3#include <boost/algorithm/string.hpp>
4#include <boost/io/ios_state.hpp>
54SettingsManager::~SettingsManager() {
60 return settingsManager;
65 this->executableName = executableName;
71 std::vector<std::string> argumentVector(argc - 1);
72 for (
int i = 1; i < argc; ++i) {
73 argumentVector[i - 1] = std::string(argv[i]);
80 if (commandLineString.empty()) {
83 std::vector<std::string> argumentVector;
84 boost::split(argumentVector, commandLineString, boost::is_any_of(
"\t "));
90 std::string optionNameWithDashes = (isShort ?
"-" :
"--") + optionName;
92 std::map<std::string, std::vector<std::string>> similarOptionNames;
93 for (
auto const& longOption : longNameToOptions) {
94 if (similarStrings.
add(
"--" + longOption.first)) {
95 similarOptionNames[
"--" + longOption.first].push_back(longOption.first);
98 for (
auto const& shortOption : shortNameToOptions) {
99 if (similarStrings.
add(
"-" + shortOption.first)) {
100 for (
auto const& option : shortOption.second) {
101 similarOptionNames[
"-" + shortOption.first].push_back(option->getLongName());
105 std::string errorMessage =
"Unknown option '" + optionNameWithDashes +
"'.";
106 if (!similarOptionNames.empty()) {
108 std::vector<std::string> sortedSimilarOptionNames;
109 auto similarStringsList = similarStrings.
toList();
110 for (
auto const& s : similarStringsList) {
111 for (
auto const& longOptionName : similarOptionNames.at(s)) {
112 sortedSimilarOptionNames.push_back(longOptionName);
115 errorMessage += getHelpForSelection({}, sortedSimilarOptionNames,
"",
"##### Suggested options:");
117 STORM_LOG_THROW(
false, storm::exceptions::OptionParserException, errorMessage);
122 bool optionActive =
false;
123 bool activeOptionIsShortName =
false;
124 std::string activeOptionName =
"";
125 std::vector<std::string> argumentCache;
128 for (uint_fast64_t i = 0; i < commandLineArguments.size(); ++i) {
129 std::string
const& currentArgument = commandLineArguments[i];
132 if (!currentArgument.empty() && currentArgument.at(0) ==
'-') {
136 setOptionsArguments(activeOptionName, activeOptionIsShortName ? this->shortNameToOptions : this->longNameToOptions, argumentCache);
139 argumentCache.clear();
144 if (currentArgument.at(1) ==
'-') {
147 std::string optionName = currentArgument.substr(2);
148 auto optionIterator = this->longNameToOptions.find(optionName);
149 if (optionIterator == this->longNameToOptions.end()) {
152 activeOptionIsShortName =
false;
153 activeOptionName = optionName;
157 std::string optionName = currentArgument.substr(1);
158 auto optionIterator = this->shortNameToOptions.find(optionName);
159 if (optionIterator == this->shortNameToOptions.end()) {
162 activeOptionIsShortName =
true;
163 activeOptionName = optionName;
165 }
else if (optionActive) {
167 argumentCache.push_back(currentArgument);
170 "Found stray argument '" << currentArgument <<
"' that is not preceeded by a matching option.");
176 setOptionsArguments(activeOptionName, activeOptionIsShortName ? this->shortNameToOptions : this->longNameToOptions, argumentCache);
186 this->finalizeAllModules();
190 std::map<std::string, std::vector<std::string>> configurationFileSettings = parseConfigFile(configFilename);
192 for (
auto const& optionArgumentsPair : configurationFileSettings) {
193 auto options = this->longNameToOptions.find(optionArgumentsPair.first);
199 for (
auto option : options->second) {
200 if (option->getHasOptionBeenSet()) {
203 STORM_LOG_WARN(
"The option '" << option->getLongName() <<
"' of module '" << option->getModuleName()
204 <<
"' has been set in the configuration file '" << configFilename
205 <<
"', but was overwritten on the command line.\n");
209 setOptionArguments(optionArgumentsPair.first, option, optionArgumentsPair.second);
214 this->finalizeAllModules();
218 STORM_PRINT(
"usage: " << executableName <<
" [options]\n\n");
220 if (filter ==
"frequent" || filter ==
"all") {
221 bool includeAdvanced = (filter ==
"all");
223 uint_fast64_t maxLength = getPrintLengthOfLongestOption(includeAdvanced);
225 std::vector<std::string> invisibleModules;
226 uint64_t numHidden = 0;
227 for (
auto const& moduleName : this->moduleNames) {
232 if (!includeAdvanced) {
233 auto moduleIterator = moduleOptions.find(moduleName);
234 if (moduleIterator != this->moduleOptions.end()) {
235 bool allAdvanced =
true;
236 for (
auto const& option : moduleIterator->second) {
237 if (!option->getIsAdvanced()) {
243 if (!moduleIterator->second.empty() && allAdvanced) {
244 invisibleModules.push_back(moduleName);
250 if (!includeAdvanced) {
251 if (numHidden == 1) {
256 if (!invisibleModules.empty()) {
257 if (invisibleModules.size() == 1) {
258 STORM_PRINT(invisibleModules.size() <<
" hidden module (" << boost::join(invisibleModules,
", ") <<
").\n");
260 STORM_PRINT(invisibleModules.size() <<
" hidden modules (" << boost::join(invisibleModules,
", ") <<
").\n");
263 STORM_PRINT(
"\nType '" + executableName +
" --help modulename' to display all options of a specific module.\n");
264 STORM_PRINT(
"Type '" + executableName +
" --help all' to display a complete list of options.\n");
268 std::regex hintRegex(filter, std::regex_constants::ECMAScript | std::regex_constants::icase);
271 std::vector<std::string> matchingModuleNames;
272 for (
auto const& moduleName : this->moduleNames) {
273 if (std::regex_search(moduleName, hintRegex)) {
275 matchingModuleNames.push_back(moduleName);
281 std::vector<std::string> matchingOptionNames;
282 for (
auto const& optionName : this->longOptionNames) {
283 if (std::regex_search(optionName, hintRegex)) {
284 matchingOptionNames.push_back(optionName);
288 std::string optionList = getHelpForSelection(matchingModuleNames, matchingOptionNames,
289 "Matching modules for filter '" + filter +
"':",
"Matching options for filter '" + filter +
"':");
290 if (optionList.empty()) {
291 STORM_PRINT(
"Filter '" << filter <<
"' did not match any modules or options.\n");
298std::string SettingsManager::getHelpForSelection(std::vector<std::string>
const& selectedModuleNames, std::vector<std::string>
const& selectedLongOptionNames,
299 std::string modulesHeader, std::string optionsHeader)
const {
300 std::stringstream stream;
303 std::set<std::shared_ptr<Option>> printedOptions;
306 uint_fast64_t maxLengthModules = 0;
307 for (
auto const& moduleName : selectedModuleNames) {
308 maxLengthModules = std::max(maxLengthModules, getPrintLengthOfLongestOption(moduleName,
true));
310 auto optionIterator = this->moduleOptions.find(moduleName);
311 STORM_LOG_ASSERT(optionIterator != this->moduleOptions.end(),
"Unable to find selected module " << moduleName <<
".");
312 printedOptions.insert(optionIterator->second.begin(), optionIterator->second.end());
316 std::vector<std::shared_ptr<Option>> matchingOptions;
317 uint_fast64_t maxLengthOptions = 0;
318 for (
auto const& optionName : selectedLongOptionNames) {
319 auto optionIterator = this->longNameToOptions.find(optionName);
320 STORM_LOG_ASSERT(optionIterator != this->longNameToOptions.end(),
"Unable to find selected option " << optionName <<
".");
321 for (
auto const& option : optionIterator->second) {
324 if (printedOptions.find(option) == printedOptions.end()) {
325 maxLengthOptions = std::max(maxLengthOptions, option->getPrintLength());
326 matchingOptions.push_back(option);
327 printedOptions.insert(option);
333 uint_fast64_t maxLength = std::max(maxLengthModules, maxLengthOptions);
334 if (selectedModuleNames.size() > 0) {
335 if (modulesHeader !=
"") {
336 stream << modulesHeader <<
'\n';
338 for (
auto const& matchingModuleName : selectedModuleNames) {
339 stream << getHelpForModule(matchingModuleName, maxLength,
true);
344 if (matchingOptions.size() > 0) {
345 if (optionsHeader !=
"") {
346 stream << optionsHeader <<
'\n';
348 for (
auto const& option : matchingOptions) {
349 stream << std::setw(maxLength) << std::left << *option <<
'\n';
356 auto moduleIterator = moduleOptions.find(moduleName);
357 if (moduleIterator == this->moduleOptions.end()) {
364 uint64_t numOfOptions = 0;
365 for (
auto const& option : moduleIterator->second) {
366 if (includeAdvanced || !option->getIsAdvanced()) {
371 std::stringstream stream;
372 if (numOfOptions > 0) {
373 std::string displayedModuleName =
"'" + moduleName +
"'";
374 if (!includeAdvanced) {
375 displayedModuleName +=
" (" + std::to_string(numOfOptions) +
"/" + std::to_string(moduleIterator->second.size()) +
" shown)";
377 stream <<
"##### Module " << displayedModuleName <<
" " << std::string(std::min(maxLength, maxLength - displayedModuleName.length() - 14),
'#') <<
'\n';
381 boost::io::ios_flags_saver out(std::cout);
383 for (
auto const& option : moduleIterator->second) {
384 if (includeAdvanced || !option->getIsAdvanced()) {
385 stream << std::setw(maxLength) << std::left << *option <<
'\n';
393uint_fast64_t SettingsManager::getPrintLengthOfLongestOption(
bool includeAdvanced)
const {
394 uint_fast64_t length = 0;
395 for (
auto const& moduleName : this->moduleNames) {
396 length = std::max(getPrintLengthOfLongestOption(moduleName, includeAdvanced), length);
401uint_fast64_t SettingsManager::getPrintLengthOfLongestOption(std::string
const& moduleName,
bool includeAdvanced)
const {
402 auto moduleIterator = modules.find(moduleName);
403 STORM_LOG_THROW(moduleIterator != modules.end(), storm::exceptions::IllegalFunctionCallException,
404 "Unable to retrieve option length of unknown module '" << moduleName <<
"'.");
405 return moduleIterator->second->getPrintLengthOfLongestOption(includeAdvanced);
409 auto moduleIterator = this->modules.find(moduleSettings->getModuleName());
410 STORM_LOG_THROW(moduleIterator == this->modules.end(), storm::exceptions::IllegalFunctionCallException,
411 "Unable to register module '" << moduleSettings->getModuleName() <<
"' because a module with the same name already exists.");
414 std::string moduleName = moduleSettings->getModuleName();
415 this->moduleNames.push_back(moduleName);
416 this->modules.emplace(moduleSettings->getModuleName(), std::move(moduleSettings));
417 auto iterator = this->modules.find(moduleName);
418 std::unique_ptr<modules::ModuleSettings>
const&
settings = iterator->second;
421 this->moduleOptions.emplace(moduleName, std::vector<std::shared_ptr<Option>>());
423 for (
auto const& option :
settings->getOptions()) {
424 this->addOption(option);
429void SettingsManager::addOption(std::shared_ptr<Option>
const& option) {
431 auto moduleOptionIterator = this->moduleOptions.find(option->getModuleName());
432 STORM_LOG_THROW(moduleOptionIterator != this->moduleOptions.end(), storm::exceptions::IllegalFunctionCallException,
433 "Cannot add option for unknown module '" << option->getModuleName() <<
"'.");
434 moduleOptionIterator->second.emplace_back(option);
439 if (!option->getRequiresModulePrefix()) {
440 bool isCompatible = this->isCompatible(option, option->getLongName(), this->longNameToOptions);
441 STORM_LOG_THROW(isCompatible, storm::exceptions::IllegalFunctionCallException,
442 "Unable to add option '" << option->getLongName() <<
"', because an option with the same name is incompatible with it.");
443 addOptionToMap(option->getLongName(), option, this->longNameToOptions);
446 addOptionToMap(option->getModuleName() +
":" + option->getLongName(), option, this->longNameToOptions);
447 longOptionNames.push_back(option->getModuleName() +
":" + option->getLongName());
449 if (option->getHasShortName()) {
450 if (!option->getRequiresModulePrefix()) {
451 bool isCompatible = this->isCompatible(option, option->getShortName(), this->shortNameToOptions);
452 STORM_LOG_THROW(isCompatible, storm::exceptions::IllegalFunctionCallException,
453 "Unable to add option '" << option->getLongName() <<
"', because an option with the same name is incompatible with it.");
454 addOptionToMap(option->getShortName(), option, this->shortNameToOptions);
456 addOptionToMap(option->getModuleName() +
":" + option->getShortName(), option, this->shortNameToOptions);
462 return this->moduleOptions.find(moduleName) != this->moduleOptions.end();
464 return this->modules.find(moduleName) != this->modules.end();
469 auto moduleIterator = this->modules.find(moduleName);
470 STORM_LOG_THROW(moduleIterator != this->modules.end(), storm::exceptions::IllegalFunctionCallException,
471 "Cannot retrieve unknown module '" << moduleName <<
"'.");
472 return *moduleIterator->second;
476 auto moduleIterator = this->modules.find(moduleName);
477 STORM_LOG_THROW(moduleIterator != this->modules.end(), storm::exceptions::IllegalFunctionCallException,
478 "Cannot retrieve unknown module '" << moduleName <<
"'.");
479 return *moduleIterator->second;
482bool SettingsManager::isCompatible(std::shared_ptr<Option>
const& option, std::string
const& optionName,
483 std::unordered_map<std::string, std::vector<std::shared_ptr<Option>>>
const& optionMap) {
484 auto optionIterator = optionMap.find(optionName);
485 if (optionIterator != optionMap.end()) {
486 for (
auto const& otherOption : optionIterator->second) {
487 bool locallyCompatible = option->isCompatibleWith(*otherOption);
488 if (!locallyCompatible) {
496void SettingsManager::setOptionArguments(std::string
const& optionName, std::shared_ptr<Option> option, std::vector<std::string>
const& argumentCache) {
497 STORM_LOG_THROW(argumentCache.size() <= option->getArgumentCount(), storm::exceptions::OptionParserException,
498 "Too many arguments for option '" << optionName <<
"'.");
499 STORM_LOG_THROW(!option->getHasOptionBeenSet(), storm::exceptions::OptionParserException,
"Option '" << optionName <<
"' is set multiple times.");
502 for (uint_fast64_t i = 0;
i < argumentCache.size(); ++
i) {
503 ArgumentBase& argument = option->getArgument(i);
504 bool conversionOk = argument.setFromStringValue(argumentCache[i]);
505 STORM_LOG_THROW(conversionOk, storm::exceptions::OptionParserException,
506 "Value '" << argumentCache[i] <<
"' is invalid for argument <" << argument.getName() <<
"> of option:\n"
511 for (uint_fast64_t i = argumentCache.size(); i < option->getArgumentCount(); ++
i) {
512 ArgumentBase& argument = option->getArgument(i);
513 STORM_LOG_THROW(argument.getIsOptional(), storm::exceptions::OptionParserException,
514 "Non-optional argument <" << argument.getName() <<
"> of option:\n"
516 argument.setFromDefaultValue();
519 option->setHasOptionBeenSet();
520 if (optionName != option->getLongName() && optionName != option->getShortName() && boost::starts_with(optionName, option->getModuleName())) {
521 option->setHasOptionBeenSetWithModulePrefix();
525void SettingsManager::setOptionsArguments(std::string
const& optionName, std::unordered_map<std::string, std::vector<std::shared_ptr<Option>>>
const& optionMap,
526 std::vector<std::string>
const& argumentCache) {
527 auto optionIterator = optionMap.find(optionName);
528 STORM_LOG_THROW(optionIterator != optionMap.end(), storm::exceptions::OptionParserException,
"Unknown option '" << optionName <<
"'.");
531 for (
auto& option : optionIterator->second) {
532 setOptionArguments(optionName, option, argumentCache);
536void SettingsManager::addOptionToMap(std::string
const& name, std::shared_ptr<Option>
const& option,
537 std::unordered_map<std::string, std::vector<std::shared_ptr<Option>>>& optionMap) {
538 auto optionIterator = optionMap.find(name);
539 if (optionIterator == optionMap.end()) {
540 std::vector<std::shared_ptr<Option>> optionVector;
541 optionVector.push_back(option);
542 optionMap.emplace(name, optionVector);
544 optionIterator->second.push_back(option);
548void SettingsManager::finalizeAllModules() {
549 for (
auto const& nameModulePair : this->modules) {
550 nameModulePair.second->finalize();
551 nameModulePair.second->check();
555std::map<std::string, std::vector<std::string>> SettingsManager::parseConfigFile(std::string
const& filename)
const {
556 std::map<std::string, std::vector<std::string>> result;
561 bool globalScope =
true;
562 std::string activeModule =
"";
563 uint_fast64_t lineNumber = 1;
567 if (line.at(0) ==
'[') {
569 line.at(0) ==
'[' && line.find(
"]") == line.length() - 1 && line.find(
"[", 1) == line.npos, storm::exceptions::OptionParserException,
570 "Illegal module name header in configuration file '" << filename <<
" in line " << std::to_string(lineNumber)
571 <<
". Expected [<module>] where <module> is a placeholder for a known module.");
574 std::string moduleName = line.substr(1, line.length() - 2);
575 STORM_LOG_THROW(moduleName !=
"" && (moduleName ==
"global" || (this->modules.find(moduleName) != this->modules.end())),
576 storm::exceptions::OptionParserException,
577 "Module header in configuration file '" << filename <<
" in line " << std::to_string(lineNumber) <<
" refers to unknown module '"
578 << moduleName <<
".");
581 if (moduleName ==
"global") {
584 activeModule = moduleName;
590 std::size_t assignmentSignIndex = line.find(
"=");
591 bool containsAssignment =
false;
592 if (assignmentSignIndex != line.npos) {
593 containsAssignment =
true;
596 std::string optionName;
597 if (containsAssignment) {
598 optionName = line.substr(0, assignmentSignIndex);
604 STORM_LOG_THROW(this->longNameToOptions.find(optionName) != this->longNameToOptions.end(), storm::exceptions::OptionParserException,
605 "Option assignment in configuration file '" << filename <<
" in line " << lineNumber <<
" refers to unknown option '"
606 << optionName <<
"'.");
608 STORM_LOG_THROW(this->longNameToOptions.find(activeModule +
":" + optionName) != this->longNameToOptions.end(),
609 storm::exceptions::OptionParserException,
610 "Option assignment in configuration file '" << filename <<
" in line " << lineNumber <<
" refers to unknown option '"
611 << activeModule <<
":" << optionName <<
"'.");
614 std::string fullOptionName = (!globalScope ? activeModule +
":" :
"") + optionName;
615 STORM_LOG_WARN_COND(result.find(fullOptionName) == result.end(),
"Option '" << fullOptionName <<
"' is set in line " << lineNumber
616 <<
" of configuration file " << filename
617 <<
", but has been set before.");
621 if (containsAssignment) {
622 std::string assignedValues = line.substr(assignmentSignIndex + 1);
623 std::vector<std::string> argumentCache;
627 std::regex argumentRegex(
"\"(([^\\\\\"]|((\\\\\\\\)*\\\\\")|\\\\[^\"])*)\"|(([^ \\\\\"]|((\\\\\\\\)*\\\\\")|\\\\[^\"])+)");
628 boost::algorithm::trim_left(assignedValues);
630 while (!assignedValues.empty()) {
632 bool hasMatch = std::regex_search(assignedValues, match, argumentRegex);
636 hasMatch, storm::exceptions::OptionParserException,
637 "Parsing error in configuration file '" << filename <<
"' in line " << lineNumber <<
". Unexpected input '" << assignedValues <<
"'.");
640 std::string matchedArgument = std::string(match[0].first, match[0].second);
641 if (matchedArgument.at(0) ==
'"') {
642 matchedArgument = matchedArgument.substr(1, matchedArgument.length() - 2);
644 argumentCache.push_back(matchedArgument);
646 assignedValues = assignedValues.substr(match.length());
647 boost::algorithm::trim_left(assignedValues);
651 result.emplace(fullOptionName, argumentCache);
654 result.emplace(fullOptionName, std::vector<std::string>());
679void initializeAll(std::string
const& name, std::string
const& executableName) {
Provides the central API for the registration of command line options and parsing the options from th...
void setFromCommandLine(int const argc, char const *const argv[])
This function parses the given command line arguments and sets all registered options accordingly.
std::string getHelpForModule(std::string const &moduleName, uint_fast64_t maxLength=30, bool includeAdvanced=true) const
This function prints a help message for the specified module to the standard output.
void setFromExplodedString(std::vector< std::string > const &commandLineArguments)
This function parses the given command line arguments (represented by several strings) and sets all r...
void setFromString(std::string const &commandLineString)
This function parses the given command line arguments (represented by one big string) and sets all re...
void setFromConfigurationFile(std::string const &configFilename)
This function parses the given file and sets all registered options accordingly.
void addModule(std::unique_ptr< modules::ModuleSettings > &&moduleSettings, bool doRegister=true)
Adds a new module with the given name.
void printHelp(std::string const &filter="frequent") const
This function prints a help message to the standard output.
void handleUnknownOption(std::string const &optionName, bool isShort) const
Throws an exception with a nice error message indicating similar valid option names.
void setName(std::string const &name, std::string const &executableName)
Sets the name of the tool.
SettingsManager(SettingsManager const &)=delete
modules::ModuleSettings const & getModule(std::string const &moduleName) const
Retrieves the settings of the module with the given name.
bool hasModule(std::string const &moduleName, bool checkHidden=false) const
Checks whether the module with the given name exists.
static SettingsManager & manager()
Retrieves the only existing instance of a settings manager.
This class represents the settings for the abstraction procedures.
static const std::string moduleName
static const std::string moduleName
This is the base class of the settings for a particular module.
bool add(std::string const &string)
Adds the given string to the set of similar strings (if it is similar).
std::string toDidYouMeanString() const
Returns a "Did you mean abc?" string.
std::vector< std::string > toList() const
Gets a list of all added strings that are similar to the reference string.
#define STORM_LOG_WARN(message)
#define STORM_LOG_ASSERT(cond, message)
#define STORM_LOG_WARN_COND(cond, message)
#define STORM_LOG_THROW(cond, exception, message)
#define STORM_PRINT(message)
Define the macros that print information and optionally also log it.
std::basic_istream< CharT, Traits > & getline(std::basic_istream< CharT, Traits > &input, std::basic_string< CharT, Traits, Allocator > &str)
Overloaded getline function which handles different types of newline ( and \r).
void closeFile(std::ofstream &stream)
Close the given file after writing.
void openFile(std::string const &filepath, std::ofstream &filestream, bool append=false, bool silent=false)
Open the given file for writing.
storm::settings::modules::BuildSettings & mutableBuildSettings()
Retrieves the build settings in a mutable form.
storm::settings::modules::AbstractionSettings & mutableAbstractionSettings()
Retrieves the abstraction settings in a mutable form.
bool hasModule()
Returns true if the given module is registered.
SettingsType const & getModule()
Get module.
void addModule(bool doRegister=true)
Add new module to use for the settings.
void initializeAll(std::string const &name, std::string const &executableName)
Initialize the settings manager with all available modules.
SettingsManager const & manager()
Retrieves the settings manager.
SettingsManager & mutableManager()
Retrieves the settings manager.