mirror of
https://github.com/NixOS/nix
synced 2025-06-24 22:11:15 +02:00
Restore dynamic derivations!
This method does *not* create a new type of goal. We instead just make
`DerivationGoal` more sophisticated, which is much easier to do now that
`DerivationBuildingGoal` has been split from it (and so many fields are
gone, or or local variables instead).
This avoids the need for a secondarily trampoline goal that interacted
poorly with `addWantedOutputs`. That, I hope, will mean the bugs from
before do not reappear.
There may in fact be a reason to introduce such a trampoline in the
future, but it would only happen in conjunction with getting rid of
`addWantedOutputs`.
Restores the functionality (and tests) that was reverted in
f4f28cdd0e
.
This commit is contained in:
parent
bf5d544d3b
commit
57348b677b
12 changed files with 145 additions and 77 deletions
|
@ -296,7 +296,7 @@ Goal::Co DerivationBuildingGoal::gaveUpOnSubstitution()
|
||||||
|
|
||||||
// FIXME wanted outputs
|
// FIXME wanted outputs
|
||||||
auto resolvedDrvGoal = worker.makeDerivationGoal(
|
auto resolvedDrvGoal = worker.makeDerivationGoal(
|
||||||
pathResolved, OutputsSpec::All{}, buildMode);
|
makeConstantStorePathRef(pathResolved), OutputsSpec::All{}, buildMode);
|
||||||
{
|
{
|
||||||
Goals waitees{resolvedDrvGoal};
|
Goals waitees{resolvedDrvGoal};
|
||||||
co_await await(std::move(waitees));
|
co_await await(std::move(waitees));
|
||||||
|
@ -338,7 +338,7 @@ Goal::Co DerivationBuildingGoal::gaveUpOnSubstitution()
|
||||||
|
|
||||||
throw Error(
|
throw Error(
|
||||||
"derivation '%s' doesn't have expected output '%s' (derivation-goal.cc/realisation)",
|
"derivation '%s' doesn't have expected output '%s' (derivation-goal.cc/realisation)",
|
||||||
worker.store.printStorePath(resolvedDrvGoal->drvPath), outputName);
|
resolvedDrvGoal->drvReq->to_string(worker.store), outputName);
|
||||||
}();
|
}();
|
||||||
|
|
||||||
if (!drv->type().isImpure()) {
|
if (!drv->type().isImpure()) {
|
||||||
|
|
|
@ -24,16 +24,16 @@
|
||||||
|
|
||||||
namespace nix {
|
namespace nix {
|
||||||
|
|
||||||
DerivationGoal::DerivationGoal(const StorePath & drvPath,
|
DerivationGoal::DerivationGoal(ref<const SingleDerivedPath> drvReq,
|
||||||
const OutputsSpec & wantedOutputs, Worker & worker, BuildMode buildMode)
|
const OutputsSpec & wantedOutputs, Worker & worker, BuildMode buildMode)
|
||||||
: Goal(worker, loadDerivation())
|
: Goal(worker, loadDerivation())
|
||||||
, drvPath(drvPath)
|
, drvReq(drvReq)
|
||||||
, wantedOutputs(wantedOutputs)
|
, wantedOutputs(wantedOutputs)
|
||||||
, buildMode(buildMode)
|
, buildMode(buildMode)
|
||||||
{
|
{
|
||||||
name = fmt(
|
name = fmt(
|
||||||
"building of '%s' from .drv file",
|
"building of '%s' from .drv file",
|
||||||
DerivedPath::Built { makeConstantStorePathRef(drvPath), wantedOutputs }.to_string(worker.store));
|
DerivedPath::Built { drvReq, wantedOutputs }.to_string(worker.store));
|
||||||
trace("created");
|
trace("created");
|
||||||
|
|
||||||
mcExpectedBuilds = std::make_unique<MaintainCount<uint64_t>>(worker.expectedBuilds);
|
mcExpectedBuilds = std::make_unique<MaintainCount<uint64_t>>(worker.expectedBuilds);
|
||||||
|
@ -43,8 +43,8 @@ DerivationGoal::DerivationGoal(const StorePath & drvPath,
|
||||||
|
|
||||||
DerivationGoal::DerivationGoal(const StorePath & drvPath, const BasicDerivation & drv,
|
DerivationGoal::DerivationGoal(const StorePath & drvPath, const BasicDerivation & drv,
|
||||||
const OutputsSpec & wantedOutputs, Worker & worker, BuildMode buildMode)
|
const OutputsSpec & wantedOutputs, Worker & worker, BuildMode buildMode)
|
||||||
: Goal(worker, haveDerivation())
|
: Goal(worker, haveDerivation(drvPath))
|
||||||
, drvPath(drvPath)
|
, drvReq(makeConstantStorePathRef(drvPath))
|
||||||
, wantedOutputs(wantedOutputs)
|
, wantedOutputs(wantedOutputs)
|
||||||
, buildMode(buildMode)
|
, buildMode(buildMode)
|
||||||
{
|
{
|
||||||
|
@ -52,7 +52,7 @@ DerivationGoal::DerivationGoal(const StorePath & drvPath, const BasicDerivation
|
||||||
|
|
||||||
name = fmt(
|
name = fmt(
|
||||||
"building of '%s' from in-memory derivation",
|
"building of '%s' from in-memory derivation",
|
||||||
DerivedPath::Built { makeConstantStorePathRef(drvPath), drv.outputNames() }.to_string(worker.store));
|
DerivedPath::Built { drvReq, drv.outputNames() }.to_string(worker.store));
|
||||||
trace("created");
|
trace("created");
|
||||||
|
|
||||||
mcExpectedBuilds = std::make_unique<MaintainCount<uint64_t>>(worker.expectedBuilds);
|
mcExpectedBuilds = std::make_unique<MaintainCount<uint64_t>>(worker.expectedBuilds);
|
||||||
|
@ -61,13 +61,24 @@ DerivationGoal::DerivationGoal(const StorePath & drvPath, const BasicDerivation
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static StorePath pathPartOfReq(const SingleDerivedPath & req)
|
||||||
|
{
|
||||||
|
return std::visit(
|
||||||
|
overloaded{
|
||||||
|
[&](const SingleDerivedPath::Opaque & bo) { return bo.path; },
|
||||||
|
[&](const SingleDerivedPath::Built & bfd) { return pathPartOfReq(*bfd.drvPath); },
|
||||||
|
},
|
||||||
|
req.raw());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
std::string DerivationGoal::key()
|
std::string DerivationGoal::key()
|
||||||
{
|
{
|
||||||
/* Ensure that derivations get built in order of their name,
|
/* Ensure that derivations get built in order of their name,
|
||||||
i.e. a derivation named "aardvark" always comes before
|
i.e. a derivation named "aardvark" always comes before
|
||||||
"baboon". And substitution goals always happen before
|
"baboon". And substitution goals always happen before
|
||||||
derivation goals (due to "b$"). */
|
derivation goals (due to "b$"). */
|
||||||
return "b$" + std::string(drvPath.name()) + "$" + worker.store.printStorePath(drvPath);
|
return "b$" + std::string(pathPartOfReq(*drvReq).name()) + "$" + drvReq->to_string(worker.store);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -93,24 +104,46 @@ void DerivationGoal::addWantedOutputs(const OutputsSpec & outputs)
|
||||||
|
|
||||||
|
|
||||||
Goal::Co DerivationGoal::loadDerivation() {
|
Goal::Co DerivationGoal::loadDerivation() {
|
||||||
trace("local derivation");
|
trace("need to load derivation from file");
|
||||||
|
|
||||||
{
|
{
|
||||||
/* The first thing to do is to make sure that the derivation
|
/* The first thing to do is to make sure that the derivation
|
||||||
exists. If it doesn't, it may be created through a
|
exists. If it doesn't, it may be built from another
|
||||||
substitute. */
|
derivation, or merely substituted. We can make goal to get it
|
||||||
|
and not worry about which method it takes to get the
|
||||||
|
derivation. */
|
||||||
|
|
||||||
if (buildMode != bmNormal || !worker.evalStore.isValidPath(drvPath)) {
|
if (auto optDrvPath = [this]() -> std::optional<StorePath> {
|
||||||
Goals waitees{upcast_goal(worker.makePathSubstitutionGoal(drvPath))};
|
if (buildMode != bmNormal)
|
||||||
|
return std::nullopt;
|
||||||
|
|
||||||
|
auto drvPath = StorePath::dummy;
|
||||||
|
try {
|
||||||
|
drvPath = resolveDerivedPath(worker.store, *drvReq);
|
||||||
|
} catch (MissingRealisation &) {
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
auto cond = worker.evalStore.isValidPath(drvPath) || worker.store.isValidPath(drvPath);
|
||||||
|
return cond ? std::optional{drvPath} : std::nullopt;
|
||||||
|
}()) {
|
||||||
|
trace(
|
||||||
|
fmt("already have drv '%s' for '%s', can go straight to building",
|
||||||
|
worker.store.printStorePath(*optDrvPath),
|
||||||
|
drvReq->to_string(worker.store)));
|
||||||
|
} else {
|
||||||
|
trace("need to obtain drv we want to build");
|
||||||
|
Goals waitees{worker.makeGoal(DerivedPath::fromSingle(*drvReq))};
|
||||||
co_await await(std::move(waitees));
|
co_await await(std::move(waitees));
|
||||||
}
|
}
|
||||||
|
|
||||||
trace("loading derivation");
|
trace("loading derivation");
|
||||||
|
|
||||||
if (nrFailed != 0) {
|
if (nrFailed != 0) {
|
||||||
co_return done(BuildResult::MiscFailure, {}, Error("cannot build missing derivation '%s'", worker.store.printStorePath(drvPath)));
|
co_return amDone(ecFailed, Error("cannot build missing derivation '%s'", drvReq->to_string(worker.store)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
StorePath drvPath = resolveDerivedPath(worker.store, *drvReq);
|
||||||
|
|
||||||
/* `drvPath' should already be a root, but let's be on the safe
|
/* `drvPath' should already be a root, but let's be on the safe
|
||||||
side: if the user forgot to make it a root, we wouldn't want
|
side: if the user forgot to make it a root, we wouldn't want
|
||||||
things being garbage collected while we're busy. */
|
things being garbage collected while we're busy. */
|
||||||
|
@ -129,13 +162,13 @@ Goal::Co DerivationGoal::loadDerivation() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
assert(drv);
|
assert(drv);
|
||||||
}
|
|
||||||
|
|
||||||
co_return haveDerivation();
|
co_return haveDerivation(drvPath);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Goal::Co DerivationGoal::haveDerivation()
|
Goal::Co DerivationGoal::haveDerivation(StorePath drvPath)
|
||||||
{
|
{
|
||||||
trace("have derivation");
|
trace("have derivation");
|
||||||
|
|
||||||
|
@ -221,11 +254,11 @@ Goal::Co DerivationGoal::haveDerivation()
|
||||||
|
|
||||||
{
|
{
|
||||||
/* Check what outputs paths are not already valid. */
|
/* Check what outputs paths are not already valid. */
|
||||||
auto [allValid, validOutputs] = checkPathValidity();
|
auto [allValid, validOutputs] = checkPathValidity(drvPath);
|
||||||
|
|
||||||
/* If they are all valid, then we're done. */
|
/* If they are all valid, then we're done. */
|
||||||
if (allValid && buildMode == bmNormal) {
|
if (allValid && buildMode == bmNormal) {
|
||||||
co_return done(BuildResult::AlreadyValid, std::move(validOutputs));
|
co_return done(drvPath, BuildResult::AlreadyValid, std::move(validOutputs));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -262,7 +295,7 @@ Goal::Co DerivationGoal::haveDerivation()
|
||||||
assert(!drv->type().isImpure());
|
assert(!drv->type().isImpure());
|
||||||
|
|
||||||
if (nrFailed > 0 && nrFailed > nrNoSubstituters && !settings.tryFallback) {
|
if (nrFailed > 0 && nrFailed > nrNoSubstituters && !settings.tryFallback) {
|
||||||
co_return done(BuildResult::TransientFailure, {},
|
co_return done(drvPath, BuildResult::TransientFailure, {},
|
||||||
Error("some substitutes for the outputs of derivation '%s' failed (usually happens due to networking issues); try '--fallback' to build derivation from source ",
|
Error("some substitutes for the outputs of derivation '%s' failed (usually happens due to networking issues); try '--fallback' to build derivation from source ",
|
||||||
worker.store.printStorePath(drvPath)));
|
worker.store.printStorePath(drvPath)));
|
||||||
}
|
}
|
||||||
|
@ -271,16 +304,16 @@ Goal::Co DerivationGoal::haveDerivation()
|
||||||
|
|
||||||
if (needRestart == NeedRestartForMoreOutputs::OutputsAddedDoNeed) {
|
if (needRestart == NeedRestartForMoreOutputs::OutputsAddedDoNeed) {
|
||||||
needRestart = NeedRestartForMoreOutputs::OutputsUnmodifedDontNeed;
|
needRestart = NeedRestartForMoreOutputs::OutputsUnmodifedDontNeed;
|
||||||
co_return haveDerivation();
|
co_return haveDerivation(std::move(drvPath));
|
||||||
}
|
}
|
||||||
|
|
||||||
auto [allValid, validOutputs] = checkPathValidity();
|
auto [allValid, validOutputs] = checkPathValidity(drvPath);
|
||||||
|
|
||||||
if (buildMode == bmNormal && allValid) {
|
if (buildMode == bmNormal && allValid) {
|
||||||
co_return done(BuildResult::Substituted, std::move(validOutputs));
|
co_return done(drvPath, BuildResult::Substituted, std::move(validOutputs));
|
||||||
}
|
}
|
||||||
if (buildMode == bmRepair && allValid) {
|
if (buildMode == bmRepair && allValid) {
|
||||||
co_return repairClosure();
|
co_return repairClosure(std::move(drvPath));
|
||||||
}
|
}
|
||||||
if (buildMode == bmCheck && !allValid)
|
if (buildMode == bmCheck && !allValid)
|
||||||
throw Error("some outputs of '%s' are not valid, so checking is not possible",
|
throw Error("some outputs of '%s' are not valid, so checking is not possible",
|
||||||
|
@ -303,7 +336,7 @@ struct value_comparison
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
Goal::Co DerivationGoal::repairClosure()
|
Goal::Co DerivationGoal::repairClosure(StorePath drvPath)
|
||||||
{
|
{
|
||||||
assert(!drv->type().isImpure());
|
assert(!drv->type().isImpure());
|
||||||
|
|
||||||
|
@ -313,7 +346,7 @@ Goal::Co DerivationGoal::repairClosure()
|
||||||
that produced those outputs. */
|
that produced those outputs. */
|
||||||
|
|
||||||
/* Get the output closure. */
|
/* Get the output closure. */
|
||||||
auto outputs = queryDerivationOutputMap();
|
auto outputs = queryDerivationOutputMap(drvPath);
|
||||||
StorePathSet outputClosure;
|
StorePathSet outputClosure;
|
||||||
for (auto & i : outputs) {
|
for (auto & i : outputs) {
|
||||||
if (!wantedOutputs.contains(i.first)) continue;
|
if (!wantedOutputs.contains(i.first)) continue;
|
||||||
|
@ -371,11 +404,11 @@ Goal::Co DerivationGoal::repairClosure()
|
||||||
throw Error("some paths in the output closure of derivation '%s' could not be repaired",
|
throw Error("some paths in the output closure of derivation '%s' could not be repaired",
|
||||||
worker.store.printStorePath(drvPath));
|
worker.store.printStorePath(drvPath));
|
||||||
}
|
}
|
||||||
co_return done(BuildResult::AlreadyValid, assertPathValidity());
|
co_return done(drvPath, BuildResult::AlreadyValid, assertPathValidity(drvPath));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
std::map<std::string, std::optional<StorePath>> DerivationGoal::queryPartialDerivationOutputMap()
|
std::map<std::string, std::optional<StorePath>> DerivationGoal::queryPartialDerivationOutputMap(const StorePath & drvPath)
|
||||||
{
|
{
|
||||||
assert(!drv->type().isImpure());
|
assert(!drv->type().isImpure());
|
||||||
|
|
||||||
|
@ -391,7 +424,7 @@ std::map<std::string, std::optional<StorePath>> DerivationGoal::queryPartialDeri
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
OutputPathMap DerivationGoal::queryDerivationOutputMap()
|
OutputPathMap DerivationGoal::queryDerivationOutputMap(const StorePath & drvPath)
|
||||||
{
|
{
|
||||||
assert(!drv->type().isImpure());
|
assert(!drv->type().isImpure());
|
||||||
|
|
||||||
|
@ -407,7 +440,7 @@ OutputPathMap DerivationGoal::queryDerivationOutputMap()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
std::pair<bool, SingleDrvOutputs> DerivationGoal::checkPathValidity()
|
std::pair<bool, SingleDrvOutputs> DerivationGoal::checkPathValidity(const StorePath & drvPath)
|
||||||
{
|
{
|
||||||
if (drv->type().isImpure()) return { false, {} };
|
if (drv->type().isImpure()) return { false, {} };
|
||||||
|
|
||||||
|
@ -422,7 +455,7 @@ std::pair<bool, SingleDrvOutputs> DerivationGoal::checkPathValidity()
|
||||||
}, wantedOutputs.raw);
|
}, wantedOutputs.raw);
|
||||||
SingleDrvOutputs validOutputs;
|
SingleDrvOutputs validOutputs;
|
||||||
|
|
||||||
for (auto & i : queryPartialDerivationOutputMap()) {
|
for (auto & i : queryPartialDerivationOutputMap(drvPath)) {
|
||||||
auto initialOutput = get(initialOutputs, i.first);
|
auto initialOutput = get(initialOutputs, i.first);
|
||||||
if (!initialOutput)
|
if (!initialOutput)
|
||||||
// this is an invalid output, gets catched with (!wantedOutputsLeft.empty())
|
// this is an invalid output, gets catched with (!wantedOutputsLeft.empty())
|
||||||
|
@ -487,9 +520,9 @@ std::pair<bool, SingleDrvOutputs> DerivationGoal::checkPathValidity()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
SingleDrvOutputs DerivationGoal::assertPathValidity()
|
SingleDrvOutputs DerivationGoal::assertPathValidity(const StorePath & drvPath)
|
||||||
{
|
{
|
||||||
auto [allValid, validOutputs] = checkPathValidity();
|
auto [allValid, validOutputs] = checkPathValidity(drvPath);
|
||||||
if (!allValid)
|
if (!allValid)
|
||||||
throw Error("some outputs are unexpectedly invalid");
|
throw Error("some outputs are unexpectedly invalid");
|
||||||
return validOutputs;
|
return validOutputs;
|
||||||
|
@ -497,6 +530,7 @@ SingleDrvOutputs DerivationGoal::assertPathValidity()
|
||||||
|
|
||||||
|
|
||||||
Goal::Done DerivationGoal::done(
|
Goal::Done DerivationGoal::done(
|
||||||
|
const StorePath & drvPath,
|
||||||
BuildResult::Status status,
|
BuildResult::Status status,
|
||||||
SingleDrvOutputs builtOutputs,
|
SingleDrvOutputs builtOutputs,
|
||||||
std::optional<Error> ex)
|
std::optional<Error> ex)
|
||||||
|
|
|
@ -30,7 +30,7 @@ void Store::buildPaths(const std::vector<DerivedPath> & reqs, BuildMode buildMod
|
||||||
if (i->exitCode != Goal::ecSuccess) {
|
if (i->exitCode != Goal::ecSuccess) {
|
||||||
#ifndef _WIN32 // TODO Enable building on Windows
|
#ifndef _WIN32 // TODO Enable building on Windows
|
||||||
if (auto i2 = dynamic_cast<DerivationGoal *>(i.get()))
|
if (auto i2 = dynamic_cast<DerivationGoal *>(i.get()))
|
||||||
failed.insert(printStorePath(i2->drvPath));
|
failed.insert(i2->drvReq->to_string(*this));
|
||||||
else
|
else
|
||||||
#endif
|
#endif
|
||||||
if (auto i2 = dynamic_cast<PathSubstitutionGoal *>(i.get()))
|
if (auto i2 = dynamic_cast<PathSubstitutionGoal *>(i.get()))
|
||||||
|
|
|
@ -54,11 +54,11 @@ std::shared_ptr<G> Worker::initGoalIfNeeded(std::weak_ptr<G> & goal_weak, Args &
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<DerivationGoal> Worker::makeDerivationGoalCommon(
|
std::shared_ptr<DerivationGoal> Worker::makeDerivationGoalCommon(
|
||||||
const StorePath & drvPath,
|
ref<const SingleDerivedPath> drvReq,
|
||||||
const OutputsSpec & wantedOutputs,
|
const OutputsSpec & wantedOutputs,
|
||||||
std::function<std::shared_ptr<DerivationGoal>()> mkDrvGoal)
|
std::function<std::shared_ptr<DerivationGoal>()> mkDrvGoal)
|
||||||
{
|
{
|
||||||
std::weak_ptr<DerivationGoal> & goal_weak = derivationGoals[drvPath];
|
std::weak_ptr<DerivationGoal> & goal_weak = derivationGoals.ensureSlot(*drvReq).value;
|
||||||
std::shared_ptr<DerivationGoal> goal = goal_weak.lock();
|
std::shared_ptr<DerivationGoal> goal = goal_weak.lock();
|
||||||
if (!goal) {
|
if (!goal) {
|
||||||
goal = mkDrvGoal();
|
goal = mkDrvGoal();
|
||||||
|
@ -71,18 +71,18 @@ std::shared_ptr<DerivationGoal> Worker::makeDerivationGoalCommon(
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
std::shared_ptr<DerivationGoal> Worker::makeDerivationGoal(const StorePath & drvPath,
|
std::shared_ptr<DerivationGoal> Worker::makeDerivationGoal(ref<const SingleDerivedPath> drvReq,
|
||||||
const OutputsSpec & wantedOutputs, BuildMode buildMode)
|
const OutputsSpec & wantedOutputs, BuildMode buildMode)
|
||||||
{
|
{
|
||||||
return makeDerivationGoalCommon(drvPath, wantedOutputs, [&]() -> std::shared_ptr<DerivationGoal> {
|
return makeDerivationGoalCommon(drvReq, wantedOutputs, [&]() -> std::shared_ptr<DerivationGoal> {
|
||||||
return std::make_shared<DerivationGoal>(drvPath, wantedOutputs, *this, buildMode);
|
return std::make_shared<DerivationGoal>(drvReq, wantedOutputs, *this, buildMode);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<DerivationGoal> Worker::makeBasicDerivationGoal(const StorePath & drvPath,
|
std::shared_ptr<DerivationGoal> Worker::makeBasicDerivationGoal(const StorePath & drvPath,
|
||||||
const BasicDerivation & drv, const OutputsSpec & wantedOutputs, BuildMode buildMode)
|
const BasicDerivation & drv, const OutputsSpec & wantedOutputs, BuildMode buildMode)
|
||||||
{
|
{
|
||||||
return makeDerivationGoalCommon(drvPath, wantedOutputs, [&]() -> std::shared_ptr<DerivationGoal> {
|
return makeDerivationGoalCommon(makeConstantStorePathRef(drvPath), wantedOutputs, [&]() -> std::shared_ptr<DerivationGoal> {
|
||||||
return std::make_shared<DerivationGoal>(drvPath, drv, wantedOutputs, *this, buildMode);
|
return std::make_shared<DerivationGoal>(drvPath, drv, wantedOutputs, *this, buildMode);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -118,10 +118,7 @@ GoalPtr Worker::makeGoal(const DerivedPath & req, BuildMode buildMode)
|
||||||
{
|
{
|
||||||
return std::visit(overloaded {
|
return std::visit(overloaded {
|
||||||
[&](const DerivedPath::Built & bfd) -> GoalPtr {
|
[&](const DerivedPath::Built & bfd) -> GoalPtr {
|
||||||
if (auto bop = std::get_if<DerivedPath::Opaque>(&*bfd.drvPath))
|
return makeDerivationGoal(bfd.drvPath, bfd.outputs, buildMode);
|
||||||
return makeDerivationGoal(bop->path, bfd.outputs, buildMode);
|
|
||||||
else
|
|
||||||
throw UnimplementedError("Building dynamic derivations in one shot is not yet implemented.");
|
|
||||||
},
|
},
|
||||||
[&](const DerivedPath::Opaque & bo) -> GoalPtr {
|
[&](const DerivedPath::Opaque & bo) -> GoalPtr {
|
||||||
return makePathSubstitutionGoal(bo.path, buildMode == bmRepair ? Repair : NoRepair);
|
return makePathSubstitutionGoal(bo.path, buildMode == bmRepair ? Repair : NoRepair);
|
||||||
|
@ -130,25 +127,45 @@ GoalPtr Worker::makeGoal(const DerivedPath & req, BuildMode buildMode)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template<typename K, typename V, typename F>
|
||||||
|
static void cullMap(std::map<K, V> & goalMap, F f)
|
||||||
|
{
|
||||||
|
for (auto i = goalMap.begin(); i != goalMap.end();)
|
||||||
|
if (!f(i->second))
|
||||||
|
i = goalMap.erase(i);
|
||||||
|
else ++i;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
template<typename K, typename G>
|
template<typename K, typename G>
|
||||||
static void removeGoal(std::shared_ptr<G> goal, std::map<K, std::weak_ptr<G>> & goalMap)
|
static void removeGoal(std::shared_ptr<G> goal, std::map<K, std::weak_ptr<G>> & goalMap)
|
||||||
{
|
{
|
||||||
/* !!! inefficient */
|
/* !!! inefficient */
|
||||||
for (auto i = goalMap.begin();
|
cullMap(goalMap, [&](const std::weak_ptr<G> & gp) -> bool {
|
||||||
i != goalMap.end(); )
|
return gp.lock() != goal;
|
||||||
if (i->second.lock() == goal) {
|
});
|
||||||
auto j = i; ++j;
|
}
|
||||||
goalMap.erase(i);
|
|
||||||
i = j;
|
template<typename K>
|
||||||
}
|
static void removeGoal(std::shared_ptr<DerivationGoal> goal, std::map<K, DerivedPathMap<std::weak_ptr<DerivationGoal>>::ChildNode> & goalMap);
|
||||||
else ++i;
|
|
||||||
|
template<typename K>
|
||||||
|
static void removeGoal(std::shared_ptr<DerivationGoal> goal, std::map<K, DerivedPathMap<std::weak_ptr<DerivationGoal>>::ChildNode> & goalMap)
|
||||||
|
{
|
||||||
|
/* !!! inefficient */
|
||||||
|
cullMap(goalMap, [&](DerivedPathMap<std::weak_ptr<DerivationGoal>>::ChildNode & node) -> bool {
|
||||||
|
if (node.value.lock() == goal)
|
||||||
|
node.value.reset();
|
||||||
|
removeGoal(goal, node.childMap);
|
||||||
|
return !node.value.expired() || !node.childMap.empty();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void Worker::removeGoal(GoalPtr goal)
|
void Worker::removeGoal(GoalPtr goal)
|
||||||
{
|
{
|
||||||
if (auto drvGoal = std::dynamic_pointer_cast<DerivationGoal>(goal))
|
if (auto drvGoal = std::dynamic_pointer_cast<DerivationGoal>(goal))
|
||||||
nix::removeGoal(drvGoal, derivationGoals);
|
nix::removeGoal(drvGoal, derivationGoals.map);
|
||||||
else if (auto drvBuildingGoal = std::dynamic_pointer_cast<DerivationBuildingGoal>(goal))
|
else if (auto drvBuildingGoal = std::dynamic_pointer_cast<DerivationBuildingGoal>(goal))
|
||||||
nix::removeGoal(drvBuildingGoal, derivationBuildingGoals);
|
nix::removeGoal(drvBuildingGoal, derivationBuildingGoals);
|
||||||
else if (auto subGoal = std::dynamic_pointer_cast<PathSubstitutionGoal>(goal))
|
else if (auto subGoal = std::dynamic_pointer_cast<PathSubstitutionGoal>(goal))
|
||||||
|
@ -297,7 +314,7 @@ void Worker::run(const Goals & _topGoals)
|
||||||
topGoals.insert(i);
|
topGoals.insert(i);
|
||||||
if (auto goal = dynamic_cast<DerivationGoal *>(i.get())) {
|
if (auto goal = dynamic_cast<DerivationGoal *>(i.get())) {
|
||||||
topPaths.push_back(DerivedPath::Built {
|
topPaths.push_back(DerivedPath::Built {
|
||||||
.drvPath = makeConstantStorePathRef(goal->drvPath),
|
.drvPath = goal->drvReq,
|
||||||
.outputs = goal->wantedOutputs,
|
.outputs = goal->wantedOutputs,
|
||||||
});
|
});
|
||||||
} else
|
} else
|
||||||
|
|
|
@ -52,6 +52,7 @@ typename DerivedPathMap<V>::ChildNode * DerivedPathMap<V>::findSlot(const Single
|
||||||
|
|
||||||
// instantiations
|
// instantiations
|
||||||
|
|
||||||
|
#include "nix/store/build/derivation-goal.hh"
|
||||||
namespace nix {
|
namespace nix {
|
||||||
|
|
||||||
template<>
|
template<>
|
||||||
|
@ -68,4 +69,7 @@ std::strong_ordering DerivedPathMap<StringSet>::ChildNode::operator <=> (
|
||||||
template struct DerivedPathMap<StringSet>::ChildNode;
|
template struct DerivedPathMap<StringSet>::ChildNode;
|
||||||
template struct DerivedPathMap<StringSet>;
|
template struct DerivedPathMap<StringSet>;
|
||||||
|
|
||||||
|
template struct DerivedPathMap<std::weak_ptr<DerivationGoal>>;
|
||||||
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -27,7 +27,7 @@ void runPostBuildHook(
|
||||||
struct DerivationGoal : public Goal
|
struct DerivationGoal : public Goal
|
||||||
{
|
{
|
||||||
/** The path of the derivation. */
|
/** The path of the derivation. */
|
||||||
StorePath drvPath;
|
ref<const SingleDerivedPath> drvReq;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The specific outputs that we need to build.
|
* The specific outputs that we need to build.
|
||||||
|
@ -62,7 +62,7 @@ struct DerivationGoal : public Goal
|
||||||
NeedRestartForMoreOutputs needRestart = NeedRestartForMoreOutputs::OutputsUnmodifedDontNeed;
|
NeedRestartForMoreOutputs needRestart = NeedRestartForMoreOutputs::OutputsUnmodifedDontNeed;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The derivation stored at drvPath.
|
* The derivation stored at `drvReq`.
|
||||||
*/
|
*/
|
||||||
std::unique_ptr<Derivation> drv;
|
std::unique_ptr<Derivation> drv;
|
||||||
|
|
||||||
|
@ -76,7 +76,7 @@ struct DerivationGoal : public Goal
|
||||||
|
|
||||||
std::unique_ptr<MaintainCount<uint64_t>> mcExpectedBuilds;
|
std::unique_ptr<MaintainCount<uint64_t>> mcExpectedBuilds;
|
||||||
|
|
||||||
DerivationGoal(const StorePath & drvPath,
|
DerivationGoal(ref<const SingleDerivedPath> drvReq,
|
||||||
const OutputsSpec & wantedOutputs, Worker & worker,
|
const OutputsSpec & wantedOutputs, Worker & worker,
|
||||||
BuildMode buildMode = bmNormal);
|
BuildMode buildMode = bmNormal);
|
||||||
DerivationGoal(const StorePath & drvPath, const BasicDerivation & drv,
|
DerivationGoal(const StorePath & drvPath, const BasicDerivation & drv,
|
||||||
|
@ -97,15 +97,15 @@ struct DerivationGoal : public Goal
|
||||||
* The states.
|
* The states.
|
||||||
*/
|
*/
|
||||||
Co loadDerivation();
|
Co loadDerivation();
|
||||||
Co haveDerivation();
|
Co haveDerivation(StorePath drvPath);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Wrappers around the corresponding Store methods that first consult the
|
* Wrappers around the corresponding Store methods that first consult the
|
||||||
* derivation. This is currently needed because when there is no drv file
|
* derivation. This is currently needed because when there is no drv file
|
||||||
* there also is no DB entry.
|
* there also is no DB entry.
|
||||||
*/
|
*/
|
||||||
std::map<std::string, std::optional<StorePath>> queryPartialDerivationOutputMap();
|
std::map<std::string, std::optional<StorePath>> queryPartialDerivationOutputMap(const StorePath & drvPath);
|
||||||
OutputPathMap queryDerivationOutputMap();
|
OutputPathMap queryDerivationOutputMap(const StorePath & drvPath);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update 'initialOutputs' to determine the current status of the
|
* Update 'initialOutputs' to determine the current status of the
|
||||||
|
@ -113,17 +113,18 @@ struct DerivationGoal : public Goal
|
||||||
* whether all outputs are valid and non-corrupt, and a
|
* whether all outputs are valid and non-corrupt, and a
|
||||||
* 'SingleDrvOutputs' structure containing the valid outputs.
|
* 'SingleDrvOutputs' structure containing the valid outputs.
|
||||||
*/
|
*/
|
||||||
std::pair<bool, SingleDrvOutputs> checkPathValidity();
|
std::pair<bool, SingleDrvOutputs> checkPathValidity(const StorePath & drvPath);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Aborts if any output is not valid or corrupt, and otherwise
|
* Aborts if any output is not valid or corrupt, and otherwise
|
||||||
* returns a 'SingleDrvOutputs' structure containing all outputs.
|
* returns a 'SingleDrvOutputs' structure containing all outputs.
|
||||||
*/
|
*/
|
||||||
SingleDrvOutputs assertPathValidity();
|
SingleDrvOutputs assertPathValidity(const StorePath & drvPath);
|
||||||
|
|
||||||
Co repairClosure();
|
Co repairClosure(StorePath drvPath);
|
||||||
|
|
||||||
Done done(
|
Done done(
|
||||||
|
const StorePath & drvPath,
|
||||||
BuildResult::Status status,
|
BuildResult::Status status,
|
||||||
SingleDrvOutputs builtOutputs = {},
|
SingleDrvOutputs builtOutputs = {},
|
||||||
std::optional<Error> ex = {});
|
std::optional<Error> ex = {});
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
|
|
||||||
#include "nix/util/types.hh"
|
#include "nix/util/types.hh"
|
||||||
#include "nix/store/store-api.hh"
|
#include "nix/store/store-api.hh"
|
||||||
|
#include "nix/store/derived-path-map.hh"
|
||||||
#include "nix/store/build/goal.hh"
|
#include "nix/store/build/goal.hh"
|
||||||
#include "nix/store/realisation.hh"
|
#include "nix/store/realisation.hh"
|
||||||
#include "nix/util/muxable-pipe.hh"
|
#include "nix/util/muxable-pipe.hh"
|
||||||
|
@ -104,7 +105,9 @@ private:
|
||||||
* Maps used to prevent multiple instantiations of a goal for the
|
* Maps used to prevent multiple instantiations of a goal for the
|
||||||
* same derivation / path.
|
* same derivation / path.
|
||||||
*/
|
*/
|
||||||
std::map<StorePath, std::weak_ptr<DerivationGoal>> derivationGoals;
|
|
||||||
|
DerivedPathMap<std::weak_ptr<DerivationGoal>> derivationGoals;
|
||||||
|
|
||||||
std::map<StorePath, std::weak_ptr<DerivationBuildingGoal>> derivationBuildingGoals;
|
std::map<StorePath, std::weak_ptr<DerivationBuildingGoal>> derivationBuildingGoals;
|
||||||
std::map<StorePath, std::weak_ptr<PathSubstitutionGoal>> substitutionGoals;
|
std::map<StorePath, std::weak_ptr<PathSubstitutionGoal>> substitutionGoals;
|
||||||
std::map<DrvOutput, std::weak_ptr<DrvOutputSubstitutionGoal>> drvOutputSubstitutionGoals;
|
std::map<DrvOutput, std::weak_ptr<DrvOutputSubstitutionGoal>> drvOutputSubstitutionGoals;
|
||||||
|
@ -202,11 +205,11 @@ private:
|
||||||
std::shared_ptr<G> initGoalIfNeeded(std::weak_ptr<G> & goal_weak, Args && ...args);
|
std::shared_ptr<G> initGoalIfNeeded(std::weak_ptr<G> & goal_weak, Args && ...args);
|
||||||
|
|
||||||
std::shared_ptr<DerivationGoal> makeDerivationGoalCommon(
|
std::shared_ptr<DerivationGoal> makeDerivationGoalCommon(
|
||||||
const StorePath & drvPath, const OutputsSpec & wantedOutputs,
|
ref<const SingleDerivedPath> drvReq, const OutputsSpec & wantedOutputs,
|
||||||
std::function<std::shared_ptr<DerivationGoal>()> mkDrvGoal);
|
std::function<std::shared_ptr<DerivationGoal>()> mkDrvGoal);
|
||||||
public:
|
public:
|
||||||
std::shared_ptr<DerivationGoal> makeDerivationGoal(
|
std::shared_ptr<DerivationGoal> makeDerivationGoal(
|
||||||
const StorePath & drvPath,
|
ref<const SingleDerivedPath> drvReq,
|
||||||
const OutputsSpec & wantedOutputs, BuildMode buildMode = bmNormal);
|
const OutputsSpec & wantedOutputs, BuildMode buildMode = bmNormal);
|
||||||
std::shared_ptr<DerivationGoal> makeBasicDerivationGoal(
|
std::shared_ptr<DerivationGoal> makeBasicDerivationGoal(
|
||||||
const StorePath & drvPath, const BasicDerivation & drv,
|
const StorePath & drvPath, const BasicDerivation & drv,
|
||||||
|
|
|
@ -21,8 +21,11 @@ namespace nix {
|
||||||
*
|
*
|
||||||
* @param V A type to instantiate for each output. It should probably
|
* @param V A type to instantiate for each output. It should probably
|
||||||
* should be an "optional" type so not every interior node has to have a
|
* should be an "optional" type so not every interior node has to have a
|
||||||
* value. `* const Something` or `std::optional<Something>` would be
|
* value. For example, the scheduler uses
|
||||||
* good choices for "optional" types.
|
* `DerivedPathMap<std::weak_ptr<DerivationCreationAndRealisationGoal>>` to
|
||||||
|
* remember which goals correspond to which outputs. `* const Something`
|
||||||
|
* or `std::optional<Something>` would also be good choices for
|
||||||
|
* "optional" types.
|
||||||
*/
|
*/
|
||||||
template<typename V>
|
template<typename V>
|
||||||
struct DerivedPathMap {
|
struct DerivedPathMap {
|
||||||
|
|
|
@ -18,4 +18,9 @@ clearStore
|
||||||
|
|
||||||
drvDep=$(nix-instantiate ./text-hashed-output.nix -A producingDrv)
|
drvDep=$(nix-instantiate ./text-hashed-output.nix -A producingDrv)
|
||||||
|
|
||||||
expectStderr 1 nix build "${drvDep}^out^out" --no-link | grepQuiet "Building dynamic derivations in one shot is not yet implemented"
|
# Store layer needs bugfix
|
||||||
|
requireDaemonNewerThan "2.30pre20250515"
|
||||||
|
|
||||||
|
out2=$(nix build "${drvDep}^out^out" --no-link)
|
||||||
|
|
||||||
|
test $out1 == $out2
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
source common.sh
|
source common.sh
|
||||||
|
|
||||||
# Store layer needs bugfix
|
# Store layer needs bugfix
|
||||||
requireDaemonNewerThan "2.27pre20250205"
|
requireDaemonNewerThan "2.30pre20250515"
|
||||||
|
|
||||||
TODO_NixOS # can't enable a sandbox feature easily
|
TODO_NixOS # can't enable a sandbox feature easily
|
||||||
|
|
||||||
|
@ -13,4 +13,4 @@ restartDaemon
|
||||||
NIX_BIN_DIR="$(dirname "$(type -p nix)")"
|
NIX_BIN_DIR="$(dirname "$(type -p nix)")"
|
||||||
export NIX_BIN_DIR
|
export NIX_BIN_DIR
|
||||||
|
|
||||||
expectStderr 1 nix build -L --file ./non-trivial.nix --no-link | grepQuiet "Building dynamic derivations in one shot is not yet implemented"
|
nix build -L --file ./non-trivial.nix --no-link
|
||||||
|
|
|
@ -4,8 +4,11 @@ source common.sh
|
||||||
|
|
||||||
out1=$(nix-build ./text-hashed-output.nix -A hello --no-out-link)
|
out1=$(nix-build ./text-hashed-output.nix -A hello --no-out-link)
|
||||||
|
|
||||||
|
# Store layer needs bugfix
|
||||||
|
requireDaemonNewerThan "2.30pre20250515"
|
||||||
|
|
||||||
clearStore
|
clearStore
|
||||||
|
|
||||||
expectStderr 1 nix-build ./text-hashed-output.nix -A wrapper --no-out-link | grepQuiet "Building dynamic derivations in one shot is not yet implemented"
|
out2=$(nix-build ./text-hashed-output.nix -A wrapper --no-out-link)
|
||||||
|
|
||||||
# diff -r $out1 $out2
|
diff -r $out1 $out2
|
||||||
|
|
|
@ -3,9 +3,7 @@
|
||||||
source common.sh
|
source common.sh
|
||||||
|
|
||||||
# Store layer needs bugfix
|
# Store layer needs bugfix
|
||||||
requireDaemonNewerThan "2.27pre20250205"
|
requireDaemonNewerThan "2.30pre20250515"
|
||||||
|
|
||||||
skipTest "dyn drv input scheduling had to be reverted for 2.27"
|
|
||||||
|
|
||||||
expected=100
|
expected=100
|
||||||
if [[ -v NIX_DAEMON_PACKAGE ]]; then expected=1; fi # work around the daemon not returning a 100 status correctly
|
if [[ -v NIX_DAEMON_PACKAGE ]]; then expected=1; fi # work around the daemon not returning a 100 status correctly
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue