对于使用 Makefile 的大型 C + + 项目,什么是好的目录结构?

对于使用 Makefile 的大型 C + + 项目,什么是好的目录结构?

我的目录结构现在是这样的:

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

现在,我不知道我的 Makefile 应该是什么样子... 它似乎不工作时。Cpp 档案及。H 文件不在同一个目录中。有没有人能给我指出一个通用的目录结构,并附带一个 Makefile,这样我就不用重新发明轮子了?

115824 次浏览

Separating the .cpp of the .h file is not always a good solution. Generally I separate both of them when it is used as a library (public header in include and private header with the source code).

If it is a library, this structure is ok.

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)

If it is a application

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)

Use the flag -I$(PROJECT_BASE)/include to specify the include path for the compilation

If it is a big project, it can be good to use tool like autoconf/automake or cmake to build everything. It will ease the development.

There is no "good directory structure". Pick a structure you're comfortable with and stick to it. Some like placing source files (headers and implementation files) in a src/ directory, so the root directory of the project has nothing but a makefile, a readme and little else. Some like placing helper libraries under a lib/ directory, unittests under test/ or src/test/, documentation under doc/ etc.

I have yet to hear of anyone splitting header files and implementation files into two distinct directories though. Personally I don't like splitting files into directories much. I usually place all my source in a single directory and all the documentation in another directory. If I rely on good search tools anyway, there's no need for a complex directory structure.

make can deal with the sort of structure where the makefile resides in a different directory than the source. The only thing is that it will invoke the rules from the directory of the makefile -- compilers usually have no problem compiling source that is in some subdirectory. You don't have to specify relative paths in your #includes; just specify the include path with compiler flags (gcc's -I flag etc).

If you have many source files, it may also be a good idea to further subdivide your source directory. For instance, one subdirectory for the core functionality of your application, one for the GUI, etc.

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

Doing so also forces you to avoid unneeded relationships between your "modules", which is a prerequisite to nice and reusable code.

There is no one specific or required directory structure.

You can set it up anyway you like. Your problem is simple to solve. Just instruct Makefile to look into subdirectories or put compiled objects into subdirectories instead of using just current directory.

You would just use in Makefile paths:

%.o : %.cpp

replace with

bin/%.o : %.cpp

So it will check if binary file in directory bin exists and so on, you can apply the same to locations where files are compiled.

There are ways to add/remove/modify paths of source and object files.

Have a look at gnu make manual, specifically section 8.3 Functions for File Names,and the one before that 8.2 Functions for String Substitution and Analysis.

You can do stuff like:

get a list of objects from list of source files in current directory:

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

Output:

Application.o Market.o ordermatch.o

If binary objects are in subdirectory bin but source code is in current directory you can apply prefix bin to generated object files:

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

Output:

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

And so on.

If you haven't seen it before read Recursive Make Considered Harmful.

Short, short version: Though very common the recursive make idiom is less than optimal and gets ever worse as projects grow larger and more complicated. An alternative is presented.

Related link: What is your experience with non-recursive make?

This is an old question. But you can consider the Pitchfork Project as a general guide.

https://github.com/vector-of-bool/pitchfork for the project.

Some Documentation here

For those who find this question after 2020, an alternative modern and reasoned vision of "Canonical Project Structure" for C++ has been presented by Boris Kolpackov: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p1204r0.html

Briefly - no include/ and src/ split. All headers, sources, modules and unit tests go into one directory. Implementation details may be separated from public API by moving to <name>/<name>/details/ subdirectory.

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

For example:

bestlib/
├── bestlib/
│   ├── foo.h
│   ├── foo.cpp
│   ├── foo.test.cpp
│   ├── bar.h
│   ├── bar.cpp
│   └── bar.test.cpp
└── tests/
├── functional_test1/
└── integration_test1/