в зависимости от каталогов в make

Это продолжение моего предыдущего вопроса: SO 4403861, потому что предложенные решения нарушили зависимости, сделав makefile бесполезен. Я не могу понять, почему.

Я использую gnu make 3.82. У меня есть правило, которое работает, если каталог obj создан:

objdir:=../obj
$(objdir)/%.o: %.C
    $(COMPILE) -MM -MT$(objdir)/$(notdir $@) $< -o $(DEPDIR)/$(notdir $(basename $<).d )
    $(COMPILE) -o $(objdir)/$(notdir $@ ) -c $<

Однако, если каталог obj отсутствует, make завершается ошибкой. Я хотел, чтобы make автоматически создавал ../obj по запросу, поэтому я добавил то, что мне казалось очень простым:

$(objdir)/%.o: %.C $(objdir)
    $(COMPILE) -MM -MT$(objdir)/$(notdir $@) $< -o $(DEPDIR)/$(notdir $(basename $<).d )
    $(COMPILE) -o $(objdir)/$(notdir $@ ) -c $<

$(objdir):
   if [ ! -d $(objdir) ] ; then mkdir $(objdir) ; fi

Когда я это делаю, make всегда принудительно компилирует каждый раз. Почему? mkdir не должно происходить, если нет каталога? Почему зависимости уничтожаются этим простым изменением?


person Dov    schedule 14.12.2010    source источник
comment
Как я прокомментировал в ответ на ваш вопрос/комментарий к моему ответу на 4403861: Ваше правило гласит, что объектный файл зависит от объектного каталога, что означает, что если объектный каталог изменился с момента последнего создания объектного файла (для например, потому что вы скомпилировали другой файл), то ваш файл необходимо перестроить. Ваша общая цель сборки зависит от существующего каталога объектов; отдельные объектные файлы не нужно перестраивать только потому, что каталог изменился.   -  person Jonathan Leffler    schedule 15.12.2010


Ответы (4)



Как уже говорили другие, проблема заключается в том, что каталог считается «измененным» всякий раз, когда члены каталога добавляются или удаляются, поэтому make видит, что ваш выходной каталог постоянно меняется, и повторно запускает все компиляции, которые вы сказали, зависит от выходного каталога .

В качестве альтернативы обходным путям, описанным другими, в последних версиях GNU make есть поддержка «предварительных требований только для заказа». Описание предварительных требований только для заказа в руководстве включает пример того, как их использовать для создания каталогов.

person slowdog    schedule 14.12.2010
comment
Я не думаю, что это совершенно верно. Каталог изменяется при добавлении, удалении или переименовании любого из его членов. (Каталог — это файл, содержащий сопоставление имен файлов с файлами.) - person reinierpost; 15.12.2010
comment
Я объясню это здесь: stackoverflow.com/ вопросы/3477418/ - person Jack Kelly; 15.12.2010
comment
Reinierpost: спасибо, поправил ответ. - person slowdog; 15.12.2010

Какой-нибудь дозорный файл отлично решит эту проблему. Позволяет написать одно правило шаблона для создания всех папок, не имеет проблем с перекомпиляцией и -j безопасна.

OBJDIR := objdir/
OBJS := $(addprefix ${OBJDIR},foo.o bar.o baz.o)

all: ${OBJS}

${OBJDIR}%.o : %.c ${OBJDIR}.sentinel
     ${COMPILE.c} ${OUTPUT_OPTION} $<

# A single pattern rule will create all appropriate folders as required
.PRECIOUS: %/.sentinel # otherwise make (annoyingly) deletes it
%/.sentinel:
     mkdir -p ${@D}
     touch $@
person bobbogo    schedule 06.01.2011

Как уже было сказано, вы не можете поставить каталог с выходными файлами в качестве зависимости, потому что он каждый раз обновляется.

Одно из решений (изложенное выше) — поставить mkdir -p перед каждой сборкой, например:

objdir:=../obj
$(objdir)/%.o: %.C
    @mkdir -p $(objdir) $(DEPDIR) ...
    $(COMPILE) -MM -MT$(objdir)/$(notdir $@) $< -o $(DEPDIR)/$(notdir $(basename $<).d )
    $(COMPILE) -o $(objdir)/$(notdir $@ ) -c $<

Но мне не нравится это решение, потому что оно заставляет запускать дополнительный процесс много-много раз. Однако, зная причину ошибки, есть другой способ:

exe: dirs $(OBJ)

В основной цели, которая создает исполняемый файл, просто вставьте целевые каталоги перед объектами. Так как он попал по порядку:

dirs:
    @mkdir -p $(objdir) $(DESTDIR) ...

и это делается только один раз для всей сборки, а не один раз для каждого файла.

person Dov    schedule 14.12.2010
comment
Это малоизвестная неприятность, но вы не можете использовать mkdir -p в параллельном make. Причина в том, что если папки /a/b/c и /a/b/cc создаются параллельно, возникает состояние гонки между тем, когда mkdir -p проверяет существование каталога /a и создает его. Другой процесс (сделать задание) может создать каталог между проверкой и действием, так что первое задание завершается с ошибкой EEXIST при создании каталога. - person Maxim Egorushkin; 05.01.2011
comment
И зависимости от каталогов должны быть только по порядку, как они уже упоминали. - person Maxim Egorushkin; 05.01.2011
comment
Как уже было сказано, это решение не работает под -j. Вы можете использовать mkdir -p, но вы должны правильно установить зависимости. По сути, вы должны сделать так, чтобы все объекты зависели от каталога. Кроме того, чтобы остановить перекомпиляцию из-за времени изменения каталога, используйте файл-страж (и правило шаблона). См. ниже (я не могу ввести решение в это поле, грррр). - person bobbogo; 06.01.2011
comment
С тем, что вы опубликовали, нет гарантии, что цель dirs будет обработана до $(OBJ) или что они не произойдут одновременно (таким образом, dirs будут созданы, но $(OBJ) провал, так как его не было). - person iheanyi; 30.04.2014