I’m working on a complex cmake-based project, with multiple programs distributed over different directories. Each program is in its own directory, as is the ROOT dictionary directory: GramsDataObj
(the project’s name is GramsSim for an experiment called GRAMS).
I’ve gotten everything to work, from LinkDefs to TTrees (getting operator<<
to work was a huge pain), except for one annoying thing: I have to include a statement like:
gSystem->Load("GramsDataObj/libGramsSimProjectDataObj.so")
in order for a program to use the dictionary. I’d rather not hard-code this statement in every one of the programs that has to access the dictionary; I’d prefer for the shared library to be linked with the program during compilation.
I know it’s possible to do this. Before embarking on this project, I set up a kiddie-level test suite. The relevant files for this question are:
STL_TrackList.h = my simple classes
STL_LinkDef.h = the LinkDef file
STLntupleCreateDict.cxx = a program that uses the dictionary
I was able to compile, link, and execute the programs with:
rootcling -f STLntupleDict.cxx STL_TrackList.h STL_LinkDef.h
g++ `root-config --cflags --libs` -shared STLntupleDict.cxx -o STLntupleDict.so -fPIC
g++ `root-config --cflags --libs` STLntupleCreateDict.cxx ./STLntupleDict.so -o STLntupleCreateDict
With this, I was able to run STLntupleCreateDict
without needing to use gSystem->Load
within the program code. So I know it’s possible.
However, I can’t recreate this behavior in the bigger project, in which I’m using cmake. In the data-object directory GramsDataObj, I have the following lines in my CMakeLists.txt
:
# My header files:
file(GLOB headerList ${CMAKE_CURRENT_SOURCE_DIR}/include/*.h)
# The source code to compile
file(GLOB DataObjSrc src/*.cc
${CMAKE_CURRENT_SOURCE_DIR}/src/*.cc
)
# To avoid typing:
set(LinkDef ${CMAKE_CURRENT_SOURCE_DIR}/include/LinkDef.hh)
set(Dictionary ${CMAKE_CURRENT_BINARY_DIR}/DataObjDict)
set(LibName ${CMAKE_PROJECT_NAME}DataObj)
# Create the dictionary
ROOT_GENERATE_DICTIONARY ( ${Dictionary} ${headerList} LINKDEF ${LinkDef} )
# Create the dictionary shared library:
add_library (${LibName} SHARED ${Dictionary}.cxx ${DataObjSrc})
The effect of these commands is to create the files I’d expect in my build directory:
# ls GramsDataObj
CMakeFiles cmake_install.cmake DataObjDict.cxx DataObjDict_rdict.pcm libGramsSimProjectDataObj.so Makefile
In the CMakeLists.txt
for one of the programs that’s going to use the dictionary, I have (among many other lines):
target_link_libraries(${PROG} ${CMAKE_PROJECT_NAME}DataObj )
target_link_libraries(${PROG} ${ROOT_LIBRARIES} )
It seems clear enough: In the dictionary directory, I create dictionary.so
file, and in the program directory I link that .so
file into the program.
In fact, when I hunt through the myriad work files that cmake creates, I can see ../GramsDataObj/libGramsSimProjectDataObj.so
in the list of libraries to be linked (along with, for example, -lRIO
and dozens of others).
But if I don’t have the gSystem->Load
line in the program’s code, I get error messages that are consistent with the dictionary not being found:
Error in <TTree::Branch>: The pointer specified for EventID is not of a class known to ROOT
Error in <TTree::Branch>: The class requested (map<int,grams::MCTrack>) for the branch "TrackList" is an instance of an stl collection and does not have a compiled CollectionProxy. Please generate the dictionary for this collection (map<int,grams::MCTrack>) to avoid to write corrupted data.
If the gSystem->Load
is present, the program runs without problems.
What am I missing?
Note: There is no MODULE option in my ROOT_GENERATE_DICTIONARY. That’s because I don’t want the dictionary to be created just for a specific executable; I just want the shared library to be compiled, then linked into the programs in other directories. If a MODULE option can be used to specify something other than a target executable, please let me know… at least, if it will help in this case.
ROOT Version: 6.24/06
Platform: AlmaLinux 9
Compiler: 9.5.0