Commit dc79651e6a

Andrew Kelley <andrew@ziglang.org>
2020-09-18 10:33:32
stage2: add CLI for `zig translate-c`
1 parent a9b1802
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();
 
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| {
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;
BRANCH_TODO
@@ -1,4 +1,3 @@
- * `zig translate-c`
  * `zig test`
  * `zig build`
  * `-ftime-report`
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)