#makefile #gnu-make
#makefile #gnu-make
Вопрос:
У меня есть куча разных исходных файлов в моем статическом HTML-блоге. Самые внешние расширения объясняют формат, который будет обработан следующим.
Пример: Исходный файл article.html.md.gz
(с целевым article.html
файлом) должен быть обработан gunzip
, затем моим процессором markdown.
Дополнительная информация:
- Порядок расширений может отличаться
- Иногда расширение не используется (
article.html.gz
) - Я знаю, как обрабатывать все различные расширения
- Я знаю, что окончательная форма всегда
article.html
В идеале я бы хотел просто написать правила следующим образом:
...
all-articles: $(ALL_HTML_FILES)
%: %.gz
gunzip ...
%: %.md
markdown ...
%: %.zip
unzip ...
И давайте make
определим, какой путь выбрать, основываясь на последовательности расширений.
Однако из документации я понимаю, что существуют ограничения на соответствие всем правилам, и вышеуказанное невозможно.
Каков наилучший способ продвижения вперед? Можно make
вообще справиться с этой ситуацией?
Расширения являются составными примерами. Мои фактические исходные файлы имеют больше смысла 🙂
Комментарии:
1. Сколько расширений может иметь файл максимум?
2. Я думаю, четыре. Возможно, 5 в будущем. Я обрабатываю статьи для уценки, подсветки синтаксиса, дорисовки html, создания шаблонов и выбираю для каждого процессора, добавляя соответствующее расширение к имени файла.
Ответ №1:
Я в отпуске, так что я откушу.
Я не поклонник шаблонных правил, они слишком ограничены и в то же время слишком произвольны на мой вкус. Вы можете добиться желаемого довольно хорошо в чистом make:
.DELETE_ON_ERROR:
all: # Default target
files := a.html.md.gz b.html.gz
cmds<.gz> = gzip -d <$< >$@
cmds<.md> = mdtool $< -o $@
define rule-text # 1:suffix 2:basename
$(if $(filter undefined,$(flavor cmds<$1>)),$(error Cannot handle $1 files: [$2$1]))
$2: $2$1 ; $(value cmds<$1>)
all: $2
endef
emit-rule = $(eval $(call rule-text,$1,$2))# 1:suffix 2:basename
emit-hierachy = $(if $(suffix $2),$(call emit-rule,$1,$2)$(call emit-hierachy,$(suffix $2),$(basename $2)))# 1:suffix 2:basename
emit-rules = $(foreach _,$1,$(call emit-hierachy,$(suffix $_),$(basename $_)))# 1:list of source files
$(call emit-rules,${files})
.PHONY: all
all: ; : $@ Success
Ключевым моментом здесь является настройка $files
на ваш список файлов.
Затем этот список передается в emit-rules.
emit-rules передает каждый файл по одному в emit-hierachy.
emit-hierachy удаляет каждое расширение по очереди, генерирует соответствующий синтаксис make, к которому он переходит $(eval …)
. иерархическая обработка продолжается до тех пор, пока у файла не останется только одно расширение.
Таким образом, a.html.md.gz
становится этот синтаксис make:
a.html.md: a.html.md.gz ; gunzip <$< >$@
a.html: a.html.md ; mdtool $< -o $@
all: a.html
Аналогично, b.html.gz
становится:
b.html: b.html.gz ; gunzip <$< >$@
all: b.html
Аккуратно, что ли?
Если вы передаете emit-rules файл с нераспознанным расширением ( c.html.pp
скажем), вы получаете ошибку во время компиляции:
1:20: *** Cannot handle .pp files: [c.html.pp]. Stop.
Время компиляции? Да, перед выполнением любых команд оболочки.
Вы можете указать make, как обрабатывать .pp
файлы, определив cmds<.pp>
🙂
Для дополнительных очков это также безопасно для параллельного использования. Таким образом, вы можете использовать -j9
на своем ноутбуке с 8 процессорами и -j33
на своей рабочей станции с 32 процессорами. Современная жизнь, да?
Комментарии:
1. Да, это здорово. По сути, вы генерируете весь make-файл (результат без правил шаблона) перед обработкой.
2. Да, имхо, это прекрасный шаблон. Кажется, работает хорошо везде, избегая всех неприятных моментов make . Это менее ограничительно и менее произвольно 🙂
3. Я проголосовал за (очевидно), но, честно говоря, это кажется немного взломанным. В «обычном» файле make я могу легко понять структуру (зависимости и то, как будут обрабатываться файлы). Я думаю, здесь тоже все ясно, с
cmds<...>
синтаксисом, но совершенно иным способом, чем тот, к которому я привык. Может быть, потому, что я занимаюсь make всего неделю. Кроме того, «слишком ограниченный и в то же время слишком произвольный одновременно» — не могу не согласиться.4. Не часто встречается да, я бы согласился. Но халтурно? нет. Я бы назвал это довольно надежной и промышленной мощью. Я использовал его в некоторых довольно крупных системах сборки. Возможность добавить несколько приличных ранних утверждений, используя
$(error …)
, стоит одной цены входа.5. @aioobe И это после того, как вы только что ссылались на соответствующий раздел из руководства (10.4) : . ПРОМЕЖУТОЧНЫЙ: создается заново только при необходимости, автоматически удаляется; .ВТОРИЧНЫЙ: создается заново только при необходимости, не удаляется автоматически (очевидно, .INTERMEDIATE: используется по умолчанию для всех неявно «связанных» целей). Тем не менее, я признаю, что все ненавидят внимательно читать руководства 😉