vivi中顶层Makefile的分析

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

你可能感兴趣的:(gcc,makefile)