117#include "haddCommandLineOptionsHelp.h"
126int main(
int argc,
char **argv )
128 if ( argc < 3 ||
"-h" == std::string(argv[1]) ||
"--help" == std::string(argv[1]) ) {
129 fprintf(stderr, kCommandLineOptionsHelp);
143 Int_t maxopenedfiles = 0;
144 Int_t verbosity = 99;
148 auto nProcesses = s.
fCpus;
153 for(
int a = 1;
a < argc; ++
a ) {
154 if ( strcmp(argv[
a],
"-T") == 0 ) {
157 }
else if ( strcmp(argv[
a],
"-a") == 0 ) {
160 }
else if ( strcmp(argv[
a],
"-f") == 0 ) {
163 }
else if ( strcmp(argv[
a],
"-k") == 0 ) {
166 }
else if ( strcmp(argv[
a],
"-O") == 0 ) {
169 }
else if (strcmp(argv[
a],
"-dbg") == 0) {
173 }
else if (strcmp(argv[
a],
"-d") == 0) {
174 if (
a + 1 != argc && argv[
a + 1][0] !=
'-') {
176 std::cerr <<
"Error: could not access the directory specified: " << argv[
a + 1]
177 <<
". We will use the system's temporal directory.\n";
179 workingDir = argv[
a + 1];
184 std::cout <<
"-d: no directory specified. We will use the system's temporal directory.\n";
187 }
else if (strcmp(argv[
a],
"-j") == 0) {
189 if (
a + 1 != argc && argv[
a + 1][0] !=
'-') {
192 for (
char *
c = argv[
a + 1]; *
c !=
'\0'; ++
c) {
195 std::cerr <<
"Error: could not parse the number of processes to run in parallel passed after -j: "
196 << argv[
a + 1] <<
". We will use the system maximum.\n";
202 request = strtol(argv[
a + 1], 0, 10);
203 if (request < kMaxLong && request >= 0) {
204 nProcesses = (
Int_t)request;
207 std::cout <<
"Parallelizing with " << nProcesses <<
" processes.\n";
209 std::cerr <<
"Error: could not parse the number of processes to use passed after -j: " << argv[
a + 1]
210 <<
". We will use the default value (number of logical cores).\n";
216 }
else if ( strcmp(argv[
a],
"-cachesize=") == 0 ) {
218 static const size_t arglen = strlen(
"-cachesize=");
221 std::cerr <<
"Error: could not parse the cache size passed after -cachesize: "
222 << argv[
a + 1] <<
". We will use the default value.\n";
225 const char *munit =
nullptr;
227 std::cerr <<
"Error: the cache size passed after -cachesize is too large: "
228 << argv[
a + 1] <<
" is greater than " <<
m << munit
229 <<
". We will use the default value.\n";
231 cacheSize =
"cachesize=";
235 }
else if ( strcmp(argv[
a],
"-cachesize") == 0 ) {
237 std::cerr <<
"Error: no cache size number was provided after -cachesize.\n";
242 std::cerr <<
"Error: could not parse the cache size passed after -cachesize: "
243 << argv[
a + 1] <<
". We will use the default value.\n";
246 const char *munit =
nullptr;
248 std::cerr <<
"Error: the cache size passed after -cachesize is too large: "
249 << argv[
a + 1] <<
" is greater than " <<
m << munit
250 <<
". We will use the default value.\n";
254 cacheSize =
"cachesize=";
261 }
else if (!strcmp(argv[
a],
"-experimental-io-features")) {
263 std::cerr <<
"Error: no IO feature was specified after -experimental-io-features; ignoring\n";
265 std::stringstream ss;
269 while (std::getline(ss, item,
',')) {
270 if (!features.
Set(item)) {
271 std::cerr <<
"Ignoring unknown feature request: " << item << std::endl;
276 }
else if ( strcmp(argv[
a],
"-n") == 0 ) {
278 std::cerr <<
"Error: no maximum number of opened was provided after -n.\n";
280 Long_t request = strtol(argv[
a+1], 0, 10);
281 if (request < kMaxLong && request >= 0) {
282 maxopenedfiles = (
Int_t)request;
286 std::cerr <<
"Error: could not parse the max number of opened file passed after -n: " << argv[
a+1] <<
". We will use the system maximum.\n";
290 }
else if ( strcmp(argv[
a],
"-v") == 0 ) {
291 if (
a+1 == argc || argv[
a+1][0] ==
'-') {
298 for (
char *
c = argv[
a+1]; *
c !=
'\0'; ++
c) {
301 hasFollowupNumber =
kFALSE;
305 if (hasFollowupNumber) {
306 Long_t request = strtol(argv[
a+1], 0, 10);
307 if (request < kMaxLong && request >= 0) {
308 verbosity = (
Int_t)request;
313 std::cerr <<
"Error: could not parse the verbosity level passed after -v: " << argv[
a+1] <<
". We will use the default value (99).\n";
318 }
else if ( argv[
a][0] ==
'-' ) {
320 if (force && argv[
a][1] ==
'f') {
322 std::cerr <<
"Error: Using option " << argv[
a] <<
" more than once is not supported.\n";
326 const char *prefix =
"";
327 if (argv[
a][1] ==
'f' && argv[
a][2] ==
'k') {
330 keepCompressionAsIs =
kTRUE;
333 if (argv[
a][1] ==
'f' && argv[
a][2] ==
'f') {
336 useFirstInputCompression =
kTRUE;
337 if (argv[
a][3] !=
'\0') {
338 std::cerr <<
"Error: option -ff should not have any suffix: " << argv[
a] <<
" (suffix has been ignored)\n";
342 for (
int alg = 0; !useFirstInputCompression && alg <= 5; ++alg) {
343 for(
int j=0; j<=9; ++j ) {
344 const int comp = (alg*100)+j;
345 snprintf(ft,7,
"-f%s%d",prefix,comp);
346 if (!strcmp(argv[
a],ft)) {
356 std::cerr <<
"Error: option " << argv[
a] <<
" is not a supported option.\n";
359 }
else if (!outputPlace) {
366 const char *targetname = 0;
368 targetname = argv[outputPlace];
370 targetname = argv[ffirst-1];
374 std::cout <<
"hadd Target file: " << targetname << std::endl;
380 if (maxopenedfiles > 0) {
384 if (useFirstInputCompression || keepCompressionAsIs) {
386 TFile *firstInput =
nullptr;
387 if (argv[ffirst] && argv[ffirst][0]==
'@') {
388 std::ifstream indirect_file(argv[ffirst]+1);
389 if( ! indirect_file.is_open() ) {
390 std::cerr<<
"hadd could not open indirect file " << (argv[ffirst]+1) << std::endl;
394 while( indirect_file ){
395 if( std::getline(indirect_file,
line) &&
line.length() ) {
403 if (firstInput && !firstInput->
IsZombie())
411 if (keepCompressionAsIs && !reoptimize)
412 std::cout <<
"hadd compression setting for meta data: " << newcomp <<
'\n';
414 std::cout <<
"hadd compression setting for all output: " << newcomp <<
'\n';
417 if (!fileMerger.
OutputFile(targetname,
"UPDATE", newcomp)) {
418 std::cerr <<
"hadd error opening target file for update :" << argv[ffirst-1] <<
"." << std::endl;
421 }
else if (!fileMerger.
OutputFile(targetname, force, newcomp)) {
422 std::cerr <<
"hadd error opening target file (does " << argv[ffirst-1] <<
" exist?)." << std::endl;
423 if (!force) std::cerr <<
"Pass \"-f\" argument to force re-creation of output file." << std::endl;
427 auto filesToProcess = argc - ffirst;
428 auto step = (filesToProcess + nProcesses - 1) / nProcesses;
429 if (multiproc && step < 3) {
432 nProcesses = (filesToProcess + step - 1) / step;
433 std::cout <<
"Each process should handle at least 3 files for efficiency.";
434 std::cout <<
" Setting the number of processes to: " << nProcesses << std::endl;
439 std::vector<std::string> partialFiles;
447 auto partialTail = uuid.AsString();
448 for (
auto i = 0; (i * step) < filesToProcess; i++) {
449 std::stringstream buffer;
450 buffer << workingDir <<
"/partial" << i <<
"_" << partialTail <<
".root";
451 partialFiles.emplace_back(buffer.str());
458 merger.SetFastMethod(
kFALSE);
460 if (!keepCompressionAsIs && merger.HasCompressionChange()) {
462 std::cout <<
"hadd Sources and Target have different compression levels" << std::endl;
463 std::cout <<
"hadd merging will be slower" << std::endl;
466 merger.SetNotrees(noTrees);
467 merger.SetMergeOptions(cacheSize);
468 merger.SetIOFeatures(features);
473 status = merger.Merge();
477 auto sequentialMerge = [&](
TFileMerger &merger,
int start,
int nFiles) {
479 for (
auto i = start; i < (start + nFiles) && i < argc; i++) {
480 if (argv[i] && argv[i][0] ==
'@') {
481 std::ifstream indirect_file(argv[i] + 1);
482 if (!indirect_file.is_open()) {
483 std::cerr <<
"hadd could not open indirect file " << (argv[i] + 1) << std::endl;
486 while (indirect_file) {
492 }
else if (!merger.
AddFile(argv[i])) {
494 std::cerr <<
"hadd skipping file with error: " << argv[i] << std::endl;
496 std::cerr <<
"hadd exiting due to error in " << argv[i] << std::endl;
501 return mergeFiles(merger);
504 auto parallelMerge = [&](
int start) {
508 if (maxopenedfiles > 0) {
511 if (!mergerP.
OutputFile(partialFiles[(start - ffirst) / step].c_str(), newcomp)) {
512 std::cerr <<
"hadd error opening target partial file" << std::endl;
515 return sequentialMerge(mergerP, start, step);
518 auto reductionFunc = [&]() {
519 for (
const auto &pf : partialFiles) {
520 fileMerger.
AddFile(pf.c_str());
522 return mergeFiles(fileMerger);
530 auto res =
p.Map(parallelMerge,
ROOT::TSeqI(ffirst, argc, step));
531 status = std::accumulate(res.begin(), res.end(), 0U) == partialFiles.size();
533 status = reductionFunc();
535 std::cout <<
"hadd failed at the parallel stage" << std::endl;
538 for (
const auto &pf : partialFiles) {
543 status = sequentialMerge(fileMerger, ffirst, filesToProcess);
546 status = sequentialMerge(fileMerger, ffirst, filesToProcess);
550 if (verbosity == 1) {
551 std::cout <<
"hadd merged " << fileMerger.
GetMergeList()->
GetEntries() <<
" input files in " << targetname
556 if (verbosity == 1) {
558 <<
" input files in " << targetname <<
".\n";
size_t size(const MatrixT &matrix)
retrieve the size of a square matrix
winID h TVirtualViewer3D TVirtualGLPainter p
R__EXTERN TSystem * gSystem
TIOFeatures provides the end-user with the ability to change the IO behavior of data written via a TT...
bool Set(EIOFeatures bits)
Set a specific IO feature.
This class provides a simple interface to execute the same task multiple times in parallel,...
A pseudo container class which is a generator of indices.
virtual Int_t GetEntries() const
This class provides file copy and merging services.
virtual Bool_t OutputFile(const char *url, Bool_t force)
Open merger output file.
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...
void SetMsgPrefix(const char *prefix)
Set the prefix to be used when printing informational message.
void SetPrintLevel(Int_t level)
@ kAll
Merge all type of objects (default)
@ kIncremental
Merge the input file with the content of the output file (if already existing).
void SetMaxOpenedFiles(Int_t newmax)
Set a limit to the number of files that TFileMerger will open simultaneously.
A ROOT file is composed of a header, followed by consecutive data records (TKey instances) with a wel...
Int_t GetCompressionSettings() const
static TFile * Open(const char *name, Option_t *option="", const char *ftitle="", Int_t compress=ROOT::RCompressionSetting::EDefaults::kUseCompiledDefault, Int_t netopt=0)
Create / open a file.
R__ALWAYS_INLINE Bool_t IsZombie() const
TString & Append(const char *cs)
virtual int GetSysInfo(SysInfo_t *info) const
Returns static system info, like OS type, CPU type, number of CPUs RAM size, etc into the SysInfo_t s...
virtual int Load(const char *module, const char *entry="", Bool_t system=kFALSE)
Load a shared library.
virtual Bool_t AccessPathName(const char *path, EAccessMode mode=kFileExists)
Returns FALSE if one can access a file using the specified access mode.
virtual int Unlink(const char *name)
Unlink, i.e.
virtual const char * TempDirectory() const
Return a user configured or systemwide directory to create temporary files in.
This class defines a UUID (Universally Unique IDentifier), also known as GUIDs (Globally Unique IDent...
void ToHumanReadableSize(value_type bytes, Bool_t si, Double_t *coeff, const char **units)
Return the size expressed in 'human readable' format.
EFromHumanReadableSize FromHumanReadableSize(std::string_view str, T &value)
Convert strings like the following into byte counts 5MB, 5 MB, 5M, 3.7GB, 123b, 456kB,...
@ kUseCompiledDefault
Use the compile-time default setting.