赞
踩
u-boot版本: u-boot-2021.01.tar.bz2
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
$(MAKE)就是调用make命令,-C是指定子目录
像我们平时使用的一些IDE的编译按钮就是将 Makefile 和make集成在一起,来编译整个工程,同样u-boot ,以及以后我们的linux内核,驱动,应用等的编译都是这么个流程。
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 表示禁止使用内置的隐含规则和变量定义;这个选项用于启用递归make,使得Makefile目标可以调用其他Makefile目标,就像调用外部程序一样;
MAKEFLAGS += -rR --include- dir= $ (CURDIR)这个选项在递归make的基础上增加了对当前目录的包含,这意味着Make会查找header文件和其他包含文件时,包括当前Makefile所在的目录。–include-dir指明搜索路径.$(CURDIR)表示当前目录,CURDIR是make的内嵌变量,自动设置为当前目录。
综上所述,这两个选项一起使用可以实现一个使用recursive make并且包括当前目录的构建过程。
4.修改C语言区域设置
在locale环境中,有一组变量,代表国际化环境中的不同设置,"C"是系统默认的locale:
LC_ALL是一个宏,如果该值设置了,则该值会覆盖所有LC_*的设置值。注意,LANG的值不受该宏影响。
LC_COLLATE定义该环境的排序和比较规则
LC_NUMERIC非货币的数字显示格式
5.编译信息的输出模式以及静默输出
当在命令行传入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 目录中。
因为默认的就是原地编译。如果需要指定具体的输出目录编译则有2种方式来指定输出目录。
第一种:make O=输出目录
第二种:export KBUILD_OUTPUT=输出目录 然后再make
如果两个都指定了(既有KBUILD_OUTPUT环境变量存在,又有O=xx),则O=xx具有更高优先级(参考源码注释 124-133行的介绍)
7.代码检查
使用参数C=来使能代码检查:
1:检查需要重新编译的文件
2:检查所有的源码文件
同样,如果参数C来源于命令行,就将C赋值给环境变量 KBUILD_CHECKSRC,如果没有则变量KBUILD_CHECKSRC为0
8.编译外部模块
如果编译外部模块,则对命令行参数变量M进行赋值
操作符比较:
操作符“:=”与操作符 “+=”的功能相同,只是操作符“:=”后面的用来定义变量(KBUILD_EXTMOD)的变量M只能是前面定义好的,
如果操作符“?=”前面的变量KBUILD_EXTMOD没有定义过,那么就将SUBDIRS赋给KBUILD_EXTMOD;
如果定义过,则语句KBUILD_EXTMOD ?= $(SUBDIRS)什么也不做。
‘?=’’ 为条件赋值操作符仅仅在变量还没有定义的情况下有效。
9.获取主机的CPU架构和操作系统
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的内核),则进行相关设置
引入kbuild系统的文件定义(后边会写一篇文章专门针对kbuild 系统做一个梳理)
build变量的定义在scripts/Kbuild.include 中定义:
设置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)
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文件。
上图基本流程是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的依赖图:
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。