版本号
MAKEFLAGES变量
20 MAKEFLAGS += -rR --include-dir=$(CURDIR)
- Makefile有两个特殊的变量:“SHELL”和“MAKEFLAGS”,这两个变量除非使用“unexport”声明,否则的话在整个make的执行过程中,它们的值始终自动的传递给子make。
- 在 Makefile 中使用“make”命令来执行其他的 Makefile文件
- 上述代码使用“+=”来给变量 MAKEFLAGS 追加了一些值,“-rR”表示禁止使用内置的隐含规则和变量定义,“--include-dir”指明搜索路径,”$(CURDIR)”表示当前目录。
命令输出
- uboot 默认编译是不会在终端中显示完整的命令,都是短命令。例如CC xxx.o和SYM u-boot.sym
- 可以通过设置变量“V=1“来实现完整的命令输出
实现源码
73 ifeq ("$(origin V)", "command line") 74 KBUILD_VERBOSE = $(V) 75 endif 76 ifndef KBUILD_VERBOSE 77 KBUILD_VERBOSE = 0 78 endif 79 80 ifeq ($(KBUILD_VERBOSE),1) 81 quiet = 82 Q = 83 else 84 quiet=quiet_ 85 Q = @ 86 endif
- 代码中先使用 ifeq 来判断"$(origin V)"和"command line"是否相等
- 函数origin 用于告诉你变量是哪来的。语法为:
$(origin)
variable 是变量名,o 函数的返回值就是变量来源,因此$(origin V)就是变量 V 的来源- 当这两个相等的时候变量 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 为“@”
- Makefile 中会用到变量 quiet 和 Q 来控制编译的时候是否在终端输出完整的命令
- Q的作用。在顶层Makefile 中有很多如下所示的命令:$(Q)$(MAKE) $(build)=tools
当 V=0 的话上述命令展开就是“@ make $(build)=tools”,make 在执行的时候默认会在终端输出命令,但是在命令前面加上“@”就不会在终端输出命令了。
当 V=1 的时候 Q 就为空,上述命令就是“make $(build)=tools”,因此在 make 执行的过程,命令会被完整的输出在终端上- quiet的作用。有些命令会有两个版本,比如:
quiet_cmd_sym ?= SYM $@
cmd_sym ?= $(OBJDUMP) -t $< > $@
sym 命令分为“quiet_cmd_sym”和“cmd_sym”两个版本,这两个命令的功能都是一样的,区别在于 make 执行的时候输出的命令不同。quiet_cmd_xxx 命令输出信息少,也就是短命令,而 cmd_xxx 命令输出信息多,也就是完整的命令。那么Makefile中就会使用变量quiet来指向这两个版本中的其中一个,进而控制命令的输出形式- 如果变量 quiet 为空的话,整个命令都会输出。
如果变量 quiet 为“quiet_”的话,仅输出短版本。
如果变量 quiet 为“silent_”的话,整个命令都不会输出
静默输出
- 编译的时候使用“make -s”即可实现静默输出
实现源码
88 # If the user is running make -s (silent mode), suppress echoing of 89 # commands 90 91 ifneq ($(filter 4.%,$(MAKE_VERSION)),) # make-4 92 ifneq ($(filter %s ,$(firstword x$(MAKEFLAGS))),) 93 quiet=silent_ 94 endif 95 else # make-3.8x 96 ifneq ($(filter s% -s%,$(MAKEFLAGS)),) 97 quiet=silent_ 98 endif 99 endif 100 101 export quiet Q KBUILD_VERBOSE
- 第91行判断当前正在使用的编译器版本号是否为4.x
- filter 函数是个过滤函数,函数格式如下:
$(filter, )
filter 函数表示以 pattern 模式过滤 text 字符串中的单词,仅保留符合模式 pattern 的单词,可以有多个模式。函数返回值就是符合 pattern 的字符串。- $(filter 4.%,$(MAKE_VERSION))的含义就是在字符串“MAKE_VERSION”中找出符合“4.%”的字符(%为通配符),MAKE_VERSION 是编译器的版本号,我们当前使用的交叉编译器版本号为 4.9.4,所以肯定可以找出“4.%”。因此$(filter 4.%,$(MAKE_VERSION))不为空,条件成立
- 第 92 行判断MAKEFLAGS的第一个单词中是否包含s
- 函数 firstword 是获取首单词,函数格式如下:
$(firstword)
firstword 函数用于取出 text 字符串中的第一个单词,函数的返回值就是获取到的单词- 当使用“make -s”编译的时候,“-s”会作为 MAKEFLAGS 变量的一部分传递给 Makefile
- 第 101 行 使用 export 导出变量 quiet、Q 和 KBUILD_VERBOSE
设置编译结果输出目录
- uboot 可以将编译出来的目标文件输出到单独的目录中,在 make 的时候使用“O”来指定输出目录,比如“make O=out”就是设置目标文件输出到 out 目录中
- 这么做是为了将源文件和编译产生的文件分开,当然也可以不指定 O 参数,不指定的话源文件和编译产生的文件都在同一个目录内
实现源码
103 # kbuild supports saving output files in a separate directory. 104 # To locate output files in a separate directory two syntaxes are supported. 105 # In both cases the working directory must be the root of the kernel src. 106 # 1) O= 107 # Use "make O=dir/to/store/output/files/" 108 # 109 # 2) Set KBUILD_OUTPUT 110 # Set the environment variable KBUILD_OUTPUT to point to the directory 111 # where the output files shall be placed. 112 # export KBUILD_OUTPUT=dir/to/store/output/files/ 113 # make 114 # 115 # The O= assignment takes precedence over the KBUILD_OUTPUT environment 116 # variable. 117 118 # KBUILD_SRC is set on invocation of make in OBJ directory 119 # KBUILD_SRC is not intended to be used by the regular user (for now) 120 ifeq ($(KBUILD_SRC),) 121 122 # OK, Make called in directory where kernel src resides 123 # Do we want to locate output files in a separate directory? 124 ifeq ("$(origin O)", "command line") 125 KBUILD_OUTPUT := $(O) 126 endif 127 128 # That's our default target when none is given on the command line 129 PHONY := _all 130 _all: 131 132 # Cancel implicit rules on top Makefile 133 $(CURDIR)/Makefile Makefile: ; 134 135 ifneq ($(KBUILD_OUTPUT),) 136 # Invoke a second make in the output directory, passing relevant variables 137 # check that the output directory actually exists 138 saved-output := $(KBUILD_OUTPUT) 139 KBUILD_OUTPUT := $(shell mkdir -p $(KBUILD_OUTPUT) && cd $(KBUILD_OUTPUT) \ 140 && /bin/pwd) ...... 155 endif # ifneq ($(KBUILD_OUTPUT),) 156 endif # ifeq ($(KBUILD_SRC),)
- 第 124 行判断“O”是否来自于命令行,如果来自命令行的话条件成立,KBUILD_OUTPUT就为$(O),因此变量 KBUILD_OUTPUT 就是输出目录
- 第 135 行判断 KBUILD_OUTPUT 是否为空
- 第 139 行调用 mkdir 命令,创建 KBUILD_OUTPUT 目录,并且将创建成功以后的绝对路径赋值给 KBUILD_OUTPUT。至此,通过 O 指定的输出目录就存在了
代码检查
- uboot 支持代码检查,使用命令“make C=1”使能代码检查,检查那些需要重新编译的文件。“make C=2”用于检查所有的源码文件
实现源码
166 # Call a source code checker (by default, "sparse") as part of the 167 # C compilation. 168 # 169 # Use 'make C=1' to enable checking of only re-compiled files. 170 # Use 'make C=2' to enable checking of *all* source files, regardless 171 # of whether they are re-compiled or not. 172 # 173 # See the file "Documentation/sparse.txt" for more details, including 174 # where to get the "sparse" utility. 175 176 ifeq ("$(origin C)", "command line") 177 KBUILD_CHECKSRC = $(C) 178 endif 179 ifndef KBUILD_CHECKSRC 180 KBUILD_CHECKSRC = 0 181 endif
- 第 176 行判断 C 是否来源于命令行,如果 C 来源于命令行,那就将 C 赋值给变量KBUILD_CHECKSRC,如果命令行没有 C 的话 KBUILD_CHECKSRC 就为 0
模块编译
- 在 uboot 中允许单独编译某个模块,使用命令“make M=dir”即可,旧语法“make SUBDIRS=dir”也是支持的
实现源码
183 # Use make M=dir to specify directory of external module to build 184 # Old syntax make ... SUBDIRS=$PWD is still supported 185 # Setting the environment variable KBUILD_EXTMOD take precedence 186 ifdef SUBDIRS 187 KBUILD_EXTMOD ?= $(SUBDIRS) 188 endif 189 190 ifeq ("$(origin M)", "command line") 191 KBUILD_EXTMOD := $(M) 192 endif 193 194 # If building an external module we do not care about the all: rule 195 # but instead _all depend on modules 196 PHONY += all 197 ifeq ($(KBUILD_EXTMOD),) 198 _all: all 199 else 200 _all: modules 201 endif 202 203 ifeq ($(KBUILD_SRC),) 204 # building in the source tree 205 srctree := . 206 else 207 ifeq ($(KBUILD_SRC)/,$(dir $(CURDIR))) 208 # building in a subdirectory of the source tree 209 srctree := .. 210 else 211 srctree := $(KBUILD_SRC) 212 endif 213 endif 214 objtree := . 215 src := $(srctree) 216 obj := $(objtree) 217 218 VPATH := $(srctree)$(if $(KBUILD_EXTMOD),:$(KBUILD_EXTMOD)) 219 220 export srctree objtree VPATH
- 第 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
获取主机架构和系统
实现源码
227 HOSTARCH := $(shell uname -m | \ 228 sed -e s/i.86/x86/ \ 229 -e s/sun4u/sparc64/ \ 230 -e s/arm.*/arm/ \ 231 -e s/sa110/arm/ \ 232 -e s/ppc64/powerpc/ \ 233 -e s/ppc/powerpc/ \ 234 -e s/macppc/powerpc/\ 235 -e s/sh.*/sh/) 236 237 HOSTOS := $(shell uname -s | tr '[:upper:]' '[:lower:]' | \ 238 sed -e 's/\(cygwin\).*/cygwin/') 239 240 export HOSTARCH HOSTOS
设置目标架构、交叉编译器和配置文件
- 编 译 uboot 的 时 候 需 要 设 置 目 标 板 架 构 和 交 叉 编 译 器 ,“ make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf-”就是用于设置 ARCH 和 CROSS_COMPILE
实现源码
244 # set default to nothing for native builds 245 ifeq ($(HOSTARCH),$(ARCH)) 246 CROSS_COMPILE ?= 247 endif 248 249 KCONFIG_CONFIG ?= .config 250 export KCONFIG_CONFIG
- 第 245 行判断 HOSTARCH 和 ARCH 这两个变量是否相等,主机架构(变量 HOSTARCH)是x86_64,而我们编译的是 ARM 版本 uboot,肯定不相等,所以 CROS_COMPILE= arm-linux-gnueabihf-
- 第 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 里面的才是实时有效的配置
调用scripts/Kbuild.include
- 主 Makefile 会调用文件 scripts/Kbuild.include 这个文件
实现源码
327 # We need some generic definitions (do not try to remake the file). 328 scripts/Kbuild.include: ; 329 include scripts/Kbuild.include
- 使用“include”包含了文件 scripts/Kbuild.include,此文件里面定义了很多变量
- 在 uboot 的编译过程中会用到 scripts/Kbuild.include 中的这些变量
交叉编译工具变量设置
实现源码
331 # Make variables (CC, etc...) 332 333 AS = $(CROSS_COMPILE)as 334 # Always use GNU ld 335 ifneq ($(shell $(CROSS_COMPILE)ld.bfd -v 2> /dev/null),) 336 LD = $(CROSS_COMPILE)ld.bfd 337 else 338 LD = $(CROSS_COMPILE)ld 339 endif 340 CC = $(CROSS_COMPILE)gcc 341 CPP = $(CC) -E 342 AR = $(CROSS_COMPILE)ar 343 NM = $(CROSS_COMPILE)nm 344 LDR = $(CROSS_COMPILE)ldr 345 STRIP = $(CROSS_COMPILE)strip 346 OBJCOPY = $(CROSS_COMPILE)objcopy 347 OBJDUMP = $(CROSS_COMPILE)objdump
导出其它变量
实现源码
368 export VERSION PATCHLEVEL SUBLEVEL UBOOTRELEASE UBOOTVERSION 369 export ARCH CPU BOARD VENDOR SOC CPUDIR BOARDDIR 370 export CONFIG_SHELL HOSTCC HOSTCFLAGS HOSTLDFLAGS CROSS_COMPILE AS LD CC 371 export CPP AR NM LDR STRIP OBJCOPY OBJDUMP 372 export MAKE AWK PERL PYTHON 373 export HOSTCXX HOSTCXXFLAGS DTC CHECK CHECKFLAGS 374 375 export KBUILD_CPPFLAGS NOSTDINC_FLAGS UBOOTINCLUDE OBJCOPYFLAGS LDFLAGS 376 export KBUILD_CFLAGS KBUILD_AFLAGS
- 这些变量中大部分都已经在前面定义了
- ARCH CPU BOARD VENDOR SOC CPUDIR BOARDDIR这 7 个变量在顶层 Makefile 是找不到的,这 7 个变量就是在 uboot 根目录下的 config.mk 里面定义的,具体的值参考imx6ull:uboot根目录config.mk分析
- 根据分析可知:
ARCH = arm
CPU = armv7
BOARD = mx6ullevk
VENDOR = freescale
SOC = mx6
CPUDIR = arch/arm/cpu/armv7
BOARDDIR = freescale/mx6ullevk
imx6ull:uboot的make xxx_defconfig 过程分析
总结:一通分析下来,绕来绕去,除了上图中的调用关系外,感觉没有得到什么有用的内容。这里需要明白的是make xxx_defconfig的执行过程中从顶层makefile开始,中间用到了许多./scripts目录下的文件以及有该目录下的某些文件得到的主机程序,最终我们看到的效果就是在uboot根目录下生成了一个.config文件,该文件中的部分内容就来自于xxx_defconfig
imx6ull:uboot的make过程分析
总结(待完善)
执行的操作
变量的含义