File indexing completed on 2026-01-09 09:30:03
0001
0002
0003
0004
0005 #include "eicrecon_cli.h"
0006
0007 #include <JANA/CLI/JBenchmarker.h>
0008 #include <JANA/CLI/JSignalHandler.h>
0009 #include <JANA/JApplication.h>
0010 #include <JANA/JVersion.h>
0011 #include <JANA/Services/JComponentManager.h>
0012 #include <algorithm>
0013 #include <cstdlib>
0014 #include <cstring>
0015 #include <filesystem>
0016 #include <iostream>
0017 #include <memory>
0018 #include <set>
0019 #include <sstream>
0020 #include <stdexcept>
0021 #include <string>
0022 #include <utility>
0023
0024 #include "JANA/JEventSource.h"
0025 #include "JANA/JException.h"
0026 #include "JANA/Services/JParameterManager.h"
0027 #include "print_info.h"
0028
0029 #define QUOTE(name) #name
0030 #define STR(macro) QUOTE(macro)
0031
0032 #ifndef EICRECON_APP_VERSION
0033 #define EICRECON_APP_VERSION Error
0034 #endif
0035
0036 #define EICRECON_APP_VERSION_STR STR(EICRECON_APP_VERSION)
0037
0038 namespace jana {
0039
0040 void PrintUsageOptions() {
0041 std::cout << "Options:" << std::endl;
0042 std::cout << " -h --help Display this message" << std::endl;
0043 std::cout << " -v --version Display version information" << std::endl;
0044 std::cout << " -j --janaversion Display JANA version information" << std::endl;
0045 std::cout << " -c --configs Display configuration parameters" << std::endl;
0046 std::cout << " -l --loadconfigs <file> Load configuration parameters from file"
0047 << std::endl;
0048 std::cout << " -d --dumpconfigs <file> Dump configuration parameters to file" << std::endl;
0049 std::cout << " -b --benchmark Run in benchmark mode" << std::endl;
0050 std::cout << " -L --list-factories List all the factories without running"
0051 << std::endl;
0052 std::cout << " -Pkey=value Specify a configuration parameter" << std::endl;
0053 std::cout << " -Pplugin:param=value Specify a parameter value for a plugin"
0054 << std::endl;
0055 std::cout << std::endl;
0056
0057 std::cout << " --list-default-plugins List all the default plugins" << std::endl;
0058 std::cout << " --list-available-plugins List plugins at $JANA_PLUGIN_PATH and $EICrecon_MY"
0059 << std::endl;
0060 std::cout << std::endl;
0061
0062 std::cout << " -Pplugins=values Comma-separated list of extra plugins to load"
0063 << std::endl;
0064 std::cout << " -Pplugins_to_ignore=values Comma-separated list of plugins to ignore"
0065 << std::endl;
0066
0067 std::cout << std::endl << std::endl;
0068 }
0069
0070 void PrintUsageExample() {
0071
0072 std::cout << "Example:" << std::endl;
0073 std::cout << " eicrecon -Pplugins=plugin1,plugin2,plugin3 -Pnthreads=8 infile.root"
0074 << std::endl;
0075 std::cout << " eicrecon -Ppodio:print_type_table=1 infile.root" << std::endl << std::endl;
0076 std::cout << std::endl << std::endl;
0077 }
0078
0079 void PrintUsage() {
0080
0081
0082
0083
0084 std::cout << std::endl;
0085 std::cout << "Usage:" << std::endl;
0086 std::cout << " eicrecon [options] source1 source2 ..." << std::endl;
0087 std::cout << std::endl;
0088
0089 std::cout << "Description:" << std::endl;
0090 std::cout << " Command-line interface for running JANA plugins. This can be used to"
0091 << std::endl;
0092 std::cout << " read in events and process them. Command-line flags control configuration"
0093 << std::endl;
0094 std::cout << " while additional arguments denote input files, which are to be loaded and"
0095 << std::endl;
0096 std::cout << " processed by the appropriate EventSource plugin." << std::endl;
0097 std::cout << std::endl;
0098
0099 PrintUsageOptions();
0100 PrintUsageExample();
0101 }
0102
0103 void PrintVersion() { std::cout << "EICrecon version: " << EICRECON_APP_VERSION_STR << std::endl; }
0104
0105 void PrintJANAVersion() { std::cout << "JANA version: " << JVersion::GetVersion() << std::endl; }
0106
0107 void PrintDefaultPlugins(std::vector<std::string> const& default_plugins) {
0108 std::cout << "\n List default plugins:\n\n";
0109 printPluginNames(default_plugins);
0110 std::cout << std::endl << std::endl;
0111 }
0112
0113 void GetPluginNamesInDir(std::set<std::string>& plugin_names, std::string dir_str) {
0114
0115 if (!std::filesystem::is_directory(dir_str)) {
0116 return;
0117 }
0118 if (std::filesystem::is_empty(dir_str)) {
0119 return;
0120 }
0121
0122 std::string full_path;
0123 std::string filename;
0124 for (const auto& entry : std::filesystem::directory_iterator(dir_str)) {
0125 full_path = std::string(entry.path());
0126 filename = full_path.substr(full_path.find_last_of("/") + 1);
0127 if (filename.substr(filename.size() - 3) == ".so") {
0128 std::string s = filename.substr(0, filename.size() - 3);
0129
0130 plugin_names.insert(s);
0131 }
0132 }
0133 }
0134
0135
0136
0137
0138 void GetPluginNamesFromEnvPath(std::set<std::string>& plugin_names, const char* env_var) {
0139 std::string dir_path;
0140 std::string paths;
0141
0142 const char* env_p = getenv(env_var);
0143 if (env_p != nullptr) {
0144 if (strcmp(env_var, "EICrecon_MY") == 0) {
0145 paths = std::string(env_p) + "/plugins";
0146 } else {
0147 paths = std::string(env_p);
0148 }
0149
0150 std::stringstream envvar_ss(paths);
0151 while (getline(envvar_ss, dir_path, ':')) {
0152 GetPluginNamesInDir(plugin_names, dir_path);
0153 }
0154 }
0155 }
0156
0157 std::vector<std::string> GetAvailablePluginNames(std::vector<std::string> const& default_plugins) {
0158
0159
0160 std::set<std::string> set_plugin_name;
0161 for (std::string s : default_plugins) {
0162 set_plugin_name.insert(s);
0163 }
0164
0165 jana::GetPluginNamesFromEnvPath(set_plugin_name, "JANA_PLUGIN_PATH");
0166 jana::GetPluginNamesFromEnvPath(set_plugin_name, "EICrecon_MY");
0167
0168 std::vector<std::string> plugin_names(set_plugin_name.begin(), set_plugin_name.end());
0169 return plugin_names;
0170 }
0171
0172 void PrintAvailablePlugins(std::vector<std::string> const& default_plugins) {
0173 std::cout << "\n List available plugins:\n\n";
0174 printPluginNames(GetAvailablePluginNames(default_plugins));
0175 std::cout << std::endl << std::endl;
0176 }
0177
0178 bool HasPrintOnlyCliOptions(UserOptions& options, std::vector<std::string> const& default_plugins) {
0179 if (options.flags[jana::ShowUsage]) {
0180 jana::PrintUsage();
0181 return true;
0182 }
0183 if (options.flags[jana::ShowVersion]) {
0184 jana::PrintVersion();
0185 return true;
0186 }
0187 if (options.flags[jana::ShowJANAVersion]) {
0188 jana::PrintJANAVersion();
0189 return true;
0190 }
0191 if (options.flags[jana::ShowDefaultPlugins]) {
0192 jana::PrintDefaultPlugins(default_plugins);
0193 return true;
0194 }
0195 if (options.flags[jana::ShowAvailablePlugins]) {
0196 jana::PrintAvailablePlugins(default_plugins);
0197 return true;
0198 }
0199 return false;
0200 }
0201
0202
0203
0204
0205 bool HasExcludeDefaultPluginsInCliParams(UserOptions& options, const std::string erase_str) {
0206 auto has_ignore_plugins = options.params.find("plugins_to_ignore");
0207 if (has_ignore_plugins == options.params.end()) {
0208 return false;
0209 }
0210
0211
0212 std::size_t pos = has_ignore_plugins->second.find(erase_str);
0213 if (pos == std::string::npos) {
0214 return false;
0215 }
0216
0217
0218 std::string ignore_str;
0219 if (erase_str.length() + pos ==
0220 has_ignore_plugins->second.length()) {
0221 ignore_str = has_ignore_plugins->second.erase(pos, erase_str.length());
0222 } else {
0223 ignore_str = has_ignore_plugins->second.erase(pos, erase_str.length() + 1);
0224 }
0225 options.params["plugins_to_ignore"] = ignore_str;
0226 return true;
0227 }
0228
0229 void AddAvailablePluginsToOptionParams(UserOptions& options,
0230 std::vector<std::string> const& default_plugins) {
0231
0232 std::set<std::string> set_plugins;
0233
0234
0235
0236
0237 std::string plugins_str;
0238 for (std::string s : set_plugins) {
0239 plugins_str += s + ",";
0240 }
0241
0242
0243
0244 if (!HasExcludeDefaultPluginsInCliParams(options, "default")) {
0245
0246
0247 for (std::string s : default_plugins) {
0248 plugins_str += s + ",";
0249 }
0250 }
0251
0252
0253 auto has_cli_plugin_params = options.params.find("plugins");
0254 if (has_cli_plugin_params != options.params.end()) {
0255 plugins_str += has_cli_plugin_params->second;
0256 options.params["plugins"] = plugins_str;
0257 } else {
0258 options.params["plugins"] = plugins_str.substr(0, plugins_str.size() - 1);
0259 }
0260 }
0261
0262 JApplication* CreateJApplication(UserOptions& options) {
0263
0264 auto* para_mgr =
0265 new JParameterManager();
0266
0267
0268 for (auto pair : options.params) {
0269 para_mgr->SetParameter(pair.first, pair.second);
0270 }
0271
0272
0273 if (options.flags[ListFactories]) {
0274 para_mgr->SetParameter("log:off", "JPluginLoader,JArrowProcessingController,JArrow");
0275 }
0276
0277 if (options.flags[LoadConfigs]) {
0278
0279 try {
0280 para_mgr->ReadConfigFile(options.load_config_file);
0281 } catch (JException& e) {
0282 std::cout << "Problem loading config file '" << options.load_config_file << "'. Exiting."
0283 << std::endl
0284 << std::endl;
0285 exit(-1);
0286 }
0287 std::cout << "Loaded config file '" << options.load_config_file << "'." << std::endl
0288 << std::endl;
0289 }
0290
0291
0292
0293 if (para_mgr->FindParameter("jana:timeout") == nullptr) {
0294 para_mgr->SetParameter("jana:timeout", 180);
0295 para_mgr->SetParameter("jana:warmup_timeout", 180);
0296 }
0297
0298 auto* app = new JApplication(para_mgr);
0299
0300 const char* env_p = getenv("EICrecon_MY");
0301 if (env_p != nullptr) {
0302 app->AddPluginPath(std::string(env_p) + "/plugins");
0303 }
0304
0305 for (auto event_src : options.eventSources) {
0306 app->Add(event_src);
0307 }
0308 return app;
0309 }
0310
0311 void AddDefaultPluginsToJApplication(JApplication* app,
0312 std::vector<std::string> const& default_plugins) {
0313 for (std::string s : default_plugins) {
0314 app->AddPlugin(s);
0315 }
0316 }
0317
0318 void PrintFactories(JApplication* app) {
0319 std::cout << std::endl << "List all the factories:" << std::endl << std::endl;
0320 printFactoryTable(app->GetComponentSummary());
0321 std::cout << std::endl;
0322 }
0323
0324 void PrintPodioCollections(JApplication* app) {
0325 if (app->GetJParameterManager()->Exists("PODIO:PRINT_TYPE_TABLE")) {
0326 bool print_type_table = app->GetParameterValue<bool>("podio:print_type_table");
0327
0328
0329 if (print_type_table) {
0330 auto event_sources = app->GetService<JComponentManager>()->get_evt_srces();
0331 for (auto* event_source : event_sources) {
0332
0333
0334 if (event_source->GetPluginName().find("podio") != std::string::npos) {
0335 event_source->DoOpen();
0336 }
0337 }
0338 }
0339 }
0340 }
0341
0342 void PrintConfigParameters(JApplication* app) {
0343
0344
0345
0346
0347
0348
0349
0350 auto params = app->GetJParameterManager()->GetAllParameters();
0351 std::size_t max_key_length = 0;
0352 std::size_t max_val_length = 0;
0353 std::size_t max_max_val_length = 32;
0354 for (auto& [key, p] : params) {
0355 max_key_length = std::max(key.length(), max_key_length);
0356 if (p->GetValue().length() > max_val_length) {
0357 if (p->GetValue().length() <= max_max_val_length) {
0358 max_val_length = p->GetValue().length();
0359 }
0360 }
0361 }
0362
0363 std::cout << "\nConfiguration Parameters:" << std::endl;
0364 std::cout << "Name" + std::string(std::max(max_key_length, std::size_t(4)) - 4, ' ') << " : ";
0365 std::cout << "Value" + std::string(std::max(max_val_length, std::size_t(5)) - 5, ' ') << " : ";
0366 std::cout << "Description" << std::endl;
0367 std::cout << std::string(max_key_length + max_val_length + 20, '-') << std::endl;
0368 for (auto& [key, p] : params) {
0369 std::stringstream ss;
0370 int key_length_diff = max_key_length - key.length();
0371 if (key_length_diff > 0) {
0372 ss << std::string(key_length_diff, ' ');
0373 }
0374 ss << key;
0375 ss << " | ";
0376
0377 int val_length_diff = max_val_length - p->GetValue().length();
0378 if (val_length_diff > 0) {
0379 ss << std::string(val_length_diff, ' ');
0380 }
0381 ss << p->GetValue();
0382 ss << " | ";
0383 ss << p->GetDescription();
0384
0385 std::cout << ss.str() << std::endl;
0386 }
0387 std::cout << std::string(max_key_length + max_val_length + 20, '-') << std::endl;
0388 }
0389
0390 int Execute(JApplication* app, UserOptions& options) {
0391
0392 std::cout << std::endl;
0393
0394 if (options.flags[ShowConfigs]) {
0395
0396 app->Initialize();
0397 if (options.flags[Benchmark]) {
0398 JBenchmarker benchmarker(
0399 app);
0400 }
0401
0402 PrintConfigParameters(app);
0403 } else if (options.flags[DumpConfigs]) {
0404
0405 app->Initialize();
0406 std::cout << std::endl
0407 << "Writing configuration options to file: " << options.dump_config_file << std::endl;
0408 app->GetJParameterManager()->WriteConfigFile(options.dump_config_file);
0409 } else if (options.flags[Benchmark]) {
0410 JSignalHandler::register_handlers(app);
0411
0412 JBenchmarker benchmarker(app);
0413 benchmarker.RunUntilFinished();
0414 } else if (options.flags[ListFactories]) {
0415 app->Initialize();
0416 PrintFactories(app);
0417
0418
0419 PrintPodioCollections(app);
0420 } else {
0421 if ((JVersion::GetMajorNumber() == 2) && (JVersion::GetMinorNumber() == 3) &&
0422 (JVersion::GetPatchNumber() <= 1)) {
0423
0424 if (not app->GetJParameterManager()->Exists("jana:parameter_verbosity")) {
0425 app->GetJParameterManager()->SetParameter("jana:parameter_verbosity", 0);
0426 }
0427 }
0428 if (not app->GetJParameterManager()->Exists("jana:parameter_strictness")) {
0429 app->GetJParameterManager()->SetParameter("jana:parameter_strictness", 2);
0430 }
0431
0432 try {
0433 JSignalHandler::register_handlers(app);
0434 app->Run();
0435 } catch (JException& e) {
0436 std::cout << "----------------------------------------------------------" << std::endl;
0437 std::cout << e << std::endl;
0438 app->SetExitCode(EXIT_FAILURE);
0439 } catch (std::runtime_error& e) {
0440 std::cout << "----------------------------------------------------------" << std::endl;
0441 std::cout << "Exception: " << e.what() << std::endl;
0442 app->SetExitCode(EXIT_FAILURE);
0443 }
0444 }
0445 return app->GetExitCode();
0446 }
0447
0448 UserOptions GetCliOptions(int nargs, char* argv[], bool expect_extra) {
0449
0450 UserOptions options;
0451
0452 std::map<std::string, Flag> tokenizer;
0453 tokenizer["-h"] = ShowUsage;
0454 tokenizer["--help"] = ShowUsage;
0455 tokenizer["-v"] = ShowVersion;
0456 tokenizer["--version"] = ShowVersion;
0457 tokenizer["-j"] = ShowJANAVersion;
0458 tokenizer["--janaversion"] = ShowJANAVersion;
0459 tokenizer["-c"] = ShowConfigs;
0460 tokenizer["--configs"] = ShowConfigs;
0461 tokenizer["-l"] = LoadConfigs;
0462 tokenizer["--loadconfigs"] = LoadConfigs;
0463 tokenizer["-d"] = DumpConfigs;
0464 tokenizer["--dumpconfigs"] = DumpConfigs;
0465 tokenizer["-b"] = Benchmark;
0466 tokenizer["--benchmark"] = Benchmark;
0467 tokenizer["-L"] = ListFactories;
0468 tokenizer["--list-factories"] = ListFactories;
0469 tokenizer["--list-default-plugins"] = ShowDefaultPlugins;
0470 tokenizer["--list-available-plugins"] = ShowAvailablePlugins;
0471
0472
0473 if (nargs == 1) {
0474 options.flags[ShowUsage] = true;
0475 }
0476
0477 for (int i = 1; i < nargs; i++) {
0478
0479 std::string arg = argv[i];
0480
0481
0482 if (argv[i][0] != '-') {
0483 options.eventSources.push_back(arg);
0484 continue;
0485 }
0486
0487 switch (tokenizer[arg]) {
0488
0489 case Benchmark:
0490 options.flags[Benchmark] = true;
0491 break;
0492
0493 case ShowUsage:
0494 options.flags[ShowUsage] = true;
0495 break;
0496
0497 case ShowVersion:
0498 options.flags[ShowVersion] = true;
0499 break;
0500
0501 case ShowJANAVersion:
0502 options.flags[ShowJANAVersion] = true;
0503 break;
0504
0505 case ShowConfigs:
0506 options.flags[ShowConfigs] = true;
0507 break;
0508
0509 case LoadConfigs:
0510 options.flags[LoadConfigs] = true;
0511 if (i + 1 < nargs && argv[i + 1][0] != '-') {
0512 options.load_config_file = argv[i + 1];
0513 i += 1;
0514 } else {
0515 options.load_config_file = "jana.config";
0516 }
0517 break;
0518
0519 case DumpConfigs:
0520 options.flags[DumpConfigs] = true;
0521 if (i + 1 < nargs && argv[i + 1][0] != '-') {
0522 options.dump_config_file = argv[i + 1];
0523 i += 1;
0524 } else {
0525 options.dump_config_file = "jana.config";
0526 }
0527 break;
0528
0529 case ListFactories:
0530 options.flags[ListFactories] = true;
0531 break;
0532
0533 case ShowDefaultPlugins:
0534 options.flags[ShowDefaultPlugins] = true;
0535 break;
0536
0537 case ShowAvailablePlugins:
0538 options.flags[ShowAvailablePlugins] = true;
0539 break;
0540
0541
0542 case Unknown:
0543 if (argv[i][0] == '-' && argv[i][1] == 'P') {
0544
0545 std::size_t pos = arg.find("=");
0546 if ((pos != std::string::npos) && (pos > 2)) {
0547 std::string key = arg.substr(2, pos - 2);
0548 std::string val = arg.substr(pos + 1);
0549 if (options.params.find(key) != options.params.end()) {
0550 std::cout << "Duplicate parameter '" << arg << "' ignored" << std::endl;
0551 } else {
0552 options.params.insert({key, val});
0553 }
0554 } else {
0555 std::cout << "Invalid JANA parameter '" << arg << "': Expected format -Pkey=value"
0556 << std::endl;
0557 options.flags[ShowConfigs] = true;
0558 }
0559 } else {
0560 if (!expect_extra) {
0561 std::cout << "Invalid command line flag '" << arg << "'" << std::endl;
0562 options.flags[ShowUsage] = true;
0563 }
0564 }
0565 }
0566 }
0567 return options;
0568 }
0569 }