[llvm] r274171 - Resubmit "Update llvm command line parser to support subcommands."

Zachary Turner via llvm-commits llvm-commits at lists.llvm.org
Mon Oct 10 08:56:20 PDT 2016


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".

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/757d5f3e/attachment-0001.html>


More information about the llvm-commits mailing list