mirror of
https://github.com/NixOS/nix.git
synced 2025-11-29 13:41:00 +01:00
refactor(libutil/topo-sort): return variant instead of throwing
The variant has on the left-hand side the topologically sorted vector and the right-hand side is a pair showing the path and its parent that represent a cycle in the graph making the sort impossible. This change prepares for enhanced cycle error messages that can provide more context about the cycle. The variant approach allows callers to handle cycles more flexibly, enabling better error reporting that shows the full cycle path and which files are involved. Adapted from Lix commit f7871fcb5. Change-Id: I70a987f470437df8beb3b1cc203ff88701d0aa1b Co-Authored-By: Maximilian Bosch <maximilian@mbosch.me>
This commit is contained in:
parent
7a60f1429f
commit
182ae393d1
6 changed files with 427 additions and 77 deletions
|
|
@ -1473,43 +1473,46 @@ SingleDrvOutputs DerivationBuilderImpl::registerOutputs()
|
|||
outputStats.insert_or_assign(outputName, std::move(st));
|
||||
}
|
||||
|
||||
auto sortedOutputNames = topoSort(
|
||||
outputsToSort,
|
||||
{[&](const std::string & name) {
|
||||
auto orifu = get(outputReferencesIfUnregistered, name);
|
||||
if (!orifu)
|
||||
auto topoSortResult = topoSort(outputsToSort, {[&](const std::string & name) {
|
||||
auto orifu = get(outputReferencesIfUnregistered, name);
|
||||
if (!orifu)
|
||||
throw BuildError(
|
||||
BuildResult::Failure::OutputRejected,
|
||||
"no output reference for '%s' in build of '%s'",
|
||||
name,
|
||||
store.printStorePath(drvPath));
|
||||
return std::visit(
|
||||
overloaded{
|
||||
/* Since we'll use the already installed versions of these, we
|
||||
can treat them as leaves and ignore any references they
|
||||
have. */
|
||||
[&](const AlreadyRegistered &) { return StringSet{}; },
|
||||
[&](const PerhapsNeedToRegister & refs) {
|
||||
StringSet referencedOutputs;
|
||||
/* FIXME build inverted map up front so no quadratic waste here */
|
||||
for (auto & r : refs.refs)
|
||||
for (auto & [o, p] : scratchOutputs)
|
||||
if (r == p)
|
||||
referencedOutputs.insert(o);
|
||||
return referencedOutputs;
|
||||
},
|
||||
},
|
||||
*orifu);
|
||||
}});
|
||||
|
||||
auto sortedOutputNames = std::visit(
|
||||
overloaded{
|
||||
[&](Cycle<std::string> & cycle) -> std::vector<std::string> {
|
||||
// TODO with more -vvvv also show the temporary paths for manual inspection.
|
||||
throw BuildError(
|
||||
BuildResult::Failure::OutputRejected,
|
||||
"no output reference for '%s' in build of '%s'",
|
||||
name,
|
||||
store.printStorePath(drvPath));
|
||||
return std::visit(
|
||||
overloaded{
|
||||
/* Since we'll use the already installed versions of these, we
|
||||
can treat them as leaves and ignore any references they
|
||||
have. */
|
||||
[&](const AlreadyRegistered &) { return StringSet{}; },
|
||||
[&](const PerhapsNeedToRegister & refs) {
|
||||
StringSet referencedOutputs;
|
||||
/* FIXME build inverted map up front so no quadratic waste here */
|
||||
for (auto & r : refs.refs)
|
||||
for (auto & [o, p] : scratchOutputs)
|
||||
if (r == p)
|
||||
referencedOutputs.insert(o);
|
||||
return referencedOutputs;
|
||||
},
|
||||
},
|
||||
*orifu);
|
||||
}},
|
||||
{[&](const std::string & path, const std::string & parent) {
|
||||
// TODO with more -vvvv also show the temporary paths for manual inspection.
|
||||
return BuildError(
|
||||
BuildResult::Failure::OutputRejected,
|
||||
"cycle detected in build of '%s' in the references of output '%s' from output '%s'",
|
||||
store.printStorePath(drvPath),
|
||||
path,
|
||||
parent);
|
||||
}});
|
||||
"cycle detected in build of '%s' in the references of output '%s' from output '%s'",
|
||||
store.printStorePath(drvPath),
|
||||
cycle.path,
|
||||
cycle.parent);
|
||||
},
|
||||
[](auto & sorted) { return sorted; }},
|
||||
topoSortResult);
|
||||
|
||||
std::reverse(sortedOutputNames.begin(), sortedOutputNames.end());
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue