LV100-顶层Makefile基础解析
U-Boot 顶层 Makefile 基础解析
一、版本号
版本号定义在这里:Makefile
VERSION = 2019
PATCHLEVEL = 04
SUBLEVEL =
EXTRAVERSION =
NAME =VERSION 是主版本号,PATCHLEVEL 是补丁版本号,SUBLEVEL 是次版本号,这三个一 起构成了 uboot 的版本号,比如当前的 uboot 版本号就是“2019.04”。EXTRAVERSION 是附加 版本信息,NAME 是和名字有关的,一般不使用这两个。
二、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)”表示当前目录。
三、命令输出
1. 默认的输出情况
uboot 默认编译是不会在终端中显示完整的命令,都是短命令:
sumu@sumu-virtual-machine:~/7Linux/imx6ull-uboot$ make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- -j16
scripts/kconfig/conf --syncconfig Kconfig
CHK include/config.h
UPD include/config.h
GEN include/autoconf.mk.dep
CFG u-boot.cfg
GEN include/autoconf.mk
# ......
*** Your GCC is older than 6.0 and will not be supported
HOSTCC scripts/dtc/dtc.o
# ......
HOSTCC tools/zynqimage.o
# ......
LD fs/fat/built-in.o
CC lib/efi_loader/efi_unicode_collation.o
# ......
OBJCOPY u-boot.srec
SYM u-boot.sym
DTC arch/arm/dts/imx6ull-14x14-ddr3-arm2.dtb
# ......
COPY u-boot.dtb
COPY u-boot.bin
CFGS u-boot-dtb.cfgout
MKIMAGE u-boot-dtb.imx
CFGCHK u-boot.cfg在终端中输出短命令虽然看起来很清爽,但是不利于分析 uboot 的编译过程。
2. 完整命令输出
可以通过设 置变量“V = 1“来实现完整的命令输出,这个在调试 uboot 的时候很有用:
sumu@sumu-virtual-machine:~/7Linux/imx6ull-uboot$ make V=1 ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- -j16
make -f ./Makefile syncconfig
#......
make -f ./scripts/Makefile.build obj=scripts/kconfig syncconfig
mkdir -p include/config include/generated
scripts/kconfig/conf --syncconfig Kconfig
make -f ./scripts/Makefile.autoconf || \
{ rm -f include/config/auto.conf; false; }
if [ -d arch/arm/mach-mx6/include/mach ]; then \
dest=../../mach-mx6/include/mach; \
else \
dest=arch-mx6; \
fi; \
# ......
*** Your GCC is older than 6.0 and will not be supported
make -f ./scripts/Makefile.build obj=scripts
make -f ./scripts/Makefile.build obj=scripts/dtc
cc -Wp,-MD,scripts/dtc/.dtc.o.d -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer -std=gnu11 -Iscripts/dtc -Iscripts/dtc/libfdt -c -o scripts/dtc/dtc.o scripts/dtc/dtc.c
# ......
make -f ./scripts/Makefile.build obj=arch/arm/mach-imx u-boot-dtb.imx
cp u-boot-dtb.bin u-boot.bin
#......
./tools/mkimage -n u-boot-dtb.cfgout -T imximage -e 0x87800000 -d u-boot-dtb.bin u-boot-dtb.imx >u-boot-dtb.imx.log && cat u-boot-dtb.imx.log
Image Type: Freescale IMX Boot Image
Image Ver: 2 (i.MX53/6/7 compatible)
Mode: DCD
Data Size: 675840 Bytes = 660.00 KiB = 0.64 MiB
Load Address: 877ff420
Entry Point: 87800000
./scripts/check-config.sh u-boot.cfg ./scripts/config_whitelist.txt .3. 命令输出控制
3.1 命令输出控制命令
顶层 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 = @
endif3.2 V 的来源
V 的来源在 Makefile 中是这一部分:
ifeq ("$(origin V)", "command line")
KBUILD_VERBOSE = $(V)
endif
ifndef KBUILD_VERBOSE
KBUILD_VERBOSE = 0
endif上述代码中先使用 ifeq 来判断 "$(origin V)" 和 "command line" 是否相等。这里用到了 Makefile 中的函数 origin,origin 和其他的函数不一样,它不操作变量的值,origin 用于告诉你变量是哪来的,语法为:
$(origin <variable>)variable 是变量名,origin 函数的返回值就是变量来源,因此 $(origin V) 就是变量 V 的来源。 如果变量 V 是在命令行定义的那么它的来源就是 "command line",这样 "$(origin V)" 和 "command line" 就相等了。当这两个相等的时候变量 KBUILD_VERBOSE 就等于 V 的值,比如在命令行中 输入“ V = 1 “ 的 话 那 么 KBUILD_VERBOSE = 1 。如果没有在命令行输入 V 的话 KBUILD_VERBOSE = 0。
3.3 Q 与 quiet
这两个变量的控制在 Makefile 中是这一部分:
ifeq ($(KBUILD_VERBOSE),1)
quiet =
Q =
else
quiet=quiet_
Q = @
endif判断 KBUILD_VERBOSE 是否为 1,如果 KBUILD_VERBOSE 为 1 的话变量 quiet 和 Q 都为空,如果 KBUILD_VERBOSE = 0 的话变量 quiet 为“quiet_“,变量 Q 为“@”,所以 V 的作用是:
- V = 1
KBUILD_VERBOSE=1
quiet=空
Q=空- V = 0 或者命令行不定义 V
KBUILD_VERBOSE=0
quiet= quiet_
Q=@4. 怎么控制输出信息?
Makefile 中会用到变量 quiet 和 Q 来控制编译的时候是否在终端输出完整的命令,在顶层 Makefile 中有很多如下所示的命令:
tools-only: scripts_basic $(version_h) $(timestamp_h)
$(Q)$(MAKE) $(build)=tools如果 V = 0 的话上述命令展开就是:
@ make $(build)=toolsmake 在执行的时候默认会在终 端输出命令,但是在命令前面加上“@”就不会在终端输出命令了。
当 V = 1 的时候 Q 就为空, 上述命令就是:
make $(build)=tools此时在 make 执行的过程,命令会被完整的输出在终端上。
有些命令会有两个版本,比如 Makefile 中的:
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_”的话,整个命令都不会输出。这部分在 Makefile - Beautify output 中有说明。
5. 静默输出
5.1 静默输出命令
设置 V = 0 或者在命令行中不定义 V 的话,编译 uboot 的时候终端中显示的短命令,但是还是会有命令输出,有时候我们在编译 uboot 的时候不需要输出命令,这个时候 就可以使用 uboot 的静默输出功能。编译的时候使用这个命令就可以实现:
make -s即可实现静默输出,顶层 Makefile 中相应的代码如下:
# If the user is running make -s (silent mode), suppress echoing of
# commands
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
endif5.2 make 版本号判断
先看 Makefile 这一部分:
ifneq ($(filter 4.%,$(MAKE_VERSION)),) # make-4
# (1)......
else # make-3.8x
# (2)......
endif这里在判断当前正在使用的 make 命令版本号是否为 4.x,首先进行如下判断:
判断 $(filter 4.%,$(MAKE_VERSION)) 和 " "(空)是否相等?如果不相等的话就成立,执行里面的(1)语句。这里用到了 Makefile 中的 filter 函数,这是 个过滤函数,函数格式如下:
$(filter <pattern...>,<text>)filter 函数表示以 pattern 模式过滤 text 字符串中的单词,仅保留符合模式 pattern 的单词, 可以有多个模式。函数返回值就是符合 pattern 的字符串。
因此 $(filter 4.%,$(MAKE_VERSION)) 的含义就是在字符串 “MAKE_VERSION” 中找出符合“4.%”的字符(%为通配符), MAKE_VERSION 是 make 工具的版本号,ubuntu16.04 里面默认自带的 make 工具版本号为 4.1,之后的版本应该都是 4.1 及以上了, 我们可以在 ubuntu 中输入“make -v”查看。所以:
$(filter 4.%,$(MAKE_VERSION)) != " "这里条件成立。后续走(1)处的语句。
5.3 确定 quiet 的值
上面我们已经确认条件成立,所以接下来走的是 Makefile 这一部分的命令:
ifneq ($(filter 4.%,$(MAKE_VERSION)),) # make-4
ifneq ($(filter %s ,$(firstword x$(MAKEFLAGS))),)
quiet=silent_
endif
else # make-3.8x
# ......
endif这里也是一个判断语句
$(filter %s ,$(firstword x$(MAKEFLAGS))) != " " (空)如果上面的条件成立,变量 quiet 等于“silent_”。这里也用到了函数 filter:
$(firstword x$(MAKEFLAGS)))在上面这个函数中过滤出符合“%s”的单词。到了函数 firstword,函数 firstword 是获取首单词,函数格式如下:
$(firstword <text>)firstword 函数用于取出 text 字符串中的第一个单词,函数的返回值就是获取到的单词。当 使用“make -s”编译的时候,“-s”会作为 MAKEFLAGS 变量的一部分传递给 Makefile。在顶层 Makefile 的这段命令之后添加以下代码:
ifneq ($(filter 4.%,$(MAKE_VERSION)),) # make-4
#......
else # make-3.8x
# ......
endif
my_test:
@echo "first word="$(firstword x$(MAKEFLAGS))然后我们在源码目录执行以下命令,看一下这个到底输出的是啥:
sumu@sumu-virtual-machine:~/7Linux/imx6ull-uboot$ make -s ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- my_test -j16
first word=xrRs从输出可以看出第一个单词是“xrRs”,把下面的命令展开
$(filter %s ,$(firstword x$(MAKEFLAGS)))
# 展开就是
$(filter %s, xrRs)而$(filter %s, xrRs)的返回值肯定不为空,条件成立,所以 quiet = silent_。
6. 导出变量
上面关于命令输出的变量的值都已经确定完毕了,最后使用 export 命令导出,导出命令在这里:Makefile
export quiet Q KBUILD_VERBOSE使用 export 导出变量 quiet、Q 和 KBUILD_VERBOSE。
四、设置编译结果输出目录
1. 输出目录控制命令
uboot 可以将编译出来的目标文件输出到单独的目录中,在 make 的时候使用“O”来指定 输出目录。比如:
make O=out就是设置目标文件输出到 out 目录中。这么做是为了将源文件和编译产生的文件分开,当然也可以不指定 O 参数,不指定的话源文件和编译产生的文件都在 同一个目录内,一般在进行 uboot 编译的时候我们不指定 O 参数。在 Makefile 中实现设置输出目录的代码如下:
# ......
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
# That's 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),)2. O 参数来源判断
Makefile 文件判断 O 参数来源的代码如下:
ifeq ("$(origin O)", "command line")
KBUILD_OUTPUT := $(O)
endif行判断“O”是否来自于命令行,如果来自命令行的话条件成立,KBUILD_OUTPUT 就为$(O),因此变量 KBUILD_OUTPUT 就是输出目录。
3. 创建 KBUILD_OUTPUT 目录
还需要判断一下这个 KBUILD_OUTPUT 目录是否存在,不存在的话,会进行创建,Makefile 中对应的代码如下:
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)"))这里相当于将 KBUILD_OUTPUT 重新赋值了一下,从相对路径变为绝对路径。经过这里之后,一个不存在的输出目录就被创建好了。
五、代码检查
1. 代码检查控制命令
uboot 支持代码检查,命令如下:
make C=1 # 使能代码检查,检查那些需要重新编译的文件。
make C=2 # 用于检查所有的源码文件2. KBUILD_CHECKSRC
顶层 Makefile 中的代码如下:
# Call a source code checker (by default, "sparse") as part of the
# C compilation.
#
# Use 'make C=1' to enable checking of only re-compiled files.
# Use 'make C=2' to enable checking of *all* source files, regardless
# of whether they are re-compiled or not.
#
# See the file "doc/sparse.txt" for more details, including
# where to get the "sparse" utility.
ifeq ("$(origin C)", "command line")
KBUILD_CHECKSRC = $(C)
endif
ifndef KBUILD_CHECKSRC
KBUILD_CHECKSRC = 0
endif和前面一样,判断一下 C 是否来源于命令行,如果 C 来源于命令行,那就将 C 赋值给变量 KBUILD_CHECKSRC,如果命令行没有 C 的话 KBUILD_CHECKSRC 就为 0。
六、模块编译
1. 模块编译命令
在 uboot 中允许单独编译某个模块,使用以下命令:
make M=dir
make SUBDIRS=dir # 旧语法是这样的,也是支持的。2. KBUILD_EXTMOD
我们看一下 Makefile 中对这个参数的定义:
# Use make M=dir to specify directory of external module to build
# Old syntax make ... SUBDIRS=$PWD is still supported
# Setting the environment variable KBUILD_EXTMOD take precedence
ifdef SUBDIRS
KBUILD_EXTMOD ?= $(SUBDIRS)
endif
ifeq ("$(origin M)", "command line")
KBUILD_EXTMOD := $(M)
endif首先判断是否定义了 SUBDIRS , 如果定义了 SUBDIRS , 变量 KBUILD_EXTMOD = SUBDIRS,这里是为了支持老语法“make SUBIDRS = dir” 。
接着后面是判断是否在命令行定义了 M,如果定义了的话 KBUILD_EXTMOD =$(M)。
3. 编译目标
接着是根据上面的 KBUILD_EXTMOD 是否为空来确定编译目标,我们看一下 Makefile 文件:
# If building an external module we do not care about the all: rule
# but instead _all depend on modules
PHONY += all
ifeq ($(KBUILD_EXTMOD),)
_all: all
else
_all: modules
endif判断 KBUILD_EXTMOD 时为空,如果为空的话目标_all 依赖 all,因此要先编译出 all。否则的话默认目标_all 依赖 modules,要先编译出 modules,也就是编译模块。一般情况 下我们不会在 uboot 中编译模块,所以此处会编译 all 这个目标。
七、VPATH 的配置
看一下 Makefile 这一部分:
ifeq ($(KBUILD_SRC),)
# building in the source tree
srctree := .
else
ifeq ($(KBUILD_SRC)/,$(dir $(CURDIR)))
# building in a subdirectory of the source tree
srctree := ..
else
srctree := $(KBUILD_SRC)
endif
endif
objtree := .
src := $(srctree)
obj := $(objtree)
VPATH := $(srctree)$(if $(KBUILD_EXTMOD),:$(KBUILD_EXTMOD))
export srctree objtree VPATH第 1~11 行判断 KBUILD_SRC 是否为空,如果为空的话就设置变量 srctree 为当前目录,即 srctree 为“.”,一般不设置 KBUILD_SRC。
第 12 行设置变量 objtree 为当前目录。
第 13~14 行分别设置变量 src 和 obj,根据前面的变量的值,这里都为当前目录。
第 16 行设置 VPATH。 这里会 KBUILD_EXTMOD 是否为空,要是非空,这里就会加入 KBUILD_EXTMOD 路径。也就是要编译的模块的路径。
第 18 行导出变量 scrtree、objtree 和 VPATH。
八、编译平台
1. 主机架构 HOSTARCH
HOSTARCH 在 Makefile 中定义如下:
HOSTARCH := $(shell uname -m | \
sed -e s/i.86/x86/ \
-e s/sun4u/sparc64/ \
-e s/arm.*/arm/ \
-e s/sa110/arm/ \
-e s/ppc64/powerpc/ \
-e s/ppc/powerpc/ \
-e s/macppc/powerpc/\
-e s/sh.*/sh/)变量 HOSTARCH,用于保存主机架构,这里调用 shell 命令“uname -m”获取架构名称:
sumu@sumu-virtual-machine:~/7Linux/imx6ull-uboot$ uname -m
x86_64可以看出当前 ubuntu 主机架构为“x86_64”,shell 中的“|”表示管道,意思是将 左边的输出作为右边的输入,sed -e 是替换命令,“sed -e s/i.86/x86/”表示将管道输入的字符串 中的“i.86”替换为“x86”,其他的“sed -e s”命令同理。对于我现在运行的 ubuntu 系统而言,HOSTARCH = x86_64。
2. 主机操作系统 HOSTOS
HOSTOS 在 Makefile 中定义如下:
HOSTOS := $(shell uname -s | tr '[:upper:]' '[:lower:]' | \
sed -e 's/\(cygwin\).*/cygwin/')变量 HOSTOS,此变量用于保存主机 OS 的值,先使用 shell 命令“uname -s”来获取主机 OS,结果下所示:
sumu@sumu-virtual-machine:~/7Linux/imx6ull-uboot$ uname -s
Linux可以看出此时的主机 OS 为“Linux”,使用管道将“Linux”作为后面“ tr '[:upper:]' '[:lower:]' ”的输入,“ tr '[:upper:]' '[:lower:]' ”表示将所有的大写字母替换为小写字母,因此得到 “ linux ”。最后同样使用管道,将“ linux ”作为“ sed -e 's/(cygwin).*/cygwin/' ”的输入,用于将 cygwin.*替换为 cygwin。因此,现在在这里 HOSTOS = linux。
3. 导出 HOSTARCH 和 HOSTOS
接着导出这两个变量,在 Makefile 中定义如下:
export HOSTARCH HOSTOS4. 目标架构和编译器
4.1 设置命令
编译 uboot 的时候需要设置目标板架构和交叉编译器,我们一般在使用 make 命令的时候传入:
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf-这个命令就是用于设置 ARCH 和 CROSS_COMPILE。
4.2 ARCH 与 CROSS_COMPILE
在顶层 Makefile 中代码如下:
ifeq ($(HOSTARCH),$(ARCH))
CROSS_COMPILE ?=
endif这里判断 HOSTARCH 和 ARCH 这两个变量是否相等,前面我们知道主机架构(变量 HOSTARCH)是 x86_64,ARCH 就是通过 make 命令传入,要是没有传入,那 ARCH 就为空,我们编译的是 ARM 版本 uboot,肯定不相等,所以 CROSS_COMPILE = arm-linux-gnueabihf-。当然,要是我们在 ARM 开发板装了 GCC,这里就不需要前缀了,因为开发板上装的 GCC 一定就是 ARM 版本的,可以直接本地编译,不需要交叉编译。
可以看到,每次编译 uboot 的时候都要在 make 命令后面设置 ARCH 和 CROSS_COMPILE,使用起来很麻烦(当然,写 shell 脚本调用 Makefile 的除外),我们完全可以直接修改顶层 Makefile,在里面加入 ARCH 和 CROSS_COMPILE 的定义,如下所示:
ARCH ?= arm
CROSS_COMPILE ?= arm-linux-gnueabihf-这样直接在顶层 Makefile 里面定义 ARCH 和 CROSS_COMPILE,这样就不用每次编译的时候都要在 make 命令后面定义 ARCH 和 CROSS_COMPILE,直接一个 make 就可以了。
九、配置文件
KCONFIG_CONFIG ?= .config
export KCONFIG_CONFIGuboot 是可以配置的,这里设置配置文件为 .config。.config 文件默认是没有的,需要使用命令:
make xxx_defconfig对 uboot 进行配置,配置完成以后就会在 uboot 根目录下生成 .config。默认情况下.config 和 xxx_defconfig 内容是一样的,因为 .config 就是从 xxx_defconfig 复制过来的。如果后续自行调整 了 uboot 的一些配置参数,那么这些新的配置参数就添加到了.config 中,而不是 xxx_defconfig。 相当于 xxx_defconfig 只是一些初始配置,而 .config 里面的才是实时有效的配置。我们在图形配置界面进行的各种配置也会被写入到这个 .config 文件中。
十、通用定义
1. Kbuild.include
这里还会有一些通用的定义在 scripts/Kbuild.include 文件中:
####
# kbuild: Generic definitions
# Convenient variables
comma := ,
quote := "
squote := '
empty :=
space := $(empty) $(empty)
pound := \#
###
# Name of target with a '.' as filename prefix. foo/bar.o => foo/.bar.o
dot-target = $(dir $@).$(notdir $@)
###
# The temporary file to save gcc -MD generated dependencies must not
# contain a comma
depfile = $(subst $(comma),_,$(dot-target).d)2. 怎么被包含到 Makefile 中?
在 Makefile 中调用的代码:
# We need some generic definitions (do not try to remake the file).
scripts/Kbuild.include: ;
include scripts/Kbuild.include在 uboot 的编译过程中会用到 scripts/Kbuild.include 中的这些变量,后面用到的时候再分析。
十一、相关变量
1. 交叉编译工具变量
上面我们只是设置了 CROSS_COMPILE 的名字,但是交叉编译器其他的工具还没有设置,顶层 Makefile 中相关代码如下:
AS = $(CROSS_COMPILE)as
# Always use GNU ld
ifneq ($(shell $(CROSS_COMPILE)ld.bfd -v 2> /dev/null),)
LD = $(CROSS_COMPILE)ld.bfd
else
LD = $(CROSS_COMPILE)ld
endif
CC = $(CROSS_COMPILE)gcc
CPP = $(CC) -E
AR = $(CROSS_COMPILE)ar
NM = $(CROSS_COMPILE)nm
LDR = $(CROSS_COMPILE)ldr
STRIP = $(CROSS_COMPILE)strip
OBJCOPY = $(CROSS_COMPILE)objcopy
OBJDUMP = $(CROSS_COMPILE)objdump
LEX = flex
YACC = bison
AWK = awk
PERL = perl
PYTHON ?= python
PYTHON2 = python2
PYTHON3 = python3
DTC ?= $(objtree)/scripts/dtc/dtc2. 编译选项
我们编译的时候还有一些选项的定义,这些在顶层的 Makefile 中也有定义,例如这里就有一些宏和标志:
CHECKFLAGS := -D__linux__ -Dlinux -D__STDC__ -Dunix -D__unix__ \
-Wbitwise -Wno-return-void -D__CHECK_ENDIAN__ $(CF)
KBUILD_CPPFLAGS := -D__KERNEL__ -D__UBOOT__
KBUILD_CFLAGS := -Wall -Wstrict-prototypes \
-Wno-format-security \
-fno-builtin -ffreestanding $(CSTD_FLAG)
KBUILD_CFLAGS += -fshort-wchar -fno-strict-aliasing
KBUILD_AFLAGS := -D__ASSEMBLY__3. 变量导出
接下来在顶层 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 LEX YACC AWK PERL PYTHON PYTHON2 PYTHON3
export HOSTCXX HOSTCXXFLAGS CHECK CHECKFLAGS DTC DTC_FLAGS
export KBUILD_CPPFLAGS NOSTDINC_FLAGS UBOOTINCLUDE OBJCOPYFLAGS LDFLAGS
export KBUILD_CFLAGS KBUILD_AFLAGS
# When compiling out-of-tree modules, put MODVERDIR in the module
# tree rather than in the kernel tree. The kernel tree might
# even be read-only.
export MODVERDIR := $(if $(KBUILD_EXTMOD),$(firstword $(KBUILD_EXTMOD))/).tmp_versions
# Files to ignore in find ... statements
export RCS_FIND_IGNORE := \( -name SCCS -o -name BitKeeper -o -name .svn -o \
-name CVS -o -name .pc -o -name .hg -o -name .git \) \
-prune -o
export RCS_TAR_IGNORE := --exclude SCCS --exclude BitKeeper --exclude .svn \
--exclude CVS --exclude .pc --exclude .hg --exclude .git这些变量中大部分都已经在前面定义了,我们重点来看一下下面这几个变量:
ARCH CPU BOARD VENDOR SOC CPUDIR BOARDDIR这 7 个变量在顶层 Makefile 是找不到的,说明这 7 个变量是在其他文件里面定义的。先来看一下这 7 个变量都是什么内容,在顶层 Makefile 中导出变量结束的位置输入如图所示的内容:
# ......
export RCS_TAR_IGNORE := --exclude SCCS --exclude BitKeeper --exclude .svn \
--exclude CVS --exclude .pc --exclude .hg --exclude .git
my_test:
@echo "ARCH"=$(ARCH)
@echo "CPU"=$(CPU)
@echo "BOARD"=$(BOARD)
@echo "VENDOR"=$(VENDOR)
@echo "SOC"=$(SOC)
@echo "CPUDIR"=$(CPUDIR)
@echo "BOARDDIR"=$(BOARDDIR)然后执行以下命令看一下打印信息:
sumu@sumu-virtual-machine:~/7Linux/imx6ull-uboot$ make -s ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- my_test -j16
ARCH=arm
CPU=armv7
BOARD=mx6ullevk
VENDOR=freescale
SOC=mx6
CPUDIR=arch/arm/cpu/armv7
BOARDDIR=freescale/mx6ullevk可以看到这 7 个变量的值,这 7 个变量是从哪里来的呢?在 uboot 根目录下有个文件叫做 config.mk,这 7 个变量就是在 config.mk 里面定义的,打开 config.mk 内容如下:
# ......
# clear VENDOR for tcsh
VENDOR :=
#########################################################################
ARCH := $(CONFIG_SYS_ARCH:"%"=%)
CPU := $(CONFIG_SYS_CPU:"%"=%)
ifdef CONFIG_SPL_BUILD
ifdef CONFIG_TEGRA
CPU := arm720t
endif
endif
BOARD := $(CONFIG_SYS_BOARD:"%"=%)
ifneq ($(CONFIG_SYS_VENDOR),)
VENDOR := $(CONFIG_SYS_VENDOR:"%"=%)
endif
ifneq ($(CONFIG_SYS_SOC),)
SOC := $(CONFIG_SYS_SOC:"%"=%)
endif
# ......
CPUDIR=arch/$(ARCH)/cpu$(if $(CPU),/$(CPU),)
# ......
ifneq ($(BOARD),)
ifdef VENDOR
BOARDDIR = $(VENDOR)/$(BOARD)
else
BOARDDIR = $(BOARD)
endif
endif
# ......接下来需要找到 CONFIG_SYS_ARCH、 CONFIG_SYS_CPU、 CONFIG_SYS_BOARD、CONFIG_SYS_VENDOR 和 CONFIG_SYS_SOC 这 5 个变量的值。这 5 个变量在 uboot 根目录下的 .config 文件(这个文件需要执行配置命令后才会生成)中有定义,,可以打开看一下,搜索一下这个几个变量,会发现他们在 .config 文件中定义如下:
CONFIG_SYS_ARCH="arm"
CONFIG_SYS_CPU="armv7"
CONFIG_SYS_SOC="mx6"
CONFIG_SYS_VENDOR="freescale"
CONFIG_SYS_BOARD="mx6ull_alpha_emmc"
CONFIG_SYS_CONFIG_NAME="mx6ull_alpha_emmc"主要是这里我是用的自己移植的板子,所以这些名字什么都不太一样,不过用 nxp evk 评估板的默认配置文件的话会是对应的。所以就有:
ARCH = arm
CPU = armv7
BOARD = mx6ullevk
VENDOR = freescale
SOC = mx6
CPUDIR = arch/arm/cpu/armv7
BOARDDIR = freescale/mx6ullevk会发现和上面的打印信息是一样的。再来分析 config.mk ,就会发现在 config.mk 中读取的文件有 :
arch/arm/config.mk
arch/arm/cpu/armv7/config.mk
arch/arm/cpu/armv7/mx6/config.mk (此文件不存在)
board/ freescale/mx6ullevk/config.mk (此文件不存在)