Skip to content

Commit

Permalink
Split OutputsSpec and ExtendedOutputsSpec, use the former more
Browse files Browse the repository at this point in the history
`DerivedPath::Built` and `DerivationGoal` were previously using a
regular set with the convention that the empty set means all outputs.
But it is easy to forget about this rule when processing those sets.
Using `OutputSpec` forces us to get it right.
  • Loading branch information
Ericson2314 committed Dec 20, 2022
1 parent d0d5851 commit adb153a
Show file tree
Hide file tree
Showing 23 changed files with 343 additions and 159 deletions.
44 changes: 10 additions & 34 deletions src/libcmd/installables.cc
Original file line number Diff line number Diff line change
Expand Up @@ -530,10 +530,12 @@ std::vector<InstallableValue::DerivationInfo> InstallableAttrPath::toDerivations

std::set<std::string> outputsToInstall;

if (auto outputNames = std::get_if<OutputNames>(&extendedOutputsSpec))
auto * outputsSpec = std::get_if<ExtendedOutputsSpec::Explicit>(&extendedOutputsSpec);

if (auto outputNames = std::get_if<OutputsSpec::Names>(outputsSpec))
outputsToInstall = *outputNames;
else
for (auto & output : drvInfo.queryOutputs(false, std::get_if<DefaultOutputs>(&extendedOutputsSpec)))
for (auto & output : drvInfo.queryOutputs(false, std::get_if<ExtendedOutputsSpec::Default>(&extendedOutputsSpec)))
outputsToInstall.insert(output.first);

res.push_back(DerivationInfo {
Expand Down Expand Up @@ -662,7 +664,9 @@ std::tuple<std::string, FlakeRef, InstallableValue::DerivationInfo> InstallableF
priority = aPriority->getInt();
}

if (outputsToInstall.empty() || std::get_if<AllOutputs>(&extendedOutputsSpec)) {
auto * outputsSpec = std::get_if<ExtendedOutputsSpec::Explicit>(&extendedOutputsSpec);

if (outputsToInstall.empty() || std::get_if<OutputsSpec::All>(outputsSpec)) {
outputsToInstall.clear();
if (auto aOutputs = attr->maybeGetAttr(state->sOutputs))
for (auto & s : aOutputs->getListOfStrings())
Expand All @@ -672,7 +676,7 @@ std::tuple<std::string, FlakeRef, InstallableValue::DerivationInfo> InstallableF
if (outputsToInstall.empty())
outputsToInstall.insert("out");

if (auto outputNames = std::get_if<OutputNames>(&extendedOutputsSpec))
if (auto outputNames = std::get_if<OutputsSpec::Names>(outputsSpec))
outputsToInstall = *outputNames;

auto drvInfo = DerivationInfo {
Expand Down Expand Up @@ -807,7 +811,7 @@ std::vector<std::shared_ptr<Installable>> SourceExprCommand::parseInstallables(
result.push_back(
std::make_shared<InstallableAttrPath>(
state, *this, vFile,
prefix == "." ? "" : prefix,
prefix == "." ? "" : std::string { prefix },
extendedOutputsSpec));
}

Expand Down Expand Up @@ -918,35 +922,7 @@ std::vector<std::pair<std::shared_ptr<Installable>, BuiltPathWithResult>> Instal
for (auto & installable : backmap[path]) {
std::visit(overloaded {
[&](const DerivedPath::Built & bfd) {
OutputPathMap outputs;
auto drv = evalStore->readDerivation(bfd.drvPath);
auto outputHashes = staticOutputHashes(*evalStore, drv); // FIXME: expensive
auto drvOutputs = drv.outputsAndOptPaths(*store);
for (auto & output : bfd.outputs) {
auto outputHash = get(outputHashes, output);
if (!outputHash)
throw Error(
"the derivation '%s' doesn't have an output named '%s'",
store->printStorePath(bfd.drvPath), output);
if (settings.isExperimentalFeatureEnabled(Xp::CaDerivations)) {
DrvOutput outputId { *outputHash, output };
auto realisation = store->queryRealisation(outputId);
if (!realisation)
throw Error(
"cannot operate on an output of the "
"unbuilt derivation '%s'",
outputId.to_string());
outputs.insert_or_assign(output, realisation->outPath);
} else {
// If ca-derivations isn't enabled, assume that
// the output path is statically known.
auto drvOutput = get(drvOutputs, output);
assert(drvOutput);
assert(drvOutput->second);
outputs.insert_or_assign(
output, *drvOutput->second);
}
}
auto outputs = resolveDerivedPath(*store, bfd, &*evalStore);
res.push_back({installable, {.path = BuiltPath::Built { bfd.drvPath, outputs }}});
},
[&](const DerivedPath::Opaque & bo) {
Expand Down
2 changes: 1 addition & 1 deletion src/libexpr/flake/flakeref.cc
Original file line number Diff line number Diff line change
Expand Up @@ -245,7 +245,7 @@ std::tuple<FlakeRef, std::string, ExtendedOutputsSpec> parseFlakeRefWithFragment
bool isFlake)
{
auto [prefix, extendedOutputsSpec] = ExtendedOutputsSpec::parse(url);
auto [flakeRef, fragment] = parseFlakeRefWithFragment(prefix, baseDir, allowMissing, isFlake);
auto [flakeRef, fragment] = parseFlakeRefWithFragment(std::string { prefix }, baseDir, allowMissing, isFlake);
return {std::move(flakeRef), fragment, extendedOutputsSpec};
}

Expand Down
16 changes: 6 additions & 10 deletions src/libexpr/primops.cc
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ StringMap EvalState::realiseContext(const PathSet & context)
if (!store->isValidPath(ctx))
debugThrowLastTrace(InvalidPathError(store->printStorePath(ctx)));
if (!outputName.empty() && ctx.isDerivation()) {
drvs.push_back({ctx, {outputName}});
drvs.push_back({ctx, OutputsSpec::Names{outputName}});
} else {
res.insert_or_assign(ctxS, ctxS);
}
Expand All @@ -68,16 +68,12 @@ StringMap EvalState::realiseContext(const PathSet & context)
store->buildPaths(buildReqs);

/* Get all the output paths corresponding to the placeholders we had */
for (auto & [drvPath, outputs] : drvs) {
const auto outputPaths = store->queryDerivationOutputMap(drvPath);
for (auto & outputName : outputs) {
auto outputPath = get(outputPaths, outputName);
if (!outputPath)
debugThrowLastTrace(Error("derivation '%s' does not have an output named '%s'",
store->printStorePath(drvPath), outputName));
for (auto & drv : drvs) {
auto outputs = resolveDerivedPath(*store, drv);
for (auto & [outputName, outputPath] : outputs) {
res.insert_or_assign(
downstreamPlaceholder(*store, drvPath, outputName),
store->printStorePath(*outputPath)
downstreamPlaceholder(*store, drv.drvPath, outputName),
store->printStorePath(outputPath)
);
}
}
Expand Down
47 changes: 26 additions & 21 deletions src/libstore/build/derivation-goal.cc
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@
namespace nix {

DerivationGoal::DerivationGoal(const StorePath & drvPath,
const StringSet & wantedOutputs, Worker & worker, BuildMode buildMode)
const OutputsSpec & wantedOutputs, Worker & worker, BuildMode buildMode)
: Goal(worker, DerivedPath::Built { .drvPath = drvPath, .outputs = wantedOutputs })
, useDerivation(true)
, drvPath(drvPath)
Expand All @@ -83,7 +83,7 @@ DerivationGoal::DerivationGoal(const StorePath & drvPath,


DerivationGoal::DerivationGoal(const StorePath & drvPath, const BasicDerivation & drv,
const StringSet & wantedOutputs, Worker & worker, BuildMode buildMode)
const OutputsSpec & wantedOutputs, Worker & worker, BuildMode buildMode)
: Goal(worker, DerivedPath::Built { .drvPath = drvPath, .outputs = wantedOutputs })
, useDerivation(false)
, drvPath(drvPath)
Expand Down Expand Up @@ -143,18 +143,11 @@ void DerivationGoal::work()
(this->*state)();
}

void DerivationGoal::addWantedOutputs(const StringSet & outputs)
void DerivationGoal::addWantedOutputs(const OutputsSpec & outputs)
{
/* If we already want all outputs, there is nothing to do. */
if (wantedOutputs.empty()) return;

if (outputs.empty()) {
wantedOutputs.clear();
bool newOutputs = wantedOutputs.merge(outputs);
if (newOutputs)
needRestart = true;
} else
for (auto & i : outputs)
if (wantedOutputs.insert(i).second)
needRestart = true;
}


Expand Down Expand Up @@ -391,7 +384,7 @@ void DerivationGoal::repairClosure()
auto outputs = queryDerivationOutputMap();
StorePathSet outputClosure;
for (auto & i : outputs) {
if (!wantOutput(i.first, wantedOutputs)) continue;
if (!wantedOutputs.contains(i.first)) continue;
worker.store.computeFSClosure(i.second, outputClosure);
}

Expand Down Expand Up @@ -423,7 +416,7 @@ void DerivationGoal::repairClosure()
if (drvPath2 == outputsToDrv.end())
addWaitee(upcast_goal(worker.makePathSubstitutionGoal(i, Repair)));
else
addWaitee(worker.makeDerivationGoal(drvPath2->second, StringSet(), bmRepair));
addWaitee(worker.makeDerivationGoal(drvPath2->second, OutputsSpec::All(), bmRepair));
}

if (waitees.empty()) {
Expand Down Expand Up @@ -991,10 +984,15 @@ void DerivationGoal::resolvedFinished()

StorePathSet outputPaths;

// `wantedOutputs` might be empty, which means “all the outputs”
auto realWantedOutputs = wantedOutputs;
if (realWantedOutputs.empty())
realWantedOutputs = resolvedDrv.outputNames();
// `wantedOutputs` might merely indicate “all the outputs”
auto realWantedOutputs = std::visit(overloaded {
[&](const OutputsSpec::All &) {
return resolvedDrv.outputNames();
},
[&](const OutputsSpec::Names & names) {
return names;
},
}, wantedOutputs.raw());

for (auto & wantedOutput : realWantedOutputs) {
auto initialOutput = get(initialOutputs, wantedOutput);
Expand Down Expand Up @@ -1322,7 +1320,14 @@ std::pair<bool, DrvOutputs> DerivationGoal::checkPathValidity()
if (!drv->type().isPure()) return { false, {} };

bool checkHash = buildMode == bmRepair;
auto wantedOutputsLeft = wantedOutputs;
auto wantedOutputsLeft = std::visit(overloaded {
[&](const OutputsSpec::All &) {
return StringSet {};
},
[&](const OutputsSpec::Names & names) {
return names;
},
}, wantedOutputs.raw());
DrvOutputs validOutputs;

for (auto & i : queryPartialDerivationOutputMap()) {
Expand All @@ -1331,7 +1336,7 @@ std::pair<bool, DrvOutputs> DerivationGoal::checkPathValidity()
// this is an invalid output, gets catched with (!wantedOutputsLeft.empty())
continue;
auto & info = *initialOutput;
info.wanted = wantOutput(i.first, wantedOutputs);
info.wanted = wantedOutputs.contains(i.first);
if (info.wanted)
wantedOutputsLeft.erase(i.first);
if (i.second) {
Expand Down Expand Up @@ -1369,7 +1374,7 @@ std::pair<bool, DrvOutputs> DerivationGoal::checkPathValidity()
validOutputs.emplace(drvOutput, Realisation { drvOutput, info.known->path });
}

// If we requested all the outputs via the empty set, we are always fine.
// If we requested all the outputs, we are always fine.
// If we requested specific elements, the loop above removes all the valid
// ones, so any that are left must be invalid.
if (!wantedOutputsLeft.empty())
Expand Down
9 changes: 5 additions & 4 deletions src/libstore/build/derivation-goal.hh
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

#include "parsed-derivations.hh"
#include "lock.hh"
#include "outputs-spec.hh"
#include "store-api.hh"
#include "pathlocks.hh"
#include "goal.hh"
Expand Down Expand Up @@ -55,7 +56,7 @@ struct DerivationGoal : public Goal

/* The specific outputs that we need to build. Empty means all of
them. */
StringSet wantedOutputs;
OutputsSpec wantedOutputs;

/* Mapping from input derivations + output names to actual store
paths. This is filled in by waiteeDone() as each dependency
Expand Down Expand Up @@ -128,10 +129,10 @@ struct DerivationGoal : public Goal
std::string machineName;

DerivationGoal(const StorePath & drvPath,
const StringSet & wantedOutputs, Worker & worker,
const OutputsSpec & wantedOutputs, Worker & worker,
BuildMode buildMode = bmNormal);
DerivationGoal(const StorePath & drvPath, const BasicDerivation & drv,
const StringSet & wantedOutputs, Worker & worker,
const OutputsSpec & wantedOutputs, Worker & worker,
BuildMode buildMode = bmNormal);
virtual ~DerivationGoal();

Expand All @@ -142,7 +143,7 @@ struct DerivationGoal : public Goal
void work() override;

/* Add wanted outputs to an already existing derivation goal. */
void addWantedOutputs(const StringSet & outputs);
void addWantedOutputs(const OutputsSpec & outputs);

/* The states. */
void getDerivation();
Expand Down
3 changes: 2 additions & 1 deletion src/libstore/build/entry-points.cc
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,8 @@ void LocalStore::repairPath(const StorePath & path)
auto info = queryPathInfo(path);
if (info->deriver && isValidPath(*info->deriver)) {
goals.clear();
goals.insert(worker.makeDerivationGoal(*info->deriver, StringSet(), bmRepair));
// FIXME: Should just build the specific output we need.
goals.insert(worker.makeDerivationGoal(*info->deriver, OutputsSpec::All { }, bmRepair));
worker.run(goals);
} else
throw Error(worker.exitStatus(), "cannot repair path '%s'", printStorePath(path));
Expand Down
2 changes: 1 addition & 1 deletion src/libstore/build/local-derivation-goal.cc
Original file line number Diff line number Diff line change
Expand Up @@ -2758,7 +2758,7 @@ DrvOutputs LocalDerivationGoal::registerOutputs()
signRealisation(thisRealisation);
worker.store.registerDrvOutput(thisRealisation);
}
if (wantOutput(outputName, wantedOutputs))
if (wantedOutputs.contains(outputName))
builtOutputs.emplace(thisRealisation.id, thisRealisation);
}

Expand Down
6 changes: 3 additions & 3 deletions src/libstore/build/worker.cc
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ Worker::~Worker()

std::shared_ptr<DerivationGoal> Worker::makeDerivationGoalCommon(
const StorePath & drvPath,
const StringSet & wantedOutputs,
const OutputsSpec & wantedOutputs,
std::function<std::shared_ptr<DerivationGoal>()> mkDrvGoal)
{
std::weak_ptr<DerivationGoal> & goal_weak = derivationGoals[drvPath];
Expand All @@ -59,7 +59,7 @@ std::shared_ptr<DerivationGoal> Worker::makeDerivationGoalCommon(


std::shared_ptr<DerivationGoal> Worker::makeDerivationGoal(const StorePath & drvPath,
const StringSet & wantedOutputs, BuildMode buildMode)
const OutputsSpec & wantedOutputs, BuildMode buildMode)
{
return makeDerivationGoalCommon(drvPath, wantedOutputs, [&]() -> std::shared_ptr<DerivationGoal> {
return !dynamic_cast<LocalStore *>(&store)
Expand All @@ -70,7 +70,7 @@ std::shared_ptr<DerivationGoal> Worker::makeDerivationGoal(const StorePath & drv


std::shared_ptr<DerivationGoal> Worker::makeBasicDerivationGoal(const StorePath & drvPath,
const BasicDerivation & drv, const StringSet & wantedOutputs, BuildMode buildMode)
const BasicDerivation & drv, const OutputsSpec & wantedOutputs, BuildMode buildMode)
{
return makeDerivationGoalCommon(drvPath, wantedOutputs, [&]() -> std::shared_ptr<DerivationGoal> {
return !dynamic_cast<LocalStore *>(&store)
Expand Down
6 changes: 3 additions & 3 deletions src/libstore/build/worker.hh
Original file line number Diff line number Diff line change
Expand Up @@ -140,15 +140,15 @@ public:
/* derivation goal */
private:
std::shared_ptr<DerivationGoal> makeDerivationGoalCommon(
const StorePath & drvPath, const StringSet & wantedOutputs,
const StorePath & drvPath, const OutputsSpec & wantedOutputs,
std::function<std::shared_ptr<DerivationGoal>()> mkDrvGoal);
public:
std::shared_ptr<DerivationGoal> makeDerivationGoal(
const StorePath & drvPath,
const StringSet & wantedOutputs, BuildMode buildMode = bmNormal);
const OutputsSpec & wantedOutputs, BuildMode buildMode = bmNormal);
std::shared_ptr<DerivationGoal> makeBasicDerivationGoal(
const StorePath & drvPath, const BasicDerivation & drv,
const StringSet & wantedOutputs, BuildMode buildMode = bmNormal);
const OutputsSpec & wantedOutputs, BuildMode buildMode = bmNormal);

/* substitution goal */
std::shared_ptr<PathSubstitutionGoal> makePathSubstitutionGoal(const StorePath & storePath, RepairFlag repair = NoRepair, std::optional<ContentAddress> ca = std::nullopt);
Expand Down
6 changes: 0 additions & 6 deletions src/libstore/derivations.cc
Original file line number Diff line number Diff line change
Expand Up @@ -688,12 +688,6 @@ std::map<std::string, Hash> staticOutputHashes(Store & store, const Derivation &
}


bool wantOutput(const std::string & output, const std::set<std::string> & wanted)
{
return wanted.empty() || wanted.find(output) != wanted.end();
}


static DerivationOutput readDerivationOutput(Source & in, const Store & store)
{
const auto pathS = readString(in);
Expand Down
2 changes: 0 additions & 2 deletions src/libstore/derivations.hh
Original file line number Diff line number Diff line change
Expand Up @@ -294,8 +294,6 @@ typedef std::map<StorePath, DrvHash> DrvHashes;
// FIXME: global, though at least thread-safe.
extern Sync<DrvHashes> drvHashes;

bool wantOutput(const std::string & output, const std::set<std::string> & wanted);

struct Source;
struct Sink;

Expand Down
Loading

0 comments on commit adb153a

Please sign in to comment.