linux kernel和最新的uboot都采用了kbuild来实现构建。所以有必要了解一下KBuild基础知识。
KBuild是基于gnu make 实现的一个内核构建系统,也就是对于gnu make的扩展,将公共的一些部分提取到scripts/makefile.build中,一些使用的函数提取到scripts/makefile.lib中。
通过在顶层的makefile中include scripts/Kbuild.include来指定这些变量的使用。
比如Kbuild.include中有 build := -f $(srctree)/scripts/Makefile.build obj 就指定了makefile文件为Makefile.build
在其他的makefile中
$(MAKE) $(build)=dts dtbs 就可以展开进行使用。
比如uboot的 make rpi_defconfig 执行后
$(Q)$(MAKE) $(build)=scripts/kconfig $@ 就被展开成了
@make -f ./scripts/Makefile.build obj=scripts/kconfig rpi_defconfig
obj=xxxx通常是一个目录。后续的Makefile.build访问$(obj)就是这个目录了。
Makefile.build首先就定义了一个伪目标
PHONY := __build
__build:
第一个__build:是空目标。避免后面include其他文件的时候寻找隐含的第一个目标。
后续的这个__build才是主角
__build: $(if $(KBUILD_BUILTIN),$(builtin-target) $(lib-target) $(extra-y)) \
$(if $(KBUILD_MODULES),$(obj-m) $(modorder-target)) \
$(subdir-ym) $(always)
@:
如果 KBUILD_BUILTIN为1 那么构建 $(builtin-target) $(lib-target) $(extra-y)
如果 obj-m obj-y obj- subdir-m lib-target不为NULL 那么构建$(obj)/built-in.o
ifneq ($(strip $(obj-y) $(obj-m) $(obj-) $(subdir-m) $(lib-target)),)
builtin-target := $(obj)/built-in.o
endif
构建的规则是 构建所有的obj-y。将他们连接成一个o文件
$(builtin-target): $(obj-y) FORCE
$(call if_changed,link_o_target)
如果 KBUILD_MODULES为1 那么构建 $(obj-m) $(modorder-target))
uboot没有modules。
一定会被构建的是是$(subdir-ym)子目录以及 $(always)
如果存在子目录。对于子目录执行
$(subdir-ym):
$(Q)$(MAKE) $(build)=$@
也就是将一些通用的放到makefile.build里面。然后剩下的子目录里面只要简单添加一些obj-x之类的就可以了。