ROOT macros and shared libraries
A ROOT macro contains pure C++ code, which additionally can contain ROOT classes and other ROOT objects (→ see ROOT classes, data types and global variables). A ROOT macro can consist of simple or multi-line commands, but also of arbitrarily complex class and function definitions.
You can save a ROOT macro in a file and execute it at the ROOT prompt or the system prompt (→ see Creating ROOT macros).
You also can compile a ROOT macro (→ see Compiling ROOT macros).
ROOT provides many tutorials that are available as ROOT macros (→ see ROOT tutorial page).
Creating ROOT macros
The name of the ROOT macro and the file name (without file extension) in which the macro is saved must match.
-
Create a new file in your preferred text editor.
-
Use the following general structure for the ROOT macro:
void MacroName() {
...
your lines of C++ code
code line ends with ;
...
}
- Save the file ROOT macro, using the macro name as file name: MacroName.C
Executing ROOT macros
You can execute a ROOT macro:
- at the system prompt,
- at the ROOT prompt,
- by loading it to a ROOT session.
To execute a ROOT macro at the system prompt, type:
root MacroName.C
– or –
To execute a ROOT macro at the ROOT prompt, type:
.x MacroName.C
– or –
To load a ROOT macro to a ROOT session, type (at the ROOT prompt):
.L MacroName.C
MacroName()
Note
You can load multiple ROOT macros, as each ROOT macro has a unique name in the ROOT name space.
In addition, you can:
Executing a ROOT macro from a ROOT macro
You can execute a ROOT macro conditionally inside another ROOT macro.
- Call the interpreter TROOT::ProcessLine().
ProcessLine()
takes a parameter, which is a pointer to an int
or to a
TInterpreter::EErrorCode
to let you access the interpreter error code after an attempt to interpret.
This contains the error as defined in enum TInterpreter::EErrorCode
with TInterpreter::kSuccess
as being the value for a successful execution.
Example
The example $ROOTSYS/tutorials/tree/cernstaff.C
calls a ROOT macro to build a ROOT file, if it does not exist.
void cernstaff() {
if (gSystem->AccessPathName("cernstaff.root")) {
gROOT->ProcessLine(".x cernbuild.C");
}
Executing a ROOT macro from the invocation of ROOT
You can pass a macro to ROOT in its invocation.
Example
The exact kind of quoting depends on the used shell. This example works for bash-like shells.
$ root -l -b 'myCode.C("some String", 12)'
Compiling ROOT macros
You can use ACLiC (Compiling Your Code) to compile your code and to build a dictionary and a shared library from your ROOT macro. ACliC is implemented in TSystem::CompileMacro().
When using ACliC, ROOT checks what library really needs to be build and calls your system’s C++ compiler, linker and dictionary generator. Then ROOT loads a native shared library.
ACLiC executes the following steps:
-
Calls
rootcling
to create automatically a dictionary.
For creating a dictionary manually, → see Using rootcling to generate dictionaries manually. -
Calls the the system’s C++ compiler to build the shared library.
-
If there are errors, it calls the C++ compiler to build a dummy executable to clearly report the unresolved symbols.
ACLiC adds the classes and functions declared in included files with the same name as the
ROOT macro files with one of following extensions: .h
, .hh
, .hpp
, .hxx
, .hPP
, .hXX
.
This means that, by default, you cannot combine ROOT macros from different files into one
library by using #include
statements; you will need to compile each ROOT macro separately.
Compiling a ROOT macro with ACLiC
Before you can compile your interpreted ROOT macro, you need to add the include statements for the classes used in the ROOT macro. Only then you can build and load a shared library containing your ROOT macro.
You can compile a ROOT macro with:
-
default optimizations
-
optimizations
-
debug symbols
Compilation ensures that the shared library is rebuilt.
Note
Do not call ACLiC with a ROOT macro that has a function called
main()
.
To compile a ROOT macro and build a shared library, type:
root[] .L MyScript.C+
The +
option compiles the code and generates a shared library. The name of the shared library is the filename
where the dot before the extension is replaced by an underscore. In addition, the shared library
extension is added.
Example
On most platforms, hsimple.cxx
will generate hsimple_cxx.so
.
The +
command rebuilds the library only if the ROOT macro or any of the files it includes
are newer than the library.
When checking the timestamp, ACLiC generates a dependency file, which name is the same as
the library name, just replacing the so
extension by the d
extension.
To compile a ROOT macro with default optimizations, type:
root[] .L MyScript.C++g
To compile a ROOT macro with optimizations, type:
root[] .L MyScript.C++O
To compile a ROOT macro with debug symbols, type:
root[] .L MyScript.C++
Setting the include path
The $ROOTSYS/include
directory is automatically appended to the include path.
To get the include path, type:
root[] .include
To append the include path, type:
root[] .include $HOME/mypackage/include
Append the following line in the ROOT macro to include the include path:
gSystem->AddIncludePath(" -I$HOME/mypackage/include")
To overwrite an existing include path, type:
gSystem->SetIncludePath(" -I$HOME/mypackage/include")
To add a static library that should be used during linking, type:
gSystem->AddLinkedLibs("-L/my/path -l*anylib*");
For adding a shared library, you can load it before you compile the ROOT macros, by
gSystem->Load("mydir/mylib");
Generating dictionaries
A dictionary (“reflection database”) contains information about the types and functions that are available in a library.
With a dictionary you can call functions inside libraries. Dictionaries are also needed to write a class into a ROOT file (→ see ROOT files).
A dictionary consists of a source file, which contains the type information needed by Cling and ROOT’s I/O subsystem. This source file needs to be generated from the library’s headers and then compiled, linked and loaded. Only then does Cling and ROOT know what is inside a library.
There are two ways to generate a dictionary:
-
using ACLiC
-
using
rootcling
Using ACLiC to generate dictionaries
With a given header file MyHeader.h
, ACliC automatically generates a dictionary:
root[] .L MyHeader.h+
Using rootcling to generate dictionaries manually
You can manually create a dictionary by using rootcling
:
rootcling -f DictOutput.cxx -c OPTIONS Header1.h Header2.h ... Linkdef.h
-
DictOutput.cxx
Specifies the output file that will contain the dictionary. It will be accompanied by a header fileDictOutput.h
. -
OPTIONS
are:-
Isomething
: Adding an include path, so thatrootcling
can find the files included inHeader1.h
,Header2.h
, etc. -
DSOMETHING
: Define a preprocessor macro, which is sometimes needed to parse the header files.
-
-
Header1.h Header2.h...
: The headers files. -
Linkdef.h
: Tellsrootcling
, which classes should be added to the dictionary, → see Selecting dictionary entries: Linkdef.h.
Note
Dictionaries that are used within the same project must have unique names.
Compiled object files relative to dictionary source files cannot reside in the same library or in two libraries loaded by the same application if the original source files have the same name.
Example
In the first step, a TEvent
and a TTrack
class is defined. Next an event object is created to add tracks to it. The track objects have a pointer to their event. This shows that the I/O system correctly handles circular references.
In the second step, a TEvent
and a TTrack
call are implemented.
After that you can use rootcling
to manually generate a directory. This generates the eventdict.cxx
file.
The TEvent.h header
#ifndef __TEvent__
#define __TEvent__
#include "TObject.h"
class TCollection;
class TTrack;
class TEvent : public TObject {
private:
Int_t fId; // Event sequential id
Float_t fTotalMom; // Total momentum.
TCollection *fTracks; // Collection of tracks.
public:
TEvent() { fId = 0; fTracks = 0; }
TEvent(Int_t id);
~TEvent();
void AddTrack(TTrack *t);
Int_t GetId() const { return fId; }
Int_t GetNoTracks() const;
void Print(Option_t *opt="");
Float_t TotalMomentum();
ClassDef(TEvent,1); //Simple event class.
};
The TTrack.h header
#ifndef __TTrack __
#define __TTrack__
#include "TObject.h"
class TEvent;
class TTrack : public TObject {
private:
Int_t fId; // Track sequential id.
TEvent *fEvent; // TEvent to which track belongs.
Float_t fPx; // x part of track momentum.
Float_t fPy; // y part of track momentum.
Float_t fPz; // z part of track momentum.
public:
TTrack() { fId = 0; fEvent = 0; fPx = fPy = fPz = 0; }
TTrack(Int_t id, Event *ev, Float_t px,Float_t py,Float_t pz);
Float_t Momentum() const;
TEvent *GetEvent() const { return fEvent; }
void Print(Option_t *opt="");
ClassDef (TTrack,1); //Simple track class.
};
Implementation of TEvent and TTrack class
TEvent.cxx:
#include <iostream.h>
#include "TOrdCollection.h"
#include "TEvent.h"
#include "TTrack.h"
ClassImp(TEvent)
...
TTrack.cxx:
#include <iostream.h>
#include "TMath.h"
#include "Track.h"
#include "Event.h"
ClassImp(TTrack)
...
Using rootcling to generate the dictionary
rootcling eventdict.cxx -c TEvent.h TTrack.h
eventdict.cxx - the generated dictionary
void TEvent::Streamer(TBuffer &R__b) {
// Stream an object of class TEvent.
if (R__b.IsReading()) {
Version_t R__v = R__b.ReadVersion();
TObject::(R__b);
R__b >> fId;
R__b >> fTotalMom;
R__b >> fTracks;
} else {
R__b.WriteVersion(TEvent::IsA());
TObject::Streamer(R__b);
R__b << fId;
R__b << fTotalMom;
R__b << fTracks;
}
}
Selecting dictionary entries: Linkdef.h
To select which types and functions should go into a dictionary, create a Linkdef.h
file that you use when you call rootcint
manually. The Linkdef.h
file is passed as the last argument to rootcint
. It must end on Linkdef.h, LinkDef.h
, or linkdef.h
. For example, My_Linkdef.h
is correct, Linkdef_mine.h
is not.
The Linkdef.h
file contains directives for rootcint
, for what a dictionary should be created: select the types and functions that will be accessible from the prompt (or in general through CINT) and for I/O.
Preamble: deselection
A Linkdef.h
file starts with the following preamble:
#ifdef __CINT__
#pragma link off all globals;
#pragma link off all classes;
#pragma link off all functions;
#pragma link C++ nestedclasses;
The first line protects the compiler from seeing the rootcint
directives. The rootcint
directives are in the form of #pragma
statements. A #pragma
link of all something says that by default, rootcint
should not generate the dictionary for anything it sees.
The nested classes directive tells rootcint
not to ignore nestedclasses
, this is, classes defined inside classes like here:
class Outer {
public:
class Inner {
public:
// we want a dictionary for this one, too!
...
};
...
};
Selection
In the next step, tell rootcint
for which objects the dictionary should be generated for:
#pragma link C++ class AliEvent+;
#pragma link C++ function StrDup;
#pragma link C++ function operator+(const TString&,const TString&);
#pragma link C++ global gROOT;
#pragma link C++ global gEnv;
#pragma link C++ enum EMessageTypes;
Note
**The
+
after the class name: This enables an essential feature forrootcint
. It is not a default setting, so you must add+
at the end.
Selection by file name
Sometimes it is easier to say: Create a dictionary for everything defined in the MyHeader.h
file.
Write the following statement into the Linkdef.h
file:
#pragma link C++ defined_in "subdir/MyHeader.h";
Make sure that subdir/MyHeader.h
corresponds to one of the header files that is passed to rootcint
.
Closing
Add the following line at the end of the Linkdef.h
file:
#endif /* __CINT__ */.
Example of a Linkdef.h file
#ifdef __CINT__
#pragma link off all globals;
#pragma link off all classes;
#pragma link off all functions;
#pragma link C++ nestedclasses;
#pragma link C++ global gHtml;
#pragma link C++ class THtml;
#endif
Developing portable ROOT macros
Portable ROOT macros run both with the Cling interpreter and ACLiC (Compiling Your Code).
Therefore, it is recommended not to use the Cling extensions and program around the Cling limitations.
If it is not possible to program around the Cling limitations, use the C preprocessor symbols
defined for Cling and rootcling
:
-
__CLING__
is defined for both ROOT androotcling
. -
__ROOTCLING__
(and__MAKECINT__
for backward compatibility) is only defined in rootcling.
Use !defined(__CLING__) || defined(__ROOTCLING__)
to bracket code that needs to be seen by
the compiler and rootcling
, but will be invisible to the interpreter.
– or –
Use !defined(__CLING__)
to bracket code that should be seen only by the compiler and not by Cling nor rootcling
.
Example
Hiding the declaration and initialization of the array gArray
from both Cling and rootcling
:
#if !defined(__CLING__)
int gArray[] = { 2, 3, 4};
#endif
Cling and rootcling
will ignore all statements between the #if !defined (__CLING__)
and #endif
. Because ACLiC calls rootcling
to build a dictionary, the declaration of
gArray
will not be included in the dictionary, and consequently, gArray
will not be
available at the command line even if ACLiC is used.
If you want use the ROOT macro in the interpreter, you have to bracket the usage of gArray
between the #if
’s, since the definition is not visible.
#if !defined(__CLING__)
int gArray[] = { 2, 3, 4};
#elif defined(__ROOTCLING__)
int gArray[];
#endif
gArray
will be visible to rootcling
, but still not visible to Cling. If you use ACLiC, gArray
will be available at the command line.
Included header files
It is recommended to write ROOT macros with all the needed include statements. Only a few header files are not handled correctly by Cling.
You can include following types of headers in the interpreted and compiled mode:
-
The subset of standard C/C++ headers defined in
$ROOTSYS/Cling/include
. -
Headers of classes defined in a previously loaded library (including ROOT own library). The defined class must have a name known to ROOT (this is a class with a
ClassDef
).
Hiding header files from rootcling
that are necessary for the compiler but optional for the interpreter can lead to a fatal error.
Example
#ifndef __CLING__
#include "TTree.h"
#else
class TTree;
#endif
class subTree : public TTree {
};
In this case, rootcling
does not have enough information about the
TTree
class to produce the correct
dictionary file.
If you try this, rootcling
and compiling will be error free. However, instantiating a
subTree
object from the Cling command line will cause a fatal error.
In general, it is recommended to let rootcling
see as many header files as possible.
Embedding the rootcling call into a GNU Makefile
Use the following statement to compile and run the code.
.L MyCode.C+
If you need to use a Makefile, there is the following rule for generating a dictionary (see code snippet below). It will create a new source file, which you should
compile like all the other sources in your library. In addition, you need to
add the include path for ROOT, and you might have to link against ROOT’s libraries: libCore
.
This rule generates the rootcling
dictionary for the headers $(HEADERS)
and a library
containing the dictionary and the compiled $(SOURCES)
:
MyDict.cxx: $(HEADERS) Linkdef.h
[TAB] rootcling -f $@ -c $(CXXFLAGS) -p $^
libMyLib.so: MyDict.cxx $(SOURCES)
[TAB] g++ -shared -o$@ `root-config --ldflags` $(CXXFLAGS) -I$(ROOTSYS)/include $^