Skip to content

Commit 7503062

Browse files
authored
Merge pull request #14479 from lovesegfault/topo-sort-handle-cycles
refactor(libutil/topo-sort): return variant instead of throwing
2 parents 68a5110 + 182ae39 commit 7503062

File tree

6 files changed

+427
-77
lines changed

6 files changed

+427
-77
lines changed

src/libstore/local-store.cc

Lines changed: 16 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -989,19 +989,22 @@ void LocalStore::registerValidPaths(const ValidPathInfos & infos)
989989
error if a cycle is detected and roll back the
990990
transaction. Cycles can only occur when a derivation
991991
has multiple outputs. */
992-
topoSort(
993-
paths,
994-
{[&](const StorePath & path) {
995-
auto i = infos.find(path);
996-
return i == infos.end() ? StorePathSet() : i->second.references;
997-
}},
998-
{[&](const StorePath & path, const StorePath & parent) {
999-
return BuildError(
1000-
BuildResult::Failure::OutputRejected,
1001-
"cycle detected in the references of '%s' from '%s'",
1002-
printStorePath(path),
1003-
printStorePath(parent));
1004-
}});
992+
auto topoSortResult = topoSort(paths, {[&](const StorePath & path) {
993+
auto i = infos.find(path);
994+
return i == infos.end() ? StorePathSet() : i->second.references;
995+
}});
996+
997+
std::visit(
998+
overloaded{
999+
[&](const Cycle<StorePath> & cycle) {
1000+
throw BuildError(
1001+
BuildResult::Failure::OutputRejected,
1002+
"cycle detected in the references of '%s' from '%s'",
1003+
printStorePath(cycle.path),
1004+
printStorePath(cycle.parent));
1005+
},
1006+
[](auto &) { /* Success, continue */ }},
1007+
topoSortResult);
10051008

10061009
txn.commit();
10071010
});

src/libstore/misc.cc

Lines changed: 19 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -311,22 +311,25 @@ MissingPaths Store::queryMissing(const std::vector<DerivedPath> & targets)
311311

312312
StorePaths Store::topoSortPaths(const StorePathSet & paths)
313313
{
314-
return topoSort(
315-
paths,
316-
{[&](const StorePath & path) {
317-
try {
318-
return queryPathInfo(path)->references;
319-
} catch (InvalidPath &) {
320-
return StorePathSet();
321-
}
322-
}},
323-
{[&](const StorePath & path, const StorePath & parent) {
324-
return BuildError(
325-
BuildResult::Failure::OutputRejected,
326-
"cycle detected in the references of '%s' from '%s'",
327-
printStorePath(path),
328-
printStorePath(parent));
329-
}});
314+
auto result = topoSort(paths, {[&](const StorePath & path) {
315+
try {
316+
return queryPathInfo(path)->references;
317+
} catch (InvalidPath &) {
318+
return StorePathSet();
319+
}
320+
}});
321+
322+
return std::visit(
323+
overloaded{
324+
[&](const Cycle<StorePath> & cycle) -> StorePaths {
325+
throw BuildError(
326+
BuildResult::Failure::OutputRejected,
327+
"cycle detected in the references of '%s' from '%s'",
328+
printStorePath(cycle.path),
329+
printStorePath(cycle.parent));
330+
},
331+
[](const auto & sorted) { return sorted; }},
332+
result);
330333
}
331334

332335
std::map<DrvOutput, StorePath>

src/libstore/unix/build/derivation-builder.cc

Lines changed: 38 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1470,43 +1470,46 @@ SingleDrvOutputs DerivationBuilderImpl::registerOutputs()
14701470
outputStats.insert_or_assign(outputName, std::move(st));
14711471
}
14721472

1473-
auto sortedOutputNames = topoSort(
1474-
outputsToSort,
1475-
{[&](const std::string & name) {
1476-
auto orifu = get(outputReferencesIfUnregistered, name);
1477-
if (!orifu)
1473+
auto topoSortResult = topoSort(outputsToSort, {[&](const std::string & name) {
1474+
auto orifu = get(outputReferencesIfUnregistered, name);
1475+
if (!orifu)
1476+
throw BuildError(
1477+
BuildResult::Failure::OutputRejected,
1478+
"no output reference for '%s' in build of '%s'",
1479+
name,
1480+
store.printStorePath(drvPath));
1481+
return std::visit(
1482+
overloaded{
1483+
/* Since we'll use the already installed versions of these, we
1484+
can treat them as leaves and ignore any references they
1485+
have. */
1486+
[&](const AlreadyRegistered &) { return StringSet{}; },
1487+
[&](const PerhapsNeedToRegister & refs) {
1488+
StringSet referencedOutputs;
1489+
/* FIXME build inverted map up front so no quadratic waste here */
1490+
for (auto & r : refs.refs)
1491+
for (auto & [o, p] : scratchOutputs)
1492+
if (r == p)
1493+
referencedOutputs.insert(o);
1494+
return referencedOutputs;
1495+
},
1496+
},
1497+
*orifu);
1498+
}});
1499+
1500+
auto sortedOutputNames = std::visit(
1501+
overloaded{
1502+
[&](Cycle<std::string> & cycle) -> std::vector<std::string> {
1503+
// TODO with more -vvvv also show the temporary paths for manual inspection.
14781504
throw BuildError(
14791505
BuildResult::Failure::OutputRejected,
1480-
"no output reference for '%s' in build of '%s'",
1481-
name,
1482-
store.printStorePath(drvPath));
1483-
return std::visit(
1484-
overloaded{
1485-
/* Since we'll use the already installed versions of these, we
1486-
can treat them as leaves and ignore any references they
1487-
have. */
1488-
[&](const AlreadyRegistered &) { return StringSet{}; },
1489-
[&](const PerhapsNeedToRegister & refs) {
1490-
StringSet referencedOutputs;
1491-
/* FIXME build inverted map up front so no quadratic waste here */
1492-
for (auto & r : refs.refs)
1493-
for (auto & [o, p] : scratchOutputs)
1494-
if (r == p)
1495-
referencedOutputs.insert(o);
1496-
return referencedOutputs;
1497-
},
1498-
},
1499-
*orifu);
1500-
}},
1501-
{[&](const std::string & path, const std::string & parent) {
1502-
// TODO with more -vvvv also show the temporary paths for manual inspection.
1503-
return BuildError(
1504-
BuildResult::Failure::OutputRejected,
1505-
"cycle detected in build of '%s' in the references of output '%s' from output '%s'",
1506-
store.printStorePath(drvPath),
1507-
path,
1508-
parent);
1509-
}});
1506+
"cycle detected in build of '%s' in the references of output '%s' from output '%s'",
1507+
store.printStorePath(drvPath),
1508+
cycle.path,
1509+
cycle.parent);
1510+
},
1511+
[](auto & sorted) { return sorted; }},
1512+
topoSortResult);
15101513

15111514
std::reverse(sortedOutputNames.begin(), sortedOutputNames.end());
15121515

src/libutil-tests/meson.build

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ sources = files(
7474
'strings.cc',
7575
'suggestions.cc',
7676
'terminal.cc',
77+
'topo-sort.cc',
7778
'url.cc',
7879
'util.cc',
7980
'xml-writer.cc',

0 commit comments

Comments
 (0)