VERSION = 0
PATCHLEVEL = 1
SUBLEVEL = 4
VIVIRELEASE=$(VERSION).$(PATCHLEVEL).$(SUBLEVEL)
#定义vivi的版本信息
ARCH := arm
#定义平台消息
#检查系统是采用哪个BASH
#-x检查文件是否存在if [ -x /bin/bash ]注意格式
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头文件的目录,在下面编译中会用到
LINUX_INCLUDE_DIR = /usr/local/arm/4.3.2/arm-none-linux-gnueabi/libc/usr/include
#vivi头文件的路径
VIVIPATH = $(TOPDIR)/include
#宿主机的的gcc,版本根据主机变化而变化
#定义了gcc的参数,-Wall表示打开警告信息
#-Wstrict-prototypes为定义参数类型的变量和函数输出警告
#-O2对代码进行优化
#-fomit-frame-pointer忽略帧指针
HOSTCC = gcc
HOSTCFLAGS = -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer
#定义交叉编译器的变量地址
CROSS_COMPILE = /usr/local/arm/4.3.2/bin/arm-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
#对外部声明以上的变量,即在其他的MAKEFILE文件也可以使用以上的变量
all: do-it-all
#定义目标和依赖
ifeq (.config,$(wildcard .config))
#'wildcard' 的函 数,它有一个参数,功能是展开成一列所有符合由其参数描述的文
#件名,文件间以空格间隔
#上面这句的目的就是在当前目录下查找,config文件
include .config
#如果当前目录下存在.config文件,则包含.config文件
else
CONFIGURATION = config
do-it-all: config
#如果不存在则定义变量CONFIGURATION,并且对变量进行赋值
#定义目标和依赖di-it-all: config
endif
do-it-all: Version vivi
#追加do-it-all的依赖文件
#上面的函数的目的就是在vivi的根目录下查找.config文件,如果存在则包含.config;
#do-it-all依赖的文件为Version和vivi
#如果不存在,则定义变量CONFIGURATION变量;
#do-it-all的依赖文件Version和vivi
#
# standard CFLAGS
#
CPPFLAGS := -I$(VIVIPATH) -I$(LINUX_INCLUDE_DIR)
#定义编译器预处理的参数
#-I表示在头文件的搜索路径中添加(-Idir)dir目录
#CFLAGS := $(CPPFLAGS) -Wall -Wstrict-prototypes -Wno-trigraphs -O2 \
# -fomit-frame-pointer -fno-strict-aliasing -fno-common
#normal flags
CFLAGS := $(CPPFLAGS) -Wall -Wstrict-prototypes -O2 -fPIC -fomit-frame-pointer
#交叉编译器的参数,基本上宿主gcc的参数相同,参见上面的解释
#补充一个-fPIC,与地址无关。具体作用不明白???
#symbol table make up
#CFLAGS := $(CPPFLAGS) -Wall -Wstrict-prototypes -O2 -fPIC -fomit-frame-pointer -ggdb
AFLAGS := -D__ASSEMBLY__ $(CPPFLAGS)
#汇编器选项
CORE_FILES = init/main.o init/version.o lib/lib.o
LIBS := lib/priv_data/priv_data.o
SUBDIRS = drivers lib
#定义核心文件,库和子目录,这些变量在下面都会用到
DRIVERS-y :=
DRIVERS-$(CONFIG_SERIAL) += drivers/serial/serial.o
DRIVERS-$(CONFIG_MTD) += drivers/mtd/mtd.o
DRIVERS := $(DRIVERS-y)
#定义变量DRIVERS-y,看到这里我们就知道了为什么要包含.config文件的原因(在vivi根目#录下存在.config的情况下,打开.config文件,里面就会有CONFIG_SERIAL和CONFIG_MTD的#定义,如果在组态的时候增加了相关的选项,则就将其赋值为y)
#那如果没有.config的情况下,则在do-it-all的依赖中添加了一个config依赖文件;
#而利用make config来配置,从而生成.config文件。因此作用是相同的。
CLEAN_FILES = \
vivi-elf \
vivi \
vivi.nm \
vivi.map
#定义需要清除的文件
#
# Location of the gcc arm libs.
#
ARM_GCC_LIBS = /usr/local/arm/4.3.2/arm-none-linux-gnueabi/libc/armv4t/lib
#定义交叉编译库目录
OBJCOPYFLAGS = -R .comment -R .stab -R .stabstr
#定义objcopy的参数,objcopy包含了很多参数,具体的看使用手册
#-R选项是用来删掉包含了二进制文件不需要的内容的那些部分。
CLIBS = -L$(ARM_GCC_LIBS) -L/usr/local/arm/4.3.2/arm-none-linux-gnueabi/libc/armv4t/usr/lib -L/usr/local/arm/4.3.2/lib/gcc/arm-none-linux-gnueabi/4.3.2/armv4t/ -lgcc -lc
#定义CLIBS; -Ldir 表示在-I选项的搜索路径列表中添加dir目录
#-lgcc -lc标准的库文件。具体是什么我也不知道,有待研究
#laputa symbolic
LINKFLAGS = -Tarch/vivi.lds -Bstatic
#连接参数;-T表示使用连接脚本,这个脚本会替代"ld"的缺省连接脚本。所以命令文件必
#须指定所有需要的东西以精确描述输出文件
#-Bstatic表示不连接共享库,
DISTCLEAN_FILES = \
include/autoconf.h include/version.h \
scripts/lxdialog/*.o scripts/lxdialog/lxdialog \
.menuconfig.log \
.config .config.old
#定义需要删除的文件
include arch/Makefile
#包含子目录的Makefile文件。vivi中的Makefile使用分层管理的机制
#这样的好处就是方便管理
export CPPFLAGS CFLAGS AFLAGS
export DRIVERS LDFLAGS
#向外部声明变量
Version: dummy
@rm -f include/compile.h
#定义目标Version,其依赖文件是dummy,
#命令就是删除include/compile.h文件
#执行本命令时不再屏幕上打印命令的内容
vivi: include/version.h $(CONFIGURATION) init/main.o init/version.o linuxsubdirs
$(LD) -v $(LINKFLAGS) \
$(HEAD) \
$(CORE_FILES) \
$(DRIVERS) \
$(LIBS) \
-o vivi-elf $(CLIBS)
$(NM) -v -l vivi-elf > vivi.map
$(OBJCOPY) -O binary -S vivi-elf vivi $(OBJCOPYFLAGS)
#定义目标vivi,其中vivi包含的目标比较多,有则包括,没有则需要首先编译获得
#这个时候我们发现,linuxsubdirs没有定义,而且HEAD也没有,但是我们在前面的时候就有一个
#include arch/Makefile。我们进去到arch目录下去看看该目录的Makefile文件
#HEAD := arch/$(MACHINE)/head.o
#linuxsubdirs我还没找到
#后来继续往下面看的时候发现linuxsubdirs有定义,这样就告诉我们,有时候不需要钻入
#死胡同里面,没准路就在前面的拐角处
#NM函数和OBJCOPY函数的功能看相关资料
#在这里的意思分别是将vivi-elf文件的变量和函数的地址放在文件vivi.map中
#利用vivi-elf生成二进制文件vivi,取出不必要的信息
oldconfig:
$(CONFIG_SHELL) scripts/Configure -d arch/config.in
config:
$(CONFIG_SHELL) scripts/Configure arch/config.in
menuconfig: include/version.h
$(MAKE) -C scripts/lxdialog all
$(CONFIG_SHELL) scripts/Menuconfig arch/config.in
#关于上面的3个分析,网上有很多资料,我就不详细分析,需要注意的是menuconfig目标
#该目标首先是进入scripts/lxdialog目录然后执行make命令,其实也就是一个图形界面的
#操作
clean:
find . \( -name '*.o' -o -name core -o -name ".*.flags" \) -type f -print \
| grep -v lxdialog/ | xargs rm -f
rm -f $(CLEAN_FILES)
#clean目标,一共用到了find,grep,xargs 3个命令。
#相关的命令分析请看我的linux命令解析部分,宗旨这个函数的目的就是在当前目录下
#找到所有的.o,.flags和core的普通文件(搜索的路径中不包括lxdialog),并且打印出来
#然后删除它们,并且删除CLEAN_FILES文件
distclean: clean
rm -f $(DISTCLEAN_FILES)
linuxsubdirs: $(patsubst %, _dir_%, $(SUBDIRS))
$(patsubst %, _dir_%, $(SUBDIRS)) : include/version.h
$(MAKE) CFLAGS="$(CFLAGS) $(CFLAGS_KERNEL)" -C $(patsubst _dir_%, %, $@)
#上面的就是需要看patsubst函数的用法
# $@表示一个目标名种的目标文件
# make -C 表示进入该目录再执行make命令
$(TOPDIR)/include/version.h: include/version.h
$(TOPDIR)/include/compile.h: include/compile.h
#
#定义了在include目录下的两个目标文件,version.h,compile.h
#在DISTCLEAN_FILES的依赖文件中,有compile.h文件,因此可以看出compile.h和version.h两个文件是需要
#配置的过程中生成的。那么这两个文件又是怎么生成的,它们的作用是什么?下面我们具体分析。
#
#
include/compile.h: $(CONFIGURATION) include/version.h
@echo -n \#define UTS_VERSION \"\#$(VIVIRELEASE) > .ver
@if [ -f .name ]; then echo -n \-`cat .name` >> .ver; fi
@echo ' '`date`'"' >> .ver
@echo \#define VIVI_COMPILE_TIME \"`date +%T`\" >> .ver
@echo \#define VIVI_COMPILE_BY \"`whoami`\" >> .ver
@echo \#define VIVI_COMPILE_HOST \"`hostname`\" >> .ver
@if [ -x /bin/dnsdomainname ]; then \
echo \#define VIVI_COMPILE_DOMAIN \"`dnsdomainname`\"; \
elif [ -x /bin/domainname ]; then \
echo \#define VIVI_COMPILE_DOMAIN \"`domainname`\"; \
else \
echo \#define VIVI_COMPILE_DOMAIN ; \
fi >> .ver
@echo \#define VIVI_COMPILER \"`$(CC) $(CFLAGS) -v 2>&1 | tail -1`\" >> .ver
@mv -f .ver $@
include/version.h:
@echo \#define VIVI_RELEASE \"$(VIVIRELEASE)\" > .ver
@echo \#define VIVI_VERSION_CODE `expr $(VERSION) \\* 65536 + $(PATCHLEVEL) \\* 256 + $(SUBLEVEL)` >> .ver
@echo '#define VIVI_VERSION(a,b,c) (((a) << 16) + ((b) << 8) + (c))' >>.ver
@mv -f .ver $@
#上面两个说明了compile.h和version.h文件的生成过程,这里需要注意的就是echo的用法
# echo -n表示不换行
# 其他的date,whoami,hostname则分别查看相关命令的用法
init/version.o: init/version.c include/compile.h
$(CC) $(CFLAGS) -DUTS_MACHINE='"$(ARCH)"' -c -o init/version.o init/version.c
init/main.o: init/main.c
$(CC) $(CFLAGS) $(CFLAGS_KERNEL) $(PROFILING) -c -o $*.o $<
%: ./arch/def-configs/%
$(MAKE) distclean
cp arch/def-configs/$* ./.config -f
$(MAKE) oldconfig
$(MAKE)
ifdef CONFIGURATION
..$(CONFIGURATION):
@echo
@echo "You have a bad or nonexistent" .$(CONFIGURATION) ": running 'make" $(CONFIGURATION)"'"
@echo
$(MAKE) $(CONFIGURATION)
@echo
@echo "Successful. Try re-making (ignore the error that follows)"
@echo
exit 1
dummy:
else
dummy:
endif
include Rules.make