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 @@ -468,6 +468,38 @@ 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. +template struct cb { + std::function CB; + + cb(std::function CB) : CB(CB) {} + + template void apply(Opt &O) const { O.setCallback(CB); } +}; + +namespace detail { +template +struct function_traits : public function_traits {}; + +template +struct function_traits { + using result_type = R; + using arg_type = A; +}; +} // namespace detail + +template +cb::result_type, + typename detail::function_traits::arg_type> +callback(F CB) { + using result_type = typename detail::function_traits::result_type; + using arg_type = typename detail::function_traits::arg_type; + static_assert(std::is_same::value, + "callback return type must be void"); + return cb(CB); +} + //===----------------------------------------------------------------------===// // OptionValue class @@ -1341,6 +1373,7 @@ return true; // Parse error! this->setValue(Val); this->setPosition(pos); + Callback(Val); return false; } @@ -1399,6 +1432,7 @@ template DataType &operator=(const T &Val) { this->setValue(Val); + Callback(Val); return this->getValue(); } @@ -1408,6 +1442,14 @@ apply(this, Ms...); done(); } + + void setCallback( + std::function CB) { + Callback = CB; + } + + std::function Callback = + [](const typename ParserClass::parser_data_type &) {}; }; extern template class opt; @@ -1547,6 +1589,7 @@ list_storage::addValue(Val); setPosition(pos); Positions.push_back(pos); + Callback(Val); return false; } @@ -1593,6 +1636,14 @@ apply(this, Ms...); done(); } + + void setCallback( + std::function CB) { + Callback = CB; + } + + std::function Callback = + [](const typename ParserClass::parser_data_type &) {}; }; // multi_val - Modifier to set the number of additional values. @@ -1693,6 +1744,7 @@ this->addValue(Val); setPosition(pos); Positions.push_back(pos); + Callback(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,66 @@ 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([&](const bool &) { OptA = true; })); + StackOption OptC( + "c", cl::desc("option c -- This option turns on options a and b"), + cl::callback([&](const bool &) { 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([&](const std::string &Str) { + 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