2020-11-09 16:40:10 +02:00
|
|
|
|
#include "drv-output-substitution-goal.hh"
|
2021-10-28 17:43:54 +03:00
|
|
|
|
#include "finally.hh"
|
2020-11-09 16:40:10 +02:00
|
|
|
|
#include "worker.hh"
|
|
|
|
|
#include "substitution-goal.hh"
|
2021-10-28 17:43:54 +03:00
|
|
|
|
#include "callback.hh"
|
2020-11-09 16:40:10 +02:00
|
|
|
|
|
|
|
|
|
namespace nix {
|
|
|
|
|
|
2022-03-09 13:25:35 +02:00
|
|
|
|
DrvOutputSubstitutionGoal::DrvOutputSubstitutionGoal(
|
|
|
|
|
const DrvOutput & id,
|
|
|
|
|
Worker & worker,
|
|
|
|
|
RepairFlag repair,
|
|
|
|
|
std::optional<ContentAddress> ca)
|
|
|
|
|
: Goal(worker, DerivedPath::Opaque { StorePath::dummy })
|
2020-11-09 16:40:10 +02:00
|
|
|
|
, id(id)
|
|
|
|
|
{
|
|
|
|
|
state = &DrvOutputSubstitutionGoal::init;
|
|
|
|
|
name = fmt("substitution of '%s'", id.to_string());
|
|
|
|
|
trace("created");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void DrvOutputSubstitutionGoal::init()
|
|
|
|
|
{
|
|
|
|
|
trace("init");
|
2021-05-19 17:27:09 +03:00
|
|
|
|
|
|
|
|
|
/* If the derivation already exists, we’re done */
|
|
|
|
|
if (worker.store.queryRealisation(id)) {
|
|
|
|
|
amDone(ecSuccess);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2020-11-09 16:40:10 +02:00
|
|
|
|
subs = settings.useSubstitutes ? getDefaultSubstituters() : std::list<ref<Store>>();
|
|
|
|
|
tryNext();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void DrvOutputSubstitutionGoal::tryNext()
|
|
|
|
|
{
|
2022-03-08 20:50:46 +02:00
|
|
|
|
trace("trying next substituter");
|
2020-11-09 16:40:10 +02:00
|
|
|
|
|
|
|
|
|
if (subs.size() == 0) {
|
|
|
|
|
/* None left. Terminate this goal and let someone else deal
|
|
|
|
|
with it. */
|
2022-04-06 13:41:18 +03:00
|
|
|
|
debug("derivation output '%s' is required, but there is no substituter that can provide it", id.to_string());
|
2020-11-09 16:40:10 +02:00
|
|
|
|
|
|
|
|
|
/* Hack: don't indicate failure if there were no substituters.
|
|
|
|
|
In that case the calling derivation should just do a
|
|
|
|
|
build. */
|
|
|
|
|
amDone(substituterFailed ? ecFailed : ecNoSubstituters);
|
|
|
|
|
|
|
|
|
|
if (substituterFailed) {
|
|
|
|
|
worker.failedSubstitutions++;
|
|
|
|
|
worker.updateProgress();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2021-10-28 17:43:54 +03:00
|
|
|
|
sub = subs.front();
|
2020-11-09 16:40:10 +02:00
|
|
|
|
subs.pop_front();
|
|
|
|
|
|
|
|
|
|
// FIXME: Make async
|
2021-10-28 17:43:54 +03:00
|
|
|
|
// outputInfo = sub->queryRealisation(id);
|
2023-03-07 14:51:02 +02:00
|
|
|
|
|
|
|
|
|
/* The callback of the curl download below can outlive `this` (if
|
|
|
|
|
some other error occurs), so it must not touch `this`. So put
|
|
|
|
|
the shared state in a separate refcounted object. */
|
|
|
|
|
downloadState = std::make_shared<DownloadState>();
|
|
|
|
|
downloadState->outPipe.create();
|
2021-10-28 17:43:54 +03:00
|
|
|
|
|
|
|
|
|
sub->queryRealisation(
|
2023-03-07 14:51:02 +02:00
|
|
|
|
id,
|
|
|
|
|
{ [downloadState(downloadState)](std::future<std::shared_ptr<const Realisation>> res) {
|
2021-10-28 17:43:54 +03:00
|
|
|
|
try {
|
2023-03-07 14:51:02 +02:00
|
|
|
|
Finally updateStats([&]() { downloadState->outPipe.writeSide.close(); });
|
|
|
|
|
downloadState->promise.set_value(res.get());
|
2021-10-28 17:43:54 +03:00
|
|
|
|
} catch (...) {
|
2023-03-07 14:51:02 +02:00
|
|
|
|
downloadState->promise.set_exception(std::current_exception());
|
2021-10-28 17:43:54 +03:00
|
|
|
|
}
|
|
|
|
|
} });
|
|
|
|
|
|
2023-03-07 14:51:02 +02:00
|
|
|
|
worker.childStarted(shared_from_this(), {downloadState->outPipe.readSide.get()}, true, false);
|
2021-10-28 17:43:54 +03:00
|
|
|
|
|
|
|
|
|
state = &DrvOutputSubstitutionGoal::realisationFetched;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void DrvOutputSubstitutionGoal::realisationFetched()
|
|
|
|
|
{
|
|
|
|
|
worker.childTerminated(this);
|
|
|
|
|
|
|
|
|
|
try {
|
2023-03-07 14:51:02 +02:00
|
|
|
|
outputInfo = downloadState->promise.get_future().get();
|
2021-10-28 17:43:54 +03:00
|
|
|
|
} catch (std::exception & e) {
|
|
|
|
|
printError(e.what());
|
|
|
|
|
substituterFailed = true;
|
|
|
|
|
}
|
|
|
|
|
|
2020-11-09 16:40:10 +02:00
|
|
|
|
if (!outputInfo) {
|
2021-10-28 17:43:54 +03:00
|
|
|
|
return tryNext();
|
2020-11-09 16:40:10 +02:00
|
|
|
|
}
|
|
|
|
|
|
2021-05-19 17:27:09 +03:00
|
|
|
|
for (const auto & [depId, depPath] : outputInfo->dependentRealisations) {
|
|
|
|
|
if (depId != id) {
|
|
|
|
|
if (auto localOutputInfo = worker.store.queryRealisation(depId);
|
|
|
|
|
localOutputInfo && localOutputInfo->outPath != depPath) {
|
|
|
|
|
warn(
|
2021-06-22 11:46:29 +03:00
|
|
|
|
"substituter '%s' has an incompatible realisation for '%s', ignoring.\n"
|
|
|
|
|
"Local: %s\n"
|
|
|
|
|
"Remote: %s",
|
2021-05-19 17:27:09 +03:00
|
|
|
|
sub->getUri(),
|
2021-06-22 11:46:29 +03:00
|
|
|
|
depId.to_string(),
|
|
|
|
|
worker.store.printStorePath(localOutputInfo->outPath),
|
|
|
|
|
worker.store.printStorePath(depPath)
|
|
|
|
|
);
|
2021-05-19 17:27:09 +03:00
|
|
|
|
tryNext();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
addWaitee(worker.makeDrvOutputSubstitutionGoal(depId));
|
2021-05-19 15:51:34 +03:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-11-09 16:40:10 +02:00
|
|
|
|
addWaitee(worker.makePathSubstitutionGoal(outputInfo->outPath));
|
|
|
|
|
|
|
|
|
|
if (waitees.empty()) outPathValid();
|
|
|
|
|
else state = &DrvOutputSubstitutionGoal::outPathValid;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void DrvOutputSubstitutionGoal::outPathValid()
|
|
|
|
|
{
|
|
|
|
|
assert(outputInfo);
|
2022-03-08 20:50:46 +02:00
|
|
|
|
trace("output path substituted");
|
2020-11-09 16:40:10 +02:00
|
|
|
|
|
|
|
|
|
if (nrFailed > 0) {
|
|
|
|
|
debug("The output path of the derivation output '%s' could not be substituted", id.to_string());
|
|
|
|
|
amDone(nrNoSubstituters > 0 || nrIncompleteClosure > 0 ? ecIncompleteClosure : ecFailed);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
worker.store.registerDrvOutput(*outputInfo);
|
|
|
|
|
finished();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void DrvOutputSubstitutionGoal::finished()
|
|
|
|
|
{
|
|
|
|
|
trace("finished");
|
|
|
|
|
amDone(ecSuccess);
|
|
|
|
|
}
|
|
|
|
|
|
2022-02-25 17:00:00 +02:00
|
|
|
|
std::string DrvOutputSubstitutionGoal::key()
|
2020-11-09 16:40:10 +02:00
|
|
|
|
{
|
|
|
|
|
/* "a$" ensures substitution goals happen before derivation
|
|
|
|
|
goals. */
|
|
|
|
|
return "a$" + std::string(id.to_string());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void DrvOutputSubstitutionGoal::work()
|
|
|
|
|
{
|
|
|
|
|
(this->*state)();
|
|
|
|
|
}
|
|
|
|
|
|
2021-10-28 17:43:54 +03:00
|
|
|
|
void DrvOutputSubstitutionGoal::handleEOF(int fd)
|
|
|
|
|
{
|
2023-03-07 14:51:02 +02:00
|
|
|
|
if (fd == downloadState->outPipe.readSide.get()) worker.wakeUp(shared_from_this());
|
2021-10-28 17:43:54 +03:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2020-11-09 16:40:10 +02:00
|
|
|
|
}
|