using a proper event loop basis we no longer have to worry about most of the intricacies of poll(), or platform-dependent replacements for it. we may even be able to use the event loop and its promise system for all of our scheduling in the future. we don't do any real async processing yet, this is just preparation to separate the first such change from the huge api design difference with the async framework we chose (kj from capnp): kj::Promise, unlike std::future, doesn't return exceptions unmangled. it instead wraps any non-kj exception into a kj exception, erasing all type information and preserving mostly the what() string in the process. this makes sense in the capnp rpc use case where unrestricted exception types can't be transferred, and since it moves error handling styles closer to a world we'd actually like there's no harm in doing it only here for now Change-Id: I20f888de74d525fb2db36ca30ebba4bcfe9cc838
158 lines
4.8 KiB
C++
158 lines
4.8 KiB
C++
#include "worker.hh"
|
|
#include "substitution-goal.hh"
|
|
#include "derivation-goal.hh"
|
|
#include "local-store.hh"
|
|
#include "strings.hh"
|
|
|
|
namespace nix {
|
|
|
|
static auto runWorker(Worker & worker, auto mkGoals)
|
|
{
|
|
return worker.run(mkGoals);
|
|
}
|
|
|
|
void Store::buildPaths(const std::vector<DerivedPath> & reqs, BuildMode buildMode, std::shared_ptr<Store> evalStore)
|
|
{
|
|
auto aio = kj::setupAsyncIo();
|
|
Worker worker(*this, evalStore ? *evalStore : *this, aio);
|
|
|
|
auto goals = runWorker(worker, [&](GoalFactory & gf) {
|
|
Goals goals;
|
|
for (auto & br : reqs)
|
|
goals.insert(gf.makeGoal(br, buildMode));
|
|
return goals;
|
|
});
|
|
|
|
StringSet failed;
|
|
std::shared_ptr<Error> ex;
|
|
for (auto & i : goals) {
|
|
if (i->ex) {
|
|
if (ex)
|
|
logError(i->ex->info());
|
|
else
|
|
ex = i->ex;
|
|
}
|
|
if (i->exitCode != Goal::ecSuccess) {
|
|
if (auto i2 = dynamic_cast<DerivationGoal *>(i.get()))
|
|
failed.insert(printStorePath(i2->drvPath));
|
|
else if (auto i2 = dynamic_cast<PathSubstitutionGoal *>(i.get()))
|
|
failed.insert(printStorePath(i2->storePath));
|
|
}
|
|
}
|
|
|
|
if (failed.size() == 1 && ex) {
|
|
ex->withExitStatus(worker.failingExitStatus());
|
|
throw std::move(*ex);
|
|
} else if (!failed.empty()) {
|
|
if (ex) logError(ex->info());
|
|
throw Error(worker.failingExitStatus(), "build of %s failed", concatStringsSep(", ", quoteStrings(failed)));
|
|
}
|
|
}
|
|
|
|
std::vector<KeyedBuildResult> Store::buildPathsWithResults(
|
|
const std::vector<DerivedPath> & reqs,
|
|
BuildMode buildMode,
|
|
std::shared_ptr<Store> evalStore)
|
|
{
|
|
auto aio = kj::setupAsyncIo();
|
|
Worker worker(*this, evalStore ? *evalStore : *this, aio);
|
|
|
|
std::vector<std::pair<const DerivedPath &, GoalPtr>> state;
|
|
|
|
auto goals = runWorker(worker, [&](GoalFactory & gf) {
|
|
Goals goals;
|
|
for (const auto & req : reqs) {
|
|
auto goal = gf.makeGoal(req, buildMode);
|
|
goals.insert(goal);
|
|
state.push_back({req, goal});
|
|
}
|
|
return goals;
|
|
});
|
|
|
|
std::vector<KeyedBuildResult> results;
|
|
|
|
for (auto & [req, goalPtr] : state)
|
|
results.emplace_back(goalPtr->buildResult.restrictTo(req));
|
|
|
|
return results;
|
|
}
|
|
|
|
BuildResult Store::buildDerivation(const StorePath & drvPath, const BasicDerivation & drv,
|
|
BuildMode buildMode)
|
|
{
|
|
auto aio = kj::setupAsyncIo();
|
|
Worker worker(*this, *this, aio);
|
|
|
|
try {
|
|
auto goals = runWorker(worker, [&](GoalFactory & gf) -> Goals {
|
|
return Goals{gf.makeBasicDerivationGoal(drvPath, drv, OutputsSpec::All{}, buildMode)};
|
|
});
|
|
auto goal = *goals.begin();
|
|
return goal->buildResult.restrictTo(DerivedPath::Built {
|
|
.drvPath = makeConstantStorePathRef(drvPath),
|
|
.outputs = OutputsSpec::All {},
|
|
});
|
|
} catch (Error & e) {
|
|
return BuildResult {
|
|
.status = BuildResult::MiscFailure,
|
|
.errorMsg = e.msg(),
|
|
};
|
|
};
|
|
}
|
|
|
|
|
|
void Store::ensurePath(const StorePath & path)
|
|
{
|
|
/* If the path is already valid, we're done. */
|
|
if (isValidPath(path)) return;
|
|
|
|
auto aio = kj::setupAsyncIo();
|
|
Worker worker(*this, *this, aio);
|
|
|
|
auto goals = runWorker(worker, [&](GoalFactory & gf) {
|
|
return Goals{gf.makePathSubstitutionGoal(path)};
|
|
});
|
|
auto goal = *goals.begin();
|
|
|
|
if (goal->exitCode != Goal::ecSuccess) {
|
|
if (goal->ex) {
|
|
goal->ex->withExitStatus(worker.failingExitStatus());
|
|
throw std::move(*goal->ex);
|
|
} else
|
|
throw Error(worker.failingExitStatus(), "path '%s' does not exist and cannot be created", printStorePath(path));
|
|
}
|
|
}
|
|
|
|
|
|
void Store::repairPath(const StorePath & path)
|
|
{
|
|
auto aio = kj::setupAsyncIo();
|
|
Worker worker(*this, *this, aio);
|
|
|
|
auto goals = runWorker(worker, [&](GoalFactory & gf) {
|
|
return Goals{gf.makePathSubstitutionGoal(path, Repair)};
|
|
});
|
|
auto goal = *goals.begin();
|
|
|
|
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)) {
|
|
worker.run([&](GoalFactory & gf) {
|
|
return Goals{gf.makeGoal(
|
|
DerivedPath::Built{
|
|
.drvPath = makeConstantStorePathRef(*info->deriver),
|
|
// FIXME: Should just build the specific output we need.
|
|
.outputs = OutputsSpec::All{},
|
|
},
|
|
bmRepair
|
|
)};
|
|
});
|
|
} else
|
|
throw Error(worker.failingExitStatus(), "cannot repair path '%s'", printStorePath(path));
|
|
}
|
|
}
|
|
|
|
}
|