C/C++工程管理:makefile模板
C/C++工程,代码文件一旦多起来,需要考虑怎样有效的组织管理它们。多数情况下可以用CMake构建,少数时候例如嵌入式linux平台,用cmake也是完全没有问题的,但总是有工程师喜欢坚持用makefile,我不知道和u-boot使用makefile有没有关系,但如果坚持要用makefile,我认为要考虑如下2点:
- .o/.a/.so等,生成的各种二进制文件,不要放在.c/.cpp源码同级目录,而是放在独立的构建目录,并且目录应该自动创建。
- 分别指定
C_SRCS
和CXX_SRCS
很麻烦,应当支持让makefile自行过滤出来。
查找makefile的教程,关键字"万能通用makefile“,以及B站上排名第一的视频,大都在讲如何编一个可执行文件目标,很少有提及静态库、动态库目标的;如果makefile解决的问题仅仅是可执行文件的构建,那么完全没必要用makefile,g++ *.cpp
一把梭岂不是很好?
此外,有些人不知道Address Sanitizer这一内存问题排查利器的存在,它是可以在makefile中配置的。
针对以上问题,本人用一个实际项目fibercv的代码予以实践:fibercv的代码会构建静态库(libfibercv.a)、动态库(libfibercv.so)和可执行文件(demo),并且生成目录为obj目录。
C/C++代码的目录结构:
(base) home% tree
.
├── CMakeLists.txt
├── demo
│ └── main.cpp
├── deps
│ ├── glad
│ │ ├── glad.c
│ │ └── glad.h
│ ├── KHR
│ │ └── khrplatform.h
│ └── stb
│ ├── stb_image.h
│ └── stb_image_write.h
├── fibercv
│ ├── fc_log.c
│ ├── fc_log.h
│ ├── fibercv.h
│ ├── image.cpp
│ ├── image.h
│ ├── improc_private.cpp
│ ├── improc_private.h
│ ├── imshow.cpp
│ └── imshow.h
├── makefile
├── mingren.jpg
├── readme.md
对应的万能通用makefile如下:
#CC := clang
#CXX := clang++
#CXXFLAGS = -Ideps/ -I./
#LDFLAGS = -lglfw
#SRCS = fibercv/image.cpp fibercv/imshow.cpp fibercv/improc_private.cpp fibercv/fc_log.c deps/glad/glad.c demo/main.cpp
#all:
# clang++ $(CXXFLAGS) $(SRCS) $(LDFLAGS) -o demo
BUILD_DIR=obj
SRCS := fibercv/image.cpp fibercv/imshow.cpp fibercv/improc_private.cpp fibercv/fc_log.c deps/glad/glad.c
C_SRCS = $(filter %.c,$(SRCS))
CXX_SRCS = $(filter %.cpp,$(SRCS))
C_OBJS := $(patsubst %.c,$(BUILD_DIR)/%.o,$(C_SRCS))
CXX_OBJS := $(patsubst %.cpp,$(BUILD_DIR)/%.o,$(CXX_SRCS))
CC = clang
CXX = clang++
AR = ar crs
ASAN_FLAGS :=-g -fno-omit-frame-pointer -fsanitize=address
INCLUDE = -I./deps
CFLAGS += -fPIC $(ASAN_FLAGS) $(INCLUDE)
CXXFLAGS += -fPIC $(ASAN_FLAGS) $(INCLUDE)
SHARE = -fPIC -shared
SO_LDFLAGS += -lglfw
FIBERCV_LIB:=$(BUILD_DIR)/libfibercv.a
FIBERCV_SO:=$(BUILD_DIR)/libfibercv.so
DEMO=$(BUILD_DIR)/demo
all: $(FIBERCV_LIB) $(DEMO)
#all: $(FIBERCV_SO) $(DEMO)
info:
@echo "C_SRCS:" $(C_SRCS)
@echo "CXX_SRCS:" $(CXX_SRCS)
@echo "C_OBJS:" $(C_OBJS)
@echo "CXX_OBJS:" $(CXX_OBJS)
#-----------------------
# fibercv static library
#-----------------------
$(FIBERCV_LIB): $(CXX_OBJS) $(C_OBJS)
$(AR) $@ $^
$(BUILD_DIR)/%.o: %.cpp
mkdir -p $(@D)
$(CXX) $(CXXFLAGS) -c $^ -o $@
$(BUILD_DIR)/%.o: %.c
mkdir -p $(@D)
$(CC) $(CFLAGS) -c $^ -o $@
#-----------------------
# fibercv shared library
#-----------------------
$(FIBERCV_SO): $(CXX_OBJS) $(C_OBJS)
$(CXX) $(SHARE) $^ $(SO_LDFLAGS) -o $@
#-----------------------
# demo executable
#-----------------------
DEMO_SRCS := demo/main.cpp
DEMO_CXXFLAGS := -I./
export PKG_CONFIG_PATH=/home/zz/soft/glfw3/lib/pkgconfig:$PKG_CONFIG_PATH
DEMO_LDFLAGS := -lfibercv -Lobj/ `pkg-config --libs glfw3` -ldl -lpthread
$(DEMO): $(DEMO_SRCS)
$(CXX) $(DEMO_CXXFLAGS) $^ $(DEMO_LDFLAGS) -o $@
.PHONY:
clean:
rm -f $(C_OBJS)
rm -f $(CXX_OBJS)
rm -f $(FIBERCV_LIB)
rm -f $(FIBERCV_SO)
rm -f $(DEMO)
一些坑
make sort
不符合预期
sort:
cmake --build ${BUILD_DIR} --target sort
./sort
看起来 sort
是 make 的保留字, make sort
执行时候不会执行 ./sort
. 两种解决方法:
- 把 target 改为别的名字
- 添加
.PHONY
说明
.PHONY : sort
sort:
cmake --build ${BUILD_DIR} --target sort
./sort
参考
Greatness is never a given, it must be earned.