mirror of
https://github.com/ziglang/zig.git
synced 2025-12-25 07:33:08 +00:00
Merge pull request #4774 from ziglang/zig-cc
ability to use `zig cc` as a drop-in C compiler
This commit is contained in:
commit
7ffdf59c44
126
src-self-hosted/clang_options.zig
Normal file
126
src-self-hosted/clang_options.zig
Normal file
@ -0,0 +1,126 @@
|
||||
const std = @import("std");
|
||||
const mem = std.mem;
|
||||
|
||||
pub const list = @import("clang_options_data.zig").data;
|
||||
|
||||
pub const CliArg = struct {
|
||||
name: []const u8,
|
||||
syntax: Syntax,
|
||||
|
||||
/// TODO we're going to want to change this when we start shipping self-hosted because this causes
|
||||
/// all the functions in stage2.zig to get exported.
|
||||
zig_equivalent: @import("stage2.zig").ClangArgIterator.ZigEquivalent,
|
||||
|
||||
/// Prefixed by "-"
|
||||
pd1: bool = false,
|
||||
|
||||
/// Prefixed by "--"
|
||||
pd2: bool = false,
|
||||
|
||||
/// Prefixed by "/"
|
||||
psl: bool = false,
|
||||
|
||||
pub const Syntax = union(enum) {
|
||||
/// A flag with no values.
|
||||
flag,
|
||||
|
||||
/// An option which prefixes its (single) value.
|
||||
joined,
|
||||
|
||||
/// An option which is followed by its value.
|
||||
separate,
|
||||
|
||||
/// An option which is either joined to its (non-empty) value, or followed by its value.
|
||||
joined_or_separate,
|
||||
|
||||
/// An option which is both joined to its (first) value, and followed by its (second) value.
|
||||
joined_and_separate,
|
||||
|
||||
/// An option followed by its values, which are separated by commas.
|
||||
comma_joined,
|
||||
|
||||
/// An option which consumes an optional joined argument and any other remaining arguments.
|
||||
remaining_args_joined,
|
||||
|
||||
/// An option which is which takes multiple (separate) arguments.
|
||||
multi_arg: u8,
|
||||
};
|
||||
|
||||
pub fn matchEql(self: CliArg, arg: []const u8) u2 {
|
||||
if (self.pd1 and arg.len >= self.name.len + 1 and
|
||||
mem.startsWith(u8, arg, "-") and mem.eql(u8, arg[1..], self.name))
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
if (self.pd2 and arg.len >= self.name.len + 2 and
|
||||
mem.startsWith(u8, arg, "--") and mem.eql(u8, arg[2..], self.name))
|
||||
{
|
||||
return 2;
|
||||
}
|
||||
if (self.psl and arg.len >= self.name.len + 1 and
|
||||
mem.startsWith(u8, arg, "/") and mem.eql(u8, arg[1..], self.name))
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
pub fn matchStartsWith(self: CliArg, arg: []const u8) usize {
|
||||
if (self.pd1 and arg.len >= self.name.len + 1 and
|
||||
mem.startsWith(u8, arg, "-") and mem.startsWith(u8, arg[1..], self.name))
|
||||
{
|
||||
return self.name.len + 1;
|
||||
}
|
||||
if (self.pd2 and arg.len >= self.name.len + 2 and
|
||||
mem.startsWith(u8, arg, "--") and mem.startsWith(u8, arg[2..], self.name))
|
||||
{
|
||||
return self.name.len + 2;
|
||||
}
|
||||
if (self.psl and arg.len >= self.name.len + 1 and
|
||||
mem.startsWith(u8, arg, "/") and mem.startsWith(u8, arg[1..], self.name))
|
||||
{
|
||||
return self.name.len + 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
/// Shortcut function for initializing a `CliArg`
|
||||
pub fn flagpd1(name: []const u8) CliArg {
|
||||
return .{
|
||||
.name = name,
|
||||
.syntax = .flag,
|
||||
.zig_equivalent = .other,
|
||||
.pd1 = true,
|
||||
};
|
||||
}
|
||||
|
||||
/// Shortcut function for initializing a `CliArg`
|
||||
pub fn joinpd1(name: []const u8) CliArg {
|
||||
return .{
|
||||
.name = name,
|
||||
.syntax = .joined,
|
||||
.zig_equivalent = .other,
|
||||
.pd1 = true,
|
||||
};
|
||||
}
|
||||
|
||||
/// Shortcut function for initializing a `CliArg`
|
||||
pub fn jspd1(name: []const u8) CliArg {
|
||||
return .{
|
||||
.name = name,
|
||||
.syntax = .joined_or_separate,
|
||||
.zig_equivalent = .other,
|
||||
.pd1 = true,
|
||||
};
|
||||
}
|
||||
|
||||
/// Shortcut function for initializing a `CliArg`
|
||||
pub fn sepd1(name: []const u8) CliArg {
|
||||
return .{
|
||||
.name = name,
|
||||
.syntax = .separate,
|
||||
.zig_equivalent = .other,
|
||||
.pd1 = true,
|
||||
};
|
||||
}
|
||||
5702
src-self-hosted/clang_options_data.zig
Normal file
5702
src-self-hosted/clang_options_data.zig
Normal file
File diff suppressed because it is too large
Load Diff
@ -113,6 +113,7 @@ const Error = extern enum {
|
||||
TargetHasNoDynamicLinker,
|
||||
InvalidAbiVersion,
|
||||
InvalidOperatingSystemVersion,
|
||||
UnknownClangOption,
|
||||
};
|
||||
|
||||
const FILE = std.c.FILE;
|
||||
@ -1215,3 +1216,173 @@ fn convertSlice(slice: [][:0]u8, ptr: *[*][*:0]u8, len: *usize) !void {
|
||||
}
|
||||
ptr.* = new_slice.ptr;
|
||||
}
|
||||
|
||||
const clang_args = @import("clang_options.zig").list;
|
||||
|
||||
// ABI warning
|
||||
pub const ClangArgIterator = extern struct {
|
||||
has_next: bool,
|
||||
zig_equivalent: ZigEquivalent,
|
||||
only_arg: [*:0]const u8,
|
||||
second_arg: [*:0]const u8,
|
||||
other_args_ptr: [*]const [*:0]const u8,
|
||||
other_args_len: usize,
|
||||
argv_ptr: [*]const [*:0]const u8,
|
||||
argv_len: usize,
|
||||
next_index: usize,
|
||||
|
||||
// ABI warning
|
||||
pub const ZigEquivalent = extern enum {
|
||||
target,
|
||||
o,
|
||||
c,
|
||||
other,
|
||||
positional,
|
||||
l,
|
||||
ignore,
|
||||
driver_punt,
|
||||
pic,
|
||||
no_pic,
|
||||
nostdlib,
|
||||
shared,
|
||||
rdynamic,
|
||||
wl,
|
||||
preprocess,
|
||||
optimize,
|
||||
debug,
|
||||
sanitize,
|
||||
};
|
||||
|
||||
fn init(argv: []const [*:0]const u8) ClangArgIterator {
|
||||
return .{
|
||||
.next_index = 2, // `zig cc foo` this points to `foo`
|
||||
.has_next = argv.len > 2,
|
||||
.zig_equivalent = undefined,
|
||||
.only_arg = undefined,
|
||||
.second_arg = undefined,
|
||||
.other_args_ptr = undefined,
|
||||
.other_args_len = undefined,
|
||||
.argv_ptr = argv.ptr,
|
||||
.argv_len = argv.len,
|
||||
};
|
||||
}
|
||||
|
||||
fn next(self: *ClangArgIterator) !void {
|
||||
assert(self.has_next);
|
||||
assert(self.next_index < self.argv_len);
|
||||
// In this state we know that the parameter we are looking at is a root parameter
|
||||
// rather than an argument to a parameter.
|
||||
self.other_args_ptr = self.argv_ptr + self.next_index;
|
||||
self.other_args_len = 1; // We adjust this value below when necessary.
|
||||
const arg = mem.span(self.argv_ptr[self.next_index]);
|
||||
self.next_index += 1;
|
||||
defer {
|
||||
if (self.next_index >= self.argv_len) self.has_next = false;
|
||||
}
|
||||
|
||||
if (!mem.startsWith(u8, arg, "-")) {
|
||||
self.zig_equivalent = .positional;
|
||||
self.only_arg = arg.ptr;
|
||||
return;
|
||||
}
|
||||
|
||||
find_clang_arg: for (clang_args) |clang_arg| switch (clang_arg.syntax) {
|
||||
.flag => {
|
||||
const prefix_len = clang_arg.matchEql(arg);
|
||||
if (prefix_len > 0) {
|
||||
self.zig_equivalent = clang_arg.zig_equivalent;
|
||||
self.only_arg = arg.ptr + prefix_len;
|
||||
|
||||
break :find_clang_arg;
|
||||
}
|
||||
},
|
||||
.joined, .comma_joined => {
|
||||
// joined example: --target=foo
|
||||
// comma_joined example: -Wl,-soname,libsoundio.so.2
|
||||
const prefix_len = clang_arg.matchStartsWith(arg);
|
||||
if (prefix_len != 0) {
|
||||
self.zig_equivalent = clang_arg.zig_equivalent;
|
||||
self.only_arg = arg.ptr + prefix_len; // This will skip over the "--target=" part.
|
||||
|
||||
break :find_clang_arg;
|
||||
}
|
||||
},
|
||||
.joined_or_separate => {
|
||||
// Examples: `-lfoo`, `-l foo`
|
||||
const prefix_len = clang_arg.matchStartsWith(arg);
|
||||
if (prefix_len == arg.len) {
|
||||
if (self.next_index >= self.argv_len) {
|
||||
std.debug.warn("Expected parameter after '{}'\n", .{arg});
|
||||
process.exit(1);
|
||||
}
|
||||
self.only_arg = self.argv_ptr[self.next_index];
|
||||
self.next_index += 1;
|
||||
self.other_args_len += 1;
|
||||
self.zig_equivalent = clang_arg.zig_equivalent;
|
||||
|
||||
break :find_clang_arg;
|
||||
} else if (prefix_len != 0) {
|
||||
self.zig_equivalent = clang_arg.zig_equivalent;
|
||||
self.only_arg = arg.ptr + prefix_len;
|
||||
|
||||
break :find_clang_arg;
|
||||
}
|
||||
},
|
||||
.joined_and_separate => {
|
||||
// Example: `-Xopenmp-target=riscv64-linux-unknown foo`
|
||||
const prefix_len = clang_arg.matchStartsWith(arg);
|
||||
if (prefix_len != 0) {
|
||||
self.only_arg = arg.ptr + prefix_len;
|
||||
if (self.next_index >= self.argv_len) {
|
||||
std.debug.warn("Expected parameter after '{}'\n", .{arg});
|
||||
process.exit(1);
|
||||
}
|
||||
self.second_arg = self.argv_ptr[self.next_index];
|
||||
self.next_index += 1;
|
||||
self.other_args_len += 1;
|
||||
self.zig_equivalent = clang_arg.zig_equivalent;
|
||||
break :find_clang_arg;
|
||||
}
|
||||
},
|
||||
.separate => if (clang_arg.matchEql(arg) > 0) {
|
||||
if (self.next_index >= self.argv_len) {
|
||||
std.debug.warn("Expected parameter after '{}'\n", .{arg});
|
||||
process.exit(1);
|
||||
}
|
||||
self.only_arg = self.argv_ptr[self.next_index];
|
||||
self.next_index += 1;
|
||||
self.other_args_len += 1;
|
||||
self.zig_equivalent = clang_arg.zig_equivalent;
|
||||
break :find_clang_arg;
|
||||
},
|
||||
.remaining_args_joined => {
|
||||
const prefix_len = clang_arg.matchStartsWith(arg);
|
||||
if (prefix_len != 0) {
|
||||
@panic("TODO");
|
||||
}
|
||||
},
|
||||
.multi_arg => if (clang_arg.matchEql(arg) > 0) {
|
||||
@panic("TODO");
|
||||
},
|
||||
}
|
||||
else {
|
||||
std.debug.warn("Unknown Clang option: '{}'\n", .{arg});
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export fn stage2_clang_arg_iterator(
|
||||
result: *ClangArgIterator,
|
||||
argc: usize,
|
||||
argv: [*]const [*:0]const u8,
|
||||
) void {
|
||||
result.* = ClangArgIterator.init(argv[0..argc]);
|
||||
}
|
||||
|
||||
export fn stage2_clang_arg_next(it: *ClangArgIterator) Error {
|
||||
it.next() catch |err| switch (err) {
|
||||
error.UnknownClangOption => return .UnknownClangOption,
|
||||
};
|
||||
return .None;
|
||||
}
|
||||
|
||||
@ -2003,6 +2003,7 @@ enum WantCSanitize {
|
||||
struct CFile {
|
||||
ZigList<const char *> args;
|
||||
const char *source_path;
|
||||
const char *preprocessor_only_basename;
|
||||
};
|
||||
|
||||
// When adding fields, check if they should be added to the hash computation in build_with_cache
|
||||
@ -2147,6 +2148,7 @@ struct CodeGen {
|
||||
// As an input parameter, mutually exclusive with enable_cache. But it gets
|
||||
// populated in codegen_build_and_link.
|
||||
Buf *output_dir;
|
||||
Buf *c_artifact_dir;
|
||||
const char **libc_include_dir_list;
|
||||
size_t libc_include_dir_len;
|
||||
|
||||
|
||||
@ -9269,6 +9269,7 @@ void add_cc_args(CodeGen *g, ZigList<const char *> &args, const char *out_dep_pa
|
||||
case BuildModeDebug:
|
||||
// windows c runtime requires -D_DEBUG if using debug libraries
|
||||
args.append("-D_DEBUG");
|
||||
args.append("-Og");
|
||||
|
||||
if (g->libc_link_lib != nullptr) {
|
||||
args.append("-fstack-protector-strong");
|
||||
@ -9723,13 +9724,17 @@ static void gen_c_object(CodeGen *g, Buf *self_exe_path, CFile *c_file) {
|
||||
buf_len(c_source_basename), 0);
|
||||
|
||||
Buf *final_o_basename = buf_alloc();
|
||||
// We special case when doing build-obj for just one C file
|
||||
if (main_output_dir_is_just_one_c_object_pre(g)) {
|
||||
buf_init_from_buf(final_o_basename, g->root_out_name);
|
||||
if (c_file->preprocessor_only_basename == nullptr) {
|
||||
// We special case when doing build-obj for just one C file
|
||||
if (main_output_dir_is_just_one_c_object_pre(g)) {
|
||||
buf_init_from_buf(final_o_basename, g->root_out_name);
|
||||
} else {
|
||||
os_path_extname(c_source_basename, final_o_basename, nullptr);
|
||||
}
|
||||
buf_append_str(final_o_basename, target_o_file_ext(g->zig_target));
|
||||
} else {
|
||||
os_path_extname(c_source_basename, final_o_basename, nullptr);
|
||||
buf_init_from_str(final_o_basename, c_file->preprocessor_only_basename);
|
||||
}
|
||||
buf_append_str(final_o_basename, target_o_file_ext(g->zig_target));
|
||||
|
||||
CacheHash *cache_hash;
|
||||
if ((err = create_c_object_cache(g, &cache_hash, true))) {
|
||||
@ -9778,7 +9783,13 @@ static void gen_c_object(CodeGen *g, Buf *self_exe_path, CFile *c_file) {
|
||||
Termination term;
|
||||
ZigList<const char *> args = {};
|
||||
args.append(buf_ptr(self_exe_path));
|
||||
args.append("cc");
|
||||
args.append("clang");
|
||||
|
||||
if (c_file->preprocessor_only_basename != nullptr) {
|
||||
args.append("-E");
|
||||
} else {
|
||||
args.append("-c");
|
||||
}
|
||||
|
||||
Buf *out_dep_path = buf_sprintf("%s.d", buf_ptr(out_obj_path));
|
||||
add_cc_args(g, args, buf_ptr(out_dep_path), false);
|
||||
@ -9786,7 +9797,6 @@ static void gen_c_object(CodeGen *g, Buf *self_exe_path, CFile *c_file) {
|
||||
args.append("-o");
|
||||
args.append(buf_ptr(out_obj_path));
|
||||
|
||||
args.append("-c");
|
||||
args.append(buf_ptr(c_source_file));
|
||||
|
||||
for (size_t arg_i = 0; arg_i < c_file->args.length; arg_i += 1) {
|
||||
@ -9841,6 +9851,7 @@ static void gen_c_object(CodeGen *g, Buf *self_exe_path, CFile *c_file) {
|
||||
os_path_join(artifact_dir, final_o_basename, o_final_path);
|
||||
}
|
||||
|
||||
g->c_artifact_dir = artifact_dir;
|
||||
g->link_objects.append(o_final_path);
|
||||
g->caches_to_release.append(cache_hash);
|
||||
|
||||
|
||||
@ -83,6 +83,7 @@ const char *err_str(Error err) {
|
||||
case ErrorTargetHasNoDynamicLinker: return "target has no dynamic linker";
|
||||
case ErrorInvalidAbiVersion: return "invalid C ABI version";
|
||||
case ErrorInvalidOperatingSystemVersion: return "invalid operating system version";
|
||||
case ErrorUnknownClangOption: return "unknown Clang option";
|
||||
}
|
||||
return "(invalid error)";
|
||||
}
|
||||
|
||||
@ -2008,7 +2008,7 @@ static const char *get_def_lib(CodeGen *parent, const char *name, Buf *def_in_fi
|
||||
|
||||
ZigList<const char *> args = {};
|
||||
args.append(buf_ptr(self_exe_path));
|
||||
args.append("cc");
|
||||
args.append("clang");
|
||||
args.append("-x");
|
||||
args.append("c");
|
||||
args.append(buf_ptr(def_in_file));
|
||||
|
||||
284
src/main.cpp
284
src/main.cpp
@ -36,7 +36,7 @@ static int print_full_usage(const char *arg0, FILE *file, int return_code) {
|
||||
" build-lib [source] create library from source or object files\n"
|
||||
" build-obj [source] create object from source or assembly\n"
|
||||
" builtin show the source code of @import(\"builtin\")\n"
|
||||
" cc C compiler\n"
|
||||
" cc use Zig as a drop-in C compiler\n"
|
||||
" fmt parse files and render in canonical zig format\n"
|
||||
" id print the base64-encoded compiler id\n"
|
||||
" init-exe initialize a `zig build` application in the cwd\n"
|
||||
@ -271,7 +271,7 @@ static int main0(int argc, char **argv) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (argc >= 2 && (strcmp(argv[1], "cc") == 0 ||
|
||||
if (argc >= 2 && (strcmp(argv[1], "clang") == 0 ||
|
||||
strcmp(argv[1], "-cc1") == 0 || strcmp(argv[1], "-cc1as") == 0))
|
||||
{
|
||||
return ZigClang_main(argc, argv);
|
||||
@ -430,6 +430,7 @@ static int main0(int argc, char **argv) {
|
||||
bool enable_dump_analysis = false;
|
||||
bool enable_doc_generation = false;
|
||||
bool emit_bin = true;
|
||||
const char *emit_bin_override_path = nullptr;
|
||||
bool emit_asm = false;
|
||||
bool emit_llvm_ir = false;
|
||||
bool emit_h = false;
|
||||
@ -451,6 +452,8 @@ static int main0(int argc, char **argv) {
|
||||
bool function_sections = false;
|
||||
const char *mcpu = nullptr;
|
||||
CodeModel code_model = CodeModelDefault;
|
||||
const char *override_soname = nullptr;
|
||||
bool only_preprocess = false;
|
||||
|
||||
ZigList<const char *> llvm_argv = {0};
|
||||
llvm_argv.append("zig (LLVM option parsing)");
|
||||
@ -575,9 +578,228 @@ static int main0(int argc, char **argv) {
|
||||
return (term.how == TerminationIdClean) ? term.code : -1;
|
||||
} else if (argc >= 2 && strcmp(argv[1], "fmt") == 0) {
|
||||
return stage2_fmt(argc, argv);
|
||||
}
|
||||
} else if (argc >= 2 && strcmp(argv[1], "cc") == 0) {
|
||||
emit_h = false;
|
||||
strip = true;
|
||||
|
||||
for (int i = 1; i < argc; i += 1) {
|
||||
bool c_arg = false;
|
||||
Stage2ClangArgIterator it;
|
||||
stage2_clang_arg_iterator(&it, argc, argv);
|
||||
bool nostdlib = false;
|
||||
bool is_shared_lib = false;
|
||||
ZigList<Buf *> linker_args = {};
|
||||
while (it.has_next) {
|
||||
if ((err = stage2_clang_arg_next(&it))) {
|
||||
fprintf(stderr, "unable to parse command line parameters: %s\n", err_str(err));
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
switch (it.kind) {
|
||||
case Stage2ClangArgTarget: // example: -target riscv64-linux-unknown
|
||||
target_string = it.only_arg;
|
||||
break;
|
||||
case Stage2ClangArgO: // -o
|
||||
emit_bin_override_path = it.only_arg;
|
||||
enable_cache = CacheOptOn;
|
||||
break;
|
||||
case Stage2ClangArgC: // -c
|
||||
c_arg = true;
|
||||
break;
|
||||
case Stage2ClangArgOther:
|
||||
for (size_t i = 0; i < it.other_args_len; i += 1) {
|
||||
clang_argv.append(it.other_args_ptr[i]);
|
||||
}
|
||||
break;
|
||||
case Stage2ClangArgPositional: {
|
||||
Buf *arg_buf = buf_create_from_str(it.only_arg);
|
||||
if (buf_ends_with_str(arg_buf, ".c") ||
|
||||
buf_ends_with_str(arg_buf, ".cc") ||
|
||||
buf_ends_with_str(arg_buf, ".cpp") ||
|
||||
buf_ends_with_str(arg_buf, ".cxx") ||
|
||||
buf_ends_with_str(arg_buf, ".s"))
|
||||
{
|
||||
CFile *c_file = heap::c_allocator.create<CFile>();
|
||||
c_file->source_path = it.only_arg;
|
||||
c_source_files.append(c_file);
|
||||
} else {
|
||||
objects.append(it.only_arg);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case Stage2ClangArgL: // -l
|
||||
if (strcmp(it.only_arg, "c") == 0)
|
||||
have_libc = true;
|
||||
link_libs.append(it.only_arg);
|
||||
break;
|
||||
case Stage2ClangArgIgnore:
|
||||
break;
|
||||
case Stage2ClangArgDriverPunt:
|
||||
// Never mind what we're doing, just pass the args directly. For example --help.
|
||||
return ZigClang_main(argc, argv);
|
||||
case Stage2ClangArgPIC:
|
||||
want_pic = WantPICEnabled;
|
||||
break;
|
||||
case Stage2ClangArgNoPIC:
|
||||
want_pic = WantPICDisabled;
|
||||
break;
|
||||
case Stage2ClangArgNoStdLib:
|
||||
nostdlib = true;
|
||||
break;
|
||||
case Stage2ClangArgShared:
|
||||
is_dynamic = true;
|
||||
is_shared_lib = true;
|
||||
break;
|
||||
case Stage2ClangArgRDynamic:
|
||||
rdynamic = true;
|
||||
break;
|
||||
case Stage2ClangArgWL: {
|
||||
const char *arg = it.only_arg;
|
||||
for (;;) {
|
||||
size_t pos = 0;
|
||||
while (arg[pos] != ',' && arg[pos] != 0) pos += 1;
|
||||
linker_args.append(buf_create_from_mem(arg, pos));
|
||||
if (arg[pos] == 0) break;
|
||||
arg += pos + 1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case Stage2ClangArgPreprocess:
|
||||
only_preprocess = true;
|
||||
break;
|
||||
case Stage2ClangArgOptimize:
|
||||
// alright what release mode do they want?
|
||||
if (strcmp(it.only_arg, "Os") == 0) {
|
||||
build_mode = BuildModeSmallRelease;
|
||||
} else if (strcmp(it.only_arg, "O2") == 0 ||
|
||||
strcmp(it.only_arg, "O3") == 0 ||
|
||||
strcmp(it.only_arg, "O4") == 0)
|
||||
{
|
||||
build_mode = BuildModeFastRelease;
|
||||
} else if (strcmp(it.only_arg, "Og") == 0) {
|
||||
build_mode = BuildModeDebug;
|
||||
} else {
|
||||
for (size_t i = 0; i < it.other_args_len; i += 1) {
|
||||
clang_argv.append(it.other_args_ptr[i]);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case Stage2ClangArgDebug:
|
||||
strip = false;
|
||||
if (strcmp(it.only_arg, "-g") == 0) {
|
||||
// we handled with strip = false above
|
||||
} else {
|
||||
for (size_t i = 0; i < it.other_args_len; i += 1) {
|
||||
clang_argv.append(it.other_args_ptr[i]);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case Stage2ClangArgSanitize:
|
||||
if (strcmp(it.only_arg, "undefined") == 0) {
|
||||
want_sanitize_c = WantCSanitizeEnabled;
|
||||
} else {
|
||||
for (size_t i = 0; i < it.other_args_len; i += 1) {
|
||||
clang_argv.append(it.other_args_ptr[i]);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
// Parse linker args
|
||||
for (size_t i = 0; i < linker_args.length; i += 1) {
|
||||
Buf *arg = linker_args.at(i);
|
||||
if (buf_eql_str(arg, "-soname")) {
|
||||
i += 1;
|
||||
if (i >= linker_args.length) {
|
||||
fprintf(stderr, "expected linker arg after '%s'\n", buf_ptr(arg));
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
Buf *soname_buf = linker_args.at(i);
|
||||
override_soname = buf_ptr(soname_buf);
|
||||
// use it as --name
|
||||
// example: libsoundio.so.2
|
||||
size_t prefix = 0;
|
||||
if (buf_starts_with_str(soname_buf, "lib")) {
|
||||
prefix = 3;
|
||||
}
|
||||
size_t end = buf_len(soname_buf);
|
||||
if (buf_ends_with_str(soname_buf, ".so")) {
|
||||
end -= 3;
|
||||
} else {
|
||||
bool found_digit = false;
|
||||
while (end > 0 && isdigit(buf_ptr(soname_buf)[end - 1])) {
|
||||
found_digit = true;
|
||||
end -= 1;
|
||||
}
|
||||
if (found_digit && end > 0 && buf_ptr(soname_buf)[end - 1] == '.') {
|
||||
end -= 1;
|
||||
} else {
|
||||
end = buf_len(soname_buf);
|
||||
}
|
||||
if (buf_ends_with_str(buf_slice(soname_buf, prefix, end), ".so")) {
|
||||
end -= 3;
|
||||
}
|
||||
}
|
||||
out_name = buf_ptr(buf_slice(soname_buf, prefix, end));
|
||||
} else if (buf_eql_str(arg, "-rpath")) {
|
||||
i += 1;
|
||||
if (i >= linker_args.length) {
|
||||
fprintf(stderr, "expected linker arg after '%s'\n", buf_ptr(arg));
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
Buf *rpath = linker_args.at(i);
|
||||
rpath_list.append(buf_ptr(rpath));
|
||||
} else {
|
||||
fprintf(stderr, "warning: unsupported linker arg: %s\n", buf_ptr(arg));
|
||||
}
|
||||
}
|
||||
|
||||
if (want_sanitize_c == WantCSanitizeEnabled && build_mode == BuildModeFastRelease) {
|
||||
build_mode = BuildModeSafeRelease;
|
||||
}
|
||||
|
||||
if (!nostdlib && !have_libc) {
|
||||
have_libc = true;
|
||||
link_libs.append("c");
|
||||
}
|
||||
if (only_preprocess) {
|
||||
cmd = CmdBuild;
|
||||
out_type = OutTypeObj;
|
||||
emit_bin = false;
|
||||
// Transfer "objects" into c_source_files
|
||||
for (size_t i = 0; i < objects.length; i += 1) {
|
||||
CFile *c_file = heap::c_allocator.create<CFile>();
|
||||
c_file->source_path = objects.at(i);
|
||||
c_source_files.append(c_file);
|
||||
}
|
||||
for (size_t i = 0; i < c_source_files.length; i += 1) {
|
||||
Buf *src_path;
|
||||
if (emit_bin_override_path != nullptr) {
|
||||
src_path = buf_create_from_str(emit_bin_override_path);
|
||||
} else {
|
||||
src_path = buf_create_from_str(c_source_files.at(i)->source_path);
|
||||
}
|
||||
Buf basename = BUF_INIT;
|
||||
os_path_split(src_path, nullptr, &basename);
|
||||
c_source_files.at(i)->preprocessor_only_basename = buf_ptr(&basename);
|
||||
}
|
||||
} else if (!c_arg) {
|
||||
cmd = CmdBuild;
|
||||
if (is_shared_lib) {
|
||||
out_type = OutTypeLib;
|
||||
} else {
|
||||
out_type = OutTypeExe;
|
||||
}
|
||||
if (emit_bin_override_path == nullptr) {
|
||||
emit_bin_override_path = "a.out";
|
||||
}
|
||||
} else {
|
||||
cmd = CmdBuild;
|
||||
out_type = OutTypeObj;
|
||||
}
|
||||
if (c_source_files.length == 0 && objects.length == 0) {
|
||||
// For example `zig cc` and no args should print the "no input files" message.
|
||||
return ZigClang_main(argc, argv);
|
||||
}
|
||||
} else for (int i = 1; i < argc; i += 1) {
|
||||
char *arg = argv[i];
|
||||
|
||||
if (arg[0] == '-') {
|
||||
@ -1138,6 +1360,18 @@ static int main0(int argc, char **argv) {
|
||||
buf_out_name = buf_alloc();
|
||||
os_path_extname(&basename, buf_out_name, nullptr);
|
||||
}
|
||||
if (need_name && buf_out_name == nullptr && objects.length == 1) {
|
||||
Buf basename = BUF_INIT;
|
||||
os_path_split(buf_create_from_str(objects.at(0)), nullptr, &basename);
|
||||
buf_out_name = buf_alloc();
|
||||
os_path_extname(&basename, buf_out_name, nullptr);
|
||||
}
|
||||
if (need_name && buf_out_name == nullptr && emit_bin_override_path != nullptr) {
|
||||
Buf basename = BUF_INIT;
|
||||
os_path_split(buf_create_from_str(emit_bin_override_path), nullptr, &basename);
|
||||
buf_out_name = buf_alloc();
|
||||
os_path_extname(&basename, buf_out_name, nullptr);
|
||||
}
|
||||
|
||||
if (need_name && buf_out_name == nullptr) {
|
||||
fprintf(stderr, "--name [name] not provided and unable to infer\n\n");
|
||||
@ -1213,6 +1447,10 @@ static int main0(int argc, char **argv) {
|
||||
g->function_sections = function_sections;
|
||||
g->code_model = code_model;
|
||||
|
||||
if (override_soname) {
|
||||
g->override_soname = buf_create_from_str(override_soname);
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < lib_dirs.length; i += 1) {
|
||||
codegen_add_lib_dir(g, lib_dirs.at(i));
|
||||
}
|
||||
@ -1291,7 +1529,43 @@ static int main0(int argc, char **argv) {
|
||||
os_spawn_process(args, &term);
|
||||
return term.code;
|
||||
} else if (cmd == CmdBuild) {
|
||||
if (g->enable_cache) {
|
||||
if (emit_bin_override_path != nullptr) {
|
||||
#if defined(ZIG_OS_WINDOWS)
|
||||
buf_replace(g->output_dir, '/', '\\');
|
||||
#endif
|
||||
Buf *dest_path = buf_create_from_str(emit_bin_override_path);
|
||||
Buf *source_path;
|
||||
if (only_preprocess) {
|
||||
source_path = buf_alloc();
|
||||
Buf *pp_only_basename = buf_create_from_str(
|
||||
c_source_files.at(0)->preprocessor_only_basename);
|
||||
os_path_join(g->output_dir, pp_only_basename, source_path);
|
||||
|
||||
} else {
|
||||
source_path = &g->bin_file_output_path;
|
||||
}
|
||||
if ((err = os_update_file(source_path, dest_path))) {
|
||||
fprintf(stderr, "unable to copy %s to %s: %s\n", buf_ptr(source_path),
|
||||
buf_ptr(dest_path), err_str(err));
|
||||
return main_exit(root_progress_node, EXIT_FAILURE);
|
||||
}
|
||||
} else if (only_preprocess) {
|
||||
#if defined(ZIG_OS_WINDOWS)
|
||||
buf_replace(g->c_artifact_dir, '/', '\\');
|
||||
#endif
|
||||
// dump the preprocessed output to stdout
|
||||
for (size_t i = 0; i < c_source_files.length; i += 1) {
|
||||
Buf *source_path = buf_alloc();
|
||||
Buf *pp_only_basename = buf_create_from_str(
|
||||
c_source_files.at(i)->preprocessor_only_basename);
|
||||
os_path_join(g->c_artifact_dir, pp_only_basename, source_path);
|
||||
if ((err = os_dump_file(source_path, stdout))) {
|
||||
fprintf(stderr, "unable to read %s: %s\n", buf_ptr(source_path),
|
||||
err_str(err));
|
||||
return main_exit(root_progress_node, EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
} else if (g->enable_cache) {
|
||||
#if defined(ZIG_OS_WINDOWS)
|
||||
buf_replace(&g->bin_file_output_path, '/', '\\');
|
||||
buf_replace(g->output_dir, '/', '\\');
|
||||
|
||||
24
src/os.cpp
24
src/os.cpp
@ -1051,6 +1051,30 @@ static Error copy_open_files(FILE *src_f, FILE *dest_f) {
|
||||
}
|
||||
}
|
||||
|
||||
Error os_dump_file(Buf *src_path, FILE *dest_file) {
|
||||
Error err;
|
||||
|
||||
FILE *src_f = fopen(buf_ptr(src_path), "rb");
|
||||
if (!src_f) {
|
||||
int err = errno;
|
||||
if (err == ENOENT) {
|
||||
return ErrorFileNotFound;
|
||||
} else if (err == EACCES || err == EPERM) {
|
||||
return ErrorAccess;
|
||||
} else {
|
||||
return ErrorFileSystem;
|
||||
}
|
||||
}
|
||||
copy_open_files(src_f, dest_file);
|
||||
if ((err = copy_open_files(src_f, dest_file))) {
|
||||
fclose(src_f);
|
||||
return err;
|
||||
}
|
||||
|
||||
fclose(src_f);
|
||||
return ErrorNone;
|
||||
}
|
||||
|
||||
#if defined(ZIG_OS_WINDOWS)
|
||||
static void windows_filetime_to_os_timestamp(FILETIME *ft, OsTimeStamp *mtime) {
|
||||
mtime->sec = (((ULONGLONG) ft->dwHighDateTime) << 32) + ft->dwLowDateTime;
|
||||
|
||||
@ -129,6 +129,7 @@ void os_file_close(OsFile *file);
|
||||
Error ATTRIBUTE_MUST_USE os_write_file(Buf *full_path, Buf *contents);
|
||||
Error ATTRIBUTE_MUST_USE os_copy_file(Buf *src_path, Buf *dest_path);
|
||||
Error ATTRIBUTE_MUST_USE os_update_file(Buf *src_path, Buf *dest_path);
|
||||
Error ATTRIBUTE_MUST_USE os_dump_file(Buf *src_path, FILE *dest_file);
|
||||
|
||||
Error ATTRIBUTE_MUST_USE os_fetch_file(FILE *file, Buf *out_contents);
|
||||
Error ATTRIBUTE_MUST_USE os_fetch_file_path(Buf *full_path, Buf *out_contents);
|
||||
|
||||
@ -304,3 +304,15 @@ enum Error stage2_detect_native_paths(struct Stage2NativePaths *native_paths) {
|
||||
|
||||
return ErrorNone;
|
||||
}
|
||||
|
||||
void stage2_clang_arg_iterator(struct Stage2ClangArgIterator *it,
|
||||
size_t argc, char **argv)
|
||||
{
|
||||
const char *msg = "stage0 called stage2_clang_arg_iterator";
|
||||
stage2_panic(msg, strlen(msg));
|
||||
}
|
||||
|
||||
enum Error stage2_clang_arg_next(struct Stage2ClangArgIterator *it) {
|
||||
const char *msg = "stage0 called stage2_clang_arg_next";
|
||||
stage2_panic(msg, strlen(msg));
|
||||
}
|
||||
|
||||
43
src/stage2.h
43
src/stage2.h
@ -105,6 +105,7 @@ enum Error {
|
||||
ErrorTargetHasNoDynamicLinker,
|
||||
ErrorInvalidAbiVersion,
|
||||
ErrorInvalidOperatingSystemVersion,
|
||||
ErrorUnknownClangOption,
|
||||
};
|
||||
|
||||
// ABI warning
|
||||
@ -316,4 +317,46 @@ struct Stage2NativePaths {
|
||||
// ABI warning
|
||||
ZIG_EXTERN_C enum Error stage2_detect_native_paths(struct Stage2NativePaths *native_paths);
|
||||
|
||||
// ABI warning
|
||||
enum Stage2ClangArg {
|
||||
Stage2ClangArgTarget,
|
||||
Stage2ClangArgO,
|
||||
Stage2ClangArgC,
|
||||
Stage2ClangArgOther,
|
||||
Stage2ClangArgPositional,
|
||||
Stage2ClangArgL,
|
||||
Stage2ClangArgIgnore,
|
||||
Stage2ClangArgDriverPunt,
|
||||
Stage2ClangArgPIC,
|
||||
Stage2ClangArgNoPIC,
|
||||
Stage2ClangArgNoStdLib,
|
||||
Stage2ClangArgShared,
|
||||
Stage2ClangArgRDynamic,
|
||||
Stage2ClangArgWL,
|
||||
Stage2ClangArgPreprocess,
|
||||
Stage2ClangArgOptimize,
|
||||
Stage2ClangArgDebug,
|
||||
Stage2ClangArgSanitize,
|
||||
};
|
||||
|
||||
// ABI warning
|
||||
struct Stage2ClangArgIterator {
|
||||
bool has_next;
|
||||
enum Stage2ClangArg kind;
|
||||
const char *only_arg;
|
||||
const char *second_arg;
|
||||
const char **other_args_ptr;
|
||||
size_t other_args_len;
|
||||
const char **argv_ptr;
|
||||
size_t argv_len;
|
||||
size_t next_index;
|
||||
};
|
||||
|
||||
// ABI warning
|
||||
ZIG_EXTERN_C void stage2_clang_arg_iterator(struct Stage2ClangArgIterator *it,
|
||||
size_t argc, char **argv);
|
||||
|
||||
// ABI warning
|
||||
ZIG_EXTERN_C enum Error stage2_clang_arg_next(struct Stage2ClangArgIterator *it);
|
||||
|
||||
#endif
|
||||
|
||||
@ -1,14 +1,14 @@
|
||||
// To get started, run this tool with no args and read the help message.
|
||||
//
|
||||
// The build systems of musl-libc and glibc require specifying a single target
|
||||
// architecture. Meanwhile, Zig supports out-of-the-box cross compilation for
|
||||
// every target. So the process to create libc headers that Zig ships is to use
|
||||
// this tool.
|
||||
// First, use the musl/glibc build systems to create installations of all the
|
||||
// targets in the `glibc_targets`/`musl_targets` variables.
|
||||
// Next, run this tool to create a new directory which puts .h files into
|
||||
// <arch> subdirectories, with `generic` being files that apply to all architectures.
|
||||
// You'll then have to manually update Zig source repo with these new files.
|
||||
//! To get started, run this tool with no args and read the help message.
|
||||
//!
|
||||
//! The build systems of musl-libc and glibc require specifying a single target
|
||||
//! architecture. Meanwhile, Zig supports out-of-the-box cross compilation for
|
||||
//! every target. So the process to create libc headers that Zig ships is to use
|
||||
//! this tool.
|
||||
//! First, use the musl/glibc build systems to create installations of all the
|
||||
//! targets in the `glibc_targets`/`musl_targets` variables.
|
||||
//! Next, run this tool to create a new directory which puts .h files into
|
||||
//! <arch> subdirectories, with `generic` being files that apply to all architectures.
|
||||
//! You'll then have to manually update Zig source repo with these new files.
|
||||
|
||||
const std = @import("std");
|
||||
const Arch = std.Target.Cpu.Arch;
|
||||
|
||||
460
tools/update_clang_options.zig
Normal file
460
tools/update_clang_options.zig
Normal file
@ -0,0 +1,460 @@
|
||||
//! 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",
|
||||
},
|
||||
.{
|
||||
.name = "pipe",
|
||||
.ident = "ignore",
|
||||
},
|
||||
.{
|
||||
.name = "help",
|
||||
.ident = "driver_punt",
|
||||
},
|
||||
.{
|
||||
.name = "fPIC",
|
||||
.ident = "pic",
|
||||
},
|
||||
.{
|
||||
.name = "fno-PIC",
|
||||
.ident = "no_pic",
|
||||
},
|
||||
.{
|
||||
.name = "nostdlib",
|
||||
.ident = "nostdlib",
|
||||
},
|
||||
.{
|
||||
.name = "no-standard-libraries",
|
||||
.ident = "nostdlib",
|
||||
},
|
||||
.{
|
||||
.name = "shared",
|
||||
.ident = "shared",
|
||||
},
|
||||
.{
|
||||
.name = "rdynamic",
|
||||
.ident = "rdynamic",
|
||||
},
|
||||
.{
|
||||
.name = "Wl,",
|
||||
.ident = "wl",
|
||||
},
|
||||
.{
|
||||
.name = "E",
|
||||
.ident = "preprocess",
|
||||
},
|
||||
.{
|
||||
.name = "preprocess",
|
||||
.ident = "preprocess",
|
||||
},
|
||||
.{
|
||||
.name = "S",
|
||||
.ident = "driver_punt",
|
||||
},
|
||||
.{
|
||||
.name = "assemble",
|
||||
.ident = "driver_punt",
|
||||
},
|
||||
.{
|
||||
.name = "O1",
|
||||
.ident = "optimize",
|
||||
},
|
||||
.{
|
||||
.name = "O2",
|
||||
.ident = "optimize",
|
||||
},
|
||||
.{
|
||||
.name = "Og",
|
||||
.ident = "optimize",
|
||||
},
|
||||
.{
|
||||
.name = "O",
|
||||
.ident = "optimize",
|
||||
},
|
||||
.{
|
||||
.name = "Ofast",
|
||||
.ident = "optimize",
|
||||
},
|
||||
.{
|
||||
.name = "optimize",
|
||||
.ident = "optimize",
|
||||
},
|
||||
.{
|
||||
.name = "g",
|
||||
.ident = "debug",
|
||||
},
|
||||
.{
|
||||
.name = "debug",
|
||||
.ident = "debug",
|
||||
},
|
||||
.{
|
||||
.name = "g-dwarf",
|
||||
.ident = "debug",
|
||||
},
|
||||
.{
|
||||
.name = "g-dwarf-2",
|
||||
.ident = "debug",
|
||||
},
|
||||
.{
|
||||
.name = "g-dwarf-3",
|
||||
.ident = "debug",
|
||||
},
|
||||
.{
|
||||
.name = "g-dwarf-4",
|
||||
.ident = "debug",
|
||||
},
|
||||
.{
|
||||
.name = "g-dwarf-5",
|
||||
.ident = "debug",
|
||||
},
|
||||
.{
|
||||
.name = "fsanitize",
|
||||
.ident = "sanitize",
|
||||
},
|
||||
};
|
||||
|
||||
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_objects = std.ArrayList(*json.ObjectMap).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_objects.append(&kv.value.Object);
|
||||
}
|
||||
}
|
||||
// Some options have multiple matches. As an example, "-Wl,foo" matches both
|
||||
// "W" and "Wl,". So we sort this list in order of descending priority.
|
||||
std.sort.sort(*json.ObjectMap, all_objects.span(), objectLessThan);
|
||||
|
||||
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_objects.span()) |obj| {
|
||||
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("{} has unrecognized prefix '{}'\n", .{ name, prefix });
|
||||
std.process.exit(1);
|
||||
}
|
||||
}
|
||||
const syntax = objSyntax(obj);
|
||||
|
||||
if (knownOption(name)) |ident| {
|
||||
try stdout.print(
|
||||
\\.{{
|
||||
\\ .name = "{}",
|
||||
\\ .syntax = {},
|
||||
\\ .zig_equivalent = .{},
|
||||
\\ .pd1 = {},
|
||||
\\ .pd2 = {},
|
||||
\\ .psl = {},
|
||||
\\}},
|
||||
\\
|
||||
, .{ name, syntax, ident, pd1, pd2, pslash });
|
||||
} else if (pd1 and !pd2 and !pslash and syntax == .flag) {
|
||||
try stdout.print("flagpd1(\"{}\"),\n", .{name});
|
||||
} else if (pd1 and !pd2 and !pslash and syntax == .joined) {
|
||||
try stdout.print("joinpd1(\"{}\"),\n", .{name});
|
||||
} else if (pd1 and !pd2 and !pslash and syntax == .joined_or_separate) {
|
||||
try stdout.print("jspd1(\"{}\"),\n", .{name});
|
||||
} else if (pd1 and !pd2 and !pslash and syntax == .separate) {
|
||||
try stdout.print("sepd1(\"{}\"),\n", .{name});
|
||||
} else {
|
||||
try stdout.print(
|
||||
\\.{{
|
||||
\\ .name = "{}",
|
||||
\\ .syntax = {},
|
||||
\\ .zig_equivalent = .other,
|
||||
\\ .pd1 = {},
|
||||
\\ .pd2 = {},
|
||||
\\ .psl = {},
|
||||
\\}},
|
||||
\\
|
||||
, .{ name, syntax, pd1, pd2, pslash });
|
||||
}
|
||||
}
|
||||
|
||||
try stdout.writeAll(
|
||||
\\};};
|
||||
\\
|
||||
);
|
||||
|
||||
try stdout_bos.flush();
|
||||
}
|
||||
|
||||
// TODO we should be able to import clang_options.zig but currently this is problematic because it will
|
||||
// import stage2.zig and that causes a bunch of stuff to get exported
|
||||
const Syntax = union(enum) {
|
||||
/// A flag with no values.
|
||||
flag,
|
||||
|
||||
/// An option which prefixes its (single) value.
|
||||
joined,
|
||||
|
||||
/// An option which is followed by its value.
|
||||
separate,
|
||||
|
||||
/// An option which is either joined to its (non-empty) value, or followed by its value.
|
||||
joined_or_separate,
|
||||
|
||||
/// An option which is both joined to its (first) value, and followed by its (second) value.
|
||||
joined_and_separate,
|
||||
|
||||
/// An option followed by its values, which are separated by commas.
|
||||
comma_joined,
|
||||
|
||||
/// An option which consumes an optional joined argument and any other remaining arguments.
|
||||
remaining_args_joined,
|
||||
|
||||
/// An option which is which takes multiple (separate) arguments.
|
||||
multi_arg: u8,
|
||||
|
||||
pub fn format(
|
||||
self: Syntax,
|
||||
comptime fmt: []const u8,
|
||||
options: std.fmt.FormatOptions,
|
||||
out_stream: var,
|
||||
) !void {
|
||||
switch (self) {
|
||||
.multi_arg => |n| return out_stream.print(".{{.{}={}}}", .{ @tagName(self), n }),
|
||||
else => return out_stream.print(".{}", .{@tagName(self)}),
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
fn objSyntax(obj: *json.ObjectMap) Syntax {
|
||||
const num_args = @intCast(u8, obj.get("NumArgs").?.value.Integer);
|
||||
for (obj.get("!superclasses").?.value.Array.span()) |superclass_json| {
|
||||
const superclass = superclass_json.String;
|
||||
if (std.mem.eql(u8, superclass, "Joined")) {
|
||||
return .joined;
|
||||
} else if (std.mem.eql(u8, superclass, "CLJoined")) {
|
||||
return .joined;
|
||||
} else if (std.mem.eql(u8, superclass, "CLIgnoredJoined")) {
|
||||
return .joined;
|
||||
} else if (std.mem.eql(u8, superclass, "CLCompileJoined")) {
|
||||
return .joined;
|
||||
} else if (std.mem.eql(u8, superclass, "JoinedOrSeparate")) {
|
||||
return .joined_or_separate;
|
||||
} else if (std.mem.eql(u8, superclass, "CLJoinedOrSeparate")) {
|
||||
return .joined_or_separate;
|
||||
} else if (std.mem.eql(u8, superclass, "CLCompileJoinedOrSeparate")) {
|
||||
return .joined_or_separate;
|
||||
} else if (std.mem.eql(u8, superclass, "Flag")) {
|
||||
return .flag;
|
||||
} else if (std.mem.eql(u8, superclass, "CLFlag")) {
|
||||
return .flag;
|
||||
} else if (std.mem.eql(u8, superclass, "CLIgnoredFlag")) {
|
||||
return .flag;
|
||||
} else if (std.mem.eql(u8, superclass, "Separate")) {
|
||||
return .separate;
|
||||
} else if (std.mem.eql(u8, superclass, "JoinedAndSeparate")) {
|
||||
return .joined_and_separate;
|
||||
} else if (std.mem.eql(u8, superclass, "CommaJoined")) {
|
||||
return .comma_joined;
|
||||
} else if (std.mem.eql(u8, superclass, "CLRemainingArgsJoined")) {
|
||||
return .remaining_args_joined;
|
||||
} else if (std.mem.eql(u8, superclass, "MultiArg")) {
|
||||
return .{ .multi_arg = num_args };
|
||||
}
|
||||
}
|
||||
const name = obj.get("Name").?.value.String;
|
||||
if (std.mem.eql(u8, name, "<input>")) {
|
||||
return .flag;
|
||||
} else if (std.mem.eql(u8, name, "<unknown>")) {
|
||||
return .flag;
|
||||
}
|
||||
const kind_def = obj.get("Kind").?.value.Object.get("def").?.value.String;
|
||||
if (std.mem.eql(u8, kind_def, "KIND_FLAG")) {
|
||||
return .flag;
|
||||
}
|
||||
const key = obj.get("!name").?.value.String;
|
||||
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);
|
||||
}
|
||||
|
||||
fn syntaxMatchesWithEql(syntax: Syntax) bool {
|
||||
return switch (syntax) {
|
||||
.flag,
|
||||
.separate,
|
||||
.multi_arg,
|
||||
=> true,
|
||||
|
||||
.joined,
|
||||
.joined_or_separate,
|
||||
.joined_and_separate,
|
||||
.comma_joined,
|
||||
.remaining_args_joined,
|
||||
=> false,
|
||||
};
|
||||
}
|
||||
|
||||
fn objectLessThan(a: *json.ObjectMap, b: *json.ObjectMap) bool {
|
||||
// Priority is determined by exact matches first, followed by prefix matches in descending
|
||||
// length, with key as a final tiebreaker.
|
||||
const a_syntax = objSyntax(a);
|
||||
const b_syntax = objSyntax(b);
|
||||
|
||||
const a_match_with_eql = syntaxMatchesWithEql(a_syntax);
|
||||
const b_match_with_eql = syntaxMatchesWithEql(b_syntax);
|
||||
|
||||
if (a_match_with_eql and !b_match_with_eql) {
|
||||
return true;
|
||||
} else if (!a_match_with_eql and b_match_with_eql) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!a_match_with_eql and !b_match_with_eql) {
|
||||
const a_name = a.get("Name").?.value.String;
|
||||
const b_name = b.get("Name").?.value.String;
|
||||
if (a_name.len != b_name.len) {
|
||||
return a_name.len > b_name.len;
|
||||
}
|
||||
}
|
||||
|
||||
const a_key = a.get("!name").?.value.String;
|
||||
const b_key = b.get("!name").?.value.String;
|
||||
return std.mem.lessThan(u8, a_key, b_key);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user