1
0
Fork 0
mirror of https://github.com/NixOS/nix synced 2025-06-25 02:21:16 +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/include/nix/store/pathlocks\.hh$''
''^src/libstore/profiles\.cc$'' ''^src/libstore/profiles\.cc$''
''^src/libstore/include/nix/store/profiles\.hh$'' ''^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/remote-fs-accessor\.cc$''
''^src/libstore/include/nix/store/remote-fs-accessor\.hh$'' ''^src/libstore/include/nix/store/remote-fs-accessor\.hh$''
''^src/libstore/include/nix/store/remote-store-connection\.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/include/nix/store/build/derivation-building-goal\.hh$''
''^src/libstore/build/derivation-goal\.cc$'' ''^src/libstore/build/derivation-goal\.cc$''
''^src/libstore/include/nix/store/build/derivation-goal\.hh$'' ''^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/entry-points\.cc$''
''^src/libstore/build/goal\.cc$'' ''^src/libstore/build/goal\.cc$''
''^src/libstore/include/nix/store/build/goal\.hh$'' ''^src/libstore/include/nix/store/build/goal\.hh$''

View file

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

View file

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

View file

@ -1565,31 +1565,7 @@ static void derivationStrictInternal(
DerivationOutput::Deferred { }); DerivationOutput::Deferred { });
} }
auto hashModulo = hashDerivationModulo(*state.store, Derivation(drv), true); resolveInputAddressed(*state.store, drv);
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 {});
}
}
} }
/* Write the resulting term into the Nix store directory. */ /* 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 { 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) { \ TEST_F(FIXTURE, NAME ## _read) { \
readProtoTest(STEM, VERSION, VALUE); \ readProtoTest(STEM, VERSION, VALUE); \
} \ }
#define VERSIONED_WRITE_CHARACTERIZATION_TEST(FIXTURE, NAME, STEM, VERSION, VALUE) \
TEST_F(FIXTURE, NAME ## _write) { \ TEST_F(FIXTURE, NAME ## _write) { \
writeProtoTest(STEM, VERSION, VALUE); \ 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( CHARACTERIZATION_TEST(
vector, vector,
"vector", "vector",

View file

@ -72,16 +72,16 @@ VERSIONED_CHARACTERIZATION_TEST(
VERSIONED_CHARACTERIZATION_TEST( VERSIONED_CHARACTERIZATION_TEST(
ServeProtoTest, ServeProtoTest,
drvOutput, drvOutput_2_8,
"drv-output", "drv-output-2.8",
defaultVersion, 2 << 8 | 8,
(std::tuple<DrvOutput, DrvOutput> { (std::tuple<DrvOutput, DrvOutput> {
{ {
.drvHash = Hash::parseSRI("sha256-FePFYIlMuycIXPZbWi7LGEiMmZSX9FMbaQenWBzm1Sc="), .drvPath = StorePath { "g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-foo.drv" },
.outputName = "baz", .outputName = "baz",
}, },
DrvOutput { DrvOutput {
.drvHash = Hash::parseSRI("sha256-b4afnqKCO9oWXgYHb9DeQ2berSwOjS27rSd9TxXDc/U="), .drvPath = StorePath { "g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-foo.drv" },
.outputName = "quux", .outputName = "quux",
}, },
})) }))
@ -90,34 +90,27 @@ VERSIONED_CHARACTERIZATION_TEST(
VERSIONED_CHARACTERIZATION_TEST( VERSIONED_CHARACTERIZATION_TEST(
ServeProtoTest, ServeProtoTest,
realisation, unkeyedRealisation_2_8,
"realisation", "unkeyed-realisation-2.8",
defaultVersion, 2 << 8 | 8,
(std::tuple<Realisation, Realisation> { (UnkeyedRealisation {
Realisation { .outPath = StorePath { "g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-foo" },
.id = DrvOutput { .signatures = { "asdf", "qwer" },
.drvHash = Hash::parseSRI("sha256-FePFYIlMuycIXPZbWi7LGEiMmZSX9FMbaQenWBzm1Sc="), }))
.outputName = "baz",
}, VERSIONED_CHARACTERIZATION_TEST(
ServeProtoTest,
realisation_2_8,
"realisation-2.8",
2 << 8 | 8,
(Realisation {
UnkeyedRealisation {
.outPath = StorePath { "g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-foo" }, .outPath = StorePath { "g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-foo" },
.signatures = { "asdf", "qwer" }, .signatures = { "asdf", "qwer" },
}, },
Realisation { DrvOutput {
.id = { .drvPath = StorePath { "g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-foo.drv" },
.drvHash = Hash::parseSRI("sha256-FePFYIlMuycIXPZbWi7LGEiMmZSX9FMbaQenWBzm1Sc="), .outputName = "baz",
.outputName = "baz",
},
.outPath = StorePath { "g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-foo" },
.signatures = { "asdf", "qwer" },
.dependentRealisations = {
{
DrvOutput {
.drvHash = Hash::parseSRI("sha256-b4afnqKCO9oWXgYHb9DeQ2berSwOjS27rSd9TxXDc/U="),
.outputName = "quux",
},
StorePath { "g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-foo" },
},
},
}, },
})) }))
@ -173,7 +166,10 @@ VERSIONED_CHARACTERIZATION_TEST(
t; 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, ServeProtoTest,
buildResult_2_6, buildResult_2_6,
"build-result-2.6", "build-result-2.6",
@ -200,20 +196,64 @@ VERSIONED_CHARACTERIZATION_TEST(
{ {
"foo", "foo",
{ {
.id = DrvOutput {
.drvHash = Hash::parseSRI("sha256-b4afnqKCO9oWXgYHb9DeQ2berSwOjS27rSd9TxXDc/U="),
.outputName = "foo",
},
.outPath = StorePath { "g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-foo" }, .outPath = StorePath { "g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-foo" },
}, },
}, },
{ {
"bar", "bar",
{ {
.id = DrvOutput { .outPath = StorePath { "g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-bar" },
.drvHash = Hash::parseSRI("sha256-b4afnqKCO9oWXgYHb9DeQ2berSwOjS27rSd9TxXDc/U="), },
.outputName = "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" }, .outPath = StorePath { "g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-bar" },
}, },
}, },

View file

@ -125,49 +125,42 @@ VERSIONED_CHARACTERIZATION_TEST(
VERSIONED_CHARACTERIZATION_TEST( VERSIONED_CHARACTERIZATION_TEST(
WorkerProtoTest, WorkerProtoTest,
drvOutput, drvOutput,
"drv-output", "drv-output-1.39",
defaultVersion, 1 << 8 | 39,
(std::tuple<DrvOutput, DrvOutput> { (std::tuple<DrvOutput, DrvOutput> {
{ {
.drvHash = Hash::parseSRI("sha256-FePFYIlMuycIXPZbWi7LGEiMmZSX9FMbaQenWBzm1Sc="), .drvPath = StorePath { "g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-foo.drv" },
.outputName = "baz", .outputName = "baz",
}, },
DrvOutput { DrvOutput {
.drvHash = Hash::parseSRI("sha256-b4afnqKCO9oWXgYHb9DeQ2berSwOjS27rSd9TxXDc/U="), .drvPath = StorePath { "g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-foo.drv" },
.outputName = "quux", .outputName = "quux",
}, },
})) }))
VERSIONED_CHARACTERIZATION_TEST( VERSIONED_CHARACTERIZATION_TEST(
WorkerProtoTest, WorkerProtoTest,
realisation, unkeyedRealisation_1_39,
"realisation", "unkeyed-realisation-1.39",
defaultVersion, 1 << 8 | 39,
(std::tuple<Realisation, Realisation> { (UnkeyedRealisation {
Realisation { .outPath = StorePath { "g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-foo" },
.id = DrvOutput { .signatures = { "asdf", "qwer" },
.drvHash = Hash::parseSRI("sha256-FePFYIlMuycIXPZbWi7LGEiMmZSX9FMbaQenWBzm1Sc="), }))
.outputName = "baz",
}, VERSIONED_CHARACTERIZATION_TEST(
WorkerProtoTest,
realisation_1_39,
"realisation-1.39",
1 << 8 | 39,
(Realisation {
UnkeyedRealisation {
.outPath = StorePath { "g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-foo" }, .outPath = StorePath { "g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-foo" },
.signatures = { "asdf", "qwer" }, .signatures = { "asdf", "qwer" },
}, },
Realisation { DrvOutput {
.id = { .drvPath = StorePath { "g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-foo.drv" },
.drvHash = Hash::parseSRI("sha256-FePFYIlMuycIXPZbWi7LGEiMmZSX9FMbaQenWBzm1Sc="), .outputName = "baz",
.outputName = "baz",
},
.outPath = StorePath { "g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-foo" },
.signatures = { "asdf", "qwer" },
.dependentRealisations = {
{
DrvOutput {
.drvHash = Hash::parseSRI("sha256-b4afnqKCO9oWXgYHb9DeQ2berSwOjS27rSd9TxXDc/U="),
.outputName = "quux",
},
StorePath { "g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-foo" },
},
},
}, },
})) }))
@ -194,7 +187,10 @@ VERSIONED_CHARACTERIZATION_TEST(
t; 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, WorkerProtoTest,
buildResult_1_28, buildResult_1_28,
"build-result-1.28", "build-result-1.28",
@ -216,20 +212,12 @@ VERSIONED_CHARACTERIZATION_TEST(
{ {
"foo", "foo",
{ {
.id = DrvOutput {
.drvHash = Hash::parseSRI("sha256-b4afnqKCO9oWXgYHb9DeQ2berSwOjS27rSd9TxXDc/U="),
.outputName = "foo",
},
.outPath = StorePath { "g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-foo" }, .outPath = StorePath { "g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-foo" },
}, },
}, },
{ {
"bar", "bar",
{ {
.id = DrvOutput {
.drvHash = Hash::parseSRI("sha256-b4afnqKCO9oWXgYHb9DeQ2berSwOjS27rSd9TxXDc/U="),
.outputName = "bar",
},
.outPath = StorePath { "g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-bar" }, .outPath = StorePath { "g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-bar" },
}, },
}, },
@ -239,7 +227,8 @@ VERSIONED_CHARACTERIZATION_TEST(
t; t;
})) }))
VERSIONED_CHARACTERIZATION_TEST( // See above note
VERSIONED_READ_CHARACTERIZATION_TEST(
WorkerProtoTest, WorkerProtoTest,
buildResult_1_29, buildResult_1_29,
"build-result-1.29", "build-result-1.29",
@ -266,20 +255,12 @@ VERSIONED_CHARACTERIZATION_TEST(
{ {
"foo", "foo",
{ {
.id = DrvOutput {
.drvHash = Hash::parseSRI("sha256-b4afnqKCO9oWXgYHb9DeQ2berSwOjS27rSd9TxXDc/U="),
.outputName = "foo",
},
.outPath = StorePath { "g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-foo" }, .outPath = StorePath { "g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-foo" },
}, },
}, },
{ {
"bar", "bar",
{ {
.id = DrvOutput {
.drvHash = Hash::parseSRI("sha256-b4afnqKCO9oWXgYHb9DeQ2berSwOjS27rSd9TxXDc/U="),
.outputName = "bar",
},
.outPath = StorePath { "g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-bar" }, .outPath = StorePath { "g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-bar" },
}, },
}, },
@ -291,7 +272,8 @@ VERSIONED_CHARACTERIZATION_TEST(
t; t;
})) }))
VERSIONED_CHARACTERIZATION_TEST( // See above note
VERSIONED_READ_CHARACTERIZATION_TEST(
WorkerProtoTest, WorkerProtoTest,
buildResult_1_37, buildResult_1_37,
"build-result-1.37", "build-result-1.37",
@ -318,20 +300,58 @@ VERSIONED_CHARACTERIZATION_TEST(
{ {
"foo", "foo",
{ {
.id = DrvOutput {
.drvHash = Hash::parseSRI("sha256-b4afnqKCO9oWXgYHb9DeQ2berSwOjS27rSd9TxXDc/U="),
.outputName = "foo",
},
.outPath = StorePath { "g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-foo" }, .outPath = StorePath { "g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-foo" },
}, },
}, },
{ {
"bar", "bar",
{ {
.id = DrvOutput { .outPath = StorePath { "g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-bar" },
.drvHash = Hash::parseSRI("sha256-b4afnqKCO9oWXgYHb9DeQ2berSwOjS27rSd9TxXDc/U="), },
.outputName = "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" }, .outPath = StorePath { "g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-bar" },
}, },
}, },

View file

@ -493,22 +493,36 @@ StorePath BinaryCacheStore::addToStore(
})->path; })->path;
} }
void BinaryCacheStore::queryRealisationUncached(const DrvOutput & id, std::string BinaryCacheStore::makeRealisationPath(const DrvOutput & id)
Callback<std::shared_ptr<const Realisation>> callback) noexcept
{ {
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)); auto callbackPtr = std::make_shared<decltype(callback)>(std::move(callback));
Callback<std::optional<std::string>> newCallback = { Callback<std::optional<std::string>> newCallback = {
[=](std::future<std::optional<std::string>> fut) { [=,this](std::future<std::optional<std::string>> fut) {
try { try {
auto data = fut.get(); auto data = fut.get();
if (!data) return (*callbackPtr)({}); if (!data) return (*callbackPtr)({});
auto realisation = Realisation::fromJSON( UnkeyedRealisation realisation { .outPath = StorePath::dummy, };
nlohmann::json::parse(*data), outputInfoFilePath); auto json = nlohmann::json::parse(*data);
return (*callbackPtr)(std::make_shared<const Realisation>(realisation)); 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 (...) { } catch (...) {
callbackPtr->rethrow(); callbackPtr->rethrow();
} }
@ -518,11 +532,14 @@ void BinaryCacheStore::queryRealisationUncached(const DrvOutput & id,
getFile(outputInfoFilePath, std::move(newCallback)); getFile(outputInfoFilePath, std::move(newCallback));
} }
void BinaryCacheStore::registerDrvOutput(const Realisation& info) { void BinaryCacheStore::registerDrvOutput(const Realisation & info)
{
if (diskCache) if (diskCache)
diskCache->upsertRealisation(getUri(), info); diskCache->upsertRealisation(getUri(), info);
auto filePath = realisationsPrefix + "/" + info.id.to_string() + ".doi"; upsertFile(
upsertFile(filePath, info.toJSON().dump(), "application/json"); makeRealisationPath(info.id),
info.toJSON(*this).dump(),
"application/json");
} }
ref<SourceAccessor> BinaryCacheStore::getFSAccessor(bool requireValidPath) 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-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 #ifndef _WIN32 // TODO enable build hook on Windows
# include "nix/store/build/hook-instance.hh" # include "nix/store/build/hook-instance.hh"
# include "nix/store/build/derivation-builder.hh" # include "nix/store/build/derivation-builder.hh"
@ -264,7 +264,7 @@ Goal::Co DerivationBuildingGoal::gaveUpOnSubstitution()
auto mEntry = get(inputGoals, drvPath); auto mEntry = get(inputGoals, drvPath);
if (!mEntry) return std::nullopt; 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; if (!buildResult.success()) return std::nullopt;
auto i = get(buildResult.builtOutputs, outputName); auto i = get(buildResult.builtOutputs, outputName);
@ -295,8 +295,8 @@ Goal::Co DerivationBuildingGoal::gaveUpOnSubstitution()
}); });
// FIXME wanted outputs // FIXME wanted outputs
auto resolvedDrvGoal = worker.makeDerivationGoal( auto resolvedDrvGoal = worker.makeDerivationCreationAndRealisationGoal(
makeConstantStorePathRef(pathResolved), OutputsSpec::All{}, buildMode); pathResolved, OutputsSpec::All{}, drvResolved, buildMode);
{ {
Goals waitees{resolvedDrvGoal}; Goals waitees{resolvedDrvGoal};
co_await await(std::move(waitees)); co_await await(std::move(waitees));
@ -304,23 +304,16 @@ Goal::Co DerivationBuildingGoal::gaveUpOnSubstitution()
trace("resolved derivation finished"); trace("resolved derivation finished");
auto resolvedDrv = *resolvedDrvGoal->drv; auto resolvedResult = resolvedDrvGoal->buildResult;
auto resolvedResult = resolvedDrvGoal->getBuildResult(DerivedPath::Built{
.drvPath = makeConstantStorePathRef(pathResolved),
.outputs = OutputsSpec::All{},
});
SingleDrvOutputs builtOutputs; SingleDrvOutputs builtOutputs;
if (resolvedResult.success()) { if (resolvedResult.success()) {
auto resolvedHashes = staticOutputHashes(worker.store, resolvedDrv);
StorePathSet outputPaths; StorePathSet outputPaths;
for (auto & outputName : resolvedDrv.outputNames()) { for (auto & outputName : drvResolved.outputNames()) {
auto initialOutput = get(initialOutputs, outputName); auto initialOutput = get(initialOutputs, outputName);
auto resolvedHash = get(resolvedHashes, outputName); if ((!initialOutput))
if ((!initialOutput) || (!resolvedHash))
throw Error( throw Error(
"derivation '%s' doesn't have expected output '%s' (derivation-goal.cc/resolve)", "derivation '%s' doesn't have expected output '%s' (derivation-goal.cc/resolve)",
worker.store.printStorePath(drvPath), outputName); worker.store.printStorePath(drvPath), outputName);
@ -329,31 +322,21 @@ Goal::Co DerivationBuildingGoal::gaveUpOnSubstitution()
auto take1 = get(resolvedResult.builtOutputs, outputName); auto take1 = get(resolvedResult.builtOutputs, outputName);
if (take1) return *take1; 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 outputs in resolvedResult, this can get out of sync with the
store, which is our actual source of truth. For now we just store, which is our actual source of truth. For now we just
check the store directly if it fails. */ 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; if (take2) return *take2;
throw Error( throw Error(
"derivation '%s' doesn't have expected output '%s' (derivation-goal.cc/realisation)", "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); outputPaths.insert(realisation.outPath);
builtOutputs.emplace(outputName, realisation); builtOutputs.emplace(outputName, realisation);
} }
@ -1165,7 +1148,7 @@ std::pair<bool, SingleDrvOutputs> DerivationBuildingGoal::checkPathValidity()
: PathStatus::Corrupt, : PathStatus::Corrupt,
}; };
} }
auto drvOutput = DrvOutput{info.outputHash, i.first}; auto drvOutput = DrvOutput{drvPath, i.first};
if (experimentalFeatureSettings.isEnabled(Xp::CaDerivations)) { if (experimentalFeatureSettings.isEnabled(Xp::CaDerivations)) {
if (auto real = worker.store.queryRealisation(drvOutput)) { if (auto real = worker.store.queryRealisation(drvOutput)) {
info.known = { info.known = {
@ -1177,16 +1160,21 @@ std::pair<bool, SingleDrvOutputs> DerivationBuildingGoal::checkPathValidity()
// derivation, and the output path is valid, but we don't have // derivation, and the output path is valid, but we don't have
// its realisation stored (probably because it has been built // its realisation stored (probably because it has been built
// without the `ca-derivations` experimental flag). // without the `ca-derivations` experimental flag).
worker.store.registerDrvOutput( worker.store.registerDrvOutput(Realisation {
Realisation { {
drvOutput, .outPath = info.known->path,
info.known->path, },
} drvOutput,
); });
} }
} }
if (info.known && info.known->isValid()) 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; 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 { namespace nix {
DerivationGoal::DerivationGoal(ref<const SingleDerivedPath> drvReq, DerivationGoal::DerivationGoal(const StorePath & drvPath, const Derivation & drv,
const OutputsSpec & wantedOutputs, Worker & worker, BuildMode buildMode) const OutputName & wantedOutput, Worker & worker, BuildMode buildMode)
: Goal(worker, loadDerivation()) : Goal(worker, haveDerivation())
, drvReq(drvReq) , drvPath(drvPath)
, wantedOutputs(wantedOutputs) , wantedOutput(wantedOutput)
, 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)
, buildMode(buildMode) , buildMode(buildMode)
{ {
this->drv = std::make_unique<Derivation>(drv); this->drv = std::make_unique<Derivation>(drv);
name = fmt( name = fmt(
"building of '%s' from in-memory derivation", "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"); trace("created");
mcExpectedBuilds = std::make_unique<MaintainCount<uint64_t>>(worker.expectedBuilds); 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() std::string DerivationGoal::key()
{ {
/* Ensure that derivations get built in order of their name, /* Ensure that derivations get built in order of their name,
i.e. a derivation named "aardvark" always comes before i.e. a derivation named "aardvark" always comes before
"baboon". And substitution goals always happen before "baboon". And substitution goals always happen before
derivation goals (due to "b$"). */ 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) Goal::Co DerivationGoal::haveDerivation()
{
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)
{ {
trace("have derivation"); trace("have derivation");
@ -205,10 +94,17 @@ Goal::Co DerivationGoal::haveDerivation(StorePath drvPath)
trace("outer build done"); trace("outer build done");
buildResult = g->getBuildResult(DerivedPath::Built{ buildResult = g->buildResult;
.drvPath = makeConstantStorePathRef(drvPath), for (auto it = buildResult.builtOutputs.begin(); it != buildResult.builtOutputs.end(); ) {
.outputs = wantedOutputs, 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); co_return amDone(g->exitCode, g->ex);
}; };
@ -222,11 +118,9 @@ Goal::Co DerivationGoal::haveDerivation(StorePath drvPath)
if (impure) experimentalFeatureSettings.require(Xp::ImpureDerivations); if (impure) experimentalFeatureSettings.require(Xp::ImpureDerivations);
auto outputHashes = staticOutputHashes(worker.evalStore, *drv); for (auto & [outputName, _] : drv->outputs) {
for (auto & [outputName, outputHash] : outputHashes) {
InitialOutput v{ InitialOutput v{
.wanted = true, // Will be refined later .wanted = true, // Will be refined later
.outputHash = outputHash
}; };
/* TODO we might want to also allow randomizing the paths /* 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. */ /* 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 they are all valid, then we're done. */
if (allValid && buildMode == bmNormal) { 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( waitees.insert(
upcast_goal( upcast_goal(
worker.makeDrvOutputSubstitutionGoal( worker.makeDrvOutputSubstitutionGoal(
DrvOutput{status.outputHash, outputName}, DrvOutput{drvPath, outputName},
buildMode == bmRepair ? Repair : NoRepair buildMode == bmRepair ? Repair : NoRepair
) )
) )
@ -295,25 +189,20 @@ Goal::Co DerivationGoal::haveDerivation(StorePath drvPath)
assert(!drv->type().isImpure()); assert(!drv->type().isImpure());
if (nrFailed > 0 && nrFailed > nrNoSubstituters && !settings.tryFallback) { 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 ", 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))); worker.store.printStorePath(drvPath)));
} }
nrFailed = nrNoSubstituters = 0; nrFailed = nrNoSubstituters = 0;
if (needRestart == NeedRestartForMoreOutputs::OutputsAddedDoNeed) { auto [allValid, validOutputs] = checkPathValidity();
needRestart = NeedRestartForMoreOutputs::OutputsUnmodifedDontNeed;
co_return haveDerivation(std::move(drvPath));
}
auto [allValid, validOutputs] = checkPathValidity(drvPath);
if (buildMode == bmNormal && allValid) { 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) { if (buildMode == bmRepair && allValid) {
co_return repairClosure(std::move(drvPath)); co_return repairClosure();
} }
if (buildMode == bmCheck && !allValid) if (buildMode == bmCheck && !allValid)
throw Error("some outputs of '%s' are not valid, so checking is not possible", 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()); assert(!drv->type().isImpure());
@ -346,11 +235,10 @@ Goal::Co DerivationGoal::repairClosure(StorePath drvPath)
that produced those outputs. */ that produced those outputs. */
/* Get the output closure. */ /* Get the output closure. */
auto outputs = queryDerivationOutputMap(drvPath); auto outputs = queryDerivationOutputMap();
StorePathSet outputClosure; StorePathSet outputClosure;
for (auto & i : outputs) { if (auto mPath = get(outputs, wantedOutput)) {
if (!wantedOutputs.contains(i.first)) continue; worker.store.computeFSClosure(*mPath, outputClosure);
worker.store.computeFSClosure(i.second, outputClosure);
} }
/* Filter out our own outputs (which we have already checked). */ /* 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", throw Error("some paths in the output closure of derivation '%s' could not be repaired",
worker.store.printStorePath(drvPath)); 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()); assert(!drv->type().isImpure());
@ -424,7 +312,7 @@ std::map<std::string, std::optional<StorePath>> DerivationGoal::queryPartialDeri
return res; return res;
} }
OutputPathMap DerivationGoal::queryDerivationOutputMap(const StorePath & drvPath) OutputPathMap DerivationGoal::queryDerivationOutputMap()
{ {
assert(!drv->type().isImpure()); 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, {} }; if (drv->type().isImpure()) return { false, {} };
bool checkHash = buildMode == bmRepair; bool checkHash = buildMode == bmRepair;
auto wantedOutputsLeft = std::visit(overloaded { StringSet wantedOutputsLeft{wantedOutput};
[&](const OutputsSpec::All &) {
return StringSet {};
},
[&](const OutputsSpec::Names & names) {
return static_cast<StringSet>(names);
},
}, wantedOutputs.raw);
SingleDrvOutputs validOutputs; SingleDrvOutputs validOutputs;
for (auto & i : queryPartialDerivationOutputMap(drvPath)) { for (auto & i : queryPartialDerivationOutputMap()) {
auto initialOutput = get(initialOutputs, i.first); auto initialOutput = get(initialOutputs, i.first);
if (!initialOutput) if (!initialOutput)
// this is an invalid output, gets catched with (!wantedOutputsLeft.empty()) // this is an invalid output, gets catched with (!wantedOutputsLeft.empty())
continue; continue;
auto & info = *initialOutput; auto & info = *initialOutput;
info.wanted = wantedOutputs.contains(i.first); info.wanted = wantedOutput == i.first;
if (info.wanted) if (info.wanted)
wantedOutputsLeft.erase(i.first); wantedOutputsLeft.erase(i.first);
if (i.second) { if (i.second) {
@ -475,7 +356,7 @@ std::pair<bool, SingleDrvOutputs> DerivationGoal::checkPathValidity(const StoreP
: PathStatus::Corrupt, : PathStatus::Corrupt,
}; };
} }
auto drvOutput = DrvOutput{info.outputHash, i.first}; auto drvOutput = DrvOutput{drvPath, i.first};
if (experimentalFeatureSettings.isEnabled(Xp::CaDerivations)) { if (experimentalFeatureSettings.isEnabled(Xp::CaDerivations)) {
if (auto real = worker.store.queryRealisation(drvOutput)) { if (auto real = worker.store.queryRealisation(drvOutput)) {
info.known = { 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 // derivation, and the output path is valid, but we don't have
// its realisation stored (probably because it has been built // its realisation stored (probably because it has been built
// without the `ca-derivations` experimental flag). // without the `ca-derivations` experimental flag).
worker.store.registerDrvOutput( worker.store.registerDrvOutput(Realisation {
Realisation { {
drvOutput, .outPath = info.known->path,
info.known->path, },
} drvOutput,
); });
} }
} }
if (info.known && info.known->isValid()) 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. // 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) if (!allValid)
throw Error("some outputs are unexpectedly invalid"); throw Error("some outputs are unexpectedly invalid");
return validOutputs; return validOutputs;
@ -530,7 +416,6 @@ SingleDrvOutputs DerivationGoal::assertPathValidity(const StorePath & drvPath)
Goal::Done DerivationGoal::done( Goal::Done DerivationGoal::done(
const StorePath & drvPath,
BuildResult::Status status, BuildResult::Status status,
SingleDrvOutputs builtOutputs, SingleDrvOutputs builtOutputs,
std::optional<Error> ex) std::optional<Error> ex)
@ -546,7 +431,7 @@ Goal::Done DerivationGoal::done(
mcExpectedBuilds.reset(); mcExpectedBuilds.reset();
if (buildResult.success()) { if (buildResult.success()) {
auto wantedBuiltOutputs = filterDrvOutputs(wantedOutputs, std::move(builtOutputs)); auto wantedBuiltOutputs = filterDrvOutputs(OutputsSpec::Names{wantedOutput}, std::move(builtOutputs));
assert(!wantedBuiltOutputs.empty()); assert(!wantedBuiltOutputs.empty());
buildResult.builtOutputs = std::move(wantedBuiltOutputs); buildResult.builtOutputs = std::move(wantedBuiltOutputs);
if (status == BuildResult::Built) 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/worker.hh"
#include "nix/store/build/substitution-goal.hh" #include "nix/store/build/substitution-goal.hh"
#include "nix/util/callback.hh" #include "nix/util/callback.hh"
#include "nix/store/store-open.hh" #include "nix/store/build/build-trace-goal.hh"
namespace nix { namespace nix {
DrvOutputSubstitutionGoal::DrvOutputSubstitutionGoal( DrvOutputSubstitutionGoal::DrvOutputSubstitutionGoal(
const DrvOutput & id, const DrvOutput & id, Worker & worker, RepairFlag repair, std::optional<ContentAddress> ca)
Worker & worker,
RepairFlag repair,
std::optional<ContentAddress> ca)
: Goal(worker, init()) : Goal(worker, init())
, id(id) , id(id)
{ {
name = fmt("substitution of '%s'", id.to_string()); name = fmt("substitution of '%s'", id.render(worker.store));
trace("created"); trace("created");
} }
Goal::Co DrvOutputSubstitutionGoal::init() Goal::Co DrvOutputSubstitutionGoal::init()
{ {
trace("init"); trace("init");
/* If the derivation already exists, were done */ auto goal0 = worker.makeBuildTraceGoal({
if (worker.store.queryRealisation(id)) { makeConstantStorePathRef(id.drvPath),
co_return amDone(ecSuccess); id.outputName,
} });
co_await await(Goals{upcast_goal(goal0)});
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));
trace("output path substituted"); trace("output path substituted");
if (nrFailed > 0) { 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); co_return amDone(nrNoSubstituters > 0 ? ecNoSubstituters : ecFailed);
} }
worker.store.registerDrvOutput(*outputInfo); auto goal1 = worker.makePathSubstitutionGoal(goal0->outputInfo->outPath);
goal0.reset();
trace("finished"); goal1->preserveException = true;
co_return amDone(ecSuccess); co_await await(Goals{upcast_goal(goal1)});
co_return amDone(goal1->exitCode, goal1->ex);
} }
std::string DrvOutputSubstitutionGoal::key() std::string DrvOutputSubstitutionGoal::key()
{ {
/* "a$" ensures substitution goals happen before derivation /* "a$" ensures substitution goals happen before derivation
goals. */ goals. */
return "a$" + std::string(id.to_string()); return "b$" + std::string(id.render(worker.store));
} }
void DrvOutputSubstitutionGoal::handleEOF(Descriptor fd) void DrvOutputSubstitutionGoal::handleEOF(Descriptor fd)
@ -160,5 +53,4 @@ void DrvOutputSubstitutionGoal::handleEOF(Descriptor fd)
worker.wakeUp(shared_from_this()); 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/worker.hh"
#include "nix/store/build/substitution-goal.hh" #include "nix/store/build/substitution-goal.hh"
#ifndef _WIN32 // TODO Enable building on Windows #include "nix/store/build/derivation-creation-and-realisation-goal.hh"
# include "nix/store/build/derivation-goal.hh"
#endif
#include "nix/store/local-store.hh" #include "nix/store/local-store.hh"
#include "nix/util/strings.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); ex = std::move(i->ex);
} }
if (i->exitCode != Goal::ecSuccess) { if (i->exitCode != Goal::ecSuccess) {
#ifndef _WIN32 // TODO Enable building on Windows if (auto i2 = dynamic_cast<DerivationCreationAndRealisationGoal *>(i.get()))
if (auto i2 = dynamic_cast<DerivationGoal *>(i.get()))
failed.insert(i2->drvReq->to_string(*this)); failed.insert(i2->drvReq->to_string(*this));
else else if (auto i2 = dynamic_cast<PathSubstitutionGoal *>(i.get()))
#endif
if (auto i2 = dynamic_cast<PathSubstitutionGoal *>(i.get()))
failed.insert(printStorePath(i2->storePath)); failed.insert(printStorePath(i2->storePath));
} }
} }
@ -70,7 +66,7 @@ std::vector<KeyedBuildResult> Store::buildPathsWithResults(
for (auto & [req, goalPtr] : state) for (auto & [req, goalPtr] : state)
results.emplace_back(KeyedBuildResult { results.emplace_back(KeyedBuildResult {
goalPtr->getBuildResult(req), goalPtr->buildResult,
/* .path = */ req, /* .path = */ req,
}); });
@ -81,19 +77,11 @@ BuildResult Store::buildDerivation(const StorePath & drvPath, const BasicDerivat
BuildMode buildMode) BuildMode buildMode)
{ {
Worker worker(*this, *this); Worker worker(*this, *this);
#ifndef _WIN32 // TODO Enable building on Windows auto goal = worker.makeDerivationCreationAndRealisationGoal(drvPath, OutputsSpec::All {}, drv, buildMode);
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
try { try {
worker.run(Goals{goal}); worker.run(Goals{goal});
return goal->getBuildResult(DerivedPath::Built { return goal->buildResult;
.drvPath = makeConstantStorePathRef(drvPath),
.outputs = OutputsSpec::All {},
});
} catch (Error & e) { } catch (Error & e) {
return BuildResult { return BuildResult {
.status = BuildResult::MiscFailure, .status = BuildResult::MiscFailure,

View file

@ -101,30 +101,6 @@ bool CompareGoalPtrs::operator() (const GoalPtr & a, const GoalPtr & b) const {
return s1 < s2; 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) void addToWeakGoals(WeakGoals & goals, GoalPtr p)
{ {
if (goals.find(p) != goals.end()) 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/drv-output-substitution-goal.hh"
#include "nix/store/build/derivation-goal.hh" #include "nix/store/build/derivation-goal.hh"
#include "nix/store/build/derivation-building-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 #ifndef _WIN32 // TODO Enable building on Windows
# include "nix/store/build/hook-instance.hh" # include "nix/store/build/hook-instance.hh"
#endif #endif
#include "nix/store/build/build-trace-goal.hh"
#include "nix/store/build/derivation-resolution-goal.hh"
#include "nix/util/signals.hh" #include "nix/util/signals.hh"
namespace nix { namespace nix {
@ -53,52 +56,40 @@ std::shared_ptr<G> Worker::initGoalIfNeeded(std::weak_ptr<G> & goal_weak, Args &
return goal; return goal;
} }
std::shared_ptr<DerivationGoal> Worker::makeDerivationGoalCommon( std::shared_ptr<DerivationCreationAndRealisationGoal> Worker::makeDerivationCreationAndRealisationGoal(
ref<const SingleDerivedPath> drvReq, ref<const SingleDerivedPath> drvReq,
const OutputsSpec & wantedOutputs, const OutputsSpec & wantedOutputs,
std::function<std::shared_ptr<DerivationGoal>()> mkDrvGoal) BuildMode buildMode)
{ {
std::weak_ptr<DerivationGoal> & goal_weak = derivationGoals.ensureSlot(*drvReq).value; return initGoalIfNeeded(
std::shared_ptr<DerivationGoal> goal = goal_weak.lock(); derivationCreationAndRealisationGoals.ensureSlot(*drvReq).value[wantedOutputs],
if (!goal) { drvReq, wantedOutputs, *this, buildMode);
goal = mkDrvGoal();
goal_weak = goal;
wakeUp(goal);
} else {
goal->addWantedOutputs(wantedOutputs);
}
return goal;
} }
std::shared_ptr<DerivationGoal> Worker::makeDerivationGoal(ref<const SingleDerivedPath> drvReq, std::shared_ptr<DerivationCreationAndRealisationGoal> Worker::makeDerivationCreationAndRealisationGoal(
const OutputsSpec & wantedOutputs, BuildMode buildMode) const StorePath & drvPath,
const OutputsSpec & wantedOutputs,
const Derivation & drv,
BuildMode buildMode)
{ {
return makeDerivationGoalCommon(drvReq, wantedOutputs, [&]() -> std::shared_ptr<DerivationGoal> { return initGoalIfNeeded(
return std::make_shared<DerivationGoal>(drvReq, wantedOutputs, *this, buildMode); 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 initGoalIfNeeded(derivationGoals[drvPath][wantedOutput], drvPath, drv, wantedOutput, *this, buildMode);
return std::make_shared<DerivationGoal>(drvPath, drv, wantedOutputs, *this, buildMode);
});
} }
std::shared_ptr<DerivationBuildingGoal> Worker::makeDerivationBuildingGoal(const StorePath & drvPath, std::shared_ptr<DerivationBuildingGoal> Worker::makeDerivationBuildingGoal(const StorePath & drvPath,
const Derivation & drv, BuildMode buildMode) const Derivation & drv, BuildMode buildMode)
{ {
std::weak_ptr<DerivationBuildingGoal> & goal_weak = derivationBuildingGoals[drvPath]; return initGoalIfNeeded(derivationBuildingGoals[drvPath], drvPath, drv, *this, buildMode);
auto goal = goal_weak.lock(); // FIXME
if (!goal) {
goal = std::make_shared<DerivationBuildingGoal>(drvPath, drv, *this, buildMode);
goal_weak = goal;
wakeUp(goal);
}
return goal;
} }
@ -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) GoalPtr Worker::makeGoal(const DerivedPath & req, BuildMode buildMode)
{ {
return std::visit(overloaded { return std::visit(overloaded {
[&](const DerivedPath::Built & bfd) -> GoalPtr { [&](const DerivedPath::Built & bfd) -> GoalPtr {
return makeDerivationGoal(bfd.drvPath, bfd.outputs, buildMode); return makeDerivationCreationAndRealisationGoal(bfd.drvPath, bfd.outputs, buildMode);
}, },
[&](const DerivedPath::Opaque & bo) -> GoalPtr { [&](const DerivedPath::Opaque & bo) -> GoalPtr {
return makePathSubstitutionGoal(bo.path, buildMode == bmRepair ? Repair : NoRepair); 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> 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();) for (auto i = goalMap.begin(); i != goalMap.end();)
if (!f(i->second)) if (!std::forward<F>(f)(i->second))
i = goalMap.erase(i); i = goalMap.erase(i);
else ++i; else ++i;
} }
template<typename K, typename G> template<typename G>
static void removeGoal(std::shared_ptr<G> goal, std::map<K, std::weak_ptr<G>> & goalMap) static bool removeGoal(std::shared_ptr<G> goal, std::weak_ptr<G> & gp)
{ {
/* !!! inefficient */ return gp.lock() != goal;
cullMap(goalMap, [&](const std::weak_ptr<G> & gp) -> bool {
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> template<typename K, typename G, typename Inner>
static void removeGoal(std::shared_ptr<DerivationGoal> goal, std::map<K, DerivedPathMap<std::weak_ptr<DerivationGoal>>::ChildNode> & goalMap) 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 */ /* !!! inefficient */
cullMap(goalMap, [&](DerivedPathMap<std::weak_ptr<DerivationGoal>>::ChildNode & node) -> bool { cullMap(goalMap, [&](Inner & inner) -> bool {
if (node.value.lock() == goal) return removeGoal(goal, inner);
node.value.reset();
removeGoal(goal, node.childMap);
return !node.value.expired() || !node.childMap.empty();
}); });
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) void Worker::removeGoal(GoalPtr goal)
{ {
if (auto drvGoal = std::dynamic_pointer_cast<DerivationGoal>(goal)) if (auto drvGoal = std::dynamic_pointer_cast<DerivationCreationAndRealisationGoal>(goal))
nix::removeGoal(drvGoal, derivationGoals.map); 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)) else if (auto drvBuildingGoal = std::dynamic_pointer_cast<DerivationBuildingGoal>(goal))
nix::removeGoal(drvBuildingGoal, derivationBuildingGoals); nix::removeGoal(drvBuildingGoal, derivationBuildingGoals);
else if (auto subGoal = std::dynamic_pointer_cast<PathSubstitutionGoal>(goal)) else if (auto subGoal = std::dynamic_pointer_cast<PathSubstitutionGoal>(goal))
@ -312,7 +338,7 @@ void Worker::run(const Goals & _topGoals)
for (auto & i : _topGoals) { for (auto & i : _topGoals) {
topGoals.insert(i); topGoals.insert(i);
if (auto goal = dynamic_cast<DerivationGoal *>(i.get())) { if (auto goal = dynamic_cast<DerivationCreationAndRealisationGoal *>(i.get())) {
topPaths.push_back(DerivedPath::Built { topPaths.push_back(DerivedPath::Built {
.drvPath = goal->drvReq, .drvPath = goal->drvReq,
.outputs = goal->wantedOutputs, .outputs = goal->wantedOutputs,
@ -579,4 +605,14 @@ GoalPtr upcast_goal(std::shared_ptr<DerivationGoal> subGoal)
return 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" outputName text not null, -- symbolic output id, usually "out"
outputPath integer not null, outputPath integer not null,
signatures text, -- space-separated list 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); 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) std::optional<StorePath> CommonProto::Serialise<std::optional<StorePath>>::read(const StoreDirConfig & store, CommonProto::ReadConn conn)
{ {
auto s = readString(conn.from); auto s = readString(conn.from);

View file

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

View file

@ -787,7 +787,7 @@ Sync<DrvHashes> drvHashes;
/* Look up the derivation by value and memoize the /* Look up the derivation by value and memoize the
`hashDerivationModulo` call. `hashDerivationModulo` call.
*/ */
static const DrvHash pathDerivationModulo(Store & store, const StorePath & drvPath) static const DrvHashModulo & pathDerivationModulo(Store & store, const StorePath & drvPath)
{ {
{ {
auto hashes = drvHashes.lock(); auto hashes = drvHashes.lock();
@ -796,13 +796,16 @@ static const DrvHash pathDerivationModulo(Store & store, const StorePath & drvPa
return h->second; return h->second;
} }
} }
auto h = hashDerivationModulo(
store,
store.readInvalidDerivation(drvPath),
false);
// Cache it // Cache it
drvHashes.lock()->insert_or_assign(drvPath, h); auto [iter, _] = drvHashes.lock()->insert_or_assign(
return h; drvPath,
hashDerivationModulo(
store,
store.readInvalidDerivation(drvPath),
false));
return iter->second;
} }
/* See the header for interface details. These are the implementation details. /* 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 don't leak the provenance of fixed outputs, reducing pointless cache
misses as the build itself won't know this. 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. */ /* Return a fixed hash for fixed-output derivations. */
if (type.isFixed()) { if (drv.type().isFixed()) {
std::map<std::string, Hash> outputHashes; std::map<std::string, Hash> outputHashes;
for (const auto & i : drv.outputs) { for (const auto & i : drv.outputs) {
auto & dof = std::get<DerivationOutput::CAFixed>(i.second.raw); 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))); + store.printStorePath(dof.path(store, drv.name, i.first)));
outputHashes.insert_or_assign(i.first, std::move(hash)); outputHashes.insert_or_assign(i.first, std::move(hash));
} }
return DrvHash { return outputHashes;
.hashes = outputHashes,
.kind = DrvHash::Kind::Regular,
};
} }
auto kind = std::visit(overloaded { if (std::visit(overloaded {
[](const DerivationType::InputAddressed & ia) { [](const DerivationType::InputAddressed & ia) {
/* This might be a "pesimistically" deferred output, so we don't /* This might be a "pesimistically" deferred output, so we don't
"taint" the kind yet. */ "taint" the kind yet. */
return DrvHash::Kind::Regular; return false;
}, },
[](const DerivationType::ContentAddressed & ca) { [](const DerivationType::ContentAddressed & ca) {
return ca.fixed // Already covered
? DrvHash::Kind::Regular assert(!ca.fixed);
: DrvHash::Kind::Deferred; return true;
}, },
[](const DerivationType::Impure &) -> DrvHash::Kind { [](const DerivationType::Impure &) {
return DrvHash::Kind::Deferred; 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; DerivedPathMap<StringSet>::ChildNode::Map inputs2;
for (auto & [drvPath, node] : drv.inputDrvs.map) { 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); const auto & res = pathDerivationModulo(store, drvPath);
if (res.kind == DrvHash::Kind::Deferred) if (std::visit(overloaded {
kind = DrvHash::Kind::Deferred; [&](const DrvHashModulo::DeferredDrv &) {
for (auto & outputName : node.value) { return true;
const auto h = get(res.hashes, outputName); },
if (!h) // Regular non-CA derivation, replace derivation
throw Error("no hash for output '%s' of derivation '%s'", outputName, drv.name); [&](const DrvHashModulo::DrvHash & drvHash) {
inputs2[h->to_string(HashFormat::Base16, false)].value.insert(outputName); 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)); return 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;
} }
@ -1029,25 +1041,39 @@ void BasicDerivation::applyRewrites(const StringMap & rewrites)
env = std::move(newEnv); 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) { for (auto & [outputName, output] : drv.outputs) {
if (std::holds_alternative<DerivationOutput::Deferred>(output.raw)) { if (std::holds_alternative<DerivationOutput::Deferred>(output.raw)) {
auto h = get(hashModulo.hashes, outputName); std::visit(overloaded {
if (!h) [&](const DrvHashModulo::DrvHash & drvHash) {
throw Error("derivation '%s' output '%s' has no hash (derivations.cc/rewriteDerivation)", auto outPath = store.makeOutputPath(outputName, drvHash, drv.name);
drv.name, outputName); drv.env.insert_or_assign(outputName, store.printStorePath(outPath));
auto outPath = store.makeOutputPath(outputName, *h, drv.name); output = DerivationOutput::InputAddressed {
drv.env[outputName] = store.printStorePath(outPath); .path = std::move(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 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)) nullptr, make_ref<const SingleDerivedPath>(SingleDerivedPath::Opaque{inputDrv}), inputNode, queryResolutionChain))
return std::nullopt; 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. // combinations that are currently prohibited.
type(); type();
std::optional<DrvHash> hashesModulo; std::optional<DrvHashModulo> hashModulo_;
for (auto & i : outputs) {
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 { std::visit(overloaded {
[&](const DerivationOutput::InputAddressed & doia) { [&](const DerivationOutput::InputAddressed & doia) {
if (!hashesModulo) { std::visit(overloaded {
// somewhat expensive so we do lazily [&](const DrvHashModulo::DrvHash & drvHash) {
hashesModulo = hashDerivationModulo(store, *this, true); StorePath recomputed = store.makeOutputPath(outputName, drvHash, drvName);
} if (doia.path != recomputed)
auto currentOutputHash = get(hashesModulo->hashes, i.first); throw Error(
if (!currentOutputHash) "derivation '%s' has incorrect output '%s', should be '%s'",
throw Error("derivation '%s' has unexpected output '%s' (local-store / hashesModulo) named '%s'", store.printStorePath(drvPath),
store.printStorePath(drvPath), store.printStorePath(doia.path), i.first); store.printStorePath(doia.path),
StorePath recomputed = store.makeOutputPath(i.first, *currentOutputHash, drvName); store.printStorePath(recomputed));
if (doia.path != recomputed) },
throw Error("derivation '%s' has incorrect output '%s', should be '%s'", [&](const DrvHashModulo::CaOutputHashes &) {
store.printStorePath(drvPath), store.printStorePath(doia.path), store.printStorePath(recomputed)); /* Shouldn't happen as the original output is
envHasRightPath(doia.path, i.first); 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) { [&](const DerivationOutput::CAFixed & dof) {
auto path = dof.path(store, drvName, i.first); auto path = dof.path(store, drvName, outputName);
envHasRightPath(path, i.first); envHasRightPath(path, outputName);
}, },
[&](const DerivationOutput::CAFloating &) { [&](const DerivationOutput::CAFloating &) {
/* Nothing to check */ /* Nothing to check */
}, },
[&](const DerivationOutput::Deferred &) { [&](const DerivationOutput::Deferred &) {
/* Nothing to check */ /* 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 &) { [&](const DerivationOutput::Impure &) {
/* Nothing to check */ /* Nothing to check */
}, },
}, i.second.raw); }, output.raw);
} }
} }

View file

@ -52,7 +52,9 @@ typename DerivedPathMap<V>::ChildNode * DerivedPathMap<V>::findSlot(const Single
// instantiations // 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 { namespace nix {
template<> template<>
@ -69,7 +71,7 @@ std::strong_ordering DerivedPathMap<StringSet>::ChildNode::operator <=> (
template struct DerivedPathMap<StringSet>::ChildNode; template struct DerivedPathMap<StringSet>::ChildNode;
template struct DerivedPathMap<StringSet>; 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); return store.printStorePath(path);
} }
nlohmann::json SingleDerivedPath::Built::toJSON(Store & store) const { nlohmann::json SingleDerivedPath::Built::toJSON(const StoreDirConfig & store) const
nlohmann::json res; {
res["drvPath"] = drvPath->toJSON(store); return nlohmann::json{
// Fallback for the input-addressed derivation case: We expect to always be {"drvPath", drvPath->toJSON(store)},
// able to print the output paths, so lets do it {"output", output},
// 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 DerivedPath::Built::toJSON(Store & store) const { nlohmann::json DerivedPath::Built::toJSON(const StoreDirConfig & store) const
nlohmann::json res; {
res["drvPath"] = drvPath->toJSON(store); return nlohmann::json{
// Fallback for the input-addressed derivation case: We expect to always be {"drvPath", drvPath->toJSON(store)},
// able to print the output paths, so lets do it {"outputs", outputs},
// 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 SingleDerivedPath::toJSON(Store & store) const nlohmann::json SingleDerivedPath::toJSON(const StoreDirConfig & store) const
{ {
return std::visit([&](const auto & buildable) { return std::visit([&](const auto & buildable) {
return buildable.toJSON(store); return buildable.toJSON(store);
}, raw()); }, raw());
} }
nlohmann::json DerivedPath::toJSON(Store & store) const nlohmann::json DerivedPath::toJSON(const StoreDirConfig & store) const
{ {
return std::visit([&](const auto & buildable) { return std::visit([&](const auto & buildable) {
return buildable.toJSON(store); return buildable.toJSON(store);

View file

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

View file

@ -71,13 +71,27 @@ private:
protected: 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 &); BinaryCacheStore(Config &);
/**
* Compute the path to the given realisation
*
* It's `${realisationsPrefix}/${drvPath}/${outputName}`.
*/
std::string makeRealisationPath(const DrvOutput & id);
public: public:
virtual bool fileExists(const std::string & path) = 0; virtual bool fileExists(const std::string & path) = 0;
@ -161,7 +175,7 @@ public:
void registerDrvOutput(const Realisation & info) override; void registerDrvOutput(const Realisation & info) override;
void queryRealisationUncached(const DrvOutput &, 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; 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 struct InitialOutput
{ {
bool wanted; bool wanted;
Hash outputHash;
std::optional<InitialOutputStatus> known; 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. * 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 struct DerivationGoal : public Goal
{ {
/** The path of the derivation. */ /** The path of the derivation. */
ref<const SingleDerivedPath> drvReq; StorePath drvPath;
/** /**
* The specific outputs that we need to build. * The specific outputs that we need to build.
*/ */
OutputsSpec wantedOutputs; OutputName wantedOutput;
/** /**
* See `needRestart`; just for that field. * The derivation stored at drvPath.
*/
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`.
*/ */
std::unique_ptr<Derivation> drv; std::unique_ptr<Derivation> drv;
@ -76,11 +53,8 @@ struct DerivationGoal : public Goal
std::unique_ptr<MaintainCount<uint64_t>> mcExpectedBuilds; std::unique_ptr<MaintainCount<uint64_t>> mcExpectedBuilds;
DerivationGoal(ref<const SingleDerivedPath> drvReq, DerivationGoal(const StorePath & drvPath, const Derivation & drv,
const OutputsSpec & wantedOutputs, Worker & worker, const OutputName & wantedOutput, Worker & worker,
BuildMode buildMode = bmNormal);
DerivationGoal(const StorePath & drvPath, const BasicDerivation & drv,
const OutputsSpec & wantedOutputs, Worker & worker,
BuildMode buildMode = bmNormal); BuildMode buildMode = bmNormal);
~DerivationGoal() = default; ~DerivationGoal() = default;
@ -96,16 +70,15 @@ struct DerivationGoal : public Goal
/** /**
* The states. * The states.
*/ */
Co loadDerivation(); Co haveDerivation();
Co haveDerivation(StorePath drvPath);
/** /**
* Wrappers around the corresponding Store methods that first consult the * Wrappers around the corresponding Store methods that first consult the
* derivation. This is currently needed because when there is no drv file * derivation. This is currently needed because when there is no drv file
* there also is no DB entry. * there also is no DB entry.
*/ */
std::map<std::string, std::optional<StorePath>> queryPartialDerivationOutputMap(const StorePath & drvPath); std::map<std::string, std::optional<StorePath>> queryPartialDerivationOutputMap();
OutputPathMap queryDerivationOutputMap(const StorePath & drvPath); OutputPathMap queryDerivationOutputMap();
/** /**
* Update 'initialOutputs' to determine the current status of the * 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 * whether all outputs are valid and non-corrupt, and a
* 'SingleDrvOutputs' structure containing the valid outputs. * '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 * Aborts if any output is not valid or corrupt, and otherwise
* returns a 'SingleDrvOutputs' structure containing all outputs. * returns a 'SingleDrvOutputs' structure containing all outputs.
*/ */
SingleDrvOutputs assertPathValidity(const StorePath & drvPath); SingleDrvOutputs assertPathValidity();
Co repairClosure(StorePath drvPath); Co repairClosure();
Done done( Done done(
const StorePath & drvPath,
BuildResult::Status status, BuildResult::Status status,
SingleDrvOutputs builtOutputs = {}, SingleDrvOutputs builtOutputs = {},
std::optional<Error> ex = {}); 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 * 2. Substitute the corresponding output path
* 3. Register the output info * 3. Register the output info
*/ */
class DrvOutputSubstitutionGoal : public Goal { class DrvOutputSubstitutionGoal : public Goal
{
/** /**
* The drv output we're trying to substitute * The drv output we're trying to substitute
@ -28,21 +29,25 @@ class DrvOutputSubstitutionGoal : public Goal {
DrvOutput id; DrvOutput id;
public: public:
DrvOutputSubstitutionGoal(const DrvOutput& id, Worker & worker, RepairFlag repair = NoRepair, std::optional<ContentAddress> ca = std::nullopt); DrvOutputSubstitutionGoal(
const DrvOutput & id,
typedef void (DrvOutputSubstitutionGoal::*GoalState)(); Worker & worker,
GoalState state; RepairFlag repair = NoRepair,
std::optional<ContentAddress> ca = std::nullopt);
Co init(); 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; std::string key() override;
void handleEOF(Descriptor fd) override; void handleEOF(Descriptor fd) override;
JobCategory jobCategory() const override { JobCategory jobCategory() const override
{
return JobCategory::Substitution; return JobCategory::Substitution;
}; };
}; };

View file

@ -105,13 +105,11 @@ public:
*/ */
ExitCode exitCode = ecBusy; ExitCode exitCode = ecBusy;
protected:
/** /**
* Build result. * Build result.
*/ */
BuildResult buildResult; BuildResult buildResult;
public:
/** /**
* Suspend our goal and wait until we get `work`-ed again. * Suspend our goal and wait until we get `work`-ed again.
* `co_await`-able by @ref Co. * `co_await`-able by @ref Co.
@ -358,18 +356,6 @@ protected:
public: public:
virtual void cleanup() { } 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 * 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 * it around. Set by a waitee which sees itself as the designated

View file

@ -14,10 +14,13 @@
namespace nix { namespace nix {
/* Forward definition. */ /* Forward definition. */
struct DerivationCreationAndRealisationGoal;
struct DerivationGoal; struct DerivationGoal;
struct DerivationBuildingGoal; struct DerivationBuildingGoal;
struct PathSubstitutionGoal; struct PathSubstitutionGoal;
class DrvOutputSubstitutionGoal; class DrvOutputSubstitutionGoal;
class BuildTraceGoal;
class DerivationResolutionGoal;
/** /**
* Workaround for not being able to declare a something like * 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<PathSubstitutionGoal> subGoal);
GoalPtr upcast_goal(std::shared_ptr<DrvOutputSubstitutionGoal> 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; typedef std::chrono::time_point<std::chrono::steady_clock> steady_time_point;
@ -106,11 +112,14 @@ private:
* same derivation / path. * 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<DerivationBuildingGoal>> derivationBuildingGoals;
std::map<StorePath, std::weak_ptr<PathSubstitutionGoal>> substitutionGoals; std::map<StorePath, std::weak_ptr<PathSubstitutionGoal>> substitutionGoals;
std::map<DrvOutput, std::weak_ptr<DrvOutputSubstitutionGoal>> drvOutputSubstitutionGoals; std::map<DrvOutput, std::weak_ptr<DrvOutputSubstitutionGoal>> drvOutputSubstitutionGoals;
std::map<StorePath, std::weak_ptr<DerivationResolutionGoal>> derivationResolutionGoals;
/** /**
* Goals waiting for busy paths to be unlocked. * Goals waiting for busy paths to be unlocked.
@ -204,16 +213,20 @@ private:
template<class G, typename... Args> template<class G, typename... Args>
std::shared_ptr<G> initGoalIfNeeded(std::weak_ptr<G> & goal_weak, Args && ...args); std::shared_ptr<G> initGoalIfNeeded(std::weak_ptr<G> & goal_weak, Args && ...args);
std::shared_ptr<DerivationGoal> makeDerivationGoalCommon( std::shared_ptr<DerivationCreationAndRealisationGoal> makeDerivationCreationAndRealisationGoal(
ref<const SingleDerivedPath> drvReq, const OutputsSpec & wantedOutputs,
std::function<std::shared_ptr<DerivationGoal>()> mkDrvGoal);
public:
std::shared_ptr<DerivationGoal> makeDerivationGoal(
ref<const SingleDerivedPath> drvReq, ref<const SingleDerivedPath> drvReq,
const OutputsSpec & wantedOutputs, BuildMode buildMode = bmNormal); const OutputsSpec & wantedOutputs, BuildMode buildMode = bmNormal);
std::shared_ptr<DerivationGoal> makeBasicDerivationGoal(
const StorePath & drvPath, const BasicDerivation & drv, public:
const OutputsSpec & wantedOutputs, BuildMode buildMode = bmNormal); 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" * @ref DerivationBuildingGoal "derivation goal"
@ -223,11 +236,26 @@ public:
BuildMode buildMode = bmNormal); 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); 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); 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`. * Make a goal corresponding to the `DerivedPath`.
* *

View file

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

View file

@ -12,7 +12,6 @@ struct Source;
class StorePath; class StorePath;
struct ContentAddress; struct ContentAddress;
struct DrvOutput; struct DrvOutput;
struct Realisation;
/** /**
@ -69,8 +68,6 @@ template<>
DECLARE_COMMON_SERIALISER(ContentAddress); DECLARE_COMMON_SERIALISER(ContentAddress);
template<> template<>
DECLARE_COMMON_SERIALISER(DrvOutput); DECLARE_COMMON_SERIALISER(DrvOutput);
template<>
DECLARE_COMMON_SERIALISER(Realisation);
#define COMMA_ , #define COMMA_ ,
template<typename T> template<typename T>
@ -80,8 +77,8 @@ DECLARE_COMMON_SERIALISER(std::set<T COMMA_ Compare>);
template<typename... Ts> template<typename... Ts>
DECLARE_COMMON_SERIALISER(std::tuple<Ts...>); DECLARE_COMMON_SERIALISER(std::tuple<Ts...>);
template<typename K, typename V> template<typename K, typename V, typename Compare>
DECLARE_COMMON_SERIALISER(std::map<K COMMA_ V>); DECLARE_COMMON_SERIALISER(std::map<K COMMA_ V COMMA_ Compare>);
#undef COMMA_ #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 * derivations (fixed-output or not) will have a different hash for each
* output. * 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 { /**
/** * Known CA drv's output hashes, for fixed-output derivations whose
* Statically determined derivations. * output hashes are always known since they are fixed up-front.
* This hash will be directly used to compute the output paths */
*/ using CaOutputHashes = std::map<std::string, Hash>;
Regular,
/**
* Floating-output derivations (and their reverse dependencies).
*/
Deferred,
};
/** /**
* The kind of derivation this is, simplified for just "derivation hash * This derivation doesn't yet have known output hashes.
* modulo" purposes. *
* 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 * Returns hashes with the details of fixed-output subderivations
* expunged. * expunged.
@ -492,20 +502,23 @@ void operator |= (DrvHash::Kind & self, const DrvHash::Kind & other) noexcept;
* ATerm, after subderivations have been likewise expunged from that * ATerm, after subderivations have been likewise expunged from that
* derivation. * 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 * If a derivation is input addressed and doesn't yet have its input
* derivation (modulo the self-references). * 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(). * Memoisation of hashDerivationModulo().
*/ */
typedef std::map<StorePath, DrvHash> DrvHashes; typedef std::map<StorePath, DrvHashModulo> DrvHashes;
// FIXME: global, though at least thread-safe. // FIXME: global, though at least thread-safe.
extern Sync<DrvHashes> drvHashes; extern Sync<DrvHashes> drvHashes;

View file

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

View file

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

View file

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

View file

@ -160,7 +160,7 @@ private:
* Check lower store if upper DB does not have. * Check lower store if upper DB does not have.
*/ */
void queryRealisationUncached(const DrvOutput&, 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. * Call `remountIfNecessary` after collecting garbage normally.

View file

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

View file

@ -12,9 +12,12 @@ config_pub_h = configure_file(
headers = [config_pub_h] + files( headers = [config_pub_h] + files(
'binary-cache-store.hh', 'binary-cache-store.hh',
'build-result.hh', 'build-result.hh',
'build/build-trace-goal.hh',
'build/derivation-goal.hh', 'build/derivation-goal.hh',
'build/derivation-building-goal.hh', 'build/derivation-building-goal.hh',
'build/derivation-building-misc.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/drv-output-substitution-goal.hh',
'build/goal.hh', 'build/goal.hh',
'build/substitution-goal.hh', 'build/substitution-goal.hh',

View file

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

View file

@ -112,7 +112,7 @@ struct RemoteStore :
void registerDrvOutput(const Realisation & info) override; void registerDrvOutput(const Realisation & info) override;
void queryRealisationUncached(const DrvOutput &, 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; 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 , #define SERVE_USE_LENGTH_PREFIX_SERIALISER_COMMA ,
SERVE_USE_LENGTH_PREFIX_SERIALISER( SERVE_USE_LENGTH_PREFIX_SERIALISER(
template<typename K SERVE_USE_LENGTH_PREFIX_SERIALISER_COMMA typename 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>) std::map<K SERVE_USE_LENGTH_PREFIX_SERIALISER_COMMA V SERVE_USE_LENGTH_PREFIX_SERIALISER_COMMA Compare>)
/** /**
* Use `CommonProto` where possible. * Use `CommonProto` where possible.

View file

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

View file

@ -38,6 +38,7 @@ MakeError(SubstituterDisabled, Error);
MakeError(InvalidStoreReference, Error); MakeError(InvalidStoreReference, Error);
struct UnkeyedRealisation;
struct Realisation; struct Realisation;
struct RealisedPath; struct RealisedPath;
struct DrvOutput; struct DrvOutput;
@ -319,13 +320,13 @@ public:
/** /**
* Query the information about a realisation. * 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(). * Asynchronous version of queryRealisation().
*/ */
void queryRealisation(const DrvOutput &, 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, virtual void queryPathInfoUncached(const StorePath & path,
Callback<std::shared_ptr<const ValidPathInfo>> callback) noexcept = 0; Callback<std::shared_ptr<const ValidPathInfo>> callback) noexcept = 0;
virtual void queryRealisationUncached(const DrvOutput &, virtual void queryRealisationUncached(const DrvOutput &,
Callback<std::shared_ptr<const Realisation>> callback) noexcept = 0; Callback<std::shared_ptr<const UnkeyedRealisation>> callback) noexcept = 0;
public: public:
@ -902,10 +903,4 @@ std::optional<ValidPathInfo> decodeValidPathInfo(
const ContentAddress * getDerivationCA(const BasicDerivation & drv); 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 , #define WORKER_USE_LENGTH_PREFIX_SERIALISER_COMMA ,
WORKER_USE_LENGTH_PREFIX_SERIALISER( WORKER_USE_LENGTH_PREFIX_SERIALISER(
template<typename K WORKER_USE_LENGTH_PREFIX_SERIALISER_COMMA typename 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>) std::map<K WORKER_USE_LENGTH_PREFIX_SERIALISER_COMMA V WORKER_USE_LENGTH_PREFIX_SERIALISER_COMMA Compare>)
/** /**
* Use `CommonProto` where possible. * Use `CommonProto` where possible.

View file

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

View file

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

View file

@ -100,8 +100,6 @@ struct LocalStore::State::Stmts {
SQLiteStmt QueryAllRealisedOutputs; SQLiteStmt QueryAllRealisedOutputs;
SQLiteStmt QueryPathFromHashPart; SQLiteStmt QueryPathFromHashPart;
SQLiteStmt QueryValidPaths; SQLiteStmt QueryValidPaths;
SQLiteStmt QueryRealisationReferences;
SQLiteStmt AddRealisationReference;
}; };
LocalStore::LocalStore(ref<const Config> config) LocalStore::LocalStore(ref<const Config> config)
@ -363,19 +361,6 @@ LocalStore::LocalStore(ref<const Config> config)
where drvPath = ? 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()); info.signatures.end());
state->stmts->UpdateRealisedOutput.use() state->stmts->UpdateRealisedOutput.use()
(concatStringsSep(" ", combinedSignatures)) (concatStringsSep(" ", combinedSignatures))
(info.id.strHash()) (info.id.drvPath.to_string())
(info.id.outputName) (info.id.outputName)
.exec(); .exec();
} else { } else {
@ -618,30 +603,12 @@ void LocalStore::registerDrvOutput(const Realisation & info)
} }
} else { } else {
state->stmts->RegisterRealisedOutput.use() state->stmts->RegisterRealisedOutput.use()
(info.id.strHash()) (info.id.drvPath.to_string())
(info.id.outputName) (info.id.outputName)
(printStorePath(info.outPath)) (printStorePath(info.outPath))
(concatStringsSep(" ", info.signatures)) (concatStringsSep(" ", info.signatures))
.exec(); .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) 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, 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, LocalStore::State & state,
const DrvOutput & id) const DrvOutput & id)
{ {
auto useQueryRealisedOutput( auto useQueryRealisedOutput(
state.stmts->QueryRealisedOutput.use() state.stmts->QueryRealisedOutput.use()
(id.strHash()) (id.drvPath.to_string())
(id.outputName)); (id.outputName));
if (!useQueryRealisedOutput.next()) if (!useQueryRealisedOutput.next())
return std::nullopt; return std::nullopt;
@ -1597,15 +1564,14 @@ std::optional<std::pair<int64_t, Realisation>> LocalStore::queryRealisationCore_
return {{ return {{
realisationDbId, realisationDbId,
Realisation{ UnkeyedRealisation{
.id = id,
.outPath = outputPath, .outPath = outputPath,
.signatures = signatures, .signatures = signatures,
} }
}}; }};
} }
std::optional<const Realisation> LocalStore::queryRealisation_( std::optional<const UnkeyedRealisation> LocalStore::queryRealisation_(
LocalStore::State & state, LocalStore::State & state,
const DrvOutput & id) const DrvOutput & id)
{ {
@ -1614,38 +1580,21 @@ std::optional<const Realisation> LocalStore::queryRealisation_(
return std::nullopt; return std::nullopt;
auto [realisationDbId, res] = *maybeCore; 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 }; return { res };
} }
void LocalStore::queryRealisationUncached(const DrvOutput & id, void LocalStore::queryRealisationUncached(const DrvOutput & id,
Callback<std::shared_ptr<const Realisation>> callback) noexcept Callback<std::shared_ptr<const UnkeyedRealisation>> callback) noexcept
{ {
try { try {
auto maybeRealisation auto maybeRealisation
= retrySQLite<std::optional<const Realisation>>([&]() { = retrySQLite<std::optional<const UnkeyedRealisation>>([&]() {
auto state(_state.lock()); auto state(_state.lock());
return queryRealisation_(*state, id); return queryRealisation_(*state, id);
}); });
if (maybeRealisation) if (maybeRealisation)
callback( callback(
std::make_shared<const Realisation>(maybeRealisation.value())); std::make_shared<const UnkeyedRealisation>(maybeRealisation.value()));
else else
callback(nullptr); callback(nullptr);

View file

@ -253,8 +253,12 @@ subdir('nix-meson-build-support/common')
sources = files( sources = files(
'binary-cache-store.cc', 'binary-cache-store.cc',
'build-result.cc', 'build-result.cc',
'build/build-trace-goal.cc',
'build/derivation-goal.cc', 'build/derivation-goal.cc',
'build/derivation-building-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/drv-output-substitution-goal.cc',
'build/entry-points.cc', 'build/entry-points.cc',
'build/goal.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 // If there are unknown output paths, attempt to find if the
// paths are known to substituters through a realisation. // paths are known to substituters through a realisation.
auto outputHashes = staticOutputHashes(*this, *drv);
knownOutputPaths = true; knownOutputPaths = true;
for (auto [outputName, hash] : outputHashes) { for (auto & [outputName, _] : drv->outputs) {
if (!bfd.outputs.contains(outputName)) if (!bfd.outputs.contains(outputName))
continue; continue;
bool found = false; bool found = false;
for (auto &sub : getDefaultSubstituters()) { for (auto &sub : getDefaultSubstituters()) {
auto realisation = sub->queryRealisation({hash, outputName}); auto realisation = sub->queryRealisation({drvPath, outputName});
if (!realisation) if (!realisation)
continue; continue;
found = true; 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_) OutputPathMap resolveDerivedPath(Store & store, const DerivedPath::Built & bfd, Store * evalStore_)
{ {
auto drvPath = resolveDerivedPath(store, *bfd.drvPath, evalStore_); auto drvPath = resolveDerivedPath(store, *bfd.drvPath, evalStore_);
@ -422,7 +355,7 @@ OutputPathMap resolveDerivedPath(Store & store, const DerivedPath::Built & bfd,
OutputPathMap outputs; OutputPathMap outputs;
for (auto & [outputName, outputPathOpt] : outputsOpt) { for (auto & [outputName, outputPathOpt] : outputsOpt) {
if (!outputPathOpt) if (!outputPathOpt)
throw MissingRealisation(bfd.drvPath->to_string(store), outputName); throw MissingRealisation(store, *bfd.drvPath, drvPath, outputName);
auto & outputPath = *outputPathOpt; auto & outputPath = *outputPathOpt;
outputs.insert_or_assign(outputName, outputPath); outputs.insert_or_assign(outputName, outputPath);
} }
@ -446,7 +379,7 @@ StorePath resolveDerivedPath(Store & store, const SingleDerivedPath & req, Store
store.printStorePath(drvPath), bfd.output); store.printStorePath(drvPath), bfd.output);
auto & optPath = outputPaths.at(bfd.output); auto & optPath = outputPaths.at(bfd.output);
if (!optPath) if (!optPath)
throw MissingRealisation(bfd.drvPath->to_string(store), bfd.output); throw MissingRealisation(store, *bfd.drvPath, drvPath, bfd.output);
return *optPath; return *optPath;
}, },
}, req.raw()); }, req.raw());

View file

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

View file

@ -1,130 +1,113 @@
#include "nix/store/realisation.hh" #include "nix/store/realisation.hh"
#include "nix/store/store-api.hh" #include "nix/store/store-api.hh"
#include "nix/util/closure.hh"
#include "nix/util/signature/local-keys.hh" #include "nix/util/signature/local-keys.hh"
#include <nlohmann/json.hpp> #include "nix/util/json-utils.hh"
namespace nix { namespace nix {
MakeError(InvalidDerivationOutputId, Error); MakeError(InvalidDerivationOutputId, Error);
DrvOutput DrvOutput::parse(const std::string &strRep) { DrvOutput DrvOutput::parse(const StoreDirConfig & store, std::string_view s)
size_t n = strRep.find("!"); {
if (n == strRep.npos) size_t n = s.rfind('^');
throw InvalidDerivationOutputId("Invalid derivation output id %s", strRep); if (n == s.npos)
throw InvalidDerivationOutputId("Invalid derivation output id '%s': missing '^'", s);
return DrvOutput{ return DrvOutput{
.drvHash = Hash::parseAnyPrefixed(strRep.substr(0, n)), .drvPath = store.parseStorePath(s.substr(0, n)),
.outputName = strRep.substr(n+1), .outputName = OutputName{s.substr(n + 1)},
}; };
} }
std::string DrvOutput::to_string() const { std::string DrvOutput::render(const StoreDirConfig & store) const
return strHash() + "!" + outputName;
}
std::set<Realisation> Realisation::closure(Store & store, const std::set<Realisation> & startOutputs)
{ {
std::set<Realisation> res; return std::string(store.printStorePath(drvPath)) + "^" + outputName;
Realisation::closure(store, startOutputs, res);
return res;
} }
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> { return std::string(drvPath.to_string()) + "^" + outputName;
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);
});
} }
nlohmann::json Realisation::toJSON() const { nlohmann::json DrvOutput::toJSON(const StoreDirConfig & store) const
auto jsonDependentRealisations = nlohmann::json::object(); {
for (auto & [depId, depOutPath] : dependentRealisations)
jsonDependentRealisations.emplace(depId.to_string(), depOutPath.to_string());
return nlohmann::json{ return nlohmann::json{
{"id", id.to_string()}, {"drvPath", store.printStorePath(drvPath)},
{"outPath", outPath.to_string()}, {"outputName", outputName},
{"signatures", signatures},
{"dependentRealisations", jsonDependentRealisations},
}; };
} }
Realisation Realisation::fromJSON( DrvOutput DrvOutput::fromJSON(const StoreDirConfig & store, const nlohmann::json & json)
const nlohmann::json& json, {
const std::string& whence) { auto obj = getObject(json);
auto getOptionalField = [&](std::string fieldName) -> std::optional<std::string> {
auto fieldIterator = json.find(fieldName); return {
if (fieldIterator == json.end()) .drvPath = store.parseStorePath(getString(valueAt(obj, "drvPath"))),
return std::nullopt; .outputName = getString(valueAt(obj, "outputName")),
return {*fieldIterator};
}; };
auto getField = [&](std::string fieldName) -> std::string { }
if (auto field = getOptionalField(fieldName))
return *field; nlohmann::json UnkeyedRealisation::toJSON(const StoreDirConfig & store) const
else {
throw Error( return nlohmann::json{
"Drv output info file '%1%' is corrupt, missing field %2%", {"outPath", store.printStorePath(outPath)},
whence, fieldName); {"signatures", signatures},
}; };
}
UnkeyedRealisation UnkeyedRealisation::fromJSON(const StoreDirConfig & store, const nlohmann::json & json)
{
auto obj = getObject(json);
StringSet signatures; StringSet signatures;
if (auto signaturesIterator = json.find("signatures"); signaturesIterator != json.end()) if (auto * signaturesJson = get(obj, "signatures"))
signatures.insert(signaturesIterator->begin(), signaturesIterator->end()); signatures = getStringSet(*signaturesJson);
std::map <DrvOutput, StorePath> dependentRealisations; return {
if (auto jsonDependencies = json.find("dependentRealisations"); jsonDependencies != json.end()) .outPath = store.parseStorePath(getString(valueAt(obj, "outPath"))),
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")),
.signatures = signatures, .signatures = signatures,
.dependentRealisations = dependentRealisations,
}; };
} }
std::string Realisation::fingerprint() const nlohmann::json Realisation::toJSON(const StoreDirConfig & store) const
{ {
auto serialized = toJSON(); return nlohmann::json{
serialized.erase("signatures"); {"key", id.toJSON(store)},
return serialized.dump(); {"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 // 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 // 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; size_t good = 0;
for (auto & sig : signatures) for (auto & sig : signatures)
if (checkSignature(publicKeys, sig)) if (checkSignature(store, key, publicKeys, sig))
good++; good++;
return good; return good;
} }
SingleDrvOutputs filterDrvOutputs(const OutputsSpec & wanted, SingleDrvOutputs && outputs)
SingleDrvOutputs filterDrvOutputs(const OutputsSpec& wanted, SingleDrvOutputs&& outputs)
{ {
SingleDrvOutputs ret = std::move(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)) if (!wanted.contains(it->first))
it = ret.erase(it); it = ret.erase(it);
else else
@ -150,53 +132,25 @@ SingleDrvOutputs filterDrvOutputs(const OutputsSpec& wanted, SingleDrvOutputs&&
return ret; return ret;
} }
StorePath RealisedPath::path() const { const StorePath & RealisedPath::path() const
return std::visit([](auto && arg) { return arg.getPath(); }, raw); {
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( MissingRealisation::MissingRealisation(
Store& store, const StoreDirConfig & store,
const RealisedPath::Set& startPaths, const SingleDerivedPath & drvPath,
RealisedPath::Set& ret) const StorePath & drvPathResolved,
const OutputName & outputName)
: MissingRealisation{store, drvPathResolved, outputName}
{ {
// FIXME: This only builds the store-path closure, not the real realisation addTrace({}, "looking up realisation for derivation '%s'", drvPath.to_string(store));
// 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;
} }
} // namespace nix } // namespace nix

View file

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

View file

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

View file

@ -6,6 +6,7 @@
#include "nix/store/serve-protocol-impl.hh" #include "nix/store/serve-protocol-impl.hh"
#include "nix/util/archive.hh" #include "nix/util/archive.hh"
#include "nix/store/path-info.hh" #include "nix/store/path-info.hh"
#include "nix/util/json-utils.hh"
#include <nlohmann/json.hpp> #include <nlohmann/json.hpp>
@ -25,13 +26,22 @@ BuildResult ServeProto::Serialise<BuildResult>::read(const StoreDirConfig & stor
>> status.isNonDeterministic >> status.isNonDeterministic
>> status.startTime >> status.startTime
>> status.stopTime; >> status.stopTime;
if (GET_PROTOCOL_MINOR(conn.version) >= 6) {
auto builtOutputs = ServeProto::Serialise<DrvOutputs>::read(store, conn); if (GET_PROTOCOL_MINOR(conn.version) >= 8) {
for (auto && [output, realisation] : builtOutputs) 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( status.builtOutputs.insert_or_assign(
std::move(output.outputName), output.substr(n + 1),
std::move(realisation)); UnkeyedRealisation{StorePath{
getString(valueAt(getObject(nlohmann::json::parse(realisation)), "outPath"))
}});
}
} }
return status; return status;
} }
@ -47,11 +57,12 @@ void ServeProto::Serialise<BuildResult>::write(const StoreDirConfig & store, Ser
<< status.isNonDeterministic << status.isNonDeterministic
<< status.startTime << status.startTime
<< status.stopTime; << status.stopTime;
if (GET_PROTOCOL_MINOR(conn.version) >= 6) {
DrvOutputs builtOutputs; if (GET_PROTOCOL_MINOR(conn.version) >= 8) {
for (auto & [output, realisation] : status.builtOutputs) ServeProto::write(store, conn, status.builtOutputs);
builtOutputs.insert_or_assign(realisation.id, realisation); } else if (GET_PROTOCOL_MINOR(conn.version) >= 6) {
ServeProto::write(store, conn, builtOutputs); // 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; return outputs;
auto drv = evalStore.readInvalidDerivation(path); auto drv = evalStore.readInvalidDerivation(path);
auto drvHashes = staticOutputHashes(*this, drv); for (auto & [outputName, _] : drv.outputs) {
for (auto & [outputName, hash] : drvHashes) { auto realisation = queryRealisation(DrvOutput{path, outputName});
auto realisation = queryRealisation(DrvOutput{hash, outputName});
if (realisation) { if (realisation) {
outputs.insert_or_assign(outputName, realisation->outPath); outputs.insert_or_assign(outputName, realisation->outPath);
} else { } else {
@ -500,7 +499,7 @@ OutputPathMap Store::queryDerivationOutputMap(const StorePath & path, Store * ev
OutputPathMap result; OutputPathMap result;
for (auto & [outName, optOutPath] : resp) { for (auto & [outName, optOutPath] : resp) {
if (!optOutPath) if (!optOutPath)
throw MissingRealisation(printStorePath(path), outName); throw MissingRealisation(*this, path, outName);
result.insert_or_assign(outName, *optOutPath); result.insert_or_assign(outName, *optOutPath);
} }
return result; return result;
@ -716,7 +715,7 @@ void Store::queryPathInfo(const StorePath & storePath,
} }
void Store::queryRealisation(const DrvOutput & id, void Store::queryRealisation(const DrvOutput & id,
Callback<std::shared_ptr<const Realisation>> callback) noexcept Callback<std::shared_ptr<const UnkeyedRealisation>> callback) noexcept
{ {
try { try {
@ -748,18 +747,18 @@ void Store::queryRealisation(const DrvOutput & id,
queryRealisationUncached( queryRealisationUncached(
id, id,
{ [this, id, callbackPtr]( { [this, id, callbackPtr](
std::future<std::shared_ptr<const Realisation>> fut) { std::future<std::shared_ptr<const UnkeyedRealisation>> fut) {
try { try {
auto info = fut.get(); auto info = fut.get();
if (diskCache) { if (diskCache) {
if (info) if (info)
diskCache->upsertRealisation(getUri(), *info); diskCache->upsertRealisation(getUri(), {*info, id});
else else
diskCache->upsertAbsentRealisation(getUri(), id); diskCache->upsertAbsentRealisation(getUri(), id);
} }
(*callbackPtr)(std::shared_ptr<const Realisation>(info)); (*callbackPtr)(std::shared_ptr<const UnkeyedRealisation>(info));
} catch (...) { } catch (...) {
callbackPtr->rethrow(); 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; std::promise<RealPtr> promise;
queryRealisation(id, queryRealisation(id,
@ -1019,36 +1018,21 @@ std::map<StorePath, StorePath> copyPaths(
SubstituteFlag substitute) SubstituteFlag substitute)
{ {
StorePathSet storePaths; StorePathSet storePaths;
std::set<Realisation> toplevelRealisations; std::vector<const Realisation *> realisations;
for (auto & path : paths) { for (auto & path : paths) {
storePaths.insert(path.path()); 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); experimentalFeatureSettings.require(Xp::CaDerivations);
toplevelRealisations.insert(*realisation); realisations.push_back(realisation);
} }
} }
auto pathsMap = copyPaths(srcStore, dstStore, storePaths, repair, checkSigs, substitute); auto pathsMap = copyPaths(srcStore, dstStore, storePaths, repair, checkSigs, substitute);
try { try {
// Copy the realisation closure // Copy the realisation closure
processGraph<Realisation>( for (const auto * realisation : realisations)
Realisation::closure(srcStore, toplevelRealisations), dstStore.registerDrvOutput(*realisation, checkSigs);
[&](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);
});
} catch (MissingExperimentalFeature & e) { } catch (MissingExperimentalFeature & e) {
// Don't fail if the remote doesn't support CA derivations is it might // 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 // not be within our control to change that, and we might still want
@ -1154,8 +1138,19 @@ void copyClosure(
{ {
if (&srcStore == &dstStore) return; if (&srcStore == &dstStore) return;
RealisedPath::Set closure; StorePathSet closure0;
RealisedPath::closure(srcStore, paths, closure); 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); copyPaths(srcStore, dstStore, closure, repair, checkSigs, substitute);
} }
@ -1300,7 +1295,7 @@ void Store::signRealisation(Realisation & realisation)
for (auto & secretKeyFile : secretKeyFiles.get()) { for (auto & secretKeyFile : secretKeyFiles.get()) {
SecretKey secretKey(readFile(secretKeyFile)); SecretKey secretKey(readFile(secretKeyFile));
LocalSigner signer(std::move(secretKey)); 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); auto oldinfo = get(initialOutputs, outputName);
assert(oldinfo); assert(oldinfo);
auto thisRealisation = Realisation { auto thisRealisation = Realisation {
.id = DrvOutput { {
oldinfo->outputHash, .outPath = newInfo.path,
outputName },
DrvOutput {
.drvPath = drvPath,
.outputName = outputName,
}, },
.outPath = newInfo.path
}; };
if (experimentalFeatureSettings.isEnabled(Xp::CaDerivations) if (experimentalFeatureSettings.isEnabled(Xp::CaDerivations)
&& !drv.type().isImpure()) && !drv.type().isImpure())

View file

@ -6,6 +6,7 @@
#include "nix/store/worker-protocol-impl.hh" #include "nix/store/worker-protocol-impl.hh"
#include "nix/util/archive.hh" #include "nix/util/archive.hh"
#include "nix/store/path-info.hh" #include "nix/store/path-info.hh"
#include "nix/util/json-utils.hh"
#include <chrono> #include <chrono>
#include <nlohmann/json.hpp> #include <nlohmann/json.hpp>
@ -124,7 +125,7 @@ void WorkerProto::Serialise<DerivedPath>::write(const StoreDirConfig & store, Wo
[&](const StorePath & drvPath) { [&](const StorePath & drvPath) {
throw Error("trying to request '%s', but daemon protocol %d.%d is too old (< 1.29) to request a derivation file", throw Error("trying to request '%s', but daemon protocol %d.%d is too old (< 1.29) to request a derivation file",
store.printStorePath(drvPath), store.printStorePath(drvPath),
GET_PROTOCOL_MAJOR(conn.version), GET_PROTOCOL_MAJOR(conn.version) >> 8,
GET_PROTOCOL_MINOR(conn.version)); GET_PROTOCOL_MINOR(conn.version));
}, },
[&](std::monostate) { [&](std::monostate) {
@ -157,6 +158,7 @@ BuildResult WorkerProto::Serialise<BuildResult>::read(const StoreDirConfig & sto
BuildResult res; BuildResult res;
res.status = static_cast<BuildResult::Status>(readInt(conn.from)); res.status = static_cast<BuildResult::Status>(readInt(conn.from));
conn.from >> res.errorMsg; conn.from >> res.errorMsg;
if (GET_PROTOCOL_MINOR(conn.version) >= 29) { if (GET_PROTOCOL_MINOR(conn.version) >= 29) {
conn.from conn.from
>> res.timesBuilt >> res.timesBuilt
@ -164,17 +166,27 @@ BuildResult WorkerProto::Serialise<BuildResult>::read(const StoreDirConfig & sto
>> res.startTime >> res.startTime
>> res.stopTime; >> res.stopTime;
} }
if (GET_PROTOCOL_MINOR(conn.version) >= 37) { if (GET_PROTOCOL_MINOR(conn.version) >= 37) {
res.cpuUser = WorkerProto::Serialise<std::optional<std::chrono::microseconds>>::read(store, conn); res.cpuUser = WorkerProto::Serialise<std::optional<std::chrono::microseconds>>::read(store, conn);
res.cpuSystem = 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); if (GET_PROTOCOL_MINOR(conn.version) >= 39) {
for (auto && [output, realisation] : builtOutputs) 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( res.builtOutputs.insert_or_assign(
std::move(output.outputName), output.substr(n + 1),
std::move(realisation)); UnkeyedRealisation{StorePath{
getString(valueAt(getObject(nlohmann::json::parse(realisation)), "outPath"))
}});
}
} }
return res; return res;
} }
@ -183,6 +195,7 @@ void WorkerProto::Serialise<BuildResult>::write(const StoreDirConfig & store, Wo
conn.to conn.to
<< res.status << res.status
<< res.errorMsg; << res.errorMsg;
if (GET_PROTOCOL_MINOR(conn.version) >= 29) { if (GET_PROTOCOL_MINOR(conn.version) >= 29) {
conn.to conn.to
<< res.timesBuilt << res.timesBuilt
@ -190,15 +203,17 @@ void WorkerProto::Serialise<BuildResult>::write(const StoreDirConfig & store, Wo
<< res.startTime << res.startTime
<< res.stopTime; << res.stopTime;
} }
if (GET_PROTOCOL_MINOR(conn.version) >= 37) { if (GET_PROTOCOL_MINOR(conn.version) >= 37) {
WorkerProto::write(store, conn, res.cpuUser); WorkerProto::write(store, conn, res.cpuUser);
WorkerProto::write(store, conn, res.cpuSystem); WorkerProto::write(store, conn, res.cpuSystem);
} }
if (GET_PROTOCOL_MINOR(conn.version) >= 28) {
DrvOutputs builtOutputs; if (GET_PROTOCOL_MINOR(conn.version) >= 39) {
for (auto & [output, realisation] : res.builtOutputs) WorkerProto::write(store, conn, res.builtOutputs);
builtOutputs.insert_or_assign(realisation.id, realisation); } else if (GET_PROTOCOL_MINOR(conn.version) >= 28) {
WorkerProto::write(store, conn, builtOutputs); // 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) { 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 { } else {
// We don't know the answer; protocol to old. // We don't know the answer; protocol to old.
res.remoteTrustsUs = std::nullopt; 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 { }; output.second = DerivationOutput::Deferred { };
drv.env[output.first] = ""; drv.env[output.first] = "";
} }
auto hashesModulo = hashDerivationModulo(*evalStore, drv, true);
for (auto & output : drv.outputs) { resolveInputAddressed(*evalStore, drv);
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);
}
} }
auto shellDrvPath = writeDerivation(*evalStore, drv); auto shellDrvPath = writeDerivation(*evalStore, drv);

View file

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

View file

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

View file

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