master
1//! To get started, run this tool with no args and read the help message.
2//!
3//! Clang has a file "options.td" which describes all of its command line parameter options.
4//! When using `zig cc`, Zig acts as a proxy between the user and Clang. It does not need
5//! to understand all the parameters, but it does need to understand some of them, such as
6//! the target. This means that Zig must understand when a C command line parameter expects
7//! to "consume" the next parameter on the command line.
8//!
9//! For example, `-z -target` would mean to pass `-target` to the linker, whereas `-E -target`
10//! would mean that the next parameter specifies the target.
11
12const std = @import("std");
13const fs = std.fs;
14const assert = std.debug.assert;
15const json = std.json;
16
17const KnownOpt = struct {
18 name: []const u8,
19
20 /// Corresponds to stage.zig ClangArgIterator.Kind
21 ident: []const u8,
22};
23
24const known_options = [_]KnownOpt{
25 .{
26 .name = "target",
27 .ident = "target",
28 },
29 .{
30 .name = "o",
31 .ident = "o",
32 },
33 .{
34 .name = "c",
35 .ident = "c",
36 },
37 .{
38 .name = "r",
39 .ident = "r",
40 },
41 .{
42 .name = "l",
43 .ident = "l",
44 },
45 .{
46 .name = "pipe",
47 .ident = "ignore",
48 },
49 .{
50 .name = "help",
51 .ident = "driver_punt",
52 },
53 .{
54 .name = "fPIC",
55 .ident = "pic",
56 },
57 .{
58 .name = "fpic",
59 .ident = "pic",
60 },
61 .{
62 .name = "fno-PIC",
63 .ident = "no_pic",
64 },
65 .{
66 .name = "fno-pic",
67 .ident = "no_pic",
68 },
69 .{
70 .name = "fPIE",
71 .ident = "pie",
72 },
73 .{
74 .name = "fpie",
75 .ident = "pie",
76 },
77 .{
78 .name = "pie",
79 .ident = "pie",
80 },
81 .{
82 .name = "fno-PIE",
83 .ident = "no_pie",
84 },
85 .{
86 .name = "fno-pie",
87 .ident = "no_pie",
88 },
89 .{
90 .name = "no-pie",
91 .ident = "no_pie",
92 },
93 .{
94 .name = "nopie",
95 .ident = "no_pie",
96 },
97 .{
98 .name = "flto",
99 .ident = "lto",
100 },
101 .{
102 .name = "fno-lto",
103 .ident = "no_lto",
104 },
105 .{
106 .name = "funwind-tables",
107 .ident = "unwind_tables",
108 },
109 .{
110 .name = "fno-unwind-tables",
111 .ident = "no_unwind_tables",
112 },
113 .{
114 .name = "fasynchronous-unwind-tables",
115 .ident = "asynchronous_unwind_tables",
116 },
117 .{
118 .name = "fno-asynchronous-unwind-tables",
119 .ident = "no_asynchronous_unwind_tables",
120 },
121 .{
122 .name = "nolibc",
123 .ident = "nostdlib",
124 },
125 .{
126 .name = "nostdlib",
127 .ident = "nostdlib",
128 },
129 .{
130 .name = "no-standard-libraries",
131 .ident = "nostdlib",
132 },
133 .{
134 .name = "nostdlib++",
135 .ident = "nostdlib_cpp",
136 },
137 .{
138 .name = "nostdinc++",
139 .ident = "nostdlib_cpp",
140 },
141 .{
142 .name = "nostdlibinc",
143 .ident = "nostdlibinc",
144 },
145 .{
146 .name = "nostdinc",
147 .ident = "nostdlibinc",
148 },
149 .{
150 .name = "no-standard-includes",
151 .ident = "nostdlibinc",
152 },
153 .{
154 .name = "shared",
155 .ident = "shared",
156 },
157 .{
158 .name = "rdynamic",
159 .ident = "rdynamic",
160 },
161 .{
162 .name = "Wl,",
163 .ident = "wl",
164 },
165 .{
166 .name = "Wp,",
167 .ident = "wp",
168 },
169 .{
170 .name = "Xlinker",
171 .ident = "for_linker",
172 },
173 .{
174 .name = "for-linker",
175 .ident = "for_linker",
176 },
177 .{
178 .name = "for-linker=",
179 .ident = "for_linker",
180 },
181 .{
182 .name = "z",
183 .ident = "linker_input_z",
184 },
185 .{
186 .name = "E",
187 .ident = "preprocess_only",
188 },
189 .{
190 .name = "preprocess",
191 .ident = "preprocess_only",
192 },
193 .{
194 .name = "S",
195 .ident = "asm_only",
196 },
197 .{
198 .name = "assemble",
199 .ident = "asm_only",
200 },
201 .{
202 .name = "O0",
203 .ident = "optimize",
204 },
205 .{
206 .name = "O1",
207 .ident = "optimize",
208 },
209 .{
210 .name = "O2",
211 .ident = "optimize",
212 },
213 // O3 is only detected from the joined "-O" option
214 .{
215 .name = "O4",
216 .ident = "optimize",
217 },
218 .{
219 .name = "Og",
220 .ident = "optimize",
221 },
222 .{
223 .name = "Os",
224 .ident = "optimize",
225 },
226 // Oz is only detected from the joined "-O" option
227 .{
228 .name = "O",
229 .ident = "optimize",
230 },
231 .{
232 .name = "Ofast",
233 .ident = "optimize",
234 },
235 .{
236 .name = "optimize",
237 .ident = "optimize",
238 },
239 .{
240 .name = "g1",
241 .ident = "debug",
242 },
243 .{
244 .name = "gline-tables-only",
245 .ident = "debug",
246 },
247 .{
248 .name = "g",
249 .ident = "debug",
250 },
251 .{
252 .name = "debug",
253 .ident = "debug",
254 },
255 .{
256 .name = "gdwarf32",
257 .ident = "gdwarf32",
258 },
259 .{
260 .name = "gdwarf64",
261 .ident = "gdwarf64",
262 },
263 .{
264 .name = "gdwarf",
265 .ident = "debug",
266 },
267 .{
268 .name = "gdwarf-2",
269 .ident = "debug",
270 },
271 .{
272 .name = "gdwarf-3",
273 .ident = "debug",
274 },
275 .{
276 .name = "gdwarf-4",
277 .ident = "debug",
278 },
279 .{
280 .name = "gdwarf-5",
281 .ident = "debug",
282 },
283 .{
284 .name = "fsanitize",
285 .ident = "sanitize",
286 },
287 .{
288 .name = "fno-sanitize",
289 .ident = "no_sanitize",
290 },
291 .{
292 .name = "fsanitize-trap",
293 .ident = "sanitize_trap",
294 },
295 .{
296 .name = "fno-sanitize-trap",
297 .ident = "no_sanitize_trap",
298 },
299 .{
300 .name = "T",
301 .ident = "linker_script",
302 },
303 .{
304 .name = "###",
305 .ident = "dry_run",
306 },
307 .{
308 .name = "v",
309 .ident = "verbose",
310 },
311 .{
312 .name = "verbose",
313 .ident = "verbose",
314 },
315 .{
316 .name = "L",
317 .ident = "lib_dir",
318 },
319 .{
320 .name = "library-directory",
321 .ident = "lib_dir",
322 },
323 .{
324 .name = "mcpu",
325 .ident = "mcpu",
326 },
327 .{
328 .name = "march",
329 .ident = "mcpu",
330 },
331 .{
332 .name = "mtune",
333 .ident = "mcpu",
334 },
335 .{
336 .name = "mred-zone",
337 .ident = "red_zone",
338 },
339 .{
340 .name = "mno-red-zone",
341 .ident = "no_red_zone",
342 },
343 .{
344 .name = "fomit-frame-pointer",
345 .ident = "omit_frame_pointer",
346 },
347 .{
348 .name = "fno-omit-frame-pointer",
349 .ident = "no_omit_frame_pointer",
350 },
351 .{
352 .name = "ffunction-sections",
353 .ident = "function_sections",
354 },
355 .{
356 .name = "fno-function-sections",
357 .ident = "no_function_sections",
358 },
359 .{
360 .name = "fdata-sections",
361 .ident = "data_sections",
362 },
363 .{
364 .name = "fno-data-sections",
365 .ident = "no_data_sections",
366 },
367 .{
368 .name = "fbuiltin",
369 .ident = "builtin",
370 },
371 .{
372 .name = "fno-builtin",
373 .ident = "no_builtin",
374 },
375 .{
376 .name = "fcolor-diagnostics",
377 .ident = "color_diagnostics",
378 },
379 .{
380 .name = "fno-color-diagnostics",
381 .ident = "no_color_diagnostics",
382 },
383 .{
384 .name = "fcaret-diagnostics",
385 .ident = "color_diagnostics",
386 },
387 .{
388 .name = "fno-caret-diagnostics",
389 .ident = "no_color_diagnostics",
390 },
391 .{
392 .name = "fstack-check",
393 .ident = "stack_check",
394 },
395 .{
396 .name = "fno-stack-check",
397 .ident = "no_stack_check",
398 },
399 .{
400 .name = "stack-protector",
401 .ident = "stack_protector",
402 },
403 .{
404 .name = "fstack-protector",
405 .ident = "stack_protector",
406 },
407 .{
408 .name = "fno-stack-protector",
409 .ident = "no_stack_protector",
410 },
411 .{
412 .name = "fstack-protector-strong",
413 .ident = "stack_protector",
414 },
415 .{
416 .name = "fstack-protector-all",
417 .ident = "stack_protector",
418 },
419 .{
420 .name = "MD",
421 .ident = "dep_file",
422 },
423 .{
424 .name = "write-dependencies",
425 .ident = "dep_file",
426 },
427 .{
428 .name = "MV",
429 .ident = "dep_file",
430 },
431 .{
432 .name = "MF",
433 .ident = "dep_file",
434 },
435 .{
436 .name = "MT",
437 .ident = "dep_file",
438 },
439 .{
440 .name = "MG",
441 .ident = "dep_file",
442 },
443 .{
444 .name = "print-missing-file-dependencies",
445 .ident = "dep_file",
446 },
447 .{
448 .name = "MJ",
449 .ident = "dep_file",
450 },
451 .{
452 .name = "MM",
453 .ident = "dep_file_to_stdout",
454 },
455 .{
456 .name = "M",
457 .ident = "dep_file_to_stdout",
458 },
459 .{
460 .name = "user-dependencies",
461 .ident = "dep_file_to_stdout",
462 },
463 .{
464 .name = "MMD",
465 .ident = "dep_file",
466 },
467 .{
468 .name = "write-user-dependencies",
469 .ident = "dep_file",
470 },
471 .{
472 .name = "MP",
473 .ident = "dep_file",
474 },
475 .{
476 .name = "MQ",
477 .ident = "dep_file",
478 },
479 .{
480 .name = "F",
481 .ident = "framework_dir",
482 },
483 .{
484 .name = "framework",
485 .ident = "framework",
486 },
487 .{
488 .name = "s",
489 .ident = "strip",
490 },
491 .{
492 .name = "dynamiclib",
493 .ident = "shared",
494 },
495 .{
496 .name = "mexec-model",
497 .ident = "exec_model",
498 },
499 .{
500 .name = "emit-llvm",
501 .ident = "emit_llvm",
502 },
503 .{
504 .name = "sysroot",
505 .ident = "sysroot",
506 },
507 .{
508 .name = "entry",
509 .ident = "entry",
510 },
511 .{
512 .name = "e",
513 .ident = "entry",
514 },
515 .{
516 .name = "u",
517 .ident = "force_undefined_symbol",
518 },
519 .{
520 .name = "weak-l",
521 .ident = "weak_library",
522 },
523 .{
524 .name = "weak_library",
525 .ident = "weak_library",
526 },
527 .{
528 .name = "weak_framework",
529 .ident = "weak_framework",
530 },
531 .{
532 .name = "headerpad_max_install_names",
533 .ident = "headerpad_max_install_names",
534 },
535 .{
536 .name = "compress-debug-sections",
537 .ident = "compress_debug_sections",
538 },
539 .{
540 .name = "compress-debug-sections=",
541 .ident = "compress_debug_sections",
542 },
543 .{
544 .name = "install_name",
545 .ident = "install_name",
546 },
547 .{
548 .name = "undefined",
549 .ident = "undefined",
550 },
551 .{
552 .name = "x",
553 .ident = "x",
554 },
555 .{
556 .name = "ObjC",
557 .ident = "force_load_objc",
558 },
559 .{
560 .name = "municode",
561 .ident = "mingw_unicode_entry_point",
562 },
563 .{
564 .name = "fsanitize-coverage-trace-pc-guard",
565 .ident = "san_cov_trace_pc_guard",
566 },
567 .{
568 .name = "fsanitize-coverage",
569 .ident = "san_cov",
570 },
571 .{
572 .name = "fno-sanitize-coverage",
573 .ident = "no_san_cov",
574 },
575 .{
576 .name = "rtlib",
577 .ident = "rtlib",
578 },
579 .{
580 .name = "rtlib=",
581 .ident = "rtlib",
582 },
583 .{
584 .name = "static",
585 .ident = "static",
586 },
587 .{
588 .name = "dynamic",
589 .ident = "dynamic",
590 },
591};
592
593const blacklisted_options = [_][]const u8{};
594
595fn knownOption(name: []const u8) ?[]const u8 {
596 const chopped_name = if (std.mem.endsWith(u8, name, "=")) name[0 .. name.len - 1] else name;
597 for (known_options) |item| {
598 if (std.mem.eql(u8, chopped_name, item.name)) {
599 return item.ident;
600 }
601 }
602 return null;
603}
604
605const cpu_targets = struct {
606 pub const aarch64 = std.Target.aarch64;
607 pub const amdgcn = std.Target.amdgcn;
608 pub const arc = std.Target.arc;
609 pub const arm = std.Target.arm;
610 pub const avr = std.Target.avr;
611 pub const bpf = std.Target.bpf;
612 pub const csky = std.Target.csky;
613 pub const hexagon = std.Target.hexagon;
614 pub const loongarch = std.Target.loongarch;
615 pub const m68k = std.Target.m68k;
616 pub const mips = std.Target.mips;
617 pub const msp430 = std.Target.msp430;
618 pub const nvptx = std.Target.nvptx;
619 pub const powerpc = std.Target.powerpc;
620 pub const riscv = std.Target.riscv;
621 pub const s390x = std.Target.s390x;
622 pub const sparc = std.Target.sparc;
623 pub const spirv = std.Target.spirv;
624 pub const ve = std.Target.ve;
625 pub const wasm = std.Target.wasm;
626 pub const x86 = std.Target.x86;
627 pub const xtensa = std.Target.xtensa;
628};
629
630pub fn main() anyerror!void {
631 var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
632 defer arena.deinit();
633
634 const allocator = arena.allocator();
635 const args = try std.process.argsAlloc(allocator);
636
637 var stdout_buffer: [4000]u8 = undefined;
638 var stdout_writer = fs.File.stdout().writerStreaming(&stdout_buffer);
639 const stdout = &stdout_writer.interface;
640
641 if (args.len <= 1) printUsageAndExit(args[0]);
642
643 if (std.mem.eql(u8, args[1], "--help")) {
644 printUsage(stdout, args[0]) catch std.process.exit(2);
645 stdout.flush() catch std.process.exit(2);
646 std.process.exit(0);
647 }
648
649 if (args.len < 3) printUsageAndExit(args[0]);
650
651 const llvm_tblgen_exe = args[1];
652 if (std.mem.startsWith(u8, llvm_tblgen_exe, "-")) printUsageAndExit(args[0]);
653
654 const llvm_src_root = args[2];
655 if (std.mem.startsWith(u8, llvm_src_root, "-")) printUsageAndExit(args[0]);
656
657 var llvm_to_zig_cpu_features = std.StringHashMap([]const u8).init(allocator);
658
659 inline for (@typeInfo(cpu_targets).@"struct".decls) |decl| {
660 const Feature = @field(cpu_targets, decl.name).Feature;
661 const all_features = @field(cpu_targets, decl.name).all_features;
662
663 for (all_features, 0..) |feat, i| {
664 const llvm_name = feat.llvm_name orelse continue;
665 const zig_feat = @as(Feature, @enumFromInt(i));
666 const zig_name = @tagName(zig_feat);
667 try llvm_to_zig_cpu_features.put(llvm_name, zig_name);
668 }
669 }
670
671 const child_args = [_][]const u8{
672 llvm_tblgen_exe,
673 "--dump-json",
674 try std.fmt.allocPrint(allocator, "{s}/clang/include/clang/Driver/Options.td", .{llvm_src_root}),
675 try std.fmt.allocPrint(allocator, "-I={s}/llvm/include", .{llvm_src_root}),
676 try std.fmt.allocPrint(allocator, "-I={s}/clang/include/clang/Driver", .{llvm_src_root}),
677 };
678
679 const child_result = try std.process.Child.run(.{
680 .allocator = allocator,
681 .argv = &child_args,
682 .max_output_bytes = 100 * 1024 * 1024,
683 });
684
685 std.debug.print("{s}\n", .{child_result.stderr});
686
687 const json_text = switch (child_result.term) {
688 .Exited => |code| if (code == 0) child_result.stdout else {
689 std.debug.print("llvm-tblgen exited with code {d}\n", .{code});
690 std.process.exit(1);
691 },
692 else => {
693 std.debug.print("llvm-tblgen crashed\n", .{});
694 std.process.exit(1);
695 },
696 };
697
698 const parsed = try json.parseFromSlice(json.Value, allocator, json_text, .{});
699 defer parsed.deinit();
700 const root_map = &parsed.value.object;
701
702 var all_objects = std.array_list.Managed(*json.ObjectMap).init(allocator);
703 {
704 var it = root_map.iterator();
705 it_map: while (it.next()) |kv| {
706 if (kv.key_ptr.len == 0) continue;
707 if (kv.key_ptr.*[0] == '!') continue;
708 if (kv.value_ptr.* != .object) continue;
709 if (!kv.value_ptr.object.contains("NumArgs")) continue;
710 if (!kv.value_ptr.object.contains("Name")) continue;
711 for (blacklisted_options) |blacklisted_key| {
712 if (std.mem.eql(u8, blacklisted_key, kv.key_ptr.*)) continue :it_map;
713 }
714 if (kv.value_ptr.object.get("Name").?.string.len == 0) continue;
715 try all_objects.append(&kv.value_ptr.object);
716 }
717 }
718 // Some options have multiple matches. As an example, "-Wl,foo" matches both
719 // "W" and "Wl,". So we sort this list in order of descending priority.
720 std.mem.sort(*json.ObjectMap, all_objects.items, {}, objectLessThan);
721
722 try stdout.writeAll(
723 \\// This file is generated by tools/update_clang_options.zig.
724 \\// zig fmt: off
725 \\const clang_options = @import("clang_options.zig");
726 \\const CliArg = clang_options.CliArg;
727 \\const flagpd1 = clang_options.flagpd1;
728 \\const flagpsl = clang_options.flagpsl;
729 \\const joinpd1 = clang_options.joinpd1;
730 \\const jspd1 = clang_options.jspd1;
731 \\const sepd1 = clang_options.sepd1;
732 \\const m = clang_options.m;
733 \\pub const data = blk: { @setEvalBranchQuota(6000); break :blk &[_]CliArg{
734 \\
735 );
736
737 for (all_objects.items) |obj| {
738 const name = obj.get("Name").?.string;
739 var pd1 = false;
740 var pd2 = false;
741 var pslash = false;
742 for (obj.get("Prefixes").?.array.items) |prefix_json| {
743 const prefix = prefix_json.string;
744 if (std.mem.eql(u8, prefix, "-")) {
745 pd1 = true;
746 } else if (std.mem.eql(u8, prefix, "--")) {
747 pd2 = true;
748 } else if (std.mem.eql(u8, prefix, "/")) {
749 pslash = true;
750 } else {
751 std.debug.print("{s} has unrecognized prefix '{s}'\n", .{ name, prefix });
752 std.process.exit(1);
753 }
754 }
755 const syntax = objSyntax(obj) orelse continue;
756
757 if (std.mem.eql(u8, name, "MT") and syntax == .flag) {
758 // `-MT foo` is ambiguous because there is also an -MT flag
759 // The canonical way to specify the flag is with `/MT` and so we make this
760 // the only way.
761 try stdout.print("flagpsl(\"{s}\"),\n", .{name});
762 } else if (knownOption(name)) |ident| {
763
764 // Workaround the fact that in 'Options.td' -Ofast is listed as 'joined'
765 const final_syntax = if (std.mem.eql(u8, name, "Ofast")) .flag else syntax;
766
767 try stdout.print(
768 \\.{{
769 \\ .name = "{s}",
770 \\ .syntax = {f},
771 \\ .zig_equivalent = .{s},
772 \\ .pd1 = {},
773 \\ .pd2 = {},
774 \\ .psl = {},
775 \\}},
776 \\
777 , .{ name, final_syntax, ident, pd1, pd2, pslash });
778 } else if (pd1 and !pd2 and !pslash and syntax == .flag) {
779 if ((std.mem.startsWith(u8, name, "mno-") and
780 llvm_to_zig_cpu_features.contains(name["mno-".len..])) or
781 (std.mem.startsWith(u8, name, "m") and
782 llvm_to_zig_cpu_features.contains(name["m".len..])))
783 {
784 try stdout.print("m(\"{s}\"),\n", .{name});
785 } else {
786 try stdout.print("flagpd1(\"{s}\"),\n", .{name});
787 }
788 } else if (!pd1 and !pd2 and pslash and syntax == .flag) {
789 try stdout.print("flagpsl(\"{s}\"),\n", .{name});
790 } else if (pd1 and !pd2 and !pslash and syntax == .joined) {
791 try stdout.print("joinpd1(\"{s}\"),\n", .{name});
792 } else if (pd1 and !pd2 and !pslash and syntax == .joined_or_separate) {
793 try stdout.print("jspd1(\"{s}\"),\n", .{name});
794 } else if (pd1 and !pd2 and !pslash and syntax == .separate) {
795 try stdout.print("sepd1(\"{s}\"),\n", .{name});
796 } else {
797 try stdout.print(
798 \\.{{
799 \\ .name = "{s}",
800 \\ .syntax = {f},
801 \\ .zig_equivalent = .other,
802 \\ .pd1 = {},
803 \\ .pd2 = {},
804 \\ .psl = {},
805 \\}},
806 \\
807 , .{ name, syntax, pd1, pd2, pslash });
808 }
809 }
810
811 try stdout.writeAll(
812 \\};};
813 \\
814 );
815
816 try stdout.flush();
817}
818
819// TODO we should be able to import clang_options.zig but currently this is problematic because it will
820// import stage2.zig and that causes a bunch of stuff to get exported
821const Syntax = union(enum) {
822 /// A flag with no values.
823 flag,
824
825 /// An option which prefixes its (single) value.
826 joined,
827
828 /// An option which is followed by its value.
829 separate,
830
831 /// An option which is either joined to its (non-empty) value, or followed by its value.
832 joined_or_separate,
833
834 /// An option which is both joined to its (first) value, and followed by its (second) value.
835 joined_and_separate,
836
837 /// An option followed by its values, which are separated by commas.
838 comma_joined,
839
840 /// An option which consumes an optional joined argument and any other remaining arguments.
841 remaining_args_joined,
842
843 /// An option which is which takes multiple (separate) arguments.
844 multi_arg: u8,
845
846 pub fn format(
847 self: Syntax,
848 out_stream: *std.Io.Writer,
849 ) std.Io.Writer.Error!void {
850 switch (self) {
851 .multi_arg => |n| return out_stream.print(".{{.{t}={d}}}", .{ self, n }),
852 else => return out_stream.print(".{s}", .{@tagName(self)}),
853 }
854 }
855};
856
857fn objSyntax(obj: *json.ObjectMap) ?Syntax {
858 const num_args = @as(u8, @intCast(obj.get("NumArgs").?.integer));
859 for (obj.get("!superclasses").?.array.items) |superclass_json| {
860 const superclass = superclass_json.string;
861 if (std.mem.eql(u8, superclass, "Joined")) {
862 return .joined;
863 } else if (std.mem.eql(u8, superclass, "CLJoined")) {
864 return .joined;
865 } else if (std.mem.eql(u8, superclass, "CLIgnoredJoined")) {
866 return .joined;
867 } else if (std.mem.eql(u8, superclass, "CLCompileJoined")) {
868 return .joined;
869 } else if (std.mem.eql(u8, superclass, "CLDXCJoined")) {
870 return .joined;
871 } else if (std.mem.eql(u8, superclass, "JoinedOrSeparate")) {
872 return .joined_or_separate;
873 } else if (std.mem.eql(u8, superclass, "CLJoinedOrSeparate")) {
874 return .joined_or_separate;
875 } else if (std.mem.eql(u8, superclass, "CLCompileJoinedOrSeparate")) {
876 return .joined_or_separate;
877 } else if (std.mem.eql(u8, superclass, "DXCJoinedOrSeparate")) {
878 return .joined_or_separate;
879 } else if (std.mem.eql(u8, superclass, "CLDXCJoinedOrSeparate")) {
880 return .joined_or_separate;
881 } else if (std.mem.eql(u8, superclass, "Flag")) {
882 return .flag;
883 } else if (std.mem.eql(u8, superclass, "CLFlag")) {
884 return .flag;
885 } else if (std.mem.eql(u8, superclass, "CLIgnoredFlag")) {
886 return .flag;
887 } else if (std.mem.eql(u8, superclass, "Separate")) {
888 return .separate;
889 } else if (std.mem.eql(u8, superclass, "JoinedAndSeparate")) {
890 return .joined_and_separate;
891 } else if (std.mem.eql(u8, superclass, "CommaJoined")) {
892 return .comma_joined;
893 } else if (std.mem.eql(u8, superclass, "CLRemainingArgsJoined")) {
894 return .remaining_args_joined;
895 } else if (std.mem.eql(u8, superclass, "MultiArg")) {
896 return .{ .multi_arg = num_args };
897 }
898 }
899 const name = obj.get("Name").?.string;
900 if (std.mem.eql(u8, name, "<input>")) {
901 return .flag;
902 } else if (std.mem.eql(u8, name, "<unknown>")) {
903 return .flag;
904 }
905 const kind_def = obj.get("Kind").?.object.get("def").?.string;
906 if (std.mem.eql(u8, kind_def, "KIND_FLAG")) {
907 return .flag;
908 }
909 const key = obj.get("!name").?.string;
910 std.debug.print("{s} (key {s}) has unrecognized superclasses:\n", .{ name, key });
911 for (obj.get("!superclasses").?.array.items) |superclass_json| {
912 std.debug.print(" {s}\n", .{superclass_json.string});
913 }
914 //std.process.exit(1);
915 return null;
916}
917
918fn syntaxMatchesWithEql(syntax: Syntax) bool {
919 return switch (syntax) {
920 .flag,
921 .separate,
922 .multi_arg,
923 => true,
924
925 .joined,
926 .joined_or_separate,
927 .joined_and_separate,
928 .comma_joined,
929 .remaining_args_joined,
930 => false,
931 };
932}
933
934fn objectLessThan(context: void, a: *json.ObjectMap, b: *json.ObjectMap) bool {
935 _ = context;
936 // Priority is determined by exact matches first, followed by prefix matches in descending
937 // length, with key as a final tiebreaker.
938 const a_syntax = objSyntax(a) orelse return false;
939 const b_syntax = objSyntax(b) orelse return true;
940
941 const a_match_with_eql = syntaxMatchesWithEql(a_syntax);
942 const b_match_with_eql = syntaxMatchesWithEql(b_syntax);
943
944 if (a_match_with_eql and !b_match_with_eql) {
945 return true;
946 } else if (!a_match_with_eql and b_match_with_eql) {
947 return false;
948 }
949
950 if (!a_match_with_eql and !b_match_with_eql) {
951 const a_name = a.get("Name").?.string;
952 const b_name = b.get("Name").?.string;
953 if (a_name.len != b_name.len) {
954 return a_name.len > b_name.len;
955 }
956 }
957
958 const a_key = a.get("!name").?.string;
959 const b_key = b.get("!name").?.string;
960 return std.mem.lessThan(u8, a_key, b_key);
961}
962
963fn printUsageAndExit(arg0: []const u8) noreturn {
964 const w, _ = std.debug.lockStderrWriter(&.{});
965 defer std.debug.unlockStderrWriter();
966 printUsage(w, arg0) catch std.process.exit(2);
967 std.process.exit(1);
968}
969
970fn printUsage(w: *std.Io.Writer, arg0: []const u8) std.Io.Writer.Error!void {
971 try w.print(
972 \\Usage: {s} /path/to/llvm-tblgen /path/to/git/llvm/llvm-project
973 \\Alternative Usage: zig run /path/to/git/zig/tools/update_clang_options.zig -- /path/to/llvm-tblgen /path/to/git/llvm/llvm-project
974 \\
975 \\Prints to stdout Zig code which you can use to replace the file src/clang_options_data.zig.
976 \\
977 , .{arg0});
978}