1. 程式人生 > 其它 >[轉載]linux kernel makefile-auto.conf, auto.conf.cmd, autoconf.h依賴和生成

[轉載]linux kernel makefile-auto.conf, auto.conf.cmd, autoconf.h依賴和生成

原文連結:https://blog.csdn.net/lcw_202/article/details/6661364

linux kernel makefile 中 auto.conf auto.conf.cmd autoconf.h 依賴和生成

在編譯構建性目標時(如 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 are detected. - include include /config/auto .conf.cmd # 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

其中,
引用 -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)/Makefile silentoldconfig

這裡我們看到要生成一個目標 silentoldconfig ,這個目標定義在 scripts/kconfig/Makefile 中。因為這裡使用 -f 選項重新指定了頂層 Makefile,而目標又是 silentoldconfig ,所以該命令最終會在頂層 Makefile 的 462-464 這裡執行:
?
1 2 3 %config: scripts_basic outputmakefile FORCE $(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) { /* 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 kernel configuration.\n\n" )); exit (1); } if (conf_write_autoconf()) { fprintf (stderr, _( "\n*** Error during update of the kernel configuration.\n\n" )); return 1; } } else { if (conf_write(NULL)) { fprintf (stderr, _( "\n*** Error during writing of the 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" " * Automatically generated C config: don't edit\n" " * Linux kernel version: %s\n" " * %s" " */\n" "#define AUTOCONF_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(); /* * 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;

上面程式碼中的 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_HZ 1000 。