MEMORY | INTERRUPT | TIMER | 并发与同步 | 进程管理 | 调度 | uboot | DTB | ARMV8 | ATF | Kernel Data Structure | PHY | LINUX2.6 | 驱动合集 | UART子系统 | USB专题 |

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)/$@
#         cmd_cc_o_c       = $(CC) $(c_flags) -c -o $@ $<
#
# 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 $@ :<
#
# 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 = @ — 命令不会回显。讲了这么多,不如来个实例看着直观:

4.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)  $@      注释掉
quiet_cmd_cc_o_c = $(CC) $(c_flags) -c -o $@ $<
cmd_cc_o_c       = $(CC) $(c_flags) -c -o $@ $<

执行 sudo make drivers/char/mem.o
在这里插入图片描述

4.1 有无@的区别

@表示在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 $@, $(PHONY)),- due to target is PHONY,                    \
        $(if $(wildcard $@),                                                 \
            $(if $(strip $(any-prereq)),- due to: $(any-prereq),             \
                $(if $(arg-check),                                           \
                    $(if $(cmd_$@),- due to command line change,             \
                        $(if $(filter $@, $(targets)),                       \
                            - due to missing .cmd file,                      \
                            - due to $(notdir $@) 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 $@ $(@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) $@;					\
	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)  $@

由于变量quiet_modtag为空,quiet_cmd_cc_o_c又可以展开为

quiet_cmd_cc_o_c = CC  $@

就是打印" CC $@ "这么一句话 。

总结

好了,前面巴拉巴拉一大堆,是时候来个总结啦,就好像千里行军总得找个好地方,整顿行囊,这样才能走得更远,更稳。

  • echo-cmd 函数: 判断命令$ (quiet)cmd_$ (1)或者cmd_$(1)是否定义如果有定义,echo-cmd就会将这个命令打印出来。且后面紧跟着这条命令的具体执行;
  • escsq 函数 : 转义单引号(在单引号前面加个 \),以便在echo语句中使用;
  • $(call echo-cmd,cc_o_c) : 命令行未传入V参数值的情况下 展开为echo ’ CC $@’ ;
  • $(call echo-cmd,checksrc) : 返回空,不关注;
  • $ (call echo-cmd,record_mcount) : 返回空,不关注;
  • echo-why 变量 :未定义,不关注;
  • cmd_modversions 变量 :未定义,不关注;
  • cmd_record_mcount 变量 :未定义,不关注;

posted on 2022-11-02 22:23  BSP-路人甲  阅读(249)  评论(0编辑  收藏  举报

导航