首页 > 解决方案 > 如何为具有多个目录的 C++ 项目创建 Makefile?

问题描述

我想为具有以下布局的项目创建一个 Makefile:

Source files (.cpp, potentially .c) in /src, with potential subdirectories
Header files (.h, .hpp...) in /inc, with potential subdirectories
Object files (.o) in /obj, with potential subdirectories
External libraries in /lib
Compiled program in /bin

到目前为止,我已经设法一起编写了这个 Makefile,但是有一些问题:

SRC_DIR     := src
BIN_DIR     := bin
LIB_DIR        := lib
INC_DIR        := inc
OBJ_DIR        := obj

SRCEXTS     := .c .C .cc .cpp .CPP .c++ .cxx .cp
HDREXTS     := .h .H .hh .hpp .HPP .h++ .hxx .hp

TARGETS        := $(BIN_DIR)/program
SOURCES        := $(wildcard $(addprefix $(SRC_DIR)/*,$(SRCEXTS)))
HEADERS        := $(wildcard $(addprefix $(LIB_DIR)/*,$(HDREXTS)))
OBJECTS        := $(addsuffix .o, $(basename $(SOURCES)))

CXX         = g++
CXXFLAGS     = -std=c++17 -c -g -Wall

.PHONY: all clean

all: $(TARGETS)

$(TARGETS): $(OBJECTS)
    $(CXX) $^ -o $@

$(OBJ_DIR)%$(OBJECTS): $(SRC_DIR)%$(SOURCES)
    $(CXX) $(CXXFLAGS) $< -o $@

clean:
    rm -f $(OBJECTS) $(TARGETS)

我试图让它尽可能“通用”,所以未来的项目可以用这个布局和 makefile 作为模板开始。目前,它在源代码的 src 目录中创建 .o 文件。尝试编译程序时也会失败

g++ src/main.o -o bin/program
/usr/bin/ld: src/main.o: _ZSt4cout: invalid version 3 (max 0)
/usr/bin/ld: src/main.o: error adding symbols: bad value
collect2: error: ld returned 1 exit status
make: *** [Makefile:23: bin/program] Error 1

对 C++ 开发非常陌生。一段时间以来一直在疯狂追逐,试图清楚地了解这一切是如何运作的。我的代码基本上是我偶然发现的几个代码片段的怪异科学怪人怪物。希望我的意图足够清楚,这是我最后的努力!提前致谢 :)

标签: c++makefile

解决方案


正如@JohnBollinger 指出的那样,您一次尝试太多。我将建议进行一些更改以使您的 makefile 起步。

我无法解释您在尝试构建可执行文件时遇到的错误(您没有向我们提供足够的信息来重现该错误),但它看起来不像是 Make 问题。我建议您尝试在使用 Make 的情况下使用命令行构建它,看看会发生什么。

我将假设您的源名称以“.cpp”结尾(例如src/sailboat/foo.cpp),标题名称以“.hpp”结尾,并且目录树obj/已经存在且正确。这些限制是临时的训练轮;当你有更多的技能时,你可以删除它们。

首先,找到源文件。这个:

SOURCES := $(wildcard $(addprefix $(SRC_DIR)/*,$(SRCEXTS)))

src/如果有子目录将不起作用。要递归到子目录,我们将使用find. (GNUMake 有一个可用的快捷方式,但现在我们将以缓慢而谨慎的方式进行操作)。

SOURCES := $(shell find src -name "*.cpp")

现在构造所需目标文件的名称,例如obj/sailboat/foo.o. 这个:

OBJECTS := $(addsuffix .o, $(basename $(SOURCES)))

会给你src/sailboat/foo.o。我们需要一个不同的命令来更改前导目录和后缀:

OBJECTS := $(patsubst src/%.cpp,obj/%.o,$(SOURCES))

一些源文件引用了头文件,所以在我们开始构建对象之前,我们必须能够提供它们。编译器可以找到所需的头文件,但我们必须告诉它在哪里搜索。所以我们需要目录,而不是完整路径:

HEADERS := $(shell find inc -name "*.hpp")
HEADERDIRS := $(sort $(dir $(HEADERS)))

(这sort只是为了删除重复项。没有必要,但很整洁。)

现在是构建对象的规则。这是不正确的:

$(OBJ_DIR)%$(OBJECTS): $(SRC_DIR)%$(SOURCES)
    $(CXX) $(CXXFLAGS) $< -o $@

请记住,它OBJECTS可以包含多个以空格分隔的单词。因此,如果它包含foo bar,则目标将是obj/%foo bar,这显然不是您想要的。同样,先决条件列表是错误的,配方也是错误的。把它扔掉,然后重新开始。

$(OBJ_DIR)/%.o: $(SRC_DIR)/%.cpp
    $(CXX) $< -c -o $@

然后记住头文件,并添加标志来告诉编译器在哪里寻找它们:

INCLUDEFLAGS := $(addprefix -I,$(HEADERDIRS))

$(OBJ_DIR)/%.o: $(SRC_DIR)/%.cpp
    $(CXX) $< -c $(INCLUDEFLAGS) -o $@

这应该足以让您的 makefile 正常工作;进一步的细化可以等待。


推荐阅读