Make: запускать общие правила до / после для набора целей - PullRequest
0 голосов
/ 08 октября 2018

Я хочу построить следующий граф зависимостей, но с pre и post без артефактов:

make dependency graph

Перед созданием / обновлениемлюбая из a, b или c команда pre должна выполняться один раз, а затем post должна выполняться один раз.И то, и другое не должно создавать артефакты.И, конечно, они должны запускаться только только , если любой из a b c изменил .Все это должно быть инициировано фиктивной all целью, т. Е. a никогда не запускается независимо.

Использование предварительных условий только для заказа a: | pre не помогает, потому что это всегда run,Заставить post зависеть от a b c не будет работать, потому что тогда он также запускается все время, потому что post не создает артефакт.

Если это невозможно и артефакты требуются в конце концов, какpre (более интересный из двух) будет работать только в том случае, если какая-либо из зависимых от него целей изменилась?

Примечание: a и т. д. являются обычными объектами makefile (который может быть вызван независимо), например:

a: a.in a.dependency
    @echo Creating a
    @mkabc a.in > a

Ответы [ 2 ]

0 голосов
/ 09 октября 2018

В 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
0 голосов
/ 08 октября 2018

Не уверен, что я понимаю все детали, но, если вы захотите построить 5 целей при вызове make all, с указанными зависимостями (и, возможно, a, b и c параллельно), вы можетенапример:

.PHONY: all pre post

all:
    $(MAKE) pre
    $(MAKE) a b c
    $(MAKE) post

pre:
    <pre-recipe>

post:
    <post-recipe>

a:
    <a-recipe>
...
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...