makefilegnu-makegrofftroff

GNU make: several targets in one pattern rule


With explicit targets I can combine several rules like

foo.o bar.o: $(SOURCES)
    cc $< -o $@

This is equivalent of

foo.o: $(SOURCES)
    cc $< -o $@
bar.o: $(SOURCES)
    cc $< -o $@

But I want to use pattern rules.

I have several troff documents (man, README) and I want to generate .html and .ascii files.

Naive approach is

GROFF := groff
DOCS := man README

DOC_FILES = $(foreach doc,$(DOCS),$(doc).html $(doc).ascii)
CALL_GROFF = $(GROFF) -T$(subst $*.,,$@) -mman $< > $@

%.html %.ascii: %.doc
        $(CALL_GROFF)

.DEFAULT: all
all: $(DOC_FILES)

.PHONY: clean
clean:
        rm $(DOC_FILES)

But it doesn't work, because make believes that all files are created with one command (much like & in modern make: https://www.gnu.org/software/make/manual/html_node/Multiple-Targets.html)

Obviously I can do

GROFF := groff
DOCS := man README

DOC_FILES = $(foreach doc,$(DOCS),$(doc).html $(doc).ascii)
CALL_GROFF = $(GROFF) -T$(subst $*.,,$@) -mman $< > $@

%.ascii: %.doc
        $(CALL_GROFF)

%.html: %.doc
        $(CALL_GROFF)

.DEFAULT: all
all: $(DOC_FILES)

.PHONY: clean
clean:
        rm $(DOC_FILES)

But it is a kind of copy-paste.

Could it be solved with GNU make?


Solution

  • This is exactly how this works; it's a long-standing feature. From the documentation:

    Pattern rules may have more than one target; however, every target must contain a % character. Pattern rules are always treated as grouped targets (see Multiple Targets in a Rule) regardless of whether they use the : or &: separator.

    As example states, it was meant to deal with programs that generate more than one output in one invocation, like bison. You can either update your recipe to generate both files in one shot, or keep the rules separated as you do now.