Register the realisations for unresolved drvs

Once a build is done, get back to the original derivation, and register
all the newly built outputs for this derivation.

This allows Nix to work properly with derivations that don't have all
their build inputs available − thus allowing garbage collection and
(once it's implemented) binary substitution
This commit is contained in:
regnat 2021-01-27 10:03:05 +01:00 committed by Théophane Hufschmitt
parent be1b5c4e59
commit 87c8d3d702
7 changed files with 67 additions and 24 deletions

View file

@ -506,6 +506,7 @@ void DerivationGoal::inputsRealised()
Derivation drvResolved { *std::move(attempt) }; Derivation drvResolved { *std::move(attempt) };
auto pathResolved = writeDerivation(worker.store, drvResolved); auto pathResolved = writeDerivation(worker.store, drvResolved);
resolvedDrv = drvResolved;
auto msg = fmt("Resolved derivation: '%s' -> '%s'", auto msg = fmt("Resolved derivation: '%s' -> '%s'",
worker.store.printStorePath(drvPath), worker.store.printStorePath(drvPath),
@ -1019,7 +1020,45 @@ void DerivationGoal::buildDone()
} }
void DerivationGoal::resolvedFinished() { void DerivationGoal::resolvedFinished() {
done(BuildResult::Built); assert(resolvedDrv);
// If the derivation was originally a full `Derivation` (and not just
// a `BasicDerivation`, we must retrieve it because the `staticOutputHashes`
// will be wrong otherwise
Derivation fullDrv = *drv;
if (auto upcasted = dynamic_cast<Derivation *>(drv.get()))
fullDrv = *upcasted;
auto originalHashes = staticOutputHashes(worker.store, fullDrv);
auto resolvedHashes = staticOutputHashes(worker.store, *resolvedDrv);
// `wantedOutputs` might be empty, which means “all the outputs”
auto realWantedOutputs = wantedOutputs;
if (realWantedOutputs.empty())
realWantedOutputs = resolvedDrv->outputNames();
for (auto & wantedOutput : realWantedOutputs) {
assert(originalHashes.count(wantedOutput) != 0);
assert(resolvedHashes.count(wantedOutput) != 0);
auto realisation = worker.store.queryRealisation(
DrvOutput{resolvedHashes.at(wantedOutput), wantedOutput}
);
// We've just built it, but maybe the build failed, in which case the
// realisation won't be there
if (realisation) {
auto newRealisation = *realisation;
newRealisation.id = DrvOutput{originalHashes.at(wantedOutput), wantedOutput};
worker.store.registerDrvOutput(newRealisation);
} else {
// If we don't have a realisation, then it must mean that something
// failed when building the resolved drv
assert(!result.success());
}
}
// This is potentially a bit fishy in terms of error reporting. Not sure
// how to do it in a cleaner way
amDone(nrFailed == 0 ? ecSuccess : ecFailed, ex);
} }
HookReply DerivationGoal::tryBuildHook() HookReply DerivationGoal::tryBuildHook()
@ -3804,6 +3843,19 @@ void DerivationGoal::checkPathValidity()
: PathStatus::Corrupt, : PathStatus::Corrupt,
}; };
} }
if (settings.isExperimentalFeatureEnabled("ca-derivations")) {
Derivation fullDrv = *drv;
if (auto upcasted = dynamic_cast<Derivation *>(drv.get()))
fullDrv = *upcasted;
auto outputHashes = staticOutputHashes(worker.store, fullDrv);
if (auto real = worker.store.queryRealisation(
DrvOutput{outputHashes.at(i.first), i.first})) {
info.known = {
.path = real->outPath,
.status = PathStatus::Valid,
};
}
}
initialOutputs.insert_or_assign(i.first, info); initialOutputs.insert_or_assign(i.first, info);
} }
} }

View file

@ -48,6 +48,9 @@ struct DerivationGoal : public Goal
/* The path of the derivation. */ /* The path of the derivation. */
StorePath drvPath; StorePath drvPath;
/* The path of the corresponding resolved derivation */
std::optional<BasicDerivation> resolvedDrv;
/* The specific outputs that we need to build. Empty means all of /* The specific outputs that we need to build. Empty means all of
them. */ them. */
StringSet wantedOutputs; StringSet wantedOutputs;

View file

@ -756,8 +756,13 @@ std::optional<BasicDerivation> Derivation::tryResolveUncached(Store & store) {
StringSet newOutputNames; StringSet newOutputNames;
for (auto & outputName : input.second) { for (auto & outputName : input.second) {
auto actualPathOpt = inputDrvOutputs.at(outputName); auto actualPathOpt = inputDrvOutputs.at(outputName);
if (!actualPathOpt) if (!actualPathOpt) {
warn("Input %s!%s missing, aborting the resolving",
store.printStorePath(input.first),
outputName
);
return std::nullopt; return std::nullopt;
}
auto actualPath = *actualPathOpt; auto actualPath = *actualPathOpt;
inputRewrites.emplace( inputRewrites.emplace(
downstreamPlaceholder(store, input.first, outputName), downstreamPlaceholder(store, input.first, outputName),
@ -782,6 +787,8 @@ std::optional<BasicDerivation> Derivation::tryResolve(Store& store, const StoreP
// This is quite dirty and leaky, but will disappear once #4340 is merged // This is quite dirty and leaky, but will disappear once #4340 is merged
static Sync<std::map<StorePath, std::optional<Derivation>>> resolutionsCache; static Sync<std::map<StorePath, std::optional<Derivation>>> resolutionsCache;
debug("Trying to resolve %s", store.printStorePath(drvPath));
{ {
auto resolutions = resolutionsCache.lock(); auto resolutions = resolutionsCache.lock();
auto resolvedDrvIter = resolutions->find(drvPath); auto resolvedDrvIter = resolutions->find(drvPath);

View file

@ -883,7 +883,7 @@ StorePathSet LocalStore::queryValidDerivers(const StorePath & path)
std::map<std::string, std::optional<StorePath>> std::map<std::string, std::optional<StorePath>>
LocalStore::queryDerivationOutputMapNoResolve(const StorePath& path_) LocalStore::queryPartialDerivationOutputMap(const StorePath& path_)
{ {
auto path = path_; auto path = path_;
auto outputs = retrySQLite<std::map<std::string, std::optional<StorePath>>>([&]() { auto outputs = retrySQLite<std::map<std::string, std::optional<StorePath>>>([&]() {

View file

@ -127,7 +127,7 @@ public:
StorePathSet queryValidDerivers(const StorePath & path) override; StorePathSet queryValidDerivers(const StorePath & path) override;
std::map<std::string, std::optional<StorePath>> queryDerivationOutputMapNoResolve(const StorePath & path) override; std::map<std::string, std::optional<StorePath>> queryPartialDerivationOutputMap(const StorePath & path) override;
std::optional<StorePath> queryPathFromHashPart(const std::string & hashPart) override; std::optional<StorePath> queryPathFromHashPart(const std::string & hashPart) override;

View file

@ -366,7 +366,7 @@ bool Store::PathInfoCacheValue::isKnownNow()
return std::chrono::steady_clock::now() < time_point + ttl; return std::chrono::steady_clock::now() < time_point + ttl;
} }
std::map<std::string, std::optional<StorePath>> Store::queryDerivationOutputMapNoResolve(const StorePath & path) std::map<std::string, std::optional<StorePath>> Store::queryPartialDerivationOutputMap(const StorePath & path)
{ {
std::map<std::string, std::optional<StorePath>> outputs; std::map<std::string, std::optional<StorePath>> outputs;
auto drv = readInvalidDerivation(path); auto drv = readInvalidDerivation(path);
@ -376,19 +376,6 @@ std::map<std::string, std::optional<StorePath>> Store::queryDerivationOutputMapN
return outputs; return outputs;
} }
std::map<std::string, std::optional<StorePath>> Store::queryPartialDerivationOutputMap(const StorePath & path)
{
if (settings.isExperimentalFeatureEnabled("ca-derivations")) {
auto resolvedDrv = Derivation::tryResolve(*this, path);
if (resolvedDrv) {
auto resolvedDrvPath = writeDerivation(*this, *resolvedDrv, NoRepair, true);
if (isValidPath(resolvedDrvPath))
return queryDerivationOutputMapNoResolve(resolvedDrvPath);
}
}
return queryDerivationOutputMapNoResolve(path);
}
OutputPathMap Store::queryDerivationOutputMap(const StorePath & path) { OutputPathMap Store::queryDerivationOutputMap(const StorePath & path) {
auto resp = queryPartialDerivationOutputMap(path); auto resp = queryPartialDerivationOutputMap(path);
OutputPathMap result; OutputPathMap result;

View file

@ -415,12 +415,6 @@ public:
`std::nullopt`. */ `std::nullopt`. */
virtual std::map<std::string, std::optional<StorePath>> queryPartialDerivationOutputMap(const StorePath & path); virtual std::map<std::string, std::optional<StorePath>> queryPartialDerivationOutputMap(const StorePath & path);
/*
* Similar to `queryPartialDerivationOutputMap`, but doesn't try to resolve
* the derivation
*/
virtual std::map<std::string, std::optional<StorePath>> queryDerivationOutputMapNoResolve(const StorePath & path);
/* Query the mapping outputName=>outputPath for the given derivation. /* Query the mapping outputName=>outputPath for the given derivation.
Assume every output has a mapping and throw an exception otherwise. */ Assume every output has a mapping and throw an exception otherwise. */
OutputPathMap queryDerivationOutputMap(const StorePath & path); OutputPathMap queryDerivationOutputMap(const StorePath & path);