Move enabled experimental feature to libutil struct

This is needed in subsequent commits to allow the settings and CLI args
infrastructure itself to read this setting.
This commit is contained in:
John Ericson 2023-03-17 10:33:48 -04:00
parent 1b6c96bbcb
commit 296831f641
28 changed files with 113 additions and 70 deletions

View file

@ -305,7 +305,7 @@ connected:
std::set<Realisation> missingRealisations;
StorePathSet missingPaths;
if (settings.isExperimentalFeatureEnabled(Xp::CaDerivations) && !drv.type().hasKnownOutputPaths()) {
if (experimentalFeatureSettings.isEnabled(Xp::CaDerivations) && !drv.type().hasKnownOutputPaths()) {
for (auto & outputName : wantedOutputs) {
auto thisOutputHash = outputHashes.at(outputName);
auto thisOutputId = DrvOutput{ thisOutputHash, outputName };
@ -337,7 +337,7 @@ connected:
for (auto & realisation : missingRealisations) {
// Should hold, because if the feature isn't enabled the set
// of missing realisations should be empty
settings.requireExperimentalFeature(Xp::CaDerivations);
experimentalFeatureSettings.require(Xp::CaDerivations);
store->registerDrvOutput(realisation);
}

View file

@ -166,7 +166,7 @@ Path lookupFileArg(EvalState & state, std::string_view s)
}
else if (hasPrefix(s, "flake:")) {
settings.requireExperimentalFeature(Xp::Flakes);
experimentalFeatureSettings.require(Xp::Flakes);
auto flakeRef = parseFlakeRef(std::string(s.substr(6)), {}, true, false);
auto storePath = flakeRef.resolve(state.store).fetchTree(state.store).first.storePath;
return state.store->toRealPath(storePath);

View file

@ -332,7 +332,7 @@ void completeFlakeRefWithFragment(
void completeFlakeRef(ref<Store> store, std::string_view prefix)
{
if (!settings.isExperimentalFeatureEnabled(Xp::Flakes))
if (!experimentalFeatureSettings.isEnabled(Xp::Flakes))
return;
if (prefix == "")

View file

@ -320,7 +320,7 @@ LockedFlake lockFlake(
const FlakeRef & topRef,
const LockFlags & lockFlags)
{
settings.requireExperimentalFeature(Xp::Flakes);
experimentalFeatureSettings.require(Xp::Flakes);
FlakeCache flakeCache;

View file

@ -469,7 +469,7 @@ expr_simple
new ExprString(std::move(path))});
}
| URI {
static bool noURLLiterals = settings.isExperimentalFeatureEnabled(Xp::NoUrlLiterals);
static bool noURLLiterals = experimentalFeatureSettings.isEnabled(Xp::NoUrlLiterals);
if (noURLLiterals)
throw ParseError({
.msg = hintfmt("URL literals are disabled"),
@ -816,7 +816,7 @@ std::pair<bool, std::string> EvalState::resolveSearchPathElem(const SearchPathEl
}
else if (hasPrefix(elem.second, "flake:")) {
settings.requireExperimentalFeature(Xp::Flakes);
experimentalFeatureSettings.require(Xp::Flakes);
auto flakeRef = parseFlakeRef(elem.second.substr(6), {}, true, false);
debug("fetching flake search path element '%s''", elem.second);
auto storePath = flakeRef.resolve(store).fetchTree(store).first.storePath;

View file

@ -1141,13 +1141,13 @@ drvName, Bindings * attrs, Value & v)
if (i->name == state.sContentAddressed) {
contentAddressed = state.forceBool(*i->value, noPos, context_below);
if (contentAddressed)
settings.requireExperimentalFeature(Xp::CaDerivations);
experimentalFeatureSettings.require(Xp::CaDerivations);
}
else if (i->name == state.sImpure) {
isImpure = state.forceBool(*i->value, noPos, context_below);
if (isImpure)
settings.requireExperimentalFeature(Xp::ImpureDerivations);
experimentalFeatureSettings.require(Xp::ImpureDerivations);
}
/* The `args' attribute is special: it supplies the
@ -4114,7 +4114,7 @@ void EvalState::createBaseEnv()
if (RegisterPrimOp::primOps)
for (auto & primOp : *RegisterPrimOp::primOps)
if (!primOp.experimentalFeature
|| settings.isExperimentalFeatureEnabled(*primOp.experimentalFeature))
|| experimentalFeatureSettings.isEnabled(*primOp.experimentalFeature))
{
addPrimOp({
.fun = primOp.fun,

View file

@ -190,7 +190,7 @@ static void fetchTree(
static void prim_fetchTree(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{
settings.requireExperimentalFeature(Xp::Flakes);
experimentalFeatureSettings.require(Xp::Flakes);
fetchTree(state, pos, args, v, std::nullopt, FetchTreeParams { .allowNameArgument = false });
}

View file

@ -199,10 +199,10 @@ void DerivationGoal::haveDerivation()
parsedDrv = std::make_unique<ParsedDerivation>(drvPath, *drv);
if (!drv->type().hasKnownOutputPaths())
settings.requireExperimentalFeature(Xp::CaDerivations);
experimentalFeatureSettings.require(Xp::CaDerivations);
if (!drv->type().isPure()) {
settings.requireExperimentalFeature(Xp::ImpureDerivations);
experimentalFeatureSettings.require(Xp::ImpureDerivations);
for (auto & [outputName, output] : drv->outputs) {
auto randomPath = StorePath::random(outputPathName(drv->name, outputName));
@ -336,7 +336,7 @@ void DerivationGoal::gaveUpOnSubstitution()
for (auto & i : dynamic_cast<Derivation *>(drv.get())->inputDrvs) {
/* Ensure that pure, non-fixed-output derivations don't
depend on impure derivations. */
if (settings.isExperimentalFeatureEnabled(Xp::ImpureDerivations) && drv->type().isPure() && !drv->type().isFixed()) {
if (experimentalFeatureSettings.isEnabled(Xp::ImpureDerivations) && drv->type().isPure() && !drv->type().isFixed()) {
auto inputDrv = worker.evalStore.readDerivation(i.first);
if (!inputDrv.type().isPure())
throw Error("pure derivation '%s' depends on impure derivation '%s'",
@ -477,7 +477,7 @@ void DerivationGoal::inputsRealised()
ca.fixed
/* Can optionally resolve if fixed, which is good
for avoiding unnecessary rebuilds. */
? settings.isExperimentalFeatureEnabled(Xp::CaDerivations)
? experimentalFeatureSettings.isEnabled(Xp::CaDerivations)
/* Must resolve if floating and there are any inputs
drvs. */
: true);
@ -488,7 +488,7 @@ void DerivationGoal::inputsRealised()
}, drvType.raw());
if (resolveDrv && !fullDrv.inputDrvs.empty()) {
settings.requireExperimentalFeature(Xp::CaDerivations);
experimentalFeatureSettings.require(Xp::CaDerivations);
/* We are be able to resolve this derivation based on the
now-known results of dependencies. If so, we become a
@ -1352,7 +1352,7 @@ std::pair<bool, DrvOutputs> DerivationGoal::checkPathValidity()
};
}
auto drvOutput = DrvOutput{info.outputHash, i.first};
if (settings.isExperimentalFeatureEnabled(Xp::CaDerivations)) {
if (experimentalFeatureSettings.isEnabled(Xp::CaDerivations)) {
if (auto real = worker.store.queryRealisation(drvOutput)) {
info.known = {
.path = real->outPath,

View file

@ -413,7 +413,7 @@ void LocalDerivationGoal::startBuilder()
)
{
#if __linux__
settings.requireExperimentalFeature(Xp::Cgroups);
experimentalFeatureSettings.require(Xp::Cgroups);
auto cgroupFS = getCgroupFS();
if (!cgroupFS)
@ -1393,7 +1393,7 @@ struct RestrictedStore : public virtual RestrictedStoreConfig, public virtual Lo
void LocalDerivationGoal::startDaemon()
{
settings.requireExperimentalFeature(Xp::RecursiveNix);
experimentalFeatureSettings.require(Xp::RecursiveNix);
Store::Params params;
params["path-info-cache-size"] = "0";
@ -2268,7 +2268,7 @@ DrvOutputs LocalDerivationGoal::registerOutputs()
bool discardReferences = false;
if (auto structuredAttrs = parsedDrv->getStructuredAttrs()) {
if (auto udr = get(*structuredAttrs, "unsafeDiscardReferences")) {
settings.requireExperimentalFeature(Xp::DiscardReferences);
experimentalFeatureSettings.require(Xp::DiscardReferences);
if (auto output = get(*udr, outputName)) {
if (!output->is_boolean())
throw Error("attribute 'unsafeDiscardReferences.\"%s\"' of derivation '%s' must be a Boolean", outputName, drvPath.to_string());
@ -2688,7 +2688,7 @@ DrvOutputs LocalDerivationGoal::registerOutputs()
},
.outPath = newInfo.path
};
if (settings.isExperimentalFeatureEnabled(Xp::CaDerivations)
if (experimentalFeatureSettings.isEnabled(Xp::CaDerivations)
&& drv->type().isPure())
{
signRealisation(thisRealisation);

View file

@ -231,10 +231,10 @@ struct ClientSettings
try {
if (name == "ssh-auth-sock") // obsolete
;
else if (name == settings.experimentalFeatures.name) {
else if (name == experimentalFeatureSettings.experimentalFeatures.name) {
// We dont want to forward the experimental features to
// the daemon, as that could cause some pretty weird stuff
if (parseFeatures(tokenizeString<StringSet>(value)) != settings.experimentalFeatures.get())
if (parseFeatures(tokenizeString<StringSet>(value)) != experimentalFeatureSettings.experimentalFeatures.get())
debug("Ignoring the client-specified experimental features");
} else if (name == settings.pluginFiles.name) {
if (tokenizeString<Paths>(value) != settings.pluginFiles.get())

View file

@ -221,7 +221,7 @@ static DerivationOutput parseDerivationOutput(const Store & store,
}
const auto hashType = parseHashType(hashAlgo);
if (hash == "impure") {
settings.requireExperimentalFeature(Xp::ImpureDerivations);
experimentalFeatureSettings.require(Xp::ImpureDerivations);
assert(pathS == "");
return DerivationOutput::Impure {
.method = std::move(method),
@ -236,7 +236,7 @@ static DerivationOutput parseDerivationOutput(const Store & store,
},
};
} else {
settings.requireExperimentalFeature(Xp::CaDerivations);
experimentalFeatureSettings.require(Xp::CaDerivations);
assert(pathS == "");
return DerivationOutput::CAFloating {
.method = std::move(method),

View file

@ -105,7 +105,7 @@ RealisedPath::Set BuiltPath::toRealisedPaths(Store & store) const
auto drvHashes =
staticOutputHashes(store, store.readDerivation(p.drvPath));
for (auto& [outputName, outputPath] : p.outputs) {
if (settings.isExperimentalFeatureEnabled(
if (experimentalFeatureSettings.isEnabled(
Xp::CaDerivations)) {
auto drvOutput = get(drvHashes, outputName);
if (!drvOutput)

View file

@ -166,18 +166,6 @@ StringSet Settings::getDefaultExtraPlatforms()
return extraPlatforms;
}
bool Settings::isExperimentalFeatureEnabled(const ExperimentalFeature & feature)
{
auto & f = experimentalFeatures.get();
return std::find(f.begin(), f.end(), feature) != f.end();
}
void Settings::requireExperimentalFeature(const ExperimentalFeature & feature)
{
if (!isExperimentalFeatureEnabled(feature))
throw MissingExperimentalFeature(feature);
}
bool Settings::isWSL1()
{
struct utsname utsbuf;

View file

@ -3,7 +3,6 @@
#include "types.hh"
#include "config.hh"
#include "util.hh"
#include "experimental-features.hh"
#include <map>
#include <limits>
@ -932,13 +931,6 @@ public:
are loaded as plugins (non-recursively).
)"};
Setting<std::set<ExperimentalFeature>> experimentalFeatures{this, {}, "experimental-features",
"Experimental Nix features to enable."};
bool isExperimentalFeatureEnabled(const ExperimentalFeature &);
void requireExperimentalFeature(const ExperimentalFeature &);
Setting<size_t> narBufferSize{this, 32 * 1024 * 1024, "nar-buffer-size",
"Maximum size of NARs before spilling them to disk."};

View file

@ -336,7 +336,7 @@ LocalStore::LocalStore(const Params & params)
else openDB(*state, false);
if (settings.isExperimentalFeatureEnabled(Xp::CaDerivations)) {
if (experimentalFeatureSettings.isEnabled(Xp::CaDerivations)) {
migrateCASchema(state->db, dbDir + "/ca-schema", globalLock);
}
@ -366,7 +366,7 @@ LocalStore::LocalStore(const Params & params)
state->stmts->QueryPathFromHashPart.create(state->db,
"select path from ValidPaths where path >= ? limit 1;");
state->stmts->QueryValidPaths.create(state->db, "select path from ValidPaths");
if (settings.isExperimentalFeatureEnabled(Xp::CaDerivations)) {
if (experimentalFeatureSettings.isEnabled(Xp::CaDerivations)) {
state->stmts->RegisterRealisedOutput.create(state->db,
R"(
insert into Realisations (drvPath, outputName, outputPath, signatures)
@ -754,7 +754,7 @@ void LocalStore::checkDerivationOutputs(const StorePath & drvPath, const Derivat
void LocalStore::registerDrvOutput(const Realisation & info, CheckSigsFlag checkSigs)
{
settings.requireExperimentalFeature(Xp::CaDerivations);
experimentalFeatureSettings.require(Xp::CaDerivations);
if (checkSigs == NoCheckSigs || !realisationIsUntrusted(info))
registerDrvOutput(info);
else
@ -763,7 +763,7 @@ void LocalStore::registerDrvOutput(const Realisation & info, CheckSigsFlag check
void LocalStore::registerDrvOutput(const Realisation & info)
{
settings.requireExperimentalFeature(Xp::CaDerivations);
experimentalFeatureSettings.require(Xp::CaDerivations);
retrySQLite<void>([&]() {
auto state(_state.lock());
if (auto oldR = queryRealisation_(*state, info.id)) {
@ -1052,7 +1052,7 @@ LocalStore::queryPartialDerivationOutputMap(const StorePath & path_)
return outputs;
});
if (!settings.isExperimentalFeatureEnabled(Xp::CaDerivations))
if (!experimentalFeatureSettings.isEnabled(Xp::CaDerivations))
return outputs;
auto drv = readInvalidDerivation(path);

View file

@ -129,7 +129,7 @@ struct AutoUserLock : UserLock
useUserNamespace = false;
#endif
settings.requireExperimentalFeature(Xp::AutoAllocateUids);
experimentalFeatureSettings.require(Xp::AutoAllocateUids);
assert(settings.startId > 0);
assert(settings.uidCount % maxIdsPerBuild == 0);
assert((uint64_t) settings.startId + (uint64_t) settings.uidCount <= std::numeric_limits<uid_t>::max());

View file

@ -326,7 +326,7 @@ OutputPathMap resolveDerivedPath(Store & store, const DerivedPath::Built & bfd,
throw Error(
"the derivation '%s' doesn't have an output named '%s'",
store.printStorePath(bfd.drvPath), output);
if (settings.isExperimentalFeatureEnabled(Xp::CaDerivations)) {
if (experimentalFeatureSettings.isEnabled(Xp::CaDerivations)) {
DrvOutput outputId { *outputHash, output };
auto realisation = store.queryRealisation(outputId);
if (!realisation)

View file

@ -265,7 +265,7 @@ void RemoteStore::setOptions(Connection & conn)
overrides.erase(settings.buildCores.name);
overrides.erase(settings.useSubstitutes.name);
overrides.erase(loggerSettings.showTrace.name);
overrides.erase(settings.experimentalFeatures.name);
overrides.erase(experimentalFeatureSettings.experimentalFeatures.name);
overrides.erase(settings.pluginFiles.name);
conn.to << overrides.size();
for (auto & i : overrides)
@ -876,7 +876,7 @@ std::vector<BuildResult> RemoteStore::buildPathsWithResults(
"the derivation '%s' doesn't have an output named '%s'",
printStorePath(bfd.drvPath), output);
auto outputId = DrvOutput{ *outputHash, output };
if (settings.isExperimentalFeatureEnabled(Xp::CaDerivations)) {
if (experimentalFeatureSettings.isEnabled(Xp::CaDerivations)) {
auto realisation =
queryRealisation(outputId);
if (!realisation)

View file

@ -445,10 +445,10 @@ StringSet StoreConfig::getDefaultSystemFeatures()
{
auto res = settings.systemFeatures.get();
if (settings.isExperimentalFeatureEnabled(Xp::CaDerivations))
if (experimentalFeatureSettings.isEnabled(Xp::CaDerivations))
res.insert("ca-derivations");
if (settings.isExperimentalFeatureEnabled(Xp::RecursiveNix))
if (experimentalFeatureSettings.isEnabled(Xp::RecursiveNix))
res.insert("recursive-nix");
return res;
@ -1017,7 +1017,7 @@ std::map<StorePath, StorePath> copyPaths(
for (auto & path : paths) {
storePaths.insert(path.path());
if (auto realisation = std::get_if<Realisation>(&path.raw)) {
settings.requireExperimentalFeature(Xp::CaDerivations);
experimentalFeatureSettings.require(Xp::CaDerivations);
toplevelRealisations.insert(*realisation);
}
}
@ -1250,7 +1250,7 @@ std::optional<StorePath> Store::getBuildDerivationPath(const StorePath & path)
}
}
if (!settings.isExperimentalFeatureEnabled(Xp::CaDerivations) || !isValidPath(path))
if (!experimentalFeatureSettings.isEnabled(Xp::CaDerivations) || !isValidPath(path))
return path;
auto drv = readDerivation(path);

View file

@ -444,4 +444,30 @@ GlobalConfig::Register::Register(Config * config)
configRegistrations->emplace_back(config);
}
ExperimentalFeatureSettings experimentalFeatureSettings;
static GlobalConfig::Register rSettings(&experimentalFeatureSettings);
bool ExperimentalFeatureSettings::isEnabled(const ExperimentalFeature & feature) const
{
auto & f = experimentalFeatures.get();
return std::find(f.begin(), f.end(), feature) != f.end();
}
void ExperimentalFeatureSettings::require(const ExperimentalFeature & feature) const
{
if (!isEnabled(feature))
throw MissingExperimentalFeature(feature);
}
bool ExperimentalFeatureSettings::isEnabled(const std::optional<ExperimentalFeature> & feature) const
{
return !feature || isEnabled(*feature);
}
void ExperimentalFeatureSettings::require(const std::optional<ExperimentalFeature> & feature) const
{
if (feature) require(*feature);
}
}

View file

@ -1,12 +1,13 @@
#pragma once
#include <cassert>
#include <map>
#include <set>
#include "types.hh"
#include <nlohmann/json_fwd.hpp>
#pragma once
#include "types.hh"
#include "experimental-features.hh"
namespace nix {
@ -357,4 +358,37 @@ struct GlobalConfig : public AbstractConfig
extern GlobalConfig globalConfig;
struct ExperimentalFeatureSettings : Config {
Setting<std::set<ExperimentalFeature>> experimentalFeatures{this, {}, "experimental-features",
"Experimental Nix features to enable."};
/**
* Check whether the given experimental feature is enabled.
*/
bool isEnabled(const ExperimentalFeature &) const;
/**
* Require an experimental feature be enabled, throwing an error if it is
* not.
*/
void require(const ExperimentalFeature &) const;
/**
* `std::nullopt` pointer means no feature, which means there is nothing that could be
* disabled, and so the function returns true in that case.
*/
bool isEnabled(const std::optional<ExperimentalFeature> &) const;
/**
* `std::nullopt` pointer means no feature, which means there is nothing that could be
* disabled, and so the function does nothing in that case.
*/
void require(const std::optional<ExperimentalFeature> &) const;
};
// FIXME: don't use a global variable.
extern ExperimentalFeatureSettings experimentalFeatureSettings;
}

View file

@ -440,7 +440,7 @@ static void main_nix_build(int argc, char * * argv)
shell = store->printStorePath(shellDrvOutputs.at("out").value()) + "/bin/bash";
}
if (settings.isExperimentalFeatureEnabled(Xp::CaDerivations)) {
if (experimentalFeatureSettings.isEnabled(Xp::CaDerivations)) {
auto resolvedDrv = drv.tryResolve(*store);
assert(resolvedDrv && "Successfully resolved the derivation");
drv = *resolvedDrv;

View file

@ -208,7 +208,7 @@ static StorePath getDerivationEnvironment(ref<Store> store, ref<Store> evalStore
drv.name += "-env";
drv.env.emplace("name", drv.name);
drv.inputSrcs.insert(std::move(getEnvShPath));
if (settings.isExperimentalFeatureEnabled(Xp::CaDerivations)) {
if (experimentalFeatureSettings.isEnabled(Xp::CaDerivations)) {
for (auto & output : drv.outputs) {
output.second = DerivationOutput::Deferred {},
drv.env[output.first] = hashPlaceholder(output.first);

View file

@ -1328,7 +1328,7 @@ struct CmdFlake : NixMultiCommand
{
if (!command)
throw UsageError("'nix flake' requires a sub-command.");
settings.requireExperimentalFeature(Xp::Flakes);
experimentalFeatureSettings.require(Xp::Flakes);
command->second->run();
}
};

View file

@ -297,7 +297,10 @@ void mainWrapped(int argc, char * * argv)
}
if (argc == 2 && std::string(argv[1]) == "__dump-builtins") {
settings.experimentalFeatures = {Xp::Flakes, Xp::FetchClosure};
experimentalFeatureSettings.experimentalFeatures = {
Xp::Flakes,
Xp::FetchClosure,
};
evalSettings.pureEval = false;
EvalState state({}, openStore("dummy://"));
auto res = nlohmann::json::object();
@ -366,7 +369,7 @@ void mainWrapped(int argc, char * * argv)
if (args.command->first != "repl"
&& args.command->first != "doctor"
&& args.command->first != "upgrade-nix")
settings.requireExperimentalFeature(Xp::NixCommand);
experimentalFeatureSettings.require(Xp::NixCommand);
if (args.useNet && !haveInternet()) {
warn("you don't have Internet access; disabling some network-dependent features");

View file

@ -45,7 +45,7 @@ struct CmdRealisationInfo : BuiltPathsCommand, MixJSON
void run(ref<Store> store, BuiltPaths && paths) override
{
settings.requireExperimentalFeature(Xp::CaDerivations);
experimentalFeatureSettings.require(Xp::CaDerivations);
RealisedPath::Set realisations;
for (auto & builtPath : paths) {

View file

@ -224,7 +224,7 @@ struct CmdRegistry : virtual NixMultiCommand
void run() override
{
settings.requireExperimentalFeature(Xp::Flakes);
experimentalFeatureSettings.require(Xp::Flakes);
if (!command)
throw UsageError("'nix registry' requires a sub-command.");
command->second->run();

View file

@ -37,7 +37,7 @@ struct CmdRepl : RawInstallablesCommand
void applyDefaultInstallables(std::vector<std::string> & rawInstallables) override
{
if (!settings.isExperimentalFeatureEnabled(Xp::ReplFlake) && !(file) && rawInstallables.size() >= 1) {
if (!experimentalFeatureSettings.isEnabled(Xp::ReplFlake) && !(file) && rawInstallables.size() >= 1) {
warn("future versions of Nix will require using `--file` to load a file");
if (rawInstallables.size() > 1)
warn("more than one input file is not currently supported");