Странное поведение make с правилом

У меня есть правило, которое создает файл, а другое - копирует его. Но при запуске make предварительные условия правила вызываются дважды, но я не знаю почему!

Если у меня есть $ (shell ./myscript) в предварительном требовании вместо фактического $ (warning ...), сценарий вызывается дважды!

Makefile, воспроизводящий ошибку:

NAME := test

$(NAME):
    @touch $@

install-$(NAME): $(NAME) install-copy-$(NAME)

.PHONY: install-$(NAME)
.PRECIOUS: %.copy

.SECONDEXPANSION:

%.copy: $$*
    @cp $< $@

install-copy-%: $$(warning START) $$*.copy $$(warning END)
    @echo done

Фактический выход:

make: START
make: START
make: END
done

Ожидаемый результат:

make: START
make: END
done

Почему СТАРТ выставляется дважды? Если у меня есть функция, которая вызывает сценарий вместо $ (предупреждения), сценарий вызывается дважды.

Понятия не имею ... Я хочу сохранить .SECONDEXPANSION, потому что я не хочу, чтобы $ (сценарий оболочки ...) или $ (предупреждение ...) вызывалось, когда правило не вызывается. Я не могу перечислить все файлы, которые будут установлены в .PRECIOUS, потому что я не знаю, поскольку этот файл создается внешним скриптом. (как doxygen). С .PRECIOUS:% .copy в первый раз START выводится дважды, но как только файл существует, START выводится один раз ...

Спасибо!

Makefile с реальным случаем:

NAME := test
INSTALL_DIR := saved

# generate the documentation into $$*/
generateDoc-%:
    touch $*/$(shell head /dev/urandom | tr -dc A-Za-z0-9 | head -c 13 ; echo '')

install-$(NAME): generateDoc-$(NAME) install-doc-$(NAME)

.PHONY: install-$(NAME)
.PRECIOUS: $(INSTALL_DIR)/%

.SECONDEXPANSION:

$(INSTALL_DIR)/%: $$(NAME)/$$*
    cp $< $@

install-doc-%: $$(warning START) $$(subst $$*,$$(INSTALL_DIR),$$(shell find $$* -type f)) $$(warning END)
    @echo done

person dj4567    schedule 09.10.2019    source источник
comment
Я не знаю, но помещать warning утверждения в список предварительных требований - это очень странно.   -  person Beta    schedule 09.10.2019
comment
да, это просто для того, чтобы показать, что предварительные требования читаются дважды ... Вместо этого предупреждения у меня есть команда оболочки для вывода списка файлов, которые мне нужно установить. Эта команда дорогостоящая, и я не хочу запускать ее дважды.   -  person dj4567    schedule 09.10.2019
comment
если doxygen создает файлы в / usr / doc, то shell ls $$*/documentation не найдет файлов для копирования оттуда   -  person Alex Cohn    schedule 10.10.2019
comment
Нет, сначала мы создаем документацию с помощью generateDoc-%: он генерирует файл документации в test / documentation. Затем мы вызываем install-doc-%, который устанавливает документацию в / usr / doc: ls test / documentation найдет все сгенерированные файлы.   -  person dj4567    schedule 10.10.2019
comment
Но цель `$ (NAME) / documentation /%` актуальна, cp test/documentation/% до /usr/doc не произойдет.   -  person Alex Cohn    schedule 11.10.2019
comment
вам необходимо иметь doxygen на вашем компьютере для создания документации, и вам нужны файлы с документацией doxygen. Вот почему я даю вам первый Makefile, потому что на самом деле вам нужно больше материала.   -  person dj4567    schedule 11.10.2019
comment
Я обновил свой вопрос, добавив новый Makefile, который воспроизводит реальное поведение случая без необходимости использования doxygen. Вам просто нужно запустить make install-test, и вы увидите! Спасибо и извините за удобство.   -  person dj4567    schedule 11.10.2019


Ответы (1)


make -d объясняет, что после создания цели test он пытается сделать цель install-copy-test (которая вызывает первую пару START / END). После этого ищет правило с промежуточным файлом test.copy.

Все это при условии, что вы запустите make install-test

> make install-test -n
touch test
make: START
make: END
make: START
make: END
cp test test.copy
done
rm test.copy

Естественным решением было бы указать

.PRECIOUS: $(NAME).copy
person Alex Cohn    schedule 10.10.2019
comment
Я не могу добавить .copy в PRECIOUS, я могу просто добавить PRECIOUS:% .copy, но не работает. (работает только тогда, когда файл уже существует, но не в первый раз). Я не могу добавить все имя .copy, потому что его файл сгенерирован внешним скриптом (doxygen). Итак, я запускаю команду doxygen, затем запускаю $ (shell find в каталоге doxygen, чтобы получить все файлы для установки). Итак, решение не работает :( - person dj4567; 10.10.2019
comment
Вы можете поставить .PRECIOUS: $(NAME).copy даже перед .SECONDEXPANSION: (на самом деле, также $(NAME): можно переместить выше). - person Alex Cohn; 10.10.2019
comment
Я имел в виду $(NAME).copy, а не %.copy! - person Alex Cohn; 10.10.2019
comment
Да, но я не могу. Это просто пример для воспроизведения моего случая. В моем реальном случае это не $ (NAME) .copy, а весь файл, созданный doxygen. Я не могу добавить в .PRECIOUS все имя файла, которое сгенерирует doxygen. Вы понимаете? - person dj4567; 10.10.2019
comment
Давайте попробуем подготовить простой пример, который лучше представляет ваш вариант использования. Кроме того, нужно ли вообще хранить файлы .copy? Может, хорошо, что make их в итоге убирает? - person Alex Cohn; 10.10.2019
comment
Я обновил свой вопрос. Файл необходимо сохранить, поскольку этот make используется для документации по установке. - person dj4567; 10.10.2019