Skip to content

Commit fca95ec

Browse files
committed
[main] Add --abort-on-failure flag to rootcp and some sanity checks
1 parent 84d95cb commit fca95ec

1 file changed

Lines changed: 75 additions & 38 deletions

File tree

main/src/rootcp.cxx

Lines changed: 75 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -27,24 +27,25 @@
2727
using namespace ROOT::CmdLine;
2828

2929
static const char *const kShortHelp = "usage: rootcp [-h] [-c COMPRESS] [--recreate] [-r|--recursive] [--replace] "
30-
"[-v|--verbose] SOURCE [SOURCE ...] DEST\n";
30+
"[-v|--verbose] [-a|--abort-on-failure] SOURCE [SOURCE ...] DEST\n";
3131
static const char *const kLongHelp = R"(
3232
Copy objects from ROOT files into another
3333
3434
positional arguments:
35-
SOURCE Source file(s)
36-
DEST Destination file
35+
SOURCE Source file(s)
36+
DEST Destination file
3737
3838
options:
39-
-h, --help show this help message and exit
39+
-h, --help show this help message and exit
4040
-c, --compress COMPRESS
41-
change the compression settings of the destination file (if not already
42-
existing).
43-
--recreate recreate the destination file.
44-
-r, --recursive recurse inside directories
45-
--replace replace object if already existing
46-
-v be verbose
47-
-vv be even more verbose
41+
change the compression settings of the destination file (if not already
42+
existing).
43+
--recreate recreate the destination file.
44+
-r, --recursive recurse inside directories
45+
--replace replace object if already existing
46+
-a, --abort-on-failure abort if any object fails to be copied (instead of skipping such objects)
47+
-v be verbose
48+
-vv be even more verbose
4849
4950
Note: If an object has been written to a file multiple times, rootcp will copy only the latest version of that object.
5051
@@ -86,6 +87,7 @@ struct RootCpArgs {
8687
bool fRecreate = false;
8788
bool fReplace = false;
8889
bool fRecursive = false;
90+
bool fAbortOnFailure = false;
8991
std::vector<std::string> fSources;
9092
};
9193

@@ -100,6 +102,7 @@ static RootCpArgs ParseArgs(const char **args, int nArgs)
100102
opts.AddFlag({"--recreate"});
101103
opts.AddFlag({"--replace"});
102104
opts.AddFlag({"-r", "--recursive"});
105+
opts.AddFlag({"-a", "--abort-on-failure"});
103106
opts.AddFlag({"-h", "--help"});
104107
opts.AddFlag({"-v"});
105108
opts.AddFlag({"-vv"});
@@ -124,6 +127,7 @@ static RootCpArgs ParseArgs(const char **args, int nArgs)
124127
outArgs.fRecursive = opts.GetSwitch("recursive");
125128
outArgs.fReplace = opts.GetSwitch("replace");
126129
outArgs.fRecreate = opts.GetSwitch("recreate");
130+
outArgs.fAbortOnFailure = opts.GetSwitch("abort-on-failure");
127131

128132
if (opts.GetSwitch("vv"))
129133
SetLogVerbosity(3);
@@ -143,7 +147,7 @@ static std::unique_ptr<TFile> OpenFile(const char *fileName, const char *mode)
143147
gErrorIgnoreLevel = kError;
144148
auto file = std::unique_ptr<TFile>(TFile::Open(fileName, mode));
145149
if (!file || file->IsZombie()) {
146-
Err() << "File " << fileName << "does not exist.\n";
150+
Err() << "File `" << fileName << "` does not exist.\n";
147151
return nullptr;
148152
}
149153
gErrorIgnoreLevel = origLv;
@@ -175,7 +179,9 @@ static std::pair<std::string_view, std::string_view> DecomposePath(std::string_v
175179

176180
// Copies `nodeIdx`-th node from `src`'s object tree to the file in `dest`.
177181
// `nodeIdx` is assumed to be in range.
178-
static void CopyNode(const RootSource &src, const RootCpDestination &dest, NodeIdx_t nodeIdx, const RootCpArgs &args)
182+
// Returns true if the node was successfully copied.
183+
[[nodiscard]] static bool
184+
CopyNode(const RootSource &src, const RootCpDestination &dest, NodeIdx_t nodeIdx, const RootCpArgs &args)
179185
{
180186
TFile *srcfile = src.fObjectTree.fFile.get();
181187
// The file is guaranteed to be valid by ParseRootSource: if this crashes, it's a bug in there.
@@ -208,7 +214,7 @@ static void CopyNode(const RootSource &src, const RootCpDestination &dest, NodeI
208214

209215
if (src.fFileName == dest.fFname && srcFullPath == destFullPath) {
210216
Err() << src.fFileName << ":" << srcFullPath << ": source and destination cannot be the same\n";
211-
return;
217+
return false;
212218
}
213219

214220
Info(2) << "cp " << src.fFileName << ":" << srcFullPath << " -> " << dest.fFname << ":" << destFullPath << "\n";
@@ -229,33 +235,33 @@ static void CopyNode(const RootSource &src, const RootCpDestination &dest, NodeI
229235
if (destKey && !TClass::GetClass(destKey->GetClassName())->InheritsFrom("TDirectory") && !args.fReplace) {
230236
Err() << "an object of type '" << destKey->GetClassName() << "' already exists at " << dest.fFname << ':'
231237
<< destFullPath << ". Use the --replace flag to overwrite existing objects.\n";
232-
return;
238+
return false;
233239
}
234240

235241
// retrieve the object's key
236242
const TDirectory *srcDir = srcfile->GetDirectory(std::string(srcDirPath).c_str(), true);
237243
if (!srcDir) {
238244
Err() << "failed to get source directory '" << srcDirPath << "'\n";
239-
return;
245+
return false;
240246
}
241247
const TKey *srcKey = srcDir->GetKey(node.fName.c_str());
242248
if (!srcKey) {
243249
Err() << "failed to read key of object '" << srcFullPath << "'\n";
244-
return;
250+
return false;
245251
}
246252

247253
// Verify that the class is known and supported.
248254
const std::string &className = node.fClassName;
249255
const TClass *cl = TClass::GetClass(className.c_str());
250256
if (!cl) {
251-
Err() << "unknown object type: " << className << "; object will be skipped.\n";
252-
return;
257+
Err() << "unknown object type: '" << className << "'.\n";
258+
return false;
253259
}
254260

255261
Info(3) << "read object \"" << srcFullPath << "\" of type " << node.fClassName << "\n";
256262
if (!destDir) {
257263
Err() << "failed to create or get destination directory \"" << dest.fFname << ":" << destDirPath << "\"\n";
258-
return;
264+
return false;
259265
}
260266

261267
// Delete previous object if we're replacing it
@@ -267,19 +273,24 @@ static void CopyNode(const RootSource &src, const RootCpDestination &dest, NodeI
267273
//
268274
if (cl->InheritsFrom("TObject")) {
269275
TObject *obj = node.fKey->ReadObj();
270-
if (!obj) {
276+
if (!obj || obj->IsZombie()) {
271277
Err() << "failed to read object \"" << srcFullPath << "\".\n";
272-
return;
278+
return false;
273279
}
274280

275281
if (TTree *old = dynamic_cast<TTree *>(obj)) {
276282
// special case for TTree
277283
TDirectory::TContext ctx(gDirectory, destDir);
278284
obj = old->CloneTree(-1, "fast");
279-
if (dest.fIsNewObject) {
280-
static_cast<TTree *>(obj)->SetName(std::string(destBaseName).c_str());
285+
if (!obj || obj->IsZombie()) {
286+
Err() << "failed to clone tree '" << srcFullPath << "'.\n";
287+
return false;
288+
} else {
289+
if (dest.fIsNewObject) {
290+
static_cast<TTree *>(obj)->SetName(std::string(destBaseName).c_str());
291+
}
292+
obj->Write();
281293
}
282-
obj->Write();
283294
old->Delete();
284295
} else if (cl->InheritsFrom("TDirectory")) {
285296
// directory
@@ -290,8 +301,11 @@ static void CopyNode(const RootSource &src, const RootCpDestination &dest, NodeI
290301
destDir->mkdir(node.fName.c_str(), srcKey->GetTitle(), true);
291302
RootCpDestination dest2 = dest;
292303
dest2.fPath = dest.fPath + (dest.fPath.empty() ? "" : "/") + node.fName;
293-
for (auto childIdx = node.fFirstChild; childIdx < node.fFirstChild + node.fNChildren; ++childIdx)
294-
CopyNode(src, dest2, childIdx, args);
304+
for (auto childIdx = node.fFirstChild; childIdx < node.fFirstChild + node.fNChildren; ++childIdx) {
305+
if (!CopyNode(src, dest2, childIdx, args) && args.fAbortOnFailure) {
306+
return false;
307+
}
308+
}
295309
}
296310
} else {
297311
// regular TObject
@@ -301,7 +315,10 @@ static void CopyNode(const RootSource &src, const RootCpDestination &dest, NodeI
301315
} else {
302316
Warn() << "object '" << node.fName << "' of type '" << node.fClassName
303317
<< "' will not be copied, as its type is currently unsupported by rootcp.\n";
318+
return false;
304319
}
320+
321+
return true;
305322
}
306323

307324
int main(int argc, char **argv)
@@ -398,16 +415,28 @@ int main(int argc, char **argv)
398415
if (args.fCompression)
399416
destFile->SetCompressionSettings(*args.fCompression);
400417

418+
const auto DoNodeCopy = [](auto &&src, auto &&dst, auto nodeIdx, auto &&args_) {
419+
bool ok = CopyNode(src, dst, nodeIdx, args_);
420+
if (ok)
421+
return true;
422+
423+
if (args_.fAbortOnFailure) {
424+
Err() << "node failed to be copied. Aborting.\n";
425+
return false;
426+
} else {
427+
Warn() << "node will be skipped. Use --abort-on-failure if you want to abort on failure.\n";
428+
return true;
429+
}
430+
};
431+
401432
const std::uint32_t flags = args.fRecursive * EGetMatchingPathsFlags::kRecursive;
402-
bool errors = false;
403433
for (const auto &[srcFname, srcPattern] : sourcesFileAndPattern) {
404434
auto src = ROOT::CmdLine::GetMatchingPathsInFile(srcFname, srcPattern, flags);
405435
if (!src.fErrors.empty()) {
406436
for (const auto &err : src.fErrors)
407437
Err() << err << "\n";
408438

409-
errors = true;
410-
break;
439+
goto errors;
411440
}
412441

413442
// We should never register files to the global list for performance reasons.
@@ -418,8 +447,7 @@ int main(int argc, char **argv)
418447
Err() << "multiple sources were specified but destination path \"" << destFname << ":" << destPath
419448
<< "\" is not a directory.\n";
420449

421-
errors = true;
422-
break;
450+
goto errors;
423451
}
424452

425453
// Iterate all objects we need to copy
@@ -429,24 +457,33 @@ int main(int argc, char **argv)
429457
dest.fIsNewObject = destIsNewObject;
430458
dest.fPath = destPath;
431459
for (auto nodeIdx : src.fObjectTree.fLeafList) {
432-
CopyNode(src, dest, nodeIdx, args);
460+
if (!DoNodeCopy(src, dest, nodeIdx, args) && args.fAbortOnFailure) {
461+
goto errors;
462+
}
433463
}
434464
for (auto nodeIdx : src.fObjectTree.fDirList) {
435465
if (nodeIdx == 0) {
436-
// The root file node needs special treatment; for all other "top-level" directories, CopyNode handles them.
466+
// The root file node needs special treatment; for all other "top-level" directories, CopyNode handles
467+
// them.
437468
const auto &node = src.fObjectTree.fNodes[nodeIdx];
438469
for (auto childIdx = node.fFirstChild; childIdx < node.fFirstChild + node.fNChildren; ++childIdx)
439-
CopyNode(src, dest, childIdx, args);
470+
if (!DoNodeCopy(src, dest, childIdx, args) && args.fAbortOnFailure)
471+
goto errors;
440472
} else {
441-
CopyNode(src, dest, nodeIdx, args);
473+
if (!DoNodeCopy(src, dest, nodeIdx, args) && args.fAbortOnFailure)
474+
goto errors;
442475
}
443476
}
444477
}
445478

446-
if (errors && !srcIsSameAsDstFile) {
479+
// Success.
480+
return 0;
481+
482+
errors:
483+
if (!srcIsSameAsDstFile) {
447484
// If the destination file was fresh, make sure we don't end up with a half-copied file in case of errors.
448485
gSystem->Unlink(std::string(destFname).c_str());
449486
}
450487

451-
return errors;
488+
return 1;
452489
}

0 commit comments

Comments
 (0)