Linux_Unix环境下的Make和Makefile详解

无论是在 Linux 还是在 Unix 环境中, make 都是一个非常重要的编译命令。不管是自己进行项目开发还是安装应用软件,我们都经常要用到 make make install 。利用 make 工具,我们可以将大型的开发项目分解成为多个更易于管理的模块,对于一个包括几百个源文件的应用程序,使用 make makefile 工具就可以简洁明快地理顺各个源文件之间纷繁复杂的相互关系。而且如此多的源文件,如果每次都要键入 gcc 命令进行编译的话,那对程序员来说简直就是一场灾难。而 make 工具则可自动完成编译工作,并且可以只对程序员在上次编译后修改过的部分进行编译。因此,有效的利用 make makefile 工具可以大大提高项目开发的效率。同时掌握 make makefile 之后,您也不会再面对着 Linux 下的应用软件手足无措了。

  但令人遗憾的是,在许多讲述 Linux 应用的书籍上都没有详细介绍这个功能强大但又非常复杂的编译工具。在这里我就向大家详细介绍一下 make 及其描述文件 makefile

   Makefile 文件

   Make 工具最主要也是最基本的功能就是通过 makefile 文件来描述源程序之间的相互关系并自动维护编译工作。而 makefile 文件需要按照某种语法进行编写,文件中需要说明如何编译各个源文件并连接生成可执行文件,并要求定义源文件之间的依赖关系。 makefile 文件是许多编译器 -- 包括 Windows NT 下的编译器 -- 维护编译信息的常用方法,只是在集成开发环境中,用户通过友好的界面修改 makefile 文件而已。

  在 UNIX 系统中,习惯使用 Makefile 作为 makfile 文件。如果要使用其他文件作为 makefile ,则可利用类似下面的 make 命令选项指定 makefile 文件:

   $ make -f Makefile.debug

  例如,一个名为 prog 的程序由三个 C 源文件 filea.c fileb.c filec.c 以及库文件 LS 编译生成,这三个文件还分别包含自己的头文件 a.h b.h c.h 。通常情况下, C 编译器将会输出三个目标文件 filea.o fileb.o filec.o 。假设 filea.c fileb.c 都要声明用到一个名为 defs 的文件,但 filec.c 不用。即在 filea.c fileb.c 里都有这样的声明:

   #include "defs"

  那么下面的文档就描述了这些文件之间的相互联系 :

   ---------------------------------------------------------
   #It is a example for describing makefile
   prog : filea.o fileb.o filec.o
   cc filea.o fileb.o filec.o -LS -o prog
   filea.o : filea.c a.h defs
   cc -c filea.c
   fileb.o : fileb.c b.h defs
   cc -c fileb.c
   filec.o : filec.c c.h
   cc -c filec.c
   ----------------------------------------------------------

  这个描述文档就是一个简单的 makefile 文件。

  从上面的例子注意到,第一个字符为 # 的行为注释行。第一个非注释行指定 prog 由三个目标文件 filea.o fileb.o filec.o 链接生成。第三行描述了如何从 prog 所依赖的文件建立可执行文件。接下来的 4 6 8 行分别指定三个目标文件,以及它们所依赖的 .c .h 文件以及 defs 文件。而 5 7 9 行则指定了如何从目标所依赖的文件建立目标。

  当 filea.c a.h 文件在编译之后又被修改,则 make 工具可自动重新编译 filea.o ,如果在前后两次编译之间, filea.C a.h 均没有被修改,而且 test.o 还存在的话,就没有必要重新编译。这种依赖关系在多源文件的程序编译中尤其重要。通过这种依赖关系的定义, make 工具可避免许多不必要的编译工作。当然,利用 Shell 脚本也可以达到自动编译的效果,但是, Shell 脚本将全部编译任何源文件,包括哪些不必要重新编译的源文件,而 make 工具则可根据目标上一次编译的时间和目标所依赖的源文件的更新时间而自动判断应当编译哪个源文件。

   Makefile 文件作为一种描述文档一般需要包含以下内容 :

   宏定义
   源文件之间的相互依赖关系
   可执行的命令

   Makefile 中允许使用简单的宏指代源文件及其相关编译信息,在 Linux 中也称宏为变量。在引用宏时只需在变量前加 $ 符号,但值得注意的是,如果变量名的长度超过一个字符,在引用时就必须加圆括号()。

  下面都是有效的宏引用:

   $(CFLAGS)
   $2
   $Z
   $(Z)

  其中最后两个引用是完全一致的。

  需要注意的是一些宏的预定义变量,在 Unix 系统中, $* $@ $? $< 四个特殊宏的值在执行命令的过程中会发生相应的变化,而在 GNU make 中则定义了更多的预定义变量。关于预定义变量的详细内容,宏定义的使用可以使我们脱离那些冗长乏味的编译选项,为编写 makefile 文件带来很大的方便。

   ---------------------------------------------------------
   # Define a macro for the object files
   OBJECTS= filea.o fileb.o filec.o
   # Define a macro for the library file
   LIBES= -LS
   # use macros rewrite makefile
   prog: $(OBJECTS)
   cc $(OBJECTS) $(LIBES) -o prog
   ……
   ---------------------------------------------------------

  此时如果执行不带参数的 make 命令,将连接三个目标文件和库文件 LS ;但是如果在 make 命令后带有新的宏定义:

   make "LIBES= -LL -LS"

  则命令行后面的宏定义将覆盖 makefile 文件中的宏定义。若 LL 也是库文件,此时 make 命令将连接三个目标文件以及两个库文件 LS LL

  在 Unix 系统中没有对常量 NULL 作出明确的定义,因此我们要定义 NULL 字符串时要使用下述宏定义:

   STRINGNAME=

   Make 命令

  在 make 命令后不仅可以出现宏定义,还可以跟其他命令行参数,这些参数指定了需要编译的目标文件。其标准形式为:

   target1 [target2 …]:[:][dependent1 …][;commands][#…]
   [(tab) commands][#…]

  方括号中间的部分表示可选项。 Targets dependents 当中可以包含字符、数字、句点和 "/" 符号。除了引用, commands 中不能含有 "#", 也不允许换行。

  在通常的情况下命令行参数中只含有一个 ":" ,此时 command 序列通常和 makefile 文件中某些定义文件间依赖关系的描述行有关。如果与目标相关连的那些描述行指定了相关的 command 序列,那么就执行这些相关的 command 命令,即使在分号和 (tab) 后面的 aommand 字段甚至有可能是 NULL 。如果那些与目标相关连的行没有指定 command ,那么将调用系统默认的目标文件生成规则。

  如果命令行参数中含有两个冒号 "::" ,则此时的 command 序列也许会和 makefile 中所有描述文件依赖关系的行有关。此时将执行那些与目标相关连的描述行所指向的相关命令。同时还将执行 build-in 规则。

  如果在执行 command 命令时返回了一个非 "0" 的出错信号,例如 makefile 文件中出现了错误的目标文件名或者出现了以连字符打头的命令字符串, make 操作一般会就此终止,但如果 make 后带有 "-i" 参数,则 make 将忽略此类出错信号。

   Make 命本身可带有四种参数:标志、宏定义、描述文件名和目标文件名。其标准形式为:

   Make [flags] [macro definitions] [targets]

   Unix 系统下标志位 flags 选项及其含义为:

   -f file   指定 file 文件为描述文件,如果 file 参数为 "-" 符,那么描述文件指向标准输入。如果没有 "-f" 参数,则系统将默认当前目录下名为 makefile 或者名为 Makefile 的文件为描述文件。在 Linux 中, GNU make 工具在当前工作目录中按照 GNUmakefile makefile Makefile 的顺序搜索 makefile 文件。
   -i   忽略命令执行返回的出错信息。
   -s   沉默模式,在执行之前不输出相应的命令行信息。
   -r   禁止使用 build-in 规则。
   -n   非执行模式,输出所有执行命令,但并不执行。
   -t   更新目标文件。
   -q    make 操作将根据目标文件是否已经更新返回 "0" 或非 "0" 的状态信息。
   -p    输出所有宏定义和目标文件描述。
   -d    Debug 模式,输出有关文件和检测时间的详细信息。

   Linux make 标志位的常用选项与 Unix 系统中稍有不同,下面我们只列出了不同部分:

   -c dir    在读取 makefile 之前改变到指定的目录 dir
   -I dir   当包含其他 makefile 文件时,利用该选项指定搜索目录。
  

你可能感兴趣的:(C++,c,linux,unix,C#)