diff --git a/llvm/docs/CommandLine.rst b/llvm/docs/CommandLine.rst --- a/llvm/docs/CommandLine.rst +++ b/llvm/docs/CommandLine.rst @@ -996,6 +996,15 @@ * The **cl::cat** attribute specifies the option category that the option belongs to. The category should be a `cl::OptionCategory`_ object. +.. _cl::callback: + +* The **cl::callback** attribute specifies a callback function that is + called when an option is seen, and can be used to set other options, + as in option A implies option B. If the option is a `cl::list`, + and `cl::CommaSeparated` is also specified, the callback will fire + once for each value. This could be used to validate combinations or + selectively set other options. + Option Modifiers ---------------- diff --git a/llvm/docs/ReleaseNotes.rst b/llvm/docs/ReleaseNotes.rst --- a/llvm/docs/ReleaseNotes.rst +++ b/llvm/docs/ReleaseNotes.rst @@ -90,6 +90,9 @@ ``-cfguard-nochecks`` option. Note that this feature should always be used with optimizations enabled. +* ``Callbacks`` have been added to ``CommandLine Options``. These can + be used to validate of selectively enable other options. + Changes to the LLVM IR ---------------------- diff --git a/llvm/include/llvm/Support/CommandLine.h b/llvm/include/llvm/Support/CommandLine.h --- a/llvm/include/llvm/Support/CommandLine.h +++ b/llvm/include/llvm/Support/CommandLine.h @@ -286,6 +286,7 @@ SmallVector Categories; // The Categories this option belongs to SmallPtrSet Subs; // The subcommands this option belongs to. + std::function Callback = [](void*){}; // Callback function inline enum NumOccurrencesFlag getNumOccurrencesFlag() const { return (enum NumOccurrencesFlag)Occurrences; @@ -337,6 +338,7 @@ void setPosition(unsigned pos) { Position = pos; } void addCategory(OptionCategory &C); void addSubCommand(SubCommand &S) { Subs.insert(&S); } + void setCallback(std::function CB) { Callback = CB; } protected: explicit Option(enum NumOccurrencesFlag OccurrencesFlag, @@ -468,6 +470,16 @@ template void apply(Opt &O) const { O.addSubCommand(Sub); } }; +// callback - Specify a callback function to be called when an option is seen. +// Can be used to set other options automatically. +struct callback { + std::function CB; + + callback(std::function CB) : CB(CB) {} + + void apply(Option &O) const { O.setCallback(CB); } +}; + //===----------------------------------------------------------------------===// // OptionValue class @@ -1341,6 +1353,7 @@ return true; // Parse error! this->setValue(Val); this->setPosition(pos); + Callback(reinterpret_cast(&Val)); return false; } @@ -1399,6 +1412,8 @@ template DataType &operator=(const T &Val) { this->setValue(Val); + T &NCVal = const_cast(Val); + Callback(reinterpret_cast(&NCVal)); return this->getValue(); } @@ -1547,6 +1562,7 @@ list_storage::addValue(Val); setPosition(pos); Positions.push_back(pos); + Callback(reinterpret_cast(&Val)); return false; } @@ -1693,6 +1709,7 @@ this->addValue(Val); setPosition(pos); Positions.push_back(pos); + Callback(reinterpret_cast(&Val)); return false; } diff --git a/llvm/unittests/Support/CommandLineTest.cpp b/llvm/unittests/Support/CommandLineTest.cpp --- a/llvm/unittests/Support/CommandLineTest.cpp +++ b/llvm/unittests/Support/CommandLineTest.cpp @@ -61,7 +61,7 @@ ~StackOption() override { this->removeArgument(); } template StackOption &operator=(const DT &V) { - this->setValue(V); + Base::operator=(V); return *this; } }; @@ -1703,4 +1703,67 @@ cl::ResetAllOptionOccurrences(); } -} // anonymous namespace + +TEST(CommandLineTest, Callback) { + cl::ResetCommandLineParser(); + + StackOption OptA("a", cl::desc("option a")); + StackOption OptB( + "b", cl::desc("option b -- This option turns on option a"), + cl::callback([&](void *) { OptA = true; })); + StackOption OptC( + "c", cl::desc("option c -- This option turns on options a and b"), + cl::callback([&](void *) { OptB = true; })); + StackOption> List( + "list", + cl::desc( + "option list -- This option turns on options a, b, and c when " + "'foo' is included in list"), + cl::CommaSeparated, cl::callback([&](void *Val) { + std::string Str = *reinterpret_cast(Val); + if (Str == "foo") + OptC = true; + })); + + const char *args1[] = {"prog", "-a"}; + EXPECT_TRUE(cl::ParseCommandLineOptions(2, args1)); + EXPECT_TRUE(OptA); + EXPECT_FALSE(OptB); + EXPECT_FALSE(OptC); + EXPECT_TRUE(List.size() == 0); + cl::ResetAllOptionOccurrences(); + + const char *args2[] = {"prog", "-b"}; + EXPECT_TRUE(cl::ParseCommandLineOptions(2, args2)); + EXPECT_TRUE(OptA); + EXPECT_TRUE(OptB); + EXPECT_FALSE(OptC); + EXPECT_TRUE(List.size() == 0); + cl::ResetAllOptionOccurrences(); + + const char *args3[] = {"prog", "-c"}; + EXPECT_TRUE(cl::ParseCommandLineOptions(2, args3)); + EXPECT_TRUE(OptA); + EXPECT_TRUE(OptB); + EXPECT_TRUE(OptC); + EXPECT_TRUE(List.size() == 0); + cl::ResetAllOptionOccurrences(); + + const char *args4[] = {"prog", "--list=foo,bar"}; + EXPECT_TRUE(cl::ParseCommandLineOptions(2, args4)); + EXPECT_TRUE(OptA); + EXPECT_TRUE(OptB); + EXPECT_TRUE(OptC); + EXPECT_TRUE(List.size() == 2); + cl::ResetAllOptionOccurrences(); + + const char *args5[] = {"prog", "--list=bar"}; + EXPECT_TRUE(cl::ParseCommandLineOptions(2, args5)); + EXPECT_FALSE(OptA); + EXPECT_FALSE(OptB); + EXPECT_FALSE(OptC); + EXPECT_TRUE(List.size() == 1); + + cl::ResetAllOptionOccurrences(); +} +} // anonymous namespace