c++ - 如何为具有多个目录的 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++ 开发非常陌生。一段时间以来一直在疯狂追逐,试图清楚地了解这一切是如何运作的。我的代码基本上是我偶然发现的几个代码片段的怪异科学怪人怪物。希望我的意图足够清楚,这是我最后的努力!提前致谢 :)
解决方案
正如@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 正常工作;进一步的细化可以等待。
推荐阅读
- css - 浏览器如何渲染/应用 CSS
- php - 从提交数组更新表数据会导致所有行因为相同的数据
- typescript - 如何使用 Ramda 和 Typescript 删除不必要的强制转换
- c++ - 将 float 转换为 std::string 而不会丢失精度。我也不想使用 sprintf
- java - 使用 MVC 时我应该选择什么监听器让视图监听模型?(爪哇)
- android - 带有 JSON 数据请求和响应的 POST 操作 - Retrofit vs Volley
- php - 来自被调用文件的魔术常数 - 不是动作文件
- sorting - 在交换过程中无法获得我想要的最终列表
- javascript - 隐藏相关视频 Youtube Iframe API
- azure - qnamaker 不应用知识库中的更改