Edward Edward - 15 days ago 9
C++ Question

Out-of-source build doesn't match rule for object files

I'm trying to set up a Makefile for out-of-source builds of a C++ project, based on a template I found on this blog post. However, for some reason, the pattern-matching target

$(BUILD_DIR)/$.cpp.o:
doesn't seem to be working, because I get errors like "No rule to make target 'build/somefile.cpp.o'."

Here is my Makefile, with some of the unnecessary details removed. Note that the SRCS variable is built up from other variables because eventually I want to support different targets using different sets of source files, but for now SRCS has only one definition and could be defined in one line.

CXX = g++
RM = rm -f
MKDIR_P ?= mkdir -p

SRC_DIR := ./src
BUILD_DIR := ./build

CPPFLAGS := -std=c++14 -g3 -O0 -Wall

LFLAGS := -L"./libraries/mutils"
INCLUDES := -I"$(SRC_DIR)/" -I"./libraries/"
LIBS := -lmutils -lpthread

OBJS = $(SRCS:$(SRC_DIR)/%=$(BUILD_DIR)/%.o)
DEPS = $(OBJS:.o=.d)

COMMON_SRCS := sourcefile1.cpp sourcefile2.cpp sourcefile3.cpp sourcefile4.cpp
COMMON_SRCS := $(addprefix $(SRC_DIR)/,$(COMMON_SRCS))
COMMON_SRCS += $(shell find $(SRC_DIR)/util -name *.cpp)

SIM_SRCS := $(shell find $(SRC_DIR)/simulation -name *.cpp)
SIM_SRCS += $(SRC_DIR)/SimulationMain.cpp

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

SRCS = $(COMMON_SRCS) $(SIM_SRCS)

build/simulation: $(OBJS)
$(CXX) $(LFLAGS) $(OBJS) -o $@ $(LIBS)

.PHONY: clean

clean:
$(RM) -r $(BUILD_DIR)

-include $(DEPS)


If I try to run this with
make build/simulation
, I get
*** No rule to make target 'build/sourcefile1.cpp.o', needed by 'build/simulation'. Stop.
This is the first file in the OBJS list, so it seems like Make expanded all the variables but then couldn't match a single object file to a rule. What could be going wrong here?

My first thought was that the problem is the
./
in BUILD_DIR, and Make can't match
build/sourcefile1.cpp.o
to the rule
./build/%.cpp.o : %.cpp
, but I get the exact same error if I remove the
./
from BUILD_DIR and SRC_DIR.

Answer

What's going wrong

The issue is caused by this line:

$(BUILD_DIR)/%.cpp.o: %.cpp

% will be sourcefile1 according to the left-hand side, so expanded line looks like this and the pattern gets ignored:

$(BUILD_DIR)/sourcefile1.cpp.o: sourcefile1.cpp

You need to get src/sourcefile1.cpp on the right to make this rule work.

How to fix it

You can either change the line to:

$(BUILD_DIR)/%.cpp.o: $(SRC_DIR)/%.cpp

Or make paths of object files include src/ (i.e., don't delete $(SRC_DIR) part from their path):

OBJS = $(SRCS:%=$(BUILD_DIR)/%.o)

Both solutions should be equivalent, they just adjust different parts of the rule.