【问题标题】:Generating target subdirectory with makefile based on dependency wildcard基于依赖通配符使用makefile生成目标子目录
【发布时间】:2019-08-04 13:03:15
【问题描述】:

我正在尝试使用 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

这是我当前的 makefile...

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 执行查找/替换和匹配 - 这确实成功生成了我想要的输出,但是每次更新一个文件时,它都会重新生成所有个文件,这似乎远非理想。

有没有一种直接的方法来操作 makefile 的目标/依赖字符串,来做这样的事情?

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

【问题讨论】:

    标签: makefile


    【解决方案1】:

    这可能是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
    

    【讨论】:

      【解决方案2】:

      有不止一种方法可以做到这一点,但没有一种是特别干净的。

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

      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))))
      

      【讨论】:

        猜你喜欢
        • 2021-02-24
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-11-08
        • 1970-01-01
        • 1970-01-01
        • 2014-01-23
        • 1970-01-01
        相关资源
        最近更新 更多