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:
parent
be1b5c4e59
commit
87c8d3d702
7 changed files with 67 additions and 24 deletions
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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>>>([&]() {
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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);
|
||||||
|
|
Loading…
Reference in a new issue