一、概述  

1、理解u-boot的makefile需要的准备

  linux常用命令、shell脚本基础知识、makefile脚本基础知识

2、Makefile的元素

万变不离其宗,无论工程多么复杂,文件多么庞大,其实源于最简单的makefile。Makefile典型的规则如下。

目标:依赖1,依赖2••••••

       命令

举一个简单的例子

nand.bin : head.o nand.o main.o
arm-linux-ld -Tnand.lds -o nand_elf head.o main.o
arm-linux-objcopy -O binary -S nand_elf nand.bin

head.o:head.S
arm-linux-gcc -Wall -c -o head.o head.S

nand.o:nand.c s3c2440_addr.h
arm-linux-gcc -Wall -c -o nand.o nand.c

main.o:main.c s3c2440_addr.h
arm-linux-gcc -Wall -c -o main.o main.c

clean:
rm -f nand.bin nand_elf head.o nand.o main.o

形象的表达:待实现一个产品(目标),这个目的需要确定的原材料才能实现(依赖),还需要一套加工手段来制作(编译规则)。

当最初的makefile设计完成后,为了实现可裁剪、自动化编译等目的,不得不加入更多的makefile规则,当然make工具相应的功能也需要增加。可以肯定:这些增加的规则无非是更加方便、灵活的生成指定目标,更加方便的生成依赖关系,更加方便的生成编译规则。

举例:

    更加方便灵活的的生成指定目标:通过选项指定生成目标的路径

    更加方便的生成依赖:自动生成依赖文件、通过变量选择编译的源码

    更加方便的生成指定规则:隐含规则、模式规则、通过变量选择使用的编译器及选项

3、u-boot Makefile体系的组成  

   Uboot是一个庞大的工程,需要从众多的源文件中选择部分源文件,采用合适的编译手段,生成特定的目标文件。这不是一个简单的任务,需要顶层的makefile,众多的顶层makefile,makefile的include文件(config.mk、rules.mk)等来完成这个工作。

u-bootMakefile 体系
名    称 描    述
顶层Makefile 从总体上控制着u-boot的编译、连接,定义总目标u-boot.bin
顶层config.mk 规定了编译的规则,被所有Makefile所调用
顶层rules.mk 生成依赖关系,被各级子目录Makefile所调用
各级子目录Makefile 决定当前目录的编译、连接
顶层mkconfig 在编译之前运行,为编译做准备

  mkconfig(shel l脚本)是在编译之前做一些准备,笔者认为可以不算做Makefile集体中的一部分。

u-boot编译过程中生成的与编译相关的文件
名称 描述
include/config.mk mkconfig所生成,被顶层Makefile所包含,定义ARCH、CPU等全局变量
各级子目录.depend 各级子目录makefile所生成,并被各级子目录makefile所包含,定义依赖关系

二、Makefile的目标

1、Makefile的总目标

顶层Makefile的总目标all是一个伪目标,有几个分目标(目标文件)组成:$(obj)u-boot.srec $(obj)u-boot.bin $(obj)System.map $(U_BOOT_NAND)。其实,用户也可以添加其他目标,如$(obj)u-boot.dis、$(obj)u-boot.img、$(obj)u-boot.hex等等。由于是一个伪目标,所以只要输入命令make all,总会重建几个分目标。

ALL = $(obj)u-boot.srec $(obj)u-boot.bin $(obj)System.map $(U_BOOT_NAND)

2、Makefile的各级子目录中的目标

  子目录中的all定义了当前目录所有要生成的目标,LIB定义了当前目录要生成的静态库,OBJS定义了当前目录由“.c”文件编译生成的目标“.o”,SOBJS定义了当前目录由“.S”文件编译生成的目标“.o”。

all:    $(obj).depend $(START) $(LIB)
$(LIB): $(obj).depend $(OBJS) $(SOBJS)

三、Makefile的依赖

1、总目标的依赖

$(obj)u-boot:        depend version $(SUBDIRS) $(OBJS) $(LIBS) $(LDSCRIPT)

  总目标的依赖是由“depend version $(SUBDIRS) $(OBJS) $(LIBS) $(LDSCRIPT)”构成,重要的元素是“$(OBJS) $(LIBS)”。下面,我们看看“$(OBJS) $(LIBS)”,它们又是什么构成的,从而查看u-boot是怎样选择参与编译连接的文件的。

编译的过程是(*.c *.S) -- > (*.o *.a) --> (*.bin),但是make工具分析Makefile的过程是相反的。为了生成(*.bin) 需要依赖(*.o *.a),为了生成(*.o *.a)间接的又需要(*.c *.S),可以说(*.c *.S)是最初的依赖(原材料)。

Uboot是一个通用的启动代码,可以在众多的硬件平台上例如arm、powerpc,和操作系统上运行,例如vxworks、linux等等。源码中有相应的源文件包,例如lib_ppc,lib_arm等等。其实,还有各种驱动,网卡、显示器、串口、键盘等等。但是,对于一个特定的应用目标,它的硬件平台是确定的,所用的操作系统也是确定的,它的外设也是确定的。所以,需要对这些源文件进行裁剪。

在执行make all之前,首先要执行make *_config例如make smdk2410_config,通过执行mkconfig脚本,可以确定所用cpu的架构(ARCH)例如arm ,cpu的种类(CPU)例如arm920t,开发板(BOARD)例如smdk2410 ,soc(SOC)例如 s3c24x0,这些信息保存在mkconfig脚本生成的makefile包含文件include/config.mk中。include/config.mk会被包含进入顶层makefile中,根据这个文件所定义的变量值,从而确定用那些文件(依赖),例如lib_arm/,board/smdk2410,cpu/arm920t等等。这是一种大方向上的裁剪方式。

至于所用外设之类的裁剪方式,不是通过makefile来实现的,而是通过C语言的预处理实现的,配置文件是include/configs/<board_name>.h,例如include/configs/smdk2410.h。倘若smdk2410开发板上有CS8900网卡,可以通过在配置文件中定义一个宏来实现对网卡驱动的调用,例如“#define CONFIG_DRIVER_CS8900       1”。

本文主要讨论的是makefile的分析,所以就来看看makefile是怎样裁剪源文件的。

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 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)

可以看到,通过调用ARCH、CPU、BOARD、SOC等变量来确定总目标依赖来的构成。

2、各级子母录的依赖

依赖关系对于更新了源文件重建目标文件来说是重要的。比如下边这个依赖关系,我们希望在更改了nand.c,或者更改了s3c2440_addr.h后,执行make都能重建nand.o。

nand.o:nand.c s3c2440_addr.h

通常c源文件更新了,对应的目标文件需要重建,由于模式规则(%.o:%.c)的支持,是能自动实现的。如果c源程序包含的头文件如果改变了,也需要重建此c源程序的目标文件。但是,困难在于描述c源程序依赖的头文件相当困难,因为c源程序当前文件包含的头文件可能本身还包含其他的头文件,而且人工描述头文件的依赖实在是麻烦的工作。

gcc提供了一种自动产生依赖关系的方法,例如假设想查看test.c的依赖,执行命令gcc –M test.c就OK了。

Uboot自动生成依赖关系的方法是在每个子文件夹的makefile中调用rules.mk生成的.depend依赖关系文件。

rules.mk的内容如下:

#########################################################################
_depend: $(obj).depend
$(obj).depend: $(src)Makefile $(TOPDIR)/config.mk $(SRCS)
@rm -f $@ @for f in $(SRCS); do \
g=`basename $$f | sed -e 's/\(.*\)\.\w/\1.o/'`; \
$(CC) -M $(HOST_CFLAGS) $(CPPFLAGS) -MQ $(obj)$$g $$f >> $@ ; \
done
#########################################################################

四、makefile编译规则

源文件(*.c *.S) -- > 目标文件、库文件(*.o *.a) -->总目标 (*.srec *.bin *.map)。

 1、 总目标的编译规则 库文件 (*.o *.a) -->总目标 (*.srec *.bin *.map)

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 \
-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 $(OBJS):
$(MAKE) -C cpu/$(CPU) $(if $(REMOTE_BUILD),$@,$(notdir $@)) $(LIBS):
$(MAKE) -C $(dir $(subst $(obj),,$@)) $(SUBDIRS):
$(MAKE) -C $@ all $(NAND_SPL): version
$(MAKE) -C nand_spl/board/$(BOARDDIR) all $(U_BOOT_NAND): $(NAND_SPL) $(obj)u-boot.bin
cat $(obj)nand_spl/u-boot-spl-16k.bin $(obj)u-boot.bin > $(obj)u-boot-nand.bin version:
@echo -n "#define U_BOOT_VERSION \"U-Boot " > $(VERSION_FILE); \
echo -n "$(U_BOOT_VERSION)" >> $(VERSION_FILE); \
echo -n $(shell $(CONFIG_SHELL) $(TOPDIR)/tools/setlocalversion \
$(TOPDIR)) >> $(VERSION_FILE); \
echo "\"" >> $(VERSION_FILE) gdbtools:
$(MAKE) -C tools/gdb all || exit updater:
$(MAKE) -C tools/updater all || exit env:
$(MAKE) -C tools/env all || exit depend dep:
for dir in $(SUBDIRS) ; do $(MAKE) -C $$dir _depend ; done tags ctags:
ctags -w -o $(OBJTREE)/ctags `find $(SUBDIRS) include \
lib_generic board/$(BOARDDIR) cpu/$(CPU) lib_$(ARCH) \
fs/cramfs fs/fat fs/fdos fs/jffs2 \
net disk rtc dtt drivers drivers/sk98lin common \
\( -name CVS -prune \) -o \( -name '*.[ch]' -print \)` etags:
etags -a -o $(OBJTREE)/etags `find $(SUBDIRS) include \
lib_generic board/$(BOARDDIR) cpu/$(CPU) lib_$(ARCH) \
fs/cramfs fs/fat fs/fdos fs/jffs2 \
net disk rtc dtt drivers drivers/sk98lin common \
\( -name CVS -prune \) -o \( -name '*.[ch]' -print \)` $(obj)System.map: $(obj)u-boot
@$(NM) $< | \
grep -v '\(compiled\)\|\(\.o$$\)\|\( [aUw] \)\|\(\.\.ng$$\)\|\(LASH[RL]DI\)' | \
sort > $(obj)System.map

 2、子目录中的编译规则   源文件(*.c *.S) -- > 目标文件、库文件(*.o *.a)

  子目录中的编译规则是在顶层目录的config.mk中给出,如下所示。

$(obj)%.s:      %.S
$(CPP) $(AFLAGS) -o $@ $< $(obj)%.o: %.S
$(CC) $(AFLAGS) -c -o $@ $< $(obj)%.o: %.c
$(CC) $(CFLAGS) -c -o $@ $<

库文件的编译规则在各子目录中的makefile中给出,举例board/smdk2410/libsmdk2410.a的生成规则。   board/smdk2410文件夹中的makefile内容如下:

$(LIB):   $(obj).depend $(OBJS) $(SOBJS)
$(AR) $(ARFLAGS) $@ $(OBJS) $(SOBJS)

 注意:分析Makefile的细节,必须要分析它的包含文件config.mk,它确定了程序编译、连接的选项,以及目标文件生成的规则等等内容。 

五、u-boot整个编译过程

1、makefile整体解析过程

  为了生成u-boot.bin这个文件,首先要生成构成u-boot.bin的各个库文件、目标文件。为了各个库文件、目标文件就必须进入各个子目录执行其中的Makefile。由此,确定了整个编译的命令和顺序。

2、makefile整体编译过程

  首先,根据各个库文件、目标文件出现的先后顺序,依次进入各个子目录编译从而生成这些目标

$(OBJS):
  echo $(OBJS)
  $(MAKE) -C cpu/$(CPU) $(if $(REMOTE_BUILD),$@,$(notdir $@))

$(LIBS):
  $(MAKE) -C $(dir $(subst $(obj),,$@))

  然后,回到顶层目录,继续执行顶层Makefile的总目标,最后生成u-boot.bin。

六、u-boot编译的特点

1、编译子目录

  编译子目录的方法是进入子目录,然后执行子目录中的Makefile

2、子目录Makefile的内容

  子目录Makefile的编译规则以及依赖是由顶层的config.mk、rules.mk构成,子目录的Makefile在使用时是用include包含进来的。

附:uboot下载地址:ftp://ftp.denx.de/pub/u-boot/

u-boot Makefile整体解析的更多相关文章

  1. Spring Boot启动原理解析

    Spring Boot启动原理解析http://www.cnblogs.com/moonandstar08/p/6550758.html 前言 前面几章我们见识了SpringBoot为我们做的自动配置 ...

  2. Linux下C++的通用Makefile与解析

    本文给出万能Makefile的具体实现,以及对其中的关键点进行解析.所谓C++万能Makefile,即可编译链接所有的C++程序,而只需作很少的修改. 号称万能Makefile,一统江湖.我对原版的M ...

  3. spring boot 源码解析52-actuate中MVCEndPoint解析

    今天有个别项目的jolokia的endpoint不能访问,调试源码发现:endpoint.enabled的开关导致的. 关于Endpoint, <Springboot Endpoint之二:En ...

  4. spring boot 1.视图解析器,2.开启静态资源访问

    1.spring boot 视图解析器 #视图解析器 #前缀spring.mvc.view.prefix=/pages/ #后缀..jsp.dospring.mvc.view.suffix=.jsp ...

  5. spring boot 源码解析11-ConfigurationClassPostProcessor类加载解析

    前言 ConfigurationClassPostProcessor实现了BeanDefinitionRegistryPostProcessor接口,该类会在AbstractApplicationCo ...

  6. Spring Boot系列(四):Spring Boot源码解析

    一.自动装配原理 之前博文已经讲过,@SpringBootApplication继承了@EnableAutoConfiguration,该注解导入了AutoConfigurationImport Se ...

  7. (最新 9000 字 )Spring Boot 配置特性解析

    爱生活,爱编码,微信搜一搜[架构技术专栏]关注这个喜欢分享的地方.本文 架构技术专栏 已收录,有各种JVM.多线程.源码视频.资料以及技术文章等你来拿 一.概述 目前Spring Boot版本: 2. ...

  8. Spring boot ConditionalOnClass原理解析

    Spring boot如何自动加载 对于Springboot的ConditionalOnClass注解一直非常好奇,原因是我们的jar包里面可能没有对应的class,而使用ConditionalOnC ...

  9. 关于linux内核模块Makefile的解析

    转载:http://www.embeddedlinux.org.cn/html/yingjianqudong/201403/23-2820.html Linux内核是一种单体内核,但是通过动态加载模块 ...

随机推荐

  1. hud 1241 Oil Deposits

    Oil Deposits Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) Tot ...

  2. [Whole Web] [Node.js, PM2] Controlling runaway apps using pm2

    shows how to enable features in your pm2 config file that allow you to prevent runaway apps from bri ...

  3. Antelope与 Barracude MYSQL 文件格式

    作者:吴炳锡 来源:http://www.mysqlsupport.cn/ 联系方式: wubingxi#163.com 转载请注明作/译者和出处,并且不能用于商业用途,违者必究. Antelope是 ...

  4. 通用块层、IO调度层以及设备驱动层的数据结构

    http://blog.chinaunix.net/uid-28897358-id-3775640.html

  5. sort函数的例子

    10.11编写程序,使用stable_sort和isShorter将传递给你的elimDups版本的vector排序.打印vector的内容. #include<algorithm> #i ...

  6. android模拟器经常出现的一些问题及解决办法

    1.Unable to get view server version from device exlipse下编写好android应用程序时候,右键项目 run as android applica ...

  7. 命令行修改weblogic用户名和密码

    语法为:java weblogic.security.utils.AdminAccount <NewAdminUserName> <NewAdminPassword>  dir ...

  8. Adobe Flash Platform产品介绍

    解释 adobe flash platform flex是一个免费的开源的框架 该应用程序通常是用在 浏览器和 桌面 AIR中的 了解 flex框架 ,Flex sdk 和adobe flash bu ...

  9. iOS9.0之后不支持http请求解决方案

    打开info.plist文件,添加关键字 NSAppTransportSecurity 类型 Dictionary 在 NSAppTransportSecurity 下添加关键字 NSAllowsAr ...

  10. CentOS7上安装Pycharm

    下载pycharm $ wget https://download.jetbrains.com/python/pycharm-professional-2016.1.2.tar.gz 解压 $ .ta ...