From dc79651e6a46dc050976feabbb1b5e25a68dcf3f Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 18 Sep 2020 01:33:32 -0700 Subject: [PATCH] stage2: add CLI for `zig translate-c` --- BRANCH_TODO | 1 - CMakeLists.txt | 26 ++++------- src-self-hosted/Compilation.zig | 20 +++++---- src-self-hosted/main.zig | 77 ++++++++++++++++++++++++++++++--- src-self-hosted/translate_c.zig | 4 +- 5 files changed, 93 insertions(+), 35 deletions(-) diff --git a/BRANCH_TODO b/BRANCH_TODO index 369cd2643..9455511db 100644 --- a/BRANCH_TODO +++ b/BRANCH_TODO @@ -1,4 +1,3 @@ - * `zig translate-c` * `zig test` * `zig build` * `-ftime-report` diff --git a/CMakeLists.txt b/CMakeLists.txt index c8746338d..d55c76ee9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -392,15 +392,18 @@ if(ZIG_TEST_COVERAGE) set(EXE_LDFLAGS "${EXE_LDFLAGS} -fprofile-arcs -ftest-coverage") endif() -add_library(zig_cpp STATIC ${ZIG_CPP_SOURCES}) +add_library(zig_cpp STATIC ${ZIG_SOURCES} ${ZIG_CPP_SOURCES}) set_target_properties(zig_cpp PROPERTIES COMPILE_FLAGS ${EXE_CFLAGS} ) target_link_libraries(zig_cpp LINK_PUBLIC + opt_c_util + ${SOFTFLOAT_LIBRARIES} ${CLANG_LIBRARIES} ${LLD_LIBRARIES} ${LLVM_LIBRARIES} + ${CMAKE_THREAD_LIBS_INIT} ) if(ZIG_WORKAROUND_POLLY_SO) target_link_libraries(zig_cpp LINK_PUBLIC "-Wl,${ZIG_WORKAROUND_POLLY_SO}") @@ -411,27 +414,16 @@ set_target_properties(opt_c_util PROPERTIES COMPILE_FLAGS "${OPTIMIZED_C_FLAGS}" ) -add_library(zigcompiler STATIC ${ZIG_SOURCES}) -set_target_properties(zigcompiler PROPERTIES - COMPILE_FLAGS ${EXE_CFLAGS} - LINK_FLAGS ${EXE_LDFLAGS} -) -target_link_libraries(zigcompiler LINK_PUBLIC - zig_cpp - opt_c_util - ${SOFTFLOAT_LIBRARIES} - ${CMAKE_THREAD_LIBS_INIT} -) if(NOT MSVC) - target_link_libraries(zigcompiler LINK_PUBLIC ${LIBXML2}) + target_link_libraries(zig_cpp LINK_PUBLIC ${LIBXML2}) endif() if(ZIG_DIA_GUIDS_LIB) - target_link_libraries(zigcompiler LINK_PUBLIC ${ZIG_DIA_GUIDS_LIB}) + target_link_libraries(zig_cpp LINK_PUBLIC ${ZIG_DIA_GUIDS_LIB}) endif() if(MSVC OR MINGW) - target_link_libraries(zigcompiler LINK_PUBLIC version) + target_link_libraries(zig_cpp LINK_PUBLIC version) endif() add_executable(zig0 ${ZIG0_SOURCES}) @@ -439,7 +431,7 @@ set_target_properties(zig0 PROPERTIES COMPILE_FLAGS ${EXE_CFLAGS} LINK_FLAGS ${EXE_LDFLAGS} ) -target_link_libraries(zig0 zigcompiler) +target_link_libraries(zig0 zig_cpp) if(MSVC) set(ZIG1_OBJECT "${CMAKE_BINARY_DIR}/zig1.obj") @@ -493,7 +485,7 @@ set_target_properties(zig PROPERTIES COMPILE_FLAGS ${EXE_CFLAGS} LINK_FLAGS ${EXE_LDFLAGS} ) -target_link_libraries(zig zigcompiler "${ZIG1_OBJECT}") +target_link_libraries(zig "${ZIG1_OBJECT}" zig_cpp) if(MSVC) target_link_libraries(zig ntdll.lib) elseif(MINGW) diff --git a/src-self-hosted/Compilation.zig b/src-self-hosted/Compilation.zig index 514ad1ed0..ed4650848 100644 --- a/src-self-hosted/Compilation.zig +++ b/src-self-hosted/Compilation.zig @@ -1005,7 +1005,7 @@ fn updateCObject(comp: *Compilation, c_object: *CObject) !void { defer tracy.end(); if (!build_options.have_llvm) { - return comp.failCObj(c_object, "clang not available: compiler not built with LLVM extensions enabled", .{}); + return comp.failCObj(c_object, "clang not available: compiler built without LLVM extensions", .{}); } const self_exe_path = comp.self_exe_path orelse return comp.failCObj(c_object, "clang compilation disabled", .{}); @@ -1081,10 +1081,7 @@ fn updateCObject(comp: *Compilation, c_object: *CObject) !void { try argv.appendSlice(c_object.src.extra_flags); if (comp.verbose_cc) { - for (argv.items[0 .. argv.items.len - 1]) |arg| { - std.debug.print("{} ", .{arg}); - } - std.debug.print("{}\n", .{argv.items[argv.items.len - 1]}); + dump_argv(argv.items); } const child = try std.ChildProcess.init(argv.items, arena); @@ -1190,7 +1187,7 @@ fn tmpFilePath(comp: *Compilation, arena: *Allocator, suffix: []const u8) error{ } /// Add common C compiler args between translate-c and C object compilation. -fn addCCArgs( +pub fn addCCArgs( comp: *Compilation, arena: *Allocator, argv: *std.ArrayList([]const u8), @@ -1671,12 +1668,19 @@ fn wantBuildLibUnwindFromSource(comp: *Compilation) bool { } fn updateBuiltinZigFile(comp: *Compilation, mod: *Module) !void { - const source = try comp.generateBuiltinZigSource(); + const source = try comp.generateBuiltinZigSource(comp.gpa); defer comp.gpa.free(source); try mod.zig_cache_artifact_directory.handle.writeFile("builtin.zig", source); } -pub fn generateBuiltinZigSource(comp: *Compilation) ![]u8 { +pub fn dump_argv(argv: []const []const u8) void { + for (argv[0 .. argv.len - 1]) |arg| { + std.debug.print("{} ", .{arg}); + } + std.debug.print("{}\n", .{argv[argv.len - 1]}); +} + +pub fn generateBuiltinZigSource(comp: *Compilation, allocator: *Allocator) ![]u8 { var buffer = std.ArrayList(u8).init(comp.gpa); defer buffer.deinit(); diff --git a/src-self-hosted/main.zig b/src-self-hosted/main.zig index 77ca2b398..f18ccdd80 100644 --- a/src-self-hosted/main.zig +++ b/src-self-hosted/main.zig @@ -15,6 +15,7 @@ const build_options = @import("build_options"); const warn = std.log.warn; const introspect = @import("introspect.zig"); const LibCInstallation = @import("libc_installation.zig").LibCInstallation; +const translate_c = @import("translate_c.zig"); pub fn fatal(comptime format: []const u8, args: anytype) noreturn { std.log.emerg(format, args); @@ -864,6 +865,10 @@ pub fn buildOutputType( } } + if (arg_mode == .translate_c and c_source_files.items.len != 1) { + fatal("translate-c expects exactly 1 source file (found {})", .{c_source_files.items.len}); + } + const root_name = if (provided_name) |n| n else blk: { if (root_src_file) |file| { const basename = fs.path.basename(file); @@ -1175,10 +1180,10 @@ pub fn buildOutputType( defer comp.destroy(); if (show_builtin) { - const source = try comp.generateBuiltinZigSource(); - defer comp.gpa.free(source); - try std.io.getStdOut().writeAll(source); - return; + return std.io.getStdOut().writeAll(try comp.generateBuiltinZigSource(arena)); + } + if (arg_mode == .translate_c) { + return cmdTranslateC(comp, arena); } try updateModule(gpa, comp, zir_out_path); @@ -1248,6 +1253,63 @@ fn updateModule(gpa: *Allocator, comp: *Compilation, zir_out_path: ?[]const u8) } } +fn cmdTranslateC(comp: *Compilation, arena: *Allocator) !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.addCCArgs(arena, &argv, file_ext, true, null); + 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(); + + var bos = io.bufferedOutStream(io.getStdOut().writer()); + _ = try std.zig.render(comp.gpa, bos.writer(), tree); + try bos.flush(); +} + pub const usage_libc = \\Usage: zig libc \\ @@ -1401,8 +1463,9 @@ pub fn cmdFmt(gpa: *Allocator, args: []const []const u8) !void { process.exit(code); } - const stdout = io.getStdOut().outStream(); - _ = try std.zig.render(gpa, stdout, tree); + var bos = io.bufferedOutStream(io.getStdOut().writer()); + _ = try std.zig.render(gpa, bos.writer(), tree); + try bos.flush(); return; } @@ -1644,7 +1707,7 @@ extern "c" fn ZigClang_main(argc: c_int, argv: [*:null]?[*:0]u8) c_int; /// TODO https://github.com/ziglang/zig/issues/3257 fn punt_to_clang(arena: *Allocator, args: []const []const u8) error{OutOfMemory} { if (!build_options.have_llvm) - fatal("`zig cc` and `zig c++` unavailable: compiler not built with LLVM extensions enabled", .{}); + fatal("`zig cc` and `zig c++` unavailable: compiler built without LLVM extensions", .{}); // Convert the args to the format Clang expects. const argv = try arena.alloc(?[*:0]u8, args.len + 1); for (args) |arg, i| { diff --git a/src-self-hosted/translate_c.zig b/src-self-hosted/translate_c.zig index 1cdb5099e..c5c5231ca 100644 --- a/src-self-hosted/translate_c.zig +++ b/src-self-hosted/translate_c.zig @@ -1,5 +1,5 @@ -// This is the userland implementation of translate-c which is used by both stage1 -// and stage2. +//! This is the userland implementation of translate-c which is used by both stage1 +//! and stage2. const std = @import("std"); const assert = std.debug.assert;