欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

linux内核编译基础知识储备 --- 过渡篇(四)

程序员文章站 2022-07-14 16:26:04
...

一. 一种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_
linux内核编译基础知识储备 --- 过渡篇(四)
(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
linux内核编译基础知识储备 --- 过渡篇(四)
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 变量 :未定义,不关注;