2727using namespace ROOT ::CmdLine;
2828
2929static 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 " ;
3131static const char *const kLongHelp = R"(
3232Copy objects from ROOT files into another
3333
3434positional arguments:
35- SOURCE Source file(s)
36- DEST Destination file
35+ SOURCE Source file(s)
36+ DEST Destination file
3737
3838options:
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
4950Note: 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
307324int 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