U-Boot顶层Makefile分析

顶层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过程会放在下一篇文章分析,请大家多多指教,点赞评论。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值