uboot源码分析二 顶层makefile源码分析一

16 篇文章 5 订阅

在这里插入图片描述

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。

MAKEFLAGS += -rR --include-dir=$(CURDIR)

在这里插入图片描述
上述代码中先使用 ifeq 来判断"$(origin V)"和"command line"是否相等。这里用到了 Makefile中的函数 origin, origin 和其他的函数不一样,它不操作变量的值, origin 用于告诉你变量是哪来的,语法为:

$(origin )

variable 是变量名, o 函数的返回值就是变量来源,因此 ( o r i g i n V ) 就 是 变 量 V 的 来 源 。 如 果 变 量 V 是 在 命 令 行 定 义 的 那 么 它 的 来 源 是 " c o m m a n d l i n e " , 这 样 " (origin V)就是变量 V 的来源。如果变量 V 是在命令行定义的那么它的来源是"command line",这样" (originV)VV"commandline""(origin V)"和"commandline"就相等了。当这两个相等的时候变量 KBUILD_VERBOSE 就等于 V 的值,比如在命令行中输 入 “ V=1 “ 的 话 那 么 KBUILD_VERBOSE=1 。 如 果 没 有 在 命 令 行 输 入 V 的 话
KBUILD_VERBOSE=0。
第 80 行判断 KBUILD_VERBOSE 是否为 1,如果 KBUILD_VERBOSE 为 1 的话变量 quiet和 Q 都为空,如果 KBUILD_VERBOSE=0 的话变量 quiet 为“quiet_“,变量 Q 为“@” ,综上所述:
V=1 的话:
KBUILD_VERBOSE=1
quiet= 空 。
Q= 空。

V=0 或者命令行不定义 V 的话:
KBUILD_VERBOSE=0
quiet= quiet_。
Q= @。

Makefile 中会用到变量 quiet 和 Q 来控制编译的时候是否在终端输出完整的命令,在顶层
Makefile 中有很多如下所示的命令:

( Q ) (Q) (Q)(MAKE) $(build)=tools

如果 V=0 的话上述命令展开就是“@ make $(build)=tools”, make 在执行的时候默认会在终端输出命令,但是在命令前面加上“@”就不会在终端输出命令了。当 V=1 的时候 Q 就为空,上述命令就是“make $(build)=tools”,因此在 make 执行的过程,命令会被完整的输出在终端上。有些命令会有两个版本,比如:

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_”的话,整个命令都不会输出

静默输出

上一小节讲了,设置 V=0 或者在命令行中不定义 V 的话,编译 uboot 的时候终端中显示的短命令,但是还是会有命令输出,有时候我们在编译 uboot 的时候不需要输出命令,这个时候就可以使用 uboot 的静默输出功能。编译的时候使用“make -s”即可实现静默输出,顶层 Makefile中相应的代码如下
在这里插入图片描述
第 91 行判断当前正在使用的编译器版本号是否为 4.x,判断 ( f i l t e r 4. (filter 4.%, (filter4.(MAKE_VERSION))和“ ” (空)是否相等,如果不相等的话就成立,执行里面的语句。也就是说 ( f i l t e r 4. (filter4.%, (filter4.(MAKE_VERSION))不为空的话条件就成立,这里用到了 Makefile 中的 filter 函数,这是
个过滤函数,函数格式如下:

$(filter <pattern…>, )

filter 函数表示以 pattern 模式过滤 text 字符串中的单词,仅保留符合模式 pattern 的单词,可以有多个模式。函数返回值就是符合 pattern 的字符串。因此 ( f i l t e r 4. (filter 4.%, (filter4.(MAKE_VERSION))的含义就是在字符串“ MAKE_VERSION”中找出符合“ 4.%”的字符(%为通配符),
MAKE_VERSION 是编译器的版本号,我们当前使用的交叉编译器版本号为 4.9.4,所以肯定可以找出“4.%”。因此 ( f i l t e r 4. (filter 4.%, (filter4.(MAKE_VERSION))不为空,条件成立,执行 92~94 行的语句。第 92 行也是一个判断语句,如果 ( f i l t e r (filter %s , (filter(firstword x ( M A K E F L A G S ) ) ) 不 为 空 的 话 条 件 成 立 , 变 量 q u i e t 等 于 “ s i l e n t ” 。 这 里 也 用 到 了 函 数 f i l t e r , 在 (MAKEFLAGS)))不为空的话条件成立,变量 quiet 等于“silent_”。这里也用到了函数 filter,在 (MAKEFLAGS)))quietsilentfilter(firstword x$(MAKEFLAGS)))中过滤出符合“%s”的单词。到了函数 firstword,函数 firstword 是获取首单词,函数格式如下:

$(firstword )

firstword 函数用于取出 text 字符串中的第一个单词,函数的返回值就是获取到的单词。当使用“make -s”编译的时候,“-s”会作为 MAKEFLAGS 变量的一部分传递给 Makefile。
第 101 行 使用 export 导出变量 quiet、 Q 和 KBUILD_VERBOSE

设置编译结果输出目录

uboot 可以将编译出来的目标文件输出到单独的目录中,在 make 的时候使用“O”来指定输出目录,比如“make O=out”就是设置目标文件输出到 out 目录中。这么做是为了将源文件和编译产生的文件分开,当然也可以不指定 O 参数,不指定的话源文件和编译产生的文件都在同一个目录内,一般我们不指定 O 参数。
在这里插入图片描述

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),)
# We process the rest of the Makefile if this is the final invocation of make
ifeq ($(skip-makefile),)

第 124 行判断“O”是否来自于命令行,如果来自命令行的话条件成立, KBUILD_OUTPUT就为$(O),因此变量 KBUILD_OUTPUT 就是输出目录。
第 135 行判断 KBUILD_OUTPUT 是否为空。
第 139 行调用 mkdir 命令,创建 KBUILD_OUTPUT 目录,并且将创建成功以后的绝对路径赋值给 KBUILD_OUTPUT。至此,通过 O 指定的输出目录就存在了。

代码检查

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

模块编译
在 uboot 中允许单独编译某个模块,使用命令“ make M=dir”即可,旧语法“ makeSUBDIRS=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
# Make sure CDPATH settings don't interfere
unexport CDPATH


第 186 行 判 断 是 否 定 义 了 SUBDIRS , 如 果 定 义 了 SUBDIRS , 变 量KBUILD_EXTMOD=SUBDIRS,这里是为了支持老语法“make SUBIDRS=dir”
第 190 行判断是否在命令行定义了 M,如果定义了的话 KBUILD_EXTMOD=$(M)。
第 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。

获取主机架构和系统

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

uname -m 获取架构名称

shell 中的“|”表示管道,意思是将左边的输出作为右边的输入, sed -e 是替换命令,“sed -e s/i.86/x86/”表示将管道输入的字符串中的“i.86”替换为“x86”,其他的“sed -s”命令同理。对于我的电脑而言, HOSTARCH=x86_64。

“name -s”来获取主机 OS

使用管道将“Linux”作为后面“tr ‘[:upper:]’’[:lower:]’”的输入,“tr ‘[:upper:]’ ‘[:lower:]’”表示将所有的大写字母替换为小写字母,因此得到“linux”。最后同样使用管道,将“linux”作为“sed -e ‘s/(cygwin).*/cygwin/’”的输入,用于将 cygwin.*替换为 cygwin。因此, HOSTOS=linux。
第 240 行导出 HOSTARCH=x86_64, HOSTOS=linux。

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

编 译 uboot 的 时 候 需 要 设 置 目 标 板 架 构 和 交 叉 编 译 器 ,“ make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf-”就是用于设置 ARCH 和 CROSS_COMPILE,在顶层Makefile 中代码如下:
在这里插入图片描述
第 245 行判断 HOSTARCH 和 ARCH 这两个变量是否相等,主机架构(变量 HOSTARCH)是x86_64,而我们编译的是 ARM 版本 uboot,肯定不相等,所以 CROS_COMPILE= arm-linuxgnueabihf-。
直接在顶层 Makefile 里面定义 ARCH 和 CROSS_COMPILE,这样就不用每次编译的时候都要在 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 里面的才是实时有效的配置。

苹果的配置文件

HOSTCC       = cc
HOSTCXX      = c++
HOSTCFLAGS   = -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer
HOSTCXXFLAGS = -O2

ifeq ($(HOSTOS),cygwin)
HOSTCFLAGS	+= -ansi
endif

# Mac OS X / Darwin's C preprocessor is Apple specific.  It
# generates numerous errors and warnings.  We want to bypass it
# and use GNU C's cpp.	To do this we pass the -traditional-cpp
# option to the compiler.  Note that the -traditional-cpp flag
# DOES NOT have the same semantics as GNU C's flag, all it does
# is invoke the GNU preprocessor in stock ANSI/ISO C fashion.
#
# Apple's linker is similar, thanks to the new 2 stage linking
# multiple symbol definitions are treated as errors, hence the
# -multiply_defined suppress option to turn off this error.
#
ifeq ($(HOSTOS),darwin)
# get major and minor product version (e.g. '10' and '6' for Snow Leopard)
DARWIN_MAJOR_VERSION	= $(shell sw_vers -productVersion | cut -f 1 -d '.')
DARWIN_MINOR_VERSION	= $(shell sw_vers -productVersion | cut -f 2 -d '.')

os_x_before	= $(shell if [ $(DARWIN_MAJOR_VERSION) -le $(1) -a \
	$(DARWIN_MINOR_VERSION) -le $(2) ] ; then echo "$(3)"; else echo "$(4)"; fi ;)

# Snow Leopards build environment has no longer restrictions as described above
HOSTCC       = $(call os_x_before, 10, 5, "cc", "gcc")
HOSTCFLAGS  += $(call os_x_before, 10, 4, "-traditional-cpp")
HOSTLDFLAGS += $(call os_x_before, 10, 5, "-multiply_defined suppress")

# since Lion (10.7) ASLR is on by default, but we use linker generated lists
# in some host tools which is a problem then ... so disable ASLR for these
# tools
HOSTLDFLAGS += $(call os_x_before, 10, 7, "", "-Xlinker -no_pie")
endif

# Decide whether to build built-in, modular, or both.
# Normally, just do built-in.

KBUILD_MODULES :=
KBUILD_BUILTIN := 1

# If we have only "make modules", don't compile built-in objects.
# When we're building modules with modversions, we need to consider
# the built-in objects during the descend as well, in order to
# make sure the checksums are up to date before we record them.

ifeq ($(MAKECMDGOALS),modules)
  KBUILD_BUILTIN := $(if $(CONFIG_MODVERSIONS),1)
endif

# If we have "make <whatever> modules", compile modules
# in addition to whatever we do anyway.
# Just "make" or "make all" shall build modules as well

# U-Boot does not need modules
#ifneq ($(filter all _all modules,$(MAKECMDGOALS)),)
#  KBUILD_MODULES := 1
#endif

#ifeq ($(MAKECMDGOALS),)
#  KBUILD_MODULES := 1
#endif
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值