海思3559:uboot顶层Makefile分析

顶层Makefile的内容主要结构为:

  1. 确定版本号及主机信息
  2. 实现静默编译功能
  3. 设置各种路径
  4. 设置编译工具链
  5. 设置规则
  6. 设置与cpu相关的伪目标
    需要注意的是,结构顺序并不代表代码执行顺序。

1.确定版本号及主机信息

VERSION = 2016
PATCHLEVEL = 11
SUBLEVEL =
EXTRAVERSION =
NAME =
version_h := include/generated/version_autogenerated.h
timestamp_h := include/generated/timestamp_autogenerated.h
UBOOTVERSION = $(VERSION)$(if $(PATCHLEVEL),.$(PATCHLEVEL)$(if $(SUBLEVEL),.$(SUBLEVEL)))$(EXTRAVERSION)

  最开始四个变量的含义依次为:主版本号、次版本号、再次版本号、附加的版本信息(值为空,可以给用户使用)
timestamp_autogenerated.h 这个文件是在make之后自动生成的,文件内容是一条宏,这条宏给将其他.c文件提供uboot的编译时间
  version_autogenerated.h这个文件是在make之后自动生成的,文件内容是一条宏,这条宏给将其他.c文件提供uboot的版本号
  UBOOTVERSION 为最终生成的uboot版本
  验证方法:自己修改主Makefile中几个Version有关的变量,然后重新编译uboot,然后烧录到SD卡中,从SD卡启动,然后去看启动时uboot打印出来的版本信息,看看变化是不是和自己的分析一致。

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这个名字:HOST是主机;ARCH是architecture(架构)的缩写,表示CPU的架构。所以HOSTARCH就表示主机的CPU的架构
  makefile的函数调用与变量调用很类似,格式是$ ( function arguments),其实上面一大段的意思是变量HOSTARCH的值是一个函数的返回值shell是makefile中的一个函数,$ (shell XXX)会被解析成执行shell命令XXX;此处是执行了一条 uname -m |sed -e ……,符号 \是makefile的换行符其中,|是shell语法中的管道结构,例如:XXX | YYY ,表达式XXX 的输出将作为表达式YYY的输入,YYY的输出才是整句表达式的输出uname -m 指令将输出负责编译的主机cpu架构,比如ixx86;sed -e是替换命令,比如把ixx86替换为x86,由此可见这个HOSTARCH变量的值将得到负责编译的主机cpu架构。大部分情况下我们得到的都是x86


HOSTOS := $(shell uname -s | tr '[:upper:]' '[:lower:]' | \
	    sed -e 's/\(cygwin\).*/cygwin/')

  这个HOSTOS变量和上一句HOSTARCH变量的原理类似,管道第一部分uname -s会得到负责编译的主机的OS,比如Linux
  管道第二部分是将大写转换成小写
  管道第三部分的意思是如果前面一个部分得到了cygwin系统,则格式要转换一下。不必深究,因为cygwin基本没人用……
  由此可见这个HOSTOS变量的值将得到负责编译的主机操作系统,大部分情况下我们得到的都是linux

CONFIG_SHELL := $(shell if [ -x "$$BASH" ]; then echo $$BASH; \
	  else if [ -x /bin/bash ]; then echo /bin/bash; \
	  else echo sh; fi ; fi)
export	HOSTARCH HOSTOS

  SHELL定义使用哪种sh解释器来解释脚本,BASH的值为/bin/sh也可能为,整个语句的意思是判断/bin/sh是否可执行
  如果可执行将/bin/sh 赋值给变量SHELL
  如果不可执行,进一步判断/bin/bash的可执行性
  如果/bin/sh和/bin/bash都不可执行,则直接将sh赋值给SHELL变量

  export HOSTARCH HOSTOS

  导出上面变量到全局,使其为环境变量,让其他的文件也可以使用架构和系统信息

2.实现静默编译功能

  平时默认编译时命令行会打印出来很多编译信息。但是有时候我们不希望看到这些编译信息,就后台编译即可。这就叫静默编译。

# If it is set to "quiet_", only the short version will be printed.
# If it is set to "silent_", nothing will be printed at all, since

  使用方法就是编译时make -s,-s会作为MAKEFLAGS传给Makefile,老版本uboot作用下XECHO变量就会被变成空(默认等于echo),于是实现了静默编译。

# 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

export quiet Q KBUILD_VERBOSE

  此外,相关设置也需要kbuild值来决定是否开启,我理解只有等于1时才会生效,相关注释是这样说的

# Normally, we echo the whole command before executing it. By making
# that echo $($(quiet)$(cmd)), we now have the possibility to set
# $(quiet) to choose other forms of output instead, e.g.
#
#         quiet_cmd_cc_o_c = Compiling $(RELDIR)/$@
#         cmd_cc_o_c       = $(CC) $(c_flags) -c -o $@ $<
#
# If $(quiet) is empty, the whole command will be printed.
# If it is set to "quiet_", only the short version will be printed.
# If it is set to "silent_", nothing will be printed at all, since
# the variable $(silent_cmd_cc_o_c) doesn't exist.
#
# A simple variant is to prefix commands with $(Q) - that's useful
# for commands that shall be hidden in non-verbose mode.
#
#	$(Q)ln $@ :<
#
# If KBUILD_VERBOSE equals 0 then the above command will be hidden.
# If KBUILD_VERBOSE equals 1 then the above command is displayed.
#
# To put more focus on warnings, be less verbose as default
# Use 'make V=1' to see the full commands

3.设置各种路径

两种编译方法

  (1)编译复杂项目,Makefile提供2种编译管理方法。默认情况下是当前文件夹中的.c文件,编译出来的.o文件会放在同一文件夹下。这种方式叫原地编译。原地编译的好处就是处理起来简单。
  (2)原地编译有一些坏处:第一,污染了源文件目录。第二的缺陷就是一套源代码只能按照一种配置和编译方法进行处理,无法同时维护2个或2个以上的配置编译方式。
  (假如这套源代码有2种配置,那么编译其中一种配置后,要想编译另一种配置需要先make distclean清除第一种的编译生成文件才可以进行。)
  (3)为了解决以上2种缺陷,uboot支持单独输出文件夹方式的编译(linux kernel也支持,而且uboot的这种技术就是从linux kernel偷师而来)。基本思路就是在编译时另外指定一个输出目录,将来所有的编译生成的.o文件或生成的其他文件全部丢到那个输出目录下去。源代码目录不做任何污染,这样输出目录就只是承载了本次配置编译的所有结果。
  (4)具体用法:默认的就是原地编译。如果需要指定具体的输出目录编译则有2种方式来指定输出目录。(具体参考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)

  第一种:make O=输出目录
  第二种:export BUILD_DIR=输出目录 然后再make
  如果两个都指定了(既有BUILD_DIR环境变量存在,又有O=xx),则O=xx具有更高优先级,会覆盖方法二。
  而且方法一必须每次输入make时都要输入参数(不论是make clean还是make config 的时候),格式如make O=/tmp/build disclean

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

  这句是shell语法中简写的if表达式,其作用是当输出文件夹路径不存在时就创建它
  其中包含两个表达式,表达式1||表达式2,当表达式1为真时,表达式2不会被解释器执行,因为总结果一定为真,(尽管总的结果没有意义)。唯有表达式1为假时,解释器才会去执行表达式2,这样就能用逻辑表达式来实现if语句的功能了
  表达式1中,方括号是固定用法,是为了突出里面表达式的作用是判断语句。-p是shell中判断路径是否存在的符号,如果路径存在则为表达式1为真;如果路径不存在,表达式1为假,解释器会执行表达式2,即创建输出文件夹路径。
  这整个一段功能是确保输出文件夹路径创建成功,&&的功能是连续执行两句语句,当cd $(BUILD_DIR)执行完后,执行/bin/pwd,即打印当前路径;
$(if xxx,yyy,zzz)是makefile的判断函数,如果xxx为真,则执行yyy并返回值,否则执行zzz并返回值,由此可知如果未成功创建BUILD_DIR,就会输出错误打印信息;

objtree		:= .
src		:= $(srctree)
obj		:= $(objtree)

  (1)OBJTREE:编译出的.o文件存放的目录的根目录。在默认编译下,OBJTREE等于当前目录;在O=xx编译下,OBJTREE就等于我们设置的那个输出目录。
  (2)SRCTREE: 源码目录,其实就是源代码的根目录,也就是当前目录。
  总结:在默认编译下,OBJTREE和SRCTREE相等;在O=xx这种编译下OBJTREE和SRCTREE不相等。Makefile中定义这两个变量,其实就是为了记录编译后的.o文件往哪里放,就是为了实现O=xx的这种编译方式的。

猜你喜欢

转载自blog.csdn.net/qq_42330920/article/details/127155690