linux内核编译基础知识储备 --- 过渡篇(四)
一. 一种makefile中定义函数的方式
The call function is unique in that it can be used to create new parameterized functions. You can write a complex expression as the value of a variable, then use call to expand it with different values.
The syntax of the call function is:
$(call variable,param,param,…)
When make expands this function, it assigns each param to temporary variables $(1), $(2), etc.
一个类似宏定义的定义,当make展开这个函数时,它将每个参数分配给临时变量$ (1)、$ (2)等。还是很抽象是不?不要紧,后续我们会讲到它的相关实例。
二. makefile之if函数
#if 函数的语法是:
$(if <condition>,<then-part> )
或
$(if <condition>,<then-part>,<else-part> )
<condition>参数是if的表达式,如果其返回的为非空字符串,那么这个表达式就相当于返回真,于是,<then-part>会被计算,否则<else-part>会被计算
if函数的返回值是,
如果<condition>为真(非空字符串),那个<then-part>会是整个函数的返回值,
如果<condition>为假(空字符串),那么<else-part>会是整个函数的返回值,此时如果<else-part>没有被定义,那么,整个函数返回空字串。
三. MAKE变量
$(MAKE)就是预设的 make 这个命令的名称(或者路径)。
四. $(quiet)
# 顶层Makefile
# We are using a recursive build, so we need to do a little thinking
# to get the ordering right.
#
# Most importantly: sub-Makefiles should only ever modify files in
# their own directory. If in some directory we have a dependency on
# a file in another dir (which doesn't happen often, but it's often
# unavoidable when linking the built-in.o targets which finally
# turn into vmlinux), we will call a sub make in that other dir, and
# after that we are sure that everything which is in that other dir
# is now up to date.
#
# The only cases where we need to modify files which have global
# effects are thus separated out and done before the recursive
# descending is started. They are now explicitly listed as the
# prepare rule.
# To put more focus on warnings, be less verbose as default
# Use 'make V=1' to see the full commands
ifeq ("$(origin V)", "command line")
KBUILD_VERBOSE = $(V)
endif
ifndef KBUILD_VERBOSE
KBUILD_VERBOSE = 0
endif
# Beautify output
# ---------------------------------------------------------------------------
#
# Normally, we echo the whole command before executing it. By making
# that echo $($(quiet)$(cmd)), we now have the possibility to set
# $(quiet) to choose other forms of output instead, e.g.
#
# quiet_cmd_cc_o_c = Compiling $(RELDIR)/aaa@qq.com
# cmd_cc_o_c = $(CC) $(c_flags) -c -o aaa@qq.com $<
#
# If $(quiet) is empty, the whole command will be printed.
# If it is set to "quiet_", only the short version will be printed.
# If it is set to "silent_", nothing will be printed at all, since
# the variable $(silent_cmd_cc_o_c) doesn't exist.
#
# A simple variant is to prefix commands with $(Q) - that's useful
# for commands that shall be hidden in non-verbose mode.
#
# $(Q)ln aaa@qq.com :<
#
# If KBUILD_VERBOSE equals 0 then the above command will be hidden.
# If KBUILD_VERBOSE equals 1 then the above command is displayed.
ifeq ($(KBUILD_VERBOSE),1)
quiet =
Q =
else
quiet=quiet_
Q = @
endif
这里的Q代表着是否为quiet静默编译,需要在make编译时通过传入V=1来指定。通常情况下我们不会在命令行传入V这个参数,此时走的是else分支(特别指定除外), quiet=quiet_ — 只打印简短的编译信息,Q = @ — 命令不会回显。讲了这么多,不如来个实例看着直观:
1. quiet = 空 与 quiet=quiet_ 的区别
(1)执行 sudo make drivers/char/mem.o ,此时 quiet=quiet_
(2)在scripts/Makefile.build中把quiet_cmd_cc_o_c定义成与cmd_cc_o_c一样的形式,模拟quiet = 空的场景,只是为了测试,看不懂不要紧,后面会有对应的讲解,这里只是给出一个直观的图形,免得那么抽象;
# quiet_cmd_cc_o_c = CC $(quiet_modtag) aaa@qq.com 注释掉
quiet_cmd_cc_o_c = $(CC) $(c_flags) -c -o aaa@qq.com $<
cmd_cc_o_c = $(CC) $(c_flags) -c -o aaa@qq.com $<
执行 sudo make drivers/char/mem.o
2. 有无@的区别
@表示在make时不输出make的信息(类似Windows下的echo off)。就是是否回显命令行,比较简单,这里就不再赘述。
五. echo-cmd 变量
1. echo-cmd 变量的定义
# scripts/Kbuild.include
# echo command.
# Short version is used, if $(quiet) equals `quiet_', otherwise full one.
echo-cmd = $(if $($(quiet)cmd_$(1)),\
echo ' $(call escsq,$($(quiet)cmd_$(1)))$(echo-why)';)
echo-cmd就是打印出调用的命令。if $ ($ (quiet)cmd_$ (1)) 是判断命令$ (quiet)cmd_$ (1)或者cmd_$(1)是否定义?如果有定义,echo-cmd就会将这个命令打印出来。(例如对于 $(call echo-cmd,checksrc) 就是打印quiet_cmd_checksrc或者cmd_checksrc )。
有一点比较重要的是你或许注意到了末尾括号前有一个分号";" ,这是因为 一般在调用echo-cmd命令后面紧跟一个与之对应的要执行的命令,由于echo也是一条命令,用分号来分割两个命令。比如:
$(call echo-cmd,cc_o_c) $(cmd_cc_o_c)
想想其实也不觉得奇怪,只打印命令肯定不是我们的目的,执行这条命令才是我们的目的!打印只是为了方便编译调试。
2. escsq 函数
转义单引号(在单引号前面加个 \),以便在echo语句中使用。
# scripts/Kbuild.include
squote := '
...
#Escape single quote for use in echo statements
escsq = $(subst $(squote),'\$(squote)',$1)
3. echo-why
通常情况下我们不会在命令行传入V这个参数,因此KBUILD_VERBOSE=0,因此下面这个分支走不到,即echo-why为空。
# scripts/Kbuild.include
ifeq ($(KBUILD_VERBOSE),2)
why = \
$(if $(filter aaa@qq.com, $(PHONY)),- due to target is PHONY, \
$(if $(wildcard aaa@qq.com), \
$(if $(strip $(any-prereq)),- due to: $(any-prereq), \
$(if $(arg-check), \
$(if $(aaa@qq.com),- due to command line change, \
$(if $(filter aaa@qq.com, $(targets)), \
- due to missing .cmd file, \
- due to $(notdir aaa@qq.com) not in $$(targets) \
) \
) \
) \
), \
- due to target missing \
) \
)
echo-why = $(call escsq, $(strip $(why)))
endif
六. $(call echo-cmd,checksrc)
通过前面的分析, $(call echo-cmd,checksrc)可以展开为:
$(if $(quiet_cmd_checksrc),echo ' $(call escsq,$(quiet_cmd_checksrc))';)
其中quiet_cmd_checksrc变量的定义如下:
# scripts/Makefile.build
# Linus' kernel sanity checking tool
ifneq ($(KBUILD_CHECKSRC),0)
ifeq ($(KBUILD_CHECKSRC),2)
quiet_cmd_force_checksrc = CHECK $<
cmd_force_checksrc = $(CHECK) $(CHECKFLAGS) $(c_flags) $< ;
else
quiet_cmd_checksrc = CHECK $<
cmd_checksrc = $(CHECK) $(CHECKFLAGS) $(c_flags) $< ;
endif
endif
一般情况下我们不会在命令行传入C这个参数(定义如下,在顶层Makefile中),因此KBUILD_CHECKSRC = 0,因此下面这个分支走不到,*$ (call echo-cmd,checksrc)为空。参数C定义在顶层Makefile中:
# Call a source code checker (by default, "sparse") as part of the
# C compilation.
#
# Use 'make C=1' to enable checking of only re-compiled files.
# Use 'make C=2' to enable checking of *all* source files, regardless
# of whether they are re-compiled or not.
#
# See the file "Documentation/sparse.txt" for more details, including
# where to get the "sparse" utility.
ifeq ("$(origin C)", "command line")
KBUILD_CHECKSRC = $(C)
endif
ifndef KBUILD_CHECKSRC
KBUILD_CHECKSRC = 0
endif
七. $(cmd_modversions)
一般情况下不会定义CONFIG_MODVERSIONS,因此 $(cmd_modversions)为空。
其中 $(cmd_modversions)~变量的定义如下:
# scripts/Makefile.build
ifndef CONFIG_MODVERSIONS
...
else
...
cmd_modversions = \
if $(OBJDUMP) -h $(@D)/.tmp_$(@F) | grep -q __ksymtab; then \
$(call cmd_gensymtypes,$(KBUILD_SYMTYPES),$(@:.o=.symtypes)) \
> $(@D)/.tmp_$(@F:.o=.ver); \
\
$(LD) $(LDFLAGS) -r -o aaa@qq.com $(@D)/.tmp_$(@F) \
-T $(@D)/.tmp_$(@F:.o=.ver); \
rm -f $(@D)/.tmp_$(@F) $(@D)/.tmp_$(@F:.o=.ver); \
else \
mv -f $(@D)/.tmp_$(@F) aaa@qq.com; \
fi;
endif
八. $(cmd_record_mcount)
一般情况下不会定义CONFIG_FTRACE_MCOUNT_RECORD,因此 $(cmd_record_mcount)为空。 其中 cmd_record_mcount变量的定义如下:
# scripts/Makefile.build
ifdef CONFIG_FTRACE_MCOUNT_RECORD
...
cmd_record_mcount = \
if [ "$(findstring -pg,$(_c_flags))" = "-pg" ]; then \
$(sub_cmd_record_mcount) \
fi;
endif
九. $(call echo-cmd,record_mcount)
通过前面的分析,call echo-cmd,record_mcount可以展开为:
$(if $(quiet_record_mcount),echo ' $(call escsq,$(quiet_cmd_record_mcount))';)
未定义quiet_record_mcount,因此 $(call echo-cmd,record_mcount)为空。
十.$(call echo-cmd,cc_o_c)
通过前面的分析 , $(call echo-cmd,cc_o_c)可以展开为:
$(if $(quiet_cmd_cc_o_c),echo ' $(call escsq,$(quiet_cmd_cc_o_c))';)
其中quiet_cmd_cc_o_c变量的定义在scripts/Makefile.build中:
# scripts/Makefile.build
quiet_cmd_cc_o_c = CC $(quiet_modtag) aaa@qq.com
由于变量quiet_modtag为空,quiet_cmd_cc_o_c又可以展开为
quiet_cmd_cc_o_c = CC aaa@qq.com
就是打印" CC aaa@qq.com "这么一句话 。
总结
好了,前面巴拉巴拉一大堆,是时候来个总结啦,就好像千里行军总得找个好地方,整顿行囊,这样才能走得更远,更稳。
echo-cmd 函数: 判断命令$ (quiet)cmd_$ (1)或者cmd_$(1)是否定义如果有定义,echo-cmd就会将这个命令打印出来。且后面紧跟着这条命令的具体执行;
escsq 函数 : 转义单引号(在单引号前面加个 \),以便在echo语句中使用;
$(call echo-cmd,cc_o_c) : 命令行未传入V参数值的情况下 展开为echo ’ CC aaa@qq.com’ ;
$(call echo-cmd,checksrc) : 返回空,不关注;
$ (call echo-cmd,record_mcount) : 返回空,不关注;
echo-why 变量 :未定义,不关注;
cmd_modversions 变量 :未定义,不关注;
cmd_record_mcount 变量 :未定义,不关注;
上一篇: (34)内核编程基础
下一篇: 驱动基础——内核模块