[llvm] r274054 - Update llvm command line parser to support subcommands.

Zachary Turner via llvm-commits llvm-commits at lists.llvm.org
Tue Jun 28 13:09:48 PDT 2016


Author: zturner
Date: Tue Jun 28 15:09:47 2016
New Revision: 274054

URL: http://llvm.org/viewvc/llvm-project?rev=274054&view=rev
Log:
Update llvm command line parser to support subcommands.

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
Differential Revision: http://reviews.llvm.org/D21485

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=274054&r1=274053&r2=274054&view=diff
==============================================================================
--- llvm/trunk/include/llvm/Support/CommandLine.h (original)
+++ llvm/trunk/include/llvm/Support/CommandLine.h Tue Jun 28 15:09:47 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,10 @@ 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);
+
+void ResetCommandLineOptions();
 
 } // End namespace cl
 

Modified: llvm/trunk/lib/Support/CommandLine.cpp
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/Support/CommandLine.cpp?rev=274054&r1=274053&r2=274054&view=diff
==============================================================================
--- llvm/trunk/lib/Support/CommandLine.cpp (original)
+++ llvm/trunk/lib/Support/CommandLine.cpp Tue Jun 28 15:09:47 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,54 @@ 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");
+  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 +151,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 +168,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 +278,58 @@ 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();
+
+    for (auto SC : RegisteredSubCommands) {
+      for (auto &O : SC->OptionsMap)
+        O.second->reset();
+    }
+    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 +364,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 +397,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 +427,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 +990,16 @@ 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,
+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 +1022,30 @@ void CommandLineParser::ParseCommandLine
   // Determine whether or not there are an unlimited number of positionals
   bool HasUnlimitedPositionals = false;
 
+  // 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();
+  }
+
+  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 +1061,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 +1101,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 +1148,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 +1160,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 +1176,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 +1207,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 +1316,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 +1671,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 +1704,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 +1722,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 +1750,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) {
+      // 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 +1948,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 +1962,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 +2005,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 +2114,25 @@ 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;
+  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 +2140,9 @@ void cl::HideUnrelatedOptions(ArrayRef<c
   }
 }
 
+void cl::ResetCommandLineOptions() { GlobalParser->reset(); }
+
 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=274054&r1=274053&r2=274054&view=diff
==============================================================================
--- llvm/trunk/unittests/Support/CommandLineTest.cpp (original)
+++ llvm/trunk/unittests/Support/CommandLineTest.cpp Tue Jun 28 15:09:47 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,189 @@ 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::ResetCommandLineOptions();
+
+  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;
+
+  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;
+
+  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::ResetCommandLineOptions();
+
+  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::ResetCommandLineOptions();
+
+  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;
+
+  EXPECT_FALSE(AllOpt);
+  EXPECT_TRUE(cl::ParseCommandLineOptions(3, args2, nullptr, true));
+  EXPECT_TRUE(AllOpt);
+
+  AllOpt = false;
+
+  EXPECT_FALSE(AllOpt);
+  EXPECT_TRUE(cl::ParseCommandLineOptions(3, args3, nullptr, true));
+  EXPECT_TRUE(AllOpt);
+}
+
+TEST(CommandLineTest, ReparseCommandLineOptions) {
+  cl::ResetCommandLineOptions();
+
+  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;
+
+  EXPECT_FALSE(TopLevelOpt);
+  EXPECT_TRUE(cl::ParseCommandLineOptions(2, args, nullptr, true));
+  EXPECT_TRUE(TopLevelOpt);
+}
+
+TEST(CommandLineTest, RemoveFromRegularSubCommand) {
+  cl::ResetCommandLineOptions();
+
+  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();
+
+  EXPECT_FALSE(cl::ParseCommandLineOptions(3, args, nullptr, true));
+}
+
+TEST(CommandLineTest, RemoveFromTopLevelSubCommand) {
+  cl::ResetCommandLineOptions();
+
+  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();
+
+  EXPECT_FALSE(cl::ParseCommandLineOptions(2, args, nullptr, true));
+}
+
+TEST(CommandLineTest, RemoveFromAllSubCommands) {
+  cl::ResetCommandLineOptions();
+
+  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;
+
+  EXPECT_FALSE(RemoveOption);
+  EXPECT_TRUE(cl::ParseCommandLineOptions(3, args1, nullptr, true));
+  EXPECT_TRUE(RemoveOption);
+
+  RemoveOption = false;
+
+  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.
+  EXPECT_FALSE(cl::ParseCommandLineOptions(2, args0, nullptr, true));
+  EXPECT_FALSE(cl::ParseCommandLineOptions(3, args1, nullptr, true));
+  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=274054&r1=274053&r2=274054&view=diff
==============================================================================
--- llvm/trunk/unittests/Support/ProgramTest.cpp (original)
+++ llvm/trunk/unittests/Support/ProgramTest.cpp Tue Jun 28 15:09:47 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;




More information about the llvm-commits mailing list