U-BOOT分析之顶层Makefile文件

发布时间 2023-11-22 18:23:43作者: zxddesk

U-BOOT分析(二)之顶层Makefile文件(1)

U-BOOT版本

u-boot版本:    u-boot-2021.01.tar.bz2

Makefile && make简介

      Makefile: 是一个描述文件定义一系列的规则来指定源文件编译的先后顺序,拥有特定的语法规则,makefile文件描述了整个工程中所有文件的__编译顺序,编译规则__,支持函数定义和函数调用,能够直接集成操作系统中的各种命令,Makefile 是支持嵌套的,也就是顶层 Makefile 可以调用子目录中的 Makefile 文件。Makefile 嵌套在大项目中很常见,一般大项目里面所有的源代码都不会放到同一个目录中,各个功能模块的源代码都是分开的,各自存放在各自的目录中。每个功能模块目录下都有一个 Makefile,这个 Makefile 只处理本模块的编译链接工作,这样所有的编译链接工作就不用全部放到一个 Makefile 中,可以使得 Makefile 变得简明容易维护。
      make: 是一个应用程序,解析源程序之间的依赖关系,根据依赖关系自动维护编译工作,执行宿主操作系统中的各种命令,它可以解释makefile中的指令或者说规则,make 支持递归调用,也就是在makefile中使用make命令执行其他的Makefile文件,这个文件一般都是子目录的Makefile文件。假如在当前目录下有一个subdir子目录,这个目录中又有Makefile文件,那么在工程编译的时候其主目录中的Makefile就可以调用子目录中的Makefile,以此来完成子目录中的Makefile编译,主目录中可以使用如下命令来执行子目录中的Makefile:

 $(MAKE) -C subdir
 
  • 1

$(MAKE)就是调用make命令,-C是指定子目录
像我们平时使用的一些IDE的编译按钮就是将 Makefile 和make集成在一起,来编译整个工程,同样u-boot ,以及以后我们的linux内核,驱动,应用等的编译都是这么个流程。

接下来分析u-boot的顶层Makefile文件,u-boot 的顶层Makefile的以下内容将被提到

1.版本号
2.MAKEFLAG
3.HOST_ARCH
4.修改C语言区域设置
5.编译信息的输出模式以及静默输出
6.设置编译结果输出目录
7.代码检查
8.编译外部模块
9.获取主机的CPU架构和操作系统
10.设置交叉编译器,以及配置文件
11.scripts_basic 依赖的生成
12.outputmakefile的输出
13.make xxx_defconfig过程
14.make uboot的编译

1.版本号:
版本号

2.MAKEFLAG: Makefile 的特殊变量
在这里插入图片描述
上边介绍到make是可以递归的,在 make 递归执行的过程中,最上层的 make 称为 主控make ,它的命令行选项,如 “-k”, “-s” 等会通过环境变量 “MAKEFLAGS” 传递给子 make 进程。变量 “MAKEFLAGS” 的值会被主控 make 自动的设置为包含所执行 make 时的命令行选项的字符串。比如主控执行 make 时使用 “-k” 和 “-s” 选项,那么 “MAKEFLAGS” 的值就为 ks 。子 make 进程处理时,会把此环境变量的值作为执行的命令行选项,因此子 make 进程就使用 “-k” 和 “-s” 这两个命令行选项

MAKEFLAGS += -rR --include-dir=$(CURDIR)
 
  • 1

这句话意思是给变量MAKEFLAGS追加了值,MAKEFLAGS,默认情况(没有用“unexport”声明),-rR表示禁止使用内置的隐含规则和表达式,–include-dir指明搜索路径.$(CURDIR)表示当前目录,CURDIR是make的内嵌变量,自动设置为当前目录。

  • 隐含规则则是内建在make 中,为make 提供了重建某一类目标文件(.o 等)的通用方法,同时这些隐含规则所用-到的变量也就是所谓的隐含变量。
  • 隐含规则的好处是在Makefile 中不需要明确给出重建某一个目标的命令,甚至可以不需要规则。make会为你自动搜寻匹配的隐含规则链。
  • 隐含规则的代价之一就是低效,系统必须搜索可能的隐含规则链。同时隐含规则也有可能应用了不是你想要的规则而引入很难debug的错误。

3.HOST_ARCH
在这里插入图片描述
在这里插入图片描述
host_arch.h定义了一些机器名字的代码符号,Makefiel包含这个文件用于下边的机器类型比较。

  • shell uname -m 表示执行uname –m的shell命令,他执行的结果是取出机器硬件名(这台机器是64位的,所以其得到的结果就是x86_64)
  • export将变量传递到子make过程,unexport禁止将变量传递到子make过程。
    23 行表示不导出这个机器平台名字
  • ifeq 等价于 if equal,ifneq 等价于 if not equal
    24-25 行 表示如果获取到的平台名字是 x86_64 就导出这个 HOST_ARCH 且 HOST_ARCH=HOST_ARCH_X86_64,值是定义于 host_arch.h这个文件的。
  • $(findstring FIND,IN)
    • 函数名称:查找字符串函数—findstring。
    • 函数功能:搜索字串“IN”,查找“FIND”字串。
    • 返回值:如果在“IN”之中存在“FIND” ,则返回“FIND”,否则返回空,
      所以26行 就是说明 空空不相等,也就是 MK_ARCH = 某个ix86的值 后边几行的判断也是一样,最后确定 d导出HOST_ARCH 是哪种机器类型。

4.修改C语言区域设置
在locale环境中,有一组变量,代表国际化环境中的不同设置,"C"是系统默认的locale:
LC_ALL是一个宏,如果该值设置了,则该值会覆盖所有LC_*的设置值。注意,LANG的值不受该宏影响。
LC_COLLATE定义该环境的排序和比较规则
LC_NUMERIC非货币的数字显示格式
5.编译信息的输出模式以及静默输出
quiet静默编译当在命令行传入V这个变量的值为1(V=1)时,就会使能quiet、Q变量的值为空,make在执行Makefile命令时就会向屏幕输出所执行的命令;当在命令行不传入V这个变量或者V的值为0(V=0)时,就会使能quiet=quiet_、Q= @,make在执行Makefile命令时就不会向屏幕输出所执行的命令。
export quiet Q KBUILD_VERBOSE之后通过export把这三个变量传给下层makefile

应用变量的语法是:$(变量名)。如KBUILD_VERBOSE = $(V)中的$(V)。
“ifeq”语法是ifeq(; , ; ),功能是比较参数“arg1”和“arg2”的值是否相同。
函数origin并不操作变量的值,只是告诉你你的这个变量是哪里来的。
origin函数的返回值有:
"undefined"从来没有定义过、“default”是一个默认的定义、“
"environment"是一个环境变量、
"file"这个变量被定义在Makefile中
"command line"这个变量是被命令行定义的
"override"是被override指示符重新定义的
"automatic"是一个命令运行中的自动化变量

第105行代码注释:make -s 使用静默方式编译其原理就是构建quiet =silent_。
filter 函数表示以 pattern(样式)模式过滤 text 字符串中的单词,仅保留符合模式 pattern 的单词,可以有多个模式。函数返回值就是符合 pattern 的字符串。
第 108 行判断当前正在使用的编译器版本号是否为 4.x,判断 ( f i l t e r 4. (filter 4.%, (filter4.(MAKE_VERSION))和“ ”(空)是否相等,如果不相等的话就成立,执行里面的语句。
第 109 行firstword函数用于去除text字符串中的第一个单词,函数的返回值就是获取到的单词,同样113行也是匹配 MAKEFLAG 的 -s字符串构建quiet =silent_。

6.设置编译结果输出目录
编译复杂项目,Makefile 有2种编译方法:
1.原地编译:默认情况下是当前文件夹中的.c文件,编译出来的.o文件会放在同一文件夹下,原地编译的好处就是处理起来简单.原地编译有一些坏处:第一,污染了源文件目录。第二的缺陷就是一套源代码只能按照一种配置和编译方法进行处理,无法同时维护2个或2个以上的配置编译方式。
2.单独编译:编译时另外指定一个输出目录,将来所有的编译生成的.o文件或生成的其他文件全部丢到那个输出目录下去。源代码目录不做任何污染,这样输出目录就承载了本次配置编译的所有结果,这样就解决以上2种缺陷。

uboot 可以将编译出来的目标文件输出到单独的目录中,在 make 的时候使用“O”来指定
输出目录,比如“make O=out”就是设置目标文件输出到 out 目录中。
build_dir_set

因为默认的就是原地编译。如果需要指定具体的输出目录编译则有2种方式来指定输出目录。
第一种:make O=输出目录
第二种:export KBUILD_OUTPUT=输出目录 然后再make
如果两个都指定了(既有KBUILD_OUTPUT环境变量存在,又有O=xx),则O=xx具有更高优先级(参考源码注释 124-133行的介绍)
7.代码检查
check code
使用参数​​C=​​来使能代码检查:
1:检查需要重新编译的文件
2:检查所有的源码文件
同样,如果参数C来源于命令行,就将C赋值给环境变量 ​​KBUILD_CHECKSRC​​​,如果没有则变量​​KBUILD_CHECKSRC​​为0
8.编译外部模块
externmodule
如果编译外部模块,则对命令行参数变量M进行赋值
操作符比较:
操作符“:=”与操作符 “+=”的功能相同,只是操作符“:=”后面的用来定义变量(KBUILD_EXTMOD)的变量M只能是前面定义好的,
如果操作符“?=”前面的变量KBUILD_EXTMOD没有定义过,那么就将SUBDIRS赋给KBUILD_EXTMOD;
如果定义过,则语句KBUILD_EXTMOD ?= $(SUBDIRS)什么也不做。
‘?=’’ 为条件赋值操作符仅仅在变量还没有定义的情况下有效。
9.获取主机的CPU架构和操作系统
hostarch
HOSTARCH:获取主机架构与系统名 | 表示管道,管道前的输出作为管道后的输入,sed -e表示替换 ,uname —m表示获取主机架构x86_64,uname -s表示获取系统名称linux
HOSTOS :uname -s 获取主机OS tr ‘[:upper:]’ ‘[:lower:]’ 表示将大写字母替换小写字母。
10.设置交叉编译器,以及配置文件
编译uboot的时候需要设置目标板架构和交叉编译器
“make ARCH=arm
CROSS_COMPILE=arm-linux-gnueabihf-”
就是用于设置 ARCH 和 CROSS_COMPILE
CROSS_COMPILE是定义交叉编译工具链的前缀的。定义这些前缀是为了在后面用(用前缀加上后缀来定义编译过程中用到的各种工具链中的工具)。我们把前缀和后缀分开还有一个原因就是:在不同CPU架构上的交叉编译工具链,只是前缀不一样,后缀都是一样的。因此定义时把前缀和后缀分开,只需要在定义前缀时区分各种架构即可实现可移植性。
在这里插入图片描述
第266行 KCONFIG_CONFIG,如果没定义的话用 KCONFIG_CONFIG =.config,而.config默认是没有的,需要
使用命令“make xxx_defconfig” 对 uboot 进行配置,配置完成以后就会在 uboot 根目录下生成.config。默认情况下.config 和xxx_defconfig 内容是一样的,因为.config 就是从 xxx_defconfig 复制过来的。如果后续调整了 uboot 的一些配置参数,那么这些新的配置参数就添加到了.config 中,而不是 xxx_defconfig。因此xxx_defconfig 只是一些初始配置,而.config 里面的才是实时最新有效的配置。
如果主机操作是darwin(MAC OS的内核),则进行相关设置
hostdarwin
引入kbuild系统的文件定义
kbuild_include
build变量的定义在scripts/Kbuild.include 中定义:
kbuild_include_build
设置CC ,LD,LDR,等的编译套件,其实就是设置前缀 如果前边设置的 CROSS_COMPILE = arm-linux-gnueabi- ,CC=arm-linux-gnueabi-gcc。
套件变量
这其中有的变量已经定义了,有的变量从未出现,比如第二行的变量,而这几个变量就是从根目录下的config.mk来的。config.mk 的内容定义

ARCH := $(CONFIG_SYS_ARCH:"%"=%)
CPU := $(CONFIG_SYS_CPU:"%"=%)
ifdef CONFIG_SPL_BUILD
ifdef CONFIG_ARCH_TEGRA
CPU := arm720t
endif
endif
BOARD := $(CONFIG_SYS_BOARD:"%"=%)
ifneq ($(CONFIG_SYS_VENDOR),)
VENDOR := $(CONFIG_SYS_VENDOR:"%"=%)
endif
ifneq ($(CONFIG_SYS_SOC),)
SOC := $(CONFIG_SYS_SOC:"%"=%)
endif
 

这里面的​​CONFIG_SYS_xxx​​​变量是从配置文件​​.config​​来的
11.scripts_basic 依赖的生成

# ===========================================================================
# Rules shared between *config targets and build targets

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

Q默认=@ ,MAKE =make,build =-f ./scripts/Makefile.build obj,这里obj就是传入的 scripts/basic,上边说过build变量的定义在scripts/Kbuild.include 中定义:因此要生成目标scripts_basic要执行:make -f ./scripts/Makefile.build obj=scripts/basic
12.outputmakefile的输出

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
 

ln是linux中一个非常重要命令,它的功能是为某一个文件在另外一个位置建立一个同不的链接,这个命令最常用的参数是-s,具体用法是:ln –s 源文件 目标文件。当我们需要在不同的目录,用到相同的文件时,我们不需要在每一个需要的目录下都放一个必须相同的文件,我们只要在某个固定的目录,放上该文件,然后在 其它的目录下用ln命令链接(link)它就可以,不必重复的占用磁盘空间。例如:ln –s /bin/less /usr/local/bin/less 在这里由于KBUILD_SRC =空,不执行命令,
13.make xxx_defconfig过程

version_h := include/generated/version_autogenerated.h
timestamp_h := include/generated/timestamp_autogenerated.h
defaultenv_h := include/generated/defaultenv_autogenerated.h
dt_h := include/generated/dt.h

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

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

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

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
 

xxx_config的目标是%config, %是通配符,依赖是​​scripts_basic outputmakefile FORCE​​这三项,​scripts_basic outputmakefile前边都定义了,FORCE 在文件最后定义

PHONY += FORCE
FORCE:
# Declare the contents of the PHONY variable as phony.  We keep that
# information in a variable so we can use it in if_changed and friends.
.PHONY: $(PHONY)
 
  • 1
  • 2
  • 3
  • 4
  • 5

FORCE是没有规则和依赖的,所以每次都会重新生成FORCE,​当FORCE作为其它目标的依赖时,由于FORCE总是被更新过的,所以依赖所在的规则总是会执行的​,因此.config 生成都会 调用2次 Makefile.build脚本
第一次是生成 script_basic目标 make -f ./scripts/Makefile.build obj=scripts/basic
第二次是 %config目标 make -f ./scripts/Makefile.build obj=scripts/kconfig xxx_defconfig
目的是将xxx_defconfig文件的内容输出到配置文件.config中,生成.config文件。

 
 
 
 
 
 
 
 
 
 
 
make xxx_config
%config目标
依赖
scripts_basic
outputmakefile
FORCE
规则
命令:make -f ./scripts/Makefile.build obj=scripts/kconfig xxx_defconfig
作用:根据 xxx_deconfig 生成.config文件
命令:make -f ./scripts/Makefile.build obj=scripts/basic
作用:编译scripts/bastic/fixdep.c

上图基本流程是uboot编译之前make xxx_defconfig是生成.config文件的过程。​
14.make uboot的编译

PHONY += inputs
inputs: $(INPUTS-y)

all: .binman_stamp inputs
ifeq ($(CONFIG_BINMAN),y)
	$(call if_changed,binman)
endif

# Timestamp file to make sure that binman always runs
.binman_stamp: FORCE
	@touch $@

ifeq ($(CONFIG_DEPRECATED),y)
	$(warning "You have deprecated configuration options enabled in your .config! Please check your configuration.")
ifeq ($(CONFIG_SPI),y)
ifneq ($(CONFIG_DM_SPI)$(CONFIG_OF_CONTROL),yy)
	$(warning "The relevant config item with associated code will remove in v2019.07 release.")
endif
endif
endif
ifneq ($(CONFIG_DM),y)
	@echo >&2 "===================== WARNING ======================"
	@echo >&2 "This board does not use CONFIG_DM. CONFIG_DM will be"
	@echo >&2 "compulsory starting with the v2020.01 release."
	@echo >&2 "Failure to update may result in board removal."
	@echo >&2 "See doc/driver-model/migration.rst for more info."
	@echo >&2 "===================================================="
endif
......
 
 

all目标依赖 INPUTS-y

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

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

# Generate this input file for binman
ifeq ($(CONFIG_SPL),)
INPUTS-$(CONFIG_ARCH_MEDIATEK) += u-boot-mtk.bin
endif

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

ifeq ($(CONFIG_INIT_SP_RELATIVE)$(CONFIG_OF_SEPARATE),yy)
INPUTS-y += init_sp_bss_offset_check
endif

ifeq ($(CONFIG_MPC85xx)$(CONFIG_OF_SEPARATE),yy)
INPUTS-y += u-boot-with-dtb.bin
endif

ifeq ($(CONFIG_ARCH_ROCKCHIP),y)
# On ARM64 this target is produced by binman so we don't need this dep
ifeq ($(CONFIG_ARM64),y)
ifeq ($(CONFIG_SPL),y)
# TODO: Get binman to generate this too
INPUTS-y += u-boot-rockchip.bin
endif
else
ifeq ($(CONFIG_SPL),y)
# Generate these inputs for binman which will create the output files
INPUTS-y += idbloader.img u-boot.img
endif
endif
endif

INPUTS-$(CONFIG_X86) += u-boot-x86-start16.bin u-boot-x86-reset16.bin \
	$(if $(CONFIG_SPL_X86_16BIT_INIT),spl/u-boot-spl.bin) \
	$(if $(CONFIG_TPL_X86_16BIT_INIT),tpl/u-boot-tpl.bin)
 

u-boot.bin目标

MKIMAGEFLAGS_fit-dtb.blob = -f auto -A $(ARCH) -T firmware -C none -O u-boot \
	-a 0 -e 0 -E \
	$(patsubst %,-b arch/$(ARCH)/dts/%.dtb,$(subst ",,$(CONFIG_OF_LIST))) -d /dev/null

ifneq ($(EXT_DTB),)
u-boot-fit-dtb.bin: u-boot-nodtb.bin $(EXT_DTB)
		$(call if_changed,cat)
else
u-boot-fit-dtb.bin: u-boot-nodtb.bin $(FINAL_DTB_CONTAINER)
	$(call if_changed,cat)
endif

u-boot.bin: u-boot-fit-dtb.bin FORCE
	$(call if_changed,copy)

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

else 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
 
 

u-boot-nodtb.bin 目标


u-boot-nodtb.bin: u-boot FORCE
	$(call if_changed,objcopy_uboot)
	$(BOARD_SIZE_CHECK)

u-boot.ldr:	u-boot
		$(CREATE_LDR_ENV)
		$(LDR) -T $(CONFIG_CPU) -c $@ $< $(LDR_FLAGS)
		$(BOARD_SIZE_CHECK)

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

ifeq ($(CONFIG_RISCV),y)
	@tools/prelink-riscv $@ 0
endif
 

u-boot-init和u-boot-main


libs-y += lib/
...

libs-y += $(if $(BOARDDIR),board/$(BOARDDIR)/)

libs-y := $(sort $(libs-y))

u-boot-dirs	:= $(patsubst %/,%,$(filter %/, $(libs-y))) tools examples

u-boot-alldirs	:= $(sort $(u-boot-dirs) $(patsubst %/,%,$(filter %/, $(libs-))))

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

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

​head-y​​没有定义,该变量和CPU架构有关,在相关架构下的子Makefile中定义,比如arch/arm/Makefile中定义如下:
head-y := arch/arm/cpu/$(CPU)/start.o
​​libs-y​​是uboot各子目录中built-in.o的集合
u-boot.lds在各个架构目录下,比如arch/arm/cpu/u-boot.lds
built-in.o文件的生成
以driver/gpio/built-in.o为例,在drivers/gpio/目录下有个名为.built-in.o.cmd的文件,
在这里插入图片描述

build-in.o的规则在 Makefile.build文件里边


# ===========================================================================

ifneq ($(strip $(lib-y) $(lib-m) $(lib-)),)
lib-target := $(obj)/lib.a
endif

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

builtin-target 的依赖 obj-y

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

targets += $(builtin-target)
endif # builtin-target
 

这个规则依赖$(obj-y),obj-y变量实际上是Makefile.build里面根据obj参数包含另外的Makefile带进来的

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

# Added for U-Boot
asflags-y  += $(PLATFORM_CPPFLAGS)
ccflags-y  += $(PLATFORM_CPPFLAGS)
cppflags-y += $(PLATFORM_CPPFLAGS)
 

综上,u-boot目标是以u-boot.lds为链接脚本,将arch/arm/cpu/armv7/start.o和各个子目录下的built-in.o链接在一起生成u-boot​,最后放一张生成下uboot.bin的依赖图:

 
默认目标
依赖
依赖
 
 
 
 
 
 
设备树依赖
依赖
依赖
依赖
依赖
依赖
变量
 
变量
 
 
 
 
 
 
make
_all
all
INPUTS-y
uboot.srec
uboot.bin
uboot.sym
System.map
binary_size_check
u-boot.*
u-bootdtb.bin
u-bootnodtb.bin
u-boot
u-boot-init
u-boot-main
u-boot.lds
head-y
arch/xxx/.../start.o
libs-y
所有子目录下build-in.o集合
arch/xxx/cpu/u-boot.lds
u-boot-onenand.bin
u-boot-with-spl-pbl.bin
u-boot-tpl.bin
u-boot.* .etc