带有条件生成文件的 makefile

makefile with conditionally generated file

我如何向 make 传达脚本可以有条件地修改文件,以便 make 等待 stat 所有文件,直到该脚本完成 运行 ?


考虑以下玩具示例系列文件:

// version
2
// foo.h - note the space between the 1 and the semicolon
static constexpr int VERSION = 1 ;
//update.py
import sys 

with open(sys.argv[1]) as f:
    cur = int(f.readlines()[0].split()[5])

with open('version') as f:
    exp = int(f.readlines()[0].strip())

if cur < exp:
    with open(sys.argv[1], 'w') as f:
        print >> f, 'static constexpr int VERSION = {} ;'.format(exp)
// main.cxx
#include "foo.h"
#include <iostream>

int main() {
    std::cout << VERSION << std::endl;
}
// makefile
all : a.out updater

a.out : main.o
    g++ -o a.out main.o

main.o : main.cxx
    g++ -std=c++11 -o main.o -c main.cxx -MP -MMD -MF main.d

.PHONY : updater
updater :
    python update.py foo.h

-include main.d

这里的目的是我有一个文件 foo.h,它 有条件地 由脚本 update.py 更新(如果你增加数量在 version 中,foo.h 将被更新——否则什么也不会发生)。我想以某种方式向 make 传达这个概念 - 它不是 stat foo.h 以确定是否需要重新制作 main.o 直到 update.py完成 运行。我如何确保订购?

注意:version这里只是一个占位符,用于表示一系列复杂的先决条件,不容易表达。简单地添加 foo.h : version 不是解决方案。

如果您想要在且仅当脚本修改 foo.h 时重建 main.oa.out,这可能是 递归 Make 的工作:

all : updater
    @$(MAKE) a.out                      

...

main.o : main.cxx foo.h
   ...

首先,如果您不想让 make 更新 main.o 等,直到 foo.h 更新之后,您必须将 foo.h 列为先决条件:

main.o : main.cxx foo.h
        g++ -std=c++11 -o main.o -c main.cxx -MP -MMD -MF main.d

(虽然我当然不会这样写,但我总是会这样使用变量:

CXX = g++
CXXFLAGS = -std=c++11
DEPFLAGS = -MP -MMD -MF $*.d

main.o : main.cxx foo.h
        $(CXX) $(CXXFLAGS) $(CPPFLAGS) $(DEPFLAGS) -o $@ -c $<

但这就是我...)

其次,你必须有一个规则来更新foo.h。正如你所建议的,它应该是 .PHONY 所以它总是 运行,即使它并不总是更新 foo.h:

.PHONY: updater
updater:
        python update.py foo.h

现在您必须连接这两个规则,因此您必须创建一个将 foo.h 链接到 updater 的规则:

foo.h : updater ;

注意这里的分号,那是关键。它的工作方式是这样的:updater 是假的,所以它总是 运行,这意味着任何依赖它的目标总是 运行。很明显,您不能列出 main.o : main.cxx updater,因为那样每次都会重建 main.o

所以你可以引入一个中间规则,foo.h : updater。无论如何你都需要这样做,因为你需要将 foo.h 列为先决条件,所以你需要一个目标来构建它。

然而,这本身并没有帮助,因为 make 发现没有构建 foo.h 的方法,所以它没有意识到当 updater 是 运行 时它构建了foo.h。您可以通过为 foo.h 创建一个空配方来解决这个问题;这就是 ; 的用途:

foo.h : updater ;

为这个目标foo.h创建一个空配方,这足以让make检查文件foo.h是否被修改。当它被修改(updater)时,main.o 将被重建。未修改时,main.o 不会重建(除非有其他原因重建它)。