CMake library does not load in root macro


Hi,

I have been trying to use ROOT in a CMake project and have come up against an issue. I have a group of custom classes in .cxx files with associated header .h files. Now I want to create a shared dynamic dictionary, that I can then load into my .C root macros to access types and functions within the library without having to recompile the library every time I change something trivial in the root macro (i.e. the color of a histogram).

I’ve been reading up online about how to use CMake to do this, and my understanding is that my CMakeLists file needs to first use root_generate_dictionary to create the dictionary with the header files and the linkdef file, and then add_library (with the SHARED flag) to create the .so file. Then target_link_libraries is used to link the library to ${ROOT_LIBRARIES}.

I’ve followed all of these steps, and have obtained the .so file. However, when I tried to use it in the macro using

gSystem->Load(path/lib.so)

I got a cling dynamic library manager error saying “undefined symbol”. After some digging, I learnt about mangling in c++ and realized the “undefined symbol” being referenced was a mangled function name. I experimented and managed to successfully load the library and even include one of my dictionary header files using

extern "C" {
gSystem->Load(path/lib.so)
#include "path/Name.h"
}

However, now when I try to actually call and initialize an object of the custom type within a void method in the macro, I once again get an error message about a symbol that I believe is coming from this mangling. The error is:

IncrementalExecutor::executeFunction: symbol ‘_ZN16NameC1Ev’ unresolved while linking [cling interface function]!
You are probably missing the definition of Name::Name()
Maybe you need to load the corresponding shared library?

I am now at a loss of how to proceed, and any help would be greatly appreciated. I am very new to CMake and building projects generally, I am going entirely off of the Modern CMake and More Modern CMake tutorials, and the ROOT documentation. Is there an issue in how I am generating the library and dictionary that could avoid it getting mangled in the first place, or is there something further I need to do in the macro to access it? Thanks in advance for the help.

ROOT Version: 6.34
Platform: CentOS8, running ROOT in a Singularity container based on the ROOT docker container
Compiler: CMake


I guess @bellenot can help you with cmake.

Can you share your CMakeLists.txt and a minimal reproducer?

Hi there, thanks for the help. I am trying to attach the files but it is saying that as a new user I cannot attach in the post, is there any way around this?

You should be able to upload now. But before that, can you try to add this line in your CMakeLists.txt:

set_target_properties(libname PROPERTIES ENABLE_EXPORTS 1)

And replace libname by your target name

Unfortunately adding the line did not work, and I still get the same issue. I’ve attached a zip file with my real CMakeLists file (here called “real_cmakelists.txt” to avoid confusion with the minimal one). The CMakeLists and LinkDef are stripped down versions of the real ones I am using, here with only one file added to the library and dictionary instead of all of them. There are also the relevant .cxx and .h files and a minimal test.C macro that creates the problem on my side. Sorry for my inexperience, I’m not quite sure what you need for a minimal reproducer, I hope this is sufficient.

upload.zip (14.4 KB)

So here is the modified CMakeLists.txt (basically adding G__GGFW.cxx in add_library(libGGFW ...):

cmake_minimum_required(VERSION 3.0 FATAL_ERROR)
project(GGFW LANGUAGES CXX)

list (APPEND CMAKE_PREFIX_PATH $ENV{ROOTSYS})

find_package(ROOT 6.16 CONFIG REQUIRED)

include(${ROOT_USE_FILE})
set(CMAKE_CXX_FLAGS "${ROOT_CXX_FLAGS}")
include_directories(${ROOT_INCLUDE_DIRS} ${CMAKE_CURRENT_SOURCE_DIR})

ROOT_GENERATE_DICTIONARY(G__GGFW BootstrapProfile.h LINKDEF GGFWLinkDef.h) 

add_library(libGGFW SHARED G__GGFW.cxx BootstrapProfile.cxx)
set_target_properties(libGGFW PROPERTIES ENABLE_EXPORTS 1)
target_include_directories(libGGFW PUBLIC ${CMAKE_SOURCE_DIR})
target_link_libraries(libGGFW PUBLIC ${ROOT_LIBRARIES})

And modified test.C:

#include "BootstrapProfile.h"
#include <iostream>

void test()
{ 
   gSystem->Load("libGGFW");
   BootstrapProfile *bs = new BootstrapProfile;
   std::cout << "testing" << std::endl;
   return;
}

And the output, after copying libGGFW.dll and libGGFW_rdict.pcm in the source directory:

C:\root-dev\rootdev\nnathans>root -l test.C
root [0]
Processing test.C...
testing
root [1] .q

FYI, as you can see I tested on Windows, so let me know if something doesn’t work for you or if something is not clear

Just tried to implement, unfortunately I am still getting the same error - the one from before I added the extern “C”:

cling::DynamicLibraryManager::loadLibrary(): path/[liblibGGFW.so](http://liblibggfw.so/): undefined symbol: _ZN16BootstrapProfile8StreamerER7TBuffer
Error in <AutoloadLibraryMU>: Failed to load library path/liblibGGFW.socling JIT session error: Failed to materialize symbols: { (<Process Symbols>, { _ZN16BootstrapProfileC1Ev }) }

(I don’t see a .dll file after building, but I guess the .so file is equivalent? I copied both the .so and the .pcm file into my source directory after building.)

I see an extra lib (liblibGGFW.so) can you try again from scratch and with this version:

cmake_minimum_required(VERSION 3.0 FATAL_ERROR)
project(GGFW LANGUAGES CXX)

list (APPEND CMAKE_PREFIX_PATH $ENV{ROOTSYS})

find_package(ROOT 6.16 CONFIG REQUIRED)

include(${ROOT_USE_FILE})
set(CMAKE_CXX_FLAGS "${ROOT_CXX_FLAGS}")
include_directories(${ROOT_INCLUDE_DIRS} ${CMAKE_CURRENT_SOURCE_DIR})

ROOT_GENERATE_DICTIONARY(G__GGFW BootstrapProfile.h LINKDEF GGFWLinkDef.h) 

add_library(GGFW SHARED G__GGFW.cxx BootstrapProfile.cxx)
set_target_properties(GGFW PROPERTIES ENABLE_EXPORTS 1)
target_include_directories(GGFW PUBLIC ${CMAKE_SOURCE_DIR})
target_link_libraries(GGFW PUBLIC ${ROOT_LIBRARIES})

Still no change with that version - I had noticed the extra lib too, so I was loading with

gSystem->Load("liblibGGFW");

The error is consistent either way.

Well, that works for me on Ubuntu:

ubuntu@root-cmake-devel:~/rootdev/forum/nnathans/build$ cmake ../
CMake Deprecation Warning at CMakeLists.txt:1 (cmake_minimum_required):
  Compatibility with CMake < 3.5 will be removed from a future version of
  CMake.

  Update the VERSION argument <min> value or use a ...<max> suffix to tell
  CMake that the project does not need compatibility with older versions.


-- The CXX compiler identification is GNU 9.4.0
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Check for working CXX compiler: /usr/bin/c++ - skipped
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Found Vdt: /home/ubuntu/build/master/include (found version "0.4")
-- Configuring done (0.2s)
-- Generating done (0.0s)
-- Build files have been written to: /home/ubuntu/rootdev/forum/nnathans/build
ubuntu@root-cmake-devel:~/rootdev/forum/nnathans/build$ make -j3
[ 25%] Generating G__GGFW.cxx, libGGFW_rdict.pcm, libGGFW.rootmap
[ 75%] Building CXX object CMakeFiles/GGFW.dir/G__GGFW.cxx.o
[ 75%] Building CXX object CMakeFiles/GGFW.dir/BootstrapProfile.cxx.o
[100%] Linking CXX shared library libGGFW.so
[100%] Built target GGFW
ubuntu@root-cmake-devel:~/rootdev/forum/nnathans/build$ cp libGGFW* ../
ubuntu@root-cmake-devel:~/rootdev/forum/nnathans/build$ cd ..
ubuntu@root-cmake-devel:~/rootdev/forum/nnathans$ root -l test.C
root [0]
Processing test.C...
testing
root [1]

with:

ubuntu@root-cmake-devel:~/rootdev/forum/nnathans$ more CMakeLists.txt
cmake_minimum_required(VERSION 3.0 FATAL_ERROR)
project(GGFW LANGUAGES CXX)

list (APPEND CMAKE_PREFIX_PATH $ENV{ROOTSYS})

find_package(ROOT 6.16 CONFIG REQUIRED)

include(${ROOT_USE_FILE})
set(CMAKE_CXX_FLAGS "${ROOT_CXX_FLAGS}")
include_directories(${ROOT_INCLUDE_DIRS} ${CMAKE_CURRENT_SOURCE_DIR})

ROOT_GENERATE_DICTIONARY(G__GGFW BootstrapProfile.h LINKDEF GGFWLinkDef.h)

add_library(GGFW SHARED G__GGFW.cxx BootstrapProfile.cxx)
set_target_properties(GGFW PROPERTIES ENABLE_EXPORTS 1)
target_include_directories(GGFW PUBLIC ${CMAKE_SOURCE_DIR})
target_link_libraries(GGFW PUBLIC ${ROOT_LIBRARIES})
ubuntu@root-cmake-devel:~/rootdev/forum/nnathans$ more test.C
#include "BootstrapProfile.h"
#include <iostream>

void test()
{
   gSystem->Load("libGGFW");
   BootstrapProfile* bs = new BootstrapProfile;
   std::cout<<"testing"<<std::endl;
   return;
}

It turned out I had made some changes to the LinkDef file this morning while I was testing things and had simply forgotten to change it back. Fixing this meant that the new CMakeLists solved the problem, both for the small test library and for the larger one. Thank you so much for your help!

1 Like

Glad to read the problem is fixed! And you’re very welcome!

1 Like