kconfig: print more info when we see a recursive dependency
Consider following kconfig file: config TEST1 bool "test 1" depends on TEST2 config TEST2 bool "test 2" depends on TEST1 Previously kconfig would report: foo:6:error: found recursive dependency: TEST2 -> TEST1 -> TEST2 With the following patch kconfig reports: foo:5:error: recursive dependency detected! foo:5: symbol TEST2 depends on TEST1 foo:1: symbol TEST1 depends on TEST2 Note that we now report where the offending symbols are defined. This can be a great help for complex situations involving several files. Patch is originally from Roman Zippel with a few adjustments by Sam. Signed-off-by: Sam Ravnborg <sam@ravnborg.org> Cc: Roman Zippel <zippel@linux-m68k.org> Signed-off-by: Michal Marek <mmarek@suse.cz>
This commit is contained in:
		
							parent
							
								
									59e89e3ddf
								
							
						
					
					
						commit
						d595cea624
					
				| @ -783,6 +783,110 @@ struct symbol **sym_re_search(const char *pattern) | |||||||
| 	return sym_arr; | 	return sym_arr; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | /*
 | ||||||
|  |  * When we check for recursive dependencies we use a stack to save | ||||||
|  |  * current state so we can print out relevant info to user. | ||||||
|  |  * The entries are located on the call stack so no need to free memory. | ||||||
|  |  * Note inser() remove() must always match to properly clear the stack. | ||||||
|  |  */ | ||||||
|  | static struct dep_stack { | ||||||
|  | 	struct dep_stack *prev, *next; | ||||||
|  | 	struct symbol *sym; | ||||||
|  | 	struct property *prop; | ||||||
|  | 	struct expr *expr; | ||||||
|  | } *check_top; | ||||||
|  | 
 | ||||||
|  | static void dep_stack_insert(struct dep_stack *stack, struct symbol *sym) | ||||||
|  | { | ||||||
|  | 	memset(stack, 0, sizeof(*stack)); | ||||||
|  | 	if (check_top) | ||||||
|  | 		check_top->next = stack; | ||||||
|  | 	stack->prev = check_top; | ||||||
|  | 	stack->sym = sym; | ||||||
|  | 	check_top = stack; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void dep_stack_remove(void) | ||||||
|  | { | ||||||
|  | 	check_top = check_top->prev; | ||||||
|  | 	if (check_top) | ||||||
|  | 		check_top->next = NULL; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /*
 | ||||||
|  |  * Called when we have detected a recursive dependency. | ||||||
|  |  * check_top point to the top of the stact so we use | ||||||
|  |  * the ->prev pointer to locate the bottom of the stack. | ||||||
|  |  */ | ||||||
|  | static void sym_check_print_recursive(struct symbol *last_sym) | ||||||
|  | { | ||||||
|  | 	struct dep_stack *stack; | ||||||
|  | 	struct symbol *sym, *next_sym; | ||||||
|  | 	struct menu *menu = NULL; | ||||||
|  | 	struct property *prop; | ||||||
|  | 	struct dep_stack cv_stack; | ||||||
|  | 
 | ||||||
|  | 	if (sym_is_choice_value(last_sym)) { | ||||||
|  | 		dep_stack_insert(&cv_stack, last_sym); | ||||||
|  | 		last_sym = prop_get_symbol(sym_get_choice_prop(last_sym)); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	for (stack = check_top; stack != NULL; stack = stack->prev) | ||||||
|  | 		if (stack->sym == last_sym) | ||||||
|  | 			break; | ||||||
|  | 	if (!stack) { | ||||||
|  | 		fprintf(stderr, "unexpected recursive dependency error\n"); | ||||||
|  | 		return; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	for (; stack; stack = stack->next) { | ||||||
|  | 		sym = stack->sym; | ||||||
|  | 		next_sym = stack->next ? stack->next->sym : last_sym; | ||||||
|  | 		prop = stack->prop; | ||||||
|  | 
 | ||||||
|  | 		/* for choice values find the menu entry (used below) */ | ||||||
|  | 		if (sym_is_choice(sym) || sym_is_choice_value(sym)) { | ||||||
|  | 			for (prop = sym->prop; prop; prop = prop->next) { | ||||||
|  | 				menu = prop->menu; | ||||||
|  | 				if (prop->menu) | ||||||
|  | 					break; | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 		if (stack->sym == last_sym) | ||||||
|  | 			fprintf(stderr, "%s:%d:error: recursive dependency detected!\n", | ||||||
|  | 				prop->file->name, prop->lineno); | ||||||
|  | 		if (stack->expr) { | ||||||
|  | 			fprintf(stderr, "%s:%d:\tsymbol %s %s value contains %s\n", | ||||||
|  | 				prop->file->name, prop->lineno, | ||||||
|  | 				sym->name ? sym->name : "<choice>", | ||||||
|  | 				prop_get_type_name(prop->type), | ||||||
|  | 				next_sym->name ? next_sym->name : "<choice>"); | ||||||
|  | 		} else if (stack->prop) { | ||||||
|  | 			fprintf(stderr, "%s:%d:\tsymbol %s depends on %s\n", | ||||||
|  | 				prop->file->name, prop->lineno, | ||||||
|  | 				sym->name ? sym->name : "<choice>", | ||||||
|  | 				next_sym->name ? next_sym->name : "<choice>"); | ||||||
|  | 		} else if (sym_is_choice(sym)) { | ||||||
|  | 			fprintf(stderr, "%s:%d:\tchoice %s contains symbol %s\n", | ||||||
|  | 				menu->file->name, menu->lineno, | ||||||
|  | 				sym->name ? sym->name : "<choice>", | ||||||
|  | 				next_sym->name ? next_sym->name : "<choice>"); | ||||||
|  | 		} else if (sym_is_choice_value(sym)) { | ||||||
|  | 			fprintf(stderr, "%s:%d:\tsymbol %s is part of choice %s\n", | ||||||
|  | 				menu->file->name, menu->lineno, | ||||||
|  | 				sym->name ? sym->name : "<choice>", | ||||||
|  | 				next_sym->name ? next_sym->name : "<choice>"); | ||||||
|  | 		} else { | ||||||
|  | 			fprintf(stderr, "%s:%d:\tsymbol %s is selected by %s\n", | ||||||
|  | 				prop->file->name, prop->lineno, | ||||||
|  | 				sym->name ? sym->name : "<choice>", | ||||||
|  | 				next_sym->name ? next_sym->name : "<choice>"); | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if (check_top == &cv_stack) | ||||||
|  | 		dep_stack_remove(); | ||||||
|  | } | ||||||
| 
 | 
 | ||||||
| static struct symbol *sym_check_expr_deps(struct expr *e) | static struct symbol *sym_check_expr_deps(struct expr *e) | ||||||
| { | { | ||||||
| @ -819,24 +923,33 @@ static struct symbol *sym_check_sym_deps(struct symbol *sym) | |||||||
| { | { | ||||||
| 	struct symbol *sym2; | 	struct symbol *sym2; | ||||||
| 	struct property *prop; | 	struct property *prop; | ||||||
|  | 	struct dep_stack stack; | ||||||
|  | 
 | ||||||
|  | 	dep_stack_insert(&stack, sym); | ||||||
| 
 | 
 | ||||||
| 	sym2 = sym_check_expr_deps(sym->rev_dep.expr); | 	sym2 = sym_check_expr_deps(sym->rev_dep.expr); | ||||||
| 	if (sym2) | 	if (sym2) | ||||||
| 		return sym2; | 		goto out; | ||||||
| 
 | 
 | ||||||
| 	for (prop = sym->prop; prop; prop = prop->next) { | 	for (prop = sym->prop; prop; prop = prop->next) { | ||||||
| 		if (prop->type == P_CHOICE || prop->type == P_SELECT) | 		if (prop->type == P_CHOICE || prop->type == P_SELECT) | ||||||
| 			continue; | 			continue; | ||||||
|  | 		stack.prop = prop; | ||||||
| 		sym2 = sym_check_expr_deps(prop->visible.expr); | 		sym2 = sym_check_expr_deps(prop->visible.expr); | ||||||
| 		if (sym2) | 		if (sym2) | ||||||
| 			break; | 			break; | ||||||
| 		if (prop->type != P_DEFAULT || sym_is_choice(sym)) | 		if (prop->type != P_DEFAULT || sym_is_choice(sym)) | ||||||
| 			continue; | 			continue; | ||||||
|  | 		stack.expr = prop->expr; | ||||||
| 		sym2 = sym_check_expr_deps(prop->expr); | 		sym2 = sym_check_expr_deps(prop->expr); | ||||||
| 		if (sym2) | 		if (sym2) | ||||||
| 			break; | 			break; | ||||||
|  | 		stack.expr = NULL; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | out: | ||||||
|  | 	dep_stack_remove(); | ||||||
|  | 
 | ||||||
| 	return sym2; | 	return sym2; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -845,6 +958,9 @@ static struct symbol *sym_check_choice_deps(struct symbol *choice) | |||||||
| 	struct symbol *sym, *sym2; | 	struct symbol *sym, *sym2; | ||||||
| 	struct property *prop; | 	struct property *prop; | ||||||
| 	struct expr *e; | 	struct expr *e; | ||||||
|  | 	struct dep_stack stack; | ||||||
|  | 
 | ||||||
|  | 	dep_stack_insert(&stack, choice); | ||||||
| 
 | 
 | ||||||
| 	prop = sym_get_choice_prop(choice); | 	prop = sym_get_choice_prop(choice); | ||||||
| 	expr_list_for_each_sym(prop->expr, e, sym) | 	expr_list_for_each_sym(prop->expr, e, sym) | ||||||
| @ -858,10 +974,8 @@ static struct symbol *sym_check_choice_deps(struct symbol *choice) | |||||||
| 
 | 
 | ||||||
| 	expr_list_for_each_sym(prop->expr, e, sym) { | 	expr_list_for_each_sym(prop->expr, e, sym) { | ||||||
| 		sym2 = sym_check_sym_deps(sym); | 		sym2 = sym_check_sym_deps(sym); | ||||||
| 		if (sym2) { | 		if (sym2) | ||||||
| 			fprintf(stderr, " -> %s", sym->name); |  | ||||||
| 			break; | 			break; | ||||||
| 		} |  | ||||||
| 	} | 	} | ||||||
| out: | out: | ||||||
| 	expr_list_for_each_sym(prop->expr, e, sym) | 	expr_list_for_each_sym(prop->expr, e, sym) | ||||||
| @ -871,6 +985,8 @@ out: | |||||||
| 	    prop_get_symbol(sym_get_choice_prop(sym2)) == choice) | 	    prop_get_symbol(sym_get_choice_prop(sym2)) == choice) | ||||||
| 		sym2 = choice; | 		sym2 = choice; | ||||||
| 
 | 
 | ||||||
|  | 	dep_stack_remove(); | ||||||
|  | 
 | ||||||
| 	return sym2; | 	return sym2; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -880,18 +996,20 @@ struct symbol *sym_check_deps(struct symbol *sym) | |||||||
| 	struct property *prop; | 	struct property *prop; | ||||||
| 
 | 
 | ||||||
| 	if (sym->flags & SYMBOL_CHECK) { | 	if (sym->flags & SYMBOL_CHECK) { | ||||||
| 		fprintf(stderr, "%s:%d:error: found recursive dependency: %s", | 		sym_check_print_recursive(sym); | ||||||
| 		        sym->prop->file->name, sym->prop->lineno, |  | ||||||
| 			sym->name ? sym->name : "<choice>"); |  | ||||||
| 		return sym; | 		return sym; | ||||||
| 	} | 	} | ||||||
| 	if (sym->flags & SYMBOL_CHECKED) | 	if (sym->flags & SYMBOL_CHECKED) | ||||||
| 		return NULL; | 		return NULL; | ||||||
| 
 | 
 | ||||||
| 	if (sym_is_choice_value(sym)) { | 	if (sym_is_choice_value(sym)) { | ||||||
|  | 		struct dep_stack stack; | ||||||
|  | 
 | ||||||
| 		/* for choice groups start the check with main choice symbol */ | 		/* for choice groups start the check with main choice symbol */ | ||||||
|  | 		dep_stack_insert(&stack, sym); | ||||||
| 		prop = sym_get_choice_prop(sym); | 		prop = sym_get_choice_prop(sym); | ||||||
| 		sym2 = sym_check_deps(prop_get_symbol(prop)); | 		sym2 = sym_check_deps(prop_get_symbol(prop)); | ||||||
|  | 		dep_stack_remove(); | ||||||
| 	} else if (sym_is_choice(sym)) { | 	} else if (sym_is_choice(sym)) { | ||||||
| 		sym2 = sym_check_choice_deps(sym); | 		sym2 = sym_check_choice_deps(sym); | ||||||
| 	} else { | 	} else { | ||||||
| @ -900,14 +1018,8 @@ struct symbol *sym_check_deps(struct symbol *sym) | |||||||
| 		sym->flags &= ~SYMBOL_CHECK; | 		sym->flags &= ~SYMBOL_CHECK; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	if (sym2) { | 	if (sym2 && sym2 == sym) | ||||||
| 		fprintf(stderr, " -> %s", sym->name ? sym->name : "<choice>"); | 		sym2 = NULL; | ||||||
| 		if (sym2 == sym) { |  | ||||||
| 			fprintf(stderr, "\n"); |  | ||||||
| 			zconfnerrs++; |  | ||||||
| 			sym2 = NULL; |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 
 | 
 | ||||||
| 	return sym2; | 	return sym2; | ||||||
| } | } | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user