模块M01 心智模型与 make 入口篇号M01-01 / 41预计阅读30 分钟主线GNU Make文章目录本篇目标30 秒速览正文1. Make 是什么不是编译器而是「施工队长」2. 最小可运行例子三元组一眼看清3. 多条规则依赖链怎么连起来4. 配方每一行默认是一条 shell 命令5. Make、Makefile、make 命令三个词别混6. 为何本专栏以 GNU Make 为主线7. 对照表本篇相关术语读 Makefile 检查清单练习判断题对 / 错改错题动手题下一篇预告本篇目标建立 Make 的整体图景分清「你手敲编译命令」和「让make按规则推导该编什么」分别在干什么掌握 Makefile 里最基础的三元组——目标target、依赖prerequisite、配方recipe。学完本篇你打开任意 Makefile能先认出「谁依赖谁、怎么生成」并为 M01-02 的「make 如何找到并执行 Makefile」打好概念基础。30 秒速览Make是构建编排工具根据「文件是否过期」决定要不要重新执行命令。Makefile 的核心是规则目标: 依赖 下方缩进的配方要执行的 shell 命令。目标往往是要生成的文件如hello、main.o也可以是逻辑动作名如clean后文会讲.PHONY。依赖是「先于目标存在/更新」的文件或其它目标任一依赖比目标新就会重跑配方。配方必须以Tab缩进不是空格默认用/bin/sh执行每一行。本专栏主线是GNU Make读文件时先找规则再看变量与函数M03、M04。正文1. Make 是什么不是编译器而是「施工队长」gcc、clang、ld负责真正编译、链接。Make不替代它们而是根据一份说明书Makefile决定现在该生成哪些文件哪些已经是新的、可以跳过按什么顺序调用哪些命令你敲 make → Make 读 Makefile → 比对时间戳 → 只执行过期规则的配方 ↓ gcc / ar / cp …可以把它想成角色类比编译器 / 链接器真正干活的工人锯木头、上螺丝Make施工队长看图纸、看哪块墙该重做Makefile图纸写清「成品 ← 原料 工序」目标文件.o、可执行文件工地上的半成品 / 成品因此会敲gcc≠ 会读 Makefile。单条编译命令是一步操作Makefile 是把「多文件、多步骤、增量跳过」组织成可重复构建的规则网。本专栏的目标正是让你快速读懂这张网并写出可维护的网。2. 最小可运行例子三元组一眼看清在空目录里准备源文件与 Makefilehello.c#includestdio.hintmain(void){puts(hello, make);return0;}Makefilehello: hello.c gcc -o hello hello.c注意第二行前面是Tab不是空格。在该目录执行$makegcc-ohello hello.c $ ./hello hello,make$makemake:hellois up to date.第二次什么都不做Make 发现目标hello比依赖hello.c还新或一样新判定「无需重建」。这条规则拆开就是部分本例中的内容含义目标hello想得到的文件或逻辑名依赖hello.c生成目标前必须就绪、且会影响是否过期的输入配方gcc -o hello hello.c如何从依赖得到目标关系写成一句话若依赖比目标新或目标不存在就执行配方去更新目标。这就是 Make 的时间戳心智M06 会展开边界情况。本篇先牢固记住这一句。3. 多条规则依赖链怎么连起来真实项目很少只有一条规则。常见形态是「可执行文件 ← 若干.o← 若干.c」hello: main.o util.o gcc -o hello main.o util.o main.o: main.c gcc -c main.c util.o: util.c gcc -c util.c你执行make或make hello时Make 大致会发现默认要做的目标是hello默认目标规则见 M01-03。看hello依赖main.o、util.o——若它们过期或不存在先去更新它们。更新main.o时再看它依赖main.c必要时执行gcc -c main.c。两个.o就绪后再执行链接配方生成hello。画成依赖图hello / \ main.o util.o | | main.c util.c读 Makefile 的第一技能不要从上到下当「脚本顺序」硬背而是问——我想要的最终目标是谁它直接依赖谁那些依赖是否还有自己的规则Make 是按依赖图推导不是简单的「文件第几行先执行」。配方里的命令顺序仍重要但规则之间由依赖关系决定先后。4. 配方每一行默认是一条 shell 命令要点本篇够用细节后续篇展开缩进必须是 Tab。若误用空格GNU Make 常报*** missing separator. Stop.M09-02 会精读这类报错。配方里每一行默认交给/bin/sh -c执行可用变量改 shell后文再讲。配方里可以用 Make 的变量与自动变量如$那是 M03 的主场本篇先认「这是要跑的命令」。同一个目标下可以有多行配方hello: hello.c gcc -Wall -o hello hello.c echo built hello以开头的行执行时不回显该命令本身只看到命令输出。以-开头的行命令失败时不让 make 立刻失败慎用M09 再谈。对照「手敲」与「交给 Make」手敲Makefile每次自己想该编谁Make 根据时间戳决定改了一个.c仍可能全量重链设计好依赖后可只重编受影响部分命令散落在笔记/聊天记录里规则写进仓库人人可make5. Make、Makefile、make 命令三个词别混术语含义Make这类构建工具的总称本专栏指GNU Make行为make你在终端里运行的那个程序Makefile规则文本文件常见名Makefile/makefile也可用-f指定见 M01-02规则rule目标: 依赖 配方目标target冒号左侧常为文件名依赖 / 先决条件prerequisite冒号右侧配方recipeTab 缩进的命令行常见误解误解澄清「Makefile 是另一种编程语言」它是声明式规则 少量 Make 语法配方里仍是 shell 命令「Make 会替我选编译器」不会配方里写什么就跑什么gcc/clang/自定义脚本「Makefile 从上到下顺序执行」默认目标及依赖图决定做什么未依赖到的规则可以永不执行6. 为何本专栏以 GNU Make 为主线Linux 上默认的make几乎都是GNU Make开源 C/C、嵌入式、大量工程构建文档都以它为准。模式规则、函数、$(shell)、条件指令等「读大型 Makefile 必备」能力GNU Make 文档最完整。其它 make如部分 BSD make语法有差异本专栏在M10-02只做边界提示正文不混用。你在文档或报错里看到gmake通常就是 GNU Make 的另一种调用名尤其在部分 BSD 系统上。7. 对照表本篇相关术语术语含义目标规则要更新/生成的名字常为文件依赖决定是否过期、以及先构建谁的输入配方更新目标时执行的 shell 命令Tab 开头过期out of date目标不存在或任一依赖比目标新up to date无需重建依赖图目标与依赖连成的有向关系GNU Make本专栏默认的 make 实现读 Makefile 检查清单打开任意Makefile先过一遍能否指出至少一条完整规则的目标 / 依赖 / 配方默认你想构建的「成品」是哪个目标它是否出现在某条规则左侧配方前是否是Tab若编辑器显示为空格要警惕是否存在「目标依赖.o.o再依赖.c」这类链条试着在纸上画两三层有没有看起来像动作名、不一定对应真实文件的目标如clean、install先记下来M01-03 讲.PHONY练习判断题对 / 错Make 本身负责把 C 源码编译成机器码。规则「hello: hello.c 配方」的含义是只要hello.c比hello新或hello不存在就执行配方。配方行用四个空格缩进和用 Tab 缩进对 GNU Make 完全等价。Makefile 里所有规则都会在每次make时从上到下执行一遍。参考答案错。Make 编排何时调用编译器真正编译的是配方里的gcc等程序。对。这是时间戳增量构建的基本规则。错。配方必须以 Tab 开头空格会导致missing separator一类错误。错。Make 按默认目标及你指定的目标沿依赖图决定执行哪些配方无关规则可不动。改错题下面这份 Makefile 想生成hello但有问题。请指出至少两处并给出改正版。hello: hello.c gcc -o hello hello.c假设当前目录已有hello.c。参考答案问题示例配方行用了空格缩进应改为Tab。视编辑器而定若文件名不是 Make 默认会找的名字或路径不对也会失败——本篇假定文件名为Makefile且与源文件同目录找文件规则见 M01-02。改正版hello: hello.c gcc -o hello hello.c第二行开头为 Tab。动手题按本篇第 2 节写出hello.c与Makefile执行两次make观察第二次输出。改一句hello.c的字符串后再make确认会重新编译。用手画依赖图若再增加util.o/util.c图应长什么样下一篇预告M01-02《make 如何找到并执行 Makefile-f-C与默认文件名》— Make 按什么顺序找GNUmakefile/makefile/Makefile-f与-C如何改变工作目录与文件选择以及「在仓库根目录make」时你实际在驱动哪份文件。学完 M01-02你就能在多目录工程里找对入口。