519a2843d916a3e671a0825284d09b45d27cbf4d
[u/mrichter/AliRoot.git] / PWGHF / correlationHF / macros / run-single-task.C
1 //-*- Mode: C++ -*-
2 // $Id$
3
4 /// @file   run-single-task.C
5 /// @author Matthias.Richter@ift.uib.no
6 /// @date   2012-04-06
7 /// @brief  Run a single task
8 ///
9 /// Helper macro to run a single task either locally or on Grid
10 /// Usage:
11 /// aliroot -b -q -l run-single-task.C'("mode", "input", "tasks", "name", "options", events, "path", "pattern", "friendPattern", "outputDir", "user")'
12 ///  arguments
13 ///   mode:    local, full, test, compile, merge, collect
14 ///   input:   In grid mode a list of run numbers. 
15 ///            In local mode:
16 ///             - ESD file AliESDs.root
17 ///             - a list of ESD files in a text file <choose-file-name>.txt
18 ///             - AOD file AliAOD.root
19 ///             - AODs with predefined list/AODs in same folder, specifiy as "AOD"
20 ///
21 ///   tasks:   list of class names/source or header files of tasks, or AddTask-macros
22 ///
23 ///  optional arguments
24 ///   name:    analysis name (default 'myanalysis')
25 ///   options: optional arguments passed onto the task, e.g. 'mc', 'event-mixing'
26 ///            options of run-single-task:
27 ///              'mcData' -> the run numbers indicate MC Data, no '000' prepended
28 ///   events:  number of events to be processed (default -1 -> all)
29 ///   path:    data search path for grid analysis (default from configuration file)
30 ///   pattern: data search pattern (default from configuration file)
31 ///   friend pattern: friend file search pattern (default from configuration file)
32 ///   output dir: output directory in Grid home (default gridwork/yyyy-mm-dd_hh-mm)
33 ///   user:    default NULL, using user of active token
34 ///
35 /// Examples:
36 /// aliroot -b -q -l run-single-task.C'("full", "146860", "AliAnalysisTaskSample", "myanalysis_LHC11a")'
37 ///
38 /// aliroot -b -q -l run-single-task.C'("local", "$ALICE_ROOT/test/ppbench/AliESDs.root", "AliAnalysisTaskSample")'
39 ///
40 /// aliroot -b -q -l run-single-task.C'("local", "AOD", "AddTaskSample.C")'
41 ///
42 /// aliroot -b -q -l run-single-task.C'("full", "146860", "AliAnalysisTaskSample", "correlation3p_LHC11a", 0, -1, "/alice/data/2011/LHC11a", "*/pass2_without_SDD/AOD*/*/AliAOD.root")'
43 ///
44 /// aliroot -b -q -l run-single-task.C'("merge", "gridwork/mydir", "AliAnalysisTaskSample", "myanalysis_LHC11a")'
45 ///
46 /// aliroot -b -q -l run-single-task.C'("collect", "gridwork/mydir", "AliAnalysisTaskSample", "myanalysis_LHC11a")'
47 ///
48 /// Data input:
49 /// depending on the format of the search pattern either the ESD or AOD input handler is used.
50 ///
51 /// Source files:
52 /// If the task and classes used by the task are not in an AliRoot library available, e.g.
53 /// for the purpose of development, all header and source files need to be in the local
54 /// directory. The macro searches automatically for dependencies, compiles those and
55 /// copies files to the Grid working directory. In order to make the files accessible in
56 /// the local directory, the files can be just linked.
57 /// <pre>
58 /// for f in <search path>; do ln -s $f; done
59 /// </pre>
60 /// If there are dependencies (include headers) which are not available in the working
61 /// directory, the macro searches for the aliroot libraries implementing them, and adds
62 /// the libraries if found to the setup.
63 ///
64 /// Local analysis:
65 /// requires only the path to the input file and the task class name. If the specified file is
66 /// a text file (.txt) each line can contain an input ESD file path, all files are chained.
67 /// Analysis on local AOD files needs to be setup prior to this macro. gDirectory must contain
68 /// a TChain object of name 'aodTree'. This is for example created by macros like
69 /// $ALICE_ROOT/PWGHF/vertexingHF/MakeAODInputChain.C
70 /// Set $ALICE_ROOT/PWGHF/correlationHF/macros/setupDxHFE.C for an example.
71 ///
72 /// Grid analysis:
73 /// All modes provided by the AliAnalysisAlien plugin can be used, e.g. full, test, offline
74 /// A couple of settings need to be defined in a configuration file 'grid-config.C' which can be
75 /// either in the local directory or home directory. The file can look like
76 /// <pre>
77 ///   const char* alienAPIVersion="V1.1x";
78 ///   const char* alienROOTVersion="v5-34-01";
79 ///   const char* alienAliROOTVersion="v5-03-61-AN";
80 ///   const char* defaultGridDataDir="/alice/data/2011/LHC11a";
81 ///   const char* defaultDataPattern="*/pass2_without_SDD/*/AliESDs.root";
82 ///   const char* defaultFriendDataPattern="";
83 ///   {} // note this empty body
84 /// </pre>
85 /// Data path and pattern can also be specified as command line arguments.
86 /// The working directory in the grid home directory of the user is set to
87 /// gridwork/<date>_<time> (gridwork/yyyy-mm-dd_hh-mm), can be overridden by command line
88 /// parameter.
89 ///
90 /// Options:
91 /// Options to the macro can be propagated via the parameter 'arguments', the known options
92 /// are parsed and filtered out from the arguments, which are than further propagated to
93 /// AdTask macros and task.
94 /// --mcData        switch indicates that the input data is mc data, the run numbers have
95 ///                 a different format in real data
96 /// --nTestFiles=   number of test files to be used in test mode (default 10)
97 /// --merge=        merging mode 'local', 'grid', 'collect' (default Grid)
98 ///
99 /// Merging of output:
100 /// The result of Grid analysis is merged remotely by using the ability of AliAnalysisAlien
101 /// to create stages of merging jobs. Being in the directory where analysis was originally
102 /// launched, the macro can be run in mode 'merge' with the remote working directory,
103 /// tasknames and the analysis name as arguments. Final result can be collected in mode
104 /// 'collect' with the same arguments, see examples.
105 /// Optionally, within the 'options' argument a list of output directories in the
106 /// remote grid directory can be specified, e.g. "000 002".
107 ///
108 /// Merging of output in mode 'terminate':
109 /// The output files can be merged locally by using the argument '--merge=local'. In that
110 /// case all files are downloaded to the local machine and merged. The merging on grid
111 /// requires to launch the analysis with argument '--merge=grid'. After the jobs are done
112 /// further steps are required, 
113 /// 1) run in mode "terminate" with argument '--merge=grid' and working directory on grid,
114 /// 2) run in mode "terminate" with argument '--merge=collect' and working directory on grid.
115 /// Step 1) can be repeated  multiple times, the AnalysisManager will in each stage merge
116 /// several files of the previous stage, it will notify you when the final result has
117 /// already been merged.
118 /// 
119 /// Suggestions:
120 /// Feedback appreciated: Matthias.Richter@cern.ch
121 /// If you find this macro useful but not applicable without patching it, let me know
122 /// your changes and use cases.
123
124
125 #if defined(__CINT__) && !defined(__MAKECINT__)
126 ///////////////////////////////////////////////////////////////////////////////////////////////////
127 //
128 // environment
129 //
130 int macroVerbosity=0;
131 const char* defaultAnalysisName="myanalysis";
132 const char* includePath="-I. -I$ROOTSYS/include -I$ALICE_ROOT/include";
133 const char* libraryDependencies=
134   "libSTEERBase.so "
135   "libESD.so "
136   "libAOD.so "
137   "libANALYSIS.so "
138   "libANALYSISalice.so "
139   ;
140
141 TString BuildJDLName(const char* analysisName);
142 AliAnalysisManager* LoadAnalysisManager(const char* filename);
143 AliInputEventHandler* LoadCustomInputHandler(const char* name);
144 TChain* CreateChain(const char* filename);
145 TString GetIncludeHeaders(const char* filename, TString& headers, TString& libs, bool loadClass=true);
146 TObject* BuildCodeTree(const char* filename, TObject* pTree);
147 int  ProcessCodeTree(TObject* tree, TString& sources, TString& headers, TString& libs);
148 void ErrorConfigurationFile(const char* fileName);
149
150 void run_single_task(const char* mode,
151                      const char* input,
152                      const char* tasknames=NULL,
153                      const char* analysisName=defaultAnalysisName,
154                      const char* arguments="",
155                      int nevents=-1,
156                      const char* gridDataDir=NULL,
157                      const char* dataPattern=NULL,
158                      const char* friendDataPattern=NULL,
159                      TString odir="",
160                      const char* user=NULL
161                      )
162 {
163   ///////////////////////////////////////////////////////////////////////////////////////////////////
164   ///////////////////////////////////////////////////////////////////////////////////////////////////
165   ///////////////////////////////////////////////////////////////////////////////////////////////////
166   //
167   // defaults
168   //
169   if (analysisName==defaultAnalysisName && gDirectory!=NULL) {
170     // NOTE: the direct pointer comparison is on purpose
171     // string comparison not necessary in this special case
172
173     // fetch the analysis name from the setup file
174     const char* confObjectName="analysis_name";
175     TObject* confObject=gDirectory->FindObject(confObjectName);
176     if (confObject) {
177       analysisName=confObject->GetTitle();
178     }
179   }
180
181   bool bRunLocal=strcmp(mode, "local")==0;
182   bool bCompileOnly=strcmp(mode, "compile")==0;
183   if (bCompileOnly) {
184     bRunLocal=true;
185     if (tasknames==NULL) {
186       // short form with tasknames as the second argument
187       tasknames=input;
188     }
189   }
190   int mergeMode=0;
191   if ((strcmp(mode, "merge")==0 && (mergeMode=1)>0) ||
192       (strcmp(mode, "collect")==0 && (mergeMode=2)>0)) {
193     mode="terminate";
194     odir=input;
195     input="0";
196   }
197
198   ///////////////////////////////////////////////////////////////////////////////////////////////////
199   ///////////////////////////////////////////////////////////////////////////////////////////////////
200   ///////////////////////////////////////////////////////////////////////////////////////////////////
201   //
202   // argument settings
203   //
204   bool bRunAnalysis=true;
205   bool bDefaultOutput=true;
206   bool bMergeOnGrid=mergeMode==2?false:true; // default true in all cases but 'collect'
207   bool mcData=false;
208   int nTestFiles=10;
209   int nMaxInputFiles=100;
210   TString strArguments(arguments);
211   TString mergeDirs;
212   TString strCustomInputHandler;
213   TObjArray* tokens=strArguments.Tokenize(" ");
214   if (tokens) {
215     for (int iToken=0; iToken<tokens->GetEntriesFast(); iToken++) {
216       TObject* token=tokens->At(iToken);
217       if (!token) continue;
218       TString arg=token->GetName();
219       const char* key=NULL;
220
221       if (arg.CompareTo("--help")==0 ||
222           arg.CompareTo("-h")==0 ||
223           arg.CompareTo("help")==0 ||
224           arg.CompareTo("options")==0) {
225         // printing help when called without arguments
226         run_single_task();
227         return;
228       }
229       key="--mcData";
230       if (arg.CompareTo(key)==0) {
231         // this is an argument to the macro, don't propagate it further to tasks
232         // switch indicates that the input data is mc data, the run numbers have
233         // a different format in real data
234         // NOTE: not to be confused with option 'mc' which is propagated to tasks
235         // and switches processing and output modes inside tasks
236         mcData=true;
237         continue;
238       }
239       key="--merge=";
240       if (arg.BeginsWith(key)) {
241         // this is an argument to the macro, don't propagate it further to tasks
242         strArguments.ReplaceAll(arg, "");
243         arg.ReplaceAll(key, "");
244         if (arg.CompareTo("local")==0) {
245           // download all files and merge locally
246           bMergeOnGrid=false;
247         } else if (arg.CompareTo("collect")==0) {
248           // download the output of merging on Grid
249           // macro must have been called in mode "terminate" with option
250           // --merge=grid and the correct grid working directory
251           bMergeOnGrid=false;
252         } else if (arg.CompareTo("grid")==0) {
253           // merge output on grid,  the correct grid working directory
254           // must be provided
255           bMergeOnGrid=true;
256         }
257         continue;
258       }
259       key="--nTestFiles=";
260       if (arg.BeginsWith(key)) {
261         // this is an argument to the macro, don't propagate it further to tasks
262         strArguments.ReplaceAll(arg, "");
263         arg.ReplaceAll(key, "");
264         nTestFiles=arg.Atoi();
265         continue;
266       }
267       key="--noDefaultOutput";
268       if (arg.CompareTo(key)==0) {
269         // this is an argument to the macro, don't propagate it further to tasks
270         strArguments.ReplaceAll(arg, "");
271         bDefaultOutput=false;
272         continue;
273       }
274       key="--stopBeforeRunning";
275       if (arg.CompareTo(key)==0) {
276         // this is an argument to the macro, don't propagate it further to tasks
277         strArguments.ReplaceAll(arg, "");
278         bRunAnalysis=false;
279         continue;
280       }
281       key="--maxInputFiles=";
282       if (arg.BeginsWith(key)) {
283         // this is an argument to the macro, don't propagate it further to tasks
284         strArguments.ReplaceAll(arg, "");
285         arg.ReplaceAll(key, "");
286         nMaxInputFiles=arg.Atoi();
287         continue;
288       }
289       key="--InputHandler=";
290       if (arg.BeginsWith(key)) {
291         arg.ReplaceAll(key, "");
292         strCustomInputHandler=arg;
293       }
294       if (!arg.BeginsWith("-") && mergeMode>0) {
295         // treat as subdirectories in the remote grid dir
296         mergeDirs+=" "; mergeDirs+=arg;
297         // this is an argument to the macro, don't propagate it further to tasks
298         strArguments.ReplaceAll(arg, "");
299       }
300     }
301     delete tokens;
302   }
303
304   ///////////////////////////////////////////////////////////////////////////////////////////////////
305   ///////////////////////////////////////////////////////////////////////////////////////////////////
306   ///////////////////////////////////////////////////////////////////////////////////////////////////
307   //
308   // load task classes and find and load all dependencies
309   //
310   gSystem->AddIncludePath(includePath);
311   TString libraries=libraryDependencies;
312   if (gDirectory!=NULL) {
313     // fetch the analysis libraries from the setup file
314     const char* confObjectName="analysis_libraries";
315     TObject* confObject=gDirectory->FindObject(confObjectName);
316     if (confObject) {
317       TString analysisLibraries(confObject->GetTitle());
318       TObjArray* pTokens=analysisLibraries.Tokenize(" ");
319       if (pTokens) {
320         for (int i=0; i<pTokens->GetEntriesFast(); i++) {
321           if (libraries.Contains(pTokens->At(i)->GetName())==0) {
322             libraries+=" ";
323             libraries+=pTokens->At(i)->GetName();
324             TString library=pTokens->At(i)->GetName();
325             if (!library.EndsWith(".so")) libraries+=".so";
326           }
327         }
328         delete pTokens;
329       }
330     }
331   }
332   TObjArray* pTokens=libraries.Tokenize(" ");
333   if (pTokens) {
334     for (int i=0; i<pTokens->GetEntriesFast(); i++) {
335       TString library=pTokens->At(i)->GetName();
336       if (!library.EndsWith(".so")) {
337         cerr << "libraries need to have ending '.so' in order to be correctly processed by alien plugin, please correct library name '" << library << "'" << endl;
338       }
339       if (gSystem->Load(pTokens->At(i)->GetName())==0) {
340         cout << "loading " << pTokens->At(i)->GetName() << endl;
341       }
342     }
343     delete pTokens;
344   }
345
346   TString taskNames=tasknames;
347   TString taskClasses="";
348   TString taskSources="";
349   TString taskHeaders="";
350   TString addTaskMacros="";
351   TString dependencyHeader;
352   TString dependencySource;
353   TString parPackages="";
354   TString delimiter(" ");
355   TStringToken taskNameTokens(taskNames, delimiter);
356   TObject* pCodeTree=NULL;
357   {
358     while (taskNameTokens.NextToken()) {
359       TString taskSource(taskNameTokens);
360       TString taskHeader(taskNameTokens);
361       bool bIsAddTask=false;
362       if (taskSource.EndsWith(".C")) {
363         // suppose that's an 'AddTask' macro
364         taskHeader="";
365         bIsAddTask=true;
366       } else if (taskSource.EndsWith(".par")) {
367         // par file
368         if (gSystem->AccessPathName(taskSource)!=0) {
369           ::Error("run_single_task", Form("par file '%s' not found in current directory, you might want to set a symbolic link", taskSource.Data()));
370           return;
371         }
372         parPackages+=" ";
373         parPackages+=taskSource;
374         continue;
375       } else if (taskSource.EndsWith(".h")) {
376         taskSource.ReplaceAll(".h", "");
377         taskClasses+=" ";
378         taskClasses+=taskSource;
379         taskSource+=".cxx";
380       } else if (taskSource.EndsWith(".cxx")) {
381         taskHeader.ReplaceAll(".cxx", "");
382         taskClasses+=" ";
383         taskClasses+=taskHeader;
384         taskHeader+=".h";
385       } else {
386         taskClasses+=" ";
387         taskClasses+=taskSource;
388         taskSource+=".cxx";
389         taskHeader+=".h";
390       }
391       if (gSystem->AccessPathName(taskSource)==0) {
392         pCodeTree=BuildCodeTree(taskSource, pCodeTree);
393         if (!bIsAddTask) {taskSources+=" "; taskSources+=taskSource;}
394         else {addTaskMacros+=" "; addTaskMacros+=taskSource;}
395       }
396       if (gSystem->AccessPathName(taskHeader)==0) {
397         pCodeTree=BuildCodeTree(taskHeader, pCodeTree);
398         taskHeaders+=" "; taskHeaders+=taskHeader;
399       }
400     }
401   }
402   if (!strCustomInputHandler.IsNull()) {
403     if (strCustomInputHandler.EndsWith(".h")) strCustomInputHandler.ReplaceAll(".h", ".cxx");
404     else if (!strCustomInputHandler.EndsWith(".cxx")) strCustomInputHandler+=".cxx";
405     pCodeTree=BuildCodeTree(strCustomInputHandler, pCodeTree);
406   }
407   ProcessCodeTree(pCodeTree, dependencySource, dependencyHeader, libraries);
408   if (bCompileOnly) return;
409
410   cout << "Tasks: " << taskClasses << endl;
411   cout << "Task files: " << taskSources << addTaskMacros << taskHeaders << endl;
412   cout << "Dependency classes: " << dependencySource << endl;
413   cout << "Dependency headers: " << dependencyHeader << endl;
414   cout << "Dependency libraries: " << libraries << endl;
415   cout << "Packages: " << parPackages << endl;
416
417   ///////////////////////////////////////////////////////////////////////////////////////////////////
418   ///////////////////////////////////////////////////////////////////////////////////////////////////
419   ///////////////////////////////////////////////////////////////////////////////////////////////////
420   //
421   // grid defaults
422   //
423   const char* gridConfigFile="grid-config.C";
424   TString strGridConfigFile=gridConfigFile;
425   if (gSystem->AccessPathName(strGridConfigFile)!=0) {
426     strGridConfigFile.Prepend("/");
427     strGridConfigFile.Prepend(gSystem->Getenv("HOME"));
428     if (gSystem->AccessPathName(strGridConfigFile)!=0) {
429       if (!bRunLocal && mergeMode==0) {
430         ErrorConfigurationFile(gridConfigFile);
431         return;
432       }
433       strGridConfigFile="";
434     }
435   }
436
437   // load the grid configuration file if not merging and not running locally
438   if (strGridConfigFile.IsNull()==0 && !bRunLocal) {
439     cout << "loading grid configuration from file '" << strGridConfigFile << "':" << endl;
440     gROOT->LoadMacro(strGridConfigFile);
441     cout << " alienAPIVersion          =" << alienAPIVersion     << endl;
442     cout << " alienROOTVersion         =" << alienROOTVersion    << endl;
443     cout << " alienAliROOTVersion      =" << alienAliROOTVersion << endl;
444     cout << " defaultGridDataDir       =" << defaultGridDataDir  << endl;
445     cout << " defaultDataPattern       =" << defaultDataPattern  << endl;
446     cout << " defaultFriendDataPattern =" << defaultFriendDataPattern  << endl;
447
448     if (gridDataDir==NULL) gridDataDir=defaultGridDataDir;
449     if (dataPattern==NULL) dataPattern=defaultDataPattern;
450     if (friendDataPattern==NULL) friendDataPattern=defaultFriendDataPattern;
451   } else if (bRunLocal) {
452     if (dataPattern==NULL) {
453       // thats a very crude logic, I guess it can fail in some special cases
454       TString strin=input;
455       if (strin.Contains("AOD"))
456         dataPattern="AOD";
457       else if (strin.Contains("ESD"))
458         dataPattern="ESD";
459     }
460   }
461
462   if(!bRunLocal) {
463     // Connect to AliEn
464     TGrid::Connect("alien://");
465   }
466   ///////////////////////////////////////////////////////////////////////////////////////////////////
467   ///////////////////////////////////////////////////////////////////////////////////////////////////
468   ///////////////////////////////////////////////////////////////////////////////////////////////////
469   //
470   // make the analysis manager
471   //
472   AliAnalysisManager *pManager=NULL;
473   pManager=new AliAnalysisManager("AnalysisManager");
474   if (!pManager) {
475     cerr << "failed to create AnalysisManager" << endl;
476     return;
477   }
478   AliInputEventHandler *pInputHandler = NULL;
479   TString strDataPattern(dataPattern);
480   if (!strCustomInputHandler.IsNull()) {
481     pInputHandler=LoadCustomInputHandler(strCustomInputHandler);
482   }
483   else if (strDataPattern.Contains("AOD")) pInputHandler=new AliAODInputHandler;
484   else if (strDataPattern.Contains("ESD")) pInputHandler=new AliESDInputHandler;
485   else if (mergeMode>0) pInputHandler=new AliInputEventHandler; // a default handler, not used in the end
486   else {
487     cerr << "can not determine input type from data pattern '" << strDataPattern << "'" << endl;
488     return;
489   }
490   if (!pInputHandler) {
491     cerr << "failed to created input handler" << endl;
492     return;
493   }
494   //pInputHandler->SetReadFriends(kFALSE);
495   pManager->SetInputEventHandler(pInputHandler);  
496   pManager->SetNSysInfo(1000);
497
498   TString ofile=Form("%s.root", analysisName);
499
500   ///////////////////////////////////////////////////////////////////////////////////////////////////
501   ///////////////////////////////////////////////////////////////////////////////////////////////////
502   ///////////////////////////////////////////////////////////////////////////////////////////////////
503   //
504   // init for local or GRID analysis
505   //
506   AliAnalysisAlien *alienHandler = NULL; // for grid analysis
507   TChain *chain=NULL; // for local analysis
508   TString strInput=input;
509   if (bRunLocal) {
510     ///////////////////////////////////////////////////////////////////////////////////////////////////
511     //
512     // local analysis
513     //
514     if (strInput.BeginsWith("alien://")) {
515       // file on Grid -> connect to AliEn
516       TGrid::Connect("alien://");
517     }
518     if (strInput.EndsWith(".root") && gSystem->AccessPathName(strInput)==0) {
519       // open a local file
520       chain=CreateChain(strInput.Data());
521     }
522     if (chain) {
523       // nothing to do here, just forward to the original
524       // functionality if the chain was not created already
525     } else 
526     if(strInput.EndsWith("AliESDs.root")){
527       // suppose it's a single ESD file
528       chain = new TChain("esdTree"); 
529       chain->Add(strInput);
530     } else if(strInput.EndsWith(".txt")) {
531       // Constructs chain from filenames in *.txt
532       // in the form $DIR/AliESDs.root  
533       gROOT->LoadMacro("$ALICE_ROOT/PWG0/CreateESDChain.C");
534       // chain can contain up to 200 files, value can be modified to 
535       // include a subset of what the *txt file contains
536       chain = CreateESDChain(strInput.Data(),200); 
537
538       // check if the files are on grid
539       TIter next(chain->GetListOfFiles());
540       TChainElement *chEl = 0;
541       while(( chEl = (TChainElement*)next() )){
542         TString tmp = chEl->GetTitle();     
543         if(tmp.BeginsWith("alien://")) {
544           TGrid::Connect("alien://");
545           break;
546         }
547       }
548     } else if(strInput.EndsWith("ESD")){
549       // fetch esd tree from the setup macro
550       const char* esdTreeName="esdTree";
551       if (gDirectory!=NULL) {
552         TObject* chainObject=gDirectory->FindObject(esdTreeName);
553         if (chainObject) {
554           chain=dynamic_cast<TChain*>(chainObject);
555         }
556       }
557       if (!chain) {
558         ::Error("run_single_task", Form("failed to fetch esd tree object from setup; the chain with name '%s' has to be created before calling this macro", esdTreeName));
559         return;
560       }
561     } else if(strInput.EndsWith("AliAOD.root")){
562       // single local AOD file
563       chain = new TChain("aodTree"); 
564       chain->Add(strInput);
565     } else if(strInput.EndsWith("AOD")){
566       // fetch aod tree from the setup macro
567       const char* aodTreeName="aodTree";
568       if (gDirectory!=NULL) {
569         TObject* chainObject=gDirectory->FindObject(aodTreeName);
570         if (chainObject) {
571           chain=dynamic_cast<TChain*>(chainObject);
572         }
573       }
574       if (!chain) {
575         ::Error("run_single_task", Form("failed to fetch aod tree object from setup; the chain with name '%s' has to be created before calling this macro", aodTreeName));
576         return;
577       }
578     } else {
579       ::Error("run_single_task", Form("invalid input"));
580       return;
581     }
582   } else {
583     ///////////////////////////////////////////////////////////////////////////////////////////////////
584     //
585     // grid analysis
586     //
587     bool bSetRun=true;
588     if (!strInput.IsDigit()) {
589       // support for external macros specifying the the runs to be
590       // analyzed
591       // the input is expected to be an external plugin with name 'input'
592       // and all run numbers being set
593       TObject* pObj=gDirectory->FindObject(input);
594       if (pObj) alienHandler=dynamic_cast<AliAnalysisAlien*>(pObj);
595       if (!alienHandler) {
596         ::Error("run_single_task", Form("can not find plugin of name '%s', please setup alien handler with name and run numbers before calling this macro", input));
597         return;
598       }
599       bSetRun=false;
600     } else {
601       alienHandler=new AliAnalysisAlien();
602     }
603     if (!alienHandler) {
604       ::Error("run_single_task", Form("failed to create alien handler"));
605       return;
606     }
607
608     // do not check for copying to grid (CLOSE_SE)
609     alienHandler->SetCheckCopy(kFALSE);
610
611     // Set the run mode (can be "full", "test", "offline", "submit" or "terminate")
612     alienHandler->SetRunMode(mode);
613
614     // number of files in test mode configurable via argument '--nTestFiles='
615     if(mode=="test") alienHandler->SetNtestFiles(nTestFiles);
616   
617     // check the versions available on alien with the command 'packages'
618     if (mergeMode==0) {
619     alienHandler->SetAPIVersion(alienAPIVersion);
620     alienHandler->SetROOTVersion(alienROOTVersion);
621     alienHandler->SetAliROOTVersion(alienAliROOTVersion);
622     }
623
624     // using only default output
625     // the alien plugin automatically recognizes all output files associated to output
626     // containers, all files are treated in the standard output and added to the
627     // root-archieve.root, which also seems to be needed for merging on Grid
628     // see further down for using non-default output
629     alienHandler->SetDefaultOutputs(bDefaultOutput);
630
631     if (user && user[0]!=0) alienHandler->SetUser(user);
632
633     // data alien directory
634     alienHandler->SetGridDataDir(gridDataDir);
635   
636     // Set data search pattern
637     alienHandler->SetDataPattern(dataPattern);
638     alienHandler->SetFriendChainName(friendDataPattern);
639
640     TObjArray* packageTokens=parPackages.Tokenize(" " );
641     if (packageTokens) {
642       for (int iPackageToken=0; iPackageToken<packageTokens->GetEntriesFast(); iPackageToken++) {
643         alienHandler->EnablePackage(packageTokens->At(iPackageToken)->GetName());
644       }
645       delete packageTokens;
646     }
647
648     if (bSetRun) {
649       // only set if input is a run number
650       if (!mcData && !strInput.BeginsWith("000"))
651         alienHandler->SetRunPrefix("000");   // real data
652
653       alienHandler->AddRunNumber(input);
654     }
655
656     if (mergeMode>0) {
657       // the merge and collect modes have been added to simplify merging on grid
658       // the treatment of arguments are a bit different in order to reduce list
659       // of required arguments
660       TString delimiter(" ");
661       if (mergeDirs.IsNull()) mergeDirs="000";
662       TStringToken dir(mergeDirs, delimiter);
663       while (dir.NextToken()) {
664         alienHandler->AddDataFile(dir->Data());
665       }
666       // use the specified directory names rather than a counter
667       alienHandler->SetOutputToRunNo();
668     }
669
670     // define working and output directories
671     TDatime dt;
672     if(odir.IsNull())
673       odir=(Form("gridwork/%04d-%02d-%02d_%02d-%02d", dt.GetYear(), dt.GetMonth(), dt.GetDay(), dt.GetHour(), dt.GetMinute()));
674     cout << odir << endl;
675     alienHandler->SetGridWorkingDir(odir); // relative to $HOME
676     alienHandler->SetGridOutputDir("output");   // relative to working dir
677     //alienHandler->SetOverwriteMode();                // overwrites the contents of the working and output directory
678
679     // workaround for a Root feature: GetIncludePath() appends always
680     // the current Root include path including escaped quotes. Those
681     // quotes make it difficult to pass the output directly. Search for the
682     // last appended include path and truncate
683     TString strIncludePath(gSystem->GetIncludePath());
684     Int_t pos=strIncludePath.Index(includePath);
685     if (pos>=0) {
686       Int_t cut=0;
687       do {
688         cut=pos+strlen(includePath);
689       } while ((pos=strIncludePath.Index(includePath, cut))>cut);
690       strIncludePath.Resize(cut);
691     }
692     alienHandler->AddIncludePath(strIncludePath);
693
694     // Note: there is no extra source or header file to be transferred if 'AddTask' macros are used
695     alienHandler->SetAnalysisSource(Form("%s %s %s %s", dependencySource.Data(), dependencyHeader.Data(), taskSources.Data(), taskHeaders.Data()));
696     alienHandler->SetAdditionalLibs(Form("%s %s %s %s %s", libraries.Data(), dependencySource.Data(), dependencyHeader.Data(), taskSources.Data(), taskHeaders.Data()));
697
698     // Optionally set a name for the generated analysis macro (default MyAnalysis.C)
699     TString macroName; macroName.Form("run_%s.C",analysisName); macroName.ReplaceAll("-","_");
700     alienHandler->SetAnalysisMacro(macroName);
701   
702     //alienHandler->SetExecutable("comparison.sh");
703     alienHandler->SetExecutable(Form("run_%s.sh",analysisName));
704
705     alienHandler->SetSplitMaxInputFileNumber(nMaxInputFiles);
706   
707     // Optionally set number of failed jobs that will trigger killing waiting sub-jobs.
708     //    alienHandler->SetMaxInitFailed(10);
709   
710     // Optionally resubmit threshold.
711     alienHandler->SetMasterResubmitThreshold(90); // in %
712
713     alienHandler->SetTTL(86400);// in sec
714   
715     // Optionally set input format (default xml-single)
716     alienHandler->SetInputFormat("xml-single");
717  
718     // Optionally modify the name of the generated JDL (default analysis.jdl)
719     alienHandler->SetJDLName(BuildJDLName(analysisName));
720  
721     // Optionally modify job price (default 1)
722     alienHandler->SetPrice(1);
723   
724     // Optionally modify split mode (default 'se')
725     alienHandler->SetSplitMode("se");
726   
727     // configure merging on grid,
728     // argument '--merge=collect' sets 'false' for fetching the merged output
729     alienHandler->SetMergeViaJDL(bMergeOnGrid); 
730
731     alienHandler->SetOneStageMerging(kFALSE);
732     alienHandler->SetMaxMergeStages(2);
733   }
734
735   // Connect plugin to the analysis manager
736   if (alienHandler) {
737     pManager->SetGridHandler(alienHandler);
738   }
739
740   ///////////////////////////////////////////////////////////////////////////////////////////////////
741   ///////////////////////////////////////////////////////////////////////////////////////////////////
742   ///////////////////////////////////////////////////////////////////////////////////////////////////
743   //
744   // create task from the name, create output container, connect slots
745   //
746   TObjArray* taskClassTokens=taskClasses.Tokenize(" ");
747   if (taskClassTokens) {
748     for (int iTaskClassToken=0; iTaskClassToken<taskClassTokens->GetEntriesFast(); iTaskClassToken++) {
749       AliAnalysisTaskSE *pTask=NULL;
750       TString taskName=taskClassTokens->At(iTaskClassToken)->GetName();
751       taskName.ReplaceAll(".cxx", "");
752       TClass* pCl=TClass::GetClass(taskName);
753       if (!pCl) {
754         cerr << "can not load class " << taskName << endl;
755         return;
756       }
757       void* p=pCl->New();
758       if (!p) {
759         cerr << "failed to instantiate class " << taskName << endl;
760         return;
761       }
762       pTask=reinterpret_cast<AliAnalysisTaskSE*>(p);
763       pManager->AddTask(pTask);
764       AliAnalysisDataContainer *pContainer=pManager->CreateContainer(analysisName ,TObject::Class(), AliAnalysisManager::kOutputContainer, ofile);       
765       pManager->ConnectInput(pTask,0,pManager->GetCommonInputContainer());
766       pManager->ConnectOutput(pTask,1,pContainer);
767     }
768     delete taskClassTokens;
769   }
770   TObjArray* taskMacroTokens=addTaskMacros.Tokenize(" ");
771   if (taskMacroTokens) {
772     for (int iTaskMacroToken=0; iTaskMacroToken<taskMacroTokens->GetEntriesFast(); iTaskMacroToken++) {
773       TString taskSource= taskMacroTokens->At(iTaskMacroToken)->GetName();
774
775       taskSource+="+g";
776       TString configuration;
777       if(!strArguments.Contains("file=")) configuration+=Form(" file=%s",ofile.Data()); 
778       if(!strArguments.Contains("name=")) configuration+=Form(" name=%s",analysisName); 
779       configuration+=" "; configuration+=strArguments.Data();
780       if (gDirectory) gDirectory->Add(new TNamed("run_single_task_configuration", configuration.Data()));
781       gROOT->Macro(taskMacroTokens->At(iTaskMacroToken)->GetName());
782     }
783     delete taskMacroTokens;
784   }
785
786   if (!bDefaultOutput) {
787     // fetch all output files from the output containers
788     TString ofiles;
789     TIter nextcontainer(pManager->GetContainers());
790     TObject* objContainer=NULL;
791     while ((objContainer=nextcontainer())!=NULL) {
792       AliAnalysisDataContainer* container=dynamic_cast<AliAnalysisDataContainer*>(objContainer);
793       if (!container) continue;
794       ofiles+=container->GetFileName();
795       ofiles+=" ";
796     }
797
798     alienHandler->SetOutputFiles(ofiles);
799     // Optionally define the files to be archived.
800     alienHandler->SetOutputArchive("log_archive.zip:stdout,stderr");
801   }
802
803   ///////////////////////////////////////////////////////////////////////////////////////////////////
804   ///////////////////////////////////////////////////////////////////////////////////////////////////
805   ///////////////////////////////////////////////////////////////////////////////////////////////////
806   //
807   // run
808   //
809
810   if (!pManager->InitAnalysis()) {
811     cerr << "failed to initialize analysis" << endl;
812     return;
813   }
814   if (nevents<0) nevents=1000000000;
815   pManager->PrintStatus();
816   if (!bRunAnalysis) return;
817   if (bRunLocal) {
818     pManager->StartAnalysis("local", chain, nevents);
819   } else {
820     pManager->StartAnalysis("grid", nevents);
821   }
822 }
823
824 void run_single_task()
825 {
826   // Print help
827   cout << "\nrun-single-task.C: Helper macro to run a single task either locally or on Grid"
828        << "\nUsage:"
829        << "\naliroot -b -q -l run-single-task.C'(\"mode\", \"input\", \"tasks\", \"name\", \"options\", events, \"path\", \"pattern\", \"friendPattern\", \"outputDir\", \"user\")' "
830        << "\n arguments"
831        << "\n  mode:    local, full, test, compile, merge, collect"
832        << "\n  input:   In grid mode a list of run numbers. "
833        << "\n           In local mode:"
834        << "\n            - ESD file AliESDs.root"
835        << "\n            - a list of ESD files in a text file <choose-file-name>.txt"
836        << "\n            - AOD file AliAOD.root"
837        << "\n            - AODs with predefined list/AODs in same folder, specifiy as \"AOD\""
838        << "\n"
839        << "\n  tasks:   list of class names/source or header files of tasks, or AddTask-macros"
840        << "\n"
841        << "\n optional arguments"
842        << "\n  name:    analysis name (default 'myanalysis')"
843        << "\n  options: optional arguments passed onto the task, e.g. 'mc', 'event-mixing'"
844        << "\n           options of run-single-task:"
845        << "\n             'mcData' -> the run numbers indicate MC Data, no '000' prepended"
846        << "\n  events:  number of events to be processed (default -1 -> all)"
847        << "\n  path:    data search path for grid analysis (default from configuration file)"
848        << "\n  pattern: data search pattern (default from configuration file)"
849        << "\n  friend pattern: friend file search pattern (default from configuration file)"
850        << "\n  output dir: output directory in Grid home (default gridwork/yyyy-mm-dd_hh-mm)"
851        << "\n  user:    default NULL, using user of active token"
852        << "\n" << endl;
853   cout << "Examples:"
854        << "\naliroot -b -q -l run-single-task.C'(\"full\", \"146860\", \"AliAnalysisTaskSample\", \"myanalysis_LHC11a\")'"
855        << "\n"
856        << "\naliroot -b -q -l run-single-task.C'(\"local\", \"$ALICE_ROOT/test/ppbench/AliESDs.root\", \"AliAnalysisTaskSample\")'"
857        << "\n"
858        << "\naliroot -b -q -l run-single-task.C'(\"local\", \"AOD\", \"AddTaskSample.C\")'"
859        << "\n"
860        << "\naliroot -b -q -l run-single-task.C'(\"full\", \"146860\", \"AliAnalysisTaskSample\", \"correlation3p_LHC11a\", 0, -1, \"/alice/data/2011/LHC11a\", \"*/pass2_without_SDD/AOD*/*/AliAOD.root\")'"
861        << "\n"
862        << "\naliroot -b -q -l run-single-task.C'(\"merge\", \"gridwork/mydir\", \"AliAnalysisTaskSample\", \"myanalysis_LHC11a\")'"
863        << "\n"
864        << "\naliroot -b -q -l run-single-task.C'(\"collect\", \"gridwork/mydir\", \"AliAnalysisTaskSample\", \"myanalysis_LHC11a\")'"
865        << "\n" << endl;
866
867   cout << "Further options: \n" 
868        << "--merge=local/grid/collect (merge option when running in mode 'terminate', simplified by runniing modes 'merge' and 'collect')\n"
869        << "                           (if you want to merge files on grid, run with --merge=grid and --merge=collect to fetch files)\n"
870        << "--mcData                   (needed if running on MC)\n"
871        << "--nTestFiles=              (number of test files to use from grid, default=10)\n"
872        << "--maxInputFiles=           (number of files in each subjob on grid, default=100)\n"
873        << "--noDefaultOutput \n"
874        << "--stopBeforeRunning        (Testmode for run-single-task, will stop right before starting the analysis)\n\n"
875        << "To get the keywords to send to the AddTask-macros, run them individually with argument help\n\n";
876 }
877
878 // method for backward compatibility
879 void run_single_task(const char* mode,
880                      const char* input,
881                      const char* tasknames,
882                      const char* analysisName,
883                      Bool_t useMC,
884                      int nevents=-1,
885                      const char* gridDataDir=NULL,
886                      const char* dataPattern=NULL,
887                      const char* friendDataPattern=NULL,
888                      TString odir="",
889                      const char* user=NULL
890                      )
891 {
892   run_single_task(mode,
893                   input,
894                   tasknames,
895                   analysisName,
896                   (useMC?"mc":""),
897                   nevents,
898                   gridDataDir,
899                   dataPattern,
900                   friendDataPattern,
901                   odir,
902                   user
903                   );
904 }
905
906 // method for calling with a fixed working directory, e.g. in mode terminate 
907 void run_single_task(const char* mode,
908                      const char* input,
909                      const char* tasknames,
910                      const char* analysisName,
911                      const char* arguments,
912                      const char* workdir
913                      )
914 {
915   TString odir(workdir);
916   run_single_task(mode,
917                   input,
918                   tasknames,
919                   analysisName,
920                   arguments,
921                   -1,
922                   NULL,
923                   NULL,
924                   NULL,
925                   odir,
926                   NULL
927                   );
928 }
929
930 TString GetIncludeHeaders(const char* filename, TString& headers, TString& libs, bool loadClass)
931 {
932   // scan the file and add all include headers found by path
933   // to the parameter headers
934   ifstream input(filename);
935   if (input.bad()) {
936     cerr << "failed to open file " << filename << endl;
937     return headers;
938   }
939   TString line; 
940   while (!line.ReadLine(input).eof()) {
941     if (!line.Contains("#include") || !line.Contains(".h")) continue;
942     line=line(0, line.Index(".h"));line+=".h";
943     line.Replace(0, line.Index("#include"), "");
944     line.ReplaceAll("#include", "");
945     line.ReplaceAll(" ", "");
946     line.ReplaceAll("\"", "");
947     if (!line.BeginsWith("Ali") && !line.BeginsWith("T")) continue;
948     if (gSystem->AccessPathName(line)!=0) {
949       // not an include file in the current directory, check if class
950       // is available or find library
951       line.ReplaceAll(".h","");
952       //cout << "checking class " << line << endl;
953       if (TClass::GetClass(line)==NULL) {
954         TString command;
955         TString resfilename(gSystem->TempDirectory()); resfilename+="/findlib.txt";
956         command.Form("for lib in $ALICE_ROOT/lib/*/lib*.so; do (nm $lib | grep %s | grep ' T ' | grep Class_Name > /dev/null) && echo $lib > %s; done", line.Data(), resfilename.Data());
957         gSystem->Exec(command);
958         ifstream resfile(resfilename.Data());
959         if (resfile.good()) {
960           TString result;
961           if (!result.ReadLine(resfile).eof()) {
962             Ssiz_t haveSlash=-1;
963             while ((haveSlash=result.First('/'))>=0) result.Replace(0, haveSlash+1, "");
964             if (!libs.Contains(result)) {
965               cout << "loading dependency library '" << result << "' for class '" << line << "'" << endl;
966               gSystem->Load(result);
967               if (!libs.IsNull()) libs+=" ";
968               libs+=result;
969             }
970           }
971           command="rm "; command+=resfilename;
972           gSystem->Exec(command);
973         }
974       }
975     } else {
976       if (headers.Contains(line)) {
977         if (!headers.BeginsWith(line)) {
978           headers.ReplaceAll(line, "");
979           if (!headers.IsNull()) headers.Insert(0, " ");
980           if (macroVerbosity>0) cout << "moving " << line << endl;
981           headers.Insert(0, line);
982         }
983         continue;
984       }
985       if (!headers.IsNull()) headers.Insert(0, " ");
986       if (macroVerbosity>0) cout << "inserting " << line << endl;
987       headers.Insert(0, line);
988       TString source=line; source.ReplaceAll(".h", ".cxx");
989       if (gSystem->AccessPathName(source)==0) {
990         GetIncludeHeaders(source, headers, libs);
991       }
992       GetIncludeHeaders(line, headers, libs);
993       if (loadClass && gSystem->AccessPathName(source)==0) {
994         line.ReplaceAll(".h", "");
995         if (TClass::GetClass(line)==NULL) {
996           source+="+g";
997           gROOT->LoadMacro(source);
998         }
999       }
1000     }
1001   }
1002
1003   return headers;
1004 }
1005
1006 TString BuildJDLName(const char* analysisName)
1007 {
1008   TString jdlname(Form("run_%s.jdl",(analysisName!=NULL?analysisName:"analysis")));
1009   return jdlname;
1010 }
1011
1012 AliAnalysisManager* LoadAnalysisManager(const char* filename)
1013 {
1014   // open file and loop through objects to find AnalysisManager
1015   TFile* file=TFile::Open(infilename);
1016   if (!file || file->IsZombie()) {
1017     return;
1018   }
1019
1020   TList* keys=file->GetListOfKeys();
1021   if (!keys || keys->GetEntries()==0) {
1022     cerr << "can not find any keys in file " << infilename << endl;
1023     return;
1024   }
1025
1026   TObject* pObj=NULL;
1027   TObject* pKey=NULL;
1028   TList output;
1029   TIter nextkey(keys);
1030   while (pKey=nextkey()) {
1031     file->GetObject(pKey->GetName(), pObj);
1032     if (pObj && pObj->IsA()!=AliAnalysisManager::Class())
1033       return dynamic_cast<AliAnalysisManager*>(pObj);
1034   }
1035 }
1036
1037 AliInputEventHandler* LoadCustomInputHandler(const char* name)
1038 {
1039   // load a custom input handler
1040   TString className(name);
1041   className.ReplaceAll(".cxx", "");
1042   TClass* pCl=TClass::GetClass(className);
1043   if (!pCl) {
1044     cerr << "can not load class " << className << endl;
1045     return;
1046   }
1047   void* p=pCl->New();
1048   if (!p) {
1049     cerr << "failed to instantiate class " << className << endl;
1050     return;
1051   }
1052   return dynamic_cast<AliInputEventHandler*>(p);
1053 }
1054
1055 TChain* CreateChain(const char* filename)
1056 {
1057   // create input TChain object with tree name derived from input file
1058   TChain* chain=NULL;
1059   TFile* file=TFile::Open(filename);
1060   if (!file || file->IsZombie()) {
1061     return NULL;
1062   }
1063
1064   TList* keys=file->GetListOfKeys();
1065   if (!keys || keys->GetEntries()==0) {
1066     cerr << "can not find any keys in file " << filename << endl;
1067     return NULL;
1068   }
1069
1070   TObject* pObj=NULL;
1071   TObject* pKey=NULL;
1072   TIter nextkey(keys);
1073   while (pKey=nextkey()) {
1074     file->GetObject(pKey->GetName(), pObj);
1075     if (!pObj || pObj->IsA()!=TTree::Class()) continue;
1076     TChain* chain = new TChain(pObj->GetName()); 
1077     chain->Add(filename);
1078     break;
1079   }
1080   file->Close();
1081   delete file;
1082   if (chain) {
1083     cout << "created chain " << chain->GetName() << endl;
1084   }
1085   return chain;
1086 }
1087
1088 void ErrorConfigurationFile(const char* fileName) {
1089   cout << endl;
1090   cout << "/// -------------------------------------------------------------------" << endl;
1091   cout << "/// Warning: can not find configuration file '" << fileName << "'" << endl;
1092   cout << "/// please create a configuration file in either local or HOME directory, or in" << endl;
1093   cout << "/// specified location. Below is an example, fill in your preferred defaults." << endl;
1094   cout << "/// -------------------------------------------------------------------" << endl;
1095   cout << endl;
1096   cout << "const char* alienAPIVersion=\"V1.1x\";" << endl;
1097   cout << "const char* alienROOTVersion=\"v5-33-02a\";" << endl;
1098   cout << "const char* alienAliROOTVersion=\"v5-01-Rev-29\";" << endl;
1099   cout << "const char* defaultGridDataDir=\"/alice/data/2011/LHC11f\";" << endl;
1100   cout << "const char* defaultDataPattern=\"*ESDs.root\";" << endl;
1101   cout << "const char* defaultFriendDataPattern=\"\";" << endl;
1102   cout << "{} // note this empty body";
1103   cout << endl;
1104 }
1105
1106 class AliCodeNode : public TNamed
1107 {
1108 public:
1109   AliCodeNode();
1110   AliCodeNode(const char* filename);
1111   ~AliCodeNode();
1112
1113   enum {
1114     kTypeInvalid = 0,
1115     kTypeSource = 1,
1116     kTypeHeader,
1117     kTypeMacro,
1118     kNofTypes
1119   };
1120     
1121   const TList& GetParents() const {return fParents;}
1122   const TList& GetChilds() const {return fChilds;}
1123   int AddParent(AliCodeNode* pParent);
1124   int InsertChild(AliCodeNode* pChild);
1125   bool HasChilds() const {return (GetChilds().GetEntries()-fProcessedChilds.GetEntries())>0;}
1126   int MarkProcessed();
1127   int MarkChildProcessed(AliCodeNode* pChild);
1128   bool HasSourceParent();
1129   //int DisconnectParents();
1130
1131   bool IsHeader() const {return fType==kTypeHeader;}
1132   bool IsSource() const {return fType==kTypeSource;}
1133   void HaveFile(bool haveFile) {fHaveFile=haveFile;}
1134   bool HaveFile() const {return fHaveFile;}
1135   void Print(Option_t *option="") const;
1136
1137 protected:
1138   //int DisconnectChild(const AliCodeNode& child);
1139
1140 private:
1141   TList fParents; // list of parents
1142   TList fChilds;  // list of childs
1143   TList fProcessedChilds;  // list of processed childs
1144   int   fType;    // source of header
1145   bool  fHaveFile;// file is existing in pwd
1146
1147   ClassDef(AliCodeNode, 1)
1148 };
1149
1150 class AliCodeTree : public TObject
1151 {
1152 public:
1153   AliCodeTree(short verbosity=0) : fNodes(), fIndentCount(0), fVerbosity(verbosity) {fNodes.SetOwner(kTRUE);}
1154   ~AliCodeTree() {}
1155
1156   AliCodeNode* Build(const char* topfile, AliCodeNode* parent=NULL);
1157   AliCodeNode* FindNode(const char* name);
1158   int Sort();
1159   int LoadClasses(TString& libs);
1160   int LoadClasses() {TString dummy; return LoadClasses(dummy);}
1161   int GetHeaders(TString& headers);
1162   int GetSources(TString& sources);
1163
1164   void Print(Option_t *option="") const;
1165
1166 private:
1167   TObjArray fNodes; // list of nodes
1168   short fIndentCount;
1169   short fVerbosity;
1170
1171   ClassDef(AliCodeTree, 1)
1172 };
1173
1174 ClassImp(AliCodeNode)
1175
1176 AliCodeNode::AliCodeNode()
1177  : TNamed()
1178  , fParents()
1179  , fChilds()
1180  , fProcessedChilds()
1181  , fType(AliCodeNode::kTypeInvalid)
1182  , fHaveFile(false)
1183 {
1184 }
1185
1186 AliCodeNode::AliCodeNode(const char* filename)
1187   : TNamed(filename, filename)
1188   , fParents()
1189   , fChilds()
1190   , fProcessedChilds()
1191   , fType(AliCodeNode::kTypeInvalid)
1192  , fHaveFile(false)
1193 {
1194   TString s(filename);
1195   if (s.EndsWith(".cxx")) fType=kTypeSource;
1196   else if (s.EndsWith(".h")) fType=kTypeHeader;
1197   else if (s.EndsWith(".C")) fType=kTypeMacro;
1198 }
1199
1200 AliCodeNode::~AliCodeNode()
1201 {
1202 }
1203
1204 int AliCodeNode::AddParent(AliCodeNode* pParent)
1205 {
1206   if (!pParent) return -1;
1207   if (fParents.FindObject(pParent)) return 0;
1208   if (GetChilds().FindObject(pParent)) {
1209     cerr << "error: circular dependency: can not add " << pParent->GetName() << " as parent to " << this->GetName() << endl;
1210     return -2;
1211   }
1212   fParents.Add(pParent);
1213   return 0;
1214 }
1215
1216 int AliCodeNode::InsertChild(AliCodeNode* pChild)
1217 {
1218   if (!pChild) return -1;
1219   if (fChilds.FindObject(pChild)) return 0;
1220   if (pChild->GetChilds().FindObject(this)) {
1221     cerr << "error: circular dependency: can not add " << pChild->GetName() << " as child to " << this->GetName() << endl;
1222     return -2;
1223   }
1224   fChilds.Add(pChild);
1225   return 0;
1226 }
1227
1228 int AliCodeNode::MarkProcessed()
1229 {
1230   TIter parents(&fParents);
1231   TObject* obj=NULL;
1232   while ((obj=parents())) {
1233     AliCodeNode* parent=dynamic_cast<AliCodeNode*>(obj);
1234     parent->MarkChildProcessed(this);
1235   }
1236
1237   return 0;
1238 }
1239
1240 bool AliCodeNode::HasSourceParent()
1241 {
1242   if (fType!=kTypeHeader) return false;
1243   TString name(GetName());
1244   name.ReplaceAll(".h", ".cxx");
1245   TIter parents(&fParents);
1246   TObject* obj=NULL;
1247   while ((obj=parents())) {
1248     if (name.CompareTo(obj->GetName())==0)
1249       return true;
1250   }
1251   return false;
1252 }
1253
1254 int AliCodeNode::MarkChildProcessed(AliCodeNode* pChild)
1255 {
1256   if (!pChild) return -1;
1257   if (fChilds.FindObject(pChild)==NULL) {
1258     cerr << "node " << GetName() << ": failed to find child node " << pChild->GetName() << endl;
1259     return -1;
1260   }
1261   if (fProcessedChilds.FindObject(pChild)!=NULL) {
1262     cerr << "node " << GetName() << ": child node " << pChild->GetName() << " already processed" << endl;
1263     return 0;
1264   }
1265   fProcessedChilds.Add(pChild);
1266   return 0;
1267 }
1268
1269 void AliCodeNode::Print(Option_t */*option*/) const
1270 {
1271   cout   << "-- " << GetName() << endl;
1272   TObject* obj=NULL;
1273   cout   << "    - parents" << endl;
1274   TIter parents(&fParents);
1275   while ((obj=parents())) {
1276     cout << "    |- " << obj->GetName() << endl;
1277   }
1278   cout   << "    - childs" << endl;
1279   TIter childs(&fChilds);
1280   while ((obj=childs())) {
1281     cout << "    |- " << obj->GetName() << endl;
1282   }
1283 }
1284
1285 ClassImp(AliCodeTree)
1286
1287 AliCodeNode* AliCodeTree::Build(const char* topfile, AliCodeNode* parent)
1288 {
1289   // scan the file and recursively add all include headers found by path
1290   int iResult=0;
1291   AliCodeNode* node=FindNode(topfile);
1292   if (!node) {
1293     if (fVerbosity>0) cout << setw(2*fIndentCount) << " " << "new node " << topfile << endl;
1294     fIndentCount++;
1295     node=new AliCodeNode(topfile);
1296     fNodes.Add(node);
1297     ifstream input(topfile);
1298     if (input.good()) {
1299       node->HaveFile(true);
1300       TString line; 
1301       while (!line.ReadLine(input).eof()) {
1302         if (!line.Contains("#include") || !line.Contains(".h")) continue;
1303         line=line(0, line.Index(".h"));line+=".h";
1304         line.Replace(0, line.Index("#include"), "");
1305         line.ReplaceAll("#include", "");
1306         line.ReplaceAll(" ", "");
1307         line.ReplaceAll("\"", "");
1308         if (!line.BeginsWith("Ali") && !line.BeginsWith("T")) continue;
1309         AliCodeNode* child=NULL;
1310         TString source=line; source.ReplaceAll(".h", ".cxx");
1311         if (source.CompareTo(topfile)!=0 && gSystem->AccessPathName(source)==0) {
1312           child=Build(source, node);
1313           node->InsertChild(child);
1314         }
1315         child=Build(line, node);
1316         node->InsertChild(child);
1317       }
1318     }
1319     fIndentCount--;
1320   }
1321   if (parent) {
1322     if ((iResult=node->AddParent(parent))<0)
1323       return NULL;
1324   }
1325   if (fVerbosity>0) cout << setw(2*fIndentCount) << " " << "finished " << topfile << endl;
1326   return node;
1327 }
1328
1329 int AliCodeTree::Sort()
1330 {
1331   TObjArray sortedNodes;
1332   int nNodes=fNodes.GetEntriesFast();
1333   int nCount=0;
1334   while (sortedNodes.GetEntriesFast()<nNodes && nCount<nNodes) {
1335     for (int i=0; i<nNodes; i++) {
1336       if (fNodes[i]==NULL) continue;
1337       AliCodeNode* node=dynamic_cast<AliCodeNode*>(fNodes[i]);
1338       if (node->HasChilds()) {
1339         continue;
1340       }
1341       fNodes[i]=NULL;
1342       sortedNodes.Add(node);
1343       node->MarkProcessed();
1344     }
1345     nCount++;
1346   }
1347
1348   for (int i=0; i<nNodes; i++) {
1349     fNodes[i]=sortedNodes[i];
1350   }
1351
1352   return 0;
1353 }
1354
1355 int AliCodeTree::LoadClasses(TString& libs)
1356 {
1357   TIter next(&fNodes);
1358   TObject* obj=NULL;
1359   while ((obj=next())!=NULL) {
1360     AliCodeNode* node=dynamic_cast<AliCodeNode*>(obj);
1361     TString name(node->GetName());
1362     if (node->IsHeader()) {
1363       if (node->HasSourceParent()) {
1364         // nothing to do, class going to be compiled from source
1365         continue;
1366       }
1367       name.ReplaceAll(".h", "");
1368       if (TClass::GetClass(name)!=NULL) {
1369         // class available in the system
1370         continue;
1371       }
1372
1373       TString command;
1374       TString resfilename(gSystem->TempDirectory()); resfilename+="/findlib.txt";
1375       command.Form("for lib in $ALICE_ROOT/lib/*/lib*.so; do (nm $lib | grep %s | grep ' T ' | grep Class_Name > /dev/null) && echo $lib > %s; done", name.Data(), resfilename.Data());
1376       gSystem->Exec(command);
1377       ifstream resfile(resfilename.Data());
1378       if (resfile.good()) {
1379         TString result;
1380         if (!result.ReadLine(resfile).eof()) {
1381           Ssiz_t haveSlash=-1;
1382           while ((haveSlash=result.First('/'))>=0) result.Replace(0, haveSlash+1, "");
1383           if (!libs.Contains(result)) {
1384             cout << "loading dependency library '" << result << "' for class '" << name << "'" << endl;
1385             gSystem->Load(result);
1386             if (!libs.IsNull()) libs+=" ";
1387             libs+=result;
1388           }
1389         }
1390         command="rm "; command+=resfilename;
1391         gSystem->Exec(command);
1392       }
1393       continue;
1394     }
1395     if (node->IsSource()) {
1396       TString classname(name);
1397       classname.ReplaceAll(".cxx", "");
1398       if (TClass::GetClass(classname)!=NULL) {
1399         // class available in the system
1400         continue;
1401       }
1402       name+="+g";
1403       gROOT->LoadMacro(name);
1404     }
1405   }
1406   return 0;
1407 }
1408
1409 int AliCodeTree::GetHeaders(TString& headers)
1410 {
1411   TIter next(&fNodes);
1412   TObject* obj=NULL;
1413   while ((obj=next())!=NULL) {
1414     AliCodeNode* node=dynamic_cast<AliCodeNode*>(obj);
1415     if (!node->IsHeader() || !node->HaveFile()) continue;
1416     if (!headers.IsNull()) headers+=" ";
1417     headers+=node->GetName();
1418   }
1419   return 0;
1420 }
1421
1422 int AliCodeTree::GetSources(TString& sources)
1423 {
1424   TIter next(&fNodes);
1425   TObject* obj=NULL;
1426   while ((obj=next())!=NULL) {
1427     AliCodeNode* node=dynamic_cast<AliCodeNode*>(obj);
1428     if (!node->IsSource() || !node->HaveFile()) continue;
1429     if (!sources.IsNull()) sources+=" ";
1430     sources+=node->GetName();
1431   }
1432   return 0;
1433 }
1434
1435 AliCodeNode* AliCodeTree::FindNode(const char* name)
1436 {
1437   TObject* node=fNodes.FindObject(name);
1438   if (!node) return NULL;
1439   return dynamic_cast<AliCodeNode*>(node);
1440 }
1441
1442 void AliCodeTree::Print(Option_t *option) const
1443 {
1444   const char* key=NULL;
1445   int indent=0;
1446   TString childOptions;
1447   const TString delimiter(" ");
1448   TStringToken options(option, delimiter);
1449   while (options.NextToken()) {
1450     key="indent=";
1451     if (options.BeginsWith(key)) {
1452       TString arg(options);
1453       arg.ReplaceAll(key, "");
1454       indent=arg.Atoi();
1455       continue;
1456     }
1457     childOptions+=" ";
1458     childOptions+=options;
1459   }
1460   childOptions+=Form("indent=%d", indent+1);
1461   TIter next(&fNodes);
1462   TObject* obj=NULL;
1463   while ((obj=next())!=NULL) {
1464     obj->Print(childOptions);
1465   }
1466 }
1467
1468 TObject* BuildCodeTree(const char* filename, TObject* useObject)
1469 {
1470   AliCodeTree* pTree=NULL;
1471   if (useObject) pTree=dynamic_cast<AliCodeTree*>(useObject);
1472   if (!pTree) pTree=new AliCodeTree;
1473   if (!pTree) return NULL;
1474   
1475   pTree->Build(filename);
1476   return pTree;
1477 }
1478
1479 int ProcessCodeTree(TObject* tree, TString& sources, TString& headers, TString& libs)
1480 {
1481   if (!tree) return -1;
1482   AliCodeTree* pTree=dynamic_cast<AliCodeTree*>(tree);
1483   pTree->Sort();
1484   pTree->LoadClasses(libs);
1485   pTree->GetHeaders(headers);
1486   pTree->GetSources(sources);
1487   return 0;
1488 }
1489
1490 #else
1491 #include "TObject.h"
1492 #include "TNamed.h"
1493 #include "TList.h"
1494 #include "TObjArray.h"
1495 #include "TString.h"
1496 #include "TPRegexp.h"
1497 #include "TSystem.h"
1498 #include "TROOT.h"
1499 #include "TGrid.h"
1500 #include "TChain.h"
1501 #include "TChainElement.h"
1502 // #include "AliAnalysisManager.h"
1503 // #include "AliAnalysisAlien.h"
1504 // #include "AliAnalysisTaskSE.h"
1505 // #include "AliInputEventHandler.h"
1506 // #include "AliAODInputHandler.h"
1507 // #include "AliESDInputHandler.h"
1508 #include <iostream>
1509 #include <fstream>
1510 #include <iomanip>
1511 using namespace std;
1512
1513 #endif