refactor some nix-env and nix profile code into libcmd

Change-Id: Iefc8bbd34b4bc6012175cb3d6e6a8207973bc792
This commit is contained in:
Qyriad 2024-04-27 13:24:39 -06:00
parent 78ce710722
commit 1d00657e9b
7 changed files with 162 additions and 115 deletions

100
src/libcmd/cmd-profiles.cc Normal file
View file

@ -0,0 +1,100 @@
#include "cmd-profiles.hh"
#include "built-path.hh"
#include "names.hh"
#include "store-api.hh"
namespace nix
{
DrvInfos queryInstalled(EvalState & state, const Path & userEnv)
{
DrvInfos elems;
if (pathExists(userEnv + "/manifest.json"))
throw Error("profile '%s' is incompatible with 'nix-env'; please use 'nix profile' instead", userEnv);
auto manifestFile = userEnv + "/manifest.nix";
if (pathExists(manifestFile)) {
Value v;
state.evalFile(state.rootPath(CanonPath(manifestFile)), v);
Bindings & bindings(*state.allocBindings(0));
getDerivations(state, v, "", bindings, elems, false);
}
return elems;
}
std::string showVersions(const std::set<std::string> & versions)
{
if (versions.empty()) return "";
std::set<std::string> versions2;
for (auto & version : versions)
versions2.insert(version.empty() ? "ε" : version);
return concatStringsSep(", ", versions2);
}
bool ProfileElementSource::operator<(const ProfileElementSource & other) const
{
return std::tuple(originalRef.to_string(), attrPath, outputs)
< std::tuple(other.originalRef.to_string(), other.attrPath, other.outputs);
}
std::string ProfileElementSource::to_string() const
{
return fmt("%s#%s%s", originalRef, attrPath, outputs.to_string());
}
std::string ProfileElement::identifier() const
{
if (source) {
return source->to_string();
}
StringSet names;
for (auto & path : storePaths) {
names.insert(DrvName(path.name()).name);
}
return concatStringsSep(", ", names);
}
std::set<std::string> ProfileElement::toInstallables(Store & store)
{
if (source) {
return {source->to_string()};
}
StringSet rawPaths;
for (auto & path : storePaths) {
rawPaths.insert(store.printStorePath(path));
}
return rawPaths;
}
std::string ProfileElement::versions() const
{
StringSet versions;
for (auto & path : storePaths) {
versions.insert(DrvName(path.name()).version);
}
return showVersions(versions);
}
bool ProfileElement::operator<(const ProfileElement & other) const
{
return std::tuple(identifier(), storePaths) < std::tuple(other.identifier(), other.storePaths);
}
void ProfileElement::updateStorePaths(
ref<Store> evalStore, ref<Store> store, const BuiltPaths & builtPaths
)
{
storePaths.clear();
for (auto & buildable : builtPaths) {
std::visit(
overloaded{
[&](const BuiltPath::Opaque & bo) { storePaths.insert(bo.path); },
[&](const BuiltPath::Built & bfd) {
for (auto & output : bfd.outputs) {
storePaths.insert(output.second);
}
},
},
buildable.raw()
);
}
}
}

View file

@ -0,0 +1,56 @@
#pragma once
///@file
#include "built-path.hh"
#include "eval.hh"
#include "flake/flakeref.hh"
#include "get-drvs.hh"
#include "types.hh"
#include <string>
#include <set>
namespace nix
{
struct ProfileElementSource
{
FlakeRef originalRef;
// FIXME: record original attrpath.
FlakeRef lockedRef;
std::string attrPath;
ExtendedOutputsSpec outputs;
bool operator<(const ProfileElementSource & other) const;
std::string to_string() const;
};
constexpr int DEFAULT_PRIORITY = 5;
struct ProfileElement
{
StorePathSet storePaths;
std::optional<ProfileElementSource> source;
bool active = true;
int priority = DEFAULT_PRIORITY;
std::string identifier() const;
/**
* Return a string representing an installable corresponding to the current
* element, either a flakeref or a plain store path
*/
std::set<std::string> toInstallables(Store & store);
std::string versions() const;
bool operator<(const ProfileElement & other) const;
void updateStorePaths(ref<Store> evalStore, ref<Store> store, const BuiltPaths & builtPaths);
};
DrvInfos queryInstalled(EvalState & state, const Path & userEnv);
std::string showVersions(const std::set<std::string> & versions);
}

View file

@ -342,8 +342,6 @@ void completeFlakeRefWithFragment(
const Strings & defaultFlakeAttrPaths,
std::string_view prefix);
std::string showVersions(const std::set<std::string> & versions);
void printClosureDiff(
ref<Store> store,
const StorePath & beforePath,

View file

@ -1,6 +1,7 @@
libcmd_sources = files(
'built-path.cc',
'command-installable-value.cc',
'cmd-profiles.cc',
'command.cc',
'common-eval-args.cc',
'editor-for.cc',
@ -18,6 +19,7 @@ libcmd_sources = files(
libcmd_headers = files(
'built-path.hh',
'command-installable-value.hh',
'cmd-profiles.hh',
'command.hh',
'common-eval-args.hh',
'editor-for.hh',

View file

@ -16,22 +16,6 @@
namespace nix {
DrvInfos queryInstalled(EvalState & state, const Path & userEnv)
{
DrvInfos elems;
if (pathExists(userEnv + "/manifest.json"))
throw Error("profile '%s' is incompatible with 'nix-env'; please use 'nix profile' instead", userEnv);
auto manifestFile = userEnv + "/manifest.nix";
if (pathExists(manifestFile)) {
Value v;
state.evalFile(state.rootPath(CanonPath(manifestFile)), v);
Bindings & bindings(*state.allocBindings(0));
getDerivations(state, v, "", bindings, elems, false);
}
return elems;
}
bool createUserEnv(EvalState & state, DrvInfos & elems,
const Path & profile, bool keepDerivations,
const std::string & lockToken)

View file

@ -1,4 +1,5 @@
#include "command.hh"
#include "cmd-profiles.hh"
#include "shared.hh"
#include "store-api.hh"
#include "common-args.hh"
@ -43,15 +44,6 @@ GroupedPaths getClosureInfo(ref<Store> store, const StorePath & toplevel)
return groupedPaths;
}
std::string showVersions(const std::set<std::string> & versions)
{
if (versions.empty()) return "";
std::set<std::string> versions2;
for (auto & version : versions)
versions2.insert(version.empty() ? "ε" : version);
return concatStringsSep(", ", versions2);
}
void printClosureDiff(
ref<Store> store,
const StorePath & beforePath,

View file

@ -1,4 +1,5 @@
#include "command.hh"
#include "cmd-profiles.hh"
#include "installable-flake.hh"
#include "common-args.hh"
#include "shared.hh"
@ -17,92 +18,6 @@
using namespace nix;
struct ProfileElementSource
{
FlakeRef originalRef;
// FIXME: record original attrpath.
FlakeRef lockedRef;
std::string attrPath;
ExtendedOutputsSpec outputs;
bool operator < (const ProfileElementSource & other) const
{
return
std::tuple(originalRef.to_string(), attrPath, outputs) <
std::tuple(other.originalRef.to_string(), other.attrPath, other.outputs);
}
std::string to_string() const
{
return fmt("%s#%s%s", originalRef, attrPath, outputs.to_string());
}
};
const int defaultPriority = 5;
struct ProfileElement
{
StorePathSet storePaths;
std::optional<ProfileElementSource> source;
bool active = true;
int priority = defaultPriority;
std::string identifier() const
{
if (source)
return source->to_string();
StringSet names;
for (auto & path : storePaths)
names.insert(DrvName(path.name()).name);
return concatStringsSep(", ", names);
}
/**
* Return a string representing an installable corresponding to the current
* element, either a flakeref or a plain store path
*/
std::set<std::string> toInstallables(Store & store)
{
if (source)
return {source->to_string()};
StringSet rawPaths;
for (auto & path : storePaths)
rawPaths.insert(store.printStorePath(path));
return rawPaths;
}
std::string versions() const
{
StringSet versions;
for (auto & path : storePaths)
versions.insert(DrvName(path.name()).version);
return showVersions(versions);
}
bool operator < (const ProfileElement & other) const
{
return std::tuple(identifier(), storePaths) < std::tuple(other.identifier(), other.storePaths);
}
void updateStorePaths(
ref<Store> evalStore,
ref<Store> store,
const BuiltPaths & builtPaths)
{
storePaths.clear();
for (auto & buildable : builtPaths) {
std::visit(overloaded {
[&](const BuiltPath::Opaque & bo) {
storePaths.insert(bo.path);
},
[&](const BuiltPath::Built & bfd) {
for (auto & output : bfd.outputs)
storePaths.insert(output.second);
},
}, buildable.raw());
}
}
};
struct ProfileManifest
{
@ -361,8 +276,8 @@ struct CmdProfileInstall : InstallablesCommand, MixDefaultProfile
: ({
auto * info2 = dynamic_cast<ExtraPathInfoValue *>(&*info);
info2
? info2->value.priority.value_or(defaultPriority)
: defaultPriority;
? info2->value.priority.value_or(DEFAULT_PRIORITY)
: DEFAULT_PRIORITY;
});
element.updateStorePaths(getEvalStore(), store, res);