From f24ff056cb36c3ceb887722c44db64b705371dae Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Thu, 6 Feb 2025 22:39:01 +0100 Subject: [PATCH] Make `nix flake metadata|update|lock` lazy These don't need to evaluate anything (except for the flake metadata in flake.nix) so we can make these commands operate on lazy trees without risk of any semantic change in the evaluator. However, `nix flake metadata` now no longer prints the store path, which is a breaking change (but unavoidable if we want lazy trees). --- src/libflake/flake/flake.cc | 38 +++++++++++++++++++------------ src/libflake/flake/flake.hh | 11 ++++++++- src/nix/flake.cc | 10 +++----- tests/functional/flakes/flakes.sh | 1 - 4 files changed, 37 insertions(+), 23 deletions(-) diff --git a/src/libflake/flake/flake.cc b/src/libflake/flake/flake.cc index 717848ee1..90945f949 100644 --- a/src/libflake/flake/flake.cc +++ b/src/libflake/flake/flake.cc @@ -397,7 +397,8 @@ static Flake getFlake( const FlakeRef & originalRef, bool useRegistries, FlakeCache & flakeCache, - const InputAttrPath & lockRootAttrPath) + const InputAttrPath & lockRootAttrPath, + bool forceLazy) { // Fetch a lazy tree first. auto [accessor, resolvedRef, lockedRef] = fetchOrSubstituteTree( @@ -419,17 +420,22 @@ static Flake getFlake( lockedRef = lockedRef2; } - // Copy the tree to the store. - auto storePath = copyInputToStore(state, lockedRef.input, originalRef.input, accessor); - // Re-parse flake.nix from the store. - return readFlake(state, originalRef, resolvedRef, lockedRef, state.rootPath(state.store->toRealPath(storePath)), lockRootAttrPath); + return readFlake( + state, originalRef, resolvedRef, lockedRef, + forceLazy && lockedRef.input.isLocked() + ? SourcePath(accessor) + : // Copy the tree to the store. + state.rootPath( + state.store->toRealPath( + copyInputToStore(state, lockedRef.input, originalRef.input, accessor))), + lockRootAttrPath); } -Flake getFlake(EvalState & state, const FlakeRef & originalRef, bool useRegistries) +Flake getFlake(EvalState & state, const FlakeRef & originalRef, bool useRegistries, bool forceLazy) { FlakeCache flakeCache; - return getFlake(state, originalRef, useRegistries, flakeCache, {}); + return getFlake(state, originalRef, useRegistries, flakeCache, {}, forceLazy); } static LockFile readLockFile( @@ -455,7 +461,7 @@ LockedFlake lockFlake( auto useRegistries = lockFlags.useRegistries.value_or(settings.useRegistries); - auto flake = getFlake(state, topRef, useRegistries, flakeCache, {}); + auto flake = getFlake(state, topRef, useRegistries, flakeCache, {}, lockFlags.forceLazy); if (lockFlags.applyNixConfig) { flake.config.apply(settings); @@ -630,7 +636,7 @@ LockedFlake lockFlake( if (auto resolvedPath = resolveRelativePath()) { return readFlake(state, *input.ref, *input.ref, *input.ref, *resolvedPath, inputAttrPath); } else { - return getFlake(state, *input.ref, useRegistries, flakeCache, inputAttrPath); + return getFlake(state, *input.ref, useRegistries, flakeCache, inputAttrPath, lockFlags.forceLazy); } }; @@ -781,10 +787,14 @@ LockedFlake lockFlake( auto [accessor, resolvedRef, lockedRef] = fetchOrSubstituteTree( state, *input.ref, useRegistries, flakeCache); - // FIXME: allow input to be lazy. - auto storePath = copyInputToStore(state, lockedRef.input, input.ref->input, accessor); - - return {state.rootPath(state.store->toRealPath(storePath)), lockedRef}; + return { + lockFlags.forceLazy && lockedRef.input.isLocked() + ? SourcePath(accessor) + : state.rootPath( + state.store->toRealPath( + copyInputToStore(state, lockedRef.input, input.ref->input, accessor))), + lockedRef + }; } }(); @@ -894,7 +904,7 @@ LockedFlake lockFlake( repo, so we should re-read it. FIXME: we could also just clear the 'rev' field... */ auto prevLockedRef = flake.lockedRef; - flake = getFlake(state, topRef, useRegistries); + flake = getFlake(state, topRef, useRegistries, lockFlags.forceLazy); if (lockFlags.commitLockFile && flake.lockedRef.input.getRev() && diff --git a/src/libflake/flake/flake.hh b/src/libflake/flake/flake.hh index 8d9b9a698..3696fd110 100644 --- a/src/libflake/flake/flake.hh +++ b/src/libflake/flake/flake.hh @@ -123,7 +123,11 @@ struct Flake } }; -Flake getFlake(EvalState & state, const FlakeRef & flakeRef, bool useRegistries); +Flake getFlake( + EvalState & state, + const FlakeRef & flakeRef, + bool useRegistries, + bool forceLazy = false); /** * Fingerprint of a locked flake; used as a cache key. @@ -221,6 +225,11 @@ struct LockFlags * for those inputs will be ignored. */ std::set inputUpdates; + + /** + * If set, do not copy the flake to the Nix store. + */ + bool forceLazy = false; }; LockedFlake lockFlake( diff --git a/src/nix/flake.cc b/src/nix/flake.cc index 6f220b495..1cc13bf59 100644 --- a/src/nix/flake.cc +++ b/src/nix/flake.cc @@ -133,6 +133,7 @@ public: lockFlags.recreateLockFile = updateAll; lockFlags.writeLockFile = true; lockFlags.applyNixConfig = true; + lockFlags.forceLazy = true; lockFlake(); } @@ -165,6 +166,7 @@ struct CmdFlakeLock : FlakeCommand lockFlags.writeLockFile = true; lockFlags.failOnUnlocked = true; lockFlags.applyNixConfig = true; + lockFlags.forceLazy = true; lockFlake(); } @@ -211,12 +213,10 @@ struct CmdFlakeMetadata : FlakeCommand, MixJSON void run(nix::ref store) override { + lockFlags.forceLazy = true; auto lockedFlake = lockFlake(); auto & flake = lockedFlake.flake; - // Currently, all flakes are in the Nix store via the rootFS accessor. - auto storePath = store->printStorePath(sourcePathToStorePath(store, flake.path).first); - if (json) { nlohmann::json j; if (flake.description) @@ -237,7 +237,6 @@ struct CmdFlakeMetadata : FlakeCommand, MixJSON j["revCount"] = *revCount; if (auto lastModified = flake.lockedRef.input.getLastModified()) j["lastModified"] = *lastModified; - j["path"] = storePath; j["locks"] = lockedFlake.lockFile.toJSON().first; if (auto fingerprint = lockedFlake.getFingerprint(store, fetchSettings)) j["fingerprint"] = fingerprint->to_string(HashFormat::Base16, false); @@ -254,9 +253,6 @@ struct CmdFlakeMetadata : FlakeCommand, MixJSON logger->cout( ANSI_BOLD "Description:" ANSI_NORMAL " %s", *flake.description); - logger->cout( - ANSI_BOLD "Path:" ANSI_NORMAL " %s", - storePath); if (auto rev = flake.lockedRef.input.getRev()) logger->cout( ANSI_BOLD "Revision:" ANSI_NORMAL " %s", diff --git a/tests/functional/flakes/flakes.sh b/tests/functional/flakes/flakes.sh index d8c9f254d..8936afa22 100755 --- a/tests/functional/flakes/flakes.sh +++ b/tests/functional/flakes/flakes.sh @@ -69,7 +69,6 @@ nix flake metadata "$flake1Dir" | grepQuiet 'URL:.*flake1.*' # Test 'nix flake metadata --json'. json=$(nix flake metadata flake1 --json | jq .) [[ $(echo "$json" | jq -r .description) = 'Bla bla' ]] -[[ -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) ]]