Search code examples
makefile

target specific variable value changed but not working in Makefile


I want to create a Makefile where release build object files and program are generated in a ./build directory and debug build object files and program generated in a ./debug directory.

To achieve this I am using the target-specific variable values feature of make as per https://www.gnu.org/software/make/manual/make.html#Target_002dspecific

But the feature is not updating the directory to store the .o and program for some reason.

Makefile is below. When I run the command: make

then the release build is correctly created in the ./build directory.

But if instead I run : make debug

then the .o and program file is generated also in the ./build directory? Why is the directory not updated in the line:

debug: BUILD_DIR = ./debug

Here is the Makefile

CXX=g++
CXXFLAGS=-Wall -std=c++17 -Wextra -Wconversion
PROJECTNAME=ntpserver
SOURCES = main.cpp select-server.cpp ntp-server.cpp address.cpp
BUILD_DIR = ./build

OBJ = $(SOURCES:%.cpp=$(BUILD_DIR)/%.o)

$(BUILD_DIR)/$(PROJECTNAME): $(OBJ)
    $(CXX) $(LDFLAGS) $^ $(LDLIBS) -o $@


debug: CPPFLAGS += -DDEBUG
debug: CXXFLAGS += -g
debug: BUILD_DIR = ./debug
debug: $(BUILD_DIR)/$(PROJECTNAME)

$(BUILD_DIR)/%.o: %.cpp
    $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $< -o $@

.PHONY: clean
clean:
    $(RM) $(BUILD_DIR)/*

Solution

  • As the manual says, target-specific variable values are available in the target's recipe, not the target's prerequisite list.

    There are two approaches you can choose from. The simpler one is to use Recursive Make, but that may cause trouble later, so I advise against it.

    The more tedious but maintainable way is to create a parallel to build:

    DEBUG_DIR := ./debug
    DEBUG_OBJ = $(SOURCES:%.cpp=$(DEBUG_DIR)/%.o)
    
    $(DEBUG_DIR)/$(PROJECTNAME): $(DEBUG_OBJ)
        $(CXX) $(LDFLAGS) $^ $(LDLIBS) -o $@
    
    debug: $(DEBUG_DIR)/$(PROJECTNAME)
    
    $(DEBUG_DIR)/%.o: %.cpp
        $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $< -o $@
    

    (Once you have that working properly, you should consider moving the debug-specific compiler flags to the object pattern rule.)