make与makefile的编写

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/ArchyLi/article/details/78699583

一、简介


大家都说makefile是用来检验一个人有没有具有完备的大型工程的能力,因为它关系到了整个工程的编译规则。

它像一个shell一样在文件的内部也可以使用操作系统命令,一般我们在这个文件里面按照文件的类型功能和模块分为若干个目录,它指定了什么文件先编译什么文件后编译,它还有一个好处就是自动化编译,一旦我们写好了makefile文件,我们只需要一个make命令便可以让整个工程完成编译。

如果我们没有这个程序,我们则需要一个一个把关系输入进去,很麻烦,但是如果我们有了makefile这个文件便很大程度上的提高了我们编写程序的效率。

二、编译与链接


程序在生成可执行文件的过程中需要经过编译和链接的过程,这两个过程又和我们编写makefile密切相关。
编译:
程序在编译过程中会确定语法、函数、变量的声明正确与否,只要所有的语法全部正确,编译器就会生成出中间可执行文件。如果出错,编译器会给出一个警告,但是仍然可以生成中间可执行文件。

链接:
链接的过程是链接函数和全局变量的过程,即是链接由编译产生的众多可执行文件的过程。如果我们在链接的过程中,在中间可执行文件没有找到对应的函数实体,那么编译器就会给出一个错误码。

前者是告诉编译器函数和变量的声明正确,后者是告诉编译器头文件所在的正确位置。

如下图一个.c的文件经过预处理、编译、汇编、链接后产生可执行文件的一个过程,红色方框表明的是这些过程所依赖的代码,在后续我们的makefile的编写过程中把它叫做依赖关系。

这里写图片描述

三、makefile的实现


当我们使用makefile的时候需要一条名为make的命令,make命令会要一个makefile文件,去告诉make命令如何去编译和链接整个程序。

而makefile的编译和链接程序的规则有以下三条,这三条规则保证了我们在修改程序之后可以减少编译的次数,不需要人为检测哪些需要再次编译。

1、如果这个工程没有编译过,那么我们的所有C文件都要编译并被链接。

2、如果这个工程的某几个C文件被修改,那么我们只编译被修改的C文件,并链接目标程序。

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

依赖关系:

只要我们在makefile中定义好了依赖关系以后,它后续的一行定义了如何生成目标文件的操作系统命令,此处需要注意的是:一定要以一个Tab键作为开头。

(1)make命令

make命令并不管命令怎么工作的,它只管执行所定义的命令。
那么make命令是如何工作的呢?

  1. make会在当前的目录下找到名字为makefile或者Makefile的文件
  2. 如果找到,它会找到文件中的第一个目标文件,并且把这个文件作为最终的目标文件。
  3. 如果目标文件不存在,或者目标文件所依赖的后面的.o文件修改时间要比目标文件新,那么它会执行后面所定义的命令来生成目标文件。
  4. 如果目标文件所依赖的.o文件也存在,那么make会在当前的文件中找到.o文件的依赖性,如果找到则根据那个规则生成.o文件(优点类似堆栈的过程)
  5. 如果C和H文件都存在,make会生成.o文件,然后用.o文件声明make的最后一个任务,也就是执行目标文件了。

在makefile的最后我们可以写上clean,它没有被第一个目标文件直接或者简介的关联,那么它后面所定义的命令是不会被自动执行的,不过我们可以显示的要make执行,即调用make clean来清除所有的文件以便我们重新编译。

(2) 清空⽬目标⽂文件的规则

每个Makefile中都应该写⼀一个清空⽬目标⽂文件(.o和执⾏行⽂文件)的规则,这不仅便于 重编译,也很利于保持⽂文件的清洁。这是⼀一个“修养”(呵呵,还记得我的《编程修养》吗)。⼀一般的风格都是:

clean:
    rm edit $(objects) 

更为稳健的做法是:

.PHONY : clean
clean:
    -rm edit $(objects) 

①伪目标

.PHONY意思表示clean是⼀一个“伪目标”。
而在rm命令前⾯面加了⼀一个小减号的意思就是,也许某些⽂文件出现问题,但不要管,继续做后面的事。当然,clean的规则不要放在文件的开头,不然,这就会变成make的默认目标。所以一般情况下我们采用的是——“clean从来都是放在⽂文件的最后”。

  1. 如果make执行时,有“-I”或“–include-dir”参数,那么make就会在这个参数所指定的目录下去寻找。
  2. 如果目录/include(⼀一般是:/usr/local/bin或/usr/include)存在的话,make也会去找。

伪目标一般没有依赖的文件。但是,我们也可以为伪目标指定所依赖的文件。伪目标同样可以作为“默认目标”,只要将其放在第⼀一个。一个示例就是,如果你的Makefile需要一口气生成若干个可执行文件,但你只想简单地敲⼀一个make完事,并且,所有的目 标文件都写在⼀一个Makefile中,那么你可以使⽤用“伪目标”这个特性:

all : prog1 prog2 prog3
.PHONY : all

prog1 : prog1.o utils.o
    cc -o prog1 prog1.o utils.o

prog2 : prog2.o
    cc -o prog2 prog2.o

prog3 : prog3.o sort.o utils.o
    cc -o prog3 prog3.o sort.o utils.o 

我们知道,Makefile中的第一个目标会被作为其默认目标。我们声明了一个“all”的伪目标,其依赖于其它三个目标。由于伪目标的特性是,总是被执行的,所以其依赖的那三个目标就总是不被“all”这个目标更新。所以,其它三个目标的规则总是会被决议。也就达到了我们一口气生成多个目标的目的。“.PHONY : all”声明了“all”这个目标为“伪目标”。

②显示命令

显示命令: 依赖命令前加@

③命令执行

当依赖目标更新于目标时,也就是当规则的目标需要被更新时,make会一条一条的执行其后的命令。需要注意的是,如果你要让上一条命令的结果应用在下一条命令时,你应该使用‘分号’分隔这两条命令
举例一:

exec:
    cd /home/admin
    pwd      

举例二:

exec: 
     cd /home/admin; pwd 

当我们执行“make exec”时,第一个例子中的cd没有起作用,pwd会打印出当前的Makefile目录,而第二个例子中,cd就起作用了,pwd会打印出“/home/admin”。

猜你喜欢

转载自blog.csdn.net/ArchyLi/article/details/78699583
今日推荐