В make есть только один способ заставить команду исполниться до того, как будет создана цель X, но только в том случае, если нужно построить цель X, и это помещает команду в первую очередь в рецепте для цели X.способ манипулировать графом зависимостей в GNU make так, чтобы make определял, нужно ли создавать цель, и, если это так, сначала строит какую-то другую цель перед запуском рецепта.
Так что вам определенно придется использовать рекурсивный makeздесь, поместив команду для построения цели pre
в рецептуру других целей.Однако, конечно, это приведет к тому, что он будет построен несколько раз, что вам не нужно.
Один из способов обойти это - использовать трюк, используя eval
.Попробуйте это (не проверено):
BUILD_PRE = $(shell $(MAKE) -j1 pre >/dev/null)
post: a b c
echo $@
pre:
echo $@
a b c:
$(BUILD_PRE)$(eval BUILD_PRE =)
touch $@
.PHONY: pre post
В правиле для a
, b
и c
мы сначала расширяем переменную BUILD_PRE
, что приводит к рекурсивному вызову make через shell
вызов.Затем расширение eval
сбросит значение BUILD_PRE
, так что теперь оно пустое;это означает, что в последующих правилах для b
и c
эта первая строка будет расширена до пустой строки, и pre
больше не будет выполняться.
Вы можете спросить, зачем нам нужно использовать shell
Вот?Разве мы не можем просто использовать:
BUILD_PRE = $(MAKE) -j1 pre
, чтобы первый рецепт содержал рекурсивную марку?Проблема в том, что он не будет работать с параллельным make.Предположим, что первая цель, которая пытается построить, - a
(это всегда будет, конечно).Этот рецепт будет содержать рекурсивный вызов make и make его запустит.Но если вы используете -j
, make не ждет завершения этого рецепта: он попытается запустить b
и c
.Поскольку BUILD_PRE
теперь пусто, вы получаете только одну сборку pre
, но b
и c
не ожидают завершения pre
.
Используя функцию shell
,рекурсивный вызов принудительно завершается при расширении рецепта, прежде чем будет запущен любой другой рецепт.
Должен сказать, я подозреваю, что в этом могут быть некоторые странные вещи.В частности, когда make обычно вызывает рекурсивную сборку, он выполняет некоторые настройки и т. Д., Что не произойдет, если рекурсивная сборка вызывается через shell
.Но это должно работать.
Редактировать: Окончательный Makefile с префиксом '+', чтобы пометить рекурсивные вызовы make:
all: allabc
BUILD_PRE = $(shell $(MAKE) pre)
BUILD_POST =
pre:
@echo PRE abc >&2
post:
@echo POST abc >&2
allabc: a b c
@+$(BUILD_POST) > /dev/null
a:
+$(BUILD_PRE)$(eval BUILD_PRE = )
touch "$@"
$(eval BUILD_POST = $$(MAKE) post)
b:
+$(BUILD_PRE)$(eval BUILD_PRE = )
touch "$@"
$(eval BUILD_POST = $$(MAKE) post)
c:
+$(BUILD_PRE)$(eval BUILD_PRE = )
touch "$@"
$(eval BUILD_POST = $$(MAKE) post)
clean:
rm -f a b c