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