mirror of
https://github.com/NixOS/nix.git
synced 2025-11-09 12:06:01 +01:00
Merge pull request #14159 from NixOS/revert-resolution-goal
Revert #14097, #14022
This commit is contained in:
commit
cd9c208baf
41 changed files with 487 additions and 881 deletions
|
|
@ -117,11 +117,10 @@ RealisedPath::Set BuiltPath::toRealisedPaths(Store & store) const
|
||||||
"the derivation '%s' has unrealised output '%s' (derived-path.cc/toRealisedPaths)",
|
"the derivation '%s' has unrealised output '%s' (derived-path.cc/toRealisedPaths)",
|
||||||
store.printStorePath(p.drvPath->outPath()),
|
store.printStorePath(p.drvPath->outPath()),
|
||||||
outputName);
|
outputName);
|
||||||
DrvOutput key{*drvOutput, outputName};
|
auto thisRealisation = store.queryRealisation(DrvOutput{*drvOutput, outputName});
|
||||||
auto thisRealisation = store.queryRealisation(key);
|
|
||||||
assert(thisRealisation); // We’ve built it, so we must
|
assert(thisRealisation); // We’ve built it, so we must
|
||||||
// have the realisation
|
// have the realisation
|
||||||
res.insert(Realisation{*thisRealisation, std::move(key)});
|
res.insert(*thisRealisation);
|
||||||
} else {
|
} else {
|
||||||
res.insert(outputPath);
|
res.insert(outputPath);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -112,17 +112,20 @@ CHARACTERIZATION_TEST(
|
||||||
"realisation",
|
"realisation",
|
||||||
(std::tuple<Realisation, Realisation>{
|
(std::tuple<Realisation, Realisation>{
|
||||||
Realisation{
|
Realisation{
|
||||||
{
|
.id =
|
||||||
.outPath = StorePath{"g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-foo"},
|
|
||||||
.signatures = {"asdf", "qwer"},
|
|
||||||
},
|
|
||||||
DrvOutput{
|
DrvOutput{
|
||||||
.drvHash = Hash::parseSRI("sha256-FePFYIlMuycIXPZbWi7LGEiMmZSX9FMbaQenWBzm1Sc="),
|
.drvHash = Hash::parseSRI("sha256-FePFYIlMuycIXPZbWi7LGEiMmZSX9FMbaQenWBzm1Sc="),
|
||||||
.outputName = "baz",
|
.outputName = "baz",
|
||||||
},
|
},
|
||||||
|
.outPath = StorePath{"g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-foo"},
|
||||||
|
.signatures = {"asdf", "qwer"},
|
||||||
},
|
},
|
||||||
Realisation{
|
Realisation{
|
||||||
|
.id =
|
||||||
{
|
{
|
||||||
|
.drvHash = Hash::parseSRI("sha256-FePFYIlMuycIXPZbWi7LGEiMmZSX9FMbaQenWBzm1Sc="),
|
||||||
|
.outputName = "baz",
|
||||||
|
},
|
||||||
.outPath = StorePath{"g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-foo"},
|
.outPath = StorePath{"g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-foo"},
|
||||||
.signatures = {"asdf", "qwer"},
|
.signatures = {"asdf", "qwer"},
|
||||||
.dependentRealisations =
|
.dependentRealisations =
|
||||||
|
|
@ -136,11 +139,6 @@ CHARACTERIZATION_TEST(
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
|
||||||
.drvHash = Hash::parseSRI("sha256-FePFYIlMuycIXPZbWi7LGEiMmZSX9FMbaQenWBzm1Sc="),
|
|
||||||
.outputName = "baz",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}))
|
}))
|
||||||
|
|
||||||
CHARACTERIZATION_TEST(
|
CHARACTERIZATION_TEST(
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
#include <gtest/gtest.h>
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
#include "nix/store/dummy-store-impl.hh"
|
#include "nix/store/dummy-store.hh"
|
||||||
#include "nix/store/globals.hh"
|
#include "nix/store/globals.hh"
|
||||||
#include "nix/store/realisation.hh"
|
#include "nix/store/realisation.hh"
|
||||||
|
|
||||||
|
|
@ -13,7 +13,7 @@ TEST(DummyStore, realisation_read)
|
||||||
auto store = [] {
|
auto store = [] {
|
||||||
auto cfg = make_ref<DummyStoreConfig>(StoreReference::Params{});
|
auto cfg = make_ref<DummyStoreConfig>(StoreReference::Params{});
|
||||||
cfg->readOnly = false;
|
cfg->readOnly = false;
|
||||||
return cfg->openDummyStore();
|
return cfg->openStore();
|
||||||
}();
|
}();
|
||||||
|
|
||||||
auto drvHash = Hash::parseExplicitFormatUnprefixed(
|
auto drvHash = Hash::parseExplicitFormatUnprefixed(
|
||||||
|
|
@ -22,17 +22,6 @@ TEST(DummyStore, realisation_read)
|
||||||
auto outputName = "foo";
|
auto outputName = "foo";
|
||||||
|
|
||||||
EXPECT_EQ(store->queryRealisation({drvHash, outputName}), nullptr);
|
EXPECT_EQ(store->queryRealisation({drvHash, outputName}), nullptr);
|
||||||
|
|
||||||
UnkeyedRealisation value{
|
|
||||||
.outPath = StorePath{"g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-foo.drv"},
|
|
||||||
};
|
|
||||||
|
|
||||||
store->buildTrace.insert({drvHash, {{outputName, make_ref<UnkeyedRealisation>(value)}}});
|
|
||||||
|
|
||||||
auto value2 = store->queryRealisation({drvHash, outputName});
|
|
||||||
|
|
||||||
ASSERT_TRUE(value2);
|
|
||||||
EXPECT_EQ(*value2, value);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace nix
|
} // namespace nix
|
||||||
|
|
|
||||||
|
|
@ -49,9 +49,8 @@ INSTANTIATE_TEST_SUITE_P(
|
||||||
RealisationJsonTest,
|
RealisationJsonTest,
|
||||||
([] {
|
([] {
|
||||||
Realisation simple{
|
Realisation simple{
|
||||||
{
|
|
||||||
.outPath = StorePath{"g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-foo.drv"},
|
.id =
|
||||||
},
|
|
||||||
{
|
{
|
||||||
.drvHash = Hash::parseExplicitFormatUnprefixed(
|
.drvHash = Hash::parseExplicitFormatUnprefixed(
|
||||||
"ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad",
|
"ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad",
|
||||||
|
|
@ -59,6 +58,7 @@ INSTANTIATE_TEST_SUITE_P(
|
||||||
HashFormat::Base16),
|
HashFormat::Base16),
|
||||||
.outputName = "foo",
|
.outputName = "foo",
|
||||||
},
|
},
|
||||||
|
.outPath = StorePath{"g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-foo.drv"},
|
||||||
};
|
};
|
||||||
return ::testing::Values(
|
return ::testing::Values(
|
||||||
std::pair{
|
std::pair{
|
||||||
|
|
|
||||||
|
|
@ -95,17 +95,20 @@ VERSIONED_CHARACTERIZATION_TEST(
|
||||||
defaultVersion,
|
defaultVersion,
|
||||||
(std::tuple<Realisation, Realisation>{
|
(std::tuple<Realisation, Realisation>{
|
||||||
Realisation{
|
Realisation{
|
||||||
{
|
.id =
|
||||||
|
DrvOutput{
|
||||||
|
.drvHash = Hash::parseSRI("sha256-FePFYIlMuycIXPZbWi7LGEiMmZSX9FMbaQenWBzm1Sc="),
|
||||||
|
.outputName = "baz",
|
||||||
|
},
|
||||||
.outPath = StorePath{"g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-foo"},
|
.outPath = StorePath{"g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-foo"},
|
||||||
.signatures = {"asdf", "qwer"},
|
.signatures = {"asdf", "qwer"},
|
||||||
},
|
},
|
||||||
|
Realisation{
|
||||||
|
.id =
|
||||||
{
|
{
|
||||||
.drvHash = Hash::parseSRI("sha256-FePFYIlMuycIXPZbWi7LGEiMmZSX9FMbaQenWBzm1Sc="),
|
.drvHash = Hash::parseSRI("sha256-FePFYIlMuycIXPZbWi7LGEiMmZSX9FMbaQenWBzm1Sc="),
|
||||||
.outputName = "baz",
|
.outputName = "baz",
|
||||||
},
|
},
|
||||||
},
|
|
||||||
Realisation{
|
|
||||||
{
|
|
||||||
.outPath = StorePath{"g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-foo"},
|
.outPath = StorePath{"g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-foo"},
|
||||||
.signatures = {"asdf", "qwer"},
|
.signatures = {"asdf", "qwer"},
|
||||||
.dependentRealisations =
|
.dependentRealisations =
|
||||||
|
|
@ -119,11 +122,6 @@ VERSIONED_CHARACTERIZATION_TEST(
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
|
||||||
.drvHash = Hash::parseSRI("sha256-FePFYIlMuycIXPZbWi7LGEiMmZSX9FMbaQenWBzm1Sc="),
|
|
||||||
.outputName = "baz",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}))
|
}))
|
||||||
|
|
||||||
VERSIONED_CHARACTERIZATION_TEST(ServeProtoTest, buildResult_2_2, "build-result-2.2", 2 << 8 | 2, ({
|
VERSIONED_CHARACTERIZATION_TEST(ServeProtoTest, buildResult_2_2, "build-result-2.2", 2 << 8 | 2, ({
|
||||||
|
|
@ -198,27 +196,25 @@ VERSIONED_CHARACTERIZATION_TEST(
|
||||||
{
|
{
|
||||||
"foo",
|
"foo",
|
||||||
{
|
{
|
||||||
{
|
.id =
|
||||||
.outPath = StorePath{"g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-foo"},
|
|
||||||
},
|
|
||||||
DrvOutput{
|
DrvOutput{
|
||||||
.drvHash =
|
.drvHash =
|
||||||
Hash::parseSRI("sha256-b4afnqKCO9oWXgYHb9DeQ2berSwOjS27rSd9TxXDc/U="),
|
Hash::parseSRI("sha256-b4afnqKCO9oWXgYHb9DeQ2berSwOjS27rSd9TxXDc/U="),
|
||||||
.outputName = "foo",
|
.outputName = "foo",
|
||||||
},
|
},
|
||||||
|
.outPath = StorePath{"g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-foo"},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"bar",
|
"bar",
|
||||||
{
|
{
|
||||||
{
|
.id =
|
||||||
.outPath = StorePath{"g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-bar"},
|
|
||||||
},
|
|
||||||
DrvOutput{
|
DrvOutput{
|
||||||
.drvHash =
|
.drvHash =
|
||||||
Hash::parseSRI("sha256-b4afnqKCO9oWXgYHb9DeQ2berSwOjS27rSd9TxXDc/U="),
|
Hash::parseSRI("sha256-b4afnqKCO9oWXgYHb9DeQ2berSwOjS27rSd9TxXDc/U="),
|
||||||
.outputName = "bar",
|
.outputName = "bar",
|
||||||
},
|
},
|
||||||
|
.outPath = StorePath{"g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-bar"},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -148,17 +148,20 @@ VERSIONED_CHARACTERIZATION_TEST(
|
||||||
defaultVersion,
|
defaultVersion,
|
||||||
(std::tuple<Realisation, Realisation>{
|
(std::tuple<Realisation, Realisation>{
|
||||||
Realisation{
|
Realisation{
|
||||||
{
|
.id =
|
||||||
.outPath = StorePath{"g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-foo"},
|
|
||||||
.signatures = {"asdf", "qwer"},
|
|
||||||
},
|
|
||||||
DrvOutput{
|
DrvOutput{
|
||||||
.drvHash = Hash::parseSRI("sha256-FePFYIlMuycIXPZbWi7LGEiMmZSX9FMbaQenWBzm1Sc="),
|
.drvHash = Hash::parseSRI("sha256-FePFYIlMuycIXPZbWi7LGEiMmZSX9FMbaQenWBzm1Sc="),
|
||||||
.outputName = "baz",
|
.outputName = "baz",
|
||||||
},
|
},
|
||||||
|
.outPath = StorePath{"g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-foo"},
|
||||||
|
.signatures = {"asdf", "qwer"},
|
||||||
},
|
},
|
||||||
Realisation{
|
Realisation{
|
||||||
|
.id =
|
||||||
{
|
{
|
||||||
|
.drvHash = Hash::parseSRI("sha256-FePFYIlMuycIXPZbWi7LGEiMmZSX9FMbaQenWBzm1Sc="),
|
||||||
|
.outputName = "baz",
|
||||||
|
},
|
||||||
.outPath = StorePath{"g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-foo"},
|
.outPath = StorePath{"g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-foo"},
|
||||||
.signatures = {"asdf", "qwer"},
|
.signatures = {"asdf", "qwer"},
|
||||||
.dependentRealisations =
|
.dependentRealisations =
|
||||||
|
|
@ -172,11 +175,6 @@ VERSIONED_CHARACTERIZATION_TEST(
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
DrvOutput{
|
|
||||||
.drvHash = Hash::parseSRI("sha256-FePFYIlMuycIXPZbWi7LGEiMmZSX9FMbaQenWBzm1Sc="),
|
|
||||||
.outputName = "baz",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}))
|
}))
|
||||||
|
|
||||||
VERSIONED_CHARACTERIZATION_TEST(WorkerProtoTest, buildResult_1_27, "build-result-1.27", 1 << 8 | 27, ({
|
VERSIONED_CHARACTERIZATION_TEST(WorkerProtoTest, buildResult_1_27, "build-result-1.27", 1 << 8 | 27, ({
|
||||||
|
|
@ -216,25 +214,25 @@ VERSIONED_CHARACTERIZATION_TEST(
|
||||||
{
|
{
|
||||||
"foo",
|
"foo",
|
||||||
{
|
{
|
||||||
{
|
.id =
|
||||||
.outPath = StorePath{"g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-foo"},
|
|
||||||
},
|
|
||||||
DrvOutput{
|
DrvOutput{
|
||||||
.drvHash = Hash::parseSRI("sha256-b4afnqKCO9oWXgYHb9DeQ2berSwOjS27rSd9TxXDc/U="),
|
.drvHash =
|
||||||
|
Hash::parseSRI("sha256-b4afnqKCO9oWXgYHb9DeQ2berSwOjS27rSd9TxXDc/U="),
|
||||||
.outputName = "foo",
|
.outputName = "foo",
|
||||||
},
|
},
|
||||||
|
.outPath = StorePath{"g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-foo"},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"bar",
|
"bar",
|
||||||
{
|
{
|
||||||
{
|
.id =
|
||||||
.outPath = StorePath{"g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-bar"},
|
|
||||||
},
|
|
||||||
DrvOutput{
|
DrvOutput{
|
||||||
.drvHash = Hash::parseSRI("sha256-b4afnqKCO9oWXgYHb9DeQ2berSwOjS27rSd9TxXDc/U="),
|
.drvHash =
|
||||||
|
Hash::parseSRI("sha256-b4afnqKCO9oWXgYHb9DeQ2berSwOjS27rSd9TxXDc/U="),
|
||||||
.outputName = "bar",
|
.outputName = "bar",
|
||||||
},
|
},
|
||||||
|
.outPath = StorePath{"g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-bar"},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
@ -269,27 +267,25 @@ VERSIONED_CHARACTERIZATION_TEST(
|
||||||
{
|
{
|
||||||
"foo",
|
"foo",
|
||||||
{
|
{
|
||||||
{
|
.id =
|
||||||
.outPath = StorePath{"g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-foo"},
|
|
||||||
},
|
|
||||||
DrvOutput{
|
DrvOutput{
|
||||||
.drvHash =
|
.drvHash =
|
||||||
Hash::parseSRI("sha256-b4afnqKCO9oWXgYHb9DeQ2berSwOjS27rSd9TxXDc/U="),
|
Hash::parseSRI("sha256-b4afnqKCO9oWXgYHb9DeQ2berSwOjS27rSd9TxXDc/U="),
|
||||||
.outputName = "foo",
|
.outputName = "foo",
|
||||||
},
|
},
|
||||||
|
.outPath = StorePath{"g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-foo"},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"bar",
|
"bar",
|
||||||
{
|
{
|
||||||
{
|
.id =
|
||||||
.outPath = StorePath{"g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-bar"},
|
|
||||||
},
|
|
||||||
DrvOutput{
|
DrvOutput{
|
||||||
.drvHash =
|
.drvHash =
|
||||||
Hash::parseSRI("sha256-b4afnqKCO9oWXgYHb9DeQ2berSwOjS27rSd9TxXDc/U="),
|
Hash::parseSRI("sha256-b4afnqKCO9oWXgYHb9DeQ2berSwOjS27rSd9TxXDc/U="),
|
||||||
.outputName = "bar",
|
.outputName = "bar",
|
||||||
},
|
},
|
||||||
|
.outPath = StorePath{"g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-bar"},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
@ -328,27 +324,25 @@ VERSIONED_CHARACTERIZATION_TEST(
|
||||||
{
|
{
|
||||||
"foo",
|
"foo",
|
||||||
{
|
{
|
||||||
{
|
.id =
|
||||||
.outPath = StorePath{"g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-foo"},
|
|
||||||
},
|
|
||||||
DrvOutput{
|
DrvOutput{
|
||||||
.drvHash =
|
.drvHash =
|
||||||
Hash::parseSRI("sha256-b4afnqKCO9oWXgYHb9DeQ2berSwOjS27rSd9TxXDc/U="),
|
Hash::parseSRI("sha256-b4afnqKCO9oWXgYHb9DeQ2berSwOjS27rSd9TxXDc/U="),
|
||||||
.outputName = "foo",
|
.outputName = "foo",
|
||||||
},
|
},
|
||||||
|
.outPath = StorePath{"g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-foo"},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"bar",
|
"bar",
|
||||||
{
|
{
|
||||||
{
|
.id =
|
||||||
.outPath = StorePath{"g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-bar"},
|
|
||||||
},
|
|
||||||
DrvOutput{
|
DrvOutput{
|
||||||
.drvHash =
|
.drvHash =
|
||||||
Hash::parseSRI("sha256-b4afnqKCO9oWXgYHb9DeQ2berSwOjS27rSd9TxXDc/U="),
|
Hash::parseSRI("sha256-b4afnqKCO9oWXgYHb9DeQ2berSwOjS27rSd9TxXDc/U="),
|
||||||
.outputName = "bar",
|
.outputName = "bar",
|
||||||
},
|
},
|
||||||
|
.outPath = StorePath{"g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-bar"},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -502,15 +502,10 @@ StorePath BinaryCacheStore::addToStore(
|
||||||
->path;
|
->path;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string BinaryCacheStore::makeRealisationPath(const DrvOutput & id)
|
|
||||||
{
|
|
||||||
return realisationsPrefix + "/" + id.to_string() + ".doi";
|
|
||||||
}
|
|
||||||
|
|
||||||
void BinaryCacheStore::queryRealisationUncached(
|
void BinaryCacheStore::queryRealisationUncached(
|
||||||
const DrvOutput & id, Callback<std::shared_ptr<const UnkeyedRealisation>> callback) noexcept
|
const DrvOutput & id, Callback<std::shared_ptr<const Realisation>> callback) noexcept
|
||||||
{
|
{
|
||||||
auto outputInfoFilePath = makeRealisationPath(id);
|
auto outputInfoFilePath = realisationsPrefix + "/" + id.to_string() + ".doi";
|
||||||
|
|
||||||
auto callbackPtr = std::make_shared<decltype(callback)>(std::move(callback));
|
auto callbackPtr = std::make_shared<decltype(callback)>(std::move(callback));
|
||||||
|
|
||||||
|
|
@ -520,12 +515,11 @@ void BinaryCacheStore::queryRealisationUncached(
|
||||||
if (!data)
|
if (!data)
|
||||||
return (*callbackPtr)({});
|
return (*callbackPtr)({});
|
||||||
|
|
||||||
std::shared_ptr<const UnkeyedRealisation> realisation;
|
std::shared_ptr<const Realisation> realisation;
|
||||||
try {
|
try {
|
||||||
realisation = std::make_shared<const UnkeyedRealisation>(nlohmann::json::parse(*data));
|
realisation = std::make_shared<const Realisation>(nlohmann::json::parse(*data));
|
||||||
} catch (Error & e) {
|
} catch (Error & e) {
|
||||||
e.addTrace(
|
e.addTrace({}, "while parsing file '%s' as a realisation", outputInfoFilePath);
|
||||||
{}, "while parsing file '%s' as a realisation for key '%s'", outputInfoFilePath, id.to_string());
|
|
||||||
throw;
|
throw;
|
||||||
}
|
}
|
||||||
return (*callbackPtr)(std::move(realisation));
|
return (*callbackPtr)(std::move(realisation));
|
||||||
|
|
@ -541,7 +535,8 @@ 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");
|
auto filePath = realisationsPrefix + "/" + info.id.to_string() + ".doi";
|
||||||
|
upsertFile(filePath, static_cast<nlohmann::json>(info).dump(), "application/json");
|
||||||
}
|
}
|
||||||
|
|
||||||
ref<RemoteFSAccessor> BinaryCacheStore::getRemoteFSAccessor(bool requireValidPath)
|
ref<RemoteFSAccessor> BinaryCacheStore::getRemoteFSAccessor(bool requireValidPath)
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
#include "nix/store/build/derivation-building-goal.hh"
|
#include "nix/store/build/derivation-building-goal.hh"
|
||||||
#include "nix/store/build/derivation-env-desugar.hh"
|
#include "nix/store/build/derivation-env-desugar.hh"
|
||||||
|
#include "nix/store/build/derivation-trampoline-goal.hh"
|
||||||
#ifndef _WIN32 // TODO enable build hook on Windows
|
#ifndef _WIN32 // TODO enable build hook on Windows
|
||||||
# include "nix/store/build/hook-instance.hh"
|
# include "nix/store/build/hook-instance.hh"
|
||||||
# include "nix/store/build/derivation-builder.hh"
|
# include "nix/store/build/derivation-builder.hh"
|
||||||
|
|
@ -26,8 +27,8 @@
|
||||||
namespace nix {
|
namespace nix {
|
||||||
|
|
||||||
DerivationBuildingGoal::DerivationBuildingGoal(
|
DerivationBuildingGoal::DerivationBuildingGoal(
|
||||||
const StorePath & drvPath, const Derivation & drv_, Worker & worker, BuildMode buildMode, bool storeDerivation)
|
const StorePath & drvPath, const Derivation & drv_, Worker & worker, BuildMode buildMode)
|
||||||
: Goal(worker, gaveUpOnSubstitution(storeDerivation))
|
: Goal(worker, gaveUpOnSubstitution())
|
||||||
, drvPath(drvPath)
|
, drvPath(drvPath)
|
||||||
, buildMode(buildMode)
|
, buildMode(buildMode)
|
||||||
{
|
{
|
||||||
|
|
@ -124,10 +125,50 @@ static void runPostBuildHook(
|
||||||
|
|
||||||
/* At least one of the output paths could not be
|
/* At least one of the output paths could not be
|
||||||
produced using a substitute. So we have to build instead. */
|
produced using a substitute. So we have to build instead. */
|
||||||
Goal::Co DerivationBuildingGoal::gaveUpOnSubstitution(bool storeDerivation)
|
Goal::Co DerivationBuildingGoal::gaveUpOnSubstitution()
|
||||||
{
|
{
|
||||||
Goals waitees;
|
Goals waitees;
|
||||||
|
|
||||||
|
std::map<ref<const SingleDerivedPath>, GoalPtr, value_comparison> inputGoals;
|
||||||
|
|
||||||
|
{
|
||||||
|
std::function<void(ref<const SingleDerivedPath>, const DerivedPathMap<StringSet>::ChildNode &)>
|
||||||
|
addWaiteeDerivedPath;
|
||||||
|
|
||||||
|
addWaiteeDerivedPath = [&](ref<const SingleDerivedPath> inputDrv,
|
||||||
|
const DerivedPathMap<StringSet>::ChildNode & inputNode) {
|
||||||
|
if (!inputNode.value.empty()) {
|
||||||
|
auto g = worker.makeGoal(
|
||||||
|
DerivedPath::Built{
|
||||||
|
.drvPath = inputDrv,
|
||||||
|
.outputs = inputNode.value,
|
||||||
|
},
|
||||||
|
buildMode == bmRepair ? bmRepair : bmNormal);
|
||||||
|
inputGoals.insert_or_assign(inputDrv, g);
|
||||||
|
waitees.insert(std::move(g));
|
||||||
|
}
|
||||||
|
for (const auto & [outputName, childNode] : inputNode.childMap)
|
||||||
|
addWaiteeDerivedPath(
|
||||||
|
make_ref<SingleDerivedPath>(SingleDerivedPath::Built{inputDrv, outputName}), childNode);
|
||||||
|
};
|
||||||
|
|
||||||
|
for (const auto & [inputDrvPath, inputNode] : drv->inputDrvs.map) {
|
||||||
|
/* Ensure that pure, non-fixed-output derivations don't
|
||||||
|
depend on impure derivations. */
|
||||||
|
if (experimentalFeatureSettings.isEnabled(Xp::ImpureDerivations) && !drv->type().isImpure()
|
||||||
|
&& !drv->type().isFixed()) {
|
||||||
|
auto inputDrv = worker.evalStore.readDerivation(inputDrvPath);
|
||||||
|
if (inputDrv.type().isImpure())
|
||||||
|
throw Error(
|
||||||
|
"pure derivation '%s' depends on impure derivation '%s'",
|
||||||
|
worker.store.printStorePath(drvPath),
|
||||||
|
worker.store.printStorePath(inputDrvPath));
|
||||||
|
}
|
||||||
|
|
||||||
|
addWaiteeDerivedPath(makeConstantStorePathRef(inputDrvPath), inputNode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* Copy the input sources from the eval store to the build
|
/* Copy the input sources from the eval store to the build
|
||||||
store.
|
store.
|
||||||
|
|
||||||
|
|
@ -172,17 +213,177 @@ Goal::Co DerivationBuildingGoal::gaveUpOnSubstitution(bool storeDerivation)
|
||||||
|
|
||||||
/* Determine the full set of input paths. */
|
/* Determine the full set of input paths. */
|
||||||
|
|
||||||
if (storeDerivation) {
|
/* First, the input derivations. */
|
||||||
assert(drv->inputDrvs.map.empty());
|
{
|
||||||
/* Store the resolved derivation, as part of the record of
|
auto & fullDrv = *drv;
|
||||||
what we're actually building */
|
|
||||||
writeDerivation(worker.store, *drv);
|
auto drvType = fullDrv.type();
|
||||||
|
bool resolveDrv =
|
||||||
|
std::visit(
|
||||||
|
overloaded{
|
||||||
|
[&](const DerivationType::InputAddressed & ia) {
|
||||||
|
/* must resolve if deferred. */
|
||||||
|
return ia.deferred;
|
||||||
|
},
|
||||||
|
[&](const DerivationType::ContentAddressed & ca) {
|
||||||
|
return !fullDrv.inputDrvs.map.empty()
|
||||||
|
&& (ca.fixed
|
||||||
|
/* Can optionally resolve if fixed, which is good
|
||||||
|
for avoiding unnecessary rebuilds. */
|
||||||
|
? experimentalFeatureSettings.isEnabled(Xp::CaDerivations)
|
||||||
|
/* Must resolve if floating and there are any inputs
|
||||||
|
drvs. */
|
||||||
|
: true);
|
||||||
|
},
|
||||||
|
[&](const DerivationType::Impure &) { return true; }},
|
||||||
|
drvType.raw)
|
||||||
|
/* no inputs are outputs of dynamic derivations */
|
||||||
|
|| std::ranges::any_of(fullDrv.inputDrvs.map.begin(), fullDrv.inputDrvs.map.end(), [](auto & pair) {
|
||||||
|
return !pair.second.childMap.empty();
|
||||||
|
});
|
||||||
|
|
||||||
|
if (resolveDrv && !fullDrv.inputDrvs.map.empty()) {
|
||||||
|
experimentalFeatureSettings.require(Xp::CaDerivations);
|
||||||
|
|
||||||
|
/* We are be able to resolve this derivation based on the
|
||||||
|
now-known results of dependencies. If so, we become a
|
||||||
|
stub goal aliasing that resolved derivation goal. */
|
||||||
|
std::optional attempt = fullDrv.tryResolve(
|
||||||
|
worker.store,
|
||||||
|
[&](ref<const SingleDerivedPath> drvPath, const std::string & outputName) -> std::optional<StorePath> {
|
||||||
|
auto mEntry = get(inputGoals, drvPath);
|
||||||
|
if (!mEntry)
|
||||||
|
return std::nullopt;
|
||||||
|
|
||||||
|
auto & buildResult = (*mEntry)->buildResult;
|
||||||
|
return std::visit(
|
||||||
|
overloaded{
|
||||||
|
[](const BuildResult::Failure &) -> std::optional<StorePath> { return std::nullopt; },
|
||||||
|
[&](const BuildResult::Success & success) -> std::optional<StorePath> {
|
||||||
|
auto i = get(success.builtOutputs, outputName);
|
||||||
|
if (!i)
|
||||||
|
return std::nullopt;
|
||||||
|
|
||||||
|
return i->outPath;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
buildResult.inner);
|
||||||
|
});
|
||||||
|
if (!attempt) {
|
||||||
|
/* TODO (impure derivations-induced tech debt) (see below):
|
||||||
|
The above attempt should have found it, but because we manage
|
||||||
|
inputDrvOutputs statefully, sometimes it gets out of sync with
|
||||||
|
the real source of truth (store). So we query the store
|
||||||
|
directly if there's a problem. */
|
||||||
|
attempt = fullDrv.tryResolve(worker.store, &worker.evalStore);
|
||||||
|
}
|
||||||
|
assert(attempt);
|
||||||
|
Derivation drvResolved{std::move(*attempt)};
|
||||||
|
|
||||||
|
auto pathResolved = writeDerivation(worker.store, drvResolved);
|
||||||
|
|
||||||
|
auto msg =
|
||||||
|
fmt("resolved derivation: '%s' -> '%s'",
|
||||||
|
worker.store.printStorePath(drvPath),
|
||||||
|
worker.store.printStorePath(pathResolved));
|
||||||
|
act = std::make_unique<Activity>(
|
||||||
|
*logger,
|
||||||
|
lvlInfo,
|
||||||
|
actBuildWaiting,
|
||||||
|
msg,
|
||||||
|
Logger::Fields{
|
||||||
|
worker.store.printStorePath(drvPath),
|
||||||
|
worker.store.printStorePath(pathResolved),
|
||||||
|
});
|
||||||
|
|
||||||
|
/* TODO https://github.com/NixOS/nix/issues/13247 we should
|
||||||
|
let the calling goal do this, so it has a change to pass
|
||||||
|
just the output(s) it cares about. */
|
||||||
|
auto resolvedDrvGoal =
|
||||||
|
worker.makeDerivationTrampolineGoal(pathResolved, OutputsSpec::All{}, drvResolved, buildMode);
|
||||||
|
{
|
||||||
|
Goals waitees{resolvedDrvGoal};
|
||||||
|
co_await await(std::move(waitees));
|
||||||
|
}
|
||||||
|
|
||||||
|
trace("resolved derivation finished");
|
||||||
|
|
||||||
|
auto resolvedResult = resolvedDrvGoal->buildResult;
|
||||||
|
|
||||||
|
// No `std::visit` for coroutines yet
|
||||||
|
if (auto * successP = resolvedResult.tryGetSuccess()) {
|
||||||
|
auto & success = *successP;
|
||||||
|
SingleDrvOutputs builtOutputs;
|
||||||
|
|
||||||
|
auto outputHashes = staticOutputHashes(worker.evalStore, *drv);
|
||||||
|
auto resolvedHashes = staticOutputHashes(worker.store, drvResolved);
|
||||||
|
|
||||||
|
StorePathSet outputPaths;
|
||||||
|
|
||||||
|
for (auto & outputName : drvResolved.outputNames()) {
|
||||||
|
auto outputHash = get(outputHashes, outputName);
|
||||||
|
auto resolvedHash = get(resolvedHashes, outputName);
|
||||||
|
if ((!outputHash) || (!resolvedHash))
|
||||||
|
throw Error(
|
||||||
|
"derivation '%s' doesn't have expected output '%s' (derivation-goal.cc/resolve)",
|
||||||
|
worker.store.printStorePath(drvPath),
|
||||||
|
outputName);
|
||||||
|
|
||||||
|
auto realisation = [&] {
|
||||||
|
auto take1 = get(success.builtOutputs, outputName);
|
||||||
|
if (take1)
|
||||||
|
return *take1;
|
||||||
|
|
||||||
|
/* The above `get` should work. But stateful tracking of
|
||||||
|
outputs in resolvedResult, this can get out of sync with the
|
||||||
|
store, which is our actual source of truth. For now we just
|
||||||
|
check the store directly if it fails. */
|
||||||
|
auto take2 = worker.evalStore.queryRealisation(DrvOutput{*resolvedHash, outputName});
|
||||||
|
if (take2)
|
||||||
|
return *take2;
|
||||||
|
|
||||||
|
throw Error(
|
||||||
|
"derivation '%s' doesn't have expected output '%s' (derivation-goal.cc/realisation)",
|
||||||
|
worker.store.printStorePath(pathResolved),
|
||||||
|
outputName);
|
||||||
|
}();
|
||||||
|
|
||||||
|
if (!drv->type().isImpure()) {
|
||||||
|
auto newRealisation = realisation;
|
||||||
|
newRealisation.id = DrvOutput{*outputHash, outputName};
|
||||||
|
newRealisation.signatures.clear();
|
||||||
|
if (!drv->type().isFixed()) {
|
||||||
|
auto & drvStore = worker.evalStore.isValidPath(drvPath) ? worker.evalStore : worker.store;
|
||||||
|
newRealisation.dependentRealisations =
|
||||||
|
drvOutputReferences(worker.store, *drv, realisation.outPath, &drvStore);
|
||||||
|
}
|
||||||
|
worker.store.signRealisation(newRealisation);
|
||||||
|
worker.store.registerDrvOutput(newRealisation);
|
||||||
|
}
|
||||||
|
outputPaths.insert(realisation.outPath);
|
||||||
|
builtOutputs.emplace(outputName, realisation);
|
||||||
|
}
|
||||||
|
|
||||||
|
runPostBuildHook(worker.store, *logger, drvPath, outputPaths);
|
||||||
|
|
||||||
|
auto status = success.status;
|
||||||
|
if (status == BuildResult::Success::AlreadyValid)
|
||||||
|
status = BuildResult::Success::ResolvesToAlreadyValid;
|
||||||
|
|
||||||
|
co_return doneSuccess(success.status, std::move(builtOutputs));
|
||||||
|
} else if (resolvedResult.tryGetFailure()) {
|
||||||
|
co_return doneFailure({
|
||||||
|
BuildResult::Failure::DependencyFailed,
|
||||||
|
"build of resolved derivation '%s' failed",
|
||||||
|
worker.store.printStorePath(pathResolved),
|
||||||
|
});
|
||||||
|
} else
|
||||||
|
assert(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
|
||||||
/* If we get this far, we know no dynamic drvs inputs */
|
/* If we get this far, we know no dynamic drvs inputs */
|
||||||
|
|
||||||
for (auto & [depDrvPath, depNode] : drv->inputDrvs.map) {
|
for (auto & [depDrvPath, depNode] : fullDrv.inputDrvs.map) {
|
||||||
for (auto & outputName : depNode.value) {
|
for (auto & outputName : depNode.value) {
|
||||||
/* Don't need to worry about `inputGoals`, because
|
/* Don't need to worry about `inputGoals`, because
|
||||||
impure derivations are always resolved above. Can
|
impure derivations are always resolved above. Can
|
||||||
|
|
@ -1092,22 +1293,13 @@ DerivationBuildingGoal::checkPathValidity(std::map<std::string, InitialOutput> &
|
||||||
// without the `ca-derivations` experimental flag).
|
// without the `ca-derivations` experimental flag).
|
||||||
worker.store.registerDrvOutput(
|
worker.store.registerDrvOutput(
|
||||||
Realisation{
|
Realisation{
|
||||||
{
|
|
||||||
.outPath = info.known->path,
|
|
||||||
},
|
|
||||||
drvOutput,
|
drvOutput,
|
||||||
|
info.known->path,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (info.known && info.known->isValid())
|
if (info.known && info.known->isValid())
|
||||||
validOutputs.emplace(
|
validOutputs.emplace(i.first, Realisation{drvOutput, info.known->path});
|
||||||
i.first,
|
|
||||||
Realisation{
|
|
||||||
{
|
|
||||||
.outPath = info.known->path,
|
|
||||||
},
|
|
||||||
drvOutput,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool allValid = true;
|
bool allValid = true;
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
#include "nix/store/build/derivation-goal.hh"
|
#include "nix/store/build/derivation-goal.hh"
|
||||||
#include "nix/store/build/derivation-building-goal.hh"
|
#include "nix/store/build/derivation-building-goal.hh"
|
||||||
#include "nix/store/build/derivation-resolution-goal.hh"
|
|
||||||
#ifndef _WIN32 // TODO enable build hook on Windows
|
#ifndef _WIN32 // TODO enable build hook on Windows
|
||||||
# include "nix/store/build/hook-instance.hh"
|
# include "nix/store/build/hook-instance.hh"
|
||||||
# include "nix/store/build/derivation-builder.hh"
|
# include "nix/store/build/derivation-builder.hh"
|
||||||
|
|
@ -30,9 +29,8 @@ DerivationGoal::DerivationGoal(
|
||||||
const Derivation & drv,
|
const Derivation & drv,
|
||||||
const OutputName & wantedOutput,
|
const OutputName & wantedOutput,
|
||||||
Worker & worker,
|
Worker & worker,
|
||||||
BuildMode buildMode,
|
BuildMode buildMode)
|
||||||
bool storeDerivation)
|
: Goal(worker, haveDerivation())
|
||||||
: Goal(worker, haveDerivation(storeDerivation))
|
|
||||||
, drvPath(drvPath)
|
, drvPath(drvPath)
|
||||||
, wantedOutput(wantedOutput)
|
, wantedOutput(wantedOutput)
|
||||||
, outputHash{[&] {
|
, outputHash{[&] {
|
||||||
|
|
@ -66,7 +64,7 @@ std::string DerivationGoal::key()
|
||||||
}.to_string(worker.store);
|
}.to_string(worker.store);
|
||||||
}
|
}
|
||||||
|
|
||||||
Goal::Co DerivationGoal::haveDerivation(bool storeDerivation)
|
Goal::Co DerivationGoal::haveDerivation()
|
||||||
{
|
{
|
||||||
trace("have derivation");
|
trace("have derivation");
|
||||||
|
|
||||||
|
|
@ -148,104 +146,9 @@ Goal::Co DerivationGoal::haveDerivation(bool storeDerivation)
|
||||||
worker.store.printStorePath(drvPath));
|
worker.store.printStorePath(drvPath));
|
||||||
}
|
}
|
||||||
|
|
||||||
auto resolutionGoal = worker.makeDerivationResolutionGoal(drvPath, *drv, buildMode);
|
|
||||||
{
|
|
||||||
Goals waitees{resolutionGoal};
|
|
||||||
co_await await(std::move(waitees));
|
|
||||||
}
|
|
||||||
if (nrFailed != 0) {
|
|
||||||
co_return doneFailure({BuildResult::Failure::DependencyFailed, "resolution failed"});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (resolutionGoal->resolvedDrv) {
|
|
||||||
auto & [pathResolved, drvResolved] = *resolutionGoal->resolvedDrv;
|
|
||||||
|
|
||||||
auto resolvedDrvGoal =
|
|
||||||
worker.makeDerivationGoal(pathResolved, drvResolved, wantedOutput, buildMode, /*storeDerivation=*/true);
|
|
||||||
{
|
|
||||||
Goals waitees{resolvedDrvGoal};
|
|
||||||
co_await await(std::move(waitees));
|
|
||||||
}
|
|
||||||
|
|
||||||
trace("resolved derivation finished");
|
|
||||||
|
|
||||||
auto resolvedResult = resolvedDrvGoal->buildResult;
|
|
||||||
|
|
||||||
// No `std::visit` for coroutines yet
|
|
||||||
if (auto * successP = resolvedResult.tryGetSuccess()) {
|
|
||||||
auto & success = *successP;
|
|
||||||
auto outputHashes = staticOutputHashes(worker.evalStore, *drv);
|
|
||||||
auto resolvedHashes = staticOutputHashes(worker.store, drvResolved);
|
|
||||||
|
|
||||||
StorePathSet outputPaths;
|
|
||||||
|
|
||||||
auto outputHash = get(outputHashes, wantedOutput);
|
|
||||||
auto resolvedHash = get(resolvedHashes, wantedOutput);
|
|
||||||
if ((!outputHash) || (!resolvedHash))
|
|
||||||
throw Error(
|
|
||||||
"derivation '%s' doesn't have expected output '%s' (derivation-goal.cc/resolve)",
|
|
||||||
worker.store.printStorePath(drvPath),
|
|
||||||
wantedOutput);
|
|
||||||
|
|
||||||
auto realisation = [&] {
|
|
||||||
auto take1 = get(success.builtOutputs, wantedOutput);
|
|
||||||
if (take1)
|
|
||||||
return static_cast<UnkeyedRealisation>(*take1);
|
|
||||||
|
|
||||||
/* The above `get` should work. But stateful tracking of
|
|
||||||
outputs in resolvedResult, this can get out of sync with the
|
|
||||||
store, which is our actual source of truth. For now we just
|
|
||||||
check the store directly if it fails. */
|
|
||||||
auto take2 = worker.evalStore.queryRealisation(
|
|
||||||
DrvOutput{
|
|
||||||
.drvHash = *resolvedHash,
|
|
||||||
.outputName = wantedOutput,
|
|
||||||
});
|
|
||||||
if (take2)
|
|
||||||
return *take2;
|
|
||||||
|
|
||||||
throw Error(
|
|
||||||
"derivation '%s' doesn't have expected output '%s' (derivation-goal.cc/realisation)",
|
|
||||||
worker.store.printStorePath(pathResolved),
|
|
||||||
wantedOutput);
|
|
||||||
}();
|
|
||||||
|
|
||||||
if (!drv->type().isImpure()) {
|
|
||||||
Realisation newRealisation{
|
|
||||||
realisation,
|
|
||||||
{
|
|
||||||
.drvHash = *outputHash,
|
|
||||||
.outputName = wantedOutput,
|
|
||||||
}};
|
|
||||||
newRealisation.signatures.clear();
|
|
||||||
if (!drv->type().isFixed()) {
|
|
||||||
auto & drvStore = worker.evalStore.isValidPath(drvPath) ? worker.evalStore : worker.store;
|
|
||||||
newRealisation.dependentRealisations =
|
|
||||||
drvOutputReferences(worker.store, *drv, realisation.outPath, &drvStore);
|
|
||||||
}
|
|
||||||
worker.store.signRealisation(newRealisation);
|
|
||||||
worker.store.registerDrvOutput(newRealisation);
|
|
||||||
}
|
|
||||||
outputPaths.insert(realisation.outPath);
|
|
||||||
|
|
||||||
auto status = success.status;
|
|
||||||
if (status == BuildResult::Success::AlreadyValid)
|
|
||||||
status = BuildResult::Success::ResolvesToAlreadyValid;
|
|
||||||
|
|
||||||
co_return doneSuccess(status, std::move(realisation));
|
|
||||||
} else if (resolvedResult.tryGetFailure()) {
|
|
||||||
co_return doneFailure({
|
|
||||||
BuildResult::Failure::DependencyFailed,
|
|
||||||
"build of resolved derivation '%s' failed",
|
|
||||||
worker.store.printStorePath(pathResolved),
|
|
||||||
});
|
|
||||||
} else
|
|
||||||
assert(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Give up on substitution for the output we want, actually build this derivation */
|
/* Give up on substitution for the output we want, actually build this derivation */
|
||||||
|
|
||||||
auto g = worker.makeDerivationBuildingGoal(drvPath, *drv, buildMode, storeDerivation);
|
auto g = worker.makeDerivationBuildingGoal(drvPath, *drv, buildMode);
|
||||||
|
|
||||||
/* We will finish with it ourselves, as if we were the derivational goal. */
|
/* We will finish with it ourselves, as if we were the derivational goal. */
|
||||||
g->preserveException = true;
|
g->preserveException = true;
|
||||||
|
|
@ -266,16 +169,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
|
||||||
|
|
@ -390,20 +284,18 @@ Goal::Co DerivationGoal::repairClosure()
|
||||||
co_return doneSuccess(BuildResult::Success::AlreadyValid, assertPathValidity());
|
co_return doneSuccess(BuildResult::Success::AlreadyValid, assertPathValidity());
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<std::pair<UnkeyedRealisation, PathStatus>> DerivationGoal::checkPathValidity()
|
std::optional<std::pair<Realisation, PathStatus>> DerivationGoal::checkPathValidity()
|
||||||
{
|
{
|
||||||
if (drv->type().isImpure())
|
if (drv->type().isImpure())
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
|
|
||||||
auto drvOutput = DrvOutput{outputHash, wantedOutput};
|
auto drvOutput = DrvOutput{outputHash, wantedOutput};
|
||||||
|
|
||||||
std::optional<UnkeyedRealisation> mRealisation;
|
std::optional<Realisation> mRealisation;
|
||||||
|
|
||||||
if (auto * mOutput = get(drv->outputs, wantedOutput)) {
|
if (auto * mOutput = get(drv->outputs, wantedOutput)) {
|
||||||
if (auto mPath = mOutput->path(worker.store, drv->name, wantedOutput)) {
|
if (auto mPath = mOutput->path(worker.store, drv->name, wantedOutput)) {
|
||||||
mRealisation = UnkeyedRealisation{
|
mRealisation = Realisation{drvOutput, std::move(*mPath)};
|
||||||
.outPath = std::move(*mPath),
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
throw Error(
|
throw Error(
|
||||||
|
|
@ -431,14 +323,7 @@ std::optional<std::pair<UnkeyedRealisation, PathStatus>> DerivationGoal::checkPa
|
||||||
// derivation, and the output path is valid, but we don't have
|
// derivation, and the output path is valid, but we don't have
|
||||||
// its realisation stored (probably because it has been built
|
// its realisation stored (probably because it has been built
|
||||||
// without the `ca-derivations` experimental flag).
|
// without the `ca-derivations` experimental flag).
|
||||||
worker.store.registerDrvOutput(
|
worker.store.registerDrvOutput(*mRealisation);
|
||||||
Realisation{
|
|
||||||
*mRealisation,
|
|
||||||
{
|
|
||||||
.drvHash = outputHash,
|
|
||||||
.outputName = wantedOutput,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return {{*mRealisation, status}};
|
return {{*mRealisation, status}};
|
||||||
|
|
@ -446,7 +331,7 @@ std::optional<std::pair<UnkeyedRealisation, PathStatus>> DerivationGoal::checkPa
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
UnkeyedRealisation DerivationGoal::assertPathValidity()
|
Realisation DerivationGoal::assertPathValidity()
|
||||||
{
|
{
|
||||||
auto checkResult = checkPathValidity();
|
auto checkResult = checkPathValidity();
|
||||||
if (!(checkResult && checkResult->second == PathStatus::Valid))
|
if (!(checkResult && checkResult->second == PathStatus::Valid))
|
||||||
|
|
@ -454,20 +339,11 @@ UnkeyedRealisation DerivationGoal::assertPathValidity()
|
||||||
return checkResult->first;
|
return checkResult->first;
|
||||||
}
|
}
|
||||||
|
|
||||||
Goal::Done DerivationGoal::doneSuccess(BuildResult::Success::Status status, UnkeyedRealisation builtOutput)
|
Goal::Done DerivationGoal::doneSuccess(BuildResult::Success::Status status, Realisation builtOutput)
|
||||||
{
|
{
|
||||||
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();
|
||||||
|
|
|
||||||
|
|
@ -1,210 +0,0 @@
|
||||||
#include "nix/store/build/derivation-resolution-goal.hh"
|
|
||||||
#include "nix/store/build/derivation-env-desugar.hh"
|
|
||||||
#include "nix/store/build/worker.hh"
|
|
||||||
#include "nix/util/util.hh"
|
|
||||||
#include "nix/store/common-protocol.hh"
|
|
||||||
#include "nix/store/globals.hh"
|
|
||||||
|
|
||||||
#include <sys/types.h>
|
|
||||||
#include <fcntl.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
|
|
||||||
#include <nlohmann/json.hpp>
|
|
||||||
|
|
||||||
namespace nix {
|
|
||||||
|
|
||||||
DerivationResolutionGoal::DerivationResolutionGoal(
|
|
||||||
const StorePath & drvPath, const Derivation & drv_, Worker & worker, BuildMode buildMode)
|
|
||||||
: Goal(worker, resolveDerivation())
|
|
||||||
, drvPath(drvPath)
|
|
||||||
{
|
|
||||||
drv = std::make_unique<Derivation>(drv_);
|
|
||||||
|
|
||||||
name = fmt("building of '%s' from in-memory derivation", worker.store.printStorePath(drvPath));
|
|
||||||
trace("created");
|
|
||||||
|
|
||||||
/* Prevent the .chroot directory from being
|
|
||||||
garbage-collected. (See isActiveTempFile() in gc.cc.) */
|
|
||||||
worker.store.addTempRoot(this->drvPath);
|
|
||||||
}
|
|
||||||
|
|
||||||
void DerivationResolutionGoal::timedOut(Error && ex) {}
|
|
||||||
|
|
||||||
std::string DerivationResolutionGoal::key()
|
|
||||||
{
|
|
||||||
/* Ensure that derivations get built in order of their name,
|
|
||||||
i.e. a derivation named "aardvark" always comes before
|
|
||||||
"baboon". And substitution goals always happen before
|
|
||||||
derivation goals (due to "bd$"). */
|
|
||||||
return "rd$" + std::string(drvPath.name()) + "$" + worker.store.printStorePath(drvPath);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Used for `inputGoals` local variable below
|
|
||||||
*/
|
|
||||||
struct value_comparison
|
|
||||||
{
|
|
||||||
template<typename T>
|
|
||||||
bool operator()(const ref<T> & lhs, const ref<T> & rhs) const
|
|
||||||
{
|
|
||||||
return *lhs < *rhs;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/* At least one of the output paths could not be
|
|
||||||
produced using a substitute. So we have to build instead. */
|
|
||||||
Goal::Co DerivationResolutionGoal::resolveDerivation()
|
|
||||||
{
|
|
||||||
Goals waitees;
|
|
||||||
|
|
||||||
std::map<ref<const SingleDerivedPath>, GoalPtr, value_comparison> inputGoals;
|
|
||||||
|
|
||||||
{
|
|
||||||
std::function<void(ref<const SingleDerivedPath>, const DerivedPathMap<StringSet>::ChildNode &)>
|
|
||||||
addWaiteeDerivedPath;
|
|
||||||
|
|
||||||
addWaiteeDerivedPath = [&](ref<const SingleDerivedPath> inputDrv,
|
|
||||||
const DerivedPathMap<StringSet>::ChildNode & inputNode) {
|
|
||||||
if (!inputNode.value.empty()) {
|
|
||||||
auto g = worker.makeGoal(
|
|
||||||
DerivedPath::Built{
|
|
||||||
.drvPath = inputDrv,
|
|
||||||
.outputs = inputNode.value,
|
|
||||||
},
|
|
||||||
buildMode == bmRepair ? bmRepair : bmNormal);
|
|
||||||
inputGoals.insert_or_assign(inputDrv, g);
|
|
||||||
waitees.insert(std::move(g));
|
|
||||||
}
|
|
||||||
for (const auto & [outputName, childNode] : inputNode.childMap)
|
|
||||||
addWaiteeDerivedPath(
|
|
||||||
make_ref<SingleDerivedPath>(SingleDerivedPath::Built{inputDrv, outputName}), childNode);
|
|
||||||
};
|
|
||||||
|
|
||||||
for (const auto & [inputDrvPath, inputNode] : drv->inputDrvs.map) {
|
|
||||||
/* Ensure that pure, non-fixed-output derivations don't
|
|
||||||
depend on impure derivations. */
|
|
||||||
if (experimentalFeatureSettings.isEnabled(Xp::ImpureDerivations) && !drv->type().isImpure()
|
|
||||||
&& !drv->type().isFixed()) {
|
|
||||||
auto inputDrv = worker.evalStore.readDerivation(inputDrvPath);
|
|
||||||
if (inputDrv.type().isImpure())
|
|
||||||
throw Error(
|
|
||||||
"pure derivation '%s' depends on impure derivation '%s'",
|
|
||||||
worker.store.printStorePath(drvPath),
|
|
||||||
worker.store.printStorePath(inputDrvPath));
|
|
||||||
}
|
|
||||||
|
|
||||||
addWaiteeDerivedPath(makeConstantStorePathRef(inputDrvPath), inputNode);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
co_await await(std::move(waitees));
|
|
||||||
|
|
||||||
trace("all inputs realised");
|
|
||||||
|
|
||||||
if (nrFailed != 0) {
|
|
||||||
auto msg =
|
|
||||||
fmt("Cannot build '%s'.\n"
|
|
||||||
"Reason: " ANSI_RED "%d %s failed" ANSI_NORMAL ".",
|
|
||||||
Magenta(worker.store.printStorePath(drvPath)),
|
|
||||||
nrFailed,
|
|
||||||
nrFailed == 1 ? "dependency" : "dependencies");
|
|
||||||
msg += showKnownOutputs(worker.store, *drv);
|
|
||||||
co_return amDone(ecFailed, {BuildError(BuildResult::Failure::DependencyFailed, msg)});
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Gather information necessary for computing the closure and/or
|
|
||||||
running the build hook. */
|
|
||||||
|
|
||||||
/* Determine the full set of input paths. */
|
|
||||||
|
|
||||||
/* First, the input derivations. */
|
|
||||||
{
|
|
||||||
auto & fullDrv = *drv;
|
|
||||||
|
|
||||||
auto drvType = fullDrv.type();
|
|
||||||
bool resolveDrv =
|
|
||||||
std::visit(
|
|
||||||
overloaded{
|
|
||||||
[&](const DerivationType::InputAddressed & ia) {
|
|
||||||
/* must resolve if deferred. */
|
|
||||||
return ia.deferred;
|
|
||||||
},
|
|
||||||
[&](const DerivationType::ContentAddressed & ca) {
|
|
||||||
return !fullDrv.inputDrvs.map.empty()
|
|
||||||
&& (ca.fixed
|
|
||||||
/* Can optionally resolve if fixed, which is good
|
|
||||||
for avoiding unnecessary rebuilds. */
|
|
||||||
? experimentalFeatureSettings.isEnabled(Xp::CaDerivations)
|
|
||||||
/* Must resolve if floating and there are any inputs
|
|
||||||
drvs. */
|
|
||||||
: true);
|
|
||||||
},
|
|
||||||
[&](const DerivationType::Impure &) { return true; }},
|
|
||||||
drvType.raw)
|
|
||||||
/* no inputs are outputs of dynamic derivations */
|
|
||||||
|| std::ranges::any_of(fullDrv.inputDrvs.map.begin(), fullDrv.inputDrvs.map.end(), [](auto & pair) {
|
|
||||||
return !pair.second.childMap.empty();
|
|
||||||
});
|
|
||||||
|
|
||||||
if (resolveDrv && !fullDrv.inputDrvs.map.empty()) {
|
|
||||||
experimentalFeatureSettings.require(Xp::CaDerivations);
|
|
||||||
|
|
||||||
/* We are be able to resolve this derivation based on the
|
|
||||||
now-known results of dependencies. If so, we become a
|
|
||||||
stub goal aliasing that resolved derivation goal. */
|
|
||||||
std::optional attempt = fullDrv.tryResolve(
|
|
||||||
worker.store,
|
|
||||||
[&](ref<const SingleDerivedPath> drvPath, const std::string & outputName) -> std::optional<StorePath> {
|
|
||||||
auto mEntry = get(inputGoals, drvPath);
|
|
||||||
if (!mEntry)
|
|
||||||
return std::nullopt;
|
|
||||||
|
|
||||||
auto & buildResult = (*mEntry)->buildResult;
|
|
||||||
return std::visit(
|
|
||||||
overloaded{
|
|
||||||
[](const BuildResult::Failure &) -> std::optional<StorePath> { return std::nullopt; },
|
|
||||||
[&](const BuildResult::Success & success) -> std::optional<StorePath> {
|
|
||||||
auto i = get(success.builtOutputs, outputName);
|
|
||||||
if (!i)
|
|
||||||
return std::nullopt;
|
|
||||||
|
|
||||||
return i->outPath;
|
|
||||||
},
|
|
||||||
},
|
|
||||||
buildResult.inner);
|
|
||||||
});
|
|
||||||
if (!attempt) {
|
|
||||||
/* TODO (impure derivations-induced tech debt) (see below):
|
|
||||||
The above attempt should have found it, but because we manage
|
|
||||||
inputDrvOutputs statefully, sometimes it gets out of sync with
|
|
||||||
the real source of truth (store). So we query the store
|
|
||||||
directly if there's a problem. */
|
|
||||||
attempt = fullDrv.tryResolve(worker.store, &worker.evalStore);
|
|
||||||
}
|
|
||||||
assert(attempt);
|
|
||||||
|
|
||||||
auto pathResolved = writeDerivation(worker.store, *attempt, NoRepair, /*readOnly =*/true);
|
|
||||||
|
|
||||||
auto msg =
|
|
||||||
fmt("resolved derivation: '%s' -> '%s'",
|
|
||||||
worker.store.printStorePath(drvPath),
|
|
||||||
worker.store.printStorePath(pathResolved));
|
|
||||||
act = std::make_unique<Activity>(
|
|
||||||
*logger,
|
|
||||||
lvlInfo,
|
|
||||||
actBuildWaiting,
|
|
||||||
msg,
|
|
||||||
Logger::Fields{
|
|
||||||
worker.store.printStorePath(drvPath),
|
|
||||||
worker.store.printStorePath(pathResolved),
|
|
||||||
});
|
|
||||||
|
|
||||||
resolvedDrv =
|
|
||||||
std::make_unique<std::pair<StorePath, BasicDerivation>>(std::move(pathResolved), *std::move(attempt));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
co_return amDone(ecSuccess, std::nullopt);
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace nix
|
|
||||||
|
|
@ -43,10 +43,10 @@ Goal::Co DrvOutputSubstitutionGoal::init()
|
||||||
outPipe->createAsyncPipe(worker.ioport.get());
|
outPipe->createAsyncPipe(worker.ioport.get());
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
auto promise = std::make_shared<std::promise<std::shared_ptr<const UnkeyedRealisation>>>();
|
auto promise = std::make_shared<std::promise<std::shared_ptr<const Realisation>>>();
|
||||||
|
|
||||||
sub->queryRealisation(
|
sub->queryRealisation(
|
||||||
id, {[outPipe(outPipe), promise(promise)](std::future<std::shared_ptr<const UnkeyedRealisation>> res) {
|
id, {[outPipe(outPipe), promise(promise)](std::future<std::shared_ptr<const Realisation>> res) {
|
||||||
try {
|
try {
|
||||||
Finally updateStats([&]() { outPipe->writeSide.close(); });
|
Finally updateStats([&]() { outPipe->writeSide.close(); });
|
||||||
promise->set_value(res.get());
|
promise->set_value(res.get());
|
||||||
|
|
@ -75,7 +75,7 @@ Goal::Co DrvOutputSubstitutionGoal::init()
|
||||||
* The realisation corresponding to the given output id.
|
* The realisation corresponding to the given output id.
|
||||||
* Will be filled once we can get it.
|
* Will be filled once we can get it.
|
||||||
*/
|
*/
|
||||||
std::shared_ptr<const UnkeyedRealisation> outputInfo;
|
std::shared_ptr<const Realisation> outputInfo;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
outputInfo = promise->get_future().get();
|
outputInfo = promise->get_future().get();
|
||||||
|
|
@ -132,7 +132,7 @@ Goal::Co DrvOutputSubstitutionGoal::init()
|
||||||
}
|
}
|
||||||
|
|
||||||
Goal::Co DrvOutputSubstitutionGoal::realisationFetched(
|
Goal::Co DrvOutputSubstitutionGoal::realisationFetched(
|
||||||
Goals waitees, std::shared_ptr<const UnkeyedRealisation> outputInfo, nix::ref<nix::Store> sub)
|
Goals waitees, std::shared_ptr<const Realisation> outputInfo, nix::ref<nix::Store> sub)
|
||||||
{
|
{
|
||||||
waitees.insert(worker.makePathSubstitutionGoal(outputInfo->outPath));
|
waitees.insert(worker.makePathSubstitutionGoal(outputInfo->outPath));
|
||||||
|
|
||||||
|
|
@ -145,7 +145,7 @@ Goal::Co DrvOutputSubstitutionGoal::realisationFetched(
|
||||||
co_return amDone(nrNoSubstituters > 0 ? ecNoSubstituters : ecFailed);
|
co_return amDone(nrNoSubstituters > 0 ? ecNoSubstituters : ecFailed);
|
||||||
}
|
}
|
||||||
|
|
||||||
worker.store.registerDrvOutput({*outputInfo, id});
|
worker.store.registerDrvOutput(*outputInfo);
|
||||||
|
|
||||||
trace("finished");
|
trace("finished");
|
||||||
co_return amDone(ecSuccess);
|
co_return amDone(ecSuccess);
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,6 @@
|
||||||
#include "nix/store/build/substitution-goal.hh"
|
#include "nix/store/build/substitution-goal.hh"
|
||||||
#include "nix/store/build/drv-output-substitution-goal.hh"
|
#include "nix/store/build/drv-output-substitution-goal.hh"
|
||||||
#include "nix/store/build/derivation-goal.hh"
|
#include "nix/store/build/derivation-goal.hh"
|
||||||
#include "nix/store/build/derivation-resolution-goal.hh"
|
|
||||||
#include "nix/store/build/derivation-building-goal.hh"
|
#include "nix/store/build/derivation-building-goal.hh"
|
||||||
#include "nix/store/build/derivation-trampoline-goal.hh"
|
#include "nix/store/build/derivation-trampoline-goal.hh"
|
||||||
#ifndef _WIN32 // TODO Enable building on Windows
|
#ifndef _WIN32 // TODO Enable building on Windows
|
||||||
|
|
@ -76,26 +75,15 @@ std::shared_ptr<DerivationTrampolineGoal> Worker::makeDerivationTrampolineGoal(
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<DerivationGoal> Worker::makeDerivationGoal(
|
std::shared_ptr<DerivationGoal> Worker::makeDerivationGoal(
|
||||||
const StorePath & drvPath,
|
const StorePath & drvPath, const Derivation & drv, const OutputName & wantedOutput, BuildMode buildMode)
|
||||||
const Derivation & drv,
|
|
||||||
const OutputName & wantedOutput,
|
|
||||||
BuildMode buildMode,
|
|
||||||
bool storeDerivation)
|
|
||||||
{
|
{
|
||||||
return initGoalIfNeeded(
|
return initGoalIfNeeded(derivationGoals[drvPath][wantedOutput], drvPath, drv, wantedOutput, *this, buildMode);
|
||||||
derivationGoals[drvPath][wantedOutput], drvPath, drv, wantedOutput, *this, buildMode, storeDerivation);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<DerivationResolutionGoal>
|
std::shared_ptr<DerivationBuildingGoal>
|
||||||
Worker::makeDerivationResolutionGoal(const StorePath & drvPath, const Derivation & drv, BuildMode buildMode)
|
Worker::makeDerivationBuildingGoal(const StorePath & drvPath, const Derivation & drv, BuildMode buildMode)
|
||||||
{
|
{
|
||||||
return initGoalIfNeeded(derivationResolutionGoals[drvPath], drvPath, drv, *this, buildMode);
|
return initGoalIfNeeded(derivationBuildingGoals[drvPath], drvPath, drv, *this, buildMode);
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<DerivationBuildingGoal> Worker::makeDerivationBuildingGoal(
|
|
||||||
const StorePath & drvPath, const Derivation & drv, BuildMode buildMode, bool storeDerivation)
|
|
||||||
{
|
|
||||||
return initGoalIfNeeded(derivationBuildingGoals[drvPath], drvPath, drv, *this, buildMode, storeDerivation);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<PathSubstitutionGoal>
|
std::shared_ptr<PathSubstitutionGoal>
|
||||||
|
|
@ -170,8 +158,6 @@ void Worker::removeGoal(GoalPtr goal)
|
||||||
nix::removeGoal(drvGoal, derivationTrampolineGoals.map);
|
nix::removeGoal(drvGoal, derivationTrampolineGoals.map);
|
||||||
else if (auto drvGoal = std::dynamic_pointer_cast<DerivationGoal>(goal))
|
else if (auto drvGoal = std::dynamic_pointer_cast<DerivationGoal>(goal))
|
||||||
nix::removeGoal(drvGoal, derivationGoals);
|
nix::removeGoal(drvGoal, derivationGoals);
|
||||||
else if (auto drvResolutionGoal = std::dynamic_pointer_cast<DerivationResolutionGoal>(goal))
|
|
||||||
nix::removeGoal(drvResolutionGoal, derivationResolutionGoals);
|
|
||||||
else if (auto drvBuildingGoal = std::dynamic_pointer_cast<DerivationBuildingGoal>(goal))
|
else if (auto drvBuildingGoal = std::dynamic_pointer_cast<DerivationBuildingGoal>(goal))
|
||||||
nix::removeGoal(drvBuildingGoal, derivationBuildingGoals);
|
nix::removeGoal(drvBuildingGoal, derivationBuildingGoals);
|
||||||
else if (auto subGoal = std::dynamic_pointer_cast<PathSubstitutionGoal>(goal))
|
else if (auto subGoal = std::dynamic_pointer_cast<PathSubstitutionGoal>(goal))
|
||||||
|
|
|
||||||
|
|
@ -964,7 +964,7 @@ static void performOp(
|
||||||
if (GET_PROTOCOL_MINOR(conn.protoVersion) < 31) {
|
if (GET_PROTOCOL_MINOR(conn.protoVersion) < 31) {
|
||||||
auto outputId = DrvOutput::parse(readString(conn.from));
|
auto outputId = DrvOutput::parse(readString(conn.from));
|
||||||
auto outputPath = StorePath(readString(conn.from));
|
auto outputPath = StorePath(readString(conn.from));
|
||||||
store->registerDrvOutput(Realisation{{.outPath = outputPath}, outputId});
|
store->registerDrvOutput(Realisation{.id = outputId, .outPath = outputPath});
|
||||||
} else {
|
} else {
|
||||||
auto realisation = WorkerProto::Serialise<Realisation>::read(*store, rconn);
|
auto realisation = WorkerProto::Serialise<Realisation>::read(*store, rconn);
|
||||||
store->registerDrvOutput(realisation);
|
store->registerDrvOutput(realisation);
|
||||||
|
|
@ -986,7 +986,7 @@ static void performOp(
|
||||||
} else {
|
} else {
|
||||||
std::set<Realisation> realisations;
|
std::set<Realisation> realisations;
|
||||||
if (info)
|
if (info)
|
||||||
realisations.insert({*info, outputId});
|
realisations.insert(*info);
|
||||||
WorkerProto::write(*store, wconn, realisations);
|
WorkerProto::write(*store, wconn, realisations);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,6 @@
|
||||||
#include "nix/util/callback.hh"
|
#include "nix/util/callback.hh"
|
||||||
#include "nix/util/memory-source-accessor.hh"
|
#include "nix/util/memory-source-accessor.hh"
|
||||||
#include "nix/store/dummy-store-impl.hh"
|
#include "nix/store/dummy-store-impl.hh"
|
||||||
#include "nix/store/realisation.hh"
|
|
||||||
|
|
||||||
#include <boost/unordered/concurrent_flat_map.hpp>
|
#include <boost/unordered/concurrent_flat_map.hpp>
|
||||||
|
|
||||||
|
|
@ -252,10 +251,7 @@ struct DummyStoreImpl : DummyStore
|
||||||
|
|
||||||
void registerDrvOutput(const Realisation & output) override
|
void registerDrvOutput(const Realisation & output) override
|
||||||
{
|
{
|
||||||
auto ref = make_ref<UnkeyedRealisation>(output);
|
unsupported("registerDrvOutput");
|
||||||
buildTrace.insert_or_visit({output.id.drvHash, {{output.id.outputName, ref}}}, [&](auto & kv) {
|
|
||||||
kv.second.insert_or_assign(output.id.outputName, make_ref<UnkeyedRealisation>(output));
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void narFromPath(const StorePath & path, Sink & sink) override
|
void narFromPath(const StorePath & path, Sink & sink) override
|
||||||
|
|
@ -270,18 +266,9 @@ struct DummyStoreImpl : DummyStore
|
||||||
throw Error("path '%s' is not valid", printStorePath(path));
|
throw Error("path '%s' is not valid", printStorePath(path));
|
||||||
}
|
}
|
||||||
|
|
||||||
void queryRealisationUncached(
|
void
|
||||||
const DrvOutput & drvOutput, Callback<std::shared_ptr<const UnkeyedRealisation>> callback) noexcept override
|
queryRealisationUncached(const DrvOutput &, Callback<std::shared_ptr<const Realisation>> callback) noexcept override
|
||||||
{
|
{
|
||||||
bool visited = false;
|
|
||||||
buildTrace.cvisit(drvOutput.drvHash, [&](const auto & kv) {
|
|
||||||
if (auto it = kv.second.find(drvOutput.outputName); it != kv.second.end()) {
|
|
||||||
visited = true;
|
|
||||||
callback(it->second.get_ptr());
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!visited)
|
|
||||||
callback(nullptr);
|
callback(nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -80,22 +80,13 @@ private:
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
||||||
/**
|
// The prefix under which realisation infos will be stored
|
||||||
* The prefix under which realisation infos will be stored
|
const std::string realisationsPrefix = "realisations";
|
||||||
*/
|
|
||||||
constexpr const static std::string realisationsPrefix = "realisations";
|
|
||||||
|
|
||||||
constexpr const static std::string cacheInfoFile = "nix-cache-info";
|
const std::string cacheInfoFile = "nix-cache-info";
|
||||||
|
|
||||||
BinaryCacheStore(Config &);
|
BinaryCacheStore(Config &);
|
||||||
|
|
||||||
/**
|
|
||||||
* Compute the path to the given realisation
|
|
||||||
*
|
|
||||||
* It's `${realisationsPrefix}/${drvOutput}.doi`.
|
|
||||||
*/
|
|
||||||
std::string makeRealisationPath(const DrvOutput & id);
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
virtual bool fileExists(const std::string & path) = 0;
|
virtual bool fileExists(const std::string & path) = 0;
|
||||||
|
|
@ -184,7 +175,7 @@ public:
|
||||||
void registerDrvOutput(const Realisation & info) override;
|
void registerDrvOutput(const Realisation & info) override;
|
||||||
|
|
||||||
void queryRealisationUncached(
|
void queryRealisationUncached(
|
||||||
const DrvOutput &, Callback<std::shared_ptr<const UnkeyedRealisation>> callback) noexcept override;
|
const DrvOutput &, Callback<std::shared_ptr<const Realisation>> callback) noexcept override;
|
||||||
|
|
||||||
void narFromPath(const StorePath & path, Sink & sink) override;
|
void narFromPath(const StorePath & path, Sink & sink) override;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -29,21 +29,8 @@ typedef enum { rpAccept, rpDecline, rpPostpone } HookReply;
|
||||||
*/
|
*/
|
||||||
struct DerivationBuildingGoal : public Goal
|
struct DerivationBuildingGoal : public Goal
|
||||||
{
|
{
|
||||||
/**
|
|
||||||
* @param storeDerivation Whether to store the derivation in
|
|
||||||
* `worker.store`. This is useful for newly-resolved derivations. In this
|
|
||||||
* case, the derivation was not created a priori, e.g. purely (or close
|
|
||||||
* enough) from evaluation of the Nix language, but also depends on the
|
|
||||||
* exact content produced by upstream builds. It is strongly advised to
|
|
||||||
* have a permanent record of such a resolved derivation in order to
|
|
||||||
* faithfully reconstruct the build history.
|
|
||||||
*/
|
|
||||||
DerivationBuildingGoal(
|
DerivationBuildingGoal(
|
||||||
const StorePath & drvPath,
|
const StorePath & drvPath, const Derivation & drv, Worker & worker, BuildMode buildMode = bmNormal);
|
||||||
const Derivation & drv,
|
|
||||||
Worker & worker,
|
|
||||||
BuildMode buildMode = bmNormal,
|
|
||||||
bool storeDerivation = false);
|
|
||||||
~DerivationBuildingGoal();
|
~DerivationBuildingGoal();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
@ -113,7 +100,7 @@ private:
|
||||||
/**
|
/**
|
||||||
* The states.
|
* The states.
|
||||||
*/
|
*/
|
||||||
Co gaveUpOnSubstitution(bool storeDerivation);
|
Co gaveUpOnSubstitution();
|
||||||
Co tryToBuild();
|
Co tryToBuild();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -168,7 +155,7 @@ private:
|
||||||
|
|
||||||
JobCategory jobCategory() const override
|
JobCategory jobCategory() const override
|
||||||
{
|
{
|
||||||
return JobCategory::Administration;
|
return JobCategory::Build;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -40,16 +40,12 @@ struct DerivationGoal : public Goal
|
||||||
*/
|
*/
|
||||||
OutputName wantedOutput;
|
OutputName wantedOutput;
|
||||||
|
|
||||||
/**
|
|
||||||
* @param storeDerivation See `DerivationBuildingGoal`. This is just passed along.
|
|
||||||
*/
|
|
||||||
DerivationGoal(
|
DerivationGoal(
|
||||||
const StorePath & drvPath,
|
const StorePath & drvPath,
|
||||||
const Derivation & drv,
|
const Derivation & drv,
|
||||||
const OutputName & wantedOutput,
|
const OutputName & wantedOutput,
|
||||||
Worker & worker,
|
Worker & worker,
|
||||||
BuildMode buildMode = bmNormal,
|
BuildMode buildMode = bmNormal);
|
||||||
bool storeDerivation = false);
|
|
||||||
~DerivationGoal() = default;
|
~DerivationGoal() = default;
|
||||||
|
|
||||||
void timedOut(Error && ex) override
|
void timedOut(Error && ex) override
|
||||||
|
|
@ -84,7 +80,7 @@ private:
|
||||||
/**
|
/**
|
||||||
* The states.
|
* The states.
|
||||||
*/
|
*/
|
||||||
Co haveDerivation(bool storeDerivation);
|
Co haveDerivation();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return `std::nullopt` if the output is unknown, e.g. un unbuilt
|
* Return `std::nullopt` if the output is unknown, e.g. un unbuilt
|
||||||
|
|
@ -93,17 +89,17 @@ private:
|
||||||
* of the wanted output, and a `PathStatus` with the
|
* of the wanted output, and a `PathStatus` with the
|
||||||
* current status of that output.
|
* current status of that output.
|
||||||
*/
|
*/
|
||||||
std::optional<std::pair<UnkeyedRealisation, PathStatus>> checkPathValidity();
|
std::optional<std::pair<Realisation, PathStatus>> checkPathValidity();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Aborts if any output is not valid or corrupt, and otherwise
|
* Aborts if any output is not valid or corrupt, and otherwise
|
||||||
* returns a 'Realisation' for the wanted output.
|
* returns a 'Realisation' for the wanted output.
|
||||||
*/
|
*/
|
||||||
UnkeyedRealisation assertPathValidity();
|
Realisation assertPathValidity();
|
||||||
|
|
||||||
Co repairClosure();
|
Co repairClosure();
|
||||||
|
|
||||||
Done doneSuccess(BuildResult::Success::Status status, UnkeyedRealisation builtOutput);
|
Done doneSuccess(BuildResult::Success::Status status, Realisation builtOutput);
|
||||||
|
|
||||||
Done doneFailure(BuildError ex);
|
Done doneFailure(BuildError ex);
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,82 +0,0 @@
|
||||||
#pragma once
|
|
||||||
///@file
|
|
||||||
|
|
||||||
#include "nix/store/derivations.hh"
|
|
||||||
#include "nix/store/derivation-options.hh"
|
|
||||||
#include "nix/store/build/derivation-building-misc.hh"
|
|
||||||
#include "nix/store/store-api.hh"
|
|
||||||
#include "nix/store/build/goal.hh"
|
|
||||||
|
|
||||||
namespace nix {
|
|
||||||
|
|
||||||
struct BuilderFailureError;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A goal for resolving a derivation. Resolving a derivation (@see
|
|
||||||
* `Derivation::tryResolve`) simplifies its inputs, replacing
|
|
||||||
* `inputDrvs` with `inputSrcs.
|
|
||||||
*
|
|
||||||
* Conceptually, we resolve all derivations. For input-addressed
|
|
||||||
* derivations (that don't transtively depend on content-addressed
|
|
||||||
* derivations), however, we don't actually use the resolved derivation,
|
|
||||||
* because the output paths would appear invalid (if we tried to verify
|
|
||||||
* them), since they are computed from the original, unresolved inputs.
|
|
||||||
*
|
|
||||||
* That said, if we ever made the new flavor of input-addressing as described
|
|
||||||
* in issue #9259, then the input-addressing would be based on the resolved
|
|
||||||
* inputs, and we like the CA case *would* use the output of this goal.
|
|
||||||
*
|
|
||||||
* (The point of this discussion is not to randomly stuff information on
|
|
||||||
* a yet-unimplemented feature (issue #9259) in the codebase, but
|
|
||||||
* rather, to illustrate that there is no inherent tension between
|
|
||||||
* explicit derivation resolution and input-addressing in general. That
|
|
||||||
* tension only exists with the type of input-addressing we've
|
|
||||||
* historically used.)
|
|
||||||
*/
|
|
||||||
struct DerivationResolutionGoal : public Goal
|
|
||||||
{
|
|
||||||
DerivationResolutionGoal(
|
|
||||||
const StorePath & drvPath, const Derivation & drv, Worker & worker, BuildMode buildMode = bmNormal);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* If the derivation needed to be resolved, this is resulting
|
|
||||||
* resolved derivations and its path.
|
|
||||||
*/
|
|
||||||
std::unique_ptr<std::pair<StorePath, BasicDerivation>> resolvedDrv;
|
|
||||||
|
|
||||||
void timedOut(Error && ex) override;
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The path of the derivation.
|
|
||||||
*/
|
|
||||||
StorePath drvPath;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The derivation stored at drvPath.
|
|
||||||
*/
|
|
||||||
std::unique_ptr<Derivation> drv;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The remainder is state held during the build.
|
|
||||||
*/
|
|
||||||
|
|
||||||
BuildMode buildMode;
|
|
||||||
|
|
||||||
std::unique_ptr<Activity> act;
|
|
||||||
|
|
||||||
std::string key() override;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The states.
|
|
||||||
*/
|
|
||||||
Co resolveDerivation();
|
|
||||||
|
|
||||||
JobCategory jobCategory() const override
|
|
||||||
{
|
|
||||||
return JobCategory::Administration;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace nix
|
|
||||||
|
|
@ -39,8 +39,7 @@ public:
|
||||||
GoalState state;
|
GoalState state;
|
||||||
|
|
||||||
Co init();
|
Co init();
|
||||||
Co
|
Co realisationFetched(Goals waitees, std::shared_ptr<const Realisation> outputInfo, nix::ref<nix::Store> sub);
|
||||||
realisationFetched(Goals waitees, std::shared_ptr<const UnkeyedRealisation> outputInfo, nix::ref<nix::Store> sub);
|
|
||||||
|
|
||||||
void timedOut(Error && ex) override
|
void timedOut(Error && ex) override
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,6 @@ namespace nix {
|
||||||
/* Forward definition. */
|
/* Forward definition. */
|
||||||
struct DerivationTrampolineGoal;
|
struct DerivationTrampolineGoal;
|
||||||
struct DerivationGoal;
|
struct DerivationGoal;
|
||||||
struct DerivationResolutionGoal;
|
|
||||||
struct DerivationBuildingGoal;
|
struct DerivationBuildingGoal;
|
||||||
struct PathSubstitutionGoal;
|
struct PathSubstitutionGoal;
|
||||||
class DrvOutputSubstitutionGoal;
|
class DrvOutputSubstitutionGoal;
|
||||||
|
|
@ -112,7 +111,6 @@ private:
|
||||||
DerivedPathMap<std::map<OutputsSpec, std::weak_ptr<DerivationTrampolineGoal>>> derivationTrampolineGoals;
|
DerivedPathMap<std::map<OutputsSpec, std::weak_ptr<DerivationTrampolineGoal>>> derivationTrampolineGoals;
|
||||||
|
|
||||||
std::map<StorePath, std::map<OutputName, std::weak_ptr<DerivationGoal>>> derivationGoals;
|
std::map<StorePath, std::map<OutputName, std::weak_ptr<DerivationGoal>>> derivationGoals;
|
||||||
std::map<StorePath, std::weak_ptr<DerivationResolutionGoal>> derivationResolutionGoals;
|
|
||||||
std::map<StorePath, std::weak_ptr<DerivationBuildingGoal>> derivationBuildingGoals;
|
std::map<StorePath, std::weak_ptr<DerivationBuildingGoal>> derivationBuildingGoals;
|
||||||
std::map<StorePath, std::weak_ptr<PathSubstitutionGoal>> substitutionGoals;
|
std::map<StorePath, std::weak_ptr<PathSubstitutionGoal>> substitutionGoals;
|
||||||
std::map<DrvOutput, std::weak_ptr<DrvOutputSubstitutionGoal>> drvOutputSubstitutionGoals;
|
std::map<DrvOutput, std::weak_ptr<DrvOutputSubstitutionGoal>> drvOutputSubstitutionGoals;
|
||||||
|
|
@ -223,23 +221,13 @@ public:
|
||||||
const StorePath & drvPath,
|
const StorePath & drvPath,
|
||||||
const Derivation & drv,
|
const Derivation & drv,
|
||||||
const OutputName & wantedOutput,
|
const OutputName & wantedOutput,
|
||||||
BuildMode buildMode = bmNormal,
|
BuildMode buildMode = bmNormal);
|
||||||
bool storeDerivation = false);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @ref DerivationResolutionGoal "derivation resolution goal"
|
* @ref DerivationBuildingGoal "derivation goal"
|
||||||
*/
|
*/
|
||||||
std::shared_ptr<DerivationResolutionGoal>
|
std::shared_ptr<DerivationBuildingGoal>
|
||||||
makeDerivationResolutionGoal(const StorePath & drvPath, const Derivation & drv, BuildMode buildMode = bmNormal);
|
makeDerivationBuildingGoal(const StorePath & drvPath, const Derivation & drv, BuildMode buildMode = bmNormal);
|
||||||
|
|
||||||
/**
|
|
||||||
* @ref DerivationBuildingGoal "derivation building goal"
|
|
||||||
*/
|
|
||||||
std::shared_ptr<DerivationBuildingGoal> makeDerivationBuildingGoal(
|
|
||||||
const StorePath & drvPath,
|
|
||||||
const Derivation & drv,
|
|
||||||
BuildMode buildMode = bmNormal,
|
|
||||||
bool storeDerivation = false);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @ref PathSubstitutionGoal "substitution goal"
|
* @ref PathSubstitutionGoal "substitution goal"
|
||||||
|
|
|
||||||
|
|
@ -30,18 +30,6 @@ struct DummyStore : virtual Store
|
||||||
*/
|
*/
|
||||||
boost::concurrent_flat_map<StorePath, PathInfoAndContents> contents;
|
boost::concurrent_flat_map<StorePath, PathInfoAndContents> contents;
|
||||||
|
|
||||||
/**
|
|
||||||
* The build trace maps the pair of a content-addressing (fixed or
|
|
||||||
* floating) derivations an one of its output to a
|
|
||||||
* (content-addressed) store object.
|
|
||||||
*
|
|
||||||
* It is [curried](https://en.wikipedia.org/wiki/Currying), so we
|
|
||||||
* instead having a single output with a `DrvOutput` key, we have an
|
|
||||||
* outer map for the derivation, and inner maps for the outputs of a
|
|
||||||
* given derivation.
|
|
||||||
*/
|
|
||||||
boost::concurrent_flat_map<Hash, std::map<std::string, ref<UnkeyedRealisation>>> buildTrace;
|
|
||||||
|
|
||||||
DummyStore(ref<const Config> config)
|
DummyStore(ref<const Config> config)
|
||||||
: Store{*config}
|
: Store{*config}
|
||||||
, config(config)
|
, config(config)
|
||||||
|
|
|
||||||
|
|
@ -3,8 +3,6 @@
|
||||||
|
|
||||||
#include "nix/store/store-api.hh"
|
#include "nix/store/store-api.hh"
|
||||||
|
|
||||||
#include <boost/unordered/concurrent_flat_map.hpp>
|
|
||||||
|
|
||||||
namespace nix {
|
namespace nix {
|
||||||
|
|
||||||
struct DummyStore;
|
struct DummyStore;
|
||||||
|
|
|
||||||
|
|
@ -208,8 +208,8 @@ public:
|
||||||
*/
|
*/
|
||||||
std::optional<TrustedFlag> isTrustedClient() override;
|
std::optional<TrustedFlag> isTrustedClient() override;
|
||||||
|
|
||||||
void queryRealisationUncached(
|
void
|
||||||
const DrvOutput &, Callback<std::shared_ptr<const UnkeyedRealisation>> callback) noexcept override
|
queryRealisationUncached(const DrvOutput &, Callback<std::shared_ptr<const Realisation>> callback) noexcept override
|
||||||
// TODO: Implement
|
// TODO: Implement
|
||||||
{
|
{
|
||||||
unsupported("queryRealisation");
|
unsupported("queryRealisation");
|
||||||
|
|
|
||||||
|
|
@ -173,7 +173,7 @@ private:
|
||||||
* Check lower store if upper DB does not have.
|
* Check lower store if upper DB does not have.
|
||||||
*/
|
*/
|
||||||
void queryRealisationUncached(
|
void queryRealisationUncached(
|
||||||
const DrvOutput &, Callback<std::shared_ptr<const UnkeyedRealisation>> callback) noexcept override;
|
const DrvOutput &, Callback<std::shared_ptr<const Realisation>> callback) noexcept override;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Call `remountIfNecessary` after collecting garbage normally.
|
* Call `remountIfNecessary` after collecting garbage normally.
|
||||||
|
|
|
||||||
|
|
@ -385,10 +385,10 @@ public:
|
||||||
void cacheDrvOutputMapping(
|
void cacheDrvOutputMapping(
|
||||||
State & state, const uint64_t deriver, const std::string & outputName, const StorePath & output);
|
State & state, const uint64_t deriver, const std::string & outputName, const StorePath & output);
|
||||||
|
|
||||||
std::optional<const UnkeyedRealisation> queryRealisation_(State & state, const DrvOutput & id);
|
std::optional<const Realisation> queryRealisation_(State & state, const DrvOutput & id);
|
||||||
std::optional<std::pair<int64_t, UnkeyedRealisation>> queryRealisationCore_(State & state, const DrvOutput & id);
|
std::optional<std::pair<int64_t, Realisation>> queryRealisationCore_(State & state, const DrvOutput & id);
|
||||||
void queryRealisationUncached(
|
void queryRealisationUncached(
|
||||||
const DrvOutput &, Callback<std::shared_ptr<const UnkeyedRealisation>> callback) noexcept override;
|
const DrvOutput &, Callback<std::shared_ptr<const Realisation>> callback) noexcept override;
|
||||||
|
|
||||||
std::optional<std::string> getVersion() override;
|
std::optional<std::string> getVersion() override;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,6 @@ headers = [ config_pub_h ] + files(
|
||||||
'build/derivation-building-misc.hh',
|
'build/derivation-building-misc.hh',
|
||||||
'build/derivation-env-desugar.hh',
|
'build/derivation-env-desugar.hh',
|
||||||
'build/derivation-goal.hh',
|
'build/derivation-goal.hh',
|
||||||
'build/derivation-resolution-goal.hh',
|
|
||||||
'build/derivation-trampoline-goal.hh',
|
'build/derivation-trampoline-goal.hh',
|
||||||
'build/drv-output-substitution-goal.hh',
|
'build/drv-output-substitution-goal.hh',
|
||||||
'build/goal.hh',
|
'build/goal.hh',
|
||||||
|
|
|
||||||
|
|
@ -46,12 +46,12 @@ struct DrvOutput
|
||||||
|
|
||||||
static DrvOutput parse(const std::string &);
|
static DrvOutput parse(const std::string &);
|
||||||
|
|
||||||
bool operator==(const DrvOutput &) const = default;
|
GENERATE_CMP(DrvOutput, me->drvHash, me->outputName);
|
||||||
auto operator<=>(const DrvOutput &) const = default;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct UnkeyedRealisation
|
struct Realisation
|
||||||
{
|
{
|
||||||
|
DrvOutput id;
|
||||||
StorePath outPath;
|
StorePath outPath;
|
||||||
|
|
||||||
StringSet signatures;
|
StringSet signatures;
|
||||||
|
|
@ -64,35 +64,22 @@ struct UnkeyedRealisation
|
||||||
*/
|
*/
|
||||||
std::map<DrvOutput, StorePath> dependentRealisations;
|
std::map<DrvOutput, StorePath> dependentRealisations;
|
||||||
|
|
||||||
std::string fingerprint(const DrvOutput & key) const;
|
std::string fingerprint() const;
|
||||||
|
void sign(const Signer &);
|
||||||
|
bool checkSignature(const PublicKeys & publicKeys, const std::string & sig) const;
|
||||||
|
size_t checkSignatures(const PublicKeys & publicKeys) const;
|
||||||
|
|
||||||
void sign(const DrvOutput & key, const Signer &);
|
static std::set<Realisation> closure(Store &, const std::set<Realisation> &);
|
||||||
|
static void closure(Store &, const std::set<Realisation> &, std::set<Realisation> & res);
|
||||||
|
|
||||||
bool checkSignature(const DrvOutput & key, const PublicKeys & publicKeys, const std::string & sig) const;
|
bool isCompatibleWith(const Realisation & other) const;
|
||||||
|
|
||||||
size_t checkSignatures(const DrvOutput & key, const PublicKeys & publicKeys) const;
|
StorePath getPath() const
|
||||||
|
|
||||||
const StorePath & getPath() const
|
|
||||||
{
|
{
|
||||||
return outPath;
|
return outPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO sketchy that it avoids signatures
|
GENERATE_CMP(Realisation, me->id, me->outPath);
|
||||||
GENERATE_CMP(UnkeyedRealisation, me->outPath);
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Realisation : UnkeyedRealisation
|
|
||||||
{
|
|
||||||
DrvOutput id;
|
|
||||||
|
|
||||||
bool isCompatibleWith(const UnkeyedRealisation & other) const;
|
|
||||||
|
|
||||||
static std::set<Realisation> closure(Store &, const std::set<Realisation> &);
|
|
||||||
|
|
||||||
static void closure(Store &, const std::set<Realisation> &, std::set<Realisation> & res);
|
|
||||||
|
|
||||||
bool operator==(const Realisation &) const = default;
|
|
||||||
auto operator<=>(const Realisation &) const = default;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -116,13 +103,12 @@ struct OpaquePath
|
||||||
{
|
{
|
||||||
StorePath path;
|
StorePath path;
|
||||||
|
|
||||||
const StorePath & getPath() const
|
StorePath getPath() const
|
||||||
{
|
{
|
||||||
return path;
|
return path;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool operator==(const OpaquePath &) const = default;
|
GENERATE_CMP(OpaquePath, me->path);
|
||||||
auto operator<=>(const OpaquePath &) const = default;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -130,7 +116,7 @@ struct OpaquePath
|
||||||
*/
|
*/
|
||||||
struct RealisedPath
|
struct RealisedPath
|
||||||
{
|
{
|
||||||
/**
|
/*
|
||||||
* A path is either the result of the realisation of a derivation or
|
* A path is either the result of the realisation of a derivation or
|
||||||
* an opaque blob that has been directly added to the store
|
* an opaque blob that has been directly added to the store
|
||||||
*/
|
*/
|
||||||
|
|
@ -152,14 +138,13 @@ struct RealisedPath
|
||||||
/**
|
/**
|
||||||
* Get the raw store path associated to this
|
* Get the raw store path associated to this
|
||||||
*/
|
*/
|
||||||
const StorePath & path() const;
|
StorePath path() const;
|
||||||
|
|
||||||
void closure(Store & store, Set & ret) const;
|
void closure(Store & store, Set & ret) const;
|
||||||
static void closure(Store & store, const Set & startPaths, Set & ret);
|
static void closure(Store & store, const Set & startPaths, Set & ret);
|
||||||
Set closure(Store & store) const;
|
Set closure(Store & store) const;
|
||||||
|
|
||||||
bool operator==(const RealisedPath &) const = default;
|
GENERATE_CMP(RealisedPath, me->raw);
|
||||||
auto operator<=>(const RealisedPath &) const = default;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class MissingRealisation : public Error
|
class MissingRealisation : public Error
|
||||||
|
|
@ -182,5 +167,4 @@ public:
|
||||||
|
|
||||||
} // namespace nix
|
} // namespace nix
|
||||||
|
|
||||||
JSON_IMPL(nix::UnkeyedRealisation)
|
|
||||||
JSON_IMPL(nix::Realisation)
|
JSON_IMPL(nix::Realisation)
|
||||||
|
|
|
||||||
|
|
@ -102,7 +102,7 @@ struct RemoteStore : public virtual Store, public virtual GcStore, public virtua
|
||||||
void registerDrvOutput(const Realisation & info) override;
|
void registerDrvOutput(const Realisation & info) override;
|
||||||
|
|
||||||
void queryRealisationUncached(
|
void queryRealisationUncached(
|
||||||
const DrvOutput &, Callback<std::shared_ptr<const UnkeyedRealisation>> callback) noexcept override;
|
const DrvOutput &, Callback<std::shared_ptr<const Realisation>> callback) noexcept override;
|
||||||
|
|
||||||
void
|
void
|
||||||
buildPaths(const std::vector<DerivedPath> & paths, BuildMode buildMode, std::shared_ptr<Store> evalStore) override;
|
buildPaths(const std::vector<DerivedPath> & paths, BuildMode buildMode, std::shared_ptr<Store> evalStore) override;
|
||||||
|
|
|
||||||
|
|
@ -31,7 +31,6 @@ MakeError(SubstituterDisabled, Error);
|
||||||
|
|
||||||
MakeError(InvalidStoreReference, Error);
|
MakeError(InvalidStoreReference, Error);
|
||||||
|
|
||||||
struct UnkeyedRealisation;
|
|
||||||
struct Realisation;
|
struct Realisation;
|
||||||
struct RealisedPath;
|
struct RealisedPath;
|
||||||
struct DrvOutput;
|
struct DrvOutput;
|
||||||
|
|
@ -399,12 +398,12 @@ public:
|
||||||
/**
|
/**
|
||||||
* Query the information about a realisation.
|
* Query the information about a realisation.
|
||||||
*/
|
*/
|
||||||
std::shared_ptr<const UnkeyedRealisation> queryRealisation(const DrvOutput &);
|
std::shared_ptr<const Realisation> queryRealisation(const DrvOutput &);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Asynchronous version of queryRealisation().
|
* Asynchronous version of queryRealisation().
|
||||||
*/
|
*/
|
||||||
void queryRealisation(const DrvOutput &, Callback<std::shared_ptr<const UnkeyedRealisation>> callback) noexcept;
|
void queryRealisation(const DrvOutput &, Callback<std::shared_ptr<const Realisation>> callback) noexcept;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check whether the given valid path info is sufficiently attested, by
|
* Check whether the given valid path info is sufficiently attested, by
|
||||||
|
|
@ -431,8 +430,8 @@ protected:
|
||||||
|
|
||||||
virtual void
|
virtual void
|
||||||
queryPathInfoUncached(const StorePath & path, Callback<std::shared_ptr<const ValidPathInfo>> callback) noexcept = 0;
|
queryPathInfoUncached(const StorePath & path, Callback<std::shared_ptr<const ValidPathInfo>> callback) noexcept = 0;
|
||||||
virtual void queryRealisationUncached(
|
virtual void
|
||||||
const DrvOutput &, Callback<std::shared_ptr<const UnkeyedRealisation>> callback) noexcept = 0;
|
queryRealisationUncached(const DrvOutput &, Callback<std::shared_ptr<const Realisation>> callback) noexcept = 0;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -77,7 +77,7 @@ void LocalOverlayStore::registerDrvOutput(const Realisation & info)
|
||||||
// First do queryRealisation on lower layer to populate DB
|
// First do queryRealisation on lower layer to populate DB
|
||||||
auto res = lowerStore->queryRealisation(info.id);
|
auto res = lowerStore->queryRealisation(info.id);
|
||||||
if (res)
|
if (res)
|
||||||
LocalStore::registerDrvOutput({*res, info.id});
|
LocalStore::registerDrvOutput(*res);
|
||||||
|
|
||||||
LocalStore::registerDrvOutput(info);
|
LocalStore::registerDrvOutput(info);
|
||||||
}
|
}
|
||||||
|
|
@ -108,12 +108,12 @@ void LocalOverlayStore::queryPathInfoUncached(
|
||||||
}
|
}
|
||||||
|
|
||||||
void LocalOverlayStore::queryRealisationUncached(
|
void LocalOverlayStore::queryRealisationUncached(
|
||||||
const DrvOutput & drvOutput, Callback<std::shared_ptr<const UnkeyedRealisation>> callback) noexcept
|
const DrvOutput & drvOutput, Callback<std::shared_ptr<const Realisation>> callback) noexcept
|
||||||
{
|
{
|
||||||
auto callbackPtr = std::make_shared<decltype(callback)>(std::move(callback));
|
auto callbackPtr = std::make_shared<decltype(callback)>(std::move(callback));
|
||||||
|
|
||||||
LocalStore::queryRealisationUncached(
|
LocalStore::queryRealisationUncached(
|
||||||
drvOutput, {[this, drvOutput, callbackPtr](std::future<std::shared_ptr<const UnkeyedRealisation>> fut) {
|
drvOutput, {[this, drvOutput, callbackPtr](std::future<std::shared_ptr<const Realisation>> fut) {
|
||||||
try {
|
try {
|
||||||
auto info = fut.get();
|
auto info = fut.get();
|
||||||
if (info)
|
if (info)
|
||||||
|
|
@ -123,7 +123,7 @@ void LocalOverlayStore::queryRealisationUncached(
|
||||||
}
|
}
|
||||||
// If we don't have it, check lower store
|
// If we don't have it, check lower store
|
||||||
lowerStore->queryRealisation(
|
lowerStore->queryRealisation(
|
||||||
drvOutput, {[callbackPtr](std::future<std::shared_ptr<const UnkeyedRealisation>> fut) {
|
drvOutput, {[callbackPtr](std::future<std::shared_ptr<const Realisation>> fut) {
|
||||||
try {
|
try {
|
||||||
(*callbackPtr)(fut.get());
|
(*callbackPtr)(fut.get());
|
||||||
} catch (...) {
|
} catch (...) {
|
||||||
|
|
|
||||||
|
|
@ -1036,7 +1036,7 @@ bool LocalStore::pathInfoIsUntrusted(const ValidPathInfo & info)
|
||||||
|
|
||||||
bool LocalStore::realisationIsUntrusted(const Realisation & realisation)
|
bool LocalStore::realisationIsUntrusted(const Realisation & realisation)
|
||||||
{
|
{
|
||||||
return config->requireSigs && !realisation.checkSignatures(realisation.id, getPublicKeys());
|
return config->requireSigs && !realisation.checkSignatures(getPublicKeys());
|
||||||
}
|
}
|
||||||
|
|
||||||
void LocalStore::addToStore(const ValidPathInfo & info, Source & source, RepairFlag repair, CheckSigsFlag checkSigs)
|
void LocalStore::addToStore(const ValidPathInfo & info, Source & source, RepairFlag repair, CheckSigsFlag checkSigs)
|
||||||
|
|
@ -1586,7 +1586,7 @@ void LocalStore::addSignatures(const StorePath & storePath, const StringSet & si
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<std::pair<int64_t, UnkeyedRealisation>>
|
std::optional<std::pair<int64_t, Realisation>>
|
||||||
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.strHash())(id.outputName));
|
||||||
|
|
@ -1598,13 +1598,14 @@ LocalStore::queryRealisationCore_(LocalStore::State & state, const DrvOutput & i
|
||||||
|
|
||||||
return {
|
return {
|
||||||
{realisationDbId,
|
{realisationDbId,
|
||||||
UnkeyedRealisation{
|
Realisation{
|
||||||
|
.id = id,
|
||||||
.outPath = outputPath,
|
.outPath = outputPath,
|
||||||
.signatures = signatures,
|
.signatures = signatures,
|
||||||
}}};
|
}}};
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<const UnkeyedRealisation> LocalStore::queryRealisation_(LocalStore::State & state, const DrvOutput & id)
|
std::optional<const Realisation> LocalStore::queryRealisation_(LocalStore::State & state, const DrvOutput & id)
|
||||||
{
|
{
|
||||||
auto maybeCore = queryRealisationCore_(state, id);
|
auto maybeCore = queryRealisationCore_(state, id);
|
||||||
if (!maybeCore)
|
if (!maybeCore)
|
||||||
|
|
@ -1630,13 +1631,13 @@ std::optional<const UnkeyedRealisation> LocalStore::queryRealisation_(LocalStore
|
||||||
}
|
}
|
||||||
|
|
||||||
void LocalStore::queryRealisationUncached(
|
void LocalStore::queryRealisationUncached(
|
||||||
const DrvOutput & id, Callback<std::shared_ptr<const UnkeyedRealisation>> callback) noexcept
|
const DrvOutput & id, Callback<std::shared_ptr<const Realisation>> callback) noexcept
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
auto maybeRealisation = retrySQLite<std::optional<const UnkeyedRealisation>>(
|
auto maybeRealisation =
|
||||||
[&]() { return queryRealisation_(*_state->lock(), id); });
|
retrySQLite<std::optional<const Realisation>>([&]() { return queryRealisation_(*_state->lock(), id); });
|
||||||
if (maybeRealisation)
|
if (maybeRealisation)
|
||||||
callback(std::make_shared<const UnkeyedRealisation>(maybeRealisation.value()));
|
callback(std::make_shared<const Realisation>(maybeRealisation.value()));
|
||||||
else
|
else
|
||||||
callback(nullptr);
|
callback(nullptr);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -302,7 +302,6 @@ sources = files(
|
||||||
'build/derivation-check.cc',
|
'build/derivation-check.cc',
|
||||||
'build/derivation-env-desugar.cc',
|
'build/derivation-env-desugar.cc',
|
||||||
'build/derivation-goal.cc',
|
'build/derivation-goal.cc',
|
||||||
'build/derivation-resolution-goal.cc',
|
|
||||||
'build/derivation-trampoline-goal.cc',
|
'build/derivation-trampoline-goal.cc',
|
||||||
'build/drv-output-substitution-goal.cc',
|
'build/drv-output-substitution-goal.cc',
|
||||||
'build/entry-points.cc',
|
'build/entry-points.cc',
|
||||||
|
|
|
||||||
|
|
@ -360,12 +360,11 @@ drvOutputReferences(Store & store, const Derivation & drv, const StorePath & out
|
||||||
if (!outputHash)
|
if (!outputHash)
|
||||||
throw Error(
|
throw Error(
|
||||||
"output '%s' of derivation '%s' isn't realised", outputName, store.printStorePath(inputDrv));
|
"output '%s' of derivation '%s' isn't realised", outputName, store.printStorePath(inputDrv));
|
||||||
DrvOutput key{*outputHash, outputName};
|
auto thisRealisation = store.queryRealisation(DrvOutput{*outputHash, outputName});
|
||||||
auto thisRealisation = store.queryRealisation(key);
|
|
||||||
if (!thisRealisation)
|
if (!thisRealisation)
|
||||||
throw Error(
|
throw Error(
|
||||||
"output '%s' of derivation '%s' isn’t built", outputName, store.printStorePath(inputDrv));
|
"output '%s' of derivation '%s' isn’t built", outputName, store.printStorePath(inputDrv));
|
||||||
inputRealisations.insert({*thisRealisation, std::move(key)});
|
inputRealisations.insert(*thisRealisation);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!inputNode.value.empty()) {
|
if (!inputNode.value.empty()) {
|
||||||
|
|
|
||||||
|
|
@ -39,7 +39,7 @@ void Realisation::closure(Store & store, const std::set<Realisation> & startOutp
|
||||||
std::set<Realisation> res;
|
std::set<Realisation> res;
|
||||||
for (auto & [currentDep, _] : current.dependentRealisations) {
|
for (auto & [currentDep, _] : current.dependentRealisations) {
|
||||||
if (auto currentRealisation = store.queryRealisation(currentDep))
|
if (auto currentRealisation = store.queryRealisation(currentDep))
|
||||||
res.insert({*currentRealisation, currentDep});
|
res.insert(*currentRealisation);
|
||||||
else
|
else
|
||||||
throw Error("Unrealised derivation '%s'", currentDep.to_string());
|
throw Error("Unrealised derivation '%s'", currentDep.to_string());
|
||||||
}
|
}
|
||||||
|
|
@ -61,25 +61,24 @@ void Realisation::closure(Store & store, const std::set<Realisation> & startOutp
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string UnkeyedRealisation::fingerprint(const DrvOutput & key) const
|
std::string Realisation::fingerprint() const
|
||||||
{
|
{
|
||||||
nlohmann::json serialized = Realisation{*this, key};
|
nlohmann::json serialized = *this;
|
||||||
serialized.erase("signatures");
|
serialized.erase("signatures");
|
||||||
return serialized.dump();
|
return serialized.dump();
|
||||||
}
|
}
|
||||||
|
|
||||||
void UnkeyedRealisation::sign(const DrvOutput & key, const Signer & signer)
|
void Realisation::sign(const Signer & signer)
|
||||||
{
|
{
|
||||||
signatures.insert(signer.signDetached(fingerprint(key)));
|
signatures.insert(signer.signDetached(fingerprint()));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool UnkeyedRealisation::checkSignature(
|
bool Realisation::checkSignature(const PublicKeys & publicKeys, const std::string & sig) const
|
||||||
const DrvOutput & key, const PublicKeys & publicKeys, const std::string & sig) const
|
|
||||||
{
|
{
|
||||||
return verifyDetached(fingerprint(key), sig, publicKeys);
|
return verifyDetached(fingerprint(), sig, publicKeys);
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t UnkeyedRealisation::checkSignatures(const DrvOutput & key, const PublicKeys & publicKeys) const
|
size_t Realisation::checkSignatures(const PublicKeys & publicKeys) const
|
||||||
{
|
{
|
||||||
// FIXME: Maybe we should return `maxSigs` if the realisation corresponds to
|
// FIXME: Maybe we should return `maxSigs` if the realisation corresponds to
|
||||||
// an input-addressed one − because in that case the drv is enough to check
|
// an input-addressed one − because in that case the drv is enough to check
|
||||||
|
|
@ -87,18 +86,19 @@ size_t UnkeyedRealisation::checkSignatures(const DrvOutput & key, const PublicKe
|
||||||
|
|
||||||
size_t good = 0;
|
size_t good = 0;
|
||||||
for (auto & sig : signatures)
|
for (auto & sig : signatures)
|
||||||
if (checkSignature(key, publicKeys, sig))
|
if (checkSignature(publicKeys, sig))
|
||||||
good++;
|
good++;
|
||||||
return good;
|
return good;
|
||||||
}
|
}
|
||||||
|
|
||||||
const StorePath & RealisedPath::path() const
|
StorePath RealisedPath::path() const
|
||||||
{
|
{
|
||||||
return std::visit([](auto && arg) -> auto & { return arg.getPath(); }, raw);
|
return std::visit([](auto && arg) { return arg.getPath(); }, raw);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Realisation::isCompatibleWith(const UnkeyedRealisation & other) const
|
bool Realisation::isCompatibleWith(const Realisation & other) const
|
||||||
{
|
{
|
||||||
|
assert(id == other.id);
|
||||||
if (outPath == other.outPath) {
|
if (outPath == other.outPath) {
|
||||||
if (dependentRealisations.empty() != other.dependentRealisations.empty()) {
|
if (dependentRealisations.empty() != other.dependentRealisations.empty()) {
|
||||||
warn(
|
warn(
|
||||||
|
|
@ -144,7 +144,7 @@ namespace nlohmann {
|
||||||
|
|
||||||
using namespace nix;
|
using namespace nix;
|
||||||
|
|
||||||
UnkeyedRealisation adl_serializer<UnkeyedRealisation>::from_json(const json & json0)
|
Realisation adl_serializer<Realisation>::from_json(const json & json0)
|
||||||
{
|
{
|
||||||
auto json = getObject(json0);
|
auto json = getObject(json0);
|
||||||
|
|
||||||
|
|
@ -157,39 +157,25 @@ UnkeyedRealisation adl_serializer<UnkeyedRealisation>::from_json(const json & js
|
||||||
for (auto & [jsonDepId, jsonDepOutPath] : getObject(*jsonDependencies))
|
for (auto & [jsonDepId, jsonDepOutPath] : getObject(*jsonDependencies))
|
||||||
dependentRealisations.insert({DrvOutput::parse(jsonDepId), jsonDepOutPath});
|
dependentRealisations.insert({DrvOutput::parse(jsonDepId), jsonDepOutPath});
|
||||||
|
|
||||||
return UnkeyedRealisation{
|
return Realisation{
|
||||||
|
.id = DrvOutput::parse(valueAt(json, "id")),
|
||||||
.outPath = valueAt(json, "outPath"),
|
.outPath = valueAt(json, "outPath"),
|
||||||
.signatures = signatures,
|
.signatures = signatures,
|
||||||
.dependentRealisations = dependentRealisations,
|
.dependentRealisations = dependentRealisations,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
void adl_serializer<UnkeyedRealisation>::to_json(json & json, const UnkeyedRealisation & r)
|
void adl_serializer<Realisation>::to_json(json & json, const Realisation & r)
|
||||||
{
|
{
|
||||||
auto jsonDependentRealisations = nlohmann::json::object();
|
auto jsonDependentRealisations = nlohmann::json::object();
|
||||||
for (auto & [depId, depOutPath] : r.dependentRealisations)
|
for (auto & [depId, depOutPath] : r.dependentRealisations)
|
||||||
jsonDependentRealisations.emplace(depId.to_string(), depOutPath);
|
jsonDependentRealisations.emplace(depId.to_string(), depOutPath);
|
||||||
json = {
|
json = {
|
||||||
|
{"id", r.id.to_string()},
|
||||||
{"outPath", r.outPath},
|
{"outPath", r.outPath},
|
||||||
{"signatures", r.signatures},
|
{"signatures", r.signatures},
|
||||||
{"dependentRealisations", jsonDependentRealisations},
|
{"dependentRealisations", jsonDependentRealisations},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
Realisation adl_serializer<Realisation>::from_json(const json & json0)
|
|
||||||
{
|
|
||||||
auto json = getObject(json0);
|
|
||||||
|
|
||||||
return Realisation{
|
|
||||||
static_cast<UnkeyedRealisation>(json0),
|
|
||||||
DrvOutput::parse(valueAt(json, "id")),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
void adl_serializer<Realisation>::to_json(json & json, const Realisation & r)
|
|
||||||
{
|
|
||||||
json = static_cast<const UnkeyedRealisation &>(r);
|
|
||||||
json["id"] = r.id.to_string();
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace nlohmann
|
} // namespace nlohmann
|
||||||
|
|
|
||||||
|
|
@ -501,7 +501,7 @@ void RemoteStore::registerDrvOutput(const Realisation & info)
|
||||||
}
|
}
|
||||||
|
|
||||||
void RemoteStore::queryRealisationUncached(
|
void RemoteStore::queryRealisationUncached(
|
||||||
const DrvOutput & id, Callback<std::shared_ptr<const UnkeyedRealisation>> callback) noexcept
|
const DrvOutput & id, Callback<std::shared_ptr<const Realisation>> callback) noexcept
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
auto conn(getConnection());
|
auto conn(getConnection());
|
||||||
|
|
@ -515,21 +515,21 @@ void RemoteStore::queryRealisationUncached(
|
||||||
conn->to << id.to_string();
|
conn->to << id.to_string();
|
||||||
conn.processStderr();
|
conn.processStderr();
|
||||||
|
|
||||||
auto real = [&]() -> std::shared_ptr<const UnkeyedRealisation> {
|
auto real = [&]() -> std::shared_ptr<const Realisation> {
|
||||||
if (GET_PROTOCOL_MINOR(conn->protoVersion) < 31) {
|
if (GET_PROTOCOL_MINOR(conn->protoVersion) < 31) {
|
||||||
auto outPaths = WorkerProto::Serialise<std::set<StorePath>>::read(*this, *conn);
|
auto outPaths = WorkerProto::Serialise<std::set<StorePath>>::read(*this, *conn);
|
||||||
if (outPaths.empty())
|
if (outPaths.empty())
|
||||||
return nullptr;
|
return nullptr;
|
||||||
return std::make_shared<const UnkeyedRealisation>(UnkeyedRealisation{.outPath = *outPaths.begin()});
|
return std::make_shared<const Realisation>(Realisation{.id = id, .outPath = *outPaths.begin()});
|
||||||
} else {
|
} else {
|
||||||
auto realisations = WorkerProto::Serialise<std::set<Realisation>>::read(*this, *conn);
|
auto realisations = WorkerProto::Serialise<std::set<Realisation>>::read(*this, *conn);
|
||||||
if (realisations.empty())
|
if (realisations.empty())
|
||||||
return nullptr;
|
return nullptr;
|
||||||
return std::make_shared<const UnkeyedRealisation>(*realisations.begin());
|
return std::make_shared<const Realisation>(*realisations.begin());
|
||||||
}
|
}
|
||||||
}();
|
}();
|
||||||
|
|
||||||
callback(std::shared_ptr<const UnkeyedRealisation>(real));
|
callback(std::shared_ptr<const Realisation>(real));
|
||||||
} catch (...) {
|
} catch (...) {
|
||||||
return callback.rethrow();
|
return callback.rethrow();
|
||||||
}
|
}
|
||||||
|
|
@ -626,15 +626,13 @@ std::vector<KeyedBuildResult> RemoteStore::buildPathsWithResults(
|
||||||
auto realisation = queryRealisation(outputId);
|
auto realisation = queryRealisation(outputId);
|
||||||
if (!realisation)
|
if (!realisation)
|
||||||
throw MissingRealisation(outputId);
|
throw MissingRealisation(outputId);
|
||||||
success.builtOutputs.emplace(output, Realisation{*realisation, outputId});
|
success.builtOutputs.emplace(output, *realisation);
|
||||||
} else {
|
} else {
|
||||||
success.builtOutputs.emplace(
|
success.builtOutputs.emplace(
|
||||||
output,
|
output,
|
||||||
Realisation{
|
Realisation{
|
||||||
UnkeyedRealisation{
|
.id = outputId,
|
||||||
.outPath = outputPath,
|
.outPath = outputPath,
|
||||||
},
|
|
||||||
outputId,
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -107,7 +107,7 @@ struct RestrictedStore : public virtual IndirectRootStore, public virtual GcStor
|
||||||
void registerDrvOutput(const Realisation & info) override;
|
void registerDrvOutput(const Realisation & info) override;
|
||||||
|
|
||||||
void queryRealisationUncached(
|
void queryRealisationUncached(
|
||||||
const DrvOutput & id, Callback<std::shared_ptr<const UnkeyedRealisation>> callback) noexcept override;
|
const DrvOutput & id, Callback<std::shared_ptr<const Realisation>> callback) noexcept override;
|
||||||
|
|
||||||
void
|
void
|
||||||
buildPaths(const std::vector<DerivedPath> & paths, BuildMode buildMode, std::shared_ptr<Store> evalStore) override;
|
buildPaths(const std::vector<DerivedPath> & paths, BuildMode buildMode, std::shared_ptr<Store> evalStore) override;
|
||||||
|
|
@ -244,7 +244,7 @@ void RestrictedStore::registerDrvOutput(const Realisation & info)
|
||||||
}
|
}
|
||||||
|
|
||||||
void RestrictedStore::queryRealisationUncached(
|
void RestrictedStore::queryRealisationUncached(
|
||||||
const DrvOutput & id, Callback<std::shared_ptr<const UnkeyedRealisation>> callback) noexcept
|
const DrvOutput & id, Callback<std::shared_ptr<const Realisation>> callback) noexcept
|
||||||
// XXX: This should probably be allowed if the realisation corresponds to
|
// XXX: This should probably be allowed if the realisation corresponds to
|
||||||
// an allowed derivation
|
// an allowed derivation
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -598,8 +598,7 @@ void Store::queryPathInfo(const StorePath & storePath, Callback<ref<const ValidP
|
||||||
}});
|
}});
|
||||||
}
|
}
|
||||||
|
|
||||||
void Store::queryRealisation(
|
void Store::queryRealisation(const DrvOutput & id, Callback<std::shared_ptr<const Realisation>> callback) noexcept
|
||||||
const DrvOutput & id, Callback<std::shared_ptr<const UnkeyedRealisation>> callback) noexcept
|
|
||||||
{
|
{
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|
@ -625,20 +624,20 @@ void Store::queryRealisation(
|
||||||
|
|
||||||
auto callbackPtr = std::make_shared<decltype(callback)>(std::move(callback));
|
auto callbackPtr = std::make_shared<decltype(callback)>(std::move(callback));
|
||||||
|
|
||||||
queryRealisationUncached(id, {[this, id, callbackPtr](std::future<std::shared_ptr<const UnkeyedRealisation>> fut) {
|
queryRealisationUncached(id, {[this, id, callbackPtr](std::future<std::shared_ptr<const Realisation>> fut) {
|
||||||
try {
|
try {
|
||||||
auto info = fut.get();
|
auto info = fut.get();
|
||||||
|
|
||||||
if (diskCache) {
|
if (diskCache) {
|
||||||
if (info)
|
if (info)
|
||||||
diskCache->upsertRealisation(
|
diskCache->upsertRealisation(
|
||||||
config.getReference().render(/*FIXME withParams=*/false), {*info, id});
|
config.getReference().render(/*FIXME withParams=*/false), *info);
|
||||||
else
|
else
|
||||||
diskCache->upsertAbsentRealisation(
|
diskCache->upsertAbsentRealisation(
|
||||||
config.getReference().render(/*FIXME withParams=*/false), id);
|
config.getReference().render(/*FIXME withParams=*/false), id);
|
||||||
}
|
}
|
||||||
|
|
||||||
(*callbackPtr)(std::shared_ptr<const UnkeyedRealisation>(info));
|
(*callbackPtr)(std::shared_ptr<const Realisation>(info));
|
||||||
|
|
||||||
} catch (...) {
|
} catch (...) {
|
||||||
callbackPtr->rethrow();
|
callbackPtr->rethrow();
|
||||||
|
|
@ -646,9 +645,9 @@ void Store::queryRealisation(
|
||||||
}});
|
}});
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<const UnkeyedRealisation> Store::queryRealisation(const DrvOutput & id)
|
std::shared_ptr<const Realisation> Store::queryRealisation(const DrvOutput & id)
|
||||||
{
|
{
|
||||||
using RealPtr = std::shared_ptr<const UnkeyedRealisation>;
|
using RealPtr = std::shared_ptr<const Realisation>;
|
||||||
std::promise<RealPtr> promise;
|
std::promise<RealPtr> promise;
|
||||||
|
|
||||||
queryRealisation(id, {[&](std::future<RealPtr> result) {
|
queryRealisation(id, {[&](std::future<RealPtr> result) {
|
||||||
|
|
@ -911,12 +910,11 @@ std::map<StorePath, StorePath> copyPaths(
|
||||||
std::set<Realisation> toplevelRealisations;
|
std::set<Realisation> toplevelRealisations;
|
||||||
for (auto & path : paths) {
|
for (auto & path : paths) {
|
||||||
storePaths.insert(path.path());
|
storePaths.insert(path.path());
|
||||||
if (auto * realisation = std::get_if<Realisation>(&path.raw)) {
|
if (auto realisation = std::get_if<Realisation>(&path.raw)) {
|
||||||
experimentalFeatureSettings.require(Xp::CaDerivations);
|
experimentalFeatureSettings.require(Xp::CaDerivations);
|
||||||
toplevelRealisations.insert(*realisation);
|
toplevelRealisations.insert(*realisation);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
auto pathsMap = copyPaths(srcStore, dstStore, storePaths, repair, checkSigs, substitute);
|
auto pathsMap = copyPaths(srcStore, dstStore, storePaths, repair, checkSigs, substitute);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|
@ -933,7 +931,7 @@ std::map<StorePath, StorePath> copyPaths(
|
||||||
"dependency of '%s' but isn't registered",
|
"dependency of '%s' but isn't registered",
|
||||||
drvOutput.to_string(),
|
drvOutput.to_string(),
|
||||||
current.id.to_string());
|
current.id.to_string());
|
||||||
children.insert({*currentChild, drvOutput});
|
children.insert(*currentChild);
|
||||||
}
|
}
|
||||||
return children;
|
return children;
|
||||||
},
|
},
|
||||||
|
|
@ -1201,7 +1199,7 @@ void Store::signRealisation(Realisation & realisation)
|
||||||
for (auto & secretKeyFile : secretKeyFiles.get()) {
|
for (auto & secretKeyFile : secretKeyFiles.get()) {
|
||||||
SecretKey secretKey(readFile(secretKeyFile));
|
SecretKey secretKey(readFile(secretKeyFile));
|
||||||
LocalSigner signer(std::move(secretKey));
|
LocalSigner signer(std::move(secretKey));
|
||||||
realisation.sign(realisation.id, signer);
|
realisation.sign(signer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1830,12 +1830,7 @@ SingleDrvOutputs DerivationBuilderImpl::registerOutputs()
|
||||||
for (auto & [outputName, newInfo] : infos) {
|
for (auto & [outputName, newInfo] : infos) {
|
||||||
auto oldinfo = get(initialOutputs, outputName);
|
auto oldinfo = get(initialOutputs, outputName);
|
||||||
assert(oldinfo);
|
assert(oldinfo);
|
||||||
auto thisRealisation = Realisation{
|
auto thisRealisation = Realisation{.id = DrvOutput{oldinfo->outputHash, outputName}, .outPath = newInfo.path};
|
||||||
{
|
|
||||||
.outPath = newInfo.path,
|
|
||||||
},
|
|
||||||
DrvOutput{oldinfo->outputHash, outputName},
|
|
||||||
};
|
|
||||||
if (experimentalFeatureSettings.isEnabled(Xp::CaDerivations) && !drv.type().isImpure()) {
|
if (experimentalFeatureSettings.isEnabled(Xp::CaDerivations) && !drv.type().isImpure()) {
|
||||||
store.signRealisation(thisRealisation);
|
store.signRealisation(thisRealisation);
|
||||||
store.registerDrvOutput(thisRealisation);
|
store.registerDrvOutput(thisRealisation);
|
||||||
|
|
|
||||||
|
|
@ -222,22 +222,3 @@ public:
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace nix
|
} // namespace nix
|
||||||
|
|
||||||
template<>
|
|
||||||
struct std::hash<nix::Hash>
|
|
||||||
{
|
|
||||||
std::size_t operator()(const nix::Hash & hash) const noexcept
|
|
||||||
{
|
|
||||||
assert(hash.hashSize > sizeof(size_t));
|
|
||||||
return *reinterpret_cast<const std::size_t *>(&hash.hash);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
namespace nix {
|
|
||||||
|
|
||||||
inline std::size_t hash_value(const Hash & hash)
|
|
||||||
{
|
|
||||||
return std::hash<Hash>{}(hash);
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace nix
|
|
||||||
|
|
|
||||||
|
|
@ -178,8 +178,7 @@ test "$(<<<"$out" grep -cE '^error:')" = 4
|
||||||
|
|
||||||
out="$(nix build -f fod-failing.nix -L x4 2>&1)" && status=0 || status=$?
|
out="$(nix build -f fod-failing.nix -L x4 2>&1)" && status=0 || status=$?
|
||||||
test "$status" = 1
|
test "$status" = 1
|
||||||
# Precise number of errors depends on daemon version / goal refactorings
|
test "$(<<<"$out" grep -cE '^error:')" = 2
|
||||||
(( "$(<<<"$out" grep -cE '^error:')" >= 2 ))
|
|
||||||
|
|
||||||
if isDaemonNewer "2.29pre"; then
|
if isDaemonNewer "2.29pre"; then
|
||||||
<<<"$out" grepQuiet -E "error: Cannot build '.*-x4\\.drv'"
|
<<<"$out" grepQuiet -E "error: Cannot build '.*-x4\\.drv'"
|
||||||
|
|
@ -187,13 +186,11 @@ if isDaemonNewer "2.29pre"; then
|
||||||
else
|
else
|
||||||
<<<"$out" grepQuiet -E "error: 1 dependencies of derivation '.*-x4\\.drv' failed to build"
|
<<<"$out" grepQuiet -E "error: 1 dependencies of derivation '.*-x4\\.drv' failed to build"
|
||||||
fi
|
fi
|
||||||
# Either x2 or x3 could have failed, x4 depends on both symmetrically
|
<<<"$out" grepQuiet -E "hash mismatch in fixed-output derivation '.*-x2\\.drv'"
|
||||||
<<<"$out" grepQuiet -E "hash mismatch in fixed-output derivation '.*-x[23]\\.drv'"
|
|
||||||
|
|
||||||
out="$(nix build -f fod-failing.nix -L x4 --keep-going 2>&1)" && status=0 || status=$?
|
out="$(nix build -f fod-failing.nix -L x4 --keep-going 2>&1)" && status=0 || status=$?
|
||||||
test "$status" = 1
|
test "$status" = 1
|
||||||
# Precise number of errors depends on daemon version / goal refactorings
|
test "$(<<<"$out" grep -cE '^error:')" = 3
|
||||||
(( "$(<<<"$out" grep -cE '^error:')" >= 3 ))
|
|
||||||
if isDaemonNewer "2.29pre"; then
|
if isDaemonNewer "2.29pre"; then
|
||||||
<<<"$out" grepQuiet -E "error: Cannot build '.*-x4\\.drv'"
|
<<<"$out" grepQuiet -E "error: Cannot build '.*-x4\\.drv'"
|
||||||
<<<"$out" grepQuiet -E "Reason: 2 dependencies failed."
|
<<<"$out" grepQuiet -E "Reason: 2 dependencies failed."
|
||||||
|
|
|
||||||
|
|
@ -65,4 +65,7 @@ buildViaSubstitute use-a-prime-more-outputs^first
|
||||||
# Should only fetch the output we asked for
|
# Should only fetch the output we asked for
|
||||||
[[ -d "$(jq -r <"$TEST_ROOT"/a.json '.[0].outputs.out')" ]]
|
[[ -d "$(jq -r <"$TEST_ROOT"/a.json '.[0].outputs.out')" ]]
|
||||||
[[ -f "$(jq -r <"$TEST_ROOT"/a.json '.[2].outputs.first')" ]]
|
[[ -f "$(jq -r <"$TEST_ROOT"/a.json '.[2].outputs.first')" ]]
|
||||||
[[ ! -e "$(jq -r <"$TEST_ROOT"/a.json '.[2].outputs.second')" ]]
|
|
||||||
|
# Output should *not* be here, this is the bug
|
||||||
|
[[ -e "$(jq -r <"$TEST_ROOT"/a.json '.[2].outputs.second')" ]]
|
||||||
|
skipTest "bug is not yet fixed"
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue