Reputation: 3736
I have the following makefile:
CC ?= gcc
LD := gcc
CFLAGS := -Wall -Wextra -Werror -Wfatal-errors
LDFLAGS :=
LIBRARIES := m c
INCLUDEDIRS := .
LIBS = $(addprefix -l,$(LIBRARIES))
INCLUDES = $(addprefix -I,$(INCLUDEDIRS))
SRC := $(wildcard *.c)
TARGET = $(TARGETDIR)/test
OBJDIR = $(TARGETDIR)/obj/
OBJ = $(addprefix $(OBJDIR),$(SRC:%.c=%.c.o))
.SUFFIXES:
.SUFFIXES: .c.o
.PHONY: all debug i7avx i7avx-debug
all: TARGETDIR := generic
all: CFLAGS += -O3
all: LDFLAGS += -s
all: $(TARGET)
debug: CFLAGS += -Og
debug: TARGETDIR := generic/dbg
debug: $(TARGET)
$(OBJDIR):
@mkdir -p $(OBJDIR)
$(OBJ): | $(OBJDIR)
$(OBJDIR)%.c.o : %.c
$(CC) $(CFLAGS) $(INCLUDES) -c $< -o $@
$(TARGET) : $(OBJ)
$(LD) $(LDFLAGS) -o $@ $^ $(LIBS)
The special thing here is that the output directory depends on the target. Currently, only all and debug is defined, but the idea is to support a whole slew of architectures, and to define an outputdir per target.
Problem: this does not work. If I run this, I get:
cc -Wall -Wextra -Werror -Wfatal-errors -O3 -I. -c main.c -o /obj/main.c.o
Assembler messages:
Fatal error: can't create /obj/main.c.o: No such file or directory
make: *** [Makefile:37: /obj/main.c.o] Error 1
Which implies that the TARGETDIR variable was expanded too late. If I replace the automatic variables with real variables, it does work:
$(OBJ): | $(OBJDIR)
$(OBJDIR)%.c.o : %.c
$(CC) $(CFLAGS) $(INCLUDES) -c $(SRC) -o $(OBJ)
$(TARGET) : $(OBJ)
$(LD) $(LDFLAGS) -o $(TARGET) $(OBJ) $(LIBS)
running this:
cc -Wall -Wextra -Werror -Wfatal-errors -O3 -I. -c main.c -o generic/obj/main.c.o
gcc -s -o generic/test generic/obj/main.c.o -lm -lc
Sooo, how can I make the autmatic variables expand after the TARGETDIR was defined?
Upvotes: 0
Views: 353
Reputation: 461
First, the reason why it does not work :
You're using target-specific variables, but those are only available in the context of a target recipe (I'm quoting the manual here), not during the rules evaluation :
Make will first read your Makefile, evaluate your $(OBJDIR)
and $(TARGET)
rules (at this point $(TARGETDIR)
is not yet defined) then, it will try to update all
, and at this point set $(TARGETDIR)
to the target-specific value for all
(which explains why you're second example work, but it should rebuild every time).
I may have some hints to achieving what you're trying to do (I'm actually planning on doing a similar thing soon) :
you could use the eval function to generate one rule for each build/archi, like this:
#define TARGET_RULE
$(TARGET) : $(OBJ)
$$(RECIPE)
#endif
$(foreach TARGETDIR, $(BUILD_LIST), $(eval $(TARGET_RULE))
($$
is needed for the recipe to avoid it being expanded during the rule evaluation)
You should also be able to define only the rule or rules for the target you are currently building (not sure if that would make a signifiant perf difference).
Upvotes: 0
Reputation: 99124
Make does handle wildcards very deftly, or this would be a much easier problem.
As it is, I think the best solution is to use recursive Make. Just change this:
all: $(TARGET)
debug: $(TARGET)
to this:
all debug:
$(MAKE) $(TARGET) TARGETDIR=$(TARGETDIR) CFLAGS+='$(CFLAGS)' LDFLAGS=$(LDFLAGS)
Upvotes: 1