Makefile — несколько рецептов, вызывающих один, с переменными — запускается только первый

#makefile

#makefile

Вопрос:

Я настроил свой makefile, как показано ниже, чтобы минимизировать дублирование кода

Рецепты представляют собой набор блоков, которые задают переменную, а затем запускают рецепт sleeper_agent. Они отлично работают при вызове по отдельности, как make xlsx_sleeper например.

Но когда я вызываю all_sleepers , компилируется только первый (xlsx_sleeper).

Я пытался объявить их как поддельные ( .PHONY: all_sleepers xlsx_sleeper docx_sleeper pptx_sleeper pdf_sleeper png_sleeper ), что ничего не меняет

и добавление a .ПРИНУДИТЕЛЬНО применить правило к правилу sleeper_agent, в результате чего такого файла или каталога не будет:

 .FORCE:
sleeper_agent: .FORCE [...]
  

Вот мой makefile:

 all_sleepers: xlsx_sleeper docx_sleeper pptx_sleeper png_sleeper pdf_sleeper

sleeper_agent: $(OBJ)/sleeper_agent.o $(OBJ)/identities.o
    windres icons/$(ext)/resource.rc -O coff -o obj/$(ext).res
    $(CC) -o $(BIN)/sleeper_$(ext).exe $^ $(OBJ)/$(ext).res $(CFLAGS) $(LIBS)

xlsx_sleeper: ext=xlsx
xlsx_sleeper: sleeper_agent

docx_sleeper: ext=docx
docx_sleeper: sleeper_agent

pptx_sleeper: ext=pptx
pptx_sleeper: sleeper_agent

png_sleeper: ext=png
png_sleeper: sleeper_agent

pdf_sleeper: ext=pdf
pdf_sleeper: sleeper_agent
  

Комментарии:

1. Ваша ментальная модель make неверна: только строки в самом рецепте являются обязательными по своей природе (плюс также поток определений переменных с некоторыми оговорками). Базовая структура выполнения, то есть определение того, какой рецепт когда выполнять, не является обязательной, это модель потока графа: цели — это узлы, а предварительные условия определяют ребра в этом графике до следующего (более раннего) узла. Узел обновляется не более одного раза при запуске make, а именно, когда его более ранние узлы моложе его самого. Узлы являются «глобальными» на этом графике, после посещения узел больше не рассматривается при том же запуске.

Ответ №1:

Ваша проблема в том, что make не видит никаких причин, по которым он должен перестраивать sleeper_agent цель несколько раз. Вероятно, вам следует придерживаться философии make:

  1. Постарайтесь использовать реальные файлы в качестве целевых ( $(BIN)/sleeper_xlsx.exe ).
  2. Используйте только поддельные (не файловые) цели:
    • Чтобы присвоить символические имена другим целям или группам целей ( all_sleepers , xlsx_sleeper , …)
    • Для правил, которые не создают файлы ( clean , help …)
  3. Объявляйте фальшивые цели как таковые ( .PHONY: ... )

Пример использования статических правил шаблона, автоматических переменных и patsubst функции make:

 SLEEPER  := xlsx docx pptx png pdf
EXE      := $(patsubst %,$(BIN)/sleeper_%.exe,$(SLEEPER))
SHORT    := $(patsubst %,%_sleeper,$(SLEEPER))

.PHONY: all_sleepers $(SHORT) clean_sleepers

all_sleepers: $(EXE)

$(SHORT): %_sleeper: $(BIN)/sleeper_%.exe

$(EXE): $(BIN)/sleeper_%.exe: $(OBJ)/sleeper_agent.o $(OBJ)/identities.o
    windres icons/$*/resource.rc -O coff -o obj/$*.res
    $(CC) -o $@ $^ $(OBJ)/$*.res $(CFLAGS) $(LIBS)

clean_sleepers:
    rm -f $(EXE)
  

И тогда вы сможете запустить:

 make all_sleepers
  

чтобы собрать их все или:

 make xlsx_sleeper
  

для сборки только одного из них. EXE это список реальных исполняемых файлов, а правило статического шаблона объясняет, как их создавать. В своем рецепте $* автоматическая переменная расширяется как строка, соответствующая % подстановочному знаку. SHORT это список xxxx_sleeper ярлыков, и другое правило статического шаблона объясняет для каждого из них, какому реальному исполняемому файлу оно соответствует. all_sleepers и xxxx_sleeper ярлыки (плюс clean_sleepers тот, который я добавил в качестве примера) правильно объявлены как поддельные, потому что таких реальных файлов нет.