We want to be able to write down `foo.drv^bar.drv^baz`:
`foo.drv^bar.drv` is the dynamic derivation (since it is itself a
derivation output, `bar.drv` from `foo.drv`).
To that end, we create `Single{Derivation,BuiltPath}` types, that are
very similar except instead of having multiple outputs (in a set or
map), they have a single one. This is for everything to the left of the
rightmost `^`.
`NixStringContextElem` has an analogous change, and now can reuse
`SingleDerivedPath` at the top level. In fact, if we ever get rid of
`DrvDeep`, `NixStringContextElem` could be replaced with
`SingleDerivedPath` entirely!
Important note: some JSON formats have changed.
We already can *produce* dynamic derivations, but we can't refer to them
directly. Today, we can merely express building or example at the top
imperatively over time by building `foo.drv^bar.drv`, and then with a
second nix invocation doing `<result-from-first>^baz`, but this is not
declarative. The ethos of Nix of being able to write down the full plan
everything you want to do, and then execute than plan with a single
command, and for that we need the new inductive form of these types.
Co-authored-by: Robert Hensing <roberth@users.noreply.github.com>
Co-authored-by: Valentin Gagarin <valentin.gagarin@tweag.io>
128 lines
4.0 KiB
C++
128 lines
4.0 KiB
C++
#include "built-path.hh"
|
||
#include "derivations.hh"
|
||
#include "store-api.hh"
|
||
|
||
#include <nlohmann/json.hpp>
|
||
|
||
#include <optional>
|
||
|
||
namespace nix {
|
||
|
||
#define CMP_ONE(CHILD_TYPE, MY_TYPE, FIELD, COMPARATOR) \
|
||
bool MY_TYPE ::operator COMPARATOR (const MY_TYPE & other) const \
|
||
{ \
|
||
const MY_TYPE* me = this; \
|
||
auto fields1 = std::make_tuple<const CHILD_TYPE &, const FIELD_TYPE &>(*me->drvPath, me->FIELD); \
|
||
me = &other; \
|
||
auto fields2 = std::make_tuple<const CHILD_TYPE &, const FIELD_TYPE &>(*me->drvPath, me->FIELD); \
|
||
return fields1 COMPARATOR fields2; \
|
||
}
|
||
#define CMP(CHILD_TYPE, MY_TYPE, FIELD) \
|
||
CMP_ONE(CHILD_TYPE, MY_TYPE, FIELD, ==) \
|
||
CMP_ONE(CHILD_TYPE, MY_TYPE, FIELD, !=) \
|
||
CMP_ONE(CHILD_TYPE, MY_TYPE, FIELD, <)
|
||
|
||
#define FIELD_TYPE std::pair<std::string, StorePath>
|
||
CMP(SingleBuiltPath, SingleBuiltPathBuilt, output)
|
||
#undef FIELD_TYPE
|
||
|
||
#define FIELD_TYPE std::map<std::string, StorePath>
|
||
CMP(SingleBuiltPath, BuiltPathBuilt, outputs)
|
||
#undef FIELD_TYPE
|
||
|
||
#undef CMP
|
||
#undef CMP_ONE
|
||
|
||
StorePath SingleBuiltPath::outPath() const
|
||
{
|
||
return std::visit(
|
||
overloaded{
|
||
[](const SingleBuiltPath::Opaque & p) { return p.path; },
|
||
[](const SingleBuiltPath::Built & b) { return b.output.second; },
|
||
}, raw()
|
||
);
|
||
}
|
||
|
||
StorePathSet BuiltPath::outPaths() const
|
||
{
|
||
return std::visit(
|
||
overloaded{
|
||
[](const BuiltPath::Opaque & p) { return StorePathSet{p.path}; },
|
||
[](const BuiltPath::Built & b) {
|
||
StorePathSet res;
|
||
for (auto & [_, path] : b.outputs)
|
||
res.insert(path);
|
||
return res;
|
||
},
|
||
}, raw()
|
||
);
|
||
}
|
||
|
||
nlohmann::json BuiltPath::Built::toJSON(const Store & store) const
|
||
{
|
||
nlohmann::json res;
|
||
res["drvPath"] = drvPath->toJSON(store);
|
||
for (const auto & [outputName, outputPath] : outputs) {
|
||
res["outputs"][outputName] = store.printStorePath(outputPath);
|
||
}
|
||
return res;
|
||
}
|
||
|
||
nlohmann::json SingleBuiltPath::Built::toJSON(const Store & store) const
|
||
{
|
||
nlohmann::json res;
|
||
res["drvPath"] = drvPath->toJSON(store);
|
||
auto & [outputName, outputPath] = output;
|
||
res["output"] = outputName;
|
||
res["outputPath"] = store.printStorePath(outputPath);
|
||
return res;
|
||
}
|
||
|
||
nlohmann::json SingleBuiltPath::toJSON(const Store & store) const
|
||
{
|
||
return std::visit([&](const auto & buildable) {
|
||
return buildable.toJSON(store);
|
||
}, raw());
|
||
}
|
||
|
||
nlohmann::json BuiltPath::toJSON(const Store & store) const
|
||
{
|
||
return std::visit([&](const auto & buildable) {
|
||
return buildable.toJSON(store);
|
||
}, raw());
|
||
}
|
||
|
||
RealisedPath::Set BuiltPath::toRealisedPaths(Store & store) const
|
||
{
|
||
RealisedPath::Set res;
|
||
std::visit(
|
||
overloaded{
|
||
[&](const BuiltPath::Opaque & p) { res.insert(p.path); },
|
||
[&](const BuiltPath::Built & p) {
|
||
auto drvHashes =
|
||
staticOutputHashes(store, store.readDerivation(p.drvPath->outPath()));
|
||
for (auto& [outputName, outputPath] : p.outputs) {
|
||
if (experimentalFeatureSettings.isEnabled(
|
||
Xp::CaDerivations)) {
|
||
auto drvOutput = get(drvHashes, outputName);
|
||
if (!drvOutput)
|
||
throw Error(
|
||
"the derivation '%s' has unrealised output '%s' (derived-path.cc/toRealisedPaths)",
|
||
store.printStorePath(p.drvPath->outPath()), outputName);
|
||
auto thisRealisation = store.queryRealisation(
|
||
DrvOutput{*drvOutput, outputName});
|
||
assert(thisRealisation); // We’ve built it, so we must
|
||
// have the realisation
|
||
res.insert(*thisRealisation);
|
||
} else {
|
||
res.insert(outputPath);
|
||
}
|
||
}
|
||
},
|
||
},
|
||
raw());
|
||
return res;
|
||
}
|
||
|
||
}
|