SoftwareSphere Home

Aims of this project

The first aim of this project was, and is, to have a C++ build system that is simple, robust, extensible, understandable, logical and possibly portable.

Simplicity

We need a build system for C/C++ code but it has to be simple, as simpler as possible. We should not spend hours to configure a build, or to adjust the build options, or to try a build only to discover that we have selected the wrong compiler options.

Robustness

We need a robust way to define C++ modules and their builds. The files that describe the build process must be robust, which means that there must be very few different ways to obtain the same result, possibly a result should be associated to a unique content of this file.
The configurations must be defined in separate files because in this way they aren't affected by changes to the project file. The project file could be lost or deleted while the configuration files are still at safe.

Understandability

The file that define the project must also be easy to read and to understand, it absolutely should not be another program, with another set of syntax rules to know. Let's repeat this: the project file must not be a program itself, it must be a static data structure.
Almost all designers agree that for such kind of files XML is the best solution today, however we do not want to have enormous files, and we hate excessive markup in XML files. The XML used for this project is very lean, it uses attributes when it has sense, and uses nested elements to represent actual data structures not simple values.
The understandability applies also to the configuration files, which have to be easy to read and to understand. Of course the meaning of the compiler options can't be described completely in such files, but anyway the parameters must have a clear name, a type and a clear list of possible values, for the meaning of such values there is the compiler's documentation.

Maintainability

The file that define the C++ project must be easy to maintain, we must be able to modify it with a simple text editor, and without any risk. For the practical work we will provide a GUI tool that shows the data structure and rewrites this file, but we always have to be allowed to modify the file by hand, when the project editor is not available, or because we have to modify the file in a way that the GUI design didn't foresee.

Extensibility

Of course we would like to be able to extend the build system with new procedures. This is a quality that the good build systems available now already have. The problem is to have all the qualities in the same system. With extensibility we could add special tasks to a build. However we don't want to create an all-purpose tool, because there are already many, so the extensibility has a precise meaning for this system. For example we should be able to create pre-compilation tasks, pre-linking tasks, and post-linking tasks. For any other needs we prefer to use a generic task execution program.

Speed

The build system should be very efficient, it can not be slow. For example it must be as faster as possible when analyzing the source code dependencies. Of course it must be fast also when loading or saving project files with many modules or many source files listed.

Logicality

With logicality we mean that the project file has to have a logical structure, and there should not be the need of a detailed guide to the meaning of the tags and attributes. The attributes for example must have meaningful names, and the relations between the attributes of several elements must be logical and sensed. An example is the assignment of the output directory to a target: if a target has the "outputDir" attribute then its output directory is exactly the value of that attribute, but the target element can also be without such attribute, and in that case its output directory is established by the containing element, which can be a target group or the buildspace element. This reasoning holds also for several other attributes and element types.

Concepts of the current design

The design of our C++ build system is based upon the follolowing concepts.

Packages

A package is a container for a set of source code files that make a component. A package has a unique name inside the buildspace. Packages are not nestable, this fact makes them very different from the Java packages. The aim is to compile each package in a separate DLL, so since DLLs can't contain other DLLs we have decided that packages can't contain other packages. Anyway the C++ namespaces can still be used to organize the names of the classes and other parts of the source code. The meaning of the C++ namespaces is actually what the word means, a space of names, without constraints about the physical location of the program parts. A package is defined by its name and its list of source files. The source files of a package should be all in the same folder of the file system. It is also possible that two or more packages, sometimes all the packages of the buildspace, have their source files placed in the same folder, as long as the filenames are unique of course. A package has also some other attributes that are related to the source code organization.

Targets

A target is a binary file to build, and it can be an executable file, a static libraries or a DLL. Maybe we can define other types of targets too but these are the most important and common. A target is connected to a package, from which it takes the source files. Actually a target must have a package assigned to it or it would be empty. The name of the target is the title of the corresponding output file, which has extension according to the target type and the compiler used. A target could be built separately by giving the needed parameters to the compiler, but often there are more targets with the same compiler options so we require that a target is assigned to a bundle which holds the options for the building tools.

Bundles

A bundle is a collection of targets with the same builder and the same configuration. A bundle can contain both DLL targets and EXE targets or static library targets. The specific options for compiler or linker are stored in the separate file of the configuration. The bundle holds also the information about the destination folder and intermediate files folder. A typical bundle contains the complete set of targets made from all the packages of the buildspace with a particular builder and configuration.

Configurations

A configuration is a complete set of parameters for the build tools. The configuration however does not include the paths of input and output files, or the output folders, because those are given by the bundles and targets objects. A configuration is stored in a separate file and it is referred by means of its name, which is unique among the set of configurations of that particular builder. This is important to avoid to copy the compiler options in each project file. The files that define the configurations are easy to read and can also be edited by hand. Each configuration belongs to a particular builder and follows the builder's configuration definition or configuration template. The configuration tells the options of all the build tools, i.e. compiler, linker, other special compilers, librarian tool etc..

Configuration definitions

A configuration definition is the definition of the possible options of a particular build toolkit. It tells the names and types of the possible parameters, their default values and their possible values. Each builder has one and one only configuration definition, that is stored in a read-only file since it doesn't have to be modified once it has been prepared correctly. The structure of configuration definitions is enough flexible to describe all the available C++ compiler toolkits.

Builders

A builder represents a particular compiler toolkit, like Microsoft C/C++ compiler, GCC, Turbo C++, etc... A builder is defined by a set of files that contains the builder's configuration template, the builder's settings and properties, the builder's environment variables and possibly other data needed to make the builder work.
The most important use of the multiple builders is when you have to build a C++ program for different platforms. For example the Microsoft compiler can run only on a Windows platform, but the GNU compiler is available for both Windows and Linux so if you have to make a program for the Linux platform you can develop it on a Windows platform and build it with the Windows port of the GNU compiler.

Buildspaces

A buildspace is a collection of packages, targets and bundles, that have to be built together. The packages defined in a buildspace of course will have some dependency to each other, otherwise we can use completely separated buildspaces. The buildspace defines the limits of the set of source files that are scanned to find the dependencies. Only the source files that belong to the buildspace are examined. Since a buildspace can contain multiple targets and multiple bundles, we don't need to create multiple buildspaces, one buildspace it is enough for a software project, and different buildspaces are needed only for completely unrelated projects.