掌握Linux项目自动化构建:从零入门make与Makefile

在这里插入图片描述

文章目录

      • 前言:
    • 一、初识自动化构建工具
      • 1.1 什么是make/Makefile?
      • 1.2 快速体验
    • 二、深入理解核心机制
      • 2.1 依赖关系与依赖方法
      • 2.2 伪目标的妙用
      • 2.3 具体语法
        • a.makefile的基本雏形
        • b.makefile推导原则!
    • 三、更加具有通用型的makefile
      • 1. 变量定义部分
      • 2. 编译规则部分
      • 3. 模式规则(通配规则)
      • 4. 伪目标(`.PHONY`)
      • 5. 完整执行流程示例
      • 6. 新手常见问题
      • 总结
    • 四、高手必备的实用技巧
      • 1.调试 Makefile
      • 2. 常见问题与解决方案
        • Q1:修改头文件后 `make` 不重新编译?
        • Q2:如何指定其他名称的 Makefile?
        • Q3:如何实现跨平台编译?

前言:

不会写Makefile的程序员,就像不会用筷子的美食家——永远尝不到工程化开发的精髓。

在Windows环境下我们习惯使用Visual Studio等IDE的一键编译,但在Linux开发环境中,掌握Makefile就像获得了一把打开高效开发之门的钥匙。它能让你:

  1. 实现真正的自动化编译 - 一个命令完成整个项目的构建
  2. 提升编译效率 - 只重新编译修改过的文件
  3. 管理复杂项目 - 轻松处理多文件、多目录的依赖关系
  4. 跨平台移植 - 一套构建规则适应不同开发环境

一、初识自动化构建工具

1.1 什么是make/Makefile?

在Linux开发中,make是一个智能编译命令,而Makefile是它的配置文件。这对组合就像烹饪食谱:

  • Makefile是菜谱(记录食材和步骤)
  • make是厨师(按菜谱自动执行)

1.2 快速体验

步骤演示:3分钟完成第一个自动化构建

  1. 创建测试文件
# test.c
#include <stdio.h>
int main() {printf("Hello Makefile!\n");return 0;
}
  1. 编写Makefile
# 基础版Makefile
mytest: test.cgcc test.c -o mytest.PHONY: clean
clean:rm -f mytest
  1. 一键编译运行
$ make       # 自动编译
$ ./mytest   # 运行程序
hello Makefile!
$ make clean # 清理项目

二、深入理解核心机制

2.1 依赖关系与依赖方法

核心思想:依赖关系和依赖方法,形成目标文件。

mytest: test.c         # 依赖关系gcc test.c -o mytest  # 依赖方法

理解这两个概念是掌握Makefile的关键:

eg:月底了,没钱了,要让爸爸打钱。

概念生活案例技术解释
依赖关系“我是你儿子”目标文件与源文件的关联
依赖方法“打钱”生成目标文件的具体命令

这两者必须同时存在,事情才能办成!

2.2 伪目标的妙用

.PHONY标记的特殊目标:

.PHONY: clean
clean:rm -f mytest
  • 总是执行清理命令
  • 避免与同名文件冲突
  • 支持make clean独立执行

2.3 具体语法

a.makefile的基本雏形
mytest: test.cgcc test.c -o mytest.PHONY: clean
clean:rm -f mytest
  • mytest是目标文件,test.c是依赖文件,而有多个依赖文件就是依赖文件列表;

  • mytest:test.c是依赖关系;

  • clean也是目标文件,依赖文件是空的,下面是方法;

    make会自定向下扫描makefile文件,默认形成第一个目标文件

    如果想指定形成,make targetname

  • .PHONY是伪目标,所依赖的方法:总是被执行的!

    1.为什么没有.PHONY修饰的目标文件,第一次可以编译,之后就不可以去编译了?

    • 因为要提高效率。

    2.它是怎么做到的?

    • 首次编译:目标文件(如可执行文件)不存在,Make工具会直接执行编译命令生成该文件。

    • 后续编译:Make工具会比较目标文件和其依赖文件(如源文件)的最后修改时间(Modify Time)

      • 若依赖文件比目标文件新(例如源文件被修改过),则重新编译。

      • 若目标文件较新或两者时间相同,则跳过编译,认为输出已是最新。

    3.我们要是想再次编译呢?

    • 手动更新文件时间戳可触发编译:

      touch test.c
      make
      
  • makefile的注释我们用#来注释;

  •   stat test.c //显示文件test.c的详细属性信息File: ‘test.c’Size: 1024      	Blocks: 8          IO Block: 4096   regular fileDevice: 801h/2049d	Inode: 1234567    Links: 1Access: (0644/-rw-r--r--)  Uid: ( 1000/    your_username)   Gid: ( 1000/    your_groupname)Access: 2024-01-01 12:00:00.000000000 +0800Modify: 2024-01-02 13:00:00.000000000 +0800Change: 2024-01-02 13:00:00.000000000 +0800Birth: -
    

    文件=内容+属性

    • 改变内容Modify,Access time变化,改变属性Change time变化。

    如何手动更新时间戳?

    • 修改 atime
      touch -a test.c  # 仅更新 atime
      
    • 修改 mtime
      touch -m test.c  # 仅更新 mtime
      
    • 触发 ctime 更新
      chmod +x test.c  # 修改权限(必然更新 ctime)
      
b.makefile推导原则!
  • make会进行依赖关系的推导,直到依赖文件是存在的。推导的过程我们类似于一个 将依赖方法不断入栈,推导完毕,出栈执行方法!
  • 典型处理流程:
需要更新
无需更新
终极目标
检查依赖
依赖存在?
查找下级依赖
对比时间戳
执行编译
跳过编译

三、更加具有通用型的makefile

BIN=mytest
#SRC=$(shell ls *.c)
SRC=$(wildcard *.c)
OBJ=$(SRC:.c=.o)
CC=gcc
RM=rm -f$(BIN):$(OBJ)@$(CC) $^ -o $@@echo "链接 $^ 成 $@"
%.o:%.c@$(CC) -c $<@echo "编译 ... $< 成 $@".PHONY:clean
clean:@$(RM) $(OBJ) $(BIN).PHONY:test
test:@echo $(BIN)@echo $(SRC)@echo $(OBJ)

下面我会逐行详细解释这个 Makefile 的每一部分.

1. 变量定义部分

BIN=mytest
#SRC=$(shell ls *.c)
SRC=$(wildcard *.c)
OBJ=$(SRC:.c=.o)
CC=gcc
RM=rm -f
代码解释
BIN=mytest定义变量 BIN,表示最终生成的可执行文件名(这里是 mytest)。
#SRC=$(shell ls *.c)注释掉的代码:用 ls 命令获取所有 .c 文件(不推荐,可能有空格问题)。
SRC=$(wildcard *.c)正确做法:使用 wildcard 函数获取当前目录下所有 .c 文件列表。
OBJ=$(SRC:.c=.o)SRC 中的 .c 替换为 .o,得到目标文件列表(如 main.cmain.o)。
CC=gcc定义变量 CC,表示使用的编译器(这里是 gcc)。
RM=rm -f定义变量 RM,表示删除命令(-f 表示强制删除,不提示)。

类比:

  • BIN 像是最终产品的名字(比如“汽车”)。
  • SRC 是原材料清单(所有 .c 文件,比如“发动机.c、轮胎.c”)。
  • OBJ 是加工后的零件(.o 文件,比如“发动机.o、轮胎.o”)。

2. 编译规则部分

$(BIN):$(OBJ)@$(CC) $^ -o $@@echo "链接 $^ 成 $@"
代码解释
$(BIN):$(OBJ)目标文件 $(BIN) 依赖于所有 .o 文件($(OBJ))。
@$(CC) $^ -o $@$^ 表示所有依赖文件(.o 文件),$@ 表示目标文件($(BIN))。
实际执行:gcc main.o utils.o -o mytest
@echo "链接..."打印提示信息(@ 表示不显示命令本身,只输出结果)。

关键符号:

  • $^:所有依赖文件的集合(比如 main.o utils.o)。
  • $@:当前目标文件名(比如 mytest)。

3. 模式规则(通配规则)

%.o:%.c@$(CC) -c $<@echo "编译 ... $< 成 $@"
代码解释
%.o:%.c模式规则:所有 .o 文件依赖于同名的 .c 文件(如 main.o 依赖 main.c)。
@$(CC) -c $<$< 表示第一个依赖文件(这里是 .c 文件)。
实际执行:gcc -c main.c(生成 main.o)。
@echo "编译..."打印编译过程信息。

关键符号:

  • $<:当前依赖的第一个文件(比如 main.c)。

4. 伪目标(.PHONY

.PHONY:clean
clean:@$(RM) $(OBJ) $(BIN).PHONY:test
test:@echo $(BIN)@echo $(SRC)@echo $(OBJ)
代码解释
.PHONY:clean声明 clean 是一个伪目标(不生成实际文件,仅执行命令)。
@$(RM) $(OBJ) $(BIN)删除所有 .o 文件和可执行文件 $(BIN)(实际执行:rm -f main.o mytest)。
.PHONY:test声明 test 是伪目标,用于调试变量。
@echo $(BIN)...打印变量 BINSRCOBJ 的值(检查变量是否正确)。

为什么用 .PHONY
如果目录下恰好有一个名为 clean 的文件,Make 会认为 clean 已是最新而不执行命令。加上 .PHONY 可以强制执行。

5. 完整执行流程示例

假设目录下有 main.cutils.c

  1. 首次运行 make

    • 根据 %.o:%.c 规则,编译所有 .c 文件生成 .o 文件:
      gcc -c main.c -o main.o
      gcc -c utils.c -o utils.o
      
    • 根据 $(BIN):$(OBJ) 规则,链接 .o 文件生成 mytest
      gcc main.o utils.o -o mytest
      
  2. 运行 make clean

    • 删除所有 .o 文件和 mytest
      rm -f main.o utils.o mytest
      
  3. 运行 make test

    • 打印变量值(用于调试):
      echo mytest
      echo main.c utils.c
      echo main.o utils.o
      

6. 新手常见问题

  1. 为什么用 wildcard 而不用 ls

    • ls *.c 可能因文件名含空格或特殊字符出错,wildcard 是 Makefile 内置的安全函数。
  2. $^$< 的区别?

    • $^:所有依赖文件(用于链接阶段)。
    • $<:第一个依赖文件(用于编译单个 .c 文件时)。
  3. @ 的作用?

    • 禁止命令回显(Make 默认会打印执行的命令,@ 让终端只显示命令的输出)。

总结

  • 变量:定义文件名、工具命令等(BIN, SRC, CC)。
  • 规则:指定目标和依赖关系(目标:依赖)。
  • 自动变量$@(目标)、$^(所有依赖)、$<(第一个依赖)。
  • 伪目标.PHONY 声明非文件目标(如 clean)。

通过这个 Makefile,你可以:

  1. 编译所有 .c 文件生成可执行文件 mytest
  2. 清理生成的文件(make clean)。
  3. 调试变量值(make test)。

四、高手必备的实用技巧

1.调试 Makefile

$ make -n   # 显示将要执行的命令
$ make -d   # 显示详细调试信息
  • 作用:Makefile 默认会隐藏执行的命令(只显示结果),可以通过以下方式调试:
    • make -n:仅打印命令但不执行(模拟运行)。
    • make --debug:显示详细的执行过程(如依赖检查、规则匹配)。

2. 常见问题与解决方案

Q1:修改头文件后 make 不重新编译?
main.o: main.c header.h  # 显式声明头文件依赖$(CC) -c $< -o $@
  • 问题原因
    Makefile 默认只检查 .c 文件的修改时间,如果 header.h 被修改但未声明依赖,不会触发重新编译。
  • 解决方案
    在目标规则中显式列出所有依赖的头文件(如上例),或通过 gcc -MM 自动生成依赖关系(推荐)。
Q2:如何指定其他名称的 Makefile?
make -f MyMakefile  # 使用自定义文件名(如 MyMakefile)
  • 适用场景
    项目中有多个构建配置文件(如 MakefileMyMakefile),需指定其中一个执行。
Q3:如何实现跨平台编译?
ifeq ($(OS),Windows_NT)  # 判断是否为 WindowsRM = del /Q         # Windows 删除命令
elseRM = rm -f          # Linux/macOS 删除命令
endif
  • 作用
    根据操作系统动态切换命令,避免平台兼容性问题(如 rm 在 Windows 中不可用)。
  • 扩展:还可用于设置不同的编译器、路径分隔符等。

📌 小贴士:优秀的Makefile就像项目说明书,能让您的代码更易于维护和协作!

希望这篇指南能帮助您开启自动化构建之旅!如有疑问,欢迎在评论区交流讨论~
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/75725.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

深度分页优化思路

深度分页优化思路 思考以下问题 查询以下SQL的流程是怎么样的呢&#xff1f; 为什么只查询10条数据需要7秒&#xff1f; # 查询时间7秒 SELECT * FROM user ORDER BY age LIMIT 1000000, 10问题分析 为什么分页查询随着翻页的深入&#xff0c;会变得越来越慢。 其实&#xff0…

使用 Vite 提升前端开发体验:入门与配置指南

在现代前端开发中&#xff0c;构建工具的选择对开发效率和项目性能有着至关重要的影响。Vite 是一个新兴的前端构建工具&#xff0c;由 Vue.js 的作者尤雨溪开发&#xff0c;旨在通过利用现代浏览器的原生 ES 模块特性&#xff0c;提供更快的开发服务器启动速度和更高效的热更新…

MYSQL基本语法使用

目录 一、mysql之DML 增加语句 删除语句和truncate 更新语句 replace语句 select查询语句 二、select多种用法 查询时的别名使用 分组 分组后的筛选 结果排序 分页功能 分表 多表关联查询 练习题 一、单表查询 二、多表查询 前面已经学习了mysql的安装和基本语…

自动化测试selenium(Java版)

1.准备工作 1.1.下载浏览器 自动化测试首先我们要准备一个浏览器,我们这里使用谷歌(chrome)浏览器. 1.2.安装驱动管理 每一个浏览器都是靠浏览器驱动程序来启动,但是浏览器的版本更新非常快,可能我们今天测试的是一个版本,第二天发布了一个新的版本,那么我们就要重构代码,很…

HarmonyOS Next应用架构设计与模块化开发详解

引言 在HarmonyOS Next开发中&#xff0c;合理的应用架构设计和模块化开发是构建高效、可维护应用的关键。本文将深入探讨HarmonyOS Next应用的架构设计思路&#xff0c;并通过实际代码示例展示如何实现模块化开发。 应用架构设计 HarmonyOS Next应用通常采用分层架构设计&…

伊利工业旅游4.0,近距离感受高品质的魅力

3月24日&#xff0c;在2025年第112届全国糖酒会&#xff08;简称春糖&#xff09;前夕&#xff0c;伊利集团“可感知高品质探寻荟”活动在成都召开&#xff0c;记者走进伊利在西南地区最大的乳制品生产基地—邛崃工厂&#xff0c;零距离见证液态奶、酸奶、冷饮等乳制品的诞生&a…

测试用例生成平台通过大模型升级查询功能,生成智能测试用例

在测试工作中&#xff0c;查询功能是各类系统的核心模块&#xff0c;传统的测试用例编写往往耗时且重复。如何让老旧平台焕发新活力&#xff1f;本文将结合大模型技术&#xff0c;通过用户输入的字段信息&#xff0c;自动化生成高效、精准的测试用例。同时&#xff0c;我们还将…

基于javaweb的SpringBoot雪具商城系统设计与实现(源码+文档+部署讲解)

技术范围&#xff1a;SpringBoot、Vue、SSM、HLMT、Jsp、PHP、Nodejs、Python、爬虫、数据可视化、小程序、安卓app、大数据、物联网、机器学习等设计与开发。 主要内容&#xff1a;免费功能设计、开题报告、任务书、中期检查PPT、系统功能实现、代码编写、论文编写和辅导、论…

【AI学习笔记】Coze平台实现将Excel文档批量导入数据库全过程

背景前摇&原视频教程&#xff1a; 最近看到很多同学都在用Coze平台操作数据&#xff0c;我也想了解一下工作流的搭建和数据处理过程&#xff0c;但是一下子又看不懂太复杂的逻辑&#xff0c;于是上B站搜索相关的基础教程。 Coze官方教程&#xff1a; 之前有看过Coze平台…

【Axure高保真原型】纵向图片轮播

今天和大家分享纵向图片轮播的原型模版&#xff0c;载入后会自动循环轮播&#xff0c;鼠标移入图片后停止轮播&#xff0c;可以通过点击上下箭头&#xff0c;向上或向下滑动切换上一张或下一张图片&#xff0c;也可以点击右侧小圆点快速切换至对应图片……具体效果可以打开下方…

力扣32.最长有效括号(栈)

32. 最长有效括号 - 力扣&#xff08;LeetCode&#xff09; 代码区&#xff1a; #include<stack> #include<string> /*最长有效*/ class Solution { public:int longestValidParentheses(string s) {stack<int> st;int ans0;int ns.length();st.push(-1);fo…

如何在 React 项目中使用React.lazy和Suspense实现组件的懒加载?

大白话如何在 React 项目中使用React.lazy和Suspense实现组件的懒加载&#xff1f; 在 React 项目里&#xff0c;有时候组件功能多、体积大&#xff0c;要是一次性把所有组件都加载进来&#xff0c;网页加载速度就会变慢。而 React 提供了 React.lazy 和 Suspense 这两个好东西…

ffmpeg-将多个视频切片成一个新的视频

使用 ffmpeg 工具可以轻松完成将多个视频切片合并为一个新的视频。以下是实现这一目标的具体步骤和命令。 步骤概览 1、将多个视频切片。 2、创建文本文件列出切片的视频片段。 3、使用 ffmpeg 合并这些切片为一个新的视频。 一&#xff1a;安装 ffmpeg 确保你的系统中已经安…

【第2月_day10】Pandas数据查看与选择

以下是专为小白设计的 Pandas数据查看与选择 学习内容&#xff0c;从基础到应用逐步讲解&#xff0c;附带清晰示例和注意事项&#xff1a; 一、数据查看&#xff1a;快速了解你的数据 1. head() 和 tail() 作用&#xff1a;查看数据的前几行或后几行&#xff0c;默认显示5行。…

Jetpack LiveData 使用与原理解析

一、引言 在 Android 开发中&#xff0c;数据的变化需要及时反映到界面上是一个常见的需求。然而&#xff0c;传统的方式可能会导致代码复杂、难以维护&#xff0c;并且容易出现内存泄漏等问题。Jetpack 组件中的 LiveData 为我们提供了一种优雅的解决方案&#xff0c;它是一种…

Unity2D 五子棋 + Photon联网双人对战

开发环境配置 Unity版本2022.3 创建Photon账号以及申请Photon中国区服务 官网申请账号&#xff1a;Multiplayer Game Development Made Easy Photon Engine 中国区服务&#xff1a; 光子引擎photonengine中文站 成都动联无限科技有限公司(vibrantlink.com) 导入PUN2插件以及…

(UI自动化测试web端)第二篇:元素定位的方法_css定位之属性选择器

看代码里的【find_element_by_css_selector( )】( )里的表达式怎么写&#xff1f; 文章介绍了第四种写法属性选择器 &#xff0c;你要根据网页中的实际情况来判断自己到底要用哪一种方法来进行元素定位。每种方法都要多练习&#xff0c;全都熟了之后你在工作当中使用起来元素定…

预编译能否 100%防 sql 注入?

&#x1f31f; 什么是 SQL 注入&#xff1f; SQL 注入&#xff08;SQL Injection&#xff09;是指攻击者利用特殊输入&#xff0c;让数据库执行它本来不应该执行的代码&#xff0c;从而获取或篡改数据。 就像在考试的时候偷偷改题目&#xff0c;让老师改成你想要的内容&#…

第十五章 | Layer2、Rollup 与 ZK 技术实战解析

&#x1f4da; 第十五章 | Layer2、Rollup 与 ZK 技术实战解析 ——构建下一代高性能区块链应用&#xff0c;从 Solidity 到 zkSync&#xff01; ✅ 本章导读 Layer2 和零知识证明&#xff08;ZK&#xff09;正成为区块链发展的核心方向。 随着主网 Gas 居高不下、TPS 无法满…

2025-03-26 学习记录--C/C++-PTA 6-3 求链式表的表长

合抱之木&#xff0c;生于毫末&#xff1b;九层之台&#xff0c;起于累土&#xff1b;千里之行&#xff0c;始于足下。&#x1f4aa;&#x1f3fb; 一、题目描述 ⭐️ 6-3 求链式表的表长 本题要求实现一个函数&#xff0c;求链式表的表长。 函数接口定义&#xff1a; &…