U-boot顶层Makefile分析-第二节

二、make xxx_defconfig过程

在编译uboot之前要使用“make xxx_defconfig”命令来配置uboot,过程源码如下:

version_h := include/generated/version_autogenerated.h
timestamp_h := include/generated/timestamp_autogenerated.h

no-dot-config-targets := clean clobber mrproper distclean \
			 help %docs check% coccicheck \
			 ubootversion backup

config-targets := 0
mixed-targets  := 0
dot-config     := 1

version_h和timestamp_h两个变量保存了两个自动生成的文件,接着定义了4个变量的值。

ifneq ($(filter $(no-dot-config-targets), $(MAKECMDGOALS)),)
	ifeq ($(filter-out $(no-dot-config-targets), $(MAKECMDGOALS)),)
		dot-config := 0
	endif
endif

ifeq ($(KBUILD_EXTMOD),)
        ifneq ($(filter config %config,$(MAKECMDGOALS)),)
                config-targets := 1
                ifneq ($(words $(MAKECMDGOALS)),1)
                        mixed-targets := 1
                endif
        endif
endif

MAKECMDGOALS是 make的一个环境变量,这个变量会保存你所指定的终极目标列表,“比如执行“ make mx6ull_alientek_emmc_defconfig”,那么 MAKECMDGOALS就为 mx6ull_alientek_emmc_defconfig。很明显过滤后为空,所以条件不成立,变量 dot-config依
旧为 1。

接着根据前面分析过KBUILD_EXTMOD为空,条件成立继续运行。接着将 MAKECMDGOALS中不符合“ config”和 “%config”的部分过滤掉,如果剩下的部分不为空条件就成立,很明显此处条件成立,变量 config-targets=1。接着统计 MAKECMDGOALS中的单词个数,如果不为1的话条件成立。很明显,MAKECMDGOALS的单词个数是 1个,所以条件不成立,mixed-targets继续为0。

ifeq ($(mixed-targets),1)
# ===========================================================================
# We're called with mixed targets (*config and build targets).
# Handle them one by one.

PHONY += $(MAKECMDGOALS) __build_one_by_one

$(filter-out __build_one_by_one, $(MAKECMDGOALS)): __build_one_by_one
	@:

__build_one_by_one:
	$(Q)set -e; \
	for i in $(MAKECMDGOALS); do \
		$(MAKE) -f $(srctree)/Makefile $$i; \
	done

else
ifeq ($(config-targets),1)
# ===========================================================================
# *config targets only - make sure prerequisites are updated, and descend
# in scripts/kconfig to make the *config target

KBUILD_DEFCONFIG := sandbox_defconfig
export KBUILD_DEFCONFIG KBUILD_KCONFIG

config: scripts_basic outputmakefile FORCE
	$(Q)$(MAKE) $(build)=scripts/kconfig $@

%config: scripts_basic outputmakefile FORCE
	$(Q)$(MAKE) $(build)=scripts/kconfig $@

else

mixed-targets不为1,执行第一个else后的代码,config-targets=1接着执行分支代码,注意我们是执行make xxx_defconfig,所以我们在这里执行的是带通配符*config这个目标。我们看到它有三个依赖,其中FORCE有如下定义:

PHONY += FORCE
FORCE:

可以看出 FORCE是没有规则和依赖的,所以每次都会重新生成 FORCE。当 FORCE作为
其他目标的依赖时,由于 FORCE总是被更新过的,因此依赖所在的规则总是会执行的。

再来看剩下两个依赖:

# Basic helpers built in scripts/
PHONY += scripts_basic
scripts_basic:
	$(Q)$(MAKE) $(build)=scripts/basic
	$(Q)rm -f .tmp_quiet_recordmcount

# To avoid any implicit rule to kick in, define an empty command.
scripts/basic/%: scripts_basic ;

PHONY += outputmakefile
# outputmakefile generates a Makefile in the output directory, if using a
# separate output directory. This allows convenient use of make in the
# output directory.
outputmakefile:
ifneq ($(KBUILD_SRC),)
	$(Q)ln -fsn $(srctree) source
	$(Q)$(CONFIG_SHELL) $(srctree)/scripts/mkmakefile \
	    $(srctree) $(objtree) $(VERSION) $(PATCHLEVEL)
endif

KBUILD_SRC在前面已经分析过为空值了,所以条件不成立,outputmakefile无效

scripts_basic的规则,其对应的命令用到了变量 Q、 MAKE和 build,其中

Q=@或为空
MAKE=make

变量 build是在 scripts/Kbuild.include文件中有定义,实际值如下:

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

scripts_basic展开以后如下:

scripts_basic:
    @make -f ./scripts/Makefile.build obj=scripts/basic     //也可以没有 @,视配置而定
    @rm -f . tmp_quiet_recordmcount                         //也可以没有 @

回到%config处,看完目标和依赖后,将命令为展开为:

@make -f ./scripts/Makefile.build obj=scripts/kconfig xxx_defconfig    //也可以没有@

我们看到scripts_basic和%config调用的两个命令都用到了文件 scripts/Makefile.build,我们接下来重点分析一下scripts/Makefile.build文件。

先分析scripts_basic下调用此文件

# Modified for U-Boot
prefix := tpl
src := $(patsubst $(prefix)/%,%,$(obj))
ifeq ($(obj),$(src))
prefix := spl
src := $(patsubst $(prefix)/%,%,$(obj))
ifeq ($(obj),$(src))
prefix := .
endif
endif

patsubst是替换函数,格式如下:

$(patsubst <pattern>,<replacement>,<text>)

此函数用于在 text中查找符合pattern的部分,如果匹配的话就用replacement替换掉。pattenr是可以包含通配符“ “%”,如果 replacement中也包含通配符“ “%”,那么 replacement中的这个“ “%”将是 pattern中的那个“ “%”所代表的字符串。函数的返回值为替换后的字符串。因此,第 10行就是在“ scripts/basic”中查找符合 tpl/%”的部分,然后将 tpl/”取消掉,但是scripts/basic”没有 tpl/”,所以 src= scripts/basic。同理src也是如此,接着进行一个判断,是否相等,相等的话条件成立,很明显,此处条件成立。prefix为.。

接着看:

# 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)

将它们展开得到:

kbuild-dir=./scripts/basic
kbuild-file= ./scripts/basic/Makefile

继续分析: 

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

__build是默认目标,因为命令“@make -f ./scripts/Makefile.build obj=scripts/basic”没有指
定目标,所以会使用到默认目标:__build。在顶层 Makefile中,KBUILD_BUILTIN为 1
KBUILD_MODULES为 0,因此展开后目标 __build为:  

__build:$(builtin-target) $(lib-target) $(extra-y)) $(subdir-ym) $(always)
    @:

可以看出目标 __build有 5个依赖: builtin-target、lib-target、extra-y、subdir-ym和 always。但只有 always有效(具体过程可打印变量查看),因此 __build最终为:

__build: scripts/basic/fixdep
    @:

__build依赖于 scripts/basic/fixdep,所以要 先编译 scripts/basic/fixdep.c,生成 fixdep,前面
已经读取了 scripts/basic/Makefile文件。
综上所述,scripts_basic目标的作用就是编译出 scripts/basic/fixdep这个软件。

接下来分析%config目标对应的命令:

结合目标命令以及上面的相同分析,我们得出相关几个变量的值:

src= scripts/kconfig 
kbuild-dir = ./scripts/kconfig 
kbuild-file = ./scripts/kconfig/Makefile 
include ./scripts/kconfig/Makefile

scripts/kconfig/Makefile文件有如下所示内容: 

%_defconfig: $(obj)/conf
	$(Q)$< $(silent) --defconfig=arch/$(SRCARCH)/configs/$@ $(Kconfig)

# Added for U-Boot (backward compatibility)
%_config: %_defconfig
	@:

目标%_defconfig刚好和我们输入的 xxx_defconfig匹配,所以会执行这条规则。依赖为
$(obj)/conf,展开后就是 scripts/kconfig/conf。接下来就是检查并生成依赖 scripts/kconfig/conf。
conf是主机软件,到这里我们就打住,不要纠结。

得到scripts/kconfig/conf 以后就要执行目标%_defconfig 的命令,将其展开为:

@ scripts/kconfig/conf --defconfig=arch/../configs/xxx_defconfig Kconfig

这里会将mx6ull_alientek_emmc_defconfig 中的配置输出到.config 文件中,最终生成uboot 根目录下的.config 文件。

至此,make xxx_defconfig 就分析完了,附上正点的流程图。

三、make过程

配置好uboot 以后就可以直接make 编译了,因为没有指明目标,所以会使用默认目标,主
Makefile 中的默认目标为_all,根据前面的分析,这里不编译模块,_all的依赖就是 all。在主 Makefile中 all目标规则如下:

all:		$(ALL-y)
ifneq ($(CONFIG_SYS_GENERIC_BOARD),y)
	@echo "===================== WARNING ======================"
	@echo "Please convert this board to generic board."
	@echo "Otherwise it will be removed by the end of 2014."
	@echo "See doc/README.generic-board for further information"
	@echo "===================================================="
endif
ifeq ($(CONFIG_DM_I2C_COMPAT),y)
	@echo "===================== WARNING ======================"
	@echo "This board uses CONFIG_DM_I2C_COMPAT. Please remove"
	@echo "(possibly in a subsequent patch in your series)"
	@echo "before sending patches to the mailing list."
	@echo "===================================================="
endif

可以看到,all目标依赖 $(ALL-y),而在顶层 Makefile中,ALL-y如下:

# Always append ALL so that arch config.mk's can add custom ones
ALL-y += u-boot.srec u-boot.bin u-boot.sym System.map u-boot.cfg binary_size_check

ALL-$(CONFIG_ONENAND_U_BOOT) += u-boot-onenand.bin
ifeq ($(CONFIG_SPL_FSL_PBL),y)
ALL-$(CONFIG_RAMBOOT_PBL) += u-boot-with-spl-pbl.bin
else
ifneq ($(CONFIG_SECURE_BOOT), y)
# For Secure Boot The Image needs to be signed and Header must also
# be included. So The image has to be built explicitly
ALL-$(CONFIG_RAMBOOT_PBL) += u-boot.pbl
endif
endif
ALL-$(CONFIG_SPL) += spl/u-boot-spl.bin
ALL-$(CONFIG_SPL_FRAMEWORK) += u-boot.img
ALL-$(CONFIG_TPL) += tpl/u-boot-tpl.bin
ALL-$(CONFIG_OF_SEPARATE) += u-boot.dtb
ifeq ($(CONFIG_SPL_FRAMEWORK),y)
ALL-$(CONFIG_OF_SEPARATE) += u-boot-dtb.img
endif
ALL-$(CONFIG_OF_HOSTFILE) += u-boot.dtb
ifneq ($(CONFIG_SPL_TARGET),)
ALL-$(CONFIG_SPL) += $(CONFIG_SPL_TARGET:"%"=%)
endif
ALL-$(CONFIG_REMAKE_ELF) += u-boot.elf
ALL-$(CONFIG_EFI_APP) += u-boot-app.efi
ALL-$(CONFIG_EFI_STUB) += u-boot-payload.efi

ifneq ($(BUILD_ROM),)
ALL-$(CONFIG_X86_RESET_VECTOR) += u-boot.rom
endif

# enable combined SPL/u-boot/dtb rules for tegra
ifeq ($(CONFIG_TEGRA)$(CONFIG_SPL),yy)
ALL-y += u-boot-tegra.bin u-boot-nodtb-tegra.bin
ALL-$(CONFIG_OF_SEPARATE) += u-boot-dtb-tegra.bin
endif

# Add optional build target if defined in board/cpu/soc headers
ifneq ($(CONFIG_BUILD_TARGET),)
ALL-y += $(CONFIG_BUILD_TARGET:"%"=%)
endif

ALL-y包含 u-boot.srec、 u-boot.bin、u-boot.sym、System.map、u-boot.cfg和binary_size_check这几个文件。根据 uboot的配置情况也可能包含其他的文件,这里主要是因为可以对这些变量进行赋值,就是所谓使能。ALL-y里面有个 u-boot.bin,这个就是我们最终需要的 uboot二进制可执行文件,所做的所有工作就是为了它。

在顶层 Makefile中找到 u-boot.bin目标对应的规则,如下所示:

ifeq ($(CONFIG_OF_SEPARATE),y)
u-boot-dtb.bin: u-boot-nodtb.bin dts/dt.dtb FORCE
	$(call if_changed,cat)

u-boot.bin: u-boot-dtb.bin FORCE
	$(call if_changed,copy)
else
u-boot.bin: u-boot-nodtb.bin FORCE
	$(call if_changed,copy)
endif

首先判断 CONFIG_OF_SEPARATE是否等于 y,如果相等,那条件就成立,在 .config中搜索“ CONFIG_OF_SEPARAT”,没有找到,说明条件不成立。运行else后的代码,就是目标u-boot.bin的规则,目标u-boot.bin依赖于 u-boot-nodtb.bin,命令为 $(call if_changed,copy),这里调用了 if_changed,if_changed是一个函数,这个函数在scripts/Kbuild.include中有定义,而顶层Makefile中会包含 scripts/Kbuild.include文件。函数引用的变量比较多,也比较绕,我们只需要知道它可以从u-boot-nodtb.bin生成 u-boot.bin就行了。

生成 u-boot-nodtb.bin文件用到的顶层Makefile中相关代码如下:

u-boot-nodtb.bin: u-boot FORCE
	$(call if_changed,objcopy)
	$(call DO_STATIC_RELA,$<,$@,$(CONFIG_SYS_TEXT_BASE))
	$(BOARD_SIZE_CHECK)

我们可以看到,目标 u-boot-nodtb.bin又依赖于 u-boot,顶层 Makefile中 u-boot相关规则如下:

u-boot:	$(u-boot-init) $(u-boot-main) u-boot.lds FORCE
	$(call if_changed,u-boot__)
ifeq ($(CONFIG_KALLSYMS),y)
	$(call cmd,smap)
	$(call cmd,u-boot__) common/system_map.o
endif

目标 u-boot依赖于 u-boot_init、 u-boot-main和 u-boot.lds u-boot_init和 u-boot-main是两个变量,在顶层 Makefile中有定义:

u-boot-init := $(head-y)
u-boot-main := $(libs-y)

分析得到: 

u-boot-init= arch/arm/cpu/armv7/start.o

$(libs-y)在顶层 Makefile中被定义为 uboot所有子目录下 build-in.o的集合,从主Makefile620行开始中看出。

libs-y := $(patsubst %/, %/built-in.o, $(libs-y))

这里调用了函数 patsubst,将 libs-y中的“ “/”替换为 ”/built-in.o”,比如 drivers/dma/”就变为了“ drivers/dma/built-in.o”,相当于将 libs-y改为所有子目录中 built-in.o文件的集合。那么 u-boot-main就等于所有子目录中 built-in.o的集合。

这个规则就相当于将以 u-boot.lds为链接脚本,将 arch/arm/cpu/armv7/start.o和各个子目录下的 built-in.o链接在一起生成 u-boot。

接下来的重点就是各子目录下的 built-in.o是怎么生成的,以 drivers/gpio/built-in.o为例,在
drivers/gpio/目录下会有个名为.built-in.o.cmd的文件,此文件内容如下:

cmd_drivers/gpio/built-in.o := arm-linux-gnueabihf-ld.bfd -r -o drivers/
gpio/built-in.o drivers/gpio/mxc_gpio.o

从命令“ cmd_drivers/gpio/built-in.o”可以看出 drivers/gpio/built-in.o这个文件是使用 ld命令由文件 drivers/gpio/mxc_gpio.o生成而来的,mxc_gpio.o是 mxc_gpio.c编译生成的 .o文件,这个是 NXP的 I.MX系列的 GPIO驱动文件。其中的-r参数产生可重定向的输出,比如,产生一个输出文件它可再次作为 ld 的输入,这经常被叫做部分链接 ”,当我们需要将几个小的 .o文件链接成为一个 .o文件的时候,需要使用此选项。最终将各个子目录中的 built-in.o文件链接在一起就形成了u-boot。

我们重新编译,并在命令行设置V=1。可以看出最终是用 arm-linux-gnueabihf-ld.bfd命令将 arch/arm/cpu/armv7/start.o和其他众多的 built_in.o链接在一起,形成 u-boot

目标all除了 u-boot.bin以外还有其他的依赖,比如 u-boot.srec 、 u-boot.sym 、 System.map、u-boot.cfg和 binary_size_check等等,这些依赖的生成方法和 u-boot.bin很类似,就不再叙述了。最后附上正点的make流程图:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值