3.1 頂層make defconfig規則
make xxx_defconfig 的執行主要分成三個部分:
- 執行
make -f ./scripts/Makefile.build obj=scripts/basic
,編譯生成scripts/basic/fixdep
工具 - 執行
make -f ./scripts/Makefile.build obj=scripts/kconfig rpi_3_32b_defconfig
編譯生成scripts/kconfig/conf
工具 - 執行
scripts/kconfig/conf --defconfig=arch/../configs/xxx_defconfig Kconfig
生成最終的.config
配置文件
執行 make xxx_defconfig
命令時,u-boot 根目錄下的 Makefile 中有唯一的規則匹配目標:
代碼第 467 到 478 行
1 # =========================================================================== 2 # *config targets only - make sure prerequisites are updated, and descend 3 # in scripts/kconfig to make the *config target 4 5 KBUILD_DEFCONFIG := sandbox_defconfig 6 export KBUILD_DEFCONFIG KBUILD_KCONFIG 7 8 config: scripts_basic outputmakefile FORCE 9 $(Q)$(MAKE) $(build)=scripts/kconfig $@ 10 11 %config: scripts_basic outputmakefile FORCE 12 $(Q)$(MAKE) $(build)=scripts/kconfig $@
注釋意思為,僅限 *config 目標,確保先決條件已經更新,並在 scripts/kconfig 下創建 *config 目標。上面有兩個變量 config 和 %config,% 符號為通配符,對應所有的 xxxconfig 目標,前面已經說過。我們的 make xxx_defconfig 就對應 %config,我們並沒有執行 make config 命令。
Makefile中幾種變量賦值運算符:
- = :最簡單的賦值
- := :一般也是賦值
- 以上這兩個大部分情況下效果是一樣的,但是有時候不一樣。
- 用 = 賦值的變量,在被解析時他的值取決於最后一次賦值時的值,所以看變量引用的值時不能只往前面看,還要往后面看。
- 用 := 來賦值的,則是就地直接解析,只用往前看即可。
- ?= : 如果變量前面並沒有賦值過則執行這條賦值,如果前面已經賦值過了則本行被忽略。
- += 用來給一個已經賦值的變量接續賦值,意思就是把這次的值加到原來的值的后面,有點類似於strcat
- 在shell makefile等文件中,可以認為所有變量都是字符串,+= 就相當於給字符串 strcat 接續內容
- +=續接的內容和原來的內容之間會自動加一個空格隔開
3.1.1 代碼執行到%config 的條件
先往上分析下這段代碼的執行條件: ifeq ($(config-targets),1),代碼在415 到 447 行
1 # To make sure we do not include .config for any of the *config targets
2 # catch them early, and hand them over to scripts/kconfig/Makefile 3 # It is allowed to specify more targets when calling make, including 4 # mixing *config targets and build targets. 5 # For example 'make oldconfig all'. 6 # Detect when mixed targets is specified, and make a second invocation 7 # of make so .config is not included in this case either (for *config). 8 9 version_h := include/generated/version_autogenerated.h 10 timestamp_h := include/generated/timestamp_autogenerated.h 11 12 no-dot-config-targets := clean clobber mrproper distclean \ 13 help %docs check% coccicheck \ 14 ubootversion backup tests 15 16 config-targets := 0 17 mixed-targets := 0 18 dot-config := 1 19 20 ifneq ($(filter $(no-dot-config-targets), $(MAKECMDGOALS)),) 21 ifeq ($(filter-out $(no-dot-config-targets), $(MAKECMDGOALS)),) 22 dot-config := 0 23 endif 24 endif 25 26 ifeq ($(KBUILD_EXTMOD),) 27 ifneq ($(filter config %config,$(MAKECMDGOALS)),) 28 config-targets := 1 29 ifneq ($(words $(MAKECMDGOALS)),1) 30 mixed-targets := 1 31 endif 32 endif 33 endif
代碼注釋內容:為了確保我們不包含任何 * config 目標的 .config,請盡早捕獲它們,並將它們交給 scripts / kconfig / Makefile。調用make 時允許指定更多目標,包括混合 * config 目標和構建目標。例如 'make oldconfig all' 。檢測何時指定了混合目標,並進行make的第二次調用,因此 .config不包含在這種情況下(對於* config)。
- version_h:版本號文件,此文件是自動生成的
- timestamp_h:時間戳文件,此文件是自動生成的
- no-dot-config-targets:指代的是那些和 .config 沒有關系的目標
- config-targets:配置目標,初始值設置為0
- mixed-targets:混合目標,初始值設置為0
- dot-config:初始值設置為1
變量 MAKECMDGOALS:make 在執行時會設置一個特殊變量 -- "MAKECMDGOALS" ,該變量記錄了命令行參數指定的終極目標列表,沒有通過參數指定終極目標時此變量為空。該變量僅限於用在特殊場合(比如判斷),在 Makefile 中最好不要對它進行重新定義。
我們執行 make xxx_defconfig 的時候,MAKECMDGOALS 變量的值就為 xxx_defconfig。
filter 函數 和 filter-out 函數:
1 $(filter PATTERN…,TEXT) 2 函數名稱: 過濾函數— filter。 3 函數功能: 過濾掉字串“ TEXT”中所有不符合模式“ PATTERN”的單詞,保留所 4 有符合此模式的單詞。可以使用多個模式。模式中一般需要包含模式字 5 符“ %”。存在多個模式時,模式表達式之間使用空格分割。 6 返回值:空格分割的“ TEXT”字串中所有符合模式“ PATTERN”的字串。 7 函數說明:“ filter”函數可以用來去除一個變量中的某些字符串
1 $(filter-out PATTERN...,TEXT) 2 函數名稱: 反過濾函數— filter-out 3 函數功能: 和“ filter”函數實現的功能相反。過濾掉字串“ TEXT”中所有符合模式“ PATTERN”的單詞,保留所有不符合此模式的單詞。
可以有多個模式。存在多個模式時,模式表達式之間使用空格分割 4 返回值: 空格分割的“ TEXT”字串中所有不符合模式“ PATTERN”的字串。 5 函數說明: “ filter-out”函數也可以用來去除一個變量中的某些字符串,(實現和“ filter”函數相反)
代碼執行的過程就為,如果 過濾掉 MAKECMDGOALS 不符合 no-dot-config-targets 后結果不為空,則執行分支語句。很顯然過濾后為空,則不執行分支語句,dot-config 依然 值為1。
接着執行下一條 ifeq 語句,對 KBUILD_EXTMOD 進行判定。KBUILD_EXTMODE 的賦值地方在代碼 182 到 191 行處:
1 # Use make M=dir to specify directory of external module to build 2 # Old syntax make ... SUBDIRS=$PWD is still supported 3 # Setting the environment variable KBUILD_EXTMOD take precedence 4 ifdef SUBDIRS 5 KBUILD_EXTMOD ?= $(SUBDIRS) 6 endif 7 8 ifeq ("$(origin M)", "command line") 9 KBUILD_EXTMOD := $(M) 10 endif
由注釋可以知道,SUBDIRS 這個變量是通過執行 make 的時候傳進來的,我們並沒有執行此項,所以未定義SUBDIRS,第一個分支不會去走。第二個 if 語句為 ifeq 語句,這里使用 origin 函數。
origin 函數不是操作變量(即它的參數),它只是獲取此變量(參數)相關的信息,告訴我們這個變量的出處(定義方式)。
那么 ifeq 語句可以理解為 如果make傳入的命令行變量存在且是M,那么,變量KBUILD_EXTMOD變為變量M的值。
第一階段中我們並沒有傳入 M 值,則 KBUILD_EXTMOD 值為空
繼續回到此小節主代碼處,當前執行 KBUILD_EXTMOD 的判定,此處滿足 ifeq 條件,開始執行分支語句,分支語句同樣是一個判斷,首先過濾掉 MAKECMDGOALS 不符合 config 和 %config 模式的字符串,然后返回 xxx_defconfig ,xxx_defconfig 再與 空進行比較,if 語句為 ifneq ,很顯然, filter 語句不為空,與空進行比較,滿足 ifneq 執行語句。
此處將 config-targets 重新賦值為 1;賦值完后,進行 ifneq 條件判斷,再次涉及 makefile 的函數——words。
顯然我們的傳入的單詞數據為1,與1相等,則不執行分支,即 mixed-targets 不進行重新賦值,依然為0。
再代碼進行到 ifeq ($(config-targets),1) 時候,先進行了 ifeq ($(mixed-targets),1)分支,如果 mixed-targets 則執行另一條分支,就不會再執行ifeq ($(config-targets),1) 了。我們這里執行的ifeq ($(mixed-targets),1) 的 else中的分支語句。
到此處,ifeq ($(config-targets),1) 是否會執行已經分析完畢。
當前我們已經知道的變量的值為:
- MAKECMDGOALS = xxx_defconfig
- KBUILD_EXTMOD =
- version_h := include/generated/version_autogenerated.h
- timestamp_h := include/generated/timestamp_autogenerated.h
- no-dot-config-targets := clean clobber mrproper distclean help %docs check% coccicheck ubootversion backup tests
- config-targets := 1
- mixed-targets := 0
- dot-config := 1
3.1.2 %config分析
ifeq ($(config-targets),1) 中也由else分支,我們從上面的小節可以知道,else 分支不會去執行。所以很多代碼可以忽略了
從這里可以知道,此處此處選擇語句一直執行到 1655 行,466~480 行才是我們需要分析的。
471 到 472 行
1 KBUILD_DEFCONFIG := sandbox_defconfig 2 export KBUILD_DEFCONFIG KBUILD_KCONFIG
這里定義了兩個環境變量:
- KBUILD_DEFCONFIG = sandbox_defconfig
- KBUILD_KCONFIG 為空
繼續執行474 到 475 行
1 config: scripts_basic outputmakefile FORCE 2 $(Q)$(MAKE) $(build)=scripts/kconfig $@
此處目標沒有匹配,不會去執行
繼續執行477 478 行
1 %config: scripts_basic outputmakefile FORCE 2 $(Q)$(MAKE) $(build)=scripts/kconfig $@
%config 依賴scripts_basic outputmakefile FORCE
(1)依賴 FORCE
FORCE 的定義在 1748 和 1749 行
1 PHONY += FORCE 2 FORCE:
FORCE
被定義為一個空目標。如果一個目標添加 FORCE
依賴,每次編譯都會去先去執行 FORCE
(實際上什么都不做),然后運行命令更新目標,這樣就能確保目標每次都會被更新。
(2)依賴 scripts_basic
392 - 402 行
1 # =========================================================================== 2 # Rules shared between *config targets and build targets 3 4 # Basic helpers built in scripts/ 5 PHONY += scripts_basic 6 scripts_basic: 7 $(Q)$(MAKE) $(build)=scripts/basic 8 $(Q)rm -f .tmp_quiet_recordmcount 9 10 # To avoid any implicit rule to kick in, define an empty command. 11 scripts/basic/%: scripts_basic ;
Q = @,MAKE = make,build 變量的定義在 scripts/Kbuild.include 文件中
主Makefile 在327-329 行包含 scripts/Kbuild.include 文件
1 # We need some generic definitions (do not try to remake the file). 2 scripts/Kbuild.include: ; 3 include scripts/Kbuild.include
build 變量在 scripts/Kbuild.include 在177 - 181 行定義
1 ### 2 # Shorthand for $(Q)$(MAKE) -f scripts/Makefile.build obj= 3 # Usage: 4 # $(Q)$(MAKE) $(build)=dir 5 build := -f $(srctree)/scripts/Makefile.build obj
srctree 定義在主 Makefile 中202-212 行
1 ifeq ($(KBUILD_SRC),) 2 # building in the source tree 3 srctree := . 4 else 5 ifeq ($(KBUILD_SRC)/,$(dir $(CURDIR))) 6 # building in a subdirectory of the source tree 7 srctree := .. 8 else 9 srctree := $(KBUILD_SRC) 10 endif 11 endif
KBUILD_SRC (構建的源碼目錄) 在執行 make 命令的時候並沒有傳入,設為空,則srctree 為當前 uboot 源碼的根目錄
scripts/Kbuild.include 在177 - 181 行的展開為:build := -f ./scripts/Makefile.build obj
主 Makefile 中 scripts_basic 的展開為:
1 scripts_basic: 2 make -f ./scripts/Makefile.build obj=scripts/basic # 根據傳入的 obj 參數顯示的執行 ./scripts/Makefile.build 文件 3 rm -f .tmp_quiet_recordmcount
./scripts/Makefile.build 文件之后再分析。
(3)outputmakefile 依賴
404 - 413 行
1 PHONY += outputmakefile 2 # outputmakefile generates a Makefile in the output directory, if using a 3 # separate output directory. This allows convenient use of make in the 4 # output directory. 5 outputmakefile: 6 ifneq ($(KBUILD_SRC),) 7 $(Q)ln -fsn $(srctree) source 8 $(Q)$(CONFIG_SHELL) $(srctree)/scripts/mkmakefile \ 9 $(srctree) $(objtree) $(VERSION) $(PATCHLEVEL) 10 endif
KBUILD_SRC 為空,所以ifneq 中的語句不會執行。 outputmakefile 為空
重新回到 %config 處
1 %config: scripts_basic outputmakefile FORCE 2 $(Q)$(MAKE) $(build)=scripts/kconfig $@
依據前面的條件,展開表達式為:
1 make -f ./scripts/Makefile.build obj=scripts/kconfig xxx_defconfig
3.2 總結
3.2.1 變量
- MAKECMDGOALS = xxx_defconfig
- KBUILD_EXTMOD =
- version_h := include/generated/version_autogenerated.h
- timestamp_h := include/generated/timestamp_autogenerated.h
- no-dot-config-targets := clean clobber mrproper distclean help %docs check% coccicheck ubootversion backup tests
- config-targets := 1
- mixed-targets := 0
- dot-config := 1
- KBUILD_SRC =
- build := -f ./scripts/Makefile.build obj
3.2.2 環境變量
- KBUILD_DEFCONFIG := sandbox_defconfig
- KBUILD_KCONFIG =
3.2.3 需要進行分析的地方
(1)scripts_basic 目標執行的命令
make -f ./scripts/Makefile.build obj=scripts/basic
(2)%config 目標執行的命令
make -f ./scripts/Makefile.build obj=scripts/kconfig xxx_defconfig