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