Linux系统移植:U-Boot 顶层 Makefile 分析(上)

Posted JeckXu666

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Linux系统移植:U-Boot 顶层 Makefile 分析(上)相关的知识,希望对你有一定的参考价值。

Linux系统移植:U-Boot 顶层 Makefile 分析(上)

一、版本号

打开 Makefile 可以在顶层看到他的版本信息

VERSION 是主版本号,PATCHLEVEL 是补丁版本号,SUBLEVEL 是次版本号,这三个一起构成了 uboot 的版本号

EXTRAVERSION 是附加版本信息,NAME 是和名字有关的,一般不使用这两个

二、传递变量到子 make

主目录的 Makefile 文件可以调用子目录中的 Makefile 文件,进行编译,主目录调用方式如下:

$(MAKE) -C subdir

$(MAKE) 就是调用 “make” 命令,-C 指定子目录

主 Makefile 可以使用 “export” 来导出要传递给子 make 的变量,使用 “unexport” 来声明不导出

export VARIABLE ……	//导出变量给子 make
unexport VARIABLE……	//不导出变量给子 make

“SHELL” 和 “MAKEFLAGS”,这两个变量除非使用 “unexport” 声明,否则的话在整个make的执行过程中,它们的值 始终自动的传递给子 make,我们可以在代码中看到 MAKEFLAGS

该代码使用 “+=” 来给变量 MAKEFLAGS 追加了一些值“-rR”表示禁止使用内置的隐含规则和变量定义“–include-dir”指明搜索路径,”$(CURDIR)”表示当前目录

三、命令输出

uboot 编译时会打印许多数据信息,内容较多,不利于分析 uboot 的编译过程,所以默认编译是不会在终端中显示完整的命令,具体通过设置变量 “V=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 = @
endif

其中 ifeq 判断 $(origin V) 和 command line 是否相等,相等则执行下面的语句

其中 origin 用于告诉你变量是哪来的,语法为:

$(origin <variable>)

上面的判断就是判断变量 V 的定义是来自 command line ,也就是命令行,然后执行变量 KBUILD_VERBOSE 等于 V 的值,如果没有在命令行输入 V 的话 KBUILD_VERBOSE 就等于 0 !

第二个 ifeq 用来判断 KBUILD_VERBOSE 是否为 1,如果 KBUILD_VERBOSE 为 1 的话变量 quiet ,和 Q 都为空,如果 KBUILD_VERBOSE=0 的话变量 quiet 为 “quiet_“,变量 Q 为 “@” !

变量 quiet 和 Q 来控制编译的时候是否在终端输出完整的命令,比如如下一段代码

Q = @ 时就不会执行后面的命令,不会在终端上输出命令,而有些命令会有多个版本,使用 quiet 进行控制,如下:

quiet_cmd_sym ?= SYM $@
cmd_sym ?= $(OBJDUMP) -t $< > $@

如果变量 quiet 为空的话,整个命令都会输出

如果变量 quiet 为 “quiet_” 的话,仅输出短版本

如果变量 quiet 为 “silent_” 的话,整个命令都不会输出

四、静默输出

设置 V=0 或者在命令行中不定义 V 的话,编译 uboot 的时候终端中显示的短命令,但是还是会有命令输出,有时候我们在编译 uboot 的时候不需要输出命令,这个时候就可以使用 uboot 的静默输出功能,我们编译的时候用 “make -s” 即可实现静默输出

比如,我在 uboot 的编译脚本使用 -s 对 uboot 进行编译,编译时将进行静默输出,命令行只显示部分内容

在顶层 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
endif

其中 filter 函数是个过滤函数,函数格式如下:

$(filter <pattern...>,<text>)

表示以 pattern 模式过滤 text 字符串中的单词,仅保留符合模式 pattern 的单词,可以有多个模式。函数返回值就是符合 pattern 的字符串

比如 $(filter 4.%,$(MAKE_VERSION)) 的含义就是在字符串“MAKE_VERSION”中找出符合“ 4.%”的字符 (%为通配符)

五、编译输出目录

Makefile 可以将编译出来的目标文件输出到单独的目录中,在 make 的时候使用 “O” 来指定输出目录,比如 “make O=dir” 就是设置目标文件输出到 dir 目录中,通过生成到指定的目录,可以使编译结果更加简明,如果不指定 O 参数,源文件和编译产生的文件都在同一个目录内

Makefile 中相关的代码如下:

具体代码如下:

# 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),)

# We process the rest of the Makefile if this is the final invocation of make
ifeq ($(skip-makefile),)

# Do not print "Entering directory ...",
# but we want to display it when entering to the output directory
# so that IDEs/editors are able to understand relative filenames.
MAKEFLAGS += --no-print-directory

代码判断 “O” 是否来自于命令行,来自命令行就将 KBUILD_OUTPUT 等于 $(O) ,然后创建 KBUILD_OUTPUT 对应的文件夹

六、代码检查

uboot 的 Makefile 支持代码检查,使用命令 “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

此处代码,如果 C 来源于命令行,那就将 C 赋值给变量 KBUILD_CHECKSRC,如果命令行没有 C 的话 KBUILD_CHECKSRC 就为 0,代码会根据他的值进行判断执行不同程序

七、模块编译

uboot 的 Makefile 支持单独编译一个模块,使用命令 “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

代码还是老样子,判断 M 来自命令行,然后赋值给 KBUILD_EXTMOD ,之后判断 KBUILD_EXTMOD 是否为空,如果为空的话目标 _all 依赖 all,先编译出 all,否则的话默认目标 _all 依赖 modules,先编译出 modules,也就是编译模块

之后判断 KBUILD_SRC 是否为空,如果为空的话就设置变量 srctree 为当前目录,即 srctree 为 “.”,一般不设置 KBUILD_SRC,之后将前两个变量关联到 VPATH 变量,然后导出 scrtree、objtree 和 VPATH

八、获取主机架构和系统

顶层 Makefile 会也会获取主机架构和系统

第一部分代码调用 shell 命令 “uname -m” 获取架构名称,同时 shell 中的 “|” 表示管道,意思是将左边的输出作为右边的输入,sed -e 是替换命令,“sed -e s/i.86/x86/” 表示将管道输入的字符串中的 “i.86” 替换为 “x86”,其他的“sed -e s”命令同理

第二部分使用 shell 命令“uname-s”来获取主机 OS,然后替换保存

第三部分代码导出 HOSTARCH、HOSTOS

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

编译 uboot 的时要设置目标板架构和交叉编译器,配置代码如下:

编译时使用脚本需要加入 make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- 就是用于设置 ARCH 和 CROSS_COMPILE

然后 Makefile 代码中判断 HOSTARCH 和 ARCH 这两个变量是否相等,不相等就 CROSS_COMPILE= arm-linux-gnueabihf- 使用交叉编译器

当然默认每次编译 uboot 的时候都要在 make 命令后面设置 ARCH 和 CROSS_COMPILE,使用起来很麻烦,我们可以直接在 Makefile 内加入 两者的定义,方便我们开发

后面的代码定义变量 KCONFIG_CONFIG,因为 uboot 是可以配置的,这里配置文件为.config,.config 默认是没有的,需要使用命令 “make xxx_defconfig” 对 uboot 进行配置,配置完成以后就会在 uboot 根目录下生成 .config。默认情况下 .config 和 xxx_defconfig 内容是一样的,因为.config 就是从 xxx_defconfig 复制过来的。如果后续自行调整了 uboot 的一些配置参数,那么这些新的配置参数就添加到了.config 中,而不是 xxx_defconfig。相当于 xxx_defconfig 只是一些初始配置,而.config 里面的才是实时有效的配置

以上是关于Linux系统移植:U-Boot 顶层 Makefile 分析(上)的主要内容,如果未能解决你的问题,请参考以下文章

Linux系统移植:U-Boot 工程创建

U-BOOT-2016.07移植 (第一篇) 初步分析

Linux系统移植:正点原子 U-Boot 移植

04.移植u-boot

Linux系统移植:U-Boot 链接脚本

Linux系统移植:NXP 官板 uboot 移植