Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

InstallableFlake::toDerivedPaths(): Support paths and store paths #7484

Merged
merged 9 commits into from
Jan 10, 2023
202 changes: 106 additions & 96 deletions src/libcmd/installables.cc
Original file line number Diff line number Diff line change
Expand Up @@ -358,7 +358,7 @@ void completeFlakeRef(ref<Store> store, std::string_view prefix)
}
}

DerivedPath Installable::toDerivedPath()
DerivedPathWithInfo Installable::toDerivedPath()
{
auto buildables = toDerivedPaths();
if (buildables.size() != 1)
Expand Down Expand Up @@ -422,21 +422,9 @@ struct InstallableStorePath : Installable
return req.to_string(*store);
}

DerivedPaths toDerivedPaths() override
DerivedPathsWithInfo toDerivedPaths() override
{
return { req };
}

StorePathSet toDrvPaths(ref<Store> store) override
{
return std::visit(overloaded {
[&](const DerivedPath::Built & bfd) -> StorePathSet {
return { bfd.drvPath };
},
[&](const DerivedPath::Opaque & bo) -> StorePathSet {
return { getDeriver(store, *this, bo.path) };
},
}, req.raw());
return {{.path = req, .info = {} }};
}

std::optional<StorePath> getStorePath() override
Expand All @@ -452,34 +440,6 @@ struct InstallableStorePath : Installable
}
};

DerivedPaths InstallableValue::toDerivedPaths()
{
DerivedPaths res;

std::map<StorePath, std::set<std::string>> drvsToOutputs;
RealisedPath::Set drvsToCopy;

// Group by derivation, helps with .all in particular
for (auto & drv : toDerivations()) {
for (auto & outputName : drv.outputsToInstall)
drvsToOutputs[drv.drvPath].insert(outputName);
drvsToCopy.insert(drv.drvPath);
}

for (auto & i : drvsToOutputs)
res.push_back(DerivedPath::Built { i.first, i.second });

return res;
}

StorePathSet InstallableValue::toDrvPaths(ref<Store> store)
{
StorePathSet res;
for (auto & drv : toDerivations())
res.insert(drv.drvPath);
return res;
}

struct InstallableAttrPath : InstallableValue
{
SourceExprCommand & cmd;
Expand Down Expand Up @@ -509,40 +469,45 @@ struct InstallableAttrPath : InstallableValue
return {vRes, pos};
}

virtual std::vector<InstallableValue::DerivationInfo> toDerivations() override;
};
DerivedPathsWithInfo toDerivedPaths() override
{
auto v = toValue(*state).first;

std::vector<InstallableValue::DerivationInfo> InstallableAttrPath::toDerivations()
{
auto v = toValue(*state).first;
Bindings & autoArgs = *cmd.getAutoArgs(*state);

Bindings & autoArgs = *cmd.getAutoArgs(*state);
DrvInfos drvInfos;
getDerivations(*state, *v, "", autoArgs, drvInfos, false);

DrvInfos drvInfos;
getDerivations(*state, *v, "", autoArgs, drvInfos, false);
// Backward compatibility hack: group results by drvPath. This
// helps keep .all output together.
std::map<StorePath, DerivedPath::Built> byDrvPath;

std::vector<DerivationInfo> res;
for (auto & drvInfo : drvInfos) {
auto drvPath = drvInfo.queryDrvPath();
if (!drvPath)
throw Error("'%s' is not a derivation", what());
for (auto & drvInfo : drvInfos) {
auto drvPath = drvInfo.queryDrvPath();
if (!drvPath)
throw Error("'%s' is not a derivation", what());

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

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

res.push_back(DerivationInfo {
.drvPath = *drvPath,
.outputsToInstall = std::move(outputsToInstall)
});
}
auto derivedPath = byDrvPath.emplace(*drvPath, DerivedPath::Built { .drvPath = *drvPath }).first;

return res;
}
for (auto & output : outputsToInstall)
derivedPath->second.outputs.insert(output);
}

DerivedPathsWithInfo res;
for (auto & [_, info] : byDrvPath)
res.push_back({ .path = { info } });

return res;
}
};

std::vector<std::string> InstallableFlake::getActualAttrPaths()
{
Expand Down Expand Up @@ -630,16 +595,46 @@ InstallableFlake::InstallableFlake(
throw UsageError("'--arg' and '--argstr' are incompatible with flakes");
}

std::tuple<std::string, FlakeRef, InstallableValue::DerivationInfo> InstallableFlake::toDerivation()
DerivedPathsWithInfo InstallableFlake::toDerivedPaths()
{
Activity act(*logger, lvlTalkative, actUnknown, fmt("evaluating derivation '%s'", what()));

auto attr = getCursor(*state);

auto attrPath = attr->getAttrPathStr();

if (!attr->isDerivation())
throw Error("flake output attribute '%s' is not a derivation", attrPath);
if (!attr->isDerivation()) {

// FIXME: use eval cache?
auto v = attr->forceValue();

if (v.type() == nPath) {
PathSet context;
auto storePath = state->copyPathToStore(context, Path(v.path));
return {{
.path = DerivedPath::Opaque {
.path = std::move(storePath),
}
}};
}

else if (v.type() == nString) {
PathSet context;
auto s = state->forceString(v, context, noPos, fmt("while evaluating the flake output attribute '%s'", attrPath));
auto storePath = state->store->maybeParseStorePath(s);
if (storePath && context.count(std::string(s))) {
return {{
.path = DerivedPath::Opaque {
.path = std::move(*storePath),
}
}};
} else
throw Error("flake output attribute '%s' evaluates to the string '%s' which is not a store path", attrPath, s);
}

else
throw Error("flake output attribute '%s' is not a derivation or path", attrPath);
}

auto drvPath = attr->forceDerivation();

Expand Down Expand Up @@ -674,20 +669,19 @@ std::tuple<std::string, FlakeRef, InstallableValue::DerivationInfo> InstallableF
if (auto outputNames = std::get_if<OutputNames>(&outputsSpec))
outputsToInstall = *outputNames;

auto drvInfo = DerivationInfo {
.drvPath = std::move(drvPath),
.outputsToInstall = std::move(outputsToInstall),
.priority = priority,
};

return {attrPath, getLockedFlake()->flake.lockedRef, std::move(drvInfo)};
}

std::vector<InstallableValue::DerivationInfo> InstallableFlake::toDerivations()
{
std::vector<DerivationInfo> res;
res.push_back(std::get<2>(toDerivation()));
return res;
return {{
.path = DerivedPath::Built {
.drvPath = std::move(drvPath),
.outputs = std::move(outputsToInstall),
},
.info = {
.priority = priority,
.originalRef = flakeRef,
.resolvedRef = getLockedFlake()->flake.lockedRef,
.attrPath = attrPath,
.outputsSpec = outputsSpec,
}
}};
}

std::pair<Value *, PosIdx> InstallableFlake::toValue(EvalState & state)
Expand Down Expand Up @@ -895,13 +889,19 @@ std::vector<std::pair<std::shared_ptr<Installable>, BuiltPathWithResult>> Instal
if (mode == Realise::Nothing)
settings.readOnlyMode = true;

struct Aux
{
ExtraPathInfo info;
std::shared_ptr<Installable> installable;
};

std::vector<DerivedPath> pathsToBuild;
std::map<DerivedPath, std::vector<std::shared_ptr<Installable>>> backmap;
std::map<DerivedPath, std::vector<Aux>> backmap;

for (auto & i : installables) {
for (auto b : i->toDerivedPaths()) {
pathsToBuild.push_back(b);
backmap[b].push_back(i);
pathsToBuild.push_back(b.path);
backmap[b.path].push_back({.info = b.info, .installable = i});
}
}

Expand All @@ -914,7 +914,7 @@ std::vector<std::pair<std::shared_ptr<Installable>, BuiltPathWithResult>> Instal
printMissing(store, pathsToBuild, lvlError);

for (auto & path : pathsToBuild) {
for (auto & installable : backmap[path]) {
for (auto & aux : backmap[path]) {
std::visit(overloaded {
[&](const DerivedPath::Built & bfd) {
OutputPathMap outputs;
Expand Down Expand Up @@ -943,10 +943,14 @@ std::vector<std::pair<std::shared_ptr<Installable>, BuiltPathWithResult>> Instal
output, *drvOutput->second);
}
}
res.push_back({installable, {.path = BuiltPath::Built { bfd.drvPath, outputs }}});
res.push_back({aux.installable, {
.path = BuiltPath::Built { bfd.drvPath, outputs },
.info = aux.info}});
},
[&](const DerivedPath::Opaque & bo) {
res.push_back({installable, {.path = BuiltPath::Opaque { bo.path }}});
res.push_back({aux.installable, {
.path = BuiltPath::Opaque { bo.path },
.info = aux.info}});
},
}, path.raw());
}
Expand All @@ -962,16 +966,22 @@ std::vector<std::pair<std::shared_ptr<Installable>, BuiltPathWithResult>> Instal
if (!buildResult.success())
buildResult.rethrow();

for (auto & installable : backmap[buildResult.path]) {
for (auto & aux : backmap[buildResult.path]) {
std::visit(overloaded {
[&](const DerivedPath::Built & bfd) {
std::map<std::string, StorePath> outputs;
for (auto & path : buildResult.builtOutputs)
outputs.emplace(path.first.outputName, path.second.outPath);
res.push_back({installable, {.path = BuiltPath::Built { bfd.drvPath, outputs }, .result = buildResult}});
res.push_back({aux.installable, {
.path = BuiltPath::Built { bfd.drvPath, outputs },
.info = aux.info,
.result = buildResult}});
},
[&](const DerivedPath::Opaque & bo) {
res.push_back({installable, {.path = BuiltPath::Opaque { bo.path }, .result = buildResult}});
res.push_back({aux.installable, {
.path = BuiltPath::Opaque { bo.path },
.info = aux.info,
.result = buildResult}});
},
}, buildResult.path.raw());
}
Expand Down Expand Up @@ -1056,7 +1066,7 @@ StorePathSet Installable::toDerivations(
[&](const DerivedPath::Built & bfd) {
drvPaths.insert(bfd.drvPath);
},
}, b.raw());
}, b.path.raw());

return drvPaths;
}
Expand Down
47 changes: 24 additions & 23 deletions src/libcmd/installables.hh
Original file line number Diff line number Diff line change
Expand Up @@ -52,26 +52,42 @@ enum class OperateOn {
Derivation
};

struct ExtraPathInfo
{
std::optional<NixInt> priority;
std::optional<FlakeRef> originalRef;
std::optional<FlakeRef> resolvedRef;
std::optional<std::string> attrPath;
// FIXME: merge with DerivedPath's 'outputs' field?
std::optional<OutputsSpec> outputsSpec;
};

/* A derived path with any additional info that commands might
need from the derivation. */
struct DerivedPathWithInfo
{
DerivedPath path;
ExtraPathInfo info;
};

struct BuiltPathWithResult
{
BuiltPath path;
ExtraPathInfo info;
std::optional<BuildResult> result;
};

typedef std::vector<DerivedPathWithInfo> DerivedPathsWithInfo;

struct Installable
{
virtual ~Installable() { }

virtual std::string what() const = 0;

virtual DerivedPaths toDerivedPaths() = 0;

virtual StorePathSet toDrvPaths(ref<Store> store)
{
throw Error("'%s' cannot be converted to a derivation path", what());
}
virtual DerivedPathsWithInfo toDerivedPaths() = 0;

DerivedPath toDerivedPath();
DerivedPathWithInfo toDerivedPath();

UnresolvedApp toApp(EvalState & state);

Expand Down Expand Up @@ -146,19 +162,6 @@ struct InstallableValue : Installable
ref<EvalState> state;

InstallableValue(ref<EvalState> state) : state(state) {}

struct DerivationInfo
{
StorePath drvPath;
std::set<std::string> outputsToInstall;
std::optional<NixInt> priority;
};

virtual std::vector<DerivationInfo> toDerivations() = 0;

DerivedPaths toDerivedPaths() override;

StorePathSet toDrvPaths(ref<Store> store) override;
};

struct InstallableFlake : InstallableValue
Expand Down Expand Up @@ -186,9 +189,7 @@ struct InstallableFlake : InstallableValue

Value * getFlakeOutputs(EvalState & state, const flake::LockedFlake & lockedFlake);

std::tuple<std::string, FlakeRef, DerivationInfo> toDerivation();

std::vector<DerivationInfo> toDerivations() override;
DerivedPathsWithInfo toDerivedPaths() override;

std::pair<Value *, PosIdx> toValue(EvalState & state) override;

Expand Down
Loading