summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--nixos/doc/manual/man-nixos-option.xml21
-rw-r--r--nixos/doc/manual/release-notes/rl-2003.xml2
-rw-r--r--nixos/modules/installer/tools/nixos-option/nixos-option.cc251
3 files changed, 151 insertions, 123 deletions
diff --git a/nixos/doc/manual/man-nixos-option.xml b/nixos/doc/manual/man-nixos-option.xml
index b82f31256099..b921386d0df0 100644
--- a/nixos/doc/manual/man-nixos-option.xml
+++ b/nixos/doc/manual/man-nixos-option.xml
@@ -14,12 +14,16 @@
<refsynopsisdiv>
<cmdsynopsis>
<command>nixos-option</command>
+
<arg>
- <option>-I</option> <replaceable>path</replaceable>
+ <group choice='req'>
+ <arg choice='plain'><option>-r</option></arg>
+ <arg choice='plain'><option>--recursive</option></arg>
+ </group>
</arg>
<arg>
- <option>--all</option>
+ <option>-I</option> <replaceable>path</replaceable>
</arg>
<arg>
@@ -46,23 +50,22 @@
</para>
<variablelist>
<varlistentry>
- <term>
- <option>-I</option> <replaceable>path</replaceable>
- </term>
+ <term><option>-r</option></term>
+ <term><option>--recursive</option></term>
<listitem>
<para>
- This option is passed to the underlying
- <command>nix-instantiate</command> invocation.
+ Print all the values at or below the specified path recursively.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>
- <option>--all</option>
+ <option>-I</option> <replaceable>path</replaceable>
</term>
<listitem>
<para>
- Print the values of all options.
+ This option is passed to the underlying
+ <command>nix-instantiate</command> invocation.
</para>
</listitem>
</varlistentry>
diff --git a/nixos/doc/manual/release-notes/rl-2003.xml b/nixos/doc/manual/release-notes/rl-2003.xml
index 4ee9ebd6a2b4..8883c99d4b70 100644
--- a/nixos/doc/manual/release-notes/rl-2003.xml
+++ b/nixos/doc/manual/release-notes/rl-2003.xml
@@ -59,7 +59,7 @@
<listitem>
<para>
<command>nixos-option</command> has been rewritten in C++, speeding it up, improving correctness,
- and adding a <option>--all</option> option which prints all options and their values.
+ and adding a <option>-r</option> option which prints all options and their values recursively.
</para>
</listitem>
<listitem>
diff --git a/nixos/modules/installer/tools/nixos-option/nixos-option.cc b/nixos/modules/installer/tools/nixos-option/nixos-option.cc
index 9b92dc829cd1..1a7b07a74f8a 100644
--- a/nixos/modules/installer/tools/nixos-option/nixos-option.cc
+++ b/nixos/modules/installer/tools/nixos-option/nixos-option.cc
@@ -131,12 +131,12 @@ bool isOption(Context & ctx, const Value & v)
if (v.type != tAttrs) {
return false;
}
- const auto & atualType = v.attrs->find(ctx.underscoreType);
- if (atualType == v.attrs->end()) {
+ const auto & actualType = v.attrs->find(ctx.underscoreType);
+ if (actualType == v.attrs->end()) {
return false;
}
try {
- Value evaluatedType = evaluateValue(ctx, *atualType->value);
+ Value evaluatedType = evaluateValue(ctx, *actualType->value);
if (evaluatedType.type != tString) {
return false;
}
@@ -197,9 +197,107 @@ void recurse(const std::function<bool(const std::string & path, std::variant<Val
}
}
-// Calls f on all the option names
-void mapOptions(const std::function<void(const std::string & path)> & f, Context & ctx, Value root)
+bool optionTypeIs(Context & ctx, Value & v, const std::string & soughtType)
{
+ try {
+ const auto & typeLookup = v.attrs->find(ctx.state.sType);
+ if (typeLookup == v.attrs->end()) {
+ return false;
+ }
+ Value type = evaluateValue(ctx, *typeLookup->value);
+ if (type.type != tAttrs) {
+ return false;
+ }
+ const auto & nameLookup = type.attrs->find(ctx.state.sName);
+ if (nameLookup == type.attrs->end()) {
+ return false;
+ }
+ Value name = evaluateValue(ctx, *nameLookup->value);
+ if (name.type != tString) {
+ return false;
+ }
+ return name.string.s == soughtType;
+ } catch (Error &) {
+ return false;
+ }
+}
+
+bool isAggregateOptionType(Context & ctx, Value & v)
+{
+ return optionTypeIs(ctx, v, "attrsOf") || optionTypeIs(ctx, v, "listOf") || optionTypeIs(ctx, v, "loaOf");
+}
+
+MakeError(OptionPathError, EvalError);
+
+Value getSubOptions(Context & ctx, Value & option)
+{
+ Value getSubOptions = evaluateValue(ctx, *findAlongAttrPath(ctx.state, "type.getSubOptions", ctx.autoArgs, option));
+ if (getSubOptions.type != tLambda) {
+ throw OptionPathError("Option's type.getSubOptions isn't a function");
+ }
+ Value emptyString{};
+ nix::mkString(emptyString, "");
+ Value v;
+ ctx.state.callFunction(getSubOptions, emptyString, v, nix::Pos{});
+ return v;
+}
+
+// Carefully walk an option path, looking for sub-options when a path walks past
+// an option value.
+struct FindAlongOptionPathRet
+{
+ Value option;
+ std::string path;
+};
+FindAlongOptionPathRet findAlongOptionPath(Context & ctx, const std::string & path)
+{
+ Strings tokens = parseAttrPath(path);
+ Value v = ctx.optionsRoot;
+ std::string processedPath;
+ for (auto i = tokens.begin(); i != tokens.end(); i++) {
+ const auto & attr = *i;
+ try {
+ bool lastAttribute = std::next(i) == tokens.end();
+ v = evaluateValue(ctx, v);
+ if (attr.empty()) {
+ throw OptionPathError("empty attribute name");
+ }
+ if (isOption(ctx, v) && optionTypeIs(ctx, v, "submodule")) {
+ v = getSubOptions(ctx, v);
+ }
+ if (isOption(ctx, v) && isAggregateOptionType(ctx, v)) {
+ auto subOptions = getSubOptions(ctx, v);
+ if (lastAttribute && subOptions.attrs->empty()) {
+ break;
+ }
+ v = subOptions;
+ // Note that we've consumed attr, but didn't actually use it. This is the path component that's looked
+ // up in the list or attribute set that doesn't name an option -- the "root" in "users.users.root.name".
+ } else if (v.type != tAttrs) {
+ throw OptionPathError("Value is %s while a set was expected", showType(v));
+ } else {
+ const auto & next = v.attrs->find(ctx.state.symbols.create(attr));
+ if (next == v.attrs->end()) {
+ throw OptionPathError("Attribute not found", attr, path);
+ }
+ v = *next->value;
+ }
+ processedPath = appendPath(processedPath, attr);
+ } catch (OptionPathError & e) {
+ throw OptionPathError("At '%s' in path '%s': %s", attr, path, e.msg());
+ }
+ }
+ return {v, processedPath};
+}
+
+// Calls f on all the option names at or below the option described by `path`.
+// Note that "the option described by `path`" is not trivial -- if path describes a value inside an aggregate
+// option (such as users.users.root), the *option* described by that path is one path component shorter
+// (eg: users.users), which results in f being called on sibling-paths (eg: users.users.nixbld1). If f
+// doesn't want these, it must do its own filtering.
+void mapOptions(const std::function<void(const std::string & path)> & f, Context & ctx, const std::string & path)
+{
+ auto root = findAlongOptionPath(ctx, path);
recurse(
[f, &ctx](const std::string & path, std::variant<Value, std::exception_ptr> v) {
bool isOpt = std::holds_alternative<std::exception_ptr>(v) || isOption(ctx, std::get<Value>(v));
@@ -208,7 +306,7 @@ void mapOptions(const std::function<void(const std::string & path)> & f, Context
}
return !isOpt;
},
- ctx, root, "");
+ ctx, root.option, root.path);
}
// Calls f on all the config values inside one option.
@@ -294,9 +392,11 @@ void printAttrs(Context & ctx, Out & out, Value & v, const std::string & path)
Out attrsOut(out, "{", "}", v.attrs->size());
for (const auto & a : v.attrs->lexicographicOrder()) {
std::string name = a->name;
- attrsOut << name << " = ";
- printValue(ctx, attrsOut, *a->value, appendPath(path, name));
- attrsOut << ";" << Out::sep;
+ if (!forbiddenRecursionName(name)) {
+ attrsOut << name << " = ";
+ printValue(ctx, attrsOut, *a->value, appendPath(path, name));
+ attrsOut << ";" << Out::sep;
+ }
}
}
@@ -380,17 +480,26 @@ void printConfigValue(Context & ctx, Out & out, const std::string & path, std::v
out << ";\n";
}
-void printAll(Context & ctx, Out & out)
+// Replace with std::starts_with when C++20 is available
+bool starts_with(const std::string & s, const std::string & prefix)
+{
+ return s.size() >= prefix.size() &&
+ std::equal(s.begin(), std::next(s.begin(), prefix.size()), prefix.begin(), prefix.end());
+}
+
+void printRecursive(Context & ctx, Out & out, const std::string & path)
{
mapOptions(
- [&ctx, &out](const std::string & optionPath) {
+ [&ctx, &out, &path](const std::string & optionPath) {
mapConfigValuesInOption(
- [&ctx, &out](const std::string & configPath, std::variant<Value, std::exception_ptr> v) {
- printConfigValue(ctx, out, configPath, v);
+ [&ctx, &out, &path](const std::string & configPath, std::variant<Value, std::exception_ptr> v) {
+ if (starts_with(configPath, path)) {
+ printConfigValue(ctx, out, configPath, v);
+ }
},
optionPath, ctx);
},
- ctx, ctx.optionsRoot);
+ ctx, path);
}
void printAttr(Context & ctx, Out & out, const std::string & path, Value & root)
@@ -450,95 +559,17 @@ void printListing(Out & out, Value & v)
}
}
-bool optionTypeIs(Context & ctx, Value & v, const std::string & soughtType)
-{
- try {
- const auto & typeLookup = v.attrs->find(ctx.state.sType);
- if (typeLookup == v.attrs->end()) {
- return false;
- }
- Value type = evaluateValue(ctx, *typeLookup->value);
- if (type.type != tAttrs) {
- return false;
- }
- const auto & nameLookup = type.attrs->find(ctx.state.sName);
- if (nameLookup == type.attrs->end()) {
- return false;
- }
- Value name = evaluateValue(ctx, *nameLookup->value);
- if (name.type != tString) {
- return false;
- }
- return name.string.s == soughtType;
- } catch (Error &) {
- return false;
- }
-}
-
-bool isAggregateOptionType(Context & ctx, Value & v)
-{
- return optionTypeIs(ctx, v, "attrsOf") || optionTypeIs(ctx, v, "listOf") || optionTypeIs(ctx, v, "loaOf");
-}
-
-MakeError(OptionPathError, EvalError);
-
-Value getSubOptions(Context & ctx, Value & option)
-{
- Value getSubOptions = evaluateValue(ctx, *findAlongAttrPath(ctx.state, "type.getSubOptions", ctx.autoArgs, option));
- if (getSubOptions.type != tLambda) {
- throw OptionPathError("Option's type.getSubOptions isn't a function");
- }
- Value emptyString{};
- nix::mkString(emptyString, "");
- Value v;
- ctx.state.callFunction(getSubOptions, emptyString, v, nix::Pos{});
- return v;
-}
-
-// Carefully walk an option path, looking for sub-options when a path walks past
-// an option value.
-Value findAlongOptionPath(Context & ctx, const std::string & path)
-{
- Strings tokens = parseAttrPath(path);
- Value v = ctx.optionsRoot;
- for (auto i = tokens.begin(); i != tokens.end(); i++) {
- const auto & attr = *i;
- try {
- bool lastAttribute = std::next(i) == tokens.end();
- v = evaluateValue(ctx, v);
- if (attr.empty()) {
- throw OptionPathError("empty attribute name");
- }
- if (isOption(ctx, v) && optionTypeIs(ctx, v, "submodule")) {
- v = getSubOptions(ctx, v);
- }
- if (isOption(ctx, v) && isAggregateOptionType(ctx, v) && !lastAttribute) {
- v = getSubOptions(ctx, v);
- // Note that we've consumed attr, but didn't actually use it. This is the path component that's looked
- // up in the list or attribute set that doesn't name an option -- the "root" in "users.users.root.name".
- } else if (v.type != tAttrs) {
- throw OptionPathError("Value is %s while a set was expected", showType(v));
- } else {
- const auto & next = v.attrs->find(ctx.state.symbols.create(attr));
- if (next == v.attrs->end()) {
- throw OptionPathError("Attribute not found", attr, path);
- }
- v = *next->value;
- }
- } catch (OptionPathError & e) {
- throw OptionPathError("At '%s' in path '%s': %s", attr, path, e.msg());
- }
- }
- return v;
-}
-
void printOne(Context & ctx, Out & out, const std::string & path)
{
try {
- Value option = findAlongOptionPath(ctx, path);
+ auto result = findAlongOptionPath(ctx, path);
+ Value & option = result.option;
option = evaluateValue(ctx, option);
+ if (path != result.path) {
+ out << "Note: showing " << result.path << " instead of " << path << "\n";
+ }
if (isOption(ctx, option)) {
- printOption(ctx, out, path, option);
+ printOption(ctx, out, result.path, option);
} else {
printListing(out, option);
}
@@ -552,7 +583,7 @@ void printOne(Context & ctx, Out & out, const std::string & path)
int main(int argc, char ** argv)
{
- bool all = false;
+ bool recursive = false;
std::string path = ".";
std::string optionsExpr = "(import <nixpkgs/nixos> {}).options";
std::string configExpr = "(import <nixpkgs/nixos> {}).config";
@@ -568,8 +599,8 @@ int main(int argc, char ** argv)
nix::showManPage("nixos-option");
} else if (*arg == "--version") {
nix::printVersion("nixos-option");
- } else if (*arg == "--all") {
- all = true;
+ } else if (*arg == "-r" || *arg == "--recursive") {
+ recursive = true;
} else if (*arg == "--path") {
path = nix::getArg(*arg, arg, end);
} else if (*arg == "--options_expr") {
@@ -598,18 +629,12 @@ int main(int argc, char ** argv)
Context ctx{*state, *myArgs.getAutoArgs(*state), optionsRoot, configRoot};
Out out(std::cout);
- if (all) {
- if (!args.empty()) {
- throw UsageError("--all cannot be used with arguments");
- }
- printAll(ctx, out);
- } else {
- if (args.empty()) {
- printOne(ctx, out, "");
- }
- for (const auto & arg : args) {
- printOne(ctx, out, arg);
- }
+ auto print = recursive ? printRecursive : printOne;
+ if (args.empty()) {
+ print(ctx, out, "");
+ }
+ for (const auto & arg : args) {
+ print(ctx, out, arg);
}
ctx.state.printStats();