mirror of
https://github.com/NixOS/nix.git
synced 2025-11-09 12:06:01 +01:00
Remove dependent realisations
This progress on #11896. It introduces some issues temporarily which will be fixed when #11928 is fixed.
This commit is contained in:
parent
f7dcfd1072
commit
f392663533
17 changed files with 106 additions and 392 deletions
|
|
@ -88,16 +88,22 @@ public:
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
#define VERSIONED_CHARACTERIZATION_TEST_NO_JSON(FIXTURE, NAME, STEM, VERSION, VALUE) \
|
#define VERSIONED_READ_CHARACTERIZATION_TEST_NO_JSON(FIXTURE, NAME, STEM, VERSION, VALUE) \
|
||||||
TEST_F(FIXTURE, NAME##_read) \
|
TEST_F(FIXTURE, NAME##_read) \
|
||||||
{ \
|
{ \
|
||||||
readProtoTest(STEM, VERSION, VALUE); \
|
readProtoTest(STEM, VERSION, VALUE); \
|
||||||
} \
|
|
||||||
TEST_F(FIXTURE, NAME##_write) \
|
|
||||||
{ \
|
|
||||||
writeProtoTest(STEM, VERSION, VALUE); \
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#define VERSIONED_WRITE_CHARACTERIZATION_TEST_NO_JSON(FIXTURE, NAME, STEM, VERSION, VALUE) \
|
||||||
|
TEST_F(FIXTURE, NAME##_write) \
|
||||||
|
{ \
|
||||||
|
writeProtoTest(STEM, VERSION, VALUE); \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define VERSIONED_CHARACTERIZATION_TEST_NO_JSON(FIXTURE, NAME, STEM, VERSION, VALUE) \
|
||||||
|
VERSIONED_READ_CHARACTERIZATION_TEST_NO_JSON(FIXTURE, NAME, STEM, VERSION, VALUE) \
|
||||||
|
VERSIONED_WRITE_CHARACTERIZATION_TEST_NO_JSON(FIXTURE, NAME, STEM, VERSION, VALUE)
|
||||||
|
|
||||||
#define VERSIONED_CHARACTERIZATION_TEST(FIXTURE, NAME, STEM, VERSION, VALUE) \
|
#define VERSIONED_CHARACTERIZATION_TEST(FIXTURE, NAME, STEM, VERSION, VALUE) \
|
||||||
VERSIONED_CHARACTERIZATION_TEST_NO_JSON(FIXTURE, NAME, STEM, VERSION, VALUE) \
|
VERSIONED_CHARACTERIZATION_TEST_NO_JSON(FIXTURE, NAME, STEM, VERSION, VALUE) \
|
||||||
TEST_F(FIXTURE, NAME##_json_read) \
|
TEST_F(FIXTURE, NAME##_json_read) \
|
||||||
|
|
|
||||||
|
|
@ -47,24 +47,30 @@ public:
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
#define CHARACTERIZATION_TEST(NAME, STEM, VALUE) \
|
#define READ_CHARACTERIZATION_TEST(NAME, STEM, VALUE) \
|
||||||
TEST_F(CommonProtoTest, NAME##_read) \
|
TEST_F(CommonProtoTest, NAME##_read) \
|
||||||
{ \
|
{ \
|
||||||
readProtoTest(STEM, VALUE); \
|
readProtoTest(STEM, VALUE); \
|
||||||
} \
|
} \
|
||||||
TEST_F(CommonProtoTest, NAME##_write) \
|
TEST_F(CommonProtoTest, NAME##_json_read) \
|
||||||
{ \
|
{ \
|
||||||
writeProtoTest(STEM, VALUE); \
|
readJsonTest(STEM, VALUE); \
|
||||||
} \
|
|
||||||
TEST_F(CommonProtoTest, NAME##_json_read) \
|
|
||||||
{ \
|
|
||||||
readJsonTest(STEM, VALUE); \
|
|
||||||
} \
|
|
||||||
TEST_F(CommonProtoTest, NAME##_json_write) \
|
|
||||||
{ \
|
|
||||||
writeJsonTest(STEM, VALUE); \
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#define WRITE_CHARACTERIZATION_TEST(NAME, STEM, VALUE) \
|
||||||
|
TEST_F(CommonProtoTest, NAME##_write) \
|
||||||
|
{ \
|
||||||
|
writeProtoTest(STEM, VALUE); \
|
||||||
|
} \
|
||||||
|
TEST_F(CommonProtoTest, NAME##_json_write) \
|
||||||
|
{ \
|
||||||
|
writeJsonTest(STEM, VALUE); \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define CHARACTERIZATION_TEST(NAME, STEM, VALUE) \
|
||||||
|
READ_CHARACTERIZATION_TEST(NAME, STEM, VALUE) \
|
||||||
|
WRITE_CHARACTERIZATION_TEST(NAME, STEM, VALUE)
|
||||||
|
|
||||||
CHARACTERIZATION_TEST(
|
CHARACTERIZATION_TEST(
|
||||||
string,
|
string,
|
||||||
"string",
|
"string",
|
||||||
|
|
@ -141,7 +147,7 @@ CHARACTERIZATION_TEST(
|
||||||
},
|
},
|
||||||
}))
|
}))
|
||||||
|
|
||||||
CHARACTERIZATION_TEST(
|
READ_CHARACTERIZATION_TEST(
|
||||||
realisation_with_deps,
|
realisation_with_deps,
|
||||||
"realisation-with-deps",
|
"realisation-with-deps",
|
||||||
(std::tuple<Realisation>{
|
(std::tuple<Realisation>{
|
||||||
|
|
@ -149,16 +155,6 @@ CHARACTERIZATION_TEST(
|
||||||
{
|
{
|
||||||
.outPath = StorePath{"g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-foo"},
|
.outPath = StorePath{"g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-foo"},
|
||||||
.signatures = {"asdf", "qwer"},
|
.signatures = {"asdf", "qwer"},
|
||||||
.dependentRealisations =
|
|
||||||
{
|
|
||||||
{
|
|
||||||
DrvOutput{
|
|
||||||
.drvHash = Hash::parseSRI("sha256-b4afnqKCO9oWXgYHb9DeQ2berSwOjS27rSd9TxXDc/U="),
|
|
||||||
.outputName = "quux",
|
|
||||||
},
|
|
||||||
StorePath{"g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-foo"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
.drvHash = Hash::parseSRI("sha256-FePFYIlMuycIXPZbWi7LGEiMmZSX9FMbaQenWBzm1Sc="),
|
.drvHash = Hash::parseSRI("sha256-FePFYIlMuycIXPZbWi7LGEiMmZSX9FMbaQenWBzm1Sc="),
|
||||||
|
|
|
||||||
|
|
@ -44,54 +44,45 @@ TEST_P(RealisationJsonTest, to_json)
|
||||||
writeJsonTest(name, value);
|
writeJsonTest(name, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Realisation simple{
|
||||||
|
{
|
||||||
|
.outPath = StorePath{"g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-foo.drv"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.drvHash = Hash::parseExplicitFormatUnprefixed(
|
||||||
|
"ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad",
|
||||||
|
HashAlgorithm::SHA256,
|
||||||
|
HashFormat::Base16),
|
||||||
|
.outputName = "foo",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
INSTANTIATE_TEST_SUITE_P(
|
INSTANTIATE_TEST_SUITE_P(
|
||||||
RealisationJSON,
|
RealisationJSON,
|
||||||
RealisationJsonTest,
|
RealisationJsonTest,
|
||||||
([] {
|
::testing::Values(
|
||||||
Realisation simple{
|
std::pair{
|
||||||
{
|
"simple",
|
||||||
.outPath = StorePath{"g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-foo.drv"},
|
simple,
|
||||||
},
|
},
|
||||||
{
|
std::pair{
|
||||||
.drvHash = Hash::parseExplicitFormatUnprefixed(
|
"with-signature",
|
||||||
"ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad",
|
[&] {
|
||||||
HashAlgorithm::SHA256,
|
auto r = simple;
|
||||||
HashFormat::Base16),
|
// FIXME actually sign properly
|
||||||
.outputName = "foo",
|
r.signatures = {"asdfasdfasdf"};
|
||||||
},
|
return r;
|
||||||
};
|
}(),
|
||||||
return ::testing::Values(
|
}));
|
||||||
std::pair{
|
|
||||||
"simple",
|
|
||||||
simple,
|
|
||||||
},
|
|
||||||
std::pair{
|
|
||||||
"with-signature",
|
|
||||||
[&] {
|
|
||||||
auto r = simple;
|
|
||||||
// FIXME actually sign properly
|
|
||||||
r.signatures = {"asdfasdfasdf"};
|
|
||||||
return r;
|
|
||||||
}()},
|
|
||||||
std::pair{
|
|
||||||
"with-dependent-realisations",
|
|
||||||
[&] {
|
|
||||||
auto r = simple;
|
|
||||||
r.dependentRealisations = {{
|
|
||||||
{
|
|
||||||
.drvHash = Hash::parseExplicitFormatUnprefixed(
|
|
||||||
"ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad",
|
|
||||||
HashAlgorithm::SHA256,
|
|
||||||
HashFormat::Base16),
|
|
||||||
.outputName = "foo",
|
|
||||||
},
|
|
||||||
StorePath{"g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-foo.drv"},
|
|
||||||
}};
|
|
||||||
return r;
|
|
||||||
}(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
()));
|
/**
|
||||||
|
* We no longer have a notion of "dependent realisations", but we still
|
||||||
|
* want to parse old realisation files. So make this just be a read test
|
||||||
|
* (no write direction), accordingly.
|
||||||
|
*/
|
||||||
|
TEST_F(RealisationTest, dependent_realisations_from_json)
|
||||||
|
{
|
||||||
|
readJsonTest("with-dependent-realisations", simple);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace nix
|
} // namespace nix
|
||||||
|
|
|
||||||
|
|
@ -116,7 +116,7 @@ VERSIONED_CHARACTERIZATION_TEST(
|
||||||
},
|
},
|
||||||
}))
|
}))
|
||||||
|
|
||||||
VERSIONED_CHARACTERIZATION_TEST(
|
VERSIONED_READ_CHARACTERIZATION_TEST(
|
||||||
ServeProtoTest,
|
ServeProtoTest,
|
||||||
realisation_with_deps,
|
realisation_with_deps,
|
||||||
"realisation-with-deps",
|
"realisation-with-deps",
|
||||||
|
|
@ -126,16 +126,6 @@ VERSIONED_CHARACTERIZATION_TEST(
|
||||||
{
|
{
|
||||||
.outPath = StorePath{"g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-foo"},
|
.outPath = StorePath{"g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-foo"},
|
||||||
.signatures = {"asdf", "qwer"},
|
.signatures = {"asdf", "qwer"},
|
||||||
.dependentRealisations =
|
|
||||||
{
|
|
||||||
{
|
|
||||||
DrvOutput{
|
|
||||||
.drvHash = Hash::parseSRI("sha256-b4afnqKCO9oWXgYHb9DeQ2berSwOjS27rSd9TxXDc/U="),
|
|
||||||
.outputName = "quux",
|
|
||||||
},
|
|
||||||
StorePath{"g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-foo"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
.drvHash = Hash::parseSRI("sha256-FePFYIlMuycIXPZbWi7LGEiMmZSX9FMbaQenWBzm1Sc="),
|
.drvHash = Hash::parseSRI("sha256-FePFYIlMuycIXPZbWi7LGEiMmZSX9FMbaQenWBzm1Sc="),
|
||||||
|
|
|
||||||
|
|
@ -169,7 +169,7 @@ VERSIONED_CHARACTERIZATION_TEST(
|
||||||
},
|
},
|
||||||
}))
|
}))
|
||||||
|
|
||||||
VERSIONED_CHARACTERIZATION_TEST(
|
VERSIONED_READ_CHARACTERIZATION_TEST(
|
||||||
WorkerProtoTest,
|
WorkerProtoTest,
|
||||||
realisation_with_deps,
|
realisation_with_deps,
|
||||||
"realisation-with-deps",
|
"realisation-with-deps",
|
||||||
|
|
@ -179,16 +179,6 @@ VERSIONED_CHARACTERIZATION_TEST(
|
||||||
{
|
{
|
||||||
.outPath = StorePath{"g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-foo"},
|
.outPath = StorePath{"g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-foo"},
|
||||||
.signatures = {"asdf", "qwer"},
|
.signatures = {"asdf", "qwer"},
|
||||||
.dependentRealisations =
|
|
||||||
{
|
|
||||||
{
|
|
||||||
DrvOutput{
|
|
||||||
.drvHash = Hash::parseSRI("sha256-b4afnqKCO9oWXgYHb9DeQ2berSwOjS27rSd9TxXDc/U="),
|
|
||||||
.outputName = "quux",
|
|
||||||
},
|
|
||||||
StorePath{"g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-foo"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
.drvHash = Hash::parseSRI("sha256-FePFYIlMuycIXPZbWi7LGEiMmZSX9FMbaQenWBzm1Sc="),
|
.drvHash = Hash::parseSRI("sha256-FePFYIlMuycIXPZbWi7LGEiMmZSX9FMbaQenWBzm1Sc="),
|
||||||
|
|
|
||||||
|
|
@ -209,11 +209,6 @@ Goal::Co DerivationGoal::haveDerivation(bool storeDerivation)
|
||||||
.outputName = wantedOutput,
|
.outputName = wantedOutput,
|
||||||
}};
|
}};
|
||||||
newRealisation.signatures.clear();
|
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.signRealisation(newRealisation);
|
||||||
worker.store.registerDrvOutput(newRealisation);
|
worker.store.registerDrvOutput(newRealisation);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -86,32 +86,8 @@ Goal::Co DrvOutputSubstitutionGoal::init()
|
||||||
if (!outputInfo)
|
if (!outputInfo)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
bool failed = false;
|
|
||||||
|
|
||||||
Goals waitees;
|
Goals waitees;
|
||||||
|
|
||||||
for (const auto & [depId, depPath] : outputInfo->dependentRealisations) {
|
|
||||||
if (depId != id) {
|
|
||||||
if (auto localOutputInfo = worker.store.queryRealisation(depId);
|
|
||||||
localOutputInfo && localOutputInfo->outPath != depPath) {
|
|
||||||
warn(
|
|
||||||
"substituter '%s' has an incompatible realisation for '%s', ignoring.\n"
|
|
||||||
"Local: %s\n"
|
|
||||||
"Remote: %s",
|
|
||||||
sub->config.getHumanReadableURI(),
|
|
||||||
depId.to_string(),
|
|
||||||
worker.store.printStorePath(localOutputInfo->outPath),
|
|
||||||
worker.store.printStorePath(depPath));
|
|
||||||
failed = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
waitees.insert(worker.makeDrvOutputSubstitutionGoal(depId));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (failed)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
waitees.insert(worker.makePathSubstitutionGoal(outputInfo->outPath));
|
waitees.insert(worker.makePathSubstitutionGoal(outputInfo->outPath));
|
||||||
|
|
||||||
co_await await(std::move(waitees));
|
co_await await(std::move(waitees));
|
||||||
|
|
|
||||||
|
|
@ -12,30 +12,3 @@ create table if not exists Realisations (
|
||||||
);
|
);
|
||||||
|
|
||||||
create index if not exists IndexRealisations on Realisations(drvPath, outputName);
|
create index if not exists IndexRealisations on Realisations(drvPath, outputName);
|
||||||
|
|
||||||
-- We can end-up in a weird edge-case where a path depends on itself because
|
|
||||||
-- it’s an output of a CA derivation, that happens to be the same as one of its
|
|
||||||
-- dependencies.
|
|
||||||
-- In that case we have a dependency loop (path -> realisation1 -> realisation2
|
|
||||||
-- -> path) that we need to break by removing the dependencies between the
|
|
||||||
-- realisations
|
|
||||||
create trigger if not exists DeleteSelfRefsViaRealisations before delete on ValidPaths
|
|
||||||
begin
|
|
||||||
delete from RealisationsRefs where realisationReference in (
|
|
||||||
select id from Realisations where outputPath = old.id
|
|
||||||
);
|
|
||||||
end;
|
|
||||||
|
|
||||||
create table if not exists RealisationsRefs (
|
|
||||||
referrer integer not null,
|
|
||||||
realisationReference integer,
|
|
||||||
foreign key (referrer) references Realisations(id) on delete cascade,
|
|
||||||
foreign key (realisationReference) references Realisations(id) on delete restrict
|
|
||||||
);
|
|
||||||
-- used by deletion trigger
|
|
||||||
create index if not exists IndexRealisationsRefsRealisationReference on RealisationsRefs(realisationReference);
|
|
||||||
|
|
||||||
-- used by QueryRealisationReferences
|
|
||||||
create index if not exists IndexRealisationsRefs on RealisationsRefs(referrer);
|
|
||||||
-- used by cascade deletion when ValidPaths is deleted
|
|
||||||
create index if not exists IndexRealisationsRefsOnOutputPath on Realisations(outputPath);
|
|
||||||
|
|
|
||||||
|
|
@ -56,14 +56,6 @@ struct UnkeyedRealisation
|
||||||
|
|
||||||
StringSet signatures;
|
StringSet signatures;
|
||||||
|
|
||||||
/**
|
|
||||||
* The realisations that are required for the current one to be valid.
|
|
||||||
*
|
|
||||||
* When importing this realisation, the store will first check that all its
|
|
||||||
* dependencies exist, and map to the correct output path
|
|
||||||
*/
|
|
||||||
std::map<DrvOutput, StorePath> dependentRealisations;
|
|
||||||
|
|
||||||
std::string fingerprint(const DrvOutput & key) const;
|
std::string fingerprint(const DrvOutput & key) const;
|
||||||
|
|
||||||
void sign(const DrvOutput & key, const Signer &);
|
void sign(const DrvOutput & key, const Signer &);
|
||||||
|
|
@ -87,10 +79,6 @@ struct Realisation : UnkeyedRealisation
|
||||||
|
|
||||||
bool isCompatibleWith(const UnkeyedRealisation & other) const;
|
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;
|
bool operator==(const Realisation &) const = default;
|
||||||
auto operator<=>(const Realisation &) const = default;
|
auto operator<=>(const Realisation &) const = default;
|
||||||
};
|
};
|
||||||
|
|
@ -154,10 +142,6 @@ struct RealisedPath
|
||||||
*/
|
*/
|
||||||
const StorePath & path() const &;
|
const StorePath & path() const &;
|
||||||
|
|
||||||
void closure(Store & store, Set & ret) const;
|
|
||||||
static void closure(Store & store, const Set & startPaths, Set & ret);
|
|
||||||
Set closure(Store & store) const;
|
|
||||||
|
|
||||||
bool operator==(const RealisedPath &) const = default;
|
bool operator==(const RealisedPath &) const = default;
|
||||||
auto operator<=>(const RealisedPath &) const = default;
|
auto operator<=>(const RealisedPath &) const = default;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -1001,9 +1001,6 @@ decodeValidPathInfo(const Store & store, std::istream & str, std::optional<HashR
|
||||||
|
|
||||||
const ContentAddress * getDerivationCA(const BasicDerivation & drv);
|
const ContentAddress * getDerivationCA(const BasicDerivation & drv);
|
||||||
|
|
||||||
std::map<DrvOutput, StorePath>
|
|
||||||
drvOutputReferences(Store & store, const Derivation & drv, const StorePath & outputPath, Store * evalStore = nullptr);
|
|
||||||
|
|
||||||
template<>
|
template<>
|
||||||
struct json_avoids_null<TrustedFlag> : std::true_type
|
struct json_avoids_null<TrustedFlag> : std::true_type
|
||||||
{};
|
{};
|
||||||
|
|
|
||||||
|
|
@ -390,21 +390,6 @@ LocalStore::LocalStore(ref<const Config> config)
|
||||||
where drvPath = ?
|
where drvPath = ?
|
||||||
;
|
;
|
||||||
)");
|
)");
|
||||||
state->stmts->QueryRealisationReferences.create(
|
|
||||||
state->db,
|
|
||||||
R"(
|
|
||||||
select drvPath, outputName from Realisations
|
|
||||||
join RealisationsRefs on realisationReference = Realisations.id
|
|
||||||
where referrer = ?;
|
|
||||||
)");
|
|
||||||
state->stmts->AddRealisationReference.create(
|
|
||||||
state->db,
|
|
||||||
R"(
|
|
||||||
insert or replace into RealisationsRefs (referrer, realisationReference)
|
|
||||||
values (
|
|
||||||
(select id from Realisations where drvPath = ? and outputName = ?),
|
|
||||||
(select id from Realisations where drvPath = ? and outputName = ?));
|
|
||||||
)");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -654,25 +639,6 @@ void LocalStore::registerDrvOutput(const Realisation & info)
|
||||||
concatStringsSep(" ", info.signatures))
|
concatStringsSep(" ", info.signatures))
|
||||||
.exec();
|
.exec();
|
||||||
}
|
}
|
||||||
for (auto & [outputId, depPath] : info.dependentRealisations) {
|
|
||||||
auto localRealisation = queryRealisationCore_(*state, outputId);
|
|
||||||
if (!localRealisation)
|
|
||||||
throw Error(
|
|
||||||
"unable to register the derivation '%s' as it "
|
|
||||||
"depends on the non existent '%s'",
|
|
||||||
info.id.to_string(),
|
|
||||||
outputId.to_string());
|
|
||||||
if (localRealisation->second.outPath != depPath)
|
|
||||||
throw Error(
|
|
||||||
"unable to register the derivation '%s' as it "
|
|
||||||
"depends on a realisation of '%s' that doesn’t"
|
|
||||||
"match what we have locally",
|
|
||||||
info.id.to_string(),
|
|
||||||
outputId.to_string());
|
|
||||||
state->stmts->AddRealisationReference
|
|
||||||
.use()(info.id.strHash())(info.id.outputName)(outputId.strHash())(outputId.outputName)
|
|
||||||
.exec();
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1609,21 +1575,6 @@ std::optional<const UnkeyedRealisation> LocalStore::queryRealisation_(LocalStore
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
auto [realisationDbId, res] = *maybeCore;
|
auto [realisationDbId, res] = *maybeCore;
|
||||||
|
|
||||||
std::map<DrvOutput, StorePath> dependentRealisations;
|
|
||||||
auto useRealisationRefs(state.stmts->QueryRealisationReferences.use()(realisationDbId));
|
|
||||||
while (useRealisationRefs.next()) {
|
|
||||||
auto depId = DrvOutput{
|
|
||||||
Hash::parseAnyPrefixed(useRealisationRefs.getStr(0)),
|
|
||||||
useRealisationRefs.getStr(1),
|
|
||||||
};
|
|
||||||
auto dependentRealisation = queryRealisationCore_(state, depId);
|
|
||||||
assert(dependentRealisation); // Enforced by the db schema
|
|
||||||
auto outputPath = dependentRealisation->second.outPath;
|
|
||||||
dependentRealisations.insert({depId, outputPath});
|
|
||||||
}
|
|
||||||
|
|
||||||
res.dependentRealisations = dependentRealisations;
|
|
||||||
|
|
||||||
return {res};
|
return {res};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -330,65 +330,6 @@ StorePaths Store::topoSortPaths(const StorePathSet & paths)
|
||||||
}});
|
}});
|
||||||
}
|
}
|
||||||
|
|
||||||
std::map<DrvOutput, StorePath>
|
|
||||||
drvOutputReferences(const std::set<Realisation> & inputRealisations, const StorePathSet & pathReferences)
|
|
||||||
{
|
|
||||||
std::map<DrvOutput, StorePath> res;
|
|
||||||
|
|
||||||
for (const auto & input : inputRealisations) {
|
|
||||||
if (pathReferences.count(input.outPath)) {
|
|
||||||
res.insert({input.id, input.outPath});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::map<DrvOutput, StorePath>
|
|
||||||
drvOutputReferences(Store & store, const Derivation & drv, const StorePath & outputPath, Store * evalStore_)
|
|
||||||
{
|
|
||||||
auto & evalStore = evalStore_ ? *evalStore_ : store;
|
|
||||||
|
|
||||||
std::set<Realisation> inputRealisations;
|
|
||||||
|
|
||||||
auto accumRealisations = [&](this auto & self,
|
|
||||||
const StorePath & inputDrv,
|
|
||||||
const DerivedPathMap<StringSet>::ChildNode & inputNode) -> void {
|
|
||||||
if (!inputNode.value.empty()) {
|
|
||||||
auto outputHashes = staticOutputHashes(evalStore, evalStore.readDerivation(inputDrv));
|
|
||||||
for (const auto & outputName : inputNode.value) {
|
|
||||||
auto outputHash = get(outputHashes, outputName);
|
|
||||||
if (!outputHash)
|
|
||||||
throw Error(
|
|
||||||
"output '%s' of derivation '%s' isn't realised", outputName, store.printStorePath(inputDrv));
|
|
||||||
DrvOutput key{*outputHash, outputName};
|
|
||||||
auto thisRealisation = store.queryRealisation(key);
|
|
||||||
if (!thisRealisation)
|
|
||||||
throw Error(
|
|
||||||
"output '%s' of derivation '%s' isn’t built", outputName, store.printStorePath(inputDrv));
|
|
||||||
inputRealisations.insert({*thisRealisation, std::move(key)});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!inputNode.value.empty()) {
|
|
||||||
auto d = makeConstantStorePathRef(inputDrv);
|
|
||||||
for (const auto & [outputName, childNode] : inputNode.childMap) {
|
|
||||||
SingleDerivedPath next = SingleDerivedPath::Built{d, outputName};
|
|
||||||
self(
|
|
||||||
// TODO deep resolutions for dynamic derivations, issue #8947, would go here.
|
|
||||||
resolveDerivedPath(store, next, evalStore_),
|
|
||||||
childNode);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
for (const auto & [inputDrv, inputNode] : drv.inputDrvs.map)
|
|
||||||
accumRealisations(inputDrv, inputNode);
|
|
||||||
|
|
||||||
auto info = store.queryPathInfo(outputPath);
|
|
||||||
|
|
||||||
return drvOutputReferences(Realisation::closure(store, inputRealisations), info->references);
|
|
||||||
}
|
|
||||||
|
|
||||||
OutputPathMap resolveDerivedPath(Store & store, const DerivedPath::Built & bfd, Store * evalStore_)
|
OutputPathMap resolveDerivedPath(Store & store, const DerivedPath::Built & bfd, Store * evalStore_)
|
||||||
{
|
{
|
||||||
auto drvPath = resolveDerivedPath(store, *bfd.drvPath, evalStore_);
|
auto drvPath = resolveDerivedPath(store, *bfd.drvPath, evalStore_);
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
#include "nix/store/realisation.hh"
|
#include "nix/store/realisation.hh"
|
||||||
#include "nix/store/store-api.hh"
|
#include "nix/store/store-api.hh"
|
||||||
#include "nix/util/closure.hh"
|
|
||||||
#include "nix/util/signature/local-keys.hh"
|
#include "nix/util/signature/local-keys.hh"
|
||||||
#include "nix/util/json-utils.hh"
|
#include "nix/util/json-utils.hh"
|
||||||
#include <nlohmann/json.hpp>
|
#include <nlohmann/json.hpp>
|
||||||
|
|
@ -26,41 +25,6 @@ std::string DrvOutput::to_string() const
|
||||||
return strHash() + "!" + outputName;
|
return strHash() + "!" + outputName;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::set<Realisation> Realisation::closure(Store & store, const std::set<Realisation> & startOutputs)
|
|
||||||
{
|
|
||||||
std::set<Realisation> res;
|
|
||||||
Realisation::closure(store, startOutputs, res);
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Realisation::closure(Store & store, const std::set<Realisation> & startOutputs, std::set<Realisation> & res)
|
|
||||||
{
|
|
||||||
auto getDeps = [&](const Realisation & current) -> std::set<Realisation> {
|
|
||||||
std::set<Realisation> res;
|
|
||||||
for (auto & [currentDep, _] : current.dependentRealisations) {
|
|
||||||
if (auto currentRealisation = store.queryRealisation(currentDep))
|
|
||||||
res.insert({*currentRealisation, currentDep});
|
|
||||||
else
|
|
||||||
throw Error("Unrealised derivation '%s'", currentDep.to_string());
|
|
||||||
}
|
|
||||||
return res;
|
|
||||||
};
|
|
||||||
|
|
||||||
computeClosure<Realisation>(
|
|
||||||
startOutputs,
|
|
||||||
res,
|
|
||||||
[&](const Realisation & current, std::function<void(std::promise<std::set<Realisation>> &)> processEdges) {
|
|
||||||
std::promise<std::set<Realisation>> promise;
|
|
||||||
try {
|
|
||||||
auto res = getDeps(current);
|
|
||||||
promise.set_value(res);
|
|
||||||
} catch (...) {
|
|
||||||
promise.set_exception(std::current_exception());
|
|
||||||
}
|
|
||||||
return processEdges(promise);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string UnkeyedRealisation::fingerprint(const DrvOutput & key) const
|
std::string UnkeyedRealisation::fingerprint(const DrvOutput & key) const
|
||||||
{
|
{
|
||||||
nlohmann::json serialized = Realisation{*this, key};
|
nlohmann::json serialized = Realisation{*this, key};
|
||||||
|
|
@ -99,43 +63,7 @@ const StorePath & RealisedPath::path() const &
|
||||||
|
|
||||||
bool Realisation::isCompatibleWith(const UnkeyedRealisation & other) const
|
bool Realisation::isCompatibleWith(const UnkeyedRealisation & other) const
|
||||||
{
|
{
|
||||||
if (outPath == other.outPath) {
|
return outPath == other.outPath;
|
||||||
if (dependentRealisations.empty() != other.dependentRealisations.empty()) {
|
|
||||||
warn(
|
|
||||||
"Encountered a realisation for '%s' with an empty set of "
|
|
||||||
"dependencies. This is likely an artifact from an older Nix. "
|
|
||||||
"I’ll try to fix the realisation if I can",
|
|
||||||
id.to_string());
|
|
||||||
return true;
|
|
||||||
} else if (dependentRealisations == other.dependentRealisations) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void RealisedPath::closure(Store & store, const RealisedPath::Set & startPaths, RealisedPath::Set & ret)
|
|
||||||
{
|
|
||||||
// FIXME: This only builds the store-path closure, not the real realisation
|
|
||||||
// closure
|
|
||||||
StorePathSet initialStorePaths, pathsClosure;
|
|
||||||
for (auto & path : startPaths)
|
|
||||||
initialStorePaths.insert(path.path());
|
|
||||||
store.computeFSClosure(initialStorePaths, pathsClosure);
|
|
||||||
ret.insert(startPaths.begin(), startPaths.end());
|
|
||||||
ret.insert(pathsClosure.begin(), pathsClosure.end());
|
|
||||||
}
|
|
||||||
|
|
||||||
void RealisedPath::closure(Store & store, RealisedPath::Set & ret) const
|
|
||||||
{
|
|
||||||
RealisedPath::closure(store, {*this}, ret);
|
|
||||||
}
|
|
||||||
|
|
||||||
RealisedPath::Set RealisedPath::closure(Store & store) const
|
|
||||||
{
|
|
||||||
RealisedPath::Set ret;
|
|
||||||
closure(store, ret);
|
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace nix
|
} // namespace nix
|
||||||
|
|
@ -162,27 +90,19 @@ UnkeyedRealisation adl_serializer<UnkeyedRealisation>::from_json(const json & js
|
||||||
if (auto signaturesOpt = optionalValueAt(json, "signatures"))
|
if (auto signaturesOpt = optionalValueAt(json, "signatures"))
|
||||||
signatures = *signaturesOpt;
|
signatures = *signaturesOpt;
|
||||||
|
|
||||||
std::map<DrvOutput, StorePath> dependentRealisations;
|
|
||||||
if (auto jsonDependencies = optionalValueAt(json, "dependentRealisations"))
|
|
||||||
for (auto & [jsonDepId, jsonDepOutPath] : getObject(*jsonDependencies))
|
|
||||||
dependentRealisations.insert({DrvOutput::parse(jsonDepId), jsonDepOutPath});
|
|
||||||
|
|
||||||
return UnkeyedRealisation{
|
return UnkeyedRealisation{
|
||||||
.outPath = valueAt(json, "outPath"),
|
.outPath = valueAt(json, "outPath"),
|
||||||
.signatures = signatures,
|
.signatures = signatures,
|
||||||
.dependentRealisations = dependentRealisations,
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
void adl_serializer<UnkeyedRealisation>::to_json(json & json, const UnkeyedRealisation & r)
|
void adl_serializer<UnkeyedRealisation>::to_json(json & json, const UnkeyedRealisation & r)
|
||||||
{
|
{
|
||||||
auto jsonDependentRealisations = nlohmann::json::object();
|
|
||||||
for (auto & [depId, depOutPath] : r.dependentRealisations)
|
|
||||||
jsonDependentRealisations.emplace(depId.to_string(), depOutPath);
|
|
||||||
json = {
|
json = {
|
||||||
{"outPath", r.outPath},
|
{"outPath", r.outPath},
|
||||||
{"signatures", r.signatures},
|
{"signatures", r.signatures},
|
||||||
{"dependentRealisations", jsonDependentRealisations},
|
// back-compat
|
||||||
|
{"dependentRealisations", json::object()},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -292,7 +292,7 @@ std::vector<KeyedBuildResult> RestrictedStore::buildPathsWithResults(
|
||||||
next->computeFSClosure(newPaths, closure);
|
next->computeFSClosure(newPaths, closure);
|
||||||
for (auto & path : closure)
|
for (auto & path : closure)
|
||||||
goal.addDependency(path);
|
goal.addDependency(path);
|
||||||
for (auto & real : Realisation::closure(*next, newRealisations))
|
for (auto & real : newRealisations)
|
||||||
goal.addedDrvOutputs.insert(real.id);
|
goal.addedDrvOutputs.insert(real.id);
|
||||||
|
|
||||||
return results;
|
return results;
|
||||||
|
|
|
||||||
|
|
@ -915,36 +915,21 @@ std::map<StorePath, StorePath> copyPaths(
|
||||||
SubstituteFlag substitute)
|
SubstituteFlag substitute)
|
||||||
{
|
{
|
||||||
StorePathSet storePaths;
|
StorePathSet storePaths;
|
||||||
std::set<Realisation> toplevelRealisations;
|
std::vector<const Realisation *> realisations;
|
||||||
for (auto & path : paths) {
|
for (auto & path : paths) {
|
||||||
storePaths.insert(path.path());
|
storePaths.insert(path.path());
|
||||||
if (auto * realisation = std::get_if<Realisation>(&path.raw)) {
|
if (auto * realisation = std::get_if<Realisation>(&path.raw)) {
|
||||||
experimentalFeatureSettings.require(Xp::CaDerivations);
|
experimentalFeatureSettings.require(Xp::CaDerivations);
|
||||||
toplevelRealisations.insert(*realisation);
|
realisations.push_back(realisation);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
auto pathsMap = copyPaths(srcStore, dstStore, storePaths, repair, checkSigs, substitute);
|
auto pathsMap = copyPaths(srcStore, dstStore, storePaths, repair, checkSigs, substitute);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Copy the realisation closure
|
// Copy the realisations. TODO batch this
|
||||||
processGraph<Realisation>(
|
for (const auto * realisation : realisations)
|
||||||
Realisation::closure(srcStore, toplevelRealisations),
|
dstStore.registerDrvOutput(*realisation, checkSigs);
|
||||||
[&](const Realisation & current) -> std::set<Realisation> {
|
|
||||||
std::set<Realisation> children;
|
|
||||||
for (const auto & [drvOutput, _] : current.dependentRealisations) {
|
|
||||||
auto currentChild = srcStore.queryRealisation(drvOutput);
|
|
||||||
if (!currentChild)
|
|
||||||
throw Error(
|
|
||||||
"incomplete realisation closure: '%s' is a "
|
|
||||||
"dependency of '%s' but isn't registered",
|
|
||||||
drvOutput.to_string(),
|
|
||||||
current.id.to_string());
|
|
||||||
children.insert({*currentChild, drvOutput});
|
|
||||||
}
|
|
||||||
return children;
|
|
||||||
},
|
|
||||||
[&](const Realisation & current) -> void { dstStore.registerDrvOutput(current, checkSigs); });
|
|
||||||
} catch (MissingExperimentalFeature & e) {
|
} catch (MissingExperimentalFeature & e) {
|
||||||
// Don't fail if the remote doesn't support CA derivations is it might
|
// Don't fail if the remote doesn't support CA derivations is it might
|
||||||
// not be within our control to change that, and we might still want
|
// not be within our control to change that, and we might still want
|
||||||
|
|
@ -1055,8 +1040,19 @@ void copyClosure(
|
||||||
if (&srcStore == &dstStore)
|
if (&srcStore == &dstStore)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
RealisedPath::Set closure;
|
StorePathSet closure0;
|
||||||
RealisedPath::closure(srcStore, paths, closure);
|
for (auto & path : paths) {
|
||||||
|
if (auto * opaquePath = std::get_if<OpaquePath>(&path.raw)) {
|
||||||
|
closure0.insert(opaquePath->path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
StorePathSet closure1;
|
||||||
|
srcStore.computeFSClosure(closure0, closure1);
|
||||||
|
|
||||||
|
RealisedPath::Set closure = paths;
|
||||||
|
for (auto && path : closure1)
|
||||||
|
closure.insert({std::move(path)});
|
||||||
|
|
||||||
copyPaths(srcStore, dstStore, closure, repair, checkSigs, substitute);
|
copyPaths(srcStore, dstStore, closure, repair, checkSigs, substitute);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -25,4 +25,9 @@ nix build -f nondeterministic.nix dep2 --no-link
|
||||||
# If everything goes right, we should rebuild dep2 rather than fetch it from
|
# If everything goes right, we should rebuild dep2 rather than fetch it from
|
||||||
# the cache (because that would mean duplicating `current-time` in the closure),
|
# the cache (because that would mean duplicating `current-time` in the closure),
|
||||||
# and have `dep1 == dep2`.
|
# and have `dep1 == dep2`.
|
||||||
|
|
||||||
|
# FIXME: Force the use of small-step resolutions only to fix this in a
|
||||||
|
# better way (#11896, #11928).
|
||||||
|
skipTest "temporarily broken because dependent realisations are removed"
|
||||||
|
|
||||||
nix build --substituters "$REMOTE_STORE" -f nondeterministic.nix toplevel --no-require-sigs --no-link
|
nix build --substituters "$REMOTE_STORE" -f nondeterministic.nix toplevel --no-require-sigs --no-link
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,10 @@ nix copy --to "$REMOTE_STORE" --file ./content-addressed.nix
|
||||||
|
|
||||||
# Restart the build on an empty store, ensuring that we don't build
|
# Restart the build on an empty store, ensuring that we don't build
|
||||||
clearStore
|
clearStore
|
||||||
buildDrvs --substitute --substituters "$REMOTE_STORE" --no-require-sigs -j0 transitivelyDependentCA
|
# FIXME: `dependentCA` should not need to be explicitly mentioned in
|
||||||
|
# this. Force the use of small-step resolutions only to allow not
|
||||||
|
# mentioning it explicitly again. (#11896, #11928).
|
||||||
|
buildDrvs --substitute --substituters "$REMOTE_STORE" --no-require-sigs -j0 transitivelyDependentCA dependentCA
|
||||||
# Check that the thing we’ve just substituted has its realisation stored
|
# Check that the thing we’ve just substituted has its realisation stored
|
||||||
nix realisation info --file ./content-addressed.nix transitivelyDependentCA
|
nix realisation info --file ./content-addressed.nix transitivelyDependentCA
|
||||||
# Check that its dependencies have it too
|
# Check that its dependencies have it too
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue