stage2: zig translate-c supports --enable-cache

This matches master branch behavior and makes the test-translate-c tests
pass.
This commit is contained in:
Andrew Kelley 2020-09-26 16:48:02 -07:00
parent 26f2f9bf1c
commit fe4c348f57
3 changed files with 124 additions and 55 deletions

View File

@ -1,5 +1,4 @@
* restore the legacy -femit-h feature using the stage1 backend
* figure out why test-translate-c is failing
* tests passing with -Dskip-non-native
* `-ftime-report`
* -fstack-report print stack size diagnostics\n"

View File

@ -1251,7 +1251,7 @@ pub fn performAllTheWork(self: *Compilation) error{OutOfMemory}!void {
};
}
fn obtainCObjectCacheManifest(comp: *Compilation) Cache.Manifest {
pub fn obtainCObjectCacheManifest(comp: *Compilation) Cache.Manifest {
var man = comp.cache_parent.obtain();
// Only things that need to be added on top of the base hash, and only things
@ -1289,6 +1289,7 @@ pub fn cImport(comp: *Compilation, c_src: []const u8) !CImportResult {
var man = comp.obtainCObjectCacheManifest();
defer man.deinit();
man.hash.add(@as(u16, 0xb945)); // Random number to distinguish translate-c from compiling C objects
man.hash.addBytes(c_src);
// If the previous invocation resulted in clang errors, we will see a hit
@ -1581,7 +1582,7 @@ fn updateCObject(comp: *Compilation, c_object: *CObject) !void {
};
}
fn tmpFilePath(comp: *Compilation, arena: *Allocator, suffix: []const u8) error{OutOfMemory}![]const u8 {
pub fn tmpFilePath(comp: *Compilation, arena: *Allocator, suffix: []const u8) error{OutOfMemory}![]const u8 {
const s = std.fs.path.sep_str;
const rand_int = comp.rand.int(u64);
if (comp.local_cache_directory.path) |p| {
@ -1598,6 +1599,7 @@ pub fn addTranslateCCArgs(
ext: FileExt,
out_dep_path: ?[]const u8,
) !void {
try argv.appendSlice(&[_][]const u8{ "-x", "c" });
try comp.addCCArgs(arena, argv, ext, out_dep_path);
// This gives us access to preprocessing entities, presumably at the cost of performance.
try argv.appendSlice(&[_][]const u8{ "-Xclang", "-detailed-preprocessing-record" });

View File

@ -16,6 +16,7 @@ const warn = std.log.warn;
const introspect = @import("introspect.zig");
const LibCInstallation = @import("libc_installation.zig").LibCInstallation;
const translate_c = @import("translate_c.zig");
const Cache = @import("Cache.zig");
pub fn fatal(comptime format: []const u8, args: anytype) noreturn {
std.log.emerg(format, args);
@ -481,12 +482,19 @@ fn buildOutputType(
switch (arg_mode) {
.build, .translate_c, .zig_test, .run => {
var optimize_mode_string: ?[]const u8 = null;
output_mode = switch (arg_mode) {
.build => |m| m,
.translate_c => .Obj,
.zig_test, .run => .Exe,
switch (arg_mode) {
.build => |m| {
output_mode = m;
},
.translate_c => {
emit_bin = .no;
output_mode = .Obj;
},
.zig_test, .run => {
output_mode = .Exe;
},
else => unreachable,
};
}
// TODO finish self-hosted and add support for emitting C header files
emit_h = .no;
//switch (arg_mode) {
@ -1537,7 +1545,7 @@ fn buildOutputType(
return std.io.getStdOut().writeAll(try comp.generateBuiltinZigSource(arena));
}
if (arg_mode == .translate_c) {
return cmdTranslateC(comp, arena);
return cmdTranslateC(comp, arena, have_enable_cache);
}
const hook: AfterUpdateHook = blk: {
@ -1556,7 +1564,7 @@ fn buildOutputType(
try updateModule(gpa, comp, zir_out_path, hook);
if (build_options.is_stage1 and comp.stage1_lock != null and watch) {
std.log.warn("--watch is not recommended with the stage1 backend; it leaks memory and is not capable of incremental compilation", .{});
warn("--watch is not recommended with the stage1 backend; it leaks memory and is not capable of incremental compilation", .{});
}
switch (arg_mode) {
@ -1701,61 +1709,121 @@ fn updateModule(gpa: *Allocator, comp: *Compilation, zir_out_path: ?[]const u8,
}
}
fn cmdTranslateC(comp: *Compilation, arena: *Allocator) !void {
fn cmdTranslateC(comp: *Compilation, arena: *Allocator, enable_cache: bool) !void {
if (!build_options.have_llvm)
fatal("cannot translate-c: compiler built without LLVM extensions", .{});
assert(comp.c_source_files.len == 1);
var argv = std.ArrayList([]const u8).init(arena);
const c_source_file = comp.c_source_files[0];
const file_ext = Compilation.classifyFileExt(c_source_file.src_path);
try comp.addTranslateCCArgs(arena, &argv, file_ext, null);
try argv.append(c_source_file.src_path);
if (comp.verbose_cc) {
std.debug.print("clang ", .{});
Compilation.dump_argv(argv.items);
}
const translated_zig_basename = try std.fmt.allocPrint(arena, "{}.zig", .{comp.bin_file.options.root_name});
// Convert to null terminated args.
const new_argv_with_sentinel = try arena.alloc(?[*:0]const u8, argv.items.len + 1);
new_argv_with_sentinel[argv.items.len] = null;
const new_argv = new_argv_with_sentinel[0..argv.items.len :null];
for (argv.items) |arg, i| {
new_argv[i] = try arena.dupeZ(u8, arg);
}
var man: Cache.Manifest = comp.obtainCObjectCacheManifest();
defer if (enable_cache) man.deinit();
const c_headers_dir_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{"include"});
const c_headers_dir_path_z = try arena.dupeZ(u8, c_headers_dir_path);
var clang_errors: []translate_c.ClangErrMsg = &[0]translate_c.ClangErrMsg{};
const tree = translate_c.translate(
comp.gpa,
new_argv.ptr,
new_argv.ptr + new_argv.len,
&clang_errors,
c_headers_dir_path_z,
) catch |err| switch (err) {
error.OutOfMemory => return error.OutOfMemory,
error.ASTUnitFailure => fatal("clang API returned errors but due to a clang bug, it is not exposing the errors for zig to see. For more details: https://github.com/ziglang/zig/issues/4455", .{}),
error.SemanticAnalyzeFail => {
for (clang_errors) |clang_err| {
std.debug.print("{}:{}:{}: {}\n", .{
if (clang_err.filename_ptr) |p| p[0..clang_err.filename_len] else "(no file)",
clang_err.line + 1,
clang_err.column + 1,
clang_err.msg_ptr[0..clang_err.msg_len],
});
}
process.exit(1);
},
man.hash.add(@as(u16, 0xb945)); // Random number to distinguish translate-c from compiling C objects
_ = man.addFile(c_source_file.src_path, null) catch |err| {
fatal("unable to process '{}': {}", .{ c_source_file.src_path, @errorName(err) });
};
defer tree.deinit();
var bos = io.bufferedOutStream(io.getStdOut().writer());
_ = try std.zig.render(comp.gpa, bos.writer(), tree);
try bos.flush();
const digest = if (try man.hit()) man.final() else digest: {
var argv = std.ArrayList([]const u8).init(arena);
var zig_cache_tmp_dir = try comp.local_cache_directory.handle.makeOpenPath("tmp", .{});
defer zig_cache_tmp_dir.close();
const ext = Compilation.classifyFileExt(c_source_file.src_path);
const out_dep_path: ?[]const u8 = blk: {
if (comp.disable_c_depfile or !ext.clangSupportsDepFile())
break :blk null;
const c_src_basename = fs.path.basename(c_source_file.src_path);
const dep_basename = try std.fmt.allocPrint(arena, "{}.d", .{c_src_basename});
const out_dep_path = try comp.tmpFilePath(arena, dep_basename);
break :blk out_dep_path;
};
try comp.addTranslateCCArgs(arena, &argv, ext, out_dep_path);
try argv.append(c_source_file.src_path);
if (comp.verbose_cc) {
std.debug.print("clang ", .{});
Compilation.dump_argv(argv.items);
}
// Convert to null terminated args.
const new_argv_with_sentinel = try arena.alloc(?[*:0]const u8, argv.items.len + 1);
new_argv_with_sentinel[argv.items.len] = null;
const new_argv = new_argv_with_sentinel[0..argv.items.len :null];
for (argv.items) |arg, i| {
new_argv[i] = try arena.dupeZ(u8, arg);
}
const c_headers_dir_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{"include"});
const c_headers_dir_path_z = try arena.dupeZ(u8, c_headers_dir_path);
var clang_errors: []translate_c.ClangErrMsg = &[0]translate_c.ClangErrMsg{};
const tree = translate_c.translate(
comp.gpa,
new_argv.ptr,
new_argv.ptr + new_argv.len,
&clang_errors,
c_headers_dir_path_z,
) catch |err| switch (err) {
error.OutOfMemory => return error.OutOfMemory,
error.ASTUnitFailure => fatal("clang API returned errors but due to a clang bug, it is not exposing the errors for zig to see. For more details: https://github.com/ziglang/zig/issues/4455", .{}),
error.SemanticAnalyzeFail => {
for (clang_errors) |clang_err| {
std.debug.print("{}:{}:{}: {}\n", .{
if (clang_err.filename_ptr) |p| p[0..clang_err.filename_len] else "(no file)",
clang_err.line + 1,
clang_err.column + 1,
clang_err.msg_ptr[0..clang_err.msg_len],
});
}
process.exit(1);
},
};
defer tree.deinit();
if (out_dep_path) |dep_file_path| {
const dep_basename = std.fs.path.basename(dep_file_path);
// Add the files depended on to the cache system.
try man.addDepFilePost(zig_cache_tmp_dir, dep_basename);
// Just to save disk space, we delete the file because it is never needed again.
zig_cache_tmp_dir.deleteFile(dep_basename) catch |err| {
warn("failed to delete '{}': {}", .{ dep_file_path, @errorName(err) });
};
}
const digest = man.final();
const o_sub_path = try fs.path.join(arena, &[_][]const u8{ "o", &digest });
var o_dir = try comp.local_cache_directory.handle.makeOpenPath(o_sub_path, .{});
defer o_dir.close();
var zig_file = try o_dir.createFile(translated_zig_basename, .{});
defer zig_file.close();
var bos = io.bufferedOutStream(zig_file.writer());
_ = try std.zig.render(comp.gpa, bos.writer(), tree);
try bos.flush();
man.writeManifest() catch |err| warn("failed to write cache manifest: {}", .{@errorName(err)});
break :digest digest;
};
if (enable_cache) {
const full_zig_path = try comp.local_cache_directory.join(arena, &[_][]const u8{
"o", &digest, translated_zig_basename,
});
try io.getStdOut().writer().print("{}\n", .{full_zig_path});
return cleanExit();
} else {
const out_zig_path = try fs.path.join(arena, &[_][]const u8{ "o", &digest, translated_zig_basename });
const zig_file = try comp.local_cache_directory.handle.openFile(out_zig_path, .{});
defer zig_file.close();
try io.getStdOut().writeFileAll(zig_file, .{});
return cleanExit();
}
}
pub const usage_libc =