Skip to content

Commit 408bcbc

Browse files
committed
[main] Add --strict flag to rootcp and some sanity checks
1 parent 84d95cb commit 408bcbc

1 file changed

Lines changed: 65 additions & 30 deletions

File tree

main/src/rootcp.cxx

Lines changed: 65 additions & 30 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 fStrict = 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.fStrict = opts.GetSwitch("abort-on-failure");
127131

128132
if (opts.GetSwitch("vv"))
129133
SetLogVerbosity(3);
@@ -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.fStrict) {
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,6 +415,20 @@ 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_.fStrict) {
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;
402433
bool errors = false;
403434
for (const auto &[srcFname, srcPattern] : sourcesFileAndPattern) {
@@ -429,16 +460,20 @@ int main(int argc, char **argv)
429460
dest.fIsNewObject = destIsNewObject;
430461
dest.fPath = destPath;
431462
for (auto nodeIdx : src.fObjectTree.fLeafList) {
432-
CopyNode(src, dest, nodeIdx, args);
463+
if (!DoNodeCopy(src, dest, nodeIdx, args) && args.fStrict)
464+
return 1;
433465
}
434466
for (auto nodeIdx : src.fObjectTree.fDirList) {
435467
if (nodeIdx == 0) {
436-
// The root file node needs special treatment; for all other "top-level" directories, CopyNode handles them.
468+
// The root file node needs special treatment; for all other "top-level" directories, CopyNode handles
469+
// them.
437470
const auto &node = src.fObjectTree.fNodes[nodeIdx];
438471
for (auto childIdx = node.fFirstChild; childIdx < node.fFirstChild + node.fNChildren; ++childIdx)
439-
CopyNode(src, dest, childIdx, args);
472+
if (!DoNodeCopy(src, dest, childIdx, args) && args.fStrict)
473+
return 1;
440474
} else {
441-
CopyNode(src, dest, nodeIdx, args);
475+
if (!DoNodeCopy(src, dest, nodeIdx, args) && args.fStrict)
476+
return 1;
442477
}
443478
}
444479
}

0 commit comments

Comments
 (0)