You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

213 lines
6.2 KiB

  1. /*
  2. * settings.cpp
  3. *
  4. * Created on: 22.11.2012
  5. * Author: Gereon Kremer
  6. */
  7. #include "src/utility/settings.h"
  8. #include "src/exceptions/BaseException.h"
  9. #include "log4cplus/logger.h"
  10. #include "log4cplus/loggingmacros.h"
  11. extern log4cplus::Logger logger;
  12. #include <boost/algorithm/string/join.hpp>
  13. namespace mrmc {
  14. namespace settings {
  15. namespace bpo = boost::program_options;
  16. /*
  17. * static initializers
  18. */
  19. std::unique_ptr<bpo::options_description> mrmc::settings::Settings::desc = nullptr;
  20. std::string mrmc::settings::Settings::binaryName = "";
  21. mrmc::settings::Settings* mrmc::settings::Settings::inst = nullptr;
  22. std::map< std::pair<std::string, std::string>, bpo::options_description* > mrmc::settings::Settings::modules;
  23. /*!
  24. * The constructor fills the option descriptions, parses the
  25. * command line and the config file and puts the option values to
  26. * our option mapping.
  27. *
  28. * If a configfile is set in the commandline, we load this one.
  29. * Otherwise, if filename is not NULL, we load this one. Otherwise,
  30. * we load no config file.
  31. *
  32. * @param argc should be argc passed to main function
  33. * @param argv should be argv passed to main function
  34. * @param filename either nullptr or name of config file
  35. */
  36. Settings::Settings(const int argc, const char* argv[], const char* filename)
  37. {
  38. Settings::binaryName = std::string(argv[0]);
  39. try
  40. {
  41. // Initially fill description objects
  42. this->initDescriptions();
  43. // Take care of positional arguments
  44. Settings::positional.add("trafile", 1);
  45. Settings::positional.add("labfile", 1);
  46. // Check module triggers, add corresponding options
  47. std::map< std::string, std::list< std::string > > options;
  48. for (auto it : Settings::modules)
  49. {
  50. options[it.first.first].push_back(it.first.second);
  51. }
  52. for (auto it : options)
  53. {
  54. std::stringstream str;
  55. str << "select " << it.first << " module (" << boost::algorithm::join(it.second, ", ") << ")";
  56. Settings::desc->add_options()
  57. (it.first.c_str(), bpo::value<std::string>(), str.str().c_str())
  58. ;
  59. }
  60. // Perform first parse run
  61. this->firstRun(argc, argv, filename);
  62. // Check module triggers
  63. for (auto it : Settings::modules)
  64. {
  65. std::pair< std::string, std::string > trigger = it.first;
  66. if (this->vm.count(trigger.first))
  67. {
  68. if (this->vm[trigger.first].as<std::string>().compare(trigger.second) == 0)
  69. {
  70. Settings::desc->add(*it.second);
  71. Settings::modules.erase(trigger);
  72. }
  73. }
  74. }
  75. // Stop if help is set
  76. if (this->vm.count("help") > 0)
  77. {
  78. return;
  79. }
  80. // Perform second run
  81. this->secondRun(argc, argv, filename);
  82. // Finalize parsed options, check for specified requirements
  83. bpo::notify(this->vm);
  84. LOG4CPLUS_DEBUG(logger, "Finished loading config.");
  85. }
  86. catch (bpo::reading_file e)
  87. {
  88. std::cerr << "Could not read config file " << filename << std::endl;
  89. LOG4CPLUS_ERROR(logger, "Could not read config file");
  90. }
  91. catch (bpo::required_option e)
  92. {
  93. throw mrmc::exceptions::InvalidSettings() << "Required option missing";
  94. }
  95. catch (bpo::validation_error e)
  96. {
  97. throw mrmc::exceptions::InvalidSettings() << "Validation failed: " << e.what();
  98. }
  99. catch (bpo::invalid_command_line_syntax e)
  100. {
  101. throw mrmc::exceptions::InvalidSettings() << e.what();
  102. }
  103. catch (bpo::error e)
  104. {
  105. throw mrmc::exceptions::InvalidSettings() << e.what();
  106. }
  107. }
  108. /*!
  109. * Initially fill options_description objects.
  110. * First puts some generic options, then calls all register Callbacks.
  111. */
  112. void Settings::initDescriptions()
  113. {
  114. LOG4CPLUS_DEBUG(logger, "Initializing descriptions.");
  115. Settings::desc = std::unique_ptr<bpo::options_description>(new bpo::options_description("Generic Options"));
  116. Settings::desc->add_options()
  117. ("help,h", "produce help message")
  118. ("verbose,v", "be verbose")
  119. ("configfile,c", bpo::value<std::string>(), "name of config file")
  120. ("test-prctl", bpo::value<std::string>(), "name of prctl file")
  121. ("trafile", bpo::value<std::string>()->required(), "name of the .tra file")
  122. ("labfile", bpo::value<std::string>()->required(), "name of the .lab file")
  123. ;
  124. }
  125. /*!
  126. * Perform a sloppy parsing run: parse command line and config file (if
  127. * given), but allow for unregistered options, do not check requirements
  128. * from options_description objects, do not check positional arguments.
  129. */
  130. void Settings::firstRun(const int argc, const char* argv[], const char* filename)
  131. {
  132. LOG4CPLUS_DEBUG(logger, "Performing first run.");
  133. // parse command line
  134. bpo::store(bpo::command_line_parser(argc, argv).options(*(Settings::desc)).allow_unregistered().run(), this->vm);
  135. /*
  136. * load config file if specified
  137. */
  138. if (this->vm.count("configfile"))
  139. {
  140. bpo::store(bpo::parse_config_file<char>(this->vm["configfile"].as<std::string>().c_str(), *(Settings::desc)), this->vm, true);
  141. }
  142. else if (filename != NULL)
  143. {
  144. bpo::store(bpo::parse_config_file<char>(filename, *(Settings::desc)), this->vm, true);
  145. }
  146. }
  147. /*!
  148. * Perform the second parser run: parse command line and config file (if
  149. * given) and check for unregistered options, requirements from
  150. * options_description objects and positional arguments.
  151. */
  152. void Settings::secondRun(const int argc, const char* argv[], const char* filename)
  153. {
  154. LOG4CPLUS_DEBUG(logger, "Performing second run.");
  155. // Parse command line
  156. bpo::store(bpo::command_line_parser(argc, argv).options(*(Settings::desc)).positional(this->positional).run(), this->vm);
  157. /*
  158. * load config file if specified
  159. */
  160. if (this->vm.count("configfile"))
  161. {
  162. bpo::store(bpo::parse_config_file<char>(this->vm["configfile"].as<std::string>().c_str(), *(Settings::desc)), this->vm, true);
  163. }
  164. else if (filename != NULL)
  165. {
  166. bpo::store(bpo::parse_config_file<char>(filename, *(Settings::desc)), this->vm, true);
  167. }
  168. }
  169. /*!
  170. * Print a short general usage information consisting of the positional
  171. * options and the list of available command line options.
  172. *
  173. * Use it like this:
  174. * @code std::cout << mrmc::settings::help; @endcode
  175. */
  176. std::ostream& help(std::ostream& os)
  177. {
  178. os << "Usage: " << mrmc::settings::Settings::binaryName << " [options] <transition file> <label file>" << std::endl;
  179. os << *(mrmc::settings::Settings::desc) << std::endl;
  180. for (auto it : Settings::modules)
  181. {
  182. os << *(it.second) << std::endl;
  183. }
  184. return os;
  185. }
  186. } // namespace settings
  187. } // namespace mrmc