二、顶层Makefile前面部分分析
1.递归调用make(20-29)
在源码内有许许多多的目录,顶层Makefile也要调用子目录(subdir)中的makefile。
$(MAKE) -C subdir
export :用于给子makefile传入变量
unexport :用于不给子makefile导入变量
SHELL / MAKEFLAGS :无论用不用export , 都会将变量直接传给子makefile的。
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
2.命令行编译V=1的用处(73-86)
看代码不难发先,它会比较V在命令行中输入的值,如果为非0,就会给KBUILD_VERBOSE变量赋值。为1的时候就会给quiet与Q附一个空值。也就是说在给每一个编译命令都会调用quiet与Q之来判断是否要打印出详细的命令代码。其中Q=@。在makefile中,如果在命令前面使用@,就不会打印在终端了。其中quiet_cmd_socboot , 与cmd_socboot命令等效,quiet=quiet_的话,输出较短版本的命令。
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
$(Q)$(MAKE) -C $(KBUILD_OUTPUT) KBUILD_SRC=$(CURDIR) \
-f $(CURDIR)/Makefile $(filter-out _all sub-make,$(MAKECMDGOALS))
quiet_cmd_socboot = SOCBOOT $@
cmd_socboot = cat spl/u-boot-spl.sfp spl/u-boot-spl.sfp \
spl/u-boot-spl.sfp spl/u-boot-spl.sfp \
u-boot.img > $@ || rm -f $@
3.静默编译(91-101)
使用make -s可以执行静默编译,编译全程不会打印命令信息,很少用。quiet = silent_。
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
4.O变量设置(120-164)
编译的时候可以将编译的输出文件存到某一目录下,就可以使用0=/dir。如果不使用O,就会在当前目录中。
ifeq ($(KBUILD_SRC),)
ifeq ("$(origin O)", "command line")
KBUILD_OUTPUT := $(O)
endif
PHONY := _all
_all:
$(CURDIR)/Makefile Makefile: ;
ifneq ($(KBUILD_OUTPUT),)
#... ...
endif
endif
5.C变量设置(176-181)
用于源码检查,C=1使能检查用于重新编译连接的代码。C=2检查所有的代码。
ifeq ("$(origin C)", "command line")
KBUILD_CHECKSRC = $(C)
endif
ifndef KBUILD_CHECKSRC
KBUILD_CHECKSRC = 0
endif
6.M变量设置(183-220)
uboot编译模块,我们也使不上。有模块编译外部模块需要建立一些依赖源,最后会导出一些srctree objtree变量。
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
7.获取主机架构与系统(225-241)
获取系统架构,我们电脑的架构是x86 ,利用管道符进行替换i.86 。最终导出变量HOSTARCH=x86_64,HOSTOS=linux。
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
8.默认设置(244-264)
为了方便可以在Makefile的这里指定架构ARCH与CROSS_COMPILE。其余还有一些主机相关的信息不管。
ifeq ($(HOSTARCH),$(ARCH))
CROSS_COMPILE ?=
endif
KCONFIG_CONFIG ?= .config
export KCONFIG_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)
HOSTCC = cc
HOSTCXX = c++
HOSTCFLAGS = -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer
HOSTCXXFLAGS = -O2
ifeq ($(HOSTOS),cygwin)
HOSTCFLAGS += -ansi
endif
9.初始化定义(328-376)
引用了Kbuild.include一个文件,以及定义好多熟悉的变量以及导出CPU信息相关的变量。但是ARCH这些事哪里导出来的呢?在config.mk文件里有定义变量,那些变量最终是存在.config文件中的。
scripts/Kbuild.include: ;
include scripts/Kbuild.include
AS = $(CROSS_COMPILE)as
ifneq ($(shell $(CROSS_COMPILE)ld.bfd -v 2> /dev/null),)
LD = $(CROSS_COMPILE)ld.bfd
else
LD = $(CROSS_COMPILE)ld
endif
CC = $(CROSS_COMPILE)gcc
#... ...
export ARCH CPU BOARD VENDOR SOC CPUDIR BOARDDIR
export CONFIG_SHELL HOSTCC HOSTCFLAGS HOSTLDFLAGS CROSS_COMPILE AS LD CC
export CPP AR NM LDR STRIP OBJCOPY OBJDUMP
export MAKE AWK PERL PYTHON
export HOSTCXX HOSTCXXFLAGS DTC CHECK CHECKFLAGS
#... ...
三、编译处理过程
make xxx_defconfig
make V=1
10.xxx_defconfig编译规则(395-480)
主要还是后面%config这两行,%是通配符。前面省略的有scripts_basic outputmakefile目标变量的生成规则。FORCE变量是总是执行。
KBUILD_SRC为空,则outputmakefile变量无效。
scripts_basic中的build变量在Kbuile.include中的build := -f $(srctree)/scripts/Makefile.build obj
最终make defconfig执行的是:
PHONY += scripts_basic
scripts_basic:
$(Q)$(MAKE) $(build)=scripts/basic
$(Q)rm -f .tmp_quiet_recordmcount
PHONY += outputmakefile
outputmakefile:
ifneq ($(KBUILD_SRC),)
$(Q)ln -fsn $(srctree) source
$(Q)$(CONFIG_SHELL) $(srctree)/scripts/mkmakefile \
$(srctree) $(objtree) $(VERSION) $(PATCHLEVEL)
endif
%config: scripts_basic outputmakefile FORCE
$(Q)$(MAKE) $(build)=scripts/kconfig $@
PHONY += FORCE
FORCE:
#最终执行的是
#@make -f $(srctree)/scripts/Makefile.build obj=scripts/basic
#@make -f $(srctree)/scripts/Makefile.build obj=scripts/kconfig xxx_defconfig
第一条命令:)/scripts/Makefile.build的obj与src进行比较。prefix=.执行默认规则,则执行__build。__build对应于scipts/basic/fixdep对应的fixdep.c软件
src = scripts/basic用于编译出fixdep.c。在scripts/basic/下的Makefile用于编译。
第二条命令:
src肯定会改变。在makefile.build中的
%_defconfig:scripts/kconfig/conf
$(Q)$< $(silent) --defconfig=arch/../configs/xxx_defconfig
11.make(802-816)
默认目标_all , 依赖于all ,又依赖于ALL-y,有很多。
PHONY += all
ifeq ($(KBUILD_EXTMOD),)
_all: all
ALL-y += u-boot.srec u-boot.bin u-boot.sym System.map u-boot.cfg binary_size_check
all: $(ALL-y)
ifneq ($(CONFIG_SYS_GENERIC_BOARD),y)
@echo "===================== WARNING ======================"
@echo "Please convert this board to generic board."
@echo "Otherwise it will be removed by the end of 2014."
@echo "See doc/README.generic-board for further information"
@echo "===================================================="
endif
ifeq ($(CONFIG_DM_I2C_COMPAT),y)
@echo "===================== WARNING ======================"
@echo "This board uses CONFIG_DM_I2C_COMPAT. Please remove"
@echo "(possibly in a subsequent patch in your series)"
@echo "before sending patches to the mailing list."
@echo "===================================================="
endif
编译ALL会生成很多东西,我们主要来看u-boot.bin的依赖。
u-boot.bin: u-boot-nodtb.bin FORCE 。其中u-boot.bin依赖于u-boot-nodtb.bin。而它又依赖u-boot
u-boot-nodtb.bin: u-boot FORCE
$(call if_changed,objcopy)
$(call DO_STATIC_RELA,$<,$@,$(CONFIG_SYS_TEXT_BASE))
$(BOARD_SIZE_CHECK)
u-boot: $(u-boot-init) $(u-boot-main) u-boot.lds FORCE
$(call if_changed,u-boot__)
ifeq ($(CONFIG_KALLSYMS),y)
$(call cmd,smap)
$(call cmd,u-boot__) common/system_map.o
endif
u-boot-init := $(head-y)
u-boot-main := $(libs-y)
#在arch/arm/Makefile中
#即head-y := arch/arm/cpu/armv7/start.o
head-y := arch/arm/cpu/$(CPU)/start.o
#libs-y的依赖
libs-y += lib/
libs-$(HAVE_VENDOR_COMMON_LIB) += board/$(VENDOR)/common/
libs-$(CONFIG_OF_EMBED) += dts/
libs-y += fs/
libs-y += net/
#... ...
libs-y := $(patsubst %/, %/built-in.o, $(libs-y))
#... ...
libs-y就是将大量的built-in.o保存下来。那么u-boot就是将built-in.o与start.o的链接一起,生成u-boot。
那么问题来了,start.o是由start.s得来,built-in.o是从何而来?
在drivers/下的每个外设下有built-in.o.cmd文件,存放命令。
cmd_drivers/mmc/built-in.o := \
arm-linux-gnueabihf-ld.bfd \
-r -o drivers/mmc/built-in.o \
drivers/mmc/fsl_esdhc.o drivers/mmc/mmc.o \
drivers/mmc/mmc_write.o
12.make dtbs(818-820)
PHONY += dtbs
dtbs dts/dt.dtb: checkdtc u-boot
$(Q)$(MAKE) $(build)=dts dtbs
链接脚本
链接脚本u-boot.lds。链接首地址是0x87800000。链接的是start.o 与大量built-in.o
0x87800000在include/configs/mx6-common.h文件中定义。