diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index 0212162dd..12b11f1ac 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -2317,6 +2317,9 @@ BackedStringView EvalState::coerceToString( } if (v.type() == nPath) { + // FIXME: instead of copying the path to the store, we could + // return a virtual store path that lazily copies the path to + // the store in devirtualize(). return !canonicalizePath && !copyToStore ? // FIXME: hack to preserve path literals that end in a @@ -2406,7 +2409,7 @@ StorePath EvalState::copyPathToStore(NixStringContext & context, const SourcePat *store, path.resolveSymlinks(SymlinkResolution::Ancestors), settings.readOnlyMode ? FetchMode::DryRun : FetchMode::Copy, - path.baseName(), + computeBaseName(path), ContentAddressMethod::Raw::NixArchive, nullptr, repair); diff --git a/src/libexpr/include/nix/expr/eval.hh b/src/libexpr/include/nix/expr/eval.hh index 505a7d1e7..3249b50a0 100644 --- a/src/libexpr/include/nix/expr/eval.hh +++ b/src/libexpr/include/nix/expr/eval.hh @@ -586,6 +586,19 @@ public: StorePath copyPathToStore(NixStringContext & context, const SourcePath & path); + + /** + * Compute the base name for a `SourcePath`. For non-store paths, + * this is just `SourcePath::baseName()`. But for store paths, for + * backwards compatibility, it needs to be `-source`, + * i.e. as if the path were copied to the Nix store. This results + * in a "double-copied" store path like + * `/nix/store/--source`. We don't need to + * materialize /nix/store/-source though. Still, this + * requires reading/hashing the path twice. + */ + std::string computeBaseName(const SourcePath & path); + /** * Path coercion. * diff --git a/src/libexpr/paths.cc b/src/libexpr/paths.cc index f4c4de5fa..a27ebcae2 100644 --- a/src/libexpr/paths.cc +++ b/src/libexpr/paths.cc @@ -52,4 +52,19 @@ std::string EvalState::devirtualize(std::string_view s, const NixStringContext & return rewriteStrings(std::string(s), rewrites); } +std::string EvalState::computeBaseName(const SourcePath & path) +{ + if (path.accessor == rootFS) { + if (auto storePath = store->maybeParseStorePath(path.path.abs())) { + warn( + "Performing inefficient double copy of path '%s' to the store. " + "This can typically be avoided by rewriting an attribute like `src = ./.` " + "to `src = builtins.path { path = ./.; name = \"source\"; }`.", + path); + return std::string(fetchToStore(*store, path, FetchMode::DryRun).to_string()); + } + } + return std::string(path.baseName()); +} + } diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index 34677f9a3..7243f09ce 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -2539,7 +2539,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], ContentAddressMethod::Raw::NixArchive, std::nullopt, v, context); + addPath(state, pos, state.computeBaseName(path), path, args[0], ContentAddressMethod::Raw::NixArchive, std::nullopt, v, context); } static RegisterPrimOp primop_filterSource({