1. 概述
conf的源码位于scripts/kconfig目录下。
2. conf的编译
u-boot配置过程执行make pangu_basic_defconfig V=1,对应于顶层Makefile中的%config目标:
%config: scripts_basic outputmakefile FORCE
$(Q)$(MAKE) $(build)=scripts/kconfig $@
在这里展开后相当于:
pangu_basic_defconfig: scripts_basic outputmakefile FORCE
make -f ./scripts/Makefile.build obj=scripts/kconfig pangu_basic_defconfig
这个规则的命令会指定参数obj=scripts/kconfig和pangu_basic_defconfig进入scripts/Makefile.build进行编译。
在scripts/Makefile.build中,会根据参数obj=scripts/kconfig,包含scripts/kconfig文件夹下的子Makefile:
# The filename Kbuild has precedence over Makefile
kbuild-dir := $(if $(filter /%,$(src)),$(src),$(srctree)/$(src))
kbuild-file := $(if $(wildcard $(kbuild-dir)/Kbuild),$(kbuild-dir)/Kbuild,$(kbuild-dir)/Makefile)
include $(kbuild-file)
scripts/kconfig/Makefile中相应的规则为:
%_defconfig: $(obj)/conf
$(Q)$< $(silent) --defconfig=arch/$(SRCARCH)/configs/$@ $(Kconfig)
在这里展开为:
pangu_basic_defconfig: scripts/kconfig/conf
scripts/kconfig/conf --defconfig=arch/../configs/pangu_basic_defconfig Kconfig
由于pangu_basic_defconfig依赖于scripts/kconfig/conf,所以conf将会被编译。
conf依赖于conf.c和zconf.tab.c,后者进一步包含所需要的词法分析文件,整个编译链接过程如下:
make -f ./scripts/Makefile.build obj=scripts/kconfig pangu_basic_defconfig
cc -Wp,-MD,scripts/kconfig/.conf.o.d -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer -std=gnu11 -D_DEFAULT_SOURCE -D_XOPEN_SOURCE=600 -DCURSES_LOC="<ncurses.h>" -DNCURSES_WIDECHAR=1 -DLOCALE -c -o scripts/kconfig/conf.o scripts/kconfig/conf.c
bison -oscripts/kconfig/zconf.tab.c -t -l scripts/kconfig/zconf.y
flex -oscripts/kconfig/zconf.lex.c -L scripts/kconfig/zconf.l
cc -Wp,-MD,scripts/kconfig/.zconf.tab.o.d -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer -std=gnu11 -D_DEFAULT_SOURCE -D_XOPEN_SOURCE=600 -DCURSES_LOC="<ncurses.h>" -DNCURSES_WIDECHAR=1 -DLOCALE -Iscripts/kconfig -c -o scripts/kconfig/zconf.tab.o scripts/kconfig/zconf.tab.c
cc -o scripts/kconfig/conf scripts/kconfig/conf.o scripts/kconfig/zconf.tab.o
scripts/kconfig/conf --defconfig=arch/../configs/pangu_basic_defconfig Kconfig
(注:执行make pangu_basic_defconfig时,打开V=1选项可以看到整个过程)
除了u-boot配置过程(make pangu_basic_defconfig)外,u-boot编译过程中也有对应的规则编译conf,见顶层Makefile:
# If .config is newer than include/config/auto.conf, someone tinkered
# with it and forgot to run make oldconfig.
# if auto.conf.cmd is missing then we are probably in a cleaned tree so
# we execute the config step to be sure to catch updated Kconfig files
include/config/%.conf: $(KCONFIG_CONFIG) include/config/auto.conf.cmd
$(Q)$(MAKE) -f $(srctree)/Makefile syncconfig
@# If the following part fails, include/config/auto.conf should be
@# deleted so "make silentoldconfig" will be re-run on the next build.
$(Q)$(MAKE) -f $(srctree)/scripts/Makefile.autoconf || \
{ rm -f include/config/auto.conf; false; }
@# include/config.h has been updated after "make silentoldconfig".
@# We need to touch include/config/auto.conf so it gets newer
@# than include/config.h.
@# Otherwise, 'make silentoldconfig' would be invoked twice.
$(Q)touch include/config/auto.conf
在这个规则中也会根据syncconfig再次检查并更新scripts/kconfig/conf文件。
3. conf调用
在u-boot的配置编译过程中,conf被调用了两次:一次是在配置过程中,一次是在编译过程中。
3.1 在u-boot配置过程中调用
在u-boot的配置过程中,完成conf的编译后,会随即调用命令:
scripts/kconfig/conf --defconfig=arch/../configs/pangu_basic_defconfig Kconfig
去生成根目录下的.config文件。
3.2 在u-boot编译过程中调用
u-boot配置完成后,执行make命令开始编译时,会检查.config是否比include/config/auto.conf更新,规则如下:
# If .config is newer than include/config/auto.conf, someone tinkered
# with it and forgot to run make oldconfig.
# if auto.conf.cmd is missing then we are probably in a cleaned tree so
# we execute the config step to be sure to catch updated Kconfig files
include/config/%.conf: $(KCONFIG_CONFIG) include/config/auto.conf.cmd
$(Q)$(MAKE) -f $(srctree)/Makefile syncconfig
@# If the following part fails, include/config/auto.conf should be
@# deleted so "make silentoldconfig" will be re-run on the next build.
$(Q)$(MAKE) -f $(srctree)/scripts/Makefile.autoconf || \
{ rm -f include/config/auto.conf; false; }
@# include/config.h has been updated after "make silentoldconfig".
@# We need to touch include/config/auto.conf so it gets newer
@# than include/config.h.
@# Otherwise, 'make silentoldconfig' would be invoked twice.
$(Q)touch include/config/auto.conf
这个规则的make输出log如下:
make -f ./Makefile syncconfig
make -f ./scripts/Makefile.build obj=scripts/basic
rm -f .tmp_quiet_recordmcount
make -f ./scripts/Makefile.build obj=scripts/kconfig syncconfig
mkdir -p include/config include/generated
scripts/kconfig/conf --syncconfig Kconfig
make -f ./scripts/Makefile.autoconf || \
{ rm -f include/config/auto.conf; false; }
if [ -d arch/arm/mach-stm32mp/include/mach ]; then \
dest=../../mach-stm32mp/include/mach; \
else \
dest=arch-stm32mp; \
fi; \
ln -fsn $dest arch/arm/include/asm/arch
... ...
实际执行$(Q)$(MAKE) -f $(srctree)/Makefile syncconfig命令的结果就是检查并更新scripts/kconfig/conf文件,然后调用之:
scripts/kconfig/conf --syncconfig Kconfig。
为了检查这个命令的输出,可以直接在命令行执行命令:
wei@wei-PC:~/stm32mp/uboot/u-boot-st$ make -f ./Makefile syncconfig V=1
make -f ./scripts/Makefile.build obj=scripts/basic
rm -f .tmp_quiet_recordmcount
make -f ./scripts/Makefile.build obj=scripts/kconfig syncconfig
mkdir -p include/config include/generated
scripts/kconfig/conf --syncconfig Kconfig
其实跟命令行上运行make syncconfig是一样的效果。
执行结果就是:
- 在include/config下生成auto.conf和auto.conf.cmd,以及tristate.conf;
- 在include/generated下生成autoconfig.h文件;
3.3 调用简述
简而言之:
- 配置阶段生成.config文件
scripts/kconfig/conf --defconfig=arch/../configs/pangu_basic_defconfig Kconfig
- 编译阶段生成auto.conf和autoconf.h
scripts/kconfig/conf --syncconfig Kconfig
4. conf源码分析
conf由conf.o和zconf.tab.o链接而来,其中conf.c生成conf.o,是整个应用的主程序;zconf.tab.c生成zconf.tab.o,完成具体的词法和语法分析任务。
4.1 zconf.tab.c
zconf.tab.c用于读取并分析整个Kconfig系统的文件,较为复杂,也比较枯燥,此处略过。
4.2 conf.c
conf.c是conf主程序的文件,通过分析main函数可以大致了解操作流程:
4.2.1 解析参数部分
while ((opt = getopt_long(ac, av, "s", long_opts, NULL)) != -1) {
if (opt == 's') {
conf_set_message_callback(NULL);
continue;
}
input_mode = (enum input_mode)opt;
switch (opt) {
case syncconfig:
sync_kconfig = 1;
break;
case defconfig:
case savedefconfig:
defconfig_file = optarg;
break;
case randconfig:
{
struct timeval now;
unsigned int seed;
char *seed_env;
/*
* Use microseconds derived seed,
* compensate for systems where it may be zero
*/
gettimeofday(&now, NULL);
seed = (unsigned int)((now.tv_sec + 1) * (now.tv_usec + 1));
seed_env = getenv("KCONFIG_SEED");
if( seed_env && *seed_env ) {
char *endp;
int tmp = (int)strtol(seed_env, &endp, 0);
if (*endp == '\0') {
seed = tmp;
}
}
fprintf( stderr, "KCONFIG_SEED=0x%X\n", seed );
srand(seed);
break;
}
case oldaskconfig:
case oldconfig:
case allnoconfig:
case allyesconfig:
case allmodconfig:
case alldefconfig:
case listnewconfig:
case olddefconfig:
break;
case '?':
conf_usage(progname);
exit(1);
break;
}
}
if (ac == optind) {
fprintf(stderr, _("%s: Kconfig file missing\n"), av[0]);
conf_usage(progname);
exit(1);
}
name = av[optind];
conf_parse(name);
//zconfdump(stdout);
if (sync_kconfig) {
name = conf_get_configname();
if (stat(name, &tmpstat)) {
fprintf(stderr, _("***\n"
"*** Configuration file \"%s\" not found!\n"
"***\n"
"*** Please run some configurator (e.g. \"make oldconfig\" or\n"
"*** \"make menuconfig\" or \"make xconfig\").\n"
"***\n"), name);
exit(1);
}
}
... ...
第一次调用:scripts/kconfig/conf --defconfig=arch/../configs/pangu_basic_defconfig Kconfig
参数解析后:
- input_mode:defconfig
- defconfig_file:arch/../configs/pangu_basic_defconfig
- name = av[optind]:Kconfig
第二次调用:scripts/kconfig/conf --syncconfig Kconfig
参数解析后:
- input_mode:syncconfig
- name = av[optind]:Kconfig
4.2.2 读取Kconfig系统配置文件
设置好input_mode和name后:
conf_parse(name);
//zconfdump(stdout);
if (sync_kconfig) {
name = conf_get_configname();
if (stat(name, &tmpstat)) {
fprintf(stderr, _("***\n"
"*** Configuration file \"%s\" not found!\n"
"***\n"
"*** Please run some configurator (e.g. \"make oldconfig\" or\n"
"*** \"make menuconfig\" or \"make xconfig\").\n"
"***\n"), name);
exit(1);
}
}
- 调用conf_parse(name)从$(srctree)目录下依次查找名为Kconfig的文件,然后将取得的信息存放在链表中;
- 如果syncconfig,即sync_kconfig=1,还要调用conf_get_configname并检查顶层目录下的.config文件是否存在。
4.2.3 读取指定的配置文件
switch (input_mode) {
case defconfig:
if (!defconfig_file)
defconfig_file = conf_get_default_confname();
if (conf_read(defconfig_file)) {
fprintf(stderr,
_("***\n"
"*** Can't find default configuration \"%s\"!\n"
"***\n"),
defconfig_file);
exit(1);
}
break;
case savedefconfig:
case syncconfig:
case ...:
conf_read(NULL);
break;
... ...
default:
break;
}
- 如果是defconfig,调用conf_read(defconfig_file)读取指定的配置文件arch/../configs/pangu_basic_defconfig
- 如果是syncconfig,调用conf_read(NULL)读取生成的.config。(conf_read传入的参数为NULL时,在conf_read_simple中会将读取的文件指向.config)
4.2.4 检查更新设置
接下来:
if (sync_kconfig) {
if (conf_get_changed()) {
name = getenv("KCONFIG_NOSILENTUPDATE");
if (name && *name) {
fprintf(stderr,
_("\n*** The configuration requires explicit update.\n\n"));
return 1;
}
}
}
switch (input_mode) {
... ...
case defconfig:
conf_set_all_new_symbols(def_default);
break;
... ...
case oldconfig:
case listnewconfig:
case syncconfig:
/* Update until a loop caused no more changes */
do {
conf_cnt = 0;
check_conf(&rootmenu);
} while (conf_cnt);
break;
case olddefconfig:
default:
break;
}
- 如果是syncconfig,检查.config是否被改动的,并检查各项设置的有效性
- 如果是defconfig,设置默认值
4.2.5 更新.config,设置默认值
最后:
if (sync_kconfig) {
/* syncconfig is used during the build so we shall update autoconf.
* All other commands are only used to generate a config.
*/
if (conf_get_changed() && conf_write(NULL)) {
fprintf(stderr, _("\n*** Error during writing of the configuration.\n\n"));
exit(1);
}
if (conf_write_autoconf()) {
fprintf(stderr, _("\n*** Error during update of the configuration.\n\n"));
return 1;
}
} else if (input_mode == savedefconfig) {
if (conf_write_defconfig(defconfig_file)) {
fprintf(stderr, _("n*** Error while saving defconfig to: %s\n\n"),
defconfig_file);
return 1;
}
} else if (input_mode != listnewconfig) {
if (conf_write(NULL)) {
fprintf(stderr, _("\n*** Error during writing of the configuration.\n\n"));
exit(1);
}
}
- 如果是syncconfig:
(1)调用conf_get_changed()检查是否更新过,然后调用conf_write(NULL)将更新的项写入到.config文件中
(2)调用conf_write_autoconf()更新以下文件:
include/generated/autoconf.h
include/config/auto.conf.cmd
include/config/tristate.conf
include/config/auto.conf
- 如果是defconfig,进入最后一个(input_mode != listnewconfig)分支,调用conf_write(NULL),将读取到的所有配置写入.config中。
5. 总结
(1)配置时,执行make pangu_basic_defconfig,会根据规则生成scripts/kconfig/conf工具:
pangu_basic_defconfig: scripts_basic outputmakefile FORCE
make -f ./scripts/Makefile.build obj=scripts/kconfig pangu_basic_defconfig
利用生成的scripts/kconfig/conf工具,根据默认配置,在根目录下生成.config文件。
scripts/kconfig/conf --defconfig=arch/../configs/pangu_basic_defconfig Kconfig
(2)编译时,执行
scripts/kconfig/conf --syncconfig Kconfig
* 检查并分析系统中各个`Kconfig`文件
* 同配置时生成的`.config`比较,更新`.config`文件
* 生成相应的其他文件供下一步编译使用
- `include/generated/autoconf.h`
- `include/config/auto.conf.cmd`
- `include/config/tristate.conf`
- `include/config/auto.conf`