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

UBOOT编译--- include/config/auto.conf、 include/config/auto.conf.cmd、 include/generated/autoconf.h (二)的更多相关文章

  1. ././include/linux/kconfig.h:4:32: fatal error: generated/autoconf.h: No such file or directory 解决办法

    我在编写内核驱动模块的时候报了一个非常奇怪的错误,如下图: 在目录下看了一下确实没有发现这个文件,感觉很奇怪,因为我记得之前编译模块是没有错误的,所以不可能是我代码写的有问题. 查阅了资料很多说要清除 ...

  2. 《Linux内核Makefile分析》之 auto.conf, auto.conf.cmd, autoconf.h【转】

    转自:http://blog.sina.com.cn/s/blog_87c063060101l25y.html 转载:http://blog.csdn.net/lcw_202/article/deta ...

  3. vue-loader was used without the corresponding plugin. Make sure to include VueLoaderPlugin in your webpack config.

    默认,webpack无法打包.vue文件,需要安装 相关的loader: cnpm i vue-loader vue-template-compiler -D 提示以下错误信息: Module Err ...

  4. 编译安装php Cannot find MySQL header files under /usr/include/mysql.

    编译php-5.5-6的mysql支持,出现Cannot find MySQL header files under /usr/include/mysql. Note that the MySQL c ...

  5. 错误: 找不到或无法加载主类 Files\apache-activemq-5.10.0\bin\..\conf\login.config

    在启动activemq的时候出现错误:“错误: 找不到或无法加载主类 Files\apache-activemq-5.10.0\bin\..\conf\login.config”,之前用activem ...

  6. VS2010编译错误:是否忘记了向源中添加“#include "stdafx.h

    VS2010编译错误:是否忘记了向源中添加“#include "stdafx.h 编译提示:fatal error C1010: 在查找预编译头时遇到意外的文件结尾.是否忘记了向源中添加“# ...

  7. Linux移植之auto.conf、autoconf.h、Mach-types.h的生成过程简析

    在Linux移植之make uImage编译过程分析中分析了uImage文件产生的过程,在uImage产生的过程中,顺带还产生了其它的一些中间文件.这里主要介绍几个比较关键的文件 1.linux-2. ...

  8. OK335xS U-boot 编译问题&无Linux shell 问题

    /************************************************************************** * OK335xS U-boot 编译问题&am ...

  9. UBOOT编译--- make xxx_deconfig过程详解(一)

    make xxx_deconfig过程详解 1. 前言 2. 概述 3. build变量的定义 4. 目标%config的定义 4.1 依赖 scripts_basic 4.1.1 语句$(if $ ...

  10. 嵌入式Linux驱动学习之路(四)u-boot编译分析

    u-boot编译分析 在配置完成后,执行make开始编译.这里打开Makefile. 首先在目标all前有一句话首先检查是否有include/config.mk文件来判断是否成功配置过. ifeq ( ...

随机推荐

  1. KingbaseESV8R6 垃圾回收原理以及如何预防膨胀

    背景 KingbaseESV8R6支持snapshot too old 那么实际工作中,经常看到表又膨胀了,那么我们讨论一下导致对象膨胀的常见原因有哪些呢? 未开启autovacuum 对于未开启au ...

  2. [Python]-os模块-文件读取

    import os 在Python中,os模块用来处理文件路径,比较方便. os读取文件 在读取文件过程中,最常用的几个功能如下: os.listdir() 获取此目录下的所有目录名,并且存为列表.在 ...

  3. Chrome 浏览器缓存的问题

    浏览器缓存的问题 Chrome 在浏览器中设置: 1 Ctrl + Shift + Delete 2 Ctrl + F5 强制刷新 3 F12 打开控制台 ---> Network ---> ...

  4. ProxySQL 配置MySQL节点

    转载自:https://www.jianshu.com/p/ca1b78b5d615 可以在mysql_servers表和mysql_replication_hostgroups表(可选)中配置后端的 ...

  5. ceph安装Dashboard并开启Object Gateway管理功能

    安装配置dashboard Ceph-Dashboard 是用 Python 开发的一个 Ceph 的监控面板,用来监控 Ceph 的运行状态.同时 提供 REST API 来访问状态数据. 低版本的 ...

  6. 1_Linux

    一. Linux介绍 1.1 引言 在学习Linux之前, 大家先了解开发环境,生产,测试环境 开发环境: 平时大家大多是在Windows或者Mac操作系统下去编写代码进行开发,在开发环境中安装大量的 ...

  7. Kafka Eagle 3.0.1功能预览

    1.概述 最近有同学留言,关于Kafka Eagle的一些使用问题.今天笔者就为大家来详细介绍Kafka Eagle 3.0.1的功能以及使用方法. 2.内容 在3.0.1版本中,EFAK优化了分布式 ...

  8. python_flask开发环境设置

    flask开发环境设置,在powershell终端窗口,可以通过一下方式设置: # 设置当前app实例 $env:FLASK_APP="app:create_app()" #将当前 ...

  9. Go实现优雅关机与平滑重启

    前言 优雅关机就是服务端关机命令发出后不是立即关机,而是等待当前还在处理的请求全部处理完毕后再退出程序,是一种对客户端友好的关机方式.而执行Ctrl+C关闭服务端时,会强制结束进程导致正在访问的请求出 ...

  10. Python函数-2V2

    一.导入 $$ f ( x , y ) = 2 x + 3 y $$ 上面括号里面的就是数学公式里的自变量,自变量就相当于函数里的参数. 二.为什么要有参数 如果一个大楼里有两种尺寸不一的窗户,显然在 ...