//
class OptionCategory {
private:
- StringRef Name = "General Category";
- StringRef Description;
+ StringRef const Name;
+ StringRef const Description;
void registerCategory();
public:
- OptionCategory(StringRef Name,
- StringRef Description = "")
+ OptionCategory(StringRef const Name,
+ StringRef const Description = "")
: Name(Name), Description(Description) {
registerCategory();
}
- OptionCategory() = default;
StringRef getName() const { return Name; }
StringRef getDescription() const { return Description; }
-
- SmallPtrSet<Option *, 16> MemberOptions;
};
// The general Option Category (used as default category).
-extern ManagedStatic<OptionCategory> GeneralCategory;
+extern OptionCategory GeneralCategory;
//===----------------------------------------------------------------------===//
// SubCommand class
StringRef ArgStr; // The argument string itself (ex: "help", "o")
StringRef HelpStr; // The descriptive text message for -help
StringRef ValueStr; // String describing what the value of this option is
-
- // Return the set of OptionCategories that this Option belongs to.
- SmallPtrSet<OptionCategory *, 1> getCategories() const;
-
- // Return the set of SubCommands that this Option belongs to.
- SmallPtrSet<SubCommand *, 1> getSubCommands() const;
+ SmallVector<OptionCategory *, 1>
+ Categories; // The Categories this option belongs to
+ SmallPtrSet<SubCommand *, 1> Subs; // The subcommands this option belongs to.
inline enum NumOccurrencesFlag getNumOccurrencesFlag() const {
return (enum NumOccurrencesFlag)Occurrences;
return getNumOccurrencesFlag() == cl::ConsumeAfter;
}
+ bool isInAllSubCommands() const {
+ return any_of(Subs, [](const SubCommand *SC) {
+ return SC == &*AllSubCommands;
+ });
+ }
+
//-------------------------------------------------------------------------===
// Accessor functions set by OptionModifiers
//
void setMiscFlag(enum MiscFlags M) { Misc |= M; }
void setPosition(unsigned pos) { Position = pos; }
void addCategory(OptionCategory &C);
+ void addSubCommand(SubCommand &S) { Subs.insert(&S); }
protected:
explicit Option(enum NumOccurrencesFlag OccurrencesFlag,
enum OptionHidden Hidden)
: NumOccurrences(0), Occurrences(OccurrencesFlag), Value(0),
HiddenFlag(Hidden), Formatting(NormalFormatting), Misc(0),
- FullyInitialized(false), Position(0), AdditionalVals(0) {}
+ FullyInitialized(false), Position(0), AdditionalVals(0) {
+ Categories.push_back(&GeneralCategory);
+ }
inline void setNumAdditionalVals(unsigned n) { AdditionalVals = n; }
// addArgument - Register this argument with the commandline system.
//
- virtual void addArgument(SubCommand &SC);
-
- // addArgument - Only called in done() method to add default
- // TopLevelSubCommand.
- void addArgument() {
- if (!FullyInitialized)
- addArgument(*TopLevelSubCommand);
- }
+ void addArgument();
/// Unregisters this option from the CommandLine system.
///
sub(SubCommand &S) : Sub(S) {}
- template <class Opt> void apply(Opt &O) const { O.addArgument(Sub); }
+ template <class Opt> void apply(Opt &O) const { O.addSubCommand(Sub); }
};
//===----------------------------------------------------------------------===//
error("cl::alias must have argument name specified!");
if (!AliasFor)
error("cl::alias must have an cl::aliasopt(option) specified!");
- for(OptionCategory *Cat: AliasFor->getCategories())
- addCategory(*Cat);
- for(SubCommand *SC: AliasFor->getSubCommands())
- Option::addArgument(*SC);
+ if (!Subs.empty())
+ error("cl::alias must not have cl::sub(), aliased option's cl::sub() will be used!");
+ Subs = AliasFor->Subs;
+ Categories = AliasFor->Categories;
+ addArgument();
}
public:
AliasFor = &O;
}
- // Does nothing when called via apply. Aliases call Option::addArgument
- // directly in the done() method to actually add the option..
- void addArgument(SubCommand &SC) override {}
-
template <class... Mods>
explicit alias(const Mods &... Ms)
: Option(Optional, Hidden), AliasFor(nullptr) {
// This collects Options added with the cl::DefaultOption flag. Since they can
// be overridden, they are not added to the appropriate SubCommands until
// ParseCommandLineOptions actually runs.
- SmallVector<std::pair<Option*, SubCommand*>, 4> DefaultOptions;
+ SmallVector<Option*, 4> DefaultOptions;
// This collects the different option categories that have been registered.
SmallPtrSet<OptionCategory *, 16> RegisteredOptionCategories;
SmallPtrSet<SubCommand *, 4> RegisteredSubCommands;
CommandLineParser() : ActiveSubCommand(nullptr) {
- RegisteredOptionCategories.insert(&*GeneralCategory);
registerSubCommand(&*TopLevelSubCommand);
registerSubCommand(&*AllSubCommands);
}
}
void addLiteralOption(Option &Opt, StringRef Name) {
- for(SubCommand *SC: Opt.getSubCommands())
- addLiteralOption(Opt, SC, Name);
- }
-
- void addOption(Option *O, SubCommand *SC, bool ProcessDefaultOptions = false) {
- if (!ProcessDefaultOptions && O->isDefaultOption()) {
- DefaultOptions.push_back(std::make_pair(O, SC));
- return;
+ if (Opt.Subs.empty())
+ addLiteralOption(Opt, &*TopLevelSubCommand, Name);
+ else {
+ for (auto SC : Opt.Subs)
+ addLiteralOption(Opt, SC, Name);
}
+ }
+ void addOption(Option *O, SubCommand *SC) {
bool HadErrors = false;
if (O->hasArgStr()) {
// If it's a DefaultOption, check to make sure it isn't already there.
for (const auto &Sub : RegisteredSubCommands) {
if (SC == Sub)
continue;
- addOption(O, Sub, ProcessDefaultOptions);
+ addOption(O, Sub);
}
}
}
- void addDefaultOptions() {
- for (std::pair<Option *, SubCommand *> &DO : DefaultOptions) {
- addOption(DO.first, DO.second, true);
+ void addOption(Option *O, bool ProcessDefaultOption = false) {
+ if (!ProcessDefaultOption && O->isDefaultOption()) {
+ DefaultOptions.push_back(O);
+ return;
+ }
+
+ if (O->Subs.empty()) {
+ addOption(O, &*TopLevelSubCommand);
+ } else {
+ for (auto SC : O->Subs)
+ addOption(O, SC);
}
}
}
void removeOption(Option *O) {
- for (auto SC : RegisteredSubCommands)
- removeOption(O, SC);
+ if (O->Subs.empty())
+ removeOption(O, &*TopLevelSubCommand);
+ else {
+ if (O->isInAllSubCommands()) {
+ for (auto SC : RegisteredSubCommands)
+ removeOption(O, SC);
+ } else {
+ for (auto SC : O->Subs)
+ removeOption(O, SC);
+ }
+ }
}
bool hasOptions(const SubCommand &Sub) const {
}
void updateArgStr(Option *O, StringRef NewName) {
- for (auto SC : RegisteredSubCommands)
- updateArgStr(O, NewName, SC);
+ if (O->Subs.empty())
+ updateArgStr(O, NewName, &*TopLevelSubCommand);
+ else {
+ if (O->isInAllSubCommands()) {
+ for (auto SC : RegisteredSubCommands)
+ updateArgStr(O, NewName, SC);
+ } else {
+ for (auto SC : O->Subs)
+ updateArgStr(O, NewName, SC);
+ }
+ }
}
void printOptionValues();
MoreHelp.clear();
RegisteredOptionCategories.clear();
- RegisteredOptionCategories.insert(&*GeneralCategory);
ResetAllOptionOccurrences();
RegisteredSubCommands.clear();
GlobalParser->MoreHelp.push_back(Help);
}
-void Option::addArgument(SubCommand &SC) {
- GlobalParser->addOption(this, &SC);
+void Option::addArgument() {
+ GlobalParser->addOption(this);
FullyInitialized = true;
}
void Option::removeArgument() { GlobalParser->removeOption(this); }
-SmallPtrSet<OptionCategory *, 1> Option::getCategories() const {
- SmallPtrSet<OptionCategory *, 1> Cats;
- for (OptionCategory *C: GlobalParser->RegisteredOptionCategories) {
- if (C->MemberOptions.find(this) != C->MemberOptions.end())
- Cats.insert(C);
- }
- if (Cats.empty())
- Cats.insert(&*GeneralCategory);
- return Cats;
-}
-
-SmallPtrSet<SubCommand *, 1> Option::getSubCommands() const {
- // This can happen for enums and literal options.
- if (ArgStr.empty())
- return SmallPtrSet<SubCommand *, 1>{&*TopLevelSubCommand};
-
- SmallPtrSet<SubCommand *, 1> Subs;
- for (SubCommand *SC : GlobalParser->getRegisteredSubcommands()) {
- auto I = SC->OptionsMap.find(ArgStr);
- if (I != SC->OptionsMap.end() && I->getValue() == this)
- Subs.insert(SC);
- }
- return Subs;
-}
-
void Option::setArgStr(StringRef S) {
if (FullyInitialized)
GlobalParser->updateArgStr(this, S);
}
void Option::addCategory(OptionCategory &C) {
- C.MemberOptions.insert(this);
+ assert(!Categories.empty() && "Categories cannot be empty.");
+ // Maintain backward compatibility by replacing the default GeneralCategory
+ // if it's still set. Otherwise, just add the new one. The GeneralCategory
+ // must be explicitly added if you want multiple categories that include it.
+ if (&C != &GeneralCategory && Categories[0] == &GeneralCategory)
+ Categories[0] = &C;
+ else if (find(Categories, &C) == Categories.end())
+ Categories.push_back(&C);
}
void Option::reset() {
}
// Initialise the general option category.
-LLVM_REQUIRE_CONSTANT_INITIALIZATION
-ManagedStatic<OptionCategory> llvm::cl::GeneralCategory;
+OptionCategory llvm::cl::GeneralCategory("General options");
void OptionCategory::registerCategory() {
GlobalParser->registerCategory(this);
auto &SinkOpts = ChosenSubCommand->SinkOpts;
auto &OptionsMap = ChosenSubCommand->OptionsMap;
- addDefaultOptions();
+ for (auto O: DefaultOptions) {
+ addOption(O, true);
+ }
if (ConsumeAfterOpt) {
assert(PositionalOpts.size() > 0 &&
// options within categories will also be alphabetically sorted.
for (size_t I = 0, E = Opts.size(); I != E; ++I) {
Option *Opt = Opts[I].second;
- for (auto *Cat : Opt->getCategories()) {
+ for (auto &Cat : Opt->Categories) {
assert(CategorizedOptions.count(Cat) > 0 &&
"Option has an unregistered category");
CategorizedOptions[Cat].push_back(Opt);
void cl::HideUnrelatedOptions(cl::OptionCategory &Category, SubCommand &Sub) {
for (auto &I : Sub.OptionsMap) {
- for (OptionCategory *Cat : I.second->getCategories()) {
+ for (auto &Cat : I.second->Categories) {
if (Cat != &Category &&
Cat != &GenericCategory)
I.second->setHiddenFlag(cl::ReallyHidden);
void cl::HideUnrelatedOptions(ArrayRef<const cl::OptionCategory *> Categories,
SubCommand &Sub) {
for (auto &I : Sub.OptionsMap) {
- for (OptionCategory *Cat : I.second->getCategories()) {
+ for (auto &Cat : I.second->Categories) {
if (find(Categories, Cat) == Categories.end() && Cat != &GenericCategory)
I.second->setHiddenFlag(cl::ReallyHidden);
}
cl::Option *Retrieved = Map["test-option"];
ASSERT_EQ(&TestOption, Retrieved) << "Retrieved wrong option.";
- ASSERT_NE(Retrieved->getCategories().end(),
- find_if(Retrieved->getCategories(),
+ ASSERT_NE(Retrieved->Categories.end(),
+ find_if(Retrieved->Categories,
[&](const llvm::cl::OptionCategory *Cat) {
- return Cat == &*cl::GeneralCategory;
+ return Cat == &cl::GeneralCategory;
}))
<< "Incorrect default option category.";
Retrieved->addCategory(TestCategory);
- ASSERT_NE(Retrieved->getCategories().end(),
- find_if(Retrieved->getCategories(),
+ ASSERT_NE(Retrieved->Categories.end(),
+ find_if(Retrieved->Categories,
[&](const llvm::cl::OptionCategory *Cat) {
return Cat == &TestCategory;
}))
TEST(CommandLineTest, UseOptionCategory) {
StackOption<int> TestOption2("test-option", cl::cat(TestCategory));
- ASSERT_NE(TestOption2.getCategories().end(),
- find_if(TestOption2.getCategories(),
+ ASSERT_NE(TestOption2.Categories.end(),
+ find_if(TestOption2.Categories,
[&](const llvm::cl::OptionCategory *Cat) {
return Cat == &TestCategory;
}))
TEST(CommandLineTest, UseMultipleCategories) {
StackOption<int> TestOption2("test-option2", cl::cat(TestCategory),
- cl::cat(*cl::GeneralCategory),
- cl::cat(*cl::GeneralCategory));
- auto TestOption2Categories = TestOption2.getCategories();
+ cl::cat(cl::GeneralCategory),
+ cl::cat(cl::GeneralCategory));
// Make sure cl::GeneralCategory wasn't added twice.
- ASSERT_EQ(TestOption2Categories.size(), 2U);
+ ASSERT_EQ(TestOption2.Categories.size(), 2U);
- ASSERT_NE(TestOption2Categories.end(),
- find_if(TestOption2Categories,
+ ASSERT_NE(TestOption2.Categories.end(),
+ find_if(TestOption2.Categories,
[&](const llvm::cl::OptionCategory *Cat) {
return Cat == &TestCategory;
}))
<< "Failed to assign Option Category.";
- ASSERT_NE(TestOption2Categories.end(),
- find_if(TestOption2Categories,
+ ASSERT_NE(TestOption2.Categories.end(),
+ find_if(TestOption2.Categories,
[&](const llvm::cl::OptionCategory *Cat) {
- return Cat == &*cl::GeneralCategory;
+ return Cat == &cl::GeneralCategory;
}))
<< "Failed to assign General Category.";
cl::OptionCategory AnotherCategory("Additional test Options", "Description");
StackOption<int> TestOption("test-option", cl::cat(TestCategory),
cl::cat(AnotherCategory));
- auto TestOptionCategories = TestOption.getCategories();
- ASSERT_EQ(TestOptionCategories.end(),
- find_if(TestOptionCategories,
+ ASSERT_EQ(TestOption.Categories.end(),
+ find_if(TestOption.Categories,
[&](const llvm::cl::OptionCategory *Cat) {
- return Cat == &*cl::GeneralCategory;
+ return Cat == &cl::GeneralCategory;
}))
<< "Failed to remove General Category.";
- ASSERT_NE(TestOptionCategories.end(),
- find_if(TestOptionCategories,
+ ASSERT_NE(TestOption.Categories.end(),
+ find_if(TestOption.Categories,
[&](const llvm::cl::OptionCategory *Cat) {
return Cat == &TestCategory;
}))
<< "Failed to assign Option Category.";
- ASSERT_NE(TestOptionCategories.end(),
- find_if(TestOptionCategories,
+ ASSERT_NE(TestOption.Categories.end(),
+ find_if(TestOption.Categories,
[&](const llvm::cl::OptionCategory *Cat) {
return Cat == &AnotherCategory;
}))
testAliasRequired(array_lengthof(opts2), opts2);
}
-TEST(CommandLineTest, AliasWithSubCommand) {
- StackSubCommand SC1("sc1", "Subcommand 1");
- StackOption<std::string> Option1("option", cl::value_desc("output file"),
- cl::init("-"), cl::desc("Option"),
- cl::sub(SC1));
- StackOption<std::string, cl::alias> Alias1("o", llvm::cl::aliasopt(Option1),
- cl::desc("Alias for --option"),
- cl::sub(SC1));
-}
-
-TEST(CommandLineTest, AliasWithMultipleSubCommandsWithSameOption) {
- StackSubCommand SC1("sc1", "Subcommand 1");
- StackOption<std::string> Option1("option", cl::value_desc("output file"),
- cl::init("-"), cl::desc("Option"),
- cl::sub(SC1));
- StackSubCommand SC2("sc2", "Subcommand 2");
- StackOption<std::string> Option2("option", cl::value_desc("output file"),
- cl::init("-"), cl::desc("Option"),
- cl::sub(SC2));
-
- StackOption<std::string, cl::alias> Alias1("o", llvm::cl::aliasopt(Option1),
- cl::desc("Alias for --option"));
-}
-
TEST(CommandLineTest, HideUnrelatedOptions) {
StackOption<int> TestOption1("hide-option-1");
StackOption<int> TestOption2("hide-option-2", cl::cat(TestCategory));