2016-04-28 3 views
0

Как передать make, что скрипт может условно изменить файл, так что make ждет до stat всех файлов, пока этот скрипт не завершится?makefile с условно сгенерированным файлом


Рассмотрим следующий пример игрушечный ряд файлов:

// 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 как-то - это не statfoo.h, чтобы определить, нужно ли его переделать main.o до update.py заканчивается. Как я могу обеспечить заказ?

Примечание: version здесь просто заполнитель для сложной серии предпосылок, которые нелегко выразить. Просто добавление foo.h : version не является решением проблемы.

+1

'foo.h: updater' возможно? Или даже просто сделайте рецепт для 'updater' рецепта для' foo.h'. – user657267

+0

@ user657267 Это не работает. – Barry

+0

Извините, что я ошибался в том, чтобы переместить рецепт на 'foo.h', но создав' foo.h' цель 'main.o' и' updater', цель 'foo.h' должна работать. – user657267

ответ

1

Во-первых, если вы не хотите сделать, чтобы обновить 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 

(Altho тьфу, конечно, я никогда бы не написать это, я бы всегда использовать переменные, как это:

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

main.o : main.cxx foo.h 
     $(CXX) $(CXXFLAGS) $(CPPFLAGS) $(DEPFLAGS) -o [email protected] -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 в качестве предварительного условия, поэтому вам нужна цель для его создания.

Однако само по себе это не поможет, потому что видит, что делают там нет никакого рецепта, чтобы построить foo.h, так что не понимает, что, когда updater был запущен он построен foo.h. Вы обойдете это, создав пустой рецепт для foo.h; это то, что ; для:

foo.h : updater ; 

, который создает пустой рецепт для этой цели foo.h и этого достаточно, чтобы сделать, чтобы проверить, является ли файл foo.h был изменен или нет. Когда он будет изменен (на updater), тогда main.o будет перестроен. Когда он не будет изменен, main.o не будет восстановлен (если нет другой причины для его восстановления).

+0

Точка с запятой делает всю разницу! Вы можете удалить первую часть ответа, prereq из 'foo.h' обрабатывается' -include main.d' – Barry

+0

Это не достаточно хорошо для сгенерированных файлов, потому что при первом запуске 'make' там не будет быть любыми '.d' файлами, поэтому make не будет знать, что что-то зависит от' foo.h', поэтому 'foo.h' не будет создан, тогда вы получите ошибку компиляции, пытаясь скомпилировать и построить первый '.d'. Если вы не говорите, что 'foo.h' всегда существует, но может также автоматически обновляться ...? Это немного ... странно ... обычно люди, использующие системы SCM, не любят проверять автоматически обновляемые файлы. Кроме того, даже тогда он не будет пытаться обновить 'foo.h' в первый раз. – MadScientist

+0

Это очень хороший момент, спасибо. – Barry

2

Если вы хотите восстановить main.o и a.out тогда и только тогда, когда сценарий изменяет foo.h, это может быть работа для рекурсивной Марка:

all : updater 
    @$(MAKE) a.out      

... 

main.o : main.cxx foo.h 
    ... 
+0

Это работает (+1), но я хотел бы избежать рекурсивного создания здесь. – Barry

Смежные вопросы