基本   makefile   结构   
    GNU   Make   的主要工作是读进一个文本文件,   makefile   。这个文件里主要是有关哪些文件('target'目的文件)是从哪些别的文件('dependencies'依靠文件)中产生的,用什么命令来进行这个产生过程。有了这些信息,   make   会检查磁碟上的文件,如果目的文件的时间戳(该文件生成或被改动时的时间)比至少它的一个依靠文件旧的话,   make   就执行相应的命令,以便更新目的文件。(目的文件不一定是最后的可执行档,它可以是任何一个文件。)   
    makefile   一般被叫做"makefile"或"Makefile"。当然你可以   在   make   的命令行指定别的文件名。如果你不特别指定,它会寻找"makefile"或"Makefile",因此使用这两个名字是最简单的。   
    一个   makefile   主要含有一系列的规则,如下:   
  :   ...   
  (tab)<command>   
  (tab)<command>   
  .   
  .   
  .   
  例如,考虑以下的   makefile   :   
  ===   makefile   开始   ===   
  myprog   :   foo.o   bar.o   
  gcc   foo.o   bar.o   -o   myprog   
  foo.o   :   foo.c   foo.h   bar.h   
  gcc   -c   foo.c   -o   foo.o   
  bar.o   :   bar.c   bar.h   
  gcc   -c   bar.c   -o   bar.o   
  ===   makefile   结束   ===   
    这是一个非常基本的   makefile   --   make   从最上面开始,把上   面第一个目的,'myprog',   做为它的主要目标(一个它需要保证其总是最新的最终目标)。给出的规则说明只要文件'myprog'   比文件'foo.o'或'bar.o'中的任何一个旧,下一行的命令将   会被执行。   
    但是,在检查文件   foo.o   和   bar.o   的时间戳之前,它会往下查   找那些把   foo.o   或   bar.o   做为目标文件的规则。它找到的关于   foo.o   的规则,该文件的依靠文件是   foo.c,   foo.h   和   bar.h   。   它从下面再找不到生成这些依靠文件的规则,它就开始检查磁碟上这些依靠文件的时间戳。如果这些文件中任何一个的时间戳比   foo.o   的新,命令   'gcc   -   o   foo.o   foo.c'   将会执行,从而更新文件   foo.o   。     
    接下来对文件   bar.o   做类似的检查,依靠文件在这里是文件   bar.c   和   bar.h   。现在,   make   回到'myprog'的规则。如果刚才两个规则中的任   何一个被执行,myprog就   
  需要重建(因为其中一个   .o   档就会比'myprog'新),因此连接命令将被执行。   
  2.2   编写   make   规则   (Rules)   
    最明显的(也是最简单的)编写规则的方法是一个一个的查看源码文件,把它们的目标文件做为目的,而C源码文件和被它   #include   的   header   档做为依靠文件。但是你也要把其它被这些   header   档   #include   的   header   档也列为依靠文件,还有那些被包括的文件所包括的文件……然后你会发现要对越来越多的文件进行管理,然后你的头发开始脱落,你的脾气开始变坏,你的脸色变成菜色,你走在路上开始跟电线杆子碰撞,终于你捣毁你的电脑显示器,停止编程。到底有没有些容易点儿的方法呢?   
    当然有!向编译器要!在编译每一个源码文件的时候,它实在应该知道应该包括什么样的header   档。使用   gcc   的时候,用   -M   开关,它会为每一个你给它的C文件输出一个规则,把目标文件做为目的,而这个C文件和所有应该被   #include   的   header   文件将做为依靠文件。注意这个规则会加入所有   header   文件,包   括被角括号(`<',   `>')和双引号(`"')所包围的文件。其实我们可以相当肯定系统   header   档(比如   stdio.h,   stdlib.h   等等)不会   被我们更改,如果你用   -   MM   来代替   -M   传递给   gcc,那些用角括号包围的   header   档将不会被包括。(这会节省一些编译时间)   
    由   gcc   输出的规则不会含有命令部分;你可以自己写入你的命令   或者什么也不写,而让make   使用它的隐含的规则(参考下面的   2.4   节)。   
    
  2.3   Makefile   变量   
    上面提到   makefiles   里主要包含一些规则。它们包含的其它的东西是变量定义。makefile   里的变量就像一个环境变量(environment   variable)。   事实上,环境变量在   make   过程中被解释成   make的变量。这些   变量是大小写敏感的,一般使用大写字母。它们可以从几乎任何   地方被引用,也可以被用来做很多事情,比如:   
    i)   贮存一个文件名列表。在上面的例子里,生成可执行文件的   规则包含一些目标文件名做为依靠。在这个规则的命令行   里同样的那些文件被输送给   gcc     
  做为命令参数。如果在这   里使用一个变数来贮存所有的目标文件名,加入新的目标   文件会变的简单而且较不易出错。   
    ii)   贮存可执行文件名。如果你的项目被用在一个非   gcc   的系统里,或者如果你想使用一个不同的编译器,你必须将所有使用编译器的地方改成用新的编译器名。但是如果使用一   个变量来代替编译器名,那么你只需要改变一个地方,其   它所有地方的命令名就都改变了。   
    iii)   贮存编译器旗标。假设你想给你所有的编译命令传递一组   相同的选项(例如   -Wall   -   O   -g);如果你把这组选项存入一个变量,那么你可以把这个变量放在所有呼叫编译器   的地方。而当你要改变选项的时候,你只需在一个地方改   变这个变量的内容。要设定一个变量,你只要在一行的开始写下这个变量的名字,后面跟一个   =   号,后面跟你要设定的这个变量的值。以后你要引用这个变量,写一个   $   符号,后面是围在括号里的变量名。比如在下面,我们把前面的   makefile   利用变量重写一遍:   
  ===   makefile   开始   ===   
  OBJS   =   foo.o   bar.o   
  CC   =   gcc   
  CFLAGS   =   -Wall   -O   -g   
  myprog   :   $(OBJS)   
  $(CC)   $(OBJS)   -o   myprog   
  foo.o   :   foo.c   foo.h   bar.h   
  $(CC)   $(CFLAGS)   -c   foo.c   -o   foo.o   
  bar.o   :   bar.c   bar.h   
  $(CC)   $(CFLAGS)   -c   bar.c   -o   bar.o   
  ===   makefile   结束   ===   
    还有一些设定好的内部变量,它们根据每一个规则内容定义。三个比较有用的变量是   $@,   $<   和   $^   (这些变量不需要括号括住)。   $@   扩展成当前规则的目的文件名,   $<   扩展成依靠列表中的第   一个依靠文件,而   $^   扩展成整个依靠的列表(除掉了里面所有重复的文件名)。利用这些变量,我们可以把上面的   makefile   写成:   
  ===   makefile   开始   ===   
  OBJS   =   foo.o   bar.o   
  CC   =   gcc   
  CFLAGS   =   -Wall   -O   -g   
  myprog   :   $(OBJS)   
  $(CC)   $^   -o   $@   
  foo.o   :   foo.c   foo.h   bar.h   
  $(CC)   $(CFLAGS)   -c   $<   -o   $@   
  bar.o   :   bar.c   bar.h   
  $(CC)   $(CFLAGS)   -c   $<   -o   $@   
  ===   makefile   结束   ===   
    你可以用变量做许多其它的事情,特别是当你把它们和函数混合使用的时候。如果需要更进一步的了解,请参考   GNU   Make   手册。   ('man   make',   'man   makefile')   
    
  2.8   一个更好的   makefile   
    这是一个我为我大多数项目设计的   makefile   。它应该可以不需要修改的用在大部分项目里。我主要把它用在   djgpp   上,那是一个   DOS   版的gcc   编译器。因此你可以看到执行的命令名、   'alleg'   程序包、   和   RM   -F   变量都反映了这一点。   
  ===   makefile   开始   ===   
  ######################################   
  #     
  #   Generic   makefile     
  #     
  #   by   George   Foot     
  #   email:   george.foot@merton.ox.ac.uk     
  #     
  #   Copyright   (c)   1997   George   Foot     
  #   All   rights   reserved.     
  #   保留所有版权     
  #     
  #   No   warranty,   no   liability;     
  #   you   use   this   at   your   own   risk.     
  #   没保险,不负责     
  #   你要用这个,你自己担风险     
  #     
  #   You   are   free   to   modify   and     
  #   distribute   this   without   giving     
  #   credit   to   the   original   author.     
  #   你可以随便更改和散发这个文件     
  #   而不需要给原作者什么荣誉。     
  #   (你好意思?)     
  #     
  ######################################   
  ###   Customising   
  #   用户设定   
  #   
  #   Adjust   the   following   if   necessary;   EXECUTABLE   is   the   target   
  #   executable's   filename,   and   LIBS   is   a   list   of   libraries   to   link   in   
  #   (e.g.   alleg,   stdcx,   iostr,   etc).   You   can   override   these   on   make's   
  #   command   line   of   course,   if   you   prefer   to   do   it   that   way.   
  #     
  #   如果需要,调整下面的东西。   EXECUTABLE   是目标的可执行文件名,   LIBS   
  #   是一个需要连接的程序包列表(例如   alleg,   stdcx,   iostr   等等)。当然你   
  #   可以在   make   的命令行覆盖它们,你愿意就没问题。   
  #     
  EXECUTABLE   :=   mushroom.exe   
  LIBS   :=   alleg   
  #   Now   alter   any   implicit   rules'   variables   if   you   like,   e.g.:   
  #   
  #   现在来改变任何你想改动的隐含规则中的变量,例如   
  CFLAGS   :=   -g   -Wall   -O3   -m486   
  CXXFLAGS   :=   $(CFLAGS)   
  #   The   next   bit   checks   to   see   whether   rm   is   in   your   djgpp   bin   
  #   directory;   if   not   it   uses   del   instead,   but   this   can   cause   (harmless)   
  #   `File   not   found'   error   messages.   If   you   are   not   using   DOS   at   all,   
  #   set   the   variable   to   something   which   will   unquestioningly   remove   
  #   files.   
  #   
  #   下面先检查你的   djgpp   命令目录下有没有   rm   命令,如果没有,我们使用   
  #   del   命令来代替,但有可能给我们   'File   not   found'   这个错误信息,这没   
  #   什么大碍。如果你不是用   DOS   ,把它设定成一个删文件而不废话的命令。   
  #   (其实这一步在   UNIX   类的系统上是多余的,只是方便   DOS   用户。   UNIX   
  #   用户可以删除这5行命令。)   
  ifneq   ($(wildcard   $(DJDIR)/bin/rm.exe),)   
  RM-F   :=   rm   -f   
  else   
  RM-F   :=   del   
  endif   
  #   You   shouldn't   need   to   change   anything   below   this   point.   
  #   
  #   从这里开始,你应该不需要改动任何东西。(我是不太相信,太NB了!)   
  SOURCE   :=   $(wildcard   *.c)   $(wildcard   *.cc)   
  OBJS   :=   $(patsubst   %.c,%.o,$(patsubst   %.cc,%.o,$(SOURCE)))   
  DEPS   :=   $(patsubst   %.o,%.d,$(OBJS))   
  MISSING_DEPS   :=   $(filter-out   $(wildcard   $(DEPS)),$(DEPS))   
  MISSING_DEPS_SOURCES   :=   $(wildcard   $(patsubst   %.d,%.c,$(MISSING_DEPS))   \   
  $(patsubst   %.d,%.cc,$(MISSING_DEPS)))   
  CPPFLAGS   +=   -MD   
  .PHONY   :   everything   deps   objs   clean   veryclean   rebuild   
  everything   :   $(EXECUTABLE)   
  deps   :   $(DEPS)   
  objs   :   $(OBJS)   
  clean   :   
  @$(RM-F)   *.o   
  @$(RM-F)   *.d   
  veryclean:   clean   
  @$(RM-F)   $(EXECUTABLE)   
  rebuild:   veryclean   everything   
  ifneq   ($(MISSING_DEPS),)   
  $(MISSING_DEPS)   :   
  @$(RM-F)   $(