From aefc6c4f41bfac0c76807c234fd0a786dd40f140 Mon Sep 17 00:00:00 2001 From: Eli Kogan-Wang Date: Wed, 11 May 2022 12:15:08 +0200 Subject: [PATCH 1/6] Add priority for nix profile install --- src/libstore/builtins/buildenv.cc | 3 ++- src/nix/profile.cc | 29 +++++++++++++++++++++++++++-- tests/nix-profile.sh | 16 ++++++++++++++++ 3 files changed, 45 insertions(+), 3 deletions(-) diff --git a/src/libstore/builtins/buildenv.cc b/src/libstore/builtins/buildenv.cc index 6f6ad57cb..c17c76e71 100644 --- a/src/libstore/builtins/buildenv.cc +++ b/src/libstore/builtins/buildenv.cc @@ -93,8 +93,9 @@ static void createLinks(State & state, const Path & srcDir, const Path & dstDir, auto prevPriority = state.priorities[dstFile]; if (prevPriority == priority) throw Error( - "packages '%1%' and '%2%' have the same priority %3%; " + "files '%1%' and '%2%' have the same priority %3%; " "use 'nix-env --set-flag priority NUMBER INSTALLED_PKGNAME' " + "or 'nix profile --priority NUMBER INSTALLED_PKGNAME' " "to change the priority of one of the conflicting packages" " (0 being the highest priority)", srcFile, readLink(dstFile), priority); diff --git a/src/nix/profile.cc b/src/nix/profile.cc index 685776bec..fb8bef670 100644 --- a/src/nix/profile.cc +++ b/src/nix/profile.cc @@ -37,7 +37,7 @@ struct ProfileElement StorePathSet storePaths; std::optional source; bool active = true; - // FIXME: priority + int priority = 5; std::string describe() const { @@ -116,6 +116,9 @@ struct ProfileManifest 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]), @@ -153,6 +156,7 @@ struct ProfileManifest 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->resolvedRef.to_string(); @@ -177,7 +181,7 @@ struct ProfileManifest for (auto & element : elements) { for (auto & path : element.storePaths) { if (element.active) - pkgs.emplace_back(store->printStorePath(path), true, 5); + pkgs.emplace_back(store->printStorePath(path), true, element.priority); references.insert(path); } } @@ -259,6 +263,23 @@ builtPathsPerInstallable( struct CmdProfileInstall : InstallablesCommand, MixDefaultProfile { + std::optional priority; + CmdProfileInstall() { + addFlag({ + .longName = "priority", + .description = "The priority of the package to install.", + .labels = {"priority"}, + .handler = {[&](std::string s) { + try{ + priority = std::stoi(s); + } catch (std::invalid_argument & e) { + throw ParseError("invalid priority '%s'", s); + } + }}, + // .completer = // no completer since number + }); + }; + std::string description() override { return "install a package into a profile"; @@ -282,6 +303,10 @@ struct CmdProfileInstall : InstallablesCommand, MixDefaultProfile for (auto & installable : installables) { ProfileElement element; + if(priority) { + element.priority = *priority; + }; + if (auto installable2 = std::dynamic_pointer_cast(installable)) { // FIXME: make build() return this? auto [attrPath, resolvedRef, drv] = installable2->toDerivation(); diff --git a/tests/nix-profile.sh b/tests/nix-profile.sh index f8da3d929..1cc724483 100644 --- a/tests/nix-profile.sh +++ b/tests/nix-profile.sh @@ -120,3 +120,19 @@ nix profile install "$flake1Dir^man" (! [ -e $TEST_HOME/.nix-profile/bin/hello ]) [ -e $TEST_HOME/.nix-profile/share/man ] (! [ -e $TEST_HOME/.nix-profile/include ]) + +# test priority +nix profile remove 0 + +# Make another flake. +flake2Dir=$TEST_ROOT/flake2 +printf World > $flake1Dir/who +cp -r $flake1Dir $flake2Dir +printf World2 > $flake2Dir/who + +nix profile install $flake1Dir +[[ $($TEST_HOME/.nix-profile/bin/hello) = "Hello World" ]] +nix profile install $flake2Dir --priority 100 +[[ $($TEST_HOME/.nix-profile/bin/hello) = "Hello World" ]] +nix profile install $flake2Dir --priority 0 +[[ $($TEST_HOME/.nix-profile/bin/hello) = "Hello World2" ]] From be2b19041eeec53fba24f7c2494f3f700a4ec595 Mon Sep 17 00:00:00 2001 From: Eli Kogan-Wang Date: Fri, 13 May 2022 22:02:28 +0200 Subject: [PATCH 2/6] Integrate review changes --- src/libcmd/installables.cc | 11 ++++++++--- src/libcmd/installables.hh | 3 ++- src/libexpr/eval-cache.cc | 22 ++++++++++++++++++++++ src/libexpr/eval-cache.hh | 3 +++ src/libstore/builtins/buildenv.cc | 2 +- src/nix/profile.cc | 25 ++++++++++++------------- tests/nix-profile.sh | 2 ++ 7 files changed, 50 insertions(+), 18 deletions(-) diff --git a/src/libcmd/installables.cc b/src/libcmd/installables.cc index a94e60aca..3f6dfd592 100644 --- a/src/libcmd/installables.cc +++ b/src/libcmd/installables.cc @@ -609,7 +609,7 @@ InstallableFlake::InstallableFlake( throw UsageError("'--arg' and '--argstr' are incompatible with flakes"); } -std::tuple InstallableFlake::toDerivation() +std::tuple> InstallableFlake::toDerivation() { auto attr = getCursor(*state); @@ -621,11 +621,15 @@ std::tuple InstallableF auto drvPath = attr->forceDerivation(); std::set outputsToInstall; + std::optional priority; - if (auto aMeta = attr->maybeGetAttr(state->sMeta)) + if (auto aMeta = attr->maybeGetAttr(state->sMeta)) { if (auto aOutputsToInstall = aMeta->maybeGetAttr("outputsToInstall")) for (auto & s : aOutputsToInstall->getListOfStrings()) outputsToInstall.insert(s); + if (auto aPriority = aMeta->maybeGetAttr("priority")) + priority = aPriority->getInt(); + } if (outputsToInstall.empty() || std::get_if(&outputsSpec)) { outputsToInstall.clear(); @@ -643,9 +647,10 @@ std::tuple InstallableF auto drvInfo = DerivationInfo { .drvPath = std::move(drvPath), .outputsToInstall = std::move(outputsToInstall), + .priority = priority, }; - return {attrPath, getLockedFlake()->flake.lockedRef, std::move(drvInfo)}; + return {attrPath, getLockedFlake()->flake.lockedRef, std::move(drvInfo), priority}; } std::vector InstallableFlake::toDerivations() diff --git a/src/libcmd/installables.hh b/src/libcmd/installables.hh index 1a5a96153..d7b61f1b8 100644 --- a/src/libcmd/installables.hh +++ b/src/libcmd/installables.hh @@ -142,6 +142,7 @@ struct InstallableValue : Installable { StorePath drvPath; std::set outputsToInstall; + std::optional priority; }; virtual std::vector toDerivations() = 0; @@ -176,7 +177,7 @@ struct InstallableFlake : InstallableValue Value * getFlakeOutputs(EvalState & state, const flake::LockedFlake & lockedFlake); - std::tuple toDerivation(); + std::tuple> toDerivation(); std::vector toDerivations() override; diff --git a/src/libexpr/eval-cache.cc b/src/libexpr/eval-cache.cc index 0eb4bc79e..b4bce512b 100644 --- a/src/libexpr/eval-cache.cc +++ b/src/libexpr/eval-cache.cc @@ -621,6 +621,28 @@ bool AttrCursor::getBool() return v.boolean; } +NixInt AttrCursor::getInt() +{ + if (root->db) { + if (!cachedValue) + cachedValue = root->db->getAttr(getKey()); + if (cachedValue && !std::get_if(&cachedValue->second)) { + if (auto i = std::get_if(&cachedValue->second)) { + debug("using cached Integer attribute '%s'", getAttrPathStr()); + return *i; + } else + throw TypeError("'%s' is not an Integer", getAttrPathStr()); + } + } + + auto & v = forceValue(); + + if (v.type() != nInt) + throw TypeError("'%s' is not an Integer", getAttrPathStr()); + + return v.integer; +} + std::vector AttrCursor::getListOfStrings() { if (root->db) { diff --git a/src/libexpr/eval-cache.hh b/src/libexpr/eval-cache.hh index 636e293ad..105e9217b 100644 --- a/src/libexpr/eval-cache.hh +++ b/src/libexpr/eval-cache.hh @@ -63,6 +63,7 @@ typedef std::variant< misc_t, failed_t, bool, + NixInt, std::vector > AttrValue; @@ -116,6 +117,8 @@ public: bool getBool(); + NixInt getInt(); + std::vector getListOfStrings(); std::vector getAttrs(); diff --git a/src/libstore/builtins/buildenv.cc b/src/libstore/builtins/buildenv.cc index c17c76e71..58ef89a1c 100644 --- a/src/libstore/builtins/buildenv.cc +++ b/src/libstore/builtins/buildenv.cc @@ -95,7 +95,7 @@ static void createLinks(State & state, const Path & srcDir, const Path & dstDir, throw Error( "files '%1%' and '%2%' have the same priority %3%; " "use 'nix-env --set-flag priority NUMBER INSTALLED_PKGNAME' " - "or 'nix profile --priority NUMBER INSTALLED_PKGNAME' " + "or 'nix profile install --priority NUMBER INSTALLED_PKGNAME' " "to change the priority of one of the conflicting packages" " (0 being the highest priority)", srcFile, readLink(dstFile), priority); diff --git a/src/nix/profile.cc b/src/nix/profile.cc index fb8bef670..ca5041873 100644 --- a/src/nix/profile.cc +++ b/src/nix/profile.cc @@ -269,14 +269,7 @@ struct CmdProfileInstall : InstallablesCommand, MixDefaultProfile .longName = "priority", .description = "The priority of the package to install.", .labels = {"priority"}, - .handler = {[&](std::string s) { - try{ - priority = std::stoi(s); - } catch (std::invalid_argument & e) { - throw ParseError("invalid priority '%s'", s); - } - }}, - // .completer = // no completer since number + .handler = {&priority}, }); }; @@ -303,21 +296,27 @@ struct CmdProfileInstall : InstallablesCommand, MixDefaultProfile for (auto & installable : installables) { ProfileElement element; - if(priority) { - element.priority = *priority; - }; + if (auto installable2 = std::dynamic_pointer_cast(installable)) { // FIXME: make build() return this? - auto [attrPath, resolvedRef, drv] = installable2->toDerivation(); + auto [attrPath, resolvedRef, drv, priority] = installable2->toDerivation(); element.source = ProfileElementSource { installable2->flakeRef, resolvedRef, attrPath, installable2->outputsSpec }; + + if(drv.priority) { + element.priority = *drv.priority; + } } + if(priority) { // if --priority was specified we want to override the priority of the installable + element.priority = *priority; + }; + element.updateStorePaths(getEvalStore(), store, builtPaths[installable.get()]); manifest.elements.push_back(std::move(element)); @@ -476,7 +475,7 @@ struct CmdProfileUpgrade : virtual SourceExprCommand, MixDefaultProfile, MixProf Strings{}, lockFlags); - auto [attrPath, resolvedRef, drv] = installable->toDerivation(); + auto [attrPath, resolvedRef, drv, priority] = installable->toDerivation(); if (element.source->resolvedRef == resolvedRef) continue; diff --git a/tests/nix-profile.sh b/tests/nix-profile.sh index 1cc724483..7ba3235fa 100644 --- a/tests/nix-profile.sh +++ b/tests/nix-profile.sh @@ -136,3 +136,5 @@ nix profile install $flake2Dir --priority 100 [[ $($TEST_HOME/.nix-profile/bin/hello) = "Hello World" ]] nix profile install $flake2Dir --priority 0 [[ $($TEST_HOME/.nix-profile/bin/hello) = "Hello World2" ]] +# nix profile install $flake1Dir --priority 100 +# [[ $($TEST_HOME/.nix-profile/bin/hello) = "Hello World" ]] From c81d24f1c70cc454c9a88cea70048d8563f60784 Mon Sep 17 00:00:00 2001 From: Eli Kogan-Wang Date: Mon, 16 May 2022 02:28:21 +0200 Subject: [PATCH 3/6] Add int to eval-cache, bump eval cache schema version --- src/libexpr/eval-cache.cc | 24 +++++++++++++++++++++++- src/libexpr/eval-cache.hh | 1 + 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/src/libexpr/eval-cache.cc b/src/libexpr/eval-cache.cc index b4bce512b..bf811c8ed 100644 --- a/src/libexpr/eval-cache.cc +++ b/src/libexpr/eval-cache.cc @@ -47,7 +47,7 @@ struct AttrDb { auto state(_state->lock()); - Path cacheDir = getCacheDir() + "/nix/eval-cache-v3"; + Path cacheDir = getCacheDir() + "/nix/eval-cache-v4"; createDirs(cacheDir); Path dbPath = cacheDir + "/" + fingerprint.to_string(Base16, false) + ".sqlite"; @@ -175,6 +175,24 @@ struct AttrDb }); } + AttrId setInt( + AttrKey key, + int n) + { + return doSQLite([&]() + { + auto state(_state->lock()); + + state->insertAttribute.use() + (key.first) + (symbols[key.second]) + (AttrType::Int) + (n).exec(); + + return state->db.getLastInsertedRowId(); + }); + } + AttrId setListOfStrings( AttrKey key, const std::vector & l) @@ -287,6 +305,8 @@ struct AttrDb } case AttrType::Bool: return {{rowId, queryAttribute.getInt(2) != 0}}; + case AttrType::Int: + return {{rowId, queryAttribute.getInt(2)}}; case AttrType::ListOfStrings: return {{rowId, tokenizeString>(queryAttribute.getStr(2), "\t")}}; case AttrType::Missing: @@ -426,6 +446,8 @@ Value & AttrCursor::forceValue() cachedValue = {root->db->setString(getKey(), v.path), string_t{v.path, {}}}; else if (v.type() == nBool) cachedValue = {root->db->setBool(getKey(), v.boolean), v.boolean}; + else if (v.type() == nInt) + cachedValue = {root->db->setInt(getKey(), v.integer), v.integer}; else if (v.type() == nAttrs) ; // FIXME: do something? else diff --git a/src/libexpr/eval-cache.hh b/src/libexpr/eval-cache.hh index 105e9217b..ec255c60d 100644 --- a/src/libexpr/eval-cache.hh +++ b/src/libexpr/eval-cache.hh @@ -45,6 +45,7 @@ enum AttrType { Failed = 5, Bool = 6, ListOfStrings = 7, + Int = 8, }; struct placeholder_t {}; From 27d0f6747d7e70be4b9ade28ce77444e6135cadb Mon Sep 17 00:00:00 2001 From: Eli Kogan-Wang Date: Mon, 16 May 2022 15:17:35 +0200 Subject: [PATCH 4/6] resolve redundant priority passing, wrap NixInt in eval-cache variant --- src/libcmd/installables.cc | 4 ++-- src/libcmd/installables.hh | 2 +- src/libexpr/eval-cache.cc | 6 +++--- src/libexpr/eval-cache.hh | 3 ++- src/nix/profile.cc | 4 ++-- 5 files changed, 10 insertions(+), 9 deletions(-) diff --git a/src/libcmd/installables.cc b/src/libcmd/installables.cc index 3f6dfd592..635ce19b6 100644 --- a/src/libcmd/installables.cc +++ b/src/libcmd/installables.cc @@ -609,7 +609,7 @@ InstallableFlake::InstallableFlake( throw UsageError("'--arg' and '--argstr' are incompatible with flakes"); } -std::tuple> InstallableFlake::toDerivation() +std::tuple InstallableFlake::toDerivation() { auto attr = getCursor(*state); @@ -650,7 +650,7 @@ std::tupleflake.lockedRef, std::move(drvInfo), priority}; + return {attrPath, getLockedFlake()->flake.lockedRef, std::move(drvInfo)}; } std::vector InstallableFlake::toDerivations() diff --git a/src/libcmd/installables.hh b/src/libcmd/installables.hh index d7b61f1b8..5d715210e 100644 --- a/src/libcmd/installables.hh +++ b/src/libcmd/installables.hh @@ -177,7 +177,7 @@ struct InstallableFlake : InstallableValue Value * getFlakeOutputs(EvalState & state, const flake::LockedFlake & lockedFlake); - std::tuple> toDerivation(); + std::tuple toDerivation(); std::vector toDerivations() override; diff --git a/src/libexpr/eval-cache.cc b/src/libexpr/eval-cache.cc index bf811c8ed..6b3c27fd5 100644 --- a/src/libexpr/eval-cache.cc +++ b/src/libexpr/eval-cache.cc @@ -306,7 +306,7 @@ struct AttrDb case AttrType::Bool: return {{rowId, queryAttribute.getInt(2) != 0}}; case AttrType::Int: - return {{rowId, queryAttribute.getInt(2)}}; + return {{rowId, (int_t) queryAttribute.getInt(2)}}; case AttrType::ListOfStrings: return {{rowId, tokenizeString>(queryAttribute.getStr(2), "\t")}}; case AttrType::Missing: @@ -649,9 +649,9 @@ NixInt AttrCursor::getInt() if (!cachedValue) cachedValue = root->db->getAttr(getKey()); if (cachedValue && !std::get_if(&cachedValue->second)) { - if (auto i = std::get_if(&cachedValue->second)) { + if (auto i = std::get_if(&cachedValue->second)) { debug("using cached Integer attribute '%s'", getAttrPathStr()); - return *i; + return (*i).x; } else throw TypeError("'%s' is not an Integer", getAttrPathStr()); } diff --git a/src/libexpr/eval-cache.hh b/src/libexpr/eval-cache.hh index ec255c60d..68b5952eb 100644 --- a/src/libexpr/eval-cache.hh +++ b/src/libexpr/eval-cache.hh @@ -52,6 +52,7 @@ struct placeholder_t {}; struct missing_t {}; struct misc_t {}; struct failed_t {}; +struct int_t { NixInt x; int_t(NixInt x) : x(x) {}; }; typedef uint64_t AttrId; typedef std::pair AttrKey; typedef std::pair string_t; @@ -64,7 +65,7 @@ typedef std::variant< misc_t, failed_t, bool, - NixInt, + int_t, std::vector > AttrValue; diff --git a/src/nix/profile.cc b/src/nix/profile.cc index ca5041873..1aae347df 100644 --- a/src/nix/profile.cc +++ b/src/nix/profile.cc @@ -300,7 +300,7 @@ struct CmdProfileInstall : InstallablesCommand, MixDefaultProfile if (auto installable2 = std::dynamic_pointer_cast(installable)) { // FIXME: make build() return this? - auto [attrPath, resolvedRef, drv, priority] = installable2->toDerivation(); + auto [attrPath, resolvedRef, drv] = installable2->toDerivation(); element.source = ProfileElementSource { installable2->flakeRef, resolvedRef, @@ -475,7 +475,7 @@ struct CmdProfileUpgrade : virtual SourceExprCommand, MixDefaultProfile, MixProf Strings{}, lockFlags); - auto [attrPath, resolvedRef, drv, priority] = installable->toDerivation(); + auto [attrPath, resolvedRef, drv] = installable->toDerivation(); if (element.source->resolvedRef == resolvedRef) continue; From e53349dd6e4a710eb7abff78722853cad418e9d2 Mon Sep 17 00:00:00 2001 From: Eli Kogan-Wang Date: Mon, 16 May 2022 16:16:06 +0200 Subject: [PATCH 5/6] change priority conflict message --- src/libstore/builtins/buildenv.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libstore/builtins/buildenv.cc b/src/libstore/builtins/buildenv.cc index 58ef89a1c..47458a388 100644 --- a/src/libstore/builtins/buildenv.cc +++ b/src/libstore/builtins/buildenv.cc @@ -95,7 +95,7 @@ static void createLinks(State & state, const Path & srcDir, const Path & dstDir, throw Error( "files '%1%' and '%2%' have the same priority %3%; " "use 'nix-env --set-flag priority NUMBER INSTALLED_PKGNAME' " - "or 'nix profile install --priority NUMBER INSTALLED_PKGNAME' " + "or type 'nix profile install --help' if using 'nix profile' to find out how" "to change the priority of one of the conflicting packages" " (0 being the highest priority)", srcFile, readLink(dstFile), priority); From 43a2c1367292733d3e0aa2e57137c897fb66d8f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9ophane=20Hufschmitt?= Date: Mon, 16 May 2022 16:36:21 +0200 Subject: [PATCH 6/6] Make nix::eval_cache::int_t more idiomatic MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Don’t explicitely give it a constructor, but use aggregate initialization instead (also prevents having an implicit coertion, which is probably good here) --- src/libexpr/eval-cache.cc | 6 +++--- src/libexpr/eval-cache.hh | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/libexpr/eval-cache.cc b/src/libexpr/eval-cache.cc index 6b3c27fd5..6a2e775d0 100644 --- a/src/libexpr/eval-cache.cc +++ b/src/libexpr/eval-cache.cc @@ -306,7 +306,7 @@ struct AttrDb case AttrType::Bool: return {{rowId, queryAttribute.getInt(2) != 0}}; case AttrType::Int: - return {{rowId, (int_t) queryAttribute.getInt(2)}}; + return {{rowId, int_t{queryAttribute.getInt(2)}}}; case AttrType::ListOfStrings: return {{rowId, tokenizeString>(queryAttribute.getStr(2), "\t")}}; case AttrType::Missing: @@ -447,7 +447,7 @@ Value & AttrCursor::forceValue() else if (v.type() == nBool) cachedValue = {root->db->setBool(getKey(), v.boolean), v.boolean}; else if (v.type() == nInt) - cachedValue = {root->db->setInt(getKey(), v.integer), v.integer}; + cachedValue = {root->db->setInt(getKey(), v.integer), int_t{v.integer}}; else if (v.type() == nAttrs) ; // FIXME: do something? else @@ -651,7 +651,7 @@ NixInt AttrCursor::getInt() if (cachedValue && !std::get_if(&cachedValue->second)) { if (auto i = std::get_if(&cachedValue->second)) { debug("using cached Integer attribute '%s'", getAttrPathStr()); - return (*i).x; + return i->x; } else throw TypeError("'%s' is not an Integer", getAttrPathStr()); } diff --git a/src/libexpr/eval-cache.hh b/src/libexpr/eval-cache.hh index 68b5952eb..c93e55b93 100644 --- a/src/libexpr/eval-cache.hh +++ b/src/libexpr/eval-cache.hh @@ -52,7 +52,7 @@ struct placeholder_t {}; struct missing_t {}; struct misc_t {}; struct failed_t {}; -struct int_t { NixInt x; int_t(NixInt x) : x(x) {}; }; +struct int_t { NixInt x; }; typedef uint64_t AttrId; typedef std::pair AttrKey; typedef std::pair string_t;