[轉載]linux kernel makefile-auto.conf, auto.conf.cmd, autoconf.h依賴和生成
阿新 • • 發佈:2022-02-12
原文連結: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 。