aboutsummaryrefslogtreecommitdiff
path: root/src/libstore
diff options
context:
space:
mode:
Diffstat (limited to 'src/libstore')
-rw-r--r--src/libstore/binary-cache-store.cc19
-rw-r--r--src/libstore/binary-cache-store.hh31
-rw-r--r--src/libstore/build-result.hh90
-rw-r--r--src/libstore/build/derivation-goal.cc416
-rw-r--r--src/libstore/build/derivation-goal.hh49
-rw-r--r--src/libstore/build/drv-output-substitution-goal.cc16
-rw-r--r--src/libstore/build/drv-output-substitution-goal.hh2
-rw-r--r--src/libstore/build/entry-points.cc64
-rw-r--r--src/libstore/build/goal.cc6
-rw-r--r--src/libstore/build/goal.hh33
-rw-r--r--src/libstore/build/local-derivation-goal.cc222
-rw-r--r--src/libstore/build/local-derivation-goal.hh6
-rw-r--r--src/libstore/build/substitution-goal.cc34
-rw-r--r--src/libstore/build/substitution-goal.hh9
-rw-r--r--src/libstore/build/worker.cc6
-rw-r--r--src/libstore/build/worker.hh4
-rw-r--r--src/libstore/builtins/buildenv.cc14
-rw-r--r--src/libstore/builtins/fetchurl.cc2
-rw-r--r--src/libstore/builtins/unpack-channel.cc2
-rw-r--r--src/libstore/daemon.cc53
-rw-r--r--src/libstore/derivations.cc505
-rw-r--r--src/libstore/derivations.hh174
-rw-r--r--src/libstore/derived-path.cc36
-rw-r--r--src/libstore/derived-path.hh8
-rw-r--r--src/libstore/dummy-store.cc9
-rw-r--r--src/libstore/filetransfer.cc30
-rw-r--r--src/libstore/filetransfer.hh8
-rw-r--r--src/libstore/gc-store.hh84
-rw-r--r--src/libstore/gc.cc31
-rw-r--r--src/libstore/globals.cc4
-rw-r--r--src/libstore/globals.hh75
-rw-r--r--src/libstore/legacy-ssh-store.cc27
-rw-r--r--src/libstore/local-fs-store.cc2
-rw-r--r--src/libstore/local-fs-store.hh9
-rw-r--r--src/libstore/local-store.cc54
-rw-r--r--src/libstore/local-store.hh20
-rw-r--r--src/libstore/lock.hh4
-rw-r--r--src/libstore/log-store.hh21
-rw-r--r--src/libstore/machines.cc21
-rw-r--r--src/libstore/machines.hh14
-rw-r--r--src/libstore/make-content-addressed.cc80
-rw-r--r--src/libstore/make-content-addressed.hh12
-rw-r--r--src/libstore/misc.cc9
-rw-r--r--src/libstore/names.hh8
-rw-r--r--src/libstore/nar-accessor.cc2
-rw-r--r--src/libstore/nar-info.cc2
-rw-r--r--src/libstore/optimise-store.cc2
-rw-r--r--src/libstore/parsed-derivations.cc2
-rw-r--r--src/libstore/parsed-derivations.hh1
-rw-r--r--src/libstore/path-with-outputs.cc10
-rw-r--r--src/libstore/path.cc9
-rw-r--r--src/libstore/path.hh4
-rw-r--r--src/libstore/pathlocks.cc4
-rw-r--r--src/libstore/pathlocks.hh6
-rw-r--r--src/libstore/profiles.cc16
-rw-r--r--src/libstore/profiles.hh4
-rw-r--r--src/libstore/references.cc4
-rw-r--r--src/libstore/remote-store.cc144
-rw-r--r--src/libstore/remote-store.hh26
-rw-r--r--src/libstore/repair-flag.hh7
-rw-r--r--src/libstore/s3-binary-cache-store.cc11
-rw-r--r--src/libstore/sqlite.cc2
-rw-r--r--src/libstore/ssh-store.cc4
-rw-r--r--src/libstore/ssh.cc2
-rw-r--r--src/libstore/store-api.cc36
-rw-r--r--src/libstore/store-api.hh189
-rw-r--r--src/libstore/store-cast.hh16
-rw-r--r--src/libstore/worker-protocol.hh4
68 files changed, 1789 insertions, 1041 deletions
diff --git a/src/libstore/binary-cache-store.cc b/src/libstore/binary-cache-store.cc
index 6e4458f7a..9226c4e19 100644
--- a/src/libstore/binary-cache-store.cc
+++ b/src/libstore/binary-cache-store.cc
@@ -307,7 +307,7 @@ void BinaryCacheStore::addToStore(const ValidPathInfo & info, Source & narSource
}});
}
-StorePath BinaryCacheStore::addToStoreFromDump(Source & dump, const string & name,
+StorePath BinaryCacheStore::addToStoreFromDump(Source & dump, std::string_view name,
FileIngestionMethod method, HashType hashAlgo, RepairFlag repair, const StorePathSet & references)
{
if (method != FileIngestionMethod::Recursive || hashAlgo != htSHA256)
@@ -385,8 +385,14 @@ void BinaryCacheStore::queryPathInfoUncached(const StorePath & storePath,
}});
}
-StorePath BinaryCacheStore::addToStore(const string & name, const Path & srcPath,
- FileIngestionMethod method, HashType hashAlgo, PathFilter & filter, RepairFlag repair, const StorePathSet & references)
+StorePath BinaryCacheStore::addToStore(
+ std::string_view name,
+ const Path & srcPath,
+ FileIngestionMethod method,
+ HashType hashAlgo,
+ PathFilter & filter,
+ RepairFlag repair,
+ const StorePathSet & references)
{
/* FIXME: Make BinaryCacheStore::addToStoreCommon support
non-recursive+sha256 so we can just use the default
@@ -418,8 +424,11 @@ StorePath BinaryCacheStore::addToStore(const string & name, const Path & srcPath
})->path;
}
-StorePath BinaryCacheStore::addTextToStore(const string & name, const string & s,
- const StorePathSet & references, RepairFlag repair)
+StorePath BinaryCacheStore::addTextToStore(
+ std::string_view name,
+ std::string_view s,
+ const StorePathSet & references,
+ RepairFlag repair)
{
auto textHash = hashString(htSHA256, s);
auto path = makeTextPath(name, textHash, references);
diff --git a/src/libstore/binary-cache-store.hh b/src/libstore/binary-cache-store.hh
index 7599230d9..ca538b3cb 100644
--- a/src/libstore/binary-cache-store.hh
+++ b/src/libstore/binary-cache-store.hh
@@ -2,6 +2,7 @@
#include "crypto.hh"
#include "store-api.hh"
+#include "log-store.hh"
#include "pool.hh"
@@ -28,7 +29,9 @@ struct BinaryCacheStoreConfig : virtual StoreConfig
"other than -1 which we reserve to indicate Nix defaults should be used"};
};
-class BinaryCacheStore : public virtual BinaryCacheStoreConfig, public virtual Store
+class BinaryCacheStore : public virtual BinaryCacheStoreConfig,
+ public virtual Store,
+ public virtual LogStore
{
private:
@@ -98,15 +101,23 @@ public:
void addToStore(const ValidPathInfo & info, Source & narSource,
RepairFlag repair, CheckSigsFlag checkSigs) override;
- StorePath addToStoreFromDump(Source & dump, const string & name,
- FileIngestionMethod method, HashType hashAlgo, RepairFlag repair, const StorePathSet & references ) override;
-
- StorePath addToStore(const string & name, const Path & srcPath,
- FileIngestionMethod method, HashType hashAlgo,
- PathFilter & filter, RepairFlag repair, const StorePathSet & references) override;
-
- StorePath addTextToStore(const string & name, const string & s,
- const StorePathSet & references, RepairFlag repair) override;
+ StorePath addToStoreFromDump(Source & dump, std::string_view name,
+ FileIngestionMethod method, HashType hashAlgo, RepairFlag repair, const StorePathSet & references) override;
+
+ StorePath addToStore(
+ std::string_view name,
+ const Path & srcPath,
+ FileIngestionMethod method,
+ HashType hashAlgo,
+ PathFilter & filter,
+ RepairFlag repair,
+ const StorePathSet & references) override;
+
+ StorePath addTextToStore(
+ std::string_view name,
+ std::string_view s,
+ const StorePathSet & references,
+ RepairFlag repair) override;
void registerDrvOutput(const Realisation & info) override;
diff --git a/src/libstore/build-result.hh b/src/libstore/build-result.hh
new file mode 100644
index 000000000..7f9bcce6d
--- /dev/null
+++ b/src/libstore/build-result.hh
@@ -0,0 +1,90 @@
+#pragma once
+
+#include "realisation.hh"
+#include "derived-path.hh"
+
+#include <string>
+#include <chrono>
+
+
+namespace nix {
+
+struct BuildResult
+{
+ /* Note: don't remove status codes, and only add new status codes
+ at the end of the list, to prevent client/server
+ incompatibilities in the nix-store --serve protocol. */
+ enum Status {
+ Built = 0,
+ Substituted,
+ AlreadyValid,
+ PermanentFailure,
+ InputRejected,
+ OutputRejected,
+ TransientFailure, // possibly transient
+ CachedFailure, // no longer used
+ TimedOut,
+ MiscFailure,
+ DependencyFailed,
+ LogLimitExceeded,
+ NotDeterministic,
+ ResolvesToAlreadyValid,
+ NoSubstituters,
+ } status = MiscFailure;
+ std::string errorMsg;
+
+ std::string toString() const {
+ auto strStatus = [&]() {
+ switch (status) {
+ case Built: return "Built";
+ case Substituted: return "Substituted";
+ case AlreadyValid: return "AlreadyValid";
+ case PermanentFailure: return "PermanentFailure";
+ case InputRejected: return "InputRejected";
+ case OutputRejected: return "OutputRejected";
+ case TransientFailure: return "TransientFailure";
+ case CachedFailure: return "CachedFailure";
+ case TimedOut: return "TimedOut";
+ case MiscFailure: return "MiscFailure";
+ case DependencyFailed: return "DependencyFailed";
+ case LogLimitExceeded: return "LogLimitExceeded";
+ case NotDeterministic: return "NotDeterministic";
+ case ResolvesToAlreadyValid: return "ResolvesToAlreadyValid";
+ default: return "Unknown";
+ };
+ }();
+ return strStatus + ((errorMsg == "") ? "" : " : " + errorMsg);
+ }
+
+ /* How many times this build was performed. */
+ unsigned int timesBuilt = 0;
+
+ /* If timesBuilt > 1, whether some builds did not produce the same
+ result. (Note that 'isNonDeterministic = false' does not mean
+ the build is deterministic, just that we don't have evidence of
+ non-determinism.) */
+ bool isNonDeterministic = false;
+
+ /* The derivation we built or the store path we substituted. */
+ DerivedPath path;
+
+ /* For derivations, a mapping from the names of the wanted outputs
+ to actual paths. */
+ DrvOutputs builtOutputs;
+
+ /* The start/stop times of the build (or one of the rounds, if it
+ was repeated). */
+ time_t startTime = 0, stopTime = 0;
+
+ bool success()
+ {
+ return status == Built || status == Substituted || status == AlreadyValid || status == ResolvesToAlreadyValid;
+ }
+
+ void rethrow()
+ {
+ throw Error("%s", errorMsg);
+ }
+};
+
+}
diff --git a/src/libstore/build/derivation-goal.cc b/src/libstore/build/derivation-goal.cc
index 151217b8b..6582497bd 100644
--- a/src/libstore/build/derivation-goal.cc
+++ b/src/libstore/build/derivation-goal.cc
@@ -66,7 +66,7 @@ namespace nix {
DerivationGoal::DerivationGoal(const StorePath & drvPath,
const StringSet & wantedOutputs, Worker & worker, BuildMode buildMode)
- : Goal(worker)
+ : Goal(worker, DerivedPath::Built { .drvPath = drvPath, .outputs = wantedOutputs })
, useDerivation(true)
, drvPath(drvPath)
, wantedOutputs(wantedOutputs)
@@ -85,7 +85,7 @@ DerivationGoal::DerivationGoal(const StorePath & drvPath,
DerivationGoal::DerivationGoal(const StorePath & drvPath, const BasicDerivation & drv,
const StringSet & wantedOutputs, Worker & worker, BuildMode buildMode)
- : Goal(worker)
+ : Goal(worker, DerivedPath::Built { .drvPath = drvPath, .outputs = wantedOutputs })
, useDerivation(false)
, drvPath(drvPath)
, wantedOutputs(wantedOutputs)
@@ -116,7 +116,7 @@ DerivationGoal::~DerivationGoal()
}
-string DerivationGoal::key()
+std::string DerivationGoal::key()
{
/* Ensure that derivations get built in order of their name,
i.e. a derivation named "aardvark" always comes before
@@ -135,7 +135,7 @@ void DerivationGoal::killChild()
void DerivationGoal::timedOut(Error && ex)
{
killChild();
- done(BuildResult::TimedOut, ex);
+ done(BuildResult::TimedOut, {}, ex);
}
@@ -182,7 +182,7 @@ void DerivationGoal::loadDerivation()
trace("loading derivation");
if (nrFailed != 0) {
- done(BuildResult::MiscFailure, Error("cannot build missing derivation '%s'", worker.store.printStorePath(drvPath)));
+ done(BuildResult::MiscFailure, {}, Error("cannot build missing derivation '%s'", worker.store.printStorePath(drvPath)));
return;
}
@@ -204,10 +204,33 @@ void DerivationGoal::haveDerivation()
{
trace("have derivation");
- if (drv->type() == DerivationType::CAFloating)
+ parsedDrv = std::make_unique<ParsedDerivation>(drvPath, *drv);
+
+ if (!drv->type().hasKnownOutputPaths())
settings.requireExperimentalFeature(Xp::CaDerivations);
- retrySubstitution = false;
+ if (!drv->type().isPure()) {
+ settings.requireExperimentalFeature(Xp::ImpureDerivations);
+
+ for (auto & [outputName, output] : drv->outputs) {
+ auto randomPath = StorePath::random(outputPathName(drv->name, outputName));
+ assert(!worker.store.isValidPath(randomPath));
+ initialOutputs.insert({
+ outputName,
+ InitialOutput {
+ .wanted = true,
+ .outputHash = impureOutputHash,
+ .known = InitialOutputStatus {
+ .path = randomPath,
+ .status = PathStatus::Absent
+ }
+ }
+ });
+ }
+
+ gaveUpOnSubstitution();
+ return;
+ }
for (auto & i : drv->outputsAndOptPaths(worker.store))
if (i.second.second)
@@ -215,34 +238,23 @@ void DerivationGoal::haveDerivation()
auto outputHashes = staticOutputHashes(worker.evalStore, *drv);
for (auto & [outputName, outputHash] : outputHashes)
- initialOutputs.insert({
+ initialOutputs.insert({
outputName,
- InitialOutput{
+ InitialOutput {
.wanted = true, // Will be refined later
.outputHash = outputHash
}
- });
+ });
/* Check what outputs paths are not already valid. */
- checkPathValidity();
- bool allValid = true;
- for (auto & [_, status] : initialOutputs) {
- if (!status.wanted) continue;
- if (!status.known || !status.known->isValid()) {
- allValid = false;
- break;
- }
- }
+ auto [allValid, validOutputs] = checkPathValidity();
/* If they are all valid, then we're done. */
if (allValid && buildMode == bmNormal) {
- done(BuildResult::AlreadyValid);
+ done(BuildResult::AlreadyValid, std::move(validOutputs));
return;
}
- parsedDrv = std::make_unique<ParsedDerivation>(drvPath, *drv);
-
-
/* We are first going to try to create the invalid output paths
through substitutes. If that doesn't work, we'll build
them. */
@@ -276,8 +288,10 @@ void DerivationGoal::outputsSubstitutionTried()
{
trace("all outputs substituted (maybe)");
+ assert(drv->type().isPure());
+
if (nrFailed > 0 && nrFailed > nrNoSubstituters + nrIncompleteClosure && !settings.tryFallback) {
- done(BuildResult::TransientFailure,
+ done(BuildResult::TransientFailure, {},
Error("some substitutes for the outputs of derivation '%s' failed (usually happens due to networking issues); try '--fallback' to build derivation from source ",
worker.store.printStorePath(drvPath)));
return;
@@ -301,23 +315,17 @@ void DerivationGoal::outputsSubstitutionTried()
return;
}
- checkPathValidity();
- size_t nrInvalid = 0;
- for (auto & [_, status] : initialOutputs) {
- if (!status.wanted) continue;
- if (!status.known || !status.known->isValid())
- nrInvalid++;
- }
+ auto [allValid, validOutputs] = checkPathValidity();
- if (buildMode == bmNormal && nrInvalid == 0) {
- done(BuildResult::Substituted);
+ if (buildMode == bmNormal && allValid) {
+ done(BuildResult::Substituted, std::move(validOutputs));
return;
}
- if (buildMode == bmRepair && nrInvalid == 0) {
+ if (buildMode == bmRepair && allValid) {
repairClosure();
return;
}
- if (buildMode == bmCheck && nrInvalid > 0)
+ if (buildMode == bmCheck && !allValid)
throw Error("some outputs of '%s' are not valid, so checking is not possible",
worker.store.printStorePath(drvPath));
@@ -325,18 +333,27 @@ void DerivationGoal::outputsSubstitutionTried()
gaveUpOnSubstitution();
}
+
/* At least one of the output paths could not be
produced using a substitute. So we have to build instead. */
void DerivationGoal::gaveUpOnSubstitution()
{
- /* Make sure checkPathValidity() from now on checks all
- outputs. */
- wantedOutputs.clear();
-
/* The inputs must be built before we can build this goal. */
+ inputDrvOutputs.clear();
if (useDerivation)
- for (auto & i : dynamic_cast<Derivation *>(drv.get())->inputDrvs)
+ for (auto & i : dynamic_cast<Derivation *>(drv.get())->inputDrvs) {
+ /* Ensure that pure, non-fixed-output derivations don't
+ depend on impure derivations. */
+ if (drv->type().isPure() && !drv->type().isFixed()) {
+ auto inputDrv = worker.evalStore.readDerivation(i.first);
+ if (!inputDrv.type().isPure())
+ throw Error("pure derivation '%s' depends on impure derivation '%s'",
+ worker.store.printStorePath(drvPath),
+ worker.store.printStorePath(i.first));
+ }
+
addWaitee(worker.makeDerivationGoal(i.first, i.second, buildMode == bmRepair ? bmRepair : bmNormal));
+ }
/* Copy the input sources from the eval store to the build
store. */
@@ -364,6 +381,8 @@ void DerivationGoal::gaveUpOnSubstitution()
void DerivationGoal::repairClosure()
{
+ assert(drv->type().isPure());
+
/* If we're repairing, we now know that our own outputs are valid.
Now check whether the other paths in the outputs closure are
good. If not, then start derivation goals for the derivations
@@ -409,7 +428,7 @@ void DerivationGoal::repairClosure()
}
if (waitees.empty()) {
- done(BuildResult::AlreadyValid);
+ done(BuildResult::AlreadyValid, assertPathValidity());
return;
}
@@ -423,7 +442,7 @@ void DerivationGoal::closureRepaired()
if (nrFailed > 0)
throw Error("some paths in the output closure of derivation '%s' could not be repaired",
worker.store.printStorePath(drvPath));
- done(BuildResult::AlreadyValid);
+ done(BuildResult::AlreadyValid, assertPathValidity());
}
@@ -434,13 +453,14 @@ void DerivationGoal::inputsRealised()
if (nrFailed != 0) {
if (!useDerivation)
throw Error("some dependencies of '%s' are missing", worker.store.printStorePath(drvPath));
- done(BuildResult::DependencyFailed, Error(
+ done(BuildResult::DependencyFailed, {}, Error(
"%s dependencies of derivation '%s' failed to build",
nrFailed, worker.store.printStorePath(drvPath)));
return;
}
- if (retrySubstitution) {
+ if (retrySubstitution && !retriedSubstitution) {
+ retriedSubstitution = true;
haveDerivation();
return;
}
@@ -454,19 +474,40 @@ void DerivationGoal::inputsRealised()
if (useDerivation) {
auto & fullDrv = *dynamic_cast<Derivation *>(drv.get());
- if (settings.isExperimentalFeatureEnabled(Xp::CaDerivations) &&
- ((!fullDrv.inputDrvs.empty() && derivationIsCA(fullDrv.type()))
- || fullDrv.type() == DerivationType::DeferredInputAddressed)) {
+ auto drvType = fullDrv.type();
+ bool resolveDrv = std::visit(overloaded {
+ [&](const DerivationType::InputAddressed & ia) {
+ /* must resolve if deferred. */
+ return ia.deferred;
+ },
+ [&](const DerivationType::ContentAddressed & ca) {
+ return !fullDrv.inputDrvs.empty() && (
+ ca.fixed
+ /* Can optionally resolve if fixed, which is good
+ for avoiding unnecessary rebuilds. */
+ ? settings.isExperimentalFeatureEnabled(Xp::CaDerivations)
+ /* Must resolve if floating and there are any inputs
+ drvs. */
+ : true);
+ },
+ [&](const DerivationType::Impure &) {
+ return true;
+ }
+ }, drvType.raw());
+
+ if (resolveDrv && !fullDrv.inputDrvs.empty()) {
+ settings.requireExperimentalFeature(Xp::CaDerivations);
+
/* 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);
+ now-known results of dependencies. If so, we become a
+ stub goal aliasing that resolved derivation goal. */
+ std::optional attempt = fullDrv.tryResolve(worker.store, inputDrvOutputs);
assert(attempt);
Derivation drvResolved { *std::move(attempt) };
auto pathResolved = writeDerivation(worker.store, drvResolved);
- auto msg = fmt("Resolved derivation: '%s' -> '%s'",
+ auto msg = fmt("resolved derivation: '%s' -> '%s'",
worker.store.printStorePath(drvPath),
worker.store.printStorePath(pathResolved));
act = std::make_unique<Activity>(*logger, lvlInfo, actBuildWaiting, msg,
@@ -487,21 +528,13 @@ void DerivationGoal::inputsRealised()
/* 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. */
- assert(worker.evalStore.isValidPath(drvPath));
- auto outputs = worker.evalStore.queryPartialDerivationOutputMap(depDrvPath);
- for (auto & j : wantedDepOutputs) {
- if (outputs.count(j) > 0) {
- auto optRealizedInput = outputs.at(j);
- if (!optRealizedInput)
- throw Error(
- "derivation '%s' requires output '%s' from input derivation '%s', which is supposedly realized already, yet we still don't know what path corresponds to that output",
- worker.store.printStorePath(drvPath), j, worker.store.printStorePath(depDrvPath));
- worker.store.computeFSClosure(*optRealizedInput, inputPaths);
- } else
+ for (auto & j : wantedDepOutputs)
+ if (auto outPath = get(inputDrvOutputs, { depDrvPath, j }))
+ worker.store.computeFSClosure(*outPath, inputPaths);
+ else
throw Error(
"derivation '%s' requires non-existent output '%s' from input derivation '%s'",
worker.store.printStorePath(drvPath), j, worker.store.printStorePath(depDrvPath));
- }
}
}
@@ -515,7 +548,7 @@ void DerivationGoal::inputsRealised()
/* Don't repeat fixed-output derivations since they're already
verified by their output hash.*/
- nrRounds = derivationIsFixed(derivationType) ? 1 : settings.buildRepeat + 1;
+ nrRounds = derivationType.isFixed() ? 1 : settings.buildRepeat + 1;
/* Okay, try to build. Note that here we don't wait for a build
slot to become available, since we don't need one if there is a
@@ -523,10 +556,11 @@ void DerivationGoal::inputsRealised()
state = &DerivationGoal::tryToBuild;
worker.wakeUp(shared_from_this());
- result = BuildResult();
+ buildResult = BuildResult { .path = buildResult.path };
}
-void DerivationGoal::started() {
+void DerivationGoal::started()
+{
auto msg = fmt(
buildMode == bmRepair ? "repairing outputs of '%s'" :
buildMode == bmCheck ? "checking outputs of '%s'" :
@@ -588,19 +622,12 @@ void DerivationGoal::tryToBuild()
omitted, but that would be less efficient.) Note that since we
now hold the locks on the output paths, no other process can
build this derivation, so no further checks are necessary. */
- checkPathValidity();
- bool allValid = true;
- for (auto & [_, status] : initialOutputs) {
- if (!status.wanted) continue;
- if (!status.known || !status.known->isValid()) {
- allValid = false;
- break;
- }
- }
+ auto [allValid, validOutputs] = checkPathValidity();
+
if (buildMode != bmCheck && allValid) {
debug("skipping build of derivation '%s', someone beat us to it", worker.store.printStorePath(drvPath));
outputLocks.setDeletion(true);
- done(BuildResult::AlreadyValid);
+ done(BuildResult::AlreadyValid, std::move(validOutputs));
return;
}
@@ -626,7 +653,7 @@ void DerivationGoal::tryToBuild()
/* Yes, it has started doing so. Wait until we get
EOF from the hook. */
actLock.reset();
- result.startTime = time(0); // inexact
+ buildResult.startTime = time(0); // inexact
state = &DerivationGoal::buildDone;
started();
return;
@@ -830,8 +857,8 @@ void DerivationGoal::buildDone()
debug("builder process for '%s' finished", worker.store.printStorePath(drvPath));
- result.timesBuilt++;
- result.stopTime = time(0);
+ buildResult.timesBuilt++;
+ buildResult.stopTime = time(0);
/* So the child is gone now. */
worker.childTerminated(this);
@@ -876,11 +903,11 @@ void DerivationGoal::buildDone()
/* Compute the FS closure of the outputs and register them as
being valid. */
- registerOutputs();
+ auto builtOutputs = registerOutputs();
StorePathSet outputPaths;
- for (auto & [_, path] : finalOutputs)
- outputPaths.insert(path);
+ for (auto & [_, output] : buildResult.builtOutputs)
+ outputPaths.insert(output.outPath);
runPostBuildHook(
worker.store,
*logger,
@@ -890,7 +917,7 @@ void DerivationGoal::buildDone()
if (buildMode == bmCheck) {
cleanupPostOutputsRegisteredModeCheck();
- done(BuildResult::Built);
+ done(BuildResult::Built, std::move(builtOutputs));
return;
}
@@ -911,6 +938,8 @@ void DerivationGoal::buildDone()
outputLocks.setDeletion(true);
outputLocks.unlock();
+ done(BuildResult::Built, std::move(builtOutputs));
+
} catch (BuildError & e) {
outputLocks.unlock();
@@ -926,71 +955,66 @@ void DerivationGoal::buildDone()
st =
dynamic_cast<NotDeterministic*>(&e) ? BuildResult::NotDeterministic :
statusOk(status) ? BuildResult::OutputRejected :
- derivationIsImpure(derivationType) || diskFull ? BuildResult::TransientFailure :
+ !derivationType.isSandboxed() || diskFull ? BuildResult::TransientFailure :
BuildResult::PermanentFailure;
}
- done(st, e);
+ done(st, {}, e);
return;
}
-
- done(BuildResult::Built);
}
-void DerivationGoal::resolvedFinished() {
+void DerivationGoal::resolvedFinished()
+{
+ trace("resolved derivation finished");
+
assert(resolvedDrvGoal);
auto resolvedDrv = *resolvedDrvGoal->drv;
+ auto & resolvedResult = resolvedDrvGoal->buildResult;
- auto resolvedHashes = staticOutputHashes(worker.store, resolvedDrv);
+ DrvOutputs builtOutputs;
- StorePathSet outputPaths;
+ if (resolvedResult.success()) {
+ auto resolvedHashes = staticOutputHashes(worker.store, resolvedDrv);
- // `wantedOutputs` might be empty, which means “all the outputs”
- auto realWantedOutputs = wantedOutputs;
- if (realWantedOutputs.empty())
- realWantedOutputs = resolvedDrv.outputNames();
+ StorePathSet outputPaths;
- for (auto & wantedOutput : realWantedOutputs) {
- assert(initialOutputs.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{initialOutputs.at(wantedOutput).outputHash, wantedOutput};
- newRealisation.signatures.clear();
- newRealisation.dependentRealisations = drvOutputReferences(worker.store, *drv, realisation->outPath);
- signRealisation(newRealisation);
- worker.store.registerDrvOutput(newRealisation);
- outputPaths.insert(realisation->outPath);
- } else {
- // If we don't have a realisation, then it must mean that something
- // failed when building the resolved drv
- assert(!result.success());
+ // `wantedOutputs` might be empty, which means “all the outputs”
+ auto realWantedOutputs = wantedOutputs;
+ if (realWantedOutputs.empty())
+ realWantedOutputs = resolvedDrv.outputNames();
+
+ for (auto & wantedOutput : realWantedOutputs) {
+ assert(initialOutputs.count(wantedOutput) != 0);
+ assert(resolvedHashes.count(wantedOutput) != 0);
+ auto realisation = resolvedResult.builtOutputs.at(
+ DrvOutput { resolvedHashes.at(wantedOutput), wantedOutput });
+ if (drv->type().isPure()) {
+ auto newRealisation = realisation;
+ newRealisation.id = DrvOutput { initialOutputs.at(wantedOutput).outputHash, wantedOutput };
+ newRealisation.signatures.clear();
+ if (!drv->type().isFixed())
+ newRealisation.dependentRealisations = drvOutputReferences(worker.store, *drv, realisation.outPath);
+ signRealisation(newRealisation);
+ worker.store.registerDrvOutput(newRealisation);
+ }
+ outputPaths.insert(realisation.outPath);
+ builtOutputs.emplace(realisation.id, realisation);
}
+
+ runPostBuildHook(
+ worker.store,
+ *logger,
+ drvPath,
+ outputPaths
+ );
}
- runPostBuildHook(
- worker.store,
- *logger,
- drvPath,
- outputPaths
- );
-
- auto status = [&]() {
- auto resolvedResult = resolvedDrvGoal->getResult();
- switch (resolvedResult.status) {
- case BuildResult::AlreadyValid:
- return BuildResult::ResolvesToAlreadyValid;
- default:
- return resolvedResult.status;
- }
- }();
+ auto status = resolvedResult.status;
+ if (status == BuildResult::AlreadyValid)
+ status = BuildResult::ResolvesToAlreadyValid;
- done(status);
+ done(status, std::move(builtOutputs));
}
HookReply DerivationGoal::tryBuildHook()
@@ -1013,7 +1037,7 @@ HookReply DerivationGoal::tryBuildHook()
/* Read the first line of input, which should be a word indicating
whether the hook wishes to perform the build. */
- string reply;
+ std::string reply;
while (true) {
auto s = [&]() {
try {
@@ -1025,8 +1049,8 @@ HookReply DerivationGoal::tryBuildHook()
}();
if (handleJSONLogMessage(s, worker.act, worker.hook->activities, true))
;
- else if (string(s, 0, 2) == "# ") {
- reply = string(s, 2);
+ else if (s.substr(0, 2) == "# ") {
+ reply = s.substr(2);
break;
}
else {
@@ -1091,7 +1115,7 @@ HookReply DerivationGoal::tryBuildHook()
/* Create the log file and pipe. */
Path logFile = openLogFile();
- set<int> fds;
+ std::set<int> fds;
fds.insert(hook->fromHook.readSide.get());
fds.insert(hook->builderOut.readSide.get());
worker.childStarted(shared_from_this(), fds, false, false);
@@ -1100,7 +1124,7 @@ HookReply DerivationGoal::tryBuildHook()
}
-void DerivationGoal::registerOutputs()
+DrvOutputs DerivationGoal::registerOutputs()
{
/* When using a build hook, the build hook can register the output
as valid (by doing `nix-store --import'). If so we don't have
@@ -1109,21 +1133,7 @@ void DerivationGoal::registerOutputs()
We can only early return when the outputs are known a priori. For
floating content-addressed derivations this isn't the case.
*/
- for (auto & [outputName, optOutputPath] : worker.store.queryPartialDerivationOutputMap(drvPath)) {
- if (!wantOutput(outputName, wantedOutputs))
- continue;
- if (!optOutputPath)
- throw BuildError(
- "output '%s' from derivation '%s' does not have a known output path",
- outputName, worker.store.printStorePath(drvPath));
- auto & outputPath = *optOutputPath;
- if (!worker.store.isValidPath(outputPath))
- throw BuildError(
- "output '%s' from derivation '%s' is supposed to be at '%s' but that path is not valid",
- outputName, worker.store.printStorePath(drvPath), worker.store.printStorePath(outputPath));
-
- finalOutputs.insert_or_assign(outputName, outputPath);
- }
+ return assertPathValidity();
}
Path DerivationGoal::openLogFile()
@@ -1140,10 +1150,10 @@ Path DerivationGoal::openLogFile()
logDir = localStore->logDir;
else
logDir = settings.nixLogDir;
- Path dir = fmt("%s/%s/%s/", logDir, LocalFSStore::drvsLogDir, string(baseName, 0, 2));
+ Path dir = fmt("%s/%s/%s/", logDir, LocalFSStore::drvsLogDir, baseName.substr(0, 2));
createDirs(dir);
- Path logFileName = fmt("%s/%s%s", dir, string(baseName, 2),
+ Path logFileName = fmt("%s/%s%s", dir, baseName.substr(2),
settings.compressLog ? ".bz2" : "");
fdLogFile = open(logFileName.c_str(), O_CREAT | O_WRONLY | O_TRUNC | O_CLOEXEC, 0666);
@@ -1175,16 +1185,17 @@ bool DerivationGoal::isReadDesc(int fd)
return fd == hook->builderOut.readSide.get();
}
-
-void DerivationGoal::handleChildOutput(int fd, const string & data)
+void DerivationGoal::handleChildOutput(int fd, std::string_view data)
{
- if (isReadDesc(fd))
+ // local & `ssh://`-builds are dealt with here.
+ auto isWrittenToLog = isReadDesc(fd);
+ if (isWrittenToLog)
{
logSize += data.size();
if (settings.maxLogSize && logSize > settings.maxLogSize) {
killChild();
done(
- BuildResult::LogLimitExceeded,
+ BuildResult::LogLimitExceeded, {},
Error("%s killed after writing more than %d bytes of log output",
getName(), settings.maxLogSize));
return;
@@ -1207,7 +1218,16 @@ void DerivationGoal::handleChildOutput(int fd, const string & data)
if (hook && fd == hook->fromHook.readSide.get()) {
for (auto c : data)
if (c == '\n') {
- handleJSONLogMessage(currentHookLine, worker.act, hook->activities, true);
+ auto json = parseJSONMessage(currentHookLine);
+ if (json) {
+ auto s = handleJSONLogMessage(*json, worker.act, hook->activities, true);
+ // ensure that logs from a builder using `ssh-ng://` as protocol
+ // are also available to `nix log`.
+ if (s && !isWrittenToLog && logSink && (*json)["type"] == resBuildLogLine) {
+ auto f = (*json)["fields"];
+ (*logSink)((f.size() > 0 ? f.at(0).get<std::string>() : "") + "\n");
+ }
+ }
currentHookLine.clear();
} else
currentHookLine += c;
@@ -1241,7 +1261,8 @@ void DerivationGoal::flushLine()
std::map<std::string, std::optional<StorePath>> DerivationGoal::queryPartialDerivationOutputMap()
{
- if (!useDerivation || drv->type() != DerivationType::CAFloating) {
+ assert(drv->type().isPure());
+ if (!useDerivation || drv->type().hasKnownOutputPaths()) {
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));
@@ -1253,7 +1274,8 @@ std::map<std::string, std::optional<StorePath>> DerivationGoal::queryPartialDeri
OutputPathMap DerivationGoal::queryDerivationOutputMap()
{
- if (!useDerivation || drv->type() != DerivationType::CAFloating) {
+ assert(drv->type().isPure());
+ if (!useDerivation || drv->type().hasKnownOutputPaths()) {
OutputPathMap res;
for (auto & [name, output] : drv->outputsAndOptPaths(worker.store))
res.insert_or_assign(name, *output.second);
@@ -1264,10 +1286,14 @@ OutputPathMap DerivationGoal::queryDerivationOutputMap()
}
-void DerivationGoal::checkPathValidity()
+std::pair<bool, DrvOutputs> DerivationGoal::checkPathValidity()
{
+ if (!drv->type().isPure()) return { false, {} };
+
bool checkHash = buildMode == bmRepair;
auto wantedOutputsLeft = wantedOutputs;
+ DrvOutputs validOutputs;
+
for (auto & i : queryPartialDerivationOutputMap()) {
InitialOutput & info = initialOutputs.at(i.first);
info.wanted = wantOutput(i.first, wantedOutputs);
@@ -1284,27 +1310,30 @@ void DerivationGoal::checkPathValidity()
: PathStatus::Corrupt,
};
}
+ auto drvOutput = DrvOutput{initialOutputs.at(i.first).outputHash, i.first};
if (settings.isExperimentalFeatureEnabled(Xp::CaDerivations)) {
- auto drvOutput = DrvOutput{initialOutputs.at(i.first).outputHash, i.first};
if (auto real = worker.store.queryRealisation(drvOutput)) {
info.known = {
.path = real->outPath,
.status = PathStatus::Valid,
};
- } else if (info.known && info.known->status == PathStatus::Valid) {
- // We know the output because it' a static output of the
+ } else if (info.known && info.known->isValid()) {
+ // We know the output because it's a static output of the
// derivation, and the output path is valid, but we don't have
// its realisation stored (probably because it has been built
- // without the `ca-derivations` experimental flag)
+ // without the `ca-derivations` experimental flag).
worker.store.registerDrvOutput(
- Realisation{
+ Realisation {
drvOutput,
info.known->path,
}
);
}
}
+ if (info.wanted && info.known && info.known->isValid())
+ validOutputs.emplace(drvOutput, Realisation { drvOutput, info.known->path });
}
+
// If we requested all the outputs via the empty set, we are always fine.
// If we requested specific elements, the loop above removes all the valid
// ones, so any that are left must be invalid.
@@ -1312,24 +1341,49 @@ void DerivationGoal::checkPathValidity()
throw Error("derivation '%s' does not have wanted outputs %s",
worker.store.printStorePath(drvPath),
concatStringsSep(", ", quoteStrings(wantedOutputsLeft)));
+
+ bool allValid = true;
+ for (auto & [_, status] : initialOutputs) {
+ if (!status.wanted) continue;
+ if (!status.known || !status.known->isValid()) {
+ allValid = false;
+ break;
+ }
+ }
+
+ return { allValid, validOutputs };
+}
+
+
+DrvOutputs DerivationGoal::assertPathValidity()
+{
+ auto [allValid, validOutputs] = checkPathValidity();
+ if (!allValid)
+ throw Error("some outputs are unexpectedly invalid");
+ return validOutputs;
}
-void DerivationGoal::done(BuildResult::Status status, std::optional<Error> ex)
+void DerivationGoal::done(
+ BuildResult::Status status,
+ DrvOutputs builtOutputs,
+ std::optional<Error> ex)
{
- result.status = status;
+ buildResult.status = status;
if (ex)
- result.errorMsg = ex->what();
- amDone(result.success() ? ecSuccess : ecFailed, ex);
- if (result.status == BuildResult::TimedOut)
+ // FIXME: strip: "error: "
+ buildResult.errorMsg = ex->what();
+ if (buildResult.status == BuildResult::TimedOut)
worker.timedOut = true;
- if (result.status == BuildResult::PermanentFailure)
+ if (buildResult.status == BuildResult::PermanentFailure)
worker.permanentFailure = true;
mcExpectedBuilds.reset();
mcRunningBuilds.reset();
- if (result.success()) {
+ if (buildResult.success()) {
+ assert(!builtOutputs.empty());
+ buildResult.builtOutputs = std::move(builtOutputs);
if (status == BuildResult::Built)
worker.doneBuilds++;
} else {
@@ -1343,9 +1397,23 @@ void DerivationGoal::done(BuildResult::Status status, std::optional<Error> ex)
if (traceBuiltOutputsFile != "") {
std::fstream fs;
fs.open(traceBuiltOutputsFile, std::fstream::out);
- fs << worker.store.printStorePath(drvPath) << "\t" << result.toString() << std::endl;
+ fs << worker.store.printStorePath(drvPath) << "\t" << buildResult.toString() << std::endl;
}
+
+ amDone(buildResult.success() ? ecSuccess : ecFailed, ex);
}
+void DerivationGoal::waiteeDone(GoalPtr waitee, ExitCode result)
+{
+ Goal::waiteeDone(waitee, result);
+
+ if (waitee->buildResult.success())
+ if (auto bfd = std::get_if<DerivedPath::Built>(&waitee->buildResult.path))
+ for (auto & [output, realisation] : waitee->buildResult.builtOutputs)
+ inputDrvOutputs.insert_or_assign(
+ { bfd->drvPath, output.outputName },
+ realisation.outPath);
+}
+
}
diff --git a/src/libstore/build/derivation-goal.hh b/src/libstore/build/derivation-goal.hh
index e112542c7..2d8bfd592 100644
--- a/src/libstore/build/derivation-goal.hh
+++ b/src/libstore/build/derivation-goal.hh
@@ -57,12 +57,21 @@ struct DerivationGoal : public Goal
them. */
StringSet wantedOutputs;
+ /* Mapping from input derivations + output names to actual store
+ paths. This is filled in by waiteeDone() as each dependency
+ finishes, before inputsRealised() is reached, */
+ std::map<std::pair<StorePath, std::string>, StorePath> inputDrvOutputs;
+
/* Whether additional wanted outputs have been added. */
bool needRestart = false;
/* Whether to retry substituting the outputs after building the
- inputs. */
- bool retrySubstitution;
+ inputs. This is done in case of an incomplete closure. */
+ bool retrySubstitution = false;
+
+ /* Whether we've retried substitution, in which case we won't try
+ again. */
+ bool retriedSubstitution = false;
/* The derivation stored at drvPath. */
std::unique_ptr<Derivation> drv;
@@ -104,20 +113,8 @@ struct DerivationGoal : public Goal
typedef void (DerivationGoal::*GoalState)();
GoalState state;
- /* The final output paths of the build.
-
- - For input-addressed derivations, always the precomputed paths
-
- - For content-addressed derivations, calcuated from whatever the hash
- ends up being. (Note that fixed outputs derivations that produce the
- "wrong" output still install that data under its true content-address.)
- */
- OutputPathMap finalOutputs;
-
BuildMode buildMode;
- BuildResult result;
-
/* The current round, if we're building multiple times. */
size_t curRound = 1;
@@ -145,15 +142,13 @@ struct DerivationGoal : public Goal
void timedOut(Error && ex) override;
- string key() override;
+ std::string key() override;
void work() override;
/* Add wanted outputs to an already existing derivation goal. */
void addWantedOutputs(const StringSet & outputs);
- BuildResult getResult() { return result; }
-
/* The states. */
void getDerivation();
void loadDerivation();
@@ -175,7 +170,7 @@ struct DerivationGoal : public Goal
/* Check that the derivation outputs all exist and register them
as valid. */
- virtual void registerOutputs();
+ virtual DrvOutputs registerOutputs();
/* Open a log file and a pipe to it. */
Path openLogFile();
@@ -200,7 +195,7 @@ struct DerivationGoal : public Goal
virtual bool isReadDesc(int fd);
/* Callback used by the worker to write to the log. */
- void handleChildOutput(int fd, const string & data) override;
+ void handleChildOutput(int fd, std::string_view data) override;
void handleEOF(int fd) override;
void flushLine();
@@ -210,8 +205,17 @@ struct DerivationGoal : public Goal
std::map<std::string, std::optional<StorePath>> queryPartialDerivationOutputMap();
OutputPathMap queryDerivationOutputMap();
- /* Return the set of (in)valid paths. */
- void checkPathValidity();
+ /* Update 'initialOutputs' to determine the current status of the
+ outputs of the derivation. Also returns a Boolean denoting
+ whether all outputs are valid and non-corrupt, and a
+ 'DrvOutputs' structure containing the valid and wanted
+ outputs. */
+ std::pair<bool, DrvOutputs> checkPathValidity();
+
+ /* Aborts if any output is not valid or corrupt, and otherwise
+ returns a 'DrvOutputs' structure containing the wanted
+ outputs. */
+ DrvOutputs assertPathValidity();
/* Forcibly kill the child process, if any. */
virtual void killChild();
@@ -222,8 +226,11 @@ struct DerivationGoal : public Goal
void done(
BuildResult::Status status,
+ DrvOutputs builtOutputs = {},
std::optional<Error> ex = {});
+ void waiteeDone(GoalPtr waitee, ExitCode result) override;
+
StorePathSet exportReferences(const StorePathSet & storePaths);
};
diff --git a/src/libstore/build/drv-output-substitution-goal.cc b/src/libstore/build/drv-output-substitution-goal.cc
index b9602e696..b7f7b5ab1 100644
--- a/src/libstore/build/drv-output-substitution-goal.cc
+++ b/src/libstore/build/drv-output-substitution-goal.cc
@@ -6,8 +6,12 @@
namespace nix {
-DrvOutputSubstitutionGoal::DrvOutputSubstitutionGoal(const DrvOutput& id, Worker & worker, RepairFlag repair, std::optional<ContentAddress> ca)
- : Goal(worker)
+DrvOutputSubstitutionGoal::DrvOutputSubstitutionGoal(
+ const DrvOutput & id,
+ Worker & worker,
+ RepairFlag repair,
+ std::optional<ContentAddress> ca)
+ : Goal(worker, DerivedPath::Opaque { StorePath::dummy })
, id(id)
{
state = &DrvOutputSubstitutionGoal::init;
@@ -32,12 +36,12 @@ void DrvOutputSubstitutionGoal::init()
void DrvOutputSubstitutionGoal::tryNext()
{
- trace("Trying next substituter");
+ trace("trying next substituter");
if (subs.size() == 0) {
/* None left. Terminate this goal and let someone else deal
with it. */
- debug("drv output '%s' is required, but there is no substituter that can provide it", id.to_string());
+ debug("derivation output '%s' is required, but there is no substituter that can provide it", id.to_string());
/* Hack: don't indicate failure if there were no substituters.
In that case the calling derivation should just do a
@@ -119,7 +123,7 @@ void DrvOutputSubstitutionGoal::realisationFetched()
void DrvOutputSubstitutionGoal::outPathValid()
{
assert(outputInfo);
- trace("Output path substituted");
+ trace("output path substituted");
if (nrFailed > 0) {
debug("The output path of the derivation output '%s' could not be substituted", id.to_string());
@@ -137,7 +141,7 @@ void DrvOutputSubstitutionGoal::finished()
amDone(ecSuccess);
}
-string DrvOutputSubstitutionGoal::key()
+std::string DrvOutputSubstitutionGoal::key()
{
/* "a$" ensures substitution goals happen before derivation
goals. */
diff --git a/src/libstore/build/drv-output-substitution-goal.hh b/src/libstore/build/drv-output-substitution-goal.hh
index 67ae2624a..948dbda8f 100644
--- a/src/libstore/build/drv-output-substitution-goal.hh
+++ b/src/libstore/build/drv-output-substitution-goal.hh
@@ -51,7 +51,7 @@ public:
void timedOut(Error && ex) override { abort(); };
- string key() override;
+ std::string key() override;
void work() override;
void handleEOF(int fd) override;
diff --git a/src/libstore/build/entry-points.cc b/src/libstore/build/entry-points.cc
index 9b4cfd835..bea7363db 100644
--- a/src/libstore/build/entry-points.cc
+++ b/src/libstore/build/entry-points.cc
@@ -47,43 +47,51 @@ void Store::buildPaths(const std::vector<DerivedPath> & reqs, BuildMode buildMod
}
}
+std::vector<BuildResult> Store::buildPathsWithResults(
+ const std::vector<DerivedPath> & reqs,
+ BuildMode buildMode,
+ std::shared_ptr<Store> evalStore)
+{
+ Worker worker(*this, evalStore ? *evalStore : *this);
+
+ Goals goals;
+ for (const auto & br : reqs) {
+ std::visit(overloaded {
+ [&](const DerivedPath::Built & bfd) {
+ goals.insert(worker.makeDerivationGoal(bfd.drvPath, bfd.outputs, buildMode));
+ },
+ [&](const DerivedPath::Opaque & bo) {
+ goals.insert(worker.makePathSubstitutionGoal(bo.path, buildMode == bmRepair ? Repair : NoRepair));
+ },
+ }, br.raw());
+ }
+
+ worker.run(goals);
+
+ std::vector<BuildResult> results;
+
+ for (auto & i : goals)
+ results.push_back(i->buildResult);
+
+ return results;
+}
+
BuildResult Store::buildDerivation(const StorePath & drvPath, const BasicDerivation & drv,
BuildMode buildMode)
{
Worker worker(*this, *this);
auto goal = worker.makeBasicDerivationGoal(drvPath, drv, {}, buildMode);
- BuildResult result;
-
try {
worker.run(Goals{goal});
- result = goal->getResult();
+ return goal->buildResult;
} catch (Error & e) {
- result.status = BuildResult::MiscFailure;
- result.errorMsg = e.msg();
- }
- // XXX: Should use `goal->queryPartialDerivationOutputMap()` once it's
- // extended to return the full realisation for each output
- auto staticDrvOutputs = drv.outputsAndOptPaths(*this);
- auto outputHashes = staticOutputHashes(*this, drv);
- for (auto & [outputName, staticOutput] : staticDrvOutputs) {
- auto outputId = DrvOutput{outputHashes.at(outputName), outputName};
- if (staticOutput.second)
- result.builtOutputs.insert_or_assign(
- outputId,
- Realisation{ outputId, *staticOutput.second}
- );
- if (settings.isExperimentalFeatureEnabled(Xp::CaDerivations) && !derivationHasKnownOutputPaths(drv.type())) {
- auto realisation = this->queryRealisation(outputId);
- if (realisation)
- result.builtOutputs.insert_or_assign(
- outputId,
- *realisation
- );
- }
- }
-
- return result;
+ return BuildResult {
+ .status = BuildResult::MiscFailure,
+ .errorMsg = e.msg(),
+ .path = DerivedPath::Built { .drvPath = drvPath },
+ };
+ };
}
diff --git a/src/libstore/build/goal.cc b/src/libstore/build/goal.cc
index 7c985128b..58e805f55 100644
--- a/src/libstore/build/goal.cc
+++ b/src/libstore/build/goal.cc
@@ -5,8 +5,8 @@ namespace nix {
bool CompareGoalPtrs::operator() (const GoalPtr & a, const GoalPtr & b) const {
- string s1 = a->key();
- string s2 = b->key();
+ std::string s1 = a->key();
+ std::string s2 = b->key();
return s1 < s2;
}
@@ -28,7 +28,7 @@ void Goal::addWaitee(GoalPtr waitee)
void Goal::waiteeDone(GoalPtr waitee, ExitCode result)
{
- assert(waitees.find(waitee) != waitees.end());
+ assert(waitees.count(waitee));
waitees.erase(waitee);
trace(fmt("waitee '%s' done; %d left", waitee->name, waitees.size()));
diff --git a/src/libstore/build/goal.hh b/src/libstore/build/goal.hh
index 192e416d2..35121c5d9 100644
--- a/src/libstore/build/goal.hh
+++ b/src/libstore/build/goal.hh
@@ -2,6 +2,7 @@
#include "types.hh"
#include "store-api.hh"
+#include "build-result.hh"
namespace nix {
@@ -18,8 +19,8 @@ struct CompareGoalPtrs {
};
/* Set of goals. */
-typedef set<GoalPtr, CompareGoalPtrs> Goals;
-typedef set<WeakGoalPtr, std::owner_less<WeakGoalPtr>> WeakGoals;
+typedef std::set<GoalPtr, CompareGoalPtrs> Goals;
+typedef std::set<WeakGoalPtr, std::owner_less<WeakGoalPtr>> WeakGoals;
/* A map of paths to goals (and the other way around). */
typedef std::map<StorePath, WeakGoalPtr> WeakGoalMap;
@@ -39,30 +40,32 @@ struct Goal : public std::enable_shared_from_this<Goal>
WeakGoals waiters;
/* Number of goals we are/were waiting for that have failed. */
- unsigned int nrFailed;
+ size_t nrFailed = 0;
/* Number of substitution goals we are/were waiting for that
failed because there are no substituters. */
- unsigned int nrNoSubstituters;
+ size_t nrNoSubstituters = 0;
/* Number of substitution goals we are/were waiting for that
failed because they had unsubstitutable references. */
- unsigned int nrIncompleteClosure;
+ size_t nrIncompleteClosure = 0;
/* Name of this goal for debugging purposes. */
- string name;
+ std::string name;
/* Whether the goal is finished. */
- ExitCode exitCode;
+ ExitCode exitCode = ecBusy;
+
+ /* Build result. */
+ BuildResult buildResult;
/* Exception containing an error message, if any. */
std::optional<Error> ex;
- Goal(Worker & worker) : worker(worker)
- {
- nrFailed = nrNoSubstituters = nrIncompleteClosure = 0;
- exitCode = ecBusy;
- }
+ Goal(Worker & worker, DerivedPath path)
+ : worker(worker)
+ , buildResult { .path = std::move(path) }
+ { }
virtual ~Goal()
{
@@ -75,7 +78,7 @@ struct Goal : public std::enable_shared_from_this<Goal>
virtual void waiteeDone(GoalPtr waitee, ExitCode result);
- virtual void handleChildOutput(int fd, const string & data)
+ virtual void handleChildOutput(int fd, std::string_view data)
{
abort();
}
@@ -87,7 +90,7 @@ struct Goal : public std::enable_shared_from_this<Goal>
void trace(const FormatOrString & fs);
- string getName()
+ std::string getName()
{
return name;
}
@@ -97,7 +100,7 @@ struct Goal : public std::enable_shared_from_this<Goal>
by the worker (important!), etc. */
virtual void timedOut(Error && ex) = 0;
- virtual string key() = 0;
+ virtual std::string key() = 0;
void amDone(ExitCode result, std::optional<Error> ex = {});
diff --git a/src/libstore/build/local-derivation-goal.cc b/src/libstore/build/local-derivation-goal.cc
index 0d0afea2d..40ef706a6 100644
--- a/src/libstore/build/local-derivation-goal.cc
+++ b/src/libstore/build/local-derivation-goal.cc
@@ -1,4 +1,5 @@
#include "local-derivation-goal.hh"
+#include "gc-store.hh"
#include "hook-instance.hh"
#include "worker.hh"
#include "builtins.hh"
@@ -193,7 +194,7 @@ void LocalDerivationGoal::tryLocalBuild() {
outputLocks.unlock();
buildUser.reset();
worker.permanentFailure = true;
- done(BuildResult::InputRejected, e);
+ done(BuildResult::InputRejected, {}, e);
return;
}
@@ -394,7 +395,7 @@ void LocalDerivationGoal::startBuilder()
else if (settings.sandboxMode == smDisabled)
useChroot = false;
else if (settings.sandboxMode == smRelaxed)
- useChroot = !(derivationIsImpure(derivationType)) && !noChroot;
+ useChroot = derivationType.isSandboxed() && !noChroot;
}
auto & localStore = getLocalStore();
@@ -481,12 +482,12 @@ void LocalDerivationGoal::startBuilder()
temporary build directory. The text files have the format used
by `nix-store --register-validity'. However, the deriver
fields are left empty. */
- string s = get(drv->env, "exportReferencesGraph").value_or("");
+ auto s = get(drv->env, "exportReferencesGraph").value_or("");
Strings ss = tokenizeString<Strings>(s);
if (ss.size() % 2 != 0)
throw BuildError("odd number of tokens in 'exportReferencesGraph': '%1%'", s);
for (Strings::iterator i = ss.begin(); i != ss.end(); ) {
- string fileName = *i++;
+ auto fileName = *i++;
static std::regex regex("[A-Za-z_][A-Za-z0-9_.-]*");
if (!std::regex_match(fileName, regex))
throw Error("invalid file name '%s' in 'exportReferencesGraph'", fileName);
@@ -517,10 +518,10 @@ void LocalDerivationGoal::startBuilder()
i.pop_back();
}
size_t p = i.find('=');
- if (p == string::npos)
+ if (p == std::string::npos)
dirsInChroot[i] = {i, optional};
else
- dirsInChroot[string(i, 0, p)] = {string(i, p + 1), optional};
+ dirsInChroot[i.substr(0, p)] = {i.substr(p + 1), optional};
}
dirsInChroot[tmpDirInSandbox] = tmpDir;
@@ -607,7 +608,7 @@ void LocalDerivationGoal::startBuilder()
"nogroup:x:65534:\n", sandboxGid()));
/* Create /etc/hosts with localhost entry. */
- if (!(derivationIsImpure(derivationType)))
+ if (derivationType.isSandboxed())
writeFile(chrootRootDir + "/etc/hosts", "127.0.0.1 localhost\n::1 localhost\n");
/* Make the closure of the inputs available in the chroot,
@@ -671,9 +672,10 @@ void LocalDerivationGoal::startBuilder()
auto state = stBegin;
auto lines = runProgram(settings.preBuildHook, false, args);
auto lastPos = std::string::size_type{0};
- for (auto nlPos = lines.find('\n'); nlPos != string::npos;
- nlPos = lines.find('\n', lastPos)) {
- auto line = std::string{lines, lastPos, nlPos - lastPos};
+ for (auto nlPos = lines.find('\n'); nlPos != std::string::npos;
+ nlPos = lines.find('\n', lastPos))
+ {
+ auto line = lines.substr(lastPos, nlPos - lastPos);
lastPos = nlPos + 1;
if (state == stBegin) {
if (line == "extra-sandbox-paths" || line == "extra-chroot-dirs") {
@@ -686,10 +688,10 @@ void LocalDerivationGoal::startBuilder()
state = stBegin;
} else {
auto p = line.find('=');
- if (p == string::npos)
+ if (p == std::string::npos)
dirsInChroot[line] = line;
else
- dirsInChroot[string(line, 0, p)] = string(line, p + 1);
+ dirsInChroot[line.substr(0, p)] = line.substr(p + 1);
}
}
}
@@ -754,7 +756,7 @@ void LocalDerivationGoal::startBuilder()
if (tcsetattr(builderOut.writeSide.get(), TCSANOW, &term))
throw SysError("putting pseudoterminal into raw mode");
- result.startTime = time(0);
+ buildResult.startTime = time(0);
/* Fork a child to build the package. */
@@ -794,7 +796,7 @@ void LocalDerivationGoal::startBuilder()
us.
*/
- if (!(derivationIsImpure(derivationType)))
+ if (derivationType.isSandboxed())
privateNetwork = true;
userNamespaceSync.create();
@@ -912,9 +914,12 @@ void LocalDerivationGoal::startBuilder()
sandboxMountNamespace = open(fmt("/proc/%d/ns/mnt", (pid_t) pid).c_str(), O_RDONLY);
if (sandboxMountNamespace.get() == -1)
throw SysError("getting sandbox mount namespace");
- sandboxUserNamespace = open(fmt("/proc/%d/ns/user", (pid_t) pid).c_str(), O_RDONLY);
- if (sandboxUserNamespace.get() == -1)
- throw SysError("getting sandbox user namespace");
+
+ if (usingUserNamespace) {
+ sandboxUserNamespace = open(fmt("/proc/%d/ns/user", (pid_t) pid).c_str(), O_RDONLY);
+ if (sandboxUserNamespace.get() == -1)
+ throw SysError("getting sandbox user namespace");
+ }
/* Signal the builder that we've updated its user namespace. */
writeFull(userNamespaceSync.writeSide.get(), "1");
@@ -938,7 +943,7 @@ void LocalDerivationGoal::startBuilder()
/* Check if setting up the build environment failed. */
std::vector<std::string> msgs;
while (true) {
- string msg = [&]() {
+ std::string msg = [&]() {
try {
return readLine(builderOut.readSide.get());
} catch (Error & e) {
@@ -950,8 +955,8 @@ void LocalDerivationGoal::startBuilder()
throw;
}
}();
- if (string(msg, 0, 1) == "\2") break;
- if (string(msg, 0, 1) == "\1") {
+ if (msg.substr(0, 1) == "\2") break;
+ if (msg.substr(0, 1) == "\1") {
FdSource source(builderOut.readSide.get());
auto ex = readError(source);
ex.addTrace({}, "while setting up the build environment");
@@ -987,7 +992,7 @@ void LocalDerivationGoal::initTmpDir() {
env[i.first] = i.second;
} else {
auto hash = hashString(htSHA256, i.first);
- string fn = ".attr-" + hash.to_string(Base32, false);
+ std::string fn = ".attr-" + hash.to_string(Base32, false);
Path p = tmpDir + "/" + fn;
writeFile(p, rewriteStrings(i.second, inputRewrites));
chownToBuilder(p);
@@ -1044,7 +1049,7 @@ void LocalDerivationGoal::initEnv()
derivation, tell the builder, so that for instance `fetchurl'
can skip checking the output. On older Nixes, this environment
variable won't be set, so `fetchurl' will do the check. */
- if (derivationIsFixed(derivationType)) env["NIX_OUTPUT_CHECKED"] = "1";
+ if (derivationType.isFixed()) env["NIX_OUTPUT_CHECKED"] = "1";
/* *Only* if this is a fixed-output derivation, propagate the
values of the environment variables specified in the
@@ -1055,7 +1060,7 @@ void LocalDerivationGoal::initEnv()
to the builder is generally impure, but the output of
fixed-output derivations is by definition pure (since we
already know the cryptographic hash of the output). */
- if (derivationIsImpure(derivationType)) {
+ if (!derivationType.isSandboxed()) {
for (auto & i : parsedDrv->getStringsAttr("impureEnvVars").value_or(Strings()))
env[i] = getEnv(i).value_or("");
}
@@ -1078,7 +1083,7 @@ void LocalDerivationGoal::writeStructuredAttrs()
for (auto & [i, v] : json["outputs"].get<nlohmann::json::object_t>()) {
/* The placeholder must have a rewrite, so we use it to cover both the
cases where we know or don't know the output path ahead of time. */
- rewritten[i] = rewriteStrings(v, inputRewrites);
+ rewritten[i] = rewriteStrings((std::string) v, inputRewrites);
}
json["outputs"] = rewritten;
@@ -1123,7 +1128,7 @@ struct RestrictedStoreConfig : virtual LocalFSStoreConfig
/* A wrapper around LocalStore that only allows building/querying of
paths that are in the input closures of the build or were added via
recursive Nix calls. */
-struct RestrictedStore : public virtual RestrictedStoreConfig, public virtual LocalFSStore
+struct RestrictedStore : public virtual RestrictedStoreConfig, public virtual LocalFSStore, public virtual GcStore
{
ref<LocalStore> next;
@@ -1184,10 +1189,14 @@ struct RestrictedStore : public virtual RestrictedStoreConfig, public virtual Lo
std::optional<StorePath> queryPathFromHashPart(const std::string & hashPart) override
{ throw Error("queryPathFromHashPart"); }
- StorePath addToStore(const string & name, const Path & srcPath,
- FileIngestionMethod method = FileIngestionMethod::Recursive, HashType hashAlgo = htSHA256,
- PathFilter & filter = defaultPathFilter, RepairFlag repair = NoRepair,
- const StorePathSet & references = StorePathSet()) override
+ StorePath addToStore(
+ std::string_view name,
+ const Path & srcPath,
+ FileIngestionMethod method,
+ HashType hashAlgo,
+ PathFilter & filter,
+ RepairFlag repair,
+ const StorePathSet & references) override
{ throw Error("addToStore"); }
void addToStore(const ValidPathInfo & info, Source & narSource,
@@ -1197,17 +1206,24 @@ struct RestrictedStore : public virtual RestrictedStoreConfig, public virtual Lo
goal.addDependency(info.path);
}
- StorePath addTextToStore(const string & name, const string & s,
- const StorePathSet & references, RepairFlag repair = NoRepair) override
+ StorePath addTextToStore(
+ std::string_view name,
+ std::string_view s,
+ const StorePathSet & references,
+ RepairFlag repair = NoRepair) override
{
auto path = next->addTextToStore(name, s, references, repair);
goal.addDependency(path);
return path;
}
- StorePath addToStoreFromDump(Source & dump, const string & name,
- FileIngestionMethod method = FileIngestionMethod::Recursive, HashType hashAlgo = htSHA256, RepairFlag repair = NoRepair,
- const StorePathSet & references = StorePathSet()) override
+ StorePath addToStoreFromDump(
+ Source & dump,
+ std::string_view name,
+ FileIngestionMethod method,
+ HashType hashAlgo,
+ RepairFlag repair,
+ const StorePathSet & references) override
{
auto path = next->addToStoreFromDump(dump, name, method, hashAlgo, repair, references);
goal.addDependency(path);
@@ -1245,6 +1261,16 @@ struct RestrictedStore : public virtual RestrictedStoreConfig, public virtual Lo
void buildPaths(const std::vector<DerivedPath> & paths, BuildMode buildMode, std::shared_ptr<Store> evalStore) override
{
+ for (auto & result : buildPathsWithResults(paths, buildMode, evalStore))
+ if (!result.success())
+ result.rethrow();
+ }
+
+ std::vector<BuildResult> buildPathsWithResults(
+ const std::vector<DerivedPath> & paths,
+ BuildMode buildMode = bmNormal,
+ std::shared_ptr<Store> evalStore = nullptr) override
+ {
assert(!evalStore);
if (buildMode != bmNormal) throw Error("unsupported build mode");
@@ -1257,26 +1283,13 @@ struct RestrictedStore : public virtual RestrictedStoreConfig, public virtual Lo
throw InvalidPath("cannot build '%s' in recursive Nix because path is unknown", req.to_string(*next));
}
- next->buildPaths(paths, buildMode);
-
- for (auto & path : paths) {
- auto p = std::get_if<DerivedPath::Built>(&path);
- if (!p) continue;
- auto & bfd = *p;
- auto drv = readDerivation(bfd.drvPath);
- auto drvHashes = staticOutputHashes(*this, drv);
- auto outputs = next->queryDerivationOutputMap(bfd.drvPath);
- for (auto & [outputName, outputPath] : outputs)
- if (wantOutput(outputName, bfd.outputs)) {
- newPaths.insert(outputPath);
- if (settings.isExperimentalFeatureEnabled(Xp::CaDerivations)) {
- auto thisRealisation = next->queryRealisation(
- DrvOutput{drvHashes.at(outputName), outputName}
- );
- assert(thisRealisation);
- newRealisations.insert(*thisRealisation);
- }
- }
+ auto results = next->buildPathsWithResults(paths, buildMode);
+
+ for (auto & result : results) {
+ for (auto & [outputName, output] : result.builtOutputs) {
+ newPaths.insert(output.outPath);
+ newRealisations.insert(output);
+ }
}
StorePathSet closure;
@@ -1285,6 +1298,8 @@ struct RestrictedStore : public virtual RestrictedStoreConfig, public virtual Lo
goal.addDependency(path);
for (auto & real : Realisation::closure(*next, newRealisations))
goal.addedDrvOutputs.insert(real.id);
+
+ return results;
}
BuildResult buildDerivation(const StorePath & drvPath, const BasicDerivation & drv,
@@ -1325,6 +1340,12 @@ struct RestrictedStore : public virtual RestrictedStoreConfig, public virtual Lo
next->queryMissing(allowed, willBuild, willSubstitute,
unknown, downloadSize, narSize);
}
+
+ virtual std::optional<std::string> getBuildLog(const StorePath & path) override
+ { return std::nullopt; }
+
+ virtual void addBuildLog(const StorePath & path, std::string_view log) override
+ { unsupported("addBuildLog"); }
};
@@ -1653,7 +1674,7 @@ void LocalDerivationGoal::runChild()
/* Fixed-output derivations typically need to access the
network, so give them access to /etc/resolv.conf and so
on. */
- if (derivationIsImpure(derivationType)) {
+ if (!derivationType.isSandboxed()) {
// Only use nss functions to resolve hosts and
// services. Don’t use it for anything else that may
// be configured for this system. This limits the
@@ -1897,7 +1918,7 @@ void LocalDerivationGoal::runChild()
sandboxProfile += "(import \"sandbox-defaults.sb\")\n";
- if (derivationIsImpure(derivationType))
+ if (!derivationType.isSandboxed())
sandboxProfile += "(import \"sandbox-network.sb\")\n";
/* Add the output paths we'll use at build-time to the chroot */
@@ -1918,7 +1939,7 @@ void LocalDerivationGoal::runChild()
"can't map '%1%' to '%2%': mismatched impure paths not supported on Darwin",
i.first, i.second.source);
- string path = i.first;
+ std::string path = i.first;
struct stat st;
if (lstat(path.c_str(), &st)) {
if (i.second.optional && errno == ENOENT)
@@ -1970,7 +1991,7 @@ void LocalDerivationGoal::runChild()
args.push_back("IMPORT_DIR=" + settings.nixDataDir + "/nix/sandbox/");
if (allowLocalNetworking) {
args.push_back("-D");
- args.push_back(string("_ALLOW_LOCAL_NETWORKING=1"));
+ args.push_back(std::string("_ALLOW_LOCAL_NETWORKING=1"));
}
args.push_back(drv->builder);
} else {
@@ -1989,7 +2010,7 @@ void LocalDerivationGoal::runChild()
args.push_back(rewriteStrings(i, inputRewrites));
/* Indicate that we managed to set up the build environment. */
- writeFull(STDERR_FILENO, string("\2\n"));
+ writeFull(STDERR_FILENO, std::string("\2\n"));
/* Execute the program. This should not return. */
if (drv->isBuiltin()) {
@@ -2007,7 +2028,7 @@ void LocalDerivationGoal::runChild()
else if (drv->builder == "builtin:unpack-channel")
builtinUnpackChannel(drv2);
else
- throw Error("unsupported builtin builder '%1%'", string(drv->builder, 8));
+ throw Error("unsupported builtin builder '%1%'", drv->builder.substr(8));
_exit(0);
} catch (std::exception & e) {
writeFull(STDERR_FILENO, e.what() + std::string("\n"));
@@ -2053,7 +2074,7 @@ void LocalDerivationGoal::runChild()
}
-void LocalDerivationGoal::registerOutputs()
+DrvOutputs LocalDerivationGoal::registerOutputs()
{
/* When using a build hook, the build hook can register the output
as valid (by doing `nix-store --import'). If so we don't have
@@ -2062,10 +2083,8 @@ void LocalDerivationGoal::registerOutputs()
We can only early return when the outputs are known a priori. For
floating content-addressed derivations this isn't the case.
*/
- if (hook) {
- DerivationGoal::registerOutputs();
- return;
- }
+ if (hook)
+ return DerivationGoal::registerOutputs();
std::map<std::string, ValidPathInfo> infos;
@@ -2188,6 +2207,8 @@ void LocalDerivationGoal::registerOutputs()
std::reverse(sortedOutputNames.begin(), sortedOutputNames.end());
+ OutputPathMap finalOutputs;
+
for (auto & outputName : sortedOutputNames) {
auto output = drv->outputs.at(outputName);
auto & scratchPath = scratchOutputs.at(outputName);
@@ -2258,7 +2279,7 @@ void LocalDerivationGoal::registerOutputs()
return res;
};
- auto newInfoFromCA = [&](const DerivationOutputCAFloating outputHash) -> ValidPathInfo {
+ auto newInfoFromCA = [&](const DerivationOutput::CAFloating outputHash) -> ValidPathInfo {
auto & st = outputStats.at(outputName);
if (outputHash.method == FileIngestionMethod::Flat) {
/* The output path should be a regular file without execute permission. */
@@ -2324,7 +2345,8 @@ void LocalDerivationGoal::registerOutputs()
};
ValidPathInfo newInfo = std::visit(overloaded {
- [&](const DerivationOutputInputAddressed & output) {
+
+ [&](const DerivationOutput::InputAddressed & output) {
/* input-addressed case */
auto requiredFinalPath = output.path;
/* Preemptively add rewrite rule for final hash, as that is
@@ -2343,8 +2365,9 @@ void LocalDerivationGoal::registerOutputs()
newInfo0.references.insert(newInfo0.path);
return newInfo0;
},
- [&](const DerivationOutputCAFixed & dof) {
- auto newInfo0 = newInfoFromCA(DerivationOutputCAFloating {
+
+ [&](const DerivationOutput::CAFixed & dof) {
+ auto newInfo0 = newInfoFromCA(DerivationOutput::CAFloating {
.method = dof.hash.method,
.hashType = dof.hash.hash.type,
});
@@ -2365,19 +2388,25 @@ void LocalDerivationGoal::registerOutputs()
}
return newInfo0;
},
- [&](DerivationOutputCAFloating dof) {
+
+ [&](const DerivationOutput::CAFloating & dof) {
return newInfoFromCA(dof);
},
- [&](DerivationOutputDeferred) {
+
+ [&](const DerivationOutput::Deferred &) -> ValidPathInfo {
// No derivation should reach that point without having been
// rewritten first
assert(false);
- // Ugly, but the compiler insists on having this return a value
- // of type `ValidPathInfo` despite the `assert(false)`, so
- // let's provide it
- return *(ValidPathInfo*)0;
},
- }, output.output);
+
+ [&](const DerivationOutput::Impure & doi) {
+ return newInfoFromCA(DerivationOutput::CAFloating {
+ .method = doi.method,
+ .hashType = doi.hashType,
+ });
+ },
+
+ }, output.raw());
/* FIXME: set proper permissions in restorePath() so
we don't have to do another traversal. */
@@ -2487,11 +2516,12 @@ void LocalDerivationGoal::registerOutputs()
}
if (buildMode == bmCheck) {
- // In case of FOD mismatches on `--check` an error must be thrown as this is also
- // a source for non-determinism.
+ /* In case of fixed-output derivations, if there are
+ mismatches on `--check` an error must be thrown as this is
+ also a source for non-determinism. */
if (delayedException)
std::rethrow_exception(delayedException);
- return;
+ return assertPathValidity();
}
/* Apply output checks. */
@@ -2503,7 +2533,7 @@ void LocalDerivationGoal::registerOutputs()
assert(prevInfos.size() == infos.size());
for (auto i = prevInfos.begin(), j = infos.begin(); i != prevInfos.end(); ++i, ++j)
if (!(*i == *j)) {
- result.isNonDeterministic = true;
+ buildResult.isNonDeterministic = true;
Path prev = worker.store.printStorePath(i->second.path) + checkSuffix;
bool prevExists = keepPreviousRound && pathExists(prev);
hintformat hint = prevExists
@@ -2541,7 +2571,7 @@ void LocalDerivationGoal::registerOutputs()
if (curRound < nrRounds) {
prevInfos = std::move(infos);
- return;
+ return {};
}
/* Remove the .check directories if we're done. FIXME: keep them
@@ -2576,17 +2606,27 @@ void LocalDerivationGoal::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. */
+ DrvOutputs builtOutputs;
- if (settings.isExperimentalFeatureEnabled(Xp::CaDerivations)) {
- for (auto& [outputName, newInfo] : infos) {
- auto thisRealisation = Realisation{
- .id = DrvOutput{initialOutputs.at(outputName).outputHash,
- outputName},
- .outPath = newInfo.path};
+ for (auto & [outputName, newInfo] : infos) {
+ auto thisRealisation = Realisation {
+ .id = DrvOutput {
+ initialOutputs.at(outputName).outputHash,
+ outputName
+ },
+ .outPath = newInfo.path
+ };
+ if (settings.isExperimentalFeatureEnabled(Xp::CaDerivations)
+ && drv->type().isPure())
+ {
signRealisation(thisRealisation);
worker.store.registerDrvOutput(thisRealisation);
}
+ if (wantOutput(outputName, wantedOutputs))
+ builtOutputs.emplace(thisRealisation.id, thisRealisation);
}
+
+ return builtOutputs;
}
void LocalDerivationGoal::signRealisation(Realisation & realisation)
@@ -2595,7 +2635,7 @@ void LocalDerivationGoal::signRealisation(Realisation & realisation)
}
-void LocalDerivationGoal::checkOutputs(const std::map<Path, ValidPathInfo> & outputs)
+void LocalDerivationGoal::checkOutputs(const std::map<std::string, ValidPathInfo> & outputs)
{
std::map<Path, const ValidPathInfo &> outputsByPath;
for (auto & output : outputs)
@@ -2667,8 +2707,8 @@ void LocalDerivationGoal::checkOutputs(const std::map<Path, ValidPathInfo> & out
for (auto & i : *value) {
if (worker.store.isStorePath(i))
spec.insert(worker.store.parseStorePath(i));
- else if (finalOutputs.count(i))
- spec.insert(finalOutputs.at(i));
+ else if (outputs.count(i))
+ spec.insert(outputs.at(i).path);
else throw BuildError("derivation contains an illegal reference specifier '%s'", i);
}
@@ -2691,7 +2731,7 @@ void LocalDerivationGoal::checkOutputs(const std::map<Path, ValidPathInfo> & out
}
if (!badPaths.empty()) {
- string badPathsStr;
+ std::string badPathsStr;
for (auto & i : badPaths) {
badPathsStr += "\n ";
badPathsStr += worker.store.printStorePath(i);
diff --git a/src/libstore/build/local-derivation-goal.hh b/src/libstore/build/local-derivation-goal.hh
index bfdf91d89..d456e9cae 100644
--- a/src/libstore/build/local-derivation-goal.hh
+++ b/src/libstore/build/local-derivation-goal.hh
@@ -58,11 +58,11 @@ struct LocalDerivationGoal : public DerivationGoal
typedef map<Path, ChrootPath> DirsInChroot; // maps target path to source path
DirsInChroot dirsInChroot;
- typedef map<string, string> Environment;
+ typedef map<std::string, std::string> Environment;
Environment env;
#if __APPLE__
- typedef string SandboxProfile;
+ typedef std::string SandboxProfile;
SandboxProfile additionalSandboxProfile;
#endif
@@ -169,7 +169,7 @@ struct LocalDerivationGoal : public DerivationGoal
/* Check that the derivation outputs all exist and register them
as valid. */
- void registerOutputs() override;
+ DrvOutputs registerOutputs() override;
void signRealisation(Realisation &) override;
diff --git a/src/libstore/build/substitution-goal.cc b/src/libstore/build/substitution-goal.cc
index 5ecf1da7e..ca5218627 100644
--- a/src/libstore/build/substitution-goal.cc
+++ b/src/libstore/build/substitution-goal.cc
@@ -6,7 +6,7 @@
namespace nix {
PathSubstitutionGoal::PathSubstitutionGoal(const StorePath & storePath, Worker & worker, RepairFlag repair, std::optional<ContentAddress> ca)
- : Goal(worker)
+ : Goal(worker, DerivedPath::Opaque { storePath })
, storePath(storePath)
, repair(repair)
, ca(ca)
@@ -24,6 +24,20 @@ PathSubstitutionGoal::~PathSubstitutionGoal()
}
+void PathSubstitutionGoal::done(
+ ExitCode result,
+ BuildResult::Status status,
+ std::optional<std::string> errorMsg)
+{
+ buildResult.status = status;
+ if (errorMsg) {
+ debug(*errorMsg);
+ buildResult.errorMsg = *errorMsg;
+ }
+ amDone(result);
+}
+
+
void PathSubstitutionGoal::work()
{
(this->*state)();
@@ -38,7 +52,7 @@ void PathSubstitutionGoal::init()
/* If the path already exists we're done. */
if (!repair && worker.store.isValidPath(storePath)) {
- amDone(ecSuccess);
+ done(ecSuccess, BuildResult::AlreadyValid);
return;
}
@@ -60,12 +74,14 @@ void PathSubstitutionGoal::tryNext()
if (subs.size() == 0) {
/* None left. Terminate this goal and let someone else deal
with it. */
- debug("path '%s' is required, but there is no substituter that can build it", worker.store.printStorePath(storePath));
/* 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);
+ done(
+ substituterFailed ? ecFailed : ecNoSubstituters,
+ BuildResult::NoSubstituters,
+ fmt("path '%s' is required, but there is no substituter that can build it", worker.store.printStorePath(storePath)));
if (substituterFailed) {
worker.failedSubstitutions++;
@@ -162,8 +178,10 @@ void PathSubstitutionGoal::referencesValid()
trace("all references realised");
if (nrFailed > 0) {
- debug("some references of path '%s' could not be realised", worker.store.printStorePath(storePath));
- amDone(nrNoSubstituters > 0 || nrIncompleteClosure > 0 ? ecIncompleteClosure : ecFailed);
+ done(
+ nrNoSubstituters > 0 || nrIncompleteClosure > 0 ? ecIncompleteClosure : ecFailed,
+ BuildResult::DependencyFailed,
+ fmt("some references of path '%s' could not be realised", worker.store.printStorePath(storePath)));
return;
}
@@ -268,11 +286,11 @@ void PathSubstitutionGoal::finished()
worker.updateProgress();
- amDone(ecSuccess);
+ done(ecSuccess, BuildResult::Substituted);
}
-void PathSubstitutionGoal::handleChildOutput(int fd, const string & data)
+void PathSubstitutionGoal::handleChildOutput(int fd, std::string_view data)
{
}
diff --git a/src/libstore/build/substitution-goal.hh b/src/libstore/build/substitution-goal.hh
index 70c806d23..a73f8e666 100644
--- a/src/libstore/build/substitution-goal.hh
+++ b/src/libstore/build/substitution-goal.hh
@@ -53,13 +53,18 @@ struct PathSubstitutionGoal : public Goal
/* Content address for recomputing store path */
std::optional<ContentAddress> ca;
+ void done(
+ ExitCode result,
+ BuildResult::Status status,
+ std::optional<std::string> errorMsg = {});
+
public:
PathSubstitutionGoal(const StorePath & storePath, Worker & worker, RepairFlag repair = NoRepair, std::optional<ContentAddress> ca = std::nullopt);
~PathSubstitutionGoal();
void timedOut(Error && ex) override { abort(); };
- string key() override
+ std::string key() override
{
/* "a$" ensures substitution goals happen before derivation
goals. */
@@ -77,7 +82,7 @@ public:
void finished();
/* Callback used by the worker to write to the log. */
- void handleChildOutput(int fd, const string & data) override;
+ void handleChildOutput(int fd, std::string_view data) override;
void handleEOF(int fd) override;
void cleanup() override;
diff --git a/src/libstore/build/worker.cc b/src/libstore/build/worker.cc
index f11c5ce68..f72c1cc9c 100644
--- a/src/libstore/build/worker.cc
+++ b/src/libstore/build/worker.cc
@@ -161,7 +161,7 @@ unsigned Worker::getNrLocalBuilds()
}
-void Worker::childStarted(GoalPtr goal, const set<int> & fds,
+void Worker::childStarted(GoalPtr goal, const std::set<int> & fds,
bool inBuildSlot, bool respectTimeouts)
{
Child child;
@@ -377,7 +377,7 @@ void Worker::waitForInput()
GoalPtr goal = j->goal.lock();
assert(goal);
- set<int> fds2(j->fds);
+ std::set<int> fds2(j->fds);
std::vector<unsigned char> buffer(4096);
for (auto & k : fds2) {
if (pollStatus.at(fdToPollStatus.at(k)).revents) {
@@ -394,7 +394,7 @@ void Worker::waitForInput()
} else {
printMsg(lvlVomit, "%1%: read %2% bytes",
goal->getName(), rd);
- string data((char *) buffer.data(), rd);
+ std::string data((char *) buffer.data(), rd);
j->lastOutput = after;
goal->handleChildOutput(k, data);
}
diff --git a/src/libstore/build/worker.hh b/src/libstore/build/worker.hh
index 6a3b99c02..a1e036a96 100644
--- a/src/libstore/build/worker.hh
+++ b/src/libstore/build/worker.hh
@@ -38,7 +38,7 @@ struct Child
{
WeakGoalPtr goal;
Goal * goal2; // ugly hackery
- set<int> fds;
+ std::set<int> fds;
bool respectTimeouts;
bool inBuildSlot;
steady_time_point lastOutput; /* time we last got output on stdout/stderr */
@@ -167,7 +167,7 @@ public:
/* Registers a running child process. `inBuildSlot' means that
the process counts towards the jobs limit. */
- void childStarted(GoalPtr goal, const set<int> & fds,
+ void childStarted(GoalPtr goal, const std::set<int> & fds,
bool inBuildSlot, bool respectTimeouts);
/* Unregisters a running child process. `wakeSleepers' should be
diff --git a/src/libstore/builtins/buildenv.cc b/src/libstore/builtins/buildenv.cc
index e88fc687a..6f6ad57cb 100644
--- a/src/libstore/builtins/buildenv.cc
+++ b/src/libstore/builtins/buildenv.cc
@@ -47,9 +47,9 @@ static void createLinks(State & state, const Path & srcDir, const Path & dstDir,
throw;
}
- /* The files below are special-cased to that they don't show up
- * in user profiles, either because they are useless, or
- * because they would cauase pointless collisions (e.g., each
+ /* The files below are special-cased to that they don't show
+ * up in user profiles, either because they are useless, or
+ * because they would cause pointless collisions (e.g., each
* Python package brings its own
* `$out/lib/pythonX.Y/site-packages/easy-install.pth'.)
*/
@@ -57,7 +57,9 @@ static void createLinks(State & state, const Path & srcDir, const Path & dstDir,
hasSuffix(srcFile, "/nix-support") ||
hasSuffix(srcFile, "/perllocal.pod") ||
hasSuffix(srcFile, "/info/dir") ||
- hasSuffix(srcFile, "/log"))
+ hasSuffix(srcFile, "/log") ||
+ hasSuffix(srcFile, "/manifest.nix") ||
+ hasSuffix(srcFile, "/manifest.json"))
continue;
else if (S_ISDIR(srcSt.st_mode)) {
@@ -123,7 +125,7 @@ void buildProfile(const Path & out, Packages && pkgs)
createLinks(state, pkgDir, out, priority);
try {
- for (const auto & p : tokenizeString<std::vector<string>>(
+ for (const auto & p : tokenizeString<std::vector<std::string>>(
readFile(pkgDir + "/nix-support/propagated-user-env-packages"), " \n"))
if (!done.count(p))
postponed.insert(p);
@@ -161,7 +163,7 @@ void buildProfile(const Path & out, Packages && pkgs)
void builtinBuildenv(const BasicDerivation & drv)
{
- auto getAttr = [&](const string & name) {
+ auto getAttr = [&](const std::string & name) {
auto i = drv.env.find(name);
if (i == drv.env.end()) throw Error("attribute '%s' missing", name);
return i->second;
diff --git a/src/libstore/builtins/fetchurl.cc b/src/libstore/builtins/fetchurl.cc
index 4fb5d8a06..af3dfc409 100644
--- a/src/libstore/builtins/fetchurl.cc
+++ b/src/libstore/builtins/fetchurl.cc
@@ -16,7 +16,7 @@ void builtinFetchurl(const BasicDerivation & drv, const std::string & netrcData)
writeFile(settings.netrcFile, netrcData, 0600);
}
- auto getAttr = [&](const string & name) {
+ auto getAttr = [&](const std::string & name) {
auto i = drv.env.find(name);
if (i == drv.env.end()) throw Error("attribute '%s' missing", name);
return i->second;
diff --git a/src/libstore/builtins/unpack-channel.cc b/src/libstore/builtins/unpack-channel.cc
index d18e3ddaf..426d58a53 100644
--- a/src/libstore/builtins/unpack-channel.cc
+++ b/src/libstore/builtins/unpack-channel.cc
@@ -5,7 +5,7 @@ namespace nix {
void builtinUnpackChannel(const BasicDerivation & drv)
{
- auto getAttr = [&](const string & name) {
+ auto getAttr = [&](const std::string & name) {
auto i = drv.env.find(name);
if (i == drv.env.end()) throw Error("attribute '%s' missing", name);
return i->second;
diff --git a/src/libstore/daemon.cc b/src/libstore/daemon.cc
index 1ddd1a4d5..de69b50ee 100644
--- a/src/libstore/daemon.cc
+++ b/src/libstore/daemon.cc
@@ -1,7 +1,11 @@
#include "daemon.hh"
#include "monitor-fd.hh"
#include "worker-protocol.hh"
+#include "build-result.hh"
#include "store-api.hh"
+#include "store-cast.hh"
+#include "gc-store.hh"
+#include "log-store.hh"
#include "path-with-outputs.hh"
#include "finally.hh"
#include "archive.hh"
@@ -479,8 +483,8 @@ static void performOp(TunnelLogger * logger, ref<Store> store,
}
case wopAddTextToStore: {
- string suffix = readString(from);
- string s = readString(from);
+ std::string suffix = readString(from);
+ std::string s = readString(from);
auto refs = worker_proto::read(*store, from, Phantom<StorePathSet> {});
logger->startWork();
auto path = store->addTextToStore(suffix, s, refs, NoRepair);
@@ -530,6 +534,25 @@ static void performOp(TunnelLogger * logger, ref<Store> store,
break;
}
+ case wopBuildPathsWithResults: {
+ auto drvs = readDerivedPaths(*store, clientVersion, from);
+ BuildMode mode = bmNormal;
+ mode = (BuildMode) readInt(from);
+
+ /* Repairing is not atomic, so disallowed for "untrusted"
+ clients. */
+ if (mode == bmRepair && !trusted)
+ throw Error("repairing is not allowed because you are not in 'trusted-users'");
+
+ logger->startWork();
+ auto results = store->buildPathsWithResults(drvs, mode);
+ logger->stopWork();
+
+ worker_proto::write(*store, to, results);
+
+ break;
+ }
+
case wopBuildDerivation: {
auto drvPath = store->parseStorePath(readString(from));
BasicDerivation drv;
@@ -537,6 +560,8 @@ static void performOp(TunnelLogger * logger, ref<Store> store,
BuildMode buildMode = (BuildMode) readInt(from);
logger->startWork();
+ auto drvType = drv.type();
+
/* Content-addressed derivations are trustless because their output paths
are verified by their content alone, so any derivation is free to
try to produce such a path.
@@ -569,12 +594,12 @@ static void performOp(TunnelLogger * logger, ref<Store> store,
derivations, we throw out the precomputed output paths and just
store the hashes, so there aren't two competing sources of truth an
attacker could exploit. */
- if (drv.type() == DerivationType::InputAddressed && !trusted)
+ if (!(drvType.isCA() || trusted))
throw Error("you are not privileged to build input-addressed derivations");
/* Make sure that the non-input-addressed derivations that got this far
are in fact content-addressed if we don't trust them. */
- assert(derivationIsCA(drv.type()) || trusted);
+ assert(drvType.isCA() || trusted);
/* Recompute the derivation path when we cannot trust the original. */
if (!trusted) {
@@ -583,7 +608,7 @@ static void performOp(TunnelLogger * logger, ref<Store> store,
original not-necessarily-resolved derivation to verify the drv
derivation as adequate claim to the input-addressed output
paths. */
- assert(derivationIsCA(drv.type()));
+ assert(drvType.isCA());
Derivation drv2;
static_cast<BasicDerivation &>(drv2) = drv;
@@ -622,9 +647,12 @@ static void performOp(TunnelLogger * logger, ref<Store> store,
case wopAddIndirectRoot: {
Path path = absPath(readString(from));
+
logger->startWork();
- store->addIndirectRoot(path);
+ auto & gcStore = require<GcStore>(*store);
+ gcStore.addIndirectRoot(path);
logger->stopWork();
+
to << 1;
break;
}
@@ -639,7 +667,8 @@ static void performOp(TunnelLogger * logger, ref<Store> store,
case wopFindRoots: {
logger->startWork();
- Roots roots = store->findRoots(!trusted);
+ auto & gcStore = require<GcStore>(*store);
+ Roots roots = gcStore.findRoots(!trusted);
logger->stopWork();
size_t size = 0;
@@ -670,7 +699,8 @@ static void performOp(TunnelLogger * logger, ref<Store> store,
logger->startWork();
if (options.ignoreLiveness)
throw Error("you are not allowed to ignore liveness");
- store->collectGarbage(options, results);
+ auto & gcStore = require<GcStore>(*store);
+ gcStore.collectGarbage(options, results);
logger->stopWork();
to << results.paths << results.bytesFreed << 0 /* obsolete */;
@@ -698,8 +728,8 @@ static void performOp(TunnelLogger * logger, ref<Store> store,
if (GET_PROTOCOL_MINOR(clientVersion) >= 12) {
unsigned int n = readInt(from);
for (unsigned int i = 0; i < n; i++) {
- string name = readString(from);
- string value = readString(from);
+ auto name = readString(from);
+ auto value = readString(from);
clientSettings.overrides.emplace(name, value);
}
}
@@ -927,11 +957,12 @@ static void performOp(TunnelLogger * logger, ref<Store> store,
logger->startWork();
if (!trusted)
throw Error("you are not privileged to add logs");
+ auto & logStore = require<LogStore>(*store);
{
FramedSource source(from);
StringSink sink;
source.drainInto(sink);
- store->addBuildLog(path, sink.s);
+ logStore.addBuildLog(path, sink.s);
}
logger->stopWork();
to << 1;
diff --git a/src/libstore/derivations.cc b/src/libstore/derivations.cc
index 40af6a775..1c695de82 100644
--- a/src/libstore/derivations.cc
+++ b/src/libstore/derivations.cc
@@ -11,78 +11,120 @@ namespace nix {
std::optional<StorePath> DerivationOutput::path(const Store & store, std::string_view drvName, std::string_view outputName) const
{
return std::visit(overloaded {
- [](const DerivationOutputInputAddressed & doi) -> std::optional<StorePath> {
+ [](const DerivationOutput::InputAddressed & doi) -> std::optional<StorePath> {
return { doi.path };
},
- [&](const DerivationOutputCAFixed & dof) -> std::optional<StorePath> {
+ [&](const DerivationOutput::CAFixed & dof) -> std::optional<StorePath> {
return {
dof.path(store, drvName, outputName)
};
},
- [](const DerivationOutputCAFloating & dof) -> std::optional<StorePath> {
+ [](const DerivationOutput::CAFloating & dof) -> std::optional<StorePath> {
return std::nullopt;
},
- [](const DerivationOutputDeferred &) -> std::optional<StorePath> {
+ [](const DerivationOutput::Deferred &) -> std::optional<StorePath> {
return std::nullopt;
},
- }, output);
+ [](const DerivationOutput::Impure &) -> std::optional<StorePath> {
+ return std::nullopt;
+ },
+ }, raw());
}
-StorePath DerivationOutputCAFixed::path(const Store & store, std::string_view drvName, std::string_view outputName) const {
+StorePath DerivationOutput::CAFixed::path(const Store & store, std::string_view drvName, std::string_view outputName) const
+{
return store.makeFixedOutputPath(
hash.method, hash.hash,
outputPathName(drvName, outputName));
}
-bool derivationIsCA(DerivationType dt) {
- switch (dt) {
- case DerivationType::InputAddressed: return false;
- case DerivationType::CAFixed: return true;
- case DerivationType::CAFloating: return true;
- case DerivationType::DeferredInputAddressed: return false;
- };
- // Since enums can have non-variant values, but making a `default:` would
- // disable exhaustiveness warnings.
- assert(false);
+bool DerivationType::isCA() const
+{
+ /* Normally we do the full `std::visit` to make sure we have
+ exhaustively handled all variants, but so long as there is a
+ variant called `ContentAddressed`, it must be the only one for
+ which `isCA` is true for this to make sense!. */
+ return std::visit(overloaded {
+ [](const InputAddressed & ia) {
+ return false;
+ },
+ [](const ContentAddressed & ca) {
+ return true;
+ },
+ [](const Impure &) {
+ return true;
+ },
+ }, raw());
}
-bool derivationIsFixed(DerivationType dt) {
- switch (dt) {
- case DerivationType::InputAddressed: return false;
- case DerivationType::CAFixed: return true;
- case DerivationType::CAFloating: return false;
- case DerivationType::DeferredInputAddressed: return false;
- };
- assert(false);
+bool DerivationType::isFixed() const
+{
+ return std::visit(overloaded {
+ [](const InputAddressed & ia) {
+ return false;
+ },
+ [](const ContentAddressed & ca) {
+ return ca.fixed;
+ },
+ [](const Impure &) {
+ return false;
+ },
+ }, raw());
}
-bool derivationHasKnownOutputPaths(DerivationType dt) {
- switch (dt) {
- case DerivationType::InputAddressed: return true;
- case DerivationType::CAFixed: return true;
- case DerivationType::CAFloating: return false;
- case DerivationType::DeferredInputAddressed: return false;
- };
- assert(false);
+bool DerivationType::hasKnownOutputPaths() const
+{
+ return std::visit(overloaded {
+ [](const InputAddressed & ia) {
+ return !ia.deferred;
+ },
+ [](const ContentAddressed & ca) {
+ return ca.fixed;
+ },
+ [](const Impure &) {
+ return false;
+ },
+ }, raw());
}
-bool derivationIsImpure(DerivationType dt) {
- switch (dt) {
- case DerivationType::InputAddressed: return false;
- case DerivationType::CAFixed: return true;
- case DerivationType::CAFloating: return false;
- case DerivationType::DeferredInputAddressed: return false;
- };
- assert(false);
+bool DerivationType::isSandboxed() const
+{
+ return std::visit(overloaded {
+ [](const InputAddressed & ia) {
+ return true;
+ },
+ [](const ContentAddressed & ca) {
+ return ca.sandboxed;
+ },
+ [](const Impure &) {
+ return false;
+ },
+ }, raw());
+}
+
+
+bool DerivationType::isPure() const
+{
+ return std::visit(overloaded {
+ [](const InputAddressed & ia) {
+ return true;
+ },
+ [](const ContentAddressed & ca) {
+ return true;
+ },
+ [](const Impure &) {
+ return false;
+ },
+ }, raw());
}
bool BasicDerivation::isBuiltin() const
{
- return string(builder, 0, 8) == "builtin:";
+ return builder.substr(0, 8) == "builtin:";
}
@@ -104,19 +146,19 @@ StorePath writeDerivation(Store & store,
/* Read string `s' from stream `str'. */
-static void expect(std::istream & str, const string & s)
+static void expect(std::istream & str, std::string_view s)
{
char s2[s.size()];
str.read(s2, s.size());
- if (string(s2, s.size()) != s)
+ if (std::string(s2, s.size()) != s)
throw FormatError("expected string '%1%'", s);
}
/* Read a C-style string from stream `str'. */
-static string parseString(std::istream & str)
+static std::string parseString(std::istream & str)
{
- string res;
+ std::string res;
expect(str, "\"");
int c;
while ((c = str.get()) != '"')
@@ -172,42 +214,41 @@ static DerivationOutput parseDerivationOutput(const Store & store,
{
if (hashAlgo != "") {
auto method = FileIngestionMethod::Flat;
- if (string(hashAlgo, 0, 2) == "r:") {
+ if (hashAlgo.substr(0, 2) == "r:") {
method = FileIngestionMethod::Recursive;
hashAlgo = hashAlgo.substr(2);
}
const auto hashType = parseHashType(hashAlgo);
- if (hash != "") {
+ if (hash == "impure") {
+ settings.requireExperimentalFeature(Xp::ImpureDerivations);
+ assert(pathS == "");
+ return DerivationOutput::Impure {
+ .method = std::move(method),
+ .hashType = std::move(hashType),
+ };
+ } else if (hash != "") {
validatePath(pathS);
- return DerivationOutput {
- .output = DerivationOutputCAFixed {
- .hash = FixedOutputHash {
- .method = std::move(method),
- .hash = Hash::parseNonSRIUnprefixed(hash, hashType),
- },
+ return DerivationOutput::CAFixed {
+ .hash = FixedOutputHash {
+ .method = std::move(method),
+ .hash = Hash::parseNonSRIUnprefixed(hash, hashType),
},
};
} else {
settings.requireExperimentalFeature(Xp::CaDerivations);
assert(pathS == "");
- return DerivationOutput {
- .output = DerivationOutputCAFloating {
- .method = std::move(method),
- .hashType = std::move(hashType),
- },
+ return DerivationOutput::CAFloating {
+ .method = std::move(method),
+ .hashType = std::move(hashType),
};
}
} else {
if (pathS == "") {
- return DerivationOutput {
- .output = DerivationOutputDeferred { }
- };
+ return DerivationOutput::Deferred { };
}
validatePath(pathS);
- return DerivationOutput {
- .output = DerivationOutputInputAddressed {
- .path = store.parseStorePath(pathS),
- }
+ return DerivationOutput::InputAddressed {
+ .path = store.parseStorePath(pathS),
};
}
}
@@ -260,8 +301,8 @@ Derivation parseDerivation(const Store & store, std::string && s, std::string_vi
/* Parse the environment variables. */
expect(str, ",[");
while (!endOfList(str)) {
- expect(str, "("); string name = parseString(str);
- expect(str, ","); string value = parseString(str);
+ expect(str, "("); auto name = parseString(str);
+ expect(str, ","); auto value = parseString(str);
expect(str, ")");
drv.env[name] = value;
}
@@ -271,7 +312,7 @@ Derivation parseDerivation(const Store & store, std::string && s, std::string_vi
}
-static void printString(string & res, std::string_view s)
+static void printString(std::string & res, std::string_view s)
{
boost::container::small_vector<char, 64 * 1024> buffer;
buffer.reserve(s.size() * 2 + 2);
@@ -289,7 +330,7 @@ static void printString(string & res, std::string_view s)
}
-static void printUnquotedString(string & res, std::string_view s)
+static void printUnquotedString(std::string & res, std::string_view s)
{
res += '"';
res.append(s);
@@ -298,7 +339,7 @@ static void printUnquotedString(string & res, std::string_view s)
template<class ForwardIterator>
-static void printStrings(string & res, ForwardIterator i, ForwardIterator j)
+static void printStrings(std::string & res, ForwardIterator i, ForwardIterator j)
{
res += '[';
bool first = true;
@@ -311,7 +352,7 @@ static void printStrings(string & res, ForwardIterator i, ForwardIterator j)
template<class ForwardIterator>
-static void printUnquotedStrings(string & res, ForwardIterator i, ForwardIterator j)
+static void printUnquotedStrings(std::string & res, ForwardIterator i, ForwardIterator j)
{
res += '[';
bool first = true;
@@ -323,10 +364,10 @@ static void printUnquotedStrings(string & res, ForwardIterator i, ForwardIterato
}
-string Derivation::unparse(const Store & store, bool maskOutputs,
+std::string Derivation::unparse(const Store & store, bool maskOutputs,
std::map<std::string, StringSet> * actualInputs) const
{
- string s;
+ std::string s;
s.reserve(65536);
s += "Derive([";
@@ -335,27 +376,33 @@ string Derivation::unparse(const Store & store, bool maskOutputs,
if (first) first = false; else s += ',';
s += '('; printUnquotedString(s, i.first);
std::visit(overloaded {
- [&](const DerivationOutputInputAddressed & doi) {
+ [&](const DerivationOutput::InputAddressed & doi) {
s += ','; printUnquotedString(s, maskOutputs ? "" : store.printStorePath(doi.path));
s += ','; printUnquotedString(s, "");
s += ','; printUnquotedString(s, "");
},
- [&](const DerivationOutputCAFixed & dof) {
+ [&](const DerivationOutput::CAFixed & dof) {
s += ','; printUnquotedString(s, maskOutputs ? "" : store.printStorePath(dof.path(store, name, i.first)));
s += ','; printUnquotedString(s, dof.hash.printMethodAlgo());
s += ','; printUnquotedString(s, dof.hash.hash.to_string(Base16, false));
},
- [&](const DerivationOutputCAFloating & dof) {
+ [&](const DerivationOutput::CAFloating & dof) {
s += ','; printUnquotedString(s, "");
s += ','; printUnquotedString(s, makeFileIngestionPrefix(dof.method) + printHashType(dof.hashType));
s += ','; printUnquotedString(s, "");
},
- [&](const DerivationOutputDeferred &) {
+ [&](const DerivationOutput::Deferred &) {
s += ','; printUnquotedString(s, "");
s += ','; printUnquotedString(s, "");
s += ','; printUnquotedString(s, "");
+ },
+ [&](const DerivationOutputImpure & doi) {
+ // FIXME
+ s += ','; printUnquotedString(s, "");
+ s += ','; printUnquotedString(s, makeFileIngestionPrefix(doi.method) + printHashType(doi.hashType));
+ s += ','; printUnquotedString(s, "impure");
}
- }, i.second.output);
+ }, i.second.raw());
s += ')';
}
@@ -401,7 +448,7 @@ string Derivation::unparse(const Store & store, bool maskOutputs,
// FIXME: remove
-bool isDerivation(const string & fileName)
+bool isDerivation(const std::string & fileName)
{
return hasSuffix(fileName, drvExtension);
}
@@ -419,49 +466,100 @@ std::string outputPathName(std::string_view drvName, std::string_view outputName
DerivationType BasicDerivation::type() const
{
- std::set<std::string_view> inputAddressedOutputs, fixedCAOutputs, floatingCAOutputs, deferredIAOutputs;
+ std::set<std::string_view>
+ inputAddressedOutputs,
+ fixedCAOutputs,
+ floatingCAOutputs,
+ deferredIAOutputs,
+ impureOutputs;
std::optional<HashType> floatingHashType;
+
for (auto & i : outputs) {
std::visit(overloaded {
- [&](const DerivationOutputInputAddressed &) {
+ [&](const DerivationOutput::InputAddressed &) {
inputAddressedOutputs.insert(i.first);
},
- [&](const DerivationOutputCAFixed &) {
+ [&](const DerivationOutput::CAFixed &) {
fixedCAOutputs.insert(i.first);
},
- [&](const DerivationOutputCAFloating & dof) {
+ [&](const DerivationOutput::CAFloating & dof) {
floatingCAOutputs.insert(i.first);
if (!floatingHashType) {
floatingHashType = dof.hashType;
} else {
if (*floatingHashType != dof.hashType)
- throw Error("All floating outputs must use the same hash type");
+ throw Error("all floating outputs must use the same hash type");
}
},
- [&](const DerivationOutputDeferred &) {
- deferredIAOutputs.insert(i.first);
+ [&](const DerivationOutput::Deferred &) {
+ deferredIAOutputs.insert(i.first);
},
- }, i.second.output);
+ [&](const DerivationOutput::Impure &) {
+ impureOutputs.insert(i.first);
+ },
+ }, i.second.raw());
}
- if (inputAddressedOutputs.empty() && fixedCAOutputs.empty() && floatingCAOutputs.empty() && deferredIAOutputs.empty()) {
- throw Error("Must have at least one output");
- } else if (! inputAddressedOutputs.empty() && fixedCAOutputs.empty() && floatingCAOutputs.empty() && deferredIAOutputs.empty()) {
- return DerivationType::InputAddressed;
- } else if (inputAddressedOutputs.empty() && ! fixedCAOutputs.empty() && floatingCAOutputs.empty() && deferredIAOutputs.empty()) {
+ if (inputAddressedOutputs.empty()
+ && fixedCAOutputs.empty()
+ && floatingCAOutputs.empty()
+ && deferredIAOutputs.empty()
+ && impureOutputs.empty())
+ throw Error("must have at least one output");
+
+ if (!inputAddressedOutputs.empty()
+ && fixedCAOutputs.empty()
+ && floatingCAOutputs.empty()
+ && deferredIAOutputs.empty()
+ && impureOutputs.empty())
+ return DerivationType::InputAddressed {
+ .deferred = false,
+ };
+
+ if (inputAddressedOutputs.empty()
+ && !fixedCAOutputs.empty()
+ && floatingCAOutputs.empty()
+ && deferredIAOutputs.empty()
+ && impureOutputs.empty())
+ {
if (fixedCAOutputs.size() > 1)
// FIXME: Experimental feature?
- throw Error("Only one fixed output is allowed for now");
+ throw Error("only one fixed output is allowed for now");
if (*fixedCAOutputs.begin() != "out")
- throw Error("Single fixed output must be named \"out\"");
- return DerivationType::CAFixed;
- } else if (inputAddressedOutputs.empty() && fixedCAOutputs.empty() && ! floatingCAOutputs.empty() && deferredIAOutputs.empty()) {
- return DerivationType::CAFloating;
- } else if (inputAddressedOutputs.empty() && fixedCAOutputs.empty() && floatingCAOutputs.empty() && !deferredIAOutputs.empty()) {
- return DerivationType::DeferredInputAddressed;
- } else {
- throw Error("Can't mix derivation output types");
+ throw Error("single fixed output must be named \"out\"");
+ return DerivationType::ContentAddressed {
+ .sandboxed = false,
+ .fixed = true,
+ };
}
+
+ if (inputAddressedOutputs.empty()
+ && fixedCAOutputs.empty()
+ && !floatingCAOutputs.empty()
+ && deferredIAOutputs.empty()
+ && impureOutputs.empty())
+ return DerivationType::ContentAddressed {
+ .sandboxed = true,
+ .fixed = false,
+ };
+
+ if (inputAddressedOutputs.empty()
+ && fixedCAOutputs.empty()
+ && floatingCAOutputs.empty()
+ && !deferredIAOutputs.empty()
+ && impureOutputs.empty())
+ return DerivationType::InputAddressed {
+ .deferred = true,
+ };
+
+ if (inputAddressedOutputs.empty()
+ && fixedCAOutputs.empty()
+ && floatingCAOutputs.empty()
+ && deferredIAOutputs.empty()
+ && !impureOutputs.empty())
+ return DerivationType::Impure { };
+
+ throw Error("can't mix derivation output types");
}
@@ -473,7 +571,7 @@ Sync<DrvHashes> drvHashes;
/* Look up the derivation by value and memoize the
`hashDerivationModulo` call.
*/
-static const DrvHashModulo pathDerivationModulo(Store & store, const StorePath & drvPath)
+static const DrvHash pathDerivationModulo(Store & store, const StorePath & drvPath)
{
{
auto hashes = drvHashes.lock();
@@ -508,92 +606,87 @@ static const DrvHashModulo pathDerivationModulo(Store & store, const StorePath &
don't leak the provenance of fixed outputs, reducing pointless cache
misses as the build itself won't know this.
*/
-DrvHashModulo hashDerivationModulo(Store & store, const Derivation & drv, bool maskOutputs)
+DrvHash hashDerivationModulo(Store & store, const Derivation & drv, bool maskOutputs)
{
- bool isDeferred = false;
+ auto type = drv.type();
+
/* Return a fixed hash for fixed-output derivations. */
- switch (drv.type()) {
- case DerivationType::CAFixed: {
+ if (type.isFixed()) {
std::map<std::string, Hash> outputHashes;
for (const auto & i : drv.outputs) {
- auto & dof = std::get<DerivationOutputCAFixed>(i.second.output);
+ auto & dof = std::get<DerivationOutput::CAFixed>(i.second.raw());
auto hash = hashString(htSHA256, "fixed:out:"
+ dof.hash.printMethodAlgo() + ":"
+ dof.hash.hash.to_string(Base16, false) + ":"
+ store.printStorePath(dof.path(store, drv.name, i.first)));
outputHashes.insert_or_assign(i.first, std::move(hash));
}
- return outputHashes;
+ return DrvHash {
+ .hashes = outputHashes,
+ .kind = DrvHash::Kind::Regular,
+ };
}
- case DerivationType::CAFloating:
- isDeferred = true;
- break;
- case DerivationType::InputAddressed:
- break;
- case DerivationType::DeferredInputAddressed:
- break;
+
+ if (!type.isPure()) {
+ std::map<std::string, Hash> outputHashes;
+ for (const auto & [outputName, _] : drv.outputs)
+ outputHashes.insert_or_assign(outputName, impureOutputHash);
+ return DrvHash {
+ .hashes = outputHashes,
+ .kind = DrvHash::Kind::Deferred,
+ };
}
- /* For other derivations, replace the inputs paths with recursive
- calls to this function. */
+ auto kind = std::visit(overloaded {
+ [](const DerivationType::InputAddressed & ia) {
+ /* This might be a "pesimistically" deferred output, so we don't
+ "taint" the kind yet. */
+ return DrvHash::Kind::Regular;
+ },
+ [](const DerivationType::ContentAddressed & ca) {
+ return ca.fixed
+ ? DrvHash::Kind::Regular
+ : DrvHash::Kind::Deferred;
+ },
+ [](const DerivationType::Impure &) -> DrvHash::Kind {
+ assert(false);
+ }
+ }, drv.type().raw());
+
std::map<std::string, StringSet> inputs2;
- for (auto & i : drv.inputDrvs) {
- const auto & res = pathDerivationModulo(store, i.first);
- std::visit(overloaded {
- // Regular non-CA derivation, replace derivation
- [&](const Hash & drvHash) {
- inputs2.insert_or_assign(drvHash.to_string(Base16, false), i.second);
- },
- [&](const DeferredHash & deferredHash) {
- isDeferred = true;
- inputs2.insert_or_assign(deferredHash.hash.to_string(Base16, false), i.second);
- },
- // CA derivation's output hashes
- [&](const CaOutputHashes & outputHashes) {
- std::set<std::string> justOut = { "out" };
- for (auto & output : i.second) {
- /* Put each one in with a single "out" output.. */
- const auto h = outputHashes.at(output);
- inputs2.insert_or_assign(
- h.to_string(Base16, false),
- justOut);
- }
- },
- }, res);
+ for (auto & [drvPath, inputOutputs0] : drv.inputDrvs) {
+ // Avoid lambda capture restriction with standard / Clang
+ auto & inputOutputs = inputOutputs0;
+ const auto & res = pathDerivationModulo(store, drvPath);
+ if (res.kind == DrvHash::Kind::Deferred)
+ kind = DrvHash::Kind::Deferred;
+ for (auto & outputName : inputOutputs) {
+ const auto h = res.hashes.at(outputName);
+ inputs2[h.to_string(Base16, false)].insert(outputName);
+ }
}
auto hash = hashString(htSHA256, drv.unparse(store, maskOutputs, &inputs2));
- if (isDeferred)
- return DeferredHash { hash };
- else
- return hash;
+ std::map<std::string, Hash> outputHashes;
+ for (const auto & [outputName, _] : drv.outputs) {
+ outputHashes.insert_or_assign(outputName, hash);
+ }
+
+ return DrvHash {
+ .hashes = outputHashes,
+ .kind = kind,
+ };
}
std::map<std::string, Hash> staticOutputHashes(Store & store, const Derivation & drv)
{
- std::map<std::string, Hash> res;
- std::visit(overloaded {
- [&](const Hash & drvHash) {
- for (auto & outputName : drv.outputNames()) {
- res.insert({outputName, drvHash});
- }
- },
- [&](const DeferredHash & deferredHash) {
- for (auto & outputName : drv.outputNames()) {
- res.insert({outputName, deferredHash.hash});
- }
- },
- [&](const CaOutputHashes & outputHashes) {
- res = outputHashes;
- },
- }, hashDerivationModulo(store, drv, true));
- return res;
+ return hashDerivationModulo(store, drv, true).hashes;
}
-bool wantOutput(const string & output, const std::set<string> & wanted)
+bool wantOutput(const std::string & output, const std::set<std::string> & wanted)
{
return wanted.empty() || wanted.find(output) != wanted.end();
}
@@ -616,7 +709,8 @@ StringSet BasicDerivation::outputNames() const
return names;
}
-DerivationOutputsAndOptPaths BasicDerivation::outputsAndOptPaths(const Store & store) const {
+DerivationOutputsAndOptPaths BasicDerivation::outputsAndOptPaths(const Store & store) const
+{
DerivationOutputsAndOptPaths outsAndOptPaths;
for (auto output : outputs)
outsAndOptPaths.insert(std::make_pair(
@@ -627,7 +721,8 @@ DerivationOutputsAndOptPaths BasicDerivation::outputsAndOptPaths(const Store & s
return outsAndOptPaths;
}
-std::string_view BasicDerivation::nameFromPath(const StorePath & drvPath) {
+std::string_view BasicDerivation::nameFromPath(const StorePath & drvPath)
+{
auto nameWithSuffix = drvPath.name();
constexpr std::string_view extension = ".drv";
assert(hasSuffix(nameWithSuffix, extension));
@@ -669,27 +764,32 @@ void writeDerivation(Sink & out, const Store & store, const BasicDerivation & dr
for (auto & i : drv.outputs) {
out << i.first;
std::visit(overloaded {
- [&](const DerivationOutputInputAddressed & doi) {
+ [&](const DerivationOutput::InputAddressed & doi) {
out << store.printStorePath(doi.path)
<< ""
<< "";
},
- [&](const DerivationOutputCAFixed & dof) {
+ [&](const DerivationOutput::CAFixed & dof) {
out << store.printStorePath(dof.path(store, drv.name, i.first))
<< dof.hash.printMethodAlgo()
<< dof.hash.hash.to_string(Base16, false);
},
- [&](const DerivationOutputCAFloating & dof) {
+ [&](const DerivationOutput::CAFloating & dof) {
out << ""
<< (makeFileIngestionPrefix(dof.method) + printHashType(dof.hashType))
<< "";
},
- [&](const DerivationOutputDeferred &) {
+ [&](const DerivationOutput::Deferred &) {
out << ""
<< ""
<< "";
},
- }, i.second.output);
+ [&](const DerivationOutput::Impure & doi) {
+ out << ""
+ << (makeFileIngestionPrefix(doi.method) + printHashType(doi.hashType))
+ << "impure";
+ },
+ }, i.second.raw());
}
worker_proto::write(store, out, drv.inputSrcs);
out << drv.platform << drv.builder << drv.args;
@@ -714,21 +814,19 @@ std::string downstreamPlaceholder(const Store & store, const StorePath & drvPath
}
-static void rewriteDerivation(Store & store, BasicDerivation & drv, const StringMap & rewrites) {
-
- debug("Rewriting the derivation");
-
- for (auto &rewrite: rewrites) {
+static void rewriteDerivation(Store & store, BasicDerivation & drv, const StringMap & rewrites)
+{
+ for (auto & rewrite : rewrites) {
debug("rewriting %s as %s", rewrite.first, rewrite.second);
}
drv.builder = rewriteStrings(drv.builder, rewrites);
- for (auto & arg: drv.args) {
+ for (auto & arg : drv.args) {
arg = rewriteStrings(arg, rewrites);
}
StringPairs newEnv;
- for (auto & envVar: drv.env) {
+ for (auto & envVar : drv.env) {
auto envName = rewriteStrings(envVar.first, rewrites);
auto envValue = rewriteStrings(envVar.second, rewrites);
newEnv.emplace(envName, envValue);
@@ -737,43 +835,52 @@ static void rewriteDerivation(Store & store, BasicDerivation & drv, const String
auto hashModulo = hashDerivationModulo(store, Derivation(drv), true);
for (auto & [outputName, output] : drv.outputs) {
- if (std::holds_alternative<DerivationOutputDeferred>(output.output)) {
- Hash h = std::get<Hash>(hashModulo);
+ if (std::holds_alternative<DerivationOutput::Deferred>(output.raw())) {
+ auto & h = hashModulo.hashes.at(outputName);
auto outPath = store.makeOutputPath(outputName, h, drv.name);
drv.env[outputName] = store.printStorePath(outPath);
- output = DerivationOutput {
- .output = DerivationOutputInputAddressed {
- .path = std::move(outPath),
- },
+ output = DerivationOutput::InputAddressed {
+ .path = std::move(outPath),
};
}
}
}
-std::optional<BasicDerivation> Derivation::tryResolve(Store & store) {
+std::optional<BasicDerivation> Derivation::tryResolve(Store & store) const
+{
+ std::map<std::pair<StorePath, std::string>, StorePath> inputDrvOutputs;
+
+ for (auto & input : inputDrvs)
+ for (auto & [outputName, outputPath] : store.queryPartialDerivationOutputMap(input.first))
+ if (outputPath)
+ inputDrvOutputs.insert_or_assign({input.first, outputName}, *outputPath);
+
+ return tryResolve(store, inputDrvOutputs);
+}
+
+std::optional<BasicDerivation> Derivation::tryResolve(
+ Store & store,
+ const std::map<std::pair<StorePath, std::string>, StorePath> & inputDrvOutputs) const
+{
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) {
- warn("output %s of input %s missing, aborting the resolving",
+ for (auto & [inputDrv, inputOutputs] : inputDrvs) {
+ for (auto & outputName : inputOutputs) {
+ if (auto actualPath = get(inputDrvOutputs, { inputDrv, outputName })) {
+ inputRewrites.emplace(
+ downstreamPlaceholder(store, inputDrv, outputName),
+ store.printStorePath(*actualPath));
+ resolved.inputSrcs.insert(*actualPath);
+ } else {
+ warn("output '%s' of input '%s' missing, aborting the resolving",
outputName,
- store.printStorePath(input.first)
- );
- return std::nullopt;
+ store.printStorePath(inputDrv));
+ return {};
}
- auto actualPath = *actualPathOpt;
- inputRewrites.emplace(
- downstreamPlaceholder(store, input.first, outputName),
- store.printStorePath(actualPath));
- resolved.inputSrcs.insert(std::move(actualPath));
}
}
@@ -782,4 +889,6 @@ std::optional<BasicDerivation> Derivation::tryResolve(Store & store) {
return resolved;
}
+const Hash impureOutputHash = hashString(htSHA256, "impure");
+
}
diff --git a/src/libstore/derivations.hh b/src/libstore/derivations.hh
index a644cec60..af198a767 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 "repair-flag.hh"
#include "sync.hh"
#include <map>
@@ -40,70 +41,124 @@ struct DerivationOutputCAFloating
};
/* Input-addressed output which depends on a (CA) derivation whose hash isn't
- * known atm
+ * known yet.
*/
struct DerivationOutputDeferred {};
-struct DerivationOutput
+/* Impure output which is moved to a content-addressed location (like
+ CAFloating) but isn't registered as a realization.
+ */
+struct DerivationOutputImpure
{
- std::variant<
- DerivationOutputInputAddressed,
- DerivationOutputCAFixed,
- DerivationOutputCAFloating,
- DerivationOutputDeferred
- > output;
+ /* information used for expected hash computation */
+ FileIngestionMethod method;
+ HashType hashType;
+};
+
+typedef std::variant<
+ DerivationOutputInputAddressed,
+ DerivationOutputCAFixed,
+ DerivationOutputCAFloating,
+ DerivationOutputDeferred,
+ DerivationOutputImpure
+> _DerivationOutputRaw;
+
+struct DerivationOutput : _DerivationOutputRaw
+{
+ using Raw = _DerivationOutputRaw;
+ using Raw::Raw;
+
+ using InputAddressed = DerivationOutputInputAddressed;
+ using CAFixed = DerivationOutputCAFixed;
+ using CAFloating = DerivationOutputCAFloating;
+ using Deferred = DerivationOutputDeferred;
+ using Impure = DerivationOutputImpure;
/* Note, when you use this function you should make sure that you're passing
the right derivation name. When in doubt, you should use the safer
interface provided by BasicDerivation::outputsAndOptPaths */
std::optional<StorePath> path(const Store & store, std::string_view drvName, std::string_view outputName) const;
+
+ inline const Raw & raw() const {
+ return static_cast<const Raw &>(*this);
+ }
};
-typedef std::map<string, DerivationOutput> DerivationOutputs;
+typedef std::map<std::string, DerivationOutput> DerivationOutputs;
/* These are analogues to the previous DerivationOutputs data type, but they
also contains, for each output, the (optional) store path in which it would
be written. To calculate values of these types, see the corresponding
functions in BasicDerivation */
-typedef std::map<string, std::pair<DerivationOutput, std::optional<StorePath>>>
+typedef std::map<std::string, std::pair<DerivationOutput, std::optional<StorePath>>>
DerivationOutputsAndOptPaths;
/* For inputs that are sub-derivations, we specify exactly which
output IDs we are interested in. */
typedef std::map<StorePath, StringSet> DerivationInputs;
-typedef std::map<string, string> StringPairs;
-
-enum struct DerivationType : uint8_t {
- InputAddressed,
- DeferredInputAddressed,
- CAFixed,
- CAFloating,
+struct DerivationType_InputAddressed {
+ bool deferred;
};
-/* Do the outputs of the derivation have paths calculated from their content,
- or from the derivation itself? */
-bool derivationIsCA(DerivationType);
-
-/* Is the content of the outputs fixed a-priori via a hash? Never true for
- non-CA derivations. */
-bool derivationIsFixed(DerivationType);
+struct DerivationType_ContentAddressed {
+ bool sandboxed;
+ bool fixed;
+};
-/* Is the derivation impure and needs to access non-deterministic resources, or
- pure and can be sandboxed? Note that whether or not we actually sandbox the
- derivation is controlled separately. Never true for non-CA derivations. */
-bool derivationIsImpure(DerivationType);
+struct DerivationType_Impure {
+};
-/* Does the derivation knows its own output paths?
- * Only true when there's no floating-ca derivation involved in the closure.
- */
-bool derivationHasKnownOutputPaths(DerivationType);
+typedef std::variant<
+ DerivationType_InputAddressed,
+ DerivationType_ContentAddressed,
+ DerivationType_Impure
+> _DerivationTypeRaw;
+
+struct DerivationType : _DerivationTypeRaw {
+ using Raw = _DerivationTypeRaw;
+ using Raw::Raw;
+ using InputAddressed = DerivationType_InputAddressed;
+ using ContentAddressed = DerivationType_ContentAddressed;
+ using Impure = DerivationType_Impure;
+
+ /* Do the outputs of the derivation have paths calculated from their content,
+ or from the derivation itself? */
+ bool isCA() const;
+
+ /* Is the content of the outputs fixed a-priori via a hash? Never true for
+ non-CA derivations. */
+ bool isFixed() const;
+
+ /* Whether the derivation is fully sandboxed. If false, the
+ sandbox is opened up, e.g. the derivation has access to the
+ network. Note that whether or not we actually sandbox the
+ derivation is controlled separately. Always true for non-CA
+ derivations. */
+ bool isSandboxed() const;
+
+ /* Whether the derivation is expected to produce the same result
+ every time, and therefore it only needs to be built once. This
+ is only false for derivations that have the attribute '__impure
+ = true'. */
+ bool isPure() const;
+
+ /* Does the derivation knows its own output paths?
+ Only true when there's no floating-ca derivation involved in the
+ closure, or if fixed output.
+ */
+ bool hasKnownOutputPaths() const;
+
+ inline const Raw & raw() const {
+ return static_cast<const Raw &>(*this);
+ }
+};
struct BasicDerivation
{
DerivationOutputs outputs; /* keyed on symbolic IDs */
StorePathSet inputSrcs; /* inputs that are sources */
- string platform;
+ std::string platform;
Path builder;
Strings args;
StringPairs env;
@@ -142,7 +197,14 @@ struct Derivation : BasicDerivation
added directly to input sources.
2. Input placeholders are replaced with realized input store paths. */
- std::optional<BasicDerivation> tryResolve(Store & store);
+ std::optional<BasicDerivation> tryResolve(Store & store) const;
+
+ /* Like the above, but instead of querying the Nix database for
+ realisations, uses a given mapping from input derivation paths
+ + output names to actual output store paths. */
+ std::optional<BasicDerivation> tryResolve(
+ Store & store,
+ const std::map<std::pair<StorePath, std::string>, StorePath> & inputDrvOutputs) const;
Derivation() = default;
Derivation(const BasicDerivation & bd) : BasicDerivation(bd) { }
@@ -152,8 +214,6 @@ struct Derivation : BasicDerivation
class Store;
-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,
@@ -164,7 +224,7 @@ StorePath writeDerivation(Store & store,
Derivation parseDerivation(const Store & store, std::string && s, std::string_view name);
// FIXME: remove
-bool isDerivation(const string & fileName);
+bool isDerivation(const std::string & fileName);
/* Calculate the name that will be used for the store path for this
output.
@@ -173,17 +233,27 @@ bool isDerivation(const string & fileName);
the output name is "out". */
std::string outputPathName(std::string_view drvName, std::string_view outputName);
-// known CA drv's output hashes, current just for fixed-output derivations
-// whose output hashes are always known since they are fixed up-front.
-typedef std::map<std::string, Hash> CaOutputHashes;
-struct DeferredHash { Hash hash; };
+// The hashes modulo of a derivation.
+//
+// Each output is given a hash, although in practice only the content-addressed
+// derivations (fixed-output or not) will have a different hash for each
+// output.
+struct DrvHash {
+ std::map<std::string, Hash> hashes;
-typedef std::variant<
- Hash, // regular DRV normalized hash
- CaOutputHashes, // Fixed-output derivation hashes
- DeferredHash // Deferred hashes for floating outputs drvs and their dependencies
-> DrvHashModulo;
+ enum struct Kind : bool {
+ // Statically determined derivations.
+ // This hash will be directly used to compute the output paths
+ Regular,
+ // Floating-output derivations (and their reverse dependencies).
+ Deferred,
+ };
+
+ Kind kind;
+};
+
+void operator |= (DrvHash::Kind & self, const DrvHash::Kind & other) noexcept;
/* Returns hashes with the details of fixed-output subderivations
expunged.
@@ -208,21 +278,23 @@ typedef std::variant<
ATerm, after subderivations have been likewise expunged from that
derivation.
*/
-DrvHashModulo hashDerivationModulo(Store & store, const Derivation & drv, bool maskOutputs);
+DrvHash hashDerivationModulo(Store & store, const Derivation & drv, bool maskOutputs);
/*
Return a map associating each output to a hash that uniquely identifies its
derivation (modulo the self-references).
+
+ FIXME: what is the Hash in this map?
*/
-std::map<std::string, Hash> staticOutputHashes(Store& store, const Derivation& drv);
+std::map<std::string, Hash> staticOutputHashes(Store & store, const Derivation & drv);
/* Memoisation of hashDerivationModulo(). */
-typedef std::map<StorePath, DrvHashModulo> DrvHashes;
+typedef std::map<StorePath, DrvHash> DrvHashes;
// FIXME: global, though at least thread-safe.
extern Sync<DrvHashes> drvHashes;
-bool wantOutput(const string & output, const std::set<string> & wanted);
+bool wantOutput(const std::string & output, const std::set<std::string> & wanted);
struct Source;
struct Sink;
@@ -247,4 +319,6 @@ std::string hashPlaceholder(const std::string_view outputName);
dependency which is a CA derivation. */
std::string downstreamPlaceholder(const Store & store, const StorePath & drvPath, std::string_view outputName);
+extern const Hash impureOutputHash;
+
}
diff --git a/src/libstore/derived-path.cc b/src/libstore/derived-path.cc
index 3d188e981..319b1c790 100644
--- a/src/libstore/derived-path.cc
+++ b/src/libstore/derived-path.cc
@@ -1,4 +1,5 @@
#include "derived-path.hh"
+#include "derivations.hh"
#include "store-api.hh"
#include <nlohmann/json.hpp>
@@ -11,6 +12,21 @@ nlohmann::json DerivedPath::Opaque::toJSON(ref<Store> store) const {
return res;
}
+nlohmann::json DerivedPath::Built::toJSON(ref<Store> store) const {
+ nlohmann::json res;
+ res["drvPath"] = store->printStorePath(drvPath);
+ // Fallback for the input-addressed derivation case: We expect to always be
+ // able to print the output paths, so let’s do it
+ auto knownOutputs = store->queryPartialDerivationOutputMap(drvPath);
+ for (const auto& output : outputs) {
+ if (knownOutputs.at(output))
+ res["outputs"][output] = store->printStorePath(knownOutputs.at(output).value());
+ else
+ res["outputs"][output] = nullptr;
+ }
+ return res;
+}
+
nlohmann::json BuiltPath::Built::toJSON(ref<Store> store) const {
nlohmann::json res;
res["drvPath"] = store->printStorePath(drvPath);
@@ -35,16 +51,22 @@ StorePathSet BuiltPath::outPaths() const
);
}
-nlohmann::json derivedPathsWithHintsToJSON(const BuiltPaths & buildables, ref<Store> store) {
+template<typename T>
+nlohmann::json stuffToJSON(const std::vector<T> & ts, ref<Store> store) {
auto res = nlohmann::json::array();
- for (const BuiltPath & buildable : buildables) {
- std::visit([&res, store](const auto & buildable) {
- res.push_back(buildable.toJSON(store));
- }, buildable.raw());
+ for (const T & t : ts) {
+ std::visit([&res, store](const auto & t) {
+ res.push_back(t.toJSON(store));
+ }, t.raw());
}
return res;
}
+nlohmann::json derivedPathsWithHintsToJSON(const BuiltPaths & buildables, ref<Store> store)
+{ return stuffToJSON<BuiltPath>(buildables, store); }
+nlohmann::json derivedPathsToJSON(const DerivedPaths & paths, ref<Store> store)
+{ return stuffToJSON<DerivedPath>(paths, store); }
+
std::string DerivedPath::Opaque::to_string(const Store & store) const {
return store.printStorePath(path);
@@ -75,9 +97,9 @@ DerivedPath::Built DerivedPath::Built::parse(const Store & store, std::string_vi
assert(n != s.npos);
auto drvPath = store.parseStorePath(s.substr(0, n));
auto outputsS = s.substr(n + 1);
- std::set<string> outputs;
+ std::set<std::string> outputs;
if (outputsS != "*")
- outputs = tokenizeString<std::set<string>>(outputsS, ",");
+ outputs = tokenizeString<std::set<std::string>>(outputsS, ",");
return {drvPath, outputs};
}
diff --git a/src/libstore/derived-path.hh b/src/libstore/derived-path.hh
index 9d6ace069..24a0ae773 100644
--- a/src/libstore/derived-path.hh
+++ b/src/libstore/derived-path.hh
@@ -25,6 +25,9 @@ struct DerivedPathOpaque {
nlohmann::json toJSON(ref<Store> store) const;
std::string to_string(const Store & store) const;
static DerivedPathOpaque parse(const Store & store, std::string_view);
+
+ bool operator < (const DerivedPathOpaque & b) const
+ { return path < b.path; }
};
/**
@@ -45,6 +48,10 @@ struct DerivedPathBuilt {
std::string to_string(const Store & store) const;
static DerivedPathBuilt parse(const Store & store, std::string_view);
+ nlohmann::json toJSON(ref<Store> store) const;
+
+ bool operator < (const DerivedPathBuilt & b) const
+ { return std::make_pair(drvPath, outputs) < std::make_pair(b.drvPath, b.outputs); }
};
using _DerivedPathRaw = std::variant<
@@ -119,5 +126,6 @@ typedef std::vector<DerivedPath> DerivedPaths;
typedef std::vector<BuiltPath> BuiltPaths;
nlohmann::json derivedPathsWithHintsToJSON(const BuiltPaths & buildables, ref<Store> store);
+nlohmann::json derivedPathsToJSON(const DerivedPaths & , ref<Store> store);
}
diff --git a/src/libstore/dummy-store.cc b/src/libstore/dummy-store.cc
index 62dc21c59..b4fbe0b70 100644
--- a/src/libstore/dummy-store.cc
+++ b/src/libstore/dummy-store.cc
@@ -21,7 +21,7 @@ struct DummyStore : public virtual DummyStoreConfig, public virtual Store
, Store(params)
{ }
- string getUri() override
+ std::string getUri() override
{
return *uriSchemes().begin();
}
@@ -43,8 +43,11 @@ struct DummyStore : public virtual DummyStoreConfig, public virtual Store
RepairFlag repair, CheckSigsFlag checkSigs) override
{ unsupported("addToStore"); }
- StorePath addTextToStore(const string & name, const string & s,
- const StorePathSet & references, RepairFlag repair) override
+ StorePath addTextToStore(
+ std::string_view name,
+ std::string_view s,
+ const StorePathSet & references,
+ RepairFlag repair) override
{ unsupported("addTextToStore"); }
void narFromPath(const StorePath & path, Sink & sink) override
diff --git a/src/libstore/filetransfer.cc b/src/libstore/filetransfer.cc
index 6b62311cf..c46262299 100644
--- a/src/libstore/filetransfer.cc
+++ b/src/libstore/filetransfer.cc
@@ -33,12 +33,12 @@ FileTransferSettings fileTransferSettings;
static GlobalConfig::Register rFileTransferSettings(&fileTransferSettings);
-std::string resolveUri(const std::string & uri)
+std::string resolveUri(std::string_view uri)
{
if (uri.compare(0, 8, "channel:") == 0)
- return "https://nixos.org/channels/" + std::string(uri, 8) + "/nixexprs.tar.xz";
+ return "https://nixos.org/channels/" + std::string(uri.substr(8)) + "/nixexprs.tar.xz";
else
- return uri;
+ return std::string(uri);
}
struct curlFileTransfer : public FileTransfer
@@ -128,7 +128,7 @@ struct curlFileTransfer : public FileTransfer
if (requestHeaders) curl_slist_free_all(requestHeaders);
try {
if (!done)
- fail(FileTransferError(Interrupted, nullptr, "download of '%s' was interrupted", request.uri));
+ fail(FileTransferError(Interrupted, {}, "download of '%s' was interrupted", request.uri));
} catch (...) {
ignoreException();
}
@@ -197,15 +197,15 @@ struct curlFileTransfer : public FileTransfer
result.etag = "";
result.data.clear();
result.bodySize = 0;
- statusMsg = trim(match[1]);
+ statusMsg = trim(match.str(1));
acceptRanges = false;
encoding = "";
} else {
auto i = line.find(':');
- if (i != string::npos) {
- string name = toLower(trim(string(line, 0, i)));
+ if (i != std::string::npos) {
+ std::string name = toLower(trim(line.substr(0, i)));
if (name == "etag") {
- result.etag = trim(string(line, i + 1));
+ result.etag = trim(line.substr(i + 1));
/* Hack to work around a GitHub bug: it sends
ETags, but ignores If-None-Match. So if we get
the expected ETag on a 200 response, then shut
@@ -218,8 +218,8 @@ struct curlFileTransfer : public FileTransfer
return 0;
}
} else if (name == "content-encoding")
- encoding = trim(string(line, i + 1));
- else if (name == "accept-ranges" && toLower(trim(std::string(line, i + 1))) == "bytes")
+ encoding = trim(line.substr(i + 1));
+ else if (name == "accept-ranges" && toLower(trim(line.substr(i + 1))) == "bytes")
acceptRanges = true;
}
}
@@ -704,7 +704,7 @@ struct curlFileTransfer : public FileTransfer
auto s3Res = s3Helper.getObject(bucketName, key);
FileTransferResult res;
if (!s3Res.data)
- throw FileTransferError(NotFound, nullptr, "S3 object '%s' does not exist", request.uri);
+ throw FileTransferError(NotFound, {}, "S3 object '%s' does not exist", request.uri);
res.data = std::move(*s3Res.data);
callback(std::move(res));
#else
@@ -866,18 +866,18 @@ FileTransferError::FileTransferError(FileTransfer::Error error, std::optional<st
// FIXME: Due to https://github.com/NixOS/nix/issues/3841 we don't know how
// to print different messages for different verbosity levels. For now
// we add some heuristics for detecting when we want to show the response.
- if (response && (response->size() < 1024 || response->find("<html>") != string::npos))
+ if (response && (response->size() < 1024 || response->find("<html>") != std::string::npos))
err.msg = hintfmt("%1%\n\nresponse body:\n\n%2%", normaltxt(hf.str()), chomp(*response));
else
err.msg = hf;
}
-bool isUri(const string & s)
+bool isUri(std::string_view s)
{
if (s.compare(0, 8, "channel:") == 0) return true;
size_t pos = s.find("://");
- if (pos == string::npos) return false;
- string scheme(s, 0, pos);
+ if (pos == std::string::npos) return false;
+ std::string scheme(s, 0, pos);
return scheme == "http" || scheme == "https" || scheme == "file" || scheme == "channel" || scheme == "git" || scheme == "s3" || scheme == "ssh";
}
diff --git a/src/libstore/filetransfer.hh b/src/libstore/filetransfer.hh
index 3e61b23b1..ca61e3937 100644
--- a/src/libstore/filetransfer.hh
+++ b/src/libstore/filetransfer.hh
@@ -119,17 +119,17 @@ class FileTransferError : public Error
{
public:
FileTransfer::Error error;
- std::optional<string> response; // intentionally optional
+ std::optional<std::string> response; // intentionally optional
template<typename... Args>
- FileTransferError(FileTransfer::Error error, std::optional<string> response, const Args & ... args);
+ FileTransferError(FileTransfer::Error error, std::optional<std::string> response, const Args & ... args);
virtual const char* sname() const override { return "FileTransferError"; }
};
-bool isUri(const string & s);
+bool isUri(std::string_view s);
/* Resolve deprecated 'channel:<foo>' URLs. */
-std::string resolveUri(const std::string & uri);
+std::string resolveUri(std::string_view uri);
}
diff --git a/src/libstore/gc-store.hh b/src/libstore/gc-store.hh
new file mode 100644
index 000000000..b3cbbad74
--- /dev/null
+++ b/src/libstore/gc-store.hh
@@ -0,0 +1,84 @@
+#pragma once
+
+#include "store-api.hh"
+
+
+namespace nix {
+
+
+typedef std::unordered_map<StorePath, std::unordered_set<std::string>> Roots;
+
+
+struct GCOptions
+{
+ /* Garbage collector operation:
+
+ - `gcReturnLive': return the set of paths reachable from
+ (i.e. in the closure of) the roots.
+
+ - `gcReturnDead': return the set of paths not reachable from
+ the roots.
+
+ - `gcDeleteDead': actually delete the latter set.
+
+ - `gcDeleteSpecific': delete the paths listed in
+ `pathsToDelete', insofar as they are not reachable.
+ */
+ typedef enum {
+ gcReturnLive,
+ gcReturnDead,
+ gcDeleteDead,
+ gcDeleteSpecific,
+ } GCAction;
+
+ GCAction action{gcDeleteDead};
+
+ /* If `ignoreLiveness' is set, then reachability from the roots is
+ ignored (dangerous!). However, the paths must still be
+ unreferenced *within* the store (i.e., there can be no other
+ store paths that depend on them). */
+ bool ignoreLiveness{false};
+
+ /* For `gcDeleteSpecific', the paths to delete. */
+ StorePathSet pathsToDelete;
+
+ /* Stop after at least `maxFreed' bytes have been freed. */
+ uint64_t maxFreed{std::numeric_limits<uint64_t>::max()};
+};
+
+
+struct GCResults
+{
+ /* Depending on the action, the GC roots, or the paths that would
+ be or have been deleted. */
+ PathSet paths;
+
+ /* For `gcReturnDead', `gcDeleteDead' and `gcDeleteSpecific', the
+ number of bytes that would be or was freed. */
+ uint64_t bytesFreed = 0;
+};
+
+
+struct GcStore : public virtual Store
+{
+ inline static std::string operationName = "Garbage collection";
+
+ /* Add an indirect root, which is merely a symlink to `path' from
+ /nix/var/nix/gcroots/auto/<hash of `path'>. `path' is supposed
+ to be a symlink to a store path. The garbage collector will
+ automatically remove the indirect root when it finds that
+ `path' has disappeared. */
+ virtual void addIndirectRoot(const Path & path) = 0;
+
+ /* Find the roots of the garbage collector. Each root is a pair
+ (link, storepath) where `link' is the path of the symlink
+ outside of the Nix store that point to `storePath'. If
+ 'censor' is true, privacy-sensitive information about roots
+ found in /proc is censored. */
+ virtual Roots findRoots(bool censor) = 0;
+
+ /* Perform a garbage collection. */
+ virtual void collectGarbage(const GCOptions & options, GCResults & results) = 0;
+};
+
+}
diff --git a/src/libstore/gc.cc b/src/libstore/gc.cc
index e35199b3d..f65fb1b2e 100644
--- a/src/libstore/gc.cc
+++ b/src/libstore/gc.cc
@@ -47,9 +47,8 @@ static void makeSymlink(const Path & link, const Path & target)
void LocalStore::addIndirectRoot(const Path & path)
{
- string hash = hashString(htSHA1, path).to_string(Base32, false);
- Path realRoot = canonPath((format("%1%/%2%/auto/%3%")
- % stateDir % gcRootsDir % hash).str());
+ std::string hash = hashString(htSHA1, path).to_string(Base32, false);
+ Path realRoot = canonPath(fmt("%1%/%2%/auto/%3%", stateDir, gcRootsDir, hash));
makeSymlink(realRoot, path);
}
@@ -162,7 +161,7 @@ void LocalStore::addTempRoot(const StorePath & path)
}
/* Append the store path to the temporary roots file. */
- string s = printStorePath(path) + '\0';
+ auto s = printStorePath(path) + '\0';
writeFull(state->fdTempRoots.get(), s);
}
@@ -203,12 +202,12 @@ void LocalStore::findTempRoots(Roots & tempRoots, bool censor)
}
/* Read the entire file. */
- string contents = readFile(fd.get());
+ auto contents = readFile(fd.get());
/* Extract the roots. */
- string::size_type pos = 0, end;
+ std::string::size_type pos = 0, end;
- while ((end = contents.find((char) 0, pos)) != string::npos) {
+ while ((end = contents.find((char) 0, pos)) != std::string::npos) {
Path root(contents, pos, end - pos);
debug("got temporary root '%s'", root);
tempRoots[parseStorePath(root)].emplace(censor ? censored : fmt("{temp:%d}", pid));
@@ -305,7 +304,7 @@ Roots LocalStore::findRoots(bool censor)
typedef std::unordered_map<Path, std::unordered_set<std::string>> UncheckedRoots;
-static void readProcLink(const string & file, UncheckedRoots & roots)
+static void readProcLink(const std::string & file, UncheckedRoots & roots)
{
/* 64 is the starting buffer size gnu readlink uses... */
auto bufsiz = ssize_t{64};
@@ -328,7 +327,7 @@ try_again:
.emplace(file);
}
-static string quoteRegexChars(const string & raw)
+static std::string quoteRegexChars(const std::string & raw)
{
static auto specialRegex = std::regex(R"([.^$\\*+?()\[\]{}|])");
return std::regex_replace(raw, specialRegex, R"(\$&)");
@@ -383,7 +382,7 @@ void LocalStore::findRuntimeRoots(Roots & roots, bool censor)
try {
auto mapFile = fmt("/proc/%s/maps", ent->d_name);
- auto mapLines = tokenizeString<std::vector<string>>(readFile(mapFile), "\n");
+ auto mapLines = tokenizeString<std::vector<std::string>>(readFile(mapFile), "\n");
for (const auto & line : mapLines) {
auto match = std::smatch{};
if (std::regex_match(line, match, mapRegex))
@@ -414,7 +413,7 @@ void LocalStore::findRuntimeRoots(Roots & roots, bool censor)
try {
std::regex lsofRegex(R"(^n(/.*)$)");
auto lsofLines =
- tokenizeString<std::vector<string>>(runProgram(LSOF, true, { "-n", "-w", "-F", "n" }), "\n");
+ tokenizeString<std::vector<std::string>>(runProgram(LSOF, true, { "-n", "-w", "-F", "n" }), "\n");
for (const auto & line : lsofLines) {
std::smatch match;
if (std::regex_match(line, match, lsofRegex))
@@ -679,7 +678,8 @@ void LocalStore::collectGarbage(const GCOptions & options, GCResults & results)
alive.insert(start);
try {
StorePathSet closure;
- computeFSClosure(*path, closure);
+ computeFSClosure(*path, closure,
+ /* flipDirection */ false, gcKeepOutputs, gcKeepDerivations);
for (auto & p : closure)
alive.insert(p);
} catch (InvalidPath &) { }
@@ -784,7 +784,7 @@ void LocalStore::collectGarbage(const GCOptions & options, GCResults & results)
struct dirent * dirent;
while (errno = 0, dirent = readdir(dir.get())) {
checkInterrupt();
- string name = dirent->d_name;
+ std::string name = dirent->d_name;
if (name == "." || name == ".." || name == linksName) continue;
if (auto storePath = maybeParseStorePath(storeDir + "/" + name))
@@ -825,7 +825,7 @@ void LocalStore::collectGarbage(const GCOptions & options, GCResults & results)
struct dirent * dirent;
while (errno = 0, dirent = readdir(dir.get())) {
checkInterrupt();
- string name = dirent->d_name;
+ std::string name = dirent->d_name;
if (name == "." || name == "..") continue;
Path path = linksDir + "/" + name;
@@ -842,7 +842,8 @@ void LocalStore::collectGarbage(const GCOptions & options, GCResults & results)
if (unlink(path.c_str()) == -1)
throw SysError("deleting '%1%'", path);
- results.bytesFreed += st.st_size;
+ /* Do not accound for deleted file here. Rely on deletePath()
+ accounting. */
}
struct stat st;
diff --git a/src/libstore/globals.cc b/src/libstore/globals.cc
index 81ca9cc0f..cc009a026 100644
--- a/src/libstore/globals.cc
+++ b/src/libstore/globals.cc
@@ -100,7 +100,7 @@ std::vector<Path> getUserConfigFiles()
// Use the paths specified in NIX_USER_CONF_FILES if it has been defined
auto nixConfFiles = getEnv("NIX_USER_CONF_FILES");
if (nixConfFiles.has_value()) {
- return tokenizeString<std::vector<string>>(nixConfFiles.value(), ":");
+ return tokenizeString<std::vector<std::string>>(nixConfFiles.value(), ":");
}
// Use the paths specified by the XDG spec
@@ -181,7 +181,7 @@ bool Settings::isWSL1()
return hasSuffix(utsbuf.release, "-Microsoft");
}
-const string nixVersion = PACKAGE_VERSION;
+const std::string nixVersion = PACKAGE_VERSION;
NLOHMANN_JSON_SERIALIZE_ENUM(SandboxMode, {
{SandboxMode::smEnabled, true},
diff --git a/src/libstore/globals.hh b/src/libstore/globals.hh
index 893c95bd6..feb6899cd 100644
--- a/src/libstore/globals.hh
+++ b/src/libstore/globals.hh
@@ -113,7 +113,7 @@ public:
bool verboseBuild = true;
Setting<size_t> logLines{this, 10, "log-lines",
- "If `verbose-build` is false, the number of lines of the tail of "
+ "The number of lines of the tail of "
"the log to show if a build fails."};
MaxBuildJobsSetting maxBuildJobs{
@@ -880,55 +880,6 @@ public:
are loaded as plugins (non-recursively).
)"};
- Setting<StringMap> accessTokens{this, {}, "access-tokens",
- R"(
- Access tokens used to access protected GitHub, GitLab, or
- other locations requiring token-based authentication.
-
- Access tokens are specified as a string made up of
- space-separated `host=token` values. The specific token
- used is selected by matching the `host` portion against the
- "host" specification of the input. The actual use of the
- `token` value is determined by the type of resource being
- accessed:
-
- * Github: the token value is the OAUTH-TOKEN string obtained
- as the Personal Access Token from the Github server (see
- https://docs.github.com/en/developers/apps/authorizing-oath-apps).
-
- * Gitlab: the token value is either the OAuth2 token or the
- Personal Access Token (these are different types tokens
- for gitlab, see
- https://docs.gitlab.com/12.10/ee/api/README.html#authentication).
- The `token` value should be `type:tokenstring` where
- `type` is either `OAuth2` or `PAT` to indicate which type
- of token is being specified.
-
- Example `~/.config/nix/nix.conf`:
-
- ```
- access-tokens = github.com=23ac...b289 gitlab.mycompany.com=PAT:A123Bp_Cd..EfG gitlab.com=OAuth2:1jklw3jk
- ```
-
- Example `~/code/flake.nix`:
-
- ```nix
- input.foo = {
- type = "gitlab";
- host = "gitlab.mycompany.com";
- owner = "mycompany";
- repo = "pro";
- };
- ```
-
- This example specifies three tokens, one each for accessing
- github.com, gitlab.mycompany.com, and sourceforge.net.
-
- The `input.foo` uses the "gitlab" fetcher, which might
- requires specifying the token type along with the token
- value.
- )"};
-
Setting<std::set<ExperimentalFeature>> experimentalFeatures{this, {}, "experimental-features",
"Experimental Nix features to enable."};
@@ -936,18 +887,9 @@ public:
void requireExperimentalFeature(const ExperimentalFeature &);
- Setting<bool> allowDirty{this, true, "allow-dirty",
- "Whether to allow dirty Git/Mercurial trees."};
-
- Setting<bool> warnDirty{this, true, "warn-dirty",
- "Whether to warn about dirty Git/Mercurial trees."};
-
Setting<size_t> narBufferSize{this, 32 * 1024 * 1024, "nar-buffer-size",
"Maximum size of NARs before spilling them to disk."};
- Setting<std::string> flakeRegistry{this, "https://github.com/NixOS/flake-registry/raw/master/flake-registry.json", "flake-registry",
- "Path or URI of the global flake registry."};
-
Setting<bool> allowSymlinkedStore{
this, false, "allow-symlinked-store",
R"(
@@ -960,19 +902,6 @@ public:
resolves to a different location from that of the build machine. You
can enable this setting if you are sure you're not going to do that.
)"};
-
- Setting<bool> useRegistries{this, true, "use-registries",
- "Whether to use flake registries to resolve flake references."};
-
- Setting<bool> acceptFlakeConfig{this, false, "accept-flake-config",
- "Whether to accept nix configuration from a flake without prompting."};
-
- Setting<std::string> commitLockFileSummary{
- this, "", "commit-lockfile-summary",
- R"(
- The commit summary to use when committing changed flake lock files. If
- empty, the summary is generated based on the action performed.
- )"};
};
@@ -988,6 +917,6 @@ void loadConfFile();
// Used by the Settings constructor
std::vector<Path> getUserConfigFiles();
-extern const string nixVersion;
+extern const std::string nixVersion;
}
diff --git a/src/libstore/legacy-ssh-store.cc b/src/libstore/legacy-ssh-store.cc
index f8b2662af..dd34b19c6 100644
--- a/src/libstore/legacy-ssh-store.cc
+++ b/src/libstore/legacy-ssh-store.cc
@@ -2,6 +2,7 @@
#include "pool.hh"
#include "remote-store.hh"
#include "serve-protocol.hh"
+#include "build-result.hh"
#include "store-api.hh"
#include "path-with-outputs.hh"
#include "worker-protocol.hh"
@@ -48,7 +49,7 @@ struct LegacySSHStore : public virtual LegacySSHStoreConfig, public virtual Stor
static std::set<std::string> uriSchemes() { return {"ssh"}; }
- LegacySSHStore(const string & scheme, const string & host, const Params & params)
+ LegacySSHStore(const std::string & scheme, const std::string & host, const Params & params)
: StoreConfig(params)
, LegacySSHStoreConfig(params)
, Store(params)
@@ -107,7 +108,7 @@ struct LegacySSHStore : public virtual LegacySSHStoreConfig, public virtual Stor
return conn;
};
- string getUri() override
+ std::string getUri() override
{
return *uriSchemes().begin() + "://" + host;
}
@@ -225,13 +226,21 @@ struct LegacySSHStore : public virtual LegacySSHStoreConfig, public virtual Stor
std::optional<StorePath> queryPathFromHashPart(const std::string & hashPart) override
{ unsupported("queryPathFromHashPart"); }
- StorePath addToStore(const string & name, const Path & srcPath,
- FileIngestionMethod method, HashType hashAlgo,
- PathFilter & filter, RepairFlag repair, const StorePathSet & references) override
+ StorePath addToStore(
+ std::string_view name,
+ const Path & srcPath,
+ FileIngestionMethod method,
+ HashType hashAlgo,
+ PathFilter & filter,
+ RepairFlag repair,
+ const StorePathSet & references) override
{ unsupported("addToStore"); }
- StorePath addTextToStore(const string & name, const string & s,
- const StorePathSet & references, RepairFlag repair) override
+ StorePath addTextToStore(
+ std::string_view name,
+ std::string_view s,
+ const StorePathSet & references,
+ RepairFlag repair) override
{ unsupported("addTextToStore"); }
private:
@@ -270,7 +279,7 @@ public:
conn->to.flush();
- BuildResult status;
+ BuildResult status { .path = DerivedPath::Built { .drvPath = drvPath } };
status.status = (BuildResult::Status) readInt(conn->from);
conn->from >> status.errorMsg;
@@ -308,7 +317,7 @@ public:
conn->to.flush();
- BuildResult result;
+ BuildResult result { .path = DerivedPath::Opaque { StorePath::dummy } };
result.status = (BuildResult::Status) readInt(conn->from);
if (!result.success()) {
diff --git a/src/libstore/local-fs-store.cc b/src/libstore/local-fs-store.cc
index c933251db..c5ae7536f 100644
--- a/src/libstore/local-fs-store.cc
+++ b/src/libstore/local-fs-store.cc
@@ -85,7 +85,7 @@ void LocalFSStore::narFromPath(const StorePath & path, Sink & sink)
dumpPath(getRealStoreDir() + std::string(printStorePath(path), storeDir.size()), sink);
}
-const string LocalFSStore::drvsLogDir = "drvs";
+const std::string LocalFSStore::drvsLogDir = "drvs";
std::optional<std::string> LocalFSStore::getBuildLog(const StorePath & path_)
{
diff --git a/src/libstore/local-fs-store.hh b/src/libstore/local-fs-store.hh
index e44b27cc2..e6fb3201a 100644
--- a/src/libstore/local-fs-store.hh
+++ b/src/libstore/local-fs-store.hh
@@ -1,6 +1,8 @@
#pragma once
#include "store-api.hh"
+#include "gc-store.hh"
+#include "log-store.hh"
namespace nix {
@@ -23,11 +25,14 @@ struct LocalFSStoreConfig : virtual StoreConfig
"physical path to the Nix store"};
};
-class LocalFSStore : public virtual LocalFSStoreConfig, public virtual Store
+class LocalFSStore : public virtual LocalFSStoreConfig,
+ public virtual Store,
+ public virtual GcStore,
+ public virtual LogStore
{
public:
- const static string drvsLogDir;
+ const static std::string drvsLogDir;
LocalFSStore(const Params & params);
diff --git a/src/libstore/local-store.cc b/src/libstore/local-store.cc
index 284e385e6..d77fff963 100644
--- a/src/libstore/local-store.cc
+++ b/src/libstore/local-store.cc
@@ -70,7 +70,7 @@ int getSchema(Path schemaPath)
{
int curSchema = 0;
if (pathExists(schemaPath)) {
- string s = readFile(schemaPath);
+ auto s = readFile(schemaPath);
auto n = string2Int<int>(s);
if (!n)
throw Error("'%1%' is corrupt", schemaPath);
@@ -239,7 +239,7 @@ LocalStore::LocalStore(const Params & params)
res = posix_fallocate(fd.get(), 0, settings.reservedSize);
#endif
if (res == -1) {
- writeFull(fd.get(), string(settings.reservedSize, 'X'));
+ writeFull(fd.get(), std::string(settings.reservedSize, 'X'));
[[gnu::unused]] auto res2 = ftruncate(fd.get(), settings.reservedSize);
}
}
@@ -450,7 +450,7 @@ void LocalStore::openDB(State & state, bool create)
throw SysError("Nix database directory '%1%' is not writable", dbDir);
/* Open the Nix database. */
- string dbPath = dbDir + "/db.sqlite";
+ std::string dbPath = dbDir + "/db.sqlite";
auto & db(state.db);
state.db = SQLite(dbPath, create);
@@ -471,19 +471,19 @@ void LocalStore::openDB(State & state, bool create)
should be safe enough. If the user asks for it, don't sync at
all. This can cause database corruption if the system
crashes. */
- string syncMode = settings.fsyncMetadata ? "normal" : "off";
+ std::string syncMode = settings.fsyncMetadata ? "normal" : "off";
db.exec("pragma synchronous = " + syncMode);
/* Set the SQLite journal mode. WAL mode is fastest, so it's the
default. */
- string mode = settings.useSQLiteWAL ? "wal" : "truncate";
- string prevMode;
+ std::string mode = settings.useSQLiteWAL ? "wal" : "truncate";
+ std::string prevMode;
{
SQLiteStmt stmt;
stmt.create(db, "pragma main.journal_mode;");
if (sqlite3_step(stmt) != SQLITE_ROW)
throwSQLiteError(db, "querying journal mode");
- prevMode = string((const char *) sqlite3_column_text(stmt, 0));
+ prevMode = std::string((const char *) sqlite3_column_text(stmt, 0));
}
if (prevMode != mode &&
sqlite3_exec(db, ("pragma main.journal_mode = " + mode + ";").c_str(), 0, 0, 0) != SQLITE_OK)
@@ -679,7 +679,7 @@ void LocalStore::checkDerivationOutputs(const StorePath & drvPath, const Derivat
{
assert(drvPath.isDerivation());
std::string drvName(drvPath.name());
- drvName = string(drvName, 0, drvName.size() - drvExtension.size());
+ drvName = drvName.substr(0, drvName.size() - drvExtension.size());
auto envHasRightPath = [&](const StorePath & actual, const std::string & varName)
{
@@ -695,31 +695,34 @@ void LocalStore::checkDerivationOutputs(const StorePath & drvPath, const Derivat
// combinations that are currently prohibited.
drv.type();
- std::optional<Hash> h;
+ std::optional<DrvHash> hashesModulo;
for (auto & i : drv.outputs) {
std::visit(overloaded {
- [&](const DerivationOutputInputAddressed & doia) {
- if (!h) {
+ [&](const DerivationOutput::InputAddressed & doia) {
+ if (!hashesModulo) {
// somewhat expensive so we do lazily
- auto temp = hashDerivationModulo(*this, drv, true);
- h = std::get<Hash>(temp);
+ hashesModulo = hashDerivationModulo(*this, drv, true);
}
- StorePath recomputed = makeOutputPath(i.first, *h, drvName);
+ StorePath recomputed = makeOutputPath(i.first, hashesModulo->hashes.at(i.first), drvName);
if (doia.path != recomputed)
throw Error("derivation '%s' has incorrect output '%s', should be '%s'",
printStorePath(drvPath), printStorePath(doia.path), printStorePath(recomputed));
envHasRightPath(doia.path, i.first);
},
- [&](const DerivationOutputCAFixed & dof) {
+ [&](const DerivationOutput::CAFixed & dof) {
StorePath path = makeFixedOutputPath(dof.hash.method, dof.hash.hash, drvName);
envHasRightPath(path, i.first);
},
- [&](const DerivationOutputCAFloating &) {
+ [&](const DerivationOutput::CAFloating &) {
/* Nothing to check */
},
- [&](const DerivationOutputDeferred &) {
+ [&](const DerivationOutput::Deferred &) {
+ /* Nothing to check */
+ },
+ [&](const DerivationOutput::Impure &) {
+ /* Nothing to check */
},
- }, i.second.output);
+ }, i.second.raw());
}
}
@@ -786,7 +789,11 @@ void LocalStore::registerDrvOutput(const Realisation & info)
});
}
-void LocalStore::cacheDrvOutputMapping(State & state, const uint64_t deriver, const string & outputName, const StorePath & output)
+void LocalStore::cacheDrvOutputMapping(
+ State & state,
+ const uint64_t deriver,
+ const std::string & outputName,
+ const StorePath & output)
{
retrySQLite<void>([&]() {
state.stmts->AddDerivationOutput.use()
@@ -795,7 +802,6 @@ void LocalStore::cacheDrvOutputMapping(State & state, const uint64_t deriver, co
(printStorePath(output))
.exec();
});
-
}
@@ -1318,7 +1324,7 @@ void LocalStore::addToStore(const ValidPathInfo & info, Source & source,
}
-StorePath LocalStore::addToStoreFromDump(Source & source0, const string & name,
+StorePath LocalStore::addToStoreFromDump(Source & source0, std::string_view name,
FileIngestionMethod method, HashType hashAlgo, RepairFlag repair, const StorePathSet & references)
{
/* For computing the store path. */
@@ -1436,7 +1442,9 @@ StorePath LocalStore::addToStoreFromDump(Source & source0, const string & name,
}
-StorePath LocalStore::addTextToStore(const string & name, const string & s,
+StorePath LocalStore::addTextToStore(
+ std::string_view name,
+ std::string_view s,
const StorePathSet & references, RepairFlag repair)
{
auto hash = hashString(htSHA256, s);
@@ -1548,7 +1556,7 @@ bool LocalStore::verifyStore(bool checkContents, RepairFlag repair)
for (auto & link : readDirectory(linksDir)) {
printMsg(lvlTalkative, "checking contents of '%s'", link.name);
Path linkPath = linksDir + "/" + link.name;
- string hash = hashPath(htSHA256, linkPath).first.to_string(Base32, false);
+ std::string hash = hashPath(htSHA256, linkPath).first.to_string(Base32, false);
if (hash != link.name) {
printError("link '%s' was modified! expected hash '%s', got '%s'",
linkPath, link.name, hash);
diff --git a/src/libstore/local-store.hh b/src/libstore/local-store.hh
index 8cf9c68b3..70d225be3 100644
--- a/src/libstore/local-store.hh
+++ b/src/libstore/local-store.hh
@@ -5,6 +5,7 @@
#include "pathlocks.hh"
#include "store-api.hh"
#include "local-fs-store.hh"
+#include "gc-store.hh"
#include "sync.hh"
#include "util.hh"
@@ -43,7 +44,7 @@ struct LocalStoreConfig : virtual LocalFSStoreConfig
};
-class LocalStore : public virtual LocalStoreConfig, public virtual LocalFSStore
+class LocalStore : public virtual LocalStoreConfig, public virtual LocalFSStore, public virtual GcStore
{
private:
@@ -144,11 +145,14 @@ public:
void addToStore(const ValidPathInfo & info, Source & source,
RepairFlag repair, CheckSigsFlag checkSigs) override;
- StorePath addToStoreFromDump(Source & dump, const string & name,
+ StorePath addToStoreFromDump(Source & dump, std::string_view name,
FileIngestionMethod method, HashType hashAlgo, RepairFlag repair, const StorePathSet & references) override;
- StorePath addTextToStore(const string & name, const string & s,
- const StorePathSet & references, RepairFlag repair) override;
+ StorePath addTextToStore(
+ std::string_view name,
+ std::string_view s,
+ const StorePathSet & references,
+ RepairFlag repair) override;
void addTempRoot(const StorePath & path) override;
@@ -204,7 +208,11 @@ public:
derivation 'deriver'. */
void registerDrvOutput(const Realisation & info) override;
void registerDrvOutput(const Realisation & info, CheckSigsFlag checkSigs) override;
- void cacheDrvOutputMapping(State & state, const uint64_t deriver, const string & outputName, const StorePath & output);
+ void cacheDrvOutputMapping(
+ State & state,
+ const uint64_t deriver,
+ const std::string & outputName,
+ const StorePath & output);
std::optional<const Realisation> queryRealisation_(State & state, const DrvOutput & id);
std::optional<std::pair<int64_t, Realisation>> queryRealisationCore_(State & state, const DrvOutput & id);
@@ -292,7 +300,7 @@ private:
typedef std::pair<dev_t, ino_t> Inode;
-typedef set<Inode> InodesSeen;
+typedef std::set<Inode> InodesSeen;
/* "Fix", or canonicalise, the meta-data of the files in a store path
diff --git a/src/libstore/lock.hh b/src/libstore/lock.hh
index 8fbb67ddc..3d29a7b5b 100644
--- a/src/libstore/lock.hh
+++ b/src/libstore/lock.hh
@@ -13,7 +13,7 @@ private:
AutoCloseFD fdUserLock;
bool isEnabled = false;
- string user;
+ std::string user;
uid_t uid = 0;
gid_t gid = 0;
std::vector<gid_t> supplementaryGIDs;
@@ -23,7 +23,7 @@ public:
void kill();
- string getUser() { return user; }
+ std::string getUser() { return user; }
uid_t getUID() { assert(uid); return uid; }
uid_t getGID() { assert(gid); return gid; }
std::vector<gid_t> getSupplementaryGIDs() { return supplementaryGIDs; }
diff --git a/src/libstore/log-store.hh b/src/libstore/log-store.hh
new file mode 100644
index 000000000..ff1b92e17
--- /dev/null
+++ b/src/libstore/log-store.hh
@@ -0,0 +1,21 @@
+#pragma once
+
+#include "store-api.hh"
+
+
+namespace nix {
+
+struct LogStore : public virtual Store
+{
+ inline static std::string operationName = "Build log storage and retrieval";
+
+ /* Return the build log of the specified store path, if available,
+ or null otherwise. */
+ virtual std::optional<std::string> getBuildLog(const StorePath & path) = 0;
+
+ virtual void addBuildLog(const StorePath & path, std::string_view log) = 0;
+
+ static LogStore & require(Store & store);
+};
+
+}
diff --git a/src/libstore/machines.cc b/src/libstore/machines.cc
index b6270a81b..e87f46980 100644
--- a/src/libstore/machines.cc
+++ b/src/libstore/machines.cc
@@ -39,19 +39,19 @@ Machine::Machine(decltype(storeUri) storeUri,
sshPublicHostKey(sshPublicHostKey)
{}
-bool Machine::allSupported(const std::set<string> & features) const
+bool Machine::allSupported(const std::set<std::string> & features) const
{
return std::all_of(features.begin(), features.end(),
- [&](const string & feature) {
+ [&](const std::string & feature) {
return supportedFeatures.count(feature) ||
mandatoryFeatures.count(feature);
});
}
-bool Machine::mandatoryMet(const std::set<string> & features) const
+bool Machine::mandatoryMet(const std::set<std::string> & features) const
{
return std::all_of(mandatoryFeatures.begin(), mandatoryFeatures.end(),
- [&](const string & feature) {
+ [&](const std::string & feature) {
return features.count(feature);
});
}
@@ -89,7 +89,7 @@ ref<Store> Machine::openStore() const
static std::vector<std::string> expandBuilderLines(const std::string & builders)
{
std::vector<std::string> result;
- for (auto line : tokenizeString<std::vector<string>>(builders, "\n;")) {
+ for (auto line : tokenizeString<std::vector<std::string>>(builders, "\n;")) {
trim(line);
line.erase(std::find(line.begin(), line.end(), '#'), line.end());
if (line.empty()) continue;
@@ -117,7 +117,7 @@ static std::vector<std::string> expandBuilderLines(const std::string & builders)
static Machine parseBuilderLine(const std::string & line)
{
- const auto tokens = tokenizeString<std::vector<string>>(line);
+ const auto tokens = tokenizeString<std::vector<std::string>>(line);
auto isSet = [&](size_t fieldIndex) {
return tokens.size() > fieldIndex && tokens[fieldIndex] != "" && tokens[fieldIndex] != "-";
@@ -146,17 +146,18 @@ static Machine parseBuilderLine(const std::string & line)
return {
tokens[0],
- isSet(1) ? tokenizeString<std::vector<string>>(tokens[1], ",") : std::vector<string>{settings.thisSystem},
+ isSet(1) ? tokenizeString<std::vector<std::string>>(tokens[1], ",") : std::vector<std::string>{settings.thisSystem},
isSet(2) ? tokens[2] : "",
isSet(3) ? parseUnsignedIntField(3) : 1U,
isSet(4) ? parseUnsignedIntField(4) : 1U,
- isSet(5) ? tokenizeString<std::set<string>>(tokens[5], ",") : std::set<string>{},
- isSet(6) ? tokenizeString<std::set<string>>(tokens[6], ",") : std::set<string>{},
+ isSet(5) ? tokenizeString<std::set<std::string>>(tokens[5], ",") : std::set<std::string>{},
+ isSet(6) ? tokenizeString<std::set<std::string>>(tokens[6], ",") : std::set<std::string>{},
isSet(7) ? ensureBase64(7) : ""
};
}
-static Machines parseBuilderLines(const std::vector<std::string>& builders) {
+static Machines parseBuilderLines(const std::vector<std::string> & builders)
+{
Machines result;
std::transform(builders.begin(), builders.end(), std::back_inserter(result), parseBuilderLine);
return result;
diff --git a/src/libstore/machines.hh b/src/libstore/machines.hh
index 341d9bd97..834626de9 100644
--- a/src/libstore/machines.hh
+++ b/src/libstore/machines.hh
@@ -8,19 +8,19 @@ class Store;
struct Machine {
- const string storeUri;
- const std::vector<string> systemTypes;
- const string sshKey;
+ const std::string storeUri;
+ const std::vector<std::string> systemTypes;
+ const std::string sshKey;
const unsigned int maxJobs;
const unsigned int speedFactor;
- const std::set<string> supportedFeatures;
- const std::set<string> mandatoryFeatures;
+ const std::set<std::string> supportedFeatures;
+ const std::set<std::string> mandatoryFeatures;
const std::string sshPublicHostKey;
bool enabled = true;
- bool allSupported(const std::set<string> & features) const;
+ bool allSupported(const std::set<std::string> & features) const;
- bool mandatoryMet(const std::set<string> & features) const;
+ bool mandatoryMet(const std::set<std::string> & features) const;
Machine(decltype(storeUri) storeUri,
decltype(systemTypes) systemTypes,
diff --git a/src/libstore/make-content-addressed.cc b/src/libstore/make-content-addressed.cc
new file mode 100644
index 000000000..64d172918
--- /dev/null
+++ b/src/libstore/make-content-addressed.cc
@@ -0,0 +1,80 @@
+#include "make-content-addressed.hh"
+#include "references.hh"
+
+namespace nix {
+
+std::map<StorePath, StorePath> makeContentAddressed(
+ Store & srcStore,
+ Store & dstStore,
+ const StorePathSet & storePaths)
+{
+ StorePathSet closure;
+ srcStore.computeFSClosure(storePaths, closure);
+
+ auto paths = srcStore.topoSortPaths(closure);
+
+ std::reverse(paths.begin(), paths.end());
+
+ std::map<StorePath, StorePath> remappings;
+
+ for (auto & path : paths) {
+ auto pathS = srcStore.printStorePath(path);
+ auto oldInfo = srcStore.queryPathInfo(path);
+ std::string oldHashPart(path.hashPart());
+
+ StringSink sink;
+ srcStore.narFromPath(path, sink);
+
+ StringMap rewrites;
+
+ StorePathSet references;
+ bool hasSelfReference = false;
+ for (auto & ref : oldInfo->references) {
+ if (ref == path)
+ hasSelfReference = true;
+ else {
+ auto i = remappings.find(ref);
+ auto replacement = i != remappings.end() ? i->second : ref;
+ // FIXME: warn about unremapped paths?
+ if (replacement != ref)
+ rewrites.insert_or_assign(srcStore.printStorePath(ref), srcStore.printStorePath(replacement));
+ references.insert(std::move(replacement));
+ }
+ }
+
+ sink.s = rewriteStrings(sink.s, rewrites);
+
+ HashModuloSink hashModuloSink(htSHA256, oldHashPart);
+ hashModuloSink(sink.s);
+
+ auto narModuloHash = hashModuloSink.finish().first;
+
+ auto dstPath = dstStore.makeFixedOutputPath(
+ FileIngestionMethod::Recursive, narModuloHash, path.name(), references, hasSelfReference);
+
+ printInfo("rewriting '%s' to '%s'", pathS, srcStore.printStorePath(dstPath));
+
+ StringSink sink2;
+ RewritingSink rsink2(oldHashPart, std::string(dstPath.hashPart()), sink2);
+ rsink2(sink.s);
+ rsink2.flush();
+
+ ValidPathInfo info { dstPath, hashString(htSHA256, sink2.s) };
+ info.references = std::move(references);
+ if (hasSelfReference) info.references.insert(info.path);
+ info.narSize = sink.s.size();
+ info.ca = FixedOutputHash {
+ .method = FileIngestionMethod::Recursive,
+ .hash = narModuloHash,
+ };
+
+ StringSource source(sink2.s);
+ dstStore.addToStore(info, source);
+
+ remappings.insert_or_assign(std::move(path), std::move(info.path));
+ }
+
+ return remappings;
+}
+
+}
diff --git a/src/libstore/make-content-addressed.hh b/src/libstore/make-content-addressed.hh
new file mode 100644
index 000000000..c4a66ed41
--- /dev/null
+++ b/src/libstore/make-content-addressed.hh
@@ -0,0 +1,12 @@
+#pragma once
+
+#include "store-api.hh"
+
+namespace nix {
+
+std::map<StorePath, StorePath> makeContentAddressed(
+ Store & srcStore,
+ Store & dstStore,
+ const StorePathSet & storePaths);
+
+}
diff --git a/src/libstore/misc.cc b/src/libstore/misc.cc
index 6409874ff..2bbd7aa70 100644
--- a/src/libstore/misc.cc
+++ b/src/libstore/misc.cc
@@ -87,7 +87,7 @@ std::optional<ContentAddress> getDerivationCA(const BasicDerivation & drv)
{
auto out = drv.outputs.find("out");
if (out != drv.outputs.end()) {
- if (auto v = std::get_if<DerivationOutputCAFixed>(&out->second.output))
+ if (const auto * v = std::get_if<DerivationOutput::CAFixed>(&out->second.raw()))
return v->hash;
}
return std::nullopt;
@@ -277,15 +277,15 @@ std::map<DrvOutput, StorePath> drvOutputReferences(
{
std::set<Realisation> inputRealisations;
- for (const auto& [inputDrv, outputNames] : drv.inputDrvs) {
+ for (const auto & [inputDrv, outputNames] : drv.inputDrvs) {
auto outputHashes =
staticOutputHashes(store, store.readDerivation(inputDrv));
- for (const auto& outputName : outputNames) {
+ for (const auto & outputName : outputNames) {
auto thisRealisation = store.queryRealisation(
DrvOutput{outputHashes.at(outputName), outputName});
if (!thisRealisation)
throw Error(
- "output '%s' of derivation '%s' isn’t built", outputName,
+ "output '%s' of derivation '%s' isn't built", outputName,
store.printStorePath(inputDrv));
inputRealisations.insert(*thisRealisation);
}
@@ -295,4 +295,5 @@ std::map<DrvOutput, StorePath> drvOutputReferences(
return drvOutputReferences(Realisation::closure(store, inputRealisations), info->references);
}
+
}
diff --git a/src/libstore/names.hh b/src/libstore/names.hh
index 6f01fe2a1..3977fc6cc 100644
--- a/src/libstore/names.hh
+++ b/src/libstore/names.hh
@@ -10,9 +10,9 @@ struct Regex;
struct DrvName
{
- string fullName;
- string name;
- string version;
+ std::string fullName;
+ std::string name;
+ std::string version;
unsigned int hits;
DrvName();
@@ -25,7 +25,7 @@ private:
std::unique_ptr<Regex> regex;
};
-typedef list<DrvName> DrvNames;
+typedef std::list<DrvName> DrvNames;
std::string_view nextComponent(std::string_view::const_iterator & p,
const std::string_view::const_iterator end);
diff --git a/src/libstore/nar-accessor.cc b/src/libstore/nar-accessor.cc
index 7d27d7667..72d41cc94 100644
--- a/src/libstore/nar-accessor.cc
+++ b/src/libstore/nar-accessor.cc
@@ -90,7 +90,7 @@ struct NarAccessor : public FSAccessor
void receiveContents(std::string_view data) override
{ }
- void createSymlink(const Path & path, const string & target) override
+ void createSymlink(const Path & path, const std::string & target) override
{
createMember(path,
NarMember{FSAccessor::Type::tSymlink, false, 0, 0, target});
diff --git a/src/libstore/nar-info.cc b/src/libstore/nar-info.cc
index 49079388a..2d75e7a82 100644
--- a/src/libstore/nar-info.cc
+++ b/src/libstore/nar-info.cc
@@ -11,7 +11,7 @@ NarInfo::NarInfo(const Store & store, const std::string & s, const std::string &
return Error("NAR info file '%1%' is corrupt", whence);
};
- auto parseHashField = [&](const string & s) {
+ auto parseHashField = [&](const std::string & s) {
try {
return Hash::parseAnyPrefixed(s);
} catch (BadHash &) {
diff --git a/src/libstore/optimise-store.cc b/src/libstore/optimise-store.cc
index 13cb142f8..8af9b1dde 100644
--- a/src/libstore/optimise-store.cc
+++ b/src/libstore/optimise-store.cc
@@ -77,7 +77,7 @@ Strings LocalStore::readDirectoryIgnoringInodes(const Path & path, const InodeHa
continue;
}
- string name = dirent->d_name;
+ std::string name = dirent->d_name;
if (name == "." || name == "..") continue;
names.push_back(name);
}
diff --git a/src/libstore/parsed-derivations.cc b/src/libstore/parsed-derivations.cc
index 8c65053e4..f2288a04e 100644
--- a/src/libstore/parsed-derivations.cc
+++ b/src/libstore/parsed-derivations.cc
@@ -93,7 +93,7 @@ StringSet ParsedDerivation::getRequiredSystemFeatures() const
StringSet res;
for (auto & i : getStringsAttr("requiredSystemFeatures").value_or(Strings()))
res.insert(i);
- if (!derivationHasKnownOutputPaths(drv.type()))
+ if (!drv.type().hasKnownOutputPaths())
res.insert("ca-derivations");
return res;
}
diff --git a/src/libstore/parsed-derivations.hh b/src/libstore/parsed-derivations.hh
index effcf099d..95bec21e8 100644
--- a/src/libstore/parsed-derivations.hh
+++ b/src/libstore/parsed-derivations.hh
@@ -1,5 +1,6 @@
#pragma once
+#include "derivations.hh"
#include "store-api.hh"
#include <nlohmann/json_fwd.hpp>
diff --git a/src/libstore/path-with-outputs.cc b/src/libstore/path-with-outputs.cc
index e5a121e00..078c117bd 100644
--- a/src/libstore/path-with-outputs.cc
+++ b/src/libstore/path-with-outputs.cc
@@ -22,9 +22,9 @@ DerivedPath StorePathWithOutputs::toDerivedPath() const
std::vector<DerivedPath> toDerivedPaths(const std::vector<StorePathWithOutputs> ss)
{
- std::vector<DerivedPath> reqs;
- for (auto & s : ss) reqs.push_back(s.toDerivedPath());
- return reqs;
+ std::vector<DerivedPath> reqs;
+ for (auto & s : ss) reqs.push_back(s.toDerivedPath());
+ return reqs;
}
@@ -49,9 +49,9 @@ std::pair<std::string_view, StringSet> parsePathWithOutputs(std::string_view s)
{
size_t n = s.find("!");
return n == s.npos
- ? std::make_pair(s, std::set<string>())
+ ? std::make_pair(s, std::set<std::string>())
: std::make_pair(((std::string_view) s).substr(0, n),
- tokenizeString<std::set<string>>(((std::string_view) s).substr(n + 1), ","));
+ tokenizeString<std::set<std::string>>(((std::string_view) s).substr(n + 1), ","));
}
diff --git a/src/libstore/path.cc b/src/libstore/path.cc
index e642abcd5..392db225e 100644
--- a/src/libstore/path.cc
+++ b/src/libstore/path.cc
@@ -1,5 +1,7 @@
#include "store-api.hh"
+#include <sodium.h>
+
namespace nix {
static void checkName(std::string_view path, std::string_view name)
@@ -41,6 +43,13 @@ bool StorePath::isDerivation() const
StorePath StorePath::dummy("ffffffffffffffffffffffffffffffff-x");
+StorePath StorePath::random(std::string_view name)
+{
+ Hash hash(htSHA1);
+ randombytes_buf(hash.hash, hash.hashSize);
+ return StorePath(hash, name);
+}
+
StorePath Store::parseStorePath(std::string_view path) const
{
auto p = canonPath(std::string(path));
diff --git a/src/libstore/path.hh b/src/libstore/path.hh
index 06ba0663b..77fd0f8dc 100644
--- a/src/libstore/path.hh
+++ b/src/libstore/path.hh
@@ -58,11 +58,13 @@ public:
}
static StorePath dummy;
+
+ static StorePath random(std::string_view name);
};
typedef std::set<StorePath> StorePathSet;
typedef std::vector<StorePath> StorePaths;
-typedef std::map<string, StorePath> OutputPathMap;
+typedef std::map<std::string, StorePath> OutputPathMap;
typedef std::map<StorePath, std::optional<ContentAddress>> StorePathCAMap;
diff --git a/src/libstore/pathlocks.cc b/src/libstore/pathlocks.cc
index 2da74e262..42023cd0a 100644
--- a/src/libstore/pathlocks.cc
+++ b/src/libstore/pathlocks.cc
@@ -74,7 +74,7 @@ PathLocks::PathLocks()
}
-PathLocks::PathLocks(const PathSet & paths, const string & waitMsg)
+PathLocks::PathLocks(const PathSet & paths, const std::string & waitMsg)
: deletePaths(false)
{
lockPaths(paths, waitMsg);
@@ -82,7 +82,7 @@ PathLocks::PathLocks(const PathSet & paths, const string & waitMsg)
bool PathLocks::lockPaths(const PathSet & paths,
- const string & waitMsg, bool wait)
+ const std::string & waitMsg, bool wait)
{
assert(fds.empty());
diff --git a/src/libstore/pathlocks.hh b/src/libstore/pathlocks.hh
index 919c8904c..5e3a734b4 100644
--- a/src/libstore/pathlocks.hh
+++ b/src/libstore/pathlocks.hh
@@ -20,15 +20,15 @@ class PathLocks
{
private:
typedef std::pair<int, Path> FDPair;
- list<FDPair> fds;
+ std::list<FDPair> fds;
bool deletePaths;
public:
PathLocks();
PathLocks(const PathSet & paths,
- const string & waitMsg = "");
+ const std::string & waitMsg = "");
bool lockPaths(const PathSet & _paths,
- const string & waitMsg = "",
+ const std::string & waitMsg = "",
bool wait = true);
~PathLocks();
void unlock();
diff --git a/src/libstore/profiles.cc b/src/libstore/profiles.cc
index 73163424c..3e4188188 100644
--- a/src/libstore/profiles.cc
+++ b/src/libstore/profiles.cc
@@ -15,12 +15,12 @@ namespace nix {
/* Parse a generation name of the format
`<profilename>-<number>-link'. */
-static std::optional<GenerationNumber> parseName(const string & profileName, const string & name)
+static std::optional<GenerationNumber> parseName(const std::string & profileName, const std::string & name)
{
- if (string(name, 0, profileName.size() + 1) != profileName + "-") return {};
- string s = string(name, profileName.size() + 1);
- string::size_type p = s.find("-link");
- if (p == string::npos) return {};
+ if (name.substr(0, profileName.size() + 1) != profileName + "-") return {};
+ auto s = name.substr(profileName.size() + 1);
+ auto p = s.find("-link");
+ if (p == std::string::npos) return {};
if (auto n = string2Int<unsigned int>(s.substr(0, p)))
return *n;
else
@@ -209,13 +209,13 @@ void deleteGenerationsOlderThan(const Path & profile, time_t t, bool dryRun)
}
-void deleteGenerationsOlderThan(const Path & profile, const string & timeSpec, bool dryRun)
+void deleteGenerationsOlderThan(const Path & profile, std::string_view timeSpec, bool dryRun)
{
if (timeSpec.empty() || timeSpec[timeSpec.size() - 1] != 'd')
throw UsageError("invalid number of days specifier '%1%', expected something like '14d'", timeSpec);
time_t curTime = time(0);
- string strDays = string(timeSpec, 0, timeSpec.size() - 1);
+ auto strDays = timeSpec.substr(0, timeSpec.size() - 1);
auto days = string2Int<int>(strDays);
if (!days || *days < 1)
@@ -274,7 +274,7 @@ void lockProfile(PathLocks & lock, const Path & profile)
}
-string optimisticLockProfile(const Path & profile)
+std::string optimisticLockProfile(const Path & profile)
{
return pathExists(profile) ? readLink(profile) : "";
}
diff --git a/src/libstore/profiles.hh b/src/libstore/profiles.hh
index d100c970c..408ca039c 100644
--- a/src/libstore/profiles.hh
+++ b/src/libstore/profiles.hh
@@ -42,7 +42,7 @@ void deleteOldGenerations(const Path & profile, bool dryRun);
void deleteGenerationsOlderThan(const Path & profile, time_t t, bool dryRun);
-void deleteGenerationsOlderThan(const Path & profile, const string & timeSpec, bool dryRun);
+void deleteGenerationsOlderThan(const Path & profile, std::string_view timeSpec, bool dryRun);
void switchLink(Path link, Path target);
@@ -66,7 +66,7 @@ void lockProfile(PathLocks & lock, const Path & profile);
generally cheap, since the build results are still in the Nix
store. Most of the time, only the user environment has to be
rebuilt. */
-string optimisticLockProfile(const Path & profile);
+std::string optimisticLockProfile(const Path & profile);
/* Resolve ~/.nix-profile. If ~/.nix-profile doesn't exist yet, create
it. */
diff --git a/src/libstore/references.cc b/src/libstore/references.cc
index 91b3fc142..34dce092c 100644
--- a/src/libstore/references.cc
+++ b/src/libstore/references.cc
@@ -68,7 +68,7 @@ void RefScanSink::operator () (std::string_view data)
std::pair<StorePathSet, HashResult> scanForReferences(
- const string & path,
+ const std::string & path,
const StorePathSet & refs)
{
HashSink hashSink { htSHA256 };
@@ -121,7 +121,7 @@ void RewritingSink::operator () (std::string_view data)
s.append(data);
size_t j = 0;
- while ((j = s.find(from, j)) != string::npos) {
+ while ((j = s.find(from, j)) != std::string::npos) {
matches.push_back(pos + j);
s.replace(j, from.size(), to);
}
diff --git a/src/libstore/remote-store.cc b/src/libstore/remote-store.cc
index 573becfbd..347e32094 100644
--- a/src/libstore/remote-store.cc
+++ b/src/libstore/remote-store.cc
@@ -1,7 +1,9 @@
#include "serialise.hh"
#include "util.hh"
#include "path-with-outputs.hh"
+#include "gc-store.hh"
#include "remote-fs-accessor.hh"
+#include "build-result.hh"
#include "remote-store.hh"
#include "worker-protocol.hh"
#include "archive.hh"
@@ -89,6 +91,35 @@ void write(const Store & store, Sink & out, const DrvOutput & drvOutput)
}
+BuildResult read(const Store & store, Source & from, Phantom<BuildResult> _)
+{
+ auto path = worker_proto::read(store, from, Phantom<DerivedPath> {});
+ BuildResult res { .path = path };
+ res.status = (BuildResult::Status) readInt(from);
+ from
+ >> res.errorMsg
+ >> res.timesBuilt
+ >> res.isNonDeterministic
+ >> res.startTime
+ >> res.stopTime;
+ res.builtOutputs = worker_proto::read(store, from, Phantom<DrvOutputs> {});
+ return res;
+}
+
+void write(const Store & store, Sink & to, const BuildResult & res)
+{
+ worker_proto::write(store, to, res.path);
+ to
+ << res.status
+ << res.errorMsg
+ << res.timesBuilt
+ << res.isNonDeterministic
+ << res.startTime
+ << res.stopTime;
+ worker_proto::write(store, to, res.builtOutputs);
+}
+
+
std::optional<StorePath> read(const Store & store, Source & from, Phantom<std::optional<StorePath>> _)
{
auto s = readString(from);
@@ -500,7 +531,7 @@ std::optional<StorePath> RemoteStore::queryPathFromHashPart(const std::string &
ref<const ValidPathInfo> RemoteStore::addCAToStore(
Source & dump,
- const string & name,
+ std::string_view name,
ContentAddressMethod caMethod,
const StorePathSet & references,
RepairFlag repair)
@@ -582,7 +613,7 @@ ref<const ValidPathInfo> RemoteStore::addCAToStore(
}
-StorePath RemoteStore::addToStoreFromDump(Source & dump, const string & name,
+StorePath RemoteStore::addToStoreFromDump(Source & dump, std::string_view name,
FileIngestionMethod method, HashType hashType, RepairFlag repair, const StorePathSet & references)
{
return addCAToStore(dump, name, FixedOutputHashMethod{ .fileIngestionMethod = method, .hashType = hashType }, references, repair)->path;
@@ -661,8 +692,11 @@ void RemoteStore::addMultipleToStore(
}
-StorePath RemoteStore::addTextToStore(const string & name, const string & s,
- const StorePathSet & references, RepairFlag repair)
+StorePath RemoteStore::addTextToStore(
+ std::string_view name,
+ std::string_view s,
+ const StorePathSet & references,
+ RepairFlag repair)
{
StringSource source(s);
return addCAToStore(source, name, TextHashMethod{}, references, repair)->path;
@@ -742,17 +776,24 @@ static void writeDerivedPaths(RemoteStore & store, ConnectionHandle & conn, cons
}
}
-void RemoteStore::buildPaths(const std::vector<DerivedPath> & drvPaths, BuildMode buildMode, std::shared_ptr<Store> evalStore)
+void RemoteStore::copyDrvsFromEvalStore(
+ const std::vector<DerivedPath> & paths,
+ std::shared_ptr<Store> evalStore)
{
if (evalStore && evalStore.get() != this) {
/* The remote doesn't have a way to access evalStore, so copy
the .drvs. */
RealisedPath::Set drvPaths2;
- for (auto & i : drvPaths)
+ for (auto & i : paths)
if (auto p = std::get_if<DerivedPath::Built>(&i))
drvPaths2.insert(p->drvPath);
copyClosure(*evalStore, *this, drvPaths2);
}
+}
+
+void RemoteStore::buildPaths(const std::vector<DerivedPath> & drvPaths, BuildMode buildMode, std::shared_ptr<Store> evalStore)
+{
+ copyDrvsFromEvalStore(drvPaths, evalStore);
auto conn(getConnection());
conn->to << wopBuildPaths;
@@ -769,6 +810,91 @@ void RemoteStore::buildPaths(const std::vector<DerivedPath> & drvPaths, BuildMod
readInt(conn->from);
}
+std::vector<BuildResult> RemoteStore::buildPathsWithResults(
+ const std::vector<DerivedPath> & paths,
+ BuildMode buildMode,
+ std::shared_ptr<Store> evalStore)
+{
+ copyDrvsFromEvalStore(paths, evalStore);
+
+ std::optional<ConnectionHandle> conn_(getConnection());
+ auto & conn = *conn_;
+
+ if (GET_PROTOCOL_MINOR(conn->daemonVersion) >= 34) {
+ conn->to << wopBuildPathsWithResults;
+ writeDerivedPaths(*this, conn, paths);
+ conn->to << buildMode;
+ conn.processStderr();
+ return worker_proto::read(*this, conn->from, Phantom<std::vector<BuildResult>> {});
+ } else {
+ // Avoid deadlock.
+ conn_.reset();
+
+ // Note: this throws an exception if a build/substitution
+ // fails, but meh.
+ buildPaths(paths, buildMode, evalStore);
+
+ std::vector<BuildResult> results;
+
+ for (auto & path : paths) {
+ std::visit(
+ overloaded {
+ [&](const DerivedPath::Opaque & bo) {
+ results.push_back(BuildResult {
+ .status = BuildResult::Substituted,
+ .path = bo,
+ });
+ },
+ [&](const DerivedPath::Built & bfd) {
+ BuildResult res {
+ .status = BuildResult::Built,
+ .path = bfd,
+ };
+
+ OutputPathMap outputs;
+ auto drv = evalStore->readDerivation(bfd.drvPath);
+ auto outputHashes = staticOutputHashes(*evalStore, drv); // FIXME: expensive
+ auto drvOutputs = drv.outputsAndOptPaths(*this);
+ for (auto & output : bfd.outputs) {
+ if (!outputHashes.count(output))
+ throw Error(
+ "the derivation '%s' doesn't have an output named '%s'",
+ printStorePath(bfd.drvPath), output);
+ auto outputId =
+ DrvOutput{outputHashes.at(output), output};
+ if (settings.isExperimentalFeatureEnabled(Xp::CaDerivations)) {
+ auto realisation =
+ queryRealisation(outputId);
+ if (!realisation)
+ throw Error(
+ "cannot operate on an output of unbuilt "
+ "content-addressed derivation '%s'",
+ outputId.to_string());
+ res.builtOutputs.emplace(realisation->id, *realisation);
+ } else {
+ // If ca-derivations isn't enabled, assume that
+ // the output path is statically known.
+ assert(drvOutputs.count(output));
+ assert(drvOutputs.at(output).second);
+ res.builtOutputs.emplace(
+ outputId,
+ Realisation {
+ .id = outputId,
+ .outPath = *drvOutputs.at(output).second
+ });
+ }
+ }
+
+ results.push_back(res);
+ }
+ },
+ path.raw());
+ }
+
+ return results;
+ }
+}
+
BuildResult RemoteStore::buildDerivation(const StorePath & drvPath, const BasicDerivation & drv,
BuildMode buildMode)
@@ -778,7 +904,7 @@ BuildResult RemoteStore::buildDerivation(const StorePath & drvPath, const BasicD
writeDerivation(conn->to, *this, drv);
conn->to << buildMode;
conn.processStderr();
- BuildResult res;
+ BuildResult res { .path = DerivedPath::Built { .drvPath = drvPath } };
res.status = (BuildResult::Status) readInt(conn->from);
conn->from >> res.errorMsg;
if (GET_PROTOCOL_MINOR(conn->daemonVersion) >= 29) {
@@ -1000,7 +1126,7 @@ std::exception_ptr RemoteStore::Connection::processStderr(Sink * sink, Source *
auto msg = readNum<uint64_t>(from);
if (msg == STDERR_WRITE) {
- string s = readString(from);
+ auto s = readString(from);
if (!sink) throw Error("no sink");
(*sink)(s);
}
@@ -1017,7 +1143,7 @@ std::exception_ptr RemoteStore::Connection::processStderr(Sink * sink, Source *
if (GET_PROTOCOL_MINOR(daemonVersion) >= 26) {
return std::make_exception_ptr(readError(from));
} else {
- string error = readString(from);
+ auto error = readString(from);
unsigned int status = readInt(from);
return std::make_exception_ptr(Error(status, error));
}
diff --git a/src/libstore/remote-store.hh b/src/libstore/remote-store.hh
index b91d25fa9..8493be6fc 100644
--- a/src/libstore/remote-store.hh
+++ b/src/libstore/remote-store.hh
@@ -4,6 +4,8 @@
#include <string>
#include "store-api.hh"
+#include "gc-store.hh"
+#include "log-store.hh"
namespace nix {
@@ -29,7 +31,10 @@ struct RemoteStoreConfig : virtual StoreConfig
/* FIXME: RemoteStore is a misnomer - should be something like
DaemonStore. */
-class RemoteStore : public virtual RemoteStoreConfig, public virtual Store
+class RemoteStore : public virtual RemoteStoreConfig,
+ public virtual Store,
+ public virtual GcStore,
+ public virtual LogStore
{
public:
@@ -66,13 +71,13 @@ public:
/* Add a content-addressable store path. `dump` will be drained. */
ref<const ValidPathInfo> addCAToStore(
Source & dump,
- const string & name,
+ std::string_view name,
ContentAddressMethod caMethod,
const StorePathSet & references,
RepairFlag repair);
/* Add a content-addressable store path. Does not support references. `dump` will be drained. */
- StorePath addToStoreFromDump(Source & dump, const string & name,
+ StorePath addToStoreFromDump(Source & dump, std::string_view name,
FileIngestionMethod method = FileIngestionMethod::Recursive, HashType hashAlgo = htSHA256, RepairFlag repair = NoRepair, const StorePathSet & references = StorePathSet()) override;
void addToStore(const ValidPathInfo & info, Source & nar,
@@ -83,8 +88,11 @@ public:
RepairFlag repair,
CheckSigsFlag checkSigs) override;
- StorePath addTextToStore(const string & name, const string & s,
- const StorePathSet & references, RepairFlag repair) override;
+ StorePath addTextToStore(
+ std::string_view name,
+ std::string_view s,
+ const StorePathSet & references,
+ RepairFlag repair) override;
void registerDrvOutput(const Realisation & info) override;
@@ -93,6 +101,11 @@ public:
void buildPaths(const std::vector<DerivedPath> & paths, BuildMode buildMode, std::shared_ptr<Store> evalStore) override;
+ std::vector<BuildResult> buildPathsWithResults(
+ const std::vector<DerivedPath> & paths,
+ BuildMode buildMode,
+ std::shared_ptr<Store> evalStore) override;
+
BuildResult buildDerivation(const StorePath & drvPath, const BasicDerivation & drv,
BuildMode buildMode) override;
@@ -167,6 +180,9 @@ private:
std::atomic_bool failed{false};
+ void copyDrvsFromEvalStore(
+ const std::vector<DerivedPath> & paths,
+ std::shared_ptr<Store> evalStore);
};
diff --git a/src/libstore/repair-flag.hh b/src/libstore/repair-flag.hh
new file mode 100644
index 000000000..a13cda312
--- /dev/null
+++ b/src/libstore/repair-flag.hh
@@ -0,0 +1,7 @@
+#pragma once
+
+namespace nix {
+
+enum RepairFlag : bool { NoRepair = false, Repair = true };
+
+}
diff --git a/src/libstore/s3-binary-cache-store.cc b/src/libstore/s3-binary-cache-store.cc
index a024e971d..844553ad3 100644
--- a/src/libstore/s3-binary-cache-store.cc
+++ b/src/libstore/s3-binary-cache-store.cc
@@ -87,7 +87,11 @@ static void initAWS()
});
}
-S3Helper::S3Helper(const string & profile, const string & region, const string & scheme, const string & endpoint)
+S3Helper::S3Helper(
+ const std::string & profile,
+ const std::string & region,
+ const std::string & scheme,
+ const std::string & endpoint)
: config(makeConfig(region, scheme, endpoint))
, client(make_ref<Aws::S3::S3Client>(
profile == ""
@@ -121,7 +125,10 @@ class RetryStrategy : public Aws::Client::DefaultRetryStrategy
}
};
-ref<Aws::Client::ClientConfiguration> S3Helper::makeConfig(const string & region, const string & scheme, const string & endpoint)
+ref<Aws::Client::ClientConfiguration> S3Helper::makeConfig(
+ const std::string & region,
+ const std::string & scheme,
+ const std::string & endpoint)
{
initAWS();
auto res = make_ref<Aws::Client::ClientConfiguration>();
diff --git a/src/libstore/sqlite.cc b/src/libstore/sqlite.cc
index 1d6baf02d..e6ecadd7f 100644
--- a/src/libstore/sqlite.cc
+++ b/src/libstore/sqlite.cc
@@ -71,7 +71,7 @@ uint64_t SQLite::getLastInsertedRowId()
return sqlite3_last_insert_rowid(db);
}
-void SQLiteStmt::create(sqlite3 * db, const string & sql)
+void SQLiteStmt::create(sqlite3 * db, const std::string & sql)
{
checkInterrupt();
assert(!stmt);
diff --git a/src/libstore/ssh-store.cc b/src/libstore/ssh-store.cc
index bb03daef4..62daa838c 100644
--- a/src/libstore/ssh-store.cc
+++ b/src/libstore/ssh-store.cc
@@ -52,6 +52,10 @@ public:
bool sameMachine() override
{ return false; }
+ // FIXME extend daemon protocol, move implementation to RemoteStore
+ std::optional<std::string> getBuildLog(const StorePath & path) override
+ { unsupported("getBuildLog"); }
+
private:
struct Connection : RemoteStore::Connection
diff --git a/src/libstore/ssh.cc b/src/libstore/ssh.cc
index 93f72675d..1bbad71f2 100644
--- a/src/libstore/ssh.cc
+++ b/src/libstore/ssh.cc
@@ -29,7 +29,7 @@ void SSHMaster::addCommonSSHOpts(Strings & args)
if (!sshPublicHostKey.empty()) {
Path fileName = (Path) *state->tmpDir + "/host-key";
auto p = host.rfind("@");
- string thost = p != string::npos ? string(host, p + 1) : host;
+ std::string thost = p != std::string::npos ? std::string(host, p + 1) : host;
writeFile(fileName, thost + " " + base64Decode(sshPublicHostKey) + "\n");
args.insert(args.end(), {"-oUserKnownHostsFile=" + fileName});
}
diff --git a/src/libstore/store-api.cc b/src/libstore/store-api.cc
index 84767e917..59937be4d 100644
--- a/src/libstore/store-api.cc
+++ b/src/libstore/store-api.cc
@@ -1,6 +1,7 @@
#include "crypto.hh"
#include "fs-accessor.hh"
#include "globals.hh"
+#include "derivations.hh"
#include "store-api.hh"
#include "util.hh"
#include "nar-info-disk-cache.hh"
@@ -39,7 +40,7 @@ Path Store::followLinksToStore(std::string_view _path) const
Path path = absPath(std::string(_path));
while (!isInStore(path)) {
if (!isLink(path)) break;
- string target = readLink(path);
+ auto target = readLink(path);
path = absPath(target, dirOf(path));
}
if (!isInStore(path))
@@ -138,8 +139,8 @@ StorePath Store::makeStorePath(std::string_view type,
std::string_view hash, std::string_view name) const
{
/* e.g., "source:sha256:1abc...:/nix/store:foo.tar.gz" */
- string s = std::string { type } + ":" + std::string { hash }
- + ":" + storeDir + ":" + std::string { name };
+ auto s = std::string(type) + ":" + std::string(hash)
+ + ":" + storeDir + ":" + std::string(name);
auto h = compressHash(hashString(htSHA256, s), 20);
return StorePath(h, name);
}
@@ -161,7 +162,7 @@ StorePath Store::makeOutputPath(std::string_view id,
static std::string makeType(
const Store & store,
- string && type,
+ std::string && type,
const StorePathSet & references,
bool hasSelfReference = false)
{
@@ -229,15 +230,23 @@ std::pair<StorePath, Hash> Store::computeStorePathForPath(std::string_view name,
}
-StorePath Store::computeStorePathForText(const string & name, const string & s,
+StorePath Store::computeStorePathForText(
+ std::string_view name,
+ std::string_view s,
const StorePathSet & references) const
{
return makeTextPath(name, hashString(htSHA256, s), references);
}
-StorePath Store::addToStore(const string & name, const Path & _srcPath,
- FileIngestionMethod method, HashType hashAlgo, PathFilter & filter, RepairFlag repair, const StorePathSet & references)
+StorePath Store::addToStore(
+ std::string_view name,
+ const Path & _srcPath,
+ FileIngestionMethod method,
+ HashType hashAlgo,
+ PathFilter & filter,
+ RepairFlag repair,
+ const StorePathSet & references)
{
Path srcPath(absPath(_srcPath));
auto source = sinkToSource([&](Sink & sink) {
@@ -688,10 +697,10 @@ StorePathSet Store::queryValidPaths(const StorePathSet & paths, SubstituteFlag m
/* Return a string accepted by decodeValidPathInfo() that
registers the specified paths as valid. Note: it's the
responsibility of the caller to provide a closure. */
-string Store::makeValidityRegistration(const StorePathSet & paths,
+std::string Store::makeValidityRegistration(const StorePathSet & paths,
bool showDerivers, bool showHash)
{
- string s = "";
+ std::string s = "";
for (auto & i : paths) {
s += printStorePath(i) + "\n";
@@ -761,11 +770,11 @@ void Store::pathInfoToJSON(JSONPlaceholder & jsonOut, const StorePathSet & store
for (auto & storePath : storePaths) {
auto jsonPath = jsonList.object();
- jsonPath.attr("path", printStorePath(storePath));
try {
auto info = queryPathInfo(storePath);
+ jsonPath.attr("path", printStorePath(info->path));
jsonPath
.attr("narHash", info->narHash.to_string(hashBase, true))
.attr("narSize", info->narSize);
@@ -819,6 +828,7 @@ void Store::pathInfoToJSON(JSONPlaceholder & jsonOut, const StorePathSet & store
}
} catch (InvalidPath &) {
+ jsonPath.attr("path", printStorePath(storePath));
jsonPath.attr("valid", false);
}
}
@@ -1130,7 +1140,7 @@ std::optional<ValidPathInfo> decodeValidPathInfo(const Store & store, std::istre
getline(str, path);
if (str.eof()) { return {}; }
if (!hashGiven) {
- string s;
+ std::string s;
getline(str, s);
auto narHash = Hash::parseAny(s, htSHA256);
getline(str, s);
@@ -1143,7 +1153,7 @@ std::optional<ValidPathInfo> decodeValidPathInfo(const Store & store, std::istre
std::string deriver;
getline(str, deriver);
if (deriver != "") info.deriver = store.parseStorePath(deriver);
- string s;
+ std::string s;
getline(str, s);
auto n = string2Int<int>(s);
if (!n) throw Error("number expected");
@@ -1167,7 +1177,7 @@ std::string Store::showPaths(const StorePathSet & paths)
}
-string showPaths(const PathSet & paths)
+std::string showPaths(const PathSet & paths)
{
return concatStringsSep(", ", quoteStrings(paths));
}
diff --git a/src/libstore/store-api.hh b/src/libstore/store-api.hh
index 4068f8f35..0c8a4db56 100644
--- a/src/libstore/store-api.hh
+++ b/src/libstore/store-api.hh
@@ -10,8 +10,8 @@
#include "sync.hh"
#include "globals.hh"
#include "config.hh"
-#include "derivations.hh"
#include "path-info.hh"
+#include "repair-flag.hh"
#include <atomic>
#include <limits>
@@ -62,6 +62,8 @@ MakeError(BadStorePath, Error);
MakeError(InvalidStoreURI, Error);
+struct BasicDerivation;
+struct Derivation;
class FSAccessor;
class NarInfoDiskCache;
class Store;
@@ -76,127 +78,10 @@ enum AllowInvalidFlag : bool { DisallowInvalid = false, AllowInvalid = true };
const uint32_t exportMagic = 0x4558494e;
-typedef std::unordered_map<StorePath, std::unordered_set<std::string>> Roots;
-
-
-struct GCOptions
-{
- /* Garbage collector operation:
-
- - `gcReturnLive': return the set of paths reachable from
- (i.e. in the closure of) the roots.
-
- - `gcReturnDead': return the set of paths not reachable from
- the roots.
-
- - `gcDeleteDead': actually delete the latter set.
-
- - `gcDeleteSpecific': delete the paths listed in
- `pathsToDelete', insofar as they are not reachable.
- */
- typedef enum {
- gcReturnLive,
- gcReturnDead,
- gcDeleteDead,
- gcDeleteSpecific,
- } GCAction;
-
- GCAction action{gcDeleteDead};
-
- /* If `ignoreLiveness' is set, then reachability from the roots is
- ignored (dangerous!). However, the paths must still be
- unreferenced *within* the store (i.e., there can be no other
- store paths that depend on them). */
- bool ignoreLiveness{false};
-
- /* For `gcDeleteSpecific', the paths to delete. */
- StorePathSet pathsToDelete;
-
- /* Stop after at least `maxFreed' bytes have been freed. */
- uint64_t maxFreed{std::numeric_limits<uint64_t>::max()};
-};
-
-
-struct GCResults
-{
- /* Depending on the action, the GC roots, or the paths that would
- be or have been deleted. */
- PathSet paths;
-
- /* For `gcReturnDead', `gcDeleteDead' and `gcDeleteSpecific', the
- number of bytes that would be or was freed. */
- uint64_t bytesFreed = 0;
-};
-
-
enum BuildMode { bmNormal, bmRepair, bmCheck };
+struct BuildResult;
-struct BuildResult
-{
- /* Note: don't remove status codes, and only add new status codes
- at the end of the list, to prevent client/server
- incompatibilities in the nix-store --serve protocol. */
- enum Status {
- Built = 0,
- Substituted,
- AlreadyValid,
- PermanentFailure,
- InputRejected,
- OutputRejected,
- TransientFailure, // possibly transient
- CachedFailure, // no longer used
- TimedOut,
- MiscFailure,
- DependencyFailed,
- LogLimitExceeded,
- NotDeterministic,
- ResolvesToAlreadyValid,
- } status = MiscFailure;
- std::string errorMsg;
-
- std::string toString() const {
- auto strStatus = [&]() {
- switch (status) {
- case Built: return "Built";
- case Substituted: return "Substituted";
- case AlreadyValid: return "AlreadyValid";
- case PermanentFailure: return "PermanentFailure";
- case InputRejected: return "InputRejected";
- case OutputRejected: return "OutputRejected";
- case TransientFailure: return "TransientFailure";
- case CachedFailure: return "CachedFailure";
- case TimedOut: return "TimedOut";
- case MiscFailure: return "MiscFailure";
- case DependencyFailed: return "DependencyFailed";
- case LogLimitExceeded: return "LogLimitExceeded";
- case NotDeterministic: return "NotDeterministic";
- case ResolvesToAlreadyValid: return "ResolvesToAlreadyValid";
- default: return "Unknown";
- };
- }();
- return strStatus + ((errorMsg == "") ? "" : " : " + errorMsg);
- }
-
- /* How many times this build was performed. */
- unsigned int timesBuilt = 0;
-
- /* If timesBuilt > 1, whether some builds did not produce the same
- result. (Note that 'isNonDeterministic = false' does not mean
- the build is deterministic, just that we don't have evidence of
- non-determinism.) */
- bool isNonDeterministic = false;
-
- DrvOutputs builtOutputs;
-
- /* The start/stop times of the build (or one of the rounds, if it
- was repeated). */
- time_t startTime = 0, stopTime = 0;
-
- bool success() {
- return status == Built || status == Substituted || status == AlreadyValid || status == ResolvesToAlreadyValid;
- }
-};
struct StoreConfig : public Config
{
@@ -352,7 +237,9 @@ public:
simply yield a different store path, so other users wouldn't be
affected), but it has some backwards compatibility issues (the
hashing scheme changes), so I'm not doing that for now. */
- StorePath computeStorePathForText(const string & name, const string & s,
+ StorePath computeStorePathForText(
+ std::string_view name,
+ std::string_view s,
const StorePathSet & references) const;
/* Check whether a path is valid. */
@@ -482,9 +369,14 @@ public:
validity the resulting path. The resulting path is returned.
The function object `filter' can be used to exclude files (see
libutil/archive.hh). */
- virtual StorePath addToStore(const string & name, const Path & srcPath,
- FileIngestionMethod method = FileIngestionMethod::Recursive, HashType hashAlgo = htSHA256,
- PathFilter & filter = defaultPathFilter, RepairFlag repair = NoRepair, const StorePathSet & references = StorePathSet());
+ virtual StorePath addToStore(
+ std::string_view name,
+ const Path & srcPath,
+ FileIngestionMethod method = FileIngestionMethod::Recursive,
+ HashType hashAlgo = htSHA256,
+ PathFilter & filter = defaultPathFilter,
+ RepairFlag repair = NoRepair,
+ const StorePathSet & references = StorePathSet());
/* Copy the contents of a path to the store and register the
validity the resulting path, using a constant amount of
@@ -499,15 +391,18 @@ public:
false).
`dump` may be drained */
// FIXME: remove?
- virtual StorePath addToStoreFromDump(Source & dump, const string & name,
+ virtual StorePath addToStoreFromDump(Source & dump, std::string_view name,
FileIngestionMethod method = FileIngestionMethod::Recursive, HashType hashAlgo = htSHA256, RepairFlag repair = NoRepair,
const StorePathSet & references = StorePathSet())
{ unsupported("addToStoreFromDump"); }
/* Like addToStore, but the contents written to the output path is
a regular file containing the given string. */
- virtual StorePath addTextToStore(const string & name, const string & s,
- const StorePathSet & references, RepairFlag repair = NoRepair) = 0;
+ virtual StorePath addTextToStore(
+ std::string_view name,
+ std::string_view s,
+ const StorePathSet & references,
+ RepairFlag repair = NoRepair) = 0;
/**
* Add a mapping indicating that `deriver!outputName` maps to the output path
@@ -539,6 +434,16 @@ public:
BuildMode buildMode = bmNormal,
std::shared_ptr<Store> evalStore = nullptr);
+ /* Like `buildPaths()`, but return a vector of `BuildResult`s
+ corresponding to each element in `paths`. Note that in case of
+ a build/substitution error, this function won't throw an
+ exception, but return a `BuildResult` containing an error
+ message. */
+ virtual std::vector<BuildResult> buildPathsWithResults(
+ const std::vector<DerivedPath> & paths,
+ BuildMode buildMode = bmNormal,
+ std::shared_ptr<Store> evalStore = nullptr);
+
/* Build a single non-materialized derivation (i.e. not from an
on-disk .drv file).
@@ -585,30 +490,10 @@ public:
virtual void addTempRoot(const StorePath & path)
{ debug("not creating temporary root, store doesn't support GC"); }
- /* Add an indirect root, which is merely a symlink to `path' from
- /nix/var/nix/gcroots/auto/<hash of `path'>. `path' is supposed
- to be a symlink to a store path. The garbage collector will
- automatically remove the indirect root when it finds that
- `path' has disappeared. */
- virtual void addIndirectRoot(const Path & path)
- { unsupported("addIndirectRoot"); }
-
- /* Find the roots of the garbage collector. Each root is a pair
- (link, storepath) where `link' is the path of the symlink
- outside of the Nix store that point to `storePath'. If
- 'censor' is true, privacy-sensitive information about roots
- found in /proc is censored. */
- virtual Roots findRoots(bool censor)
- { unsupported("findRoots"); }
-
- /* Perform a garbage collection. */
- virtual void collectGarbage(const GCOptions & options, GCResults & results)
- { unsupported("collectGarbage"); }
-
/* Return a string representing information about the path that
can be loaded into the database using `nix-store --load-db' or
`nix-store --register-validity'. */
- string makeValidityRegistration(const StorePathSet & paths,
+ std::string makeValidityRegistration(const StorePathSet & paths,
bool showDerivers, bool showHash);
/* Write a JSON representation of store path metadata, such as the
@@ -722,14 +607,6 @@ public:
*/
StorePathSet exportReferences(const StorePathSet & storePaths, const StorePathSet & inputPaths);
- /* Return the build log of the specified store path, if available,
- or null otherwise. */
- virtual std::optional<std::string> getBuildLog(const StorePath & path)
- { return std::nullopt; }
-
- virtual void addBuildLog(const StorePath & path, std::string_view log)
- { unsupported("addBuildLog"); }
-
/* Hack to allow long-running processes like hydra-queue-runner to
occasionally flush their path info cache. */
void clearPathInfoCache()
@@ -910,7 +787,7 @@ struct RegisterStoreImplementation
/* Display a set of paths in human-readable form (i.e., between quotes
and separated by commas). */
-string showPaths(const PathSet & paths);
+std::string showPaths(const PathSet & paths);
std::optional<ValidPathInfo> decodeValidPathInfo(
diff --git a/src/libstore/store-cast.hh b/src/libstore/store-cast.hh
new file mode 100644
index 000000000..ff62fc359
--- /dev/null
+++ b/src/libstore/store-cast.hh
@@ -0,0 +1,16 @@
+#pragma once
+
+#include "store-api.hh"
+
+namespace nix {
+
+template<typename T>
+T & require(Store & store)
+{
+ auto * castedStore = dynamic_cast<T *>(&store);
+ if (!castedStore)
+ throw UsageError("%s not supported by store '%s'", T::operationName, store.getUri());
+ return *castedStore;
+}
+
+}
diff --git a/src/libstore/worker-protocol.hh b/src/libstore/worker-protocol.hh
index c8332afe6..87088a3ac 100644
--- a/src/libstore/worker-protocol.hh
+++ b/src/libstore/worker-protocol.hh
@@ -9,7 +9,7 @@ namespace nix {
#define WORKER_MAGIC_1 0x6e697863
#define WORKER_MAGIC_2 0x6478696f
-#define PROTOCOL_VERSION (1 << 8 | 33)
+#define PROTOCOL_VERSION (1 << 8 | 34)
#define GET_PROTOCOL_MAJOR(x) ((x) & 0xff00)
#define GET_PROTOCOL_MINOR(x) ((x) & 0x00ff)
@@ -57,6 +57,7 @@ typedef enum {
wopQueryRealisation = 43,
wopAddMultipleToStore = 44,
wopAddBuildLog = 45,
+ wopBuildPathsWithResults = 46,
} WorkerOp;
@@ -91,6 +92,7 @@ MAKE_WORKER_PROTO(, ContentAddress);
MAKE_WORKER_PROTO(, DerivedPath);
MAKE_WORKER_PROTO(, Realisation);
MAKE_WORKER_PROTO(, DrvOutput);
+MAKE_WORKER_PROTO(, BuildResult);
MAKE_WORKER_PROTO(template<typename T>, std::vector<T>);
MAKE_WORKER_PROTO(template<typename T>, std::set<T>);