【u-boot-2018.11】make工具之conf

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`

猜你喜欢

转载自blog.csdn.net/linuxweiyh/article/details/100528718