kbuild: generate modules.builtin
[safe/jmp/linux-2.6] / scripts / kconfig / menu.c
index 8c59b21..059a246 100644 (file)
@@ -9,13 +9,16 @@
 #define LKC_DIRECT_LINK
 #include "lkc.h"
 
+static const char nohelp_text[] = N_(
+       "There is no help available for this kernel option.\n");
+
 struct menu rootmenu;
 static struct menu **last_entry_ptr;
 
 struct file *file_list;
 struct file *current_file;
 
-static void menu_warn(struct menu *menu, const char *fmt, ...)
+void menu_warn(struct menu *menu, const char *fmt, ...)
 {
        va_list ap;
        va_start(ap, fmt);
@@ -61,10 +64,11 @@ void menu_end_entry(void)
 {
 }
 
-void menu_add_menu(void)
+struct menu *menu_add_menu(void)
 {
-       current_menu = current_entry;
+       menu_end_entry();
        last_entry_ptr = &current_entry->list;
+       return current_menu = current_entry;
 }
 
 void menu_end_menu(void)
@@ -73,7 +77,7 @@ void menu_end_menu(void)
        current_menu = current_menu->parent;
 }
 
-struct expr *menu_check_dep(struct expr *e)
+static struct expr *menu_check_dep(struct expr *e)
 {
        if (!e)
                return e;
@@ -113,7 +117,7 @@ void menu_set_type(int type)
                sym->type = type;
                return;
        }
-       menu_warn(current_entry, "type of '%s' redefined from '%s' to '%s'\n",
+       menu_warn(current_entry, "type of '%s' redefined from '%s' to '%s'",
            sym->name ? sym->name : "<choice>",
            sym_type_name(sym->type), sym_type_name(type));
 }
@@ -123,22 +127,27 @@ struct property *menu_add_prop(enum prop_type type, char *prompt, struct expr *e
        struct property *prop = prop_alloc(type, current_entry->sym);
 
        prop->menu = current_entry;
-       prop->text = prompt;
        prop->expr = expr;
        prop->visible.expr = menu_check_dep(dep);
 
        if (prompt) {
+               if (isspace(*prompt)) {
+                       prop_warn(prop, "leading whitespace ignored");
+                       while (isspace(*prompt))
+                               prompt++;
+               }
                if (current_entry->prompt)
-                       menu_warn(current_entry, "prompt redefined\n");
+                       prop_warn(prop, "prompt redefined");
                current_entry->prompt = prop;
        }
+       prop->text = prompt;
 
        return prop;
 }
 
-void menu_add_prompt(enum prop_type type, char *prompt, struct expr *dep)
+struct property *menu_add_prompt(enum prop_type type, char *prompt, struct expr *dep)
 {
-       menu_add_prop(type, prompt, NULL, dep);
+       return menu_add_prop(type, prompt, NULL, dep);
 }
 
 void menu_add_expr(enum prop_type type, struct expr *expr, struct expr *dep)
@@ -151,7 +160,34 @@ void menu_add_symbol(enum prop_type type, struct symbol *sym, struct expr *dep)
        menu_add_prop(type, NULL, expr_alloc_symbol(sym), dep);
 }
 
-void sym_check_prop(struct symbol *sym)
+void menu_add_option(int token, char *arg)
+{
+       struct property *prop;
+
+       switch (token) {
+       case T_OPT_MODULES:
+               prop = prop_alloc(P_DEFAULT, modules_sym);
+               prop->expr = expr_alloc_symbol(current_entry->sym);
+               break;
+       case T_OPT_DEFCONFIG_LIST:
+               if (!sym_defconfig_list)
+                       sym_defconfig_list = current_entry->sym;
+               else if (sym_defconfig_list != current_entry->sym)
+                       zconf_error("trying to redefine defconfig symbol");
+               break;
+       case T_OPT_ENV:
+               prop_add_env(arg);
+               break;
+       }
+}
+
+static int menu_range_valid_sym(struct symbol *sym, struct symbol *sym2)
+{
+       return sym2->type == S_INT || sym2->type == S_HEX ||
+              (sym2->type == S_UNKNOWN && sym_string_valid(sym, sym2->name));
+}
+
+static void sym_check_prop(struct symbol *sym)
 {
        struct property *prop;
        struct symbol *sym2;
@@ -170,12 +206,9 @@ void sym_check_prop(struct symbol *sym)
                                prop_warn(prop,
                                    "config symbol '%s' uses select, but is "
                                    "not boolean or tristate", sym->name);
-                       else if (sym2->type == S_UNKNOWN)
-                               prop_warn(prop,
-                                   "'select' used by config symbol '%s' "
-                                   "refer to undefined symbol '%s'",
-                                   sym->name, sym2->name);
-                       else if (sym2->type != S_BOOLEAN && sym2->type != S_TRISTATE)
+                       else if (sym2->type != S_UNKNOWN &&
+                                sym2->type != S_BOOLEAN &&
+                                sym2->type != S_TRISTATE)
                                prop_warn(prop,
                                    "'%s' has wrong type. 'select' only "
                                    "accept arguments of boolean and "
@@ -185,8 +218,8 @@ void sym_check_prop(struct symbol *sym)
                        if (sym->type != S_INT && sym->type != S_HEX)
                                prop_warn(prop, "range is only allowed "
                                                "for int or hex symbols");
-                       if (!sym_string_valid(sym, prop->expr->left.sym->name) ||
-                           !sym_string_valid(sym, prop->expr->right.sym->name))
+                       if (!menu_range_valid_sym(sym, prop->expr->left.sym) ||
+                           !menu_range_valid_sym(sym, prop->expr->right.sym))
                                prop_warn(prop, "range is invalid");
                        break;
                default:
@@ -205,15 +238,21 @@ void menu_finalize(struct menu *parent)
        sym = parent->sym;
        if (parent->list) {
                if (sym && sym_is_choice(sym)) {
-                       /* find the first choice value and find out choice type */
+                       if (sym->type == S_UNKNOWN) {
+                               /* find the first choice value to find out choice type */
+                               current_entry = parent;
+                               for (menu = parent->list; menu; menu = menu->next) {
+                                       if (menu->sym && menu->sym->type != S_UNKNOWN) {
+                                               menu_set_type(menu->sym->type);
+                                               break;
+                                       }
+                               }
+                       }
+                       /* set the type of the remaining choice values */
                        for (menu = parent->list; menu; menu = menu->next) {
-                               if (menu->sym) {
-                                       current_entry = parent;
-                                       menu_set_type(menu->sym->type);
-                                       current_entry = menu;
+                               current_entry = menu;
+                               if (menu->sym && menu->sym->type == S_UNKNOWN)
                                        menu_set_type(sym->type);
-                                       break;
-                               }
                        }
                        parentdep = expr_alloc_symbol(sym);
                } else if (parent->prompt)
@@ -281,27 +320,43 @@ void menu_finalize(struct menu *parent)
                }
        }
        for (menu = parent->list; menu; menu = menu->next) {
-               if (sym && sym_is_choice(sym) && menu->sym) {
+               if (sym && sym_is_choice(sym) &&
+                   menu->sym && !sym_is_choice_value(menu->sym)) {
+                       current_entry = menu;
                        menu->sym->flags |= SYMBOL_CHOICEVAL;
                        if (!menu->prompt)
                                menu_warn(menu, "choice value must have a prompt");
                        for (prop = menu->sym->prop; prop; prop = prop->next) {
-                               if (prop->type == P_PROMPT && prop->menu != menu) {
-                                       prop_warn(prop, "choice values "
-                                           "currently only support a "
-                                           "single prompt");
-                               }
                                if (prop->type == P_DEFAULT)
                                        prop_warn(prop, "defaults for choice "
-                                           "values not supported");
+                                                 "values not supported");
+                               if (prop->menu == menu)
+                                       continue;
+                               if (prop->type == P_PROMPT &&
+                                   prop->menu->parent->sym != sym)
+                                       prop_warn(prop, "choice value used outside its choice group");
+                       }
+                       /* Non-tristate choice values of tristate choices must
+                        * depend on the choice being set to Y. The choice
+                        * values' dependencies were propagated to their
+                        * properties above, so the change here must be re-
+                        * propagated.
+                        */
+                       if (sym->type == S_TRISTATE && menu->sym->type != S_TRISTATE) {
+                               basedep = expr_alloc_comp(E_EQUAL, sym, &symbol_yes);
+                               menu->dep = expr_alloc_and(basedep, menu->dep);
+                               for (prop = menu->sym->prop; prop; prop = prop->next) {
+                                       if (prop->menu != menu)
+                                               continue;
+                                       prop->visible.expr = expr_alloc_and(expr_copy(basedep),
+                                                                           prop->visible.expr);
+                               }
                        }
-                       current_entry = menu;
-                       menu_set_type(sym->type);
                        menu_add_symbol(P_CHOICE, sym, NULL);
                        prop = sym_get_choice_prop(sym);
                        for (ep = &prop->expr; *ep; ep = &(*ep)->left.expr)
                                ;
-                       *ep = expr_alloc_one(E_CHOICE, NULL);
+                       *ep = expr_alloc_one(E_LIST, NULL);
                        (*ep)->right.sym = menu->sym;
                }
                if (menu->list && (!menu->prompt || !menu->prompt->text)) {
@@ -318,11 +373,10 @@ void menu_finalize(struct menu *parent)
 
        if (sym && !(sym->flags & SYMBOL_WARNED)) {
                if (sym->type == S_UNKNOWN)
-                       menu_warn(parent, "config symbol defined "
-                           "without type\n");
+                       menu_warn(parent, "config symbol defined without type");
 
                if (sym_is_choice(sym) && !parent->prompt)
-                       menu_warn(parent, "choice must have a prompt\n");
+                       menu_warn(parent, "choice must have a prompt");
 
                /* Check properties connected to this symbol */
                sym_check_prop(sym);
@@ -365,9 +419,9 @@ bool menu_is_visible(struct menu *menu)
 const char *menu_get_prompt(struct menu *menu)
 {
        if (menu->prompt)
-               return _(menu->prompt->text);
+               return menu->prompt->text;
        else if (menu->sym)
-               return _(menu->sym->name);
+               return menu->sym->name;
        return NULL;
 }
 
@@ -388,3 +442,92 @@ struct menu *menu_get_parent_menu(struct menu *menu)
        return menu;
 }
 
+bool menu_has_help(struct menu *menu)
+{
+       return menu->help != NULL;
+}
+
+const char *menu_get_help(struct menu *menu)
+{
+       if (menu->help)
+               return menu->help;
+       else
+               return "";
+}
+
+static void get_prompt_str(struct gstr *r, struct property *prop)
+{
+       int i, j;
+       struct menu *submenu[8], *menu;
+
+       str_printf(r, _("Prompt: %s\n"), _(prop->text));
+       str_printf(r, _("  Defined at %s:%d\n"), prop->menu->file->name,
+               prop->menu->lineno);
+       if (!expr_is_yes(prop->visible.expr)) {
+               str_append(r, _("  Depends on: "));
+               expr_gstr_print(prop->visible.expr, r);
+               str_append(r, "\n");
+       }
+       menu = prop->menu->parent;
+       for (i = 0; menu != &rootmenu && i < 8; menu = menu->parent)
+               submenu[i++] = menu;
+       if (i > 0) {
+               str_printf(r, _("  Location:\n"));
+               for (j = 4; --i >= 0; j += 2) {
+                       menu = submenu[i];
+                       str_printf(r, "%*c-> %s", j, ' ', _(menu_get_prompt(menu)));
+                       if (menu->sym) {
+                               str_printf(r, " (%s [=%s])", menu->sym->name ?
+                                       menu->sym->name : _("<choice>"),
+                                       sym_get_string_value(menu->sym));
+                       }
+                       str_append(r, "\n");
+               }
+       }
+}
+
+void get_symbol_str(struct gstr *r, struct symbol *sym)
+{
+       bool hit;
+       struct property *prop;
+
+       if (sym && sym->name)
+               str_printf(r, "Symbol: %s [=%s]\n", sym->name,
+                          sym_get_string_value(sym));
+       for_all_prompts(sym, prop)
+               get_prompt_str(r, prop);
+       hit = false;
+       for_all_properties(sym, prop, P_SELECT) {
+               if (!hit) {
+                       str_append(r, "  Selects: ");
+                       hit = true;
+               } else
+                       str_printf(r, " && ");
+               expr_gstr_print(prop->expr, r);
+       }
+       if (hit)
+               str_append(r, "\n");
+       if (sym->rev_dep.expr) {
+               str_append(r, _("  Selected by: "));
+               expr_gstr_print(sym->rev_dep.expr, r);
+               str_append(r, "\n");
+       }
+       str_append(r, "\n\n");
+}
+
+void menu_get_ext_help(struct menu *menu, struct gstr *help)
+{
+       struct symbol *sym = menu->sym;
+
+       if (menu_has_help(menu)) {
+               if (sym->name) {
+                       str_printf(help, "CONFIG_%s:\n\n", sym->name);
+                       str_append(help, _(menu_get_help(menu)));
+                       str_append(help, "\n");
+               }
+       } else {
+               str_append(help, nohelp_text);
+       }
+       if (sym)
+               get_symbol_str(help, sym);
+}