UBOOT编译--- include/config/auto.conf、 include/config/auto.conf.cmd、 include/generated/autoconf.h (二)
1. 前言
UBOOT版本:uboot2018.03,开发板myimx8mmek240。
2. 背景
在编译构建目标时(如 make xxx),顶层 Makefile 的 dot-config 变量值设置为 1 。 如下:
#note: 顶层Makefile dot-config := 1 ifneq ($(filter $(no-dot-config-targets), $(MAKECMDGOALS)),) ifeq ($(filter-out $(no-dot-config-targets), $(MAKECMDGOALS)),) dot-config := 0 endif endi
在顶层 Makefile 中:
#note: 顶层Makefile KCONFIG_CONFIG ?= .config export KCONFIG_CONFIG ifeq ($(dot-config),1) # Read in config -include include/config/auto.conf (1) # Read in dependencies to all Kconfig* files, make sure to run # oldconfig if changes are detected. -include include/config/auto.conf.cmd (2) # To avoid any implicit rule to kick in, define an empty command $(KCONFIG_CONFIG) include/config/auto.conf.cmd: ; # 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 #endif
其中(1)(2)两行尝试包含 auto.conf 和 auto.conf.cmd 这两个文件。由于使用 -include 进行声明,所以即使这两个文件不存在也不会报错退出,比如在第 1 次编译uboot,且已经配置好 .config 文件时,这两个文件还未生成。
假如我们已经编译好了xxx_deconfig 这个目标,那么我们会在 include/config 这个目录下看到 auto.conf 和 auto.conf.cmd 这两个文件。
从 include/config/%.conf: $(KCONFIG_CONFIG) include/config/auto.conf.cmd 这条规则可以知道,auto.conf 文件依赖于 $(KCONFIG_CONFIG) 和 include/config/auto.conf.cmd 。其中 $(KCONFIG_CONFIG) 变量的值就是 .config 这个配置文件。
3. 分析
# To avoid any implicit rule to kick in, define an empty command $(KCONFIG_CONFIG) include/config/auto.conf.cmd: ;
现在仍然假设 auto.conf 和 auto.conf.cmd 还没有生成,那么由上面这条语句可以知道,该语句中的目标没有依赖,也没有生成它的规则命令,所以可想 GNU Make 本身无法生成 auto.conf.cmd 的。然后该条语句后面的一个分号表明,这两个目标被强制是最新的。所以下面标注的这几条命令得以执行:
#note: 顶层Makefile include/config/%.conf: $(KCONFIG_CONFIG) include/config/auto.conf.cmd $(Q)$(MAKE) -f $(srctree)/Makefile silentoldconfig (1) @# 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; } (2) @# 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 (3)
3.1 语句 $ (Q) $(MAKE) -f $(srctree)/Makefile silentoldconfig
这里我们看到要生成一个目标 silentoldconfig ,这个目标定义在 scripts/kconfig/Makefile 中。因为这里使用 -f 选项重新指定了顶层 Makefile,而目标又是 silentoldconfig ,所以该命令最终会在顶层 Makefile 里执行:
#note: 顶层Makefile # We need some generic definitions (do not try to remake the file). scripts/Kbuild.include: ; include scripts/Kbuild.include //注意这个引用 ...... %config: scripts_basic outputmakefile FORCE $(Q)$(MAKE) $(build)=scripts/kconfig $@
在scripts/kconfig/Makefile中(至于为什么会引用scripts/kconfig/Makefile,参见UBOOT编译--- make xxx_deconfig过程详解(一)4.1.4.3 小节)
# note:scripts/kconfig/Makefile ifdef KBUILD_KCONFIG Kconfig := $(KBUILD_KCONFIG) else Kconfig := Kconfig endif ...... silentoldconfig: $(obj)/conf $(Q)mkdir -p include/config include/generated $(Q)test -e include/generated/autoksyms.h || \ touch include/generated/autoksyms.h $< $(silent) --$@ $(Kconfig)
从上面看到,silentoldconfig 目标需要依赖 conf 这个程序,该程序也在 scripts/kconfig 目录下生成。
$ < $ (silent) --$ @ $ (Kconfig)该条命令相当于 scripts/kconfig/conf -s --myimx8mmek240-8mm-2g_defconfig .config 。
其中scripts/kconfig/conf的生成:参见UBOOT编译--- make xxx_deconfig过程详解(一)4.4小节。
conf 程序的源代码的主函数在同目录的 conf.c 文件中,在 main() 函数中看到:
#note:/scripts/kconfig/conf.c enum input_mode { oldaskconfig, silentoldconfig, //1 oldconfig, allnoconfig, allyesconfig, allmodconfig, alldefconfig, randconfig, defconfig, savedefconfig, listnewconfig, olddefconfig, } input_mode = oldaskconfig; ...... 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;
所以,
(1) 在使用 s 参数时,message的回调函数被注册为空,即conf_message函数无输出打印;
(2) 同时参数opt被赋值为1,等价于silentoldconfig;sync_kconfig = 1;
(3) input_mode = silentoldconfig;
同样在 main() 函数还看到:
#note:/scripts/kconfig/confdata.c const char *conf_get_configname(void) { char *name = getenv("KCONFIG_CONFIG"); return name ? name : ".config"; } #note:/scripts/kconfig/conf.c 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); } }
上面代码中,如果我们从未配置过UBOOT,那么就会打印出错误信息,然后退出。这里假设已经配置过UBOOT(参见UBOOT编译--- make xxx_deconfig过程详解(一)),并生成了 .config 文件,那么在 main() 函数中会来到:
#note:/scripts/kconfig/conf.c 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 oldaskconfig: case oldconfig: case listnewconfig: case olddefconfig: conf_read(NULL); break;
前面已经讲过由于使用 -s 选项,则 input_mode 为silentoldconfig,所以这里会执行 conf_read(NULL)函数。
conf_read(NULL) 函数用来读取 .config 文件。读取的各种相关内容主要存放在一个 struct symbol 结构链表里,而各个结构的相关指针则放在一个 symbol_hash[] 的数组中,对于数组中元素的寻找通过 fnv32 哈希算法进行定位。
最后会来到 conf.c 中的底部:
#note:/scripts/kconfig/conf.c 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 目标时 sync_kconfig 变量才会为 1 。
(1)在 if (conf_get_changed() && conf_write(NULL)) 这个判断里,conf_get_changed() 函数判断 .config 文件是否做过变动,如果是,那么会调用 conf_write(NULL) 来重新写 .config 文件。实际上,对于 defconfig, oldconfig,menuconfig,***xxx_deconfig 等目标来说,conf 程序最终也是调用 conf_write() 函数将配置结果写入 .config 文件中。
(2)确保了 .config 已经最新后,那么调用 conf_write_autoconf() 生成 auto.conf,auto.conf.cmd 以及 autoconf.h 这 3 个文件。
来到 conf_write_autoconf() 函数:
#note:/scripts/kconfig/confdata.c int conf_write_autoconf(void) { struct symbol *sym; const char *name; FILE *out, *tristate, *out_h; int i; sym_clear_all_valid(); file_write_dep("include/config/auto.conf.cmd"); if (conf_split_config()) return 1; ......
在 conf_write_autoconf() 里,调用 file_write_dep("include/config/auto.conf.cmd"); 函数将相关内容写入 auto.conf.cmd 文件。在生成的 auto.conf.cmd 文件中可以看到:
deps_config := \ test/overlay/Kconfig \ test/env/Kconfig \ test/dm/Kconfig \ ...... board/abilis/tb100/Kconfig \ arch/arc/Kconfig \ arch/Kconfig \ Kconfig include/config/auto.conf: \ $(deps_config) ifneq "$(UBOOTVERSION)" "2018.03" include/config/auto.conf: FORCE endif $(deps_config): ;
可以看到 auto.conf 文件中的内容依赖于 $(deps_config) 变量里定义的东西,这些东西基本上是各个目录下的 Kconfig 以及其它一些相关文件。
仍然在 conf_write_autoconf() 里,分别建立了 .tmpconfig,.tmpconfig_tristate 和 .tmpconfig.h 这 3 个临时文件,然后将文件头的注释部分分别写入到这几个临时文件中,接着在 for_all_symbols(i, sym) 这个循环里(是一个宏)里将相关内容分别写入到这几个文件中。
#note:/scripts/kconfig/confdata.c out = fopen(".tmpconfig", "w"); if (!out) return 1; tristate = fopen(".tmpconfig_tristate", "w"); if (!tristate) { fclose(out); return 1; } out_h = fopen(".tmpconfig.h", "w"); if (!out_h) { fclose(out); fclose(tristate); return 1; } conf_write_heading(out, &kconfig_printer_cb, NULL); conf_write_heading(tristate, &tristate_printer_cb, NULL); conf_write_heading(out_h, &header_printer_cb, NULL); for_all_symbols(i, sym) { sym_calc_value(sym); if (!(sym->flags & SYMBOL_WRITE) || !sym->name) continue; /* write symbol to auto.conf, tristate and header files */ conf_write_symbol(out, sym, &kconfig_printer_cb, (void *)1); conf_write_symbol(tristate, sym, &tristate_printer_cb, (void *)1); conf_write_symbol(out_h, sym, &header_printer_cb, NULL); } fclose(out); fclose(tristate); fclose(out_h);
在最后一段代码中,将这几个临时文件进行改名:
#note:/scripts/kconfig/confdata.c const char *conf_get_autoconfig_name(void) { char *name = getenv("KCONFIG_AUTOCONFIG"); return name ? name : "include/config/auto.conf"; } ...... name = getenv("KCONFIG_AUTOHEADER"); if (!name) name = "include/generated/autoconf.h"; if (rename(".tmpconfig.h", name)) return 1; name = getenv("KCONFIG_TRISTATE"); if (!name) name = "include/config/tristate.conf"; if (rename(".tmpconfig_tristate", name)) return 1; name = conf_get_autoconfig_name(); /* * This must be the last step, kbuild has a dependency on auto.conf * and this marks the successful completion of the previous steps. */ if (rename(".tmpconfig", name)) return 1;
从上面可以看到,分别生成了以下几个文件:
include/generated/autoconf.h include/config/tristate.conf include/config/auto.conf
3.2 语句 $ (Q) $(MAKE) -f $(srctree)/scripts/Makefile.autoconf
由于篇幅的原因,放在下一讲。
4. 总结
(1)auto.conf 和 .config 的差别是:auto.conf 里去掉了 .config 中的注释项目以及空格行,其它的都一样。
(2) include/generated/autoconf.h 头文件由UBOOT本身使用,主要用来预处理 C 代码,把auto.conf中的定义转换成C语言的宏定义。。
(1)在 .config 或 auto.conf 中后接"=y"的项,如:
CONFIG_OF_LIBFDT=y
在 autoconfig.h 中会被定义为:
#define CONFIG_OF_LIBFDT 1
(2)在 .config 或 auto.conf 中后接字符串值的项,如:
CONFIG_DEFAULT_FDT_FILE="myimx8mmek240-8mm.dtb"
在 autoconfig.h 中会被定义为:
#define CONFIG_DEFAULT_FDT_FILE "myimx8mmek240-8mm.dtb"
(3)在 .config 或 auto.conf 中后接数值的项,如:
CONFIG_SDP_LOADADDR=0x40400000
在 autoconfig.h 中会被定义为:
#define CONFIG_SDP_LOADADDR 0x40400000
本文来自博客园,作者:BSP-路人甲,转载请注明原文链接:https://www.cnblogs.com/jianhua1992/p/16852774.html,并保留此段声明,否则保留追究法律责任的权利。
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步