Uboot顶层Makefile解析-1. defconfig过程分析

发布时间 2023-11-28 10:51:10作者: fuzidage

1 版本号

image
Top Makefile的开头会有版本描述,VERSION 是主版本号,PATCHLEVEL 是补丁版本号,SUBLEVEL 是次版本号,这三个一 起构成了 uboot 的版本号,比如当前的 uboot 版本号就是“2016.03”。EXTRAVERSION 是附加 版本信息,NAME 是和名字有关的,一般不使用这两个。

2 MAKEFLAGS

image
有两个特殊的变量:“SHELL”和“MAKEFLAGS”,这两个变量除非使用“unexport”声明, 否则的话在整个make的执行过程中,它们的值始终自动的传递给子make。
MAKEFLAGS 追加了一些值,“-rR”表示禁止使用内置的隐 含规则和变量定义,“--include-dir”指明搜索路径,”$(CURDIR)”表示当前目录。
image

3 设置命令输出详细程度

①uboot 默认编译使用短命令。
image
②设置变量“V=1“来实现完整的命令输出。
image
image
③make -s设置成静默输出,将会silent_输出,不打印任何提示信息。如下:
image
MAKE_VERSION就是make版本号,我这里是4.2.1。因此filter 4.%,$(MAKE_VERSION)得到的过滤结果不为空。
image

当使用“make -s”编译的时候,“-s”会作为 MAKEFLAGS 变量的一部分传递给 Makefile。因此$(firstword x$(MAKEFLAGS))得到xrRs,最后quiet=silent_,否则不使用silent_输出。最后使用 export 导出变量 quiet、Q 和 KBUILD_VERBOSE。
image
image

4 设置编译结果输出位置

在 make 的时候使用“O”来指定 输出目录,这么做是为了将源文件 和编译产生的文件分开,当然也可以不指定 O 参数,不指定的话源文件和编译产生的文件都在 同一个目录内。
image
判断“O”是否来自于命令行,如果来自命令行的话,KBUILD_OUTPUT 就为$(O),也就是输出目录。
image
一开始判断KBUILD_OUTPUT 是否为空。 如果指定了输出目录就调用 mkdir 命令创建目录。

5 代码检查

命令“make C=1”使能代码检查,检查那些需要重新编译的文 件。“make C=2”用于检查所有的源码文件。
image

6 子模块编译

使用命令“make M=dir”即可,旧语法“make SUBDIRS=dir”也是支持的。
image
如果定 义 了 SUBDIRS或者M,那么KBUILD_EXTMOD就会被赋值。然后进入目标_all 依赖 modules,要先编译出 modules,也就是编译模块。
否则KBUILD_EXTMOD为空,进入all编译。
判断 KBUILD_SRC 是否为空,如果为空的话就设置变量 srctree 为当前目录,否则srctree就是KBUILD_SRC。一般不设置 KBUILD_SRC。
设置变量 src 和 obj,都为当前目录,设置VPATH,导出量 scrtree、objtree 和 VPATH。

7 7.获取主机架构系统信息

image
最终开发服务器主机架构和操作系统信息如下:
image

8 设置目标架构、工具链和配置文件

HOSTARCH是x86_64,我们编译make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf-”就是用于设置目标 ARCH 和 CROSS_COMPILE。
image
KCONFIG_CONFIG,这里设置配置文件为.config,.config 默认是没有的,需要使用命令“make xxx_defconfig” 对 uboot 进行配置,配置完成以后就会在 uboot 根目录下生成.config。
设置主机编译器HOSTCC,HOSTCXX等。

9 调用 scripts/Kbuild.include

image
Kbuild.include定义了很多变量:
image
里面定了build变量,后面产生配置文件时会用到:
image

10 设置交叉编译器

image

11 核心变量导出

在顶层 Makefile 会导出很多变量:

export VERSION PATCHLEVEL SUBLEVEL UBOOTRELEASE UBOOTVERSION
export ARCH CPU BOARD VENDOR SOC CPUDIR BOARDDIR
export CONFIG_SHELL HOSTCC HOSTCFLAGS HOSTLDFLAGS CROSS_COMPILE AS LD CC
export CPP AR NM LDR STRIP OBJCOPY OBJDUMP
export MAKE AWK PERL PYTHON
export HOSTCXX HOSTCXXFLAGS DTC CHECK CHECKFLAGS

export KBUILD_CPPFLAGS NOSTDINC_FLAGS UBOOTINCLUDE OBJCOPYFLAGS LDFLAGS
export KBUILD_CFLAGS KBUILD_AFLAGS

image
打印出export的这些变量:
image
①这些变量来自config.mk,里面定义了ARCH,CPU,BOARD,VENDOR,SOC,BOARDDIR等变量。变 量 ARCH , 值 为 $(CONFIG_SYS_ARCH:"%"=%) , 也 就 是 提 取 CONFIG_SYS_ARCH 里面双引号“”之间的内容。比如 CONFIG_SYS_ARCH=“arm”的话, ARCH=arm。经过展开确定了CPUDIR=arch/arm/cpu/armv7。
②这里有一个sinclude指令,sinclude 和 include 的功能类似,在 Makefile 中都是读取指定文件内容,这里读取 文件$(srctree)/arch/$(ARCH)/config.mk 的内容。sinclude 读取的文件如果不存在的话不会报错。
③依次包含arch,cpu,soc,vendor,board相关的config.mk
image
.config如下:
image

12 make xxx_defconfig 配置过程

输入命令进行配置:

make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- mx6ull_14x14_ddr512_emmc_defconfig

image

12.1 产生版本日期信息

先产生version_h和timestamp_h。version_h记录uboot版本和编译器版本。timestamp_h记录uboot的日期时间戳信息。打开这两个头文件:
image
image

12.2 配置变量

MAKECMDGOALS 是 make 的一个环境变量,这个变量会保存你所指 定的终极目标列表,比如执行“make mx6ull_alientek_emmc_defconfig”,那么 MAKECMDGOALS 就为 mx6ull_alientek_emmc_defconfig。filter函数将 MAKECMDGOALS 中符合 no-dot-config-targets 的部分过滤出来,可以看到为空,所以dot-config还是等于1.

KBUILD_EXTMOD前面提到没有编译子模块,KBUILD_EXTMOD为空,进入,在判断

ifneq ($(filter config %config,$(MAKECMDGOALS),)

当make mx6ull_alientek_emmc_defconfig, 明显过滤出来不为空,因此config-targets=1.

words $(MAKECMDGOALS)等于1,最后得到:

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

12.3 执行3个依赖

image
KBUILD_DEFCONFIG等于mx6ull_14x14_ddr512_emmc_defconfig, KBUILD_KCONFIG等于空,导出。
匹配到%config, 执行scripts_basic、outputmakefile 和 FORCE 3个依赖.
image

12.3.1 scripts_basic

image

scripts/Kbuild.include定义了build变量。$Q$(MAKE) $(build)=scripts/basic展开build变量后得:

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

scripts_basic 会调用文件./scripts/Makefile.build,打开这个文件:
image

这里$(obj)=scripts/basic, patsubst 是替换函数在“scripts/basic”中查找符合“tpl/%”的部分,然后将“tpl/”取消掉,但是 “scripts/basic”没有“tpl/”,所以 src= scripts/basic。两个ifeq ($(obj),$(src))都满足条件,最终prefix等于.。
image
kbuild-dir 展开后为:

$(if $(filter /%, scripts/basic), scripts/basic, ./scripts/basic)

因为没有以“/”为开头的单词,所以$(filter /%, scripts/basic)的结果为空,kbuilddir=./scripts/basic。
kbuild-file 展开后为:

$(if $(wildcard ./scripts/basic/Kbuild), ./scripts/basic/Kbuild, ./scripts/basic/Makefile)

scrpts/basic 目录中没有 Kbuild 这个文件,所以 kbuild-file= ./scripts/basic/Makefile.
scripts/Makefile.build文件包含/scripts/basic/Makefile

12.3.1.1.1 找到默认目标_build

再继续分析scripts/Makefile.build:
image

__build 是默认目标,因为命令“@make -f ./scripts/Makefile.build obj=scripts/basic”没有指定目标,所以会使用到默认目标:__build。
image

顶层 Makefile 中,KBUILD_BUILTIN 为 1, KBUILD_MODULES 为 0,因此展开后目标__build 为:

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

直接打印出这5个目标:所以最终只有一个依赖scripts/basic/fixdep要执行,产生fixdep.
image

执行打印如下:
image
至此第一个依赖scripts_basic就结束了。
总结:scripts_basic就是利用scripts/Makefile.build去找到_build目标,然后去scripts/basic目录编译出fixdep.

12.3.2 outputmakefile

由于KBUILD_SRC为空,不执行。否则会为源码路径创建source这个符号链接,执行mkmakefile。

12.3.3 FORCE

Makefile最底下定了FORCE目标,可以看到什么都不执行。
image

12.4 产生.config

回到%config 处:
$(Q)$(MAKE) $(build)=scripts/kconfig $@展开后

make -f ./scripts/Makefile.build obj=scripts/kconfig mx6ull_14x14_ddr512_emmc_defconfig

再次进入scripts/Makefile.build, 用(echo)得到一些变量值:

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

打开./scripts/kconfig/Makefile:
image
%_defconfig: $(obj)/conf匹配到我们的mx6ull_14x14_ddr512_emmc_defconfig,先编译依赖scripts/kconfig/conf,编译生成scripts/kconfig/conf如下:
image
最终利用conf工具从configs目录找到mx6ull_14x14_ddr512_emmc_defconfig,将配置写成.config文件。
总结defconfig配置过程:
image