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
@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+;
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