diff options
author | Dimitry Andric <dim@FreeBSD.org> | 2019-08-20 20:50:12 +0000 |
---|---|---|
committer | Dimitry Andric <dim@FreeBSD.org> | 2019-08-20 20:50:12 +0000 |
commit | e6d1592492a3a379186bfb02bd0f4eda0669c0d5 (patch) | |
tree | 599ab169a01f1c86eda9adc774edaedde2f2db5b /lib/Support/CommandLine.cpp | |
parent | 1a56a5ead7a2e84bee8240f5f6b033b5f1707154 (diff) | |
download | src-e6d1592492a3a379186bfb02bd0f4eda0669c0d5.tar.gz src-e6d1592492a3a379186bfb02bd0f4eda0669c0d5.zip |
Vendor import of stripped llvm trunk r366426 (just before the release_90vendor/llvm/llvm-trunk-r366426
branch point):
https://llvm.org/svn/llvm-project/llvm/trunk@366426
Notes
Notes:
svn path=/vendor/llvm/dist/; revision=351278
svn path=/vendor/llvm/llvm-trunk-r366426/; revision=351279; tag=vendor/llvm/llvm-trunk-r366426
Diffstat (limited to 'lib/Support/CommandLine.cpp')
-rw-r--r-- | lib/Support/CommandLine.cpp | 445 |
1 files changed, 334 insertions, 111 deletions
diff --git a/lib/Support/CommandLine.cpp b/lib/Support/CommandLine.cpp index f7290b54dcf3..25510fa58ff5 100644 --- a/lib/Support/CommandLine.cpp +++ b/lib/Support/CommandLine.cpp @@ -1,9 +1,8 @@ //===-- CommandLine.cpp - Command line parser implementation --------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // @@ -55,6 +54,7 @@ template class basic_parser<bool>; template class basic_parser<boolOrDefault>; template class basic_parser<int>; template class basic_parser<unsigned>; +template class basic_parser<unsigned long>; template class basic_parser<unsigned long long>; template class basic_parser<double>; template class basic_parser<float>; @@ -79,6 +79,7 @@ void parser<bool>::anchor() {} void parser<boolOrDefault>::anchor() {} void parser<int>::anchor() {} void parser<unsigned>::anchor() {} +void parser<unsigned long>::anchor() {} void parser<unsigned long long>::anchor() {} void parser<double>::anchor() {} void parser<float>::anchor() {} @@ -87,8 +88,47 @@ void parser<char>::anchor() {} //===----------------------------------------------------------------------===// +static StringRef ArgPrefix = " -"; +static StringRef ArgPrefixLong = " --"; +static StringRef ArgHelpPrefix = " - "; + +static size_t argPlusPrefixesSize(StringRef ArgName) { + size_t Len = ArgName.size(); + if (Len == 1) + return Len + ArgPrefix.size() + ArgHelpPrefix.size(); + return Len + ArgPrefixLong.size() + ArgHelpPrefix.size(); +} + +static StringRef argPrefix(StringRef ArgName) { + if (ArgName.size() == 1) + return ArgPrefix; + return ArgPrefixLong; +} + +// Option predicates... +static inline bool isGrouping(const Option *O) { + return O->getMiscFlags() & cl::Grouping; +} +static inline bool isPrefixedOrGrouping(const Option *O) { + return isGrouping(O) || O->getFormattingFlag() == cl::Prefix || + O->getFormattingFlag() == cl::AlwaysPrefix; +} + + namespace { +class PrintArg { + StringRef ArgName; +public: + PrintArg(StringRef ArgName) : ArgName(ArgName) {} + friend raw_ostream &operator<<(raw_ostream &OS, const PrintArg&); +}; + +raw_ostream &operator<<(raw_ostream &OS, const PrintArg& Arg) { + OS << argPrefix(Arg.ArgName) << Arg.ArgName; + return OS; +} + class CommandLineParser { public: // Globals for name and overview of program. Program name is not a string to @@ -99,6 +139,11 @@ public: // This collects additional help to be printed. std::vector<StringRef> MoreHelp; + // This collects Options added with the cl::DefaultOption flag. Since they can + // be overridden, they are not added to the appropriate SubCommands until + // ParseCommandLineOptions actually runs. + SmallVector<Option*, 4> DefaultOptions; + // This collects the different option categories that have been registered. SmallPtrSet<OptionCategory *, 16> RegisteredOptionCategories; @@ -113,7 +158,8 @@ public: void ResetAllOptionOccurrences(); bool ParseCommandLineOptions(int argc, const char *const *argv, - StringRef Overview, raw_ostream *Errs = nullptr); + StringRef Overview, raw_ostream *Errs = nullptr, + bool LongOptionsUseDoubleDash = false); void addLiteralOption(Option &Opt, SubCommand *SC, StringRef Name) { if (Opt.hasArgStr()) @@ -147,6 +193,11 @@ public: void addOption(Option *O, SubCommand *SC) { bool HadErrors = false; if (O->hasArgStr()) { + // If it's a DefaultOption, check to make sure it isn't already there. + if (O->isDefaultOption() && + SC->OptionsMap.find(O->ArgStr) != SC->OptionsMap.end()) + return; + // Add argument to the argument map! if (!SC->OptionsMap.insert(std::make_pair(O->ArgStr, O)).second) { errs() << ProgramName << ": CommandLine Error: Option '" << O->ArgStr @@ -186,7 +237,12 @@ public: } } - void addOption(Option *O) { + void addOption(Option *O, bool ProcessDefaultOption = false) { + if (!ProcessDefaultOption && O->isDefaultOption()) { + DefaultOptions.push_back(O); + return; + } + if (O->Subs.empty()) { addOption(O, &*TopLevelSubCommand); } else { @@ -202,8 +258,12 @@ public: OptionNames.push_back(O->ArgStr); SubCommand &Sub = *SC; - for (auto Name : OptionNames) - Sub.OptionsMap.erase(Name); + auto End = Sub.OptionsMap.end(); + for (auto Name : OptionNames) { + auto I = Sub.OptionsMap.find(Name); + if (I != End && I->getValue() == O) + Sub.OptionsMap.erase(I); + } if (O->getFormattingFlag() == cl::Positional) for (auto Opt = Sub.PositionalOpts.begin(); @@ -267,8 +327,13 @@ public: if (O->Subs.empty()) updateArgStr(O, NewName, &*TopLevelSubCommand); else { - for (auto SC : O->Subs) - updateArgStr(O, NewName, SC); + if (O->isInAllSubCommands()) { + for (auto SC : RegisteredSubCommands) + updateArgStr(O, NewName, SC); + } else { + for (auto SC : O->Subs) + updateArgStr(O, NewName, SC); + } } } @@ -332,12 +397,21 @@ public: AllSubCommands->reset(); registerSubCommand(&*TopLevelSubCommand); registerSubCommand(&*AllSubCommands); + + DefaultOptions.clear(); } private: SubCommand *ActiveSubCommand; Option *LookupOption(SubCommand &Sub, StringRef &Arg, StringRef &Value); + Option *LookupLongOption(SubCommand &Sub, StringRef &Arg, StringRef &Value, + bool LongOptionsUseDoubleDash, bool HaveDoubleDash) { + Option *Opt = LookupOption(Sub, Arg, Value); + if (Opt && LongOptionsUseDoubleDash && !HaveDoubleDash && !isGrouping(Opt)) + return nullptr; + return Opt; + } SubCommand *LookupSubCommand(StringRef Name); }; @@ -365,6 +439,26 @@ void Option::setArgStr(StringRef S) { GlobalParser->updateArgStr(this, S); assert((S.empty() || S[0] != '-') && "Option can't start with '-"); ArgStr = S; + if (ArgStr.size() == 1) + setMiscFlag(Grouping); +} + +void Option::addCategory(OptionCategory &C) { + assert(!Categories.empty() && "Categories cannot be empty."); + // Maintain backward compatibility by replacing the default GeneralCategory + // if it's still set. Otherwise, just add the new one. The GeneralCategory + // must be explicitly added if you want multiple categories that include it. + if (&C != &GeneralCategory && Categories[0] == &GeneralCategory) + Categories[0] = &C; + else if (find(Categories, &C) == Categories.end()) + Categories.push_back(&C); +} + +void Option::reset() { + NumOccurrences = 0; + setDefault(); + if (isDefaultOption()) + removeArgument(); } // Initialise the general option category. @@ -374,7 +468,11 @@ void OptionCategory::registerCategory() { GlobalParser->registerCategory(this); } -// A special subcommand representing no subcommand +// A special subcommand representing no subcommand. It is particularly important +// that this ManagedStatic uses constant initailization and not dynamic +// initialization because it is referenced from cl::opt constructors, which run +// dynamically in an arbitrary order. +LLVM_REQUIRE_CONSTANT_INITIALIZATION ManagedStatic<SubCommand> llvm::cl::TopLevelSubCommand; // A special subcommand that can be used to put an option into all subcommands. @@ -599,15 +697,6 @@ static bool ProvidePositionalOption(Option *Handler, StringRef Arg, int i) { return ProvideOption(Handler, Handler->ArgStr, Arg, 0, nullptr, Dummy); } -// Option predicates... -static inline bool isGrouping(const Option *O) { - return O->getFormattingFlag() == cl::Grouping; -} -static inline bool isPrefixedOrGrouping(const Option *O) { - return isGrouping(O) || O->getFormattingFlag() == cl::Prefix || - O->getFormattingFlag() == cl::AlwaysPrefix; -} - // getOptionPred - Check to see if there are any options that satisfy the // specified predicate with names that are the prefixes in Name. This is // checked by progressively stripping characters off of the name, checking to @@ -617,8 +706,9 @@ static inline bool isPrefixedOrGrouping(const Option *O) { static Option *getOptionPred(StringRef Name, size_t &Length, bool (*Pred)(const Option *), const StringMap<Option *> &OptionsMap) { - StringMap<Option *>::const_iterator OMI = OptionsMap.find(Name); + if (OMI != OptionsMap.end() && !Pred(OMI->getValue())) + OMI = OptionsMap.end(); // Loop while we haven't found an option and Name still has at least two // characters in it (so that the next iteration will not be the empty @@ -626,6 +716,8 @@ static Option *getOptionPred(StringRef Name, size_t &Length, while (OMI == OptionsMap.end() && Name.size() > 1) { Name = Name.substr(0, Name.size() - 1); // Chop off the last character. OMI = OptionsMap.find(Name); + if (OMI != OptionsMap.end() && !Pred(OMI->getValue())) + OMI = OptionsMap.end(); } if (OMI != OptionsMap.end() && Pred(OMI->second)) { @@ -652,40 +744,46 @@ HandlePrefixedOrGroupedOption(StringRef &Arg, StringRef &Value, if (!PGOpt) return nullptr; - // If the option is a prefixed option, then the value is simply the - // rest of the name... so fall through to later processing, by - // setting up the argument name flags and value fields. - if (PGOpt->getFormattingFlag() == cl::Prefix || - PGOpt->getFormattingFlag() == cl::AlwaysPrefix) { - Value = Arg.substr(Length); + do { + StringRef MaybeValue = + (Length < Arg.size()) ? Arg.substr(Length) : StringRef(); Arg = Arg.substr(0, Length); assert(OptionsMap.count(Arg) && OptionsMap.find(Arg)->second == PGOpt); - return PGOpt; - } - // This must be a grouped option... handle them now. Grouping options can't - // have values. - assert(isGrouping(PGOpt) && "Broken getOptionPred!"); + // cl::Prefix options do not preserve '=' when used separately. + // The behavior for them with grouped options should be the same. + if (MaybeValue.empty() || PGOpt->getFormattingFlag() == cl::AlwaysPrefix || + (PGOpt->getFormattingFlag() == cl::Prefix && MaybeValue[0] != '=')) { + Value = MaybeValue; + return PGOpt; + } - do { - // Move current arg name out of Arg into OneArgName. - StringRef OneArgName = Arg.substr(0, Length); - Arg = Arg.substr(Length); - - // Because ValueRequired is an invalid flag for grouped arguments, - // we don't need to pass argc/argv in. - assert(PGOpt->getValueExpectedFlag() != cl::ValueRequired && - "Option can not be cl::Grouping AND cl::ValueRequired!"); + if (MaybeValue[0] == '=') { + Value = MaybeValue.substr(1); + return PGOpt; + } + + // This must be a grouped option. + assert(isGrouping(PGOpt) && "Broken getOptionPred!"); + + // Grouping options inside a group can't have values. + if (PGOpt->getValueExpectedFlag() == cl::ValueRequired) { + ErrorParsing |= PGOpt->error("may not occur within a group!"); + return nullptr; + } + + // Because the value for the option is not required, we don't need to pass + // argc/argv in. int Dummy = 0; - ErrorParsing |= - ProvideOption(PGOpt, OneArgName, StringRef(), 0, nullptr, Dummy); + ErrorParsing |= ProvideOption(PGOpt, Arg, StringRef(), 0, nullptr, Dummy); // Get the next grouping option. + Arg = MaybeValue; PGOpt = getOptionPred(Arg, Length, isGrouping, OptionsMap); - } while (PGOpt && Length != Arg.size()); + } while (PGOpt); - // Return the last option with Arg cut down to just the last one. - return PGOpt; + // We could not find a grouping option in the remainder of Arg. + return nullptr; } static bool RequiresValue(const Option *O) { @@ -869,6 +967,13 @@ void cl::TokenizeWindowsCommandLine(StringRef Src, StringSaver &Saver, // QUOTED state means that it's reading a token quoted by double quotes. if (State == QUOTED) { if (C == '"') { + if (I < (E - 1) && Src[I + 1] == '"') { + // Consecutive double-quotes inside a quoted string implies one + // double-quote. + Token.push_back('"'); + I = I + 1; + continue; + } State = UNQUOTED; continue; } @@ -992,41 +1097,84 @@ static bool ExpandResponseFile(StringRef FName, StringSaver &Saver, bool cl::ExpandResponseFiles(StringSaver &Saver, TokenizerCallback Tokenizer, SmallVectorImpl<const char *> &Argv, bool MarkEOLs, bool RelativeNames) { - unsigned RspFiles = 0; bool AllExpanded = true; + struct ResponseFileRecord { + const char *File; + size_t End; + }; + + // To detect recursive response files, we maintain a stack of files and the + // position of the last argument in the file. This position is updated + // dynamically as we recursively expand files. + SmallVector<ResponseFileRecord, 3> FileStack; + + // Push a dummy entry that represents the initial command line, removing + // the need to check for an empty list. + FileStack.push_back({"", Argv.size()}); // Don't cache Argv.size() because it can change. for (unsigned I = 0; I != Argv.size();) { + while (I == FileStack.back().End) { + // Passing the end of a file's argument list, so we can remove it from the + // stack. + FileStack.pop_back(); + } + const char *Arg = Argv[I]; // Check if it is an EOL marker if (Arg == nullptr) { ++I; continue; } + if (Arg[0] != '@') { ++I; continue; } - // If we have too many response files, leave some unexpanded. This avoids - // crashing on self-referential response files. - if (RspFiles++ > 20) - return false; + const char *FName = Arg + 1; + auto IsEquivalent = [FName](const ResponseFileRecord &RFile) { + return sys::fs::equivalent(RFile.File, FName); + }; + + // Check for recursive response files. + if (std::any_of(FileStack.begin() + 1, FileStack.end(), IsEquivalent)) { + // This file is recursive, so we leave it in the argument stream and + // move on. + AllExpanded = false; + ++I; + continue; + } // Replace this response file argument with the tokenization of its // contents. Nested response files are expanded in subsequent iterations. SmallVector<const char *, 0> ExpandedArgv; - if (!ExpandResponseFile(Arg + 1, Saver, Tokenizer, ExpandedArgv, - MarkEOLs, RelativeNames)) { + if (!ExpandResponseFile(FName, Saver, Tokenizer, ExpandedArgv, MarkEOLs, + RelativeNames)) { // We couldn't read this file, so we leave it in the argument stream and // move on. AllExpanded = false; ++I; continue; } + + for (ResponseFileRecord &Record : FileStack) { + // Increase the end of all active records by the number of newly expanded + // arguments, minus the response file itself. + Record.End += ExpandedArgv.size() - 1; + } + + FileStack.push_back({FName, I + ExpandedArgv.size()}); Argv.erase(Argv.begin() + I); Argv.insert(Argv.begin() + I, ExpandedArgv.begin(), ExpandedArgv.end()); } + + // If successful, the top of the file stack will mark the end of the Argv + // stream. A failure here indicates a bug in the stack popping logic above. + // Note that FileStack may have more than one element at this point because we + // don't have a chance to pop the stack when encountering recursive files at + // the end of the stream, so seeing that doesn't indicate a bug. + assert(FileStack.size() > 0 && Argv.size() == FileStack.back().End); return AllExpanded; } @@ -1071,7 +1219,8 @@ void cl::ParseEnvironmentOptions(const char *progName, const char *envVar, bool cl::ParseCommandLineOptions(int argc, const char *const *argv, StringRef Overview, raw_ostream *Errs, - const char *EnvVar) { + const char *EnvVar, + bool LongOptionsUseDoubleDash) { SmallVector<const char *, 20> NewArgv; BumpPtrAllocator A; StringSaver Saver(A); @@ -1091,7 +1240,7 @@ bool cl::ParseCommandLineOptions(int argc, const char *const *argv, // Parse all options. return GlobalParser->ParseCommandLineOptions(NewArgc, &NewArgv[0], Overview, - Errs); + Errs, LongOptionsUseDoubleDash); } void CommandLineParser::ResetAllOptionOccurrences() { @@ -1106,7 +1255,8 @@ void CommandLineParser::ResetAllOptionOccurrences() { bool CommandLineParser::ParseCommandLineOptions(int argc, const char *const *argv, StringRef Overview, - raw_ostream *Errs) { + raw_ostream *Errs, + bool LongOptionsUseDoubleDash) { assert(hasOptions() && "No options specified!"); // Expand response files. @@ -1152,6 +1302,10 @@ bool CommandLineParser::ParseCommandLineOptions(int argc, auto &SinkOpts = ChosenSubCommand->SinkOpts; auto &OptionsMap = ChosenSubCommand->OptionsMap; + for (auto O: DefaultOptions) { + addOption(O, true); + } + if (ConsumeAfterOpt) { assert(PositionalOpts.size() > 0 && "Cannot specify cl::ConsumeAfter without a positional argument!"); @@ -1212,6 +1366,7 @@ bool CommandLineParser::ParseCommandLineOptions(int argc, std::string NearestHandlerString; StringRef Value; StringRef ArgName = ""; + bool HaveDoubleDash = false; // Check to see if this is a positional argument. This argument is // considered to be positional if it doesn't start with '-', if it is "-" @@ -1249,26 +1404,31 @@ bool CommandLineParser::ParseCommandLineOptions(int argc, // option is another positional argument. If so, treat it as an argument, // otherwise feed it to the eating positional. ArgName = StringRef(argv[i] + 1); - // Eat leading dashes. - while (!ArgName.empty() && ArgName[0] == '-') + // Eat second dash. + if (!ArgName.empty() && ArgName[0] == '-') { + HaveDoubleDash = true; ArgName = ArgName.substr(1); + } - Handler = LookupOption(*ChosenSubCommand, ArgName, Value); + Handler = LookupLongOption(*ChosenSubCommand, ArgName, Value, + LongOptionsUseDoubleDash, HaveDoubleDash); if (!Handler || Handler->getFormattingFlag() != cl::Positional) { ProvidePositionalOption(ActivePositionalArg, StringRef(argv[i]), i); continue; // We are done! } - } else { // We start with a '-', must be an argument. ArgName = StringRef(argv[i] + 1); - // Eat leading dashes. - while (!ArgName.empty() && ArgName[0] == '-') + // Eat second dash. + if (!ArgName.empty() && ArgName[0] == '-') { + HaveDoubleDash = true; ArgName = ArgName.substr(1); + } - Handler = LookupOption(*ChosenSubCommand, ArgName, Value); + Handler = LookupLongOption(*ChosenSubCommand, ArgName, Value, + LongOptionsUseDoubleDash, HaveDoubleDash); // Check to see if this "option" is really a prefixed or grouped argument. - if (!Handler) + if (!Handler && !(LongOptionsUseDoubleDash && HaveDoubleDash)) Handler = HandlePrefixedOrGroupedOption(ArgName, Value, ErrorParsing, OptionsMap); @@ -1282,12 +1442,12 @@ bool CommandLineParser::ParseCommandLineOptions(int argc, if (!Handler) { if (SinkOpts.empty()) { *Errs << ProgramName << ": Unknown command line argument '" << argv[i] - << "'. Try: '" << argv[0] << " -help'\n"; + << "'. Try: '" << argv[0] << " --help'\n"; if (NearestHandler) { // If we know a near match, report it as well. - *Errs << ProgramName << ": Did you mean '-" << NearestHandlerString - << "'?\n"; + *Errs << ProgramName << ": Did you mean '" + << PrintArg(NearestHandlerString) << "'?\n"; } ErrorParsing = true; @@ -1321,14 +1481,14 @@ bool CommandLineParser::ParseCommandLineOptions(int argc, << ": Not enough positional command line arguments specified!\n" << "Must specify at least " << NumPositionalRequired << " positional argument" << (NumPositionalRequired > 1 ? "s" : "") - << ": See: " << argv[0] << " -help\n"; + << ": See: " << argv[0] << " --help\n"; ErrorParsing = true; } else if (!HasUnlimitedPositionals && PositionalVals.size() > PositionalOpts.size()) { *Errs << ProgramName << ": Too many positional arguments specified!\n" << "Can specify at most " << PositionalOpts.size() - << " positional arguments: See: " << argv[0] << " -help\n"; + << " positional arguments: See: " << argv[0] << " --help\n"; ErrorParsing = true; } else if (!ConsumeAfterOpt) { @@ -1441,7 +1601,7 @@ bool Option::error(const Twine &Message, StringRef ArgName, raw_ostream &Errs) { if (ArgName.empty()) Errs << HelpStr; // Be nice for positional arguments else - Errs << GlobalParser->ProgramName << ": for the -" << ArgName; + Errs << GlobalParser->ProgramName << ": for the " << PrintArg(ArgName); Errs << " option: " << Message << "\n"; return true; @@ -1484,12 +1644,16 @@ static StringRef getValueStr(const Option &O, StringRef DefaultMsg) { // // Return the width of the option tag for printing... -size_t alias::getOptionWidth() const { return ArgStr.size() + 6; } +size_t alias::getOptionWidth() const { + return argPlusPrefixesSize(ArgStr); +} void Option::printHelpStr(StringRef HelpStr, size_t Indent, - size_t FirstLineIndentedBy) { + size_t FirstLineIndentedBy) { + assert(Indent >= FirstLineIndentedBy); std::pair<StringRef, StringRef> Split = HelpStr.split('\n'); - outs().indent(Indent - FirstLineIndentedBy) << " - " << Split.first << "\n"; + outs().indent(Indent - FirstLineIndentedBy) + << ArgHelpPrefix << Split.first << "\n"; while (!Split.second.empty()) { Split = Split.second.split('\n'); outs().indent(Indent) << Split.first << "\n"; @@ -1498,8 +1662,8 @@ void Option::printHelpStr(StringRef HelpStr, size_t Indent, // Print out the option for the alias. void alias::printOptionInfo(size_t GlobalWidth) const { - outs() << " -" << ArgStr; - printHelpStr(HelpStr, GlobalWidth, ArgStr.size() + 6); + outs() << PrintArg(ArgStr); + printHelpStr(HelpStr, GlobalWidth, argPlusPrefixesSize(ArgStr)); } //===----------------------------------------------------------------------===// @@ -1511,7 +1675,7 @@ void alias::printOptionInfo(size_t GlobalWidth) const { // Return the width of the option tag for printing... size_t basic_parser_impl::getOptionWidth(const Option &O) const { - size_t Len = O.ArgStr.size(); + size_t Len = argPlusPrefixesSize(O.ArgStr); auto ValName = getValueName(); if (!ValName.empty()) { size_t FormattingLen = 3; @@ -1520,7 +1684,7 @@ size_t basic_parser_impl::getOptionWidth(const Option &O) const { Len += getValueStr(O, ValName).size() + FormattingLen; } - return Len + 6; + return Len; } // printOptionInfo - Print out information about this option. The @@ -1528,7 +1692,7 @@ size_t basic_parser_impl::getOptionWidth(const Option &O) const { // void basic_parser_impl::printOptionInfo(const Option &O, size_t GlobalWidth) const { - outs() << " -" << O.ArgStr; + outs() << PrintArg(O.ArgStr); auto ValName = getValueName(); if (!ValName.empty()) { @@ -1544,7 +1708,7 @@ void basic_parser_impl::printOptionInfo(const Option &O, void basic_parser_impl::printOptionName(const Option &O, size_t GlobalWidth) const { - outs() << " -" << O.ArgStr; + outs() << PrintArg(O.ArgStr); outs().indent(GlobalWidth - O.ArgStr.size()); } @@ -1603,6 +1767,16 @@ bool parser<unsigned>::parse(Option &O, StringRef ArgName, StringRef Arg, return false; } +// parser<unsigned long> implementation +// +bool parser<unsigned long>::parse(Option &O, StringRef ArgName, StringRef Arg, + unsigned long &Value) { + + if (Arg.getAsInteger(0, Value)) + return O.error("'" + Arg + "' value invalid for ulong argument!"); + return false; +} + // parser<unsigned long long> implementation // bool parser<unsigned long long>::parse(Option &O, StringRef ArgName, @@ -1610,7 +1784,7 @@ bool parser<unsigned long long>::parse(Option &O, StringRef ArgName, unsigned long long &Value) { if (Arg.getAsInteger(0, Value)) - return O.error("'" + Arg + "' value invalid for uint argument!"); + return O.error("'" + Arg + "' value invalid for ullong argument!"); return false; } @@ -1652,12 +1826,29 @@ unsigned generic_parser_base::findOption(StringRef Name) { return e; } +static StringRef EqValue = "=<value>"; +static StringRef EmptyOption = "<empty>"; +static StringRef OptionPrefix = " ="; +static size_t OptionPrefixesSize = OptionPrefix.size() + ArgHelpPrefix.size(); + +static bool shouldPrintOption(StringRef Name, StringRef Description, + const Option &O) { + return O.getValueExpectedFlag() != ValueOptional || !Name.empty() || + !Description.empty(); +} + // Return the width of the option tag for printing... size_t generic_parser_base::getOptionWidth(const Option &O) const { if (O.hasArgStr()) { - size_t Size = O.ArgStr.size() + 6; - for (unsigned i = 0, e = getNumOptions(); i != e; ++i) - Size = std::max(Size, getOption(i).size() + 8); + size_t Size = + argPlusPrefixesSize(O.ArgStr) + EqValue.size(); + for (unsigned i = 0, e = getNumOptions(); i != e; ++i) { + StringRef Name = getOption(i); + if (!shouldPrintOption(Name, getDescription(i), O)) + continue; + size_t NameSize = Name.empty() ? EmptyOption.size() : Name.size(); + Size = std::max(Size, NameSize + OptionPrefixesSize); + } return Size; } else { size_t BaseSize = 0; @@ -1673,20 +1864,46 @@ size_t generic_parser_base::getOptionWidth(const Option &O) const { void generic_parser_base::printOptionInfo(const Option &O, size_t GlobalWidth) const { if (O.hasArgStr()) { - outs() << " -" << O.ArgStr; - Option::printHelpStr(O.HelpStr, GlobalWidth, O.ArgStr.size() + 6); + // When the value is optional, first print a line just describing the + // option without values. + if (O.getValueExpectedFlag() == ValueOptional) { + for (unsigned i = 0, e = getNumOptions(); i != e; ++i) { + if (getOption(i).empty()) { + outs() << PrintArg(O.ArgStr); + Option::printHelpStr(O.HelpStr, GlobalWidth, + argPlusPrefixesSize(O.ArgStr)); + break; + } + } + } + outs() << PrintArg(O.ArgStr) << EqValue; + Option::printHelpStr(O.HelpStr, GlobalWidth, + EqValue.size() + + argPlusPrefixesSize(O.ArgStr)); for (unsigned i = 0, e = getNumOptions(); i != e; ++i) { - size_t NumSpaces = GlobalWidth - getOption(i).size() - 8; - outs() << " =" << getOption(i); - outs().indent(NumSpaces) << " - " << getDescription(i) << '\n'; + StringRef OptionName = getOption(i); + StringRef Description = getDescription(i); + if (!shouldPrintOption(OptionName, Description, O)) + continue; + assert(GlobalWidth >= OptionName.size() + OptionPrefixesSize); + size_t NumSpaces = GlobalWidth - OptionName.size() - OptionPrefixesSize; + outs() << OptionPrefix << OptionName; + if (OptionName.empty()) { + outs() << EmptyOption; + assert(NumSpaces >= EmptyOption.size()); + NumSpaces -= EmptyOption.size(); + } + if (!Description.empty()) + outs().indent(NumSpaces) << ArgHelpPrefix << " " << Description; + outs() << '\n'; } } else { if (!O.HelpStr.empty()) outs() << " " << O.HelpStr << '\n'; for (unsigned i = 0, e = getNumOptions(); i != e; ++i) { - auto Option = getOption(i); - outs() << " -" << Option; + StringRef Option = getOption(i); + outs() << " " << PrintArg(Option); Option::printHelpStr(getDescription(i), GlobalWidth, Option.size() + 8); } } @@ -1700,7 +1917,7 @@ static const size_t MaxOptWidth = 8; // arbitrary spacing for printOptionDiff void generic_parser_base::printGenericOptionDiff( const Option &O, const GenericOptionValue &Value, const GenericOptionValue &Default, size_t GlobalWidth) const { - outs() << " -" << O.ArgStr; + outs() << " " << PrintArg(O.ArgStr); outs().indent(GlobalWidth - O.ArgStr.size()); unsigned NumOpts = getNumOptions(); @@ -1750,6 +1967,7 @@ PRINT_OPT_DIFF(bool) PRINT_OPT_DIFF(boolOrDefault) PRINT_OPT_DIFF(int) PRINT_OPT_DIFF(unsigned) +PRINT_OPT_DIFF(unsigned long) PRINT_OPT_DIFF(unsigned long long) PRINT_OPT_DIFF(double) PRINT_OPT_DIFF(float) @@ -1919,7 +2137,7 @@ public: printSubCommands(Subs, MaxSubLen); outs() << "\n"; outs() << " Type \"" << GlobalParser->ProgramName - << " <subcommand> -help\" to get more help on a specific " + << " <subcommand> --help\" to get more help on a specific " "subcommand"; } @@ -1986,9 +2204,11 @@ protected: // options within categories will also be alphabetically sorted. for (size_t I = 0, E = Opts.size(); I != E; ++I) { Option *Opt = Opts[I].second; - assert(CategorizedOptions.count(Opt->Category) > 0 && - "Option has an unregistered category"); - CategorizedOptions[Opt->Category].push_back(Opt); + for (auto &Cat : Opt->Categories) { + assert(CategorizedOptions.count(Cat) > 0 && + "Option has an unregistered category"); + CategorizedOptions[Cat].push_back(Opt); + } } // Now do printing. @@ -1996,7 +2216,7 @@ protected: Category = SortedCategories.begin(), E = SortedCategories.end(); Category != E; ++Category) { - // Hide empty categories for -help, but show for -help-hidden. + // Hide empty categories for --help, but show for --help-hidden. const auto &CategoryOptions = CategorizedOptions[*Category]; bool IsEmptyCategory = CategoryOptions.empty(); if (!ShowHidden && IsEmptyCategory) @@ -2012,7 +2232,7 @@ protected: else outs() << "\n"; - // When using -help-hidden explicitly state if the category has no + // When using --help-hidden explicitly state if the category has no // options associated with it. if (IsEmptyCategory) { outs() << " This option category has no options.\n"; @@ -2062,11 +2282,11 @@ static HelpPrinterWrapper WrappedHiddenPrinter(UncategorizedHiddenPrinter, static cl::OptionCategory GenericCategory("Generic Options"); // Define uncategorized help printers. -// -help-list is hidden by default because if Option categories are being used -// then -help behaves the same as -help-list. +// --help-list is hidden by default because if Option categories are being used +// then --help behaves the same as --help-list. static cl::opt<HelpPrinter, true, parser<bool>> HLOp( "help-list", - cl::desc("Display list of available options (-help-list-hidden for more)"), + cl::desc("Display list of available options (--help-list-hidden for more)"), cl::location(UncategorizedNormalPrinter), cl::Hidden, cl::ValueDisallowed, cl::cat(GenericCategory), cl::sub(*AllSubCommands)); @@ -2080,10 +2300,13 @@ static cl::opt<HelpPrinter, true, parser<bool>> // behaviour at runtime depending on whether one or more Option categories have // been declared. static cl::opt<HelpPrinterWrapper, true, parser<bool>> - HOp("help", cl::desc("Display available options (-help-hidden for more)"), + HOp("help", cl::desc("Display available options (--help-hidden for more)"), cl::location(WrappedNormalPrinter), cl::ValueDisallowed, cl::cat(GenericCategory), cl::sub(*AllSubCommands)); +static cl::alias HOpA("h", cl::desc("Alias for --help"), cl::aliasopt(HOp), + cl::DefaultOption); + static cl::opt<HelpPrinterWrapper, true, parser<bool>> HHOp("help-hidden", cl::desc("Display all available options"), cl::location(WrappedHiddenPrinter), cl::Hidden, cl::ValueDisallowed, @@ -2108,7 +2331,7 @@ void HelpPrinterWrapper::operator=(bool Value) { // registered then it is useful to show the categorized help instead of // uncategorized help. if (GlobalParser->RegisteredOptionCategories.size() > 1) { - // unhide -help-list option so user can have uncategorized output if they + // unhide --help-list option so user can have uncategorized output if they // want it. HLOp.setHiddenFlag(NotHidden); @@ -2242,21 +2465,21 @@ cl::getRegisteredSubcommands() { void cl::HideUnrelatedOptions(cl::OptionCategory &Category, SubCommand &Sub) { for (auto &I : Sub.OptionsMap) { - if (I.second->Category != &Category && - I.second->Category != &GenericCategory) - I.second->setHiddenFlag(cl::ReallyHidden); + for (auto &Cat : I.second->Categories) { + if (Cat != &Category && + Cat != &GenericCategory) + I.second->setHiddenFlag(cl::ReallyHidden); + } } } void cl::HideUnrelatedOptions(ArrayRef<const cl::OptionCategory *> Categories, SubCommand &Sub) { - auto CategoriesBegin = Categories.begin(); - auto CategoriesEnd = Categories.end(); for (auto &I : Sub.OptionsMap) { - if (std::find(CategoriesBegin, CategoriesEnd, I.second->Category) == - CategoriesEnd && - I.second->Category != &GenericCategory) - I.second->setHiddenFlag(cl::ReallyHidden); + for (auto &Cat : I.second->Categories) { + if (find(Categories, Cat) == Categories.end() && Cat != &GenericCategory) + I.second->setHiddenFlag(cl::ReallyHidden); + } } } |