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];
135 bool isNewOption = !currentArgument.empty() && currentArgument.at(0) ==
'-';
136 if (isNewOption && optionActive) {
137 auto const& activeOptionMap = activeOptionIsShortName ? shortNameToOptions : longNameToOptions;
138 auto activeOptionIterator = activeOptionMap.find(activeOptionName);
139 bool activeOptionExpectsMandatoryArgument = activeOptionIterator != activeOptionMap.end() && !activeOptionIterator->second.empty() &&
140 argumentCache.size() < activeOptionIterator->second.front()->getArgumentCount() &&
141 !activeOptionIterator->second.front()->getArgument(argumentCache.size()).getIsOptional();
142 bool currentArgumentIsKnownOption = currentArgument.size() > 1 && currentArgument.at(1) ==
'-'
143 ? longNameToOptions.find(currentArgument.substr(2)) != longNameToOptions.end()
144 : shortNameToOptions.find(currentArgument.substr(1)) != shortNameToOptions.end();
145 if (activeOptionExpectsMandatoryArgument && !currentArgumentIsKnownOption) {
154 setOptionsArguments(activeOptionName, activeOptionIsShortName ? this->shortNameToOptions : this->longNameToOptions, argumentCache);
157 argumentCache.clear();
162 if (currentArgument.size() > 1 && currentArgument.at(1) ==
'-') {
165 std::string optionName = currentArgument.substr(2);
166 auto optionIterator = this->longNameToOptions.find(optionName);
167 if (optionIterator == this->longNameToOptions.end()) {
170 activeOptionIsShortName =
false;
171 activeOptionName = optionName;
175 std::string optionName = currentArgument.substr(1);
176 auto optionIterator = this->shortNameToOptions.find(optionName);
177 if (optionIterator == this->shortNameToOptions.end()) {
180 activeOptionIsShortName =
true;
181 activeOptionName = optionName;
183 }
else if (optionActive) {
185 argumentCache.push_back(currentArgument);
188 "Found stray argument '" << currentArgument <<
"' that is not preceeded by a matching option.");
194 setOptionsArguments(activeOptionName, activeOptionIsShortName ? this->shortNameToOptions : this->longNameToOptions, argumentCache);
204 this->finalizeAllModules();
208 std::map<std::string, std::vector<std::string>> configurationFileSettings = parseConfigFile(configFilename);
210 for (
auto const& optionArgumentsPair : configurationFileSettings) {
211 auto options = this->longNameToOptions.find(optionArgumentsPair.first);
217 for (
auto option : options->second) {
218 if (option->getHasOptionBeenSet()) {
221 STORM_LOG_WARN(
"The option '" << option->getLongName() <<
"' of module '" << option->getModuleName()
222 <<
"' has been set in the configuration file '" << configFilename
223 <<
"', but was overwritten on the command line.\n");
227 setOptionArguments(optionArgumentsPair.first, option, optionArgumentsPair.second);
232 this->finalizeAllModules();
236 std::cout <<
"usage: " << executableName <<
" [options]\n\n";
238 if (filter ==
"frequent" || filter ==
"all") {
239 bool includeAdvanced = (filter ==
"all");
241 uint_fast64_t maxLength = getPrintLengthOfLongestOption(includeAdvanced);
243 std::vector<std::string> invisibleModules;
244 uint64_t numHidden = 0;
245 for (
auto const& moduleName : this->moduleNames) {
250 if (!includeAdvanced) {
251 auto moduleIterator = moduleOptions.find(moduleName);
252 if (moduleIterator != this->moduleOptions.end()) {
253 bool allAdvanced =
true;
254 for (
auto const& option : moduleIterator->second) {
255 if (!option->getIsAdvanced()) {
261 if (!moduleIterator->second.empty() && allAdvanced) {
262 invisibleModules.push_back(moduleName);
268 if (!includeAdvanced) {
269 if (numHidden == 1) {
270 std::cout << numHidden <<
" hidden option.\n";
272 std::cout << numHidden <<
" hidden options.\n";
274 if (!invisibleModules.empty()) {
275 if (invisibleModules.size() == 1) {
276 std::cout << invisibleModules.size() <<
" hidden module (" << boost::join(invisibleModules,
", ") <<
").\n";
278 std::cout << invisibleModules.size() <<
" hidden modules (" << boost::join(invisibleModules,
", ") <<
").\n";
281 std::cout <<
"\nType '" + executableName +
" --help modulename' to display all options of a specific module.\n";
282 std::cout <<
"Type '" + executableName +
" --help all' to display a complete list of options.\n";
286 std::regex hintRegex(filter, std::regex_constants::ECMAScript | std::regex_constants::icase);
289 std::vector<std::string> matchingModuleNames;
290 for (
auto const& moduleName : this->moduleNames) {
291 if (std::regex_search(moduleName, hintRegex)) {
293 matchingModuleNames.push_back(moduleName);
299 std::vector<std::string> matchingOptionNames;
300 for (
auto const& optionName : this->longOptionNames) {
301 if (std::regex_search(optionName, hintRegex)) {
302 matchingOptionNames.push_back(optionName);
306 std::string optionList = getHelpForSelection(matchingModuleNames, matchingOptionNames,
307 "Matching modules for filter '" + filter +
"':",
"Matching options for filter '" + filter +
"':");
308 if (optionList.empty()) {
309 std::cout <<
"Filter '" << filter <<
"' did not match any modules or options.\n";
311 std::cout << optionList;
316std::string SettingsManager::getHelpForSelection(std::vector<std::string>
const& selectedModuleNames, std::vector<std::string>
const& selectedLongOptionNames,
317 std::string modulesHeader, std::string optionsHeader)
const {
318 std::stringstream stream;
321 std::set<std::shared_ptr<Option>> printedOptions;
324 uint_fast64_t maxLengthModules = 0;
325 for (
auto const& moduleName : selectedModuleNames) {
326 maxLengthModules = std::max(maxLengthModules, getPrintLengthOfLongestOption(moduleName,
true));
328 auto optionIterator = this->moduleOptions.find(moduleName);
329 STORM_LOG_ASSERT(optionIterator != this->moduleOptions.end(),
"Unable to find selected module " << moduleName <<
".");
330 printedOptions.insert(optionIterator->second.begin(), optionIterator->second.end());
334 std::vector<std::shared_ptr<Option>> matchingOptions;
335 uint_fast64_t maxLengthOptions = 0;
336 for (
auto const& optionName : selectedLongOptionNames) {
337 auto optionIterator = this->longNameToOptions.find(optionName);
338 STORM_LOG_ASSERT(optionIterator != this->longNameToOptions.end(),
"Unable to find selected option " << optionName <<
".");
339 for (
auto const& option : optionIterator->second) {
342 if (printedOptions.find(option) == printedOptions.end()) {
343 maxLengthOptions = std::max(maxLengthOptions, option->getPrintLength());
344 matchingOptions.push_back(option);
345 printedOptions.insert(option);
351 uint_fast64_t maxLength = std::max(maxLengthModules, maxLengthOptions);
352 if (selectedModuleNames.size() > 0) {
353 if (modulesHeader !=
"") {
354 stream << modulesHeader <<
'\n';
356 for (
auto const& matchingModuleName : selectedModuleNames) {
357 stream << getHelpForModule(matchingModuleName, maxLength,
true);
362 if (matchingOptions.size() > 0) {
363 if (optionsHeader !=
"") {
364 stream << optionsHeader <<
'\n';
366 for (
auto const& option : matchingOptions) {
367 stream << std::setw(maxLength) << std::left << *option <<
'\n';
374 auto moduleIterator = moduleOptions.find(moduleName);
375 if (moduleIterator == this->moduleOptions.end()) {
382 uint64_t numOfOptions = 0;
383 for (
auto const& option : moduleIterator->second) {
384 if (includeAdvanced || !option->getIsAdvanced()) {
389 std::stringstream stream;
390 if (numOfOptions > 0) {
391 std::string displayedModuleName =
"'" + moduleName +
"'";
392 if (!includeAdvanced) {
393 displayedModuleName +=
" (" + std::to_string(numOfOptions) +
"/" + std::to_string(moduleIterator->second.size()) +
" shown)";
395 stream <<
"##### Module " << displayedModuleName <<
" " << std::string(std::min(maxLength, maxLength - displayedModuleName.length() - 14),
'#') <<
'\n';
399 boost::io::ios_flags_saver out(std::cout);
401 for (
auto const& option : moduleIterator->second) {
402 if (includeAdvanced || !option->getIsAdvanced()) {
403 stream << std::setw(maxLength) << std::left << *option <<
'\n';
411uint_fast64_t SettingsManager::getPrintLengthOfLongestOption(
bool includeAdvanced)
const {
412 uint_fast64_t length = 0;
413 for (
auto const& moduleName : this->moduleNames) {
414 length = std::max(getPrintLengthOfLongestOption(moduleName, includeAdvanced), length);
419uint_fast64_t SettingsManager::getPrintLengthOfLongestOption(std::string
const& moduleName,
bool includeAdvanced)
const {
420 auto moduleIterator = modules.find(moduleName);
421 STORM_LOG_THROW(moduleIterator != modules.end(), storm::exceptions::IllegalFunctionCallException,
422 "Unable to retrieve option length of unknown module '" << moduleName <<
"'.");
423 return moduleIterator->second->getPrintLengthOfLongestOption(includeAdvanced);
427 auto moduleIterator = this->modules.find(moduleSettings->getModuleName());
428 STORM_LOG_THROW(moduleIterator == this->modules.end(), storm::exceptions::IllegalFunctionCallException,
429 "Unable to register module '" << moduleSettings->getModuleName() <<
"' because a module with the same name already exists.");
432 std::string moduleName = moduleSettings->getModuleName();
433 this->moduleNames.push_back(moduleName);
434 this->modules.emplace(moduleSettings->getModuleName(), std::move(moduleSettings));
435 auto iterator = this->modules.find(moduleName);
436 std::unique_ptr<modules::ModuleSettings>
const&
settings = iterator->second;
439 this->moduleOptions.emplace(moduleName, std::vector<std::shared_ptr<Option>>());
441 for (
auto const& option :
settings->getOptions()) {
442 this->addOption(option);
447void SettingsManager::addOption(std::shared_ptr<Option>
const& option) {
449 auto moduleOptionIterator = this->moduleOptions.find(option->getModuleName());
450 STORM_LOG_THROW(moduleOptionIterator != this->moduleOptions.end(), storm::exceptions::IllegalFunctionCallException,
451 "Cannot add option for unknown module '" << option->getModuleName() <<
"'.");
452 moduleOptionIterator->second.emplace_back(option);
457 if (!option->getRequiresModulePrefix()) {
458 bool isCompatible = storm::settings::SettingsManager::isCompatible(option, option->getLongName(), this->longNameToOptions);
459 STORM_LOG_THROW(isCompatible, storm::exceptions::IllegalFunctionCallException,
460 "Unable to add option '" << option->getLongName() <<
"', because an option with the same name is incompatible with it.");
461 addOptionToMap(option->getLongName(), option, this->longNameToOptions);
464 addOptionToMap(option->getModuleName() +
":" + option->getLongName(), option, this->longNameToOptions);
465 longOptionNames.push_back(option->getModuleName() +
":" + option->getLongName());
467 if (option->getHasShortName()) {
468 if (!option->getRequiresModulePrefix()) {
469 bool isCompatible = storm::settings::SettingsManager::isCompatible(option, option->getShortName(), this->shortNameToOptions);
470 STORM_LOG_THROW(isCompatible, storm::exceptions::IllegalFunctionCallException,
471 "Unable to add option '" << option->getLongName() <<
"', because an option with the same name is incompatible with it.");
472 addOptionToMap(option->getShortName(), option, this->shortNameToOptions);
474 addOptionToMap(option->getModuleName() +
":" + option->getShortName(), option, this->shortNameToOptions);
480 return this->moduleOptions.find(moduleName) != this->moduleOptions.end();
482 return this->modules.find(moduleName) != this->modules.end();
487 auto moduleIterator = this->modules.find(moduleName);
488 STORM_LOG_THROW(moduleIterator != this->modules.end(), storm::exceptions::IllegalFunctionCallException,
489 "Cannot retrieve unknown module '" << moduleName <<
"'.");
490 return *moduleIterator->second;
494 auto moduleIterator = this->modules.find(moduleName);
495 STORM_LOG_THROW(moduleIterator != this->modules.end(), storm::exceptions::IllegalFunctionCallException,
496 "Cannot retrieve unknown module '" << moduleName <<
"'.");
497 return *moduleIterator->second;
500bool SettingsManager::isCompatible(std::shared_ptr<Option>
const& option, std::string
const& optionName,
501 std::unordered_map<std::string, std::vector<std::shared_ptr<Option>>>
const& optionMap) {
502 auto optionIterator = optionMap.find(optionName);
503 if (optionIterator != optionMap.end()) {
504 for (
auto const& otherOption : optionIterator->second) {
505 bool locallyCompatible = option->isCompatibleWith(*otherOption);
506 if (!locallyCompatible) {
514void SettingsManager::setOptionArguments(std::string
const& optionName, std::shared_ptr<Option> option, std::vector<std::string>
const& argumentCache) {
515 STORM_LOG_THROW(argumentCache.size() <= option->getArgumentCount(), storm::exceptions::OptionParserException,
516 "Too many arguments for option '" << optionName <<
"'.");
517 STORM_LOG_THROW(!option->getHasOptionBeenSet(), storm::exceptions::OptionParserException,
"Option '" << optionName <<
"' is set multiple times.");
520 for (uint_fast64_t i = 0;
i < argumentCache.size(); ++
i) {
521 ArgumentBase& argument = option->getArgument(i);
522 bool conversionOk = argument.setFromStringValue(argumentCache[i]);
523 STORM_LOG_THROW(conversionOk, storm::exceptions::OptionParserException,
524 "Value '" << argumentCache[i] <<
"' is invalid for argument <" << argument.getName() <<
"> of option:\n"
529 for (uint_fast64_t i = argumentCache.size(); i < option->getArgumentCount(); ++
i) {
530 ArgumentBase& argument = option->getArgument(i);
531 STORM_LOG_THROW(argument.getIsOptional(), storm::exceptions::OptionParserException,
532 "Non-optional argument <" << argument.getName() <<
"> of option:\n"
534 argument.setFromDefaultValue();
537 option->setHasOptionBeenSet();
538 if (optionName != option->getLongName() && optionName != option->getShortName() && boost::starts_with(optionName, option->getModuleName())) {
539 option->setHasOptionBeenSetWithModulePrefix();
543void SettingsManager::setOptionsArguments(std::string
const& optionName, std::unordered_map<std::string, std::vector<std::shared_ptr<Option>>>
const& optionMap,
544 std::vector<std::string>
const& argumentCache) {
545 auto optionIterator = optionMap.find(optionName);
546 STORM_LOG_THROW(optionIterator != optionMap.end(), storm::exceptions::OptionParserException,
"Unknown option '" << optionName <<
"'.");
549 for (
auto& option : optionIterator->second) {
550 setOptionArguments(optionName, option, argumentCache);
554void SettingsManager::addOptionToMap(std::string
const& name, std::shared_ptr<Option>
const& option,
555 std::unordered_map<std::string, std::vector<std::shared_ptr<Option>>>& optionMap) {
556 auto optionIterator = optionMap.find(name);
557 if (optionIterator == optionMap.end()) {
558 std::vector<std::shared_ptr<Option>> optionVector;
559 optionVector.push_back(option);
560 optionMap.emplace(name, optionVector);
562 optionIterator->second.push_back(option);
566void SettingsManager::finalizeAllModules() {
567 for (
auto const& nameModulePair : this->modules) {
568 nameModulePair.second->finalize();
569 nameModulePair.second->check();
573std::map<std::string, std::vector<std::string>> SettingsManager::parseConfigFile(std::string
const& filename)
const {
574 std::map<std::string, std::vector<std::string>> result;
579 bool globalScope =
true;
580 std::string activeModule =
"";
581 uint_fast64_t lineNumber = 1;
585 if (line.at(0) ==
'[') {
587 line.at(0) ==
'[' && line.find(
"]") == line.length() - 1 && line.find(
"[", 1) == line.npos, storm::exceptions::OptionParserException,
588 "Illegal module name header in configuration file '" << filename <<
" in line " << std::to_string(lineNumber)
589 <<
". Expected [<module>] where <module> is a placeholder for a known module.");
592 std::string moduleName = line.substr(1, line.length() - 2);
593 STORM_LOG_THROW(moduleName !=
"" && (moduleName ==
"global" || (this->modules.find(moduleName) != this->modules.end())),
594 storm::exceptions::OptionParserException,
595 "Module header in configuration file '" << filename <<
" in line " << std::to_string(lineNumber) <<
" refers to unknown module '"
596 << moduleName <<
".");
599 if (moduleName ==
"global") {
602 activeModule = moduleName;
608 std::size_t assignmentSignIndex = line.find(
"=");
609 bool containsAssignment =
false;
610 if (assignmentSignIndex != std::string::npos) {
611 containsAssignment =
true;
614 std::string optionName;
615 if (containsAssignment) {
616 optionName = line.substr(0, assignmentSignIndex);
622 STORM_LOG_THROW(this->longNameToOptions.find(optionName) != this->longNameToOptions.end(), storm::exceptions::OptionParserException,
623 "Option assignment in configuration file '" << filename <<
" in line " << lineNumber <<
" refers to unknown option '"
624 << optionName <<
"'.");
626 STORM_LOG_THROW(this->longNameToOptions.find(activeModule +
":" + optionName) != this->longNameToOptions.end(),
627 storm::exceptions::OptionParserException,
628 "Option assignment in configuration file '" << filename <<
" in line " << lineNumber <<
" refers to unknown option '"
629 << activeModule <<
":" << optionName <<
"'.");
632 std::string fullOptionName = (!globalScope ? activeModule +
":" :
"") + optionName;
633 STORM_LOG_WARN_COND(result.find(fullOptionName) == result.end(),
"Option '" << fullOptionName <<
"' is set in line " << lineNumber
634 <<
" of configuration file " << filename
635 <<
", but has been set before.");
639 if (containsAssignment) {
640 std::string assignedValues = line.substr(assignmentSignIndex + 1);
641 std::vector<std::string> argumentCache;
645 std::regex argumentRegex(
"\"(([^\\\\\"]|((\\\\\\\\)*\\\\\")|\\\\[^\"])*)\"|(([^ \\\\\"]|((\\\\\\\\)*\\\\\")|\\\\[^\"])+)");
646 boost::algorithm::trim_left(assignedValues);
648 while (!assignedValues.empty()) {
650 bool hasMatch = std::regex_search(assignedValues, match, argumentRegex);
654 hasMatch, storm::exceptions::OptionParserException,
655 "Parsing error in configuration file '" << filename <<
"' in line " << lineNumber <<
". Unexpected input '" << assignedValues <<
"'.");
658 std::string matchedArgument = std::string(match[0].first, match[0].second);
659 if (matchedArgument.at(0) ==
'"') {
660 matchedArgument = matchedArgument.substr(1, matchedArgument.length() - 2);
662 argumentCache.push_back(matchedArgument);
664 assignedValues = assignedValues.substr(match.length());
665 boost::algorithm::trim_left(assignedValues);
669 result.emplace(fullOptionName, argumentCache);
672 result.emplace(fullOptionName, std::vector<std::string>());
697void 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)
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.