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:
Andrew Trick
2013-05-06 21:56:23 +00:00
parent b072090f39
commit b7ad33b719
4 changed files with 323 additions and 28 deletions

View File

@@ -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.