Merge pull request #7609 from obsidiansystems/hide-experimental-settings
Hide experimental settings
This commit is contained in:
commit
570829d67e
|
@ -305,7 +305,7 @@ connected:
|
||||||
|
|
||||||
std::set<Realisation> missingRealisations;
|
std::set<Realisation> missingRealisations;
|
||||||
StorePathSet missingPaths;
|
StorePathSet missingPaths;
|
||||||
if (settings.isExperimentalFeatureEnabled(Xp::CaDerivations) && !drv.type().hasKnownOutputPaths()) {
|
if (experimentalFeatureSettings.isEnabled(Xp::CaDerivations) && !drv.type().hasKnownOutputPaths()) {
|
||||||
for (auto & outputName : wantedOutputs) {
|
for (auto & outputName : wantedOutputs) {
|
||||||
auto thisOutputHash = outputHashes.at(outputName);
|
auto thisOutputHash = outputHashes.at(outputName);
|
||||||
auto thisOutputId = DrvOutput{ thisOutputHash, outputName };
|
auto thisOutputId = DrvOutput{ thisOutputHash, outputName };
|
||||||
|
@ -337,7 +337,7 @@ connected:
|
||||||
for (auto & realisation : missingRealisations) {
|
for (auto & realisation : missingRealisations) {
|
||||||
// Should hold, because if the feature isn't enabled the set
|
// Should hold, because if the feature isn't enabled the set
|
||||||
// of missing realisations should be empty
|
// of missing realisations should be empty
|
||||||
settings.requireExperimentalFeature(Xp::CaDerivations);
|
experimentalFeatureSettings.require(Xp::CaDerivations);
|
||||||
store->registerDrvOutput(realisation);
|
store->registerDrvOutput(realisation);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -166,7 +166,7 @@ Path lookupFileArg(EvalState & state, std::string_view s)
|
||||||
}
|
}
|
||||||
|
|
||||||
else if (hasPrefix(s, "flake:")) {
|
else if (hasPrefix(s, "flake:")) {
|
||||||
settings.requireExperimentalFeature(Xp::Flakes);
|
experimentalFeatureSettings.require(Xp::Flakes);
|
||||||
auto flakeRef = parseFlakeRef(std::string(s.substr(6)), {}, true, false);
|
auto flakeRef = parseFlakeRef(std::string(s.substr(6)), {}, true, false);
|
||||||
auto storePath = flakeRef.resolve(state.store).fetchTree(state.store).first.storePath;
|
auto storePath = flakeRef.resolve(state.store).fetchTree(state.store).first.storePath;
|
||||||
return state.store->toRealPath(storePath);
|
return state.store->toRealPath(storePath);
|
||||||
|
|
|
@ -332,7 +332,7 @@ void completeFlakeRefWithFragment(
|
||||||
|
|
||||||
void completeFlakeRef(ref<Store> store, std::string_view prefix)
|
void completeFlakeRef(ref<Store> store, std::string_view prefix)
|
||||||
{
|
{
|
||||||
if (!settings.isExperimentalFeatureEnabled(Xp::Flakes))
|
if (!experimentalFeatureSettings.isEnabled(Xp::Flakes))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (prefix == "")
|
if (prefix == "")
|
||||||
|
|
|
@ -320,7 +320,7 @@ LockedFlake lockFlake(
|
||||||
const FlakeRef & topRef,
|
const FlakeRef & topRef,
|
||||||
const LockFlags & lockFlags)
|
const LockFlags & lockFlags)
|
||||||
{
|
{
|
||||||
settings.requireExperimentalFeature(Xp::Flakes);
|
experimentalFeatureSettings.require(Xp::Flakes);
|
||||||
|
|
||||||
FlakeCache flakeCache;
|
FlakeCache flakeCache;
|
||||||
|
|
||||||
|
|
|
@ -469,7 +469,7 @@ expr_simple
|
||||||
new ExprString(std::move(path))});
|
new ExprString(std::move(path))});
|
||||||
}
|
}
|
||||||
| URI {
|
| URI {
|
||||||
static bool noURLLiterals = settings.isExperimentalFeatureEnabled(Xp::NoUrlLiterals);
|
static bool noURLLiterals = experimentalFeatureSettings.isEnabled(Xp::NoUrlLiterals);
|
||||||
if (noURLLiterals)
|
if (noURLLiterals)
|
||||||
throw ParseError({
|
throw ParseError({
|
||||||
.msg = hintfmt("URL literals are disabled"),
|
.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:")) {
|
else if (hasPrefix(elem.second, "flake:")) {
|
||||||
settings.requireExperimentalFeature(Xp::Flakes);
|
experimentalFeatureSettings.require(Xp::Flakes);
|
||||||
auto flakeRef = parseFlakeRef(elem.second.substr(6), {}, true, false);
|
auto flakeRef = parseFlakeRef(elem.second.substr(6), {}, true, false);
|
||||||
debug("fetching flake search path element '%s''", elem.second);
|
debug("fetching flake search path element '%s''", elem.second);
|
||||||
auto storePath = flakeRef.resolve(store).fetchTree(store).first.storePath;
|
auto storePath = flakeRef.resolve(store).fetchTree(store).first.storePath;
|
||||||
|
|
|
@ -1141,13 +1141,13 @@ drvName, Bindings * attrs, Value & v)
|
||||||
if (i->name == state.sContentAddressed) {
|
if (i->name == state.sContentAddressed) {
|
||||||
contentAddressed = state.forceBool(*i->value, noPos, context_below);
|
contentAddressed = state.forceBool(*i->value, noPos, context_below);
|
||||||
if (contentAddressed)
|
if (contentAddressed)
|
||||||
settings.requireExperimentalFeature(Xp::CaDerivations);
|
experimentalFeatureSettings.require(Xp::CaDerivations);
|
||||||
}
|
}
|
||||||
|
|
||||||
else if (i->name == state.sImpure) {
|
else if (i->name == state.sImpure) {
|
||||||
isImpure = state.forceBool(*i->value, noPos, context_below);
|
isImpure = state.forceBool(*i->value, noPos, context_below);
|
||||||
if (isImpure)
|
if (isImpure)
|
||||||
settings.requireExperimentalFeature(Xp::ImpureDerivations);
|
experimentalFeatureSettings.require(Xp::ImpureDerivations);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* The `args' attribute is special: it supplies the
|
/* The `args' attribute is special: it supplies the
|
||||||
|
@ -4114,7 +4114,7 @@ void EvalState::createBaseEnv()
|
||||||
if (RegisterPrimOp::primOps)
|
if (RegisterPrimOp::primOps)
|
||||||
for (auto & primOp : *RegisterPrimOp::primOps)
|
for (auto & primOp : *RegisterPrimOp::primOps)
|
||||||
if (!primOp.experimentalFeature
|
if (!primOp.experimentalFeature
|
||||||
|| settings.isExperimentalFeatureEnabled(*primOp.experimentalFeature))
|
|| experimentalFeatureSettings.isEnabled(*primOp.experimentalFeature))
|
||||||
{
|
{
|
||||||
addPrimOp({
|
addPrimOp({
|
||||||
.fun = primOp.fun,
|
.fun = primOp.fun,
|
||||||
|
|
|
@ -190,7 +190,7 @@ static void fetchTree(
|
||||||
|
|
||||||
static void prim_fetchTree(EvalState & state, const PosIdx pos, Value * * args, Value & v)
|
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 });
|
fetchTree(state, pos, args, v, std::nullopt, FetchTreeParams { .allowNameArgument = false });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -75,21 +75,25 @@ struct FetchSettings : public Config
|
||||||
Path or URI of the global flake registry.
|
Path or URI of the global flake registry.
|
||||||
|
|
||||||
When empty, disables the global flake registry.
|
When empty, disables the global flake registry.
|
||||||
)"};
|
)",
|
||||||
|
{}, true, Xp::Flakes};
|
||||||
|
|
||||||
|
|
||||||
Setting<bool> useRegistries{this, true, "use-registries",
|
Setting<bool> useRegistries{this, true, "use-registries",
|
||||||
"Whether to use flake registries to resolve flake references."};
|
"Whether to use flake registries to resolve flake references.",
|
||||||
|
{}, true, Xp::Flakes};
|
||||||
|
|
||||||
Setting<bool> acceptFlakeConfig{this, false, "accept-flake-config",
|
Setting<bool> acceptFlakeConfig{this, false, "accept-flake-config",
|
||||||
"Whether to accept nix configuration from a flake without prompting."};
|
"Whether to accept nix configuration from a flake without prompting.",
|
||||||
|
{}, true, Xp::Flakes};
|
||||||
|
|
||||||
Setting<std::string> commitLockFileSummary{
|
Setting<std::string> commitLockFileSummary{
|
||||||
this, "", "commit-lockfile-summary",
|
this, "", "commit-lockfile-summary",
|
||||||
R"(
|
R"(
|
||||||
The commit summary to use when committing changed flake lock files. If
|
The commit summary to use when committing changed flake lock files. If
|
||||||
empty, the summary is generated based on the action performed.
|
empty, the summary is generated based on the action performed.
|
||||||
)"};
|
)",
|
||||||
|
{}, true, Xp::Flakes};
|
||||||
};
|
};
|
||||||
|
|
||||||
// FIXME: don't use a global variable.
|
// FIXME: don't use a global variable.
|
||||||
|
|
|
@ -199,10 +199,10 @@ void DerivationGoal::haveDerivation()
|
||||||
parsedDrv = std::make_unique<ParsedDerivation>(drvPath, *drv);
|
parsedDrv = std::make_unique<ParsedDerivation>(drvPath, *drv);
|
||||||
|
|
||||||
if (!drv->type().hasKnownOutputPaths())
|
if (!drv->type().hasKnownOutputPaths())
|
||||||
settings.requireExperimentalFeature(Xp::CaDerivations);
|
experimentalFeatureSettings.require(Xp::CaDerivations);
|
||||||
|
|
||||||
if (!drv->type().isPure()) {
|
if (!drv->type().isPure()) {
|
||||||
settings.requireExperimentalFeature(Xp::ImpureDerivations);
|
experimentalFeatureSettings.require(Xp::ImpureDerivations);
|
||||||
|
|
||||||
for (auto & [outputName, output] : drv->outputs) {
|
for (auto & [outputName, output] : drv->outputs) {
|
||||||
auto randomPath = StorePath::random(outputPathName(drv->name, outputName));
|
auto randomPath = StorePath::random(outputPathName(drv->name, outputName));
|
||||||
|
@ -336,7 +336,7 @@ void DerivationGoal::gaveUpOnSubstitution()
|
||||||
for (auto & i : dynamic_cast<Derivation *>(drv.get())->inputDrvs) {
|
for (auto & i : dynamic_cast<Derivation *>(drv.get())->inputDrvs) {
|
||||||
/* Ensure that pure, non-fixed-output derivations don't
|
/* Ensure that pure, non-fixed-output derivations don't
|
||||||
depend on impure derivations. */
|
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);
|
auto inputDrv = worker.evalStore.readDerivation(i.first);
|
||||||
if (!inputDrv.type().isPure())
|
if (!inputDrv.type().isPure())
|
||||||
throw Error("pure derivation '%s' depends on impure derivation '%s'",
|
throw Error("pure derivation '%s' depends on impure derivation '%s'",
|
||||||
|
@ -477,7 +477,7 @@ void DerivationGoal::inputsRealised()
|
||||||
ca.fixed
|
ca.fixed
|
||||||
/* Can optionally resolve if fixed, which is good
|
/* Can optionally resolve if fixed, which is good
|
||||||
for avoiding unnecessary rebuilds. */
|
for avoiding unnecessary rebuilds. */
|
||||||
? settings.isExperimentalFeatureEnabled(Xp::CaDerivations)
|
? experimentalFeatureSettings.isEnabled(Xp::CaDerivations)
|
||||||
/* Must resolve if floating and there are any inputs
|
/* Must resolve if floating and there are any inputs
|
||||||
drvs. */
|
drvs. */
|
||||||
: true);
|
: true);
|
||||||
|
@ -488,7 +488,7 @@ void DerivationGoal::inputsRealised()
|
||||||
}, drvType.raw());
|
}, drvType.raw());
|
||||||
|
|
||||||
if (resolveDrv && !fullDrv.inputDrvs.empty()) {
|
if (resolveDrv && !fullDrv.inputDrvs.empty()) {
|
||||||
settings.requireExperimentalFeature(Xp::CaDerivations);
|
experimentalFeatureSettings.require(Xp::CaDerivations);
|
||||||
|
|
||||||
/* We are be able to resolve this derivation based on the
|
/* We are be able to resolve this derivation based on the
|
||||||
now-known results of dependencies. If so, we become a
|
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};
|
auto drvOutput = DrvOutput{info.outputHash, i.first};
|
||||||
if (settings.isExperimentalFeatureEnabled(Xp::CaDerivations)) {
|
if (experimentalFeatureSettings.isEnabled(Xp::CaDerivations)) {
|
||||||
if (auto real = worker.store.queryRealisation(drvOutput)) {
|
if (auto real = worker.store.queryRealisation(drvOutput)) {
|
||||||
info.known = {
|
info.known = {
|
||||||
.path = real->outPath,
|
.path = real->outPath,
|
||||||
|
|
|
@ -413,7 +413,7 @@ void LocalDerivationGoal::startBuilder()
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
#if __linux__
|
#if __linux__
|
||||||
settings.requireExperimentalFeature(Xp::Cgroups);
|
experimentalFeatureSettings.require(Xp::Cgroups);
|
||||||
|
|
||||||
auto cgroupFS = getCgroupFS();
|
auto cgroupFS = getCgroupFS();
|
||||||
if (!cgroupFS)
|
if (!cgroupFS)
|
||||||
|
@ -1420,7 +1420,7 @@ struct RestrictedStore : public virtual RestrictedStoreConfig, public virtual Lo
|
||||||
|
|
||||||
void LocalDerivationGoal::startDaemon()
|
void LocalDerivationGoal::startDaemon()
|
||||||
{
|
{
|
||||||
settings.requireExperimentalFeature(Xp::RecursiveNix);
|
experimentalFeatureSettings.require(Xp::RecursiveNix);
|
||||||
|
|
||||||
Store::Params params;
|
Store::Params params;
|
||||||
params["path-info-cache-size"] = "0";
|
params["path-info-cache-size"] = "0";
|
||||||
|
@ -2280,7 +2280,7 @@ DrvOutputs LocalDerivationGoal::registerOutputs()
|
||||||
bool discardReferences = false;
|
bool discardReferences = false;
|
||||||
if (auto structuredAttrs = parsedDrv->getStructuredAttrs()) {
|
if (auto structuredAttrs = parsedDrv->getStructuredAttrs()) {
|
||||||
if (auto udr = get(*structuredAttrs, "unsafeDiscardReferences")) {
|
if (auto udr = get(*structuredAttrs, "unsafeDiscardReferences")) {
|
||||||
settings.requireExperimentalFeature(Xp::DiscardReferences);
|
experimentalFeatureSettings.require(Xp::DiscardReferences);
|
||||||
if (auto output = get(*udr, outputName)) {
|
if (auto output = get(*udr, outputName)) {
|
||||||
if (!output->is_boolean())
|
if (!output->is_boolean())
|
||||||
throw Error("attribute 'unsafeDiscardReferences.\"%s\"' of derivation '%s' must be a Boolean", outputName, drvPath.to_string());
|
throw Error("attribute 'unsafeDiscardReferences.\"%s\"' of derivation '%s' must be a Boolean", outputName, drvPath.to_string());
|
||||||
|
@ -2700,7 +2700,7 @@ DrvOutputs LocalDerivationGoal::registerOutputs()
|
||||||
},
|
},
|
||||||
.outPath = newInfo.path
|
.outPath = newInfo.path
|
||||||
};
|
};
|
||||||
if (settings.isExperimentalFeatureEnabled(Xp::CaDerivations)
|
if (experimentalFeatureSettings.isEnabled(Xp::CaDerivations)
|
||||||
&& drv->type().isPure())
|
&& drv->type().isPure())
|
||||||
{
|
{
|
||||||
signRealisation(thisRealisation);
|
signRealisation(thisRealisation);
|
||||||
|
|
|
@ -231,10 +231,10 @@ struct ClientSettings
|
||||||
try {
|
try {
|
||||||
if (name == "ssh-auth-sock") // obsolete
|
if (name == "ssh-auth-sock") // obsolete
|
||||||
;
|
;
|
||||||
else if (name == settings.experimentalFeatures.name) {
|
else if (name == experimentalFeatureSettings.experimentalFeatures.name) {
|
||||||
// We don’t want to forward the experimental features to
|
// We don’t want to forward the experimental features to
|
||||||
// the daemon, as that could cause some pretty weird stuff
|
// 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");
|
debug("Ignoring the client-specified experimental features");
|
||||||
} else if (name == settings.pluginFiles.name) {
|
} else if (name == settings.pluginFiles.name) {
|
||||||
if (tokenizeString<Paths>(value) != settings.pluginFiles.get())
|
if (tokenizeString<Paths>(value) != settings.pluginFiles.get())
|
||||||
|
|
|
@ -221,7 +221,7 @@ static DerivationOutput parseDerivationOutput(const Store & store,
|
||||||
}
|
}
|
||||||
const auto hashType = parseHashType(hashAlgo);
|
const auto hashType = parseHashType(hashAlgo);
|
||||||
if (hash == "impure") {
|
if (hash == "impure") {
|
||||||
settings.requireExperimentalFeature(Xp::ImpureDerivations);
|
experimentalFeatureSettings.require(Xp::ImpureDerivations);
|
||||||
assert(pathS == "");
|
assert(pathS == "");
|
||||||
return DerivationOutput::Impure {
|
return DerivationOutput::Impure {
|
||||||
.method = std::move(method),
|
.method = std::move(method),
|
||||||
|
@ -236,7 +236,7 @@ static DerivationOutput parseDerivationOutput(const Store & store,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
settings.requireExperimentalFeature(Xp::CaDerivations);
|
experimentalFeatureSettings.require(Xp::CaDerivations);
|
||||||
assert(pathS == "");
|
assert(pathS == "");
|
||||||
return DerivationOutput::CAFloating {
|
return DerivationOutput::CAFloating {
|
||||||
.method = std::move(method),
|
.method = std::move(method),
|
||||||
|
|
|
@ -105,7 +105,7 @@ RealisedPath::Set BuiltPath::toRealisedPaths(Store & store) const
|
||||||
auto drvHashes =
|
auto drvHashes =
|
||||||
staticOutputHashes(store, store.readDerivation(p.drvPath));
|
staticOutputHashes(store, store.readDerivation(p.drvPath));
|
||||||
for (auto& [outputName, outputPath] : p.outputs) {
|
for (auto& [outputName, outputPath] : p.outputs) {
|
||||||
if (settings.isExperimentalFeatureEnabled(
|
if (experimentalFeatureSettings.isEnabled(
|
||||||
Xp::CaDerivations)) {
|
Xp::CaDerivations)) {
|
||||||
auto drvOutput = get(drvHashes, outputName);
|
auto drvOutput = get(drvHashes, outputName);
|
||||||
if (!drvOutput)
|
if (!drvOutput)
|
||||||
|
|
|
@ -166,18 +166,6 @@ StringSet Settings::getDefaultExtraPlatforms()
|
||||||
return extraPlatforms;
|
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()
|
bool Settings::isWSL1()
|
||||||
{
|
{
|
||||||
struct utsname utsbuf;
|
struct utsname utsbuf;
|
||||||
|
|
|
@ -3,7 +3,6 @@
|
||||||
#include "types.hh"
|
#include "types.hh"
|
||||||
#include "config.hh"
|
#include "config.hh"
|
||||||
#include "util.hh"
|
#include "util.hh"
|
||||||
#include "experimental-features.hh"
|
|
||||||
|
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <limits>
|
#include <limits>
|
||||||
|
@ -932,13 +931,6 @@ public:
|
||||||
are loaded as plugins (non-recursively).
|
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",
|
Setting<size_t> narBufferSize{this, 32 * 1024 * 1024, "nar-buffer-size",
|
||||||
"Maximum size of NARs before spilling them to disk."};
|
"Maximum size of NARs before spilling them to disk."};
|
||||||
|
|
||||||
|
|
|
@ -336,7 +336,7 @@ LocalStore::LocalStore(const Params & params)
|
||||||
|
|
||||||
else openDB(*state, false);
|
else openDB(*state, false);
|
||||||
|
|
||||||
if (settings.isExperimentalFeatureEnabled(Xp::CaDerivations)) {
|
if (experimentalFeatureSettings.isEnabled(Xp::CaDerivations)) {
|
||||||
migrateCASchema(state->db, dbDir + "/ca-schema", globalLock);
|
migrateCASchema(state->db, dbDir + "/ca-schema", globalLock);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -366,7 +366,7 @@ LocalStore::LocalStore(const Params & params)
|
||||||
state->stmts->QueryPathFromHashPart.create(state->db,
|
state->stmts->QueryPathFromHashPart.create(state->db,
|
||||||
"select path from ValidPaths where path >= ? limit 1;");
|
"select path from ValidPaths where path >= ? limit 1;");
|
||||||
state->stmts->QueryValidPaths.create(state->db, "select path from ValidPaths");
|
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,
|
state->stmts->RegisterRealisedOutput.create(state->db,
|
||||||
R"(
|
R"(
|
||||||
insert into Realisations (drvPath, outputName, outputPath, signatures)
|
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)
|
void LocalStore::registerDrvOutput(const Realisation & info, CheckSigsFlag checkSigs)
|
||||||
{
|
{
|
||||||
settings.requireExperimentalFeature(Xp::CaDerivations);
|
experimentalFeatureSettings.require(Xp::CaDerivations);
|
||||||
if (checkSigs == NoCheckSigs || !realisationIsUntrusted(info))
|
if (checkSigs == NoCheckSigs || !realisationIsUntrusted(info))
|
||||||
registerDrvOutput(info);
|
registerDrvOutput(info);
|
||||||
else
|
else
|
||||||
|
@ -763,7 +763,7 @@ void LocalStore::registerDrvOutput(const Realisation & info, CheckSigsFlag check
|
||||||
|
|
||||||
void LocalStore::registerDrvOutput(const Realisation & info)
|
void LocalStore::registerDrvOutput(const Realisation & info)
|
||||||
{
|
{
|
||||||
settings.requireExperimentalFeature(Xp::CaDerivations);
|
experimentalFeatureSettings.require(Xp::CaDerivations);
|
||||||
retrySQLite<void>([&]() {
|
retrySQLite<void>([&]() {
|
||||||
auto state(_state.lock());
|
auto state(_state.lock());
|
||||||
if (auto oldR = queryRealisation_(*state, info.id)) {
|
if (auto oldR = queryRealisation_(*state, info.id)) {
|
||||||
|
@ -1052,7 +1052,7 @@ LocalStore::queryPartialDerivationOutputMap(const StorePath & path_)
|
||||||
return outputs;
|
return outputs;
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!settings.isExperimentalFeatureEnabled(Xp::CaDerivations))
|
if (!experimentalFeatureSettings.isEnabled(Xp::CaDerivations))
|
||||||
return outputs;
|
return outputs;
|
||||||
|
|
||||||
auto drv = readInvalidDerivation(path);
|
auto drv = readInvalidDerivation(path);
|
||||||
|
|
|
@ -129,7 +129,7 @@ struct AutoUserLock : UserLock
|
||||||
useUserNamespace = false;
|
useUserNamespace = false;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
settings.requireExperimentalFeature(Xp::AutoAllocateUids);
|
experimentalFeatureSettings.require(Xp::AutoAllocateUids);
|
||||||
assert(settings.startId > 0);
|
assert(settings.startId > 0);
|
||||||
assert(settings.uidCount % maxIdsPerBuild == 0);
|
assert(settings.uidCount % maxIdsPerBuild == 0);
|
||||||
assert((uint64_t) settings.startId + (uint64_t) settings.uidCount <= std::numeric_limits<uid_t>::max());
|
assert((uint64_t) settings.startId + (uint64_t) settings.uidCount <= std::numeric_limits<uid_t>::max());
|
||||||
|
|
|
@ -326,7 +326,7 @@ OutputPathMap resolveDerivedPath(Store & store, const DerivedPath::Built & bfd,
|
||||||
throw Error(
|
throw Error(
|
||||||
"the derivation '%s' doesn't have an output named '%s'",
|
"the derivation '%s' doesn't have an output named '%s'",
|
||||||
store.printStorePath(bfd.drvPath), output);
|
store.printStorePath(bfd.drvPath), output);
|
||||||
if (settings.isExperimentalFeatureEnabled(Xp::CaDerivations)) {
|
if (experimentalFeatureSettings.isEnabled(Xp::CaDerivations)) {
|
||||||
DrvOutput outputId { *outputHash, output };
|
DrvOutput outputId { *outputHash, output };
|
||||||
auto realisation = store.queryRealisation(outputId);
|
auto realisation = store.queryRealisation(outputId);
|
||||||
if (!realisation)
|
if (!realisation)
|
||||||
|
|
|
@ -265,7 +265,7 @@ void RemoteStore::setOptions(Connection & conn)
|
||||||
overrides.erase(settings.buildCores.name);
|
overrides.erase(settings.buildCores.name);
|
||||||
overrides.erase(settings.useSubstitutes.name);
|
overrides.erase(settings.useSubstitutes.name);
|
||||||
overrides.erase(loggerSettings.showTrace.name);
|
overrides.erase(loggerSettings.showTrace.name);
|
||||||
overrides.erase(settings.experimentalFeatures.name);
|
overrides.erase(experimentalFeatureSettings.experimentalFeatures.name);
|
||||||
overrides.erase(settings.pluginFiles.name);
|
overrides.erase(settings.pluginFiles.name);
|
||||||
conn.to << overrides.size();
|
conn.to << overrides.size();
|
||||||
for (auto & i : overrides)
|
for (auto & i : overrides)
|
||||||
|
@ -876,7 +876,7 @@ std::vector<BuildResult> RemoteStore::buildPathsWithResults(
|
||||||
"the derivation '%s' doesn't have an output named '%s'",
|
"the derivation '%s' doesn't have an output named '%s'",
|
||||||
printStorePath(bfd.drvPath), output);
|
printStorePath(bfd.drvPath), output);
|
||||||
auto outputId = DrvOutput{ *outputHash, output };
|
auto outputId = DrvOutput{ *outputHash, output };
|
||||||
if (settings.isExperimentalFeatureEnabled(Xp::CaDerivations)) {
|
if (experimentalFeatureSettings.isEnabled(Xp::CaDerivations)) {
|
||||||
auto realisation =
|
auto realisation =
|
||||||
queryRealisation(outputId);
|
queryRealisation(outputId);
|
||||||
if (!realisation)
|
if (!realisation)
|
||||||
|
|
|
@ -445,10 +445,10 @@ StringSet StoreConfig::getDefaultSystemFeatures()
|
||||||
{
|
{
|
||||||
auto res = settings.systemFeatures.get();
|
auto res = settings.systemFeatures.get();
|
||||||
|
|
||||||
if (settings.isExperimentalFeatureEnabled(Xp::CaDerivations))
|
if (experimentalFeatureSettings.isEnabled(Xp::CaDerivations))
|
||||||
res.insert("ca-derivations");
|
res.insert("ca-derivations");
|
||||||
|
|
||||||
if (settings.isExperimentalFeatureEnabled(Xp::RecursiveNix))
|
if (experimentalFeatureSettings.isEnabled(Xp::RecursiveNix))
|
||||||
res.insert("recursive-nix");
|
res.insert("recursive-nix");
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
|
@ -1017,7 +1017,7 @@ std::map<StorePath, StorePath> copyPaths(
|
||||||
for (auto & path : paths) {
|
for (auto & path : paths) {
|
||||||
storePaths.insert(path.path());
|
storePaths.insert(path.path());
|
||||||
if (auto realisation = std::get_if<Realisation>(&path.raw)) {
|
if (auto realisation = std::get_if<Realisation>(&path.raw)) {
|
||||||
settings.requireExperimentalFeature(Xp::CaDerivations);
|
experimentalFeatureSettings.require(Xp::CaDerivations);
|
||||||
toplevelRealisations.insert(*realisation);
|
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;
|
return path;
|
||||||
|
|
||||||
auto drv = readDerivation(path);
|
auto drv = readDerivation(path);
|
||||||
|
|
|
@ -52,7 +52,7 @@ std::shared_ptr<Completions> completions;
|
||||||
|
|
||||||
std::string completionMarker = "___COMPLETE___";
|
std::string completionMarker = "___COMPLETE___";
|
||||||
|
|
||||||
std::optional<std::string> needsCompletion(std::string_view s)
|
static std::optional<std::string> needsCompletion(std::string_view s)
|
||||||
{
|
{
|
||||||
if (!completions) return {};
|
if (!completions) return {};
|
||||||
auto i = s.find(completionMarker);
|
auto i = s.find(completionMarker);
|
||||||
|
@ -120,6 +120,12 @@ void Args::parseCmdline(const Strings & _cmdline)
|
||||||
|
|
||||||
if (!argsSeen)
|
if (!argsSeen)
|
||||||
initialFlagsProcessed();
|
initialFlagsProcessed();
|
||||||
|
|
||||||
|
/* Now that we are done parsing, make sure that any experimental
|
||||||
|
* feature required by the flags is enabled */
|
||||||
|
for (auto & f : flagExperimentalFeatures)
|
||||||
|
experimentalFeatureSettings.require(f);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Args::processFlag(Strings::iterator & pos, Strings::iterator end)
|
bool Args::processFlag(Strings::iterator & pos, Strings::iterator end)
|
||||||
|
@ -128,12 +134,18 @@ bool Args::processFlag(Strings::iterator & pos, Strings::iterator end)
|
||||||
|
|
||||||
auto process = [&](const std::string & name, const Flag & flag) -> bool {
|
auto process = [&](const std::string & name, const Flag & flag) -> bool {
|
||||||
++pos;
|
++pos;
|
||||||
|
|
||||||
|
if (auto & f = flag.experimentalFeature)
|
||||||
|
flagExperimentalFeatures.insert(*f);
|
||||||
|
|
||||||
std::vector<std::string> args;
|
std::vector<std::string> args;
|
||||||
bool anyCompleted = false;
|
bool anyCompleted = false;
|
||||||
for (size_t n = 0 ; n < flag.handler.arity; ++n) {
|
for (size_t n = 0 ; n < flag.handler.arity; ++n) {
|
||||||
if (pos == end) {
|
if (pos == end) {
|
||||||
if (flag.handler.arity == ArityAny || anyCompleted) break;
|
if (flag.handler.arity == ArityAny || anyCompleted) break;
|
||||||
throw UsageError("flag '%s' requires %d argument(s)", name, flag.handler.arity);
|
throw UsageError(
|
||||||
|
"flag '%s' requires %d argument(s), but only %d were given",
|
||||||
|
name, flag.handler.arity, n);
|
||||||
}
|
}
|
||||||
if (auto prefix = needsCompletion(*pos)) {
|
if (auto prefix = needsCompletion(*pos)) {
|
||||||
anyCompleted = true;
|
anyCompleted = true;
|
||||||
|
@ -152,7 +164,11 @@ bool Args::processFlag(Strings::iterator & pos, Strings::iterator end)
|
||||||
for (auto & [name, flag] : longFlags) {
|
for (auto & [name, flag] : longFlags) {
|
||||||
if (!hiddenCategories.count(flag->category)
|
if (!hiddenCategories.count(flag->category)
|
||||||
&& hasPrefix(name, std::string(*prefix, 2)))
|
&& hasPrefix(name, std::string(*prefix, 2)))
|
||||||
|
{
|
||||||
|
if (auto & f = flag->experimentalFeature)
|
||||||
|
flagExperimentalFeatures.insert(*f);
|
||||||
completions->add("--" + name, flag->description);
|
completions->add("--" + name, flag->description);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -172,7 +188,8 @@ bool Args::processFlag(Strings::iterator & pos, Strings::iterator end)
|
||||||
if (prefix == "-") {
|
if (prefix == "-") {
|
||||||
completions->add("--");
|
completions->add("--");
|
||||||
for (auto & [flagName, flag] : shortFlags)
|
for (auto & [flagName, flag] : shortFlags)
|
||||||
completions->add(std::string("-") + flagName, flag->description);
|
if (experimentalFeatureSettings.isEnabled(flag->experimentalFeature))
|
||||||
|
completions->add(std::string("-") + flagName, flag->description);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -219,6 +236,8 @@ nlohmann::json Args::toJSON()
|
||||||
auto flags = nlohmann::json::object();
|
auto flags = nlohmann::json::object();
|
||||||
|
|
||||||
for (auto & [name, flag] : longFlags) {
|
for (auto & [name, flag] : longFlags) {
|
||||||
|
/* Skip experimental flags when listing flags. */
|
||||||
|
if (!experimentalFeatureSettings.isEnabled(flag->experimentalFeature)) continue;
|
||||||
auto j = nlohmann::json::object();
|
auto j = nlohmann::json::object();
|
||||||
if (flag->aliases.count(name)) continue;
|
if (flag->aliases.count(name)) continue;
|
||||||
if (flag->shortName)
|
if (flag->shortName)
|
||||||
|
|
|
@ -117,6 +117,8 @@ protected:
|
||||||
Handler handler;
|
Handler handler;
|
||||||
std::function<void(size_t, std::string_view)> completer;
|
std::function<void(size_t, std::string_view)> completer;
|
||||||
|
|
||||||
|
std::optional<ExperimentalFeature> experimentalFeature;
|
||||||
|
|
||||||
static Flag mkHashTypeFlag(std::string && longName, HashType * ht);
|
static Flag mkHashTypeFlag(std::string && longName, HashType * ht);
|
||||||
static Flag mkHashTypeOptFlag(std::string && longName, std::optional<HashType> * oht);
|
static Flag mkHashTypeOptFlag(std::string && longName, std::optional<HashType> * oht);
|
||||||
};
|
};
|
||||||
|
@ -188,6 +190,16 @@ public:
|
||||||
friend class MultiCommand;
|
friend class MultiCommand;
|
||||||
|
|
||||||
MultiCommand * parent = nullptr;
|
MultiCommand * parent = nullptr;
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Experimental features needed when parsing args. These are checked
|
||||||
|
* after flag parsing is completed in order to support enabling
|
||||||
|
* experimental features coming after the flag that needs the
|
||||||
|
* experimental feature.
|
||||||
|
*/
|
||||||
|
std::set<ExperimentalFeature> flagExperimentalFeatures;
|
||||||
};
|
};
|
||||||
|
|
||||||
/* A command is an argument parser that can be executed by calling its
|
/* A command is an argument parser that can be executed by calling its
|
||||||
|
@ -253,8 +265,6 @@ enum CompletionType {
|
||||||
};
|
};
|
||||||
extern CompletionType completionType;
|
extern CompletionType completionType;
|
||||||
|
|
||||||
std::optional<std::string> needsCompletion(std::string_view s);
|
|
||||||
|
|
||||||
void completePath(size_t, std::string_view prefix);
|
void completePath(size_t, std::string_view prefix);
|
||||||
|
|
||||||
void completeDir(size_t, std::string_view prefix);
|
void completeDir(size_t, std::string_view prefix);
|
||||||
|
|
|
@ -70,10 +70,17 @@ void AbstractConfig::reapplyUnknownSettings()
|
||||||
set(s.first, s.second);
|
set(s.first, s.second);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Whether we should process the option. Excludes aliases, which are handled elsewhere, and disabled features.
|
||||||
|
static bool applicable(const Config::SettingData & sd)
|
||||||
|
{
|
||||||
|
return !sd.isAlias
|
||||||
|
&& experimentalFeatureSettings.isEnabled(sd.setting->experimentalFeature);
|
||||||
|
}
|
||||||
|
|
||||||
void Config::getSettings(std::map<std::string, SettingInfo> & res, bool overriddenOnly)
|
void Config::getSettings(std::map<std::string, SettingInfo> & res, bool overriddenOnly)
|
||||||
{
|
{
|
||||||
for (auto & opt : _settings)
|
for (auto & opt : _settings)
|
||||||
if (!opt.second.isAlias && (!overriddenOnly || opt.second.setting->overridden))
|
if (applicable(opt.second) && (!overriddenOnly || opt.second.setting->overridden))
|
||||||
res.emplace(opt.first, SettingInfo{opt.second.setting->to_string(), opt.second.setting->description});
|
res.emplace(opt.first, SettingInfo{opt.second.setting->to_string(), opt.second.setting->description});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -147,9 +154,8 @@ nlohmann::json Config::toJSON()
|
||||||
{
|
{
|
||||||
auto res = nlohmann::json::object();
|
auto res = nlohmann::json::object();
|
||||||
for (auto & s : _settings)
|
for (auto & s : _settings)
|
||||||
if (!s.second.isAlias) {
|
if (applicable(s.second))
|
||||||
res.emplace(s.first, s.second.setting->toJSON());
|
res.emplace(s.first, s.second.setting->toJSON());
|
||||||
}
|
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -157,24 +163,31 @@ std::string Config::toKeyValue()
|
||||||
{
|
{
|
||||||
auto res = std::string();
|
auto res = std::string();
|
||||||
for (auto & s : _settings)
|
for (auto & s : _settings)
|
||||||
if (!s.second.isAlias) {
|
if (applicable(s.second))
|
||||||
res += fmt("%s = %s\n", s.first, s.second.setting->to_string());
|
res += fmt("%s = %s\n", s.first, s.second.setting->to_string());
|
||||||
}
|
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Config::convertToArgs(Args & args, const std::string & category)
|
void Config::convertToArgs(Args & args, const std::string & category)
|
||||||
{
|
{
|
||||||
for (auto & s : _settings)
|
for (auto & s : _settings) {
|
||||||
|
/* We do include args for settings gated on disabled
|
||||||
|
experimental-features. The args themselves however will also be
|
||||||
|
gated on any experimental feature the underlying setting is. */
|
||||||
if (!s.second.isAlias)
|
if (!s.second.isAlias)
|
||||||
s.second.setting->convertToArg(args, category);
|
s.second.setting->convertToArg(args, category);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
AbstractSetting::AbstractSetting(
|
AbstractSetting::AbstractSetting(
|
||||||
const std::string & name,
|
const std::string & name,
|
||||||
const std::string & description,
|
const std::string & description,
|
||||||
const std::set<std::string> & aliases)
|
const std::set<std::string> & aliases,
|
||||||
: name(name), description(stripIndentation(description)), aliases(aliases)
|
std::optional<ExperimentalFeature> experimentalFeature)
|
||||||
|
: name(name)
|
||||||
|
, description(stripIndentation(description))
|
||||||
|
, aliases(aliases)
|
||||||
|
, experimentalFeature(experimentalFeature)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -210,6 +223,7 @@ void BaseSetting<T>::convertToArg(Args & args, const std::string & category)
|
||||||
.category = category,
|
.category = category,
|
||||||
.labels = {"value"},
|
.labels = {"value"},
|
||||||
.handler = {[this](std::string s) { overridden = true; set(s); }},
|
.handler = {[this](std::string s) { overridden = true; set(s); }},
|
||||||
|
.experimentalFeature = experimentalFeature,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (isAppendable())
|
if (isAppendable())
|
||||||
|
@ -219,6 +233,7 @@ void BaseSetting<T>::convertToArg(Args & args, const std::string & category)
|
||||||
.category = category,
|
.category = category,
|
||||||
.labels = {"value"},
|
.labels = {"value"},
|
||||||
.handler = {[this](std::string s) { overridden = true; set(s, true); }},
|
.handler = {[this](std::string s) { overridden = true; set(s, true); }},
|
||||||
|
.experimentalFeature = experimentalFeature,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -270,13 +285,15 @@ template<> void BaseSetting<bool>::convertToArg(Args & args, const std::string &
|
||||||
.longName = name,
|
.longName = name,
|
||||||
.description = fmt("Enable the `%s` setting.", name),
|
.description = fmt("Enable the `%s` setting.", name),
|
||||||
.category = category,
|
.category = category,
|
||||||
.handler = {[this]() { override(true); }}
|
.handler = {[this]() { override(true); }},
|
||||||
|
.experimentalFeature = experimentalFeature,
|
||||||
});
|
});
|
||||||
args.addFlag({
|
args.addFlag({
|
||||||
.longName = "no-" + name,
|
.longName = "no-" + name,
|
||||||
.description = fmt("Disable the `%s` setting.", name),
|
.description = fmt("Disable the `%s` setting.", name),
|
||||||
.category = category,
|
.category = category,
|
||||||
.handler = {[this]() { override(false); }}
|
.handler = {[this]() { override(false); }},
|
||||||
|
.experimentalFeature = experimentalFeature,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -444,4 +461,30 @@ GlobalConfig::Register::Register(Config * config)
|
||||||
configRegistrations->emplace_back(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);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,12 +1,13 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <set>
|
#include <set>
|
||||||
|
|
||||||
#include "types.hh"
|
|
||||||
|
|
||||||
#include <nlohmann/json_fwd.hpp>
|
#include <nlohmann/json_fwd.hpp>
|
||||||
|
|
||||||
#pragma once
|
#include "types.hh"
|
||||||
|
#include "experimental-features.hh"
|
||||||
|
|
||||||
namespace nix {
|
namespace nix {
|
||||||
|
|
||||||
|
@ -194,12 +195,15 @@ public:
|
||||||
|
|
||||||
bool overridden = false;
|
bool overridden = false;
|
||||||
|
|
||||||
|
std::optional<ExperimentalFeature> experimentalFeature;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
||||||
AbstractSetting(
|
AbstractSetting(
|
||||||
const std::string & name,
|
const std::string & name,
|
||||||
const std::string & description,
|
const std::string & description,
|
||||||
const std::set<std::string> & aliases);
|
const std::set<std::string> & aliases,
|
||||||
|
std::optional<ExperimentalFeature> experimentalFeature = std::nullopt);
|
||||||
|
|
||||||
virtual ~AbstractSetting()
|
virtual ~AbstractSetting()
|
||||||
{
|
{
|
||||||
|
@ -240,8 +244,9 @@ public:
|
||||||
const bool documentDefault,
|
const bool documentDefault,
|
||||||
const std::string & name,
|
const std::string & name,
|
||||||
const std::string & description,
|
const std::string & description,
|
||||||
const std::set<std::string> & aliases = {})
|
const std::set<std::string> & aliases = {},
|
||||||
: AbstractSetting(name, description, aliases)
|
std::optional<ExperimentalFeature> experimentalFeature = std::nullopt)
|
||||||
|
: AbstractSetting(name, description, aliases, experimentalFeature)
|
||||||
, value(def)
|
, value(def)
|
||||||
, defaultValue(def)
|
, defaultValue(def)
|
||||||
, documentDefault(documentDefault)
|
, documentDefault(documentDefault)
|
||||||
|
@ -296,8 +301,9 @@ public:
|
||||||
const std::string & name,
|
const std::string & name,
|
||||||
const std::string & description,
|
const std::string & description,
|
||||||
const std::set<std::string> & aliases = {},
|
const std::set<std::string> & aliases = {},
|
||||||
const bool documentDefault = true)
|
const bool documentDefault = true,
|
||||||
: BaseSetting<T>(def, documentDefault, name, description, aliases)
|
std::optional<ExperimentalFeature> experimentalFeature = std::nullopt)
|
||||||
|
: BaseSetting<T>(def, documentDefault, name, description, aliases, experimentalFeature)
|
||||||
{
|
{
|
||||||
options->addSetting(this);
|
options->addSetting(this);
|
||||||
}
|
}
|
||||||
|
@ -357,4 +363,37 @@ struct GlobalConfig : public AbstractConfig
|
||||||
|
|
||||||
extern GlobalConfig globalConfig;
|
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;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -450,7 +450,6 @@ template<class C> Strings quoteStrings(const C & c)
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Remove trailing whitespace from a string. FIXME: return
|
/* Remove trailing whitespace from a string. FIXME: return
|
||||||
std::string_view. */
|
std::string_view. */
|
||||||
std::string chomp(std::string_view s);
|
std::string chomp(std::string_view s);
|
||||||
|
|
|
@ -440,7 +440,7 @@ static void main_nix_build(int argc, char * * argv)
|
||||||
shell = store->printStorePath(shellDrvOutputs.at("out").value()) + "/bin/bash";
|
shell = store->printStorePath(shellDrvOutputs.at("out").value()) + "/bin/bash";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (settings.isExperimentalFeatureEnabled(Xp::CaDerivations)) {
|
if (experimentalFeatureSettings.isEnabled(Xp::CaDerivations)) {
|
||||||
auto resolvedDrv = drv.tryResolve(*store);
|
auto resolvedDrv = drv.tryResolve(*store);
|
||||||
assert(resolvedDrv && "Successfully resolved the derivation");
|
assert(resolvedDrv && "Successfully resolved the derivation");
|
||||||
drv = *resolvedDrv;
|
drv = *resolvedDrv;
|
||||||
|
|
|
@ -208,7 +208,7 @@ static StorePath getDerivationEnvironment(ref<Store> store, ref<Store> evalStore
|
||||||
drv.name += "-env";
|
drv.name += "-env";
|
||||||
drv.env.emplace("name", drv.name);
|
drv.env.emplace("name", drv.name);
|
||||||
drv.inputSrcs.insert(std::move(getEnvShPath));
|
drv.inputSrcs.insert(std::move(getEnvShPath));
|
||||||
if (settings.isExperimentalFeatureEnabled(Xp::CaDerivations)) {
|
if (experimentalFeatureSettings.isEnabled(Xp::CaDerivations)) {
|
||||||
for (auto & output : drv.outputs) {
|
for (auto & output : drv.outputs) {
|
||||||
output.second = DerivationOutput::Deferred {},
|
output.second = DerivationOutput::Deferred {},
|
||||||
drv.env[output.first] = hashPlaceholder(output.first);
|
drv.env[output.first] = hashPlaceholder(output.first);
|
||||||
|
|
|
@ -1335,7 +1335,7 @@ struct CmdFlake : NixMultiCommand
|
||||||
{
|
{
|
||||||
if (!command)
|
if (!command)
|
||||||
throw UsageError("'nix flake' requires a sub-command.");
|
throw UsageError("'nix flake' requires a sub-command.");
|
||||||
settings.requireExperimentalFeature(Xp::Flakes);
|
experimentalFeatureSettings.require(Xp::Flakes);
|
||||||
command->second->run();
|
command->second->run();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -54,12 +54,11 @@ static bool haveInternet()
|
||||||
|
|
||||||
std::string programPath;
|
std::string programPath;
|
||||||
|
|
||||||
struct HelpRequested { };
|
|
||||||
|
|
||||||
struct NixArgs : virtual MultiCommand, virtual MixCommonArgs
|
struct NixArgs : virtual MultiCommand, virtual MixCommonArgs
|
||||||
{
|
{
|
||||||
bool useNet = true;
|
bool useNet = true;
|
||||||
bool refresh = false;
|
bool refresh = false;
|
||||||
|
bool helpRequested = false;
|
||||||
bool showVersion = false;
|
bool showVersion = false;
|
||||||
|
|
||||||
NixArgs() : MultiCommand(RegisterCommand::getCommandsFor({})), MixCommonArgs("nix")
|
NixArgs() : MultiCommand(RegisterCommand::getCommandsFor({})), MixCommonArgs("nix")
|
||||||
|
@ -74,7 +73,7 @@ struct NixArgs : virtual MultiCommand, virtual MixCommonArgs
|
||||||
.longName = "help",
|
.longName = "help",
|
||||||
.description = "Show usage information.",
|
.description = "Show usage information.",
|
||||||
.category = miscCategory,
|
.category = miscCategory,
|
||||||
.handler = {[&]() { throw HelpRequested(); }},
|
.handler = {[this]() { this->helpRequested = true; }},
|
||||||
});
|
});
|
||||||
|
|
||||||
addFlag({
|
addFlag({
|
||||||
|
@ -297,7 +296,10 @@ void mainWrapped(int argc, char * * argv)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (argc == 2 && std::string(argv[1]) == "__dump-builtins") {
|
if (argc == 2 && std::string(argv[1]) == "__dump-builtins") {
|
||||||
settings.experimentalFeatures = {Xp::Flakes, Xp::FetchClosure};
|
experimentalFeatureSettings.experimentalFeatures = {
|
||||||
|
Xp::Flakes,
|
||||||
|
Xp::FetchClosure,
|
||||||
|
};
|
||||||
evalSettings.pureEval = false;
|
evalSettings.pureEval = false;
|
||||||
EvalState state({}, openStore("dummy://"));
|
EvalState state({}, openStore("dummy://"));
|
||||||
auto res = nlohmann::json::object();
|
auto res = nlohmann::json::object();
|
||||||
|
@ -334,7 +336,11 @@ void mainWrapped(int argc, char * * argv)
|
||||||
|
|
||||||
try {
|
try {
|
||||||
args.parseCmdline(argvToStrings(argc, argv));
|
args.parseCmdline(argvToStrings(argc, argv));
|
||||||
} catch (HelpRequested &) {
|
} catch (UsageError &) {
|
||||||
|
if (!args.helpRequested && !completions) throw;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (args.helpRequested) {
|
||||||
std::vector<std::string> subcommand;
|
std::vector<std::string> subcommand;
|
||||||
MultiCommand * command = &args;
|
MultiCommand * command = &args;
|
||||||
while (command) {
|
while (command) {
|
||||||
|
@ -346,8 +352,6 @@ void mainWrapped(int argc, char * * argv)
|
||||||
}
|
}
|
||||||
showHelp(subcommand, args);
|
showHelp(subcommand, args);
|
||||||
return;
|
return;
|
||||||
} catch (UsageError &) {
|
|
||||||
if (!completions) throw;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (completions) {
|
if (completions) {
|
||||||
|
@ -366,7 +370,7 @@ void mainWrapped(int argc, char * * argv)
|
||||||
if (args.command->first != "repl"
|
if (args.command->first != "repl"
|
||||||
&& args.command->first != "doctor"
|
&& args.command->first != "doctor"
|
||||||
&& args.command->first != "upgrade-nix")
|
&& args.command->first != "upgrade-nix")
|
||||||
settings.requireExperimentalFeature(Xp::NixCommand);
|
experimentalFeatureSettings.require(Xp::NixCommand);
|
||||||
|
|
||||||
if (args.useNet && !haveInternet()) {
|
if (args.useNet && !haveInternet()) {
|
||||||
warn("you don't have Internet access; disabling some network-dependent features");
|
warn("you don't have Internet access; disabling some network-dependent features");
|
||||||
|
|
|
@ -45,7 +45,7 @@ struct CmdRealisationInfo : BuiltPathsCommand, MixJSON
|
||||||
|
|
||||||
void run(ref<Store> store, BuiltPaths && paths) override
|
void run(ref<Store> store, BuiltPaths && paths) override
|
||||||
{
|
{
|
||||||
settings.requireExperimentalFeature(Xp::CaDerivations);
|
experimentalFeatureSettings.require(Xp::CaDerivations);
|
||||||
RealisedPath::Set realisations;
|
RealisedPath::Set realisations;
|
||||||
|
|
||||||
for (auto & builtPath : paths) {
|
for (auto & builtPath : paths) {
|
||||||
|
|
|
@ -224,7 +224,7 @@ struct CmdRegistry : virtual NixMultiCommand
|
||||||
|
|
||||||
void run() override
|
void run() override
|
||||||
{
|
{
|
||||||
settings.requireExperimentalFeature(Xp::Flakes);
|
experimentalFeatureSettings.require(Xp::Flakes);
|
||||||
if (!command)
|
if (!command)
|
||||||
throw UsageError("'nix registry' requires a sub-command.");
|
throw UsageError("'nix registry' requires a sub-command.");
|
||||||
command->second->run();
|
command->second->run();
|
||||||
|
|
|
@ -38,7 +38,7 @@ struct CmdRepl : RawInstallablesCommand
|
||||||
|
|
||||||
void applyDefaultInstallables(std::vector<std::string> & rawInstallables) override
|
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");
|
warn("future versions of Nix will require using `--file` to load a file");
|
||||||
if (rawInstallables.size() > 1)
|
if (rawInstallables.size() > 1)
|
||||||
warn("more than one input file is not currently supported");
|
warn("more than one input file is not currently supported");
|
||||||
|
|
23
tests/experimental-features.sh
Normal file
23
tests/experimental-features.sh
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
source common.sh
|
||||||
|
|
||||||
|
# Without flakes, flake options should not show up
|
||||||
|
# With flakes, flake options should show up
|
||||||
|
|
||||||
|
function both_ways {
|
||||||
|
nix --experimental-features 'nix-command' "$@" | grepQuietInverse flake
|
||||||
|
nix --experimental-features 'nix-command flakes' "$@" | grepQuiet flake
|
||||||
|
|
||||||
|
# Also, the order should not matter
|
||||||
|
nix "$@" --experimental-features 'nix-command' | grepQuietInverse flake
|
||||||
|
nix "$@" --experimental-features 'nix-command flakes' | grepQuiet flake
|
||||||
|
}
|
||||||
|
|
||||||
|
# Simple case, the configuration effects the running command
|
||||||
|
both_ways show-config
|
||||||
|
|
||||||
|
# Complicated case, earlier args effect later args
|
||||||
|
|
||||||
|
both_ways store gc --help
|
||||||
|
|
||||||
|
expect 1 nix --experimental-features 'nix-command' show-config --flake-registry 'https://no'
|
||||||
|
nix --experimental-features 'nix-command flakes' show-config --flake-registry 'https://no'
|
|
@ -18,6 +18,7 @@ nix_tests = \
|
||||||
gc.sh \
|
gc.sh \
|
||||||
remote-store.sh \
|
remote-store.sh \
|
||||||
lang.sh \
|
lang.sh \
|
||||||
|
experimental-features.sh \
|
||||||
fetchMercurial.sh \
|
fetchMercurial.sh \
|
||||||
gc-auto.sh \
|
gc-auto.sh \
|
||||||
user-envs.sh \
|
user-envs.sh \
|
||||||
|
|
Loading…
Reference in a new issue