Makefile

1 什么是Makefile?为什么要用Makefile?

        Linux 环境下的程序员需要构建和管理自己的工程,Makefile描述了整个工程的编译、连接等规则。包括:工程中的哪些源文件需要编译以及如何编译、需要创建那些库文件以及如何创建这些库文件、如何最后产生我们想要的可执行文件。一个工程的源文件数量很多,并且根据属性,功能的不同放在不同的目录中,Makefile定义了一系列规则指定了哪些文件要先编译,哪些文件要后编译等待。程序员通过make命令来解释Makefile中的指定,从而完成整个工程的自动化编译。


        我们知道程序编译的过程是先将原文件编译成中间目标文件(.obj或者.o),然后将大量的中间目标文件链接成可执行文件。编译时只要语法,函数与变量的声明正确就能编译成中间目标文件,有多少个源文件就会生成多少个中间目标文件;链接时将中间目标文件连接成我们的应用程序

2 Makefile格式

   目标文件target:源文件或者目标

   命令(任意shell命令)

   上面就是表示目标文件是由哪些文件生成。

             例如:main.o : main.c defs.h 
                         cc -c main.c (命令一定要以一个Tab键作为开头)

         此代码表示通过main.c和defs.h文件生成一个中间目标文件main.o,其实就是执行gcc -c -I defs.h地址 main.c -o main.o

 3 变量的使用

        和C,Java中一样,Makefile中也有变量,作用类似于C语言中的宏,当某个字符串在Makefile文件中多次使用,而一旦此字符串需要修改时,就不得不找出文件中所有的此字符串进行修改,Makefile中变量的作用就是将此字符串定义成一个变量,当修改时只需要在变量定义的地方进行修改就可以了;还有一个作用就是将某些通用的长字符串提取出来,用的时候可以直接用一个变量名替代,非常的方便

          给个例子:

          edit : main.o kbd.o command.o display.o \                (\ 表示换行)
              insert.o search.o files.o utils.o 
            cc -o edit main.o kbd.o command.o display.o \ 
                       insert.o search.o files.o utils.o 
          main.o : main.c defs.h 
                         cc -c main.c 
          kbd.o : kbd.c defs.h command.h 
                       cc -c kbd.c 
          command.o : command.c defs.h command.h 
                                  cc -c command.c 
          display.o : display.c defs.h buffer.h 
                               cc -c display.c 
          insert.o : insert.c defs.h buffer.h 
                           cc -c insert.c 
           search.o : search.c defs.h buffer.h 
                             cc -c search.c 
           files.o : files.c defs.h buffer.h command.h 
                         cc -c files.c 
           utils.o : utils.c defs.h 
                         cc -c utils.c 
    clean : 
            rm edit main.o kbd.o command.o display.o \ 
               insert.o search.o files.o utils.o 

      这个例子中clean中main.o kbd.o command.o display.o insert.o search.o files.o utils.o与第一行代码中是一样的,所以我们就可以:

      OBJ = main.o kbd.o command.o display.o insert.o search.o files.o utils.o

      这样上述代码就变成

            OBJ = main.o kbd.o command.o display.o insert.o search.o files.o utils.o

             edit : $(OBJ) 
            cc -o edit $(OBJ) 
          main.o : main.c defs.h 
                         cc -c main.c 
          kbd.o : kbd.c defs.h command.h 
                       cc -c kbd.c 
          command.o : command.c defs.h command.h 
                                  cc -c command.c 
          display.o : display.c defs.h buffer.h 
                               cc -c display.c 
          insert.o : insert.c defs.h buffer.h 
                           cc -c insert.c 
           search.o : search.c defs.h buffer.h 
                             cc -c search.c 
           files.o : files.c defs.h buffer.h command.h 
                         cc -c files.c 
           utils.o : utils.c defs.h 
                         cc -c utils.c 

           clean : 
                        rm edit $(OBJ) 

         是的,定义变量之后,我们通过$(变量名)使用这个变量 。

 4 Makefile其实是有自己强大的自动推导能力的,对于一个.o文件,Makefile会自动将相同名称的.c文件加入到它的依赖关系中,比如上面代码中省略的部分中有

           main.o : main.c defs.h 
                         cc -c main.c 

         对于Makefile,它会自动将main.c文件加入到main.o:之后,所以上面完全可以写成

         main.o : defs.h 
                         cc -c main.c

        所以上面的例子就可以写成:

OBJ = main.o kbd.o command.o display.o insert.o search.o files.o utils.o

 edit : $(OBJ) 
                  cc -o edit $(OBJ) 
          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 edit $(OBJ) 

 既然能自动推导出同名的.c文件,那么头文件能不能也自动生成呢?答案是肯定的,不然在大型工程中,我们还得知道所有C文件中包含哪些头文件,要是修改了一个头文件难道还得在Makefile中添上吗?当然不是,一般的C/C++编译器都支持“-M”的选项(GUN的C/C++编译器中得用“-MM”),自动寻找文件中包含的头文件,并生成一个依赖关系。在Makefile中,我们可以将 gcc -M  C文件 生成的依赖关系放在一个.d文件中,让make自动更新.d文件,并将其包含在我们的主Makefile文件中。

%.d: %.c 
            @set -e; rm -f $@; \ 
             $(CC) -M $(CPPFLAGS) $< > $@.$$$$; \ 
                          sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' < $@.$$$$ > $@; \ 
             rm -f $@.$$$$ 

5 clean

上面的代码最后

clean : 
                        rm edit $(OBJ) 

这是用来清除所有的目标文件的,包括(中间目标文件和可执行文件),用户可以用“make clean”命令执行此代码来清除目标文件

6 Makefile的工作方式   

在默认的方式下,也就是我们只输入make命令。那么:
         1
make会在当前目录下找名字叫“Makefile”“makefile”的文件。
         2
)如果找到,它会找文件中的第一个目标文件(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只管文件的依赖性,即,如果在我找了依赖关系之后,冒号后面的文件还是不在,那么对不起,我就不工作啦。

当然,如果你很有个性的表示我想给Makefile重起一个名字可以吗?当然可以,只要你在执行make的时候:make -f 文件名  就可以了

Makefile 编译规则:

1) 如果工程未编译过,那么所有的C文件都要编译并被链接

2) 如果工程中某几个文件被修改,那么直编译被修改的文件并链接目标文件

3) 如果工程的头文件被改变了,那么需要编译引用了这几个头文件的C文件,并链接目标文件。


7 Makefile编写

   注释:#      与C或者Java等语言编辑不同,在Makefile中用“#”来进行行注释,作用类似于C中的"//"

   引用其他Makefile:include Makefile文件路径  这在大型项目中是十分常见的,它其实也类似于C中的include

    -     如果你在命令前加上了一个“-”符号,如-include,就表示不要报错,继续执行

    命令前一定是tab键

    \    如果命令过长,可以利用“\”进行换行

    VPATH变量   这个特殊的变量指定了一个目录,表示如果在当前目录中找不到依赖文件或者目标文件,那么就到这个目录中找,这个变量可以定义多个目录路径,用“:”分割,搜索目录的顺序根据此变量值的顺序排序

    还有很多细节,可以参照陈皓的《跟我一起写 Makefile 》,我也整理过一些常见的函数,变量等。


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值