aboutsummaryrefslogtreecommitdiff
path: root/src/libcmd
diff options
context:
space:
mode:
authorEelco Dolstra <edolstra@gmail.com>2023-01-10 15:57:58 +0100
committerGitHub <noreply@github.com>2023-01-10 15:57:58 +0100
commit1c98daf6e8bdf771ed3b17c947384fef4ed8d006 (patch)
tree112152ad99d08d2acbf8fe80bf542e26864804c8 /src/libcmd
parentcc02e119670c2d1803b8732864b653126e849aa5 (diff)
parent59cc920cc0751f93d4a3b717da72505a7bab2ee7 (diff)
Merge pull request #7484 from edolstra/fix-7417
InstallableFlake::toDerivedPaths(): Support paths and store paths
Diffstat (limited to 'src/libcmd')
-rw-r--r--src/libcmd/installables.cc202
-rw-r--r--src/libcmd/installables.hh47
2 files changed, 130 insertions, 119 deletions
diff --git a/src/libcmd/installables.cc b/src/libcmd/installables.cc
index 79361e94e..c0db2a715 100644
--- a/src/libcmd/installables.cc
+++ b/src/libcmd/installables.cc
@@ -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)
@@ -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
@@ -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;
@@ -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()
{
@@ -630,7 +595,7 @@ 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()));
@@ -638,8 +603,38 @@ std::tuple<std::string, FlakeRef, InstallableValue::DerivationInfo> InstallableF
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();
@@ -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)
@@ -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});
}
}
@@ -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;
@@ -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());
}
@@ -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());
}
@@ -1056,7 +1066,7 @@ StorePathSet Installable::toDerivations(
[&](const DerivedPath::Built & bfd) {
drvPaths.insert(bfd.drvPath);
},
- }, b.raw());
+ }, b.path.raw());
return drvPaths;
}
diff --git a/src/libcmd/installables.hh b/src/libcmd/installables.hh
index 02ea351d3..250cf7e83 100644
--- a/src/libcmd/installables.hh
+++ b/src/libcmd/installables.hh
@@ -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);
@@ -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
@@ -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;