Kbuild 深度解析:1. scripts_basic 的规则是如何被制定的

文章详细探讨了Linux内核构建系统Kbuild中的关键组件Makefile.build的工作原理,特别是如何处理编译目标,如hostprogs的分类和编译。通过分析Makefile.build、Makefile.lib和Makefile.host,揭示了Kbuild如何为不同类型的编译对象指定规则,以及如何批量处理编译任务。
摘要由CSDN通过智能技术生成

kbuild 的机制和流程还是比较复杂的。
从某个点切入,是个比较好的办法。

uboot 和 Linux kernel 的主 Makefile 里面,都有下面这段话:

PHONY += scripts_basic
scripts_basic:
	$(Q)$(MAKE) $(build)=scripts/basic
	$(Q)rm -f .tmp_quiet_recordmcount

重点分析 $(Q)$(MAKE) $(build)=scripts/basic,忽略 $(Q),展开得:

make -f ./scripts/Makefile.build obj=scripts/basic

关于这句话,有很多的故事要讲。

$(build)的展开

主 Makefile 里面,有一个非常重要的 include:

include scripts/Kbuild.include

这是一个头文件,相当于把 Kbuild.include 的内容直接写到主 Makefile 里。
Kbuild.include 代码片段:

###
# Shorthand for $(Q)$(MAKE) -f scripts/Makefile.build obj=
# Usage:
# $(Q)$(MAKE) $(build)=dir
build := -f $(srctree)/scripts/Makefile.build obj

这句话的实质,是变量的字符串展开;就是说 build 被当做了 Kbuild 的一个保留的字符串变量。
所以 $(Q)$(MAKE) $(build)=scripts/basic 的展开,就是把$(build) 用 := 后的字符串替换:

make -f ./scripts/Makefile.build obj=scripts/basic

-f 是 Makefile 的语法:

The arguments ‘-f name’ or ‘–file=name’ tell make to read the file name as the makefile.

Kbuild 之灵魂 Makefile.build(一)

Kbuild 中,几乎或者全部的 .o 都是由 Makefile.build 来完成编译的。
Makefile.build 会为不同类型的编译对象,指定不同的规则。类似于大家喜闻乐见的:

gcc -c -o hello.o hello.c


但是 Kbuild 追求的是批量化编译,所以指定规则的过程很复杂,需要认真分析下。
这篇文章,只分析 hostprogs 相关的规则生成和编译。
hostprogs 是在编译过程中主机所用到的一些工具;hostprogs 编译的输出件是运行在主机上的,最后不会被链接到目标文件中。

make -f ./scripts/Makefile.build obj=scripts/basic

过滤掉 Makefile.build 中和 hostprogs 无关的代码,得:

src := $(obj)
# $(obj) 是传入的参数,即 scripts/basic
PHONY := __build
__build:
...
include scripts/Kbuild.include

kbuild-dir := $(if $(filter /%,$(src)),$(src),$(srctree)/$(src))
kbuild-file := $(if $(wildcard $(kbuild-dir)/Kbuild),$(kbuild-dir)/Kbuild,$(kbuild-dir)/Makefile)
include $(kbuild-file)

include scripts/Makefile.lib

hostprogs := $(sort $(hostprogs))
ifneq ($(hostprogs),)
include scripts/Makefile.host
endif
...
__build: $(if $(KBUILD_BUILTIN), $(targets-for-builtin)) \
	 $(if $(KBUILD_MODULES), $(targets-for-modules)) \
	 $(subdir-ym) $(always-y)
	@:

PHONY += FORCE
FORCE:
...
.PHONY: $(PHONY)

找到子目录中的 Makefile

关于 $(kbuild-file) 的分析,暂略。
这些语句,相当于 include 了scripts/basic/Makefile
这个文件就一句话:

# scripts/basic/Makefile
hostprogs-always-y	+= fixdep

使用 Makefile.lib 对编译目标进行分类和整理

scripts/Makefile.lib 会对各种编译目标进行转换、抽象和提取。
编译目标的种类有很多,这里只追踪 hostprogs 相关的。

# scripts/Makefile.lib
hostprogs += $(hostprogs-always-y) $(hostprogs-always-m)
always-y += $(hostprogs-always-y) $(hostprogs-always-m)
always-y	:= $(addprefix $(obj)/,$(always-y))

这样,可以获得
hostprogs = fixdep
always-y = scripts/basic/fixdep

是的,虽然 Makefile.build 这个文件很长,但是真正和 scripts/basic 相关的,就上面 3 行代码。

Makefile.host 所做的工作

因为 hostprogs 不为空,所以引入了另一个包含:

# scripts/Makefile.build
ifneq ($(hostprogs),)
include scripts/Makefile.host
endif

scripts/Makefile.host 这个文件会将 $(hostprogs) 转换为 $(host-csingle)$(host-cmulti) 等目标。

这里需要介绍下 host-csinglehost-cmultihost-cobjshost-cxxmultihost-cxxobjs 这几个变量。
这是 Makefile.host 所能处理的几类编译目标。
Makefile.host 会为这几类目标生成编译规则。
说到底,Kbuild 的这一套机制,目的就是为不同的编译目标生成编译规则。

host-cobjs 实例

这里只是举例,在编译 scripts/basic 时是不会用到 host-cobjs 类型的目标的。
介绍 host-cobjs,只是为后面分析 host-csingle 做铺垫。
以 qconf 为例,scripts/kconfig/Makefile 中的代码段:

# scripts/kconfig/Makefile
# object files used by all kconfig flavours
common-objs	:= confdata.o expr.o lexer.lex.o parser.tab.o preprocess.o \
		   symbol.o util.o
hostprogs	+= qconf
qconf-cxxobjs	:= qconf.o qconf-moc.o
qconf-objs	:= images.o $(common-objs)

然后可得:

hostprogs = qconf,也许还有其他的,此处忽略
qconf-objs = images.o confdata.o expr.o lexer.lex.o parser.tab.o preprocess.o symbol.o util.o

Makefile.host 对 host-cobjs 的处理

# scripts/Makefile.host
# Object (.o) files compiled from .c files
host-cobjs	:= $(sort $(foreach m,$(hostprogs),$($(m)-objs)))	# 语句1
host-cobjs	:= $(addprefix $(obj)/,$(host-cobjs))	# 语句2

foreach 的作用,是将 $(hostprogs) 中的变量逐个取出,然后带入到 $($(m)-objs))
即得到$(qconf-objs)

注意,$(qconf-objs) 还会被展开一次,即得到
images.o confdata.o expr.o lexer.lex.o parser.tab.o preprocess.o symbol.o util.o

所以,语句1 的结果是:

host-cobjs := images.o confdata.o expr.o lexer.lex.o parser.tab.o preprocess.o symbol.o util.o

语句2 是为上面的这些 .o 添加目录前缀。

所以 $(host-cobjs) 实际上是若干 .c 对应的 .o 的集合。

当为 $(host-cobjs) 制定好规则以后,上面的 images.o confdata.o ... 所对应的 images.c confdata.c ... 均会批量化的按照制定的规则进行编译。

# 这里用到了“静态模式”的概念,暂不展开
$(host-cobjs): $(obj)/%.o: $(src)/%.c FORCE
	$(call if_changed_dep,host-cobjs)

if_changed_dep,的这个逗号后面,一定不要加空格什么的
或者说 call 的用法就这样,逗号后面不要加不相关的字符
另外,对应的 cmd_cpp_lds 等命令,一定用延时赋值“=”,而不是直接赋值“:=”,因为:
如果命令中用到 $@ 或者 $<时,延时赋值可以展开,直接赋值不会展开。
一定要注意!!!!!

host-csingle 实例

Makefile.host 对 host-csingle 的处理

# scripts/Makefile.host
# C code
# Executables compiled from a single .c file
host-csingle	:= $(foreach m,$(hostprogs), \
			$(if $($(m)-objs)$($(m)-cxxobjs),,$(m)))	# 语句1
host-csingle	:= $(addprefix $(obj)/,$(host-csingle))	# 语句2

$(if ...) 函数的用法,暂略。
此处 hostprogs = fixdep,第一次带入得 $(fixdep-objs)$(fixdep-cxxobjs),因为它们两个展开后,均为空,所以语句1 的返回结果就是 $(host-csingle) = fixdep
语句2 为 fixdep 添加了前缀,$(host-csingle) = scripts/basic/fixdep
所以,$(host-csingle) 指的是那些由单个 .c 编译而成的可执行文件,比如说 fixdep

Makefile.host 为 host-csingle 制定了编译规则。

# Create executable from a single .c file
# host-csingle -> Executable
quiet_cmd_host-csingle 	= HOSTCC  $@
      cmd_host-csingle	= $(HOSTCC) $(hostc_flags) $(KBUILD_HOSTLDFLAGS) -o $@ $< \
		$(KBUILD_HOSTLDLIBS) $(HOSTLDLIBS_$(target-stem))
$(host-csingle): $(obj)/%: $(src)/%.c FORCE
	$(call if_changed_dep,host-csingle)

这个编译规则,暂不展开解析了。

再回到 scripts/Makefile.build

Makefile.host 为目标制定了规则。但是,制定规则不代表调用规则。
那 scripts_basic 的规则是怎么被调用的呢?
Makefile.build 的默认编译目标是 __build:

# scripts/Makefile.build
PHONY := __build
__build:
...
__build: $(if $(KBUILD_BUILTIN), $(targets-for-builtin)) \
	 $(if $(KBUILD_MODULES), $(targets-for-modules)) \
	 $(subdir-ym) $(always-y)
	@:

其实,这里有个难以理解和追踪的地方了:
__build 中的有效依赖是 $(always-y) ,但是搜索编整个 Makefile 工程都找不到对 $(always-y) 目标的编译——没有为 $(always-y) 设计编译规则。
那么,目标是怎么被编译的呢?
前面 scripts/Makefile.host 中得到了 $(host-csingle) ,虽然 __build 中并没有包含 $(host-csingle) ,但是 $(always-y)$(host-csingle) 包含了相同的目标 scripts/basic/fixdep
所以 __build 对 $(always-y) 的依赖就是对 scripts/basic/fixdep 的依赖。
scripts/basic/fixdep 的编译规则已经被 $(host-csingle) 制定了。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值