nix profile: Support overriding outputs
This commit is contained in:
parent
4a79cba511
commit
a3c6c5b1c7
7 changed files with 101 additions and 19 deletions
|
@ -1,5 +1,6 @@
|
||||||
#include "path-with-outputs.hh"
|
#include "path-with-outputs.hh"
|
||||||
#include "store-api.hh"
|
#include "store-api.hh"
|
||||||
|
#include "nlohmann/json.hpp"
|
||||||
|
|
||||||
#include <regex>
|
#include <regex>
|
||||||
|
|
||||||
|
@ -84,4 +85,43 @@ std::pair<std::string, OutputsSpec> parseOutputsSpec(const std::string & s)
|
||||||
return {match[1], tokenizeString<OutputNames>(match[4].str(), ",")};
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
|
|
||||||
#include "path.hh"
|
#include "path.hh"
|
||||||
#include "derived-path.hh"
|
#include "derived-path.hh"
|
||||||
|
#include "nlohmann/json_fwd.hpp"
|
||||||
|
|
||||||
namespace nix {
|
namespace nix {
|
||||||
|
|
||||||
|
@ -34,9 +35,13 @@ StorePathWithOutputs followLinksToStorePathWithOutputs(const Store & store, std:
|
||||||
|
|
||||||
typedef std::set<std::string> OutputNames;
|
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;
|
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. */
|
'prefix^*', returning the prefix and the outputs spec. */
|
||||||
std::pair<std::string, OutputsSpec> parseOutputsSpec(const std::string & s);
|
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 &);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -58,11 +58,13 @@ std::ostream & operator <<(std::ostream & str, const ExperimentalFeature & featu
|
||||||
return str << showExperimentalFeature(feature);
|
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);
|
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 std::string input = j;
|
||||||
const auto parsed = parseExperimentalFeature(input);
|
const auto parsed = parseExperimentalFeature(input);
|
||||||
|
|
||||||
|
|
|
@ -55,7 +55,7 @@ public:
|
||||||
* Semi-magic conversion to and from json.
|
* Semi-magic conversion to and from json.
|
||||||
* See the nlohmann/json readme for more details.
|
* See the nlohmann/json readme for more details.
|
||||||
*/
|
*/
|
||||||
void to_json(nlohmann::json&, const ExperimentalFeature&);
|
void to_json(nlohmann::json &, const ExperimentalFeature &);
|
||||||
void from_json(const nlohmann::json&, ExperimentalFeature&);
|
void from_json(const nlohmann::json &, ExperimentalFeature &);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,6 +20,13 @@ R""(
|
||||||
# nix profile install nixpkgs/d73407e8e6002646acfdef0e39ace088bacc83da#hello
|
# nix profile install nixpkgs/d73407e8e6002646acfdef0e39ace088bacc83da#hello
|
||||||
```
|
```
|
||||||
|
|
||||||
|
* Install a specific output of a package:
|
||||||
|
|
||||||
|
```console
|
||||||
|
# nix profile install nixpkgs#bash^man
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
# Description
|
# Description
|
||||||
|
|
||||||
This command adds *installables* to a Nix profile.
|
This command adds *installables* to a Nix profile.
|
||||||
|
|
|
@ -22,13 +22,13 @@ struct ProfileElementSource
|
||||||
// FIXME: record original attrpath.
|
// FIXME: record original attrpath.
|
||||||
FlakeRef resolvedRef;
|
FlakeRef resolvedRef;
|
||||||
std::string attrPath;
|
std::string attrPath;
|
||||||
// FIXME: output names
|
OutputsSpec outputs;
|
||||||
|
|
||||||
bool operator < (const ProfileElementSource & other) const
|
bool operator < (const ProfileElementSource & other) const
|
||||||
{
|
{
|
||||||
return
|
return
|
||||||
std::pair(originalRef.to_string(), attrPath) <
|
std::tuple(originalRef.to_string(), attrPath, outputs) <
|
||||||
std::pair(other.originalRef.to_string(), other.attrPath);
|
std::tuple(other.originalRef.to_string(), other.attrPath, other.outputs);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -42,7 +42,7 @@ struct ProfileElement
|
||||||
std::string describe() const
|
std::string describe() const
|
||||||
{
|
{
|
||||||
if (source)
|
if (source)
|
||||||
return fmt("%s#%s", source->originalRef, source->attrPath);
|
return fmt("%s#%s%s", source->originalRef, source->attrPath, printOutputsSpec(source->outputs));
|
||||||
StringSet names;
|
StringSet names;
|
||||||
for (auto & path : storePaths)
|
for (auto & path : storePaths)
|
||||||
names.insert(DrvName(path.name()).name);
|
names.insert(DrvName(path.name()).name);
|
||||||
|
@ -98,7 +98,7 @@ struct ProfileManifest
|
||||||
auto version = json.value("version", 0);
|
auto version = json.value("version", 0);
|
||||||
std::string sUrl;
|
std::string sUrl;
|
||||||
std::string sOriginalUrl;
|
std::string sOriginalUrl;
|
||||||
switch(version){
|
switch (version) {
|
||||||
case 1:
|
case 1:
|
||||||
sUrl = "uri";
|
sUrl = "uri";
|
||||||
sOriginalUrl = "originalUri";
|
sOriginalUrl = "originalUri";
|
||||||
|
@ -116,11 +116,12 @@ struct ProfileManifest
|
||||||
for (auto & p : e["storePaths"])
|
for (auto & p : e["storePaths"])
|
||||||
element.storePaths.insert(state.store->parseStorePath((std::string) p));
|
element.storePaths.insert(state.store->parseStorePath((std::string) p));
|
||||||
element.active = e["active"];
|
element.active = e["active"];
|
||||||
if (e.value(sUrl,"") != "") {
|
if (e.value(sUrl, "") != "") {
|
||||||
element.source = ProfileElementSource{
|
element.source = ProfileElementSource {
|
||||||
parseFlakeRef(e[sOriginalUrl]),
|
parseFlakeRef(e[sOriginalUrl]),
|
||||||
parseFlakeRef(e[sUrl]),
|
parseFlakeRef(e[sUrl]),
|
||||||
e["attrPath"]
|
e["attrPath"],
|
||||||
|
e["outputs"].get<OutputsSpec>()
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
elements.emplace_back(std::move(element));
|
elements.emplace_back(std::move(element));
|
||||||
|
@ -156,6 +157,7 @@ struct ProfileManifest
|
||||||
obj["originalUrl"] = element.source->originalRef.to_string();
|
obj["originalUrl"] = element.source->originalRef.to_string();
|
||||||
obj["url"] = element.source->resolvedRef.to_string();
|
obj["url"] = element.source->resolvedRef.to_string();
|
||||||
obj["attrPath"] = element.source->attrPath;
|
obj["attrPath"] = element.source->attrPath;
|
||||||
|
obj["outputs"] = element.source->outputs;
|
||||||
}
|
}
|
||||||
array.push_back(obj);
|
array.push_back(obj);
|
||||||
}
|
}
|
||||||
|
@ -283,10 +285,11 @@ struct CmdProfileInstall : InstallablesCommand, MixDefaultProfile
|
||||||
if (auto installable2 = std::dynamic_pointer_cast<InstallableFlake>(installable)) {
|
if (auto installable2 = std::dynamic_pointer_cast<InstallableFlake>(installable)) {
|
||||||
// FIXME: make build() return this?
|
// FIXME: make build() return this?
|
||||||
auto [attrPath, resolvedRef, drv] = installable2->toDerivation();
|
auto [attrPath, resolvedRef, drv] = installable2->toDerivation();
|
||||||
element.source = ProfileElementSource{
|
element.source = ProfileElementSource {
|
||||||
installable2->flakeRef,
|
installable2->flakeRef,
|
||||||
resolvedRef,
|
resolvedRef,
|
||||||
attrPath,
|
attrPath,
|
||||||
|
installable2->outputsSpec
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -443,7 +446,7 @@ struct CmdProfileUpgrade : virtual SourceExprCommand, MixDefaultProfile, MixProf
|
||||||
getEvalState(),
|
getEvalState(),
|
||||||
FlakeRef(element.source->originalRef),
|
FlakeRef(element.source->originalRef),
|
||||||
"",
|
"",
|
||||||
DefaultOutputs(), // FIXME
|
element.source->outputs,
|
||||||
Strings{element.source->attrPath},
|
Strings{element.source->attrPath},
|
||||||
Strings{},
|
Strings{},
|
||||||
lockFlags);
|
lockFlags);
|
||||||
|
@ -455,10 +458,11 @@ struct CmdProfileUpgrade : virtual SourceExprCommand, MixDefaultProfile, MixProf
|
||||||
printInfo("upgrading '%s' from flake '%s' to '%s'",
|
printInfo("upgrading '%s' from flake '%s' to '%s'",
|
||||||
element.source->attrPath, element.source->resolvedRef, resolvedRef);
|
element.source->attrPath, element.source->resolvedRef, resolvedRef);
|
||||||
|
|
||||||
element.source = ProfileElementSource{
|
element.source = ProfileElementSource {
|
||||||
installable->flakeRef,
|
installable->flakeRef,
|
||||||
resolvedRef,
|
resolvedRef,
|
||||||
attrPath,
|
attrPath,
|
||||||
|
installable->outputsSpec
|
||||||
};
|
};
|
||||||
|
|
||||||
installables.push_back(installable);
|
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) {
|
for (size_t i = 0; i < manifest.elements.size(); ++i) {
|
||||||
auto & element(manifest.elements[i]);
|
auto & element(manifest.elements[i]);
|
||||||
logger->cout("%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->originalRef.to_string() + "#" + element.source->attrPath + printOutputsSpec(element.source->outputs) : "-",
|
||||||
element.source ? element.source->resolvedRef.to_string() + "#" + element.source->attrPath : "-",
|
element.source ? element.source->resolvedRef.to_string() + "#" + element.source->attrPath + printOutputsSpec(element.source->outputs) : "-",
|
||||||
concatStringsSep(" ", store->printStorePathSet(element.storePaths)));
|
concatStringsSep(" ", store->printStorePathSet(element.storePaths)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -101,3 +101,22 @@ printf Utrecht > $flake1Dir/who
|
||||||
nix profile install $flake1Dir
|
nix profile install $flake1Dir
|
||||||
[[ $($TEST_HOME/.nix-profile/bin/hello) = "Hello Utrecht" ]]
|
[[ $($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: ]]
|
[[ $(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 ])
|
||||||
|
|
Loading…
Reference in a new issue