@@ -1396,8 +1396,18 @@ SingleDrvOutputs DerivationBuilderImpl::registerOutputs()
13961396 struct PerhapsNeedToRegister
13971397 {
13981398 StorePathSet refs;
1399+ /* *
1400+ * References to other outputs. Built by looking up in
1401+ * `scratchOutputsInverse`.
1402+ */
1403+ StringSet otherOutputs;
13991404 };
14001405
1406+ /* inverse map of scratchOutputs for efficient lookup */
1407+ std::map<StorePath, std::string> scratchOutputsInverse;
1408+ for (auto & [outputName, path] : scratchOutputs)
1409+ scratchOutputsInverse.insert_or_assign (path, outputName);
1410+
14011411 std::map<std::string, std::variant<AlreadyRegistered, PerhapsNeedToRegister>> outputReferencesIfUnregistered;
14021412 std::map<std::string, struct stat > outputStats;
14031413 for (auto & [outputName, _] : drv.outputs ) {
@@ -1466,36 +1476,40 @@ SingleDrvOutputs DerivationBuilderImpl::registerOutputs()
14661476 references = scanForReferences (blank, actualPath, referenceablePaths);
14671477 }
14681478
1469- outputReferencesIfUnregistered.insert_or_assign (outputName, PerhapsNeedToRegister{.refs = references});
1479+ StringSet referencedOutputs;
1480+ for (auto & r : references)
1481+ if (auto * o = get (scratchOutputsInverse, r))
1482+ referencedOutputs.insert (*o);
1483+
1484+ outputReferencesIfUnregistered.insert_or_assign (
1485+ outputName,
1486+ PerhapsNeedToRegister{
1487+ .refs = references,
1488+ .otherOutputs = referencedOutputs,
1489+ });
14701490 outputStats.insert_or_assign (outputName, std::move (st));
14711491 }
14721492
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- }});
1493+ StringSet emptySet;
1494+
1495+ auto topoSortResult = topoSort (outputsToSort, [&](const std::string & name) -> const StringSet & {
1496+ auto * orifu = get (outputReferencesIfUnregistered, name);
1497+ if (!orifu)
1498+ throw BuildError (
1499+ BuildResult::Failure::OutputRejected,
1500+ " no output reference for '%s' in build of '%s'" ,
1501+ name,
1502+ store.printStorePath (drvPath));
1503+ return std::visit (
1504+ overloaded{
1505+ /* Since we'll use the already installed versions of these, we
1506+ can treat them as leaves and ignore any references they
1507+ have. */
1508+ [&](const AlreadyRegistered &) -> const StringSet & { return emptySet; },
1509+ [&](const PerhapsNeedToRegister & refs) -> const StringSet & { return refs.otherOutputs ; },
1510+ },
1511+ *orifu);
1512+ });
14991513
15001514 auto sortedOutputNames = std::visit (
15011515 overloaded{
0 commit comments