This documentation is a documentation for Gaudi-based project maintainers. If you are only a user or a developer, you should have a look at GaudiCMake315Configuration This file is not here to describe what can be done with the configuration but rather why the configuration is like this and what you should be aware of before trying to modify it.

This documentation is being written right after the modernization of Gaudi itself. At that time CMake 3.15 was just released.

Introduction

In this twiki page we are going to explain in details what the configuration of Gaudi is made of. For details about how to use CMake or how to configure a project that uses CMake, see GaudiCMake315Configuration.

If you need information on CMake you can either use the official latest documentation or the memento in appendix.

Future work (to be updated)

  • In Gaudi
    • try GaudiRelease in Gaudi
      • generate doxygen documentation
      • add a job in GitLab CI to automate it
      • set correct sonames for shared objects (libraries) depending on ABI changes. see Wikipedia and CMake. set_property(TARGET PROPERTY SOVERSION <x.y.z>) would tell CMake to set the soname of the .so file but will also build a .so.x.y.z file and a .so symlink that points to it (no hardcoded path, the one in the current directory is pointed)
    • remove no longer needed stuff in the ci-utils/ folder
    • add a job in GitLab CI to check the formatting (with clang-format). Current error of the script is "sed: can't read cmake/GaudiProjectConfig.cmake: No such file or directory" because the file has been removed.
  • From Gaudi to CVMFS
    • move the folder cmake/toolchains/ and the modules CcacheOption.cmake and DeveloperBuildType.cmake somewhere on CVMFS. Any project could use these toolchains and modules, not only Gaudi.
  • Refactored all LHCb's Gaudi-based projects: LHCb, Lbcom, Rec, Phys, Analysis Brunel, Moore, DaVinci...
    • with a script (see appendix) or by hand or anything else

Wishlist from third-party players

  • From Boost
    • BoostConfig.cmake handles Boost_USE_STATIC_LIBS correctly * remove set(BUILD_SHARED_LIBS ON) from GaudiDependencies.cmake if it happens * BoostConfig.cmake takes QUIET argument into account * BoostConfig.cmake has a way to set the version of the python component
      • apparently only 2.7 is available at the moment (Boost 1.70) * BoostConfig.cmake marks Boost_DIR and boost__DIR as advanced
      • remove all mark_as_advanced() for them in GaudiDependencies.cmake if it happens
  • From ROOT
    • ROOTConfig.cmake exposes the C++ standard used to compile ROOT * in ROOT_CXX_STANDARD for example, then remove the parsing of ROOT_CXX_FLAGS in CMakeLists.txt at the top-level of Gaudi * ROOTConfig.cmake displays a message when successfully found
      • remove the message() in GaudiDependencies.cmake if it happens
  • From LCG
    • when installing TBB, also install (or do not remove) the official TBBConfig.cmake * remove FindTBB.cmake from Gaudi if it happens

Conventions and good practices

  • In this twiki page "we" will refer to the authors (package maintainers) and "you" to the reader (developer/user). "I" refers to the first author.
  • A CMake option or cached variable (may be set by the user at configure time) must be all capital. e.g. GAUDI_USE_CPPUNIT
  • Always keep in mind that a project can be built standalone or as a part of a stack of projects. Be careful when using (CMAKE_SOURCE_DIR, PROJECT_SOURCE_DIR, CMAKE_CURRENT_SOURCE_DIR, CMAKE_PROJECT_NAME, PROJECT_NAME...)
  • If you do not know which conventions to follow when editing a file, look at another file that has the same purpose and follow the same style.
  • CMake files and functions should be documented in rst. It is the format chosen by Kitware to document CMake itself. You may have a look at how CMake modules are documented cat $(dirname $(which cmake))/../share/cmake-3.15/Modules/FeatureSummary.cmake A web documentation is generated from this comment with sphinx.
SyntaxHighlightingPlugin: Language cmake is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.
#[========================================================================[.rst:
Title
-----

Description...

Sub-title
^^^^^^^^^

Stuff...

#]========================================================================]
  • In files that can be used in any projects, it is a good idea to make sur that the CMake policy is recent enough.
SyntaxHighlightingPlugin: Language cmake is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.
cmake_policy(PUSH)
cmake_policy(VERSION 3.15)
# ... code ...
cmake_policy(POP)
  • In CMake tests, SRC_DIR and BIN_DIR refer to the values of PROJECT_SOURCE_DIR and PROJECT_BINARY_DIR in Gaudi. They do not have the same name because these variables will exists and have different values in the scope of the test.
  • Test names should be PackageName.TestName
  • Dependencies of a target should only be direct dependencies of it. Let CMake handle the transitivity.
  • Unfortunately there can only be one directory that contains QMTest tests, thus .qmt files cannot be configure files (.qmt.in).

Important files and directories

build.$BINARY_TAG/

is the usual name given to the build tree

cmake/

contains all the files related to CMake

modules/

contains all the find-module-files (Find???.cmake) needed in Gaudi and Gaudi-based prjects. In principle this file should also contain the modules (used with include() in CMakeLists.txt=s). This way the =CMAKE_MODULE_PATH can contain one less entry. In Gaudi's case, the two modules should be moved so it is temporary. See Future work.

In principle these find-module files could also be moved elsewhere (on CVMFS), this way they could be used by any project (not only Gaudi-based projects). That is what CMake does with the folder cmake_install_folder/share/cmake-3.15/Modules/ which contains all the modules and find-module files.

If you are already worrying because CMAKE_MODULE_PATH is not yet and environment variable like CMAKE_PREFIX_PATH, the solution is to add at the beginning of GaudiDependencies.cmake SyntaxHighlightingPlugin: Language cmake is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.

file(TO_CMAKE_PATH "$ENV{CMAKE_MODULE_PATH}" _module_path_list)
list(APPEND CMAKE_MODULE_PATH ${_module_path_list}) # because in CMake environment variables are used after variables

FindCppUnit.cmake a good example of a find-module file

SyntaxHighlightingPlugin: Language cmake is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.
# - Locate CppUnit library
# Defines:
#
#  CPPUNIT_FOUND
#  CPPUNIT_INCLUDE_DIR
#  CPPUNIT_INCLUDE_DIRS (not cached)
#  CPPUNIT_LIBRARY
#  CPPUNIT_LIBRARIES (not cached)
Original documentation SyntaxHighlightingPlugin: Language cmake is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.
# Imports:
#
#  CppUnit::cppunit
#
# Usage of the target instead of the variables is advised
Updated documentation SyntaxHighlightingPlugin: Language cmake is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.
# Find quietly if already found before
if(DEFINED CACHE{CPPUNIT_INCLUDE_DIR})
  set(${CMAKE_FIND_PACKAGE_NAME}_FIND_QUIETLY YES)
endif()
This is new. The reason behind this it that once the package has been found all the other configurations will not redo the lookup so the package will be "found" in the same location each and every time so there is no need to show it to the developer.

The following lines are the old content of this file. SyntaxHighlightingPlugin: Language cmake is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.

find_path(CPPUNIT_INCLUDE_DIR cppunit/Test.h)
find_library(CPPUNIT_LIBRARY NAMES cppunit)
We look for the include directory and the libraries we will link against. SyntaxHighlightingPlugin: Language cmake is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.
set(CPPUNIT_INCLUDE_DIRS ${CPPUNIT_INCLUDE_DIR})

# handle the QUIETLY and REQUIRED arguments and set CPPUNIT_FOUND to TRUE if
# all listed variables are TRUE
INCLUDE(FindPackageHandleStandardArgs)
FIND_PACKAGE_HANDLE_STANDARD_ARGS(CppUnit DEFAULT_MSG CPPUNIT_INCLUDE_DIR CPPUNIT_LIBRARY)
Using FindPackageHandleStandardArgs is the best thing that can be done in a find-module file. SyntaxHighlightingPlugin: Language cmake is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.
mark_as_advanced(CPPUNIT_FOUND CPPUNIT_INCLUDE_DIR CPPUNIT_LIBRARY)

set(CPPUNIT_LIBRARIES ${CPPUNIT_LIBRARY} ${CMAKE_DL_LIBS})
Marking variables as advanced is also a good thing because it will not pollute the user's options in ccmake or cmake-gui.

The end of the file is new. SyntaxHighlightingPlugin: Language cmake is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.

# Modernisation: create an interface target to link against
if(TARGET CppUnit::cppunit)
    return()
endif()
If an imported target already exists we cannot create an other one. It may be created by a previous call to find_package(CppUnit). SyntaxHighlightingPlugin: Language cmake is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.
if(CPPUNIT_FOUND)
  add_library(CppUnit::cppunit IMPORTED INTERFACE)
The target can either be a SHARED IMPORTED target with its IMPORTED_LOCATION set or be an INTERFACE target that will have INTERFACE property for transitivity of libraries and include directories. SyntaxHighlightingPlugin: Language cmake is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.
target_include_directories(CppUnit::cppunit SYSTEM INTERFACE "${CPPUNIT_INCLUDE_DIRS}")
Headers are SYSTEM to silence their warnings. SyntaxHighlightingPlugin: Language cmake is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.
target_link_libraries(CppUnit::cppunit INTERFACE "${CPPUNIT_LIBRARIES}")
  # Display the imported target for the user to know
  if(NOT ${CMAKE_FIND_PACKAGE_NAME}_FIND_QUIETLY)
    message(STATUS "  Import target: CppUnit::cppunit")
  endif()
endif()
Here tell the user which imported targets are available. SyntaxHighlightingPlugin: Language cmake is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.
if(COMMAND __deprecate_var_for_target)
We may or may not use this find-module file in a Gaudi-based project which means that we may or may not have access to functions defined in GaudiToolbox. SyntaxHighlightingPlugin: Language cmake is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.
foreach(v IN ITEMS CPPUNIT_INCLUDE_DIR CPPUNIT_INCLUDE_DIRS CPPUNIT_LIBRARY CPPUNIT_LIBRARIES)
    variable_watch(${v} __deprecate_var_for_target)
  endforeach()
endif()
This will prompt the user not to use variables because an imported target is now provided.

FindTBB.cmake

should be removed in a near future. It was taken from the internet and then modified. It is in the public domain.

tests/

contains all the unit tests related to CMake and Gaudi's configuration itself.

dummyGaudiDownstreamProject/

is a "hello world!" project built on the top of Gaudi. It tries #include <Gaudi/PluginService.h> which comes from the package GaudiPluginService and #include <DUMMYGAUDIDOWNSTREAMPROJECT_VERSION.h> and #include <MainVersion.h> which are generated. Explanation of its CMakeLists.txt: SyntaxHighlightingPlugin: Language cmake is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.
if(NOT TARGET Gaudi::GaudiPluginService)
   find_package(Gaudi REQUIRED) # Imports all the targets and functions and find the dependencies
endif()
This is here to be able to build a downstream project on top of an installed Gaudi or as part of a full stack. SyntaxHighlightingPlugin: Language cmake is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.
# Set the C++ standard
  set(CMAKE_CXX_STANDARD ${GAUDI_CXX_STANDARD})
  set(CMAKE_CXX_STANDARD_REQUIRED TRUE)
  set(CMAKE_CXX_EXTENSIONS OFF)
This is here to use the same C++ standard as the one used to build Gaudi. SyntaxHighlightingPlugin: Language cmake is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.
gaudi_add_executable(mainexe SOURCES src/main.cpp LINK Gaudi::GaudiPluginService)
Links against a target defined by Gaudi (a shared library) either imported or built in the full stack (in this case the alias is used). SyntaxHighlightingPlugin: Language cmake is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.
gaudi_generate_version_header_file() # generate DUMMYGAUDIDOWNSTREAMPROJECT_VERSION.h
  gaudi_generate_version_header_file(Main) # generate MainVersion.h
  target_include_directories(mainexe PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/include)
Here we check that we can use a gaudi_*() function and in src/main.cpp we check that we have access to what they define. SyntaxHighlightingPlugin: Language cmake is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.
###################### Simple unit tests beyond this line ######################
# Check validity of GAUDI_CXX_STANDARD
if(NOT GAUDI_CXX_STANDARD)
    message(FATAL_ERROR "GAUDI_CXX_STANDARD is not set or empty.")
endif()
# Check that we have a run target
  if(NOT TARGET run)
      message(FATAL_ERROR "There is no run target.")
  endif()
# Targets needed for gaudi_add_module()
  foreach(target IN ITEMS Gaudi::listcomponents Gaudi::genconf)
      # Check they are targets
      if(NOT TARGET ${target})
          message(FATAL_ERROR "${target} is not a target.")
      endif()
      # Check they exist on the disk if not build by the project
      get_target_property(target_imported ${target} IMPORTED)
      if(target_imported)
          get_target_property(loc ${target} LOCATION)
          if(NOT EXISTS "${loc}")
              message(FATAL_ERROR "${target} is imported from ${loc} but the file does not exist.")
          endif()
      endif()
  endforeach()
This CMakeLists.txt also contains some unit tests because in the end it is the only purpose of this project. We test if the C++ standard used to compile Gaudi is exported, to check that an IMPORTED target called "run" exist (it wraps the call to the run script), to check that executable targets needed by gaudi_add_module() are imported and exist at the expected location.

dummyLHCbFullStack/

contains a CMakeLists.txt that describe the build of a stack composed of Gaudi and dummyGaudiDownstreamProject. SyntaxHighlightingPlugin: Language cmake is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.
cmake_minimum_required(VERSION 3.15)
  project(dummyLHCbFullStack)
  # Add the projects of the stack (from bottom to top)
  add_subdirectory(${SRC_DIR} ${BIN_DIR}/dummyLHCbFullStack/Gaudi)
  add_subdirectory(${SRC_DIR}/cmake/tests/dummyGaudiDownstreamProject ${BIN_DIR}/dummyLHCbFullStack/dummyGaudiDownstreamProject)
In this file, we can see the minimal stuff needed for a CMake project configuration. There is a cmake_minimum_required() followed by a project() and the list of the projects of the stack from the bottom to the top (the order of the configuration). Here we use the signature add_subdirectory(source_dir binary_dir) because the source directory is given with an absolute pass because it is not a subdirectory of the directory of the CMakeLists.txt. We can notice that, the binary_dir of Gaudi is different to the "real" binary_dir of Gaudi. Since the configuration for a full stack differ from the configuration standalone, we use another build tree not to mess up with the "real" build tree. The drawback though, is that it takes way longer, because everything needs to be recompiled (using ccache is advised).

dummyProject/

contains a project designed to test as much as possible the gaudi_*() outside Gaudi. The goal is that if the tests defined in this folder fail, one or more gaudi_*() function is bugged. If the tests defined in this folder pass but the configuration fails, it means that the configuration (the code in one or more CMakeLists.txt) does not use the functions correctly. The architecture of the directory is standard. SyntaxHighlightingPlugin: Language cmake is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.
add_executable(Gaudi::listcomponents IMPORTED)
  set_target_properties(Gaudi::listcomponents PROPERTIES IMPORTED_LOCATION "${CMAKE_CURRENT_SOURCE_DIR}/scripts/fakelistcomponents.sh")
  add_executable(Gaudi::genconf IMPORTED)
  set_target_properties(Gaudi::genconf PROPERTIES IMPORTED_LOCATION "${CMAKE_CURRENT_SOURCE_DIR}/scripts/fakegenconf.sh")
The CMakeLists.txt contains calls to the main gaudi_*() functions (we would have wished it contain calls to all without any modification but in the end we would have ended up rewriting another Gaudi). Here we emulate some programs defined in Gaudi because the dummy project is not built on top of Gaudi (and that is the purpose). Should the arguments given to these programs (listcomponents and genconf) change, their fake implementation would need to be updated.

testGaudiDownstream.cmake, testGaudiInstallation.cmake, testGaudiToolbox.cmake

are modules that add unit tests to CTest when included. They all follow the same principle: they have a big switch (if, elseif...) in them that will launch a different test depending on the definition of a variable. This enables us to have all the tests related to a given purpose declared (with add_test()) and defined (the cmake code of the test) in one place. SyntaxHighlightingPlugin: Language cmake is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.
if(DEFINED TEST_SOMETHING)
    # code of one test
elseif(DEFINED TEST_SOMETHING_ELSE)
    # code of another test
else()
    # declaration of the tests with add_test()
    add_test(NAME cmake.test_something
             COMMAND ${CMAKE_COMMAND} -D TEST_SOMETHING:BOOL=TRUE
                                      # other options
                                      -P ${CMAKE_CURRENT_LIST_FILE}) # this file
  set_tests_properties(cmake.test_something PROPERTIES
       DEPENDS cmake.test_a
       FIXTURES_REQUIRED cmake.test_a
       FIXTURES_SETUP cmake.test_something
       LABELS CMake)
endif()
We register a test called "cmake.test_something". To run this test, CTest must run CMake in script mode on the current file and defined the boolean variable TEST_SOMETHING to TRUE enabling the first if to be true, thus running its code. DEPENDS and FIXTURES_REQUIRED prevent tests to be run concurrently and mess with the other. FIXTURES_SETUP will enable other tests to do the same. LABELS enables the use of -L when running CTest.

testGaudiDownstream.cmake

cmake.test_dummyGaudiDownstreamProject
tries to build and run the dummyGaudiDownstreamProject from an installed Gaudi. SyntaxHighlightingPlugin: Language cmake is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.
file(REMOVE_RECURSE ${BIN_DIR}/dummyGaudiDownstreamProject)
This removes a previously built dummyGaudiDownstreamProject. SyntaxHighlightingPlugin: Language cmake is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.
if(CMAKE_TOOLCHAIN_FILE)
   set(toolchain "-DCMAKE_TOOLCHAIN_FILE=${CMAKE_TOOLCHAIN_FILE}")
endif()
This is because a toolchain may or may not have been passed to the test (the tests command uses a generator expression). SyntaxHighlightingPlugin: Language cmake is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.
execute_process(COMMAND ${CMAKE_COMMAND} -E env CMAKE_PREFIX_PATH=${GAUDI_INSTALL_DIR}:$ENV{CMAKE_PREFIX_PATH}
                        ${CMAKE_COMMAND}
                        -S ${CMAKE_CURRENT_LIST_DIR}/dummyGaudiDownstreamProject
                        -B ${BIN_DIR}/dummyGaudiDownstreamProject
                        -G ${CMAKE_GENERATOR}
                        -D CMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER}
                        ${toolchain}
                        RESULT_VARIABLE returned_value
                        COMMAND_ECHO STDOUT)
if(NOT returned_value EQUAL "0")
   message(FATAL_ERROR "Configuration of the downstream project failed.")
endif()
Here we try to build the dummy project. We do not use the run script. We only specify the install location of Gaudi and its build environment. The build tree is <GaudiBuildTree>/dummyGaudiDownstreamProject. We use the same compiler and generator as the ones used for Gaudi. We use a toolchain if one was specified. The configure command will be printed on stdout (can be seen if we use CTest in verbose mode) for an easier debugging. Finally, we test the returned value to check that the configuration is OK. SyntaxHighlightingPlugin: Language cmake is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.
execute_process(COMMAND ${CMAKE_COMMAND} --build
                        ${BIN_DIR}/dummyGaudiDownstreamProject
                        RESULT_VARIABLE returned_value
                        COMMAND_ECHO STDOUT)
if(NOT returned_value EQUAL "0")
   message(FATAL_ERROR "Compilation of the downstream project failed.")
endif()
Here we build. Same thing with the returned value. SyntaxHighlightingPlugin: Language cmake is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.
# Try to execute
execute_process(COMMAND ${BIN_DIR}/dummyGaudiDownstreamProject/run
                        ${BIN_DIR}/dummyGaudiDownstreamProject/mainexe
                        RESULT_VARIABLE returned_value
                        COMMAND_ECHO STDOUT)
if(NOT returned_value EQUAL "0")
   message(FATAL_ERROR "Execution of the downstream project failed.")
endif()
In the end, we try to run the built software with its own run script. Same thing with the returned value.

cmake.test_dummyGaudiDownstreamProject_from_build_tree
reconfigure Gaudi with GAUDI_BUILD_TREE_AS_INSTALL_AREA=ON and does not recompile Gaudi because there is no need to do so, then tries to build and run the dummyGaudiDownstreamProject from the build tree of Gaudi. SyntaxHighlightingPlugin: Language cmake is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.
# Save the cache
file(MAKE_DIRECTORY ${BIN_DIR}/bck)
file(COPY ${BIN_DIR}/CMakeCache.txt DESTINATION ${BIN_DIR}/bck)
file(COPY ${BIN_DIR}/CTestTestfile.cmake DESTINATION ${BIN_DIR}/bck)
file(COPY ${BIN_DIR}/cmake_install.cmake DESTINATION ${BIN_DIR}/bck)
file(COPY ${BIN_DIR}/GaudiConfig.cmake DESTINATION ${BIN_DIR}/bck)
if(CMAKE_TOOLCHAIN_FILE)
    set(toolchain "-DCMAKE_TOOLCHAIN_FILE=${CMAKE_TOOLCHAIN_FILE}")
endif()
Here we save all the files that will be modified by the reconfiguration of Gaudi. We reconfigure because we do not want to rebuilt Gaudi from scratch for this test but we do not want to modify the configuration of the user. These files will be restored at the end of the test. WARNING: if the test unexpectedly end (with Ctrl-C for instance), these files are not restored. Thus the configuration would be a mess. You would need to reconfigure. Same thing with the toolchain as in the previous test. SyntaxHighlightingPlugin: Language cmake is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.
execute_process(COMMAND ${CMAKE_COMMAND}
                    -D GAUDI_BUILD_TREE_AS_INSTALL_AREA=ON
                    -S ${SRC_DIR}
                    -B ${BIN_DIR}
                    -G ${CMAKE_GENERATOR}
                    -D CMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER}
                    ${toolchain}
                RESULT_VARIABLE returned_value
                COMMAND_ECHO STDOUT)
if(NOT returned_value EQUAL "0")
    file(RENAME ${BIN_DIR}/bck/CMakeCache.txt ${BIN_DIR}/CMakeCache.txt) # revert to old cache
    file(RENAME ${BIN_DIR}/bck/CTestTestfile.cmake ${BIN_DIR}/CTestTestfile.cmake)
    file(RENAME ${BIN_DIR}/bck/cmake_install.cmake ${BIN_DIR}/cmake_install.cmake)
    file(RENAME ${BIN_DIR}/bck/GaudiConfig.cmake ${BIN_DIR}/GaudiConfig.cmake)
    message(FATAL_ERROR "Reconfiguration of Gaudi failed")
endif()
Here we reconfigure Gaudi with GAUDI_BUILD_TREE_AS_INSTALL_AREA=ON. If the reconfiguration fails, the files are restored. SyntaxHighlightingPlugin: Language cmake is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.
execute_process(COMMAND ${CMAKE_COMMAND} -E env CMAKE_PREFIX_PATH=${BIN_DIR}:$ENV{CMAKE_PREFIX_PATH}
                        ${CMAKE_COMMAND}
                        -S ${CMAKE_CURRENT_LIST_DIR}/dummyGaudiDownstreamProject
                        -B ${BIN_DIR}/dummyGaudiDownstreamProjectFromBuildTree
                        -G ${CMAKE_GENERATOR}
                        -D CMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER}
                        ${toolchain}
                        RESULT_VARIABLE returned_value
                        COMMAND_ECHO STDOUT)
if(NOT returned_value EQUAL "0")
    file(RENAME ${BIN_DIR}/bck/CMakeCache.txt ${BIN_DIR}/CMakeCache.txt) # revert to old cache
    file(RENAME ${BIN_DIR}/bck/CTestTestfile.cmake ${BIN_DIR}/CTestTestfile.cmake)
    file(RENAME ${BIN_DIR}/bck/cmake_install.cmake ${BIN_DIR}/cmake_install.cmake)
    file(RENAME ${BIN_DIR}/bck/GaudiConfig.cmake ${BIN_DIR}/GaudiConfig.cmake)
    message(FATAL_ERROR "Configuration of dummyGaudiDownstreamProject using the build tree of Gaudi failed.")
endif()
Here we configure dummyGaudiDownstreamProject the same way as in the previous test but we give the path to Gaudi build tree instead of an install location. SyntaxHighlightingPlugin: Language cmake is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.
execute_process(COMMAND ${CMAKE_COMMAND} --build
                        ${BIN_DIR}/dummyGaudiDownstreamProjectFromBuildTree
                        RESULT_VARIABLE returned_value
                        COMMAND_ECHO STDOUT)
if(NOT returned_value EQUAL "0")
    file(RENAME ${BIN_DIR}/bck/CMakeCache.txt ${BIN_DIR}/CMakeCache.txt)
    file(RENAME ${BIN_DIR}/bck/CTestTestfile.cmake ${BIN_DIR}/CTestTestfile.cmake)
    file(RENAME ${BIN_DIR}/bck/cmake_install.cmake ${BIN_DIR}/cmake_install.cmake)
    file(RENAME ${BIN_DIR}/bck/GaudiConfig.cmake ${BIN_DIR}/GaudiConfig.cmake)
    message(FATAL_ERROR "Build of dummyGaudiDownstreamProject using the build tree of Gaudi failed")
endif()
# Try to execute
execute_process(COMMAND ${BIN_DIR}/dummyGaudiDownstreamProjectFromBuildTree/run
                        ${BIN_DIR}/dummyGaudiDownstreamProjectFromBuildTree/mainexe
                        RESULT_VARIABLE returned_value
                        COMMAND_ECHO STDOUT)
# Reset cached variables
file(RENAME ${BIN_DIR}/bck/CMakeCache.txt ${BIN_DIR}/CMakeCache.txt)
file(RENAME ${BIN_DIR}/bck/CTestTestfile.cmake ${BIN_DIR}/CTestTestfile.cmake)
file(RENAME ${BIN_DIR}/bck/cmake_install.cmake ${BIN_DIR}/cmake_install.cmake)
file(RENAME ${BIN_DIR}/bck/GaudiConfig.cmake ${BIN_DIR}/GaudiConfig.cmake)
if(NOT returned_value EQUAL "0")
    message(FATAL_ERROR "Execution of dummyGaudiDownstreamProject using the build tree of Gaudi failed.")
endif()
Here we build and try to execute. Same thing as in the previous test and same thing about file restoration.

cmake.test_dummyGaudiDownstreamProject_full_stack
tries to build and run the project dummyLHCbFullStack. SyntaxHighlightingPlugin: Language cmake is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.
if(CMAKE_TOOLCHAIN_FILE)
    set(toolchain "-DCMAKE_TOOLCHAIN_FILE=${CMAKE_TOOLCHAIN_FILE}")
endif()
Same thing with the toolchain. SyntaxHighlightingPlugin: Language cmake is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.
execute_process(COMMAND ${CMAKE_COMMAND}
                        -D SRC_DIR=${SRC_DIR}
                        -D BIN_DIR=${BIN_DIR}
                        -S ${CMAKE_CURRENT_LIST_DIR}/dummyLHCbFullStack
                        -B ${BIN_DIR}/dummyLHCbFullStack
                        -G ${CMAKE_GENERATOR}
                        -D CMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER}
                        ${toolchain}
                RESULT_VARIABLE returned_value
                COMMAND_ECHO STDOUT)
if(NOT returned_value EQUAL "0")
    message(FATAL_ERROR "Configuration of dummyLHCbFullStack failed")
endif()
Here instead of configuring dummyGaudiDownstreamProject, we configure dummyLHCbFullStack that will configure both Gaudi and dummyGaudiDownstreamProject. SyntaxHighlightingPlugin: Language cmake is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.
execute_process(COMMAND ${CMAKE_COMMAND} --build
                        ${BIN_DIR}/dummyLHCbFullStack
                        RESULT_VARIABLE returned_value
                        COMMAND_ECHO STDOUT)
if(NOT returned_value EQUAL "0")
    message(FATAL_ERROR "Build of dummyLHCbFullStack failed")
endif()
# Try to execute
execute_process(COMMAND ${BIN_DIR}/dummyLHCbFullStack/run
                        ${BIN_DIR}/dummyLHCbFullStack/dummyGaudiDownstreamProject/mainexe
                        RESULT_VARIABLE returned_value
                        COMMAND_ECHO STDOUT)
if(NOT returned_value EQUAL "0")
    message(FATAL_ERROR "Execution of dummyLHCbFullStack failed.")
endif()
Like in the other tests, we try to build and run the software. This test is long because it does not use the build tree of Gaudi not to mess with it.

In the end, tests defined in this file are pretty useless because the dummyGaudiDownstreamProject is a dummy project and does not really use features from Gaudi. Trying to build LHCb is a better test. SyntaxHighlightingPlugin: Language cmake is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.

add_test(NAME cmake.test_dummyGaudiDownstreamProject
         COMMAND ${CMAKE_COMMAND} -D TEST_GAUDI_DOWNSTREAM:BOOL=TRUE
                                  -D GAUDI_INSTALL_DIR=${CMAKE_CURRENT_BINARY_DIR}/GaudiTestInstall
                                  -D BIN_DIR=${CMAKE_CURRENT_BINARY_DIR}
                                  -D CMAKE_GENERATOR=${CMAKE_GENERATOR}
                                  -D CMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER}
                                  $<$<BOOL:CMAKE_TOOLCHAIN_FILE>:-DCMAKE_TOOLCHAIN_FILE=${CMAKE_TOOLCHAIN_FILE}>
                                  -P ${CMAKE_CURRENT_LIST_FILE})
set_tests_properties(cmake.test_dummyGaudiDownstreamProject PROPERTIES
    DEPENDS cmake.test_gaudi_install
    FIXTURES_REQUIRED cmake.test_gaudi_install
    FIXTURES_SETUP cmake.test_dummyGaudiDownstreamProject
    LABELS CMake)
# Disable this test if did not install Gaudi
if(GAUDI_BUILD_TREE_AS_INSTALL_AREA)
    set_tests_properties(cmake.test_dummyGaudiDownstreamProject
        PROPERTIES DISABLED TRUE)
endif()
This test is disabled if we build Gaudi with GAUDI_BUILD_TREE_AS_INSTALL_AREA because we cannot install without reconfiguring Gaudi. SyntaxHighlightingPlugin: Language cmake is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.
# Test usability from the build tree
add_test(NAME cmake.test_dummyGaudiDownstreamProject_from_build_tree
         COMMAND ${CMAKE_COMMAND} -D TEST_GAUDI_DOWNSTREAM_FROM_BUILD:BOOL=TRUE
                                  -D SRC_DIR=${CMAKE_CURRENT_SOURCE_DIR}
                                  -D BIN_DIR=${CMAKE_CURRENT_BINARY_DIR}
                                  -D CMAKE_GENERATOR=${CMAKE_GENERATOR}
                                  -D CMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER}
                                  $<$<BOOL:CMAKE_TOOLCHAIN_FILE>:-DCMAKE_TOOLCHAIN_FILE=${CMAKE_TOOLCHAIN_FILE}>
                                  -P ${CMAKE_CURRENT_LIST_FILE})
set_tests_properties(cmake.test_dummyGaudiDownstreamProject_from_build_tree PROPERTIES
    DEPENDS "cmake.test_gaudi_install;cmake.test_gaudi_install_package" # not to mess up the cache
    FIXTURES_SETUP cmake.test_dummyGaudiDownstreamProject_from_build_tree
    LABELS CMake)
# Test usability as part of a stack
add_test(NAME cmake.test_dummyGaudiDownstreamProject_full_stack
         COMMAND ${CMAKE_COMMAND} -D TEST_GAUDI_DOWNSTREAM_FULL_STACK:BOOL=TRUE
                                  -D SRC_DIR=${CMAKE_CURRENT_SOURCE_DIR}
                                  -D BIN_DIR=${CMAKE_CURRENT_BINARY_DIR}
                                  -D CMAKE_GENERATOR=${CMAKE_GENERATOR}
                                  -D CMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER}
                                  $<$<BOOL:CMAKE_TOOLCHAIN_FILE>:-DCMAKE_TOOLCHAIN_FILE=${CMAKE_TOOLCHAIN_FILE}>
                                  -P ${CMAKE_CURRENT_LIST_FILE})
set_tests_properties(cmake.test_dummyGaudiDownstreamProject_full_stack PROPERTIES
    DEPENDS cmake.test_dummyGaudiDownstreamProject_from_build_tree
    FIXTURES_SETUP cmake.test_dummyGaudiDownstreamProject_full_stack
    LABELS CMake)
This is how the tests are run. They follow the conventions defined before. $<$:-DCMAKE_TOOLCHAIN_FILE=${CMAKE_TOOLCHAIN_FILE}> is a generator expression to pass a toolchain file to the test if one was given to the configuration.

testGaudiInstallation.cmake

cmake.test_gaudi_install
tries to install Gaudi in ${CMAKE_CURRENT_BINARY_DIR}/GaudiTestInstall, removing any previous installation if needed. SyntaxHighlightingPlugin: Language cmake is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.
execute_process(COMMAND ${CMAKE_COMMAND}
                        --install ${BIN_DIR}
                        --prefix ${GAUDI_INSTALL_DIR}
                RESULT_VARIABLE returned_value
                COMMAND_ECHO STDOUT)
if(NOT returned_value EQUAL "0")
    message(FATAL_ERROR "Installation of Gaudi failed")
endif()
Here we use cmake --install which is a new feature of cmake 3.15 The installation command is printed on stdout if CTest is in verbose mode.We check that the result is OK.

cmake.test_gaudi_install_check
checks that some special files are installed. It checks at least one script per scripts/ directory, one header per include/ directory, one python module per python/ directory (those folders are installed at once with gaudi_install() so if one file is missing it probably means that someone forgot to install the directory) one shared object, one .components, one .confdb, one .rootmap (these are generated automatically by gaudi_add_module(), if one of them is missing, the function has probably a problem), GAUDI_VERSION.h... It would be nice if this test could be maintained to still fit this description and thus validate the installation. SyntaxHighlightingPlugin: Language cmake is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.
if(NOT EXISTS ${GAUDI_INSTALL_DIR}/include/GaudiHive/HiveSlimEventLoopMgr.h)
    message(FATAL_ERROR "Include directory not installed correctly,"
        " HiveSlimEventLoopMgr.h not found in include/GaudiHive/")
endif()
This test is just a lot of if this file does not exist, error.

cmake.test_gaudi_install_package
tries to remove the old packages of Gaudi before trying to package Gaudi according to its CPack settings. This test is disabled if Gaudi can be used from the build tree.

testGaudiToolbox.cmake
contains tests to build, run, test and install dummyProject and check that GaudiToolbox.cmake behave as expected. Here, instead of defining huge tests that do all the steps to carry out an action, the whole file is about working on dummyProject. Each test is a step in the configuration. SyntaxHighlightingPlugin: Language cmake is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.
add_test(NAME cmake.test_dummyProject_clean
         COMMAND rm -rf ${CMAKE_CURRENT_BINARY_DIR}/dummyProjectBinaryDir)
This test just clean the build of old tests. SyntaxHighlightingPlugin: Language cmake is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.
add_test(NAME cmake.test_dummyProject_configure
         COMMAND ${CMAKE_COMMAND}
            -DCMAKE_INSTALL_PREFIX=${CMAKE_CURRENT_BINARY_DIR}/dummyProjectBinaryDir/install
            -DBIN_DIR=${CMAKE_CURRENT_BINARY_DIR}
            -S ${CMAKE_CURRENT_LIST_DIR}/dummyProject
            -B ${CMAKE_CURRENT_BINARY_DIR}/dummyProjectBinaryDir
            -G ${CMAKE_GENERATOR}
            -D CMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER}
            $<$<BOOL:CMAKE_TOOLCHAIN_FILE>:-DCMAKE_TOOLCHAIN_FILE=${CMAKE_TOOLCHAIN_FILE}>)
# ...
add_test(NAME cmake.test_dummyProject_build
         COMMAND ${CMAKE_COMMAND} --build ${CMAKE_CURRENT_BINARY_DIR}/dummyProjectBinaryDir)
# ...
add_test(NAME cmake.test_dummyProject_test
         COMMAND ${CMAKE_COMMAND} --build ${CMAKE_CURRENT_BINARY_DIR}/dummyProjectBinaryDir
                                  --target test)
# ...
add_test(NAME cmake.test_dummyProject_run
         COMMAND ${CMAKE_COMMAND} -DTEST_EXECTUTE_BINARY_DIR:BOOL=TRUE
                                  -DBIN_DIR:PATH=${CMAKE_CURRENT_BINARY_DIR}
                                  -P ${CMAKE_CURRENT_LIST_FILE})
Here, we configure, build, run the tests of the project and run a binary file. SyntaxHighlightingPlugin: Language cmake is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.
add_test(NAME cmake.test_dummyProject_install
         COMMAND ${CMAKE_COMMAND}
            --build ${CMAKE_CURRENT_BINARY_DIR}/dummyProjectBinaryDir
            --target install)
# ...
add_test(NAME cmake.test_dummyProject_install_checks
         COMMAND ${CMAKE_COMMAND} -DTEST_INSTALLATION_LAYOUT:BOOL=TRUE
                                  -DBIN_DIR:PATH=${CMAKE_CURRENT_BINARY_DIR}
                                  -DINSTALL_DIR=dummyProjectBinaryDir/install
                                  -P ${CMAKE_CURRENT_LIST_FILE})
Finally, we install it and test the installation

toolchains/

contains some toolchain files that can be used to configure Gaudi. This folder should be moved somewhere on CVMFS.

fragments/

contains factored out code that can be used in any toolchain file (gcc8.cmake set up the compiler to be gcc8 and dependencies.cmake fill the environment variables with the software releases on CVMFS). SyntaxHighlightingPlugin: Language cmake is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.
set(_prefix /cvmfs/sft.cern.ch/lcg/releases)
# Set the compiler
set(CMAKE_CXX_COMPILER ${_prefix}/gcc/8.2.0-3fa06/x86_64-centos7/bin/g++)
This is the absolute path to the compiler. SyntaxHighlightingPlugin: Language cmake is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.
set(ENV{PATH} "${_prefix}/gcc/8.2.0-3fa06/x86_64-centos7/bin:${_prefix}/binutils/2.30/x86_64-centos7/bin:/usr/bin")
Here we set PATH to the path to the compiler and the path to ar, as, ranlib, ld... We also add /usr/bin because we need standard command like chmod. SyntaxHighlightingPlugin: Language cmake is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.
list(APPEND CMAKE_INCLUDE_PATH ${_prefix}/gcc/8.2.0-3fa06/x86_64-centos7/include/c++/8.2.0
                               ${_prefix}/gcc/8.2.0-3fa06/x86_64-centos7/include/c++/8.2.0/x86_64-pc-linux-gnu)
set(ENV{ROOT_INCLUDE_PATH} "${_prefix}/gcc/8.2.0-3fa06/x86_64-centos7/include/c++/8.2.0:${_prefix}/gcc/8.2.0-3fa06/x86_64-centos7/include/c++/8.2.0/x86_64-pc-linux-gnu")
include_directories(BEFORE SYSTEM ${_prefix}/gcc/8.2.0-3fa06/x86_64-centos7/include/c++/8.2.0
                                  ${_prefix}/gcc/8.2.0-3fa06/x86_64-centos7/include/c++/8.2.0/x86_64-pc-linux-gnu)
Here we set everything for the headers. First we append to CMAKE_INCLUDE_PATH the include directories of the standard library. This variable is used by find_path() and may be specified through the command line that is why we do not override it with set() Then we set ROOT_INCLUDE_PATH for dictionaries to be able to use the standard library. Then we add to every target of the build the include directories of the standard headers. SyntaxHighlightingPlugin: Language cmake is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.
execute_process(COMMAND ${CMAKE_CXX_COMPILER} -print-file-name=libstdc++.so OUTPUT_VARIABLE stdlib_path)
get_filename_component(stdlib_path ${stdlib_path} REALPATH)
get_filename_component(stdlibdir ${stdlib_path} DIRECTORY)
list(APPEND CMAKE_LIBRARY_PATH ${stdlibdir})
set(ENV{LD_LIBRARY_PATH} "${stdlibdir}")
link_directories(BEFORE ${stdlibdir})
# FIXME: On MacOS it must be needed to add to CMAKE_LIBRARY_PATH the path to Foundation
Finally, we take care of the standard library by first asking the compiler where the libraries are then resolving potential symlinks then appending their location to CMAKE_LIBRARY_PATH (used by find_library()). Then we set LD_LIBRARY_PATH to this path and add this directory to the library directories of every target. I have never compiled Gaudi on MacOS and MacOS requires the Foundation framework but I do not know what to do with it.

SyntaxHighlightingPlugin: Language cmake is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.

# This file contains what is needed to setup the dependencies
# NB: path to Intel VTune Amplifier should be provided externally (by setting
# the environment variable CMAKE_PREFIX_PATH or -DCMAKE_PREFIX_PATH at configure time)
There could be licensing issues otherwise. SyntaxHighlightingPlugin: Language cmake is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.
set(_prefix /cvmfs/sft.cern.ch/lcg/releases)

file(TO_CMAKE_PATH "$ENV{PATH}" path)
file(TO_CMAKE_PATH "$ENV{LD_LIBRARY_PATH}" ld_library_path)
file(TO_CMAKE_PATH "$ENV{ROOT_INCLUDE_PATH}" root_include_path)
set(pythonpath) # empty the PYTHONPATH
Here we convert PATHs environment variables to CMake lists to be able to use list(). PATH, LD_LIBRARY_PATH and ROOT_INCLUDE_PATH are set with the compiler but PYTHONPATH is not, that is why we start with an empty one. SyntaxHighlightingPlugin: Language cmake is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.
# Set the paths to the dependencies
file(GLOB libs LIST_DIRECTORIES true "${_prefix}/LCG_${LCG_VERSION}/*/*/${BINARY_TAG}") # */* = name/version
list(FILTER libs EXCLUDE REGEX "^${_prefix}.*(ninja|Gaudi)") # do not use ninja or Gaudi from there
We take everything from CVMFS except ninja and Gaudi that would not be suitable. SyntaxHighlightingPlugin: Language cmake is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.
foreach(lib IN LISTS libs)
    list(APPEND CMAKE_PREFIX_PATH ${lib})
Each folder can be browse by find_package() SyntaxHighlightingPlugin: Language cmake is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.
if(EXISTS ${lib}/bin)
        list(PREPEND path ${lib}/bin)
    endif()
    if(EXISTS ${lib}/lib)
        file(GLOB inner_files "${lib}/lib/*.so")
        if(inner_files)
            list(PREPEND ld_library_path ${lib}/lib)
        endif()
        file(GLOB inner_files "${lib}/lib/*.py")
        if(inner_files)
            list(PREPEND pythonpath ${lib}/lib)
        endif()
    endif()
    if(EXISTS ${lib}/lib64)
        file(GLOB inner_files "${lib}/lib64/*.so")
        if(inner_files)
            list(PREPEND ld_library_path ${lib}/lib64)
        endif()
        file(GLOB inner_files "${lib}/lib64/*.py")
        if(inner_files)
            list(PREPEND pythonpath ${lib}/lib64)
        endif()
    endif()
    if(EXISTS ${lib}/include)
        list(PREPEND root_include_path ${lib}/include)
    endif()
We add path to executable, libraries and include directories to corresponding PATHs variables. SyntaxHighlightingPlugin: Language cmake is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.
endforeach()

# file(TO_NATIVE_PATH) does not transform ; to :
string(REPLACE ";" ":" path "${path}")
set(ENV{PATH} "${path}")
string(REPLACE ";" ":" ld_library_path "${ld_library_path}")
set(ENV{LD_LIBRARY_PATH} "${ld_library_path}")
string(REPLACE ";" ":" pythonpath "${pythonpath}")
set(ENV{PYTHONPATH} "${pythonpath}")
string(REPLACE ";" ":" root_include_path "${root_include_path}")
set(ENV{ROOT_INCLUDE_PATH} "${root_include_path}")
We set environment variables. SyntaxHighlightingPlugin: Language cmake is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.
# PYTHONHOME needed by gdb
list(FILTER libs INCLUDE REGEX "${_prefix}/LCG_${LCG_VERSION}/Python/[^/]+/${BINARY_TAG}")
list(GET libs 0 pythonhome)
set(ENV{PYTHONHOME} "${pythonhome}")
gdb need PYTHONHOME to know where python files are located. SyntaxHighlightingPlugin: Language cmake is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.
list(APPEND CMAKE_PROGRAM_PATH ${_prefix}/nose/1.3.7-fa22b/${BINARY_TAG}/bin
                               ${_prefix}/ccache/3.7.1-7651f/${BINARY_TAG}/bin)
Finally, we add paths to nosetests and ccache to be found by find_program().

$BINARY_TAG.cmake

are toolchain files that can be used directly or symlinked. SyntaxHighlightingPlugin: Language cmake is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.
set(LCG_VERSION 96 CACHE STRING "The version of LCG the build is based on")
set(BINARY_TAG x86_64-centos7-gcc8-dbg CACHE STRING "Platform used to compile")
set(CMAKE_BUILD_TYPE Developer CACHE STRING "The build type used to build")
set(CMAKE_USE_CCACHE ON CACHE BOOL "ccache enabled by the toolchain")
We set here a bunch of cached variables. LCG_VERSION is pointless for the configuration but it may help the user. BINARY_TAG describe the platform. CMAKE_BUILD_TYPE set the build type according to BINARY_TAG. CMAKE_USE_CCACHE is set to ON by default if we use a toolchain because if we use a toolchain then ccache is available so why not use it. SyntaxHighlightingPlugin: Language cmake is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.
# do not use directly CMAKE_CURRENT_LIST_DIR because it does not follow symlinks
get_filename_component(realfile ${CMAKE_CURRENT_LIST_FILE} REALPATH)
get_filename_component(CURRENT_DIR ${realfile} DIRECTORY)
Since this file will probably be symlinked we need to resolve the symlink instead of using CMAKE_CURRENT_LIST_DIR. SyntaxHighlightingPlugin: Language cmake is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.
include(${CURRENT_DIR}/fragments/gcc8.cmake)
include(${CURRENT_DIR}/fragments/dependencies.cmake)
We first include the compiler that will set PATH, LD_LIBRARY_PATH and ROOT_INCLUDE_PATH. Then we include the dependencies that will append to these environment variable and set PYTHONPATH.

CcacheOption.cmake

is a module that should be moved to CVMFS because any project could use it. It adds the option CMAKE_USE_CCACHE (OFF by default) to the project. (See This twiki page) SyntaxHighlightingPlugin: Language cmake is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.
include_guard() # can only be included once per project
option(CMAKE_USE_CCACHE "Enable/disable ccache for C and C++" OFF) # add the option

if(CMAKE_USE_CCACHE)
  set(_ccache_dipslay_message FALSE)
  if(NOT CCACHE_PROGRAM) # if ccache has not been found yet
    set(_ccache_dipslay_message TRUE) # will display a message
  endif()
  find_program(CCACHE_PROGRAM ccache) # look for ccache
  mark_as_advanced(CCACHE_PROGRAM)
  if(CCACHE_PROGRAM)
    foreach(lang IN ITEMS C CXX)
      if(NOT CCACHE_PROGRAM IN_LIST CMAKE_${lang}_COMPILER_LAUNCHER)
        list(APPEND CMAKE_${lang}_COMPILER_LAUNCHER ${CCACHE_PROGRAM})
      endif()
    endforeach()
    if(_ccache_dipslay_message)
      message(STATUS "Found ccache: ${CCACHE_PROGRAM}")
      message(STATUS "  Enabled ccache for C and C++")
    endif()
  else()
    message(FATAL_ERROR "ccache was not found! Check your CMAKE_PREFIX_PATH")
  endif()
endif()
It can be included once per project thanks to include_guard(). option( ) declares the option. If the user has enabled the option (if(CMAKE_USE_CCACHE)) we will try to use ccache. if(NOT CCACHE_PROGRAM) is here because we want to display a message only the first time ccache is found (like any other dependency see the description of GaudiDependencies.cmake). Then we try to find ccache (find_program(CCACHE_PROGRAM ccache)) We mark it as advanced because it is something the user should not modify by hand unless he really knows what he is doing (either changing the program used without any checks or clearing the value to force CMake to redo the lookup). In any case, if the option is enabled and ccache is not found an error occurs and a message is displayed.

DeveloperBuildType.cmake

is a module that should be moved to CVMFS because any project could use it. SyntaxHighlightingPlugin: Language cmake is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.
include_guard(GLOBAL) # can only be included once per configuration
Since this file mainly sets default values of cached variables it needs to be included only once (once per CMakeCache.txt). SyntaxHighlightingPlugin: Language cmake is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.
set(_flags "-g -Wall -Wextra -pedantic -Wnon-virtual-dtor -Werror=return-type -Wwrite-strings -Wpointer-arith -Woverloaded-virtual")
if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") # only for gcc because this flag is not supported by clang yet
    set(_flags "${_flags} -Wsuggest-override")
endif()
# WARNING: The default value of a cached variable cannot be modified after it has been set
set(CMAKE_CXX_FLAGS_${_CONFIG} "${_flags}"
    CACHE INTERNAL "Compile flags for ${_config} build type")
Here, we cannot set the value of the cached variable then append a "-W" because we are, in fact, setting the default value of the cached variable. The only possibility to change it afterward is to use FORCE at the end but we do not want to do so because it would also override a value set by the user (either on the command line or with ccmake or ...). This build type is a Debug+Warning build type. It has -g and lots of -W.... SyntaxHighlightingPlugin: Language cmake is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.
foreach(bin_type IN ITEMS EXE STATIC SHARED MODULE)
    set(CMAKE_${bin_type}_LINKER_FLAGS_${_CONFIG} "-Wl,--no-undefined -Wl,-z,max-page-size=0x1000"
        CACHE INTERNAL "Linker flags for ${bin_type} with ${_config} build type")
endforeach()
We set the linker flags for each type of target we may compile. SyntaxHighlightingPlugin: Language cmake is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.
if(CMAKE_CONFIGURATION_TYPES)
  list(APPEND CMAKE_CONFIGURATION_TYPES ${_config})
  set(CMAKE_CONFIGURATION_TYPES ${CMAKE_CONFIGURATION_TYPES} PARENT_SCOPE) # if build full stack
endif()
This tells multi-config generators (IDEs) that there is one more build type they can add to the list of possible build type. We set the value in the parent scope because, if we build a stack of project, the first project (namely Gaudi) will be able to include this module but not the others. So we propagate the modification at the stack level.

extract_qmtest_metadata.py

is a python2 script to generate cmake declaration of tests based on .qms folder containing .qmt files. This script is used in the function gaudi_add_tests(QMTest).

GaudiConfig.cmake.in

is the template of Gaudi's config-file. The same file is used to generate the config-file for an installed Gaudi and an exported Gaudi (used from the build tree). SyntaxHighlightingPlugin: Language cmake is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.
@PACKAGE_INIT@
Because we will use PACKAGE_ variables and the set_and_check() macro. SyntaxHighlightingPlugin: Language cmake is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.
# Set the C++ standard used to build Gaudi
set(GAUDI_CXX_STANDARD @GAUDI_CXX_STANDARD@ CACHE INTERNAL "Version of C++ used to compile Gaudi")
set(GAUDI_BUILD_TREE_AS_INSTALL_AREA @GAUDI_BUILD_TREE_AS_INSTALL_AREA@)
We export some variables. The C++ standard used as a cached variable because it will be useful in downstream projects. GAUDI_BUILD_TREE_AS_INSTALL_AREA because it makes the rest of the file easier to read after the configuration. SyntaxHighlightingPlugin: Language cmake is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.
set_and_check(_gaudi_config_files_location @_gaudi_config_files_location@)
list(PREPEND CMAKE_MODULE_PATH ${_gaudi_config_files_location}/modules) # Find*.cmake
list(PREPEND CMAKE_MODULE_PATH ${_gaudi_config_files_location}) # other modules
_gaudi_config_files_location comes from the CMakeLists.txt in Gaudi. set_and_check() will make sure that the directory exists when this file will be processed during the configuration of a downstream project. CMAKE_MODULE_PATH is set to enable downstream project to include module with their names rather than their locations (e.g. include(DeveloperBuildType)). SyntaxHighlightingPlugin: Language cmake is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.
if(NOT GAUDI_BUILD_TREE_AS_INSTALL_AREA)
    # Set environment variables (so that we only need the CMAKE_PREFIX_PATH to use Gaudi)
    set(ENV{PATH} "$ENV{PATH}:@PACKAGE_CMAKE_INSTALL_BINDIR@")
    set(ENV{LD_LIBRARY_PATH} "$ENV{LD_LIBRARY_PATH}:@PACKAGE_CMAKE_INSTALL_LIBDIR@:@PACKAGE_GAUDI_INSTALL_PLUGINDIR@")
    set(ENV{ROOT_INCLUDE_PATH} "$ENV{ROOT_INCLUDE_PATH}:@PACKAGE_CMAKE_INSTALL_INCLUDEDIR@")
    set(ENV{PYTHONPATH} "$ENV{PYTHONPATH}:@PACKAGE_GAUDI_INSTALL_PYTHONDIR@")
    # NB: it needs to be done before including GaudiToolbox
endif()
If Gaudi has been installed, we set the environment of the downstream project by adding the location of Gaudi's executable, libraries, plugins, headers and python modules to the right environment variables. Consequently, having CMAKE_PREFIX_PATH that points to the install prefix of Gaudi is enough to by able to use Gaudi with CMake. The environment variables needs to be set before the inclusion of GaudiToolbox.cmake because GaudiToolbox.cmake set up the run script and the run script uses the value of environment variables at configure time when it is processed. SyntaxHighlightingPlugin: Language cmake is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.
if(NOT GAUDI_NO_TOOLBOX)
    # Import the functions
    include(GaudiToolbox)
endif()
For some reasons the downstream project may not want to import GaudiToolbox so it can set(GAUDI_NO_TOOLBOX TRUE) before find_package(Gaudi) SyntaxHighlightingPlugin: Language cmake is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.
# Enable optional dependencies
@GAUDI_OPTIONAL_DEPENDENCIES@
This is replaced at generation time by severals set(GAUDI_USE_??? ON|OFF) These variables are the options available in Gaudi at configure time to enable or disable optional dependencies. They are no longer options when Gaudi is imported because if a library has been used to build Gaudi, it needs to be available to use Gaudi in downstream projects. This needs to be done before including GaudiDependencies.cmake that will do the lookup based on these variables. SyntaxHighlightingPlugin: Language cmake is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.
if(@PROJECT_NAME@_FIND_QUIETLY)
    set(GAUDI_DEPENDENCIES_FIND_QUIETLY TRUE)
endif()
include(GaudiDependencies)
Now we look for Gaudi's third party dependencies according the aforementioned variables. If the QUIET argument was given to find_package(Gaudi) we forward this argument to the other dependencies. We handle the lookup in this file because in CMake's philosophy the transitivity should by handled by CMake. Consequently, we do not want the downstream project to do the lookup because Gaudi can do it. Downstream projects should only look for their direct dependencies. SyntaxHighlightingPlugin: Language cmake is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.
include("${CMAKE_CURRENT_LIST_DIR}/@PROJECT_NAME@Targets.cmake")
Here we include the file generated either by install(EXPORT) (for relocatable packages) or by export(EXPORT) (for non-relocatable packages). This file defines all the imported targets that downstream projects may use from Gaudi. SyntaxHighlightingPlugin: Language cmake is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.
if(GAUDI_BUILD_TREE_AS_INSTALL_AREA AND NOT GAUDI_NO_TOOLBOX)
    @_set_extract_qmtest_metadata@
    # Add paths of Gaudi's run script content to the run script downstream
    file(STRINGS "${CMAKE_CURRENT_LIST_DIR}/gaudienv.sh" __gaudi_exported_paths REGEX "^export .*PATH=\".+\"$")
    list(TRANSFORM __gaudi_exported_paths REPLACE "^export (.*PATH)=\"(.+)\"$" "\\1=\\2")
    foreach(exported_path IN LISTS __gaudi_exported_paths)
        string(REPLACE "=" ";" path_value ${exported_path})
        list(POP_FRONT path_value path_name)
        string(TOLOWER ${path_name} path_name)
        file(TO_CMAKE_PATH "${path_value}" path_value)
        set_property(TARGET target_runtime_paths APPEND
            PROPERTY runtime_${path_name} ${path_value})
    endforeach()
    # NB: it needs to be done after including GaudiToolbox
endif()
If we use Gaudi from its build tree and we import GaudiToolbox... _set_extract_qmtest_metadata used to be set(extract_qmtest_metadata @extract_qmtest_metadata@) but this way it would have wrote in the config-file a path that is in the source tree. This should not be visible after installation so this trick. Note that this whole block of code has nothing to do in the config-file after installation but it is easier for us and does not pollute that much the top-level CMakeLists.txt of Gaudi. Then we parse the content of gaudienv.sh which is the file that contains all the runtime environment generated by the configuration for the run script. The content is added to the runtime environment of the downstream project with target_runtime_paths defined in GaudiToolbox (that is the reason why this needs to be done after including GaudiToolbox). This is needed because we do not install Gaudi thus we do not use the previous block of code to set up the environment variables. SyntaxHighlightingPlugin: Language cmake is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.
if(NOT @PROJECT_NAME@_FIND_QUIETLY)
    message(STATUS "Found @PROJECT_NAME@: ${CMAKE_CURRENT_LIST_DIR} (found version @PROJECT_VERSION@)")
endif()
We are not in a find-module-file and we do not use FindPackageHandleStandardArgs but it does not mean that we cannot tell Gaudi has been found (only if not in QUIET mode.) SyntaxHighlightingPlugin: Language cmake is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.
mark_as_advanced(@PROJECT_NAME@_DIR)
Hide the location of this file to non-advanced users.

GaudiDependencies.cmake

contains the dependency lookup. It is in a separate file because it can be used at configure time to build Gaudi or in GaudiConfig.cmake by other projects. SyntaxHighlightingPlugin: Language cmake is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.
set(__quiet)
if(GAUDI_DEPENDENCIES_FIND_QUIETLY)
  set(__quiet QUIET)
endif()
To pass QUIET or nothing to each call to find_package() we have a variable that will be expanded to QUIET or nothing (${__quiet}). SyntaxHighlightingPlugin: Language cmake is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.
include(FeatureSummary)
set(FeatureSummary_REQUIRED_PKG_TYPES RECOMMENDED REQUIRED)
FeatureSummary is a built-in module of CMake that especially defines feature_summary() (see at the end of the file). Here we will use REQUIRED for required dependencies and RECOMMENDED for optional dependencies. Both are required because we decided that if GAUDI_USE_SOMETHING is TRUE then you explicitly want to use something so it means it is required. SyntaxHighlightingPlugin: Language cmake is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.
set(Boost_USE_STATIC_LIBS OFF)
set(OLD_BUILD_SHARED_LIBS ${BUILD_SHARED_LIBS}) # FIXME: the day BoostConfig.cmake handles Boost_USE_STATIC_LIBS correctly
set(BUILD_SHARED_LIBS ON)
find_package(Boost 1.70 ${__quiet} CONFIG COMPONENTS system filesystem regex
  thread python unit_test_framework program_options log log_setup graph)
set_package_properties(Boost PROPERTIES TYPE REQUIRED)
set(BUILD_SHARED_LIBS ${OLD_BUILD_SHARED_LIBS})
mark_as_advanced(Boost_DIR) # FIXME: the day Boost correctly marks as advanced its variables
foreach(component IN ITEMS system filesystem regex thread python unit_test_framework
                           program_options log log_setup graph atomic chrono
                           date_time headers)
  mark_as_advanced(boost_${component}_DIR)
endforeach()
This is Boost crap. The reason behind all this is that before Boost 1.70, Kitware was maintaining a find-module-file for Boost. This FindBoost.cmake defined variables like Boost_USE_STATIC_LIBS to tell if we want static or shared libraries. Since Boost 1.70, another third party project, namely "Boost CMake", provides a config-file for Boost. Unfortunately it does not have the same interface and is still under development. To be able to have this config-file import shared libraries, we need to set BUILD_SHARED_LIBS to ON. Furthermore, it does not provide a way of asking for Boost::python36. The old FindBoost.cmake had 2 components, namely Boost::python27 and Boost::python36. Now we only have Boost::python. If it were to change, make sure to update all CMakeLists.txt that may need it. Why do we anyway use this config-file? Because there is a rule that says when a config-file is provided by the editor of a project, all the find-module-files maintained in random projects slowly die. Moreover, this config-file does not mark as advanced all the _DIR variables generated automatically by find_package(). Notice that the list of components we require is not the full list of available components. SyntaxHighlightingPlugin: Language cmake is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.
set(Python_FIND_STRATEGY VERSION) # Find latest version available
find_package(Python 2.7.15 ${__quiet} COMPONENTS Interpreter Development)
set_package_properties(Python PROPERTIES TYPE REQUIRED)
Python is almost simple. We require at least 2.7.15. The validate Python_FIND_STRATEGY is set to version to have find_package() look for the highest version it can find instead of the first one found. Before CMake 3.15 it was the default. SyntaxHighlightingPlugin: Language cmake is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.
find_package(ROOT 6.18 ${__quiet} CONFIG COMPONENTS Core RIO Hist Thread Matrix
  MathCore Net XMLIO Tree TreePlayer Graf3d Graf Gpad)
mark_as_advanced(ROOT_DIR ROOT_genmap_CMD ROOT_rootdraw_CMD)
set_package_properties(ROOT PROPERTIES TYPE REQUIRED)
# FIXME: the day ROOTConfig.cmake displays at least that it was found remove these lines
if(NOT GAUDI_DEPENDENCIES_FIND_QUIETLY)
  message(STATUS "Found ROOT: ${ROOT_DIR} (found version ${ROOT_VERSION})")
endif()
Like for Boost, we mark some stuff as advanced and we display a message because ROOTConfig.cmake never prints anything. SyntaxHighlightingPlugin: Language cmake is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.
# FIXME: if you are using the normal version of TBB, it has a config file so
#   remove cmake/FindTBB.cmake and add CONFIG to the next line.
find_package(TBB 2019.0.11007.2 ${__quiet})
set_package_properties(TBB PROPERTIES TYPE REQUIRED)
The comment says it all. SyntaxHighlightingPlugin: Language cmake is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.
set(_gaudi_ZLIB_MIN_VERSION 1.2.11)
foreach(dep IN ITEMS UUID Threads ZLIB Rangev3 cppgsl)
  find_package(${dep} ${_gaudi_${dep}_MIN_VERSION} ${__quiet})
  set_package_properties(${dep} PROPERTIES TYPE REQUIRED)
endforeach()
Here we factored out the lookup for little dependencies. For each required dependency, we call find_package() and then we call set_package_properties() (defined in FeatureSummary) to tell CMake it is required. SyntaxHighlightingPlugin: Language cmake is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.
set(_gaudi_CLHEP_MIN_VERSION 2.4.1.0)
set(_gaudi_gperftools_MIN_VERSION 2.7.0)
set(_gaudi_Doxygen_MIN_VERSION 1.8.15)
foreach(dep IN ITEMS AIDA XercesC CLHEP HepPDT CppUnit unwind gperftools
                     Doxygen IntelAmplifier jemalloc)
  string(TOUPPER ${dep} DEP)
  if(GAUDI_USE_${DEP})
    find_package(${dep} ${_gaudi_${dep}_MIN_VERSION} ${__quiet})
    if(CMAKE_FIND_PACKAGE_NAME) # if the lookup is perform from GaudiConfig.cmake
      # then, all optional dependencies become required
      set_package_properties(${dep} PROPERTIES TYPE REQUIRED)
    else()
      set_package_properties(${dep} PROPERTIES TYPE RECOMMENDED)
    endif()
  endif()
endforeach()
Here we do a similar loop but for optional dependencies. First, we check the option to know if the dependency is enabled. Then we call find_package(). Then we check if we are building Gaudi or importing Gaudi. If we build Gaudi, optional dependencies are RECOMMENDED if we import Gaudi, they are REQUIRED as explained earlier. SyntaxHighlightingPlugin: Language cmake is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.
# Make sure platform specific system libraries are available
if(UNIX AND NOT APPLE)
  find_library(rt_LIBRARY rt)
  mark_as_advanced(rt_LIBRARY)
  if(NOT rt_LIBRARY)
    message(FATAL_ERROR "rt was not found")
  endif()
endif()
if(APPLE)
  find_library(Foundation_FRAMEWORK Foundation)
  mark_as_advanced(Foundation_FRAMEWORK)
  if(NOT Foundation_FRAMEWORK)
    message(FATAL_ERROR "Foundation framework was not found")
  endif()
endif()
UNIX systems need rt and MacOS ones need Foundation. In MacOS' case I do not know what changes because I do not have any MacOS system to test Gaudi on and no target links against Foundation. SyntaxHighlightingPlugin: Language cmake is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.
# Print a summary of the lookup
feature_summary(FATAL_ON_MISSING_REQUIRED_PACKAGES
  DESCRIPTION "Missing Gaudi's required dependencies:"
  QUIET_ON_EMPTY
  WHAT REQUIRED_PACKAGES_NOT_FOUND)
feature_summary(DESCRIPTION "Gaudi's required optional dependencies found:"
  QUIET_ON_EMPTY
  WHAT RECOMMENDED_PACKAGES_FOUND)
feature_summary(FATAL_ON_MISSING_REQUIRED_PACKAGES
  DESCRIPTION "Missing Gaudi's required optional dependencies:"
  QUIET_ON_EMPTY
  WHAT RECOMMENDED_PACKAGES_NOT_FOUND)
# Reset this summary not to mess with downstream projects look up
if(COMMAND __reset_feature_summary)
  __reset_feature_summary()
endif()
THIS is the most important part of this file: the report. We use FeatureSummary instead of directly specifing REQUIRED to find_package() because if we had done so, we would have had to reconfigure each and every time a third party dependency was not found because it would have raised an error, until we fixed all dependency issues. Here feature_summary() will print at once all the required dependencies that were not found to be able to fix all the issues at once. If a REQUIRED or RECOMMENDED dependency was not found, an error occurs. All the RECOMMENDED (optional) dependencies used are displayed. For __reset_feature_summary() see GaudiToolbox. SyntaxHighlightingPlugin: Language cmake is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.
if(COMMAND __silence_header_warnings)
  __silence_header_warnings(Boost::headers Boost::system Boost::filesystem
      Boost::regex Boost::thread Boost::python Boost::unit_test_framework
      Boost::program_options Boost::log Boost::log_setup Boost::graph
      ROOT::Core ROOT::RIO ROOT::Hist ROOT::Thread ROOT::Matrix ROOT::MathCore
      ROOT::Net ROOT::XMLIO ROOT::Tree ROOT::TreePlayer ROOT::Graf3d
      ROOT::Graf ROOT::Gpad ZLIB::ZLIB cppgsl::cppgsl CppUnit::cppunit)
endif()
See GaudiToolbox.

GaudiToolbox.cmake

See GaudiToolbox (next part)

Most sub-projects of Gaudi are standard-ish but we will give details on some of them.

Gaudi

is the sub-project that builds the executable Gaudi. This executable used to be referred as Gaudi.exe and this name is still used is several places. So, for backward compatibility, we create a symlink SyntaxHighlightingPlugin: Language cmake is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.
file(CREATE_LINK Gaudi ${CMAKE_CURRENT_BINARY_DIR}/Gaudi.exe SYMBOLIC)

GaudiCoreSvc

SyntaxHighlightingPlugin: Language cmake is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.
gaudi_generate_version_header_file(GaudiCoreSvc)
target_include_directories(GaudiCoreSvc PRIVATE
  "${CMAKE_CURRENT_BINARY_DIR}/include")
if(UNIX AND NOT APPLE)
  target_link_libraries(GaudiCoreSvc PRIVATE ${rt_LIBRARY})
endif()
Here we generate a version header. To have GaudiCoreSvc be able to use it we add directory where it is generated to the list of include directories of GaudiCoreSvc. On UNIX platform, GaudiCoreSvc needs rt.

GaudiExamples

The CMakeLists.txt of this package is one of the most complete ones, thus it is a good example. That is why it is used in the user guide. (Here some sources were removed from the list of sources.) SyntaxHighlightingPlugin: Language cmake is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.
gaudi_add_library(GaudiExamplesLib
                  SOURCES src/Lib/Counter.cpp
                  LINK PUBLIC GaudiKernel)
gaudi_add_module(GaudiExamples
                 SOURCES src/AbortEvent/AbortEventAlg.cpp
                 LINK GaudiKernel
                      GaudiAlgLib
                      GaudiUtilsLib
                      GaudiExamplesLib
                      ROOT::Tree
                      ROOT::RIO
                      ROOT::Hist
                      ROOT::Net
                      Rangev3::rangev3)
Examples of how to use gaudi_*() functions. Within Gaudi, Gaudi's targets can be referred as Gaudi??? or Gaudi::Gaudi???. We use targets defined by third-party dependencies here instead of the old ${dep_LIBRARY} or ${dep_INCLUDE_DIR}... SyntaxHighlightingPlugin: Language cmake is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.
if(GAUDI_USE_AIDA)
    target_sources(GaudiExamples PRIVATE src/EvtColsEx/EvtColAlg.cpp
                                         src/RandomNumber/RandomNumberAlg.cpp)
    target_link_libraries(GaudiExamples PRIVATE AIDA::aida)
endif()
This how we manage optional dependencies. If they are enabled, we add special source files to the list of sources of a target and we link the target against them. It is standard CMake. It uses internally target properties. SyntaxHighlightingPlugin: Language cmake is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.
if(TARGET RootCnvLib)
    target_sources(GaudiExamples PRIVATE src/MultiInput/DumpAddress.cpp
                                         src/MultiInput/MIReadAlg.cpp
                                         src/MultiInput/MIWriteAlg.cpp)
    target_link_libraries(GaudiExamples PRIVATE RootCnvLib)
endif()
Here it is a special case of optional dependency we want to link against RootCnvLib but this target may not have been built because it requires an optional dependency to be enabled. That is why we test the existence of the target. SyntaxHighlightingPlugin: Language cmake is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.
gaudi_add_executable(Allocator
                        SOURCES src/Allocator/Allocator.cpp
                                src/Allocator/MyClass1A.cpp
                                src/Allocator/MyClass1.cpp
                        LINK GaudiExamplesLib
                            GaudiAlgLib
                            GaudiKernel)
# ...
gaudi_generate_confuserdb()

# Tests
gaudi_add_tests(QMTest)
gaudi_add_tests(nosetests)

# Compiled python module
gaudi_add_python_module(PyExample
                        SOURCES src/PythonModule/Functions.cpp
                                src/PythonModule/PyExample.cpp
                        LINK Python::Python
                             Boost::python)

# ROOT dictionaries
gaudi_add_dictionary(GaudiExamplesDict
                     HEADERFILES src/IO/dict.h
                     SELECTION src/IO/dict.xml
                     LINK GaudiExamplesLib)

# Install python modules
gaudi_install(PYTHON)
# Install other scripts
configure_file(scripts/TupleEx3.py.in
               ${CMAKE_CURRENT_BINARY_DIR}/generated/scripts/TupleEx3.py)
configure_file(scripts/TupleEx4.py.in
               ${CMAKE_CURRENT_BINARY_DIR}/generated/scripts/TupleEx4.py)
gaudi_install(SCRIPTS)
gaudi_install(SCRIPTS ${CMAKE_CURRENT_BINARY_DIR}/generated/scripts/)
Here we have an example of scripts that are configured at generation time with configure_file() then installed with gaudi_install().

GaudiKernel

SyntaxHighlightingPlugin: Language cmake is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.
# Look for VectorClass required header (private dependency)
find_path(VectorClass_INCLUDE_DIR NAMES instrset_detect.cpp)
if(VectorClass_INCLUDE_DIR AND NOT VectorClass_VERSION)
  # check that the version is good enough
  set(VectorClass_VERSION 0.0)
  file(STRINGS ${VectorClass_INCLUDE_DIR}/instrset.h _vectorclass_guard REGEX "define +INSTRSET_H +[0-9]+")
  list(GET _vectorclass_guard 0 _vectorclass_guard)
  if(_vectorclass_guard MATCHES "INSTRSET_H +([0-9]+)")
    string(REGEX REPLACE "([0-9]+)([0-9][0-9])" "\\1.\\2" VectorClass_VERSION "${CMAKE_MATCH_1}")
  endif()
  set(VectorClass_VERSION "${VectorClass_VERSION}" CACHE INTERNAL "")
endif()
if(NOT VectorClass_INCLUDE_DIR OR VectorClass_VERSION VERSION_LESS 1.25)
  if(VectorClass_INCLUDE_DIR)
    message(STATUS "Found VectorClass instrset_detect ${VectorClass_VERSION} at ${VectorClass_INCLUDE_DIR}")
  endif()
  message(WARNING "using internal VectorClass instrset_detect")
  set(VectorClass_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/src/contrib" CACHE PATH "" FORCE)
  set(VectorClass_VERSION "1.25" CACHE INTERNAL "" FORCE)
endif()
mark_as_advanced(VectorClass_INCLUDE_DIR)
message(STATUS "Using VectorClass instrset_detect ${VectorClass_VERSION} at ${VectorClass_INCLUDE_DIR}")
Here we do a lookup of the sources of VectorClass. It is not in a FindVectorClass.cmake because is the VectorClass it found is not recent enough, we use an internal implementation of VectorClass defined in src/contrib. SyntaxHighlightingPlugin: Language cmake is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.
gaudi_add_library(GaudiKernel
  SOURCES src/Lib/AlgContext.cpp
  LINK
    PUBLIC GaudiPluginService
           Boost::headers
           Boost::filesystem
           Boost::thread
           Boost::regex
           Boost::system
           TBB::tbb
           ${CMAKE_DL_LIBS}
           ROOT::Core
    PRIVATE cppgsl::cppgsl)
target_include_directories(GaudiKernel PRIVATE ${VectorClass_INCLUDE_DIR})
GaudiKernel needs dl and VectorClass, thus ${CMAKE_DL_LIBS} and ${VectorClass_INCLUDE_DIR}. SyntaxHighlightingPlugin: Language cmake is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.
if(APPLE)
  target_sources(GaudiKernel PRIVATE src/Lib/Platform/SystemMacOS.h
                                     src/Lib/Platform/SystemMacOS.mm)
  target_link_libraries(GaudiKernel PUBLIC Foundation)
elseif(WIN32)
  target_sources(GaudiKernel PRIVATE src/Lib/Platform/SystemWin32.h
                                     src/Lib/Platform/SystemWin32.cpp)
elseif(UNIX)
  target_sources(GaudiKernel PRIVATE src/Lib/Platform/SystemLinux.h
                                     src/Lib/Platform/SystemLinux.cpp)
  target_link_libraries(GaudiKernel PUBLIC ${rt_LIBRARY})
endif()
GaudiKernel contains some sources specific to the platform. Here the headers are specified for no particular reason. SyntaxHighlightingPlugin: Language cmake is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.
gaudi_add_executable(profile_Property
                     SOURCES tests/src/profile_Property.cpp
                     LINK GaudiKernel)
get_filename_component(package_name ${CMAKE_CURRENT_SOURCE_DIR} NAME)
gaudi_add_executable(DirSearchPath_test
                     SOURCES tests/src/DirSearchPath_test.cpp
                     LINK GaudiKernel
                     TEST)
gaudi_add_executable(PathResolver_test SOURCES tests/src/PathResolver_test.cpp
  LINK GaudiKernel)
add_test(NAME ${package_name}.PathResolver_test
         COMMAND run $<TARGET_FILE:PathResolver_test>
         WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/tests)
gaudi_add_executable(Parsers_test SOURCES tests/src/parsers.cpp
  LINK GaudiKernel TEST)
Lots of tests are executable that just have to be run. If the test has a special behavior we create an executable then we use add_test directly. The "problem" with these tests is that they are exported at install time. All the tests are installed (the binaries) but they are not exported because it is highly unlikely that their targets could be useful in downstream projects. SyntaxHighlightingPlugin: Language cmake is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.
add_executable(test_StatusCodeFail tests/src/test_StatusCode_fail.cxx)
target_link_libraries(test_StatusCodeFail PRIVATE GaudiKernel)
set_target_properties(test_StatusCodeFail PROPERTIES EXCLUDE_FROM_ALL TRUE EXCLUDE_FROM_DEFAULT_BUILD TRUE)
add_test(NAME ${package_name}.test_StatusCodeFail
        COMMAND ${CMAKE_COMMAND} --build ${CMAKE_BINARY_DIR} --target test_StatusCodeFail)
set_property(TEST ${package_name}.test_StatusCodeFail PROPERTY PASS_REGULAR_EXPRESSION "FAIL01;FAIL02;FAIL03;FAIL04")
All standard feature of CMake can be used. SyntaxHighlightingPlugin: Language cmake is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.
if(GAUDI_USE_CPPUNIT)
  gaudi_add_executable(test_SerializeSTL SOURCES tests/src/test_SerializeSTL.cpp
    LINK GaudiKernel CppUnit::cppunit TEST)
endif()
Tests that uses an optional dependency. SyntaxHighlightingPlugin: Language cmake is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.
gaudi_add_tests(nosetests ${CMAKE_CURRENT_SOURCE_DIR}/tests/nose/confdb)
gaudi_add_tests(nosetests)

gaudi_add_module(test_CustomFactory
  SOURCES tests/src/custom_factory.cpp
  LINK GaudiPluginService)

# Genconf
gaudi_add_executable(genconf
  SOURCES src/Util/genconf.cpp
  LINK GaudiKernel
       GaudiPluginService
       Boost::headers
       Boost::program_options
       Boost::regex
       Boost::log
       Boost::log_setup
       ROOT::Core)
target_compile_definitions(genconf PRIVATE BOOST_LOG_DYN_LINK)
set_target_properties(genconf PROPERTIES ENABLE_EXPORTS TRUE)
if(WIN32)
  gaudi_add_executable(genwindef
    SOURCES src/Util/genwindef.cpp
            src/Util/LibSymbolInfo.cpp)
endif()
gaudi_add_executable(instructionsetLevel
  SOURCES src/Util/instructionsetLevel.cpp
  LINK GaudiKernel
       GaudiPluginService)
file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/custom_factory_testdir)
add_test(NAME ${package_name}.genconf_with_custom_factory
  COMMAND ${CMAKE_COMMAND} -E env LD_LIBRARY_PATH=$<TARGET_FILE_DIR:test_CustomFactory>:$<TARGET_FILE_DIR:GaudiCoreSvc>:$ENV{LD_LIBRARY_PATH}
          $<TARGET_FILE:genconf>
          -o ${CMAKE_CURRENT_BINARY_DIR}/custom_factory_testdir
          -p CustomFactoryTest
          -i test_CustomFactory
  WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/custom_factory_testdir)
Stuff related to genconf. SyntaxHighlightingPlugin: Language cmake is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.
# QMTest
gaudi_add_tests(QMTest)

# ROOT dictionaries
gaudi_add_dictionary(GaudiKernelDict
                     HEADERFILES dict/dictionary.h
                     SELECTION dict/dictionary.xml
                     LINK GaudiKernel)
  
# Install python modules
gaudi_install(PYTHON)
# Install other scripts
gaudi_install(SCRIPTS)

# Check that gdb can be used for stack traces
find_program(gdb_cmd gdb)
mark_as_advanced(gdb_cmd)
if(gdb_cmd)
  get_filename_component(gdb_dir "${gdb_cmd}" DIRECTORY)
  file(TO_CMAKE_PATH "$ENV{PATH}" envpath)
  if(NOT gdb_dir IN_LIST envpath)
    set_property(TARGET target_runtime_paths APPEND
      PROPERTY runtime_path $<SHELL_PATH:${gdb_dir}>)
  endif()
else()
  message(WARNING "gdb not found, you will not be able to have stack traces for problems")
endif()
Here, we make sure that gdb is available at runtime (in the runtime environment). find_program() tries to find it in the CMAKE_PREFIX_PATH and CMAKE_PROGRAM_PATH and then in ENV{PATH}. If we found it and it is not in the path we add it through the target defined in GaudiToolbox. It exposes the internal machinery of Gaudi but it is a good thing not to have gaudi_env() like functions because people would then use it to set tests environment instead of using the appropriate tag in .qmt files. I am worrying about one case. What if a gdb is found in CMAKE_PREFIX_PATH but you expect to use your gdb (the one in your PATH) at runtime?

GaudiMonitor

SyntaxHighlightingPlugin: Language cmake is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.
if(WIN32)
  target_link_libraries(GaudiMonitor PRIVATE ws2_32.lib)
  # These are needed because Windows compiler doesn't like standard C and POSIX functions,
  # but this package contains some imported (external) C file that is compiled here in C++.
  target_compile_definitions(GaudiMonitor PRIVATE _CRT_SECURE_NO_WARNINGS
                                                  _SCL_SECURE_NO_WARNINGS)
  target_compile_options(GaudiMonitor /wd4996)
endif()
I have no idea why it is needed. Since LHCb does not support Windows this is apparently for ATLAS.

GaudiPluginService

SyntaxHighlightingPlugin: Language cmake is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.
option(GAUDI_REFLEX_COMPONENT_ALIASES
       "allow use of old style (Reflex) component names"
       ON)
An option specific to GaudiPluginService. SyntaxHighlightingPlugin: Language cmake is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.
# Build library
gaudi_add_library(GaudiPluginService
                  SOURCES src/PluginServiceV2.cpp
                          src/PluginServiceV1.cpp
                          src/capi_pluginservice.cpp
                  LINK
                    PRIVATE ${CMAKE_DL_LIBS}
                            stdc++fs
                    PUBLIC Threads::Threads)
if(GAUDI_REFLEX_COMPONENT_ALIASES)
  target_compile_definitions(GaudiPluginService PRIVATE
    GAUDI_REFLEX_COMPONENT_ALIASES)
endif()
We need to explicitly link against dl stdc++fs (no variable because it is a standard library like m) and pthread. SyntaxHighlightingPlugin: Language cmake is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.
# Build runtime
gaudi_add_executable(listcomponents
                     SOURCES src/listcomponents.cpp
                     LINK GaudiPluginService
                          ${CMAKE_DL_LIBS})

# Tests
gaudi_add_library(Test_GaudiPluginService_UseCasesLib
                  SOURCES tests/src/UseCasesLib.cpp
                  LINK PRIVATE GaudiPluginService
                       Boost::unit_test_framework)
gaudi_add_executable(Test_GaudiPluginService_UseCases
                     SOURCES tests/src/UseCasesTests.cpp
                     LINK GaudiPluginService
                          Boost::unit_test_framework
                     TEST)

gaudi_add_library(Test_GaudiPluginService_LegacyUseCasesLib
                  SOURCES tests/src/LegacyUseCasesLib.cpp
                  LINK PRIVATE GaudiPluginService)
gaudi_add_executable(Test_GaudiPluginService_LegacyUseCases
                     SOURCES tests/src/LegacyUseCasesTests.cpp
                     LINK GaudiPluginService
                          Boost::unit_test_framework
                     TEST)
GaudiPluginService is the only package where libraries are built for tests. SyntaxHighlightingPlugin: Language cmake is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.
get_filename_component(package_name ${CMAKE_CURRENT_SOURCE_DIR} NAME)
# tests on listcomponents
add_test(NAME ${package_name}.listcomponents.usage
         COMMAND run $<TARGET_FILE:listcomponents>)
Some tests after.

GaudiProfiling

SyntaxHighlightingPlugin: Language cmake is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.
if(NOT CMAKE_SYSTEM_NAME MATCHES Linux)
    message(WARNING "Requirements for building GaudiProfiling not reached. "
        "GaudiProfiling will not be built. "
        "Not building for Linux.")
    return()
endif()
This package is only available on Linux. SyntaxHighlightingPlugin: Language cmake is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.
#-----------------------------------
# PerfMon profiler
#-----------------------------------
if(GAUDI_USE_UNWIND AND CMAKE_SYSTEM_PROCESSOR STREQUAL "x86_64")
  gaudi_add_module(GaudiProfiling
                   SOURCES src/component/IgHook_IgHookTrace.cpp
                           src/component/PerfMonAuditor.cpp
                   LINK GaudiKernel
                        ZLIB::ZLIB
                        unwind::unwind)
  target_include_directories(GaudiProfiling PRIVATE src/component)

  gaudi_add_python_module(PyCPUFamily
                          SOURCES src/python/CPUFamily.cpp
                          LINK Python::Python
                               Boost::python)

  gaudi_add_executable(GaudiGenProfilingHtml
                       SOURCES src/app/pfm_gen_analysis.cpp
                       LINK GaudiKernel
                            ZLIB::ZLIB
                            unwind::unwind)
else()
  message(STATUS "GaudiProfiling: the module PerfMon profiler is disabled")
endif()

#-----------------------------------
# Google PerfTools profiler
#-----------------------------------
if(GAUDI_USE_GPERFTOOLS)
  gaudi_add_module(GaudiGoogleProfiling
                   SOURCES src/component/google/GoogleAuditor.cpp
                   LINK GaudiKernel
                        GaudiAlgLib
                        ZLIB::ZLIB
                        gperftools::gperftools)

  # Special handling of unresolved symbols in GaudiGoogleProfiling.
  # The profilers need to have libtcmalloc.so or libprofiler.so pre-loaded to
  # work, so it's better if the symbols stay undefined in case somebody tries to
  # use the profilers without preloading the libraries.
  set(gprof_linker_flags)
  foreach(undef_symbol IN ITEMS IsHeapProfilerRunning
                                HeapProfilerStart HeapProfilerStop
                                HeapProfilerDump GetHeapProfile
                                ProfilerStart ProfilerStop
                                _ZN15HeapLeakCheckerC1EPKc
                                _ZN15HeapLeakChecker9DoNoLeaksENS_15ShouldSymbolizeE
                                _ZN15HeapLeakCheckerD1Ev)
    list(APPEND gprof_linker_flags "-Wl,--defsym,${undef_symbol}=0")
  endforeach()
  set_target_properties(GaudiGoogleProfiling PROPERTIES LINK_OPTIONS "${gprof_linker_flags}")
else()
    message(STATUS "GaudiProfiling: the module Google PerfTools profiler is disabled")
endif()

#-----------------------------------
# Intel VTune profiler
#-----------------------------------
if(GAUDI_USE_INTELAMPLIFIER)
  gaudi_add_module(IntelProfiler
                   SOURCES src/component/intel/IntelProfilerAuditor.cpp
                   LINK GaudiKernel
                        IntelAmplifier::libittnotify)
  gaudi_add_module(GaudiIntelProfiling
                   SOURCES src/component/intel/IntelProfile.cpp
                   LINK GaudiKernel
                        GaudiAlgLib
                        IntelAmplifier::libittnotify)
else()
  message(STATUS "GaudiProfiling: the module Intel VTune profiler is disabled")
endif()
Intel VTune Amplifier is the only optional dependency that is not enable by default. It it is disable this is the only block of code that is not processed. SyntaxHighlightingPlugin: Language cmake is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.
#-----------------------------------
# Valgrind profiler
#-----------------------------------
gaudi_add_module(GaudiValgrindProfiling
                 SOURCES src/component/valgrind/CallgrindProfile.cpp
                 LINK GaudiKernel
                      GaudiAlgLib
                      ZLIB::ZLIB)

#-----------------------------------
# jemalloc
#-----------------------------------
gaudi_add_module(GaudiJemalloc
                 SOURCES src/component/jemalloc/JemallocProfile.cpp
                         src/component/jemalloc/JemallocProfileSvc.cpp
                 LINK GaudiKernel
                      GaudiAlgLib)

# Special handling of unresolved symbols in Jemmalloc.
# The profilers need to have libjemalloc.so pre-loaded to
# work, so it's better if the symbols stay undefined in case somebody tries to
# use the profilers without preloading the libraries.
set_target_properties(GaudiJemalloc PROPERTIES LINK_OPTIONS "-Wl,--defsym,mallctl=0")

if(GAUDI_USE_JEMALLOC)
  add_test(NAME GaudiProfiling.jira.gaudi_1045
           COMMAND run ${CMAKE_COMMAND} -E env LD_PRELOAD=$<TARGET_PROPERTY:jemalloc::jemalloc,LOCATION> gaudirun.py)
endif()
Here, we do not link against jemalloc, instead we just need to preload it in order the test not to fail. SyntaxHighlightingPlugin: Language cmake is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.
# Register the header only library (install include/)
gaudi_add_header_only_library(GaudiProfilingHeaders)
One of the 2 header only library of Gaudi. The other is in GaudiHive. %ENDCODE%

GaudiPython

SyntaxHighlightingPlugin: Language cmake is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.
gaudi_add_dictionary(GaudiPythonDict
                     HEADERFILES dict/kernel.h
                     SELECTION dict/selection_kernel.xml
                     LINK GaudiPythonLib
                          GaudiUtilsLib)
if(GAUDI_USE_AIDA)
   target_compile_definitions(GaudiPythonDict PRIVATE AIDA_FOUND)
endif()
if(GAUDI_USE_CLHEP)
   target_compile_definitions(GaudiPythonDict PRIVATE CLHEP_FOUND)
endif()
Here, we give the information that some optional dependencies are used to the header.

GaudiRelease

Actually, I have nerver tried to do a fake release of Gaudi. I do not even know if this package work nor what it should produce (documentation generated by doxygen). What is important to know though, is that this package is one of the 3 places where a glob pattern is used. It means that you need to reconfigure if you want it to be re-evaluated. SyntaxHighlightingPlugin: Language cmake is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.
file(GLOB project_rel_notes ${CMAKE_CURRENT_SOURCE_DIR}/doc/release.notes*.html)

PartPropSvc

SyntaxHighlightingPlugin: Language cmake is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.
# PartPropSvc subdirectory

# If HepPDT is not available, give up on this subdirectory:
if(NOT GAUDI_USE_HEPPDT)
    message(WARNING "Requirements for building PartPropSvc disabled. "
        "PartPropSvc will not be built. "
        "HepPDT is disabled.")
    return()
endif()

# Build the plugin
gaudi_add_module(PartPropSvc
                 SOURCES src/PartPropSvc.cpp
                 LINK GaudiKernel
                      Boost::headers
                      Boost::regex
                      HepPDT::heppdt)
The only target built by this package requires HepPDT so if it is disabled we do not build it. SyntaxHighlightingPlugin: Language cmake is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.
# # Install shared files TODO: if it is not used, drop the whole directory
# install(FILES share/PartPropSvc.py
#         DESTINATION jobOptions/PartPropSvc)
# install(FILES share/PDGTABLE.MeV
#               share/PDGTABLE.2002.MeV
#               share/PDGTABLE.2014.MeV
#         DESTINATION share)
We still need to ask someone from ATLAS if these files need to be installed.

Top level CMakeLists.txt

SyntaxHighlightingPlugin: Language cmake is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.
cmake_minimum_required(VERSION 3.15)
The configuration needs at least CMake 3.15. SyntaxHighlightingPlugin: Language cmake is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.
project(Gaudi VERSION 32.0
              LANGUAGES CXX
              DESCRIPTION "CERN Gaudi framework"
              HOMEPAGE_URL "https://gitlab.cern.ch/gaudi/Gaudi")
VERSION is used is the configuration. LANGUAGES is here to disable the C language and thus the C compiler checks. SyntaxHighlightingPlugin: Language cmake is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.
# Add the "Developer" build type
include(cmake/DeveloperBuildType.cmake)
# CMAKE_USE_CCACHE for ccache
include(cmake/CcacheOption.cmake)

# Import Gaudi functions (gaudi_*) ==> for documentation look in the file
include(cmake/GaudiToolbox.cmake)
Here we add our custom modules to the configuration. Should DeveloperBuildType and CcacheOption be moved, we would need to update CMAKE_MODULE_PATH (not an environment variable yet) and remove cmake/ and .cmake of calls to include(). SyntaxHighlightingPlugin: Language cmake is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.
# Export the list of compile commands
set(CMAKE_EXPORT_COMPILE_COMMANDS ON CACHE BOOL "Enable/Disable output of compile_commands.json" FORCE)
Standard CMake here to generate compile_commands.json. SyntaxHighlightingPlugin: Language cmake is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.
# Options to choose dependencies that are not required to build Gaudi
foreach(optional_dependency IN ITEMS AIDA XERCESC CLHEP HEPPDT CPPUNIT UNWIND
                                     GPERFTOOLS DOXYGEN JEMALLOC)
  option(GAUDI_USE_${optional_dependency} "Enable the dependency ${optional_dependency}" ON)
  set(GAUDI_OPTIONAL_DEPENDENCIES "${GAUDI_OPTIONAL_DEPENDENCIES}set(GAUDI_USE_${optional_dependency} ${GAUDI_USE_${optional_dependency}})\n")
endforeach()
# By default VTune is OFF
option(GAUDI_USE_INTELAMPLIFIER "Enable the dependency INTELAMPLIFIER" OFF)
set(GAUDI_OPTIONAL_DEPENDENCIES "${GAUDI_OPTIONAL_DEPENDENCIES}set(GAUDI_USE_INTELAMPLIFIER ${GAUDI_USE_INTELAMPLIFIER})\n")
Here, it is a factored out way of defining the options to enable/disable the optional dependencies. First we declare the option with the default value of ON. Then we append to GAUDI_OPTIONAL_DEPENDENCIES a line of CMake code to set the variable to the real value of this variable at configure time. GAUDI_OPTIONAL_DEPENDENCIES is expanded is GaudiConfig.cmake. There is one particular case, namely Intel VTune Amplifier, that is disable by default for licencing reasons. SyntaxHighlightingPlugin: Language cmake is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.
# Find all the dependencies of the project
list(PREPEND CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake/modules") # (Find*.cmake)
if(DEFINED CACHE{ROOT_DIR}) # Not to have the output displayed each time
  set(GAUDI_DEPENDENCIES_FIND_QUIETLY TRUE)
endif()
include(cmake/GaudiDependencies.cmake)
Here we call GaudiDependencies.cmake to do the dependency lookup. First we update the location of find-module-files. Then we use a feature from CMake 3.14 to know if a variable defined by a dependency exists in CMakeCache to know if it is the first configuration or not. SyntaxHighlightingPlugin: Language cmake is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.
# The C++ standard used must be the same as ROOT's
if(NOT GAUDI_CXX_STANDARD)
  # FIXME: the day ROOT_CXX_STANDARD is provided by ROOTConfig.cmake, remove this lookup
  set(ROOT_CXX_STANDARD ${ROOT_CXX_FLAGS}) # try to parse ROOT compile flags
  separate_arguments(ROOT_CXX_STANDARD)
  list(FILTER ROOT_CXX_STANDARD INCLUDE REGEX "-std=(c|gnu)\\+\\+[0-9]+")
  list(TRANSFORM ROOT_CXX_STANDARD REPLACE "-std=(c|gnu)\\+\\+([0-9]+)" "\\2")
  if(NOT ROOT_CXX_STANDARD)
    message(FATAL_ERROR "Cannot find the C++ standard of ROOT")
  endif()
  list(GET ROOT_CXX_STANDARD 0 ROOT_CXX_STANDARD)
  set(GAUDI_CXX_STANDARD ${ROOT_CXX_STANDARD} CACHE STRING "The version of C++ used to compile Gaudi")
endif()
message(STATUS "Gaudi will be built using C++${GAUDI_CXX_STANDARD} standard.")
set(CMAKE_CXX_STANDARD ${GAUDI_CXX_STANDARD})
set(CMAKE_CXX_STANDARD_REQUIRED TRUE) # do not fall back on older version
set(CMAKE_CXX_EXTENSIONS OFF) # strict c++.. instead of gnu++..
Here, we retrieve the C++ standard used by ROOT to use the same for Gaudi. It is mandatory because ROOT dictionaries use C++ that will be parsed by cling embedded in ROOT. We cannot use a more recent C++ standard otherwise cling would not understand it. To retrieve this standard, we parse ROOT's compile flags (that are imported in ROOTConfig.cmake). We look for -std=c++17 for instance. This parsing will fail if the flag is -std=c++1z for example. SyntaxHighlightingPlugin: Language cmake is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.
# Prevent the installation if exporting to the build tree
option(GAUDI_BUILD_TREE_AS_INSTALL_AREA "Set this to ON to disable \
installation of Gaudi but be able to use the build tree as an installed Gaudi" OFF)
if(GAUDI_BUILD_TREE_AS_INSTALL_AREA)
  install(CODE "message(FATAL_ERROR \"The option GAUDI_BUILD_TREE_AS_INSTALL_AREA \
was set to TRUE at configure time, it is impossible to install ${PROJECT_NAME}.\")")
endif()
Even if GAUDI_BUILD_TREE_AS_INSTALL_AREA is TRUE, gaudi_add_*() functions still call install() thus generating cmake_install.cmake files for each target. In order the installation to fail before attempting anything, we raise a FATAL_ERROR before adding all the sub-projects to the configuration. SyntaxHighlightingPlugin: Language cmake is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.
enable_testing()
Mandatory to call this function once to add a special target call test to the build system. It does not harm if it is called several times. It needs to be done before adding the sub-projects to the configuration. SyntaxHighlightingPlugin: Language cmake is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.
add_subdirectory(GaudiPluginService)
add_subdirectory(GaudiPolicy)
add_subdirectory(GaudiKernel)
add_subdirectory(GaudiCoreSvc)
add_subdirectory(GaudiUtils)
add_subdirectory(Gaudi)
add_subdirectory(GaudiAlg)
add_subdirectory(GaudiAud)
add_subdirectory(GaudiCommonSvc)
add_subdirectory(GaudiHive)
add_subdirectory(GaudiMonitor)
add_subdirectory(GaudiMP)
add_subdirectory(GaudiPartProp)
add_subdirectory(GaudiProfiling)
add_subdirectory(GaudiPython)
add_subdirectory(GaudiRelease)
add_subdirectory(GaudiSvc)
add_subdirectory(PartPropSvc)
add_subdirectory(RootCnv)
add_subdirectory(RootHistCnv)
add_subdirectory(GaudiExamples)
Explicit list of sub-projects without dependency conflicts. SyntaxHighlightingPlugin: Language cmake is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.
# Generate GAUDI_VERSION.h
gaudi_generate_version_header_file()

# Framework level tests
# Test scripts inside cmake/
gaudi_add_tests(nosetests ${PROJECT_SOURCE_DIR}/cmake/extract_qmtest_metadata.py)

if(NOT PROJECT_NAME STREQUAL CMAKE_PROJECT_NAME) # If we are building the full stack as one project
  # we do not install nor package nor register CMake tests
  # but we add Gaudi's CMake modules to the CMAKE_MODULE_PATH for downstream projects
  list(PREPEND CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake)
  set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} PARENT_SCOPE)
  return()
endif()
Here, we do what is necessary for the downstream projects to be able to use what is defined by Gaudi (CMake modules). Since, we cannot install the FullStack project there is no need to process the rest of the file. The trick used here is if the name of the top level project is not the one we are in, we are in a project of a stack. SyntaxHighlightingPlugin: Language cmake is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.
# Unit tests of gaudi_* functions
include(cmake/tests/testGaudiToolbox.cmake)
# Unit tests of the installation
include(cmake/tests/testGaudiInstallation.cmake)
# Unit tests of Gaudi as dependency in a downstream project (with config files)
include(cmake/tests/testGaudiDownstream.cmake)
Inclusion of all the tests described earlier. SyntaxHighlightingPlugin: Language cmake is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.
# Installation of Gaudi
include(CMakePackageConfigHelpers)
# Generate the config files
if(GAUDI_BUILD_TREE_AS_INSTALL_AREA)
  set(_gaudi_config_files_location ${CMAKE_CURRENT_SOURCE_DIR}/cmake)
  set(_set_extract_qmtest_metadata "set(extract_qmtest_metadata ${extract_qmtest_metadata} CACHE INTERNAL \"\" FORCE)")
else() # used in GaudiConfig.cmake.in
  set(_gaudi_config_files_location "\${CMAKE_CURRENT_LIST_DIR}")
endif()
If we install Gaudi, the CMake modules will be located in the same folder as GaudiConfig.cmake. If we are in the build tree, they are in the source tree. Here we also set _set_extract_qmtest_metadata for GaudiConfig.cmake.in discussed earlier. SyntaxHighlightingPlugin: Language cmake is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.
configure_package_config_file(cmake/GaudiConfig.cmake.in ${PROJECT_NAME}Config.cmake
  INSTALL_DESTINATION "${GAUDI_INSTALL_CONFIGDIR}"
  PATH_VARS CMAKE_INSTALL_BINDIR CMAKE_INSTALL_LIBDIR CMAKE_INSTALL_INCLUDEDIR
            GAUDI_INSTALL_PLUGINDIR GAUDI_INSTALL_PYTHONDIR
  NO_CHECK_REQUIRED_COMPONENTS_MACRO)
write_basic_package_version_file(${PROJECT_NAME}ConfigVersion.cmake
  COMPATIBILITY AnyNewerVersion)
  
Here we generate GaudiConfig.cmake and GaudiConfigVersion.cmake with a standard module of CMake (CMakePackageConfigHelpers). GAUDI_BUILD_TREE_AS_INSTALL_AREA will be taken into account. SyntaxHighlightingPlugin: Language cmake is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.
# Install or export Gaudi
if(GAUDI_BUILD_TREE_AS_INSTALL_AREA) # no installation, use build tree
  # Generate the file to import the exported targets in downstream projects
  export(EXPORT ${PROJECT_NAME} NAMESPACE ${PROJECT_NAME}::
         FILE "${PROJECT_NAME}Targets.cmake")
  message(STATUS "Using the build tree instead of the install area. ${PROJECT_NAME} cannot be installed.")
Here, we generate a non-relocatable GaudiTargets.cmake with export(EXPORT) that GaudiConfig.cmake in the build tree will use. SyntaxHighlightingPlugin: Language cmake is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.
else() # regular installation
  # Install the set of exported targets
  install(EXPORT ${PROJECT_NAME} NAMESPACE ${PROJECT_NAME}::
              DESTINATION "${CMAKE_INSTALL_PREFIX}"
          FILE "${PROJECT_NAME}Targets.cmake"
              DESTINATION "${GAUDI_INSTALL_CONFIGDIR}")
Here we generate a relocatable GaudiTargets.cmake for an installation. SyntaxHighlightingPlugin: Language cmake is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.
# Install cmake files for downstream project to be able to use Gaudi
  gaudi_install(CMAKE cmake/GaudiToolbox.cmake
                      cmake/GaudiDependencies.cmake
                      cmake/extract_qmtest_metadata.py # used in gaudi_add_tests(QMTest)
                      cmake/DeveloperBuildType.cmake
                      cmake/CcacheOption.cmake
                      "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake"
                      "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake"
                      cmake/modules) # Find*.cmake
CMake required files for downstream projects. Should DeveloperBuildType and CcacheOption be move, we would need to remove them from this list. SyntaxHighlightingPlugin: Language cmake is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.
# Add an uninstall target
  add_custom_target(uninstall
    COMMAND test -f ${CMAKE_CURRENT_BINARY_DIR}/install_manifest.txt &&
            xargs rm < ${CMAKE_CURRENT_BINARY_DIR}/install_manifest.txt &&
            rm ${CMAKE_CURRENT_BINARY_DIR}/install_manifest.txt &&
            echo ${PROJECT_NAME} uninstalled successfully. ||
            echo ${PROJECT_NAME} has not been installed yet or you need to be root to uninstall.
    COMMENT "Uninstalling ${PROJECT_NAME}")
endif()
This uninstall target is only for convenience when we want to test the installation. We do not expect you to need it. Should Gaudi be built on Windows make sur using Unix commands works (e.g. with PowerShell). SyntaxHighlightingPlugin: Language cmake is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.
# CPack configuration
set(CPACK_PACKAGE_NAME ${PROJECT_NAME})
set(CPACK_PACKAGE_VENDOR "CERN-LHCb")
set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "${PROJECT_DESCRIPTION}")
set(CPACK_PACKAGE_VERSION ${PROJECT_VERSION})
foreach(v IN ITEMS MAJOR MINOR PATCH)
   set(CPACK_PACKAGE_VERSION_${v} ${PROJECT_VERSION_${v}})
endforeach()
if(DEFINED BINARY_TAG)
  set(CPACK_SYSTEM_NAME ${BINARY_TAG})
elseif(DEFINED ENV{BINARY_TAG})
  set(CPACK_SYSTEM_NAME $ENV{BINARY_TAG})
endif()
set(CPACK_GENERATOR "ZIP;RPM") # package format
# The following line is needed to prevent the compilation of python modules before packaging (an incorrect version of python may be used).
# FIXME: the day we can tell CMake to prepend the call to rpm by the run script, do it and remove this line (something like ``set(CPACK_RPM_LAUNCHER ${CMAKE_BINARY_DIR}/run)``)
set(CPACK_RPM_SPEC_MORE_DEFINE "%global __os_install_post %(echo '%{__os_install_post}' | sed -e 's!/usr/lib[^[:space:]]*/brp-python-bytecompile[[:space:]].*$!!g')")
# -- for the sources
set(CPACK_SOURCE_IGNORE_FILES "/InstallArea/;/build\\\\..*/;/\\\\.git/;/\\\\.git.*")
set(CPACK_SOURCE_GENERATOR "ZIP;RPM")
include(CPack)
Actually, the CPack configuration is not needed and is just here to see if it works. It adds two targets to the build, namely package and package_source.

Makefile

This Makefile is actually a wrapper to build Gaudi more easily. SyntaxHighlightingPlugin: Language cmake is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.
# settings
CMAKE := cmake
CTEST := ctest

# Guess if a toolchain or cache preload should be used
ifneq ($(wildcard $(CURDIR)/toolchain.cmake),)
  override CMAKEFLAGS += -D CMAKE_TOOLCHAIN_FILE=$(CURDIR)/toolchain.cmake
endif
ifneq ($(wildcard $(CURDIR)/cache_preload.cmake),)
  override CMAKEFLAGS += -C $(CURDIR)/cache_preload.cmake
endif
If a file called toolchain.cmake exists, it is used as toolchain file for Gaudi's configuration. If a file called cache_preload.cmake exists, it is used to pre-load the CMakeCache.cmake of Gaudi's configuration. NB: the cache is pre-loaded before the toolchain file is processed. SyntaxHighlightingPlugin: Language cmake is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.
# Retrieve the platform we build on
ifndef BINARY_TAG
  ifdef CMAKECONFIG
    BINARY_TAG := ${CMAKECONFIG}
  else
    ifdef CMTCONFIG
      BINARY_TAG := ${CMTCONFIG}
      $(info You are using CMTCONFIG but your BINARY_TAG is not set, you should `export BINARY_TAG=$$CMTCONFIG`)
    else
      BINARY_TAG := unknown
    endif
  endif
endif
Attempts to find the platform you want to build for. SyntaxHighlightingPlugin: Language cmake is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.
BUILDDIR := $(CURDIR)/build.$(BINARY_TAG)
Default name of the build tree. SyntaxHighlightingPlugin: Language cmake is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.
# default target
all:

# deep clean
purge:
	$(RM) -r $(BUILDDIR) $(CURDIR)/InstallArea/$(BINARY_TAG)
	find $(CURDIR) "(" -name "InstallArea" -prune -o -name "*.pyc" -o -name "*ref.~*~.new" ")" -a -type f -exec $(RM) -v \{} \;
make purge removes everything related to the build. SyntaxHighlightingPlugin: Language cmake is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.
# delegate any target to the build directory (except 'purge')
ifneq ($(MAKECMDGOALS),purge)
%: $(BUILDDIR)/CMakeCache.txt FORCE
	+$(CMAKE) --build $(BUILDDIR) -t $* -- $(BUILDFLAGS)
endif

# aliases
.PHONY: configure remove_cache FORCE
ifneq ($(wildcard $(BUILDDIR)/CMakeCache.txt),)
configure: remove_cache $(BUILDDIR)/CMakeCache.txt
else
configure: $(BUILDDIR)/CMakeCache.txt
endif
	@ # do not delegate further
If we configure for the first time, we configure. If we call make configure on an already configured project, it will be reconfigured completely (remove CMakeCache.cmake then configure). This enables us to reconfigure cleanly the project from scratch without calling make purge which would erase all the binaries. SyntaxHighlightingPlugin: Language cmake is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.
# This wrapping around the test target is used to ensure the generation of
# the XML output from ctest.
test: $(BUILDDIR)/CMakeCache.txt
	$(RM) -r $(BUILDDIR)/Testing $(BUILDDIR)/html
	-cd $(BUILDDIR) && $(CTEST) -T test $(ARGS)
Produce the XML report to be converted with CTestXML2HTML which is no longer part of Gaudi (formerly in GaudiPolicy/scripts). SyntaxHighlightingPlugin: Language cmake is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.
ifeq ($(VERBOSE),)
# less verbose install (see GAUDI-1018)
# (emulate the default CMake install target)
install: all
	$(CMAKE) --install $(BUILDDIR) | grep -v "^-- Up-to-date:"
endif
Here we install with CMake 3.15 command line. SyntaxHighlightingPlugin: Language cmake is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.
# ensure that the target are always passed to the CMake Makefile
FORCE:
	@ # dummy target

# Makefiles are used as implicit targets in make, but we should not consider
# them for delegation.
$(MAKEFILE_LIST):
	@ # do not delegate further

# trigger CMake configuration
$(BUILDDIR)/CMakeCache.txt:
	CMAKE_GENERATOR=Ninja $(CMAKE) -S $(CURDIR) -B $(BUILDDIR) $(CMAKEFLAGS)
# to force reconfiguration
remove_cache:
	@rm -f $(BUILDDIR)/CMakeCache.txt
To configure, we use the current directory as source tree. The build tree is the one described earlier. We forward the flags you want to pass to CMake. CMAKE_GENERATOR is here to set the default generator used by CMake. It is overridden by -G if specified in CMAKEFLAGS.

cache_preload.cmake

This file is not tracked by git. It is useful if you want modify the default configuration of Gaudi. Example: SyntaxHighlightingPlugin: Language cmake is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.
echo "set(CMAKE_USE_CCACHE ON CACHE BOOL \"Use ccache\")" > cache_preload.cmake

toolchain.cmake

Most of the time this file is a symbolic link to a toolchain on CVMFS (or for the time being, in Gaudi). SyntaxHighlightingPlugin: Language cmake is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.
ln -s cmake/toolchains/$BINARY_TAG.cmake toolchain.cmake

GaudiToolbox

SyntaxHighlightingPlugin: Language cmake is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.
include_guard(GLOBAL)
Because everything defined in this file is globally visible (namely functions and global imported targets). SyntaxHighlightingPlugin: Language cmake is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.
option(GAUDI_INSTALL_OPTIONAL "Target installation is optional if TRUE" FALSE)
if(GAUDI_INSTALL_OPTIONAL)
    set(_gaudi_install_optional OPTIONAL)
endif()
This is because we will pass ${_gaudi_install_optional} to install() inside gaudi_add_*() functions. So we cannot use directly GAUDI_INSTALL_OPTIONAL which is a boolean. SyntaxHighlightingPlugin: Language cmake is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.
# Option to prevent genconf to fail (turn this to true if you use
# a sanitizer)
option(GAUDI_GENCONF_NO_FAIL "Prevent genconf to fail (for builds with a sanitizer)" FALSE)
if(GAUDI_GENCONF_NO_FAIL)
    set(_gaudi_no_fail "|| true")
endif()
Same explanation as GAUDI_INSTALL_OPTIONAL. SyntaxHighlightingPlugin: Language cmake is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.
set(CMAKE_INSTALL_BINDIR "bin" CACHE STRING "Install executable in <prefix>/\${CMAKE_INSTALL_BINDIR}")
set(CMAKE_INSTALL_LIBDIR "lib" CACHE STRING "Install libraries in <prefix>/\${CMAKE_INSTALL_LIBDIR}")
set(CMAKE_INSTALL_INCLUDEDIR "include" CACHE STRING "Install public headers in <prefix>/\${CMAKE_INSTALL_INCLUDEDIR}")
set(GAUDI_INSTALL_PLUGINDIR "${CMAKE_INSTALL_LIBDIR}" CACHE STRING "Install plugins in <prefix>/\${GAUDI_INSTALL_PLUGINDIR}")
set(GAUDI_INSTALL_PYTHONDIR "python" CACHE STRING "Install python packages in <prefix>/\${GAUDI_INSTALL_PYTHONDIR}")
set(GAUDI_INSTALL_CONFIGDIR "lib/cmake/${PROJECT_NAME}" CACHE STRING "Install cmake files in <prefix>/\${GAUDI_INSTALL_CONFIGDIR}")
Default installation layout. It is defined in the cache to be modified more easily. SyntaxHighlightingPlugin: Language cmake is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.
function(gaudi_add_library lib_name)
    cmake_parse_arguments(
        ARG
        ""
        ""
        "SOURCES;LINK"
        ${ARGN}
    )
Like many other gaudi_add_*() functions, this one has a SOURCES argument for the list of source files. We could have done without a SOURCES keyword and have used ARG_UNPARSED_ARGUMENTS to get the list of sources. It would actually be more consistent with CMake add_executable() and add_library() but modifing a bit the signature of the function will make people realise that something has changed, glob patterns are no onger handled. LINK is in fact a wrapper around target_link_libraries(). It is here to ease the use of dependencies. Without it you would have to call gaudi_add_*() then target_link_libraries(). Furthermore, it will make people realise that we do no longer use LINK_LIBRARIES or INCLUDE_DIRS because we do not link against libraries or use their include directories, we link against targets that handle the transitivity themself. SyntaxHighlightingPlugin: Language cmake is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.
add_library(${lib_name} SHARED "${ARG_SOURCES}")
All the libraries we build are shared objects. We could also have not specified the type of library built when calling add_library() and have set BUILD_SHARED_LIBS to TRUE. On one hand it would have enable the user to build either static or shared libraries but on the other hand, the function it better sefl-contained this way. SyntaxHighlightingPlugin: Language cmake is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.
# Public headers
    if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/include)
        target_include_directories(${lib_name}
            PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>)
    endif()
We use a generator expression to set the public header directory because it will change after installation. SyntaxHighlightingPlugin: Language cmake is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.
# Dependencies
    if(ARG_LINK)
        target_link_libraries(${lib_name} ${ARG_LINK})
    endif()
Wrapper. SyntaxHighlightingPlugin: Language cmake is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.
# Alias
    add_library(${PROJECT_NAME}::${lib_name} ALIAS ${lib_name})
lib_name is the name of the target inside Gaudi. Gaudi::lib_name is the name of the imported target. We can use Gaudi::lib_name everywhere in downstream project. It will work with an imported Gaudi and a stack build. SyntaxHighlightingPlugin: Language cmake is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.
# Install the library
    install(
        TARGETS ${lib_name}
        EXPORT  ${PROJECT_NAME}
        INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
        ${_gaudi_install_optional})
We install the target and add it to an export set that has the name of the project. This export set is used by export(EXPORT) or install(EXPORT) to generate GaudiTargets.cmake. We also specify the location of public headers after the installation. SyntaxHighlightingPlugin: Language cmake is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.
# -- and the include directory
    get_property(include_installed DIRECTORY PROPERTY include_installed)
    if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/include AND NOT include_installed)
        install(DIRECTORY include/
                TYPE INCLUDE)
        set_property(DIRECTORY PROPERTY include_installed TRUE)
    endif()
We install the include directory. We use a directory property to know if the include/ folder of this directory has already been installed because a sub-project may build several shared libraries (e.g. GaudiPluginService). SyntaxHighlightingPlugin: Language cmake is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.
# Runtime ROOT_INCLUDE_PATH
    set_property(TARGET target_runtime_paths APPEND
        PROPERTY runtime_root_include_path $<TARGET_PROPERTY:${lib_name},INTERFACE_INCLUDE_DIRECTORIES>)
endfunction()
Finally, we extend the runtime ROOT_INCLUDE_PATH with all the directories containing public headers for this target. We use a generator expression because you may add other include directories with target_include_directories() afterward. SyntaxHighlightingPlugin: Language cmake is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.
function(gaudi_add_header_only_library lib_name)
    cmake_parse_arguments(
        ARG
        ""
        ""
        "LINK"
        ${ARGN}
    )
    add_library(${lib_name} INTERFACE)
Here we need to define an interface target because we do not have any sources. This is CMake way to define header only libraries. SyntaxHighlightingPlugin: Language cmake is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.
# Public headers
    if(NOT EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/include)
        message(FATAL_ERROR "Cannot find public headers in include/")
    endif()
    target_include_directories(${lib_name}
        INTERFACE $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>)
The headers. SyntaxHighlightingPlugin: Language cmake is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.
# Dependencies
    if(ARG_LINK)
        target_link_libraries(${lib_name} INTERFACE ${ARG_LINK})
    endif()
Since the target is an interface target, we only have interface libraries. This is for CMake to handle the transitivity of dependencies. SyntaxHighlightingPlugin: Language cmake is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.
# Alias
    add_library(${PROJECT_NAME}::${lib_name} ALIAS ${lib_name})
    # Install the library
    install(
        TARGETS ${lib_name}
        EXPORT  ${PROJECT_NAME}
        INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
        ${_gaudi_install_optional})
    get_property(include_installed DIRECTORY PROPERTY include_installed)
    if(NOT include_installed)
        install(DIRECTORY include/
                TYPE INCLUDE)
        set_property(DIRECTORY PROPERTY include_installed TRUE)
    endif()
    # Runtime ROOT_INCLUDE_PATH
    set_property(TARGET target_runtime_paths APPEND
        PROPERTY runtime_root_include_path $<TARGET_PROPERTY:${lib_name},INTERFACE_INCLUDE_DIRECTORIES>)
endfunction()
Same as gaudi_add_library(). SyntaxHighlightingPlugin: Language cmake is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.
function(gaudi_add_module plugin_name)
    cmake_parse_arguments(
        ARG
        ""
        ""
        "SOURCES;LINK;GENCONF_OPTIONS"
        ${ARGN}
    )
Here we have one more argument: GENCONF_OPTIONS to be able to pass options to genconf. SyntaxHighlightingPlugin: Language cmake is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.
add_library(${plugin_name} MODULE "${ARG_SOURCES}")
A module library is a plugin. SyntaxHighlightingPlugin: Language cmake is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.
# dependencies
    if(ARG_LINK)
        target_link_libraries(${plugin_name} PRIVATE ${ARG_LINK})
    endif()
All the dependencies of a plugin are PRIVATE because it is meant to be loaded dynamically. SyntaxHighlightingPlugin: Language cmake is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.
# install
    install(
        TARGETS ${plugin_name}
        LIBRARY DESTINATION "${GAUDI_INSTALL_PLUGINDIR}"
        ${_gaudi_install_optional})
You may decide to install plugin in a different directory than shared libraries. SyntaxHighlightingPlugin: Language cmake is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.
# Create a symlink to the module in a directory that is added to the rpath of every target of the build
    add_custom_command(TARGET ${plugin_name} POST_BUILD
        BYPRODUCTS ${CMAKE_BINARY_DIR}/.plugins/${CMAKE_SHARED_MODULE_PREFIX}${plugin_name}${CMAKE_SHARED_MODULE_SUFFIX}
        COMMAND ${CMAKE_COMMAND} -E create_symlink $<TARGET_FILE:${plugin_name}> ${CMAKE_BINARY_DIR}/.plugins/$<TARGET_FILE_NAME:${plugin_name}>)
This is for an easier usage at runtime because the plugins have to be found in the LD_LIBRARY_PATH. Unfortunately, it is not possible to use generator expressions in BYPRODUCTS. SyntaxHighlightingPlugin: Language cmake is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.
# generate the list of components for all the plugins of the project
    add_custom_command(TARGET ${plugin_name} POST_BUILD
        BYPRODUCTS ${plugin_name}.components
        COMMAND run $<TARGET_FILE:Gaudi::listcomponents> --output ${plugin_name}.components $<TARGET_FILE_NAME:${plugin_name}>)
Here we use listcomponents built by GaudiPluginService to generate a .components file of the plugin. We run it in th runtime environment. SyntaxHighlightingPlugin: Language cmake is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.
_merge_files(MergeComponents "${CMAKE_BINARY_DIR}/${PROJECT_NAME}.components" # see private functions at the end
        ${plugin_name} "${CMAKE_CURRENT_BINARY_DIR}/${plugin_name}.components"
        "Merging .components files")
All .components files of all plugins are merged at the end of the build into Gaudi.components. SyntaxHighlightingPlugin: Language cmake is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.
# genconf
    get_filename_component(package_name ${CMAKE_CURRENT_SOURCE_DIR} NAME)
    file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/genConf/${package_name})
    add_custom_command(TARGET ${plugin_name} POST_BUILD
        BYPRODUCTS genConf/${package_name}/${plugin_name}.confdb genConf/${package_name}/${plugin_name}Conf.py # genConf/${package_name}/__init__.py
        COMMAND run $<TARGET_FILE:Gaudi::genconf>
            --configurable-module=GaudiKernel.Proxy
            --configurable-default-name=Configurable.DefaultName
            --configurable-algorithm=ConfigurableAlgorithm
            --configurable-algtool=ConfigurableAlgTool
            --configurable-auditor=ConfigurableAuditor
            --configurable-service=ConfigurableService
            ${ARG_GENCONF_OPTIONS}
            -o genConf/${package_name}
            -p ${package_name}
            -i $<TARGET_FILE:${plugin_name}>
            ${_gaudi_no_fail}
        COMMAND_EXPAND_LISTS)
Here we create a genConf/ directory. It will be populated by genconf outputs, namely a .confdb, a Conf.py and an __init__.py. Notice that the __init__.py is not listed as BYPRODUCTS because a sub-project may build several module libraries, thus producing several __init__.py. In CMake a file cannot be produced by more than one command. We run it in the runtime environment. By default, genconf works on an AthenaProject that is why we have the "--configurable-"s. SyntaxHighlightingPlugin: Language cmake is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.
if(TARGET listcomponents) # if we are building Gaudi (this function is not called from a downstream project)
        add_dependencies(${plugin_name} listcomponents genconf GaudiCoreSvc)
    endif()
If we are building Gaudi (or the full stack), we need listcomponents and genconf to be built before being used. SyntaxHighlightingPlugin: Language cmake is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.
if(NOT EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/python/${package_name}/__init__.py) # if there is no python package in the source tree
        set(init_file "${CMAKE_CURRENT_BINARY_DIR}/genConf/${package_name}/__init__.py") # we install the __init__.py generated by genconf
    endif()
    install(FILES ${CMAKE_CURRENT_BINARY_DIR}/genConf/${package_name}/${plugin_name}Conf.py
            ${init_file}
        DESTINATION "${GAUDI_INSTALL_PYTHONDIR}/${package_name}" # Install the python module alongside the other python modules of this package
        ${_gaudi_install_optional})
This is needed to install the __init__.py file because it is not installed why the rest. You may want to build several modules in the same sub-project. They will all be processed by genconf SyntaxHighlightingPlugin: Language cmake is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.
# Merge the .confdb files
    _merge_files_confdb(${plugin_name} "${CMAKE_CURRENT_BINARY_DIR}/genConf/${package_name}/${plugin_name}.confdb") # see private functions at the end
This is different from the .components files because gaudi_add_module() is not the only function to produce .confdb, gaudi_generate_confuserdb() does it to. SyntaxHighlightingPlugin: Language cmake is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.
# To append the path to the generated library to LD_LIBRARY_PATH with run
    set_property(TARGET target_runtime_paths APPEND
        PROPERTY runtime_ld_library_path $<SHELL_PATH:$<TARGET_FILE_DIR:${plugin_name}>>)
    # To append the path to the generated Conf.py file to PYTHONPATH
    set_property(TARGET target_runtime_paths APPEND
        PROPERTY runtime_pythonpath $<SHELL_PATH:${CMAKE_CURRENT_BINARY_DIR}/genConf>)
endfunction()
We append to LD_LIBRARY_PATH. This should not be needed since we have a .plugins/ folder that contains symlinks to the pluigns but for some reasons it is not enough. We also add to the PYTHONPATH the path to the python package generated by genconf. SyntaxHighlightingPlugin: Language cmake is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.
function(gaudi_add_python_module module_name)
    cmake_parse_arguments(
        ARG
        ""
        "PACKAGE"
        "SOURCES;LINK"
        ${ARGN}
    )
    # Detect which python package is used and compile the module
    # FIXME: (The day CMake can invoke functions whose name are in a variable, use a loop)
    if(COMMAND Python_add_library)
        Python_add_library(${module_name} MODULE ${ARG_SOURCES})
    elseif(COMMAND Python3_add_library)
        Python3_add_library(${module_name} MODULE ${ARG_SOURCES})
    elseif(COMMAND Python2_add_library)
        Python2_add_library(${module_name} MODULE ${ARG_SOURCES})
    else()
        message(FATAL_ERROR "Python is not available, call find_package(Python) first.")
    endif()
This is needed because in CMake we may find_package(Python|Python2|Python3) (NB: it is recommended to use Python unless we need to handle both Python2 and Python3 in the same project). Python_add_library() is a add_library() that is defined by FindPython.cmake and link by default to Python::Module. SyntaxHighlightingPlugin: Language cmake is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.
# dependencies
    if(ARG_LINK)
        target_link_libraries(${module_name} PRIVATE ${ARG_LINK})
    endif()
It is a module so it only has private dependencies. SyntaxHighlightingPlugin: Language cmake is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.
# Add it to the runtime PYTHONPATH
    set_property(TARGET target_runtime_paths APPEND
        PROPERTY runtime_pythonpath $<SHELL_PATH:${CMAKE_CURRENT_BINARY_DIR}>)
    # install
    if(ARG_PACKAGE)
        set(package_path "${ARG_PACKAGE}")
        get_filename_component(package_name "${package_path}" NAME)
    else()
        get_filename_component(package_name "${CMAKE_CURRENT_SOURCE_DIR}" NAME)
        set(package_path "${CMAKE_CURRENT_SOURCE_DIR}/python/${package_name}")
    endif()
    if(NOT EXISTS "${package_path}/__init__.py")
        message(FATAL_ERROR "${package_path}/__init__.py"
            " must exist and the package must be installed (see gaudi_install(PYTHON)).")
    endif()
    install(
        TARGETS ${module_name}
        LIBRARY DESTINATION "${GAUDI_INSTALL_PYTHONDIR}/${package_name}"
        ${_gaudi_install_optional})
endfunction()
We add it to the PYTHONPATH. The compiled python module can be added to any other python package at installation. SyntaxHighlightingPlugin: Language cmake is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.
function(gaudi_add_executable exe_name)
    cmake_parse_arguments(
        ARG
        "TEST"
        ""
        "SOURCES;LINK"
        ${ARGN}
    )
    add_executable(${exe_name} "${ARG_SOURCES}")
    # dependencies
    if(ARG_LINK)
        target_link_libraries(${exe_name} PRIVATE ${ARG_LINK})
    endif()
We cannot link against an executable, so all dependencies are private. You may use standard CMake later to enable POSITION_INDEPENDENT_CODE and add public dependencies. SyntaxHighlightingPlugin: Language cmake is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.
# test
    if(ARG_TEST)
        get_filename_component(package_name ${CMAKE_CURRENT_SOURCE_DIR} NAME)
        add_test(NAME ${package_name}.${exe_name} COMMAND run $<TARGET_FILE:${exe_name}>)
    endif()
Same idea as LINK. With TEST we wrap the call to add_test(). SyntaxHighlightingPlugin: Language cmake is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.
# install
    if(NOT ARG_TEST)
        set(_export EXPORT ${PROJECT_NAME})
        add_executable(${PROJECT_NAME}::${exe_name} ALIAS ${exe_name})
    endif()
There is no need to export the tests because downstream project will probably not use them. SyntaxHighlightingPlugin: Language cmake is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.
install(
        TARGETS  ${exe_name}
        ${_export}
        ${_gaudi_install_optional})
    # Runtime PATH with run
    set_property(TARGET target_runtime_paths APPEND
        PROPERTY runtime_path $<SHELL_PATH:$<TARGET_FILE_DIR:${exe_name}>>)
endfunction()
We install and export the executable and add them to the runtime PATH. SyntaxHighlightingPlugin: Language cmake is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.
function(gaudi_add_tests type)
    # Naming convention for tests: PackageName.TestName
    get_filename_component(package_name ${CMAKE_CURRENT_SOURCE_DIR} NAME)
This line means get the package name. It is used in lots of places in the code. We do not really need a function to wrap this line. SyntaxHighlightingPlugin: Language cmake is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.
_get_python_interpreter(python) # see private functions at the end
    # Custom test_directory
    if(ARGV1)
        set(test_directory "${ARGV1}")
    endif()

    # Test framework used
    if(type STREQUAL "QMTest")
        if(test_directory)
            set(qmtest_root_dir "${test_directory}")
        else()
            set(qmtest_root_dir "${CMAKE_CURRENT_SOURCE_DIR}/tests/qmtest")
        endif()
        file(GLOB_RECURSE qmt_files RELATIVE "${qmtest_root_dir}" "${qmtest_root_dir}/*.qmt") # ordered lexicographically
This is unfortunate that QMTest cannot auto-detect tests like nosetests and pytest because it forces us to use a glob pattern. It is one of the 3 places where a glob pattern is used in Gaudi. SyntaxHighlightingPlugin: Language cmake is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.
string(TOLOWER "${package_name}" subdir_name_lower)
        file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/qmtest_tmp")
        # Add a test for each qmt files
        foreach(qmt_file IN LISTS qmt_files)
            string(REPLACE ".qms/" "." qmt_name "${qmt_file}")
            string(REPLACE ".qmt" "" qmt_name "${qmt_name}")
            string(REGEX REPLACE "^${subdir_name_lower}\\." "" qmt_name "${qmt_name}")
            add_test(NAME ${package_name}.${qmt_name}
                     COMMAND run ${python} -m GaudiTesting.Run
                        --skip-return-code 77
                        --report ctest
                        --common-tmpdir ${CMAKE_CURRENT_BINARY_DIR}/qmtest_tmp
                        --workdir ${qmtest_root_dir}
                        ${qmtest_root_dir}/${qmt_file}
                     WORKING_DIRECTORY "${qmtest_root_dir}")
            set_tests_properties(${package_name}.${qmt_name} PROPERTIES LABELS QMTest
                                                                        SKIP_RETURN_CODE 77)
        endforeach()
This is how we register a QMTest test with the right naming conventions and how we run it with GaudiTesting.Run. SyntaxHighlightingPlugin: Language cmake is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.
# Extract dependencies to a cmake file
        find_file(extract_qmtest_metadata extract_qmtest_metadata.py
                  PATHS ${Gaudi_SOURCE_DIR}/cmake # When building Gaudi
                        ${Gaudi_DIR}              # When using an installed Gaudi
                  NO_DEFAULT_PATH)
Here we look for a script defined by Gaudi. There is only 2 places were this script can be. It is either in the source tree of Gaudi if we are building it (standalone or full stack) or it is alongside GaudiConfig.cmake if it was installed. Moreover, this variable will be written in GaudiConfig.cmake is GAUDI_BUILD_TREE_AS_INSTALL_AREA has been given at configure time. SyntaxHighlightingPlugin: Language cmake is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.
if(NOT extract_qmtest_metadata)
            message(FATAL_ERROR "Cannot find extract_qmtest_metadata.py")
        endif()
        mark_as_advanced(extract_qmtest_metadata)
        execute_process(COMMAND ${python} ${extract_qmtest_metadata}
                                ${package_name} ${qmtest_root_dir}
                        OUTPUT_FILE ${CMAKE_CURRENT_BINARY_DIR}/qmt_deps.cmake
                        RESULT_VARIABLE qmt_deps_retcode)
        if(NOT qmt_deps_retcode EQUAL "0")
            message(WARNING "Failed to compute dependencies of QMTest tests.")
            return()
        endif()
        # Include the generated file with the QMTest dependencies
        include(${CMAKE_CURRENT_BINARY_DIR}/qmt_deps.cmake)
qmt_deps.cmake is generated by extract_qmtest_metadata it defines in CMake language the dependencies between tests. Thus, we include it to set the dependencies between QMTest tests. SyntaxHighlightingPlugin: Language cmake is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.
elseif(type STREQUAL "nosetests")
        _import_nosetests() # creates the imported target nosetests for the
                            # generator expression $<TARGET_FILE:nosetests>
        if(NOT test_directory)
            set(test_directory "${CMAKE_CURRENT_SOURCE_DIR}/tests/nose")
        endif()
        get_filename_component(name "${test_directory}" NAME)
        add_test(NAME ${package_name}.${name}
                 COMMAND run $<TARGET_FILE:nosetests> -v --with-doctest ${test_directory})
nosetests is able to auto-detect the tests that are defined is a folder. The consequence though, is that for CTest, there is only one test. SyntaxHighlightingPlugin: Language cmake is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.
elseif(type STREQUAL "pytest")
        _import_pytest() # creates the imported target pytest for the
                         # generator expression $<TARGET_FILE:pytest>
        if(NOT test_directory)
            set(test_directory "${CMAKE_CURRENT_SOURCE_DIR}/tests/pytest")
        endif()
        get_filename_component(name "${test_directory}" NAME)
        add_test(NAME ${package_name}.${name}
                COMMAND run $<TARGET_FILE:pytest> -v --doctest-modules ${test_directory})

    else()
        message(FATAL_ERROR "${type} is not a valid test framework.")
    endif()
endfunction()
pytest is similar to nosetests. It is not used yet inside Gaudi. SyntaxHighlightingPlugin: Language cmake is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.
function(gaudi_add_dictionary dictionary)
    cmake_parse_arguments(
        ARG
        ""
        "SELECTION"
        "HEADERFILES;OPTIONS;LINK"
        ${ARGN}
    )
    # Generate sources of the dictionary
    set(gensrcdict ${CMAKE_CURRENT_BINARY_DIR}/${dictionary}.cxx)
    set(rootmapname ${CMAKE_CURRENT_BINARY_DIR}/${dictionary}.rootmap)
    set(pcmfile ${CMAKE_CURRENT_BINARY_DIR}/${dictionary}_rdict.pcm)
    add_custom_command(OUTPUT ${gensrcdict} ${rootmapname} ${pcmfile}
        COMMAND run
                ${ROOT_genreflex_CMD} # comes from ROOTConfig.cmake
                ${ARG_HEADERFILES}
                -o ${gensrcdict}
                --rootmap=${rootmapname}
                --rootmap-lib=lib${dictionary}
                --select=${ARG_SELECTION}
                "-I$<JOIN:$<TARGET_PROPERTY:${dictionary},INCLUDE_DIRECTORIES>,;-I>"
                "-D$<JOIN:$<TARGET_PROPERTY:${dictionary},COMPILE_DEFINITIONS>,;-D>"
                ${ARG_OPTIONS}
        DEPENDS "${ARG_HEADERFILES};${ARG_SELECTION}"
        WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
        COMMENT "Generating ${dictionary}.cxx and ${dictionary}.rootmap and ${dictionary}_rdict.pcm"
        COMMAND_EXPAND_LISTS)
Here, we run genreflex in the runtime environment. We use generator expressions to generate a CMake-style lists of include directories and compile definitions that are then expanded thanks to COMMAND_EXPAND_LISTS. These lists will never be empty because the first element of each is defined later. SyntaxHighlightingPlugin: Language cmake is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.
add_custom_target(${dictionary}-gen ALL DEPENDS "${gensrcdict};${rootmapname};${pcmfile}")
    # Build the dictionary as a plugin
    add_library(${dictionary} MODULE ${gensrcdict})
    if(ARG_LINK)
        target_link_libraries(${dictionary} PRIVATE ${ARG_LINK})
    endif()
    target_include_directories(${dictionary} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
    target_compile_definitions(${dictionary} PRIVATE GAUDI_DICTIONARY)
    # Install the dictionary
    install(
        TARGETS ${dictionary}
        LIBRARY DESTINATION "${GAUDI_INSTALL_PLUGINDIR}"
        ${_gaudi_install_optional})
    install(FILES ${pcmfile}
        DESTINATION "${GAUDI_INSTALL_PLUGINDIR}"
        ${_gaudi_install_optional})
    # Merge all the .rootmap files
    _merge_files(MergeRootmaps "${CMAKE_BINARY_DIR}/${PROJECT_NAME}Dict.rootmap" # see private functions at the end
        ${dictionary}-gen "${CMAKE_CURRENT_BINARY_DIR}/${dictionary}.rootmap"
        "Merging .rootmap files")
endfunction()
We install the dictionary and related files and merge .rootmap files. SyntaxHighlightingPlugin: Language cmake is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.
function(gaudi_install type)
    if(type STREQUAL "PYTHON")
        if(ARGV1)
            set(directory ${ARGV1})
        else()
            set(directory python/)
        endif()
        install(DIRECTORY ${directory}
            DESTINATION "${GAUDI_INSTALL_PYTHONDIR}"
            REGEX "(~|\\.py[co]|\\.in)$" EXCLUDE)
We exclude compiled python modules and configure files from the installation because they are not python modules. SyntaxHighlightingPlugin: Language cmake is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.
if(NOT IS_ABSOLUTE ${directory})
            set(directory ${CMAKE_CURRENT_SOURCE_DIR}/${directory})
        endif()
        # Generate a special __init__.py in the build tree that gather every other pieces
        file(GLOB python_packages LIST_DIRECTORIES true "${directory}/*")
This is the third and last place where a glob pattern is used in Gaudi. Consequently, the last case when we have to reconfigure is when a new python package is added in a sub-project that already contained an installed python package. (If it was not installed, you need to add gaudi_install(PYTHON) to the CMakeLists.txt of the sub-project.) SyntaxHighlightingPlugin: Language cmake is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.
list(FILTER python_packages EXCLUDE REGEX "\\.(py[co]?|in)$")
        foreach(python_package IN LISTS python_packages)
            get_filename_component(python_package_name ${python_package} NAME)
            if(NOT EXISTS ${CMAKE_BINARY_DIR}/python/${python_package_name}/__init__.py)
                file(MAKE_DIRECTORY ${CMAKE_BINARY_DIR}/python/${python_package_name})
                file(WRITE ${CMAKE_BINARY_DIR}/python/${python_package_name}/__init__.py
"import os, sys
__path__ = [d for d in [os.path.join(d, '${python_package_name}') for d in sys.path if d]
            if (d.startswith('${CMAKE_CURRENT_BINARY_DIR}') or
                d.startswith('${CMAKE_CURRENT_SOURCE_DIR}')) and
            (os.path.exists(d) or 'python.zip' in d)]
if os.path.exists('${CMAKE_CURRENT_SOURCE_DIR}/python/${python_package_name}/__init__.py'):
    execfile('${CMAKE_CURRENT_SOURCE_DIR}/python/${python_package_name}/__init__.py')
")
Here we generate a __init__.py file in the build tree that will point to all the scattered parts of the package. In the future we may use python's pkgutil. SyntaxHighlightingPlugin: Language cmake is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.
endif()
        endforeach()
        # Runtime PYTHONPATH with run
        set_property(TARGET target_runtime_paths APPEND
            PROPERTY runtime_pythonpath $<SHELL_PATH:${directory}>)
    elseif(type STREQUAL "SCRIPTS")
        if(ARGV1)
            set(directory ${ARGV1})
        else()
            set(directory scripts/)
        endif()
        install(DIRECTORY ${directory}
            TYPE BIN
            FILE_PERMISSIONS OWNER_READ GROUP_READ WORLD_READ # same as PROGRAMS
                             OWNER_WRITE
                             OWNER_EXECUTE GROUP_EXECUTE WORLD_EXECUTE
            REGEX "(~|\\.py[co]|\\.in)$" EXCLUDE)
Here we install all files that are not compiled python modules or backup files or configure files as PROGRAMS. Remove the explicit list of file permissions the day install(DIRECTORY) has an option to install files with the same permission as install(PROGRAMS) SyntaxHighlightingPlugin: Language cmake is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.
# Runtime PATH with run
        if(NOT IS_ABSOLUTE ${directory})
            set(directory ${CMAKE_CURRENT_SOURCE_DIR}/${directory})
        endif()
        set_property(TARGET target_runtime_paths APPEND
            PROPERTY runtime_path $<SHELL_PATH:${directory}>)
    elseif(type STREQUAL "CMAKE")
        foreach(entity IN LISTS ARGN)
Here, instead of one directory, we may install severals at once. SyntaxHighlightingPlugin: Language cmake is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.
if(NOT IS_ABSOLUTE ${entity})
                set(entity "${CMAKE_CURRENT_SOURCE_DIR}/${entity}")
            endif()
            if(IS_DIRECTORY ${entity})
                install(DIRECTORY "${entity}"
                        DESTINATION "${GAUDI_INSTALL_CONFIGDIR}")
            else()
                install(FILES "${entity}"
                        DESTINATION "${GAUDI_INSTALL_CONFIGDIR}")
            endif()
        endforeach()
    else()
        message(FATAL_ERROR "${type} is not a valid first argument for gaudi_install().")
    endif()
endfunction()
We install directories as directories and files as files. SyntaxHighlightingPlugin: Language cmake is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.
function(gaudi_generate_confuserdb)
    # Get package_name
    get_filename_component(package_name ${CMAKE_CURRENT_SOURCE_DIR} NAME)
    # Handle default value
    if(NOT ARGV)
        set(modules "${package_name}.Configuration")
    else()
        set(modules "${ARGV}")
    endif()
    # Handle options
    file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/genConf/${package_name})
    set(output_file "${CMAKE_CURRENT_BINARY_DIR}/genConf/${package_name}/${package_name}_user.confdb")
    # Add the custom target to generate the _user.confdb file
    string(REPLACE "." "/" modules_path_list "${modules}")
    list(TRANSFORM modules_path_list PREPEND "${CMAKE_CURRENT_SOURCE_DIR}/python/")
    list(TRANSFORM modules_path_list APPEND ".py")
Here we retrieve the paths to the list of python modules. We turn GaudiExamples.Configuration into ${CMAKE_CURRENT_SOURCE_DIR}/python/GaudiExamples/Configuration.py. SyntaxHighlightingPlugin: Language cmake is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.
add_custom_command(OUTPUT "${output_file}"
        COMMAND run genconfuser.py
                -r ${CMAKE_CURRENT_SOURCE_DIR}/python
                -o "${output_file}"
                ${package_name}
                ${modules}
                ${_gaudi_no_fail}
        DEPENDS "${modules_path_list}"
        COMMENT "Generating ConfigurableUser of ${package_name}"
        COMMAND_EXPAND_LISTS)
    add_custom_target(${package_name}_confuserdb ALL DEPENDS "${output_file}")
    # Merge ${package_name}_user.confdb with the others in ${CMAKE_BINARY_DIR}/${PROJECT_NAME}.confdb
    _merge_files_confdb(${package_name}_confuserdb "${output_file}") # see private functions at the end
endfunction()
We merge the _user.confdb files with the other .confdb files. SyntaxHighlightingPlugin: Language cmake is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.
function(gaudi_check_python_module)  
    _get_python_interpreter(python) # see private functions at the end
    set(not_found)
    foreach(module IN LISTS ARGV)
        execute_process(COMMAND ${python} -c "import ${module}"
                        RESULT_VARIABLE returned_value
                        OUTPUT_QUIET ERROR_QUIET)
        if(NOT returned_value EQUAL "0")
            list(APPEND not_found ${module})
        endif()
    endforeach()
    if(not_found)
        message(FATAL_ERROR "The following python module(s) cannot be imported: ${not_found}.")
    endif()
endfunction()
This function can be used in script mode. Trying to import a python module it the easiest way to know if we can import it. Here we do something like FeatureSummary. We try everything and report the list of missing dependencies. SyntaxHighlightingPlugin: Language cmake is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.
function(gaudi_generate_version_header_file)
    # Handle default value
    if(NOT ARGV0)
        string(TOUPPER ${PROJECT_NAME} NAME)
        set(output_file_name ${NAME}_VERSION.h)
        install(FILES ${CMAKE_BINARY_DIR}/include/${output_file_name} TYPE INCLUDE)
    else()
        set(name ${ARGV0})
        string(TOUPPER ${name} NAME)
        set(output_file_name ${name}Version.h)
    endif()
    # No patch number => set to 0
    if(NOT PROJECT_VERSION_PATCH)
        set(PROJECT_VERSION_PATCH 0)
    endif()
We are in a function so this variable is local. The real PROJECT_VERSION_PATCH is not modified. SyntaxHighlightingPlugin: Language cmake is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.
# Generate the configure file only once
    file(GENERATE OUTPUT include/${output_file_name} CONTENT
"#ifndef ${NAME}_VERSION_H
#define ${NAME}_VERSION_H

#ifndef CALC_GAUDI_VERSION
#define CALC_GAUDI_VERSION(maj,min) (((maj) << 16) + (min))
#endif

#define ${NAME}_MAJOR_VERSION ${PROJECT_VERSION_MAJOR}
#define ${NAME}_MINOR_VERSION ${PROJECT_VERSION_MINOR}
#define ${NAME}_PATCH_VERSION ${PROJECT_VERSION_PATCH}

#define ${NAME}_VERSION CALC_GAUDI_VERSION(${NAME}_MAJOR_VERSION,${NAME}_MINOR_VERSION)

#endif // ${NAME}_VERSION_H")
endfunction()
Here we use CALC_GAUDI_VERSION instead of CALC_${PROJECT_NAME}_VERSION because every Gaudi-based project has the same way of computing the version so we do not need several macros.

Now let us explain the private functions. SyntaxHighlightingPlugin: Language cmake is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.

# Get the python interpreter
#   return_python_var: name of the returned value
function(_get_python_interpreter return_python_var)
    # Check which python is available
    set(python)
    foreach(package IN ITEMS Python Python3 Python2)
        if(${package}_Interpreter_FOUND)
            set(python ${${package}_EXECUTABLE})
We use Python[23]_EXECUTABLE instead of Python[23]::Interpreter because this enables the use of this function in script mode. The configuration may use find_package(Python) or find_package(Python2) or find_package(Python3), that is the reason of this loop. SyntaxHighlightingPlugin: Language cmake is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.
break()
        endif()
    endforeach()
    # Display a warning if none was found
    if(NOT python)
        message(WARNING "No python interpreter was found, "
            "call find_package(Python) first.")
    endif()
    # return
    set(${return_python_var} ${python} PARENT_SCOPE)
endfunction()
Setting a value in the parent scope is a way of doing return is CMake. SyntaxHighlightingPlugin: Language cmake is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.
# This functions merge files generated during the build
# (.components, .rootmap, .confdb) in a single one.
#   merge_target: target to merge the byproducts of other targets
#   output_file: the absolute path to the file that will contain all the others
#   dependency: a target on which ${merge_target} depends
#   file_to_merge: the absolute path to the file to be merged in ${output_file}
#   message: a message to display while building ${merge_target}
function(_merge_files merge_target output_file dependency file_to_merge message)
    if(NOT TARGET ${merge_target})
        add_custom_command(OUTPUT "${output_file}"
            COMMAND cat $<TARGET_PROPERTY:${merge_target},fragments> > "${output_file}"
We need to have cat available. We merge all the files that are "fragments" of the ultimate file. SyntaxHighlightingPlugin: Language cmake is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.
DEPENDS $<TARGET_PROPERTY:${merge_target},fragments>
Fortunatly we can use generator expressions in DEPENDS. SyntaxHighlightingPlugin: Language cmake is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.
COMMENT "${message}"
            COMMAND_EXPAND_LISTS)
        add_custom_target(${merge_target} ALL DEPENDS "${output_file}")
        install(FILES "${output_file}"
            TYPE LIB
            ${_gaudi_install_optional})
    endif()
Whether is it Gaudi.confdb, Gaudi.components or Gaudi.rootmap they are installed alongside the shared libraries. SyntaxHighlightingPlugin: Language cmake is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.
add_dependencies(${merge_target} ${dependency})
    set_property(TARGET ${merge_target} APPEND PROPERTY fragments "${file_to_merge}")
This is how we add new fragments to the ultimate file: by appending to a target property. SyntaxHighlightingPlugin: Language cmake is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.
endfunction()
# special wrapper for genconf and genconfuser so that they always have the same merge_target
function(_merge_files_confdb dependency file_to_merge)
    _merge_files(MergeConfdb "${CMAKE_BINARY_DIR}/${PROJECT_NAME}.confdb"
        ${dependency} "${file_to_merge}"
        "Merging .confdb files")
endfunction()
This function is just here to ensure that .confdb and _user.confdb are merged in the same file using the same target with the same message. SyntaxHighlightingPlugin: Language cmake is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.
# A function to factor out nosetests and pytest lookup
function(_import_runtime runtime)
    if(TARGET ${runtime}) # this function can be called several times
        return()
    endif()
    find_program(${runtime}_PROGRAM ${runtime})
    mark_as_advanced(${runtime}_PROGRAM)
    if(NOT ${runtime}_PROGRAM)
        message(FATAL_ERROR "Unable to find ${runtime}, ${runtime} is required.")
    endif()
    add_executable(${runtime} IMPORTED GLOBAL)
    set_target_properties(${runtime} PROPERTIES IMPORTED_LOCATION ${${runtime}_PROGRAM})
endfunction()


# This functions import nosetests and pytest to be able to use them
# in gaudi_add_tests(nosetests|pytest)
function(_import_nosetests)
    _import_runtime(nosetests)
endfunction()
function(_import_pytest)
    _import_runtime(pytest)
endfunction()
In principle the functions defined in this module are meant to be unsable without any setup beforehand (except for python stuff because we cannot know which python the user wants to use in the project). Here nosetests and pytest will always be the same programs. We create the imported target for gaudi_add_tests(). It is not an implicit dependency because if the configuration does not contain any gaudi_add_tests(nosetests|pytest) it is not called. On the other hand, if gaudi_add_tests(nosetests|pytest) is called then there is an explicit dependency on nosetests or pytest.

Let us explain the private internal magic that happens in this module. SyntaxHighlightingPlugin: Language cmake is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.

# To generate the script run and make it executable
if(NOT CMAKE_SCRIPT_MODE_FILE AND NOT TARGET target_runtime_paths)
If we are in script mode we cannot define targets, thus this block of code is not processed. Some functions may be used in script mode like gaudi_check_python_module() so it make sense to have the entire module usable in script mode. Moreover, we cannot define a target more than once so this block of code is not processed if a target_runtime_paths already exists. This may be used to override the generation of the runtime environment. SyntaxHighlightingPlugin: Language cmake is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.
add_custom_target(target_runtime_paths)
This target does nothing it is only here to hold values with target properties. It is like a hashmap. SyntaxHighlightingPlugin: Language cmake is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.
foreach(env_var IN ITEMS PATH LD_LIBRARY_PATH PYTHONPATH ROOT_INCLUDE_PATH)
        # Clean the paths
        string(REGEX REPLACE "(^:|:$)" "" _ENV_${env_var} "$ENV{${env_var}}")
        string(REGEX REPLACE "::+" ":" _ENV_${env_var} "${_ENV_${env_var}}")
        file(TO_CMAKE_PATH "${_ENV_${env_var}}" _ENV_${env_var})
Here we sanitize the content of PATHs environment variables and turn them into CMake lists to be able to use the generator expression REMOVE_DUPLICATES later on. SyntaxHighlightingPlugin: Language cmake is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.
endforeach()
    string(TOLOWER "${CMAKE_PROJECT_NAME}env.sh" _env_file)
Each project has a different environment script name but anyway the environment is generated only once per configuration. So if we build a full stack the script will be named lhcbfullstackenv.sh and there will not be any gaudienv.sh nor lhcbenv.sh... SyntaxHighlightingPlugin: Language cmake is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.
file(GENERATE OUTPUT ${CMAKE_BINARY_DIR}/${_env_file}
                  CONTENT "#!/bin/sh
# Auto-generated script to set environment variables
export PATH=\"$<SHELL_PATH:$<REMOVE_DUPLICATES:$<GENEX_EVAL:$<TARGET_PROPERTY:target_runtime_paths,runtime_path>>;${_ENV_PATH}>>\"
export LD_LIBRARY_PATH=\"$<SHELL_PATH:$<REMOVE_DUPLICATES:$<GENEX_EVAL:$<TARGET_PROPERTY:target_runtime_paths,runtime_ld_library_path>>;${_ENV_LD_LIBRARY_PATH}>>\"
export PYTHONPATH=\"$<SHELL_PATH:$<REMOVE_DUPLICATES:$<GENEX_EVAL:$<TARGET_PROPERTY:target_runtime_paths,runtime_pythonpath>>;${_ENV_PYTHONPATH}>>\"
$<$<NOT:$<STREQUAL:$ENV{PYTHONHOME},>>:export PYTHONHOME=\"$ENV{PYTHONHOME}\">
export ROOT_INCLUDE_PATH=\"$<SHELL_PATH:$<REMOVE_DUPLICATES:$<GENEX_EVAL:$<TARGET_PROPERTY:target_runtime_paths,runtime_root_include_path>>;${_ENV_ROOT_INCLUDE_PATH}>>\"
export ENV_CMAKE_SOURCE_DIR=\"${CMAKE_SOURCE_DIR}\"
export ENV_CMAKE_BINARY_DIR=\"${CMAKE_BINARY_DIR}\"
export ENV_CMAKE_BUILD_TYPE=\"$<CONFIG>\"
$<IF:$<NOT:$<STREQUAL:${BINARY_TAG},>>,export BINARY_TAG=\"${BINARY_TAG}\",$<$<NOT:$<STREQUAL:$ENV{BINARY_TAG},>>:export BINARY_TAG=\"$ENV{BINARY_TAG}\">>

# Other user defined commands
${RUN_SCRIPT_EXTRA_COMMANDS}
")
This is where the magic happens. Foreach PATH-like variable, we do the same thing. Step by step example with PATH:
  1. ${_ENV_PATH} is the basis of the content of the variable. It either comes from the environment or from the toolchain. It is expanded at configure time when the module is processed (included).
  2. $<TARGET_PROPERTY:target_runtime_paths,runtime_path> contains path to targets built by the current project in the build tree. This property is filled by gaudi_add_*() functions. It enables the syntax ./run prog in the build tree.
  3. $<GENEX_EVAL:$<TARGET_PROPERTY:target_runtime_paths,runtime_path>> is here to tell CMake to expand the generator expression otherwise instead of the content of the property "$<TARGET_PROPERTY:target_runtime_paths,runtime_path>" would be written in the script.
  4. $<GENEX_EVAL:$<TARGET_PROPERTY:target_runtime_paths,runtime_path>>;${_ENV_PATH} we create a CMake list by concatenating the two parts. The one that points to the build tree prepend the other in order the binaries already defined in the environment not to override the newly built ones.
  5. $<REMOVE_DUPLICATES:$<GENEX_EVAL:$<TARGET_PROPERTY:target_runtime_paths,runtime_path>>;${_ENV_PATH}> we removes the duplicates if any
  6. $<SHELL_PATH:$<REMOVE_DUPLICATES:$<GENEX_EVAL:$<TARGET_PROPERTY:target_runtime_paths,runtime_path>>;${_ENV_PATH}>> This turns the CMake list into a shell ":"-separated list.
  7. export PATH=\"$<SHELL_PATH:$<REMOVE_DUPLICATES:$<GENEX_EVAL:$<TARGET_PROPERTY:target_runtime_paths,runtime_path>>;${_ENV_PATH}>>\" We export the new value surrounded by quotes. These quotes also help the parsing of the environment in GaudiConfig.cmake if used from the build tree.
At the end, we have RUN_SCRIPT_EXTRA_COMMANDS which is empty by default. It is here to ease the convertion of the old gaudi_env() function in downstream projects. It may be remove without consequences on Gaudi itself. SyntaxHighlightingPlugin: Language cmake is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.
# Since we cannot tell file(GENERATE) to create an executable file (at generation time)
    # we create one (at configure time) that source the first one, then we make it executable
    file(WRITE ${CMAKE_BINARY_DIR}/run "#!/bin/sh\n# Auto-generated script to set the environment before the execution of a command\nsource ${CMAKE_BINARY_DIR}/${_env_file}\nexec \"$@\"\n")
    execute_process(COMMAND chmod a+x ${CMAKE_BINARY_DIR}/run)
The environment script is sourced by the run script because file(GENERATE) does not have any option to generate executable files. To create the run at configure time, we use "\n" because for some unknown reasons, typing plain newlines made the execute_process() not work as expected. SyntaxHighlightingPlugin: Language cmake is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.
# Add a executable target for convenience
    add_executable(run IMPORTED GLOBAL)
    set_target_properties(run PROPERTIES IMPORTED_LOCATION ${CMAKE_BINARY_DIR}/run)
To be able to use "run" in COMMAND. SyntaxHighlightingPlugin: Language cmake is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.
# Add the path to the merged confdb file to LD_LIBRARY_PATH
    set_property(TARGET target_runtime_paths APPEND
        PROPERTY runtime_ld_library_path $<SHELL_PATH:${CMAKE_BINARY_DIR}>)
    # Path to python at the top level of the build tree where the __init__.py files are
    set_property(TARGET target_runtime_paths APPEND
        PROPERTY runtime_pythonpath $<SHELL_PATH:${CMAKE_BINARY_DIR}/python>)
First thing we add to the properties are the folder that contains Gaudi.components Gaudi.confdb and Gaudi.rootmap to LD_LIBRARY_PATH and the python/ folder that has the special __init__.py files to gather the scattered parts of packages. This folder needs to be the first one in PYTHONPATH. SyntaxHighlightingPlugin: Language cmake is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.
# Prepend the rpath of every target with the folder containing the symlinks to the plugins
    file(MAKE_DIRECTORY ${CMAKE_BINARY_DIR}/.plugins)
    link_directories(BEFORE ${CMAKE_BINARY_DIR}/.plugins)
endif()
Here link_directories() is not used to add a "-L" to the link command but rather to prepend the rpath of the targets. Otherwise, they may find the plugins they look for in the view with their rpath and may use the one from the wrong Gaudi (the one installed in the view). This prevent this behavior.

In the end, let us have a look at the functions that could be standard CMake functions but does not exist yet. SyntaxHighlightingPlugin: Language cmake is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.

# deprecate variables if a target exist
function(__deprecate_var_for_target var access val file)
  if(access STREQUAL "READ_ACCESS" AND
        (NOT file MATCHES ".*Find.+\\.cmake") AND
        (NOT file MATCHES ".*Config\\.cmake") )
    message(DEPRECATION "The variable ${var} is deprecated, use the target instead.")
  endif()
endfunction()
# then call
# variable_watch(var_name __deprecate_var_for_target)
# for each variable that is deprecated
# => use it in every Find*.cmake
This function is meant to be used in find-module-files to entice people to use imported targets instead of Dep_LIBRARY or Dep_INCLUDE_DIR... Check variable_watch() documentation for more information. It prints a deprecation message when a deprecated variable is accessed. SyntaxHighlightingPlugin: Language cmake is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.
# Silence the warning from third-party headers
function(__silence_header_warnings)
    foreach(target IN LISTS ARGN)
        get_target_property(inc_dir ${target} INTERFACE_INCLUDE_DIRECTORIES)
        set_target_properties(${target} PROPERTIES
            INTERFACE_SYSTEM_INCLUDE_DIRECTORIES "${inc_dir}")
    endforeach()
endfunction()
# then call
# __silence_header_warnings(targets...)
With this function we declare that include directories of a target are also SYSTEM include directories. Since "-isystem" wins over "-I" the headers of these targets are treated as system headers and do not display warnings. SyntaxHighlightingPlugin: Language cmake is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.
# Reset the found and not found packages for FeatureSummary
# call this function after calling feature_summary(...)
function(__reset_feature_summary)
    set_property(GLOBAL PROPERTY PACKAGES_FOUND "")
    set_property(GLOBAL PROPERTY PACKAGES_NOT_FOUND "")
endfunction()
# strangely this function is not defined in the standard
# CMake module FeatureSummary
The FeatureSummary built-in CMake module is awesome but it lacks this function. Resetting the lookup summary is mandatory otherwise downstream project will seen Gaudi's dependencies in their dependency summary which is not good. (Same behavior form an installed Gaudi or a build tree of Gaudi or a full stack (in this last case required dependencies change)).

Continuous integration

SyntaxHighlightingPlugin: Language yaml is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.
image: gitlab-registry.cern.ch/lhcb-core/lbdocker/centos7-build

stages:
  - build
  - test

variables:
  CMAKE_GENERATOR: 'Ninja' # default build system
This environment variable will have this value in each job. SyntaxHighlightingPlugin: Language yaml is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.
# All the configuration
before_script:
  # FIXME: When cmake 3.15 is installed on cvmfs use it
  - export PATH="$PWD/cmakelocal/bin:$PATH"
  - test -f cmakelocal/bin/cmake || mkdir -p cmakelocal && wget -qO- "https://github.com/Kitware/CMake/releases/download/v3.15.0/cmake-3.15.0-Linux-x86_64.tar.gz" | tar --strip-components=1 -xz -C cmakelocal
  - export CMAKE_PREFIX_PATH="/cvmfs/lhcb.cern.ch/lib/contrib/ninja/1.8.2.g81279.kitware.dyndep-1.jobserver-1/Linux-x86_64:$CMAKE_PREFIX_PATH" # ninja build
  - export CCACHE_DIR=$PWD/.ccache
Currently CMake 3.15 is installed on CVMFS in /cvmfs/lhcb.cern.ch but not yet in /cvmfs/sft.cern.ch that is why this FIXME is still here. We use the runner's cache not to have to redownload cmake in each job. SyntaxHighlightingPlugin: Language yaml is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.
# cache cmake not to re-download it for each job
cache:
  paths:
    - cmakelocal

# Job templates
.ccache: &template_ccache
  cache:
    key: "$CI_JOB_NAME-$CI_COMMIT_REF_SLUG"
    paths:
    - .ccache
    - cmakelocal # overridden otherwise
This is to keep the ccache cache between pipelines. SyntaxHighlightingPlugin: Language yaml is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.
### Build
build.x86_64-centos7-gcc8-opt:
  stage: build
  script:
    - cmake -S . -B build.x86_64-centos7-gcc8-opt -D CMAKE_TOOLCHAIN_FILE=cmake/toolchains/x86_64-centos7-gcc8-opt.cmake
    - cmake --build build.x86_64-centos7-gcc8-opt
  artifacts:
    paths:
      - build.x86_64-centos7-gcc8-opt/ # pass the result of the build to the test stage
    expire_in: 1 day
  <<: *template_ccache

build.x86_64-centos7-gcc8-dbg:
  stage: build
  script:
    - cmake -S . -B build.x86_64-centos7-gcc8-dbg -D CMAKE_TOOLCHAIN_FILE=cmake/toolchains/x86_64-centos7-gcc8-dbg.cmake
    - cmake --build build.x86_64-centos7-gcc8-dbg
  artifacts:
    paths:
      - build.x86_64-centos7-gcc8-dbg/ # pass the result of the build to the test stage
    expire_in: 1 day
  <<: *template_ccache
The build jobs use toolchain files to be in the same environment each time. We would anyway need to set the environment for each job for each pipeline. SyntaxHighlightingPlugin: Language yaml is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.
### Test build
compile_make:
  stage: test
  script:
    - cmake -S . -B build.makefile -G "Unix Makefiles" -D CMAKE_TOOLCHAIN_FILE=cmake/toolchains/x86_64-centos7-gcc8-opt.cmake
    - cmake --build build.makefile -j `nproc`
  <<: *template_ccache

compile_view:
  stage: test
  script:
    - source /cvmfs/sft.cern.ch/lcg/views/LCG_96/x86_64-centos7-gcc8-opt/setup.sh
    - export CMAKE_PREFIX_PATH="$CMAKE_PREFIX_PATH:/cvmfs/sft.cern.ch/lcg/releases/LCG_95/vectorclass/1.30/x86_64-centos7-gcc8-opt"
    - export PATH="$PWD/cmakelocal/bin:$PATH"
    - cmake -S . -B build.view -D CMAKE_USE_CCACHE=ON
    - cmake --build build.view
  <<: *template_ccache
These tests are just here to make sure we are still able to build with GNU make and to build in a view. SyntaxHighlightingPlugin: Language yaml is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.
### Test
test.x86_64-centos7-gcc8-opt:
  stage: test
  script:
    - find build.x86_64-centos7-gcc8-opt -type f -exec touch -d $(date +@%s) \{} \; # not to re-run cmake
    - cd build.x86_64-centos7-gcc8-opt
    - ctest -T test --schedule-random
  artifacts:
    paths:
      - build.x86_64-centos7-gcc8-opt/Testing/*/Test.xml
    when: on_failure
    expire_in: 1 week

test.x86_64-centos7-gcc8-dbg:
  stage: test
  script:
    - find build.x86_64-centos7-gcc8-dbg -type f -exec touch -d $(date +@%s) \{} \; # not to re-run cmake
    - cd build.x86_64-centos7-gcc8-dbg
    - ctest -T test --schedule-random
  artifacts:
    paths:
      - build.x86_64-centos7-gcc8-dbg/Testing/*/Test.xml
    when: on_failure
    expire_in: 1 week
These jobs run every test defined in Gaudi the find is here to make sure the targets will not be recompiled. They produce XML reports that can be downloaded.

WARNING: Gaudi contains async tests with reference files that may fail. SyntaxHighlightingPlugin: Language yaml is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.

### Package (almost deploy)
package:
  stage: test
  script:
    - find build.x86_64-centos7-gcc8-opt -type f -exec touch -d $(date +@%s) \{} \; # not to re-run cmake
    - cmake --build build.x86_64-centos7-gcc8-opt -t package
  artifacts:
    paths:
      - build.x86_64-centos7-gcc8-opt/*.zip
      - build.x86_64-centos7-gcc8-opt/*.rpm
    expire_in: 1 week
This is pointless but it is nice to have a usable output at the end of the pipeline (because it is the purpose of Continuous integration).

WARNING: Sometimes the pipeline fail because the compiler takes to much resources and the runner kill it. Thus, the build system reports an error. An error occured so the job failed so did the pipeline.

"How to"s

How to maintain the dependency lookup?

The dependency lookup is defined in GaudiDependencies.cmake. Nerver forget that this file is used during Gaudi's configuration and during Gaudi's importation in downstream projects.

If a new required dependency (Dep) is added it must be added to the list of dependencies in the foreach(dep) loop that looks for them. It is better to define the minimal required version but do this only if a DepConfigVersion.cmake file exists or if the FindDep.cmake retrieves the version. To do this set(_gaudi_Dep_MIN_VERSION 1.2.3) If the dependency does not provide a DepConfig.cmake file, you must write a FindDep.cmake file and put it under cmake/modules/ alongside the other find-module files or on CVMFS if they have been moved.

If an old required dependency is removed (no longer used anywhere, no variable defined by it is used, no target defined by it are used), remove the name of the dependency from the list of required dependencies in the foreach(dep), remove its set(_gaudi_Dep_MIN_VERSION) if any and remove its find-module file from cmake/modules/ if has not been moved on CVMFS yet. If it is on CVMFS, other projects may still need this find-module file.

If a new third optional dependency is added do the same things as for required dependencies but to the second foreach(dep) loop. Furthermore, you need to add the option to enable/disable the dependency. To do so, add it to the foreach(optional_dependency) in the top level CMakeLists.txt of the project.

To remove an optional dependency, remove it option in the top level CMakeLists.txt of the project and do the same things as for required dependencies.

What to do when a third-party dependency provides a config-file?

Sometimes, the update does not come from the project but from the dependencies. Remember the following rule "Once a config-file is provided by the editor of the dependency, drop the support of your find-module and use the official config-file."

First, check that your project is able to use the official config-file. You may need to update the imported targets your targets link against. Add CONFIG to the call to find_package() that looks for this dependency. Be careful if you want to use CMAKE_FIND_PACKAGE_PREFER_CONFIG because the day you update the binaries of your dependencies and a config-file is provided without you knowing about it, you will probably have trouble figuring out why CMake says that the imported target of your dependency does not exist.

How to update a find-module file?

We are going to show this with the example of CppUnit.

Initial find-module file: SyntaxHighlightingPlugin: Language cmake is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.

# - Locate CppUnit library
# Defines:
#
#  CPPUNIT_FOUND
#  CPPUNIT_INCLUDE_DIR
#  CPPUNIT_INCLUDE_DIRS (not cached)
#  CPPUNIT_LIBRARY
#  CPPUNIT_LIBRARIES (not cached)

find_path(CPPUNIT_INCLUDE_DIR cppunit/Test.h)
find_library(CPPUNIT_LIBRARY NAMES cppunit)

set(CPPUNIT_INCLUDE_DIRS ${CPPUNIT_INCLUDE_DIR})

# handle the QUIETLY and REQUIRED arguments and set CPPUNIT_FOUND to TRUE if
# all listed variables are TRUE
INCLUDE(FindPackageHandleStandardArgs)
FIND_PACKAGE_HANDLE_STANDARD_ARGS(CppUnit DEFAULT_MSG CPPUNIT_INCLUDE_DIR CPPUNIT_LIBRARY)

mark_as_advanced(CPPUNIT_FOUND CPPUNIT_INCLUDE_DIR CPPUNIT_LIBRARY)

set(CPPUNIT_LIBRARIES ${CPPUNIT_LIBRARY} ${CMAKE_DL_LIBS})

To update it and take advantage of imported targets (and transitivity of dependencies), there is no need to deeply understand this file. We will mainly use the documentation at the beginning. Anyway, even if you are proud of your find-module file, remember that it needs to work until a config-file comes out not until the end of the world.

We want to define an imported target that will spare us the hassle to link against a shared object and to add external include directories to our targets. Since the documentation is very useful, the first thing to do is to update it by appending a block describing the imported target. SyntaxHighlightingPlugin: Language cmake is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.

# Imports:
#
#  CppUnit::cppunit
#
# Usage of the target instead of the variables is advised

Then (I am still wondering if it is such a good idea) add a block to silence the lookup if it cannot change. Use any cached variable. SyntaxHighlightingPlugin: Language cmake is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.

# Find quietly if already found before
if(DEFINED CACHE{CPPUNIT_INCLUDE_DIR})
  set(${CMAKE_FIND_PACKAGE_NAME}_FIND_QUIETLY YES)
endif()

Finally, jump to the end of the file and add the imported target. Make sure the imported target is not yet defined. Create an IMPORTED INTERFACE target and use target_include_directories(), target_link_libraries(), target_compile_definitions(), target_compile_options()... as you would have done without the imported target onto your targets. Be nice to the user and display that an imported target exists (if not QUIET). In Gaudi the imported target is named PackageName::packagename unless there is a better name. SyntaxHighlightingPlugin: Language cmake is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.

# Modernisation: create an interface target to link against
if(TARGET CppUnit::cppunit)
    return()
endif()
if(CPPUNIT_FOUND)
  add_library(CppUnit::cppunit IMPORTED INTERFACE)
  target_include_directories(CppUnit::cppunit SYSTEM INTERFACE "${CPPUNIT_INCLUDE_DIRS}")
  target_link_libraries(CppUnit::cppunit INTERFACE "${CPPUNIT_LIBRARIES}")
  # Display the imported target for the user to know
  if(NOT ${CMAKE_FIND_PACKAGE_NAME}_FIND_QUIETLY)
    message(STATUS "  Import target: CppUnit::cppunit")
  endif()
endif()

To polish your file, now that you have an imported target, you may deprecate the variables with the private function of GaudiToolbox. SyntaxHighlightingPlugin: Language cmake is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.

if(COMMAND __deprecate_var_for_target)
  foreach(v IN ITEMS CPPUNIT_INCLUDE_DIR CPPUNIT_INCLUDE_DIRS CPPUNIT_LIBRARY CPPUNIT_LIBRARIES)
    variable_watch(${v} __deprecate_var_for_target)
  endforeach()
endif()

The final find-module looks like this: SyntaxHighlightingPlugin: Language cmake is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.

# - Locate CppUnit library
# Defines:
#
#  CPPUNIT_FOUND
#  CPPUNIT_INCLUDE_DIR
#  CPPUNIT_INCLUDE_DIRS (not cached)
#  CPPUNIT_LIBRARY
#  CPPUNIT_LIBRARIES (not cached)
#
# Imports:
#
#  CppUnit::cppunit
#
# Usage of the target instead of the variables is advised

# Find quietly if already found before
if(DEFINED CACHE{CPPUNIT_INCLUDE_DIR})
  set(${CMAKE_FIND_PACKAGE_NAME}_FIND_QUIETLY YES)
endif()

find_path(CPPUNIT_INCLUDE_DIR cppunit/Test.h)
find_library(CPPUNIT_LIBRARY NAMES cppunit)

set(CPPUNIT_INCLUDE_DIRS ${CPPUNIT_INCLUDE_DIR})

# handle the QUIETLY and REQUIRED arguments and set CPPUNIT_FOUND to TRUE if
# all listed variables are TRUE
INCLUDE(FindPackageHandleStandardArgs)
FIND_PACKAGE_HANDLE_STANDARD_ARGS(CppUnit DEFAULT_MSG CPPUNIT_INCLUDE_DIR CPPUNIT_LIBRARY)

mark_as_advanced(CPPUNIT_FOUND CPPUNIT_INCLUDE_DIR CPPUNIT_LIBRARY)

set(CPPUNIT_LIBRARIES ${CPPUNIT_LIBRARY} ${CMAKE_DL_LIBS})

# Modernisation: create an interface target to link against
if(TARGET CppUnit::cppunit)
    return()
endif()
if(CPPUNIT_FOUND)
  add_library(CppUnit::cppunit IMPORTED INTERFACE)
  target_include_directories(CppUnit::cppunit SYSTEM INTERFACE "${CPPUNIT_INCLUDE_DIRS}")
  target_link_libraries(CppUnit::cppunit INTERFACE "${CPPUNIT_LIBRARIES}")
  # Display the imported target for the user to know
  if(NOT ${CMAKE_FIND_PACKAGE_NAME}_FIND_QUIETLY)
    message(STATUS "  Import target: CppUnit::cppunit")
  endif()
endif()

if(COMMAND __deprecate_var_for_target)
  foreach(v IN ITEMS CPPUNIT_INCLUDE_DIR CPPUNIT_INCLUDE_DIRS CPPUNIT_LIBRARY CPPUNIT_LIBRARIES)
    variable_watch(${v} __deprecate_var_for_target)
  endforeach()
endif()

If the dependencies contains several libraries, you just need to create several imported targets. See Findgperftools.cmake to have an example.

One may think it is not a good idea to define an imported target in a find-module because the day a config-file is provided it may use different names for its imported targets. This is true but I think it is easier to do a find-and-replace onto the imported target name to update it everywhere than having to remove every target_include_directories(), target_*() or include_directories() or link_libraries() of a project.

How to modernize a sub-project?

All the functions that do no longer exist needs either to be removed or changed to perform the same action with modern CMake following good practices. All the functions that were modernize and still exist but with a different signature need to be updated to the new signature.

The main modifications due to the modernization are:

  • no topological sort at the beginning of the configuration, thus we removed gaudi_subdir() and gaudi_depend_on_subdir()
  • no gaudi_project(), we use project()
  • centralized dependency lookup
  • explicit optional dependencies, we use if(GAUDI_USE_CLHEP) instead of if(CLHEP_FOUND)
  • CMake is not meant be set the test environment, we no longer use gaudi_env() we use VAR=value in .qmt files.

What if a target defined upstream is overridden by a downstream project?

I have never seen this yet but I was told it may happened.

Change: SyntaxHighlightingPlugin: Language cmake is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.

gaudi_add_library(MyTarget ...)
To: SyntaxHighlightingPlugin: Language cmake is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.
set(_prefix)
if(TARGET MyTarget) # if we build the full stack and the target already exists elsewhere
  set(_prefix ${PROJECT_NAME}::)
endif()
gaudi_add_library(${_prefix}MyTarget ...)
set_target_properties(${_prefix}MyTarget PROPERTIES OUTPUT_NAME MyTarget) # to keep the same name (e.g. libMyTarget.so)

Example: LHCb/GaudiGSL

We are going to modernize GaudiGSL which is a sub-project of LHCb step by step.

Initial CMakeLists.txt: SyntaxHighlightingPlugin: Language cmake is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.

gaudi_subdir(GaudiGSL)

gaudi_depends_on_subdirs(GaudiAlg GaudiUtils)

find_package(CLHEP)
find_package(GSL)
find_package(ROOT REQUIRED)
find_package(Boost REQUIRED)

# If any of the non-essential externals is missing, give up on building
# these targets.
if( NOT CLHEP_FOUND OR NOT GSL_FOUND )
   message( STATUS "Requirements for GaudiGSL not found. "
      "Not building package" )
   return()
endif()

# Hide some CLHEP/ROOT compile time warnings
include_directories(SYSTEM ${CLHEP_INCLUDE_DIRS} ${ROOT_INCLUDE_DIRS} ${Boost_INCLUDE_DIRS})

#---Libraries---------------------------------------------------------------
gaudi_add_library(GaudiGSLLib src/Lib/*.cpp
                  LINK_LIBRARIES GaudiAlgLib GSL CLHEP
                  INCLUDE_DIRS GSL CLHEP
                  PUBLIC_HEADERS GaudiGSL GaudiMath)
gaudi_add_module(GaudiGSL src/Components/*.cpp
                 LINK_LIBRARIES GaudiGSLLib)

gaudi_add_module(GaudiGSLExamples examples/src/*.cpp
                 LINK_LIBRARIES GaudiGSLLib)

#---Dictionaries------------------------------------------------------------
gaudi_add_dictionary(GaudiGSLMath dict/GaudiGSLMath.h dict/GaudiGSLMath.xml LINK_LIBRARIES GaudiGSLLib)

#---Executables-------------------------------------------------------------
macro(add_gsl_unit_test name)
  gaudi_add_unit_test(${name} src/Tests/${name}.cpp LINK_LIBRARIES GaudiGSLLib GaudiUtilsLib)
endmacro()

foreach(test IntegralInTest DerivativeTest 2DoubleFuncTest GSLAdaptersTest
             PFuncTest ExceptionsTest SimpleFuncTest 3DoubleFuncTest InterpTest
             Integral1Test)
  add_gsl_unit_test(${test})
endforeach()

Let us go! SyntaxHighlightingPlugin: Language cmake is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.

gaudi_subdir(GaudiGSL)

gaudi_depends_on_subdirs(GaudiAlg GaudiUtils)
These lines are now useless. We can remove them

SyntaxHighlightingPlugin: Language cmake is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.

find_package(CLHEP)
find_package(GSL)
find_package(ROOT REQUIRED)
find_package(Boost REQUIRED)

# If any of the non-essential externals is missing, give up on building
# these targets.
if( NOT CLHEP_FOUND OR NOT GSL_FOUND )
   message( STATUS "Requirements for GaudiGSL not found. "
      "Not building package" )
   return()
endif()
These third party dependencies (CLHEP, GSL, ROOT, Boost) must be moved to LHCbDependencies.cmake using FeatureSummary as in GaudiDependencies.cmake. We then see that GSL and CLHEP should be required because you cannot build without them.

So LHCbDependencies.cmake looks more and less like this: SyntaxHighlightingPlugin: Language cmake is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.

# Lookup of the dependencies of LHCb

if(LHCB_DEPENDENCIES_FIND_QUIETLY)
    set(__quiet QUIET)
endif()

include(FeatureSummary)
set(FeatureSummary_REQUIRED_PKG_TYPES RECOMMENDED REQUIRED)

### Required
# Gaudi
if(NOT TARGET Gaudi::GaudiPluginService) # if not full stack project
  find_package(Gaudi 32.0 ${__quiet})
  set_package_properties(Gaudi PROPERTIES TYPE REQUIRED)
endif()

# Boost
set(Boost_USE_STATIC_LIBS OFF)
set(OLD_BUILD_SHARED_LIBS ${BUILD_SHARED_LIBS}) # FIXME: the day BoostConfig.cmake handles Boost_USE_STATIC_LIBS correctly
set(BUILD_SHARED_LIBS ON)
find_package(Boost 1.70 ${__quiet} CONFIG)
set_package_properties(Boost PROPERTIES TYPE REQUIRED)
set(BUILD_SHARED_LIBS ${OLD_BUILD_SHARED_LIBS})

# ROOT
find_package(ROOT 6.18 ${__quiet} CONFIG)
set_package_properties(ROOT PROPERTIES TYPE REQUIRED)
# FIXME: the day ROOTConfig.cmake displays at least that it was found remove these lines
if(NOT LHCB_DEPENDENCIES_FIND_QUIETLY)
  message(STATUS "Found ROOT: ${ROOT_DIR} (found version ${ROOT_VERSION})")
endif()

# Others required dependencies
foreach(dep IN ITEMS CLHEP GSL)
  find_package(${dep} ${_lhcb_${dep}_MIN_VERSION} ${__quiet})
  set_package_properties(${dep} PROPERTIES TYPE REQUIRED)
endforeach()

# Print a summary of the lookup
feature_summary(FATAL_ON_MISSING_REQUIRED_PACKAGES
  DESCRIPTION "Missing LHCb's required dependencies:"
  QUIET_ON_EMPTY
  WHAT REQUIRED_PACKAGES_NOT_FOUND)
feature_summary(DESCRIPTION "LHCb's required optional dependencies found:"
  QUIET_ON_EMPTY
  WHAT RECOMMENDED_PACKAGES_FOUND)
feature_summary(FATAL_ON_MISSING_REQUIRED_PACKAGES
  DESCRIPTION "Missing LHCb's required optional dependencies:"
  QUIET_ON_EMPTY
  WHAT RECOMMENDED_PACKAGES_NOT_FOUND)
# Reset this summary not to mess with downstream projects look up
if(COMMAND __reset_feature_summary)
  __reset_feature_summary()
endif()

# Silence warnings from dependencies
if(COMMAND __silence_header_warnings)
  __silence_header_warnings(Boost::headers ROOT::Core GSL::gsl)
endif()

Then we stumble across: SyntaxHighlightingPlugin: Language cmake is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.

# Hide some CLHEP/ROOT compile time warnings
include_directories(SYSTEM ${CLHEP_INCLUDE_DIRS} ${ROOT_INCLUDE_DIRS} ${Boost_INCLUDE_DIRS})
It is a very bad practice. It is already handled in LHCbDependencies.cmake. So we drop these lines.

Then we have the part where the binaries are declared. SyntaxHighlightingPlugin: Language cmake is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.

#---Libraries---------------------------------------------------------------
gaudi_add_library(GaudiGSLLib src/Lib/*.cpp
                  LINK_LIBRARIES GaudiAlgLib GSL CLHEP
                  INCLUDE_DIRS GSL CLHEP
                  PUBLIC_HEADERS GaudiGSL GaudiMath)
gaudi_add_module(GaudiGSL src/Components/*.cpp
                 LINK_LIBRARIES GaudiGSLLib)

gaudi_add_module(GaudiGSLExamples examples/src/*.cpp
                 LINK_LIBRARIES GaudiGSLLib)

#---Dictionaries------------------------------------------------------------
gaudi_add_dictionary(GaudiGSLMath dict/GaudiGSLMath.h dict/GaudiGSLMath.xml LINK_LIBRARIES GaudiGSLLib)
These function still exist but do not have the same signature. We will replace glob patterns with the explicit list of sources. We will use targets instead of LINK_LIBRARIES and INCLUDE_DIRECTORIES. Finally we will move the folders GaudiGSL/ and GaudiMath/ into include/.

Run:

cd GaudiGSL # inside LHCb
mkdir include
mv GaudiGSL/ GaudiMath/ include/

Then write: SyntaxHighlightingPlugin: Language cmake is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.

# Library
gaudi_add_library(GaudiGSLLib
                  SOURCES
                    src/Lib/Adapter.cpp
                    src/Lib/Adapters.cpp
                    src/Lib/Constant.cpp
                    src/Lib/GaudiGSL.cpp
                    src/Lib/GslErrorHandlers.cpp
                    src/Lib/GSLFunAdapters.cpp
                    src/Lib/Helpers.cpp
                    src/Lib/Integral.cpp
                    src/Lib/Interpolation.cpp
                    src/Lib/NumericalDefiniteIntegral.cpp
                    src/Lib/NumericalDerivative.cpp
                    src/Lib/NumericalIndefiniteIntegral.cpp
                    src/Lib/Splines.cpp
                  LINK
                    PUBLIC
                      Gaudi::GaudiAlgLib
                      GSL::gsl
                      CLHEP::CLHEP)

# Plugin
gaudi_add_module(GaudiGSL
                 SOURCES
                   src/Components/EqSolver.cpp
                   src/Components/GslErrorCount.cpp
                   src/Components/GslErrorPrint.cpp
                   src/Components/FuncMinimum.cpp
                   src/Components/GslErrorException.cpp
                   src/Components/GslSvc.cpp
                 LINK GaudiGSLLib)

gaudi_add_module(GaudiGSLExamples
                 SOURCES
                   examples/src/EqSolverGenAlg.cpp
                   examples/src/EqSolverPAlg.cpp
                   examples/src/FuncMinimumIAlg.cpp
                   examples/src/EqSolverIAlg.cpp
                   examples/src/FuncMinimumGenAlg.cpp
                   examples/src/FuncMinimumPAlg.cpp
                 LINK GaudiGSLLib)

# Dictionary
gaudi_add_dictionary(GaudiGSLMathDict
                     HEADERFILES dict/GaudiGSLMath.h 
                     SELECTION dict/GaudiGSLMath.xml
                     LINK GaudiGSLLib)

In the end, we notice a bunch of test that need to be compiled (not python tests). SyntaxHighlightingPlugin: Language cmake is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.

#---Executables-------------------------------------------------------------
macro(add_gsl_unit_test name)
  gaudi_add_unit_test(${name} src/Tests/${name}.cpp LINK_LIBRARIES GaudiGSLLib GaudiUtilsLib)
endmacro()

foreach(test IntegralInTest DerivativeTest 2DoubleFuncTest GSLAdaptersTest
             PFuncTest ExceptionsTest SimpleFuncTest 3DoubleFuncTest InterpTest
             Integral1Test)
  add_gsl_unit_test(${test})
endforeach()
Defining a macro here is useless, we can just use the iterator of the loop. gaudi_add_unit_test() was removed, now we use gaudi_add_executable(... TEST). In modern CMake, foreach() should specify IN ITEMS or IN LISTS.

SyntaxHighlightingPlugin: Language cmake is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.

foreach(test IN ITEMS IntegralInTest DerivativeTest 2DoubleFuncTest GSLAdaptersTest
                      PFuncTest ExceptionsTest SimpleFuncTest 3DoubleFuncTest InterpTest
                      Integral1Test)
  gaudi_add_executable(${test}
                       SOURCES src/Tests/${test}.cpp
                       LINK GaudiGSLLib
                            Gaudi::GaudiUtilsLib
                       TEST)
endforeach()

Final CMakeLists.txt: SyntaxHighlightingPlugin: Language cmake is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.

# Library
gaudi_add_library(GaudiGSLLib
                  SOURCES
                    src/Lib/Adapter.cpp
                    src/Lib/Adapters.cpp
                    src/Lib/Constant.cpp
                    src/Lib/GaudiGSL.cpp
                    src/Lib/GslErrorHandlers.cpp
                    src/Lib/GSLFunAdapters.cpp
                    src/Lib/Helpers.cpp
                    src/Lib/Integral.cpp
                    src/Lib/Interpolation.cpp
                    src/Lib/NumericalDefiniteIntegral.cpp
                    src/Lib/NumericalDerivative.cpp
                    src/Lib/NumericalIndefiniteIntegral.cpp
                    src/Lib/Splines.cpp
                  LINK
                    PUBLIC
                      Gaudi::GaudiAlgLib
                      GSL::gsl
                      CLHEP::CLHEP)

# Plugin
gaudi_add_module(GaudiGSL
                 SOURCES
                   src/Components/EqSolver.cpp
                   src/Components/GslErrorCount.cpp
                   src/Components/GslErrorPrint.cpp
                   src/Components/FuncMinimum.cpp
                   src/Components/GslErrorException.cpp
                   src/Components/GslSvc.cpp
                 LINK GaudiGSLLib)

gaudi_add_module(GaudiGSLExamples
                 SOURCES
                   examples/src/EqSolverGenAlg.cpp
                   examples/src/EqSolverPAlg.cpp
                   examples/src/FuncMinimumIAlg.cpp
                   examples/src/EqSolverIAlg.cpp
                   examples/src/FuncMinimumGenAlg.cpp
                   examples/src/FuncMinimumPAlg.cpp
                 LINK GaudiGSLLib)

# Dictionary
gaudi_add_dictionary(GaudiGSLMathDict
                     HEADERFILES dict/GaudiGSLMath.h 
                     SELECTION dict/GaudiGSLMath.xml
                     LINK GaudiGSLLib)

# Tests
foreach(test IN ITEMS IntegralInTest DerivativeTest 2DoubleFuncTest GSLAdaptersTest
                      PFuncTest ExceptionsTest SimpleFuncTest 3DoubleFuncTest InterpTest
                      Integral1Test)
  gaudi_add_executable(${test}
                       SOURCES src/Tests/${test}.cpp
                       LINK GaudiGSLLib
                            Gaudi::GaudiUtilsLib
                       TEST)
endforeach()

How to install Gaudi even if it was built with its build tree as install area or in a full stack?

If Gaudi was build with GAUDI_BUILD_TREE_AS_INSTALL_AREA=ON, GaudiConfig.cmake is not relocatable. If Gaudi was build as part of a stack project, GaudiConfig.cmake does not exist. However the binaries do not need to be recompiled to be installed. Thus, if you used GAUDI_BUILD_TREE_AS_INSTALL_AREA=ON, you just need to reconfigure Gaudi with GAUDI_BUILD_TREE_AS_INSTALL_AREA=OFF, the build system should not have any target to rebuild. If Gaudi was build as part of a stack, configure Gaudi alone with the build tree of the stack as Gaudi's build tree: cmake -S Gaudi -B build.$BINARY_TAG/Gaudi and cmake --install build.$BINARY_TAG/Gaudi.

How to override the generation of the runtime environment?

The runtime environment is generated by GaudiToolbox only if no target called target_runtime_paths exist when it is included. So to override the generation of the build environment, you need to add_custom_target(target_runtime_paths) before include(GaudiToolbox) or find_package(Gaudi), then the block of code that generate the run script will not be processed. You will be free to define whatever you want to generate the runtime environment. Moreover, the gaudi_add_*() will continue to append paths to target_runtime_paths target properties that you will be able to use to generate the runtime environment.

If you just need to add stuff in the runtime environment do not bother with overridding it completely. Use set(RUN_SCRIPT_EXTRA_COMMANDS "${RUN_SCRIPT_EXTRA_COMMANDS}\nexport VAR=value\nexport SOME_PATH=/path/to/something:$SOME_PATH").

What if the support for IDEs is dropped or IDE's CMake generator support glob patterns?

We may drop the explicit list of sources and enable the use of glob pattern forwarded to the build system with file(GLOB|GLOB_RECURSE ... CONFIGURE_DEPENDS). This way no one would neither need to add a source file to the list of sources each time a new one is added nor touch CMakeLists.txt to trigger the reconfiguration.

Appendix

CMake memento

SyntaxHighlightingPlugin: Language md is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.

# CMake

**CMake** is a build management software supported by Kitware.
[Official webpage](https://cmake.org/) and
[Official documetation](https://cmake.org/cmake/help/latest/)

It aims at generating compilation scripts like `makefile` or `build.ninja` or
`project.sln`...
CMake does not compile anything alone, it needs a build system (like `make`)
and a compiler (like `gcc`) to build a software. Moreover, CMake is not a
dependency manager (like `maven`).

CMake is bundled with other tools, namely, **CTest**
to run unit tests and **CPack** to package software.


## Concepts

- *CMakeLists.txt*: is the file CMake looks for when launched. It contains all
  the instructions CMake must follow to generate compilation scripts.
- **generator**: a generator is the build system cmake produce files for (e.g.
  make, ninja, Visual Studio, Xcode...).
  [Official list](https://cmake.org/cmake/help/latest/manual/cmake-generators.7.html)
- **target**: the result of a compilation: executable or library. Each target has its on properties.
  Analogy with OOP: target=object, add_library/executable=constructor,
  properties=attributes, (get/set_)target_*=methods
  It is possible to set the default value of every property by setting `CMAKE_property_name`
- Visibility:
  - **PUBLIC**: public properties are needed to compile the target and targets which depend on it.
  - **PRIVATE**: private properties are only needed to compile the target
  - **INTERFACE**: interface properties are not needed to compile the target but targets which
  depend on it will need them
  - declaring something PRIVATE will populate a property of the target, declaring something
  INTERFACE will populate a INTERFACE_property of the target, declaring something PUBLIC will
  populate both.
- Steps of compilation with CMake:
  1. The cache is loaded.
  1. If there is a toolchain it is processed.
  1. **Configure time**: when CMake process "CMakeLists.txt".
  1. **Generation time**: when generator expressions are expanded and files (build files
  and files created with `configure_file()` or `file(GENERATE)`) are generated.
  1. **Build time**: when the build system (make, ninja...) is called and the compiler is run.
  1. **Install time** (optional): when the target _install_ (or _package_) is called (e.g. make install)
- The **toolchain** is the set of tools used to build a project (compilers, ...).
- CMake will assume that a directory is a **source tree** (directory that contains
  the sources) if it contains a *CMakeLists.txt* and will assume that a directory is a
  **build tree** (directory that contains the products of the build)
  if it contains a *CMakeCache.txt*. That is why it is recommended
  to do an **out-of-source build** (source tree != build tree).


## Syntax

### Basics

- **comments**: `# I am a single line comment`
  ```cmake
  #[===[
	  I am a multi line comment
  #]===]
  ```
- **variables**: are all _string_ typed, but can also be lists
  - `set(var_name a_value)` to set a value
  - `${var_name}` to use the value
  - if the value may contain spaces, the variable be quoted: `"${var}"`
  - even this works: `set(${var_name}_abc 1)` ==> ${a_value_abc} is 1
  - About scopes :
    > When a variable that is already set is set again in a 
    > subdirectory it overrides the value in that scope and any
    > deeper subdirectories but not in the parent scope.
    > Loop and control flow blocks do not have their own scopes.
    `set(var value PARENT_SCOPE)` will set a variable into the parent scope but not
    the current one.
  - **Cached variables**: can be defined from the command line (`cmake -DVAR=val`)
    and are stored in a text file (CMakeCache.txt) so that CMake can
    remember them between executions. They are visible in all scopes.
    ```cmake
    set(MY_CACHED_VARIABLE "DEFAULT_VALUE" CACHE STRING "Description")
    option(MY_CACHED_BOOLEAN_VARIABLE "something that can be enabled" OFF) # shortcut for BOOL
    ```
    Default values are used when the variable if not defined by the command line.
    `set(... FORCE)` will always set the value to its default unless it is defined
    by the command line. It is also the only way to override CMake variables default values.
  - **Environnement variables**: `$ENV{HOME}` to get the value
    and `set(ENV{HOME} value)` to modify the value only in
    CMake (the environment outside CMake is not modified).
  - **Properties**: are variables attached to a target (or test, directory...)
    `set_target_properties(TargetName PROPERTIES PROP_NAME value)`,
    `set_property(TARGET target1 target2 PROPERTY PROP_NAME value)`,
    `get_property(result_var TARGET target1 PROPERTY PROP_NAME)`
  - **Lists**: `set(my_list 1 2 3 4)` is the same as `set(my_list "1;2;3;4")`
    but is different as `set(str "1 2 3 4")` which is a string.
    List of lists are possible.
    - _Tip_: use `separate_arguments(var)` to turn a string with spaces
      into a list.
- **Print on output**: `message([mode] "hello world!")` where `mode` is
  - FATAL_ERROR, SEND_ERROR (displayed on stderr, prevent the generation of build files)
  - WARNING, AUTHOR_WARNING, DEPRECATION, NOTICE (displayed on stderr)
  - STATUS, VERBOSE, DEBUG or TRACE (displayed on stdout)
  - **NB**: `set(CMAKE_ERROR_DEPRECATED TRUE)` to treat deprecation messages as errors.
- **if** blocks:
  ```cmake
  if(<condition>)
    # something
  elseif(<condition>)
    # something
  else()
    # something
  endif()
  ```
  list of operators: [https://cmake.org/cmake/help/latest/command/if.html](https://cmake.org/cmake/help/latest/command/if.html)
- **foreach loops**:
  ```cmake
  foreach(var IN ITEMS 1 2 3)
    message(${var})
  endforeach()

  foreach(var IN LISTS my_list ITEMS 5 6 final_value)
    message(${var}) # 7 values printed
  endforeach()
  ```
- `while()`, `endwhile()`, `break()`, `continue()` also exist
- **Generator-expressions**: are evaluated at **build time** (or **install time**)
  by the build system (make, ninja, ...).
  - *Boolean generator*: `$<EQUAL:val1,val2>` will be replaced by `TRUE` if val1==val2
  - *String-valued generator*: `"$<BOOL_VAR:value>"` will be replaced by value if BOOL_VAR is true.
  ```cmake
  target_include_directories(my_target PUBLIC
    $<BUILD_INTERFACE:${CMAKE_SOURCE_DIR}/include>
    $<INSTALL_INTERFACE:include>
  )
  ```
  `$<IF:cond,valueIfTrue,valueIfFalse>`, `$<TARGET_FILE_NAME:target>`...
  Calls to generator expressions can be nested.
  For the complete list see [https://cmake.org/cmake/help/latest/manual/cmake-generator-expressions.7.html](https://cmake.org/cmake/help/latest/manual/cmake-generator-expressions.7.html).


### Functions, macros and modules

- **functions**: are always with variable number of arguments
  and have their own scope of variables.
  They do not return any value.
  ```cmake
  function(func_name arg1 arg_ret) # and unnamed parameters
    message(${arg1})
    # the list of unnamed parameters are stored in the list ${ARGN}
    set(${arg_ret} "a returned value" PARENT_SCOPE) # to fake return values
  endfunction()
  ```
  - Argument parsing:
    ```cmake
    function(my_function)
      cmake_parse_arguments(
          MY_FUNCTION_PREFIX
          "BOOL_VAR1;BOOL_VAR2"
          "SINGLE_VAR1;SINGLE_VAR2;SINGLE_VAR3"
          "LIST_VAR1;LIST_VAR2"
          ${ARGN}
      )
    endfunction()

    my_function(BOOL_VAR1 SINGLE_VAR2 value LIST_VAR1 some other values)
    # inside the function
    MY_FUNCTION_PREFIX_BOOL_VAR1 = TRUE
    MY_FUNCTION_PREFIX_BOOL_VAR2 = FALSE
    MY_FUNCTION_PREFIX_SINGLE_VAR1 = <UNDEFINED>
    MY_FUNCTION_PREFIX_SINGLE_VAR2 = value
    MY_FUNCTION_PREFIX_SINGLE_VAR3 = <UNDEFINED>
    MY_FUNCTION_PREFIX_LIST_VAR1 = "some;other;values"
    MY_FUNCTION_PREFIX_LIST_VAR2 = <UNDEFINED>
    ```
- **macros**: do not have their own scope and will spread their
  variables in the parent scope. Be very careful.
- **modules**: are files that contain cmake code and can be included with `include()`

**NB**: Use macros to wrap commands that have output parameters,
otherwise create a function.

**NB**: How to deprecate functions and variables
```cmake
# deprecate the function/macro my_cmd
macro(my_cmd)
  message(DEPRECATION "This command is deprecated.")
  _my_cmd(${ARGV})
endmacro()
# deprecate any variable
function(__deprecated_var var access val file stack)
  if(access STREQUAL "READ_ACCESS")
    message(DEPRECATION "The variable ${var} is deprecated")
  endif()
endfunction()
variable_watch(var_name __deprecated_var)
```
Note that an overridden function can still be called with "_name()".
```cmake
# Caveats of overriding
function(my_func) #1
endfunction()
function(my_func) #2
  _my_func() # calls 2 recursively (not 1)
endfunction()
function(my_func) #3
  _my_func() # calls 2
endfunction()
my_func() # calls 3
```

**NB**: When creating a function that add targets to the build,
instead of having lots of parameters, use custom target properties
and expand them in the command line with `$<TARGET_PROPERTY:tgt,prop>`
and possibly `COMMAND_EXPAND_LISTS`. This way it is possible to set things
after the function call (like with `target_*()`).


### Running commands

- at configure time (when CMakeLists.txt is parsed):
  `execute_process(COMMAND echo hello)`
  to display the command before it is run (for debugging),
  `execute_process(COMMAND <cmd> COMMAND_ECHO <STDOUT|STDERR>)`
- at build time (when the targets are built):
  - produce a file (that may then be used as a source (dependency) to build other targets):
    `add_custom_command(OUTPUT path/to/a/file COMMAND echo hello DEPENDS file1 file2)`
  - add a new target to the build system:
    `add_custom_target(my_target ALL DEPENDS path/to/a/file COMMENT "Building my_target")`
  - add a build command to a target production:
    `add_custom_command(TARGET a_target POST_BUILD COMMAND echo hello)`
  - **NB**: custom targets are always considered out of date. Never use the
    `COMMAND` option of `add_custom_target()` but use `add_custom_command()`
    and the `DEPENDS` option of `add_custom_target()`.


### Add information to files at configure time

Fill some "template files" with values of CMake variables. It can be any
type of file: headers, cmake modules, docs, scripts...

In a file with ".in" extension (like stuff.h.in)
```
#pragma once

// @useful_comment@

#define VAR @VAR@
#cmakedefine OPTIONAL_VAR @OPTIONAL_VAR@
#cmakedefine01 @OPTIONAL_VAR@
```

In CMakeLists.txt
```cmake
set(useful_comment "Hey, I'm a comment!")
set(VAR 123)
configure_file("${PROJECT_SOURCE_DIR}/include/myLib/stuff.h.in"
               "${PROJECT_BINARY_DIR}/include/myLib/stuff.h")
```

It will generate (in the build tree)
```c
#pragma once

// Hey, I'm a comment!

#define VAR 123
/* #undef OPTIONAL_VAR */
#define OPTIONAL_VAR 0
```

:warning: If the generated file is a header, then the path to
the generated file in the build tree must be added to the
include directories of the targets if any (with `target_include_directories()`)


## Project architecture

```
- project                    ==> name of the project (root folder)
  - .gitignore               
  - README.md                
  - LICENCE                  
  - CMakeLists.txt           ==> file to configure the project
  - cmake                    ==> directory containing cmake code (to find libraries, ...)
    - FindSomeLib.cmake      
  - include                  ==> the content of this folder could be copy-pasted to /usr/include
    - project                
      - lib.hpp              
  - src                      
    - lib.cpp                ==> sources of the library
  - tests                    
    - test1source.cpp        
    - test2script.sh         
  - docs                     
    - Doxyfile.in            
  - extern                   
    - googletest_gitsubmodule
  - scripts                  
    - helper.py              
```
Use `set(CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake" ${CMAKE_MODULE_PATH})` so that
".cmake" files can be included with `include(filename)`.

If the project contains sub-projects, then each sub-project should be a sub-directory
with its own "CMakeLists.txt" and the top level folder should have a master
"CMakeLists.txt" that uses `add_subdirectory()` and may define some global variables.


### Top level CMakeLists.txt

```cmake
# CMake version to use
cmake_minimum_required(VERSION 3.14)

# The global project
project(MyProject VERSION 1.0
                  LANGUAGES CXX
                  DESCRIPTION "Very nice project" )

# set variables relative to the whole project here (e.g. CMAKE_MODULE_PATH)
# optionally enable warning for the whole project here

# Add subdirectories (that contain their own CMakeLists.txt)
add_subdirectory(sub-project1)
add_subdirectory(sub-project2)
...
# Or build binaries if no sub-modules
```

**Enable warnings** in the whole project
(but it is better to do it for each target in subdirectories):
```cmake
if(MSVC)
  add_compile_options(/W4)
else()
  add_compile_options(-Wall -Wextra "-Wl,--no-undefined")
endif()
```

**NB**: It is mandatory to call the `project()` function at the beginning
of the toplevel CMakeLists.txt file. Additional commands can be passed with
`CMAKE_PROJECT_INCLUDE` and `CMAKE_PROJECT_INCLUDE_BEFORE`.


### Sub CMakeLists.txt

```cmake
cmake_minimum_required(VERSION 3.14)
project(MySubProject VERSION 1.0
                     LANGUAGES CXX
                     DESCRIPTION "Very nice sub-project" )

# look for dependencies here
find_package(SomeLib 4.2 REQUIRED)

# build binaries and link here
add_library(...)
add_executable(...)
target_*(...)
```

**NB**: To where we are in the source/build tree there is a couple of variables:
* `CMAKE_SOURCE_DIR`/`CMAKE_BINARY_DIR`: source/build tree of the top level project
* `PROJECT_SOURCE_DIR`/`PROJECT_BINARY_DIR`: source/build tree of the last project (last call to `project()`)
* `CMAKE_CURRENT_SOURCE_DIR`/`CMAKE_CURRENT_BINARY_DIR`: source/build tree of the current CMakeLists.txt being processed
* `CMAKE_CURRENT_LIST_DIR`, `CMAKE_CURRENT_LIST_FILE`...


## Building binaries

- compile an **executable**: `add_executable(one two.cpp three.h)`
- compile a **library**: `add_library(one STATIC two.cpp three.h)` or `add_library(another SHARED another.cpp another.h)`
  - specify the folder containing the public headers: `target_include_directories(one PUBLIC include)`
  (or `target_include_directories(my_library PRIVATE include/)` and
  `target_include_directories(my_library SYSTEM INTERFACE include/)`
  to disable header warnings for downstream projects but keep them in ours.
  It is possible to use the generator expressions
  `$<BUILD_INTERFACE:${CMAKE_SOURCE_DIR}/include> $<INSTALL_INTERFACE:include>`)
- tell cmake a target needs a **dependency** (library+headers): `target_link_libraries(another PRIVATE one)`

_Remarks_:
  - add source files to a target afterward: `target_sources(target PRIVATE file.cpp)`
  - 3 types of libraries can be built: `STATIC`, `SHARED` or `MODULE` (modules
  are plugins, they are not linked but can be loaded dynamically)
  - it is possible to use glob patterns to specify the list of source files.
  `file(GLOB src *.c)`
    - _CAVEATS_: 
      - the glob pattern is evaluated at configure time which means that each
      time a file is added cmake must be called to reconfigure before compiling
      the project.
      - if you still want to use a glob pattern, use the CONFIGURE_DEPENDS
      option like this `file(GLOB src CONFIGURE_DEPENDS *.c)` which will pass
      the globing expression to the build system so that each time the project
      is compiled the expression is processed. :warning: Not all generators
      support this feature (see
      [doc](https://cmake.org/cmake/help/latest/command/file.html?highlight=file#glob)),
      make and ninja do.


### Create alias of targets

```cmake
add_library(PackageName::target ALIAS target)
# then, target or PackageName::target can be used
add_executable(<name> ALIAS <target>) # same for executable
```

**NB**: Always make an alias of each target which will be exported so that
it can be used the same way whether it is imported or built.
`add_library(PackageName::one ALIAS one)` and then use PackageName as NAMESPACE
when exporting the export set of targets.

**NB**: An ALIAS target cannot be used to modify target properties. It
cannot be installed. It cannot be aliased again.


### "Building" header-only libraries

```cmake
add_library(mylib INTERFACE) # fake target, nothing is built
target_include_directories(mylib INTERFACE include
                           $<BUILD_INTERFACE:${CMAKE_SOURCE_DIR}/include> # folder where headers are in
                           $<INSTALL_INTERFACE:include>) # folder where headers will be after installation
target_link_libraries(mylib INTERFACE Boost::boost) # libraries required to compile a project based on mylib
# Then, use mylib as any other target
```


## Choosing compile features

```cmake
# specify the version of C++
set(CMAKE_CXX_STANDARD 17) # set default for the whole project in the top level CMakeLists.txt
set(CMAKE_CXX_STANDARD_REQUIRED TRUE) # do not fall back on older version
set(CMAKE_CXX_EXTENSIONS OFF) # strict c++.. instead of gnu++..

target_compile_features(my_target PUBLIC cxx_std_11) # override default for one target

# Interprocedural optimization
include(CheckIPOSupported)
check_ipo_supported(RESULT ipo_is_supported)
if(ipo_is_supported)
  set_target_properties(my_target PROPERTIES INTERPROCEDURAL_OPTIMIZATION TRUE)
endif()
```


## How to link against external libraries?

The following snippets assume that the external libraries are installed
(binaries and headers are in /usr/local/(include|lib) for example).


### The library is standard (e.g. maths)

```cmake
find_library(MATH_LIBRARY m)
if(MATH_LIBRARY)
  target_link_libraries(my_target PRIVATE ${MATH_LIBRARY})
endif()
```


### The library use CMake and has a config-file or a find-module exists for CMake to use it

```cmake
find_package(Dependency [1.42] REQUIRED)
target_link_libraries(my_target PRIVATE Dependency) # will also set include directories
```

What happened here?
* CMake looks for a dependency-config.cmake or DependencyConfig.cmake or
FindDependency.cmake file in `CMAKE_PREFIX_PATH` to find Dependency and may
produce an ERROR if not found (because of REQUIRED).
[More information](https://cmake.org/cmake/help/latest/command/find_package.html#search-procedure)
* Then every INTERFACE_properties of the target "Dependency" is added to
"my_target" properties (because of PRIVATE).

Sometimes, with some libraries, it is possible to load only some parts of
the library. These parts are called components.

```cmake
find_package(Dependency REQUIRED COMPONENTS component1 component2)
target_link_libraries(my_target PRIVATE Dependency::component1 Dependency::component2)
```

**NB**: To tell CMake to use the highest/latest version available of a package
`set(CMAKE_FIND_PACKAGE_SORT_ORDER NATURAL)` before calling `find_package()`.
Note that it works only for config-file packages (\*-config.cmake or
\*Config.cmake) and not for find module packages (Find\*.cmake).


#### What to do if no target were imported? (with find-module)

In case of error
"Target "AAA" links to target "XXX" but the target was not found."
happening (while the library is correctly installed).
This can happened with old find-modules.

```cmake
add_library(Dependency IMPORTED INTERFACE)
target_include_directories(Dependency INTERFACE "${Dependency_INCLUDE_DIRS}")
target_link_libraries(Dependency INTERFACE "${Dependency_LIBRARIES}")
```

**NB**: `Dependency_LIBRARIES` and `Dependency_INCLUDE_DIRS` are not standard
so it is better to open the file "FindDependency.cmake" and look at which
variables are exported.


### Library = binaries + headers

```cmake
find_path(FOO_INCLUDE_DIR "foo.h" "/usr/include" "/usr/local/include") # where the headers may be (optional)
find_library(FOO_LIBRARY "foo" "/usr/lib" "/usr/local/lib") # where the binaries may be (optional)
# NB: FOO_INCLUDE_DIR and FOO_LIBRARY are cached (no need to search again next times)
if(FOO_INCLUDE_DIR AND FOO_LIBRARY)
	add_library(foo INTERFACE)
	target_include_directories(foo INTERFACE ${FOO_INCLUDE_DIR})
	target_link_libraries(foo INTERFACE ${FOO_LIBRARY})
else()
	message(FATAL_ERROR "library foo was not found")
endif()
```


#### A reusable alternative: write a find-module file

In a file called "FindFoo.cmake" in the cmake folder of the project:
```cmake
# FindFoo.cmake
# Export the following targets: Foo::Foo

find_path(Foo_INCLUDE_DIR foo.h)
find_library(Foo_LIBRARY foo)
mark_as_advanced(Foo_INCLUDE_DIR Foo_LIBRARY)

include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(Foo REQUIRED_VARS Foo_LIBRARY Foo_INCLUDE_DIR)

if(Foo_FOUND AND NOT TARGET Foo::Foo)
  add_library(Foo::Foo UNKNOWN IMPORTED)
  set_target_properties(Foo::Foo PROPERTIES
    IMPORTED_LOCATION "${Foo_LIBRARY}"
    INTERFACE_SYSTEM_INCLUDE_DIRECTORIES "${Foo_INCLUDE_DIR}")
endif()
```

This is a minimal find-module, for a more complete version see
[https://cmake.org/cmake/help/latest/manual/cmake-developer.7.html](https://cmake.org/cmake/help/latest/manual/cmake-developer.7.html)


### Library = sources + another build system

```cmake
add_custom_command(OUTPUT path/to/output
    COMMAND cmd_to_build
    DEPENDS some_target)
add_custom_target(generate_target ALL
    DEPENDS path/to/output)
# optionally add a fake target
```


### Download and integrate third-party libraries

For an easiest and lightest integration of external libraries,
it is a good idea to use git submodules.
```cmake
find_package(Git QUIET)
if(GIT_FOUND AND EXISTS "${PROJECT_SOURCE_DIR}/.git")
  # Update submodules as needed
  option(GIT_SUBMODULE "Check submodules during build" ON)
  if(GIT_SUBMODULE)
    message(STATUS "Submodule update")
    execute_process(COMMAND Git::Git submodule update --init --recursive
                    WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
                    RESULT_VARIABLE GIT_SUBMOD_RESULT)
    if(NOT GIT_SUBMOD_RESULT EQUAL "0")
      message(FATAL_ERROR "git submodule update --init failed with ${GIT_SUBMOD_RESULT}, please checkout submodules")
    endif()
  endif()
endif()

if(NOT EXISTS "${PROJECT_SOURCE_DIR}/extern/repo/CMakeLists.txt")
  message(FATAL_ERROR "The submodules were not downloaded! GIT_SUBMODULE was turned off or failed. Please update submodules and try again.")
endif()
```

But it is also possible to download a library with CMake (here Catch2 and Google test)
```cmake
# At configure time
include(FetchContent)
FetchContent_Declare( # prepare
  catch
  GIT_REPOSITORY https://github.com/catchorg/Catch2.git
  GIT_TAG        v2.2.1
  [DOWNLOAD_DIR "..."]
  [SOURCE_DIR "..."]
  [BINARY_DIR "..."]
)
FetchContent_MakeAvailable(catch) # download

# Or at build time
include(ExternalProject)
ExternalProject_Add(googletest
    GIT_REPOSITORY https://github.com/google/googletest.git
    GIT_TAG master
    [SOURCE_DIR "${CMAKE_BINARY_DIR}/googletest-src"]
    [BINARY_DIR "${CMAKE_BINARY_DIR}/googletest-build"]
    [CONFIGURE_COMMAND "..."]
    [BUILD_COMMAND "..."]
    [INSTALL_COMMAND "..."]
    [TEST_COMMAND "..."]
)
```


### Good practice for dependencies lookup

It is a good idea to list all the calls to `find_package()`
in the same place and to use the `FeatureSummary` CMake package.

```cmake
include(FeatureSummary)

find_package(XXX)
set_package_properties(XXX PROPERTIES
  TYPE REQUIRED
  PURPOSE "Used to do something.")
[... other find_package ...]

feature_summary(INCLUDE_QUIET_PACKAGES
  FATAL_ON_MISSING_REQUIRED_PACKAGES
  WHAT ALL)
```
Documentation: [https://cmake.org/cmake/help/latest/module/FeatureSummary.html](https://cmake.org/cmake/help/latest/module/FeatureSummary.html)


## How to make easily linkable/installable/packageable libraries

In a file named "my_package-config.cmake.in", paste:
```cmake
#[=============================================[
@package_name@ config file

Import the following targets:
  @namespace_prefix@::(@library_names@)

Set the variables:
  @package_name@_FOUND
  @package_name@_VERSION
  @package_name@_INCLUDE_DIRS (legacy)
  @package_name@_LIBRARIES    (legacy)

@package_name@ needs the following dependencies to work:
  @package_dependencies@
#]=============================================]

include_guard()

@PACKAGE_INIT@

if(NOT @package_name@_FIND_QUIETLY)
  message(STATUS "Import @package_name@")
endif()

# Find dependencies of the package
include(CMakeFindDependencyMacro)
foreach(dep IN ITEMS @package_dependencies@)
  separate_arguments(dep)
  find_dependency(${dep})
  list(GET dep 0 dep_name)
  if(NOT ${dep_name}_FOUND)
    set(@package_name@_FOUND FALSE)
    set(@package_name@_NOT_FOUND_MESSAGE "Dependency ${dep_name} of @package_name@ not found")
    return()
  endif()
endforeach()

include("${CMAKE_CURRENT_LIST_DIR}/@package_name@_targets.cmake")

set(@package_name@_INCLUDE_DIRS)
set(@package_name@_LIBRARIES)
foreach(library_name IN ITEMS @library_names@)
    get_target_property(tmp_inc @namespace_prefix@::${library_name} INTERFACE_INCLUDE_DIRECTORIES)
    list(APPEND @package_name@_INCLUDE_DIRS ${tmp_inc})
    list(APPEND @package_name@_LIBRARIES @namespace_prefix@::${library_name})
endforeach()
```

In the CMakeLists.txt of the package of libraries, fill the first part of this
file and paste the second.
```cmake
cmake_minimum_required(VERSION 3.14)

# project description
project(my_package VERSION 1.0
                 LANGUAGES C
                 DESCRIPTION "Am amazing package" )

# Look for dependencies (if any)
set(package_dependencies "Foo 3.7" "Bar 1.2 COMPONENTS Baz")
find_package(Foo 3.7 REQUIRED)
find_package(Bar 1.2 REQUIRED COMPONENTS Baz)

# Names of the libraries to export and name of the prefix
# it will then be usable from other package trough "${namespace_prefix}::${library_name}"
set(library_names my_lib my_other_lib) # Names of libraries (that will be exported)
set(namespace_prefix MyPackage)        # "${namespace_prefix}::${library_name}" usable
set(package_name my_package)           # find_package(my_package) will work

# Sources and headers of each libraries
set(my_lib_sources file.c)
set(my_lib_headers "${CMAKE_SOURCE_DIR}/include/${package_name}/my_lib/file.h")
set(my_lib_header_dir "${CMAKE_SOURCE_DIR}/include/${package_name}")
set(my_lib_dependencies PRIVATE some_lib PUBLIC some_other_lib)
set(my_lib_type SHARED) # STATIC, SHARED or MODULE

set(my_other_lib_sources other_file.c)
set(my_other_lib_headers ${CMAKE_SOURCE_DIR}/include/${package_name}/my_other_lib/other_file.h)
set(my_other_lib_header_dir ${CMAKE_SOURCE_DIR}/include/${package_name})
set(my_other_lib_dependencies PRIVATE some_lib PUBLIC some_other_lib)
set(my_other_lib_type SHARED)


### Automatic beyond this line

# set export parameters
set(${package_name}_dest "lib/${package_name}-${PROJECT_VERSION}")
set(${package_name}_include_dest "include/${package_name}-${PROJECT_VERSION}")
set(${package_name}_config_files_dest "lib/cmake/${package_name}-${PROJECT_VERSION}")

foreach(library_name IN LISTS library_names)
    # build the library
    add_library(${library_name} ${${library_name}_type} ${${library_name}_sources} ${${library_name}_headers})
    add_library(${namespace_prefix}::${library_name} ALIAS ${library_name})
    target_include_directories(${library_name} PUBLIC
        $<BUILD_INTERFACE:${${library_name}_header_dir}>
        $<INSTALL_INTERFACE:${${package_name}_include_dest}>) # or append /${library_name} not to have to write #include <library_name/file.h>
    set_target_properties(${library_name} PROPERTIES PUBLIC_HEADER ${${library_name}_headers})
    target_link_libraries(${library_name} ${${library_name}_dependencies})

    # export the library
    install(TARGETS ${library_name} EXPORT ${package_name}
        LIBRARY DESTINATION "${${package_name}_dest}"
        PUBLIC_HEADER DESTINATION "${${package_name}_include_dest}/${library_name}"
        INCLUDES DESTINATION "${${package_name}_include_dest}/${library_name}")
endforeach()

# export the package of targets
include(CMakePackageConfigHelpers)
configure_package_config_file(${package_name}-config.cmake.in 
    "${PROJECT_BINARY_DIR}/${package_name}-config.cmake"
    INSTALL_DESTINATION "${${package_name}_config_files_dest}")
write_basic_package_version_file(${package_name}-config-version.cmake COMPATIBILITY AnyNewerVersion)
install(FILES
        "${PROJECT_BINARY_DIR}/${package_name}-config.cmake"
        "${PROJECT_BINARY_DIR}/${package_name}-config-version.cmake"
        DESTINATION "${${package_name}_config_files_dest}")
install(EXPORT ${package_name} NAMESPACE ${namespace_prefix}:: DESTINATION "${${package_name}_dest}"
        FILE "${package_name}_targets.cmake" DESTINATION "${${package_name}_config_files_dest}")
```


### Install the library:

```sh
cmake -S . -B build/
cmake --build build/ --target install
```
The library will then be installed (in "/usr/local/" by default on linux).
This may required root privileges if it targets a system folder.

```sh
# Or, for an already built project (after --build)
cmake --install build/ [--prefix path/to/install/folder ]
```

Installing a project produces a "install_manifest.txt" file that
contains the list of all installed files. It can be used to uninstall
the project `xargs rm < install_manifest.txt` (can be a custom target
of the build).


### Use the installed library as dependency in another project

```cmake
# list(APPEND CMAKE_PREFIX_PATH "path/to/custom/install/folder") # if non standard install location
find_package(my_package REQUIRED)
target_link_libraries(client_target PRIVATE MyPackage::my_lib)
```

To include headers
```c
#include <my_lib/file.h>
```


## Go further with components

It is possible to have parts of a package that are not loaded
automatically with `find_package()`. These parts are called
**components** and are loaded when specified to `find_package()`.


### Make a part of a library a component

This is very similar to building a library.

In the file "my_package-config.cmake.in", append:
```cmake
### ... normal configuration ...

# Find components
foreach(comp IN ITEMS ${@package_name@_FIND_COMPONENTS})
  include("${SELF_DIR}/@package_name@_${comp}.cmake")
  if(NOT TARGET @namespace_prefix@::${comp})
    set(@package_name@_FOUND False)
    if(@package_name@_FIND_REQUIRED_${comp})
      set(@package_name@_NOT_FOUND_MESSAGE "Unsupported component: ${comp}")
      return()
    endif()
  else()
    get_target_property(tmp_inc @namespace_prefix@::${comp} INTERFACE_INCLUDE_DIRECTORIES)
    list(APPEND @package_name@_INCLUDE_DIRS ${tmp_inc})
    list(APPEND @package_name@_LIBRARIES @namespace_prefix@::${comp})
  endif()
endforeach()
```

If the component sources are in a separated folder, put this in
the "CMakeLists.txt" of the folder and fill the first part: (otherwise
see after)
```cmake
cmake_minimum_required(VERSION 3.14)

# project description
project(super_component VERSION 1.0
                 LANGUAGES C
                 DESCRIPTION "A component of the amazing package." )

set(component_library_names my_component)
set(namespace_prefix MyPackage) # same as in the core part of the package
set(package_name my_package)    # same as in the core part of the package
set(component_name super_component)

# Sources and headers of each libraries
set(my_component_sources my_component.c)
set(my_component_headers "${CMAKE_SOURCE_DIR}/include/${package_name}/my_component/my_component.h")
set(my_component_header_dir "${CMAKE_SOURCE_DIR}/include/${package_name}")
set(my_component_type SHARED)
set(my_component_dependencies PRIVATE MyPackage::my_lib)


### Automatic beyond this line

# set export parameters
set(${package_name}_dest "lib/${package_name}-${PROJECT_VERSION}")
set(${package_name}_include_dest "include/${package_name}-${PROJECT_VERSION}")
set(${component_name}_config_files_dest "lib/cmake/${package_name}-${PROJECT_VERSION}")

foreach(library_name IN LISTS component_library_names)
    # build the library
    add_library(${library_name} ${${library_name}_type} ${${library_name}_sources} ${${library_name}_headers})
    add_library(${namespace_prefix}::${library_name} ALIAS ${library_name})
    target_include_directories(${library_name} PUBLIC
        $<BUILD_INTERFACE:${${library_name}_header_dir}>
        $<INSTALL_INTERFACE:${${package_name}_include_dest}>) # or append /${library_name} not to have to write #include <library_name/file.h>
    set_target_properties(${library_name} PROPERTIES PUBLIC_HEADER ${${library_name}_headers})
    target_link_libraries(${library_name} ${${library_name}_dependencies})

    # export the library
    install(TARGETS ${library_name} EXPORT ${component_name}
        LIBRARY DESTINATION "${${package_name}_dest}"
        PUBLIC_HEADER DESTINATION "${${package_name}_include_dest}/${library_name}"
        INCLUDES DESTINATION "${${package_name}_include_dest}/${library_name}")
endforeach()

# export the package of targets for this component
install(EXPORT ${component_name} NAMESPACE ${namespace_prefix}:: DESTINATION "${${package_name}_dest}"
        FILE "${package_name}_${component_name}.cmake" DESTINATION "${${component_name}_config_files_dest}")
```

If it is not in a separated folder:
- remove calls to `cmake_minimum_required()` and `project()`
- in the "CMakeLists.txt" of the package
  - set `component_library_names` and `component_name`
  - set sources, headers... for each library of the component
  - copy and paste from "set(${component_name}_config_files_dest ...
  to the end" at the end of CMakeLists.txt

**NB**: It is a good idea to have only one exported library in a component and
to give it the name of the component so that the user can use the target
`Package::Component` after `find_package(Package REQUIRED COMPONENTS Component)`.


### Use components of a library

In the "CMakeLists.txt" of the project that requires the component:
```cmake
find_package(my_package 1.4 REQUIRED COMPONENTS super_component)
# MyPackage::my_component is available
```


## Package the project (CPack)

At the end of the CMakeLists.txt (the root one) or in a separate
file which is included
```cmake
set(CPACK_PACKAGE_NAME "my-package") # top project name by default
set(CPACK_PACKAGE_VENDOR "MyCompany")
set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "A package of useful libraries")
foreach(t MAJOR MINOR PATCH)
   set(CPACK_PACKAGE_VERSION_${t} ${PROJECT_VERSION_${t}})
endforeach()
set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_CURRENT_SOURCE_DIR}/LICENCE")
set(CPACK_RESOURCE_FILE_README "${CMAKE_CURRENT_SOURCE_DIR}/README.md")
set(CPACK_GENERATOR "TGZ;ZIP;DEB;RPM;NuGet") # error if empty
set(CPACK_SOURCE_GENERATOR "TGZ;ZIP")
set(CPACK_SOURCE_IGNORE_FILES /.git /install /.*build.*)
include(CPack)
```
This create two new targets in the build system called "package" and
"package_source" and a file "CPackConfig.cmake" in the build tree.

Running `cmake --build build/ --target package` will generate
"${CPACK_PACKAGE_NAME}-${CPACK_PACKAGE_VERSION}-${CPACK_SYSTEM_NAME}.(zip|tar.gz|rpm|deb)"
files for each ${CPACK_GENERATOR} selected (if possible on the machine).

The generated packages contain everything installed with `install()`.

It is also possible to use directly the `cpack` command.
```sh
cd build/
cpack [-G "ZIP;TGZ"] [-D <var>=<val>] [--config MyCPackConfig.cmake]
# by default cpack will process the file CPackConfig.cmake to generate the packages
```
[CPack documentation](https://cmake.org/cmake/help/latest/manual/cpack.1.html)


## Build types

### For single-configuration generators (e.g. make, ninja)

The build type must be set at configure time with the variable CMAKE_BUILD_TYPE.
```sh
$ cmake -DCMAKE_BUILD_TYPE=Debug ...
```

Possible values are: Debug, Release, MinSizeRel, RelWithDebInfo.

It is possible to define a default build type in CMakeLists.txt.
```cmake
set(default_build_type "Release")
if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
  message(STATUS "Setting build type to '${default_build_type}' as none was specified.")
  set(CMAKE_BUILD_TYPE "${default_build_type}" CACHE
      STRING "Choose the type of build." FORCE)
  # Set the possible values of build type for cmake-gui
  set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS
    "Debug" "Release" "MinSizeRel" "RelWithDebInfo")
endif()
```


### For multi-configurations generators (e.g. Visual Studio, Xcode)

The build type is selected at build time either with the IDE interface
or with cmake with `cmake --build build/ --config Debug`.


### Advice for both

Because the way build types are handled depends on the generator, it is
advised to use the boolean generator expression `$<CONFIG:cfg>` to modify
things depending on the build type.


### Custom build types

It is possible to create other build types than the default ones.
We only have to set the values of the cached variables `SOMETHING_<CONFIG>`
like `CMAKE_C_FLAGS_<CONFIG>`, `CMAKE_CXX_FLAGS_<CONFIG>`,
`CMAKE_EXE_LINKER_FLAGS_<CONFIG>`, `CMAKE_SHARED_LINKER_FLAGS_<CONFIG>`...

```cmake
set(CMAKE_C_FLAGS__MyCustomBuildType -s -O2 -Wall -Wextra)
# set some other variables

# The following lines "register" the new build type
if(CMAKE_CONFIGURATION_TYPES)
  list(APPEND CMAKE_CONFIGURATION_TYPES "MyCustomBuildType")
else()
  set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS
    "Debug" "Release" "MinSizeRel" "RelWithDebInfo" "MyCustomBuildType")
endif()
```


## Set up a toolchain

The toolchain is cmake way of enabling cross compilation. A toolchain file
is basically a list of tools to use to compile the project. This file
is processed just before the first CMakeLists.txt of the source tree.

In a file named "toolchain.cmake" (for instance):
```cmake
# This enables Linux users to compile for Windows
set(CMAKE_SYSTEM_NAME Windows)
set(CMAKE_C_COMPILER x86_64-w64-mingw32-gcc)
set(CMAKE_CXX_COMPILER x86_64-w64-mingw32-g++)
set(CMAKE_RC_COMPILER x86_64-w64-mingw32-windres)

set(CMAKE_FIND_ROOT_PATH /usr/x86_64-w64-mingw32)

set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)

set(CMAKE_CROSSCOMPILING_EMULATOR wine64)
```

Never put logic in toolchain files.

```sh
# To use the toolchain file
-D CMAKE_TOOLCHAIN_FILE=toolchain.cmake # at configure time
```


## Pre-load the cache

It is possible to specify some initial entries for the cache in
a file that will be processed before everything else.
This file must only contain calls to `set(... CACHE ...)`.

In a file named "cache_preload.cmake" (for instance):
```cmake
set(MY_CACHED_VARIABLE "a value" CACHE STRING "description")
set(MY_OPTION ON CACHE BOOL "help message")
```

```sh
# To use the file
cmake ... -C cache_preload.cmake # at configure time
```


## Useful build-in things

### Useful commands

- `file()`: to manipulate files (read, write, search,
  get info, lock, download, make directories...)
- `string()`: to manipulate strings (find, replace, regex,
  append, concat, upper/lower case, compare...)
- `list()`: to manipulate lists (get element, sublist,
  find, filter, append, transform, sort...)
- `math(EXPR <result_var> "<calculation>" [OUTPUT_FORMAT <format>])`
- `try_compile()`, `try_run()`: try to build or run something at configure time


### Useful variables

- `${CMAKE_COMMAND}` contains the path to `cmake` itself
- `${<dependency>_DIR}` contains the path to "dependency-config.cmake"


### Useful modules

```cmake
# for debugging cmake files
include(CMakePrintHelpers)
cmake_print_properties(...)
cmake_print_variables(...)

# check compiler flags
include(CheckCXXCompilerFlag)
check_cxx_compiler_flag(-someflag isSomeFlagAccepted)
```


## CMake command line tool

```sh
cd path/to/project/root/dir
# Configure the project (configure time) ==> always separate source tree and build tree
cmake -S . -B build/ [-G generator] # generate "makefile" or "build.ninja" or whatever
# build the project (build time)
cmake --build build/ # equivalent to "make"

# build a specific target (useful for test, install, package)
cmake --build build/ -t MyTarget # equivalent to "make MyTarget"

# define or modify a cached variable (at configure time)
cmake -DVAR_NAME=value ...
# set log level (default is status) (at configure time)
cmake --loglevel=<error|warning|notice|status|verbose|debug|trace>
# select the number of jobs running in parallel for the build system (at build time)
cmake -j 8 ...
# run a script (written in cmake language)
cmake -P cmake-script

# debug
cmake --trace ... # display each line processed (at configure time)
cmake --trace-expand ... # same but expands the variables
cmake --trace-source="filename" ... # same but only for one file
cmake --build build/ --clean-first ... # == --target clean then ...
cmake --build build/ -v # verbose

# see the cache
cmake -LAH build/ # list all cached variables and their help message

# Set default values for compilers and generator with environment variables (before configure)
CMAKE_GENERATOR="Unix Makefiles"
CC=gcc
CXX=clang++
# /!\ build systems programs and compilers are searched in the CMAKE_PREFIX_PATH
# before being searched in the PATH. (see https://cmake.org/cmake/help/latest/command/find_package.html#search-procedure)

# Other things not related to build
cmake --system-information | less # information about the system (more info if launched from a build tree)
cmake -E <cmd> [args...] # use a command (system agnostic) e.g. env, touch, sha256sum...
```

Note that it is also possible to use a TUI by invoking
`ccmake` to fill cached variables, then configure the
project by pressing "c" and then generate the files by pressing "g".
It is also possible to use a GUI with `cmake-gui`
to perform the same things (configuring and generating).
`ccmake` and `cmake-gui` support the same parameters
as the `cmake` command but they cannot compile they
can only configure.

:warning: You have to reconfigure the project
(by calling `cmake -S -B ...`) when you:
- you use `file(GLOB ...)` (without CONFIGURE_DEPENDS) and you added a new file
- you want to modify cached variables with the command line (-D...) and you did
  not modify CMakeLists.txt
- you want to modify the architecture of the build tree

:warning: If you want to switch generator (e.g. use ninja instead of make)
you first need to remove CMakeCache.txt file and CMakeFiles directory
(`cd build && rm -rf CMakeCache.txt CMakeFiles */CMakeFiles`) and
then configure with cmake (by calling `cmake -S -B ...`).

:information_source: About the cache:
- The cache (CMakeCache.txt) is built the first time the projet is configured
- To modify the cache:
  - modify CMakeCache.txt
  - use ccmake or cmake-gui after the first configuration
  - use "-D OPT=val" when running cmake
  - use `set(var ... CACHE ... FORCE)` in CMakeLists.txt
  - pre-load a cache with `cmake -C cache_preload.cmake ...`
- To reset the cache, delete CMakeCache.txt and reconfigure the project
- If you just want to reset some variables, delete them from the cache then use
  `cmake --build build/ -t rebuild_cache`

:information_source: On the contrary, the generated files are generated each
time the project is reconfigured.


## Optimize CMake

Comes from [https://gitlab.kitware.com/cmake/community/wikis/doc/cmake/Performance-Tips](https://gitlab.kitware.com/cmake/community/wikis/doc/cmake/Performance-Tips)

### Optimize the configuration

To see what takes the longest:
```sh
strace -r cmake -S . -B build/ --trace 2>&1 | sort -r | less
```

Advice:
- Try not to use `execute_process()` which happen at configure time and
  use `add_custom_command()` instead which happen at build time (and can
  be parallelized).
- `list(APPEND var val)` is faster than `set(var "${var};val")`
- Use `include_guard()` at the beginning of files that can be
  included (\*.cmake, Find\*.cmake, \*Config.cmake)
- Do not call `find_package()` multiple times for the same package (even
  with different components).
- Do not test constants in functions but have different version of them
  ```cmake
  if(condition on a constant)
    function(foo)
      # code
    endfunction()
  else()
    function(foo)
      # code
    endfunction()
  endif()
  ```
- Use `break()` or `return()` in loops as soon as possible
- Use `list(FILTER)` then a loop rather than `if()` inside loops
- Use `file(STRINGS ... REGEX ...)` instead of `file(READ)` and `string(REGEX)`
- Use `foreach(elem IN LISTS list)` instead of expanding the lists
- Use the latest version of CMake (new policies are faster)


### Optimize the build

Use a release build type: "Release", "MinSizeRel" or "RelWithDebInfo".
Or use optimization flags: "-O2", "-O3" or "-Os"
```sh
cmake -D CMAKE_BUILD_TYPE=Release ... # configure step of single configuration generators
# Select "compile release" on multi-configuration generators
export CXXFLAGS=-O3
cmake ... # or -D CMAKE_CXX_FLAGS=-O3
```


## CMake do's and don'ts

### Use

- `PUBLIC`, `PRIVATE` or `INTERFACE` always
- a white board to visualize the dependency graph (or see graphviz below)
- at least CMake 3.0.0
- global definitions of project properties (e.g. warnings)
- let cmake manage the transitivity
- imported targets from external dependencies (with `target_link_libraries()`)
- find modules (Find\*.cmake) for libraries that does not support CMake
- export libraries interface
- `cmake_parse_arguments()` for functions with complex arguments behavior
- toolchain files for cross compiling
- properties instead of variables whenever possible


### Don't use
- `include_directories()`
- `link_directories()`
- `link_libraries()`
- `add_definitions()`
- `add_compile_options()`
- path outside of the module (especially with `target_include_directories()`)
- flags directly instead of cmake commands
- `CMAKE_CXX_FLAGS`
- `${lib_INCLUDE_DIRS}` or `${lib_LIBRARIES}`, use targets instead
- variables defined in external project


## Be careful with

- new versions of `Boost` because Boost does not provide a finder.
  The CMake team provides one (later on) through a CMake update.
  It is the same for some other libraries like SDL, PNG, OpenCL...
- `file(GLOB)` because each time a file is added, CMake has to be called
  to evaluate the list of files (the build system would not be able to see
  that a file was added otherwise)
- `export(EXPORT)`  because it generates non-relocatable configuration files
  but is useful if you want to use your project from its build tree rather
  than install it.
  Use `install(EXPORT)` instead.


## Some ideas and creeds to keep in mind while writing CMakeLists.txt

- Don't build packages, write packagable libraries!
- Think in terms of targets and properties, not in terms of object files and compile flags.
- The build system handles transitivity.
- CMake code is code, apply good practice (comments, indent, DRY...)


## Bonus

### Export compile commands to a json file

```cmake
set(CMAKE_EXPORT_COMPILE_COMMANDS ON CACHE BOOL "Enable/Disable output of compile_commands.json" FORCE)
```
There is no variable to export the link commands yet but for each target,
the link command is stored in "<build tree of the CMakeLists.txt>/CMakeFiles/<target>.dir/link.txt".


### CCache

```cmake
find_program(CCACHE_PROGRAM ccache)
if(CCACHE_PROGRAM)
    set(CMAKE_CXX_COMPILER_LAUNCHER "${CCACHE_PROGRAM}")
endif()
```


### Static analysis utilities

Use the target properties `CXX_CLANG_TIDY`, `CXX_CPPCHECK`, `CXX_CPPLINT`,
`CXX_INCLUDE_WHAT_YOU_USE` and `LINK_WHAT_YOU_USE`.


### Graph to visualize dependencies

```sh
mkdir graphviz && cd graphviz
cmake --graphviz=graph ..
dot graph -Tsvg -o graph.svg
```


### Use imported executable as command

```cmake
find_program(PYTEST_PROGRAM pytest)
if(NOT PYTEST_PROGRAM)
  message(FATAL_ERROR "Unable to find pytest.")
endif()
add_executable(pytest IMPORTED)
set_target_properties(pytest PROPERTIES IMPORTED_LOCATION ${PYTEST_PROGRAM})
# Then it is possible to use the target as an executable
add_test(NAME test_python COMMAND pytest --cov=mymodule --cov-report html)
```

Useful scripts

checkSourceFilesUsage.sh

#!/bin/bash
# Author: Thomas Cluzel <thomas.cluzel@cern.ch>
# This script checks if all the files in the src/ directory of every
# sub-projects are used in the CMakeLists.txt of the sub-project.
# This script is to be launched at the top level of the project.

for subpackage in `ls`
do
    if [ -f $subpackage/CMakeLists.txt ] # process only sub-packages that contains compiled stuff
    then
        # List all the sources they currently used
        array_of_sources=($(cat $subpackage/CMakeLists.txt | grep ' src/' | sed -r -e 's,^.*(src/[^ )]+).*$,\1,g'))
        cd $subpackage
        echo ==================== $subpackage ====================
        # List all the sources available in the src/ directory
        sources=$(find src -name '*.cpp' 2> /dev/null)
        if [ "x$sources" = "x" ] ; then continue ; fi
        for source_file in $sources
        do
            if [[ " ${array_of_sources[*]} " != *" $source_file "* ]] # check if some files are not used
            then
                echo "CMakeLists.txt of $subpackage does not use $source_file"
            fi
        done
        cd ..
    fi
done

extractCMakeDoc.py

#! /usr/bin/env python3
# -*- coding: utf-8 -*-

"""
A script to extract all blocks of documentation from CMake files.

:param $1: the name of the file to extract documentation from
:param $2: (optional) string to join the documentations

From
  #[===[.rst:
  doc1
  #]===]
  function(hello)
  # some code
  endfunction()
  #[===[.rst:
  doc2
  #]===]
  function(goodbye)
  # some code
  endfunction()
It retrieves
  doc1

  doc2

Example:
  
  ./extractCMakeDoc.py GaudiToolbox.cmake $'\n\n----------------------------------------\n\n'


:Author: Thomas Cluzel
:Mail: thomas.cluzel@cern.ch
:Date: Wed Aug 7 13:19:31 CEST 2019
"""

import re

def extract_doc(filename, glue:str = "\n\n\n\n\n") -> str:
  """
  This function extract all blocks of documentation of a CMake file.

  :param filename: str or path, to a file containing CMake code
  :param glue: str, string to join all blocks found
  :return: str, only the documentation
  """
  re_doc = re.compile(r'#\[(=+)\[[^\n]*\n(.*?)#\]\1\]', flags=re.DOTALL)
  with open(filename, "r") as file:
    return glue.join([match[1] for match in re_doc.findall(file.read())])
  
if __name__ == "__main__":
  from sys import argv
  print(extract_doc(*argv[1:]))

listCMakeFunctions.sh

# Thomas CLUZEL
# Script to list the names of all functions in a CMake file
#  $1: path to the file

if [ "x$1" = "x" ]
then
  echo "Error, you must specify a file \n \
Usage: \n \
  $0 file.cmake \n \
List the names of all functions declared in this file"
fi

filename="$1"
cat "$filename" | grep -v "endfunction" | grep "function(" | cut -d")" -f 1 | cut -d"(" -f 2 | cut -d" " -f 1

exit 0

updateCMakeLocally.sh

# Thomas CLUZEL
# Script to update an installed CMake to a particular version
#  $1 : version number of the release e.g. "3.14.1", "3.15.0-rc4"

version=$1

if [ "x$version" = "x" ]
then
   echo "Give the version number as first argument (e.g. 3.14.1, 3.15.0-rc4)"
   exit 1
fi

cd $(dirname $(dirname $(which cmake)))
cmake_install_dir=$(basename $PWD)
cd ..
rm -rf $cmake_install_dir
mkdir $cmake_install_dir
wget -qO- "https://github.com/Kitware/CMake/releases/download/v$version/cmake-$version-Linux-x86_64.tar.gz" | \
   tar --strip-components=1 -xz -C $cmake_install_dir

echo "CMake has been updated to version $version"

exit 0

old2modernCMake.py

#old2modernCMake
#! /usr/bin/env python3
# -*- coding: utf-8 -*-

"""
Script to convert the old CMake configuration of Gaudi based projects
to the new configuration.

Author: Thomas Cluzel <thomas.cluzel@cern.ch>
Last update: Thu Jul 18 11:45:39 CEST 2019
"""

import os
import re
import shutil

### Some function needed later
def cmake_parse_arguments(bool_keys:list, single_keys:list, multi_keys:list, \
    args:list) -> dict:
    """
    This function emulates the behavior of the CMake function
    cmake_parse_arguments().
    See: https://cmake.org/cmake/help/latest/command/cmake_parse_arguments.html
    There is no <prefix>, it is useless here.

    :param bool_keys: list of boolean parameters
    :param single_keys: list of single-valued parameters
    :param multi_keys: list of multi-valued parameters
    :param args: list of arguments given to a function
    :return: a dict containing the values of parameters

    >>> cmake_parse_arguments(["BOOL", "EMPTY"], ["SINGLE"], ["MULTI"], \
        ["abc", "BOOL", "SINGLE", "hey", "MULTI", "a", "b", "c"])
    {'unparsed': ['abc'], 'bools': ['BOOL'], 'SINGLE': 'hey', \
'MULTI': ['a', 'b', 'c']}
    """
    all_keys = []
    all_keys.extend(bool_keys)
    all_keys.extend(single_keys)
    all_keys.extend(multi_keys)
    return_dict = {"unparsed":[], "bools":[]}
    wait_single = None
    wait_multiple = None
    for arg in args:
        if(arg in bool_keys):
            return_dict["bools"].append(arg)
        elif(arg in single_keys):
            wait_single = arg
        elif(arg in multi_keys):
            wait_multiple = arg
            return_dict[wait_multiple] = []
        elif(wait_single):
            return_dict[wait_single] = arg
            wait_single = None
        elif(wait_multiple):
            return_dict[wait_multiple].append(arg)
        else:
            return_dict["unparsed"].append(arg)
    return return_dict

def convert_glob(expr:str, result_var:str = "files") -> str:
    """
    This function converts a globing expression to a call to file(GLOB)
    if needed.

    :param expr: a globing expression
    "param result_var: the name of the CMake variable to get the result.
    """
    return f"file(GLOB {result_var} {expr})" if '*' in expr else expr # TODO: do a ls with and without src/ at the beginning

def convert_lib_name(lib_name:str) -> str:
    """
    This function return the new name of lib_name.

    >>> convert_lib_name("PythonLibs")
    'Python::python'
    """
    if(lib_name == "PythonLibs"):
        return "Python::python"
    if(lib_name == "ROOT"): # TODO: do better
        return "ROOT::Core"
    if(lib_name == "Boost"):
        return "Boost::headers"
    if(lib_name in ("GaudiPluginService", "GaudiKernel", "GaudiUtilsLib",
    "GaudiAlgLib", "GaudiCommonSvcLib", "GaudiMPLib", "GaudiPythonLib",
    "RootCnvLib", "GaudiExamplesLib")):
        return "Gaudi::" + lib_name
    return lib_name

def topological_sort(graph):
    """
    This function perform a topological sort of a graph.

    :param graph: a dict, keys are nodes, graph[key] is a list of node that
        must precede key in the order.

    >>> topological_sort({'GaudiPluginService': [], 'GaudiPolicy': \
['GaudiPluginService'], 'GaudiKernel': ['GaudiPluginService'], \
'GaudiCoreSvc': ['GaudiKernel'], 'GaudiUtils': \
['GaudiKernel', 'GaudiCoreSvc'], 'GaudiExamples': ['GaudiUtils']})
    ['GaudiPluginService', 'GaudiPolicy', 'GaudiKernel', 'GaudiCoreSvc', \
'GaudiUtils', 'GaudiExamples']
    """
    seen = set()
    order = []
    for node in graph.keys():
        if node in seen:
            continue
        stack = [node]
        while stack:
            v = stack.pop()
            if v not in order:
                order.append(v)
                stack.extend(graph[v])
    return order

### Functions to modernize function calls
def modernize_gaudi_add_library(args:list) -> str:
    """
    This function modernize the call to gaudi_add_library.

    :param args: the list of parameters given to the old function
    :return: a string containing the call to the new function

    >>> modernize_gaudi_add_library(["GaudiMPLib", "src/Lib/*.cpp", \
"LINK_LIBRARIES", "GaudiKernel", "PythonLibs", "ROOT", "INCLUDE_DIRS", \
"ROOT", "PythonLibs"]) # "PUBLIC_HEADERS", "GaudiMP"
    'file(GLOB source0 src/Lib/*.cpp)\\n\
gaudi_add_library(GaudiMPLib SOURCES ${source0} LINK PUBLIC \
Gaudi::GaudiKernel Python::python ROOT::Core)\\n'
    """
    # return value initialization
    out = ""
    # first argument is the name of the library
    name = args.pop(0)
    arg_dict = cmake_parse_arguments(["NO_PUBLIC_HEADERS"], [], ["LIBRARIES", \
        "LINK_LIBRARIES", "INCLUDE_DIRS", "PUBLIC_HEADERS"], args)
    # extract sources (may contain glob)
    sources = []
    for i, glob in enumerate(arg_dict["unparsed"]):
        if('$' in glob): # is it the result of the expansion of a variable
            print(f"the sources of the shared library {name} are "
            "stored in {glob}. Not converted.", file=sys.stderr)
            continue
        sources.append("${source" + str(i) + "}")
        out += convert_glob(glob, "source" + str(i)) + '\n'
    # use them in the call to gaudi_add_library
    out += "gaudi_add_library(" + name + " SOURCES " + " ".join(sources)
    if("LINK_LIBRARIES" in arg_dict):
        out += " LINK PUBLIC"
        for lib in arg_dict["LINK_LIBRARIES"]:
            out += " " + convert_lib_name(lib)
    out += ")\n"
    if("INCLUDE_DIRS" in arg_dict):
        include_dirs = set(arg_dict["INCLUDE_DIRS"]) - \
            set(arg_dict["LINK_LIBRARIES"])
        if(include_dirs):
            out += "target_include_directories(" + \
                " ".join(include_dirs) + ")\n"
    if("PUBLIC_HEADERS" in arg_dict):
        include_dir = os.path.join(os.getcwd(), "include")
        os.mkdir(include_dir)
        for header in arg_dict["PUBLIC_HEADERS"]:
            shutil.move(header, include_dir)
    return out

# dict of the function to modernize function calls
modernize = {
    "find_package": None,
    "gaudi_subdir": None,
    "gaudi_add_library": modernize_gaudi_add_library,
    "gaudi_add_module": None,
    "gaudi_add_python_module": None,
    "gaudi_add_executable": None,
    "gaudi_add_test": None,
    "gaudi_add_unit_test": None,
    "gaudi_add_compile_test": None,
    "gaudi_add_dictionary": None,
    "gaudi_generate_confuserdb": None,
    "gaudi_env": None,
    "gaudi_depends_on_subdirs": None,
    "gaudi_install_headers": None,
    "gaudi_install_python_modules": None,
    "gaudi_install_scripts": None
}

def modernize_function_call(function_call:str):
    """
    This function transform a CMake function call from the old style
    to the new style.

    :param function_call: the function call stored in a string
        e.g. find_package(Boost REQUIRED COMPONENTS regex system)
    """
    name, args = function_call.split("(")
    name = name.lower()
    args = args.replace(")", "")
    new_function_call = ""
    if(name in modernize and modernize[name] is not None):
        new_function_call += modernize[name](args.split())
    else:
        new_function_call += function_call
        print(f"The function {name} has not yet a rule to be converted.")
    return new_function_call

def modernize_folder(folder:str):
    """
    This function read the content of the CMakeLists.txt
    file of a given folder and modernize it as much as possible.

    :param folder: path to the folder that contains a CMakeLists.txt
    """
    new_content = ""
    with open(os.path.join(folder, "CMakeLists.txt"), "r") as \
    cmake_lists:
        content = cmake_lists.read()
        # remove comments from the content
        content = re.sub(r'#\[(=+)\[.*?#\]\1\]', '', content, flags=re.DOTALL)
        content = re.sub(r'#.*\n', '', content)
        # strip spaces
        content = re.sub(r'\s+', " ", content)
        # extract all function call
        function_calls = re.findall(r'\w+\([^()]*\)', content)
        for call in function_calls:
            new_content += modernize_function_call(call)
    with open(os.path.join(folder, "CMakeLists.txt"), "w") as \
    cmake_lists:
        cmake_lists.write(new_content)

def main(top_level_folder:str =  os.getcwd()):
    """
    This function modernize a source tree.

    :param top_level_folder: path to the folder at the top of the source tree
        default value is the current working directory.
    """
    modernize_folder(top_level_folder)
    # TODO: write files like cmake/${PROJECT_NAME}Dependencies.cmake

if __name__ == "__main__":
    from sys import argv
    if(len(argv) == 2):
        main(argv[1])
    else:
        main()

FullStack CMakeLists.txt

SyntaxHighlightingPlugin: Language cmake is either undefined or unsupported. Check SyntaxHighlightingPlugin for more information.
#[========================================================================[.rst:
LHCb full stack
---------------

This file describe the build of a stack of LHCb projects.
The goal is to speed up the build of the stack by configuring and
compiling every sub-projects at once.
Build of the stack (without this file): configure Gaudi, compile Gaudi, (install
  Gaudi), configure LHCb, compile LHCb, (install LHCb), configure Lbcom...
Build of the stack (with this file): configure all the projects,
  compile all the projects (in parallel)

How to
^^^^^^

Assuming the repository has been cloned and the commands are run
at the top level.
First, set up the environment (set BINARY_TAG, set *PATH or source a view or use a toolchain)
Configure: ``cmake -S . -B build.$BINARY_TAG [-D CMAKE_TOOLCHAIN_FILE=path/to/a/toolchain]``
Build: ``cmake --build build.$BINARY_TAG -j `nproc` ``
Test: ``cd build.$BINARY_TAG ; ctest -j `nproc` ; cd ..``

Note that if you build the full stack at once, you cannot install nor package
any of the projects.

Options
^^^^^^^

* ``CLONE_WITH_KERBEROS``: if ON, the repositories will be cloned using Kerberos, standard https otherwise
* All options defined in the projects of the stack.

Ideas
^^^^^

* Use git submodules and remove ``FetchContent``

#]========================================================================]

cmake_minimum_required(VERSION 3.15)

project(LHCbFullStack
        LANGUAGES CXX
        DESCRIPTION "The full stack of LHCb projects starting from Gaudi")

# option to set the way ``git clone`` should be done
option(CLONE_WITH_KERBEROS "Use kerberos to clone the repositories instead of standard https" OFF)
if(CLONE_WITH_KERBEROS)
  set(gitlab_url ":@gitlab.cern.ch:8443")
else()
  set(gitlab_url "gitlab.cern.ch")
endif()

enable_testing()
# So that we can run all the tests of the stack at once

# Download the projects of the stack
include(FetchContent)
set(projects "Gaudi https://${gitlab_url}/gaudi/Gaudi.git"
             "LHCb https://${gitlab_url}/lhcb/LHCb.git")
foreach(project IN LISTS projects)
  separate_arguments(project)
  list(POP_BACK project project_url)
  # Download the sub-project only if it does not already exists
  if(NOT EXISTS ${PROJECT_SOURCE_DIR}/${project}/CMakeLists.txt)
    message(STATUS "Downloading ${project} from ${project_url}")
    FetchContent_Declare(${project}
                         GIT_REPOSITORY ${project_url}
                         GIT_TAG origin/master
                         GIT_PROGRESS TRUE
                         DOWNLOAD_DIR ${CMAKE_SOURCE_DIR}/${project}
                         SOURCE_DIR ${CMAKE_SOURCE_DIR}/${project}
                         BINARY_DIR ${CMAKE_BINARY_DIR}/${project})
    FetchContent_GetProperties(${project})
    string(TOLOWER "${project}" project_lc)
    if(NOT ${project_lc}_POPULATED)
      FetchContent_Populate(${project})
    endif()
  endif()
  # but always add its sources to the configuration
  message(STATUS "Adding ${project} to the build")
  add_subdirectory(${project})
endforeach()

-- ThomasCluzel - 2019-08-22

Edit | Attach | Watch | Print version | History: r3 < r2 < r1 | Backlinks | Raw View | WYSIWYG | More topic actions
Topic revision: r3 - 2019-08-23 - unknown
 
    • Cern Search Icon Cern Search
    • TWiki Search Icon TWiki Search
    • Google Search Icon Google Search

    LHCb All webs login

This site is powered by the TWiki collaboration platform Powered by PerlCopyright &© 2008-2023 by the contributing authors. All material on this collaboration platform is the property of the contributing authors.
or Ideas, requests, problems regarding TWiki? use Discourse or Send feedback