authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-08-11 19:44:29-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-09-24 20:01:19-07:00
log9e979e5a9dbee39d45ac7dc4fbedb63ddcae8e99
tree39e76a847d026849299c4756a3b124a4e2801199
parentea169e6ccfa0792629bebaa64e9d41fb5dd2a594

Compilation: re-implement cImport


3 files changed, 105 insertions(+), 18 deletions(-)

src/Compilation.zig+83-6
......@@ -5655,12 +5655,89 @@ pub const CImportResult = struct {
56555655};
56565656
56575657/// Caller owns returned memory.
5658pub fn cImport(comp: *Compilation, c_src: []const u8, owner_mod: *Package.Module) !CImportResult {
5658pub fn cImport(
5659 comp: *Compilation,
5660 c_src: []const u8,
5661 owner_mod: *Package.Module,
5662 prog_node: std.Progress.Node,
5663) !CImportResult {
56595664 dev.check(.translate_c_command);
5660 _ = comp;
5661 _ = c_src;
5662 _ = owner_mod;
5663 @panic("TODO execute 'zig translate-c' as a sub process and use the results");
5665
5666 const cimport_basename = "cimport.h";
5667 const translated_basename = "cimport.zig";
5668
5669 var man = comp.obtainCObjectCacheManifest(owner_mod);
5670 defer man.deinit();
5671
5672 man.hash.add(@as(u16, 0x7dd9)); // Random number to distinguish translate-c from compiling C objects
5673 man.hash.addBytes(c_src);
5674
5675 const digest, const is_hit = if (try man.hit()) .{ man.finalBin(), true } else digest: {
5676 var arena_allocator = std.heap.ArenaAllocator.init(comp.gpa);
5677 defer arena_allocator.deinit();
5678 const arena = arena_allocator.allocator();
5679
5680 const tmp_basename = std.fmt.hex(std.crypto.random.int(u64));
5681 const tmp_sub_path = "tmp" ++ fs.path.sep_str ++ tmp_basename;
5682 const cache_dir = comp.dirs.local_cache.handle;
5683 const out_h_sub_path = tmp_sub_path ++ fs.path.sep_str ++ cimport_basename;
5684
5685 try cache_dir.makePath(tmp_sub_path);
5686
5687 const out_h_path = try comp.dirs.local_cache.join(arena, &.{out_h_sub_path});
5688 const translated_path = try comp.dirs.local_cache.join(arena, &.{ tmp_sub_path, translated_basename });
5689 const out_dep_path = try std.fmt.allocPrint(arena, "{s}.d", .{out_h_path});
5690
5691 if (comp.verbose_cimport) log.info("writing C import source to {s}", .{out_h_path});
5692 try cache_dir.writeFile(.{ .sub_path = out_h_sub_path, .data = c_src });
5693
5694 var argv = std.array_list.Managed([]const u8).init(comp.gpa);
5695 defer argv.deinit();
5696 try comp.addTranslateCCArgs(arena, &argv, .c, out_dep_path, owner_mod);
5697 try argv.appendSlice(&.{ out_h_path, "-o", translated_path });
5698
5699 if (comp.verbose_cc) dump_argv(argv.items);
5700 var stdout: []u8 = undefined;
5701 try @import("main.zig").translateC(comp.gpa, arena, argv.items, prog_node, &stdout);
5702 if (comp.verbose_cimport and stdout.len != 0) log.info("unexpected stdout: {s}", .{stdout});
5703
5704 const dep_sub_path = out_h_sub_path ++ ".d";
5705 if (comp.verbose_cimport) log.info("processing dep file at {s}", .{dep_sub_path});
5706 try man.addDepFilePost(cache_dir, dep_sub_path);
5707 switch (comp.cache_use) {
5708 .whole => |whole| if (whole.cache_manifest) |whole_cache_manifest| {
5709 whole.cache_manifest_mutex.lock();
5710 defer whole.cache_manifest_mutex.unlock();
5711 try whole_cache_manifest.addDepFilePost(cache_dir, dep_sub_path);
5712 },
5713 .incremental, .none => {},
5714 }
5715
5716 const bin_digest = man.finalBin();
5717 const hex_digest = Cache.binToHex(bin_digest);
5718 const o_sub_path = "o" ++ fs.path.sep_str ++ hex_digest;
5719
5720 if (comp.verbose_cimport) log.info("renaming {s} to {s}", .{ tmp_sub_path, o_sub_path });
5721 try fs.rename(cache_dir, tmp_sub_path, cache_dir, o_sub_path);
5722
5723 break :digest .{ bin_digest, false };
5724 };
5725
5726 if (man.have_exclusive_lock) {
5727 // Write the updated manifest. This is a no-op if the manifest is not dirty. Note that it is
5728 // possible we had a hit and the manifest is dirty, for example if the file mtime changed but
5729 // the contents were the same, we hit the cache but the manifest is dirty and we need to update
5730 // it to prevent doing a full file content comparison the next time around.
5731 man.writeManifest() catch |err| {
5732 log.warn("failed to write cache manifest for C import: {s}", .{@errorName(err)});
5733 };
5734 }
5735
5736 return .{
5737 .digest = digest,
5738 .cache_hit = is_hit,
5739 .errors = std.zig.ErrorBundle.empty,
5740 };
56645741}
56655742
56665743fn workerUpdateCObject(
......@@ -6964,7 +7041,7 @@ fn addCommonCCArgs(
69647041pub fn addCCArgs(
69657042 comp: *const Compilation,
69667043 arena: Allocator,
6967 argv: *std.ArrayList([]const u8),
7044 argv: *std.array_list.Managed([]const u8),
69687045 ext: FileExt,
69697046 out_dep_path: ?[]const u8,
69707047 mod: *Package.Module,
src/Sema.zig+5-6
......@@ -5713,10 +5713,6 @@ fn zirCImport(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileEr
57135713 const extra = sema.code.extraData(Zir.Inst.Block, pl_node.payload_index);
57145714 const body = sema.code.bodySlice(extra.end, extra.data.body_len);
57155715
5716 // we check this here to avoid undefined symbols
5717 if (!build_options.have_llvm)
5718 return sema.fail(parent_block, src, "C import unavailable; Zig compiler built without LLVM extensions", .{});
5719
57205716 var c_import_buf = std.array_list.Managed(u8).init(gpa);
57215717 defer c_import_buf.deinit();
57225718
......@@ -5741,8 +5737,11 @@ fn zirCImport(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileEr
57415737
57425738 _ = try sema.analyzeInlineBody(&child_block, body, inst);
57435739
5744 var c_import_res = comp.cImport(c_import_buf.items, parent_block.ownerModule()) catch |err|
5745 return sema.fail(&child_block, src, "C import failed: {s}", .{@errorName(err)});
5740 const prog_node = zcu.cur_sema_prog_node.start("@cImport", 0);
5741 defer prog_node.end();
5742
5743 var c_import_res = comp.cImport(c_import_buf.items, parent_block.ownerModule(), prog_node) catch |err|
5744 return sema.fail(&child_block, src, "C import failed: {t}", .{err});
57465745 defer c_import_res.deinit(gpa);
57475746
57485747 if (c_import_res.errors.errorMessageCount() != 0) {
src/main.zig+17-6
......@@ -4559,12 +4559,7 @@ fn cmdTranslateC(
45594559 try argv.append(c_source_file.src_path);
45604560 if (comp.verbose_cc) Compilation.dump_argv(argv.items);
45614561
4562 try jitCmd(comp.gpa, arena, argv.items, .{
4563 .cmd_name = "translate-c",
4564 .root_src_path = "translate-c/main.zig",
4565 .depend_on_aro = true,
4566 .progress_node = prog_node,
4567 });
4562 try translateC(comp.gpa, arena, argv.items, prog_node, null);
45684563
45694564 if (out_dep_path) |dep_file_path| {
45704565 const dep_basename = fs.path.basename(dep_file_path);
......@@ -4585,6 +4580,22 @@ fn cmdTranslateC(
45854580 return cleanExit();
45864581}
45874582
4583pub fn translateC(
4584 gpa: Allocator,
4585 arena: Allocator,
4586 argv: []const []const u8,
4587 prog_node: std.Progress.Node,
4588 capture: ?*[]u8,
4589) !void {
4590 try jitCmd(gpa, arena, argv, .{
4591 .cmd_name = "translate-c",
4592 .root_src_path = "translate-c/main.zig",
4593 .depend_on_aro = true,
4594 .progress_node = prog_node,
4595 .capture = capture,
4596 });
4597}
4598
45884599const usage_init =
45894600 \\Usage: zig init
45904601 \\