#include "storm/settings/SettingsManager.h" #include #include #include #include #include #include #include #include #include "storm/exceptions/IllegalFunctionCallException.h" #include "storm/exceptions/OptionParserException.h" #include "storm/utility/storm-version.h" #include "storm/settings/modules/GeneralSettings.h" #include "storm/settings/modules/CoreSettings.h" #include "storm/settings/modules/IOSettings.h" #include "storm/settings/modules/ModelCheckerSettings.h" #include "storm/settings/modules/DebugSettings.h" #include "storm/settings/modules/CuddSettings.h" #include "storm/settings/modules/BuildSettings.h" #include "storm/settings/modules/SylvanSettings.h" #include "storm/settings/modules/EigenEquationSolverSettings.h" #include "storm/settings/modules/GmmxxEquationSolverSettings.h" #include "storm/settings/modules/NativeEquationSolverSettings.h" #include "storm/settings/modules/EliminationSettings.h" #include "storm/settings/modules/LongRunAverageSolverSettings.h" #include "storm/settings/modules/MinMaxEquationSolverSettings.h" #include "storm/settings/modules/GameSolverSettings.h" #include "storm/settings/modules/BisimulationSettings.h" #include "storm/settings/modules/GlpkSettings.h" #include "storm/settings/modules/GurobiSettings.h" #include "storm/settings/modules/Smt2SmtSolverSettings.h" #include "storm/settings/modules/TopologicalEquationSolverSettings.h" #include "storm/settings/modules/ExplorationSettings.h" #include "storm/settings/modules/ResourceSettings.h" #include "storm/settings/modules/AbstractionSettings.h" #include "storm/settings/modules/JitBuilderSettings.h" #include "storm/settings/modules/MultiObjectiveSettings.h" #include "storm/settings/modules/MultiplierSettings.h" #include "storm/settings/modules/TransformationSettings.h" #include "storm/settings/modules/HintSettings.h" #include "storm/utility/macros.h" #include "storm/utility/file.h" #include "storm/utility/string.h" #include "storm/settings/Option.h" namespace storm { namespace settings { SettingsManager::SettingsManager() : modules(), longNameToOptions(), shortNameToOptions(), moduleOptions() { } SettingsManager::~SettingsManager() { // Intentionally left empty. } SettingsManager& SettingsManager::manager() { static SettingsManager settingsManager; return settingsManager; } void SettingsManager::setName(std::string const& name, std::string const& executableName) { this->name = name; this->executableName = executableName; } void SettingsManager::setFromCommandLine(int const argc, char const * const argv[]) { // We convert the arguments to a vector of strings and strip off the first element since it refers to the // name of the program. std::vector argumentVector(argc - 1); for (int i = 1; i < argc; ++i) { argumentVector[i - 1] = std::string(argv[i]); } this->setFromExplodedString(argumentVector); } void SettingsManager::setFromString(std::string const& commandLineString) { if (commandLineString.empty()) { this->setFromExplodedString({}); } else { std::vector argumentVector; boost::split(argumentVector, commandLineString, boost::is_any_of("\t ")); this->setFromExplodedString(argumentVector); } } void SettingsManager::handleUnknownOption(std::string const& optionName, bool isShort) const { std::string optionNameWithDashes = (isShort ? "-" : "--") + optionName; storm::utility::string::SimilarStrings similarStrings(optionNameWithDashes, 0.6, false); std::map> similarOptionNames; for (auto const& longOption : longNameToOptions) { if (similarStrings.add("--" + longOption.first)) { similarOptionNames["--" + longOption.first].push_back(longOption.first); } } for (auto const& shortOption : shortNameToOptions) { if (similarStrings.add("-" + shortOption.first)) { for (auto const& option : shortOption.second) { similarOptionNames["-" + shortOption.first].push_back(option->getLongName()); } } } std::string errorMessage = "Unknown option '" + optionNameWithDashes + "'."; if (!similarOptionNames.empty()) { errorMessage += " " + similarStrings.toDidYouMeanString() + "\n\n"; std::vector sortedSimilarOptionNames; auto similarStringsList = similarStrings.toList(); for (auto const& s : similarStringsList) { for (auto const& longOptionName : similarOptionNames.at(s)) { sortedSimilarOptionNames.push_back(longOptionName); } } errorMessage += getHelpForSelection({}, sortedSimilarOptionNames, "", "##### Suggested options:"); } STORM_LOG_THROW(false, storm::exceptions::OptionParserException, errorMessage); } void SettingsManager::setFromExplodedString(std::vector const& commandLineArguments) { // In order to assign the parsed arguments to an option, we need to keep track of the "active" option's name. bool optionActive = false; bool activeOptionIsShortName = false; std::string activeOptionName = ""; std::vector argumentCache; // Walk through all arguments. for (uint_fast64_t i = 0; i < commandLineArguments.size(); ++i) { std::string const& currentArgument = commandLineArguments[i]; // Check if the given argument is a new option or belongs to a previously given option. if (!currentArgument.empty() && currentArgument.at(0) == '-') { if (optionActive) { // At this point we know that a new option is about to come. Hence, we need to assign the current // cache content to the option that was active until now. setOptionsArguments(activeOptionName, activeOptionIsShortName ? this->shortNameToOptions : this->longNameToOptions, argumentCache); // After the assignment, the argument cache needs to be cleared. argumentCache.clear(); } else { optionActive = true; } if (currentArgument.at(1) == '-') { // In this case, the argument has to be the long name of an option. Try to get all options that // match the long name. std::string optionName = currentArgument.substr(2); auto optionIterator = this->longNameToOptions.find(optionName); if (optionIterator == this->longNameToOptions.end()) { handleUnknownOption(optionName, false); } activeOptionIsShortName = false; activeOptionName = optionName; } else { // In this case, the argument has to be the short name of an option. Try to get all options that // match the short name. std::string optionName = currentArgument.substr(1); auto optionIterator = this->shortNameToOptions.find(optionName); if (optionIterator == this->shortNameToOptions.end()) { handleUnknownOption(optionName, true); } activeOptionIsShortName = true; activeOptionName = optionName; } } else if (optionActive) { // Add the current argument to the list of arguments for the currently active options. argumentCache.push_back(currentArgument); } else { STORM_LOG_THROW(false, storm::exceptions::OptionParserException, "Found stray argument '" << currentArgument << "' that is not preceeded by a matching option."); } } // If an option is still active at this point, we need to set it. if (optionActive) { setOptionsArguments(activeOptionName, activeOptionIsShortName ? this->shortNameToOptions : this->longNameToOptions, argumentCache); } // Include the options from a possibly specified configuration file, but don't overwrite existing settings. if (storm::settings::hasModule() && storm::settings::getModule().isConfigSet()) { this->setFromConfigurationFile(storm::settings::getModule().getConfigFilename()); } // Finally, check whether all modules are okay with the current settings. this->finalizeAllModules(); } void SettingsManager::setFromConfigurationFile(std::string const& configFilename) { std::map> configurationFileSettings = parseConfigFile(configFilename); for (auto const& optionArgumentsPair : configurationFileSettings) { auto options = this->longNameToOptions.find(optionArgumentsPair.first); // We don't need to check whether this option exists or not, because this is already checked when // parsing the configuration file. // Now go through all the matching options and set them according to the values. for (auto option : options->second) { if (option->getHasOptionBeenSet()) { // If the option was already set from the command line, we issue a warning and ignore the // settings from the configuration file. 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); } else { // If, however, the option has not been set yet, we try to assign values ot its arguments // based on the argument strings. setOptionArguments(optionArgumentsPair.first, option, optionArgumentsPair.second); } } } // Finally, check whether all modules are okay with the current settings. this->finalizeAllModules(); } void SettingsManager::printHelp(std::string const& filter) const { STORM_PRINT("usage: " << executableName << " [options]" << std::endl << std::endl); if (filter == "frequent" || filter == "all") { bool includeAdvanced = (filter == "all"); // Find longest option name. uint_fast64_t maxLength = getPrintLengthOfLongestOption(includeAdvanced); std::vector invisibleModules; uint64_t numHidden = 0; for (auto const& moduleName : this->moduleNames) { // Only print for visible modules. if (hasModule(moduleName, true)) { STORM_PRINT(getHelpForModule(moduleName, maxLength, includeAdvanced)); // collect 'hidden' options if (!includeAdvanced) { auto moduleIterator = moduleOptions.find(moduleName); if (moduleIterator != this->moduleOptions.end()) { bool allAdvanced = true; for (auto const& option : moduleIterator->second) { if (!option->getIsAdvanced()) { allAdvanced = false; } else { ++numHidden; } } if (!moduleIterator->second.empty() && allAdvanced) { invisibleModules.push_back(moduleName); } } } } } if (!includeAdvanced) { if (numHidden == 1) { STORM_PRINT(numHidden << " hidden option." << std::endl); } else { STORM_PRINT(numHidden << " hidden options." << std::endl); } if (!invisibleModules.empty()) { if (invisibleModules.size() == 1) { STORM_PRINT(invisibleModules.size() << " hidden module (" << boost::join(invisibleModules, ", ") << ")." << std::endl); } else { STORM_PRINT(invisibleModules.size() << " hidden modules (" << boost::join(invisibleModules, ", ") << ")." << std::endl); } } STORM_PRINT(std::endl << "Type '" + executableName + " --help modulename' to display all options of a specific module." << std::endl); STORM_PRINT("Type '" + executableName + " --help all' to display a complete list of options." << std::endl); } } else { // Create a regular expression from the input hint. std::regex hintRegex(filter, std::regex_constants::ECMAScript | std::regex_constants::icase); // Try to match the regular expression against the known modules. std::vector matchingModuleNames; for (auto const& moduleName : this->moduleNames) { if (std::regex_search(moduleName, hintRegex)) { if (hasModule(moduleName, true)) { matchingModuleNames.push_back(moduleName); } } } // Try to match the regular expression against the known options. std::vector matchingOptionNames; for (auto const& optionName : this->longOptionNames) { if (std::regex_search(optionName, hintRegex)) { matchingOptionNames.push_back(optionName); } } std::string optionList = getHelpForSelection(matchingModuleNames, matchingOptionNames, "Matching modules for filter '" + filter +"':", "Matching options for filter '" + filter +"':"); if (optionList.empty()) { STORM_PRINT("Filter '" << filter << "' did not match any modules or options." << std::endl); } else { STORM_PRINT(optionList); } } } std::string SettingsManager::getHelpForSelection(std::vector const& selectedModuleNames, std::vector const& selectedLongOptionNames, std::string modulesHeader, std::string optionsHeader) const { std::stringstream stream; // Remember which options we printed, so we don't display options twice. std::set> printedOptions; // Try to match the regular expression against the known modules. uint_fast64_t maxLengthModules = 0; for (auto const& moduleName : selectedModuleNames) { maxLengthModules = std::max(maxLengthModules, getPrintLengthOfLongestOption(moduleName, true)); // Add all options of this module to the list of printed options so we don't print them twice. auto optionIterator = this->moduleOptions.find(moduleName); STORM_LOG_ASSERT(optionIterator != this->moduleOptions.end(), "Unable to find selected module " << moduleName << "."); printedOptions.insert(optionIterator->second.begin(), optionIterator->second.end()); } // Try to match the regular expression against the known options. std::vector> matchingOptions; uint_fast64_t maxLengthOptions = 0; for (auto const& optionName : selectedLongOptionNames) { auto optionIterator = this->longNameToOptions.find(optionName); STORM_LOG_ASSERT(optionIterator != this->longNameToOptions.end(), "Unable to find selected option " << optionName << "."); for (auto const& option : optionIterator->second) { // Only add the option if we have not already added it to the list of options that is going // to be printed anyway. if (printedOptions.find(option) == printedOptions.end()) { maxLengthOptions = std::max(maxLengthOptions, option->getPrintLength()); matchingOptions.push_back(option); printedOptions.insert(option); } } } // Print the matching modules. uint_fast64_t maxLength = std::max(maxLengthModules, maxLengthOptions); if (selectedModuleNames.size() > 0) { if (modulesHeader != "") { stream << modulesHeader << std::endl; } for (auto const& matchingModuleName : selectedModuleNames) { stream << getHelpForModule(matchingModuleName, maxLength, true); } } // Print the matching options. if (matchingOptions.size() > 0) { if (optionsHeader != "") { stream << optionsHeader << std::endl; } for (auto const& option : matchingOptions) { stream << std::setw(maxLength) << std::left << *option << std::endl; } } return stream.str(); } std::string SettingsManager::getHelpForModule(std::string const& moduleName, uint_fast64_t maxLength, bool includeAdvanced) const { auto moduleIterator = moduleOptions.find(moduleName); if(moduleIterator == this->moduleOptions.end()) { return ""; } //STORM_LOG_THROW(moduleIterator != moduleOptions.end(), storm::exceptions::IllegalFunctionCallException, "Cannot print help for unknown module '" << moduleName << "'."); // Check whether there is at least one (enabled) option in this module uint64_t numOfOptions = 0; for (auto const& option : moduleIterator->second) { if (includeAdvanced || !option->getIsAdvanced()) { ++numOfOptions; } } std::stringstream stream; if (numOfOptions > 0) { std::string displayedModuleName = "'" + moduleName + "'"; if (!includeAdvanced) { displayedModuleName += " (" + std::to_string(numOfOptions) + "/" + std::to_string(moduleIterator->second.size()) + " shown)"; } stream << "##### Module " << displayedModuleName << " " << std::string(std::min(maxLength, maxLength - displayedModuleName.length() - 14), '#') << std::endl; // Save the flags for std::cout so we can manipulate them and be sure they will be restored as soon as this // stream goes out of scope. boost::io::ios_flags_saver out(std::cout); for (auto const& option : moduleIterator->second) { if (includeAdvanced || !option->getIsAdvanced()) { stream << std::setw(maxLength) << std::left << *option << std::endl; } } stream << std::endl; } return stream.str(); } uint_fast64_t SettingsManager::getPrintLengthOfLongestOption(bool includeAdvanced) const { uint_fast64_t length = 0; for (auto const& moduleName : this->moduleNames) { length = std::max(getPrintLengthOfLongestOption(moduleName, includeAdvanced), length); } return length; } uint_fast64_t SettingsManager::getPrintLengthOfLongestOption(std::string const& moduleName, bool includeAdvanced) const { auto moduleIterator = modules.find(moduleName); STORM_LOG_THROW(moduleIterator != modules.end(), storm::exceptions::IllegalFunctionCallException, "Unable to retrieve option length of unknown module '" << moduleName << "'."); return moduleIterator->second->getPrintLengthOfLongestOption(includeAdvanced); } void SettingsManager::addModule(std::unique_ptr&& moduleSettings, bool doRegister) { auto moduleIterator = this->modules.find(moduleSettings->getModuleName()); 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."); // Take over the module settings object. std::string const& moduleName = moduleSettings->getModuleName(); this->moduleNames.push_back(moduleName); this->modules.emplace(moduleSettings->getModuleName(), std::move(moduleSettings)); auto iterator = this->modules.find(moduleName); std::unique_ptr const& settings = iterator->second; if (doRegister) { this->moduleOptions.emplace(moduleName, std::vector>()); // Now register the options of the module. for (auto const& option : settings->getOptions()) { this->addOption(option); } } } void SettingsManager::addOption(std::shared_ptr