From b8db81573a1e5a4e26da10d778d4400684a96832 Mon Sep 17 00:00:00 2001 From: Chuck Date: Fri, 6 Sep 2019 14:32:22 -0700 Subject: [PATCH] Support submodules (Fixes #13121) --- .../tools/nixos-option/nixos-option.cc | 56 +++++++++++++++---- 1 file changed, 46 insertions(+), 10 deletions(-) diff --git a/nixos/modules/installer/tools/nixos-option/nixos-option.cc b/nixos/modules/installer/tools/nixos-option/nixos-option.cc index 8e2a8f5c018e..755c2f0b0027 100644 --- a/nixos/modules/installer/tools/nixos-option/nixos-option.cc +++ b/nixos/modules/installer/tools/nixos-option/nixos-option.cc @@ -440,6 +440,45 @@ void printListing(Out & out, Value * v) } } +bool optionTypeIs(Context * ctx, Value & v, std::string const & sought_type) +{ + try { + auto const & type_lookup = v.attrs->find(ctx->state->sType); + if (type_lookup == v.attrs->end()) { + return false; + } + Value type = evaluateValue(ctx, type_lookup->value); + if (type.type != tAttrs) { + return false; + } + auto const & name_lookup = type.attrs->find(ctx->state->sName); + if (name_lookup == type.attrs->end()) { + return false; + } + Value name = evaluateValue(ctx, name_lookup->value); + if (name.type != tString) { + return false; + } + return name.string.s == sought_type; + } catch (Error &) { + return false; + } +} + +Value getSubOptions(Context * ctx, Value & option) +{ + Value getSubOptions = + evaluateValue(ctx, findAlongAttrPath(*ctx->state, "type.getSubOptions", *ctx->autoArgs, option)); + if (getSubOptions.type != tLambda) { + throw Error("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, std::string const & path) @@ -453,16 +492,13 @@ Value findAlongOptionPath(Context * ctx, std::string const & path) 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. + if (isOption(ctx, v) && optionTypeIs(ctx, v, "submodule")) { + v = getSubOptions(ctx, v); + } + if (isOption(ctx, v) && optionTypeIs(ctx, v, "loaOf") && !last_attribute) { + 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 Error("attribute '%s' in path '%s' attempts to index a value that should be a set but is %s", attr, path, showType(v));