| ... | @@ -16,6 +16,7 @@ const warn = std.log.warn; | ... | @@ -16,6 +16,7 @@ const warn = std.log.warn; |
| 16 | const introspect = @import("introspect.zig"); | 16 | const introspect = @import("introspect.zig"); |
| 17 | const LibCInstallation = @import("libc_installation.zig").LibCInstallation; | 17 | const LibCInstallation = @import("libc_installation.zig").LibCInstallation; |
| 18 | const translate_c = @import("translate_c.zig"); | 18 | const translate_c = @import("translate_c.zig"); |
| | 19 | const Cache = @import("Cache.zig"); |
| 19 | | 20 | |
| 20 | pub fn fatal(comptime format: []const u8, args: anytype) noreturn { | 21 | pub fn fatal(comptime format: []const u8, args: anytype) noreturn { |
| 21 | std.log.emerg(format, args); | 22 | std.log.emerg(format, args); |
| ... | @@ -481,12 +482,19 @@ fn buildOutputType( | ... | @@ -481,12 +482,19 @@ fn buildOutputType( |
| 481 | switch (arg_mode) { | 482 | switch (arg_mode) { |
| 482 | .build, .translate_c, .zig_test, .run => { | 483 | .build, .translate_c, .zig_test, .run => { |
| 483 | var optimize_mode_string: ?[]const u8 = null; | 484 | var optimize_mode_string: ?[]const u8 = null; |
| 484 | output_mode = switch (arg_mode) { | 485 | switch (arg_mode) { |
| 485 | .build => |m| m, | 486 | .build => |m| { |
| 486 | .translate_c => .Obj, | 487 | output_mode = m; |
| 487 | .zig_test, .run => .Exe, | 488 | }, |
| | 489 | .translate_c => { |
| | 490 | emit_bin = .no; |
| | 491 | output_mode = .Obj; |
| | 492 | }, |
| | 493 | .zig_test, .run => { |
| | 494 | output_mode = .Exe; |
| | 495 | }, |
| 488 | else => unreachable, | 496 | else => unreachable, |
| 489 | }; | 497 | } |
| 490 | // TODO finish self-hosted and add support for emitting C header files | 498 | // TODO finish self-hosted and add support for emitting C header files |
| 491 | emit_h = .no; | 499 | emit_h = .no; |
| 492 | //switch (arg_mode) { | 500 | //switch (arg_mode) { |
| ... | @@ -1537,7 +1545,7 @@ fn buildOutputType( | ... | @@ -1537,7 +1545,7 @@ fn buildOutputType( |
| 1537 | return std.io.getStdOut().writeAll(try comp.generateBuiltinZigSource(arena)); | 1545 | return std.io.getStdOut().writeAll(try comp.generateBuiltinZigSource(arena)); |
| 1538 | } | 1546 | } |
| 1539 | if (arg_mode == .translate_c) { | 1547 | if (arg_mode == .translate_c) { |
| 1540 | return cmdTranslateC(comp, arena); | 1548 | return cmdTranslateC(comp, arena, have_enable_cache); |
| 1541 | } | 1549 | } |
| 1542 | | 1550 | |
| 1543 | const hook: AfterUpdateHook = blk: { | 1551 | const hook: AfterUpdateHook = blk: { |
| ... | @@ -1556,7 +1564,7 @@ fn buildOutputType( | ... | @@ -1556,7 +1564,7 @@ fn buildOutputType( |
| 1556 | try updateModule(gpa, comp, zir_out_path, hook); | 1564 | try updateModule(gpa, comp, zir_out_path, hook); |
| 1557 | | 1565 | |
| 1558 | if (build_options.is_stage1 and comp.stage1_lock != null and watch) { | 1566 | if (build_options.is_stage1 and comp.stage1_lock != null and watch) { |
| 1559 | std.log.warn("--watch is not recommended with the stage1 backend; it leaks memory and is not capable of incremental compilation", .{}); | 1567 | warn("--watch is not recommended with the stage1 backend; it leaks memory and is not capable of incremental compilation", .{}); |
| 1560 | } | 1568 | } |
| 1561 | | 1569 | |
| 1562 | switch (arg_mode) { | 1570 | switch (arg_mode) { |
| ... | @@ -1701,61 +1709,121 @@ fn updateModule(gpa: *Allocator, comp: *Compilation, zir_out_path: ?[]const u8, | ... | @@ -1701,61 +1709,121 @@ fn updateModule(gpa: *Allocator, comp: *Compilation, zir_out_path: ?[]const u8, |
| 1701 | } | 1709 | } |
| 1702 | } | 1710 | } |
| 1703 | | 1711 | |
| 1704 | fn cmdTranslateC(comp: *Compilation, arena: *Allocator) !void { | 1712 | fn cmdTranslateC(comp: *Compilation, arena: *Allocator, enable_cache: bool) !void { |
| 1705 | if (!build_options.have_llvm) | 1713 | if (!build_options.have_llvm) |
| 1706 | fatal("cannot translate-c: compiler built without LLVM extensions", .{}); | 1714 | fatal("cannot translate-c: compiler built without LLVM extensions", .{}); |
| 1707 | | 1715 | |
| 1708 | assert(comp.c_source_files.len == 1); | 1716 | assert(comp.c_source_files.len == 1); |
| | 1717 | const c_source_file = comp.c_source_files[0]; |
| 1709 | | 1718 | |
| 1710 | var argv = std.ArrayList([]const u8).init(arena); | 1719 | const translated_zig_basename = try std.fmt.allocPrint(arena, "{}.zig", .{comp.bin_file.options.root_name}); |
| 1711 | | 1720 | |
| 1712 | const c_source_file = comp.c_source_files[0]; | 1721 | var man: Cache.Manifest = comp.obtainCObjectCacheManifest(); |
| 1713 | const file_ext = Compilation.classifyFileExt(c_source_file.src_path); | 1722 | defer if (enable_cache) man.deinit(); |
| 1714 | try comp.addTranslateCCArgs(arena, &argv, file_ext, null); | | |
| 1715 | try argv.append(c_source_file.src_path); | | |
| 1716 | | 1723 | |
| 1717 | if (comp.verbose_cc) { | 1724 | man.hash.add(@as(u16, 0xb945)); // Random number to distinguish translate-c from compiling C objects |
| 1718 | std.debug.print("clang ", .{}); | 1725 | _ = man.addFile(c_source_file.src_path, null) catch |err| { |
| 1719 | Compilation.dump_argv(argv.items); | 1726 | fatal("unable to process '{}': {}", .{ c_source_file.src_path, @errorName(err) }); |
| 1720 | } | 1727 | }; |
| 1721 | | 1728 | |
| 1722 | // Convert to null terminated args. | 1729 | const digest = if (try man.hit()) man.final() else digest: { |
| 1723 | const new_argv_with_sentinel = try arena.alloc(?[*:0]const u8, argv.items.len + 1); | 1730 | var argv = std.ArrayList([]const u8).init(arena); |
| 1724 | new_argv_with_sentinel[argv.items.len] = null; | | |
| 1725 | const new_argv = new_argv_with_sentinel[0..argv.items.len :null]; | | |
| 1726 | for (argv.items) |arg, i| { | | |
| 1727 | new_argv[i] = try arena.dupeZ(u8, arg); | | |
| 1728 | } | | |
| 1729 | | 1731 | |
| 1730 | const c_headers_dir_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{"include"}); | 1732 | var zig_cache_tmp_dir = try comp.local_cache_directory.handle.makeOpenPath("tmp", .{}); |
| 1731 | const c_headers_dir_path_z = try arena.dupeZ(u8, c_headers_dir_path); | 1733 | defer zig_cache_tmp_dir.close(); |
| 1732 | var clang_errors: []translate_c.ClangErrMsg = &[0]translate_c.ClangErrMsg{}; | 1734 | |
| 1733 | const tree = translate_c.translate( | 1735 | const ext = Compilation.classifyFileExt(c_source_file.src_path); |
| 1734 | comp.gpa, | 1736 | const out_dep_path: ?[]const u8 = blk: { |
| 1735 | new_argv.ptr, | 1737 | if (comp.disable_c_depfile or !ext.clangSupportsDepFile()) |
| 1736 | new_argv.ptr + new_argv.len, | 1738 | break :blk null; |
| 1737 | &clang_errors, | 1739 | |
| 1738 | c_headers_dir_path_z, | 1740 | const c_src_basename = fs.path.basename(c_source_file.src_path); |
| 1739 | ) catch |err| switch (err) { | 1741 | const dep_basename = try std.fmt.allocPrint(arena, "{}.d", .{c_src_basename}); |
| 1740 | error.OutOfMemory => return error.OutOfMemory, | 1742 | const out_dep_path = try comp.tmpFilePath(arena, dep_basename); |
| 1741 | 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", .{}), | 1743 | break :blk out_dep_path; |
| 1742 | error.SemanticAnalyzeFail => { | 1744 | }; |
| 1743 | for (clang_errors) |clang_err| { | 1745 | |
| 1744 | std.debug.print("{}:{}:{}: {}\n", .{ | 1746 | try comp.addTranslateCCArgs(arena, &argv, ext, out_dep_path); |
| 1745 | if (clang_err.filename_ptr) |p| p[0..clang_err.filename_len] else "(no file)", | 1747 | try argv.append(c_source_file.src_path); |
| 1746 | clang_err.line + 1, | 1748 | |
| 1747 | clang_err.column + 1, | 1749 | if (comp.verbose_cc) { |
| 1748 | clang_err.msg_ptr[0..clang_err.msg_len], | 1750 | std.debug.print("clang ", .{}); |
| 1749 | }); | 1751 | Compilation.dump_argv(argv.items); |
| 1750 | } | 1752 | } |
| 1751 | process.exit(1); | 1753 | |
| 1752 | }, | 1754 | // Convert to null terminated args. |
| | 1755 | const new_argv_with_sentinel = try arena.alloc(?[*:0]const u8, argv.items.len + 1); |
| | 1756 | new_argv_with_sentinel[argv.items.len] = null; |
| | 1757 | const new_argv = new_argv_with_sentinel[0..argv.items.len :null]; |
| | 1758 | for (argv.items) |arg, i| { |
| | 1759 | new_argv[i] = try arena.dupeZ(u8, arg); |
| | 1760 | } |
| | 1761 | |
| | 1762 | const c_headers_dir_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{"include"}); |
| | 1763 | const c_headers_dir_path_z = try arena.dupeZ(u8, c_headers_dir_path); |
| | 1764 | var clang_errors: []translate_c.ClangErrMsg = &[0]translate_c.ClangErrMsg{}; |
| | 1765 | const tree = translate_c.translate( |
| | 1766 | comp.gpa, |
| | 1767 | new_argv.ptr, |
| | 1768 | new_argv.ptr + new_argv.len, |
| | 1769 | &clang_errors, |
| | 1770 | c_headers_dir_path_z, |
| | 1771 | ) catch |err| switch (err) { |
| | 1772 | error.OutOfMemory => return error.OutOfMemory, |
| | 1773 | 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", .{}), |
| | 1774 | error.SemanticAnalyzeFail => { |
| | 1775 | for (clang_errors) |clang_err| { |
| | 1776 | std.debug.print("{}:{}:{}: {}\n", .{ |
| | 1777 | if (clang_err.filename_ptr) |p| p[0..clang_err.filename_len] else "(no file)", |
| | 1778 | clang_err.line + 1, |
| | 1779 | clang_err.column + 1, |
| | 1780 | clang_err.msg_ptr[0..clang_err.msg_len], |
| | 1781 | }); |
| | 1782 | } |
| | 1783 | process.exit(1); |
| | 1784 | }, |
| | 1785 | }; |
| | 1786 | defer tree.deinit(); |
| | 1787 | |
| | 1788 | if (out_dep_path) |dep_file_path| { |
| | 1789 | const dep_basename = std.fs.path.basename(dep_file_path); |
| | 1790 | // Add the files depended on to the cache system. |
| | 1791 | try man.addDepFilePost(zig_cache_tmp_dir, dep_basename); |
| | 1792 | // Just to save disk space, we delete the file because it is never needed again. |
| | 1793 | zig_cache_tmp_dir.deleteFile(dep_basename) catch |err| { |
| | 1794 | warn("failed to delete '{}': {}", .{ dep_file_path, @errorName(err) }); |
| | 1795 | }; |
| | 1796 | } |
| | 1797 | |
| | 1798 | const digest = man.final(); |
| | 1799 | const o_sub_path = try fs.path.join(arena, &[_][]const u8{ "o", &digest }); |
| | 1800 | var o_dir = try comp.local_cache_directory.handle.makeOpenPath(o_sub_path, .{}); |
| | 1801 | defer o_dir.close(); |
| | 1802 | var zig_file = try o_dir.createFile(translated_zig_basename, .{}); |
| | 1803 | defer zig_file.close(); |
| | 1804 | |
| | 1805 | var bos = io.bufferedOutStream(zig_file.writer()); |
| | 1806 | _ = try std.zig.render(comp.gpa, bos.writer(), tree); |
| | 1807 | try bos.flush(); |
| | 1808 | |
| | 1809 | man.writeManifest() catch |err| warn("failed to write cache manifest: {}", .{@errorName(err)}); |
| | 1810 | |
| | 1811 | break :digest digest; |
| 1753 | }; | 1812 | }; |
| 1754 | defer tree.deinit(); | | |
| 1755 | | 1813 | |
| 1756 | var bos = io.bufferedOutStream(io.getStdOut().writer()); | 1814 | if (enable_cache) { |
| 1757 | _ = try std.zig.render(comp.gpa, bos.writer(), tree); | 1815 | const full_zig_path = try comp.local_cache_directory.join(arena, &[_][]const u8{ |
| 1758 | try bos.flush(); | 1816 | "o", &digest, translated_zig_basename, |
| | 1817 | }); |
| | 1818 | try io.getStdOut().writer().print("{}\n", .{full_zig_path}); |
| | 1819 | return cleanExit(); |
| | 1820 | } else { |
| | 1821 | const out_zig_path = try fs.path.join(arena, &[_][]const u8{ "o", &digest, translated_zig_basename }); |
| | 1822 | const zig_file = try comp.local_cache_directory.handle.openFile(out_zig_path, .{}); |
| | 1823 | defer zig_file.close(); |
| | 1824 | try io.getStdOut().writeFileAll(zig_file, .{}); |
| | 1825 | return cleanExit(); |
| | 1826 | } |
| 1759 | } | 1827 | } |
| 1760 | | 1828 | |
| 1761 | pub const usage_libc = | 1829 | pub const usage_libc = |