<svg xmlns="http://www.w3.org/2000/svg" style="display: none;"> <path stroke-linecap="round" d="M5,0 0,2.5 5,5z" id="raphael-marker-block" style="-web ...
auto.conf、 auto.conf.cmd、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");
<span class="token keyword">return</span> name <span class="token operator">?</span> name <span class="token operator">:</span> <span class="token string">".config"</span><span class="token punctuation">;</span>
}
#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;
<span class="token function">sym_clear_all_valid</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token function">file_write_dep</span><span class="token punctuation">(</span><span class="token string">"include/config/auto.conf.cmd"</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token function">conf_split_config</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token keyword">return</span> <span class="token number">1</span><span class="token punctuation">;</span>
......
在 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 \
<span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span> board<span class="token operator">/</span>abilis<span class="token operator">/</span>tb100<span class="token operator">/</span>Kconfig \ arch<span class="token operator">/</span>arc<span class="token operator">/</span>Kconfig \ arch<span class="token operator">/</span>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 <span class="token operator">=</span> <span class="token function">fopen</span><span class="token punctuation">(</span><span class="token string">".tmpconfig_tristate"</span><span class="token punctuation">,</span> <span class="token string">"w"</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span>tristate<span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span> <span class="token function">fclose</span><span class="token punctuation">(</span>out<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">return</span> <span class="token number">1</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> out_h <span class="token operator">=</span> <span class="token function">fopen</span><span class="token punctuation">(</span><span class="token string">".tmpconfig.h"</span><span class="token punctuation">,</span> <span class="token string">"w"</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span>out_h<span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span> <span class="token function">fclose</span><span class="token punctuation">(</span>out<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token function">fclose</span><span class="token punctuation">(</span>tristate<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">return</span> <span class="token number">1</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token function">conf_write_heading</span><span class="token punctuation">(</span>out<span class="token punctuation">,</span> <span class="token operator">&</span>kconfig_printer_cb<span class="token punctuation">,</span> <span class="token constant">NULL</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token function">conf_write_heading</span><span class="token punctuation">(</span>tristate<span class="token punctuation">,</span> <span class="token operator">&</span>tristate_printer_cb<span class="token punctuation">,</span> <span class="token constant">NULL</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token function">conf_write_heading</span><span class="token punctuation">(</span>out_h<span class="token punctuation">,</span> <span class="token operator">&</span>header_printer_cb<span class="token punctuation">,</span> <span class="token constant">NULL</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token function">for_all_symbols</span><span class="token punctuation">(</span>i<span class="token punctuation">,</span> sym<span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span> <span class="token function">sym_calc_value</span><span class="token punctuation">(</span>sym<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span><span class="token punctuation">(</span>sym<span class="token operator">-></span>flags <span class="token operator">&</span> SYMBOL_WRITE<span class="token punctuation">)</span> <span class="token operator">||</span> <span class="token operator">!</span>sym<span class="token operator">-></span>name<span class="token punctuation">)</span> <span class="token keyword">continue</span><span class="token punctuation">;</span> <span class="token comment">/* write symbol to auto.conf, tristate and header files */</span> <span class="token function">conf_write_symbol</span><span class="token punctuation">(</span>out<span class="token punctuation">,</span> sym<span class="token punctuation">,</span> <span class="token operator">&</span>kconfig_printer_cb<span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token keyword">void</span> <span class="token operator">*</span><span class="token punctuation">)</span><span class="token number">1</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token function">conf_write_symbol</span><span class="token punctuation">(</span>tristate<span class="token punctuation">,</span> sym<span class="token punctuation">,</span> <span class="token operator">&</span>tristate_printer_cb<span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token keyword">void</span> <span class="token operator">*</span><span class="token punctuation">)</span><span class="token number">1</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token function">conf_write_symbol</span><span class="token punctuation">(</span>out_h<span class="token punctuation">,</span> sym<span class="token punctuation">,</span> <span class="token operator">&</span>header_printer_cb<span class="token punctuation">,</span> <span class="token constant">NULL</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token function">fclose</span><span class="token punctuation">(</span>out<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token function">fclose</span><span class="token punctuation">(</span>tristate<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token function">fclose</span><span class="token punctuation">(</span>out_h<span class="token punctuation">)</span><span class="token punctuation">;</span>
在最後一段代碼中,將這幾個臨時文件進行改名:
#note:/scripts/kconfig/confdata.c const char *conf_get_autoconfig_name(void) { char *name = getenv("KCONFIG_AUTOCONFIG");
<span class="token keyword">return</span> name <span class="token operator">?</span> name <span class="token operator">:</span> <span class="token string">"include/config/auto.conf"</span><span class="token punctuation">;</span>
}
......
name <span class="token operator">=</span> <span class="token function">getenv</span><span class="token punctuation">(</span><span class="token string">"KCONFIG_AUTOHEADER"</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span>name<span class="token punctuation">)</span> name <span class="token operator">=</span> <span class="token string">"include/generated/autoconf.h"</span><span class="token punctuation">;</span> <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token function">rename</span><span class="token punctuation">(</span><span class="token string">".tmpconfig.h"</span><span class="token punctuation">,</span> name<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token keyword">return</span> <span class="token number">1</span><span class="token punctuation">;</span> name <span class="token operator">=</span> <span class="token function">getenv</span><span class="token punctuation">(</span><span class="token string">"KCONFIG_TRISTATE"</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span>name<span class="token punctuation">)</span> name <span class="token operator">=</span> <span class="token string">"include/config/tristate.conf"</span><span class="token punctuation">;</span> <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token function">rename</span><span class="token punctuation">(</span><span class="token string">".tmpconfig_tristate"</span><span class="token punctuation">,</span> name<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token keyword">return</span> <span class="token number">1</span><span class="token punctuation">;</span> name <span class="token operator">=</span> <span class="token function">conf_get_autoconfig_name</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">/* * This must be the last step, kbuild has a dependency on auto.conf * and this marks the successful completion of the previous steps. */</span> <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token function">rename</span><span class="token punctuation">(</span><span class="token string">".tmpconfig"</span><span class="token punctuation">,</span> name<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token keyword">return</span> <span class="token number">1</span><span class="token punctuation">;</span>
從上面可以看到,分別生成了以下幾個文件:
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