这段时间学习的压力要小的多,处理做LFS外,还编译了vivi,vivi是mizi公司一款bootloader,总体说了,这是一个轻量级的bootloader,代码的规模比较小,非常适合用于学习研究,不想uboot,万行的代码,不是那么容易读的,同时,vivi还有一个优点,就是它的Makefile文件结构,完全是仿照kernel的Makefile,所以,学习vivi的Makefile是很有价值的,可以从vivi的 makefile看出,很多的Makefile是直接从kernel的文件中的粘贴过来的。废话不说了,我们开始解析下vivi的Makefile文件吧。
  首先需要看一些vivi的文件组织形式:
|-- COPYING  //这是一个GPL文件
|-- CVS       //这是一个版本控制软件的文件夹
|-- Documentation  //相关说明文档
|-- Makefile     //Makefile文件
|-- Rules.make   //Makefile通用规则
|-- arch         //与平台板卡相关的文件
|-- drivers      //驱动文件
|-- include      //头文件
|-- init        // 系统初始化文件
|-- lib       //库文件
|-- scripts   //一些vivi的配置脚本,同时包含有menuconfig的源代码
|-- util    //二进制工具,这里有两个工具:imagewrite 和ecc

上边就是vivi的文件组织结构,有一些文件是与编译无关的,一些说明文档、版本控制文件以及其他一些文件,我们把不需要的文件删除,util这里边的工具用在其他的地方,可以选择性的编译,但这些文件是与vivi的编译无关的,当然,GPL协议是非常重要的,这里不是说GPL不重要,删除只是为了列举与vivi编译有关的文件。下边是精简的文件组织形式:

|-- Makefile   //主Makefile
|-- Rules.make   //通用Makefile规则
|-- arch        //与平台板卡相关的文件
|-- drivers      //驱动文件
|-- include      //头文件
|-- init       //初始化文件
|-- lib        //库文件
|-- scripts    //配置脚本与menuconfig的源码

我们来分析下vivi从配置到最后生成的总体流程:
 
               make menuconfig 或者 make config 或者 make oldconfig
                               |||||
                               \    /
                               生成 vivi/.config文件【保存了配置文件】
                               ||
                               \/
                               然后make ,这时候make根据.config的内容进行编译。
这里我们感兴趣的是执行make menuconfig后,出现的图形化菜单选项是怎么完成这个功能的,其实,mizi公司的程序是直接仿照kernel的形式,实质就是COPY过来的。  具体的过程是这样的,当执行make menuconfig ,首先 进入到scripts/lxdialog文件下,用gcc编译一个 menuconfig程序出来,这就是为什么在你输入make menuconfig的时候,会打印出来如下的内容:
 

make -C scripts/lxdialog all
make[1]: Entering directory `/tmp/vivi/scripts/lxdialog' # 跳转入目录
make[1]: Leaving directory `/tmp/vivi/scripts/lxdialog' # 编译完后,离开目录
bin/sh scripts/Menuconfig arch/config.in #执行lxdialog arch/config.in
Using defaults found in .config
Preparing scripts: functions, parsing.............done.

它首先是跳转如scripts/lxdialog文件夹下,然后进行编译,生成lxdialog的可执行文件,这个主要用于构建可视化图形界面,具体的功能这里不做详细介绍了,这样,编译生成lxdialog后,  这个可执行文件和menuconfig脚本的相互配合下就可以进行配置了,同时需要说明的时,在图形化配置中,会出现很多的菜单化选项,那么这些菜单化选项是否可以更改,当然是可以的,如果你够仔细,就会发现,在vivi真个目录中,有很多的config.in文件,这些文件就存储了这些的菜单选项,只要修改这些文件就可以任意的添加自己的选项了,这里对这些的菜单配置文件做一些简要的说明。
在arch/config.in中的文件内容:
 

choice 'ARM system type' \
  "SA1100-based        CONFIG_ARCH_SA1100 \
   PXA250/210-based    CONFIG_ARCH_PXA250 \
   S3C2400-based    CONFIG_ARCH_S3C2400 \
   S3C2410-based    CONFIG_ARCH_S3C2410"


mainmenu_option next_comment
comment 'Implementations'
if [ "$CONFIG_ARCH_SA1100" = "y" ]; then
  choice 'Platform' \
    "KINGS    CONFIG_SA1100_KINGS \
     FORTE    CONFIG_SA1100_FORTE \
     SUNS    CONFIG_SA1100_SUNS \
     SUNS_OLD    CONFIG_SA1100_SUNS_OLD \
     GILL    CONFIG_SA1100_GILL \
     ENDA    CONFIG_SA1100_ENDA \
     EXILIEN102    CONFIG_SA1100_EXILIEN102 \
     WISMO    CONFIG_SA1100_WISMO"

这里只截取了一小部分, 这些脚本都是相似的,如果感兴趣可以自己去打开看看,相似的脚本还出现在/driver/mtd  driver/mtd/nand driver/mtd/nor driver/mtd/maps  driver/serial等文件夹下,这些是和kernel的菜单化配置一样的,都是直接copy过来,给我们一个提示:
   学习的第一层是优秀的模仿,学会模仿也是一种学习能力。

通过上边的操作,我们认为所有的配置已经完成了,应该开始编译工作了,至于编译的流程需要我们看看makefile文件。由于Makefile文件比较长,不方便全部贴在这里。我们分段进行分析。

VERSION = 0
PATCHLEVEL = 1
SUBLEVEL = 4

VIVIRELEASE=$(VERSION).$(PATCHLEVEL).$(SUBLEVEL)

ARCH := arm

CONFIG_SHELL := $(shell if [ -x "$$BASH" ]; then echo $$BASH; \
    else if [ -x /bin/bash ]; then echo /bin/bash; \
    else echo sh; fi ; fi)
TOPDIR    := $(shell /bin/pwd)

#
# change this to point to the Linux include directory
#
LINUX_INCLUDE_DIR    = /usr/local/arm/2.95.3/include/


VIVIPATH = $(TOPDIR)/include

HOSTCC = gcc
HOSTCFLAGS = -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer

CROSS_COMPILE = /usr/local/arm/2.95.3/bin/arm-linux-
#CROSS_COMPILE = /opt/host/armv4l/bin/armv4l-redhat-linux-

#
# Include the make variables (CC, etc...)
#

AS = $(CROSS_COMPILE)as
LD = $(CROSS_COMPILE)ld
CC = $(CROSS_COMPILE)gcc
CPP = $(CC) -E
AR = $(CROSS_COMPILE)ar
NM = $(CROSS_COMPILE)nm
STRIP = $(CROSS_COMPILE)strip
OBJCOPY = $(CROSS_COMPILE)objcopy
OBJDUMP = $(CROSS_COMPILE)objdump
MAKEFILES = $(TOPDIR)/.config
MD5SUM        = md5sum
PERL = perl
AWK        = awk

export VERSION PATCHLEVEL SUBLEVEL KERNELRELEASE \
        CONFIG_SHELL TOPDIR VIVIPATH HOSTCC HOSTCFLAGS CROSS_COMPILE AS LD CC \
    CPP AR NM STRIP OBJCOPY OBJDUMP MAKE MAKEFILES MD5SUM PERL AWK

# 版本号  在生成的vivi中,开头的版本号就是有这个地方的变量来决定

VERSION = 0
PATCHLEVEL = 1
SUBLEVEL = 4  

# VIVI 这里设置输出版本信息的格式。
VIVIRELEASE=$(VERSION).$(PATCHLEVEL).$(SUBLEVEL)

#  ":=" 与"="的区别: ”:=“为直接展开,”=“为递归展开,详细内容看GNU make中文手册6.2.2
# ”:=“在此处会定义一个变量,直接对其赋值,对于引用会直接找到引用的源,表示立即展开
#”:“只是简单的替换,不会直接找到引用的源,在以后,如果引用的源发生变化,此处的变量也会发生变化。表示递归展开
#########  举例  ######### 
#
# VAR1=TMP1;
# TMP=$(VAR1)
#  VAR1=TMP2
# TMP1:=$(VAR1)
#ECHO $(TMP)  ===>显示 TMP2
#ECHO $(TMP1)===>显示 TMP1
#
#ARCH是定义体系结构, VIVI此处的定义是说明该VIVI是针对ARM平台
ARCH := arm


# 这里一处make函数的调用,该函数为shell,该行语句的主要作用是设置shell命令解释器
#这条命令的作用是执行函数把结果给变量
#make函数的调用语法为:$()或者${}
#shell函数的作用是”执行命令,把命令的执行结果作为变量的内容“
#COMFIG_SHELL  指定命令解释器
CONFIG_SHELL := $(shell if [ -x "$$BASH" ]; then echo $$BASH; \
 else if [ -x /bin/bash ]; then echo /bin/bash; \
 else echo sh; fi ; fi)
# 设置当前顶层目录, 该Makefile所在目录  shell函数的用法,把命令pwd的结果赋值给TOPDIR
TOPDIR := $(shell /bin/pwd)

#
# change this to point to the Linux include directory
#  指定LINUX内核的INCLUDE目录,在vivi中,没有起作用,可以删除该语句
#
LINUX_INCLUDE_DIR = /usr/local/arm/2.95.3/include/

# 指定vivi的头文件所在目录
#
VIVIPATH           = $(TOPDIR)/include

# 定义本地的编译器 这里指定编译器为gcc
HOSTCC          = gcc
#定义本地的编译器的编译参数,vivi的编译需用使用arm-linux-gcc(交叉编译器),那么定义gcc有什么作用?
#解答: 在一次使用make menuconfig的时候,需要使用gcc编译menuconfig选项程序,只为配置服务。
#跳转到scripts/lxdialog
#执行该目录下的make all
# make menuconfig :::---->>
###跳转到scripts/lxdialog
###执行该目录下的make all
###make -C scripts/lxdialog all
###make[1]: Entering directory `/tmp/vivi/scripts/lxdialog' # 跳转入目录
###make[1]: Leaving directory `/tmp/vivi/scripts/lxdialog'  # 编译完后,离开目录
###/bin/sh scripts/Menuconfig arch/config.in                #执行Menuconfig arch/config.in  
###Using defaults found in .config
###Preparing scripts: functions, parsing.............done.

HOSTCFLAGS      = -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer


#定义交叉编译器的路径,要注意一些公认的定义方法,一般CROSS_COMPILE就是交叉编译器 ARCH就是于体系结构相关的
CROSS_COMPILE   = /usr/local/arm/2.95.3/bin/arm-linux-
#CROSS_COMPILE   = /opt/host/armv4l/bin/armv4l-redhat-linux-

#
# Include the make variables (CC, etc...)
#  定义工具链 tools chains
#
AS              = $(CROSS_COMPILE)as
LD              = $(CROSS_COMPILE)ld
CC              = $(CROSS_COMPILE)gcc
CPP             = $(CC) -E
AR              = $(CROSS_COMPILE)ar
NM              = $(CROSS_COMPILE)nm
STRIP           = $(CROSS_COMPILE)strip
OBJCOPY         = $(CROSS_COMPILE)objcopy
OBJDUMP         = $(CROSS_COMPILE)objdump
MAKEFILES       = $(TOPDIR)/.config
#MD5 
MD5SUM  = md5sum
# PERL   脚本
PERL            = perl
AWK  = awk
# 导出前边定义的变量 主要是因为主makefile需要调用很多子makefile,
# 这样导出后,就可以让子makefile使用这些以前定义的变量了。
export  VERSION PATCHLEVEL SUBLEVEL KERNELRELEASE \
        CONFIG_SHELL TOPDIR VIVIPATH HOSTCC HOSTCFLAGS CROSS_COMPILE AS LD CC \
 CPP AR NM STRIP OBJCOPY OBJDUMP MAKE MAKEFILES MD5SUM PERL AWK
# 定义就宣告结束了
#下边是具体的编译规则