C++ Makefile,是否可以进一步分解它?

问题描述

我有一个学校项目,我想编写一个 Makefile,我看到了一些将 Makefile 与多个源目录和多个可执行文件一起使用的示例,但仍然无法将其正确实现到我的 Makefile 中。

PS:我使用 doctest 进行单元测试(我无法更改它)。

这是项目结构(我无法更改):

.
├── bin
├── build
├── extern
│   └── doctest.h
├── include
│   ├── file1.hpp
│   └── file2.hpp
├── src
│   ├── file1.cpp
│   └── file2.cpp
├── tests
│   ├── file1-test.cpp
│   └── file2-test.cpp
└──  Makefile

我有以下目录:

  • bin:用于所有可执行文件。

  • build:对于所有对象 (.o)。

  • extern:用于 doctest 标头(这是我存储任何其他库的位置)

  • include:用于所有标头 (.hpp)。

  • src:对于所有类 (.cpp)。

  • tests:用于所有单元测试(还有 .cpp

您可以将 file1.cpp 视为类,将 file1.hpp 视为类标头,将 file1-test.cpp 视为类的单元测试。

这是我的 Makefile:

BIN_DIR := bin/
BUILD_DIR := build/
EXTERN_DIR := extern/
INCLUDE_DIR := include/
SOURCE_DIR := src/
TESTS_DIR := tests/
DEP_DIR := .dep/

DEPENDS := $(patsubst %.o,$(BUILD_DIR)$(DEP_DIR)%.d,$(notdir $(wildcard $(BUILD_DIR)*.o)))

EXE := $(addprefix $(BIN_DIR),file1-test file2-test)

OBJS_1 := $(addprefix $(BUILD_DIR),file1.o)
OBJS_2 := $(addprefix $(BUILD_DIR),file1.o file2.o)

CXX := clang++
CXXFLAGS := -Wall -std=c++11 -g -O3 -I$(INCLUDE_DIR) -I$(EXTERN_DIR)

vpath %.cpp $(SOURCE_DIR) $(TESTS_DIR)

.PHONY: all clean

all: $(EXE)

$(BUILD_DIR) $(BIN_DIR) $(BUILD_DIR)$(DEP_DIR):
    @mkdir -p $@

$(BUILD_DIR)%.o: %.cpp | $(BUILD_DIR) $(BUILD_DIR)$(DEP_DIR)
    @$(CXX) $(CXXFLAGS) -MMD -MP -MF $(BUILD_DIR)$(DEP_DIR)$(notdir $(basename $@).d) -c $< -o $@

$(BIN_DIR)%: $(BUILD_DIR)%.o | $(BIN_DIR)
    @$(CXX) -o $@ $^

$(BIN_DIR)file1-test: $(OBJS_1)
$(BIN_DIR)file2-test: $(OBJS_2)

.PRECIOUS: $(BUILD_DIR)%.o

-include $(DEPENDS)

clean:
    -rm -rf $(BIN_DIR) $(BUILD_DIR)

所以我的问题是:

  • 我的 Makefile 是否遵循良好做法?

  • 优化了吗?如果没有,我怎样才能让它变得更好?

  • 对于每个新的可执行文件,我必须添加一个 OBJS_X 变量和一个目标 $(BIN_DIR)fileX-test: $(OBJS_X),我可以去掉它吗?如果是的话,有人可以给我写一些通用规则,这样我就不必每次想要一个新的可执行文件时都指定一个变量和一个目标。

  • 如果我只想编译一个可执行文件,我必须使用 make bin/fileX-test。是否可以只运行 make fileX-test 而不是 make bin/fileX-test(但仍然在 bin 目录中构建它)?我试图实现这样的规则: fileX-test: $(BIN_DIR)fileX-test 但它没有按我想要的方式工作,在编译的最后它开始执行内置规则,我不知道为什么。谁能解释一下?

最终答案:

我认为这是一个很好的答案,如果它可以在以后对某人有所帮助:

BIN_DIR := bin/
BUILD_DIR := build/
EXTERN_DIR := extern/
INCLUDE_DIR := include/
SOURCE_DIR := src/
TESTS_DIR := tests/
DEP_DIR := $(BUILD_DIR).dep/

CXX := g++
CXXFLAGS := -Wall -std=c++11 -g -O3 -I$(INCLUDE_DIR) -I$(EXTERN_DIR)
DEPFLAGS := -MMD -MP -MF $(DEP_DIR)

vpath %.cpp $(SOURCE_DIR) $(TESTS_DIR)

file1-test_OBJECTS := $(addprefix $(BUILD_DIR),file1.o)
file2-test_OBJECTS := $(addprefix $(BUILD_DIR),file1.o file2.o)

EXE := $(patsubst %_OBJECTS,%,$(filter %_OBJECTS,$(.VARIABLES)))

.PHONY: all keep help check clean $(EXE)

all: $(EXE:%=$(BIN_DIR)%)

$(foreach E,$(EXE),$(eval $(BIN_DIR)$E: $($E_OBJECTS)))
$(foreach E,$(eval $E: $(BIN_DIR)$E ;))

$(BUILD_DIR) $(BIN_DIR) $(DEP_DIR):
    @mkdir -p $@

$(BUILD_DIR)%.o: %.cpp | $(BUILD_DIR) $(DEP_DIR) $(BIN_DIR)
    @$(CXX) $(CXXFLAGS) $(DEPFLAGS)$(@F:.o=.d) -c $< -o $@

$(BIN_DIR)%: $(BUILD_DIR)%.o
    @$(CXX) -o $@ $^

-include $(wildcard $(DEP_DIR)*.d)

keep: $(EXE:%=$(BUILD_DIR)%.o)

clean:
    -@rm -rf $(BIN_DIR)* $(BUILD_DIR)* $(DEP_DIR)*

解决方法

暂无找到可以解决该程序问题的有效方法,小编努力寻找整理中!

如果你已经找到好的解决方法,欢迎将解决方案带上本链接一起发送给小编。

小编邮箱:dio#foxmail.com (将#修改为@)