makefile target specific variables as prerequisites
gnu-make, makefile
Solution
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.
Problem
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 ```