-
Notifications
You must be signed in to change notification settings - Fork 75
Expand file tree
/
Copy pathargument_parser.cpp
More file actions
381 lines (330 loc) · 12.8 KB
/
Copy pathargument_parser.cpp
File metadata and controls
381 lines (330 loc) · 12.8 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
#include "command/argument_parser.h"
#include <span>
#include <string_view>
#include <utility>
#include <kota/deco/option.h>
#include "llvm/ADT/StringExtras.h"
#include "llvm/ADT/StringTable.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/raw_ostream.h"
#include "clang/Driver/Driver.h"
#include "clang/Driver/Types.h"
namespace clice {
namespace option {
namespace eo = kota::option;
namespace detail {
#define OPTTABLE_STR_TABLE_CODE
#include "clang/Driver/Options.inc"
#undef OPTTABLE_STR_TABLE_CODE
#define OPTTABLE_PREFIXES_TABLE_CODE
#include "clang/Driver/Options.inc"
#undef OPTTABLE_PREFIXES_TABLE_CODE
static_assert(OptionPrefixesTable[0].value() == 0, "pfx_none: 0 prefixes");
static_assert(OptionPrefixesTable[1].value() == 1, "pfx_dash: 1 prefix");
static_assert(OptionPrefixesTable[3].value() == 2, "pfx_dash_double: 2 prefixes");
static_assert(OptionPrefixesTable[6].value() == 1, "pfx_double: 1 prefix");
static_assert(OptionPrefixesTable[8].value() == 3, "pfx_all: 3 prefixes");
static_assert(OptionPrefixesTable[12].value() == 2, "pfx_slash_dash: 2 prefixes");
constexpr std::span<const std::string_view> prefixes(unsigned offset) {
switch(offset) {
case 0: return eo::pfx_none;
case 1: return eo::pfx_dash;
case 3: return eo::pfx_dash_double;
case 6: return eo::pfx_double;
case 8: return eo::pfx_all;
case 12: return eo::pfx_slash_dash;
default: std::unreachable();
}
}
constexpr std::string_view str_at(unsigned offset) {
auto ref = OptionStrTable[llvm::StringTable::Offset(offset)];
return {ref.data(), ref.size()};
}
} // namespace detail
const eo::OptTable& table() {
using enum eo::Kind;
using detail::prefixes;
using detail::str_at;
enum ClangDriverFlag : unsigned {
HelpHidden = eo::HelpHidden,
RenderAsInput = eo::RenderAsInput,
RenderJoined = eo::RenderJoined,
Ignored = 1u << 4,
LinkOption = 1u << 5,
LinkerInput = 1u << 6,
NoArgumentUnused = 1u << 7,
NoXarchOption = 1u << 8,
TargetSpecific = 1u << 9,
Unsupported = 1u << 10,
};
constexpr static eo::Option option_infos[] = {
#define OPTION(PREFIXES_OFFSET, \
NAME_OFFSET, \
ID, \
KIND, \
GROUP, \
ALIAS, \
ALIAS_ARGS, \
FLAGS, \
VISIBILITY, \
PARAM, \
HELP, \
HELP_TEXTS, \
META_VAR, \
VALUES) \
eo::Option{ \
.prefixes = prefixes(PREFIXES_OFFSET), \
.prefixed_name = str_at(NAME_OFFSET), \
.id = OPT_##ID, \
.kind = KIND, \
.group_id = OPT_##GROUP, \
.alias_id = OPT_##ALIAS, \
.alias_args = ALIAS_ARGS, \
.flags = FLAGS, \
.visibility = VISIBILITY, \
.num_args = PARAM, \
.help_text = HELP, \
.meta_var = META_VAR, \
},
#include "clang/Driver/Options.inc"
#undef OPTION
};
const static auto opt_table = [] {
auto t = eo::OptTable(std::span(option_infos));
t.tablegen_mode = true;
return t;
}();
return opt_table;
}
} // namespace option
using namespace option;
bool is_discarded_option(unsigned id) {
switch(id) {
/// Input file, unknown args, and output — we manage these ourselves.
/// -main-file-name is per-file input identity injected by to_argv()
/// for cc1 commands; it only labels diagnostics/debug info.
case OPT_INPUT:
case OPT_UNKNOWN:
case OPT__DASH_DASH:
case OPT_main_file_name:
case OPT_c:
case OPT_o:
case OPT_dxc_Fc:
case OPT_dxc_Fo:
case OPT__SLASH_Fo:
case OPT__SLASH_Fd:
/// PCH building.
case OPT_emit_pch:
case OPT_include_pch:
case OPT__SLASH_Yu:
case OPT__SLASH_Fp:
/// Dependency scan.
case OPT_E:
case OPT_M:
case OPT_MM:
case OPT_MD:
case OPT_MMD:
case OPT_MF:
case OPT_MT:
case OPT_MQ:
case OPT_MG:
case OPT_MP:
case OPT_show_inst:
case OPT_show_encoding:
case OPT_show_includes:
case OPT__SLASH_showFilenames:
case OPT__SLASH_showFilenames_:
case OPT__SLASH_showIncludes:
case OPT__SLASH_showIncludes_user:
/// C++ modules — we handle these ourselves.
case OPT_fmodule_file:
case OPT_fmodule_output:
case OPT_fprebuilt_module_path: return true;
default: return false;
}
}
bool is_user_content_option(unsigned id) {
switch(id) {
case OPT_I:
case OPT_isystem:
case OPT_iquote:
case OPT_idirafter:
case OPT_D:
case OPT_U:
case OPT_include: return true;
default: return false;
}
}
bool is_include_path_option(unsigned id) {
switch(id) {
case OPT_I:
case OPT_isystem:
case OPT_iquote:
case OPT_idirafter: return true;
default: return false;
}
}
bool is_xclang_option(unsigned id) {
return id == OPT_Xclang;
}
llvm::StringRef resource_dir() {
static std::string dir = [] {
static int anchor;
auto exe = llvm::sys::fs::getMainExecutable("", &anchor);
if(exe.empty()) {
return std::string{};
}
return clang::driver::Driver::GetResourcesPath(exe);
}();
return dir;
}
bool is_codegen_option(unsigned id) {
/// Debug info options form a group (-g, -gdwarf-*, -gsplit-dwarf, etc.).
if(auto opt = option::table().option(id); opt && opt->matches(OPT_DebugInfo_Group)) {
return true;
}
switch(id) {
/// Position-independent code — pure codegen, no macro or semantic effect.
case OPT_fPIC:
case OPT_fno_PIC:
case OPT_fpic:
case OPT_fno_pic:
case OPT_fPIE:
case OPT_fno_PIE:
case OPT_fpie:
case OPT_fno_pie:
/// Frame pointer and unwind tables — pure codegen.
case OPT_fomit_frame_pointer:
case OPT_fno_omit_frame_pointer:
case OPT_funwind_tables:
case OPT_fno_unwind_tables:
case OPT_fasynchronous_unwind_tables:
case OPT_fno_asynchronous_unwind_tables:
/// Stack protection — pure codegen.
case OPT_fstack_protector:
case OPT_fstack_protector_strong:
case OPT_fstack_protector_all:
case OPT_fno_stack_protector:
/// Section splitting, LTO, semantic interposition — pure codegen/linker.
case OPT_fdata_sections:
case OPT_fno_data_sections:
case OPT_ffunction_sections:
case OPT_fno_function_sections:
case OPT_flto:
case OPT_flto_EQ:
case OPT_fno_lto:
case OPT_fsemantic_interposition:
case OPT_fno_semantic_interposition:
case OPT_fvisibility_inlines_hidden:
/// Diagnostics output formatting — doesn't affect analysis.
case OPT_fcolor_diagnostics:
case OPT_fno_color_diagnostics:
/// Floating-point codegen — doesn't define macros (unlike -ffast-math).
case OPT_ftrapping_math:
case OPT_fno_trapping_math: return true;
default: return false;
}
}
bool is_diagnostics_option(unsigned id) {
if(id == OPT_w) {
return true;
}
if(auto opt = option::table().option(id)) {
return opt->matches(OPT_Diag_Group) || opt->matches(OPT_pedantic_Group);
}
return false;
}
std::string canonicalize(llvm::ArrayRef<std::string> args, ArgsProfile profile) {
std::string buf;
if(args.empty()) {
return buf;
}
llvm::raw_string_ostream os(buf);
auto append = [&](std::string_view fragment) {
os << fragment << '\0';
};
/// The driver affects language defaults (clang vs clang++ vs clang-cl).
append(args[0]);
std::vector<std::string> parse_args(args.begin() + 1, args.end());
auto options = kota::option::ParseOptions{.dash_dash_parsing = true,
.visibility = default_visibility(args[0])};
for(auto& result: option::table().parse(parse_args, options)) {
if(!result.has_value()) {
/// Unparseable arguments are kept verbatim: dropping an option
/// we don't understand could merge keys that must differ.
auto index = result.error().index;
if(index < parse_args.size()) {
append(parse_args[index]);
}
continue;
}
auto& arg = *result;
/// Unknown options are kept verbatim, same rationale as parse errors.
if(arg.id == OPT_UNKNOWN) {
if(arg.index < parse_args.size()) {
append(parse_args[arg.index]);
}
continue;
}
bool keep = false;
switch(profile) {
case ArgsProfile::Full: keep = true; break;
case ArgsProfile::Frontend:
keep = !is_discarded_option(arg.id) && !is_codegen_option(arg.id);
break;
case ArgsProfile::Preprocessing:
keep = !is_discarded_option(arg.id) && !is_codegen_option(arg.id) &&
!is_diagnostics_option(arg.id);
break;
}
if(keep) {
option::table().render(arg, append);
}
}
return buf;
}
std::string print_argv(llvm::ArrayRef<const char*> args) {
std::string buf;
llvm::raw_string_ostream os(buf);
bool sep = false;
for(llvm::StringRef arg: args) {
if(sep)
os << ' ';
sep = true;
if(llvm::all_of(arg, llvm::isPrint) &&
arg.find_first_of(" \t\n\"\\") == llvm::StringRef::npos) {
os << arg;
continue;
}
os << '"';
os.write_escaped(arg, /*UseHexEscapes=*/true);
os << '"';
}
return std::move(os.str());
}
unsigned default_visibility(llvm::StringRef driver) {
auto name = llvm::sys::path::filename(driver);
name.consume_back(".exe");
auto is_cl = [](llvm::StringRef s) {
return s.equals_insensitive("cl") || s.equals_insensitive("clang-cl");
};
/// cl.exe and clang-cl.exe both need MSVC-style /options.
/// Also handle versioned names like clang-cl-17, clang-cl-17.0.1.
if(is_cl(name) || is_cl(name.rtrim("0123456789.-"))) {
return ~0u;
}
/// Exclude CLOption to prevent /U, /D, /I from matching Unix paths.
return ~static_cast<unsigned>(CLOption);
}
bool is_c_family_file(llvm::StringRef filename) {
namespace types = clang::driver::types;
auto ext = llvm::sys::path::extension(filename);
if(ext.empty()) {
return false;
}
/// Drop the leading dot: ".cpp" -> "cpp".
auto type = types::lookupTypeForExtension(ext.drop_front());
return type != types::TY_INVALID && types::isAcceptedByClang(type);
}
} // namespace clice