[llvm] r274171 - Resubmit "Update llvm command line parser to support subcommands."
Alex L via llvm-commits
llvm-commits at lists.llvm.org
Mon Oct 10 09:03:42 PDT 2016
On 10 October 2016 at 16:56, Zachary Turner <zturner at google.com> wrote:
> So here, `Sub` refers to the active SubCommand. For example, if you typed
> "llvm-pdbdump raw <options>" then `Sub` will refer to the RawSubcommand.
> There are two special subcommands, `TopLevelSubcommand` and
> `AllSubcommands`. The former becomes the active subcommand when you type
> no options at all. For example, if you were to run `llvm-pdbdump -foo"
> Then the active subcommand would be `TopLevelSubcommand`. Since
> subcommands cannot be nested, `TopLevelSubcommand` is the only subcommand
> from which it is possible for other subcommands to be "children" of. So
> the first part of the conditional is saying "Only print this if the user
> did not explicitly specify any subcommand". The >2 comes from the fact
> that `TopLevelSubcommand` and `AllSubcommands` are builtin subcommands.
> Even if someone does not define any subcommands for their tool, there will
> still be those two. So the second check is equivalent to saying "If the
> user has explicitly defined at least 1 subcommand".
>
Thanks for the explanation, that makes sense. I stumbled across this when I
was investigating why the help didn't print out subcommands when I had less
than 3 sub-commands in a private tool. Given this explanation it has to be
some mistake in my code or my reasoning. I'll double-check my code.
Alex
>
> On Mon, Oct 10, 2016 at 3:44 AM Alex L <arphaman at gmail.com> wrote:
>
>> On 29 June 2016 at 22:48, Zachary Turner via llvm-commits <
>> llvm-commits at lists.llvm.org> wrote:
>>
>> Author: zturner
>> Date: Wed Jun 29 16:48:26 2016
>> New Revision: 274171
>>
>> URL: http://llvm.org/viewvc/llvm-project?rev=274171&view=rev
>> Log:
>> Resubmit "Update llvm command line parser to support subcommands."
>>
>> This fixes an issue where occurrence counts would be unexpectedly
>> reset when parsing different parts of a command line multiple
>> times.
>>
>> **ORIGINAL COMMIT MESSAGE**
>>
>> This allows command line tools to use syntaxes like the following:
>>
>> llvm-foo.exe command1 -o1 -o2
>> llvm-foo.exe command2 -p1 -p2
>>
>> Where command1 and command2 contain completely different sets of
>> valid options. This is backwards compatible with previous uses
>> of llvm cl which did not support subcommands, as any option
>> which specifies no optional subcommand (e.g. all existing
>> code) goes into a special "top level" subcommand that expects
>> dashed options to appear immediately after the program name.
>> For example, code which is subcommand unaware would generate
>> a command line such as the following, where no subcommand
>> is specified:
>>
>> llvm-foo.exe -q1 -q2
>>
>> The top level subcommand can co-exist with actual subcommands,
>> as it is implemented as an actual subcommand which is searched
>> if no explicit subcommand is specified. So llvm-foo.exe as
>> specified above could be written so as to support all three
>> aforementioned command lines simultaneously.
>>
>> There is one additional "special" subcommand called AllSubCommands,
>> which can be used to inject an option into every subcommand.
>> This is useful to support things like help, so that commands
>> such as:
>>
>> llvm-foo.exe --help
>> llvm-foo.exe command1 --help
>> llvm-foo.exe command2 --help
>>
>> All work and display the help for the selected subcommand
>> without having to explicitly go and write code to handle each
>> one separately.
>>
>> This patch is submitted without an example of anything actually
>> using subcommands, but a followup patch will convert the
>> llvm-pdbdump tool to use subcommands.
>>
>> Reviewed By: beanz
>>
>> Modified:
>> llvm/trunk/include/llvm/Support/CommandLine.h
>> llvm/trunk/lib/Support/CommandLine.cpp
>> llvm/trunk/unittests/Support/CommandLineTest.cpp
>> llvm/trunk/unittests/Support/ProgramTest.cpp
>>
>> Modified: llvm/trunk/include/llvm/Support/CommandLine.h
>> URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/include/
>> llvm/Support/CommandLine.h?rev=274171&r1=274170&r2=274171&view=diff
>> ============================================================
>> ==================
>> --- llvm/trunk/include/llvm/Support/CommandLine.h (original)
>> +++ llvm/trunk/include/llvm/Support/CommandLine.h Wed Jun 29 16:48:26
>> 2016
>> @@ -21,10 +21,12 @@
>> #define LLVM_SUPPORT_COMMANDLINE_H
>>
>> #include "llvm/ADT/ArrayRef.h"
>> +#include "llvm/ADT/SmallPtrSet.h"
>> #include "llvm/ADT/SmallVector.h"
>> #include "llvm/ADT/StringMap.h"
>> #include "llvm/ADT/Twine.h"
>> #include "llvm/Support/Compiler.h"
>> +#include "llvm/Support/ManagedStatic.h"
>> #include <cassert>
>> #include <climits>
>> #include <cstdarg>
>> @@ -43,8 +45,9 @@ namespace cl {
>> //===------------------------------------------------------
>> ----------------===//
>> // ParseCommandLineOptions - Command line option processing entry point.
>> //
>> -void ParseCommandLineOptions(int argc, const char *const *argv,
>> - const char *Overview = nullptr);
>> +bool ParseCommandLineOptions(int argc, const char *const *argv,
>> + const char *Overview = nullptr,
>> + bool IgnoreErrors = false);
>>
>> //===------------------------------------------------------
>> ----------------===//
>> // ParseEnvironmentOptions - Environment variable option processing
>> alternate
>> @@ -171,6 +174,45 @@ public:
>> extern OptionCategory GeneralCategory;
>>
>> //===------------------------------------------------------
>> ----------------===//
>> +// SubCommand class
>> +//
>> +class SubCommand {
>> +private:
>> + const char *const Name = nullptr;
>> + const char *const Description = nullptr;
>> +
>> +protected:
>> + void registerSubCommand();
>> + void unregisterSubCommand();
>> +
>> +public:
>> + SubCommand(const char *const Name, const char *const Description =
>> nullptr)
>> + : Name(Name), Description(Description) {
>> + registerSubCommand();
>> + }
>> + SubCommand() {}
>> +
>> + void reset();
>> +
>> + operator bool() const;
>> +
>> + const char *getName() const { return Name; }
>> + const char *getDescription() const { return Description; }
>> +
>> + SmallVector<Option *, 4> PositionalOpts;
>> + SmallVector<Option *, 4> SinkOpts;
>> + StringMap<Option *> OptionsMap;
>> +
>> + Option *ConsumeAfterOpt = nullptr; // The ConsumeAfter option if it
>> exists.
>> +};
>> +
>> +// A special subcommand representing no subcommand
>> +extern ManagedStatic<SubCommand> TopLevelSubCommand;
>> +
>> +// A special subcommand that can be used to put an option into all
>> subcommands.
>> +extern ManagedStatic<SubCommand> AllSubCommands;
>> +
>> +//===------------------------------------------------------
>> ----------------===//
>> // Option Base class
>> //
>> class alias;
>> @@ -209,6 +251,7 @@ public:
>> StringRef HelpStr; // The descriptive text message for -help
>> StringRef ValueStr; // String describing what the value of this option
>> is
>> OptionCategory *Category; // The Category this option belongs to
>> + SmallPtrSet<SubCommand *, 4> Subs; // The subcommands this option
>> belongs to.
>> bool FullyInitialized; // Has addArguemnt been called?
>>
>> inline enum NumOccurrencesFlag getNumOccurrencesFlag() const {
>> @@ -229,6 +272,16 @@ public:
>>
>> // hasArgStr - Return true if the argstr != ""
>> bool hasArgStr() const { return !ArgStr.empty(); }
>> + bool isPositional() const { return getFormattingFlag() ==
>> cl::Positional; }
>> + bool isSink() const { return getMiscFlags() & cl::Sink; }
>> + bool isConsumeAfter() const {
>> + return getNumOccurrencesFlag() == cl::ConsumeAfter;
>> + }
>> + bool isInAllSubCommands() const {
>> + return std::any_of(Subs.begin(), Subs.end(), [](const SubCommand
>> *SC) {
>> + return SC == &*AllSubCommands;
>> + });
>> + }
>>
>> //---------------------------------------------------------
>> ----------------===
>> // Accessor functions set by OptionModifiers
>> @@ -243,6 +296,7 @@ public:
>> void setMiscFlag(enum MiscFlags M) { Misc |= M; }
>> void setPosition(unsigned pos) { Position = pos; }
>> void setCategory(OptionCategory &C) { Category = &C; }
>> + void addSubCommand(SubCommand &S) { Subs.insert(&S); }
>>
>> protected:
>> explicit Option(enum NumOccurrencesFlag OccurrencesFlag,
>> @@ -287,6 +341,7 @@ public:
>>
>> public:
>> inline int getNumOccurrences() const { return NumOccurrences; }
>> + inline void reset() { NumOccurrences = 0; }
>> virtual ~Option() {}
>> };
>>
>> @@ -349,6 +404,14 @@ struct cat {
>> template <class Opt> void apply(Opt &O) const {
>> O.setCategory(Category); }
>> };
>>
>> +// sub - Specify the subcommand that this option belongs to.
>> +struct sub {
>> + SubCommand ⋐
>> + sub(SubCommand &S) : Sub(S) {}
>> +
>> + template <class Opt> void apply(Opt &O) const { O.addSubCommand(Sub); }
>> +};
>> +
>> //===------------------------------------------------------
>> ----------------===//
>> // OptionValue class
>>
>> @@ -1589,6 +1652,7 @@ class alias : public Option {
>> error("cl::alias must have argument name specified!");
>> if (!AliasFor)
>> error("cl::alias must have an cl::aliasopt(option) specified!");
>> + Subs = AliasFor->Subs;
>> addArgument();
>> }
>>
>> @@ -1669,7 +1733,7 @@ void PrintHelpMessage(bool Hidden = fals
>> /// Hopefully this API can be depricated soon. Any situation where
>> options need
>> /// to be modified by tools or libraries should be handled by sane APIs
>> rather
>> /// than just handing around a global list.
>> -StringMap<Option *> &getRegisteredOptions();
>> +StringMap<Option *> &getRegisteredOptions(SubCommand &Sub);
>>
>> //===------------------------------------------------------
>> ----------------===//
>> // Standalone command line processing utilities.
>> @@ -1737,7 +1801,8 @@ bool ExpandResponseFiles(StringSaver &Sa
>> /// Some tools (like clang-format) like to be able to hide all options
>> that are
>> /// not specific to the tool. This function allows a tool to specify a
>> single
>> /// option category to display in the -help output.
>> -void HideUnrelatedOptions(cl::OptionCategory &Category);
>> +void HideUnrelatedOptions(cl::OptionCategory &Category,
>> + SubCommand &Sub = *TopLevelSubCommand);
>>
>> /// \brief Mark all options not part of the categories as
>> cl::ReallyHidden.
>> ///
>> @@ -1746,7 +1811,19 @@ void HideUnrelatedOptions(cl::OptionCate
>> /// Some tools (like clang-format) like to be able to hide all options
>> that are
>> /// not specific to the tool. This function allows a tool to specify a
>> single
>> /// option category to display in the -help output.
>> -void HideUnrelatedOptions(ArrayRef<const cl::OptionCategory *>
>> Categories);
>> +void HideUnrelatedOptions(ArrayRef<const cl::OptionCategory *>
>> Categories,
>> + SubCommand &Sub = *TopLevelSubCommand);
>> +
>> +/// \brief Reset all command line options to a state that looks as if
>> they have
>> +/// never appeared on the command line. This is useful for being able
>> to parse
>> +/// a command line multiple times (especially useful for writing tests).
>> +void ResetAllOptionOccurrences();
>> +
>> +/// \brief Reset the command line parser back to its initial state. This
>> +/// removes
>> +/// all options, categories, and subcommands and returns the parser to a
>> state
>> +/// where no options are supported.
>> +void ResetCommandLineParser();
>>
>> } // End namespace cl
>>
>>
>> Modified: llvm/trunk/lib/Support/CommandLine.cpp
>> URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/
>> Support/CommandLine.cpp?rev=274171&r1=274170&r2=274171&view=diff
>> ============================================================
>> ==================
>> --- llvm/trunk/lib/Support/CommandLine.cpp (original)
>> +++ llvm/trunk/lib/Support/CommandLine.cpp Wed Jun 29 16:48:26 2016
>> @@ -19,6 +19,7 @@
>> #include "llvm/Support/CommandLine.h"
>> #include "llvm-c/Support.h"
>> #include "llvm/ADT/ArrayRef.h"
>> +#include "llvm/ADT/DenseMap.h"
>> #include "llvm/ADT/STLExtras.h"
>> #include "llvm/ADT/SmallPtrSet.h"
>> #include "llvm/ADT/SmallString.h"
>> @@ -94,35 +95,56 @@ public:
>> // This collects additional help to be printed.
>> std::vector<const char *> MoreHelp;
>>
>> - SmallVector<Option *, 4> PositionalOpts;
>> - SmallVector<Option *, 4> SinkOpts;
>> - StringMap<Option *> OptionsMap;
>> -
>> - Option *ConsumeAfterOpt; // The ConsumeAfter option if it exists.
>> -
>> // This collects the different option categories that have been
>> registered.
>> SmallPtrSet<OptionCategory *, 16> RegisteredOptionCategories;
>>
>> - CommandLineParser() : ProgramOverview(nullptr),
>> ConsumeAfterOpt(nullptr) {}
>> + // This collects the different subcommands that have been registered.
>> + SmallPtrSet<SubCommand *, 4> RegisteredSubCommands;
>>
>> - void ParseCommandLineOptions(int argc, const char *const *argv,
>> - const char *Overview);
>> + CommandLineParser() : ProgramOverview(nullptr),
>> ActiveSubCommand(nullptr) {
>> + registerSubCommand(&*TopLevelSubCommand);
>> + registerSubCommand(&*AllSubCommands);
>> + }
>>
>> - void addLiteralOption(Option &Opt, const char *Name) {
>> - if (!Opt.hasArgStr()) {
>> - if (!OptionsMap.insert(std::make_pair(Name, &Opt)).second) {
>> - errs() << ProgramName << ": CommandLine Error: Option '" << Name
>> - << "' registered more than once!\n";
>> - report_fatal_error("inconsistency in registered CommandLine
>> options");
>> + void ResetAllOptionOccurrences();
>> +
>> + bool ParseCommandLineOptions(int argc, const char *const *argv,
>> + const char *Overview, bool IgnoreErrors);
>> +
>> + void addLiteralOption(Option &Opt, SubCommand *SC, const char *Name) {
>> + if (Opt.hasArgStr())
>> + return;
>> + if (!SC->OptionsMap.insert(std::make_pair(Name, &Opt)).second) {
>> + errs() << ProgramName << ": CommandLine Error: Option '" << Name
>> + << "' registered more than once!\n";
>> + report_fatal_error("inconsistency in registered CommandLine
>> options");
>> + }
>> +
>> + // If we're adding this to all sub-commands, add it to the ones that
>> have
>> + // already been registered.
>> + if (SC == &*AllSubCommands) {
>> + for (const auto &Sub : RegisteredSubCommands) {
>> + if (SC == Sub)
>> + continue;
>> + addLiteralOption(Opt, Sub, Name);
>> }
>> }
>> }
>>
>> - void addOption(Option *O) {
>> + void addLiteralOption(Option &Opt, const char *Name) {
>> + if (Opt.Subs.empty())
>> + addLiteralOption(Opt, &*TopLevelSubCommand, Name);
>> + else {
>> + for (auto SC : Opt.Subs)
>> + addLiteralOption(Opt, SC, Name);
>> + }
>> + }
>> +
>> + void addOption(Option *O, SubCommand *SC) {
>> bool HadErrors = false;
>> if (O->hasArgStr()) {
>> // Add argument to the argument map!
>> - if (!OptionsMap.insert(std::make_pair(O->ArgStr, O)).second) {
>> + if (!SC->OptionsMap.insert(std::make_pair(O->ArgStr, O)).second) {
>> errs() << ProgramName << ": CommandLine Error: Option '" <<
>> O->ArgStr
>> << "' registered more than once!\n";
>> HadErrors = true;
>> @@ -131,15 +153,15 @@ public:
>>
>> // Remember information about positional options.
>> if (O->getFormattingFlag() == cl::Positional)
>> - PositionalOpts.push_back(O);
>> + SC->PositionalOpts.push_back(O);
>> else if (O->getMiscFlags() & cl::Sink) // Remember sink options
>> - SinkOpts.push_back(O);
>> + SC->SinkOpts.push_back(O);
>> else if (O->getNumOccurrencesFlag() == cl::ConsumeAfter) {
>> - if (ConsumeAfterOpt) {
>> + if (SC->ConsumeAfterOpt) {
>> O->error("Cannot specify more than one option with
>> cl::ConsumeAfter!");
>> HadErrors = true;
>> }
>> - ConsumeAfterOpt = O;
>> + SC->ConsumeAfterOpt = O;
>> }
>>
>> // Fail hard if there were errors. These are strictly unrecoverable
>> and
>> @@ -148,47 +170,102 @@ public:
>> // linked LLVM distribution.
>> if (HadErrors)
>> report_fatal_error("inconsistency in registered CommandLine
>> options");
>> +
>> + // If we're adding this to all sub-commands, add it to the ones that
>> have
>> + // already been registered.
>> + if (SC == &*AllSubCommands) {
>> + for (const auto &Sub : RegisteredSubCommands) {
>> + if (SC == Sub)
>> + continue;
>> + addOption(O, Sub);
>> + }
>> + }
>> }
>>
>> - void removeOption(Option *O) {
>> + void addOption(Option *O) {
>> + if (O->Subs.empty()) {
>> + addOption(O, &*TopLevelSubCommand);
>> + } else {
>> + for (auto SC : O->Subs)
>> + addOption(O, SC);
>> + }
>> + }
>> +
>> + void removeOption(Option *O, SubCommand *SC) {
>> SmallVector<StringRef, 16> OptionNames;
>> O->getExtraOptionNames(OptionNames);
>> if (O->hasArgStr())
>> OptionNames.push_back(O->ArgStr);
>> +
>> + SubCommand &Sub = *SC;
>> for (auto Name : OptionNames)
>> - OptionsMap.erase(Name);
>> + Sub.OptionsMap.erase(Name);
>>
>> if (O->getFormattingFlag() == cl::Positional)
>> - for (auto Opt = PositionalOpts.begin(); Opt !=
>> PositionalOpts.end();
>> - ++Opt) {
>> + for (auto Opt = Sub.PositionalOpts.begin();
>> + Opt != Sub.PositionalOpts.end(); ++Opt) {
>> if (*Opt == O) {
>> - PositionalOpts.erase(Opt);
>> + Sub.PositionalOpts.erase(Opt);
>> break;
>> }
>> }
>> else if (O->getMiscFlags() & cl::Sink)
>> - for (auto Opt = SinkOpts.begin(); Opt != SinkOpts.end(); ++Opt) {
>> + for (auto Opt = Sub.SinkOpts.begin(); Opt != Sub.SinkOpts.end();
>> ++Opt) {
>> if (*Opt == O) {
>> - SinkOpts.erase(Opt);
>> + Sub.SinkOpts.erase(Opt);
>> break;
>> }
>> }
>> - else if (O == ConsumeAfterOpt)
>> - ConsumeAfterOpt = nullptr;
>> + else if (O == Sub.ConsumeAfterOpt)
>> + Sub.ConsumeAfterOpt = nullptr;
>> }
>>
>> - bool hasOptions() {
>> - return (!OptionsMap.empty() || !PositionalOpts.empty() ||
>> - nullptr != ConsumeAfterOpt);
>> + void removeOption(Option *O) {
>> + if (O->Subs.empty())
>> + removeOption(O, &*TopLevelSubCommand);
>> + else {
>> + if (O->isInAllSubCommands()) {
>> + for (auto SC : RegisteredSubCommands)
>> + removeOption(O, SC);
>> + } else {
>> + for (auto SC : O->Subs)
>> + removeOption(O, SC);
>> + }
>> + }
>> }
>>
>> - void updateArgStr(Option *O, StringRef NewName) {
>> - if (!OptionsMap.insert(std::make_pair(NewName, O)).second) {
>> + bool hasOptions(const SubCommand &Sub) const {
>> + return (!Sub.OptionsMap.empty() || !Sub.PositionalOpts.empty() ||
>> + nullptr != Sub.ConsumeAfterOpt);
>> + }
>> +
>> + bool hasOptions() const {
>> + for (const auto &S : RegisteredSubCommands) {
>> + if (hasOptions(*S))
>> + return true;
>> + }
>> + return false;
>> + }
>> +
>> + SubCommand *getActiveSubCommand() { return ActiveSubCommand; }
>> +
>> + void updateArgStr(Option *O, StringRef NewName, SubCommand *SC) {
>> + SubCommand &Sub = *SC;
>> + if (!Sub.OptionsMap.insert(std::make_pair(NewName, O)).second) {
>> errs() << ProgramName << ": CommandLine Error: Option '" <<
>> O->ArgStr
>> << "' registered more than once!\n";
>> report_fatal_error("inconsistency in registered CommandLine
>> options");
>> }
>> - OptionsMap.erase(O->ArgStr);
>> + Sub.OptionsMap.erase(O->ArgStr);
>> + }
>> +
>> + void updateArgStr(Option *O, StringRef NewName) {
>> + if (O->Subs.empty())
>> + updateArgStr(O, NewName, &*TopLevelSubCommand);
>> + else {
>> + for (auto SC : O->Subs)
>> + updateArgStr(O, NewName, SC);
>> + }
>> }
>>
>> void printOptionValues();
>> @@ -203,8 +280,55 @@ public:
>> RegisteredOptionCategories.insert(cat);
>> }
>>
>> + void registerSubCommand(SubCommand *sub) {
>> + assert(count_if(RegisteredSubCommands,
>> + [sub](const SubCommand *Sub) {
>> + return (sub->getName() != nullptr) &&
>> + (Sub->getName() == sub->getName());
>> + }) == 0 &&
>> + "Duplicate subcommands");
>> + RegisteredSubCommands.insert(sub);
>> +
>> + // For all options that have been registered for all subcommands,
>> add the
>> + // option to this subcommand now.
>> + if (sub != &*AllSubCommands) {
>> + for (auto &E : AllSubCommands->OptionsMap) {
>> + Option *O = E.second;
>> + if ((O->isPositional() || O->isSink() || O->isConsumeAfter()) ||
>> + O->hasArgStr())
>> + addOption(O, sub);
>> + else
>> + addLiteralOption(*O, sub, E.first().str().c_str());
>> + }
>> + }
>> + }
>> +
>> + void unregisterSubCommand(SubCommand *sub) {
>> + RegisteredSubCommands.erase(sub);
>> + }
>> +
>> + void reset() {
>> + ActiveSubCommand = nullptr;
>> + ProgramName.clear();
>> + ProgramOverview = nullptr;
>> +
>> + MoreHelp.clear();
>> + RegisteredOptionCategories.clear();
>> +
>> + ResetAllOptionOccurrences();
>> + RegisteredSubCommands.clear();
>> +
>> + TopLevelSubCommand->reset();
>> + AllSubCommands->reset();
>> + registerSubCommand(&*TopLevelSubCommand);
>> + registerSubCommand(&*AllSubCommands);
>> + }
>> +
>> private:
>> - Option *LookupOption(StringRef &Arg, StringRef &Value);
>> + SubCommand *ActiveSubCommand;
>> +
>> + Option *LookupOption(SubCommand &Sub, StringRef &Arg, StringRef
>> &Value);
>> + SubCommand *LookupSubCommand(const char *Name);
>> };
>>
>> } // namespace
>> @@ -239,6 +363,32 @@ void OptionCategory::registerCategory()
>> GlobalParser->registerCategory(this);
>> }
>>
>> +// A special subcommand representing no subcommand
>> +ManagedStatic<SubCommand> llvm::cl::TopLevelSubCommand;
>> +
>> +// A special subcommand that can be used to put an option into all
>> subcommands.
>> +ManagedStatic<SubCommand> llvm::cl::AllSubCommands;
>> +
>> +void SubCommand::registerSubCommand() {
>> + GlobalParser->registerSubCommand(this);
>> +}
>> +
>> +void SubCommand::unregisterSubCommand() {
>> + GlobalParser->unregisterSubCommand(this);
>> +}
>> +
>> +void SubCommand::reset() {
>> + PositionalOpts.clear();
>> + SinkOpts.clear();
>> + OptionsMap.clear();
>> +
>> + ConsumeAfterOpt = nullptr;
>> +}
>> +
>> +SubCommand::operator bool() const {
>> + return (GlobalParser->getActiveSubCommand() == this);
>> +}
>> +
>> //===------------------------------------------------------
>> ----------------===//
>> // Basic, shared command line option processing machinery.
>> //
>> @@ -246,25 +396,29 @@ void OptionCategory::registerCategory()
>> /// LookupOption - Lookup the option specified by the specified option
>> on the
>> /// command line. If there is a value specified (after an equal sign)
>> return
>> /// that as well. This assumes that leading dashes have already been
>> stripped.
>> -Option *CommandLineParser::LookupOption(StringRef &Arg, StringRef
>> &Value) {
>> +Option *CommandLineParser::LookupOption(SubCommand &Sub, StringRef &Arg,
>> + StringRef &Value) {
>> // Reject all dashes.
>> if (Arg.empty())
>> return nullptr;
>> + assert(&Sub != &*AllSubCommands);
>>
>> size_t EqualPos = Arg.find('=');
>>
>> // If we have an equals sign, remember the value.
>> if (EqualPos == StringRef::npos) {
>> // Look up the option.
>> - StringMap<Option *>::const_iterator I = OptionsMap.find(Arg);
>> - return I != OptionsMap.end() ? I->second : nullptr;
>> + auto I = Sub.OptionsMap.find(Arg);
>> + if (I == Sub.OptionsMap.end())
>> + return nullptr;
>> +
>> + return I != Sub.OptionsMap.end() ? I->second : nullptr;
>> }
>>
>> // If the argument before the = is a valid option name, we match. If
>> not,
>> // return Arg unmolested.
>> - StringMap<Option *>::const_iterator I =
>> - OptionsMap.find(Arg.substr(0, EqualPos));
>> - if (I == OptionsMap.end())
>> + auto I = Sub.OptionsMap.find(Arg.substr(0, EqualPos));
>> + if (I == Sub.OptionsMap.end())
>> return nullptr;
>>
>> Value = Arg.substr(EqualPos + 1);
>> @@ -272,6 +426,21 @@ Option *CommandLineParser::LookupOption(
>> return I->second;
>> }
>>
>> +SubCommand *CommandLineParser::LookupSubCommand(const char *Name) {
>> + if (Name == nullptr)
>> + return &*TopLevelSubCommand;
>> + for (auto S : RegisteredSubCommands) {
>> + if (S == &*AllSubCommands)
>> + continue;
>> + if (S->getName() == nullptr)
>> + continue;
>> +
>> + if (StringRef(S->getName()) == StringRef(Name))
>> + return S;
>> + }
>> + return &*TopLevelSubCommand;
>> +}
>> +
>> /// LookupNearestOption - Lookup the closest match to the option
>> specified by
>> /// the specified option on the command line. If there is a value
>> specified
>> /// (after an equal sign) return that as well. This assumes that
>> leading dashes
>> @@ -820,14 +989,25 @@ void cl::ParseEnvironmentOptions(const c
>> ParseCommandLineOptions(newArgc, &newArgv[0], Overview);
>> }
>>
>> -void cl::ParseCommandLineOptions(int argc, const char *const *argv,
>> - const char *Overview) {
>> - GlobalParser->ParseCommandLineOptions(argc, argv, Overview);
>> +bool cl::ParseCommandLineOptions(int argc, const char *const *argv,
>> + const char *Overview, bool
>> IgnoreErrors) {
>> + return GlobalParser->ParseCommandLineOptions(argc, argv, Overview,
>> + IgnoreErrors);
>> }
>>
>> -void CommandLineParser::ParseCommandLineOptions(int argc,
>> +void CommandLineParser::ResetAllOptionOccurrences() {
>> + // So that we can parse different command lines multiple times in
>> succession
>> + // we reset all option values to look like they have never been seen
>> before.
>> + for (auto SC : RegisteredSubCommands) {
>> + for (auto &O : SC->OptionsMap)
>> + O.second->reset();
>> + }
>> +}
>> +
>> +bool CommandLineParser::ParseCommandLineOptions(int argc,
>> const char *const *argv,
>> - const char *Overview) {
>> + const char *Overview,
>> + bool IgnoreErrors) {
>> assert(hasOptions() && "No options specified!");
>>
>> // Expand response files.
>> @@ -850,6 +1030,23 @@ void CommandLineParser::ParseCommandLine
>> // Determine whether or not there are an unlimited number of
>> positionals
>> bool HasUnlimitedPositionals = false;
>>
>> + int FirstArg = 1;
>> + SubCommand *ChosenSubCommand = &*TopLevelSubCommand;
>> + if (argc >= 2 && argv[FirstArg][0] != '-') {
>> + // If the first argument specifies a valid subcommand, start
>> processing
>> + // options from the second argument.
>> + ChosenSubCommand = LookupSubCommand(argv[FirstArg]);
>> + if (ChosenSubCommand != &*TopLevelSubCommand)
>> + FirstArg = 2;
>> + }
>> + GlobalParser->ActiveSubCommand = ChosenSubCommand;
>> +
>> + assert(ChosenSubCommand);
>> + auto &ConsumeAfterOpt = ChosenSubCommand->ConsumeAfterOpt;
>> + auto &PositionalOpts = ChosenSubCommand->PositionalOpts;
>> + auto &SinkOpts = ChosenSubCommand->SinkOpts;
>> + auto &OptionsMap = ChosenSubCommand->OptionsMap;
>> +
>> if (ConsumeAfterOpt) {
>> assert(PositionalOpts.size() > 0 &&
>> "Cannot specify cl::ConsumeAfter without a positional
>> argument!");
>> @@ -865,23 +1062,28 @@ void CommandLineParser::ParseCommandLine
>> else if (ConsumeAfterOpt) {
>> // ConsumeAfter cannot be combined with "optional" positional
>> options
>> // unless there is only one positional argument...
>> - if (PositionalOpts.size() > 1)
>> - ErrorParsing |= Opt->error(
>> - "error - this positional option will never be matched, "
>> - "because it does not Require a value, and a "
>> - "cl::ConsumeAfter option is active!");
>> + if (PositionalOpts.size() > 1) {
>> + if (!IgnoreErrors)
>> + Opt->error("error - this positional option will never be
>> matched, "
>> + "because it does not Require a value, and a "
>> + "cl::ConsumeAfter option is active!");
>> + ErrorParsing = true;
>> + }
>> } else if (UnboundedFound && !Opt->hasArgStr()) {
>> // This option does not "require" a value... Make sure this
>> option is
>> // not specified after an option that eats all extra arguments,
>> or this
>> // one will never get any!
>> //
>> - ErrorParsing |= Opt->error("error - option can never match,
>> because "
>> - "another positional argument will
>> match an "
>> - "unbounded number of values, and this
>> option"
>> - " does not require a value!");
>> - errs() << ProgramName << ": CommandLine Error: Option '" <<
>> Opt->ArgStr
>> - << "' is all messed up!\n";
>> - errs() << PositionalOpts.size();
>> + if (!IgnoreErrors) {
>> + Opt->error("error - option can never match, because "
>> + "another positional argument will match an "
>> + "unbounded number of values, and this option"
>> + " does not require a value!");
>> + errs() << ProgramName << ": CommandLine Error: Option '"
>> + << Opt->ArgStr << "' is all messed up!\n";
>> + errs() << PositionalOpts.size();
>> + }
>> + ErrorParsing = true;
>> }
>> UnboundedFound |= EatsUnboundedNumberOfValues(Opt);
>> }
>> @@ -900,7 +1102,7 @@ void CommandLineParser::ParseCommandLine
>>
>> // Loop over all of the arguments... processing them.
>> bool DashDashFound = false; // Have we read '--'?
>> - for (int i = 1; i < argc; ++i) {
>> + for (int i = FirstArg; i < argc; ++i) {
>> Option *Handler = nullptr;
>> Option *NearestHandler = nullptr;
>> std::string NearestHandlerString;
>> @@ -947,7 +1149,7 @@ void CommandLineParser::ParseCommandLine
>> while (!ArgName.empty() && ArgName[0] == '-')
>> ArgName = ArgName.substr(1);
>>
>> - Handler = LookupOption(ArgName, Value);
>> + Handler = LookupOption(*ChosenSubCommand, ArgName, Value);
>> if (!Handler || Handler->getFormattingFlag() != cl::Positional) {
>> ProvidePositionalOption(ActivePositionalArg, argv[i], i);
>> continue; // We are done!
>> @@ -959,7 +1161,7 @@ void CommandLineParser::ParseCommandLine
>> while (!ArgName.empty() && ArgName[0] == '-')
>> ArgName = ArgName.substr(1);
>>
>> - Handler = LookupOption(ArgName, Value);
>> + Handler = LookupOption(*ChosenSubCommand, ArgName, Value);
>>
>> // Check to see if this "option" is really a prefixed or grouped
>> argument.
>> if (!Handler)
>> @@ -975,13 +1177,15 @@ void CommandLineParser::ParseCommandLine
>>
>> if (!Handler) {
>> if (SinkOpts.empty()) {
>> - errs() << ProgramName << ": Unknown command line argument '" <<
>> argv[i]
>> - << "'. Try: '" << argv[0] << " -help'\n";
>> -
>> - if (NearestHandler) {
>> - // If we know a near match, report it as well.
>> - errs() << ProgramName << ": Did you mean '-" <<
>> NearestHandlerString
>> - << "'?\n";
>> + if (!IgnoreErrors) {
>> + errs() << ProgramName << ": Unknown command line argument '"
>> + << argv[i] << "'. Try: '" << argv[0] << " -help'\n";
>> +
>> + if (NearestHandler) {
>> + // If we know a near match, report it as well.
>> + errs() << ProgramName << ": Did you mean '-" <<
>> NearestHandlerString
>> + << "'?\n";
>> + }
>> }
>>
>> ErrorParsing = true;
>> @@ -1004,17 +1208,21 @@ void CommandLineParser::ParseCommandLine
>>
>> // Check and handle positional arguments now...
>> if (NumPositionalRequired > PositionalVals.size()) {
>> - errs() << ProgramName
>> - << ": Not enough positional command line arguments
>> specified!\n"
>> - << "Must specify at least " << NumPositionalRequired
>> - << " positional arguments: See: " << argv[0] << " -help\n";
>> + if (!IgnoreErrors) {
>> + errs() << ProgramName
>> + << ": Not enough positional command line arguments
>> specified!\n"
>> + << "Must specify at least " << NumPositionalRequired
>> + << " positional arguments: 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";
>> + if (!IgnoreErrors) {
>> + errs() << ProgramName << ": Too many positional arguments
>> specified!\n"
>> + << "Can specify at most " << PositionalOpts.size()
>> + << " positional arguments: See: " << argv[0] << " -help\n";
>> + }
>> ErrorParsing = true;
>>
>> } else if (!ConsumeAfterOpt) {
>> @@ -1109,8 +1317,12 @@ void CommandLineParser::ParseCommandLine
>> MoreHelp.clear();
>>
>> // If we had an error processing our arguments, don't let the program
>> execute
>> - if (ErrorParsing)
>> - exit(1);
>> + if (ErrorParsing) {
>> + if (!IgnoreErrors)
>> + exit(1);
>> + return false;
>> + }
>> + return true;
>> }
>>
>> //===------------------------------------------------------
>> ----------------===//
>> @@ -1460,6 +1672,11 @@ static int OptNameCompare(const std::pai
>> return strcmp(LHS->first, RHS->first);
>> }
>>
>> +static int SubNameCompare(const std::pair<const char *, SubCommand *>
>> *LHS,
>> + const std::pair<const char *, SubCommand *>
>> *RHS) {
>> + return strcmp(LHS->first, RHS->first);
>> +}
>> +
>> // Copy Options into a vector so we can sort them as we like.
>> static void sortOpts(StringMap<Option *> &OptMap,
>> SmallVectorImpl<std::pair<const char *, Option *>>
>> &Opts,
>> @@ -1488,6 +1705,17 @@ static void sortOpts(StringMap<Option *>
>> array_pod_sort(Opts.begin(), Opts.end(), OptNameCompare);
>> }
>>
>> +static void
>> +sortSubCommands(const SmallPtrSetImpl<SubCommand *> &SubMap,
>> + SmallVectorImpl<std::pair<const char *, SubCommand *>>
>> &Subs) {
>> + for (const auto &S : SubMap) {
>> + if (S->getName() == nullptr)
>> + continue;
>> + Subs.push_back(std::make_pair(S->getName(), S));
>> + }
>> + array_pod_sort(Subs.begin(), Subs.end(), SubNameCompare);
>> +}
>> +
>> namespace {
>>
>> class HelpPrinter {
>> @@ -1495,12 +1723,25 @@ protected:
>> const bool ShowHidden;
>> typedef SmallVector<std::pair<const char *, Option *>, 128>
>> StrOptionPairVector;
>> + typedef SmallVector<std::pair<const char *, SubCommand *>, 128>
>> + StrSubCommandPairVector;
>> // Print the options. Opts is assumed to be alphabetically sorted.
>> virtual void printOptions(StrOptionPairVector &Opts, size_t
>> MaxArgLen) {
>> for (size_t i = 0, e = Opts.size(); i != e; ++i)
>> Opts[i].second->printOptionInfo(MaxArgLen);
>> }
>>
>> + void printSubCommands(StrSubCommandPairVector &Subs, size_t
>> MaxSubLen) {
>> + for (const auto &S : Subs) {
>> + outs() << " " << S.first;
>> + if (S.second->getDescription()) {
>> + outs().indent(MaxSubLen - strlen(S.first));
>> + outs() << " - " << S.second->getDescription();
>> + }
>> + outs() << "\n";
>> + }
>> + }
>> +
>> public:
>> explicit HelpPrinter(bool showHidden) : ShowHidden(showHidden) {}
>> virtual ~HelpPrinter() {}
>> @@ -1510,23 +1751,56 @@ public:
>> if (!Value)
>> return;
>>
>> + SubCommand *Sub = GlobalParser->getActiveSubCommand();
>> + auto &OptionsMap = Sub->OptionsMap;
>> + auto &PositionalOpts = Sub->PositionalOpts;
>> + auto &ConsumeAfterOpt = Sub->ConsumeAfterOpt;
>> +
>> StrOptionPairVector Opts;
>> - sortOpts(GlobalParser->OptionsMap, Opts, ShowHidden);
>> + sortOpts(OptionsMap, Opts, ShowHidden);
>> +
>> + StrSubCommandPairVector Subs;
>> + sortSubCommands(GlobalParser->RegisteredSubCommands, Subs);
>>
>> if (GlobalParser->ProgramOverview)
>> outs() << "OVERVIEW: " << GlobalParser->ProgramOverview << "\n";
>>
>> - outs() << "USAGE: " << GlobalParser->ProgramName << " [options]";
>> + if (Sub == &*TopLevelSubCommand)
>> + outs() << "USAGE: " << GlobalParser->ProgramName
>> + << " [subcommand] [options]";
>> + else {
>> + if (Sub->getDescription() != nullptr) {
>> + outs() << "SUBCOMMAND '" << Sub->getName()
>> + << "': " << Sub->getDescription() << "\n\n";
>> + }
>> + outs() << "USAGE: " << GlobalParser->ProgramName << " " <<
>> Sub->getName()
>> + << " [options]";
>> + }
>>
>> - for (auto Opt : GlobalParser->PositionalOpts) {
>> + for (auto Opt : PositionalOpts) {
>> if (Opt->hasArgStr())
>> outs() << " --" << Opt->ArgStr;
>> outs() << " " << Opt->HelpStr;
>> }
>>
>> // Print the consume after option info if it exists...
>> - if (GlobalParser->ConsumeAfterOpt)
>> - outs() << " " << GlobalParser->ConsumeAfterOpt->HelpStr;
>> + if (ConsumeAfterOpt)
>> + outs() << " " << ConsumeAfterOpt->HelpStr;
>> +
>> + if (Sub == &*TopLevelSubCommand && Subs.size() > 2) {
>>
>>
>> I'm sorry if I missed something, but I looked at the review for this
>> commit, and couldn't find an answer to one question that I have, so I hope
>> that you can help me:
>>
>> Is the > 2 intentional here? I think because of this check subcommands
>> don't show up in help unless there are 3 of them. It seems like a bug to
>> me, since it's valid to have a tool with just 2 subcommands.
>>
>> Thanks,
>> Alex
>>
>>
>> + // Compute the maximum subcommand length...
>> + size_t MaxSubLen = 0;
>> + for (size_t i = 0, e = Subs.size(); i != e; ++i)
>> + MaxSubLen = std::max(MaxSubLen, strlen(Subs[i].first));
>> +
>> + outs() << "\n\n";
>> + outs() << "SUBCOMMANDS:\n\n";
>> + printSubCommands(Subs, MaxSubLen);
>> + outs() << "\n";
>> + outs() << " Type \"" << GlobalParser->ProgramName
>> + << " <subcommand> -help\" to get more help on a specific "
>> + "subcommand";
>> + }
>>
>> outs() << "\n\n";
>>
>> @@ -1675,12 +1949,13 @@ static cl::opt<HelpPrinter, true, parser
>> "help-list",
>> cl::desc("Display list of available options (-help-list-hidden for
>> more)"),
>> cl::location(UncategorizedNormalPrinter), cl::Hidden,
>> cl::ValueDisallowed,
>> - cl::cat(GenericCategory));
>> + cl::cat(GenericCategory), cl::sub(*AllSubCommands));
>>
>> static cl::opt<HelpPrinter, true, parser<bool>>
>> HLHOp("help-list-hidden", cl::desc("Display list of all available
>> options"),
>> cl::location(UncategorizedHiddenPrinter), cl::Hidden,
>> - cl::ValueDisallowed, cl::cat(GenericCategory));
>> + cl::ValueDisallowed, cl::cat(GenericCategory),
>> + cl::sub(*AllSubCommands));
>>
>> // Define uncategorized/categorized help printers. These printers change
>> their
>> // behaviour at runtime depending on whether one or more Option
>> categories have
>> @@ -1688,22 +1963,23 @@ static cl::opt<HelpPrinter, true, parser
>> static cl::opt<HelpPrinterWrapper, true, parser<bool>>
>> HOp("help", cl::desc("Display available options (-help-hidden for
>> more)"),
>> cl::location(WrappedNormalPrinter), cl::ValueDisallowed,
>> - cl::cat(GenericCategory));
>> + cl::cat(GenericCategory), cl::sub(*AllSubCommands));
>>
>> static cl::opt<HelpPrinterWrapper, true, parser<bool>>
>> HHOp("help-hidden", cl::desc("Display all available options"),
>> cl::location(WrappedHiddenPrinter), cl::Hidden,
>> cl::ValueDisallowed,
>> - cl::cat(GenericCategory));
>> + cl::cat(GenericCategory), cl::sub(*AllSubCommands));
>>
>> static cl::opt<bool> PrintOptions(
>> "print-options",
>> cl::desc("Print non-default options after command line parsing"),
>> - cl::Hidden, cl::init(false), cl::cat(GenericCategory));
>> + cl::Hidden, cl::init(false), cl::cat(GenericCategory),
>> + cl::sub(*AllSubCommands));
>>
>> static cl::opt<bool> PrintAllOptions(
>> "print-all-options",
>> cl::desc("Print all option values after command line parsing"),
>> cl::Hidden,
>> - cl::init(false), cl::cat(GenericCategory));
>> + cl::init(false), cl::cat(GenericCategory), cl::sub(*AllSubCommands));
>>
>> void HelpPrinterWrapper::operator=(bool Value) {
>> if (!Value)
>> @@ -1730,7 +2006,7 @@ void CommandLineParser::printOptionValue
>> return;
>>
>> SmallVector<std::pair<const char *, Option *>, 128> Opts;
>> - sortOpts(OptionsMap, Opts, /*ShowHidden*/ true);
>> + sortOpts(ActiveSubCommand->OptionsMap, Opts, /*ShowHidden*/ true);
>>
>> // Compute the maximum argument length...
>> size_t MaxArgLen = 0;
>> @@ -1839,22 +2115,26 @@ void cl::AddExtraVersionPrinter(void (*f
>> ExtraVersionPrinters->push_back(func);
>> }
>>
>> -StringMap<Option *> &cl::getRegisteredOptions() {
>> - return GlobalParser->OptionsMap;
>> +StringMap<Option *> &cl::getRegisteredOptions(SubCommand &Sub) {
>> + auto &Subs = GlobalParser->RegisteredSubCommands;
>> + (void)Subs;
>> + assert(std::find(Subs.begin(), Subs.end(), &Sub) != Subs.end());
>> + return Sub.OptionsMap;
>> }
>>
>> -void cl::HideUnrelatedOptions(cl::OptionCategory &Category) {
>> - for (auto &I : GlobalParser->OptionsMap) {
>> +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);
>> }
>> }
>>
>> -void cl::HideUnrelatedOptions(ArrayRef<const cl::OptionCategory *>
>> Categories) {
>> +void cl::HideUnrelatedOptions(ArrayRef<const cl::OptionCategory *>
>> Categories,
>> + SubCommand &Sub) {
>> auto CategoriesBegin = Categories.begin();
>> auto CategoriesEnd = Categories.end();
>> - for (auto &I : GlobalParser->OptionsMap) {
>> + for (auto &I : Sub.OptionsMap) {
>> if (std::find(CategoriesBegin, CategoriesEnd, I.second->Category) ==
>> CategoriesEnd &&
>> I.second->Category != &GenericCategory)
>> @@ -1862,7 +2142,12 @@ void cl::HideUnrelatedOptions(ArrayRef<c
>> }
>> }
>>
>> +void cl::ResetCommandLineParser() { GlobalParser->reset(); }
>> +void cl::ResetAllOptionOccurrences() {
>> + GlobalParser->ResetAllOptionOccurrences();
>> +}
>> +
>> void LLVMParseCommandLineOptions(int argc, const char *const *argv,
>> const char *Overview) {
>> - llvm::cl::ParseCommandLineOptions(argc, argv, Overview);
>> + llvm::cl::ParseCommandLineOptions(argc, argv, Overview, true);
>> }
>>
>> Modified: llvm/trunk/unittests/Support/CommandLineTest.cpp
>> URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/unittests/
>> Support/CommandLineTest.cpp?rev=274171&r1=274170&r2=274171&view=diff
>> ============================================================
>> ==================
>> --- llvm/trunk/unittests/Support/CommandLineTest.cpp (original)
>> +++ llvm/trunk/unittests/Support/CommandLineTest.cpp Wed Jun 29 16:48:26
>> 2016
>> @@ -67,6 +67,22 @@ public:
>> : Base(M0, M1, M2, M3) {}
>>
>> ~StackOption() override { this->removeArgument(); }
>> +
>> + template <class DT> StackOption<T> &operator=(const DT &V) {
>> + this->setValue(V);
>> + return *this;
>> + }
>> +};
>> +
>> +class StackSubCommand : public cl::SubCommand {
>> +public:
>> + StackSubCommand(const char *const Name,
>> + const char *const Description = nullptr)
>> + : SubCommand(Name, Description) {}
>> +
>> + StackSubCommand() : SubCommand() {}
>> +
>> + ~StackSubCommand() { unregisterSubCommand(); }
>> };
>>
>>
>> @@ -78,7 +94,8 @@ TEST(CommandLineTest, ModifyExisitingOpt
>> const char ArgString[] = "new-test-option";
>> const char ValueString[] = "Integer";
>>
>> - StringMap<cl::Option *> &Map = cl::getRegisteredOptions();
>> + StringMap<cl::Option *> &Map =
>> + cl::getRegisteredOptions(*cl::TopLevelSubCommand);
>>
>> ASSERT_TRUE(Map.count("test-option") == 1) <<
>> "Could not find option in map.";
>> @@ -237,7 +254,8 @@ TEST(CommandLineTest, HideUnrelatedOptio
>> ASSERT_EQ(cl::NotHidden, TestOption2.getOptionHiddenFlag())
>> << "Hid extra option that should be visable.";
>>
>> - StringMap<cl::Option *> &Map = cl::getRegisteredOptions();
>> + StringMap<cl::Option *> &Map =
>> + cl::getRegisteredOptions(*cl::TopLevelSubCommand);
>> ASSERT_EQ(cl::NotHidden, Map["help"]->getOptionHiddenFlag())
>> << "Hid default option that should be visable.";
>> }
>> @@ -261,9 +279,201 @@ TEST(CommandLineTest, HideUnrelatedOptio
>> ASSERT_EQ(cl::NotHidden, TestOption3.getOptionHiddenFlag())
>> << "Hid extra option that should be visable.";
>>
>> - StringMap<cl::Option *> &Map = cl::getRegisteredOptions();
>> + StringMap<cl::Option *> &Map =
>> + cl::getRegisteredOptions(*cl::TopLevelSubCommand);
>> ASSERT_EQ(cl::NotHidden, Map["help"]->getOptionHiddenFlag())
>> << "Hid default option that should be visable.";
>> }
>>
>> +TEST(CommandLineTest, SetValueInSubcategories) {
>> + cl::ResetCommandLineParser();
>> +
>> + StackSubCommand SC1("sc1", "First subcommand");
>> + StackSubCommand SC2("sc2", "Second subcommand");
>> +
>> + StackOption<bool> TopLevelOpt("top-level", cl::init(false));
>> + StackOption<bool> SC1Opt("sc1", cl::sub(SC1), cl::init(false));
>> + StackOption<bool> SC2Opt("sc2", cl::sub(SC2), cl::init(false));
>> +
>> + EXPECT_FALSE(TopLevelOpt);
>> + EXPECT_FALSE(SC1Opt);
>> + EXPECT_FALSE(SC2Opt);
>> + const char *args[] = {"prog", "-top-level"};
>> + EXPECT_TRUE(cl::ParseCommandLineOptions(2, args, nullptr, true));
>> + EXPECT_TRUE(TopLevelOpt);
>> + EXPECT_FALSE(SC1Opt);
>> + EXPECT_FALSE(SC2Opt);
>> +
>> + TopLevelOpt = false;
>> +
>> + cl::ResetAllOptionOccurrences();
>> + EXPECT_FALSE(TopLevelOpt);
>> + EXPECT_FALSE(SC1Opt);
>> + EXPECT_FALSE(SC2Opt);
>> + const char *args2[] = {"prog", "sc1", "-sc1"};
>> + EXPECT_TRUE(cl::ParseCommandLineOptions(3, args2, nullptr, true));
>> + EXPECT_FALSE(TopLevelOpt);
>> + EXPECT_TRUE(SC1Opt);
>> + EXPECT_FALSE(SC2Opt);
>> +
>> + SC1Opt = false;
>> +
>> + cl::ResetAllOptionOccurrences();
>> + EXPECT_FALSE(TopLevelOpt);
>> + EXPECT_FALSE(SC1Opt);
>> + EXPECT_FALSE(SC2Opt);
>> + const char *args3[] = {"prog", "sc2", "-sc2"};
>> + EXPECT_TRUE(cl::ParseCommandLineOptions(3, args3, nullptr, true));
>> + EXPECT_FALSE(TopLevelOpt);
>> + EXPECT_FALSE(SC1Opt);
>> + EXPECT_TRUE(SC2Opt);
>> +}
>> +
>> +TEST(CommandLineTest, LookupFailsInWrongSubCommand) {
>> + cl::ResetCommandLineParser();
>> +
>> + StackSubCommand SC1("sc1", "First subcommand");
>> + StackSubCommand SC2("sc2", "Second subcommand");
>> +
>> + StackOption<bool> SC1Opt("sc1", cl::sub(SC1), cl::init(false));
>> + StackOption<bool> SC2Opt("sc2", cl::sub(SC2), cl::init(false));
>> +
>> + const char *args[] = {"prog", "sc1", "-sc2"};
>> + EXPECT_FALSE(cl::ParseCommandLineOptions(3, args, nullptr, true));
>> +}
>> +
>> +TEST(CommandLineTest, AddToAllSubCommands) {
>> + cl::ResetCommandLineParser();
>> +
>> + StackSubCommand SC1("sc1", "First subcommand");
>> + StackOption<bool> AllOpt("everywhere", cl::sub(*cl::AllSubCommands),
>> + cl::init(false));
>> + StackSubCommand SC2("sc2", "Second subcommand");
>> +
>> + const char *args[] = {"prog", "-everywhere"};
>> + const char *args2[] = {"prog", "sc1", "-everywhere"};
>> + const char *args3[] = {"prog", "sc2", "-everywhere"};
>> +
>> + EXPECT_FALSE(AllOpt);
>> + EXPECT_TRUE(cl::ParseCommandLineOptions(2, args, nullptr, true));
>> + EXPECT_TRUE(AllOpt);
>> +
>> + AllOpt = false;
>> +
>> + cl::ResetAllOptionOccurrences();
>> + EXPECT_FALSE(AllOpt);
>> + EXPECT_TRUE(cl::ParseCommandLineOptions(3, args2, nullptr, true));
>> + EXPECT_TRUE(AllOpt);
>> +
>> + AllOpt = false;
>> +
>> + cl::ResetAllOptionOccurrences();
>> + EXPECT_FALSE(AllOpt);
>> + EXPECT_TRUE(cl::ParseCommandLineOptions(3, args3, nullptr, true));
>> + EXPECT_TRUE(AllOpt);
>> +}
>> +
>> +TEST(CommandLineTest, ReparseCommandLineOptions) {
>> + cl::ResetCommandLineParser();
>> +
>> + StackOption<bool> TopLevelOpt("top-level", cl::sub(*cl::
>> TopLevelSubCommand),
>> + cl::init(false));
>> +
>> + const char *args[] = {"prog", "-top-level"};
>> +
>> + EXPECT_FALSE(TopLevelOpt);
>> + EXPECT_TRUE(cl::ParseCommandLineOptions(2, args, nullptr, true));
>> + EXPECT_TRUE(TopLevelOpt);
>> +
>> + TopLevelOpt = false;
>> +
>> + cl::ResetAllOptionOccurrences();
>> + EXPECT_FALSE(TopLevelOpt);
>> + EXPECT_TRUE(cl::ParseCommandLineOptions(2, args, nullptr, true));
>> + EXPECT_TRUE(TopLevelOpt);
>> +}
>> +
>> +TEST(CommandLineTest, RemoveFromRegularSubCommand) {
>> + cl::ResetCommandLineParser();
>> +
>> + StackSubCommand SC("sc", "Subcommand");
>> + StackOption<bool> RemoveOption("remove-option", cl::sub(SC),
>> cl::init(false));
>> + StackOption<bool> KeepOption("keep-option", cl::sub(SC),
>> cl::init(false));
>> +
>> + const char *args[] = {"prog", "sc", "-remove-option"};
>> +
>> + EXPECT_FALSE(RemoveOption);
>> + EXPECT_TRUE(cl::ParseCommandLineOptions(3, args, nullptr, true));
>> + EXPECT_TRUE(RemoveOption);
>> +
>> + RemoveOption.removeArgument();
>> +
>> + cl::ResetAllOptionOccurrences();
>> + EXPECT_FALSE(cl::ParseCommandLineOptions(3, args, nullptr, true));
>> +}
>> +
>> +TEST(CommandLineTest, RemoveFromTopLevelSubCommand) {
>> + cl::ResetCommandLineParser();
>> +
>> + StackOption<bool> TopLevelRemove(
>> + "top-level-remove", cl::sub(*cl::TopLevelSubCommand),
>> cl::init(false));
>> + StackOption<bool> TopLevelKeep(
>> + "top-level-keep", cl::sub(*cl::TopLevelSubCommand),
>> cl::init(false));
>> +
>> + const char *args[] = {"prog", "-top-level-remove"};
>> +
>> + EXPECT_FALSE(TopLevelRemove);
>> + EXPECT_TRUE(cl::ParseCommandLineOptions(2, args, nullptr, true));
>> + EXPECT_TRUE(TopLevelRemove);
>> +
>> + TopLevelRemove.removeArgument();
>> +
>> + cl::ResetAllOptionOccurrences();
>> + EXPECT_FALSE(cl::ParseCommandLineOptions(2, args, nullptr, true));
>> +}
>> +
>> +TEST(CommandLineTest, RemoveFromAllSubCommands) {
>> + cl::ResetCommandLineParser();
>> +
>> + StackSubCommand SC1("sc1", "First Subcommand");
>> + StackSubCommand SC2("sc2", "Second Subcommand");
>> + StackOption<bool> RemoveOption("remove-option",
>> cl::sub(*cl::AllSubCommands),
>> + cl::init(false));
>> + StackOption<bool> KeepOption("keep-option",
>> cl::sub(*cl::AllSubCommands),
>> + cl::init(false));
>> +
>> + const char *args0[] = {"prog", "-remove-option"};
>> + const char *args1[] = {"prog", "sc1", "-remove-option"};
>> + const char *args2[] = {"prog", "sc2", "-remove-option"};
>> +
>> + // It should work for all subcommands including the top-level.
>> + EXPECT_FALSE(RemoveOption);
>> + EXPECT_TRUE(cl::ParseCommandLineOptions(2, args0, nullptr, true));
>> + EXPECT_TRUE(RemoveOption);
>> +
>> + RemoveOption = false;
>> +
>> + cl::ResetAllOptionOccurrences();
>> + EXPECT_FALSE(RemoveOption);
>> + EXPECT_TRUE(cl::ParseCommandLineOptions(3, args1, nullptr, true));
>> + EXPECT_TRUE(RemoveOption);
>> +
>> + RemoveOption = false;
>> +
>> + cl::ResetAllOptionOccurrences();
>> + EXPECT_FALSE(RemoveOption);
>> + EXPECT_TRUE(cl::ParseCommandLineOptions(3, args2, nullptr, true));
>> + EXPECT_TRUE(RemoveOption);
>> +
>> + RemoveOption.removeArgument();
>> +
>> + // It should not work for any subcommands including the top-level.
>> + cl::ResetAllOptionOccurrences();
>> + EXPECT_FALSE(cl::ParseCommandLineOptions(2, args0, nullptr, true));
>> + cl::ResetAllOptionOccurrences();
>> + EXPECT_FALSE(cl::ParseCommandLineOptions(3, args1, nullptr, true));
>> + cl::ResetAllOptionOccurrences();
>> + EXPECT_FALSE(cl::ParseCommandLineOptions(3, args2, nullptr, true));
>> +}
>> +
>> } // anonymous namespace
>>
>> Modified: llvm/trunk/unittests/Support/ProgramTest.cpp
>> URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/unittests/
>> Support/ProgramTest.cpp?rev=274171&r1=274170&r2=274171&view=diff
>> ============================================================
>> ==================
>> --- llvm/trunk/unittests/Support/ProgramTest.cpp (original)
>> +++ llvm/trunk/unittests/Support/ProgramTest.cpp Wed Jun 29 16:48:26 2016
>> @@ -231,7 +231,7 @@ TEST_F(ProgramEnvTest, TestExecuteNoWait
>> // LoopCount should only be incremented once.
>> while (true) {
>> ++LoopCount;
>> - ProcessInfo WaitResult = Wait(PI1, 0, true, &Error);
>> + ProcessInfo WaitResult = llvm::sys::Wait(PI1, 0, true, &Error);
>> ASSERT_TRUE(Error.empty());
>> if (WaitResult.Pid == PI1.Pid)
>> break;
>> @@ -248,7 +248,7 @@ TEST_F(ProgramEnvTest, TestExecuteNoWait
>> // cse, LoopCount should be greater than 1 (more than one increment
>> occurs).
>> while (true) {
>> ++LoopCount;
>> - ProcessInfo WaitResult = Wait(PI2, 0, false, &Error);
>> + ProcessInfo WaitResult = llvm::sys::Wait(PI2, 0, false, &Error);
>> ASSERT_TRUE(Error.empty());
>> if (WaitResult.Pid == PI2.Pid)
>> break;
>>
>>
>> _______________________________________________
>> llvm-commits mailing list
>> llvm-commits at lists.llvm.org
>> http://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-commits
>>
>>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.llvm.org/pipermail/llvm-commits/attachments/20161010/7de9c44a/attachment.html>
More information about the llvm-commits
mailing list