一、构建:
定义:构建是指将源代码和资源文件 转换成可以运行的应用程序的过程 构建通常会包括编译链接 打包和部署等步骤
【构建总览】
源文件: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:
执行命令后:
生成了新的所有文件: