Всегда обрабатывать самое внешнее расширение файла (и попутно удалять расширения)

#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: используется по умолчанию для всех неявно «связанных» целей). Тем не менее, я признаю, что все ненавидят внимательно читать руководства 😉