mirror of
https://github.com/NixOS/nix.git
synced 2025-11-08 19:46:02 +01:00
Revert "Use the hash modulo in the derivation outputs"
Fix #11897
As described in the issue, this makes for a simpler and much more
intuitive notion of a realisation key. This is better for pedagogy, and
interoperability between more tools.
The way the issue was written was that we would switch to only having
shallow realisations first, and then do this. But going to only shallow
realisations is more complex change, and it turns out we weren't even
testing for the benefits that derivation hashes (modulo FODs) provided
in the deep realisation case, so I now just want to do this first.
Doing this gets the binary cache data structures in order, which will
unblock the Hydra fixed-output-derivation tracking work. I don't want to
delay that work while I figure out the changes needed for
shallow-realisations only.
This reverts commit bab1cda0e6.
This commit is contained in:
parent
f392663533
commit
b41ce0d263
53 changed files with 909 additions and 700 deletions
|
|
@ -108,20 +108,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(Xp::CaDerivations)) {
|
if (experimentalFeatureSettings.isEnabled(Xp::CaDerivations)) {
|
||||||
auto drvOutput = get(drvHashes, outputName);
|
DrvOutput key{
|
||||||
if (!drvOutput)
|
.drvPath = p.drvPath->outPath(),
|
||||||
throw Error(
|
.outputName = outputName,
|
||||||
"the derivation '%s' has unrealised output '%s' (derived-path.cc/toRealisedPaths)",
|
};
|
||||||
store.printStorePath(p.drvPath->outPath()),
|
|
||||||
outputName);
|
|
||||||
DrvOutput key{*drvOutput, outputName};
|
|
||||||
auto thisRealisation = store.queryRealisation(key);
|
auto thisRealisation = store.queryRealisation(key);
|
||||||
assert(thisRealisation); // We’ve built it, so we must
|
// We’ve built it, so we must have the realisation.
|
||||||
// have the realisation
|
assert(thisRealisation);
|
||||||
res.insert(Realisation{*thisRealisation, std::move(key)});
|
res.insert(Realisation{*thisRealisation, key});
|
||||||
} else {
|
} else {
|
||||||
res.insert(outputPath);
|
res.insert(outputPath);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1718,28 +1718,7 @@ static void derivationStrictInternal(EvalState & state, std::string_view drvName
|
||||||
drv.outputs.insert_or_assign(i, DerivationOutput::Deferred{});
|
drv.outputs.insert_or_assign(i, 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. */
|
||||||
|
|
|
||||||
|
|
@ -108,61 +108,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{
|
|
||||||
{
|
|
||||||
.outPath = StorePath{"g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-foo"},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
.drvHash = Hash::parseSRI("sha256-FePFYIlMuycIXPZbWi7LGEiMmZSX9FMbaQenWBzm1Sc="),
|
|
||||||
.outputName = "baz",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Realisation{
|
|
||||||
{
|
|
||||||
.outPath = StorePath{"g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-foo"},
|
|
||||||
.signatures = {"asdf", "qwer"},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
.drvHash = Hash::parseSRI("sha256-FePFYIlMuycIXPZbWi7LGEiMmZSX9FMbaQenWBzm1Sc="),
|
|
||||||
.outputName = "baz",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}))
|
|
||||||
|
|
||||||
READ_CHARACTERIZATION_TEST(
|
|
||||||
realisation_with_deps,
|
|
||||||
"realisation-with-deps",
|
|
||||||
(std::tuple<Realisation>{
|
|
||||||
Realisation{
|
|
||||||
{
|
|
||||||
.outPath = StorePath{"g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-foo"},
|
|
||||||
.signatures = {"asdf", "qwer"},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
.drvHash = Hash::parseSRI("sha256-FePFYIlMuycIXPZbWi7LGEiMmZSX9FMbaQenWBzm1Sc="),
|
|
||||||
.outputName = "baz",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}))
|
|
||||||
|
|
||||||
CHARACTERIZATION_TEST(
|
CHARACTERIZATION_TEST(
|
||||||
vector,
|
vector,
|
||||||
"vector",
|
"vector",
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,10 @@
|
||||||
{
|
{
|
||||||
"dependentRealisations": {},
|
"key": {
|
||||||
"id": "sha256:ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad!foo",
|
"drvPath": "g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-bar.drv",
|
||||||
"outPath": "g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-foo.drv",
|
"outputName": "foo"
|
||||||
"signatures": []
|
},
|
||||||
|
"value": {
|
||||||
|
"outPath": "g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-foo",
|
||||||
|
"signatures": []
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,12 @@
|
||||||
{
|
{
|
||||||
"dependentRealisations": {},
|
"key": {
|
||||||
"id": "sha256:ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad!foo",
|
"drvPath": "g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-bar.drv",
|
||||||
"outPath": "g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-foo.drv",
|
"outputName": "foo"
|
||||||
"signatures": [
|
},
|
||||||
"asdfasdfasdf"
|
"value": {
|
||||||
]
|
"outPath": "g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-foo",
|
||||||
|
"signatures": [
|
||||||
|
"asdfasdfasdf"
|
||||||
|
]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
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.
|
|
@ -37,20 +37,19 @@ TEST(DummyStore, realisation_read)
|
||||||
return cfg->openDummyStore();
|
return cfg->openDummyStore();
|
||||||
}();
|
}();
|
||||||
|
|
||||||
auto drvHash = Hash::parseExplicitFormatUnprefixed(
|
StorePath drvPath{"g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-bar.drv"};
|
||||||
"ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad", HashAlgorithm::SHA256, HashFormat::Base16);
|
|
||||||
|
|
||||||
auto outputName = "foo";
|
auto outputName = "foo";
|
||||||
|
|
||||||
EXPECT_EQ(store->queryRealisation({drvHash, outputName}), nullptr);
|
EXPECT_EQ(store->queryRealisation({drvPath, outputName}), nullptr);
|
||||||
|
|
||||||
UnkeyedRealisation value{
|
UnkeyedRealisation value{
|
||||||
.outPath = StorePath{"g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-foo.drv"},
|
.outPath = StorePath{"g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-foo"},
|
||||||
};
|
};
|
||||||
|
|
||||||
store->buildTrace.insert({drvHash, {{outputName, make_ref<UnkeyedRealisation>(value)}}});
|
store->buildTrace.insert({drvPath, {{outputName, make_ref<UnkeyedRealisation>(value)}}});
|
||||||
|
|
||||||
auto value2 = store->queryRealisation({drvHash, outputName});
|
auto value2 = store->queryRealisation({drvPath, outputName});
|
||||||
|
|
||||||
ASSERT_TRUE(value2);
|
ASSERT_TRUE(value2);
|
||||||
EXPECT_EQ(*value2, value);
|
EXPECT_EQ(*value2, value);
|
||||||
|
|
|
||||||
|
|
@ -44,45 +44,30 @@ TEST_P(RealisationJsonTest, to_json)
|
||||||
writeJsonTest(name, value);
|
writeJsonTest(name, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
Realisation simple{
|
INSTANTIATE_TEST_SUITE_P(RealisationJSON, RealisationJsonTest, ([] {
|
||||||
{
|
Realisation simple{
|
||||||
.outPath = StorePath{"g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-foo.drv"},
|
{
|
||||||
},
|
.outPath = StorePath{"g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-foo"},
|
||||||
{
|
},
|
||||||
.drvHash = Hash::parseExplicitFormatUnprefixed(
|
{
|
||||||
"ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad",
|
.drvPath = StorePath{"g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-bar.drv"},
|
||||||
HashAlgorithm::SHA256,
|
.outputName = "foo",
|
||||||
HashFormat::Base16),
|
},
|
||||||
.outputName = "foo",
|
};
|
||||||
},
|
return ::testing::Values(
|
||||||
};
|
std::pair{
|
||||||
|
"simple",
|
||||||
INSTANTIATE_TEST_SUITE_P(
|
simple,
|
||||||
RealisationJSON,
|
},
|
||||||
RealisationJsonTest,
|
std::pair{
|
||||||
::testing::Values(
|
"with-signature",
|
||||||
std::pair{
|
[&] {
|
||||||
"simple",
|
auto r = simple;
|
||||||
simple,
|
// FIXME actually sign properly
|
||||||
},
|
r.signatures = {"asdfasdfasdf"};
|
||||||
std::pair{
|
return r;
|
||||||
"with-signature",
|
}(),
|
||||||
[&] {
|
});
|
||||||
auto r = simple;
|
}()));
|
||||||
// FIXME actually sign properly
|
|
||||||
r.signatures = {"asdfasdfasdf"};
|
|
||||||
return r;
|
|
||||||
}(),
|
|
||||||
}));
|
|
||||||
|
|
||||||
/**
|
|
||||||
* We no longer have a notion of "dependent realisations", but we still
|
|
||||||
* want to parse old realisation files. So make this just be a read test
|
|
||||||
* (no write direction), accordingly.
|
|
||||||
*/
|
|
||||||
TEST_F(RealisationTest, dependent_realisations_from_json)
|
|
||||||
{
|
|
||||||
readJsonTest("with-dependent-realisations", simple);
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace nix
|
} // namespace nix
|
||||||
|
|
|
||||||
|
|
@ -73,16 +73,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",
|
||||||
},
|
},
|
||||||
}))
|
}))
|
||||||
|
|
@ -91,46 +91,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"},
|
||||||
{
|
.signatures = {"asdf", "qwer"},
|
||||||
.outPath = StorePath{"g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-foo"},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
.drvHash = Hash::parseSRI("sha256-FePFYIlMuycIXPZbWi7LGEiMmZSX9FMbaQenWBzm1Sc="),
|
|
||||||
.outputName = "baz",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Realisation{
|
|
||||||
{
|
|
||||||
.outPath = StorePath{"g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-foo"},
|
|
||||||
.signatures = {"asdf", "qwer"},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
.drvHash = Hash::parseSRI("sha256-FePFYIlMuycIXPZbWi7LGEiMmZSX9FMbaQenWBzm1Sc="),
|
|
||||||
.outputName = "baz",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}))
|
}))
|
||||||
|
|
||||||
VERSIONED_READ_CHARACTERIZATION_TEST(
|
VERSIONED_CHARACTERIZATION_TEST(
|
||||||
ServeProtoTest,
|
ServeProtoTest,
|
||||||
realisation_with_deps,
|
realisation_2_8,
|
||||||
"realisation-with-deps",
|
"realisation-2.8",
|
||||||
defaultVersion,
|
2 << 8 | 8,
|
||||||
(std::tuple<Realisation>{
|
(Realisation{
|
||||||
Realisation{
|
UnkeyedRealisation{
|
||||||
{
|
.outPath = StorePath{"g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-foo"},
|
||||||
.outPath = StorePath{"g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-foo"},
|
.signatures = {"asdf", "qwer"},
|
||||||
.signatures = {"asdf", "qwer"},
|
},
|
||||||
},
|
{
|
||||||
{
|
.drvPath = StorePath{"g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-foo.drv"},
|
||||||
.drvHash = Hash::parseSRI("sha256-FePFYIlMuycIXPZbWi7LGEiMmZSX9FMbaQenWBzm1Sc="),
|
.outputName = "baz",
|
||||||
.outputName = "baz",
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
}))
|
}))
|
||||||
|
|
||||||
|
|
@ -180,7 +161,10 @@ VERSIONED_CHARACTERIZATION_TEST(ServeProtoTest, buildResult_2_3, "build-result-2
|
||||||
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, buildResult_2_6, "build-result-2.6", 2 << 8 | 6, ({
|
ServeProtoTest, buildResult_2_6, "build-result-2.6", 2 << 8 | 6, ({
|
||||||
using namespace std::literals::chrono_literals;
|
using namespace std::literals::chrono_literals;
|
||||||
std::tuple<BuildResult, BuildResult, BuildResult> t{
|
std::tuple<BuildResult, BuildResult, BuildResult> t{
|
||||||
|
|
@ -206,27 +190,65 @@ VERSIONED_CHARACTERIZATION_TEST(
|
||||||
{
|
{
|
||||||
"foo",
|
"foo",
|
||||||
{
|
{
|
||||||
{
|
.outPath = StorePath{"g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-foo"},
|
||||||
.outPath = StorePath{"g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-foo"},
|
|
||||||
},
|
|
||||||
DrvOutput{
|
|
||||||
.drvHash =
|
|
||||||
Hash::parseSRI("sha256-b4afnqKCO9oWXgYHb9DeQ2berSwOjS27rSd9TxXDc/U="),
|
|
||||||
.outputName = "foo",
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"bar",
|
"bar",
|
||||||
{
|
{
|
||||||
{
|
.outPath = StorePath{"g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-bar"},
|
||||||
.outPath = StorePath{"g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-bar"},
|
},
|
||||||
},
|
},
|
||||||
DrvOutput{
|
},
|
||||||
.drvHash =
|
}},
|
||||||
Hash::parseSRI("sha256-b4afnqKCO9oWXgYHb9DeQ2berSwOjS27rSd9TxXDc/U="),
|
.timesBuilt = 1,
|
||||||
.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{.inner{BuildResult::Failure{
|
||||||
|
.status = BuildResult::Failure::OutputRejected,
|
||||||
|
.errorMsg = "no idea why",
|
||||||
|
}}},
|
||||||
|
BuildResult{
|
||||||
|
.inner{BuildResult::Failure{
|
||||||
|
.status = BuildResult::Failure::NotDeterministic,
|
||||||
|
.errorMsg = "no idea why",
|
||||||
|
.isNonDeterministic = true,
|
||||||
|
}},
|
||||||
|
.timesBuilt = 3,
|
||||||
|
.startTime = 30,
|
||||||
|
.stopTime = 50,
|
||||||
|
},
|
||||||
|
BuildResult{
|
||||||
|
.inner{BuildResult::Success{
|
||||||
|
.status = BuildResult::Success::Built,
|
||||||
|
.builtOutputs =
|
||||||
|
{
|
||||||
|
{
|
||||||
|
"foo",
|
||||||
|
{
|
||||||
|
.outPath = StorePath{"g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-foo"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"bar",
|
||||||
|
{
|
||||||
|
.outPath = StorePath{"g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-bar"},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -129,61 +129,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"},
|
||||||
{
|
.signatures = {"asdf", "qwer"},
|
||||||
.outPath = StorePath{"g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-foo"},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
.drvHash = Hash::parseSRI("sha256-FePFYIlMuycIXPZbWi7LGEiMmZSX9FMbaQenWBzm1Sc="),
|
|
||||||
.outputName = "baz",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Realisation{
|
|
||||||
{
|
|
||||||
.outPath = StorePath{"g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-foo"},
|
|
||||||
.signatures = {"asdf", "qwer"},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
.drvHash = Hash::parseSRI("sha256-FePFYIlMuycIXPZbWi7LGEiMmZSX9FMbaQenWBzm1Sc="),
|
|
||||||
.outputName = "baz",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}))
|
}))
|
||||||
|
|
||||||
VERSIONED_READ_CHARACTERIZATION_TEST(
|
VERSIONED_CHARACTERIZATION_TEST(
|
||||||
WorkerProtoTest,
|
WorkerProtoTest,
|
||||||
realisation_with_deps,
|
realisation_1_39,
|
||||||
"realisation-with-deps",
|
"realisation-1.39",
|
||||||
defaultVersion,
|
1 << 8 | 39,
|
||||||
(std::tuple<Realisation>{
|
(Realisation{
|
||||||
Realisation{
|
UnkeyedRealisation{
|
||||||
{
|
.outPath = StorePath{"g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-foo"},
|
||||||
.outPath = StorePath{"g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-foo"},
|
.signatures = {"asdf", "qwer"},
|
||||||
.signatures = {"asdf", "qwer"},
|
},
|
||||||
},
|
{
|
||||||
{
|
.drvPath = StorePath{"g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-foo.drv"},
|
||||||
.drvHash = Hash::parseSRI("sha256-FePFYIlMuycIXPZbWi7LGEiMmZSX9FMbaQenWBzm1Sc="),
|
.outputName = "baz",
|
||||||
.outputName = "baz",
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
}))
|
}))
|
||||||
|
|
||||||
|
|
@ -205,7 +186,10 @@ VERSIONED_CHARACTERIZATION_TEST(WorkerProtoTest, buildResult_1_27, "build-result
|
||||||
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, buildResult_1_28, "build-result-1.28", 1 << 8 | 28, ({
|
WorkerProtoTest, buildResult_1_28, "build-result-1.28", 1 << 8 | 28, ({
|
||||||
using namespace std::literals::chrono_literals;
|
using namespace std::literals::chrono_literals;
|
||||||
std::tuple<BuildResult, BuildResult, BuildResult> t{
|
std::tuple<BuildResult, BuildResult, BuildResult> t{
|
||||||
|
|
@ -224,25 +208,13 @@ VERSIONED_CHARACTERIZATION_TEST(
|
||||||
{
|
{
|
||||||
"foo",
|
"foo",
|
||||||
{
|
{
|
||||||
{
|
.outPath = StorePath{"g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-foo"},
|
||||||
.outPath = StorePath{"g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-foo"},
|
|
||||||
},
|
|
||||||
DrvOutput{
|
|
||||||
.drvHash = Hash::parseSRI("sha256-b4afnqKCO9oWXgYHb9DeQ2berSwOjS27rSd9TxXDc/U="),
|
|
||||||
.outputName = "foo",
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"bar",
|
"bar",
|
||||||
{
|
{
|
||||||
{
|
.outPath = StorePath{"g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-bar"},
|
||||||
.outPath = StorePath{"g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-bar"},
|
|
||||||
},
|
|
||||||
DrvOutput{
|
|
||||||
.drvHash = Hash::parseSRI("sha256-b4afnqKCO9oWXgYHb9DeQ2berSwOjS27rSd9TxXDc/U="),
|
|
||||||
.outputName = "bar",
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
@ -251,7 +223,8 @@ VERSIONED_CHARACTERIZATION_TEST(
|
||||||
t;
|
t;
|
||||||
}))
|
}))
|
||||||
|
|
||||||
VERSIONED_CHARACTERIZATION_TEST(
|
// See above note
|
||||||
|
VERSIONED_READ_CHARACTERIZATION_TEST(
|
||||||
WorkerProtoTest, buildResult_1_29, "build-result-1.29", 1 << 8 | 29, ({
|
WorkerProtoTest, buildResult_1_29, "build-result-1.29", 1 << 8 | 29, ({
|
||||||
using namespace std::literals::chrono_literals;
|
using namespace std::literals::chrono_literals;
|
||||||
std::tuple<BuildResult, BuildResult, BuildResult> t{
|
std::tuple<BuildResult, BuildResult, BuildResult> t{
|
||||||
|
|
@ -277,27 +250,13 @@ VERSIONED_CHARACTERIZATION_TEST(
|
||||||
{
|
{
|
||||||
"foo",
|
"foo",
|
||||||
{
|
{
|
||||||
{
|
.outPath = StorePath{"g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-foo"},
|
||||||
.outPath = StorePath{"g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-foo"},
|
|
||||||
},
|
|
||||||
DrvOutput{
|
|
||||||
.drvHash =
|
|
||||||
Hash::parseSRI("sha256-b4afnqKCO9oWXgYHb9DeQ2berSwOjS27rSd9TxXDc/U="),
|
|
||||||
.outputName = "foo",
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"bar",
|
"bar",
|
||||||
{
|
{
|
||||||
{
|
.outPath = StorePath{"g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-bar"},
|
||||||
.outPath = StorePath{"g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-bar"},
|
|
||||||
},
|
|
||||||
DrvOutput{
|
|
||||||
.drvHash =
|
|
||||||
Hash::parseSRI("sha256-b4afnqKCO9oWXgYHb9DeQ2berSwOjS27rSd9TxXDc/U="),
|
|
||||||
.outputName = "bar",
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
@ -310,7 +269,8 @@ VERSIONED_CHARACTERIZATION_TEST(
|
||||||
t;
|
t;
|
||||||
}))
|
}))
|
||||||
|
|
||||||
VERSIONED_CHARACTERIZATION_TEST(
|
// See above note
|
||||||
|
VERSIONED_READ_CHARACTERIZATION_TEST(
|
||||||
WorkerProtoTest, buildResult_1_37, "build-result-1.37", 1 << 8 | 37, ({
|
WorkerProtoTest, buildResult_1_37, "build-result-1.37", 1 << 8 | 37, ({
|
||||||
using namespace std::literals::chrono_literals;
|
using namespace std::literals::chrono_literals;
|
||||||
std::tuple<BuildResult, BuildResult, BuildResult> t{
|
std::tuple<BuildResult, BuildResult, BuildResult> t{
|
||||||
|
|
@ -336,27 +296,60 @@ VERSIONED_CHARACTERIZATION_TEST(
|
||||||
{
|
{
|
||||||
"foo",
|
"foo",
|
||||||
{
|
{
|
||||||
{
|
.outPath = StorePath{"g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-foo"},
|
||||||
.outPath = StorePath{"g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-foo"},
|
|
||||||
},
|
|
||||||
DrvOutput{
|
|
||||||
.drvHash =
|
|
||||||
Hash::parseSRI("sha256-b4afnqKCO9oWXgYHb9DeQ2berSwOjS27rSd9TxXDc/U="),
|
|
||||||
.outputName = "foo",
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"bar",
|
"bar",
|
||||||
{
|
{
|
||||||
{
|
.outPath = StorePath{"g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-bar"},
|
||||||
.outPath = StorePath{"g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-bar"},
|
},
|
||||||
},
|
},
|
||||||
DrvOutput{
|
},
|
||||||
.drvHash =
|
}},
|
||||||
Hash::parseSRI("sha256-b4afnqKCO9oWXgYHb9DeQ2berSwOjS27rSd9TxXDc/U="),
|
.timesBuilt = 1,
|
||||||
.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{.inner{BuildResult::Failure{
|
||||||
|
.status = BuildResult::Failure::OutputRejected,
|
||||||
|
.errorMsg = "no idea why",
|
||||||
|
}}},
|
||||||
|
BuildResult{
|
||||||
|
.inner{BuildResult::Failure{
|
||||||
|
.status = BuildResult::Failure::NotDeterministic,
|
||||||
|
.errorMsg = "no idea why",
|
||||||
|
.isNonDeterministic = true,
|
||||||
|
}},
|
||||||
|
.timesBuilt = 3,
|
||||||
|
.startTime = 30,
|
||||||
|
.stopTime = 50,
|
||||||
|
},
|
||||||
|
BuildResult{
|
||||||
|
.inner{BuildResult::Success{
|
||||||
|
.status = BuildResult::Success::Built,
|
||||||
|
.builtOutputs =
|
||||||
|
{
|
||||||
|
{
|
||||||
|
"foo",
|
||||||
|
{
|
||||||
|
.outPath = StorePath{"g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-foo"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"bar",
|
||||||
|
{
|
||||||
|
.outPath = StorePath{"g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-bar"},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -514,7 +514,7 @@ StorePath BinaryCacheStore::addToStore(
|
||||||
|
|
||||||
std::string BinaryCacheStore::makeRealisationPath(const DrvOutput & id)
|
std::string BinaryCacheStore::makeRealisationPath(const DrvOutput & id)
|
||||||
{
|
{
|
||||||
return realisationsPrefix + "/" + id.to_string() + ".doi";
|
return realisationsPrefix + "/" + id.drvPath.to_string() + "/" + id.outputName + ".doi";
|
||||||
}
|
}
|
||||||
|
|
||||||
void BinaryCacheStore::queryRealisationUncached(
|
void BinaryCacheStore::queryRealisationUncached(
|
||||||
|
|
@ -535,7 +535,10 @@ void BinaryCacheStore::queryRealisationUncached(
|
||||||
realisation = std::make_shared<const UnkeyedRealisation>(nlohmann::json::parse(*data));
|
realisation = std::make_shared<const UnkeyedRealisation>(nlohmann::json::parse(*data));
|
||||||
} catch (Error & e) {
|
} catch (Error & e) {
|
||||||
e.addTrace(
|
e.addTrace(
|
||||||
{}, "while parsing file '%s' as a realisation for key '%s'", outputInfoFilePath, id.to_string());
|
{},
|
||||||
|
"while parsing file '%s' as a build trace value for key '%s'",
|
||||||
|
outputInfoFilePath,
|
||||||
|
id.to_string());
|
||||||
throw;
|
throw;
|
||||||
}
|
}
|
||||||
return (*callbackPtr)(std::move(realisation));
|
return (*callbackPtr)(std::move(realisation));
|
||||||
|
|
@ -551,7 +554,10 @@ void BinaryCacheStore::registerDrvOutput(const Realisation & info)
|
||||||
{
|
{
|
||||||
if (diskCache)
|
if (diskCache)
|
||||||
diskCache->upsertRealisation(config.getReference().render(/*FIXME withParams=*/false), info);
|
diskCache->upsertRealisation(config.getReference().render(/*FIXME withParams=*/false), info);
|
||||||
upsertFile(makeRealisationPath(info.id), static_cast<nlohmann::json>(info).dump(), "application/json");
|
upsertFile(
|
||||||
|
makeRealisationPath(info.id),
|
||||||
|
static_cast<nlohmann::json>(static_cast<const UnkeyedRealisation &>(info)).dump(),
|
||||||
|
"application/json");
|
||||||
}
|
}
|
||||||
|
|
||||||
ref<RemoteFSAccessor> BinaryCacheStore::getRemoteFSAccessor(bool requireValidPath)
|
ref<RemoteFSAccessor> BinaryCacheStore::getRemoteFSAccessor(bool requireValidPath)
|
||||||
|
|
|
||||||
|
|
@ -212,9 +212,8 @@ Goal::Co DerivationBuildingGoal::tryToBuild()
|
||||||
given this information by the downstream goal, that cannot happen
|
given this information by the downstream goal, that cannot happen
|
||||||
anymore if the downstream goal only cares about one output, but
|
anymore if the downstream goal only cares about one output, but
|
||||||
we care about all outputs. */
|
we care about all outputs. */
|
||||||
auto outputHashes = staticOutputHashes(worker.evalStore, *drv);
|
for (auto & [outputName, _] : drv->outputs) {
|
||||||
for (auto & [outputName, outputHash] : outputHashes) {
|
InitialOutput v;
|
||||||
InitialOutput v{.outputHash = outputHash};
|
|
||||||
|
|
||||||
/* TODO we might want to also allow randomizing the paths
|
/* TODO we might want to also allow randomizing the paths
|
||||||
for regular CA derivations, e.g. for sake of checking
|
for regular CA derivations, e.g. for sake of checking
|
||||||
|
|
@ -1106,7 +1105,7 @@ DerivationBuildingGoal::checkPathValidity(std::map<std::string, InitialOutput> &
|
||||||
: 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 = {
|
||||||
|
|
|
||||||
|
|
@ -36,12 +36,6 @@ DerivationGoal::DerivationGoal(
|
||||||
, drvPath(drvPath)
|
, drvPath(drvPath)
|
||||||
, wantedOutput(wantedOutput)
|
, wantedOutput(wantedOutput)
|
||||||
, drv{std::make_unique<Derivation>(drv)}
|
, drv{std::make_unique<Derivation>(drv)}
|
||||||
, outputHash{[&] {
|
|
||||||
auto outputHashes = staticOutputHashes(worker.evalStore, drv);
|
|
||||||
if (auto * mOutputHash = get(outputHashes, wantedOutput))
|
|
||||||
return *mOutputHash;
|
|
||||||
throw Error("derivation '%s' does not have output '%s'", worker.store.printStorePath(drvPath), wantedOutput);
|
|
||||||
}()}
|
|
||||||
, buildMode(buildMode)
|
, buildMode(buildMode)
|
||||||
{
|
{
|
||||||
|
|
||||||
|
|
@ -100,7 +94,7 @@ Goal::Co DerivationGoal::haveDerivation(bool storeDerivation)
|
||||||
them. */
|
them. */
|
||||||
if (settings.useSubstitutes && drvOptions.substitutesAllowed()) {
|
if (settings.useSubstitutes && drvOptions.substitutesAllowed()) {
|
||||||
if (!checkResult)
|
if (!checkResult)
|
||||||
waitees.insert(upcast_goal(worker.makeDrvOutputSubstitutionGoal(DrvOutput{outputHash, wantedOutput})));
|
waitees.insert(upcast_goal(worker.makeDrvOutputSubstitutionGoal(DrvOutput{drvPath, wantedOutput})));
|
||||||
else {
|
else {
|
||||||
auto * cap = getDerivationCA(*drv);
|
auto * cap = getDerivationCA(*drv);
|
||||||
waitees.insert(upcast_goal(worker.makePathSubstitutionGoal(
|
waitees.insert(upcast_goal(worker.makePathSubstitutionGoal(
|
||||||
|
|
@ -167,12 +161,7 @@ Goal::Co DerivationGoal::haveDerivation(bool storeDerivation)
|
||||||
// No `std::visit` for coroutines yet
|
// No `std::visit` for coroutines yet
|
||||||
if (auto * successP = resolvedResult.tryGetSuccess()) {
|
if (auto * successP = resolvedResult.tryGetSuccess()) {
|
||||||
auto & success = *successP;
|
auto & success = *successP;
|
||||||
auto outputHashes = staticOutputHashes(worker.evalStore, *drv);
|
if (!drv->outputs.contains(wantedOutput))
|
||||||
auto resolvedHashes = staticOutputHashes(worker.store, drvResolved);
|
|
||||||
|
|
||||||
auto outputHash = get(outputHashes, wantedOutput);
|
|
||||||
auto resolvedHash = get(resolvedHashes, wantedOutput);
|
|
||||||
if ((!outputHash) || (!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),
|
worker.store.printStorePath(drvPath),
|
||||||
|
|
@ -181,7 +170,7 @@ Goal::Co DerivationGoal::haveDerivation(bool storeDerivation)
|
||||||
auto realisation = [&] {
|
auto realisation = [&] {
|
||||||
auto take1 = get(success.builtOutputs, wantedOutput);
|
auto take1 = get(success.builtOutputs, wantedOutput);
|
||||||
if (take1)
|
if (take1)
|
||||||
return static_cast<UnkeyedRealisation>(*take1);
|
return *take1;
|
||||||
|
|
||||||
/* The above `get` should work. But stateful 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
|
||||||
|
|
@ -189,7 +178,7 @@ Goal::Co DerivationGoal::haveDerivation(bool storeDerivation)
|
||||||
check the store directly if it fails. */
|
check the store directly if it fails. */
|
||||||
auto take2 = worker.evalStore.queryRealisation(
|
auto take2 = worker.evalStore.queryRealisation(
|
||||||
DrvOutput{
|
DrvOutput{
|
||||||
.drvHash = *resolvedHash,
|
.drvPath = pathResolved,
|
||||||
.outputName = wantedOutput,
|
.outputName = wantedOutput,
|
||||||
});
|
});
|
||||||
if (take2)
|
if (take2)
|
||||||
|
|
@ -205,7 +194,7 @@ Goal::Co DerivationGoal::haveDerivation(bool storeDerivation)
|
||||||
Realisation newRealisation{
|
Realisation newRealisation{
|
||||||
realisation,
|
realisation,
|
||||||
{
|
{
|
||||||
.drvHash = *outputHash,
|
.drvPath = drvPath,
|
||||||
.outputName = wantedOutput,
|
.outputName = wantedOutput,
|
||||||
}};
|
}};
|
||||||
newRealisation.signatures.clear();
|
newRealisation.signatures.clear();
|
||||||
|
|
@ -251,16 +240,7 @@ Goal::Co DerivationGoal::haveDerivation(bool storeDerivation)
|
||||||
/* In checking mode, the builder will not register any outputs.
|
/* In checking mode, the builder will not register any outputs.
|
||||||
So we want to make sure the ones that we wanted to check are
|
So we want to make sure the ones that we wanted to check are
|
||||||
properly there. */
|
properly there. */
|
||||||
success.builtOutputs = {{
|
success.builtOutputs = {{wantedOutput, assertPathValidity()}};
|
||||||
wantedOutput,
|
|
||||||
{
|
|
||||||
assertPathValidity(),
|
|
||||||
{
|
|
||||||
.drvHash = outputHash,
|
|
||||||
.outputName = wantedOutput,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}};
|
|
||||||
} else {
|
} else {
|
||||||
/* Otherwise the builder will give us info for out output, but
|
/* Otherwise the builder will give us info for out output, but
|
||||||
also for other outputs. Filter down to just our output so as
|
also for other outputs. Filter down to just our output so as
|
||||||
|
|
@ -385,7 +365,7 @@ std::optional<std::pair<UnkeyedRealisation, PathStatus>> DerivationGoal::checkPa
|
||||||
if (drv->type().isImpure())
|
if (drv->type().isImpure())
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
|
|
||||||
auto drvOutput = DrvOutput{outputHash, wantedOutput};
|
auto drvOutput = DrvOutput{drvPath, wantedOutput};
|
||||||
|
|
||||||
std::optional<UnkeyedRealisation> mRealisation;
|
std::optional<UnkeyedRealisation> mRealisation;
|
||||||
|
|
||||||
|
|
@ -425,7 +405,7 @@ std::optional<std::pair<UnkeyedRealisation, PathStatus>> DerivationGoal::checkPa
|
||||||
Realisation{
|
Realisation{
|
||||||
*mRealisation,
|
*mRealisation,
|
||||||
{
|
{
|
||||||
.drvHash = outputHash,
|
.drvPath = drvPath,
|
||||||
.outputName = wantedOutput,
|
.outputName = wantedOutput,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
@ -448,16 +428,7 @@ Goal::Done DerivationGoal::doneSuccess(BuildResult::Success::Status status, Unke
|
||||||
{
|
{
|
||||||
buildResult.inner = BuildResult::Success{
|
buildResult.inner = BuildResult::Success{
|
||||||
.status = status,
|
.status = status,
|
||||||
.builtOutputs = {{
|
.builtOutputs = {{wantedOutput, std::move(builtOutput)}},
|
||||||
wantedOutput,
|
|
||||||
{
|
|
||||||
std::move(builtOutput),
|
|
||||||
DrvOutput{
|
|
||||||
.drvHash = outputHash,
|
|
||||||
.outputName = wantedOutput,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
mcExpectedBuilds.reset();
|
mcExpectedBuilds.reset();
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@ DrvOutputSubstitutionGoal::DrvOutputSubstitutionGoal(const DrvOutput & id, Worke
|
||||||
: 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");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -107,7 +107,8 @@ Goal::Co DrvOutputSubstitutionGoal::init()
|
||||||
|
|
||||||
/* None left. Terminate this goal and let someone else deal
|
/* None left. Terminate this goal and let someone else deal
|
||||||
with it. */
|
with it. */
|
||||||
debug("derivation output '%s' is required, but there is no substituter that can provide it", id.to_string());
|
debug(
|
||||||
|
"derivation output '%s' is required, but there is no substituter that can provide it", id.render(worker.store));
|
||||||
|
|
||||||
if (substituterFailed) {
|
if (substituterFailed) {
|
||||||
worker.failedSubstitutions++;
|
worker.failedSubstitutions++;
|
||||||
|
|
@ -122,7 +123,7 @@ Goal::Co DrvOutputSubstitutionGoal::init()
|
||||||
|
|
||||||
std::string DrvOutputSubstitutionGoal::key()
|
std::string DrvOutputSubstitutionGoal::key()
|
||||||
{
|
{
|
||||||
return "a$" + std::string(id.to_string());
|
return "a$" + std::string(id.render(worker.store));
|
||||||
}
|
}
|
||||||
|
|
||||||
void DrvOutputSubstitutionGoal::handleEOF(Descriptor fd)
|
void DrvOutputSubstitutionGoal::handleEOF(Descriptor fd)
|
||||||
|
|
|
||||||
|
|
@ -46,34 +46,6 @@ void CommonProto::Serialise<ContentAddress>::write(
|
||||||
conn.to << renderContentAddress(ca);
|
conn.to << renderContentAddress(ca);
|
||||||
}
|
}
|
||||||
|
|
||||||
Realisation CommonProto::Serialise<Realisation>::read(const StoreDirConfig & store, CommonProto::ReadConn conn)
|
|
||||||
{
|
|
||||||
std::string rawInput = readString(conn.from);
|
|
||||||
try {
|
|
||||||
return nlohmann::json::parse(rawInput);
|
|
||||||
} catch (Error & e) {
|
|
||||||
e.addTrace({}, "while parsing a realisation object in the remote protocol");
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void CommonProto::Serialise<Realisation>::write(
|
|
||||||
const StoreDirConfig & store, CommonProto::WriteConn conn, const Realisation & realisation)
|
|
||||||
{
|
|
||||||
conn.to << static_cast<nlohmann::json>(realisation).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>
|
std::optional<StorePath>
|
||||||
CommonProto::Serialise<std::optional<StorePath>>::read(const StoreDirConfig & store, CommonProto::ReadConn conn)
|
CommonProto::Serialise<std::optional<StorePath>>::read(const StoreDirConfig & store, CommonProto::ReadConn conn)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -963,33 +963,31 @@ static void performOp(
|
||||||
|
|
||||||
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{{.outPath = outputPath}, outputId});
|
//}
|
||||||
} else {
|
auto realisation = WorkerProto::Serialise<Realisation>::read(*store, rconn);
|
||||||
auto realisation = WorkerProto::Serialise<Realisation>::read(*store, rconn);
|
store->registerDrvOutput(realisation);
|
||||||
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)
|
if (info)
|
||||||
outPaths.insert(info->outPath);
|
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, outputId});
|
|
||||||
WorkerProto::write(*store, wconn, realisations);
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -866,13 +866,14 @@ 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 DrvHashModulo pathDerivationModulo(Store & store, const StorePath & drvPath)
|
||||||
{
|
{
|
||||||
std::optional<DrvHash> hash;
|
std::optional<DrvHashModulo> hash;
|
||||||
if (drvHashes.cvisit(drvPath, [&hash](const auto & kv) { hash.emplace(kv.second); })) {
|
if (drvHashes.cvisit(drvPath, [&hash](const auto & kv) { hash.emplace(kv.second); })) {
|
||||||
return *hash;
|
return *hash;
|
||||||
}
|
}
|
||||||
auto h = hashDerivationModulo(store, store.readInvalidDerivation(drvPath), false);
|
auto h = hashDerivationModulo(store, store.readInvalidDerivation(drvPath), false);
|
||||||
|
|
||||||
// Cache it
|
// Cache it
|
||||||
drvHashes.insert_or_assign(drvPath, h);
|
drvHashes.insert_or_assign(drvPath, h);
|
||||||
return h;
|
return h;
|
||||||
|
|
@ -895,12 +896,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);
|
||||||
|
|
@ -910,54 +909,66 @@ 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(
|
if (std::visit(
|
||||||
overloaded{
|
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 ? DrvHash::Kind::Regular : DrvHash::Kind::Deferred;
|
// Already covered
|
||||||
},
|
assert(!ca.fixed);
|
||||||
[](const DerivationType::Impure &) -> DrvHash::Kind { return DrvHash::Kind::Deferred; }},
|
return true;
|
||||||
drv.type().raw);
|
},
|
||||||
|
[](const DerivationType::Impure &) { return true; }},
|
||||||
|
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(
|
||||||
kind = DrvHash::Kind::Deferred;
|
overloaded{
|
||||||
for (auto & outputName : node.value) {
|
[&](const DrvHashModulo::DeferredDrv &) { return true; },
|
||||||
const auto h = get(res.hashes, outputName);
|
// Regular non-CA derivation, replace derivation
|
||||||
if (!h)
|
[&](const DrvHashModulo::DrvHash & drvHash) {
|
||||||
throw Error("no hash for output '%s' of derivation '%s'", outputName, drv.name);
|
inputs2.insert_or_assign(drvHash.to_string(HashFormat::Base16, false), node);
|
||||||
inputs2[h->to_string(HashFormat::Base16, false)].value.insert(outputName);
|
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static DerivationOutput readDerivationOutput(Source & in, const StoreDirConfig & store)
|
static DerivationOutput readDerivationOutput(Source & in, const StoreDirConfig & store)
|
||||||
|
|
@ -1107,22 +1118,39 @@ void BasicDerivation::applyRewrites(const StringMap & rewrites)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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(
|
||||||
if (!h)
|
overloaded{
|
||||||
throw Error(
|
[&](const DrvHashModulo::DrvHash & drvHash) {
|
||||||
"derivation '%s' output '%s' has no hash (derivations.cc/rewriteDerivation)", drv.name, outputName);
|
auto outPath = store.makeOutputPath(outputName, drvHash, drv.name);
|
||||||
auto outPath = store.makeOutputPath(outputName, *h, drv.name);
|
drv.env.insert_or_assign(outputName, store.printStorePath(outPath));
|
||||||
drv.env[outputName] = store.printStorePath(outPath);
|
output = DerivationOutput::InputAddressed{
|
||||||
output = DerivationOutput::InputAddressed{
|
.path = std::move(outPath),
|
||||||
.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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1205,9 +1233,13 @@ std::optional<BasicDerivation> Derivation::tryResolve(
|
||||||
queryResolutionChain))
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Derivation::checkInvariants(Store & store, const StorePath & drvPath) const
|
void Derivation::checkInvariants(Store & store, const StorePath & drvPath) const
|
||||||
|
|
@ -1235,46 +1267,79 @@ 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(
|
std::visit(
|
||||||
overloaded{
|
overloaded{
|
||||||
[&](const DerivationOutput::InputAddressed & doia) {
|
[&](const DerivationOutput::InputAddressed & doia) {
|
||||||
if (!hashesModulo) {
|
std::visit(
|
||||||
// somewhat expensive so we do lazily
|
overloaded{
|
||||||
hashesModulo = hashDerivationModulo(store, *this, true);
|
[&](const DrvHashModulo::DrvHash & drvHash) {
|
||||||
}
|
StorePath recomputed = store.makeOutputPath(outputName, drvHash, drvName);
|
||||||
auto currentOutputHash = get(hashesModulo->hashes, i.first);
|
if (doia.path != recomputed)
|
||||||
if (!currentOutputHash)
|
throw Error(
|
||||||
throw Error(
|
"derivation '%s' has incorrect output '%s', should be '%s'",
|
||||||
"derivation '%s' has unexpected output '%s' (local-store / hashesModulo) named '%s'",
|
store.printStorePath(drvPath),
|
||||||
store.printStorePath(drvPath),
|
store.printStorePath(doia.path),
|
||||||
store.printStorePath(doia.path),
|
store.printStorePath(recomputed));
|
||||||
i.first);
|
},
|
||||||
StorePath recomputed = store.makeOutputPath(i.first, *currentOutputHash, drvName);
|
[&](const DrvHashModulo::CaOutputHashes &) {
|
||||||
if (doia.path != recomputed)
|
/* Shouldn't happen as the original output is
|
||||||
throw Error(
|
input-addressed. */
|
||||||
"derivation '%s' has incorrect output '%s', should be '%s'",
|
assert(false);
|
||||||
store.printStorePath(drvPath),
|
},
|
||||||
store.printStorePath(doia.path),
|
[&](const DrvHashModulo::DeferredDrv &) {
|
||||||
store.printStorePath(recomputed));
|
throw Error(
|
||||||
envHasRightPath(doia.path, i.first);
|
"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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -332,7 +332,7 @@ struct DummyStoreImpl : DummyStore
|
||||||
void registerDrvOutput(const Realisation & output) override
|
void registerDrvOutput(const Realisation & output) override
|
||||||
{
|
{
|
||||||
auto ref = make_ref<UnkeyedRealisation>(output);
|
auto ref = make_ref<UnkeyedRealisation>(output);
|
||||||
buildTrace.insert_or_visit({output.id.drvHash, {{output.id.outputName, ref}}}, [&](auto & kv) {
|
buildTrace.insert_or_visit({output.id.drvPath, {{output.id.outputName, ref}}}, [&](auto & kv) {
|
||||||
kv.second.insert_or_assign(output.id.outputName, make_ref<UnkeyedRealisation>(output));
|
kv.second.insert_or_assign(output.id.outputName, make_ref<UnkeyedRealisation>(output));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -341,7 +341,7 @@ struct DummyStoreImpl : DummyStore
|
||||||
const DrvOutput & drvOutput, Callback<std::shared_ptr<const UnkeyedRealisation>> callback) noexcept override
|
const DrvOutput & drvOutput, Callback<std::shared_ptr<const UnkeyedRealisation>> callback) noexcept override
|
||||||
{
|
{
|
||||||
bool visited = false;
|
bool visited = false;
|
||||||
buildTrace.cvisit(drvOutput.drvHash, [&](const auto & kv) {
|
buildTrace.cvisit(drvOutput.drvPath, [&](const auto & kv) {
|
||||||
if (auto it = kv.second.find(drvOutput.outputName); it != kv.second.end()) {
|
if (auto it = kv.second.find(drvOutput.outputName); it != kv.second.end()) {
|
||||||
visited = true;
|
visited = true;
|
||||||
callback(it->second.get_ptr());
|
callback(it->second.get_ptr());
|
||||||
|
|
|
||||||
|
|
@ -82,8 +82,13 @@ protected:
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The prefix under which realisation infos will be stored
|
* 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 = "realisations";
|
constexpr const static std::string realisationsPrefix = "build-trace";
|
||||||
|
|
||||||
constexpr const static std::string cacheInfoFile = "nix-cache-info";
|
constexpr const static std::string cacheInfoFile = "nix-cache-info";
|
||||||
|
|
||||||
|
|
@ -92,7 +97,7 @@ protected:
|
||||||
/**
|
/**
|
||||||
* Compute the path to the given realisation
|
* Compute the path to the given realisation
|
||||||
*
|
*
|
||||||
* It's `${realisationsPrefix}/${drvOutput}.doi`.
|
* It's `${realisationsPrefix}/${drvPath}/${outputName}`.
|
||||||
*/
|
*/
|
||||||
std::string makeRealisationPath(const DrvOutput & id);
|
std::string makeRealisationPath(const DrvOutput & id);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -45,7 +45,6 @@ struct InitialOutputStatus
|
||||||
|
|
||||||
struct InitialOutput
|
struct InitialOutput
|
||||||
{
|
{
|
||||||
Hash outputHash;
|
|
||||||
std::optional<InitialOutputStatus> known;
|
std::optional<InitialOutputStatus> known;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -71,8 +71,6 @@ private:
|
||||||
*/
|
*/
|
||||||
std::unique_ptr<Derivation> drv;
|
std::unique_ptr<Derivation> drv;
|
||||||
|
|
||||||
const Hash outputHash;
|
|
||||||
|
|
||||||
const BuildMode buildMode;
|
const BuildMode buildMode;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -26,12 +26,13 @@ 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(template<typename K COMMA_ typename V>, std::map<K COMMA_ V>)
|
COMMON_USE_LENGTH_PREFIX_SERIALISER(
|
||||||
|
template<typename K COMMA_ typename V COMMA_ typename Compare>, std::map<K COMMA_ V COMMA_ Compare>)
|
||||||
#undef COMMA_
|
#undef COMMA_
|
||||||
|
|
||||||
/* protocol-specific templates */
|
/* protocol-specific templates */
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,6 @@ struct Source;
|
||||||
class StorePath;
|
class StorePath;
|
||||||
struct ContentAddress;
|
struct ContentAddress;
|
||||||
struct DrvOutput;
|
struct DrvOutput;
|
||||||
struct Realisation;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Shared serializers between the worker protocol, serve protocol, and a
|
* Shared serializers between the worker protocol, serve protocol, and a
|
||||||
|
|
@ -70,8 +69,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>
|
||||||
|
|
@ -81,8 +78,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_
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -427,34 +427,39 @@ 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 {
|
|
||||||
/**
|
|
||||||
* Statically determined derivations.
|
|
||||||
* This hash will be directly used to compute the output paths
|
|
||||||
*/
|
|
||||||
Regular,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Floating-output derivations (and their reverse dependencies).
|
|
||||||
*/
|
|
||||||
Deferred,
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The kind of derivation this is, simplified for just "derivation hash
|
* Known CA drv's output hashes, for fixed-output derivations whose
|
||||||
* modulo" purposes.
|
* output hashes are always known since they are fixed up-front.
|
||||||
*/
|
*/
|
||||||
Kind kind;
|
using CaOutputHashes = std::map<std::string, Hash>;
|
||||||
};
|
|
||||||
|
|
||||||
void operator|=(DrvHash::Kind & self, const DrvHash::Kind & other) noexcept;
|
/**
|
||||||
|
* This derivation doesn't yet have known output hashes.
|
||||||
|
*
|
||||||
|
* Either because itself is floating CA, or it (transtively) depends
|
||||||
|
* on a floating CA derivation.
|
||||||
|
*/
|
||||||
|
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);
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns hashes with the details of fixed-output subderivations
|
* Returns hashes with the details of fixed-output subderivations
|
||||||
|
|
@ -480,15 +485,17 @@ 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);
|
||||||
|
|
||||||
struct DrvHashFct
|
struct DrvHashFct
|
||||||
{
|
{
|
||||||
|
|
@ -503,7 +510,7 @@ struct DrvHashFct
|
||||||
/**
|
/**
|
||||||
* Memoisation of hashDerivationModulo().
|
* Memoisation of hashDerivationModulo().
|
||||||
*/
|
*/
|
||||||
typedef boost::concurrent_flat_map<StorePath, DrvHash, DrvHashFct> DrvHashes;
|
typedef boost::concurrent_flat_map<StorePath, DrvHashModulo, DrvHashFct> DrvHashes;
|
||||||
|
|
||||||
// FIXME: global, though at least thread-safe.
|
// FIXME: global, though at least thread-safe.
|
||||||
extern DrvHashes drvHashes;
|
extern DrvHashes drvHashes;
|
||||||
|
|
|
||||||
|
|
@ -49,7 +49,7 @@ struct DummyStore : virtual Store
|
||||||
* outer map for the derivation, and inner maps for the outputs of a
|
* outer map for the derivation, and inner maps for the outputs of a
|
||||||
* given derivation.
|
* given derivation.
|
||||||
*/
|
*/
|
||||||
boost::concurrent_flat_map<Hash, std::map<std::string, ref<UnkeyedRealisation>>> buildTrace;
|
boost::concurrent_flat_map<StorePath, std::map<std::string, ref<UnkeyedRealisation>>> buildTrace;
|
||||||
|
|
||||||
DummyStore(ref<const Config> config)
|
DummyStore(ref<const Config> config)
|
||||||
: Store{*config}
|
: Store{*config}
|
||||||
|
|
|
||||||
|
|
@ -56,14 +56,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>
|
||||||
|
|
@ -109,11 +109,11 @@ 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, Compare>>::read(
|
||||||
LengthPrefixedProtoHelper<Inner, std::map<K, V>>::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);
|
||||||
|
|
@ -123,9 +123,9 @@ LengthPrefixedProtoHelper<Inner, std::map<K, V>>::read(const StoreDirConfig & st
|
||||||
return resMap;
|
return resMap;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class Inner, typename K, typename V>
|
template<class Inner, typename K, typename V, typename Compare>
|
||||||
void LengthPrefixedProtoHelper<Inner, std::map<K, V>>::write(
|
void 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) {
|
||||||
|
|
|
||||||
|
|
@ -18,33 +18,40 @@ 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
|
/**
|
||||||
{
|
* Skips the store dir on the `drvPath`
|
||||||
return drvHash.to_string(HashFormat::Base16, true);
|
*/
|
||||||
}
|
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;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Includes the store dir on `drvPath`
|
||||||
|
*/
|
||||||
|
static DrvOutput parse(const StoreDirConfig & store, std::string_view);
|
||||||
|
|
||||||
bool operator==(const DrvOutput &) const = default;
|
bool operator==(const DrvOutput &) const = default;
|
||||||
auto operator<=>(const DrvOutput &) const = default;
|
auto operator<=>(const DrvOutput &) const = default;
|
||||||
|
|
@ -64,6 +71,16 @@ struct UnkeyedRealisation
|
||||||
|
|
||||||
size_t checkSignatures(const DrvOutput & key, const PublicKeys & publicKeys) const;
|
size_t checkSignatures(const DrvOutput & key, const PublicKeys & publicKeys) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Just check the `outPath`. Signatures don't matter for this.
|
||||||
|
* Callers must ensure that the corresponding key is the same for
|
||||||
|
* most use-cases.
|
||||||
|
*/
|
||||||
|
bool isCompatibleWith(const UnkeyedRealisation & other) const
|
||||||
|
{
|
||||||
|
return outPath == other.outPath;
|
||||||
|
}
|
||||||
|
|
||||||
const StorePath & getPath() const
|
const StorePath & getPath() const
|
||||||
{
|
{
|
||||||
return outPath;
|
return outPath;
|
||||||
|
|
@ -77,8 +94,6 @@ struct Realisation : UnkeyedRealisation
|
||||||
{
|
{
|
||||||
DrvOutput id;
|
DrvOutput id;
|
||||||
|
|
||||||
bool isCompatibleWith(const UnkeyedRealisation & other) const;
|
|
||||||
|
|
||||||
bool operator==(const Realisation &) const = default;
|
bool operator==(const Realisation &) const = default;
|
||||||
auto operator<=>(const Realisation &) const = default;
|
auto operator<=>(const Realisation &) const = default;
|
||||||
};
|
};
|
||||||
|
|
@ -89,16 +104,7 @@ struct Realisation : UnkeyedRealisation
|
||||||
* 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;
|
|
||||||
|
|
||||||
struct OpaquePath
|
struct OpaquePath
|
||||||
{
|
{
|
||||||
|
|
@ -149,19 +155,17 @@ struct RealisedPath
|
||||||
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)
|
MissingRealisation(const StoreDirConfig & store, const StorePath & drvPath, const OutputName & outputName);
|
||||||
: Error(
|
MissingRealisation(
|
||||||
"cannot operate on output '%s' of the "
|
const StoreDirConfig & store,
|
||||||
"unbuilt derivation '%s'",
|
const SingleDerivedPath & drvPath,
|
||||||
outputName,
|
const StorePath & drvPathResolved,
|
||||||
drv)
|
const OutputName & outputName);
|
||||||
{
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace nix
|
} // namespace nix
|
||||||
|
|
|
||||||
|
|
@ -34,8 +34,10 @@ 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
|
||||||
std::map<K SERVE_USE_LENGTH_PREFIX_SERIALISER_COMMA V>)
|
typename Compare>
|
||||||
|
,
|
||||||
|
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)
|
||||||
|
|
||||||
|
|
@ -18,6 +18,9 @@ struct Source;
|
||||||
// items being serialised
|
// items being serialised
|
||||||
struct BuildResult;
|
struct BuildResult;
|
||||||
struct UnkeyedValidPathInfo;
|
struct UnkeyedValidPathInfo;
|
||||||
|
struct DrvOutput;
|
||||||
|
struct UnkeyedRealisation;
|
||||||
|
struct Realisation;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The "serve protocol", used by ssh:// stores.
|
* The "serve protocol", used by ssh:// stores.
|
||||||
|
|
@ -178,6 +181,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);
|
||||||
|
|
@ -190,8 +199,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_
|
||||||
|
|
||||||
} // namespace nix
|
} // namespace nix
|
||||||
|
|
|
||||||
|
|
@ -34,8 +34,10 @@ 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
|
||||||
std::map<K WORKER_USE_LENGTH_PREFIX_SERIALISER_COMMA V>)
|
typename Compare>
|
||||||
|
,
|
||||||
|
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.
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,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 MINIMUM_PROTOCOL_VERSION (1 << 8 | 18)
|
#define MINIMUM_PROTOCOL_VERSION (1 << 8 | 18)
|
||||||
#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)
|
||||||
|
|
@ -35,6 +35,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;
|
||||||
|
|
||||||
|
|
@ -261,6 +264,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>);
|
||||||
|
|
@ -277,8 +288,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_
|
||||||
|
|
||||||
} // namespace nix
|
} // namespace nix
|
||||||
|
|
|
||||||
|
|
@ -56,9 +56,10 @@ protected:
|
||||||
void upsertFile(
|
void upsertFile(
|
||||||
const std::string & path, RestartableSource & source, const std::string & mimeType, uint64_t sizeHint) override
|
const std::string & path, RestartableSource & source, const std::string & mimeType, uint64_t sizeHint) override
|
||||||
{
|
{
|
||||||
auto path2 = config->binaryCacheDir + "/" + path;
|
auto path2 = std::filesystem::path{config->binaryCacheDir} / path;
|
||||||
static std::atomic<int> counter{0};
|
static std::atomic<int> counter{0};
|
||||||
Path tmp = fmt("%s.tmp.%d.%d", path2, getpid(), ++counter);
|
createDirs(path2.parent_path());
|
||||||
|
auto tmp = path2 + fmt(".tmp.%d.%d", getpid(), ++counter);
|
||||||
AutoDelete del(tmp, false);
|
AutoDelete del(tmp, false);
|
||||||
writeFile(tmp, source);
|
writeFile(tmp, source);
|
||||||
std::filesystem::rename(tmp, path2);
|
std::filesystem::rename(tmp, path2);
|
||||||
|
|
|
||||||
|
|
@ -110,8 +110,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)
|
||||||
|
|
@ -621,7 +619,7 @@ void LocalStore::registerDrvOutput(const Realisation & info)
|
||||||
auto combinedSignatures = oldR->signatures;
|
auto combinedSignatures = oldR->signatures;
|
||||||
combinedSignatures.insert(info.signatures.begin(), info.signatures.end());
|
combinedSignatures.insert(info.signatures.begin(), info.signatures.end());
|
||||||
state->stmts->UpdateRealisedOutput
|
state->stmts->UpdateRealisedOutput
|
||||||
.use()(concatStringsSep(" ", combinedSignatures))(info.id.strHash())(info.id.outputName)
|
.use()(concatStringsSep(" ", combinedSignatures))(info.id.drvPath.to_string())(info.id.outputName)
|
||||||
.exec();
|
.exec();
|
||||||
} else {
|
} else {
|
||||||
throw Error(
|
throw Error(
|
||||||
|
|
@ -635,7 +633,7 @@ void LocalStore::registerDrvOutput(const Realisation & info)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
state->stmts->RegisterRealisedOutput
|
state->stmts->RegisterRealisedOutput
|
||||||
.use()(info.id.strHash())(info.id.outputName)(printStorePath(info.outPath))(
|
.use()(info.id.drvPath.to_string())(info.id.outputName)(printStorePath(info.outPath))(
|
||||||
concatStringsSep(" ", info.signatures))
|
concatStringsSep(" ", info.signatures))
|
||||||
.exec();
|
.exec();
|
||||||
}
|
}
|
||||||
|
|
@ -1553,7 +1551,7 @@ void LocalStore::addSignatures(const StorePath & storePath, const StringSet & si
|
||||||
std::optional<std::pair<int64_t, UnkeyedRealisation>>
|
std::optional<std::pair<int64_t, UnkeyedRealisation>>
|
||||||
LocalStore::queryRealisationCore_(LocalStore::State & state, const DrvOutput & id)
|
LocalStore::queryRealisationCore_(LocalStore::State & state, const DrvOutput & id)
|
||||||
{
|
{
|
||||||
auto useQueryRealisedOutput(state.stmts->QueryRealisedOutput.use()(id.strHash())(id.outputName));
|
auto useQueryRealisedOutput(state.stmts->QueryRealisedOutput.use()(id.drvPath.to_string())(id.outputName));
|
||||||
if (!useQueryRealisedOutput.next())
|
if (!useQueryRealisedOutput.next())
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
auto realisationDbId = useQueryRealisedOutput.getInt(0);
|
auto realisationDbId = useQueryRealisedOutput.getInt(0);
|
||||||
|
|
|
||||||
|
|
@ -240,16 +240,15 @@ MissingPaths 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;
|
||||||
|
|
@ -362,7 +361,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);
|
||||||
}
|
}
|
||||||
|
|
@ -386,7 +385,7 @@ StorePath resolveDerivedPath(Store & store, const SingleDerivedPath & req, Store
|
||||||
bfd.output);
|
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;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
@ -121,24 +127,24 @@ public:
|
||||||
state->insertRealisation.create(
|
state->insertRealisation.create(
|
||||||
state->db,
|
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->insertMissingRealisation.create(
|
||||||
state->db,
|
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->queryRealisation.create(
|
||||||
state->db,
|
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,27 @@ public:
|
||||||
|
|
||||||
auto now = time(0);
|
auto now = time(0);
|
||||||
|
|
||||||
auto queryRealisation(state->queryRealisation.use()(cache.id)(id.to_string())(
|
auto queryRealisation(state->queryRealisation.use()(cache.id)(id.drvPath.to_string())(id.outputName)(
|
||||||
now - settings.ttlNegativeNarInfoCache)(now - settings.ttlPositiveNarInfoCache));
|
now - settings.ttlNegativeNarInfoCache)(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};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
return {
|
return {
|
||||||
oValid,
|
oValid,
|
||||||
std::make_shared<Realisation>(nlohmann::json::parse(queryRealisation.getStr(0))),
|
std::make_shared<Realisation>(
|
||||||
|
UnkeyedRealisation{
|
||||||
|
.outPath = StorePath{queryRealisation.getStr(0)},
|
||||||
|
.signatures = nlohmann::json::parse(queryRealisation.getStr(1)),
|
||||||
|
},
|
||||||
|
id),
|
||||||
};
|
};
|
||||||
} catch (Error & e) {
|
} catch (Error & e) {
|
||||||
e.addTrace({}, "while parsing the local disk cache");
|
e.addTrace({}, "reading build trace key-value from the local disk cache");
|
||||||
throw;
|
throw;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
@ -355,7 +366,9 @@ public:
|
||||||
auto & cache(getCache(*state, uri));
|
auto & cache(getCache(*state, uri));
|
||||||
|
|
||||||
state->insertRealisation
|
state->insertRealisation
|
||||||
.use()(cache.id)(realisation.id.to_string())(static_cast<nlohmann::json>(realisation).dump())(time(0))
|
.use()(cache.id)(realisation.id.drvPath.to_string())(realisation.id.outputName)(
|
||||||
|
realisation.outPath.to_string())(static_cast<nlohmann::json>(realisation.signatures).dump())(
|
||||||
|
time(0))
|
||||||
.exec();
|
.exec();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -366,7 +379,7 @@ public:
|
||||||
auto state(_state.lock());
|
auto state(_state.lock());
|
||||||
|
|
||||||
auto & cache(getCache(*state, uri));
|
auto & cache(getCache(*state, uri));
|
||||||
state->insertMissingRealisation.use()(cache.id)(id.to_string())(time(0)).exec();
|
state->insertMissingRealisation.use()(cache.id)(id.drvPath.to_string())(id.outputName)(time(0)).exec();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -8,28 +8,34 @@ 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("!");
|
size_t n = s.rfind('^');
|
||||||
if (n == strRep.npos)
|
if (n == s.npos)
|
||||||
throw InvalidDerivationOutputId("Invalid derivation output id %s", strRep);
|
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::render(const StoreDirConfig & store) const
|
||||||
|
{
|
||||||
|
return std::string(store.printStorePath(drvPath)) + "^" + outputName;
|
||||||
|
}
|
||||||
|
|
||||||
std::string DrvOutput::to_string() const
|
std::string DrvOutput::to_string() const
|
||||||
{
|
{
|
||||||
return strHash() + "!" + outputName;
|
return std::string(drvPath.to_string()) + "^" + outputName;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string UnkeyedRealisation::fingerprint(const DrvOutput & key) const
|
std::string UnkeyedRealisation::fingerprint(const DrvOutput & key) const
|
||||||
{
|
{
|
||||||
nlohmann::json serialized = Realisation{*this, key};
|
auto serialised = static_cast<nlohmann::json>(Realisation{*this, key});
|
||||||
serialized.erase("signatures");
|
auto value = serialised.find("value");
|
||||||
return serialized.dump();
|
assert(value != serialised.end());
|
||||||
|
value->erase("signatures");
|
||||||
|
return serialised.dump();
|
||||||
}
|
}
|
||||||
|
|
||||||
void UnkeyedRealisation::sign(const DrvOutput & key, const Signer & signer)
|
void UnkeyedRealisation::sign(const DrvOutput & key, const Signer & signer)
|
||||||
|
|
@ -61,9 +67,20 @@ const StorePath & RealisedPath::path() const &
|
||||||
return std::visit([](auto & arg) -> auto & { return arg.getPath(); }, raw);
|
return std::visit([](auto & arg) -> auto & { return arg.getPath(); }, raw);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Realisation::isCompatibleWith(const UnkeyedRealisation & 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))
|
||||||
{
|
{
|
||||||
return outPath == other.outPath;
|
}
|
||||||
|
|
||||||
|
MissingRealisation::MissingRealisation(
|
||||||
|
const StoreDirConfig & store,
|
||||||
|
const SingleDerivedPath & drvPath,
|
||||||
|
const StorePath & drvPathResolved,
|
||||||
|
const OutputName & outputName)
|
||||||
|
: MissingRealisation{store, drvPathResolved, outputName}
|
||||||
|
{
|
||||||
|
addTrace({}, "looking up realisation for derivation '%s'", drvPath.to_string(store));
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace nix
|
} // namespace nix
|
||||||
|
|
@ -74,24 +91,32 @@ using namespace nix;
|
||||||
|
|
||||||
DrvOutput adl_serializer<DrvOutput>::from_json(const json & json)
|
DrvOutput adl_serializer<DrvOutput>::from_json(const json & json)
|
||||||
{
|
{
|
||||||
return DrvOutput::parse(getString(json));
|
auto obj = getObject(json);
|
||||||
|
|
||||||
|
return {
|
||||||
|
.drvPath = valueAt(obj, "drvPath"),
|
||||||
|
.outputName = getString(valueAt(obj, "outputName")),
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
void adl_serializer<DrvOutput>::to_json(json & json, const DrvOutput & drvOutput)
|
void adl_serializer<DrvOutput>::to_json(json & json, const DrvOutput & drvOutput)
|
||||||
{
|
{
|
||||||
json = drvOutput.to_string();
|
json = {
|
||||||
|
{"drvPath", drvOutput.drvPath},
|
||||||
|
{"outputName", drvOutput.outputName},
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
UnkeyedRealisation adl_serializer<UnkeyedRealisation>::from_json(const json & json0)
|
UnkeyedRealisation adl_serializer<UnkeyedRealisation>::from_json(const json & json)
|
||||||
{
|
{
|
||||||
auto json = getObject(json0);
|
auto obj = getObject(json);
|
||||||
|
|
||||||
StringSet signatures;
|
StringSet signatures;
|
||||||
if (auto signaturesOpt = optionalValueAt(json, "signatures"))
|
if (auto * signaturesJson = get(obj, "signatures"))
|
||||||
signatures = *signaturesOpt;
|
signatures = getStringSet(*signaturesJson);
|
||||||
|
|
||||||
return UnkeyedRealisation{
|
return {
|
||||||
.outPath = valueAt(json, "outPath"),
|
.outPath = valueAt(obj, "outPath"),
|
||||||
.signatures = signatures,
|
.signatures = signatures,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
@ -101,25 +126,25 @@ void adl_serializer<UnkeyedRealisation>::to_json(json & json, const UnkeyedReali
|
||||||
json = {
|
json = {
|
||||||
{"outPath", r.outPath},
|
{"outPath", r.outPath},
|
||||||
{"signatures", r.signatures},
|
{"signatures", r.signatures},
|
||||||
// back-compat
|
|
||||||
{"dependentRealisations", json::object()},
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
Realisation adl_serializer<Realisation>::from_json(const json & json0)
|
Realisation adl_serializer<Realisation>::from_json(const json & json)
|
||||||
{
|
{
|
||||||
auto json = getObject(json0);
|
auto obj = getObject(json);
|
||||||
|
|
||||||
return Realisation{
|
return {
|
||||||
static_cast<UnkeyedRealisation>(json0),
|
static_cast<UnkeyedRealisation>(valueAt(obj, "value")),
|
||||||
valueAt(json, "id"),
|
static_cast<DrvOutput>(valueAt(obj, "key")),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
void adl_serializer<Realisation>::to_json(json & json, const Realisation & r)
|
void adl_serializer<Realisation>::to_json(json & json, const Realisation & r)
|
||||||
{
|
{
|
||||||
json = static_cast<const UnkeyedRealisation &>(r);
|
json = {
|
||||||
json["id"] = r.id;
|
{"key", r.id},
|
||||||
|
{"value", static_cast<const UnkeyedRealisation &>(r)},
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace nlohmann
|
} // namespace nlohmann
|
||||||
|
|
|
||||||
|
|
@ -507,7 +507,7 @@ void RemoteStore::queryRealisationUncached(
|
||||||
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);
|
||||||
}
|
}
|
||||||
|
|
@ -516,21 +516,12 @@ void RemoteStore::queryRealisationUncached(
|
||||||
conn->to << id.to_string();
|
conn->to << id.to_string();
|
||||||
conn.processStderr();
|
conn.processStderr();
|
||||||
|
|
||||||
auto real = [&]() -> std::shared_ptr<const UnkeyedRealisation> {
|
callback([&]() -> std::shared_ptr<const UnkeyedRealisation> {
|
||||||
if (GET_PROTOCOL_MINOR(conn->protoVersion) < 31) {
|
auto realisation = WorkerProto::Serialise<std::optional<UnkeyedRealisation>>::read(*this, *conn);
|
||||||
auto outPaths = WorkerProto::Serialise<std::set<StorePath>>::read(*this, *conn);
|
if (!realisation)
|
||||||
if (outPaths.empty())
|
return nullptr;
|
||||||
return nullptr;
|
return std::make_shared<const UnkeyedRealisation>(*realisation);
|
||||||
return std::make_shared<const UnkeyedRealisation>(UnkeyedRealisation{.outPath = *outPaths.begin()});
|
}());
|
||||||
} else {
|
|
||||||
auto realisations = WorkerProto::Serialise<std::set<Realisation>>::read(*this, *conn);
|
|
||||||
if (realisations.empty())
|
|
||||||
return nullptr;
|
|
||||||
return std::make_shared<const UnkeyedRealisation>(*realisations.begin());
|
|
||||||
}
|
|
||||||
}();
|
|
||||||
|
|
||||||
callback(std::shared_ptr<const UnkeyedRealisation>(real));
|
|
||||||
} catch (...) {
|
} catch (...) {
|
||||||
return callback.rethrow();
|
return callback.rethrow();
|
||||||
}
|
}
|
||||||
|
|
@ -612,30 +603,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 = queryRealisation(outputId);
|
auto realisation = queryRealisation(outputId);
|
||||||
if (!realisation)
|
if (!realisation)
|
||||||
throw MissingRealisation(outputId);
|
throw MissingRealisation(*this, outputId);
|
||||||
success.builtOutputs.emplace(output, Realisation{*realisation, outputId});
|
success.builtOutputs.emplace(output, *realisation);
|
||||||
} else {
|
} else {
|
||||||
success.builtOutputs.emplace(
|
success.builtOutputs.emplace(
|
||||||
output,
|
output,
|
||||||
Realisation{
|
UnkeyedRealisation{
|
||||||
UnkeyedRealisation{
|
.outPath = outputPath,
|
||||||
.outPath = outputPath,
|
|
||||||
},
|
|
||||||
outputId,
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -281,9 +281,18 @@ std::vector<KeyedBuildResult> RestrictedStore::buildPathsWithResults(
|
||||||
|
|
||||||
for (auto & result : results) {
|
for (auto & result : results) {
|
||||||
if (auto * successP = result.tryGetSuccess()) {
|
if (auto * successP = result.tryGetSuccess()) {
|
||||||
for (auto & [outputName, output] : successP->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] : successP->builtOutputs) {
|
||||||
|
newPaths.insert(output.outPath);
|
||||||
|
newRealisations.insert(
|
||||||
|
{output,
|
||||||
|
{
|
||||||
|
.drvPath = drvPath,
|
||||||
|
.outputName = outputName,
|
||||||
|
}});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
|
|
||||||
|
|
@ -24,10 +25,19 @@ BuildResult ServeProto::Serialise<BuildResult>::read(const StoreDirConfig & stor
|
||||||
|
|
||||||
if (GET_PROTOCOL_MINOR(conn.version) >= 3)
|
if (GET_PROTOCOL_MINOR(conn.version) >= 3)
|
||||||
conn.from >> status.timesBuilt >> failure.isNonDeterministic >> status.startTime >> status.stopTime;
|
conn.from >> status.timesBuilt >> failure.isNonDeterministic >> status.startTime >> 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)
|
success.builtOutputs = ServeProto::Serialise<std::map<OutputName, UnkeyedRealisation>>::read(store, conn);
|
||||||
success.builtOutputs.insert_or_assign(std::move(output.outputName), std::move(realisation));
|
} 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);
|
||||||
|
success.builtOutputs.insert_or_assign(
|
||||||
|
output.substr(n + 1),
|
||||||
|
UnkeyedRealisation{
|
||||||
|
StorePath{getString(valueAt(getObject(nlohmann::json::parse(realisation)), "outPath"))}});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (BuildResult::Success::statusIs(rawStatus)) {
|
if (BuildResult::Success::statusIs(rawStatus)) {
|
||||||
|
|
@ -51,15 +61,18 @@ void ServeProto::Serialise<BuildResult>::write(
|
||||||
default value for the fields that don't exist in that case. */
|
default value for the fields that don't exist in that case. */
|
||||||
auto common = [&](std::string_view errorMsg, bool isNonDeterministic, const auto & builtOutputs) {
|
auto common = [&](std::string_view errorMsg, bool isNonDeterministic, const auto & builtOutputs) {
|
||||||
conn.to << errorMsg;
|
conn.to << errorMsg;
|
||||||
|
|
||||||
if (GET_PROTOCOL_MINOR(conn.version) >= 3)
|
if (GET_PROTOCOL_MINOR(conn.version) >= 3)
|
||||||
conn.to << res.timesBuilt << isNonDeterministic << res.startTime << res.stopTime;
|
conn.to << res.timesBuilt << isNonDeterministic << res.startTime << res.stopTime;
|
||||||
if (GET_PROTOCOL_MINOR(conn.version) >= 6) {
|
|
||||||
DrvOutputs builtOutputsFullKey;
|
if (GET_PROTOCOL_MINOR(conn.version) >= 8) {
|
||||||
for (auto & [output, realisation] : builtOutputs)
|
ServeProto::write(store, conn, builtOutputs);
|
||||||
builtOutputsFullKey.insert_or_assign(realisation.id, realisation);
|
} else if (GET_PROTOCOL_MINOR(conn.version) >= 6) {
|
||||||
ServeProto::write(store, conn, builtOutputsFullKey);
|
// We no longer support these types of realisations
|
||||||
|
ServeProto::write(store, conn, StringMap{});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
std::visit(
|
std::visit(
|
||||||
overloaded{
|
overloaded{
|
||||||
[&](const BuildResult::Failure & failure) {
|
[&](const BuildResult::Failure & failure) {
|
||||||
|
|
@ -144,4 +157,82 @@ void ServeProto::Serialise<ServeProto::BuildOptions>::write(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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));
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace nix
|
} // namespace nix
|
||||||
|
|
|
||||||
|
|
@ -367,9 +367,8 @@ Store::queryPartialDerivationOutputMap(const StorePath & path, Store * evalStore
|
||||||
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 {
|
||||||
|
|
@ -389,7 +388,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;
|
||||||
|
|
|
||||||
|
|
@ -1870,7 +1870,10 @@ SingleDrvOutputs DerivationBuilderImpl::registerOutputs()
|
||||||
{
|
{
|
||||||
.outPath = newInfo.path,
|
.outPath = newInfo.path,
|
||||||
},
|
},
|
||||||
DrvOutput{oldinfo->outputHash, outputName},
|
DrvOutput{
|
||||||
|
.drvPath = drvPath,
|
||||||
|
.outputName = outputName,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
if (experimentalFeatureSettings.isEnabled(Xp::CaDerivations) && !drv.type().isImpure()) {
|
if (experimentalFeatureSettings.isEnabled(Xp::CaDerivations) && !drv.type().isImpure()) {
|
||||||
store.signRealisation(thisRealisation);
|
store.signRealisation(thisRealisation);
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
|
|
@ -132,7 +133,7 @@ void WorkerProto::Serialise<DerivedPath>::write(
|
||||||
throw Error(
|
throw Error(
|
||||||
"trying to request '%s', but daemon protocol %d.%d is too old (< 1.29) to request a derivation file",
|
"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) {
|
||||||
|
|
@ -174,14 +175,24 @@ BuildResult WorkerProto::Serialise<BuildResult>::read(const StoreDirConfig & sto
|
||||||
if (GET_PROTOCOL_MINOR(conn.version) >= 29) {
|
if (GET_PROTOCOL_MINOR(conn.version) >= 29) {
|
||||||
conn.from >> res.timesBuilt >> failure.isNonDeterministic >> res.startTime >> res.stopTime;
|
conn.from >> res.timesBuilt >> failure.isNonDeterministic >> res.startTime >> 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)
|
success.builtOutputs = WorkerProto::Serialise<std::map<OutputName, UnkeyedRealisation>>::read(store, conn);
|
||||||
success.builtOutputs.insert_or_assign(std::move(output.outputName), std::move(realisation));
|
} 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);
|
||||||
|
success.builtOutputs.insert_or_assign(
|
||||||
|
output.substr(n + 1),
|
||||||
|
UnkeyedRealisation{
|
||||||
|
StorePath{getString(valueAt(getObject(nlohmann::json::parse(realisation)), "outPath"))}});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (BuildResult::Success::statusIs(rawStatus)) {
|
if (BuildResult::Success::statusIs(rawStatus)) {
|
||||||
|
|
@ -205,20 +216,24 @@ void WorkerProto::Serialise<BuildResult>::write(
|
||||||
default value for the fields that don't exist in that case. */
|
default value for the fields that don't exist in that case. */
|
||||||
auto common = [&](std::string_view errorMsg, bool isNonDeterministic, const auto & builtOutputs) {
|
auto common = [&](std::string_view errorMsg, bool isNonDeterministic, const auto & builtOutputs) {
|
||||||
conn.to << errorMsg;
|
conn.to << errorMsg;
|
||||||
|
|
||||||
if (GET_PROTOCOL_MINOR(conn.version) >= 29) {
|
if (GET_PROTOCOL_MINOR(conn.version) >= 29) {
|
||||||
conn.to << res.timesBuilt << isNonDeterministic << res.startTime << res.stopTime;
|
conn.to << res.timesBuilt << isNonDeterministic << res.startTime << 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 builtOutputsFullKey;
|
if (GET_PROTOCOL_MINOR(conn.version) >= 39) {
|
||||||
for (auto & [output, realisation] : builtOutputs)
|
WorkerProto::write(store, conn, builtOutputs);
|
||||||
builtOutputsFullKey.insert_or_assign(realisation.id, realisation);
|
} else if (GET_PROTOCOL_MINOR(conn.version) >= 28) {
|
||||||
WorkerProto::write(store, conn, builtOutputsFullKey);
|
// Don't support those types of realisations anymore.
|
||||||
|
WorkerProto::write(store, conn, StringMap{});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
std::visit(
|
std::visit(
|
||||||
overloaded{
|
overloaded{
|
||||||
[&](const BuildResult::Failure & failure) {
|
[&](const BuildResult::Failure & failure) {
|
||||||
|
|
@ -309,4 +324,113 @@ void WorkerProto::Serialise<WorkerProto::ClientHandshakeInfo>::write(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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));
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace nix
|
} // namespace nix
|
||||||
|
|
|
||||||
|
|
@ -346,13 +346,11 @@ static int main_build_remote(int argc, char ** argv)
|
||||||
optResult = std::move(res[0]);
|
optResult = std::move(res[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
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);
|
||||||
|
|
@ -362,7 +360,7 @@ static int main_build_remote(int argc, char ** argv)
|
||||||
auto i = success.builtOutputs.find(outputName);
|
auto i = success.builtOutputs.find(outputName);
|
||||||
assert(i != success.builtOutputs.end());
|
assert(i != success.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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -279,16 +279,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);
|
||||||
|
|
|
||||||
|
|
@ -163,10 +163,13 @@ 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(static_cast<nlohmann::json>(*realisation).dump().c_str(), 0)));
|
XPUSHs(sv_2mortal(newSVpv(static_cast<nlohmann::json>(*realisation).dump().c_str(), 0)));
|
||||||
else
|
else
|
||||||
|
|
|
||||||
|
|
@ -49,14 +49,14 @@ fi
|
||||||
clearStore
|
clearStore
|
||||||
nix build --file ../simple.nix -L --no-link --post-build-hook "$pushToStore"
|
nix build --file ../simple.nix -L --no-link --post-build-hook "$pushToStore"
|
||||||
clearStore
|
clearStore
|
||||||
rm -r "$REMOTE_STORE_DIR/realisations"
|
rm -r "$REMOTE_STORE_DIR/build-trace"
|
||||||
nix build --file ../simple.nix -L --no-link --substitute --substituters "$REMOTE_STORE" --no-require-sigs -j0
|
nix build --file ../simple.nix -L --no-link --substitute --substituters "$REMOTE_STORE" --no-require-sigs -j0
|
||||||
# There's no easy way to check whether a realisation is present on the local
|
# There's no easy way to check whether a realisation is present on the local
|
||||||
# store − short of manually querying the db, but the build environment doesn't
|
# store − short of manually querying the db, but the build environment doesn't
|
||||||
# have the sqlite binary − so we instead push things again, and check that the
|
# have the sqlite binary − so we instead push things again, and check that the
|
||||||
# realisations have correctly been pushed to the remote store
|
# realisations have correctly been pushed to the remote store
|
||||||
nix copy --to "$REMOTE_STORE" --file ../simple.nix
|
nix copy --to "$REMOTE_STORE" --file ../simple.nix
|
||||||
if [[ -z "$(ls "$REMOTE_STORE_DIR/realisations")" ]]; then
|
if [[ -z "$(ls "$REMOTE_STORE_DIR/build-trace")" ]]; then
|
||||||
echo "Realisations not rebuilt"
|
echo "Realisations not rebuilt"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
@ -71,5 +71,5 @@ buildDrvs --substitute --substituters "$REMOTE_STORE" --no-require-sigs -j0
|
||||||
# Try rebuilding, but remove the realisations from the remote cache to force
|
# Try rebuilding, but remove the realisations from the remote cache to force
|
||||||
# using the cachecache
|
# using the cachecache
|
||||||
clearStore
|
clearStore
|
||||||
rm "$REMOTE_STORE_DIR"/realisations/*
|
rm -r "$REMOTE_STORE_DIR"/build-trace/*
|
||||||
buildDrvs --substitute --substituters "$REMOTE_STORE" --no-require-sigs -j0
|
buildDrvs --substitute --substituters "$REMOTE_STORE" --no-require-sigs -j0
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue