build.cc: Don't use hasSubstitute()

Instead make a single call to querySubstitutablePathInfo() per
derivation output.  This is faster and prevents having to implement
the "have" function in the binary cache substituter.
This commit is contained in:
Eelco Dolstra 2012-07-08 18:39:24 -04:00
parent 400e556b34
commit 425cc612ad

View file

@ -94,7 +94,7 @@ typedef map<Path, WeakGoalPtr> WeakGoalMap;
class Goal : public boost::enable_shared_from_this<Goal> class Goal : public boost::enable_shared_from_this<Goal>
{ {
public: public:
typedef enum {ecBusy, ecSuccess, ecFailed} ExitCode; typedef enum {ecBusy, ecSuccess, ecFailed, ecNoSubstituters} ExitCode;
protected: protected:
@ -111,6 +111,10 @@ protected:
/* Number of goals we are/were waiting for that have failed. */ /* Number of goals we are/were waiting for that have failed. */
unsigned int nrFailed; unsigned int nrFailed;
/* Number of substitution goals we are/were waiting for that
failed because there are no substituters. */
unsigned int nrNoSubstituters;
/* Name of this goal for debugging purposes. */ /* Name of this goal for debugging purposes. */
string name; string name;
@ -119,7 +123,7 @@ protected:
Goal(Worker & worker) : worker(worker) Goal(Worker & worker) : worker(worker)
{ {
nrFailed = 0; nrFailed = nrNoSubstituters = 0;
exitCode = ecBusy; exitCode = ecBusy;
} }
@ -306,7 +310,9 @@ void Goal::waiteeDone(GoalPtr waitee, ExitCode result)
trace(format("waitee `%1%' done; %2% left") % trace(format("waitee `%1%' done; %2% left") %
waitee->name % waitees.size()); waitee->name % waitees.size());
if (result == ecFailed) ++nrFailed; if (result == ecFailed || result == ecNoSubstituters) ++nrFailed;
if (result == ecNoSubstituters) ++nrNoSubstituters;
if (waitees.empty() || (result == ecFailed && !keepGoing)) { if (waitees.empty() || (result == ecFailed && !keepGoing)) {
@ -330,7 +336,7 @@ void Goal::amDone(ExitCode result)
{ {
trace("done"); trace("done");
assert(exitCode == ecBusy); assert(exitCode == ecBusy);
assert(result == ecSuccess || result == ecFailed); assert(result == ecSuccess || result == ecFailed || result == ecNoSubstituters);
exitCode = result; exitCode = result;
foreach (WeakGoals::iterator, i, waiters) { foreach (WeakGoals::iterator, i, waiters) {
GoalPtr goal = i->lock(); GoalPtr goal = i->lock();
@ -736,6 +742,8 @@ HookInstance::~HookInstance()
typedef enum {rpAccept, rpDecline, rpPostpone} HookReply; typedef enum {rpAccept, rpDecline, rpPostpone} HookReply;
class SubstitutionGoal;
class DerivationGoal : public Goal class DerivationGoal : public Goal
{ {
private: private:
@ -985,10 +993,8 @@ void DerivationGoal::haveDerivation()
/* We are first going to try to create the invalid output paths /* We are first going to try to create the invalid output paths
through substitutes. If that doesn't work, we'll build through substitutes. If that doesn't work, we'll build
them. */ them. */
foreach (PathSet::iterator, i, invalidOutputs) if (queryBoolSetting("build-use-substitutes", true))
/* Don't bother creating a substitution goal if there are no foreach (PathSet::iterator, i, invalidOutputs)
substitutes. */
if (queryBoolSetting("build-use-substitutes", true) && worker.store.hasSubstitutes(*i))
addWaitee(worker.makeSubstitutionGoal(*i)); addWaitee(worker.makeSubstitutionGoal(*i));
if (waitees.empty()) /* to prevent hang (no wake-up event) */ if (waitees.empty()) /* to prevent hang (no wake-up event) */
@ -1002,10 +1008,10 @@ void DerivationGoal::outputsSubstituted()
{ {
trace("all outputs substituted (maybe)"); trace("all outputs substituted (maybe)");
if (nrFailed > 0 && !tryFallback) if (nrFailed > 0 && nrFailed > nrNoSubstituters && !tryFallback)
throw Error(format("some substitutes for the outputs of derivation `%1%' failed; try `--fallback'") % drvPath); throw Error(format("some substitutes for the outputs of derivation `%1%' failed; try `--fallback'") % drvPath);
nrFailed = 0; nrFailed = nrNoSubstituters = 0;
if (checkPathValidity(false).size() == 0) { if (checkPathValidity(false).size() == 0) {
amDone(ecSuccess); amDone(ecSuccess);
@ -2241,6 +2247,9 @@ private:
/* The current substituter. */ /* The current substituter. */
Path sub; Path sub;
/* Whether any substituter can realise this path */
bool hasSubstitute;
/* Path info returned by the substituter's query info operation. */ /* Path info returned by the substituter's query info operation. */
SubstitutablePathInfo info; SubstitutablePathInfo info;
@ -2282,6 +2291,7 @@ public:
SubstitutionGoal::SubstitutionGoal(const Path & storePath, Worker & worker) SubstitutionGoal::SubstitutionGoal(const Path & storePath, Worker & worker)
: Goal(worker) : Goal(worker)
, hasSubstitute(false)
{ {
this->storePath = storePath; this->storePath = storePath;
state = &SubstitutionGoal::init; state = &SubstitutionGoal::init;
@ -2345,7 +2355,10 @@ void SubstitutionGoal::tryNext()
/* None left. Terminate this goal and let someone else deal /* None left. Terminate this goal and let someone else deal
with it. */ with it. */
debug(format("path `%1%' is required, but there is no substituter that can build it") % storePath); debug(format("path `%1%' is required, but there is no substituter that can build it") % storePath);
amDone(ecFailed); /* Hack: don't indicate failure if there were no substituters.
In that case the calling derivation should just do a
build. */
amDone(hasSubstitute ? ecFailed : ecNoSubstituters);
return; return;
} }
@ -2358,6 +2371,7 @@ void SubstitutionGoal::tryNext()
SubstitutablePathInfos::iterator k = infos.find(storePath); SubstitutablePathInfos::iterator k = infos.find(storePath);
if (k == infos.end()) { tryNext(); return; } if (k == infos.end()) { tryNext(); return; }
info = k->second; info = k->second;
hasSubstitute = true;
/* To maintain the closure invariant, we first have to realise the /* To maintain the closure invariant, we first have to realise the
paths referenced by this one. */ paths referenced by this one. */