首页 > 代码库 > 《Linux内核Makefile分析》之 auto.conf, auto.conf.cmd, autoconf.h【转】
《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/details/6661364
在编译构建性目标时(如 make vmlinux),顶层 Makefile 的 $(dot-config) 变量值为 1 。
在顶层 Makefile 的 497-504 行看到:
?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | ifeq ( $(dot-config) ,1) # Read in config - include include /config/auto .conf ifeq ( $(KBUILD_EXTMOD) ,) # Read in dependencies to all Kconfig* files,make sure to run # oldconfig if changes aredetected. - include include /config/auto .conf.cmd # To avoid any implicit rule to kick in, definean empty command $(KCONFIG_CONFIG) include /config/auto .conf.cmd: ; # If .config is newer thaninclude/config/auto.conf, someone tinkered # with it and forgot to run makeoldconfig. # if auto.conf.cmd is missing then we areprobably in a cleaned tree so # we execute the config step to be sure tocatch updated Kconfig files include /config/ %.conf: $(KCONFIG_CONFIG) include /config/auto .conf.cmd $(Q) $(MAKE) -f $(srctree) /Makefile silentoldconfig |
其中,
引用-include include/config/auto.conf
-include include/config/auto.conf.cmd
这两行尝试包含 auto.conf 和 auto.conf.cmd 这两个文件。由于使用 -include进行声明,所以即使这两个文件不存在也不会报错退出,比如在第 1 次编译内核,且已经配置好 .config文件时,这两个文件还未生成。
假如我们已经编译好了 vmlinux 这个目标,那么我们会在 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这个配置文件。那么
include/config/auto.conf.cmd这个文件应该在什么时候生成?
现在仍然假设 auto.conf 和 auto.conf.cmd 还没有生成,那么由上面的 $(KCONFIG_CONFIG) include/config/auto.conf.cmd:; 这条语句知道,该语句中的目标没有依赖,也没有生成它的规则命令,所以可想 GNU Make 本身无法生成auto.conf.cmd 的。然后该条语句后面的一个分号表明,这两个目标被强制是最新的,所以下面这条命令得以执行:
$(Q)$(MAKE) -f $(srctree)/Makefilesilentoldconfig
这里我们看到要生成一个目标 silentoldconfig ,这个目标定义在 scripts/kconfig/Makefile中。因为这里使用 -f 选项重新指定了顶层 Makefile,而目标又是 silentoldconfig ,所以该命令最终会在顶层Makefile 的 462-464 这里执行:
这时,我们来到 scripts/kconfig/Makefile 文件里。在该文件的 32-34 行看到:
从上面看到,silentoldconfig 目标需要依赖 conf 这个程序,该程序也在 scripts/kconfig目录下生成。
$< -s $(Kconfig) 该条命令相当于conf -s $(Kconfig) ,这里 $(Kconfig) 是位于不同平台目录下的 Kconfig 文件,比如在 x86平台就是 arch/x86/Kconfig 。
conf 程序的源代码的主函数在同目录的 conf.c 文件中,在 main() 函数中看到:
所以,在使用 s 参数时,sync_kconfig 这个变量会为 1 。同样在 main() 函数还看到:
上面代码中,如果我们从未配置过内核,那么就会打印出错误信息,然后退出。这里假设已经配置过内核,并生成了 .config 文件,那么在main() 函数中会来到:
由于使用 s 选项,则 input_mode 为 ask_silent,所以这里会执行 conf_read(NULL);函数。
conf_read(NULL); 函数用来读取 .config 文件。读取的各种相关内容主要存放在一个 struct symbol结构链表里,而各个结构的相关指针则放在一个 symbol_hash[] 的数组中,对于数组中元素的寻找通过 fnv32哈希算法进行定位。
最后会来到 conf.c 中的底部:
实际上也只有当处理 silentoldconfig 目标是 sync_kconfig 变量才会为 1 。上面代码中的conf_write_autoconf() 函数就用来生成 auto.conf, auto.conf.cmd 以及autoconf.h 这 3 个文件。
在 if (conf_get_changed() && conf_write(NULL))这个判断里,conf_get_changed() 函数判断 .config 文件是否做过变动,如果是,那么会调用conf_write(NULL) 来重新写 .config 文件。实际上,对于 defconfig, oldconfig,menuconfig 等目标来说,conf 程序最终也是调用 conf_write() 函数将配置结果写入 .config文件中(最后那个 else 里的内容便是)。
确保了 .config 已经最新后,那么调用 conf_write_autoconf() 生成auto.conf,auto.conf.cmd 以及 autoconf.h 这 3 个文件。
来到 conf_write_autoconf() 函数:
在 conf_write_autoconf() 里,调用file_write_dep("include/config/auto.conf.cmd"); 函数将相关内容写入auto.conf.cmd 文件。在生成的 auto.conf.cmd 文件中可以看到:
可以看到 auto.conf 文件中的内容依赖于 $(deps_config) 变量里定义的东西,这些东西基本上是各个目录下的Kconfig 以及其它一些相关文件。
auto.config 和 .config 的差别是在 auto.config 里去掉了 .config中的注释项目以及空格行,其它的都一样。
仍然在 conf_write_autoconf() 里,分别建立了 .tmpconfig,.tmpconfig_tristate 和.tmpconfig.h 这 3 个临时文件:
然后将文件头的注释部分分别写入到这几个临时文件中:
接着在 for_all_symbols(i, sym) 这个循环里(是一个宏)里将相关内容分别写入到这几个文件中。
在最后一段代码中,将这几个临时文件进行改名:
上面代码中的 conf_get_autoconfig_name() 实现为:
从上面可以看到,分别生成了以下几个文件:
其中 include/generated/autoconf.h 头文件由内核本身使用,主要用来预处理 C 代码。比如在 .config或 auto.conf 中定义要编译为模块的项,如:
CONFIG_DEBUG_NX_TEST=m
在 autoconf.h 中会被定义为:
#define CONFIG_DEBUG_NX_TEST_MODULE 1
在 .config 或 auto.conf 后接字符串值的项,如:
CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config"
在 autoconfig.h 中会被定义为:
#define CONFIG_DEFCONFIG_LIST"/lib/modules/$UNAME_RELEASE/.config"
同样对应于 int 型的项如 CONFIG_HZ=1000 在 autoconf.h 中被定义为 #define CONFIG_HZ1000 。
现在仍然假设 auto.conf 和 auto.conf.cmd 还没有生成,那么由上面的 $(KCONFIG_CONFIG) include/config/auto.conf.cmd:; 这条语句知道,该语句中的目标没有依赖,也没有生成它的规则命令,所以可想 GNU Make 本身无法生成auto.conf.cmd 的。然后该条语句后面的一个分号表明,这两个目标被强制是最新的,所以下面这条命令得以执行:
$(Q)$(MAKE) -f $(srctree)/Makefilesilentoldconfig
这里我们看到要生成一个目标 silentoldconfig ,这个目标定义在 scripts/kconfig/Makefile中。因为这里使用 -f 选项重新指定了顶层 Makefile,而目标又是 silentoldconfig ,所以该命令最终会在顶层Makefile 的 462-464 这里执行:
?
1 2 3 | %config: scripts_basic outputmakefileFORCE $(Q) mkdir -p include /linux include /config $(Q) $(MAKE) $(build) =scripts /kconfig $@ |
这时,我们来到 scripts/kconfig/Makefile 文件里。在该文件的 32-34 行看到:
?
1 2 3 | silentoldconfig: $(obj) /conf $(Q) mkdir -p include /generated $<-s $(Kconfig) |
从上面看到,silentoldconfig 目标需要依赖 conf 这个程序,该程序也在 scripts/kconfig目录下生成。
$< -s $(Kconfig) 该条命令相当于conf -s $(Kconfig) ,这里 $(Kconfig) 是位于不同平台目录下的 Kconfig 文件,比如在 x86平台就是 arch/x86/Kconfig 。
conf 程序的源代码的主函数在同目录的 conf.c 文件中,在 main() 函数中看到:
?
1 2 3 4 5 6 7 8 9 10 | while ((opt =getopt(ac, av, "osdD:nmyrh" )) != -1){ switch (opt) { case ‘o‘ : input_mode= ask_silent; break ; case ‘s‘ : input_mode= ask_silent; sync_kconfig= 1; break ; ...... |
所以,在使用 s 参数时,sync_kconfig 这个变量会为 1 。同样在 main() 函数还看到:
?
1 2 3 4 5 6 7 8 9 10 11 12 13 | if (sync_kconfig) { name= conf_get_configname(); if (stat(name, &tmpstat)) { fprintf (stderr,_( "***\n" "***You have not yet configured your kernel!\n" "***(missing kernel config file \"%s\")\n" "***\n" "***Please run some configurator (e.g. \"make oldconfig\"or\n" "***\"make menuconfig\" or \"make xconfig\").\n" "***\n" ),name); exit (1); } } |
上面代码中,如果我们从未配置过内核,那么就会打印出错误信息,然后退出。这里假设已经配置过内核,并生成了 .config 文件,那么在main() 函数中会来到:
?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | switch (input_mode) { case set_default: 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 ask_silent: case ask_all: case ask_new: conf_read(NULL); break ; ...... |
由于使用 s 选项,则 input_mode 为 ask_silent,所以这里会执行 conf_read(NULL);函数。
conf_read(NULL); 函数用来读取 .config 文件。读取的各种相关内容主要存放在一个 struct symbol结构链表里,而各个结构的相关指针则放在一个 symbol_hash[] 的数组中,对于数组中元素的寻找通过 fnv32哈希算法进行定位。
最后会来到 conf.c 中的底部:
?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | if (sync_kconfig) { if (conf_get_changed() &&conf_write(NULL)) { fprintf (stderr,_( "\n*** Error during writing ofthe kernel configuration.\n\n" )); exit (1); } if (conf_write_autoconf()) { fprintf (stderr,_( "\n*** Error during update of thekernel configuration.\n\n" )); return 1; } } else { if (conf_write(NULL)) { fprintf (stderr,_( "\n*** Error during writing ofthe kernel configuration.\n\n" )); exit (1); } } |
实际上也只有当处理 silentoldconfig 目标是 sync_kconfig 变量才会为 1 。上面代码中的conf_write_autoconf() 函数就用来生成 auto.conf, auto.conf.cmd 以及autoconf.h 这 3 个文件。
在 if (conf_get_changed() && conf_write(NULL))这个判断里,conf_get_changed() 函数判断 .config 文件是否做过变动,如果是,那么会调用conf_write(NULL) 来重新写 .config 文件。实际上,对于 defconfig, oldconfig,menuconfig 等目标来说,conf 程序最终也是调用 conf_write() 函数将配置结果写入 .config文件中(最后那个 else 里的内容便是)。
确保了 .config 已经最新后,那么调用 conf_write_autoconf() 生成auto.conf,auto.conf.cmd 以及 autoconf.h 这 3 个文件。
来到 conf_write_autoconf() 函数:
在 conf_write_autoconf() 里,调用file_write_dep("include/config/auto.conf.cmd"); 函数将相关内容写入auto.conf.cmd 文件。在生成的 auto.conf.cmd 文件中可以看到:
?
1 2 | include /config/auto .conf: \ $(deps_config) |
可以看到 auto.conf 文件中的内容依赖于 $(deps_config) 变量里定义的东西,这些东西基本上是各个目录下的Kconfig 以及其它一些相关文件。
auto.config 和 .config 的差别是在 auto.config 里去掉了 .config中的注释项目以及空格行,其它的都一样。
仍然在 conf_write_autoconf() 里,分别建立了 .tmpconfig,.tmpconfig_tristate 和.tmpconfig.h 这 3 个临时文件:
?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | 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; } |
然后将文件头的注释部分分别写入到这几个临时文件中:
?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | sym= sym_lookup( "KERNELVERSION" ,0); sym_calc_value(sym); time (&now); fprintf (out, "#\n" "#Automatically generated make config: don‘t edit\n" "#Linux kernel version: %s\n" "#%s" "#\n" , sym_get_string_value(sym), ctime (&now)); fprintf (tristate, "#\n" "#Automatically generated - do not edit\n" "\n" ); fprintf (out_h, "\n" "#defineAUTOCONF_INCLUDED\n" , sym_get_string_value(sym), ctime (&now)); |
接着在 for_all_symbols(i, sym) 这个循环里(是一个宏)里将相关内容分别写入到这几个文件中。
在最后一段代码中,将这几个临时文件进行改名:
?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | 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(); if ( rename ( ".tmpconfig" ,name)) return 1; |
上面代码中的 conf_get_autoconfig_name() 实现为:
?
1 2 3 4 5 6 | const char *conf_get_autoconfig_name( void ) { char *name = getenv ( "KCONFIG_AUTOCONFIG" ); return name ? name : "include/config/auto.conf" ; } |
从上面可以看到,分别生成了以下几个文件:
引用include/generated/autoconf.h
include/config/tristate.conf
include/config/auto.conf
其中 include/generated/autoconf.h 头文件由内核本身使用,主要用来预处理 C 代码。比如在 .config或 auto.conf 中定义要编译为模块的项,如:
CONFIG_DEBUG_NX_TEST=m
在 autoconf.h 中会被定义为:
#define CONFIG_DEBUG_NX_TEST_MODULE 1
在 .config 或 auto.conf 后接字符串值的项,如:
CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config"
在 autoconfig.h 中会被定义为:
#define CONFIG_DEFCONFIG_LIST"/lib/modules/$UNAME_RELEASE/.config"
同样对应于 int 型的项如 CONFIG_HZ=1000 在 autoconf.h 中被定义为 #define CONFIG_HZ1000 。
《Linux内核Makefile分析》之 auto.conf, auto.conf.cmd, autoconf.h【转】
声明:以上内容来自用户投稿及互联网公开渠道收集整理发布,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任,若内容有误或涉及侵权可进行投诉: 投诉/举报 工作人员会在5个工作日内联系你,一经查实,本站将立刻删除涉嫌侵权内容。