mirror of
https://codeberg.org/ziglang/zig.git
synced 2025-12-06 13:54:21 +00:00
274 lines
10 KiB
Zig
274 lines
10 KiB
Zig
//! To get started, run this tool with no args and read the help message.
|
|
//!
|
|
//! Clang has a file "options.td" which describes all of its command line parameter options.
|
|
//! When using `zig cc`, Zig acts as a proxy between the user and Clang. It does not need
|
|
//! to understand all the parameters, but it does need to understand some of them, such as
|
|
//! the target. This means that Zig must understand when a C command line parameter expects
|
|
//! to "consume" the next parameter on the command line.
|
|
//!
|
|
//! For example, `-z -target` would mean to pass `-target` to the linker, whereas `-E -target`
|
|
//! would mean that the next parameter specifies the target.
|
|
|
|
const std = @import("std");
|
|
const fs = std.fs;
|
|
const assert = std.debug.assert;
|
|
const json = std.json;
|
|
|
|
const KnownOpt = struct {
|
|
name: []const u8,
|
|
|
|
/// Corresponds to stage.zig ClangArgIterator.Kind
|
|
ident: []const u8,
|
|
};
|
|
|
|
const known_options = [_]KnownOpt{
|
|
.{
|
|
.name = "target",
|
|
.ident = "target",
|
|
},
|
|
.{
|
|
.name = "o",
|
|
.ident = "o",
|
|
},
|
|
.{
|
|
.name = "c",
|
|
.ident = "c",
|
|
},
|
|
.{
|
|
.name = "l",
|
|
.ident = "l",
|
|
},
|
|
};
|
|
|
|
const blacklisted_options = [_][]const u8{};
|
|
|
|
fn knownOption(name: []const u8) ?[]const u8 {
|
|
const chopped_name = if (std.mem.endsWith(u8, name, "=")) name[0 .. name.len - 1] else name;
|
|
for (known_options) |item| {
|
|
if (std.mem.eql(u8, chopped_name, item.name)) {
|
|
return item.ident;
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
pub fn main() anyerror!void {
|
|
var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
|
|
defer arena.deinit();
|
|
|
|
const allocator = &arena.allocator;
|
|
const args = try std.process.argsAlloc(allocator);
|
|
|
|
if (args.len <= 1) {
|
|
usageAndExit(std.io.getStdErr(), args[0], 1);
|
|
}
|
|
if (std.mem.eql(u8, args[1], "--help")) {
|
|
usageAndExit(std.io.getStdOut(), args[0], 0);
|
|
}
|
|
if (args.len < 3) {
|
|
usageAndExit(std.io.getStdErr(), args[0], 1);
|
|
}
|
|
|
|
const llvm_tblgen_exe = args[1];
|
|
if (std.mem.startsWith(u8, llvm_tblgen_exe, "-")) {
|
|
usageAndExit(std.io.getStdErr(), args[0], 1);
|
|
}
|
|
|
|
const llvm_src_root = args[2];
|
|
if (std.mem.startsWith(u8, llvm_src_root, "-")) {
|
|
usageAndExit(std.io.getStdErr(), args[0], 1);
|
|
}
|
|
|
|
const child_args = [_][]const u8{
|
|
llvm_tblgen_exe,
|
|
"--dump-json",
|
|
try std.fmt.allocPrint(allocator, "{}/clang/include/clang/Driver/Options.td", .{llvm_src_root}),
|
|
try std.fmt.allocPrint(allocator, "-I={}/llvm/include", .{llvm_src_root}),
|
|
try std.fmt.allocPrint(allocator, "-I={}/clang/include/clang/Driver", .{llvm_src_root}),
|
|
};
|
|
|
|
const child_result = try std.ChildProcess.exec2(.{
|
|
.allocator = allocator,
|
|
.argv = &child_args,
|
|
.max_output_bytes = 100 * 1024 * 1024,
|
|
});
|
|
|
|
std.debug.warn("{}\n", .{child_result.stderr});
|
|
|
|
const json_text = switch (child_result.term) {
|
|
.Exited => |code| if (code == 0) child_result.stdout else {
|
|
std.debug.warn("llvm-tblgen exited with code {}\n", .{code});
|
|
std.process.exit(1);
|
|
},
|
|
else => {
|
|
std.debug.warn("llvm-tblgen crashed\n", .{});
|
|
std.process.exit(1);
|
|
},
|
|
};
|
|
|
|
var parser = json.Parser.init(allocator, false);
|
|
const tree = try parser.parse(json_text);
|
|
const root_map = &tree.root.Object;
|
|
|
|
var all_names = std.ArrayList([]const u8).init(allocator);
|
|
{
|
|
var it = root_map.iterator();
|
|
it_map: while (it.next()) |kv| {
|
|
if (kv.key.len == 0) continue;
|
|
if (kv.key[0] == '!') continue;
|
|
if (kv.value != .Object) continue;
|
|
if (!kv.value.Object.contains("NumArgs")) continue;
|
|
if (!kv.value.Object.contains("Name")) continue;
|
|
for (blacklisted_options) |blacklisted_key| {
|
|
if (std.mem.eql(u8, blacklisted_key, kv.key)) continue :it_map;
|
|
}
|
|
if (kv.value.Object.get("Name").?.value.String.len == 0) continue;
|
|
try all_names.append(kv.key);
|
|
}
|
|
}
|
|
std.sort.sort([]const u8, all_names.span(), nameLessThan);
|
|
|
|
var stdout_bos = std.io.bufferedOutStream(std.io.getStdOut().outStream());
|
|
const stdout = stdout_bos.outStream();
|
|
try stdout.writeAll(
|
|
\\// This file is generated by tools/update_clang_options.zig.
|
|
\\// zig fmt: off
|
|
\\usingnamespace @import("clang_options.zig");
|
|
\\pub const data = blk: { @setEvalBranchQuota(6000); break :blk &[_]CliArg{
|
|
\\
|
|
);
|
|
|
|
for (all_names.span()) |key| {
|
|
const obj = &root_map.get(key).?.value.Object;
|
|
const name = obj.get("Name").?.value.String;
|
|
var pd1 = false;
|
|
var pd2 = false;
|
|
var pslash = false;
|
|
for (obj.get("Prefixes").?.value.Array.span()) |prefix_json| {
|
|
const prefix = prefix_json.String;
|
|
if (std.mem.eql(u8, prefix, "-")) {
|
|
pd1 = true;
|
|
} else if (std.mem.eql(u8, prefix, "--")) {
|
|
pd2 = true;
|
|
} else if (std.mem.eql(u8, prefix, "/")) {
|
|
pslash = true;
|
|
} else {
|
|
std.debug.warn("{} (key {}) has unrecognized prefix '{}'\n", .{ name, key, prefix });
|
|
std.process.exit(1);
|
|
}
|
|
}
|
|
const num_args = @intCast(u8, obj.get("NumArgs").?.value.Integer);
|
|
const syntax_str: []const u8 = blk: {
|
|
for (obj.get("!superclasses").?.value.Array.span()) |superclass_json| {
|
|
const superclass = superclass_json.String;
|
|
if (std.mem.eql(u8, superclass, "Joined")) {
|
|
break :blk ".joined";
|
|
} else if (std.mem.eql(u8, superclass, "CLJoined")) {
|
|
break :blk ".joined";
|
|
} else if (std.mem.eql(u8, superclass, "CLIgnoredJoined")) {
|
|
break :blk ".joined";
|
|
} else if (std.mem.eql(u8, superclass, "CLCompileJoined")) {
|
|
break :blk ".joined";
|
|
} else if (std.mem.eql(u8, superclass, "JoinedOrSeparate")) {
|
|
break :blk ".joined_or_separate";
|
|
} else if (std.mem.eql(u8, superclass, "CLJoinedOrSeparate")) {
|
|
break :blk ".joined_or_separate";
|
|
} else if (std.mem.eql(u8, superclass, "CLCompileJoinedOrSeparate")) {
|
|
break :blk ".joined_or_separate";
|
|
} else if (std.mem.eql(u8, superclass, "Flag")) {
|
|
break :blk ".flag";
|
|
} else if (std.mem.eql(u8, superclass, "CLFlag")) {
|
|
break :blk ".flag";
|
|
} else if (std.mem.eql(u8, superclass, "CLIgnoredFlag")) {
|
|
break :blk ".flag";
|
|
} else if (std.mem.eql(u8, superclass, "Separate")) {
|
|
break :blk ".separate";
|
|
} else if (std.mem.eql(u8, superclass, "JoinedAndSeparate")) {
|
|
break :blk ".joined_and_separate";
|
|
} else if (std.mem.eql(u8, superclass, "CommaJoined")) {
|
|
break :blk ".comma_joined";
|
|
} else if (std.mem.eql(u8, superclass, "CLRemainingArgsJoined")) {
|
|
break :blk ".remaining_args_joined";
|
|
} else if (std.mem.eql(u8, superclass, "MultiArg")) {
|
|
break :blk try std.fmt.allocPrint(allocator, ".{{ .multi_arg = {} }}", .{num_args});
|
|
}
|
|
}
|
|
if (std.mem.eql(u8, name, "<input>")) {
|
|
break :blk ".flag";
|
|
} else if (std.mem.eql(u8, name, "<unknown>")) {
|
|
break :blk ".flag";
|
|
}
|
|
const kind_def = obj.get("Kind").?.value.Object.get("def").?.value.String;
|
|
if (std.mem.eql(u8, kind_def, "KIND_FLAG")) {
|
|
break :blk ".flag";
|
|
}
|
|
std.debug.warn("{} (key {}) has unrecognized superclasses:\n", .{ name, key });
|
|
for (obj.get("!superclasses").?.value.Array.span()) |superclass_json| {
|
|
std.debug.warn(" {}\n", .{superclass_json.String});
|
|
}
|
|
std.process.exit(1);
|
|
};
|
|
if (knownOption(name)) |ident| {
|
|
try stdout.print(
|
|
\\.{{
|
|
\\ .name = "{}",
|
|
\\ .syntax = {},
|
|
\\ .zig_equivalent = .{},
|
|
\\ .pd1 = {},
|
|
\\ .pd2 = {},
|
|
\\ .psl = {},
|
|
\\}},
|
|
\\
|
|
, .{ name, syntax_str, ident, pd1, pd2, pslash });
|
|
} else if (pd1 and !pd2 and !pslash and
|
|
std.mem.eql(u8, syntax_str, ".flag"))
|
|
{
|
|
try stdout.print("flagpd1(\"{}\"),\n", .{name});
|
|
} else if (pd1 and !pd2 and !pslash and
|
|
std.mem.eql(u8, syntax_str, ".joined"))
|
|
{
|
|
try stdout.print("joinpd1(\"{}\"),\n", .{name});
|
|
} else if (pd1 and !pd2 and !pslash and
|
|
std.mem.eql(u8, syntax_str, ".joined_or_separate"))
|
|
{
|
|
try stdout.print("jspd1(\"{}\"),\n", .{name});
|
|
} else if (pd1 and !pd2 and !pslash and
|
|
std.mem.eql(u8, syntax_str, ".separate"))
|
|
{
|
|
try stdout.print("sepd1(\"{}\"),\n", .{name});
|
|
} else {
|
|
try stdout.print(
|
|
\\.{{
|
|
\\ .name = "{}",
|
|
\\ .syntax = {},
|
|
\\ .zig_equivalent = .other,
|
|
\\ .pd1 = {},
|
|
\\ .pd2 = {},
|
|
\\ .psl = {},
|
|
\\}},
|
|
\\
|
|
, .{ name, syntax_str, pd1, pd2, pslash });
|
|
}
|
|
}
|
|
|
|
try stdout.writeAll(
|
|
\\};};
|
|
\\
|
|
);
|
|
|
|
try stdout_bos.flush();
|
|
}
|
|
|
|
fn nameLessThan(a: []const u8, b: []const u8) bool {
|
|
return std.mem.lessThan(u8, a, b);
|
|
}
|
|
|
|
fn usageAndExit(file: fs.File, arg0: []const u8, code: u8) noreturn {
|
|
file.outStream().print(
|
|
\\Usage: {} /path/to/llvm-tblgen /path/to/git/llvm/llvm-project
|
|
\\
|
|
\\Prints to stdout Zig code which you can use to replace the file src-self-hosted/clang_options_data.zig.
|
|
\\
|
|
, .{arg0}) catch std.process.exit(1);
|
|
std.process.exit(code);
|
|
}
|