1 概述
本文基于u-boot树莓派3代配置过程进行分析,环境如下:
编译环境:Ubuntu 14.04 LTS
编译工具:arm-Linux-gnueabi-gcc
代码版本:u-boot v2016.09
配置文件:rpi_3_32b_defconfig
u-boot的编译跟kernel编译一样,分两步执行:
- 第一步:配置,执行make xxx_defconfig
进行各项配置,生成.config
文件
- 第二步:编译,执行make进行编译,生成可执行的二进制文件u-boot.bin或u-boot.elf
u-boot自v2014.10
版本开始引入KBuild系统,Makefile的管理和组织跟以前版本的代码有了很大的不同,其Makefile更加复杂。整个Makefile中,嵌套了很多其它不同用途的Makefile,各种目标和依赖也很多,make分析很容易陷进去,让人摸不着头脑。
本文介绍第一步—配置,目的是为了得到.confg文件,供第二步使用。
本文涉及的配置命令:
- 1
- 1
先从简单的make defconfig
配置过程着手吧。
命令行输入:
- 1
- 1
配置命令参数说明:
- rpi_3_32b_defconfig
是树莓派3代32位编译的配置文件
- V=1
指示编译显示详细的输出。默认V=0
,编译仅显示必要的简略信息
编译输出如下:
从输出的log看,make rpi_3_32b_defconfig
的执行主要分为3个部分,见图上的标示:
- 1. 执行make -f ./scripts/Makefile.build obj=scripts/basic
,编译生成scripts/basic/fixdep
工具
- 2. 执行make -f ./scripts/Makefile.build obj=scripts/kconfig rpi_3_32b_defconfig
编译生成scripts/kconfig/conf
工具
- 3. 执行scripts/kconfig/conf --defconfig=arch/../configs/rpi_3_32b_defconfig Kconfig
生成最终的.config
配置文件
跟原始的代码相比,执行make rpi_3_32b_defconfig
后文件夹内容的变化如下:
被后续编译用到的文件是.config。
2 详细配置流程分析
言归正传,整个配置流程的目的就是为了生成.config
文件,下面详细分析.config
文件是如何一步一步生成的。
Makefile的核心是依赖和命令。对于每个目标,首先会检查依赖,如果依赖存在,则执行命令更新目标;如果依赖不存在,则会以依赖为目标,先生成依赖,待依赖生成后,再执行命令生成目标。
2.1 顶层make defconfig规则
执行make xxx_defconfig
命令时,u-boot根目录下的Makefile中有唯一的规则匹配目标:
- 1
- 2
- 3
- 1
- 2
- 3
对于目标,rpi_3_32b_defconfig
,展开则有:
- 1
- 2
- 1
- 2
其中$(build)
在kbuild.include
中定义:
- 1
- 1
2.1.1 依赖
1、依赖scripts_basic
依赖scripts_basic:
- 1
- 2
- 3
- 4
- 5
- 6
- 1
- 2
- 3
- 4
- 5
- 6
可见scripts_basic没有进一步的依赖,展开后规则如下:
- 1
- 2
- 3
- 1
- 2
- 3
2、依赖outputmakefile
依赖outputmakefile:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
outputmakefile也没有进一步的依赖。
如果执行如下命令:
- 1
- 1
那么所有生成的目标都将放到out目录,此时会通过outputmakefile导出一个makefile到out目录进行编译。
由于在当前目录下编译,$(KBUILD_SRC)
为空,不需要导出makefile文件,outputmakefile为空目标。
3、依赖FORCE
依赖FORCE:
- 1
- 2
- 3
- 1
- 2
- 3
FORCE
被定义为一个空目标。
如果一个目标添加FORCE
依赖,每次编译都会去先去执行FORCE
(实际上什么都不做),然后运行命令更新目标,这样就能确保目标每次都会被更新。在这里也就保证目标rpi_3_32b_defconfig
的命令:
- 1
- 1
总是能够被执行。
以上是rpi_3_32b_defconfig
的所有依赖,分析完依赖后再分析命令。
2.1.2 顶层make defconfig的命令
1、依赖scripts_basic的命令
目标rpi_3_32b_defconfig
的三个依赖scripts_basic
,outputmakefile
和FORCE
中,只有scripts_basic
需要执行命令,展开如下
- 1
- 2
- 3
- 1
- 2
- 3
然后Make命令会转到文件scripts/Makefile.build
去执行。
第一次调用scripts/Makefile.build进行编译
文件script/Makefile.build
的开头会根据传入的obj=scripts/basic
参数设置src=scripts/basic
:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
然后搜寻$(srctree)/$(src)
子目录下的makefile,并包含进来:
- 1
- 2
- 3
- 4
- 1
- 2
- 3
- 4
这里展开替换后相当于:
- 1
- 1
文件scripts/basic/Makefile
中定义了编译在主机上执行的工具fixdep:
- 1
- 2
- 3
- 4
- 5
- 1
- 2
- 3
- 4
- 5
工具fixdep用于更新每一个生成目标的依赖文件*.cmd
。
上面定义的这个$(always)
在scripts/Makefile.build
里会被添加到targets中:
- 1
- 1
关于如何编译主机上可执行的程序,会在另外的文章中分析。
简而言之,scripts_basic
规则
- 1
- 2
- 1
- 2
它的最终结果就是编译scripts/basic/fixdep.c
生成主机上的可执行文件fixdep。至于为什么要编译fixdep和如何使用fixdep,会在另外的文章中分析。
2、顶层rpi_3_32b_defconfig的命令
完成对依赖scripts_basic
的更新后,接下来就是执行顶层目标的命令完成对rpi_3_32b_defconfig
的更新,展开后的规则如下:
- 1
- 2
- 1
- 2
其中$(build)
在kbuild.include
中定义:
- 1
- 2
- 3
- 4
- 5
- 1
- 2
- 3
- 4
- 5
这个make命令会第二次转到scripts/Makefile.build
去执行。
第二次调用scripts/Makefile.build进行编译
文件script/Makefile.build
的开头会根据传入的obj=scripts/kconfig
参数设置src=scripts/kconfig
。然后搜寻$(srctree)/$(src)
子目录下的makefile,由于src=scripts/kconfig
参数不同于第一次调用的参数(src=scripts/basic
),此处包含的makefile也不同于第一次的makefile了:
- 1
- 2
- 3
- 4
- 1
- 2
- 3
- 4
这里替换展开后相当于:
- 1
- 1
文件scripts/kconfig/Makefile
中定义了所有匹配%config
的目标:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 1
- 1
文件
scripts/kconfig/Makefile
中对于这里传入的
rpi_3_32b_defconfig
,匹配的目标是:
- 1
- 2
- 1
- 2
展开为:
- 1
- 2
- 3
- 1
- 2
- 3
此处目标rpi_3_32b_defconfig
依赖于scripts/kconfig/conf
,接下来检查并生成依赖。
- 1
- 1
hostprogs-y
指出conf被定义为主机上执行的程序,其依赖于另外两个文件:
- 1
- 1
通过编译conf.c
和zconf.tab.c
生成conf-objs
,并链接为scripts/kconfig/conf
。
生成依赖后就是执行目标的命令了:
- 1
- 1
工具scripts/kconfig/conf
的操作会在单独的文章中分析,此处只做简要的说明:
conf
工具从根目录下开始树状读取默认的Kconfig
文件,分析其配置并保存在内存中。分析完默认的Kconfig
后再读取指定文件(即arch/../configs/rpi_3_32b_defconfig
)更新得到最终的符号表,并输出到.config
文件中。
至此完成了make rpi_3_32b_defconfig
执行配置涉及的所有依赖和命令的分析。
3 make xxxx_defconfig配置流程简图
整个配置流程阐述得比较啰嗦,可以用一个简单的依赖图表示,如下:
(可以将图片拖到浏览器的其他窗口看大图)