mirror of
https://github.com/NixOS/nix.git
synced 2024-11-25 16:23:02 +00:00
Some effort to minimize flake dependencies
For example, if the top-level flake depends on "nixpkgs/release-19.03", and one of its dependencies depends on "nixpkgs", then the latter will be mapped to "nixpkgs/release-19.03", rather than whatever the default branch of "nixpkgs" is. Thus you get only one "nixpkgs" dependency rather than two. This currently only works in a breadth-first way, so the other way around (i.e. if the top-level flake depends on "nixpkgs", and a dependency depends on "nixpkgs/release-19.03") still results in two "nixpkgs" dependencies.
This commit is contained in:
parent
c67407172d
commit
aeb7148afd
@ -112,7 +112,9 @@ static FlakeRef lookupFlake(EvalState & state, const FlakeRef & flakeRef, const
|
|||||||
return flakeRef;
|
return flakeRef;
|
||||||
}
|
}
|
||||||
|
|
||||||
FlakeRef maybeLookupFlake(
|
/* If 'allowLookup' is true, then resolve 'flakeRef' using the
|
||||||
|
registries. */
|
||||||
|
static FlakeRef maybeLookupFlake(
|
||||||
EvalState & state,
|
EvalState & state,
|
||||||
const FlakeRef & flakeRef,
|
const FlakeRef & flakeRef,
|
||||||
bool allowLookup)
|
bool allowLookup)
|
||||||
@ -126,6 +128,23 @@ FlakeRef maybeLookupFlake(
|
|||||||
return flakeRef;
|
return flakeRef;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
typedef std::vector<std::pair<FlakeRef, FlakeRef>> RefMap;
|
||||||
|
|
||||||
|
static FlakeRef lookupInRefMap(
|
||||||
|
const RefMap & refMap,
|
||||||
|
const FlakeRef & flakeRef)
|
||||||
|
{
|
||||||
|
// FIXME: inefficient.
|
||||||
|
for (auto & i : refMap) {
|
||||||
|
if (flakeRef.contains(i.first)) {
|
||||||
|
debug("mapping '%s' to previously seen input '%s' -> '%s",
|
||||||
|
flakeRef, i.first, i.second);
|
||||||
|
return i.second;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return flakeRef;
|
||||||
|
}
|
||||||
|
|
||||||
static SourceInfo fetchFlake(EvalState & state, const FlakeRef & resolvedRef)
|
static SourceInfo fetchFlake(EvalState & state, const FlakeRef & resolvedRef)
|
||||||
{
|
{
|
||||||
@ -205,15 +224,21 @@ static void expectType(EvalState & state, ValueType type,
|
|||||||
showType(type), showType(value.type), pos);
|
showType(type), showType(value.type), pos);
|
||||||
}
|
}
|
||||||
|
|
||||||
Flake getFlake(EvalState & state, const FlakeRef & originalRef, bool allowLookup)
|
static Flake getFlake(EvalState & state, const FlakeRef & originalRef,
|
||||||
|
bool allowLookup, RefMap & refMap)
|
||||||
{
|
{
|
||||||
auto flakeRef = maybeLookupFlake(state, originalRef, allowLookup);
|
auto flakeRef = lookupInRefMap(refMap,
|
||||||
|
maybeLookupFlake(state,
|
||||||
|
lookupInRefMap(refMap, originalRef), allowLookup));
|
||||||
|
|
||||||
SourceInfo sourceInfo = fetchFlake(state, flakeRef);
|
SourceInfo sourceInfo = fetchFlake(state, flakeRef);
|
||||||
debug("got flake source '%s' with flakeref %s", sourceInfo.storePath, sourceInfo.resolvedRef.to_string());
|
debug("got flake source '%s' with flakeref %s", sourceInfo.storePath, sourceInfo.resolvedRef.to_string());
|
||||||
|
|
||||||
FlakeRef resolvedRef = sourceInfo.resolvedRef;
|
FlakeRef resolvedRef = sourceInfo.resolvedRef;
|
||||||
|
|
||||||
|
refMap.push_back({originalRef, resolvedRef});
|
||||||
|
refMap.push_back({flakeRef, resolvedRef});
|
||||||
|
|
||||||
state.store->assertStorePath(sourceInfo.storePath);
|
state.store->assertStorePath(sourceInfo.storePath);
|
||||||
|
|
||||||
if (state.allowedPaths)
|
if (state.allowedPaths)
|
||||||
@ -314,13 +339,27 @@ Flake getFlake(EvalState & state, const FlakeRef & originalRef, bool allowLookup
|
|||||||
return flake;
|
return flake;
|
||||||
}
|
}
|
||||||
|
|
||||||
static SourceInfo getNonFlake(EvalState & state, const FlakeRef & flakeRef)
|
Flake getFlake(EvalState & state, const FlakeRef & originalRef, bool allowLookup)
|
||||||
{
|
{
|
||||||
|
RefMap refMap;
|
||||||
|
return getFlake(state, originalRef, allowLookup, refMap);
|
||||||
|
}
|
||||||
|
|
||||||
|
static SourceInfo getNonFlake(EvalState & state, const FlakeRef & originalRef,
|
||||||
|
bool allowLookup, RefMap & refMap)
|
||||||
|
{
|
||||||
|
auto flakeRef = lookupInRefMap(refMap,
|
||||||
|
maybeLookupFlake(state,
|
||||||
|
lookupInRefMap(refMap, originalRef), allowLookup));
|
||||||
|
|
||||||
auto sourceInfo = fetchFlake(state, flakeRef);
|
auto sourceInfo = fetchFlake(state, flakeRef);
|
||||||
debug("got non-flake source '%s' with flakeref %s", sourceInfo.storePath, sourceInfo.resolvedRef.to_string());
|
debug("got non-flake source '%s' with flakeref %s", sourceInfo.storePath, sourceInfo.resolvedRef.to_string());
|
||||||
|
|
||||||
FlakeRef resolvedRef = sourceInfo.resolvedRef;
|
FlakeRef resolvedRef = sourceInfo.resolvedRef;
|
||||||
|
|
||||||
|
refMap.push_back({originalRef, resolvedRef});
|
||||||
|
refMap.push_back({flakeRef, resolvedRef});
|
||||||
|
|
||||||
state.store->assertStorePath(sourceInfo.storePath);
|
state.store->assertStorePath(sourceInfo.storePath);
|
||||||
|
|
||||||
if (state.allowedPaths)
|
if (state.allowedPaths)
|
||||||
@ -360,6 +399,7 @@ bool allowedToUseRegistries(HandleLockFile handle, bool isTopRef)
|
|||||||
Note that this is lazy: we only recursively fetch inputs that are
|
Note that this is lazy: we only recursively fetch inputs that are
|
||||||
not in the lockfile yet. */
|
not in the lockfile yet. */
|
||||||
static std::pair<Flake, LockedInput> updateLocks(
|
static std::pair<Flake, LockedInput> updateLocks(
|
||||||
|
RefMap & refMap,
|
||||||
const std::string & inputPath,
|
const std::string & inputPath,
|
||||||
EvalState & state,
|
EvalState & state,
|
||||||
const Flake & flake,
|
const Flake & flake,
|
||||||
@ -372,6 +412,8 @@ static std::pair<Flake, LockedInput> updateLocks(
|
|||||||
flake.originalRef,
|
flake.originalRef,
|
||||||
flake.sourceInfo.narHash);
|
flake.sourceInfo.narHash);
|
||||||
|
|
||||||
|
std::vector<std::function<void()>> postponed;
|
||||||
|
|
||||||
for (auto & [id, input] : flake.inputs) {
|
for (auto & [id, input] : flake.inputs) {
|
||||||
auto inputPath2 = (inputPath.empty() ? "" : inputPath + "/") + id;
|
auto inputPath2 = (inputPath.empty() ? "" : inputPath + "/") + id;
|
||||||
auto i = oldEntry.inputs.find(id);
|
auto i = oldEntry.inputs.find(id);
|
||||||
@ -380,29 +422,36 @@ static std::pair<Flake, LockedInput> updateLocks(
|
|||||||
} else {
|
} else {
|
||||||
if (handleLockFile == AllPure || handleLockFile == TopRefUsesRegistries)
|
if (handleLockFile == AllPure || handleLockFile == TopRefUsesRegistries)
|
||||||
throw Error("cannot update flake input '%s' in pure mode", id);
|
throw Error("cannot update flake input '%s' in pure mode", id);
|
||||||
if (input.isFlake) {
|
|
||||||
auto actualInput = getFlake(state, input.ref,
|
auto warn = [&](const SourceInfo & sourceInfo) {
|
||||||
allowedToUseRegistries(handleLockFile, false));
|
|
||||||
if (i == oldEntry.inputs.end())
|
if (i == oldEntry.inputs.end())
|
||||||
printMsg(lvlWarn, "mapped flake input '%s' to '%s'",
|
printInfo("mapped flake input '%s' to '%s'",
|
||||||
inputPath2, actualInput.sourceInfo.resolvedRef);
|
inputPath2, sourceInfo.resolvedRef);
|
||||||
else
|
else
|
||||||
printMsg(lvlWarn, "updated flake input '%s' from '%s' to '%s'",
|
printMsg(lvlWarn, "updated flake input '%s' from '%s' to '%s'",
|
||||||
inputPath2, i->second.originalRef, actualInput.sourceInfo.resolvedRef);
|
inputPath2, i->second.originalRef, sourceInfo.resolvedRef);
|
||||||
newEntry.inputs.insert_or_assign(id,
|
};
|
||||||
updateLocks(inputPath2, state, actualInput, handleLockFile, {}, false).second);
|
|
||||||
|
if (input.isFlake) {
|
||||||
|
auto actualInput = getFlake(state, input.ref,
|
||||||
|
allowedToUseRegistries(handleLockFile, false), refMap);
|
||||||
|
warn(actualInput.sourceInfo);
|
||||||
|
postponed.push_back([&, id{id}, inputPath2, actualInput]() {
|
||||||
|
newEntry.inputs.insert_or_assign(id,
|
||||||
|
updateLocks(refMap, inputPath2, state, actualInput, handleLockFile, {}, false).second);
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
auto sourceInfo = getNonFlake(state,
|
auto sourceInfo = getNonFlake(state, input.ref,
|
||||||
maybeLookupFlake(state, input.ref,
|
allowedToUseRegistries(handleLockFile, false), refMap);
|
||||||
allowedToUseRegistries(handleLockFile, false)));
|
warn(sourceInfo);
|
||||||
printMsg(lvlWarn, "mapped flake input '%s' to '%s'",
|
|
||||||
inputPath2, sourceInfo.resolvedRef);
|
|
||||||
newEntry.inputs.insert_or_assign(id,
|
newEntry.inputs.insert_or_assign(id,
|
||||||
LockedInput(sourceInfo.resolvedRef, input.ref, sourceInfo.narHash));
|
LockedInput(sourceInfo.resolvedRef, input.ref, sourceInfo.narHash));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (auto & f : postponed) f();
|
||||||
|
|
||||||
return {flake, newEntry};
|
return {flake, newEntry};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -423,8 +472,10 @@ ResolvedFlake resolveFlake(EvalState & state, const FlakeRef & topRef, HandleLoc
|
|||||||
+ "/" + flake.sourceInfo.resolvedRef.subdir + "/flake.lock");
|
+ "/" + flake.sourceInfo.resolvedRef.subdir + "/flake.lock");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
RefMap refMap;
|
||||||
|
|
||||||
LockFile lockFile(updateLocks(
|
LockFile lockFile(updateLocks(
|
||||||
"", state, flake, handleLockFile, oldLockFile, true).second);
|
refMap, "", state, flake, handleLockFile, oldLockFile, true).second);
|
||||||
|
|
||||||
if (!(lockFile == oldLockFile)) {
|
if (!(lockFile == oldLockFile)) {
|
||||||
if (allowedToWrite(handleLockFile)) {
|
if (allowedToWrite(handleLockFile)) {
|
||||||
@ -500,7 +551,8 @@ static void prim_callFlake(EvalState & state, const Pos & pos, Value * * args, V
|
|||||||
|
|
||||||
callFlake(state, flake, lazyInput->lockedInput, v);
|
callFlake(state, flake, lazyInput->lockedInput, v);
|
||||||
} else {
|
} else {
|
||||||
auto sourceInfo = getNonFlake(state, lazyInput->lockedInput.ref);
|
RefMap refMap;
|
||||||
|
auto sourceInfo = getNonFlake(state, lazyInput->lockedInput.ref, false, refMap);
|
||||||
|
|
||||||
if (sourceInfo.narHash != lazyInput->lockedInput.narHash)
|
if (sourceInfo.narHash != lazyInput->lockedInput.narHash)
|
||||||
throw Error("the content hash of repository '%s' doesn't match the hash recorded in the referring lockfile", sourceInfo.resolvedRef);
|
throw Error("the content hash of repository '%s' doesn't match the hash recorded in the referring lockfile", sourceInfo.resolvedRef);
|
||||||
|
@ -80,13 +80,6 @@ struct Flake
|
|||||||
|
|
||||||
Flake getFlake(EvalState & state, const FlakeRef & flakeRef, bool allowLookup);
|
Flake getFlake(EvalState & state, const FlakeRef & flakeRef, bool allowLookup);
|
||||||
|
|
||||||
/* If 'allowLookup' is true, then resolve 'flakeRef' using the
|
|
||||||
registries. */
|
|
||||||
FlakeRef maybeLookupFlake(
|
|
||||||
EvalState & state,
|
|
||||||
const FlakeRef & flakeRef,
|
|
||||||
bool allowLookup);
|
|
||||||
|
|
||||||
/* Fingerprint of a locked flake; used as a cache key. */
|
/* Fingerprint of a locked flake; used as a cache key. */
|
||||||
typedef Hash Fingerprint;
|
typedef Hash Fingerprint;
|
||||||
|
|
||||||
|
@ -255,6 +255,23 @@ FlakeRef FlakeRef::baseRef() const // Removes the ref and rev from a FlakeRef.
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool FlakeRef::contains(const FlakeRef & other) const
|
||||||
|
{
|
||||||
|
if (!(data == other.data))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (ref && ref != other.ref)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (rev && rev != other.rev)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (subdir != other.subdir)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
std::optional<FlakeRef> parseFlakeRef(
|
std::optional<FlakeRef> parseFlakeRef(
|
||||||
const std::string & uri, bool allowRelative)
|
const std::string & uri, bool allowRelative)
|
||||||
{
|
{
|
||||||
|
@ -182,6 +182,12 @@ struct FlakeRef
|
|||||||
return std::get_if<FlakeRef::IsPath>(&data)
|
return std::get_if<FlakeRef::IsPath>(&data)
|
||||||
&& rev == Hash(rev->type);
|
&& rev == Hash(rev->type);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Return true if 'other' is not less specific than 'this'. For
|
||||||
|
example, 'nixpkgs' contains 'nixpkgs/release-19.03', and both
|
||||||
|
'nixpkgs' and 'nixpkgs/release-19.03' contain
|
||||||
|
'nixpkgs/release-19.03/<hash>'. */
|
||||||
|
bool contains(const FlakeRef & other) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
std::ostream & operator << (std::ostream & str, const FlakeRef & flakeRef);
|
std::ostream & operator << (std::ostream & str, const FlakeRef & flakeRef);
|
||||||
|
Loading…
Reference in New Issue
Block a user