当我们拿到开发板以后,是有三种 uboot 的,这三种 uboot的区别如表所示:
U-Boot 初次编译
首先在 Ubuntu 中安装 ncurses 库, 否则编译会报错:
sudo apt-get install libncurses5-dev
将正点原子提供的uboot-imx-2016.03-2.1.0-ge468cdc-v1.5.tar.bz2拷贝到自己建的文件夹下,并进行解压。
tar -vxjf uboot-imx-2016.03-2.1.0-g8b546e4.tar.bz2
这里使用的是 512MB+8GB 的 EMMC 核心板,使用如下命令来编译对应的 uboot:
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- distclean
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- mx6ull_14x14_ddr512_emmc_defconfig
make V=1 ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- -j12
上面的命令序列是在使用Make工具来编译一个针对特定硬件(如基于ARM架构的设备)的Linux内核或其他软件项目。这些命令通常在Linux内核开发或为嵌入式系统编译软件时使用。
1. make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- distclean
make
:调用Make工具,它根据Makefile(一个定义了编译规则的文件)来执行编译任务。ARCH=arm
:设置ARCH
变量为arm
,告诉Make工具当前的目标架构是ARM。这对于编译针对特定架构的代码非常重要。CROSS_COMPILE=arm-linux-gnueabihf-
:设置CROSS_COMPILE
变量,指定交叉编译工具链的前缀。这里arm-linux-gnueabihf-
前缀意味着将使用名为arm-linux-gnueabihf-gcc
(等等)的交叉编译器。交叉编译是在一种架构(如x86)上编译另一种架构(如ARM)上运行的代码的过程。distclean
:这是一个Makefile中定义的目标,它的作用通常是清除所有编译生成的文件和配置,使项目回到初始状态。
2. make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- mx6ull_14x14_ddr512_emmc_defconfig
- 这条命令几乎与第一条命令相同,不同之处在于最后的目标是
mx6ull_14x14_ddr512_emmc_defconfig
。 mx6ull_14x14_ddr512_emmc_defconfig
是一个特定的配置目标,用于生成一个预设的配置文件。这个配置文件包含了为特定硬件(在这个例子中是搭载了512MB DDR内存和eMMC存储的MX6ULL 14x14芯片)编译内核所需的选项和设置。
3. make V=1 ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- -j12
V=1
:设置V
变量为1
,通常用于使make命令输出更详细的编译信息,帮助开发者更好地理解编译过程中发生了什么。-j12
:这个选项告诉Make工具并行执行多个任务(在这个例子中是12个)。这可以显著减少编译时间,特别是在多核心处理器上。- 这条命令没有指定目标,所以Make将会尝试编译默认目标,通常是完整的软件或内核。这个命令实际上开始了编译过程,使用了前面命令设置的配置和交叉编译工具链。
我们可以新建一个 shell 脚本文件,将这些命令写到 shell 脚本文件里面,然后每次只需要执行 shell 脚本即可完成编译工作。新建
名为 mx6ull_alientek_emmc.sh 的 shell 脚本文件,然后在里面输入如下内容:
#!/bin/bash
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- distclean
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- mx6ull_14x14_ddr512_emmc_defconfig
make -s ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- -j12
U-Boot 顶层 Makefile 详解
编译后的uboot比编译前的uboot多了一些文件夹。具体作用如下图所示:
接下来我们主要分析Makefile这个重要文件。在阅读 uboot 源码之前,肯定是要先看一下顶层 Makefile,分析 gcc 版本代码的时候一定是
先从顶层 Makefile 开始的,然后再是子 Makefile,这样通过层层分析 Makefile 即可了解整个工程的组织结构。
版本号
VERSION 是主版本号, PATCHLEVEL 是补丁版本号, SUBLEVEL 是次版本号,这三个一起构成了 uboot 的版本号,比如当前的 uboot 版本号就是“2016.03”。 EXTRAVERSION 是附加版本信息, NAME 是和名字有关的,一般不使用这两个。
MAKEFLAGS 变量
$(MAKE) -C subdir
在 Makefile 中使用 $(MAKE) -C subdir
这条命令,是为了在当前的 make 过程中启动一个新的 make 子进程,这个子进程会切换到 subdir
子目录下,并在那里查找并执行 Makefile。这种方式常用于大型项目,其中项目被分解为多个子项目或模块,每个子项目或模块都有自己的 Makefile。下面我们分步骤详细解释这个命令的每个部分:
$(MAKE)
$(MAKE)
是一个特殊的变量,它引用了当前正在使用的 make 工具的命令。这可能是make
,也可能是gmake
或其他的 make 工具的名称,具体取决于系统和环境。- 使用
$(MAKE)
而不是直接写make
是一个好习惯。这样做的主要原因是,当 make 文件中包含对其他 Makefile 的递归调用时,$(MAKE)
可以确保使用的是与顶层相同的 make 版本。这在不同系统之间移植项目时尤其重要,因为不同系统可能安装了不同版本的 make 工具。
-C subdir
-C
是 make 命令的一个选项,它告诉 make 在执行操作之前切换到指定的目录。在这个例子中,subdir
就是要切换到的目录。- 这意味着 make 将在
subdir
目录中查找 Makefile 并执行它,而不是在当前目录。这对于组织有多个组件或子项目的大型项目非常有用,每个组件可以有自己的 Makefile,负责构建该组件。
使用场景
假设你有一个项目,它包含几个子目录,每个子目录都有自己的源代码和 Makefile。项目的目录结构可能如下所示:
project/
│
├── subdir1/
│ ├── Makefile
│ └── ...
│
├── subdir2/
│ ├── Makefile
│ └── ...
│
└── Makefile
在项目的根目录下的 Makefile 中,你可以使用 $(MAKE) -C subdir1
来构建 subdir1
中的代码,使用 $(MAKE) -C subdir2
来构建 subdir2
中的代码。这样,每个子目录的构建过程都是独立的,由其自己的 Makefile 控制,而顶层 Makefile 负责协调这些构建过程。有时候我们需要向子 make 传递变量,这个时候使用“export”来导出要传递给子 make 的变量即可,如果不希望哪个变量传递给子make 的话就使用“unexport”来声明不导出。
在 Makefile 中,export
和 unexport
指令用于控制环境变量的传递给子 make 进程。这是因为 make 工具允许通过递归调用(即从一个 Makefile 中调用另一个 Makefile)来构建复杂的项目。在这种情况下,父 make 进程可能需要将一些变量传递给它所调用的子 make 进程,使得这些子进程可以在相同的环境下运行或者有条件地修改其行为。
在大型或复杂的项目中,项目通常被分解为多个子项目或模块,每个子项目或模块都有自己的 Makefile。这些子项目可能需要一些公共的配置信息,比如编译器选项、目标架构、特定的路径等。通过在顶层 Makefile 中导出这些变量,然后在子 Makefile 中使用这些已导出的变量,可以确保所有子项目都使用一致的配置,这样做可以提高项目的可维护性和一致性。
-
export
: 当你在 Makefile 中使用export
指令时,后面跟着的变量会被导出到环境中,这样任何由当前 Makefile 启动的子 make 进程都能访问这些变量。这些变量成为了环境变量,对子进程是可见的。 -
unexport
: 相反,unexport
指令用于防止一个变量被导出到子 make 进程。如果你不希望某个变量被子进程继承,就可以使用这个指令。
假设你有一个顶层 Makefile,它调用了子目录中的另一个 Makefile:
export CFLAGS=-Wall
all:
$(MAKE) -C subdir
在这个例子中,CFLAGS
被导出为环境变量,当执行 $(MAKE) -C subdir
时,子 make 进程会继承 CFLAGS
变量。在 subdir
的 Makefile 中,就可以直接使用这个变量,而不需要重新定义它。
有两个特殊的变量:“SHELL”和“MAKEFLAGS”,这两个变量除非使用“unexport”声明,否则的话在整个make的执行过程中,它们的值始终自动的传递给子make。
- SHELL:
作用:SHELL 变量指定了 make 使用的 shell 程序。默认情况下,make 使用 /bin/sh 作为其 shell,但用户可以通过设置 SHELL 变量来改变这一行为。
为什么自动传递:自动传递 SHELL 变量确保了所有的 make 进程(无论父进程还是子进程)都使用相同的 shell 程序来执行 shell 命令。这对于保证构建脚本的一致性和可预测性至关重要,尤其是当构建脚本依赖于特定 shell 的特性时。 - MAKEFLAGS:
作用:MAKEFLAGS 变量包含了传递给 make 的标志(flags),这些标志可以修改 make 的行为。例如,如果你在命令行上运行 make 时使用了 -j(指定并行构建的作业数)或 -k(即使出现错误也继续进行其他的目标)等选项,这些选项会被自动加入到 MAKEFLAGS 变量中。
为什么自动传递:自动传递 MAKEFLAGS 变量确保了所有的 make 子进程都将以与父进程相同的方式执行,包括错误处理、输出控制、并行作业数等。这是保持整个构建过程一致性的关键,尤其是在复杂的项目中,不同层级的 Makefile 可能需要以相同的方式对待错误或并行化策略。
命令输出
顶层 Makefile 中控制命令输出的代码如下:
ifeq ("$(origin V)", "command line")
KBUILD_VERBOSE = $(V)
endif
ifndef KBUILD_VERBOSE
KBUILD_VERBOSE = 0
endif
ifeq ($(KBUILD_VERBOSE),1)
quiet =
Q =
else
quiet=quiet_
Q = @
endif
这段代码是从 Makefile 中提取的,通常用于控制编译过程中命令的显示方式。它允许用户通过命令行参数或环境变量来控制构建过程的冗余级别。让我们逐步解析这段代码的含义和作用:
第一部分:判断 V
变量的来源
ifeq ("$(origin V)", "command line")
KBUILD_VERBOSE = $(V)
endif
- 这部分代码检查变量
V
是否是从命令行传递给 make 的。$(origin V)
是一个特殊的函数,用于返回变量V
的定义来源(例如,环境、命令行、Makefile 等)。 - 如果
V
是在命令行上定义的(例如,通过make V=1
),则将KBUILD_VERBOSE
设置为V
的值。这允许用户在调用 make 时动态地控制输出的详细程度。
第二部分:设置 KBUILD_VERBOSE
的默认值
ifndef KBUILD_VERBOSE
KBUILD_VERBOSE = 0
endif
- 这部分代码检查
KBUILD_VERBOSE
是否未定义。如果未定义,则将其设置为0
。这意味着默认情况下,构建过程将不显示所有命令(即,非冗余模式)。
第三部分:根据 KBUILD_VERBOSE
的值设置 quiet
和 Q
ifeq ($(KBUILD_VERBOSE),1)
quiet =
Q =
else
quiet=quiet_
Q = @
endif
- 这部分代码根据
KBUILD_VERBOSE
的值来设置两个变量:quiet
和Q
。 - 如果
KBUILD_VERBOSE
等于1
,则quiet
和Q
都被设置为空字符串。这表示构建过程将显示所有执行的命令,因为在 Makefile 的命令行前没有@
符号时,make 会打印该命令。 - 如果
KBUILD_VERBOSE
不等于1
(通常意味着它是0
),则quiet
被设置为quiet_
,而Q
被设置为@
。@
符号用于在执行命令时不显示该命令本身,这样构建过程就更加“安静”。quiet
变量的值(quiet_
)可能用于定义一些特定的打印前缀或用于其他目的,具体取决于 Makefile 的其他部分如何使用它。
静默输出
设置 V=0 或者在命令行中不定义 V 的话,编译 uboot 的时候终端中显示的短命令,但是还是会有命令输出,有时候我们在编译 uboot 的时候不需要输出命令,这个时候就可以使用 uboot 的静默输出功能。编译的时候使用“make -s”即可实现静默输出
ifneq ($(filter 4.%,$(MAKE_VERSION)),) # make-4
ifneq ($(filter %s ,$(firstword x$(MAKEFLAGS))),)
quiet=silent_
endif
else # make-3.8x
ifneq ($(filter s% -s%,$(MAKEFLAGS)),)
quiet=silent_
endif
endif
这段代码用于检测 GNU Make 的版本以及是否在命令行上指定了“silent”模式(通常是通过 -s
或 --silent
选项),并据此设置一个变量 quiet
,以控制 Makefile 中命令的输出。这是一种在不同版本的 Make 程序中保持兼容性的技巧,同时允许开发者通过命令行选项控制输出的详细程度。下面是对这段代码的逐行解释:
1: 检测 GNU Make 的版本
ifneq ($(filter 4.%,$(MAKE_VERSION)),) # make-4
- 这行代码检查
MAKE_VERSION
变量是否以4.
开头。MAKE_VERSION
是一个内置变量,包含了当前 GNU Make 的版本号。$(filter 4.%,$(MAKE_VERSION))
的作用是从MAKE_VERSION
中筛选出所有以4.
开头的版本号。 - 如果结果非空,意味着当前 GNU Make 的版本至少是 4.x,代码块进入“make-4”部分。
2: 检测是否在命令行上指定了“silent”模式(GNU Make 4.x)
ifneq ($(filter %s ,$(firstword x$(MAKEFLAGS))),)
quiet=silent_
endif
- 这里,
$(MAKEFLAGS)
包含了传递给 make 的所有标志(即命令行选项)。通过在$(MAKEFLAGS)
前添加一个x
并使用$(firstword ...)
,确保即使MAKEFLAGS
为空,firstword
函数也能返回一个非空字符串(即x
),这样filter
函数总是有一个字符串可以检查。 $(filter %s ,x$(MAKEFLAGS))
的目的是检查MAKEFLAGS
中是否包含一个以s
结尾的选项(这里的空格是为了避免匹配像--some-option
这样实际上并不是表示 silent 的选项)。如果找到了,quiet
被设置为silent_
。
3: 对于早期版本的 GNU Make(3.8x)
else # make-3.8x
ifneq ($(filter s% -s%,$(MAKEFLAGS)),)
quiet=silent_
endif
endif
- 如果 GNU Make 的版本低于 4.x,代码进入这个分支。
- 这里使用
$(filter s% -s%,$(MAKEFLAGS))
来检查MAKEFLAGS
是否包含以s
开头的选项或者是-s
。这是因为在 GNU Make 的早期版本中,MAKEFLAGS
的格式可能与 4.x 有所不同,需要使用不同的方法来检测 silent 模式。 - 如果条件满足,同样将
quiet
设置为silent_
。
设置编译结果输出目录
uboot 可以将编译出来的目标文件输出到单独的目录中,在 make 的时候使用“O”来指定输出目录,比如“make O=out”就是设置目标文件输出到 out 目录中。
代码检查
uboot 支持代码检查,使用命令“make C=1”使能代码检查,检查那些需要重新编译的文件。“make C=2”用于检查所有的源码文件。
模块编译
在 uboot 中允许单独编译某个模块,使用命令“ make M=dir”即可,旧语法“ make SUBDIRS=dir”也是支持的.
获取主机架构和系统
接下来顶层 Makefile 会获取主机架构和系统,也就是我们电脑的架构和系统:
227 HOSTARCH := $(shell uname -m | \
228 sed -e s/i.86/x86/ \
229 -e s/sun4u/sparc64/ \
230 -e s/arm.*/arm/ \
231 -e s/sa110/arm/ \
232 -e s/ppc64/powerpc/ \
233 -e s/ppc/powerpc/ \
234 -e s/macppc/powerpc/\
235 -e s/sh.*/sh/)
236
237 HOSTOS := $(shell uname -s | tr '[:upper:]' '[:lower:]' | \
238 sed -e 's/\(cygwin\).*/cygwin/')
239
240 export HOSTARCH HOSTOS
这段代码是一部分常见于构建系统和自动化脚本中的Shell脚本,其目的是确定运行脚本的机器的架构 (HOSTARCH
) 和操作系统 (HOSTOS
)。这些信息通常用于配置和编译跨平台的软件。下面是对每一行的详细解释:
第 227-235 行:确定 HOSTARCH
227 HOSTARCH := $(shell uname -m | \
228 sed -e s/i.86/x86/ \
229 -e s/sun4u/sparc64/ \
230 -e s/arm.*/arm/ \
231 -e s/sa110/arm/ \
232 -e s/ppc64/powerpc/ \
233 -e s/ppc/powerpc/ \
234 -e s/macppc/powerpc/\
235 -e s/sh.*/sh/)
HOSTARCH := $(shell uname -m | ...)
:这行代码使用uname -m
命令获取当前机器的硬件名称(架构),然后通过管道传递给sed
命令进行处理。sed
命令用于处理文本数据。它接收uname -m
的输出,并根据一系列的替换规则修改这些输出:s/i.86/x86/
:将任何以i
开头,后接一个字符,再接86
的架构名称(如i386
、i486
等)替换为x86
。s/sun4u/sparc64/
:将sun4u
替换为sparc64
。s/arm.*/arm/
:将以arm
开头的任何架构名称简化为arm
。s/sa110/arm/
:将sa110
替换为arm
。s/ppc64/powerpc/
和s/ppc/powerpc/
:将ppc64
和ppc
都替换为powerpc
。s/macppc/powerpc/
:将macppc
替换为powerpc
。s/sh.*/sh/
:将以sh
开头的任何架构名称简化为sh
。
第 237-238 行:确定 HOSTOS
237 HOSTOS := $(shell uname -s | tr '[:upper:]' '[:lower:]' | \
238 sed -e 's/\(cygwin\).*/cygwin/')
HOSTOS := $(shell uname -s | ...)
:这行代码使用uname -s
命令获取当前操作系统的名称。tr '[:upper:]' '[:lower:]'
:这个命令将所有大写字母转换为小写字母,确保操作系统名称的一致性,不受大小写影响。sed -e 's/\(cygwin\).*/cygwin/'
:如果操作系统名称中包含cygwin
,则将整个名称简化为cygwin
。这里使用了正则表达式的捕获组\( ... \)
和.*
来匹配cygwin
后的任何字符,并将整个匹配替换为cygwin
。
第 240 行:导出变量
240 export HOSTARCH HOSTOS
- 这行代码使用
export
命令将HOSTARCH
和HOSTOS
变量导出,使得这些变量在当前脚本启动的子进程中也可用。这对于构建系统中的子脚本和程序来说是必要的,因为它们可能需要根据不同的架构和操作系统进行不同的处理。
设置目标架构、交叉编译器和配置文件
编 译 uboot 的 时 候 需 要 设 置 目 标 板 架 构 和 交 叉 编 译 器 ,“ make ARCH=armCROSS_COMPILE=arm-linux-gnueabihf-”就是用于设置 ARCH 和 CROSS_COMPILE,在顶层
Makefile 中代码如下:
244 # set default to nothing for native builds
245 ifeq ($(HOSTARCH),$(ARCH))
246 CROSS_COMPILE ?=
247 endif
248
249 KCONFIG_CONFIG ?= .config
250 export KCONFIG_CONFIG
第 244 行:注释
244 # set default to nothing for native builds
- 这行是一个注释,说明了接下来代码的目的:为本地构建(即目标架构与宿主架构相同的情况)设置默认值为空。
第 245-247 行:条件判断和设置变量
245 ifeq ($(HOSTARCH),$(ARCH))
246 CROSS_COMPILE ?=
247 endif
ifeq ($(HOSTARCH),$(ARCH))
:这是一个条件判断语句,检查HOSTARCH
(宿主机器的架构)是否与ARCH
(目标架构)相同。这里HOSTARCH
和ARCH
是在 Makefile 或其包含的环境中定义的变量。CROSS_COMPILE ?=
:如果条件为真(即构建目标与宿主机器架构相同),则设置CROSS_COMPILE
变量为空。CROSS_COMPILE
通常用于指定交叉编译工具链的前缀。例如,如果目标是 ARM 架构,CROSS_COMPILE
可能被设置为arm-linux-gnueabihf-
。通过设置为空,这一行表明不需要交叉编译工具链,因为我们正在进行本地构建。?=
是 Makefile 中的条件赋值操作符,仅当CROSS_COMPILE
未被之前的 Makefile 或命令行定义时才设置其值。
第 249 行:设置默认配置文件路径
249 KCONFIG_CONFIG ?= .config
- 这行设置
KCONFIG_CONFIG
变量的默认值为.config
,除非它已经在其他地方被定义。.config
文件通常包含内核或应用的配置选项,这是 Linux 内核和许多其他基于 Kconfig 系统的项目的标准配置文件名。
第 250 行:导出变量
250 export KCONFIG_CONFIG
export KCONFIG_CONFIG
:这条指令将KCONFIG_CONFIG
变量导出到环境中,使其在由这个 Makefile 触发的任何子进程中都可用。这对于确保所有子 Makefile 和脚本都使用相同的配置文件非常重要。
调用 scripts/Kbuild.include
主 Makefile 会调用文件 scripts/Kbuild.include 这个文件,在 uboot 的编译过程中会用到 scripts/Kbuild.include 中的这些变量,后面用到的时候再分析。
交叉编译工具变量设置
只是设置了 CROSS_COMPILE 的名字,但是交叉编译器其他的工具还没有设置,顶层 Makefile 中相关代码如下:
333 AS = $(CROSS_COMPILE)as
334 # Always use GNU ld
335 ifneq ($(shell $(CROSS_COMPILE)ld.bfd -v 2> /dev/null),)
336 LD = $(CROSS_COMPILE)ld.bfd
337 else
338 LD = $(CROSS_COMPILE)ld
339 endif
340 CC = $(CROSS_COMPILE)gcc
341 CPP = $(CC) -E
342 AR = $(CROSS_COMPILE)ar
343 NM = $(CROSS_COMPILE)nm
344 LDR = $(CROSS_COMPILE)ldr
345 STRIP = $(CROSS_COMPILE)strip
346 OBJCOPY = $(CROSS_COMPILE)objcopy
347 OBJDUMP = $(CROSS_COMPILE)objdump
这段代码是 Makefile 中的一部分,主要用于设置编译环境中的各种工具,特别是针对可能的交叉编译场景。每个变量都关联到特定的编译工具,而 CROSS_COMPILE
变量的前缀则用于指定特定架构的工具链。下面是对每一行的详细解释:
第 333 行:汇编器设置
333 AS = $(CROSS_COMPILE)as
AS
变量被设置为使用CROSS_COMPILE
前缀的汇编器as
。这意味着如果进行交叉编译,as
将是为目标架构定制的版本。
第 334-339 行:链接器选择
334 # Always use GNU ld
335 ifneq ($(shell $(CROSS_COMPILE)ld.bfd -v 2> /dev/null),)
336 LD = $(CROSS_COMPILE)ld.bfd
337 else
338 LD = $(CROSS_COMPILE)ld
339 endif
- 这段代码通过检测
ld.bfd
版本的存在来决定使用哪个链接器。ld.bfd
是 GNU ld 链接器的一种变体,通常提供更多的功能或对特定架构有更好的支持。 - 如果
$(shell $(CROSS_COMPILE)ld.bfd -v 2> /dev/null)
返回的是非空字符串,说明ld.bfd
可用,那么LD
将被设置为$(CROSS_COMPILE)ld.bfd
。 - 如果
ld.bfd
不可用,则回退到使用标准的ld
链接器。
第 340-347 行:其他编译工具设置
340 CC = $(CROSS_COMPILE)gcc
341 CPP = $(CC) -E
342 AR = $(CROSS_COMPILE)ar
343 NM = $(CROSS_COMPILE)nm
344 LDR = $(CROSS_COMPILE)ldr
345 STRIP = $(CROSS_COMPILE)strip
346 OBJCOPY = $(CROSS_COMPILE)objcopy
347 OBJDUMP = $(CROSS_COMPILE)objdump
CC
设置为使用CROSS_COMPILE
前缀的 GCC 编译器。CPP
是 C 预处理器,这里设置为调用CC
(即gcc
)并加上-E
选项,仅进行预处理。AR
用于创建静态库。NM
显示符号信息。LDR
可能是在这个特定环境中定义的一个工具,这不是一个标准的 GNU 工具名称,可能是特定项目或工具链的一部分。STRIP
用于去除二进制文件中的符号信息,减小尺寸。OBJCOPY
用于复制和转换目标文件。OBJDUMP
用于显示二进制文件中的信息。
导出其他变量
368 export VERSION PATCHLEVEL SUBLEVEL UBOOTRELEASE UBOOTVERSION
369 export ARCH CPU BOARD VENDOR SOC CPUDIR BOARDDIR
370 export CONFIG_SHELL HOSTCC HOSTCFLAGS HOSTLDFLAGS CROSS_COMPILE AS
LD CC
371 export CPP AR NM LDR STRIP OBJCOPY OBJDUMP
372 export MAKE AWK PERL PYTHON
373 export HOSTCXX HOSTCXXFLAGS DTC CHECK CHECKFLAGS
374
375 export KBUILD_CPPFLAGS NOSTDINC_FLAGS UBOOTINCLUDE OBJCOPYFLAGS
LDFLAGS
376 export KBUILD_CFLAGS KBUILD_AFLAGS
make xxx_defconfig 过程
414 # To make sure we do not include .config for any of the *config
targets
415 # catch them early, and hand them over to scripts/kconfig/Makefile
416 # It is allowed to specify more targets when calling make,
including
417 # mixing *config targets and build targets.
418 # For example 'make oldconfig all'.
419 # Detect when mixed targets is specified, and make a second
invocation
420 # of make so .config is not included in this case either (for
*config).
421
422 version_h := include/generated/version_autogenerated.h
423 timestamp_h := include/generated/timestamp_autogenerated.h
424
425 no-dot-config-targets := clean clobber mrproper distclean \
426 help %docs check% coccicheck \
427 ubootversion backup
428
429 config-targets := 0
430 mixed-targets := 0
431 dot-config := 1
432
433 ifneq ($(filter $(no-dot-config-targets), $(MAKECMDGOALS)),)
434 ifeq ($(filter-out $(no-dot-config-targets), $(MAKECMDGOALS)),)
435 dot-config := 0
436 endif
437 endif
438
439 ifeq ($(KBUILD_EXTMOD),)
440 ifneq ($(filter config %config,$(MAKECMDGOALS)),)
441 config-targets := 1
442 ifneq ($(words $(MAKECMDGOALS)),1)
443 mixed-targets := 1
444 endif
445 endif
446 endif
447
448 ifeq ($(mixed-targets),1)
449 # ================================================================
450 # We're called with mixed targets (*config and build targets).
451 # Handle them one by one.
452
453 PHONY += $(MAKECMDGOALS) __build_one_by_one
454
455 $(filter-out __build_one_by_one, $(MAKECMDGOALS)):
__build_one_by_one
456 @:
457
458 __build_one_by_one:
459 $(Q)set -e; \
460 for i in $(MAKECMDGOALS); do \
461 $(MAKE) -f $(srctree)/Makefile $$i; \
462 done
463
464 else
465 ifeq ($(config-targets),1)
466 # ================================================================
467 # *config targets only - make sure prerequisites are updated, and
descend
468 # in scripts/kconfig to make the *config target
469
470 KBUILD_DEFCONFIG := sandbox_defconfig
471 export KBUILD_DEFCONFIG KBUILD_KCONFIG
472
473 config: scripts_basic outputmakefile FORCE
474 $(Q)$(MAKE) $(build)=scripts/kconfig $@
475
476 %config: scripts_basic outputmakefile FORCE
477 $(Q)$(MAKE) $(build)=scripts/kconfig $@
478
479 else
480 #==================================================================
481 # Build targets only - this includes vmlinux, arch specific
targets, clean
482 # targets and others. In general all targets except *config
targets.
483
484 ifeq ($(dot-config),1)
485 # Read in config
486 -include include/config/auto.conf
第 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-targets=1。
第 442 行统计 MAKECMDGOALS 中的单词个数,如果不为 1 的话条件成立。此处调用
Makefile 中的 words 函数来统计单词个数, words 函数格式如下:
$(words
很明显, 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 行有如下定义:
示例代码 31.3.13.2 顶层 Makefile 代码段
1610 PHONY += FORCE
1611 FORCE:
由于 FORCE 没有定义任何依赖,并且它没有与之关联的文件(即没有物理文件名为 FORCE 的文件),Make 无法找到一个实际的文件来检查时间戳。因此,Make 每次运行时都会认为 FORCE 需要被更新,因为它总是缺少一个有效的时间戳来进行比较。
依赖 scripts_basic 和 outputmakefile 在顶层 Makefile 中的内容如下:
示例代码 31.3.13.3 顶层 Makefile 代码段
394 # Basic helpers built in scripts/
395 PHONY += scripts_basic
396 scripts_basic:
397 $(Q)$(MAKE) $(build)=scripts/basic
398 $(Q)rm -f .tmp_quiet_recordmcount
399
400 # To avoid any implicit rule to kick in, define an empty command.
401 scripts/basic/%: scripts_basic ;
402
403 PHONY += outputmakefile
404 # outputmakefile generates a Makefile in the output directory, if
405 # using a separate output directory. This allows convenient use of
406 # make in the output directory.
407 outputmakefile:
408 ifneq ($(KBUILD_SRC),)
409 $(Q)ln -fsn $(srctree) source
410 $(Q)$(CONFIG_SHELL) $(srctree)/scripts/mkmakefile \
411 $(srctree) $(objtree) $(VERSION) $(PATCHLEVEL)
412 endif
第 408 行,判断 KBUILD_SRC 是否为空,只有变量 KBUILD_SRC 不为空的时候,outputmakefile 才有意义,经过我们前面的分析 KBUILD_SRC 为空,所以 outputmakefile 无效。只有 scripts_basic 是有效的。
第 396~398 行是 scripts_basic 的规则,其对应的命令用到了变量 Q、 MAKE 和 build,其中:
Q=@或为空
MAKE=make
变量 build 是在 scripts/Kbuild.include 文件中有定义,定义如下:
示例代码 31.3.13.3 Kbuild.include 代码段
177 ###
178 # Shorthand for $(Q)$(MAKE) -f scripts/Makefile.build obj=
179 # Usage:
180 # $(Q)$(MAKE) $(build)=dir
181 build := -f $(srctree)/scripts/Makefile.build obj
从示例代码 31.3.13.3 可以看出 build=-f $(srctree)/scripts/Makefile.build obj,经过前面的分析可知,变量 srctree 为”.”,因此:
build=-f ./scripts/Makefile.build obj
scripts_basic 展开以后如下:
scripts_basic:
@make -f ./scripts/Makefile.build obj=scripts/basic //也可以没有@,视配置而定
@rm -f . tmp_quiet_recordmcount //也可以没有@
scripts_basic 会调用文件./scripts/Makefile.build,这个我们后面在分析。
接着回到示例代码 31.3.13.1 中的%config 处,内容如下:
%config: scripts_basic outputmakefile FORCE
$(Q)$(MAKE) $(build)=scripts/kconfig $@
将命令展开就是:
@make -f ./scripts/Makefile.build obj=scripts/kconfig xxx_defconfig
同样也跟文件./scripts/Makefile.build 有关,我们后面再分析此文件。使用如下命令配置 uboot,
并观察其配置过程:
make mx6ull_14x14_ddr512_emmc_defconfig V=1
①、 scripts_basic 目标对应的命令
@make -f ./scripts/Makefile.build obj=scripts/basic
②、 %config 目标对应的命令
@make -f ./scripts/Makefile.build obj=scripts/kconfig xxx_defconfig
Makefile.build 脚本分析
从上一小节可知,“ make xxx_defconfig“配置 uboot 的时候如下两行命令会执行脚本
scripts/Makefile.build:
@make -f ./scripts/Makefile.build obj=scripts/basic
@make -f ./scripts/Makefile.build obj=scripts/kconfig xxx_defconfig
1、 scripts_basic 目标对应的命令
scripts_basic 目标对应的命令为: @make -f ./scripts/Makefile.build obj=scripts/basic。打开文
件 scripts/Makefile.build,有如下代码:
示例代码 31.3.14.1 Makefile.build 代码段
8 # Modified for U-Boot
9 prefix := tpl
10 src := $(patsubst $(prefix)/%,%,$(obj))
11 ifeq ($(obj),$(src))
12 prefix := spl
13 src := $(patsubst $(prefix)/%,%,$(obj))
14 ifeq ($(obj),$(src))
15 prefix := .
16 endif
17 endif
第 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:
示例代码 31.3.14.2 Makefile.build 代码段
56 # The filename Kbuild has precedence over Makefile
57 kbuild-dir := $(if $(filter /%,$(src)),$(src),$(srctree)/$(src))
58 kbuild-file := $(if $(wildcard $(kbuild-dir)/Kbuild),$(kbuilddir)/Kbuild,$(kbuild-dir)/Makefile)
59 include $(kbuild-file)
继续分析 scripts/Makefile.build,如下代码:
示例代码 31.3.14.3 Makefile.build 代码段
116 __build: $(if $(KBUILD_BUILTIN),$(builtin-target) $(lib-target)
$(extra-y)) \
117 $(if $(KBUILD_MODULES),$(obj-m) $(modorder-target)) \
118 $(subdir-ym) $(always)
119 @:
__build 是默认目标,因为命令“@make -f ./scripts/Makefile.build obj=scripts/basic”没有指定目标,所以会使用到默认目标: __build。在顶层 Makefile 中, KBUILD_BUILTIN 为 1,KBUILD_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 V=1
从上图可以看出,只有 always 有效,因此__build 最终为:
__build: scripts/basic/fixdep
@:
__build 依赖于 scripts/basic/fixdep,所以要先编译 scripts/basic/fixdep.c,生成 fixdep,前面
已经读取了 scripts/basic/Makefile 文件。
2、 %config 目标对应的命令
%config 目 标 对 应 的 命 令 为 : @make -f ./scripts/Makefile.build obj=scripts/kconfig
xxx_defconfig,各个变量值如下:
src= scripts/kconfig
kbuild-dir = ./scripts/kconfig
kbuild-file = ./scripts/kconfig/Makefile
include ./scripts/kconfig/Makefile
可以看出, Makefile.build 会读取 scripts/kconfig/Makefile 中的内容,此文件有如下所示内容:
示例代码 31.3.14.4 scripts/kconfig/Makefile 代码段
113 %_defconfig: $(obj)/conf
114 $(Q)$< $(silent) --defconfig=arch/$(SRCARCH)/configs/$@
$(Kconfig)
115
116 # Added for U-Boot (backward compatibility)
117 %_config: %_defconfig
118 @:
目标%_defconfig 刚好和我们输入的 xxx_defconfig 匹配,所以会执行这条规则。依赖为$(obj)/conf,展开后就是 scripts/kconfig/conf。接下来就是检查并生成依赖 scripts/kconfig/conf。conf 是主机软件,到这里我们就打住,不要纠结 conf 是怎么编译出来的,否则就越陷越深,太绕了,像 conf 这种主机所使用的工具类软件我们一般不关心它是如何编译产生的。如果一定要看是 conf 是怎么生成的,可以输入如下命令重新配置 uboot,在重新配置 uboot 的过程中就会输出 conf 编译信息。
make distclean
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- mx6ull_14x14_ddr512_emmc_
defconfig V=1
得到 scripts/kconfig/conf 以后就要执行目标%_defconfig 的命令:
$(Q)$< $(silent) --defconfig=arch/$(SRCARCH)/configs/$@ $(Kconfig)
相关的变量值如下:
silent=-s 或为空
SRCARCH=..
Kconfig=Kconfig
将其展开就是:
@ scripts/kconfig/conf --defconfig=arch/../configs/xxx_defconfig Kconfig
上述命令用到了 xxx_defconfig 文件,比如 mx6ull_alientek_emmc_defconfig。这里会将mx6ull_alientek_emmc_defconfig 中的配置输出到.config 文件中,最终生成 uboot 根目录下
的.config 文件。
这个就是命令 make xxx_defconfig 执行流程,
这部分看的迷迷糊糊 有点难以理解。等到之后还要重新再看。