【问题标题】:C++ Makefile auto-depencies with multiple executables具有多个可执行文件的 C++ Makefile 自动依赖关系
【发布时间】:2021-05-02 18:15:26
【问题描述】:

我有一个学校项目,我想编写一个 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 是类的单元测试。

在上面的示例中,我有 2 个测试文件,但在项目的最后我会有更多,并且对于每个测试文件,我都会有一个可执行文件。


我的目标:

  • 我想运行make 并编译所有单元测试(tests/ 目录中的所有.cpp)。

  • 我希望所有可执行文件都存储在bin/ 目录中,所有二进制文件都存储在build/ 目录中。


这是我的 Makefile:

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

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

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

EXEC_FILES = file1-test file2-test
BIN = $(addprefix $(BIN_DIR), $(EXEC_FILES))

all: $(BIN) | $(BIN_DIR)

$(BUILD_DIR)%.o: %.cpp | $(BUILD_DIR)
    $(CXX) $(CXXFLAGS) -c -o $@ $^

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

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

$(BIN_DIR)file2-test: $(BUILD_DIR)file1.o $(BUILD_DIR)file2.o $(BUILD_DIR)file2-test.o
    $(CXX) -o $@ $^

clean:
    -rm -f $(BIN_DIR)* $(BUILD_DIR)*

它运行良好,但我觉得它正在做一些多余的事情,我可以通过更多的 Makefile 艺术知识来避免,尤其是在这里:

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

$(BIN_DIR)file2-test: $(BUILD_DIR)file1.o $(BUILD_DIR)file2.o $(BUILD_DIR)file2-test.o
    $(CXX) -o $@ $^

目前这个 Makefile 是正确的,因为我只有 2 个可执行文件,但我最终会得到 15+ 个,我不想每个可执行文件有 15 倍:

$(BIN_DIR)xxx-test: $(BUILD_DIR)xxx.o etc.
    $(CXX) -o $@ $^

我到底需要什么...:

基本上,我需要编写一个通用规则来获取给定目标的所有适当依赖项。

在阅读了多篇文章后,我认为这都是关于自动依赖的。

我很确定最终结果会像这样,但遗憾的是我无法让它在我的情况下工作:

$(BIN_DIR)%: ???
    @$(CXX) -o $@ $^

我已经看过这个(以及关于该主题的许多其他帖子):http://make.mad-scientist.net/papers/advanced-auto-dependency-generation/,但我仍然无法弄清楚。

那么我该如何编写一个可以完成这项工作的表达式,有人可以给我一个工作示例或类似的东西吗?

编辑 1:

基于此帖子:Makefile (Auto-Dependency Generation)

我将这些行添加到我的Makefile

SRC = $(wildcard $(SOURCE_DIR)*.cpp)
SRC += $(wildcard $(TESTS_DIR)*.cpp)

这个想法是从源目录(srctests)中获取所有.cpp。然后我将-MDD 选项添加到我的CXXFLAGS 变量中,为每个目标创建一个.d 文件(至少这是我认为它正在做的事情):

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

最后,我添加了这个:

$(BIN_DIR)%: $(SRC)
    $(CXX) -o $@ $^

-include $(SRC:.cpp=.d)

我期望它做什么:

  • 创建一个.d 文件,其中包含每个目标的所有依赖项。

  • 获取.d 文件中的依赖项并将它们转换为.o 以获取给定目标所需的所有对象。

但它似乎没有达到我的预期。

编辑 3:

经过一些更改,我最终得到了这个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), Coord-test Fourmi-test)

OBJS_1 := $(addprefix $(BUILD_DIR), Coord.o)
OBJS_2 := $(addprefix $(BUILD_DIR), Coord.o Fourmi.o)

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

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

all: $(EXE)

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

$(BIN_DIR):
    mkdir -p $@

$(BUILD_DIR)%.o: %.cpp | $(BUILD_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)Coord-test: $(OBJS_1)
$(BIN_DIR)Fourmi-test: $(OBJS_2)

.PRECIOUS: $(BUILD_DIR)%.o

-include $(DEPENDS)

clean:
    -rm -f $(BIN_DIR)* $(BUILD_DIR)* $(BUILD_DIR)$(DEP_DIR)*

它正在工作,但我必须为每个新的可执行文件添加OBS_X

我也想分解这个,但我不知道这是否可能?如果有人可以告诉我。

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

$(BIN_DIR)Coord-test: $(OBJS_1)
$(BIN_DIR)Fourmi-test: $(OBJS_2)

【问题讨论】:

    标签: c++ makefile project-structure


    【解决方案1】:

    既然你知道你总会有一个foo-test.o 来构建一个foo-test 程序,你可以这样写你的模式规则:

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

    但是,make 无法推断出构建这些可执行文件可能需要哪些其他对象。你只需要告诉它。因此,对于上述示例,您可以添加以下内容:

    $(BIN_DIR)file1-test: $(BUILD_DIR)file1.o 
    $(BIN_DIR)file2-test: $(BUILD_DIR)file1.o $(BUILD_DIR)file2.o
    

    您无需将配方放在这里,这只是为这些目标添加更多先决条件。您也不必输入$(BUILD_DIR)file1-test.o 等,因为这是从模式规则中推断出来的。

    但是,如果您确实有需要使用的其他个目标文件,则必须明确列出它们,这是没有办法的。

    【讨论】:

    • 好的,谢谢,如果我不能让它工作,这可能就是我要做的,但是看看这个帖子:stackoverflow.com/questions/8025766/…,我觉得我们可以使用-MMD标志来实现我想要的。我编辑了我的原始帖子。
    • 没有。该方法允许您自动找出目标文件的依赖关系(哪些头文件用于编译目标文件)。这与找出用于创建可执行文件的目标文件完全不同。每个目标文件都是从源文件构建的,每个源文件都明确包含对其使用的所有头文件的引用,因此编译器可以跟踪它们并打印出来。任何地方都没有类似的所需目标文件列表,因此您必须自己手动将它们写入您的 makefile。
    • 好的,我按照您的建议做了一些更改,现在我还有一些其他问题,我编辑了它。
    • SO 中的每个问题都应提出一个特定问题并获得该问题的答案。在同一个问题中创建一长串增量问题是不合适的。如果您有不同的问题,请创建另一个问题,并确保显示您的 makefile(或足够的文件以了解情况),并显示您调用的 make 命令和您得到的错误输出或对发生的事情的清晰描述和为什么这不是你想要发生的。 它没有达到我的预期不是我们可以帮助解决的问题。
    • 我做到了,对不起,感谢您的建议,这是新帖子:stackoverflow.com/questions/67375725/…
    猜你喜欢
    • 1970-01-01
    • 2017-04-11
    • 2011-04-14
    • 1970-01-01
    • 2013-05-21
    • 2022-01-25
    • 1970-01-01
    • 2023-04-06
    • 1970-01-01
    相关资源
    最近更新 更多