tags:

views:

371

answers:

5

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 ?

+1  A: 

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.

Phong
+2  A: 

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

wilhelmtell
and `bin/` for the executables... :)
Lipis
+1  A: 

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.

Gnurou
+2  A: 

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.

stefanB
A: 

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?

dmckee