哈尔滨网站建设企业,网络服务费属于什么费用,个人网站开发流程,怎么建投票网站参考#xff1a;U-Boot顶层Makefile介绍 作者#xff1a;一只青木呀 发布时间#xff1a; 2020-10-22 16:22:17 网址#xff1a;https://blog.csdn.net/weixin_45309916/article/details/109218569 目录0、学习目的1、准备好uboot源码2、分析顶层Makefile2.1、版本号2.2、M… 参考U-Boot顶层Makefile介绍 作者一只青木呀 发布时间 2020-10-22 16:22:17 网址https://blog.csdn.net/weixin_45309916/article/details/109218569 目录0、学习目的1、准备好uboot源码2、分析顶层Makefile2.1、版本号2.2、MAKEFLAGS 变量2.3、命令输出参数V的效果2.4、静默输出2.5、设置编译结果输出目录2.6、代码检查2.7、模块编译2.8、获取主机架构和系统2.9 、设置目标架构、交叉编译器和配置文件2.10、调用 scripts/Kbuild.include2.11、交叉编译工具变量设置3.12、导出其他变量2.13、make xxx_defconfig 过程2.14、 Makefile.build 脚本分析(接上一节内容)2.14.1、scripts_basic 目标对应的命令2.14.2、 %config 目标对应的命令总结make xxx_defconfig过程2.15、 make 编译过程总结make命令的流程0、学习目的
分析顶层Makefile主要是为了知道以后要改哪些东西。
Makefile配置好哪些C文件需要编译配置好以后通过make命令编译即可。
1、准备好uboot源码
这里分析的是2016.版本的uboot。
uboot源码下载https://blog.csdn.net/weixin_45309916/article/details/109176510
下载好后打开顶层Makefile。 一共是1600多行接下来就开始分析啦
2、分析顶层Makefile
在阅读 uboot 源码之前肯定是要先看一下顶层 Makefile分析 gcc 版本代码的时候一定是先从顶层 Makefile 开始的然后再是子 Makefile这样通过层层分析 Makefile 即可了解整个工程的组织结构。顶层 Makefile 也就是 uboot 根目录下的 Makefile 文件由于顶层 Makefile 文件内容比较多所以我们将其分开来看。
2.1、版本号
顶层 Makefile 一开始是版本号内容如下(为了方便分析顶层 Makefile 代码段前段行号采用 Makefile 中的行号因为 uboot 会更新因此行号可能会与你所看的顶层 Makefile 有所不同) VERSION 是主版本号 PATCHLEVEL 是补丁版本号 SUBLEVEL 是次版本号这三个一起构成了 uboot 的版本号比如当前的 uboot 版本号就是“2016.03”。 EXTRAVERSION 是附加版本信息 NAME 是和名字有关的一般不使用这两个。
2.2、MAKEFLAGS 变量
make 是支持递归调用的也就是在 Makefile 中使用“make”命令来执行其他的Makefile文件一般都是子目录中的 Makefile 文件。假如在当前目录下存在一个“subdir”子目录这个子目录中又有其对应的 Makefile 文件那么这个工程在编译的时候其主目录中的 Makefile 就可以调用子目录中的 Makefile以此来完成所有子目录的编译。主目录的 Makefile 可以使用如下代码来编译这个子目录
$(MAKE) -C subdir$(MAKE)就是调用“make”命令 -C 指定子目录。有时候我们需要向子 make 传递变量这个时候使用“export”来导出要传递给子 make 的变量即可如果不希望哪个变量传递给子make 的话就使用“unexport”来声明不导出:
export VARIABLE …… //导出变量给子 make 。
unexport VARIABLE…… //不导出变量给子 make。有两个特殊的变量“SHELL”和“MAKEFLAGS”这两个变量除非使用“unexport”声明否则的话在整个make的执行过程中它们的值始终自动的传递给子make。在uboot的主Makefile中有如下代码
MAKEFLAGS -rR --include-dir$(CURDIR)上述代码使用“”来给变量 MAKEFLAGS 追加了一些值“-rR”表示禁止使用内置的隐含规则和变量定义“–include-dir”指明搜索路径 ”$(CURDIR)”表示当前目录。
2.3、命令输出参数V的效果
uboot 默认编译就是输入编译命令make -j12是不会在终端中显示完整的命令都是短命令如下图所示 在终端中输出短命令虽然看起来很清爽但是不利于分析 uboot 的编译过程。可以通过设置变量“V1“来实现完整的命令输出就是输入编译命令make V1 -j12这个在调试 uboot 的时候很有用结果如下图所示 顶层 Makefile 中控制命令输出的代码如下 上述代码中先使用 ifeq 来判断$(origin V)和command line是否相等。这里用到了 Makefile中的函数 origin origin 和其他的函数不一样它不操作变量的值 origin 用于告诉你变量是哪来的语法为
$(origin variable)variable 是变量名 origin 函数的返回值就是变量来源因此( o r i g i n V ) 就 是 变 量 V 的 来 源 。 如 果 变 量 V 是 在 命 令 行 定 义 的 那 么 它 的 来 源 就 是 c o m m a n d l i n e 这 样 (origin V)就是变量 V 的来源。如果变量 V 是在命令行定义的那么它的来源就是command line这样(originV)就是变量V的来源。如果变量V是在命令行定义的那么它的来源就是commandline这样(origin V)和commandline就相等了。当这两个相等的时候变量 KBUILD_VERBOSE 就等于 V 的值比如在命令行中输 入 “ V1 “ 的 话 那 么 KBUILD_VERBOSE1 。 如 果 没 有 在 命 令 行 输 入 V 的 话KBUILD_VERBOSE0。
第 80 行判断 KBUILD_VERBOSE 是否为 1如果 KBUILD_VERBOSE 为 1 的话变量 quiet和 Q 都为空如果 KBUILD_VERBOSE0 的话变量 quiet 为“quiet_“变量 Q 为“” ,综上所述: V1 的话:
KBUILD_VERBOSE1
quiet 空 。
Q 空。V0 或者命令行不定义 V 的话
KBUILD_VERBOSE0
quiet quiet_。
Q 。Makefile 中会用到变量 quiet 和 Q 来控制编译的时候是否在终端输出完整的命令在顶层Makefile 中有很多如下所示的命令
$(Q)$(MAKE) $(build)tools如果 V0 的话上述命令展开就是“ make $(build)tools” make 在执行的时候默认会在终端输出命令但是在命令前面加上“”就不会在终端输出命令了shell脚本语法。当 V1 的时候 Q 就为空上述命令就是“make $(build)tools”因此在 make 执行的过程命令会被完整的输出在终端上。
有些命令会有两个版本比如
quiet_cmd_sym ? SYM $
cmd_sym ? $(OBJDUMP) -t $ $sym 命令分为“quiet_cmd_sym”和“cmd_sym”两个版本这两个命令的功能都是一样的区别在于 make 执行的时候输出的命令不同。 quiet_cmd_xxx 命令输出信息少也就是短命令而 cmd_xxx 命令输出信息多也就是完整的命令。
如果变量 quiet 为空的话整个命令都会输出。 如果变量 quiet 为“quiet_”的话仅输出短版本。 如果变量 quiet 为“silent_”的话整个命令都不会输出。
2.4、静默输出
设置 V0 或者在命令行中不定义 V 的话编译 uboot 的时候终端中显示的短命令但是还是会有命令输出有时候我们在编译 uboot 的时候不需要输出任何命令这个时候就可以使用 uboot 的静默输出功能。编译的时候使用“make -s”即可实现静默输出顶层 Makefile中相应的代码如下
2.5、设置编译结果输出目录
uboot 可以将编译出来的目标文件输出到单独的目录中在 make 的时候使用“O”来指定输出目录比如“make Oout”就是设置目标文件输出到 out 目录中。这么做是为了将源文件和编译产生的文件分开当然也可以不指定 O 参数不指定的话源文件和编译产生的文件都在同一个目录内一般我们不指定 O 参数。顶层 Makefile 中相关的代码如下
# kbuild supports saving output files in a separate directory.
# To locate output files in a separate directory two syntaxes are supported.
# In both cases the working directory must be the root of the kernel src.
# 1) O
# Use make Odir/to/store/output/files/
#
# 2) Set KBUILD_OUTPUT
# Set the environment variable KBUILD_OUTPUT to point to the directory
# where the output files shall be placed.
# export KBUILD_OUTPUTdir/to/store/output/files/
# make
#
# The O assignment takes precedence over the KBUILD_OUTPUT environment
# variable.# KBUILD_SRC is set on invocation of make in OBJ directory
# KBUILD_SRC is not intended to be used by the regular user (for now)
ifeq ($(KBUILD_SRC),)# OK, Make called in directory where kernel src resides
# Do we want to locate output files in a separate directory?
ifeq ($(origin O), command line) KBUILD_OUTPUT : $(O)
endif# Thats our default target when none is given on the command line
PHONY : _all
_all:# Cancel implicit rules on top Makefile
$(CURDIR)/Makefile Makefile: ;ifneq ($(KBUILD_OUTPUT),)
# Invoke a second make in the output directory, passing relevant variables
# check that the output directory actually exists
saved-output : $(KBUILD_OUTPUT)
KBUILD_OUTPUT : $(shell mkdir -p $(KBUILD_OUTPUT) cd $(KBUILD_OUTPUT) \ /bin/pwd)
$(if $(KBUILD_OUTPUT),, \$(error failed to create output directory $(saved-output)))PHONY $(MAKECMDGOALS) sub-make$(filter-out _all sub-make $(CURDIR)/Makefile, $(MAKECMDGOALS)) _all: sub-make:sub-make: FORCE$(Q)$(MAKE) -C $(KBUILD_OUTPUT) KBUILD_SRC$(CURDIR) \-f $(CURDIR)/Makefile $(filter-out _all sub-make,$(MAKECMDGOALS))# Leave processing to above invocation of make
skip-makefile : 1
endif # ifneq ($(KBUILD_OUTPUT),)
endif # ifeq ($(KBUILD_SRC),)ifeq ($(origin O), command line) 判断“O”是否来自于命令行如果来自命令行的话条件成立 KBUILD_OUTPUT就为$(O)因此变量 KBUILD_OUTPUT 就是输出目录。
ifneq ($(KBUILD_OUTPUT),)判断 KBUILD_OUTPUT 是否为空。
KBUILD_OUTPUT : $(shell mkdir -p $(KBUILD_OUTPUT) cd $(KBUILD_OUTPUT) \ /bin/pwd)调用 mkdir 命令创建 KBUILD_OUTPUT 目录并且将创建成功以后的绝对路径赋值给 KBUILD_OUTPUT。至此通过 O 指定的输出目录就存在了。
2.6、代码检查
uboot 支持代码检查使用命令“make C1”使能代码检查检查那些需要重新编译的文件。“make C2”用于检查所有的源码文件顶层 Makefile 中的代码如下 第 176 行判断 C 是否来源于命令行如果 C 来源于命令行那就将 C 赋值给变量KBUILD_CHECKSRC如果命令行没有 C 的话 KBUILD_CHECKSRC 就为 0。
2.7、模块编译
在 uboot 中允许单独编译某个模块使用命令“ make Mdir”即可旧语法“ makeSUBDIRSdir”也是支持的。顶层 Makefile 中的代码如下 第 186 行 判 断 是 否 定 义 了 SUBDIRS 如 果 定 义 了 SUBDIRS 变 量KBUILD_EXTMODSUBDIRS这里是为了支持老语法“make SUBIDRSdir”
第 190 行判断是否在命令行定义了 M如果定义了的话 KBUILD_EXTMOD$(M)。
第 197 行判断 KBUILD_EXTMOD 时为空如果为空的话目标_all 依赖 all因此要先编译出 all。否则的话默认目标_all 依赖 modules要先编译出 modules也就是编译模块。一般情况下我们不会在 uboot 中编译模块所以此处会编译 all 这个目标。
第 203 行判断 KBUILD_SRC 是否为空如果为空的话就设置变量 srctree 为当前目录即srctree 为“.”一般不设置 KBUILD_SRC。
第 214 行设置变量 objtree 为当前目录。
第 215 和 216 行分别设置变量 src 和 obj都为当前目录。
第 218 行设置 VPATH。
第 220 行导出变量 scrtree、 objtree 和 VPATH。
2.8、获取主机架构和系统
接下来顶层 Makefile 会获取主机架构和系统也就是我们电脑的架构和系统代码如下 第 227 行定义了一个变量 HOSTARCH用于保存主机架构这里调用 shell 命令“uname -m”获取架构名称结果如下图所示 从上图可以看出当前电脑主机架构为“x86_64” shell 中的“|”表示管道意思是将左边的输出作为右边的输入 sed -e 是替换命令“sed -e s/i.86/x86/”表示将管道输入的字符串中的“i.86”替换为“x86”其他的“sed -s”命令同理。对于我的电脑而言 HOSTARCHx86_64。第 237 行定义了变量 HOSTOS此变量用于保存主机 OS 的值先使用 shell 命令“name -s”来获取主机 OS结果如下图所示 从上图可以看出此时的主机 OS 为“Linux”使用管道将“Linux”作为后面“tr ‘[:upper:]’’[:lower:]’”的输入“tr ‘[:upper:]’ ‘[:lower:]’”表示将所有的大写字母替换为小写字母因此得到“linux”。最后同样使用管道将“linux”作为“sed -e ‘s/(cygwin).*/cygwin/’”的输入用于将cygwin.*替换为 cygwin。因此 HOSTOSlinux。
第 240 行导出 HOSTARCHx86_64 HOSTOSlinux。
2.9 、设置目标架构、交叉编译器和配置文件
编 译 uboot 的 时 候 需 要 设 置 目 标 板 架 构 和 交 叉 编 译 器 “ make ARCHarmCROSS_COMPILEarm-linux-gnueabihf-”就是用于设置 ARCH 和 CROSS_COMPILE在顶层Makefile 中代码如下 第 245 行判断 HOSTARCH 和 ARCH 这两个变量是否相等主机架构(变量 HOSTARCH)是x86_64而我们编译的是 ARM 版本 uboot肯定不相等所以 CROS_COMPILE arm-linuxgnueabihf-。
第 249 行定义变量 KCONFIG_CONFIG uboot 是可以配置的这里设置配置文件为.config前面树莓派学习过 .config 默认是没有的需要使用命令“make xxx_defconfig”对 uboot 进行配置配置完成以后就会在 uboot 根目录下生成.config。默认情况下.config 和xxx_defconfig 内容是一样的因为.config 就是从 xxx_defconfig 复制过来的。如果后续自行调整了 uboot 的一些配置参数那么这些新的配置参数就添加到了.config 中而不是 xxx_defconfig。相当于 xxx_defconfig 只是一些初始配置而.config 里面的才是实时有效的配置。
2.10、调用 scripts/Kbuild.include
主 Makefile 会调用文件 scripts/Kbuild.include这个文件顶层 Makefile 中代码如下 scripts/Kbuild.include文件部分内容 在 uboot 的编译过程中会用到 scripts/Kbuild.include 中的这些变量
2.11、交叉编译工具变量设置
上面我们只是设置了 CROSS_COMPILE 的名字但是交叉编译器其他的工具还没有设置顶层 Makefile 中相关代码如下
3.12、导出其他变量
接下来在顶层 Makefile 会导出很多变量代码如下 这些变量中大部分都已经在前面定义了我们重点来看一下下面这几个变量
ARCH CPU BOARD VENDOR SOC CPUDIR BOARDDIR修改好顶层 Makefile 以后命令行输入以下命令
make mytest结果如图下所示 从上图可以看到这 7 个变量的值这 7 个变量是从哪里来的呢在 uboot 根目录下有个文件叫做 config.mk这 7 个变量就是在 config.mk在uboot源码根目录下 里面定义的打开 config.mk 内容如下 第 25 行 定 义 变 量 ARCH架构 值 为 $(CONFIG_SYS_ARCH:%%) 也 就 是 提 取CONFIG_SYS_ARCH 里面双引号“”之间的内容。比如 CONFIG_SYS_ARCH“arm”的话ARCHarm。 第 26 行定义变量 CPU值为$(CONFIG_SYS_CPU:%%)。 第 32 行定义变量 BOARD板子值为(CONFIG_SYS_BOARD:%%)。 第 34 行定义变量 VENDOR值为$(CONFIG_SYS_VENDOR:%%)。 第 37 行定义变量 SOC值为$(CONFIG_SYS_SOC:%%)。 第 44 行定义变量 CPUDIRCPU目录值为 arch/( A R C H ) / c p u (ARCH)/cpu(ARCH)/cpu(if ( C P U ) , / (CPU),/(CPU),/(CPU),)。 第 46 行 sinclude 和 include 的功能类似在 Makefile 中都是读取指定文件内容这里读取文件( s r c t r e e ) / a r c h / (srctree)/arch/(srctree)/arch/(ARCH)/config.mk 的内容。 sinclude 读取的文件如果不存在的话不会报错。 第 47 行读取文件( s r c t r e e ) / (srctree)/(srctree)/(CPUDIR)/config.mk 的内容。 第 50 行读取文件( s r c t r e e ) / (srctree)/(srctree)/(CPUDIR)/$(SOC)/config.mk 的内容。 第 54 行 定 义 变 量 BOARDDIR 如 果 定 义 了 VENDOR 那 么BOARDDIR( V E N D O R ) / (VENDOR)/(VENDOR)/(BOARD)否则的 BOARDDIR$(BOARD)。 第 60 行读取文件( s r c t r e e ) / b o a r d / (srctree)/board/(srctree)/board/(BOARDDIR)/config.mk
接下来需要找到 CONFIG_SYS_ARCH、 CONFIG_SYS_CPU、 CONFIG_SYS_BOARD、CONFIG_SYS_VENDOR 和 CONFIG_SYS_SOC 这 5 个变量的值。这 5 个变量在 uboot 根目录下的.config 文件中有定义定义如下 根据示例代码可知
ARCH arm
CPU armv7
BOARD mx6ullevk
VENDOR freescale
SOC mx6
CPUDIR arch/arm/cpu/armv7
BOARDDIR freescale/mx6ullevk在 config.mk 中读取的文件有
arch/arm/config.mk
arch/arm/cpu/armv7/config.mk
arch/arm/cpu/armv7/mx6/config.mk (此文件不存在)
board/ freescale/mx6ullevk/config.mk (此文件不存在)2.13、make xxx_defconfig 过程
在编译 uboot 之前要使用“make xxx_defconfig”命令来配置 uboot那么这个配置过程是如何运行的呢在顶层 Makefile 中有如下代码
xxx_defconfig 对应代码第476行下面有详解 第 422 行定义了变量 version_h这变量保存版本号文件此文件是自动生成的。文件include/generated/version_autogenerated.h 内容如下图所示 第 423 行定义了变量 timestamp_h此变量保存时间戳文件此文件也是自动生成的。文件include/generated/timestamp_autogenerated.h 内容如下图所示 第 425 行定义了变量 no-dot-config-targets。 第 429 行定义了变量 config-targets初始值为 0。 第 430 行定义了变量 mixed-targets初始值为 0 第 431 行定义了变量 dot-config初始值为 1。 第 433 行将 MAKECMDGOALS 中不符合 no-dot-config-targets 的部分过滤掉剩下的如果不为空的话条件就成立。 MAKECMDGOALS 是 make 的一个环境变量这个变量会保存你所指定的终极目标列表比如执行“make mx6ull_alientek_emmc_defconfig”那么 MAKECMDGOALS就为 mx6ull_alientek_emmc_defconfig。很明显过滤后为空所以条件不成立变量 dot-config 依旧为 1。 第439行判断KBUILD_EXTMOD是否为空如果KBUILD_EXTMOD为空的话条件成立经过前面的分析我们知道 KBUILD_EXTMOD 为空所以条件成立。 第 440 行将 MAKECMDGOALS 中不符合“config”和“%config”的部分过滤掉如果剩下的部分不为空条件就成立很明显此处条件成立变量 config-targets1。 第 442 行统计 MAKECMDGOALS 中的单词个数如果不为 1 的话条件成立。此处调用Makefile 中的 words 函数来统计单词个数 words 函数格式如下
$(words text)很明显 MAKECMDGOALS 的单词个数是 1 个所以条件不成立 mixed-targets 继续为0。综上所述这些变量值如下
config-targets 1
mixed-targets 0
dot-config 1第 448 行如果变量 mixed-targets 为 1 的话条件成立很明显条件不成立。 第 465 行如果变量 config-targets 为 1 的话条件成立很明显条件成立执行这个分支。 第 473 行没有目标与之匹配所以不执行。 第 476 行有目标与之匹配当输入“make xxx_defconfig”的时候就会匹配到%config 目标目标“%config”依赖于 scripts_basic、 outputmakefile 和 FORCE。 FORCE 在顶层 Makefile的 1610 行有如下定义 可以看出 FORCE 是没有规则和依赖的所以每次都会重新生成 FORCE。当 FORCE 作为其他目标的依赖时由于 FORCE 总是被更新过的因此依赖所在的规则总是会执行的。 依赖 scripts_basic 和 outputmakefile 在顶层 Makefile 中的内容如下 第 408 行判断 KBUILD_SRC 是否为空只有变量 KBUILD_SRC 不为空的时候outputmakefile 才有意义经过下面的代码分析 KBUILD_SRC 为空所以 outputmakefile 无效。只有 scripts_basic 是有效的。 第 396~398 行是 scripts_basic 的规则其对应的命令用到了变量 Q、 MAKE 和 build其中
Q或为空 //Q代表要不要显示完整的命令行无关紧要
MAKEmake变量 build 是在 scripts/Kbuild.include 文件中有定义定义如下 从代码可以看出 build-f $(srctree)/scripts/Makefile.build obj经过前面的分析可知变量 srctree 为”.”因此
build-f ./scripts/Makefile.build objscripts_basic 展开以后如下
scripts_basic:
make -f ./scripts/Makefile.build objscripts/basic //也可以没有视配置而定
rm -f . tmp_quiet_recordmcount //也可以没有scripts_basic 会调用文件./scripts/Makefile.build
接着回到示例代码中的%config 处内容如下
%config: scripts_basic outputmakefile FORCE
$(Q)$(MAKE) $(build)scripts/kconfig $将命令展开就是
make -f ./scripts/Makefile.build objscripts/kconfig xxx_defconfig同样也跟文件./scripts/Makefile.build 有关,使用如下命令配置 uboot并观察其配置过程
make mx6ull_14x14_ddr512_emmc_defconfig V1配置过程如下图所示 从上图可以看出我们的分析是正确的接下来就要结合下面两行命令重点分析一下文件 scripts/Makefile.build。 ①、 scripts_basic 目标对应的命令
make -f ./scripts/Makefile.build objscripts/basic②、 %config 目标对应的命令
make -f ./scripts/Makefile.build objscripts/kconfig xxx_defconfig2.14、 Makefile.build 脚本分析(接上一节内容)
从上一小节可知“ make xxx_defconfig“配置 uboot 的时候如下两行命令会执行脚本scripts/Makefile.build
make -f ./scripts/Makefile.build objscripts/basic
make -f ./scripts/Makefile.build objscripts/kconfig xxx_defconfig依次来分析一下
2.14.1、scripts_basic 目标对应的命令
scripts_basic 目标对应的命令为 make -f ./scripts/Makefile.build objscripts/basic。打开文件 scripts/Makefile.build有如下代码 第 9 行定义了变量 prefix 值为 tpl。
第 10 行定义了变量 src这里用到了函数 patsubst此行代码展开后为
$(patsubst tpl/%,%, scripts/basic)patsubst 是替换函数格式如下
$(patsubst pattern,replacement,text)此函数用于在 text 中查找符合 pattern 的部分如果匹配的话就用 replacement 替换掉。pattenr 是可以包含通配符“%”如果 replacement 中也包含通配符“%”那么 replacement 中的这个“%”将是 pattern 中的那个“%”所代表的字符串。函数的返回值为替换后的字符串。因此第 10 行就是在“scripts/basic”中查找符合“tpl/%”的部分然后将“tpl/”取消掉但是“scripts/basic”没有“tpl/”所以 src scripts/basic。
第 11 行判断变量 obj 和 src 是否相等相等的话条件成立很明显此处条件成立。
第 12 行和第 9 行一样只是这里处理的是“spl”“scripts/basic”里面也没有“spl/”所以src 继续为 scripts/basic。
第 15 行因为变量 obj 和 src 相等所以 prefix.。
继续分析 scripts/Makefile.build有如下代码 将 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。最 后将 59 行展开即
include ./scripts/basic/Makefile也就是读取 scripts/basic 下面的 Makefile 文件。
继续分析 scripts/Makefile.build如下代码 __build 是默认目标因为命令“make -f ./scripts/Makefile.build objscripts/basic”没有指定目标所以会使用到默认目标 __build。在顶层 Makefile 中 KBUILD_BUILTIN 为 1KBUILD_MODULES 为 0因此展开后目标__build 为
__build:$(builtin-target) $(lib-target) $(extra-y)) $(subdir-ym) $(always)
:可以看出目标__build 有 5 个依赖 builtin-target、 lib-target、 extra-y、 subdir-ym 和 always。这 5 个依赖的具体内容我们就不通过源码来分析了直接在 scripts/Makefile.build 中输入图所示内容将这 5 个变量的值打印出来 执行如下命令
make mx6ull_14x14_ddr512_emmc_defconfig V1结果如下图所示 从上图可以看出只有 always 有效因此__build 最终为
__build: scripts/basic/fixdep
:__build 依赖于 scripts/basic/fixdep所以要先 scripts/basic/fixdep.c 编译生成 fixdep前面已经读取了 scripts/basic/Makefile 文件。
综上所述 scripts_basic 目标的作用就是编译出 scripts/basic/fixdep 这个软件具体作用未知。
2.14.2、 %config 目标对应的命令
%config 目 标 对 应 的 命 令 为 make -f ./scripts/Makefile.build objscripts/kconfigxxx_defconfig各个变量值如下
src scripts/kconfig
kbuild-dir ./scripts/kconfig
kbuild-file ./scripts/kconfig/Makefile
include ./scripts/kconfig/Makefile可以看出 Makefilke.build 会读取 scripts/kconfig/Makefile 中的内容此文件有如下所示内容 目标%_defconfig 刚好和我们输入的 xxx_defconfig 匹配所以会执行这条规则。依赖为$(obj)/conf展开后就是 scripts/kconfig/conf。接下来就是检查并生成依赖 scripts/kconfig/conf。conf 是主机软件到这里我们就打住不要纠结 conf 是怎么编译出来的否则就越陷越深太绕了像 conf 这种主机所使用的工具类软件我们一般不关心它是如何编译产生的。如果一定要看是 conf 是怎么生成的可以输入如下命令重新配置 uboot在重新配置 uboot 的过程中就会输出 conf 编译信息。
make distclean
make ARCHarm CROSS_COMPILEarm-linux-gnueabihf- mx6ull_14x14_ddr512_emmc_
defconfig V1得到 scripts/kconfig/conf 以后就要执行目标%_defconfig 的命令
$(Q)$ $(silent) --defconfigarch/$(SRCARCH)/configs/$ $(Kconfig)相关的变量值如下
silent-s 或为空
SRCARCH..
KconfigKconfig将其展开就是 scripts/kconfig/conf --defconfigarch/../configs/xxx_defconfig Kconfig上述命令用到了 xxx_defconfig 文件比如 mx6ull_alientek_emmc_defconfig。这里会将mx6ull_alientek_emmc_defconfig 中的配置输出到.config 文件中最终生成 uboot 根目录下的.config 文件。
总结make xxx_defconfig过程 至此make xxx_defconfig 就分析完了接下来就要分析一下 u-boot.bin 是怎么生成的了。
2.15、 make 编译过程
配置好 uboot 以后就可以直接 make 编译了也就是前面Makefile配置好哪些C文件需要编译因为没有指明目标所以会使用默认目标主Makefile 中的默认目标如下
目标_all 又依赖于 all如下所示 如 果 KBUILD_EXTMOD 为 空 的 话 _all 依 赖 于 all 。 这 里 不 编 译 模 块 所 以KBUILD_EXTMOD 肯定为空 _all 的依赖就是 all。在主 Makefile 中 all 目标规则如下 从 802 行可以看出 all 目标依赖$(ALL-y)而在顶层 Makefile 中 ALL-y 如下 从示例代码代码可以看出 ALL-y 包含 u-boot.srec、 u-boot.bin、 u-boot.sym、System.map、 u-boot.cfg 和 binary_size_check 这几个文件。根据 uboot 的配置情况也可能包含其他的文件比如
ALL-$(CONFIG_ONENAND_U_BOOT) u-boot-onenand.binCONFIG_ONENAND_U_BOOT 就是 uboot 中跟 ONENAND 配置有关的如果我们使能了ONENAND那么在.config 配置文件中就会有“CONFIG_ONENAND_U_BOOTy”这一句。相当于 CONFIG_ONENAND_U_BOOT 是个变量这个变量的值为“y”所以展开以后就是
ALL-y u-boot-onenand.bin这个就是.config 里面的配置参数的含义这些参数其实都是变量后面跟着变量值会在顶层 Makefile 或者其他 Makefile 中调用这些变量。
ALL-y 里面有个 u-boot.bin这个就是我们最终需要的 uboot 二进制可执行文件所作的所有工作就是为了它。在顶层 Makefile 中找到 u-boot.bin 目标对应的规则如下所示 第 825 行判断 CONFIG_OF_SEPARATE 是否等于 y如果相等那条件就成立在.config中搜索“CONFIG_OF_SEPARAT”没有找到说明条件不成立。
第 832 行就是目标 u-boot.bin 的规则目标 u-boot.bin 依赖于 u-boot-nodtb.bin命令为$(callif_changed,copy) 这 里 调 用 了 if_changed if_changed 是 一 个 函 数 这 个 函 数 在scripts/Kbuild.include 中有定义而顶层 Makefile 中会包含 scripts/Kbuild.include 文件这个前面已经说过了。
if_changed 在 Kbuild.include 中的定义如下 第 227 行为 if_changed 的描述根据描述在一些先决条件比目标新的时候或者命令行有改变的时候 if_changed 就会执行一些命令。
第 257 行就是函数 if_changed if_changed 函数引用的变量比较多也比较绕我们只需要知道它可以从 u-boot-nodtb.bin 生成 u-boot.bin 就行了。
既然 u-boot.bin 依赖于 u-boot-nodtb.bin那么肯定要先生成 u-boot-nodtb.bin 文件顶层Makefile 中相关代码如下 目标 u-boot-nodtb.bin 又依赖于 u-boot顶层 Makefile 中 u-boot 相关规则如下: 目标 u-boot 依赖于 u-boot_init、 u-boot-main 和 u-boot.lds u-boot_init 和 u-boot-main 是两个变量在顶层 Makefile 中有定义值如下 $ (head-y)跟 CPU 架构有关我们使用的是 ARM 芯片所以 head-y 在 arch/arm/Makefile 中被指定为
head-y : arch/arm/cpu/$(CPU)/start.o根据分析我们知道 CPUarmv7因此 head-y 展开以后就是
head-y : arch/arm/cpu/armv7/start.o因此
u-boot-init arch/arm/cpu/armv7/start.o$(libs-y)在顶层 Makefile 中被定义为 uboot 所有子目录下 build-in.o 的集合代码如下 从上面的代码可以看出 libs-y 都是 uboot 各子目录的集合最后
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 文件的集合。那么 uboot-main 就等于所有子目录中 built-in.o 的集合。
这个规则就相当于将以 u-boot.lds 为链接脚本将 arch/arm/cpu/armv7/start.o 和各个子目录下的 built-in.o 链接在一起生成 u-boot。 u-boot.lds 的规则如下
u-boot.lds: $(LDSCRIPT) prepare FORCE
$(call if_changed_dep,cpp_lds)接下来的重点就是各子目录下的 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 驱动文件。这里用到了 ld 的“-r”参数参数含义如下
-r –relocateable: 产生可重定向的输出比如产生一个输出文件它可再次作为‘ld’ 的输入这经常被叫做“部分链接”当我们需要将几个小的.o 文件链接成为一个.o 文件的时候需要使用此选项。
最终将各个子目录中的 built-in.o 文件链接在一起就形成了 u-boot使用如下命令编译 uboot就可以看到链接的过程
make ARCHarm CROSS_COMPILEarm-linux-gnueabihf- mx6ull_14x14_ddr512_emmc_
defconfig V1
make ARCHarm CROSS_COMPILEarm-linux-gnueabihf- V1编译的时候会有如下图所示内容输出 grep命令搜索ox87800000 将其整理一下内容如下
arm-linux-gnueabihf-ld.bfd -pie --gc-sections -Bstatic -Ttext 0x87800000 \
-o u-boot -T u-boot.lds \
arch/arm/cpu/armv7/start.o \
--start-group arch/arm/cpu/built-in.o \
arch/arm/cpu/armv7/built-in.o \
arch/arm/imx-common/built-in.o \
arch/arm/lib/built-in.o \
board/freescale/common/built-in.o \
board/freescale/mx6ull_alientek_emmc/built-in.o \
cmd/built-in.o \
common/built-in.o \
disk/built-in.o \
drivers/built-in.o \
drivers/dma/built-in.o \
drivers/gpio/built-in.o \
……
drivers/spi/built-in.o \
drivers/usb/dwc3/built-in.o \
drivers/usb/emul/built-in.o \
drivers/usb/eth/built-in.o \
drivers/usb/gadget/built-in.o \
drivers/usb/gadget/udc/built-in.o \
drivers/usb/host/built-in.o \
drivers/usb/musb-new/built-in.o \
drivers/usb/musb/built-in.o \
drivers/usb/phy/built-in.o \
drivers/usb/ulpi/built-in.o \
fs/built-in.o \
lib/built-in.o \
net/built-in.o \
test/built-in.o \
test/dm/built-in.o \
--end-group arch/arm/lib/eabi_compat.o \
-L /usr/local/arm/gcc-linaro-4.9.4-2017.01-x86_64_arm-linux-gnueabihf/bin/../lib/gcc/arm-linuxgnueabihf/4.9.4 -lgcc -Map u-boot.map可以看出最终是用 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命令的流程 make xxx_defconfig 用于配置 uboot这个命令最主要的目的就是生成.config 文件。
make用于编译 uboot这个命令的主要工作就是生成二进制的 u-boot.bin 文件和其他的一些与 uboot 有关的文件比如 u-boot.imx 等等