makefile的编写

什么是make?

  利用make,一步就可以完成大量的源文件编译和链接,从而不必再一条条输入gcc命令。

 

什么是makefile?

  makefile是一个文本文件,里面记录了哪些文件需要先编译,哪些文件需要后编译,哪些文件需要重新编译。makefile的好处是“自动编译”,只需要一个make命令,整个工程自动编译,极大提高了效率。

  编写一个makefile需要提前知道:1.编译、链接的概念; 2.gcc、g++的相关命令参数 -g -o -c 

 

编译、链接的概念

  编译:对于C、C++,源文件需要编译成object file 目标文件,unix下会生成.o文件,这个动作叫compile 编译。一般来说,每一个源文件都对应着一个中间目标文件,.o文件或者.OBJ文件。

  编译时,编译器需要的是语法正确,函数、变量声明的正确。对于后者,你需要告诉编译器头文件的位置(头文件中应只是声明而不是定义,定义应放在c\c++文件中),只要所有的语法正确,编译器就可以编译出中间目标文件。

  链接:将大量的object file合成为一个可执行文件的动作叫作link 链接。

  链接时,主要是链接函数和全局变量。所以我们可以通过.o /.obj文件来链接我们的应用程序。链接不管函数所在的源文件,只管函数的中间目标文件(Object File),在大多数的时候,由于函数的Object File太多,而在链接时需要明确指出中间目标文件名,这很不方便。所以我们给这些Object Files打成包,Windows下叫作链接库文件(Library File),也就是.lib文件。在Linux下就是Archive File,也就是.a文件。

  

gcc的相关选项 -g -o -c  

  1.-g 是为了gdb 的调试,不然gdb用不到
  2.-o output_filename,确定输出文件的名称为output_filename,同时这个名称不能和源文件同名。如果不给出这个选项,gcc就给出预设的可执行文件a.out。
    一般语法:gcc filename.c -o filename
    上面的意思是如果你不打 -o filename 那么默认就是输出filemame.out.这个-o就是用来控制输出文件的。
  3.-c 只编译不链接
 
 
makefile的规则
目标 : 需要的条件    #注意冒号两边的空格
    命令         #注意前头用TAB缩进

 

  解释一下:
  • 目标可以是一个或者多个Object File,也可以是可执行文件
  • 需要的条件就是生成目标所需要的文件或者目标
  • 命令就是生成目标所需要执行的脚本

  一句话就是,makefile的规则规定的编译的依赖关系,目标文件依赖于条件,生成规则用命令来描述。在编译时,如果需要的条件比目标新,那么就会执行生成命令来更新目标。

  举例说明:假设我们写下如下的三个文件,add.h 用于声明 add 函数,add.c 提供两个整数相加的函数体,而 main.c中调用 add 函数: 

/* filename:add.h */
extern int add(int i, int j);

/* filename:add.c */
int add(int i, int j)
{
    return i + j;
}

/* filename:main.c */
#include "add.h"
main()
{
    int a, b;
    a = 2;
    b = 3;
    printf("the sum of a+b is %d", add(a + b));
}
Add

  现在,将上述三个文件写入makefile:

test : main.o add.o
    gcc main.o add.o -o test
main.o : main.c add.h
    gcc -c main.c \  //这里的'\'只是换行,命令还是gcc -c main.c -o main.o
  -o main.o add.o : add.c add.h gcc -c add.c -o add.o

  上述 makefile 利用 add.c 和 add.h 文件执行 gcc -c add.c -o add.o 命令产生 add.o 目标代码,利用main.c 和 add.h 文件执行 gcc -c main.c -o main.o 命令产生 main.o 目标代码,最后利用 main.o 和 add.o文件(两个模块的目标代码)执行 gcc main.o add.o -o test 命令产生可执行文件 test。

clean:
    rm *.o

  要是觉得除了目标文件test外,生成了过多的中间目标文件,那么在make编译后,执行make clean可以删除所有中间目标文件,需要注意的是,执行make clean后,当前目录下的所有*.o文件都被删除了,包括不在makefile中出现过的.o文件。同时,clean不会自动执行,因为clean后面没有条件,clean也不是一个目标文件,它不过是一个动作,自然就不会自动执行所定义的命令。

 

 

make是如何工作的?

  在默认情况,也就是我们只输入make命令的情况下,有:

  1. make在当前目录下找到makefile文件或Makefile文件
  2. 如果找到,它会找第一个目标,在上述例子中即是test,并把test作为最终的目标文件
  3. 如果test不存在或者test的条件文件比test的要更新,那么将执行后面的命令来生成test文件
  4. 如果test所依赖的某个.o文件不存在,则make将查找该.o文件的依赖,找到后再用相关的规则生产该.o文件
  5. .c / .h文件必须存在,由make将它们编译生成.o文件,然后再用.o文件生成最终的目标文件test

 

makefile中使用变量

  我们可在 makefile 中加入变量,另外。环境变量在 make 过程中也被解释成 make 的变量。这些变量

是大小写敏感的,一般使用大写字母。Make 变量可以做很多事情,例如:

  • 存储一个文件名列表
  • 存储一个可执行文件名
  • 存储编译器选项。

  可能这么说很迷糊,举个例子,如果有个makefile的代码如下:

  test : main.o kbd.o command.o display.o /
                  insert.o search.o files.o 
            cc -o edit main.o kbd.o command.o display.o /  //cc是gcc的软连接
                       insert.o search.o files.o 

  条件文件有八个中间目标文件,而且7个.o文件的字符串被重复了两次,如果你要再增加一个.o文件作为test的依赖,那么就可能会发现遗漏。所以为了方便makefile的维护,我们用一个变量代表字符串,也可以理解为宏。

  于是可以将增加一个utils.o文件的情况改写成下列代码:

    OBJS = main.o kbd.o command.o display.o /
                  insert.o search.o files.o utils.o  //增加utils.o
   CC = gcc
   CFLAGS = -Wall -g //-c比较特殊,一般不把它加入变量;注意这里不加 - o ,因为这个选项后面必须要加可执行文件名
test : $(OBJS)
//变量前加一个说明符$ $(CC) $(OBJS) -o test main.o : main.c defs.h $(CC) $(CFLAGS) -c main.c kbd.o : kbd.c defs.h command.h $(CC) $(CFLAGS) -c kbd.c command.o : command.c defs.h command.h $(CC) $(CFLAGS) -c command.c display.o : display.c defs.h buffer.h   $(CC) $(CFLAGS) -c display.c insert.o : insert.c defs.h buffer.h $(CC) $(CFLAGS) -c insert.c search.o : search.c defs.h buffer.h $(CC) $(CFLAGS) -c search.c files.o : files.c defs.h buffer.h command.h $(CC) $(CFLAGS) -c files.c utils.o : utils.c defs.h //增加生成utils.o的规则 $(CC) $(CFLAGS) -c utils.c clean : rm $(objects)

  只修改了一处,十分的方便

 

让make自动推导

  make很强大,它可以自动推到依赖的文件和依赖文件后面的命令,于是我们就没必要在每个.o文件后都写上类似的命令,而是让make自动识别,自动推导。

  比如,make发现一个.o文件,它就会自动地把.c文件加在依赖关系中,比如说make找到一个main.o,那么就推导出main.c是main.o的依赖文件。并且gcc -c main.c 也会被推导出来。

  通过make的这个特性,我们简化上面一个例子的makefile:

objects = main.o kbd.o command.o display.o /
              insert.o search.o files.o utils.o
test : $(objects)
    gcc -o test $(objects)
main.o : defs.h
kbd.o : defs.h command.h
command.o : defs.h command.h
display.o : defs.h buffer.h
insert.o : defs.h buffer.h
search.o : defs.h buffer.h
files.o : defs.h buffer.h command.h
utils.o : defs.h
clean :
    rm $(objects)

 

MakeFile学习之路还很长,博文部分内容借鉴大牛陈浩,他的makefile专栏http://blog.csdn.net/haoel/article/details/2887

 

 

posted @ 2017-09-20 14:11  bw98  阅读(358)  评论(2编辑  收藏  举报