#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:
- Постарайтесь использовать реальные файлы в качестве целевых (
$(BIN)/sleeper_xlsx.exe
). - Используйте только поддельные (не файловые) цели:
- Чтобы присвоить символические имена другим целям или группам целей (
all_sleepers
,xlsx_sleeper
, …) - Для правил, которые не создают файлы (
clean
,help
…)
- Чтобы присвоить символические имена другим целям или группам целей (
- Объявляйте фальшивые цели как таковые (
.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
тот, который я добавил в качестве примера) правильно объявлены как поддельные, потому что таких реальных файлов нет.