Specify all Makefile targets as .PHONY with wildcard - makefile

All targets in my Makefile aren't real files, is it valid to specify just .PHONY: %. Or should I list all targets?

You need to list all the targets that are meant to be phony targets as prerequisites of the .PHONY target instead of just writing .PHONY: %.
.PHONY: % doesn't do what you think it does (i.e., turning every target into a phony target).
As an example, consider the following makefile:
.PHONY: %
foo:
#echo creating $#
#touch $#
For this makefile above:
$ make
creating foo
$ make
make: 'foo' is up to date.
Therefore, the target foo is not turned into a phony target by having .PHONY: % in your makefile. Otherwise, foo's recipe would have been executed, since phony targets are always outdated.

Related

How to prevent the prerequisite of a target from expanding when the target is not used?

In a Makefile with multiple targets, how can one prevent the prerequisites of targets that are not being used from expanding? See the following example:
thisexpands = $(warning Expanded!)
.PHONY: target1
target1: $(thisexpands)
#echo Target 1
.PHONY: target2
target2:
#echo Target 2
Calling target2 forces thisexpands to expand, even though it is lazily evaluated and it and target1 are never used.
In my real world case expanding thisexpands when calling target1 is an issue, because it is a shell command that prints errors when called out of context of target1 and it's parent targets(not shown here).
Makefiles are fully parsed before the first rule is run. As part of parsing, all the targets and prerequisites must be expanded. You can find details of when expansion happens for different parts of a makefile in How make Reads a Makefile in the GNU make manual.
One way is to use recursion:
thisexpands = $(warning Expanded!)
target1: ; $(MAKE) target1-recurse T1_DEPS='$(value thisexpands)'
T1_DEPS =
target1-recurse: $(T1_DEPS)
#echo Target 1
This doesn't work:
You can probably defer expansion by using secondary expansion, something like this:
.SECONDEXPANSION:
target1: $$(thisexpands)
Be very careful that you escape the prerequisite list appropriately.
There's no way to cancel the expansion completely. However, you can use the conditional assignment based on the value of $(MAKECMDGOALS):
thisexpands = $(if $(filter target1,$(MAKECMDGOALS)),$(warning Expanded!))
.PHONY: target1
target1: $(thisexpands)
#echo Target 1
.PHONY: target2
target2:
#echo Target 2
Note that it works if target1 is only built explicitly (make target1) and not by default, or as a part of building another target.

GNU Make ignoring a phony rule specified by wildcard?

I am learning some courses about compiling some C code into specific assembly. I decided that the generated assembly should be manually inspected, so I came up with less something.s as a "test" rule.
As a fan-but-newbie of Make, I wrote this Makefile:
CODES := a
LESS ?= less
CODES_TEST := $(patsubst %,%-test,${CODES})
.PHONY: all test ${CODES_TEST} clean
all: $(patsubst %,%.s,${CODES})
test: all
%-test: %.s
${LESS} $^
%.s: %.c
${CC} ${CFLAGS} -S -o $# $^
clean:
rm -f *.o *.s
And I have this minimal a.c file:
int asdfg(void) { return 54321; }
I then typed make a-test in Bash, expecting less showing up with the content of a.s, only to be told this:
make: Nothing to be done for 'a-test'.
I got the above response regardless of the presence of a.s, which generates normally if I do make a.s or just make (implicitly runs the first rule, all).
I checked my Makefile and I don't think I made a typo or another simple mistake.
What did I miss with the above Makefile?
How can I get Make to execute less a.s when I run make a-test?
There is nothing to be done for a-test because the only rule that would make it is the implicit pattern rule:
%-test: %.s
${LESS} $^
and, per the manual 4.6 Phony Targets:
The implicit rule search (see Implicit Rules) is skipped for .PHONY targets.
and, since it is .PHONY, its mere non-existence does make it out-of-date.
To get around this, while preserving the phoiness, replace:
%-test: %.s
${LESS} $^
with:
${CODES_TEST}: %-test: %.s
${LESS} $^
Then the rule is a static pattern rule and no longer an implicit one.

How to dynamically generate Makefile targets

Say I have a few files named foo.c, bar.c, baz.c, ... I want to create a Makefile target for each one that runs a build task.
foo:
make foo.c
.PHONY: foo
foo.c:
run build foo
bar:
make bar.c
.PHONY: bar
bar.c:
run build bar
...
I essentially just want to do make foo and it builds make foo.c. But I have x number of files and want to have make tasks for each. How to accomplish this. Something like:
FILES = $(foo bar baz ...)
$(FILES): $(FILES)
make $(FILE)
.PHONY: $(FILE)
$(FILES).c: $(FILES)
run build $(FILE)
But all the files are independent from each other.
You can use pattern rules for the .c targets and static pattern rules for the ones that have to be phony targets (i.e., foo and bar in your example):
targets := foo bar
.PHONY: $(targets)
# static pattern rule
$(targets): %:
make $*.c
# pattern rule
%.c:
run build $*

Match patten rule before explicit rule

I'm trying to generically add some behaviour to every target in a Makefile, without modifying the targets.
My current attempt is thus:
%: $*
#echo 'Logging $* target'
.PHONY: test
test:
#echo 'Inside explicit test target'
When I run make test, I'd like to match the % pattern rule, which would execute test as a prerequisite ($* expanding to the pattern stem), and then log the target that was run.
$ make test
Inside explicit test target
Logging test target
Instead, what happens is that make test matches the explicit test target (presumably since it's a closer match):
$ make test
Inside explicit test target
How can I get this to work, without changing the explicit test target?
EDIT:
Another attempt...
.SECONDEXPANSION:
%: $$*
#echo 'Logging $* target'
results in
$ make test
make: Circular Makefile <- Makefile dependency dropped.
inside actual test target
I appears from your own answer, which has beaten me to the punch, that
you're concerned only to trigger a preliminary action for targets that are
mentioned on the commandline - $(MAKECMDGOALS). From the posting I took
it that you wanted such an action for "every target in a Makefile", which
would include all targets that are prerequisite to the commandline targets or,
if there are no commandline targets, to the default target.
Anyhow, you may still be interested in a solution to the more general problem.
You want a preliminary action to be executed before the recipe for every target.
Your question is: how to match a patten rule before explicit rule?
This is an XY way of posing the problem, because make will consult pattern
rules to find a way of making a target only if you don't give it an explicit
recipe. You know, for example, that make has a pre-defined pattern rule for
making an .o file from a .c file. Even so, if my makefile is:
test.o:
#echo $#
then make prints test.o, without any attempt to find test.c and compile it.
And if my make file is:
test.o: test.c
#echo $#
test.c:
#echo $#
then make prints:
test.c
test.o
needing no resort to the pattern rule. But if my makefile is:
test.o: test.c
Then make says:
make: *** No rule to make target 'test.c', needed by 'test.o'. Stop
So you can't do what you're after in the way your question supposes,
because the preliminary action you want to provoke from the pattern
rule could be provoked only if there were no other action for the target.
In that case the reasons for the failures of your two posted attempts are fairly academic,
and you may wish to scroll to The Chase.
In your first attempt, with:
%: $*
#echo 'Logging $* target'
The pattern rule - which is unemployed by make test - is equivalent to:
%:
#echo 'Logging $* target'
because $* only assumes a value in the recipe, not in the pattern rule. You
can make this pattern rule be employed by making any target for which the
makefile does not provide a recipe, e.g. make nonsuch will print Logging nonsuch target;
but that is of no use.
The second attempt, with:
.SECONDEXPANSION:
%: $$*
#echo 'Logging $* target'
does the right thing to create the rule you intend to create. But the
meaning of that rule is:
<target>: <target>
#echo 'Logging <target> target'
making every target to which this rule is applied a prerequisite of itself.
Inevitably this will result in a circular dependency error for all such targets.
As you saw, this circularity does not affect the your test target because
it has an explicit recipe and does not employ the rule. But it does provoke
the surprising error:
make: Circular Makefile <- Makefile dependency dropped.
That happens because the first target that make automatically considers is
the makefile itself. Unlike the test target, you have no recipe for
the makefile; so the pattern rule applies to it, making the makefile dependent
on itself.
The Chase
You can achieve what you want by a different approach. In a actual project
it is more than likely that in any makefile you can compute a list of
all possible targets. From this you can generate a corresponding list of
auxiliary targets, say, target => target.prelim, where the
sole purpose of target.prelim is to provoke, when it should and not
otherwise, the required preliminary action for target; and you can get make
to generate a list of order-only rules, target: | target.prelim,
for each target, such that target.prelim will not be considered in determining whether target
must be made, but will be made before target whenever target needs to be made.
Here is an illustration:
SRCS := main.c foo.c
OBJS := $(SRCS:.c=.o)
TARGETS := all prog $(OBJS)
PRELIMS := $(patsubst %,%.prelim,$(TARGETS))
define prelim_rule =
$(1): | $(1).prelim
endef
$(foreach target,$(TARGETS),$(eval $(call prelim_rule,$(target))))
.PHONY: all
all: prog
prog: $(OBJS)
$(CC) $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) -o $# $(OBJS) $(LIBS)
clean:
rm -f $(OBJS) $(PRELIMS) prog
%.prelim:
#echo "Logging target $(#:%.prelim=%)"
#touch $#
And a sample session:
$ make
Logging target all
Logging target main.o
cc -c -o main.o main.c
Logging target foo.o
cc -c -o foo.o foo.c
Logging target prog
cc -o prog main.o foo.o
$ make
make: Nothing to be done for 'all'.
$ make clean
rm -f main.o foo.o all.prelim prog.prelim main.o.prelim foo.o.prelim prog
$ make main.o
Logging target main.o
cc -c -o main.o main.c
$ make main.o
make: 'main.o' is up to date.
$ # A prelim can't out-date its target...
$ touch main.o.prelim
$ make main.o
make: 'main.o' is up to date.
I realise that this isn't answering my question as asked, but it has the effect I want - executing a shell command as late in the Makefile processing as possible.
MYVAR?=foo
.PHONY: test
test:
#echo 'Inside test target'
LOG=$(shell echo 'Logging $(MAKECMDGOALS), myvar=$(MYVAR)' > log)
.SECONDEXPANSION:
force: $$(LOG)
LOG is a deferred variable, so is not expanded until Make evaluates the prerequisite list of the force target.
In a single Makefile, the .SECONDEXPANSION: part is not needed, since the force target is evaluated after MYVAR is set.
However, if I move the LOG variable and force variable into a sub-makefile, it would be easy to include subMakefile before the MYVAR?= line - which would not work.
By specifying .SECONDEXPANSION for force, the reliance on ordering is removed.

GNU Makefile - Pattern rule with multiple targets with one dependency ignores all targets but the first

I want to make a language depend target. In Particular: I have one source-file and I want to create different Objects which where add to the corresponding language folder. That single source file will differ in the C-Flags, the compiler will get. As long as I used it in a static way, it works quite fine.
de/info.o en/info.o es/info.o : info.c
$(ECHO) (DEP) $< for $#
Now I thought, it would be great if it is a bit more dynamic, in case i'll add a new language depending file. So I used a wildcard as followed:
de/%.o en/%.o es/%.o : %.c
$(ECHO) (DEP) $< for $#
But now it just make the first target and ignores the rest. The Make-Debug prints the following thing:
Successfully remade target file `de/info.o'.
Considering target file `en/info.o'.
File `en/info.o' was considered already.
Just in case: No, the objects do not exist. So there is no target, but an existing dependencie, so make should execute the rules.
EDIT: Found a solution for that Problem.
define FOO
$(1)/%.o : %.c
$(ECHO) $$< for $(1)
endef
$(foreach lang,$(LANGUAGE_LIST), $(eval $(call FOO,$(lang))))
Inspired by: http://www.gnu.org/software/make/manual/make.html#Eval-Function
Pattern rules work differently than implicit rules. While an implicit rule such as
a b c: d
command
is equivalent to the longer notation
a: d
command
b: d
command
c: d
command
this does NOT hold for pattern rules. Pattern rules with multiple targets are explicitly required to build all of their targets in a single invocation of command. Thus you would have to write
$ cat GNUmakefile
all: de/x.o en/x.o es/x.o
de/%.o: %.c
#echo $# from $<
en/%.o: %.c
#echo $# from $<
es/%.o: %.c
#echo $# from $<
$ gmake
de/x.o from x.c
en/x.o from x.c
es/x.o from x.c
The relevant documentation is found in 10.5.1 Introduction to Pattern Rules of the GNU make manual:
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. When searching for a pattern rule to match a target, the target patterns of a rule other than the one that matches the target in need of a rule are incidental: make worries only about giving a recipe and prerequisites to the file presently in question. However, when this file’s recipe is run, the other targets are marked as having been updated themselves.

Resources