7 #include "gutilities.h"
31 executableName = gutilities::getFileFromPath(argv[0]);
32 executableCallingDir = gutilities::getDirFromPath(argv[0]);
33 installDir = gutilities::gemc_root();
37 addGOptions(user_defined_options);
40 defineSwitch(
"gui",
"use Graphical User Interface");
41 defineSwitch(
"i",
"use interactive batch mode");
43 GVariable(
"conf_yaml",
"saved_configuration",
"the prefix for filename that store the used options"),
44 "The default value appends \"_saved_configuration\" to the executable name.");
47 defineOption(
GVariable(
"tt", 500,
"tests timeout (ms)"),
48 "Timeout in milliseconds for the code tests that have GUI. ");
54 {
"release", gversion,
"release version number"},
55 {
"release_date", grelease_date,
"release date"},
56 {
"Reference", greference,
"article reference"},
57 {
"Homepage", gweb,
"homepage"},
58 {
"Author", gauthor,
"author"}
60 defineOption(
GVERSION_STRING,
"version information",
version,
"Version information. Not settable by user.");
62 string help =
"Levels: \n \n";
63 help +=
" - 0: (default) shush\n";
64 help +=
" - 1: normal information\n";
65 help +=
" - 2: detailed information\n \n";
66 help +=
"Example: -verbosity.general=1 \n \n";
67 help +=
"This option can be repeated.\n \n";
68 defineOption(
"verbosity",
"Sets the log verbosity for various classes", option_verbosity_names, help);
71 help =
"Debug information Types: \n \n";
72 help +=
" - false: (default): do not print debug information\n";
73 help +=
" - true: print debug information\n\n";
74 help +=
"Example: -debug.general=true \n \n";
75 help +=
"This option can be repeated.\n \n";
76 defineOption(
"debug",
"Sets the debug level for various classes", option_verbosity_names, help);
79 for (
int i = 1; i < argc; i++) {
80 if (strcmp(argv[i],
"-h") == 0 || strcmp(argv[i],
"--h") == 0 ||
81 strcmp(argv[i],
"-help") == 0 || strcmp(argv[i],
"--help") == 0) {
83 }
else if (strcmp(argv[i],
"-hweb") == 0) {
85 }
else if (strcmp(argv[i],
"-v") == 0 || strcmp(argv[i],
"--v") == 0 ||
86 strcmp(argv[i],
"-version") == 0 || strcmp(argv[i],
"--version") == 0) {
89 }
else if (strcmp(argv[i],
"help") == 0) {
90 printOptionOrSwitchHelp(argv[i + 1]);
96 yaml_files = findYamls(argc, argv);
97 for (
auto &yaml_file: yaml_files) {
98 cout <<
" Parsing " << yaml_file << endl;
99 setOptionsValuesFromYamlFile(yaml_file);
103 for (
int i = 1; i < argc; i++) {
104 string candidate = argv[i];
105 if (candidate.empty())
continue;
106 if (find(yaml_files.begin(), yaml_files.end(), candidate) != yaml_files.end())
continue;
107 if (candidate[0] ==
'-') {
108 string argStr = candidate.substr(1);
109 size_t eqPos = argStr.find(
'=');
110 if (eqPos != string::npos) {
111 string keyPart = argStr.substr(0, eqPos);
112 string valuePart = argStr.substr(eqPos + 1);
113 size_t dotPos = keyPart.find(
'.');
114 if (dotPos != string::npos) {
116 string mainOption = keyPart.substr(0, dotPos);
117 string subOption = keyPart.substr(dotPos + 1);
118 if (doesOptionExist(mainOption)) {
119 auto it = getOptionIterator(mainOption);
120 it->set_sub_option_value(subOption, valuePart);
122 cerr <<
"The option " << mainOption <<
" is not known to this system." << endl;
127 if (doesOptionExist(keyPart)) {
128 setOptionValuesFromCommandLineArgument(keyPart, valuePart);
130 cerr <<
"The option " << keyPart <<
" is not known to this system." << endl;
136 const string& possibleSwitch = argStr;
137 if (switches.find(possibleSwitch) != switches.end()) {
138 switches[possibleSwitch].turnOn();
140 cerr <<
"The switch " << possibleSwitch <<
" is not known to this system." << endl;
145 cerr <<
"The command-line argument \"" << candidate <<
"\" is not valid." << endl;
154 string yamlConf_filename = executableName +
"." + getScalarString(
"conf_yaml") +
".yaml";
155 cout <<
" Saving options to " << yamlConf_filename << endl << endl;
156 yamlConf =
new std::ofstream(yamlConf_filename);
167 if (switches.find(name) == switches.end()) {
168 switches[name] =
GSwitch(description);
170 std::cerr << FATALERRORL <<
"The " << YELLOWHHL << name << RSTHHR
171 <<
" switch is already present." << std::endl;
183 if (doesOptionExist(gvar.
name)) {
184 std::cerr << FATALERRORL <<
"The " << YELLOWHHL << gvar.
name << RSTHHR
185 <<
" option is already present." << std::endl;
200 void GOptions::defineOption(
const std::string &name,
const std::string &description,
const std::vector <GVariable> &g_vars,
201 const std::string &help) {
202 if (doesOptionExist(name)) {
203 std::cerr << FATALERRORL <<
"The " << YELLOWHHL << name << RSTHHR
204 <<
" option is already present." << std::endl;
207 goptions.emplace_back(name, description, g_vars, help);
218 auto it = getOptionIterator(tag);
220 cerr << FATALERRORL <<
"The option " << YELLOWHHL << tag << RSTHHR
221 <<
" was not found." << endl;
224 return it->value.begin()->second.as<
int>();
234 auto it = getOptionIterator(tag);
236 cerr << FATALERRORL <<
"The option " << YELLOWHHL << tag << RSTHHR
237 <<
" was not found." << endl;
240 return it->value.begin()->second.as<
double>();
250 auto it = getOptionIterator(tag);
252 cerr << FATALERRORL <<
"The option " << YELLOWHHL << tag << RSTHHR
253 <<
" was not found." << endl;
256 return it->value.begin()->second.as<
string>();
264 void GOptions::printOptionOrSwitchHelp(
const std::string &tag)
const {
265 auto switchIt = switches.find(tag);
266 if (switchIt != switches.end()) {
267 cout << KGRN <<
"-" << tag << RST <<
": " << switchIt->second.getDescription() << endl << endl;
268 cout << TPOINTITEM <<
"Default value is " << (switchIt->second.getStatus() ?
"on" :
"off") << endl << endl;
271 for (
const auto &goption:
goptions) {
272 if (goption.name == tag) {
273 goption.printHelp(
true);
277 cerr << FATALERRORL <<
"The " << YELLOWHHL << tag << RSTHHR
278 <<
" option is not known to this system." << endl;
289 vector <string> GOptions::findYamls(
int argc,
char *argv[]) {
290 vector <string> yaml_files;
291 for (
int i = 1; i < argc; i++) {
292 string arg = argv[i];
293 size_t pos = arg.find(
".yaml");
294 if (pos != string::npos) yaml_files.push_back(arg);
295 pos = arg.find(
".yml");
296 if (pos != string::npos) yaml_files.push_back(arg);
306 [&tag](
const auto& option) {
307 return option.name == tag;
316 void GOptions::setOptionsValuesFromYamlFile(
const std::string &yaml) {
319 config = YAML::LoadFile(yaml);
320 }
catch (YAML::ParserException &e) {
321 cerr << FATALERRORL <<
"Error parsing " << YELLOWHHL << yaml << RSTHHR
322 <<
" yaml file." << endl;
323 cerr << e.what() << endl;
324 cerr <<
"Try validating the yaml file with an online yaml validator, e.g., https://www.yamllint.com" << endl;
328 for (
auto it = config.begin(); it != config.end(); ++it) {
329 auto option_name = it->first.as<std::string>();
330 auto option_it = getOptionIterator(option_name);
332 if (switches.find(option_name) == switches.end()) {
333 cerr << FATALERRORL <<
"The option or switch " << YELLOWHHL << option_name << RSTHHR
334 <<
" is not known to this system." << endl;
337 switches[option_name].turnOn();
340 YAML::NodeType::value type = it->second.Type();
342 case YAML::NodeType::Scalar:
343 option_it->set_scalar_value(it->second.as<std::string>());
345 case YAML::NodeType::Sequence:
346 option_it->set_value(it->second);
348 case YAML::NodeType::Map:
349 option_it->set_value(it->second);
364 void GOptions::setOptionValuesFromCommandLineArgument(
const std::string &optionName,
const std::string &possibleYamlNode) {
365 YAML::Node node = YAML::Load(possibleYamlNode);
366 auto option_it = getOptionIterator(optionName);
367 if (node.Type() == YAML::NodeType::Scalar) {
368 option_it->set_scalar_value(possibleYamlNode);
370 option_it->set_value(node);
380 std::vector<GOption>::iterator GOptions::getOptionIterator(
const std::string &name) {
382 [&name](
GOption &option) { return option.name == name; });
392 std::vector<GOption>::const_iterator GOptions::getOptionIterator(
const std::string &name)
const {
394 [&name](
const GOption &option) { return option.name == name; });
404 auto it = switches.find(tag);
405 if (it != switches.end()) {
406 return it->second.getStatus();
408 std::cerr << FATALERRORL <<
"The switch " << YELLOWHHL << tag << RSTHHR
409 <<
" was not found." << std::endl;
422 auto sequence_node = getOptionNode(option_name);
423 for (
auto seq_item : sequence_node) {
424 for (
auto map_item = seq_item.begin(); map_item != seq_item.end(); ++map_item) {
425 if (map_item->first.as<
string>() == map_key) {
426 return map_item->second;
430 cerr << FATALERRORL <<
"The key " << YELLOWHHL << map_key << RSTHHR
431 <<
" was not found in " << YELLOWHHL << option_name << RSTHHR << endl;
446 if (node[variable_name]) {
447 return node[variable_name].as<T>();
449 return default_value;
453 template int GOptions::get_variable_in_option<int>(
const YAML::Node &node,
const std::string &variable_name,
const int &default_value);
454 template double GOptions::get_variable_in_option<double>(
const YAML::Node &node,
const std::string &variable_name,
const double &default_value);
455 template string GOptions::get_variable_in_option<string>(
const YAML::Node &node,
const std::string &variable_name,
const string &default_value);
456 template bool GOptions::get_variable_in_option<bool>(
const YAML::Node &node,
const std::string &variable_name,
const bool &default_value);
465 YAML::Node verbosity_node = getOptionNode(
"verbosity");
466 for (
auto v : verbosity_node) {
467 if (v.begin()->first.as<
string>() == tag) {
468 return v.begin()->second.as<
int>();
473 std::cerr << KRED <<
" Invalid verbosity or debug requested: " << tag << RST << std::endl;
486 YAML::Node debug_node = getOptionNode(
"debug");
487 for (
auto d : debug_node) {
488 if (d.begin()->first.as<
string>() == tag) {
489 YAML::Node valNode = d.begin()->second;
490 if (valNode.IsScalar()) {
491 auto s = valNode.as<
string>();
492 if (s ==
"true")
return 1;
493 if (s ==
"false")
return 0;
496 return valNode.as<
int>();
497 }
catch (
const YAML::BadConversion &) {
498 std::cerr <<
"Invalid debug value for " << tag << std::endl;
504 std::cerr << KRED <<
" Invalid verbosity or debug requested: " << tag << RST << std::endl;
510 void GOptions::printHelp()
const {
513 cout << KGRN << KBOLD <<
" " << executableName << RST <<
" [options] [yaml files]" << endl << endl;
514 cout <<
" Switches: " << endl << endl;
515 for (
auto &s : switches) {
516 string help =
"-" + s.first + RST +
" ";
517 cout << KGRN <<
" " << left;
518 cout.width(fill_width);
520 cout <<
": " << s.second.getDescription() << endl;
523 cout <<
" Options: " << endl << endl;
525 option.printHelp(
false);
528 cout << endl <<
" Help / Search / Introspection: " << endl << endl;
529 vector<string> helps = {
530 string(
"-h, --h, -help, --help") + RST,
531 string(
"print this help and exit"),
532 string(
"-hweb") + RST,
533 string(
"print this help in web format and exit"),
534 string(
"-v, --v, -version, --version") + RST,
535 string(
"print the version and exit\n"),
536 string(
"help <value>") + RST,
537 string(
"print detailed help for option <value> and exit"),
538 string(
"search <value>") + RST,
539 string(
"list all options containing <value> in the description and exit\n")
541 unsigned half_help = helps.size() / 2;
542 for (
unsigned i = 0; i < half_help; i++) {
543 cout << KGRN <<
" " << left;
544 cout.width(fill_width);
545 cout << helps[i * 2] <<
": " << helps[i * 2 + 1] << endl;
548 cout <<
" Note: command line options overwrite YAML file(s)." << endl << endl;
557 void GOptions::printWebHelp()
const {
564 void GOptions::saveOptions()
const {
565 for (
auto &s : switches) {
566 string status = s.second.getStatus() ?
"true" :
"false";
567 *yamlConf << s.first +
": " + status <<
"," << endl;
569 for (
const auto &option :
goptions) {
570 option.saveOption(yamlConf);
578 void GOptions::print_version() {
579 string asterisks =
"*******************************************************************";
580 cout << endl << asterisks << endl;
581 cout <<
" " << KGRN << KBOLD << executableName << RST <<
" version: " << KGRN << gversion << RST << endl;
582 cout <<
" Called from: " << KGRN << executableCallingDir << RST << endl;
583 cout <<
" Executed from: " << KGRN << installDir <<
"/bin" << RST << endl;
584 cout <<
" Released on: " << KGRN << grelease_date << RST << endl;
585 cout <<
" GEMC Reference: " << KGRN << greference << RST << endl;
586 cout <<
" GEMC Homepage: " << KGRN << gweb << RST << endl;
587 cout <<
" Author: " << KGRN << gauthor << RST << endl << endl;
588 cout << asterisks << endl << endl;
Represents a configurable option with a name, value(s), description, and help text.
The GOptions class manages command-line options and switches.
std::string getScalarString(const std::string &tag) const
Retrieves the value of a scalar string option.
bool getSwitch(const std::string &tag) const
Retrieves the status of a switch.
void defineSwitch(const std::string &name, const std::string &description)
Defines and adds a command–line switch.
GOptions()
Default constructor.
void defineOption(const GVariable &gvar, const std::string &help)
Defines and adds a scalar option.
YAML::Node getOptionMapInNode(const std::string &option_name, const std::string &map_key) const
Retrieves a map option’s value from within a YAML node.
T get_variable_in_option(const YAML::Node &node, const std::string &variable_name, const T &default_value)
Retrieves a variable from a YAML node within an option.
double getScalarDouble(const std::string &tag) const
Retrieves the value of a scalar double option.
void addGOptions(const GOptions &goptions_to_add)
Adds options from another GOptions object.
bool doesOptionExist(const std::string &tag) const
Checks if the specified option exists.
int getScalarInt(const std::string &tag) const
Retrieves the value of a scalar integer option.
int getDebugFor(const std::string &tag) const
Retrieves the debug level for the specified tag.
int getVerbosityFor(const std::string &tag) const
Retrieves the verbosity level for the specified tag.
Represents a switch with a description and a status.
#define EC__BAD_CONVERSION
#define EC__NOOPTIONFOUND
#define EC__YAML_PARSING_ERROR
#define EC__DEFINED_SWITCHALREADYPRESENT
#define EC__DEFINED_OPTION_ALREADY_PRESENT
GOptions & operator+=(GOptions &gopts, const GOptions &goptions_to_add)
Overloaded operator to add options and switches from one GOptions object to another.
Encapsulates a variable with a name, value, and description.
std::string name
The name of the variable.