mirror of
https://github.com/RPCS3/llvm.git
synced 2024-11-29 14:40:39 +00:00
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:
parent
03fb46bed1
commit
9dd8c0cffe
@ -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; }
|
||||
|
@ -44,6 +44,7 @@ public:
|
||||
unsigned short Flags;
|
||||
unsigned short GroupID;
|
||||
unsigned short AliasID;
|
||||
const char *AliasArgs;
|
||||
};
|
||||
|
||||
private:
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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");
|
||||
}
|
||||
|
@ -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"]>;
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user