-
-
Notifications
You must be signed in to change notification settings - Fork 1.5k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branches 'build-1', 'build-2', 'build-3', 'build-4', 'build-5',…
… 'build-6', 'build-7' and 'build-8' into split_build_cc
- Loading branch information
Showing
8 changed files
with
4,882 additions
and
0 deletions.
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,88 @@ | ||
#include "build.hh" | ||
|
||
namespace nix { | ||
|
||
|
||
bool CompareGoalPtrs::operator() (const GoalPtr & a, const GoalPtr & b) const { | ||
string s1 = a->key(); | ||
string s2 = b->key(); | ||
return s1 < s2; | ||
} | ||
|
||
|
||
void addToWeakGoals(WeakGoals & goals, GoalPtr p) | ||
{ | ||
// FIXME: necessary? | ||
// FIXME: O(n) | ||
for (auto & i : goals) | ||
if (i.lock() == p) return; | ||
goals.push_back(p); | ||
} | ||
|
||
|
||
void Goal::addWaitee(GoalPtr waitee) | ||
{ | ||
waitees.insert(waitee); | ||
addToWeakGoals(waitee->waiters, shared_from_this()); | ||
} | ||
|
||
|
||
void Goal::waiteeDone(GoalPtr waitee, ExitCode result) | ||
{ | ||
assert(waitees.find(waitee) != waitees.end()); | ||
waitees.erase(waitee); | ||
|
||
trace(fmt("waitee '%s' done; %d left", waitee->name, waitees.size())); | ||
|
||
if (result == ecFailed || result == ecNoSubstituters || result == ecIncompleteClosure) ++nrFailed; | ||
|
||
if (result == ecNoSubstituters) ++nrNoSubstituters; | ||
|
||
if (result == ecIncompleteClosure) ++nrIncompleteClosure; | ||
|
||
if (waitees.empty() || (result == ecFailed && !settings.keepGoing)) { | ||
|
||
/* If we failed and keepGoing is not set, we remove all | ||
remaining waitees. */ | ||
for (auto & goal : waitees) { | ||
WeakGoals waiters2; | ||
for (auto & j : goal->waiters) | ||
if (j.lock() != shared_from_this()) waiters2.push_back(j); | ||
goal->waiters = waiters2; | ||
} | ||
waitees.clear(); | ||
|
||
worker.wakeUp(shared_from_this()); | ||
} | ||
} | ||
|
||
|
||
void Goal::amDone(ExitCode result, std::optional<Error> ex) | ||
{ | ||
trace("done"); | ||
assert(exitCode == ecBusy); | ||
assert(result == ecSuccess || result == ecFailed || result == ecNoSubstituters || result == ecIncompleteClosure); | ||
exitCode = result; | ||
|
||
if (ex) { | ||
if (!waiters.empty()) | ||
logError(ex->info()); | ||
else | ||
this->ex = std::move(*ex); | ||
} | ||
|
||
for (auto & i : waiters) { | ||
GoalPtr goal = i.lock(); | ||
if (goal) goal->waiteeDone(shared_from_this(), result); | ||
} | ||
waiters.clear(); | ||
worker.removeGoal(shared_from_this()); | ||
} | ||
|
||
|
||
void Goal::trace(const FormatOrString & fs) | ||
{ | ||
debug("%1%: %2%", name, fs.s); | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,71 @@ | ||
#include "build.hh" | ||
|
||
namespace nix { | ||
|
||
HookInstance::HookInstance() | ||
{ | ||
debug("starting build hook '%s'", settings.buildHook); | ||
|
||
/* Create a pipe to get the output of the child. */ | ||
fromHook.create(); | ||
|
||
/* Create the communication pipes. */ | ||
toHook.create(); | ||
|
||
/* Create a pipe to get the output of the builder. */ | ||
builderOut.create(); | ||
|
||
/* Fork the hook. */ | ||
pid = startProcess([&]() { | ||
|
||
commonChildInit(fromHook); | ||
|
||
if (chdir("/") == -1) throw SysError("changing into /"); | ||
|
||
/* Dup the communication pipes. */ | ||
if (dup2(toHook.readSide.get(), STDIN_FILENO) == -1) | ||
throw SysError("dupping to-hook read side"); | ||
|
||
/* Use fd 4 for the builder's stdout/stderr. */ | ||
if (dup2(builderOut.writeSide.get(), 4) == -1) | ||
throw SysError("dupping builder's stdout/stderr"); | ||
|
||
/* Hack: pass the read side of that fd to allow build-remote | ||
to read SSH error messages. */ | ||
if (dup2(builderOut.readSide.get(), 5) == -1) | ||
throw SysError("dupping builder's stdout/stderr"); | ||
|
||
Strings args = { | ||
std::string(baseNameOf(settings.buildHook.get())), | ||
std::to_string(verbosity), | ||
}; | ||
|
||
execv(settings.buildHook.get().c_str(), stringsToCharPtrs(args).data()); | ||
|
||
throw SysError("executing '%s'", settings.buildHook); | ||
}); | ||
|
||
pid.setSeparatePG(true); | ||
fromHook.writeSide = -1; | ||
toHook.readSide = -1; | ||
|
||
sink = FdSink(toHook.writeSide.get()); | ||
std::map<std::string, Config::SettingInfo> settings; | ||
globalConfig.getSettings(settings); | ||
for (auto & setting : settings) | ||
sink << 1 << setting.first << setting.second.value; | ||
sink << 0; | ||
} | ||
|
||
|
||
HookInstance::~HookInstance() | ||
{ | ||
try { | ||
toHook.writeSide = -1; | ||
if (pid != -1) pid.kill(); | ||
} catch (...) { | ||
ignoreException(); | ||
} | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,123 @@ | ||
#include "build.hh" | ||
|
||
namespace nix { | ||
|
||
static void primeCache(Store & store, const std::vector<StorePathWithOutputs> & paths) | ||
{ | ||
StorePathSet willBuild, willSubstitute, unknown; | ||
uint64_t downloadSize, narSize; | ||
store.queryMissing(paths, willBuild, willSubstitute, unknown, downloadSize, narSize); | ||
|
||
if (!willBuild.empty() && 0 == settings.maxBuildJobs && getMachines().empty()) | ||
throw Error( | ||
"%d derivations need to be built, but neither local builds ('--max-jobs') " | ||
"nor remote builds ('--builders') are enabled", willBuild.size()); | ||
} | ||
|
||
|
||
void LocalStore::buildPaths(const std::vector<StorePathWithOutputs> & drvPaths, BuildMode buildMode) | ||
{ | ||
Worker worker(*this); | ||
|
||
primeCache(*this, drvPaths); | ||
|
||
Goals goals; | ||
for (auto & path : drvPaths) { | ||
if (path.path.isDerivation()) | ||
goals.insert(worker.makeDerivationGoal(path.path, path.outputs, buildMode)); | ||
else | ||
goals.insert(worker.makeSubstitutionGoal(path.path, buildMode == bmRepair ? Repair : NoRepair)); | ||
} | ||
|
||
worker.run(goals); | ||
|
||
StorePathSet failed; | ||
std::optional<Error> ex; | ||
for (auto & i : goals) { | ||
if (i->ex) { | ||
if (ex) | ||
logError(i->ex->info()); | ||
else | ||
ex = i->ex; | ||
} | ||
if (i->exitCode != Goal::ecSuccess) { | ||
DerivationGoal * i2 = dynamic_cast<DerivationGoal *>(i.get()); | ||
if (i2) failed.insert(i2->getDrvPath()); | ||
else failed.insert(dynamic_cast<SubstitutionGoal *>(i.get())->getStorePath()); | ||
} | ||
} | ||
|
||
if (failed.size() == 1 && ex) { | ||
ex->status = worker.exitStatus(); | ||
throw *ex; | ||
} else if (!failed.empty()) { | ||
if (ex) logError(ex->info()); | ||
throw Error(worker.exitStatus(), "build of %s failed", showPaths(failed)); | ||
} | ||
} | ||
|
||
BuildResult LocalStore::buildDerivation(const StorePath & drvPath, const BasicDerivation & drv, | ||
BuildMode buildMode) | ||
{ | ||
Worker worker(*this); | ||
auto goal = worker.makeBasicDerivationGoal(drvPath, drv, {}, buildMode); | ||
|
||
BuildResult result; | ||
|
||
try { | ||
worker.run(Goals{goal}); | ||
result = goal->getResult(); | ||
} catch (Error & e) { | ||
result.status = BuildResult::MiscFailure; | ||
result.errorMsg = e.msg(); | ||
} | ||
|
||
return result; | ||
} | ||
|
||
|
||
void LocalStore::ensurePath(const StorePath & path) | ||
{ | ||
/* If the path is already valid, we're done. */ | ||
if (isValidPath(path)) return; | ||
|
||
primeCache(*this, {{path}}); | ||
|
||
Worker worker(*this); | ||
GoalPtr goal = worker.makeSubstitutionGoal(path); | ||
Goals goals = {goal}; | ||
|
||
worker.run(goals); | ||
|
||
if (goal->exitCode != Goal::ecSuccess) { | ||
if (goal->ex) { | ||
goal->ex->status = worker.exitStatus(); | ||
throw *goal->ex; | ||
} else | ||
throw Error(worker.exitStatus(), "path '%s' does not exist and cannot be created", printStorePath(path)); | ||
} | ||
} | ||
|
||
|
||
void LocalStore::repairPath(const StorePath & path) | ||
{ | ||
Worker worker(*this); | ||
GoalPtr goal = worker.makeSubstitutionGoal(path, Repair); | ||
Goals goals = {goal}; | ||
|
||
worker.run(goals); | ||
|
||
if (goal->exitCode != Goal::ecSuccess) { | ||
/* Since substituting the path didn't work, if we have a valid | ||
deriver, then rebuild the deriver. */ | ||
auto info = queryPathInfo(path); | ||
if (info->deriver && isValidPath(*info->deriver)) { | ||
goals.clear(); | ||
goals.insert(worker.makeDerivationGoal(*info->deriver, StringSet(), bmRepair)); | ||
worker.run(goals); | ||
} else | ||
throw Error(worker.exitStatus(), "cannot repair path '%s'", printStorePath(path)); | ||
} | ||
} | ||
|
||
} |
Oops, something went wrong.