Project Structure and Build System#

A template project that is kept up-to-date with the recommendations given here is available in the CTAO Gitlab.

Build System#

C++ projects shall use the CMake build system generator. In short, it is expected that the following commands will configure, build and install the project:

$ cmake -S . -B build -DCMAKE_INSTALL_PREFIX=/path/to/install
$ cmake --build build
$ cmake --install build

Projects providing libraries must export their targets, so that they can be used by downstream projects via find_package(<project> CONFIG).

Projects shall use “modern CMake”, which is centered around targets and avoids global configuration. For a nice introduction of the concepts, see the :ref:recommended-resources section.

The recommended minimum required version of CMake is 3.17, which is the version in the cmake3 package on CentOS 7. Since current CMake releases are easily obtained on all operating systems and platforms, projects may require more recent versions of CMake than are available through the standard packages of the defined computing environments.

Project Structure#

As a short overview, C++ project should have the following structure:

CMakeLists.txt      # top-level cmake file
cli/                # directory for command-line tools
  CMakeLists.txt
cmake/              # CMake modules
docs/               # doxygen documentation
  CMakeLists.txt
extern/             # Directory for third-party dependencies in submodules
include/<project>/  # directory for header files
src/                # directory for .cpp library source files
  CMakeLists.txt
tests/              # unit tests
  CMakeLists.txt

The top-level CMakeLists.txt file should be as minimal as possible, declaring the project, configuration options, major targets and then including the different subdirectories.

Public headers are placed in an include/<project-name>/ directory, this avoids naming conflicts with system headers or other projects and the whole directory can easily be installed by CMake. Code using the headers should also #include "<projectname>/<header>", again to avoid naming conflicts.

For a more complete example, have a look at the template project.

Versioning#

As per the software programming standards, projects should use semantic versioning and version information must be available in the compiled products (libraries, executables). The template project contains a CMake module to determine version information from git tags and the main CMakeLists.txt is configuring a source file to include this information into libraries and executables.

Dependencies#

In most cases, dependencies should be included via find_package, as this allows using system libraries or shared installations, testing different versions, operation without internet access, etc.

Other mechanisms like using git submodules or CMake’s FetchContent mechanism should not be used. A notable exception is GoogleTest, which is recommended to be included as a git submodule via add_subdirectory. GoogleTest is required to be build from source, and it is only needed as for running the tests and not by downstream projects.

In case a dependency is not supported directly via find_package, because the creators of that dependency do not offer a cmake configuration or cmake doesn’t provide a find_package implementation, a Find<Dependency>.cmake script can be created. These scripts shall create IMPORTED targets, so that the modern cmake mechanism for linking dependencies can be used. Common cmake modules for CTAO computing are collected in the common/cmake repository, which can be included as a submodule into other projects.