Skip to content

Commit 8b0300b

Browse files
Thomas Preud'hommepow2clk
authored andcommitted
Recommit: Add support for prefix-only CLI options
Summary: Add support for options that always prefix their value, giving an error if the value is in the next argument or if the option is given a value assignment (ie. opt=val). This is the desired behavior for the -D option of FileCheck for instance. Copyright: - Linaro (changes in version 2 of revision D55940) - GraphCore (changes in later versions and introduced when creating D56549) Reviewers: jdenny Subscribers: llvm-commits, probinson, kristina, hiraditya, JonChesterfield Differential Revision: https://reviews.llvm.org/D56549 llvm-svn: 353172 (cherry picked from LLVM commit f929a0f81b6f75f880796b3716ab8a858a764de9)
1 parent 907172f commit 8b0300b

3 files changed

Lines changed: 91 additions & 10 deletions

File tree

include/llvm/Support/CommandLine.h

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,9 @@ enum OptionHidden { // Control whether -help shows this option
134134
// enabled, and used, the value for the flag comes from the suffix of the
135135
// argument.
136136
//
137+
// AlwaysPrefix - Only allow the behavior enabled by the Prefix flag and reject
138+
// the Option=Value form.
139+
//
137140
// Grouping - With this option enabled, multiple letter options are allowed to
138141
// bunch together with only a single hyphen for the whole group. This allows
139142
// emulation of the behavior that ls uses for example: ls -la === ls -l -a
@@ -143,7 +146,8 @@ enum FormattingFlags {
143146
NormalFormatting = 0x00, // Nothing special
144147
Positional = 0x01, // Is a positional argument, no '-' required
145148
Prefix = 0x02, // Can this option directly prefix its value?
146-
Grouping = 0x03 // Can this option group with other options?
149+
AlwaysPrefix = 0x03, // Can this option only directly prefix its value?
150+
Grouping = 0x04 // Can this option group with other options?
147151
};
148152

149153
enum MiscFlags { // Miscellaneous flags to adjust argument
@@ -242,7 +246,7 @@ class Option {
242246
// detail representing the non-value
243247
unsigned Value : 2;
244248
unsigned HiddenFlag : 2; // enum OptionHidden
245-
unsigned Formatting : 2; // enum FormattingFlags
249+
unsigned Formatting : 3; // enum FormattingFlags
246250
unsigned Misc : 3;
247251
unsigned Position; // Position of last occurrence of the option
248252
unsigned AdditionalVals; // Greater than 0 for multi-valued option.

lib/Support/CommandLine.cpp

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -417,12 +417,17 @@ Option *CommandLineParser::LookupOption(SubCommand &Sub, StringRef &Arg,
417417
return I != Sub.OptionsMap.end() ? I->second : nullptr;
418418
}
419419

420-
// If the argument before the = is a valid option name, we match. If not,
421-
// return Arg unmolested.
420+
// If the argument before the = is a valid option name and the option allows
421+
// non-prefix form (ie is not AlwaysPrefix), we match. If not, signal match
422+
// failure by returning nullptr.
422423
auto I = Sub.OptionsMap.find(Arg.substr(0, EqualPos));
423424
if (I == Sub.OptionsMap.end())
424425
return nullptr;
425426

427+
auto O = I->second;
428+
if (O->getFormattingFlag() == cl::AlwaysPrefix)
429+
return nullptr;
430+
426431
Value = Arg.substr(EqualPos + 1);
427432
Arg = Arg.substr(0, EqualPos);
428433
return I->second;
@@ -534,7 +539,9 @@ static inline bool ProvideOption(Option *Handler, StringRef ArgName,
534539
switch (Handler->getValueExpectedFlag()) {
535540
case ValueRequired:
536541
if (!Value.data()) { // No value specified?
537-
if (i + 1 >= argc)
542+
// If no other argument or the option only supports prefix form, we
543+
// cannot look at the next argument.
544+
if (i + 1 >= argc || Handler->getFormattingFlag() == cl::AlwaysPrefix)
538545
return Handler->error("requires a value!");
539546
// Steal the next argument, like for '-o filename'
540547
assert(argv && "null check");
@@ -592,7 +599,8 @@ static inline bool isGrouping(const Option *O) {
592599
return O->getFormattingFlag() == cl::Grouping;
593600
}
594601
static inline bool isPrefixedOrGrouping(const Option *O) {
595-
return isGrouping(O) || O->getFormattingFlag() == cl::Prefix;
602+
return isGrouping(O) || O->getFormattingFlag() == cl::Prefix ||
603+
O->getFormattingFlag() == cl::AlwaysPrefix;
596604
}
597605

598606
// getOptionPred - Check to see if there are any options that satisfy the
@@ -642,7 +650,8 @@ HandlePrefixedOrGroupedOption(StringRef &Arg, StringRef &Value,
642650
// If the option is a prefixed option, then the value is simply the
643651
// rest of the name... so fall through to later processing, by
644652
// setting up the argument name flags and value fields.
645-
if (PGOpt->getFormattingFlag() == cl::Prefix) {
653+
if (PGOpt->getFormattingFlag() == cl::Prefix ||
654+
PGOpt->getFormattingFlag() == cl::AlwaysPrefix) {
646655
Value = Arg.substr(Length);
647656
Arg = Arg.substr(0, Length);
648657
assert(OptionsMap.count(Arg) && OptionsMap.find(Arg)->second == PGOpt);

unittests/Support/CommandLineTest.cpp

Lines changed: 71 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -45,9 +45,8 @@ class TempEnvVar {
4545
const char *const name;
4646
};
4747

48-
template <typename T>
49-
class StackOption : public cl::opt<T> {
50-
typedef cl::opt<T> Base;
48+
template <typename T, typename Base = cl::opt<T>>
49+
class StackOption : public Base {
5150
public:
5251
// One option...
5352
template<class M0t>
@@ -480,4 +479,73 @@ TEST(CommandLineTest, RemoveFromAllSubCommands) {
480479
EXPECT_FALSE(cl::ParseCommandLineOptions(3, args2, nullptr, true));
481480
}
482481

482+
TEST(CommandLineTest, PrefixOptions) {
483+
cl::ResetCommandLineParser();
484+
485+
StackOption<std::string, cl::list<std::string>> IncludeDirs(
486+
"I", cl::Prefix, cl::desc("Declare an include directory"));
487+
488+
// Test non-prefixed variant works with cl::Prefix options.
489+
EXPECT_TRUE(IncludeDirs.empty());
490+
const char *args[] = {"prog", "-I=/usr/include"};
491+
EXPECT_TRUE(cl::ParseCommandLineOptions(2, args, "", /*IgnoreErrors*/ true));
492+
EXPECT_TRUE(IncludeDirs.size() == 1);
493+
EXPECT_TRUE(IncludeDirs.front().compare("/usr/include") == 0);
494+
495+
IncludeDirs.erase(IncludeDirs.begin());
496+
cl::ResetAllOptionOccurrences();
497+
498+
// Test non-prefixed variant works with cl::Prefix options when value is
499+
// passed in following argument.
500+
EXPECT_TRUE(IncludeDirs.empty());
501+
const char *args2[] = {"prog", "-I", "/usr/include"};
502+
EXPECT_TRUE(cl::ParseCommandLineOptions(3, args2, "", /*IgnoreErrors*/ true));
503+
EXPECT_TRUE(IncludeDirs.size() == 1);
504+
EXPECT_TRUE(IncludeDirs.front().compare("/usr/include") == 0);
505+
506+
IncludeDirs.erase(IncludeDirs.begin());
507+
cl::ResetAllOptionOccurrences();
508+
509+
// Test prefixed variant works with cl::Prefix options.
510+
EXPECT_TRUE(IncludeDirs.empty());
511+
const char *args3[] = {"prog", "-I/usr/include"};
512+
EXPECT_TRUE(cl::ParseCommandLineOptions(2, args3, "", /*IgnoreErrors*/ true));
513+
EXPECT_TRUE(IncludeDirs.size() == 1);
514+
EXPECT_TRUE(IncludeDirs.front().compare("/usr/include") == 0);
515+
516+
StackOption<std::string, cl::list<std::string>> MacroDefs(
517+
"D", cl::AlwaysPrefix, cl::desc("Define a macro"),
518+
cl::value_desc("MACRO[=VALUE]"));
519+
520+
cl::ResetAllOptionOccurrences();
521+
522+
// Test non-prefixed variant does not work with cl::AlwaysPrefix options:
523+
// equal sign is part of the value.
524+
EXPECT_TRUE(MacroDefs.empty());
525+
const char *args4[] = {"prog", "-D=HAVE_FOO"};
526+
EXPECT_TRUE(cl::ParseCommandLineOptions(2, args4, "", /*IgnoreErrors*/ true));
527+
EXPECT_TRUE(MacroDefs.size() == 1);
528+
EXPECT_TRUE(MacroDefs.front().compare("=HAVE_FOO") == 0);
529+
530+
MacroDefs.erase(MacroDefs.begin());
531+
cl::ResetAllOptionOccurrences();
532+
533+
// Test non-prefixed variant does not allow value to be passed in following
534+
// argument with cl::AlwaysPrefix options.
535+
EXPECT_TRUE(MacroDefs.empty());
536+
const char *args5[] = {"prog", "-D", "HAVE_FOO"};
537+
EXPECT_FALSE(
538+
cl::ParseCommandLineOptions(3, args5, "", /*IgnoreErrors*/ true));
539+
EXPECT_TRUE(MacroDefs.empty());
540+
541+
cl::ResetAllOptionOccurrences();
542+
543+
// Test prefixed variant works with cl::AlwaysPrefix options.
544+
EXPECT_TRUE(MacroDefs.empty());
545+
const char *args6[] = {"prog", "-DHAVE_FOO"};
546+
EXPECT_TRUE(cl::ParseCommandLineOptions(2, args6, "", /*IgnoreErrors*/ true));
547+
EXPECT_TRUE(MacroDefs.size() == 1);
548+
EXPECT_TRUE(MacroDefs.front().compare("HAVE_FOO") == 0);
549+
}
550+
483551
} // anonymous namespace

0 commit comments

Comments
 (0)