Check out my previous article which describes how to install ROOT binary or compile it from source.
It often happens that you start with a simple program ROOT tailored to perform a specific calculation or fit or whatever.
It is usually a simple ROOT macro that one runs with ROOT’s interpreter.
Over time you might start adding features, your code grows up and a single-file macro becomes quite messy.
You understand you want a better code organization.
For instance, move helpers to separate files, move your RooFit custom PDFs into a separate folder et cetera et cetera.
Next thing you know you made a nice piece of software and you want other people around to be able to use it.
One day you give up on explaining new students how to use Terminal applications and you decide you want a nice graphical user interface.
Compilation of stand-alone executables that make use of some certain ROOT’s features (GUI, ClassDef…) requires some extra steps rather that just compiling source files and linking them together:Project has to have so-called LinkDef.
h file where user lists Class names that require creating a Dictionary.
A special shared library containing the Dictionary and compiled sources has to be created.
Your object files have to be linked against the required set of ROOT libraries including above mentioned shared library.
In this article I want to explain how to write a GNU Makefile that takes care of these extra steps.
It took me quite a while to do so because information was scattered around the Internet.
Additionally I’ve noticed some uncertainties in official ROOT documentation with respect to this subject.
Generally speaking a Makefile is a file that directs the way your source files are compiled and linked against correspondent libraries.
Let’s start from the project structure.
For the sake of simplicity we will use the most conventional project structure:/Makefile is located in the root project folder.
/src/ folder contains C++ source files organized in folders if necessary.
/build/ is a folder for binary object files generated during compilation.
/dist/ will contain the executable after compilation and linking is done.
h particularly used to generate a dictionary file that is essential for GUI to work.
Dictionary generation for ROOT programs with GUICERN ROOT’s GUI has a special so-called Signal/Slot mechanism that allows communication between a signal (e.
button click event) and slot (correspondent method that is called when event fires).
To launch a ROOT GUI in your system’s window manager you have to instantiate an object of a type TGMainFrame.
There are two options:You instantiate a TGMainFrame object inside your class, say MyMainFrame .
You directly inherit your class MyMainFrame : public TGMainFrame from TGMainFrame and instantiate an object of it.
No matter what option you go, in order for the signals and slots in your app to work you need to generate a dictionary for this one and only class MyMainFrame.
Any signals and slots of the child frames attached via myMainFrame->addFrame(childFrame,.
) will work without extra dictionary.
A file that tells Cling interpreter which classes do need a dictionary is /LinkDef.
h; in our simple case its content should be following:Notice we referenced MyMainFrame on line 7.
More information on LinkDef.
h files on the official website.
ROOT’s dictionary generator rootcling creates the requested app-dictionary.
cxx with the following command:rootcling -f app-dictionary.
cxx -c $(CXXFLAGS) -p Header1.
h has to be the last parameter in the list of headers.
Along with requested *.
cxx dictionary file rootcling generates another *_rdict.
pcm file that I believe has to be moved into the directory with executable.
More information on rootcling can be found on official website.
Shared libraryROOT requires us to preform another intermediate step before we start compiling and linking our source files.
We need to generate a shared library *.
so file in order to be able to link the compiles sources against it later.
gcc -shared -o app-library.
so $(LDFLAGS) $(CXXFLAGS) $(GLIBS) Source1.
Of course macOS users will use clang++ instead of g++.
More information on shared library generation can be found here.
Compiling the sources and linking objectsOkay, this should be the most straightforward section across all the article.
We go through all the source files Source#.
cpp and compile them separately:gcc $(CXXFLAGS) -c Source1.
cpp -o Source1.
ogcc $(CXXFLAGS) -c Source2.
cpp -o Source2.
Final step is linking all the object files against ROOT’s shared libraries and our application shared library that was generated in previous step:gcc -o app-executable Source1.
so $(GLIBS)At this point we have a clear plan with respect to what procedures will be carried out in the Makefile.
Makefile syntaxNow it’s time to get familiar with some of the GNU Makefile syntax.
I will basically go line-by-line through the Makefile itself and try to comprehensively comment on what’s going on.
Here $CXX variable is the operational system specific compiler command.
You know that ROOT is compiled with clang++ on macOS and g++ on Linux.
Next we define some variables that correspond to project folder and file names:Three lines below we define compiler flags, library search paths and ROOT shared libraries names respectively.
Here -O3 is the highest compiler optimization flag (for Release version).
Also notice that root-config — glibs does not list all possible ROOT libraries.
Some of them have to be added here manually (RooFit, RooFitCore etc).
Next, we automatically scan project folder for header *.
h and source *.
cpp files and form correspondent variables HEADERS and SOURCESNotice that here we exclude LinkDef.
h from HEADERS.
Why?.Remember when generating the dictionaryLinkDef.
h has to be the last parameter across all headers.
So we will include it later manually as a last one.
Object files list is created automatically from the Sources list by substituting the extension and directory name.
Couple more variable names and we will start with the Makefile targets.
Important automation in the Makefile is automatic directory creating while compiling object files.
It is a must-have when your source files are organized in sub-directories under /src.
Finally we start with the Makefile targets.
Empty target is followed by release target that adds -O3 optimization compiler flag.
The debug target adds -g flag that tells compiler to generate debug symbols for GDB.
Linking the executable binary against ROOT’s shared libraries and application shared library:Note.
Above we link our object files agains the *-library.
so shared library generated earlier.
By default, when launching the executable after compilation, it will try to locate this specific shared library under $DYLD_LIBRARY_PATH on macOS or $LD_LIBRARY_PATH on Linux.
So technically we should either move the generated *-library.
so in one of the folders in those paths or instead add library’s location to correspondent variable.
Personally I’ve decided to go totally different way here.
From my point of view it is easier to keep the shared library in the same folder with the executable.
Then we have to set the runtime library search path for the executable to be the same directory as executable.
Unfortunately there is no cross-browser way to do so.
On macOS we need to use install_name_tool and for gcc compiler on Linux we can pass -Wl,-rpath option to the linker.
Target for dictionary generation.
Manually adding LinkDef.
h in the very end of the string just like they ask us.
Shared library target depends on the sources and dictionary file.
Below we simply compile our sources.
The automatic variable $< corresponds to the first prerequisite.
Notice how we create a directory before the correspondent object file is created.
Smart!Last piece of the puzzle.
Clean, echo, and create project folders PHONY targets:Boom this is the end.
In order to compile the project with the above described structure the only thing you have to do is setup ROOT (see my previous article on ROOT setup), navigate into your project folder and run make in Terminal.
Described Makefile is cross-platform with respect to compilation on macOS or Linux.
For more information please refer to the source code of one of my projects on GitHub, SW Calculator.
It has a complete project structure and, of course a Makefile in the root project folder as well.
SummaryIn this article we learned how to create a basic Makefile for a CERN ROOT program consisting of a multiple number of sources.
We figured how to generate dictionary for ROOT’s programs with GUI, and compile a shared library for the project.
In the next article I would like to shed some light on another topic of a crucial importance, namely how to set up an IDE with CERN ROOT.
Please let me know if you have any questions.
Let me know about your ROOT Makefile experience.
.. More details