Общая зависимость Makefile запускается только один раз - PullRequest
1 голос
/ 04 марта 2020

TL; DR

Зависимость, общая для нескольких целей, вызывается только один раз.

Почему?

Сводка

В этом крошечном примере Makefile я ожидаю deploy-everywhere для развертывания staging-repo кода для подготовки и prod-repo кода для prod .

Вместо этого подготовки и prod get staging-repo код, который, по общему признанию, неоптимальный.

Это, кажется, происходит, потому что clone-repo пропускается / сокращается при повторном рассмотрении, как часть deploy-to-prod (см. -d вывод журнала ниже).

Почему это происходит, и можно ли изменить это поведение?

$ cat Makefile

.PHONY: deploy-everywhere deploy-to-prod deploy-to-staging clone-repo

deploy-everywhere: deploy-to-staging deploy-to-prod
    @echo "deployed everywhere"

deploy-to-prod: repo="prod-repo"
deploy-to-prod: clone-repo
    @echo "deployed to prod (from $(repo))"

deploy-to-staging: repo="staging-repo"
deploy-to-staging: clone-repo
    @echo "deployed to staging (from $(repo))"

clone-repo:
    @echo "clone-repo $(repo)"

Подробнее (журналы, версия)

Вот результат работы make:

$ make deploy-everywhere
clone-repo staging-repo
deployed to staging (from staging-repo)
deployed to prod (from prod-repo) <<<<< THIS IS A LIE, WE NEVER CLONED THE PROD REPO!!
deployed everywhere <<<< RIIIIGHT.

Дополнительная регистрация:

$ make -d deploy-everywhere
...
  Successfully remade target file `deploy-to-staging'.
  Considering target file `deploy-to-prod'.
   File `deploy-to-prod' does not exist.
    Pruning file `clone-repo'.          <<<< WHY? WHY?!?!?!?! WHY.
   Finished prerequisites of target file `deploy-to-prod'.
  Must remake target `deploy-to-prod'.
...

Я использую GNU Make 3.81:

$ make --version
GNU Make 3.81
Copyright (C) 2006  Free Software Foundation, Inc.
This is free software; see the source for copying conditions.
There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE.

This program built for i386-apple-darwin11.3.0

Ответы [ 2 ]

1 голос
/ 04 марта 2020

Чтобы добавить некоторое объяснение к решению Беты: Программный c контракт правила make состоит в том, что это непрозрачное обновление постоянного объекта в файловой системе, удовлетворяющее, таким образом, "меньше чем" (младше) отношение, заложенное в дереве зависимостей. Ваша конструкция OTOH скрывает зависимость от make, передавая ее в качестве параметра. Проблема, которую вы пытались обойти, заключается в том, что в make нет понятия «меньше» для сущностей, не представленных в файловой системе (в вашем случае, системы контроля версий). Единственный вопрос, который у меня возник, заключается в том, почему вы просто не добавили вызов clone к каждой цели deploy - если вы действительно хотите решить ее как зависимость "меньше, чем" (ie. Клонировать, только если локально хранилище не существует или более старое) требуется довольно большое make программирование.

РЕДАКТИРОВАТЬ после вашего первого комментария: Если я вас правильно понял, ваше представление о PHONY немного ошибочно (не полностью, просто немного). PHONY не предназначен для облегчения чего-либо в разных make прогонах, это способ иметь короткое замыкание в вашем дереве зависимостей. При заданной цепочке foo -> bar -> phony_baz (foo в зависимости от бара в зависимости от phony_baz) make в случае, если foo была главной целью, сначала посмотрите на bar, чтобы увидеть, старше ли он phony_baz, что всегда будет так. Это связано с тем, что само по себе phony_baz имеет make -явное свойство, которое всегда будет выдавать «младшее значение», когда оно сравнивается как предварительное условие с целью (а теперь важная часть :) при первом столкновении в дерево зависимостей . Все последующие столкновения с phony_baz в других ветвях дерева будут делать прямо противоположное, заканчивая оценку прямо там, на фиктивной цели. Это ничем не отличается от поведения любого другого узла в графе зависимостей - make предполагает, что контракт, который я отметил выше, выполнен, и никакие повторяющиеся действия не оправданы. Это объяснение того, что вы clone-repo не запускаете второй раз, хотя это .PHONY - ваша установка фактически представляет собой направленный ациклический c граф, в котором два ребра соединяются в одном узле (с которым make вполне подойдет, просто стоит отметить), а не дерево. Первое решение бета-версии - динамически создать два совершенно не связанных узла путем параметризации правила, тем самым возвращая фигуру дереву.

1 голос
/ 04 марта 2020

Это по замыслу. Цели в make-файле больше похожи на существительные, чем на глаголы.

Вы можете достичь желаемого несколькими способами. Вот один из них:

deploy-to-prod: clone-prod-repo
    @echo "deployed to prod (from prod-repo)"

deploy-to-staging: clone-staging-repo
    @echo "deployed to staging (from staging-repo)"

clone-%-repo:
    @echo "clone $*-repo"

РЕДАКТИРОВАТЬ: Хорошо, операция clone принимает несколько параметров. Вот один из способов сделать это:

deploy-to-prod: PARAM1=ProdOne
deploy-to-prod: PARAM2=ProdTwo
deploy-to-prod: clone-prod-repo
    @echo "deployed to prod (from prod-repo)"

deploy-to-staging: PARAM1=StagingFirst
deploy-to-staging: PARAM2=StagingSecond
deploy-to-staging: clone-staging-repo
    @echo "deployed to staging (from staging-repo)"

clone-%-repo:
    @echo clone $*-repo with $(PARAM1) and $(PARAM2)

А вот еще один:

define clone-repo
@echo clone using $(1)
@echo and $(2) in some way
endef

deploy-to-prod:
    @echo $@
    $(call clone-repo, ProdOne, ProdTwo)

deploy-to-staging:
    @echo $@
    $(call clone-repo, StagingFirst, StagingSecond)
...