Change:
We can currently display a NixOS module option type using, for example: . It seems to me that, to maximize usability, should display this string. Also, the should display this information. Perhaps it should be toggleable, though, since options involving submodule types could be quite large. EDIT: Apparently the field is currently for any submodule type. It would be nice to either pretty-print the whole type or output some kind of structured data there. Run . There is no indication of the type of this option other than the default/example values. System: (NixOS: , Ubuntu/Fedora: , ...) Nix version: (run ) Nixpkgs version: (run ) Sandboxing enabled: (run )
cc
This was fixed in .
Thank you for your contributions. This has been automatically marked as stale because it has had no activity for 180 days. If this is still important to you, we ask that you leave a comment below. Your comment can be as simple as "still important to me". This lets people see that at least one person still cares about this. Someone will have to do this at most twice a year if there is no other activity. Here are suggestions that might help resolve this more quickly: for maintainers and people that previously touched the related code and @ mention them in a comment. on the . on the on .

Discussion:
</arg> <arg> <del> <option>--verbose</option> </del> <ins> <option>--all</option> </ins> </arg> <arg> <del> <option>--xml</option> </arg> <arg choice="plain"> </del> <replaceable>option.name</replaceable> </arg> </cmdsynopsis>

</varlistentry> <varlistentry> <term> <del> <option>--verbose</option> </term> <listitem> <para> This option enables verbose mode, which currently is just the Bash <command>set</command> <option>-x</option> debug mode. </para> </listitem> </varlistentry> <varlistentry> <term> <option>--xml</option> </del> <ins> <option>--all</option> </ins> </term> <listitem> <para> <del> This option causes the output to be rendered as XML. </del> <ins> Print the values of all options. </ins> </para> </listitem> </varlistentry>

<del> #! @shell@ -e # FIXME: rewrite this in a more suitable language. usage () { exec man nixos-option exit 1 } ##################### # Process Arguments # ##################### xml=false verbose=false nixPath="" option="" exit_code=0 argfun="" for arg; do if test -z "$argfun"; then case $arg in -*) sarg="$arg" longarg="" while test "$sarg" != "-"; do case $sarg in --*) longarg=$arg; sarg="--";; -I) argfun="include_nixpath";; -*) usage;; esac # remove the first letter option sarg="-${sarg#??}" done ;; *) longarg=$arg;; esac for larg in $longarg; do case $larg in --xml) xml=true;; --verbose) verbose=true;; --help) usage;; -*) usage;; *) if test -z "$option"; then option="$larg" else usage fi;; esac done else case $argfun in set_*) var=$(echo $argfun | sed 's,^set_,,') eval $var=$arg ;; include_nixpath) nixPath="-I $arg $nixPath" ;; esac argfun="" fi done if $verbose; then set -x else set +x fi ############################# # Process the configuration # ############################# evalNix(){ # disable `-e` flag, it's possible that the evaluation of `nix-instantiate` fails (e.g. due to broken pkgs) set +e result=$(nix-instantiate ${nixPath:+$nixPath} - --eval-only "$@" 2>&1) exit_code=$? set -e if test $exit_code -eq 0; then sed '/^warning: Nix search path/d' <<EOF $result EOF return 0; else sed -n ' /^error/ { s/, at (string):[0-9]*:[0-9]*//; p; }; /^warning: Nix search path/ { p; }; ' >&2 <<EOF $result EOF exit_code=1 fi } header="let nixos = import <nixpkgs/nixos> {}; nixpkgs = import <nixpkgs> {}; in with nixpkgs.lib; " # This function is used for converting the option definition path given by # the user into accessors for reaching the definition and the declaration # corresponding to this option. generateAccessors(){ if result=$(evalNix --strict --show-trace <<EOF $header let path = "${option:+$option}"; pathList = splitString "." path; walkOptions = attrsNames: result: if attrsNames == [] then result else let name = head attrsNames; rest = tail attrsNames; in if isOption result.options then walkOptions rest { options = result.options.type.getSubOptions ""; opt = ''(${result.opt}.type.getSubOptions "")''; cfg = ''${result.cfg}."${name}"''; } else walkOptions rest { options = result.options.${name}; opt = ''${result.opt}."${name}"''; cfg = ''${result.cfg}."${name}"''; } ; walkResult = (if path == "" then x: x else walkOptions pathList) { options = nixos.options; opt = ''nixos.options''; cfg = ''nixos.config''; }; in ''let option = ${walkResult.opt}; config = ${walkResult.cfg}; in'' EOF ) then echo $result else # In case of error we want to ignore the error message roduced by the # script above, as it is iterating over each attribute, which does not # produce a nice error message.  The following code is a fallback # solution which is cause a nicer error message in the next # evaluation. echo ""let option = nixos.options${option:+.$option}; config = nixos.config${option:+.$option}; in"" fi } header="$header $(eval echo $(generateAccessors)) " evalAttr(){ local prefix="$1" local strict="$2" local suffix="$3" # If strict is set, then set it to "true". test -n "$strict" && strict=true evalNix ${strict:+--strict} <<EOF $header let value = $prefix${suffix:+.$suffix}; strict = ${strict:-false}; cleanOutput = x: with nixpkgs.lib; if isDerivation x then x.outPath else if isFunction x then "<CODE>" else if strict then if isAttrs x then mapAttrs (n: cleanOutput) x else if isList x then map cleanOutput x else x else x; in cleanOutput value EOF } evalOpt(){ evalAttr "option" "" "$@" } evalCfg(){ local strict="$1" evalAttr "config" "$strict" } findSources(){ local suffix=$1 evalNix --strict <<EOF $header option.$suffix EOF } # Given a result from nix-instantiate, recover the list of attributes it # contains. attrNames() { local attributeset=$1 # sed is used to replace un-printable subset by 0s, and to remove most of # the inner-attribute set, which reduce the likelyhood to encounter badly # pre-processed input. echo "builtins.attrNames $attributeset" |  sed 's,<[A-Z]*>,0,g; :inner; s/{[^{}]*};/0;/g; t inner;' |  evalNix --strict } # map a simple list which contains strings or paths. nixMap() { local fun="$1" local list="$2" local elem for elem in $list; do test $elem = '[' -o $elem = ']' && continue; $fun $elem done } # This duplicates the work made below, but it is useful for processing # the output of nixos-option with other tools such as nixos-gui. if $xml; then evalNix --xml --no-location <<EOF $header let sources = builtins.map (f: f.source); opt = option; cfg = config; in with nixpkgs.lib; let optStrict = v: let traverse = x : if isAttrs x then if x ? outPath then true else all id (mapAttrsFlatten (n: traverseNoAttrs) x) else traverseNoAttrs x; traverseNoAttrs = x: # do not continue in attribute sets if isAttrs x then true else if isList x then all id (map traverse x) else true; in assert traverse v; v; in if isOption opt then optStrict ({} // optionalAttrs (opt ? default) { inherit (opt) default; } // optionalAttrs (opt ? example) { inherit (opt) example; } // optionalAttrs (opt ? description) { inherit (opt) description; } // optionalAttrs (opt ? type) { typename = opt.type.description; } // optionalAttrs (opt ? options) { inherit (opt) options; } // { # to disambiguate the xml output. _isOption = true; declarations = sources opt.declarations; definitions = sources opt.definitions; value = cfg; }) else opt EOF exit $? fi if test "$(evalOpt "_type" 2> /dev/null)" = '"option"'; then echo "Value:" evalCfg 1 echo echo "Default:" if default=$(evalOpt "default" - 2> /dev/null); then echo "$default" else echo "<None>" fi echo if example=$(evalOpt "example" - 2> /dev/null); then echo "Example:" echo "$example" echo fi echo "Description:" echo echo $(evalOpt "description") echo $desc; printPath () { echo "  $1"; } echo "Declared by:" nixMap printPath "$(findSources "declarations")" echo echo "Defined by:" nixMap printPath "$(findSources "files")" echo else # echo 1>&2 "Warning: This value is not an option." result=$(evalCfg "") if [ ! -z "$result" ]; then names=$(attrNames "$result" 2> /dev/null) echo 1>&2 "This attribute set contains:" escapeQuotes () { eval echo "$1"; } nixMap escapeQuotes "$names" else echo 1>&2 "An error occurred while looking for attribute names. Are you sure that '$option' exists?" fi fi exit $exit_code </del>

<ins> cmake_minimum_required (VERSION 2.6) project (nixos-option) add_executable(nixos-option nixos-option.cc libnix-copy-paste.cc) target_link_libraries(nixos-option PRIVATE -lnixmain -lnixexpr -lnixstore -lnixutil) target_compile_features(nixos-option PRIVATE cxx_std_17) install (TARGETS nixos-option DESTINATION bin) </ins>

<ins> {stdenv, boost, cmake, pkgconfig, nix, ... }: stdenv.mkDerivation rec { name = "nixos-option"; src = ./.; nativeBuildInputs = [ cmake pkgconfig ]; buildInputs = [ boost nix ]; meta = { license = stdenv.lib.licenses.lgpl2Plus; }; } </ins>

<ins> // These are useful methods inside the nix library that ought to be exported. // Since they are not, copy/paste them here. // TODO: Delete these and use the ones in the library as they become available. #include <nix/config.h> // for nix/globals.hh's reference to SYSTEM #include "libnix-copy-paste.hh" #include <boost/format/alt_sstream.hpp>           // for basic_altstringbuf... #include <boost/format/alt_sstream_impl.hpp>      // for basic_altstringbuf... #include <boost/format/format_class.hpp>          // for basic_format #include <boost/format/format_fwd.hpp>            // for format #include <boost/format/format_implementation.hpp> // for basic_format::basi... #include <boost/optional/optional.hpp>            // for get_pointer #include <iostream>                               // for operator<<, basic_... #include <nix/types.hh>                           // for Strings, Error #include <string>                                 // for string, basic_string using boost::format; using nix::Error; using nix::Strings; using std::string; // From nix/src/libexpr/attr-path.cc Strings parseAttrPath(const string & s) { Strings res; string cur; string::const_iterator i = s.begin(); while (i != s.end()) { if (*i == '.') { res.push_back(cur); cur.clear(); } else if (*i == '"') { ++i; while (1) { if (i == s.end()) throw Error(format("missing closing quote in selection path '%1%'") % s); if (*i == '"') break; cur.push_back(*i++); } } else cur.push_back(*i); ++i; } if (!cur.empty()) res.push_back(cur); return res; } // From nix/src/nix/repl.cc bool isVarName(const string & s) { if (s.size() == 0) return false; char c = s[0]; if ((c >= '0' && c <= '9') || c == '-' || c == ''') return false; for (auto & i : s) if (!((i >= 'a' && i <= 'z') || (i >= 'A' && i <= 'Z') || (i >= '0' && i <= '9') || i == '_' || i == '-' || i == ''')) return false; return true; } // From nix/src/nix/repl.cc std::ostream & printStringValue(std::ostream & str, const char * string) { str << """; for (const char * i = string; *i; i++) if (*i == '"' || *i == '') str << "" << *i; else if (*i == 'n') str << "n"; else if (*i == 'r') str << "r"; else if (*i == 't') str << "t"; else str << *i; str << """; return str; } </ins>

<ins> #pragma once #include <iostream> #include <nix/types.hh> #include <string> nix::Strings parseAttrPath(const std::string & s); bool isVarName(const std::string & s); std::ostream & printStringValue(std::ostream & str, const char * string); </ins>

<ins> #include <nix/config.h> // for nix/globals.hh's reference to SYSTEM #include <exception>               // for exception_ptr, current_exception #include <functional>              // for function #include <iostream>                // for operator<<, basic_ostream, ostrin... #include <iterator>                // for next #include <list>                    // for _List_iterator #include <memory>                  // for allocator, unique_ptr, make_unique #include <new>                     // for operator new #include <nix/args.hh>             // for argvToStrings, UsageError #include <nix/attr-path.hh>        // for findAlongAttrPath #include <nix/attr-set.hh>         // for Attr, Bindings, Bindings::iterator #include <nix/common-eval-args.hh> // for MixEvalArgs #include <nix/eval-inline.hh>      // for EvalState::forceValue #include <nix/eval.hh>             // for EvalState, initGC, operator<< #include <nix/globals.hh>          // for initPlugins, Settings, settings #include <nix/nixexpr.hh>          // for Pos #include <nix/shared.hh>           // for getArg, LegacyArgs, printVersion #include <nix/store-api.hh>        // for openStore #include <nix/symbol-table.hh>     // for Symbol, SymbolTable #include <nix/types.hh>            // for Error, Path, Strings, PathSet #include <nix/util.hh>             // for absPath, baseNameOf #include <nix/value.hh>            // for Value, Value::(anonymous), Value:... #include <string>                  // for string, operator+, operator== #include <utility>                 // for move #include <variant>                 // for get, holds_alternative, variant #include <vector>                  // for vector<>::iterator, vector #include "libnix-copy-paste.hh" using nix::absPath; using nix::Bindings; using nix::Error; using nix::EvalState; using nix::Path; using nix::PathSet; using nix::Strings; using nix::Symbol; using nix::tAttrs; using nix::tLambda; using nix::tString; using nix::UsageError; using nix::Value; // An ostream wrapper to handle nested indentation class Out { public: class Separator {}; const static Separator sep; enum LinePolicy { ONE_LINE, MULTI_LINE }; explicit Out(std::ostream & ostream) : ostream(ostream), policy(ONE_LINE), write_since_sep(true) {} Out(Out & o, std::string const & start, std::string const & end, LinePolicy policy); Out(Out & o, std::string const & start, std::string const & end, int count) : Out(o, start, end, count < 2 ? ONE_LINE : MULTI_LINE) {} Out(Out const &) = delete; Out(Out &&) = default; Out & operator=(Out const &) = delete; Out & operator=(Out &&) = delete; ~Out() { ostream << end; } private: std::ostream & ostream; std::string indentation; std::string end; LinePolicy policy; bool write_since_sep; template <typename T> friend Out & operator<<(Out & o, T thing); }; template <typename T> Out & operator<<(Out & o, T thing) { if (!o.write_since_sep && o.policy == Out::MULTI_LINE) { o.ostream << o.indentation; } o.write_since_sep = true; o.ostream << thing; return o; } template <> Out & operator<<<Out::Separator>(Out & o, Out::Separator /* thing */) { o.ostream << (o.policy == Out::ONE_LINE ? " " : "n"); o.write_since_sep = false; return o; } Out::Out(Out & o, std::string const & start, std::string const & end, LinePolicy policy) : ostream(o.ostream), indentation(policy == ONE_LINE ? o.indentation : o.indentation + "  "), end(policy == ONE_LINE ? end : o.indentation + end), policy(policy), write_since_sep(true) { o << start; *this << Out::sep; } // Stuff needed for evaluation struct Context { Context(EvalState * state, Bindings * autoArgs, Value options_root, Value config_root) : state(state), autoArgs(autoArgs), options_root(options_root), config_root(config_root), underscore_type(state->symbols.create("_type")) {} EvalState * state; Bindings * autoArgs; Value options_root; Value config_root; Symbol underscore_type; }; Value evaluateValue(Context * ctx, Value * v) { ctx->state->forceValue(*v); if (ctx->autoArgs->empty()) { return *v; } Value called{}; ctx->state->autoCallFunction(*ctx->autoArgs, *v, called); return called; } bool isOption(Context * ctx, Value const & v) { if (v.type != tAttrs) { return false; } auto const & actual_type = v.attrs->find(ctx->underscore_type); if (actual_type == v.attrs->end()) { return false; } try { Value evaluated_type = evaluateValue(ctx, actual_type->value); if (evaluated_type.type != tString) { return false; } return evaluated_type.string.s == static_cast<std::string>("option"); } catch (Error &) { return false; } } // Add quotes to a component of a path. // These are needed for paths like: //    fileSystems."/".fsType //    systemd.units."dbus.service".text std::string quoteAttribute(std::string const & attribute) { if (isVarName(attribute)) { return attribute; } std::ostringstream buf; printStringValue(buf, attribute.c_str()); return buf.str(); } std::string const appendPath(std::string const & prefix, std::string const & suffix) { if (prefix.empty()) { return quoteAttribute(suffix); } return prefix + "." + quoteAttribute(suffix); } bool forbiddenRecursionName(std::string name) { return (!name.empty() && name[0] == '_') || name == "haskellPackages"; } void recurse(const std::function<bool(std::string const & path, std::variant<Value, std::exception_ptr>)> & f, Context * ctx, Value v, std::string const & path) { std::variant<Value, std::exception_ptr> evaluated; try { evaluated = evaluateValue(ctx, &v); } catch (Error &) { evaluated = std::current_exception(); } if (!f(path, evaluated)) { return; } if (std::holds_alternative<std::exception_ptr>(evaluated)) { return; } Value const & evaluated_value = std::get<Value>(evaluated); if (evaluated_value.type != tAttrs) { return; } for (auto const & child : evaluated_value.attrs->lexicographicOrder()) { if (forbiddenRecursionName(child->name)) { continue; } recurse(f, ctx, *child->value, appendPath(path, child->name)); } } // Calls f on all the option names void mapOptions(const std::function<void(std::string const & path)> & f, Context * ctx, Value root) { recurse( [f, ctx](std::string const & path, std::variant<Value, std::exception_ptr> v) { bool isOpt = std::holds_alternative<std::exception_ptr>(v) || isOption(ctx, std::get<Value>(v)); if (isOpt) { f(path); } return !isOpt; }, ctx, root, ""); } // Calls f on all the config values inside one option. // Simple options have one config value inside, like sound.enable = true. // Compound options have multiple config values.  For example, the option // "users.users" has about 1000 config values inside it: //   users.users.avahi.createHome = false; //   users.users.avahi.cryptHomeLuks = null; //   users.users.avahi.description = "`avahi-daemon' privilege separation user"; //   ... //   users.users.avahi.openssh.authorizedKeys.keyFiles = [ ]; //   users.users.avahi.openssh.authorizedKeys.keys = [ ]; //   ... //   users.users.avahi.uid = 10; //   users.users.avahi.useDefaultShell = false; //   users.users.cups.createHome = false; //   ... //   users.users.cups.useDefaultShell = false; //   users.users.gdm = ... ... ... //   users.users.messagebus = ... .. ... //   users.users.nixbld1 = ... .. ... //   ... //   users.users.systemd-timesync = ... .. ... void mapConfigValuesInOption(const std::function<void(std::string const & path, std::variant<Value, std::exception_ptr> v)> & f, std::string const & path, Context * ctx) { Value * option; try { option = findAlongAttrPath(*ctx->state, path, *ctx->autoArgs, ctx->config_root); } catch (Error &) { f(path, std::current_exception()); return; } recurse( [f, ctx](std::string const & path, std::variant<Value, std::exception_ptr> v) { bool leaf = std::holds_alternative<std::exception_ptr>(v) || std::get<Value>(v).type != tAttrs || ctx->state->isDerivation(std::get<Value>(v)); if (!leaf) { return true; // Keep digging } f(path, v); return false; }, ctx, *option, path); } std::string describeError(Error const & e) { return "«error: " + e.msg() + "»"; } void describeDerivation(Context * ctx, Out & out, Value v) { // Copy-pasted from nix/src/nix/repl.cc  :( Bindings::iterator i = v.attrs->find(ctx->state->sDrvPath); PathSet pathset; try { Path drvPath = i != v.attrs->end() ? ctx->state->coerceToPath(*i->pos, *i->value, pathset) : "???"; out << "«derivation " << drvPath << "»"; } catch (Error & e) { out << describeError(e); } } Value parseAndEval(EvalState * state, std::string const & expression, std::string const & path) { Value v{}; state->eval(state->parseExprFromString(expression, absPath(path)), v); return v; } void printValue(Context * ctx, Out & out, std::variant<Value, std::exception_ptr> maybe_value, std::string const & path); void printList(Context * ctx, Out & out, Value & v) { Out list_out(out, "[", "]", v.listSize()); for (unsigned int n = 0; n < v.listSize(); ++n) { printValue(ctx, list_out, *v.listElems()[n], ""); list_out << Out::sep; } } void printAttrs(Context * ctx, Out & out, Value & v, std::string const & path) { Out attrs_out(out, "{", "}", v.attrs->size()); for (const auto & a : v.attrs->lexicographicOrder()) { std::string name = a->name; attrs_out << name << " = "; printValue(ctx, attrs_out, *a->value, appendPath(path, name)); attrs_out << ";" << Out::sep; } } void multiLineStringEscape(Out & out, std::string const & s) { int i; for (i = 1; i < s.size(); i++) { if (s[i - 1] == '$' && s[i] == '{') { out << "''${"; i++; } else if (s[i - 1] == ''' && s[i] == ''') { out << "'''"; i++; } else { out << s[i - 1]; } } if (i == s.size()) { out << s[i - 1]; } } void printMultiLineString(Out & out, Value const & v) { std::string s = v.string.s; Out str_out(out, "''", "''", Out::MULTI_LINE); std::string::size_type begin = 0; while (begin < s.size()) { std::string::size_type end = s.find('n', begin); if (end == std::string::npos) { multiLineStringEscape(str_out, s.substr(begin, s.size() - begin)); break; } multiLineStringEscape(str_out, s.substr(begin, end - begin)); str_out << Out::sep; begin = end + 1; } } void printValue(Context * ctx, Out & out, std::variant<Value, std::exception_ptr> maybe_value, std::string const & path) { try { if (std::holds_alternative<std::exception_ptr>(maybe_value)) { std::rethrow_exception(std::get<std::exception_ptr>(maybe_value)); } Value v = evaluateValue(ctx, &std::get<Value>(maybe_value)); if (ctx->state->isDerivation(v)) { describeDerivation(ctx, out, v); } else if (v.isList()) { printList(ctx, out, v); } else if (v.type == tAttrs) { printAttrs(ctx, out, v, path); } else if (v.type == tString && std::string(v.string.s).find('n') != std::string::npos) { printMultiLineString(out, v); } else { ctx->state->forceValueDeep(v); out << v; } } catch (Error & e) { if (e.msg() == "The option `" + path + "' is used but not defined.") { // 93% of errors are this, and just letting this message through would be // misleading.  These values may or may not actually be "used" in the // config.  The thing throwing the error message assumes that if anything // ever looks at this value, it is a "use" of this value.  But here in // nixos-option, we are looking at this value only to print it. // In order to avoid implying that this undefined value is actually // referenced, eat the underlying error message and emit "«not defined»". out << "«not defined»"; } else { out << describeError(e); } } } void printConfigValue(Context * ctx, Out & out, std::string const & path, std::variant<Value, std::exception_ptr> v) { out << path << " = "; printValue(ctx, out, std::move(v), path); out << ";n"; } void printAll(Context * ctx, Out & out) { mapOptions( [ctx, &out](std::string const & option_path) { mapConfigValuesInOption( [ctx, &out](std::string const & config_path, std::variant<Value, std::exception_ptr> v) { printConfigValue(ctx, out, config_path, v); }, option_path, ctx); }, ctx, ctx->options_root); } void printAttr(Context * ctx, Out & out, std::string const & path, Value * root) { try { printValue(ctx, out, *findAlongAttrPath(*ctx->state, path, *ctx->autoArgs, *root), path); } catch (Error & e) { out << describeError(e); } } void printOption(Context * ctx, Out & out, std::string const & path, Value * option) { out << "Value:n"; printAttr(ctx, out, path, &ctx->config_root); out << "nnDefault:n"; printAttr(ctx, out, "default", option); out << "nnType:n"; printAttr(ctx, out, "type.description", option); out << "nnExample:n"; printAttr(ctx, out, "example", option); out << "nnDescription:n"; printAttr(ctx, out, "description", option); out << "nnDeclared by:n"; printAttr(ctx, out, "declarations", option); out << "nnDefined by:n"; printAttr(ctx, out, "files", option); out << "n"; } void printListing(Out & out, Value * v) { // Print this header on stderr rather than stdout because the old shell script // implementation did.  I don't know why. std::cerr << "This attribute set contains:n"; for (const auto & a : v->attrs->lexicographicOrder()) { std::string name = a->name; if (!name.empty() && name[0] != '_') { out << name << "n"; } } } // Carefully walk an option path, looking for sub-options when a path walks past // an option value. Value findAlongOptionPath(Context * ctx, std::string const & path) { Strings tokens = parseAttrPath(path); Value v = ctx->options_root; for (auto i = tokens.begin(); i != tokens.end(); i++) { bool last_attribute = std::next(i) == tokens.end(); auto const & attr = *i; v = evaluateValue(ctx, &v); if (attr.empty()) { throw Error("empty attribute name in selection path '%s'", path); } if (isOption(ctx, v) && !last_attribute) { Value getSubOptions = evaluateValue(ctx, findAlongAttrPath(*ctx->state, "type.getSubOptions", *ctx->autoArgs, v)); if (getSubOptions.type != tLambda) { throw Error("Option's type.getSubOptions isn't a function at '%s' in path '%s'", attr, path); } Value emptyString{}; nix::mkString(emptyString, ""); ctx->state->callFunction(getSubOptions, emptyString, v, nix::Pos{}); // Note that we've consumed attr, but didn't actually use it. } else if (v.type != tAttrs) { throw Error("attribute '%s' in path '%s' attempts to index a value that should be a set but is %s", attr, path, showType(v)); } else { auto const & next = v.attrs->find(ctx->state->symbols.create(attr)); if (next == v.attrs->end()) { throw Error("attribute '%s' in path '%s' not found", attr, path); } v = *next->value; } } return v; } void printOne(Context * ctx, Out & out, std::string const & path) { try { Value option = findAlongOptionPath(ctx, path); option = evaluateValue(ctx, &option); if (isOption(ctx, option)) { printOption(ctx, out, path, &option); } else { printListing(out, &option); } } catch (Error & e) { std::cerr << "error: " << e.msg() << "nAn error occurred while looking for attribute names. Are " "you sure that '" << path << "' exists?n"; } } int main(int argc, char ** argv) { bool all = false; std::string path = "."; std::string options_expr = "(import <nixpkgs/nixos> {}).options"; std::string config_expr = "(import <nixpkgs/nixos> {}).config"; std::vector<std::string> args; struct MyArgs : nix::LegacyArgs, nix::MixEvalArgs { using nix::LegacyArgs::LegacyArgs; }; MyArgs myArgs(nix::baseNameOf(argv[0]), [&](Strings::iterator & arg, const Strings::iterator & end) { if (*arg == "--help") { nix::showManPage("nixos-option"); } else if (*arg == "--version") { nix::printVersion("nixos-option"); } else if (*arg == "--all") { all = true; } else if (*arg == "--path") { path = nix::getArg(*arg, arg, end); } else if (*arg == "--options_expr") { options_expr = nix::getArg(*arg, arg, end); } else if (*arg == "--config_expr") { config_expr = nix::getArg(*arg, arg, end); } else if (!arg->empty() && arg->at(0) == '-') { return false; } else { args.push_back(*arg); } return true; }); myArgs.parseCmdline(nix::argvToStrings(argc, argv)); nix::initPlugins(); nix::initGC(); nix::settings.readOnlyMode = true; auto store = nix::openStore(); auto state = std::make_unique<EvalState>(myArgs.searchPath, store); Value options_root = parseAndEval(state.get(), options_expr, path); Value config_root = parseAndEval(state.get(), config_expr, path); Context ctx{state.get(), myArgs.getAutoArgs(*state), options_root, config_root}; 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 (auto const & arg : args) { printOne(&ctx, out, arg); } } ctx.state->printStats(); return 0; } </ins>

inherit (config.system.nixos-generate-config) configuration; }; <del> nixos-option = makeProg { name = "nixos-option"; src = ./nixos-option.sh; }; </del> <ins> nixos-option = pkgs.callPackage ./nixos-option { }; </ins> nixos-version = makeProg { name = "nixos-version";