怎么写Makefile

内容简要说明

本文主要说明怎样写一个   makefile,用于   UNIX/C   编程环境下。对于每一
方面,我将只说明最简单的用法(别的我也不会,呵呵)。同时,因为我没
有办法在很多系统上测试   makefile   ,所以只能说明一些应该是通用的参数
或用法。其他更深入的内容请参考手册:man   make   。

呵呵,本文预先假设你知道   unix,   c,   make,   cc,   ar   等等是干什么用的。
这样我就不用解释过多东西了。
注: <s>   是指一个空格或一个tab   -   制表符
        <tab>   专指一个制表符!


  make   的工作原理和方法简介

  make   根据一个或多个文件定义的一系列规则,通过运行其他程序如:cc,  
  ar   等等来建立调用者想得到的目标,它预先检查依赖性以至于不必作一些
无用功。makefile   是指描述这些目标、规则以及依赖性的一个文件。

如果   make   认为一个目标需要重新建立,即不是最新的,它就根据所知道
的规则来一步步建立这个文件,中间也建立这个目标的依赖者   -   如果需要
的话。如果   make   认为某个目标不需要重新建立,即不是最新的,那么它就
什么都不做。有的系统会说   target   is   up   to   date.

这样   make   先搜索到一个目标(比如说   all   ),然后把它的依赖者当作目
标继续搜索,直到   makefile   中再没有对目标的描述了。每个目标除非
  1,目录下面有一个同名的文件,或者
  2,有目标下有一个描述(不一定非要能够或可以产生这个文件),或者
  3,   make   自己知道怎么产生这个文件;
否则   make   会说   Don 't   know   how   to   make   target   'target '

注意   make   只会对搜索的第一个目标采取动作,即搜索与之相关的其他依
赖者以及建立的方法并运行命令等,此后即使你写了一个完整的目标描述,
如果没有把它与上面的第一个目标相关联的话(关联可以是间接的,即通
过好多中间目标),make   将不会为你检查该目标的依赖性,以及运行命令
并且建立这个(些)目标!!这一点比较悲惨是吧?避免让   make   忘记   建
立目标的最好的方法就是在开头先写一个   all:   alltargetslist!^_^
参见例子。


  make   的命令行开关

  -f   filenamelist   指定   make   用的   makefiles,可以是一个或几个。如果
没有   -f,make   搜索的顺序是   makefile,   Makefile,   ......   所以本文中
一律称为   makefile  

  -i   忽略   make   执行命令时检测到的错误,继续执行其他的命令。相当于
在   makefile   中有特殊功能:.IGNORE

  -n   只读取   makefile   文件并建立规则,但不执行相应的命令,这样可以
把命令打印出来供参考。注意有些特殊定义的命令将被执行,比如   .POSIX
  mode   中,前面带   +   的命令。

  -p   打印出   make   搜索到的完整的宏定义和目标说明。这个开关往往带有
很多输出,因为   make   要搜索的文件很多。

  -s   Silent   Mode.   这样   make   将在执行前不显示要执行的命令。相当于
在   makefile   中有特殊功能:.SILENT

其他开关参见手册。


  makefile   的宏定义

基本语法是   macro=value
如果   value   一行写不完可以用   <s> \   续行。
引用的时候要加   $   和括号,如   $(macro),或   ${macro}
注意:
1、宏名一般用大写以引起注意。
2、value   可以是以空格分开的好多个(比如目标,或者。。。)
3、你可以把一下所说的目标和依赖者预先用宏定义一下,这样以后就可以少
      写点东西,参见例子。


  makefile   的目标写法

基本语法:
头一行是   target1   target2   ...:   denpendency1   denpendency2   ...
注意到目标可以是列表,这样就跟一个一个目标的写法一个意思。但我一般
一次只写一个目标,因为好像多个目标在一块儿的时候下面的   dynamic
  macros   没法用(值不是我想要的)。这样就是:
target:   denpendency1   ...
冒号后面是此目标的依赖者,也就是说,此目标的建立将依赖于后面列表中
的每个程序-   通常也是   make   的一个目标。
如果依赖者一行写不完可以用   <s> \   续行。
然后就是对建立这个目标的描述:即怎样建立这个目标   -   一系列命令。
当然也可以什么都不写。这样   make   就不会建立任何目标(   all   通常就是
这样)。但   make   检查依赖性。下面所说的   all   也就是通过这个原理来建
立冒号之后的其他目标:)
**   注意:每行命令必须以一个tab为开头!!!后面跟要运行的命令,如
  cc   ......如果一行写不完可以用   <s> \   续行。续行也必须首先以一个tab
为开头!!!
命令前面(必须在tab后面)可以有   +,   -,   @,   !   等特殊符号,各有特别的
意义,这个我反正极少用,只好参考手册了:(

注意好多   makefile   中都一个目标叫   all,我原先以为是   make   的一个默认
目标,其实不是,make   没有任何默认的目标。不过在一开始写一个   all   的
目标是一个好习惯,因为这样编辑   makefile   时一下就能发现   make   会检查
哪些目标。这种情况我一般称   all   是   虚目标,而冒号之后   all   的依赖者称
为   实目标,即实际上想让   make   生成的目标。但是,如果该目录下面同时有
一个名为   all   的文件,那样就会引起   make   的混乱;如果真是这样,那你可
以改用   alltargets   什么的。


  makefile   中的特殊东西

注释和   include

注释:任何前面有   #   的行。
注意如果一个目标的命令后有   #   ,   make   会照常把它传递给   shell,然后
  shell   也当作是注释:)
如果开头有   include   filename   等行,make   会先   include   这个文件,就象
  C   语言中的   include   差不多。

  Special_Function   Target
在   makefile   的开头写   .IGNORE   .SILENT   .PRECIOUS   等等是什么意思?
.IGNORE   和   .SILENT   :   见上面   make   的命令行开关部分。
.PRECIOUS   是这样的:当   make   正在进行   make   的时候,如果你按下
  Ctrl-C,make   就会把当前这个目标文件给删除掉。如果   makefile   开头
有这个   .PRECIOUS,make   就不会这样做。
还有   .SUFFIXES   也很有用,主要是用来定义程序后缀的。不过对   C   语言
的用户来说,无非是   .c,.o   两种,都是应该已经定义好的,就不需要了。

  Special_Rules   Target
定义了   .SUFFIES   以后,你就可以为一些程序编译工作定义一些通用的规
则,例如在   C   语言中一般   .c   编译到   .o   文件的过程和参数都差不多,都是
cc   -c   -g   ***.c   什么的。这样你可以在   makefile   中这样写:

  .c.o:
  <tab> cc   -c   $ <

这样   make   在建立目标的过程中,需要把   .c   变成(一般是编译到).o   文件
的时候就运行   cc   -c   ***.c   产生   .o   文件。这样你就不必为每一个   .c   到
  .o   定义规则了。
注:我发现好像这个   $ <   只有在这里才起作用,不知为什么?
注意这个规则不必也没法写在另一个目标里面作依赖者,所以这个规则的依赖
性已经预先定义好了:.o   文件总是依赖于   .c   文件,除非你另外定义。

  Dynamic   Macros
也只能解释其中几个我经常用的。
1、$*   我只能以例子说明。比如你写:

  target1.c:   ...
  <tab> command   $*   ....

这个时候的   $*   是指   target1   。这样   $*   可以用来处理编译或预编译过程
中产生的中间文件,如   .o   等等(就写成   $*.o)
2、$ <   见上面的特殊规则说明。
3、$@   指当前正在建立的目标名。如上面的写法中,$@   是指   target1.c


这样   makefile   中的东西我知道的基本上解释完了,其实学写   makefile  
的最好的方法是   copy   一个来改成自己所需要的,我一开始也是这么干的!
为了方便大家,我最后给几个简单的例子:)
注意调试的时候多用用   make   -n,   -f   ...   什么的。


例子   1:
这个是最普通的。

all:   program1   program2

program1.o:   program1.c   program1.h
cc   -c   -g   -O   program1.c
program2.o:   program2.c   program2.h
cc   -c   -g   -O   program2.c
program1:   program1.o
cc   -o   $@   program1.o
program2:   program2.o
cc   -o   $@   program2.o

如果要简化一下,可以这样写:

all:   program1   program2

.c.o:
cc   -c   $ <

program1:   program1.o
cc   -o   $@   program1.o
program2:   program2.o
cc   -o   $@   program2.o

但这时,如果你修改了   program1.h   or   program2.h,make
不会知道要重新编译,因为没有定义对   .h   文件的依赖性。


例子   2:
这个例子中,该目录下面有3个文件:abc1.c   abc2.ec   abc3.c
我想利用   ar   建立一个静态库   libtemp.a,用于别的程序。
这个例子中,make   不会为你执行   clean   下面的命令。
除非你在   all   的   libtemp.a   后面加上一个   clean
esql   是数据库   informix   的预编译程序,它把   .ec   文件预编译
成   .c   文件,然后你可以用   cc   编译之,然后通常   .c   这个中间
文件就不需要了,要删除掉。
这里   .ec.o   的写法中就是用了这个   $*   。注意   rm   $*.c   只会删除
由   .ec文件生成的   .c   文件,不会删除你自己的文件。

.SUFFIXES:   .c   .ec   .o

#   $HOME   is   already   defined   by   your   system!
INC=$(HOME)/include
BIN=$(HOME)/bin
LIB=$(HOME)/lib

CC=cc   -I$(INC)   -L$(LIB)
CFLAG=-c   -g   -O
EC=esql   -I$(INC)   -L$(LIB)
EFLAG=-c   -g   -O

all:         libtemp.a

.c.o:
                $(CC)   $(CFLAG)   $ <

.ec.o:
$(EC)   $(EFLAG)   $ <
rm   $*.c

LIBTEMP=abc1.o   abc2.o   abc3.o
libtemp.a:             $(LIBTEMP)
                ar   -r   $@   $(LIBTEMP)

clean:
rm   *.o
mv   libtemp.a   $(LIB)/libtemp.a


posted on 2012-03-13 17:42  xiao_fu  阅读(445)  评论(0编辑  收藏  举报

导航