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