678 lines
40 KiB

  1. #include "storm/settings/SettingsManager.h"
  2. #include <cstring>
  3. #include <mutex>
  4. #include <iomanip>
  5. #include <fstream>
  6. #include <regex>
  7. #include <set>
  8. #include <boost/algorithm/string.hpp>
  9. #include <boost/io/ios_state.hpp>
  10. #include "storm/exceptions/IllegalFunctionCallException.h"
  11. #include "storm/exceptions/OptionParserException.h"
  12. #include "storm/utility/storm-version.h"
  13. #include "storm/settings/modules/GeneralSettings.h"
  14. #include "storm/settings/modules/CoreSettings.h"
  15. #include "storm/settings/modules/IOSettings.h"
  16. #include "storm/settings/modules/ModelCheckerSettings.h"
  17. #include "storm/settings/modules/DebugSettings.h"
  18. #include "storm/settings/modules/CuddSettings.h"
  19. #include "storm/settings/modules/BuildSettings.h"
  20. #include "storm/settings/modules/SylvanSettings.h"
  21. #include "storm/settings/modules/EigenEquationSolverSettings.h"
  22. #include "storm/settings/modules/GmmxxEquationSolverSettings.h"
  23. #include "storm/settings/modules/NativeEquationSolverSettings.h"
  24. #include "storm/settings/modules/EliminationSettings.h"
  25. #include "storm/settings/modules/LongRunAverageSolverSettings.h"
  26. #include "storm/settings/modules/MinMaxEquationSolverSettings.h"
  27. #include "storm/settings/modules/GameSolverSettings.h"
  28. #include "storm/settings/modules/BisimulationSettings.h"
  29. #include "storm/settings/modules/GlpkSettings.h"
  30. #include "storm/settings/modules/GurobiSettings.h"
  31. #include "storm/settings/modules/Smt2SmtSolverSettings.h"
  32. #include "storm/settings/modules/TopologicalEquationSolverSettings.h"
  33. #include "storm/settings/modules/ExplorationSettings.h"
  34. #include "storm/settings/modules/ResourceSettings.h"
  35. #include "storm/settings/modules/AbstractionSettings.h"
  36. #include "storm/settings/modules/JitBuilderSettings.h"
  37. #include "storm/settings/modules/MultiObjectiveSettings.h"
  38. #include "storm/settings/modules/MultiplierSettings.h"
  39. #include "storm/settings/modules/TransformationSettings.h"
  40. #include "storm/settings/modules/HintSettings.h"
  41. #include "storm/utility/macros.h"
  42. #include "storm/utility/file.h"
  43. #include "storm/utility/string.h"
  44. #include "storm/settings/Option.h"
  45. namespace storm {
  46. namespace settings {
  47. SettingsManager::SettingsManager() : modules(), longNameToOptions(), shortNameToOptions(), moduleOptions() {
  48. }
  49. SettingsManager::~SettingsManager() {
  50. // Intentionally left empty.
  51. }
  52. SettingsManager& SettingsManager::manager() {
  53. static SettingsManager settingsManager;
  54. return settingsManager;
  55. }
  56. void SettingsManager::setName(std::string const& name, std::string const& executableName) {
  57. this->name = name;
  58. this->executableName = executableName;
  59. }
  60. void SettingsManager::setFromCommandLine(int const argc, char const * const argv[]) {
  61. // We convert the arguments to a vector of strings and strip off the first element since it refers to the
  62. // name of the program.
  63. std::vector<std::string> argumentVector(argc - 1);
  64. for (int i = 1; i < argc; ++i) {
  65. argumentVector[i - 1] = std::string(argv[i]);
  66. }
  67. this->setFromExplodedString(argumentVector);
  68. }
  69. void SettingsManager::setFromString(std::string const& commandLineString) {
  70. if (commandLineString.empty()) {
  71. this->setFromExplodedString({});
  72. } else {
  73. std::vector<std::string> argumentVector;
  74. boost::split(argumentVector, commandLineString, boost::is_any_of("\t "));
  75. this->setFromExplodedString(argumentVector);
  76. }
  77. }
  78. void SettingsManager::handleUnknownOption(std::string const& optionName, bool isShort) const {
  79. std::string optionNameWithDashes = (isShort ? "-" : "--") + optionName;
  80. storm::utility::string::SimilarStrings similarStrings(optionNameWithDashes, 0.6, false);
  81. std::map<std::string, std::vector<std::string>> similarOptionNames;
  82. for (auto const& longOption : longNameToOptions) {
  83. if (similarStrings.add("--" + longOption.first)) {
  84. similarOptionNames["--" + longOption.first].push_back(longOption.first);
  85. }
  86. }
  87. for (auto const& shortOption : shortNameToOptions) {
  88. if (similarStrings.add("-" + shortOption.first)) {
  89. for (auto const& option : shortOption.second) {
  90. similarOptionNames["-" + shortOption.first].push_back(option->getLongName());
  91. }
  92. }
  93. }
  94. std::string errorMessage = "Unknown option '" + optionNameWithDashes + "'.";
  95. if (!similarOptionNames.empty()) {
  96. errorMessage += " " + similarStrings.toDidYouMeanString() + "\n\n";
  97. std::vector<std::string> sortedSimilarOptionNames;
  98. auto similarStringsList = similarStrings.toList();
  99. for (auto const& s : similarStringsList) {
  100. for (auto const& longOptionName : similarOptionNames.at(s)) {
  101. sortedSimilarOptionNames.push_back(longOptionName);
  102. }
  103. }
  104. errorMessage += getHelpForSelection({}, sortedSimilarOptionNames, "", "##### Suggested options:");
  105. }
  106. STORM_LOG_THROW(false, storm::exceptions::OptionParserException, errorMessage);
  107. }
  108. void SettingsManager::setFromExplodedString(std::vector<std::string> const& commandLineArguments) {
  109. // In order to assign the parsed arguments to an option, we need to keep track of the "active" option's name.
  110. bool optionActive = false;
  111. bool activeOptionIsShortName = false;
  112. std::string activeOptionName = "";
  113. std::vector<std::string> argumentCache;
  114. // Walk through all arguments.
  115. for (uint_fast64_t i = 0; i < commandLineArguments.size(); ++i) {
  116. std::string const& currentArgument = commandLineArguments[i];
  117. // Check if the given argument is a new option or belongs to a previously given option.
  118. if (!currentArgument.empty() && currentArgument.at(0) == '-') {
  119. if (optionActive) {
  120. // At this point we know that a new option is about to come. Hence, we need to assign the current
  121. // cache content to the option that was active until now.
  122. setOptionsArguments(activeOptionName, activeOptionIsShortName ? this->shortNameToOptions : this->longNameToOptions, argumentCache);
  123. // After the assignment, the argument cache needs to be cleared.
  124. argumentCache.clear();
  125. } else {
  126. optionActive = true;
  127. }
  128. if (currentArgument.at(1) == '-') {
  129. // In this case, the argument has to be the long name of an option. Try to get all options that
  130. // match the long name.
  131. std::string optionName = currentArgument.substr(2);
  132. auto optionIterator = this->longNameToOptions.find(optionName);
  133. if (optionIterator == this->longNameToOptions.end()) {
  134. handleUnknownOption(optionName, false);
  135. }
  136. activeOptionIsShortName = false;
  137. activeOptionName = optionName;
  138. } else {
  139. // In this case, the argument has to be the short name of an option. Try to get all options that
  140. // match the short name.
  141. std::string optionName = currentArgument.substr(1);
  142. auto optionIterator = this->shortNameToOptions.find(optionName);
  143. if (optionIterator == this->shortNameToOptions.end()) {
  144. handleUnknownOption(optionName, true);
  145. }
  146. activeOptionIsShortName = true;
  147. activeOptionName = optionName;
  148. }
  149. } else if (optionActive) {
  150. // Add the current argument to the list of arguments for the currently active options.
  151. argumentCache.push_back(currentArgument);
  152. } else {
  153. STORM_LOG_THROW(false, storm::exceptions::OptionParserException, "Found stray argument '" << currentArgument << "' that is not preceeded by a matching option.");
  154. }
  155. }
  156. // If an option is still active at this point, we need to set it.
  157. if (optionActive) {
  158. setOptionsArguments(activeOptionName, activeOptionIsShortName ? this->shortNameToOptions : this->longNameToOptions, argumentCache);
  159. }
  160. // Include the options from a possibly specified configuration file, but don't overwrite existing settings.
  161. if (storm::settings::hasModule<storm::settings::modules::GeneralSettings>() && storm::settings::getModule<storm::settings::modules::GeneralSettings>().isConfigSet()) {
  162. this->setFromConfigurationFile(storm::settings::getModule<storm::settings::modules::GeneralSettings>().getConfigFilename());
  163. }
  164. // Finally, check whether all modules are okay with the current settings.
  165. this->finalizeAllModules();
  166. }
  167. void SettingsManager::setFromConfigurationFile(std::string const& configFilename) {
  168. std::map<std::string, std::vector<std::string>> configurationFileSettings = parseConfigFile(configFilename);
  169. for (auto const& optionArgumentsPair : configurationFileSettings) {
  170. auto options = this->longNameToOptions.find(optionArgumentsPair.first);
  171. // We don't need to check whether this option exists or not, because this is already checked when
  172. // parsing the configuration file.
  173. // Now go through all the matching options and set them according to the values.
  174. for (auto option : options->second) {
  175. if (option->getHasOptionBeenSet()) {
  176. // If the option was already set from the command line, we issue a warning and ignore the
  177. // settings from the configuration file.
  178. STORM_LOG_WARN("The option '" << option->getLongName() << "' of module '" << option->getModuleName() << "' has been set in the configuration file '" << configFilename << "', but was overwritten on the command line." << std::endl);
  179. } else {
  180. // If, however, the option has not been set yet, we try to assign values ot its arguments
  181. // based on the argument strings.
  182. setOptionArguments(optionArgumentsPair.first, option, optionArgumentsPair.second);
  183. }
  184. }
  185. }
  186. // Finally, check whether all modules are okay with the current settings.
  187. this->finalizeAllModules();
  188. }
  189. void SettingsManager::printHelp(std::string const& filter) const {
  190. STORM_PRINT("usage: " << executableName << " [options]" << std::endl << std::endl);
  191. if (filter == "frequent" || filter == "all") {
  192. bool includeAdvanced = (filter == "all");
  193. // Find longest option name.
  194. uint_fast64_t maxLength = getPrintLengthOfLongestOption(includeAdvanced);
  195. std::vector<std::string> invisibleModules;
  196. uint64_t numHidden = 0;
  197. for (auto const& moduleName : this->moduleNames) {
  198. // Only print for visible modules.
  199. if (hasModule(moduleName, true)) {
  200. STORM_PRINT(getHelpForModule(moduleName, maxLength, includeAdvanced));
  201. // collect 'hidden' options
  202. if (!includeAdvanced) {
  203. auto moduleIterator = moduleOptions.find(moduleName);
  204. if (moduleIterator != this->moduleOptions.end()) {
  205. bool allAdvanced = true;
  206. for (auto const& option : moduleIterator->second) {
  207. if (!option->getIsAdvanced()) {
  208. allAdvanced = false;
  209. } else {
  210. ++numHidden;
  211. }
  212. }
  213. if (!moduleIterator->second.empty() && allAdvanced) {
  214. invisibleModules.push_back(moduleName);
  215. }
  216. }
  217. }
  218. }
  219. }
  220. if (!includeAdvanced) {
  221. if (numHidden == 1) {
  222. STORM_PRINT(numHidden << " hidden option." << std::endl);
  223. } else {
  224. STORM_PRINT(numHidden << " hidden options." << std::endl);
  225. }
  226. if (!invisibleModules.empty()) {
  227. if (invisibleModules.size() == 1) {
  228. STORM_PRINT(invisibleModules.size() << " hidden module (" << boost::join(invisibleModules, ", ") << ")." << std::endl);
  229. } else {
  230. STORM_PRINT(invisibleModules.size() << " hidden modules (" << boost::join(invisibleModules, ", ") << ")." << std::endl);
  231. }
  232. }
  233. STORM_PRINT(std::endl << "Type '" + executableName + " --help modulename' to display all options of a specific module." << std::endl);
  234. STORM_PRINT("Type '" + executableName + " --help all' to display a complete list of options." << std::endl);
  235. }
  236. } else {
  237. // Create a regular expression from the input hint.
  238. std::regex hintRegex(filter, std::regex_constants::ECMAScript | std::regex_constants::icase);
  239. // Try to match the regular expression against the known modules.
  240. std::vector<std::string> matchingModuleNames;
  241. for (auto const& moduleName : this->moduleNames) {
  242. if (std::regex_search(moduleName, hintRegex)) {
  243. if (hasModule(moduleName, true)) {
  244. matchingModuleNames.push_back(moduleName);
  245. }
  246. }
  247. }
  248. // Try to match the regular expression against the known options.
  249. std::vector<std::string> matchingOptionNames;
  250. for (auto const& optionName : this->longOptionNames) {
  251. if (std::regex_search(optionName, hintRegex)) {
  252. matchingOptionNames.push_back(optionName);
  253. }
  254. }
  255. std::string optionList = getHelpForSelection(matchingModuleNames, matchingOptionNames, "Matching modules for filter '" + filter +"':", "Matching options for filter '" + filter +"':");
  256. if (optionList.empty()) {
  257. STORM_PRINT("Filter '" << filter << "' did not match any modules or options." << std::endl);
  258. } else {
  259. STORM_PRINT(optionList);
  260. }
  261. }
  262. }
  263. std::string SettingsManager::getHelpForSelection(std::vector<std::string> const& selectedModuleNames, std::vector<std::string> const& selectedLongOptionNames, std::string modulesHeader, std::string optionsHeader) const {
  264. std::stringstream stream;
  265. // Remember which options we printed, so we don't display options twice.
  266. std::set<std::shared_ptr<Option>> printedOptions;
  267. // Try to match the regular expression against the known modules.
  268. uint_fast64_t maxLengthModules = 0;
  269. for (auto const& moduleName : selectedModuleNames) {
  270. maxLengthModules = std::max(maxLengthModules, getPrintLengthOfLongestOption(moduleName, true));
  271. // Add all options of this module to the list of printed options so we don't print them twice.
  272. auto optionIterator = this->moduleOptions.find(moduleName);
  273. STORM_LOG_ASSERT(optionIterator != this->moduleOptions.end(), "Unable to find selected module " << moduleName << ".");
  274. printedOptions.insert(optionIterator->second.begin(), optionIterator->second.end());
  275. }
  276. // Try to match the regular expression against the known options.
  277. std::vector<std::shared_ptr<Option>> matchingOptions;
  278. uint_fast64_t maxLengthOptions = 0;
  279. for (auto const& optionName : selectedLongOptionNames) {
  280. auto optionIterator = this->longNameToOptions.find(optionName);
  281. STORM_LOG_ASSERT(optionIterator != this->longNameToOptions.end(), "Unable to find selected option " << optionName << ".");
  282. for (auto const& option : optionIterator->second) {
  283. // Only add the option if we have not already added it to the list of options that is going
  284. // to be printed anyway.
  285. if (printedOptions.find(option) == printedOptions.end()) {
  286. maxLengthOptions = std::max(maxLengthOptions, option->getPrintLength());
  287. matchingOptions.push_back(option);
  288. printedOptions.insert(option);
  289. }
  290. }
  291. }
  292. // Print the matching modules.
  293. uint_fast64_t maxLength = std::max(maxLengthModules, maxLengthOptions);
  294. if (selectedModuleNames.size() > 0) {
  295. if (modulesHeader != "") {
  296. stream << modulesHeader << std::endl;
  297. }
  298. for (auto const& matchingModuleName : selectedModuleNames) {
  299. stream << getHelpForModule(matchingModuleName, maxLength, true);
  300. }
  301. }
  302. // Print the matching options.
  303. if (matchingOptions.size() > 0) {
  304. if (optionsHeader != "") {
  305. stream << optionsHeader << std::endl;
  306. }
  307. for (auto const& option : matchingOptions) {
  308. stream << std::setw(maxLength) << std::left << *option << std::endl;
  309. }
  310. }
  311. return stream.str();
  312. }
  313. std::string SettingsManager::getHelpForModule(std::string const& moduleName, uint_fast64_t maxLength, bool includeAdvanced) const {
  314. auto moduleIterator = moduleOptions.find(moduleName);
  315. if(moduleIterator == this->moduleOptions.end()) {
  316. return "";
  317. }
  318. //STORM_LOG_THROW(moduleIterator != moduleOptions.end(), storm::exceptions::IllegalFunctionCallException, "Cannot print help for unknown module '" << moduleName << "'.");
  319. // Check whether there is at least one (enabled) option in this module
  320. uint64_t numOfOptions = 0;
  321. for (auto const& option : moduleIterator->second) {
  322. if (includeAdvanced || !option->getIsAdvanced()) {
  323. ++numOfOptions;
  324. }
  325. }
  326. std::stringstream stream;
  327. if (numOfOptions > 0) {
  328. std::string displayedModuleName = "'" + moduleName + "'";
  329. if (!includeAdvanced) {
  330. displayedModuleName += " (" + std::to_string(numOfOptions) + "/" + std::to_string(moduleIterator->second.size()) + " shown)";
  331. }
  332. stream << "##### Module " << displayedModuleName << " " << std::string(std::min(maxLength, maxLength - displayedModuleName.length() - 14), '#') << std::endl;
  333. // Save the flags for std::cout so we can manipulate them and be sure they will be restored as soon as this
  334. // stream goes out of scope.
  335. boost::io::ios_flags_saver out(std::cout);
  336. for (auto const& option : moduleIterator->second) {
  337. if (includeAdvanced || !option->getIsAdvanced()) {
  338. stream << std::setw(maxLength) << std::left << *option << std::endl;
  339. }
  340. }
  341. stream << std::endl;
  342. }
  343. return stream.str();
  344. }
  345. uint_fast64_t SettingsManager::getPrintLengthOfLongestOption(bool includeAdvanced) const {
  346. uint_fast64_t length = 0;
  347. for (auto const& moduleName : this->moduleNames) {
  348. length = std::max(getPrintLengthOfLongestOption(moduleName, includeAdvanced), length);
  349. }
  350. return length;
  351. }
  352. uint_fast64_t SettingsManager::getPrintLengthOfLongestOption(std::string const& moduleName, bool includeAdvanced) const {
  353. auto moduleIterator = modules.find(moduleName);
  354. STORM_LOG_THROW(moduleIterator != modules.end(), storm::exceptions::IllegalFunctionCallException, "Unable to retrieve option length of unknown module '" << moduleName << "'.");
  355. return moduleIterator->second->getPrintLengthOfLongestOption(includeAdvanced);
  356. }
  357. void SettingsManager::addModule(std::unique_ptr<modules::ModuleSettings>&& moduleSettings, bool doRegister) {
  358. auto moduleIterator = this->modules.find(moduleSettings->getModuleName());
  359. STORM_LOG_THROW(moduleIterator == this->modules.end(), storm::exceptions::IllegalFunctionCallException, "Unable to register module '" << moduleSettings->getModuleName() << "' because a module with the same name already exists.");
  360. // Take over the module settings object.
  361. std::string const& moduleName = moduleSettings->getModuleName();
  362. this->moduleNames.push_back(moduleName);
  363. this->modules.emplace(moduleSettings->getModuleName(), std::move(moduleSettings));
  364. auto iterator = this->modules.find(moduleName);
  365. std::unique_ptr<modules::ModuleSettings> const& settings = iterator->second;
  366. if (doRegister) {
  367. this->moduleOptions.emplace(moduleName, std::vector<std::shared_ptr<Option>>());
  368. // Now register the options of the module.
  369. for (auto const& option : settings->getOptions()) {
  370. this->addOption(option);
  371. }
  372. }
  373. }
  374. void SettingsManager::addOption(std::shared_ptr<Option> const& option) {
  375. // First, we register to which module the given option belongs.
  376. auto moduleOptionIterator = this->moduleOptions.find(option->getModuleName());
  377. STORM_LOG_THROW(moduleOptionIterator != this->moduleOptions.end(), storm::exceptions::IllegalFunctionCallException, "Cannot add option for unknown module '" << option->getModuleName() << "'.");
  378. moduleOptionIterator->second.emplace_back(option);
  379. // Then, we add the option's name (and possibly short name) to the registered options. If a module prefix is
  380. // not required for this option, we have to add both versions to our mappings, the prefixed one and the
  381. // non-prefixed one.
  382. if (!option->getRequiresModulePrefix()) {
  383. bool isCompatible = this->isCompatible(option, option->getLongName(), this->longNameToOptions);
  384. STORM_LOG_THROW(isCompatible, storm::exceptions::IllegalFunctionCallException, "Unable to add option '" << option->getLongName() << "', because an option with the same name is incompatible with it.");
  385. addOptionToMap(option->getLongName(), option, this->longNameToOptions);
  386. }
  387. // For the prefixed name, we don't need a compatibility check, because a module is not allowed to register the same option twice.
  388. addOptionToMap(option->getModuleName() + ":" + option->getLongName(), option, this->longNameToOptions);
  389. longOptionNames.push_back(option->getModuleName() + ":" + option->getLongName());
  390. if (option->getHasShortName()) {
  391. if (!option->getRequiresModulePrefix()) {
  392. bool isCompatible = this->isCompatible(option, option->getShortName(), this->shortNameToOptions);
  393. STORM_LOG_THROW(isCompatible, storm::exceptions::IllegalFunctionCallException, "Unable to add option '" << option->getLongName() << "', because an option with the same name is incompatible with it.");
  394. addOptionToMap(option->getShortName(), option, this->shortNameToOptions);
  395. }
  396. addOptionToMap(option->getModuleName() + ":" + option->getShortName(), option, this->shortNameToOptions);
  397. }
  398. }
  399. bool SettingsManager::hasModule(std::string const& moduleName, bool checkHidden) const {
  400. if (checkHidden) {
  401. return this->moduleOptions.find(moduleName) != this->moduleOptions.end();
  402. } else {
  403. return this->modules.find(moduleName) != this->modules.end();
  404. }
  405. }
  406. modules::ModuleSettings const& SettingsManager::getModule(std::string const& moduleName) const {
  407. auto moduleIterator = this->modules.find(moduleName);
  408. STORM_LOG_THROW(moduleIterator != this->modules.end(), storm::exceptions::IllegalFunctionCallException, "Cannot retrieve unknown module '" << moduleName << "'.");
  409. return *moduleIterator->second;
  410. }
  411. modules::ModuleSettings& SettingsManager::getModule(std::string const& moduleName) {
  412. auto moduleIterator = this->modules.find(moduleName);
  413. STORM_LOG_THROW(moduleIterator != this->modules.end(), storm::exceptions::IllegalFunctionCallException, "Cannot retrieve unknown module '" << moduleName << "'.");
  414. return *moduleIterator->second;
  415. }
  416. bool SettingsManager::isCompatible(std::shared_ptr<Option> const& option, std::string const& optionName, std::unordered_map<std::string, std::vector<std::shared_ptr<Option>>> const& optionMap) {
  417. auto optionIterator = optionMap.find(optionName);
  418. if (optionIterator != optionMap.end()) {
  419. for (auto const& otherOption : optionIterator->second) {
  420. bool locallyCompatible = option->isCompatibleWith(*otherOption);
  421. if (!locallyCompatible) {
  422. return false;
  423. }
  424. }
  425. }
  426. return true;
  427. }
  428. void SettingsManager::setOptionArguments(std::string const& optionName, std::shared_ptr<Option> option, std::vector<std::string> const& argumentCache) {
  429. STORM_LOG_THROW(argumentCache.size() <= option->getArgumentCount(), storm::exceptions::OptionParserException, "Too many arguments for option '" << optionName << "'.");
  430. STORM_LOG_THROW(!option->getHasOptionBeenSet(), storm::exceptions::OptionParserException, "Option '" << optionName << "' is set multiple times.");
  431. // Now set the provided argument values one by one.
  432. for (uint_fast64_t i = 0; i < argumentCache.size(); ++i) {
  433. ArgumentBase& argument = option->getArgument(i);
  434. bool conversionOk = argument.setFromStringValue(argumentCache[i]);
  435. STORM_LOG_THROW(conversionOk, storm::exceptions::OptionParserException, "Value '" << argumentCache[i] << "' is invalid for argument <" << argument.getName() << "> of option:\n" << *option);
  436. }
  437. // In case there are optional arguments that were not set, we set them to their default value.
  438. for (uint_fast64_t i = argumentCache.size(); i < option->getArgumentCount(); ++i) {
  439. ArgumentBase& argument = option->getArgument(i);
  440. STORM_LOG_THROW(argument.getIsOptional(), storm::exceptions::OptionParserException, "Non-optional argument <" << argument.getName() << "> of option:\n" << *option);
  441. argument.setFromDefaultValue();
  442. }
  443. option->setHasOptionBeenSet();
  444. if (optionName != option->getLongName() && optionName != option->getShortName() && boost::starts_with(optionName, option->getModuleName())) {
  445. option->setHasOptionBeenSetWithModulePrefix();
  446. }
  447. }
  448. void SettingsManager::setOptionsArguments(std::string const& optionName, std::unordered_map<std::string, std::vector<std::shared_ptr<Option>>> const& optionMap, std::vector<std::string> const& argumentCache) {
  449. auto optionIterator = optionMap.find(optionName);
  450. STORM_LOG_THROW(optionIterator != optionMap.end(), storm::exceptions::OptionParserException, "Unknown option '" << optionName << "'.");
  451. // Iterate over all options and set the arguments.
  452. for (auto& option : optionIterator->second) {
  453. setOptionArguments(optionName, option, argumentCache);
  454. }
  455. }
  456. void SettingsManager::addOptionToMap(std::string const& name, std::shared_ptr<Option> const& option, std::unordered_map<std::string, std::vector<std::shared_ptr<Option>>>& optionMap) {
  457. auto optionIterator = optionMap.find(name);
  458. if (optionIterator == optionMap.end()) {
  459. std::vector<std::shared_ptr<Option>> optionVector;
  460. optionVector.push_back(option);
  461. optionMap.emplace(name, optionVector);
  462. } else {
  463. optionIterator->second.push_back(option);
  464. }
  465. }
  466. void SettingsManager::finalizeAllModules() {
  467. for (auto const& nameModulePair : this->modules) {
  468. nameModulePair.second->finalize();
  469. nameModulePair.second->check();
  470. }
  471. }
  472. std::map<std::string, std::vector<std::string>> SettingsManager::parseConfigFile(std::string const& filename) const {
  473. std::map<std::string, std::vector<std::string>> result;
  474. std::ifstream input;
  475. storm::utility::openFile(filename, input);
  476. bool globalScope = true;
  477. std::string activeModule = "";
  478. uint_fast64_t lineNumber = 1;
  479. for (std::string line; getline(input, line); ++lineNumber) {
  480. // If the first character of the line is a "[", we expect the settings of a new module to start and
  481. // the line to be of the shape [<module>].
  482. if (line.at(0) == '[') {
  483. STORM_LOG_THROW(line.at(0) == '[' && line.find("]") == line.length() - 1 && line.find("[", 1) == line.npos, storm::exceptions::OptionParserException, "Illegal module name header in configuration file '" << filename << " in line " << std::to_string(lineNumber) << ". Expected [<module>] where <module> is a placeholder for a known module.");
  484. // Extract the module name and check whether it's a legal one.
  485. std::string moduleName = line.substr(1, line.length() - 2);
  486. STORM_LOG_THROW(moduleName != "" && (moduleName == "global" || (this->modules.find(moduleName) != this->modules.end())), storm::exceptions::OptionParserException, "Module header in configuration file '" << filename << " in line " << std::to_string(lineNumber) << " refers to unknown module '" << moduleName << ".");
  487. // If the module name is "global", we unset the currently active module and treat all options to follow as unprefixed.
  488. if (moduleName == "global") {
  489. globalScope = true;
  490. } else {
  491. activeModule = moduleName;
  492. globalScope = false;
  493. }
  494. } else {
  495. // In this case, we expect the line to be of the shape o or o=a b c, where o is an option and a, b
  496. // and c are the values that are supposed to be assigned to the arguments of the option.
  497. std::size_t assignmentSignIndex = line.find("=");
  498. bool containsAssignment = false;
  499. if (assignmentSignIndex != line.npos) {
  500. containsAssignment = true;
  501. }
  502. std::string optionName;
  503. if (containsAssignment) {
  504. optionName = line.substr(0, assignmentSignIndex);
  505. } else {
  506. optionName = line;
  507. }
  508. if (globalScope) {
  509. STORM_LOG_THROW(this->longNameToOptions.find(optionName) != this->longNameToOptions.end(), storm::exceptions::OptionParserException, "Option assignment in configuration file '" << filename << " in line " << lineNumber << " refers to unknown option '" << optionName << "'.");
  510. } else {
  511. STORM_LOG_THROW(this->longNameToOptions.find(activeModule + ":" + optionName) != this->longNameToOptions.end(), storm::exceptions::OptionParserException, "Option assignment in configuration file '" << filename << " in line " << lineNumber << " refers to unknown option '" << activeModule << ":" << optionName << "'.");
  512. }
  513. std::string fullOptionName = (!globalScope ? activeModule + ":" : "") + optionName;
  514. STORM_LOG_WARN_COND(result.find(fullOptionName) == result.end(), "Option '" << fullOptionName << "' is set in line " << lineNumber << " of configuration file " << filename << ", but has been set before.");
  515. // If the current line is an assignment, split the right-hand side of the assignment into parts
  516. // enclosed by quotation marks.
  517. if (containsAssignment) {
  518. std::string assignedValues = line.substr(assignmentSignIndex + 1);
  519. std::vector<std::string> argumentCache;
  520. // As horrible as it may look, this regular expression matches either a quoted string (possibly
  521. // containing escaped quotes) or a simple word (without whitespaces and quotes).
  522. std::regex argumentRegex("\"(([^\\\\\"]|((\\\\\\\\)*\\\\\")|\\\\[^\"])*)\"|(([^ \\\\\"]|((\\\\\\\\)*\\\\\")|\\\\[^\"])+)");
  523. boost::algorithm::trim_left(assignedValues);
  524. while (!assignedValues.empty()) {
  525. std::smatch match;
  526. bool hasMatch = std::regex_search(assignedValues, match, argumentRegex);
  527. // If the input could not be matched, we have a parsing error.
  528. STORM_LOG_THROW(hasMatch, storm::exceptions::OptionParserException, "Parsing error in configuration file '" << filename << "' in line " << lineNumber << ". Unexpected input '" << assignedValues << "'.");
  529. // Extract the matched argument and cut off the quotation marks if necessary.
  530. std::string matchedArgument = std::string(match[0].first, match[0].second);
  531. if (matchedArgument.at(0) == '"') {
  532. matchedArgument = matchedArgument.substr(1, matchedArgument.length() - 2);
  533. }
  534. argumentCache.push_back(matchedArgument);
  535. assignedValues = assignedValues.substr(match.length());
  536. boost::algorithm::trim_left(assignedValues);
  537. }
  538. // After successfully parsing the argument values, we store them in the result map.
  539. result.emplace(fullOptionName, argumentCache);
  540. } else {
  541. // In this case, we can just insert the option to indicate it should be set (without arguments).
  542. result.emplace(fullOptionName, std::vector<std::string>());
  543. }
  544. }
  545. }
  546. storm::utility::closeFile(input);
  547. return result;
  548. }
  549. SettingsManager const& manager() {
  550. return SettingsManager::manager();
  551. }
  552. SettingsManager& mutableManager() {
  553. return SettingsManager::manager();
  554. }
  555. storm::settings::modules::BuildSettings& mutableBuildSettings() {
  556. return dynamic_cast<storm::settings::modules::BuildSettings&>(mutableManager().getModule(storm::settings::modules::BuildSettings::moduleName));
  557. }
  558. storm::settings::modules::AbstractionSettings& mutableAbstractionSettings() {
  559. return dynamic_cast<storm::settings::modules::AbstractionSettings&>(mutableManager().getModule(storm::settings::modules::AbstractionSettings::moduleName));
  560. }
  561. void initializeAll(std::string const& name, std::string const& executableName) {
  562. storm::settings::mutableManager().setName(name, executableName);
  563. // Register all known settings modules.
  564. storm::settings::addModule<storm::settings::modules::GeneralSettings>();
  565. storm::settings::addModule<storm::settings::modules::IOSettings>();
  566. storm::settings::addModule<storm::settings::modules::BuildSettings>();
  567. storm::settings::addModule<storm::settings::modules::CoreSettings>();
  568. storm::settings::addModule<storm::settings::modules::ModelCheckerSettings>();
  569. storm::settings::addModule<storm::settings::modules::DebugSettings>();
  570. storm::settings::addModule<storm::settings::modules::CuddSettings>();
  571. storm::settings::addModule<storm::settings::modules::SylvanSettings>();
  572. storm::settings::addModule<storm::settings::modules::GmmxxEquationSolverSettings>();
  573. storm::settings::addModule<storm::settings::modules::EigenEquationSolverSettings>();
  574. storm::settings::addModule<storm::settings::modules::NativeEquationSolverSettings>();
  575. storm::settings::addModule<storm::settings::modules::EliminationSettings>();
  576. storm::settings::addModule<storm::settings::modules::LongRunAverageSolverSettings>();
  577. storm::settings::addModule<storm::settings::modules::MinMaxEquationSolverSettings>();
  578. storm::settings::addModule<storm::settings::modules::GameSolverSettings>();
  579. storm::settings::addModule<storm::settings::modules::BisimulationSettings>();
  580. storm::settings::addModule<storm::settings::modules::GlpkSettings>();
  581. storm::settings::addModule<storm::settings::modules::GurobiSettings>();
  582. storm::settings::addModule<storm::settings::modules::TopologicalEquationSolverSettings>();
  583. storm::settings::addModule<storm::settings::modules::Smt2SmtSolverSettings>();
  584. storm::settings::addModule<storm::settings::modules::ExplorationSettings>();
  585. storm::settings::addModule<storm::settings::modules::ResourceSettings>();
  586. storm::settings::addModule<storm::settings::modules::AbstractionSettings>();
  587. storm::settings::addModule<storm::settings::modules::JitBuilderSettings>();
  588. storm::settings::addModule<storm::settings::modules::MultiObjectiveSettings>();
  589. storm::settings::addModule<storm::settings::modules::MultiplierSettings>();
  590. storm::settings::addModule<storm::settings::modules::TransformationSettings>();
  591. storm::settings::addModule<storm::settings::modules::HintSettings>();
  592. }
  593. }
  594. }