1 //===- LLDBOptionDefEmitter.cpp -------------------------------------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 //
9 // These tablegen backends emits LLDB's OptionDefinition values for different
10 // LLDB commands.
11 //
12 //===----------------------------------------------------------------------===//
13
14 #include "LLDBTableGenBackends.h"
15 #include "LLDBTableGenUtils.h"
16 #include "llvm/ADT/StringExtras.h"
17 #include "llvm/TableGen/Record.h"
18 #include "llvm/TableGen/StringMatcher.h"
19 #include "llvm/TableGen/TableGenBackend.h"
20 #include <vector>
21
22 using namespace llvm;
23 using namespace lldb_private;
24
25 namespace {
26 struct CommandOption {
27 std::vector<std::string> GroupsArg;
28 bool Required = false;
29 std::string FullName;
30 std::string ShortName;
31 std::string ArgType;
32 bool OptionalArg = false;
33 std::string Validator;
34 std::string ArgEnum;
35 std::vector<StringRef> Completions;
36 std::string Description;
37
38 CommandOption() = default;
CommandOption__anon8aaff4150111::CommandOption39 CommandOption(Record *Option) {
40 if (Option->getValue("Groups")) {
41 // The user specified a list of groups.
42 auto Groups = Option->getValueAsListOfInts("Groups");
43 for (int Group : Groups)
44 GroupsArg.push_back("LLDB_OPT_SET_" + std::to_string(Group));
45 } else if (Option->getValue("GroupStart")) {
46 // The user specified a range of groups (with potentially only one
47 // element).
48 int GroupStart = Option->getValueAsInt("GroupStart");
49 int GroupEnd = Option->getValueAsInt("GroupEnd");
50 for (int i = GroupStart; i <= GroupEnd; ++i)
51 GroupsArg.push_back("LLDB_OPT_SET_" + std::to_string(i));
52 }
53
54 // Check if this option is required.
55 Required = Option->getValue("Required");
56
57 // Add the full and short name for this option.
58 FullName = std::string(Option->getValueAsString("FullName"));
59 ShortName = std::string(Option->getValueAsString("ShortName"));
60
61 if (auto A = Option->getValue("ArgType"))
62 ArgType = A->getValue()->getAsUnquotedString();
63 OptionalArg = Option->getValue("OptionalArg") != nullptr;
64
65 if (Option->getValue("Validator"))
66 Validator = std::string(Option->getValueAsString("Validator"));
67
68 if (Option->getValue("ArgEnum"))
69 ArgEnum = std::string(Option->getValueAsString("ArgEnum"));
70
71 if (Option->getValue("Completions"))
72 Completions = Option->getValueAsListOfStrings("Completions");
73
74 if (auto D = Option->getValue("Description"))
75 Description = D->getValue()->getAsUnquotedString();
76 }
77 };
78 } // namespace
79
emitOption(const CommandOption & O,raw_ostream & OS)80 static void emitOption(const CommandOption &O, raw_ostream &OS) {
81 OS << " {";
82
83 // If we have any groups, we merge them. Otherwise we move this option into
84 // the all group.
85 if (O.GroupsArg.empty())
86 OS << "LLDB_OPT_SET_ALL";
87 else
88 OS << llvm::join(O.GroupsArg.begin(), O.GroupsArg.end(), " | ");
89
90 OS << ", ";
91
92 // Check if this option is required.
93 OS << (O.Required ? "true" : "false");
94
95 // Add the full and short name for this option.
96 OS << ", \"" << O.FullName << "\", ";
97 OS << '\'' << O.ShortName << "'";
98
99 // Decide if we have either an option, required or no argument for this
100 // option.
101 OS << ", OptionParser::";
102 if (!O.ArgType.empty()) {
103 if (O.OptionalArg)
104 OS << "eOptionalArgument";
105 else
106 OS << "eRequiredArgument";
107 } else
108 OS << "eNoArgument";
109 OS << ", ";
110
111 if (!O.Validator.empty())
112 OS << O.Validator;
113 else
114 OS << "nullptr";
115 OS << ", ";
116
117 if (!O.ArgEnum.empty())
118 OS << O.ArgEnum;
119 else
120 OS << "{}";
121 OS << ", ";
122
123 // Read the tab completions we offer for this option (if there are any)
124 if (!O.Completions.empty()) {
125 std::vector<std::string> CompletionArgs;
126 for (llvm::StringRef Completion : O.Completions)
127 CompletionArgs.push_back("CommandCompletions::e" + Completion.str() +
128 "Completion");
129
130 OS << llvm::join(CompletionArgs.begin(), CompletionArgs.end(), " | ");
131 } else
132 OS << "CommandCompletions::eNoCompletion";
133
134 // Add the argument type.
135 OS << ", eArgType";
136 if (!O.ArgType.empty()) {
137 OS << O.ArgType;
138 } else
139 OS << "None";
140 OS << ", ";
141
142 // Add the description if there is any.
143 if (!O.Description.empty()) {
144 OS << "\"";
145 llvm::printEscapedString(O.Description, OS);
146 OS << "\"";
147 } else
148 OS << "\"\"";
149 OS << "},\n";
150 }
151
152 /// Emits all option initializers to the raw_ostream.
emitOptions(std::string Command,std::vector<Record * > Records,raw_ostream & OS)153 static void emitOptions(std::string Command, std::vector<Record *> Records,
154 raw_ostream &OS) {
155 std::vector<CommandOption> Options;
156 for (Record *R : Records)
157 Options.emplace_back(R);
158
159 std::string ID = Command;
160 std::replace(ID.begin(), ID.end(), ' ', '_');
161 // Generate the macro that the user needs to define before including the
162 // *.inc file.
163 std::string NeededMacro = "LLDB_OPTIONS_" + ID;
164
165 // All options are in one file, so we need put them behind macros and ask the
166 // user to define the macro for the options that are needed.
167 OS << "// Options for " << Command << "\n";
168 OS << "#ifdef " << NeededMacro << "\n";
169 OS << "constexpr static OptionDefinition g_" + ID + "_options[] = {\n";
170 for (CommandOption &CO : Options)
171 emitOption(CO, OS);
172 // We undefine the macro for the user like Clang's include files are doing it.
173 OS << "};\n";
174 OS << "#undef " << NeededMacro << "\n";
175 OS << "#endif // " << Command << " command\n\n";
176 }
177
EmitOptionDefs(RecordKeeper & Records,raw_ostream & OS)178 void lldb_private::EmitOptionDefs(RecordKeeper &Records, raw_ostream &OS) {
179 emitSourceFileHeader("Options for LLDB command line commands.", OS);
180
181 std::vector<Record *> Options = Records.getAllDerivedDefinitions("Option");
182 for (auto &CommandRecordPair : getRecordsByName(Options, "Command")) {
183 emitOptions(CommandRecordPair.first, CommandRecordPair.second, OS);
184 }
185 }
186