Linux 进阶:一文搞懂 make 工具与 Makefile 编写

Linux 进阶:一文搞懂 make 工具与 Makefile 编写

🔥个人主页:Cx330🌸

❄️个人专栏:《C语言》《LeetCode刷题集》《数据结构-初阶》《C++知识分享》

《优选算法指南-必刷经典100题》《Linux操作系统》:从入门到入魔

🌟心向往之行必能至


🎥Cx330🌸的简介:


目录

前言:

一、先搞懂:make 是什么?Makefile 又是什么?

1.1 背景

1.2 make:Linux 下的自动化构建工具

1.3 Makefile:make 的 “操作手册”

二、最佳实践:先见一下,如何使用

2.1 构建项目

2.2 清理项目

2.3 理解Makefile/make,编译工作的推导过程,依赖关系和依赖方法

2.4 PHONY:伪目标——总被执行

三、ACM时间管理

3.1 重复make,竟然不让我make?

3.2 Access Time(访问时间)

3.3 Modify Time(修改时间)

3.4 Change Time(状态改变时间)

3.5 详解make如何知道code.c是否需要被重新编译 

四、makefile最佳实践和实用语法

4.1 $@与$^

4.2 定义变量

4.3 $<和$^

五、总结:为什么一定要学 Makefile?​


前言:

如果你在 Linux 下编译过 C/C++ 项目,一定遇到过这样的场景:

修改一个文件后,需要手动输入gcc main.c func.c -o app重新编译,文件多了还容易漏写;如果只改了一个小模块,却要重新编译所有文件 —— 既浪费时间又麻烦。

make工具Makefile,就是为解决这个问题而生的自动化构建方案。今天我们就从基础到实战,彻底掌握它们。


一、先搞懂:make 是什么?Makefile 又是什么?

1.1 背景

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

1.2 make:Linux 下的自动化构建工具

make是一个命令行工具,核心能力是:

  • 检查文件的修改时间(对比目标文件与依赖文件)
  • 只重新编译 “被修改过的依赖文件” 对应的目标
  • 按预定规则(写在 Makefile 里)逐步执行构建步骤

简单说:它能帮你 “智能编译”,避免重复劳动。

1.3 Makefile:make 的 “操作手册”

Makefile 是一个文本文件,没有后缀名,核心作用是定义构建规则—— 告诉 make:

  • 最终要生成的目标文件(比如可执行程序app
  • 生成目标需要依赖哪些文件(比如main.ofunc.o
  • 具体用什么命令生成目标(比如gcc -c main.c

没有 Makefile,make 命令就会报错(不知道该做什么)。


二、最佳实践:先见一下,如何使用

2.1 构建项目

1. 创建Makefile文件

touch Makefile //创建Makefile文件

2. vim编辑Makefile

3. make

2.2 清理项目

如果要清理项目时,可能会不小心将程序搞崩溃,所以这时候就需要Makefile自动化清理

2.3 理解Makefile/make,编译工作的推导过程,依赖关系和依赖方法


我们现在将clean换到前面,我们再make试一试:

我们发现make变成了进行clean的操作,那么我如果想编译code.c呢?—接着往下看:

我们只需在make后面加上目标文件即可!

那么make的逻辑是什么呢?

Makefile、make
默认形成的是从上向下遇到的第一个目标文件!!
且执行一组依赖关系和依赖方法

如图:

思考:make / Makefile:具体是如何形成可执行程序的呢???推导过程又是如何呢?

我们打开vim编辑Makefile

我们make一下:

make 是如何工作的,在默认的方式下,也就是我们只输入 make 命令。那么:

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

清理工程:

2.4 PHONY:伪目标——总被执行


三、ACM时间管理

3.1 重复make,竟然不让我make?

这里我们重复make发现不让我们make了,这里显示的意思是code文件没有进行更新

为什么我没有更新就不让我make呢?

答:gcc编译代码的时候,代码如果没有被编译过,或者曾经被修改过,make+gcc才会进行重新编译!

那么make如何知道code.c是否需要被重新编译呢?

 这里我们补充一个指令:

查看文件时间:stat code.c

3.2 Access Time(访问时间)

  • 缩写:atime
  • 含义:文件内容被读取 / 访问的时间(仅指查看内容,不包含修改)。
  • 触发场景:用 cat/less/more 打开文件查看内容、用 grep 匹配文件内容等。
  • 注意:部分系统为优化性能,会通过 noatime 挂载选项关闭 atime 的自动更新(避免频繁写磁盘)。

3.3 Modify Time(修改时间)

  • 缩写:mtime
  • 含义:文件内容被修改并保存的时间(仅与文件内容变更相关)。
  • 触发场景:用编辑器(如 vim)修改文件内容后保存、用 echo "内容" >> 文件 追加内容等。
  • 关联:mtime 变更时,文件的元数据也会变化,因此 ctime 会同步更新

3.4 Change Time(状态改变时间)

  • 缩写:ctime
  • 含义:文件元数据(非内容)被修改的时间(元数据包括权限、所有者、文件大小、文件名、链接数等)。
  • 触发场景:用 chmod 修改权限、chown 变更所有者、mv 重命名文件、rm 删除文件(删除前会改元数据)等。

3.5 详解make如何知道code.c是否需要被重新编译 

我们再来回顾一下:

文件 = 内容 + 属性

先说结论:文件内容改变影响的是Modify时间,文件属性改变影响的是Change时间

这里我们改变文件属性,来验证哪个时间会变化:

说明:改变文件属性影响的是文件的Change时间,而其他时间不变

我们再来改变文件内容,来验证哪个时间会改变:

这里我们改变code.c的内容来验证一下

从图中发现我们改变内容,ACM时间都改了!!!

在Linux系统中,修改文件内容,文件属性就会因此而变化,这是正常的!!!

我们再来读取一下文件信息,来观察时间变化情况:

先说结论这里读取文件信息会影响的是Access时间

但是你图片上展示的Access、Modify、Change时间都是一样的啊?我怎么知道你改变的是哪个?

说明:

我们读取文件会导致大量IO操作,Linux系统不会傻傻的你用一次就给你更新一下Access时间,这样会导致太大的IO操作,Linux对其进行特殊处理,它有一个策略,就是在你访问7、8次识别出来,他才会更新一下,最后总结一下:Access时间更新最频繁,但是他不是常更新,他有自己的策略,和系统有关

那你还没有给我解决前一个问题啊:make怎么知道我的文件是否需要被重新编译呢?

我们再来make一下:

永远都是先有源文件才有目标可执行文件!所以我们可以来画个图:

code文件时间比源文件时间新,此时我们再make一下看看结果

果然如此,所以我们得到结论:make是如何知道code.c需要被重新编译?

解析:

那么我可执行程序的时间比源文件新,说明源文件没有更新,也就说明Modify时间没有变!,如果源文件比可执行程序文件新,说明我们源文件被改了,Modify时间变了!此时make就知道code.c需要被重新编译了!

答:只要code.c的Modify时间比code可执行程序文件新,就需要重新编译!

那么我们将.PHONY修饰一下gcc编译试一下:

我们再来make一下:

此时我们发现make竟然可以一直执行,那么我想问一下大家知道.PHONY的本质了嘛?

.PHONY的本质:忽略实践对比!

但是我们不建议用.PHONY进行修饰!


四、makefile最佳实践和实用语法

在编译过程中,我们都倾向于将源文件 .c 先编译为 .o 文件

分两步是比较推荐的做法!

4.1 $@与$^

code:code.o gcc -o $@ $^ // $@代表code $^代表code.o code.o:code.c gcc -c code.c .PHONY:clean clean: rm -rf code.o code.exe 

4.2 定义变量

我们再来make一下看是否可以执行

我们可以观察到都是可以执行的!

我们再来打印一下要进行的操作!

有没有发现这样很挫,能不能不让它回显:有的!

我们只需要在前面加上@符号即可,我们再来make一下:

这样他就不会回显出来了!

那么如果不想让gcc -c code.cgcc -o code.exe code.o回显,同样可以在他们前面加上@

查看文件:

4.3 $<和$^

$^ 表示所有的依赖文件

$< 代表第一个依赖文件

如果我们创建了10个文件都进行编译,我们来看一下make结果:

 

我们创建了10个.c文件,但为什么就给我一个.o编译为.exe?

这是因为我们在声明.o文件时只声明一个,那么向编译10个就要全写上嘛?那如果100个那岂不是很麻烦?

那么接下来有两种方法来进行展开:

做法一:

这时候就可以观察到全部展开了!

我们发现没有.o文件啊,这里有个语法make自动识别:

我们再来make一下

这样.o文件也展开了

我们再对Makefile进一步优化:

这样就可以全部删除了

再进行优化:

如果我们创建上1000个.c文件进行编译,我们来看一下它的编译时间:

这里大家可能看不到过程,其实是编译了很长时间的,我们在make一下发现他不让我百衲衣,因为会花费大量时间,我们再来code.c把源文件修改一下,如何发现再make它编译一下就出来了,这是为什么呢?

因为他只编译了被修改的内容!!!

我们之前说到的展开文件还有一个做法也是比较推荐的:


五、总结:为什么一定要学 Makefile?​

  1. 效率提升:只编译修改过的文件,大型项目节省大量时间;​
  2. 统一流程:团队协作时,所有人用同一个 Makefile,避免 “本地能跑,别人跑不了”;​
  3. 进阶基础:后续学习 CMake(生成 Makefile 的工具)、自动化部署,都需要理解 Makefile 的核心逻辑。​

如果你是 Linux 开发新手,建议从 “多文件案例” 开始动手实践,重点注意Tab 键和变量简化这两个细节 —— 上手后会发现,Makefile 其实比想象中简单!


结语:掌握Makefile能显著提升Linux开发效率,特别适合需要管理多文件项目的开发者,讲解了大量实用案例和底层原理分析,帮你彻底理解这个工程化开发必备工具

Read more

43-dify案例分享-MCP-Server让工作流秒变第三方可调用服务

43-dify案例分享-MCP-Server让工作流秒变第三方可调用服务

1.前言 之前我们为大家介绍过MCP SSE插件,它能够支持MCP-server在Dify平台上的调用,从而帮助Dify与第三方平台提供的MCP-server进行无缝对接。有些小伙伴提出了疑问:既然Dify可以通过MCP SSE插件调用其他平台的MCP-server,那么Dify的工作流或Chatflow是否也能发布为MCP-server,供其他支持MCP client的工具使用呢?今天,我们将为大家介绍一款Dify插件——mcp-server,它能够实现这一功能,即将Dify的工作流或Chatflow发布为MCP-server,供其他第三方工具调用。 插件名字叫做MCP-server,我们在dify插件市场可以找到这个工具 Mcp-server 是一个由 Dify 社区贡献的 Extension 类型插件。安装后,你可以把任何 Dify 应用转变成符合 MCP 标准的 Server Endpoint,供外部 MCP 客户端直接访问。它的主要功能包括: * **暴露为 MCP 工具:**将 Dify 应用抽象为单一 MCP 工具,供外部 MCP 客户端(如

By Ne0inhk
【大模型系列篇】大模型基建工程:基于 FastAPI 自动构建 SSE MCP 服务器

【大模型系列篇】大模型基建工程:基于 FastAPI 自动构建 SSE MCP 服务器

今天我们将使用FastAPI来构建 MCP 服务器,Anthropic 推出的这个MCP 协议,目的是让 AI 代理和你的应用程序之间的对话变得更顺畅、更清晰。FastAPI 基于 Starlette 和 Uvicorn,采用异步编程模型,可轻松处理高并发请求,尤其适合 MCP 场景下大模型与外部系统的实时交互需求,其性能接近 Node.js 和 Go,在数据库查询、文件操作等 I/O 密集型任务中表现卓越。 开始今天的正题前,我们来回顾下相关的知识内容: 《高性能Python Web服务部署架构解析》、《使用Python开发MCP Server及Inspector工具调试》、《构建智能体MCP客户端:完成大模型与MCP服务端能力集成与最小闭环验证》   FastAPI基础知识 安装依赖 pip install uvicorn, fastapi FastAPI服务代码示例  from fastapi import FastAPI app

By Ne0inhk
【MCP】详细了解MCP协议:和function call的区别何在?如何使用MCP?

【MCP】详细了解MCP协议:和function call的区别何在?如何使用MCP?

本文介绍了MCP大模型上下文协议的的概念,并对比了MCP协议和function call的区别,同时用python sdk为例介绍了mcp的使用方式。 1. 什么是MCP? 官网:https://modelcontextprotocol.io/introduction 2025年,Anthropic提出了MCP协议。MCP全称为Model Context Protocol,翻译过来是大模型上下文协议。这个协议的主要为AI大模型和外部工具(比如让AI去查询信息,或者让AI操作本地文件)之间的交互提供了一个统一的处理协议。我们常用的USB TypeC接口(USB-C)统一了USB接口的样式,MCP协议就好比AI大模型中的USB-C,统一了大模型与工具的对接方式。 MCP协议采用了C/S架构,也就是服务端、客户端架构,能支持在客户端设备上调用远程Server提供的服务,同时也支持stdio流式传输模式,也就是在客户端本地启动mcp服务端。只需要在配置文件中新增MCP服务端,就能用上这个MCP服务器提供的各种工具,大大提高了大模型使用外部工具的便捷性。 MCP是开源协议,能让所有A

By Ne0inhk
超详细图文教程:用vscode+copilot(代理模式)便捷使用mcp+一个范例:用自然语言进行3d建模

超详细图文教程:用vscode+copilot(代理模式)便捷使用mcp+一个范例:用自然语言进行3d建模

在vscode使用claude mcp吧! 在vscode更新到最新版本(注意,这是前提)后,内置的copilot可以使用mcp了!!! 关于mcp(Model Context Protocol 模型上下文协议),可以参考我的上一篇文章: MCP个人理解+示例+集成管理+在python中调用示例,给AI大模型装上双手-ZEEKLOG博客 以下是使用教程: 1.点击左下角的齿轮状设置按钮,点击设置 2.在输入面板输入chat.agent.enabled,勾上勾选框 3.点击Ctrl+shift+P,输入reload,点击重新加载窗口,刷新窗口 4.打开copilot后,在右下角将模式改为代理即可。 5.点击工具按钮,开始安装mcp 先去github找到自己想要添加的mcp服务,以blender MCP为例,打开https://github.com/ahujasid/blender-mcp,可以在readme文档里看到详细的安装过程。可以看到,

By Ne0inhk