Stati c шаблоны и условия с make - PullRequest
1 голос
/ 16 июня 2020

У меня проблема с GNU Make 4.2.1. Кажется, есть некоторое взаимодействие между синтаксисом stati c шаблонных правил и условных функций , которое я не совсем понимаю.

Контекст: у меня есть Проект задокументирован набором файлов Markdown, и я хотел бы отобразить эти файлы в HTML, чтобы проверить их локально. Структура каталогов должна выглядеть так:

some_project/
├── README.md       # entry page of documentation
├── doc/            # extra docs
│   ├── foo.md
│   ├── bar.md
│   └── ...         # and some more
└── doc_html/       # HTML rendering of the docs
    ├── Makefile    # the Makefile I am trying to write
    ├── index.html  # rendered from README.md
    ├── foo.html    # ............. doc/foo.md
    ├── bar.html    # ............. doc/bar.md
    └── ...         # etc.

Без особого случая index. html, я мог бы написать что-то вроде:

%.html: ../doc/%.md
    some list of commands $< $@

Проблема в том, что предварительное условие index. html (а именно ../README.md) не соответствует шаблону. Я хотел бы разобраться с этим частным случаем, не повторяя весь список команд. Вот что у меня есть на данный момент:

DOC_PAGES = $(wildcard ../doc/*.md)
TARGETS = index.html $(patsubst %.md,%.html,$(notdir $(DOC_PAGES)))

# Function to find the source for page $(1)
source = $(if $(findstring index.html,$(1)), \
    ../README.md, \
    $(patsubst %.html,../doc/%.md,$(1)) \
)

all: $(TARGETS)

$(TARGETS): %.html: $(call source,%.html)
    @echo some list of commands $< $@

# Check the TARGETS variable and the `source' function
test:
    @echo TARGETS = $(TARGETS)
    @echo "source(index.html)" = $(call source,index.html)
    @echo "source(foo.html)" = $(call source,foo.html)

Моя source функция, похоже, работает:

$ make test
TARGETS = index.html bar.html foo.html
source(index.html) = ../README.md
source(foo.html) = ../doc/foo.md

Однако она не работает должным образом в правиле stati c

$ make
make: *** No rule to make target '../doc/index.md', needed by 'index.html'.  Stop.

Обратите внимание, что правило работает, если я удалю index. html из $(TARGETS).

Представление о том, что я делаю неправильно?

Ответы [ 2 ]

3 голосов
/ 16 июня 2020

Эта строка:

$(TARGETS): %.html: $(call source,%.html)

не может работать, потому что вы расширяете макрос source аргументом литеральной строки %.html. Вы не можете использовать шаблоны или автоматические c переменные в макросах в списках обязательных требований: макросы раскрываются сначала перед анализом или расширением шаблонов.

Однако мне кажется, что вы: Мы делаем этот путь более сложным, чем нужно. Если большинство ваших целей построено одним способом, а некоторые - другим, тогда просто создайте шаблонное правило для «большинства» и напишите явные правила для «некоторых»:

%.html: ../doc/%.md
        some list of commands $< $@

index.html : ../README.md
        commands to build index.html

Если набор команд идентичен, и вы не хотите их повторять, поместите их в переменную:

create_html = some list of commands $< $@

%.html: ../doc/%.md
        $(create_html)

index.html : ../README.md
        $(create_html)

(обязательно создайте переменную с = не :=, если вы хотите включить $< и $@ в скрипте).

ETA Вы спросили, почему кажется, что все работает: когда make расширяет это, она заменяет буквальную строку %.html. Вы можете доказать это себе, добавив вызов $(info...), например:

source = $(info 1=$(1)) $(if $(findstring index.html,$(1)), \
    ../README.md, \
    $(patsubst %.html,../doc/%.md,$(1)) \
)

, и вы увидите, что он будет печатать (только один раз, потому что правило раскрывается только один раз) 1=%.html.

Что будет дальше? Это означает, что ваш макрос расширяется до этого:

$(if $(findstring index.html,%.html), ../README.md, $(patsubst %.html,../doc/%.md,%.html))

(опять же, с использованием буквальной строки %.html). findstring всегда возвращает пустое значение, потому что index.html не может быть найдено в строке %.html, поэтому вы расширяете предложение else:

$(patsubst %.html,../doc/%.md,%.html)

Очевидно, что %.html соответствует %.html с основа %, поэтому замена выполняется и возвращает ../doc/%.md. Итак, после всего этого ваше правило выглядит так:

$(TARGETS): %.html: ../doc/%.md
        @echo some list of commands $< $@

Это точно то же самое, что и у вас раньше с вашим простым шаблоном.

0 голосов
/ 16 июня 2020

Это могло бы быть решением:

DOC_PAGES := $(wildcard ../doc/*.md)
TARGETS := index.html $(patsubst %.md,%.html,$(notdir $(DOC_PAGES)))

all: $(TARGETS)

.INTERMEDIATE: ../doc/index.md
../doc/index.md: ../README.md
    cp $< $@

%.html: ../doc/%.md
    @echo some list of commands $< $@

test:
    @echo $(TARGETS)

С выводом:

$ make
cp ../README.md ../doc/index.md
some list of commands ../doc/index.md index.html
some list of commands ../doc/bar.md bar.html
some list of commands ../doc/foo.md foo.html
rm ../doc/index.md

index. html ищет ../doc/index.md через шаблон правило, ведущее к копированию рецепта ../README.md.

Предварительные требования к специальной цели .INTERMEDIATE удаляются после завершения make. Необязательно.

...