user2747939
user2747939

Reputation: 43

Makefile multiple target vs separated targets give different results for pattern

My makefile failed. My idea was to extract files from a certain file and put them in a temporary directory. This is then rsync'ed to a source directory. Since rsync only updates when a file has changed, makefiles in the source directory do not remake unless necessary. I have isolated the problem below:

.PHONY: extract clean

FILES = filea1.cc filea2.cc filea3.cc                       \
        filea1.hh filea2.hh filea3.hh                       \
        filea1.py filea2.py filea3.py                       \
        dir-cc/filea1.cc dir-cc/filea2.cc dir-cc/filea3.cc  \
        dir-cc/filea1.hh dir-cc/filea2.hh dir-cc/filea3.hh  \
        dir-py/filea1.py dir-py/filea2.py dir-py/filea3.py

PATTERN   := $(sort $(addprefix build/%., $(patsubst .%,%,$(suffix $(FILES)))))

extract: $(addprefix build/, $(FILES))

$(PATTERN):
    mkdir -p $(dir $@); echo "hello!" > $@

#build/%.cc:;mkdir -p $(dir $@); echo "hello!" > $@
#build/%.hh:;mkdir -p $(dir $@); echo "hello!" > $@
#build/%.py:;mkdir -p $(dir $@); echo "hello!" > $@

temp:
    echo $(PATTERN)

clean:
    rm -rf build

Doing make temp gets 'build/%.cc build/%.hh build/%.py' but 'make extract' fails to make all the files:

build:
dir-cc  dir-py  filea1.cc  filea2.cc  filea3.cc

build/dir-cc:
filea1.cc  filea2.cc  filea3.cc

build/dir-py:
filea1.py  filea2.py  filea3.py

The hh files are missing. Unbelievably, in the actual work, the cc files are missing. Anyway, commenting out $(PATTERN), and removing the comments so that the targets in the code above are separate, does get all the files:

build:
dir-cc  filea1.cc  filea1.py  filea2.hh  filea3.cc  filea3.py
dir-py  filea1.hh  filea2.cc  filea2.py  filea3.hh

build/dir-cc:
filea1.cc  filea1.hh  filea2.cc  filea2.hh  filea3.cc  filea3.hh

build/dir-py:
filea1.py  filea2.py  filea3.py

Since everything has to be automatic in the actual work, and the rule is the same for all the targets (so I should not have to retype it), I vastly prefer the first version. I read the manual but I am perplexed as to why make does this. I am using GNU Make 4.1.

Upvotes: 2

Views: 737

Answers (2)

user2747939
user2747939

Reputation: 43

There was a good resolution: use the ordinary (non pattern) multiple target rule $(FILES):;rule and define pattern specific variable values to adjust the rule to the file extension.

Upvotes: 0

MadScientist
MadScientist

Reputation: 100836

It's explained in the last paragraph of the Pattern Intro section:

Pattern rules may have more than one target. Unlike normal rules, this does not act as many different rules with the same prerequisites and recipe. If a pattern rule has multiple targets, make knows that the rule’s recipe is responsible for making all of the targets. The recipe is executed only once to make all the targets.

You can't write a pattern rule with many different pattern targets, such that make will run the recipe once per individual target. That's not how it works.

You could do this, for your very simple case:

FILETYPES := $(sort $(suffix $(FILES)))

$(foreach T,$(FILETYPES),$(eval build/%$T: ; mkdir -p $$(@D); echo "hello!" > $$@))

Upvotes: 1

Related Questions