1
1
Fork 0
mirror of https://github.com/NixOS/nix.git synced 2025-11-09 12:06:01 +01:00
This commit is contained in:
John Ericson 2025-11-08 14:35:44 +01:00 committed by GitHub
commit 2be33149af
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
23 changed files with 117 additions and 415 deletions

View file

@ -1,27 +1,27 @@
{{#include build-trace-entry-v1-fixed.md}} {{#include build-trace-entry-v2-fixed.md}}
## Examples ## Examples
### Simple build trace entry ### Simple build trace entry
```json ```json
{{#include schema/build-trace-entry-v1/simple.json}} {{#include schema/build-trace-entry-v2/simple.json}}
``` ```
### Build trace entry with dependencies ### Build trace entry with dependencies
```json ```json
{{#include schema/build-trace-entry-v1/with-dependent-realisations.json}} {{#include schema/build-trace-entry-v2/with-dependent-realisations.json}}
``` ```
### Build trace entry with signature ### Build trace entry with signature
```json ```json
{{#include schema/build-trace-entry-v1/with-signature.json}} {{#include schema/build-trace-entry-v2/with-signature.json}}
``` ```
<!-- <!--
## Raw Schema ## Raw Schema
[JSON Schema for Build Trace Entry v1](schema/build-trace-entry-v1.json) [JSON Schema for Build Trace Entry v1](schema/build-trace-entry-v2.json)
--> -->

View file

@ -15,7 +15,7 @@ schemas = [
'store-object-info-v2', 'store-object-info-v2',
'derivation-v4', 'derivation-v4',
'deriving-path-v1', 'deriving-path-v1',
'build-trace-entry-v1', 'build-trace-entry-v2',
'build-result-v1', 'build-result-v1',
] ]

View file

@ -83,7 +83,7 @@ properties:
description: | description: |
A mapping from output names to their build trace entries. A mapping from output names to their build trace entries.
additionalProperties: additionalProperties:
"$ref": "build-trace-entry-v1.yaml" "$ref": "build-trace-entry-v2.yaml"
failure: failure:
type: object type: object

View file

@ -1,5 +1,5 @@
"$schema": "http://json-schema.org/draft-04/schema" "$schema": "http://json-schema.org/draft-04/schema"
"$id": "https://nix.dev/manual/nix/latest/protocols/json/schema/build-trace-entry-v1.json" "$id": "https://nix.dev/manual/nix/latest/protocols/json/schema/build-trace-entry-v2.json"
title: Build Trace Entry title: Build Trace Entry
description: | description: |
A record of a successful build outcome for a specific derivation output. A record of a successful build outcome for a specific derivation output.
@ -12,11 +12,16 @@ description: |
> [**experimental**](@docroot@/development/experimental-features.md#xp-feature-ca-derivations) > [**experimental**](@docroot@/development/experimental-features.md#xp-feature-ca-derivations)
> and subject to change. > and subject to change.
Verision history:
- Version 1: Original format
- Version 2: Remove `dependentRealisations`
type: object type: object
required: required:
- id - id
- outPath - outPath
- dependentRealisations
- signatures - signatures
properties: properties:
id: id:
@ -41,26 +46,6 @@ properties:
description: | description: |
The path to the store object that resulted from building this derivation for the given output name. The path to the store object that resulted from building this derivation for the given output name.
dependentRealisations:
type: object
title: Underlying Base Build Trace
description: |
This is for [*derived*](@docroot@/store/build-trace.md#derived) build trace entries to ensure coherence.
Keys are derivation output IDs (same format as the main `id` field).
Values are the store paths that those dependencies resolved to.
As described in the linked section on derived build trace traces, derived build trace entries must be kept in addition and not instead of the underlying base build entries.
This is the set of base build trace entries that this derived build trace is derived from.
(The set is also a map since this miniature base build trace must be coherent, mapping each key to a single value.)
patternProperties:
"^sha256:[0-9a-f]{64}![a-zA-Z_][a-zA-Z0-9_-]*$":
$ref: "store-path-v1.yaml"
title: Dependent Store Path
description: Store path that this dependency resolved to during the build
additionalProperties: false
signatures: signatures:
type: array type: array
title: Build Signatures title: Build Signatures
@ -71,4 +56,7 @@ properties:
title: Signature title: Signature
description: A single cryptographic signature description: A single cryptographic signature
additionalProperties: false additionalProperties:
dependentRealisations:
description: deprecated field
type: object

View file

@ -56,7 +56,7 @@ schemas = [
}, },
{ {
'stem' : 'build-trace-entry', 'stem' : 'build-trace-entry',
'schema' : schema_dir / 'build-trace-entry-v1.yaml', 'schema' : schema_dir / 'build-trace-entry-v2.yaml',
'files' : [ 'files' : [
'simple.json', 'simple.json',
'with-dependent-realisations.json', 'with-dependent-realisations.json',

View file

@ -69,14 +69,20 @@ public:
} }
}; };
#define VERSIONED_CHARACTERIZATION_TEST(FIXTURE, NAME, STEM, VERSION, VALUE) \ #define VERSIONED_READ_CHARACTERIZATION_TEST(FIXTURE, NAME, STEM, VERSION, VALUE) \
TEST_F(FIXTURE, NAME##_read) \ TEST_F(FIXTURE, NAME##_read) \
{ \ { \
readProtoTest(STEM, VERSION, VALUE); \ readProtoTest(STEM, VERSION, VALUE); \
} \
TEST_F(FIXTURE, NAME##_write) \
{ \
writeProtoTest(STEM, VERSION, VALUE); \
} }
#define VERSIONED_WRITE_CHARACTERIZATION_TEST(FIXTURE, NAME, STEM, VERSION, VALUE) \
TEST_F(FIXTURE, NAME##_write) \
{ \
writeProtoTest(STEM, VERSION, VALUE); \
}
#define VERSIONED_CHARACTERIZATION_TEST(FIXTURE, NAME, STEM, VERSION, VALUE) \
VERSIONED_READ_CHARACTERIZATION_TEST(FIXTURE, NAME, STEM, VERSION, VALUE) \
VERSIONED_WRITE_CHARACTERIZATION_TEST(FIXTURE, NAME, STEM, VERSION, VALUE)
} // namespace nix } // namespace nix

View file

@ -46,16 +46,22 @@ 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) \
{ \
writeProtoTest(STEM, VALUE); \
} }
#define WRITE_CHARACTERIZATION_TEST(NAME, STEM, VALUE) \
TEST_F(CommonProtoTest, NAME##_write) \
{ \
writeProtoTest(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",
@ -132,7 +138,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>{
@ -140,16 +146,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="),

View file

@ -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

View file

@ -115,7 +115,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",
@ -125,16 +125,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="),

View file

@ -168,7 +168,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",
@ -178,16 +178,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="),

View file

@ -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);
} }

View file

@ -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));

View file

@ -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
-- its an output of a CA derivation, that happens to be the same as one of its
-- dependencies.
-- In that case we have a dependency loop (path -> realisation1 -> realisation2
-- -> path) that we need to break by removing the dependencies between the
-- realisations
create trigger if not exists DeleteSelfRefsViaRealisations before delete on ValidPaths
begin
delete from RealisationsRefs where realisationReference in (
select id from Realisations where outputPath = old.id
);
end;
create table if not exists RealisationsRefs (
referrer integer not null,
realisationReference integer,
foreign key (referrer) references Realisations(id) on delete cascade,
foreign key (realisationReference) references Realisations(id) on delete restrict
);
-- used by deletion trigger
create index if not exists IndexRealisationsRefsRealisationReference on RealisationsRefs(realisationReference);
-- used by QueryRealisationReferences
create index if not exists IndexRealisationsRefs on RealisationsRefs(referrer);
-- used by cascade deletion when ValidPaths is deleted
create index if not exists IndexRealisationsRefsOnOutputPath on Realisations(outputPath);

View file

@ -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;
}; };

View file

@ -1001,7 +1001,4 @@ 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);
} // namespace nix } // namespace nix

View file

@ -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 doesnt"
"match what we have locally",
info.id.to_string(),
outputId.to_string());
state->stmts->AddRealisationReference
.use()(info.id.strHash())(info.id.outputName)(outputId.strHash())(outputId.outputName)
.exec();
}
}); });
} }
@ -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};
} }

View file

@ -329,65 +329,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' isnt 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_);

View file

@ -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. "
"Ill try to fix the realisation if I can",
id.to_string());
return true;
} else if (dependentRealisations == other.dependentRealisations) {
return true;
}
}
return false;
}
void RealisedPath::closure(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
@ -152,27 +80,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()},
}; };
} }

View file

@ -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;

View file

@ -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);
} }

View file

@ -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

View file

@ -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 weve just substituted has its realisation stored # Check that the thing weve 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