[llvm] r360532 - [CommandLine] Add long option flag for cl::ParseCommandLineOptions . Part 5 of 5

Don Hinton via llvm-commits llvm-commits at lists.llvm.org
Sat May 11 13:27:01 PDT 2019


Author: dhinton
Date: Sat May 11 13:27:01 2019
New Revision: 360532

URL: http://llvm.org/viewvc/llvm-project?rev=360532&view=rev
Log:
[CommandLine] Add long option flag for cl::ParseCommandLineOptions . Part 5 of 5

Summary:
If passed, the long option flag makes the CommandLine parser
mimic the behavior or GNU getopt_long.  Short options are a single
character prefixed by a single dash, and long options are multiple
characters prefixed by a double dash.

This patch was motivated by the discussion in the following thread:
http://lists.llvm.org/pipermail/llvm-dev/2019-April/131786.html

Reviewed By: MaskRay

Tags: #llvm

Differential Revision: https://reviews.llvm.org/D61294

Modified:
    llvm/trunk/include/llvm/Support/CommandLine.h
    llvm/trunk/lib/Support/CommandLine.cpp
    llvm/trunk/unittests/Support/CommandLineTest.cpp

Modified: llvm/trunk/include/llvm/Support/CommandLine.h
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/include/llvm/Support/CommandLine.h?rev=360532&r1=360531&r2=360532&view=diff
==============================================================================
--- llvm/trunk/include/llvm/Support/CommandLine.h (original)
+++ llvm/trunk/include/llvm/Support/CommandLine.h Sat May 11 13:27:01 2019
@@ -66,7 +66,8 @@ namespace cl {
 bool ParseCommandLineOptions(int argc, const char *const *argv,
                              StringRef Overview = "",
                              raw_ostream *Errs = nullptr,
-                             const char *EnvVar = nullptr);
+                             const char *EnvVar = nullptr,
+                             bool LongOptionsUseDoubleDash = false);
 
 //===----------------------------------------------------------------------===//
 // ParseEnvironmentOptions - Environment variable option processing alternate

Modified: llvm/trunk/lib/Support/CommandLine.cpp
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/Support/CommandLine.cpp?rev=360532&r1=360531&r2=360532&view=diff
==============================================================================
--- llvm/trunk/lib/Support/CommandLine.cpp (original)
+++ llvm/trunk/lib/Support/CommandLine.cpp Sat May 11 13:27:01 2019
@@ -105,6 +105,16 @@ static StringRef argPrefix(StringRef Arg
   return ArgPrefixLong;
 }
 
+// Option predicates...
+static inline bool isGrouping(const Option *O) {
+  return O->getMiscFlags() & cl::Grouping;
+}
+static inline bool isPrefixedOrGrouping(const Option *O) {
+  return isGrouping(O) || O->getFormattingFlag() == cl::Prefix ||
+         O->getFormattingFlag() == cl::AlwaysPrefix;
+}
+
+
 namespace {
 
 class PrintArg {
@@ -148,7 +158,8 @@ public:
   void ResetAllOptionOccurrences();
 
   bool ParseCommandLineOptions(int argc, const char *const *argv,
-                               StringRef Overview, raw_ostream *Errs = nullptr);
+                               StringRef Overview, raw_ostream *Errs = nullptr,
+                               bool LongOptionsUseDoubleDash = false);
 
   void addLiteralOption(Option &Opt, SubCommand *SC, StringRef Name) {
     if (Opt.hasArgStr())
@@ -394,6 +405,13 @@ private:
   SubCommand *ActiveSubCommand;
 
   Option *LookupOption(SubCommand &Sub, StringRef &Arg, StringRef &Value);
+  Option *LookupLongOption(SubCommand &Sub, StringRef &Arg, StringRef &Value,
+                           bool LongOptionsUseDoubleDash, bool HaveDoubleDash) {
+    Option *Opt = LookupOption(Sub, Arg, Value);
+    if (Opt && LongOptionsUseDoubleDash && !HaveDoubleDash && !isGrouping(Opt))
+      return nullptr;
+    return Opt;
+  }
   SubCommand *LookupSubCommand(StringRef Name);
 };
 
@@ -679,15 +697,6 @@ static bool ProvidePositionalOption(Opti
   return ProvideOption(Handler, Handler->ArgStr, Arg, 0, nullptr, Dummy);
 }
 
-// Option predicates...
-static inline bool isGrouping(const Option *O) {
-  return O->getMiscFlags() & cl::Grouping;
-}
-static inline bool isPrefixedOrGrouping(const Option *O) {
-  return isGrouping(O) || O->getFormattingFlag() == cl::Prefix ||
-         O->getFormattingFlag() == cl::AlwaysPrefix;
-}
-
 // getOptionPred - Check to see if there are any options that satisfy the
 // specified predicate with names that are the prefixes in Name.  This is
 // checked by progressively stripping characters off of the name, checking to
@@ -697,8 +706,9 @@ static inline bool isPrefixedOrGrouping(
 static Option *getOptionPred(StringRef Name, size_t &Length,
                              bool (*Pred)(const Option *),
                              const StringMap<Option *> &OptionsMap) {
-
   StringMap<Option *>::const_iterator OMI = OptionsMap.find(Name);
+  if (OMI != OptionsMap.end() && !Pred(OMI->getValue()))
+    OMI = OptionsMap.end();
 
   // Loop while we haven't found an option and Name still has at least two
   // characters in it (so that the next iteration will not be the empty
@@ -706,6 +716,8 @@ static Option *getOptionPred(StringRef N
   while (OMI == OptionsMap.end() && Name.size() > 1) {
     Name = Name.substr(0, Name.size() - 1); // Chop off the last character.
     OMI = OptionsMap.find(Name);
+    if (OMI != OptionsMap.end() && !Pred(OMI->getValue()))
+      OMI = OptionsMap.end();
   }
 
   if (OMI != OptionsMap.end() && Pred(OMI->second)) {
@@ -1166,7 +1178,8 @@ void cl::ParseEnvironmentOptions(const c
 
 bool cl::ParseCommandLineOptions(int argc, const char *const *argv,
                                  StringRef Overview, raw_ostream *Errs,
-                                 const char *EnvVar) {
+                                 const char *EnvVar,
+                                 bool LongOptionsUseDoubleDash) {
   SmallVector<const char *, 20> NewArgv;
   BumpPtrAllocator A;
   StringSaver Saver(A);
@@ -1186,7 +1199,7 @@ bool cl::ParseCommandLineOptions(int arg
 
   // Parse all options.
   return GlobalParser->ParseCommandLineOptions(NewArgc, &NewArgv[0], Overview,
-                                               Errs);
+                                               Errs, LongOptionsUseDoubleDash);
 }
 
 void CommandLineParser::ResetAllOptionOccurrences() {
@@ -1201,7 +1214,8 @@ void CommandLineParser::ResetAllOptionOc
 bool CommandLineParser::ParseCommandLineOptions(int argc,
                                                 const char *const *argv,
                                                 StringRef Overview,
-                                                raw_ostream *Errs) {
+                                                raw_ostream *Errs,
+                                                bool LongOptionsUseDoubleDash) {
   assert(hasOptions() && "No options specified!");
 
   // Expand response files.
@@ -1311,6 +1325,7 @@ bool CommandLineParser::ParseCommandLine
     std::string NearestHandlerString;
     StringRef Value;
     StringRef ArgName = "";
+    bool HaveDoubleDash = false;
 
     // Check to see if this is a positional argument.  This argument is
     // considered to be positional if it doesn't start with '-', if it is "-"
@@ -1349,25 +1364,30 @@ bool CommandLineParser::ParseCommandLine
       // otherwise feed it to the eating positional.
       ArgName = StringRef(argv[i] + 1);
       // Eat second dash.
-      if (!ArgName.empty() && ArgName[0] == '-')
+      if (!ArgName.empty() && ArgName[0] == '-') {
+        HaveDoubleDash = true;
         ArgName = ArgName.substr(1);
+      }
 
-      Handler = LookupOption(*ChosenSubCommand, ArgName, Value);
+      Handler = LookupLongOption(*ChosenSubCommand, ArgName, Value,
+                                 LongOptionsUseDoubleDash, HaveDoubleDash);
       if (!Handler || Handler->getFormattingFlag() != cl::Positional) {
         ProvidePositionalOption(ActivePositionalArg, StringRef(argv[i]), i);
         continue; // We are done!
       }
-
     } else { // We start with a '-', must be an argument.
       ArgName = StringRef(argv[i] + 1);
       // Eat second dash.
-      if (!ArgName.empty() && ArgName[0] == '-')
+      if (!ArgName.empty() && ArgName[0] == '-') {
+        HaveDoubleDash = true;
         ArgName = ArgName.substr(1);
+      }
 
-      Handler = LookupOption(*ChosenSubCommand, ArgName, Value);
+      Handler = LookupLongOption(*ChosenSubCommand, ArgName, Value,
+                                 LongOptionsUseDoubleDash, HaveDoubleDash);
 
       // Check to see if this "option" is really a prefixed or grouped argument.
-      if (!Handler)
+      if (!Handler && !(LongOptionsUseDoubleDash && HaveDoubleDash))
         Handler = HandlePrefixedOrGroupedOption(ArgName, Value, ErrorParsing,
                                                 OptionsMap);
 

Modified: llvm/trunk/unittests/Support/CommandLineTest.cpp
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/unittests/Support/CommandLineTest.cpp?rev=360532&r1=360531&r2=360532&view=diff
==============================================================================
--- llvm/trunk/unittests/Support/CommandLineTest.cpp (original)
+++ llvm/trunk/unittests/Support/CommandLineTest.cpp Sat May 11 13:27:01 2019
@@ -1527,4 +1527,77 @@ TEST(CommandLineTest, GroupingAndPrefix)
   cl::ResetAllOptionOccurrences();
 }
 
+TEST(CommandLineTest, LongOptions) {
+  cl::ResetCommandLineParser();
+
+  StackOption<bool> OptA("a", cl::desc("Some flag"));
+  StackOption<bool> OptBLong("long-flag", cl::desc("Some long flag"));
+  StackOption<bool, cl::alias> OptB("b", cl::desc("Alias to --long-flag"),
+                                    cl::aliasopt(OptBLong));
+  StackOption<std::string> OptAB("ab", cl::desc("Another long option"));
+
+  std::string Errs;
+  raw_string_ostream OS(Errs);
+
+  const char *args1[] = {"prog", "-a", "-ab", "val1"};
+  const char *args2[] = {"prog", "-a", "--ab", "val1"};
+  const char *args3[] = {"prog", "-ab", "--ab", "val1"};
+
+  //
+  // The following tests treat `-` and `--` the same, and always match the
+  // longest string.
+  //
+
+  EXPECT_TRUE(
+      cl::ParseCommandLineOptions(4, args1, StringRef(), &OS)); OS.flush();
+  EXPECT_TRUE(OptA);
+  EXPECT_FALSE(OptBLong);
+  EXPECT_STREQ("val1", OptAB.c_str());
+  EXPECT_TRUE(Errs.empty()); Errs.clear();
+  cl::ResetAllOptionOccurrences();
+
+  EXPECT_TRUE(
+      cl::ParseCommandLineOptions(4, args2, StringRef(), &OS)); OS.flush();
+  EXPECT_TRUE(OptA);
+  EXPECT_FALSE(OptBLong);
+  EXPECT_STREQ("val1", OptAB.c_str());
+  EXPECT_TRUE(Errs.empty()); Errs.clear();
+  cl::ResetAllOptionOccurrences();
+
+  // Fails because `-ab` and `--ab` are treated the same and appear more than
+  // once.  Also, `val1` is unexpected.
+  EXPECT_FALSE(
+      cl::ParseCommandLineOptions(4, args3, StringRef(), &OS)); OS.flush();
+  outs()<< Errs << "\n";
+  EXPECT_FALSE(Errs.empty()); Errs.clear();
+  cl::ResetAllOptionOccurrences();
+
+  //
+  // The following tests treat `-` and `--` differently, with `-` for short, and
+  // `--` for long options.
+  //
+
+  // Fails because `-ab` is treated as `-a -b`, so `-a` is seen twice, and
+  // `val1` is unexpected.
+  EXPECT_FALSE(cl::ParseCommandLineOptions(4, args1, StringRef(),
+                                           &OS, nullptr, true)); OS.flush();
+  EXPECT_FALSE(Errs.empty()); Errs.clear();
+  cl::ResetAllOptionOccurrences();
+
+  // Works because `-a` is treated differently than `--ab`.
+  EXPECT_TRUE(cl::ParseCommandLineOptions(4, args2, StringRef(),
+                                           &OS, nullptr, true)); OS.flush();
+  EXPECT_TRUE(Errs.empty()); Errs.clear();
+  cl::ResetAllOptionOccurrences();
+
+  // Works because `-ab` is treated as `-a -b`, and `--ab` is a long option.
+  EXPECT_TRUE(cl::ParseCommandLineOptions(4, args3, StringRef(),
+                                           &OS, nullptr, true));
+  EXPECT_TRUE(OptA);
+  EXPECT_TRUE(OptBLong);
+  EXPECT_STREQ("val1", OptAB.c_str());
+  OS.flush();
+  EXPECT_TRUE(Errs.empty()); Errs.clear();
+  cl::ResetAllOptionOccurrences();
+}
 }  // anonymous namespace




More information about the llvm-commits mailing list