mirror of
https://github.com/NixOS/nix
synced 2025-06-29 10:31:15 +02:00
Merge pull request #8369 from obsidiansystems/inductive-derived-path
Make the Derived Path family of types inductive for dynamic derivations
This commit is contained in:
commit
010dc7958e
48 changed files with 1136 additions and 267 deletions
|
@ -65,7 +65,7 @@ namespace nix {
|
|||
|
||||
DerivationGoal::DerivationGoal(const StorePath & drvPath,
|
||||
const OutputsSpec & wantedOutputs, Worker & worker, BuildMode buildMode)
|
||||
: Goal(worker, DerivedPath::Built { .drvPath = drvPath, .outputs = wantedOutputs })
|
||||
: Goal(worker, DerivedPath::Built { .drvPath = makeConstantStorePathRef(drvPath), .outputs = wantedOutputs })
|
||||
, useDerivation(true)
|
||||
, drvPath(drvPath)
|
||||
, wantedOutputs(wantedOutputs)
|
||||
|
@ -74,7 +74,7 @@ DerivationGoal::DerivationGoal(const StorePath & drvPath,
|
|||
state = &DerivationGoal::getDerivation;
|
||||
name = fmt(
|
||||
"building of '%s' from .drv file",
|
||||
DerivedPath::Built { drvPath, wantedOutputs }.to_string(worker.store));
|
||||
DerivedPath::Built { makeConstantStorePathRef(drvPath), wantedOutputs }.to_string(worker.store));
|
||||
trace("created");
|
||||
|
||||
mcExpectedBuilds = std::make_unique<MaintainCount<uint64_t>>(worker.expectedBuilds);
|
||||
|
@ -84,7 +84,7 @@ DerivationGoal::DerivationGoal(const StorePath & drvPath,
|
|||
|
||||
DerivationGoal::DerivationGoal(const StorePath & drvPath, const BasicDerivation & drv,
|
||||
const OutputsSpec & wantedOutputs, Worker & worker, BuildMode buildMode)
|
||||
: Goal(worker, DerivedPath::Built { .drvPath = drvPath, .outputs = wantedOutputs })
|
||||
: Goal(worker, DerivedPath::Built { .drvPath = makeConstantStorePathRef(drvPath), .outputs = wantedOutputs })
|
||||
, useDerivation(false)
|
||||
, drvPath(drvPath)
|
||||
, wantedOutputs(wantedOutputs)
|
||||
|
@ -95,7 +95,7 @@ DerivationGoal::DerivationGoal(const StorePath & drvPath, const BasicDerivation
|
|||
state = &DerivationGoal::haveDerivation;
|
||||
name = fmt(
|
||||
"building of '%s' from in-memory derivation",
|
||||
DerivedPath::Built { drvPath, drv.outputNames() }.to_string(worker.store));
|
||||
DerivedPath::Built { makeConstantStorePathRef(drvPath), drv.outputNames() }.to_string(worker.store));
|
||||
trace("created");
|
||||
|
||||
mcExpectedBuilds = std::make_unique<MaintainCount<uint64_t>>(worker.expectedBuilds);
|
||||
|
@ -1490,7 +1490,7 @@ void DerivationGoal::waiteeDone(GoalPtr waitee, ExitCode result)
|
|||
|
||||
for (auto & outputName : outputs->second) {
|
||||
auto buildResult = dg->getBuildResult(DerivedPath::Built {
|
||||
.drvPath = dg->drvPath,
|
||||
.drvPath = makeConstantStorePathRef(dg->drvPath),
|
||||
.outputs = OutputsSpec::Names { outputName },
|
||||
});
|
||||
if (buildResult.success()) {
|
||||
|
|
|
@ -77,7 +77,7 @@ BuildResult Store::buildDerivation(const StorePath & drvPath, const BasicDerivat
|
|||
try {
|
||||
worker.run(Goals{goal});
|
||||
return goal->getBuildResult(DerivedPath::Built {
|
||||
.drvPath = drvPath,
|
||||
.drvPath = makeConstantStorePathRef(drvPath),
|
||||
.outputs = OutputsSpec::All {},
|
||||
});
|
||||
} catch (Error & e) {
|
||||
|
|
|
@ -1172,6 +1172,19 @@ void LocalDerivationGoal::writeStructuredAttrs()
|
|||
}
|
||||
|
||||
|
||||
static StorePath pathPartOfReq(const SingleDerivedPath & req)
|
||||
{
|
||||
return std::visit(overloaded {
|
||||
[&](const SingleDerivedPath::Opaque & bo) {
|
||||
return bo.path;
|
||||
},
|
||||
[&](const SingleDerivedPath::Built & bfd) {
|
||||
return pathPartOfReq(*bfd.drvPath);
|
||||
},
|
||||
}, req.raw());
|
||||
}
|
||||
|
||||
|
||||
static StorePath pathPartOfReq(const DerivedPath & req)
|
||||
{
|
||||
return std::visit(overloaded {
|
||||
|
@ -1179,7 +1192,7 @@ static StorePath pathPartOfReq(const DerivedPath & req)
|
|||
return bo.path;
|
||||
},
|
||||
[&](const DerivedPath::Built & bfd) {
|
||||
return bfd.drvPath;
|
||||
return pathPartOfReq(*bfd.drvPath);
|
||||
},
|
||||
}, req.raw());
|
||||
}
|
||||
|
|
|
@ -111,7 +111,10 @@ GoalPtr Worker::makeGoal(const DerivedPath & req, BuildMode buildMode)
|
|||
{
|
||||
return std::visit(overloaded {
|
||||
[&](const DerivedPath::Built & bfd) -> GoalPtr {
|
||||
return makeDerivationGoal(bfd.drvPath, bfd.outputs, buildMode);
|
||||
if (auto bop = std::get_if<DerivedPath::Opaque>(&*bfd.drvPath))
|
||||
return makeDerivationGoal(bop->path, bfd.outputs, buildMode);
|
||||
else
|
||||
throw UnimplementedError("Building dynamic derivations in one shot is not yet implemented.");
|
||||
},
|
||||
[&](const DerivedPath::Opaque & bo) -> GoalPtr {
|
||||
return makePathSubstitutionGoal(bo.path, buildMode == bmRepair ? Repair : NoRepair);
|
||||
|
@ -265,7 +268,7 @@ void Worker::run(const Goals & _topGoals)
|
|||
for (auto & i : _topGoals) {
|
||||
topGoals.insert(i);
|
||||
if (auto goal = dynamic_cast<DerivationGoal *>(i.get())) {
|
||||
topPaths.push_back(DerivedPath::Built{goal->drvPath, goal->wantedOutputs});
|
||||
topPaths.push_back(DerivedPath::Built{makeConstantStorePathRef(goal->drvPath), goal->wantedOutputs});
|
||||
} else if (auto goal = dynamic_cast<PathSubstitutionGoal *>(i.get())) {
|
||||
topPaths.push_back(DerivedPath::Opaque{goal->storePath});
|
||||
}
|
||||
|
|
|
@ -7,52 +7,133 @@
|
|||
|
||||
namespace nix {
|
||||
|
||||
nlohmann::json DerivedPath::Opaque::toJSON(ref<Store> store) const {
|
||||
#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::string
|
||||
CMP(SingleDerivedPath, SingleDerivedPathBuilt, output)
|
||||
#undef FIELD_TYPE
|
||||
|
||||
#define FIELD_TYPE OutputsSpec
|
||||
CMP(SingleDerivedPath, DerivedPathBuilt, outputs)
|
||||
#undef FIELD_TYPE
|
||||
|
||||
#undef CMP
|
||||
#undef CMP_ONE
|
||||
|
||||
nlohmann::json DerivedPath::Opaque::toJSON(const Store & store) const
|
||||
{
|
||||
return store.printStorePath(path);
|
||||
}
|
||||
|
||||
nlohmann::json SingleDerivedPath::Built::toJSON(Store & store) const {
|
||||
nlohmann::json res;
|
||||
res["path"] = store->printStorePath(path);
|
||||
res["drvPath"] = drvPath->toJSON(store);
|
||||
// Fallback for the input-addressed derivation case: We expect to always be
|
||||
// able to print the output paths, so let’s do it
|
||||
// FIXME try-resolve on drvPath
|
||||
const auto outputMap = store.queryPartialDerivationOutputMap(resolveDerivedPath(store, *drvPath));
|
||||
res["output"] = output;
|
||||
auto outputPathIter = outputMap.find(output);
|
||||
if (outputPathIter == outputMap.end())
|
||||
res["outputPath"] = nullptr;
|
||||
else if (std::optional p = outputPathIter->second)
|
||||
res["outputPath"] = store.printStorePath(*p);
|
||||
else
|
||||
res["outputPath"] = nullptr;
|
||||
return res;
|
||||
}
|
||||
|
||||
nlohmann::json DerivedPath::Built::toJSON(ref<Store> store) const {
|
||||
nlohmann::json DerivedPath::Built::toJSON(Store & store) const {
|
||||
nlohmann::json res;
|
||||
res["drvPath"] = store->printStorePath(drvPath);
|
||||
res["drvPath"] = drvPath->toJSON(store);
|
||||
// Fallback for the input-addressed derivation case: We expect to always be
|
||||
// able to print the output paths, so let’s do it
|
||||
const auto outputMap = store->queryPartialDerivationOutputMap(drvPath);
|
||||
// FIXME try-resolve on drvPath
|
||||
const auto outputMap = store.queryPartialDerivationOutputMap(resolveDerivedPath(store, *drvPath));
|
||||
for (const auto & [output, outputPathOpt] : outputMap) {
|
||||
if (!outputs.contains(output)) continue;
|
||||
if (outputPathOpt)
|
||||
res["outputs"][output] = store->printStorePath(*outputPathOpt);
|
||||
res["outputs"][output] = store.printStorePath(*outputPathOpt);
|
||||
else
|
||||
res["outputs"][output] = nullptr;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
nlohmann::json SingleDerivedPath::toJSON(Store & store) const
|
||||
{
|
||||
return std::visit([&](const auto & buildable) {
|
||||
return buildable.toJSON(store);
|
||||
}, raw());
|
||||
}
|
||||
|
||||
nlohmann::json DerivedPath::toJSON(Store & store) const
|
||||
{
|
||||
return std::visit([&](const auto & buildable) {
|
||||
return buildable.toJSON(store);
|
||||
}, raw());
|
||||
}
|
||||
|
||||
std::string DerivedPath::Opaque::to_string(const Store & store) const
|
||||
{
|
||||
return store.printStorePath(path);
|
||||
}
|
||||
|
||||
std::string SingleDerivedPath::Built::to_string(const Store & store) const
|
||||
{
|
||||
return drvPath->to_string(store) + "^" + output;
|
||||
}
|
||||
|
||||
std::string SingleDerivedPath::Built::to_string_legacy(const Store & store) const
|
||||
{
|
||||
return drvPath->to_string(store) + "!" + output;
|
||||
}
|
||||
|
||||
std::string DerivedPath::Built::to_string(const Store & store) const
|
||||
{
|
||||
return store.printStorePath(drvPath)
|
||||
return drvPath->to_string(store)
|
||||
+ '^'
|
||||
+ outputs.to_string();
|
||||
}
|
||||
|
||||
std::string DerivedPath::Built::to_string_legacy(const Store & store) const
|
||||
{
|
||||
return store.printStorePath(drvPath)
|
||||
+ '!'
|
||||
return drvPath->to_string_legacy(store)
|
||||
+ "!"
|
||||
+ outputs.to_string();
|
||||
}
|
||||
|
||||
std::string SingleDerivedPath::to_string(const Store & store) const
|
||||
{
|
||||
return std::visit(
|
||||
[&](const auto & req) { return req.to_string(store); },
|
||||
raw());
|
||||
}
|
||||
|
||||
std::string DerivedPath::to_string(const Store & store) const
|
||||
{
|
||||
return std::visit(
|
||||
[&](const auto & req) { return req.to_string(store); },
|
||||
raw());
|
||||
}
|
||||
|
||||
std::string SingleDerivedPath::to_string_legacy(const Store & store) const
|
||||
{
|
||||
return std::visit(overloaded {
|
||||
[&](const DerivedPath::Built & req) { return req.to_string(store); },
|
||||
[&](const DerivedPath::Opaque & req) { return req.to_string(store); },
|
||||
[&](const SingleDerivedPath::Built & req) { return req.to_string_legacy(store); },
|
||||
[&](const SingleDerivedPath::Opaque & req) { return req.to_string(store); },
|
||||
}, this->raw());
|
||||
}
|
||||
|
||||
|
@ -70,30 +151,156 @@ DerivedPath::Opaque DerivedPath::Opaque::parse(const Store & store, std::string_
|
|||
return {store.parseStorePath(s)};
|
||||
}
|
||||
|
||||
DerivedPath::Built DerivedPath::Built::parse(const Store & store, std::string_view drvS, std::string_view outputsS)
|
||||
void drvRequireExperiment(
|
||||
const SingleDerivedPath & drv,
|
||||
const ExperimentalFeatureSettings & xpSettings)
|
||||
{
|
||||
std::visit(overloaded {
|
||||
[&](const SingleDerivedPath::Opaque &) {
|
||||
// plain drv path; no experimental features required.
|
||||
},
|
||||
[&](const SingleDerivedPath::Built &) {
|
||||
xpSettings.require(Xp::DynamicDerivations);
|
||||
},
|
||||
}, drv.raw());
|
||||
}
|
||||
|
||||
SingleDerivedPath::Built SingleDerivedPath::Built::parse(
|
||||
const Store & store, ref<SingleDerivedPath> drv,
|
||||
std::string_view output,
|
||||
const ExperimentalFeatureSettings & xpSettings)
|
||||
{
|
||||
drvRequireExperiment(*drv, xpSettings);
|
||||
return {
|
||||
.drvPath = store.parseStorePath(drvS),
|
||||
.drvPath = drv,
|
||||
.output = std::string { output },
|
||||
};
|
||||
}
|
||||
|
||||
DerivedPath::Built DerivedPath::Built::parse(
|
||||
const Store & store, ref<SingleDerivedPath> drv,
|
||||
std::string_view outputsS,
|
||||
const ExperimentalFeatureSettings & xpSettings)
|
||||
{
|
||||
drvRequireExperiment(*drv, xpSettings);
|
||||
return {
|
||||
.drvPath = drv,
|
||||
.outputs = OutputsSpec::parse(outputsS),
|
||||
};
|
||||
}
|
||||
|
||||
static inline DerivedPath parseWith(const Store & store, std::string_view s, std::string_view separator)
|
||||
static SingleDerivedPath parseWithSingle(
|
||||
const Store & store, std::string_view s, std::string_view separator,
|
||||
const ExperimentalFeatureSettings & xpSettings)
|
||||
{
|
||||
size_t n = s.find(separator);
|
||||
size_t n = s.rfind(separator);
|
||||
return n == s.npos
|
||||
? (SingleDerivedPath) SingleDerivedPath::Opaque::parse(store, s)
|
||||
: (SingleDerivedPath) SingleDerivedPath::Built::parse(store,
|
||||
make_ref<SingleDerivedPath>(parseWithSingle(
|
||||
store,
|
||||
s.substr(0, n),
|
||||
separator,
|
||||
xpSettings)),
|
||||
s.substr(n + 1),
|
||||
xpSettings);
|
||||
}
|
||||
|
||||
SingleDerivedPath SingleDerivedPath::parse(
|
||||
const Store & store,
|
||||
std::string_view s,
|
||||
const ExperimentalFeatureSettings & xpSettings)
|
||||
{
|
||||
return parseWithSingle(store, s, "^", xpSettings);
|
||||
}
|
||||
|
||||
SingleDerivedPath SingleDerivedPath::parseLegacy(
|
||||
const Store & store,
|
||||
std::string_view s,
|
||||
const ExperimentalFeatureSettings & xpSettings)
|
||||
{
|
||||
return parseWithSingle(store, s, "!", xpSettings);
|
||||
}
|
||||
|
||||
static DerivedPath parseWith(
|
||||
const Store & store, std::string_view s, std::string_view separator,
|
||||
const ExperimentalFeatureSettings & xpSettings)
|
||||
{
|
||||
size_t n = s.rfind(separator);
|
||||
return n == s.npos
|
||||
? (DerivedPath) DerivedPath::Opaque::parse(store, s)
|
||||
: (DerivedPath) DerivedPath::Built::parse(store, s.substr(0, n), s.substr(n + 1));
|
||||
: (DerivedPath) DerivedPath::Built::parse(store,
|
||||
make_ref<SingleDerivedPath>(parseWithSingle(
|
||||
store,
|
||||
s.substr(0, n),
|
||||
separator,
|
||||
xpSettings)),
|
||||
s.substr(n + 1),
|
||||
xpSettings);
|
||||
}
|
||||
|
||||
DerivedPath DerivedPath::parse(const Store & store, std::string_view s)
|
||||
DerivedPath DerivedPath::parse(
|
||||
const Store & store,
|
||||
std::string_view s,
|
||||
const ExperimentalFeatureSettings & xpSettings)
|
||||
{
|
||||
return parseWith(store, s, "^");
|
||||
return parseWith(store, s, "^", xpSettings);
|
||||
}
|
||||
|
||||
DerivedPath DerivedPath::parseLegacy(const Store & store, std::string_view s)
|
||||
DerivedPath DerivedPath::parseLegacy(
|
||||
const Store & store,
|
||||
std::string_view s,
|
||||
const ExperimentalFeatureSettings & xpSettings)
|
||||
{
|
||||
return parseWith(store, s, "!");
|
||||
return parseWith(store, s, "!", xpSettings);
|
||||
}
|
||||
|
||||
DerivedPath DerivedPath::fromSingle(const SingleDerivedPath & req)
|
||||
{
|
||||
return std::visit(overloaded {
|
||||
[&](const SingleDerivedPath::Opaque & o) -> DerivedPath {
|
||||
return o;
|
||||
},
|
||||
[&](const SingleDerivedPath::Built & b) -> DerivedPath {
|
||||
return DerivedPath::Built {
|
||||
.drvPath = b.drvPath,
|
||||
.outputs = OutputsSpec::Names { b.output },
|
||||
};
|
||||
},
|
||||
}, req.raw());
|
||||
}
|
||||
|
||||
const StorePath & SingleDerivedPath::Built::getBaseStorePath() const
|
||||
{
|
||||
return drvPath->getBaseStorePath();
|
||||
}
|
||||
|
||||
const StorePath & DerivedPath::Built::getBaseStorePath() const
|
||||
{
|
||||
return drvPath->getBaseStorePath();
|
||||
}
|
||||
|
||||
template<typename DP>
|
||||
static inline const StorePath & getBaseStorePath_(const DP & derivedPath)
|
||||
{
|
||||
return std::visit(overloaded {
|
||||
[&](const typename DP::Built & bfd) -> auto & {
|
||||
return bfd.drvPath->getBaseStorePath();
|
||||
},
|
||||
[&](const typename DP::Opaque & bo) -> auto & {
|
||||
return bo.path;
|
||||
},
|
||||
}, derivedPath.raw());
|
||||
}
|
||||
|
||||
const StorePath & SingleDerivedPath::getBaseStorePath() const
|
||||
{
|
||||
return getBaseStorePath_(*this);
|
||||
}
|
||||
|
||||
const StorePath & DerivedPath::getBaseStorePath() const
|
||||
{
|
||||
return getBaseStorePath_(*this);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -24,28 +24,37 @@ class Store;
|
|||
struct DerivedPathOpaque {
|
||||
StorePath path;
|
||||
|
||||
nlohmann::json toJSON(ref<Store> store) const;
|
||||
std::string to_string(const Store & store) const;
|
||||
static DerivedPathOpaque parse(const Store & store, std::string_view);
|
||||
nlohmann::json toJSON(const Store & store) const;
|
||||
|
||||
GENERATE_CMP(DerivedPathOpaque, me->path);
|
||||
};
|
||||
|
||||
struct SingleDerivedPath;
|
||||
|
||||
/**
|
||||
* A derived path that is built from a derivation
|
||||
* A single derived path that is built from a derivation
|
||||
*
|
||||
* Built derived paths are pair of a derivation and some output names.
|
||||
* They are evaluated by building the derivation, and then replacing the
|
||||
* output names with the resulting outputs.
|
||||
*
|
||||
* Note that does mean a derived store paths evaluates to multiple
|
||||
* opaque paths, which is sort of icky as expressions are supposed to
|
||||
* evaluate to single values. Perhaps this should have just a single
|
||||
* output name.
|
||||
* Built derived paths are pair of a derivation and an output name. They are
|
||||
* evaluated by building the derivation, and then taking the resulting output
|
||||
* path of the given output name.
|
||||
*/
|
||||
struct DerivedPathBuilt {
|
||||
StorePath drvPath;
|
||||
OutputsSpec outputs;
|
||||
struct SingleDerivedPathBuilt {
|
||||
ref<SingleDerivedPath> drvPath;
|
||||
std::string output;
|
||||
|
||||
/**
|
||||
* Get the store path this is ultimately derived from (by realising
|
||||
* and projecting outputs).
|
||||
*
|
||||
* Note that this is *not* a property of the store object being
|
||||
* referred to, but just of this path --- how we happened to be
|
||||
* referring to that store object. In other words, this means this
|
||||
* function breaks "referential transparency". It should therefore
|
||||
* be used only with great care.
|
||||
*/
|
||||
const StorePath & getBaseStorePath() const;
|
||||
|
||||
/**
|
||||
* Uses `^` as the separator
|
||||
|
@ -57,11 +66,139 @@ struct DerivedPathBuilt {
|
|||
std::string to_string_legacy(const Store & store) const;
|
||||
/**
|
||||
* The caller splits on the separator, so it works for both variants.
|
||||
*
|
||||
* @param xpSettings Stop-gap to avoid globals during unit tests.
|
||||
*/
|
||||
static DerivedPathBuilt parse(const Store & store, std::string_view drvPath, std::string_view outputs);
|
||||
nlohmann::json toJSON(ref<Store> store) const;
|
||||
static SingleDerivedPathBuilt parse(
|
||||
const Store & store, ref<SingleDerivedPath> drvPath,
|
||||
std::string_view outputs,
|
||||
const ExperimentalFeatureSettings & xpSettings = experimentalFeatureSettings);
|
||||
nlohmann::json toJSON(Store & store) const;
|
||||
|
||||
GENERATE_CMP(DerivedPathBuilt, me->drvPath, me->outputs);
|
||||
DECLARE_CMP(SingleDerivedPathBuilt);
|
||||
};
|
||||
|
||||
using _SingleDerivedPathRaw = std::variant<
|
||||
DerivedPathOpaque,
|
||||
SingleDerivedPathBuilt
|
||||
>;
|
||||
|
||||
/**
|
||||
* A "derived path" is a very simple sort of expression (not a Nix
|
||||
* language expression! But an expression in a the general sense) that
|
||||
* evaluates to (concrete) store path. It is either:
|
||||
*
|
||||
* - opaque, in which case it is just a concrete store path with
|
||||
* possibly no known derivation
|
||||
*
|
||||
* - built, in which case it is a pair of a derivation path and an
|
||||
* output name.
|
||||
*/
|
||||
struct SingleDerivedPath : _SingleDerivedPathRaw {
|
||||
using Raw = _SingleDerivedPathRaw;
|
||||
using Raw::Raw;
|
||||
|
||||
using Opaque = DerivedPathOpaque;
|
||||
using Built = SingleDerivedPathBuilt;
|
||||
|
||||
inline const Raw & raw() const {
|
||||
return static_cast<const Raw &>(*this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the store path this is ultimately derived from (by realising
|
||||
* and projecting outputs).
|
||||
*
|
||||
* Note that this is *not* a property of the store object being
|
||||
* referred to, but just of this path --- how we happened to be
|
||||
* referring to that store object. In other words, this means this
|
||||
* function breaks "referential transparency". It should therefore
|
||||
* be used only with great care.
|
||||
*/
|
||||
const StorePath & getBaseStorePath() const;
|
||||
|
||||
/**
|
||||
* Uses `^` as the separator
|
||||
*/
|
||||
std::string to_string(const Store & store) const;
|
||||
/**
|
||||
* Uses `!` as the separator
|
||||
*/
|
||||
std::string to_string_legacy(const Store & store) const;
|
||||
/**
|
||||
* Uses `^` as the separator
|
||||
*
|
||||
* @param xpSettings Stop-gap to avoid globals during unit tests.
|
||||
*/
|
||||
static SingleDerivedPath parse(
|
||||
const Store & store,
|
||||
std::string_view,
|
||||
const ExperimentalFeatureSettings & xpSettings = experimentalFeatureSettings);
|
||||
/**
|
||||
* Uses `!` as the separator
|
||||
*
|
||||
* @param xpSettings Stop-gap to avoid globals during unit tests.
|
||||
*/
|
||||
static SingleDerivedPath parseLegacy(
|
||||
const Store & store,
|
||||
std::string_view,
|
||||
const ExperimentalFeatureSettings & xpSettings = experimentalFeatureSettings);
|
||||
nlohmann::json toJSON(Store & store) const;
|
||||
};
|
||||
|
||||
static inline ref<SingleDerivedPath> makeConstantStorePathRef(StorePath drvPath)
|
||||
{
|
||||
return make_ref<SingleDerivedPath>(SingleDerivedPath::Opaque { drvPath });
|
||||
}
|
||||
|
||||
/**
|
||||
* A set of derived paths that are built from a derivation
|
||||
*
|
||||
* Built derived paths are pair of a derivation and some output names.
|
||||
* They are evaluated by building the derivation, and then replacing the
|
||||
* output names with the resulting outputs.
|
||||
*
|
||||
* Note that does mean a derived store paths evaluates to multiple
|
||||
* opaque paths, which is sort of icky as expressions are supposed to
|
||||
* evaluate to single values. Perhaps this should have just a single
|
||||
* output name.
|
||||
*/
|
||||
struct DerivedPathBuilt {
|
||||
ref<SingleDerivedPath> drvPath;
|
||||
OutputsSpec outputs;
|
||||
|
||||
/**
|
||||
* Get the store path this is ultimately derived from (by realising
|
||||
* and projecting outputs).
|
||||
*
|
||||
* Note that this is *not* a property of the store object being
|
||||
* referred to, but just of this path --- how we happened to be
|
||||
* referring to that store object. In other words, this means this
|
||||
* function breaks "referential transparency". It should therefore
|
||||
* be used only with great care.
|
||||
*/
|
||||
const StorePath & getBaseStorePath() const;
|
||||
|
||||
/**
|
||||
* Uses `^` as the separator
|
||||
*/
|
||||
std::string to_string(const Store & store) const;
|
||||
/**
|
||||
* Uses `!` as the separator
|
||||
*/
|
||||
std::string to_string_legacy(const Store & store) const;
|
||||
/**
|
||||
* The caller splits on the separator, so it works for both variants.
|
||||
*
|
||||
* @param xpSettings Stop-gap to avoid globals during unit tests.
|
||||
*/
|
||||
static DerivedPathBuilt parse(
|
||||
const Store & store, ref<SingleDerivedPath>,
|
||||
std::string_view,
|
||||
const ExperimentalFeatureSettings & xpSettings = experimentalFeatureSettings);
|
||||
nlohmann::json toJSON(Store & store) const;
|
||||
|
||||
DECLARE_CMP(DerivedPathBuilt);
|
||||
};
|
||||
|
||||
using _DerivedPathRaw = std::variant<
|
||||
|
@ -71,13 +208,13 @@ using _DerivedPathRaw = std::variant<
|
|||
|
||||
/**
|
||||
* A "derived path" is a very simple sort of expression that evaluates
|
||||
* to (concrete) store path. It is either:
|
||||
* to one or more (concrete) store paths. It is either:
|
||||
*
|
||||
* - opaque, in which case it is just a concrete store path with
|
||||
* - opaque, in which case it is just a single concrete store path with
|
||||
* possibly no known derivation
|
||||
*
|
||||
* - built, in which case it is a pair of a derivation path and an
|
||||
* output name.
|
||||
* - built, in which case it is a pair of a derivation path and some
|
||||
* output names.
|
||||
*/
|
||||
struct DerivedPath : _DerivedPathRaw {
|
||||
using Raw = _DerivedPathRaw;
|
||||
|
@ -90,6 +227,18 @@ struct DerivedPath : _DerivedPathRaw {
|
|||
return static_cast<const Raw &>(*this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the store path this is ultimately derived from (by realising
|
||||
* and projecting outputs).
|
||||
*
|
||||
* Note that this is *not* a property of the store object being
|
||||
* referred to, but just of this path --- how we happened to be
|
||||
* referring to that store object. In other words, this means this
|
||||
* function breaks "referential transparency". It should therefore
|
||||
* be used only with great care.
|
||||
*/
|
||||
const StorePath & getBaseStorePath() const;
|
||||
|
||||
/**
|
||||
* Uses `^` as the separator
|
||||
*/
|
||||
|
@ -100,14 +249,43 @@ struct DerivedPath : _DerivedPathRaw {
|
|||
std::string to_string_legacy(const Store & store) const;
|
||||
/**
|
||||
* Uses `^` as the separator
|
||||
*
|
||||
* @param xpSettings Stop-gap to avoid globals during unit tests.
|
||||
*/
|
||||
static DerivedPath parse(const Store & store, std::string_view);
|
||||
static DerivedPath parse(
|
||||
const Store & store,
|
||||
std::string_view,
|
||||
const ExperimentalFeatureSettings & xpSettings = experimentalFeatureSettings);
|
||||
/**
|
||||
* Uses `!` as the separator
|
||||
*
|
||||
* @param xpSettings Stop-gap to avoid globals during unit tests.
|
||||
*/
|
||||
static DerivedPath parseLegacy(const Store & store, std::string_view);
|
||||
static DerivedPath parseLegacy(
|
||||
const Store & store,
|
||||
std::string_view,
|
||||
const ExperimentalFeatureSettings & xpSettings = experimentalFeatureSettings);
|
||||
|
||||
/**
|
||||
* Convert a `SingleDerivedPath` to a `DerivedPath`.
|
||||
*/
|
||||
static DerivedPath fromSingle(const SingleDerivedPath &);
|
||||
|
||||
nlohmann::json toJSON(Store & store) const;
|
||||
};
|
||||
|
||||
typedef std::vector<DerivedPath> DerivedPaths;
|
||||
|
||||
/**
|
||||
* Used by various parser functions to require experimental features as
|
||||
* needed.
|
||||
*
|
||||
* Somewhat unfortunate this cannot just be an implementation detail for
|
||||
* this module.
|
||||
*
|
||||
* @param xpSettings Stop-gap to avoid globals during unit tests.
|
||||
*/
|
||||
void drvRequireExperiment(
|
||||
const SingleDerivedPath & drv,
|
||||
const ExperimentalFeatureSettings & xpSettings = experimentalFeatureSettings);
|
||||
}
|
||||
|
|
|
@ -38,4 +38,19 @@ DownstreamPlaceholder DownstreamPlaceholder::unknownDerivation(
|
|||
};
|
||||
}
|
||||
|
||||
DownstreamPlaceholder DownstreamPlaceholder::fromSingleDerivedPathBuilt(
|
||||
const SingleDerivedPath::Built & b)
|
||||
{
|
||||
return std::visit(overloaded {
|
||||
[&](const SingleDerivedPath::Opaque & o) {
|
||||
return DownstreamPlaceholder::unknownCaOutput(o.path, b.output);
|
||||
},
|
||||
[&](const SingleDerivedPath::Built & b2) {
|
||||
return DownstreamPlaceholder::unknownDerivation(
|
||||
DownstreamPlaceholder::fromSingleDerivedPathBuilt(b2),
|
||||
b.output);
|
||||
},
|
||||
}, b.drvPath->raw());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
#include "hash.hh"
|
||||
#include "path.hh"
|
||||
#include "derived-path.hh"
|
||||
|
||||
namespace nix {
|
||||
|
||||
|
@ -73,6 +74,17 @@ public:
|
|||
const DownstreamPlaceholder & drvPlaceholder,
|
||||
std::string_view outputName,
|
||||
const ExperimentalFeatureSettings & xpSettings = experimentalFeatureSettings);
|
||||
|
||||
/**
|
||||
* Convenience constructor that handles both cases (unknown
|
||||
* content-addressed output and unknown derivation), delegating as
|
||||
* needed to `unknownCaOutput` and `unknownDerivation`.
|
||||
*
|
||||
* Recursively builds up a placeholder from a
|
||||
* `SingleDerivedPath::Built.drvPath` chain.
|
||||
*/
|
||||
static DownstreamPlaceholder fromSingleDerivedPathBuilt(
|
||||
const SingleDerivedPath::Built & built);
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -358,6 +358,9 @@ public:
|
|||
[&](const StorePath & drvPath) {
|
||||
throw Error("wanted to fetch '%s' but the legacy ssh protocol doesn't support merely substituting drv files via the build paths command. It would build them instead. Try using ssh-ng://", printStorePath(drvPath));
|
||||
},
|
||||
[&](std::monostate) {
|
||||
throw Error("wanted build derivation that is itself a build product, but the legacy ssh protocol doesn't support that. Try using ssh-ng://");
|
||||
},
|
||||
}, sOrDrvPath);
|
||||
}
|
||||
conn->to << ss;
|
||||
|
|
|
@ -132,7 +132,7 @@ void Store::queryMissing(const std::vector<DerivedPath> & targets,
|
|||
}
|
||||
|
||||
for (auto & i : drv.inputDrvs)
|
||||
pool.enqueue(std::bind(doPath, DerivedPath::Built { i.first, i.second }));
|
||||
pool.enqueue(std::bind(doPath, DerivedPath::Built { makeConstantStorePathRef(i.first), i.second }));
|
||||
};
|
||||
|
||||
auto checkOutput = [&](
|
||||
|
@ -176,10 +176,18 @@ void Store::queryMissing(const std::vector<DerivedPath> & targets,
|
|||
|
||||
std::visit(overloaded {
|
||||
[&](const DerivedPath::Built & bfd) {
|
||||
if (!isValidPath(bfd.drvPath)) {
|
||||
auto drvPathP = std::get_if<DerivedPath::Opaque>(&*bfd.drvPath);
|
||||
if (!drvPathP) {
|
||||
// TODO make work in this case.
|
||||
warn("Ignoring dynamic derivation %s while querying missing paths; not yet implemented", bfd.drvPath->to_string(*this));
|
||||
return;
|
||||
}
|
||||
auto & drvPath = drvPathP->path;
|
||||
|
||||
if (!isValidPath(drvPath)) {
|
||||
// FIXME: we could try to substitute the derivation.
|
||||
auto state(state_.lock());
|
||||
state->unknown.insert(bfd.drvPath);
|
||||
state->unknown.insert(drvPath);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -187,7 +195,7 @@ void Store::queryMissing(const std::vector<DerivedPath> & targets,
|
|||
/* true for regular derivations, and CA derivations for which we
|
||||
have a trust mapping for all wanted outputs. */
|
||||
auto knownOutputPaths = true;
|
||||
for (auto & [outputName, pathOpt] : queryPartialDerivationOutputMap(bfd.drvPath)) {
|
||||
for (auto & [outputName, pathOpt] : queryPartialDerivationOutputMap(drvPath)) {
|
||||
if (!pathOpt) {
|
||||
knownOutputPaths = false;
|
||||
break;
|
||||
|
@ -197,8 +205,8 @@ void Store::queryMissing(const std::vector<DerivedPath> & targets,
|
|||
}
|
||||
if (knownOutputPaths && invalid.empty()) return;
|
||||
|
||||
auto drv = make_ref<Derivation>(derivationFromPath(bfd.drvPath));
|
||||
ParsedDerivation parsedDrv(StorePath(bfd.drvPath), *drv);
|
||||
auto drv = make_ref<Derivation>(derivationFromPath(drvPath));
|
||||
ParsedDerivation parsedDrv(StorePath(drvPath), *drv);
|
||||
|
||||
if (!knownOutputPaths && settings.useSubstitutes && parsedDrv.substitutesAllowed()) {
|
||||
experimentalFeatureSettings.require(Xp::CaDerivations);
|
||||
|
@ -233,9 +241,9 @@ void Store::queryMissing(const std::vector<DerivedPath> & targets,
|
|||
if (knownOutputPaths && settings.useSubstitutes && parsedDrv.substitutesAllowed()) {
|
||||
auto drvState = make_ref<Sync<DrvState>>(DrvState(invalid.size()));
|
||||
for (auto & output : invalid)
|
||||
pool.enqueue(std::bind(checkOutput, bfd.drvPath, drv, output, drvState));
|
||||
pool.enqueue(std::bind(checkOutput, drvPath, drv, output, drvState));
|
||||
} else
|
||||
mustBuildDrv(bfd.drvPath, *drv);
|
||||
mustBuildDrv(drvPath, *drv);
|
||||
|
||||
},
|
||||
[&](const DerivedPath::Opaque & bo) {
|
||||
|
@ -340,7 +348,9 @@ std::map<DrvOutput, StorePath> drvOutputReferences(
|
|||
|
||||
OutputPathMap resolveDerivedPath(Store & store, const DerivedPath::Built & bfd, Store * evalStore_)
|
||||
{
|
||||
auto outputsOpt_ = store.queryPartialDerivationOutputMap(bfd.drvPath, evalStore_);
|
||||
auto drvPath = resolveDerivedPath(store, *bfd.drvPath, evalStore_);
|
||||
|
||||
auto outputsOpt_ = store.queryPartialDerivationOutputMap(drvPath, evalStore_);
|
||||
|
||||
auto outputsOpt = std::visit(overloaded {
|
||||
[&](const OutputsSpec::All &) {
|
||||
|
@ -355,7 +365,7 @@ OutputPathMap resolveDerivedPath(Store & store, const DerivedPath::Built & bfd,
|
|||
if (!pOutputPathOpt)
|
||||
throw Error(
|
||||
"the derivation '%s' doesn't have an output named '%s'",
|
||||
store.printStorePath(bfd.drvPath), output);
|
||||
bfd.drvPath->to_string(store), output);
|
||||
outputsOpt.insert_or_assign(output, std::move(*pOutputPathOpt));
|
||||
}
|
||||
return outputsOpt;
|
||||
|
@ -365,11 +375,64 @@ OutputPathMap resolveDerivedPath(Store & store, const DerivedPath::Built & bfd,
|
|||
OutputPathMap outputs;
|
||||
for (auto & [outputName, outputPathOpt] : outputsOpt) {
|
||||
if (!outputPathOpt)
|
||||
throw MissingRealisation(store.printStorePath(bfd.drvPath), outputName);
|
||||
throw MissingRealisation(bfd.drvPath->to_string(store), outputName);
|
||||
auto & outputPath = *outputPathOpt;
|
||||
outputs.insert_or_assign(outputName, outputPath);
|
||||
}
|
||||
return outputs;
|
||||
}
|
||||
|
||||
|
||||
StorePath resolveDerivedPath(Store & store, const SingleDerivedPath & req, Store * evalStore_)
|
||||
{
|
||||
auto & evalStore = evalStore_ ? *evalStore_ : store;
|
||||
|
||||
return std::visit(overloaded {
|
||||
[&](const SingleDerivedPath::Opaque & bo) {
|
||||
return bo.path;
|
||||
},
|
||||
[&](const SingleDerivedPath::Built & bfd) {
|
||||
auto drvPath = resolveDerivedPath(store, *bfd.drvPath, evalStore_);
|
||||
auto outputPaths = evalStore.queryPartialDerivationOutputMap(drvPath, evalStore_);
|
||||
if (outputPaths.count(bfd.output) == 0)
|
||||
throw Error("derivation '%s' does not have an output named '%s'",
|
||||
store.printStorePath(drvPath), bfd.output);
|
||||
auto & optPath = outputPaths.at(bfd.output);
|
||||
if (!optPath)
|
||||
throw Error("'%s' does not yet map to a known concrete store path",
|
||||
bfd.to_string(store));
|
||||
return *optPath;
|
||||
},
|
||||
}, req.raw());
|
||||
}
|
||||
|
||||
|
||||
OutputPathMap resolveDerivedPath(Store & store, const DerivedPath::Built & bfd)
|
||||
{
|
||||
auto drvPath = resolveDerivedPath(store, *bfd.drvPath);
|
||||
auto outputMap = store.queryDerivationOutputMap(drvPath);
|
||||
auto outputsLeft = std::visit(overloaded {
|
||||
[&](const OutputsSpec::All &) {
|
||||
return StringSet {};
|
||||
},
|
||||
[&](const OutputsSpec::Names & names) {
|
||||
return static_cast<StringSet>(names);
|
||||
},
|
||||
}, bfd.outputs.raw());
|
||||
for (auto iter = outputMap.begin(); iter != outputMap.end();) {
|
||||
auto & outputName = iter->first;
|
||||
if (bfd.outputs.contains(outputName)) {
|
||||
outputsLeft.erase(outputName);
|
||||
++iter;
|
||||
} else {
|
||||
iter = outputMap.erase(iter);
|
||||
}
|
||||
}
|
||||
if (!outputsLeft.empty())
|
||||
throw Error("derivation '%s' does not have an outputs %s",
|
||||
store.printStorePath(drvPath),
|
||||
concatStringsSep(", ", quoteStrings(std::get<OutputsSpec::Names>(bfd.outputs))));
|
||||
return outputMap;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -16,10 +16,16 @@ std::string StorePathWithOutputs::to_string(const Store & store) const
|
|||
DerivedPath StorePathWithOutputs::toDerivedPath() const
|
||||
{
|
||||
if (!outputs.empty()) {
|
||||
return DerivedPath::Built { path, OutputsSpec::Names { outputs } };
|
||||
return DerivedPath::Built {
|
||||
.drvPath = makeConstantStorePathRef(path),
|
||||
.outputs = OutputsSpec::Names { outputs },
|
||||
};
|
||||
} else if (path.isDerivation()) {
|
||||
assert(outputs.empty());
|
||||
return DerivedPath::Built { path, OutputsSpec::All { } };
|
||||
return DerivedPath::Built {
|
||||
.drvPath = makeConstantStorePathRef(path),
|
||||
.outputs = OutputsSpec::All { },
|
||||
};
|
||||
} else {
|
||||
return DerivedPath::Opaque { path };
|
||||
}
|
||||
|
@ -34,29 +40,36 @@ std::vector<DerivedPath> toDerivedPaths(const std::vector<StorePathWithOutputs>
|
|||
}
|
||||
|
||||
|
||||
std::variant<StorePathWithOutputs, StorePath> StorePathWithOutputs::tryFromDerivedPath(const DerivedPath & p)
|
||||
StorePathWithOutputs::ParseResult StorePathWithOutputs::tryFromDerivedPath(const DerivedPath & p)
|
||||
{
|
||||
return std::visit(overloaded {
|
||||
[&](const DerivedPath::Opaque & bo) -> std::variant<StorePathWithOutputs, StorePath> {
|
||||
[&](const DerivedPath::Opaque & bo) -> StorePathWithOutputs::ParseResult {
|
||||
if (bo.path.isDerivation()) {
|
||||
// drv path gets interpreted as "build", not "get drv file itself"
|
||||
return bo.path;
|
||||
}
|
||||
return StorePathWithOutputs { bo.path };
|
||||
},
|
||||
[&](const DerivedPath::Built & bfd) -> std::variant<StorePathWithOutputs, StorePath> {
|
||||
return StorePathWithOutputs {
|
||||
.path = bfd.drvPath,
|
||||
// Use legacy encoding of wildcard as empty set
|
||||
.outputs = std::visit(overloaded {
|
||||
[&](const OutputsSpec::All &) -> StringSet {
|
||||
return {};
|
||||
},
|
||||
[&](const OutputsSpec::Names & outputs) {
|
||||
return static_cast<StringSet>(outputs);
|
||||
},
|
||||
}, bfd.outputs.raw()),
|
||||
};
|
||||
[&](const DerivedPath::Built & bfd) -> StorePathWithOutputs::ParseResult {
|
||||
return std::visit(overloaded {
|
||||
[&](const SingleDerivedPath::Opaque & bo) -> StorePathWithOutputs::ParseResult {
|
||||
return StorePathWithOutputs {
|
||||
.path = bo.path,
|
||||
// Use legacy encoding of wildcard as empty set
|
||||
.outputs = std::visit(overloaded {
|
||||
[&](const OutputsSpec::All &) -> StringSet {
|
||||
return {};
|
||||
},
|
||||
[&](const OutputsSpec::Names & outputs) {
|
||||
return static_cast<StringSet>(outputs);
|
||||
},
|
||||
}, bfd.outputs.raw()),
|
||||
};
|
||||
},
|
||||
[&](const SingleDerivedPath::Built &) -> StorePathWithOutputs::ParseResult {
|
||||
return std::monostate {};
|
||||
},
|
||||
}, bfd.drvPath->raw());
|
||||
},
|
||||
}, p.raw());
|
||||
}
|
||||
|
|
|
@ -23,7 +23,9 @@ struct StorePathWithOutputs
|
|||
|
||||
DerivedPath toDerivedPath() const;
|
||||
|
||||
static std::variant<StorePathWithOutputs, StorePath> tryFromDerivedPath(const DerivedPath &);
|
||||
typedef std::variant<StorePathWithOutputs, StorePath, std::monostate> ParseResult;
|
||||
|
||||
static StorePathWithOutputs::ParseResult tryFromDerivedPath(const DerivedPath &);
|
||||
};
|
||||
|
||||
std::vector<DerivedPath> toDerivedPaths(const std::vector<StorePathWithOutputs>);
|
||||
|
|
|
@ -656,6 +656,9 @@ static void writeDerivedPaths(RemoteStore & store, RemoteStore::Connection & con
|
|||
GET_PROTOCOL_MAJOR(conn.daemonVersion),
|
||||
GET_PROTOCOL_MINOR(conn.daemonVersion));
|
||||
},
|
||||
[&](std::monostate) {
|
||||
throw Error("wanted to build a derivation that is itself a build product, but the legacy 'ssh://' protocol doesn't support that. Try using 'ssh-ng://'");
|
||||
},
|
||||
}, sOrDrvPath);
|
||||
}
|
||||
conn.to << ss;
|
||||
|
@ -670,9 +673,16 @@ void RemoteStore::copyDrvsFromEvalStore(
|
|||
/* The remote doesn't have a way to access evalStore, so copy
|
||||
the .drvs. */
|
||||
RealisedPath::Set drvPaths2;
|
||||
for (auto & i : paths)
|
||||
if (auto p = std::get_if<DerivedPath::Built>(&i))
|
||||
drvPaths2.insert(p->drvPath);
|
||||
for (const auto & i : paths) {
|
||||
std::visit(overloaded {
|
||||
[&](const DerivedPath::Opaque & bp) {
|
||||
// Do nothing, path is hopefully there already
|
||||
},
|
||||
[&](const DerivedPath::Built & bp) {
|
||||
drvPaths2.insert(bp.drvPath->getBaseStorePath());
|
||||
},
|
||||
}, i.raw());
|
||||
}
|
||||
copyClosure(*evalStore, *this, drvPaths2);
|
||||
}
|
||||
}
|
||||
|
@ -742,7 +752,8 @@ std::vector<KeyedBuildResult> RemoteStore::buildPathsWithResults(
|
|||
};
|
||||
|
||||
OutputPathMap outputs;
|
||||
auto drv = evalStore->readDerivation(bfd.drvPath);
|
||||
auto drvPath = resolveDerivedPath(*evalStore, *bfd.drvPath);
|
||||
auto drv = evalStore->readDerivation(drvPath);
|
||||
const auto outputHashes = staticOutputHashes(*evalStore, drv); // FIXME: expensive
|
||||
auto built = resolveDerivedPath(*this, bfd, &*evalStore);
|
||||
for (auto & [output, outputPath] : built) {
|
||||
|
@ -750,7 +761,7 @@ std::vector<KeyedBuildResult> RemoteStore::buildPathsWithResults(
|
|||
if (!outputHash)
|
||||
throw Error(
|
||||
"the derivation '%s' doesn't have an output named '%s'",
|
||||
printStorePath(bfd.drvPath), output);
|
||||
printStorePath(drvPath), output);
|
||||
auto outputId = DrvOutput{ *outputHash, output };
|
||||
if (experimentalFeatureSettings.isEnabled(Xp::CaDerivations)) {
|
||||
auto realisation =
|
||||
|
|
|
@ -945,6 +945,7 @@ void removeTempRoots();
|
|||
* Resolve the derived path completely, failing if any derivation output
|
||||
* is unknown.
|
||||
*/
|
||||
StorePath resolveDerivedPath(Store &, const SingleDerivedPath &, Store * evalStore = nullptr);
|
||||
OutputPathMap resolveDerivedPath(Store &, const DerivedPath::Built &, Store * evalStore = nullptr);
|
||||
|
||||
|
||||
|
|
|
@ -17,14 +17,34 @@ Gen<DerivedPath::Opaque> Arbitrary<DerivedPath::Opaque>::arbitrary()
|
|||
});
|
||||
}
|
||||
|
||||
Gen<SingleDerivedPath::Built> Arbitrary<SingleDerivedPath::Built>::arbitrary()
|
||||
{
|
||||
return gen::just(SingleDerivedPath::Built {
|
||||
.drvPath = make_ref<SingleDerivedPath>(*gen::arbitrary<SingleDerivedPath>()),
|
||||
.output = (*gen::arbitrary<StorePathName>()).name,
|
||||
});
|
||||
}
|
||||
|
||||
Gen<DerivedPath::Built> Arbitrary<DerivedPath::Built>::arbitrary()
|
||||
{
|
||||
return gen::just(DerivedPath::Built {
|
||||
.drvPath = *gen::arbitrary<StorePath>(),
|
||||
.drvPath = make_ref<SingleDerivedPath>(*gen::arbitrary<SingleDerivedPath>()),
|
||||
.outputs = *gen::arbitrary<OutputsSpec>(),
|
||||
});
|
||||
}
|
||||
|
||||
Gen<SingleDerivedPath> Arbitrary<SingleDerivedPath>::arbitrary()
|
||||
{
|
||||
switch (*gen::inRange<uint8_t>(0, std::variant_size_v<SingleDerivedPath::Raw>)) {
|
||||
case 0:
|
||||
return gen::just<SingleDerivedPath>(*gen::arbitrary<SingleDerivedPath::Opaque>());
|
||||
case 1:
|
||||
return gen::just<SingleDerivedPath>(*gen::arbitrary<SingleDerivedPath::Built>());
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
|
||||
Gen<DerivedPath> Arbitrary<DerivedPath>::arbitrary()
|
||||
{
|
||||
switch (*gen::inRange<uint8_t>(0, std::variant_size_v<DerivedPath::Raw>)) {
|
||||
|
@ -45,12 +65,69 @@ class DerivedPathTest : public LibStoreTest
|
|||
{
|
||||
};
|
||||
|
||||
// FIXME: `RC_GTEST_FIXTURE_PROP` isn't calling `SetUpTestSuite` because it is
|
||||
// no a real fixture.
|
||||
//
|
||||
// See https://github.com/emil-e/rapidcheck/blob/master/doc/gtest.md#rc_gtest_fixture_propfixture-name-args
|
||||
TEST_F(DerivedPathTest, force_init)
|
||||
{
|
||||
/**
|
||||
* Round trip (string <-> data structure) test for
|
||||
* `DerivedPath::Opaque`.
|
||||
*/
|
||||
TEST_F(DerivedPathTest, opaque) {
|
||||
std::string_view opaque = "/nix/store/g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-x";
|
||||
auto elem = DerivedPath::parse(*store, opaque);
|
||||
auto * p = std::get_if<DerivedPath::Opaque>(&elem);
|
||||
ASSERT_TRUE(p);
|
||||
ASSERT_EQ(p->path, store->parseStorePath(opaque));
|
||||
ASSERT_EQ(elem.to_string(*store), opaque);
|
||||
}
|
||||
|
||||
/**
|
||||
* Round trip (string <-> data structure) test for a simpler
|
||||
* `DerivedPath::Built`.
|
||||
*/
|
||||
TEST_F(DerivedPathTest, built_opaque) {
|
||||
std::string_view built = "/nix/store/g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-x.drv^bar,foo";
|
||||
auto elem = DerivedPath::parse(*store, built);
|
||||
auto * p = std::get_if<DerivedPath::Built>(&elem);
|
||||
ASSERT_TRUE(p);
|
||||
ASSERT_EQ(p->outputs, ((OutputsSpec) OutputsSpec::Names { "foo", "bar" }));
|
||||
ASSERT_EQ(*p->drvPath, ((SingleDerivedPath) SingleDerivedPath::Opaque {
|
||||
.path = store->parseStorePath(built.substr(0, 49)),
|
||||
}));
|
||||
ASSERT_EQ(elem.to_string(*store), built);
|
||||
}
|
||||
|
||||
/**
|
||||
* Round trip (string <-> data structure) test for a more complex,
|
||||
* inductive `DerivedPath::Built`.
|
||||
*/
|
||||
TEST_F(DerivedPathTest, built_built) {
|
||||
/**
|
||||
* We set these in tests rather than the regular globals so we don't have
|
||||
* to worry about race conditions if the tests run concurrently.
|
||||
*/
|
||||
ExperimentalFeatureSettings mockXpSettings;
|
||||
mockXpSettings.set("experimental-features", "dynamic-derivations ca-derivations");
|
||||
|
||||
std::string_view built = "/nix/store/g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-x.drv^foo^bar,baz";
|
||||
auto elem = DerivedPath::parse(*store, built, mockXpSettings);
|
||||
auto * p = std::get_if<DerivedPath::Built>(&elem);
|
||||
ASSERT_TRUE(p);
|
||||
ASSERT_EQ(p->outputs, ((OutputsSpec) OutputsSpec::Names { "bar", "baz" }));
|
||||
auto * drvPath = std::get_if<SingleDerivedPath::Built>(&*p->drvPath);
|
||||
ASSERT_TRUE(drvPath);
|
||||
ASSERT_EQ(drvPath->output, "foo");
|
||||
ASSERT_EQ(*drvPath->drvPath, ((SingleDerivedPath) SingleDerivedPath::Opaque {
|
||||
.path = store->parseStorePath(built.substr(0, 49)),
|
||||
}));
|
||||
ASSERT_EQ(elem.to_string(*store), built);
|
||||
}
|
||||
|
||||
/**
|
||||
* Without the right experimental features enabled, we cannot parse a
|
||||
* complex inductive derived path.
|
||||
*/
|
||||
TEST_F(DerivedPathTest, built_built_xp) {
|
||||
ASSERT_THROW(
|
||||
DerivedPath::parse(*store, "/nix/store/g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-x.drv^foo^bar,baz"),
|
||||
MissingExperimentalFeature);
|
||||
}
|
||||
|
||||
RC_GTEST_FIXTURE_PROP(
|
||||
|
|
|
@ -12,8 +12,18 @@ namespace rc {
|
|||
using namespace nix;
|
||||
|
||||
template<>
|
||||
struct Arbitrary<DerivedPath::Opaque> {
|
||||
static Gen<DerivedPath::Opaque> arbitrary();
|
||||
struct Arbitrary<SingleDerivedPath::Opaque> {
|
||||
static Gen<SingleDerivedPath::Opaque> arbitrary();
|
||||
};
|
||||
|
||||
template<>
|
||||
struct Arbitrary<SingleDerivedPath::Built> {
|
||||
static Gen<SingleDerivedPath::Built> arbitrary();
|
||||
};
|
||||
|
||||
template<>
|
||||
struct Arbitrary<SingleDerivedPath> {
|
||||
static Gen<SingleDerivedPath> arbitrary();
|
||||
};
|
||||
|
||||
template<>
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue