Nix/src/nix/main.cc
regnat 04e5d0e704 Add a description in the completion outputs
Make nix output completions in the form `completion\tdescription`.
This can't be used by bash (afaik), but other shells like zsh or fish
can display it along the completion choices
2020-10-09 09:39:51 +02:00

264 lines
7.8 KiB
C++

#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"
#include "filetransfer.hh"
#include "finally.hh"
#include "loggers.hh"
#include <sys/types.h>
#include <sys/socket.h>
#include <ifaddrs.h>
#include <netdb.h>
#include <netinet/in.h>
#include <nlohmann/json.hpp>
extern std::string chrootHelperName;
void chrootHelper(int argc, char * * argv);
namespace nix {
/* Check if we have a non-loopback/link-local network interface. */
static bool haveInternet()
{
struct ifaddrs * addrs;
if (getifaddrs(&addrs))
return true;
Finally free([&]() { freeifaddrs(addrs); });
for (auto i = addrs; i; i = i->ifa_next) {
if (!i->ifa_addr) continue;
if (i->ifa_addr->sa_family == AF_INET) {
if (ntohl(((sockaddr_in *) i->ifa_addr)->sin_addr.s_addr) != INADDR_LOOPBACK) {
return true;
}
} else if (i->ifa_addr->sa_family == AF_INET6) {
if (!IN6_IS_ADDR_LOOPBACK(&((sockaddr_in6 *) i->ifa_addr)->sin6_addr) &&
!IN6_IS_ADDR_LINKLOCAL(&((sockaddr_in6 *) i->ifa_addr)->sin6_addr))
return true;
}
}
return false;
}
std::string programPath;
struct NixArgs : virtual MultiCommand, virtual MixCommonArgs
{
bool printBuildLogs = false;
bool useNet = true;
bool refresh = false;
NixArgs() : MultiCommand(*RegisterCommand::commands), MixCommonArgs("nix")
{
categories.clear();
categories[Command::catDefault] = "Main commands";
categories[catSecondary] = "Infrequently used commands";
categories[catUtility] = "Utility/scripting commands";
categories[catNixInstallation] = "Commands for upgrading or troubleshooting your Nix installation";
addFlag({
.longName = "help",
.description = "show usage information",
.handler = {[&]() { if (!completions) showHelpAndExit(); }},
});
addFlag({
.longName = "help-config",
.description = "show configuration options",
.handler = {[&]() {
std::cout << "The following configuration options are available:\n\n";
Table2 tbl;
std::map<std::string, Config::SettingInfo> settings;
globalConfig.getSettings(settings);
for (const auto & s : settings)
tbl.emplace_back(s.first, s.second.description);
printTable(std::cout, tbl);
throw Exit();
}},
});
addFlag({
.longName = "print-build-logs",
.shortName = 'L',
.description = "print full build logs on stderr",
.handler = {[&]() {setLogFormat(LogFormat::barWithLogs); }},
});
addFlag({
.longName = "version",
.description = "show version information",
.handler = {[&]() { if (!completions) printVersion(programName); }},
});
addFlag({
.longName = "no-net",
.description = "disable substituters and consider all previously downloaded files up-to-date",
.handler = {[&]() { useNet = false; }},
});
addFlag({
.longName = "refresh",
.description = "consider all previously downloaded files out-of-date",
.handler = {[&]() { refresh = true; }},
});
deprecatedAliases.insert({"dev-shell", "develop"});
}
void printFlags(std::ostream & out) override
{
Args::printFlags(out);
std::cout <<
"\n"
"In addition, most configuration settings can be overriden using '--" ANSI_ITALIC "name value" ANSI_NORMAL "'.\n"
"Boolean settings can be overriden using '--" ANSI_ITALIC "name" ANSI_NORMAL "' or '--no-" ANSI_ITALIC "name" ANSI_NORMAL "'. See 'nix\n"
"--help-config' for a list of configuration settings.\n";
}
void printHelp(const string & programName, std::ostream & out) override
{
MultiCommand::printHelp(programName, out);
#if 0
out << "\nFor full documentation, run 'man " << programName << "' or 'man " << programName << "-" ANSI_ITALIC "COMMAND" ANSI_NORMAL "'.\n";
#endif
std::cout << "\nNote: this program is " ANSI_RED "EXPERIMENTAL" ANSI_NORMAL " and subject to change.\n";
}
void showHelpAndExit()
{
printHelp(programName, std::cout);
throw Exit();
}
std::string description() override
{
return "a tool for reproducible and declarative configuration management";
}
};
void mainWrapped(int argc, char * * argv)
{
/* The chroot helper needs to be run before any threads have been
started. */
if (argc > 0 && argv[0] == chrootHelperName) {
chrootHelper(argc, argv);
return;
}
initNix();
initGC();
programPath = argv[0];
auto programName = std::string(baseNameOf(programPath));
{
auto legacy = (*RegisterLegacyCommand::commands)[programName];
if (legacy) return legacy(argc, argv);
}
verbosity = lvlWarn;
settings.verboseBuild = false;
evalSettings.pureEval = true;
setLogFormat("bar");
Finally f([] { logger->stop(); });
NixArgs args;
if (argc == 2 && std::string(argv[1]) == "__dump-args") {
std::cout << args.toJSON().dump() << "\n";
return;
}
if (argc == 2 && std::string(argv[1]) == "__dump-builtins") {
evalSettings.pureEval = false;
EvalState state({}, openStore("dummy://"));
auto res = nlohmann::json::object();
auto builtins = state.baseEnv.values[0]->attrs;
for (auto & builtin : *builtins) {
auto b = nlohmann::json::object();
if (builtin.value->type != tPrimOp) continue;
auto primOp = builtin.value->primOp;
if (!primOp->doc) continue;
b["arity"] = primOp->arity;
b["args"] = primOp->args;
b["doc"] = trim(stripIndentation(primOp->doc));
res[(std::string) builtin.name] = std::move(b);
}
std::cout << res.dump() << "\n";
return;
}
Finally printCompletions([&]()
{
if (completions) {
std::cout << (pathCompletions ? "filenames\n" : "no-filenames\n");
for (auto & s : *completions)
std::cout << s.completion << "\t" << s.description << "\n";
}
});
try {
args.parseCmdline(argvToStrings(argc, argv));
} catch (UsageError &) {
if (!completions) throw;
}
if (completions) return;
initPlugins();
if (!args.command) args.showHelpAndExit();
if (args.command->first != "repl"
&& args.command->first != "doctor"
&& args.command->first != "upgrade-nix")
settings.requireExperimentalFeature("nix-command");
if (args.useNet && !haveInternet()) {
warn("you don't have Internet access; disabling some network-dependent features");
args.useNet = false;
}
if (!args.useNet) {
// FIXME: should check for command line overrides only.
if (!settings.useSubstitutes.overriden)
settings.useSubstitutes = false;
if (!settings.tarballTtl.overriden)
settings.tarballTtl = std::numeric_limits<unsigned int>::max();
if (!fileTransferSettings.tries.overriden)
fileTransferSettings.tries = 0;
if (!fileTransferSettings.connectTimeout.overriden)
fileTransferSettings.connectTimeout = 1;
}
if (args.refresh)
settings.tarballTtl = 0;
args.command->second->prepare();
args.command->second->run();
}
}
int main(int argc, char * * argv)
{
return nix::handleExceptions(argv[0], [&]() {
nix::mainWrapped(argc, argv);
});
}