Skip to content

Commit

Permalink
Update llvm command line parser to support subcommands.
Browse files Browse the repository at this point in the history
This allows command line tools to use syntaxes like the following:

  llvm-foo.exe command1 -o1 -o2
  llvm-foo.exe command2 -p1 -p2

Where command1 and command2 contain completely different sets of
valid options.  This is backwards compatible with previous uses
of llvm cl which did not support subcommands, as any option
which specifies no optional subcommand (e.g. all existing
code) goes into a special "top level" subcommand that expects
dashed options to appear immediately after the program name.
For example, code which is subcommand unaware would generate
a command line such as the following, where no subcommand
is specified:

  llvm-foo.exe -q1 -q2

The top level subcommand can co-exist with actual subcommands,
as it is implemented as an actual subcommand which is searched
if no explicit subcommand is specified.  So llvm-foo.exe as
specified above could be written so as to support all three
aforementioned command lines simultaneously.

There is one additional "special" subcommand called AllSubCommands,
which can be used to inject an option into every subcommand.
This is useful to support things like help, so that commands
such as:

  llvm-foo.exe --help
  llvm-foo.exe command1 --help
  llvm-foo.exe command2 --help

All work and display the help for the selected subcommand
without having to explicitly go and write code to handle each
one separately.

This patch is submitted without an example of anything actually
using subcommands, but a followup patch will convert the
llvm-pdbdump tool to use subcommands.

Reviewed By: beanz
Differential Revision: http://reviews.llvm.org/D21485

git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@274054 91177308-0d34-0410-b5e6-96231b3b80d8
  • Loading branch information
Zachary Turner committed Jun 28, 2016
1 parent c90294d commit d6a559c
Show file tree
Hide file tree
Showing 4 changed files with 654 additions and 108 deletions.
78 changes: 73 additions & 5 deletions include/llvm/Support/CommandLine.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,12 @@
#define LLVM_SUPPORT_COMMANDLINE_H

#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/SmallPtrSet.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/StringMap.h"
#include "llvm/ADT/Twine.h"
#include "llvm/Support/Compiler.h"
#include "llvm/Support/ManagedStatic.h"
#include <cassert>
#include <climits>
#include <cstdarg>
Expand All @@ -43,8 +45,9 @@ namespace cl {
//===----------------------------------------------------------------------===//
// ParseCommandLineOptions - Command line option processing entry point.
//
void ParseCommandLineOptions(int argc, const char *const *argv,
const char *Overview = nullptr);
bool ParseCommandLineOptions(int argc, const char *const *argv,
const char *Overview = nullptr,
bool IgnoreErrors = false);

//===----------------------------------------------------------------------===//
// ParseEnvironmentOptions - Environment variable option processing alternate
Expand Down Expand Up @@ -170,6 +173,45 @@ class OptionCategory {
// The general Option Category (used as default category).
extern OptionCategory GeneralCategory;

//===----------------------------------------------------------------------===//
// SubCommand class
//
class SubCommand {
private:
const char *const Name = nullptr;
const char *const Description = nullptr;

protected:
void registerSubCommand();
void unregisterSubCommand();

public:
SubCommand(const char *const Name, const char *const Description = nullptr)
: Name(Name), Description(Description) {
registerSubCommand();
}
SubCommand() {}

void reset();

operator bool() const;

const char *getName() const { return Name; }
const char *getDescription() const { return Description; }

SmallVector<Option *, 4> PositionalOpts;
SmallVector<Option *, 4> SinkOpts;
StringMap<Option *> OptionsMap;

Option *ConsumeAfterOpt = nullptr; // The ConsumeAfter option if it exists.
};

// A special subcommand representing no subcommand
extern ManagedStatic<SubCommand> TopLevelSubCommand;

// A special subcommand that can be used to put an option into all subcommands.
extern ManagedStatic<SubCommand> AllSubCommands;

//===----------------------------------------------------------------------===//
// Option Base class
//
Expand Down Expand Up @@ -209,6 +251,7 @@ class Option {
StringRef HelpStr; // The descriptive text message for -help
StringRef ValueStr; // String describing what the value of this option is
OptionCategory *Category; // The Category this option belongs to
SmallPtrSet<SubCommand *, 4> Subs; // The subcommands this option belongs to.
bool FullyInitialized; // Has addArguemnt been called?

inline enum NumOccurrencesFlag getNumOccurrencesFlag() const {
Expand All @@ -229,6 +272,16 @@ class Option {

// hasArgStr - Return true if the argstr != ""
bool hasArgStr() const { return !ArgStr.empty(); }
bool isPositional() const { return getFormattingFlag() == cl::Positional; }
bool isSink() const { return getMiscFlags() & cl::Sink; }
bool isConsumeAfter() const {
return getNumOccurrencesFlag() == cl::ConsumeAfter;
}
bool isInAllSubCommands() const {
return std::any_of(Subs.begin(), Subs.end(), [](const SubCommand *SC) {
return SC == &*AllSubCommands;
});
}

//-------------------------------------------------------------------------===
// Accessor functions set by OptionModifiers
Expand All @@ -243,6 +296,7 @@ class Option {
void setMiscFlag(enum MiscFlags M) { Misc |= M; }
void setPosition(unsigned pos) { Position = pos; }
void setCategory(OptionCategory &C) { Category = &C; }
void addSubCommand(SubCommand &S) { Subs.insert(&S); }

protected:
explicit Option(enum NumOccurrencesFlag OccurrencesFlag,
Expand Down Expand Up @@ -287,6 +341,7 @@ class Option {

public:
inline int getNumOccurrences() const { return NumOccurrences; }
inline void reset() { NumOccurrences = 0; }
virtual ~Option() {}
};

Expand Down Expand Up @@ -349,6 +404,14 @@ struct cat {
template <class Opt> void apply(Opt &O) const { O.setCategory(Category); }
};

// sub - Specify the subcommand that this option belongs to.
struct sub {
SubCommand &Sub;
sub(SubCommand &S) : Sub(S) {}

template <class Opt> void apply(Opt &O) const { O.addSubCommand(Sub); }
};

//===----------------------------------------------------------------------===//
// OptionValue class

Expand Down Expand Up @@ -1589,6 +1652,7 @@ class alias : public Option {
error("cl::alias must have argument name specified!");
if (!AliasFor)
error("cl::alias must have an cl::aliasopt(option) specified!");
Subs = AliasFor->Subs;
addArgument();
}

Expand Down Expand Up @@ -1669,7 +1733,7 @@ void PrintHelpMessage(bool Hidden = false, bool Categorized = false);
/// Hopefully this API can be depricated soon. Any situation where options need
/// to be modified by tools or libraries should be handled by sane APIs rather
/// than just handing around a global list.
StringMap<Option *> &getRegisteredOptions();
StringMap<Option *> &getRegisteredOptions(SubCommand &Sub);

//===----------------------------------------------------------------------===//
// Standalone command line processing utilities.
Expand Down Expand Up @@ -1737,7 +1801,8 @@ bool ExpandResponseFiles(StringSaver &Saver, TokenizerCallback Tokenizer,
/// Some tools (like clang-format) like to be able to hide all options that are
/// not specific to the tool. This function allows a tool to specify a single
/// option category to display in the -help output.
void HideUnrelatedOptions(cl::OptionCategory &Category);
void HideUnrelatedOptions(cl::OptionCategory &Category,
SubCommand &Sub = *TopLevelSubCommand);

/// \brief Mark all options not part of the categories as cl::ReallyHidden.
///
Expand All @@ -1746,7 +1811,10 @@ void HideUnrelatedOptions(cl::OptionCategory &Category);
/// Some tools (like clang-format) like to be able to hide all options that are
/// not specific to the tool. This function allows a tool to specify a single
/// option category to display in the -help output.
void HideUnrelatedOptions(ArrayRef<const cl::OptionCategory *> Categories);
void HideUnrelatedOptions(ArrayRef<const cl::OptionCategory *> Categories,
SubCommand &Sub = *TopLevelSubCommand);

void ResetCommandLineOptions();

} // End namespace cl

Expand Down
Loading

0 comments on commit d6a559c

Please sign in to comment.