aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/libmain/common-args.cc6
-rw-r--r--src/libmain/shared.cc10
-rw-r--r--src/libstore/build.cc11
-rw-r--r--src/libstore/crypto.cc4
-rw-r--r--src/libstore/download.cc2
-rw-r--r--src/libstore/globals.cc317
-rw-r--r--src/libstore/globals.hh312
-rw-r--r--src/libstore/local-store.cc4
-rw-r--r--src/libstore/remote-store.cc4
-rw-r--r--src/libstore/store-api.cc9
-rw-r--r--src/libutil/config.cc104
-rw-r--r--src/libutil/config.hh22
-rw-r--r--src/nix-daemon/nix-daemon.cc13
-rw-r--r--src/nix-store/nix-store.cc8
-rw-r--r--src/nix/main.cc1
15 files changed, 323 insertions, 504 deletions
diff --git a/src/libmain/common-args.cc b/src/libmain/common-args.cc
index 98693d78a..9a7a89313 100644
--- a/src/libmain/common-args.cc
+++ b/src/libmain/common-args.cc
@@ -22,7 +22,11 @@ MixCommonArgs::MixCommonArgs(const string & programName)
[](Strings ss) {
auto name = ss.front(); ss.pop_front();
auto value = ss.front();
- settings.set(name, value);
+ try {
+ settings.set(name, value);
+ } catch (UsageError & e) {
+ warn(e.what());
+ }
});
}
diff --git a/src/libmain/shared.cc b/src/libmain/shared.cc
index c1828aa7d..9d506d019 100644
--- a/src/libmain/shared.cc
+++ b/src/libmain/shared.cc
@@ -138,9 +138,6 @@ void initNix()
struct timeval tv;
gettimeofday(&tv, 0);
srandom(tv.tv_usec);
-
- if (char *pack = getenv("_NIX_OPTIONS"))
- settings.unpack(pack);
}
@@ -156,10 +153,10 @@ struct LegacyArgs : public MixCommonArgs
&settings.verboseBuild, false);
mkFlag('K', "keep-failed", "keep temporary directories of failed builds",
- &settings.keepFailed);
+ &(bool&) settings.keepFailed);
mkFlag('k', "keep-going", "keep going after a build fails",
- &settings.keepGoing);
+ &(bool&) settings.keepGoing);
mkFlag(0, "fallback", "build from source if substitution fails", []() {
settings.set("build-fallback", "true");
@@ -184,7 +181,7 @@ struct LegacyArgs : public MixCommonArgs
&settings.readOnlyMode);
mkFlag(0, "no-build-hook", "disable use of the build hook mechanism",
- &settings.useBuildHook, false);
+ &(bool&) settings.useBuildHook, false);
mkFlag(0, "show-trace", "show Nix expression stack trace in evaluation errors",
&settings.showTrace);
@@ -218,7 +215,6 @@ 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/libstore/build.cc b/src/libstore/build.cc
index b23447fa0..33c9e3704 100644
--- a/src/libstore/build.cc
+++ b/src/libstore/build.cc
@@ -462,7 +462,7 @@ UserLock::UserLock()
assert(settings.buildUsersGroup != "");
/* Get the members of the build-users-group. */
- struct group * gr = getgrnam(settings.buildUsersGroup.c_str());
+ struct group * gr = getgrnam(settings.buildUsersGroup.get().c_str());
if (!gr)
throw Error(format("the group ‘%1%’ specified in ‘build-users-group’ does not exist")
% settings.buildUsersGroup);
@@ -1690,10 +1690,7 @@ void DerivationGoal::startBuilder()
/* Are we doing a chroot build? */
{
- string x = settings.useSandbox;
- if (x != "true" && x != "false" && x != "relaxed")
- throw Error("option ‘build-use-sandbox’ must be set to one of ‘true’, ‘false’ or ‘relaxed’");
- if (x == "true") {
+ if (settings.sandboxMode == smEnabled) {
if (get(drv->env, "__noChroot") == "1")
throw Error(format("derivation ‘%1%’ has ‘__noChroot’ set, "
"but that's not allowed when ‘build-use-sandbox’ is ‘true’") % drvPath);
@@ -1704,9 +1701,9 @@ void DerivationGoal::startBuilder()
#endif
useChroot = true;
}
- else if (x == "false")
+ else if (settings.sandboxMode == smDisabled)
useChroot = false;
- else if (x == "relaxed")
+ else if (settings.sandboxMode == smRelaxed)
useChroot = !fixedOutput && get(drv->env, "__noChroot") != "1";
}
diff --git a/src/libstore/crypto.cc b/src/libstore/crypto.cc
index 9692dd83b..f56a6adab 100644
--- a/src/libstore/crypto.cc
+++ b/src/libstore/crypto.cc
@@ -105,12 +105,12 @@ PublicKeys getDefaultPublicKeys()
// FIXME: filter duplicates
- for (auto s : settings.binaryCachePublicKeys) {
+ for (auto s : settings.binaryCachePublicKeys.get()) {
PublicKey key(s);
publicKeys.emplace(key.name, key);
}
- for (auto secretKeyFile : settings.secretKeyFiles) {
+ for (auto secretKeyFile : settings.secretKeyFiles.get()) {
try {
SecretKey secretKey(readFile(secretKeyFile));
publicKeys.emplace(secretKey.name, secretKey.toPublicKey());
diff --git a/src/libstore/download.cc b/src/libstore/download.cc
index 95e6f7bac..d073e870b 100644
--- a/src/libstore/download.cc
+++ b/src/libstore/download.cc
@@ -251,7 +251,7 @@ struct CurlDownloader : public Downloader
/* If no file exist in the specified path, curl continues to work
anyway as if netrc support was disabled. */
- curl_easy_setopt(req, CURLOPT_NETRC_FILE, settings.netrcFile.c_str());
+ curl_easy_setopt(req, CURLOPT_NETRC_FILE, settings.netrcFile.get().c_str());
curl_easy_setopt(req, CURLOPT_NETRC, CURL_NETRC_OPTIONAL);
result.data = std::make_shared<std::string>();
diff --git a/src/libstore/globals.cc b/src/libstore/globals.cc
index b9f4fada5..bb61daa51 100644
--- a/src/libstore/globals.cc
+++ b/src/libstore/globals.cc
@@ -1,6 +1,7 @@
#include "globals.hh"
#include "util.hh"
#include "archive.hh"
+#include "args.hh"
#include <algorithm>
#include <map>
@@ -26,329 +27,89 @@ namespace nix {
Settings settings;
-
Settings::Settings()
+ : Config({})
+ , nixPrefix(NIX_PREFIX)
+ , nixStore(canonPath(getEnv("NIX_STORE_DIR", getEnv("NIX_STORE", NIX_STORE_DIR))))
+ , nixDataDir(canonPath(getEnv("NIX_DATA_DIR", NIX_DATA_DIR)))
+ , nixLogDir(canonPath(getEnv("NIX_LOG_DIR", NIX_LOG_DIR)))
+ , nixStateDir(canonPath(getEnv("NIX_STATE_DIR", NIX_STATE_DIR)))
+ , nixConfDir(canonPath(getEnv("NIX_CONF_DIR", NIX_CONF_DIR)))
+ , nixLibexecDir(canonPath(getEnv("NIX_LIBEXEC_DIR", NIX_LIBEXEC_DIR)))
+ , nixBinDir(canonPath(getEnv("NIX_BIN_DIR", NIX_BIN_DIR)))
+ , nixDaemonSocketFile(canonPath(nixStateDir + DEFAULT_SOCKET_PATH))
{
- deprecatedOptions = StringSet({
- "build-use-chroot", "build-chroot-dirs", "build-extra-chroot-dirs",
- "this-option-never-existed-but-who-will-know"
- });
-
- nixPrefix = NIX_PREFIX;
- nixStore = canonPath(getEnv("NIX_STORE_DIR", getEnv("NIX_STORE", NIX_STORE_DIR)));
- nixDataDir = canonPath(getEnv("NIX_DATA_DIR", NIX_DATA_DIR));
- nixLogDir = canonPath(getEnv("NIX_LOG_DIR", NIX_LOG_DIR));
- nixStateDir = canonPath(getEnv("NIX_STATE_DIR", NIX_STATE_DIR));
- nixConfDir = canonPath(getEnv("NIX_CONF_DIR", NIX_CONF_DIR));
- nixLibexecDir = canonPath(getEnv("NIX_LIBEXEC_DIR", NIX_LIBEXEC_DIR));
- nixBinDir = canonPath(getEnv("NIX_BIN_DIR", NIX_BIN_DIR));
- nixDaemonSocketFile = canonPath(nixStateDir + DEFAULT_SOCKET_PATH);
-
- // should be set with the other config options, but depends on nixLibexecDir
-#ifdef __APPLE__
- preBuildHook = nixLibexecDir + "/nix/resolve-system-dependencies";
-#endif
-
- keepFailed = false;
- keepGoing = false;
- tryFallback = false;
- maxBuildJobs = 1;
- buildCores = std::max(1U, std::thread::hardware_concurrency());
- readOnlyMode = false;
- thisSystem = SYSTEM;
- maxSilentTime = 0;
- buildTimeout = 0;
- useBuildHook = true;
- reservedSize = 8 * 1024 * 1024;
- fsyncMetadata = true;
- useSQLiteWAL = true;
- syncBeforeRegistering = false;
- useSubstitutes = true;
buildUsersGroup = getuid() == 0 ? "nixbld" : "";
- useSshSubstituter = true;
- impersonateLinux26 = false;
- keepLog = true;
- compressLog = true;
- maxLogSize = 0;
- pollInterval = 5;
- checkRootReachability = false;
- gcKeepOutputs = false;
- gcKeepDerivations = true;
- autoOptimiseStore = false;
- envKeepDerivations = false;
lockCPU = getEnv("NIX_AFFINITY_HACK", "1") == "1";
- showTrace = false;
- enableNativeCode = false;
- netrcFile = fmt("%s/%s", nixConfDir, "netrc");
caFile = getEnv("NIX_SSL_CERT_FILE", getEnv("SSL_CERT_FILE", "/etc/ssl/certs/ca-certificates.crt"));
- enableImportFromDerivation = true;
- useSandbox = "false"; // TODO: make into an enum
#if __linux__
sandboxPaths = tokenizeString<StringSet>("/bin/sh=" BASH_PATH);
#endif
- restrictEval = false;
- buildRepeat = 0;
allowedImpureHostPrefixes = tokenizeString<StringSet>(DEFAULT_ALLOWED_IMPURE_PREFIXES);
- sandboxShmSize = "50%";
- darwinLogSandboxViolations = false;
- runDiffHook = false;
- diffHook = "";
- enforceDeterminism = true;
- binaryCachePublicKeys = Strings{"cache.nixos.org-1:6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY="};
- secretKeyFiles = Strings();
- binaryCachesParallelConnections = 25;
- enableHttp2 = true;
- tarballTtl = 60 * 60;
- signedBinaryCaches = "";
- substituters = Strings();
- binaryCaches = nixStore == "/nix/store" ? Strings{"https://cache.nixos.org/"} : Strings();
- extraBinaryCaches = Strings();
- trustedUsers = Strings({"root"});
- allowedUsers = Strings({"*"});
- printMissing = true;
}
-
void Settings::loadConfFile()
{
- Path settingsFile = (format("%1%/%2%") % nixConfDir % "nix.conf").str();
- if (!pathExists(settingsFile)) return;
- string contents = readFile(settingsFile);
-
- unsigned int pos = 0;
-
- while (pos < contents.size()) {
- string line;
- while (pos < contents.size() && contents[pos] != '\n')
- line += contents[pos++];
- pos++;
-
- string::size_type hash = line.find('#');
- if (hash != string::npos)
- line = string(line, 0, hash);
-
- vector<string> tokens = tokenizeString<vector<string> >(line);
- if (tokens.empty()) continue;
-
- if (tokens.size() < 2 || tokens[1] != "=")
- throw Error(format("illegal configuration line ‘%1%’ in ‘%2%’") % line % settingsFile);
-
- string name = tokens[0];
-
- vector<string>::iterator i = tokens.begin();
- advance(i, 2);
- settings[name] = concatStringsSep(" ", Strings(i, tokens.end())); // FIXME: slow
- };
+ applyConfigFile(nixConfDir + "/nix.conf");
}
-
void Settings::set(const string & name, const string & value)
{
- settings[name] = value;
overrides[name] = value;
+ Config::set(name, value);
}
-void Settings::update()
-{
- _get(tryFallback, "build-fallback");
-
- std::string s = "1";
- _get(s, "build-max-jobs");
- if (s == "auto")
- maxBuildJobs = std::max(1U, std::thread::hardware_concurrency());
- else
- if (!string2Int(s, maxBuildJobs))
- throw Error("configuration setting ‘build-max-jobs’ should be ‘auto’ or an integer");
-
- _get(buildCores, "build-cores");
- _get(thisSystem, "system");
- _get(maxSilentTime, "build-max-silent-time");
- _get(buildTimeout, "build-timeout");
- _get(reservedSize, "gc-reserved-space");
- _get(fsyncMetadata, "fsync-metadata");
- _get(useSQLiteWAL, "use-sqlite-wal");
- _get(syncBeforeRegistering, "sync-before-registering");
- _get(useSubstitutes, "build-use-substitutes");
- _get(buildUsersGroup, "build-users-group");
- _get(impersonateLinux26, "build-impersonate-linux-26");
- _get(keepLog, "build-keep-log");
- _get(compressLog, "build-compress-log");
- _get(maxLogSize, "build-max-log-size");
- _get(pollInterval, "build-poll-interval");
- _get(checkRootReachability, "gc-check-reachability");
- _get(gcKeepOutputs, "gc-keep-outputs");
- _get(gcKeepDerivations, "gc-keep-derivations");
- _get(autoOptimiseStore, "auto-optimise-store");
- _get(envKeepDerivations, "env-keep-derivations");
- _get(sshSubstituterHosts, "ssh-substituter-hosts");
- _get(useSshSubstituter, "use-ssh-substituter");
- _get(enableNativeCode, "allow-unsafe-native-code-during-evaluation");
- _get(useCaseHack, "use-case-hack");
- _get(preBuildHook, "pre-build-hook");
- _get(keepGoing, "keep-going");
- _get(keepFailed, "keep-failed");
- _get(netrcFile, "netrc-file");
- _get(enableImportFromDerivation, "allow-import-from-derivation");
- _get(useSandbox, "build-use-sandbox", "build-use-chroot");
- _get(sandboxPaths, "build-sandbox-paths", "build-chroot-dirs");
- _get(extraSandboxPaths, "build-extra-sandbox-paths", "build-extra-chroot-dirs");
- _get(restrictEval, "restrict-eval");
- _get(buildRepeat, "build-repeat");
- _get(allowedImpureHostPrefixes, "allowed-impure-host-deps");
- _get(sandboxShmSize, "sandbox-dev-shm-size");
- _get(darwinLogSandboxViolations, "darwin-log-sandbox-violations");
- _get(runDiffHook, "run-diff-hook");
- _get(diffHook, "diff-hook");
- _get(enforceDeterminism, "enforce-determinism");
- _get(binaryCachePublicKeys, "binary-cache-public-keys");
- _get(secretKeyFiles, "secret-key-files");
- _get(binaryCachesParallelConnections, "binary-caches-parallel-connections");
- _get(enableHttp2, "enable-http2");
- _get(tarballTtl, "tarball-ttl");
- _get(signedBinaryCaches, "signed-binary-caches");
- _get(substituters, "substituters");
- _get(binaryCaches, "binary-caches");
- _get(extraBinaryCaches, "extra-binary-caches");
- _get(trustedUsers, "trusted-users");
- _get(allowedUsers, "allowed-users");
- _get(printMissing, "print-missing");
-
- /* Clear out any deprecated options that might be left, so users know we recognize the option
- but aren't processing it anymore */
- for (auto &i : deprecatedOptions) {
- if (settings.find(i) != settings.end()) {
- printError(format("warning: deprecated option '%1%' is no longer supported and will be ignored") % i);
- settings.erase(i);
- }
- }
-
- if (settings.size() != 0) {
- string bad;
- for (auto &i : settings)
- bad += "'" + i.first + "', ";
- bad.pop_back();
- bad.pop_back();
- throw Error(format("unrecognized options: %s") % bad);
- }
-}
-
-void Settings::checkDeprecated(const string & name)
+StringMap Settings::getOverrides()
{
- if (deprecatedOptions.find(name) != deprecatedOptions.end())
- printError(format("warning: deprecated option '%1%' will soon be unsupported") % name);
-}
-
-void Settings::_get(string & res, const string & name)
-{
- SettingsMap::iterator i = settings.find(name);
- if (i == settings.end()) return;
- checkDeprecated(i->first);
- settings.erase(i);
- res = i->second;
-}
-
-void Settings::_get(string & res, const string & name1, const string & name2)
-{
- SettingsMap::iterator i = settings.find(name1);
- if (i == settings.end()) i = settings.find(name2);
- if (i == settings.end()) return;
- checkDeprecated(i->first);
- settings.erase(i);
- res = i->second;
+ return overrides;
}
-
-void Settings::_get(bool & res, const string & name)
+unsigned int Settings::getDefaultCores()
{
- SettingsMap::iterator i = settings.find(name);
- if (i == settings.end()) return;
- checkDeprecated(i->first);
- settings.erase(i);
- if (i->second == "true") res = true;
- else if (i->second == "false") res = false;
- else throw Error(format("configuration option ‘%1%’ should be either ‘true’ or ‘false’, not ‘%2%’")
- % name % i->second);
+ return std::max(1U, std::thread::hardware_concurrency());
}
+const string nixVersion = PACKAGE_VERSION;
-void Settings::_get(StringSet & res, const string & name)
+template<> void Setting<SandboxMode>::set(const std::string & str)
{
- SettingsMap::iterator i = settings.find(name);
- if (i == settings.end()) return;
- checkDeprecated(i->first);
- settings.erase(i);
- res.clear();
- Strings ss = tokenizeString<Strings>(i->second);
- res.insert(ss.begin(), ss.end());
+ if (str == "true") value = smEnabled;
+ else if (str == "relaxed") value = smRelaxed;
+ else if (str == "false") value = smDisabled;
+ else throw UsageError("option '%s' has invalid value '%s'", name, str);
}
-void Settings::_get(StringSet & res, const string & name1, const string & name2)
+template<> std::string Setting<SandboxMode>::to_string()
{
- SettingsMap::iterator i = settings.find(name1);
- if (i == settings.end()) i = settings.find(name2);
- if (i == settings.end()) return;
- checkDeprecated(i->first);
- settings.erase(i);
- res.clear();
- Strings ss = tokenizeString<Strings>(i->second);
- res.insert(ss.begin(), ss.end());
+ if (value == smEnabled) return "true";
+ else if (value == smRelaxed) return "relaxed";
+ else if (value == smDisabled) return "false";
+ else abort();
}
-void Settings::_get(Strings & res, const string & name)
+template<> void Setting<unsigned int, Settings::MaxBuildJobsTag>::set(const std::string & str)
{
- SettingsMap::iterator i = settings.find(name);
- if (i == settings.end()) return;
- checkDeprecated(i->first);
- settings.erase(i);
- res = tokenizeString<Strings>(i->second);
+ if (str == "auto") value = std::max(1U, std::thread::hardware_concurrency());
+ else if (!string2Int(str, value))
+ throw UsageError("configuration setting ‘%s’ should be ‘auto’ or an integer", name);
}
-
-template<class N> void Settings::_get(N & res, const string & name)
+template<> std::string Setting<unsigned int, Settings::MaxBuildJobsTag>::to_string()
{
- SettingsMap::iterator i = settings.find(name);
- if (i == settings.end()) return;
- checkDeprecated(i->first);
- settings.erase(i);
- if (!string2Int(i->second, res))
- throw Error(format("configuration setting ‘%1%’ should have an integer value") % name);
+ return std::to_string(value);
}
-
-string Settings::pack()
+template<> void Setting<bool, Settings::CaseHackTag>::set(const std::string & str)
{
- string s;
- for (auto & i : settings) {
- if (i.first.find('\n') != string::npos ||
- i.first.find('=') != string::npos ||
- i.second.find('\n') != string::npos)
- throw Error("illegal option name/value");
- s += i.first; s += '='; s += i.second; s += '\n';
- }
- return s;
+ value = parseBool(str);
+ nix::useCaseHack = true;
}
-
-void Settings::unpack(const string & pack) {
- Strings lines = tokenizeString<Strings>(pack, "\n");
- for (auto & i : lines) {
- string::size_type eq = i.find('=');
- if (eq == string::npos)
- throw Error("illegal option name/value");
- set(i.substr(0, eq), i.substr(eq + 1));
- }
-}
-
-
-Settings::SettingsMap Settings::getOverrides()
+template<> std::string Setting<bool, Settings::CaseHackTag>::to_string()
{
- return overrides;
+ return printBool(value);
}
-
-const string nixVersion = PACKAGE_VERSION;
-
-
}
diff --git a/src/libstore/globals.hh b/src/libstore/globals.hh
index d47fdb7c9..95c8859cf 100644
--- a/src/libstore/globals.hh
+++ b/src/libstore/globals.hh
@@ -1,7 +1,7 @@
#pragma once
#include "types.hh"
-#include "logging.hh"
+#include "config.hh"
#include <map>
#include <sys/types.h>
@@ -9,36 +9,33 @@
namespace nix {
+typedef enum { smEnabled, smRelaxed, smDisabled } SandboxMode;
-struct Settings {
+extern bool useCaseHack; // FIXME
- typedef std::map<string, string> SettingsMap;
+class Settings : public Config {
- Settings();
+ StringMap overrides;
- void loadConfFile();
+ unsigned int getDefaultCores();
- void set(const string & name, const string & value);
+public:
- void update();
+ Settings();
- string pack();
+ void loadConfFile();
- void unpack(const string & pack);
+ void set(const string & name, const string & value);
- SettingsMap getOverrides();
+ StringMap getOverrides();
- /* TODO: the comments below should be strings and exposed via a nice command-line UI or similar.
- We should probably replace it with some sort of magic template or macro to minimize the amount
- of duplication and pain here. */
+ Path nixPrefix;
/* The directory where we store sources and derived files. */
Path nixStore;
Path nixDataDir; /* !!! fix */
- Path nixPrefix;
-
/* The directory where we log various operations. */
Path nixLogDir;
@@ -57,17 +54,14 @@ struct Settings {
/* File name of the socket the daemon listens to. */
Path nixDaemonSocketFile;
- /* Whether to keep temporary directories of failed builds. */
- bool keepFailed;
+ Setting<bool> keepFailed{this, false, "keep-failed",
+ "Whether to keep temporary directories of failed builds."};
- /* Whether to keep building subgoals when a sibling (another
- subgoal of the same goal) fails. */
- bool keepGoing;
+ Setting<bool> keepGoing{this, false, "keep-going",
+ "Whether to keep building derivations when another build fails."};
- /* Whether, if we cannot realise the known closure corresponding
- to a derivation, we should try to normalise the derivation
- instead. */
- bool tryFallback;
+ Setting<bool> tryFallback{this, tryFallback, "build-fallback",
+ "Whether to fall back to building when substitution fails."};
/* Whether to show build log output in real time. */
bool verboseBuild = true;
@@ -76,206 +70,206 @@ struct Settings {
the log to show if a build fails. */
size_t logLines = 10;
- /* Maximum number of parallel build jobs. 0 means unlimited. */
- unsigned int maxBuildJobs;
+ struct MaxBuildJobsTag { };
+ Setting<unsigned int, MaxBuildJobsTag> maxBuildJobs{this, 1, "build-max-jobs",
+ "Maximum number of parallel build jobs. \"auto\" means use number of cores."};
- /* Number of CPU cores to utilize in parallel within a build,
- i.e. by passing this number to Make via '-j'. 0 means that the
- number of actual CPU cores on the local host ought to be
- auto-detected. */
- unsigned int buildCores;
+ Setting<unsigned int> buildCores{this, getDefaultCores(), "build-cores",
+ "Number of CPU cores to utilize in parallel within a build, "
+ "i.e. by passing this number to Make via '-j'. 0 means that the "
+ "number of actual CPU cores on the local host ought to be "
+ "auto-detected."};
/* Read-only mode. Don't copy stuff to the store, don't change
the database. */
- bool readOnlyMode;
-
- /* The canonical system name, as returned by config.guess. */
- string thisSystem;
-
- /* The maximum time in seconds that a builer can go without
- producing any output on stdout/stderr before it is killed. 0
- means infinity. */
- time_t maxSilentTime;
+ bool readOnlyMode = false;
- /* The maximum duration in seconds that a builder can run. 0
- means infinity. */
- time_t buildTimeout;
+ Setting<std::string> thisSystem{this, SYSTEM, "system",
+ "The canonical Nix system name."};
- /* Whether to use build hooks (for distributed builds). Sometimes
- users want to disable this from the command-line. */
- bool useBuildHook;
+ Setting<time_t> maxSilentTime{this, 0, "build-max-silent-time",
+ "The maximum time in seconds that a builer can go without "
+ "producing any output on stdout/stderr before it is killed. "
+ "0 means infinity."};
- /* Amount of reserved space for the garbage collector
- (/nix/var/nix/db/reserved). */
- off_t reservedSize;
+ Setting<time_t> buildTimeout{this, 0, "build-timeout",
+ "The maximum duration in seconds that a builder can run. "
+ "0 means infinity."};
- /* Whether SQLite should use fsync. */
- bool fsyncMetadata;
+ Setting<bool> useBuildHook{this, true, "remote-builds",
+ "Whether to use build hooks (for distributed builds)."};
- /* Whether SQLite should use WAL mode. */
- bool useSQLiteWAL;
+ Setting<off_t> reservedSize{this, 8 * 1024 * 1024, "gc-reserved-space",
+ "Amount of reserved disk space for the garbage collector."};
- /* Whether to call sync() before registering a path as valid. */
- bool syncBeforeRegistering;
+ Setting<bool> fsyncMetadata{this, true, "fsync-metadata",
+ "Whether SQLite should use fsync()."};
- /* Whether to use substitutes. */
- bool useSubstitutes;
+ Setting<bool> useSQLiteWAL{this, true, "use-sqlite-wal",
+ "Whether SQLite should use WAL mode."};
- /* The Unix group that contains the build users. */
- string buildUsersGroup;
+ Setting<bool> syncBeforeRegistering{this, false, "sync-before-registering",
+ "Whether to call sync() before registering a path as valid."};
- /* Set of ssh connection strings for the ssh substituter */
- Strings sshSubstituterHosts;
+ Setting<bool> useSubstitutes{this, true, "build-use-substitutes",
+ "Whether to use substitutes."};
- /* Whether to use the ssh substituter at all */
- bool useSshSubstituter;
+ Setting<std::string> buildUsersGroup{this, "", "build-users-group",
+ "The Unix group that contains the build users."};
- /* Whether to impersonate a Linux 2.6 machine on newer kernels. */
- bool impersonateLinux26;
+ Setting<bool> impersonateLinux26{this, false, "build-impersonate-linux-26",
+ "Whether to impersonate a Linux 2.6 machine on newer kernels."};
- /* Whether to store build logs. */
- bool keepLog;
+ Setting<bool> keepLog{this, true, "build-keep-log",
+ "Whether to store build logs."};
- /* Whether to compress logs. */
- bool compressLog;
+ Setting<bool> compressLog{this, true, "build-compress-log",
+ "Whether to compress logs."};
- /* Maximum number of bytes a builder can write to stdout/stderr
- before being killed (0 means no limit). */
- unsigned long maxLogSize;
+ Setting<unsigned long> maxLogSize{this, 0, "build-max-log-size",
+ "Maximum number of bytes a builder can write to stdout/stderr "
+ "before being killed (0 means no limit)."};
/* When build-repeat > 0 and verboseBuild == true, whether to
print repeated builds (i.e. builds other than the first one) to
stderr. Hack to prevent Hydra logs from being polluted. */
bool printRepeatedBuilds = true;
- /* How often (in seconds) to poll for locks. */
- unsigned int pollInterval;
+ Setting<unsigned int> pollInterval{this, 5, "build-poll-interval",
+ "How often (in seconds) to poll for locks."};
- /* Whether to check if new GC roots can in fact be found by the
- garbage collector. */
- bool checkRootReachability;
+ Setting<bool> checkRootReachability{this, false, "gc-check-reachability",
+ "Whether to check if new GC roots can in fact be found by the "
+ "garbage collector."};
- /* Whether the garbage collector should keep outputs of live
- derivations. */
- bool gcKeepOutputs;
+ Setting<bool> gcKeepOutputs{this, false, "gc-keep-outputs",
+ "Whether the garbage collector should keep outputs of live derivations."};
- /* Whether the garbage collector should keep derivers of live
- paths. */
- bool gcKeepDerivations;
+ Setting<bool> gcKeepDerivations{this, true, "gc-keep-derivations",
+ "Whether the garbage collector should keep derivers of live paths."};
- /* Whether to automatically replace files with identical contents
- with hard links. */
- bool autoOptimiseStore;
+ Setting<bool> autoOptimiseStore{this, false, "auto-optimise-store",
+ "Whether to automatically replace files with identical contents with hard links."};
- /* Whether to add derivations as a dependency of user environments
- (to prevent them from being GCed). */
- bool envKeepDerivations;
+ Setting<bool> envKeepDerivations{this, false, "env-keep-derivations",
+ "Whether to add derivations as a dependency of user environments "
+ "(to prevent them from being GCed)."};
/* Whether to lock the Nix client and worker to the same CPU. */
bool lockCPU;
/* Whether to show a stack trace if Nix evaluation fails. */
- bool showTrace;
+ bool showTrace = false;
- /* Whether native-code enabling primops should be enabled */
- bool enableNativeCode;
+ Setting<bool> enableNativeCode{this, false, "allow-unsafe-native-code-during-evaluation",
+ "Whether builtin functions that allow executing native code should be enabled."};
- /* Whether to enable sandboxed builds (string until we get an enum for true/false/relaxed) */
- string useSandbox;
+ Setting<SandboxMode> sandboxMode{this, smDisabled, "build-use-sandbox",
+ "Whether to enable sandboxed builds. Can be \"true\", \"false\" or \"relaxed\".",
+ {"build-use-chroot"}};
- /* The basic set of paths to expose in a sandbox */
- PathSet sandboxPaths;
+ Setting<PathSet> sandboxPaths{this, {}, "build-sandbox-paths",
+ "The paths to make available inside the build sandbox.",
+ {"build-chroot-dirs"}};
- /* Any extra sandbox paths to expose */
- PathSet extraSandboxPaths;
+ Setting<PathSet> extraSandboxPaths{this, {}, "build-extra-sandbox-paths",
+ "Additional paths to make available inside the build sandbox.",
+ {"build-extra-chroot-dirs"}};
- /* Whether to allow certain questionable operations (like fetching) during evaluation */
- bool restrictEval;
+ Setting<bool> restrictEval{this, false, "restrict-eval",
+ "Whether to restrict file system access to paths in $NIX_PATH, "
+ "and to disallow fetching files from the network."};
- /* The number of times to repeat a build to check for determinism */
- int buildRepeat;
+ Setting<size_t> buildRepeat{this, 0, "build-repeat",
+ "The number of times to repeat a build in order to verify determinism."};
- /* Which prefixes to allow derivations to ask for access to (primarily for Darwin) */
- PathSet allowedImpureHostPrefixes;
+#if __linux__
+ Setting<std::string> sandboxShmSize{this, "50%", "sandbox-dev-shm-size",
+ "The size of /dev/shm in the build sandbox."};
+#endif
- /* The size of /dev/shm in the build sandbox (for Linux) */
- string sandboxShmSize;
+ Setting<PathSet> allowedImpureHostPrefixes{this, {}, "allowed-impure-host-deps",
+ "Which prefixes to allow derivations to ask for access to (primarily for Darwin)."};
- /* Whether to log Darwin sandbox access violations to the system log */
- bool darwinLogSandboxViolations;
+#if __APPLE__
+ Setting<bool> darwinLogSandboxViolations{this, false, "darwin-log-sandbox-violations",
+ "Whether to log Darwin sandbox access violations to the system log."};
+#endif
- /* ??? */
- bool runDiffHook;
+ Setting<bool> runDiffHook{this, false, "run-diff-hook",
+ "Whether to run the program specified by the diff-hook setting "
+ "repeated builds produce a different result. Typically used to "
+ "plug in diffoscope."};
- /* ??? */
- string diffHook;
+ PathSetting diffHook{this, true, "", "diff-hook",
+ "A program that prints out the differences between the two paths "
+ "specified on its command line."};
- /* Whether to fail if repeated builds produce different output */
- bool enforceDeterminism;
+ Setting<bool> enforceDeterminism{this, true, "enforce-determinism",
+ "Whether to fail if repeated builds produce different output."};
- /* The known public keys for a binary cache */
- Strings binaryCachePublicKeys;
+ Setting<Strings> binaryCachePublicKeys{this,
+ {"cache.nixos.org-1:6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY="},
+ "binary-cache-public-keys",
+ "Trusted public keys for secure substitution."};
- /* Secret keys to use for build output signing */
- Strings secretKeyFiles;
+ Setting<Strings> secretKeyFiles{this, {}, "secret-key-files",
+ "Secret keys with which to sign local builds."};
- /* Number of parallel connections to hit a binary cache with when finding out if it contains hashes */
- int binaryCachesParallelConnections;
+ Setting<size_t> binaryCachesParallelConnections{this, 25, "binary-caches-parallel-connections",
+ "Number of parallel connections to binary caches."};
- /* Whether to enable HTTP2 */
- bool enableHttp2;
+ Setting<bool> enableHttp2{this, true, "enable-http2",
+ "Whether to enable HTTP/2 support."};
- /* How soon to expire tarballs like builtins.fetchTarball and (ugh, bad name) builtins.fetchurl */
- int tarballTtl;
+ Setting<unsigned int> tarballTtl{this, 60 * 60, "tarball-ttl",
+ "How soon to expire files fetched by builtins.fetchTarball and builtins.fetchurl."};
- /* ??? */
- string signedBinaryCaches;
+ Setting<std::string> signedBinaryCaches{this, "*", "signed-binary-caches",
+ "Obsolete."};
- /* ??? */
- Strings substituters;
+ Setting<Strings> substituters{this,
+ nixStore == "/nix/store" ? Strings{"https://cache.nixos.org/"} : Strings(),
+ "substituters",
+ "The URIs of substituters (such as https://cache.nixos.org/).",
+ {"binary-caches"}};
- /* ??? */
- Strings binaryCaches;
+ // FIXME: provide a way to add to option values.
+ Setting<Strings> extraSubstituters{this, {}, "extra-substituters",
+ "Additional URIs of substituters.",
+ {"extra-binary-caches"}};
- /* ??? */
- Strings extraBinaryCaches;
-
- /* Who we trust to ask the daemon to do unsafe things */
- Strings trustedUsers;
+ Setting<Strings> trustedUsers{this, {"root"}, "trusted-users",
+ "Which users or groups are trusted to ask the daemon to do unsafe things."};
/* ?Who we trust to use the daemon in safe ways */
- Strings allowedUsers;
+ Setting<Strings> allowedUsers{this, {"*"}, "allowed-users",
+ "Which users or groups are allowed to connect to the daemon."};
- /* ??? */
- bool printMissing;
+ Setting<bool> printMissing{this, true, "print-missing",
+ "Whether to print what paths need to be built or downloaded."};
- /* The hook to run just before a build to set derivation-specific
- build settings */
- Path preBuildHook;
+ Setting<std::string> preBuildHook{this,
+#if __APPLE__
+ nixLibexecDir + "/nix/resolve-system-dependencies",
+#else
+ "",
+#endif
+ "pre-build-hook",
+ "A program to run just before a build to set derivation-specific build settings."};
- /* Path to the netrc file used to obtain usernames/passwords for
- downloads. */
- Path netrcFile;
+ Setting<std::string> netrcFile{this, fmt("%s/%s", nixConfDir, "netrc"), "netrc-file",
+ "Path to the netrc file used to obtain usernames/passwords for downloads."};
/* Path to the SSL CA file used */
Path caFile;
- /* Whether we allow import-from-derivation */
- bool enableImportFromDerivation;
-
-private:
- StringSet deprecatedOptions;
- SettingsMap settings, overrides;
-
- void checkDeprecated(const string & name);
+ Setting<bool> enableImportFromDerivation{this, true, "allow-import-from-derivation",
+ "Whether the evaluator allows importing the result of a derivation."};
- void _get(string & res, const string & name);
- void _get(string & res, const string & name1, const string & name2);
- void _get(bool & res, const string & name);
- void _get(StringSet & res, const string & name);
- void _get(StringSet & res, const string & name1, const string & name2);
- void _get(Strings & res, const string & name);
- template<class N> void _get(N & res, const string & name);
+ struct CaseHackTag { };
+ Setting<bool, CaseHackTag> useCaseHack{this, nix::useCaseHack, "use-case-hack",
+ "Whether to enable a Darwin-specific hack for dealing with file name collisions."};
};
diff --git a/src/libstore/local-store.cc b/src/libstore/local-store.cc
index 9111a45f8..5a98454ab 100644
--- a/src/libstore/local-store.cc
+++ b/src/libstore/local-store.cc
@@ -75,7 +75,7 @@ LocalStore::LocalStore(const Params & params)
mode_t perm = 01775;
- struct group * gr = getgrnam(settings.buildUsersGroup.c_str());
+ struct group * gr = getgrnam(settings.buildUsersGroup.get().c_str());
if (!gr)
printError(format("warning: the group ‘%1%’ specified in ‘build-users-group’ does not exist")
% settings.buildUsersGroup);
@@ -1335,7 +1335,7 @@ void LocalStore::signPathInfo(ValidPathInfo & info)
auto secretKeyFiles = settings.secretKeyFiles;
- for (auto & secretKeyFile : secretKeyFiles) {
+ for (auto & secretKeyFile : secretKeyFiles.get()) {
SecretKey secretKey(readFile(secretKeyFile));
info.sign(secretKey);
}
diff --git a/src/libstore/remote-store.cc b/src/libstore/remote-store.cc
index e1df137e4..da3c8eb8d 100644
--- a/src/libstore/remote-store.cc
+++ b/src/libstore/remote-store.cc
@@ -166,9 +166,7 @@ void RemoteStore::setOptions(Connection & conn)
<< settings.useSubstitutes;
if (GET_PROTOCOL_MINOR(conn.daemonVersion) >= 12) {
- Settings::SettingsMap overrides = settings.getOverrides();
- if (overrides["ssh-auth-sock"] == "")
- overrides["ssh-auth-sock"] = getEnv("SSH_AUTH_SOCK");
+ StringMap overrides = settings.getOverrides();
conn.to << overrides.size();
for (auto & i : overrides)
conn.to << i.first << i.second;
diff --git a/src/libstore/store-api.cc b/src/libstore/store-api.cc
index 514d1c2ff..835bbb90e 100644
--- a/src/libstore/store-api.cc
+++ b/src/libstore/store-api.cc
@@ -719,7 +719,7 @@ ref<Store> openStore(const std::string & uri, const Store::Params & params)
for (auto fun : *RegisterStoreImplementation::implementations) {
auto store = fun(uri, params);
if (store) {
- store->warnUnused();
+ store->warnUnknownSettings();
return ref<Store>(store);
}
}
@@ -782,13 +782,10 @@ std::list<ref<Store>> getDefaultSubstituters()
state->stores.push_back(openStore(uri));
};
- for (auto uri : settings.substituters)
+ for (auto uri : settings.substituters.get())
addStore(uri);
- for (auto uri : settings.binaryCaches)
- addStore(uri);
-
- for (auto uri : settings.extraBinaryCaches)
+ for (auto uri : settings.extraSubstituters.get())
addStore(uri);
state->done = true;
diff --git a/src/libutil/config.cc b/src/libutil/config.cc
index c05a3253b..85e5ce330 100644
--- a/src/libutil/config.cc
+++ b/src/libutil/config.cc
@@ -11,7 +11,7 @@ void Config::set(const std::string & name, const std::string & value)
i->second.setting->set(value);
}
-void Config::add(AbstractSetting * setting)
+void Config::addSetting(AbstractSetting * setting)
{
_settings.emplace(setting->name, Config::SettingData{false, setting});
for (auto & alias : setting->aliases)
@@ -41,21 +41,59 @@ void Config::add(AbstractSetting * setting)
}
}
-void Config::warnUnused()
+void Config::warnUnknownSettings()
{
for (auto & i : initials)
warn("unknown setting '%s'", i.first);
}
-std::string Config::dump()
+StringMap Config::getSettings()
{
- std::string res;
+ StringMap res;
for (auto & opt : _settings)
if (!opt.second.isAlias)
- res += opt.first + " = " + opt.second.setting->to_string() + "\n";
+ res.emplace(opt.first, opt.second.setting->to_string());
return res;
}
+void Config::applyConfigFile(const Path & path, bool fatal)
+{
+ try {
+ string contents = readFile(path);
+
+ unsigned int pos = 0;
+
+ while (pos < contents.size()) {
+ string line;
+ while (pos < contents.size() && contents[pos] != '\n')
+ line += contents[pos++];
+ pos++;
+
+ string::size_type hash = line.find('#');
+ if (hash != string::npos)
+ line = string(line, 0, hash);
+
+ vector<string> tokens = tokenizeString<vector<string> >(line);
+ if (tokens.empty()) continue;
+
+ if (tokens.size() < 2 || tokens[1] != "=")
+ throw UsageError("illegal configuration line ‘%1%’ in ‘%2%’", line, path);
+
+ string name = tokens[0];
+
+ vector<string>::iterator i = tokens.begin();
+ advance(i, 2);
+
+ try {
+ set(name, concatStringsSep(" ", Strings(i, tokens.end()))); // FIXME: slow
+ } catch (UsageError & e) {
+ if (fatal) throw;
+ warn("in configuration file '%s': %s", path, e.what());
+ }
+ };
+ } catch (SysError &) { }
+}
+
AbstractSetting::AbstractSetting(
const std::string & name,
const std::string & description,
@@ -74,41 +112,65 @@ template<> std::string Setting<std::string>::to_string()
return value;
}
-template<typename T>
-void Setting<T>::set(const std::string & str)
+template<typename T, typename Tag>
+void Setting<T, Tag>::set(const std::string & str)
{
static_assert(std::is_integral<T>::value, "Integer required.");
- try {
- auto i = std::stoll(str);
- if (i < std::numeric_limits<T>::min() ||
- i > std::numeric_limits<T>::max())
- throw UsageError("setting '%s' has out-of-range value %d", name, i);
- value = i;
- } catch (std::logic_error&) {
+ if (!string2Int(str, value))
throw UsageError("setting '%s' has invalid value '%s'", name, str);
- }
}
-template<typename T>
-std::string Setting<T>::to_string()
+template<typename T, typename Tag>
+std::string Setting<T, Tag>::to_string()
{
static_assert(std::is_integral<T>::value, "Integer required.");
return std::to_string(value);
}
-template<> void Setting<bool>::set(const std::string & str)
+bool AbstractSetting::parseBool(const std::string & str)
{
if (str == "true" || str == "yes" || str == "1")
- value = true;
+ return true;
else if (str == "false" || str == "no" || str == "0")
- value = false;
+ return false;
else
throw UsageError("Boolean setting '%s' has invalid value '%s'", name, str);
}
+template<> void Setting<bool>::set(const std::string & str)
+{
+ value = parseBool(str);
+}
+
+std::string AbstractSetting::printBool(bool b)
+{
+ return b ? "true" : "false";
+}
+
+
template<> std::string Setting<bool>::to_string()
{
- return value ? "true" : "false";
+ return printBool(value);
+}
+
+template<> void Setting<Strings>::set(const std::string & str)
+{
+ value = tokenizeString<Strings>(str);
+}
+
+template<> std::string Setting<Strings>::to_string()
+{
+ return concatStringsSep(" ", value);
+}
+
+template<> void Setting<StringSet>::set(const std::string & str)
+{
+ value = tokenizeString<StringSet>(str);
+}
+
+template<> std::string Setting<StringSet>::to_string()
+{
+ return concatStringsSep(" ", value);
}
template class Setting<int>;
diff --git a/src/libutil/config.hh b/src/libutil/config.hh
index fb2d48e9c..6c8612f67 100644
--- a/src/libutil/config.hh
+++ b/src/libutil/config.hh
@@ -47,11 +47,13 @@ public:
void set(const std::string & name, const std::string & value);
- void add(AbstractSetting * setting);
+ void addSetting(AbstractSetting * setting);
- void warnUnused();
+ void warnUnknownSettings();
- std::string dump();
+ StringMap getSettings();
+
+ void applyConfigFile(const Path & path, bool fatal = false);
};
class AbstractSetting
@@ -83,10 +85,15 @@ protected:
virtual void set(const std::string & value) = 0;
virtual std::string to_string() = 0;
+
+ bool parseBool(const std::string & str);
+ std::string printBool(bool b);
};
+struct DefaultSettingTag { };
+
/* A setting of type T. */
-template<typename T>
+template<typename T, typename Tag = DefaultSettingTag>
class Setting : public AbstractSetting
{
protected:
@@ -103,10 +110,12 @@ public:
: AbstractSetting(name, description, aliases)
, value(def)
{
- options->add(this);
+ options->addSetting(this);
}
operator const T &() const { return value; }
+ operator T &() { return value; }
+ const T & get() const { return value; }
bool operator ==(const T & v2) const { return value == v2; }
bool operator !=(const T & v2) const { return value != v2; }
void operator =(const T & v) { value = v; }
@@ -123,6 +132,9 @@ std::ostream & operator <<(std::ostream & str, const Setting<T> & opt)
return str;
}
+template<typename T>
+bool operator ==(const T & v1, const Setting<T> & v2) { return v1 == (const T &) v2; }
+
/* A special setting for Paths. These are automatically canonicalised
(e.g. "/foo//bar/" becomes "/foo/bar"). */
class PathSetting : public Setting<Path>
diff --git a/src/nix-daemon/nix-daemon.cc b/src/nix-daemon/nix-daemon.cc
index f4285693f..9fcb81dd5 100644
--- a/src/nix-daemon/nix-daemon.cc
+++ b/src/nix-daemon/nix-daemon.cc
@@ -436,30 +436,29 @@ static void performOp(ref<LocalStore> store, bool trusted, unsigned int clientVe
}
case wopSetOptions: {
- from >> settings.keepFailed;
- from >> settings.keepGoing;
- settings.set("build-fallback", readInt(from) ? "true" : "false");
+ settings.keepFailed = readInt(from);
+ settings.keepGoing = readInt(from);
+ settings.tryFallback = readInt(from);
verbosity = (Verbosity) readInt(from);
settings.set("build-max-jobs", std::to_string(readInt(from)));
- settings.set("build-max-silent-time", std::to_string(readInt(from)));
+ settings.maxSilentTime = readInt(from);
settings.useBuildHook = readInt(from) != 0;
settings.verboseBuild = lvlError == (Verbosity) readInt(from);
readInt(from); // obsolete logType
readInt(from); // obsolete printBuildTrace
settings.set("build-cores", std::to_string(readInt(from)));
- settings.set("build-use-substitutes", readInt(from) ? "true" : "false");
+ settings.useSubstitutes = readInt(from);
if (GET_PROTOCOL_MINOR(clientVersion) >= 12) {
unsigned int n = readInt(from);
for (unsigned int i = 0; i < n; i++) {
string name = readString(from);
string value = readString(from);
- if (name == "build-timeout" || name == "use-ssh-substituter")
+ if (name == "build-timeout")
settings.set(name, value);
else
settings.set(trusted ? name : "untrusted-" + name, value);
}
}
- settings.update();
startWork();
stopWork();
break;
diff --git a/src/nix-store/nix-store.cc b/src/nix-store/nix-store.cc
index a40cca982..9131b74df 100644
--- a/src/nix-store/nix-store.cc
+++ b/src/nix-store/nix-store.cc
@@ -795,11 +795,11 @@ static void opServe(Strings opFlags, Strings opArgs)
settings.maxSilentTime = readInt(in);
settings.buildTimeout = readInt(in);
if (GET_PROTOCOL_MINOR(clientVersion) >= 2)
- in >> settings.maxLogSize;
+ settings.maxLogSize = readNum<unsigned long>(in);
if (GET_PROTOCOL_MINOR(clientVersion) >= 3) {
- settings.set("build-repeat", std::to_string(readInt(in)));
- settings.set("enforce-determinism", readInt(in) != 0 ? "true" : "false");
- settings.set("run-diff-hook", "true");
+ settings.buildRepeat = readInt(in);
+ settings.enforceDeterminism = readInt(in);
+ settings.runDiffHook = readInt(in);
}
settings.printRepeatedBuilds = false;
};
diff --git a/src/nix/main.cc b/src/nix/main.cc
index fdb8f6e3a..440ced97d 100644
--- a/src/nix/main.cc
+++ b/src/nix/main.cc
@@ -42,7 +42,6 @@ void mainWrapped(int argc, char * * argv)
NixArgs args;
args.parseCmdline(argvToStrings(argc, argv));
- settings.update();
assert(args.command);