2018-11-29 18:18:36 +00:00
|
|
|
#include "command.hh"
|
|
|
|
#include "common-args.hh"
|
|
|
|
#include "shared.hh"
|
|
|
|
#include "eval.hh"
|
2019-05-29 15:25:41 +00:00
|
|
|
#include "eval-inline.hh"
|
2019-06-05 14:51:54 +00:00
|
|
|
#include "flake/flake.hh"
|
2019-05-29 15:25:41 +00:00
|
|
|
#include "get-drvs.hh"
|
|
|
|
#include "store-api.hh"
|
2019-06-17 15:59:57 +00:00
|
|
|
#include "derivations.hh"
|
2019-09-19 18:15:42 +00:00
|
|
|
#include "attr-path.hh"
|
2020-03-30 12:03:28 +00:00
|
|
|
#include "fetchers.hh"
|
|
|
|
#include "registry.hh"
|
2020-01-29 23:58:55 +00:00
|
|
|
#include "json.hh"
|
2020-04-16 23:02:29 +00:00
|
|
|
#include "sqlite.hh"
|
2019-04-16 12:10:05 +00:00
|
|
|
|
2019-02-27 18:54:18 +00:00
|
|
|
#include <nlohmann/json.hpp>
|
2019-03-29 15:18:25 +00:00
|
|
|
#include <queue>
|
2019-05-28 18:34:02 +00:00
|
|
|
#include <iomanip>
|
2018-11-29 18:18:36 +00:00
|
|
|
|
|
|
|
using namespace nix;
|
2019-05-29 13:31:07 +00:00
|
|
|
using namespace nix::flake;
|
2018-11-29 18:18:36 +00:00
|
|
|
|
2019-05-22 11:46:07 +00:00
|
|
|
class FlakeCommand : virtual Args, public EvalCommand, public MixFlakeOptions
|
2019-05-16 20:48:16 +00:00
|
|
|
{
|
2019-10-08 14:30:04 +00:00
|
|
|
std::string flakeUrl = ".";
|
2019-05-16 20:48:16 +00:00
|
|
|
|
|
|
|
public:
|
|
|
|
|
|
|
|
FlakeCommand()
|
|
|
|
{
|
2019-10-08 14:30:04 +00:00
|
|
|
expectArg("flake-url", &flakeUrl, true);
|
2019-05-16 20:48:16 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
FlakeRef getFlakeRef()
|
|
|
|
{
|
2020-01-21 15:27:53 +00:00
|
|
|
return parseFlakeRef(flakeUrl, absPath(".")); //FIXME
|
2019-05-16 20:48:16 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
Flake getFlake()
|
|
|
|
{
|
|
|
|
auto evalState = getEvalState();
|
2020-01-29 20:01:34 +00:00
|
|
|
return flake::getFlake(*evalState, getFlakeRef(), lockFlags.useRegistries);
|
2019-05-22 11:46:07 +00:00
|
|
|
}
|
|
|
|
|
2020-01-22 19:59:59 +00:00
|
|
|
LockedFlake lockFlake()
|
2019-05-22 11:46:07 +00:00
|
|
|
{
|
2020-01-29 20:01:34 +00:00
|
|
|
return flake::lockFlake(*getEvalState(), getFlakeRef(), lockFlags);
|
2019-05-16 20:48:16 +00:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
struct CmdFlakeList : EvalCommand
|
2018-11-29 18:18:36 +00:00
|
|
|
{
|
|
|
|
std::string description() override
|
|
|
|
{
|
|
|
|
return "list available Nix flakes";
|
|
|
|
}
|
|
|
|
|
|
|
|
void run(nix::ref<nix::Store> store) override
|
|
|
|
{
|
2020-01-21 15:27:53 +00:00
|
|
|
using namespace fetchers;
|
2018-11-29 18:18:36 +00:00
|
|
|
|
2020-01-21 15:27:53 +00:00
|
|
|
auto registries = getRegistries(store);
|
2019-04-17 12:03:04 +00:00
|
|
|
|
2020-01-21 15:27:53 +00:00
|
|
|
for (auto & registry : registries) {
|
|
|
|
for (auto & entry : registry->entries) {
|
|
|
|
// FIXME: format nicely
|
2020-04-16 11:46:37 +00:00
|
|
|
logger->stdout("%s %s %s",
|
2020-04-01 20:56:50 +00:00
|
|
|
registry->type == Registry::Flag ? "flags " :
|
|
|
|
registry->type == Registry::User ? "user " :
|
|
|
|
registry->type == Registry::System ? "system" :
|
2020-01-21 15:27:53 +00:00
|
|
|
"global",
|
2020-04-01 21:03:27 +00:00
|
|
|
entry.from->to_string(),
|
|
|
|
entry.to->to_string());
|
2020-01-21 15:27:53 +00:00
|
|
|
}
|
|
|
|
}
|
2018-11-29 18:18:36 +00:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2020-01-21 15:27:53 +00:00
|
|
|
static void printFlakeInfo(const Store & store, const Flake & flake)
|
2019-05-28 11:21:06 +00:00
|
|
|
{
|
2020-04-16 11:46:37 +00:00
|
|
|
logger->stdout("Resolved URL: %s", flake.resolvedRef.to_string());
|
|
|
|
logger->stdout("Locked URL: %s", flake.lockedRef.to_string());
|
2020-01-22 16:20:21 +00:00
|
|
|
if (flake.description)
|
2020-04-16 11:46:37 +00:00
|
|
|
logger->stdout("Description: %s", *flake.description);
|
|
|
|
logger->stdout("Path: %s", store.printStorePath(flake.sourceInfo->storePath));
|
2020-02-01 23:05:53 +00:00
|
|
|
if (auto rev = flake.lockedRef.input->getRev())
|
2020-04-16 11:46:37 +00:00
|
|
|
logger->stdout("Revision: %s", rev->to_string(Base16, false));
|
2020-02-01 15:41:54 +00:00
|
|
|
if (flake.sourceInfo->info.revCount)
|
2020-04-16 11:46:37 +00:00
|
|
|
logger->stdout("Revisions: %s", *flake.sourceInfo->info.revCount);
|
2020-02-01 15:41:54 +00:00
|
|
|
if (flake.sourceInfo->info.lastModified)
|
2020-04-16 11:46:37 +00:00
|
|
|
logger->stdout("Last modified: %s",
|
2020-02-01 15:41:54 +00:00
|
|
|
std::put_time(std::localtime(&*flake.sourceInfo->info.lastModified), "%F %T"));
|
2019-03-21 08:30:16 +00:00
|
|
|
}
|
|
|
|
|
2020-01-21 15:27:53 +00:00
|
|
|
static nlohmann::json flakeToJson(const Store & store, const Flake & flake)
|
2019-05-28 11:21:06 +00:00
|
|
|
{
|
2019-05-28 12:01:57 +00:00
|
|
|
nlohmann::json j;
|
2020-01-22 16:20:21 +00:00
|
|
|
if (flake.description)
|
|
|
|
j["description"] = *flake.description;
|
2020-04-06 12:56:13 +00:00
|
|
|
j["originalUrl"] = flake.originalRef.to_string();
|
2020-03-28 21:59:38 +00:00
|
|
|
j["original"] = attrsToJson(flake.originalRef.toAttrs());
|
2020-04-06 12:56:13 +00:00
|
|
|
j["resolvedUrl"] = flake.resolvedRef.to_string();
|
|
|
|
j["resolved"] = attrsToJson(flake.resolvedRef.toAttrs());
|
|
|
|
j["url"] = flake.lockedRef.to_string(); // FIXME: rename to lockedUrl
|
2020-03-28 21:59:38 +00:00
|
|
|
j["locked"] = attrsToJson(flake.lockedRef.toAttrs());
|
2020-04-02 09:51:34 +00:00
|
|
|
j["info"] = flake.sourceInfo->info.toJson();
|
2020-02-01 23:05:53 +00:00
|
|
|
if (auto rev = flake.lockedRef.input->getRev())
|
2020-02-01 22:54:20 +00:00
|
|
|
j["revision"] = rev->to_string(Base16, false);
|
2020-02-01 15:41:54 +00:00
|
|
|
if (flake.sourceInfo->info.revCount)
|
|
|
|
j["revCount"] = *flake.sourceInfo->info.revCount;
|
|
|
|
if (flake.sourceInfo->info.lastModified)
|
|
|
|
j["lastModified"] = *flake.sourceInfo->info.lastModified;
|
2020-01-21 15:27:53 +00:00
|
|
|
j["path"] = store.printStorePath(flake.sourceInfo->storePath);
|
2019-05-28 12:01:57 +00:00
|
|
|
return j;
|
|
|
|
}
|
|
|
|
|
2019-05-16 20:48:16 +00:00
|
|
|
struct CmdFlakeUpdate : FlakeCommand
|
2019-02-21 05:53:01 +00:00
|
|
|
{
|
|
|
|
std::string description() override
|
|
|
|
{
|
|
|
|
return "update flake lock file";
|
|
|
|
}
|
|
|
|
|
|
|
|
void run(nix::ref<nix::Store> store) override
|
|
|
|
{
|
2020-02-01 11:26:05 +00:00
|
|
|
/* Use --refresh by default for 'nix flake update'. */
|
|
|
|
settings.tarballTtl = 0;
|
|
|
|
|
2020-01-22 19:59:59 +00:00
|
|
|
lockFlake();
|
2019-02-21 05:53:01 +00:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2019-05-29 21:09:23 +00:00
|
|
|
static void enumerateOutputs(EvalState & state, Value & vFlake,
|
2019-09-10 13:25:10 +00:00
|
|
|
std::function<void(const std::string & name, Value & vProvide, const Pos & pos)> callback)
|
nix flake info --json: List the "provides"
It also lists the contents of "checks" and "packages".
For example:
$ nix flake info --json | jq
{
"branch": "HEAD",
"description": "The purely functional package manager",
"epoch": 2019,
"id": "nix",
"lastModified": 1559161142,
"path": "/nix/store/2w2qla8735dbxah8gai8r1nsbf5x4f5d-source",
"provides": {
"checks": {
"binaryTarball": {},
"nix-copy-closure": {},
"perlBindings": {},
"remoteBuilds": {},
"setuid": {}
},
"defaultPackage": {},
"devShell": {},
"hydraJobs": {},
"packages": {
"nix": {},
"nix-perl-bindings": {}
}
},
"revCount": 6955,
"revision": "8cb24e04e8b6cc60e2504733afe78e0eadafcd98",
"uri": "/home/eelco/Dev/nix"
}
Fixes #2820.
2019-05-29 20:17:08 +00:00
|
|
|
{
|
|
|
|
state.forceAttrs(vFlake);
|
|
|
|
|
2019-09-22 19:53:01 +00:00
|
|
|
auto aOutputs = vFlake.attrs->get(state.symbols.create("outputs"));
|
|
|
|
assert(aOutputs);
|
nix flake info --json: List the "provides"
It also lists the contents of "checks" and "packages".
For example:
$ nix flake info --json | jq
{
"branch": "HEAD",
"description": "The purely functional package manager",
"epoch": 2019,
"id": "nix",
"lastModified": 1559161142,
"path": "/nix/store/2w2qla8735dbxah8gai8r1nsbf5x4f5d-source",
"provides": {
"checks": {
"binaryTarball": {},
"nix-copy-closure": {},
"perlBindings": {},
"remoteBuilds": {},
"setuid": {}
},
"defaultPackage": {},
"devShell": {},
"hydraJobs": {},
"packages": {
"nix": {},
"nix-perl-bindings": {}
}
},
"revCount": 6955,
"revision": "8cb24e04e8b6cc60e2504733afe78e0eadafcd98",
"uri": "/home/eelco/Dev/nix"
}
Fixes #2820.
2019-05-29 20:17:08 +00:00
|
|
|
|
2020-02-14 21:45:33 +00:00
|
|
|
state.forceAttrs(*aOutputs->value);
|
nix flake info --json: List the "provides"
It also lists the contents of "checks" and "packages".
For example:
$ nix flake info --json | jq
{
"branch": "HEAD",
"description": "The purely functional package manager",
"epoch": 2019,
"id": "nix",
"lastModified": 1559161142,
"path": "/nix/store/2w2qla8735dbxah8gai8r1nsbf5x4f5d-source",
"provides": {
"checks": {
"binaryTarball": {},
"nix-copy-closure": {},
"perlBindings": {},
"remoteBuilds": {},
"setuid": {}
},
"defaultPackage": {},
"devShell": {},
"hydraJobs": {},
"packages": {
"nix": {},
"nix-perl-bindings": {}
}
},
"revCount": 6955,
"revision": "8cb24e04e8b6cc60e2504733afe78e0eadafcd98",
"uri": "/home/eelco/Dev/nix"
}
Fixes #2820.
2019-05-29 20:17:08 +00:00
|
|
|
|
2020-02-14 21:45:33 +00:00
|
|
|
for (auto & attr : *aOutputs->value->attrs)
|
2019-09-10 13:25:10 +00:00
|
|
|
callback(attr.name, *attr.value, *attr.pos);
|
nix flake info --json: List the "provides"
It also lists the contents of "checks" and "packages".
For example:
$ nix flake info --json | jq
{
"branch": "HEAD",
"description": "The purely functional package manager",
"epoch": 2019,
"id": "nix",
"lastModified": 1559161142,
"path": "/nix/store/2w2qla8735dbxah8gai8r1nsbf5x4f5d-source",
"provides": {
"checks": {
"binaryTarball": {},
"nix-copy-closure": {},
"perlBindings": {},
"remoteBuilds": {},
"setuid": {}
},
"defaultPackage": {},
"devShell": {},
"hydraJobs": {},
"packages": {
"nix": {},
"nix-perl-bindings": {}
}
},
"revCount": 6955,
"revision": "8cb24e04e8b6cc60e2504733afe78e0eadafcd98",
"uri": "/home/eelco/Dev/nix"
}
Fixes #2820.
2019-05-29 20:17:08 +00:00
|
|
|
}
|
|
|
|
|
2019-05-16 20:48:16 +00:00
|
|
|
struct CmdFlakeInfo : FlakeCommand, MixJSON
|
2019-02-21 05:53:01 +00:00
|
|
|
{
|
|
|
|
std::string description() override
|
|
|
|
{
|
|
|
|
return "list info about a given flake";
|
|
|
|
}
|
|
|
|
|
|
|
|
void run(nix::ref<nix::Store> store) override
|
|
|
|
{
|
2020-04-06 12:39:47 +00:00
|
|
|
auto flake = getFlake();
|
nix flake info --json: List the "provides"
It also lists the contents of "checks" and "packages".
For example:
$ nix flake info --json | jq
{
"branch": "HEAD",
"description": "The purely functional package manager",
"epoch": 2019,
"id": "nix",
"lastModified": 1559161142,
"path": "/nix/store/2w2qla8735dbxah8gai8r1nsbf5x4f5d-source",
"provides": {
"checks": {
"binaryTarball": {},
"nix-copy-closure": {},
"perlBindings": {},
"remoteBuilds": {},
"setuid": {}
},
"defaultPackage": {},
"devShell": {},
"hydraJobs": {},
"packages": {
"nix": {},
"nix-perl-bindings": {}
}
},
"revCount": 6955,
"revision": "8cb24e04e8b6cc60e2504733afe78e0eadafcd98",
"uri": "/home/eelco/Dev/nix"
}
Fixes #2820.
2019-05-29 20:17:08 +00:00
|
|
|
|
2020-04-06 12:39:47 +00:00
|
|
|
if (json) {
|
|
|
|
auto json = flakeToJson(*store, flake);
|
2020-04-16 17:01:49 +00:00
|
|
|
logger->stdout("%s", json.dump());
|
2020-04-06 12:39:47 +00:00
|
|
|
} else
|
2020-01-21 15:27:53 +00:00
|
|
|
printFlakeInfo(*store, flake);
|
nix flake info --json: List the "provides"
It also lists the contents of "checks" and "packages".
For example:
$ nix flake info --json | jq
{
"branch": "HEAD",
"description": "The purely functional package manager",
"epoch": 2019,
"id": "nix",
"lastModified": 1559161142,
"path": "/nix/store/2w2qla8735dbxah8gai8r1nsbf5x4f5d-source",
"provides": {
"checks": {
"binaryTarball": {},
"nix-copy-closure": {},
"perlBindings": {},
"remoteBuilds": {},
"setuid": {}
},
"defaultPackage": {},
"devShell": {},
"hydraJobs": {},
"packages": {
"nix": {},
"nix-perl-bindings": {}
}
},
"revCount": 6955,
"revision": "8cb24e04e8b6cc60e2504733afe78e0eadafcd98",
"uri": "/home/eelco/Dev/nix"
}
Fixes #2820.
2019-05-29 20:17:08 +00:00
|
|
|
}
|
|
|
|
};
|
2019-05-29 15:25:41 +00:00
|
|
|
|
2020-01-31 11:54:52 +00:00
|
|
|
struct CmdFlakeListInputs : FlakeCommand, MixJSON
|
|
|
|
{
|
|
|
|
std::string description() override
|
|
|
|
{
|
|
|
|
return "list flake inputs";
|
|
|
|
}
|
|
|
|
|
|
|
|
void run(nix::ref<nix::Store> store) override
|
|
|
|
{
|
|
|
|
auto flake = lockFlake();
|
|
|
|
|
|
|
|
if (json)
|
2020-04-16 17:01:49 +00:00
|
|
|
logger->stdout("%s", flake.lockFile.toJson());
|
2020-01-31 11:54:52 +00:00
|
|
|
else {
|
2020-04-16 11:46:37 +00:00
|
|
|
logger->stdout("%s", flake.flake.lockedRef);
|
2020-01-31 11:54:52 +00:00
|
|
|
|
2020-03-12 21:06:57 +00:00
|
|
|
std::function<void(const Node & node, const std::string & prefix)> recurse;
|
2020-01-31 11:54:52 +00:00
|
|
|
|
2020-03-12 21:06:57 +00:00
|
|
|
recurse = [&](const Node & node, const std::string & prefix)
|
2020-01-31 11:54:52 +00:00
|
|
|
{
|
2020-03-12 21:06:57 +00:00
|
|
|
for (const auto & [i, input] : enumerate(node.inputs)) {
|
2020-01-31 13:01:46 +00:00
|
|
|
//auto tree2 = tree.child(i + 1 == inputs.inputs.size());
|
2020-03-12 21:06:57 +00:00
|
|
|
bool last = i + 1 == node.inputs.size();
|
2020-04-16 11:46:37 +00:00
|
|
|
logger->stdout("%s" ANSI_BOLD "%s" ANSI_NORMAL ": %s",
|
2020-03-12 21:06:57 +00:00
|
|
|
prefix + (last ? treeLast : treeConn), input.first,
|
|
|
|
std::dynamic_pointer_cast<const LockedNode>(input.second)->lockedRef);
|
|
|
|
recurse(*input.second, prefix + (last ? treeNull : treeLine));
|
2020-01-31 11:54:52 +00:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2020-03-12 21:06:57 +00:00
|
|
|
recurse(*flake.lockFile.root, "");
|
2020-01-31 11:54:52 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2020-01-29 23:58:55 +00:00
|
|
|
struct CmdFlakeCheck : FlakeCommand
|
2019-05-29 15:25:41 +00:00
|
|
|
{
|
|
|
|
bool build = true;
|
|
|
|
|
|
|
|
CmdFlakeCheck()
|
|
|
|
{
|
|
|
|
mkFlag()
|
|
|
|
.longName("no-build")
|
|
|
|
.description("do not build checks")
|
|
|
|
.set(&build, false);
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string description() override
|
|
|
|
{
|
|
|
|
return "check whether the flake evaluates and run its tests";
|
|
|
|
}
|
|
|
|
|
|
|
|
void run(nix::ref<nix::Store> store) override
|
|
|
|
{
|
2019-05-29 19:00:44 +00:00
|
|
|
settings.readOnlyMode = !build;
|
|
|
|
|
2019-05-29 15:25:41 +00:00
|
|
|
auto state = getEvalState();
|
2020-01-22 19:59:59 +00:00
|
|
|
auto flake = lockFlake();
|
2019-05-29 15:25:41 +00:00
|
|
|
|
Support non-x86_64-linux system types in flakes
A command like
$ nix run nixpkgs#hello
will now build the attribute 'packages.${system}.hello' rather than
'packages.hello'. Note that this does mean that the flake needs to
export an attribute for every system type it supports, and you can't
build on unsupported systems. So 'packages' typically looks like this:
packages = nixpkgs.lib.genAttrs ["x86_64-linux" "i686-linux"] (system: {
hello = ...;
});
The 'checks', 'defaultPackage', 'devShell', 'apps' and 'defaultApp'
outputs similarly are now attrsets that map system types to
derivations/apps. 'nix flake check' checks that the derivations for
all platforms evaluate correctly, but only builds the derivations in
'checks.${system}'.
Fixes #2861. (That issue also talks about access to ~/.config/nixpkgs
and --arg, but I think it's reasonable to say that flakes shouldn't
support those.)
The alternative to attribute selection is to pass the system type as
an argument to the flake's 'outputs' function, e.g. 'outputs = { self,
nixpkgs, system }: ...'. However, that approach would be at odds with
hermetic evaluation and make it impossible to enumerate the packages
provided by a flake.
2019-10-15 15:52:10 +00:00
|
|
|
auto checkSystemName = [&](const std::string & system, const Pos & pos) {
|
|
|
|
// FIXME: what's the format of "system"?
|
|
|
|
if (system.find('-') == std::string::npos)
|
|
|
|
throw Error("'%s' is not a valid system type, at %s", system, pos);
|
|
|
|
};
|
|
|
|
|
2019-09-10 13:25:10 +00:00
|
|
|
auto checkDerivation = [&](const std::string & attrPath, Value & v, const Pos & pos) {
|
2019-05-29 18:57:08 +00:00
|
|
|
try {
|
|
|
|
auto drvInfo = getDerivation(*state, v, false);
|
|
|
|
if (!drvInfo)
|
|
|
|
throw Error("flake attribute '%s' is not a derivation", attrPath);
|
|
|
|
// FIXME: check meta attributes
|
2019-12-11 13:53:30 +00:00
|
|
|
return store->parseStorePath(drvInfo->queryDrvPath());
|
2019-05-29 18:57:08 +00:00
|
|
|
} catch (Error & e) {
|
2019-09-10 13:25:10 +00:00
|
|
|
e.addPrefix(fmt("while checking the derivation '" ANSI_BOLD "%s" ANSI_NORMAL "' at %s:\n", attrPath, pos));
|
2019-05-29 18:57:08 +00:00
|
|
|
throw;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2019-12-11 13:53:30 +00:00
|
|
|
std::vector<StorePathWithOutputs> drvPaths;
|
2019-05-29 15:25:41 +00:00
|
|
|
|
2019-09-10 13:25:10 +00:00
|
|
|
auto checkApp = [&](const std::string & attrPath, Value & v, const Pos & pos) {
|
2019-06-17 15:59:57 +00:00
|
|
|
try {
|
|
|
|
auto app = App(*state, v);
|
|
|
|
for (auto & i : app.context) {
|
2019-12-11 13:53:30 +00:00
|
|
|
auto [drvPathS, outputName] = decodeContext(i);
|
|
|
|
auto drvPath = store->parseStorePath(drvPathS);
|
|
|
|
if (!outputName.empty() && drvPath.isDerivation())
|
|
|
|
drvPaths.emplace_back(drvPath);
|
2019-06-17 15:59:57 +00:00
|
|
|
}
|
|
|
|
} catch (Error & e) {
|
2019-09-10 13:25:10 +00:00
|
|
|
e.addPrefix(fmt("while checking the app definition '" ANSI_BOLD "%s" ANSI_NORMAL "' at %s:\n", attrPath, pos));
|
2019-06-17 15:59:57 +00:00
|
|
|
throw;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2019-09-10 13:25:10 +00:00
|
|
|
auto checkOverlay = [&](const std::string & attrPath, Value & v, const Pos & pos) {
|
2019-09-10 12:52:22 +00:00
|
|
|
try {
|
2019-09-10 15:39:55 +00:00
|
|
|
state->forceValue(v, pos);
|
2019-09-10 12:52:22 +00:00
|
|
|
if (v.type != tLambda || v.lambda.fun->matchAttrs || std::string(v.lambda.fun->arg) != "final")
|
|
|
|
throw Error("overlay does not take an argument named 'final'");
|
|
|
|
auto body = dynamic_cast<ExprLambda *>(v.lambda.fun->body);
|
|
|
|
if (!body || body->matchAttrs || std::string(body->arg) != "prev")
|
|
|
|
throw Error("overlay does not take an argument named 'prev'");
|
|
|
|
// FIXME: if we have a 'nixpkgs' input, use it to
|
|
|
|
// evaluate the overlay.
|
|
|
|
} catch (Error & e) {
|
2019-09-10 13:25:10 +00:00
|
|
|
e.addPrefix(fmt("while checking the overlay '" ANSI_BOLD "%s" ANSI_NORMAL "' at %s:\n", attrPath, pos));
|
|
|
|
throw;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
auto checkModule = [&](const std::string & attrPath, Value & v, const Pos & pos) {
|
|
|
|
try {
|
2019-09-10 15:39:55 +00:00
|
|
|
state->forceValue(v, pos);
|
2019-09-10 13:25:10 +00:00
|
|
|
if (v.type == tLambda) {
|
|
|
|
if (!v.lambda.fun->matchAttrs || !v.lambda.fun->formals->ellipsis)
|
|
|
|
throw Error("module must match an open attribute set ('{ config, ... }')");
|
|
|
|
} else if (v.type == tAttrs) {
|
|
|
|
for (auto & attr : *v.attrs)
|
|
|
|
try {
|
2019-09-10 15:39:55 +00:00
|
|
|
state->forceValue(*attr.value, *attr.pos);
|
2019-09-10 13:25:10 +00:00
|
|
|
} catch (Error & e) {
|
|
|
|
e.addPrefix(fmt("while evaluating the option '" ANSI_BOLD "%s" ANSI_NORMAL "' at %s:\n", attr.name, *attr.pos));
|
|
|
|
throw;
|
|
|
|
}
|
|
|
|
} else
|
|
|
|
throw Error("module must be a function or an attribute set");
|
|
|
|
// FIXME: if we have a 'nixpkgs' input, use it to
|
|
|
|
// check the module.
|
|
|
|
} catch (Error & e) {
|
|
|
|
e.addPrefix(fmt("while checking the NixOS module '" ANSI_BOLD "%s" ANSI_NORMAL "' at %s:\n", attrPath, pos));
|
2019-09-10 12:52:22 +00:00
|
|
|
throw;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2019-09-10 15:39:55 +00:00
|
|
|
std::function<void(const std::string & attrPath, Value & v, const Pos & pos)> checkHydraJobs;
|
|
|
|
|
|
|
|
checkHydraJobs = [&](const std::string & attrPath, Value & v, const Pos & pos) {
|
|
|
|
try {
|
|
|
|
state->forceAttrs(v, pos);
|
|
|
|
|
|
|
|
if (state->isDerivation(v))
|
|
|
|
throw Error("jobset should not be a derivation at top-level");
|
|
|
|
|
|
|
|
for (auto & attr : *v.attrs) {
|
|
|
|
state->forceAttrs(*attr.value, *attr.pos);
|
|
|
|
if (!state->isDerivation(*attr.value))
|
|
|
|
checkHydraJobs(attrPath + "." + (std::string) attr.name,
|
|
|
|
*attr.value, *attr.pos);
|
|
|
|
}
|
|
|
|
|
|
|
|
} catch (Error & e) {
|
|
|
|
e.addPrefix(fmt("while checking the Hydra jobset '" ANSI_BOLD "%s" ANSI_NORMAL "' at %s:\n", attrPath, pos));
|
|
|
|
throw;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2019-09-19 18:15:42 +00:00
|
|
|
auto checkNixOSConfiguration = [&](const std::string & attrPath, Value & v, const Pos & pos) {
|
|
|
|
try {
|
|
|
|
Activity act(*logger, lvlChatty, actUnknown,
|
|
|
|
fmt("checking NixOS configuration '%s'", attrPath));
|
|
|
|
Bindings & bindings(*state->allocBindings(0));
|
2020-02-07 13:08:24 +00:00
|
|
|
auto vToplevel = findAlongAttrPath(*state, "config.system.build.toplevel", bindings, v).first;
|
2019-09-19 18:15:42 +00:00
|
|
|
state->forceAttrs(*vToplevel, pos);
|
|
|
|
if (!state->isDerivation(*vToplevel))
|
|
|
|
throw Error("attribute 'config.system.build.toplevel' is not a derivation");
|
|
|
|
} catch (Error & e) {
|
|
|
|
e.addPrefix(fmt("while checking the NixOS configuration '" ANSI_BOLD "%s" ANSI_NORMAL "' at %s:\n", attrPath, pos));
|
|
|
|
throw;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2019-05-29 15:25:41 +00:00
|
|
|
{
|
|
|
|
Activity act(*logger, lvlInfo, actUnknown, "evaluating flake");
|
|
|
|
|
|
|
|
auto vFlake = state->allocValue();
|
|
|
|
flake::callFlake(*state, flake, *vFlake);
|
|
|
|
|
2019-05-29 21:09:23 +00:00
|
|
|
enumerateOutputs(*state,
|
2019-05-29 15:25:41 +00:00
|
|
|
*vFlake,
|
2019-09-10 13:25:10 +00:00
|
|
|
[&](const std::string & name, Value & vOutput, const Pos & pos) {
|
2019-05-29 15:25:41 +00:00
|
|
|
Activity act(*logger, lvlChatty, actUnknown,
|
|
|
|
fmt("checking flake output '%s'", name));
|
|
|
|
|
|
|
|
try {
|
2019-09-10 15:39:55 +00:00
|
|
|
state->forceValue(vOutput, pos);
|
2019-05-29 15:25:41 +00:00
|
|
|
|
|
|
|
if (name == "checks") {
|
2019-09-10 15:39:55 +00:00
|
|
|
state->forceAttrs(vOutput, pos);
|
Support non-x86_64-linux system types in flakes
A command like
$ nix run nixpkgs#hello
will now build the attribute 'packages.${system}.hello' rather than
'packages.hello'. Note that this does mean that the flake needs to
export an attribute for every system type it supports, and you can't
build on unsupported systems. So 'packages' typically looks like this:
packages = nixpkgs.lib.genAttrs ["x86_64-linux" "i686-linux"] (system: {
hello = ...;
});
The 'checks', 'defaultPackage', 'devShell', 'apps' and 'defaultApp'
outputs similarly are now attrsets that map system types to
derivations/apps. 'nix flake check' checks that the derivations for
all platforms evaluate correctly, but only builds the derivations in
'checks.${system}'.
Fixes #2861. (That issue also talks about access to ~/.config/nixpkgs
and --arg, but I think it's reasonable to say that flakes shouldn't
support those.)
The alternative to attribute selection is to pass the system type as
an argument to the flake's 'outputs' function, e.g. 'outputs = { self,
nixpkgs, system }: ...'. However, that approach would be at odds with
hermetic evaluation and make it impossible to enumerate the packages
provided by a flake.
2019-10-15 15:52:10 +00:00
|
|
|
for (auto & attr : *vOutput.attrs) {
|
|
|
|
checkSystemName(attr.name, *attr.pos);
|
|
|
|
state->forceAttrs(*attr.value, *attr.pos);
|
|
|
|
for (auto & attr2 : *attr.value->attrs) {
|
|
|
|
auto drvPath = checkDerivation(
|
|
|
|
fmt("%s.%s.%s", name, attr.name, attr2.name),
|
|
|
|
*attr2.value, *attr2.pos);
|
|
|
|
if ((std::string) attr.name == settings.thisSystem.get())
|
2019-12-11 13:53:30 +00:00
|
|
|
drvPaths.emplace_back(drvPath);
|
Support non-x86_64-linux system types in flakes
A command like
$ nix run nixpkgs#hello
will now build the attribute 'packages.${system}.hello' rather than
'packages.hello'. Note that this does mean that the flake needs to
export an attribute for every system type it supports, and you can't
build on unsupported systems. So 'packages' typically looks like this:
packages = nixpkgs.lib.genAttrs ["x86_64-linux" "i686-linux"] (system: {
hello = ...;
});
The 'checks', 'defaultPackage', 'devShell', 'apps' and 'defaultApp'
outputs similarly are now attrsets that map system types to
derivations/apps. 'nix flake check' checks that the derivations for
all platforms evaluate correctly, but only builds the derivations in
'checks.${system}'.
Fixes #2861. (That issue also talks about access to ~/.config/nixpkgs
and --arg, but I think it's reasonable to say that flakes shouldn't
support those.)
The alternative to attribute selection is to pass the system type as
an argument to the flake's 'outputs' function, e.g. 'outputs = { self,
nixpkgs, system }: ...'. However, that approach would be at odds with
hermetic evaluation and make it impossible to enumerate the packages
provided by a flake.
2019-10-15 15:52:10 +00:00
|
|
|
}
|
|
|
|
}
|
2019-05-29 18:57:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
else if (name == "packages") {
|
2019-09-10 15:39:55 +00:00
|
|
|
state->forceAttrs(vOutput, pos);
|
Support non-x86_64-linux system types in flakes
A command like
$ nix run nixpkgs#hello
will now build the attribute 'packages.${system}.hello' rather than
'packages.hello'. Note that this does mean that the flake needs to
export an attribute for every system type it supports, and you can't
build on unsupported systems. So 'packages' typically looks like this:
packages = nixpkgs.lib.genAttrs ["x86_64-linux" "i686-linux"] (system: {
hello = ...;
});
The 'checks', 'defaultPackage', 'devShell', 'apps' and 'defaultApp'
outputs similarly are now attrsets that map system types to
derivations/apps. 'nix flake check' checks that the derivations for
all platforms evaluate correctly, but only builds the derivations in
'checks.${system}'.
Fixes #2861. (That issue also talks about access to ~/.config/nixpkgs
and --arg, but I think it's reasonable to say that flakes shouldn't
support those.)
The alternative to attribute selection is to pass the system type as
an argument to the flake's 'outputs' function, e.g. 'outputs = { self,
nixpkgs, system }: ...'. However, that approach would be at odds with
hermetic evaluation and make it impossible to enumerate the packages
provided by a flake.
2019-10-15 15:52:10 +00:00
|
|
|
for (auto & attr : *vOutput.attrs) {
|
|
|
|
checkSystemName(attr.name, *attr.pos);
|
|
|
|
state->forceAttrs(*attr.value, *attr.pos);
|
|
|
|
for (auto & attr2 : *attr.value->attrs)
|
|
|
|
checkDerivation(
|
|
|
|
fmt("%s.%s.%s", name, attr.name, attr2.name),
|
|
|
|
*attr2.value, *attr2.pos);
|
|
|
|
}
|
2019-05-29 15:25:41 +00:00
|
|
|
}
|
|
|
|
|
2019-06-17 15:59:57 +00:00
|
|
|
else if (name == "apps") {
|
2019-09-10 15:39:55 +00:00
|
|
|
state->forceAttrs(vOutput, pos);
|
Support non-x86_64-linux system types in flakes
A command like
$ nix run nixpkgs#hello
will now build the attribute 'packages.${system}.hello' rather than
'packages.hello'. Note that this does mean that the flake needs to
export an attribute for every system type it supports, and you can't
build on unsupported systems. So 'packages' typically looks like this:
packages = nixpkgs.lib.genAttrs ["x86_64-linux" "i686-linux"] (system: {
hello = ...;
});
The 'checks', 'defaultPackage', 'devShell', 'apps' and 'defaultApp'
outputs similarly are now attrsets that map system types to
derivations/apps. 'nix flake check' checks that the derivations for
all platforms evaluate correctly, but only builds the derivations in
'checks.${system}'.
Fixes #2861. (That issue also talks about access to ~/.config/nixpkgs
and --arg, but I think it's reasonable to say that flakes shouldn't
support those.)
The alternative to attribute selection is to pass the system type as
an argument to the flake's 'outputs' function, e.g. 'outputs = { self,
nixpkgs, system }: ...'. However, that approach would be at odds with
hermetic evaluation and make it impossible to enumerate the packages
provided by a flake.
2019-10-15 15:52:10 +00:00
|
|
|
for (auto & attr : *vOutput.attrs) {
|
|
|
|
checkSystemName(attr.name, *attr.pos);
|
|
|
|
state->forceAttrs(*attr.value, *attr.pos);
|
|
|
|
for (auto & attr2 : *attr.value->attrs)
|
|
|
|
checkApp(
|
|
|
|
fmt("%s.%s.%s", name, attr.name, attr2.name),
|
|
|
|
*attr2.value, *attr2.pos);
|
|
|
|
}
|
2019-06-17 15:59:57 +00:00
|
|
|
}
|
|
|
|
|
Support non-x86_64-linux system types in flakes
A command like
$ nix run nixpkgs#hello
will now build the attribute 'packages.${system}.hello' rather than
'packages.hello'. Note that this does mean that the flake needs to
export an attribute for every system type it supports, and you can't
build on unsupported systems. So 'packages' typically looks like this:
packages = nixpkgs.lib.genAttrs ["x86_64-linux" "i686-linux"] (system: {
hello = ...;
});
The 'checks', 'defaultPackage', 'devShell', 'apps' and 'defaultApp'
outputs similarly are now attrsets that map system types to
derivations/apps. 'nix flake check' checks that the derivations for
all platforms evaluate correctly, but only builds the derivations in
'checks.${system}'.
Fixes #2861. (That issue also talks about access to ~/.config/nixpkgs
and --arg, but I think it's reasonable to say that flakes shouldn't
support those.)
The alternative to attribute selection is to pass the system type as
an argument to the flake's 'outputs' function, e.g. 'outputs = { self,
nixpkgs, system }: ...'. However, that approach would be at odds with
hermetic evaluation and make it impossible to enumerate the packages
provided by a flake.
2019-10-15 15:52:10 +00:00
|
|
|
else if (name == "defaultPackage" || name == "devShell") {
|
|
|
|
state->forceAttrs(vOutput, pos);
|
|
|
|
for (auto & attr : *vOutput.attrs) {
|
|
|
|
checkSystemName(attr.name, *attr.pos);
|
|
|
|
checkDerivation(
|
|
|
|
fmt("%s.%s", name, attr.name),
|
|
|
|
*attr.value, *attr.pos);
|
|
|
|
}
|
|
|
|
}
|
2019-05-29 18:57:08 +00:00
|
|
|
|
Support non-x86_64-linux system types in flakes
A command like
$ nix run nixpkgs#hello
will now build the attribute 'packages.${system}.hello' rather than
'packages.hello'. Note that this does mean that the flake needs to
export an attribute for every system type it supports, and you can't
build on unsupported systems. So 'packages' typically looks like this:
packages = nixpkgs.lib.genAttrs ["x86_64-linux" "i686-linux"] (system: {
hello = ...;
});
The 'checks', 'defaultPackage', 'devShell', 'apps' and 'defaultApp'
outputs similarly are now attrsets that map system types to
derivations/apps. 'nix flake check' checks that the derivations for
all platforms evaluate correctly, but only builds the derivations in
'checks.${system}'.
Fixes #2861. (That issue also talks about access to ~/.config/nixpkgs
and --arg, but I think it's reasonable to say that flakes shouldn't
support those.)
The alternative to attribute selection is to pass the system type as
an argument to the flake's 'outputs' function, e.g. 'outputs = { self,
nixpkgs, system }: ...'. However, that approach would be at odds with
hermetic evaluation and make it impossible to enumerate the packages
provided by a flake.
2019-10-15 15:52:10 +00:00
|
|
|
else if (name == "defaultApp") {
|
|
|
|
state->forceAttrs(vOutput, pos);
|
|
|
|
for (auto & attr : *vOutput.attrs) {
|
|
|
|
checkSystemName(attr.name, *attr.pos);
|
|
|
|
checkApp(
|
|
|
|
fmt("%s.%s", name, attr.name),
|
|
|
|
*attr.value, *attr.pos);
|
|
|
|
}
|
|
|
|
}
|
2019-06-17 15:59:57 +00:00
|
|
|
|
Support non-x86_64-linux system types in flakes
A command like
$ nix run nixpkgs#hello
will now build the attribute 'packages.${system}.hello' rather than
'packages.hello'. Note that this does mean that the flake needs to
export an attribute for every system type it supports, and you can't
build on unsupported systems. So 'packages' typically looks like this:
packages = nixpkgs.lib.genAttrs ["x86_64-linux" "i686-linux"] (system: {
hello = ...;
});
The 'checks', 'defaultPackage', 'devShell', 'apps' and 'defaultApp'
outputs similarly are now attrsets that map system types to
derivations/apps. 'nix flake check' checks that the derivations for
all platforms evaluate correctly, but only builds the derivations in
'checks.${system}'.
Fixes #2861. (That issue also talks about access to ~/.config/nixpkgs
and --arg, but I think it's reasonable to say that flakes shouldn't
support those.)
The alternative to attribute selection is to pass the system type as
an argument to the flake's 'outputs' function, e.g. 'outputs = { self,
nixpkgs, system }: ...'. However, that approach would be at odds with
hermetic evaluation and make it impossible to enumerate the packages
provided by a flake.
2019-10-15 15:52:10 +00:00
|
|
|
else if (name == "legacyPackages") {
|
|
|
|
state->forceAttrs(vOutput, pos);
|
|
|
|
for (auto & attr : *vOutput.attrs) {
|
|
|
|
checkSystemName(attr.name, *attr.pos);
|
|
|
|
// FIXME: do getDerivations?
|
|
|
|
}
|
|
|
|
}
|
2019-06-18 07:45:14 +00:00
|
|
|
|
2019-09-10 12:52:22 +00:00
|
|
|
else if (name == "overlay")
|
2019-09-10 13:25:10 +00:00
|
|
|
checkOverlay(name, vOutput, pos);
|
|
|
|
|
|
|
|
else if (name == "overlays") {
|
2019-09-10 15:39:55 +00:00
|
|
|
state->forceAttrs(vOutput, pos);
|
2019-09-10 13:25:10 +00:00
|
|
|
for (auto & attr : *vOutput.attrs)
|
Support non-x86_64-linux system types in flakes
A command like
$ nix run nixpkgs#hello
will now build the attribute 'packages.${system}.hello' rather than
'packages.hello'. Note that this does mean that the flake needs to
export an attribute for every system type it supports, and you can't
build on unsupported systems. So 'packages' typically looks like this:
packages = nixpkgs.lib.genAttrs ["x86_64-linux" "i686-linux"] (system: {
hello = ...;
});
The 'checks', 'defaultPackage', 'devShell', 'apps' and 'defaultApp'
outputs similarly are now attrsets that map system types to
derivations/apps. 'nix flake check' checks that the derivations for
all platforms evaluate correctly, but only builds the derivations in
'checks.${system}'.
Fixes #2861. (That issue also talks about access to ~/.config/nixpkgs
and --arg, but I think it's reasonable to say that flakes shouldn't
support those.)
The alternative to attribute selection is to pass the system type as
an argument to the flake's 'outputs' function, e.g. 'outputs = { self,
nixpkgs, system }: ...'. However, that approach would be at odds with
hermetic evaluation and make it impossible to enumerate the packages
provided by a flake.
2019-10-15 15:52:10 +00:00
|
|
|
checkOverlay(fmt("%s.%s", name, attr.name),
|
2019-09-10 13:25:10 +00:00
|
|
|
*attr.value, *attr.pos);
|
|
|
|
}
|
|
|
|
|
|
|
|
else if (name == "nixosModule")
|
|
|
|
checkModule(name, vOutput, pos);
|
|
|
|
|
|
|
|
else if (name == "nixosModules") {
|
2019-09-10 15:39:55 +00:00
|
|
|
state->forceAttrs(vOutput, pos);
|
2019-09-10 13:25:10 +00:00
|
|
|
for (auto & attr : *vOutput.attrs)
|
Support non-x86_64-linux system types in flakes
A command like
$ nix run nixpkgs#hello
will now build the attribute 'packages.${system}.hello' rather than
'packages.hello'. Note that this does mean that the flake needs to
export an attribute for every system type it supports, and you can't
build on unsupported systems. So 'packages' typically looks like this:
packages = nixpkgs.lib.genAttrs ["x86_64-linux" "i686-linux"] (system: {
hello = ...;
});
The 'checks', 'defaultPackage', 'devShell', 'apps' and 'defaultApp'
outputs similarly are now attrsets that map system types to
derivations/apps. 'nix flake check' checks that the derivations for
all platforms evaluate correctly, but only builds the derivations in
'checks.${system}'.
Fixes #2861. (That issue also talks about access to ~/.config/nixpkgs
and --arg, but I think it's reasonable to say that flakes shouldn't
support those.)
The alternative to attribute selection is to pass the system type as
an argument to the flake's 'outputs' function, e.g. 'outputs = { self,
nixpkgs, system }: ...'. However, that approach would be at odds with
hermetic evaluation and make it impossible to enumerate the packages
provided by a flake.
2019-10-15 15:52:10 +00:00
|
|
|
checkModule(fmt("%s.%s", name, attr.name),
|
2019-09-10 13:25:10 +00:00
|
|
|
*attr.value, *attr.pos);
|
|
|
|
}
|
2019-09-10 12:52:22 +00:00
|
|
|
|
2019-09-19 18:15:42 +00:00
|
|
|
else if (name == "nixosConfigurations") {
|
|
|
|
state->forceAttrs(vOutput, pos);
|
|
|
|
for (auto & attr : *vOutput.attrs)
|
Support non-x86_64-linux system types in flakes
A command like
$ nix run nixpkgs#hello
will now build the attribute 'packages.${system}.hello' rather than
'packages.hello'. Note that this does mean that the flake needs to
export an attribute for every system type it supports, and you can't
build on unsupported systems. So 'packages' typically looks like this:
packages = nixpkgs.lib.genAttrs ["x86_64-linux" "i686-linux"] (system: {
hello = ...;
});
The 'checks', 'defaultPackage', 'devShell', 'apps' and 'defaultApp'
outputs similarly are now attrsets that map system types to
derivations/apps. 'nix flake check' checks that the derivations for
all platforms evaluate correctly, but only builds the derivations in
'checks.${system}'.
Fixes #2861. (That issue also talks about access to ~/.config/nixpkgs
and --arg, but I think it's reasonable to say that flakes shouldn't
support those.)
The alternative to attribute selection is to pass the system type as
an argument to the flake's 'outputs' function, e.g. 'outputs = { self,
nixpkgs, system }: ...'. However, that approach would be at odds with
hermetic evaluation and make it impossible to enumerate the packages
provided by a flake.
2019-10-15 15:52:10 +00:00
|
|
|
checkNixOSConfiguration(fmt("%s.%s", name, attr.name),
|
2019-09-19 18:15:42 +00:00
|
|
|
*attr.value, *attr.pos);
|
|
|
|
}
|
|
|
|
|
2019-09-10 15:39:55 +00:00
|
|
|
else if (name == "hydraJobs")
|
|
|
|
checkHydraJobs(name, vOutput, pos);
|
|
|
|
|
2019-06-17 16:05:32 +00:00
|
|
|
else
|
|
|
|
warn("unknown flake output '%s'", name);
|
|
|
|
|
2019-05-29 15:25:41 +00:00
|
|
|
} catch (Error & e) {
|
|
|
|
e.addPrefix(fmt("while checking flake output '" ANSI_BOLD "%s" ANSI_NORMAL "':\n", name));
|
|
|
|
throw;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2019-06-17 15:59:57 +00:00
|
|
|
if (build && !drvPaths.empty()) {
|
2019-05-29 15:25:41 +00:00
|
|
|
Activity act(*logger, lvlInfo, actUnknown, "running flake checks");
|
|
|
|
store->buildPaths(drvPaths);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2019-03-10 06:05:05 +00:00
|
|
|
struct CmdFlakeAdd : MixEvalArgs, Command
|
|
|
|
{
|
2020-01-21 15:27:53 +00:00
|
|
|
std::string fromUrl, toUrl;
|
2019-03-10 06:05:05 +00:00
|
|
|
|
|
|
|
std::string description() override
|
|
|
|
{
|
|
|
|
return "upsert flake in user flake registry";
|
|
|
|
}
|
|
|
|
|
|
|
|
CmdFlakeAdd()
|
|
|
|
{
|
2020-01-21 15:27:53 +00:00
|
|
|
expectArg("from-url", &fromUrl);
|
|
|
|
expectArg("to-url", &toUrl);
|
2019-03-10 06:05:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void run() override
|
|
|
|
{
|
2020-01-21 15:27:53 +00:00
|
|
|
auto fromRef = parseFlakeRef(fromUrl);
|
|
|
|
auto toRef = parseFlakeRef(toUrl);
|
2020-03-17 19:54:36 +00:00
|
|
|
fetchers::Attrs extraAttrs;
|
2020-02-20 22:44:06 +00:00
|
|
|
if (toRef.subdir != "") extraAttrs["dir"] = toRef.subdir;
|
2020-01-21 15:27:53 +00:00
|
|
|
auto userRegistry = fetchers::getUserRegistry();
|
|
|
|
userRegistry->remove(fromRef.input);
|
2020-02-20 21:14:44 +00:00
|
|
|
userRegistry->add(fromRef.input, toRef.input, extraAttrs);
|
2020-01-21 15:27:53 +00:00
|
|
|
userRegistry->write(fetchers::getUserRegistryPath());
|
2019-03-10 06:05:05 +00:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
struct CmdFlakeRemove : virtual Args, MixEvalArgs, Command
|
|
|
|
{
|
2020-01-21 15:27:53 +00:00
|
|
|
std::string url;
|
2019-03-10 06:05:05 +00:00
|
|
|
|
|
|
|
std::string description() override
|
|
|
|
{
|
|
|
|
return "remove flake from user flake registry";
|
|
|
|
}
|
|
|
|
|
|
|
|
CmdFlakeRemove()
|
|
|
|
{
|
2020-01-21 15:27:53 +00:00
|
|
|
expectArg("url", &url);
|
2019-03-10 06:05:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void run() override
|
|
|
|
{
|
2020-01-21 15:27:53 +00:00
|
|
|
auto userRegistry = fetchers::getUserRegistry();
|
|
|
|
userRegistry->remove(parseFlakeRef(url).input);
|
|
|
|
userRegistry->write(fetchers::getUserRegistryPath());
|
2019-03-10 06:05:05 +00:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2019-05-16 20:48:16 +00:00
|
|
|
struct CmdFlakePin : virtual Args, EvalCommand
|
2019-03-10 06:05:05 +00:00
|
|
|
{
|
2020-01-21 15:27:53 +00:00
|
|
|
std::string url;
|
2019-03-10 06:05:05 +00:00
|
|
|
|
|
|
|
std::string description() override
|
|
|
|
{
|
2020-01-21 15:27:53 +00:00
|
|
|
return "pin a flake to its current version in user flake registry";
|
2019-03-10 06:05:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
CmdFlakePin()
|
|
|
|
{
|
2020-01-21 15:27:53 +00:00
|
|
|
expectArg("url", &url);
|
2019-03-10 06:05:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void run(nix::ref<nix::Store> store) override
|
|
|
|
{
|
2020-01-21 15:27:53 +00:00
|
|
|
auto ref = parseFlakeRef(url);
|
|
|
|
auto userRegistry = fetchers::getUserRegistry();
|
|
|
|
userRegistry->remove(ref.input);
|
|
|
|
auto [tree, resolved] = ref.resolve(store).input->fetchTree(store);
|
2020-03-17 19:54:36 +00:00
|
|
|
fetchers::Attrs extraAttrs;
|
2020-02-20 22:44:06 +00:00
|
|
|
if (ref.subdir != "") extraAttrs["dir"] = ref.subdir;
|
2020-02-20 21:14:44 +00:00
|
|
|
userRegistry->add(ref.input, resolved, extraAttrs);
|
2019-03-10 06:05:05 +00:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2019-04-08 21:36:12 +00:00
|
|
|
struct CmdFlakeInit : virtual Args, Command
|
|
|
|
{
|
|
|
|
std::string description() override
|
|
|
|
{
|
|
|
|
return "create a skeleton 'flake.nix' file in the current directory";
|
|
|
|
}
|
|
|
|
|
|
|
|
void run() override
|
|
|
|
{
|
|
|
|
Path flakeDir = absPath(".");
|
|
|
|
|
|
|
|
if (!pathExists(flakeDir + "/.git"))
|
|
|
|
throw Error("the directory '%s' is not a Git repository", flakeDir);
|
|
|
|
|
|
|
|
Path flakePath = flakeDir + "/flake.nix";
|
|
|
|
|
|
|
|
if (pathExists(flakePath))
|
|
|
|
throw Error("file '%s' already exists", flakePath);
|
|
|
|
|
|
|
|
writeFile(flakePath,
|
2019-04-08 21:39:38 +00:00
|
|
|
#include "flake-template.nix.gen.hh"
|
|
|
|
);
|
2019-04-08 21:36:12 +00:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2019-05-16 20:48:16 +00:00
|
|
|
struct CmdFlakeClone : FlakeCommand
|
2019-03-21 08:30:16 +00:00
|
|
|
{
|
2019-05-22 11:46:07 +00:00
|
|
|
Path destDir;
|
2019-03-21 08:30:16 +00:00
|
|
|
|
|
|
|
std::string description() override
|
|
|
|
{
|
|
|
|
return "clone flake repository";
|
|
|
|
}
|
|
|
|
|
|
|
|
CmdFlakeClone()
|
|
|
|
{
|
2020-01-21 15:27:53 +00:00
|
|
|
mkFlag()
|
|
|
|
.shortName('f')
|
|
|
|
.longName("dest")
|
|
|
|
.label("path")
|
|
|
|
.description("destination path")
|
|
|
|
.dest(&destDir);
|
2019-03-21 08:30:16 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void run(nix::ref<nix::Store> store) override
|
|
|
|
{
|
2020-01-21 15:27:53 +00:00
|
|
|
if (destDir.empty())
|
|
|
|
throw Error("missing flag '--dest'");
|
2019-03-21 08:30:16 +00:00
|
|
|
|
2020-01-21 15:27:53 +00:00
|
|
|
getFlakeRef().resolve(store).input->clone(destDir);
|
2019-03-21 08:30:16 +00:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2020-01-29 23:58:55 +00:00
|
|
|
struct CmdFlakeArchive : FlakeCommand, MixJSON, MixDryRun
|
|
|
|
{
|
|
|
|
std::string dstUri;
|
|
|
|
|
|
|
|
CmdFlakeArchive()
|
|
|
|
{
|
|
|
|
mkFlag()
|
|
|
|
.longName("to")
|
|
|
|
.labels({"store-uri"})
|
|
|
|
.description("URI of the destination Nix store")
|
|
|
|
.dest(&dstUri);
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string description() override
|
|
|
|
{
|
|
|
|
return "copy a flake and all its inputs to a store";
|
|
|
|
}
|
|
|
|
|
|
|
|
Examples examples() override
|
|
|
|
{
|
|
|
|
return {
|
|
|
|
Example{
|
|
|
|
"To copy the dwarffs flake and its dependencies to a binary cache:",
|
|
|
|
"nix flake archive --to file:///tmp/my-cache dwarffs"
|
|
|
|
},
|
|
|
|
Example{
|
|
|
|
"To fetch the dwarffs flake and its dependencies to the local Nix store:",
|
|
|
|
"nix flake archive dwarffs"
|
|
|
|
},
|
|
|
|
Example{
|
|
|
|
"To print the store paths of the flake sources of NixOps without fetching them:",
|
|
|
|
"nix flake archive --json --dry-run nixops"
|
|
|
|
},
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
void run(nix::ref<nix::Store> store) override
|
|
|
|
{
|
|
|
|
auto flake = lockFlake();
|
|
|
|
|
2020-01-30 00:10:26 +00:00
|
|
|
auto jsonRoot = json ? std::optional<JSONObject>(std::cout) : std::nullopt;
|
2020-01-29 23:58:55 +00:00
|
|
|
|
|
|
|
StorePathSet sources;
|
|
|
|
|
|
|
|
sources.insert(flake.flake.sourceInfo->storePath.clone());
|
|
|
|
if (jsonRoot)
|
|
|
|
jsonRoot->attr("path", store->printStorePath(flake.flake.sourceInfo->storePath));
|
|
|
|
|
2020-03-12 21:06:57 +00:00
|
|
|
// FIXME: use graph output, handle cycles.
|
|
|
|
std::function<void(const Node & node, std::optional<JSONObject> & jsonObj)> traverse;
|
|
|
|
traverse = [&](const Node & node, std::optional<JSONObject> & jsonObj)
|
2020-01-29 23:58:55 +00:00
|
|
|
{
|
|
|
|
auto jsonObj2 = jsonObj ? jsonObj->object("inputs") : std::optional<JSONObject>();
|
2020-03-12 21:06:57 +00:00
|
|
|
for (auto & input : node.inputs) {
|
|
|
|
auto lockedInput = std::dynamic_pointer_cast<const LockedNode>(input.second);
|
|
|
|
assert(lockedInput);
|
2020-01-29 23:58:55 +00:00
|
|
|
auto jsonObj3 = jsonObj2 ? jsonObj2->object(input.first) : std::optional<JSONObject>();
|
|
|
|
if (!dryRun)
|
2020-03-12 21:06:57 +00:00
|
|
|
lockedInput->lockedRef.input->fetchTree(store);
|
|
|
|
auto storePath = lockedInput->computeStorePath(*store);
|
2020-01-29 23:58:55 +00:00
|
|
|
if (jsonObj3)
|
|
|
|
jsonObj3->attr("path", store->printStorePath(storePath));
|
|
|
|
sources.insert(std::move(storePath));
|
2020-03-12 21:06:57 +00:00
|
|
|
traverse(*lockedInput, jsonObj3);
|
2020-01-29 23:58:55 +00:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2020-03-12 21:06:57 +00:00
|
|
|
traverse(*flake.lockFile.root, jsonRoot);
|
2020-01-29 23:58:55 +00:00
|
|
|
|
|
|
|
if (!dryRun && !dstUri.empty()) {
|
|
|
|
ref<Store> dstStore = dstUri.empty() ? openStore() : openStore(dstUri);
|
|
|
|
copyPaths(store, dstStore, sources);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2020-04-17 11:57:02 +00:00
|
|
|
// FIXME: inefficient representation of attrs
|
2020-04-16 23:02:29 +00:00
|
|
|
static const char * schema = R"sql(
|
|
|
|
create table if not exists Attributes (
|
2020-04-17 21:04:21 +00:00
|
|
|
parent integer not null,
|
|
|
|
name text,
|
2020-04-17 11:57:02 +00:00
|
|
|
type integer not null,
|
2020-04-17 21:04:21 +00:00
|
|
|
value text,
|
|
|
|
primary key (parent, name)
|
2020-04-16 23:02:29 +00:00
|
|
|
);
|
|
|
|
)sql";
|
|
|
|
|
|
|
|
enum AttrType {
|
2020-04-17 21:04:21 +00:00
|
|
|
Placeholder = 0,
|
2020-04-19 21:07:06 +00:00
|
|
|
FullAttrs = 1,
|
2020-04-16 23:02:29 +00:00
|
|
|
String = 2,
|
2020-04-19 21:07:06 +00:00
|
|
|
Missing = 3,
|
|
|
|
Misc = 4,
|
|
|
|
Failed = 5,
|
2020-04-16 23:02:29 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
struct AttrDb
|
|
|
|
{
|
|
|
|
struct State
|
|
|
|
{
|
|
|
|
SQLite db;
|
|
|
|
SQLiteStmt insertAttribute;
|
|
|
|
SQLiteStmt queryAttribute;
|
2020-04-17 21:04:21 +00:00
|
|
|
SQLiteStmt queryAttributes;
|
2020-04-17 21:15:34 +00:00
|
|
|
std::unique_ptr<SQLiteTxn> txn;
|
2020-04-16 23:02:29 +00:00
|
|
|
};
|
|
|
|
|
2020-04-17 21:04:21 +00:00
|
|
|
struct placeholder_t {};
|
2020-04-19 21:07:06 +00:00
|
|
|
struct missing_t {};
|
|
|
|
struct misc_t {};
|
|
|
|
struct failed_t {};
|
2020-04-17 21:04:21 +00:00
|
|
|
typedef uint64_t AttrId;
|
|
|
|
typedef std::pair<AttrId, Symbol> AttrKey;
|
2020-04-19 21:07:06 +00:00
|
|
|
typedef std::variant<std::vector<Symbol>, std::string, placeholder_t, missing_t, misc_t, failed_t> AttrValue;
|
2020-04-17 21:04:21 +00:00
|
|
|
|
2020-04-16 23:02:29 +00:00
|
|
|
std::unique_ptr<Sync<State>> _state;
|
|
|
|
|
2020-04-17 11:57:02 +00:00
|
|
|
AttrDb(const Fingerprint & fingerprint)
|
2020-04-16 23:02:29 +00:00
|
|
|
: _state(std::make_unique<Sync<State>>())
|
|
|
|
{
|
|
|
|
auto state(_state->lock());
|
|
|
|
|
2020-04-17 11:57:02 +00:00
|
|
|
Path cacheDir = getCacheDir() + "/nix/eval-cache-v2";
|
|
|
|
createDirs(cacheDir);
|
|
|
|
|
|
|
|
Path dbPath = cacheDir + "/" + fingerprint.to_string(Base16, false) + ".sqlite";
|
2020-04-16 23:02:29 +00:00
|
|
|
|
|
|
|
state->db = SQLite(dbPath);
|
|
|
|
state->db.isCache();
|
|
|
|
state->db.exec(schema);
|
|
|
|
|
|
|
|
state->insertAttribute.create(state->db,
|
2020-04-17 21:04:21 +00:00
|
|
|
"insert or replace into Attributes(parent, name, type, value) values (?, ?, ?, ?)");
|
2020-04-16 23:02:29 +00:00
|
|
|
|
|
|
|
state->queryAttribute.create(state->db,
|
2020-04-17 21:04:21 +00:00
|
|
|
"select rowid, type, value from Attributes where parent = ? and name = ?");
|
|
|
|
|
|
|
|
state->queryAttributes.create(state->db,
|
|
|
|
"select name from Attributes where parent = ?");
|
2020-04-17 21:15:34 +00:00
|
|
|
|
|
|
|
state->txn = std::make_unique<SQLiteTxn>(state->db);
|
|
|
|
}
|
|
|
|
|
|
|
|
~AttrDb()
|
|
|
|
{
|
|
|
|
try {
|
|
|
|
auto state(_state->lock());
|
|
|
|
state->txn->commit();
|
|
|
|
state->txn.reset();
|
|
|
|
} catch (...) {
|
|
|
|
ignoreException();
|
|
|
|
}
|
2020-04-16 23:02:29 +00:00
|
|
|
}
|
|
|
|
|
2020-04-19 21:07:06 +00:00
|
|
|
AttrId setAttrs(
|
2020-04-17 21:04:21 +00:00
|
|
|
AttrKey key,
|
2020-04-16 23:02:29 +00:00
|
|
|
const std::vector<Symbol> & attrs)
|
|
|
|
{
|
|
|
|
auto state(_state->lock());
|
|
|
|
|
|
|
|
state->insertAttribute.use()
|
2020-04-17 21:04:21 +00:00
|
|
|
(key.first)
|
|
|
|
(key.second)
|
2020-04-19 21:07:06 +00:00
|
|
|
(AttrType::FullAttrs)
|
2020-04-17 21:04:21 +00:00
|
|
|
(0, false).exec();
|
|
|
|
|
|
|
|
AttrId rowId = state->db.getLastInsertedRowId();
|
|
|
|
assert(rowId);
|
|
|
|
|
|
|
|
for (auto & attr : attrs)
|
2020-04-19 21:07:06 +00:00
|
|
|
state->insertAttribute.use()
|
2020-04-17 21:04:21 +00:00
|
|
|
(rowId)
|
2020-04-19 21:07:06 +00:00
|
|
|
(attr)
|
|
|
|
(AttrType::Placeholder)
|
|
|
|
(0, false).exec();
|
2020-04-17 21:04:21 +00:00
|
|
|
|
|
|
|
return rowId;
|
2020-04-16 23:02:29 +00:00
|
|
|
}
|
|
|
|
|
2020-04-19 21:07:06 +00:00
|
|
|
AttrId setString(
|
2020-04-17 21:04:21 +00:00
|
|
|
AttrKey key,
|
2020-04-16 23:02:29 +00:00
|
|
|
std::string_view s)
|
|
|
|
{
|
|
|
|
auto state(_state->lock());
|
|
|
|
|
|
|
|
state->insertAttribute.use()
|
2020-04-17 21:04:21 +00:00
|
|
|
(key.first)
|
|
|
|
(key.second)
|
2020-04-16 23:02:29 +00:00
|
|
|
(AttrType::String)
|
|
|
|
(s).exec();
|
|
|
|
|
2020-04-17 21:04:21 +00:00
|
|
|
return state->db.getLastInsertedRowId();
|
|
|
|
}
|
2020-04-16 23:02:29 +00:00
|
|
|
|
2020-04-19 21:07:06 +00:00
|
|
|
AttrId setPlaceholder(AttrKey key)
|
|
|
|
{
|
|
|
|
auto state(_state->lock());
|
|
|
|
|
|
|
|
state->insertAttribute.use()
|
|
|
|
(key.first)
|
|
|
|
(key.second)
|
|
|
|
(AttrType::Placeholder)
|
|
|
|
(0, false).exec();
|
|
|
|
|
|
|
|
return state->db.getLastInsertedRowId();
|
|
|
|
}
|
|
|
|
|
|
|
|
AttrId setMissing(AttrKey key)
|
|
|
|
{
|
|
|
|
auto state(_state->lock());
|
|
|
|
|
|
|
|
state->insertAttribute.use()
|
|
|
|
(key.first)
|
|
|
|
(key.second)
|
|
|
|
(AttrType::Missing)
|
|
|
|
(0, false).exec();
|
|
|
|
|
|
|
|
return state->db.getLastInsertedRowId();
|
|
|
|
}
|
|
|
|
|
|
|
|
AttrId setMisc(AttrKey key)
|
|
|
|
{
|
|
|
|
auto state(_state->lock());
|
|
|
|
|
|
|
|
state->insertAttribute.use()
|
|
|
|
(key.first)
|
|
|
|
(key.second)
|
|
|
|
(AttrType::Misc)
|
|
|
|
(0, false).exec();
|
|
|
|
|
|
|
|
return state->db.getLastInsertedRowId();
|
|
|
|
}
|
|
|
|
|
|
|
|
AttrId setFailed(AttrKey key)
|
|
|
|
{
|
|
|
|
auto state(_state->lock());
|
|
|
|
|
|
|
|
state->insertAttribute.use()
|
|
|
|
(key.first)
|
|
|
|
(key.second)
|
|
|
|
(AttrType::Failed)
|
|
|
|
(0, false).exec();
|
|
|
|
|
|
|
|
return state->db.getLastInsertedRowId();
|
|
|
|
}
|
|
|
|
|
2020-04-17 21:04:21 +00:00
|
|
|
std::optional<std::pair<AttrId, AttrValue>> getAttr(
|
|
|
|
AttrKey key,
|
2020-04-16 23:02:29 +00:00
|
|
|
SymbolTable & symbols)
|
|
|
|
{
|
|
|
|
auto state(_state->lock());
|
|
|
|
|
2020-04-17 21:04:21 +00:00
|
|
|
auto queryAttribute(state->queryAttribute.use()(key.first)(key.second));
|
2020-04-16 23:02:29 +00:00
|
|
|
if (!queryAttribute.next()) return {};
|
|
|
|
|
2020-04-17 21:04:21 +00:00
|
|
|
auto rowId = (AttrType) queryAttribute.getInt(0);
|
|
|
|
auto type = (AttrType) queryAttribute.getInt(1);
|
2020-04-16 23:02:29 +00:00
|
|
|
|
2020-04-17 21:04:21 +00:00
|
|
|
if (type == AttrType::Placeholder)
|
|
|
|
return {{rowId, placeholder_t()}};
|
2020-04-19 21:07:06 +00:00
|
|
|
else if (type == AttrType::FullAttrs) {
|
2020-04-17 21:04:21 +00:00
|
|
|
// FIXME: expensive, should separate this out.
|
2020-04-16 23:02:29 +00:00
|
|
|
std::vector<Symbol> attrs;
|
2020-04-17 21:04:21 +00:00
|
|
|
auto queryAttributes(state->queryAttributes.use()(rowId));
|
|
|
|
while (queryAttributes.next())
|
|
|
|
attrs.push_back(symbols.create(queryAttributes.getStr(0)));
|
|
|
|
return {{rowId, attrs}};
|
2020-04-16 23:02:29 +00:00
|
|
|
} else if (type == AttrType::String) {
|
2020-04-17 21:04:21 +00:00
|
|
|
return {{rowId, queryAttribute.getStr(2)}};
|
2020-04-19 21:07:06 +00:00
|
|
|
} else if (type == AttrType::Missing) {
|
|
|
|
return {{rowId, missing_t()}};
|
|
|
|
} else if (type == AttrType::Misc) {
|
|
|
|
return {{rowId, misc_t()}};
|
|
|
|
} else if (type == AttrType::Failed) {
|
|
|
|
return {{rowId, failed_t()}};
|
2020-04-16 23:02:29 +00:00
|
|
|
} else
|
|
|
|
throw Error("unexpected type in evaluation cache");
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
struct AttrCursor;
|
|
|
|
|
|
|
|
struct AttrRoot : std::enable_shared_from_this<AttrRoot>
|
2020-04-16 13:36:15 +00:00
|
|
|
{
|
2020-04-16 23:02:29 +00:00
|
|
|
std::shared_ptr<AttrDb> db;
|
2020-04-16 13:36:15 +00:00
|
|
|
EvalState & state;
|
2020-04-16 23:02:29 +00:00
|
|
|
typedef std::function<Value *()> RootLoader;
|
|
|
|
RootLoader rootLoader;
|
2020-04-16 13:36:15 +00:00
|
|
|
RootValue value;
|
|
|
|
|
2020-04-17 11:57:02 +00:00
|
|
|
AttrRoot(std::shared_ptr<AttrDb> db, EvalState & state, RootLoader rootLoader)
|
2020-04-16 23:02:29 +00:00
|
|
|
: db(db)
|
|
|
|
, state(state)
|
|
|
|
, rootLoader(rootLoader)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
Value * getRootValue()
|
|
|
|
{
|
|
|
|
if (!value) {
|
2020-04-19 21:07:06 +00:00
|
|
|
debug("getting root value");
|
2020-04-16 23:02:29 +00:00
|
|
|
value = allocRootValue(rootLoader());
|
|
|
|
}
|
|
|
|
return *value;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::shared_ptr<AttrCursor> getRoot()
|
|
|
|
{
|
|
|
|
return std::make_shared<AttrCursor>(ref(shared_from_this()), std::nullopt);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
struct AttrCursor : std::enable_shared_from_this<AttrCursor>
|
|
|
|
{
|
|
|
|
ref<AttrRoot> root;
|
|
|
|
typedef std::optional<std::pair<std::shared_ptr<AttrCursor>, Symbol>> Parent;
|
|
|
|
Parent parent;
|
|
|
|
RootValue _value;
|
2020-04-17 21:04:21 +00:00
|
|
|
std::optional<std::pair<AttrDb::AttrId, AttrDb::AttrValue>> cachedValue;
|
2020-04-16 23:02:29 +00:00
|
|
|
|
2020-04-16 13:36:15 +00:00
|
|
|
AttrCursor(
|
2020-04-16 23:02:29 +00:00
|
|
|
ref<AttrRoot> root,
|
2020-04-16 13:36:15 +00:00
|
|
|
Parent parent,
|
2020-04-17 11:57:02 +00:00
|
|
|
Value * value = nullptr,
|
2020-04-17 21:04:21 +00:00
|
|
|
std::optional<std::pair<AttrDb::AttrId, AttrDb::AttrValue>> && cachedValue = {})
|
2020-04-17 11:57:02 +00:00
|
|
|
: root(root), parent(parent), cachedValue(std::move(cachedValue))
|
2020-04-16 23:02:29 +00:00
|
|
|
{
|
|
|
|
if (value)
|
|
|
|
_value = allocRootValue(value);
|
|
|
|
}
|
|
|
|
|
2020-04-17 21:04:21 +00:00
|
|
|
AttrDb::AttrKey getAttrKey()
|
|
|
|
{
|
|
|
|
if (!parent)
|
|
|
|
return {0, root->state.sEpsilon};
|
|
|
|
if (!parent->first->cachedValue) {
|
|
|
|
parent->first->cachedValue = root->db->getAttr(
|
|
|
|
parent->first->getAttrKey(), root->state.symbols);
|
|
|
|
assert(parent->first->cachedValue);
|
|
|
|
}
|
|
|
|
return {parent->first->cachedValue->first, parent->second};
|
|
|
|
}
|
|
|
|
|
2020-04-16 23:02:29 +00:00
|
|
|
Value & getValue()
|
2020-04-16 13:36:15 +00:00
|
|
|
{
|
2020-04-16 23:02:29 +00:00
|
|
|
if (!_value) {
|
|
|
|
if (parent) {
|
|
|
|
auto & vParent = parent->first->getValue();
|
|
|
|
root->state.forceAttrs(vParent);
|
|
|
|
auto attr = vParent.attrs->get(parent->second);
|
|
|
|
if (!attr)
|
|
|
|
throw Error("attribute '%s' is unexpectedly missing", getAttrPathStr());
|
|
|
|
_value = allocRootValue(attr->value);
|
|
|
|
} else
|
|
|
|
_value = allocRootValue(root->getRootValue());
|
|
|
|
}
|
|
|
|
return **_value;
|
2020-04-16 13:36:15 +00:00
|
|
|
}
|
|
|
|
|
2020-04-16 23:02:29 +00:00
|
|
|
std::vector<Symbol> getAttrPath() const
|
2020-04-16 13:36:15 +00:00
|
|
|
{
|
|
|
|
if (parent) {
|
|
|
|
auto attrPath = parent->first->getAttrPath();
|
|
|
|
attrPath.push_back(parent->second);
|
|
|
|
return attrPath;
|
|
|
|
} else
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
|
2020-04-16 23:02:29 +00:00
|
|
|
std::vector<Symbol> getAttrPath(Symbol name) const
|
|
|
|
{
|
|
|
|
auto attrPath = getAttrPath();
|
|
|
|
attrPath.push_back(name);
|
|
|
|
return attrPath;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string getAttrPathStr() const
|
|
|
|
{
|
|
|
|
return concatStringsSep(".", getAttrPath());
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string getAttrPathStr(Symbol name) const
|
|
|
|
{
|
|
|
|
return concatStringsSep(".", getAttrPath(name));
|
|
|
|
}
|
|
|
|
|
2020-04-19 21:07:06 +00:00
|
|
|
Value & forceValue()
|
|
|
|
{
|
|
|
|
debug("evaluating uncached attribute %s", getAttrPathStr());
|
|
|
|
|
|
|
|
auto & v = getValue();
|
|
|
|
|
|
|
|
try {
|
|
|
|
root->state.forceValue(v);
|
|
|
|
} catch (EvalError &) {
|
|
|
|
debug("setting '%s' to failed", getAttrPathStr());
|
|
|
|
if (root->db)
|
|
|
|
cachedValue = {root->db->setFailed(getAttrKey()), AttrDb::failed_t()};
|
|
|
|
throw;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (root->db && (!cachedValue || std::get_if<AttrDb::placeholder_t>(&cachedValue->second))) {
|
|
|
|
if (v.type == tString)
|
|
|
|
cachedValue = {root->db->setString(getAttrKey(), v.string.s), v.string.s};
|
|
|
|
else if (v.type == tAttrs)
|
|
|
|
; // FIXME: do something?
|
|
|
|
else
|
|
|
|
cachedValue = {root->db->setMisc(getAttrKey()), AttrDb::misc_t()};
|
|
|
|
}
|
|
|
|
|
|
|
|
return v;
|
|
|
|
}
|
|
|
|
|
2020-04-16 23:02:29 +00:00
|
|
|
std::shared_ptr<AttrCursor> maybeGetAttr(Symbol name)
|
2020-04-16 13:36:15 +00:00
|
|
|
{
|
2020-04-16 23:02:29 +00:00
|
|
|
if (root->db) {
|
2020-04-17 11:57:02 +00:00
|
|
|
if (!cachedValue)
|
2020-04-17 21:04:21 +00:00
|
|
|
cachedValue = root->db->getAttr(getAttrKey(), root->state.symbols);
|
2020-04-17 11:57:02 +00:00
|
|
|
|
|
|
|
if (cachedValue) {
|
2020-04-17 21:04:21 +00:00
|
|
|
if (auto attrs = std::get_if<std::vector<Symbol>>(&cachedValue->second)) {
|
2020-04-16 23:02:29 +00:00
|
|
|
for (auto & attr : *attrs)
|
|
|
|
if (attr == name)
|
|
|
|
return std::make_shared<AttrCursor>(root, std::make_pair(shared_from_this(), name));
|
2020-04-17 21:04:21 +00:00
|
|
|
return nullptr;
|
|
|
|
} else if (std::get_if<AttrDb::placeholder_t>(&cachedValue->second)) {
|
|
|
|
auto attr = root->db->getAttr({cachedValue->first, name}, root->state.symbols);
|
2020-04-19 21:07:06 +00:00
|
|
|
if (attr) {
|
|
|
|
if (std::get_if<AttrDb::missing_t>(&attr->second))
|
|
|
|
return nullptr;
|
|
|
|
else if (std::get_if<AttrDb::failed_t>(&attr->second))
|
|
|
|
throw EvalError("cached failure of attribute '%s'", getAttrPathStr(name));
|
|
|
|
else
|
|
|
|
return std::make_shared<AttrCursor>(root,
|
|
|
|
std::make_pair(shared_from_this(), name), nullptr, std::move(attr));
|
|
|
|
}
|
2020-04-17 21:04:21 +00:00
|
|
|
// Incomplete attrset, so need to fall thru and
|
|
|
|
// evaluate to see whether 'name' exists
|
|
|
|
} else
|
|
|
|
return nullptr;
|
2020-04-19 21:07:06 +00:00
|
|
|
//throw TypeError("'%s' is not an attribute set", getAttrPathStr());
|
2020-04-16 23:02:29 +00:00
|
|
|
}
|
|
|
|
}
|
2020-04-16 13:36:15 +00:00
|
|
|
|
2020-04-19 21:07:06 +00:00
|
|
|
auto & v = forceValue();
|
2020-04-16 23:02:29 +00:00
|
|
|
|
2020-04-19 21:07:06 +00:00
|
|
|
if (v.type != tAttrs)
|
2020-04-16 13:36:15 +00:00
|
|
|
return nullptr;
|
2020-04-19 21:07:06 +00:00
|
|
|
//throw TypeError("'%s' is not an attribute set", getAttrPathStr());
|
2020-04-16 13:36:15 +00:00
|
|
|
|
2020-04-19 21:07:06 +00:00
|
|
|
auto attr = v.attrs->get(name);
|
2020-04-16 13:36:15 +00:00
|
|
|
|
2020-04-19 21:07:06 +00:00
|
|
|
if (!attr) {
|
|
|
|
if (root->db) {
|
|
|
|
assert(cachedValue);
|
|
|
|
root->db->setMissing({cachedValue->first, name});
|
|
|
|
}
|
2020-04-16 13:36:15 +00:00
|
|
|
return nullptr;
|
2020-04-19 21:07:06 +00:00
|
|
|
}
|
2020-04-16 13:36:15 +00:00
|
|
|
|
2020-04-19 21:07:06 +00:00
|
|
|
std::optional<std::pair<AttrDb::AttrId, AttrDb::AttrValue>> cachedValue2;
|
|
|
|
if (root->db) {
|
|
|
|
assert(cachedValue);
|
|
|
|
cachedValue2 = {root->db->setPlaceholder({cachedValue->first, name}), AttrDb::placeholder_t()};
|
|
|
|
}
|
|
|
|
|
|
|
|
return std::make_shared<AttrCursor>(
|
|
|
|
root, std::make_pair(shared_from_this(), name), attr->value, std::move(cachedValue2));
|
2020-04-16 23:02:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
std::shared_ptr<AttrCursor> maybeGetAttr(std::string_view name)
|
|
|
|
{
|
|
|
|
return maybeGetAttr(root->state.symbols.create(name));
|
2020-04-16 13:36:15 +00:00
|
|
|
}
|
|
|
|
|
2020-04-16 23:02:29 +00:00
|
|
|
std::shared_ptr<AttrCursor> getAttr(Symbol name)
|
2020-04-16 13:36:15 +00:00
|
|
|
{
|
|
|
|
auto p = maybeGetAttr(name);
|
2020-04-16 23:02:29 +00:00
|
|
|
if (!p)
|
|
|
|
throw Error("attribute '%s' does not exist", getAttrPathStr(name));
|
2020-04-16 13:36:15 +00:00
|
|
|
return p;
|
|
|
|
}
|
|
|
|
|
2020-04-16 23:02:29 +00:00
|
|
|
std::shared_ptr<AttrCursor> getAttr(std::string_view name)
|
|
|
|
{
|
|
|
|
return getAttr(root->state.symbols.create(name));
|
|
|
|
}
|
|
|
|
|
2020-04-16 13:36:15 +00:00
|
|
|
std::string getString()
|
|
|
|
{
|
2020-04-16 23:02:29 +00:00
|
|
|
if (root->db) {
|
2020-04-17 11:57:02 +00:00
|
|
|
if (!cachedValue)
|
2020-04-17 21:04:21 +00:00
|
|
|
cachedValue = root->db->getAttr(getAttrKey(), root->state.symbols);
|
|
|
|
if (cachedValue && !std::get_if<AttrDb::placeholder_t>(&cachedValue->second)) {
|
|
|
|
if (auto s = std::get_if<std::string>(&cachedValue->second)) {
|
2020-04-19 21:07:06 +00:00
|
|
|
debug("using cached string attribute '%s'", getAttrPathStr());
|
2020-04-17 11:57:02 +00:00
|
|
|
return *s;
|
|
|
|
} else
|
2020-04-19 21:07:06 +00:00
|
|
|
throw TypeError("'%s' is not a string", getAttrPathStr());
|
2020-04-16 23:02:29 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-04-19 21:07:06 +00:00
|
|
|
auto & v = forceValue();
|
|
|
|
|
|
|
|
if (v.type != tString)
|
|
|
|
throw TypeError("'%s' is not a string", getAttrPathStr());
|
2020-04-17 11:57:02 +00:00
|
|
|
|
2020-04-19 21:07:06 +00:00
|
|
|
return v.string.s;
|
2020-04-16 13:36:15 +00:00
|
|
|
}
|
|
|
|
|
2020-04-16 23:02:29 +00:00
|
|
|
std::vector<Symbol> getAttrs()
|
2020-04-16 13:36:15 +00:00
|
|
|
{
|
2020-04-16 23:02:29 +00:00
|
|
|
if (root->db) {
|
2020-04-17 11:57:02 +00:00
|
|
|
if (!cachedValue)
|
2020-04-17 21:04:21 +00:00
|
|
|
cachedValue = root->db->getAttr(getAttrKey(), root->state.symbols);
|
|
|
|
if (cachedValue && !std::get_if<AttrDb::placeholder_t>(&cachedValue->second)) {
|
|
|
|
if (auto attrs = std::get_if<std::vector<Symbol>>(&cachedValue->second)) {
|
2020-04-19 21:07:06 +00:00
|
|
|
debug("using cached attrset attribute '%s'", getAttrPathStr());
|
2020-04-17 11:57:02 +00:00
|
|
|
return *attrs;
|
2020-04-16 23:02:29 +00:00
|
|
|
} else
|
2020-04-19 21:07:06 +00:00
|
|
|
throw TypeError("'%s' is not an attribute set", getAttrPathStr());
|
2020-04-16 23:02:29 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-04-19 21:07:06 +00:00
|
|
|
auto & v = forceValue();
|
|
|
|
|
|
|
|
if (v.type != tAttrs)
|
|
|
|
throw TypeError("'%s' is not an attribute set", getAttrPathStr());
|
|
|
|
|
2020-04-16 23:02:29 +00:00
|
|
|
std::vector<Symbol> attrs;
|
|
|
|
for (auto & attr : *getValue().attrs)
|
|
|
|
attrs.push_back(attr.name);
|
|
|
|
std::sort(attrs.begin(), attrs.end(), [](const Symbol & a, const Symbol & b) {
|
|
|
|
return (const string &) a < (const string &) b;
|
|
|
|
});
|
2020-04-19 21:07:06 +00:00
|
|
|
|
2020-04-17 21:04:21 +00:00
|
|
|
if (root->db)
|
2020-04-19 21:07:06 +00:00
|
|
|
cachedValue = {root->db->setAttrs(getAttrKey(), attrs), attrs};
|
2020-04-17 11:57:02 +00:00
|
|
|
|
2020-04-16 13:36:15 +00:00
|
|
|
return attrs;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool isDerivation()
|
|
|
|
{
|
|
|
|
auto aType = maybeGetAttr("type");
|
|
|
|
return aType && aType->getString() == "derivation";
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
struct CmdFlakeShow : FlakeCommand
|
|
|
|
{
|
|
|
|
bool showLegacy = false;
|
2020-04-17 12:30:04 +00:00
|
|
|
bool useEvalCache = true;
|
2020-04-16 13:36:15 +00:00
|
|
|
|
|
|
|
CmdFlakeShow()
|
|
|
|
{
|
|
|
|
mkFlag()
|
|
|
|
.longName("legacy")
|
2020-04-16 23:02:29 +00:00
|
|
|
.description("show the contents of the 'legacyPackages' output")
|
2020-04-16 13:36:15 +00:00
|
|
|
.set(&showLegacy, true);
|
2020-04-17 12:30:04 +00:00
|
|
|
|
|
|
|
mkFlag()
|
|
|
|
.longName("no-eval-cache")
|
|
|
|
.description("do not use the flake evaluation cache")
|
|
|
|
.handler([&]() { useEvalCache = false; });
|
2020-04-16 13:36:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
std::string description() override
|
|
|
|
{
|
|
|
|
return "show the outputs provided by a flake";
|
|
|
|
}
|
|
|
|
|
|
|
|
void run(nix::ref<nix::Store> store) override
|
|
|
|
{
|
|
|
|
auto state = getEvalState();
|
|
|
|
auto flake = lockFlake();
|
|
|
|
|
2020-04-16 23:02:29 +00:00
|
|
|
std::function<void(AttrCursor & visitor, const std::vector<Symbol> & attrPath, const std::string & headerPrefix, const std::string & nextPrefix)> visit;
|
2020-04-16 13:36:15 +00:00
|
|
|
|
2020-04-16 23:02:29 +00:00
|
|
|
visit = [&](AttrCursor & visitor, const std::vector<Symbol> & attrPath, const std::string & headerPrefix, const std::string & nextPrefix)
|
2020-04-16 13:36:15 +00:00
|
|
|
{
|
|
|
|
Activity act(*logger, lvlInfo, actUnknown,
|
|
|
|
fmt("evaluating '%s'", concatStringsSep(".", attrPath)));
|
|
|
|
try {
|
|
|
|
auto recurse = [&]()
|
|
|
|
{
|
|
|
|
logger->stdout("%s", headerPrefix);
|
|
|
|
auto attrs = visitor.getAttrs();
|
|
|
|
for (const auto & [i, attr] : enumerate(attrs)) {
|
|
|
|
bool last = i + 1 == attrs.size();
|
|
|
|
auto visitor2 = visitor.getAttr(attr);
|
|
|
|
auto attrPath2(attrPath);
|
|
|
|
attrPath2.push_back(attr);
|
|
|
|
visit(*visitor2, attrPath2,
|
|
|
|
fmt(ANSI_GREEN "%s%s" ANSI_NORMAL ANSI_BOLD "%s" ANSI_NORMAL, nextPrefix, last ? treeLast : treeConn, attr),
|
|
|
|
nextPrefix + (last ? treeNull : treeLine));
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
auto showDerivation = [&]()
|
|
|
|
{
|
2020-04-16 23:02:29 +00:00
|
|
|
auto name = visitor.getAttr(state->sName)->getString();
|
2020-04-16 13:36:15 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
std::string description;
|
|
|
|
|
|
|
|
if (auto aMeta = visitor.maybeGetAttr("meta")) {
|
|
|
|
if (auto aDescription = aMeta->maybeGetAttr("description"))
|
|
|
|
description = aDescription->getString();
|
|
|
|
}
|
|
|
|
*/
|
|
|
|
|
|
|
|
logger->stdout("%s: %s '%s'",
|
|
|
|
headerPrefix,
|
|
|
|
attrPath.size() == 2 && attrPath[0] == "devShell" ? "development environment" :
|
|
|
|
attrPath.size() == 3 && attrPath[0] == "checks" ? "derivation" :
|
|
|
|
attrPath.size() >= 1 && attrPath[0] == "hydraJobs" ? "derivation" :
|
|
|
|
"package",
|
|
|
|
name);
|
|
|
|
};
|
|
|
|
|
|
|
|
if (attrPath.size() == 0
|
|
|
|
|| (attrPath.size() == 1 && (
|
|
|
|
attrPath[0] == "defaultPackage"
|
|
|
|
|| attrPath[0] == "devShell"
|
|
|
|
|| attrPath[0] == "nixosConfigurations"
|
2020-04-16 23:21:24 +00:00
|
|
|
|| attrPath[0] == "nixosModules"
|
|
|
|
|| attrPath[0] == "defaultApp"))
|
2020-04-16 13:36:15 +00:00
|
|
|
|| ((attrPath.size() == 1 || attrPath.size() == 2)
|
|
|
|
&& (attrPath[0] == "checks"
|
2020-04-16 23:21:24 +00:00
|
|
|
|| attrPath[0] == "packages"
|
|
|
|
|| attrPath[0] == "apps"))
|
2020-04-16 13:36:15 +00:00
|
|
|
)
|
|
|
|
{
|
|
|
|
recurse();
|
|
|
|
}
|
|
|
|
|
|
|
|
else if (
|
|
|
|
(attrPath.size() == 2 && (attrPath[0] == "defaultPackage" || attrPath[0] == "devShell"))
|
|
|
|
|| (attrPath.size() == 3 && (attrPath[0] == "checks" || attrPath[0] == "packages"))
|
|
|
|
)
|
|
|
|
{
|
|
|
|
if (visitor.isDerivation())
|
|
|
|
showDerivation();
|
|
|
|
else
|
|
|
|
throw Error("expected a derivation");
|
|
|
|
}
|
|
|
|
|
|
|
|
else if (attrPath.size() > 0 && attrPath[0] == "hydraJobs") {
|
|
|
|
if (visitor.isDerivation())
|
|
|
|
showDerivation();
|
|
|
|
else
|
|
|
|
recurse();
|
|
|
|
}
|
|
|
|
|
|
|
|
else if (attrPath.size() > 0 && attrPath[0] == "legacyPackages") {
|
|
|
|
if (attrPath.size() == 1)
|
|
|
|
recurse();
|
|
|
|
else if (!showLegacy)
|
|
|
|
logger->stdout("%s: " ANSI_YELLOW "omitted" ANSI_NORMAL " (use '--legacy' to show)", headerPrefix);
|
|
|
|
else {
|
|
|
|
if (visitor.isDerivation())
|
|
|
|
showDerivation();
|
|
|
|
else if (attrPath.size() <= 2)
|
|
|
|
// FIXME: handle recurseIntoAttrs
|
|
|
|
recurse();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-04-16 23:21:24 +00:00
|
|
|
else if (
|
|
|
|
(attrPath.size() == 2 && attrPath[0] == "defaultApp") ||
|
|
|
|
(attrPath.size() == 3 && attrPath[0] == "apps"))
|
|
|
|
{
|
|
|
|
auto aType = visitor.maybeGetAttr("type");
|
|
|
|
if (!aType || aType->getString() != "app")
|
|
|
|
throw EvalError("not an app definition");
|
|
|
|
logger->stdout("%s: app", headerPrefix);
|
|
|
|
}
|
|
|
|
|
2020-04-16 13:36:15 +00:00
|
|
|
else {
|
|
|
|
logger->stdout("%s: %s",
|
|
|
|
headerPrefix,
|
|
|
|
attrPath.size() == 1 && attrPath[0] == "overlay" ? "Nixpkgs overlay" :
|
|
|
|
attrPath.size() == 2 && attrPath[0] == "nixosConfigurations" ? "NixOS configuration" :
|
|
|
|
attrPath.size() == 2 && attrPath[0] == "nixosModules" ? "NixOS module" :
|
|
|
|
ANSI_YELLOW "unknown" ANSI_NORMAL);
|
|
|
|
}
|
|
|
|
} catch (EvalError & e) {
|
|
|
|
if (!(attrPath.size() > 0 && attrPath[0] == "legacyPackages"))
|
2020-04-19 21:07:06 +00:00
|
|
|
throw;
|
2020-04-16 13:36:15 +00:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2020-04-17 12:30:04 +00:00
|
|
|
auto db = useEvalCache ? std::make_shared<AttrDb>(flake.getFingerprint()) : nullptr;
|
2020-04-16 23:02:29 +00:00
|
|
|
|
|
|
|
auto root = std::make_shared<AttrRoot>(db, *state,
|
|
|
|
[&]()
|
|
|
|
{
|
2020-04-19 21:07:06 +00:00
|
|
|
/* For testing whether the evaluation cache is
|
|
|
|
complete. */
|
|
|
|
if (getEnv("NIX_ALLOW_EVAL").value_or("1") == "0")
|
|
|
|
throw Error("not everything is cached, but evaluation is not allowed");
|
|
|
|
|
2020-04-16 23:02:29 +00:00
|
|
|
auto vFlake = state->allocValue();
|
|
|
|
flake::callFlake(*state, flake, *vFlake);
|
|
|
|
|
|
|
|
state->forceAttrs(*vFlake);
|
|
|
|
|
|
|
|
auto aOutputs = vFlake->attrs->get(state->symbols.create("outputs"));
|
|
|
|
assert(aOutputs);
|
|
|
|
|
|
|
|
return aOutputs->value;
|
|
|
|
});
|
2020-04-16 13:36:15 +00:00
|
|
|
|
2020-04-16 23:02:29 +00:00
|
|
|
visit(*root->getRoot(), {}, fmt(ANSI_BOLD "%s" ANSI_NORMAL, flake.flake.lockedRef), "");
|
2020-04-16 13:36:15 +00:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2018-11-29 18:18:36 +00:00
|
|
|
struct CmdFlake : virtual MultiCommand, virtual Command
|
|
|
|
{
|
|
|
|
CmdFlake()
|
2019-06-18 14:01:35 +00:00
|
|
|
: MultiCommand({
|
|
|
|
{"list", []() { return make_ref<CmdFlakeList>(); }},
|
|
|
|
{"update", []() { return make_ref<CmdFlakeUpdate>(); }},
|
|
|
|
{"info", []() { return make_ref<CmdFlakeInfo>(); }},
|
2020-01-31 11:54:52 +00:00
|
|
|
{"list-inputs", []() { return make_ref<CmdFlakeListInputs>(); }},
|
2019-06-18 14:01:35 +00:00
|
|
|
{"check", []() { return make_ref<CmdFlakeCheck>(); }},
|
|
|
|
{"add", []() { return make_ref<CmdFlakeAdd>(); }},
|
|
|
|
{"remove", []() { return make_ref<CmdFlakeRemove>(); }},
|
|
|
|
{"pin", []() { return make_ref<CmdFlakePin>(); }},
|
|
|
|
{"init", []() { return make_ref<CmdFlakeInit>(); }},
|
|
|
|
{"clone", []() { return make_ref<CmdFlakeClone>(); }},
|
2020-01-29 23:58:55 +00:00
|
|
|
{"archive", []() { return make_ref<CmdFlakeArchive>(); }},
|
2020-04-16 13:36:15 +00:00
|
|
|
{"show", []() { return make_ref<CmdFlakeShow>(); }},
|
2019-06-18 14:01:35 +00:00
|
|
|
})
|
2018-11-29 18:18:36 +00:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string description() override
|
|
|
|
{
|
|
|
|
return "manage Nix flakes";
|
|
|
|
}
|
|
|
|
|
|
|
|
void run() override
|
|
|
|
{
|
|
|
|
if (!command)
|
|
|
|
throw UsageError("'nix flake' requires a sub-command.");
|
2019-10-21 22:21:58 +00:00
|
|
|
command->prepare();
|
2018-11-29 18:18:36 +00:00
|
|
|
command->run();
|
|
|
|
}
|
|
|
|
|
|
|
|
void printHelp(const string & programName, std::ostream & out) override
|
|
|
|
{
|
|
|
|
MultiCommand::printHelp(programName, out);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2019-06-18 14:01:35 +00:00
|
|
|
static auto r1 = registerCommand<CmdFlake>("flake");
|