Make中filter-out语句为何未达预期?(make 3.81)
关于Makefile中filter-out语句的问题
问题场景
我正在阅读make手册解析现有Makefile,卡在了包含filter-out的语句:
KERNEL_ELF_DEPS = $(filter-out %: ,$(file < $(KERNEL_ELF).d)) $(KERNEL_MANIFEST) $(LAST_BUILD_CONFIG)
其中KERNEL_ELF = target/aarch64-unknown-none-softfloat/release/kernel,因此filter-out部分实际为:
$(filter-out %: ,$(file < target/aarch64-unknown-none-softfloat/release/kernel.d))
我原本认为这段代码会读取target/aarch64-unknown-none-softfloat/release/kernel.d文件,过滤掉所有以: 结尾的字符串。该文件内容如下:
/Users/simgar/projects/rust-raspberrypi-OS-tutorials/01_wait_forever/target/aarch64-unknown-none-softfloat/release/kernel: /Users/simgar/projects/rust-raspberrypi-OS-tutorials/01_wait_forever/build.rs /Users/simgar/projects/rust-raspberrypi-OS-tutorials/01_wait_forever/src/_arch/aarch64/cpu/boot.rs /Users/simgar/projects/rust-raspberrypi-OS-tutorials/01_wait_forever/src/_arch/aarch64/cpu/boot.s /Users/simgar/projects/rust-raspberrypi-OS-tutorials/01_wait_forever/src/bsp/raspberrypi/kernel.ld /Users/simgar/projects/rust-raspberrypi-OS-tutorials/01_wait_forever/src/bsp/raspberrypi.rs /Users/simgar/projects/rust-raspberrypi-OS-tutorials/01_wait_forever/src/bsp.rs /Users/simgar/projects/rust-raspberrypi-OS-tutorials/01_wait_forever/src/cpu/boot.rs /Users/simgar/projects/rust-raspberrypi-OS-tutorials/01_wait_forever/src/cpu.rs /Users/simgar/projects/rust-raspberrypi-OS-tutorials/01_wait_forever/src/main.rs /Users/simgar/projects/rust-raspberrypi-OS-tutorials/01_wait_forever/src/panic_wait.rs
我预期得到除第一个路径外的所有内容,但实际结果为空。请问正确预期是什么?我的推理哪里出错了?(注:make版本为3.81)
问题分析与解决
1. 核心问题:file函数不兼容你的make版本
file函数是GNU Make 4.0及以上版本才新增的特性,而你使用的是3.81版本。在3.81中,$(file < ...)会被当作未定义的变量展开,结果是空字符串。因此整个filter-out处理的是空内容,最终输出自然为空。
2. 过滤规则的逻辑偏差
即便使用支持file函数的高版本make,你的过滤规则%: 也无法达到预期:
- Make的通配符
%匹配的是空格分隔的单个单词,文件里的第一个元素是/path/to/kernel:(冒号后是空格分隔符,不属于这个单词本身) - 你写的
%:试图匹配以:结尾的字符串,但实际上第一个单词是以:结尾,正确的过滤规则应该是%:(不带空格)
3. 适配make 3.81的正确写法
在3.81中,要读取.d文件并提取依赖列表,可以借助shell函数调用sed命令:
KERNEL_ELF_DEPS = $(shell sed 's/^[^:]*: //' $(KERNEL_ELF).d) $(KERNEL_MANIFEST) $(LAST_BUILD_CONFIG)
sed 's/^[^:]*: //'会删除行首到第一个: 的所有内容,直接保留后面的依赖文件列表,这是make 3.81完全支持的方式。
如果后续升级到GNU Make 4.0+,修正过滤规则后的写法如下:
KERNEL_ELF_DEPS = $(filter-out %:,$(file < $(KERNEL_ELF).d)) $(KERNEL_MANIFEST) $(LAST_BUILD_CONFIG)
去掉%: 中的空格,就能正确匹配带冒号的目标文件名单词,过滤后得到预期的依赖列表。
内容的提问来源于stack exchange,提问作者simgar
相关产品推荐
相关产品推荐

