From c080c4ca563268d2510932e02d669d481c0db70f Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Tue, 2 Dec 2025 15:38:41 +0100 Subject: [PATCH] builtins.path: Propagate references from derivation outputs This restores compatibility with Nix 2.18, which behaved this way. Note that this doesn't scan for the actually visible references. Unlike in Nix 2.18, we only do this for paths with context, i.e. it applies to `builtins.storePath "/nix/store/bla..."` but not `"/nix/store/bla..."`. We don't want the latter because it shouldn't matter whether a source file happens to be in the Nix store. --- src/libexpr/primops.cc | 35 ++++++++++++++------- tests/functional/import-from-derivation.nix | 20 ++++++++++++ tests/functional/import-from-derivation.sh | 9 ++++++ 3 files changed, 53 insertions(+), 11 deletions(-) diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index ea10d4c55..f972a65bd 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -2807,11 +2807,15 @@ static void addPath( const NixStringContext & context) { try { - if (path.accessor == state.rootFS && state.store->isInStore(path.path.abs())) { + StorePathSet refs; + + if (path.accessor == state.rootFS && state.store->isInStore(path.path.abs()) && !context.empty()) { // FIXME: handle CA derivation outputs (where path needs to // be rewritten to the actual output). auto rewrites = state.realiseContext(context); path = {path.accessor, CanonPath(rewriteStrings(path.path.abs(), rewrites))}; + auto [storePath, subPath] = state.store->toStorePath(path.path.abs()); + refs = state.store->queryPathInfo(storePath)->references; } std::unique_ptr filter; @@ -2824,18 +2828,27 @@ static void addPath( std::optional expectedStorePath; if (expectedHash) expectedStorePath = state.store->makeFixedOutputPathFromCA( - name, ContentAddressWithReferences::fromParts(method, *expectedHash, {})); + name, ContentAddressWithReferences::fromParts(method, *expectedHash, {refs})); if (!expectedHash || !state.store->isValidPath(*expectedStorePath)) { - auto dstPath = fetchToStore( - state.fetchSettings, - *state.store, - path.resolveSymlinks(), - settings.readOnlyMode ? FetchMode::DryRun : FetchMode::Copy, - name, - method, - filter.get(), - state.repair); + // FIXME: support refs in fetchToStore()? + auto dstPath = refs.empty() ? fetchToStore( + state.fetchSettings, + *state.store, + path.resolveSymlinks(), + settings.readOnlyMode ? FetchMode::DryRun : FetchMode::Copy, + name, + method, + filter.get(), + state.repair) + : state.store->addToStore( + name, + path.resolveSymlinks(), + method, + HashAlgorithm::SHA256, + refs, + filter ? *filter.get() : defaultPathFilter, + state.repair); if (expectedHash && expectedStorePath != dstPath) state.error("store path mismatch in (possibly filtered) path added from '%s'", path) .atPos(pos) diff --git a/tests/functional/import-from-derivation.nix b/tests/functional/import-from-derivation.nix index 600f448a6..ac38d250d 100644 --- a/tests/functional/import-from-derivation.nix +++ b/tests/functional/import-from-derivation.nix @@ -46,4 +46,24 @@ rec { }; importAddPathExpr = import addPathExpr; + + symlink = mkDerivation { + name = "symlink"; + buildCommand = '' + mkdir $out + echo hello world > $out/text + ln -s $out/text $out/symlink + ln -s ${step1} $out/symlink2 + ''; + }; + + pathFromDerivation = builtins.path { + name = "path-from-derivation"; + path = "${symlink}/"; + }; + + pathFromDerivation2 = builtins.path { + name = "path-from-derivation"; + path = "${symlink}/text"; + }; } diff --git a/tests/functional/import-from-derivation.sh b/tests/functional/import-from-derivation.sh index a00761235..2970a830e 100755 --- a/tests/functional/import-from-derivation.sh +++ b/tests/functional/import-from-derivation.sh @@ -29,6 +29,15 @@ fi outPath2=$(nix-build ./import-from-derivation.nix -A addPath --no-out-link) [[ "$(cat "$outPath2")" = BLAFOO579 ]] +# Test that applying builtins.path to the result of a derivation propagates all references +for attr in pathFromDerivation pathFromDerivation2; do + outPath3=$(nix eval --raw -f ./import-from-derivation.nix "$attr") + refs=$(nix path-info --json "$outPath3" | jq -r '.[].references.[]') + [[ $(printf "%s" "$refs" | wc -w) = 2 ]] + [[ $refs =~ -step1 ]] + [[ $refs =~ -symlink ]] +done + # Test that IFD works with a chroot store. if canUseSandbox; then