Makefile精读 · M03-04 | 目标/模式特定变量、`export` 与环境变量边界

Makefile精读 · M03-04 | 目标/模式特定变量、`export` 与环境变量边界 模块M03 变量体系篇号M03-04 / 41预计阅读35 分钟主线GNU Make文章目录本篇目标30 秒速览正文1. 全局变量的局限2. 目标特定变量target-specific3. 模式特定变量pattern-specific4. 叠加与覆盖怎么读最终值5. exportMake 变量 → 配方环境5.1 默认边界5.2 导出5.3 何时需要 export6. export / unexport 与整表导出7. 三条通路别打架8. 与递归 Make 的交接前瞻9. 实用骨架10. M03 模块小结11. 对照表读 Makefile 检查清单练习判断题对 / 错改错题动手题下一篇预告本篇目标掌握「同一个变量名对不同目标可以有不同值」目标特定变量与模式特定变量搞清 Make 变量如何通过export进入配方里的 shell 环境以及和命令行/环境变量的边界。学完本篇M03 收官——变量从「全局赋值」到「按目标覆盖」再到「进不进子进程」形成闭环。30 秒速览目标特定变量target: VAR : value只在构建该目标及其所需的前置规则时生效。模式特定变量%.o: CFLAGS -g一类匹配到的目标套用该赋值。特定变量可叠加在全局变量之上常见用途某个文件关优化、某模块加-I。export VAR让变量出现在配方所启动的 shell 环境中未 export 的 Make 变量默认不一定在环境里。配方里写$(VAR)是 Make先展开再交给 shell环境里的$VAR是 shell 自己看的——两条通路别混。子 make$(MAKE)会继承一批标志与导出变量细节与坑见 M08-02。正文1. 全局变量的局限CFLAGS : -Wall -O2 %.o: %.c $(CC) $(CFLAGS) -c $ -o $所有.o共用同一套CFLAGS。若只有debug.o需要-O0 -g整段改全局会牵连所有文件。目标/模式特定变量就是为此准备的「局部覆盖」。2. 目标特定变量target-specific语法目标: 变量 : 值 目标: 变量 值 目标: 变量 ? 值例CC : gcc CFLAGS : -Wall -O2 debug.o: CFLAGS : -Wall -O0 -g %.o: %.c $(CC) $(CFLAGS) -c $ -o $ app: main.o debug.o util.o $(CC) -o $ $^构建debug.o时CFLAGS为-Wall -O0 -g构建其它.o时仍用全局-Wall -O2。要点赋值行本身不是配方不要用 Tab 写成配方行。特定变量在构建该目标时可见通常也会影响为了更新该目标而构建的依赖GNU Make 的继承语义目标特定变量会向该目标的先决条件链传递——读代码时记住「为这个目标服务的那条子树」。可用在全局基础上追加plugin.o: CFLAGS -fPIC3. 模式特定变量pattern-specific%.o: CFLAGS -Iinclude test_%.o: CFLAGS -DUNIT_TEST写法含义%.o: CFLAGS -Iinclude所有匹配%.o的目标追加头文件路径test_%.o: CFLAGS -DUNIT_TEST仅test_*.o追加宏与目标特定变量相比不必为每个文件写一行适合「一类文件」。常和模式规则一起读CFLAGS : -Wall build/%.o: CFLAGS -Iinclude build/%.o: src/%.c mkdir -p $(D) $(CC) $(CFLAGS) -c $ -o $阅读顺序先看全局CFLAGS→ 再看该目标命中的模式/目标特定赋值 → 再看配方里$(CFLAGS)最终长什么样。4. 叠加与覆盖怎么读最终值CFLAGS : -Wall app.o: CFLAGS : -O0 app.o: CFLAGS -g对app.o先目标特定: -O0再 -g配方中常见结果是-O0 -g不再带全局-Wall因为:整段替换了该目标上的CFLAGS。对照CFLAGS : -Wall app.o: CFLAGS -g则多为-Wall -g追加不整段替换。读法口诀有没有对该目标的:整替换有没有追加有没有模式特定也命中了%.o与app.o可能同时相关多条特定赋值都存在时以 Make 合并规则为准排错时用配方临时打印app.o: %.c echo CFLAGS$(CFLAGS) $(CC) $(CFLAGS) -c $ -o $或用已有模式规则时只加一行echo。5.exportMake 变量 → 配方环境5.1 默认边界VERSION : 1.2.3 .PHONY: show show: echo make展开$(VERSION) echo shell环境$$VERSION注意配方里写$$VERSIONMake 先把$$变成$再交给 shell 去看环境变量VERSION。通常输出类似make展开1.2.3 shell环境因为VERSION只是 Make 变量未 export时配方里的 shell 环境未必有它。5.2 导出VERSION : 1.2.3 export VERSION show: echo shell环境$$VERSION或export VERSION : 1.2.3此时 shell 侧$$VERSION能看到1.2.3。5.3 何时需要 export场景是否需要 export配方只用$(VAR)Make 展开一般不需要配方调用的脚本读环境变量$VAR需要$(MAKE) -C sub且子 Makefile 用?吃环境常需要 export 或显式传参把CFLAGS塞进第三方工具环境按工具要求 export原则能在配方里写$(VAR)解决的优先 Make 展开只有子进程/脚本坚持读环境时再export。6.export/unexport与整表导出export CC CFLAGS LDFLAGS unexport SECRETexport # 导出全部变量影响面大慎用读到光秃秃的export作者可能在图省事也会让环境变脏、子进程行为难测——改动前先搞清范围。unexport VAR取消导出避免泄漏或不必要的环境干扰。7. 三条通路别打架以CFLAGS为例通路写法谁解释Make 展开进配方$(CC) $(CFLAGS) -c …Make → 命令行文本shell 环境export CFLAGS 脚本内$CFLAGSshell / 子程序命令行覆写make CFLAGS-O0进入 Make 变量系统M03-02混用示例易晕export CFLAGS : -Wall show: echo make$(CFLAGS) echo env$$CFLAGS ./tools/print-cflags.sh若脚本只认环境必须export若脚本参数传入更干净./tools/build.sh --cflags $(CFLAGS)8. 与递归 Make 的交接前瞻export CFLAGS .PHONY: sub sub: $(MAKE) -C plugin子目录 Makefile 里的CFLAGS ? -O2可能吃到你 export 的值。若不想污染子项目不要 export改为$(MAKE) -C plugin CFLAGS$(CFLAGS)显式传参意图更清晰M08-02 展开利弊。本篇先建立边界意识即可。9. 实用骨架CC ? gcc CFLAGS ? -Wall -O2 export CC # 仅当子脚本需要时再 export此处示范可删 # 模式特定所有 .o 加 include %.o: CFLAGS -Iinclude # 目标特定单个文件调试编译 buggy.o: CFLAGS : -Wall -O0 -g .PHONY: all clean all: app app: main.o buggy.o $(CC) -o $ $^ %.o: %.c $(CC) $(CFLAGS) -c $ -o $ clean: rm -f app *.o10. M03 模块小结篇能力M03-01五种赋值:?!M03-02展开时机、自引用、覆写链 ★M03-03自动变量与目录变体 ★M03-04目标/模式特定变量、export边界下一模块M04进入文本函数patsubst、wildcard、foreach…——变量开始「算出来」而不只是手写列表。11. 对照表写法作用域VAR : x全局默认foo.o: VAR : x目标foo.o及相关构建链%.o: VAR x匹配模式的目标export VAR配方 shell 环境可见$(VAR)于配方Make 展开不依赖 export$$VAR于配方shell 环境变量读 Makefile 检查清单搜: CFLAGS/: CXXFLAGS等目标特定赋值别只看文件头部全局值区分:替换与追加对特定变量的影响配方用的是$(VAR)还是脚本读$VAR后者需要export吗是否存在裸export全量导出影响面是否可接受子 make 的变量是靠环境继承还是命令行显式传递练习判断题对 / 错foo.o: CFLAGS -g会为构建foo.o追加-g。未export的 Make 变量配方里写$(VAR)一定无法展开。配方中的$$HOME表示让 shell 读取环境变量HOME。模式特定变量%.o: CFLAGS -Iinclude只影响名字匹配%.o的目标。参考答案对。错。$(VAR)由 Make 展开不依赖 exportexport 影响的是 shell 环境里的$VAR。对。对。改错题作者希望只有sandbox.o使用-O0却写成CFLAGS : -Wall -O2 CFLAGS : -Wall -O0 %.o: %.c $(CC) $(CFLAGS) -c $ -o $应如何改成目标特定变量参考答案CFLAGS : -Wall -O2 sandbox.o: CFLAGS : -Wall -O0 %.o: %.c $(CC) $(CFLAGS) -c $ -o $若只想追加-O0、保留-Wall可用sandbox.o: CFLAGS -O0并避免全局写成-O2与-O0叠出冲突按需选择:替换或调整全局。动手题给某个.o加目标特定CFLAGS用echo $(CFLAGS)对比其它文件。不 export / export 某变量对比配方中$(VAR)与$$VAR。在开源项目里搜: CFLAGS或: CXXFLAGS找一处目标特定变量实战。下一篇预告M04-01《字符串函数substpatsubstfilterfilter-out》— 用函数从变量「算出」列表与文件名。学完 M04-01你开始具备读「函数型」Makefile 的能力。