diff --git a/src/libexpr/flake/flake.cc b/src/libexpr/flake/flake.cc index cbf4f0a6f92..10f99b8f0bf 100644 --- a/src/libexpr/flake/flake.cc +++ b/src/libexpr/flake/flake.cc @@ -7,6 +7,7 @@ #include "fetchers.hh" #include "finally.hh" #include "fetch-settings.hh" +#include "registry.hh" namespace nix { @@ -104,6 +105,7 @@ static FlakeInput parseFlakeInput(EvalState & state, auto sUrl = state.symbols.create("url"); auto sFlake = state.symbols.create("flake"); auto sFollows = state.symbols.create("follows"); + auto sRegister = state.symbols.create("register"); fetchers::Attrs attrs; std::optional url; @@ -124,7 +126,11 @@ static FlakeInput parseFlakeInput(EvalState & state, auto follows(parseInputPath(attr.value->string.s)); follows.insert(follows.begin(), lockRootPath.begin(), lockRootPath.end()); input.follows = follows; - } else { + } else if (attr.name == sRegister) { + expectType(state, nBool, *attr.value, attr.pos); + input.registered = attr.value; + } + else { switch (attr.value->type()) { case nString: attrs.emplace(state.symbols[attr.name], attr.value->string.s); @@ -359,7 +365,8 @@ LockedFlake lockFlake( std::shared_ptr oldNode, const InputPath & lockRootPath, const Path & parentPath, - bool trustLock)> + bool trustLock, + std::map registered)> computeLocks; computeLocks = [&]( @@ -369,10 +376,13 @@ LockedFlake lockFlake( std::shared_ptr oldNode, const InputPath & lockRootPath, const Path & parentPath, - bool trustLock) + bool trustLock, + std::map registered) { debug("computing lock file node '%s'", printInputPath(inputPathPrefix)); + std::set registeredChilds; + /* Get the overrides (i.e. attributes of the form 'inputs.nixops.inputs.nixpkgs.url = ...'). */ for (auto & [id, input] : flakeInputs) { @@ -382,6 +392,16 @@ LockedFlake lockFlake( inputPath.push_back(idOverride); overrides.insert_or_assign(inputPath, inputOverride); } + if (input.registered) { + /* Record the name and the eventual path of + registered inputs. Registered inputs are set to follow the recorded input path. + Only in the defining flake the registered input is handled explicitly to catch transistive inputs */ + auto inputPath(inputPathPrefix); + inputPath.push_back(id); + debug("preregistering %s", id.c_str()); + registeredChilds.insert(id); + registered.insert_or_assign(id, inputPath); + } } /* Go over the flake inputs, resolve/fetch them if @@ -419,6 +439,19 @@ LockedFlake lockFlake( continue; } + auto registeredInputID = registered.find(id); + if (registeredInputID != registered.end() + && (registeredChilds.find(id) == registeredChilds.end()) // do not substitute original input + && (input.ref->to_string() == "flake:" + id) // check that the input is not explicitly defined + ) + { + debug("Using already registered node for %s", id); + InputPath target; + target.insert(target.end(), registeredInputID->second.begin(), registeredInputID->second.end()); + node->inputs.insert_or_assign(id, target); + continue; + } + assert(input.ref); /* Do we have an entry in the existing lock file? And we @@ -503,7 +536,7 @@ LockedFlake lockFlake( mustRefetch ? getFlake(state, oldLock->lockedRef, false, flakeCache, inputPath).inputs : fakeInputs, - childNode, inputPath, oldLock, lockRootPath, parentPath, !mustRefetch); + childNode, inputPath, oldLock, lockRootPath, parentPath, !mustRefetch, registered); } else { /* We need to create a new lock file entry. So fetch @@ -553,7 +586,7 @@ LockedFlake lockFlake( ? std::dynamic_pointer_cast(oldLock) : LockFile::read( inputFlake.sourceInfo->actualPath + "/" + inputFlake.lockedRef.subdir + "/flake.lock").root, - oldLock ? lockRootPath : inputPath, localPath, false); + oldLock ? lockRootPath : inputPath, localPath, false, registered); } else { @@ -573,10 +606,11 @@ LockedFlake lockFlake( // Bring in the current ref for relative path resolution if we have it auto parentPath = canonPath(flake.sourceInfo->actualPath + "/" + flake.lockedRef.subdir, true); + std::map registered; computeLocks( flake.inputs, newLockFile.root, {}, - lockFlags.recreateLockFile ? nullptr : oldLockFile.root, {}, parentPath, false); + lockFlags.recreateLockFile ? nullptr : oldLockFile.root, {}, parentPath, false, registered); for (auto & i : lockFlags.inputOverrides) if (!overridesUsed.count(i.first)) diff --git a/src/libexpr/flake/flake.hh b/src/libexpr/flake/flake.hh index 524b18af175..c6b49e71844 100644 --- a/src/libexpr/flake/flake.hh +++ b/src/libexpr/flake/flake.hh @@ -42,6 +42,7 @@ struct FlakeInput { std::optional ref; bool isFlake = true; // true = process flake to get outputs, false = (fetched) static source path + bool registered = false; std::optional follows; FlakeInputs overrides; };