Logo ROOT   6.08/07
Reference Guide
TPackMgr.cxx
Go to the documentation of this file.
1 // @(#)root/proof:$Id$
2 // Author: G. Ganis, Oct 2015
3 
4 /*************************************************************************
5  * Copyright (C) 1995-2000, Rene Brun and Fons Rademakers. *
6  * All rights reserved. *
7  * *
8  * For the licensing terms see $ROOTSYS/LICENSE. *
9  * For the list of contributors see $ROOTSYS/README/CREDITS. *
10  *************************************************************************/
11 
12 /** \class TPackMgr
13 \ingroup proofkernel
14 
15 The PROOF package manager contains tools to manage packages.
16 This class has been created to eliminate duplications, and to allow for
17 standalone usage.
18 
19 */
20 
21 #include "TError.h"
22 #include "TFile.h"
23 #include "TFunction.h"
24 #include "THashList.h"
25 #include "TList.h"
26 #include "TMacro.h"
27 #include "TMD5.h"
28 #include "TMethodArg.h"
29 #include "TMethodCall.h"
30 #include "TObjString.h"
31 #include "TPackMgr.h"
32 #include "TParameter.h"
33 #include "TMap.h"
34 #include "TROOT.h"
35 #include "TSystem.h"
36 
38 
39 
40 #ifndef R__WIN32
41 const char* const kRM = "/bin/rm -rf";
42 const char* const kLS = "/bin/ls -l";
43 const char* const kUNTAR = "%s -c %s/%s | (cd %s; tar xf -)";
44 const char* const kUNTAR3 = "%s -c %s | (tar xf -)";
45 const char* const kGUNZIP = "gunzip";
46 #else
47 const char* const kCP = "copy";
48 const char* const kRM = "delete";
49 const char* const kLS = "dir";
50 const char* const kUNTAR = "...";
51 const char* const kUNTAR2 = "...";
52 const char* const kUNTAR3 = "...";
53 const char* const kGUNZIP = "gunzip";
54 #endif
55 
56 static void DefaultLogger(const char *msg) { Printf("%s", msg); }
57 
58 THashList *TPackMgr::fgGlobalPackMgrList = 0; // list of package managers for global packages
59 
60 ////////////////////////////////////////////////////////////////////////////////
61 /// Create a PROOF package manager
62 
63 TPackMgr::TPackMgr(const char *dir, const char *key)
64  : fLogger(DefaultLogger), fName(key), fDir(dir), fLock(dir), fEnabledPackages(0)
65 {
66  // Work with full names
68  Warning("TPackMgr", "problems expanding path '%s'", fDir.Data());
69  // The lock file in temp
70  TString lockname = TString::Format("%s/packdir-lock-%s",
72  fLock.SetName(lockname);
73 }
74 
75 ////////////////////////////////////////////////////////////////////////////////
76 /// Destroy a TPackMgr instance
77 
79 {
80  // Destroy the lock file
82 }
83 
84 ////////////////////////////////////////////////////////////////////////////////
85 /// Wrapper to notofuer / logger
86 
87 void TPackMgr::Log(const char *msg)
88 {
89  if (fLogger) {
90  if (fPfx.IsNull())
91  (*fLogger)(msg);
92  else
93  (*fLogger)(TString::Format("%s: %s", fPfx.Data(), msg));
94  }
95 }
96 
97 ////////////////////////////////////////////////////////////////////////////////
98 /// Method to build a package.
99 /// Return -1 on error, 0 otherwise
100 
101 Int_t TPackMgr::Build(const char *pack, Int_t opt)
102 {
103  Int_t rc = 0;
104 
105  TLockPathGuard lp(&fLock);
106 
107  if (gDebug > 0)
108  Info("Build", "building package %s ...", pack);
109 
110  TString ocwd = gSystem->WorkingDirectory();
111  TString pdir = TString::Format("%s/%s", fDir.Data(), pack);
112  gSystem->ChangeDirectory(pdir);
113 
114  // check for BUILD.sh and execute
115  if (!gSystem->AccessPathName("PROOF-INF/BUILD.sh")) {
116  // Notify the upper level
117  Log(TString::Format("building %s ...", pack));
118 
119  // Read version from file proofvers.txt, and if current version is
120  // not the same do a "BUILD.sh clean"
121  Bool_t goodver = kTRUE;
122  Bool_t savever = kFALSE;
123  TString v, r;
124  FILE *f = fopen("PROOF-INF/proofvers.txt", "r");
125  if (f) {
126  v.Gets(f);
127  r.Gets(f);
128  fclose(f);
129  if (opt == TPackMgr::kCheckROOT && v != gROOT->GetVersion()) goodver = kFALSE;
130  }
131  if (!f || !goodver) {
132  if (!gSystem->AccessPathName(pdir, kWritePermission)) {
133  savever = kTRUE;
134  Log(TString::Format("%s: version change"
135  " (current: %s, build: %s): cleaning ... ",
136  pack, gROOT->GetVersion(), v.Data()));
137  // Hard cleanup: go up the dir tree
139  // remove package directory
140  gSystem->Exec(TString::Format("%s %s", kRM, pdir.Data()));
141  // find gunzip...
142  char *gunzip = gSystem->Which(gSystem->Getenv("PATH"), kGUNZIP,
144  if (gunzip) {
145  TString par;
146  par.Form("%s.par", pdir.Data());
147  // untar package
148  TString cmd;
149  cmd.Form(kUNTAR3, gunzip, par.Data());
150  rc = gSystem->Exec(cmd);
151  if (rc != 0) {
152  Error("Build", "failure executing: %s", cmd.Data());
153  } else {
154  // Store md5 in package/PROOF-INF/md5.txt
155  TMD5 *md5local = TMD5::FileChecksum(par);
156  if (md5local) {
157  TString md5f = pdir + "/PROOF-INF/md5.txt";
158  TMD5::WriteChecksum(md5f, md5local);
159  // Go down to the package directory
160  gSystem->ChangeDirectory(pdir);
161  // Cleanup
162  SafeDelete(md5local);
163  } else {
164  Warning("Build", "failure calculating/saving MD5sum for '%s'", par.Data());
165  }
166  }
167  delete [] gunzip;
168  } else {
169  Error("Build", "%s not found", kGUNZIP);
170  rc = -1;
171  }
172  } else {
173  Log(TString::Format("%s: ROOT version inconsistency (current: %s, build: %s):"
174  " directory not writable: cannot re-build!!! ",
175  pack, gROOT->GetVersion(), v.Data()));
176  rc = -1;
177  }
178 
179  if (rc == 0) {
180  // To build the package we execute PROOF-INF/BUILD.sh via a pipe
181  // so that we can send back the log in (almost) real-time to the
182  // (impatient) client. Note that this operation will block, so
183  // the messages from builds on the workers will reach the client
184  // shortly after the master ones.
185  TString ipath(gSystem->GetIncludePath());
186  ipath.ReplaceAll("\"","");
187  TString cmd;
188  cmd.Form("export ROOTINCLUDEPATH=\"%s\" ; PROOF-INF/BUILD.sh", ipath.Data());
189  rc = gSystem->Exec(cmd);
190  if (rc != 0) {
191  Error("Build", "failure executing: %s", cmd.Data());
192  } else {
193  // Success: write version file
194  if (savever) {
195  f = fopen("PROOF-INF/proofvers.txt", "w");
196  if (f) {
197  fputs(gROOT->GetVersion(), f);
198  fputs(TString::Format("\n%s", gROOT->GetGitCommit()), f);
199  fclose(f);
200  }
201  }
202  }
203  }
204  } else {
205  // Notify the user
206  if (gDebug > 0)
207  Info("Build", "no PROOF-INF/BUILD.sh found for package %s", pack);
208  }
209  }
210  // Always return to the initial directory
211  gSystem->ChangeDirectory(ocwd);
212 
213  return rc;
214 }
215 
216 ////////////////////////////////////////////////////////////////////////////////
217 /// Method to load a package taking an option const char *
218 /// Return -1 on error, 0 otherwise
219 
220 Int_t TPackMgr::Load(const char *pack, const char *opts)
221 {
222  TList *optls = new TList;
223  optls->Add(new TObjString(opts));
224  Int_t rc = Load(pack, optls);
225  optls->SetOwner();
226  delete optls;
227  return rc;
228 }
229 
230 ////////////////////////////////////////////////////////////////////////////////
231 /// Method to load a package taking an option list
232 /// Return -1 on error, 0 otherwise
233 
234 Int_t TPackMgr::Load(const char *pack, TList *optls)
235 {
236  Int_t rc = 0;
237  TString emsg;
238 
239  // If already loaded don't do it again
241  Log(TString::Format("error: TPackMgr::Load: package %s already loaded", pack));
242  return 0;
243  }
244 
245  // Which pack mgr has the package?
246  if (!Has(pack)) {
247  // Check the global packages
248  TPackMgr *packmgr = 0;
249  if (!(packmgr = TPackMgr::GetPackMgr(pack, nullptr))) {
250  // Package not found
251  Log(TString::Format("error: TPackMgr::Load: failure locating %s ...", pack));
252  return -1;
253  }
254  // Load from there
255  return packmgr->Load(pack, optls);
256  }
257 
258  // We have the package
259  TString pdir = TString::Format("%s/%s", fDir.Data(), pack);
260 
261  // Check dependencies
262  TString deps = TString::Format("%s/PROOF-INF/depends", pdir.Data());
263  if (!gSystem->AccessPathName(deps)) {
264  TMacro mdeps("deps");
265  if (mdeps.ReadFile(deps) > 0) {
266  Log(TString::Format("info: TPackMgr::Load: checking dependencies for package %s ...", pack));
267  TIter nxl(mdeps.GetListOfLines());
268  TObjString *os = 0;
269  while ((os = (TObjString *)nxl())) {
270  if (!TPackMgr::IsEnabled(os->GetName(), this)) {
271  if (Load(os->GetName(), optls) < 0) {
272  // Package loading failure
273  Log(TString::Format("error: TPackMgr::Load: failure loading dep %s ...", os->GetName()));
274  return -1;
275  }
276  }
277  }
278  }
279  }
280 
281  // Make sure it has been build
282  Int_t chkveropt = kCheckROOT;
283  if (optls) {
284  TParameter<Int_t> *pcv = (TParameter<Int_t> *) optls->FindObject("PROOF_Package_CheckVersion");
285  if (pcv) {
286  chkveropt = pcv->GetVal();
287  optls->Remove(pcv);
288  delete pcv;
289  }
290  }
291  if (Build(pack, chkveropt) < 0) {
292  // Package not found
293  Log(TString::Format("error: TPackMgr::Load: package %s oes not build! ", pack));
294  return -1;
295  }
296 
297  TString ocwd = gSystem->WorkingDirectory();
298  gSystem->ChangeDirectory(pdir);
299 
300  // Shared lock from here
301  TLockPathGuard lp(&fLock, kTRUE);
302 
303  // Check for SETUP.C and execute
304  if (!gSystem->AccessPathName("PROOF-INF/SETUP.C")) {
305  // We need to change the name of the function to avoid problems when we load more packages
306  TString setup;
307  setup.Form("SETUP_%d_%x", gSystem->GetPid(), TString(pack).Hash());
308  // Remove special characters
309  TMacro setupmc("PROOF-INF/SETUP.C");
310  TObjString *setupline = setupmc.GetLineWith("SETUP(");
311  if (setupline) {
312  TString setupstring(setupline->GetString());
313  setupstring.ReplaceAll("SETUP(", TString::Format("%s(", setup.Data()));
314  setupline->SetString(setupstring);
315  } else {
316  // Macro does not contain SETUP()
317  Log(TString::Format("warning: macro '%s/PROOF-INF/SETUP.C' does not contain a SETUP()"
318  " function", pack));
319  }
320 
321  // Load the macro
322  if (!setupmc.Load()) {
323  // Macro could not be loaded
324  Log(TString::Format("error: macro '%s/PROOF-INF/SETUP.C' could not be loaded:"
325  " cannot continue", pack));
326  rc = -1;
327  } else {
328  // Check the signature
329  TFunction *fun = (TFunction *) gROOT->GetListOfGlobalFunctions()->FindObject(setup);
330  if (!fun) {
331  // Notify the upper level
332  Log(TString::Format("error: function SETUP() not found in macro '%s/PROOF-INF/SETUP.C':"
333  " cannot continue", pack));
334  rc = -1;
335  } else {
336  TMethodCall callEnv;
337  // Check the number of arguments
338  if (fun->GetNargs() == 0) {
339  // No arguments (basic signature)
340  callEnv.Init(fun);
341  if (optls && optls->GetSize() > 0) {
342  Log(TString::Format("warning: loaded SETUP() for '%s' does not take any argument:"
343  " the specified argument will be ignored", pack));
344  }
345  } else if (fun->GetNargs() == 1) {
346  TMethodArg *arg = (TMethodArg *) fun->GetListOfMethodArgs()->First();
347  if (arg) {
348  callEnv.Init(fun);
349  // Check argument type
350  TString argsig(arg->GetTitle());
351  if (argsig.BeginsWith("TList")) {
352  callEnv.ResetParam();
353  callEnv.SetParam((Long_t) optls);
354  } else if (argsig.BeginsWith("const char")) {
355  callEnv.ResetParam();
356  TObjString *os = optls ? dynamic_cast<TObjString *>(optls->First()) : 0;
357  if (os) {
358  callEnv.SetParam((Long_t) os->GetName());
359  } else {
360  if (optls && optls->First()) {
361  Log(TString::Format("warning: found object argument of type %s:"
362  " SETUP expects 'const char *': ignoring",
363  optls->First()->ClassName()));
364  }
365  callEnv.SetParam((Long_t) 0);
366  }
367  } else {
368  // Notify the upper level
369  Log(TString::Format("error: unsupported SETUP signature: SETUP(%s)"
370  " cannot continue", arg->GetTitle()));
371  rc = -1;
372  }
373  } else {
374  // Notify the upper level
375  Log("error: cannot get information about the SETUP() argument:"
376  " cannot continue");
377  rc = -1;
378  }
379  } else if (fun->GetNargs() > 1) {
380  // Notify the upper level
381  Log("error: function SETUP() can have at most a 'TList *' argument:"
382  " cannot continue");
383  rc = -1;
384  }
385  // Execute
386  Long_t setuprc = (rc == 0) ? 0 : -1;
387  if (rc == 0) {
388  callEnv.Execute(setuprc);
389  if (setuprc < 0) rc = -1;
390  }
391  }
392  }
393  }
394 
395  gSystem->ChangeDirectory(ocwd);
396 
397  if (rc == 0) {
398  // create link to package in working directory
399  gSystem->Symlink(pdir, pack);
400 
401  // add package to list of include directories to be searched
402  // by ACliC
403  gSystem->AddIncludePath(TString::Format("-I%s", pack));
404 
405  // add package to list of include directories to be searched by CINT
406  gROOT->ProcessLine(TString::Format(".I %s", pack));
407 
408  TPair *pck = (optls && optls->GetSize() > 0) ? new TPair(new TObjString(pack), optls->Clone())
409  : new TPair(new TObjString(pack), 0);
410  if (!fEnabledPackages) {
411  fEnabledPackages = new TList;
413  }
414  fEnabledPackages->Add(pck);
415  }
416 
417  return rc;
418 }
419 
420 
421 ////////////////////////////////////////////////////////////////////////////////
422 /// Method to unload a package.
423 /// Return -1 on error, 0 otherwise
424 
425 Int_t TPackMgr::Unload(const char *pack)
426 {
427  Int_t rc = 0;
428 
429  if (fEnabledPackages && fEnabledPackages->GetSize() > 0) {
430  TPair *ppack = 0;
431  if (pack && strlen(pack) > 0) {
432  if ((ppack = (TPair *) fEnabledPackages->FindObject(pack))) {
433 
434  // Remove entry from include path
435  TString aclicincpath = gSystem->GetIncludePath();
436  TString cintincpath = gInterpreter->GetIncludePath();
437  // remove interpreter part of gSystem->GetIncludePath()
438  aclicincpath.Remove(aclicincpath.Length() - cintincpath.Length() - 1);
439  // remove package's include path
440  aclicincpath.ReplaceAll(TString(" -I") + pack, "");
441  gSystem->SetIncludePath(aclicincpath);
442 
443  //TODO reset interpreter include path
444 
445  // remove entry from enabled packages list
446  delete fEnabledPackages->Remove(ppack);
447  }
448 
449  // Cleanup the link, if there
450  if (!gSystem->AccessPathName(pack))
451  if (gSystem->Unlink(pack) != 0) rc = -1;
452 
453  } else {
454 
455  // Iterate over packages and remove each package
456  TIter nxp(fEnabledPackages);
457  while ((ppack = (TPair *) nxp())) {
458  if (Unload(ppack->GetName()) != 0) rc = -1;
459  }
460 
461  }
462  }
463 
464  // We are done
465  return rc;
466 }
467 
468 ////////////////////////////////////////////////////////////////////////////////
469 /// Method to check if this package manager has package 'pack'.
470 /// Return kTRUE or kFALSE
471 
472 Bool_t TPackMgr::Has(const char *pack)
473 {
474  // always follows BuildPackage so no need to check for PROOF-INF
475  TString pdir = TString::Format("%s/%s", fDir.Data(), pack);
476 
477  // Shared lock from here
478  TLockPathGuard lp(&fLock, kTRUE);
479 
480  if (gSystem->AccessPathName(pdir, kReadPermission) ||
481  gSystem->AccessPathName(pdir + "/PROOF-INF", kReadPermission))
482  return kFALSE;
483 
484  // Relevant directories exist and ar readable
485  return kTRUE;
486 }
487 
488 ////////////////////////////////////////////////////////////////////////////////
489 /// Method to check if 'path' is in the managed directory
490 /// Return kTRUE or kFALSE
491 
492 Bool_t TPackMgr::IsInDir(const char *path)
493 {
494  return strncmp(fDir.Data(), path, fDir.Length()) ? kFALSE : kTRUE ;
495 }
496 
497 ////////////////////////////////////////////////////////////////////////////////
498 /// Method to get the path of the dir for package 'pack'.
499 /// Return -1 in case of error (not found), 0 otherwise
500 
501 Int_t TPackMgr::GetPackDir(const char *pack, TString &pdir)
502 {
503  // Make sure the extension is not ".par"
504  TString pn(pack);
505  if (strstr(pack, ".par")) pn.Remove(pn.Last('.'));
506  pdir.Form("%s/%s", fDir.Data(), pn.Data());
507  if (gSystem->AccessPathName(pdir, kReadPermission)) return -1;
508  return 0;
509 }
510 
511 ////////////////////////////////////////////////////////////////////////////////
512 /// Method to get a semi-colon separated list with the names of the enabled
513 /// packages.
514 
516 {
517  packlist = "";
518  if (!fEnabledPackages) return;
519 
520  TIter nxp(fEnabledPackages);
521  TPair *pck= 0;
522  while ((pck = (TPair *)nxp())) {
523  if (packlist.Length() <= 0)
524  packlist = pck->GetName();
525  else
526  packlist += TString::Format(";%s", pck->GetName());
527  }
528  return;
529 }
530 
531 ////////////////////////////////////////////////////////////////////////////////
532 /// Method to get the path of the PAR file for package 'pack'.
533 /// Return -1 in case of error (not found), 0 otherwise
534 
535 Int_t TPackMgr::GetParPath(const char *pack, TString &path)
536 {
537  // Make sure the extension is ".par"
538  const char *fm = (strstr(pack, ".par")) ? "%s/%s" : "%s/%s.par";
539  path.Form(fm, fDir.Data(), pack);
540  if (gSystem->AccessPathName(path, kReadPermission)) return -1;
541  return 0;
542 }
543 
544 ////////////////////////////////////////////////////////////////////////////////
545 /// Method to get the download dir; create if not existing
546 /// Return -1 in case of error (not found; not created), 0 otherwise
547 
549 {
550  dldir.Form("%s/downloaded", fDir.Data());
551  if (gSystem->AccessPathName(dldir, kReadPermission)) {
552  if (gSystem->mkdir(dldir, kTRUE) != 0) return -1;
553  if (gSystem->AccessPathName(dldir, kReadPermission)) return -1;
554  }
555  return 0;
556 }
557 
558 ////////////////////////////////////////////////////////////////////////////////
559 /// Show available packages
560 ///
561 
562 void TPackMgr::Show(const char *title)
563 {
565  // Scan the list of global packages dirs
567  TPackMgr *pm = 0;
568  while ((pm = (TPackMgr *)nxpm())) {
569  pm->Show(TString::Format("*** Global Package cache %s %s:%s ***\n",
570  pm->GetName(), gSystem->HostName(), pm->GetTitle()));
571  }
572  }
573 
574  if (title && strlen(title) > 0)
575  printf("%s\n", title);
576  else
577  printf("*** Package cache %s:%s ***\n", gSystem->HostName(), fDir.Data());
578  fflush(stdout);
579  // Shared lock from here
580  TLockPathGuard lp(&fLock, kTRUE);
581  gSystem->Exec(TString::Format("%s %s", kLS, fDir.Data()));
582  printf("\n");
583 }
584 
585 ////////////////////////////////////////////////////////////////////////////////
586 /// Clean dir for package 'pack'
587 /// Return -1 in case of error, 0 otherwise
588 ///
589 
590 Int_t TPackMgr::Clean(const char *pack)
591 {
592  // Shared lock from here
593  TLockPathGuard lp(&fLock);
594  Int_t rc = 0;
595  if (pack && strlen(pack)) {
596  // remove package directory and par file
597  rc = gSystem->Exec(TString::Format("%s %s/%s/*", kRM, fDir.Data(), pack));
598  }
599  return rc;
600 }
601 
602 ////////////////////////////////////////////////////////////////////////////////
603 /// Remove package 'pack'
604 /// If 'pack' is null or empty all packages are cleared
605 ///
606 
607 Int_t TPackMgr::Remove(const char *pack, Bool_t dolock)
608 {
609  // Shared lock from here
610  if (dolock) fLock.Lock();
611  Int_t rc1 = 0, rc2 = 0, rc3 = 0;
612  if (pack && strlen(pack)) {
613  // remove package directory and par file
614  TString path = TString::Format("%s/downloaded/%s.par", fDir.Data(), pack);
615  gSystem->Exec(TString::Format("%s %s", kRM, path.Data()));
616  if (!gSystem->AccessPathName(path, kFileExists)) rc1 = -1;
617  path.ReplaceAll("/downloaded/", "/");
618  gSystem->Exec(TString::Format("%s %s", kRM, path.Data()));
619  if (!gSystem->AccessPathName(path, kFileExists)) rc2 = -1;
620  path.Remove(path.Last('.'));
621  gSystem->Exec(TString::Format("%s %s", kRM, path.Data()));
622  if (!gSystem->AccessPathName(path, kFileExists)) rc3 = -1;
623  } else {
624  // Clear all packages
625  rc1 = gSystem->Exec(TString::Format("%s %s/*", kRM, fDir.Data()));
626  }
627  if (dolock) fLock.Unlock();
628  return (rc1 + rc2 + rc3);
629 }
630 
631 ////////////////////////////////////////////////////////////////////////////////
632 /// Get list of available packages
633 /// Returns a pointer to a TList object, transferring ownership to the caller
634 
636 {
637  TList *plist = new TList;
638  void *dir = gSystem->OpenDirectory(fDir);
639  if (dir) {
640  TString pac(gSystem->GetDirEntry(dir));
641  while (pac.Length() > 0) {
642  if (pac.EndsWith(".par")) {
643  pac.ReplaceAll(".par","");
644  plist->Add(new TObjString(pac.Data()));
645  }
646  pac = gSystem->GetDirEntry(dir);
647  }
648  }
649  gSystem->FreeDirectory(dir);
650 
651  return plist;
652 }
653 
654 ////////////////////////////////////////////////////////////////////////////////
655 /// Get list of enabled packages
656 /// Returns a pointer to a TList object, transferring ownership to the caller
657 
659 {
660  TList *epl = nullptr;
661  if (fEnabledPackages && fEnabledPackages->GetSize() > 0) {
662  epl = new TList;
663  TIter nxp(fEnabledPackages);
664  TObject *o = 0;
665  while ((o = nxp())) {
666  epl->Add(new TObjString(o->GetName()));
667  }
668  }
669  return epl;
670 }
671 
672 ////////////////////////////////////////////////////////////////////////////////
673 /// Show enabled packages
674 ///
675 
676 void TPackMgr::ShowEnabled(const char *title)
677 {
679  // Scan the list of global packages dirs
681  TPackMgr *pm = 0;
682  while ((pm = (TPackMgr *)nxpm())) {
683  pm->ShowEnabled(TString::Format("*** Global Package cache %s %s:%s ***\n",
684  pm->GetName(), gSystem->HostName(), pm->GetTitle()));
685  }
686  }
687 
688  if (!fEnabledPackages || fEnabledPackages->GetSize() <= 0) return;
689 
690  if (title && strlen(title) > 0)
691  printf("%s\n", title);
692  else
693  printf("*** Package enabled on %s ***\n", gSystem->HostName());
694  fflush(stdout);
695 
696  TIter next(fEnabledPackages);
697  while (TPair *pck = (TPair *) next()) {
698  printf("%s\n", pck->GetName());
699  }
700 }
701 
702 ////////////////////////////////////////////////////////////////////////////////
703 /// Get MD5 checksum of the PAR file corresponding to given package
704 /// Returns a pointer to a TMD5 object, transferring ownership to the caller
705 
706 TMD5 *TPackMgr::GetMD5(const char *pack)
707 {
708  // Shared lock from here
709  TLockPathGuard lp(&fLock, kTRUE);
710  // PAR file path
711  const char *fm = (strstr(pack, ".par")) ? "%s/%s" : "%s/%s.par";
712  TString parfile = TString::Format(fm, fDir.Data(), pack);
713 
714  return TMD5::FileChecksum(parfile);
715 }
716 
717 
718 ////////////////////////////////////////////////////////////////////////////////
719 /// Read MD5 checksum of the PAR file from the PROOF-INF/md5.txt file.
720 /// Returns a pointer to a TMD5 object, transferring ownership to the caller
721 
722 TMD5 *TPackMgr::ReadMD5(const char *pack)
723 {
724  TString pn(pack);
725  if (pn.EndsWith(".par")) pn.Remove(pn.Last('.'));
726 
727  TString md5f = TString::Format("%s/%s/PROOF-INF/md5.txt", fDir.Data(), pn.Data());
728  TLockPathGuard lp(&fLock, kTRUE);
729  return TMD5::ReadChecksum(md5f);
730 }
731 
732 
733 ////////////////////////////////////////////////////////////////////////////////
734 /// Read MD5 checksum of the PAR file from the PROOF-INF/md5.txt file.
735 /// Returns a pointer to a TMD5 object, transferring ownership to the caller
736 
737 Int_t TPackMgr::Unpack(const char *pack, TMD5 *sum)
738 {
739  Int_t rc = 0;
740  TString fn(pack), pn(pack);
741  if (!fn.EndsWith(".par")) fn += ".par";
742  if (pn.EndsWith(".par")) pn.Remove(pn.Last('.'));
743 
744  // Find gunzip...
745  char *gunzip = gSystem->Which(gSystem->Getenv("PATH"), kGUNZIP, kExecutePermission);
746  if (gunzip) {
747  // untar package
748  TString cmd;
749  cmd.Form(kUNTAR, gunzip, fDir.Data(), fn.Data(), fDir.Data());
750  rc = gSystem->Exec(cmd);
751  if (rc != 0)
752  Error("Unpack", "failure executing: %s (rc: %d)", cmd.Data(), rc);
753  delete [] gunzip;
754  } else {
755  Error("Unpack", "%s not found", kGUNZIP);
756  rc = -2;
757  }
758  // check that fDir/pack now exists
760  // par file did not unpack itself in the expected directory, failure
761  rc = -1;
762  Error("Unpack", "package %s did not unpack into %s", fn.Data(), pn.Data());
763  } else {
764  // store md5 in package/PROOF-INF/md5.txt
765  if (sum) {
766  TString md5f = TString::Format("%s/%s/PROOF-INF/md5.txt", fDir.Data(), pn.Data());
767  TMD5::WriteChecksum(md5f, sum);
768  }
769  }
770 
771  return rc;
772 }
773 
774 ////////////////////////////////////////////////////////////////////////////////
775 /// Install package from par (unpack the file in the directory); par can be an
776 /// URL for remote retrieval. If rmold is kTRUE an existing version of the package
777 /// is removed if existing.
778 /// Returns 0 on success, <0 otherwise
779 
780 Int_t TPackMgr::Install(const char *parpath, Bool_t rmold)
781 {
782  Int_t rc = 0;
783 
784  Info("Install", "installing %s ...", parpath);
785  const char *par = gSystem->ExpandPathName(parpath);
786 
787  // Does par exists?
789  Error("Install", "%s is invalid", par);
790  return -1;
791  }
792  TString parname = gSystem->BaseName(par);
793  TString pack = parname(0, parname.Last('.'));
794  TString dest = TString::Format("%s/%s", fDir.Data(), parname.Data());
795  TString psrc = par, ssrc;
796  TMD5 *sums = 0, *md5 = 0, *md5d = 0;
797 
798  // Check if we need to download: get the remote checksum
799  // Retrieve the checksum of the file, if available
800  // Dowload checksum file, if available
801  TString dldir;
802  if (GetDownloadDir(dldir) != 0) {
803  Error("Install", "could not create/get download directory");
804  return -1;
805  }
806 
808 
809  TString parsum(par);
810  parsum.ReplaceAll(".par", ".md5sum");
811  if (!gSystem->AccessPathName(parsum, kReadPermission)) {
812  ssrc.Form("%s/%s", dldir.Data(), gSystem->BaseName(parsum));
813  if (!TFile::Cp(parsum, ssrc)) {
814  Warning("Install", "could not retrieve %s", parsum.Data());
815  } else {
816  md5 = TMD5::ReadChecksum(ssrc);
817  }
818  }
819 
820  // Do we have already the file?
821  Bool_t parexists = (!gSystem->AccessPathName(dest)) ? kTRUE : kFALSE;
822 
823  Bool_t install = kTRUE;
824  // If yes and we are asked to clean the old one, do it
825  if (parexists) {
826  install = kFALSE;
827  if (rmold) {
828  // Asked to remove: do it
829  if (Remove(pack, kFALSE) < 0) {
830  Error("Install", "could not remove existing version of '%s'", pack.Data());
831  return -1;
832  }
833  install = kTRUE;
834  } else {
835  if (!md5) {
837  if (ft == TFile::kWeb || ft == TFile::kNet) {
838  psrc.Form("%s/%s", dldir.Data(), parname.Data());
839  if (!TFile::Cp(par, psrc)) {
840  Error("Install", "could not retrieve %s", par);
841  return -1;
842  }
843  }
844  // psrc is either the original par or the downloaded path
845  md5 = TMD5::FileChecksum(psrc);
846  }
847  // Now we need to compare with the local one
848  sums = TMD5::FileChecksum(dest);
849  if (*sums != *md5) install = kTRUE;
850  }
851  }
852  if (sums) delete sums;
853 
854  // Install if required
855  if (install) {
856  if (!TFile::Cp(psrc, dest)) {
857  Error("Install", "could not copy %s to %s", psrc.Data(), dest.Data());
858  return -1;
859  }
860  }
861  md5d = TMD5::FileChecksum(dest);
862 
863  if (md5 && *md5 != *md5d)
864  Warning("Install", "checksums do not match:\n\tdownloaded:\t%s\n\texpected:\t%s",
865  md5d->AsString(), md5->AsString());
866  if (Unpack(pack, md5d) != 0) {
867  Error("Install", "could not unpack %s", dest.Data());
868  rc = -1;
869  }
870  if (md5) delete md5;
871  return rc;
872 }
873 
874 //---------------------------------------------------------------------------------------------------
875 // Static methods
876 //---------------------------------------------------------------------------------------------------
877 
878 ////////////////////////////////////////////////////////////////////////////////
879 /// Parse one or more paths as possible sources of packages
880 /// Returns number of paths added; or -1 in case of problems
881 
883 {
884  Int_t ng = 0;
885  // List of directories where to look for global packages
886  TString globpack(paths);
887  if (globpack.Length() > 0) {
888  Int_t from = 0;
889  TString ldir;
890  while (globpack.Tokenize(ldir, from, ":")) {
891  if (gSystem->AccessPathName(ldir, kReadPermission)) {
892  ::Warning("TPackMgr::RegisterGlobalPath",
893  "directory for global packages %s does not"
894  " exist or is not readable", ldir.Data());
895  } else {
896  // Add to the list, key will be "G<ng>", i.e. "G0", "G1", ...
897  TString key;
898  key.Form("G%d", ng++);
899  if (!fgGlobalPackMgrList) {
902  }
903  TPackMgr *pmgr = new TPackMgr(ldir);
904  pmgr->SetName(key);
905  fgGlobalPackMgrList->Add(pmgr);
906  ::Info("TPackMgr::RegisterGlobalPath",
907  "manager for global packages directory %s added to the list",
908  ldir.Data());
909  }
910  }
911  }
912  // Number of registered packages
913  return ng;
914 }
915 
916 
917 ////////////////////////////////////////////////////////////////////////////////
918 /// Get the package manager having 'pack'; priority is given to packmgr, if
919 /// defined.
920 /// Returns packmgr or nullptr
921 
922 TPackMgr *TPackMgr::GetPackMgr(const char *pack, TPackMgr *packmgr)
923 {
924  if (packmgr && packmgr->Has(pack)) return packmgr;
925 
927  // Scan the list of global packages managers
929  TPackMgr *pm = 0;
930  while ((pm = (TPackMgr *)nxpm())) {
931  if (pm->Has(pack)) return pm;
932  }
933  }
934  return nullptr;
935 }
936 
937 
938 ////////////////////////////////////////////////////////////////////////////////
939 /// Get the full path to PAR, looking also in the global dirs.
940 /// Returns -1 if not found, 0 if available in global dirs, 1 if it can be
941 /// uploaded from the local package dir.
942 /// For the cases >= 0, par is filled with the path of the PAR file
943 
944 Int_t TPackMgr::FindParPath(TPackMgr *packmgr, const char *pack, TString &par)
945 {
946  // Try the package dir
947  if (packmgr && packmgr->GetParPath(pack, par) == 0) return 1;
948 
949  // Try global package dirs
951  // Scan the list of global packages dirs
953  TPackMgr *pm = 0;
954  while ((pm = (TPackMgr *)nxpm())) {
955  if (pm->GetParPath(pack, par) == 0) {
956  // Package found, stop searching
957  break;
958  }
959  par = "";
960  }
961  if (par.Length() > 0) return 0;
962  }
963  return -1;
964 }
965 
966 ////////////////////////////////////////////////////////////////////////////////
967 /// Check if the package is enabled; priority is given to packmgr, if
968 /// defined.
969 /// Returns kTRUE if enabled
970 
971 Bool_t TPackMgr::IsEnabled(const char *pack, TPackMgr *packmgr)
972 {
973  if (packmgr && packmgr->IsPackageEnabled(pack)) return kTRUE;
974 
976  // Scan the list of global packages managers
978  TPackMgr *pm = 0;
979  while ((pm = (TPackMgr *)nxpm())) {
980  if (pm->IsPackageEnabled(pack)) return kTRUE;
981  }
982  }
983  // Not Enabled
984  return kFALSE;
985 }
static Int_t FindParPath(TPackMgr *packmgr, const char *pack, TString &par)
Get the full path to PAR, looking also in the global dirs.
Definition: TPackMgr.cxx:944
static THashList * fgGlobalPackMgrList
Definition: TPackMgr.h:59
virtual const char * BaseName(const char *pathname)
Base name of a file name. Base name of /user/root is root.
Definition: TSystem.cxx:929
virtual Bool_t AccessPathName(const char *path, EAccessMode mode=kFileExists)
Returns FALSE if one can access a file using the specified access mode.
Definition: TSystem.cxx:1266
TList * GetListOfLines() const
Definition: TMacro.h:53
double par[1]
Definition: unuranDistr.cxx:38
Bool_t IsPackageEnabled(const char *pack)
Definition: TPackMgr.h:92
TMD5 * ReadMD5(const char *pack)
Read MD5 checksum of the PAR file from the PROOF-INF/md5.txt file.
Definition: TPackMgr.cxx:722
The PROOF package manager contains tools to manage packages.
Definition: TPackMgr.h:47
static long int sum(long int i)
Definition: Factory.cxx:1786
void GetEnabledPackages(TString &packlist)
Method to get a semi-colon separated list with the names of the enabled packages. ...
Definition: TPackMgr.cxx:515
Int_t Unlock()
Unlock the directory.
Definition: TLockPath.cxx:71
virtual int GetPid()
Get process id.
Definition: TSystem.cxx:712
Int_t Lock(Bool_t shared=kFALSE)
Definition: TLockPath.cxx:34
static Bool_t IsEnabled(const char *pack, TPackMgr *packmgr=nullptr)
Check if the package is enabled; priority is given to packmgr, if defined.
Definition: TPackMgr.cxx:971
const char * GetName() const
Returns name of object.
Definition: TMap.h:120
virtual void Info(const char *method, const char *msgfmt,...) const
Issue info message.
Definition: TObject.cxx:899
const char *const kRM
Definition: TPackMgr.cxx:41
virtual const char * WorkingDirectory()
Return working directory.
Definition: TSystem.cxx:866
static TMD5 * FileChecksum(const char *file)
Returns checksum of specified file.
Definition: TMD5.cxx:474
const char *const kCP
Definition: TProof.h:171
Collectable string class.
Definition: TObjString.h:32
virtual ~TPackMgr()
Destroy a TPackMgr instance.
Definition: TPackMgr.cxx:78
TString & ReplaceAll(const TString &s1, const TString &s2)
Definition: TString.h:635
const char *const kUNTAR3
Definition: TPackMgr.cxx:44
virtual void SetOwner(Bool_t enable=kTRUE)
Set whether this collection is the owner (enable==true) of its content.
Int_t Unload(const char *pack)
Method to unload a package.
Definition: TPackMgr.cxx:425
Int_t GetParPath(const char *pack, TString &path)
Method to get the path of the PAR file for package &#39;pack&#39;.
Definition: TPackMgr.cxx:535
virtual Bool_t ChangeDirectory(const char *path)
Change directory.
Definition: TSystem.cxx:857
static TPackMgr * GetPackMgr(const char *pack, TPackMgr *packmgr=nullptr)
Get the package manager having &#39;pack&#39;; priority is given to packmgr, if defined.
Definition: TPackMgr.cxx:922
#define gROOT
Definition: TROOT.h:364
Class supporting a collection of lines with C++ code.
Definition: TMacro.h:33
Basic string class.
Definition: TString.h:137
Int_t Clean(const char *pack)
Clean dir for package &#39;pack&#39; Return -1 in case of error, 0 otherwise.
Definition: TPackMgr.cxx:590
Int_t GetPackDir(const char *pack, TString &pdir)
Method to get the path of the dir for package &#39;pack&#39;.
Definition: TPackMgr.cxx:501
int Int_t
Definition: RtypesCore.h:41
Int_t Load(const char *pack, TList *optls=0)
Method to load a package taking an option list Return -1 on error, 0 otherwise.
Definition: TPackMgr.cxx:234
bool Bool_t
Definition: RtypesCore.h:59
const Bool_t kFALSE
Definition: Rtypes.h:92
TList * fEnabledPackages
Definition: TPackMgr.h:57
#define gInterpreter
Definition: TInterpreter.h:517
virtual char * Which(const char *search, const char *file, EAccessMode mode=kFileExists)
Find location of file in a search path.
Definition: TSystem.cxx:1512
Each ROOT method (see TMethod) has a linked list of its arguments.
Definition: TMethodArg.h:33
TString fPfx
Definition: TPackMgr.h:56
TPackMgr(const TPackMgr &)
void Show(const char *title=0)
Show available packages.
Definition: TPackMgr.cxx:562
Int_t Unpack(const char *pack, TMD5 *sum=0)
Read MD5 checksum of the PAR file from the PROOF-INF/md5.txt file.
Definition: TPackMgr.cxx:737
virtual TObject * Clone(const char *newname="") const
Make a clone of an collection using the Streamer facility.
const char *const kGUNZIP
Definition: TPackMgr.cxx:45
UInt_t Hash(ECaseCompare cmp=kExact) const
Return hash value.
Definition: TString.cxx:606
virtual int mkdir(const char *name, Bool_t recursive=kFALSE)
Make a file system directory.
Definition: TSystem.cxx:901
virtual TObject * FindObject(const char *name) const
Find an object in this list using its name.
Definition: TList.cxx:497
virtual Bool_t Cp(const char *dst, Bool_t progressbar=kTRUE, UInt_t buffersize=1000000)
Allows to copy this file to the dst URL.
Definition: TFile.cxx:4805
Int_t Install(const char *par, Bool_t rmold=kFALSE)
Install package from par (unpack the file in the directory); par can be an URL for remote retrieval...
Definition: TPackMgr.cxx:780
TList * GetListOfEnabled() const
Get list of enabled packages Returns a pointer to a TList object, transferring ownership to the calle...
Definition: TPackMgr.cxx:658
TString fDir
Definition: TPackMgr.h:54
virtual const char * GetDirEntry(void *dirp)
Get a directory entry. Returns 0 if no more entries.
Definition: TSystem.cxx:848
#define SafeDelete(p)
Definition: RConfig.h:507
virtual int Unlink(const char *name)
Unlink, i.e. remove, a file.
Definition: TSystem.cxx:1347
virtual const char * ClassName() const
Returns name of class to which the object belongs.
Definition: TObject.cxx:188
virtual TObjString * GetLineWith(const char *text) const
Search the first line containing text.
Definition: TMacro.cxx:300
static TString Format(const char *fmt,...)
Static method which formats a string using a printf style format descriptor and return a TString...
Definition: TString.cxx:2335
const char *const kLS
Definition: TPackMgr.cxx:42
THashList implements a hybrid collection class consisting of a hash table and a list to store TObject...
Definition: THashList.h:36
This code implements the MD5 message-digest algorithm.
Definition: TMD5.h:46
static EFileType GetType(const char *name, Option_t *option="", TString *prefix=0)
Resolve the file type as a function of the protocol field in &#39;name&#39;.
Definition: TFile.cxx:4622
void SetName(const char *name)
Set the name of the TNamed.
Definition: TPackMgr.h:72
void ShowEnabled(const char *title=0)
Show enabled packages.
Definition: TPackMgr.cxx:676
void Init(const TFunction *func)
Initialize the method invocation environment based on the TFunction object.
virtual const char * Getenv(const char *env)
Get environment variable.
Definition: TSystem.cxx:1628
Bool_t EndsWith(const char *pat, ECaseCompare cmp=kExact) const
Return true if string ends with the specified string.
Definition: TString.cxx:2221
TLockPath fLock
Definition: TPackMgr.h:55
virtual const char * TempDirectory() const
Return a user configured or systemwide directory to create temporary files in.
Definition: TSystem.cxx:1448
Method or function calling interface.
Definition: TMethodCall.h:41
const char * GetTitle() const
Returns title of object.
Definition: TPackMgr.h:73
virtual Bool_t Load() const
Load the macro into the interpreter.
Definition: TMacro.cxx:246
A doubly linked list.
Definition: TList.h:47
TList * GetList() const
Get list of available packages Returns a pointer to a TList object, transferring ownership to the cal...
Definition: TPackMgr.cxx:635
const char * GetName() const
Returns name of object.
Definition: TObjString.h:42
Named parameter, streamable and storable.
Definition: TParameter.h:49
EFileType
File type.
Definition: TFile.h:172
Int_t Build(const char *pack, Int_t opt=TPackMgr::kCheckROOT)
Method to build a package.
Definition: TPackMgr.cxx:101
Bool_t IsInDir(const char *path)
Method to check if &#39;path&#39; is in the managed directory Return kTRUE or kFALSE.
Definition: TPackMgr.cxx:492
Int_t GetNargs() const
Number of function arguments.
Definition: TFunction.cxx:164
void SetName(const char *path)
Definition: TLockPath.h:40
virtual TObject * First() const
Return the first object in the list. Returns 0 when list is empty.
Definition: TList.cxx:557
TRandom2 r(17)
R__EXTERN TSystem * gSystem
Definition: TSystem.h:549
static Int_t WriteChecksum(const char *file, const TMD5 *md5)
Writes checksum in ASCII format to specified file.
Definition: TMD5.cxx:452
SVector< double, 2 > v
Definition: Dict.h:5
virtual TObject * Remove(TObject *obj)
Remove object from the list.
Definition: TList.cxx:675
Bool_t Gets(FILE *fp, Bool_t chop=kTRUE)
Read one line from the stream, including the , or until EOF.
Definition: Stringio.cxx:198
void Form(const char *fmt,...)
Formats a string using a printf style format descriptor.
Definition: TString.cxx:2322
const char *const kUNTAR
Definition: TPackMgr.cxx:43
virtual void Error(const char *method, const char *msgfmt,...) const
Issue error message.
Definition: TObject.cxx:925
Ssiz_t Length() const
Definition: TString.h:390
const char * GetName() const
Returns name of object.
Definition: TPackMgr.h:71
virtual Int_t ReadFile(const char *filename)
Read lines in filename in this macro.
Definition: TMacro.cxx:335
const char *const kUNTAR2
Definition: TProof.h:175
virtual Int_t Exec(const char *shellcmd)
Execute a command.
Definition: TSystem.cxx:658
virtual void FreeDirectory(void *dirp)
Free a directory.
Definition: TSystem.cxx:840
TString GetString() const
Definition: TObjString.h:50
#define Printf
Definition: TGeoToOCC.h:18
TString & Remove(Ssiz_t pos)
Definition: TString.h:616
long Long_t
Definition: RtypesCore.h:50
Class used by TMap to store (key,value) pairs.
Definition: TMap.h:106
TObjArray * Tokenize(const TString &delim) const
This function is used to isolate sequential tokens in a TString.
Definition: TString.cxx:2241
virtual const char * GetIncludePath()
Get the list of include path.
Definition: TSystem.cxx:3843
#define ClassImp(name)
Definition: Rtypes.h:279
double f(double x)
void ResetParam()
Reset parameter list. To be used before the first call the SetParam().
TList * GetListOfMethodArgs()
Return list containing the TMethodArgs of a TFunction.
Definition: TFunction.cxx:126
virtual const char * HostName()
Return the system&#39;s host name.
Definition: TSystem.cxx:308
virtual int Symlink(const char *from, const char *to)
Create a symbolic link from file1 to file2.
Definition: TSystem.cxx:1338
Ssiz_t Last(char c) const
Find last occurrence of a character c.
Definition: TString.cxx:865
TPackMgrLog_t fLogger
Definition: TPackMgr.h:52
static Int_t RegisterGlobalPath(const char *paths)
Parse one or more paths as possible sources of packages Returns number of paths added; or -1 in case ...
Definition: TPackMgr.cxx:882
Bool_t IsNull() const
Definition: TString.h:387
Bool_t Has(const char *pack)
Method to check if this package manager has package &#39;pack&#39;.
Definition: TPackMgr.cxx:472
Mother of all ROOT objects.
Definition: TObject.h:37
Global functions class (global functions are obtained from CINT).
Definition: TFunction.h:30
Int_t GetDownloadDir(TString &dldir)
Method to get the download dir; create if not existing Return -1 in case of error (not found; not cre...
Definition: TPackMgr.cxx:548
virtual void Add(TObject *obj)
Definition: TList.h:81
void Execute(const char *, const char *, int *=0)
Execute method on this object with the given parameter string, e.g.
Definition: TMethodCall.h:68
#define dest(otri, vertexptr)
Definition: triangle.c:1040
void SetParam(Long_t l)
Add a long method parameter.
void Log(const char *msg)
Wrapper to notofuer / logger.
Definition: TPackMgr.cxx:87
virtual void * OpenDirectory(const char *name)
Open a directory. Returns 0 if directory does not exist.
Definition: TSystem.cxx:831
R__EXTERN Int_t gDebug
Definition: Rtypes.h:128
Int_t Remove(const char *pack=0, Bool_t dolock=kTRUE)
Remove package &#39;pack&#39; If &#39;pack&#39; is null or empty all packages are cleared.
Definition: TPackMgr.cxx:607
virtual void AddIncludePath(const char *includePath)
Add includePath to the already set include path.
Definition: TSystem.cxx:4010
const AParamType & GetVal() const
Definition: TParameter.h:77
TMD5 * GetMD5(const char *pack)
Get MD5 checksum of the PAR file corresponding to given package Returns a pointer to a TMD5 object...
Definition: TPackMgr.cxx:706
virtual Bool_t ExpandPathName(TString &path)
Expand a pathname getting rid of special shell characters like ~.
Definition: TSystem.cxx:1244
virtual const char * GetName() const
Returns name of object.
Definition: TObject.cxx:416
virtual Int_t GetSize() const
Definition: TCollection.h:95
static TMD5 * ReadChecksum(const char *file)
Returns checksum stored in ASCII in specified file.
Definition: TMD5.cxx:422
const Bool_t kTRUE
Definition: Rtypes.h:91
void SetString(const char *s)
Definition: TObjString.h:49
virtual void Warning(const char *method, const char *msgfmt,...) const
Issue warning message.
Definition: TObject.cxx:911
static void DefaultLogger(const char *msg)
Definition: TPackMgr.cxx:56
virtual void SetIncludePath(const char *includePath)
IncludePath should contain the list of compiler flags to indicate where to find user defined header f...
Definition: TSystem.cxx:4045
virtual const char * GetTitle() const
Returns title of object.
Definition: TNamed.h:52
const char * Data() const
Definition: TString.h:349