Working with a CPF Project¶
This page provides practical information on how to do the day to day tasks that arise when working with a CPF project. The text refers to an example project that can be retrieved from Github to illustrate the required steps.
Setting up the Environment¶
Before you start, you have to install the basic tools that are used by the CMakeProjectFramework.
Todo
Find out what must be installed by hand to compile the project.
Windows
Visual Studio 2017
Git
Python3
CMake 3.12.1
OpenCppCoverage (optional)
Doxygen
Linux
Gcc
Git
Python3
CMake 3.12.1
Clang (optional)
Valgrind (optional)
Doxygen
Git, Python and CMake should be callable from the command-line. (add them to PATH on Windows)
Cloning the Example Project¶
A CPF project must be based on a git repository. The CMake code relies on it when determining package versions and when handling packages that can be contained in git submodules. Because of the contained submodules the repository must be cloned by using:
> git clone --recursive https://github.com/Knitschi/ACPFTestProject.git
Configure, generate and build a CPF project¶
In order to build a freshly cloned CPF project, four commands need to be exectuted. Sadly this is a little more effort then the normal two steps (generate and build) that are used for a vanilla CMake project. The steps are implemented with the following Python 3 scripts.
0_CopyScripts.py: Copies the other scripts to the projects root directory.
1_Configure.py: Adds a CMake configuration for the project.
2_Generate.py: Create the make-files for a project configuration.
3_Make.py: Build the project.
If you have your operating system configured to run .py
files with python 3, you can omit the explicit call to python
in the following command line examples.
If this is not the case, make sure the python --version
call returns a 3.X version. On Linux you may need to use the command
python3
instead of python
.
The Copy Step¶
In order to execute the copy step run
...\ACPFTestProject> python Sources/CPFBuildscripts/0_CopyScripts.py
in the project root directory.
This step copies some python scripts into the project’s root directory. The scripts are provided by the CPFBuildscripts package. The scripts are only copied to shorten the command-line calls while working with the project. This step only needs to be executed once after cloning the repository.
The Configuration Step¶
In order to generate a configuration file run
.../ACPFTestProject> python 1_Configure.py VS --inherits VS2017-shared
on Windows or
.../ACPFTestProject> python3 1_Configure.py Gcc --inherits Gcc-shared-debug
on Linux in the project root directory.
The purpose of the configuration step is to create the Configuration/<config>.config.cmake
file that contains a set of CMake cache variables.
These variables determine things like the CMake generator, or which custom targets are included in the pipeline.
The config file is used instead of the usual variable definitions in the CMake generate step.
The name of the configuration (here VS
or Gcc
) can be chosen freely.
The --inherits
option determines a base configuration from which the created file inherits default values for all required variables.
The base configuration can be provided by the CPFCMake package or the projects CIBuildConfigurations
directory, which is the common
use case. Some of the values in the configuration file, like library locations or test file directories must be set to values that are
specific to the machine onto which the project was cloned.
After running the script you have the chance to edit the default values in the created configuration file in order to change the values to something
that is adequate for the local build. On a CI server it may sometimes be useful to set non default values of variables directly with the command line
call. This can be done by adding -D
options to the script call.
...\ACPFTestProject> 1_Configure.py VS --inherits VS2017-shared -D HUNTER_ROOT="C:/MyHunterLibs" -D CPF_TEST_FILES_DIR=="C:/Temp"
A project can have multiple configurations in parallel. This can be achieved by running the 1_Configure.py
script, multiple times
with different configuration names. However, if only one configuration is available, the configuration argument can be omitted
in the following generate and build steps.
Notes on the Configuration File Mechanism¶
The custom mechanism with the additional configuration file distinguishes the workflow of the CPF project from the
standard CMake command line workflow, where the configure and generate step are executed at the same time.
A disadvantage of CPF’s config file mechanism is that configuration information is duplicated in the config.cmake
file and the CMakeCache.txt
file.
The developer has to remember that instead of editing the CMakeCache.txt
file one now has to edit the config.cmake
file and then
re-execute the generate step.
The additional command line call may also come unexpected to developers who are used to work with normal CMake projects.
However, CMake itself provides a similar three step work-flow when using the CMake-GUI application.
Here the user can also change values of variables in the CMakeCache.txt
file before executing the generate step.
This indicates that there is a certain need for a three-step approach.
The CPF mechanism has some advantages over the two step work-flow which in my opinion outweigh the disadvantages.
Developers are relieved of remembering long lists of variable definitions that need to be typed whenever they need to re-generate the project. Especially when working on CMake code it becomes often necessary to delete the build directory in order to create fresh project. With the CPF mechanism the manual work of creating a project configuration is not lost when the build directory is deleted.
The project can define default configurations that are used by the projects CI job. This can be used to define officially supported compiler configurations and platforms.
The Generate Step¶
To execute the generate step run
...\ACPFTestProject> python 2_Generate.py VS
on Windows or
.../ACPFTestProject> python3 2_Generate.py Gcc
on Linux in the project root directory.
The generate step is the equivalent to the normally used cmake -H. -B_build -G"generator" -D...
call.
In fact running the command will print the underlying CMake command line.
The command creates the build-directory Generated/VS
that holds the generated make-files for the generator that is set
in the config file. In this example this is the Visual Studio solution for the Windows case and the make files
in the Linux case.
When the configuration argument is not given, the script will use the first configuration that is available in the
Configuration
directory.You can use the
--clean
option to delete the completeGenerated\<config>
directory before executing the generate step. This is sometimes necessary when an existing configuration is changed.
The Build Step¶
To execute a full build run
...\ACPFTestProject> python 3_Make.py VS --target pipeline --config Debug
on Windows or
.../ACPFTestProject> python3 3_Make.py Gcc --target pipeline
on Linux. This will compile the binaries as well as executing extra pipeline tasks like running the tests, do code analysis, generate the documentation or other steps that your project may have enabled via its configuration file.
When the configuration argument is not given, the script will use the first configuration for which the generate step was already executed.
Adding the
--clean
option will cause a complete rebuild instead of an incremental one.With the
--target
option one can specify which target should be build. During development this is useful if only a smaller part of the pipeline should be executed. Here is a list of available custom targets. If the--target
option is omitted completely, the script will only build the binary targets of the project.The
--config
option is only required for multi-configuration generators like Visual Studio. If it is not specified, theDebug
configuration will be build.
The Anatomy of a CPF Project¶
Now that you have built the project, it is time to take a look at the content of the test project.
The canonical Directory Structure¶
The CMakeProjectFramework enforces a fixed directory structure for the top level directories of the project. Here are the most important parts of that directory layout. Note that depending on the configuration that you built, not all of the shown directories and files will exist in your project. Many of the displayed directories do not exist in a freshly cloned CPF project.
ACPFTestProject
│ .gitignore
│ .gitmodules
│ 1_Configure.py
│ 2_Generate.py
│ 3_Make.py
│ README.rst
│ ... [other scripts that help with day to day tasks]
│
├───Configuration
│ VS.config.cmake
│ Gcc.config.cmake
│ ... [more configuration files]
│
├───Generated
│ ├───VS
│ │ │ CMakeCache.txt
│ │ │ CMakeGraphVizOptions.cmake
│ │ │ CPFDependencies.dot
│ │ │
│ │ ├───BuildStage
│ │ ├───html
│ │ ├───_CPF
│ │ ├───_pckg
│ │ ... [the usual CMake generated directories and files]
│ │
│ ├───Gcc
│ ... [more configuration directories]
│
└───Sources
│ CMakeLists.txt
│ packages.cmake
│
├───CIBuildConfigurations
│ cpfCiBuildConfigurations.json
│ VS2017-shared.config.cmake
│ Gcc-shared-debug.config.cmake
│ ... [more config files]
│
├───APackage
│ │ CMakeLists.txt
│ │ function.cpp
│ │ function.h
│ │ ... [more package source files]
│ │
│ ├───MyCustomDirectory
│ │ ... [source files in sub-directories]
│ │
│ ... [more package subdirectories]
│
├───BPackage
│
... [more package directories or global file directories]
The Root Directory¶
The ACPFTestProject
directory is the root directory of the project. This is the directory that you get when cloning a CPF project.
Most of the command line operations that are needed to handle the CPF project are executed in this directory. The directory contains
scripts to configure and build the project. It also contains the Sources, Configuration and Generated directories.
The Sources directory is stored in the repository, while the other two are generated when setting up the project.
The Sources Directory¶
The Sources directory contains all the files that are checked into the repository.
After cloning a CPF repository, this should be the only existing directory in the cpf-root-directory. The Sources directory contains
the root CMakeLists.txt
file of the repository, global files and directories for the packages that contain the payload code
of the project. There is a set of files that are in every CPF project.
CMakeLists.txt: The root
CMakeLists.txt
file creates the CI-project. This is the host project that contains the package projects that are created by the packagesCMakeLists.txt
files. The CPF dependencies are pulled in by including the cpfInit module. The Packages are added by calling the cpfAddPackages() function. Both are provided by the CPFCMake package.packages.cmake: This file defines a CMake variable that holds a list of package names that are
OWNED
by this CI-project or areEXTERNAL
packages. Owned means, that the CI-job that builds this repository is responsible for verifying that all automated checks for the package pass before it is marked with a version tag. More information about package ownership can be found here.CIBuildConfigurations: This directory provides the CI job with information about the project configurations that should be build by the CI job. These configurations are defined in files like
VS.config.cmake
which contain a set of CMake cache variables. More information about the config file mechanism can be found here.CIBuildConfigurations/cpfCiBuildConfigurations.json: A file that contains a list of configurations that are build by the projects CI job. This is only needed if the infrastructure provided by CPFMachines is used.
APackage: A directory that contains a package. The name of the package directory can be chosen by the user. It also defines the name of the main library, executable or custom target that is created by this package. A CPF project can have multiple package directories. The package directory contains all source files that belong to the package. These can hold the production code, test code or the package documentation. The package directory must contain a
CMakeLists.txt
file that calls the cpfInitPackageProject and one of thecpfAdd<X>Package()
functions. The directory structure within the package directory can be chosen freely. The relative directories of source files must be prepended when adding the files to the packagesCMakeLists.txt
file.
The Configuration Directory¶
The Configuration directory contains CMake files that define the locally used configurations of the project. This directory is
generated by calling the 1_Configure.py
script in the configuration step.
This directory is used to keep manually created project configurations out of the potentially short lived Generated directory.
The Generated Directory¶
The Generated directory contains all files that are generated by the generate- and build step. All contents of that directory can be deleted without loosing any manual work. However you will have to re-execute the generate and build step after deleting this directory.
The Generated directory contains one subdirectory for each configuration for which the generate step is executed. The configuration directories are the CMake build directories that contain the usual CMake generated files as well as some special directories that are created by the CMake code of the CPF.
CPF specific Build Directory Content:
Generated/<config>/html: The primary output directory of the project. It contains created package archives in the
Downloads
subdirectory. Thedoxygen
subdirectory contains the entry page of the generated project page, which leads to the documentation and other optionally generated html pages like coverage report.Generated/<config>/BuildStage: This directory contains all the binaries that are generated when building the project. When running an executable during debugging or automated testing, it is run from within this directory.
Generated/<config>/_CPF: A directory that is used for all internal files that are generated by the custom targets of the CPFCMake package. If everything goes well, the contents are only of interest when developing the CPFCMake package itself.
Generated/<config>/_pckg: A directory that is used to accumulate the contents of the created package archives. If everything goes well, the contents are only of interest when developing the CPFCMake package itself.
CI project, Package Projects and Package Ownership in Practice¶
The basic concepts page mentions, that the CPF wants to separate CI-functionality related cmake code from payload code.
In the repository this is reflected by the two layers of CMakeLists.txt
files. The CI-project is defined by
the root CMakeLists.txt
file in the Sources directory. The package projects are defined by the CMakeLists.txt
files
in the Sources/<package>
directories.
In the ACPFTestProject we have quite a number of packages. The packages APackage, CPackage, DPackage documentation and EPackage.
are listed in the Sources/packages.cmake
file, which defines them as owned packages. This means that
it is this CI-project’s responsibility to provide their official build pipeline that ensures that they build and work.
CPackage and documentation are fixed packages, which means that they are in the same repository as the CI-project. It is called fixed
because this fixes the package version to the version of the CI-project. The other owned packages are loose, because
they are pulled in via the git-submodule mechanism which allows them to have their version incremented independently
from the other packages.
The packages BPackage, CPFBuildscripts, CPFCMake, documentation, FPackage, GPackage and libSwitchWarningsOff are external packages. External packages are always pulled in via the git-submodule mechanism.
Common Git Operations on a CPF Project¶
Todo
Describe to most common git operations. (update of packages etc. )
Consuming Binary Library Packages created by a CPF Project¶
The cpfAddCppPackage allows you to create binary packages for your library targets.
These packages contain .cmake files that can be used by other CMake based projects to consume
your libraries with the find_package( ... CONFIG ... )
function.
Note
Currently binary packages with internal versions are not consumable by other CMake projects. This is because the standard package files do not know how to handle the internal version number format of the CPF.