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.
Recommended Resources#
Books#
Modern CMake for C++, Rafał Świdziński, Packt Publishing, ISBN 978-1-80107-005-8
Professional CMake: A Practical Guide, 15th Ed., Craig Scott