What is the scope of secondary expansion in a makefile?
If i type:
.SECONDEXPANSION:
Where does this change stop? Does it apply to the all targets from that point onward? or only up until the next target?
It applies to all targets from then on. From the docs:
...All the prerequisites of the targets defined after the special target are expanded a second time.
And if you doubt the documentation, you can try it. Modifying an examples from the docs:
.SECONDEXPANSION:
main_OBJS := main.o try.o test.o
lib_OBJS := lib.o api.o
main: $$($$#_OBJS)
#echo prerequisites are $^
lib: $$($$#_OBJS)
#echo prerequisites are $^
Related
I have a project where you can build for multiple platforms, architectures, etc. I need to be able to set various variables based on the target before compiling any source files. But what's happening is the non-platform specific files are getting compiled immediately and then the platform specific ones didn't get compiled at all.
Below calling "make foobar_mac", foo.o gets created but bar.mac.o does not and therefore during the final link bar.mac.o is missing.
OBJS = foo.c bar.$(PLATFORM).c
%.o : %.c
$(COMPILE.c) $(OUTPUT_OPTION) $<
foobar_mac: PLATFORM := mac
foobar_mac: $(OBJS)
foobar_win: PLATFORM := win
foobar_win: $(OBJS)
There are various ways to get what you want. One way is to use secondary expansion to delay the expansion until after the target-specific variable is in scope.
Like this:
.SECONDEXPANSION:
OBJS = foo.c bar.$$(PLATFORM).c
%.o : %.c
$(COMPILE.c) $(OUTPUT_OPTION) $<
foobar_mac: PLATFORM := mac
foobar_mac: $(OBJS)
foobar_win: PLATFORM := win
foobar_win: $(OBJS)
Note how we added .SECONDEXPANSION, then we escaped the $(PLATFORM) reference in OBJS as $$(PLATFORM) so it would not be expanded when GNU make parses the makefile.
There are various other ways to do this as well.
I have set up my makefile like below, to minimize code duplication
The recipes are a set of blocks that set a variable, and then run the sleeper_agent recipe. They work great when called individually as make xlsx_sleeper for example.
But when I call all_sleepers, only the first one (xlsx_sleeper) gets compiled.
I have tried declaring them as phony (.PHONY: all_sleepers xlsx_sleeper docx_sleeper pptx_sleeper pdf_sleeper png_sleeper), which changes nothing
and adding a .FORCE rule to the sleeper_agent rule, which results in no such file or directory:
.FORCE:
sleeper_agent: .FORCE [...]
Here is my makefile:
all_sleepers: xlsx_sleeper docx_sleeper pptx_sleeper png_sleeper pdf_sleeper
sleeper_agent: $(OBJ)/sleeper_agent.o $(OBJ)/identities.o
windres icons/$(ext)/resource.rc -O coff -o obj/$(ext).res
$(CC) -o $(BIN)/sleeper_$(ext).exe $^ $(OBJ)/$(ext).res $(CFLAGS) $(LIBS)
xlsx_sleeper: ext=xlsx
xlsx_sleeper: sleeper_agent
docx_sleeper: ext=docx
docx_sleeper: sleeper_agent
pptx_sleeper: ext=pptx
pptx_sleeper: sleeper_agent
png_sleeper: ext=png
png_sleeper: sleeper_agent
pdf_sleeper: ext=pdf
pdf_sleeper: sleeper_agent
Your problem is that make does not see any reason why it should rebuild the sleeper_agent target several times. You should probably stick to the make philosophy:
Try to have real files as targets ($(BIN)/sleeper_xlsx.exe).
Use phony (non-file) targets only:
To give symbolic names to other targets or groups of targets (all_sleepers, xlsx_sleeper, ...)
For rules that don't produce files (clean, help...)
Declare phony targets as such (.PHONY: ...)
Example using static pattern rules, automatic variables and the patsubst make function:
SLEEPER := xlsx docx pptx png pdf
EXE := $(patsubst %,$(BIN)/sleeper_%.exe,$(SLEEPER))
SHORT := $(patsubst %,%_sleeper,$(SLEEPER))
.PHONY: all_sleepers $(SHORT) clean_sleepers
all_sleepers: $(EXE)
$(SHORT): %_sleeper: $(BIN)/sleeper_%.exe
$(EXE): $(BIN)/sleeper_%.exe: $(OBJ)/sleeper_agent.o $(OBJ)/identities.o
windres icons/$*/resource.rc -O coff -o obj/$*.res
$(CC) -o $# $^ $(OBJ)/$*.res $(CFLAGS) $(LIBS)
clean_sleepers:
rm -f $(EXE)
And then you should be able to run:
make all_sleepers
to build them all or:
make xlsx_sleeper
to build only one of them. EXE is the list of real executable files and a static pattern rule explains how to build them. In its recipe the $* automatic variable expands as the string matching the % wildcard. SHORT is the list of xxxx_sleeper shortcuts and another static pattern rule explains for each of them to which real executable it corresponds. all_sleepers and the xxxx_sleeper shortcuts (plus the clean_sleepers I added as example) are properly declared as phony because there are no such real files.
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.
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.
I'm trying to write a makefile to build a release or debug version of my program using the same makefile (the version selection will be based on env. variable).
My first try was the following:
PROG := myProgram
ifdef DEBUG
BUILD := debug
else
BUILD := release
endif
BIN := $(BUILD)/$(PROG)
SRC := main.c
OBJDIR := $(BUILD)/obj/
OBJ := $(addprefix $(OBJDIR), $(SRC:%.c=%.o))
$(OBJDIR)/%.o: %.c
$(GCC) $(CFLAGS) -o $# $<
$(BIN): $(OBJ)
$(GCC) $(OBJ) -o $# $(LDFLAGS)
all: $(BIN)
When I try to make my project, I got the following error:
make: *** No rule to make target `release/obj/main.o', needed by `release/myProgram'. Stop.
When I replaced the rule $(OBJDIR)/%.o by $(BUILD)/obj/%.o, make is able to match the rule and build my objects file.
I am confused: why the first rule is not working?
My understanding is make will expand variables in rules before execution, so, in the end, both rules will looks the same?
What is the best practice in this case?
Thanks in advance for your help.
You put in an extra slash:
OBJDIR := $(BUILD)/obj/
$(OBJDIR)/%.o: %.c
...
The rule, as written, will match the target release/obj//main.o, but there is no rule that matches release/obj/main.o.
Just remove the offending slash:
OBJDIR := $(BUILD)/obj