diff options
-rw-r--r-- | include/llvm/Option/OptParser.td | 2 | ||||
-rw-r--r-- | include/llvm/Option/OptTable.h | 1 | ||||
-rw-r--r-- | include/llvm/Option/Option.h | 10 | ||||
-rw-r--r-- | lib/Option/Option.cpp | 22 | ||||
-rw-r--r-- | unittests/Option/OptionParsingTest.cpp | 19 | ||||
-rw-r--r-- | unittests/Option/Opts.td | 3 | ||||
-rw-r--r-- | utils/TableGen/OptParserEmitter.cpp | 33 |
7 files changed, 82 insertions, 8 deletions
diff --git a/include/llvm/Option/OptParser.td b/include/llvm/Option/OptParser.td index 394e0b6a4b..32cc2c0cd5 100644 --- a/include/llvm/Option/OptParser.td +++ b/include/llvm/Option/OptParser.td @@ -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; } diff --git a/include/llvm/Option/OptTable.h b/include/llvm/Option/OptTable.h index 8ee219d37b..a5b59cea3f 100644 --- a/include/llvm/Option/OptTable.h +++ b/include/llvm/Option/OptTable.h @@ -44,6 +44,7 @@ public: unsigned short Flags; unsigned short GroupID; unsigned short AliasID; + const char *AliasArgs; }; private: diff --git a/include/llvm/Option/Option.h b/include/llvm/Option/Option.h index 4861b59726..47fd8174c5 100644 --- a/include/llvm/Option/Option.h +++ b/include/llvm/Option/Option.h @@ -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; diff --git a/lib/Option/Option.cpp b/lib/Option/Option.cpp index 5b418e845e..1d6a3d3804 100644 --- a/lib/Option/Option.cpp +++ b/lib/Option/Option.cpp @@ -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); diff --git a/unittests/Option/OptionParsingTest.cpp b/unittests/Option/OptionParsingTest.cpp index 2c4fdcfa2c..101568a567 100644 --- a/unittests/Option/OptionParsingTest.cpp +++ b/unittests/Option/OptionParsingTest.cpp @@ -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"); +} diff --git a/unittests/Option/Opts.td b/unittests/Option/Opts.td index 8e33ba8580..986b3122af 100644 --- a/unittests/Option/Opts.td +++ b/unittests/Option/Opts.td @@ -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"]>; diff --git a/utils/TableGen/OptParserEmitter.cpp b/utils/TableGen/OptParserEmitter.cpp index 0c1f6236e0..d37939f635 100644 --- a/utils/TableGen/OptParserEmitter.cpp +++ b/utils/TableGen/OptParserEmitter.cpp @@ -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 |