nix profile install: Don't use queryDerivationOutputMap()

Instead get the outputs from Installable::build(). This will also
allow 'nix profile install' to support impure derivations.

Fixes #6286.
This commit is contained in:
Eelco Dolstra 2022-03-28 14:21:35 +02:00
parent 1844172dd1
commit 057f9ee190
4 changed files with 116 additions and 76 deletions

View file

@ -756,55 +756,20 @@ std::shared_ptr<Installable> SourceExprCommand::parseInstallable(
return installables.front(); return installables.front();
} }
BuiltPaths getBuiltPaths(ref<Store> evalStore, ref<Store> store, const DerivedPaths & hopefullyBuiltPaths) BuiltPaths Installable::build(
ref<Store> evalStore,
ref<Store> store,
Realise mode,
const std::vector<std::shared_ptr<Installable>> & installables,
BuildMode bMode)
{ {
BuiltPaths res; BuiltPaths res;
for (const auto & b : hopefullyBuiltPaths) for (auto & [_, builtPath] : build2(evalStore, store, mode, installables, bMode))
std::visit( res.push_back(builtPath);
overloaded{
[&](const DerivedPath::Opaque & bo) {
res.push_back(BuiltPath::Opaque{bo.path});
},
[&](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) {
if (!outputHashes.count(output))
throw Error(
"the derivation '%s' doesn't have an output named '%s'",
store->printStorePath(bfd.drvPath), output);
if (settings.isExperimentalFeatureEnabled(Xp::CaDerivations)) {
auto outputId =
DrvOutput{outputHashes.at(output), output};
auto realisation =
store->queryRealisation(outputId);
if (!realisation)
throw Error(
"cannot operate on an output of unbuilt "
"content-addressed 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.
assert(drvOutputs.count(output));
assert(drvOutputs.at(output).second);
outputs.insert_or_assign(
output, *drvOutputs.at(output).second);
}
}
res.push_back(BuiltPath::Built{bfd.drvPath, outputs});
},
},
b.raw());
return res; return res;
} }
BuiltPaths Installable::build( std::vector<std::pair<std::shared_ptr<Installable>, BuiltPath>> Installable::build2(
ref<Store> evalStore, ref<Store> evalStore,
ref<Store> store, ref<Store> store,
Realise mode, Realise mode,
@ -815,39 +780,93 @@ BuiltPaths Installable::build(
settings.readOnlyMode = true; settings.readOnlyMode = true;
std::vector<DerivedPath> pathsToBuild; std::vector<DerivedPath> pathsToBuild;
std::map<DerivedPath, std::vector<std::shared_ptr<Installable>>> backmap;
for (auto & i : installables) { for (auto & i : installables) {
auto b = i->toDerivedPaths(); for (auto b : i->toDerivedPaths()) {
pathsToBuild.insert(pathsToBuild.end(), b.begin(), b.end()); pathsToBuild.push_back(b);
backmap[b].push_back(i);
}
} }
std::vector<std::pair<std::shared_ptr<Installable>, BuiltPath>> res;
switch (mode) { switch (mode) {
case Realise::Nothing: case Realise::Nothing:
case Realise::Derivation: case Realise::Derivation:
printMissing(store, pathsToBuild, lvlError); printMissing(store, pathsToBuild, lvlError);
return getBuiltPaths(evalStore, store, pathsToBuild);
for (auto & path : pathsToBuild) {
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) {
if (!outputHashes.count(output))
throw Error(
"the derivation '%s' doesn't have an output named '%s'",
store->printStorePath(bfd.drvPath), output);
if (settings.isExperimentalFeatureEnabled(Xp::CaDerivations)) {
DrvOutput outputId { outputHashes.at(output), output };
auto realisation = store->queryRealisation(outputId);
if (!realisation)
throw Error(
"cannot operate on an output of unbuilt "
"content-addressed 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.
assert(drvOutputs.count(output));
assert(drvOutputs.at(output).second);
outputs.insert_or_assign(
output, *drvOutputs.at(output).second);
}
}
res.push_back({installable, BuiltPath::Built { bfd.drvPath, outputs }});
},
[&](const DerivedPath::Opaque & bo) {
res.push_back({installable, BuiltPath::Opaque { bo.path }});
},
}, path.raw());
}
}
break;
case Realise::Outputs: { case Realise::Outputs: {
BuiltPaths res;
for (auto & buildResult : store->buildPathsWithResults(pathsToBuild, bMode, evalStore)) { for (auto & buildResult : store->buildPathsWithResults(pathsToBuild, bMode, evalStore)) {
if (!buildResult.success()) if (!buildResult.success())
buildResult.rethrow(); buildResult.rethrow();
std::visit(overloaded {
[&](const DerivedPath::Built & bfd) { for (auto & installable : backmap[buildResult.path]) {
std::map<std::string, StorePath> outputs; std::visit(overloaded {
for (auto & path : buildResult.builtOutputs) [&](const DerivedPath::Built & bfd) {
outputs.emplace(path.first.outputName, path.second.outPath); std::map<std::string, StorePath> outputs;
res.push_back(BuiltPath::Built { bfd.drvPath, outputs }); for (auto & path : buildResult.builtOutputs)
}, outputs.emplace(path.first.outputName, path.second.outPath);
[&](const DerivedPath::Opaque & bo) { res.push_back({installable, BuiltPath::Built { bfd.drvPath, outputs }});
res.push_back(BuiltPath::Opaque { bo.path }); },
}, [&](const DerivedPath::Opaque & bo) {
}, buildResult.path.raw()); res.push_back({installable, BuiltPath::Opaque { bo.path }});
},
}, buildResult.path.raw());
}
} }
return res;
break;
} }
default: default:
assert(false); assert(false);
} }
return res;
} }
BuiltPaths Installable::toBuiltPaths( BuiltPaths Installable::toBuiltPaths(

View file

@ -98,6 +98,13 @@ struct Installable
const std::vector<std::shared_ptr<Installable>> & installables, const std::vector<std::shared_ptr<Installable>> & installables,
BuildMode bMode = bmNormal); BuildMode bMode = bmNormal);
static std::vector<std::pair<std::shared_ptr<Installable>, BuiltPath>> build2(
ref<Store> evalStore,
ref<Store> store,
Realise mode,
const std::vector<std::shared_ptr<Installable>> & installables,
BuildMode bMode = bmNormal);
static std::set<StorePath> toStorePaths( static std::set<StorePath> toStorePaths(
ref<Store> evalStore, ref<Store> evalStore,
ref<Store> store, ref<Store> store,
@ -185,9 +192,4 @@ ref<eval_cache::EvalCache> openEvalCache(
EvalState & state, EvalState & state,
std::shared_ptr<flake::LockedFlake> lockedFlake); std::shared_ptr<flake::LockedFlake> lockedFlake);
BuiltPaths getBuiltPaths(
ref<Store> evalStore,
ref<Store> store,
const DerivedPaths & hopefullyBuiltPaths);
} }

View file

@ -25,6 +25,9 @@ struct DerivedPathOpaque {
nlohmann::json toJSON(ref<Store> store) const; nlohmann::json toJSON(ref<Store> store) const;
std::string to_string(const Store & store) const; std::string to_string(const Store & store) const;
static DerivedPathOpaque parse(const Store & store, std::string_view); static DerivedPathOpaque parse(const Store & store, std::string_view);
bool operator < (const DerivedPathOpaque & b) const
{ return path < b.path; }
}; };
/** /**
@ -46,6 +49,9 @@ struct DerivedPathBuilt {
std::string to_string(const Store & store) const; std::string to_string(const Store & store) const;
static DerivedPathBuilt parse(const Store & store, std::string_view); static DerivedPathBuilt parse(const Store & store, std::string_view);
nlohmann::json toJSON(ref<Store> store) const; nlohmann::json toJSON(ref<Store> store) const;
bool operator < (const DerivedPathBuilt & b) const
{ return std::make_pair(drvPath, outputs) < std::make_pair(b.drvPath, b.outputs); }
}; };
using _DerivedPathRaw = std::variant< using _DerivedPathRaw = std::variant<

View file

@ -62,22 +62,21 @@ struct ProfileElement
return std::tuple(describe(), storePaths) < std::tuple(other.describe(), other.storePaths); return std::tuple(describe(), storePaths) < std::tuple(other.describe(), other.storePaths);
} }
void updateStorePaths(ref<Store> evalStore, ref<Store> store, Installable & installable) void updateStorePaths(
ref<Store> evalStore,
ref<Store> store,
const BuiltPaths & builtPaths)
{ {
// FIXME: respect meta.outputsToInstall // FIXME: respect meta.outputsToInstall
storePaths.clear(); storePaths.clear();
for (auto & buildable : getBuiltPaths(evalStore, store, installable.toDerivedPaths())) { for (auto & buildable : builtPaths) {
std::visit(overloaded { std::visit(overloaded {
[&](const BuiltPath::Opaque & bo) { [&](const BuiltPath::Opaque & bo) {
storePaths.insert(bo.path); storePaths.insert(bo.path);
}, },
[&](const BuiltPath::Built & bfd) { [&](const BuiltPath::Built & bfd) {
// TODO: Why are we querying if we know the output for (auto & output : bfd.outputs)
// names already? Is it just to figure out what the
// default one is?
for (auto & output : store->queryDerivationOutputMap(bfd.drvPath)) {
storePaths.insert(output.second); storePaths.insert(output.second);
}
}, },
}, buildable.raw()); }, buildable.raw());
} }
@ -236,6 +235,16 @@ struct ProfileManifest
} }
}; };
static std::map<Installable *, BuiltPaths>
builtPathsPerInstallable(
const std::vector<std::pair<std::shared_ptr<Installable>, BuiltPath>> & builtPaths)
{
std::map<Installable *, BuiltPaths> res;
for (auto & [installable, builtPath] : builtPaths)
res[installable.get()].push_back(builtPath);
return res;
}
struct CmdProfileInstall : InstallablesCommand, MixDefaultProfile struct CmdProfileInstall : InstallablesCommand, MixDefaultProfile
{ {
std::string description() override std::string description() override
@ -254,7 +263,9 @@ struct CmdProfileInstall : InstallablesCommand, MixDefaultProfile
{ {
ProfileManifest manifest(*getEvalState(), *profile); ProfileManifest manifest(*getEvalState(), *profile);
auto builtPaths = Installable::build(getEvalStore(), store, Realise::Outputs, installables, bmNormal); auto builtPaths = builtPathsPerInstallable(
Installable::build2(
getEvalStore(), store, Realise::Outputs, installables, bmNormal));
for (auto & installable : installables) { for (auto & installable : installables) {
ProfileElement element; ProfileElement element;
@ -269,7 +280,7 @@ struct CmdProfileInstall : InstallablesCommand, MixDefaultProfile
}; };
} }
element.updateStorePaths(getEvalStore(), store, *installable); element.updateStorePaths(getEvalStore(), store, builtPaths[installable.get()]);
manifest.elements.push_back(std::move(element)); manifest.elements.push_back(std::move(element));
} }
@ -457,12 +468,14 @@ struct CmdProfileUpgrade : virtual SourceExprCommand, MixDefaultProfile, MixProf
warn ("Use 'nix profile list' to see the current profile."); warn ("Use 'nix profile list' to see the current profile.");
} }
auto builtPaths = Installable::build(getEvalStore(), store, Realise::Outputs, installables, bmNormal); auto builtPaths = builtPathsPerInstallable(
Installable::build2(
getEvalStore(), store, Realise::Outputs, installables, bmNormal));
for (size_t i = 0; i < installables.size(); ++i) { for (size_t i = 0; i < installables.size(); ++i) {
auto & installable = installables.at(i); auto & installable = installables.at(i);
auto & element = manifest.elements[indices.at(i)]; auto & element = manifest.elements[indices.at(i)];
element.updateStorePaths(getEvalStore(), store, *installable); element.updateStorePaths(getEvalStore(), store, builtPaths[installable.get()]);
} }
updateProfile(manifest.build(store)); updateProfile(manifest.build(store));