Melis学习笔记(二):顶层Makefile分析

本文详细分析了Melis系统顶层Makefile的结构和功能,包括版本号、MAKEFLAGS变量、命令输出控制、编译结果输出目录设置、模块编译、系统架构获取、目标架构和编译器配置等。还介绍了Makefile中的赋值语句(=, :=, ?=, +=)以及$@、$^和$<的含义和用法。" 46413567,1435363,cocos2d-js 使用ttf字体全面指南,"['cocos2d', '游戏开发', 'JavaScript', '字体渲染', '资源管理']

参考自:《uboot顶层Makefile》
    《uboot学习笔记2-顶层Makefile分析(一)》
    《Makefile中:=, =, ?=和+=的含义》
    《Makefile入门二、理解$@、$^和$<》

中文文档:《跟我一起写Makefile》

Melis使用的是kbuild编译系统,在编译可加载模块时,其makefile的风格和常用的编译C程序的makefile有所不同,尽管如此,makefile的作用总归是给编译器提供编译信息。

Makefile可以嵌套,也就是顶层 Makefile 可以调用子目录中的 Makefile 文件。 Makefile 嵌套在大项目中很常见,一般大项目里面所有的源代码都不会放到同一个目录中,各个功能模块的源代码都是分开的,各自存放在各自的目录中。每个功能模块目录下都有一个 Makefile,这个 Makefile 只处理本模块的编译链接工作,这样所有的编译链接工作就不用全部放到一个 Makefile 中,可以使得 Makefile 变得简洁明了。

该文对Melis系统顶层Makefile进行了详细介绍,并简要说明其子目录Makefile的编写规则。

一、顶层Makefile

因为Melis顶层Makefile有很多注释,所以截图分段来看。(备注:Melis顶层Makefile参考uboot源文件中的Makefile)

top Makefile 整体框架

当我们执行 make 编译的时候,我们将整个 top Makefile的执行分成以下的逻辑部分:

  1. 配置(make *config):对内核中所有模块进行配置,以确定哪些模块需要编译进内核,哪些编译成外部模块,哪些不进行处理,同时生成一些配置相关的文件供内核真正编译文件时调用。
  2. 内核编译(make):根据配置部分,真正的编译整个内核源码,其中包含递归地进入各级子目录进行编译。
  3. 通过命令行传入 O=DIR 进行编译 : 所有编译过程生成的中间文件以及目标文件放置在指定的目录,保持源代码的干净。
  4. 通过命令行传入多个编译目标,比如:make clean all,make config all,等等,一次指定多个目标。
  5. 通过命令行传入 M=DIR 进行编译:指定编译某个目录下的模块,并将其编译成外部模块。
  6. 指定一些完全独立的目标,与内核真实的编译过程并不强相关,比如:make clean(清除编译结果),make kernelrelease(获取编译后的发型版本号),make headers_(内核头文件相关)。

Makefile里主要包含了五个东西:显式规则、隐晦规则、变量定义、文件指示和注释。

  • 显式规则。显式规则说明了如何生成一个或多个目标文件。这是由Makefile的书写者明显指出要生成的文件、文件的依赖文件和生成的命令。
  • 隐晦规则。由于我们的make有自动推导的功能,所以隐晦的规则可以让我们比较简略地书写 Makefile,这是由make所支持的。
  • 变量的定义。在Makefile中我们要定义一系列的变量,变量一般都是字符串,这个有点像你C语言中的宏,当Makefile被执行时,其中的变量都会被扩展到相应的引用位置上。
  • 文件指示。其包括了三个部分,一个是在一个Makefile中引用另一个Makefile,就像C语言中的include一样;另一个是指根据某些情况指定Makefile中的有效部分,就像C语言中的预编译#if一样;还有就是定义一个多行的命令。有关这一部分的内容,我会在后续的部分中讲述。
  • 注释。Makefile中只有行注释,和UNIX的Shell脚本一样,其注释是用 # 字符,这个就像C/C++中的 // 一样。如果你要在你的Makefile中使用 # 字符,可以用反斜杠进行转义,如: # 。

1、版本号

VERSION = 4          # 主版本号
PATCHLEVEL = 0       # 补丁版本号
SUBLEVEL = 0         # 次版本号
EXTRAVERSION =       # 附件版本信息
NAME = MELIS SDK     # 工程名

2、MAKEFLAGS变量

make 是支持递归调用的,也就是在 Makefile 中使用“make”命令来执行其他的 Makefile文件,一般都是子目录中的 Makefile 文件。假如在当前目录下存在一个“subdir”子目录,这个子目录中又有其对应的 Makefile 文件,那么这个工程在编译的时候其主目录中的 Makefile 就可以调用子目录中的 Makefile,以此来完成所有子目录的编译。主目录的 Makefile 可以使用如下代码来编译这个子目录:

$(MAKE) -C subdir

$(MAKE)就是调用“make”命令, -C 指定子目录,subdir就是子目录,有时候需要将变量导入子目录,export就是导入,unxeport就是不导入,需要注意的是,“SHELL”和“MAKEFLAGS”,这两个变量除非使用“unexport”声明,否则的话在整个make的执行过程中,它们的值始终自动的传递给子make。

# o Do not use make's built-in rules and variables
#   (this increases performance and avoids hard-to-debug behaviour);
# o Look for make include files relative to root of kernel src
MAKEFLAGS += -rR --include-dir=$(CURDIR)
# 使用+=来给MAKEFLAGS增加了一些变量
# -rR 的意思是禁止使用内置的隐含规则和变量定义
# --include-dir 指明搜索路径,CURDIR就是当前路径

# Avoid funny character set dependencies
unexport LC_ALL  # 不导入
LC_COLLATE=C
LC_NUMERIC=C
export LC_COLLATE LC_NUMERIC  #导入上面两个变量

# Avoid interference with shell env settings
unexport GREP_OPTIONS  # 不导入

3、命令输出

Melis 默认编译是不会在终端中显示完整的命令,都是短命令。

在这里插入图片描述
在终端中输出短命令虽然看起来很清爽,但是不利于分析 Melis 的编译过程。可以通过设置变量“V=1“来实现完整的命令输出。

# Beautify output
# ---------------------------------------------------------------------------
#
# 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

ifeq ("$(origin V)", "command line")  # 判断变量V的来源,如果来源命令行
  KBUILD_VERBOSE = $(V)  # KBUILD_VERBOSE就是V的值,为1
endif
ifndef KBUILD_VERBOSE
  KBUILD_VERBOSE = 0  # 没定义就是0
endif

#用变量 quiet 和 Q 来控制编译的时候是否在终端输出完整的命令
ifeq ($(KBUILD_VERBOSE),1)  # 由上面得知,KBUILD_VERBOSE如果等于1,V=1就进入下面
  quiet =
  Q =
else
  quiet=quiet_
  Q = @
endif

(origin V)和"command line"是否相等,这里使用到了origin函数,其语法是(origin )。origin函数不操作变量的值,只是告诉你变量是哪里来的,$(origin V)就是告诉变量V的来源,也就是判断变量V是否来自命令行定义,origin函数返回值有下面几种:

  • undefine:从来没有定义过
  • default:如果是一个默认的定义,比如“CC”这个变量,这种变量我们将在后面讲述。“environment” 如果是一个环境变量, 并且当 Makefile 被执行时, “-e”参数没有被打开。
  • file:变量被定义在Makefile中
  • command line:在命令行定义
  • override:变量被override指示符重新定义
  • automatic:是命令运行中的自动化变量

在Makefile文件中有许多下面的

$(Q)$(MAKE) $(build)=tool

如果V = 0, Q=@,就不会在中断输出命令了;否则就会输出命令。

有些命令会有两个版本,比如:

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

4、静默输出

由上一节可知,如果V=1就输出很多命令,如果V不等于0就会短命令输出,有时候不需要输出命令,这时候就是静默输出。编译的时候使用make -s即可。

# If the user is running make -s (silent mode), suppress echoing of
# commands

ifneq ($(filter 4.%,$(MAKE_VERSION)),)	# make-4.x版本,filter是过滤的,在变量中仅保留符合4.%的词
ifneq ($(filter %s ,$(firstword x$(MAKEFLAGS))),)  # 是否静默输出
  quiet=silent_
endif
else					# make-3.8x版本
ifneq ($(filter s% -s%,$(MAKEFLAGS)),)  # 如果加入-s 那条件成立,静默输出
  quiet=silent_
endif
endif

ifeq ($(MELIS_BASE),)  #初始化Melis环境变量
  $(error Please execute 'source melis-env.sh' first.)
endif

export quiet Q KBUILD_VERBOSE  # 导出这三个输出结果给其他子makefile

上面用到了Makefile中的filter和firstword函数,先来介绍一下这两个函数。

原型: $(filter ,)
名称: 过滤函数
功能: 以pattern模式过滤text字符串中的单词,保留符合模式pattern的单词,可以有多个模式。
返回: 返回符合pattern模式的字串

原型:(firstword <text>)
名称: 首单词函数
功能: 取字符串<text>中的第一个单词。
返回: 返回字符串<text>的第一个单词。

5、设置编译结果输出目录

Melis 可以将编译出来的目标文件输出到单独的目录中,在make中使用“O”来指定输出目录,比如“make O=out”就是设置目标文件输出到out目录中,不指定O参数的话,源文件和编译产生的文件分开。

# 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")  # 如果O来自命令行,就按照命令行后面的输出
  KBUILD_OUTPUT := $(O)  # KBUILD_OUTPUT就是输出目录
endif

# That's our default target when none is given on the command line
PHONY := _all   # “PHONY”来显示地指明一个目标是“伪目标”,向 make 说明,不管是否有这个文件,这个目标就是“伪目标”。
_all:

# Cancel implicit rules on top Makefile
$(CURDIR)/Makefile Makefile: ;      # 取消顶部Makefile上的隐式规则

ifneq ($(words $(subst :, ,$(CURDIR))), 1)
  $(error main directory cannot contain spaces nor colons)
endif

ifneq ($(KBUILD_OUTPUT),)  # 判断 KBUILD_OUTPUT 是否为空,如果不为空,则进入下面的代码,创建目录
# Invoke a second make in the output directory, passing relevant variables
# check that the output directory actually exists
saved-output := $(KBUILD_OUTPUT)
# 调用 mkdir 命令,创建 KBUILD_OUTPUT 目录,并且将创建成功以后的绝对路径赋值给 KBUILD_OUTPUT。至此,通过 O 指定的输出目录就存在了。
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

6、代码检查

Melis 支持代码检查,使用命令“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")  # 判断 C 是否来源于命令行,如果 C 来源于命令行,那就将 C 赋值给变量KBUILD_CHECKSRC,如果命令行没有 C 的话 KBUILD_CHECKSRC 就为 0
  KBUILD_CHECKSRC = $(C)
endif
ifndef KBUILD_CHECKSRC
  KBUILD_CHECKSRC = 0
endif

7、模块编译

在 Melis 中允许单独编译某个模块,使用命令“ make M=dir”即可,旧语法“ makeSUBDIRS=dir”也是支持的。顶层 Makefile 中的代码如下:

# 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  # 判断是否定义了 SUBDIRS ,如果定义了SUBDIRS ,变量KBUILD_EXTMOD=SUBDIRS,这里是为了支持老语法“make SUBIDRS=dir”
  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),)  # 判断 KBUILD_EXTMOD 时为空,如果为空的话目标_all 依赖 all,因此要先编译出 all。否则的话默认目标_all 依赖 modules,要先编译出 modules,也就是编译模块。一般情况下我们不会在 uboot 中编译模块,所以此处会编译 all 这个目标。
_all: all
else
_all: modules
endif

ifeq ($(KBUILD_SRC),)  # 判断 KBUILD_SRC 是否为空,如果为空的话就设置变量 srctree 为当前目录,即srctree 为“.”,一般不设置 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		:= .    # 设置变量 objtree 为当前目录
prjtree     := $(srctree)/projects
src		    := $(srctree)
obj		    := $(objtree)

VPATH		:= $(srctree)$(if $(KBUILD_EXTMOD),:$(KBUILD_EXTMOD))  # 设置 VPATH

export prjtree srctree objtree VPATH eboard-libs elibrary-libs  # 导出变量

8、SUBARCH获取系统架构

# SUBARCH tells the usermode build what the underlying arch is.  That is set
# first, and if a usermode build is happening, the "ARCH=um" on the command
# line overrides the setting of ARCH below.  If a native build is happening,
# then ARCH is assigned, getting whatever value it gets normally, and
# SUBARCH is subsequently ignored.

SUBARCH := $(shell uname -m | sed -e s/i.86/x86/ -e s/x86_64/x86/ \
				  -e s/sun4u/sparc64/ \
				  -e s/arm.*/arm/ -e s/sa110/arm/ \
				  -e s/s390x/s390/ -e s/parisc64/parisc/ \
				  -e s/ppc.*/powerpc/ -e s/mips.*/mips/ \
				  -e s/sh[234].*/sh/ -e s/aarch64.*/arm64/ )

include scripts/toolchain.mk   # 检查GCC工具链安装情况(根据配置,解压相应工具链)

定义了一个变量 SUBARCH,用于保存当前的系统架构(译注:一般都指CPU架构),这里调用 shell 命令“uname -m”获取架构名称。shell 中的“|”表示管道,意思是将左边的输出作为右边的输入, sed -e 是替换命令,“sed -e s/i.86/x86/”表示将管道输入的字符串中的“i.86”替换为“x86”,其他的“sed -s”命令同理。

它其实就是执行了如下的命令uname -m | sed -e s/i.86/x86/ -e s/x86_64/x86/ -e s/sun4u/sparc64/ -e s/arm.*/arm/ -e s/sa110/arm/ -e s/s390x/s390/ -e s/parisc64/parisc/ -e s/ppc.*/powerpc/ -e s/mips.*/mips/ -e s/sh[234].*/sh/ -e s/aarch64.*/arm64/

我的机子是Ubuntu 16.04 LTS x86_64的(即x86架构)运行一下
在这里插入图片描述如上所见,系统执行uname得到机器、操作系统和架构的信息。因为我们得到的是uname的输出,所以我们需要做一些处理再赋给变量SUBARCH。

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

# Cross compiling and selecting different set of gcc/bin-utils
# ---------------------------------------------------------------------------
#
# When performing cross compilation for other architectures ARCH shall be set
# to the target architecture. (See arch/* for the possibilities).
# ARCH can be set during invocation of make:
# make ARCH=ia64
# Another way is to have ARCH set in the environment.
# The default ARCH is the host where make is executed.

# CROSS_COMPILE specify the prefix used for all executables used
# during compilation. Only gcc and related bin-utils executables
# are prefixed with $(CROSS_COMPILE).
# CROSS_COMPILE can be set on the command line
# make CROSS_COMPILE=ia64-linux-
# Alternatively CROSS_COMPILE can be set in the environment.
# A third alternative is to store a setting in .config so that plain
# "make" in the configured kernel build directory always uses that.
# Default value for CROSS_COMPILE is not to prefix executables
# Note: Some architectures assign CROSS_COMPILE in their arch/*/Makefile
ARCH		?= $(SUBARCH)
CROSS_COMPILE	?= $(CONFIG_CROSS_COMPILE:"%"=%)

# Architecture as present in compile.h
UTS_MACHINE 	:= $(ARCH)
SRCARCH 	:= $(ARCH)

# Additional ARCH settings for x86
ifeq ($(ARCH),i386)
        SRCARCH := x86
endif
ifeq ($(ARCH),x86_64)
        SRCARCH := x86
endif

# Additional ARCH settings for sparc
ifeq ($(ARCH),sparc32)
       SRCARCH := sparc
endif
ifeq ($(ARCH),sparc64)
       SRCARCH := sparc
endif

# Additional ARCH settings for sh
ifeq ($(ARCH),sh64)
       SRCARCH := sh
endif

# Additional ARCH settings for tile
ifeq ($(ARCH),tilepro)
       SRCARCH := tile
endif
ifeq ($(ARCH),tilegx)
       SRCARCH := tile
endif

# Where to locate arch specific headers
hdr-arch  := $(SRCARCH)

获得SUBARCH之后就要设置SRCARCH和hdr-arch,SRCARCH提供了硬件架构相关代码的目录,hdr-arch提供了相关头文件的目录。

KCONFIG_CONFIG	?= .config
export KCONFIG_CONFIG   # 导出 KCONFIG_CONFIG

如果没有设置过代表内核配置文件路径的变量KCONFIG_CONFIG,下一步系统会设置它,默认情况下就是.config,这个文件是不是很熟悉,它就是我们make menuconfig后的那个.config配置文件,里面写入我们内核编译的所有信息。

# SHELL used by kbuild
CONFIG_SHELL := $(shell if [ -x "$$BASH" ]; then echo $$BASH; \
	  else if [ -x /bin/bash ]; then echo /bin/bash; \
	  else echo sh; fi ; fi)

CONFIG_SHELL是编译内核过程中要用到的shell。接下来就要设置一组和编译内核的编译器相关的变量。我们会设置主机的C和C++的编译器及相关配置项:

HOSTCC       = gcc
HOSTCXX      = g++
HOSTCFLAGS   = -Wall -Wmissing-prototypes -Wstrict-prototypes -O2 -fomit-frame-pointer -std=gnu89
HOSTCXXFLAGS = -O2

ifeq ($(shell $(HOSTCC) -v 2>&1 | grep -c "clang version"), 1)
HOSTCFLAGS  += -Wno-unused-value -Wno-unused-parameter \
		-Wno-missing-field-initializers -fno-delete-null-pointer-checks
endif

我们可以看到Makefile在这里开始适配代表C/C++编译器的变量CC和CXX。

那为什么还要HOST*这些变量呢?这是因为CC是编译内核过程中要使用的目标架构的编译器,但是HOSTCC是要被用来编译一组host程序的(下面我们就会看到)。

10、KBUILD_编译的目标

然后我们就看到变量KBUILD_MODULES和KBUILD_BUILTIN的定义,这两个变量决定了我们要编译什么东西(内核、模块或者两者都有):

# Decide whether to build built-in, modular, or both.
# Normally, just do built-in.
# Compile modules on melis by default.
# So ach sub-dir of Top can be compile seperately.
# make projects, ekernel ... ex.

KBUILD_MODULES := 1
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

ifneq ($(filter all _all modules,$(MAKECMDGOALS)),)
  KBUILD_MODULES := 1
endif

ifeq ($(MAKECMDGOALS),)
  KBUILD_MODULES := 1
endif

export KBUILD_MODULES KBUILD_BUILTIN
export KBUILD_CHECKSRC KBUILD_SRC KBUILD_EXTMOD

在这我们可以看到这些变量的定义,并且,如果我们仅仅传递了modules给make,变量KBUILD_BUILTIN会依赖于内核配置选项CONFIG_MODVERSIONS。

11、Kbuild

接着下一步操作是引入下面的文件:

# We need some generic definitions (do not try to remake the file).
scripts/Kbuild.include: ;
-include include/config/auto.conf
include scripts/Kbuild.include

文件Kbuild或者又叫做KernelBuildSystem是一个用来管理构建内核及其模块的特殊框架。kbuild文件的语法与makefile一样。文件scripts/Kbuild.include为kbuild系统提供了一些常规的定义。因为我们包含了这个kbuild文件,我们可以看到和不同工具关联的这些变量的定义,这些工具会在内核和模块编译过程中被使用(比如链接器、编译器、来自binutils的二进制工具包,等等)。

12、交叉编译工具变量设置

上面我们只是设置了 CROSS_COMPILE 的名字,但是交叉编译器其他的工具还没有设置,下面是一些设置。

# Make variables (CC, etc...)
AS		= $(CROSS_COMPILE)as
LD		= $(CROSS_COMPILE)ld
CC		= $(CROSS_COMPILE)gcc
CPP		= $(CC) -E
AR		= $(CROSS_COMPILE)ar
NM		= $(CROSS_COMPILE)nm
STRIP		= $(CROSS_COMPILE)strip
OBJCOPY		= $(CROSS_COMPILE)objcopy
OBJDUMP		= $(CROSS_COMPILE)objdump
SIZE		= $(CROSS_COMPILE)size
AWK		= awk
GENKSYMS	= scripts/genksyms/genksyms
INSTALLKERNEL  := installkernel
DEPMOD		= /sbin/depmod
PERL		= perl
PYTHON		= python
CHECK		= sparse

CHECKFLAGS     := -D__linux__ -Dlinux -D__STDC__ -Dunix -D__unix__ -D__melis__ \
		-Wbitwise -Wno-return-void $(CF)
CFLAGS_MODULE   =
AFLAGS_MODULE   =
LDFLAGS_MODULE  =
CFLAGS_KERNEL	=
AFLAGS_KERNEL	=
CFLAGS_GCOV	= -fprofile-arcs -ftest-coverage

在这些定义好的变量后面,我们又定义了两个变量:USERINCLUDE和MELISINCLUDE。他们包含了头文件的路径(第一个是给用户用的,第二个是给内核用的)

# Use USERINCLUDE when you must reference the UAPI directories only.
USERINCLUDE    := \
                    -Iinclude/generated  \
                    -Iinclude/generated/uapi/melis  \
                    -I$(srctree)/include/melis/elibrary \
                    -include $(srctree)/include/melis/kconfig.h

# Use MELISINCLUDE when you must reference the include/ directory.
# Needed to be compatible with the O= option
MELISINCLUDE    := \
                    $(if $(KBUILD_SRC), -I$(srctree)/include)           \
                    -Iinclude                                           \
                    -Iinclude/melis                                     \
                    -Iinclude/melis/common                              \
                    -Iinclude/melis/eboot                               \
                    -Iinclude/melis/eboard                              \
                    -Iinclude/melis/ekernel                             \
                    -Iinclude/melis/ekernel/arch                        \
                    -Iinclude/melis/ekernel/csp                         \
                    -Iinclude/melis/ekernel/drivers                     \
                    -Iinclude/melis/ekernel/filesystem                  \
                    -Iinclude/melis/ekernel/pthread                     \
                    -Iinclude/melis/elibrary                            \
                    -Iinclude/melis/elibrary/libc                       \
                    -Iinclude/melis/elibrary/libc/mediainfo             \
                    -Iinclude/melis/elibrary/libc/misc                  \
                    -Iinclude/melis/elibrary/libc/misc/pub0             \
                    -Iinclude/melis/emodules                            \
                    -Iinclude/melis/ipc                                 \
                    -Iinclude/melis/misc                                \
                    -Iinclude/melis/of                                  \
                    -Iinclude/melis/sys                                 \
                    -Iinclude/melis/video                               \
                    -Iekernel/subsys/finsh_cli                          \
                    -Iekernel/core/rt-thread/include                    \
                    -Iekernel/subsys/thirdparty/dfs/include             \
                    -Iekernel/drivers/hal/source                        \
                    -Iekernel/drivers/include/osal                      \
                    $(USERINCLUDE)
ifeq (${CONFIG_ARMV7_A}, y)
MELISINCLUDE  += -Iinclude/melis/ekernel/arch/cortex-v7a
endif

ifeq (${CONFIG_RISCV}, y)
MELISINCLUDE  += -Iinclude/melis/ekernel/arch/riscv 

#for application in the livedesk directory
SOLUTION	:= livedesk

#head file include at beetles -- top directory of the application solution
SOLUTION_INCLUDE    :=  -I${MELIS_BASE}/${SOLUTION}/beetles/elibs/libapps/module_adapter                \
                        -I${MELIS_BASE}/${SOLUTION}/beetles/elibs/libapps/scene/listbar                 \
                        -I${MELIS_BASE}/${SOLUTION}/beetles/elibs/lib_ex/anole                          \
                        -I${MELIS_BASE}/${SOLUTION}/beetles/elibs/lib_ex/monkey                         \
                        -I${MELIS_BASE}/${SOLUTION}/beetles/elibs/lib_ex/monkey/bookmark                \
                        -I${MELIS_BASE}/${SOLUTION}/beetles/elibs/lib_ex/monkey/formats                 \
                        -I${MELIS_BASE}/${SOLUTION}/beetles/elibs/lib_ex/monkey/formats/encode_parser   \
                        -I${MELIS_BASE}/${SOLUTION}/beetles/elibs/lib_ex/monkey/formats/encode_parser/gbk               \
                        -I${MELIS_BASE}/${SOLUTION}/beetles/elibs/lib_ex/monkey/formats/encode_parser/utf16_big         \
                        -I${MELIS_BASE}/${SOLUTION}/beetles/elibs/lib_ex/monkey/formats/encode_parser/utf16_little      \
                        -I${MELIS_BASE}/${SOLUTION}/beetles/elibs/lib_ex/monkey/formats/encode_parser/utf8              \
                        -I${MELIS_BASE}/${SOLUTION}/beetles/elibs/lib_ex/monkey/mnpl                    \
                        -I${MELIS_BASE}/${SOLUTION}/beetles/elibs/lib_ex/monkey/regedit                 \
                        -I${MELIS_BASE}/${SOLUTION}/beetles/elibs/lib_ex/robin                          \
                        -I${MELIS_BASE}/${SOLUTION}/beetles/elibs/lib_ex/robin/foundation               \
                        -I${MELIS_BASE}/${SOLUTION}/beetles/elibs/lib_ex/robin/lib                      \
                        -I${MELIS_BASE}/${SOLUTION}/beetles/include                                     \
                        -I${MELIS_BASE}/${SOLUTION}/beetles/include/applets                             \
                        -I${MELIS_BASE}/${SOLUTION}/beetles/include/elibs                               \
                        -I${MELIS_BASE}/${SOLUTION}/beetles/include/elibs/lib_ex                        \
                        -I${MELIS_BASE}/${SOLUTION}/beetles/include/elibs/lib_ex/robin/ab               \
                        -I${MELIS_BASE}/${SOLUTION}/beetles/include/elibs/lib_ex/robin/channel          \
                        -I${MELIS_BASE}/${SOLUTION}/beetles/include/elibs/lib_ex/robin/disp_output      \
                        -I${MELIS_BASE}/${SOLUTION}/beetles/include/elibs/lib_ex/robin/eq               \
                        -I${MELIS_BASE}/${SOLUTION}/beetles/include/elibs/lib_ex/robin/ff_rr_speed      \
                        -I${MELIS_BASE}/${SOLUTION}/beetles/include/elibs/lib_ex/robin/fsm_ctrl         \
                        -I${MELIS_BASE}/${SOLUTION}/beetles/include/elibs/lib_ex/robin/lyric            \
                        -I${MELIS_BASE}/${SOLUTION}/beetles/include/elibs/lib_ex/robin/media_info       \
                        -I${MELIS_BASE}/${SOLUTION}/beetles/include/elibs/lib_ex/robin/npl              \
                        -I${MELIS_BASE}/${SOLUTION}/beetles/include/elibs/lib_ex/robin/palette          \
                        -I${MELIS_BASE}/${SOLUTION}/beetles/include/elibs/lib_ex/robin/play_mode        \
                        -I${MELIS_BASE}/${SOLUTION}/beetles/include/elibs/lib_ex/robin/play_speed       \
                        -I${MELIS_BASE}/${SOLUTION}/beetles/include/elibs/lib_ex/robin/spectrum         \
                        -I${MELIS_BASE}/${SOLUTION}/beetles/include/elibs/lib_ex/robin/star             \
                        -I${MELIS_BASE}/${SOLUTION}/beetles/include/elibs/lib_ex/robin/subtitle         \
                        -I${MELIS_BASE}/${SOLUTION}/beetles/include/elibs/lib_ex/robin/track            \
                        -I${MELIS_BASE}/${SOLUTION}/beetles/include/elibs/lib_ex/robin/video_layer      \
                        -I${MELIS_BASE}/${SOLUTION}/beetles/include/elibs/lib_ex/robin/video_win        \
                        -I${MELIS_BASE}/${SOLUTION}/beetles/include/elibs/lib_ex/robin/vision_effect    \
                        -I${MELIS_BASE}/${SOLUTION}/beetles/include/elibs/lib_ex/robin/volume           \
                        -I${MELIS_BASE}/${SOLUTION}/beetles/include/elibs/lib_ex/robin/zoom             \
                        -I${MELIS_BASE}/${SOLUTION}/beetles/include/elibs/lib_ex/scanfile               \
                        -I${MELIS_BASE}/${SOLUTION}/beetles/include/init                                \
                        -I${MELIS_BASE}/${SOLUTION}/beetles/include/init/alarm                          \
                        -I${MELIS_BASE}/${SOLUTION}/beetles/include/init/background                     \
                        -I${MELIS_BASE}/${SOLUTION}/beetles/include/init/bookengine                     \
                        -I${MELIS_BASE}/${SOLUTION}/beetles/include/init/display                        \
                        -I${MELIS_BASE}/${SOLUTION}/beetles/include/init/headbar                        \
                        -I${MELIS_BASE}/${SOLUTION}/beetles/include/init/mini_music                     \
                        -I${MELIS_BASE}/${SOLUTION}/beetles/include/init/screen_lock                    \
                        -I${MELIS_BASE}/${SOLUTION}/beetles/include/init/tp_adjust                      \
                        -I${MELIS_BASE}/${SOLUTION}/beetles/include/init/ui/tp_adjust_scene             \
                        -I${MELIS_BASE}/${SOLUTION}/beetles/include/libapps                             \
                        -I${MELIS_BASE}/${SOLUTION}/beetles/include/libapps/module_adapter              \
                        -I${MELIS_BASE}/${SOLUTION}/beetles/include/libapps/scene                       \
                        -I${MELIS_BASE}/${SOLUTION}/beetles/include/libapps/srf                         \
                        -I${MELIS_BASE}/${SOLUTION}/beetles/include/mod_desktop                         \
                        -I${MELIS_BASE}/${SOLUTION}/beetles/init/                                       \
                        -I${MELIS_BASE}/${SOLUTION}/beetles/init/background/fb_lib                      \
                        -I${MELIS_BASE}/${SOLUTION}/beetles/init/headbar/                               \
                        -I${MELIS_BASE}/${SOLUTION}/beetles/init/headbar/prog_bar                       \
                        -I${MELIS_BASE}/${SOLUTION}/beetles/init/mini_music                             \
                        -I${MELIS_BASE}/${SOLUTION}/beetles/init/screen_lock                            \
                        -I${MELIS_BASE}/${SOLUTION}/beetles/init/ui                                     \
                        -I${MELIS_BASE}/${SOLUTION}/beetles/init/ui/assistant_scene                     \
                        -I${MELIS_BASE}/${SOLUTION}/beetles/init/ui/dialog_scene                        \
                        -I${MELIS_BASE}/${SOLUTION}/beetles/init/ui/tp_adjust_scene                     \
                        -I${MELIS_BASE}/${SOLUTION}/beetles/mod_desktop                                 \
                        -I${MELIS_BASE}/${SOLUTION}/beetles/mod_desktop/engine                          \
                        -I${MELIS_BASE}/${SOLUTION}/beetles/mod_desktop/engine/radio                    \
                        -I${MELIS_BASE}/${SOLUTION}/beetles/mod_desktop/engine/radio/receive            \
                        -I${MELIS_BASE}/${SOLUTION}/beetles/mod_desktop/engine/radio/send               \
                        -I${MELIS_BASE}/${SOLUTION}/beetles/mod_desktop/engine/walkman                  \
                        -I${MELIS_BASE}/${SOLUTION}/beetles/mod_desktop/engine/walkman/foundation       \
                        -I${MELIS_BASE}/${SOLUTION}/beetles/mod_desktop/engine/walkman/lib              \
                        -I${MELIS_BASE}/${SOLUTION}/beetles/mod_desktop/framework                       \
                        -I${MELIS_BASE}/${SOLUTION}/beetles/mod_desktop/functions                       \
                        -I${MELIS_BASE}/${SOLUTION}/beetles/mod_desktop/include                         \
                        -I${MELIS_BASE}/${SOLUTION}/beetles/mod_desktop/util

export      SOLUTION    SOLUTION_INCLUDE

endif #ifeq (${CONFIG_RISCV}, y)

这并不是最终确定的编译器标志,它们还可以在其他makefile里面更新(比如arch/里面的kbuild)。变量定义完之后,全部会被导出供其它makefile使用。

#KBUILD_CPPFLAGS := -D__KERNEL__
KBUILD_CPPFLAGS :=
KBUILD_CFLAGS   := -Wall -Wundef -Wstrict-prototypes -Wno-trigraphs \
		-fno-strict-aliasing -fno-common \
		-Werror-implicit-function-declaration \
		-Wno-format-security \
		-fno-builtin-printf \
		-D_SYS__PTHREADTYPES_H_

KBUILD_AFLAGS_KERNEL :=
KBUILD_CFLAGS_KERNEL :=
KBUILD_AFLAGS   := -D__ASSEMBLY__
KBUILD_AFLAGS_MODULE  := -DMODULE
KBUILD_CFLAGS_MODULE  := -DMODULE
KBUILD_LDFLAGS_MODULE := -T $(srctree)/scripts/module-common.lds

# Read KERNELRELEASE from include/config/kernel.release (if it exists)
KERNELRELEASE = $(shell cat include/config/kernel.release 2> /dev/null)
KERNELVERSION = $(VERSION)$(if $(PATCHLEVEL),.$(PATCHLEVEL)$(if $(SUBLEVEL),.$(SUBLEVEL)))$(EXTRAVERSION)

13、导出其他变量

export VERSION PATCHLEVEL SUBLEVEL KERNELRELEASE KERNELVERSION
export ARCH SRCARCH CONFIG_SHELL HOSTCC HOSTCFLAGS CROSS_COMPILE AS LD CC
export CPP AR NM STRIP OBJCOPY OBJDUMP SIZE
export MAKE AWK GENKSYMS INSTALLKERNEL PERL PYTHON UTS_MACHINE
export HOSTCXX HOSTCXXFLAGS LDFLAGS_MODULE CHECK CHECKFLAGS

export KBUILD_CPPFLAGS NOSTDINC_FLAGS MELISINCLUDE OBJCOPYFLAGS LDFLAGS
export KBUILD_CFLAGS CFLAGS_KERNEL CFLAGS_MODULE CFLAGS_GCOV CFLAGS_KASAN
export KBUILD_AFLAGS AFLAGS_KERNEL AFLAGS_MODULE
export KBUILD_AFLAGS_MODULE KBUILD_CFLAGS_MODULE KBUILD_LDFLAGS_MODULE
export KBUILD_AFLAGS_KERNEL KBUILD_CFLAGS_KERNEL
export KBUILD_ARFLAGS

# When compiling out-of-tree modules, put MODVERDIR in the module
# tree rather than in the kernel tree. The kernel tree might
# even be read-only.
export MODVERDIR := $(if $(KBUILD_EXTMOD),$(firstword $(KBUILD_EXTMOD))/).tmp_versions

# Files to ignore in find ... statements

export RCS_FIND_IGNORE := \( -name SCCS -o -name BitKeeper -o -name .svn -o    \
			  -name CVS -o -name .pc -o -name .hg -o -name .git \) \
			  -prune -o
export RCS_TAR_IGNORE := --exclude SCCS --exclude BitKeeper --exclude .svn \
			 --exclude CVS --exclude .pc --exclude .hg --exclude .git

14、

# ===========================================================================
# Rules shared between *config targets and build targets

# Basic helpers built in scripts/
PHONY += scripts_basic
scripts_basic:
	$(Q)$(MAKE) $(build)=scripts/basic
	$(Q)rm -f .tmp_quiet_recordmcount

# To avoid any implicit rule to kick in, define an empty command.
scripts/basic/%: scripts_basic ;

首先我们可以看出scripts_basic是按照scripts/basic的makefile执行make的。下面我们看看scripts/basic下的makefile都有什么
在这里插入图片描述
scripts/basic/Makefile包含了编译两个主机程序fixdep和bin2的目标。

第一个工具是fixdep:

用来优化gcc生成的依赖列表,然后在重新编译源文件的时候告诉make。

第二个工具是bin2c,

它依赖于内核配置选项CONFIG_BUILD_BIN2C,并且它是一个用来将标准输入接口(译注:即stdin)收到的二进制流通过标准输出接口(即:stdout)转换成C头文件的非常小的C程序。你可能注意到这里有些奇怪的标志,如hostprogs-y等。这个标志用于所有的kbuild文件,更多的信息你可以从documentation获得。

在我们这里,hostprogs-y告诉kbuild这里有个名为fixed的程序,这个程序会通过和Makefile相同目录的fixdep.c编译而来。

我们make时执行make之后,终端的第一个输出就是kbuild的结果
在这里插入图片描述在这里插入图片描述

PHONY += outputmakefile
# outputmakefile generates a Makefile in the output directory, if using a
# separate output directory. This allows convenient use of make in the
# output directory.
outputmakefile:
ifneq ($(KBUILD_SRC),)
	$(Q)ln -fsn $(srctree) source
	$(Q)$(CONFIG_SHELL) $(srctree)/scripts/mkmakefile \
	    $(srctree) $(objtree) $(VERSION) $(PATCHLEVEL)
endif

# Support for using generic headers in asm-generic
PHONY += asm-generic
asm-generic:
	$(Q)$(MAKE) -f $(srctree)/scripts/Makefile.asm-generic \
	            src=asm obj=arch/$(SRCARCH)/include/generated/asm
	$(Q)$(MAKE) -f $(srctree)/scripts/Makefile.asm-generic \
	            src=uapi/asm obj=arch/$(SRCARCH)/include/generated/uapi/asm

15、

(未完待补充)

二、Makefile中赋值语句(=, :=, ?=和+=)

在Makefile语法中,时不时会见到各种“=”号的赋值语句,除了常见的“=”和“:=”,还有“?=”等。

那么这些赋值等号分别表示什么含义呢?

1、“=”

“=”是最普通的等号,然而在Makefile中确实最容易搞错的赋值等号,使用”=”进行赋值,变量的值是整个makefile中最后被指定的值。不太容易理解,举个例子如下:

VIR_A = A
VIR_B = $(VIR_A) B
VIR_A = AA

经过上面的赋值后,最后VIR_B的值是AA B,而不是A B。在make时,会把整个makefile展开,拉通决定变量的值。

2、“:=”

相比于前面“最普通”的”=”,”:=”就容易理解多了。”:=”就表示直接赋值,赋予当前位置的值。同样举个例子说明:

VIR_A := A
VIR_B := $(VIR_A) B
VIR_A := AA

最后变量VIR_B的值是A B,即根据当前位置进行赋值。因此相比于”=”,”:=”才是真正意义上的直接赋值。

3、“?=”

“?=”表示如果该变量没有被赋值,则赋予等号后的值。举例:

VIR ?= new_value

如果VIR在之前没有被赋值,那么VIR的值就为new_value。

VIR := old_value
VIR ?= new_value

这种情况下,VIR的值就是old_value。

4、“+=”

“+=”和平时写代码的理解是一样的,表示将等号后面的值添加到前面的变量上。

三、理解Makefile中的$@、$^和$<

1、理解$@、$^、$<的含义

Makefile中,格式为这样的

target : prerequisties
目标:需要依赖的条件

简单的工程直接这样:

hello:hello.cc
	gcc  hello.cc -o hello

但如果文件多了呢?按部就班写会显得很长,所有这时候makefile中的常用命令就产生了,如下:

$@ 表示目标文件
$^ 表示所有的依赖文件
$< 表示第一个依赖文件
$? 表示比目标还要新的依赖文件列表

2、举例

将头文件放在include文件夹,cpp文件放在source文件夹,test.cc为可执行文件。工程目录为:

.
├── include
│ ├── myString.h
│ └── utils.h
├── Makefile
├── source
│ ├── myString.cpp
│ └── utils.cpp
└── test.cc

Makefile文件为:

TARGET = test.out

CXXFLAGS += -std=gnu++11
CC = g++
#主函数文件
CXX_FILES := ./test.cc

#cpp文件
SRC_DIRS := ./source/myString.cpp
SRC_DIRS += ./source/utils.cpp

INC_DIRS := -I ./include

LIBS += -lstdc++
LIBS += -ldl -lpthread

$(TARGET):$(CXX_FILES) $(SRC_DIRS) 
	$(info target: $@)
	$(info all: $^)
	$(info first: $<)
	$(info SRC_DIRS_all: $(SRC_DIRS))
	$(CC) -o $@ $^ $(INC_DIRS) $(CXXFLAGS) $(LIBS)

.PHONY:clean
clean:
	rm -rf $(TARGET)

输出结果如下:
在这里插入图片描述

3、简提Makefile中打印日志信息

打印用info,Makefile提供了三个命令:

$(warning "the value of LOCAL_PATH is$(FILE)")
$(info "the value of LOCAL_PATH is$(FILE)")
$(eror "the value of LOCAL_PATH is$(FILE)")
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值