Skip to content

Remove empty directories from TFileMerger's output #19331

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions core/base/inc/TDirectory.h
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,7 @@ can be replaced with the simpler and exception safe:
static void AddDirectory(Bool_t add=kTRUE);
static Bool_t AddDirectoryStatus();
virtual void Append(TObject *obj, Bool_t replace = kFALSE);
/// Append object to this directory. \see Append(TObject*, Bool_t)
virtual void Add(TObject *obj, Bool_t replace = kFALSE) { Append(obj,replace); }
virtual Int_t AppendKey(TKey *) {return 0;}
void Browse(TBrowser *b) override;
Expand Down
1 change: 1 addition & 0 deletions io/io/inc/TFileMerger.h
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@ class TFileMerger : public TObject {
void SetMergeOptions(const TString &options) { fMergeOptions = options; }
void SetMergeOptions(const std::string_view &options) { fMergeOptions = options; }
void SetIOFeatures(ROOT::TIOFeatures &features) { fIOFeatures = &features; }
/// Add object names for PartialMerge().
void AddObjectNames(const char *name) {fObjectNames += name; fObjectNames += " ";}
const char *GetObjectNames() const {return fObjectNames.Data();}
void ClearObjectNames() {fObjectNames.Clear();}
Expand Down
32 changes: 20 additions & 12 deletions io/io/src/TFileMerger.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -591,8 +591,15 @@ Bool_t TFileMerger::MergeOne(TDirectory *target, TList *sourcelist, Int_t type,
// GetPath(), so we can still figure out where we are in the recursion

// If this folder is a onlyListed object, merge everything inside.
if (onlyListed) type &= ~kOnlyListed;
status = MergeRecursive(newdir, sourcelist, type);
const auto mergeType = onlyListed ? type & ~kOnlyListed : type;
status = MergeRecursive(newdir, sourcelist, mergeType);

if ((type & kOnlyListed) && !(type & kIncremental) && !onlyListed && newdir->GetNkeys() == 0) {
// None of the children were merged, and the directory is not listed
delete newdir;
newdir = nullptr;
target->rmdir(obj->GetName());
}
// Delete newdir directory after having written it (merged)
if (!(type&kIncremental)) delete newdir;
if (onlyListed) type |= kOnlyListed;
Expand Down Expand Up @@ -936,16 +943,17 @@ Bool_t TFileMerger::MergeRecursive(TDirectory *target, TList *sourcelist, Int_t
/// the file "FileMerger.root" in the working directory. Returns true
/// on success, false in case of error.
/// The type is defined by the bit values in EPartialMergeType:
/// kRegular : normal merge, overwritting the output file
/// kIncremental : merge the input file with the content of the output file (if already exising) (default)
/// kResetable : merge only the objects with a MergeAfterReset member function.
/// kNonResetable : merge only the objects without a MergeAfterReset member function.
/// kDelayWrite : delay the TFile write (to reduce the number of write when reusing the file)
/// kAll : merge all type of objects (default)
/// kAllIncremental : merge incrementally all type of objects.
/// kOnlyListed : merge only the objects specified in fObjectNames list
/// kSkipListed : skip objects specified in fObjectNames list
/// kKeepCompression: keep compression level unchanged for each input
///
/// kRegular : normal merge, overwriting the output file
/// kIncremental : merge the input file with the content of the output file (if already exising) (default)
/// kResetable : merge only the objects with a MergeAfterReset member function.
/// kNonResetable : merge only the objects without a MergeAfterReset member function.
/// kDelayWrite : delay the TFile write (to reduce the number of write when reusing the file)
/// kAll : merge all type of objects (default)
/// kAllIncremental : merge incrementally all type of objects.
/// kOnlyListed : merge only the objects specified in fObjectNames list
/// kSkipListed : skip objects specified in fObjectNames list
/// kKeepCompression: keep compression level unchanged for each input
///
/// If the type is not set to kIncremental, the output file is deleted at the end of this operation.

Expand Down
58 changes: 58 additions & 0 deletions io/io/test/TFileMergerTests.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -221,3 +221,61 @@ TEST(TFileMerger, ChangeFile)
gSystem->Unlink("file6640mergerinput_2.root");
gSystem->Unlink("file6640mergeroutput.root");
}

TEST(TFileMerger, SelectiveMergeWithDirectories)
{
constexpr auto input1 = "selectiveMerge_input_1.root";
constexpr auto input2 = "selectiveMerge_input_2.root";
constexpr auto output = "selectiveMerge_output.root";
for (auto const &filename : {input1, input2}) {
TH1F histo("histo", "Histo", 2, 0, 1);
TFile infile(filename, "recreate");
auto dir = infile.mkdir("A");
dir->WriteObject(&histo, "Histo_A1");
if (filename == input1)
dir->WriteObject(&histo, "Histo_A2");
if (filename == input2)
dir->WriteObject(&histo, "Histo_A3");

dir = infile.mkdir("B");
dir->WriteObject(&histo, "Histo_B");

dir = infile.mkdir("C")->mkdir("D");
dir->WriteObject(&histo, "Histo_D");
dir = infile.mkdir("E")->mkdir("F");
dir->WriteObject(&histo, "Histo_F1");
if (filename == input1)
dir->WriteObject(&histo, "Histo_F2");
if (filename == input2)
dir->WriteObject(&histo, "Histo_F3");
}

{
TFileMerger fileMerger(false);
fileMerger.AddFile(input1);
fileMerger.AddFile(input2);
fileMerger.AddObjectNames("A");
fileMerger.AddObjectNames("Histo_F1 Histo_F2 Histo_F3");
fileMerger.OutputFile(output);
fileMerger.PartialMerge(TFileMerger::kOnlyListed | TFileMerger::kAll | TFileMerger::kRegular);
}

TFile outfile(output);
auto dir = outfile.Get<TDirectory>("A");
ASSERT_NE(dir, nullptr);
for (auto name : {"Histo_A1", "Histo_A2", "Histo_A3"})
EXPECT_NE(dir->Get<TH1F>(name), nullptr) << name;

EXPECT_EQ(outfile.Get("B"), nullptr);
EXPECT_EQ(outfile.Get("C"), nullptr);

dir = outfile.Get<TDirectory>("E");
ASSERT_NE(dir, nullptr);
dir = dir->Get<TDirectory>("F");
ASSERT_NE(dir, nullptr);
for (auto name : {"Histo_F1", "Histo_F2", "Histo_F3"})
EXPECT_NE(dir->Get<TH1F>(name), nullptr) << name;

for (auto name : {input1, input2, output})
gSystem->Unlink(name);
}
Loading