Skip to content

Commit

Permalink
Modularize config settings
Browse files Browse the repository at this point in the history
Allow global config settings to be defined in multiple Config
classes. For example, this means that libutil can have settings and
evaluator settings can be moved out of libstore. The Config classes
are registered in a new GlobalConfig class to which config files
etc. are applied.

Relevant to NixOS#2009 in that it
removes the need for ad hoc handling of useCaseHack, which was the
underlying cause of that issue.
  • Loading branch information
edolstra authored and Anton-Latukha committed Jul 12, 2018
1 parent e2b3544 commit a409338
Show file tree
Hide file tree
Showing 15 changed files with 192 additions and 130 deletions.
2 changes: 1 addition & 1 deletion perl/lib/Nix/Store.xs
Expand Up @@ -27,7 +27,7 @@ static ref<Store> store()
static std::shared_ptr<Store> _store;
if (!_store) {
try {
settings.loadConfFile();
loadConfFile();
settings.lockCPU = false;
_store = openStore();
} catch (Error & e) {
Expand Down
4 changes: 2 additions & 2 deletions src/libmain/common-args.cc
Expand Up @@ -29,14 +29,14 @@ MixCommonArgs::MixCommonArgs(const string & programName)
.arity(2)
.handler([](std::vector<std::string> ss) {
try {
settings.set(ss[0], ss[1]);
globalConfig.set(ss[0], ss[1]);
} catch (UsageError & e) {
warn(e.what());
}
});

std::string cat = "config";
settings.convertToArgs(*this, cat);
globalConfig.convertToArgs(*this, cat);

// Backward compatibility hack: nix-env already had a --system flag.
if (programName == "nix-env") longFlags.erase("system");
Expand Down
2 changes: 1 addition & 1 deletion src/libmain/shared.cc
Expand Up @@ -109,7 +109,7 @@ void initNix()
opensslLocks = std::vector<std::mutex>(CRYPTO_num_locks());
CRYPTO_set_locking_callback(opensslLockCallback);

settings.loadConfFile();
loadConfFile();

startSignalHandlerThread();

Expand Down
6 changes: 4 additions & 2 deletions src/libstore/build.cc
Expand Up @@ -672,8 +672,10 @@ HookInstance::HookInstance()
toHook.readSide = -1;

sink = FdSink(toHook.writeSide.get());
for (auto & setting : settings.getSettings())
sink << 1 << setting.first << setting.second;
std::map<std::string, Config::SettingInfo> settings;
globalConfig.getSettings(settings);
for (auto & setting : settings)
sink << 1 << setting.first << setting.second.value;
sink << 0;
}

Expand Down
38 changes: 11 additions & 27 deletions src/libstore/globals.cc
Expand Up @@ -28,9 +28,10 @@ namespace nix {

Settings settings;

static GlobalConfig::Register r1(&settings);

Settings::Settings()
: Config({})
, nixPrefix(NIX_PREFIX)
: 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)))
Expand Down Expand Up @@ -69,20 +70,15 @@ Settings::Settings()
allowedImpureHostPrefixes = tokenizeString<StringSet>(DEFAULT_ALLOWED_IMPURE_PREFIXES);
}

void Settings::loadConfFile()
void loadConfFile()
{
applyConfigFile(nixConfDir + "/nix.conf");
globalConfig.applyConfigFile(settings.nixConfDir + "/nix.conf");

/* We only want to send overrides to the daemon, i.e. stuff from
~/.nix/nix.conf or the command line. */
resetOverriden();
globalConfig.resetOverriden();

applyConfigFile(getConfigDir() + "/nix/nix.conf");
}

void Settings::set(const string & name, const string & value)
{
Config::set(name, value);
globalConfig.applyConfigFile(getConfigDir() + "/nix/nix.conf");
}

unsigned int Settings::getDefaultCores()
Expand Down Expand Up @@ -162,23 +158,11 @@ void initPlugins()
throw Error("could not dynamically open plugin file '%s': %s", file, dlerror());
}
}
/* We handle settings registrations here, since plugins can add settings */
if (RegisterSetting::settingRegistrations) {
for (auto & registration : *RegisterSetting::settingRegistrations)
settings.addSetting(registration);
delete RegisterSetting::settingRegistrations;
}
settings.handleUnknownSettings();
}

RegisterSetting::SettingRegistrations * RegisterSetting::settingRegistrations;

RegisterSetting::RegisterSetting(AbstractSetting * s)
{
if (!settingRegistrations)
settingRegistrations = new SettingRegistrations;
settingRegistrations->emplace_back(s);
/* Since plugins can add settings, try to re-apply previously
unknown settings. */
globalConfig.reapplyUnknownSettings();
globalConfig.warnUnknownSettings();
}


}
36 changes: 1 addition & 35 deletions src/libstore/globals.hh
Expand Up @@ -13,26 +13,6 @@ namespace nix {

typedef enum { smEnabled, smRelaxed, smDisabled } SandboxMode;

extern bool useCaseHack; // FIXME

struct CaseHackSetting : public BaseSetting<bool>
{
CaseHackSetting(Config * options,
const std::string & name,
const std::string & description,
const std::set<std::string> & aliases = {})
: BaseSetting<bool>(useCaseHack, name, description, aliases)
{
options->addSetting(this);
}

void set(const std::string & str) override
{
BaseSetting<bool>::set(str);
nix::useCaseHack = value;
}
};

struct MaxBuildJobsSetting : public BaseSetting<unsigned int>
{
MaxBuildJobsSetting(Config * options,
Expand All @@ -56,10 +36,6 @@ public:

Settings();

void loadConfFile();

void set(const string & name, const string & value);

Path nixPrefix;

/* The directory where we store sources and derived files. */
Expand Down Expand Up @@ -353,9 +329,6 @@ public:
Setting<bool> enableImportFromDerivation{this, true, "allow-import-from-derivation",
"Whether the evaluator allows importing the result of a derivation."};

CaseHackSetting useCaseHack{this, "use-case-hack",
"Whether to enable a Darwin-specific hack for dealing with file name collisions."};

Setting<unsigned long> connectTimeout{this, 0, "connect-timeout",
"Timeout for connecting to servers during downloads. 0 means use curl's builtin default."};

Expand Down Expand Up @@ -398,15 +371,8 @@ extern Settings settings;
anything else */
void initPlugins();

void loadConfFile();

extern const string nixVersion;

struct RegisterSetting
{
typedef std::vector<AbstractSetting *> SettingRegistrations;
static SettingRegistrations * settingRegistrations;
RegisterSetting(AbstractSetting * s);
};


}
5 changes: 3 additions & 2 deletions src/libstore/remote-store.cc
Expand Up @@ -187,10 +187,11 @@ void RemoteStore::setOptions(Connection & conn)
<< settings.useSubstitutes;

if (GET_PROTOCOL_MINOR(conn.daemonVersion) >= 12) {
auto overrides = settings.getSettings(true);
std::map<std::string, Config::SettingInfo> overrides;
globalConfig.getSettings(overrides, true);
conn.to << overrides.size();
for (auto & i : overrides)
conn.to << i.first << i.second;
conn.to << i.first << i.second.value;
}

conn.processStderr();
Expand Down
2 changes: 1 addition & 1 deletion src/libstore/store-api.cc
Expand Up @@ -849,7 +849,7 @@ ref<Store> openStore(const std::string & uri_,
for (auto fun : *RegisterStoreImplementation::implementations) {
auto store = fun(uri, params);
if (store) {
store->handleUnknownSettings();
store->warnUnknownSettings();
return ref<Store>(store);
}
}
Expand Down
26 changes: 17 additions & 9 deletions src/libutil/archive.cc
Expand Up @@ -13,17 +13,25 @@

#include "archive.hh"
#include "util.hh"

#include "config.hh"

namespace nix {

struct ArchiveSettings : Config
{
Setting<bool> useCaseHack{this,
#if __APPLE__
true,
#else
false,
#endif
"use-case-hack",
"Whether to enable a Darwin-specific hack for dealing with file name collisions."};
};

bool useCaseHack =
#if __APPLE__
true;
#else
false;
#endif
static ArchiveSettings archiveSettings;

static GlobalConfig::Register r1(&archiveSettings);

const std::string narVersionMagic1 = "nix-archive-1";

Expand Down Expand Up @@ -78,7 +86,7 @@ static void dump(const Path & path, Sink & sink, PathFilter & filter)
the case hack applied by restorePath(). */
std::map<string, string> unhacked;
for (auto & i : readDirectory(path))
if (useCaseHack) {
if (archiveSettings.useCaseHack) {
string name(i.name);
size_t pos = i.name.find(caseHackSuffix);
if (pos != string::npos) {
Expand Down Expand Up @@ -243,7 +251,7 @@ static void parse(ParseSink & sink, Source & source, const Path & path)
if (name <= prevName)
throw Error("NAR directory is not sorted");
prevName = name;
if (useCaseHack) {
if (archiveSettings.useCaseHack) {
auto i = names.find(name);
if (i != names.end()) {
debug(format("case collision between '%1%' and '%2%'") % i->first % name);
Expand Down
4 changes: 0 additions & 4 deletions src/libutil/archive.hh
Expand Up @@ -78,10 +78,6 @@ void restorePath(const Path & path, Source & source);
void copyNAR(Source & source, Sink & sink);


// FIXME: global variables are bad m'kay.
extern bool useCaseHack;


extern const std::string narVersionMagic1;


Expand Down
88 changes: 68 additions & 20 deletions src/libutil/config.cc
Expand Up @@ -4,15 +4,13 @@

namespace nix {

void Config::set(const std::string & name, const std::string & value)
bool Config::set(const std::string & name, const std::string & value)
{
auto i = _settings.find(name);
if (i == _settings.end()) {
extras.emplace(name, value);
} else {
i->second.setting->set(value);
i->second.setting->overriden = true;
}
if (i == _settings.end()) return false;
i->second.setting->set(value);
i->second.setting->overriden = true;
return true;
}

void Config::addSetting(AbstractSetting * setting)
Expand All @@ -23,46 +21,51 @@ void Config::addSetting(AbstractSetting * setting)

bool set = false;

auto i = extras.find(setting->name);
if (i != extras.end()) {
auto i = unknownSettings.find(setting->name);
if (i != unknownSettings.end()) {
setting->set(i->second);
setting->overriden = true;
extras.erase(i);
unknownSettings.erase(i);
set = true;
}

for (auto & alias : setting->aliases) {
auto i = extras.find(alias);
if (i != extras.end()) {
auto i = unknownSettings.find(alias);
if (i != unknownSettings.end()) {
if (set)
warn("setting '%s' is set, but it's an alias of '%s' which is also set",
alias, setting->name);
else {
setting->set(i->second);
setting->overriden = true;
extras.erase(i);
unknownSettings.erase(i);
set = true;
}
}
}
}

void Config::handleUnknownSettings()
void AbstractConfig::warnUnknownSettings()
{
for (auto & s : extras)
for (auto & s : unknownSettings)
warn("unknown setting '%s'", s.first);
}

StringMap Config::getSettings(bool overridenOnly)
void AbstractConfig::reapplyUnknownSettings()
{
auto unknownSettings2 = std::move(unknownSettings);
for (auto & s : unknownSettings2)
set(s.first, s.second);
}

void Config::getSettings(std::map<std::string, SettingInfo> & res, bool overridenOnly)
{
StringMap res;
for (auto & opt : _settings)
if (!opt.second.isAlias && (!overridenOnly || opt.second.setting->overriden))
res.emplace(opt.first, opt.second.setting->to_string());
return res;
res.emplace(opt.first, SettingInfo{opt.second.setting->to_string(), opt.second.setting->description});
}

void Config::applyConfigFile(const Path & path)
void AbstractConfig::applyConfigFile(const Path & path)
{
try {
string contents = readFile(path);
Expand Down Expand Up @@ -287,4 +290,49 @@ void PathSetting::set(const std::string & str)
value = canonPath(str);
}

bool GlobalConfig::set(const std::string & name, const std::string & value)
{
for (auto & config : *configRegistrations)
if (config->set(name, value)) return true;

unknownSettings.emplace(name, value);

return false;
}

void GlobalConfig::getSettings(std::map<std::string, SettingInfo> & res, bool overridenOnly)
{
for (auto & config : *configRegistrations)
config->getSettings(res, overridenOnly);
}

void GlobalConfig::resetOverriden()
{
for (auto & config : *configRegistrations)
config->resetOverriden();
}

void GlobalConfig::toJSON(JSONObject & out)
{
for (auto & config : *configRegistrations)
config->toJSON(out);
}

void GlobalConfig::convertToArgs(Args & args, const std::string & category)
{
for (auto & config : *configRegistrations)
config->convertToArgs(args, category);
}

GlobalConfig globalConfig;

GlobalConfig::ConfigRegistrations * GlobalConfig::configRegistrations;

GlobalConfig::Register::Register(Config * config)
{
if (!configRegistrations)
configRegistrations = new ConfigRegistrations;
configRegistrations->emplace_back(config);
}

}

0 comments on commit a409338

Please sign in to comment.