使用Makefile的大型C ++项目有什么好的目录结构?

时间:2010-03-02 03:35:00

标签: c++ makefile

使用Makefile的大型C ++项目有什么好的目录结构?

这就是我目前的目录结构:

lib/ (class implementations *.cpp)
include/ (class definitions *.h)
tests/ (main.cpp for quick tests)

现在,我不确定我的Makefile应该是什么样子......当.cpp文件和.h文件不在同一目录中时,它似乎不起作用。任何人都可以指向一个带有Makefile的公共目录结构,以便我不重新发明轮子吗?

7 个答案:

答案 0 :(得分:57)

分离.h文件的.cpp并不总是一个好的解决方案。通常我在将它们用作库时将它们分开(include中的公共头和包含源代码的私有头)。

如果它是一个库,这个结构就可以了。

lib/ (class implementations *.cpp .h)
include/ (class definitions *.h) <- Only those to be installed in your system
tests/ (main.cpp for quick tests)
doc/ (doxygen or any kind of documentation)

如果是应用程序

src/ (source for the application)
lib/ (source for the application library *.cpp *.hpp)
include/ (interface for the library *.h)
tests/ (main.cpp for quick tests) <- use cppunit for this part
doc/ (doxygen or any kind of documentation)

使用标志-I $(PROJECT_BASE)/ include指定编译的包含路径

如果它是一个大项目,使用 autoconf / automake cmake 等工具构建所有内容可能会很好。它将缓解发展。

答案 1 :(得分:12)

如果您有许多源文件,那么进一步细分源目录也是一个好主意。例如,一个用于应用程序核心功能的子目录,一个用于GUI的子目录等。

src/core
src/database
src/effects
src/gui
...

这样做也会强制您避免“模块”之间不必要的关系,这是良好和可重用代码的先决条件。

答案 2 :(得分:12)

没有特定或必需的目录结构。

你可以随意设置它。你的问题很容易解决。只需指示Makefile查看子目录或将编译对象放入子目录中,而不是仅使用当前目录。

您只需在Makefile路径中使用:

%.o : %.cpp

替换为

bin/%.o : %.cpp

因此,它将检查目录bin中的二进制文件是否存在等等,您可以将其应用于编译文件的位置。

有多种方法可以添加/删除/修改源文件和目标文件的路径。

查看gnu make manual,特别是 8.3文件名函数部分,以及 8.2字符串替换和分析函数之前的部分。

您可以执行以下操作:

从当前目录中的源文件列表中获取对象列表:

OBJ     = $(patsubst %.cpp, %.o, $(wildcard *.cpp))

输出:

Application.o Market.o ordermatch.o

如果二进制对象位于子目录bin中,但源代码位于当前目录中,则可以将前缀bin应用于生成的目标文件:

OBJ     = $(addprefix bin/,$(patsubst %.cpp, %.o, $(wildcard *.cpp)))

输出:

bin/Application.o bin/Market.o bin/ordermatch.o

等等。

答案 3 :(得分:6)

没有“良好的目录结构”。选择一个你感觉舒适的结构并坚持下去。有些人喜欢将源文件(头文件和实现文件)放在src/目录中,因此项目的根目录只有一个makefile,一个自述文件和其他内容。有些人喜欢将帮助库放在lib/目录下,在test/src/test/下进行单元测试,在doc/下进行文档编制。

我还没有听说有人将头文件和实现文件分成两个不同的目录。我个人不喜欢将文件分成很多目录。我通常将所有源代码放在一个目录中,将所有文档放在另一个目录中。如果我依赖于优秀的搜索工具,则不需要复杂的目录结构。

make可以处理makefile所在的结构与源不同的目录。唯一的事情是它将从makefile的目录中调用规则 - 编译器通常在编译某个子目录中的源代码时没有问题。您不必在#include中指定相对路径;只需使用编译器标志(gcc的-I标志等)指定包含路径。

答案 4 :(得分:4)

对于在2020年发现此问题的人,鲍里斯·科尔帕科夫(Boris Kolpackov)提出了另一种现代合理的C ++“规范项目结构”构想:http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p1204r0.html

简短-没有include/src/拆分。所有标题,源代码,模块和单元测试都放在一个目录中。可以通过移至<name>/<name>/details/子目录来将实现详细信息与公共API分开。

<name>/
├── <name>/
│   ├── headers...
│   ├── sources...
│   ├── modules...
│   └── unit tests...
└── tests/
    ├── functional_test1/
    ├── functional_test2/
    ├── integration_test1/
    ├── integration_test2/
    └── ...

答案 5 :(得分:3)

如果您在阅读Recursive Make Considered Harmful之前没有看过它。

短,短版本:虽然很常见,递归制作习惯不是最优的,随着项目变得越来越大,越来越复杂,越来越糟糕。提出了另一种选择。

相关链接:What is your experience with non-recursive make?

答案 6 :(得分:0)

这是一个古老的问题。但是您可以将“干草叉项目”视为一般指南。

https://github.com/vector-of-bool/pitchfork用于该项目。

有些Documentation here