Logo ROOT  
Reference Guide
 
Loading...
Searching...
No Matches
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 "TError.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 "TObjArray.h"
36#include "TClass.h"
37#include "TFileMergeInfo.h"
38#include "TClassRef.h"
39#include "TROOT.h"
40#include "TMemFile.h"
41#include "TVirtualMutex.h"
42
43#ifdef WIN32
44// For _getmaxstdio
45#include <cstdio>
46#else
47// For getrlimit
48#include <sys/time.h>
49#include <sys/resource.h>
50#endif
51
52#include <cstring>
53
55
58TClassRef R__RNTuple_Class("ROOT::Experimental::RNTuple");
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
376namespace {
377
378Bool_t IsMergeable(TClass *cl)
379{
380 return (cl->GetMerge() || cl->InheritsFrom(TDirectory::Class()) ||
381 (cl->IsTObject() && !cl->IsLoaded() &&
382 /* If it has a dictionary and GetMerge() is nullptr then we already know the answer
383 to the next question is 'no, if we were to ask we would useless trigger
384 auto-parsing */
385 (cl->GetMethodWithPrototype("Merge", "TCollection*,TFileMergeInfo*") ||
386 cl->GetMethodWithPrototype("Merge", "TCollection*"))));
387};
388
389Bool_t WriteOneAndDelete(const TString &name, TClass *cl, TObject *obj, bool canBeMerged, Bool_t ownobj, TDirectory *target)
390{
391 Bool_t status = kTRUE;
392 if (cl->InheritsFrom(TCollection::Class())) {
393 // Don't overwrite, if the object were not merged.
394 if (obj->Write(name, canBeMerged ? TObject::kSingleKey | TObject::kOverwrite : TObject::kSingleKey) <= 0) {
395 status = kFALSE;
396 }
397 ((TCollection *)obj)->SetOwner();
398 if (ownobj)
399 delete obj;
400 } else {
401 // Don't overwrite, if the object were not merged.
402 // NOTE: this is probably wrong for emulated objects.
403 if (cl->IsTObject()) {
404 if (obj->Write(name, canBeMerged ? TObject::kOverwrite : 0) <= 0) {
405 status = kFALSE;
406 }
408 } else {
409 if (target->WriteObjectAny((void *)obj, cl, name, canBeMerged ? "OverWrite" : "") <= 0) {
410 status = kFALSE;
411 }
412 }
413 if (ownobj)
414 cl->Destructor(obj); // just in case the class is not loaded.
415 }
416 return status;
417}
418
419Bool_t WriteCycleInOrder(const TString &name, TIter &nextkey, TIter &peeknextkey, TDirectory *target)
420{
421 // Recurse until we find a different name or type appear.
422 TKey *key = (TKey*)peeknextkey();
423 if (!key || name != key->GetName()) {
424 return kTRUE;
425 }
427 if (IsMergeable(cl))
428 return kTRUE;
429 // Now we can advance the real iterator
430 (void)nextkey();
431 Bool_t result = WriteCycleInOrder(name, nextkey, peeknextkey, target);
432 TObject *obj = key->ReadObj();
433
434 return WriteOneAndDelete(name, cl, obj, kFALSE, kTRUE, target) && result;
435};
436
437} // anonymous namespace
438
440 TString &oldkeyname, THashList &allNames, Bool_t &status, Bool_t &onlyListed,
441 const TString &path, TDirectory *current_sourcedir, TFile *current_file, TKey *key,
442 TObject *obj, TIter &nextkey)
443{
444 const char *keyname = obj ? obj->GetName() : key->GetName();
445 const char *keyclassname = obj ? obj->IsA()->GetName() : key->GetClassName();
446 const char *keytitle = obj ? obj->GetTitle() : key->GetTitle();
447
448 // Keep only the highest cycle number for each key for mergeable objects. They are stored
449 // in the (hash) list consecutively and in decreasing order of cycles, so we can continue
450 // until the name changes. We flag the case here and we act consequently later.
451 Bool_t alreadyseen = (oldkeyname == keyname) ? kTRUE : kFALSE;
452 Bool_t ownobj = kFALSE;
453
454 // Read in but do not copy directly the processIds.
455 if (strcmp(keyclassname, "TProcessID") == 0 && key) {
456 key->ReadObj();
457 return kTRUE;
458 }
459
460 // If we have already seen this object [name], we already processed
461 // the whole list of files for this objects and we can just skip it
462 // and any related cycles.
463 if (allNames.FindObject(keyname)) {
464 oldkeyname = keyname;
465 return kTRUE;
466 }
467
468 TClass *cl = TClass::GetClass(keyclassname);
469 if (!cl) {
470 Info("MergeRecursive", "cannot indentify object type (%s), name: %s title: %s",
471 keyclassname, keyname, keytitle);
472 return kTRUE;
473 }
474 // For mergeable objects we add the names in a local hashlist handling them
475 // again (see above)
476 if (IsMergeable(cl))
477 allNames.Add(new TObjString(keyname));
478
480 // Skip the TTree objects and any related cycles.
481 oldkeyname = keyname;
482 return kTRUE;
483 }
484 // Check if only the listed objects are to be merged
485 if (type & kOnlyListed) {
486 oldkeyname = keyname;
487 oldkeyname += " ";
488 onlyListed = fObjectNames.Contains(oldkeyname);
489 oldkeyname = keyname;
490 if ((!onlyListed) && (!cl->InheritsFrom(TDirectory::Class()))) return kTRUE;
491 }
492
493 if (!(type&kResetable && type&kNonResetable)) {
494 // If neither or both are requested at the same time, we merger both types.
495 if (!(type&kResetable)) {
496 if (cl->GetResetAfterMerge()) {
497 // Skip the object with a reset after merge routine (TTree and other incrementally mergeable objects)
498 oldkeyname = keyname;
499 return kTRUE;
500 }
501 }
502 if (!(type&kNonResetable)) {
503 if (!cl->GetResetAfterMerge()) {
504 // Skip the object without a reset after merge routine (Histograms and other non incrementally mergeable objects)
505 oldkeyname = keyname;
506 return kTRUE;
507 }
508 }
509 }
510 // read object from first source file
511 if (type & kIncremental) {
512 if (!obj)
513 obj = current_sourcedir->GetList()->FindObject(keyname);
514 if (!obj && key) {
515 obj = key->ReadObj();
516 ownobj = kTRUE;
517 } else if (obj && info.fIsFirst && current_sourcedir != target
518 && !cl->InheritsFrom( TDirectory::Class() )) {
519 R__ASSERT(cl->IsTObject());
520 TDirectory::TContext ctxt(current_sourcedir);
521 obj = obj->Clone();
522 ownobj = kTRUE;
523 }
524 } else if (key) {
525 obj = key->ReadObj();
526 ownobj = kTRUE;
527 }
528 if (!obj) {
529 Info("MergeRecursive", "could not read object for key {%s, %s}",
530 keyname, keytitle);
531 return kTRUE;
532 }
533 Bool_t canBeFound = (type & kIncremental) && (current_sourcedir->GetList()->FindObject(keyname) != nullptr);
534
535 // if (cl->IsTObject())
536 // obj->ResetBit(kMustCleanup);
537 if (cl->IsTObject() && cl != obj->IsA()) {
538 Error("MergeRecursive", "TKey and object retrieve disagree on type (%s vs %s). Continuing with %s.",
539 keyclassname, obj->IsA()->GetName(), obj->IsA()->GetName());
540 cl = obj->IsA();
541 }
542 Bool_t canBeMerged = kTRUE;
543
544 TList dirtodelete;
545 auto getDirectory = [&dirtodelete](TDirectory *parent, const char *name, const TString &pathname)
546 {
547 TDirectory *result = dynamic_cast<TDirectory*>(parent->GetList()->FindObject(name));
548 if (!result) {
549 result = parent->GetDirectory(pathname);
550 if (result && result != parent)
551 dirtodelete.Add(result);
552 }
553
554 return result;
555 };
556
557 if ( cl->InheritsFrom( TDirectory::Class() ) ) {
558 // it's a subdirectory
559
560 target->cd();
561 TDirectory *newdir;
562
563 // For incremental or already seen we may have already a directory created
564 if (type & kIncremental || alreadyseen) {
565 newdir = target->GetDirectory(obj->GetName());
566 if (!newdir) {
567 newdir = target->mkdir( obj->GetName(), obj->GetTitle() );
568 // newdir->ResetBit(kMustCleanup);
569 }
570 } else {
571 newdir = target->mkdir( obj->GetName(), obj->GetTitle() );
572 // newdir->ResetBit(kMustCleanup);
573 }
574
575 // newdir is now the starting point of another round of merging
576 // newdir still knows its depth within the target file via
577 // GetPath(), so we can still figure out where we are in the recursion
578
579 // If this folder is a onlyListed object, merge everything inside.
580 if (onlyListed) type &= ~kOnlyListed;
581 status = MergeRecursive(newdir, sourcelist, type);
582 // Delete newdir directory after having written it (merged)
583 if (!(type&kIncremental)) delete newdir;
584 if (onlyListed) type |= kOnlyListed;
585 if (!status) return kFALSE;
586 } else if (!cl->IsTObject() && cl->GetMerge()) {
587 // merge objects that don't derive from TObject
589 Warning("MergeRecursive", "Merging RNTuples is experimental");
590
591 // Collect all the data to be passed on to the merger
592 TList mergeData;
593 // First entry is the TKey of the ntuple
594 mergeData.Add(key);
595 // Second entry is the output file
596 mergeData.Add(target->GetFile());
597 // Remaining entries are the input files
598 TIter nextFile(sourcelist);
599 while (const auto &inFile = nextFile()) {
600 mergeData.Add(inFile);
601 }
602 // Get the merge fuction and pass the data
603 ROOT::MergeFunc_t func = cl->GetMerge();
604 Long64_t result = func(obj, &mergeData, &info);
605 mergeData.Clear("nodelete");
606 if (result < 0) {
607 Error("MergeRecursive", "Could NOT merge RNTuples!");
608 return kFALSE;
609 }
610 } else {
611 TFile *nextsource = current_file ? (TFile*)sourcelist->After( current_file ) : (TFile*)sourcelist->First();
612 Error("MergeRecursive", "Merging objects that don't inherit from TObject is unimplemented (key: %s of type %s in file %s)",
613 keyname, keyclassname, nextsource->GetName());
614 canBeMerged = kFALSE;
615 }
616 } else if (cl->IsTObject() && cl->GetMerge()) {
617 // Check if already treated
618 if (alreadyseen) return kTRUE;
619
620 TList inputs;
621 TList todelete;
623
624 // Loop over all source files and merge same-name object
625 TFile *nextsource = current_file ? (TFile*)sourcelist->After( current_file ) : (TFile*)sourcelist->First();
626 if (nextsource == 0) {
627 // There is only one file in the list
628 ROOT::MergeFunc_t func = cl->GetMerge();
629 func(obj, &inputs, &info);
630 info.fIsFirst = kFALSE;
631 } else {
632 do {
633 // make sure we are at the correct directory level by cd'ing to path
634 TDirectory *ndir = getDirectory(nextsource, target->GetName(), path);
635 if (ndir) {
636 // For consistency (and persformance), we reset the MustCleanup be also for those
637 // 'key' retrieved indirectly.
638 // ndir->ResetBit(kMustCleanup);
639 ndir->cd();
640 TObject *hobj = ndir->GetList()->FindObject(keyname);
641 if (!hobj) {
642 TKey *key2 = (TKey*)ndir->GetListOfKeys()->FindObject(keyname);
643 if (key2) {
644 hobj = key2->ReadObj();
645 if (!hobj) {
646 Info("MergeRecursive", "could not read object for key {%s, %s}; skipping file %s",
647 keyname, keytitle, nextsource->GetName());
648 nextsource = (TFile*)sourcelist->After(nextsource);
649 return kTRUE;
650 }
651 todelete.Add(hobj);
652 }
653 }
654 if (hobj) {
655 // Set ownership for collections
656 if (hobj->InheritsFrom(TCollection::Class())) {
657 ((TCollection*)hobj)->SetOwner();
658 }
659 hobj->ResetBit(kMustCleanup);
660 inputs.Add(hobj);
661 if (!oneGo) {
662 ROOT::MergeFunc_t func = cl->GetMerge();
663 Long64_t result = func(obj, &inputs, &info);
664 info.fIsFirst = kFALSE;
665 if (result < 0) {
666 Error("MergeRecursive", "calling Merge() on '%s' with the corresponding object in '%s'",
667 keyname, nextsource->GetName());
668 }
669 inputs.Clear();
670 todelete.Delete();
671 }
672 }
673 }
674 nextsource = (TFile*)sourcelist->After( nextsource );
675 } while (nextsource);
676 // Merge the list, if still to be done
677 if (oneGo || info.fIsFirst) {
678 ROOT::MergeFunc_t func = cl->GetMerge();
679 func(obj, &inputs, &info);
680 info.fIsFirst = kFALSE;
681 inputs.Clear();
682 todelete.Delete();
683 }
684 }
685 } else if (cl->IsTObject()) {
686 // try synthesizing the Merge method call according to the TObject
687 TList listH;
688 TString listHargs;
689 if (cl->GetMethodWithPrototype("Merge", "TCollection*,TFileMergeInfo*")) {
690 listHargs.Form("(TCollection*)0x%zx,(TFileMergeInfo*)0x%zx",
691 (size_t)&listH, (size_t)&info);
692 } else if (cl->GetMethodWithPrototype("Merge", "TCollection*")) {
693 listHargs.Form("((TCollection*)0x%zx)", (size_t)&listH);
694 } else {
695 // pass unmergeable objects through to the output file
696 canBeMerged = kFALSE;
697 }
698 if (canBeMerged) {
699 if (alreadyseen) {
700 // skip already seen mergeable objects, don't skip unmergeable objects
701 return kTRUE;
702 }
703 // Loop over all source files and merge same-name object
704 TFile *nextsource = current_file ? (TFile*)sourcelist->After( current_file ) : (TFile*)sourcelist->First();
705 if (nextsource == 0) {
706 // There is only one file in the list
707 Int_t error = 0;
708 obj->Execute("Merge", listHargs.Data(), &error);
709 info.fIsFirst = kFALSE;
710 if (error) {
711 Error("MergeRecursive", "calling Merge() on '%s' with the corresponding object in '%s'",
712 obj->GetName(), keyname);
713 }
714 } else {
715 while (nextsource) {
716 // make sure we are at the correct directory level by cd'ing to path
717 TDirectory *ndir = getDirectory(nextsource, target->GetName(), path);
718 if (ndir) {
719 ndir->cd();
720 TKey *key2 = (TKey*)ndir->GetListOfKeys()->FindObject(keyname);
721 if (key2) {
722 TObject *hobj = key2->ReadObj();
723 if (!hobj) {
724 Info("MergeRecursive", "could not read object for key {%s, %s}; skipping file %s",
725 keyname, keytitle, nextsource->GetName());
726 nextsource = (TFile*)sourcelist->After(nextsource);
727 return kTRUE;
728 }
729 // Set ownership for collections
730 if (hobj->InheritsFrom(TCollection::Class())) {
731 ((TCollection*)hobj)->SetOwner();
732 }
733 hobj->ResetBit(kMustCleanup);
734 listH.Add(hobj);
735 Int_t error = 0;
736 obj->Execute("Merge", listHargs.Data(), &error);
737 info.fIsFirst = kFALSE;
738 if (error) {
739 Error("MergeRecursive", "calling Merge() on '%s' with the corresponding object in '%s'",
740 obj->GetName(), nextsource->GetName());
741 }
742 listH.Delete();
743 }
744 }
745 nextsource = (TFile*)sourcelist->After( nextsource );
746 }
747 // Merge the list, if still to be done
748 if (info.fIsFirst) {
749 Int_t error = 0;
750 obj->Execute("Merge", listHargs.Data(), &error);
751 info.fIsFirst = kFALSE;
752 listH.Delete();
753 }
754 }
755 }
756 } else {
757 // Object is of no type that we can merge
758 canBeMerged = kFALSE;
759 }
760
761 // now write the merged histogram (which is "in" obj) to the target file
762 // note that this will just store obj in the current directory level,
763 // which is not persistent until the complete directory itself is stored
764 // by "target->SaveSelf()" below
765 target->cd();
766
767 oldkeyname = keyname;
768 //!!if the object is a tree, it is stored in globChain...
769 if (cl->InheritsFrom(TDirectory::Class())) {
770 // printf("cas d'une directory\n");
771
772 auto dirobj = dynamic_cast<TDirectory *>(obj);
773 TString dirpath(dirobj->GetPath());
774 // coverity[unchecked_value] 'target' is from a file so GetPath always returns path starting with filename:
775 dirpath.Remove(0, std::strlen(dirobj->GetFile()->GetPath()));
776
777 // Do not delete the directory if it is part of the output
778 // and we are in incremental mode (because it will be reused
779 // and has not been written to disk (for performance reason).
780 // coverity[var_deref_model] the IsA()->InheritsFrom guarantees that the dynamic_cast will succeed.
781 if (ownobj && (!(type & kIncremental) || dirobj->GetFile() != target)) {
782 dirobj->ResetBit(kMustCleanup);
783 delete dirobj;
784 }
785 // Let's also delete the directory from the other source (thanks to the 'allNames'
786 // mechanism above we will not process the directories when tranversing the next
787 // files).
788 TIter deliter(&dirtodelete);
789 while(TObject *ndir = deliter()) {
790 // For consistency (and performance), we reset the MustCleanup be also for those
791 // 'key' retrieved indirectly.
792 ndir->ResetBit(kMustCleanup);
793 delete ndir;
794 }
795 } else if (!canBeFound) { // Don't write the partial result for TTree and TH1
796
797 if (!canBeMerged) {
798 TIter peeknextkey(nextkey);
799 status = WriteCycleInOrder(oldkeyname, nextkey, peeknextkey, target) && status;
800 status = WriteOneAndDelete(oldkeyname, cl, obj, kFALSE, ownobj, target) && status;
801 } else {
802 status = WriteOneAndDelete(oldkeyname, cl, obj, kTRUE, ownobj, target) && status;
803 }
804 }
805 info.Reset();
806 dirtodelete.Clear("nodelete"); // If needed the delete is done explicitly above.
807 return kTRUE;
808}
809
810////////////////////////////////////////////////////////////////////////////////
811/// Merge all objects in a directory
812///
813/// The type is defined by the bit values in TFileMerger::EPartialMergeType.
814
815Bool_t TFileMerger::MergeRecursive(TDirectory *target, TList *sourcelist, Int_t type /* = kRegular | kAll */)
816{
817 Bool_t status = kTRUE;
818 Bool_t onlyListed = kFALSE;
819 if (fPrintLevel > 0) {
820 Printf("%s Target path: %s",fMsgPrefix.Data(),target->GetPath());
821 }
822
823 // Get the dir name
824 TString path(target->GetPath());
825 // coverity[unchecked_value] 'target' is from a file so GetPath always returns path starting with filename:
826 path.Remove(0, std::strlen(target->GetFile()->GetPath()));
827
828 Int_t nguess = sourcelist->GetSize()+1000;
829 THashList allNames(nguess);
830 allNames.SetOwner(kTRUE);
831 // If the mode is set to skipping list objects, add names to the allNames list
832 if (type & kSkipListed) {
833 TObjArray *arr = fObjectNames.Tokenize(" ");
834 arr->SetOwner(kFALSE);
835 for (Int_t iname=0; iname<arr->GetEntriesFast(); iname++)
836 allNames.Add(arr->At(iname));
837 delete arr;
838 }
839 ((THashList*)target->GetList())->Rehash(nguess);
840 ((THashList*)target->GetListOfKeys())->Rehash(nguess);
841
844 info.fOptions = fMergeOptions;
846 info.fOptions.Append(" fast");
847 }
848
849 TFile *current_file;
850 TDirectory *current_sourcedir;
851 if (type & kIncremental) {
852 current_file = 0;
853 current_sourcedir = target;
854 } else {
855 current_file = (TFile*)sourcelist->First();
856 current_sourcedir = current_file->GetDirectory(path);
857 }
858 while (current_file || current_sourcedir) {
859 // When current_sourcedir != 0 and current_file == 0 we are going over the target
860 // for an incremental merge.
861 if (current_sourcedir && (current_file == 0 || current_sourcedir != target)) {
862 TString oldkeyname;
863
864 // Loop over live objects
865 TIter nextobj( current_sourcedir->GetList() );
866 TObject *obj;
867 while ( (obj = (TKey*)nextobj())) {
868 auto result = MergeOne(target, sourcelist, type,
869 info, oldkeyname, allNames, status, onlyListed, path,
870 current_sourcedir, current_file,
871 nullptr, obj, nextobj);
872 if (!result)
873 return kFALSE; // Stop completely in case of error.
874 } // while ( (obj = (TKey*)nextobj()))
875
876 // loop over all keys in this directory
877 TIter nextkey( current_sourcedir->GetListOfKeys() );
878 TKey *key;
879
880 while ( (key = (TKey*)nextkey())) {
881 auto result = MergeOne(target, sourcelist, type,
882 info, oldkeyname, allNames, status, onlyListed, path,
883 current_sourcedir, current_file,
884 key, nullptr, nextkey);
885 if (!result)
886 return kFALSE; // Stop completely in case of error.
887 } // while ( ( TKey *key = (TKey*)nextkey() ) )
888 }
889 current_file = current_file ? (TFile*)sourcelist->After(current_file) : (TFile*)sourcelist->First();
890 if (current_file) {
891 current_sourcedir = current_file->GetDirectory(path);
892 } else {
893 current_sourcedir = 0;
894 }
895 }
896 // save modifications to the target directory.
897 if (!(type&kIncremental)) {
898 // In case of incremental build, we will call Write on the top directory/file, so we do not need
899 // to call SaveSelf explicilty.
900 target->SaveSelf(kTRUE);
901 }
902
903 return status;
904}
905
906////////////////////////////////////////////////////////////////////////////////
907/// Merge the files. If no output file was specified it will write into
908/// the file "FileMerger.root" in the working directory. Returns true
909/// on success, false in case of error.
910/// The type is defined by the bit values in EPartialMergeType:
911/// kRegular : normal merge, overwritting the output file
912/// kIncremental : merge the input file with the content of the output file (if already exising) (default)
913/// kResetable : merge only the objects with a MergeAfterReset member function.
914/// kNonResetable : merge only the objects without a MergeAfterReset member function.
915/// kDelayWrite : delay the TFile write (to reduce the number of write when reusing the file)
916/// kAll : merge all type of objects (default)
917/// kAllIncremental : merge incrementally all type of objects.
918/// kOnlyListed : merge only the objects specified in fObjectNames list
919/// kSkipListed : skip objects specified in fObjectNames list
920/// kKeepCompression: keep compression level unchanged for each input
921///
922/// If the type is not set to kIncremental, the output file is deleted at the end of this operation.
923
925{
926 if (!fOutputFile) {
928 if (outf.IsNull()) {
929 outf.Form("file:%s/FileMerger.root", gSystem->TempDirectory());
930 Info("PartialMerge", "will merge the results to the file %s\n"
931 "since you didn't specify a merge filename",
932 TUrl(outf).GetFile());
933 }
934 if (!OutputFile(outf.Data())) {
935 return kFALSE;
936 }
937 }
938
939 // Special treament for the single file case to improve efficiency...
940 if ((fFileList.GetEntries() == 1) && !fExcessFiles.GetEntries() &&
944
945 TFile *file = (TFile *) fFileList.First();
946 if (!file || (file && file->IsZombie())) {
947 Error("PartialMerge", "one-file case: problem attaching to file");
948 return kFALSE;
949 }
951 if (!(result = file->Cp(fOutputFilename))) {
952 Error("PartialMerge", "one-file case: could not copy '%s' to '%s'",
953 file->GetPath(), fOutputFilename.Data());
954 return kFALSE;
955 }
956 if (file->TestBit(kCanDelete)) file->Close();
957
958 // Remove the temporary file
959 if (fLocal && !file->InheritsFrom(TMemFile::Class())) {
960 TUrl u(file->GetPath(), kTRUE);
961 if (gSystem->Unlink(u.GetFile()) != 0)
962 Warning("PartialMerge", "problems removing temporary local file '%s'", u.GetFile());
963 }
965 return result;
966 }
967
969
971
973 Int_t type = in_type;
974 while (result && fFileList.GetEntries()>0) {
976
977 // Remove local copies if there are any
978 TIter next(&fFileList);
979 TFile *file;
980 while ((file = (TFile*) next())) {
981 // close the files
982 if (file->TestBit(kCanDelete)) file->Close();
983 // remove the temporary files
984 if(fLocal && !file->InheritsFrom(TMemFile::Class())) {
985 TString p(file->GetPath());
986 // coverity[unchecked_value] Index is return a value with range or NPos to select the whole name.
987 p = p(0, p.Index(':',0));
988 gSystem->Unlink(p);
989 }
990 }
992 if (result && fExcessFiles.GetEntries() > 0) {
993 // We merge the first set of files in the output,
994 // we now need to open the next set and make
995 // sure we accumulate into the output, so we
996 // switch to incremental merging (if not already set)
999 }
1000 }
1001 if (!result) {
1002 Error("Merge", "error during merge of your ROOT files");
1003 } else {
1004 // Close or write is required so the file is complete.
1005 if (in_type & kIncremental) {
1006 // In the case of 'kDelayWrite' the caller want to avoid having to
1007 // write the output objects once for every input file and instead
1008 // write it only once at the end of the process.
1009 if (!(in_type & kDelayWrite))
1011 } else {
1012 // If in_type is not incremental but type is incremental we are now in
1013 // the case where the user "explicitly" request a non-incremental merge
1014 // but we still have internally an incremental merge. Because the user
1015 // did not request the incremental merge they also probably do not to a
1016 // final Write of the file and thus not doing the write here would lead
1017 // to data loss ...
1018 if (type & kIncremental)
1020 gROOT->GetListOfFiles()->Remove(fOutputFile);
1021 fOutputFile->Close();
1022 }
1023 }
1024
1025 // Cleanup
1026 if (in_type & kIncremental) {
1027 Clear();
1028 } else {
1031 }
1032 return result;
1033}
1034
1035////////////////////////////////////////////////////////////////////////////////
1036/// Open up to fMaxOpenedFiles of the excess files.
1037
1039{
1040 if (fPrintLevel > 0) {
1041 Printf("%s Opening the next %d files", fMsgPrefix.Data(), TMath::Min(fExcessFiles.GetEntries(), fMaxOpenedFiles - 1));
1042 }
1043 Int_t nfiles = 0;
1044 TIter next(&fExcessFiles);
1045 TObjString *url = 0;
1046 TString localcopy;
1047 // We want gDirectory untouched by anything going on here
1049 while( nfiles < (fMaxOpenedFiles-1) && ( url = (TObjString*)next() ) ) {
1050 TFile *newfile = 0;
1051 if (fLocal) {
1052 TUUID uuid;
1053 localcopy.Form("file:%s/ROOTMERGE-%s.root", gSystem->TempDirectory(), uuid.AsString());
1054 if (!TFile::Cp(url->GetName(), localcopy, url->TestBit(kCpProgress))) {
1055 Error("OpenExcessFiles", "cannot get a local copy of file %s", url->GetName());
1056 return kFALSE;
1057 }
1058 newfile = TFile::Open(localcopy, "READ");
1059 } else {
1060 newfile = TFile::Open(url->GetName(), "READ");
1061 }
1062
1063 if (!newfile) {
1064 if (fLocal)
1065 Error("OpenExcessFiles", "cannot open local copy %s of URL %s",
1066 localcopy.Data(), url->GetName());
1067 else
1068 Error("OpenExcessFiles", "cannot open file %s", url->GetName());
1069 return kFALSE;
1070 } else {
1072
1073 newfile->SetBit(kCanDelete);
1074 fFileList.Add(newfile);
1075 ++nfiles;
1076 fExcessFiles.Remove(url);
1077 }
1078 }
1079 return kTRUE;
1080}
1081
1082////////////////////////////////////////////////////////////////////////////////
1083/// Intercept the case where the output TFile is deleted!
1084
1086{
1087 if (obj == fOutputFile) {
1088 Fatal("RecursiveRemove","Output file of the TFile Merger (targeting %s) has been deleted (likely due to a TTree larger than 100Gb)", fOutputFilename.Data());
1089 }
1090
1091}
1092
1093////////////////////////////////////////////////////////////////////////////////
1094/// Set a limit to the number of files that TFileMerger will open simultaneously.
1095///
1096/// If the request is higher than the system limit, we reset it to the system limit.
1097/// If the request is less than two, we reset it to 2 (one for the output file and one for the input file).
1098
1100{
1102 if (newmax < sysmax) {
1103 fMaxOpenedFiles = newmax;
1104 } else {
1105 fMaxOpenedFiles = sysmax;
1106 }
1107 if (fMaxOpenedFiles < 2) {
1108 fMaxOpenedFiles = 2;
1109 }
1110}
1111
1112////////////////////////////////////////////////////////////////////////////////
1113/// Set the prefix to be used when printing informational message.
1114
1115void TFileMerger::SetMsgPrefix(const char *prefix)
1116{
1117 fMsgPrefix = prefix;
1118}
1119
#define SafeDelete(p)
Definition RConfig.hxx:517
bool Bool_t
Definition RtypesCore.h:63
int Int_t
Definition RtypesCore.h:45
constexpr Bool_t kFALSE
Definition RtypesCore.h:101
long long Long64_t
Definition RtypesCore.h:80
constexpr Bool_t kTRUE
Definition RtypesCore.h:100
const char Option_t
Definition RtypesCore.h:66
#define BIT(n)
Definition Rtypes.h:85
#define ClassImp(name)
Definition Rtypes.h:377
#define R__ASSERT(e)
Definition TError.h:118
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 ...
TClassRef R__RNTuple_Class("ROOT::Experimental::RNTuple")
TClassRef R__TTree_Class("TTree")
static const Int_t kCpProgress
static const Int_t kCintFileNumber
winID h TVirtualViewer3D TVirtualGLPainter p
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t Float_t Float_t Float_t Int_t Int_t UInt_t UInt_t Rectangle_t Int_t Int_t Window_t TString Int_t GCValues_t GetPrimarySelectionOwner GetDisplay GetScreen GetColormap GetNativeEvent const char const char dpyName wid window const char font_name cursor keysym reg const char only_if_exist regb h Point_t winding char text const char depth char const char Int_t count const char ColorStruct_t color const char Pixmap_t Pixmap_t PictureAttributes_t attr const char char ret_data h unsigned char height h Atom_t Int_t ULong_t ULong_t unsigned char prop_list Atom_t Atom_t target
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t Float_t Float_t Float_t Int_t Int_t UInt_t UInt_t Rectangle_t result
Option_t Option_t TPoint TPoint const char mode
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t Float_t Float_t Float_t Int_t Int_t UInt_t UInt_t Rectangle_t Int_t Int_t Window_t TString Int_t GCValues_t GetPrimarySelectionOwner GetDisplay GetScreen GetColormap GetNativeEvent const char const char dpyName wid window const char font_name cursor keysym reg const char only_if_exist regb h Point_t winding char text const char depth char const char Int_t count const char ColorStruct_t color const char Pixmap_t Pixmap_t PictureAttributes_t attr const char char ret_data h unsigned char height h Atom_t Int_t ULong_t ULong_t unsigned char prop_list Atom_t Atom_t Atom_t Time_t type
char name[80]
Definition TGX11.cxx:110
@ kMustCleanup
Definition TObject.h:368
R__EXTERN TVirtualMutex * gROOTMutex
Definition TROOT.h:63
#define gROOT
Definition TROOT.h:407
void Printf(const char *fmt,...)
Formats a string in a circular formatting buffer and prints the string.
Definition TString.cxx:2503
R__EXTERN TSystem * gSystem
Definition TSystem.h:555
#define R__LOCKGUARD(mutex)
TClassRef is used to implement a permanent reference to a TClass object.
Definition TClassRef.h:28
TClass instances represent classes, structs and namespaces in the ROOT type system.
Definition TClass.h:81
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:4456
void Destructor(void *obj, Bool_t dtorOnly=kFALSE)
Explicitly call destructor for object.
Definition TClass.cxx:5400
ROOT::ResetAfterMergeFunc_t GetResetAfterMerge() const
Return the wrapper around Merge.
Definition TClass.cxx:7439
Bool_t IsLoaded() const
Return true if the shared library of this class is currently in the a process's memory.
Definition TClass.cxx:5912
Bool_t IsTObject() const
Return kTRUE is the class inherits from TObject.
Definition TClass.cxx:5938
Bool_t InheritsFrom(const char *cl) const override
Return kTRUE if this class inherits from a class with name "classname".
Definition TClass.cxx:4874
ROOT::MergeFunc_t GetMerge() const
Return the wrapper around Merge.
Definition TClass.cxx:7431
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:2968
Collection abstract base class.
Definition TCollection.h:65
static TClass * Class()
virtual Int_t GetEntries() const
virtual void SetOwner(Bool_t enable=kTRUE)
Set whether this collection is the owner (enable==true) of its content.
void Print(Option_t *option="") const override
Default print for collections, calls Print(option, 1).
virtual Int_t GetSize() const
Return the capacity of the collection, i.e.
TDirectory * GetDirectory(const char *apath, Bool_t printError=false, const char *funcname="GetDirectory") override
Find a directory named "apath".
TDirectory::TContext keeps track and restore the current directory.
Definition TDirectory.h:89
Describe directory structure in memory.
Definition TDirectory.h:45
static TClass * Class()
virtual TList * GetList() const
Definition TDirectory.h:222
virtual TDirectory * GetDirectory(const char *namecycle, Bool_t printError=false, const char *funcname="GetDirectory")
Find a directory using apath.
virtual const char * GetPath() const
Returns the full path of the directory.
virtual Bool_t cd()
Change current directory to "this" directory.
virtual TList * GetListOfKeys() const
Definition TDirectory.h:223
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.
void RecursiveRemove(TObject *obj) override
Intercept the case where the output TFile is deleted!
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.
virtual Bool_t MergeOne(TDirectory *target, TList *sourcelist, Int_t type, TFileMergeInfo &info, TString &oldkeyname, THashList &allNames, Bool_t &status, Bool_t &onlyListed, const TString &path, TDirectory *current_sourcedir, TFile *current_file, TKey *key, TObject *obj, TIter &nextkey)
TString fOutputFilename
The name of the outputfile for merging.
Definition TFileMerger.h:41
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:76
@ kIncremental
Merge the input file with the content of the output file (if already existing).
Definition TFileMerger.h:71
@ kKeepCompression
Keep compression level unchanged for each input files.
Definition TFileMerger.h:81
@ kSkipListed
Skip objects specified in fObjectNames list.
Definition TFileMerger.h:80
@ kNonResetable
Only the objects without a MergeAfterReset member function.
Definition TFileMerger.h:73
@ kResetable
Only the objects with a MergeAfterReset member function.
Definition TFileMerger.h:72
@ kOnlyListed
Only the objects specified in fObjectNames list.
Definition TFileMerger.h:79
@ kRegular
Normal merge, overwriting the output file.
Definition TFileMerger.h:70
@ kDelayWrite
Delay the TFile write (to reduce the number of write when reusing the file)
Definition TFileMerger.h:74
Bool_t fExplicitCompLevel
True if the user explicitly requested a compression 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
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
~TFileMerger() override
Cleanup.
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 an on-disk file, usually with extension .root, that stores objects in a file-system-li...
Definition TFile.h:53
Int_t GetCompressionSettings() const
Definition TFile.h:397
Int_t GetCompressionLevel() const
Definition TFile.h:391
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:4995
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:4082
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:2429
void Close(Option_t *option="") override
Close a file.
Definition TFile.cxx:943
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 override
Find object using its name.
Book space in a file, create I/O buffers, to fill them, (un)compress them.
Definition TKey.h:28
const char * GetTitle() const override
Returns title (title can contain 32x32 xpm thumbnail/icon).
Definition TKey.cxx:1540
virtual const char * GetClassName() const
Definition TKey.h:75
virtual TObject * ReadObj()
To read a TObject* from the file.
Definition TKey.cxx:758
A doubly linked list.
Definition TList.h:38
TObject * After(const TObject *obj) const override
Returns the object after object obj.
Definition TList.cxx:328
void Clear(Option_t *option="") override
Remove all objects from the list.
Definition TList.cxx:400
TObject * FindObject(const char *name) const override
Find an object in this list using its name.
Definition TList.cxx:576
void Add(TObject *obj) override
Definition TList.h:83
TObject * Remove(TObject *obj) override
Remove object from the list.
Definition TList.cxx:820
TObject * First() const override
Return the first object in the list. Returns 0 when list is empty.
Definition TList.cxx:657
void Delete(Option_t *option="") override
Remove all objects from the list AND delete all heap based objects.
Definition TList.cxx:468
static TClass * Class()
const char * GetName() const override
Returns name of object.
Definition TNamed.h:47
An array of TObjects.
Definition TObjArray.h:31
Int_t GetEntriesFast() const
Definition TObjArray.h:58
TObject * At(Int_t idx) const override
Definition TObjArray.h:164
Collectable string class.
Definition TObjString.h:28
const char * GetName() const override
Returns name of object.
Definition TObjString.h:38
Mother of all ROOT objects.
Definition TObject.h:41
virtual void Clear(Option_t *="")
Definition TObject.h:119
@ kOverwrite
overwrite existing object with same name
Definition TObject.h:92
@ kSingleKey
write collection with single key
Definition TObject.h:91
virtual const char * GetName() const
Returns name of object.
Definition TObject.cxx:439
R__ALWAYS_INLINE Bool_t TestBit(UInt_t f) const
Definition TObject.h:199
virtual TObject * Clone(const char *newname="") const
Make a clone of an object using the Streamer facility.
Definition TObject.cxx:223
virtual void Warning(const char *method, const char *msgfmt,...) const
Issue warning message.
Definition TObject.cxx:973
virtual void Execute(const char *method, const char *params, Int_t *error=nullptr)
Execute method on this object with the given parameter string, e.g.
Definition TObject.cxx:359
R__ALWAYS_INLINE Bool_t IsZombie() const
Definition TObject.h:153
virtual Int_t Write(const char *name=nullptr, Int_t option=0, Int_t bufsize=0)
Write this object to the current directory.
Definition TObject.cxx:880
void SetBit(UInt_t f, Bool_t set)
Set or unset the user status bits as specified in f.
Definition TObject.cxx:780
virtual Bool_t InheritsFrom(const char *classname) const
Returns kTRUE if object inherits from class "classname".
Definition TObject.cxx:525
virtual void Error(const char *method, const char *msgfmt,...) const
Issue error message.
Definition TObject.cxx:987
virtual void Fatal(const char *method, const char *msgfmt,...) const
Issue fatal error message.
Definition TObject.cxx:1015
virtual const char * GetTitle() const
Returns title of object.
Definition TObject.cxx:483
virtual TClass * IsA() const
Definition TObject.h:243
void ResetBit(UInt_t f)
Definition TObject.h:198
@ kCanDelete
if object in a list can be deleted
Definition TObject.h:62
@ kMustCleanup
if object destructor must call RecursiveRemove()
Definition TObject.h:64
virtual void Info(const char *method, const char *msgfmt,...) const
Issue info message.
Definition TObject.cxx:961
Basic string class.
Definition TString.h:139
void Clear()
Clear string without changing its capacity.
Definition TString.cxx:1235
const char * Data() const
Definition TString.h:376
TObjArray * Tokenize(const TString &delim) const
This function is used to isolate sequential tokens in a TString.
Definition TString.cxx:2264
Bool_t IsNull() const
Definition TString.h:414
TString & Remove(Ssiz_t pos)
Definition TString.h:685
TString & Append(const char *cs)
Definition TString.h:572
void Form(const char *fmt,...)
Formats a string using a printf style format descriptor.
Definition TString.cxx:2356
Bool_t Contains(const char *pat, ECaseCompare cmp=kExact) const
Definition TString.h:632
virtual int Unlink(const char *name)
Unlink, i.e.
Definition TSystem.cxx:1381
virtual const char * TempDirectory() const
Return a user configured or systemwide directory to create temporary files in.
Definition TSystem.cxx:1482
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:571
This class represents a WWW compatible URL.
Definition TUrl.h:33
const char * GetFile() const
Definition TUrl.h:69
void(off) SmallVectorTemplateBase< T
Long64_t(* MergeFunc_t)(void *, TCollection *, TFileMergeInfo *)
Definition Rtypes.h:115
Short_t Min(Short_t a, Short_t b)
Returns the smallest of a and b.
Definition TMathBase.h:198