1
0
Fork 0
mirror of https://github.com/NixOS/nix synced 2025-06-24 22:11:15 +02:00
This commit is contained in:
John Ericson 2025-05-25 11:25:11 +02:00 committed by GitHub
commit feafab92d4
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
75 changed files with 1988 additions and 1444 deletions

View file

@ -255,8 +255,6 @@
''^src/libstore/include/nix/store/pathlocks\.hh$''
''^src/libstore/profiles\.cc$''
''^src/libstore/include/nix/store/profiles\.hh$''
''^src/libstore/realisation\.cc$''
''^src/libstore/include/nix/store/realisation\.hh$''
''^src/libstore/remote-fs-accessor\.cc$''
''^src/libstore/include/nix/store/remote-fs-accessor\.hh$''
''^src/libstore/include/nix/store/remote-store-connection\.hh$''
@ -280,8 +278,6 @@
''^src/libstore/include/nix/store/build/derivation-building-goal\.hh$''
''^src/libstore/build/derivation-goal\.cc$''
''^src/libstore/include/nix/store/build/derivation-goal\.hh$''
''^src/libstore/build/drv-output-substitution-goal\.cc$''
''^src/libstore/include/nix/store/build/drv-output-substitution-goal\.hh$''
''^src/libstore/build/entry-points\.cc$''
''^src/libstore/build/goal\.cc$''
''^src/libstore/include/nix/store/build/goal\.hh$''

View file

@ -345,13 +345,11 @@ connected:
}
auto outputHashes = staticOutputHashes(*store, drv);
std::set<Realisation> missingRealisations;
StorePathSet missingPaths;
if (experimentalFeatureSettings.isEnabled(Xp::CaDerivations) && !drv.type().hasKnownOutputPaths()) {
for (auto & outputName : wantedOutputs) {
auto thisOutputHash = outputHashes.at(outputName);
auto thisOutputId = DrvOutput{ thisOutputHash, outputName };
auto thisOutputId = DrvOutput{ *drvPath, outputName };
if (!store->queryRealisation(thisOutputId)) {
debug("missing output %s", outputName);
assert(optResult);
@ -359,7 +357,7 @@ connected:
auto i = result.builtOutputs.find(outputName);
assert(i != result.builtOutputs.end());
auto & newRealisation = i->second;
missingRealisations.insert(newRealisation);
missingRealisations.insert({newRealisation, thisOutputId});
missingPaths.insert(newRealisation.outPath);
}
}

View file

@ -116,21 +116,16 @@ RealisedPath::Set BuiltPath::toRealisedPaths(Store & store) const
overloaded{
[&](const BuiltPath::Opaque & p) { res.insert(p.path); },
[&](const BuiltPath::Built & p) {
auto drvHashes =
staticOutputHashes(store, store.readDerivation(p.drvPath->outPath()));
for (auto& [outputName, outputPath] : p.outputs) {
if (experimentalFeatureSettings.isEnabled(
Xp::CaDerivations)) {
auto drvOutput = get(drvHashes, outputName);
if (!drvOutput)
throw Error(
"the derivation '%s' has unrealised output '%s' (derived-path.cc/toRealisedPaths)",
store.printStorePath(p.drvPath->outPath()), outputName);
auto thisRealisation = store.queryRealisation(
DrvOutput{*drvOutput, outputName});
assert(thisRealisation); // Weve built it, so we must
// have the realisation
res.insert(*thisRealisation);
if (experimentalFeatureSettings.isEnabled(Xp::CaDerivations)) {
DrvOutput key{
.drvPath = p.drvPath->outPath(),
.outputName = outputName,
};
auto thisRealisation = store.queryRealisation(key);
// Weve built it, so we must have the realisation.
assert(thisRealisation);
res.insert(Realisation{*thisRealisation, key});
} else {
res.insert(outputPath);
}

View file

@ -1565,31 +1565,7 @@ static void derivationStrictInternal(
DerivationOutput::Deferred { });
}
auto hashModulo = hashDerivationModulo(*state.store, Derivation(drv), true);
switch (hashModulo.kind) {
case DrvHash::Kind::Regular:
for (auto & i : outputs) {
auto h = get(hashModulo.hashes, i);
if (!h)
state.error<AssertionError>(
"derivation produced no hash for output '%s'",
i
).atPos(v).debugThrow();
auto outPath = state.store->makeOutputPath(i, *h, drvName);
drv.env[i] = state.store->printStorePath(outPath);
drv.outputs.insert_or_assign(
i,
DerivationOutput::InputAddressed {
.path = std::move(outPath),
});
}
break;
;
case DrvHash::Kind::Deferred:
for (auto & i : outputs) {
drv.outputs.insert_or_assign(i, DerivationOutput::Deferred {});
}
}
resolveInputAddressed(*state.store, drv);
}
/* Write the resulting term into the Nix store directory. */

View file

@ -1,4 +1,4 @@
#include "fetchers.hh"
#include "nix/fetchers/fetchers.hh"
namespace nix::fetchers {

View file

@ -64,12 +64,18 @@ public:
}
};
#define VERSIONED_CHARACTERIZATION_TEST(FIXTURE, NAME, STEM, VERSION, VALUE) \
#define VERSIONED_READ_CHARACTERIZATION_TEST(FIXTURE, NAME, STEM, VERSION, VALUE) \
TEST_F(FIXTURE, NAME ## _read) { \
readProtoTest(STEM, VERSION, VALUE); \
} \
}
#define VERSIONED_WRITE_CHARACTERIZATION_TEST(FIXTURE, NAME, STEM, VERSION, VALUE) \
TEST_F(FIXTURE, NAME ## _write) { \
writeProtoTest(STEM, VERSION, VALUE); \
}
#define VERSIONED_CHARACTERIZATION_TEST(FIXTURE, NAME, STEM, VERSION, VALUE) \
VERSIONED_READ_CHARACTERIZATION_TEST(FIXTURE, NAME, STEM, VERSION, VALUE) \
VERSIONED_WRITE_CHARACTERIZATION_TEST(FIXTURE, NAME, STEM, VERSION, VALUE)
}

View file

@ -96,51 +96,6 @@ CHARACTERIZATION_TEST(
},
}))
CHARACTERIZATION_TEST(
drvOutput,
"drv-output",
(std::tuple<DrvOutput, DrvOutput> {
{
.drvHash = Hash::parseSRI("sha256-FePFYIlMuycIXPZbWi7LGEiMmZSX9FMbaQenWBzm1Sc="),
.outputName = "baz",
},
DrvOutput {
.drvHash = Hash::parseSRI("sha256-b4afnqKCO9oWXgYHb9DeQ2berSwOjS27rSd9TxXDc/U="),
.outputName = "quux",
},
}))
CHARACTERIZATION_TEST(
realisation,
"realisation",
(std::tuple<Realisation, Realisation> {
Realisation {
.id = DrvOutput {
.drvHash = Hash::parseSRI("sha256-FePFYIlMuycIXPZbWi7LGEiMmZSX9FMbaQenWBzm1Sc="),
.outputName = "baz",
},
.outPath = StorePath { "g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-foo" },
.signatures = { "asdf", "qwer" },
},
Realisation {
.id = {
.drvHash = Hash::parseSRI("sha256-FePFYIlMuycIXPZbWi7LGEiMmZSX9FMbaQenWBzm1Sc="),
.outputName = "baz",
},
.outPath = StorePath { "g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-foo" },
.signatures = { "asdf", "qwer" },
.dependentRealisations = {
{
DrvOutput {
.drvHash = Hash::parseSRI("sha256-b4afnqKCO9oWXgYHb9DeQ2berSwOjS27rSd9TxXDc/U="),
.outputName = "quux",
},
StorePath { "g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-foo" },
},
},
},
}))
CHARACTERIZATION_TEST(
vector,
"vector",

View file

@ -72,16 +72,16 @@ VERSIONED_CHARACTERIZATION_TEST(
VERSIONED_CHARACTERIZATION_TEST(
ServeProtoTest,
drvOutput,
"drv-output",
defaultVersion,
drvOutput_2_8,
"drv-output-2.8",
2 << 8 | 8,
(std::tuple<DrvOutput, DrvOutput> {
{
.drvHash = Hash::parseSRI("sha256-FePFYIlMuycIXPZbWi7LGEiMmZSX9FMbaQenWBzm1Sc="),
.drvPath = StorePath { "g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-foo.drv" },
.outputName = "baz",
},
DrvOutput {
.drvHash = Hash::parseSRI("sha256-b4afnqKCO9oWXgYHb9DeQ2berSwOjS27rSd9TxXDc/U="),
.drvPath = StorePath { "g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-foo.drv" },
.outputName = "quux",
},
}))
@ -90,34 +90,27 @@ VERSIONED_CHARACTERIZATION_TEST(
VERSIONED_CHARACTERIZATION_TEST(
ServeProtoTest,
realisation,
"realisation",
defaultVersion,
(std::tuple<Realisation, Realisation> {
Realisation {
.id = DrvOutput {
.drvHash = Hash::parseSRI("sha256-FePFYIlMuycIXPZbWi7LGEiMmZSX9FMbaQenWBzm1Sc="),
.outputName = "baz",
},
unkeyedRealisation_2_8,
"unkeyed-realisation-2.8",
2 << 8 | 8,
(UnkeyedRealisation {
.outPath = StorePath { "g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-foo" },
.signatures = { "asdf", "qwer" },
}))
VERSIONED_CHARACTERIZATION_TEST(
ServeProtoTest,
realisation_2_8,
"realisation-2.8",
2 << 8 | 8,
(Realisation {
UnkeyedRealisation {
.outPath = StorePath { "g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-foo" },
.signatures = { "asdf", "qwer" },
},
Realisation {
.id = {
.drvHash = Hash::parseSRI("sha256-FePFYIlMuycIXPZbWi7LGEiMmZSX9FMbaQenWBzm1Sc="),
.outputName = "baz",
},
.outPath = StorePath { "g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-foo" },
.signatures = { "asdf", "qwer" },
.dependentRealisations = {
{
DrvOutput {
.drvHash = Hash::parseSRI("sha256-b4afnqKCO9oWXgYHb9DeQ2berSwOjS27rSd9TxXDc/U="),
.outputName = "quux",
},
StorePath { "g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-foo" },
},
},
DrvOutput {
.drvPath = StorePath { "g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-foo.drv" },
.outputName = "baz",
},
}))
@ -173,7 +166,10 @@ VERSIONED_CHARACTERIZATION_TEST(
t;
}))
VERSIONED_CHARACTERIZATION_TEST(
/* We now do a lossy read which does not allow us to faithfully right
back, since we changed the data type. We still however want to test
that this read works, and so for that we have a one-way test. */
VERSIONED_READ_CHARACTERIZATION_TEST(
ServeProtoTest,
buildResult_2_6,
"build-result-2.6",
@ -200,20 +196,64 @@ VERSIONED_CHARACTERIZATION_TEST(
{
"foo",
{
.id = DrvOutput {
.drvHash = Hash::parseSRI("sha256-b4afnqKCO9oWXgYHb9DeQ2berSwOjS27rSd9TxXDc/U="),
.outputName = "foo",
},
.outPath = StorePath { "g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-foo" },
},
},
{
"bar",
{
.id = DrvOutput {
.drvHash = Hash::parseSRI("sha256-b4afnqKCO9oWXgYHb9DeQ2berSwOjS27rSd9TxXDc/U="),
.outputName = "bar",
},
.outPath = StorePath { "g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-bar" },
},
},
},
.startTime = 30,
.stopTime = 50,
#if 0
// These fields are not yet serialized.
// FIXME Include in next version of protocol or document
// why they are skipped.
.cpuUser = std::chrono::milliseconds(500s),
.cpuSystem = std::chrono::milliseconds(604s),
#endif
},
};
t;
}))
VERSIONED_CHARACTERIZATION_TEST(
ServeProtoTest,
buildResult_2_8,
"build-result-2.8",
2 << 8 | 8,
({
using namespace std::literals::chrono_literals;
std::tuple<BuildResult, BuildResult, BuildResult> t {
BuildResult {
.status = BuildResult::OutputRejected,
.errorMsg = "no idea why",
},
BuildResult {
.status = BuildResult::NotDeterministic,
.errorMsg = "no idea why",
.timesBuilt = 3,
.isNonDeterministic = true,
.startTime = 30,
.stopTime = 50,
},
BuildResult {
.status = BuildResult::Built,
.timesBuilt = 1,
.builtOutputs = {
{
"foo",
{
.outPath = StorePath { "g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-foo" },
},
},
{
"bar",
{
.outPath = StorePath { "g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-bar" },
},
},

View file

@ -125,49 +125,42 @@ VERSIONED_CHARACTERIZATION_TEST(
VERSIONED_CHARACTERIZATION_TEST(
WorkerProtoTest,
drvOutput,
"drv-output",
defaultVersion,
"drv-output-1.39",
1 << 8 | 39,
(std::tuple<DrvOutput, DrvOutput> {
{
.drvHash = Hash::parseSRI("sha256-FePFYIlMuycIXPZbWi7LGEiMmZSX9FMbaQenWBzm1Sc="),
.drvPath = StorePath { "g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-foo.drv" },
.outputName = "baz",
},
DrvOutput {
.drvHash = Hash::parseSRI("sha256-b4afnqKCO9oWXgYHb9DeQ2berSwOjS27rSd9TxXDc/U="),
.drvPath = StorePath { "g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-foo.drv" },
.outputName = "quux",
},
}))
VERSIONED_CHARACTERIZATION_TEST(
WorkerProtoTest,
realisation,
"realisation",
defaultVersion,
(std::tuple<Realisation, Realisation> {
Realisation {
.id = DrvOutput {
.drvHash = Hash::parseSRI("sha256-FePFYIlMuycIXPZbWi7LGEiMmZSX9FMbaQenWBzm1Sc="),
.outputName = "baz",
},
unkeyedRealisation_1_39,
"unkeyed-realisation-1.39",
1 << 8 | 39,
(UnkeyedRealisation {
.outPath = StorePath { "g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-foo" },
.signatures = { "asdf", "qwer" },
}))
VERSIONED_CHARACTERIZATION_TEST(
WorkerProtoTest,
realisation_1_39,
"realisation-1.39",
1 << 8 | 39,
(Realisation {
UnkeyedRealisation {
.outPath = StorePath { "g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-foo" },
.signatures = { "asdf", "qwer" },
},
Realisation {
.id = {
.drvHash = Hash::parseSRI("sha256-FePFYIlMuycIXPZbWi7LGEiMmZSX9FMbaQenWBzm1Sc="),
.outputName = "baz",
},
.outPath = StorePath { "g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-foo" },
.signatures = { "asdf", "qwer" },
.dependentRealisations = {
{
DrvOutput {
.drvHash = Hash::parseSRI("sha256-b4afnqKCO9oWXgYHb9DeQ2berSwOjS27rSd9TxXDc/U="),
.outputName = "quux",
},
StorePath { "g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-foo" },
},
},
DrvOutput {
.drvPath = StorePath { "g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-foo.drv" },
.outputName = "baz",
},
}))
@ -194,7 +187,10 @@ VERSIONED_CHARACTERIZATION_TEST(
t;
}))
VERSIONED_CHARACTERIZATION_TEST(
/* We now do a lossy read which does not allow us to faithfully right
back, since we changed the data type. We still however want to test
that this read works, and so for that we have a one-way test. */
VERSIONED_READ_CHARACTERIZATION_TEST(
WorkerProtoTest,
buildResult_1_28,
"build-result-1.28",
@ -216,20 +212,12 @@ VERSIONED_CHARACTERIZATION_TEST(
{
"foo",
{
.id = DrvOutput {
.drvHash = Hash::parseSRI("sha256-b4afnqKCO9oWXgYHb9DeQ2berSwOjS27rSd9TxXDc/U="),
.outputName = "foo",
},
.outPath = StorePath { "g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-foo" },
},
},
{
"bar",
{
.id = DrvOutput {
.drvHash = Hash::parseSRI("sha256-b4afnqKCO9oWXgYHb9DeQ2berSwOjS27rSd9TxXDc/U="),
.outputName = "bar",
},
.outPath = StorePath { "g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-bar" },
},
},
@ -239,7 +227,8 @@ VERSIONED_CHARACTERIZATION_TEST(
t;
}))
VERSIONED_CHARACTERIZATION_TEST(
// See above note
VERSIONED_READ_CHARACTERIZATION_TEST(
WorkerProtoTest,
buildResult_1_29,
"build-result-1.29",
@ -266,20 +255,12 @@ VERSIONED_CHARACTERIZATION_TEST(
{
"foo",
{
.id = DrvOutput {
.drvHash = Hash::parseSRI("sha256-b4afnqKCO9oWXgYHb9DeQ2berSwOjS27rSd9TxXDc/U="),
.outputName = "foo",
},
.outPath = StorePath { "g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-foo" },
},
},
{
"bar",
{
.id = DrvOutput {
.drvHash = Hash::parseSRI("sha256-b4afnqKCO9oWXgYHb9DeQ2berSwOjS27rSd9TxXDc/U="),
.outputName = "bar",
},
.outPath = StorePath { "g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-bar" },
},
},
@ -291,7 +272,8 @@ VERSIONED_CHARACTERIZATION_TEST(
t;
}))
VERSIONED_CHARACTERIZATION_TEST(
// See above note
VERSIONED_READ_CHARACTERIZATION_TEST(
WorkerProtoTest,
buildResult_1_37,
"build-result-1.37",
@ -318,20 +300,58 @@ VERSIONED_CHARACTERIZATION_TEST(
{
"foo",
{
.id = DrvOutput {
.drvHash = Hash::parseSRI("sha256-b4afnqKCO9oWXgYHb9DeQ2berSwOjS27rSd9TxXDc/U="),
.outputName = "foo",
},
.outPath = StorePath { "g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-foo" },
},
},
{
"bar",
{
.id = DrvOutput {
.drvHash = Hash::parseSRI("sha256-b4afnqKCO9oWXgYHb9DeQ2berSwOjS27rSd9TxXDc/U="),
.outputName = "bar",
},
.outPath = StorePath { "g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-bar" },
},
},
},
.startTime = 30,
.stopTime = 50,
.cpuUser = std::chrono::microseconds(500s),
.cpuSystem = std::chrono::microseconds(604s),
},
};
t;
}))
VERSIONED_CHARACTERIZATION_TEST(
WorkerProtoTest,
buildResult_1_39,
"build-result-1.39",
1 << 8 | 39,
({
using namespace std::literals::chrono_literals;
std::tuple<BuildResult, BuildResult, BuildResult> t {
BuildResult {
.status = BuildResult::OutputRejected,
.errorMsg = "no idea why",
},
BuildResult {
.status = BuildResult::NotDeterministic,
.errorMsg = "no idea why",
.timesBuilt = 3,
.isNonDeterministic = true,
.startTime = 30,
.stopTime = 50,
},
BuildResult {
.status = BuildResult::Built,
.timesBuilt = 1,
.builtOutputs = {
{
"foo",
{
.outPath = StorePath { "g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-foo" },
},
},
{
"bar",
{
.outPath = StorePath { "g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-bar" },
},
},

View file

@ -493,22 +493,36 @@ StorePath BinaryCacheStore::addToStore(
})->path;
}
void BinaryCacheStore::queryRealisationUncached(const DrvOutput & id,
Callback<std::shared_ptr<const Realisation>> callback) noexcept
std::string BinaryCacheStore::makeRealisationPath(const DrvOutput & id)
{
auto outputInfoFilePath = realisationsPrefix + "/" + id.to_string() + ".doi";
return realisationsPrefix
+ "/" + id.drvPath.to_string()
+ "/" + id.outputName
+ ".doi";
}
void BinaryCacheStore::queryRealisationUncached(const DrvOutput & id,
Callback<std::shared_ptr<const UnkeyedRealisation>> callback) noexcept
{
auto outputInfoFilePath = makeRealisationPath(id);
auto callbackPtr = std::make_shared<decltype(callback)>(std::move(callback));
Callback<std::optional<std::string>> newCallback = {
[=](std::future<std::optional<std::string>> fut) {
[=,this](std::future<std::optional<std::string>> fut) {
try {
auto data = fut.get();
if (!data) return (*callbackPtr)({});
auto realisation = Realisation::fromJSON(
nlohmann::json::parse(*data), outputInfoFilePath);
return (*callbackPtr)(std::make_shared<const Realisation>(realisation));
UnkeyedRealisation realisation { .outPath = StorePath::dummy, };
auto json = nlohmann::json::parse(*data);
try {
realisation = UnkeyedRealisation::fromJSON(*this, json);
} catch (Error & e) {
e.addTrace({}, "reading build trace key-value file '%s'", outputInfoFilePath);
throw;
}
return (*callbackPtr)(std::make_shared<const UnkeyedRealisation>(realisation));
} catch (...) {
callbackPtr->rethrow();
}
@ -518,11 +532,14 @@ void BinaryCacheStore::queryRealisationUncached(const DrvOutput & id,
getFile(outputInfoFilePath, std::move(newCallback));
}
void BinaryCacheStore::registerDrvOutput(const Realisation& info) {
void BinaryCacheStore::registerDrvOutput(const Realisation & info)
{
if (diskCache)
diskCache->upsertRealisation(getUri(), info);
auto filePath = realisationsPrefix + "/" + info.id.to_string() + ".doi";
upsertFile(filePath, info.toJSON().dump(), "application/json");
upsertFile(
makeRealisationPath(info.id),
info.toJSON(*this).dump(),
"application/json");
}
ref<SourceAccessor> BinaryCacheStore::getFSAccessor(bool requireValidPath)

View file

@ -0,0 +1,208 @@
#include "nix/store/build/build-trace-goal.hh"
#include "nix/util/finally.hh"
#include "nix/store/build/worker.hh"
#include "nix/store/build/substitution-goal.hh"
#include "nix/util/callback.hh"
#include "nix/util/util.hh"
#include "nix/store/derivations.hh"
#include "nix/store/store-open.hh"
#include "nix/store/build/derivation-resolution-goal.hh"
namespace nix {
BuildTraceGoal::BuildTraceGoal(const SingleDerivedPath::Built & id, Worker & worker)
: Goal{worker, init()}
, id{id}
{
name = fmt("substitution of '%s'", id.to_string(worker.store));
trace("created");
}
Goal::Co BuildTraceGoal::init()
{
trace("init");
DrvOutput id2{
.drvPath = StorePath::dummy,
.outputName = id.output,
};
// No `std::visit` with coroutines :(
if (const auto * path = std::get_if<SingleDerivedPath::Opaque>(&*id.drvPath)) {
// At least we know the drv path statically, can procede
id2.drvPath = path->path;
} else if (const auto * outputDeriving = std::get_if<SingleDerivedPath::Built>(&*id.drvPath)) {
// Dynamic derivation case, need to resolve that first.
auto g = worker.makeBuildTraceGoal({
outputDeriving->drvPath,
outputDeriving->output,
});
co_await await(Goals{upcast_goal(g)});
if (nrFailed > 0) {
debug("The output deriving path '%s' could not be resolved", outputDeriving->to_string(worker.store));
co_return amDone(nrNoSubstituters > 0 ? ecNoSubstituters : ecFailed);
}
id2.drvPath = g->outputInfo->outPath;
}
/* If the derivation already exists, were done */
if ((outputInfo = worker.store.queryRealisation(id2))) {
co_return amDone(ecSuccess);
}
/**
* Firstly, whether we know the status, secondly, what it is
*/
std::optional<bool> drvIsResolved;
/* If the derivation has statically-known output paths */
if (worker.evalStore.isValidPath(id2.drvPath)) {
auto drv = worker.evalStore.readDerivation(id2.drvPath);
auto os = drv.outputsAndOptPaths(worker.store);
/* Mark what we now know */
drvIsResolved = {drv.inputDrvs.map.empty()};
if (auto * p = get(os, id2.outputName)) {
if (auto & outPath = p->second) {
outputInfo = std::make_shared<UnkeyedRealisation>(*outPath);
co_return amDone(ecSuccess);
} else {
/* Otherwise, not failure, just looking up build trace below. */
}
} else {
debug(
"Derivation '%s' does not have output '%s', impossible to find build trace key-value pair",
worker.store.printStorePath(id2.drvPath),
id2.outputName);
co_return amDone(ecFailed);
}
}
auto subs = settings.useSubstitutes ? getDefaultSubstituters() : std::list<ref<Store>>();
bool substituterFailed = false;
if (!drvIsResolved || *drvIsResolved) {
/* Since derivation might be resolved --- isn't known to be
not-resolved, it might have entries. So, let's try querying
the substituters. */
for (const auto & sub : subs) {
trace("trying next substituter");
/* The callback of the curl download below can outlive `this` (if
some other error occurs), so it must not touch `this`. So put
the shared state in a separate refcounted object. */
auto outPipe = std::make_shared<MuxablePipe>();
#ifndef _WIN32
outPipe->create();
#else
outPipe->createAsyncPipe(worker.ioport.get());
#endif
auto promise = std::make_shared<std::promise<std::shared_ptr<const UnkeyedRealisation>>>();
sub->queryRealisation(
id2, {[outPipe(outPipe), promise(promise)](std::future<std::shared_ptr<const UnkeyedRealisation>> res) {
try {
Finally updateStats([&]() { outPipe->writeSide.close(); });
promise->set_value(res.get());
} catch (...) {
promise->set_exception(std::current_exception());
}
}});
worker.childStarted(
shared_from_this(),
{
#ifndef _WIN32
outPipe->readSide.get()
#else
&*outPipe
#endif
},
true,
false);
co_await Suspend{};
worker.childTerminated(this);
std::shared_ptr<const UnkeyedRealisation> outputInfo;
try {
outputInfo = promise->get_future().get();
} catch (std::exception & e) {
printError(e.what());
substituterFailed = true;
}
if (!outputInfo)
continue;
worker.store.registerDrvOutput({*outputInfo, id2});
trace("finished");
co_return amDone(ecSuccess);
}
}
/* Derivation might not be resolved, let's try doing that */
trace("trying resolving derivation in build-trace goal");
auto g = worker.makeDerivationResolutionGoal(id2.drvPath);
co_await await(Goals{g});
if (nrFailed > 0) {
/* None left. Terminate this goal and let someone else deal
with it. */
debug(
"derivation output '%s' is required, but there is no substituter that can provide it",
id2.render(worker.store));
if (substituterFailed) {
worker.failedSubstitutions++;
worker.updateProgress();
}
/* Hack: don't indicate failure if there were no substituters.
In that case the calling derivation should just do a
build. */
co_return amDone(substituterFailed ? ecFailed : ecNoSubstituters);
}
/* This should be set if the goal succeeded */
assert(g->resolvedDrv);
/* Try everything again, now with a resolved derivation */
auto bt2 = worker.makeBuildTraceGoal({
makeConstantStorePathRef(g->resolvedDrvPath),
id2.outputName,
});
co_await await(Goals{bt2});
/* Set the build trace value as our own. Note the signure will not
match our key since we're the unresolved derivation, but that's
fine. We're not writing it to the DB; that's `bt2`' job. */
if (bt2->outputInfo)
outputInfo = bt2->outputInfo;
co_return amDone(bt2->exitCode, bt2->ex);
}
std::string BuildTraceGoal::key()
{
/* "a$" ensures substitution goals happen before derivation
goals. */
return "a$" + std::string(id.to_string(worker.store));
}
void BuildTraceGoal::handleEOF(Descriptor fd)
{
worker.wakeUp(shared_from_this());
}
}

View file

@ -1,5 +1,5 @@
#include "nix/store/build/derivation-building-goal.hh"
#include "nix/store/build/derivation-goal.hh"
#include "nix/store/build/derivation-creation-and-realisation-goal.hh"
#ifndef _WIN32 // TODO enable build hook on Windows
# include "nix/store/build/hook-instance.hh"
# include "nix/store/build/derivation-builder.hh"
@ -264,7 +264,7 @@ Goal::Co DerivationBuildingGoal::gaveUpOnSubstitution()
auto mEntry = get(inputGoals, drvPath);
if (!mEntry) return std::nullopt;
auto buildResult = (*mEntry)->getBuildResult(DerivedPath::Built{drvPath, OutputsSpec::Names{outputName}});
auto & buildResult = (*mEntry)->buildResult;
if (!buildResult.success()) return std::nullopt;
auto i = get(buildResult.builtOutputs, outputName);
@ -295,8 +295,8 @@ Goal::Co DerivationBuildingGoal::gaveUpOnSubstitution()
});
// FIXME wanted outputs
auto resolvedDrvGoal = worker.makeDerivationGoal(
makeConstantStorePathRef(pathResolved), OutputsSpec::All{}, buildMode);
auto resolvedDrvGoal = worker.makeDerivationCreationAndRealisationGoal(
pathResolved, OutputsSpec::All{}, drvResolved, buildMode);
{
Goals waitees{resolvedDrvGoal};
co_await await(std::move(waitees));
@ -304,23 +304,16 @@ Goal::Co DerivationBuildingGoal::gaveUpOnSubstitution()
trace("resolved derivation finished");
auto resolvedDrv = *resolvedDrvGoal->drv;
auto resolvedResult = resolvedDrvGoal->getBuildResult(DerivedPath::Built{
.drvPath = makeConstantStorePathRef(pathResolved),
.outputs = OutputsSpec::All{},
});
auto resolvedResult = resolvedDrvGoal->buildResult;
SingleDrvOutputs builtOutputs;
if (resolvedResult.success()) {
auto resolvedHashes = staticOutputHashes(worker.store, resolvedDrv);
StorePathSet outputPaths;
for (auto & outputName : resolvedDrv.outputNames()) {
for (auto & outputName : drvResolved.outputNames()) {
auto initialOutput = get(initialOutputs, outputName);
auto resolvedHash = get(resolvedHashes, outputName);
if ((!initialOutput) || (!resolvedHash))
if ((!initialOutput))
throw Error(
"derivation '%s' doesn't have expected output '%s' (derivation-goal.cc/resolve)",
worker.store.printStorePath(drvPath), outputName);
@ -329,31 +322,21 @@ Goal::Co DerivationBuildingGoal::gaveUpOnSubstitution()
auto take1 = get(resolvedResult.builtOutputs, outputName);
if (take1) return *take1;
/* The above `get` should work. But sateful tracking of
/* The above `get` should work. But stateful tracking of
outputs in resolvedResult, this can get out of sync with the
store, which is our actual source of truth. For now we just
check the store directly if it fails. */
auto take2 = worker.evalStore.queryRealisation(DrvOutput { *resolvedHash, outputName });
auto take2 = worker.evalStore.queryRealisation(DrvOutput {
.drvPath = pathResolved,
.outputName = outputName,
});
if (take2) return *take2;
throw Error(
"derivation '%s' doesn't have expected output '%s' (derivation-goal.cc/realisation)",
resolvedDrvGoal->drvReq->to_string(worker.store), outputName);
worker.store.printStorePath(pathResolved), outputName);
}();
if (!drv->type().isImpure()) {
auto newRealisation = realisation;
newRealisation.id = DrvOutput { initialOutput->outputHash, outputName };
newRealisation.signatures.clear();
if (!drv->type().isFixed()) {
auto & drvStore = worker.evalStore.isValidPath(drvPath)
? worker.evalStore
: worker.store;
newRealisation.dependentRealisations = drvOutputReferences(worker.store, *drv, realisation.outPath, &drvStore);
}
worker.store.signRealisation(newRealisation);
worker.store.registerDrvOutput(newRealisation);
}
outputPaths.insert(realisation.outPath);
builtOutputs.emplace(outputName, realisation);
}
@ -1165,7 +1148,7 @@ std::pair<bool, SingleDrvOutputs> DerivationBuildingGoal::checkPathValidity()
: PathStatus::Corrupt,
};
}
auto drvOutput = DrvOutput{info.outputHash, i.first};
auto drvOutput = DrvOutput{drvPath, i.first};
if (experimentalFeatureSettings.isEnabled(Xp::CaDerivations)) {
if (auto real = worker.store.queryRealisation(drvOutput)) {
info.known = {
@ -1177,16 +1160,21 @@ std::pair<bool, SingleDrvOutputs> DerivationBuildingGoal::checkPathValidity()
// 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).
worker.store.registerDrvOutput(
Realisation {
drvOutput,
info.known->path,
}
);
worker.store.registerDrvOutput(Realisation {
{
.outPath = info.known->path,
},
drvOutput,
});
}
}
if (info.known && info.known->isValid())
validOutputs.emplace(i.first, Realisation { drvOutput, info.known->path });
validOutputs.emplace(i.first, Realisation {
{
.outPath = info.known->path,
},
drvOutput,
});
}
bool allValid = true;

View file

@ -0,0 +1,177 @@
#include "nix/store/build/derivation-creation-and-realisation-goal.hh"
#include "nix/store/build/worker.hh"
#include "nix/store/derivations.hh"
namespace nix {
DerivationCreationAndRealisationGoal::DerivationCreationAndRealisationGoal(
ref<const SingleDerivedPath> drvReq, const OutputsSpec & wantedOutputs, Worker & worker, BuildMode buildMode)
: Goal(worker, init())
, drvReq(drvReq)
, wantedOutputs(wantedOutputs)
, buildMode(buildMode)
{
commonInit();
}
DerivationCreationAndRealisationGoal::DerivationCreationAndRealisationGoal(
const StorePath & drvPath,
const OutputsSpec & wantedOutputs,
const Derivation & drv,
Worker & worker,
BuildMode buildMode)
: Goal(worker, haveDerivation(drvPath, drv))
, drvReq(makeConstantStorePathRef(drvPath))
, wantedOutputs(wantedOutputs)
, buildMode(buildMode)
{
commonInit();
}
void DerivationCreationAndRealisationGoal::commonInit()
{
name =
fmt("outer obtaining drv from '%s' and then building outputs %s",
drvReq->to_string(worker.store),
std::visit(
overloaded{
[&](const OutputsSpec::All) -> std::string { return "* (all of them)"; },
[&](const OutputsSpec::Names os) { return concatStringsSep(", ", quoteStrings(os)); },
},
wantedOutputs.raw));
trace("created outer");
worker.updateProgress();
}
DerivationCreationAndRealisationGoal::~DerivationCreationAndRealisationGoal() {}
static StorePath pathPartOfReq(const SingleDerivedPath & req)
{
return std::visit(
overloaded{
[&](const SingleDerivedPath::Opaque & bo) { return bo.path; },
[&](const SingleDerivedPath::Built & bfd) { return pathPartOfReq(*bfd.drvPath); },
},
req.raw());
}
std::string DerivationCreationAndRealisationGoal::key()
{
/* Ensure that derivations get built in order of their name,
i.e. a derivation named "aardvark" always comes before "baboon". And
substitution goals and inner derivation goals always happen before
derivation goals (due to "b$"). */
return "d$" + std::string(pathPartOfReq(*drvReq).name()) + "$" + DerivedPath::Built{
.drvPath = drvReq,
.outputs = wantedOutputs,
}.to_string(worker.store);
}
void DerivationCreationAndRealisationGoal::timedOut(Error && ex) {}
Goal::Co DerivationCreationAndRealisationGoal::init()
{
trace("need to load derivation from file");
/* The first thing to do is to make sure that the derivation
exists. If it doesn't, it may be built from another derivation,
or merely substituted. We can make goal to get it and not worry
about which method it takes to get the derivation. */
if (auto optDrvPath = [this]() -> std::optional<StorePath> {
if (buildMode != bmNormal)
return std::nullopt;
auto drvPath = StorePath::dummy;
try {
drvPath = resolveDerivedPath(worker.store, *drvReq);
} catch (MissingRealisation &) {
return std::nullopt;
}
auto cond = worker.evalStore.isValidPath(drvPath) || worker.store.isValidPath(drvPath);
return cond ? std::optional{drvPath} : std::nullopt;
}()) {
trace(
fmt("already have drv '%s' for '%s', can go straight to building",
worker.store.printStorePath(*optDrvPath),
drvReq->to_string(worker.store)));
} else {
trace("need to obtain drv we want to build");
Goals waitees{worker.makeGoal(DerivedPath::fromSingle(*drvReq))};
co_await await(std::move(waitees));
}
trace("outer load and build derivation");
if (nrFailed != 0) {
co_return amDone(ecFailed, Error("cannot build missing derivation '%s'", drvReq->to_string(worker.store)));
}
StorePath drvPath = resolveDerivedPath(worker.store, *drvReq);
/* `drvPath' should already be a root, but let's be on the safe
side: if the user forgot to make it a root, we wouldn't want
things being garbage collected while we're busy. */
worker.evalStore.addTempRoot(drvPath);
/* Get the derivation. It is probably in the eval store, but it might be inthe main store:
- Resolved derivation are resolved against main store realisations, and so must be stored there.
- Dynamic derivations are built, and so are found in the main store.
*/
auto drv = [&] {
for (auto * drvStore : {&worker.evalStore, &worker.store})
if (drvStore->isValidPath(drvPath))
return drvStore->readDerivation(drvPath);
assert(false);
}();
co_return haveDerivation(std::move(drvPath), std::move(drv));
}
Goal::Co DerivationCreationAndRealisationGoal::haveDerivation(StorePath drvPath, Derivation drv)
{
trace("have derivation, will kick off derivations goals per wanted output");
auto resolvedWantedOutputs = std::visit(
overloaded{
[&](const OutputsSpec::Names & names) -> OutputsSpec::Names { return names; },
[&](const OutputsSpec::All &) -> OutputsSpec::Names {
StringSet outputs;
for (auto & [outputName, _] : drv.outputs)
outputs.insert(outputName);
return outputs;
},
},
wantedOutputs.raw);
Goals concreteDrvGoals;
/* Build this step! */
for (auto & output : resolvedWantedOutputs) {
auto g = upcast_goal(worker.makeDerivationGoal(drvPath, drv, output, buildMode));
g->preserveException = true;
/* We will finish with it ourselves, as if we were the derivational goal. */
concreteDrvGoals.insert(std::move(g));
}
optDrvPath = std::move(drvPath);
// Copy on purpose
co_await await(Goals(concreteDrvGoals));
trace("outer build done");
auto & g = *concreteDrvGoals.begin();
buildResult = g->buildResult;
for (auto & g2 : concreteDrvGoals) {
for (auto && [x, y] : g2->buildResult.builtOutputs)
buildResult.builtOutputs.insert_or_assign(x, y);
}
co_return amDone(g->exitCode, g->ex);
}
}

View file

@ -24,35 +24,18 @@
namespace nix {
DerivationGoal::DerivationGoal(ref<const SingleDerivedPath> drvReq,
const OutputsSpec & wantedOutputs, Worker & worker, BuildMode buildMode)
: Goal(worker, loadDerivation())
, drvReq(drvReq)
, wantedOutputs(wantedOutputs)
, buildMode(buildMode)
{
name = fmt(
"building of '%s' from .drv file",
DerivedPath::Built { drvReq, wantedOutputs }.to_string(worker.store));
trace("created");
mcExpectedBuilds = std::make_unique<MaintainCount<uint64_t>>(worker.expectedBuilds);
worker.updateProgress();
}
DerivationGoal::DerivationGoal(const StorePath & drvPath, const BasicDerivation & drv,
const OutputsSpec & wantedOutputs, Worker & worker, BuildMode buildMode)
: Goal(worker, haveDerivation(drvPath))
, drvReq(makeConstantStorePathRef(drvPath))
, wantedOutputs(wantedOutputs)
DerivationGoal::DerivationGoal(const StorePath & drvPath, const Derivation & drv,
const OutputName & wantedOutput, Worker & worker, BuildMode buildMode)
: Goal(worker, haveDerivation())
, drvPath(drvPath)
, wantedOutput(wantedOutput)
, buildMode(buildMode)
{
this->drv = std::make_unique<Derivation>(drv);
name = fmt(
"building of '%s' from in-memory derivation",
DerivedPath::Built { drvReq, drv.outputNames() }.to_string(worker.store));
DerivedPath::Built { makeConstantStorePathRef(drvPath), drv.outputNames() }.to_string(worker.store));
trace("created");
mcExpectedBuilds = std::make_unique<MaintainCount<uint64_t>>(worker.expectedBuilds);
@ -61,114 +44,20 @@ DerivationGoal::DerivationGoal(const StorePath & drvPath, const BasicDerivation
}
static StorePath pathPartOfReq(const SingleDerivedPath & req)
{
return std::visit(
overloaded{
[&](const SingleDerivedPath::Opaque & bo) { return bo.path; },
[&](const SingleDerivedPath::Built & bfd) { return pathPartOfReq(*bfd.drvPath); },
},
req.raw());
}
std::string DerivationGoal::key()
{
/* Ensure that derivations get built in order of their name,
i.e. a derivation named "aardvark" always comes before
"baboon". And substitution goals always happen before
derivation goals (due to "b$"). */
return "b$" + std::string(pathPartOfReq(*drvReq).name()) + "$" + drvReq->to_string(worker.store);
return "c$" + std::string(drvPath.name()) + "$" + SingleDerivedPath::Built{
.drvPath = makeConstantStorePathRef(drvPath),
.output = wantedOutput,
}.to_string(worker.store);
}
void DerivationGoal::addWantedOutputs(const OutputsSpec & outputs)
{
auto newWanted = wantedOutputs.union_(outputs);
switch (needRestart) {
case NeedRestartForMoreOutputs::OutputsUnmodifedDontNeed:
if (!newWanted.isSubsetOf(wantedOutputs))
needRestart = NeedRestartForMoreOutputs::OutputsAddedDoNeed;
break;
case NeedRestartForMoreOutputs::OutputsAddedDoNeed:
/* No need to check whether we added more outputs, because a
restart is already queued up. */
break;
case NeedRestartForMoreOutputs::BuildInProgressWillNotNeed:
/* We are already building all outputs, so it doesn't matter if
we now want more. */
break;
};
wantedOutputs = newWanted;
}
Goal::Co DerivationGoal::loadDerivation() {
trace("need to load derivation from file");
{
/* The first thing to do is to make sure that the derivation
exists. If it doesn't, it may be built from another
derivation, or merely substituted. We can make goal to get it
and not worry about which method it takes to get the
derivation. */
if (auto optDrvPath = [this]() -> std::optional<StorePath> {
if (buildMode != bmNormal)
return std::nullopt;
auto drvPath = StorePath::dummy;
try {
drvPath = resolveDerivedPath(worker.store, *drvReq);
} catch (MissingRealisation &) {
return std::nullopt;
}
auto cond = worker.evalStore.isValidPath(drvPath) || worker.store.isValidPath(drvPath);
return cond ? std::optional{drvPath} : std::nullopt;
}()) {
trace(
fmt("already have drv '%s' for '%s', can go straight to building",
worker.store.printStorePath(*optDrvPath),
drvReq->to_string(worker.store)));
} else {
trace("need to obtain drv we want to build");
Goals waitees{worker.makeGoal(DerivedPath::fromSingle(*drvReq))};
co_await await(std::move(waitees));
}
trace("loading derivation");
if (nrFailed != 0) {
co_return amDone(ecFailed, Error("cannot build missing derivation '%s'", drvReq->to_string(worker.store)));
}
StorePath drvPath = resolveDerivedPath(worker.store, *drvReq);
/* `drvPath' should already be a root, but let's be on the safe
side: if the user forgot to make it a root, we wouldn't want
things being garbage collected while we're busy. */
worker.evalStore.addTempRoot(drvPath);
/* Get the derivation. It is probably in the eval store, but it might be inthe main store:
- Resolved derivation are resolved against main store realisations, and so must be stored there.
- Dynamic derivations are built, and so are found in the main store.
*/
for (auto * drvStore : { &worker.evalStore, &worker.store }) {
if (drvStore->isValidPath(drvPath)) {
drv = std::make_unique<Derivation>(drvStore->readDerivation(drvPath));
break;
}
}
assert(drv);
co_return haveDerivation(drvPath);
}
}
Goal::Co DerivationGoal::haveDerivation(StorePath drvPath)
Goal::Co DerivationGoal::haveDerivation()
{
trace("have derivation");
@ -205,10 +94,17 @@ Goal::Co DerivationGoal::haveDerivation(StorePath drvPath)
trace("outer build done");
buildResult = g->getBuildResult(DerivedPath::Built{
.drvPath = makeConstantStorePathRef(drvPath),
.outputs = wantedOutputs,
});
buildResult = g->buildResult;
for (auto it = buildResult.builtOutputs.begin(); it != buildResult.builtOutputs.end(); ) {
if (it->first != wantedOutput) {
it = buildResult.builtOutputs.erase(it);
} else {
++it;
}
}
if (buildResult.success())
assert(buildResult.builtOutputs.count(wantedOutput) > 0);
co_return amDone(g->exitCode, g->ex);
};
@ -222,11 +118,9 @@ Goal::Co DerivationGoal::haveDerivation(StorePath drvPath)
if (impure) experimentalFeatureSettings.require(Xp::ImpureDerivations);
auto outputHashes = staticOutputHashes(worker.evalStore, *drv);
for (auto & [outputName, outputHash] : outputHashes) {
for (auto & [outputName, _] : drv->outputs) {
InitialOutput v{
.wanted = true, // Will be refined later
.outputHash = outputHash
};
/* TODO we might want to also allow randomizing the paths
@ -254,11 +148,11 @@ Goal::Co DerivationGoal::haveDerivation(StorePath drvPath)
{
/* Check what outputs paths are not already valid. */
auto [allValid, validOutputs] = checkPathValidity(drvPath);
auto [allValid, validOutputs] = checkPathValidity();
/* If they are all valid, then we're done. */
if (allValid && buildMode == bmNormal) {
co_return done(drvPath, BuildResult::AlreadyValid, std::move(validOutputs));
co_return done(BuildResult::AlreadyValid, std::move(validOutputs));
}
}
@ -274,7 +168,7 @@ Goal::Co DerivationGoal::haveDerivation(StorePath drvPath)
waitees.insert(
upcast_goal(
worker.makeDrvOutputSubstitutionGoal(
DrvOutput{status.outputHash, outputName},
DrvOutput{drvPath, outputName},
buildMode == bmRepair ? Repair : NoRepair
)
)
@ -295,25 +189,20 @@ Goal::Co DerivationGoal::haveDerivation(StorePath drvPath)
assert(!drv->type().isImpure());
if (nrFailed > 0 && nrFailed > nrNoSubstituters && !settings.tryFallback) {
co_return done(drvPath, BuildResult::TransientFailure, {},
co_return 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)));
}
nrFailed = nrNoSubstituters = 0;
if (needRestart == NeedRestartForMoreOutputs::OutputsAddedDoNeed) {
needRestart = NeedRestartForMoreOutputs::OutputsUnmodifedDontNeed;
co_return haveDerivation(std::move(drvPath));
}
auto [allValid, validOutputs] = checkPathValidity(drvPath);
auto [allValid, validOutputs] = checkPathValidity();
if (buildMode == bmNormal && allValid) {
co_return done(drvPath, BuildResult::Substituted, std::move(validOutputs));
co_return done(BuildResult::Substituted, std::move(validOutputs));
}
if (buildMode == bmRepair && allValid) {
co_return repairClosure(std::move(drvPath));
co_return repairClosure();
}
if (buildMode == bmCheck && !allValid)
throw Error("some outputs of '%s' are not valid, so checking is not possible",
@ -336,7 +225,7 @@ struct value_comparison
};
Goal::Co DerivationGoal::repairClosure(StorePath drvPath)
Goal::Co DerivationGoal::repairClosure()
{
assert(!drv->type().isImpure());
@ -346,11 +235,10 @@ Goal::Co DerivationGoal::repairClosure(StorePath drvPath)
that produced those outputs. */
/* Get the output closure. */
auto outputs = queryDerivationOutputMap(drvPath);
auto outputs = queryDerivationOutputMap();
StorePathSet outputClosure;
for (auto & i : outputs) {
if (!wantedOutputs.contains(i.first)) continue;
worker.store.computeFSClosure(i.second, outputClosure);
if (auto mPath = get(outputs, wantedOutput)) {
worker.store.computeFSClosure(*mPath, outputClosure);
}
/* Filter out our own outputs (which we have already checked). */
@ -404,11 +292,11 @@ Goal::Co DerivationGoal::repairClosure(StorePath drvPath)
throw Error("some paths in the output closure of derivation '%s' could not be repaired",
worker.store.printStorePath(drvPath));
}
co_return done(drvPath, BuildResult::AlreadyValid, assertPathValidity(drvPath));
co_return done(BuildResult::AlreadyValid, assertPathValidity());
}
std::map<std::string, std::optional<StorePath>> DerivationGoal::queryPartialDerivationOutputMap(const StorePath & drvPath)
std::map<std::string, std::optional<StorePath>> DerivationGoal::queryPartialDerivationOutputMap()
{
assert(!drv->type().isImpure());
@ -424,7 +312,7 @@ std::map<std::string, std::optional<StorePath>> DerivationGoal::queryPartialDeri
return res;
}
OutputPathMap DerivationGoal::queryDerivationOutputMap(const StorePath & drvPath)
OutputPathMap DerivationGoal::queryDerivationOutputMap()
{
assert(!drv->type().isImpure());
@ -440,28 +328,21 @@ OutputPathMap DerivationGoal::queryDerivationOutputMap(const StorePath & drvPath
}
std::pair<bool, SingleDrvOutputs> DerivationGoal::checkPathValidity(const StorePath & drvPath)
std::pair<bool, SingleDrvOutputs> DerivationGoal::checkPathValidity()
{
if (drv->type().isImpure()) return { false, {} };
bool checkHash = buildMode == bmRepair;
auto wantedOutputsLeft = std::visit(overloaded {
[&](const OutputsSpec::All &) {
return StringSet {};
},
[&](const OutputsSpec::Names & names) {
return static_cast<StringSet>(names);
},
}, wantedOutputs.raw);
StringSet wantedOutputsLeft{wantedOutput};
SingleDrvOutputs validOutputs;
for (auto & i : queryPartialDerivationOutputMap(drvPath)) {
for (auto & i : queryPartialDerivationOutputMap()) {
auto initialOutput = get(initialOutputs, i.first);
if (!initialOutput)
// this is an invalid output, gets catched with (!wantedOutputsLeft.empty())
continue;
auto & info = *initialOutput;
info.wanted = wantedOutputs.contains(i.first);
info.wanted = wantedOutput == i.first;
if (info.wanted)
wantedOutputsLeft.erase(i.first);
if (i.second) {
@ -475,7 +356,7 @@ std::pair<bool, SingleDrvOutputs> DerivationGoal::checkPathValidity(const StoreP
: PathStatus::Corrupt,
};
}
auto drvOutput = DrvOutput{info.outputHash, i.first};
auto drvOutput = DrvOutput{drvPath, i.first};
if (experimentalFeatureSettings.isEnabled(Xp::CaDerivations)) {
if (auto real = worker.store.queryRealisation(drvOutput)) {
info.known = {
@ -487,16 +368,21 @@ std::pair<bool, SingleDrvOutputs> DerivationGoal::checkPathValidity(const StoreP
// 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).
worker.store.registerDrvOutput(
Realisation {
drvOutput,
info.known->path,
}
);
worker.store.registerDrvOutput(Realisation {
{
.outPath = info.known->path,
},
drvOutput,
});
}
}
if (info.known && info.known->isValid())
validOutputs.emplace(i.first, Realisation { drvOutput, info.known->path });
validOutputs.emplace(i.first, Realisation {
{
.outPath = info.known->path,
},
drvOutput,
});
}
// If we requested all the outputs, we are always fine.
@ -520,9 +406,9 @@ std::pair<bool, SingleDrvOutputs> DerivationGoal::checkPathValidity(const StoreP
}
SingleDrvOutputs DerivationGoal::assertPathValidity(const StorePath & drvPath)
SingleDrvOutputs DerivationGoal::assertPathValidity()
{
auto [allValid, validOutputs] = checkPathValidity(drvPath);
auto [allValid, validOutputs] = checkPathValidity();
if (!allValid)
throw Error("some outputs are unexpectedly invalid");
return validOutputs;
@ -530,7 +416,6 @@ SingleDrvOutputs DerivationGoal::assertPathValidity(const StorePath & drvPath)
Goal::Done DerivationGoal::done(
const StorePath & drvPath,
BuildResult::Status status,
SingleDrvOutputs builtOutputs,
std::optional<Error> ex)
@ -546,7 +431,7 @@ Goal::Done DerivationGoal::done(
mcExpectedBuilds.reset();
if (buildResult.success()) {
auto wantedBuiltOutputs = filterDrvOutputs(wantedOutputs, std::move(builtOutputs));
auto wantedBuiltOutputs = filterDrvOutputs(OutputsSpec::Names{wantedOutput}, std::move(builtOutputs));
assert(!wantedBuiltOutputs.empty());
buildResult.builtOutputs = std::move(wantedBuiltOutputs);
if (status == BuildResult::Built)

View file

@ -0,0 +1,82 @@
#include "nix/store/build/derivation-resolution-goal.hh"
#include "nix/util/finally.hh"
#include "nix/store/build/worker.hh"
#include "nix/store/build/substitution-goal.hh"
#include "nix/util/callback.hh"
#include "nix/store/derivations.hh"
namespace nix {
DerivationResolutionGoal::DerivationResolutionGoal(const StorePath & drvPath, Worker & worker)
: Goal(worker, init())
, drvPath(drvPath)
{
name = fmt("resolution of '%s'", worker.store.printStorePath(drvPath));
trace("created");
}
Goal::Co DerivationResolutionGoal::init()
{
trace("init");
std::unique_ptr<Derivation> drv;
if (worker.evalStore.isValidPath(drvPath)) {
drv = std::make_unique<Derivation>(worker.evalStore.readDerivation(drvPath));
} else if (worker.store.isValidPath(drvPath)) {
drv = std::make_unique<Derivation>(worker.store.readDerivation(drvPath));
} else {
auto goal0 = worker.makePathSubstitutionGoal(drvPath);
goal0->preserveException = true;
co_await await(Goals{goal0});
if (nrFailed > 0)
co_return amDone(goal0->exitCode, goal0->ex);
drv = std::make_unique<Derivation>(worker.store.readDerivation(drvPath));
}
trace("output path substituted");
std::set<std::shared_ptr<BuildTraceGoal>> goals;
std::function<void(ref<SingleDerivedPath>, const DerivedPathMap<StringSet>::ChildNode &)> accumInputPaths;
accumInputPaths = [&](ref<SingleDerivedPath> depDrvPath, const DerivedPathMap<StringSet>::ChildNode & inputNode) {
for (auto & outputName : inputNode.value)
goals.insert(worker.makeBuildTraceGoal(SingleDerivedPath::Built{depDrvPath, outputName}));
for (auto & [outputName, childNode] : inputNode.childMap)
accumInputPaths(make_ref<SingleDerivedPath>(SingleDerivedPath::Built{depDrvPath, outputName}), childNode);
};
for (auto & [depDrvPath, depNode] : drv->inputDrvs.map)
accumInputPaths(makeConstantStorePathRef(depDrvPath), depNode);
if (nrFailed > 0) {
debug("TODO message");
co_return amDone(nrNoSubstituters > 0 ? ecNoSubstituters : ecFailed);
}
if (true /*auto d = drv.tryResolve(....)*/) {
//resolvedDerivation = d.take();
trace("finished");
co_return amDone(ecSuccess);
} else {
// fail
}
}
std::string DerivationResolutionGoal::key()
{
/* "a$" ensures substitution goals happen before derivation
goals. */
return "b$" + worker.store.printStorePath(drvPath);
}
void DerivationResolutionGoal::handleEOF(Descriptor fd)
{
worker.wakeUp(shared_from_this());
}
}

View file

@ -3,156 +3,49 @@
#include "nix/store/build/worker.hh"
#include "nix/store/build/substitution-goal.hh"
#include "nix/util/callback.hh"
#include "nix/store/store-open.hh"
#include "nix/store/build/build-trace-goal.hh"
namespace nix {
DrvOutputSubstitutionGoal::DrvOutputSubstitutionGoal(
const DrvOutput & id,
Worker & worker,
RepairFlag repair,
std::optional<ContentAddress> ca)
const DrvOutput & id, Worker & worker, RepairFlag repair, std::optional<ContentAddress> ca)
: Goal(worker, init())
, id(id)
{
name = fmt("substitution of '%s'", id.to_string());
name = fmt("substitution of '%s'", id.render(worker.store));
trace("created");
}
Goal::Co DrvOutputSubstitutionGoal::init()
{
trace("init");
/* If the derivation already exists, were done */
if (worker.store.queryRealisation(id)) {
co_return amDone(ecSuccess);
}
auto subs = settings.useSubstitutes ? getDefaultSubstituters() : std::list<ref<Store>>();
bool substituterFailed = false;
for (const auto & sub : subs) {
trace("trying next substituter");
/* The callback of the curl download below can outlive `this` (if
some other error occurs), so it must not touch `this`. So put
the shared state in a separate refcounted object. */
auto outPipe = std::make_shared<MuxablePipe>();
#ifndef _WIN32
outPipe->create();
#else
outPipe->createAsyncPipe(worker.ioport.get());
#endif
auto promise = std::make_shared<std::promise<std::shared_ptr<const Realisation>>>();
sub->queryRealisation(
id,
{ [outPipe(outPipe), promise(promise)](std::future<std::shared_ptr<const Realisation>> res) {
try {
Finally updateStats([&]() { outPipe->writeSide.close(); });
promise->set_value(res.get());
} catch (...) {
promise->set_exception(std::current_exception());
}
} });
worker.childStarted(shared_from_this(), {
#ifndef _WIN32
outPipe->readSide.get()
#else
&*outPipe
#endif
}, true, false);
co_await Suspend{};
worker.childTerminated(this);
/*
* The realisation corresponding to the given output id.
* Will be filled once we can get it.
*/
std::shared_ptr<const Realisation> outputInfo;
try {
outputInfo = promise->get_future().get();
} catch (std::exception & e) {
printError(e.what());
substituterFailed = true;
}
if (!outputInfo) continue;
bool failed = false;
Goals waitees;
for (const auto & [depId, depPath] : outputInfo->dependentRealisations) {
if (depId != id) {
if (auto localOutputInfo = worker.store.queryRealisation(depId);
localOutputInfo && localOutputInfo->outPath != depPath) {
warn(
"substituter '%s' has an incompatible realisation for '%s', ignoring.\n"
"Local: %s\n"
"Remote: %s",
sub->getUri(),
depId.to_string(),
worker.store.printStorePath(localOutputInfo->outPath),
worker.store.printStorePath(depPath)
);
failed = true;
break;
}
waitees.insert(worker.makeDrvOutputSubstitutionGoal(depId));
}
}
if (failed) continue;
co_return realisationFetched(std::move(waitees), outputInfo, sub);
}
/* None left. Terminate this goal and let someone else deal
with it. */
debug("derivation output '%s' is required, but there is no substituter that can provide it", id.to_string());
if (substituterFailed) {
worker.failedSubstitutions++;
worker.updateProgress();
}
/* Hack: don't indicate failure if there were no substituters.
In that case the calling derivation should just do a
build. */
co_return amDone(substituterFailed ? ecFailed : ecNoSubstituters);
}
Goal::Co DrvOutputSubstitutionGoal::realisationFetched(Goals waitees, std::shared_ptr<const Realisation> outputInfo, nix::ref<nix::Store> sub) {
waitees.insert(worker.makePathSubstitutionGoal(outputInfo->outPath));
co_await await(std::move(waitees));
auto goal0 = worker.makeBuildTraceGoal({
makeConstantStorePathRef(id.drvPath),
id.outputName,
});
co_await await(Goals{upcast_goal(goal0)});
trace("output path substituted");
if (nrFailed > 0) {
debug("The output path of the derivation output '%s' could not be substituted", id.to_string());
debug("The output path of the derivation output '%s' could not be substituted", id.render(worker.store));
co_return amDone(nrNoSubstituters > 0 ? ecNoSubstituters : ecFailed);
}
worker.store.registerDrvOutput(*outputInfo);
auto goal1 = worker.makePathSubstitutionGoal(goal0->outputInfo->outPath);
goal0.reset();
trace("finished");
co_return amDone(ecSuccess);
goal1->preserveException = true;
co_await await(Goals{upcast_goal(goal1)});
co_return amDone(goal1->exitCode, goal1->ex);
}
std::string DrvOutputSubstitutionGoal::key()
{
/* "a$" ensures substitution goals happen before derivation
goals. */
return "a$" + std::string(id.to_string());
return "b$" + std::string(id.render(worker.store));
}
void DrvOutputSubstitutionGoal::handleEOF(Descriptor fd)
@ -160,5 +53,4 @@ void DrvOutputSubstitutionGoal::handleEOF(Descriptor fd)
worker.wakeUp(shared_from_this());
}
}

View file

@ -1,8 +1,7 @@
#include "nix/store/derivations.hh"
#include "nix/store/build/worker.hh"
#include "nix/store/build/substitution-goal.hh"
#ifndef _WIN32 // TODO Enable building on Windows
# include "nix/store/build/derivation-goal.hh"
#endif
#include "nix/store/build/derivation-creation-and-realisation-goal.hh"
#include "nix/store/local-store.hh"
#include "nix/util/strings.hh"
@ -28,12 +27,9 @@ void Store::buildPaths(const std::vector<DerivedPath> & reqs, BuildMode buildMod
ex = std::move(i->ex);
}
if (i->exitCode != Goal::ecSuccess) {
#ifndef _WIN32 // TODO Enable building on Windows
if (auto i2 = dynamic_cast<DerivationGoal *>(i.get()))
if (auto i2 = dynamic_cast<DerivationCreationAndRealisationGoal *>(i.get()))
failed.insert(i2->drvReq->to_string(*this));
else
#endif
if (auto i2 = dynamic_cast<PathSubstitutionGoal *>(i.get()))
else if (auto i2 = dynamic_cast<PathSubstitutionGoal *>(i.get()))
failed.insert(printStorePath(i2->storePath));
}
}
@ -70,7 +66,7 @@ std::vector<KeyedBuildResult> Store::buildPathsWithResults(
for (auto & [req, goalPtr] : state)
results.emplace_back(KeyedBuildResult {
goalPtr->getBuildResult(req),
goalPtr->buildResult,
/* .path = */ req,
});
@ -81,19 +77,11 @@ BuildResult Store::buildDerivation(const StorePath & drvPath, const BasicDerivat
BuildMode buildMode)
{
Worker worker(*this, *this);
#ifndef _WIN32 // TODO Enable building on Windows
auto goal = worker.makeBasicDerivationGoal(drvPath, drv, OutputsSpec::All {}, buildMode);
#else
std::shared_ptr<Goal> goal;
throw UnimplementedError("Building derivations not yet implemented on windows.");
#endif
auto goal = worker.makeDerivationCreationAndRealisationGoal(drvPath, OutputsSpec::All {}, drv, buildMode);
try {
worker.run(Goals{goal});
return goal->getBuildResult(DerivedPath::Built {
.drvPath = makeConstantStorePathRef(drvPath),
.outputs = OutputsSpec::All {},
});
return goal->buildResult;
} catch (Error & e) {
return BuildResult {
.status = BuildResult::MiscFailure,

View file

@ -101,30 +101,6 @@ bool CompareGoalPtrs::operator() (const GoalPtr & a, const GoalPtr & b) const {
return s1 < s2;
}
BuildResult Goal::getBuildResult(const DerivedPath & req) const {
BuildResult res { buildResult };
if (auto pbp = std::get_if<DerivedPath::Built>(&req)) {
auto & bp = *pbp;
/* Because goals are in general shared between derived paths
that share the same derivation, we need to filter their
results to get back just the results we care about.
*/
for (auto it = res.builtOutputs.begin(); it != res.builtOutputs.end();) {
if (bp.outputs.contains(it->first))
++it;
else
it = res.builtOutputs.erase(it);
}
}
return res;
}
void addToWeakGoals(WeakGoals & goals, GoalPtr p)
{
if (goals.find(p) != goals.end())

View file

@ -5,9 +5,12 @@
#include "nix/store/build/drv-output-substitution-goal.hh"
#include "nix/store/build/derivation-goal.hh"
#include "nix/store/build/derivation-building-goal.hh"
#include "nix/store/build/derivation-creation-and-realisation-goal.hh"
#ifndef _WIN32 // TODO Enable building on Windows
# include "nix/store/build/hook-instance.hh"
#endif
#include "nix/store/build/build-trace-goal.hh"
#include "nix/store/build/derivation-resolution-goal.hh"
#include "nix/util/signals.hh"
namespace nix {
@ -53,52 +56,40 @@ std::shared_ptr<G> Worker::initGoalIfNeeded(std::weak_ptr<G> & goal_weak, Args &
return goal;
}
std::shared_ptr<DerivationGoal> Worker::makeDerivationGoalCommon(
std::shared_ptr<DerivationCreationAndRealisationGoal> Worker::makeDerivationCreationAndRealisationGoal(
ref<const SingleDerivedPath> drvReq,
const OutputsSpec & wantedOutputs,
std::function<std::shared_ptr<DerivationGoal>()> mkDrvGoal)
BuildMode buildMode)
{
std::weak_ptr<DerivationGoal> & goal_weak = derivationGoals.ensureSlot(*drvReq).value;
std::shared_ptr<DerivationGoal> goal = goal_weak.lock();
if (!goal) {
goal = mkDrvGoal();
goal_weak = goal;
wakeUp(goal);
} else {
goal->addWantedOutputs(wantedOutputs);
}
return goal;
return initGoalIfNeeded(
derivationCreationAndRealisationGoals.ensureSlot(*drvReq).value[wantedOutputs],
drvReq, wantedOutputs, *this, buildMode);
}
std::shared_ptr<DerivationGoal> Worker::makeDerivationGoal(ref<const SingleDerivedPath> drvReq,
const OutputsSpec & wantedOutputs, BuildMode buildMode)
std::shared_ptr<DerivationCreationAndRealisationGoal> Worker::makeDerivationCreationAndRealisationGoal(
const StorePath & drvPath,
const OutputsSpec & wantedOutputs,
const Derivation & drv,
BuildMode buildMode)
{
return makeDerivationGoalCommon(drvReq, wantedOutputs, [&]() -> std::shared_ptr<DerivationGoal> {
return std::make_shared<DerivationGoal>(drvReq, wantedOutputs, *this, buildMode);
});
return initGoalIfNeeded(
derivationCreationAndRealisationGoals.ensureSlot(DerivedPath::Opaque{drvPath}).value[wantedOutputs],
drvPath, wantedOutputs, drv, *this, buildMode);
}
std::shared_ptr<DerivationGoal> Worker::makeBasicDerivationGoal(const StorePath & drvPath,
const BasicDerivation & drv, const OutputsSpec & wantedOutputs, BuildMode buildMode)
std::shared_ptr<DerivationGoal> Worker::makeDerivationGoal(const StorePath & drvPath,
const Derivation & drv, const OutputName & wantedOutput, BuildMode buildMode)
{
return makeDerivationGoalCommon(makeConstantStorePathRef(drvPath), wantedOutputs, [&]() -> std::shared_ptr<DerivationGoal> {
return std::make_shared<DerivationGoal>(drvPath, drv, wantedOutputs, *this, buildMode);
});
return initGoalIfNeeded(derivationGoals[drvPath][wantedOutput], drvPath, drv, wantedOutput, *this, buildMode);
}
std::shared_ptr<DerivationBuildingGoal> Worker::makeDerivationBuildingGoal(const StorePath & drvPath,
const Derivation & drv, BuildMode buildMode)
{
std::weak_ptr<DerivationBuildingGoal> & goal_weak = derivationBuildingGoals[drvPath];
auto goal = goal_weak.lock(); // FIXME
if (!goal) {
goal = std::make_shared<DerivationBuildingGoal>(drvPath, drv, *this, buildMode);
goal_weak = goal;
wakeUp(goal);
}
return goal;
return initGoalIfNeeded(derivationBuildingGoals[drvPath], drvPath, drv, *this, buildMode);
}
@ -114,11 +105,38 @@ std::shared_ptr<DrvOutputSubstitutionGoal> Worker::makeDrvOutputSubstitutionGoal
}
std::shared_ptr<BuildTraceGoal> Worker::makeBuildTraceGoal(
const SingleDerivedPath::Built & req)
{
std::weak_ptr<BuildTraceGoal> & goal_weak = buildTraceGoals.ensureSlot(req).value;
std::shared_ptr<BuildTraceGoal> goal = goal_weak.lock();
if (!goal) {
goal = std::make_shared<BuildTraceGoal>(req, *this);
goal_weak = goal;
wakeUp(goal);
}
return goal;
}
std::shared_ptr<DerivationResolutionGoal> Worker::makeDerivationResolutionGoal(const StorePath & drvPath)
{
std::weak_ptr<DerivationResolutionGoal> & goal_weak = derivationResolutionGoals[drvPath];
auto goal = goal_weak.lock(); // FIXME
if (!goal) {
goal = std::make_shared<DerivationResolutionGoal>(drvPath, *this);
goal_weak = goal;
wakeUp(goal);
}
return goal;
}
GoalPtr Worker::makeGoal(const DerivedPath & req, BuildMode buildMode)
{
return std::visit(overloaded {
[&](const DerivedPath::Built & bfd) -> GoalPtr {
return makeDerivationGoal(bfd.drvPath, bfd.outputs, buildMode);
return makeDerivationCreationAndRealisationGoal(bfd.drvPath, bfd.outputs, buildMode);
},
[&](const DerivedPath::Opaque & bo) -> GoalPtr {
return makePathSubstitutionGoal(bo.path, buildMode == bmRepair ? Repair : NoRepair);
@ -128,44 +146,52 @@ GoalPtr Worker::makeGoal(const DerivedPath & req, BuildMode buildMode)
template<typename K, typename V, typename F>
static void cullMap(std::map<K, V> & goalMap, F f)
static void cullMap(std::map<K, V> & goalMap, F && f)
{
for (auto i = goalMap.begin(); i != goalMap.end();)
if (!f(i->second))
if (!std::forward<F>(f)(i->second))
i = goalMap.erase(i);
else ++i;
}
template<typename K, typename G>
static void removeGoal(std::shared_ptr<G> goal, std::map<K, std::weak_ptr<G>> & goalMap)
template<typename G>
static bool removeGoal(std::shared_ptr<G> goal, std::weak_ptr<G> & gp)
{
/* !!! inefficient */
cullMap(goalMap, [&](const std::weak_ptr<G> & gp) -> bool {
return gp.lock() != goal;
});
return gp.lock() != goal;
}
template<typename K>
static void removeGoal(std::shared_ptr<DerivationGoal> goal, std::map<K, DerivedPathMap<std::weak_ptr<DerivationGoal>>::ChildNode> & goalMap);
template<typename K>
static void removeGoal(std::shared_ptr<DerivationGoal> goal, std::map<K, DerivedPathMap<std::weak_ptr<DerivationGoal>>::ChildNode> & goalMap)
template<typename K, typename G, typename Inner>
static bool removeGoal(std::shared_ptr<G> goal, std::map<K, Inner> & goalMap);
template<typename K, typename G, typename Inner>
static bool removeGoal(std::shared_ptr<G> goal, std::map<K, Inner> & goalMap)
{
/* !!! inefficient */
cullMap(goalMap, [&](DerivedPathMap<std::weak_ptr<DerivationGoal>>::ChildNode & node) -> bool {
if (node.value.lock() == goal)
node.value.reset();
removeGoal(goal, node.childMap);
return !node.value.expired() || !node.childMap.empty();
cullMap(goalMap, [&](Inner & inner) -> bool {
return removeGoal(goal, inner);
});
return !goalMap.empty();
}
template<typename G>
static bool removeGoal(std::shared_ptr<G> goal, typename DerivedPathMap<std::map<OutputsSpec, std::weak_ptr<G>>>::ChildNode & node);
template<typename G>
static bool removeGoal(std::shared_ptr<G> goal, typename DerivedPathMap<std::map<OutputsSpec, std::weak_ptr<G>>>::ChildNode & node)
{
return removeGoal(goal, node.value) || removeGoal(goal, node.childMap);
}
void Worker::removeGoal(GoalPtr goal)
{
if (auto drvGoal = std::dynamic_pointer_cast<DerivationGoal>(goal))
nix::removeGoal(drvGoal, derivationGoals.map);
if (auto drvGoal = std::dynamic_pointer_cast<DerivationCreationAndRealisationGoal>(goal))
nix::removeGoal(drvGoal, derivationCreationAndRealisationGoals.map);
else if (auto drvGoal = std::dynamic_pointer_cast<DerivationGoal>(goal))
nix::removeGoal(drvGoal, derivationGoals);
else if (auto drvBuildingGoal = std::dynamic_pointer_cast<DerivationBuildingGoal>(goal))
nix::removeGoal(drvBuildingGoal, derivationBuildingGoals);
else if (auto subGoal = std::dynamic_pointer_cast<PathSubstitutionGoal>(goal))
@ -312,7 +338,7 @@ void Worker::run(const Goals & _topGoals)
for (auto & i : _topGoals) {
topGoals.insert(i);
if (auto goal = dynamic_cast<DerivationGoal *>(i.get())) {
if (auto goal = dynamic_cast<DerivationCreationAndRealisationGoal *>(i.get())) {
topPaths.push_back(DerivedPath::Built {
.drvPath = goal->drvReq,
.outputs = goal->wantedOutputs,
@ -579,4 +605,14 @@ GoalPtr upcast_goal(std::shared_ptr<DerivationGoal> subGoal)
return subGoal;
}
GoalPtr upcast_goal(std::shared_ptr<BuildTraceGoal> subGoal)
{
return subGoal;
}
GoalPtr upcast_goal(std::shared_ptr<DerivationResolutionGoal> subGoal)
{
return subGoal;
}
}

View file

@ -8,34 +8,11 @@ create table if not exists Realisations (
outputName text not null, -- symbolic output id, usually "out"
outputPath integer not null,
signatures text, -- space-separated list
foreign key (outputPath) references ValidPaths(id) on delete cascade
-- No such foreign key because we may well want realisations for
-- garbage-collected dependencies
--foreign key (outputPath) references ValidPaths(id) on delete cascade
);
create index if not exists IndexRealisations on Realisations(drvPath, outputName);
-- We can end-up in a weird edge-case where a path depends on itself because
-- its an output of a CA derivation, that happens to be the same as one of its
-- dependencies.
-- In that case we have a dependency loop (path -> realisation1 -> realisation2
-- -> path) that we need to break by removing the dependencies between the
-- realisations
create trigger if not exists DeleteSelfRefsViaRealisations before delete on ValidPaths
begin
delete from RealisationsRefs where realisationReference in (
select id from Realisations where outputPath = old.id
);
end;
create table if not exists RealisationsRefs (
referrer integer not null,
realisationReference integer,
foreign key (referrer) references Realisations(id) on delete cascade,
foreign key (realisationReference) references Realisations(id) on delete restrict
);
-- used by deletion trigger
create index if not exists IndexRealisationsRefsRealisationReference on RealisationsRefs(realisationReference);
-- used by QueryRealisationReferences
create index if not exists IndexRealisationsRefs on RealisationsRefs(referrer);
-- used by cascade deletion when ValidPaths is deleted
create index if not exists IndexRealisationsRefsOnOutputPath on Realisations(outputPath);

View file

@ -46,32 +46,6 @@ void CommonProto::Serialise<ContentAddress>::write(const StoreDirConfig & store,
}
Realisation CommonProto::Serialise<Realisation>::read(const StoreDirConfig & store, CommonProto::ReadConn conn)
{
std::string rawInput = readString(conn.from);
return Realisation::fromJSON(
nlohmann::json::parse(rawInput),
"remote-protocol"
);
}
void CommonProto::Serialise<Realisation>::write(const StoreDirConfig & store, CommonProto::WriteConn conn, const Realisation & realisation)
{
conn.to << realisation.toJSON().dump();
}
DrvOutput CommonProto::Serialise<DrvOutput>::read(const StoreDirConfig & store, CommonProto::ReadConn conn)
{
return DrvOutput::parse(readString(conn.from));
}
void CommonProto::Serialise<DrvOutput>::write(const StoreDirConfig & store, CommonProto::WriteConn conn, const DrvOutput & drvOutput)
{
conn.to << drvOutput.to_string();
}
std::optional<StorePath> CommonProto::Serialise<std::optional<StorePath>>::read(const StoreDirConfig & store, CommonProto::ReadConn conn)
{
auto s = readString(conn.from);

View file

@ -961,32 +961,30 @@ static void performOp(TunnelLogger * logger, ref<Store> store,
case WorkerProto::Op::RegisterDrvOutput: {
logger->startWork();
if (GET_PROTOCOL_MINOR(conn.protoVersion) < 31) {
auto outputId = DrvOutput::parse(readString(conn.from));
auto outputPath = StorePath(readString(conn.from));
store->registerDrvOutput(Realisation{
.id = outputId, .outPath = outputPath});
} else {
auto realisation = WorkerProto::Serialise<Realisation>::read(*store, rconn);
store->registerDrvOutput(realisation);
}
// TODO move to WorkerProto::Serialise<DrvOutput> and friends
//if (GET_PROTOCOL_MINOR(conn.protoVersion) < 39) {
// throw Error("old-style build traces no longer supported");
//}
auto realisation = WorkerProto::Serialise<Realisation>::read(*store, rconn);
store->registerDrvOutput(realisation);
logger->stopWork();
break;
}
case WorkerProto::Op::QueryRealisation: {
logger->startWork();
auto outputId = DrvOutput::parse(readString(conn.from));
auto info = store->queryRealisation(outputId);
auto outputId = WorkerProto::Serialise<DrvOutput>::read(*store, rconn);
std::optional<UnkeyedRealisation> info = *store->queryRealisation(outputId);
logger->stopWork();
if (GET_PROTOCOL_MINOR(conn.protoVersion) < 31) {
std::set<StorePath> outPaths;
if (info) outPaths.insert(info->outPath);
WorkerProto::write(*store, wconn, outPaths);
} else if (GET_PROTOCOL_MINOR(conn.protoVersion) < 39) {
// No longer support this format
WorkerProto::write(*store, wconn, StringSet{});
} else {
std::set<Realisation> realisations;
if (info) realisations.insert(*info);
WorkerProto::write(*store, wconn, realisations);
WorkerProto::write(*store, wconn, info);
}
break;
}

View file

@ -787,7 +787,7 @@ Sync<DrvHashes> drvHashes;
/* Look up the derivation by value and memoize the
`hashDerivationModulo` call.
*/
static const DrvHash pathDerivationModulo(Store & store, const StorePath & drvPath)
static const DrvHashModulo & pathDerivationModulo(Store & store, const StorePath & drvPath)
{
{
auto hashes = drvHashes.lock();
@ -796,13 +796,16 @@ static const DrvHash pathDerivationModulo(Store & store, const StorePath & drvPa
return h->second;
}
}
auto h = hashDerivationModulo(
store,
store.readInvalidDerivation(drvPath),
false);
// Cache it
drvHashes.lock()->insert_or_assign(drvPath, h);
return h;
auto [iter, _] = drvHashes.lock()->insert_or_assign(
drvPath,
hashDerivationModulo(
store,
store.readInvalidDerivation(drvPath),
false));
return iter->second;
}
/* See the header for interface details. These are the implementation details.
@ -822,12 +825,10 @@ static const DrvHash pathDerivationModulo(Store & store, const StorePath & drvPa
don't leak the provenance of fixed outputs, reducing pointless cache
misses as the build itself won't know this.
*/
DrvHash hashDerivationModulo(Store & store, const Derivation & drv, bool maskOutputs)
DrvHashModulo hashDerivationModulo(Store & store, const Derivation & drv, bool maskOutputs)
{
auto type = drv.type();
/* Return a fixed hash for fixed-output derivations. */
if (type.isFixed()) {
if (drv.type().isFixed()) {
std::map<std::string, Hash> outputHashes;
for (const auto & i : drv.outputs) {
auto & dof = std::get<DerivationOutput::CAFixed>(i.second.raw);
@ -837,58 +838,69 @@ DrvHash hashDerivationModulo(Store & store, const Derivation & drv, bool maskOut
+ store.printStorePath(dof.path(store, drv.name, i.first)));
outputHashes.insert_or_assign(i.first, std::move(hash));
}
return DrvHash {
.hashes = outputHashes,
.kind = DrvHash::Kind::Regular,
};
return outputHashes;
}
auto kind = std::visit(overloaded {
if (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;
return false;
},
[](const DerivationType::ContentAddressed & ca) {
return ca.fixed
? DrvHash::Kind::Regular
: DrvHash::Kind::Deferred;
// Already covered
assert(!ca.fixed);
return true;
},
[](const DerivationType::Impure &) -> DrvHash::Kind {
return DrvHash::Kind::Deferred;
[](const DerivationType::Impure &) {
return true;
}
}, drv.type().raw);
}, drv.type().raw)) {
return DrvHashModulo::DeferredDrv{};
}
/* For other derivations, replace the inputs paths with recursive
calls to this function. */
DerivedPathMap<StringSet>::ChildNode::Map inputs2;
for (auto & [drvPath, node] : drv.inputDrvs.map) {
/* Need to build and resolve dynamic derivations first */
if (!node.childMap.empty()) {
return DrvHashModulo::DeferredDrv{};
}
const auto & res = pathDerivationModulo(store, drvPath);
if (res.kind == DrvHash::Kind::Deferred)
kind = DrvHash::Kind::Deferred;
for (auto & outputName : node.value) {
const auto h = get(res.hashes, outputName);
if (!h)
throw Error("no hash for output '%s' of derivation '%s'", outputName, drv.name);
inputs2[h->to_string(HashFormat::Base16, false)].value.insert(outputName);
if (std::visit(overloaded {
[&](const DrvHashModulo::DeferredDrv &) {
return true;
},
// Regular non-CA derivation, replace derivation
[&](const DrvHashModulo::DrvHash & drvHash) {
inputs2.insert_or_assign(
drvHash.to_string(HashFormat::Base16, false),
node);
return false;
},
// CA derivation's output hashes
[&](const DrvHashModulo::CaOutputHashes & outputHashes) {
for (auto & outputName : node.value) {
/* Put each one in with a single "out" output.. */
const auto h = get(outputHashes, outputName);
if (!h)
throw Error("no hash for output '%s' of derivation '%s'", outputName, drv.name);
inputs2.insert_or_assign(
h->to_string(HashFormat::Base16, false),
DerivedPathMap<StringSet>::ChildNode{
.value = {"out"},
});
}
return false;
},
}, res.raw)) {
return DrvHashModulo::DeferredDrv{};
}
}
auto hash = hashString(HashAlgorithm::SHA256, drv.unparse(store, maskOutputs, &inputs2));
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)
{
return hashDerivationModulo(store, drv, true).hashes;
return hashString(HashAlgorithm::SHA256, drv.unparse(store, maskOutputs, &inputs2));
}
@ -1029,25 +1041,39 @@ void BasicDerivation::applyRewrites(const StringMap & rewrites)
env = std::move(newEnv);
}
static void rewriteDerivation(Store & store, BasicDerivation & drv, const StringMap & rewrites)
void resolveInputAddressed(Store & store, Derivation & drv)
{
drv.applyRewrites(rewrites);
std::optional<DrvHashModulo> hashModulo_;
auto hashModulo = [&]() -> const auto & {
if (!hashModulo_) {
// somewhat expensive so we do lazily
hashModulo_ = hashDerivationModulo(store, drv, true);
}
return *hashModulo_;
};
auto hashModulo = hashDerivationModulo(store, Derivation(drv), true);
for (auto & [outputName, output] : drv.outputs) {
if (std::holds_alternative<DerivationOutput::Deferred>(output.raw)) {
auto h = get(hashModulo.hashes, outputName);
if (!h)
throw Error("derivation '%s' output '%s' has no hash (derivations.cc/rewriteDerivation)",
drv.name, outputName);
auto outPath = store.makeOutputPath(outputName, *h, drv.name);
drv.env[outputName] = store.printStorePath(outPath);
output = DerivationOutput::InputAddressed {
.path = std::move(outPath),
};
std::visit(overloaded {
[&](const DrvHashModulo::DrvHash & drvHash) {
auto outPath = store.makeOutputPath(outputName, drvHash, drv.name);
drv.env.insert_or_assign(outputName, store.printStorePath(outPath));
output = DerivationOutput::InputAddressed {
.path = std::move(outPath),
};
},
[&](const DrvHashModulo::CaOutputHashes &) {
/* Shouldn't happen as the original output is
deferred (waiting to be input-addressed). */
assert(false);
},
[&](const DrvHashModulo::DeferredDrv &) {
// Nothing to do, already deferred
},
}, hashModulo().raw);
}
}
}
std::optional<BasicDerivation> Derivation::tryResolve(Store & store, Store * evalStore) const
@ -1118,9 +1144,13 @@ std::optional<BasicDerivation> Derivation::tryResolve(
nullptr, make_ref<const SingleDerivedPath>(SingleDerivedPath::Opaque{inputDrv}), inputNode, queryResolutionChain))
return std::nullopt;
rewriteDerivation(store, resolved, inputRewrites);
resolved.applyRewrites(inputRewrites);
return resolved;
Derivation resolved2{std::move(resolved)};
resolveInputAddressed(store, resolved2);
return resolved2;
}
@ -1148,38 +1178,73 @@ void Derivation::checkInvariants(Store & store, const StorePath & drvPath) const
// combinations that are currently prohibited.
type();
std::optional<DrvHash> hashesModulo;
for (auto & i : outputs) {
std::optional<DrvHashModulo> hashModulo_;
auto hashModulo = [&]() -> const auto & {
if (!hashModulo_) {
// somewhat expensive so we do lazily
hashModulo_ = hashDerivationModulo(store, *this, true);
}
return *hashModulo_;
};
for (auto & [outputName, output] : outputs) {
std::visit(overloaded {
[&](const DerivationOutput::InputAddressed & doia) {
if (!hashesModulo) {
// somewhat expensive so we do lazily
hashesModulo = hashDerivationModulo(store, *this, true);
}
auto currentOutputHash = get(hashesModulo->hashes, i.first);
if (!currentOutputHash)
throw Error("derivation '%s' has unexpected output '%s' (local-store / hashesModulo) named '%s'",
store.printStorePath(drvPath), store.printStorePath(doia.path), i.first);
StorePath recomputed = store.makeOutputPath(i.first, *currentOutputHash, drvName);
if (doia.path != recomputed)
throw Error("derivation '%s' has incorrect output '%s', should be '%s'",
store.printStorePath(drvPath), store.printStorePath(doia.path), store.printStorePath(recomputed));
envHasRightPath(doia.path, i.first);
std::visit(overloaded {
[&](const DrvHashModulo::DrvHash & drvHash) {
StorePath recomputed = store.makeOutputPath(outputName, drvHash, drvName);
if (doia.path != recomputed)
throw Error(
"derivation '%s' has incorrect output '%s', should be '%s'",
store.printStorePath(drvPath),
store.printStorePath(doia.path),
store.printStorePath(recomputed));
},
[&](const DrvHashModulo::CaOutputHashes &) {
/* Shouldn't happen as the original output is
input-addressed. */
assert(false);
},
[&](const DrvHashModulo::DeferredDrv &) {
throw Error(
"derivation '%s' has output '%s', but derivation is not yet ready to be input-addressed",
store.printStorePath(drvPath),
store.printStorePath(doia.path));
},
}, hashModulo().raw);
envHasRightPath(doia.path, outputName);
},
[&](const DerivationOutput::CAFixed & dof) {
auto path = dof.path(store, drvName, i.first);
envHasRightPath(path, i.first);
auto path = dof.path(store, drvName, outputName);
envHasRightPath(path, outputName);
},
[&](const DerivationOutput::CAFloating &) {
/* Nothing to check */
},
[&](const DerivationOutput::Deferred &) {
/* Nothing to check */
std::visit(overloaded {
[&](const DrvHashModulo::DrvHash & drvHash) {
throw Error(
"derivation '%s' has deferred output '%s', yet is ready to be input-addressed",
store.printStorePath(drvPath),
outputName);
},
[&](const DrvHashModulo::CaOutputHashes &) {
/* Shouldn't happen as the original output is
input-addressed. */
assert(false);
},
[&](const DrvHashModulo::DeferredDrv &) {
/* Nothing to check */
},
}, hashModulo().raw);
},
[&](const DerivationOutput::Impure &) {
/* Nothing to check */
},
}, i.second.raw);
}, output.raw);
}
}

View file

@ -52,7 +52,9 @@ typename DerivedPathMap<V>::ChildNode * DerivedPathMap<V>::findSlot(const Single
// instantiations
#include "nix/store/build/derivation-goal.hh"
#include "nix/store/build/build-trace-goal.hh"
#include "nix/store/build/derivation-creation-and-realisation-goal.hh"
namespace nix {
template<>
@ -69,7 +71,7 @@ std::strong_ordering DerivedPathMap<StringSet>::ChildNode::operator <=> (
template struct DerivedPathMap<StringSet>::ChildNode;
template struct DerivedPathMap<StringSet>;
template struct DerivedPathMap<std::weak_ptr<DerivationGoal>>;
template struct DerivedPathMap<std::weak_ptr<BuildTraceGoal>>;
template struct DerivedPathMap<std::map<OutputsSpec, std::weak_ptr<DerivationCreationAndRealisationGoal>>>;
};

View file

@ -41,49 +41,30 @@ nlohmann::json DerivedPath::Opaque::toJSON(const StoreDirConfig & store) const
return store.printStorePath(path);
}
nlohmann::json SingleDerivedPath::Built::toJSON(Store & store) const {
nlohmann::json res;
res["drvPath"] = drvPath->toJSON(store);
// Fallback for the input-addressed derivation case: We expect to always be
// able to print the output paths, so lets do it
// FIXME try-resolve on drvPath
const auto outputMap = store.queryPartialDerivationOutputMap(resolveDerivedPath(store, *drvPath));
res["output"] = output;
auto outputPathIter = outputMap.find(output);
if (outputPathIter == outputMap.end())
res["outputPath"] = nullptr;
else if (std::optional p = outputPathIter->second)
res["outputPath"] = store.printStorePath(*p);
else
res["outputPath"] = nullptr;
return res;
nlohmann::json SingleDerivedPath::Built::toJSON(const StoreDirConfig & store) const
{
return nlohmann::json{
{"drvPath", drvPath->toJSON(store)},
{"output", output},
};
}
nlohmann::json DerivedPath::Built::toJSON(Store & store) const {
nlohmann::json res;
res["drvPath"] = drvPath->toJSON(store);
// Fallback for the input-addressed derivation case: We expect to always be
// able to print the output paths, so lets do it
// FIXME try-resolve on drvPath
const auto outputMap = store.queryPartialDerivationOutputMap(resolveDerivedPath(store, *drvPath));
for (const auto & [output, outputPathOpt] : outputMap) {
if (!outputs.contains(output)) continue;
if (outputPathOpt)
res["outputs"][output] = store.printStorePath(*outputPathOpt);
else
res["outputs"][output] = nullptr;
}
return res;
nlohmann::json DerivedPath::Built::toJSON(const StoreDirConfig & store) const
{
return nlohmann::json{
{"drvPath", drvPath->toJSON(store)},
{"outputs", outputs},
};
}
nlohmann::json SingleDerivedPath::toJSON(Store & store) const
nlohmann::json SingleDerivedPath::toJSON(const StoreDirConfig & store) const
{
return std::visit([&](const auto & buildable) {
return buildable.toJSON(store);
}, raw());
}
nlohmann::json DerivedPath::toJSON(Store & store) const
nlohmann::json DerivedPath::toJSON(const StoreDirConfig & store) const
{
return std::visit([&](const auto & buildable) {
return buildable.toJSON(store);

View file

@ -80,7 +80,7 @@ struct DummyStore : virtual Store
{ unsupported("narFromPath"); }
void queryRealisationUncached(const DrvOutput &,
Callback<std::shared_ptr<const Realisation>> callback) noexcept override
Callback<std::shared_ptr<const UnkeyedRealisation>> callback) noexcept override
{ callback(nullptr); }
virtual ref<SourceAccessor> getFSAccessor(bool requireValidPath) override

View file

@ -71,13 +71,27 @@ private:
protected:
// The prefix under which realisation infos will be stored
const std::string realisationsPrefix = "realisations";
/**
* The prefix under which realisation infos will be stored
*
* @note The previous (still experimental, though) hash-keyed
* realisations were under "realisations". "build trace" is a better
* name anyways (issue #11895), and this serves as some light
* versioning.
*/
constexpr const static std::string realisationsPrefix = "build-trace";
const std::string cacheInfoFile = "nix-cache-info";
constexpr const static std::string cacheInfoFile = "nix-cache-info";
BinaryCacheStore(Config &);
/**
* Compute the path to the given realisation
*
* It's `${realisationsPrefix}/${drvPath}/${outputName}`.
*/
std::string makeRealisationPath(const DrvOutput & id);
public:
virtual bool fileExists(const std::string & path) = 0;
@ -161,7 +175,7 @@ public:
void registerDrvOutput(const Realisation & info) override;
void queryRealisationUncached(const DrvOutput &,
Callback<std::shared_ptr<const Realisation>> callback) noexcept override;
Callback<std::shared_ptr<const UnkeyedRealisation>> callback) noexcept override;
void narFromPath(const StorePath & path, Sink & sink) override;

View file

@ -0,0 +1,54 @@
#pragma once
///@file
#include <thread>
#include <future>
#include "nix/store/store-api.hh"
#include "nix/store/build/goal.hh"
#include "nix/store/realisation.hh"
#include "nix/util/muxable-pipe.hh"
namespace nix {
class Worker;
/**
* Try to recursively obtain build trace key-value pairs in order to
* resolve the given output deriving path.
*/
class BuildTraceGoal : public Goal
{
/**
* The output derivation path we're trying to reasolve.
*/
SingleDerivedPath::Built id;
public:
BuildTraceGoal(const SingleDerivedPath::Built & id, Worker & worker);
/**
* The realisation corresponding to the given output id.
* Will be filled once we can get it.
*/
std::shared_ptr<const UnkeyedRealisation> outputInfo;
Co init();
void timedOut(Error && ex) override
{
unreachable();
};
std::string key() override;
void handleEOF(Descriptor fd) override;
JobCategory jobCategory() const override
{
return JobCategory::Substitution;
};
};
}

View file

@ -44,7 +44,6 @@ struct InitialOutputStatus
struct InitialOutput
{
bool wanted;
Hash outputHash;
std::optional<InitialOutputStatus> known;
};

View file

@ -0,0 +1,91 @@
#pragma once
#include "nix/store/parsed-derivations.hh"
#include "nix/store/store-api.hh"
#include "nix/store/pathlocks.hh"
#include "nix/store/build/goal.hh"
namespace nix {
/**
* This goal type is essentially the serial composition (like function
* composition) of a goal for getting a derivation, and then a
* `DerivationGoal` using the newly-obtained derivation.
*
* In the (currently experimental) general inductive case of derivations
* that are themselves build outputs, that first goal will be *another*
* `DerivationCreationAndRealisationGoal`. In the (much more common) base-case
* where the derivation has no provence and is just referred to by
* (content-addressed) store path, that first goal is a
* `SubstitutionGoal`.
*
* If we already have the derivation (e.g. if the evaluator has created
* the derivation locally and then instructured the store to build it),
* we can skip the first goal entirely as a small optimization.
*/
struct DerivationCreationAndRealisationGoal : public Goal
{
/**
* How to obtain a store path of the derivation to build.
*/
ref<const SingleDerivedPath> drvReq;
/**
* The path of the derivation, once obtained.
**/
std::optional<StorePath> optDrvPath;
/**
* The specific outputs that we need to build.
*/
OutputsSpec wantedOutputs;
/**
* The final output paths of the build.
*
* - For input-addressed derivations, always the precomputed paths
*
* - For content-addressing 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;
DerivationCreationAndRealisationGoal(
ref<const SingleDerivedPath> drvReq,
const OutputsSpec & wantedOutputs,
Worker & worker,
BuildMode buildMode = bmNormal);
DerivationCreationAndRealisationGoal(
const StorePath & drvPath,
const OutputsSpec & wantedOutputs,
const Derivation & drv,
Worker & worker,
BuildMode buildMode = bmNormal);
virtual ~DerivationCreationAndRealisationGoal();
void timedOut(Error && ex) override;
std::string key() override;
Co init();
Co haveDerivation(StorePath drvPath, Derivation drv);
JobCategory jobCategory() const override
{
return JobCategory::Administration;
};
private:
/**
* Shared between both constructors
*/
void commonInit();
};
}

View file

@ -23,46 +23,23 @@ void runPostBuildHook(
/**
* A goal for building some or all of the outputs of a derivation.
*
* The derivation must already be present, either in the store in a drv
* or in memory. If the derivation itself needs to be gotten first, a
* `DerivationCreationAndRealisationGoal` goal must be used instead.
*/
struct DerivationGoal : public Goal
{
/** The path of the derivation. */
ref<const SingleDerivedPath> drvReq;
StorePath drvPath;
/**
* The specific outputs that we need to build.
*/
OutputsSpec wantedOutputs;
OutputName wantedOutput;
/**
* See `needRestart`; just for that field.
*/
enum struct NeedRestartForMoreOutputs {
/**
* The goal state machine is progressing based on the current value of
* `wantedOutputs. No actions are needed.
*/
OutputsUnmodifedDontNeed,
/**
* `wantedOutputs` has been extended, but the state machine is
* proceeding according to its old value, so we need to restart.
*/
OutputsAddedDoNeed,
/**
* The goal state machine has progressed to the point of doing a build,
* in which case all outputs will be produced, so extensions to
* `wantedOutputs` no longer require a restart.
*/
BuildInProgressWillNotNeed,
};
/**
* Whether additional wanted outputs have been added.
*/
NeedRestartForMoreOutputs needRestart = NeedRestartForMoreOutputs::OutputsUnmodifedDontNeed;
/**
* The derivation stored at `drvReq`.
* The derivation stored at drvPath.
*/
std::unique_ptr<Derivation> drv;
@ -76,11 +53,8 @@ struct DerivationGoal : public Goal
std::unique_ptr<MaintainCount<uint64_t>> mcExpectedBuilds;
DerivationGoal(ref<const SingleDerivedPath> drvReq,
const OutputsSpec & wantedOutputs, Worker & worker,
BuildMode buildMode = bmNormal);
DerivationGoal(const StorePath & drvPath, const BasicDerivation & drv,
const OutputsSpec & wantedOutputs, Worker & worker,
DerivationGoal(const StorePath & drvPath, const Derivation & drv,
const OutputName & wantedOutput, Worker & worker,
BuildMode buildMode = bmNormal);
~DerivationGoal() = default;
@ -96,16 +70,15 @@ struct DerivationGoal : public Goal
/**
* The states.
*/
Co loadDerivation();
Co haveDerivation(StorePath drvPath);
Co haveDerivation();
/**
* Wrappers around the corresponding Store methods that first consult the
* derivation. This is currently needed because when there is no drv file
* there also is no DB entry.
*/
std::map<std::string, std::optional<StorePath>> queryPartialDerivationOutputMap(const StorePath & drvPath);
OutputPathMap queryDerivationOutputMap(const StorePath & drvPath);
std::map<std::string, std::optional<StorePath>> queryPartialDerivationOutputMap();
OutputPathMap queryDerivationOutputMap();
/**
* Update 'initialOutputs' to determine the current status of the
@ -113,18 +86,17 @@ struct DerivationGoal : public Goal
* whether all outputs are valid and non-corrupt, and a
* 'SingleDrvOutputs' structure containing the valid outputs.
*/
std::pair<bool, SingleDrvOutputs> checkPathValidity(const StorePath & drvPath);
std::pair<bool, SingleDrvOutputs> checkPathValidity();
/**
* Aborts if any output is not valid or corrupt, and otherwise
* returns a 'SingleDrvOutputs' structure containing all outputs.
*/
SingleDrvOutputs assertPathValidity(const StorePath & drvPath);
SingleDrvOutputs assertPathValidity();
Co repairClosure(StorePath drvPath);
Co repairClosure();
Done done(
const StorePath & drvPath,
BuildResult::Status status,
SingleDrvOutputs builtOutputs = {},
std::optional<Error> ex = {});

View file

@ -0,0 +1,60 @@
#pragma once
///@file
#include <thread>
#include <future>
#include "nix/store/store-api.hh"
#include "nix/store/build/goal.hh"
#include "nix/store/realisation.hh"
#include "nix/util/muxable-pipe.hh"
namespace nix {
class Worker;
/**
* The purpose of this is to resolve the given derivation, so that it
* only has constant deriving paths as inputs.
*/
class DerivationResolutionGoal : public Goal
{
/**
* The derivation we're trying to substitute
*/
StorePath drvPath;
public:
DerivationResolutionGoal(const StorePath & storePath, Worker & worker);
/**
* The resolved derivation, if we succeeded.
*/
std::shared_ptr<BasicDerivation> resolvedDrv;
/**
* The path to derivation above, if we succeeded.
*
* Garbage that should not be read otherwise.
*/
StorePath resolvedDrvPath = StorePath::dummy;
Co init();
void timedOut(Error && ex) override
{
unreachable();
};
std::string key() override;
void handleEOF(Descriptor fd) override;
JobCategory jobCategory() const override
{
return JobCategory::Substitution;
};
};
}

View file

@ -20,7 +20,8 @@ class Worker;
* 2. Substitute the corresponding output path
* 3. Register the output info
*/
class DrvOutputSubstitutionGoal : public Goal {
class DrvOutputSubstitutionGoal : public Goal
{
/**
* The drv output we're trying to substitute
@ -28,21 +29,25 @@ class DrvOutputSubstitutionGoal : public Goal {
DrvOutput id;
public:
DrvOutputSubstitutionGoal(const DrvOutput& id, Worker & worker, RepairFlag repair = NoRepair, std::optional<ContentAddress> ca = std::nullopt);
typedef void (DrvOutputSubstitutionGoal::*GoalState)();
GoalState state;
DrvOutputSubstitutionGoal(
const DrvOutput & id,
Worker & worker,
RepairFlag repair = NoRepair,
std::optional<ContentAddress> ca = std::nullopt);
Co init();
Co realisationFetched(Goals waitees, std::shared_ptr<const Realisation> outputInfo, nix::ref<nix::Store> sub);
void timedOut(Error && ex) override { unreachable(); };
void timedOut(Error && ex) override
{
unreachable();
};
std::string key() override;
void handleEOF(Descriptor fd) override;
JobCategory jobCategory() const override {
JobCategory jobCategory() const override
{
return JobCategory::Substitution;
};
};

View file

@ -105,13 +105,11 @@ public:
*/
ExitCode exitCode = ecBusy;
protected:
/**
* Build result.
*/
BuildResult buildResult;
public:
/**
* Suspend our goal and wait until we get `work`-ed again.
* `co_await`-able by @ref Co.
@ -358,18 +356,6 @@ protected:
public:
virtual void cleanup() { }
/**
* Project a `BuildResult` with just the information that pertains
* to the given request.
*
* In general, goals may be aliased between multiple requests, and
* the stored `BuildResult` has information for the union of all
* requests. We don't want to leak what the other request are for
* sake of both privacy and determinism, and this "safe accessor"
* ensures we don't.
*/
BuildResult getBuildResult(const DerivedPath &) const;
/**
* Hack to say that this goal should not log `ex`, but instead keep
* it around. Set by a waitee which sees itself as the designated

View file

@ -14,10 +14,13 @@
namespace nix {
/* Forward definition. */
struct DerivationCreationAndRealisationGoal;
struct DerivationGoal;
struct DerivationBuildingGoal;
struct PathSubstitutionGoal;
class DrvOutputSubstitutionGoal;
class BuildTraceGoal;
class DerivationResolutionGoal;
/**
* Workaround for not being able to declare a something like
@ -33,6 +36,9 @@ class DrvOutputSubstitutionGoal;
*/
GoalPtr upcast_goal(std::shared_ptr<PathSubstitutionGoal> subGoal);
GoalPtr upcast_goal(std::shared_ptr<DrvOutputSubstitutionGoal> subGoal);
GoalPtr upcast_goal(std::shared_ptr<DerivationGoal> subGoal);
GoalPtr upcast_goal(std::shared_ptr<BuildTraceGoal> subGoal);
GoalPtr upcast_goal(std::shared_ptr<DerivationResolutionGoal> subGoal);
typedef std::chrono::time_point<std::chrono::steady_clock> steady_time_point;
@ -106,11 +112,14 @@ private:
* same derivation / path.
*/
DerivedPathMap<std::weak_ptr<DerivationGoal>> derivationGoals;
DerivedPathMap<std::weak_ptr<BuildTraceGoal>> buildTraceGoals;
DerivedPathMap<std::map<OutputsSpec, std::weak_ptr<DerivationCreationAndRealisationGoal>>> derivationCreationAndRealisationGoals;
std::map<StorePath, std::map<OutputName, std::weak_ptr<DerivationGoal>>> derivationGoals;
std::map<StorePath, std::weak_ptr<DerivationBuildingGoal>> derivationBuildingGoals;
std::map<StorePath, std::weak_ptr<PathSubstitutionGoal>> substitutionGoals;
std::map<DrvOutput, std::weak_ptr<DrvOutputSubstitutionGoal>> drvOutputSubstitutionGoals;
std::map<StorePath, std::weak_ptr<DerivationResolutionGoal>> derivationResolutionGoals;
/**
* Goals waiting for busy paths to be unlocked.
@ -204,16 +213,20 @@ private:
template<class G, typename... Args>
std::shared_ptr<G> initGoalIfNeeded(std::weak_ptr<G> & goal_weak, Args && ...args);
std::shared_ptr<DerivationGoal> makeDerivationGoalCommon(
ref<const SingleDerivedPath> drvReq, const OutputsSpec & wantedOutputs,
std::function<std::shared_ptr<DerivationGoal>()> mkDrvGoal);
public:
std::shared_ptr<DerivationGoal> makeDerivationGoal(
std::shared_ptr<DerivationCreationAndRealisationGoal> makeDerivationCreationAndRealisationGoal(
ref<const SingleDerivedPath> drvReq,
const OutputsSpec & wantedOutputs, BuildMode buildMode = bmNormal);
std::shared_ptr<DerivationGoal> makeBasicDerivationGoal(
const StorePath & drvPath, const BasicDerivation & drv,
const OutputsSpec & wantedOutputs, BuildMode buildMode = bmNormal);
public:
std::shared_ptr<DerivationCreationAndRealisationGoal> makeDerivationCreationAndRealisationGoal(
const StorePath & drvPath,
const OutputsSpec & wantedOutputs,
const Derivation & drv,
BuildMode buildMode = bmNormal);
std::shared_ptr<DerivationGoal> makeDerivationGoal(
const StorePath & drvPath, const Derivation & drv,
const OutputName & wantedOutput, BuildMode buildMode = bmNormal);
/**
* @ref DerivationBuildingGoal "derivation goal"
@ -223,11 +236,26 @@ public:
BuildMode buildMode = bmNormal);
/**
* @ref PathSubstitutionGoal "substitution goal"
* @ref PathSubstitutionGoal "path substitution goal"
*/
std::shared_ptr<PathSubstitutionGoal> makePathSubstitutionGoal(const StorePath & storePath, RepairFlag repair = NoRepair, std::optional<ContentAddress> ca = std::nullopt);
/**
* @ref DrvOutputSubstitutionGoal "derivation output substitution goal"
*/
std::shared_ptr<DrvOutputSubstitutionGoal> makeDrvOutputSubstitutionGoal(const DrvOutput & id, RepairFlag repair = NoRepair, std::optional<ContentAddress> ca = std::nullopt);
/**
* @ref BuildTraceGoal "derivation output substitution goal"
*/
std::shared_ptr<BuildTraceGoal> makeBuildTraceGoal(
const SingleDerivedPath::Built & key);
/**
* @ref DerivationResolutionGoal "derivation resolution goal"
*/
std::shared_ptr<DerivationResolutionGoal> makeDerivationResolutionGoal(const StorePath & drvPath);
/**
* Make a goal corresponding to the `DerivedPath`.
*

View file

@ -25,14 +25,14 @@ namespace nix {
LengthPrefixedProtoHelper<CommonProto, T >::write(store, conn, t); \
}
#define COMMA_ ,
COMMON_USE_LENGTH_PREFIX_SERIALISER(template<typename T>, std::vector<T>)
#define COMMA_ ,
COMMON_USE_LENGTH_PREFIX_SERIALISER(template<typename T COMMA_ typename Compare>, std::set<T COMMA_ Compare>)
COMMON_USE_LENGTH_PREFIX_SERIALISER(template<typename... Ts>, std::tuple<Ts...>)
COMMON_USE_LENGTH_PREFIX_SERIALISER(
template<typename K COMMA_ typename V>,
std::map<K COMMA_ V>)
template<typename K COMMA_ typename V COMMA_ typename Compare>,
std::map<K COMMA_ V COMMA_ Compare>)
#undef COMMA_

View file

@ -12,7 +12,6 @@ struct Source;
class StorePath;
struct ContentAddress;
struct DrvOutput;
struct Realisation;
/**
@ -69,8 +68,6 @@ template<>
DECLARE_COMMON_SERIALISER(ContentAddress);
template<>
DECLARE_COMMON_SERIALISER(DrvOutput);
template<>
DECLARE_COMMON_SERIALISER(Realisation);
#define COMMA_ ,
template<typename T>
@ -80,8 +77,8 @@ DECLARE_COMMON_SERIALISER(std::set<T COMMA_ Compare>);
template<typename... Ts>
DECLARE_COMMON_SERIALISER(std::tuple<Ts...>);
template<typename K, typename V>
DECLARE_COMMON_SERIALISER(std::map<K COMMA_ V>);
template<typename K, typename V, typename Compare>
DECLARE_COMMON_SERIALISER(std::map<K COMMA_ V COMMA_ Compare>);
#undef COMMA_
/**

View file

@ -440,34 +440,44 @@ std::string outputPathName(std::string_view drvName, OutputNameView outputName);
* derivations (fixed-output or not) will have a different hash for each
* output.
*/
struct DrvHash {
struct DrvHashModulo
{
/**
* Map from output names to hashes
* Single hash for the derivation
*
* This is for an input-addressed derivation that doesn't
* transitively depend on any floating-CA derivations.
*/
std::map<std::string, Hash> hashes;
using DrvHash = Hash;
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,
};
/**
* Known CA drv's output hashes, for fixed-output derivations whose
* output hashes are always known since they are fixed up-front.
*/
using CaOutputHashes = std::map<std::string, Hash>;
/**
* The kind of derivation this is, simplified for just "derivation hash
* modulo" purposes.
* This derivation doesn't yet have known output hashes.
*
* Either because itself is floating CA, or it (transtively) depends
* on a floating CA derivation.
*/
Kind kind;
using DeferredDrv = std::monostate;
using Raw = std::variant<
DrvHash,
CaOutputHashes,
DeferredDrv
>;
Raw raw;
bool operator == (const DrvHashModulo &) const = default;
//auto operator <=> (const DrvHashModulo &) const = default;
MAKE_WRAPPER_CONSTRUCTOR(DrvHashModulo);
};
void operator |= (DrvHash::Kind & self, const DrvHash::Kind & other) noexcept;
/**
* Returns hashes with the details of fixed-output subderivations
* expunged.
@ -492,20 +502,23 @@ void operator |= (DrvHash::Kind & self, const DrvHash::Kind & other) noexcept;
* ATerm, after subderivations have been likewise expunged from that
* derivation.
*/
DrvHash hashDerivationModulo(Store & store, const Derivation & drv, bool maskOutputs);
DrvHashModulo 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).
* If a derivation is input addressed and doesn't yet have its input
* addressed (is deferred) try using `hashDerivationModulo`.
*
* \todo What is the Hash in this map?
* Does nothing if not deferred input-addressed, or
* `hashDerivationModulo` indicates it is missing inputs' output paths
* and is not yet ready (and must stay deferred).
*/
std::map<std::string, Hash> staticOutputHashes(Store & store, const Derivation & drv);
void resolveInputAddressed(Store & store, Derivation & drv);
/**
* Memoisation of hashDerivationModulo().
*/
typedef std::map<StorePath, DrvHash> DrvHashes;
typedef std::map<StorePath, DrvHashModulo> DrvHashes;
// FIXME: global, though at least thread-safe.
extern Sync<DrvHashes> drvHashes;

View file

@ -77,7 +77,7 @@ struct SingleDerivedPathBuilt {
const StoreDirConfig & store, ref<const SingleDerivedPath> drvPath,
OutputNameView outputs,
const ExperimentalFeatureSettings & xpSettings = experimentalFeatureSettings);
nlohmann::json toJSON(Store & store) const;
nlohmann::json toJSON(const StoreDirConfig & store) const;
bool operator == (const SingleDerivedPathBuilt &) const noexcept;
std::strong_ordering operator <=> (const SingleDerivedPathBuilt &) const noexcept;
@ -151,7 +151,7 @@ struct SingleDerivedPath : _SingleDerivedPathRaw {
const StoreDirConfig & store,
std::string_view,
const ExperimentalFeatureSettings & xpSettings = experimentalFeatureSettings);
nlohmann::json toJSON(Store & store) const;
nlohmann::json toJSON(const StoreDirConfig & store) const;
};
static inline ref<SingleDerivedPath> makeConstantStorePathRef(StorePath drvPath)
@ -204,7 +204,7 @@ struct DerivedPathBuilt {
const StoreDirConfig & store, ref<const SingleDerivedPath>,
std::string_view,
const ExperimentalFeatureSettings & xpSettings = experimentalFeatureSettings);
nlohmann::json toJSON(Store & store) const;
nlohmann::json toJSON(const StoreDirConfig & store) const;
bool operator == (const DerivedPathBuilt &) const noexcept;
// TODO libc++ 16 (used by darwin) missing `std::set::operator <=>`, can't do yet.
@ -285,7 +285,7 @@ struct DerivedPath : _DerivedPathRaw {
*/
static DerivedPath fromSingle(const SingleDerivedPath &);
nlohmann::json toJSON(Store & store) const;
nlohmann::json toJSON(const StoreDirConfig & store) const;
};
typedef std::vector<DerivedPath> DerivedPaths;

View file

@ -193,7 +193,7 @@ public:
std::optional<TrustedFlag> isTrustedClient() override;
void queryRealisationUncached(const DrvOutput &,
Callback<std::shared_ptr<const Realisation>> callback) noexcept override
Callback<std::shared_ptr<const UnkeyedRealisation>> callback) noexcept override
// TODO: Implement
{ unsupported("queryRealisation"); }
};

View file

@ -55,14 +55,14 @@ LENGTH_PREFIXED_PROTO_HELPER(Inner, std::vector<T>);
#define COMMA_ ,
template<class Inner, typename T, typename Compare>
LENGTH_PREFIXED_PROTO_HELPER(Inner, std::set<T COMMA_ Compare>);
#undef COMMA_
template<class Inner, typename... Ts>
LENGTH_PREFIXED_PROTO_HELPER(Inner, std::tuple<Ts...>);
template<class Inner, typename K, typename V>
#define LENGTH_PREFIXED_PROTO_HELPER_X std::map<K, V>
template<class Inner, typename K, typename V, typename Compare>
#define LENGTH_PREFIXED_PROTO_HELPER_X std::map<K, V, Compare>
LENGTH_PREFIXED_PROTO_HELPER(Inner, LENGTH_PREFIXED_PROTO_HELPER_X);
#undef COMMA_
template<class Inner, typename T>
std::vector<T>
@ -110,12 +110,12 @@ void LengthPrefixedProtoHelper<Inner, std::set<T, Compare>>::write(
}
}
template<class Inner, typename K, typename V>
std::map<K, V>
LengthPrefixedProtoHelper<Inner, std::map<K, V>>::read(
template<class Inner, typename K, typename V, typename Compare>
std::map<K, V, Compare>
LengthPrefixedProtoHelper<Inner, std::map<K, V, Compare>>::read(
const StoreDirConfig & store, typename Inner::ReadConn conn)
{
std::map<K, V> resMap;
std::map<K, V, Compare> resMap;
auto size = readNum<size_t>(conn.from);
while (size--) {
auto k = S<K>::read(store, conn);
@ -125,10 +125,10 @@ LengthPrefixedProtoHelper<Inner, std::map<K, V>>::read(
return resMap;
}
template<class Inner, typename K, typename V>
template<class Inner, typename K, typename V, typename Compare>
void
LengthPrefixedProtoHelper<Inner, std::map<K, V>>::write(
const StoreDirConfig & store, typename Inner::WriteConn conn, const std::map<K, V> & resMap)
LengthPrefixedProtoHelper<Inner, std::map<K, V, Compare>>::write(
const StoreDirConfig & store, typename Inner::WriteConn conn, const std::map<K, V, Compare> & resMap)
{
conn.to << resMap.size();
for (auto & i : resMap) {

View file

@ -160,7 +160,7 @@ private:
* Check lower store if upper DB does not have.
*/
void queryRealisationUncached(const DrvOutput&,
Callback<std::shared_ptr<const Realisation>> callback) noexcept override;
Callback<std::shared_ptr<const UnkeyedRealisation>> callback) noexcept override;
/**
* Call `remountIfNecessary` after collecting garbage normally.

View file

@ -338,10 +338,10 @@ public:
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);
std::optional<const UnkeyedRealisation> queryRealisation_(State & state, const DrvOutput & id);
std::optional<std::pair<int64_t, UnkeyedRealisation>> queryRealisationCore_(State & state, const DrvOutput & id);
void queryRealisationUncached(const DrvOutput&,
Callback<std::shared_ptr<const Realisation>> callback) noexcept override;
Callback<std::shared_ptr<const UnkeyedRealisation>> callback) noexcept override;
std::optional<std::string> getVersion() override;

View file

@ -12,9 +12,12 @@ config_pub_h = configure_file(
headers = [config_pub_h] + files(
'binary-cache-store.hh',
'build-result.hh',
'build/build-trace-goal.hh',
'build/derivation-goal.hh',
'build/derivation-building-goal.hh',
'build/derivation-building-misc.hh',
'build/derivation-creation-and-realisation-goal.hh',
'build/derivation-resolution-goal.hh',
'build/drv-output-substitution-goal.hh',
'build/goal.hh',
'build/substitution-goal.hh',

View file

@ -18,64 +18,97 @@ struct OutputsSpec;
/**
* A general `Realisation` key.
*
* This is similar to a `DerivedPath::Opaque`, but the derivation is
* identified by its "hash modulo" instead of by its store path.
* This is similar to a `DerivedPath::Built`, except it is only a single
* step: `drvPath` is a `StorePath` rather than a `DerivedPath`.
*/
struct DrvOutput {
struct DrvOutput
{
/**
* The hash modulo of the derivation.
*
* Computed from the derivation itself for most types of
* derivations, but computed from the (fixed) content address of the
* output for fixed-output derivations.
* The store path to the derivation
*/
Hash drvHash;
StorePath drvPath;
/**
* The name of the output.
*/
OutputName outputName;
/**
* Skips the store dir on the `drvPath`
*/
std::string to_string() const;
std::string strHash() const
{ return drvHash.to_string(HashFormat::Base16, true); }
/**
* Skips the store dir on the `drvPath`
*/
static DrvOutput from_string(std::string_view);
static DrvOutput parse(const std::string &);
/**
* Includes the store dir on `drvPath`
*/
std::string render(const StoreDirConfig & store) const;
GENERATE_CMP(DrvOutput, me->drvHash, me->outputName);
/**
* Includes the store dir on `drvPath`
*/
static DrvOutput parse(const StoreDirConfig & store, std::string_view);
nlohmann::json toJSON(const StoreDirConfig & store) const;
static DrvOutput fromJSON(const StoreDirConfig & store, const nlohmann::json & json);
bool operator==(const DrvOutput &) const = default;
auto operator<=>(const DrvOutput &) const = default;
};
struct Realisation {
DrvOutput id;
struct UnkeyedRealisation
{
StorePath outPath;
StringSet signatures;
nlohmann::json toJSON(const StoreDirConfig & store) const;
static UnkeyedRealisation fromJSON(const StoreDirConfig & store, const nlohmann::json & json);
std::string fingerprint(const StoreDirConfig & store, const DrvOutput & key) const;
void sign(const StoreDirConfig & store, const DrvOutput & key, const Signer &);
bool checkSignature(
const StoreDirConfig & store,
const DrvOutput & key,
const PublicKeys & publicKeys,
const std::string & sig) const;
size_t checkSignatures(const StoreDirConfig & store, const DrvOutput & key, const PublicKeys & publicKeys) const;
/**
* The realisations that are required for the current one to be valid.
*
* When importing this realisation, the store will first check that all its
* dependencies exist, and map to the correct output path
* Just check the `outPath`. Signatures don't matter for this.
* Callers must ensure that the corresponding key is the same for
* most use-cases.
*/
std::map<DrvOutput, StorePath> dependentRealisations;
bool isCompatibleWith(const UnkeyedRealisation & other) const
{
return outPath == other.outPath;
}
nlohmann::json toJSON() const;
static Realisation fromJSON(const nlohmann::json& json, const std::string& whence);
const StorePath & getPath() const
{
return outPath;
}
std::string fingerprint() const;
void sign(const Signer &);
bool checkSignature(const PublicKeys & publicKeys, const std::string & sig) const;
size_t checkSignatures(const PublicKeys & publicKeys) const;
// TODO sketchy that it avoids signatures
GENERATE_CMP(UnkeyedRealisation, me->outPath);
};
static std::set<Realisation> closure(Store &, const std::set<Realisation> &);
static void closure(Store &, const std::set<Realisation> &, std::set<Realisation> & res);
struct Realisation : UnkeyedRealisation
{
DrvOutput id;
bool isCompatibleWith(const Realisation & other) const;
nlohmann::json toJSON(const StoreDirConfig & store) const;
static Realisation fromJSON(const StoreDirConfig & store, const nlohmann::json & json);
StorePath getPath() const { return outPath; }
GENERATE_CMP(Realisation, me->id, me->outPath);
bool operator==(const Realisation &) const = default;
auto operator<=>(const Realisation &) const = default;
};
/**
@ -84,39 +117,34 @@ struct Realisation {
* Since these are the outputs of a single derivation, we know the
* output names are unique so we can use them as the map key.
*/
typedef std::map<OutputName, Realisation> SingleDrvOutputs;
/**
* Collection type for multiple derivations' outputs' `Realisation`s.
*
* `DrvOutput` is used because in general the derivations are not all
* the same, so we need to identify firstly which derivation, and
* secondly which output of that derivation.
*/
typedef std::map<DrvOutput, Realisation> DrvOutputs;
typedef std::map<OutputName, UnkeyedRealisation> SingleDrvOutputs;
/**
* Filter a SingleDrvOutputs to include only specific output names
*
* Moves the `outputs` input.
*/
SingleDrvOutputs filterDrvOutputs(const OutputsSpec&, SingleDrvOutputs&&);
SingleDrvOutputs filterDrvOutputs(const OutputsSpec &, SingleDrvOutputs && outputs);
struct OpaquePath {
struct OpaquePath
{
StorePath path;
StorePath getPath() const { return path; }
const StorePath & getPath() const
{
return path;
}
GENERATE_CMP(OpaquePath, me->path);
bool operator==(const OpaquePath &) const = default;
auto operator<=>(const OpaquePath &) const = default;
};
/**
* A store path with all the history of how it went into the store
*/
struct RealisedPath {
/*
struct RealisedPath
{
/**
* A path is either the result of the realisation of a derivation or
* an opaque blob that has been directly added to the store
*/
@ -125,33 +153,38 @@ struct RealisedPath {
using Set = std::set<RealisedPath>;
RealisedPath(StorePath path) : raw(OpaquePath{path}) {}
RealisedPath(Realisation r) : raw(r) {}
RealisedPath(StorePath path)
: raw(OpaquePath{path})
{
}
RealisedPath(Realisation r)
: raw(r)
{
}
/**
* Get the raw store path associated to this
*/
StorePath path() const;
const StorePath & path() const;
void closure(Store& store, Set& ret) const;
static void closure(Store& store, const Set& startPaths, Set& ret);
Set closure(Store& store) const;
GENERATE_CMP(RealisedPath, me->raw);
bool operator==(const RealisedPath &) const = default;
auto operator<=>(const RealisedPath &) const = default;
};
class MissingRealisation : public Error
{
public:
MissingRealisation(DrvOutput & outputId)
: MissingRealisation(outputId.outputName, outputId.strHash())
{}
MissingRealisation(std::string_view drv, OutputName outputName)
: Error( "cannot operate on output '%s' of the "
"unbuilt derivation '%s'",
outputName,
drv)
{}
MissingRealisation(const StoreDirConfig & store, DrvOutput & outputId)
: MissingRealisation(store, outputId.drvPath, outputId.outputName)
{
}
MissingRealisation(const StoreDirConfig & store, const StorePath & drvPath, const OutputName & outputName);
MissingRealisation(
const StoreDirConfig & store,
const SingleDerivedPath & drvPath,
const StorePath & drvPathResolved,
const OutputName & outputName);
};
}

View file

@ -112,7 +112,7 @@ struct RemoteStore :
void registerDrvOutput(const Realisation & info) override;
void queryRealisationUncached(const DrvOutput &,
Callback<std::shared_ptr<const Realisation>> callback) noexcept override;
Callback<std::shared_ptr<const UnkeyedRealisation>> callback) noexcept override;
void buildPaths(const std::vector<DerivedPath> & paths, BuildMode buildMode, std::shared_ptr<Store> evalStore) override;

View file

@ -33,8 +33,8 @@ SERVE_USE_LENGTH_PREFIX_SERIALISER(template<typename... Ts>, std::tuple<Ts...>)
#define SERVE_USE_LENGTH_PREFIX_SERIALISER_COMMA ,
SERVE_USE_LENGTH_PREFIX_SERIALISER(
template<typename K SERVE_USE_LENGTH_PREFIX_SERIALISER_COMMA typename V>,
std::map<K SERVE_USE_LENGTH_PREFIX_SERIALISER_COMMA V>)
template<typename K SERVE_USE_LENGTH_PREFIX_SERIALISER_COMMA typename V SERVE_USE_LENGTH_PREFIX_SERIALISER_COMMA typename Compare>,
std::map<K SERVE_USE_LENGTH_PREFIX_SERIALISER_COMMA V SERVE_USE_LENGTH_PREFIX_SERIALISER_COMMA Compare>)
/**
* Use `CommonProto` where possible.

View file

@ -8,7 +8,7 @@ namespace nix {
#define SERVE_MAGIC_1 0x390c9deb
#define SERVE_MAGIC_2 0x5452eecb
#define SERVE_PROTOCOL_VERSION (2 << 8 | 7)
#define SERVE_PROTOCOL_VERSION (2 << 8 | 8)
#define GET_PROTOCOL_MAJOR(x) ((x) & 0xff00)
#define GET_PROTOCOL_MINOR(x) ((x) & 0x00ff)
@ -19,6 +19,9 @@ struct Source;
// items being serialised
struct BuildResult;
struct UnkeyedValidPathInfo;
struct DrvOutput;
struct UnkeyedRealisation;
struct Realisation;
/**
@ -174,6 +177,12 @@ inline std::ostream & operator << (std::ostream & s, ServeProto::Command op)
template<>
DECLARE_SERVE_SERIALISER(BuildResult);
template<>
DECLARE_SERVE_SERIALISER(DrvOutput);
template<>
DECLARE_SERVE_SERIALISER(UnkeyedRealisation);
template<>
DECLARE_SERVE_SERIALISER(Realisation);
template<>
DECLARE_SERVE_SERIALISER(UnkeyedValidPathInfo);
template<>
DECLARE_SERVE_SERIALISER(ServeProto::BuildOptions);
@ -186,8 +195,8 @@ DECLARE_SERVE_SERIALISER(std::set<T COMMA_ Compare>);
template<typename... Ts>
DECLARE_SERVE_SERIALISER(std::tuple<Ts...>);
template<typename K, typename V>
DECLARE_SERVE_SERIALISER(std::map<K COMMA_ V>);
template<typename K, typename V, typename Compare>
DECLARE_SERVE_SERIALISER(std::map<K COMMA_ V COMMA_ Compare>);
#undef COMMA_
}

View file

@ -38,6 +38,7 @@ MakeError(SubstituterDisabled, Error);
MakeError(InvalidStoreReference, Error);
struct UnkeyedRealisation;
struct Realisation;
struct RealisedPath;
struct DrvOutput;
@ -319,13 +320,13 @@ public:
/**
* Query the information about a realisation.
*/
std::shared_ptr<const Realisation> queryRealisation(const DrvOutput &);
std::shared_ptr<const UnkeyedRealisation> queryRealisation(const DrvOutput &);
/**
* Asynchronous version of queryRealisation().
*/
void queryRealisation(const DrvOutput &,
Callback<std::shared_ptr<const Realisation>> callback) noexcept;
Callback<std::shared_ptr<const UnkeyedRealisation>> callback) noexcept;
/**
@ -354,7 +355,7 @@ protected:
virtual void queryPathInfoUncached(const StorePath & path,
Callback<std::shared_ptr<const ValidPathInfo>> callback) noexcept = 0;
virtual void queryRealisationUncached(const DrvOutput &,
Callback<std::shared_ptr<const Realisation>> callback) noexcept = 0;
Callback<std::shared_ptr<const UnkeyedRealisation>> callback) noexcept = 0;
public:
@ -902,10 +903,4 @@ std::optional<ValidPathInfo> decodeValidPathInfo(
const ContentAddress * getDerivationCA(const BasicDerivation & drv);
std::map<DrvOutput, StorePath> drvOutputReferences(
Store & store,
const Derivation & drv,
const StorePath & outputPath,
Store * evalStore = nullptr);
}

View file

@ -33,8 +33,8 @@ WORKER_USE_LENGTH_PREFIX_SERIALISER(template<typename... Ts>, std::tuple<Ts...>)
#define WORKER_USE_LENGTH_PREFIX_SERIALISER_COMMA ,
WORKER_USE_LENGTH_PREFIX_SERIALISER(
template<typename K WORKER_USE_LENGTH_PREFIX_SERIALISER_COMMA typename V>,
std::map<K WORKER_USE_LENGTH_PREFIX_SERIALISER_COMMA V>)
template<typename K WORKER_USE_LENGTH_PREFIX_SERIALISER_COMMA typename V WORKER_USE_LENGTH_PREFIX_SERIALISER_COMMA typename Compare>,
std::map<K WORKER_USE_LENGTH_PREFIX_SERIALISER_COMMA V WORKER_USE_LENGTH_PREFIX_SERIALISER_COMMA Compare>)
/**
* Use `CommonProto` where possible.

View file

@ -13,7 +13,7 @@ namespace nix {
/* Note: you generally shouldn't change the protocol version. Define a
new `WorkerProto::Feature` instead. */
#define PROTOCOL_VERSION (1 << 8 | 38)
#define PROTOCOL_VERSION (1 << 8 | 39)
#define GET_PROTOCOL_MAJOR(x) ((x) & 0xff00)
#define GET_PROTOCOL_MINOR(x) ((x) & 0x00ff)
@ -37,6 +37,9 @@ struct BuildResult;
struct KeyedBuildResult;
struct ValidPathInfo;
struct UnkeyedValidPathInfo;
struct DrvOutput;
struct UnkeyedRealisation;
struct Realisation;
enum BuildMode : uint8_t;
enum TrustedFlag : bool;
@ -263,6 +266,14 @@ DECLARE_WORKER_SERIALISER(ValidPathInfo);
template<>
DECLARE_WORKER_SERIALISER(UnkeyedValidPathInfo);
template<>
DECLARE_WORKER_SERIALISER(DrvOutput);
template<>
DECLARE_WORKER_SERIALISER(UnkeyedRealisation);
template<>
DECLARE_WORKER_SERIALISER(Realisation);
template<>
DECLARE_WORKER_SERIALISER(std::optional<UnkeyedRealisation>);
template<>
DECLARE_WORKER_SERIALISER(BuildMode);
template<>
DECLARE_WORKER_SERIALISER(std::optional<TrustedFlag>);
@ -279,8 +290,8 @@ DECLARE_WORKER_SERIALISER(std::set<T COMMA_ Compare>);
template<typename... Ts>
DECLARE_WORKER_SERIALISER(std::tuple<Ts...>);
template<typename K, typename V>
DECLARE_WORKER_SERIALISER(std::map<K COMMA_ V>);
template<typename K, typename V, typename Compare>
DECLARE_WORKER_SERIALISER(std::map<K COMMA_ V COMMA_ Compare>);
#undef COMMA_
}

View file

@ -72,7 +72,7 @@ void LocalOverlayStore::registerDrvOutput(const Realisation & info)
// First do queryRealisation on lower layer to populate DB
auto res = lowerStore->queryRealisation(info.id);
if (res)
LocalStore::registerDrvOutput(*res);
LocalStore::registerDrvOutput({*res, info.id});
LocalStore::registerDrvOutput(info);
}
@ -106,12 +106,12 @@ void LocalOverlayStore::queryPathInfoUncached(const StorePath & path,
void LocalOverlayStore::queryRealisationUncached(const DrvOutput & drvOutput,
Callback<std::shared_ptr<const Realisation>> callback) noexcept
Callback<std::shared_ptr<const UnkeyedRealisation>> callback) noexcept
{
auto callbackPtr = std::make_shared<decltype(callback)>(std::move(callback));
LocalStore::queryRealisationUncached(drvOutput,
{[this, drvOutput, callbackPtr](std::future<std::shared_ptr<const Realisation>> fut) {
{[this, drvOutput, callbackPtr](std::future<std::shared_ptr<const UnkeyedRealisation>> fut) {
try {
auto info = fut.get();
if (info)
@ -121,7 +121,7 @@ void LocalOverlayStore::queryRealisationUncached(const DrvOutput & drvOutput,
}
// If we don't have it, check lower store
lowerStore->queryRealisation(drvOutput,
{[callbackPtr](std::future<std::shared_ptr<const Realisation>> fut) {
{[callbackPtr](std::future<std::shared_ptr<const UnkeyedRealisation>> fut) {
try {
(*callbackPtr)(fut.get());
} catch (...) {

View file

@ -100,8 +100,6 @@ struct LocalStore::State::Stmts {
SQLiteStmt QueryAllRealisedOutputs;
SQLiteStmt QueryPathFromHashPart;
SQLiteStmt QueryValidPaths;
SQLiteStmt QueryRealisationReferences;
SQLiteStmt AddRealisationReference;
};
LocalStore::LocalStore(ref<const Config> config)
@ -363,19 +361,6 @@ LocalStore::LocalStore(ref<const Config> config)
where drvPath = ?
;
)");
state->stmts->QueryRealisationReferences.create(state->db,
R"(
select drvPath, outputName from Realisations
join RealisationsRefs on realisationReference = Realisations.id
where referrer = ?;
)");
state->stmts->AddRealisationReference.create(state->db,
R"(
insert or replace into RealisationsRefs (referrer, realisationReference)
values (
(select id from Realisations where drvPath = ? and outputName = ?),
(select id from Realisations where drvPath = ? and outputName = ?));
)");
}
}
@ -603,7 +588,7 @@ void LocalStore::registerDrvOutput(const Realisation & info)
info.signatures.end());
state->stmts->UpdateRealisedOutput.use()
(concatStringsSep(" ", combinedSignatures))
(info.id.strHash())
(info.id.drvPath.to_string())
(info.id.outputName)
.exec();
} else {
@ -618,30 +603,12 @@ void LocalStore::registerDrvOutput(const Realisation & info)
}
} else {
state->stmts->RegisterRealisedOutput.use()
(info.id.strHash())
(info.id.drvPath.to_string())
(info.id.outputName)
(printStorePath(info.outPath))
(concatStringsSep(" ", info.signatures))
.exec();
}
for (auto & [outputId, depPath] : info.dependentRealisations) {
auto localRealisation = queryRealisationCore_(*state, outputId);
if (!localRealisation)
throw Error("unable to register the derivation '%s' as it "
"depends on the non existent '%s'",
info.id.to_string(), outputId.to_string());
if (localRealisation->second.outPath != depPath)
throw Error("unable to register the derivation '%s' as it "
"depends on a realisation of '%s' that doesnt"
"match what we have locally",
info.id.to_string(), outputId.to_string());
state->stmts->AddRealisationReference.use()
(info.id.strHash())
(info.id.outputName)
(outputId.strHash())
(outputId.outputName)
.exec();
}
});
}
@ -1033,7 +1000,7 @@ bool LocalStore::pathInfoIsUntrusted(const ValidPathInfo & info)
bool LocalStore::realisationIsUntrusted(const Realisation & realisation)
{
return config->requireSigs && !realisation.checkSignatures(getPublicKeys());
return config->requireSigs && !realisation.checkSignatures(*this, realisation.id, getPublicKeys());
}
void LocalStore::addToStore(const ValidPathInfo & info, Source & source,
@ -1580,13 +1547,13 @@ void LocalStore::addSignatures(const StorePath & storePath, const StringSet & si
}
std::optional<std::pair<int64_t, Realisation>> LocalStore::queryRealisationCore_(
std::optional<std::pair<int64_t, UnkeyedRealisation>> LocalStore::queryRealisationCore_(
LocalStore::State & state,
const DrvOutput & id)
{
auto useQueryRealisedOutput(
state.stmts->QueryRealisedOutput.use()
(id.strHash())
(id.drvPath.to_string())
(id.outputName));
if (!useQueryRealisedOutput.next())
return std::nullopt;
@ -1597,15 +1564,14 @@ std::optional<std::pair<int64_t, Realisation>> LocalStore::queryRealisationCore_
return {{
realisationDbId,
Realisation{
.id = id,
UnkeyedRealisation{
.outPath = outputPath,
.signatures = signatures,
}
}};
}
std::optional<const Realisation> LocalStore::queryRealisation_(
std::optional<const UnkeyedRealisation> LocalStore::queryRealisation_(
LocalStore::State & state,
const DrvOutput & id)
{
@ -1614,38 +1580,21 @@ std::optional<const Realisation> LocalStore::queryRealisation_(
return std::nullopt;
auto [realisationDbId, res] = *maybeCore;
std::map<DrvOutput, StorePath> dependentRealisations;
auto useRealisationRefs(
state.stmts->QueryRealisationReferences.use()
(realisationDbId));
while (useRealisationRefs.next()) {
auto depId = DrvOutput {
Hash::parseAnyPrefixed(useRealisationRefs.getStr(0)),
useRealisationRefs.getStr(1),
};
auto dependentRealisation = queryRealisationCore_(state, depId);
assert(dependentRealisation); // Enforced by the db schema
auto outputPath = dependentRealisation->second.outPath;
dependentRealisations.insert({depId, outputPath});
}
res.dependentRealisations = dependentRealisations;
return { res };
}
void LocalStore::queryRealisationUncached(const DrvOutput & id,
Callback<std::shared_ptr<const Realisation>> callback) noexcept
Callback<std::shared_ptr<const UnkeyedRealisation>> callback) noexcept
{
try {
auto maybeRealisation
= retrySQLite<std::optional<const Realisation>>([&]() {
= retrySQLite<std::optional<const UnkeyedRealisation>>([&]() {
auto state(_state.lock());
return queryRealisation_(*state, id);
});
if (maybeRealisation)
callback(
std::make_shared<const Realisation>(maybeRealisation.value()));
std::make_shared<const UnkeyedRealisation>(maybeRealisation.value()));
else
callback(nullptr);

View file

@ -253,8 +253,12 @@ subdir('nix-meson-build-support/common')
sources = files(
'binary-cache-store.cc',
'build-result.cc',
'build/build-trace-goal.cc',
'build/derivation-goal.cc',
'build/derivation-building-goal.cc',
'build/derivation-creation-and-realisation-goal.cc',
'build/derivation-goal.cc',
'build/derivation-resolution-goal.cc',
'build/drv-output-substitution-goal.cc',
'build/entry-points.cc',
'build/goal.cc',

View file

@ -240,16 +240,15 @@ void Store::queryMissing(const std::vector<DerivedPath> & targets,
// If there are unknown output paths, attempt to find if the
// paths are known to substituters through a realisation.
auto outputHashes = staticOutputHashes(*this, *drv);
knownOutputPaths = true;
for (auto [outputName, hash] : outputHashes) {
for (auto & [outputName, _] : drv->outputs) {
if (!bfd.outputs.contains(outputName))
continue;
bool found = false;
for (auto &sub : getDefaultSubstituters()) {
auto realisation = sub->queryRealisation({hash, outputName});
auto realisation = sub->queryRealisation({drvPath, outputName});
if (!realisation)
continue;
found = true;
@ -327,72 +326,6 @@ StorePaths Store::topoSortPaths(const StorePathSet & paths)
}});
}
std::map<DrvOutput, StorePath> drvOutputReferences(
const std::set<Realisation> & inputRealisations,
const StorePathSet & pathReferences)
{
std::map<DrvOutput, StorePath> res;
for (const auto & input : inputRealisations) {
if (pathReferences.count(input.outPath)) {
res.insert({input.id, input.outPath});
}
}
return res;
}
std::map<DrvOutput, StorePath> drvOutputReferences(
Store & store,
const Derivation & drv,
const StorePath & outputPath,
Store * evalStore_)
{
auto & evalStore = evalStore_ ? *evalStore_ : store;
std::set<Realisation> inputRealisations;
std::function<void(const StorePath &, const DerivedPathMap<StringSet>::ChildNode &)> accumRealisations;
accumRealisations = [&](const StorePath & inputDrv, const DerivedPathMap<StringSet>::ChildNode & inputNode) {
if (!inputNode.value.empty()) {
auto outputHashes =
staticOutputHashes(evalStore, evalStore.readDerivation(inputDrv));
for (const auto & outputName : inputNode.value) {
auto outputHash = get(outputHashes, outputName);
if (!outputHash)
throw Error(
"output '%s' of derivation '%s' isn't realised", outputName,
store.printStorePath(inputDrv));
auto thisRealisation = store.queryRealisation(
DrvOutput{*outputHash, outputName});
if (!thisRealisation)
throw Error(
"output '%s' of derivation '%s' isnt built", outputName,
store.printStorePath(inputDrv));
inputRealisations.insert(*thisRealisation);
}
}
if (!inputNode.value.empty()) {
auto d = makeConstantStorePathRef(inputDrv);
for (const auto & [outputName, childNode] : inputNode.childMap) {
SingleDerivedPath next = SingleDerivedPath::Built { d, outputName };
accumRealisations(
// TODO deep resolutions for dynamic derivations, issue #8947, would go here.
resolveDerivedPath(store, next, evalStore_),
childNode);
}
}
};
for (const auto & [inputDrv, inputNode] : drv.inputDrvs.map)
accumRealisations(inputDrv, inputNode);
auto info = store.queryPathInfo(outputPath);
return drvOutputReferences(Realisation::closure(store, inputRealisations), info->references);
}
OutputPathMap resolveDerivedPath(Store & store, const DerivedPath::Built & bfd, Store * evalStore_)
{
auto drvPath = resolveDerivedPath(store, *bfd.drvPath, evalStore_);
@ -422,7 +355,7 @@ OutputPathMap resolveDerivedPath(Store & store, const DerivedPath::Built & bfd,
OutputPathMap outputs;
for (auto & [outputName, outputPathOpt] : outputsOpt) {
if (!outputPathOpt)
throw MissingRealisation(bfd.drvPath->to_string(store), outputName);
throw MissingRealisation(store, *bfd.drvPath, drvPath, outputName);
auto & outputPath = *outputPathOpt;
outputs.insert_or_assign(outputName, outputPath);
}
@ -446,7 +379,7 @@ StorePath resolveDerivedPath(Store & store, const SingleDerivedPath & req, Store
store.printStorePath(drvPath), bfd.output);
auto & optPath = outputPaths.at(bfd.output);
if (!optPath)
throw MissingRealisation(bfd.drvPath->to_string(store), bfd.output);
throw MissingRealisation(store, *bfd.drvPath, drvPath, bfd.output);
return *optPath;
},
}, req.raw());

View file

@ -44,10 +44,16 @@ create table if not exists NARs (
create table if not exists Realisations (
cache integer not null,
outputId text not null,
content blob, -- Json serialisation of the realisation, or null if the realisation is absent
drvPath text not null,
outputName text not null,
-- The following are null if the realisation is absent
outputPath text,
sigs text,
timestamp integer not null,
primary key (cache, outputId),
primary key (cache, drvPath, outputName),
foreign key (cache) references BinaryCaches(id) on delete cascade
);
@ -87,7 +93,7 @@ public:
Sync<State> _state;
NarInfoDiskCacheImpl(Path dbPath = getCacheDir() + "/binary-cache-v6.sqlite")
NarInfoDiskCacheImpl(Path dbPath = getCacheDir() + "/binary-cache-v7.sqlite")
{
auto state(_state.lock());
@ -117,22 +123,22 @@ public:
state->insertRealisation.create(state->db,
R"(
insert or replace into Realisations(cache, outputId, content, timestamp)
values (?, ?, ?, ?)
insert or replace into Realisations(cache, drvPath, outputName, outputPath, sigs, timestamp)
values (?, ?, ?, ?, ?, ?)
)");
state->insertMissingRealisation.create(state->db,
R"(
insert or replace into Realisations(cache, outputId, timestamp)
values (?, ?, ?)
insert or replace into Realisations(cache, drvPath, outputName, timestamp)
values (?, ?, ?, ?)
)");
state->queryRealisation.create(state->db,
R"(
select content from Realisations
where cache = ? and outputId = ? and
((content is null and timestamp > ?) or
(content is not null and timestamp > ?))
select outputPath, sigs from Realisations
where cache = ? and drvPath = ? and outputName = ? and
((outputPath is null and timestamp > ?) or
(outputPath is not null and timestamp > ?))
)");
/* Periodically purge expired entries from the database. */
@ -295,22 +301,31 @@ public:
auto queryRealisation(state->queryRealisation.use()
(cache.id)
(id.to_string())
(id.drvPath.to_string())
(id.outputName)
(now - settings.ttlNegativeNarInfoCache)
(now - settings.ttlPositiveNarInfoCache));
if (!queryRealisation.next())
return {oUnknown, 0};
return {oUnknown, nullptr};
if (queryRealisation.isNull(0))
return {oInvalid, 0};
return {oInvalid, nullptr};
auto realisation =
std::make_shared<Realisation>(Realisation::fromJSON(
nlohmann::json::parse(queryRealisation.getStr(0)),
"Local disk cache"));
return {oValid, realisation};
try {
return {
oValid,
std::make_shared<Realisation>(
UnkeyedRealisation{
.outPath = StorePath{queryRealisation.getStr(0)},
.signatures = nlohmann::json::parse(queryRealisation.getStr(1)),
},
id),
};
} catch (Error & e) {
e.addTrace({}, "reading build trace key-value from the local disk cache");
throw;
}
});
}
@ -365,8 +380,10 @@ public:
state->insertRealisation.use()
(cache.id)
(realisation.id.to_string())
(realisation.toJSON().dump())
(realisation.id.drvPath.to_string())
(realisation.id.outputName)
(realisation.outPath.to_string())
(nlohmann::json(realisation.signatures))
(time(0)).exec();
});

View file

@ -1,130 +1,113 @@
#include "nix/store/realisation.hh"
#include "nix/store/store-api.hh"
#include "nix/util/closure.hh"
#include "nix/util/signature/local-keys.hh"
#include <nlohmann/json.hpp>
#include "nix/util/json-utils.hh"
namespace nix {
MakeError(InvalidDerivationOutputId, Error);
DrvOutput DrvOutput::parse(const std::string &strRep) {
size_t n = strRep.find("!");
if (n == strRep.npos)
throw InvalidDerivationOutputId("Invalid derivation output id %s", strRep);
DrvOutput DrvOutput::parse(const StoreDirConfig & store, std::string_view s)
{
size_t n = s.rfind('^');
if (n == s.npos)
throw InvalidDerivationOutputId("Invalid derivation output id '%s': missing '^'", s);
return DrvOutput{
.drvHash = Hash::parseAnyPrefixed(strRep.substr(0, n)),
.outputName = strRep.substr(n+1),
.drvPath = store.parseStorePath(s.substr(0, n)),
.outputName = OutputName{s.substr(n + 1)},
};
}
std::string DrvOutput::to_string() const {
return strHash() + "!" + outputName;
}
std::set<Realisation> Realisation::closure(Store & store, const std::set<Realisation> & startOutputs)
std::string DrvOutput::render(const StoreDirConfig & store) const
{
std::set<Realisation> res;
Realisation::closure(store, startOutputs, res);
return res;
return std::string(store.printStorePath(drvPath)) + "^" + outputName;
}
void Realisation::closure(Store & store, const std::set<Realisation> & startOutputs, std::set<Realisation> & res)
std::string DrvOutput::to_string() const
{
auto getDeps = [&](const Realisation& current) -> std::set<Realisation> {
std::set<Realisation> res;
for (auto& [currentDep, _] : current.dependentRealisations) {
if (auto currentRealisation = store.queryRealisation(currentDep))
res.insert(*currentRealisation);
else
throw Error(
"Unrealised derivation '%s'", currentDep.to_string());
}
return res;
};
computeClosure<Realisation>(
startOutputs, res,
[&](const Realisation& current,
std::function<void(std::promise<std::set<Realisation>>&)>
processEdges) {
std::promise<std::set<Realisation>> promise;
try {
auto res = getDeps(current);
promise.set_value(res);
} catch (...) {
promise.set_exception(std::current_exception());
}
return processEdges(promise);
});
return std::string(drvPath.to_string()) + "^" + outputName;
}
nlohmann::json Realisation::toJSON() const {
auto jsonDependentRealisations = nlohmann::json::object();
for (auto & [depId, depOutPath] : dependentRealisations)
jsonDependentRealisations.emplace(depId.to_string(), depOutPath.to_string());
nlohmann::json DrvOutput::toJSON(const StoreDirConfig & store) const
{
return nlohmann::json{
{"id", id.to_string()},
{"outPath", outPath.to_string()},
{"signatures", signatures},
{"dependentRealisations", jsonDependentRealisations},
{"drvPath", store.printStorePath(drvPath)},
{"outputName", outputName},
};
}
Realisation Realisation::fromJSON(
const nlohmann::json& json,
const std::string& whence) {
auto getOptionalField = [&](std::string fieldName) -> std::optional<std::string> {
auto fieldIterator = json.find(fieldName);
if (fieldIterator == json.end())
return std::nullopt;
return {*fieldIterator};
DrvOutput DrvOutput::fromJSON(const StoreDirConfig & store, const nlohmann::json & json)
{
auto obj = getObject(json);
return {
.drvPath = store.parseStorePath(getString(valueAt(obj, "drvPath"))),
.outputName = getString(valueAt(obj, "outputName")),
};
auto getField = [&](std::string fieldName) -> std::string {
if (auto field = getOptionalField(fieldName))
return *field;
else
throw Error(
"Drv output info file '%1%' is corrupt, missing field %2%",
whence, fieldName);
}
nlohmann::json UnkeyedRealisation::toJSON(const StoreDirConfig & store) const
{
return nlohmann::json{
{"outPath", store.printStorePath(outPath)},
{"signatures", signatures},
};
}
UnkeyedRealisation UnkeyedRealisation::fromJSON(const StoreDirConfig & store, const nlohmann::json & json)
{
auto obj = getObject(json);
StringSet signatures;
if (auto signaturesIterator = json.find("signatures"); signaturesIterator != json.end())
signatures.insert(signaturesIterator->begin(), signaturesIterator->end());
if (auto * signaturesJson = get(obj, "signatures"))
signatures = getStringSet(*signaturesJson);
std::map <DrvOutput, StorePath> dependentRealisations;
if (auto jsonDependencies = json.find("dependentRealisations"); jsonDependencies != json.end())
for (auto & [jsonDepId, jsonDepOutPath] : jsonDependencies->get<StringMap>())
dependentRealisations.insert({DrvOutput::parse(jsonDepId), StorePath(jsonDepOutPath)});
return Realisation{
.id = DrvOutput::parse(getField("id")),
.outPath = StorePath(getField("outPath")),
return {
.outPath = store.parseStorePath(getString(valueAt(obj, "outPath"))),
.signatures = signatures,
.dependentRealisations = dependentRealisations,
};
}
std::string Realisation::fingerprint() const
nlohmann::json Realisation::toJSON(const StoreDirConfig & store) const
{
auto serialized = toJSON();
serialized.erase("signatures");
return serialized.dump();
return nlohmann::json{
{"key", id.toJSON(store)},
{"value", static_cast<const UnkeyedRealisation &>(*this).toJSON(store)},
};
}
void Realisation::sign(const Signer &signer)
Realisation Realisation::fromJSON(const StoreDirConfig & store, const nlohmann::json & json)
{
signatures.insert(signer.signDetached(fingerprint()));
auto obj = getObject(json);
return {
UnkeyedRealisation::fromJSON(store, valueAt(obj, "key")),
DrvOutput::fromJSON(store, valueAt(obj, "value")),
};
}
bool Realisation::checkSignature(const PublicKeys & publicKeys, const std::string & sig) const
std::string UnkeyedRealisation::fingerprint(const StoreDirConfig & store, const DrvOutput & key) const
{
return verifyDetached(fingerprint(), sig, publicKeys);
auto serialised = Realisation{*this, key}.toJSON(store);
auto value = serialised.find("value");
assert(value != serialised.end());
value->erase("signatures");
return serialised.dump();
}
size_t Realisation::checkSignatures(const PublicKeys & publicKeys) const
void UnkeyedRealisation::sign(const StoreDirConfig & store, const DrvOutput & key, const Signer & signer)
{
signatures.insert(signer.signDetached(fingerprint(store, key)));
}
bool UnkeyedRealisation::checkSignature(
const StoreDirConfig & store, const DrvOutput & key, const PublicKeys & publicKeys, const std::string & sig) const
{
return verifyDetached(fingerprint(store, key), sig, publicKeys);
}
size_t UnkeyedRealisation::checkSignatures(
const StoreDirConfig & store, const DrvOutput & key, const PublicKeys & publicKeys) const
{
// FIXME: Maybe we should return `maxSigs` if the realisation corresponds to
// an input-addressed one because in that case the drv is enough to check
@ -132,16 +115,15 @@ size_t Realisation::checkSignatures(const PublicKeys & publicKeys) const
size_t good = 0;
for (auto & sig : signatures)
if (checkSignature(publicKeys, sig))
if (checkSignature(store, key, publicKeys, sig))
good++;
return good;
}
SingleDrvOutputs filterDrvOutputs(const OutputsSpec& wanted, SingleDrvOutputs&& outputs)
SingleDrvOutputs filterDrvOutputs(const OutputsSpec & wanted, SingleDrvOutputs && outputs)
{
SingleDrvOutputs ret = std::move(outputs);
for (auto it = ret.begin(); it != ret.end(); ) {
for (auto it = ret.begin(); it != ret.end();) {
if (!wanted.contains(it->first))
it = ret.erase(it);
else
@ -150,53 +132,25 @@ SingleDrvOutputs filterDrvOutputs(const OutputsSpec& wanted, SingleDrvOutputs&&
return ret;
}
StorePath RealisedPath::path() const {
return std::visit([](auto && arg) { return arg.getPath(); }, raw);
const StorePath & RealisedPath::path() const
{
return std::visit([](auto && arg) -> auto & { return arg.getPath(); }, raw);
}
bool Realisation::isCompatibleWith(const Realisation & other) const
MissingRealisation::MissingRealisation(
const StoreDirConfig & store, const StorePath & drvPath, const OutputName & outputName)
: Error("cannot operate on output '%s' of the unbuilt derivation '%s'", outputName, store.printStorePath(drvPath))
{
assert (id == other.id);
if (outPath == other.outPath) {
if (dependentRealisations.empty() != other.dependentRealisations.empty()) {
warn(
"Encountered a realisation for '%s' with an empty set of "
"dependencies. This is likely an artifact from an older Nix. "
"Ill try to fix the realisation if I can",
id.to_string());
return true;
} else if (dependentRealisations == other.dependentRealisations) {
return true;
}
}
return false;
}
void RealisedPath::closure(
Store& store,
const RealisedPath::Set& startPaths,
RealisedPath::Set& ret)
MissingRealisation::MissingRealisation(
const StoreDirConfig & store,
const SingleDerivedPath & drvPath,
const StorePath & drvPathResolved,
const OutputName & outputName)
: MissingRealisation{store, drvPathResolved, outputName}
{
// FIXME: This only builds the store-path closure, not the real realisation
// closure
StorePathSet initialStorePaths, pathsClosure;
for (auto& path : startPaths)
initialStorePaths.insert(path.path());
store.computeFSClosure(initialStorePaths, pathsClosure);
ret.insert(startPaths.begin(), startPaths.end());
ret.insert(pathsClosure.begin(), pathsClosure.end());
}
void RealisedPath::closure(Store& store, RealisedPath::Set & ret) const
{
RealisedPath::closure(store, {*this}, ret);
}
RealisedPath::Set RealisedPath::closure(Store& store) const
{
RealisedPath::Set ret;
closure(store, ret);
return ret;
addTrace({}, "looking up realisation for derivation '%s'", drvPath.to_string(store));
}
} // namespace nix

View file

@ -602,12 +602,12 @@ void RemoteStore::registerDrvOutput(const Realisation & info)
}
void RemoteStore::queryRealisationUncached(const DrvOutput & id,
Callback<std::shared_ptr<const Realisation>> callback) noexcept
Callback<std::shared_ptr<const UnkeyedRealisation>> callback) noexcept
{
try {
auto conn(getConnection());
if (GET_PROTOCOL_MINOR(conn->protoVersion) < 27) {
if (GET_PROTOCOL_MINOR(conn->protoVersion) < 39) {
warn("the daemon is too old to support content-addressing derivations, please upgrade it to 2.4");
return callback(nullptr);
}
@ -616,23 +616,13 @@ void RemoteStore::queryRealisationUncached(const DrvOutput & id,
conn->to << id.to_string();
conn.processStderr();
auto real = [&]() -> std::shared_ptr<const Realisation> {
if (GET_PROTOCOL_MINOR(conn->protoVersion) < 31) {
auto outPaths = WorkerProto::Serialise<std::set<StorePath>>::read(
*this, *conn);
if (outPaths.empty())
return nullptr;
return std::make_shared<const Realisation>(Realisation { .id = id, .outPath = *outPaths.begin() });
} else {
auto realisations = WorkerProto::Serialise<std::set<Realisation>>::read(
*this, *conn);
if (realisations.empty())
return nullptr;
return std::make_shared<const Realisation>(*realisations.begin());
}
}();
callback(std::shared_ptr<const Realisation>(real));
callback([&]() -> std::shared_ptr<const UnkeyedRealisation> {
auto realisation = WorkerProto::Serialise<std::optional<UnkeyedRealisation>>::read(
*this, *conn);
if (!realisation)
return nullptr;
return std::make_shared<const UnkeyedRealisation>(*realisation);
}());
} catch (...) { return callback.rethrow(); }
}
@ -724,27 +714,19 @@ std::vector<KeyedBuildResult> RemoteStore::buildPathsWithResults(
OutputPathMap outputs;
auto drvPath = resolveDerivedPath(*evalStore, *bfd.drvPath);
auto drv = evalStore->readDerivation(drvPath);
const auto outputHashes = staticOutputHashes(*evalStore, drv); // FIXME: expensive
auto built = resolveDerivedPath(*this, bfd, &*evalStore);
for (auto & [output, outputPath] : built) {
auto outputHash = get(outputHashes, output);
if (!outputHash)
throw Error(
"the derivation '%s' doesn't have an output named '%s'",
printStorePath(drvPath), output);
auto outputId = DrvOutput{ *outputHash, output };
auto outputId = DrvOutput{ drvPath, output };
if (experimentalFeatureSettings.isEnabled(Xp::CaDerivations)) {
auto realisation =
queryRealisation(outputId);
if (!realisation)
throw MissingRealisation(outputId);
throw MissingRealisation(*this, outputId);
res.builtOutputs.emplace(output, *realisation);
} else {
res.builtOutputs.emplace(
output,
Realisation {
.id = outputId,
UnkeyedRealisation {
.outPath = outputPath,
});
}

View file

@ -111,7 +111,7 @@ struct RestrictedStore : public virtual IndirectRootStore, public virtual GcStor
void registerDrvOutput(const Realisation & info) override;
void queryRealisationUncached(
const DrvOutput & id, Callback<std::shared_ptr<const Realisation>> callback) noexcept override;
const DrvOutput & id, Callback<std::shared_ptr<const UnkeyedRealisation>> callback) noexcept override;
void
buildPaths(const std::vector<DerivedPath> & paths, BuildMode buildMode, std::shared_ptr<Store> evalStore) override;
@ -254,7 +254,7 @@ void RestrictedStore::registerDrvOutput(const Realisation & info)
}
void RestrictedStore::queryRealisationUncached(
const DrvOutput & id, Callback<std::shared_ptr<const Realisation>> callback) noexcept
const DrvOutput & id, Callback<std::shared_ptr<const UnkeyedRealisation>> callback) noexcept
// XXX: This should probably be allowed if the realisation corresponds to
// an allowed derivation
{
@ -290,9 +290,19 @@ std::vector<KeyedBuildResult> RestrictedStore::buildPathsWithResults(
auto results = next->buildPathsWithResults(paths, buildMode);
for (auto & result : results) {
for (auto & [outputName, output] : result.builtOutputs) {
newPaths.insert(output.outPath);
newRealisations.insert(output);
if (auto * pathBuilt = std::get_if<DerivedPathBuilt>(&result.path)) {
// TODO ugly extra IO
auto drvPath = resolveDerivedPath(*next, *pathBuilt->drvPath);
for (auto & [outputName, output] : result.builtOutputs) {
newPaths.insert(output.outPath);
newRealisations.insert({
output,
{
.drvPath = drvPath,
.outputName = outputName,
}
});
}
}
}
@ -300,7 +310,7 @@ std::vector<KeyedBuildResult> RestrictedStore::buildPathsWithResults(
next->computeFSClosure(newPaths, closure);
for (auto & path : closure)
goal.addDependency(path);
for (auto & real : Realisation::closure(*next, newRealisations))
for (auto & real : newRealisations)
goal.addedDrvOutputs.insert(real.id);
return results;

View file

@ -6,6 +6,7 @@
#include "nix/store/serve-protocol-impl.hh"
#include "nix/util/archive.hh"
#include "nix/store/path-info.hh"
#include "nix/util/json-utils.hh"
#include <nlohmann/json.hpp>
@ -25,13 +26,22 @@ BuildResult ServeProto::Serialise<BuildResult>::read(const StoreDirConfig & stor
>> status.isNonDeterministic
>> status.startTime
>> status.stopTime;
if (GET_PROTOCOL_MINOR(conn.version) >= 6) {
auto builtOutputs = ServeProto::Serialise<DrvOutputs>::read(store, conn);
for (auto && [output, realisation] : builtOutputs)
if (GET_PROTOCOL_MINOR(conn.version) >= 8) {
status.builtOutputs = ServeProto::Serialise<std::map<OutputName, UnkeyedRealisation>>::read(store, conn);
} else if (GET_PROTOCOL_MINOR(conn.version) >= 6) {
for (auto & [output, realisation] : ServeProto::Serialise<StringMap>::read(store, conn)) {
size_t n = output.find("!");
if (n == output.npos)
throw Error("Invalid derivation output id %s", output);
status.builtOutputs.insert_or_assign(
std::move(output.outputName),
std::move(realisation));
output.substr(n + 1),
UnkeyedRealisation{StorePath{
getString(valueAt(getObject(nlohmann::json::parse(realisation)), "outPath"))
}});
}
}
return status;
}
@ -47,11 +57,12 @@ void ServeProto::Serialise<BuildResult>::write(const StoreDirConfig & store, Ser
<< status.isNonDeterministic
<< status.startTime
<< status.stopTime;
if (GET_PROTOCOL_MINOR(conn.version) >= 6) {
DrvOutputs builtOutputs;
for (auto & [output, realisation] : status.builtOutputs)
builtOutputs.insert_or_assign(realisation.id, realisation);
ServeProto::write(store, conn, builtOutputs);
if (GET_PROTOCOL_MINOR(conn.version) >= 8) {
ServeProto::write(store, conn, status.builtOutputs);
} else if (GET_PROTOCOL_MINOR(conn.version) >= 6) {
// We no longer support these types of realisations
ServeProto::write(store, conn, StringMap{});
}
}
@ -134,4 +145,80 @@ void ServeProto::Serialise<ServeProto::BuildOptions>::write(const StoreDirConfig
}
}
UnkeyedRealisation ServeProto::Serialise<UnkeyedRealisation>::read(const StoreDirConfig & store, ReadConn conn)
{
if (GET_PROTOCOL_MINOR(conn.version) < 8) {
throw Error("daemon protocol %d.%d is too old (< 2.8) to understand build trace",
GET_PROTOCOL_MAJOR(conn.version) >> 8,
GET_PROTOCOL_MINOR(conn.version));
}
auto outPath = ServeProto::Serialise<StorePath>::read(store, conn);
auto signatures = ServeProto::Serialise<StringSet>::read(store, conn);
return UnkeyedRealisation {
.outPath = std::move(outPath),
.signatures = std::move(signatures),
};
}
void ServeProto::Serialise<UnkeyedRealisation>::write(const StoreDirConfig & store, WriteConn conn, const UnkeyedRealisation & info)
{
if (GET_PROTOCOL_MINOR(conn.version) < 8) {
throw Error("daemon protocol %d.%d is too old (< 2.8) to understand build trace",
GET_PROTOCOL_MAJOR(conn.version) >> 8,
GET_PROTOCOL_MINOR(conn.version));
}
ServeProto::write(store, conn, info.outPath);
ServeProto::write(store, conn, info.signatures);
}
DrvOutput ServeProto::Serialise<DrvOutput>::read(const StoreDirConfig & store, ReadConn conn)
{
if (GET_PROTOCOL_MINOR(conn.version) < 8) {
throw Error("daemon protocol %d.%d is too old (< 2.8) to understand build trace",
GET_PROTOCOL_MAJOR(conn.version) >> 8,
GET_PROTOCOL_MINOR(conn.version));
}
auto drvPath = ServeProto::Serialise<StorePath>::read(store, conn);
auto outputName = ServeProto::Serialise<std::string>::read(store, conn);
return DrvOutput {
.drvPath = std::move(drvPath),
.outputName = std::move(outputName),
};
}
void ServeProto::Serialise<DrvOutput>::write(const StoreDirConfig & store, WriteConn conn, const DrvOutput & info)
{
if (GET_PROTOCOL_MINOR(conn.version) < 8) {
throw Error("daemon protocol %d.%d is too old (< 2.8) to understand build trace",
GET_PROTOCOL_MAJOR(conn.version) >> 8,
GET_PROTOCOL_MINOR(conn.version));
}
ServeProto::write(store, conn, info.drvPath);
ServeProto::write(store, conn, info.outputName);
}
Realisation ServeProto::Serialise<Realisation>::read(const StoreDirConfig & store, ReadConn conn)
{
auto id = ServeProto::Serialise<DrvOutput>::read(store, conn);
auto unkeyed = ServeProto::Serialise<UnkeyedRealisation>::read(store, conn);
return Realisation {
std::move(unkeyed),
std::move(id),
};
}
void ServeProto::Serialise<Realisation>::write(const StoreDirConfig & store, WriteConn conn, const Realisation & info)
{
ServeProto::write(store, conn, info.id);
ServeProto::write(store, conn, static_cast<const UnkeyedRealisation &>(info));
}
}

View file

@ -479,9 +479,8 @@ std::map<std::string, std::optional<StorePath>> Store::queryPartialDerivationOut
return outputs;
auto drv = evalStore.readInvalidDerivation(path);
auto drvHashes = staticOutputHashes(*this, drv);
for (auto & [outputName, hash] : drvHashes) {
auto realisation = queryRealisation(DrvOutput{hash, outputName});
for (auto & [outputName, _] : drv.outputs) {
auto realisation = queryRealisation(DrvOutput{path, outputName});
if (realisation) {
outputs.insert_or_assign(outputName, realisation->outPath);
} else {
@ -500,7 +499,7 @@ OutputPathMap Store::queryDerivationOutputMap(const StorePath & path, Store * ev
OutputPathMap result;
for (auto & [outName, optOutPath] : resp) {
if (!optOutPath)
throw MissingRealisation(printStorePath(path), outName);
throw MissingRealisation(*this, path, outName);
result.insert_or_assign(outName, *optOutPath);
}
return result;
@ -716,7 +715,7 @@ void Store::queryPathInfo(const StorePath & storePath,
}
void Store::queryRealisation(const DrvOutput & id,
Callback<std::shared_ptr<const Realisation>> callback) noexcept
Callback<std::shared_ptr<const UnkeyedRealisation>> callback) noexcept
{
try {
@ -748,18 +747,18 @@ void Store::queryRealisation(const DrvOutput & id,
queryRealisationUncached(
id,
{ [this, id, callbackPtr](
std::future<std::shared_ptr<const Realisation>> fut) {
std::future<std::shared_ptr<const UnkeyedRealisation>> fut) {
try {
auto info = fut.get();
if (diskCache) {
if (info)
diskCache->upsertRealisation(getUri(), *info);
diskCache->upsertRealisation(getUri(), {*info, id});
else
diskCache->upsertAbsentRealisation(getUri(), id);
}
(*callbackPtr)(std::shared_ptr<const Realisation>(info));
(*callbackPtr)(std::shared_ptr<const UnkeyedRealisation>(info));
} catch (...) {
callbackPtr->rethrow();
@ -767,9 +766,9 @@ void Store::queryRealisation(const DrvOutput & id,
} });
}
std::shared_ptr<const Realisation> Store::queryRealisation(const DrvOutput & id)
std::shared_ptr<const UnkeyedRealisation> Store::queryRealisation(const DrvOutput & id)
{
using RealPtr = std::shared_ptr<const Realisation>;
using RealPtr = std::shared_ptr<const UnkeyedRealisation>;
std::promise<RealPtr> promise;
queryRealisation(id,
@ -1019,36 +1018,21 @@ std::map<StorePath, StorePath> copyPaths(
SubstituteFlag substitute)
{
StorePathSet storePaths;
std::set<Realisation> toplevelRealisations;
std::vector<const Realisation *> realisations;
for (auto & path : paths) {
storePaths.insert(path.path());
if (auto realisation = std::get_if<Realisation>(&path.raw)) {
if (auto * realisation = std::get_if<Realisation>(&path.raw)) {
experimentalFeatureSettings.require(Xp::CaDerivations);
toplevelRealisations.insert(*realisation);
realisations.push_back(realisation);
}
}
auto pathsMap = copyPaths(srcStore, dstStore, storePaths, repair, checkSigs, substitute);
try {
// Copy the realisation closure
processGraph<Realisation>(
Realisation::closure(srcStore, toplevelRealisations),
[&](const Realisation & current) -> std::set<Realisation> {
std::set<Realisation> children;
for (const auto & [drvOutput, _] : current.dependentRealisations) {
auto currentChild = srcStore.queryRealisation(drvOutput);
if (!currentChild)
throw Error(
"incomplete realisation closure: '%s' is a "
"dependency of '%s' but isn't registered",
drvOutput.to_string(), current.id.to_string());
children.insert(*currentChild);
}
return children;
},
[&](const Realisation& current) -> void {
dstStore.registerDrvOutput(current, checkSigs);
});
for (const auto * realisation : realisations)
dstStore.registerDrvOutput(*realisation, checkSigs);
} catch (MissingExperimentalFeature & e) {
// Don't fail if the remote doesn't support CA derivations is it might
// not be within our control to change that, and we might still want
@ -1154,8 +1138,19 @@ void copyClosure(
{
if (&srcStore == &dstStore) return;
RealisedPath::Set closure;
RealisedPath::closure(srcStore, paths, closure);
StorePathSet closure0;
for (auto & path : paths) {
if (auto * opaquePath = std::get_if<OpaquePath>(&path.raw)) {
closure0.insert(opaquePath->path);
}
}
StorePathSet closure1;
srcStore.computeFSClosure(closure0, closure1);
RealisedPath::Set closure = paths;
for (auto && path : closure1)
closure.insert({std::move(path)});
copyPaths(srcStore, dstStore, closure, repair, checkSigs, substitute);
}
@ -1300,7 +1295,7 @@ void Store::signRealisation(Realisation & realisation)
for (auto & secretKeyFile : secretKeyFiles.get()) {
SecretKey secretKey(readFile(secretKeyFile));
LocalSigner signer(std::move(secretKey));
realisation.sign(signer);
realisation.sign(*this, realisation.id, signer);
}
}

View file

@ -2889,11 +2889,13 @@ SingleDrvOutputs DerivationBuilderImpl::registerOutputs()
auto oldinfo = get(initialOutputs, outputName);
assert(oldinfo);
auto thisRealisation = Realisation {
.id = DrvOutput {
oldinfo->outputHash,
outputName
{
.outPath = newInfo.path,
},
DrvOutput {
.drvPath = drvPath,
.outputName = outputName,
},
.outPath = newInfo.path
};
if (experimentalFeatureSettings.isEnabled(Xp::CaDerivations)
&& !drv.type().isImpure())

View file

@ -6,6 +6,7 @@
#include "nix/store/worker-protocol-impl.hh"
#include "nix/util/archive.hh"
#include "nix/store/path-info.hh"
#include "nix/util/json-utils.hh"
#include <chrono>
#include <nlohmann/json.hpp>
@ -124,7 +125,7 @@ void WorkerProto::Serialise<DerivedPath>::write(const StoreDirConfig & store, Wo
[&](const StorePath & drvPath) {
throw Error("trying to request '%s', but daemon protocol %d.%d is too old (< 1.29) to request a derivation file",
store.printStorePath(drvPath),
GET_PROTOCOL_MAJOR(conn.version),
GET_PROTOCOL_MAJOR(conn.version) >> 8,
GET_PROTOCOL_MINOR(conn.version));
},
[&](std::monostate) {
@ -157,6 +158,7 @@ BuildResult WorkerProto::Serialise<BuildResult>::read(const StoreDirConfig & sto
BuildResult res;
res.status = static_cast<BuildResult::Status>(readInt(conn.from));
conn.from >> res.errorMsg;
if (GET_PROTOCOL_MINOR(conn.version) >= 29) {
conn.from
>> res.timesBuilt
@ -164,17 +166,27 @@ BuildResult WorkerProto::Serialise<BuildResult>::read(const StoreDirConfig & sto
>> res.startTime
>> res.stopTime;
}
if (GET_PROTOCOL_MINOR(conn.version) >= 37) {
res.cpuUser = WorkerProto::Serialise<std::optional<std::chrono::microseconds>>::read(store, conn);
res.cpuSystem = WorkerProto::Serialise<std::optional<std::chrono::microseconds>>::read(store, conn);
}
if (GET_PROTOCOL_MINOR(conn.version) >= 28) {
auto builtOutputs = WorkerProto::Serialise<DrvOutputs>::read(store, conn);
for (auto && [output, realisation] : builtOutputs)
if (GET_PROTOCOL_MINOR(conn.version) >= 39) {
res.builtOutputs = WorkerProto::Serialise<std::map<OutputName, UnkeyedRealisation>>::read(store, conn);
} else if (GET_PROTOCOL_MINOR(conn.version) >= 28) {
for (auto && [output, realisation] : WorkerProto::Serialise<StringMap>::read(store, conn)) {
size_t n = output.find("!");
if (n == output.npos)
throw Error("Invalid derivation output id %s", output);
res.builtOutputs.insert_or_assign(
std::move(output.outputName),
std::move(realisation));
output.substr(n + 1),
UnkeyedRealisation{StorePath{
getString(valueAt(getObject(nlohmann::json::parse(realisation)), "outPath"))
}});
}
}
return res;
}
@ -183,6 +195,7 @@ void WorkerProto::Serialise<BuildResult>::write(const StoreDirConfig & store, Wo
conn.to
<< res.status
<< res.errorMsg;
if (GET_PROTOCOL_MINOR(conn.version) >= 29) {
conn.to
<< res.timesBuilt
@ -190,15 +203,17 @@ void WorkerProto::Serialise<BuildResult>::write(const StoreDirConfig & store, Wo
<< res.startTime
<< res.stopTime;
}
if (GET_PROTOCOL_MINOR(conn.version) >= 37) {
WorkerProto::write(store, conn, res.cpuUser);
WorkerProto::write(store, conn, res.cpuSystem);
}
if (GET_PROTOCOL_MINOR(conn.version) >= 28) {
DrvOutputs builtOutputs;
for (auto & [output, realisation] : res.builtOutputs)
builtOutputs.insert_or_assign(realisation.id, realisation);
WorkerProto::write(store, conn, builtOutputs);
if (GET_PROTOCOL_MINOR(conn.version) >= 39) {
WorkerProto::write(store, conn, res.builtOutputs);
} else if (GET_PROTOCOL_MINOR(conn.version) >= 28) {
// Don't support those types of realisations anymore.
WorkerProto::write(store, conn, StringMap{});
}
}
@ -260,7 +275,7 @@ WorkerProto::ClientHandshakeInfo WorkerProto::Serialise<WorkerProto::ClientHands
}
if (GET_PROTOCOL_MINOR(conn.version) >= 35) {
res.remoteTrustsUs = WorkerProto::Serialise<std::optional< TrustedFlag>>::read(store, conn);
res.remoteTrustsUs = WorkerProto::Serialise<std::optional<TrustedFlag>>::read(store, conn);
} else {
// We don't know the answer; protocol to old.
res.remoteTrustsUs = std::nullopt;
@ -281,4 +296,110 @@ void WorkerProto::Serialise<WorkerProto::ClientHandshakeInfo>::write(const Store
}
}
UnkeyedRealisation WorkerProto::Serialise<UnkeyedRealisation>::read(const StoreDirConfig & store, ReadConn conn)
{
if (GET_PROTOCOL_MINOR(conn.version) < 39) {
throw Error("daemon protocol %d.%d is too old (< 1.39) to understand build trace",
GET_PROTOCOL_MAJOR(conn.version) >> 8,
GET_PROTOCOL_MINOR(conn.version));
}
auto outPath = WorkerProto::Serialise<StorePath>::read(store, conn);
auto signatures = WorkerProto::Serialise<StringSet>::read(store, conn);
return UnkeyedRealisation {
.outPath = std::move(outPath),
.signatures = std::move(signatures),
};
}
void WorkerProto::Serialise<UnkeyedRealisation>::write(const StoreDirConfig & store, WriteConn conn, const UnkeyedRealisation & info)
{
if (GET_PROTOCOL_MINOR(conn.version) < 39) {
throw Error("daemon protocol %d.%d is too old (< 1.39) to understand build trace",
GET_PROTOCOL_MAJOR(conn.version) >> 8,
GET_PROTOCOL_MINOR(conn.version));
}
WorkerProto::write(store, conn, info.outPath);
WorkerProto::write(store, conn, info.signatures);
}
std::optional<UnkeyedRealisation> WorkerProto::Serialise<std::optional<UnkeyedRealisation>>::read(const StoreDirConfig & store, ReadConn conn)
{
if (GET_PROTOCOL_MINOR(conn.version) < 39) {
// Hack to improve compat
(void) WorkerProto::Serialise<std::string>::read(store, conn);
return std::nullopt;
} else {
auto temp = readNum<uint8_t>(conn.from);
switch (temp) {
case 0:
return std::nullopt;
case 1:
return WorkerProto::Serialise<UnkeyedRealisation>::read(store, conn);
default:
throw Error("Invalid optional build trace from remote");
}
}
}
void WorkerProto::Serialise<std::optional<UnkeyedRealisation>>::write(const StoreDirConfig & store, WriteConn conn, const std::optional<UnkeyedRealisation> & info)
{
if (!info) {
conn.to << uint8_t{0};
} else {
conn.to << uint8_t{1};
WorkerProto::write(store, conn, *info);
}
}
DrvOutput WorkerProto::Serialise<DrvOutput>::read(const StoreDirConfig & store, ReadConn conn)
{
if (GET_PROTOCOL_MINOR(conn.version) < 39) {
throw Error("daemon protocol %d.%d is too old (< 1.29) to understand build trace",
GET_PROTOCOL_MAJOR(conn.version) >> 8,
GET_PROTOCOL_MINOR(conn.version));
}
auto drvPath = WorkerProto::Serialise<StorePath>::read(store, conn);
auto outputName = WorkerProto::Serialise<std::string>::read(store, conn);
return DrvOutput {
.drvPath = std::move(drvPath),
.outputName = std::move(outputName),
};
}
void WorkerProto::Serialise<DrvOutput>::write(const StoreDirConfig & store, WriteConn conn, const DrvOutput & info)
{
if (GET_PROTOCOL_MINOR(conn.version) < 39) {
throw Error("daemon protocol %d.%d is too old (< 1.29) to understand build trace",
GET_PROTOCOL_MAJOR(conn.version) >> 8,
GET_PROTOCOL_MINOR(conn.version));
}
WorkerProto::write(store, conn, info.drvPath);
WorkerProto::write(store, conn, info.outputName);
}
Realisation WorkerProto::Serialise<Realisation>::read(const StoreDirConfig & store, ReadConn conn)
{
auto id = WorkerProto::Serialise<DrvOutput>::read(store, conn);
auto unkeyed = WorkerProto::Serialise<UnkeyedRealisation>::read(store, conn);
return Realisation {
std::move(unkeyed),
std::move(id),
};
}
void WorkerProto::Serialise<Realisation>::write(const StoreDirConfig & store, WriteConn conn, const Realisation & info)
{
WorkerProto::write(store, conn, info.id);
WorkerProto::write(store, conn, static_cast<const UnkeyedRealisation &>(info));
}
}

View file

@ -272,16 +272,8 @@ static StorePath getDerivationEnvironment(ref<Store> store, ref<Store> evalStore
output.second = DerivationOutput::Deferred { };
drv.env[output.first] = "";
}
auto hashesModulo = hashDerivationModulo(*evalStore, drv, true);
for (auto & output : drv.outputs) {
Hash h = hashesModulo.hashes.at(output.first);
auto outPath = store->makeOutputPath(output.first, h, drv.name);
output.second = DerivationOutput::InputAddressed {
.path = outPath,
};
drv.env[output.first] = store->printStorePath(outPath);
}
resolveInputAddressed(*evalStore, drv);
}
auto shellDrvPath = writeDerivation(*evalStore, drv);

View file

@ -51,7 +51,7 @@ struct CmdRealisationInfo : BuiltPathsCommand, MixJSON
for (auto & path : realisations) {
nlohmann::json currentPath;
if (auto realisation = std::get_if<Realisation>(&path.raw))
currentPath = realisation->toJSON();
currentPath = realisation->toJSON(*store);
else
currentPath["opaquePath"] = store->printStorePath(path.path());

View file

@ -40,7 +40,7 @@ O_OBJECT
}
else {
warn( \"${Package}::$func_name() -- \"
\"$var not a blessed SV reference\");
\"$var not a blessed SV reference\");
XSRETURN_UNDEF;
}
HERE
@ -162,12 +162,15 @@ StoreWrapper::queryPathInfo(char * path, int base32)
}
SV *
StoreWrapper::queryRawRealisation(char * outputId)
StoreWrapper::queryRawRealisation(char * drvPath, char * outputName)
PPCODE:
try {
auto realisation = THIS->store->queryRealisation(DrvOutput::parse(outputId));
auto realisation = THIS->store->queryRealisation(DrvOutput{
.drvPath = THIS->store->parseStorePath(drvPath),
.outputName = outputName,
});
if (realisation)
XPUSHs(sv_2mortal(newSVpv(realisation->toJSON().dump().c_str(), 0)));
XPUSHs(sv_2mortal(newSVpv(realisation->toJSON(*THIS->store).dump().c_str(), 0)));
else
XPUSHs(sv_2mortal(newSVpv("", 0)));
} catch (Error & e) {

View file

@ -58,14 +58,7 @@ clearCache
RES=$(nix build -f dependencies.nix --dry-run --json)
if [[ -z "${NIX_TESTS_CA_BY_DEFAULT-}" ]]; then
echo "$RES" | jq '.[0] | [
(.drvPath | test("'"$NIX_STORE_DIR"'.*\\.drv")),
(.outputs.out | test("'"$NIX_STORE_DIR"'"))
] | all'
else
echo "$RES" | jq '.[0] | [
(.drvPath | test("'"$NIX_STORE_DIR"'.*\\.drv")),
.outputs.out == null
] | all'
fi
echo "$RES" | jq '.[0] | [
(.drvPath | test("'"$NIX_STORE_DIR"'.*\\.drv")),
.outputs == [ "out" ]
] | all'