Logo ROOT   6.16/01
Reference Guide
TFileMerger.cxx
Go to the documentation of this file.
1// @(#)root/io:$Id$
2// Author: Andreas Peters + Fons Rademakers + Rene Brun 26/5/2005
3
4/*************************************************************************
5 * Copyright (C) 1995-2005, 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/**
13\class TFileMerger TFileMerger.cxx
14\ingroup IO
15
16This class provides file copy and merging services.
17
18It can be used to copy files (not only ROOT files), using TFile or
19any of its remote file access plugins. It is therefore useful in
20a Grid environment where the files might be accessible via Castor,
21rfio, dcap, etc.
22The merging interface allows files containing histograms and trees
23to be merged, like the standalone hadd program.
24*/
25
26#include "TFileMerger.h"
27#include "TDirectory.h"
28#include "TUrl.h"
29#include "TFile.h"
30#include "TUUID.h"
31#include "TSystem.h"
32#include "TKey.h"
33#include "THashList.h"
34#include "TObjString.h"
35#include "TClass.h"
36#include "TMethodCall.h"
37#include "Riostream.h"
38#include "TFileMergeInfo.h"
39#include "TClassRef.h"
40#include "TROOT.h"
41#include "TMemFile.h"
42#include "TVirtualMutex.h"
43
44#ifdef WIN32
45// For _getmaxstdio
46#include <stdio.h>
47#else
48// For getrlimit
49#include <sys/time.h>
50#include <sys/resource.h>
51#endif
52
53#include <cstring>
54
56
59
60static const Int_t kCpProgress = BIT(14);
61static const Int_t kCintFileNumber = 100;
62////////////////////////////////////////////////////////////////////////////////
63/// Return the maximum number of allowed opened files minus some wiggle room
64/// for CINT or at least of the standard library (stdio).
65
67{
68 int maxfiles;
69#ifdef WIN32
70 maxfiles = _getmaxstdio();
71#else
72 rlimit filelimit;
73 if (getrlimit(RLIMIT_NOFILE,&filelimit)==0) {
74 maxfiles = filelimit.rlim_cur;
75 } else {
76 // We could not get the value from getrlimit, let's return a reasonable default.
77 maxfiles = 512;
78 }
79#endif
80 if (maxfiles > kCintFileNumber) {
81 return maxfiles - kCintFileNumber;
82 } else if (maxfiles > 5) {
83 return maxfiles - 5;
84 } else {
85 return maxfiles;
86 }
87}
88
89////////////////////////////////////////////////////////////////////////////////
90/// Create file merger object.
91
93 : fMaxOpenedFiles( R__GetSystemMaxOpenedFiles() ),
94 fLocal(isLocal), fHistoOneGo(histoOneGo)
95{
98
100 gROOT->GetListOfCleanups()->Add(this);
101}
102
103////////////////////////////////////////////////////////////////////////////////
104/// Cleanup.
105
107{
108 {
110 gROOT->GetListOfCleanups()->Remove(this);
111 }
113}
114
115////////////////////////////////////////////////////////////////////////////////
116/// Reset merger file list.
117
119{
124}
125
126////////////////////////////////////////////////////////////////////////////////
127/// Add file to file merger.
128
129Bool_t TFileMerger::AddFile(const char *url, Bool_t cpProgress)
130{
131 if (fPrintLevel > 0) {
132 Printf("%s Source file %d: %s", fMsgPrefix.Data(), fFileList.GetEntries() + fExcessFiles.GetEntries() + 1, url);
133 }
134
135 TFile *newfile = 0;
136 TString localcopy;
137
138 if (fFileList.GetEntries() >= (fMaxOpenedFiles-1)) {
139
140 TObjString *urlObj = new TObjString(url);
141 fMergeList.Add(urlObj);
142
143 urlObj = new TObjString(url);
144 urlObj->SetBit(kCpProgress);
145 fExcessFiles.Add(urlObj);
146 return kTRUE;
147 }
148
149 // We want gDirectory untouched by anything going on here
151
152 if (fLocal) {
153 TUUID uuid;
154 localcopy.Form("file:%s/ROOTMERGE-%s.root", gSystem->TempDirectory(), uuid.AsString());
155 if (!TFile::Cp(url, localcopy, cpProgress)) {
156 Error("AddFile", "cannot get a local copy of file %s", url);
157 return kFALSE;
158 }
159 newfile = TFile::Open(localcopy, "READ");
160 } else {
161 newfile = TFile::Open(url, "READ");
162 }
163
164 // Zombie files should also be skipped
165 if (newfile && newfile->IsZombie()) {
166 delete newfile;
167 newfile = 0;
168 }
169
170 if (!newfile) {
171 if (fLocal)
172 Error("AddFile", "cannot open local copy %s of URL %s",
173 localcopy.Data(), url);
174 else
175 Error("AddFile", "cannot open file %s", url);
176 return kFALSE;
177 } else {
179
180 newfile->SetBit(kCanDelete);
181 fFileList.Add(newfile);
182
183 TObjString *urlObj = new TObjString(url);
184 fMergeList.Add(urlObj);
185
186 return kTRUE;
187 }
188}
189
190////////////////////////////////////////////////////////////////////////////////
191/// Add the TFile to this file merger and *do not* give ownership of the TFile to this
192/// object.
193///
194/// Return kTRUE if the addition was successful.
195
197{
198 return AddFile(source,kFALSE,cpProgress);
199}
200
201////////////////////////////////////////////////////////////////////////////////
202/// Add the TFile to this file merger and give ownership of the TFile to this
203/// object (unless kFALSE is returned).
204///
205/// Return kTRUE if the addition was successful.
206
208{
209 return AddFile(source,kTRUE,cpProgress);
210}
211
212////////////////////////////////////////////////////////////////////////////////
213/// Add the TFile to this file merger and give ownership of the TFile to this
214/// object (unless kFALSE is returned).
215///
216/// Return kTRUE if the addition was successful.
217
219{
220 if (source == 0 || source->IsZombie()) {
221 return kFALSE;
222 }
223
224 if (fPrintLevel > 0) {
225 Printf("%s Source file %d: %s",fMsgPrefix.Data(),fFileList.GetEntries()+1,source->GetName());
226 }
227
228 TFile *newfile = 0;
229 TString localcopy;
230
231 // We want gDirectory untouched by anything going on here
233 if (fLocal && !source->InheritsFrom(TMemFile::Class())) {
234 TUUID uuid;
235 localcopy.Form("file:%s/ROOTMERGE-%s.root", gSystem->TempDirectory(), uuid.AsString());
236 if (!source->Cp(localcopy, cpProgress)) {
237 Error("AddFile", "cannot get a local copy of file %s", source->GetName());
238 return kFALSE;
239 }
240 newfile = TFile::Open(localcopy, "READ");
241 // Zombie files should also be skipped
242 if (newfile && newfile->IsZombie()) {
243 delete newfile;
244 newfile = 0;
245 }
246 } else {
247 newfile = source;
248 }
249
250 if (!newfile) {
251 if (fLocal)
252 Error("AddFile", "cannot open local copy %s of URL %s",
253 localcopy.Data(), source->GetName());
254 else
255 Error("AddFile", "cannot open file %s", source->GetName());
256 return kFALSE;
257 } else {
259
260 if (own || newfile != source) {
261 newfile->SetBit(kCanDelete);
262 } else {
263 newfile->ResetBit(kCanDelete);
264 }
265 fFileList.Add(newfile);
266
267 TObjString *urlObj = new TObjString(source->GetName());
268 fMergeList.Add(urlObj);
269
270 if (newfile != source && own) {
271 delete source;
272 }
273 return kTRUE;
274 }
275}
276
277////////////////////////////////////////////////////////////////////////////////
278/// Open merger output file.
279
280Bool_t TFileMerger::OutputFile(const char *outputfile, Bool_t force, Int_t compressionLevel)
281{
282 return OutputFile(outputfile,(force?"RECREATE":"CREATE"),compressionLevel);
283}
284
285////////////////////////////////////////////////////////////////////////////////
286/// Open merger output file.
287
288Bool_t TFileMerger::OutputFile(const char *outputfile, Bool_t force)
289{
290 Bool_t res = OutputFile(outputfile,(force?"RECREATE":"CREATE"),1); // 1 is the same as the default from the TFile constructor.
292 return res;
293}
294
295////////////////////////////////////////////////////////////////////////////////
296/// Open merger output file.
297///
298/// The 'mode' parameter is passed to the TFile constructor as the option, it
299/// should be one of 'NEW','CREATE','RECREATE','UPDATE'
300/// 'UPDATE' is usually used in conjunction with IncrementalMerge.
301
302Bool_t TFileMerger::OutputFile(const char *outputfile, const char *mode, Int_t compressionLevel)
303{
304 // We want gDirectory untouched by anything going on here
306 if (TFile *outputFile = TFile::Open(outputfile, mode, "", compressionLevel))
307 return OutputFile(std::unique_ptr<TFile>(outputFile));
308
309 Error("OutputFile", "cannot open the MERGER output file %s", fOutputFilename.Data());
310 return kFALSE;
311}
312
313////////////////////////////////////////////////////////////////////////////////
314/// Set an output file opened externally by the users
315
316Bool_t TFileMerger::OutputFile(std::unique_ptr<TFile> outputfile)
317{
318 if (!outputfile || outputfile->IsZombie()) {
319 Error("OutputFile", "cannot open the MERGER output file %s", (outputfile) ? outputfile->GetName() : "");
320 return kFALSE;
321 }
322
323 if (!outputfile->IsWritable()) {
324 Error("OutputFile", "output file %s is not writable", outputfile->GetName());
325 return kFALSE;
326 }
327
329
330 TFile *oldfile = fOutputFile;
331 fOutputFile = 0; // This avoids the complaint from RecursiveRemove about the file being deleted which is here
332 // spurrious. (see RecursiveRemove).
333 SafeDelete(oldfile);
334
335 fOutputFilename = outputfile->GetName();
336 // We want gDirectory untouched by anything going on here
338 fOutputFile = outputfile.release(); // Transfer the ownership of the file.
339
340 return kTRUE;
341}
342
343////////////////////////////////////////////////////////////////////////////////
344/// Open merger output file. 'mode' is passed to the TFile constructor as the option, it should
345/// be one of 'NEW','CREATE','RECREATE','UPDATE'
346/// 'UPDATE' is usually used in conjunction with IncrementalMerge.
347
348Bool_t TFileMerger::OutputFile(const char *outputfile, const char *mode /* = "RECREATE" */)
349{
350 Bool_t res = OutputFile(outputfile,mode,1); // 1 is the same as the default from the TFile constructor.
352 return res;
353}
354
355////////////////////////////////////////////////////////////////////////////////
356/// Print list of files being merged.
357
359{
360 fFileList.Print(options);
361 fExcessFiles.Print(options);
362}
363
364////////////////////////////////////////////////////////////////////////////////
365/// Merge the files.
366///
367/// If no output file was specified it will write into
368/// the file "FileMerger.root" in the working directory. Returns true
369/// on success, false in case of error.
370
372{
373 return PartialMerge(kAll | kRegular);
374}
375
376////////////////////////////////////////////////////////////////////////////////
377/// Merge all objects in a directory
378///
379/// The type is defined by the bit values in TFileMerger::EPartialMergeType.
380
381Bool_t TFileMerger::MergeRecursive(TDirectory *target, TList *sourcelist, Int_t type /* = kRegular | kAll */)
382{
383 Bool_t status = kTRUE;
384 Bool_t onlyListed = kFALSE;
385 if (fPrintLevel > 0) {
386 Printf("%s Target path: %s",fMsgPrefix.Data(),target->GetPath());
387 }
388
389 // Get the dir name
390 TString path(target->GetPath());
391 // coverity[unchecked_value] 'target' is from a file so GetPath always returns path starting with filename:
392 path.Remove(0, std::strlen(target->GetFile()->GetPath()));
393
394 Int_t nguess = sourcelist->GetSize()+1000;
395 THashList allNames(nguess);
396 allNames.SetOwner(kTRUE);
397 // If the mode is set to skipping list objects, add names to the allNames list
398 if (type & kSkipListed) {
399 TObjArray *arr = fObjectNames.Tokenize(" ");
400 arr->SetOwner(kFALSE);
401 for (Int_t iname=0; iname<arr->GetEntriesFast(); iname++)
402 allNames.Add(arr->At(iname));
403 delete arr;
404 }
405 ((THashList*)target->GetList())->Rehash(nguess);
406 ((THashList*)target->GetListOfKeys())->Rehash(nguess);
407
408 TFileMergeInfo info(target);
410 info.fOptions = fMergeOptions;
412 info.fOptions.Append(" fast");
413 }
414
415 TFile *current_file;
416 TDirectory *current_sourcedir;
417 if (type & kIncremental) {
418 current_file = 0;
419 current_sourcedir = target;
420 } else {
421 current_file = (TFile*)sourcelist->First();
422 current_sourcedir = current_file->GetDirectory(path);
423 }
424 while (current_file || current_sourcedir) {
425 // When current_sourcedir != 0 and current_file == 0 we are going over the target
426 // for an incremental merge.
427 if (current_sourcedir && (current_file == 0 || current_sourcedir != target)) {
428
429 // loop over all keys in this directory
430 TIter nextkey( current_sourcedir->GetListOfKeys() );
431 TKey *key;
432 TString oldkeyname;
433
434 while ( (key = (TKey*)nextkey())) {
435
436 // Keep only the highest cycle number for each key for mergeable objects. They are stored
437 // in the (hash) list consecutively and in decreasing order of cycles, so we can continue
438 // until the name changes. We flag the case here and we act consequently later.
439 Bool_t alreadyseen = (oldkeyname == key->GetName()) ? kTRUE : kFALSE;
440
441 // Read in but do not copy directly the processIds.
442 if (strcmp(key->GetClassName(),"TProcessID") == 0) { key->ReadObj(); continue;}
443
444 // If we have already seen this object [name], we already processed
445 // the whole list of files for this objects and we can just skip it
446 // and any related cycles.
447 if (allNames.FindObject(key->GetName())) {
448 oldkeyname = key->GetName();
449 continue;
450 }
451
453 if (!cl) {
454 Info("MergeRecursive", "cannot indentify object type (%s), name: %s title: %s",
455 key->GetClassName(), key->GetName(), key->GetTitle());
456 continue;
457 }
458 // For mergeable objects we add the names in a local hashlist handling them
459 // again (see above)
460 if (cl->GetMerge() || cl->InheritsFrom(TDirectory::Class()) ||
461 (cl->IsTObject() &&
462 (cl->GetMethodWithPrototype("Merge", "TCollection*,TFileMergeInfo*") ||
463 cl->GetMethodWithPrototype("Merge", "TCollection*"))))
464 allNames.Add(new TObjString(key->GetName()));
465
467 // Skip the TTree objects and any related cycles.
468 oldkeyname = key->GetName();
469 continue;
470 }
471 // Check if only the listed objects are to be merged
472 if (type & kOnlyListed) {
473 onlyListed = kFALSE;
474 oldkeyname = key->GetName();
475 oldkeyname += " ";
476 onlyListed = fObjectNames.Contains(oldkeyname);
477 oldkeyname = key->GetName();
478 if ((!onlyListed) && (!cl->InheritsFrom(TDirectory::Class()))) continue;
479 }
480
481 if (!(type&kResetable && type&kNonResetable)) {
482 // If neither or both are requested at the same time, we merger both types.
483 if (!(type&kResetable)) {
484 if (cl->GetResetAfterMerge()) {
485 // Skip the object with a reset after merge routine (TTree and other incrementally mergeable objects)
486 oldkeyname = key->GetName();
487 continue;
488 }
489 }
490 if (!(type&kNonResetable)) {
491 if (!cl->GetResetAfterMerge()) {
492 // Skip the object without a reset after merge routine (Histograms and other non incrementally mergeable objects)
493 oldkeyname = key->GetName();
494 continue;
495 }
496 }
497 }
498 // read object from first source file
499 TObject *obj;
500 if (type & kIncremental) {
501 obj = current_sourcedir->GetList()->FindObject(key->GetName());
502 if (!obj) {
503 obj = key->ReadObj();
504 }
505 } else {
506 obj = key->ReadObj();
507 }
508 if (!obj) {
509 Info("MergeRecursive", "could not read object for key {%s, %s}",
510 key->GetName(), key->GetTitle());
511 continue;
512 }
513 // if (cl->IsTObject())
514 // obj->ResetBit(kMustCleanup);
515 if (cl->IsTObject() && cl != obj->IsA()) {
516 Error("MergeRecursive", "TKey and object retrieve disagree on type (%s vs %s). Continuing with %s.",
517 key->GetClassName(), obj->IsA()->GetName(), obj->IsA()->GetName());
518 cl = obj->IsA();
519 }
520 Bool_t canBeMerged = kTRUE;
521
522 if ( cl->InheritsFrom( TDirectory::Class() ) ) {
523 // it's a subdirectory
524
525 target->cd();
526 TDirectory *newdir;
527
528 // For incremental or already seen we may have already a directory created
529 if (type & kIncremental || alreadyseen) {
530 newdir = target->GetDirectory(obj->GetName());
531 if (!newdir) {
532 newdir = target->mkdir( obj->GetName(), obj->GetTitle() );
533 // newdir->ResetBit(kMustCleanup);
534 }
535 } else {
536 newdir = target->mkdir( obj->GetName(), obj->GetTitle() );
537 // newdir->ResetBit(kMustCleanup);
538 }
539
540 // newdir is now the starting point of another round of merging
541 // newdir still knows its depth within the target file via
542 // GetPath(), so we can still figure out where we are in the recursion
543
544 // If this folder is a onlyListed object, merge everything inside.
545 if (onlyListed) type &= ~kOnlyListed;
546 status = MergeRecursive(newdir, sourcelist, type);
547 if (onlyListed) type |= kOnlyListed;
548 if (!status) return status;
549 } else if (cl->GetMerge()) {
550
551 // Check if already treated
552 if (alreadyseen) continue;
553
554 TList inputs;
556
557 // Loop over all source files and merge same-name object
558 TFile *nextsource = current_file ? (TFile*)sourcelist->After( current_file ) : (TFile*)sourcelist->First();
559 if (nextsource == 0) {
560 // There is only one file in the list
561 ROOT::MergeFunc_t func = cl->GetMerge();
562 func(obj, &inputs, &info);
563 info.fIsFirst = kFALSE;
564 } else {
565 do {
566 // make sure we are at the correct directory level by cd'ing to path
567 TDirectory *ndir = nextsource->GetDirectory(path);
568 if (ndir) {
569 // For consistency (and persformance), we reset the MustCleanup be also for those
570 // 'key' retrieved indirectly.
571 // ndir->ResetBit(kMustCleanup);
572 ndir->cd();
573 TKey *key2 = (TKey*)ndir->GetListOfKeys()->FindObject(key->GetName());
574 if (key2) {
575 TObject *hobj = key2->ReadObj();
576 if (!hobj) {
577 Info("MergeRecursive", "could not read object for key {%s, %s}; skipping file %s",
578 key->GetName(), key->GetTitle(), nextsource->GetName());
579 nextsource = (TFile*)sourcelist->After(nextsource);
580 continue;
581 }
582 // Set ownership for collections
583 if (hobj->InheritsFrom(TCollection::Class())) {
584 ((TCollection*)hobj)->SetOwner();
585 }
586 hobj->ResetBit(kMustCleanup);
587 inputs.Add(hobj);
588 if (!oneGo) {
589 ROOT::MergeFunc_t func = cl->GetMerge();
590 Long64_t result = func(obj, &inputs, &info);
591 info.fIsFirst = kFALSE;
592 if (result < 0) {
593 Error("MergeRecursive", "calling Merge() on '%s' with the corresponding object in '%s'",
594 obj->GetName(), nextsource->GetName());
595 }
596 inputs.Delete();
597 }
598 }
599 }
600 nextsource = (TFile*)sourcelist->After( nextsource );
601 } while (nextsource);
602 // Merge the list, if still to be done
603 if (oneGo || info.fIsFirst) {
604 ROOT::MergeFunc_t func = cl->GetMerge();
605 func(obj, &inputs, &info);
606 info.fIsFirst = kFALSE;
607 inputs.Delete();
608 }
609 }
610 } else if (cl->IsTObject() &&
611 cl->GetMethodWithPrototype("Merge", "TCollection*,TFileMergeInfo*") ) {
612 // Object implements Merge(TCollection*,TFileMergeInfo*) and has a reflex dictionary ...
613
614 // Check if already treated
615 if (alreadyseen) continue;
616
617 TList listH;
618 TString listHargs;
619 listHargs.Form("(TCollection*)0x%lx,(TFileMergeInfo*)0x%lx", (ULong_t)&listH,(ULong_t)&info);
620
621 // Loop over all source files and merge same-name object
622 TFile *nextsource = current_file ? (TFile*)sourcelist->After( current_file ) : (TFile*)sourcelist->First();
623 if (nextsource == 0) {
624 // There is only one file in the list
625 Int_t error = 0;
626 obj->Execute("Merge", listHargs.Data(), &error);
627 info.fIsFirst = kFALSE;
628 if (error) {
629 Error("MergeRecursive", "calling Merge() on '%s' with the corresponding object in '%s'",
630 obj->GetName(), key->GetName());
631 }
632 } else {
633 while (nextsource) {
634 // make sure we are at the correct directory level by cd'ing to path
635 TDirectory *ndir = nextsource->GetDirectory(path);
636 if (ndir) {
637 // For consistency (and persformance), we reset the MustCleanup be also for those
638 // 'key' retrieved indirectly.
639 //ndir->ResetBit(kMustCleanup);
640 ndir->cd();
641 TKey *key2 = (TKey*)ndir->GetListOfKeys()->FindObject(key->GetName());
642 if (key2) {
643 TObject *hobj = key2->ReadObj();
644 if (!hobj) {
645 Info("MergeRecursive", "could not read object for key {%s, %s}; skipping file %s",
646 key->GetName(), key->GetTitle(), nextsource->GetName());
647 nextsource = (TFile*)sourcelist->After(nextsource);
648 continue;
649 }
650 // Set ownership for collections
651 if (hobj->InheritsFrom(TCollection::Class())) {
652 ((TCollection*)hobj)->SetOwner();
653 }
654 hobj->ResetBit(kMustCleanup);
655 listH.Add(hobj);
656 Int_t error = 0;
657 obj->Execute("Merge", listHargs.Data(), &error);
658 info.fIsFirst = kFALSE;
659 if (error) {
660 Error("MergeRecursive", "calling Merge() on '%s' with the corresponding object in '%s'",
661 obj->GetName(), nextsource->GetName());
662 }
663 listH.Delete();
664 }
665 }
666 nextsource = (TFile*)sourcelist->After( nextsource );
667 }
668 // Merge the list, if still to be done
669 if (info.fIsFirst) {
670 Int_t error = 0;
671 obj->Execute("Merge", listHargs.Data(), &error);
672 info.fIsFirst = kFALSE;
673 listH.Delete();
674 }
675 }
676 } else if (cl->IsTObject() &&
677 cl->GetMethodWithPrototype("Merge", "TCollection*") ) {
678 // Object implements Merge(TCollection*) and has a reflex dictionary ...
679
680 // Check if already treated
681 if (alreadyseen) continue;
682
683 TList listH;
684 TString listHargs;
685 listHargs.Form("((TCollection*)0x%lx)", (ULong_t)&listH);
686
687 // Loop over all source files and merge same-name object
688 TFile *nextsource = current_file ? (TFile*)sourcelist->After( current_file ) : (TFile*)sourcelist->First();
689 if (nextsource == 0) {
690 // There is only one file in the list
691 Int_t error = 0;
692 obj->Execute("Merge", listHargs.Data(), &error);
693 if (error) {
694 Error("MergeRecursive", "calling Merge() on '%s' with the corresponding object in '%s'",
695 obj->GetName(), key->GetName());
696 }
697 } else {
698 while (nextsource) {
699 // make sure we are at the correct directory level by cd'ing to path
700 TDirectory *ndir = nextsource->GetDirectory(path);
701 if (ndir) {
702 // For consistency (and persformance), we reset the MustCleanup be also for those
703 // 'key' retrieved indirectly.
704 //ndir->ResetBit(kMustCleanup);
705 ndir->cd();
706 TKey *key2 = (TKey*)ndir->GetListOfKeys()->FindObject(key->GetName());
707 if (key2) {
708 TObject *hobj = key2->ReadObj();
709 if (!hobj) {
710 Info("MergeRecursive", "could not read object for key {%s, %s}; skipping file %s",
711 key->GetName(), key->GetTitle(), nextsource->GetName());
712 nextsource = (TFile*)sourcelist->After(nextsource);
713 continue;
714 }
715 // Set ownership for collections
716 if (hobj->InheritsFrom(TCollection::Class())) {
717 ((TCollection*)hobj)->SetOwner();
718 }
719 hobj->ResetBit(kMustCleanup);
720 listH.Add(hobj);
721 Int_t error = 0;
722 obj->Execute("Merge", listHargs.Data(), &error);
723 info.fIsFirst = kFALSE;
724 if (error) {
725 Error("MergeRecursive", "calling Merge() on '%s' with the corresponding object in '%s'",
726 obj->GetName(), nextsource->GetName());
727 }
728 listH.Delete();
729 }
730 }
731 nextsource = (TFile*)sourcelist->After( nextsource );
732 }
733 // Merge the list, if still to be done
734 if (info.fIsFirst) {
735 Int_t error = 0;
736 obj->Execute("Merge", listHargs.Data(), &error);
737 info.fIsFirst = kFALSE;
738 listH.Delete();
739 }
740 }
741 } else {
742 // Object is of no type that we can merge
743 canBeMerged = kFALSE;
744 }
745
746 // now write the merged histogram (which is "in" obj) to the target file
747 // note that this will just store obj in the current directory level,
748 // which is not persistent until the complete directory itself is stored
749 // by "target->SaveSelf()" below
750 target->cd();
751
752 oldkeyname = key->GetName();
753 //!!if the object is a tree, it is stored in globChain...
754 if(cl->InheritsFrom( TDirectory::Class() )) {
755 //printf("cas d'une directory\n");
756
757 auto dirobj = dynamic_cast<TDirectory*>(obj);
758 TString dirpath(dirobj->GetPath());
759 // coverity[unchecked_value] 'target' is from a file so GetPath always returns path starting with filename:
760 dirpath.Remove(0, std::strlen(dirobj->GetFile()->GetPath()));
761
762 // Do not delete the directory if it is part of the output
763 // and we are in incremental mode (because it will be reuse
764 // and has not been written to disk (for performance reason).
765 // coverity[var_deref_model] the IsA()->InheritsFrom guarantees that the dynamic_cast will succeed.
766 if (!(type&kIncremental) || dirobj->GetFile() != target) {
767 dirobj->ResetBit(kMustCleanup);
768 delete dirobj;
769 }
770 // Let's also delete the directory from the other source (thanks to the 'allNames'
771 // mechanism above we will not process the directories when tranversing the next
772 // files).
773 TFile *nextsource = current_file ? (TFile*)sourcelist->After( current_file ) : (TFile*)sourcelist->First();
774 while (nextsource) {
775 TDirectory *ndir = nextsource->GetDirectory(dirpath);
776 // For consistency (and persformance), we reset the MustCleanup be also for those
777 // 'key' retrieved indirectly.
778 ndir->ResetBit(kMustCleanup);
779 delete ndir;
780 nextsource = (TFile*)sourcelist->After( nextsource );
781 }
782 } else if (cl->InheritsFrom( TCollection::Class() )) {
783 // Don't overwrite, if the object were not merged.
784 if ( obj->Write( oldkeyname, canBeMerged ? TObject::kSingleKey | TObject::kOverwrite : TObject::kSingleKey) <= 0 ) {
785 status = kFALSE;
786 }
787 ((TCollection*)obj)->SetOwner();
788 delete obj;
789 } else {
790 // Don't overwrite, if the object were not merged.
791 // NOTE: this is probably wrong for emulated objects.
792 if (cl->IsTObject()) {
793 if ( obj->Write( oldkeyname, canBeMerged ? TObject::kOverwrite : 0) <= 0) {
794 status = kFALSE;
795 }
797 } else {
798 if ( target->WriteObjectAny( (void*)obj, cl, oldkeyname, canBeMerged ? "OverWrite" : "" ) <= 0) {
799 status = kFALSE;
800 }
801 }
802 cl->Destructor(obj); // just in case the class is not loaded.
803 }
804 info.Reset();
805 } // while ( ( TKey *key = (TKey*)nextkey() ) )
806 }
807 current_file = current_file ? (TFile*)sourcelist->After(current_file) : (TFile*)sourcelist->First();
808 if (current_file) {
809 current_sourcedir = current_file->GetDirectory(path);
810 } else {
811 current_sourcedir = 0;
812 }
813 }
814 // save modifications to the target directory.
815 if (!(type&kIncremental)) {
816 // In case of incremental build, we will call Write on the top directory/file, so we do not need
817 // to call SaveSelf explicilty.
818 target->SaveSelf(kTRUE);
819 }
820
821 return status;
822}
823
824////////////////////////////////////////////////////////////////////////////////
825/// Merge the files. If no output file was specified it will write into
826/// the file "FileMerger.root" in the working directory. Returns true
827/// on success, false in case of error.
828/// The type is defined by the bit values in EPartialMergeType:
829/// kRegular : normal merge, overwritting the output file
830/// kIncremental : merge the input file with the content of the output file (if already exising) (default)
831/// kAll : merge all type of objects (default)
832/// kResetable : merge only the objects with a MergeAfterReset member function.
833/// kNonResetable : merge only the objects without a MergeAfterReset member function.
834///
835/// If the type is set to kIncremental the output file is done deleted at the end of
836/// this operation. If the type is not set to kIncremental, the output file is closed.
837
839{
840 if (!fOutputFile) {
842 if (outf.IsNull()) {
843 outf.Form("file:%s/FileMerger.root", gSystem->TempDirectory());
844 Info("PartialMerge", "will merge the results to the file %s\n"
845 "since you didn't specify a merge filename",
846 TUrl(outf).GetFile());
847 }
848 if (!OutputFile(outf.Data())) {
849 return kFALSE;
850 }
851 }
852
853 // Special treament for the single file case ...
854 if ((fFileList.GetEntries() == 1) && !fExcessFiles.GetEntries() &&
858
859 TFile *file = (TFile *) fFileList.First();
860 if (!file || (file && file->IsZombie())) {
861 Error("PartialMerge", "one-file case: problem attaching to file");
862 return kFALSE;
863 }
864 Bool_t result = kTRUE;
865 if (!(result = file->Cp(fOutputFilename))) {
866 Error("PartialMerge", "one-file case: could not copy '%s' to '%s'",
867 file->GetPath(), fOutputFilename.Data());
868 return kFALSE;
869 }
870 if (file->TestBit(kCanDelete)) file->Close();
871
872 // Remove the temporary file
873 if (fLocal && !file->InheritsFrom(TMemFile::Class())) {
874 TUrl u(file->GetPath(), kTRUE);
875 if (gSystem->Unlink(u.GetFile()) != 0)
876 Warning("PartialMerge", "problems removing temporary local file '%s'", u.GetFile());
877 }
879 return result;
880 }
881
883
885
886 Bool_t result = kTRUE;
887 Int_t type = in_type;
888 while (result && fFileList.GetEntries()>0) {
890
891 // Remove local copies if there are any
892 TIter next(&fFileList);
893 TFile *file;
894 while ((file = (TFile*) next())) {
895 // close the files
896 if (file->TestBit(kCanDelete)) file->Close();
897 // remove the temporary files
898 if(fLocal && !file->InheritsFrom(TMemFile::Class())) {
899 TString p(file->GetPath());
900 // coverity[unchecked_value] Index is return a value with range or NPos to select the whole name.
901 p = p(0, p.Index(':',0));
902 gSystem->Unlink(p);
903 }
904 }
906 if (result && fExcessFiles.GetEntries() > 0) {
907 // We merge the first set of files in the output,
908 // we now need to open the next set and make
909 // sure we accumulate into the output, so we
910 // switch to incremental merging (if not already set)
912 result = OpenExcessFiles();
913 }
914 }
915 if (!result) {
916 Error("Merge", "error during merge of your ROOT files");
917 } else {
918 // Close or write is required so the file is complete.
919 if (in_type & kIncremental) {
921 } else {
922 gROOT->GetListOfFiles()->Remove(fOutputFile);
924 }
925 }
926
927 // Cleanup
928 if (in_type & kIncremental) {
929 Clear();
930 } else {
933 }
934 return result;
935}
936
937////////////////////////////////////////////////////////////////////////////////
938/// Open up to fMaxOpenedFiles of the excess files.
939
941{
942 if (fPrintLevel > 0) {
943 Printf("%s Opening the next %d files", fMsgPrefix.Data(), TMath::Min(fExcessFiles.GetEntries(), fMaxOpenedFiles - 1));
944 }
945 Int_t nfiles = 0;
946 TIter next(&fExcessFiles);
947 TObjString *url = 0;
948 TString localcopy;
949 // We want gDirectory untouched by anything going on here
951 while( nfiles < (fMaxOpenedFiles-1) && ( url = (TObjString*)next() ) ) {
952 TFile *newfile = 0;
953 if (fLocal) {
954 TUUID uuid;
955 localcopy.Form("file:%s/ROOTMERGE-%s.root", gSystem->TempDirectory(), uuid.AsString());
956 if (!TFile::Cp(url->GetName(), localcopy, url->TestBit(kCpProgress))) {
957 Error("OpenExcessFiles", "cannot get a local copy of file %s", url->GetName());
958 return kFALSE;
959 }
960 newfile = TFile::Open(localcopy, "READ");
961 } else {
962 newfile = TFile::Open(url->GetName(), "READ");
963 }
964
965 if (!newfile) {
966 if (fLocal)
967 Error("OpenExcessFiles", "cannot open local copy %s of URL %s",
968 localcopy.Data(), url->GetName());
969 else
970 Error("OpenExcessFiles", "cannot open file %s", url->GetName());
971 return kFALSE;
972 } else {
974
975 newfile->SetBit(kCanDelete);
976 fFileList.Add(newfile);
977 ++nfiles;
978 fExcessFiles.Remove(url);
979 }
980 }
981 return kTRUE;
982}
983
984////////////////////////////////////////////////////////////////////////////////
985/// Intercept the case where the output TFile is deleted!
986
988{
989 if (obj == fOutputFile) {
990 Fatal("RecursiveRemove","Output file of the TFile Merger (targeting %s) has been deleted (likely due to a TTree larger than 100Gb)", fOutputFilename.Data());
991 }
992
993}
994
995////////////////////////////////////////////////////////////////////////////////
996/// Set a limit to the number of files that TFileMerger will open simultaneously.
997///
998/// If the request is higher than the system limit, we reset it to the system limit.
999/// If the request is less than two, we reset it to 2 (one for the output file and one for the input file).
1000
1002{
1004 if (newmax < sysmax) {
1005 fMaxOpenedFiles = newmax;
1006 } else {
1007 fMaxOpenedFiles = sysmax;
1008 }
1009 if (fMaxOpenedFiles < 2) {
1010 fMaxOpenedFiles = 2;
1011 }
1012}
1013
1014////////////////////////////////////////////////////////////////////////////////
1015/// Set the prefix to be used when printing informational message.
1016
1017void TFileMerger::SetMsgPrefix(const char *prefix)
1018{
1019 fMsgPrefix = prefix;
1020}
1021
void Class()
Definition: Class.C:29
#define SafeDelete(p)
Definition: RConfig.hxx:529
int Int_t
Definition: RtypesCore.h:41
const Bool_t kFALSE
Definition: RtypesCore.h:88
unsigned long ULong_t
Definition: RtypesCore.h:51
bool Bool_t
Definition: RtypesCore.h:59
long long Long64_t
Definition: RtypesCore.h:69
const Bool_t kTRUE
Definition: RtypesCore.h:87
const char Option_t
Definition: RtypesCore.h:62
#define BIT(n)
Definition: Rtypes.h:82
#define ClassImp(name)
Definition: Rtypes.h:363
TClassRef R__TH1_Class("TH1")
static Int_t R__GetSystemMaxOpenedFiles()
Return the maximum number of allowed opened files minus some wiggle room for CINT or at least of the ...
Definition: TFileMerger.cxx:66
TClassRef R__TTree_Class("TTree")
static const Int_t kCpProgress
Definition: TFileMerger.cxx:60
static const Int_t kCintFileNumber
Definition: TFileMerger.cxx:61
int type
Definition: TGX11.cxx:120
#define Printf
Definition: TGeoToOCC.h:18
R__EXTERN TVirtualMutex * gROOTMutex
Definition: TROOT.h:57
#define gROOT
Definition: TROOT.h:410
R__EXTERN TSystem * gSystem
Definition: TSystem.h:540
#define R__LOCKGUARD(mutex)
TClassRef is used to implement a permanent reference to a TClass object.
Definition: TClassRef.h:29
The ROOT global object gROOT contains a list of all defined classes.
Definition: TClass.h:75
TMethod * GetMethodWithPrototype(const char *method, const char *proto, Bool_t objectIsConst=kFALSE, ROOT::EFunctionMatchMode mode=ROOT::kConversionMatch)
Find the method with a given prototype.
Definition: TClass.cxx:4309
void Destructor(void *obj, Bool_t dtorOnly=kFALSE)
Explicitly call destructor for object.
Definition: TClass.cxx:5181
ROOT::ResetAfterMergeFunc_t GetResetAfterMerge() const
Bool_t IsTObject() const
Return kTRUE is the class inherits from TObject.
Definition: TClass.cxx:5700
ROOT::MergeFunc_t GetMerge() const
Bool_t InheritsFrom(const char *cl) const
Return kTRUE if this class inherits from a class with name "classname".
Definition: TClass.cxx:4720
static TClass * GetClass(const char *name, Bool_t load=kTRUE, Bool_t silent=kFALSE)
Static method returning pointer to TClass of the specified class name.
Definition: TClass.cxx:2885
Collection abstract base class.
Definition: TCollection.h:63
virtual void Print(Option_t *option="") const
Default print for collections, calls Print(option, 1).
virtual Int_t GetEntries() const
Definition: TCollection.h:177
virtual void SetOwner(Bool_t enable=kTRUE)
Set whether this collection is the owner (enable==true) of its content.
virtual Int_t GetSize() const
Return the capacity of the collection, i.e.
Definition: TCollection.h:182
virtual TDirectory * GetDirectory(const char *apath, Bool_t printError=false, const char *funcname="GetDirectory")
Find a directory named "apath".
Describe directory structure in memory.
Definition: TDirectory.h:34
virtual TList * GetList() const
Definition: TDirectory.h:149
virtual TDirectory * GetDirectory(const char *namecycle, Bool_t printError=false, const char *funcname="GetDirectory")
Find a directory using apath.
Definition: TDirectory.cxx:400
virtual const char * GetPath() const
Returns the full path of the directory.
Definition: TDirectory.cxx:987
virtual Bool_t cd(const char *path=0)
Change current directory to "this" directory.
Definition: TDirectory.cxx:497
virtual Int_t WriteObjectAny(const void *, const char *, const char *, Option_t *="", Int_t=0)
Definition: TDirectory.h:200
virtual TFile * GetFile() const
Definition: TDirectory.h:147
virtual void SaveSelf(Bool_t=kFALSE)
Definition: TDirectory.h:181
virtual TList * GetListOfKeys() const
Definition: TDirectory.h:150
virtual TDirectory * mkdir(const char *name, const char *title="")
Create a sub-directory "a" or a hierarchy of sub-directories "a/b/c/...".
TIOFeatures * fIOFeatures
This class provides file copy and merging services.
Definition: TFileMerger.h:30
TString fObjectNames
List of object names to be either merged exclusively or skipped.
Definition: TFileMerger.h:54
virtual Bool_t OutputFile(const char *url, Bool_t force)
Open merger output file.
TList fMergeList
list of TObjString containing the name of the files need to be merged
Definition: TFileMerger.h:55
virtual Bool_t AddFile(TFile *source, Bool_t own, Bool_t cpProgress)
Add the TFile to this file merger and give ownership of the TFile to this object (unless kFALSE is re...
virtual void PrintFiles(Option_t *options)
Print list of files being merged.
Bool_t fHistoOneGo
Merger histos in one go (default is kTRUE)
Definition: TFileMerger.h:53
virtual Bool_t MergeRecursive(TDirectory *target, TList *sourcelist, Int_t type=kRegular|kAll)
Merge all objects in a directory.
TList fFileList
A list the file (TFile*) which shall be merged.
Definition: TFileMerger.h:39
virtual Bool_t Merge(Bool_t=kTRUE)
Merge the files.
TString fOutputFilename
The name of the outputfile for merging.
Definition: TFileMerger.h:41
virtual void RecursiveRemove(TObject *obj)
Intercept the case where the output TFile is deleted!
TString fMsgPrefix
Prefix to be used when printing informational message (default TFileMerger)
Definition: TFileMerger.h:49
TIOFeatures * fIOFeatures
IO features to use in the output file.
Definition: TFileMerger.h:48
TFileMerger(const TFileMerger &)=delete
void SetMsgPrefix(const char *prefix)
Set the prefix to be used when printing informational message.
Bool_t fNoTrees
True if Trees should not be merged (default is kFALSE)
Definition: TFileMerger.h:43
@ kAll
Merge all type of objects (default)
Definition: TFileMerger.h:70
@ kIncremental
Merge the input file with the content of the output file (if already exising).
Definition: TFileMerger.h:66
@ kKeepCompression
Keep compression level unchanged for each input files.
Definition: TFileMerger.h:75
@ kSkipListed
Skip objects specified in fObjectNames list.
Definition: TFileMerger.h:74
@ kNonResetable
Only the objects without a MergeAfterReset member function.
Definition: TFileMerger.h:68
@ kResetable
Only the objects with a MergeAfterReset member function.
Definition: TFileMerger.h:67
@ kOnlyListed
Only the objects specified in fObjectNames list.
Definition: TFileMerger.h:73
@ kRegular
Normal merge, overwritting the output file.
Definition: TFileMerger.h:65
Bool_t fExplicitCompLevel
True if the user explicitly requested a compressio level change (default kFALSE)
Definition: TFileMerger.h:44
Bool_t fCompressionChange
True if the output and input have different compression level (default kFALSE)
Definition: TFileMerger.h:45
virtual ~TFileMerger()
Cleanup.
Int_t fPrintLevel
How much information to print out at run time.
Definition: TFileMerger.h:46
void SetMaxOpenedFiles(Int_t newmax)
Set a limit to the number of files that TFileMerger will open simultaneously.
TString fMergeOptions
Options (in string format) to be passed down to the Merge functions.
Definition: TFileMerger.h:47
Bool_t OpenExcessFiles()
Open up to fMaxOpenedFiles of the excess files.
TList fExcessFiles
! List of TObjString containing the name of the files not yet added to fFileList due to user or syste...
Definition: TFileMerger.h:56
TFile * fOutputFile
The outputfile for merging.
Definition: TFileMerger.h:40
virtual Bool_t PartialMerge(Int_t type=kAll|kIncremental)
Merge the files.
Bool_t fLocal
Makes local copies of merging files if True (default is kTRUE)
Definition: TFileMerger.h:52
virtual void Reset()
Reset merger file list.
Int_t fMaxOpenedFiles
Maximum number of files opened at the same time by the TFileMerger.
Definition: TFileMerger.h:51
virtual Bool_t AddAdoptFile(TFile *source, Bool_t cpProgress=kTRUE)
Add the TFile to this file merger and give ownership of the TFile to this object (unless kFALSE is re...
Bool_t fFastMethod
True if using Fast merging algorithm (default)
Definition: TFileMerger.h:42
A ROOT file is a suite of consecutive data records (TKey instances) with a well defined format.
Definition: TFile.h:48
virtual void Close(Option_t *option="")
Close a file.
Definition: TFile.cxx:912
Int_t GetCompressionSettings() const
Definition: TFile.h:391
Int_t GetCompressionLevel() const
Definition: TFile.h:385
virtual Int_t Write(const char *name=0, Int_t opt=0, Int_t bufsiz=0)
Write memory objects to this file.
Definition: TFile.cxx:2335
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:4873
static TFile * Open(const char *name, Option_t *option="", const char *ftitle="", Int_t compress=ROOT::RCompressionSetting::EDefaults::kUseGeneralPurpose, Int_t netopt=0)
Create / open a file.
Definition: TFile.cxx:3975
THashList implements a hybrid collection class consisting of a hash table and a list to store TObject...
Definition: THashList.h:34
TObject * FindObject(const char *name) const
Find object using its name.
Definition: THashList.cxx:262
Book space in a file, create I/O buffers, to fill them, (un)compress them.
Definition: TKey.h:24
virtual const char * GetClassName() const
Definition: TKey.h:71
virtual const char * GetTitle() const
Returns title (title can contain 32x32 xpm thumbnail/icon).
Definition: TKey.cxx:1501
virtual TObject * ReadObj()
To read a TObject* from the file.
Definition: TKey.cxx:722
A doubly linked list.
Definition: TList.h:44
virtual void Add(TObject *obj)
Definition: TList.h:87
virtual TObject * After(const TObject *obj) const
Returns the object after object obj.
Definition: TList.cxx:327
virtual TObject * Remove(TObject *obj)
Remove object from the list.
Definition: TList.cxx:818
virtual TObject * FindObject(const char *name) const
Delete a TObjLink object.
Definition: TList.cxx:574
virtual void Delete(Option_t *option="")
Remove all objects from the list AND delete all heap based objects.
Definition: TList.cxx:467
virtual TObject * First() const
Return the first object in the list. Returns 0 when list is empty.
Definition: TList.cxx:655
virtual void Clear(Option_t *option="")
Remove all objects from the list.
Definition: TList.cxx:399
virtual const char * GetName() const
Returns name of object.
Definition: TNamed.h:47
An array of TObjects.
Definition: TObjArray.h:37
Int_t GetEntriesFast() const
Definition: TObjArray.h:64
TObject * At(Int_t idx) const
Definition: TObjArray.h:165
Collectable string class.
Definition: TObjString.h:28
const char * GetName() const
Returns name of object.
Definition: TObjString.h:39
Mother of all ROOT objects.
Definition: TObject.h:37
virtual void Clear(Option_t *="")
Definition: TObject.h:100
virtual Int_t Write(const char *name=0, Int_t option=0, Int_t bufsize=0)
Write this object to the current directory.
Definition: TObject.cxx:785
virtual const char * GetName() const
Returns name of object.
Definition: TObject.cxx:357
R__ALWAYS_INLINE Bool_t TestBit(UInt_t f) const
Definition: TObject.h:172
virtual void Execute(const char *method, const char *params, Int_t *error=0)
Execute method on this object with the given parameter string, e.g.
Definition: TObject.cxx:277
virtual void Warning(const char *method, const char *msgfmt,...) const
Issue warning message.
Definition: TObject.cxx:866
R__ALWAYS_INLINE Bool_t IsZombie() const
Definition: TObject.h:134
@ kOverwrite
overwrite existing object with same name
Definition: TObject.h:88
@ kSingleKey
write collection with single key
Definition: TObject.h:87
void SetBit(UInt_t f, Bool_t set)
Set or unset the user status bits as specified in f.
Definition: TObject.cxx:694
virtual Bool_t InheritsFrom(const char *classname) const
Returns kTRUE if object inherits from class "classname".
Definition: TObject.cxx:443
virtual void Error(const char *method, const char *msgfmt,...) const
Issue error message.
Definition: TObject.cxx:880
virtual void Fatal(const char *method, const char *msgfmt,...) const
Issue fatal error message.
Definition: TObject.cxx:908
virtual const char * GetTitle() const
Returns title of object.
Definition: TObject.cxx:401
void ResetBit(UInt_t f)
Definition: TObject.h:171
@ kCanDelete
if object in a list can be deleted
Definition: TObject.h:58
@ kMustCleanup
if object destructor must call RecursiveRemove()
Definition: TObject.h:60
virtual void Info(const char *method, const char *msgfmt,...) const
Issue info message.
Definition: TObject.cxx:854
Basic string class.
Definition: TString.h:131
void Clear()
Clear string without changing its capacity.
Definition: TString.cxx:1151
const char * Data() const
Definition: TString.h:364
TObjArray * Tokenize(const TString &delim) const
This function is used to isolate sequential tokens in a TString.
Definition: TString.cxx:2172
Bool_t IsNull() const
Definition: TString.h:402
TString & Remove(Ssiz_t pos)
Definition: TString.h:668
TString & Append(const char *cs)
Definition: TString.h:559
void Form(const char *fmt,...)
Formats a string using a printf style format descriptor.
Definition: TString.cxx:2264
Bool_t Contains(const char *pat, ECaseCompare cmp=kExact) const
Definition: TString.h:619
Ssiz_t Index(const char *pat, Ssiz_t i=0, ECaseCompare cmp=kExact) const
Definition: TString.h:634
virtual int Unlink(const char *name)
Unlink, i.e.
Definition: TSystem.cxx:1371
virtual const char * TempDirectory() const
Return a user configured or systemwide directory to create temporary files in.
Definition: TSystem.cxx:1472
This class defines a UUID (Universally Unique IDentifier), also known as GUIDs (Globally Unique IDent...
Definition: TUUID.h:42
const char * AsString() const
Return UUID as string. Copy string immediately since it will be reused.
Definition: TUUID.cxx:560
This class represents a WWW compatible URL.
Definition: TUrl.h:35
const char * GetFile() const
Definition: TUrl.h:72
Long64_t(* MergeFunc_t)(void *, TCollection *, TFileMergeInfo *)
Definition: Rtypes.h:115
Short_t Min(Short_t a, Short_t b)
Definition: TMathBase.h:180
Definition: file.py:1