构建产物放在单独目录时,目录只负责保证写入位置存在,不应参与产物的新旧比较。若把目录写成普通前置条件,目录内新增或删除文件会更新目录时间戳,可能让目标发生无效重建。GNU Make 可以用 | 把这类依赖声明为 order-only prerequisite:先确保它存在,但不因它变新而重建目标。
环境:GNU Make 4.3+;Linux 或 macOS;Bash;需要 mkdir、cp、touch。
创建 Makefile,注意命令行开头是 Tab:
build/app.txt: src/app.txt | build
cp $< $@
build:
mkdir -p $@
.PHONY: clean
clean:
rm -rf build
准备输入并运行:
mkdir -p src
printf 'version=1\n' >src/app.txt
make
sleep 1
touch build
make
sleep 1
touch src/app.txt
make
第一次 make 会依次创建 build 并复制文件。更新 build 目录自身的时间戳后,第二次 make 不会执行 cp;更新真正的输入 src/app.txt 后,第三次才会重新复制。sleep 1 用于兼容时间戳精度较低的文件系统。
关键是规则中的竖线:
build/app.txt: src/app.txt | build
竖线左侧是普通前置条件,任何一项比目标新都会触发重建;右侧只保证在目标配方前完成。若改成 build/app.txt: src/app.txt build,执行 touch build 后就会再次运行 cp。目录目标也不应声明为 .PHONY,否则它的创建配方会在每次构建时执行。