I'm working on a C++ project structured as such:
/root
--/Client
----/build
----/include
----/src
--/Common
----/include
----/src
--/Server
----/build
----/include
----/src
--Makefile
Ideally, I want to compile and build both the server and the client from a single makefile. The .o files would be generated in the /build folder while the executables would be in the /root The makefile would have to compile the files from both the target and the /Common files
So I came up with this (sorry for the mess):
CC := g++
CFLAGS := -Wall -Wextra -I./Common/include -I./Server/include -I./Client/include
LDFLAGS := -lws2_32
CLIENT_SRC_DIR := ./Client/src
CLIENT_BUILD_DIR := ./Client/build
CLIENT_BIN_DIR := ./Client/bin
CLIENT_EXE := ./Client/bin/client.exe
SERVER_SRC_DIR := ./Server/src
SERVER_BUILD_DIR := ./Server/build
SERVER_BIN_DIR := ./Server/bin
SERVER_EXE := ./Server/bin/server.exe
COMMON_SRC_DIR := ./Common/src
COMMON_BUILD_DIR := ./Common/build
CLIENT_SRCS := $(wildcard $(CLIENT_SRC_DIR)/*.cpp)
CLIENT_OBJS := $(patsubst $(CLIENT_SRC_DIR)/%.cpp,$(CLIENT_BUILD_DIR)/%.o,$(CLIENT_SRCS))
SERVER_SRCS := $(wildcard $(SERVER_SRC_DIR)/*.cpp)
SERVER_OBJS := $(patsubst $(SERVER_SRC_DIR)/%.cpp,$(SERVER_BUILD_DIR)/%.o,$(SERVER_SRCS))
COMMON_SRCS := $(wildcard $(COMMON_SRC_DIR)/*.cpp)
.PHONY: all clean fclean dirs
all: client server
client: $(CLIENT_EXE)
server: $(SERVER_EXE)
$(CLIENT_EXE): $(CLIENT_OBJS)
$(CC) $(CFLAGS) -o $@ $(CLIENT_OBJS) $(LDFLAGS);
$(SERVER_EXE): $(SERVER_OBJS) | $(SERVER_BIN_DIR)
$(CC) $(CFLAGS) -o $@ $(SERVER_OBJS) $(LDFLAGS);
$(CLIENT_BUILD_DIR)/%.o: $(CLIENT_SRC_DIR)/%.cpp $(COMMON_SRC_DIR)/%.cpp
$(CC) $(CFLAGS) $^ -o $@;
$(SERVER_BUILD_DIR)/%.o: $(SERVER_SRC_DIR)/%.cpp $(COMMON_SRC_DIR)/%.cpp
$(CC) $(CFLAGS) $^ -o $@;
dirs:
mkdir "$(CLIENT_BUILD_DIR)";
mkdir "$(CLIENT_BIN_DIR)";
mkdir "$(SERVER_BUILD_DIR)";
mkdir "$(SERVER_BIN_DIR)";
clean:
rm -r $(CLIENT_BUILD_DIR) $(SERVER_BUILD_DIR);
fclean: clean
rm -r $(CLIENT_BIN_DIR) $(SERVER_BIN_DIR);
re: fclean all
Unfortunately, the message I get reads:
make: *** No rule to make target 'Client/build/main.o', needed by 'Client/bin/client.exe'. Stop.
I know that this message appears when a file is missing, but since I use the % wildcard, wouldn't it work?
I've had a really hard time trying to figure this out, thank you to whoever would help me!
Your rules are trying to do too much at once. Look at the following rule:
$(CLIENT_BUILD_DIR)/%.o: $(CLIENT_SRC_DIR)/%.cpp $(COMMON_SRC_DIR)/%.cpp
$(CC) $(CFLAGS) $^ -o $@;
We are trying to build $(CLIENT_BUILD_DIR)/main.o
, so the %
part is main
.
Substitute that into the rule:
$(CLIENT_BUILD_DIR)/main.o: $(CLIENT_SRC_DIR)/main.cpp $(COMMON_SRC_DIR)/main.cpp
$(CC) $(CFLAGS) $^ -o $@;
Oops: you depend on $(COMMON_SRC_DIR)/main.cpp
which does not exist.
I suppose you wrote this rule with the intention of saying "the .cpp file can come from either directory". In that case you just have to write two rules, one for each possible source:
$(CLIENT_BUILD_DIR)/%.o: $(CLIENT_SRC_DIR)/%.cpp
$(CC) -c $(CFLAGS) $^ -o $@;
$(CLIENT_BUILD_DIR)/%.o: $(COMMON_SRC_DIR)/%.cpp
$(CC) -c $(CFLAGS) $^ -o $@;
$(SERVER_BUILD_DIR)/%.o: $(SERVER_SRC_DIR)/%.cpp
$(CC) -c $(CFLAGS) $^ -o $@;
$(SERVER_BUILD_DIR)/%.o: $(COMMON_SRC_DIR)/%.cpp
$(CC) -c $(CFLAGS) $^ -o $@;
As an aside: you should use $(CXX)
and $(CXXFLAGS)
when compiling C++ code and pass -c
when compiling to object files.