forked from lix-project/lix
refactor profile code from nix-env and nix profile to libcmd
Change-Id: I94e948ff6b92af7d0ab53b17c1570b7549c3b4e0
This commit is contained in:
parent
78ce710722
commit
abb6ff3111
284
src/libcmd/cmd-profiles.cc
Normal file
284
src/libcmd/cmd-profiles.cc
Normal file
|
@ -0,0 +1,284 @@
|
|||
#include "cmd-profiles.hh"
|
||||
#include "builtins/buildenv.hh"
|
||||
#include "command.hh"
|
||||
#include "names.hh"
|
||||
#include "store-api.hh"
|
||||
#include "../nix-env/user-env.hh"
|
||||
|
||||
#include <tuple>
|
||||
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
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 ProfileElementSource::operator<(ProfileElementSource const & rhs) const
|
||||
{
|
||||
auto const lhsTuple = std::tuple(originalRef.to_string(), attrPath, outputs);
|
||||
auto const rhsTuple = std::tuple(rhs.originalRef.to_string(), rhs.attrPath, rhs.outputs);
|
||||
|
||||
return lhsTuple < rhsTuple;
|
||||
}
|
||||
|
||||
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<(ProfileElement const & rhs) const
|
||||
{
|
||||
auto const lhsTuple = std::tuple(identifier(), storePaths);
|
||||
auto const rhsTuple = std::tuple(rhs.identifier(), rhs.storePaths);
|
||||
return lhsTuple < rhsTuple;
|
||||
}
|
||||
|
||||
void ProfileElement::updateStorePaths(
|
||||
ref<Store> evalStore,
|
||||
ref<Store> store,
|
||||
BuiltPaths const & builtPaths
|
||||
)
|
||||
{
|
||||
storePaths.clear();
|
||||
|
||||
for (auto & buildable : builtPaths) {
|
||||
std::visit(overloaded {
|
||||
[&](BuiltPath::Opaque const & bo) {
|
||||
storePaths.insert(bo.path);
|
||||
},
|
||||
[&](BuiltPath::Built const & bfd) {
|
||||
for (auto & output : bfd.outputs) {
|
||||
storePaths.insert(output.second);
|
||||
}
|
||||
},
|
||||
}, buildable.raw());
|
||||
}
|
||||
}
|
||||
|
||||
ProfileManifest::ProfileManifest(EvalState & state, Path const & profile)
|
||||
{
|
||||
auto manifestPath = profile + "/manifest.nix";
|
||||
|
||||
if (pathExists(manifestPath)) {
|
||||
auto json = nlohmann::json::parse(readFile(manifestPath));
|
||||
|
||||
auto version = json.value("version", 0);
|
||||
std::string sUrl;
|
||||
std::string sOriginalUrl;
|
||||
switch (version) {
|
||||
case 1:
|
||||
sUrl = "uri";
|
||||
sOriginalUrl = "originalUri";
|
||||
break;
|
||||
case 2:
|
||||
sUrl = "url";
|
||||
sOriginalUrl = "originalUrl";
|
||||
break;
|
||||
default:
|
||||
throw Error("profile manifest '%s' has unsupported version %d", manifestPath, version);
|
||||
}
|
||||
|
||||
for (auto & e : json["elements"]) {
|
||||
ProfileElement element;
|
||||
for (auto & p : e["storePaths"])
|
||||
element.storePaths.insert(state.store->parseStorePath((std::string) p));
|
||||
element.active = e["active"];
|
||||
if(e.contains("priority")) {
|
||||
element.priority = e["priority"];
|
||||
}
|
||||
if (e.value(sUrl, "") != "") {
|
||||
element.source = ProfileElementSource {
|
||||
parseFlakeRef(e[sOriginalUrl]),
|
||||
parseFlakeRef(e[sUrl]),
|
||||
e["attrPath"],
|
||||
e["outputs"].get<ExtendedOutputsSpec>()
|
||||
};
|
||||
}
|
||||
elements.emplace_back(std::move(element));
|
||||
}
|
||||
} else if (pathExists(profile + "/manifest.nix")) {
|
||||
printTalkative("found nix-env profile at %s", profile);
|
||||
// FIXME: needed because of pure mode; ugly.
|
||||
state.allowPath(state.store->followLinksToStore(profile));
|
||||
state.allowPath(state.store->followLinksToStore(profile + "/manifest.nix"));
|
||||
|
||||
auto drvInfos = queryInstalled(state, state.store->followLinksToStore(profile));
|
||||
|
||||
for (auto & drvInfo : drvInfos) {
|
||||
ProfileElement element;
|
||||
element.storePaths = {drvInfo.queryOutPath()};
|
||||
elements.emplace_back(std::move(element));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
nlohmann::json ProfileManifest::toJSON(Store & store) const
|
||||
{
|
||||
auto array = nlohmann::json::array();
|
||||
for (auto & element : elements) {
|
||||
auto paths = nlohmann::json::array();
|
||||
for (auto & path : element.storePaths)
|
||||
paths.push_back(store.printStorePath(path));
|
||||
nlohmann::json obj;
|
||||
obj["storePaths"] = paths;
|
||||
obj["active"] = element.active;
|
||||
obj["priority"] = element.priority;
|
||||
if (element.source) {
|
||||
obj["originalUrl"] = element.source->originalRef.to_string();
|
||||
obj["url"] = element.source->lockedRef.to_string();
|
||||
obj["attrPath"] = element.source->attrPath;
|
||||
obj["outputs"] = element.source->outputs;
|
||||
}
|
||||
array.push_back(obj);
|
||||
}
|
||||
nlohmann::json json;
|
||||
json["version"] = 2;
|
||||
json["elements"] = array;
|
||||
return json;
|
||||
}
|
||||
|
||||
StorePath ProfileManifest::build(ref<Store> store)
|
||||
{
|
||||
auto tempDir = createTempDir();
|
||||
|
||||
StorePathSet references;
|
||||
|
||||
Packages pkgs;
|
||||
for (auto & element : elements) {
|
||||
for (auto & path : element.storePaths) {
|
||||
if (element.active) {
|
||||
pkgs.emplace_back(store->printStorePath(path), true, element.priority);
|
||||
}
|
||||
references.insert(path);
|
||||
}
|
||||
}
|
||||
|
||||
buildProfile(tempDir, std::move(pkgs));
|
||||
|
||||
writeFile(tempDir + "/manifest.json", toJSON(*store).dump());
|
||||
|
||||
/* Add the symlink tree to the store. */
|
||||
StringSink sink;
|
||||
dumpPath(tempDir, sink);
|
||||
|
||||
auto narHash = hashString(htSHA256, sink.s);
|
||||
|
||||
ValidPathInfo info {
|
||||
*store,
|
||||
"profile",
|
||||
FixedOutputInfo {
|
||||
.method = FileIngestionMethod::Recursive,
|
||||
.hash = narHash,
|
||||
.references = {
|
||||
.others = std::move(references),
|
||||
// profiles never refer to themselves
|
||||
.self = false,
|
||||
},
|
||||
},
|
||||
narHash,
|
||||
};
|
||||
info.narSize = sink.s.size();
|
||||
|
||||
StringSource source(sink.s);
|
||||
store->addToStore(info, source);
|
||||
|
||||
return std::move(info.path);
|
||||
}
|
||||
|
||||
void ProfileManifest::printDiff(
|
||||
ProfileManifest const & prev,
|
||||
ProfileManifest const & cur,
|
||||
std::string_view indent
|
||||
)
|
||||
{
|
||||
auto prevElems = prev.elements;
|
||||
std::sort(prevElems.begin(), prevElems.end());
|
||||
|
||||
auto curElems = cur.elements;
|
||||
std::sort(curElems.begin(), curElems.end());
|
||||
|
||||
auto i = prevElems.begin();
|
||||
auto j = curElems.begin();
|
||||
|
||||
bool changes = false;
|
||||
|
||||
while (i != prevElems.end() || j != curElems.end()) {
|
||||
if (j != curElems.end() && (i == prevElems.end() || i->identifier() > j->identifier())) {
|
||||
logger->cout("%s%s: ∅ -> %s", indent, j->identifier(), j->versions());
|
||||
changes = true;
|
||||
++j;
|
||||
}
|
||||
else if (i != prevElems.end() && (j == curElems.end() || i->identifier() < j->identifier())) {
|
||||
logger->cout("%s%s: %s -> ∅", indent, i->identifier(), i->versions());
|
||||
changes = true;
|
||||
++i;
|
||||
}
|
||||
else {
|
||||
auto v1 = i->versions();
|
||||
auto v2 = j->versions();
|
||||
if (v1 != v2) {
|
||||
logger->cout("%s%s: %s -> %s", indent, i->identifier(), v1, v2);
|
||||
changes = true;
|
||||
}
|
||||
++i;
|
||||
++j;
|
||||
}
|
||||
}
|
||||
|
||||
if (!changes)
|
||||
logger->cout("%sNo changes.", indent);
|
||||
}
|
||||
|
||||
}
|
78
src/libcmd/cmd-profiles.hh
Normal file
78
src/libcmd/cmd-profiles.hh
Normal file
|
@ -0,0 +1,78 @@
|
|||
#pragma once
|
||||
///@file
|
||||
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <optional>
|
||||
|
||||
#include "built-path.hh"
|
||||
#include "eval.hh"
|
||||
#include "flake/flakeref.hh"
|
||||
#include "get-drvs.hh"
|
||||
#include "path.hh"
|
||||
#include "types.hh"
|
||||
|
||||
namespace nix
|
||||
{
|
||||
|
||||
constexpr int DEFAULT_PRIORITY = 5;
|
||||
|
||||
DrvInfos queryInstalled(EvalState & state, const Path & userEnv);
|
||||
|
||||
struct ProfileElementSource
|
||||
{
|
||||
FlakeRef originalRef;
|
||||
// FIXME: record original attrpath.
|
||||
FlakeRef lockedRef;
|
||||
std::string attrPath;
|
||||
ExtendedOutputsSpec outputs;
|
||||
|
||||
bool operator<(ProfileElementSource const & rhs) const;
|
||||
|
||||
std::string to_string() const;
|
||||
};
|
||||
|
||||
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<(ProfileElement const & rhs) const;
|
||||
|
||||
void updateStorePaths(
|
||||
ref<Store> evalStore,
|
||||
ref<Store> store,
|
||||
BuiltPaths const & builtPaths
|
||||
);
|
||||
};
|
||||
|
||||
struct ProfileManifest
|
||||
{
|
||||
std::vector<ProfileElement> elements;
|
||||
|
||||
ProfileManifest()
|
||||
: elements({})
|
||||
{ }
|
||||
|
||||
ProfileManifest(EvalState & state, Path const & profile);
|
||||
|
||||
nlohmann::json toJSON(Store & store) const;
|
||||
|
||||
StorePath build(ref<Store> store);
|
||||
|
||||
static void printDiff(ProfileManifest const & prev, ProfileManifest const & cur, std::string_view indent);
|
||||
};
|
||||
|
||||
}
|
|
@ -319,4 +319,13 @@ void MixEnvironment::setEnviron() {
|
|||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
libcmd_sources = files(
|
||||
'built-path.cc',
|
||||
'cmd-profiles.cc',
|
||||
'command-installable-value.cc',
|
||||
'command.cc',
|
||||
'common-eval-args.cc',
|
||||
|
@ -17,6 +18,7 @@ libcmd_sources = files(
|
|||
|
||||
libcmd_headers = files(
|
||||
'built-path.hh',
|
||||
'cmd-profiles.hh',
|
||||
'command-installable-value.hh',
|
||||
'command.hh',
|
||||
'common-eval-args.hh',
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
#include "attr-path.hh"
|
||||
#include "cmd-profiles.hh"
|
||||
#include "common-eval-args.hh"
|
||||
#include "derivations.hh"
|
||||
#include "eval.hh"
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -5,8 +5,6 @@
|
|||
|
||||
namespace nix {
|
||||
|
||||
DrvInfos queryInstalled(EvalState & state, const Path & userEnv);
|
||||
|
||||
bool createUserEnv(EvalState & state, DrvInfos & elems,
|
||||
const Path & profile, bool keepDerivations,
|
||||
const std::string & lockToken);
|
||||
|
|
|
@ -43,15 +43,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,
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
#include "flake/flakeref.hh"
|
||||
#include "../nix-env/user-env.hh"
|
||||
#include "profiles.hh"
|
||||
#include "cmd-profiles.hh"
|
||||
#include "names.hh"
|
||||
|
||||
#include <nlohmann/json.hpp>
|
||||
|
@ -17,270 +18,8 @@
|
|||
|
||||
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
|
||||
{
|
||||
std::vector<ProfileElement> elements;
|
||||
|
||||
ProfileManifest() { }
|
||||
|
||||
ProfileManifest(EvalState & state, const Path & profile)
|
||||
{
|
||||
auto manifestPath = profile + "/manifest.json";
|
||||
|
||||
if (pathExists(manifestPath)) {
|
||||
auto json = nlohmann::json::parse(readFile(manifestPath));
|
||||
|
||||
auto version = json.value("version", 0);
|
||||
std::string sUrl;
|
||||
std::string sOriginalUrl;
|
||||
switch (version) {
|
||||
case 1:
|
||||
sUrl = "uri";
|
||||
sOriginalUrl = "originalUri";
|
||||
break;
|
||||
case 2:
|
||||
sUrl = "url";
|
||||
sOriginalUrl = "originalUrl";
|
||||
break;
|
||||
default:
|
||||
throw Error("profile manifest '%s' has unsupported version %d", manifestPath, version);
|
||||
}
|
||||
|
||||
for (auto & e : json["elements"]) {
|
||||
ProfileElement element;
|
||||
for (auto & p : e["storePaths"])
|
||||
element.storePaths.insert(state.store->parseStorePath((std::string) p));
|
||||
element.active = e["active"];
|
||||
if(e.contains("priority")) {
|
||||
element.priority = e["priority"];
|
||||
}
|
||||
if (e.value(sUrl, "") != "") {
|
||||
element.source = ProfileElementSource {
|
||||
parseFlakeRef(e[sOriginalUrl]),
|
||||
parseFlakeRef(e[sUrl]),
|
||||
e["attrPath"],
|
||||
e["outputs"].get<ExtendedOutputsSpec>()
|
||||
};
|
||||
}
|
||||
elements.emplace_back(std::move(element));
|
||||
}
|
||||
}
|
||||
|
||||
else if (pathExists(profile + "/manifest.nix")) {
|
||||
// FIXME: needed because of pure mode; ugly.
|
||||
state.allowPath(state.store->followLinksToStore(profile));
|
||||
state.allowPath(state.store->followLinksToStore(profile + "/manifest.nix"));
|
||||
|
||||
auto drvInfos = queryInstalled(state, state.store->followLinksToStore(profile));
|
||||
|
||||
for (auto & drvInfo : drvInfos) {
|
||||
ProfileElement element;
|
||||
element.storePaths = {drvInfo.queryOutPath()};
|
||||
elements.emplace_back(std::move(element));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
nlohmann::json toJSON(Store & store) const
|
||||
{
|
||||
auto array = nlohmann::json::array();
|
||||
for (auto & element : elements) {
|
||||
auto paths = nlohmann::json::array();
|
||||
for (auto & path : element.storePaths)
|
||||
paths.push_back(store.printStorePath(path));
|
||||
nlohmann::json obj;
|
||||
obj["storePaths"] = paths;
|
||||
obj["active"] = element.active;
|
||||
obj["priority"] = element.priority;
|
||||
if (element.source) {
|
||||
obj["originalUrl"] = element.source->originalRef.to_string();
|
||||
obj["url"] = element.source->lockedRef.to_string();
|
||||
obj["attrPath"] = element.source->attrPath;
|
||||
obj["outputs"] = element.source->outputs;
|
||||
}
|
||||
array.push_back(obj);
|
||||
}
|
||||
nlohmann::json json;
|
||||
json["version"] = 2;
|
||||
json["elements"] = array;
|
||||
return json;
|
||||
}
|
||||
|
||||
StorePath build(ref<Store> store)
|
||||
{
|
||||
auto tempDir = createTempDir();
|
||||
|
||||
StorePathSet references;
|
||||
|
||||
Packages pkgs;
|
||||
for (auto & element : elements) {
|
||||
for (auto & path : element.storePaths) {
|
||||
if (element.active)
|
||||
pkgs.emplace_back(store->printStorePath(path), true, element.priority);
|
||||
references.insert(path);
|
||||
}
|
||||
}
|
||||
|
||||
buildProfile(tempDir, std::move(pkgs));
|
||||
|
||||
writeFile(tempDir + "/manifest.json", toJSON(*store).dump());
|
||||
|
||||
/* Add the symlink tree to the store. */
|
||||
StringSink sink;
|
||||
dumpPath(tempDir, sink);
|
||||
|
||||
auto narHash = hashString(htSHA256, sink.s);
|
||||
|
||||
ValidPathInfo info {
|
||||
*store,
|
||||
"profile",
|
||||
FixedOutputInfo {
|
||||
.method = FileIngestionMethod::Recursive,
|
||||
.hash = narHash,
|
||||
.references = {
|
||||
.others = std::move(references),
|
||||
// profiles never refer to themselves
|
||||
.self = false,
|
||||
},
|
||||
},
|
||||
narHash,
|
||||
};
|
||||
info.narSize = sink.s.size();
|
||||
|
||||
StringSource source(sink.s);
|
||||
store->addToStore(info, source);
|
||||
|
||||
return std::move(info.path);
|
||||
}
|
||||
|
||||
static void printDiff(const ProfileManifest & prev, const ProfileManifest & cur, std::string_view indent)
|
||||
{
|
||||
auto prevElems = prev.elements;
|
||||
std::sort(prevElems.begin(), prevElems.end());
|
||||
|
||||
auto curElems = cur.elements;
|
||||
std::sort(curElems.begin(), curElems.end());
|
||||
|
||||
auto i = prevElems.begin();
|
||||
auto j = curElems.begin();
|
||||
|
||||
bool changes = false;
|
||||
|
||||
while (i != prevElems.end() || j != curElems.end()) {
|
||||
if (j != curElems.end() && (i == prevElems.end() || i->identifier() > j->identifier())) {
|
||||
logger->cout("%s%s: ∅ -> %s", indent, j->identifier(), j->versions());
|
||||
changes = true;
|
||||
++j;
|
||||
}
|
||||
else if (i != prevElems.end() && (j == curElems.end() || i->identifier() < j->identifier())) {
|
||||
logger->cout("%s%s: %s -> ∅", indent, i->identifier(), i->versions());
|
||||
changes = true;
|
||||
++i;
|
||||
}
|
||||
else {
|
||||
auto v1 = i->versions();
|
||||
auto v2 = j->versions();
|
||||
if (v1 != v2) {
|
||||
logger->cout("%s%s: %s -> %s", indent, i->identifier(), v1, v2);
|
||||
changes = true;
|
||||
}
|
||||
++i;
|
||||
++j;
|
||||
}
|
||||
}
|
||||
|
||||
if (!changes)
|
||||
logger->cout("%sNo changes.", indent);
|
||||
}
|
||||
};
|
||||
|
||||
static std::map<Installable *, std::pair<BuiltPaths, ref<ExtraPathInfo>>>
|
||||
builtPathsPerInstallable(
|
||||
const std::vector<std::pair<ref<Installable>, BuiltPathWithResult>> & builtPaths)
|
||||
|
|
Loading…
Reference in a new issue