Start of new Nix command-line interface

This commit is contained in:
Eelco Dolstra 2016-02-09 21:28:29 +01:00
parent 0db9e6cd1a
commit cd2196b089
14 changed files with 362 additions and 74 deletions

3
.gitignore vendored
View file

@ -73,9 +73,6 @@ Makefile.config
# /src/nix-env/
/src/nix-env/nix-env
# /src/nix-hash/
/src/nix-hash/nix-hash
# /src/nix-instantiate/
/src/nix-instantiate/nix-instantiate

View file

@ -5,7 +5,7 @@ makefiles = \
src/libstore/local.mk \
src/libmain/local.mk \
src/libexpr/local.mk \
src/nix-hash/local.mk \
src/nix/local.mk \
src/nix-store/local.mk \
src/nix-instantiate/local.mk \
src/nix-env/local.mk \

View file

@ -4,6 +4,7 @@
namespace nix {
MixCommonArgs::MixCommonArgs(const string & programName)
: programName(programName)
{
mkFlag('v', "verbose", "increase verbosity level", []() {
verbosity = (Verbosity) (verbosity + 1);

View file

@ -6,6 +6,7 @@ namespace nix {
struct MixCommonArgs : virtual Args
{
string programName;
MixCommonArgs(const string & programName);
};

View file

@ -145,6 +145,8 @@ public:
*dest = ss;
}});
}
friend class MultiCommand;
};
Strings argvToStrings(int argc, char * * argv);

View file

@ -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

View file

@ -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));
}
}
});
}

65
src/nix/command.cc Normal file
View file

@ -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(reserveSpace));
}
}

59
src/nix/command.hh Normal file
View file

@ -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);
}
};
}

140
src/nix/hash.cc Normal file
View file

@ -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<Command, CmdHash>(CmdHash::mFile));
static RegisterCommand r2(make_ref<Command, 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-32 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<Command, CmdToBase>(false));
static RegisterCommand r4(make_ref<Command, 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);

7
src/nix/legacy.cc Normal file
View file

@ -0,0 +1,7 @@
#include "legacy.hh"
namespace nix {
RegisterLegacyCommand::Commands * RegisterLegacyCommand::commands = 0;
}

22
src/nix/legacy.hh Normal file
View file

@ -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;
}
};
}

9
src/nix/local.mk Normal file
View file

@ -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))

55
src/nix/main.cc Normal file
View file

@ -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);
});
}