FairRoot bazuje na ROOT 6 który prekompiluje kod. Zalecaną metodą uruchamiania większych analiz jest jednak tworzenie tasku w formie skompilowanej klasy, czasem może się zdarzyć, że będziemy tworzyć kilkanaście klas, w tym wypadku warto taka klasę zwinąć do biblioteki. Jeśli nasza biblioteka będzie prawidłowo skompilowaną częścią frameworku dla danego eksperymentu będzie automatycznie ładowana.

Bibliotekę tworzymy robiąc nowy katalog np. mytask w katalogu frameworku. Jako przykład weźmiemy mpdroot. Tak więc tworzymy w katalogu mpdroota katalog mytask, a w nim 4 puste pliki:

  • CMakeLists.txt - skrypt kompilujący naszą bibliotekę
  • MyTaskCompiled.cxx - kod źródłowy naszego kodu do analizy
  • MyTaskCompiled.h - plik nagłówkowy kodu do analizy
  • MyTaskLinkDef.h - plik "linkdef".

Zacznijmy od CMakeLists.txt w środku wygląda następująco:

#sekcja headerow
set(INCLUDE_DIRECTORIES
 ${BASE_INCLUDE_DIRECTORIES}
 ${CMAKE_SOURCE_DIR}/mpdbase
 ${CMAKE_SOURCE_DIR}/mcstack
 ${CMAKE_SOURCE_DIR}/mpddst
 ${CMAKE_SOURCE_DIR}/mpddst/mcDst
 ${CMAKE_SOURCE_DIR}/mpddst/MpdMiniEvent
 ${CMAKE_SOURCE_DIR}/mytask
  
)
#####
Set(SYSTEM_INCLUDE_DIRECTORIES
 ${ROOT_INCLUDE_DIR}
)

include_directories(${INCLUDE_DIRECTORIES})
Include_Directories(SYSTEM ${SYSTEM_INCLUDE_DIRECTORIES})

set(LINK_DIRECTORIES
 ${ROOT_LIBRARY_DIR}
 ${FAIRROOT_LIBRARY_DIR}
)
 
link_directories(${LINK_DIRECTORIES})

# sekcja zrodel
set(SRCS
MyTaskCompiled.cxx
)
######
Set(HEADERS)
# sekcja dodatkowych ustawien
Set(LINKDEF MyTaskLinkDef.h)
Set(LIBRARY_NAME MyTask)
Set(DEPENDENCIES Core Base MpdDst MpdBase MpdMCStack)
######
GENERATE_LIBRARY()

Widzimy tu wydzielone kilka sekcji, które należy dostosować do naszego kodu (resztę się zwykle po prostu kopiuje tak żeby wyglądały podobnie jak w innych katalogach i odpowiednio modyfikuje). W sekcji headerów podajemy ścieżki do katalogów z plikami nagłówkowymi wymaganych przez nasze klasy. BASE_INCLUDE_DIRECTORIES ustawiają nam ścieżki do headerów ROOTa oraz FairRoota, dlatego tak naprawdę musimy podać tylko ścieżki do headerów z mpdroota. W sekcji źrodeł podaję względne ścieżki do kolejnych plików z kodem źródłowym (bez headerów!, CMake jest ustawiony tak, że sam zgaduje nazwy headerów zmieniając rozszerzenie, tak więc CMake dla klasy MyTaskCompiled.cxx będzie szukał pliku MyTaskCompiled.h). W sekcji dodatkowych ustawień ustawiamy kolejno: nazwę/ścieżkę linkdefa, nazwę biblioteki, zależności (tj. biblioteki które linkujemy do naszej biblioteki).

Plik z kodem źródłowym to zwykłe kody C++ z klasami kompilowanymi przez ROOTa przedstawiono poniżej, są one trochę bardziej rozbudowanym przykładem z poprzedniej strony - dotyczącej prostej klasy do analizy.

#include "MyTaskCompiled.h"

#include "MpdMiniMcTrack.h"
#include "MpdMiniTrack.h"
#include 

void MyTaskCompiled::Exec(Option_t* opt) {
  for (int i = 0; i < fMcTracks->GetEntriesFast(); i++) {
    MpdMiniMcTrack* mc = (MpdMiniMcTrack*) fMcTracks->UncheckedAt(i);
    TVector3 p         = mc->momentum();
    if (mc->pdgId() == 2212) fYPtSim->Fill(p.Eta(), p.Pt());
  }
  for (int i = 0; i < fRecoTracks->GetEntriesFast(); i++) {
    MpdMiniTrack* track = (MpdMiniTrack*) fRecoTracks->UncheckedAt(i);
    Int_t match         = track->mcTrackIndex();
    if (match >= 0 && match < fMcTracks->GetEntriesFast()) {
      MpdMiniMcTrack* mc = (MpdMiniMcTrack*) fMcTracks->UncheckedAt(match);
      TVector3 p         = mc->momentum();
      if (mc->pdgId() == 2212) fYPtReco->Fill(p.Eta(), p.Pt());
    }
  }
}

InitStatus MyTaskCompiled::Init() {
  FairRootManager* mng = FairRootManager::Instance();
  fRecoEvent           = (TClonesArray*) mng->GetObject("Event");
  fRecoTracks          = (TClonesArray*) mng->GetObject("Track");
  fMcEvent             = (TClonesArray*) mng->GetObject("McEvent");
  fMcTracks            = (TClonesArray*) mng->GetObject("McTrack");
  fTofData             = (TClonesArray*) mng->GetObject("BTofPidTraits");
  fYPtSim              = new TH2D("specSimCompiled", "spec;#eta;p_{T} [GeV/c]", 20, -2, 2, 20, 0, 2);
  fYPtReco             = new TH2D("specRecoCompiled", "spec;#eta;p_{T} [GeV/c]", 20, -2, 2, 20, 0, 2);
  return kSUCCESS;
}
#ifndef MPDROOT_MYTASK_MYTASKCOMPILED_H_
#define MPDROOT_MYTASK_MYTASKCOMPILED_H_

#include "MpdEvent.h"
#include "MpdMCTrack.h"
#include "MpdTrack.h"

#include <FairTask.h>
#include <FairRootManager.h>
#include <TClonesArray.h>
#include <TH2D.h>


class MyTaskCompiled : public FairTask {
  TClonesArray* fRecoTracks;
  TClonesArray* fRecoEvent;
  TClonesArray* fMcTracks;
  TClonesArray* fMcEvent;
  TClonesArray* fTofData;
  TH2D* fYPtSim;
  TH2D* fYPtReco;

protected:
  virtual InitStatus Init();
  virtual void Finish() {
    fYPtSim->Write();
    fYPtReco->Write();
  };

public:
  MyTaskCompiled() :
    fRecoTracks(nullptr),
    fRecoEvent(nullptr),
    fMcTracks(nullptr),
    fMcEvent(nullptr),
    fTofData(nullptr),
    fYPtSim(nullptr),
    fYPtReco(nullptr) {};
  void Exec(Option_t* opt = "");
  virtual ~MyTaskCompiled() {};
  ClassDef(MyTaskCompiled, 1)
};


#endif /* MPDROOT_MYTASK_MYTASKCOMPILED_H_ */

Na końcu tworzymy plik linkdef, jest on wymagany przez ROOTa. Zasadniczo w pliku tym dla każdej klasy dodaje się linię #pragma link C++ class nazwa_klasy +.

#ifdef __CINT__

#pragma link off all globals;
#pragma link off all classes;
#pragma link off all functions;


#pragma link C++ class MyTaskCompiled + ;


#endif

Na końcu musimy dodać nasz katalog do kompilacji. Ponieważ umieściliśmy nasz katalog w głównym katalogu to w tym katalogu musimy znaleźć CMakeLists.txt do którego dodamy linię add_subdirectory(mytask) w naszym przypadku mpdroot/CMakeLists.txt.

Najczęstsze błędy jakie się mogą pojawić to:

  • błąd składniowy w CMakeLists.txt (błąd generowany przez cmake)
  • pominięcie jakieś klasy w SRC (klasa niewidoczna w ROOT mimo poprawnej kompilacji)
  • pominięcie jakiegoś katalogu z potrzebnymi headerami (błąd kompilacji)
  • pominięcie klasy w LinkDef w takim przypadku klasa jest niewidoczna, a przy ręcznym ładowaniu biblioteki w ROOT np. poprzez gSystem->Load("libMyTask.so") wyrzuca błąd typu "undefined reference to "

Należy dodać, że opisane tu metody/terminologie mogą się w pewnym momencie zmienić. Co więcej obecnie FairROOT posiada makra w cmake, które umożliwiają znacznie więcej. Poza kompilacją biblioteki z klasami  możliwe jest np. tworzenie wykonywalnego programu, czy też kompilacja kodu w innym języku np. Fortran.