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