Bootstrap

Makefile与Cmake详解

一、构建:

定义:构建是指将源代码和资源文件 转换成可以运行的应用程序的过程 构建通常会包括编译链接 打包和部署等步骤

【构建总览】 


 

源文件:main.c

预处理文件:main.i

编译文件:main.s

汇编文件:main.o

链接文件:main.exe

【构建细节】 


二、Makefile(项目构建工具)的引出

    当只有一个源文件的时候,我们可以直接使用一些编译器的指令,来手动完成构建的过程,比如可以使用GCC来编译一个C语言的源文件,或者使用java c来编译一个java的源文件,但是当项目变得更加庞大和复杂,有成百上千个源文件、头文件和库文件的时候,手动编译就不太现实了。这个时候就可以借助一些自动化的构建工具,来完成整个构建的过程。不同语言和不同平台的项目,会使用不同的构建工具,比如make cmake maven等等。本节我们重点介绍C语言中的Make与Cmake工具。

【直接使用编译器构建】 

【使用项目构建工具构建】


三、gcc编译构建项目1

gcc是C语言的编译器,类似于java的javac编译器。

#include <stdio.h>

int main(){
    printf("Hello, world");
    return 0;
}

在终端下生成名为hello的可执行文件:

执行语句:

gcc main.c -o hello

如图:

下面就直接执行hello源文件:显示打印出了Hello, world

执行语句:./hello

这就是使用gcc编译器来构建的最简单的c语言文件。


四、Make工具构建项目1


1.构建原理:

Make工具构建项目,是通过读取一个叫做Makefile的文件中定义的规则来执行构建的过程的 、Makefile有点类似于maven工程中的pom.xml文件 或者前端node js项目中的package.json文件。不太一样的是 make file最初虽然是为C语言项目设计的 但是它实际上是一个通用的构建工具 。可以用来构建任何类型的项目 ,但是目前主要还是用在C或者 C++的项目中.


2.构建过程:

在目录下新增文件Message.h和Message.c

message.h:
#ifndef DIYOS_MESSAGE_H
#define DIYOS_MESSAGE_H
void message();
#endif //DIYOS_MESSAGE_H
message.c:
#include <stdio.h>
#include "message.h"
void message(){
    printf("欢迎学习Makefile");
}
main.c:

新增对Message.h的依赖(调用函数message):

#include <stdio.h>
#include "message.h"
int main(){
    message();//这里使用的是同目录下的Message.h文件里面的函数了
    printf("Hello, world");
    return 0;
}

  接下来我们看看Make工具是如何构建项目的:

3.构建规则

Makefile由一系列的规则组成,每个规则包含了目标、依赖和执行的命令。
目标,就是我们要生成的文件。依赖,就是生成这个文件所需要的文件。命令,就是生成这个文件的具体步骤。
规则其实就是告诉make两件事——文件的依赖是什么?以及如何生成这个文件?

目录下新建Makefile文件:

注意:这个文件不能有扩展名 首字母可以是大写 也可以是小写


规则定义: 
    在第一行写上目标文件的名字,也就是我们想要生成的可执行文件Hello,后面加上一个冒号,然后是这个可执行文件所依赖的文件,也就是main.c和message.c,在下面一行加上一个tab键。然后再写上生成这个可执行文件的命令——也就是:

gcc main.c message.c -o hello

这里要注意在命令前面只能是tab键,不能是空格,否则在执行make命令的时候,就会因为格式错误而报错
Hello:main.c message.c
	gcc main.c message.c -o hello

【命令全貌】

规则全解:
     这个规则的意思是:如果我们要生成hello这个可执行文件的话。他会先检查main.c和message.c。这两个文件是否有更新如果有更新的话,就会执行下面这行命令来编译这两个文件,重新生成可执行文件Hello。

4.运行Makefile:

首先删除之前生成的hello.exe:

windows下是使用命令:

del hello

然后直接在命令行中输入make即可:

 可以看到执行了Makefile中的命令。同时直接执行hello命令也是直接可以运行的。


5.make的按需编译(时间戳)特性:

     当我们执行make的时候,make会检查目标文件和依赖文件的时间戳,如果依赖文件的时间戳比目标文件新。也就是依赖文件有更新的话,就可以执行命令来重新生成目标文件,否则就会跳过这个规则而不执行任何操作。比如再来执行一下make的话,就会提示我们hello这个文件已经是最新的了,不需要重新生成那如果使用touch命令来修改一下,main.c这个源文件的时间戳。这样main.c就会比hello这个文件新,
再来执行一下make的话,就会重新生成这个可执行文件,这也是make的一个特性。
    只有当依赖文件有更新的时候,才会重新生成目标文件。否则就会跳过这个规则。这样当我们在构建大型项目的时候,就会只编译那些有更新的文件,而不是每次都重新编译整个项目。

     但是这里有个问题:像现在这样的写法是达不到这个效果的。因为我们把main.c和message.c这两个文件都写在了一行.无论哪一个文件有更新的话.都会重新编译这两个文件。
    这是因为我们省去了生成中间文件的步骤,在实际的项目中更加规范和实际的做法是: 把编译和链接分开来写,也就是先把main.c和message.c,分别编译成main.o和message.o。然后再把它们链接成hello这个可执行文件,那我们再来修改一下MAKEFILE:
#版本一写法:链接与汇编写在一起
#Hello:main.c message.c
#	gcc main.c message.c -o hello


#版本二写法:链接与汇编分开写
Hello:main.o message.o
	gcc main.o message.o -o hello

main.o:main.c
	gcc -c main.c

message.o:message.c
	gcc -c message.c

# 源文件:main.c
# 预处理文件:main.i
# 编译文件:main.s
# 汇编文件:main.o 
# 链接文件:main.exe
1.汇编与链接分开写的好处:
    这样写的好处就是,当我们修改了其中某一个源文件的时候,只需要重新编译这个原文件,然后再链接一下就可以了。其他没有修改的源文件就不需要重新编译了,当项目中有很多个源文件的时候,这样就会更加的高效。

执行一下:

发现命令全部被打印出来了,生成的exe文件也可以直接运行!!

从运行的命令看见,是先执行的编译命令,后面才执行的链接命令。所以这里的分开写意义重大!!

 


6.伪命令(.phony):

phony
美[ˈfoni]英['fəʊni:]
adj. <口>假的,欺骗的;
n. <口>赝品,骗人的东西;骗子;

   定义:

         有的时候我们可能会需要执行一些并不生成文件的操作, 比如清理临时文件 、生成文档、打包等等 ,这个时候就可以使用伪目标来定义这些规则伪目标就是一个不生成文件的目标它只是一个标签 用来执行一些操作 .


常用伪目标:

clean

        最常用的伪目标就是clean:用来清理编译过程中生成的临时文件,或者可执行文件。如图:

在Makefile文件里面添加如下代码:

clean:
	del -f *.o hello

就可以在make命令后面跟上标签后缀clean, 执行后:

发现.o与.exe文件已经被清理。 

【注意】

这里我们本地目录下不能再有文件名为clean的文件了,如果有,那么make clean这个命令就会失效 因为make会把clean当成一个文件名来处理。为了避免这种情况 可以在前面加上一个点phony 后面加上冒号和一个clean来显示的, 告诉make这个clean就是一个伪目标 这样make就不会把clean当做文件名来处理了

如下图:

 


All

    当我们执行make的时候,make会默认执行第一个规则(也就是只生成一个exe)。但是如果我们想要生成的目标文件,不止一个的话。就可以使用all这个伪目标。
不使用all:

结果:

发现没有world.exe.因为默认只生成第一个。


使用all:

执行命令后:

生成了新的所有文件:

;