C++ Build Systems - What to Use

C++ build systems

I've used SCons for over a year now and it's really cool. It's a full build system, not a build scripts generator.

It's Python based, and you write the SConstruct and SConscript (equivalent of Makefile) in Python which allows you to call any available library you may wish for and a much clearer syntax that what a Makefile authorize.

Since Python is crossplatform, so is SCons, no problem there.

It comes bundled with a good number of targets:

  • detects the binary available and automatically maps a number of extensions toward the correct binaries
  • detects the correct extensions for your objects/libraries depending on the OS, though you can override it
  • provides facilities for common operations (to be delayed after the build) like Move, Copy, Tar, and you can provide your own python scripts and hook them
  • works out of the box and yet provides many hooks of customization at every level

It's really efficient, and even proposes advanced features (like storing a hash of the preprocessed file in a sqlite db instead of using the timestamp) even though you decide of your strategy in the end.

It also offers dependencies cycle detection for free (something that definitely does not come with Makefiles) and the interface is generally just better / automated.

Did I say it was efficient ? Well it obviously allows for multiple jobs to be executed in parallel ;)

And it's also free, like in free drink, feel free to contribute ;)

I can only recommend it.

What is currently the best build system

I'd definitively put my vote up for premake. Although it is not as powerful as it's older brothers, it's main advantage is absurd simplicity and ease of use. Makes writing multi-compiler, multi-platform code a breeze, and natively generates Visual Studio solutions, XCode projects, Makefiles, and others, without any additional work needed.

Any good building tools for a C++ project, which can replace make?

The Google V8 JavaScript Engine is written in C++ and uses SCons, so I guess that's one vote for it.

Portable C++ build system

(-) demands a configuration file in every project folder

This is not correct, you just need to pass bigger pathes like:

add_program(foo src/foo.cpp src/main.cpp)

Few notes, about Boost.Jam - first it is not Boost.Jam - bjam on its own quite useless, what you are looking for is Boost.Build which is set of Jam macros that make bjam useful.

Now, I worked with both, and I must admit, Boost.Build is not suitable for any serious projects outside the Boost itself. Need to find library? Can't need to find header? Can't. Need to do something outside of simple build - and you have not idea how to do it as BB documentation... Totally useless and maybe cover 10% of BB. So most of cases you need to ask questions in BB mailing lists and...

So, if you have some complicated project - and you need to make something more then simple compile and link, stay away from Boost.Build.

So if you need to support MSVC I find today CMake as only feasible choice.

I don't tell that CMake is very good system, it has many problems but it is something
mostly suitable for cross platform development (if you need to support MSVC).

And if you don't care about MSVC and happy with MinGW... take a look on autotools as well.

About Scons - it is still less mature them CMake.

Build system for an embedded C/C++ project

The conventional way of achieving that would be to place the source code for each module into a separate directory. Each directory can contain all the source and header files for the module.

The public header for each module can be placed into a separate, common directory of headers. I'd probably use a symlink from the common directory to the relevant module directory for each header.

The compilation rules simply state that no module may include headers from other modules except for the headers in the common directory. This achieves the result that no module can include headers from another module - except for the public header (thus enforcing the private barriers).

Preventing cyclic dependencies automatically is not trivial. The problem is that you can only establish that there is a cyclic dependency by looking at several source files at a time, and the compiler only looks at one at a time.

Consider a pair of modules, ModuleA and ModuleB, and a program, Program1, that uses both modules.

base/include
ModuleA.h
ModuleB.h
base/ModuleA
ModuleA.h
ModuleA1.c
ModuleA2.c
base/ModuleB
ModuleB.h
ModuleB1.c
ModuleB2.c
base/Program1
Program1.c

When compiling Program1.c, it is perfectly legitimate for it to include both ModuleA.h and ModuleB.h if it makes use of the services of both modules. So, ModuleA.h cannot complain if ModuleB.h is included in the same translation unit (TU), and neither can ModuleB.h complain if ModuleA.h is included in the same TU.

Let us suppose it is legitimate for ModuleA to use the facilities of ModuleB. Therefore, when compiling ModuleA1.c or ModuleA2.c, there can be no issue with having both ModuleA.h and ModuleB.h included.

However, to prevent cyclic dependencies, you must be able to prohibit the code in ModuleB1.c and ModuleB2.c from using ModuleA.h.

As far as I can see, the only way to do this is some technique that requires a private header for ModuleB that says "ModuleA is already included" even though it isn't, and this is included before ModuleA.h is ever included.

The skeleton of ModuleA.h will be the standard format (and ModuleB.h will be similar):

#ifndef MODULEA_H_INCLUDED
#define MODULEA_H_INCLUDED
...contents of ModuleA.h...
#endif

Now, if the code in ModuleB1.c contains:

#define MODULEA_H_INCLUDED
#include "ModuleB.h"
...if ModuleA.h is also included, it will declare nothing...
...so anything that depends on its contents will fail to compile...

This is far from automatic.

You could do an analysis of the included files, and require that there is a loop-less topological sort of the dependencies. There used to be a program tsort on UNIX systems (and a companion program, lorder) which together provided the services needed so that a static (.a) library could be created that contained the object files in an order that did not require rescanning of the archive. The ranlib program, and eventually ar and ld took on the duties of managing the rescanning of a single library, thus making lorder in particular redundant. But tsort has more general uses; it is available on some systems (MacOS X, for instance; RHEL 5 Linux too).

So, using the dependency tracking from GCC plus tsort, you should be able to check whether there are cycles between modules. But that would have to be handled with some care.

There may be some IDE or other toolset that handles this stuff automatically. But normally programmers can be disciplined enough to avoid problems - as long as the requirements and inter-module dependencies are carefully documented.

What do companies use to build their binaries?

A lot use CMake these days, which auto-generates makefiles for various platforms. Unfortunately CMake has its own (weird) internal language, which is why I personally prefer SCons - anything it can't do naturally can be easily added - after all it's just Python code. Take a look at the list of OS projects using SCons. Many of them are quite large and non-trivial multi-platform builds.

Build system output folder structuring

You can do whatever you want with makefiles, but since you ask about cmake, the only way to do it is to run the build from the build folder. In other words, you do this (assuming that you have SolutionDir/CMakeLists.txt):

cd SolutionDir
mkdir Builds
cd Builds
cmake ..
make -j8

(or whatever make command that you want). The Builds directory can be anywhere you want, it doesn't have to be within SolutionDir. You pass the directory containing the CMakeLists.txt file to cmake.



Related Topics



Leave a reply



Submit