Skip to content

Commit fdbd17d

Browse files
authored
[llvm] Add subcommand support for OptTable (#155026)
Implement support for `subcommands` in OptTable to attain feature parity with `cl`. Design overview: https://discourse.llvm.org/t/subcommand-feature-support-in-llvm-opttable/88098 Issue: #108307
1 parent 8708968 commit fdbd17d

File tree

21 files changed

+794
-58
lines changed

21 files changed

+794
-58
lines changed

clang-tools-extra/clangd/CompileCommands.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -466,7 +466,7 @@ llvm::ArrayRef<ArgStripper::Rule> ArgStripper::rulesFor(llvm::StringRef Arg) {
466466
} AliasTable[] = {
467467
#define OPTION(PREFIX, PREFIXED_NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, \
468468
FLAGS, VISIBILITY, PARAM, HELPTEXT, HELPTEXTSFORVARIANTS, \
469-
METAVAR, VALUES) \
469+
METAVAR, VALUES, SUBCOMMANDIDS_OFFSET) \
470470
{DriverID::OPT_##ID, DriverID::OPT_##ALIAS, ALIASARGS},
471471
#include "clang/Driver/Options.inc"
472472
#undef OPTION

clang/lib/Frontend/CompilerInvocation.cpp

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -533,9 +533,9 @@ static T extractMaskValue(T KeyPath) {
533533
#define PARSE_OPTION_WITH_MARSHALLING( \
534534
ARGS, DIAGS, PREFIX_TYPE, SPELLING_OFFSET, ID, KIND, GROUP, ALIAS, \
535535
ALIASARGS, FLAGS, VISIBILITY, PARAM, HELPTEXT, HELPTEXTSFORVARIANTS, \
536-
METAVAR, VALUES, SHOULD_PARSE, ALWAYS_EMIT, KEYPATH, DEFAULT_VALUE, \
537-
IMPLIED_CHECK, IMPLIED_VALUE, NORMALIZER, DENORMALIZER, MERGER, EXTRACTOR, \
538-
TABLE_INDEX) \
536+
METAVAR, VALUES, SUBCOMMANDIDS_OFFSET, SHOULD_PARSE, ALWAYS_EMIT, KEYPATH, \
537+
DEFAULT_VALUE, IMPLIED_CHECK, IMPLIED_VALUE, NORMALIZER, DENORMALIZER, \
538+
MERGER, EXTRACTOR, TABLE_INDEX) \
539539
if ((VISIBILITY) & options::CC1Option) { \
540540
KEYPATH = MERGER(KEYPATH, DEFAULT_VALUE); \
541541
if (IMPLIED_CHECK) \
@@ -551,8 +551,9 @@ static T extractMaskValue(T KeyPath) {
551551
#define GENERATE_OPTION_WITH_MARSHALLING( \
552552
CONSUMER, PREFIX_TYPE, SPELLING_OFFSET, ID, KIND, GROUP, ALIAS, ALIASARGS, \
553553
FLAGS, VISIBILITY, PARAM, HELPTEXT, HELPTEXTSFORVARIANTS, METAVAR, VALUES, \
554-
SHOULD_PARSE, ALWAYS_EMIT, KEYPATH, DEFAULT_VALUE, IMPLIED_CHECK, \
555-
IMPLIED_VALUE, NORMALIZER, DENORMALIZER, MERGER, EXTRACTOR, TABLE_INDEX) \
554+
SUBCOMMANDIDS_OFFSET, SHOULD_PARSE, ALWAYS_EMIT, KEYPATH, DEFAULT_VALUE, \
555+
IMPLIED_CHECK, IMPLIED_VALUE, NORMALIZER, DENORMALIZER, MERGER, EXTRACTOR, \
556+
TABLE_INDEX) \
556557
if ((VISIBILITY) & options::CC1Option) { \
557558
[&](const auto &Extracted) { \
558559
if (ALWAYS_EMIT || \

clang/tools/clang-installapi/Options.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -208,7 +208,7 @@ enum ID {
208208
OPT_INVALID = 0, // This is not an option ID.
209209
#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, \
210210
VISIBILITY, PARAM, HELPTEXT, HELPTEXTSFORVARIANTS, METAVAR, \
211-
VALUES) \
211+
VALUES, SUBCOMMANDIDS_OFFSET) \
212212
OPT_##ID,
213213
#include "InstallAPIOpts.inc"
214214
LastOption

lld/MachO/DriverUtils.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ using namespace lld::macho;
4545
static constexpr OptTable::Info optInfo[] = {
4646
#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, \
4747
VISIBILITY, PARAM, HELPTEXT, HELPTEXTSFORVARIANTS, METAVAR, \
48-
VALUES) \
48+
VALUES, SUBCOMMANDIDS_OFFSET) \
4949
{PREFIX, \
5050
NAME, \
5151
HELPTEXT, \
@@ -59,7 +59,8 @@ static constexpr OptTable::Info optInfo[] = {
5959
OPT_##GROUP, \
6060
OPT_##ALIAS, \
6161
ALIASARGS, \
62-
VALUES},
62+
VALUES, \
63+
SUBCOMMANDIDS_OFFSET},
6364
#include "Options.inc"
6465
#undef OPTION
6566
};

lld/MinGW/Driver.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ enum {
6969
static constexpr opt::OptTable::Info infoTable[] = {
7070
#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, \
7171
VISIBILITY, PARAM, HELPTEXT, HELPTEXTSFORVARIANTS, METAVAR, \
72-
VALUES) \
72+
VALUES, SUBCOMMANDIDS_OFFSET) \
7373
{PREFIX, \
7474
NAME, \
7575
HELPTEXT, \
@@ -83,7 +83,8 @@ static constexpr opt::OptTable::Info infoTable[] = {
8383
OPT_##GROUP, \
8484
OPT_##ALIAS, \
8585
ALIASARGS, \
86-
VALUES},
86+
VALUES, \
87+
SUBCOMMANDIDS_OFFSET},
8788
#include "Options.inc"
8889
#undef OPTION
8990
};

lld/wasm/Driver.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -157,7 +157,7 @@ bool link(ArrayRef<const char *> args, llvm::raw_ostream &stdoutOS,
157157
static constexpr opt::OptTable::Info optInfo[] = {
158158
#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, \
159159
VISIBILITY, PARAM, HELPTEXT, HELPTEXTSFORVARIANTS, METAVAR, \
160-
VALUES) \
160+
VALUES, SUBCOMMANDIDS_OFFSET) \
161161
{PREFIX, \
162162
NAME, \
163163
HELPTEXT, \
@@ -171,7 +171,8 @@ static constexpr opt::OptTable::Info optInfo[] = {
171171
OPT_##GROUP, \
172172
OPT_##ALIAS, \
173173
ALIASARGS, \
174-
VALUES},
174+
VALUES, \
175+
SUBCOMMANDIDS_OFFSET},
175176
#include "Options.inc"
176177
#undef OPTION
177178
};

llvm/examples/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ add_subdirectory(ModuleMaker)
88
add_subdirectory(OrcV2Examples)
99
add_subdirectory(SpeculativeJIT)
1010
add_subdirectory(Bye)
11+
add_subdirectory(OptSubcommand)
1112

1213
if(LLVM_ENABLE_EH AND (NOT WIN32) AND (NOT "${LLVM_NATIVE_ARCH}" STREQUAL "ARM"))
1314
add_subdirectory(ExceptionDemo)
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
# Set the .td file to be processed for this target.
2+
set(LLVM_TARGET_DEFINITIONS Opts.td)
3+
4+
tablegen(LLVM Opts.inc -gen-opt-parser-defs)
5+
add_public_tablegen_target(HelloSubTableGen)
6+
7+
set(LLVM_LINK_COMPONENTS
8+
Support
9+
Option
10+
)
11+
12+
add_llvm_example(OptSubcommand
13+
llvm-hello-sub.cpp
14+
)
15+
16+
target_include_directories(OptSubcommand
17+
PRIVATE
18+
${CMAKE_CURRENT_BINARY_DIR}
19+
)
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
include "llvm/Option/OptParser.td"
2+
3+
def sc_foo : SubCommand<"foo", "HelpText for SubCommand foo.">;
4+
5+
def sc_bar : SubCommand<"bar", "HelpText for SubCommand bar.",
6+
"OptSubcommand bar <options>">;
7+
8+
def help : Flag<["--"], "help">,
9+
HelpText<"OptSubcommand <subcommand> <options>">;
10+
11+
def version : Flag<["-"], "version">,
12+
HelpText<"Toplevel Display the version number">;
13+
14+
def uppercase : Flag<["-"], "uppercase", [sc_foo, sc_bar]>,
15+
HelpText<"Print in uppercase">;
16+
17+
def lowercase : Flag<["-"], "lowercase", [sc_foo]>,
18+
HelpText<"Print in lowercase">;
Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
#include "llvm/ADT/ArrayRef.h"
2+
#include "llvm/ADT/StringRef.h"
3+
#include "llvm/Option/ArgList.h"
4+
#include "llvm/Option/OptTable.h"
5+
#include "llvm/Support/Error.h"
6+
#include "llvm/Support/InitLLVM.h"
7+
#include "llvm/Support/raw_ostream.h"
8+
9+
using namespace llvm;
10+
using namespace llvm::opt;
11+
12+
namespace {
13+
enum ID {
14+
OPT_INVALID = 0,
15+
#define OPTION(PREFIXES, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, \
16+
VISIBILITY, PARAM, HELPTEXT, HELPTEXTSFORVARIANTS, METAVAR, \
17+
VALUES, SUBCOMMANDIDS_OFFSET) \
18+
OPT_##ID,
19+
#include "Opts.inc"
20+
#undef OPTION
21+
};
22+
#define OPTTABLE_STR_TABLE_CODE
23+
#include "Opts.inc"
24+
#undef OPTTABLE_STR_TABLE_CODE
25+
26+
#define OPTTABLE_PREFIXES_TABLE_CODE
27+
#include "Opts.inc"
28+
#undef OPTTABLE_PREFIXES_TABLE_CODE
29+
30+
#define OPTTABLE_SUBCOMMAND_IDS_TABLE_CODE
31+
#include "Opts.inc"
32+
#undef OPTTABLE_SUBCOMMAND_IDS_TABLE_CODE
33+
34+
#define OPTTABLE_SUBCOMMANDS_CODE
35+
#include "Opts.inc"
36+
#undef OPTTABLE_SUBCOMMANDS_CODE
37+
38+
static constexpr OptTable::Info InfoTable[] = {
39+
#define OPTION(...) LLVM_CONSTRUCT_OPT_INFO(__VA_ARGS__),
40+
#include "Opts.inc"
41+
#undef OPTION
42+
};
43+
44+
class HelloSubOptTable : public GenericOptTable {
45+
public:
46+
HelloSubOptTable()
47+
: GenericOptTable(OptionStrTable, OptionPrefixesTable, InfoTable,
48+
/*IgnoreCase=*/false, OptionSubCommands,
49+
OptionSubCommandIDsTable) {}
50+
};
51+
} // namespace
52+
53+
int main(int argc, char **argv) {
54+
InitLLVM X(argc, argv);
55+
HelloSubOptTable T;
56+
unsigned MissingArgIndex, MissingArgCount;
57+
58+
auto HandleMultipleSubcommands = [](ArrayRef<StringRef> SubCommands) {
59+
assert(SubCommands.size() > 1);
60+
llvm::errs() << "error: more than one subcommand passed [\n";
61+
for (auto SC : SubCommands)
62+
llvm::errs() << " `" << SC << "`\n";
63+
llvm::errs() << "]\n";
64+
llvm::errs() << "See --help.\n";
65+
exit(1);
66+
};
67+
68+
auto HandleOtherPositionals = [](ArrayRef<StringRef> Positionals) {
69+
assert(!Positionals.empty());
70+
llvm::errs() << "error: unknown positional argument(s) [\n";
71+
for (auto SC : Positionals)
72+
llvm::errs() << " `" << SC << "`\n";
73+
llvm::errs() << "]\n";
74+
llvm::errs() << "See --help.\n";
75+
exit(1);
76+
};
77+
78+
InputArgList Args = T.ParseArgs(ArrayRef(argv + 1, argc - 1), MissingArgIndex,
79+
MissingArgCount);
80+
81+
StringRef SubCommand = Args.getSubCommand(
82+
T.getSubCommands(), HandleMultipleSubcommands, HandleOtherPositionals);
83+
// Handle help. When help options is found, ignore all other options and exit
84+
// after printing help.
85+
86+
if (Args.hasArg(OPT_help)) {
87+
T.printHelp(llvm::outs(), "llvm-hello-sub [subcommand] [options]",
88+
"LLVM Hello SubCommand Example", false, false, Visibility(),
89+
SubCommand);
90+
return 0;
91+
}
92+
93+
auto HandleSubCommandArg = [&](ID OptionType) {
94+
if (!Args.hasArg(OptionType))
95+
return false;
96+
auto O = T.getOption(OptionType);
97+
if (!O.isRegisteredSC(SubCommand)) {
98+
llvm::errs() << "Option [" << O.getName()
99+
<< "] is not valid for SubCommand [" << SubCommand << "]\n";
100+
return false;
101+
}
102+
return true;
103+
};
104+
105+
bool HasUnknownOptions = false;
106+
for (const Arg *A : Args.filtered(OPT_UNKNOWN)) {
107+
HasUnknownOptions = true;
108+
llvm::errs() << "Unknown option `" << A->getAsString(Args) << "'\n";
109+
}
110+
if (HasUnknownOptions) {
111+
llvm::errs() << "See `OptSubcommand --help`.\n";
112+
return 1;
113+
}
114+
if (SubCommand.empty()) {
115+
if (Args.hasArg(OPT_version))
116+
llvm::outs() << "LLVM Hello SubCommand Example 1.0\n";
117+
} else if (SubCommand == "foo") {
118+
if (HandleSubCommandArg(OPT_uppercase))
119+
llvm::outs() << "FOO\n";
120+
else if (HandleSubCommandArg(OPT_lowercase))
121+
llvm::outs() << "foo\n";
122+
123+
if (HandleSubCommandArg(OPT_version))
124+
llvm::outs() << "LLVM Hello SubCommand foo Example 1.0\n";
125+
126+
} else if (SubCommand == "bar") {
127+
if (HandleSubCommandArg(OPT_lowercase))
128+
llvm::outs() << "bar\n";
129+
else if (HandleSubCommandArg(OPT_uppercase))
130+
llvm::outs() << "BAR\n";
131+
132+
if (HandleSubCommandArg(OPT_version))
133+
llvm::outs() << "LLVM Hello SubCommand bar Example 1.0\n";
134+
}
135+
136+
return 0;
137+
}

0 commit comments

Comments
 (0)