makefile target specific variables as prerequisites
Solution 1
First of all, a non-phony recipe must create a target, $@
, not $(OUTPUT_DIR)$@
. Also consider converting directory dependencies into order-only prerequisites.
In order to get a proper value of $(OUTPUT_DIR)
inside the list of prerequisites, you would have to use secondary expansion, because otherwise, during the primary expansion, the global definition OUTPUT_DIR:=Test/
is used instead of the target-specific one.
Unfortunately, I can't think of a sane way to make it work using target specific variables, without resorting to secondary expansion and vpath magic. Personally I would rather setup the environment first (find out the value of OUTPUT_DIR
, etc.) and then re-execute Make with the proper values.
ifndef OUTPUT_DIR
.PHONY: default all release debug
default all: release
release: export OUTPUT_DIR := Release/
debug: export OUTPUT_DIR := Debug/
debug: export EXTRA_CXXFLAGS := -DDEBUG -g
release debug:
@$(MAKE)
else
# ...
CXXFLAGS := -Iinclude -Wall -Wextra $(EXTRA_CXXFLAGS)
PROGRAM := $(OUTPUT_DIR)corewars
OBJECTS := $(addprefix $(OUTPUT_DIR), \
main.o Machine.o Core.o ProcessQueue.o Instruction.o)
# Default target.
$(PROGRAM): $(OBJECTS) | $(OUTPUT_DIR)
$(LD) -o $@ $<
$(OUTPUT_DIR)%.o: %.cpp | $(OUTPUT_DIR)
$(CXX) -c $(CXXFLAGS) $< -o $@
$(OUTPUT_DIR):
mkdir -p $@
endif # OUTPUT_DIR
The two parts could them be split into separate makefiles, the root (starter) one, and the one that does the real work, to make the whole thing more manageable.
Solution 2
Target-specific variables are only available within the context of the recipes of the target and its recursive prerequisites. That is, target-specific variables cannot be used as targets nor prerequisites.
One workaround is the makefile there.
Related videos on Youtube
Chris Browne
typedef enum { FALSE=0, TRUE } boolean; typedef struct { int age; char* name; boolean employed; char* occupation; char* education; } programmer_t; programmer_t chris; chris.age = 31; chris.name = "Chris Browne"; chris.employed = TRUE; chris.occupation = "Chief Architect"; chris.education = "self-taught"; I maintain an active interest in a plethora of programming languages, paradigms, and toolchains. I aspire to be language and tool agnostic. Interests Artificial Intelligence programming/design Low-level high-performance microcoding (asm/c) Robotics User Interface Design Retrocoding (writing programs for old systems using the tools of the time) Programming Language Design/Development Board games Video games
Updated on September 23, 2022Comments
-
Chris Browne over 1 year
Related: Target-specific Variables as Prerequisites in a Makefile
I'm trying to craft a Makefile which uses a target-specific-variable to specify the output directory for the object files and the final executable. The idea is to maintain two separate binary versions, a 'release' version and a 'debug' version with extra debugging information.
My problem is that 'make' does a clean build every time, even if I haven't changed a thing. I'm pretty sure it's because 'make' is evaluating the prerequisites of the target 'corewars' before the variable declaration in the prerequisites for the 'debug' or 'release' target.
The Makefile is presented below.
CXX=g++ LD=g++ LDFLAGS= CXXFLAGS=-Iinclude -Wall -Wextra OBJECTS=main.o Machine.o Core.o ProcessQueue.o Instruction.o OUTPUT_DIR:=Test/ .PHONY: default .PHONY: all .PHONY: release default: release all: release release: OUTPUT_DIR:=Release/ release: corewars .PHONY: debug debug: CXXFLAGS+=-DDEBUG -g debug: OUTPUT_DIR:=Debug/ debug: corewars corewars: $(OUTPUT_DIR) $(addprefix $(OUTPUT_DIR),$(OBJECTS)) $(LD) -o $(addprefix $(OUTPUT_DIR),corewars) $(addprefix $(OUTPUT_DIR),$(OBJECTS)) Release: mkdir -p $@ Debug: mkdir -p $@ %.o: %.cpp include/%.h $(CXX) -c $(CXXFLAGS) $< -o $(OUTPUT_DIR)$@ .PHONY: clean clean: $(RM) -r Release $(RM) -r Debug
-
Chris Browne almost 12 yearsThis is an excellent sanitisation of my build system, thank you. I found it necessary to add
| $(OUTPUT_DIR)
to the prerequisite list for the rule to make.o
files, and I added a 'clean' target, but apart from that this works as quoted. Many thanks. -
Eldar Abusalimov almost 12 years@Chris, oh, yes, you're right, I forgot about it (fixed). And you're welcome! ;-)
-
Chris Browne almost 12 yearsThanks for the heads up! I'll fix that now.