u-boot-2016.09 make工具之conf

时间:2021-08-15 06:34:58

u-boot-2016.09 make工具之conf

1. 概述

conf工具的源码位于scripts/kconfig.

2. conf的编译

u-boot编译配置执行make rpi_3_32b_defconfig V=1,对应于顶层makefile中的%config目标:

%config: scripts_basic outputmakefile FORCE
    $(Q)$(MAKE) $(build)=scripts/kconfig $@

在这里展开后相当于:

rpi_3_32b_defconfig: scripts_basic outputmakefile FORCE
    make -f ./scripts/Makefile.build obj=scripts/kconfig rpi_3_32b_defconfig

这个规则中,生成rpi_3_32b_defconfig后,会指定参数obj=scripts/kconfigrpi_3_32b_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)

在这里展开为:

rpi_3_32b_defconfig: scripts/kconfig/conf scripts/kconfig/conf --defconfig=arch/../configs/rpi_3_32b_defconfig Kconfig

由于rpi_3_32b_defconfig依赖于scripts/kconfig/conf,所以conf将会被编译。
conf依赖于conf.czconf.tab.c,后者进一步包含所需要的词法分析文件,整个编译链接过程如下:

make -f ./scripts/Makefile.build obj=scripts/kconfig rpi_3_32b_defconfig
  cc -Wp,-MD,scripts/kconfig/.conf.o.d -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer   -I/usr/include/ncursesw   -DCURSES_LOC="<ncurses.h>" -DLOCALE   -c -o scripts/kconfig/conf.o scripts/kconfig/conf.c
  cat scripts/kconfig/zconf.tab.c_shipped > scripts/kconfig/zconf.tab.c
  cat scripts/kconfig/zconf.lex.c_shipped > scripts/kconfig/zconf.lex.c
  cat scripts/kconfig/zconf.hash.c_shipped > scripts/kconfig/zconf.hash.c
  cc -Wp,-MD,scripts/kconfig/.zconf.tab.o.d -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer   -I/usr/include/ncursesw   -DCURSES_LOC="<ncurses.h>" -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  

(执行make xxx_defconfig时打开V=1选项可以看到整个过程)

除了make xxx_defconfig配置过程外,u-boot正式编译过程中也会有对应的依赖,见顶层的makefile:

include/config/%.conf: $(KCONFIG_CONFIG) include/config/auto.conf.cmd
    $(Q)$(MAKE) -f $(srctree)/Makefile silentoldconfig

在这个规则中也会根据silentoldconfig再次检查并更新scripts/kconfig/conf文件。

3. conf的调用

整个u-boot的配置和编译过程中conf被调用了2次。

3.1 make配置过程调用

u-boot的make配置过程中,完成conf的编译后会随即调用命令:

scripts/kconfig/conf --defconfig=arch/../configs/rpi_3_32b_defconfig Kconfig

去生成根目录下的.config文件。
以下是生成前后的对比:
u-boot-2016.09 make工具之conf

3.2 make编译过程调用

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 silentoldconfig
    @# 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 ./scripts/Makefile.build obj=scripts/kconfig silentoldconfig
mkdir -p include/config include/generated
scripts/kconfig/conf  --silentoldconfig Kconfig
make -f ./scripts/Makefile.autoconf || \
        { rm -f include/config/auto.conf; false; }
if [ -d arch/arm/mach-bcm283x/include/mach ]; then  \
        dest=../../mach-bcm283x/include/mach;           \
    else                                \
        dest=arch-bcm283x;          \
    fi;                             \
    ln -fsn $dest arch/arm/include/asm/arch
...这里省略部分log...
touch include/config/auto.conf

实际执行$(Q)$(MAKE) -f $(srctree)/Makefile silentoldconfig命令的结果就是检查并更新scripts/kconfig/conf文件,然后调用之:
scripts/kconfig/conf --silentoldconfig Kconfig

为了检查这个命令的输出,可以直接在命令行上执行命令:

ygu@ubuntu:/opt/work/u-boot/u-boot-2016.09$ make -f ./Makefile silentoldconfig V=1
make -f ./scripts/Makefile.build obj=scripts/basic
rm -f .tmp_quiet_recordmcount
make -f ./scripts/Makefile.build obj=scripts/kconfig silentoldconfig
mkdir -p include/config include/generated
scripts/kconfig/conf  --silentoldconfig Kconfig

其实跟命令行上运行make silentoldconfig是一样的效果。
执行结果就是:

  • include/config下生成auto.confauto.conf.cmd,以及tristate.conf
  • include/generated下生成autoconf.h文件;

先执行make rpi_3_32b_defconfig,在执行make silentoldconfig的对比结果如下:
u-boot-2016.09 make工具之conf

3.3 调用简述

简而言之:

  • 第一次调用生成.config

    scripts/kconfig/conf --defconfig=arch/../configs/rpi_3_32b_defconfig Kconfig

  • 第二次调用生成auto.confautoconf.h

    scripts/kconfig/conf --silentoldconfig Kconfig

4. conf的源码分析

confconf.ozconf.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.cconf主程序的文件,通过分析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 silentoldconfig:
            sync_kconfig = 1;
            break;
        case defconfig:
        case savedefconfig:
            defconfig_file = optarg;
            break;
        ...
        }
    }
    if (ac == optind) {
        printf(_("%s: Kconfig file missing\n"), av[0]);
        conf_usage(progname);
        exit(1);
    }
    name = av[optind];
    ...

第一次调用 scripts/kconfig/conf --defconfig=arch/../configs/rpi_3_32b_defconfig Kconfig

参数解析后:

  • input_mode: defconfig_file
  • defconfig_file: arch/../configs/rpi_3_32b_defconfig
  • name = av[optind]: Kconfig

第二次调用 scripts/kconfig/conf --silentoldconfig Kconfig

参数解析后:

  • input_mode: silentoldconfig,并设置sync_kconfig为1
  • name = av[optind]: Kconfig

4.2.2 读取Kconfig系统配置文件

设置好input_modename后:

    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的文件,然后将取得的信息存放到链表中。
  • 如果是silentoldconfig,即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)) {
            printf(_("***\n"
                "*** Can't find default configuration \"%s\"!\n"
                "***\n"), defconfig_file);
            exit(1);
        }
        break;
    case savedefconfig:
    case silentoldconfig:
    case ...:
        conf_read(NULL);
        break;
    ...
    default:
        break;
    }
  • 如果是defconfig,调用conf_read(defconfig_file)读取指定的配置文件arch/../configs/rpi_3_32b_defconfig
  • 如果是silentoldconfig,调用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;
            }
        }
        valid_stdin = tty_stdio;
    } 

    switch (input_mode) {
    case ...:
        break;
    case defconfig:
        conf_set_all_new_symbols(def_default);
        break;
    case savedefconfig:
        break;
    case ...:
    case silentoldconfig:
        /* Update until a loop caused no more changes */
        do {
            conf_cnt = 0;
            check_conf(&rootmenu);
        } while (conf_cnt &&
             (input_mode != listnewconfig &&
              input_mode != olddefconfig));
        break;
    }
  • 如果是silentoldconfig,检查.config是否被改动过,并检查各项设置的有效性
  • 如果是defconfig,设置默认值

4.2.5 更新.config,生成相应文件

最后:

    if (sync_kconfig) {
        /* silentoldconfig 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);
        }
    }
  • 如果是silentoldconfig

    • 调用conf_get_changed()检查是否更新过,然后调用conf_write(NULL)将更新的项写入到.config文件中
    • 调用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. 总结

  • 配置时执行make rpi_3_32b_defconfig会根据规则生成scripts/kconfig/conf
    rpi_3_32b_defconfig: scripts_basic outputmakefile FORCE
        make -f ./scripts/Makefile.build obj=scripts/kconfig rpi_3_32b_defconfig
  • 生成conf工具后立即调用并根据默认配置在根目录下生成.config文件:
 scripts/kconfig/conf --defconfig=arch/../configs/rpi_3_32b_defconfig Kconfig
  • 编译时执行silentoldconfig
    scripts/kconfig/conf --silentoldconfig Kconfig
* 检查并分析系统中各个`Kconfig`文件
* 同配置时生成的`.config`比较,更新`.config`文件
* 生成相应的其它文件供下一步编译使用:
    - `include/generated/autoconf.h`
    - `include/config/auto.conf.cmd`
    - `include/config/tristate.conf`
    - `include/config/auto.conf`