linux Kbuild詳解關于if_changed_rule的any-prereq和arg-check原理及info調試關于fixdep沒有展開,這里說下。
文章目錄
- 1. escsq
- 2. Q、quiet
- 2. 1 make V=(0、1、2)
- 2. 2 make V=(0、1)來控制Q、quiet
- 3. fixdep
- 3. 1 fixdep是什么
- 3. 2 fixdep為什么
- 3.2.1 .config和autoconf.h的關系
- 3.2.2 autoconf.h被引用
- 3.2.3 GNU產生的依賴均包含autoconf.h
- 3.2.4 全局頭文件autoconf.h改動的影響
- 3.2.5 fixdep登場了
- 3. 3 fixdep怎么做
- 3.3.1 fixdep參數depfile
- 3.3.2 fixdep參數target
- 3.3.3 fixdep參數cmdline
- 3.3.4 fixdep輸出
- 3.3.5 過程總結
- 4. 參考
1. escsq
quote := "
squote := '
# Escape single quote for use in echo statements
escsq = $(subst $(squote),'\$(squote)',$1)
escsq :轉義(escape)單引號 (single quote)
$(subst $from, $to, $var)的用法:將$var中的$from部分替換成$to。
因此上述代碼就很明確了:$(squote)是單引號,'$(squote)'是雙引號,替換。
2. Q、quiet
2. 1 make V=(0、1、2)
V含義為verbose,表示詳細的,即打印更多的信息 。
@echo ' make V=0|1 [targets] 0 => quiet build (default), 1 => verbose build'
@echo ' make V=2 [targets] 2 => give reason for rebuild of target'
- V=0:表示不輸出編譯信息。在編譯時不指定V時,默認 V=0。
- V=1:表示輸出詳盡編譯信息。
- V=2:給出重新編譯目標的理由。
2. 2 make V=(0、1)來控制Q、quiet
- 控制Q值
ifeq ("$(origin V)", "command line")KBUILD_VERBOSE = $(V)
endif
#如果V值來源于命令行,則KBUILD_VERBOSE從$(V)獲取值ifeq ($(KBUILD_VERBOSE),1) #V=1quiet =Q =
else #V=0quiet=quiet_Q = @
endif
示例如下:
%o: %c$(Q)$(CC) -c $< -o $@
如Q=@就能控制編譯$(CC)命令不輸出。那還需要quiet做什么?
- 控制quiet值
quiet有什么神奇的力量?其實quiet并沒有什么神通的力量,只是一個普通的變量,用于生成特定區分詳細程度或參數不同(如quiet_cmd_xx、cmd_xx)的命令。
看一個例子:
quiet_cmd_checksrc = CHECK $<
cmd_checksrc = $(CHECK) $(CHECKFLAGS) $(c_flags) $< ;
兩條命令可化成一條命令為:$(quiet)cmd_checksrc 進行操作了。
3. fixdep
3. 1 fixdep是什么
fixdep是一個用Host主機從源文件fixdep.c編譯得到的整理依賴的可執行工具:
xx@xx-vb:~/Downloads/linux$ file scripts/basic/fixdep
scripts/basic/fixdep: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=3b57a9ce45b07c8ea86176d2002706756d35e692, for GNU/Linux 3.2.0, not stripped
執行提示如下:
xx@xx-vb:~/Downloads/linux$ scripts/basic/fixdep
Usage: fixdep [-e] <depfile> <target> <cmdline>-e insert extra dependencies given on stdin
代碼在scripts/basic/fixdep.c,邏輯不復雜,代碼中更像詳細點的介紹:
* It is invoked as** fixdep <depfile> <target> <cmdline>** and will read the dependency file <depfile>** The transformed dependency snipped is written to stdout.** It first generates a line** cmd_<target> = <cmdline>** and then basically copies the .<target>.d file to stdout, in the* process filtering out the dependency on autoconf.h and adding* dependencies on include/config/MY_OPTION for every* CONFIG_MY_OPTION encountered in any of the prerequisites.
簡而言之:它輸入<depfile>,<target>,和編譯該<target>的編譯命令<cmdline>,基本上拷貝.<target>.d的文件內容到標準輸出,但移除其中的對autoconf.h的依賴,將依賴文件中形如CONFIG_MY_OPTION的配置項轉成include/config/MY_OPTION的文件作為依賴。可能迷糊的你,依然好奇,這是個啥玩意?!先看fixdep為什么。
3. 2 fixdep為什么
3.2.1 .config和autoconf.h的關系
編譯內核前,通常需要進行make [xx]config,生成.config和各種由此產生的文件如tristate.conf、auto.conf、autoconf.h等(生成過程可參考文章 auto.conf, auto.conf.cmd, autoconf.h)。可以把 .config看作其他配置文件的來源,其形式為:
...
CONFIG_X86=y
CONFIG_HZ=250
CONFIG_INSTRUCTION_DECODER=y
...
而C文件不能使用這樣的信息,kbuild conf從.config轉換得到autoconf.h作為所有C文件公共配置項頭文件,其形式為:
...
#define CONFIG_X86 1
#define CONFIG_HZ 250
#define CONFIG_INSTRUCTION_DECODER 1
...
3.2.2 autoconf.h被引用
在scripts/Kbuild.include中:
cmd_and_fixdep = \
272 $(echo-cmd) $(cmd_$(1)); \
273 scripts/basic/fixdep $(depfile) $@ '$(make-cmd)' > $(dot-target).tmp; \
274 rm -f $(depfile); \
275 mv -f $(dot-target).tmp $(dot-target).cmd;其中$(1)為 cmd_cc_o_c:
177 cmd_cc_o_c = $(CC) $(c_flags) -c -o $@ $<
272行執行編譯命令展開為177行,其中c_flags在scripts/Makefile.lib中:
154 c_flags = -Wp,-MD,$(depfile) $(NOSTDINC_FLAGS) $(LINUXINCLUDE) \
155 -include $(srctree)/include/linux/compiler_types.h \
156 $(__c_flags) $(modkern_cflags) \
157 $(basename_flags) $(modname_flags)
154行包含$(LINUXINCLUDE):
LINUXINCLUDE在Makefile中定義如下:
412 LINUXINCLUDE := \413 -I$(srctree)/arch/$(SRCARCH)/include \414 -I$(objtree)/arch/$(SRCARCH)/include/generated \415 $(if $(KBUILD_SRC), -I$(srctree)/include) \416 -I$(objtree)/include \417 $(USERINCLUDE)
415行-I$(srctree)/include包含include目錄下的頭文件,這里包括 include/generated/autoconf.h。
3.2.3 GNU產生的依賴均包含autoconf.h
154行-Wp,-MD,$(depfile):編譯器的–MD FILE選項將會把依賴文件輸入到FILE中:
--MD FILE write dependency information in FILE (default none)
這個選項在cmd_and_fixdep的273行會將編譯依賴生成到$(depfile)也即.<target>.d文件中,以init/main.c為例,其在177行cmd_cc_o_c命令下編譯同時生成的init/.main.o.d文件,內容包含了autoconf.h,如下第一行:
main.o: init/main.c include/linux/kconfig.h include/generated/autoconf.h \ #autoconf.hinclude/linux/compiler_types.h include/linux/compiler-gcc.h \include/linux/types.h include/uapi/linux/types.h \...
其他文件大部分都會包含include/generated/autoconf.h,因為這是個全局的配置項頭文件。想查看形如init/.main.o.d(一般化為$(depfile))文件內容的需要改下scripts/Kbuild.include,因為正常不使用它作為依賴根據,處理后$(depfile)就被刪除了。為調試研究,我們將刪除項去掉,如下:
xx@xx-vb:~/Downloads/linux$ git diff
diff --git a/scripts/Kbuild.include b/scripts/Kbuild.include
index ce53639a864a..598489621347 100644
--- a/scripts/Kbuild.include
+++ b/scripts/Kbuild.include
@@ -271,7 +271,6 @@ ifndef CONFIG_TRIM_UNUSED_KSYMScmd_and_fixdep = \$(echo-cmd) $(cmd_$(1)); \scripts/basic/fixdep $(depfile) $@ '$(make-cmd)' > $(dot-target).tmp;\
- rm -f $(depfile); \mv -f $(dot-target).tmp $(dot-target).cmd;else
3.2.4 全局頭文件autoconf.h改動的影響
上文描述了全局頭文件autoconf.h被普遍引用的過程,如果使用-Wp,-MD,$(depfile)生成的$(depfile)中的依賴關系作為增量編譯的判斷依據,存在的問題是:A文件有個配置如CONFIG_HZ=250,B文件的配置CONFIG_INSTRUCTION_DECODER 1,兩者可能不相關,但是A文件改動了配置為CONFIG_HZ=500,從而更新了autoconf.h。由于B也引用了autoconf.h,且B的依賴關系中也依賴autoconf.h文件,從而導致幾乎所有的依賴關系包含autoconf.h的文件需要重新編譯,這個顯然不符合增量編譯的設計。
3.2.5 fixdep登場了
fixdep就是來處理這個問題的,它需要把A:autoconf.h、B:autoconf.h的方式改為A:CONFIG_HZ、B:CONFIG_INSTRUCTION_DECODER的方式,這樣互相就不會扯到蛋了。
具體改成依賴小寫空文件的形式:
GNU -MD生成式 | fixdep調整 |
---|---|
A:autoconf.h(CONFIG_HZ) | A: include/config/hz.h |
B:autoconf.h(CONFIG_INSTRUCTION_DECODER) | B: include/config/instruction/decoder.h |
所有依賴CONFIG_HZ的文件都只依賴 include/config/hz.h,而不是依賴公共的autoconf.h,這樣就做到了依賴的分離。
3. 3 fixdep怎么做
還是以init/main.c為例,就fixdep的參數depfile、target、cmdline及輸出展開:
fixdep <depfile> <target> <cmdline>
fixdep使用實例:
cmd_and_fixdep = \
272 $(echo-cmd) $(cmd_$(1)); \
273 scripts/basic/fixdep $(depfile) $@ '$(make-cmd)' > $(dot-target).tmp; \
274 rm -f $(depfile); \
275 mv -f $(dot-target).tmp $(dot-target).cmd;
3.3.1 fixdep參數depfile
對應273行的$(depfile)
#ini/main.o => ini/.main.o
dot-target = $(dir $@).$(notdir $@)# 如果有逗號替換為_,這里返回的是ini/.main.o.d
depfile = $(subst $(comma),_,$(dot-target).d)
這里的$(depfile)=init/.main.o.d,這個文件哪里來的?它是272行執行cmd_cc_o_c,其中c_flags = -Wp,-MD,$(depfile) … 編譯目標ini/main.o文件同時生成的。
3.3.2 fixdep參數target
對應273行的$@=ini/main.o
3.3.3 fixdep參數cmdline
對應273行的’$(make-cmd)’
make-cmd = $(call escsq,$(subst $(pound),$$(pound),$(subst $$,$$$$,$(cmd_$(1)))))
#--------------------------------------------------------------------------------
$(cmd_$(1))展開為:
177 cmd_cc_o_c = $(CC) $(c_flags) -c -o $@ $<
這里的’$(make-cmd)'=gcc -Wp,-MD,init/.main.o.d -nostdinc -isystem /usr/lib/gcc/x86_64-linux-gnu/9/include -I./arch/x86/include -I./arch/x86/include/generated …很長。
3.3.4 fixdep輸出
對應273行的輸出$(dot-target).tmp后經275行的重命名得到$(dot-target).cmd=init/.main.o.cmd,有興趣可以看看其內容([…]有省略):
cmd_init/main.o := gcc -Wp,-MD,init/.main.o.d -nostdinc -isystem /usr/lib/gcc/x86_64-linux-gnu/9/include -I./arch/x86/include -I./arch/x86/include/generated -I./include -I./arch/x86/include/uapi -I./arch/x86/include/generated/uapi -I./include/uapi -I./include/generated/uapi -include ./include/linux/kconfig.h -include ./include/linux/compiler_types.h -D__KERNEL__ -Wall -Wundef [...] -DKBUILD_BASENAME='"main"' -DKBUILD_MODNAME='"main"' -c -o init/main.o init/main.csource_init/main.o := init/main.cdeps_init/main.o := \$(wildcard include/config/init/env/arg/limit.h) \$(wildcard include/config/smp.h) \[...] $(wildcard include/config/strict/module/rwx.h) \include/linux/kconfig.h \$(wildcard include/config/cpu/big/endian.h) \$(wildcard include/config/booger.h) \$(wildcard include/config/foo.h) \include/linux/compiler_types.h \$(wildcard include/config/have/arch/compiler/h.h) \$(wildcard include/config/enable/must/check.h) \[...]include/trace/events/initcall.h \include/trace/define_trace.h \init/main.o: $(deps_init/main.o)$(deps_init/main.o):
3.3.5 過程總結
fixdep調整過的依賴關系保存文件$(depfile)中保存了:編譯命令變量、源文件名稱變量、fixdep修改后的依賴變量、聲明fixdep修改后依賴關系。
#1.編譯命令變量
cmd_$@ := '$(make-cmd)'#2.源文件名稱變量
source_$@ := $(depfile)中C源文件#3.fixdep修改后的依賴變量
deps_$@ := $(depfile)中去除autoconf.h,并依據源碼中CONFIG_X_Y增加$(wildcard include/config/x/y.h)依賴項#4.聲明fixdep修改后依賴關系
$@: deps_$@
deps_$@:
如下圖片看不清除,可以點擊放大看
-
編譯命令變量
-
源文件名稱變量
-
fixdep修改后的依賴變量
生成依賴變量deps_$@ ,包含兩部分:第一部分從$(depfile) (圖中右側綠色框),移除紅框autoconf.h和main.c,剩下都包含進來;第二部分查找main.c中CONFIG_X_Y的宏,添加為$(wildcard include/config/x/y.h) \的依賴項。 -
聲明了fixdep修改后依賴關系
使用fixdep修改依賴關系后,不再使用GNU -MD生成的依賴關系作為判斷依賴是否更新的依據,從而避免了依賴共享autoconf.h導致依賴關聯的問題。
4. 參考
- linux Kbuild詳解系列(8)-Kbuild中其他通用函數與變量
- auto.conf, auto.conf.cmd, autoconf.h