Pattern rule with different dependencies for different files - makefile

In my Makefile I have a pattern rule
%.out: %.in
myscript $< $#
I want this rule to be triggered not only when the files quux.in or foobar.in are modified, but also when any of the files quux/* or foobar/* (respectively) are modified.
How can one express such a dependence in a (GNU) Makefile?

How about this? You could even have a variable like:
DIR_LIST = quux foobar home france china mexico mars
%.out: %.in
myscript $< $#
$(foreach dir,${DIR_LIST},${dir})/%.out: $(foreach dir,${DIR_LIST},${dir})/%.in
myscript $< $#
OR.....
%.out: %.in
myscript $< $#
quux/%.out: quux/%.in
myscript $< $#
foobar/%.out: foobar/%.in
myscript $< $#

I found this solution:
.SECONDEXPANSION:
%.out: %.in $$(call find, $(basename %), *)
my_script $< $#
find = $(foreach dir,$(1),$(foreach d,$(wildcard $(dir)/*),$(call find,$(d),$(2))) $(wildcard $(dir)/$(strip $(2))))
The recursive wildcard find function comes from https://plus.google.com/101663514639216293981/posts/h5Xr1i8kgfu.

Related

Makefile - prereq-patterns % in $(wildcard)

I want to add rules of .cc files to a static mode rule. And I try to use $(wildcard ) to enable the prereq-pattern which exists in folder in order to avoid a No rule to make target ... error. But % in $(wildcard ) isn't transformed to the file basename. $(wildcard %.cpp) and $(wildcard %.cc) turn to be nothing.
I want to know how to solve this and make .cc, .cpp in one static mode rule.
# before
# CXX_SOURCE_FILE = $(wildcard *.cpp)
CXX_SOURCE_FILE = $(wildcard *.cpp) $(wildcard *.cc)
C++ = g++
CXX_FLAGS = -g -Wall
c++ : $(basename $(CXX_SOURCE_FILE))
# before
# $(basename $(CXX_SOURCE_FILE)) : % : %.cpp
# $(C++) $< -o $# $(CXX_FLAGS)
$(basename $(CXX_SOURCE_FILE)) : % : $(wildcard %.cpp) $(wildcard %.cc)
$(C++) $< -o $# $(CXX_FLAGS)
You cannot use wildcard like this because it will be evaluated during the parsing, before static pattern rules are considered. Unless you have source files that are literally %.cpp or %.cc your prerequisite list will be empty when the time comes to consider the rules.
A simple solution consists in separating your two sets of source files (CPP_SOURCE_FILE and CC_SOURCE_FILE) and use two different static pattern rules:
CPP_SOURCE_FILE = $(wildcard *.cpp)
CC_SOURCE_FILE = $(wildcard *.cc)
$(basename $(CPP_SOURCE_FILE)): %: %.cpp
$(C++) $< -o $# $(CXX_FLAGS)
$(basename $(CC_SOURCE_FILE)): %: %.cc
$(C++) $< -o $# $(CXX_FLAGS)
There are other solutions but they are more complicated. If you use GNU make you can either use the foreach-eval-call construct or the secondary expansion.
Example with foreach-eval-call:
# $1: executable
define MY_rule
$1: $$(wildcard $1.cpp) $$(wildcard $1.cc)
$$(C++) $$< -o $$# $$(CXX_FLAGS)
endef
$(foreach e,$(basename $(CXX_SOURCE_FILE)),$(eval $(call MY_rule,$e)))
For executable foo this will call MY_rule with parameter foo, pass the result to eval that will expand it and instantiate it as a new rule. $(call MY_rule,foo) expands as:
foo: $$(wildcard foo.cpp) $$(wildcard foo.cc)
$$(C++) $$< -o $$# $$(CXX_FLAGS)
eval expands it as:
foo: $(wildcard foo.cpp) $(wildcard foo.cc)
$(C++) $< -o $# $(CXX_FLAGS)
This is exactly what you wanted for this executable. Note the need of $$ to escape the first expansion by eval.
Example with secondary expansion:
.SECONDEXPANSION:
$(basename $(CXX_SOURCE_FILE)): $$(wildcard $$#.cpp) $$(wildcard $$#.cc)
$(C++) $< -o $# $(CXX_FLAGS)
After the first expansion, during the parsing of the Makefile, this becomes:
foo bar baz: $(wildcard $#.cpp) $(wildcard $#.cc)
$(C++) $< -o $# $(CXX_FLAGS)
Note that if you were trying to use this rule in your Makefile, instead of your static pattern rule, this would not work for the very same reason: when the Makefile is parsed and the prerequisites are expanded the automatic variables are not yet set. So, unless you have files named .cpp or .cc your liste of prerequisites would also be empty.
But every rule after .SECONDEXPANSION: has its prerequisites expanded a second time. And the second time, different from the first, the automatic variables are set, including $#. The final result is equivalent to the 3 following rules:
foo: $(wildcard foo.cpp) $(wildcard foo.cc)
$(C++) $< -o $# $(CXX_FLAGS)
bar: $(wildcard bar.cpp) $(wildcard bar.cc)
$(C++) $< -o $# $(CXX_FLAGS)
baz: $(wildcard baz.cpp) $(wildcard baz.cc)
$(C++) $< -o $# $(CXX_FLAGS)
From the GNU make manual:
the true power of this feature only becomes apparent when you discover that secondary expansions always take place within the scope of the automatic variables for that target. This means that you can use variables such as $#, $*, etc. during the second expansion and they will have their expected values, just as in the recipe. All you have to do is defer the expansion by escaping the $.
Note: here again the $$ are essential to escape the first expansion.

Makefile: match multiple pattern rules

I have a makefile like this:
EXT = .cc
BLD = .build
all: bin/analyses/test
bin/analyses/test: $(BLD)/object.o
.SECONDEXPANSION:
$(DEPS): $(BLD)/%.d: src/%$(EXT) | $(BLD)/$$(dir %)
$(CXX) $(CPPFLAGS) -MM -MT '$(#:.d=.o)' $< -MF $#
$(BLD)/%.o: | $(BLD)
$(CXX) $(CXXFLAGS) -c $(filter %$(EXT),$^) -o $#
bin/%: $(BLD)/%.o | $$(dir bin/%)
$(CXX) $(LDFLAGS) $(filter %.o,$^) -o $# $(LDLIBS)
bin/%/ $(BLD)/%/:
mkdir -p $#
If line 6 looks the way it is, then everything works. Both bin/analyses/test: and bin/%: rules are used. But if I change line 6 to
bin/analyses/%: $(BLD)/object.o
only the bin/%: rule gets picked up.
How can I make multiple pattern rules match for the same target?
First, Make sometimes removes trailing slashes from targets, which can cause some confusion. In this case it takes your rule bin/%/ $(BLD)/%/: ..., which you clearly intended for directories, and uses it for files, at least sometimes. It is easy enough to do without an explicit rule for directories, by using mkdir -p in other rules.
Second, Make doesn't combine pattern rules the way it does ordinary rules. It finds one pattern rule and uses that. In a relatively simple case like this, we can write a rule that will do what we want:
all: bin/analyses/test
$(BLD)/%.o:
mkdir -p $(dir $#)
$(CXX) $(CXXFLAGS) -c $^ -o $#
bin/analyses/%: $(BLD)/analyses/%.o $(BLD)/object.o
mkdir -p $(dir $#)
$(CXX) $(LDFLAGS) $^ -o $# $(LDLIBS)
bin/%: $(BLD)/%.o
mkdir -p $(dir $#)
$(CXX) $(LDFLAGS) $^ -o $# $(LDLIBS)
(There is some obvious redundancy in those last two rules which I don't see how to iron out without making the makefile less readable, but it works as intended.)

Copy files with GNU make

I need to copy several files. Doing a makefile:
FILES=foo.txt d1/bar.dat d2/baz.txt
TARGETDIR=/app
targets=$(addprefix $(TARGETDIR)/,$(FILES))
all: $(targets)
$(targets): $(FILES)
cp $(subst $(TARGETDIR)/,,$#) $#
Files copied correctly, but if I do touch foo.txt, all three files are copied.
I know that "the correct way" is to define three rules like:
$(TARGETDIR)/foo.txt: foo.txt
cp $^ $#
$(TARGETDIR)/d1/bar.dat: d1/bar.dat
cp $^ $#
$(TARGETDIR)/d2/baz.txt: d2/baz.txt
cp $^ $#
But in this case I have to write names of the files twice, once for these rules and once for all rule.
Is there a way to 'multiply' the rule for each name in the prerequisite?
Something like
$(TARGETDIR)/%: $(FILES)
cp $< $#
You can manipulate the target name using the text manipulation functions, if you use secondary expansion:
.SECONDEXPANSION:
$(targets): $$(patsubst $(TARGETDIR)/%, %, $$#)
echo cp $< $#
This is not the only way, but it's probably the simplest.

Same rule for different directory in makefile

I am having rule like this.
$(OBJDIR)/%.s: %.c
$(CC) $(PPE-CFLAGS) $(DEFS) -S -o $# $<
i have a list like
dirs := $(OBJDIR) $(COMOBJDIR)
So, instead of writing duplicate code for each dir, can I make them into one? Something like iteration on demand?
You have hit one of the weak spots in Make: its inability to handle multiple wildcards at once. There is a way, but it's ugly.
First take your rule:
$(OBJDIR)/%.s: %.c
$(CC) $(PPE-CFLAGS) $(DEFS) -S -o $# $<
and put it into a variable, escaping the '$' symbols and replacing $(OBJDIR) with $(1):
define myrule
$(1)/%.s: %.c
$$(CC) $$(PPE-CFLAGS) $$(DEFS) -S -o $$# $$<
endef
Then you can call it using call and invoke it using eval:
$(eval $(call myrule, $(OBJDIR))
That's good enough for OBJDIR, but to do the same for a list of directories, use foreach:
$(foreach dir, $(OBJDIR) $(COMOBJDIR), $(eval $(call myrule, $(dir))))
Or just use your variable:
dirs := $(OBJDIR) $(COMOBJDIR)
$(foreach dir, $(dirs), $(eval $(call myrule, $(dir))))
Ugly, but effective.

A make rule for verbosity

Typically we have this in a Makefile
%.o:%.c
$(cc) $(flags) -o $# -c $<
When the amount of flags is huge, I feel better to write this instead
%.o:%.c
$(info $(cc): $< --> $#)
#$(cc) $(flags) -o $# -c $<
However it can be useful to sometime see everything. So I defined a variable for that:
at=#
%.o:%.c
$(info $(cc): $< --> $#)
$(at)$(cc) $(flags) -o $# -c $<
My question is how to properly, easily set or unset $(at) from the command line. I see two solutions:
$ make verbose all
$ make verbose=1 all
With the first solution I would do this
ifeq (,$(filter verbose,$(MAKECMDGOALS)))
at=#
dummy:=$(filter-out verbose,$(MAKECMDGOALS)))
endif
With the second I might do this
ifeq (,$(filter 1,$(verbose)))
at=#
endif
Is both solutions acceptable or can I do better?
I generally set up my makefiles like this:
Recipe lines that I know I will never want to see the commands reported begin with # (typically this is just any echo statements or other similar meta-statements).
All other recipe lines do NOT prefix with #.
I add the .SILENT: psuedo-target to make the output silent by default.
I prefix (or suffix) the .SILENT: psuedo-target with a variable reference, like $V.
So, something like this:
%.o: %.c
#echo '$(cc): $< --> $#'
$(cc) $(flags) -o $# -c $<
$V.SILENT:
Now by default V is not set, so the last line expands to the .SILENT special target and no recipe commands are shown. If I run make V=1 (or any other value) then the target expands to 1.SILENT: which is nothing special to make and so is essentially ignored, and all my commands that are NOT prefixed with # are printed.

Resources