根据依赖通配符使用makefile生成目标子目录

时间:2019-08-02 22:53:11

标签: makefile

我试图将make用作静态站点生成器(类似于Jekyll)。源位于文件夹中,并运行make以将输出生成到单独的目录“ build”中。

源具有以下结构:

src
├── about.md
├── css
│   └── style.css
├── index.md
└─── posts
    ├── 2019-07-30-a-blog-post.md
    ├── 2019-07-08-another-post.md
    └── 2019-08-01-something-else.md

我正在尝试获得这样的输出:

build
├── 2019
│   ├── 07
│   │   ├── a-blog-post
│   │   │   └── index.html
│   │   ├── another-post
│   │   │   └── index.html
│   └── 08
│       └── something-else
│           └── index.html
├── about
│   └── index.html
├── css
│   └── style.min.css
└── index.html

这是我当前的文件...

define generateHTML
    mkdir -p $(dir $1)
    pandoc -f markdown -t html5 -o $1 $2 -s
endef

SRC = src
DST = build

MARKDOWNFILES := $(filter-out $(SRC)/index.md, $(wildcard $(SRC)/*.md))
HTMLTARGETS := $(MARKDOWNFILES:src/%.md=build/%/index.html)
BLOG_MARKDOWNFILES := $(wildcard $(SRC)/posts/*.md)

### [WARNING] Hacky section here ###
# Replace '-' with ' ' for all parts of file basename
FINDREPLACE = $(subst -, ,$(basename $(notdir $(mdf))))
# Get the first 3 words of the space-separated list (YYYY MM DD)
# & format as YYYY/MM/DD-
# & replace all '/' characters with '-'
FINDREPLACE1A = $(subst /,-,$(word 1, $(FINDREPLACE))/$(word 2, $(FINDREPLACE))/$(word 3,$(FINDREPLACE)))-
# Replace the full path string, with the above.
# Get the filename, minus 'YYYY-MM-DD-' prefix.
FINDREPLACE1B = $(subst $(FINDREPLACE1A),$e,$(basename $(notdir $(mdf))))
# Build the full string required for HTMLTARGET variable
# & format into 'build/YY/MM/<string>/index.html'
findReplace2 = build/$(word 1, $(FINDREPLACE))/$(word 2, $(FINDREPLACE))/$(FINDREPLACE1B)/index.html
BLOG_HTMLTARGETS := $(foreach mdf, $(BLOG_MARKDOWNFILES),$(findReplace2))

all: build/index.html $(HTMLTARGETS) $(BLOG_HTMLTARGETS)

# This generates the root index.html
build/index.html: src/index.md
    $(call generateHTML, $@, $<)

# This generates the about/index.html pages, etc.
build/%/index.html: src/%.md
    $(call generateHTML, $@, $<)

### [WARNING] Hacky section here ###
build/%/index.html: $(BLOG_MARKDOWNFILES)
    $(eval WORDLIST = $(filter-out build,$(subst /,$e ,$(subst -,$e ,$(@D)))))
    $(eval QUERY = $(SRC)/posts/$(word 1,$(WORDLIST))-$(word 2,$(WORDLIST))%$(word $(words $(WORDLIST)),$(WORDLIST)).md)
    $(eval FUZZY_FILE = $(filter $(QUERY),$(BLOG_MARKDOWNFILES)))
    $(call generateHTML, $@, $(FUZZY_FILE))

# Tidy-up
.PHONY: clean
clean:
    rm -Rf $(DST)

我已经设法将某些东西凑在一起-使用内置函数subst / word / words / filter进行查找/替换和匹配-这样做成功生成了我想要的输出,但是每次更新一个文件时,它都会重新生成 all 个文件,这似乎很不理想。

是否有一种直接的方法来操纵Makefile的目标/依赖关系字符串,以执行类似的操作?

src/posts/YYYY-MM-DD-post.md => build/YYYY/MM/post/index.html

2 个答案:

答案 0 :(得分:0)

这可能是foreach-eval-call的任务:

SUBDIRS :=
POSTS   := $(notdir $(wildcard $(SRC)/posts/*.md)) 

.PHONY: all
.DEFAULT_GOAL := all

define MY_rule
$1.split := $$(subst -, ,$1)
$1.year  := $$(word 1,$$($1.split))
$1.month := $$(word 2,$$($1.split))
$1.day   := $$(word 3,$$($1.split))
$1.name  := $$(patsubst $$($1.year)-$$($1.month)-$$($1.day)-%.md,%,$1)
$1.dir   := $$(DST)/$$($1.year)/$$($1.month)/$$($1.name)

$$($1.dir)/index.html: $$(SRC)/posts/$1 | $$($1.dir)
    $$(call generateHTML,$$<,$$@)

all: $$($1.dir)/index.html

SUBDIRS += $$($1.dir)
endef
$(foreach p,$(POSTS),$(eval $(call MY_rule,$(p))))

SUBDIRS := $(sort $(SUBDIRS))

$(SUBDIRS):
    mkdir -p $@

演示:

$ make
mkdir -p build/2019/08/something-else
pandoc -f markdown -t html5 -o src/posts/2019-08-01-something-else.md build/2019/08/something-else/index.html -s
mkdir -p build/2019/07/another-post
pandoc -f markdown -t html5 -o src/posts/2019-07-08-another-post.md build/2019/07/another-post/index.html -s
mkdir -p build/2019/07/a-blog-post
pandoc -f markdown -t html5 -o src/posts/2019-07-30-a-blog-post.md build/2019/07/a-blog-post/index.html -s

答案 1 :(得分:0)

有多种方法可以实现,但是没有一种方法特别干净。

第一个问题是将源名称转换为目标名称,例如

src/posts/2019-07-08-another-post.md => build/2019/07/another-post/index.html

一个聪明的受虐狂可以使用Make函数来做到这一点,但我可能只是调用sed:

SRC := src/posts/2019-07-08-another-post.md
TARG = $(shell echo $(SRC) | sed 's|src/posts/\(....\)-\(..\)-..-\(.*\)\.md|build/\1/\2/\3/index.html|')

有了SRCTARG之后,编写规则就很容易了。让Make自动编写规则会有点困难,但是我们可以使用模板来做到这一点:

define template
SRC := $(1)
$$(TARG): $(1)
    @echo building $$@ from $$^
    $(call generateHTML, $$@, $$<)
endef

$(eval $(call template,$(SOME_SOURCE)))

现在我们要做的就是遍历源文件,为每个文件创建一条规则:

$(foreach MF,$(BLOG_MARKDOWNFILES),$(eval $(call template,$(MF))))