mirror of
https://github.com/c64scene-ar/llvm-6502.git
synced 2025-03-25 00:35:30 +00:00
Support command line option categories.
Patch by Dan Liew! git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@181253 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
parent
b072090f39
commit
b7ad33b719
@ -618,6 +618,53 @@ would yield the help output:
|
||||
-help - display available options (-help-hidden for more)
|
||||
-o <filename> - Specify output filename
|
||||
|
||||
Grouping options into categories
|
||||
--------------------------------
|
||||
|
||||
If our program has a large number of options it may become difficult for users
|
||||
of our tool to navigate the output of ``-help``. To alleviate this problem we
|
||||
can put our options into categories. This can be done by declaring option
|
||||
categories (`cl::OptionCategory`_ objects) and then placing our options into
|
||||
these categories using the `cl::cat`_ option attribute. For example:
|
||||
|
||||
.. code-block:: c++
|
||||
|
||||
cl::OptionCategory StageSelectionCat("Stage Selection Options",
|
||||
"These control which stages are run.");
|
||||
|
||||
cl::opt<bool> Preprocessor("E",cl::desc("Run preprocessor stage."),
|
||||
cl::cat(StageSelectionCat));
|
||||
|
||||
cl::opt<bool> NoLink("c",cl::desc("Run all stages except linking."),
|
||||
cl::cat(StageSelectionCat));
|
||||
|
||||
The output of ``-help`` will become categorized if an option category is
|
||||
declared. The output looks something like ::
|
||||
|
||||
OVERVIEW: This is a small program to demo the LLVM CommandLine API
|
||||
USAGE: Sample [options]
|
||||
|
||||
OPTIONS:
|
||||
|
||||
General options:
|
||||
|
||||
-help - Display available options (-help-hidden for more)
|
||||
-help-list - Display list of available options (-help-list-hidden for more)
|
||||
|
||||
|
||||
Stage Selection Options:
|
||||
These control which stages are run.
|
||||
|
||||
-E - Run preprocessor stage.
|
||||
-c - Run all stages except linking.
|
||||
|
||||
In addition to the behaviour of ``-help`` changing when an option category is
|
||||
declared, the command line option ``-help-list`` becomes visible which will
|
||||
print the command line options as uncategorized list.
|
||||
|
||||
Note that Options that are not explicitly categorized will be placed in the
|
||||
``cl::GeneralCategory`` category.
|
||||
|
||||
.. _Reference Guide:
|
||||
|
||||
Reference Guide
|
||||
@ -946,6 +993,11 @@ This section describes the basic attributes that you can specify on options.
|
||||
of the usual modifiers on multi-valued options (besides
|
||||
``cl::ValueDisallowed``, obviously).
|
||||
|
||||
.. _cl::cat:
|
||||
|
||||
* The **cl::cat** attribute specifies the option category that the option
|
||||
belongs to. The category should be a `cl::OptionCategory`_ object.
|
||||
|
||||
Option Modifiers
|
||||
----------------
|
||||
|
||||
@ -1385,6 +1437,29 @@ For example:
|
||||
|
||||
cl::extrahelp("\nADDITIONAL HELP:\n\n This is the extra help\n");
|
||||
|
||||
.. _cl::OptionCategory:
|
||||
|
||||
The ``cl::OptionCategory`` class
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
The ``cl::OptionCategory`` class is a simple class for declaring
|
||||
option categories.
|
||||
|
||||
.. code-block:: c++
|
||||
|
||||
namespace cl {
|
||||
class OptionCategory;
|
||||
}
|
||||
|
||||
An option category must have a name and optionally a description which are
|
||||
passed to the constructor as ``const char*``.
|
||||
|
||||
Note that declaring an option category and associating it with an option before
|
||||
parsing options (e.g. statically) will change the output of ``-help`` from
|
||||
uncategorized to categorized. If an option category is declared but not
|
||||
associated with an option then it will be hidden from the output of ``-help``
|
||||
but will be shown in the output of ``-help-hidden``.
|
||||
|
||||
.. _different parser:
|
||||
.. _discussed previously:
|
||||
|
||||
|
@ -137,7 +137,23 @@ enum MiscFlags { // Miscellaneous flags to adjust argument
|
||||
Sink = 0x04 // Should this cl::list eat all unknown options?
|
||||
};
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Option Category class
|
||||
//
|
||||
class OptionCategory {
|
||||
private:
|
||||
const char *const Name;
|
||||
const char *const Description;
|
||||
void registerCategory();
|
||||
public:
|
||||
OptionCategory(const char *const Name, const char *const Description = 0)
|
||||
: Name(Name), Description(Description) { registerCategory(); }
|
||||
const char *getName() { return Name; }
|
||||
const char *getDescription() { return Description; }
|
||||
};
|
||||
|
||||
// The general Option Category (used as default category).
|
||||
extern OptionCategory GeneralCategory;
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Option Base class
|
||||
@ -173,10 +189,12 @@ class Option {
|
||||
unsigned Position; // Position of last occurrence of the option
|
||||
unsigned AdditionalVals;// Greater than 0 for multi-valued option.
|
||||
Option *NextRegistered; // Singly linked list of registered options.
|
||||
|
||||
public:
|
||||
const char *ArgStr; // The argument string itself (ex: "help", "o")
|
||||
const char *HelpStr; // The descriptive text message for -help
|
||||
const char *ValueStr; // String describing what the value of this option is
|
||||
const char *ArgStr; // The argument string itself (ex: "help", "o")
|
||||
const char *HelpStr; // The descriptive text message for -help
|
||||
const char *ValueStr; // String describing what the value of this option is
|
||||
OptionCategory *Category; // The Category this option belongs to
|
||||
|
||||
inline enum NumOccurrencesFlag getNumOccurrencesFlag() const {
|
||||
return (enum NumOccurrencesFlag)Occurrences;
|
||||
@ -214,13 +232,14 @@ public:
|
||||
void setFormattingFlag(enum FormattingFlags V) { Formatting = V; }
|
||||
void setMiscFlag(enum MiscFlags M) { Misc |= M; }
|
||||
void setPosition(unsigned pos) { Position = pos; }
|
||||
void setCategory(OptionCategory &C) { Category = &C; }
|
||||
protected:
|
||||
explicit Option(enum NumOccurrencesFlag OccurrencesFlag,
|
||||
enum OptionHidden Hidden)
|
||||
: NumOccurrences(0), Occurrences(OccurrencesFlag), Value(0),
|
||||
HiddenFlag(Hidden), Formatting(NormalFormatting), Misc(0),
|
||||
Position(0), AdditionalVals(0), NextRegistered(0),
|
||||
ArgStr(""), HelpStr(""), ValueStr("") {
|
||||
ArgStr(""), HelpStr(""), ValueStr(""), Category(&GeneralCategory) {
|
||||
}
|
||||
|
||||
inline void setNumAdditionalVals(unsigned n) { AdditionalVals = n; }
|
||||
@ -312,6 +331,16 @@ struct LocationClass {
|
||||
template<class Ty>
|
||||
LocationClass<Ty> location(Ty &L) { return LocationClass<Ty>(L); }
|
||||
|
||||
// cat - Specifiy the Option category for the command line argument to belong
|
||||
// to.
|
||||
struct cat {
|
||||
OptionCategory &Category;
|
||||
cat(OptionCategory &c) : Category(c) {}
|
||||
|
||||
template<class Opt>
|
||||
void apply(Opt &O) const { O.setCategory(Category); }
|
||||
};
|
||||
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// OptionValue class
|
||||
@ -1674,10 +1703,15 @@ struct extrahelp {
|
||||
};
|
||||
|
||||
void PrintVersionMessage();
|
||||
// This function just prints the help message, exactly the same way as if the
|
||||
// -help option had been given on the command line.
|
||||
// NOTE: THIS FUNCTION TERMINATES THE PROGRAM!
|
||||
void PrintHelpMessage();
|
||||
|
||||
/// This function just prints the help message, exactly the same way as if the
|
||||
/// -help or -help-hidden option had been given on the command line.
|
||||
///
|
||||
/// NOTE: THIS FUNCTION TERMINATES THE PROGRAM!
|
||||
///
|
||||
/// \param hidden if true will print hidden options
|
||||
/// \param categorized if true print options in categories
|
||||
void PrintHelpMessage(bool Hidden=false, bool Categorized=false);
|
||||
|
||||
} // End namespace cl
|
||||
|
||||
|
@ -33,6 +33,7 @@
|
||||
#include "llvm/Support/system_error.h"
|
||||
#include <cerrno>
|
||||
#include <cstdlib>
|
||||
#include <map>
|
||||
using namespace llvm;
|
||||
using namespace cl;
|
||||
|
||||
@ -106,6 +107,17 @@ void Option::addArgument() {
|
||||
MarkOptionsChanged();
|
||||
}
|
||||
|
||||
// This collects the different option categories that have been registered.
|
||||
typedef SmallPtrSet<OptionCategory*,16> OptionCatSet;
|
||||
static ManagedStatic<OptionCatSet> RegisteredOptionCategories;
|
||||
|
||||
// Initialise the general option category.
|
||||
OptionCategory llvm::cl::GeneralCategory("General options");
|
||||
|
||||
void OptionCategory::registerCategory()
|
||||
{
|
||||
RegisteredOptionCategories->insert(this);
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Basic, shared command line option processing machinery.
|
||||
@ -1222,11 +1234,20 @@ sortOpts(StringMap<Option*> &OptMap,
|
||||
namespace {
|
||||
|
||||
class HelpPrinter {
|
||||
protected:
|
||||
const bool ShowHidden;
|
||||
typedef SmallVector<std::pair<const char *, Option*>,128> StrOptionPairVector;
|
||||
// Print the options. Opts is assumed to be alphabetically sorted.
|
||||
virtual void printOptions(StrOptionPairVector &Opts, size_t MaxArgLen) {
|
||||
for (size_t i = 0, e = Opts.size(); i != e; ++i)
|
||||
Opts[i].second->printOptionInfo(MaxArgLen);
|
||||
}
|
||||
|
||||
public:
|
||||
explicit HelpPrinter(bool showHidden) : ShowHidden(showHidden) {}
|
||||
virtual ~HelpPrinter() {}
|
||||
|
||||
// Invoke the printer.
|
||||
void operator=(bool Value) {
|
||||
if (Value == false) return;
|
||||
|
||||
@ -1236,7 +1257,7 @@ public:
|
||||
StringMap<Option*> OptMap;
|
||||
GetOptionInfo(PositionalOpts, SinkOpts, OptMap);
|
||||
|
||||
SmallVector<std::pair<const char *, Option*>, 128> Opts;
|
||||
StrOptionPairVector Opts;
|
||||
sortOpts(OptMap, Opts, ShowHidden);
|
||||
|
||||
if (ProgramOverview)
|
||||
@ -1267,12 +1288,12 @@ public:
|
||||
MaxArgLen = std::max(MaxArgLen, Opts[i].second->getOptionWidth());
|
||||
|
||||
outs() << "OPTIONS:\n";
|
||||
for (size_t i = 0, e = Opts.size(); i != e; ++i)
|
||||
Opts[i].second->printOptionInfo(MaxArgLen);
|
||||
printOptions(Opts, MaxArgLen);
|
||||
|
||||
// Print any extra help the user has declared.
|
||||
for (std::vector<const char *>::iterator I = MoreHelp->begin(),
|
||||
E = MoreHelp->end(); I != E; ++I)
|
||||
E = MoreHelp->end();
|
||||
I != E; ++I)
|
||||
outs() << *I;
|
||||
MoreHelp->clear();
|
||||
|
||||
@ -1280,21 +1301,152 @@ public:
|
||||
exit(1);
|
||||
}
|
||||
};
|
||||
|
||||
class CategorizedHelpPrinter : public HelpPrinter {
|
||||
public:
|
||||
explicit CategorizedHelpPrinter(bool showHidden) : HelpPrinter(showHidden) {}
|
||||
|
||||
// Helper function for printOptions().
|
||||
// It shall return true if A's name should be lexographically
|
||||
// ordered before B's name. It returns false otherwise.
|
||||
static bool OptionCategoryCompare(OptionCategory *A, OptionCategory *B) {
|
||||
int Length = strcmp(A->getName(), B->getName());
|
||||
assert(Length != 0 && "Duplicate option categories");
|
||||
return Length < 0;
|
||||
}
|
||||
|
||||
// Make sure we inherit our base class's operator=()
|
||||
using HelpPrinter::operator= ;
|
||||
|
||||
protected:
|
||||
virtual void printOptions(StrOptionPairVector &Opts, size_t MaxArgLen) {
|
||||
std::vector<OptionCategory *> SortedCategories;
|
||||
std::map<OptionCategory *, std::vector<Option *> > CategorizedOptions;
|
||||
|
||||
// Collect registered option categories into vector in preperation for
|
||||
// sorting.
|
||||
for (OptionCatSet::const_iterator I = RegisteredOptionCategories->begin(),
|
||||
E = RegisteredOptionCategories->end();
|
||||
I != E; ++I)
|
||||
SortedCategories.push_back(*I);
|
||||
|
||||
// Sort the different option categories alphabetically.
|
||||
assert(SortedCategories.size() > 0 && "No option categories registered!");
|
||||
std::sort(SortedCategories.begin(), SortedCategories.end(),
|
||||
OptionCategoryCompare);
|
||||
|
||||
// Create map to empty vectors.
|
||||
for (std::vector<OptionCategory *>::const_iterator
|
||||
I = SortedCategories.begin(),
|
||||
E = SortedCategories.end();
|
||||
I != E; ++I)
|
||||
CategorizedOptions[*I] = std::vector<Option *>();
|
||||
|
||||
// Walk through pre-sorted options and assign into categories.
|
||||
// Because the options are already alphabetically sorted the
|
||||
// options within categories will also be alphabetically sorted.
|
||||
for (size_t I = 0, E = Opts.size(); I != E; ++I) {
|
||||
Option *Opt = Opts[I].second;
|
||||
assert(CategorizedOptions.count(Opt->Category) > 0 &&
|
||||
"Option has an unregistered category");
|
||||
CategorizedOptions[Opt->Category].push_back(Opt);
|
||||
}
|
||||
|
||||
// Now do printing.
|
||||
for (std::vector<OptionCategory *>::const_iterator
|
||||
Category = SortedCategories.begin(),
|
||||
E = SortedCategories.end();
|
||||
Category != E; ++Category) {
|
||||
// Hide empty categories for -help, but show for -help-hidden.
|
||||
bool IsEmptyCategory = CategorizedOptions[*Category].size() == 0;
|
||||
if (!ShowHidden && IsEmptyCategory)
|
||||
continue;
|
||||
|
||||
// Print category information.
|
||||
outs() << "\n";
|
||||
outs() << (*Category)->getName() << ":\n";
|
||||
|
||||
// Check if description is set.
|
||||
if ((*Category)->getDescription() != 0)
|
||||
outs() << (*Category)->getDescription() << "\n\n";
|
||||
else
|
||||
outs() << "\n";
|
||||
|
||||
// When using -help-hidden explicitly state if the category has no
|
||||
// options associated with it.
|
||||
if (IsEmptyCategory) {
|
||||
outs() << " This option category has no options.\n";
|
||||
continue;
|
||||
}
|
||||
// Loop over the options in the category and print.
|
||||
for (std::vector<Option *>::const_iterator
|
||||
Opt = CategorizedOptions[*Category].begin(),
|
||||
E = CategorizedOptions[*Category].end();
|
||||
Opt != E; ++Opt)
|
||||
(*Opt)->printOptionInfo(MaxArgLen);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// This wraps the Uncategorizing and Categorizing printers and decides
|
||||
// at run time which should be invoked.
|
||||
class HelpPrinterWrapper {
|
||||
private:
|
||||
HelpPrinter &UncategorizedPrinter;
|
||||
CategorizedHelpPrinter &CategorizedPrinter;
|
||||
|
||||
public:
|
||||
explicit HelpPrinterWrapper(HelpPrinter &UncategorizedPrinter,
|
||||
CategorizedHelpPrinter &CategorizedPrinter) :
|
||||
UncategorizedPrinter(UncategorizedPrinter),
|
||||
CategorizedPrinter(CategorizedPrinter) { }
|
||||
|
||||
// Invoke the printer.
|
||||
void operator=(bool Value);
|
||||
};
|
||||
|
||||
} // End anonymous namespace
|
||||
|
||||
// Define the two HelpPrinter instances that are used to print out help, or
|
||||
// help-hidden...
|
||||
//
|
||||
static HelpPrinter NormalPrinter(false);
|
||||
static HelpPrinter HiddenPrinter(true);
|
||||
// Declare the four HelpPrinter instances that are used to print out help, or
|
||||
// help-hidden as an uncategorized list or in categories.
|
||||
static HelpPrinter UncategorizedNormalPrinter(false);
|
||||
static HelpPrinter UncategorizedHiddenPrinter(true);
|
||||
static CategorizedHelpPrinter CategorizedNormalPrinter(false);
|
||||
static CategorizedHelpPrinter CategorizedHiddenPrinter(true);
|
||||
|
||||
|
||||
// Declare HelpPrinter wrappers that will decide whether or not to invoke
|
||||
// a categorizing help printer
|
||||
static HelpPrinterWrapper WrappedNormalPrinter(UncategorizedNormalPrinter,
|
||||
CategorizedNormalPrinter);
|
||||
static HelpPrinterWrapper WrappedHiddenPrinter(UncategorizedHiddenPrinter,
|
||||
CategorizedHiddenPrinter);
|
||||
|
||||
// Define uncategorized help printers.
|
||||
// -help-list is hidden by default because if Option categories are being used
|
||||
// then -help behaves the same as -help-list.
|
||||
static cl::opt<HelpPrinter, true, parser<bool> >
|
||||
HLOp("help-list",
|
||||
cl::desc("Display list of available options (-help-list-hidden for more)"),
|
||||
cl::location(UncategorizedNormalPrinter), cl::Hidden, cl::ValueDisallowed);
|
||||
|
||||
static cl::opt<HelpPrinter, true, parser<bool> >
|
||||
HLHOp("help-list-hidden",
|
||||
cl::desc("Display list of all available options"),
|
||||
cl::location(UncategorizedHiddenPrinter), cl::Hidden, cl::ValueDisallowed);
|
||||
|
||||
// Define uncategorized/categorized help printers. These printers change their
|
||||
// behaviour at runtime depending on whether one or more Option categories have
|
||||
// been declared.
|
||||
static cl::opt<HelpPrinterWrapper, true, parser<bool> >
|
||||
HOp("help", cl::desc("Display available options (-help-hidden for more)"),
|
||||
cl::location(NormalPrinter), cl::ValueDisallowed);
|
||||
cl::location(WrappedNormalPrinter), cl::ValueDisallowed);
|
||||
|
||||
static cl::opt<HelpPrinter, true, parser<bool> >
|
||||
static cl::opt<HelpPrinterWrapper, true, parser<bool> >
|
||||
HHOp("help-hidden", cl::desc("Display all available options"),
|
||||
cl::location(HiddenPrinter), cl::Hidden, cl::ValueDisallowed);
|
||||
cl::location(WrappedHiddenPrinter), cl::Hidden, cl::ValueDisallowed);
|
||||
|
||||
|
||||
|
||||
static cl::opt<bool>
|
||||
PrintOptions("print-options",
|
||||
@ -1306,6 +1458,24 @@ PrintAllOptions("print-all-options",
|
||||
cl::desc("Print all option values after command line parsing"),
|
||||
cl::Hidden, cl::init(false));
|
||||
|
||||
void HelpPrinterWrapper::operator=(bool Value) {
|
||||
if (Value == false)
|
||||
return;
|
||||
|
||||
// Decide which printer to invoke. If more than one option category is
|
||||
// registered then it is useful to show the categorized help instead of
|
||||
// uncategorized help.
|
||||
if (RegisteredOptionCategories->size() > 1) {
|
||||
// unhide -help-list option so user can have uncategorized output if they
|
||||
// want it.
|
||||
HLOp.setHiddenFlag(NotHidden);
|
||||
|
||||
CategorizedPrinter = true; // Invoke categorized printer
|
||||
}
|
||||
else
|
||||
UncategorizedPrinter = true; // Invoke uncategorized printer
|
||||
}
|
||||
|
||||
// Print the value of each option.
|
||||
void cl::PrintOptionValues() {
|
||||
if (!PrintOptions && !PrintAllOptions) return;
|
||||
@ -1393,14 +1563,22 @@ VersOp("version", cl::desc("Display the version of this program"),
|
||||
cl::location(VersionPrinterInstance), cl::ValueDisallowed);
|
||||
|
||||
// Utility function for printing the help message.
|
||||
void cl::PrintHelpMessage() {
|
||||
// This looks weird, but it actually prints the help message. The
|
||||
// NormalPrinter variable is a HelpPrinter and the help gets printed when
|
||||
// its operator= is invoked. That's because the "normal" usages of the
|
||||
// help printer is to be assigned true/false depending on whether the
|
||||
// -help option was given or not. Since we're circumventing that we have
|
||||
// to make it look like -help was given, so we assign true.
|
||||
NormalPrinter = true;
|
||||
void cl::PrintHelpMessage(bool Hidden, bool Categorized) {
|
||||
// This looks weird, but it actually prints the help message. The Printers are
|
||||
// types of HelpPrinter and the help gets printed when its operator= is
|
||||
// invoked. That's because the "normal" usages of the help printer is to be
|
||||
// assigned true/false depending on whether -help or -help-hidden was given or
|
||||
// not. Since we're circumventing that we have to make it look like -help or
|
||||
// -help-hidden were given, so we assign true.
|
||||
|
||||
if (!Hidden && !Categorized)
|
||||
UncategorizedNormalPrinter = true;
|
||||
else if (!Hidden && Categorized)
|
||||
CategorizedNormalPrinter = true;
|
||||
else if (Hidden && !Categorized)
|
||||
UncategorizedHiddenPrinter = true;
|
||||
else
|
||||
CategorizedHiddenPrinter = true;
|
||||
}
|
||||
|
||||
/// Utility function for printing version number.
|
||||
|
@ -66,4 +66,12 @@ 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));
|
||||
|
||||
ASSERT_EQ(&TestCategory,TestOption.Category) << "Failed to assign Option "
|
||||
"Category.";
|
||||
}
|
||||
|
||||
} // anonymous namespace
|
||||
|
Loading…
x
Reference in New Issue
Block a user