Makefile精读 · M02-01 | 显式规则:目标、依赖、配方与 Tab 纪律 模块M02 规则语法精读篇号M02-01 / 41预计阅读35 分钟主线GNU Make文章目录本篇目标30 秒速览正文1. 显式规则的标准形态2. 多个目标、多个依赖显式写法3. Tab 纪律不是审美是语法3.1 正确 vs 错误3.2 编辑器与「看不见的字符」3.3 哪些行需要 Tab4. 多行配方一行一个 shell5. 配方前缀、-、6. 注释、空行与可读性7. 无配方规则只挂依赖8. 对照表显式规则要素读 Makefile 检查清单练习判断题对 / 错改错题动手题下一篇预告本篇目标把「一条显式规则」的语法钉死目标、依赖、配方如何书写多行配方如何执行/-/前缀做什么Tab 与空格为何是纪律问题而非风格偏好。学完本篇你能正确写出并读懂最常见的文件规则并为 M02-02 的多目标、同目标多规则打下基础。30 秒速览显式规则基本形目标: 依赖…下一行起Tab 配方命令。一条规则可以有多个目标、多个依赖依赖可为文件或其它目标名。配方可多行默认每一行单独启动一次 shell要用同一 shell 会话需用\续行或写在一行用;连接。配方前缀不回显、-忽略错误、在-n时仍执行少见但要认得。配方缩进必须是 Tab空格会触发*** missing separator。注释用#规则行也可用\续行把长依赖列表拆开。正文1. 显式规则的标准形态目标: 依赖1 依赖2 配方命令1 配方命令2最小完整例hello: hello.c gcc -Wall -o hello hello.c拆解部分本例说明目标hello冒号左侧要更新的名字依赖hello.c冒号右侧可空无依赖目标也合法配方gcc ...必须以 Tab 开头无依赖的动作目标呼应 M01-03.PHONY: clean clean: rm -f hello *.oclean:右侧为空没有文件依赖只靠.PHONY保证每次执行。2. 多个目标、多个依赖显式写法app: main.o util.o gcc -o app main.o util.o main.o: main.c util.h gcc -c main.c util.o: util.c util.h gcc -c util.c要点依赖列表用空格分隔。长列表可用反斜杠续行app: main.o util.o extra.o \ net.o ui.o gcc -o app main.o util.o extra.o net.o ui.o「一行写多个目标」的显式规则共用同一套依赖与配方合法但语义要小心——M02-02专讲本篇先认单目标为主的常见写法。3. Tab 纪律不是审美是语法3.1 正确 vs 错误# 正确配方前是 Tab hello: hello.c gcc -o hello hello.c # 错误四个空格 hello: hello.c gcc -o hello hello.c错误时 GNU Make 典型报错Makefile:3: *** missing separator. Stop.「separator」在这里主要指规则头与配方之间配方行必须以 Tab 作为分隔/引导。3.2 编辑器与「看不见的字符」高频事故原因现象编辑器把 Tab 转成空格missing separator从网页复制 Makefile空格冒充缩进「显示空白」未开肉眼看不出来本地好好的CI 挂了建议编辑 Makefile 时关闭「Tab 转空格」或为Makefile/makefile/*.mk单独配置。用能显示 Tab/空格的编辑器视图检查。可疑时cat -A MakefileTab 常显示为^I。3.3 哪些行需要 Tab行类型是否 Tab目标: 依赖否顶格或仅因风格缩进——不要用 Tab 当「美观缩进」去缩规则头配方命令行必须 Tab 开头变量 值、include、条件指令否空行、#注释否规则头若被误加了 Tab也可能让 make 把整行当成配方或解析失败——规则头顶格写最省心。4. 多行配方一行一个 shell默认情况下配方的每一行各自调用一次 shellwrong: cd build pwd第二行的pwd不会在build/里执行cd只影响第一个短暂 shell进程结束即丢。正确写法同一 shellok: cd build pwd # 或续行反斜杠后换行仍属「逻辑一行」配方的常见写法 ok2: cd build \ pwd也可用分号ok3: cd build; pwd经验需要保留目录/变量状态 → 写在同一配方行的同一 shell里/;/\。彼此独立的命令 → 分行即可。配方里也可以写多条独立命令hello: hello.c gcc -Wall -c hello.c -o hello.o gcc -o hello hello.o rm -f hello.o每行仍是独立 shell但这里不依赖「上一行 cd 的状态」所以没问题。5. 配方前缀、-、写在配方命令最前面Tab 之后demo: echo 不回显 echo 这一行命令本身 -rm -f maybe-missing.txt echo 即使 make -n 也可能执行少见用途前缀含义不向终端回显该命令行-该命令失败非零退出时不让 make 因此失败在make -n/某些「不执行」模式下仍执行该行给递归$(MAKE)等场景用M08 再遇可叠用顺序惯例为、-、的组合例如clean: -rm -f *.o hello含义尝试删除失败不致命且少回显。慎用-忽略错误容易掩盖真问题。清理缺失文件时用-rm较常见编译步骤一般不要随意加-。6. 注释、空行与可读性# 这是注释生成演示程序 hello: hello.c # 行尾注释也可以 gcc -o hello hello.c注意配方行里的#是否启动注释取决于传给 shell 的内容——配方交给/bin/sh后按shell 规则解释。Make 层对规则头/变量行的#注释更直观。空行可以分隔规则增强可读性空行不会结束「当前规则的配方」——只要下一非空行仍是 Tab 配方就仍属于上一规则直到下一个规则头或非配方指令。hello: hello.c gcc -c hello.c -o hello.o gcc -o hello hello.o上面两行配方仍都属于hello中间空行无妨。若误把第二段写成无 Tab 的裸命令才会解析错乱。7. 无配方规则只挂依赖all: hello libutil.a hello: main.o gcc -o hello main.oall只有依赖、没有配方请求all时Make 负责更新依赖目标。这是all的常见写法M01-03。也可以给文件目标「只追加依赖、配方在别处」——这是M02-02「同一目标多条规则」的话题本篇只要求你认得规则可以没有配方。8. 对照表显式规则要素要素写法要点规则头目标: 依赖…建议顶格依赖分隔空格长列表用\续行配方每行以 Tab 开头多行 shell 状态/;/\勿假设跨行共享 cwd-Tab 后、命令前的前缀注释#无配方仅表达依赖聚合如all读 Makefile 检查清单看到一条显式规则时冒号左侧目标、右侧依赖是否分得清配方是否Tab缩进必要时cat -A多行配方是否错误地假设「上一行cd仍然有效」是否滥用-前缀忽略了不该忽略的错误是否导致排错时看不到关键命令可临时去掉或去掉make -s练习判断题对 / 错配方行用四个空格缩进在 GNU Make 中与 Tab 完全等价。默认情况下配方的每一行各自在一个新的 shell 中执行。前缀-表示该行命令失败时不让 make 失败退出。规则头hello: hello.c必须以 Tab 开头。参考答案错。配方必须 Tab空格会missing separator。对。对。错。规则头不应也不必用 Tab 开头Tab 用于配方行。改错题指出问题并改正hello: hello.c gcc -o hello hello.c cd out cp hello out/参考答案问题第一行配方用了空格应改为 Tab。cd out与下一行分属两个 shellcp不一定在out/下执行且hello产物路径与cp逻辑也混乱。一种改正思路按你真实布局调整hello: hello.c mkdir -p out gcc -o out/hello hello.c或hello: hello.c gcc -o hello hello.c mkdir -p out cp hello out/动手题故意把配方改成空格缩进观察完整报错行号。写一个含cd的错误多行配方再用改正确对比pwd输出。给clean加上-rm -f ...观察回显与「文件不存在」时是否仍成功。下一篇预告M02-02《多目标规则、同一配方与「一个目标多条规则」》— 一行多个目标时 Make 如何解释、同一目标拆成「依赖规则 配方规则」怎么合并以及常见误读。学完 M02-02你读拆得很碎的工程 Makefile 时不会懵。

本月热点