diff --git a/chapters/packages/ROOT.md b/chapters/packages/ROOT.md index 2755b32..888ec5e 100644 --- a/chapters/packages/ROOT.md +++ b/chapters/packages/ROOT.md @@ -1,8 +1,9 @@ + # ROOT ROOT is a C++ Toolkit for High Energy Physics. It is huge. There are really a lot of ways to use it in CMake, though many/most of the examples you'll find are probably wrong. Here's my recommendation. -Most importantly, there are lots of improvements in CMake in more recent versions of ROOT - try to use 6.14+! +Most importantly, there are *lots of improvements* in CMake support in more recent versions of ROOT - Using 6.16+ is much, much easier! If you really must support 6.14 or earlier, see the section at the end. ## Finding ROOT @@ -14,7 +15,7 @@ to attempt to find ROOT. If you don't have your paths set up, you can pass `-DRO ## The too-simple way -ROOT [provides a utility](https://root.cern.ch/how/integrate-root-my-project-cmake) to set up a ROOT project, which you can activate using `include("${ROOT_USE_FILE}")`. This will automatically make ugly directory level and global variables for you. It will save you a little time setting up, and will waste massive amounts of time later if you try to do anything tricky. As long as you aren't making a library, it's probably fine for simple scripts. Includes and flags are set globally, but you'll still need to link to `${ROOT_LIBRARIES}` yourself, along with possibly `ROOT_EXE_LINKER_FLAGS` (You will have to `separate_arguments` first before linking or you will get an error if there are multiple flags, like on macOS). +ROOT [provides a utility](https://root.cern.ch/how/integrate-root-my-project-cmake) to set up a ROOT project, which you can activate using `include("${ROOT_USE_FILE}")`. This will automatically make ugly directory level and global variables for you. It will save you a little time setting up, and will waste massive amounts of time later if you try to do anything tricky. As long as you aren't making a library, it's probably fine for simple scripts. Includes and flags are set globally, but you'll still need to link to `${ROOT_LIBRARIES}` yourself, along with possibly `ROOT_EXE_LINKER_FLAGS` (You will have to `separate_arguments` first before linking or you will get an error if there are multiple flags, like on macOS). Also, before 6.16, you have to manually fix a bug in the spacing. Here's what it would look like: @@ -22,27 +23,17 @@ Here's what it would look like: ## The right way (Targets) -ROOT 6.12 and earlier do not add the include directory for imported targets. ROOT 6.14+ has corrected this error. To fix this error for older ROOT versions, you'll need something like: - -[import:'setup_includes', lang:'cmake'](../../examples/root-simple/CMakeLists.txt) - -You will also often want the compile flags and definitions: - -[import:'setup_flags', lang:'cmake'](../../examples/root-simple/CMakeLists.txt) - -In CMake 3.11, you can replace that last function call with: - -[import:'modern_fix', lang:'cmake'](../../examples/root-simple-3.11/CMakeLists.txt) - -All the ROOT targets will require `ROOT::Core`, so this will be enough regardless of which ROOT targets you need. +ROOT 6.12 and earlier do not add the include directory for imported targets. ROOT 6.14+ has corrected this error, and required target properties have been getting better. To link, just pick the libraries you want to use: [import:'add_and_link', lang:'cmake'](../../examples/root-simple/CMakeLists.txt) +If you'd like to see the default list, run `root-config --libs` on the command line. + ## Components -Find ROOT allows you to specify components. It will add anything you list to ${ROOT_LIBRARIES}, so you might want to build your own target using that to avoid listing the components twice. This does not solve dependencies though; so it is an error to list `RooFit` but not `RooFitCore`. If you link to `ROOT::RooFit` instead of `${ROOT_LIBRARIES}`, then `RooFitCore` is not required. +Find ROOT allows you to specify components. It will add anything you list to `${ROOT_LIBRARIES}`, so you might want to build your own target using that to avoid listing the components twice. This did not solve dependencies; it was an error to list `RooFit` but not `RooFitCore`. If you link to `ROOT::RooFit` instead of `${ROOT_LIBRARIES}`, then `RooFitCore` is not required. ## Dictionary generation @@ -58,7 +49,10 @@ To generate, you should include the following in your CMakeLists: ```cmake include("${ROOT_DIR}/modules/RootNewMacros.cmake") -include_directories(ROOT_BUG) + +# Uncomment for ROOT versions than 6.16 +# They break if nothing is in the global include list! +# include_directories(ROOT_BUG) ``` The second line is due to a bug in the NewMacros file that causes dictionary generation to fail if there is not at least one global include directory or a `inc` folder. Here I'm including a non-existent directory just to make it work. There is no `ROOT_BUG` directory. @@ -79,3 +73,42 @@ The final two output files must sit next to the library output. This is done by [linkdef-root]: https://root.cern.ch/selecting-dictionary-entries-linkdefh +--- + +# Using Old ROOT + +If you really have to use older ROOT, you'll need something like this: + +```cmake +# ROOT targets are missing includes and flags in ROOT 6.10 and 6.12 +set_property(TARGET ROOT::Core PROPERTY + INTERFACE_INCLUDE_DIRECTORIES "${ROOT_INCLUDE_DIRS}") + +# Early ROOT does not include the flags required on targets +add_library(ROOT::Flags_CXX IMPORTED INTERFACE) + + +# ROOT 6.14 and earlier have a spacing bug in the linker flags +string(REPLACE "-L " "-L" ROOT_EXE_LINKER_FLAGS "${ROOT_EXE_LINKER_FLAGS}") + +# Fix for ROOT_CXX_FLAGS not actually being a CMake list +separate_arguments(ROOT_CXX_FLAGS) +set_property(TARGET ROOT::Flags_CXX APPEND PROPERTY + INTERFACE_COMPILE_OPTIONS ${ROOT_CXX_FLAGS}) + +# Add definitions +separate_arguments(ROOT_DEFINITIONS) +foreach(_flag ${ROOT_EXE_LINKER_FLAG_LIST}) + # Remove -D or /D if present + string(REGEX REPLACE [=[^[-//]D]=] "" _flag ${_flag}) + set_property(TARGET ROOT::Flags APPEND PROPERTY INTERFACE_LINK_LIBRARIES ${_flag}) +endforeach() + +# This also fixes a bug in the linker flags +separate_arguments(ROOT_EXE_LINKER_FLAGS) +set_property(TARGET ROOT::Flags_CXX APPEND PROPERTY + INTERFACE_LINK_LIBRARIES ${ROOT_EXE_LINKER_FLAGS}) + +# Make sure you link with ROOT::Flags_CXX too! +``` + diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 50192a7..3809818 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -10,5 +10,4 @@ add_subdirectory(extended-project) add_subdirectory(root-usefile) add_subdirectory(root-simple) -add_subdirectory(root-simple-3.11) add_subdirectory(root-dict) diff --git a/examples/root-dict/CMakeLists.txt b/examples/root-dict/CMakeLists.txt index 97a9127..fe3ab82 100644 --- a/examples/root-dict/CMakeLists.txt +++ b/examples/root-dict/CMakeLists.txt @@ -1,6 +1,6 @@ ## [main] -cmake_minimum_required(VERSION 3.4) +cmake_minimum_required(VERSION 3.4...3.15) project(RootDictExample LANGUAGES CXX) @@ -9,21 +9,14 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_EXTENSIONS OFF) set(CMAKE_PLATFORM_INDEPENDENT_CODE ON) -find_package(Threads) - find_package(ROOT CONFIG REQUIRED) include("${ROOT_DIR}/modules/RootNewMacros.cmake") -# We will set the flags ourselves -set_target_properties(ROOT::Core PROPERTIES - INTERFACE_INCLUDE_DIRECTORIES "${ROOT_INCLUDE_DIRS}") - -include_directories(ROOT_BUG) root_generate_dictionary(G__DictExample DictExample.h LINKDEF DictLinkDef.h) add_library(DictExample SHARED DictExample.cxx DictExample.h G__DictExample.cxx) target_include_directories(DictExample PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}") -target_link_libraries(DictExample PUBLIC ROOT::Core Threads::Threads) +target_link_libraries(DictExample PUBLIC ROOT::Core) ## [main] diff --git a/examples/root-simple-3.11/CMakeLists.txt b/examples/root-simple-3.11/CMakeLists.txt deleted file mode 100644 index cdb3244..0000000 --- a/examples/root-simple-3.11/CMakeLists.txt +++ /dev/null @@ -1,34 +0,0 @@ - -## [main] -cmake_minimum_required(VERSION 3.11) - -project(RootSimpleExample311 LANGUAGES CXX) - -find_package(ROOT CONFIG REQUIRED) - -## [modern_fix] -target_include_directories(ROOT::Core INTERFACE "${ROOT_INCLUDE_DIRS}") - -add_library(ROOT::Flags_CXX IMPORTED INTERFACE) - -separate_arguments(ROOT_CXX_FLAGS) -target_compile_options(ROOT::Flags_CXX INTERFACE ${ROOT_CXX_FLAGS}) - -separate_arguments(ROOT_DEFINITIONS) -target_compile_definitions(ROOT::Flags_CXX INTERFACE ${ROOT_DEFINITIONS}) - -# This fixes a bug in the linker flags -string(REPLACE "-L " "-L" ROOT_EXE_LINKER_FLAGS "${ROOT_EXE_LINKER_FLAGS}") -separate_arguments(ROOT_EXE_LINKER_FLAGS) -# Stuck into using old property method due to separate -L and -l arguments -# (A full path to -l is better!) -set_property(TARGET ROOT::Flags_CXX PROPERTY - INTERFACE_LINK_LIBRARIES ${ROOT_EXE_LINKER_FLAGS}) -## [modern_fix] - -add_executable(RootSimpleExample311 SimpleExample.cxx) -target_link_libraries(RootSimpleExample311 PUBLIC ROOT::Physics ROOT::Flags_CXX) -## [main] - -enable_testing() -add_test(NAME RootSimpleExample311 COMMAND RootSimpleExample311) diff --git a/examples/root-simple-3.11/README.md b/examples/root-simple-3.11/README.md deleted file mode 100644 index 9e34fbc..0000000 --- a/examples/root-simple-3.11/README.md +++ /dev/null @@ -1,11 +0,0 @@ -# A Simple ROOT Project (CMake 3.11) - -This is a minimal example of a ROOT project using the target system and without a dictionary. -This uses the new support in CMake 3.11+ for adding interface properties on an imported interface target. - -#### examples/root-simple-3.11/CMakeLists.txt -[import:'main', lang:'cmake'](CMakeLists.txt) - -#### examples/root-simple-3.11/SimpleExample.cxx - -[import](SimpleExample.cxx) diff --git a/examples/root-simple-3.11/SimpleExample.cxx b/examples/root-simple-3.11/SimpleExample.cxx deleted file mode 100644 index 9534202..0000000 --- a/examples/root-simple-3.11/SimpleExample.cxx +++ /dev/null @@ -1,7 +0,0 @@ -#include - -int main() { - TLorentzVector v(1,2,3,4); - v.Print(); - return 0; -} diff --git a/examples/root-simple/CMakeLists.txt b/examples/root-simple/CMakeLists.txt index fc90276..1bae685 100644 --- a/examples/root-simple/CMakeLists.txt +++ b/examples/root-simple/CMakeLists.txt @@ -1,46 +1,19 @@ ## [main] -cmake_minimum_required(VERSION 3.1) +cmake_minimum_required(VERSION 3.1...3.15) project(RootSimpleExample LANGUAGES CXX) +# Finding the ROOT package ## [find_package] -find_package(ROOT CONFIG REQUIRED) +find_package(ROOT 6.16 CONFIG REQUIRED) ## [find_package] -# ROOT targets are missing includes and flags -## [setup_includes] -set_property(TARGET ROOT::Core PROPERTY - INTERFACE_INCLUDE_DIRECTORIES "${ROOT_INCLUDE_DIRS}") -## [setup_includes] -## [setup_flags] -add_library(ROOT::Flags_CXX IMPORTED INTERFACE) - -# Fix for ROOT_CXX_FLAGS not actually being a CMake list -separate_arguments(ROOT_CXX_FLAGS) -set_property(TARGET ROOT::Flags_CXX APPEND PROPERTY - INTERFACE_COMPILE_OPTIONS ${ROOT_CXX_FLAGS}) - -# Add definitions -separate_arguments(ROOT_DEFINITIONS) -foreach(_flag ${ROOT_EXE_LINKER_FLAG_LIST}) - # Remove -D or /D if present - string(REGEX REPLACE [=[^[-//]D]=] "" _flag ${_flag}) - set_property(TARGET ROOT::Flags APPEND PROPERTY INTERFACE_LINK_LIBRARIES ${_flag}) -endforeach() - -# This also fixes a bug in the linker flags -string(REPLACE "-L " "-L" ROOT_EXE_LINKER_FLAGS "${ROOT_EXE_LINKER_FLAGS}") -separate_arguments(ROOT_EXE_LINKER_FLAGS) -set_property(TARGET ROOT::Flags_CXX APPEND PROPERTY - INTERFACE_LINK_LIBRARIES ${ROOT_EXE_LINKER_FLAGS}) -## [setup_flags] - -# Adding an exectuable program and linking to needed ROOT libraries +# Adding an executable program and linking to needed ROOT libraries ## [add_and_link] add_executable(RootSimpleExample SimpleExample.cxx) -target_link_libraries(RootSimpleExample PUBLIC ROOT::Physics ROOT::Flags_CXX) +target_link_libraries(RootSimpleExample PUBLIC ROOT::Physics) ## [add_and_link] ## [main] diff --git a/examples/root-usefile/CMakeLists.txt b/examples/root-usefile/CMakeLists.txt index df68839..63bbe8c 100644 --- a/examples/root-usefile/CMakeLists.txt +++ b/examples/root-usefile/CMakeLists.txt @@ -1,16 +1,18 @@ ## [main] -cmake_minimum_required(VERSION 3.1) +cmake_minimum_required(VERSION 3.1...3.15) project(RootUseFileExample LANGUAGES CXX) -find_package(ROOT CONFIG REQUIRED) +find_package(ROOT 6.16 CONFIG REQUIRED) +# Sets up global settings include("${ROOT_USE_FILE}") +# This is required for ROOT < 6.16 +# string(REPLACE "-L " "-L" ROOT_EXE_LINKER_FLAGS "${ROOT_EXE_LINKER_FLAGS}") + # This is required on if there is more than one flag (like on macOS) -# and this also fixes a bug in the linker flags -string(REPLACE "-L " "-L" ROOT_EXE_LINKER_FLAGS "${ROOT_EXE_LINKER_FLAGS}") separate_arguments(ROOT_EXE_LINKER_FLAGS) add_executable(RootUseFileExample SimpleExample.cxx)