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