mirror of
https://github.com/NixOS/nix
synced 2025-07-05 12:21:48 +02:00
Fix some issues with experimental config settings
Issues: 1. Features gated on disabled experimental settings should warn and be ignored, not silently succeed. 2. Experimental settings in the same config "batch" (file or env var) as the enabling of the experimental feature should work. 3. For (2), the order should not matter. These are analogous to the issues @roberth caught with my changes for arg handling, but they are instead for config handling. Co-authored-by: Robert Hensing <roberth@users.noreply.github.com>
This commit is contained in:
parent
3f9589f17e
commit
2c8475600d
7 changed files with 238 additions and 66 deletions
|
@ -3,6 +3,8 @@
|
|||
#include "abstract-setting-to-json.hh"
|
||||
#include "experimental-features.hh"
|
||||
|
||||
#include "config-impl.hh"
|
||||
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
namespace nix {
|
||||
|
@ -80,6 +82,8 @@ void Config::getSettings(std::map<std::string, SettingInfo> & res, bool overridd
|
|||
void AbstractConfig::applyConfig(const std::string & contents, const std::string & path) {
|
||||
unsigned int pos = 0;
|
||||
|
||||
std::vector<std::pair<std::string, std::string>> parsedContents;
|
||||
|
||||
while (pos < contents.size()) {
|
||||
std::string line;
|
||||
while (pos < contents.size() && contents[pos] != '\n')
|
||||
|
@ -125,8 +129,21 @@ void AbstractConfig::applyConfig(const std::string & contents, const std::string
|
|||
auto i = tokens.begin();
|
||||
advance(i, 2);
|
||||
|
||||
set(name, concatStringsSep(" ", Strings(i, tokens.end()))); // FIXME: slow
|
||||
parsedContents.push_back({
|
||||
name,
|
||||
concatStringsSep(" ", Strings(i, tokens.end())),
|
||||
});
|
||||
};
|
||||
|
||||
// First apply experimental-feature related settings
|
||||
for (auto & [name, value] : parsedContents)
|
||||
if (name == "experimental-features" || name == "extra-experimental-features")
|
||||
set(name, value);
|
||||
|
||||
// Then apply other settings
|
||||
for (auto & [name, value] : parsedContents)
|
||||
if (name != "experimental-features" && name != "extra-experimental-features")
|
||||
set(name, value);
|
||||
}
|
||||
|
||||
void AbstractConfig::applyConfigFile(const Path & path)
|
||||
|
@ -202,12 +219,6 @@ void AbstractSetting::convertToArg(Args & args, const std::string & category)
|
|||
{
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
bool BaseSetting<T>::isAppendable()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void BaseSetting<T>::convertToArg(Args & args, const std::string & category)
|
||||
{
|
||||
|
@ -231,9 +242,9 @@ void BaseSetting<T>::convertToArg(Args & args, const std::string & category)
|
|||
});
|
||||
}
|
||||
|
||||
template<> void BaseSetting<std::string>::set(const std::string & str, bool append)
|
||||
template<> std::string BaseSetting<std::string>::parse(const std::string & str) const
|
||||
{
|
||||
value = str;
|
||||
return str;
|
||||
}
|
||||
|
||||
template<> std::string BaseSetting<std::string>::to_string() const
|
||||
|
@ -242,11 +253,11 @@ template<> std::string BaseSetting<std::string>::to_string() const
|
|||
}
|
||||
|
||||
template<typename T>
|
||||
void BaseSetting<T>::set(const std::string & str, bool append)
|
||||
T BaseSetting<T>::parse(const std::string & str) const
|
||||
{
|
||||
static_assert(std::is_integral<T>::value, "Integer required.");
|
||||
if (auto n = string2Int<T>(str))
|
||||
value = *n;
|
||||
return *n;
|
||||
else
|
||||
throw UsageError("setting '%s' has invalid value '%s'", name, str);
|
||||
}
|
||||
|
@ -258,12 +269,12 @@ std::string BaseSetting<T>::to_string() const
|
|||
return std::to_string(value);
|
||||
}
|
||||
|
||||
template<> void BaseSetting<bool>::set(const std::string & str, bool append)
|
||||
template<> bool BaseSetting<bool>::parse(const std::string & str) const
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
@ -291,16 +302,15 @@ template<> void BaseSetting<bool>::convertToArg(Args & args, const std::string &
|
|||
});
|
||||
}
|
||||
|
||||
template<> void BaseSetting<Strings>::set(const std::string & str, bool append)
|
||||
template<> Strings BaseSetting<Strings>::parse(const std::string & str) const
|
||||
{
|
||||
auto ss = tokenizeString<Strings>(str);
|
||||
if (!append) value.clear();
|
||||
for (auto & s : ss) value.push_back(std::move(s));
|
||||
return tokenizeString<Strings>(str);
|
||||
}
|
||||
|
||||
template<> bool BaseSetting<Strings>::isAppendable()
|
||||
template<> void BaseSetting<Strings>::appendOrSet(Strings && newValue, bool append)
|
||||
{
|
||||
return true;
|
||||
if (!append) value.clear();
|
||||
for (auto && s : std::move(newValue)) value.push_back(std::move(s));
|
||||
}
|
||||
|
||||
template<> std::string BaseSetting<Strings>::to_string() const
|
||||
|
@ -308,16 +318,16 @@ template<> std::string BaseSetting<Strings>::to_string() const
|
|||
return concatStringsSep(" ", value);
|
||||
}
|
||||
|
||||
template<> void BaseSetting<StringSet>::set(const std::string & str, bool append)
|
||||
template<> StringSet BaseSetting<StringSet>::parse(const std::string & str) const
|
||||
{
|
||||
if (!append) value.clear();
|
||||
for (auto & s : tokenizeString<StringSet>(str))
|
||||
value.insert(s);
|
||||
return tokenizeString<StringSet>(str);
|
||||
}
|
||||
|
||||
template<> bool BaseSetting<StringSet>::isAppendable()
|
||||
template<> void BaseSetting<StringSet>::appendOrSet(StringSet && newValue, bool append)
|
||||
{
|
||||
return true;
|
||||
if (!append) value.clear();
|
||||
for (auto && s : std::move(newValue))
|
||||
value.insert(s);
|
||||
}
|
||||
|
||||
template<> std::string BaseSetting<StringSet>::to_string() const
|
||||
|
@ -325,21 +335,24 @@ template<> std::string BaseSetting<StringSet>::to_string() const
|
|||
return concatStringsSep(" ", value);
|
||||
}
|
||||
|
||||
template<> void BaseSetting<std::set<ExperimentalFeature>>::set(const std::string & str, bool append)
|
||||
template<> std::set<ExperimentalFeature> BaseSetting<std::set<ExperimentalFeature>>::parse(const std::string & str) const
|
||||
{
|
||||
if (!append) value.clear();
|
||||
std::set<ExperimentalFeature> res;
|
||||
for (auto & s : tokenizeString<StringSet>(str)) {
|
||||
auto thisXpFeature = parseExperimentalFeature(s);
|
||||
if (thisXpFeature)
|
||||
value.insert(thisXpFeature.value());
|
||||
res.insert(thisXpFeature.value());
|
||||
else
|
||||
warn("unknown experimental feature '%s'", s);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
template<> bool BaseSetting<std::set<ExperimentalFeature>>::isAppendable()
|
||||
template<> void BaseSetting<std::set<ExperimentalFeature>>::appendOrSet(std::set<ExperimentalFeature> && newValue, bool append)
|
||||
{
|
||||
return true;
|
||||
if (!append) value.clear();
|
||||
for (auto && s : std::move(newValue))
|
||||
value.insert(s);
|
||||
}
|
||||
|
||||
template<> std::string BaseSetting<std::set<ExperimentalFeature>>::to_string() const
|
||||
|
@ -350,20 +363,23 @@ template<> std::string BaseSetting<std::set<ExperimentalFeature>>::to_string() c
|
|||
return concatStringsSep(" ", stringifiedXpFeatures);
|
||||
}
|
||||
|
||||
template<> void BaseSetting<StringMap>::set(const std::string & str, bool append)
|
||||
template<> StringMap BaseSetting<StringMap>::parse(const std::string & str) const
|
||||
{
|
||||
if (!append) value.clear();
|
||||
StringMap res;
|
||||
for (auto & s : tokenizeString<Strings>(str)) {
|
||||
auto eq = s.find_first_of('=');
|
||||
if (std::string::npos != eq)
|
||||
value.emplace(std::string(s, 0, eq), std::string(s, eq + 1));
|
||||
res.emplace(std::string(s, 0, eq), std::string(s, eq + 1));
|
||||
// else ignored
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
template<> bool BaseSetting<StringMap>::isAppendable()
|
||||
template<> void BaseSetting<StringMap>::appendOrSet(StringMap && newValue, bool append)
|
||||
{
|
||||
return true;
|
||||
if (!append) value.clear();
|
||||
for (auto && [k, v] : std::move(newValue))
|
||||
value.emplace(std::move(k), std::move(v));
|
||||
}
|
||||
|
||||
template<> std::string BaseSetting<StringMap>::to_string() const
|
||||
|
@ -387,15 +403,15 @@ template class BaseSetting<StringSet>;
|
|||
template class BaseSetting<StringMap>;
|
||||
template class BaseSetting<std::set<ExperimentalFeature>>;
|
||||
|
||||
void PathSetting::set(const std::string & str, bool append)
|
||||
Path PathSetting::parse(const std::string & str) const
|
||||
{
|
||||
if (str == "") {
|
||||
if (allowEmpty)
|
||||
value = "";
|
||||
return "";
|
||||
else
|
||||
throw UsageError("setting '%s' cannot be empty", name);
|
||||
} else
|
||||
value = canonPath(str);
|
||||
return canonPath(str);
|
||||
}
|
||||
|
||||
bool GlobalConfig::set(const std::string & name, const std::string & value)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue