Search code examples
c++makefile

I can't build my .o file with the makefile ("No rule to make target X, needed by Y")


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!


Solution

  • 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.