diff options
Diffstat (limited to 'scripts/kconfig/menu.c')
| -rw-r--r-- | scripts/kconfig/menu.c | 195 | 
1 files changed, 174 insertions, 21 deletions
| diff --git a/scripts/kconfig/menu.c b/scripts/kconfig/menu.c index e9357931b47..5c5c1374b15 100644 --- a/scripts/kconfig/menu.c +++ b/scripts/kconfig/menu.c @@ -62,13 +62,8 @@ void menu_add_entry(struct symbol *sym)  		menu_add_symbol(P_SYMBOL, sym, NULL);  } -void menu_end_entry(void) -{ -} -  struct menu *menu_add_menu(void)  { -	menu_end_entry();  	last_entry_ptr = ¤t_entry->list;  	return current_menu = current_entry;  } @@ -79,19 +74,23 @@ void menu_end_menu(void)  	current_menu = current_menu->parent;  } -static struct expr *menu_check_dep(struct expr *e) +/* + * Rewrites 'm' to 'm' && MODULES, so that it evaluates to 'n' when running + * without modules + */ +static struct expr *rewrite_m(struct expr *e)  {  	if (!e)  		return e;  	switch (e->type) {  	case E_NOT: -		e->left.expr = menu_check_dep(e->left.expr); +		e->left.expr = rewrite_m(e->left.expr);  		break;  	case E_OR:  	case E_AND: -		e->left.expr = menu_check_dep(e->left.expr); -		e->right.expr = menu_check_dep(e->right.expr); +		e->left.expr = rewrite_m(e->left.expr); +		e->right.expr = rewrite_m(e->right.expr);  		break;  	case E_SYMBOL:  		/* change 'm' into 'm' && MODULES */ @@ -106,7 +105,7 @@ static struct expr *menu_check_dep(struct expr *e)  void menu_add_dep(struct expr *dep)  { -	current_entry->dep = expr_alloc_and(current_entry->dep, menu_check_dep(dep)); +	current_entry->dep = expr_alloc_and(current_entry->dep, dep);  }  void menu_set_type(int type) @@ -131,7 +130,7 @@ static struct property *menu_add_prop(enum prop_type type, char *prompt, struct  	prop->menu = current_entry;  	prop->expr = expr; -	prop->visible.expr = menu_check_dep(dep); +	prop->visible.expr = dep;  	if (prompt) {  		if (isspace(*prompt)) { @@ -213,6 +212,7 @@ void menu_add_option(int token, char *arg)  			sym_defconfig_list = current_entry->sym;  		else if (sym_defconfig_list != current_entry->sym)  			zconf_error("trying to redefine defconfig symbol"); +		sym_defconfig_list->flags |= SYMBOL_AUTO;  		break;  	case T_OPT_ENV:  		prop_add_env(arg); @@ -252,6 +252,16 @@ static void sym_check_prop(struct symbol *sym)  					    "'%s': number is invalid",  					    sym->name);  			} +			if (sym_is_choice(sym)) { +				struct property *choice_prop = +					sym_get_choice_prop(sym2); + +				if (!choice_prop || +				    prop_get_symbol(choice_prop) != sym) +					prop_warn(prop, +						  "choice default symbol '%s' is not contained in the choice", +						  sym2->name); +			}  			break;  		case P_SELECT:  		case P_IMPLY: @@ -260,13 +270,13 @@ static void sym_check_prop(struct symbol *sym)  			if (sym->type != S_BOOLEAN && sym->type != S_TRISTATE)  				prop_warn(prop,  				    "config symbol '%s' uses %s, but is " -				    "not boolean or tristate", sym->name, use); +				    "not bool or tristate", sym->name, use);  			else if (sym2->type != S_UNKNOWN &&  				 sym2->type != S_BOOLEAN &&  				 sym2->type != S_TRISTATE)  				prop_warn(prop,  				    "'%s' has wrong type. '%s' only " -				    "accept arguments of boolean and " +				    "accept arguments of bool and "  				    "tristate type", sym2->name, use);  			break;  		case P_RANGE: @@ -292,6 +302,11 @@ void menu_finalize(struct menu *parent)  	sym = parent->sym;  	if (parent->list) { +		/* +		 * This menu node has children. We (recursively) process them +		 * and propagate parent dependencies before moving on. +		 */ +  		if (sym && sym_is_choice(sym)) {  			if (sym->type == S_UNKNOWN) {  				/* find the first choice value to find out choice type */ @@ -309,30 +324,83 @@ void menu_finalize(struct menu *parent)  				if (menu->sym && menu->sym->type == S_UNKNOWN)  					menu_set_type(sym->type);  			} + +			/* +			 * Use the choice itself as the parent dependency of +			 * the contained items. This turns the mode of the +			 * choice into an upper bound on the visibility of the +			 * choice value symbols. +			 */  			parentdep = expr_alloc_symbol(sym);  		} else if (parent->prompt) +			/* Menu node for 'menu' */  			parentdep = parent->prompt->visible.expr;  		else +			/* Menu node for 'if' */  			parentdep = parent->dep; +		/* For each child menu node... */  		for (menu = parent->list; menu; menu = menu->next) { -			basedep = expr_transform(menu->dep); +			/* +			 * Propagate parent dependencies to the child menu +			 * node, also rewriting and simplifying expressions +			 */ +			basedep = rewrite_m(menu->dep); +			basedep = expr_transform(basedep);  			basedep = expr_alloc_and(expr_copy(parentdep), basedep);  			basedep = expr_eliminate_dups(basedep);  			menu->dep = basedep; +  			if (menu->sym) +				/* +				 * Note: For symbols, all prompts are included +				 * too in the symbol's own property list +				 */  				prop = menu->sym->prop;  			else +				/* +				 * For non-symbol menu nodes, we just need to +				 * handle the prompt +				 */  				prop = menu->prompt; + +			/* For each property... */  			for (; prop; prop = prop->next) {  				if (prop->menu != menu) +					/* +					 * Two possibilities: +					 * +					 * 1. The property lacks dependencies +					 *    and so isn't location-specific, +					 *    e.g. an 'option' +					 * +					 * 2. The property belongs to a symbol +					 *    defined in multiple locations and +					 *    is from some other location. It +					 *    will be handled there in that +					 *    case. +					 * +					 * Skip the property. +					 */  					continue; -				dep = expr_transform(prop->visible.expr); + +				/* +				 * Propagate parent dependencies to the +				 * property's condition, rewriting and +				 * simplifying expressions at the same time +				 */ +				dep = rewrite_m(prop->visible.expr); +				dep = expr_transform(dep);  				dep = expr_alloc_and(expr_copy(basedep), dep);  				dep = expr_eliminate_dups(dep);  				if (menu->sym && menu->sym->type != S_TRISTATE)  					dep = expr_trans_bool(dep);  				prop->visible.expr = dep; + +				/* +				 * Handle selects and implies, which modify the +				 * dependencies of the selected/implied symbol +				 */  				if (prop->type == P_SELECT) {  					struct symbol *es = prop_get_symbol(prop);  					es->rev_dep.expr = expr_alloc_or(es->rev_dep.expr, @@ -344,34 +412,81 @@ void menu_finalize(struct menu *parent)  				}  			}  		} + +		if (sym && sym_is_choice(sym)) +			expr_free(parentdep); + +		/* +		 * Recursively process children in the same fashion before +		 * moving on +		 */  		for (menu = parent->list; menu; menu = menu->next)  			menu_finalize(menu);  	} else if (sym) { +		/* +		 * Automatic submenu creation. If sym is a symbol and A, B, C, +		 * ... are consecutive items (symbols, menus, ifs, etc.) that +		 * all depend on sym, then the following menu structure is +		 * created: +		 * +		 *	sym +		 *	 +-A +		 *	 +-B +		 *	 +-C +		 *	 ... +		 * +		 * This also works recursively, giving the following structure +		 * if A is a symbol and B depends on A: +		 * +		 *	sym +		 *	 +-A +		 *	 | +-B +		 *	 +-C +		 *	 ... +		 */ +  		basedep = parent->prompt ? parent->prompt->visible.expr : NULL;  		basedep = expr_trans_compare(basedep, E_UNEQUAL, &symbol_no);  		basedep = expr_eliminate_dups(expr_transform(basedep)); + +		/* Examine consecutive elements after sym */  		last_menu = NULL;  		for (menu = parent->next; menu; menu = menu->next) {  			dep = menu->prompt ? menu->prompt->visible.expr : menu->dep;  			if (!expr_contains_symbol(dep, sym)) +				/* No dependency, quit */  				break;  			if (expr_depends_symbol(dep, sym)) +				/* Absolute dependency, put in submenu */  				goto next; + +			/* +			 * Also consider it a dependency on sym if our +			 * dependencies contain sym and are a "superset" of +			 * sym's dependencies, e.g. '(sym || Q) && R' when sym +			 * depends on R. +			 * +			 * Note that 'R' might be from an enclosing menu or if, +			 * making this a more common case than it might seem. +			 */  			dep = expr_trans_compare(dep, E_UNEQUAL, &symbol_no);  			dep = expr_eliminate_dups(expr_transform(dep));  			dep2 = expr_copy(basedep);  			expr_eliminate_eq(&dep, &dep2);  			expr_free(dep);  			if (!expr_is_yes(dep2)) { +				/* Not superset, quit */  				expr_free(dep2);  				break;  			} +			/* Superset, put in submenu */  			expr_free(dep2);  		next:  			menu_finalize(menu);  			menu->parent = parent;  			last_menu = menu;  		} +		expr_free(basedep);  		if (last_menu) {  			parent->list = parent->next;  			parent->next = last_menu->next; @@ -420,6 +535,35 @@ void menu_finalize(struct menu *parent)  			*ep = expr_alloc_one(E_LIST, NULL);  			(*ep)->right.sym = menu->sym;  		} + +		/* +		 * This code serves two purposes: +		 * +		 * (1) Flattening 'if' blocks, which do not specify a submenu +		 *     and only add dependencies. +		 * +		 *     (Automatic submenu creation might still create a submenu +		 *     from an 'if' before this code runs.) +		 * +		 * (2) "Undoing" any automatic submenus created earlier below +		 *     promptless symbols. +		 * +		 * Before: +		 * +		 *	A +		 *	if ... (or promptless symbol) +		 *	 +-B +		 *	 +-C +		 *	D +		 * +		 * After: +		 * +		 *	A +		 *	if ... (or promptless symbol) +		 *	B +		 *	C +		 *	D +		 */  		if (menu->list && (!menu->prompt || !menu->prompt->text)) {  			for (last_menu = menu->list; ; last_menu = last_menu->next) {  				last_menu->parent = parent; @@ -444,6 +588,15 @@ void menu_finalize(struct menu *parent)  		sym->flags |= SYMBOL_WARNED;  	} +	/* +	 * For non-optional choices, add a reverse dependency (corresponding to +	 * a select) of '<visibility> && m'. This prevents the user from +	 * setting the choice mode to 'n' when the choice is visible. +	 * +	 * This would also work for non-choice symbols, but only non-optional +	 * choices clear SYMBOL_OPTIONAL as of writing. Choices are implemented +	 * as a type of symbol. +	 */  	if (sym && !sym_is_optional(sym) && parent->prompt) {  		sym->rev_dep.expr = expr_alloc_or(sym->rev_dep.expr,  				expr_alloc_and(parent->prompt->visible.expr, @@ -675,16 +828,16 @@ static void get_symbol_str(struct gstr *r, struct symbol *sym,  	get_symbol_props_str(r, sym, P_SELECT, _("  Selects: "));  	if (sym->rev_dep.expr) { -		str_append(r, _("  Selected by: ")); -		expr_gstr_print(sym->rev_dep.expr, r); -		str_append(r, "\n"); +		expr_gstr_print_revdep(sym->rev_dep.expr, r, yes, "  Selected by [y]:\n"); +		expr_gstr_print_revdep(sym->rev_dep.expr, r, mod, "  Selected by [m]:\n"); +		expr_gstr_print_revdep(sym->rev_dep.expr, r, no, "  Selected by [n]:\n");  	}  	get_symbol_props_str(r, sym, P_IMPLY, _("  Implies: "));  	if (sym->implied.expr) { -		str_append(r, _("  Implied by: ")); -		expr_gstr_print(sym->implied.expr, r); -		str_append(r, "\n"); +		expr_gstr_print_revdep(sym->implied.expr, r, yes, "  Implied by [y]:\n"); +		expr_gstr_print_revdep(sym->implied.expr, r, mod, "  Implied by [m]:\n"); +		expr_gstr_print_revdep(sym->implied.expr, r, no, "  Implied by [n]:\n");  	}  	str_append(r, "\n\n"); | 
