goptions
Loading...
Searching...
No Matches
goptions.cc
Go to the documentation of this file.
1
14// goptions
15#include "goptions.h"
16#include "goptionsConventions.h"
17#include "gversion.h"
18
19// gemc
20#include "gutilities.h"
21
22// c++
23#include <algorithm>
24#include <iostream>
25#include <cstring>
26#include <cctype>
27#include <cstdlib>
28
29using namespace std;
30
31namespace {
32// Parse a human-friendly boolean token used for switches (-switch=value and YAML switch values).
33// Accepts, case-insensitively: true/1/yes/y/on and false/0/no/n/off. Returns false if @p raw is
34// not a recognized token, leaving @p out untouched.
35bool parse_bool_token(const std::string& raw, bool& out) {
36 std::string v = raw;
37 std::transform(v.begin(), v.end(), v.begin(),
38 [](unsigned char c) { return static_cast<char>(std::tolower(c)); });
39 if (v == "true" || v == "1" || v == "yes" || v == "y" || v == "on") { out = true; return true; }
40 if (v == "false" || v == "0" || v == "no" || v == "n" || v == "off") { out = false; return true; }
41 return false;
42}
43}
44
45// See goptions.h for full constructor API documentation.
46/*
47 * Parsing precedence implemented here:
48 * 1. YAML file(s), applied in argv order
49 * 2. Command-line tokens override YAML values
50 *
51 * Notes:
52 * - "help <option>" is treated as an immediate action and exits after printing.
53 * - Dot-notation routes structured updates to the owning option via GOption::set_sub_option_value().
54 */
55GOptions::GOptions(int argc, char* argv[], const GOptions& user_defined_options) {
56 executableName = gutilities::getFileFromPath(argv[0]);
57 executableCallingDir = gutilities::getDirFromPath(argv[0]);
58 installDir = gutilities::gemc_root();
59 cout << endl;
60
61 // Add user-defined options.
62 addGOptions(user_defined_options);
63
64 // switches for all everyone
65 defineSwitch("gui", "run with the graphical user interface (Qt window)");
66 defineSwitch("i", "drop into the interactive Geant4 terminal session (non-GUI mode)");
68 GVariable("conf_yaml", "saved_configuration", "infix for the YAML file that records the resolved options"),
69 "On exit the resolved configuration is written to <executable>.<conf_yaml>.yaml,\n"
70 "so the default value produces, for example, gemc.saved_configuration.yaml.\n \n"
71 "Example: -conf_yaml=run12 -> saves to gemc.run12.yaml\n \n");
72
73 // add test timeout for the tests
74 defineOption(GVariable("tt", 500, "GUI test timeout (ms)"),
75 "Milliseconds a GUI-based test waits before it auto-closes, so the module\n"
76 "example/test programs can run unattended in CI.\n \n"
77 "Example: -tt=1000\n \n");
78
79 // version is a special option, not settable by the user
80 // it is set by the gversion.h file
81 // we add it here so it can be saved to the yaml file
82 vector<GVariable> version = {
83 {"release", gversion, "release version number"},
84 {"release_date", grelease_date, "release date"},
85 {"Reference", greference, "article reference"},
86 {"Homepage", gweb, "homepage"},
87 {"Author", gauthor, "author"}
88 };
89 defineOption(GVERSION_STRING, "version information", version, "Version information. Not settable by user.");
90
91 // verbosity option: convention used across modules consuming verbosity levels
92 string help = "Levels: \n \n";
93 help += " - 0: (default) = shush\n";
94 help += " - 1: log detailed information\n";
95 help += " - 2: log extra detailed information\n \n";
96 help += "Each key names a class or module; run 'help verbosity' to list the available keys.\n \n";
97 help += "Example (one key): -verbosity.gemc=1\n";
98 help += "Example (several keys): -verbosity=\"[{gemc: 1}, {<another_key>: 2}]\"\n \n";
99 help += "Equivalent YAML:\n";
100 help += " verbosity:\n";
101 help += " - gemc: 1\n";
102 help += " - <another_key>: 2\n \n";
103 defineOption("verbosity", "Sets the log verbosity for various classes", option_verbosity_names, help);
104
105 // debug option: boolean or integer, depending on consumer expectations
106 help = "Debug information Types: \n \n";
107 help += " - false: (default): do not print debug information\n";
108 help += " - true: print debug information\n\n";
109 help += "Each key names a class or module; run 'help debug' to list the available keys.\n \n";
110 help += "Example (on/off): -debug.gemc=true\n";
111 help += "Example (several keys): -debug=\"[{gemc: true}, {<another_key>: 1}]\"\n \n";
112 help += "Equivalent YAML:\n";
113 help += " debug:\n";
114 help += " - gemc: true\n \n";
115 defineOption("debug", "Sets the debug level for various classes", option_verbosity_names, help);
116
117 // Process help/version command-line arguments.
118 // These are handled early and exit immediately (they do not proceed to parse YAML/options).
119 for (int i = 1; i < argc; i++) {
120 if (strcmp(argv[i], "-h") == 0 || strcmp(argv[i], "--h") == 0 ||
121 strcmp(argv[i], "-help") == 0 || strcmp(argv[i], "--help") == 0) {
122 printHelp();
123 }
124 else if (strcmp(argv[i], "-hweb") == 0) {
125 printWebHelp();
126 }
127 else if (strcmp(argv[i], "-v") == 0 || strcmp(argv[i], "--v") == 0 ||
128 strcmp(argv[i], "-version") == 0 || strcmp(argv[i], "--version") == 0) {
129 print_version();
130 exit(EXIT_SUCCESS);
131 }
132 else if (strcmp(argv[i], "help") == 0) {
133 // "gemc help <topic>" shows topic help; bare "gemc help" shows the general help index.
134 if (i + 1 < argc) { printOptionOrSwitchHelp(argv[i + 1]); }
135 else { printHelp(); }
136 exit(EXIT_SUCCESS);
137 }
138 else if (strcmp(argv[i], "search") == 0) {
139 // "gemc search <value>" lists options/switches whose name or description contains <value>.
140 if (i + 1 < argc) { printSearch(argv[i + 1]); }
141 else { printHelp(); }
142 exit(EXIT_SUCCESS);
143 }
144 }
145
146 // finds and parse the yaml files
147 // YAML file tokens are treated as inputs, not as "invalid command-line arguments".
148 yaml_files = findYamls(argc, argv);
149 for (auto& yaml_file : yaml_files) {
150 cout << " Parsing " << yaml_file << endl;
151 setOptionsValuesFromYamlFile(yaml_file);
152 }
153
154 // Parse command-line arguments (supports both standard YAML–style and dot–notation).
155 for (int i = 1; i < argc; i++) {
156 string candidate = argv[i];
157 if (candidate.empty()) continue;
158
159 // Skip YAML file tokens: they were already handled above.
160 if (find(yaml_files.begin(), yaml_files.end(), candidate) != yaml_files.end()) continue;
161
162 if (candidate[0] == '-') {
163 string argStr = candidate.substr(1);
164 size_t eqPos = argStr.find('=');
165
166 if (eqPos != string::npos) {
167 string keyPart = argStr.substr(0, eqPos);
168 string valuePart = argStr.substr(eqPos + 1);
169
170 // Switch with an explicit boolean value: -gui=false / -gui=true. This gives the
171 // CLI a way to turn a switch off, honoring the documented CLI-over-YAML precedence.
172 if (switches.find(keyPart) != switches.end()) {
173 bool on = false;
174 if (parse_bool_token(valuePart, on)) { on ? switches[keyPart].turnOn() : switches[keyPart].turnOff(); }
175 else {
176 cerr << "The switch " << keyPart << " accepts only true/false/yes/no/on/off/1/0." << endl;
177 exit(EC__NOOPTIONFOUND);
178 }
179 continue;
180 }
181
182 // Strip outer quotes if present (e.g., -gstreamer="[...]")
183 if (!valuePart.empty() && valuePart.front() == '"' && valuePart.back() == '"') {
184 valuePart = valuePart.substr(1, valuePart.length() - 2);
185 }
186
187 // Dot-notation targets a subkey in a structured option (e.g., verbosity.gemc).
188 size_t dotPos = keyPart.find('.');
189 if (dotPos != string::npos) {
190 string mainOption = keyPart.substr(0, dotPos);
191 string subOption = keyPart.substr(dotPos + 1);
192
193 if (doesOptionExist(mainOption)) {
194 auto it = getOptionIterator(mainOption);
195 it->set_sub_option_value(subOption, valuePart);
196 }
197 else {
198 cerr << "The option " << mainOption << " is not known to this system." << endl;
199 exit(EC__NOOPTIONFOUND);
200 }
201 }
202 else {
203 // Standard option syntax: -name=value
204 if (doesOptionExist(keyPart)) {
205 setOptionValuesFromCommandLineArgument(keyPart, valuePart);
206 }
207 else {
208 cerr << "The option " << keyPart << " is not known to this system." << endl;
209 exit(EC__NOOPTIONFOUND);
210 }
211 }
212 }
213 else {
214 // Treat as a switch: -gui, -i, etc.
215 const string& possibleSwitch = argStr;
216 if (switches.find(possibleSwitch) != switches.end()) {
217 switches[possibleSwitch].turnOn();
218 }
219 else {
220 cerr << "The switch " << possibleSwitch << " is not known to this system." << endl;
221 exit(EC__NOOPTIONFOUND);
222 }
223 }
224 }
225 else {
226 cerr << "The command-line argument \"" << candidate << "\" is not valid." << endl;
227 exit(EC__NOOPTIONFOUND);
228 }
229 }
230
231 // Always print version information.
232 print_version();
233
234 // Save the final configuration to a YAML file.
235 string yamlConf_filename = executableName + "." + getScalarString("conf_yaml") + ".yaml";
236 cout << " Saving options to " << yamlConf_filename << endl << endl;
237 yamlConf = new std::ofstream(yamlConf_filename);
238 saveOptions();
239}
240
241// Implementation note: public API docs are in goptions.h (avoid duplicate \param blocks).
242void GOptions::defineSwitch(const std::string& name, const std::string& description, bool default_status) {
243 if (switches.find(name) == switches.end()) {
244 switches[name] = GSwitch(description, default_status);
245 }
246 else {
247 std::cerr << FATALERRORL << "The " << YELLOWHHL << name << RSTHHR
248 << " switch is already present." << std::endl;
250 }
251}
252
253// Implementation note: public API docs are in goptions.h (avoid duplicate \param blocks).
254void GOptions::defineOption(const GVariable& gvar, const std::string& help) {
255 if (doesOptionExist(gvar.name)) {
256 std::cerr << FATALERRORL << "The " << YELLOWHHL << gvar.name << RSTHHR
257 << " option is already present." << std::endl;
259 }
260 else {
261 goptions.emplace_back(gvar, help);
262 }
263}
264
265// Implementation note: public API docs are in goptions.h (avoid duplicate \param blocks).
266void GOptions::defineOption(const std::string& name, const std::string& description,
267 const std::vector<GVariable>& gvars,
268 const std::string& help) {
269 if (doesOptionExist(name)) {
270 std::cerr << FATALERRORL << "The " << YELLOWHHL << name << RSTHHR
271 << " option is already present." << std::endl;
273 }
274 else {
275 goptions.emplace_back(name, description, gvars, help);
276 }
277}
278
279// Implementation note: public API docs are in goptions.h (avoid duplicate \param blocks).
280int GOptions::getScalarInt(const std::string& tag) const {
281 auto it = getOptionIterator(tag);
282 if (it == goptions.end()) {
283 cerr << FATALERRORL << "The option " << YELLOWHHL << tag << RSTHHR
284 << " was not found." << endl;
285 exit(EC__NOOPTIONFOUND);
286 }
287 return it->value.begin()->second.as<int>();
288}
289
290// Implementation note: public API docs are in goptions.h (avoid duplicate \param blocks).
291double GOptions::getScalarDouble(const std::string& tag) const {
292 auto it = getOptionIterator(tag);
293 if (it == goptions.end()) {
294 cerr << FATALERRORL << "The option " << YELLOWHHL << tag << RSTHHR
295 << " was not found." << endl;
296 exit(EC__NOOPTIONFOUND);
297 }
298 return it->value.begin()->second.as<double>();
299}
300
301// Implementation note: public API docs are in goptions.h (avoid duplicate \param blocks).
302std::string GOptions::getScalarString(const std::string& tag) const {
303 auto it = getOptionIterator(tag);
304 if (it == goptions.end()) {
305 std::cerr << FATALERRORL << "The option " << YELLOWHHL << tag << RSTHHR
306 << " was not found." << std::endl;
307 std::exit(EC__NOOPTIONFOUND);
308 }
309 const YAML::Node node = it->value.begin()->second;
310 if (node.IsNull()) return "NULL"; // force the exact sentinel you prefer
311 return node.as<std::string>();
312}
313
314
315// Private method: see header. Kept undocumented here to avoid duplicate param docs.
316void GOptions::printOptionOrSwitchHelp(const std::string& tag) const {
317 auto switchIt = switches.find(tag);
318 if (switchIt != switches.end()) {
319 cout << KGRN << "-" << tag << RST << ": " << switchIt->second.getDescription() << endl << endl;
320 cout << TPOINTITEM << "Default value is " << (switchIt->second.getStatus() ? "on" : "off") << endl << endl;
321 exit(EXIT_SUCCESS);
322 }
323 for (const auto& goption : goptions) {
324 if (goption.name == tag) {
325 goption.printHelp(true);
326 exit(EXIT_SUCCESS);
327 }
328 }
329 cerr << FATALERRORL << "The " << YELLOWHHL << tag << RSTHHR
330 << " option is not known to this system." << endl;
331 exit(EC__NOOPTIONFOUND);
332}
333
334// Private method: see header. Kept undocumented here to avoid duplicate param docs.
335void GOptions::printSearch(const std::string& tag) const {
336 // Case-insensitive substring match against switch/option names and descriptions.
337 auto to_lower = [](string s) {
338 transform(s.begin(), s.end(), s.begin(), [](unsigned char c) { return std::tolower(c); });
339 return s;
340 };
341 const string needle = to_lower(tag);
342 auto matches = [&](const string& a, const string& b) {
343 return to_lower(a).find(needle) != string::npos || to_lower(b).find(needle) != string::npos;
344 };
345
346 long int fill_width = string(HELPFILLSPACE).size() + 1;
347 cout.fill('.');
348 cout << KGRN << KBOLD << " Options and switches matching \"" << tag << "\":" << RST << endl << endl;
349
350 bool found = false;
351 for (auto& s : switches) {
352 if (matches(s.first, s.second.getDescription())) {
353 found = true;
354 cout << KGRN << " " << left;
355 cout.width(fill_width);
356 cout << "-" + s.first + RST + " " << ": " << s.second.getDescription() << endl;
357 }
358 }
359 for (auto& option : goptions) {
360 if (option.name != GVERSION_STRING && matches(option.name, option.description)) {
361 found = true;
362 option.printHelp(false);
363 }
364 }
365 if (!found) { cout << TPOINTITEM << "no match found." << endl; }
366 cout << endl << " Use " << KGRN << "help <value>" << RST << " for the detailed help of a single option." << endl
367 << endl;
368}
369
370// Private method: see header. Kept undocumented here to avoid duplicate param docs.
371vector<string> GOptions::findYamls(int argc, char* argv[]) {
372 vector<string> yaml_files;
373 auto ends_with = [](const string& s, const string& suffix) {
374 return s.size() >= suffix.size() &&
375 s.compare(s.size() - suffix.size(), suffix.size(), suffix) == 0;
376 };
377 for (int i = 1; i < argc; i++) {
378 string arg = argv[i];
379 // Match a trailing .yaml/.yml extension, not any substring, so option values such as
380 // -prefix=run.yaml.bak are not mistaken for input files (and silently dropped).
381 if (ends_with(arg, ".yaml") || ends_with(arg, ".yml")) yaml_files.push_back(arg);
382 }
383 return yaml_files;
384}
385
386// checks if the option exists
387// Public API docs are in goptions.h; doxygen param docs are kept only there.
388bool GOptions::doesOptionExist(const std::string& tag) const {
389 // [&tag] ensures we're referencing the original tag passed to the function
390 return std::any_of(goptions.begin(), goptions.end(),
391 [&tag](const auto& option) {
392 return option.name == tag;
393 });
394}
395
396// Private method: behavior described in header and top-of-file overview.
397void GOptions::setOptionsValuesFromYamlFile(const std::string& yaml) {
398 YAML::Node config;
399 try {
400 config = YAML::LoadFile(yaml);
401 }
402 catch (YAML::BadFile& e) {
403 cerr << FATALERRORL << "Cannot open yaml file " << YELLOWHHL << yaml << RSTHHR
404 << ". Check the path and spelling." << endl;
406 }
407 catch (YAML::ParserException& e) {
408 cerr << FATALERRORL << "Error parsing " << YELLOWHHL << yaml << RSTHHR
409 << " yaml file." << endl;
410 cerr << e.what() << endl;
411 cerr << "Try validating the yaml file with an online yaml validator, e.g., https://www.yamllint.com" << endl;
413 }
414
415 for (auto it = config.begin(); it != config.end(); ++it) {
416 auto option_name = it->first.as<std::string>();
417 auto option_it = getOptionIterator(option_name);
418
419 // If it is not an option, it may still be a switch.
420 if (option_it == goptions.end()) {
421 if (switches.find(option_name) == switches.end()) {
422 cerr << FATALERRORL << "The option or switch " << YELLOWHHL << option_name << RSTHHR
423 << " is not known to this system." << endl;
424 exit(EC__NOOPTIONFOUND);
425 }
426 else {
427 // A bare key (null value) means "present" -> on; an explicit boolean value is honored,
428 // so a default-on switch can be turned off from YAML (e.g. print_summary: false).
429 bool on = true;
430 if (it->second.IsScalar() && !parse_bool_token(it->second.as<std::string>(), on)) {
431 cerr << FATALERRORL << "The switch " << YELLOWHHL << option_name << RSTHHR
432 << " accepts only true/false/yes/no/on/off/1/0." << endl;
433 exit(EC__NOOPTIONFOUND);
434 }
435 on ? switches[option_name].turnOn() : switches[option_name].turnOff();
436 }
437 }
438 else {
439 YAML::NodeType::value type = it->second.Type();
440 switch (type) {
441 case YAML::NodeType::Scalar:
442 option_it->set_scalar_value(it->second.as<std::string>());
443 break;
444 case YAML::NodeType::Sequence:
445 option_it->set_value(it->second);
446 break;
447 case YAML::NodeType::Map:
448 option_it->set_value(it->second);
449 break;
450 default:
451 break;
452 }
453 }
454 }
455}
456
457// Private method: behavior described in header and top-of-file overview.
458void GOptions::setOptionValuesFromCommandLineArgument(const std::string& optionName,
459 const std::string& possibleYamlNode) {
460 YAML::Node node = YAML::Load(possibleYamlNode);
461 auto option_it = getOptionIterator(optionName);
462
463 if (node.Type() == YAML::NodeType::Scalar) {
464 option_it->set_scalar_value(possibleYamlNode);
465 }
466 else {
467 option_it->set_value(node);
468 }
469}
470
471void GOptions::setOptionValueFromString(const std::string& optionName,
472 const std::string& possibleYamlNode) {
473 setOptionValuesFromCommandLineArgument(optionName, possibleYamlNode);
474}
475
476// Private method (private API): do not \ref; use \c getOptionIterator() in docs if needed.
477std::vector<GOption>::iterator GOptions::getOptionIterator(const std::string& name) {
478 return std::find_if(goptions.begin(), goptions.end(),
479 [&name](GOption& option) { return option.name == name; });
480}
481
482// Private method (private API): do not \ref; use \c getOptionIterator() in docs if needed.
483std::vector<GOption>::const_iterator GOptions::getOptionIterator(const std::string& name) const {
484 return std::find_if(goptions.begin(), goptions.end(),
485 [&name](const GOption& option) { return option.name == name; });
486}
487
488// Implementation note: public API docs are in goptions.h (avoid duplicate \param blocks).
489bool GOptions::getSwitch(const std::string& tag) const {
490 auto it = switches.find(tag);
491 if (it != switches.end()) {
492 return it->second.getStatus();
493 }
494 else {
495 std::cerr << FATALERRORL << "The switch " << YELLOWHHL << tag << RSTHHR
496 << " was not found." << std::endl;
497 exit(EC__NOOPTIONFOUND);
498 }
499}
500
501// Implementation note: public API docs are in goptions.h (avoid duplicate \param blocks).
502YAML::Node GOptions::getOptionMapInNode(const string& option_name, const string& map_key) const {
503 auto sequence_node = getOptionNode(option_name);
504
505 for (auto seq_item : sequence_node) {
506 for (auto map_item = seq_item.begin(); map_item != seq_item.end(); ++map_item) {
507 if (map_item->first.as<string>() == map_key) {
508 return map_item->second;
509 }
510 }
511 }
512
513 cerr << FATALERRORL << "The key " << YELLOWHHL << map_key << RSTHHR
514 << " was not found in " << YELLOWHHL << option_name << RSTHHR << endl;
515 exit(EC__NOOPTIONFOUND);
516}
517
518// Template documentation lives in the header to avoid duplicate \param blocks.
519template <typename T>
520T GOptions::get_variable_in_option(const YAML::Node& node, const std::string& variable_name, const T& default_value) {
521 if (node[variable_name]) {
522 return node[variable_name].as<T>();
523 }
524 return default_value;
525}
526
527// Explicit template instantiations.
528template int GOptions::get_variable_in_option<int>(const YAML::Node& node, const std::string& variable_name,
529 const int& default_value);
530template double GOptions::get_variable_in_option<double>(const YAML::Node& node, const std::string& variable_name,
531 const double& default_value);
532template string GOptions::get_variable_in_option<string>(const YAML::Node& node, const std::string& variable_name,
533 const string& default_value);
534template bool GOptions::get_variable_in_option<bool>(const YAML::Node& node, const std::string& variable_name,
535 const bool& default_value);
536
537// Implementation note: public API docs are in goptions.h (avoid duplicate \param blocks).
538int GOptions::getVerbosityFor(const std::string& tag) const {
539 YAML::Node verbosity_node = getOptionNode("verbosity");
540 for (auto v : verbosity_node) {
541 if (v.begin()->first.as<string>() == tag) {
542 return v.begin()->second.as<int>();
543 }
544 }
545
546 // not found. error
547 std::cerr << KRED << " Invalid verbosity or debug requested: " << tag << RST << std::endl;
549}
550
551// Implementation note: public API docs are in goptions.h (avoid duplicate \param blocks).
552int GOptions::getDebugFor(const std::string& tag) const {
553 YAML::Node debug_node = getOptionNode("debug");
554 for (auto d : debug_node) {
555 if (d.begin()->first.as<string>() == tag) {
556 YAML::Node valNode = d.begin()->second;
557 if (valNode.IsScalar()) {
558 auto s = valNode.as<string>();
559 if (s == "true") return 1;
560 if (s == "false") return 0;
561 }
562 try {
563 return valNode.as<int>();
564 }
565 catch (const YAML::BadConversion&) {
566 std::cerr << "Invalid debug value for " << tag << std::endl;
568 }
569 }
570 }
571 // not found. error
572 std::cerr << KRED << " Invalid verbosity or debug requested: " << tag << RST << std::endl;
574}
575
576// Private method: no Doxygen block here to avoid duplicate \param docs.
577void GOptions::printHelp() const {
578 long int fill_width = string(HELPFILLSPACE).size() + 1;
579 cout.fill('.');
580 cout << KGRN << KBOLD << " " << executableName << RST << " [options] [yaml files]" << endl << endl;
581 cout << " Switches: " << endl << endl;
582 for (auto& s : switches) {
583 string help = "-" + s.first + RST + " ";
584 cout << KGRN << " " << left;
585 cout.width(fill_width);
586 cout << help;
587 cout << ": " << s.second.getDescription() << endl;
588 }
589 cout << endl;
590 cout << " Options: " << endl << endl;
591 for (auto& option : goptions) {
592 option.printHelp(false);
593 }
594 cout << endl;
595 cout << endl << " Help / Search / Introspection: " << endl << endl;
596 vector<string> helps = {
597 string("-h, --h, -help, --help") + RST,
598 string("print this help and exit"),
599 string("-hweb") + RST,
600 string("print this help in web format and exit"),
601 string("-v, --v, -version, --version") + RST,
602 string("print the version and exit\n"),
603 string("help <value>") + RST,
604 string("print detailed help for option <value> and exit"),
605 string("search <value>") + RST,
606 string("list all options/switches whose name or description contains <value> and exit\n")
607 };
608 unsigned half_help = helps.size() / 2;
609 for (unsigned i = 0; i < half_help; i++) {
610 cout << KGRN << " " << left;
611 cout.width(fill_width);
612 cout << helps[i * 2] << ": " << helps[i * 2 + 1] << endl;
613 }
614 cout << endl;
615 cout << " Note: command line options overwrite YAML file(s)." << endl << endl;
617}
618
619// Private method: no Doxygen block here to avoid duplicate \param docs.
620void GOptions::printWebHelp() const {
622}
623
624// Private method: no Doxygen block here to avoid duplicate \param docs.
625void GOptions::saveOptions() const {
626 for (auto& s : switches) {
627 string status = s.second.getStatus() ? "true" : "false";
628 *yamlConf << s.first + ": " + status << "," << endl;
629 }
630 for (const auto& option : goptions) {
631 option.saveOption(yamlConf);
632 }
633 yamlConf->close();
634}
635
636// Private method: no Doxygen block here to avoid duplicate \param docs.
637void GOptions::print_version() {
638 string asterisks = "*******************************************************************";
639 cout << endl << asterisks << endl;
640 cout << " " << KGRN << KBOLD << executableName << RST << " version: " << KGRN << gversion << RST << endl;
641 cout << " Called from: " << KGRN << executableCallingDir << RST << endl;
642 cout << " Install: " << KGRN << installDir << "/bin" << RST << endl; //
643 cout << " Released on: " << KGRN << grelease_date << RST << endl;
644
645 // Report the plugin search path when one was provided, either through the
646 // -plugin_path option (or a plugin_path: YAML key) or the GEMC_PLUGIN_PATH
647 // environment variable.
648 string plugin_path = doesOptionExist("plugin_path") ? getScalarString("plugin_path") : "";
649 if (plugin_path == "NULL") plugin_path = "";
650 const char* plugin_env = std::getenv("GEMC_PLUGIN_PATH");
651 if (!plugin_path.empty() || (plugin_env != nullptr && plugin_env[0] != '\0')) {
652 string combined = plugin_path;
653 if (plugin_env != nullptr && plugin_env[0] != '\0') {
654 if (!combined.empty()) combined += ':';
656 }
657 cout << " Plugin path: " << KGRN << combined << RST << endl;
658 }
659
660 cout << " GEMC Reference: " << KGRN << greference << RST << endl;
661 cout << " GEMC Homepage: " << KGRN << gweb << RST << endl;
662 cout << " Author: " << KGRN << gauthor << RST << endl << endl;
663 cout << asterisks << endl << endl;
664}
665
666// Operator documentation is provided in goptions.h; do not duplicate it here.
667GOptions& operator+=(GOptions& gopts, const GOptions& goptions_to_add) {
668 gopts.addGOptions(goptions_to_add);
669 return gopts;
670}
Stores one configuration option (scalar or structured), including schema defaults and current value.
Definition goption.h:105
Parses, stores, and exposes command-line options and YAML configuration values.
Definition goptions.h:46
bool getSwitch(const std::string &tag) const
Retrieves the status of a switch.
Definition goptions.cc:489
YAML::Node getOptionNode(const std::string &tag) const
Retrieves the YAML node for the specified option.
Definition goptions.h:208
void setOptionValueFromString(const std::string &optionName, const std::string &possibleYamlNode)
Updates an option value from a YAML-formatted string.
Definition goptions.cc:471
std::string getScalarString(const std::string &tag) const
Retrieves the value of a scalar string option.
Definition goptions.cc:302
GOptions()
Default constructor.
Definition goptions.h:56
void defineOption(const GVariable &gvar, const std::string &help)
Defines and adds a scalar option.
Definition goptions.cc:254
YAML::Node getOptionMapInNode(const std::string &option_name, const std::string &map_key) const
Retrieves a map entry value from a structured option stored as a sequence of maps.
Definition goptions.cc:502
T get_variable_in_option(const YAML::Node &node, const std::string &variable_name, const T &default_value)
Retrieves a typed variable from a YAML node within an option.
Definition goptions.cc:520
double getScalarDouble(const std::string &tag) const
Retrieves the value of a scalar double option.
Definition goptions.cc:291
bool doesOptionExist(const std::string &tag) const
Checks if an option exists.
Definition goptions.cc:388
int getScalarInt(const std::string &tag) const
Retrieves the value of a scalar integer option.
Definition goptions.cc:280
void addGOptions(const GOptions &src)
Merges options and switches from another GOptions : into this one.
Definition goptions.h:300
int getDebugFor(const std::string &tag) const
Retrieves the debug level for the specified tag.
Definition goptions.cc:552
int getVerbosityFor(const std::string &tag) const
Retrieves the verbosity level for the specified tag.
Definition goptions.cc:538
void defineSwitch(const std::string &name, const std::string &description, bool default_status=false)
Defines and adds a command-line switch.
Definition goptions.cc:242
std::vector< GVariable > option_verbosity_names
Schema entries used to define the verbosity and debug structured options.
Definition goptions.h:343
Represents a boolean command-line switch with a description and a status.
Definition gswitch.h:28
Conventions, constants, and error codes for the GOptions : / GOption : subsystem.
#define EC__BAD_CONVERSION
YAML value could not be converted to requested type.
#define EC__NOOPTIONFOUND
Option/switch/key not found, or invalid command-line token.
#define EC__YAML_PARSING_ERROR
YAML file failed to parse (syntax error or parser failure).
#define HELPFILLSPACE
Padding used when printing option/switch help.
#define EC__DEFINED_SWITCHALREADYPRESENT
Attempted to define a switch name more than once.
#define EC__DEFINED_OPTION_ALREADY_PRESENT
Attempted to define an option name more than once.
#define GVERSION_STRING
Reserved option tag used to store version information.
GOptions & operator+=(GOptions &gopts, const GOptions &goptions_to_add)
Overloaded operator to add options and switches from one GOptions : to another.
Definition goptions.cc:667
Public interface for GOptions : the YAML + command-line configuration manager.
#define RSTHHR
#define YELLOWHHL
#define KRED
#define KBOLD
#define KGRN
#define FATALERRORL
#define RST
#define TPOINTITEM
std::filesystem::path gemc_root()
string getDirFromPath(const std::string &path)
string getFileFromPath(const std::string &path)
Describes a schema entry: key name, default value, and user-facing description.
Definition goption.h:34
std::string name
Variable name (option name for scalar options, schema key name for structured options).
Definition goption.h:35