一、概述  

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. windows下载安装MariaDB5.5.32 绿色版

    1.下载地址: http://ftp.yz.yamagata-u.ac.jp/pub/dbms/mariadb/mariadb-5.5.32/win32-packages/mariadb-5.5.32 ...

  2. MAC OS X 终端命令入门 (简单常用整理)

    在这里记下..防止丢失 pwd 当前工作目录 cd(不加参数) 进root cd(folder) 进入文件夹 cd .. 上级目录 cd ~ 返回root cd - 返回上一个访问的目录 rm 文件名 ...

  3. Android(java)学习笔记143:android提供打开各种文件的API接口:setDataAndType

    android 打开各种文件(setDataAndType) private void openFile(File file){ Intent intent = new Intent(); inten ...

  4. 【原】Shell脚本-判断文件有无进而复制

    2016年7月5日某同学在群上求助要编一个判断文件或目录在某路径下有无进而有的就复制粘贴到另一路径下,无的则将代码中断(不往下执行命令)的脚本.逐一完善.模板如下(生产环境可用到路径环境变量) --- ...

  5. 通过Html5的postMessage和onMessage方法实现跨域跨文档请求访问

    在项目中有应用到不同的子项目,通过不同的二级域名实现相互调用功能.其中一个功能是将播放器作为单独的二级域名的请求接口,其他项目必须根据该二级域名调用播放器.最近需要实现视频播放完毕后的事件触发,调用父 ...

  6. Java SSL/TLS Socket实现

    通信端无需向对方证明自己的身份,则称该端处于"客户模式",否则称其处于"服务器模式",无论是客户端还是服务器端,都可处于"客户模式"或者&q ...

  7. struts2学生信息管理系统篇章①

    最近在看java1234分享的一个Struts2的学生信息管理系统的知识.因为本身java没什么底子.所以就没有什么好的技术去解决问题.一直在百度,不懂就百度.这样子下来其实也能学到一些东西,过阵子等 ...

  8. 面向服务的体系结构(service-oriented architecture,SOA)

    SOA的概念是Gartner 在1996年提出来的,并于2002年12月进一步提出SOA是“现代应用开发领域最重要的课题”.   一.SOA的定义 SOA分为广义的SOA和狭义的SOA,广义的SOA是 ...

  9. 学习笔记_过滤器详细(过滤器JavaWeb三大组件之一)

    过滤器详细 1 过滤器的生命周期 我们已经学习过Servlet的生命周期,那么Filter的生命周期也就没有什么难度了! (l)  init(FilterConfig):在服务器启动时会创建Filte ...

  10. entityframework多条件查询类

    entityframework多条件查询类 var dataaccess = new BaseAccess(); int totalCount = 0; var paramS = new OrderM ...