aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEelco Dolstra <edolstra@gmail.com>2022-05-03 14:37:28 +0200
committerEelco Dolstra <edolstra@gmail.com>2022-05-03 15:00:34 +0200
commita3c6c5b1c745a72a6a46bdf1a1de7a51a53f76b0 (patch)
treed1484046123f178724d86e265b49425e2c90628b
parent4a79cba5118f29b896f3d50164beacd4901ab01f (diff)
nix profile: Support overriding outputs
-rw-r--r--src/libstore/path-with-outputs.cc40
-rw-r--r--src/libstore/path-with-outputs.hh14
-rw-r--r--src/libutil/experimental-features.cc6
-rw-r--r--src/libutil/experimental-features.hh4
-rw-r--r--src/nix/profile-install.md7
-rw-r--r--src/nix/profile.cc30
-rw-r--r--tests/nix-profile.sh19
7 files changed, 101 insertions, 19 deletions
diff --git a/src/libstore/path-with-outputs.cc b/src/libstore/path-with-outputs.cc
index 7d180a0f6..d6d67ea05 100644
--- a/src/libstore/path-with-outputs.cc
+++ b/src/libstore/path-with-outputs.cc
@@ -1,5 +1,6 @@
#include "path-with-outputs.hh"
#include "store-api.hh"
+#include "nlohmann/json.hpp"
#include <regex>
@@ -84,4 +85,43 @@ std::pair<std::string, OutputsSpec> parseOutputsSpec(const std::string & s)
return {match[1], tokenizeString<OutputNames>(match[4].str(), ",")};
}
+std::string printOutputsSpec(const OutputsSpec & outputsSpec)
+{
+ if (std::get_if<DefaultOutputs>(&outputsSpec))
+ return "";
+
+ if (std::get_if<AllOutputs>(&outputsSpec))
+ return "^*";
+
+ if (auto outputNames = std::get_if<OutputNames>(&outputsSpec))
+ return "^" + concatStringsSep(",", *outputNames);
+
+ assert(false);
+}
+
+void to_json(nlohmann::json & json, const OutputsSpec & outputsSpec)
+{
+ if (std::get_if<DefaultOutputs>(&outputsSpec))
+ json = nullptr;
+
+ else if (std::get_if<AllOutputs>(&outputsSpec))
+ json = std::vector<std::string>({"*"});
+
+ else if (auto outputNames = std::get_if<OutputNames>(&outputsSpec))
+ json = *outputNames;
+}
+
+void from_json(const nlohmann::json & json, OutputsSpec & outputsSpec)
+{
+ if (json.is_null())
+ outputsSpec = DefaultOutputs();
+ else {
+ auto names = json.get<OutputNames>();
+ if (names == OutputNames({"*"}))
+ outputsSpec = AllOutputs();
+ else
+ outputsSpec = names;
+ }
+}
+
}
diff --git a/src/libstore/path-with-outputs.hh b/src/libstore/path-with-outputs.hh
index e4235d197..0cb5eb223 100644
--- a/src/libstore/path-with-outputs.hh
+++ b/src/libstore/path-with-outputs.hh
@@ -4,6 +4,7 @@
#include "path.hh"
#include "derived-path.hh"
+#include "nlohmann/json_fwd.hpp"
namespace nix {
@@ -34,9 +35,13 @@ StorePathWithOutputs followLinksToStorePathWithOutputs(const Store & store, std:
typedef std::set<std::string> OutputNames;
-struct AllOutputs { };
+struct AllOutputs {
+ bool operator < (const AllOutputs & _) const { return false; }
+};
-struct DefaultOutputs { };
+struct DefaultOutputs {
+ bool operator < (const DefaultOutputs & _) const { return false; }
+};
typedef std::variant<DefaultOutputs, AllOutputs, OutputNames> OutputsSpec;
@@ -44,4 +49,9 @@ typedef std::variant<DefaultOutputs, AllOutputs, OutputNames> OutputsSpec;
'prefix^*', returning the prefix and the outputs spec. */
std::pair<std::string, OutputsSpec> parseOutputsSpec(const std::string & s);
+std::string printOutputsSpec(const OutputsSpec & outputsSpec);
+
+void to_json(nlohmann::json &, const OutputsSpec &);
+void from_json(const nlohmann::json &, OutputsSpec &);
+
}
diff --git a/src/libutil/experimental-features.cc b/src/libutil/experimental-features.cc
index df37edf57..9326cd08c 100644
--- a/src/libutil/experimental-features.cc
+++ b/src/libutil/experimental-features.cc
@@ -58,11 +58,13 @@ std::ostream & operator <<(std::ostream & str, const ExperimentalFeature & featu
return str << showExperimentalFeature(feature);
}
-void to_json(nlohmann::json& j, const ExperimentalFeature& feature) {
+void to_json(nlohmann::json & j, const ExperimentalFeature & feature)
+{
j = showExperimentalFeature(feature);
}
-void from_json(const nlohmann::json& j, ExperimentalFeature& feature) {
+void from_json(const nlohmann::json & j, ExperimentalFeature & feature)
+{
const std::string input = j;
const auto parsed = parseExperimentalFeature(input);
diff --git a/src/libutil/experimental-features.hh b/src/libutil/experimental-features.hh
index a6d080094..57512830c 100644
--- a/src/libutil/experimental-features.hh
+++ b/src/libutil/experimental-features.hh
@@ -55,7 +55,7 @@ public:
* Semi-magic conversion to and from json.
* See the nlohmann/json readme for more details.
*/
-void to_json(nlohmann::json&, const ExperimentalFeature&);
-void from_json(const nlohmann::json&, ExperimentalFeature&);
+void to_json(nlohmann::json &, const ExperimentalFeature &);
+void from_json(const nlohmann::json &, ExperimentalFeature &);
}
diff --git a/src/nix/profile-install.md b/src/nix/profile-install.md
index e3009491e..aed414963 100644
--- a/src/nix/profile-install.md
+++ b/src/nix/profile-install.md
@@ -20,6 +20,13 @@ R""(
# nix profile install nixpkgs/d73407e8e6002646acfdef0e39ace088bacc83da#hello
```
+* Install a specific output of a package:
+
+ ```console
+ # nix profile install nixpkgs#bash^man
+ ```
+
+
# Description
This command adds *installables* to a Nix profile.
diff --git a/src/nix/profile.cc b/src/nix/profile.cc
index 78c8af80c..685776bec 100644
--- a/src/nix/profile.cc
+++ b/src/nix/profile.cc
@@ -22,13 +22,13 @@ struct ProfileElementSource
// FIXME: record original attrpath.
FlakeRef resolvedRef;
std::string attrPath;
- // FIXME: output names
+ OutputsSpec outputs;
bool operator < (const ProfileElementSource & other) const
{
return
- std::pair(originalRef.to_string(), attrPath) <
- std::pair(other.originalRef.to_string(), other.attrPath);
+ std::tuple(originalRef.to_string(), attrPath, outputs) <
+ std::tuple(other.originalRef.to_string(), other.attrPath, other.outputs);
}
};
@@ -42,7 +42,7 @@ struct ProfileElement
std::string describe() const
{
if (source)
- return fmt("%s#%s", source->originalRef, source->attrPath);
+ return fmt("%s#%s%s", source->originalRef, source->attrPath, printOutputsSpec(source->outputs));
StringSet names;
for (auto & path : storePaths)
names.insert(DrvName(path.name()).name);
@@ -98,7 +98,7 @@ struct ProfileManifest
auto version = json.value("version", 0);
std::string sUrl;
std::string sOriginalUrl;
- switch(version){
+ switch (version) {
case 1:
sUrl = "uri";
sOriginalUrl = "originalUri";
@@ -116,11 +116,12 @@ struct ProfileManifest
for (auto & p : e["storePaths"])
element.storePaths.insert(state.store->parseStorePath((std::string) p));
element.active = e["active"];
- if (e.value(sUrl,"") != "") {
- element.source = ProfileElementSource{
+ if (e.value(sUrl, "") != "") {
+ element.source = ProfileElementSource {
parseFlakeRef(e[sOriginalUrl]),
parseFlakeRef(e[sUrl]),
- e["attrPath"]
+ e["attrPath"],
+ e["outputs"].get<OutputsSpec>()
};
}
elements.emplace_back(std::move(element));
@@ -156,6 +157,7 @@ struct ProfileManifest
obj["originalUrl"] = element.source->originalRef.to_string();
obj["url"] = element.source->resolvedRef.to_string();
obj["attrPath"] = element.source->attrPath;
+ obj["outputs"] = element.source->outputs;
}
array.push_back(obj);
}
@@ -283,10 +285,11 @@ struct CmdProfileInstall : InstallablesCommand, MixDefaultProfile
if (auto installable2 = std::dynamic_pointer_cast<InstallableFlake>(installable)) {
// FIXME: make build() return this?
auto [attrPath, resolvedRef, drv] = installable2->toDerivation();
- element.source = ProfileElementSource{
+ element.source = ProfileElementSource {
installable2->flakeRef,
resolvedRef,
attrPath,
+ installable2->outputsSpec
};
}
@@ -443,7 +446,7 @@ struct CmdProfileUpgrade : virtual SourceExprCommand, MixDefaultProfile, MixProf
getEvalState(),
FlakeRef(element.source->originalRef),
"",
- DefaultOutputs(), // FIXME
+ element.source->outputs,
Strings{element.source->attrPath},
Strings{},
lockFlags);
@@ -455,10 +458,11 @@ struct CmdProfileUpgrade : virtual SourceExprCommand, MixDefaultProfile, MixProf
printInfo("upgrading '%s' from flake '%s' to '%s'",
element.source->attrPath, element.source->resolvedRef, resolvedRef);
- element.source = ProfileElementSource{
+ element.source = ProfileElementSource {
installable->flakeRef,
resolvedRef,
attrPath,
+ installable->outputsSpec
};
installables.push_back(installable);
@@ -514,8 +518,8 @@ struct CmdProfileList : virtual EvalCommand, virtual StoreCommand, MixDefaultPro
for (size_t i = 0; i < manifest.elements.size(); ++i) {
auto & element(manifest.elements[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 : "-",
+ element.source ? element.source->originalRef.to_string() + "#" + element.source->attrPath + printOutputsSpec(element.source->outputs) : "-",
+ element.source ? element.source->resolvedRef.to_string() + "#" + element.source->attrPath + printOutputsSpec(element.source->outputs) : "-",
concatStringsSep(" ", store->printStorePathSet(element.storePaths)));
}
}
diff --git a/tests/nix-profile.sh b/tests/nix-profile.sh
index 814192252..f8da3d929 100644
--- a/tests/nix-profile.sh
+++ b/tests/nix-profile.sh
@@ -101,3 +101,22 @@ printf Utrecht > $flake1Dir/who
nix profile install $flake1Dir
[[ $($TEST_HOME/.nix-profile/bin/hello) = "Hello Utrecht" ]]
[[ $(nix path-info --json $(realpath $TEST_HOME/.nix-profile/bin/hello) | jq -r .[].ca) =~ fixed:r:sha256: ]]
+
+# Override the outputs.
+nix profile remove 0 1
+nix profile install "$flake1Dir^*"
+[[ $($TEST_HOME/.nix-profile/bin/hello) = "Hello Utrecht" ]]
+[ -e $TEST_HOME/.nix-profile/share/man ]
+[ -e $TEST_HOME/.nix-profile/include ]
+
+printf Nix > $flake1Dir/who
+nix profile upgrade 0
+[[ $($TEST_HOME/.nix-profile/bin/hello) = "Hello Nix" ]]
+[ -e $TEST_HOME/.nix-profile/share/man ]
+[ -e $TEST_HOME/.nix-profile/include ]
+
+nix profile remove 0
+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 ])