Makefile: prerequisites with spaces

2019-09-08 02:24发布

问题:

Update: GNU Make 3.81, Ubuntu 12.04

I have a set of markdown files that I want to compile to (say) html files, so this is my rule:

%.html: %.md
    pandoc $< -o $@

So make foo.html would convert foo.md into foo.html.

However, there are spaces in the source markdown filenames and I do not have the ability to control these, that is I can't change a setting to remove the spaces.

This means if I make foo\ bar.html, I get

make: *** No rule to make target `foo bar.html'. Stop.

How can I write a generic rule %.html: %.md where the prerequisite filename has spaces?

I can get around it by using:

foo\ bar.html: foo\ bar.md
    pandoc $< -o $@

But then I must manually write out this rule for every such source file that I have, when I'd rather use the % construct. Is my only hope to do some sort of $(foreach f,$(get list of *.md files),$(eval $(call function_to_generate_rule)))?

回答1:

It seems from what @binki says that GNU make 3.82 might not have this issue, but unfortunately I do not have the option to update from v3.81 that is on my Ubuntu 12.04 machine.

I managed to "solve" it like so by using SECONDEXPANSION to substitute spaces with backslash-space in the prerequisite (so a prerequisite of foo bar.md becomes foo\ bar.md).

# define a variable with a single space
space:=
space+=

.SECONDEXPANSION:
%.html: $$(subst $$(space),\$$(space),%).md
   pandoc "$<" -o "$@"

Here is the log. Again, works on Ubuntu 12.04/GNU Make 3.81, perhaps if you have 3.82 you can use @binki's solution which seems more elegant.



回答2:

Edit

Apparently make’s support for whitespace in inference rules depends on what variant of GNU Make you are using. It just magically works fine with Gentoo’s patched sys-devel/make-3.82-r4 (and fails with Gentoo’s make 3.81-r2). I did not notice any explanation in make-3.82’s ChangeLog or NEWS or the Gentoo patches when quickly checking them. So implicit rules working with whitespace could just be a fluke in make-3.82 itself or even from Gentoo’s patchset. Official GNU support for whitespace in targets is tracked in the still-open GNU Make bug #712.

Original misguided answer

You can use any quoting characters that your shell supports. make ignores them when performing macro substitution and passes them directly to the shell. For example,

.SUFFIXES: .md .html
.md.html:
    pandoc "$(<)" > "$(@)"

results in $ make foo\ bar.html passing the shell pandoc "foo bar.md" > "foo bar.html". I decided to use the traditional style of specifying generic make rules instead of the GNU Make extension involving %, but you can do this with GNU Make’s %-style rules too, I assume.

This does not solve the potential problem of the filenames containing quote characters in them. I think that, simply, most people just avoid putting " or ' in filenames because of the likelihood of causing issues with Makefiles or other scripts. Or you could use a GNU Makefile extension to replace the " characdter with \", something that makes sh happy (we’re going to just ignore cmd for now because I don’t even…):

.SUFFIXES: .md .html
.md.html:
    pandoc "$(subst ",\",$(<))" > "$(subst ",\",$(@))"

This was tested with a file called a"b"c.md which succeeded in creating a"b"c.html (disclaimer: I used discount’s markdown command instead of pandoc).