Making CMake and dictionaries work together in a shared library in 2023

I created a set of C++ classes and I would like to be able to access them both from C++ and python code (you know, RDataFrame and those). I would like (but am not forced to) build my project with CMake.

Expectation: create a shared library and happily load it with

gSystem->Load("../build/libMyTrigger.so")

Reality:

root [0] gSystem->Load("../build/libMyTrigger.so")
(int) 0
root [1] Trigger x
libMyTrigger dictionary payload:5:10: remark: could not acquire lock file for module 'MyTrigger': failed to create unique file /cvmfs/sft.cern.ch/lcg/releases/ROOT/6.26.04-edd28/x86_64-centos7-gcc11-opt/lib/MyTrigger.pcm.lock-28f012e8: Read-only file system [-Rmodule-build]
#include "/eos/user/m/myusername/MyTriggerCode/include/MyTrigger.h"
         ^
libMyTrigger dictionary payload:5:10: remark: building module 'MyTrigger' as '/cvmfs/sft.cern.ch/lcg/releases/ROOT/6.26.04-edd28/x86_64-centos7-gcc11-opt/lib/MyTrigger.pcm' [-Rmodule-build]
error: unable to open output file '/cvmfs/sft.cern.ch/lcg/releases/ROOT/6.26.04-edd28/x86_64-centos7-gcc11-opt/lib/MyTrigger.pcm': 'Read-only file system'
libMyTrigger dictionary payload:5:10: remark: finished building module 'MyTrigger' [-Rmodule-build]
libMyTrigger dictionary payload:5:10: fatal error: could not build module 'MyTrigger'
#include "/eos/user/m/myusername/MyTriggerCode/include/MyTrigger.h"
 ~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Error in <TInterpreter::AutoParse>: Error parsing payload code for class Trigger with content:

#line 1 "libMyTrigger dictionary payload"


#define _BACKWARD_BACKWARD_WARNING_H
// Inline headers
#include "/eos/user/m/myusername/MyTriggerCode/include/MyTrigger.h"
#include "/eos/user/m/myusername/MyTriggerCode/include/TriggerRunner.h"

#undef  _BACKWARD_BACKWARD_WARNING_H

ROOT_prompt_1:1:9: error: variable has incomplete type 'Trigger'
Trigger x
        ^
libMyTrigger dictionary forward declarations' payload:5:279: note: forward declaration of 'Trigger'
class __attribute__((annotate(R"ATTRDUMP(file_name@@@../include/MyTrigger.h)ATTRDUMP"))) __attribute__((annotate(R"ATTRDUMP(pattern@@@*)ATTRDUMP"))) __attribute__((annotate("$clingAutoload$/eos/user/m/myusername/MyTriggerCode/include/MyTrigger.h")))  Trigger;
 ~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Error in <TInterpreter::AutoParse>: Error parsing payload code for class Trigger with content:

#line 1 "libSLTrigger dictionary payload"


#define _BACKWARD_BACKWARD_WARNING_H
// Inline headers
#include "/eos/user/v/vippolit/SWAN_projects/ATLAS_L0_muon_barrel_trigger/include/SLTrigger.h"
#include "/eos/user/v/vippolit/SWAN_projects/ATLAS_L0_muon_barrel_trigger/include/TriggerRunner.h"

#undef  _BACKWARD_BACKWARD_WARNING_H

ROOT_prompt_1:1:9: error: variable has incomplete type 'Trigger'
Trigger x
        ^
libSLTrigger dictionary forward declarations' payload:5:279: note: forward declaration of 'Trigger'
class __attribute__((annotate(R"ATTRDUMP(file_name@@@../include/SLTrigger.h)ATTRDUMP"))) __attribute__((annotate(R"ATTRDUMP(pattern@@@*)ATTRDUMP"))) __attribute__((annotate("$clingAutoload$/eos/user/v/vippolit/SWAN_projects/ATLAS_L0_muon_barrel_trigger/include/SLTrigger.h")))  Trigger;

My question
Could you provide some basic (but complete) example of how CMakeLists.txt should look like? Mine looks like

set(MYSOURCES "${CMAKE_CURRENT_SOURCE_DIR}/source")
set(MYINCLUDE "${CMAKE_CURRENT_SOURCE_DIR}/include")

cmake_minimum_required(VERSION 3.0 FATAL_ERROR)
project(MyTrigger VERSION 0.1
                  DESCRIPTION "Implementation of some trigger code"
                  LANGUAGES CXX)
set(CMAKE_CXX_STANDARD
    11
    CACHE STRING "C++ standard to use")
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
set(CMAKE_PLATFORM_INDEPENDENT_CODE ON)

find_package(ROOT 6.24 CONFIG REQUIRED COMPONENTS RIO Net)
include_directories(${MYINCLUDE})

add_library(MyTrigger SHARED ${MYSOURCES}/MyTrigger.cxx ${MYSOURCES}/TriggerRunner.cxx ${MYINCLUDE}/MyTrigger.h ${MYINCLUDE}/TriggerRunner.h)
target_include_directories(MyTrigger PUBLIC ${MYINCLUDE})
target_link_libraries(MyTrigger PUBLIC ROOT::ROOTVecOps)
root_generate_dictionary(G__MyTrigger ${MYINCLUDE}/MyTrigger.h ${MYINCLUDE}/TriggerRunner.h MODULE MyTrigger LINKDEF ${MYSOURCES}/MyTriggerLinkDef.h)

while the LinkDef file (featured in many of my PhD nightmares) is

#ifdef __CLING__

#pragma link off all globals;
#pragma link off all classes;
#pragma link off all functions;
#pragma link C++ nestedclasses;
#pragma link C++ nestedtypedefs;

#pragma link C++ defined_in "../include/MyTrigger.h";
#pragma link C++ defined_in "../include/TriggerRunner.h";

#endif

Please read tips for efficient and successful posting and posting code

ROOT Version: 6.26/04
Platform: linuxx8664gcc
Compiler: g++ (GCC) 11.2.0


Can you try something like this:

project(MyTrigger VERSION 0.1
                  DESCRIPTION "Implementation of some trigger code"
                  LANGUAGES CXX)

find_package(ROOT REQUIRED)

set(CMAKE_CXX_FLAGS "${ROOT_CXX_FLAGS}")
set(MYSOURCES "${CMAKE_CURRENT_SOURCE_DIR}/source")
set(MYINCLUDE "${CMAKE_CURRENT_SOURCE_DIR}/include")

include(${ROOT_USE_FILE})
include_directories(${ROOT_INCLUDE_DIRS} ${CMAKE_SOURCE_DIR} ${MYINCLUDE})
link_directories(${ROOT_LIBRARY_DIR})

# create dictonary
ROOT_GENERATE_DICTIONARY(G__MyTrigger ${MYINCLUDE}/MyTrigger.h ${MYINCLUDE}/TriggerRunner.h MODULE MyTrigger LINKDEF ${MYSOURCES}/MyTriggerLinkDef.h)
add_library(MyTrigger SHARED ${MYSOURCES}/MyTrigger.cxx ${MYSOURCES}/TriggerRunner.cxx G__MyTrigger.cxx)
target_link_libraries(MyTrigger ${ROOT_LIBRARIES} ROOT::ROOTVecOps)

Just tried, but I get the same error…

Well, looking at the error you get, it doesn’t seem to be related to CMake. Or did I miss something?

libMyTrigger dictionary payload:5:10: remark: could not acquire lock file for module 'MyTrigger': failed to create unique file /cvmfs/sft.cern.ch/lcg/releases/ROOT/6.26.04-edd28/x86_64-centos7-gcc11-opt/lib/MyTrigger.pcm.lock-28f012e8: Read-only file system [-Rmodule-build]
#include "/eos/user/m/myusername/MyTriggerCode/include/MyTrigger.h"
         ^
libMyTrigger dictionary payload:5:10: remark: building module 'MyTrigger' as '/cvmfs/sft.cern.ch/lcg/releases/ROOT/6.26.04-edd28/x86_64-centos7-gcc11-opt/lib/MyTrigger.pcm' [-Rmodule-build]
error: unable to open output file '/cvmfs/sft.cern.ch/lcg/releases/ROOT/6.26.04-edd28/x86_64-centos7-gcc11-opt/lib/MyTrigger.pcm': 'Read-only file system'
libMyTrigger dictionary payload:5:10: remark: finished building module 'MyTrigger' [-Rmodule-build]
libMyTrigger dictionary payload:5:10: fatal error: could not build module 'MyTrigger'
#include "/eos/user/m/myusername/MyTriggerCode/include/MyTrigger.h"
 ~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

I.e. 'Read-only file system'

@bellenot I guess the question is … the “libMyTrigger.so” library has successfully been built and then also successfully loaded in an interactive ROOT session, so why does ROOT try to build the “MyTrigger” module and create a “${ROOTSYS}/lib/MyTrigger.pcm” file?

@rene Do you define the “Trigger” class in one of your include files? If yes, try the hard way:

#pragma link C++ defined_in "/eos/user/m/myusername/MyTriggerCode/include/MyTrigger.h";
#pragma link C++ defined_in "/eos/user/m/myusername/MyTriggerCode/include/TriggerRunner.h";

If not, you should also add (before the two above lines): #pragma link C++ class Trigger+;

Oh, OK, then I don’t know what triggers the build of the module… Maybe @Axel has an idea?

same error also with those pragma directives…

Attach “MyTrigger.h” and “TriggerRunner.h” for inspection.

MyTrigger.h (93 Bytes)
TriggerRunner.h (125 Bytes)

I attach a full, simplified reproducer in the zipfile, to be executed as

ssh lxplus.cern.ch
bash
mkdir -p myfolder
cd myfolder
unzip mytest.zip
mkdir -p build
source setup.sh
cd build
cmake ..
make
root -l

and then

gSystem->Load("../build/libMyTrigger.so");
Trigger x

mytest.zip (3.2 KB)

Coming here via the service now ticket…
Thanks for the reproducer!

If you add before starting root -l

export LD_LIBRARY_PATH=$PWD:$LD_LIBRARY_PATH   # the folder the library is in so this should be path/to/build
root -l
gSystem->Load("libMyTrigger.so");
Trigger x

Gives me errors about the default c’tor not being available,

root [0] gSystem->Load("libSLTrigger.so");
root [1] Trigger x
ROOT_prompt_1:1:9: error: no matching constructor for initialization of 'Trigger'
Trigger x
        ^
/home/sailer/temp/ippolito/atlas_l0_muon_barrel_trigger/include/SLTrigger.h:8:7: note: candidate constructor (the implicit copy constructor) not viable: requires 1 argument, but 0 were provided
class Trigger {
      ^
/home/sailer/temp/ippolito/atlas_l0_muon_barrel_trigger/include/SLTrigger.h:8:7: note: candidate constructor (the implicit move constructor) not viable: requires 1 argument, but 0 were provided
/home/sailer/temp/ippolito/atlas_l0_muon_barrel_trigger/include/SLTrigger.h:44:4: note: candidate constructor not viable: requires 2 arguments, but 0 were provided
   Trigger(PatternWindow &windows_eta, PatternWindow &windows_phi);
   ^

Which seems to be correct, because there is no Trigger() constructor, just one with two arguments?

Not sure if the rootmap file is matched exactly with the string used in gSystem->Load or why this works better or if the relative path is supposed to work? Back to the root team for that :slight_smile:

Cheers,
Andre

With the reproducer above, where the class does have default argument-less constructors, the issue remains even after updating LD_LIBRARY_PATH.

Ok, you also have to run root from outside the build directory

cd ..
root -l
root [0] gSystem->Load("libMyTrigger.so")
(int) 0                                                                                                                                                                                                                                       
root [1] Trigger x
(Trigger &) @0x7f5a74c7f010                                                                                                                                                                                                                   

I can confirm it.

@Axel The problem appears when one starts “root” in the “build” directory, but not when it’s started elsewhere. Any idea why it breaks?

Note: one does NOT need to modify the “LD_LIBRARY_PATH” at all.

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.