mirror of
https://github.com/RPCS3/llvm.git
synced 2024-11-23 19:59:48 +00:00
[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 git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@360532 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
parent
f26529e8bb
commit
541e19ce1b
@ -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
|
||||
|
@ -105,6 +105,16 @@ static StringRef argPrefix(StringRef ArgName) {
|
||||
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(Option *Handler, StringRef Arg, int i) {
|
||||
return ProvideOption(Handler, Handler->ArgStr, Arg, 0, nullptr, Dummy);
|
||||
}
|
||||
|
||||
// Option predicates...
|
||||
static inline bool isGrouping(const Option *O) {
|
||||
return O->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(const Option *O) {
|
||||
static Option *getOptionPred(StringRef Name, size_t &Length,
|
||||
bool (*Pred)(const Option *),
|
||||
const StringMap<Option *> &OptionsMap) {
|
||||
|
||||
StringMap<Option *>::const_iterator OMI = OptionsMap.find(Name);
|
||||
if (OMI != OptionsMap.end() && !Pred(OMI->getValue()))
|
||||
OMI = OptionsMap.end();
|
||||
|
||||
// Loop while we haven't found an option and Name still has at least two
|
||||
// characters in it (so that the next iteration will not be the empty
|
||||
@ -706,6 +716,8 @@ static Option *getOptionPred(StringRef Name, size_t &Length,
|
||||
while (OMI == OptionsMap.end() && Name.size() > 1) {
|
||||
Name = Name.substr(0, Name.size() - 1); // Chop off the last character.
|
||||
OMI = OptionsMap.find(Name);
|
||||
if (OMI != OptionsMap.end() && !Pred(OMI->getValue()))
|
||||
OMI = OptionsMap.end();
|
||||
}
|
||||
|
||||
if (OMI != OptionsMap.end() && Pred(OMI->second)) {
|
||||
@ -1166,7 +1178,8 @@ void cl::ParseEnvironmentOptions(const char *progName, const char *envVar,
|
||||
|
||||
bool cl::ParseCommandLineOptions(int argc, const char *const *argv,
|
||||
StringRef Overview, raw_ostream *Errs,
|
||||
const char *EnvVar) {
|
||||
const char *EnvVar,
|
||||
bool LongOptionsUseDoubleDash) {
|
||||
SmallVector<const char *, 20> NewArgv;
|
||||
BumpPtrAllocator A;
|
||||
StringSaver Saver(A);
|
||||
@ -1186,7 +1199,7 @@ bool cl::ParseCommandLineOptions(int argc, const char *const *argv,
|
||||
|
||||
// Parse all options.
|
||||
return GlobalParser->ParseCommandLineOptions(NewArgc, &NewArgv[0], Overview,
|
||||
Errs);
|
||||
Errs, LongOptionsUseDoubleDash);
|
||||
}
|
||||
|
||||
void CommandLineParser::ResetAllOptionOccurrences() {
|
||||
@ -1201,7 +1214,8 @@ void CommandLineParser::ResetAllOptionOccurrences() {
|
||||
bool CommandLineParser::ParseCommandLineOptions(int argc,
|
||||
const char *const *argv,
|
||||
StringRef Overview,
|
||||
raw_ostream *Errs) {
|
||||
raw_ostream *Errs,
|
||||
bool LongOptionsUseDoubleDash) {
|
||||
assert(hasOptions() && "No options specified!");
|
||||
|
||||
// Expand response files.
|
||||
@ -1311,6 +1325,7 @@ bool CommandLineParser::ParseCommandLineOptions(int argc,
|
||||
std::string NearestHandlerString;
|
||||
StringRef Value;
|
||||
StringRef ArgName = "";
|
||||
bool HaveDoubleDash = false;
|
||||
|
||||
// Check to see if this is a positional argument. This argument is
|
||||
// considered to be positional if it doesn't start with '-', if it is "-"
|
||||
@ -1349,25 +1364,30 @@ bool CommandLineParser::ParseCommandLineOptions(int argc,
|
||||
// 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);
|
||||
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user