Мне кажется, я наконец понял вашу проблему.Если я правильно понял:
- У вас есть куча
*.mdwn
исходных файлов. - Вы генерируете
*.whatlinkshere
файлов из ваших *.mdwn
исходных файлов, используя утилиту backlinks.go
,Но эта утилита не выдает foo.whatlinkshere
из foo.mdwn
.Он анализирует foo.mdwn
, ищет в нем ссылки на другие страницы и для каждой найденной ссылки на bar
добавляет a [foo](foo.html)
ссылку на bar.whatlinkshere
. Из каждого foo.mdwn
исходного файла, который вы хотите создать соответствующий foo.html
файл с:
$ cmark foo.mdwn foo.whatlinkshere
Ваше правило:
$(LINKFILES): $(INFILES)
@echo Creating backlinks $@
@touch $@
@go run backlinks.go $<
содержитодна ошибка и имеет несколько недостатков.Ошибка заключается в использовании автоматической переменной $<
в рецепте.Это расширяется как первая предпосылка, которая, вероятно, всегда pageA.mdwn
в вашем случае.Не то, что вы хотите.$^
расширяется как все предпосылки, но это не правильное решение, потому что:
- ваша утилита go берет только одно имя исходного файла, но даже если она принимает несколько ...
- ... make запустит рецепт несколько раз, по одному на файл ссылок, что является пустой тратой, и ...
- ... поскольку ваша утилита go добавляет файлы ссылок, это будет даже хуже, чемпустая трата: обратные ссылки будут подсчитываться несколько раз, и ...
- ... если make работает в параллельном режиме (учтите, что вы можете предотвратить это с помощью
make -j1
или добавив специальный .NOTPARALLEL:
правила для вашего Makefile, но это жаль) есть риск возникновения гонки.
Важно : следующее работает только с плоской организацией, где все исходные файлы и HTMLфайлы находятся в том же каталоге, что и Makefile.Конечно, возможны и другие организации, но они потребуют некоторых модификаций.
Первый вариант с использованием шаблонных правил с несколькими целями
Одна возможность - использовать специальное свойство правил make pattern: когда ониесть несколько целей make считает, что одно выполнение рецепта производит все цели.Например:
pageA.w%e pageB.w%e pageC.w%e: pageA.mdwn pageB.mdwn pageC.mdwn
for m in $^; do go run backlinks.go $$m; done
указывает make, что pageA.whatlinkshere
, pageB.whatlinkshere
и pageC.whatlinkshere
все генерируются одним выполнением:
for m in pageA.mdwn pageB.mdwn pageC.mdwn; do go run backlinks.go $m; done
(make расширяет $^
каквсе предпосылки и $$m
как $m
).Конечно, мы хотим автоматизировать вычисление списка целей шаблона pageA.w%e pageB.w%e pageC.w%e
.Это должно сделать это:
INFILES := $(shell find . -name "*.mdwn")
OUTFILES := $(INFILES:.mdwn=.html)
LINKFILES := $(INFILES:.mdwn=.whatlinkshere)
LINKPATTERN := $(INFILES:.mdwn=.w%e)
.PHONY: all clean
.PRECIOUS: $(LINKFILES)
all: $(OUTFILES)
# These need to be all made before the HTML is processed
$(LINKPATTERN): $(INFILES)
@echo Creating backlinks
@rm -f $(LINKFILES)
@touch $(LINKFILES)
@for m in $^; do go run backlinks.go $$m; done
%.html: %.mdwn %.whatlinkshere
@echo Deps $^
@cmark $^ > $@
clean:
rm -f $(LINKFILES) $(OUTFILES)
Примечания:
- Я объявил
all
и clean
как фальшивые , потому что ... это то, что ониявляются. - Я объявил
whatlinkshere
файлы драгоценными , потому что (некоторые из них) рассматриваются make как промежуточные, и без этого объявления make удаляла бы их после сборки HTML-файлов. - В рецепт для файлов
whatlinkshere
я добавил rm -f $(LINKFILES)
, чтобы при выполнении рецепта мы перезапускались из чистого состояния вместо того, чтобы объединять новые данные со старыми (возможно, устаревшими) ссылками. - Ствол шаблона в
$(LINKPATTERN)
может быть любым, но должен соответствовать хотя бы одному символу.Я использовал w%e
, но whatlin%shere
тоже подойдет.Используйте то, что достаточно конкретно в вашем случае.Если у вас есть файл pageB.where
, предпочтите whatlin%shere
или what%here
.
У этого решения есть недостаток, но он связан с вашей конкретной настройкой: каждый раз по одному mdwn
файл изменяется, его необходимо повторно проанализировать (что нормально), но это может повлиять на любой файл whatlinkshere
.Это не предсказуемо, это зависит от ссылок, которые были изменены в этом исходном файле.Но более проблематичным является тот факт, что результатом этого анализа является добавленный к затронутым whatlinkshere
файлам.Они не " отредактированы " со старым содержимым относительно этого исходного файла, замененным новым.Таким образом, если вы измените только комментарий в исходном файле, все его ссылки будут снова добавлены к соответствующим whatlinkshere
файлам (пока они уже есть).Это, вероятно, не то, что вы хотите.
Именно поэтому решение, приведенное выше, удаляет все файлы whatlinkshere
и повторно анализирует все исходные файлы каждый раз, когда изменяется один единственный исходный файл.И еще одно негативное последствие заключается в том, что все файлы HTML также должны быть сгенерированы повторно, поскольку все файлы whatlinkshere
изменились (даже если их содержимое на самом деле не изменилось, но make этого не знает).Если анализ очень быстрый и у вас есть небольшое количество mdwn
файлов, все должно быть в порядке.Иначе, это неоптимально, но не легко решить из-за вашей конкретной установки.
Второй вариант с использованием рекурсивного make, отдельных файлов обратных ссылок и файлов маркеров
Однако существует возможность, которая заключается в:
- разделении ссылок на все обратные ссылкис одним файлом
whatlinkshere
для каждой пары from / to: foo.backlinks/bar.whatlinkshere
содержит все ссылки на bar
, найденные в foo.mdwn
, - с использованием рекурсивного make с одним первым вызовом (когда переменная
STEP
make равнаunset) обновить все файлы whatlinkshere
, которые должны быть, и второй вызов (STEP
установлен на 2) для генерации файлов HTML, которые должны быть, - с использованием пустых фиктивных файлов, чтобы отметить, что *Файл 1128 * был проанализирован:
foo.backlinks/.done
, - с использованием вторичного расширения , чтобы иметь возможность сослаться на основание правила шаблона в его списке предпосылок (и с использованием
$$
чтобы избежать расширения кулак).
Но, вероятно, это немного сложнее понять (и поддерживать).
INFILES := $(shell find . -name "*.mdwn")
OUTFILES := $(INFILES:.mdwn=.html)
DONEFILES := $(patsubst %.mdwn,%.backlinks/.done,$(INFILES))
.PHONY: all clean
ifeq ($(STEP),)
all $(OUTFILES): $(DONEFILES)
$(MAKE) STEP=2 $@
%.backlinks/.done: %.mdwn
rm -rf $(dir $@)
mkdir -p $(dir $@)
cp $< $(dir $@)
cd $(dir $@); go run ../backlinks.go $<; rm $<
touch $@
else
all: $(OUTFILES)
.SECONDEXPANSION:
%.html: %.mdwn $$(wildcard *.backlinks/$$*.whatlinkshere)
@echo Deps $^
@cmark $^ > $@
endif
clean:
rm -rf *.backlinks $(OUTFILES)
Даже если это выглядит сложнее, есть несколькоПреимущества этой версии:
- перестраиваются только устаревшие цели и только один раз,
- все
whatlinkshere
файлы обновляются (при необходимости) перед обновлением любого HTML-файла (при необходимости), - файлы
whatlinkshere
могут быть построены параллельно, - файлы HTML могут быть построены параллельно.
Третий вариант - использование только рекурсивных файлов марки и маркера
Если вас не интересуют неточные результаты, в которых обратные ссылки сохраняются в результатах после того, как они исчезли из исходных файлов, или когда обратные ссылки бесполезно реплицируются, мы можем повторно использовать идеи из предыдущего решения, но отбросить разделение по отдельным от / до whatlinkshere
files.
INFILES := $(wildcard *.mdwn)
OUTFILES := $(patsubst %.mdwn,%.html,$(INFILES))
LINKFILES := $(patsubst %.mdwn,%.whatlinkshere,$(INFILES))
DONEFILES := $(patsubst %.mdwn,.%.done,$(INFILES))
.PHONY: all clean
.PRECIOUS: $(LINKFILES)
ifeq ($(STEP),)
.NOTPARALLEL:
all $(OUTFILES): $(DONEFILES)
$(MAKE) STEP=2 $@
.%.done: %.mdwn
go run backlinks.go $<
touch $@
else
all: $(OUTFILES)
%.html: %.mdwn %.whatlinkshere
@echo Deps $^
@cmark $^ > $@
%.whatlinkshere:
touch $@
endif
clean:
rm -f $(OUTFILES) $(LINKFILES) $(DONEFILES)
Примечания:
- Поскольку это работает только для плоской организации, я заменил
$(shell find...)
на встроенную * make 1165 *. - Я использовал
patsubst
вместо старого синтаксиса, но это просто дело вкуса. - Правило
%.whatlinkshere:
является правилом по умолчанию для создания пропущенных пустых whatlinkshere
файлов. - Специальная цель
NOTPARALLEL:
параллельное выполнение при построении файлов whatlinkshere
.