diff options
Diffstat (limited to 'src/nix/profile.cc')
-rw-r--r-- | src/nix/profile.cc | 287 |
1 files changed, 199 insertions, 88 deletions
diff --git a/src/nix/profile.cc b/src/nix/profile.cc index 7dcc0b6d4..667904cd2 100644 --- a/src/nix/profile.cc +++ b/src/nix/profile.cc @@ -8,6 +8,7 @@ #include "flake/flakeref.hh" #include "../nix-env/user-env.hh" #include "profiles.hh" +#include "names.hh" #include <nlohmann/json.hpp> #include <regex> @@ -21,6 +22,13 @@ struct ProfileElementSource FlakeRef resolvedRef; std::string attrPath; // FIXME: output names + + bool operator < (const ProfileElementSource & other) const + { + return + std::pair(originalRef.to_string(), attrPath) < + std::pair(other.originalRef.to_string(), other.attrPath); + } }; struct ProfileElement @@ -29,6 +37,29 @@ struct ProfileElement std::optional<ProfileElementSource> source; bool active = true; // FIXME: priority + + std::string describe() const + { + if (source) + return fmt("%s#%s", source->originalRef, source->attrPath); + StringSet names; + for (auto & path : storePaths) + names.insert(DrvName(path.name()).name); + return concatStringsSep(", ", names); + } + + 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(describe(), storePaths) < std::tuple(other.describe(), other.storePaths); + } }; struct ProfileManifest @@ -129,17 +160,59 @@ struct ProfileManifest auto narHash = hashString(htSHA256, *sink.s); - ValidPathInfo info(store->makeFixedOutputPath(FileIngestionMethod::Recursive, narHash, "profile", references)); + ValidPathInfo info { + store->makeFixedOutputPath(FileIngestionMethod::Recursive, narHash, "profile", references), + narHash, + }; info.references = std::move(references); - info.narHash = narHash; info.narSize = sink.s->size(); - info.ca = FixedOutputHash { .method = FileIngestionMethod::Recursive, .hash = *info.narHash }; + info.ca = FixedOutputHash { .method = FileIngestionMethod::Recursive, .hash = info.narHash }; auto source = StringSource { *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->describe() > j->describe())) { + std::cout << fmt("%s%s: ∅ -> %s\n", indent, j->describe(), j->versions()); + changes = true; + ++j; + } + else if (i != prevElems.end() && (j == curElems.end() || i->describe() < j->describe())) { + std::cout << fmt("%s%s: %s -> ∅\n", indent, i->describe(), i->versions()); + changes = true; + ++i; + } + else { + auto v1 = i->versions(); + auto v2 = j->versions(); + if (v1 != v2) { + std::cout << fmt("%s%s: %s -> %s\n", indent, i->describe(), v1, v2); + changes = true; + } + ++i; + ++j; + } + } + + if (!changes) + std::cout << fmt("%sNo changes.\n", indent); + } }; struct CmdProfileInstall : InstallablesCommand, MixDefaultProfile @@ -149,47 +222,61 @@ struct CmdProfileInstall : InstallablesCommand, MixDefaultProfile return "install a package into a profile"; } - Examples examples() override - { - return { - Example{ - "To install a package from Nixpkgs:", - "nix profile install nixpkgs#hello" - }, - Example{ - "To install a package from a specific branch of Nixpkgs:", - "nix profile install nixpkgs/release-19.09#hello" - }, - Example{ - "To install a package from a specific revision of Nixpkgs:", - "nix profile install nixpkgs/1028bb33859f8dfad7f98e1c8d185f3d1aaa7340#hello" - }, - }; + std::string doc() override + { + return + #include "profile-install.md" + ; } void run(ref<Store> store) override { ProfileManifest manifest(*getEvalState(), *profile); - std::vector<StorePathWithOutputs> pathsToBuild; + std::vector<DerivedPath> pathsToBuild; for (auto & installable : installables) { if (auto installable2 = std::dynamic_pointer_cast<InstallableFlake>(installable)) { auto [attrPath, resolvedRef, drv] = installable2->toDerivation(); ProfileElement element; - element.storePaths = {drv.outPath}; // FIXME + if (!drv.outPath) + throw UnimplementedError("CA derivations are not yet supported by 'nix profile'"); + element.storePaths = {*drv.outPath}; // FIXME element.source = ProfileElementSource{ installable2->flakeRef, resolvedRef, attrPath, }; - pathsToBuild.push_back({drv.drvPath, StringSet{"out"}}); // FIXME + pathsToBuild.push_back(DerivedPath::Built{drv.drvPath, StringSet{drv.outputName}}); manifest.elements.emplace_back(std::move(element)); - } else - throw Error("'nix profile install' does not support argument '%s'", installable->what()); + } else { + auto buildables = build(store, Realise::Outputs, {installable}, bmNormal); + + for (auto & buildable : buildables) { + ProfileElement element; + + std::visit(overloaded { + [&](DerivedPathWithHints::Opaque bo) { + pathsToBuild.push_back(bo); + element.storePaths.insert(bo.path); + }, + [&](DerivedPathWithHints::Built bfd) { + // TODO: Why are we querying if we know the output + // names already? Is it just to figure out what the + // default one is? + for (auto & output : store->queryDerivationOutputMap(bfd.drvPath)) { + pathsToBuild.push_back(DerivedPath::Built{bfd.drvPath, {output.first}}); + element.storePaths.insert(output.second); + } + }, + }, buildable.raw()); + + manifest.elements.emplace_back(std::move(element)); + } + } } store->buildPaths(pathsToBuild); @@ -216,9 +303,8 @@ public: std::vector<Matcher> res; for (auto & s : _matchers) { - size_t n; - if (string2Int(s, n)) - res.push_back(n); + if (auto n = string2Int<size_t>(s)) + res.push_back(*n); else if (store->isStorePath(s)) res.push_back(s); else @@ -253,26 +339,11 @@ struct CmdProfileRemove : virtual EvalCommand, MixDefaultProfile, MixProfileElem return "remove packages from a profile"; } - Examples examples() override - { - return { - Example{ - "To remove a package by attribute path:", - "nix profile remove packages.x86_64-linux.hello" - }, - Example{ - "To remove all packages:", - "nix profile remove '.*'" - }, - Example{ - "To remove a package by store path:", - "nix profile remove /nix/store/rr3y0c6zyk7kjjl8y19s4lsrhn4aiq1z-hello-2.10" - }, - Example{ - "To remove a package by position:", - "nix profile remove 3" - }, - }; + std::string doc() override + { + return + #include "profile-remove.md" + ; } void run(ref<Store> store) override @@ -306,18 +377,11 @@ struct CmdProfileUpgrade : virtual SourceExprCommand, MixDefaultProfile, MixProf return "upgrade packages using their most recent flake"; } - Examples examples() override + std::string doc() override { - return { - Example{ - "To upgrade all packages that were installed using a mutable flake reference:", - "nix profile upgrade '.*'" - }, - Example{ - "To upgrade a specific package:", - "nix profile upgrade packages.x86_64-linux.hello" - }, - }; + return + #include "profile-upgrade.md" + ; } void run(ref<Store> store) override @@ -327,7 +391,7 @@ struct CmdProfileUpgrade : virtual SourceExprCommand, MixDefaultProfile, MixProf auto matchers = getMatchers(store); // FIXME: code duplication - std::vector<StorePathWithOutputs> pathsToBuild; + std::vector<DerivedPath> pathsToBuild; for (size_t i = 0; i < manifest.elements.size(); ++i) { auto & element(manifest.elements[i]); @@ -338,7 +402,13 @@ struct CmdProfileUpgrade : virtual SourceExprCommand, MixDefaultProfile, MixProf Activity act(*logger, lvlChatty, actUnknown, fmt("checking '%s' for updates", element.source->attrPath)); - InstallableFlake installable(getEvalState(), FlakeRef(element.source->originalRef), {element.source->attrPath}, {}, lockFlags); + InstallableFlake installable( + this, + getEvalState(), + FlakeRef(element.source->originalRef), + {element.source->attrPath}, + {}, + lockFlags); auto [attrPath, resolvedRef, drv] = installable.toDerivation(); @@ -347,14 +417,16 @@ struct CmdProfileUpgrade : virtual SourceExprCommand, MixDefaultProfile, MixProf printInfo("upgrading '%s' from flake '%s' to '%s'", element.source->attrPath, element.source->resolvedRef, resolvedRef); - element.storePaths = {drv.outPath}; // FIXME + if (!drv.outPath) + throw UnimplementedError("CA derivations are not yet supported by 'nix profile'"); + element.storePaths = {*drv.outPath}; // FIXME element.source = ProfileElementSource{ installable.flakeRef, resolvedRef, attrPath, }; - pathsToBuild.push_back({drv.drvPath, StringSet{"out"}}); // FIXME + pathsToBuild.push_back(DerivedPath::Built{drv.drvPath, {"out"}}); // FIXME } } @@ -364,21 +436,18 @@ struct CmdProfileUpgrade : virtual SourceExprCommand, MixDefaultProfile, MixProf } }; -struct CmdProfileInfo : virtual EvalCommand, virtual StoreCommand, MixDefaultProfile +struct CmdProfileList : virtual EvalCommand, virtual StoreCommand, MixDefaultProfile { std::string description() override { return "list installed packages"; } - Examples examples() override + std::string doc() override { - return { - Example{ - "To show what packages are installed in the default profile:", - "nix profile info" - }, - }; + return + #include "profile-list.md" + ; } void run(ref<Store> store) override @@ -387,7 +456,7 @@ struct CmdProfileInfo : virtual EvalCommand, virtual StoreCommand, MixDefaultPro for (size_t i = 0; i < manifest.elements.size(); ++i) { auto & element(manifest.elements[i]); - logger->stdout("%d %s %s %s", i, + logger->cout("%d %s %s %s", i, element.source ? element.source->originalRef.to_string() + "#" + element.source->attrPath : "-", element.source ? element.source->resolvedRef.to_string() + "#" + element.source->attrPath : "-", concatStringsSep(" ", store->printStorePathSet(element.storePaths))); @@ -399,17 +468,14 @@ struct CmdProfileDiffClosures : virtual StoreCommand, MixDefaultProfile { std::string description() override { - return "show the closure difference between each generation of a profile"; + return "show the closure difference between each version of a profile"; } - Examples examples() override + std::string doc() override { - return { - Example{ - "To show what changed between each generation of the NixOS system profile:", - "nix profile diff-closure --profile /nix/var/nix/profiles/system" - }, - }; + return + #include "profile-diff-closures.md" + ; } void run(ref<Store> store) override @@ -423,7 +489,7 @@ struct CmdProfileDiffClosures : virtual StoreCommand, MixDefaultProfile if (prevGen) { if (!first) std::cout << "\n"; first = false; - std::cout << fmt("Generation %d -> %d:\n", prevGen->number, gen.number); + std::cout << fmt("Version %d -> %d:\n", prevGen->number, gen.number); printClosureDiff(store, store->followLinksToStorePath(prevGen->path), store->followLinksToStorePath(gen.path), @@ -435,15 +501,58 @@ struct CmdProfileDiffClosures : virtual StoreCommand, MixDefaultProfile } }; -struct CmdProfile : virtual MultiCommand, virtual Command +struct CmdProfileHistory : virtual StoreCommand, EvalCommand, MixDefaultProfile +{ + std::string description() override + { + return "show all versions of a profile"; + } + + std::string doc() override + { + return + #include "profile-history.md" + ; + } + + void run(ref<Store> store) override + { + auto [gens, curGen] = findGenerations(*profile); + + std::optional<std::pair<Generation, ProfileManifest>> prevGen; + bool first = true; + + for (auto & gen : gens) { + ProfileManifest manifest(*getEvalState(), gen.path); + + if (!first) std::cout << "\n"; + first = false; + + if (prevGen) + std::cout << fmt("Version %d -> %d:\n", prevGen->first.number, gen.number); + else + std::cout << fmt("Version %d:\n", gen.number); + + ProfileManifest::printDiff( + prevGen ? prevGen->second : ProfileManifest(), + manifest, + " "); + + prevGen = {gen, std::move(manifest)}; + } + } +}; + +struct CmdProfile : NixMultiCommand { CmdProfile() : MultiCommand({ {"install", []() { return make_ref<CmdProfileInstall>(); }}, {"remove", []() { return make_ref<CmdProfileRemove>(); }}, {"upgrade", []() { return make_ref<CmdProfileUpgrade>(); }}, - {"info", []() { return make_ref<CmdProfileInfo>(); }}, + {"list", []() { return make_ref<CmdProfileList>(); }}, {"diff-closures", []() { return make_ref<CmdProfileDiffClosures>(); }}, + {"history", []() { return make_ref<CmdProfileHistory>(); }}, }) { } @@ -452,6 +561,13 @@ struct CmdProfile : virtual MultiCommand, virtual Command return "manage Nix profiles"; } + std::string doc() override + { + return + #include "profile.md" + ; + } + void run() override { if (!command) @@ -459,11 +575,6 @@ struct CmdProfile : virtual MultiCommand, virtual Command command->second->prepare(); command->second->run(); } - - void printHelp(const string & programName, std::ostream & out) override - { - MultiCommand::printHelp(programName, out); - } }; -static auto r1 = registerCommand<CmdProfile>("profile"); +static auto rCmdProfile = registerCommand<CmdProfile>("profile"); |