1const TranslateC = @This();
2
3const std = @import("std");
4const Io = std.Io;
5const Configuration = std.Build.Configuration;
6const assert = std.debug.assert;
7const OptimizeMode = std.lang.Optimize;
8
9const Step = @import("../Step.zig");
10const Maker = @import("../../Maker.zig");
11const PkgConfig = @import("../PkgConfig.zig");
12
13pub fn make(
14 translate_c: *TranslateC,
15 step_index: Configuration.Step.Index,
16 maker: *Maker,
17 progress_node: std.Progress.Node,
18) Step.ExtendedMakeError!void {
19 _ = translate_c;
20 const graph = maker.graph;
21 const arena = graph.arena; // TODO don't leak into the process arena
22 const step = maker.stepByIndex(step_index);
23 const conf = &maker.scanned_config.configuration;
24 const conf_step = step_index.ptr(conf);
25 const conf_tc = conf_step.extended.get(conf.extra).translate_c;
26 const cache_root = graph.local_cache_root;
27
28 var argv: std.ArrayList([]const u8) = .empty;
29
30 try argv.ensureUnusedCapacity(arena, 10);
31 argv.appendAssumeCapacity(graph.zig_exe);
32 argv.appendAssumeCapacity("translate-c");
33 if (conf_tc.flags.link_libc) {
34 argv.appendAssumeCapacity("-lc");
35 }
36
37 argv.appendAssumeCapacity("--cache-dir");
38 argv.appendAssumeCapacity(cache_root.path orelse ".");
39
40 argv.appendAssumeCapacity("--global-cache-dir");
41 argv.appendAssumeCapacity(graph.global_cache_root.path orelse ".");
42
43 if (conf_tc.target.get(conf)) |resolved_target| {
44 if (resolved_target.unwrapQuery(conf)) |query| {
45 argv.appendAssumeCapacity("-target");
46 argv.appendAssumeCapacity(try query.zigTriple(arena));
47 }
48 }
49
50 const opt: ?OptimizeMode = switch (conf_tc.flags.optimize) {
51 .debug, .default => null, // Skip since it's the default
52 .safe => .safe,
53 .fast => .fast,
54 .small => .small,
55 };
56 if (opt) |o| argv.appendAssumeCapacity(try arena.print("-O{t}", .{o}));
57
58 try argv.ensureUnusedCapacity(arena, conf_tc.include_dirs.len * 2);
59 for (0..conf_tc.include_dirs.len) |i|
60 try Step.Compile.appendIncludeDirFlags(arena, conf_tc.include_dirs.get(conf.extra, i), &argv, step_index, maker);
61
62 for (conf_tc.c_macros.slice) |c_macro| {
63 (try argv.addManyAsArray(arena, 2)).* = .{ "-D", c_macro.slice(conf) };
64 }
65
66 var prev_search_strategy: std.Build.Module.SystemLib.SearchStrategy = .paths_first;
67 var prev_preferred_link_mode: std.builtin.LinkMode = .dynamic;
68 var seen_system_libs: std.array_hash_map.Auto(Configuration.String, []const []const u8) = .empty;
69
70 for (conf_tc.system_libs.slice) |system_lib_index| {
71 const system_lib = system_lib_index.get(conf);
72 const system_lib_name = system_lib.name.slice(conf);
73 const system_lib_gop = try seen_system_libs.getOrPut(arena, system_lib.name);
74 if (system_lib_gop.found_existing) {
75 try argv.appendSlice(arena, system_lib_gop.value_ptr.*);
76 continue;
77 } else {
78 system_lib_gop.value_ptr.* = &.{};
79 }
80
81 if ((system_lib.flags.search_strategy != prev_search_strategy or
82 system_lib.flags.preferred_link_mode != prev_preferred_link_mode))
83 {
84 try argv.ensureUnusedCapacity(arena, 1);
85 switch (system_lib.flags.search_strategy) {
86 .no_fallback => switch (system_lib.flags.preferred_link_mode) {
87 .dynamic => argv.appendAssumeCapacity("-search_dylibs_only"),
88 .static => argv.appendAssumeCapacity("-search_static_only"),
89 },
90 .paths_first => switch (system_lib.flags.preferred_link_mode) {
91 .dynamic => argv.appendAssumeCapacity("-search_paths_first"),
92 .static => argv.appendAssumeCapacity("-search_paths_first_static"),
93 },
94 .mode_first => switch (system_lib.flags.preferred_link_mode) {
95 .dynamic => argv.appendAssumeCapacity("-search_dylibs_first"),
96 .static => argv.appendAssumeCapacity("-search_static_first"),
97 },
98 }
99 prev_search_strategy = system_lib.flags.search_strategy;
100 prev_preferred_link_mode = system_lib.flags.preferred_link_mode;
101 }
102
103 const prefix: []const u8 = prefix: {
104 if (system_lib.flags.needed) break :prefix "-needed-l";
105 if (system_lib.flags.weak) break :prefix "-weak-l";
106 break :prefix "-l";
107 };
108 l: {
109 pc: {
110 const force = switch (system_lib.flags.use_pkg_config) {
111 .no => break :pc,
112 .yes => false,
113 .force => true,
114 };
115
116 const pkg_conf_node = progress_node.start("pkg-config", 0);
117 defer pkg_conf_node.end();
118
119 if (PkgConfig.run(maker, step, arena, pkg_conf_node, system_lib_name, force)) |result| {
120 try argv.appendSlice(arena, result.cflags);
121 try argv.appendSlice(arena, result.libs);
122 try seen_system_libs.put(arena, system_lib.name, result.cflags);
123 break :l;
124 } else |err| switch (err) {
125 error.PkgConfigUnavailable,
126 error.PackageNotFound,
127 => {
128 // pkg-config failed, so fall back to linking the library by name directly.
129 assert(!force);
130 break :pc;
131 },
132 else => |e| return e,
133 }
134 }
135 try argv.append(arena, try arena.print("{s}{s}", .{
136 prefix, system_lib_name,
137 }));
138 }
139 }
140
141 try argv.ensureUnusedCapacity(arena, 2);
142
143 const c_source_path = try maker.resolveLazyPathIndexAbs(arena, conf_tc.src_path, step_index);
144 argv.appendAssumeCapacity(c_source_path);
145
146 argv.appendAssumeCapacity("--listen=-");
147 const output_dir_path = (Step.evalZigProcess(step_index, maker, argv.items, progress_node, false) catch |err| switch (err) {
148 error.NeedCompileErrorCheck => unreachable,
149 else => |e| return e,
150 }).?;
151
152 const stem = Io.Dir.path.stem(Io.Dir.path.basename(c_source_path));
153 const out_basename = try arena.print("{s}.zig", .{stem});
154
155 maker.generatedPath(conf_tc.output_file).* = try output_dir_path.join(arena, out_basename);
156}