uboot 主Makefile分析
一. Makefile 配置
1.1. make xxx_config
1.1.1. 笔者实验时是make x210_sd_config
a. x210_sd_config是Makefile下的一个目标
1.2.1. x210_sd_config 相关代码分析
a. @表示静默执行
b. MKCONFIG是Makefile的一个变量,它所表示的是一个mkconfig脚本文件
MKCONFIG := $(SRCTREE)/mkconfig
c. $(@:_config=)其结果是x210_sd,它来源于$(var:xx=yy)此句表示将变量var中以xx结尾的部分替换成yy。$@代表目标文件x210_sd_config.注意在$()的括号中的变量是不需要再加$的
d. shell传参
$1: x210_sd
$2: arm
$3: s5pc11x
$4: x210
$5: samsumg
$6: s5pc110
$# = 6
x210_sd_config : unconfig @$(MKCONFIG) $(@:_config=) arm s5pc11x x210 samsung s5pc110 @echo "TEXT_BASE = 0xc3e00000" > $(obj)board/samsung/x210/config.mk
1.2. mkconfig脚本分析
1.2.1. 创建各种链接符号
1.2.1.1. Create link to architecture specific headers
a. 在include目录下创建asm文件,指向asm-arm。
# # Create link to architecture specific headers # if [ "$SRCTREE" != "$OBJTREE" ] ; then mkdir -p ${OBJTREE}/include mkdir -p ${OBJTREE}/include2 cd ${OBJTREE}/include2 rm -f asm ln -s ${SRCTREE}/include/asm-$2 asm LNPREFIX="../../include2/asm/" cd ../include rm -rf asm-$2 rm -f asm mkdir asm-$2 ln -s asm-$2 asm else cd ./include rm -f asm ln -s asm-$2 asm fi
1.2.1.1. create link for s5pc11x SoC
a. 在include目录下创建regs.h文件,指向include/s5pc110.h
b. 在inlcude/asm-arm下创建一个arch文件,指向include/asm-arm/arch-s5pc11x
# create link for s5pc11x SoC if [ "$3" = "s5pc11x" ] ; then rm -f regs.h ln -s $6.h regs.h rm -f asm-$2/arch ln -s arch-$3 asm-$2/arch fi
1.2.1.2. proc文件链接
a. 在include/asm-arm下创建一个proc文件,指向include/asm-arm/proc-armv
if [ "$2" = "arm" ] ; then rm -f asm-$2/proc ln -s ${LNPREFIX}proc-armv asm-$2/proc fi
1.2.2. 创建include/config.mk文件
a. 编译的时候需要ARCH=arm、CPU=xx等这些变量来指导编译
# # Create include file for Make # echo "ARCH = $2" > config.mk echo "CPU = $3" >> config.mk echo "BOARD = $4" >> config.mk [ "$5" ] && [ "$5" != "NULL" ] && echo "VENDOR = $5" >> config.mk [ "$6" ] && [ "$6" != "NULL" ] && echo "SOC = $6" >> config.mk
1.2.3. 创建config.h文件
a. x210_sd.h文件会被用来生成一个config.mk文件,这个文件会被主Makefile引入,指导整个编译过程。这里面的这些宏定义会影响我们对uboot中大部分.c文件中一些条件编译的选择。从而实现最终的可移植性。
b. #include <configs/x210_sd.h>里的宏用.c或.h里代码的选择,而mkconfig中创建的链接符号相当于文件的选择
# # Create board specific header file # if [ "$APPEND" = "yes" ] # Append to existing config file then echo >> config.h else > config.h # Create new config file fi echo "/* Automatically generated - do not edit */" >>config.h echo "#include <configs/$1.h>" >>config.h
二. Makefile 分析
2.1. 这部分主要和uboot软件版本相关
VERSION = 1 PATCHLEVEL = 3 SUBLEVEL = 4 EXTRAVERSION = U_BOOT_VERSION = $(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION) VERSION_FILE = $(obj)include/version_autogenerated.h
2.2. 这部分主要获取主机(用于开发的PC)的架构和OS
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/ppc64/ppc/ \ -e s/macppc/ppc/) HOSTOS := $(shell uname -s | tr '[:upper:]' '[:lower:]' | \ sed -e 's/\(cygwin\).*/cygwin/') export HOSTARCH HOSTOS
2.3. 这部分注释说和静默编译有关,但我并不明白为什么有关,因为有没有他使用make -s都可以实现默编。
PS:命令的回显
makefile在执行命令的时候,通常会将命令显示在标准输出中,如果不想显示在标准输出中,可以使用下面的方法
1.在每个命令的前面使用@字符串如下:
这样在执行命令的时候,就不会讲这个命令回显在标准输出中
2.使用命令行参数
在make执行的命令输入中,键入-s,–silent选项,既可,它表示所有的命令都不会回显在标准输出中。
3.使用.SLIENT
给.SLIENT目标添加依赖,那么出现在依赖列表中的目标,重建的时候,将不会回显命令
######################################################################### # Allow for silent builds ifeq (,$(findstring s,$(MAKEFLAGS))) XECHO = echo else XECHO = : endif #########################################################################
2.4. 两种编译方法
2.4.1. 原地编译
a. 默认情况下是当前文件夹中的.c文件,编译出来的.o文件会放在同一文件夹下。这种方式叫原地编译。原地编译的好处就是处理起来简单。
b. 原地编译有一些坏处:第一,污染了源文件目录。第二的缺陷就是一套源代码只能按照一种配置和编译方法进行处理,无法同时维护2个或2个以上的配置编译方式。
2.4.2. 单独输出文件夹编译
a. uboot支持单独输出文件夹方式的编译(linux kernel也支持,而且uboot的这种技术就是从linux kernel学习来的)。基本思路就是在编译时另外指定一个输出目录,将来所有的编译生成的.o文件或生成的其他文件全部丢到那个输出目录下去。源代码目录不做任何污染,这样输出目录就承载了本次配置编译的所有结果。
b. 具体实现方法:
如果需要指定具体的输出目录编译则有2种方式来指定输出目录。(具体参考Makefile 56-76行注释内容)
第一种:make O=输出目录
第二种:export BUILD_DIR=输出目录 然后再make
如果两个都指定了(既有BUILD_DIR环境变量存在,又有O=xx),则O=xx具有更高优先级
2.4.3. 实现代码如下:
a. 代码中OBJTREE := $(if $(BUILD_DIR),$(BUILD_DIR),$(CURDIR))意思是:如果“BUILD_DIR”变量值不为空,则将变量“BUILD_DIR”指定到OBJTREE,否则“CURDIR”指定到OBJTREE
######################################################################### # # 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 # $(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 # Make sure CDPATH settings don't interfere unexport CDPATH
2.5. 交叉编译链工具设置
2.5.1. 判断config.mk是否存在
a. 该文件是由make xxxx_config 配置时生成的
b. ifeq ($(obj)include/config.mk,$(wildcard $(obj)include/config.mk))
c. 如果存在则进行后续的工具链配置
ifeq ($(ARCH),powerpc) ARCH = ppc endif ifeq ($(obj)include/config.mk,$(wildcard $(obj)include/config.mk)) # load ARCH, BOARD, and CPU configuration include $(obj)include/config.mk export ARCH CPU BOARD VENDOR SOC ifndef CROSS_COMPILE ifeq ($(HOSTARCH),$(ARCH)) CROSS_COMPILE = else ifeq ($(ARCH),ppc) CROSS_COMPILE = ppc_8xx- endif ifeq ($(ARCH),arm) #CROSS_COMPILE = arm-linux- #CROSS_COMPILE = /usr/local/arm/4.4.1-eabi-cortex-a8/usr/bin/arm-linux- #CROSS_COMPILE = /usr/local/arm/4.2.2-eabi/usr/bin/arm-linux- CROSS_COMPILE = /usr/local/arm/arm-2009q3/bin/arm-none-linux-gnueabi- endif ifeq ($(ARCH),i386) CROSS_COMPILE = i386-linux- 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-uclinux- endif ifeq ($(ARCH),avr32) CROSS_COMPILE = avr32-linux- endif ifeq ($(ARCH),sh) CROSS_COMPILE = sh4-linux- endif ifeq ($(ARCH),sparc) CROSS_COMPILE = sparc-elf- endif # sparc endif # HOSTARCH,ARCH endif # CROSS_COMPILE export CROSS_COMPILE
2.5. OBJS和LIBS变量
a. OBJS代表目标文件,本例中只会包含start.o。LIBS是库文件,主要包含以下文件夹lib_generic,cpu,fs,driver,commom等,下的一些.a库文件
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),mpc85xx) OBJS += cpu/$(CPU)/resetvec.o endif OBJS := $(addprefix $(obj),$(OBJS)) LIBS = lib_generic/libgeneric.a LIBS += $(shell if [ -f board/$(VENDOR)/common/Makefile ]; then echo \ "board/$(VENDOR)/common/lib$(VENDOR).a"; fi) LIBS += cpu/$(CPU)/lib$(CPU).a ifdef SOC LIBS += cpu/$(CPU)/$(SOC)/lib$(SOC).a endif ifeq ($(CPU),ixp) LIBS += cpu/ixp/npe/libnpe.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 += drivers/bios_emulator/libatibiosemu.a LIBS += drivers/block/libblock.a LIBS += drivers/dma/libdma.a LIBS += drivers/hwmon/libhwmon.a LIBS += drivers/i2c/libi2c.a LIBS += drivers/input/libinput.a LIBS += drivers/misc/libmisc.a LIBS += drivers/mmc/libmmc.a LIBS += drivers/mtd/libmtd.a LIBS += drivers/mtd/nand/libnand.a LIBS += drivers/mtd/nand_legacy/libnand_legacy.a LIBS += drivers/mtd/onenand/libonenand.a LIBS += drivers/mtd/ubi/libubi.a LIBS += drivers/mtd/spi/libspi_flash.a LIBS += drivers/net/libnet.a LIBS += drivers/net/sk98lin/libsk98lin.a LIBS += drivers/pci/libpci.a LIBS += drivers/pcmcia/libpcmcia.a LIBS += drivers/spi/libspi.a ifeq ($(CPU),mpc83xx) LIBS += drivers/qe/qe.a endif ifeq ($(CPU),mpc85xx) LIBS += drivers/qe/qe.a endif LIBS += drivers/rtc/librtc.a LIBS += drivers/serial/libserial.a LIBS += drivers/usb/libusb.a LIBS += drivers/video/libvideo.a LIBS += common/libcommon.a LIBS += libfdt/libfdt.a LIBS += api/libapi.a LIBS += post/libpost.a LIBS := $(addprefix $(obj),$(LIBS)) .PHONY : $(LIBS) $(VERSION_FILE) LIBBOARD = board/$(BOARDDIR)/lib$(BOARD).a LIBBOARD := $(addprefix $(obj),$(LIBBOARD)) # Add GCC lib PLATFORM_LIBS += -L $(shell dirname `$(CC) $(CFLAGS) -print-libgcc-file-name`) -lgcc # The "tools" are needed early, so put this first # Don't include stuff already done in $(LIBS) SUBDIRS = tools \ examples \ api_examples .PHONY : $(SUBDIRS) ifeq ($(CONFIG_NAND_U_BOOT),y) NAND_SPL = nand_spl U_BOOT_NAND = $(obj)u-boot-nand.bin endif ifeq ($(CONFIG_ONENAND_U_BOOT),y) ONENAND_IPL = onenand_bl1 U_BOOT_ONENAND = $(obj)u-boot-onenand.bin endif __OBJS := $(subst $(obj),,$(OBJS)) __LIBS := $(subst $(obj),,$(LIBS)) $(subst $(obj),,$(LIBBOARD))
2.6. 其他的
a. 其他的都是目标,这里不再分析了。