【Linux系统编程】第十三弹---项目自动化构建工具-make/Makefile

ops/2024/9/24 21:26:34/

个人主页: 熬夜学编程的小林

💗系列专栏: 【C语言详解】 【数据结构详解】【C++详解】【Linux系统编程】

目录

1、背景

2、编写makefile

2、make原理

3、理解makefile

 4、优化makefile

总结


1、背景

★ 会不会写makefile,从一个侧面说明了一个人是否具备完成大型工程的能力。
★ 一个工程中的源文件不计数,其按类型、功能、模块分别放在若干个目录中,makefile定义了一系列的规则来指定,哪些文件需要先编译,哪些文件需要后编译,哪些文件需要重新编译,甚至于进行更复杂的功能操作。
★ makefile带来的好处就是——“自动化编译”,一旦写好,只需要一个make命令,整个工程完全自动编译,极大的提高了软件开发的效率。
★ make是一个命令工具,是一个解释makefile中指令的命令工具,一般来说,大多数的IDE都有这个命令,比如:Delphi的make,Visual C++的nmake,Linux下GNU的make。可见,makefile都成为了一种在工程方面的编译方法。

总结:

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

2、编写makefile

第一步,先创建两个文件,一个用于编写源代码(test.c),一个用于自动编译源代码(makefile/Makefile)。

[jkl@VMCentos7 lesson4]$ touch test.c makefile # 创建两个文件
[jkl@VMCentos7 lesson4]$ ls
makefile  test.c

第二步,编写makefile文件。

test.exe:test.cgcc test.c -o test.exe
.PHONY:clean
clean:rm -f test.exe

注意:

  • makefile 的文件名必须是 makefile/Makefile,不能是其他名称,否则 make 识别不了。
  • 依赖文件可以有多个,也可以没有。
  • 依赖方法必须以 [Tab] 键开头,特别注意不能是四个空格。

第三步,编写test.c文件。

 #include<stdio.h>int main(){printf("hello linux\n");                                                                                        return 0;}

第四步,通过命令看效果。

[jkl@VMCentos7 lesson4]$ make
gcc test.c -o test.exe
[jkl@VMCentos7 lesson4]$ ls
makefile  test.c  test.exe
[jkl@VMCentos7 lesson4]$ ./test.exe
hello linux

2、make原理

在Linux中,输入make命令后,make会在当前目录下找文件名为 "makefile或者Makefile"的文件,1. 如果找到,则会把文件中的第一个目标文件作为最终的目标文件

[jkl@VMCentos7 lesson4]$ ls # 查看当前目录的文件,有一个makefile一个Makefile文件
makefile  Makefile  test.c
[jkl@VMCentos7 lesson4]$ make  # 把第一个文件作为目标文件
gcc test.c -o test.exe    # 执行makefile
[jkl@VMCentos7 lesson4]$ cat makefile
test.exe:test.cgcc test.c -o test.exe
.PHONY:clean
clean:rm -f test.exe
[jkl@VMCentos7 lesson4]$ cat Makefile
mytest:test.cgcc -o mytest test.c

 2. 如果没有找到,则打印提示信息。

[jkl@VMCentos7 ~]$ make
make: *** No targets specified and no makefile found.  Stop. # 没有找到目标文件

3、理解makefile

首先我们需要知道makefile文件中代码所代表的意思,通过下图进行一一解释:

注意:上面我们说了输入make指令出现两种可能,此处只讲解找到最终文件的情况。

当我们在命令行中输入make命令,会默认生成第一个目标文件,即执行第一个依赖方法。

[jkl@VMCentos7 lesson4]$ make # 默认执行第一个依赖方法,并打印出来
gcc test.c -o test.exe
[jkl@VMCentos7 lesson4]$ ls  # 查看当前目录文件,确实生成了可执行程序(目标文件)test.exe
makefile  Makefile  test.c  test.exe

当我们再次输入make命令,会出现以下提示:

[jkl@VMCentos7 lesson4]$ make
make: 'test.exe' is up to date.  # 可执行程序(目标文件)是最新生成的了

1. 为什么再次输入make命令会不让我们继续生成目标文件呢???

因为当有大量的文件的时候,编译执行会消耗大量的时间,为了达到更高的效率,因此源代码没有改变,makefile就会知道只生成一份可执行程序就可以了。

但是此时就有另外的两个问题了,编译器怎么知道哪个文件是没有改变的源代码???如果我们的目的就是想要输入命令则生成目标文件该怎么办???

2. 编译器怎么知道哪个文件是没有改变的源代码???

对比可执行程序最近修改时间与源文件最近修改时间,谁更新?

编写代码和代码生成的可执行程序的修改时间是不一样的,先写的源代码,后才有的代码生成的可执行程序。因此makefile会通过谁的时间更新,来确定到底要不要重新生成可执行程序。通过stat命令可以看到文件的三个时间。

3. 想要输入make命令则生成目标文件该怎么办???

此处可以在每个方法之前输入 .PHONY:xxx,xxx对应的方法总是要执行。xxx可以是目标文件也可以是依赖方法。

举例:想要make总是执行,可以将makefile改成如下内容:

.PHONY:test.exe
test.exe:test.cgcc test.c -o test.exe

此时输入make则会生成可执行程序。

4. 我们像VS编译一个程序可以生成解决方案,还可以清理解决方案,我们makefile如何清理解决方案呢???

 清理解决方案的实质是删除可执行程序,那么我们在makefile中提供一个删除文件的依赖方法即可,而且我们可能总是想要删除可执行程序,因此可以加上.PHONY:xxx。

test.exe:test.cgcc test.c -o test.exe
.PHONY:clean # 总是执行该依赖关系中的方法
clean:       # 删除可执行程序rm -f test.exe

5. 从上面我们可以看到,删除可执行程序是第二个依赖方法,我们前面知道make默认执行第一个依赖方法,那我们想要执行第二个依赖方法应该怎么办呢??

此处需要使用make命令+目标文件,即make clean。 只要不是第一个依赖方法,都是这样执行依赖方法!!!

[jkl@VMCentos7 lesson4]$ ls
makefile  Makefile  test.c  test.exe
[jkl@VMCentos7 lesson4]$ make clean  # 删除可执行程序
rm -f test.exe
[jkl@VMCentos7 lesson4]$ ls  # 确实删除了
makefile  Makefile  test.c

6. 虽然知道了如何执行其他时候的依赖方法,但是此时又有一个问题,如果我执行依赖方法之后不想看到执行的是什么,怎么解决呢???

解决办法是在依赖方法前面加一个@符号,则不会看到执行的内容了。

[jkl@VMCentos7 lesson4]$ cat makefile # 查看文件内容
.PHONY:test.exe
test.exe:test.c@gcc test.c -o test.exe  # 依赖方法中有@
.PHONY:clean
clean:rm -f test.exe           # 依赖方法中没有@
[jkl@VMCentos7 lesson4]$ make  # 不显示执行的是什么
[jkl@VMCentos7 lesson4]$ ls
makefile  Makefile  test.c  test.exe # 生成了可执行程序
[jkl@VMCentos7 lesson4]$ make clean  # 显示执行的是什么
rm -f test.exe
[jkl@VMCentos7 lesson4]$ ls  # 删除了可执行程序
makefile  Makefile  test.c

 4、优化makefile

前面我们理解了makefile中内容的意义,但是在依赖关系和依赖方法之间的文件名基本是重复的,有没有一种方法让它更简便一些呢???

答案是可以的,此处我们需要介绍几个makefile中一些自动化变量。

$@ : 规则的目标 ,即上面的test.exe

$<   : 规则的第一个依赖,即test.c

$?   : 所有比目标新的依赖的列表

$^   : 所有的依赖的列表,会自动去重  ,即目标后面的多个依赖列表

下面我们则对上面写的makefile文件进行优化:

1.如下为单个依赖关系的举例:

test.exe:test.cgcc -o $@ $< # 自动化变量需要在最后面 $@代表test.exe $<表示test.c
.PHONY:clean
clean:rm -f test.exe

 2.如下为多个依赖关系的举例:

test.exe:test.c test.hgcc -o $@ $^ # 自动化变量需要在最后面 $@代表test.exe $^表示test.c 和 test.h
.PHONY:clean
clean:rm -f test.exe

注意:依赖关系以空格间隔!!! 

除了makefile中的自动化变量以外,其实我们还可以自己创建变量

[jkl@VMCentos7 lesson5]$ cat makefile
bin=test.exe # 创建目标文件变量
src=test.c   # 创建被编译文件变量 $(bin):$(src)  # 使用需要加$符号gcc -o $@ $^
.PHONY:clean
clean:rm -f $(bin)

注意:创建变量跟C语言相似,但是不用加类型。 

总结


本篇博客就结束啦,谢谢大家的观看,如果公主少年们有好的建议可以留言喔,谢谢大家啦!


http://www.ppmy.cn/ops/33066.html

相关文章

mysql 指定根目录 迁移根目录

mysql 指定根目录 迁移根目录 1、问题描述2、问题分析3、解决方法3.1、初始化mysql前就手动指定mysql根目录为一个大的分区(支持动态扩容)&#xff0c;事前就根本上解决mysql根目录空间不够问题3.1.0、方法思路3.1.1、卸载mariadb3.1.2、下载Mysql安装包3.1.3、安装Mysql 8.353…

Mysql:常见问题

常见问题 一、查询缓存和缓冲池二、为什么表数据删掉一半,表文件大小不变?三、为何选择B+Tree作为索引的数据结构?四、为什么建议用自增ID做主键?五、正常运行的实例,数据写入后的最终落盘,是从redo log更新过来的还是从buffer pool更新过来的?一、查询缓存和缓冲池 1、缓…

Rust入门篇:数据类型

文章目录 前言整数类型浮点数类型布尔类型字符类型字符串字面量元组类型数组类型切片类型枚举类型结构体类型指针类型最后 前言 你好&#xff0c;我是醉墨居士&#xff0c;前面我们学习了如何使用rust在控制台进行输出&#xff0c;接下来我将带给大家一些关于计算机的基础知识…

JVM笔记2--垃圾收集算法

1、如何确认哪些对象“已死” 在上一篇文章中介绍到Java内存运行时的各个区域。其中程序计数器、虚拟机栈、本地方法栈3个区域随着线程而生&#xff0c;随线程而灭&#xff0c;栈中的栈帧随着方法的进入和退出而有条不紊的执行着入栈和出栈操作。每个栈帧中分配多少内存基本上…

C++ | Leetcode C++题解之第69题x的平方根

题目&#xff1a; 题解&#xff1a; class Solution { public:int mySqrt(int x) {if (x 0) {return 0;}double C x, x0 x;while (true) {double xi 0.5 * (x0 C / x0);if (fabs(x0 - xi) < 1e-7) {break;}x0 xi;}return int(x0);} };

通过helm在k8s上安装minio

1 helm安装minio 1.1 下载minio 添加仓库 helm repo add bitnami https://charts.bitnami.com/bitnami 将minio拉取下来 helm pull bitnami/minio --version 版本号 解压到本地开始编辑配置文件 tar -zxf minio-xxx.tgz [rootk8s-master01 minio]# vi values.yaml 1.2…

Ollama +Docker+OpenWebUI

1 Ollama 1.1 下载Ollama https://ollama.com/download 1.2 运行llama3 $ ollama run llama3 pulling manifest pulling 00e1317cbf74... 100% ▕███████████████████████████████████████████████████████████…

C#面:C# 是否可以对内存直接进行操作

在C#中&#xff0c;可以通过使用指针来对内存进行直接操作。 使用 unsafe 关键字来支持指针操作&#xff0c;并且需要在项目属性中启用选项&#xff1a;允许不安全代码。使用指针可以绕过 C# 的类型安全检查&#xff0c;因此需要谨慎使用&#xff0c;并且只有在必要的情况下才…