Generating a Shared Library (.so) out of a lot of a lot of .cpp and .hpp files via ROOT?

Greetings,

I would like to create a shared library for several .hpp and .cpp files that I have created. Typically, when I want to use a class (say, that I have a class defined a class called Event in the files Event.hpp and Event.cpp), I do something like this,

user@domain [~] ➤➤ cd include/
user@domain [~/include] ➤➤ ls
Event.cpp  Event.hpp
user@domain [~/include] ➤➤ root -l
root [0] .L Event.cpp+
Info in <TUnixSystem::ACLiC>: creating shared library /home/user/include/./Event_cpp.so
root [1] .q

After that, I end up with a *.pcm, *.d files and my library *.so. and if I want to use them in some macro, I create a rootlogon.C file on my working directory with the command,

{
     gSystem->Load(~/include/Event_cpp.so);
}

I am also include the class with the #include preprocessor directive on my macro.
I was wondering if there is any way to compile a shared library for multiple different classes (with their corresponding files .hpp and .cpp) via root and in similar style as presented above. There are some resources online on how to do this but with g++ compiler. I am aware that I can perhaps do achieve something with g++ on root by using the root-config executable (sets the appropriate dependencies for us), but i was wondering if there is an easier way to do this.

Many Thanks,

Andy


_ROOT Version: 6.24/06
Platform: Centos8
Compiler: g++ 11


Hi @andrew ,
I don’t know how to do this from the ROOT prompt (@Axel , @pcanal maybe ?) – but you can certainly produce a shared library as it is usually done in C++ (e.g. g++ -fPIC -shared -o libmylibrary.so source1.cpp source2.cpp source3.cpp $(root-config --libs --cflags)) and then load this library (and the corresponding headers) in the ROOT interpreter as usual. The process is demonstrated e.g. at Python interface: PyROOT - ROOT .

Cheers,
Enrico

Hi @eguiraud,

Thank you a lot for your reply. The g++ method worked and I am getting a single shared library out of this. However, when I load it into my main executable, say test.cpp (I have made sure that I have included all header files that I am using and loaded all libraries via gSystem->Load()…), and execute, via:

root -x test.cpp

I am getting something like:

/**
 * @file
 * @brief Definition of Monte-Carlo track object
 * @copyright Copyright (c) 2018-2020 CERN and the Allpix Squared authors.
 * This software is distributed under the terms of the MIT License, copied verbatim in the file "LICENSE.md".
 * In applying this license, CERN does not waive the privileges and immunities granted to it by virtue of its status as an
 * Intergovernmental Organization or submit itself to any jurisdiction.
 */

#ifndef ALLPIX_MC_TRACK_H
#define ALLPIX_MC_TRACK_H

#include <Math/Point3D.h>
#include <TRef.h>

#include "Object.hpp"

namespace allpix {
    /**
     * @brief Monte-Carlo track through the world
     */
    class MCTrack : public Object {
    public:
        /**
         * @brief Construct a Monte-Carlo track
         * @param start_point Global point where track came into existence
         * @param end_point Global point where track went out of existence
         * @param g4_volume Geant4 volume where track originated in
         * @param g4_prod_process_name Geant4 creation process name

… and so on (the output is just to big to include). Bare in mind, that I am using libraries from allpix squared and the one that I have compiled from classes that I have made (included in the shared library I constructed successfully above). The code you see above is a class definition in the src/objects directory in the allpix squared repository. Needless to say, the test.cpp script does not work properly. Does this problem sound familiar? Can you perhaps guess what is the cause of the issue here? I would also like to note that if I compile like:

root [0] .L test.cpp+

and then do:

root [1]: test()

then everything works properly.

Thank you again,

Andy

Hi Andrew,
what errors are you getting?

I am not explicitly getting any errors. What happens is, after executing

root -x test.cpp

the ROOT prompt prints the aforementioned output above (essentially the part of the source code of allpix squared). I might test something so that you can recreate the problem as well, hopefully without having to install allpixsquared. Just give me a few minutes and I will come back.

Thank you,

Andreas

I…thought it was a copy-paste error :smiley: sorry. I have never seen the ROOT prompt just print out parts of the source code like that, out of the blue. @Axel have you ever seen this?

@andrew a reproducer would indeed help.

Try: root -e 'gSystem->Load("libmylibrary");' test.cpp
You can also try: root test.cpp++

Hi,

So, in case you have access to CVMFS, you can do the following, in order to see the kind of output that I am having:
First, execute

source /cvmfs/clicdp.cern.ch/software/allpix-squared/2.0.3/x86_64-centos8-gcc11-opt/setup.sh > /dev/null

Note that you might have to choose the appropriate operating system, in case you are not working in a Centos8 machine. Then, you can create a test.cpp file, with content:

#define IDENT(x) x
#define XSTR(x) #x
#define STR(x) XSTR(x)
#define PATH(_path, _header) STR(IDENT(_path)IDENT(_header).hpp)

#define _OBJECTS_PATH_ /cvmfs/clicdp.cern.ch/software/allpix-squared/2.0.3/x86_64-centos8-gcc11-opt/include/objects/

#define PIXEL Pixel
#define PIXELHIT PixelHit
#define PIXELCHARGE PixelCharge
#define MCPARTICLE MCParticle
#define MCTRACK MCTrack

/** AllpixSquared Object Header Files **/
#include PATH(_OBJECTS_PATH_, PIXEL)
#include PATH(_OBJECTS_PATH_, PIXELHIT)
#include PATH(_OBJECTS_PATH_, PIXELCHARGE)
#include PATH(_OBJECTS_PATH_, MCPARTICLE)
#include PATH(_OBJECTS_PATH_, MCTRACK)

int test()
{
        gSystem->Load("/cvmfs/clicdp.cern.ch/software/allpix-squared/2.0.3/x86_64-centos8-gcc11-opt/lib/libAllpixObjects.so");
        return 0;
}

Upon executing:

root -x test.cpp

you will perhaps get the outcome that I am getting.

Regards,

Andy

Hi @Wile_E_Coyote,

Thank you for the reply. I just tried this, it yields the same outcome (what you will also perhaps get from the code i just sent to you). I note also that,

gSystem->Load("/cvmfs/clicdp.cern.ch/software/allpix-squared/2.0.3/x86_64-centos8-gcc11-opt/lib/libAllpixObjects.so");
gSystem->Load("../lib/libObjects.so");

that are in my original test.cpp file, both return 0; so there is no problem with perhaps not loading something correctly.

Thank you,

Andy

With ROOT 6, you need to load the required shared libraries before loading the source code which uses them (e.g., via a “rootlogon.C” macro).

Actually, as long as you do not use ACLiC, you can also try to add relevant “R__ADD_LIBRARY_PATH”, “R__LOAD_LIBRARY” and “R__ADD_INCLUDE_PATH” calls (right at the beginning of your macro).

Hi,

I just did this and I do not see any change. I removed the gSystem->Load commands from the source code and added them on a rootlogon.C file. Same results…Were you able to recreate the problem with the example script that I gave you?

Andy

And the thing is: If I compile my header files one by one with

root -l
root [0] .L Class1.cpp+
...
root [1] .L Class2.cpp+
...
root [2] .L Class3.cpp+

and then load all the resulting .so files on the rootlogon.C, everything works as normal. Only when I make a library with g++ is when this happens. Really peculiar…

Sorry, I just saw the update on your post. How should I add these calls in my macro?

Andy

Note that “root [0] .L Class1.cpp+” not only compiles the “Class1.cpp” file, but it actually creates (and compiles) a ROOT dictionary for it. You need to do the same if you want to achieve the same result yourself with “g++”.

A simple solution:

{ // rootlogon.C ...
  // std::cout << "... rootlogon.C ..." << std::endl;
  gROOT->LoadMacro("Class1.cpp+");
  gROOT->LoadMacro("Class2.cpp+");
  gROOT->LoadMacro("Class3.cpp+");
} // ... rootlogon.C

Note: if you change your ROOT version, you will need to delete all automatically created files “manually”, or, instead of a single “+”, use “++” (occasionally, it may also happen that ROOT will not notice that you changed some source code file and it will not recreate the dependent shared library with a single “+”).

If you like, see also: ROOT Forum → Search → “R__LOAD_LIBRARY”

Hi Andrew,
I see this is marked as solved: good, and thank you very much @Wile_E_Coyote .

If you don’t mind could you share what the problem and solution was, and, if you can, could you please share a recipe to make root -x print sources? I’m pretty sure that’s not supposed to happen and we would like to print a meaningful error message instead :smiley:

Cheers,
Enrico

@eguiraud @Wile_E_Coyote Sorry for replying late to this, the problem was not solved (I do not know how the answer is accepted, although I think that is the right direction), I know that these ROOT dictionaries are important, because I said, when I compile with root like:

root [0] .L Class1.cpp+
root [1] .L Class2.cpp+
and so on...

where ACLiC is used and load all the resulting libraries in my rootlogon.C file, there is absolutely no problem. I was trying to figure out how to use R__LOAD_LIBRARY and all of these calls in my macro. I would appreciate any help on this. Also, I did send a way to recreate the issue above, but you have to have access to CERN’s CVMFS clicdp. Let me know what you get as an output.

Kind Regards,

Andy

Ah sorry, I had missed that message.

I can reproduce the problem and luckily I see there is an actual error message on top:

$ root foo.C

Processing foo.C...
libAllpixObjects dictionary payload:705:15: error: field has incomplete type 'allpix::Pixel'
        Pixel pixel_;
              ^
/cvmfs/clicdp.cern.ch/software/allpix-squared/2.0.3/x86_64-centos8-gcc11-opt/include/objects/Pixel.hpp:31:11: note: definition of 'allpix::Pixel' is not complete until the closing '}'
    class Pixel {
          ^
libAllpixObjects dictionary payload:915:15: error: field has incomplete type 'allpix::Pixel'
        Pixel pixel_;
              ^
/cvmfs/clicdp.cern.ch/software/allpix-squared/2.0.3/x86_64-centos8-gcc11-opt/include/objects/Pixel.hpp:31:11: note: definition of 'allpix::Pixel' is not complete until the closing '}'
    class Pixel {
          ^
Error in <TInterpreter::AutoParse>: Error parsing payload code for class ROOT::Math::Cartesian2D<unsigned int> with content:

#line 1 "libAllpixObjects dictionary payload"


#define _BACKWARD_BACKWARD_WARNING_H
// Inline headers
/**
 * @file
 * @brief Definition of Object base class
 * @copyright Copyright (c) 2017-2020 CERN and the Allpix Squared authors.
 * This software is distributed under the terms of the MIT License, copied verbatim in the file "LICENSE.md".
 * In applying this license, CERN does not waive the privileges and immunities granted to it by virtue of its status as an
 * Intergovernmental Organization or submit itself to any jurisdiction.
 */

<snip>

I am not sure what the cause for the error message might be (@Axel or @pcanal might).
I’ll try to take a better look as soon as I can.

Cheers,
Enrico

Something is triggering the dictionary loading during the parsing of Pixel.hpp. It might help to load its library as part of foo.C, i.e. to add right at the top of foo.C R__LOAD_LIBRARY(libPixel) or whatever the library is called.

I’d just do the following: create a new file, All.cpp, which contains

#include "Class1.cpp"
#include "Class2.cpp"
...

and run root -l -b All.cpp+

Does that help?

1 Like