Linux项目自动化构建工具---make/makefile

Posted 做1个快乐的程序员

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Linux项目自动化构建工具---make/makefile相关的知识,希望对你有一定的参考价值。

💦不知道大家有没有听说过这样的一句话,不会写makefile就不算一个程序员。这家伙好啊,直接把我程序员的饭碗砸了,这我能忍???为了保住大家的饭碗,我来给各位读者详细解读一下make/makefile。💦

  小编奉劝各位不要轻易说一些不经过思考的话,毕竟我辉辉都说过,砸人饭碗,堪比杀生。要不然你预购的diss已经在路上了。废话不说了,让我们进入今天的主题。


1、make/makefile的引出

1.1 代码编写
  今天我们通过一组测试用例来开始今天的话题,我们在一个csdn的目录下创建main.c、test.c、test.h三个文件。

  使用vim工具对三个文件的内容进行编辑。

图1 test.h
图2 test.c
图3 main.c
  上面的3个文件,test.h和test.c为同名文件,但是后缀不同,.h称为头文件,主要放在源文件中出现的变量和函数的声明;.c称为源文件,源文件主要放变量的定义、函数的定义。

  我们在test.h中放了test函数的声明,在test.c函数中实现了test函数,然后在main.c中调用该函数。这样我们就把三个文件的内容写好了。

1.2 代码编译
疑问一: 下面我们要进行编译,怎么进行编译呢?用哪几个文件进行编译呢?
  答:我们使用gcc编译器,然后生成可执行程序,可执行程序的生成一定是编译源文件,即.c文件,所以我们:gcc -o mytest main.c test.c这样就会生成一个可执行程序。

[xd@VM-4-5-centos csdn]$ gcc -o mytest main.c test.c
[xd@VM-4-5-centos csdn]$ ll
total 24
-rw-rw-r-- 1 xd xd   57 Aug  5 20:21 main.c
-rwxrwxr-x 1 xd xd 8416 Aug  5 20:23 mytest
-rw-rw-r-- 1 xd xd   68 Aug  5 20:21 test.c
-rw-rw-r-- 1 xd xd   89 Aug  5 20:23 test.h

疑问二: 这里大家可能会有疑问,为什么我们的头文件不参与编译呢?难道头文件没有作用吗?
  答:编译器在参与编译的时候,头文件展开是在预处理阶段,编译器在参与编译的时候,头文件展开是在预处理阶段,第一步就完成了,我们的gcc在编译源文件时,不需要显示的去指明我们需要那些头文件,但是必须要让编译器找到所需要的头文件。 在本例中,编译器是可以找到所需要的头文件的,因为头文件就在当前目录下,即我们在编译main.c和test.c两个源文件时,所需要的头文件test.h在和源文件同等目录的位置,所以gcc可以帮我们找到。我们把test.h剪切到上一层目录,重新gcc编译两个源文件,就会报错,显示没有test.h文件。
  总结:我们的编译器需要头文件,在编译器看来,不用显示的告诉我头文件是谁,因为源文件中已经告诉我我要包含谁了。但是要帮我找到头文件,在默认的情况下,
    a:编译器会去源文件同等位置目录寻找头文件;
    b:标准库指定的头文件目录,像#include <stdio.h> 等头文件所在的目
    录,/user/include/stdio.h就是标准库指定的目录。

  我们在写C语言的时候学过,包含头文件的方式有两种,A:<>;B:””。<>表示去标准库指定目录寻找头文件,””表示去源文件所在同等目录去寻找头文件。

1.3 编译分类
  我们将源文件编译成二进制目标文件有两种方法:
    法一:gcc -o mytest main.c test.c直接生成二进制目标文件
    法二:gcc -c main.c -o main.o和gcc -c test.c -o test.o,先生成两个目标文件,然后链接两个目标文件,gcc -o mytest main.o test.o
  在上述两种方法中,我们常用和推荐的是法二,之所以推荐是因为:a:我们在写项目时有成千上百个.c源文件,我们有可能会用不同的源文件生成不同的可执行程序,我们如果先将其都转为.o二进制目标文件,想用那几个文件生成可执行文件,直接用命令行生成就可以。b:比如我们有100个源文件,我们每次都gcc -o mytest +100个.c文件,如果有一个.c文件更改了,我们都要全部重新编译链接,但是我们如果生成.o文件,那个文件更改了,只重新编译一下更改的那个源文件,让他生成一个.o文件即可。会提升编译效率。

  那么问题又来了,我们有成千上百个源文件,总不能每次都,这样工作量无疑是巨大的,这就有了我们的make/makefile。

2、make/makefile是什么

make是一个命令工具,是一个解释makefile中指令的命令工具,一般来说,大多数的IDE都有这个命令,比如:Delphi的make,Visual C++的nmake,Linux下GNU的make。可见,makefile都成为了一种在工程方面的编译方法。

  总结来说:make是一条命令,makefile是一个文件,两个搭配使用,完成项目自动化构建。

3、make/makefile的使用

  对于make/makefile的使用我们也是通过一个测试用例来理解:

3.1 make/makefile简要示例
  首先,我们创建一个test.c文件,里面有一个打印”hello make/makefile!”的代码,然后创建一个makefile或者Makefile的文件,m大写小写都可以,touch Makefile。Makefile文件中放的是一组我们所依赖的关系、依赖的方法,我们直接vim Makefile,里面写依赖方法和依赖关系,具体怎么写呢?
    第一行写依赖关系,如:mytest:test.c(即mytest的生成依赖test.c文件)
    第二行写依赖方法,必须以Tab开头,不能用空格代替,然后gcc -o mytest test.c


  写完以后,如果我们想要编译test.c源文件,直接make,此时就会自动执行Makefile中的依赖关系,帮我们生成一个mytest可执行程序,以后我们不管对test.c文件做了何种修改,不需要再敲写gcc等一系列指令,直接make,就会帮我们编译。


3.2 make/makefile详细示例
  通过对上面代码的分析,我们暂时了解了makefile中的依赖关系和依赖方法,其实3.1中的makefile的依赖关系是简略版,我们的mytest实际依赖的是mytest.o,而我们的mytest.o依赖的是mytest.s,mytest.s依赖的是mytest.i,mytest.i最终才是依赖test.c,那么我们重新修改一下makefile,看一下完整的makefile。(实际在真正应用中,我们并不会写的如此详细,这里是为了帮助大家理解。)


  不管是详细版makefile和简要版makefile最终都会生成一个可执行程序,我们运行就会得到结果。

3.3 clean清理项目文件
   生成项目的步骤我们会了,那么该怎么清理项目文件呢?在生成项目的过程中,产生了很多临时文件,这时候我们vim Makefile,再添加一些信息。


  对于clean,它是没有依赖文件的,直接回车+tab写依赖方法,依赖方法就是我们要清理的项目文件。这里需要注意的是,我们在clean上面添加了这样的信息:.PHONY:clean。
  我们可以将.PHONY这个理解为makefile中的某种关键字的概念,用它来修饰我们的clean,此时clean就是一个伪目标的概念;上面的mytest、test.o等都称为目标。伪目标也是目标,也是要根据依赖关系来执行依赖方法,clean没有依赖关系,直接执行依赖方法。

  经过以上的修改,makefile就算写完了。我们make执行,会生成可执行程序及一些临时文件,make clean就会清理我们的临时文件,所以我们写好makefile命令后,直接make、make clean就可以了,不需要重复写gcc等一系列命令了。(make clean和make可以同时依次执行,输入指令:make clean;make即可)

图1 先make再make clean
图2 make clean;make

3.4 伪目标
   clean之前是目标,被.PHONY修饰后变为伪目标,伪目标的特点是: 它的依赖方法总是被执行,make一遍后再make,就会提示生成的mytest已经是最新的了,但是make clean之后,还可以一直make clean,所以一般我们将清理命令clean定义为伪目标,对于可执行程序没必要定义为伪目标。

图1 目标不可以一直运行
图2 伪目标可以一直运行

4、make是如何工作的

  1. make会在当前目录下找名字叫“Makefile”或“makefile”的文件。
  2. 如果找到,它会找文件中的第一个目标文件(target),在上面的例子中,他会找到“mytest”这个文件,并把这个文件作为最终的目标文件。
  3. 如果mytest文件不存在,或是mytest所依赖的后面的mytest.o文件的文件修改时间要比mytest这个文件新(可以用 touch 测试),那么,他就会执行后面所定义的命令来生成mytest这个文件。
  4. 如果mytest所依赖的mytest.o文件不存在,那么make会在当前文件中找目标为mytest.o文件的依赖性,如果找到则再根据那一个规则生成mytest.o文件。(这有点像一个堆栈的过程)
  5. 当然,你的c文件和h文件是存在的啦,于是make会生成 mytest.o 文件,然后再用 mytest.o 文件声明make的终极任务,也就是执行文件mytest了。
  6. 这就是整个make的依赖性,make会一层又一层地去找文件的依赖关系,直到最终编译出第一个目标文件。
  7. 在找寻的过程中,如果出现错误,比如最后被依赖的文件找不到,那么make就会直接退出,并报错,而对于所定义的命令的错误,或是编译不成功,make根本不理。
  8. make只管文件的依赖性,即,如果在我找了依赖关系之后,冒号后面的文件还是不在,那么对不起,我就不工作啦。

5、扩展知识(三种特殊符号)

  三种特殊符号: @ 、 @、 @^、$<

  上图中,在对应位置替换上特殊符号后,make和make clean正常运行。
     $@:依赖关系中的目标文件(即:左侧的文件)
     $^: 依赖关系中的依赖文件列表(即:右侧的所有文件)
     &<:依赖关系中的一个依赖文件

以上是关于Linux项目自动化构建工具---make/makefile的主要内容,如果未能解决你的问题,请参考以下文章

喵呜:Linux环境基础开发工具使用篇之Linux开发工具:Linux项目自动化构建工具-make/Makefile

Linux基础项目自动化构建工具——Makefile

Linux环境开发工具gdb调试工具+Makefile自动化构建工具

Linux | 项目自动化构建工具 - make/Makefile

Linux项目自动化构建工具make与makefile

Linux项目自动化构建工具-make/Makefile (●‘◡‘●)