顶层Makefile也就是uboot根目录下的Makefile文件,由于顶层Makefile文件内容比较多,所以我们将其分开来看。本文主要参考正点原子。
一、基础信息
1.1版本号
VERSION = 2016
PATCHLEVEL = 03
SUBLEVEL =
EXTRAVERSION =
NAME =
VERSION是主版本号,PATCHLEVEL是补丁版本号,SUBLEVEL是次版本号,这三个一 起构成了uboot的版本号,比如当前的uboot版本号就是“2016.03”。EXTRAVERSION是附加版本信息,NAME是和名字有关的,一般不使用这两个。
1.2export和unexport
make是支持递归调用的,也就是在 Makefile中使用“make”命令来执行其他的Makefile文件,一般都是子目录中的Makefile文件。有时候我们需要向子make传递变量,这个时候使用“export”来导出要传递给子make的变量即可,如果不希望哪个变量传递给子make的话就使用“unexport”来声明不导出 :
export VARIABLE //导出变量给子 make 。
unexport VARIABLE //不导出变量给子 make。
1.3命令输出
uboot默认编译是不会在终端中显示完整的命令,都是短命令。在终端中输出短命令虽然看起来很清爽,但是不利于分析 uboot的编译过程。可以通过在命令行设置变量“V=1“来实现完整的命令输出,这个在调试 uboot的时候很有用。
make V=1
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
origin函数的返回值就是变量来源,因此 $(origin V)就是变量 V的来源。如果变量 V是在命令行定义的,那么它的来源就是 "command line",这样 "$(origin V)"和 "command line"就相等了。当这两个相等的时候,KBUILD_VERBOSE就等于 V的值,比如在命令行中输入“ V=1“的话那么 KBUILD_VERBOSE=1。如果没有在命令行输入 V的话,也就是"$(origin V)"和 "command line"不相等,KBUILD_VERBOSE=0。
上面代码的下半部分就是对 KBUILD_VERBOSE的值进行判断,进而对变量quiet和Q进行赋值。
Makefile中会用到变量 quiet和Q来控制编译的时候是否在终端输出完整的命令。make在执行的时候默认会在终端输出命令,但是在命令前面加上“ “@”就不会在终端输出命令了。
如果变量 quiet为空的话,整个命令都会输出;如果变量 quiet为“ quiet_”的话,仅输出短版本;
如果变量 quiet为“ silent_”的话,整个命令都不会输出。
因此总结起来可以通过在命令行设置V=1,进而给quiet和Q赋值,最终来控制命令输出。
1.4静默输出、
设置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
$(filter <pattern...>,<text>)
首先通过make -v查看make的版本,filter函数表示以pattern模式过滤 text字符串中的单词,仅保留符合模式 pattern的单词,可以有多个模式。函数返回值就是符合pattern的字符串。因此$(filter 4.%,$(MAKE_VERSION))不为空,所以继续执行
当使用“ make -s”编译的时候 ,“-s”会作为 MAKEFLAGS变量的一部分传递给 Makefile,firstword函数会找出变量的第一个单词,因此最后通过filter函数后会找出第一个且含有s的单词且不为空,所以quiet=silent_,整个命令不会输出。
1.5指定编译结果输出目录
uboot可以将编译出来的目标文件输出到单独的目录中,在make的时候使用“ O”来指定输出目录,比如“ make O=out”就是设置目标文件输出到out目录中。这么做是为了将源文件和编译产生的文件分开,当然也可以不指定O参数,不指定的话源文件和编译产生的文件都在同一个目录内,一般我们不指定O参数。代码分析起来如前面一样,这里不多叙述。要注意的是在这里出现了Makefile的默认编译目标_all,_all目前没有依赖,在后面会设置。
# 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 O=dir/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_OUTPUT=dir/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
# 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),)
1.6代码检查
uboot支持代码检查,使用命令“ make C=1”使能代码检查,检查那些需要重新编译的文件。“ make C=2”用于检查所有的源码文件,这里代码也和前面一样分析,不多叙述。
# 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 "Documentation/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
1.7模块编译
在uboot中允许单独编译某个模块,使用命令“ make M=dir”即可,旧语法 make SUBDIRS=dir”也是支持的。
# 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
# 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
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
如果我们不单独编译某个模块,即KBUILD_EXTMOD为空,目标 _all依赖 all,因此要先编译
出 all。否则的话默认目标 _all依赖 modules,要先编译出modules。此外注意KBUILD_SRC在1.5和本节都有出现,但我们一般不设置此值,即为空。接下来的代码为多个变量赋当前目录的值。
1.8获取主机架构和系统
接下来顶层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/)
HOSTOS := $(shell uname -s | tr '[:upper:]' '[:lower:]' | \
sed -e 's/\(cygwin\).*/cygwin/')
export HOSTARCH HOSTOS
1.9设置目标架构、交叉编译器和配置文件
#!/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 V=1 ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- -j12
# set default to nothing for native builds
ifeq ($(HOSTARCH),$(ARCH))
CROSS_COMPILE ?=
endif
KCONFIG_CONFIG ?= .config
export KCONFIG_CONFIG
在源码中,判断HOSTARCH和 ARCH这两个变量是否相等,主机架构 (变量 HOSTARCH)是
x86_64,而我们编译的是 ARM版本uboot,肯定不相等。我们在编译的时候,就得像第一个代码块那样,加上架构和交叉编译器的名字。但为了方便,我们可以直接在Makefile上添加这两个变量:
ARCH ?=arm
CROSS_COMPILE ?= arm-linux-gnueabihf-
接着往下看,uboot是可以配置的,这里设置配置文件为 .config,.config默认是没有的,需要使用命令“ make xxx_defconfig对 uboot进行配置,配置完成以后就会在 uboot根目录下生成 .config。默认情况下 .config和xxx_defconfig内容是一样的,因为 .config就是从 xxx_defconfig复制过来的。
1.10调用 scripts/Kbuild.include
主 Makefile会调用文件 scripts/Kbuild.include这个文件,在 uboot的编译过程中会用到 scripts/Kbuild.include中的这些变量。
scripts/Kbuild.include: ;
include scripts/Kbuild.include
1.11交叉编译工具变量设置
交叉编译器其他工具设置:
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
因篇幅原因,重点make xxx_defconfig过程以及make过程会放在下一篇文章分析,请大家多多指教,点赞评论。