笔者对Makefile不熟,一点一点分析代码,要是出错了麻烦各位大佬在评论指出。感激不尽。
第一篇,先把Makefile前面的变量和环境分析完吧。分析到all目标,然后后面的就跟着make时的流程走应该会比较好理解。
VERSION = 1
PATCHLEVEL = 1
SUBLEVEL = 6
EXTRAVERSION =
U_BOOT_VERSION = $(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION)
VERSION_FILE = $(obj)include/version_autogenerated.h
HOSTARCH := $(shell uname -m | \
sed -e s/i.86/i386/ \
-e s/sun4u/sparc64/ \
-e s/arm.*/arm/ \
-e s/sa110/arm/ \
-e s/powerpc/ppc/ \
-e s/macppc/ppc/)
HOSTOS := $(shell uname -s | tr '[:upper:]' '[:lower:]' | \
sed -e 's/\(cygwin\).*/cygwin/')
export HOSTARCH HOSTOS
VERSION:主版本号
PATCHLEVEL:补丁版本号
SUBLEVEL:次版本号
EXTRAVERSION:附加版本信息
U_BOOT_VERSION:1.1.6
HOSTARCH:使用uname -m(获取CPU架构,这个看自己的电脑),通过管道输入到sed命令,sed命令将i.86替换为i386,将sun4u替换为sparc64…
HOSTOS:使用uname -s(获取操作系统),通过管道输入到tr,tr命令将大写字符转换成小写字符,通过管道输入到sed,sed将"(cygwin).*“替换为"cygwin”。
接着,使用export将主机架构和操作系统传递到子Makefile。
将这些变量输出的结果为:
执行make mytest
# Deal with colliding definitions from tcsh etc. 处理来自tcsh等的冲突定义
VENDOR=
#########################################################################
#
# U-boot build supports producing a object files to the separate external
# directory. Two use cases are supported:
#
# 1) Add O= to the make command line
# 'make O=/tmp/build all'
#
# 2) Set environement variable BUILD_DIR to point to the desired location
# 'export BUILD_DIR=/tmp/build'
# 'make'
#
# The second approach can also be used with a MAKEALL script
# 'export BUILD_DIR=/tmp/build'
# './MAKEALL'
#
# Command line 'O=' setting overrides BUILD_DIR environent variable.
#
# When none of the above methods is used the local build is performed and
# the object files are placed in the source directory.
#
ifdef O
ifeq ("$(origin O)", "command line")
BUILD_DIR := $(O)
endif
endif
ifneq ($(BUILD_DIR),)
saved-output := $(BUILD_DIR)
# Attempt to create a output directory.
$(shell [ -d ${BUILD_DIR} ] || mkdir -p ${BUILD_DIR})
# Verify if it was successful.
BUILD_DIR := $(shell cd $(BUILD_DIR) && /bin/pwd)
$(if $(BUILD_DIR),,$(error output directory "$(saved-output)" does not exist))
endif # ifneq ($(BUILD_DIR),)
OBJTREE := $(if $(BUILD_DIR),$(BUILD_DIR),$(CURDIR))
SRCTREE := $(CURDIR)
TOPDIR := $(SRCTREE)
LNDIR := $(OBJTREE)
export TOPDIR SRCTREE OBJTREE
MKCONFIG := $(SRCTREE)/mkconfig
export MKCONFIG
ifneq ($(OBJTREE),$(SRCTREE))
REMOTE_BUILD := 1
export REMOTE_BUILD
endif
ifeq ("$(origin O)", “command line”): 如果O的内容是来自命令行的,也就是在执行make的时候带了O参数。
BUILD_DIR := $(O): 将O的内容赋给BUILD_DIR变量。
ifneq ($(BUILD_DIR),): 如果BUILD_DIR不为空。
saved-output := $(BUILD_DIR): 则将BUILD_DIR变量中的内容保存在saved-output变量中,此时saved-output变量保存的肯定是从命令行输入的O的内容。
$(shell [ -d ${BUILD_DIR} ] || mkdir -p ${BUILD_DIR}): 然后用-d 参数测试BUILD_DIR变量内容是不是一个目录,并且建立这个目录(如果目录不存在的话)。
BUILD_DIR := $(shell cd $(BUILD_DIR) && /bin/pwd): 切换到目标目录下,使用pwd命令获取路径并且再次复制给BUILD_DIR,代码里给出的解释是“Verify if it was successful”,应该是验证上面创建文件夹的操作是否成功。
$(if $(BUILD_DIR),,$(error output directory “$(saved-output)” does not exist)): 如果BUILD_DIR有数据,则不作任何操作,如果没有输出错误信息提示目标文件夹不存在。
OBJTREE := $(if $(BUILD_DIR),$(BUILD_DIR),$(CURDIR)): 如果BUILD_DIR有数据,则将BUILD_DIR付给OBJTREE变量,否则OBJTREE为当前路径。
SRCTREE := $(CURDIR): SRCTREE为当前路径 = 顶层Makefile所在路径。
TOPDIR := $(SRCTREE): TOPDIR = SRCTREE = 当前路径 = 顶层Makefile所在路径。
LNDIR := $(OBJTREE): LNDIR = OBJTREE = 设置的目标文件夹或者= 顶层Makefile所在路径。
export TOPDIR SRCTREE OBJTREE: 导出到子Makefile。
MKCONFIG := $(SRCTREE)/mkconfig: MKCONFIG为顶层Makefile所在路径下的mkconfig工具。
export MKCONFIG: 导出MKCONFIG到子Makefile。
ifneq ($(OBJTREE),$(SRCTREE))
REMOTE_BUILD := 1
export REMOTE_BUILD
endif: 如果OBJTREE不等于SRCTREE,也就是在make时使用O参数指定了目标文件夹,则REMOTE_BUILD赋值为1,并且将REMOTE_BUILD导出。
其实在上面的英文说明也有讲到O参数是用来设置编译uboot产生的目标文件路径。
我在uboot-1.1.6版本的uboot中没有找到help参数,在最新的uboot-2020.07版本中有help参数,可以将uboot提供的操作输出出来。这里贴出输出的部分信息,其中就有O参数的说明:
# $(obj) and (src) are defined in config.mk but here in main Makefile
# we also need them before config.mk is included which is the case for
# some targets like unconfig, clean, clobber, distclean, etc.
ifneq ($(OBJTREE),$(SRCTREE))
obj := $(OBJTREE)/
src := $(SRCTREE)/
else
obj :=
src :=
endif
export obj src
ifneq ($(OBJTREE),$(SRCTREE)): 如果在,make时使用O变量指定了目标文件夹路径,则:
obj := $(OBJTREE)/: obj赋值为目标文件夹路径。
src := $(SRCTREE)/: src为当前路径= 顶层Makefile所在路径。
else
obj :=
src :=
endif: 否则,obj和src都为空。
export obj src: 导出obj和src。
后面的分析以不使用O变量指定目标文件夹路径为准。
ifeq ($(OBJTREE)/include/config.mk,$(wildcard $(OBJTREE)/include/config.mk))
# load ARCH, BOARD, and CPU configuration
include $(OBJTREE)/include/config.mk
export ARCH CPU BOARD VENDOR SOC
ifndef CROSS_COMPILE
ifeq ($(HOSTARCH),ppc)
CROSS_COMPILE =
else
ifeq ($(ARCH),ppc)
CROSS_COMPILE = powerpc-linux-
endif
ifeq ($(ARCH),arm)
CROSS_COMPILE = arm-linux-
endif
ifeq ($(ARCH),i386)
ifeq ($(HOSTARCH),i386)
CROSS_COMPILE =
else
CROSS_COMPILE = i386-linux-
endif
endif
ifeq ($(ARCH),mips)
CROSS_COMPILE = mips_4KC-
endif
ifeq ($(ARCH),nios)
CROSS_COMPILE = nios-elf-
endif
ifeq ($(ARCH),nios2)
CROSS_COMPILE = nios2-elf-
endif
ifeq ($(ARCH),m68k)
CROSS_COMPILE = m68k-elf-
endif
ifeq ($(ARCH),microblaze)
CROSS_COMPILE = mb-
endif
ifeq ($(ARCH),blackfin)
CROSS_COMPILE = bfin-elf-
endif
ifeq ($(ARCH),avr32)
CROSS_COMPILE = avr32-
endif
endif
endif
export CROSS_COMPILE
# load other configuration
include $(TOPDIR)/config.mk
ifeq ($(OBJTREE)/include/config.mk,$(wildcard $(OBJTREE)/include/config.mk)): 先解释下wildcard,wildcard会匹配目录下的文件,匹配方式由后面的参数部分指定。如果没有匹配到相应的文件则返回空,否则返回匹配的文件,中间用空格隔开。因此,这一句的解释就是检查是否有OBJTREE)/include/config.mk这个文件。
记住这个ifeq,它很长,长到all目标那边
include $(OBJTREE)/include/config.mk: 包含config.mk文件,这个文件的内容其实就是目标板子的一些信息,如下所示:
ARCH = arm
CPU = arm920t
BOARD = smdk2410
SOC = s3c24x0
注:config.mk文件是在执行make xxx_config时调用mkconfig脚本生成的。后面这四个变量以上述信息为例
export ARCH CPU BOARD VENDOR SOC: 导出到子Makefile中。
随后出现的十几行代码,都是在确定CROSS_COMPILE编译器类型,并且导出到子Makefile中。
include $(TOPDIR)/config.mk: 加载顶层目录下的config.mk文件,顶层目录下的config.mk根据ARCH、CPU、BOARD、SOC这四个变量确定了编译器和编译选项等信息。
#########################################################################
# U-Boot objects....order is important (i.e. start must be first)
OBJS = cpu/$(CPU)/start.o
ifeq ($(CPU),i386)
OBJS += cpu/$(CPU)/start16.o
OBJS += cpu/$(CPU)/reset.o
endif
ifeq ($(CPU),ppc4xx)
OBJS += cpu/$(CPU)/resetvec.o
endif
ifeq ($(CPU),mpc83xx)
OBJS += cpu/$(CPU)/resetvec.o
endif
ifeq ($(CPU),mpc85xx)
OBJS += cpu/$(CPU)/resetvec.o
endif
ifeq ($(CPU),mpc86xx)
OBJS += cpu/$(CPU)/resetvec.o
endif
ifeq ($(CPU),bf533)
OBJS += cpu/$(CPU)/start1.o cpu/$(CPU)/interrupt.o cpu/$(CPU)/cache.o
OBJS += cpu/$(CPU)/cplbhdlr.o cpu/$(CPU)/cplbmgr.o cpu/$(CPU)/flush.o
endif
OBJS := $(addprefix $(obj),$(OBJS))
LIBS = lib_generic/libgeneric.a
LIBS += board/$(BOARDDIR)/lib$(BOARD).a
LIBS += cpu/$(CPU)/lib$(CPU).a
ifdef SOC
LIBS += cpu/$(CPU)/$(SOC)/lib$(SOC).a
endif
LIBS += lib_$(ARCH)/lib$(ARCH).a
LIBS += fs/cramfs/libcramfs.a fs/fat/libfat.a fs/fdos/libfdos.a fs/jffs2/libjffs2.a \
fs/reiserfs/libreiserfs.a fs/ext2/libext2fs.a
LIBS += net/libnet.a
LIBS += disk/libdisk.a
LIBS += rtc/librtc.a
LIBS += dtt/libdtt.a
LIBS += drivers/libdrivers.a
LIBS += drivers/nand/libnand.a
LIBS += drivers/nand_legacy/libnand_legacy.a
LIBS += drivers/sk98lin/libsk98lin.a
LIBS += post/libpost.a post/cpu/libcpu.a
LIBS += common/libcommon.a
LIBS += $(BOARDLIBS)
LIBS := $(addprefix $(obj),$(LIBS))
.PHONY : $(LIBS)
OBJS = cpu/$(CPU)/start.o: OBJS = cpu/arm920t/start.o。
再根据CPU的类型往OBJS变量添加一些.o文件,这里CPU的类型为arm920t,所以此时的OBJS变量值为cpu/arm920t/start.o。
OBJS := $(addprefix
(
o
b
j
)
,
(obj),
(obj),(OBJS)): 调用文件名操作函数addprefix,在OBJS加上前缀obj,因为没有使用O变量,所以OBJS依然为cpu/arm920t/start.o。
接着,在LIBS变量中添加了相关的和通用的库文件,同样地给LIBS加上前缀obj。
# Add GCC lib
PLATFORM_LIBS += -L $(shell dirname `$(CC) $(CFLAGS) -print-libgcc-file-name`) -lgcc
-L加库路径(使用dirname变量获取编译器和各种参数的所在的路径),使用-lgcc 代表链接器将连接GCC的支持库libgcc.a。看解释说Add GCC lib,所以PLATFORM_LIBS 应该是作为一个参数的存在。
# The "tools" are needed early, so put this first
# Don't include stuff already done in $(LIBS)
SUBDIRS = tools \
examples \
post \
post/cpu
.PHONY : $(SUBDIRS)
ifeq ($(CONFIG_NAND_U_BOOT),y)
NAND_SPL = nand_spl
U_BOOT_NAND = $(obj)u-boot-nand.bin
endif
__OBJS := $(subst $(obj),,$(OBJS))
__LIBS := $(subst $(obj),,$(LIBS))
SUBDIRS : tools examples post post/cpu。
ifeq ($(CONFIG_NAND_U_BOOT),y): 如果CONFIG_NAND_U_BOOT等于y,应该是在xxx_config文件里或者其他makefile中有这样定义:CONFIG_NAND_U_BOOT+=y。
NAND_SPL = nand_spl: 则NAND_SPL =nand_spl
U_BOOT_NAND = $(obj)u-boot-nand.bin: U_BOOT_NAND = u-boot-nand.bin。
endif
__OBJS := $(subst
(
o
b
j
)
,
,
(obj),,
(obj),,(OBJS)): __OBJS = 去掉obj前缀的OBJS。
__LIBS := $(subst
(
o
b
j
)
,
,
(obj),,
(obj),,(LIBS)): __LIBS = 去掉obj前缀的LIBS。
#########################################################################
#########################################################################
ALL = $(obj)u-boot.srec $(obj)u-boot.bin $(obj)System.map $(U_BOOT_NAND)
all: $(ALL)
$(obj)u-boot.hex: $(obj)u-boot
$(OBJCOPY) ${OBJCFLAGS} -O ihex $< $@
$(obj)u-boot.srec: $(obj)u-boot
$(OBJCOPY) ${OBJCFLAGS} -O srec $< $@
$(obj)u-boot.bin: $(obj)u-boot
$(OBJCOPY) ${OBJCFLAGS} -O binary $< $@
$(obj)u-boot.img: $(obj)u-boot.bin
./tools/mkimage -A $(ARCH) -T firmware -C none \
-a $(TEXT_BASE) -e 0 \
-n $(shell sed -n -e 's/.*U_BOOT_VERSION//p' $(VERSION_FILE) | \
sed -e 's/"[ ]*$$/ for $(BOARD) board"/') \
-d $< $@
$(obj)u-boot.dis: $(obj)u-boot
$(OBJDUMP) -d $< > $@
$(obj)u-boot: depend version $(SUBDIRS) $(OBJS) $(LIBS) $(LDSCRIPT)
UNDEF_SYM=`$(OBJDUMP) -x $(LIBS) |sed -n -e 's/.*\(__u_boot_cmd_.*\)/-u\1/p'|sort|uniq`;\
cd $(LNDIR) && $(LD) $(LDFLAGS) $$UNDEF_SYM $(__OBJS) \
--start-group $(__LIBS) --end-group $(PLATFORM_LIBS) \
-Map u-boot.map -o u-boot
先说明下u-boot.srec为S-Record格式的image;
u-boot.bin为原始二进制文件的image;
System.map按链接地址由小到大的顺序列出了所有符号,可以称之为系统映射表;
U_BOOT_NAND = u-boot-nand.bin,应该是烧录到nand flash的二进制格式的image;因此,
ALL: = u-boot.srec u-boot.bin System.map (u-boot-nand.bin如果CONFIG_NAND_U_BOOT不为y就没有)
all: $(ALL):这里就是编译uboot的目标all了,all目标没有相应命令,所以要实现的就是相关的依赖文件,也就是ALL了。
未完待续。。。