aboutsummaryrefslogtreecommitdiff
path: root/src/libstore
diff options
context:
space:
mode:
authorEelco Dolstra <edolstra@gmail.com>2020-09-29 11:37:29 +0200
committerGitHub <noreply@github.com>2020-09-29 11:37:29 +0200
commit1e8855a7f71c31f1b6353e0cf4d752eacc85f9c8 (patch)
treeb114d877ff4fda4fa39281ebec0900d36c3de81f /src/libstore
parented02d20e1d437ea2c3f99b13f1dd2ff31c48e0c8 (diff)
parent00135e13f49e9b20e3ef03f2516e7cc277c40ca9 (diff)
Merge pull request #3958 from obsidiansystems/ca-floating-upstream
CA derivations that depend on other CA derivations
Diffstat (limited to 'src/libstore')
-rw-r--r--src/libstore/build.cc75
-rw-r--r--src/libstore/derivations.cc57
-rw-r--r--src/libstore/derivations.hh29
-rw-r--r--src/libstore/local-store.cc50
4 files changed, 188 insertions, 23 deletions
diff --git a/src/libstore/build.cc b/src/libstore/build.cc
index 629e05f3a..0499273a4 100644
--- a/src/libstore/build.cc
+++ b/src/libstore/build.cc
@@ -1008,6 +1008,8 @@ private:
void tryLocalBuild();
void buildDone();
+ void resolvedFinished();
+
/* Is the build hook willing to perform the build? */
HookReply tryBuildHook();
@@ -1483,8 +1485,40 @@ void DerivationGoal::inputsRealised()
/* Determine the full set of input paths. */
/* First, the input derivations. */
- if (useDerivation)
- for (auto & [depDrvPath, wantedDepOutputs] : dynamic_cast<Derivation *>(drv.get())->inputDrvs) {
+ if (useDerivation) {
+ auto & fullDrv = *dynamic_cast<Derivation *>(drv.get());
+
+ if (!fullDrv.inputDrvs.empty() && fullDrv.type() == DerivationType::CAFloating) {
+ /* We are be able to resolve this derivation based on the
+ now-known results of dependencies. If so, we become a stub goal
+ aliasing that resolved derivation goal */
+ std::optional attempt = fullDrv.tryResolve(worker.store);
+ assert(attempt);
+ Derivation drvResolved { *std::move(attempt) };
+
+ auto pathResolved = writeDerivation(worker.store, drvResolved);
+ /* Add to memotable to speed up downstream goal's queries with the
+ original derivation. */
+ drvPathResolutions.lock()->insert_or_assign(drvPath, pathResolved);
+
+ auto msg = fmt("Resolved derivation: '%s' -> '%s'",
+ worker.store.printStorePath(drvPath),
+ worker.store.printStorePath(pathResolved));
+ act = std::make_unique<Activity>(*logger, lvlInfo, actBuildWaiting, msg,
+ Logger::Fields {
+ worker.store.printStorePath(drvPath),
+ worker.store.printStorePath(pathResolved),
+ });
+
+ auto resolvedGoal = worker.makeDerivationGoal(
+ pathResolved, wantedOutputs, buildMode);
+ addWaitee(resolvedGoal);
+
+ state = &DerivationGoal::resolvedFinished;
+ return;
+ }
+
+ for (auto & [depDrvPath, wantedDepOutputs] : fullDrv.inputDrvs) {
/* Add the relevant output closures of the input derivation
`i' as input paths. Only add the closures of output paths
that are specified as inputs. */
@@ -1504,6 +1538,7 @@ void DerivationGoal::inputsRealised()
worker.store.printStorePath(drvPath), j, worker.store.printStorePath(drvPath));
}
}
+ }
/* Second, the input sources. */
worker.store.computeFSClosure(drv->inputSrcs, inputPaths);
@@ -1962,6 +1997,9 @@ void DerivationGoal::buildDone()
done(BuildResult::Built);
}
+void DerivationGoal::resolvedFinished() {
+ done(BuildResult::Built);
+}
HookReply DerivationGoal::tryBuildHook()
{
@@ -4257,11 +4295,13 @@ void DerivationGoal::registerOutputs()
/* Register each output path as valid, and register the sets of
paths referenced by each of them. If there are cycles in the
outputs, this will fail. */
- ValidPathInfos infos2;
- for (auto & [outputName, newInfo] : infos) {
- infos2.push_back(newInfo);
+ {
+ ValidPathInfos infos2;
+ for (auto & [outputName, newInfo] : infos) {
+ infos2.push_back(newInfo);
+ }
+ worker.store.registerValidPaths(infos2);
}
- worker.store.registerValidPaths(infos2);
/* In case of a fixed-output derivation hash mismatch, throw an
exception now that we have registered the output as valid. */
@@ -4273,12 +4313,21 @@ void DerivationGoal::registerOutputs()
means it's safe to link the derivation to the output hash. We must do
that for floating CA derivations, which otherwise couldn't be cached,
but it's fine to do in all cases. */
- for (auto & [outputName, newInfo] : infos) {
- /* FIXME: we will want to track this mapping in the DB whether or
- not we have a drv file. */
- if (useDerivation)
- worker.store.linkDeriverToPath(drvPath, outputName, newInfo.path);
+ bool isCaFloating = drv->type() == DerivationType::CAFloating;
+
+ auto drvPathResolved = drvPath;
+ if (!useDerivation && isCaFloating) {
+ /* Once a floating CA derivations reaches this point, it
+ must already be resolved, so we don't bother trying to
+ downcast drv to get would would just be an empty
+ inputDrvs field. */
+ Derivation drv2 { *drv };
+ drvPathResolved = writeDerivation(worker.store, drv2);
}
+
+ if (useDerivation || isCaFloating)
+ for (auto & [outputName, newInfo] : infos)
+ worker.store.linkDeriverToPath(drvPathResolved, outputName, newInfo.path);
}
@@ -4568,7 +4617,7 @@ void DerivationGoal::flushLine()
std::map<std::string, std::optional<StorePath>> DerivationGoal::queryPartialDerivationOutputMap()
{
- if (drv->type() != DerivationType::CAFloating) {
+ if (!useDerivation || drv->type() != DerivationType::CAFloating) {
std::map<std::string, std::optional<StorePath>> res;
for (auto & [name, output] : drv->outputs)
res.insert_or_assign(name, output.path(worker.store, drv->name, name));
@@ -4580,7 +4629,7 @@ std::map<std::string, std::optional<StorePath>> DerivationGoal::queryPartialDeri
OutputPathMap DerivationGoal::queryDerivationOutputMap()
{
- if (drv->type() != DerivationType::CAFloating) {
+ if (!useDerivation || drv->type() != DerivationType::CAFloating) {
OutputPathMap res;
for (auto & [name, output] : drv->outputsAndOptPaths(worker.store))
res.insert_or_assign(name, *output.second);
diff --git a/src/libstore/derivations.cc b/src/libstore/derivations.cc
index 9d8ce5e36..2612f1ff7 100644
--- a/src/libstore/derivations.cc
+++ b/src/libstore/derivations.cc
@@ -69,7 +69,7 @@ bool BasicDerivation::isBuiltin() const
StorePath writeDerivation(Store & store,
- const Derivation & drv, RepairFlag repair)
+ const Derivation & drv, RepairFlag repair, bool readOnly)
{
auto references = drv.inputSrcs;
for (auto & i : drv.inputDrvs)
@@ -79,7 +79,7 @@ StorePath writeDerivation(Store & store,
held during a garbage collection). */
auto suffix = std::string(drv.name) + drvExtension;
auto contents = drv.unparse(store, false);
- return settings.readOnlyMode
+ return readOnly || settings.readOnlyMode
? store.computeStorePathForText(suffix, contents, references)
: store.addTextToStore(suffix, contents, references, repair);
}
@@ -644,4 +644,57 @@ std::string downstreamPlaceholder(const Store & store, const StorePath & drvPath
return "/" + hashString(htSHA256, clearText).to_string(Base32, false);
}
+
+// N.B. Outputs are left unchanged
+static void rewriteDerivation(Store & store, BasicDerivation & drv, const StringMap & rewrites) {
+
+ debug("Rewriting the derivation");
+
+ for (auto &rewrite: rewrites) {
+ debug("rewriting %s as %s", rewrite.first, rewrite.second);
+ }
+
+ drv.builder = rewriteStrings(drv.builder, rewrites);
+ for (auto & arg: drv.args) {
+ arg = rewriteStrings(arg, rewrites);
+ }
+
+ StringPairs newEnv;
+ for (auto & envVar: drv.env) {
+ auto envName = rewriteStrings(envVar.first, rewrites);
+ auto envValue = rewriteStrings(envVar.second, rewrites);
+ newEnv.emplace(envName, envValue);
+ }
+ drv.env = newEnv;
+}
+
+
+Sync<DrvPathResolutions> drvPathResolutions;
+
+std::optional<BasicDerivation> Derivation::tryResolve(Store & store) {
+ BasicDerivation resolved { *this };
+
+ // Input paths that we'll want to rewrite in the derivation
+ StringMap inputRewrites;
+
+ for (auto & input : inputDrvs) {
+ auto inputDrvOutputs = store.queryPartialDerivationOutputMap(input.first);
+ StringSet newOutputNames;
+ for (auto & outputName : input.second) {
+ auto actualPathOpt = inputDrvOutputs.at(outputName);
+ if (!actualPathOpt)
+ return std::nullopt;
+ auto actualPath = *actualPathOpt;
+ inputRewrites.emplace(
+ downstreamPlaceholder(store, input.first, outputName),
+ store.printStorePath(actualPath));
+ resolved.inputSrcs.insert(std::move(actualPath));
+ }
+ }
+
+ rewriteDerivation(store, resolved, inputRewrites);
+
+ return resolved;
+}
+
}
diff --git a/src/libstore/derivations.hh b/src/libstore/derivations.hh
index 0b5652685..d48266774 100644
--- a/src/libstore/derivations.hh
+++ b/src/libstore/derivations.hh
@@ -4,6 +4,7 @@
#include "types.hh"
#include "hash.hh"
#include "content-address.hh"
+#include "sync.hh"
#include <map>
#include <variant>
@@ -100,7 +101,7 @@ struct BasicDerivation
StringPairs env;
std::string name;
- BasicDerivation() { }
+ BasicDerivation() = default;
virtual ~BasicDerivation() { };
bool isBuiltin() const;
@@ -127,7 +128,17 @@ struct Derivation : BasicDerivation
std::string unparse(const Store & store, bool maskOutputs,
std::map<std::string, StringSet> * actualInputs = nullptr) const;
- Derivation() { }
+ /* Return the underlying basic derivation but with these changes:
+
+ 1. Input drvs are emptied, but the outputs of them that were used are
+ added directly to input sources.
+
+ 2. Input placeholders are replaced with realized input store paths. */
+ std::optional<BasicDerivation> tryResolve(Store & store);
+
+ Derivation() = default;
+ Derivation(const BasicDerivation & bd) : BasicDerivation(bd) { }
+ Derivation(BasicDerivation && bd) : BasicDerivation(std::move(bd)) { }
};
@@ -137,7 +148,9 @@ enum RepairFlag : bool { NoRepair = false, Repair = true };
/* Write a derivation to the Nix store, and return its path. */
StorePath writeDerivation(Store & store,
- const Derivation & drv, RepairFlag repair = NoRepair);
+ const Derivation & drv,
+ RepairFlag repair = NoRepair,
+ bool readOnly = false);
/* Read a derivation from a file. */
Derivation parseDerivation(const Store & store, std::string && s, std::string_view name);
@@ -191,6 +204,16 @@ typedef std::map<StorePath, DrvHashModulo> DrvHashes;
extern DrvHashes drvHashes; // FIXME: global, not thread-safe
+/* Memoisation of `readDerivation(..).resove()`. */
+typedef std::map<
+ StorePath,
+ std::optional<StorePath>
+> DrvPathResolutions;
+
+// FIXME: global, though at least thread-safe.
+// FIXME: arguably overlaps with hashDerivationModulo memo table.
+extern Sync<DrvPathResolutions> drvPathResolutions;
+
bool wantOutput(const string & output, const std::set<string> & wanted);
struct Source;
diff --git a/src/libstore/local-store.cc b/src/libstore/local-store.cc
index ee997ef3a..d29236a9c 100644
--- a/src/libstore/local-store.cc
+++ b/src/libstore/local-store.cc
@@ -721,7 +721,7 @@ uint64_t LocalStore::queryValidPathId(State & state, const StorePath & path)
{
auto use(state.stmtQueryPathInfo.use()(printStorePath(path)));
if (!use.next())
- throw Error("path '%s' is not valid", printStorePath(path));
+ throw InvalidPath("path '%s' is not valid", printStorePath(path));
return use.getInt(0);
}
@@ -796,18 +796,58 @@ StorePathSet LocalStore::queryValidDerivers(const StorePath & path)
}
-std::map<std::string, std::optional<StorePath>> LocalStore::queryPartialDerivationOutputMap(const StorePath & path)
+std::map<std::string, std::optional<StorePath>> LocalStore::queryPartialDerivationOutputMap(const StorePath & path_)
{
+ auto path = path_;
std::map<std::string, std::optional<StorePath>> outputs;
- BasicDerivation drv = readDerivation(path);
+ Derivation drv = readDerivation(path);
for (auto & [outName, _] : drv.outputs) {
outputs.insert_or_assign(outName, std::nullopt);
}
+ bool haveCached = false;
+ {
+ auto resolutions = drvPathResolutions.lock();
+ auto resolvedPathOptIter = resolutions->find(path);
+ if (resolvedPathOptIter != resolutions->end()) {
+ auto & [_, resolvedPathOpt] = *resolvedPathOptIter;
+ if (resolvedPathOpt)
+ path = *resolvedPathOpt;
+ haveCached = true;
+ }
+ }
+ /* can't just use else-if instead of `!haveCached` because we need to unlock
+ `drvPathResolutions` before it is locked in `Derivation::resolve`. */
+ if (!haveCached && drv.type() == DerivationType::CAFloating) {
+ /* Try resolve drv and use that path instead. */
+ auto attempt = drv.tryResolve(*this);
+ if (!attempt)
+ /* If we cannot resolve the derivation, we cannot have any path
+ assigned so we return the map of all std::nullopts. */
+ return outputs;
+ /* Just compute store path */
+ auto pathResolved = writeDerivation(*this, *std::move(attempt), NoRepair, true);
+ /* Store in memo table. */
+ /* FIXME: memo logic should not be local-store specific, should have
+ wrapper-method instead. */
+ drvPathResolutions.lock()->insert_or_assign(path, pathResolved);
+ path = std::move(pathResolved);
+ }
return retrySQLite<std::map<std::string, std::optional<StorePath>>>([&]() {
auto state(_state.lock());
- auto useQueryDerivationOutputs(state->stmtQueryDerivationOutputs.use()
- (queryValidPathId(*state, path)));
+ uint64_t drvId;
+ try {
+ drvId = queryValidPathId(*state, path);
+ } catch (InvalidPath &) {
+ /* FIXME? if the derivation doesn't exist, we cannot have a mapping
+ for it. */
+ return outputs;
+ }
+
+ auto useQueryDerivationOutputs {
+ state->stmtQueryDerivationOutputs.use()
+ (drvId)
+ };
while (useQueryDerivationOutputs.next())
outputs.insert_or_assign(