Using CMake with Athena
Introduction
ATLAS uses CMake for building all of its offline/trigger/analysis software since 2018. CMake is a very generic framework for setting up the build of practically any software.
This current tutorial is not meant as a generic introduction into CMake, but rather as a cook-book for using CMake in ATLAS projects, most importantly in Athena.
For a lower-level introduction into the basics of CMake, consider going through the following materials:
- Tutorial from CMake authors
- Basic ATLAS CMake tutorial (internal)
- HSF CMake tutorial
- General ATLAS CMake documentation
Basics
Anatomy of an ATLAS Package
ATLAS's software is split into and organized as a list of "packages". A "package" is practically just a subdirectory in the directory structure of the atlas/athena repository, for example: AthExHelloWorld
By convention, packages have the following layout:
- One
CMakeLists.txt
file sits at the root of the sub-directory. This is the description to the build system of what to do with the package. - Public headers (for "the" shared library of the package) in the package are put into a sub-directory with the same name as that of the package. There could technically be further sub-directories inside of that directory, but it's not conventional to have them.
- Private headers and source files are put into a sub-directory called
src/
. - If the package builds a "component library", the source file needed for building that component library, is called
src/components/<PkgName>_entries.cxx
. - Python code included in the package is put into a sub-directory called
python/
. - Scripts are usually put into a sub-directory called
share/
. - Unit tests, compiled or otherwise, are put into a sub-directory called
test/
. - For compiled executables there is no real convention. Putting their sources into a dedicated sub-directory would be a good choice.
Generally, there is no strict requirement for a package to be laid out in one specific way. The CMakeLists.txt
configuration of the package can technically pick up source files / scripts / etc. from any place inside of the package's directory.
In most cases, if you are not sure how to do a particular thing, it's worth having a look at how other packages would have done it.
AtlasCMake
/ AtlasLCG
In order to make it easier to set up the build of thousands of packages consistently, we provide a layer of ATLAS specific CMake code that packages should use to do "standard" things.
The code for this is in atlasexternals/Build/AtlasCMake and
atlasexternals/Build/AtlasLCG. You can find the reference documentation for the code of AtlasCMake
here
In this tutorial we will use the functions documented on that page, without duplicating the documentation of the functions in this page.
Building a Project
So far we only talked about packages. But one never tells CMake to build a package by pointing at the package's directory. One must always rather build a project.
When working with the atlas/athena repository, this is practically always the Projects/WorkDir project that you should be using.
While CMake has many command line options, in the majority of the cases when working on ATLAS code, what you need to do is to create an empty "build directory" (the convention is to make a directory called build/
beside the athena/
directory), and from inside of it execute:
* in case of a partial checkout simply:
cmake ../athena/Projects/WorkDir/
make
- in case of a partial build, after setting up a
package_filters.txt
file (more on it later), with:
cmake -DATLAS_PACKAGE_FILTER_FILE=../package_filters.txt ../athena/Projects/WorkDir/
make
Exercises
The first 3 exercises use an external repository on GitHub. Have a look here.
You will need to first of all clone it locally, simply with:
git clone https://github.com/krasznaa/atlas-cmake-tutorial.git
For these first 3 exercises, you should in all cases just use the latest Athena nightly. I.e.
setupATLAS
asetup Athena,main,latest
Add a (Reflex) dictionary to a package
Please see Exercise1 for a description of the exercise.
Add compiled and scripted unit tests to a package
Please see Exercise2 for a description of the exercise.
Introduce a new LCG external dependency for a package
Please see Exercise3 for a description of the exercise.
Work with updated externals
One of the most advanced things to do on top of a nightly build is to make use of a new version of an external project. This includes trying out new versions of Gaudi, Geant4, GeoModel or Acts. This is something that only a small number of developers do regularly, so this is mostly about making you aware of this possibility.
You have already seen in Exercise 3 how to pick up a new external from an LCG release. Apart from picking up software from LCG releases, and picking up some libraries from the
system (from /usr
), Athena
receives a number of its externals from AthenaExternals.
While for some updates in these externals it is unavoidable to perform a full build of Athena by hand, some updates are possible by "only" re-building a finite number of packages
on top of a nightly, "in the correct way".
The method is the following: * You first need to build the new/different version of the external project. This can be done in a lot of different ways... In this exercise we will make use of AthenaExternals itself to perform this build. * Set up the desired Athena nightly, then set up the build of WorkDir with a command like:
CMAKE_PREFIX_PATH=/local/directory/with/external/install cmake -DATLAS_PACKAGE_FILTER_FILE=../package_filters.txt ../athena/Projects/WorkDir/
The build should in principle work find like this, as long as you select an appropriate list of packages to build. This list depends on the external that you're
testing like this. But to test the modified binaries in action, one needs to be even more verbose. Since the environment setup script generated by CMake
(setup.sh
) has a good chance of not generating a functional runtime environment for using the updated external. Requiring you, after sourcing the
setup.sh
script of your build directory, to manually add appropriate directories to the $LD_LIBRARY_PATH
, $PYTHONPATH
, etc.
environment variables in your shell.
The exercise from here on out shows how to build GeoModel "just so", and then build the DetectorDescription/GeoModel/GeoModelUtilities package against that local version of GeoModel.
- You will first need an up-to-date version of athena. Feel free to re-use the clone of your fork that you would've used on a previous day.
- Build GeoModel with the help of this local athena clone. To do that, you can use the Projects/Athena/build_externals.sh script like the following:
setupATLAS
asetup none,gcc13,cmakesetup --cmakeversion=3.29.5
./athena/Projects/Athena/build_externals.sh -t RelWithDebInfo -b $PWD/geomodel_build -c -x -DATLAS_GEOMODEL_SOURCE="GIT_REPOSITORY;https://gitlab.cern.ch/GeoModelDev/GeoModel.git;GIT_TAG;6.8.0" -k Package_GeoModel
- Now set up a
package_filters.txt
file with the following contents:
+ DetectorDescription/GeoModel/GeoModelUtilities
- .*
- After all of this, you can perform the build of the package in a new/separate terminal with:
setupATLAS
asetup Athena,main,latest
CMAKE_PREFIX_PATH=$PWD/geomodel_build/install/AthenaExternals/25.0.25/InstallArea/x86_64-el9-gcc13-opt:$CMAKE_PREFIX_PATH cmake -DATLAS_PACKAGE_FILTER_FILE=$PWD/package_filters.txt -S athena/Projects/WorkDir/ -B athena_build
VERBOSE=1 cmake --build ./athena_build/
Pay attention to the compilation and linking commands coming up in the latest command. Which should all point to your local GeoModel installation.