mirror of
https://github.com/privatevoid-net/nix-super.git
synced 2024-11-24 23:06:16 +02:00
Improve subflake handling
Relative 'path:' inputs are now handled correctly in call-flake.nix. This does require the lock file to record what the 'parent' is relative to which a node's source should be fetched.
This commit is contained in:
parent
0d14ffbcba
commit
5c2603d9c7
4 changed files with 107 additions and 53 deletions
|
@ -4,24 +4,6 @@ let
|
||||||
|
|
||||||
lockFile = builtins.fromJSON lockFileStr;
|
lockFile = builtins.fromJSON lockFileStr;
|
||||||
|
|
||||||
allNodes =
|
|
||||||
builtins.mapAttrs
|
|
||||||
(key: node:
|
|
||||||
let
|
|
||||||
|
|
||||||
sourceInfo =
|
|
||||||
if key == lockFile.root
|
|
||||||
then rootSrc
|
|
||||||
else fetchTree (node.info or {} // removeAttrs node.locked ["dir"]);
|
|
||||||
|
|
||||||
subdir = if key == lockFile.root then rootSubdir else node.locked.dir or "";
|
|
||||||
|
|
||||||
flake = import (sourceInfo + (if subdir != "" then "/" else "") + subdir + "/flake.nix");
|
|
||||||
|
|
||||||
inputs = builtins.mapAttrs
|
|
||||||
(inputName: inputSpec: allNodes.${resolveInput inputSpec})
|
|
||||||
(node.inputs or {});
|
|
||||||
|
|
||||||
# Resolve a input spec into a node name. An input spec is
|
# Resolve a input spec into a node name. An input spec is
|
||||||
# either a node name, or a 'follows' path from the root
|
# either a node name, or a 'follows' path from the root
|
||||||
# node.
|
# node.
|
||||||
|
@ -41,6 +23,34 @@ let
|
||||||
(resolveInput lockFile.nodes.${nodeName}.inputs.${builtins.head path})
|
(resolveInput lockFile.nodes.${nodeName}.inputs.${builtins.head path})
|
||||||
(builtins.tail path);
|
(builtins.tail path);
|
||||||
|
|
||||||
|
allNodes =
|
||||||
|
builtins.mapAttrs
|
||||||
|
(key: node:
|
||||||
|
let
|
||||||
|
|
||||||
|
sourceInfo =
|
||||||
|
if key == lockFile.root
|
||||||
|
then rootSrc
|
||||||
|
else if node.locked.type == "path" && builtins.substring 0 1 node.locked.path != "/"
|
||||||
|
then
|
||||||
|
let
|
||||||
|
parentNode = allNodes.${getInputByPath lockFile.root node.parent};
|
||||||
|
in parentNode.sourceInfo // {
|
||||||
|
outPath = parentNode.sourceInfo.outPath + ("/" + node.locked.path);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
# FIXME: remove obsolete node.info.
|
||||||
|
fetchTree (node.info or {} // removeAttrs node.locked ["dir"]);
|
||||||
|
|
||||||
|
subdir = if key == lockFile.root then rootSubdir else node.locked.dir or "";
|
||||||
|
|
||||||
|
flake =
|
||||||
|
import (sourceInfo.outPath + ((if subdir != "" then "/" else "") + subdir + "/flake.nix"));
|
||||||
|
|
||||||
|
inputs = builtins.mapAttrs
|
||||||
|
(inputName: inputSpec: allNodes.${resolveInput inputSpec})
|
||||||
|
(node.inputs or {});
|
||||||
|
|
||||||
outputs = flake.outputs (inputs // { self = result; });
|
outputs = flake.outputs (inputs // { self = result; });
|
||||||
|
|
||||||
result = outputs // sourceInfo // { inherit inputs; inherit outputs; inherit sourceInfo; };
|
result = outputs // sourceInfo // { inherit inputs; inherit outputs; inherit sourceInfo; };
|
||||||
|
|
|
@ -307,11 +307,16 @@ LockedFlake lockFlake(
|
||||||
debug("old lock file: %s", oldLockFile);
|
debug("old lock file: %s", oldLockFile);
|
||||||
|
|
||||||
// FIXME: check whether all overrides are used.
|
// FIXME: check whether all overrides are used.
|
||||||
std::map<InputPath, FlakeInput> overrides;
|
std::map<InputPath, std::tuple<FlakeInput, SourcePath, std::optional<InputPath>>> overrides;
|
||||||
std::set<InputPath> overridesUsed, updatesUsed;
|
std::set<InputPath> overridesUsed, updatesUsed;
|
||||||
|
|
||||||
for (auto & i : lockFlags.inputOverrides)
|
for (auto & i : lockFlags.inputOverrides)
|
||||||
overrides.insert_or_assign(i.first, FlakeInput { .ref = i.second });
|
overrides.emplace(
|
||||||
|
i.first,
|
||||||
|
std::make_tuple(
|
||||||
|
FlakeInput { .ref = i.second },
|
||||||
|
state.rootPath("/"),
|
||||||
|
std::nullopt));
|
||||||
|
|
||||||
LockFile newLockFile;
|
LockFile newLockFile;
|
||||||
|
|
||||||
|
@ -322,18 +327,29 @@ LockedFlake lockFlake(
|
||||||
std::shared_ptr<Node> node,
|
std::shared_ptr<Node> node,
|
||||||
const InputPath & inputPathPrefix,
|
const InputPath & inputPathPrefix,
|
||||||
std::shared_ptr<const Node> oldNode,
|
std::shared_ptr<const Node> oldNode,
|
||||||
const InputPath & lockRootPath,
|
const InputPath & followsPrefix,
|
||||||
const SourcePath & parentPath,
|
const SourcePath & sourcePath,
|
||||||
bool trustLock)>
|
bool trustLock)>
|
||||||
computeLocks;
|
computeLocks;
|
||||||
|
|
||||||
computeLocks = [&](
|
computeLocks = [&](
|
||||||
|
/* The inputs of this node, either from flake.nix or
|
||||||
|
flake.lock */
|
||||||
const FlakeInputs & flakeInputs,
|
const FlakeInputs & flakeInputs,
|
||||||
|
/* The node whose locks are to be updated.*/
|
||||||
std::shared_ptr<Node> node,
|
std::shared_ptr<Node> node,
|
||||||
|
/* The path to this node in the lock file graph. */
|
||||||
const InputPath & inputPathPrefix,
|
const InputPath & inputPathPrefix,
|
||||||
|
/* The old node, if any, from which locks can be
|
||||||
|
copied. */
|
||||||
std::shared_ptr<const Node> oldNode,
|
std::shared_ptr<const Node> oldNode,
|
||||||
const InputPath & lockRootPath,
|
/* The prefix relative to which 'follows' should be
|
||||||
const SourcePath & parentPath,
|
interpreted. When a node is initially locked, it's
|
||||||
|
relative to the node's flake; when it's already locked,
|
||||||
|
it's relative to the root of the lock file. */
|
||||||
|
const InputPath & followsPrefix,
|
||||||
|
/* The source path of this node's flake. */
|
||||||
|
const SourcePath & sourcePath,
|
||||||
bool trustLock)
|
bool trustLock)
|
||||||
{
|
{
|
||||||
debug("computing lock file node '%s'", printInputPath(inputPathPrefix));
|
debug("computing lock file node '%s'", printInputPath(inputPathPrefix));
|
||||||
|
@ -345,7 +361,8 @@ LockedFlake lockFlake(
|
||||||
auto inputPath(inputPathPrefix);
|
auto inputPath(inputPathPrefix);
|
||||||
inputPath.push_back(id);
|
inputPath.push_back(id);
|
||||||
inputPath.push_back(idOverride);
|
inputPath.push_back(idOverride);
|
||||||
overrides.insert_or_assign(inputPath, inputOverride);
|
overrides.emplace(inputPath,
|
||||||
|
std::make_tuple(inputOverride, sourcePath, inputPathPrefix));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -364,13 +381,18 @@ LockedFlake lockFlake(
|
||||||
ancestors? */
|
ancestors? */
|
||||||
auto i = overrides.find(inputPath);
|
auto i = overrides.find(inputPath);
|
||||||
bool hasOverride = i != overrides.end();
|
bool hasOverride = i != overrides.end();
|
||||||
if (hasOverride) {
|
if (hasOverride)
|
||||||
overridesUsed.insert(inputPath);
|
overridesUsed.insert(inputPath);
|
||||||
// Respect the “flakeness” of the input even if we
|
auto input = hasOverride ? std::get<0>(i->second) : input2;
|
||||||
// override it
|
|
||||||
i->second.isFlake = input2.isFlake;
|
/* Resolve relative 'path:' inputs relative to
|
||||||
}
|
the source path of the overrider. */
|
||||||
auto & input = hasOverride ? i->second : input2;
|
auto overridenSourcePath = hasOverride ? std::get<1>(i->second) : sourcePath;
|
||||||
|
|
||||||
|
/* Respect the "flakeness" of the input even if we
|
||||||
|
override it. */
|
||||||
|
if (hasOverride)
|
||||||
|
input.isFlake = input2.isFlake;
|
||||||
|
|
||||||
/* Resolve 'follows' later (since it may refer to an input
|
/* Resolve 'follows' later (since it may refer to an input
|
||||||
path we haven't processed yet. */
|
path we haven't processed yet. */
|
||||||
|
@ -386,17 +408,20 @@ LockedFlake lockFlake(
|
||||||
|
|
||||||
assert(input.ref);
|
assert(input.ref);
|
||||||
|
|
||||||
|
auto overridenParentPath =
|
||||||
|
input.ref->input.isRelative()
|
||||||
|
? std::optional<InputPath>(hasOverride ? std::get<2>(i->second) : inputPathPrefix)
|
||||||
|
: std::nullopt;
|
||||||
|
|
||||||
/* Get the input flake, resolve 'path:./...'
|
/* Get the input flake, resolve 'path:./...'
|
||||||
flakerefs relative to the parent flake. */
|
flakerefs relative to the parent flake. */
|
||||||
auto getInputFlake = [&]()
|
auto getInputFlake = [&]()
|
||||||
{
|
{
|
||||||
if (input.ref->input.isRelative()) {
|
if (input.ref->input.isRelative()) {
|
||||||
SourcePath inputSourcePath {
|
SourcePath inputSourcePath {
|
||||||
parentPath.accessor,
|
overridenSourcePath.accessor,
|
||||||
CanonPath(*input.ref->input.getSourcePath(), *parentPath.path.parent())
|
CanonPath(*input.ref->input.getSourcePath(), *overridenSourcePath.path.parent())
|
||||||
};
|
};
|
||||||
// FIXME: we need to record in the lock
|
|
||||||
// file what the parent flake is.
|
|
||||||
return readFlake(state, *input.ref, *input.ref, *input.ref, inputSourcePath, inputPath);
|
return readFlake(state, *input.ref, *input.ref, *input.ref, inputSourcePath, inputPath);
|
||||||
} else
|
} else
|
||||||
return getFlake(state, *input.ref, useRegistries, inputPath);
|
return getFlake(state, *input.ref, useRegistries, inputPath);
|
||||||
|
@ -415,6 +440,7 @@ LockedFlake lockFlake(
|
||||||
|
|
||||||
if (oldLock
|
if (oldLock
|
||||||
&& oldLock->originalRef == *input.ref
|
&& oldLock->originalRef == *input.ref
|
||||||
|
&& oldLock->parentPath == overridenParentPath
|
||||||
&& !hasOverride)
|
&& !hasOverride)
|
||||||
{
|
{
|
||||||
debug("keeping existing input '%s'", inputPathS);
|
debug("keeping existing input '%s'", inputPathS);
|
||||||
|
@ -423,7 +449,8 @@ LockedFlake lockFlake(
|
||||||
didn't change and there is no override from a
|
didn't change and there is no override from a
|
||||||
higher level flake. */
|
higher level flake. */
|
||||||
auto childNode = std::make_shared<LockedNode>(
|
auto childNode = std::make_shared<LockedNode>(
|
||||||
oldLock->lockedRef, oldLock->originalRef, oldLock->isFlake);
|
oldLock->lockedRef, oldLock->originalRef, oldLock->isFlake,
|
||||||
|
oldLock->parentPath);
|
||||||
|
|
||||||
node->inputs.insert_or_assign(id, childNode);
|
node->inputs.insert_or_assign(id, childNode);
|
||||||
|
|
||||||
|
@ -466,7 +493,7 @@ LockedFlake lockFlake(
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
auto absoluteFollows(lockRootPath);
|
auto absoluteFollows(followsPrefix);
|
||||||
absoluteFollows.insert(absoluteFollows.end(), follows->begin(), follows->end());
|
absoluteFollows.insert(absoluteFollows.end(), follows->begin(), follows->end());
|
||||||
fakeInputs.emplace(i.first, FlakeInput {
|
fakeInputs.emplace(i.first, FlakeInput {
|
||||||
.follows = absoluteFollows,
|
.follows = absoluteFollows,
|
||||||
|
@ -477,13 +504,14 @@ LockedFlake lockFlake(
|
||||||
|
|
||||||
if (mustRefetch) {
|
if (mustRefetch) {
|
||||||
auto inputFlake = getInputFlake();
|
auto inputFlake = getInputFlake();
|
||||||
computeLocks(inputFlake.inputs, childNode, inputPath, oldLock, lockRootPath, inputFlake.path, !mustRefetch);
|
computeLocks(inputFlake.inputs, childNode, inputPath, oldLock, followsPrefix,
|
||||||
|
inputFlake.path, !mustRefetch);
|
||||||
} else {
|
} else {
|
||||||
// FIXME: parentPath is wrong here, we
|
// FIXME: sourcePath is wrong here, we
|
||||||
// should pass a lambda that lazily
|
// should pass a lambda that lazily
|
||||||
// fetches the parent flake if needed
|
// fetches the parent flake if needed
|
||||||
// (i.e. getInputFlake()).
|
// (i.e. getInputFlake()).
|
||||||
computeLocks(fakeInputs, childNode, inputPath, oldLock, lockRootPath, parentPath, !mustRefetch);
|
computeLocks(fakeInputs, childNode, inputPath, oldLock, followsPrefix, sourcePath, !mustRefetch);
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
@ -506,7 +534,9 @@ LockedFlake lockFlake(
|
||||||
if (input.isFlake) {
|
if (input.isFlake) {
|
||||||
auto inputFlake = getInputFlake();
|
auto inputFlake = getInputFlake();
|
||||||
|
|
||||||
auto childNode = std::make_shared<LockedNode>(inputFlake.lockedRef, ref);
|
auto childNode = std::make_shared<LockedNode>(
|
||||||
|
inputFlake.lockedRef, ref, true,
|
||||||
|
overridenParentPath);
|
||||||
|
|
||||||
node->inputs.insert_or_assign(id, childNode);
|
node->inputs.insert_or_assign(id, childNode);
|
||||||
|
|
||||||
|
@ -526,7 +556,7 @@ LockedFlake lockFlake(
|
||||||
oldLock
|
oldLock
|
||||||
? std::dynamic_pointer_cast<const Node>(oldLock)
|
? std::dynamic_pointer_cast<const Node>(oldLock)
|
||||||
: readLockFile(inputFlake).root,
|
: readLockFile(inputFlake).root,
|
||||||
oldLock ? lockRootPath : inputPath,
|
oldLock ? followsPrefix : inputPath,
|
||||||
inputFlake.path,
|
inputFlake.path,
|
||||||
false);
|
false);
|
||||||
}
|
}
|
||||||
|
@ -537,7 +567,7 @@ LockedFlake lockFlake(
|
||||||
auto [accessor, lockedRef] = resolvedRef.lazyFetch(state.store);
|
auto [accessor, lockedRef] = resolvedRef.lazyFetch(state.store);
|
||||||
|
|
||||||
node->inputs.insert_or_assign(id,
|
node->inputs.insert_or_assign(id,
|
||||||
std::make_shared<LockedNode>(lockedRef, ref, false));
|
std::make_shared<LockedNode>(lockedRef, ref, false, overridenParentPath));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -549,8 +579,13 @@ LockedFlake lockFlake(
|
||||||
};
|
};
|
||||||
|
|
||||||
computeLocks(
|
computeLocks(
|
||||||
flake->inputs, newLockFile.root, {},
|
flake->inputs,
|
||||||
lockFlags.recreateLockFile ? nullptr : oldLockFile.root, {}, flake->path, false);
|
newLockFile.root,
|
||||||
|
{},
|
||||||
|
lockFlags.recreateLockFile ? nullptr : oldLockFile.root,
|
||||||
|
{},
|
||||||
|
flake->path,
|
||||||
|
false);
|
||||||
|
|
||||||
for (auto & i : lockFlags.inputOverrides)
|
for (auto & i : lockFlags.inputOverrides)
|
||||||
if (!overridesUsed.count(i.first))
|
if (!overridesUsed.count(i.first))
|
||||||
|
|
|
@ -31,9 +31,10 @@ FlakeRef getFlakeRef(
|
||||||
}
|
}
|
||||||
|
|
||||||
LockedNode::LockedNode(const nlohmann::json & json)
|
LockedNode::LockedNode(const nlohmann::json & json)
|
||||||
: lockedRef(getFlakeRef(json, "locked", "info"))
|
: lockedRef(getFlakeRef(json, "locked", "info")) // FIXME: remove "info"
|
||||||
, originalRef(getFlakeRef(json, "original", nullptr))
|
, originalRef(getFlakeRef(json, "original", nullptr))
|
||||||
, isFlake(json.find("flake") != json.end() ? (bool) json["flake"] : true)
|
, isFlake(json.find("flake") != json.end() ? (bool) json["flake"] : true)
|
||||||
|
, parentPath(json.find("parent") != json.end() ? (std::optional<InputPath>) json["parent"] : std::nullopt)
|
||||||
{
|
{
|
||||||
if (!lockedRef.input.isLocked() && !lockedRef.input.isRelative())
|
if (!lockedRef.input.isLocked() && !lockedRef.input.isRelative())
|
||||||
throw Error("lock file contains unlocked input '%s'",
|
throw Error("lock file contains unlocked input '%s'",
|
||||||
|
@ -164,7 +165,10 @@ nlohmann::json LockFile::toJSON() const
|
||||||
if (auto lockedNode = std::dynamic_pointer_cast<const LockedNode>(node)) {
|
if (auto lockedNode = std::dynamic_pointer_cast<const LockedNode>(node)) {
|
||||||
n["original"] = fetchers::attrsToJSON(lockedNode->originalRef.toAttrs());
|
n["original"] = fetchers::attrsToJSON(lockedNode->originalRef.toAttrs());
|
||||||
n["locked"] = fetchers::attrsToJSON(lockedNode->lockedRef.toAttrs());
|
n["locked"] = fetchers::attrsToJSON(lockedNode->lockedRef.toAttrs());
|
||||||
if (!lockedNode->isFlake) n["flake"] = false;
|
if (!lockedNode->isFlake)
|
||||||
|
n["flake"] = false;
|
||||||
|
if (lockedNode->parentPath)
|
||||||
|
n["parent"] = *lockedNode->parentPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
nodes[key] = std::move(n);
|
nodes[key] = std::move(n);
|
||||||
|
|
|
@ -33,11 +33,16 @@ struct LockedNode : Node
|
||||||
FlakeRef lockedRef, originalRef;
|
FlakeRef lockedRef, originalRef;
|
||||||
bool isFlake = true;
|
bool isFlake = true;
|
||||||
|
|
||||||
|
/* The node relative to which relative source paths
|
||||||
|
(e.g. 'path:../foo') are interpreted. */
|
||||||
|
std::optional<InputPath> parentPath;
|
||||||
|
|
||||||
LockedNode(
|
LockedNode(
|
||||||
const FlakeRef & lockedRef,
|
const FlakeRef & lockedRef,
|
||||||
const FlakeRef & originalRef,
|
const FlakeRef & originalRef,
|
||||||
bool isFlake = true)
|
bool isFlake = true,
|
||||||
: lockedRef(lockedRef), originalRef(originalRef), isFlake(isFlake)
|
std::optional<InputPath> parentPath = {})
|
||||||
|
: lockedRef(lockedRef), originalRef(originalRef), isFlake(isFlake), parentPath(parentPath)
|
||||||
{ }
|
{ }
|
||||||
|
|
||||||
LockedNode(const nlohmann::json & json);
|
LockedNode(const nlohmann::json & json);
|
||||||
|
|
Loading…
Reference in a new issue