uboot学习笔记2-顶层Makefile分析(一)

Makefile可以嵌套,也就是顶层 Makefile 可以调用子目录中的 Makefile 文件。 Makefile 嵌套在大项目中很常见,一般大项目里面所有的源代码都不会放到同一个目录中,各个功能模块的源代码都是分开的,各自存放在各自的目录中。每个功能模块目录下都有一个 Makefile,这个 Makefile 只处理本模块的编译链接工作,这样所有的编译链接工作就不用全部放到一个 Makefile 中,可以使得 Makefile 变得简洁明了。

因为uboot源文件中的Makefile有很多注释,所以截图分段来看

1.版本号

uboot学习笔记2-顶层Makefile分析(一)
VERSION是主版本号,PATCHLEVEL补丁版本号,SUBLEVEL 是次版本号,这三个一起构成了 uboot 的版本号
EXTRAVERSION 是附加版本信息, NAME 是和名字有关的

2.MAKEFLAGS变量

uboot学习笔记2-顶层Makefile分析(一)
在大工程中,我们会把不同模块或是不同的功能的源文件放在不同的目录中,每个目录下都可以书写一个Makedile,这样Makefile将会变得容易维护。

例如我们有一个子目录叫subdir,该目录下有一个Makefile文件,来指明了这个目录下文件的编译规则,我们的总控Makefile可以这样书写:
subsystem:
cd subdir && $(MAKE)
等价于:
subsystem:
$(MAKE) -C subdir
$(MAKE)就是调用“make”命令, -C 指定子目录。需要向子make传递变量就使用“export”来导出要传递给子make的变量即可,如果不希望哪个变量传递给子make的话就使用“unexport”

第20行的代码中使用“+=”来给变量 MAKEFLAGS 追加了一些值,“-rR”表示禁止使用内置的隐含规则和变量定义,“–include-dir”指明搜索路径, ”$(CURDIR)”表示当前目录

3. 命令输出

uboot学习笔记2-顶层Makefile分析(一)
ifeq表示条件语句的开始,endif是条件表达式的结束
第73行 判断(originV)"commandline"使origin(origin V)和"command line"是否相等,这里使用到了origin函数,其语法是`(origin )`origin函数不操作变量的值,只是告诉你变量是哪里来的,$(origin V)就是告诉变量V的来源,也就是判断变量V是否来自命令行定义origin函数返回值有下面几种:

  • undefine:从来没有定义过
  • default:如果是一个默认的定义,比如“CC”这个变量,这种变量我们将在后面讲述。 environment” 如果是一个环境变量, 并且当 Makefile 被执行时, “-e”参数没有被打开
  • file:变量被定义在Makefile中
  • command line:在命令行定义
  • override:变量被override指示符重新定义
  • automatic:是命令运行中的自动化变量
    第74行: 当ifeq中的条件成立的时候,变量KBUILD_VERBOSE就等于V所对应的值
    第76行~第78行 如果变量KBUILD_VERBOSE没有定义的话,就定义变量KBUILD_VERBOSE初始值为0
    第80行 判断变量KBUILD_VERBOSE是否等于1,如果KBUILD_VERBOSE等于1,则变量quiet和Q都等于空,如果KBUILD_VERBOSE=0则变量quiet为quiet_,变量Q为@

关于命令输出在正点原子的手册上是这样子写的
uboot学习笔记2-顶层Makefile分析(一)

4.静默输出

uboot学习笔记2-顶层Makefile分析(一)
在上面的命令输出中如果设置V=0或者在命令行中不定义V的话,编译uboot的时候终端中显示的短命令,但是还是会有命令输出,有时候我们在编译uboot的时候不需要输出命令,这个时候可以采用uboot的静默输出。编译的时候使用make-s即可。

第91行: 这里用到了Makefile中的filter函数,先来介绍一下这个函数
原型:$(filter <pattern…>,)
这是一个过滤函数,功能是以pattern模式过滤text字符串中的单词,保留符合模式pattern的单词,可以有多个模式。
返回值:返回符合pattern模式的字串
第91行中就是判断挡墙正在使用的编译器版本号是否为4.x,%是一个通配符,代表任意长度的字符串。

由于ubuntu16.04自带的make是4.1的,所以$ (filter 4.%,$(MAKE_VERSION))不为空,条件成立执行92~94行

**第92行:**用到了函数filter和firstword,下面是firstword的解释
原型:(firstword<text>)<text><text>92filter(firstword <text>) 名称:首单词函数 功能:取字符串<text>中的第一个单词。 返回:返回字符串<text>的第一个单词。 第92句的意思是这里也用到了函数 filter,在(firstword x$(MAKEFLAGS)))中过滤出符合“%s”的单词。到了函数 firstword,函数 firstword 是获取首单词,也就是过滤出过滤出首个以x开头的单词

**第101行:**使用 export 导出变量 quiet、 Q 和 KBUILD_VERBOSE。

5.设置编译输出结果

uboot 可以将编译出来的目标文件输出到单独的目录中,在make中使用“O”来指定输出目录,比如“make O=out”就是设置目标文件输出到out目录中,不指定O参数的话,源文件和编译产生的文件分开。
uboot学习笔记2-顶层Makefile分析(一)
第124行: 判断“O”是否来自命令行,如果来自命令行成立,KBUILD_OUTPUT就为$(O)
第129行: .PHONY”来显示地指明一个目标是“伪目标”,向 make 说明,不管是否有这个文件,这个目标就是“伪目标”。
第133行: 取消顶部Makefile上的隐式规则
第 135 行 判断 KBUILD_OUTPUT 是否为空。
第 139 行 调用 mkdir 命令,创建 KBUILD_OUTPUT 目录,并且将创建成功以后的绝对路径赋值给 KBUILD_OUTPUT。至此,通过 O 指定的输出目录就存在了。

6.代码检查

uboot 支持代码检查,使用命令“make C=1”使能代码检查,检查那些需要重新编译的文件。“make C=2”用于检查所有的源码文件
uboot学习笔记2-顶层Makefile分析(一)
第 176 行 判断 C 是否来源于命令行,如果 C 来源于命令行,那就将 C 赋值给变量KBUILD_CHECKSRC,如果命令行没有 C 的话 KBUILD_CHECKSRC 就为 0

7.模块编译

在 uboot 中允许单独编译某个模块,使用命令“ make M=dir”即可,旧语法“ makeSUBDIRS=dir”也是支持的。顶层 Makefile 中的代码如下:
uboot学习笔记2-顶层Makefile分析(一)
第 186 行 判断是否定义了 SUBDIRS ,如果定义了SUBDIRS ,变量KBUILD_EXTMOD=SUBDIRS,这里是为了支持老语法“make SUBIDRS=dir”
第 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。

8.获取主机架构和系统

接下来顶层 Makefile 会获取主机架构和系统,也就是我们电脑的架构和系统,代码如下:
uboot学习笔记2-顶层Makefile分析(一)
第 227 行 定义了一个变量 HOSTARCH,用于保存主机架构,这里调用 shell 命令“uname -m”获取架构名称。shell 中的“|”表示管道,意思是将左边的输出作为右边的输入, sed -e 是替换命令,“sed -e s/i.86/x86/”表示将管道输入的字符串中的“i.86”替换为“x86”,其他的“sed -s”命令同理。
第 237 行 定义了变量 HOSTOS,此变量用于保存主机 OS 的值,先使用 shell 命令“name -s”来获取主机 OS
这里使用的是Ubuntu16.04,所以OS就是Linux,使用管道将Linux传到“tr ‘[:upper:]’ ‘[:lower:]’”这是将大写字母替换成小写字母,也就是将Linux替换成linux,linux将作为“sed -e ‘s/(cygwin).*/cygwin/’”的输入
第 240 行 导出 HOSTARCH=x86_64, HOSTOS=linux。

9. 设置目标架构、交叉编译器和配置文件

这个就是在命令行中要输入的,在编译uboot的时候需要设置目标架构和交叉编译器
uboot学习笔记2-顶层Makefile分析(一)
第 245 行 判断 HOSTARCH 和 ARCH 这两个变量是否相等,主机架构(变量 HOSTARCH)是x86_64,而我们编译的是 ARM 版本 uboot,所以不相等。
这里可以指明架构和交叉编译器,这样编译的时候就不用每次在make命令后面定义ARCH和CROSS_COMPILE了
第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 里面的才是实时有效的配置。

10.调用scripts/Kbuild.include

主 Makefile 会调用文件 scripts/Kbuild.include 这个文件
uboot学习笔记2-顶层Makefile分析(一)
“include”包含了文件 scripts/Kbuild.include,此文件里面定义了很多变量

11.交叉编译工具变量设置

上面我们只是设置了 CROSS_COMPILE 的名字,但是交叉编译器其他的工具还没有设置,下面是一些设置。
uboot学习笔记2-顶层Makefile分析(一)

12.导出其他变量

导出变量
uboot学习笔记2-顶层Makefile分析(一)
有些变量的定义不在makefile中,比如在.config中

13.make xxx_defconfig

这是编译uboot之前的一个步骤,用来配置uboot
uboot学习笔记2-顶层Makefile分析(一)
uboot学习笔记2-顶层Makefile分析(一)

不得不说把这个Makefile写出来的真是神仙