aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorEelco Dolstra <eelco.dolstra@logicblox.com>2016-03-04 15:21:23 +0100
committerEelco Dolstra <eelco.dolstra@logicblox.com>2016-03-04 15:21:23 +0100
commit7c9d7a253c52dfbf8488d3394fe8af104a3af234 (patch)
treeb78f69734d35f428456dde41a87808feead701e0 /src
parent0a26b56cba5d9f1fa815273fadc500284dda1118 (diff)
parent1b4b16cc6d9585c7bbeb871edc815137baef8f83 (diff)
Merge branch 'new-cli'
Diffstat (limited to 'src')
-rw-r--r--src/boost/format/exceptions.hpp10
-rw-r--r--src/libmain/common-args.cc38
-rw-r--r--src/libmain/common-args.hh23
-rw-r--r--src/libmain/shared.cc148
-rw-r--r--src/libmain/shared.hh3
-rw-r--r--src/libstore/http-binary-cache-store.cc76
-rw-r--r--src/libstore/local-binary-cache-store.cc14
-rw-r--r--src/libstore/store-api.hh5
-rw-r--r--src/libutil/args.cc179
-rw-r--r--src/libutil/args.hh162
-rw-r--r--src/nix-hash/local.mk7
-rw-r--r--src/nix-hash/nix-hash.cc63
-rw-r--r--src/nix/build.cc46
-rw-r--r--src/nix/cat.cc74
-rw-r--r--src/nix/command.cc65
-rw-r--r--src/nix/command.hh59
-rw-r--r--src/nix/hash.cc140
-rw-r--r--src/nix/installables.cc75
-rw-r--r--src/nix/installables.hh38
-rw-r--r--src/nix/legacy.cc7
-rw-r--r--src/nix/legacy.hh22
-rw-r--r--src/nix/local.mk9
-rw-r--r--src/nix/ls.cc123
-rw-r--r--src/nix/main.cc55
24 files changed, 1284 insertions, 157 deletions
diff --git a/src/boost/format/exceptions.hpp b/src/boost/format/exceptions.hpp
index 79e452449..a7641458c 100644
--- a/src/boost/format/exceptions.hpp
+++ b/src/boost/format/exceptions.hpp
@@ -33,7 +33,7 @@ namespace io {
class format_error : public std::exception
{
public:
- format_error() {}
+ format_error() { abort(); }
virtual const char *what() const throw()
{
return "boost::format_error: "
@@ -44,7 +44,7 @@ public:
class bad_format_string : public format_error
{
public:
- bad_format_string() {}
+ bad_format_string() { abort(); }
virtual const char *what() const throw()
{
return "boost::bad_format_string: "
@@ -55,7 +55,7 @@ public:
class too_few_args : public format_error
{
public:
- too_few_args() {}
+ too_few_args() { abort(); }
virtual const char *what() const throw()
{
return "boost::too_few_args: "
@@ -66,7 +66,7 @@ public:
class too_many_args : public format_error
{
public:
- too_many_args() {}
+ too_many_args() { abort(); }
virtual const char *what() const throw()
{
return "boost::too_many_args: "
@@ -78,7 +78,7 @@ public:
class out_of_range : public format_error
{
public:
- out_of_range() {}
+ out_of_range() { abort(); }
virtual const char *what() const throw()
{
return "boost::out_of_range: "
diff --git a/src/libmain/common-args.cc b/src/libmain/common-args.cc
new file mode 100644
index 000000000..9219f380c
--- /dev/null
+++ b/src/libmain/common-args.cc
@@ -0,0 +1,38 @@
+#include "common-args.hh"
+#include "globals.hh"
+
+namespace nix {
+
+MixCommonArgs::MixCommonArgs(const string & programName)
+ : programName(programName)
+{
+ mkFlag('v', "verbose", "increase verbosity level", []() {
+ verbosity = (Verbosity) (verbosity + 1);
+ });
+
+ mkFlag(0, "quiet", "decrease verbosity level", []() {
+ verbosity = verbosity > lvlError ? (Verbosity) (verbosity - 1) : lvlError;
+ });
+
+ mkFlag(0, "debug", "enable debug output", []() {
+ verbosity = lvlDebug;
+ });
+
+ mkFlag1(0, "log-type", "type", "set logging format ('pretty', 'flat', 'systemd')",
+ [](std::string s) {
+ if (s == "pretty") logType = ltPretty;
+ else if (s == "escapes") logType = ltEscapes;
+ else if (s == "flat") logType = ltFlat;
+ else if (s == "systemd") logType = ltSystemd;
+ else throw UsageError("unknown log type");
+ });
+
+ mkFlag(0, "option", {"name", "value"}, "set a Nix configuration option (overriding nix.conf)", 2,
+ [](Strings ss) {
+ auto name = ss.front(); ss.pop_front();
+ auto value = ss.front();
+ settings.set(name, value);
+ });
+}
+
+}
diff --git a/src/libmain/common-args.hh b/src/libmain/common-args.hh
new file mode 100644
index 000000000..2c0d71edd
--- /dev/null
+++ b/src/libmain/common-args.hh
@@ -0,0 +1,23 @@
+#pragma once
+
+#include "args.hh"
+
+namespace nix {
+
+struct MixCommonArgs : virtual Args
+{
+ string programName;
+ MixCommonArgs(const string & programName);
+};
+
+struct MixDryRun : virtual Args
+{
+ bool dryRun;
+
+ MixDryRun()
+ {
+ mkFlag(0, "dry-run", "show what this command would do without doing it", &dryRun);
+ }
+};
+
+}
diff --git a/src/libmain/shared.cc b/src/libmain/shared.cc
index c27302227..e883967b7 100644
--- a/src/libmain/shared.cc
+++ b/src/libmain/shared.cc
@@ -1,7 +1,8 @@
#include "config.h"
-#include "shared.hh"
+#include "common-args.hh"
#include "globals.hh"
+#include "shared.hh"
#include "store-api.hh"
#include "util.hh"
@@ -85,16 +86,6 @@ void printMissing(ref<Store> store, const PathSet & willBuild,
}
-static void setLogType(string lt)
-{
- if (lt == "pretty") logType = ltPretty;
- else if (lt == "escapes") logType = ltEscapes;
- else if (lt == "flat") logType = ltFlat;
- else if (lt == "systemd") logType = ltSystemd;
- else throw UsageError("unknown log type");
-}
-
-
string getArg(const string & opt,
Strings::iterator & i, const Strings::iterator & end)
{
@@ -181,77 +172,80 @@ void initNix()
}
-void parseCmdLine(int argc, char * * argv,
- std::function<bool(Strings::iterator & arg, const Strings::iterator & end)> parseArg)
+struct LegacyArgs : public MixCommonArgs
{
- /* Put the arguments in a vector. */
- Strings args;
- argc--; argv++;
- while (argc--) args.push_back(*argv++);
-
- /* Process default options. */
- for (Strings::iterator i = args.begin(); i != args.end(); ++i) {
- string arg = *i;
-
- /* Expand compound dash options (i.e., `-qlf' -> `-q -l -f'). */
- if (arg.length() > 2 && arg[0] == '-' && arg[1] != '-' && isalpha(arg[1])) {
- *i = (string) "-" + arg[1];
- auto next = i; ++next;
- for (unsigned int j = 2; j < arg.length(); j++)
- if (isalpha(arg[j]))
- args.insert(next, (string) "-" + arg[j]);
- else {
- args.insert(next, string(arg, j));
- break;
- }
- arg = *i;
- }
+ std::function<bool(Strings::iterator & arg, const Strings::iterator & end)> parseArg;
- if (arg == "--verbose" || arg == "-v") verbosity = (Verbosity) (verbosity + 1);
- else if (arg == "--quiet") verbosity = verbosity > lvlError ? (Verbosity) (verbosity - 1) : lvlError;
- else if (arg == "--log-type") {
- string s = getArg(arg, i, args.end());
- setLogType(s);
- }
- else if (arg == "--no-build-output" || arg == "-Q")
- settings.buildVerbosity = lvlVomit;
- else if (arg == "--print-build-trace")
- settings.printBuildTrace = true;
- else if (arg == "--keep-failed" || arg == "-K")
- settings.keepFailed = true;
- else if (arg == "--keep-going" || arg == "-k")
- settings.keepGoing = true;
- else if (arg == "--fallback")
+ LegacyArgs(const std::string & programName,
+ std::function<bool(Strings::iterator & arg, const Strings::iterator & end)> parseArg)
+ : MixCommonArgs(programName), parseArg(parseArg)
+ {
+ mkFlag('Q', "no-build-output", "do not show build output",
+ &settings.buildVerbosity, lvlVomit);
+
+ mkFlag(0, "print-build-trace", "emit special build trace message",
+ &settings.printBuildTrace);
+
+ mkFlag('K', "keep-failed", "keep temporary directories of failed builds",
+ &settings.keepFailed);
+
+ mkFlag('k', "keep-going", "keep going after a build fails",
+ &settings.keepGoing);
+
+ mkFlag(0, "fallback", "build from source if substitution fails", []() {
settings.set("build-fallback", "true");
- else if (arg == "--max-jobs" || arg == "-j")
- settings.set("build-max-jobs", getArg(arg, i, args.end()));
- else if (arg == "--cores")
- settings.set("build-cores", getArg(arg, i, args.end()));
- else if (arg == "--readonly-mode")
- settings.readOnlyMode = true;
- else if (arg == "--max-silent-time")
- settings.set("build-max-silent-time", getArg(arg, i, args.end()));
- else if (arg == "--timeout")
- settings.set("build-timeout", getArg(arg, i, args.end()));
- else if (arg == "--no-build-hook")
- settings.useBuildHook = false;
- else if (arg == "--show-trace")
- settings.showTrace = true;
- else if (arg == "--no-gc-warning")
- gcWarning = false;
- else if (arg == "--option") {
- ++i; if (i == args.end()) throw UsageError("‘--option’ requires two arguments");
- string name = *i;
- ++i; if (i == args.end()) throw UsageError("‘--option’ requires two arguments");
- string value = *i;
- settings.set(name, value);
- }
- else {
- if (!parseArg(i, args.end()))
- throw UsageError(format("unrecognised option ‘%1%’") % *i);
- }
+ });
+
+ auto intSettingAlias = [&](char shortName, const std::string & longName,
+ const std::string & description, const std::string & dest) {
+ mkFlag<unsigned int>(shortName, longName, description, [=](unsigned int n) {
+ settings.set(dest, std::to_string(n));
+ });
+ };
+
+ intSettingAlias('j', "max-jobs", "maximum number of parallel builds", "build-max-jobs");
+ intSettingAlias(0, "cores", "maximum number of CPU cores to use inside a build", "build-cores");
+ intSettingAlias(0, "max-silent-time", "number of seconds of silence before a build is killed", "build-max-silent-time");
+ intSettingAlias(0, "timeout", "number of seconds before a build is killed", "build-timeout");
+
+ mkFlag(0, "readonly-mode", "do not write to the Nix store",
+ &settings.readOnlyMode);
+
+ mkFlag(0, "no-build-hook", "disable use of the build hook mechanism",
+ &settings.useBuildHook, false);
+
+ mkFlag(0, "show-trace", "show Nix expression stack trace in evaluation errors",
+ &settings.showTrace);
+
+ mkFlag(0, "no-gc-warning", "disable warning about not using ‘--add-root’",
+ &gcWarning, false);
}
+ bool processFlag(Strings::iterator & pos, Strings::iterator end) override
+ {
+ if (MixCommonArgs::processFlag(pos, end)) return true;
+ bool res = parseArg(pos, end);
+ if (res) ++pos;
+ return res;
+ }
+
+ bool processArgs(const Strings & args, bool finish) override
+ {
+ if (args.empty()) return true;
+ assert(args.size() == 1);
+ Strings ss(args);
+ auto pos = ss.begin();
+ if (!parseArg(pos, ss.end()))
+ throw UsageError(format("unexpected argument ‘%1%’") % args.front());
+ return true;
+ }
+};
+
+
+void parseCmdLine(int argc, char * * argv,
+ std::function<bool(Strings::iterator & arg, const Strings::iterator & end)> parseArg)
+{
+ LegacyArgs(baseNameOf(argv[0]), parseArg).parseCmdline(argvToStrings(argc, argv));
settings.update();
}
diff --git a/src/libmain/shared.hh b/src/libmain/shared.hh
index 0682267fa..6d94a22f7 100644
--- a/src/libmain/shared.hh
+++ b/src/libmain/shared.hh
@@ -1,6 +1,7 @@
#pragma once
#include "util.hh"
+#include "args.hh"
#include <signal.h>
@@ -9,8 +10,6 @@
namespace nix {
-MakeError(UsageError, nix::Error);
-
class Exit : public std::exception
{
public:
diff --git a/src/libstore/http-binary-cache-store.cc b/src/libstore/http-binary-cache-store.cc
new file mode 100644
index 000000000..78f4497e7
--- /dev/null
+++ b/src/libstore/http-binary-cache-store.cc
@@ -0,0 +1,76 @@
+#include "binary-cache-store.hh"
+#include "download.hh"
+
+namespace nix {
+
+class HttpBinaryCacheStore : public BinaryCacheStore
+{
+private:
+
+ Path cacheUri;
+
+ ref<Downloader> downloader;
+
+public:
+
+ HttpBinaryCacheStore(std::shared_ptr<Store> localStore,
+ const Path & secretKeyFile, const Path & publicKeyFile,
+ const Path & _cacheUri)
+ : BinaryCacheStore(localStore, secretKeyFile, publicKeyFile)
+ , cacheUri(_cacheUri)
+ , downloader(makeDownloader())
+ {
+ if (cacheUri.back() == '/')
+ cacheUri.pop_back();
+ }
+
+ void init() override
+ {
+ // FIXME: do this lazily?
+ if (!fileExists("nix-cache-info"))
+ throw Error(format("‘%s’ does not appear to be a binary cache") % cacheUri);
+ }
+
+protected:
+
+ bool fileExists(const std::string & path) override
+ {
+ try {
+ DownloadOptions options;
+ options.showProgress = DownloadOptions::no;
+ options.head = true;
+ downloader->download(cacheUri + "/" + path, options);
+ return true;
+ } catch (DownloadError & e) {
+ if (e.error == Downloader::NotFound)
+ return false;
+ throw;
+ }
+ }
+
+ void upsertFile(const std::string & path, const std::string & data)
+ {
+ throw Error("uploading to an HTTP binary cache is not supported");
+ }
+
+ std::string getFile(const std::string & path) override
+ {
+ DownloadOptions options;
+ options.showProgress = DownloadOptions::no;
+ return downloader->download(cacheUri + "/" + path, options).data;
+ }
+
+};
+
+static RegisterStoreImplementation regStore([](const std::string & uri) -> std::shared_ptr<Store> {
+ if (std::string(uri, 0, 7) != "http://" &&
+ std::string(uri, 0, 8) != "https://") return 0;
+ auto store = std::make_shared<HttpBinaryCacheStore>(std::shared_ptr<Store>(0),
+ "", "", // FIXME: allow the signing key to be set
+ uri);
+ store->init();
+ return store;
+});
+
+}
+
diff --git a/src/libstore/local-binary-cache-store.cc b/src/libstore/local-binary-cache-store.cc
index a10c9d106..8590aea18 100644
--- a/src/libstore/local-binary-cache-store.cc
+++ b/src/libstore/local-binary-cache-store.cc
@@ -65,13 +65,21 @@ std::string LocalBinaryCacheStore::getFile(const std::string & path)
return readFile(binaryCacheDir + "/" + path);
}
+ref<Store> openLocalBinaryCacheStore(std::shared_ptr<Store> localStore,
+ const Path & secretKeyFile, const Path & publicKeyFile,
+ const Path & binaryCacheDir)
+{
+ auto store = std::make_shared<LocalBinaryCacheStore>(
+ localStore, secretKeyFile, publicKeyFile, binaryCacheDir);
+ store->init();
+ return ref<Store>(std::shared_ptr<Store>(store));
+}
+
static RegisterStoreImplementation regStore([](const std::string & uri) -> std::shared_ptr<Store> {
if (std::string(uri, 0, 7) != "file://") return 0;
- auto store = std::make_shared<LocalBinaryCacheStore>(std::shared_ptr<Store>(0),
+ return openLocalBinaryCacheStore(std::shared_ptr<Store>(0),
"", "", // FIXME: allow the signing key to be set
std::string(uri, 7));
- store->init();
- return store;
});
}
diff --git a/src/libstore/store-api.hh b/src/libstore/store-api.hh
index 97e834ed2..9825d45db 100644
--- a/src/libstore/store-api.hh
+++ b/src/libstore/store-api.hh
@@ -453,6 +453,11 @@ ref<Store> openStoreAt(const std::string & uri);
ref<Store> openStore();
+ref<Store> openLocalBinaryCacheStore(std::shared_ptr<Store> localStore,
+ const Path & secretKeyFile, const Path & publicKeyFile,
+ const Path & binaryCacheDir);
+
+
/* Store implementation registration. */
typedef std::function<std::shared_ptr<Store>(const std::string & uri)> OpenStore;
diff --git a/src/libutil/args.cc b/src/libutil/args.cc
new file mode 100644
index 000000000..6e4b82a27
--- /dev/null
+++ b/src/libutil/args.cc
@@ -0,0 +1,179 @@
+#include "args.hh"
+#include "hash.hh"
+
+namespace nix {
+
+void Args::parseCmdline(const Strings & _cmdline)
+{
+ Strings pendingArgs;
+ bool dashDash = false;
+
+ Strings cmdline(_cmdline);
+
+ for (auto pos = cmdline.begin(); pos != cmdline.end(); ) {
+
+ auto arg = *pos;
+
+ /* Expand compound dash options (i.e., `-qlf' -> `-q -l -f',
+ `-j3` -> `-j 3`). */
+ if (!dashDash && arg.length() > 2 && arg[0] == '-' && arg[1] != '-' && isalpha(arg[1])) {
+ *pos = (string) "-" + arg[1];
+ auto next = pos; ++next;
+ for (unsigned int j = 2; j < arg.length(); j++)
+ if (isalpha(arg[j]))
+ cmdline.insert(next, (string) "-" + arg[j]);
+ else {
+ cmdline.insert(next, string(arg, j));
+ break;
+ }
+ arg = *pos;
+ }
+
+ if (!dashDash && arg == "--") {
+ dashDash = true;
+ ++pos;
+ }
+ else if (!dashDash && std::string(arg, 0, 1) == "-") {
+ if (!processFlag(pos, cmdline.end()))
+ throw UsageError(format("unrecognised flag ‘%1%’") % arg);
+ }
+ else {
+ pendingArgs.push_back(*pos++);
+ if (processArgs(pendingArgs, false))
+ pendingArgs.clear();
+ }
+ }
+
+ processArgs(pendingArgs, true);
+}
+
+void Args::printHelp(const string & programName, std::ostream & out)
+{
+ std::cout << "Usage: " << programName << " <FLAGS>...";
+ for (auto & exp : expectedArgs) {
+ std::cout << renderLabels({exp.label});
+ // FIXME: handle arity > 1
+ if (exp.arity == 0) std::cout << "...";
+ }
+ std::cout << "\n";
+
+ auto s = description();
+ if (s != "")
+ std::cout << "\nSummary: " << s << ".\n";
+
+ if (longFlags.size()) {
+ std::cout << "\n";
+ std::cout << "Flags:\n";
+ printFlags(out);
+ }
+}
+
+void Args::printFlags(std::ostream & out)
+{
+ Table2 table;
+ for (auto & flags : longFlags)
+ table.push_back(std::make_pair(
+ "--" + flags.first + renderLabels(flags.second.labels),
+ flags.second.description));
+ printTable(out, table);
+}
+
+bool Args::processFlag(Strings::iterator & pos, Strings::iterator end)
+{
+ assert(pos != end);
+
+ auto process = [&](const std::string & name, const Flag & flag) -> bool {
+ ++pos;
+ Strings args;
+ for (size_t n = 0 ; n < flag.arity; ++n) {
+ if (pos == end)
+ throw UsageError(format("flag ‘%1%’ requires %2% argument(s)")
+ % name % flag.arity);
+ args.push_back(*pos++);
+ }
+ flag.handler(args);
+ return true;
+ };
+
+ if (string(*pos, 0, 2) == "--") {
+ auto i = longFlags.find(string(*pos, 2));
+ if (i == longFlags.end()) return false;
+ return process("--" + i->first, i->second);
+ }
+
+ if (string(*pos, 0, 1) == "-" && pos->size() == 2) {
+ auto c = (*pos)[1];
+ auto i = shortFlags.find(c);
+ if (i == shortFlags.end()) return false;
+ return process(std::string("-") + c, i->second);
+ }
+
+ return false;
+}
+
+bool Args::processArgs(const Strings & args, bool finish)
+{
+ if (expectedArgs.empty()) {
+ if (!args.empty())
+ throw UsageError(format("unexpected argument ‘%1%’") % args.front());
+ return true;
+ }
+
+ auto & exp = expectedArgs.front();
+
+ bool res = false;
+
+ if ((exp.arity == 0 && finish) ||
+ (exp.arity > 0 && args.size() == exp.arity))
+ {
+ exp.handler(args);
+ expectedArgs.pop_front();
+ res = true;
+ }
+
+ if (finish && !expectedArgs.empty())
+ throw UsageError("more arguments are required");
+
+ return res;
+}
+
+void Args::mkHashTypeFlag(const std::string & name, HashType * ht)
+{
+ mkFlag1(0, name, "TYPE", "hash algorithm (‘md5’, ‘sha1’, ‘sha256’, or ‘sha512’)", [=](std::string s) {
+ *ht = parseHashType(s);
+ if (*ht == htUnknown)
+ throw UsageError(format("unknown hash type ‘%1%’") % s);
+ });
+}
+
+Strings argvToStrings(int argc, char * * argv)
+{
+ Strings args;
+ argc--; argv++;
+ while (argc--) args.push_back(*argv++);
+ return args;
+}
+
+std::string renderLabels(const Strings & labels)
+{
+ std::string res;
+ for (auto label : labels) {
+ for (auto & c : label) c = std::toupper(c);
+ res += " <" + label + ">";
+ }
+ return res;
+}
+
+void printTable(std::ostream & out, const Table2 & table)
+{
+ size_t max = 0;
+ for (auto & row : table)
+ max = std::max(max, row.first.size());
+ for (auto & row : table) {
+ out << " " << row.first
+ << std::string(max - row.first.size() + 2, ' ')
+ << row.second << "\n";
+ }
+}
+
+}
diff --git a/src/libutil/args.hh b/src/libutil/args.hh
new file mode 100644
index 000000000..4469a046d
--- /dev/null
+++ b/src/libutil/args.hh
@@ -0,0 +1,162 @@
+#pragma once
+
+#include <iostream>
+#include <map>
+#include <memory>
+
+#include "util.hh"
+
+namespace nix {
+
+MakeError(UsageError, nix::Error);
+
+enum HashType : char;
+
+class Args
+{
+public:
+
+ /* Parse the command line, throwing a UsageError if something goes
+ wrong. */
+ void parseCmdline(const Strings & cmdline);
+
+ virtual void printHelp(const string & programName, std::ostream & out);
+
+ virtual std::string description() { return ""; }
+
+protected:
+
+ /* Flags. */
+ struct Flag
+ {
+ std::string description;
+ Strings labels;
+ size_t arity;
+ std::function<void(Strings)> handler;
+ };
+
+ std::map<std::string, Flag> longFlags;
+ std::map<char, Flag> shortFlags;
+
+ virtual bool processFlag(Strings::iterator & pos, Strings::iterator end);
+
+ void printFlags(std::ostream & out);
+
+ /* Positional arguments. */
+ struct ExpectedArg
+ {
+ std::string label;
+ size_t arity; // 0 = any
+ std::function<void(Strings)> handler;
+ };
+
+ std::list<ExpectedArg> expectedArgs;
+
+ virtual bool processArgs(const Strings & args, bool finish);
+
+public:
+
+ /* Helper functions for constructing flags / positional
+ arguments. */
+
+ void mkFlag(char shortName, const std::string & longName,
+ const Strings & labels, const std::string & description,
+ size_t arity, std::function<void(Strings)> handler)
+ {
+ auto flag = Flag{description, labels, arity, handler};
+ if (shortName) shortFlags[shortName] = flag;
+ longFlags[longName] = flag;
+ }
+
+ void mkFlag(char shortName, const std::string & longName,
+ const std::string & description, std::function<void()> fun)
+ {
+ mkFlag(shortName, longName, {}, description, 0, std::bind(fun));
+ }
+
+ void mkFlag1(char shortName, const std::string & longName,
+ const std::string & label, const std::string & description,
+ std::function<void(std::string)> fun)
+ {
+ mkFlag(shortName, longName, {label}, description, 1, [=](Strings ss) {
+ fun(ss.front());
+ });
+ }
+
+ void mkFlag(char shortName, const std::string & name,
+ const std::string & description, bool * dest)
+ {
+ mkFlag(shortName, name, description, dest, true);
+ }
+
+ void mkFlag(char shortName, const std::string & longName,
+ const std::string & label, const std::string & description,
+ string * dest)
+ {
+ mkFlag1(shortName, longName, label, description, [=](std::string s) {
+ *dest = s;
+ });
+ }
+
+ void mkHashTypeFlag(const std::string & name, HashType * ht);
+
+ template<class T>
+ void mkFlag(char shortName, const std::string & longName, const std::string & description,
+ T * dest, const T & value)
+ {
+ mkFlag(shortName, longName, {}, description, 0, [=](Strings ss) {
+ *dest = value;
+ });
+ }
+
+ template<class I>
+ void mkIntFlag(char shortName, const std::string & longName,
+ const std::string & description, I * dest)
+ {
+ mkFlag<I>(shortName, longName, description, [=](I n) {
+ *dest = n;
+ });
+ }
+
+ template<class I>
+ void mkFlag(char shortName, const std::string & longName,
+ const std::string & description, std::function<void(I)> fun)
+ {
+ mkFlag(shortName, longName, {"N"}, description, 1, [=](Strings ss) {
+ I n;
+ if (!string2Int(ss.front(), n))
+ throw UsageError(format("flag ‘--%1%’ requires a integer argument") % longName);
+ fun(n);
+ });
+ }
+
+ /* Expect a string argument. */
+ void expectArg(const std::string & label, string * dest)
+ {
+ expectedArgs.push_back(ExpectedArg{label, 1, [=](Strings ss) {
+ *dest = ss.front();
+ }});
+ }
+
+ /* Expect 0 or more arguments. */
+ void expectArgs(const std::string & label, Strings * dest)
+ {
+ expectedArgs.push_back(ExpectedArg{label, 0, [=](Strings ss) {
+ *dest = ss;
+ }});
+ }
+
+ friend class MultiCommand;
+};
+
+Strings argvToStrings(int argc, char * * argv);
+
+/* Helper function for rendering argument labels. */
+std::string renderLabels(const Strings & labels);
+
+/* Helper function for printing 2-column tables. */
+typedef std::vector<std::pair<std::string, std::string>> Table2;
+
+void printTable(std::ostream & out, const Table2 & table);
+
+}
diff --git a/src/nix-hash/local.mk b/src/nix-hash/local.mk
deleted file mode 100644
index 7c290ca84..000000000
--- a/src/nix-hash/local.mk
+++ /dev/null
@@ -1,7 +0,0 @@
-programs += nix-hash
-
-nix-hash_DIR := $(d)
-
-nix-hash_SOURCES := $(d)/nix-hash.cc
-
-nix-hash_LIBS = libmain libstore libutil libformat
diff --git a/src/nix-hash/nix-hash.cc b/src/nix-hash/nix-hash.cc
deleted file mode 100644
index 8035162ae..000000000
--- a/src/nix-hash/nix-hash.cc
+++ /dev/null
@@ -1,63 +0,0 @@
-#include "hash.hh"
-#include "shared.hh"
-
-#include <iostream>
-
-using namespace nix;
-
-
-int main(int argc, char * * argv)
-{
- HashType ht = htMD5;
- bool flat = false;
- bool base32 = false;
- bool truncate = false;
- enum { opHash, opTo32, opTo16 } op = opHash;
-
- Strings ss;
-
- return handleExceptions(argv[0], [&]() {
- initNix();
-
- parseCmdLine(argc, argv, [&](Strings::iterator & arg, const Strings::iterator & end) {
- if (*arg == "--help")
- showManPage("nix-hash");
- else if (*arg == "--version")
- printVersion("nix-hash");
- else if (*arg == "--flat") flat = true;
- else if (*arg == "--base32") base32 = true;
- else if (*arg == "--truncate") truncate = true;
- else if (*arg == "--type") {
- string s = getArg(*arg, arg, end);
- ht = parseHashType(s);
- if (ht == htUnknown)
- throw UsageError(format("unknown hash type ‘%1%’") % s);
- }
- else if (*arg == "--to-base16") op = opTo16;
- else if (*arg == "--to-base32") op = opTo32;
- else if (*arg != "" && arg->at(0) == '-')
- return false;
- else
- ss.push_back(*arg);
- return true;
- });
-
- if (op == opHash) {
- for (auto & i : ss) {
- Hash h = flat ? hashFile(ht, i) : hashPath(ht, i).first;
- if (truncate && h.hashSize > 20) h = compressHash(h, 20);
- std::cout << format("%1%\n") %
- (base32 ? printHash32(h) : printHash(h));
- }
- }
-
- else {
- for (auto & i : ss) {
- Hash h = parseHash16or32(ht, i);
- std::cout << format("%1%\n") %
- (op == opTo16 ? printHash(h) : printHash32(h));
- }
- }
- });
-}
-
diff --git a/src/nix/build.cc b/src/nix/build.cc
new file mode 100644
index 000000000..812464d75
--- /dev/null
+++ b/src/nix/build.cc
@@ -0,0 +1,46 @@
+#include "command.hh"
+#include "common-args.hh"
+#include "installables.hh"
+#include "shared.hh"
+#include "store-api.hh"
+
+using namespace nix;
+
+struct CmdBuild : StoreCommand, MixDryRun, MixInstallables
+{
+ CmdBuild()
+ {
+ }
+
+ std::string name() override
+ {
+ return "build";
+ }
+
+ std::string description() override
+ {
+ return "build a derivation or fetch a store path";
+ }
+
+ void run(ref<Store> store) override
+ {
+ auto elems = evalInstallables(store);
+
+ PathSet pathsToBuild;
+
+ for (auto & elem : elems) {
+ if (elem.isDrv)
+ pathsToBuild.insert(elem.drvPath);
+ else
+ pathsToBuild.insert(elem.outPaths.begin(), elem.outPaths.end());
+ }
+
+ printMissing(store, pathsToBuild);
+
+ if (dryRun) return;
+
+ store->buildPaths(pathsToBuild);
+ }
+};
+
+static RegisterCommand r1(make_ref<CmdBuild>());
diff --git a/src/nix/cat.cc b/src/nix/cat.cc
new file mode 100644
index 000000000..2405a8cb4
--- /dev/null
+++ b/src/nix/cat.cc
@@ -0,0 +1,74 @@
+#include "command.hh"
+#include "store-api.hh"
+#include "fs-accessor.hh"
+#include "nar-accessor.hh"
+
+using namespace nix;
+
+struct MixCat : virtual Args
+{
+ std::string path;
+
+ void cat(ref<FSAccessor> accessor)
+ {
+ auto st = accessor->stat(path);
+ if (st.type == FSAccessor::Type::tMissing)
+ throw Error(format("path ‘%1%’ does not exist") % path);
+ if (st.type != FSAccessor::Type::tRegular)
+ throw Error(format("path ‘%1%’ is not a regular file") % path);
+
+ std::cout << accessor->readFile(path);
+ }
+};
+
+struct CmdCatStore : StoreCommand, MixCat
+{
+ CmdCatStore()
+ {
+ expectArg("path", &path);
+ }
+
+ std::string name() override
+ {
+ return "cat-store";
+ }
+
+ std::string description() override
+ {
+ return "print the contents of a store file on stdout";
+ }
+
+ void run(ref<Store> store) override
+ {
+ cat(store->getFSAccessor());
+ }
+};
+
+struct CmdCatNar : StoreCommand, MixCat
+{
+ Path narPath;
+
+ CmdCatNar()
+ {
+ expectArg("nar", &narPath);
+ expectArg("path", &path);
+ }
+
+ std::string name() override
+ {
+ return "cat-nar";
+ }
+
+ std::string description() override
+ {
+ return "print the contents of a file inside a NAR file";
+ }
+
+ void run(ref<Store> store) override
+ {
+ cat(makeNarAccessor(make_ref<std::string>(readFile(narPath))));
+ }
+};
+
+static RegisterCommand r1(make_ref<CmdCatStore>());
+static RegisterCommand r2(make_ref<CmdCatNar>());
diff --git a/src/nix/command.cc b/src/nix/command.cc
new file mode 100644
index 000000000..9c80f4309
--- /dev/null
+++ b/src/nix/command.cc
@@ -0,0 +1,65 @@
+#include "command.hh"
+#include "store-api.hh"
+
+namespace nix {
+
+Commands * RegisterCommand::commands = 0;
+
+MultiCommand::MultiCommand(const Commands & _commands)
+ : commands(_commands)
+{
+ expectedArgs.push_back(ExpectedArg{"command", 1, [=](Strings ss) {
+ assert(!command);
+ auto i = commands.find(ss.front());
+ if (i == commands.end())
+ throw UsageError(format("‘%1%’ is not a recognised command") % ss.front());
+ command = i->second;
+ }});
+}
+
+void MultiCommand::printHelp(const string & programName, std::ostream & out)
+{
+ if (command) {
+ command->printHelp(programName + " " + command->name(), out);
+ return;
+ }
+
+ out << "Usage: " << programName << " <COMMAND> <FLAGS>... <ARGS>...\n";
+
+ out << "\n";
+ out << "Common flags:\n";
+ printFlags(out);
+
+ out << "\n";
+ out << "Available commands:\n";
+
+ Table2 table;
+ for (auto & command : commands)
+ table.push_back(std::make_pair(command.second->name(), command.second->description()));
+ printTable(out, table);
+
+ out << "\n";
+ out << "For full documentation, run ‘man " << programName << "’ or ‘man " << programName << "-<COMMAND>’.\n";
+}
+
+bool MultiCommand::processFlag(Strings::iterator & pos, Strings::iterator end)
+{
+ if (Args::processFlag(pos, end)) return true;
+ if (command && command->processFlag(pos, end)) return true;
+ return false;
+}
+
+bool MultiCommand::processArgs(const Strings & args, bool finish)
+{
+ if (command)
+ return command->processArgs(args, finish);
+ else
+ return Args::processArgs(args, finish);
+}
+
+void StoreCommand::run()
+{
+ run(openStore());
+}
+
+}
diff --git a/src/nix/command.hh b/src/nix/command.hh
new file mode 100644
index 000000000..a84721ccf
--- /dev/null
+++ b/src/nix/command.hh
@@ -0,0 +1,59 @@
+#pragma once
+
+#include "args.hh"
+
+namespace nix {
+
+/* A command is an argument parser that can be executed by calling its
+ run() method. */
+struct Command : virtual Args
+{
+ virtual std::string name() = 0;
+ virtual void prepare() { };
+ virtual void run() = 0;
+};
+
+class Store;
+
+/* A command that require a Nix store. */
+struct StoreCommand : virtual Command
+{
+ bool reserveSpace;
+ StoreCommand(bool reserveSpace = true)
+ : reserveSpace(reserveSpace) { };
+ void run() override;
+ virtual void run(ref<Store>) = 0;
+};
+
+typedef std::map<std::string, ref<Command>> Commands;
+
+/* An argument parser that supports multiple subcommands,
+ i.e. ‘<command> <subcommand>’. */
+struct MultiCommand : virtual Args
+{
+ Commands commands;
+
+ std::shared_ptr<Command> command;
+
+ MultiCommand(const Commands & commands);
+
+ void printHelp(const string & programName, std::ostream & out) override;
+
+ bool processFlag(Strings::iterator & pos, Strings::iterator end) override;
+
+ bool processArgs(const Strings & args, bool finish) override;
+};
+
+/* A helper class for registering commands globally. */
+struct RegisterCommand
+{
+ static Commands * commands;
+
+ RegisterCommand(ref<Command> command)
+ {
+ if (!commands) commands = new Commands;
+ commands->emplace(command->name(), command);
+ }
+};
+
+}
diff --git a/src/nix/hash.cc b/src/nix/hash.cc
new file mode 100644
index 000000000..5dd891e8a
--- /dev/null
+++ b/src/nix/hash.cc
@@ -0,0 +1,140 @@
+#include "command.hh"
+#include "hash.hh"
+#include "legacy.hh"
+#include "shared.hh"
+
+using namespace nix;
+
+struct CmdHash : Command
+{
+ enum Mode { mFile, mPath };
+ Mode mode;
+ bool base32 = false;
+ bool truncate = false;
+ HashType ht = htSHA512;
+ Strings paths;
+
+ CmdHash(Mode mode) : mode(mode)
+ {
+ mkFlag(0, "base32", "print hash in base-32", &base32);
+ mkFlag(0, "base16", "print hash in base-16", &base32, false);
+ mkHashTypeFlag("type", &ht);
+ expectArgs("paths", &paths);
+ }
+
+ std::string name() override
+ {
+ return mode == mFile ? "hash-file" : "hash-path";
+ }
+
+ std::string description() override
+ {
+ return mode == mFile
+ ? "print cryptographic hash of a regular file"
+ : "print cryptographic hash of the NAR serialisation of a path";
+ }
+
+ void run() override
+ {
+ for (auto path : paths) {
+ Hash h = mode == mFile ? hashFile(ht, path) : hashPath(ht, path).first;
+ if (truncate && h.hashSize > 20) h = compressHash(h, 20);
+ std::cout << format("%1%\n") %
+ (base32 ? printHash32(h) : printHash(h));
+ }
+ }
+};
+
+static RegisterCommand r1(make_ref<CmdHash>(CmdHash::mFile));
+static RegisterCommand r2(make_ref<CmdHash>(CmdHash::mPath));
+
+struct CmdToBase : Command
+{
+ bool toBase32;
+ HashType ht = htSHA512;
+ Strings args;
+
+ CmdToBase(bool toBase32) : toBase32(toBase32)
+ {
+ mkHashTypeFlag("type", &ht);
+ expectArgs("strings", &args);
+ }
+
+ std::string name() override
+ {
+ return toBase32 ? "to-base32" : "to-base16";
+ }
+
+ std::string description() override
+ {
+ return toBase32
+ ? "convert a hash to base-32 representation"
+ : "convert a hash to base-16 representation";
+ }
+
+ void run() override
+ {
+ for (auto s : args) {
+ Hash h = parseHash16or32(ht, s);
+ std::cout << format("%1%\n") %
+ (toBase32 ? printHash32(h) : printHash(h));
+ }
+ }
+};
+
+static RegisterCommand r3(make_ref<CmdToBase>(false));
+static RegisterCommand r4(make_ref<CmdToBase>(true));
+
+/* Legacy nix-hash command. */
+static int compatNixHash(int argc, char * * argv)
+{
+ HashType ht = htMD5;
+ bool flat = false;
+ bool base32 = false;
+ bool truncate = false;
+ enum { opHash, opTo32, opTo16 } op = opHash;
+ Strings ss;
+
+ parseCmdLine(argc, argv, [&](Strings::iterator & arg, const Strings::iterator & end) {
+ if (*arg == "--help")
+ showManPage("nix-hash");
+ else if (*arg == "--version")
+ printVersion("nix-hash");
+ else if (*arg == "--flat") flat = true;
+ else if (*arg == "--base32") base32 = true;
+ else if (*arg == "--truncate") truncate = true;
+ else if (*arg == "--type") {
+ string s = getArg(*arg, arg, end);
+ ht = parseHashType(s);
+ if (ht == htUnknown)
+ throw UsageError(format("unknown hash type ‘%1%’") % s);
+ }
+ else if (*arg == "--to-base16") op = opTo16;
+ else if (*arg == "--to-base32") op = opTo32;
+ else if (*arg != "" && arg->at(0) == '-')
+ return false;
+ else
+ ss.push_back(*arg);
+ return true;
+ });
+
+ if (op == opHash) {
+ CmdHash cmd(flat ? CmdHash::mFile : CmdHash::mPath);
+ cmd.ht = ht;
+ cmd.base32 = base32;
+ cmd.truncate = truncate;
+ cmd.paths = ss;
+ cmd.run();
+ }
+
+ else {
+ CmdToBase cmd(op == opTo32);
+ cmd.args = ss;
+ cmd.ht = ht;
+ cmd.run();
+ }
+
+ return 0;
+}
+
+static RegisterLegacyCommand s1("nix-hash", compatNixHash);
diff --git a/src/nix/installables.cc b/src/nix/installables.cc
new file mode 100644
index 000000000..fb5a51582
--- /dev/null
+++ b/src/nix/installables.cc
@@ -0,0 +1,75 @@
+#include "attr-path.hh"
+#include "common-opts.hh"
+#include "derivations.hh"
+#include "eval-inline.hh"
+#include "eval.hh"
+#include "get-drvs.hh"
+#include "installables.hh"
+#include "store-api.hh"
+
+namespace nix {
+
+UserEnvElems MixInstallables::evalInstallables(ref<Store> store)
+{
+ UserEnvElems res;
+
+ for (auto & installable : installables) {
+
+ if (std::string(installable, 0, 1) == "/") {
+
+ if (isStorePath(installable)) {
+
+ if (isDerivation(installable)) {
+ UserEnvElem elem;
+ // FIXME: handle empty case, drop version
+ elem.attrPath = {storePathToName(installable)};
+ elem.isDrv = true;
+ elem.drvPath = installable;
+ res.push_back(elem);
+ }
+
+ else {
+ UserEnvElem elem;
+ // FIXME: handle empty case, drop version
+ elem.attrPath = {storePathToName(installable)};
+ elem.isDrv = false;
+ elem.outPaths = {installable};
+ res.push_back(elem);
+ }
+ }
+
+ else
+ throw UsageError(format("don't know what to do with ‘%1%’") % installable);
+ }
+
+ else {
+
+ EvalState state({}, store);
+
+ Expr * e = state.parseExprFromFile(resolveExprPath(lookupFileArg(state, file)));
+
+ Value vRoot;
+ state.eval(e, vRoot);
+
+ std::map<string, string> autoArgs_;
+ Bindings & autoArgs(*evalAutoArgs(state, autoArgs_));
+
+ Value & v(*findAlongAttrPath(state, installable, autoArgs, vRoot));
+ state.forceValue(v);
+
+ DrvInfos drvs;
+ getDerivations(state, v, "", autoArgs, drvs, false);
+
+ for (auto & i : drvs) {
+ UserEnvElem elem;
+ elem.isDrv = true;
+ elem.drvPath = i.queryDrvPath();
+ res.push_back(elem);
+ }
+ }
+ }
+
+ return res;
+}
+
+}
diff --git a/src/nix/installables.hh b/src/nix/installables.hh
new file mode 100644
index 000000000..5eb897d46
--- /dev/null
+++ b/src/nix/installables.hh
@@ -0,0 +1,38 @@
+#pragma once
+
+#include "args.hh"
+
+namespace nix {
+
+struct UserEnvElem
+{
+ Strings attrPath;
+
+ // FIXME: should use boost::variant or so.
+ bool isDrv;
+
+ // Derivation case:
+ Path drvPath;
+ StringSet outputNames;
+
+ // Non-derivation case:
+ PathSet outPaths;
+};
+
+typedef std::vector<UserEnvElem> UserEnvElems;
+
+struct MixInstallables : virtual Args
+{
+ Strings installables;
+ Path file = "<nixpkgs>";
+
+ MixInstallables()
+ {
+ mkFlag('f', "file", "file", "evaluate FILE rather than the default", &file);
+ expectArgs("installables", &installables);
+ }
+
+ UserEnvElems evalInstallables(ref<Store> store);
+};
+
+}
diff --git a/src/nix/legacy.cc b/src/nix/legacy.cc
new file mode 100644
index 000000000..6df09ee37
--- /dev/null
+++ b/src/nix/legacy.cc
@@ -0,0 +1,7 @@
+#include "legacy.hh"
+
+namespace nix {
+
+RegisterLegacyCommand::Commands * RegisterLegacyCommand::commands = 0;
+
+}
diff --git a/src/nix/legacy.hh b/src/nix/legacy.hh
new file mode 100644
index 000000000..b67b70eb5
--- /dev/null
+++ b/src/nix/legacy.hh
@@ -0,0 +1,22 @@
+#pragma once
+
+#include <functional>
+#include <map>
+
+namespace nix {
+
+typedef std::function<void(int, char * *)> MainFunction;
+
+struct RegisterLegacyCommand
+{
+ typedef std::map<std::string, MainFunction> Commands;
+ static Commands * commands;
+
+ RegisterLegacyCommand(const std::string & name, MainFunction fun)
+ {
+ if (!commands) commands = new Commands;
+ (*commands)[name] = fun;
+ }
+};
+
+}
diff --git a/src/nix/local.mk b/src/nix/local.mk
new file mode 100644
index 000000000..f6e7073b6
--- /dev/null
+++ b/src/nix/local.mk
@@ -0,0 +1,9 @@
+programs += nix
+
+nix_DIR := $(d)
+
+nix_SOURCES := $(wildcard $(d)/*.cc)
+
+nix_LIBS = libexpr libmain libstore libutil libformat
+
+$(eval $(call install-symlink, nix, $(bindir)/nix-hash))
diff --git a/src/nix/ls.cc b/src/nix/ls.cc
new file mode 100644
index 000000000..3476dfb05
--- /dev/null
+++ b/src/nix/ls.cc
@@ -0,0 +1,123 @@
+#include "command.hh"
+#include "store-api.hh"
+#include "fs-accessor.hh"
+#include "nar-accessor.hh"
+
+using namespace nix;
+
+struct MixLs : virtual Args
+{
+ std::string path;
+
+ bool recursive = false;
+ bool verbose = false;
+ bool showDirectory = false;
+
+ MixLs()
+ {
+ mkFlag('R', "recursive", "list subdirectories recursively", &recursive);
+ mkFlag('l', "long", "show more file information", &verbose);
+ mkFlag('d', "directory", "show directories rather than their contents", &showDirectory);
+ }
+
+ void list(ref<FSAccessor> accessor)
+ {
+ std::function<void(const FSAccessor::Stat &, const Path &, const std::string &, bool)> doPath;
+
+ auto showFile = [&](const Path & curPath, const std::string & relPath) {
+ if (verbose) {
+ auto st = accessor->stat(curPath);
+ std::string tp =
+ st.type == FSAccessor::Type::tRegular ?
+ (st.isExecutable ? "-r-xr-xr-x" : "-r--r--r--") :
+ st.type == FSAccessor::Type::tSymlink ? "lrwxrwxrwx" :
+ "dr-xr-xr-x";
+ std::cout <<
+ (format("%s %20d %s") % tp % st.fileSize % relPath);
+ if (st.type == FSAccessor::Type::tSymlink)
+ std::cout << " -> " << accessor->readLink(curPath)
+ ;
+ std::cout << "\n";
+ if (recursive && st.type == FSAccessor::Type::tDirectory)
+ doPath(st, curPath, relPath, false);
+ } else {
+ std::cout << relPath << "\n";
+ if (recursive) {
+ auto st = accessor->stat(curPath);
+ if (st.type == FSAccessor::Type::tDirectory)
+ doPath(st, curPath, relPath, false);
+ }
+ }
+ };
+
+ doPath = [&](const FSAccessor::Stat & st , const Path & curPath,
+ const std::string & relPath, bool showDirectory)
+ {
+ if (st.type == FSAccessor::Type::tDirectory && !showDirectory) {
+ auto names = accessor->readDirectory(curPath);
+ for (auto & name : names)
+ showFile(curPath + "/" + name, relPath + "/" + name);
+ } else
+ showFile(curPath, relPath);
+ };
+
+ auto st = accessor->stat(path);
+ if (st.type == FSAccessor::Type::tMissing)
+ throw Error(format("path ‘%1%’ does not exist") % path);
+ doPath(st, path,
+ st.type == FSAccessor::Type::tDirectory ? "." : baseNameOf(path),
+ showDirectory);
+ }
+};
+
+struct CmdLsStore : StoreCommand, MixLs
+{
+ CmdLsStore()
+ {
+ expectArg("path", &path);
+ }
+
+ std::string name() override
+ {
+ return "ls-store";
+ }
+
+ std::string description() override
+ {
+ return "show information about a store path";
+ }
+
+ void run(ref<Store> store) override
+ {
+ list(store->getFSAccessor());
+ }
+};
+
+struct CmdLsNar : Command, MixLs
+{
+ Path narPath;
+
+ CmdLsNar()
+ {
+ expectArg("nar", &narPath);
+ expectArg("path", &path);
+ }
+
+ std::string name() override
+ {
+ return "ls-nar";
+ }
+
+ std::string description() override
+ {
+ return "show information about the contents of a NAR file";
+ }
+
+ void run() override
+ {
+ list(makeNarAccessor(make_ref<std::string>(readFile(narPath))));
+ }
+};
+
+static RegisterCommand r1(make_ref<CmdLsStore>());
+static RegisterCommand r2(make_ref<CmdLsNar>());
diff --git a/src/nix/main.cc b/src/nix/main.cc
new file mode 100644
index 000000000..20d3ea5c2
--- /dev/null
+++ b/src/nix/main.cc
@@ -0,0 +1,55 @@
+#include <algorithm>
+
+#include "command.hh"
+#include "common-args.hh"
+#include "eval.hh"
+#include "globals.hh"
+#include "legacy.hh"
+#include "shared.hh"
+#include "store-api.hh"
+
+namespace nix {
+
+struct NixArgs : virtual MultiCommand, virtual MixCommonArgs
+{
+ NixArgs() : MultiCommand(*RegisterCommand::commands), MixCommonArgs("nix")
+ {
+ mkFlag('h', "help", "show usage information", [=]() {
+ printHelp(programName, std::cout);
+ throw Exit();
+ });
+
+ mkFlag(0, "version", "show version information", std::bind(printVersion, programName));
+ }
+};
+
+void mainWrapped(int argc, char * * argv)
+{
+ initNix();
+ initGC();
+
+ string programName = baseNameOf(argv[0]);
+
+ {
+ auto legacy = (*RegisterLegacyCommand::commands)[programName];
+ if (legacy) return legacy(argc, argv);
+ }
+
+ NixArgs args;
+
+ args.parseCmdline(argvToStrings(argc, argv));
+
+ assert(args.command);
+
+ args.command->prepare();
+ args.command->run();
+}
+
+}
+
+int main(int argc, char * * argv)
+{
+ return nix::handleExceptions(argv[0], [&]() {
+ nix::mainWrapped(argc, argv);
+ });
+}