aioobe
aioobe

Reputation: 421100

Always process outermost file extension (and strip extensions along the way)

I have a bunch of different source files in my static HTML blog. The outermost extensions explain the format to be processed next.

Example: Source file article.html.md.gz (with target article.html) should be processed by gunzip, then by my markdown processor.

Further details:

Ideally I would have liked to just write rules as follows:

...

all-articles: $(ALL_HTML_FILES)

%: %.gz
    gunzip ...

%: %.md
    markdown ...

%: %.zip
    unzip ...

And let make figure out the path to take based on the sequence of extensions.

From the documentation however, I understand that there are constraints on match-all rules, and the above is not possible.

What's the best way forward? Can make handle this situation at all?

Extensions are made up examples. My actual source files make more sense :-)

Upvotes: 1

Views: 107

Answers (1)

bobbogo
bobbogo

Reputation: 15493

I'm on holiday so I'll bite.

I'm not a fan of pattern rules, they are too restricted and yet too arbitrary at the same time for my tastes. You can achieve what you want quite nicely in pure 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

The key here is to set $files to your list of files. This list is then passed to emit-rules. emit-rules passes each file one-at-a-time to emit-hierachy.

emit-hierachy strips off each extension in turn, generates the appropriate make syntax, which it passes to $(eval …). emit-hierachy carries on until the file has only one extension left.

Thus a.html.md.gz becomes this make syntax:

a.html.md: a.html.md.gz ; gunzip <$< >$@
a.html: a.html.md ; mdtool $< -o $@
all: a.html

Similarly, b.html.gz becomes:

b.html: b.html.gz ; gunzip <$< >$@
all: b.html

Neato, or what?

If you give emit-rules a file with an unrecognised extension (c.html.pp say), you get a compile-time error:

1:20: *** Cannot handle .pp files: [c.html.pp].  Stop.

Compile-time? Yeah, before any shell commands are run.

You can tell make how to handle .pp files by defining cmds<.pp> :-)

For extra points it's also parallel safe. So you can use -j9 on your 8 CPU laptop, and -j33 on your 32 CPU workstation. Modern life eh?

Upvotes: 2

Related Questions