-
Notifications
You must be signed in to change notification settings - Fork 15.2k
[llvm] Add subcommand support for OptTable #155026
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 24 commits
3584c6a
8665d9e
d485046
b325c78
61334ef
1690f3b
9c419d9
19e6437
8d5363b
51ef95e
043bbc8
bc8bd1b
dcb6779
e7ea647
ff8034a
e99a9a0
a480c0c
d41c2ae
c1cddc9
e28d2b0
3bc51b7
1feae67
66af1aa
b907a9c
9485199
5c31cea
5995d18
064868a
c5a0cf5
dfc592e
8d11673
db25a6a
ebdb5b8
3d7c81e
845a073
396a041
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
# Set the .td file to be processed for this target. | ||
set(LLVM_TARGET_DEFINITIONS Opts.td) | ||
|
||
tablegen(LLVM Opts.inc -gen-opt-parser-defs) | ||
add_public_tablegen_target(HelloSubTableGen) | ||
|
||
set(LLVM_LINK_COMPONENTS | ||
Support | ||
Option | ||
) | ||
|
||
add_llvm_example(OptSubcommand | ||
llvm-hello-sub.cpp | ||
) | ||
|
||
target_include_directories(OptSubcommand | ||
PRIVATE | ||
${CMAKE_CURRENT_BINARY_DIR} | ||
) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
include "llvm/Option/OptParser.td" | ||
|
||
def sc_foo : SubCommand<"foo", "HelpText for SubCommand foo.">; | ||
|
||
def sc_bar : SubCommand<"bar", "HelpText for SubCommand bar.", | ||
"OptSubcommand bar <options>">; | ||
|
||
def help : Flag<["--"], "help">, | ||
HelpText<"OptSubcommand <subcommand> <options>">; | ||
|
||
def version : Flag<["-"], "version">, | ||
HelpText<"Toplevel Display the version number">; | ||
|
||
def uppercase : Flag<["-"], "uppercase", [sc_foo, sc_bar]>, | ||
HelpText<"Print in uppercase">; | ||
|
||
def lowercase : Flag<["-"], "lowercase", [sc_foo]>, | ||
HelpText<"Print in lowercase">; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,124 @@ | ||
#include "llvm/ADT/ArrayRef.h" | ||
#include "llvm/ADT/StringRef.h" | ||
#include "llvm/Option/ArgList.h" | ||
#include "llvm/Option/OptTable.h" | ||
#include "llvm/Support/Error.h" | ||
#include "llvm/Support/InitLLVM.h" | ||
#include "llvm/Support/raw_ostream.h" | ||
|
||
using namespace llvm; | ||
using namespace llvm::opt; | ||
|
||
namespace { | ||
enum ID { | ||
OPT_INVALID = 0, | ||
#define OPTION(PREFIXES, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, \ | ||
VISIBILITY, PARAM, HELPTEXT, HELPTEXTSFORVARIANTS, METAVAR, \ | ||
VALUES, SUBCOMMANDIDS_OFFSET) \ | ||
OPT_##ID, | ||
#include "Opts.inc" | ||
#undef OPTION | ||
}; | ||
#define OPTTABLE_STR_TABLE_CODE | ||
#include "Opts.inc" | ||
#undef OPTTABLE_STR_TABLE_CODE | ||
|
||
#define OPTTABLE_PREFIXES_TABLE_CODE | ||
#include "Opts.inc" | ||
#undef OPTTABLE_PREFIXES_TABLE_CODE | ||
|
||
#define OPTTABLE_SUBCOMMAND_IDS_TABLE_CODE | ||
#include "Opts.inc" | ||
#undef OPTTABLE_SUBCOMMAND_IDS_TABLE_CODE | ||
|
||
#define OPTTABLE_SUBCOMMANDS_CODE | ||
#include "Opts.inc" | ||
#undef OPTTABLE_SUBCOMMANDS_CODE | ||
|
||
static constexpr OptTable::Info InfoTable[] = { | ||
#define OPTION(...) LLVM_CONSTRUCT_OPT_INFO(__VA_ARGS__), | ||
#include "Opts.inc" | ||
#undef OPTION | ||
}; | ||
|
||
class HelloSubOptTable : public GenericOptTable { | ||
public: | ||
HelloSubOptTable() | ||
: GenericOptTable(OptionStrTable, OptionPrefixesTable, InfoTable, | ||
/*IgnoreCase=*/false, OptionSubCommands, | ||
OptionSubCommandIDsTable) {} | ||
}; | ||
} // namespace | ||
|
||
int main(int argc, char **argv) { | ||
InitLLVM X(argc, argv); | ||
HelloSubOptTable T; | ||
unsigned MissingArgIndex, MissingArgCount; | ||
|
||
auto HandleMultipleSubcommands = [](ArrayRef<StringRef> SubCommands) { | ||
assert(SubCommands.size() > 1); | ||
llvm::errs() << "error: more than one subcommand passed [\n"; | ||
for (auto SC : SubCommands) | ||
llvm::errs() << " `" << SC << "`\n"; | ||
llvm::errs() << "]\n"; | ||
llvm::errs() << "See --help.\n"; | ||
exit(1); | ||
}; | ||
|
||
auto HandleOtherPositionals = [](ArrayRef<StringRef> Positionals) { | ||
assert(!Positionals.empty()); | ||
llvm::errs() << "error: unknown positional argument(s) [\n"; | ||
for (auto SC : Positionals) | ||
llvm::errs() << " `" << SC << "`\n"; | ||
llvm::errs() << "]\n"; | ||
llvm::errs() << "See --help.\n"; | ||
exit(1); | ||
}; | ||
|
||
InputArgList Args = T.ParseArgs(ArrayRef(argv + 1, argc - 1), MissingArgIndex, | ||
MissingArgCount); | ||
|
||
StringRef Subcommand = Args.getSubcommand( | ||
T.getSubCommands(), HandleMultipleSubcommands, HandleOtherPositionals); | ||
// Handle help. When help options is found, ignore all other options and exit | ||
// after printing help. | ||
|
||
if (Args.hasArg(OPT_help)) { | ||
T.printHelp(llvm::outs(), "llvm-hello-sub [subcommand] [options]", | ||
"LLVM Hello Subcommand Example", false, false, Visibility(), | ||
Subcommand); | ||
return 0; | ||
} | ||
bool HasUnknownOptions = false; | ||
for (const Arg *A : Args.filtered(OPT_UNKNOWN)) { | ||
HasUnknownOptions = true; | ||
llvm::errs() << "Unknown option `" << A->getAsString(Args) << "'\n"; | ||
} | ||
if (HasUnknownOptions) { | ||
llvm::errs() << "See `OptSubcommand --help`.\n"; | ||
return 1; | ||
} | ||
if (Subcommand.empty()) { | ||
if (Args.hasArg(OPT_version)) | ||
llvm::outs() << "LLVM Hello Subcommand Example 1.0\n"; | ||
} else if (Subcommand == "foo") { | ||
if (Args.hasArg(OPT_uppercase)) | ||
llvm::outs() << "FOO\n"; | ||
else if (Args.hasArg(OPT_lowercase)) | ||
llvm::outs() << "foo\n"; | ||
|
||
if (Args.hasArg(OPT_version)) | ||
llvm::outs() << "LLVM Hello Subcommand foo Example 1.0\n"; | ||
|
||
} else if (Subcommand == "bar") { | ||
if (Args.hasArg(OPT_lowercase)) | ||
llvm::outs() << "bar\n"; | ||
else if (Args.hasArg(OPT_uppercase)) | ||
llvm::outs() << "BAR\n"; | ||
|
||
if (Args.hasArg(OPT_version)) | ||
llvm::outs() << "LLVM Hello Subcommand bar Example 1.0\n"; | ||
} | ||
|
||
return 0; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -20,6 +20,7 @@ | |
#include "llvm/Option/OptSpecifier.h" | ||
#include "llvm/Option/Option.h" | ||
#include "llvm/Support/Compiler.h" | ||
#include "llvm/Support/Error.h" | ||
#include <algorithm> | ||
#include <cstddef> | ||
#include <initializer_list> | ||
|
@@ -280,6 +281,22 @@ class ArgList { | |
/// list. | ||
virtual unsigned getNumInputArgStrings() const = 0; | ||
|
||
/// getSubcommand - Find subcommand from the arguments if the usage is valid. | ||
/// | ||
/// \param AllSubCommands - A list of all valid subcommands. | ||
/// \param HandleMultipleSubcommands - A callback for the case where multiple | ||
/// subcommands are present in the arguments. It gets a list of all found | ||
/// subcommands. | ||
/// \param HandleOtherPositionals - A callback for the case where positional | ||
/// arguments that are not subcommands are present. | ||
/// \return The name of the subcommand found. If no subcommand is found, | ||
/// this returns an empty StringRef. If multiple subcommands are found, the | ||
/// first one is returned. | ||
StringRef getSubcommand( | ||
ArrayRef<OptTable::SubCommand> AllSubCommands, | ||
std::function<void(ArrayRef<StringRef>)> HandleMultipleSubcommands, | ||
std::function<void(ArrayRef<StringRef>)> HandleOtherPositionals) const; | ||
Comment on lines
+297
to
+298
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe add comments above briefly explaining what these callbacks are for. Mostly requesting since the implication to me (based on the example usage) is that a user might want to just throw an error and exit. If this is the primary/only desirable case, then that might be worth noting. I personally can't think of a reason one might not want to print help and exit, but maybe someone else might have a reason to. We could probably discourage that with comments or maybe saying the callback is NoReturn if we want. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done. PTAL. |
||
|
||
/// @} | ||
/// @name Argument Lookup Utilities | ||
/// @{ | ||
|
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Rather than having a directory in
examples
dedicated to just subcommands, I think we should a directory forOption
and subcommands should be just one of the examples in there.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll start a new PR for that change.