Implemented public interface for modifying registered (not positional or sink options) command line options at runtime.

Patch by Dan Liew!

git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@181254 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
Andrew Trick 2013-05-06 21:56:35 +00:00
parent b7ad33b719
commit 61e0172197
4 changed files with 142 additions and 3 deletions

View File

@ -1267,6 +1267,57 @@ only consists of one function `cl::ParseCommandLineOptions`_) and three main
classes: `cl::opt`_, `cl::list`_, and `cl::alias`_. This section describes
these three classes in detail.
.. _cl::getRegisteredOptions:
The ``cl::getRegisteredOptions`` function
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
The ``cl::getRegisteredOptions`` function is designed to give a programmer
access to declared non positional command line options so that how they appear
in ``-help`` can be modified prior to calling `cl::ParseCommandLineOptions`_.
Note this method should not be called during any static initialisation because
it cannot be guaranteed that all options will have been initialised. Hence it
should be called from ``main``.
This function can be used to gain access to options declared in libraries that
the tool writter may not have direct access to.
The function retrieves a :ref:`StringMap <dss_stringmap>` that maps the option
string (e.g. ``-help``) to an ``Option*``.
Here is an example of how the function could be used:
.. code-block:: c++
using namespace llvm;
int main(int argc, char **argv) {
cl::OptionCategory AnotherCategory("Some options");
StringMap<cl::Option*> Map;
cl::getRegisteredOptions(Map);
//Unhide useful option and put it in a different category
assert(Map.count("print-all-options") > 0);
Map["print-all-options"]->setHiddenFlag(cl::NotHidden);
Map["print-all-options"]->setCategory(AnotherCategory);
//Hide an option we don't want to see
assert(Map.count("enable-no-infs-fp-math") > 0);
Map["enable-no-infs-fp-math"]->setHiddenFlag(cl::Hidden);
//Change --version to --show-version
assert(Map.count("version") > 0);
Map["version"]->setArgStr("show-version");
//Change --help description
assert(Map.count("help") > 0);
Map["help"]->setDescription("Shows help");
cl::ParseCommandLineOptions(argc, argv, "This is a small program to demo the LLVM CommandLine API");
...
}
.. _cl::ParseCommandLineOptions:
The ``cl::ParseCommandLineOptions`` function

View File

@ -22,6 +22,7 @@
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/Twine.h"
#include "llvm/ADT/StringMap.h"
#include "llvm/Support/Compiler.h"
#include "llvm/Support/type_traits.h"
#include <cassert>
@ -1713,6 +1714,39 @@ void PrintVersionMessage();
/// \param categorized if true print options in categories
void PrintHelpMessage(bool Hidden=false, bool Categorized=false);
//===----------------------------------------------------------------------===//
// Public interface for accessing registered options.
//
/// \brief Use this to get a StringMap to all registered named options
/// (e.g. -help). Note \p Map Should be an empty StringMap.
///
/// \param [out] map will be filled with mappings where the key is the
/// Option argument string (e.g. "help") and value is the corresponding
/// Option*.
///
/// Access to unnamed arguments (i.e. positional) are not provided because
/// it is expected that the client already has access to these.
///
/// Typical usage:
/// \code
/// main(int argc,char* argv[]) {
/// StringMap<llvm::cl::Option*> opts;
/// llvm::cl::getRegisteredOptions(opts);
/// assert(opts.count("help") == 1)
/// opts["help"]->setDescription("Show alphabetical help information")
/// // More code
/// llvm::cl::ParseCommandLineOptions(argc,argv);
/// //More code
/// }
/// \endcode
///
/// This interface is useful for modifying options in libraries that are out of
/// the control of the client. The options should be modified before calling
/// llvm::cl::ParseCommandLineOptions().
void getRegisteredOptions(StringMap<Option*> &Map);
} // End namespace cl
} // End namespace llvm

View File

@ -1596,3 +1596,13 @@ void cl::AddExtraVersionPrinter(void (*func)()) {
ExtraVersionPrinters->push_back(func);
}
void cl::getRegisteredOptions(StringMap<Option*> &Map)
{
// Get all the options.
SmallVector<Option*, 4> PositionalOpts; //NOT USED
SmallVector<Option*, 4> SinkOpts; //NOT USED
assert(Map.size() == 0 && "StringMap must be empty");
GetOptionInfo(PositionalOpts, SinkOpts, Map);
return;
}

View File

@ -41,6 +41,45 @@ class TempEnvVar {
const char *const name;
};
cl::OptionCategory TestCategory("Test Options", "Description");
cl::opt<int> TestOption("test-option", cl::desc("old description"));
TEST(CommandLineTest, ModifyExisitingOption) {
const char Description[] = "New description";
const char ArgString[] = "new-test-option";
const char ValueString[] = "Integer";
StringMap<cl::Option*> Map;
cl::getRegisteredOptions(Map);
ASSERT_TRUE(Map.count("test-option") == 1) <<
"Could not find option in map.";
cl::Option *Retrieved = Map["test-option"];
ASSERT_EQ(&TestOption, Retrieved) << "Retrieved wrong option.";
ASSERT_EQ(&cl::GeneralCategory,Retrieved->Category) <<
"Incorrect default option category.";
Retrieved->setCategory(TestCategory);
ASSERT_EQ(&TestCategory,Retrieved->Category) <<
"Failed to modify option's option category.";
Retrieved->setDescription(Description);
ASSERT_STREQ(Retrieved->HelpStr, Description) <<
"Changing option description failed.";
Retrieved->setArgStr(ArgString);
ASSERT_STREQ(ArgString, Retrieved->ArgStr) <<
"Failed to modify option's Argument string.";
Retrieved->setValueStr(ValueString);
ASSERT_STREQ(Retrieved->ValueStr, ValueString) <<
"Failed to modify option's Value string.";
Retrieved->setHiddenFlag(cl::Hidden);
ASSERT_EQ(cl::Hidden, TestOption.getOptionHiddenFlag()) <<
"Failed to modify option's hidden flag.";
}
#ifndef SKIP_ENVIRONMENT_TESTS
const char test_env_var[] = "LLVM_TEST_COMMAND_LINE_FLAGS";
@ -55,6 +94,12 @@ TEST(CommandLineTest, ParseEnvironment) {
// This test used to make valgrind complain
// ("Conditional jump or move depends on uninitialised value(s)")
//
// Warning: Do not run any tests after this one that try to gain access to
// registered command line options because this will likely result in a
// SEGFAULT. This can occur because the cl::opt in the test below is declared
// on the stack which will be destroyed after the test completes but the
// command line system will still hold a pointer to a deallocated cl::Option.
TEST(CommandLineTest, ParseEnvironmentToLocalVar) {
// Put cl::opt on stack to check for proper initialization of fields.
cl::opt<std::string> EnvironmentTestOptionLocal("env-test-opt-local");
@ -67,10 +112,9 @@ TEST(CommandLineTest, ParseEnvironmentToLocalVar) {
#endif // SKIP_ENVIRONMENT_TESTS
TEST(CommandLineTest, UseOptionCategory) {
cl::OptionCategory TestCategory("Test Options", "Description");
cl::opt<int> TestOption("test-option", cl::cat(TestCategory));
cl::opt<int> TestOption2("test-option", cl::cat(TestCategory));
ASSERT_EQ(&TestCategory,TestOption.Category) << "Failed to assign Option "
ASSERT_EQ(&TestCategory,TestOption2.Category) << "Failed to assign Option "
"Category.";
}