Various fixes
[u/mrichter/AliRoot.git] / PWGLF / FORWARD / trains / ProofHelper.C
1 /**
2  * @file   ProofHelper.C
3  * @author Christian Holm Christensen <cholm@master.hehi.nbi.dk>
4  * @date   Tue Oct 16 18:58:37 2012
5  * 
6  * @brief  
7  * 
8  *
9  * @ingroup pwglf_forward_trains_helper
10  * 
11  */
12 #ifndef PROOFHELPER_C
13 #define PROOFHELPER_C
14 #include "Helper.C"
15 #ifndef __CINT__
16 # include "OutputUtilities.C"
17 # include "ParUtilities.C"
18 # include "ChainBuilder.C"
19 # include <TUrl.h>
20 # include <TString.h>
21 # include <TProof.h>
22 # include <TProofLog.h>
23 # include <TProofDebug.h>
24 # include <AliAnalysisManager.h>
25 # include <TEnv.h>
26 # include <TChain.h>
27 #else
28 class TUrl;
29 class TChain;
30 #endif
31
32 // ===================================================================
33 /**
34  * Handle analysis on a Proof farm. 
35  * 
36  * This helper is triggered by URIs of the form 
37  *
38  * @code
39  * proof://[<user>@]<host>[:<port>]/<dsname>[?<options>][#<treename>]
40  * @endcode 
41  * where 
42  * <dl>
43  *   <dt>&lt;user@gt;</dt>
44  *   <dd>Optional user name</dd>
45  *   <dt>&lt;host@gt;</dt>
46  *   <dd>PROOF cluster master host</dd>
47  *   <dt>&lt;port@gt;</dt>
48  *   <dd>Optional PROOF cluster port on master host</dd>
49  *   <dt>&lt;dsname@gt;</dt>
50  *   <dd>Data set name</dd>
51  *   <dt><tt>&lt;datadir&gt;</tt></dt>
52  *   <dd>is the base directory holding data files </dd>
53  *   <dt><tt>&lt;collection&gt;</tt></dt>
54  *   <dd>is an ASCII or XML list of input sources</dd>
55  *   <dt><tt>&lt;file&gt;</tt></dt>
56  *   <dd>is a single ROOT file</dd>
57  *   <dt>&lt;treename@gt;</dt>
58  *   <dd>Optional tree name in data set, often <tt>esdTree</tt> or
59  *   <tt>aodTree</tt></dd>
60  *   <dt>&lt;options@gt;</dt>
61  *   <dd>List of options separated by an &amp;
62  *     <dl>
63  *       <dt><tt>workers=N[x]</tt></dt>
64  *       <dd>Set the number of workers to use.  If <tt>x</tt> is appended, 
65  *         then it's maximum number of workers per slave</dd>
66  *       <dt><tt>dsname</tt>[=&lt;output dataset&gt;]</dt>
67  *       <dd>Register tree output (e.g., AOD) as a new data set on the
68  *         PROOF cluster. If &lt;output dataset&gt; is not specified, take
69  *         the name of the train.</dd>
70  *       <dt><tt>par[=all]</tt></dt>
71  *       <dd>Use PAR files.  If the value <tt>all</tt> is given, then also 
72  *         PAR files of STEERBase, ESD, AOD, ANALYSIS, OADB, ANALYSISalice 
73  *         are used. </dd>
74  *       <dt><tt>mode=[default,rec,sim,train,custom]</tt></dt>
75  *       <dd>Set the AliROOT mode.  If not specified <tt>default</tt> 
76  *         is assumed</tt>.  See also CreateAliROOTPar</dd>
77  *       <dt><tt>storage=&lt;url&gt;</tt></dt>
78  *       <dd>Specify a non-default storage location for special output
79  *         (e.g., AOD trees).  &lt;url&gt; should be a valid XRootd 
80  *         server URI accessible to the slaves - e.g., 
81  *         <tt>root://lxplus.cern.ch:10930//tmp</tt>.</dd>
82  *     </dl>
83  *   </dd>
84  * </dl>  
85  *
86  * @ingroup pwglf_forward_trains_helper
87  */
88 struct ProofHelper : public Helper
89 {
90   /** 
91    * Constructor 
92    * 
93    * @param url  Url 
94    * @param opts Options 
95    */
96   ProofHelper(const TUrl& url, Int_t verbose)
97     : Helper(url, verbose), 
98       fExtraLibs(""),
99       fExtraPars(""),
100       fExtraSrcs(""),
101       fUsePars(false), 
102       fBasePars(false)
103   {
104     fOptions.Add("workers",  "N[x]", "Number of workers to use", "0");
105     fOptions.Add("dsname",   "NAME", "Make output dataset", "");
106     fOptions.Add("par",      "tasks|all", "Use par files",           "tasks");
107     fOptions.Add("mode",     "default|rec|sim", "AliROOT mode",      "default");
108     fOptions.Add("storage",  "URL", "Location for external storage", "");    
109     fOptions.Add("wrapper",  "CMD", "Wrapper command", "");
110     fOptions.Add("clear",    "PKGS", "Clear packages ','-separated", "");
111     fOptions.Add("reset",    "soft|hard", "Reset cluster", "hard");
112     if (!fUrl.GetUser() || fUrl.GetUser()[0] == '\0') 
113       fUrl.SetUser(gSystem->GetUserInfo()->fUser);
114   }
115   /** 
116    * Destructor 
117    */
118   virtual ~ProofHelper() {}
119   /** 
120    * Load a library/PAR/script 
121    * 
122    * @param name   Name 
123    * @param par    If true, upload & enable PAR 
124    * @param slaves If true, also load on slaves
125    * 
126    * @return true on success 
127    */
128   virtual Bool_t LoadLibrary(const TString& name, 
129                              Bool_t slaves=true)
130   {
131     if (!fUsePars) {
132       Int_t ret = gSystem->Load(MakeLibraryName(name));
133       if (ret < 0) return false;
134       if (slaves) fExtraLibs.Append(Form(":%s", name.Data()));
135     }
136     else { 
137       if (!ParUtilities::Find(name)) { 
138         Error("ProofHelper::LoadLibrary", "Failed to find PAR file %s", 
139               name.Data());
140         return false;
141       }
142       if (!ParUtilities::Build(name)) { 
143         Error("ProofHelper::LoadLibrary", "Failed to build PAR file %s", 
144               name.Data());
145         return false;
146       }
147       if (gProof->UploadPackage(name.Data(), TProof::kRemoveOld) < 0) {
148         Error("ProofHelper::LoadLibrary", "Failed to upload PAR file %s", 
149               name.Data());
150         return false;
151       }
152       fExtraPars.Append(Form(":%s", name.Data()));
153     }
154     return true;
155   }
156   /** 
157    * Load a source file, and compile it 
158    * 
159    * @param name Name of the source file 
160    * 
161    * @return true on success
162    */
163   virtual Bool_t LoadSource(const TString& name)
164   {
165     if (!Helper::LoadSource(name)) return false;
166     fExtraSrcs.Append(Form(":%s", gSystem->BaseName(name.Data())));
167     return true;
168   }
169   /** 
170    * Set-up to load the AliROOT libraries 
171    * 
172    * @param par Whether to use PAR files 
173    * 
174    * @return true on success
175    */
176   virtual Bool_t LoadAliROOT()
177   {
178     if (!gSystem->Getenv("ALICE_ROOT")) { 
179       Error("ProofHelper::LoadAliROOT", "Local AliROOT not available");
180       return false;
181     }
182
183     Bool_t tmp = fUsePars;
184     fUsePars   = fBasePars;
185     if (!LoadLibrary("STEERBase"))     return false;
186     if (!LoadLibrary("ESD"))           return false;
187     if (!LoadLibrary("AOD"))           return false;
188     if (!LoadLibrary("ANALYSIS"))      return false;
189     if (!LoadLibrary("OADB"))          return false;
190     if (!LoadLibrary("ANALYSISalice")) return false;
191     fUsePars = tmp;
192
193     return CreateAliROOTPar();
194   }
195   /** 
196    * Get the name of the AliROOT par file to use 
197    * 
198    * @return String 
199    */
200   virtual const char* AliROOTParName() const
201   {
202     return "ALIROOT";
203   }
204   /** 
205    * Create an AliROOT par file from the executing AliROOT.  This PAR
206    * file basically uses the environment of the client - that is, we
207    * assume that the used AliROOT is accessible on the slaves - e.g.,
208    * via an NFS export.
209    * 
210    * Note, the SETUP.C script take one argument - a TList of TNamed
211    * parameters.  Parameters processed are      
212    *
213    * - ALIROOT_MODE=[default,aliroot,rec,sim,train]
214    *   - default: Load base analysis libraries 
215    *   - aliroot: Load $ALICE_ROOT/macros/loadlibs.C
216    *   - rec:     Load $ALICE_ROOT/macros/loadlibsrec.C
217    *   - sim:     Load $ALICE_ROOT/macros/loadlibssim.C
218    * - ALIROOT_EXTRA_LIBS Colon separated list of additional (Ali)ROOT
219    *   libraries to load on the slaves.
220    * 
221    * The generated PAR file is uploaded but not enabled until we have 
222    * populated fExtraLibs.  The enabling takes place at the end of the 
223    * set-up. 
224    * 
225    * @return true on success, false otherwise.     */
226   virtual Bool_t CreateAliROOTPar()
227   {
228     if (fBasePars) return true;
229
230     TString parName(AliROOTParName());
231     TString parFile(Form("%s.par", parName.Data()));
232
233     // --- Check if we have the drirectory already -------------------
234     if (gSystem->AccessPathName(parName.Data()) == 0) { 
235       // Let's remove it to get a clean slate 
236       if (gSystem->Exec(Form("rm -rf %s", parName.Data())) != 0) {
237         Error("ProofHelper", "Failed to remove %s", parName.Data());
238         return false;
239       }
240     }
241     // --- Check if the PAR file is there, and remove it if so -------
242     if (gSystem->AccessPathName(parFile.Data()) == 0) { 
243       if (gSystem->Unlink(parFile.Data()) != 0) { 
244         Error("ProofHelper::CreateAliROOTPar", "Failed to remove %s", 
245               parFile.Data());
246         return false;
247       }
248     }
249       
250
251     // Set-up directories 
252     if (gSystem->MakeDirectory(parName) < 0) {
253       Error("ProofHelper::CreateAliROOTPar", "Could not make directory '%s'", 
254             parName.Data());
255       return false;
256     }
257     
258     if (gSystem->MakeDirectory(Form("%s/PROOF-INF", parName.Data()))) {
259       Error("ProofHelper::CreateAliROOTPar", 
260             "Could not make directory %s/PROOF-INF", 
261             parName.Data());
262       return false;
263     }
264
265     std::ofstream b(Form("%s/PROOF-INF/BUILD.sh",parName.Data()));
266     if (!b) { 
267       Error("ProofHelper::CreateAliROOTPar", 
268             "Failed to make BUILD.sh shell script");
269       return false;
270     }
271     b << "#!/bin/sh\n\n"
272       << "# echo Nothing to do\n"
273       << "exit 0\n"
274       << std::endl;
275     b.close();
276     gSystem->Exec(Form("chmod a+x %s/PROOF-INF/BUILD.sh",parName.Data()));
277
278     std::ofstream s(Form("%s/PROOF-INF/SETUP.C", parName.Data()));
279     if (!s) { 
280       Error("ProofHelper::CreateAliROOTPar", 
281             "Failed to make SETUP.C ROOT script");
282       return false;
283     }
284     s << "void SETUP(TList* opts) {\n"
285       << "  gSystem->Setenv(\"ALICE\",\"" 
286       << gSystem->Getenv("ALICE") << "\");\n"
287       << "  gSystem->Setenv(\"ALICE_ROOT\",\"" 
288       << gSystem->Getenv("ALICE_ROOT") << "\");\n"
289       << "  gSystem->Setenv(\"ALICE_TARGET\",\"" 
290       << gSystem->Getenv("ALICE_TARGET") << "\");\n"
291       << "  gSystem->AddDynamicPath("
292       << "\"$(ALICE_ROOT)/lib/tgt_$(ALICE_TARGET)\");\n";
293     if (gSystem->Getenv("OADB_PATH")) 
294       s << "  gSystem->Setenv(\"OADB_PATH\",\"" 
295         << gSystem->Getenv("OADB_PATH") << "\");\n";
296     s << "  \n"
297       << "  // Info(\"SETUP\",\"Loading ROOT libraries\");\n"
298       << "  gSystem->Load(\"libTree\");\n"
299       << "  gSystem->Load(\"libGeom\");\n"
300       << "  gSystem->Load(\"libVMC\");\n"
301       << "  gSystem->Load(\"libPhysics\");\n"
302       << "  gSystem->Load(\"libMinuit\");\n"
303       << "  \n";
304     s << "  // Info(\"SETUP\",\"Parameter list:\");\n"
305       << "  if (!opts) return;\n"
306       << "  //opts->ls();\n"
307       << "  \n";
308     s << "  TObject* par = opts->FindObject(\"ALIROOT_MODE\");\n"
309       << "  if (par) {\n"
310       << "    // Info(\"SETUP\",\"ALIROOT mode: %s\", par->GetTitle());\n"
311       << "    TString mode(par->GetTitle());\n"
312       << "    if (mode.EqualTo(\"default\",TString::kIgnoreCase)) {\n"
313       << "      gSystem->Load(\"libSTEERBase\");\n"
314       << "      gSystem->Load(\"libESD\");\n"
315       << "      gSystem->Load(\"libAOD\");\n"
316       << "      gSystem->Load(\"libANALYSIS\");\n"
317       << "      gSystem->Load(\"libOADB\");\n"
318       << "      gSystem->Load(\"libANALYSISalice\");\n"
319       << "    }\n"
320       << "    else if (mode.EqualTo(\"aliroot\",TString::kIgnoreCase)) \n"
321       << "      gROOT->Macro(\"$ALICE_ROOT/macros/loadlibs.C\");\n"
322       << "    else if (mode.EqualTo(\"rec\",TString::kIgnoreCase)) \n"
323       << "      gROOT->Macro(\"$ALICE_ROOT/macros/loadlibsrec.C\");\n"
324       << "    else if (mode.EqualTo(\"sim\",TString::kIgnoreCase)) \n"
325       << "      gROOT->Macro(\"$ALICE_ROOT/macros/loadlibssim.C\");\n"
326       << "    else if (mode.EqualTo(\"train\",TString::kIgnoreCase)) \n"
327       << "      gROOT->Macro(\"$ALICE_ROOT/macros/loadlibstrain.C\");\n"
328       << "    else if (mode.EqualTo(\"custom\",TString::kIgnoreCase)) \n"
329       << "      gROOT->Macro(\"$ALICE_ROOT/macros/loadlibstrain.C\");\n"
330       << "  }\n"
331       << "  \n";
332     s << "  par = opts->FindObject(\"ALIROOT_EXTRA_LIBS\");\n"
333       << "  if (par) {\n"
334       << "    Info(\"SETUP\",\"Libaries to load: %s\n\",par->GetTitle());\n"
335       << "    TString tit(par->GetTitle());\n"
336       << "    TObjArray* tokens = tit.Tokenize(\":\");\n"
337       << "    TObject*   lib    = 0;\n"
338       << "    TIter      next(tokens);\n"
339       << "    while ((lib = next())) {\n"
340       << "      TString libName(lib->GetName());\n"
341       << "      if (!libName.BeginsWith(\"lib\")) libName.Prepend(\"lib\");\n"
342       << "      // Info(\"SETUP\",\"Loading %s ...\",libName.Data());\n"
343       << "      gSystem->Load(Form(\"lib%s\",lib->GetName()));\n"
344       << "    }\n"
345       << "  }\n"
346       << "}\n"
347       << std::endl;
348     s.close();
349
350     Int_t ret = gSystem->Exec(Form("tar -czf %s %s",
351                                    parFile.Data(), parName.Data()));
352     if (ret != 0) { 
353       Error("ProofHelper::CreateAliROOTPar", "Failed to pack up PAR file %s",
354             parFile.Data());
355       return false;
356     }
357
358     ret = gProof->UploadPackage(parFile.Data(),TProof::kRemoveOld);
359     if (ret != 0) { 
360       Error("ProofHelper::CreateAliROOTPar", 
361             "Failed to upload the AliROOT PAR file");
362       return false;
363     }
364     // Note, the PAR isn't enabled until much later when we've
365     // collected all the needed libraries in fExtraLibs
366     return true;
367   }
368   /** 
369    * Get the mode identifier 
370    * 
371    * @return Always kProof
372    */
373   virtual UShort_t Mode() const { return kProof; }
374   /**
375    * Get the mode string used for AliAnalysisManager::StartAnalysis
376    */
377   virtual const char* ModeString() const { return "proof"; }
378   /** 
379    * Set-up done before task set-ups 
380    * 
381    * @return true on success 
382    */
383   virtual Bool_t PreSetup()
384   {
385     // --- Set prefered GSI method ---------------------------------
386     gEnv->SetValue("XSec.GSI.DelegProxy", "2");
387
388     // --- Add ALICE_ROOT directory to search path for packages ----
389     // Info("ProofHelper::PreSetup", "Set location of packages");
390     gEnv->SetValue("Proof.GlobalPackageDirs", 
391                    Form("%s:%s", 
392                         gEnv->GetValue("Proof.GlobalPackageDirs", "."), 
393                         gSystem->Getenv("ALICE_ROOT")));
394
395     // --- Forming the URI we use to connect with --------------------
396     TUrl connect(fUrl);
397     connect.SetAnchor("");
398     connect.SetFile("");
399     connect.SetOptions("");
400
401     // --- Check if we need to reset first ---------------------------
402     if (fOptions.Has("reset")) { 
403       TString reset = fOptions.Get("reset");
404       Bool_t  hard  = (reset.IsNull() || 
405                        reset.EqualTo("hard", TString::kIgnoreCase));
406       Info("ProofHelper::PreSetup", "Doing a %s reset of %s", 
407            hard ? "hard" : "soft", connect.GetUrl());
408       TProof::Reset(connect.GetUrl(), hard);
409       Int_t secs = 3;
410       Info("ProofHelper::PreSetup", 
411            "Waiting for %d second%s for things to settle", secs,
412            secs > 1 ? "s" : "");
413       gSystem->Sleep(1000*secs);
414     }
415       
416     // --- Check if we're using a wrapper ----------------------------
417     if (fOptions.Has("wrapper")) { 
418       TString wrapper = fOptions.Get("wrapper");
419       if (wrapper.IsNull()) 
420         // In case of no argument, use GDB 
421         // Just run and backtrace 
422         wrapper = "/usr/bin/gdb --batch -ex run -ex bt --args";
423       Info("ProofHelper::PreSetup", "Using wrapper command: %s", 
424            wrapper.Data());
425       TProof::AddEnvVar("PROOF_WRAPPERCMD", wrapper);
426     }
427
428     // --- PAR parameters --------------------------------------------
429     fUsePars  = fOptions.Has("par");
430     fBasePars = (fUsePars && 
431                  fOptions.Get("par").EqualTo("all",TString::kIgnoreCase));
432
433     // --- Connect to the cluster ------------------------------------
434     TString opts;
435     if (fOptions.Has("workers")) 
436       opts.Append(Form("workers=%s", fOptions.Get("workers").Data()));
437       
438     Info("ProofHelper::PreSetup", "Connecting to %s with %soptions %s", 
439          connect.GetUrl(), 
440          opts.IsNull() ? "no " : "", 
441          opts.Data());
442     TString proto(connect.GetProtocol());
443     if (proto.BeginsWith("lite") && fOptions.Has("workers")) 
444       TProof::Open(opts);
445     else 
446       TProof::Open(connect.GetUrl(), opts);
447     // TProof::Open(connect.GetHost(), opts);
448     if (!gProof) { 
449       Error("ProofHelper::PreSetup", "Failed to open Proof connection %s", 
450             connect.GetUrl());
451       return false;
452     }
453     
454     // --- Check if we need to clear packages ------------------------
455     if (fOptions.Has("clear")) {
456       TString pkgs = fOptions.Get("clear");
457       if (pkgs.IsNull() || pkgs.EqualTo("all", TString::kIgnoreCase)) { 
458         // No value given, clear all 
459         if (gProof->ClearPackages() != 0) 
460           Warning("ProofHelper::PreSetup", "Failed to lear all packages");
461       }
462       else { 
463         // Tokenize on ',' and clear each package 
464         TObjArray* pars = pkgs.Tokenize(",");
465         TObject*   pkg  = 0;
466         TIter      next(pars); 
467         while ((pkg = next())) { 
468           if (gProof->ClearPackage(pkg->GetName()) != 0)
469             Warning("ProofHelper::PreSetup", "Failed to clear package %s", 
470                     pkg->GetName());
471         }
472         pars->Delete();
473       }
474     }
475     return true;
476   }
477   /** 
478    * Set-up done after the task set-ups 
479    *
480    * @return true on success 
481    */
482   virtual Bool_t PostSetup() 
483   {
484     AliAnalysisManager* mgr = AliAnalysisManager::GetAnalysisManager();
485     if (!mgr) { 
486       Error("ProofHelper::PostSetup", "No analysis manager defined");
487       return false;
488     }
489
490     // --- Check for output ------------------------------------------
491     if (fOptions.Has("dsname")) 
492       OutputUtilities::RegisterDataset(fOptions.Get("dsname"));
493     if (fOptions.Has("storage"))
494       OutputUtilities::RegisterStorage(fOptions.Get("storage"));
495
496     // --- If we are not using PARs for Base, enable special PAR -----
497     if (!fBasePars) {
498       TString tmp(fExtraLibs.Strip(TString::kBoth,':'));
499       TList* params = new TList;
500       params->SetOwner(true);
501       params->Add(new TNamed("ALIROOT_EXTRA_LIBS", tmp.Data()));
502       if (fOptions.Has("mode"))
503         params->Add(new TNamed("ALIROOT_MODE", fOptions.Get("mode").Data()));
504       else
505         params->Add(new TNamed("ALIROOT_MODE", "default"));
506       Int_t ret = gProof->EnablePackage(AliROOTParName(), params, true);
507       if (ret < 0) {
508         Error("ProofHelper::EnableAliROOT", "Failed to enable AliROOT PAR %s", 
509               AliROOTParName());
510         return false;
511       }
512     }
513     
514     // --- Load par files --------------------------------------------
515     TString    tmp  = fExtraPars.Strip(TString::kBoth,':');
516     TObjArray* pars = tmp.Tokenize(":");
517     TObject*   obj  = 0;
518     TIter      next(pars);
519     while ((obj = next())) { 
520       // Enable the package, but do not build on client - already done
521       Int_t ret = gProof->EnablePackage(obj->GetName(), true);
522       if (ret < 0) { 
523         Error("ProofHelper::PostSetup", "Failed to enable PAR %s",
524               obj->GetName());
525         return false;
526       }
527     }
528     
529     // --- Load extra sources ----------------------------------------
530     TString    tmp2 = fExtraSrcs.Strip(TString::kBoth, ':');
531     TObjArray* srcs = tmp2.Tokenize(":");
532     TIter      next2(srcs);
533     while ((obj = next())) { 
534       Int_t ret = gProof->Load(Form("%s++g", obj->GetName()), true);
535       if (ret < 0) { 
536         Error("ProofHelper::PostSetup", "Failed to compile %s", obj->GetName());
537         return false;
538       }
539     }
540     return true;
541   }
542   /** 
543    * Start the analysis 
544    * 
545    * @param nEvents Number of events to analyse 
546    * 
547    * @return The return value of AliAnalysisManager::StartAnalysis
548    */
549   virtual Long64_t Run(Long64_t nEvents=-1) 
550   {
551     AliAnalysisManager* mgr = AliAnalysisManager::GetAnalysisManager();
552     gProof->SetLogLevel(TMath::Max(fVerbose-2,0), 
553                         /* TProofDebug::kPacketizer| */
554                         TProofDebug::kLoop|
555                         /* TProofDebug::kSelector|
556                         TProofDebug::kOutput|
557                         TProofDebug::kInput|
558                         TProofDebug::kGlobal|*/
559                         TProofDebug::kPackage);
560     TString dsName(fUrl.GetFile());
561     // if (fUrl.GetAnchor() && fUrl.GetAnchor()[0] != '\0') 
562     //   dsName.Append(Form("#%s", fUrl.GetAnchor()));
563     Long64_t ret = mgr->StartAnalysis(fUrl.GetProtocol(), dsName, nEvents);
564     
565     if (fVerbose > 10) 
566       TProof::Mgr(fUrl.GetUrl())->GetSessionLogs()->Save("*","proof.log");
567     return ret;
568   }
569   /** 
570    * Print information to standard output
571    * 
572    * @param option 
573    */
574   virtual void Print(Option_t* option="") const 
575   {
576     Helper::Print(option);
577     std::cout << std::boolalpha 
578               << "  --- Other settings -------\n"
579               << "  Extra libraries  : " << fExtraLibs << "\n"
580               << "  Extra PARs       : " << fExtraPars << "\n"
581               << "  Extra sources    : " << fExtraSrcs << "\n"
582               << "  Use PARs of tasks: " << fUsePars   << "\n"
583               << "  Use PARs of base : " << fBasePars  
584               << std::noboolalpha << std::endl;
585   }
586   /** 
587    * Path of output 
588    * 
589    * @return Path to output - possibly a data set
590    */
591   virtual TString OutputPath() const 
592   {
593     TString ret;
594     if (fOptions.Has("dsname")) {
595       ret = Form("/%s/%s/", gProof->GetGroup(), gProof->GetUser());
596       ret.Append(OutputUtilities::RegisteredDataset());
597     }
598     return ret;
599   }
600   /** 
601    * @return URL help string
602    */
603   virtual const Char_t* UrlHelp() const 
604   {
605     return "proof://<host>[:<port>]/[<dataset>|<path>][?<options>][#<treeName>]";
606   }
607   /** 
608    * @return Short description 
609    */
610   virtual const char* Desc() const { return "PROOF"; }
611   TString fExtraLibs;
612   TString fExtraPars;
613   TString fExtraSrcs;
614   Bool_t  fUsePars;
615   Bool_t  fBasePars;
616 };
617 #endif
618 //
619 // EOF
620 //