What's a good directory structure for larger C++ projects using Makefile?

C++Makefile

C++ Problem Overview


What's a good directory structure for larger C++ projects using Makefile ?

This is how my directory structure looks at the moment:

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

Now, I'm not sure how my Makefile should look like... it doesn't seem to work when .cpp files and .h files aren't in the same directory. Could anyone point me to a common directory structure with an accompanying Makefile so that I don't reinvent the wheel ?

C++ Solutions


Solution 1 - C++

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.

Solution 2 - C++

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/
    └── ...

Solution 3 - C++

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.

Solution 4 - C++

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.

Solution 5 - C++

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

Solution 6 - C++

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).

Solution 7 - C++

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?

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionOlivier LalondeView Question on Stackoverflow
Solution 1 - C++PhongView Answer on Stackoverflow
Solution 2 - C++4LegsDrivenCatView Answer on Stackoverflow
Solution 3 - C++GnurouView Answer on Stackoverflow
Solution 4 - C++stefanBView Answer on Stackoverflow
Solution 5 - C++kervinView Answer on Stackoverflow
Solution 6 - C++wilhelmtellView Answer on Stackoverflow
Solution 7 - C++dmckee --- ex-moderator kittenView Answer on Stackoverflow