Chinaunix首页 | 论坛 | 博客
  • 博客访问: 3423535
  • 博文数量: 864
  • 博客积分: 14125
  • 博客等级: 上将
  • 技术积分: 10634
  • 用 户 组: 普通用户
  • 注册时间: 2007-07-27 16:53
个人简介

https://github.com/zytc2009/BigTeam_learning

文章分类

全部博文(864)

文章存档

2023年(1)

2021年(1)

2019年(3)

2018年(1)

2017年(10)

2015年(3)

2014年(8)

2013年(3)

2012年(69)

2011年(103)

2010年(357)

2009年(283)

2008年(22)

分类: LINUX

2009-02-10 11:04:21

工程管理(Make File)基础
       什么是Make File?很多Windows的程序员都不知道这个东西,因为那些Windows的IDE都为我们做了这个工作,但我觉得要成为一名专业的程序员Make File还是要懂。这就好像是我现在懂了C语言,但是我还要去了解编译器的“内幕”一样。Make File关系到了整个工程的编译规则。对于一个大型的工程来说,其中的源文件不计其数,并分别按类型、功能、模块分别放在若干个目录中,这就需要我们能够有一套方便好用的工具来管理这些源文件的编译和链接。因为,如果每次都将全部的文件编译一遍,可能会大大的浪费开发时间,所以能够识别哪些文件需要重新编译的功能就很有必要了。幸好我们有Make File来帮助我们做这些,不然真是麻烦大了。
       Make File定义了一系列的规则来指定哪些文件需要重新编译,甚至于进行更复杂的功能操作。带来的好处就是——“自动化编译”,只需要一个make命令,整个工程完全自动编译,极大的提高了软件开发的效率。make是一个命令工具,是一个解释Make File中指令的命令工具,一般来说,大多数的IDE都有这个命令,比如:Delphi的make,Visual C++的nmake,Linux下的GNU make等。可以看出,Make File已经是大多数工程管理的标准工具了。
核心原理
       要掌握一个东西,就应该首先掌握它的核心思想,因此在这里我们将首先来看看Make File的核心原理——依赖规则。看下面:
    target ... : prerequisites ...
            command
            ...
    target也就是一个目标文件,可以是Object File,也可以是执行文件。还可以是一个标签(Label),对于标签这种特性,在后续的“伪目标”中会有叙述。
    prerequisites就是,要生成那个target所需要的文件或是目标。或者理解为,生成target需要prerequisites。
    command也就是make需要执行的命令。
       这是一个文件的依赖关系,也就是说,target这一个或多个的目标文件依赖于prerequisites中的文件,其生成规则定义在command中。说白一点就是说,prerequisites中如果有一个以上的文件比target文件更新的话,command所定义的命令就会被执行。这就是Make File的规则,也就是Make File中最为核心的原理。
       完成一个简单的演示例子,在这个例子中有main.c和command.c两个源文件,一个defs.h头文件,main.c和command.c中都包含defs.h头文件。要求能够输出main.o和command.o两个目标文件。定义的Make规则如下:

edit : main.o command.o
       cc –o edit main.o command.o
      
main.o : main.c defs.h
       cc –c main.c -o main.o
 
command.o : command.c defs.h
       cc –c command.c –o command.o
      
clean :
       rm main.o \
       command.o

       反斜杠(\)是换行的意思,告诉Make程序下面一行于这一行紧接着。clean是用来清除生成的main.o和command.o文件。请注意,在上面的每一行缩进中,Make要求必须使用Tab键,不能使用空格做为缩进量。
       在这个例子中,目标文件(target)包含文件edit和中间目标文件(*.o),依赖文件(prerequisites)就是冒号后面的那些 .c 文件和 .h文件。每一个 .o 文件都有一组依赖文件,而这些 .o 文件又是目标文件edit的依赖文件。依赖关系本质上就是说明了目标文件是由哪些文件生成的。
       在定义好依赖关系后,后续的那一行定义了如何生成目标文件的操作系统命令,一定要以一个Tab键做为开头。记住,make并不管命令是怎么工作的,他只管执行所定义的命令。make会比较targets文件和prerequisites文件的修改日期,如果prerequisites文件的日期要比targets文件的日期要新,或者target不存在的话,那么,make就会执行后续定义的命令。
       这里要说明一点的是,clean不是一个文件,它只不过是一个动作名字,有点像C语言中的标签(Label)一样。clean的冒号后什么也没有,因此make不会自动去查找clean的依赖性,也就不会自动执行其后所定义的命令。要执行其后的命令,就要在make命令后明显得指出这个标签(Label)的名字。这样的方法非常有用,我们可以在一个Make File中定义不用的编译或是和编译无关的命令,比如程序的打包,程序的备份,等等。
的工作方式
       在默认的方式下,我们只输入make命令,或者使用-f参数指定需要执行的Make File文件,然后make程序会进行如下操作:
    1、make会在当前目录下找名字叫“Makefile”或“makefile”的文件(注意没有扩展名)或通过-f指定的Make File文件。
    2、如果找到Make File文件,它会找文件中的第一个目标文件(target),在上面的例子中,他会找到“edit”这个文件,并把这个文件做为最终的目标文件。
    3、如果edit文件不存在,或是edit所依赖的后面的 .o 文件的文件修改时间要比edit这个文件新,那么,他就会执行后面所定义的命令来生成edit这个文件。
    4、如果edit所依赖的.o文件也存在,那么make会在当前文件中找目标为.o文件的依赖性,如果找到则再根据那一个规则生成.o文件。(这有点像一个堆栈的过程)
    5、最后当然是C文件和H文件都存在的啦,于是make会生成 .o文件,然后再用.o文件生成make的终极任务——目标文件edit了。
       这就是整个make的依赖性,make会一层又一层地去找文件的依赖关系,直到最终编译出第一个目标文件。如果在找寻的过程中出现错误,比如最后被依赖的文件找不到,那么make就会直接退出并报错;而对于所定义命令的错误,或是编译不成功,make根本不理。make只管文件的依赖性,即,如果在我找了依赖关系之后,冒号后面的文件还是不在,那么对不起,我就不工作啦。
       通过上述分析,我们知道,像clean这种,没有被第一个目标文件直接或间接关联,那么它后面所定义的命令将不会被自动执行,不过,我们可以显示要求make执行。即命令——“make clean”,以此来清除所有的目标文件,以便于重新编译。于是在上面的例子中,如果这个工程已被编译过了,当我们修改了其中一个源文件,比如main.c,那么根据我们的依赖性,我们的目标main.o会被重编译(也就是在这个依性关系后面所定义的命令),于是main.o的文件也是最新的啦,于是main.o的文件修改时间要比edit要新,所以edit也会被重新生成了(详见edit目标文件后定义的命令)。而如果我们改变了“defs.h”,那么,main.o和command.o都会被重新编译,那么理所当然的,edit也会被重生成。
       为了更加清晰的理清Make File的工作方式,现在我们总结一下Make File的各个工作阶段及其执行的内容:
    1、读入所有的Makefile
    2、读入被include的其它Makefile
    3、初始化文件中的变量
    4、分析所有规则
    5、为所有的目标文件创建依赖关系链
    6、根据依赖关系,决定哪些目标要重新生成
    7、执行生成命令
       1-5步为第一个阶段,6-7为第二个阶段。第一个阶段中,如果定义的变量被使用了,那么,make会把其展开在使用的位置。但make并不会完全马上展开,make使用的是拖延战术,如果变量出现在依赖关系的规则中,那么仅当这条依赖被决定要使用了,变量才会在其内部展开。
实例
       或许您会觉得上面的例子也太简单了,根本不值一提。那么现在就来一个高阶的应用来看看。还记得我们在编译器基础一节所作的Test4例子吗,我们就在这个例子中添加一个makefile 文件(注意没有扩展名)。

makefile – 一个简单却齐全的Make File实例
SHELL = sh
#=====================================================================
# Name:
#    makefile
#
# Description:
#    Makefile to build the $(TARGET) module.
#
#   The following nmake targets are available in this makefile:
#
#     all           - make .elf and .mod image files (default)
#     clean         - delete object directory and image files
#     filename.o    - make object file
#     filename.mix - make C and ASM mix file
#
#   The above targets can be made with the following command:
#
#     make  [target]
#
# Assumptions:
#    1. The ARM ADS 1.0.1 or higher tools are installed
#    2. The run environment of make toos is exist in this OS
#
#----------------------------------------------------------------------------
#=====================================================================
TARGET        = Test4#
SCLFILE       = system.scl#
C_OBJS        = mainapp.o raminit.o
A_OBJS        = bootblock.o
OBJS          = $(C_OBJS) $(A_OBJS)
 
APP_PATH      = .#
 
# set the search path
vpath %.c $(APP_PATH)
vpath %.s $(APP_PATH)
 
#-------------------------------------------------------------------------------
# Target file name and type definitions
#-------------------------------------------------------------------------------
 
EXETYPE    = elf#                # Target image file format
MODULE     = bin#                # Binary module extension
 
#-------------------------------------------------------------------------------
# Target compile time symbol definitions
#-------------------------------------------------------------------------------
 
ARMASM     = -D_ARM_ASM_#
 
#-------------------------------------------------------------------------------
# Software tool and environment definitions
#-------------------------------------------------------------------------------
ARMBIN = $(ARMHOME)/Bin#
ARMBIN := $(subst \,/,$(ARMBIN))#
 
ARMCC   = $(ARMBIN)/armcc#       # ARM ADS ARM 32-bit inst. set ANSI C compiler
ASM     = $(ARMBIN)/armasm#      # ARM ADS assembler
LD      = $(ARMBIN)/armlink#     # ARM ADS linker
HEXTOOL = $(ARMBIN)/fromelf#     # ARM ADS utility to create hex file from image
 
OBJ_CMD    = -o#                 # Command line option to specify output filename
 
#-------------------------------------------------------------------------------
# Processor architecture options
#-------------------------------------------------------------------------------
 
CPU = -cpu ARM7TDMI#             # ARM7TDMI target processor
 
#-------------------------------------------------------------------------------
# ARM Procedure Call Standard (APCS) options
#-------------------------------------------------------------------------------
APCS = -apcs /noswst/interwork
 
#-------------------------------------------------------------------------------
# Additional compile time error checking options
#-------------------------------------------------------------------------------
 
CHK = -fa#                       # Check for data flow anomolies
 
#-------------------------------------------------------------------------------
# Compiler output options
#-------------------------------------------------------------------------------
 
OUT = -c#                        # Object file output only
 
#-------------------------------------------------------------------------------
# Compiler/assembler debug options
#-------------------------------------------------------------------------------
 
DBG = -g#                        # Enable debug
 
#-------------------------------------------------------------------------------
# Compiler optimization options
#-------------------------------------------------------------------------------
 
OPT = -Ospace -O2#               # Full compiler optimization for space
 
#-------------------------------------------------------------------------------
# Compiler code generation options
#-------------------------------------------------------------------------------
 
END = -littleend#                # Compile for little endian memory architecture
ZA = -zo#                       # LDR may only access 32-bit aligned addresses
 
CODE = $(END) $(ZA)#
 
#-------------------------------------------------------------------------------
# Include file search path options
#-------------------------------------------------------------------------------
 
INC = -I.#
 
#-------------------------------------------------------------------------------
# Linker options
#-------------------------------------------------------------------------------
 
LINK_CMD = -o#                    # Command line option to specify output file
                                  # on linking
LIST     = -list $(TARGET).map#   # Direct map and info output to file
INFO     = -elf -map -info sizes,totals,veneers,unused
LINK_OPT = -scatter $(SCLFILE)#   # Use scatter load description file
#-------------------------------------------------------------------------------
# HEXTOOL options
#-------------------------------------------------------------------------------
 
BINFORMAT = -bin#
OUTPUT    = -output#
 
#-------------------------------------------------------------------------------
# Compiler flag definitions
#-------------------------------------------------------------------------------
 
CFLAGS = $(OUT) $(INC) $(CPU) $(APCS) $(CODE) $(CHK) $(DBG) $(OPT)
AFLAGS = $(CPU) $(APCS) $(INC)
 
#----------------------------------------------------------------------------
# Default target
#----------------------------------------------------------------------------
.PHONY : all
all : startup $(TARGET).$(MODULE) complete
 
.PHONY : startup
startup:
       @echo ---------------------------------------------------------------
       @echo Compile startup
       @echo ---------------------------------------------------------------
      
.PHONY : complete
complete:
       @echo ---------------------------------------------------------------
       @echo All have been done
       @echo ---------------------------------------------------------------
      
#----------------------------------------------------------------------------
# Clean target
#----------------------------------------------------------------------------
 
# The object subdirectory, target image file, and target hex file are deleted.
.PHONY : clean
clean :
       @echo ---------------------------------------------------------------
       @echo CLEAN
       -rm -f *.o
       -rm -f *.i
       -rm -f *.dep
       -rm -f $(TARGET).$(EXETYPE)
       -rm $(TARGET).$(MODULE)
       -rm $(TARGET).map
       @echo ---------------------------------------------------------------
        #=====================================================================
#                           DEFAULT SUFFIX RULES
#=====================================================================
SRC_FILE = $(@F:.o=.c)#        # Input source file specification
OBJ_FILE = $(OBJ_CMD) $(@F)#   # Output object file specification
 
.SUFFIXES :
.SUFFIXES : .o .c .s .mix .dep
 
#--------------------------------------------------------------------------
# C code inference rules
#----------------------------------------------------------------------------
%.o:%.c
       @echo ---------------------------------------------------------------
       @echo OBJECT $(@F)
       $(ARMCC) $(CFLAGS) $(OBJ_FILE) $(SRC_FILE)
       @echo ---------------------------------------------------------------
      
%.mix:%.c
       @echo ---------------------------------------------------------------
       @echo OBJECT $(@F)
       $(ARMCC) -S -fs $(CFLAGS) $(INC) $(OBJ_FILE) $<
       @echo ---------------------------------------------------------------
 
#-------------------------------------------------------------------------------
# Assembly code inference rules
#-------------------------------------------------------------------------------
%.o:%.s
       @echo ---------------------------------------------------------------
       @echo OBJECT $(@F)
       $(ARMCC) -ansic -E $(AFLAGS) $(ARMASM) $< > $*.i
       $(ASM) $(AFLAGS) $(OBJ_FILE) $*.i
       @echo ---------------------------------------------------------------
 
#-------------------------------------------------------------------------------
# Depend file inference rules
#-------------------------------------------------------------------------------
%.dep:%.c
       @echo ---------------------------------------------------------------
       $(ARMCC) -ansic -M $< > $*.dep
       @echo ---------------------------------------------------------------
 
#=====================================================================
#                           MODULE SPECIFIC RULES
#=====================================================================
 
#----------------------------------------------------------------------------
# Lib file targets
#----------------------------------------------------------------------------
 
$(TARGET).$(MODULE) : $(TARGET).$(EXETYPE)
       @echo ---------------------------------------------------------------
       @echo TARGET $@
       $(HEXTOOL) $(TARGET).$(EXETYPE) $(BINFORMAT) $(OUTPUT) $(TARGET).$(MODULE)
 
$(TARGET).$(EXETYPE) : $(OBJS)
       @echo ---------------------------------------------------------------
       @echo TARGET $@
       $(LD) $(INFO) $(LINK_OPT) $(LIST) $(LINK_CMD) $(TARGET).$(EXETYPE) $(OBJS)
 
# --------------------------------------------
# C file dependency list
# --------------------------------------------
ifeq ($(MAKECMDGOALS),)
-include $(C_OBJS:.o=.dep)
else
ifeq ($(filter all,$(MAKECMDGOALS)),all)
-include $(C_OBJS:.o=.dep)
endif
endif
 
# --------------------------------------------
# ASM file dependency list
# --------------------------------------------
bootblock.o : bootblock.s
bootblock.o : system.h

 
       可不要小看这短短的200多行Make File啊,正所谓“麻雀虽小,五脏俱全”,它可是该有的都有了。我们可以对它进行扩展,生成一个非常自动化的Make系统来。接下来我将逐行解释这个文件中的内容。
       最顶行的SHELL = sh是告诉make程序,使用的SHELL程序是sh.exe。在本例中使用的是cygwin环境下的shell程序和命令的。cygwin是用来在windows操作系统下模拟unix命令和操作方式的实用程序,通过Internet您可以很容易的找到这个程序以及GNU Make程序,不过ARM编译器就需要我们自己购买了。这是我很对不起各位读者的地方,虽然这个例子是在我的机器上测试通过的,但是各位读者朋友们要搭建这个环境就没那么容易了。这个环境中主要有三个要素:cygwin应用、GNU Make以及ARM编译器。
       接下来是注释并说明这个文件的,在Make File中“#”号表示后面紧接的是注释,作用于从“#”号之后的一行,千万不要幻想使用“#”注释多行语句,在Make File中没有可以注释多行的语句。接下来就进入了正式的Make File执行语句了。
4.3.1变量的定义

TARGET       = Test4#
SCLFILE       = system.scl#
C_OBJS        = mainapp.o raminit.o
A_OBJS        = bootblock.o
 
APP_PATH      = .#

       这里面定义了5个Make File变量,为了能够容易区分变量,通常在Make File中都使用全大写字符串做为变量名,当然这不是必须的,只是一个约定俗成的习惯而已。如果需要使用定义的变量则使用$()来饮用这个变量的值,如上面的OBJS = $(C_OBJS) $(A_OBJS)一句中就使用了C_OBJS和A_OBJS两个变量。如果我们要使用真实的“$”字符,那么我们需要用“$$”来表示。如果引用的变量没有定义,不要害怕,Make不会报错,Make会认为这个变量是空的。这里也提醒各位读者注意,要认真的使用变量名,不要弄错了,因为Make不会报告未定义变量的任何错误。
       Make File中的变量定义是不分先后的,比如上面的C_OBJS和A_OBJS可以定义在OBJS变量之后,所得到的结果是一样的。这是一个十分具有迷惑性的方式,我不建议使用这种方式,虽然它可能让我们觉得很“自由”。为了避免这种情况,可以使用“:=”号来为变量赋值,这种方式只能使用已经定义好的变量,如果变量在前面没有定义,则使用空值来代替。例如:
       A = $(B)
       B = debug
       此时,A的值是debug。如果使用
       A := $(B)
       B = debug
       则此时A的值是空,因为在A变量之前B还没有定义。与之对应的赋值操作符还有“?=”,它的作用是首先判断这个变量有没有在前面被定义过,如果没有定义则给这个变量赋值,否则使用已经定义的值。
       在这里,OBJS变量也可以采用如下的方式实现:
       OBJS = $(C_OBJS)
OBJS += $(A_OBJS)
其中使用“+=”来进行变量的连接。
       这六个变更的意义依次是:目标文件的名称、链接器使用的Scatter-Loading描述文件名、C语言的.o文件、汇编语言的.o文件、全部.o文件和.c文件的路径。
4.3.2搜索路径

# set the search path
vpath %.c $(APP_PATH)
vpath %.s $(APP_PATH)

       vpath是Make用来设置指定文件类型搜索路径的,上面两句分别设置了.c和.s的源文件搜索路径。在大型项目中,由于多个路径都存在源文件(.c),因此可以设置多个同扩展名的vpath,并按照所设置路径的先后顺序进行源文件的搜索。注意,这里可给我们提供了一种设置路径优先级的方法。例如,在A路径中有一个main.c,在B路径中也有一个main.c,那么我们就可以通过优先设置那一个路径的vpath %.c,来决定优先编译哪个路径的mian.c了。这种设置方法有的时候是很有用的,各位读者可以根据系统各自不同的需求来使用。
4.3.3编译器变量的定义

#-------------------------------------------------------------------------------
# Target file name and type definitions
#-------------------------------------------------------------------------------
 
EXETYPE    = elf#                # Target image file format
MODULE     = bin#                # Binary module extension
 
#-------------------------------------------------------------------------------
# Target compile time symbol definitions
#-------------------------------------------------------------------------------
 
ARMASM     = -D_ARM_ASM_#
 
#-------------------------------------------------------------------------------
# Software tool and environment definitions
#-------------------------------------------------------------------------------
ARMBIN = $(ARMHOME)/Bin#
ARMBIN := $(subst \,/,$(ARMBIN))#
 
ARMCC   = $(ARMBIN)/armcc#       # ARM ADS ARM 32-bit inst. set ANSI C compiler
ASM     = $(ARMBIN)/armasm#      # ARM ADS assembler
LD      = $(ARMBIN)/armlink#     # ARM ADS linker
HEXTOOL = $(ARMBIN)/fromelf#     # ARM ADS utility to create hex file from image
 
OBJ_CMD    = -o#                 # Command line option to specify output filename

       在这段代码中主要定义了各种使用的变量,EXETYPE和MODULE分别是链接生成的文件的扩展名;ARMASM是给编译器使用的符号定义变量;ARMBIN是ARM编译器所在文件路径;接下来的是使用的ARM编译器可执行程序名。这里主要说明一下“ARMBIN := $(subst \,/,$(ARMBIN))#”语句,subst是Make字符串替换函数,原形是:
       $(subst ,,)
       意义是将字符串中的字符串替换成字符串。在这里是将“\”替换成“/”来表示路径分割符,在Make File中将“/”做为路径的分割符的。
       在紧接着的代码都是定义编译器和链接器命令的变量,这里就不一一的介绍了,有兴趣的读者可以参考ARM编译器的帮助文档。
4.3.4依赖规则

#----------------------------------------------------------------------------
# Default target
#----------------------------------------------------------------------------
.PHONY : all
all : startup $(TARGET).$(MODULE) complete
 
.PHONY : startup
startup:
       @echo ---------------------------------------------------------------
       @echo Compile startup
       @echo ---------------------------------------------------------------
      
.PHONY : complete
complete:
       @echo ---------------------------------------------------------------
       @echo All have been done
       @echo ---------------------------------------------------------------

       这里的all是整个Make File中的第一个依赖关系,根据前面的叙述我们知道,make会从这个依赖关系开始执行。all是make的一个目标,由于all只是一个标签,并没有与之相关的文件,因此我们称all为一个“伪目标”。“.PHONY”用来显式地指明一个目标是“伪目标”,目的是向make说明不管是否有这个文件,这个目标都是“伪目标”。通常在没有和“伪目标”同名的文件情况下,是不需要使用“.PHONY”来显式声明一个“伪目标”的。
       我们可以看到all目标有三个子目标:startup、$(TARGET).$(MODULE)和complete。make将按照这个顺序执行三个子目标。startup和complete两个目标显示了开始和结束的提示信息,echo是在屏幕上输出信息的命令。默认情况下make将显示其执行的命令,在命令前面加上“@”符号将不会输出命令信息。如startup部分如果不加“@”则会有如下结果输出:
       echo ---------------------------------------------------------------
       ---------------------------------------------------------------
       echo Compile startup
       Compile startup
       echo ---------------------------------------------------------------
       ---------------------------------------------------------------
 
       接下来让我们看看$(TARGET).$(MODULE)规则:

#----------------------------------------------------------------------------
# Lib file targets
#----------------------------------------------------------------------------
 
$(TARGET).$(MODULE) : $(TARGET).$(EXETYPE)
       @echo ---------------------------------------------------------------
       @echo TARGET $@
       $(HEXTOOL) $(TARGET).$(EXETYPE) $(BINFORMAT) $(OUTPUT) $(TARGET).$(MODULE)
 
$(TARGET).$(EXETYPE) : $(OBJS)
       @echo ---------------------------------------------------------------
       @echo TARGET $@
       $(LD) $(INFO) $(LINK_OPT) $(LIST) $(LINK_CMD) $(TARGET).$(EXETYPE) $(OBJS)

       $(TARGET).$(MODULE)依赖于$(TARGET).$(EXETYPE),当$(TARGET).$(EXETYPE)文件比$(TARGET).$(MODULE)文件要新的时候,就会执行$(TARGET).$(MODULE)命令,执行生成$(TARGET).$(MODULE)文件的命令。
       $(TARGET).$(EXETYPE)依赖于$(OBJS)中包含的各个.o文件。如果有任何一个.o文件比$(TARGET).$(EXETYPE)文件要新,则执行链接命令。$@表示目标文件集,在本例中也就是$(TARGET).$(EXETYPE) : $(OBJS)中的$(TARGET).$(EXETYPE)。
       接下来就到了.o文件的以来关系了,这部分是一个Make File的主要处理的部分,在正式介绍它们之前我们先看下面的定义:

#=====================================================================
#                           DEFAULT SUFFIX RULES
#=====================================================================
SRC_FILE = $(@F:.o=.c)#        # Input source file specification
OBJ_FILE = $(OBJ_CMD) $(@F)#   # Output object file specification
 
.SUFFIXES :
.SUFFIXES : .o .c .s .mix .dep

       SRC_FILE = $(@F:.o=.c)定义了输入的源文件文件名,$(@F)表示$@中的文件名部分,
$(@F:.o=.c)就是将$(@F)中的.o替换成.c,从而得到了源文件名。注意,这一句需要在执行命令的时候才会进行真正的展开,因此在这里只是定义了一个变量。OBJ_FILE定义了一个输出目标.o文件的变量。
       .SUFFIXES是用来通知make新的扩展名。.SUFFIXES:语句是清空默认的文件扩展名识别。.SUFFIXES : .o .c .s .mix .dep声明了五个文件扩展名:
       .o    —— C编译器生成的文件
       .c    —— C语言源文件
       .s    —— 汇编语言源文件
       .dep —— .c和.s文件的头文件依赖关系
       .mix —— 由.c生成C和汇编语句混合文件的扩展名

#--------------------------------------------------------------------------
# C code inference rules
#----------------------------------------------------------------------------
%.o:%.c
       @echo ---------------------------------------------------------------
       @echo OBJECT $(@F)
       $(ARMCC) $(CFLAGS) $(OBJ_FILE) $(SRC_FILE)
       @echo ---------------------------------------------------------------
      
%.mix:%.c
       @echo ---------------------------------------------------------------
       @echo OBJECT $(@F)
       $(ARMCC) -S -fs $(CFLAGS) $(INC) $(OBJ_FILE) $<
       @echo ---------------------------------------------------------------

       上面定义了.o和.mix文件的默认依赖关系,在这里固定了它们和源文件.c之间的关系。“%”号是通配符,%.o代表任何一个.o文件。%.o:%.c指定相应的.o文件依赖于.c文件。$<表示依赖的文件名,这里是指%.c所匹配的文件。.mix文件是将C语句“翻译”成对应的汇编语句,这个文件可以用来分析C源文件的效率。

#-------------------------------------------------------------------------------
# Assembly code inference rules
#-------------------------------------------------------------------------------
%.o:%.s
       @echo ---------------------------------------------------------------
       @echo OBJECT $(@F)
       $(ARMCC) -ansic -E $(AFLAGS) $(ARMASM) $< > $*.i
       $(ASM) $(AFLAGS) $(OBJ_FILE) $*.i
       @echo ---------------------------------------------------------------

       上面定义了.o文件与.s文件的依赖关系。$(ARMCC) -ansic -E $(AFLAGS) $(ARMASM) $< > $*.i语句的意思是将汇编语句首先使用编译器进行预编译处理,这样处理的好处是我们可以在汇编文件中使用C的预编译指令了,比如#define和#include等。处理之后的文件通过“>”重定位符号输出到$*.i文件中。$*表示目标的没有扩展名部分,比如dir/main.o :mian.c则$*等于dir/main部分。
4.3.5 .Dep文件

#-------------------------------------------------------------------------------
# Depend file inference rules
#-------------------------------------------------------------------------------
%.dep:%.c
       @echo ---------------------------------------------------------------
       $(ARMCC) -ansic -M $< > $*.dep
       @echo ---------------------------------------------------------------

       上面定义了.dep文件的依赖关系。.dep文件是由编译器输出的符合make依赖规则的.o文件与.h文件的依赖性规则定义的文本文件。这里就是生成.dep文件的命令部分。在Make File中会通过include命令包含.dep文件(include还可以包含其他的Make File文件),从而引入目标文件与头文件之间的依赖关系,典型的.dep文件内容如下:

mainapp.o:      mainapp.c
mainapp.o:      system.h

       下面来看看怎样包含.dep文件:

# --------------------------------------------
# C file dependency list
# --------------------------------------------
ifeq ($(MAKECMDGOALS),)
-include $(C_OBJS:.o=.dep)
else
ifeq ($(filter all,$(MAKECMDGOALS)),all)
-include $(C_OBJS:.o=.dep)
endif
endif

       ifneq(ifeq)是Make File的条件判断语句,在Make File中有六个条件判断的关键字:ifeq、ifneq、ifdef、ifndef、else和endif。ifeq的意思表示条件语句的开始,并指定一个条件表达式,表达式包含两个参数,以逗号分隔,表达式以圆括号括起。else表示条件表达式为假的情况。endif表示一个条件语句的结束,任何一个条件表达式都应该以endif结束。ifeq是判断条件相等,ifneq是判断条件不等。ifdef/ifndef是判断变量是/否被定义。
       $(filter all,$(MAKECMDGOALS))是从变量MAKECMDGOALS中过滤出all字符串。MAKECMDGOALS变量中会存放我们所指定的终极目标的列表,如果在命令行上,我们没有指定目标,那么这个变量是空值。这个终极目标是指我们在调用make命令时输入的,例如执行make clean,则MAKECMDGOALS变量值就是clean。filter是make的过滤函数,与之对应的还有filter –out的反过滤函数,它们的函数原型如下:
       $(filter ,)
       $(filter-out ,)
       filter函数以模式过滤字符串中的单词,保留符合模式的单词。filter-out函数以模式过滤字符串中的单词,去除符合模式的单词。
       这里ifeq ($(MAKECMDGOALS),)判断用户指定的目标是空时则执行include命令。否则在目标是all的时候也执行include命令。如果include的文件不存在,make会查找该文件的依赖关系,在这里则产生了.dep的依赖关系并执行生成.dep文件的命令。在命令前面加“-”表示忽略执行命令时产生的错误。
       这部分就完成了一个由include触发的.dep的生成.dep到包含.o与.h依赖关系的整个过程,具有很强的参考价值。
4.3.6清除规则

#----------------------------------------------------------------------------
# Clean target
#----------------------------------------------------------------------------
 
# The object subdirectory, target image file, and target hex file are deleted.
.PHONY : clean
clean :
       @echo ---------------------------------------------------------------
       @echo CLEAN
       -rm -f *.o
       -rm -f *.i
       -rm -f *.dep
       -rm -f $(TARGET).$(EXETYPE)
       -rm $(TARGET).$(MODULE)
       -rm $(TARGET).map
       @echo ---------------------------------------------------------------
      

       这里就是调用了一些前面讲过的命令,清除生成的文件。rm是一个Shell程序,不同的Shell会有不同的命令,各位读者可以根据自己不同的运行环境进行相应的修改。
符号说明
       在上一节中介绍了大部分Make File中的符号的意义和用法,这里为了方便各位查找,将最常用的一些Make File符号列举出来。
4.4.1关键词

关键词
用途
define
定义一个“数据包”,是用enddef做结尾,可以包含多行的命令。
ifeq/ifneq
条件判断,可以搭配else使用,endif结尾。原型:ifeq(Arg1,Arg2)。
ifdef/ifndef
变量是否定义的条件判断,可以搭配else使用,endif结尾。原型:ifdef Var。
=
变量赋值语句。如果右值包含另一个变量,则可以在后面定义这个变量。
:=
变量赋值语句。如果右值包含另一个变量,则只能引用已定义的变量。
?=
条件赋值语句。如果此变量未定义才重新赋值。
+=
为当前变量追加内容。
%
通配符
vpath
设置搜索路径,原型vpath %.x ,x表示文件扩展名
\
换行符
@
放在命令前面隐藏命令输出
-
放在命令前面忽略命令错误
:
依赖规则定义符,使用方式:目标:依赖
override
用来指示即便此变量是由make的命令行参数设置的,也使用新的赋值。因为默认情况下Makefile中对这个变量的赋值会被忽略。
.PHONY
显式声明伪目标
.SUFFIXES
声明扩展名

4.4.2 Make函数
       按功能字符串、函数名排序:

函数原型
描述
$(subst ,,)
把字串中的字符串替换成
$(patsubst ,,)
查找中的单词(单词以“空格”、“Tab”或“回车”“换行”分隔)是否符合模式,如果匹配的话,则以替换。这里,可以包括通配符“%”,表示任意长度的字串。如果中也包含“%”,那么,中的这个“%”将是中的那个“%”所代表的字串。(可以用“\”来转义,以“\%”来表示真实含义的“%”字符)
$(strip )
去掉字串中开头和结尾的空字符。
$(findstring ,)
在字串中查找字串。
$(filter ,)
模式过滤字符串中的单词,保留符合模式的单词。可以有多个模式。
$(filter-out ,)
模式过滤字符串中的单词,去除符合模式的单词。可以有多个模式。
$(sort )
给字符串中的单词排序(升序)。
$(word ,)
取字符串中第个单词。(从一开始)
$(wordlist ,,)
从字符串中取从开始到的单词串。是一个数字。
$(words )
统计中字符串中的单词个数。
$(firstword )
取字符串中的第一个单词。
$(dir )
从文件名序列中取出目录部分。目录部分是指最后一个反斜杠(“/”)之前的部分。如果没有反斜杠,那么返回“./”。
$(notdir )
从文件名序列中取出非目录部分。非目录部分是指最后一个反斜杠(“/”)之后的部分。
$(suffix )
从文件名序列中取出各个文件名的后缀。
$(basename )
从文件名序列中取出各个文件名的前缀部分。
$(addsuffix ,)
把后缀加到中的每个单词后面。
$(addprefix ,)
把前缀加到中的每个单词后面。
$(join ,)
中的单词对应地加到的单词后面。如果的单词个数要比的多,那么,中的多出来的单词将保持原样。如果的单词个数要比多,那么,多出来的单词将被复制到中。
$(foreach ,,)
把参数中的单词逐一取出放到参数所指定的变量中,然后再执行所包含的表达式。每一次会返回一个字符串,循环过程中,的所返回的每个字符串会以空格分隔,最后当整个循环结束时,所返回的每个字符串所组成的整个字符串(以空格分隔)将会是foreach函数的返回值。
$(if ,)
$(if ,,),参数是if的表达式,如果其返回的为非空字符串,那么这个表达式就相当于返回真,于是,会被计算,否则会被计算。
$(call ,,,...)
call函数是唯一一个可以用来创建新的参数化的函数。我们可以写一个非常复杂的表达式,这个表达式中,我们可以定义许多参数,然后我们可以用call函数来向这个表达式传递参数。当make执行这个函数时,参数中的变量,如$(1),$(2),$(3)等,会被参数依次取代。而的返回值就是call函数的返回值。
$(origin )
origin函数不像其它的函数,他并不操作变量的值,他只是告诉我们这个变量是哪里来的。是变量的名字,不应该是引用。所以我们最好不要在中使用“$”字符。Origin函数会以其返回值来告诉我们这个变量的“出生情况”,下面,是origin函数的返回值:
“undefined”
      如果从来没有定义过,origin函数返回这个值“undefined”。
 default”
      如果是一个默认的定义,比如“CC”这个变量,这种变量我们将在后面讲述。
“environment”
      如果是一个环境变量,并且当Makefile被执行时,“-e”参数没有被打开。
file”
      如果这个变量被定义在Makefile中。
“command line”
      如果这个变量是被命令行定义的。
 “override”
      如果是被override指示符重新定义的。
“automatic”
      如果是一个命令运行中的自动化变量。
$(error )
产生一个致命的错误,是错误信息。退出Make执行。
$(warning )
输出一段警告信息,而make继续执行。
$(shell )
使用Shell执行命令

 
4.4.3自动化变量
       自动化变量通常用来在依赖规则的命令行中表示规则的一部分。通常依赖规则是如下定义方式:Target : Prerequisites。

$@
表示规则中的目标文件集Target。在模式(即"%")规则中,如果有多个目标,那么,"$@"就是匹配于目标中模式定义的集合。
$%
仅当目标是函数库文件中,表示规则中的目标成员名。例如,如果一个目标是"foo.a(bar.o)",那么,"$%"就是"bar.o","$@"就是"foo.a"。如果目标不是函数库文件(Unix下是[.a],Windows下是[.lib]),其值为空。
$<
依赖目标中的第一个目标名字。如果依赖目标是以模式(即"%")定义的,那么"$<"将是符合模式的一系列的文件集。注意,其是一个一个取出来的。
$?
所有同目标相比更新的依赖目标的集合。以空格分隔。
$^
所有的依赖目标的集合。以空格分隔。如果在依赖目标中有多个重复的,那个这个变量会去除重复的依赖目标,只保留一份。
$+
这个变量很像"$^",也是所有依赖目标的集合。只是它不去除重复的依赖目标。
$*
这个变量表示目标模式中"%"及其之前的部分。如果目标是"dir/a.foo.b",并且目标的模式是"a.%.b",那么,"$*"的值就是"dir/a.foo"。这个变量对于构造有关联的文件名是比较有效。如果目标中没有模式的定义,那么"$*"也就不能被推导出,但是,如果目标文件的后缀是make所识别的,那么"$*"就是除了后缀的那一部分。例如:如果目标是"foo.c",因为".c"是make所能识别的后缀名,所以,"$*"的值就是"foo"。这个特性是GNU make的,很有可能不兼容于其它版本的make,所以,我们应该尽量避免使用"$*",除非是在隐含规则或是静态模式中。如果目标中的后缀是make所不能识别的,那么"$*"就是空值。

       上面七个自动化变量可以加上D(Directory)或F(File Name)来分别表示路径和文件名部分。例如,$(@F)表示"$@"的文件部分,如果"$@"值是"dir/foo.o",那么"$(@F)"就是"foo.o","$(@F)"相当于函数"$(notdir $@)";$(@D) 表示"$@"的目录部分(不以斜杠做为结尾),如果"$@"值是"dir/foo.o",那么"$(@D)"就是"dir",而如果"$@"中没有包含斜杠的话,其值就是"."(当前目录)。
小结
       Make是用来进行工程管理的一个非常有效的机制,它核心的思想是通过定义不同的依赖关系,实现工程编译的管理,不必每次编译的时候都将全部的源文件重新编译,而只需要编译从上次编译时直到现在已经修改的源文件。虽然现代的集成开发环境(IDE)已经不再需要我们直接修改Makefile,但是在一些没有IDE开发环境领域(如嵌入式系统或操作系统的开发)还是需要直接使用Make进行工程管理的。况且,通过对Make的了解可以贯通整个程序开发的过程,这对于希望掌握程序开发来龙去脉的相关人员来说是十分有必要的。还有一点是现在关于Make的文档和书籍基本上没在市面上见到,就算是有提到也不是十分的详细,因此希望这里可以提供给各位读者一个相对系统和详细的介绍。
       Make与编译器之间有什么样的关系?
阅读(512) | 评论(0) | 转发(0) |
0

上一篇:BREW(1)

下一篇:brew7

给主人留下些什么吧!~~