Skip to content

Commit 625ed1c

Browse files
committed
Revert "Revert "Adapt scheduler to work with dynamic derivations""
This fixes dynamic derivations, reverting #9081. #9052 However will be reintroduced unless this is modified somehow. This reverts commit 8440afb.
1 parent c6b5503 commit 625ed1c

12 files changed

+390
-48
lines changed
Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
1+
#include "create-derivation-and-realise-goal.hh"
2+
#include "worker.hh"
3+
4+
namespace nix {
5+
6+
CreateDerivationAndRealiseGoal::CreateDerivationAndRealiseGoal(ref<SingleDerivedPath> drvReq,
7+
const OutputsSpec & wantedOutputs, Worker & worker, BuildMode buildMode)
8+
: Goal(worker, DerivedPath::Built { .drvPath = drvReq, .outputs = wantedOutputs })
9+
, drvReq(drvReq)
10+
, wantedOutputs(wantedOutputs)
11+
, buildMode(buildMode)
12+
{
13+
state = &CreateDerivationAndRealiseGoal::getDerivation;
14+
name = fmt(
15+
"outer obtaining drv from '%s' and then building outputs %s",
16+
drvReq->to_string(worker.store),
17+
std::visit(overloaded {
18+
[&](const OutputsSpec::All) -> std::string {
19+
return "* (all of them)";
20+
},
21+
[&](const OutputsSpec::Names os) {
22+
return concatStringsSep(", ", quoteStrings(os));
23+
},
24+
}, wantedOutputs.raw));
25+
trace("created outer");
26+
27+
worker.updateProgress();
28+
}
29+
30+
31+
CreateDerivationAndRealiseGoal::~CreateDerivationAndRealiseGoal()
32+
{
33+
}
34+
35+
36+
static StorePath pathPartOfReq(const SingleDerivedPath & req)
37+
{
38+
return std::visit(overloaded {
39+
[&](const SingleDerivedPath::Opaque & bo) {
40+
return bo.path;
41+
},
42+
[&](const SingleDerivedPath::Built & bfd) {
43+
return pathPartOfReq(*bfd.drvPath);
44+
},
45+
}, req.raw());
46+
}
47+
48+
49+
std::string CreateDerivationAndRealiseGoal::key()
50+
{
51+
/* Ensure that derivations get built in order of their name,
52+
i.e. a derivation named "aardvark" always comes before "baboon". And
53+
substitution goals and inner derivation goals always happen before
54+
derivation goals (due to "b$"). */
55+
return "c$" + std::string(pathPartOfReq(*drvReq).name()) + "$" + drvReq->to_string(worker.store);
56+
}
57+
58+
59+
void CreateDerivationAndRealiseGoal::timedOut(Error && ex)
60+
{
61+
}
62+
63+
64+
void CreateDerivationAndRealiseGoal::work()
65+
{
66+
(this->*state)();
67+
}
68+
69+
70+
void CreateDerivationAndRealiseGoal::addWantedOutputs(const OutputsSpec & outputs)
71+
{
72+
/* If we already want all outputs, there is nothing to do. */
73+
auto newWanted = wantedOutputs.union_(outputs);
74+
bool needRestart = !newWanted.isSubsetOf(wantedOutputs);
75+
wantedOutputs = newWanted;
76+
77+
if (!needRestart) return;
78+
79+
if (!optDrvPath)
80+
// haven't started steps where the outputs matter yet
81+
return;
82+
worker.makeDerivationGoal(*optDrvPath, outputs, buildMode);
83+
}
84+
85+
86+
void CreateDerivationAndRealiseGoal::getDerivation()
87+
{
88+
trace("outer init");
89+
90+
/* The first thing to do is to make sure that the derivation
91+
exists. If it doesn't, it may be created through a
92+
substitute. */
93+
if (auto optDrvPath = [this]() -> std::optional<StorePath> {
94+
if (buildMode != bmNormal) return std::nullopt;
95+
96+
auto drvPath = StorePath::dummy;
97+
try {
98+
drvPath = resolveDerivedPath(worker.store, *drvReq);
99+
} catch (MissingRealisation &) {
100+
return std::nullopt;
101+
}
102+
return worker.evalStore.isValidPath(drvPath) || worker.store.isValidPath(drvPath)
103+
? std::optional { drvPath }
104+
: std::nullopt;
105+
}()) {
106+
trace(fmt("already have drv '%s' for '%s', can go straight to building",
107+
worker.store.printStorePath(*optDrvPath),
108+
drvReq->to_string(worker.store)));
109+
110+
loadAndBuildDerivation();
111+
} else {
112+
trace("need to obtain drv we want to build");
113+
114+
addWaitee(worker.makeGoal(DerivedPath::fromSingle(*drvReq)));
115+
116+
state = &CreateDerivationAndRealiseGoal::loadAndBuildDerivation;
117+
if (waitees.empty()) work();
118+
}
119+
}
120+
121+
122+
void CreateDerivationAndRealiseGoal::loadAndBuildDerivation()
123+
{
124+
trace("outer load and build derivation");
125+
126+
if (nrFailed != 0) {
127+
amDone(ecFailed, Error("cannot build missing derivation '%s'", drvReq->to_string(worker.store)));
128+
return;
129+
}
130+
131+
StorePath drvPath = resolveDerivedPath(worker.store, *drvReq);
132+
/* Build this step! */
133+
concreteDrvGoal = worker.makeDerivationGoal(drvPath, wantedOutputs, buildMode);
134+
addWaitee(upcast_goal(concreteDrvGoal));
135+
state = &CreateDerivationAndRealiseGoal::buildDone;
136+
optDrvPath = std::move(drvPath);
137+
if (waitees.empty()) work();
138+
}
139+
140+
141+
void CreateDerivationAndRealiseGoal::buildDone()
142+
{
143+
trace("outer build done");
144+
145+
buildResult = upcast_goal(concreteDrvGoal)->getBuildResult(DerivedPath::Built {
146+
.drvPath = drvReq,
147+
.outputs = wantedOutputs,
148+
});
149+
150+
if (buildResult.success())
151+
amDone(ecSuccess);
152+
else
153+
amDone(ecFailed, Error("building '%s' failed", drvReq->to_string(worker.store)));
154+
}
155+
156+
157+
}
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
#pragma once
2+
3+
#include "parsed-derivations.hh"
4+
#include "lock.hh"
5+
#include "store-api.hh"
6+
#include "pathlocks.hh"
7+
#include "goal.hh"
8+
9+
namespace nix {
10+
11+
struct DerivationGoal;
12+
13+
/**
14+
* This goal type is essentially the serial composition (like function
15+
* composition) of a goal for getting a derivation, and then a
16+
* `DerivationGoal` using the newly-obtained derivation.
17+
*
18+
* In the (currently experimental) general inductive case of derivations
19+
* that are themselves build outputs, that first goal will be *another*
20+
* `CreateDerivationAndRealiseGoal`. In the (much more common) base-case
21+
* where the derivation has no provence and is just referred to by
22+
* (content-addressed) store path, that first goal is a
23+
* `SubstitutionGoal`.
24+
*
25+
* If we already have the derivation (e.g. if the evalutator has created
26+
* the derivation locally and then instructured the store to build it),
27+
* we can skip the first goal entirely as a small optimization.
28+
*/
29+
struct CreateDerivationAndRealiseGoal : public Goal
30+
{
31+
/**
32+
* How to obtain a store path of the derivation to build.
33+
*/
34+
ref<SingleDerivedPath> drvReq;
35+
36+
/**
37+
* The path of the derivation, once obtained.
38+
**/
39+
std::optional<StorePath> optDrvPath;
40+
41+
/**
42+
* The goal for the corresponding concrete derivation.
43+
**/
44+
std::shared_ptr<DerivationGoal> concreteDrvGoal;
45+
46+
/**
47+
* The specific outputs that we need to build.
48+
*/
49+
OutputsSpec wantedOutputs;
50+
51+
typedef void (CreateDerivationAndRealiseGoal::*GoalState)();
52+
GoalState state;
53+
54+
/**
55+
* The final output paths of the build.
56+
*
57+
* - For input-addressed derivations, always the precomputed paths
58+
*
59+
* - For content-addressed derivations, calcuated from whatever the
60+
* hash ends up being. (Note that fixed outputs derivations that
61+
* produce the "wrong" output still install that data under its
62+
* true content-address.)
63+
*/
64+
OutputPathMap finalOutputs;
65+
66+
BuildMode buildMode;
67+
68+
CreateDerivationAndRealiseGoal(ref<SingleDerivedPath> drvReq,
69+
const OutputsSpec & wantedOutputs, Worker & worker,
70+
BuildMode buildMode = bmNormal);
71+
virtual ~CreateDerivationAndRealiseGoal();
72+
73+
void timedOut(Error && ex) override;
74+
75+
std::string key() override;
76+
77+
void work() override;
78+
79+
/**
80+
* Add wanted outputs to an already existing derivation goal.
81+
*/
82+
void addWantedOutputs(const OutputsSpec & outputs);
83+
84+
/**
85+
* The states.
86+
*/
87+
void getDerivation();
88+
void loadAndBuildDerivation();
89+
void buildDone();
90+
91+
JobCategory jobCategory() const override {
92+
return JobCategory::Administration;
93+
};
94+
};
95+
96+
}

src/libstore/build/derivation-goal.cc

Lines changed: 8 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ DerivationGoal::DerivationGoal(const StorePath & drvPath,
4444
, wantedOutputs(wantedOutputs)
4545
, buildMode(buildMode)
4646
{
47-
state = &DerivationGoal::getDerivation;
47+
state = &DerivationGoal::loadDerivation;
4848
name = fmt(
4949
"building of '%s' from .drv file",
5050
DerivedPath::Built { makeConstantStorePathRef(drvPath), wantedOutputs }.to_string(worker.store));
@@ -139,24 +139,6 @@ void DerivationGoal::addWantedOutputs(const OutputsSpec & outputs)
139139
}
140140

141141

142-
void DerivationGoal::getDerivation()
143-
{
144-
trace("init");
145-
146-
/* The first thing to do is to make sure that the derivation
147-
exists. If it doesn't, it may be created through a
148-
substitute. */
149-
if (buildMode == bmNormal && worker.evalStore.isValidPath(drvPath)) {
150-
loadDerivation();
151-
return;
152-
}
153-
154-
addWaitee(upcast_goal(worker.makePathSubstitutionGoal(drvPath)));
155-
156-
state = &DerivationGoal::loadDerivation;
157-
}
158-
159-
160142
void DerivationGoal::loadDerivation()
161143
{
162144
trace("loading derivation");
@@ -1579,23 +1561,24 @@ void DerivationGoal::waiteeDone(GoalPtr waitee, ExitCode result)
15791561
if (!useDerivation) return;
15801562
auto & fullDrv = *dynamic_cast<Derivation *>(drv.get());
15811563

1582-
auto * dg = dynamic_cast<DerivationGoal *>(&*waitee);
1583-
if (!dg) return;
1564+
std::optional info = tryGetConcreteDrvGoal(waitee);
1565+
if (!info) return;
1566+
const auto & [dg, drvReq] = *info;
15841567

1585-
auto * nodeP = fullDrv.inputDrvs.findSlot(DerivedPath::Opaque { .path = dg->drvPath });
1568+
auto * nodeP = fullDrv.inputDrvs.findSlot(drvReq.get());
15861569
if (!nodeP) return;
15871570
auto & outputs = nodeP->value;
15881571

15891572
for (auto & outputName : outputs) {
1590-
auto buildResult = dg->getBuildResult(DerivedPath::Built {
1591-
.drvPath = makeConstantStorePathRef(dg->drvPath),
1573+
auto buildResult = dg.get().getBuildResult(DerivedPath::Built {
1574+
.drvPath = makeConstantStorePathRef(dg.get().drvPath),
15921575
.outputs = OutputsSpec::Names { outputName },
15931576
});
15941577
if (buildResult.success()) {
15951578
auto i = buildResult.builtOutputs.find(outputName);
15961579
if (i != buildResult.builtOutputs.end())
15971580
inputDrvOutputs.insert_or_assign(
1598-
{ dg->drvPath, outputName },
1581+
{ dg.get().drvPath, outputName },
15991582
i->second.outPath);
16001583
}
16011584
}

src/libstore/build/derivation-goal.hh

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,10 @@ struct InitialOutput {
5656

5757
/**
5858
* A goal for building some or all of the outputs of a derivation.
59+
*
60+
* The derivation must already be present, either in the store in a drv
61+
* or in memory. If the derivation itself needs to be gotten first, a
62+
* `CreateDerivationAndRealiseGoal` goal must be used instead.
5963
*/
6064
struct DerivationGoal : public Goal
6165
{
@@ -237,7 +241,6 @@ struct DerivationGoal : public Goal
237241
/**
238242
* The states.
239243
*/
240-
void getDerivation();
241244
void loadDerivation();
242245
void haveDerivation();
243246
void outputsSubstitutionTried();

src/libstore/build/entry-points.cc

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
#include "worker.hh"
22
#include "substitution-goal.hh"
33
#ifndef _WIN32 // TODO Enable building on Windows
4+
# include "create-derivation-and-realise-goal.hh"
45
# include "derivation-goal.hh"
56
#endif
67
#include "local-store.hh"
@@ -29,8 +30,8 @@ void Store::buildPaths(const std::vector<DerivedPath> & reqs, BuildMode buildMod
2930
}
3031
if (i->exitCode != Goal::ecSuccess) {
3132
#ifndef _WIN32 // TODO Enable building on Windows
32-
if (auto i2 = dynamic_cast<DerivationGoal *>(i.get()))
33-
failed.insert(printStorePath(i2->drvPath));
33+
if (auto i2 = dynamic_cast<CreateDerivationAndRealiseGoal *>(i.get()))
34+
failed.insert(i2->drvReq->to_string(*this));
3435
else
3536
#endif
3637
if (auto i2 = dynamic_cast<PathSubstitutionGoal *>(i.get()))

src/libstore/build/goal.hh

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,16 @@ enum struct JobCategory {
4949
* A substitution an arbitrary store object; it will use network resources.
5050
*/
5151
Substitution,
52+
/**
53+
* A goal that does no "real" work by itself, and just exists to depend on
54+
* other goals which *do* do real work. These goals therefore are not
55+
* limited.
56+
*
57+
* These goals cannot infinitely create themselves, so there is no risk of
58+
* a "fork bomb" type situation (which would be a problem even though the
59+
* goal do no real work) either.
60+
*/
61+
Administration,
5262
};
5363

5464
struct Goal : public std::enable_shared_from_this<Goal>

0 commit comments

Comments
 (0)