Option parsing: add support for alias arguments.

This makes option aliases more powerful by enabling them to
pass along arguments to the option they're aliasing.

For example, if we have a joined option "-foo=", we can now
specify a flag option "-bar" to be an alias of that, with the
argument "baz".

This is especially useful for the cl.exe compatible clang driver,
where many options are aliases. For example, this patch enables
us to alias "/Ox" to "-O3" (-O is a joined option), and "/WX" to
"-Werror" (again, -W is a joined option).

Differential Revision: http://llvm-reviews.chandlerc.com/D1245

git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@187537 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
Hans Wennborg 2013-07-31 22:44:41 +00:00
parent 03fb46bed1
commit 9dd8c0cffe
7 changed files with 82 additions and 8 deletions

View File

@ -89,6 +89,7 @@ class Option<list<string> prefixes, string name, OptionKind kind> {
list<OptionFlag> Flags = [];
OptionGroup Group = ?;
Option Alias = ?;
list<string> AliasArgs = [];
}
// Helpers for defining options.
@ -113,6 +114,7 @@ class JoinedAndSeparate<list<string> prefixes, string name>
// Mix-ins for adding optional attributes.
class Alias<Option alias> { Option Alias = alias; }
class AliasArgs<list<string> aliasargs> { list<string> AliasArgs = aliasargs; }
class EnumName<string name> { string EnumName = name; }
class Flags<list<OptionFlag> flags> { list<OptionFlag> Flags = flags; }
class Group<OptionGroup group> { OptionGroup Group = group; }

View File

@ -44,6 +44,7 @@ public:
unsigned short Flags;
unsigned short GroupID;
unsigned short AliasID;
const char *AliasArgs;
};
private:

View File

@ -103,6 +103,16 @@ public:
return Owner->getOption(Info->AliasID);
}
/// \brief Get the alias arguments as a \0 separated list.
/// E.g. ["foo", "bar"] would be returned as "foo\0bar\0".
const char *getAliasArgs() const {
assert(Info && "Must have a valid info!");
assert((!Info->AliasArgs || Info->AliasArgs[0] != 0) &&
"AliasArgs should be either 0 or non-empty.");
return Info->AliasArgs;
}
/// \brief Get the default prefix for this option.
StringRef getPrefix() const {
const char *Prefix = *Info->Prefixes;

View File

@ -26,6 +26,13 @@ Option::Option(const OptTable::Info *info, const OptTable *owner)
// tracking, it is not an inherent limitation.
assert((!Info || !getAlias().isValid() || !getAlias().getAlias().isValid()) &&
"Multi-level aliases are not supported.");
if (Info && getAliasArgs()) {
assert(getAlias().isValid() && "Only alias options can have alias args.");
assert(getKind() == FlagClass && "Only Flag aliases can have alias args.");
assert(getAlias().getKind() != FlagClass &&
"Cannot provide alias args to a flag option.");
}
}
Option::~Option() {
@ -106,11 +113,22 @@ Arg *Option::accept(const ArgList &Args,
}
switch (getKind()) {
case FlagClass:
case FlagClass: {
if (ArgSize != strlen(Args.getArgString(Index)))
return 0;
return new Arg(UnaliasedOption, Spelling, Index++);
Arg *A = new Arg(UnaliasedOption, Spelling, Index++);
if (getAliasArgs()) {
const char *Val = getAliasArgs();
while (*Val != '\0') {
A->getValues().push_back(Val);
// Move past the '\0' to the next argument.
Val += strlen(Val) + 1;
}
}
return A;
}
case JoinedClass: {
const char *Value = Args.getArgString(Index) + ArgSize;
return new Arg(UnaliasedOption, Spelling, Index++, Value);

View File

@ -17,9 +17,11 @@
using namespace llvm;
using namespace llvm::opt;
#define SUPPORT_ALIASARGS // FIXME: Remove when no longer necessary.
enum ID {
OPT_INVALID = 0, // This is not an option ID.
#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, FLAGS, PARAM, \
#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \
HELPTEXT, METAVAR) OPT_##ID,
#include "Opts.inc"
LastOption
@ -37,10 +39,10 @@ enum OptionFlags {
};
static const OptTable::Info InfoTable[] = {
#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, FLAGS, PARAM, \
#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \
HELPTEXT, METAVAR) \
{ PREFIX, NAME, HELPTEXT, METAVAR, OPT_##ID, Option::KIND##Class, PARAM, \
FLAGS, OPT_##GROUP, OPT_##ALIAS },
FLAGS, OPT_##GROUP, OPT_##ALIAS, ALIASARGS },
#include "Opts.inc"
#undef OPTION
};
@ -145,3 +147,14 @@ TEST(Option, ParseAliasInGroup) {
OwningPtr<InputArgList> AL(T.ParseArgs(MyArgs, array_endof(MyArgs), MAI, MAC));
EXPECT_TRUE(AL->hasArg(OPT_H));
}
TEST(Option, AliasArgs) {
TestOptTable T;
unsigned MAI, MAC;
const char *MyArgs[] = { "-J", "-Joo" };
OwningPtr<InputArgList> AL(T.ParseArgs(MyArgs, array_endof(MyArgs), MAI, MAC));
EXPECT_TRUE(AL->hasArg(OPT_B));
EXPECT_EQ(AL->getAllArgValues(OPT_B)[0], "foo");
EXPECT_EQ(AL->getAllArgValues(OPT_B)[1], "bar");
}

View File

@ -19,3 +19,6 @@ def H : Flag<["-"], "H">, Flags<[HelpHidden]>;
def my_group : OptionGroup<"my group">;
def I : Flag<["-"], "I">, Alias<H>, Group<my_group>;
def J : Flag<["-"], "J">, Alias<B>, AliasArgs<["foo"]>;
def Joo : Flag<["-"], "Joo">, Alias<B>, AliasArgs<["bar"]>;

View File

@ -152,11 +152,22 @@ void EmitOptParser(RecordKeeper &Records, raw_ostream &OS) {
OS << "/////////\n";
OS << "// Groups\n\n";
OS << "#ifdef OPTION\n";
// FIXME: Remove when option parsing clients are updated.
OS << "#ifdef SUPPORT_ALIASARGS\n";
OS << "#define OPTIONX OPTION\n";
OS << "#else\n";
OS << "#define OPTIONX(prefix, name, id, kind, group, alias, aliasargs, "
<< "flags, param, helptext, metavar) "
<< "OPTION(prefix, name, id, kind, "
<< "group, alias, flags, param, helptext, metavar)\n";
OS << "#endif\n";
for (unsigned i = 0, e = Groups.size(); i != e; ++i) {
const Record &R = *Groups[i];
// Start a single option entry.
OS << "OPTION(";
OS << "OPTIONX(";
// The option prefix;
OS << "0";
@ -178,7 +189,7 @@ void EmitOptParser(RecordKeeper &Records, raw_ostream &OS) {
OS << "INVALID";
// The other option arguments (unused for groups).
OS << ", INVALID, 0, 0";
OS << ", INVALID, 0, 0, 0";
// The option help text.
if (!isa<UnsetInit>(R.getValueInit("HelpText"))) {
@ -199,7 +210,7 @@ void EmitOptParser(RecordKeeper &Records, raw_ostream &OS) {
const Record &R = *Opts[i];
// Start a single option entry.
OS << "OPTION(";
OS << "OPTIONX(";
// The option prefix;
std::vector<std::string> prf = R.getValueAsListOfStrings("Prefixes");
@ -228,6 +239,21 @@ void EmitOptParser(RecordKeeper &Records, raw_ostream &OS) {
else
OS << "INVALID";
// The option alias arguments (if any).
// Emitted as a \0 separated list in a string, e.g. ["foo", "bar"]
// would become "foo\0bar\0". Note that the compiler adds an implicit
// terminating \0 at the end.
OS << ", ";
std::vector<std::string> AliasArgs = R.getValueAsListOfStrings("AliasArgs");
if (AliasArgs.size() == 0) {
OS << "0";
} else {
OS << "\"";
for (size_t i = 0, e = AliasArgs.size(); i != e; ++i)
OS << AliasArgs[i] << "\\0";
OS << "\"";
}
// The option flags.
const ListInit *LI = R.getValueAsListInit("Flags");
if (LI->empty()) {
@ -261,6 +287,7 @@ void EmitOptParser(RecordKeeper &Records, raw_ostream &OS) {
OS << ")\n";
}
OS << "#undef OPTIONX\n"; // FIXME: Remove when option clients are updated.
OS << "#endif\n";
}
} // end namespace llvm