mirror of
https://github.com/NixOS/nix
synced 2025-06-25 02:21:16 +02:00
Merge 7b01c8b8cd
into 087f268615
This commit is contained in:
commit
feafab92d4
75 changed files with 1988 additions and 1444 deletions
|
@ -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$''
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
// We’ve built it, so we must have the realisation.
|
||||||
auto thisRealisation = store.queryRealisation(
|
assert(thisRealisation);
|
||||||
DrvOutput{*drvOutput, outputName});
|
res.insert(Realisation{*thisRealisation, key});
|
||||||
assert(thisRealisation); // We’ve built it, so we must
|
|
||||||
// have the realisation
|
|
||||||
res.insert(*thisRealisation);
|
|
||||||
} else {
|
} else {
|
||||||
res.insert(outputPath);
|
res.insert(outputPath);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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. */
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
#include "fetchers.hh"
|
#include "nix/fetchers/fetchers.hh"
|
||||||
|
|
||||||
namespace nix::fetchers {
|
namespace nix::fetchers {
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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",
|
||||||
|
|
BIN
src/libstore-tests/data/serve-protocol/build-result-2.8.bin
Normal file
BIN
src/libstore-tests/data/serve-protocol/build-result-2.8.bin
Normal file
Binary file not shown.
BIN
src/libstore-tests/data/serve-protocol/drv-output-2.8.bin
Normal file
BIN
src/libstore-tests/data/serve-protocol/drv-output-2.8.bin
Normal file
Binary file not shown.
BIN
src/libstore-tests/data/serve-protocol/realisation-2.8.bin
Normal file
BIN
src/libstore-tests/data/serve-protocol/realisation-2.8.bin
Normal file
Binary file not shown.
Binary file not shown.
BIN
src/libstore-tests/data/worker-protocol/build-result-1.39.bin
Normal file
BIN
src/libstore-tests/data/worker-protocol/build-result-1.39.bin
Normal file
Binary file not shown.
BIN
src/libstore-tests/data/worker-protocol/drv-output-1.39.bin
Normal file
BIN
src/libstore-tests/data/worker-protocol/drv-output-1.39.bin
Normal file
Binary file not shown.
BIN
src/libstore-tests/data/worker-protocol/realisation-1.39.bin
Normal file
BIN
src/libstore-tests/data/worker-protocol/realisation-1.39.bin
Normal file
Binary file not shown.
Binary file not shown.
|
@ -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" },
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
@ -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" },
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
@ -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)
|
||||||
|
|
208
src/libstore/build/build-trace-goal.cc
Normal file
208
src/libstore/build/build-trace-goal.cc
Normal 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, we’re 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());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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;
|
||||||
|
|
177
src/libstore/build/derivation-creation-and-realisation-goal.cc
Normal file
177
src/libstore/build/derivation-creation-and-realisation-goal.cc
Normal 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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)
|
||||||
|
|
82
src/libstore/build/derivation-resolution-goal.cc
Normal file
82
src/libstore/build/derivation-resolution-goal.cc
Normal 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());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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, we’re 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());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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())
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
|
||||||
-- it’s 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);
|
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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>>>;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -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 let’s 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 let’s 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);
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
|
54
src/libstore/include/nix/store/build/build-trace-goal.hh
Normal file
54
src/libstore/include/nix/store/build/build-trace-goal.hh
Normal 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;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
|
@ -44,7 +44,6 @@ struct InitialOutputStatus
|
||||||
struct InitialOutput
|
struct InitialOutput
|
||||||
{
|
{
|
||||||
bool wanted;
|
bool wanted;
|
||||||
Hash outputHash;
|
|
||||||
std::optional<InitialOutputStatus> known;
|
std::optional<InitialOutputStatus> known;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -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();
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
|
@ -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 = {});
|
||||||
|
|
|
@ -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;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
|
@ -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;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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`.
|
||||||
*
|
*
|
||||||
|
|
|
@ -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_
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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_
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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"); }
|
||||||
};
|
};
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
@ -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',
|
||||||
|
|
|
@ -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);
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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_
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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_
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 (...) {
|
||||||
|
|
|
@ -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 doesn’t"
|
|
||||||
"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);
|
||||||
|
|
||||||
|
|
|
@ -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',
|
||||||
|
|
|
@ -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' isn’t 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());
|
||||||
|
|
|
@ -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();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -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. "
|
|
||||||
"I’ll 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
|
||||||
|
|
|
@ -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,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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));
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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())
|
||||||
|
|
|
@ -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));
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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());
|
||||||
|
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue