I'm working on a project that will have both a server and a client, using Boost's ASIO library for networking. I'm not really comfortable working with Makefiles, but, with some help from Chase Lambert's https://makefiletutorial.com/#makefile-cookbook and other things I found online I got the following Makefile:
BUILD_DIR := ./build
INC_DIRS := ./include
SRC_DIRS := ./src
SERVER_TARGET_EXEC := server
CLIENT_TARGET_EXEC := client
TARGETS := $(BUILD_DIR)/$(SERVER_TARGET_EXEC) $(BUILD_DIR)/$(CLIENT_TARGET_EXEC)
# Find all the C and C++ files we want to compile
# For SRCS we exclude files inside the src/server and src/client directories
SRCS := $(shell find $(SRC_DIRS) \( -name "*.cpp" -or -name "*.c" \) ! \( -path '*/server/*' -or -path '*/client/*' \))
SERVER_SRCS := $(shell find $(SRC_DIRS)/server -name *.cpp -or -name *.c)
CLIENT_SRCS := $(shell find $(SRC_DIRS)/client -name *.cpp -or -name *.c)
# String substitution for every C/C++ file.
# As an example, hello.cpp turns into ./build/hello.cpp.o
OBJS := $(SRCS:%=$(BUILD_DIR)/%.o)
SERVER_OBJS := $(SERVER_SRCS:%=$(BUILD_DIR)/%.o)
CLIENT_OBJS := $(CLIENT_SRCS:%=$(BUILD_DIR)/%.o)
ALL_OBJS := $(SUDOKU_OBJS) $(SERVER_OBJS) $(CLIENT_OBJS)
# String substitution (suffix version without %).
# As an example, ./build/hello.cpp.o turns into ./build/hello.cpp.d
DEPS := $(ALL_OBJS:.o=.d)
# Every folder in ./src will need to be passed to GCC so that it can find header files
INC_DIRS += $(shell find $(SRC_DIRS) -type d ! \( -path '*/server' -or -path '*/client' \))
# Add a prefix to INC_DIRS. So moduleA would become -ImoduleA. GCC understands this -I flag
INC_FLAGS := $(addprefix -I,$(INC_DIRS))
# The -MMD and -MP flags together generate Makefiles for us!
# These files will have .d instead of .o as the ouput
CPPFLAGS := $(INC_FLAGS) -MMD -MP -g
CPPFLAGS += -g
CPPFLAGS += -Wall -Wextra
CPPFLAGS += -Wpedantic -Warray-bounds -Weffc++
CPPFLAGS += -Werror
# For Boost ASIO we need to link against Boost Thread and Boost System
LDFLAGS := -pthread
LDFLAGS += -lboost_thread -lboost_system
all: $(TARGETS)
$(BUILD_DIR)/$(SERVER_TARGET_EXEC): $(OBJS) $(SERVER_OBJS)
$(BUILD_DIR)/$(CLIENT_TARGET_EXEC): $(OBJS) $(CLIENT_OBJS)
# The final build step
$(TARGETS):
$(CXX) $^ -o $@ $(LDFLAGS)
# Build step for C source
$(BUILD_DIR)/%.c.o: %.c
mkdir -p $(dir $@)
$(CC) $(CPPFLAGS) $(CFLAGS) -c $< -o $@
# Build step for C++ source
$(BUILD_DIR)/%.cpp.o: %.cpp
mkdir -p $(dir $@)
$(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $< -o $@
.PHONY: clean
clean:
rm -r $(BUILD_DIR)
# Include the .d makefiles. The - at the front suppresses the errors of missing
# Makefiles. Initially, all the .d files will be missing, and we don't want those
# errors to show up
-include $(DEPS)
Any suggestions on what could be improved?
-
\$\begingroup\$ Is SUDOKU_OBJS a copy paste error? \$\endgroup\$Reinderien– Reinderien2021年08月11日 03:01:32 +00:00Commented Aug 11, 2021 at 3:01
1 Answer 1
Why are INC_DIRS
and SRC_DIRS
pluralized if they only contain one value each, and are treated that way throughout the rest of the makefile? These should be singular.
It looks like $(OBJS)
is common to both executables, and is re-linked in each case. It would be (in most cases) better to represent this as a separate, dedicated link step that produces a .so
shared object rather than static linking. This shared object file would be shipped beside both the server and client, and the size of the server and client binaries would be reduced.
Your
mkdir -p $(dir $@)
$(CC) $(CPPFLAGS) $(CFLAGS) -c $< -o $@
would ideally be split up into two different make targets - one to make the directory, and the second depending on the directory and compiling the object.
SUDOKU_OBJS
seems a likely error.
-
\$\begingroup\$ Thank you for your suggestions, I like the idea of compiling
$(OBJS)
as a shared object :) And yes,SUDOKU_OBJS
was indeed a copy paste error, I forgot to change its name when modifying the code for my question :/ \$\endgroup\$Spiwocoal– Spiwocoal2021年08月11日 13:59:08 +00:00Commented Aug 11, 2021 at 13:59