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