当前位置: 首页 > 工具软件 > ko-build > 使用案例 >

linux内核Makefile中的变量build--- 过渡篇(五)

丰飞龙
2023-12-01

一. kbuild系统主要涉及的几个文件

文件名作用
Makefile内核源代码顶层目录的Makefile文件
scripts/Makefile.build通常在进行递归make时会用到的Makefile文件
scripts/Makefile.host如果需要生成可执行文件时会用到的文件。例如:在编译内核之前需要配置内核,此时会编译生成配置程序,在这个过程中会用到这个文件
scripts/Kbuild.include可以看作是kbuild系统的头文件。定义了一些常用的变量。其中有一个变量很重要:build。
scripts/Makefile.lib负责归类分析obj-y、obj-m和其中的目录subdir-ym所使用的规则
scripts/Makefile.modinst在安装模块时会用到的文件
Makefile.headerinst内核头文件安装时使用的规则
Makefile.modpost模块编译的第二阶段,由.o和.mod生成.ko时使用的规则
Makefile.clean内核源码目录清理规则

二. 常用目标分类

目标 常用目标举例作用
配置
config 启动Kconfig,以不同界面来配置内核。
xconfig
menuconfig
编译
all编译vmlinux内核映像和内核模块
vmlinux编译vmlinux内核映像
modules编译内核模块
安装
headers_install 安装内核头文件/模块。
modules_install
源码浏览
tags 生成代码浏览工具所需要的文件。
TAGS
cscope
静态分析
checkstack 检查并分析内核代码。
namespacecheck
headers_check
文档转换%doc把kernel文档转成不同格式
内核打包%pkg以不同的安装格式编译内核
构架相关(以arm为例)
zImage生成压缩的内核映像/td>
uImage生成压缩的u-boot可引导的内核映像
install安装内核映像

其中的构架相关目标在顶层Makefile上并未出现,而是被包含在平台相关的Makefile(arch/$(ARCH)/Makefile)中。

三. build变量的定义
build变量定义在scripts/Kbuild.include中:

build := -f $(if $(KBUILD_SRC),$(srctree)/)scripts/Makefile.build obj

其中$(KBUILD_SRC)正常情况下为空,所以变量的定义可简化为:

build := -f scripts/Makefile.build obj

四. 展开(MAKE)$(build)= 后的形式
build的常用形式:

$(MAKE) $(build)=$(build-dir) [para]

其中[para] 可选。使用scripts/Kbuild.include中的$(build)变量定义,进行变量替换,上述命令被展开为:

$(MAKE) -f scripts/Makefile.build obj=$(build-dir)  [para]

Make进入由参数-f指定的Make文件scripts/Makefile.build,并传入参数 obj=$(build-dir)和para。

在scripts/Makefile.build的处理过程中,$(obj)代表此次Make命令要处理(编译、链接、和生成)文件所在的目录,该目录下的Makefile文件通常情况下都会被Makefile.build所引用。引用方法如下:

# 在递归时包含子目录的Kbuild(如果存在的话)或者Makefile文件
src := $(obj)
...
# The filename Kbuild has precedence over Makefile
kbuild-dir := $(if $(filter /%,$(src)),$(src),$(srctree)/$(src))
kbuild-file := $(if $(wildcard $(kbuild-dir)/Kbuild),$(kbuild-dir)/Kbuild,$(kbuild-dir)/Makefile)
include $(kbuild-file)

$ (obj)目录下的Makefile暂记为$(obj)/Makefile。针对Make命令,有两种情况:不指定Make目标和指定目标。

1. 不指定目标
比如:

$(MAKE) $(build)=init

由于未指定目标,这时会使用Makefile.build中的默认目标__build。然后更进一步,会使用$(obj)/Makefile(init/Makefile)中定义的变量来进行目标匹配。

__build在Makefile.build中的构建规则为:

__build: $(if $(KBUILD_BUILTIN),$(builtin-target) $(lib-target) $(extra-y)) \
	 $(if $(KBUILD_MODULES),$(obj-m) $(modorder-target)) \
	 $(subdir-ym) $(always)
	@:

首先会构建该默认目标__build的依赖。Make会寻找重建这些依赖的规则。而这些规则要么在当前的Makefile文件Makefile.build中,要么在Makefile.build include的$ (obj)/Makefile中在不指定Make目标的情况下,会使用Makefile.build中的构建规则来重建依赖。相应地,只重构那些在$(obj)/Makefile定义的依赖。其他的依赖要么不满足条件,要么找不到重构规则而被忽略。

---------------------------举例 -----------------------
例如命令 $ (MAKE) $ (build)=init:
没有指明Make目标,那么将使用Makefile.build中的默认目标__build,且会包含init目标的Makefile文件,即init/Makefile。
在__build规则中,因$ (KBUILD_BUILTIN)被主目录设置为1,且export,所以将首先重建依赖$ (builtin-target)所以将首先重建依赖$ (builtin-target)、$ (lib-target) 、$ (extra-y))、$ (subdir-ym) $ (always) (删除线的依赖一般为空,不为空的场景这里就不再讲解)。

# scripts/Makefile.build
ifneq ($(strip $(obj-y) $(obj-m) $(obj-n) $(obj-) $(subdir-m) $(lib-target)),)
builtin-target := $(obj)/built-in.o
endif

$(builtin-target): $(obj-y) FORCE
	$(call if_changed,link_o_target)

该规则同样要首先重建依赖$ (obj-y)。而$ (obj-y)在init/Makefile中定义且被赋值。这时Make又会查找$ (obj-y)包含文件的构建规则。同样地,该规则要么在Makefile.build中,要么在$ (obj)/Makefile中。此处$(obj-y)为.o文件的合集(main.o version.o mounts.o initramfs.o calibrate.o),.o文件的构建规则在Makefile.build中被定义:

# Built-in and composite module parts
$(obj)/%.o: $(src)/%.c FORCE
	$(call cmd,force_checksrc)
	$(call if_changed_rule,cc_o_c)

如果不懂if_changed_rule函数的使用,请移步到内核源代码单个.o文件的编译过程(六)中,里面有一小节有讲到这个函数)。

$(call if_changed,link_o_target)即调用cmd_link_o_target 生成init/built-in.o。定义如下

# scripts/Makefile.build
#
# Rule to compile a set of .o files into one .o file
#
ifdef builtin-target
quiet_cmd_link_o_target = LD      $@
# If the list of objects to link is empty, just create an empty built-in.o
cmd_link_o_target = $(if $(strip $(obj-y)),\
		      $(LD) $(ld_flags) -r -o $@ $(filter $(obj-y), $^) \
		      $(cmd_secanalysis),\
		      rm -f $@; $(AR) rcs $@)

实际执行时的打印如下:

 arm-linux-ld -EL    -r -o init/built-in.o init/main.o init/version.o init/mounts.o init/initramfs.o init/calibrate.o 

-----------------举例结束 --------------------

一直在重复这样一个递归“规则----> 规则目标–>依赖—>重建依赖—>依赖的规则---->…”的过程,直至最后的目标文件被构建,然后逆推,由依赖层层重建其规则目标。如果构建的最终目标是主机程序。

小结:
由于没有指定Make目标,那么将使用Makefile.build的默认目标__build,建构的入口点就在此。Make在Makefile.build 和$(obj)/Makefile中寻找 __build依赖的重建规则。依次变量展开,依赖层层递归重建。

2. 指定目标
一般情况下,在(MAKE) $ (build)=build_dir [para] 中,通过参数[para] 指定Make目标时,使用的是$ (obj)/Makefile文件中构建规则。这时,在$ (obj)/Makefile文件中不仅要给一些变量赋值,且还包含本目录下目标的重建规则。

比如在顶层Makefile中,我们常见的%config目标:

%config: scripts_basic outputmakefile FORCE
   $(Q)mkdir -p include/linux include/config
   $(Q)$(MAKE) $(build)=scripts/kconfig $@

在此指定Make目标为自动化变量$@,当我们输入类似如下的命令:

make menuconfig

那么,上述Make命令会被替换为:

make -f scripts/Makefile.build obj=scripts/kconfig menuconfig

即指定Make的目标为 menuconfig。那么在$(obj)目录(scripts/kconfig)下的Makefile中包含menuconfig模式匹配规则。其他的流程和上节的"不指定目标"处理类似。在此不再赘述。

五. scripts/Makefile.build文件总框架

#定义src变量
src:=$(obj)

#包含几个关键的文件
-include include/config/auto.conf  #if xxx_CONFIG配置选项
include scripts/Kbuild.include    #if_changed、echo-cmd等变量

#包含obj目录下的Kbuild或Makefile文件
kbuild-dir:=$(if $(filter /%,$(src)),$(src),$(srctree)/$(src))
kbuild-file:=$(if $(wildcard $(kbuild-dir)/Kbuild),$(kbuild-dir)/Kbuild,$(kbuild-dir)/Makefile)
include$(kbuild-file)   

#包含Makefile.lib
include scripts/Makefile.lib  

#编译主机程序时,要包含Makefile.host
ifneq ($(hostprogs-y)$(hostprogs-m),)
include scripts/Makefile.host   
endif

#定义builtin-target
ifneq ($(strip $(obj-y) $(obj-m) $(obj-n) $(obj-) $(subdir-m) $(lib-target)),)
builtin-target:=$(obj)/built-in.o
endif

#__build构建规则
__build:$(if $(KBUILD_BUILTIN),$(builtin-target) $(lib-target) $(extra-y))\
    $(if $(KBUILD_MODULES),$(obj-m) $(modorder-target))\
    $(subdir-ym)$(always)
    @:

#$(obj)/built-in.o的生成规则,注意先要构建依赖$(obj-y)
$(builtin-target): $(obj-y) FORCE
	$(call if_changed,link_o_target)

# 普通模式匹配规则
define rule_cc_o_c
	$(call echo-cmd,checksrc) $(cmd_checksrc)			  \
	$(call echo-cmd,cc_o_c) $(cmd_cc_o_c);				  \
	$(cmd_modversions)						  \
	$(call echo-cmd,record_mcount)					  \
	$(cmd_record_mcount)						  \
	scripts/basic/fixdep $(depfile) $@ '$(call make-cmd,cc_o_c)' >    \
	                                              $(dot-target).tmp;  \
	rm -f $(depfile);						  \
	mv -f $(dot-target).tmp $(dot-target).cmd
endef

# Built-in and composite module parts
$(obj)/%.o: $(src)/%.c FORCE
	$(call cmd,force_checksrc)
	$(call if_changed_rule,cc_o_c)
 类似资料: