1const TranslateC = @This();
2
3const std = @import("std");
4const fs = std.fs;
5const mem = std.mem;
6const Step = std.Build.Step;
7const LazyPath = std.Build.LazyPath;
8const Configuration = std.Build.Configuration;
9
10step: Step,
11source: std.Build.LazyPath,
12include_dirs: std.ArrayList(std.Build.Module.IncludeDir) = .empty,
13system_libs: std.ArrayList(std.Build.Module.SystemLib) = .empty,
14c_macros: std.ArrayList(Configuration.String) = .empty,
15target: std.Build.ResolvedTarget,
16optimize: std.builtin.Optimize,
17output_file: Configuration.GeneratedFileIndex,
18link_libc: bool,
19
20pub const base_tag: Step.Tag = .translate_c;
21
22pub const Options = struct {
23 root_source_file: std.Build.LazyPath,
24 target: std.Build.ResolvedTarget,
25 optimize: std.builtin.Optimize,
26 link_libc: bool = true,
27};
28
29pub fn create(owner: *std.Build, options: Options) *TranslateC {
30 const graph = owner.graph;
31 const translate_c = graph.create(TranslateC);
32 const source = options.root_source_file.dupe(graph);
33 translate_c.* = .{
34 .step = .init(.{
35 .tag = base_tag,
36 .name = "translate-c",
37 .owner = owner,
38 }),
39 .source = source,
40 .target = options.target,
41 .optimize = options.optimize,
42 .output_file = graph.addGeneratedFile(&translate_c.step),
43 .link_libc = options.link_libc,
44 };
45 source.addStepDependencies(&translate_c.step);
46 return translate_c;
47}
48
49pub const AddExecutableOptions = struct {
50 name: ?[]const u8 = null,
51 version: ?std.SemanticVersion = null,
52 target: ?std.Build.ResolvedTarget = null,
53 optimize: ?std.builtin.Optimize = null,
54 linkage: ?std.builtin.LinkMode = null,
55};
56
57pub fn getOutput(translate_c: *TranslateC) std.Build.LazyPath {
58 return .{ .generated = .{ .index = translate_c.output_file } };
59}
60
61/// Creates a module from the translated source and adds it to the package's
62/// module set making it available to other packages which depend on this one.
63/// `createModule` can be used instead to create a private module.
64pub fn addModule(translate_c: *TranslateC, name: []const u8) *std.Build.Module {
65 return setUpModule(translate_c, translate_c.step.owner.addModule(name, .{
66 .root_source_file = translate_c.getOutput(),
67 .target = translate_c.target,
68 .optimize = translate_c.optimize,
69 .link_libc = translate_c.link_libc,
70 }));
71}
72
73/// Creates a private module from the translated source to be used by the
74/// current package, but not exposed to other packages depending on this one.
75/// `addModule` can be used instead to create a public module.
76pub fn createModule(translate_c: *TranslateC) *std.Build.Module {
77 return setUpModule(translate_c, translate_c.step.owner.createModule(.{
78 .root_source_file = translate_c.getOutput(),
79 .target = translate_c.target,
80 .optimize = translate_c.optimize,
81 .link_libc = translate_c.link_libc,
82 }));
83}
84
85fn setUpModule(translate_c: *TranslateC, module: *std.Build.Module) *std.Build.Module {
86 const graph = translate_c.step.owner.graph;
87 const arena = graph.arena;
88
89 if (translate_c.link_libc) module.link_libc = true;
90
91 for (translate_c.system_libs.items) |system_lib| {
92 module.link_objects.append(arena, .{ .system_lib = system_lib }) catch @panic("OOM");
93 }
94
95 return module;
96}
97
98pub fn addAfterIncludePath(translate_c: *TranslateC, lazy_path: LazyPath) void {
99 const graph = translate_c.step.owner.graph;
100 const arena = graph.arena;
101 translate_c.include_dirs.append(arena, .{ .path_after = lazy_path.dupe(graph) }) catch
102 @panic("OOM");
103 lazy_path.addStepDependencies(&translate_c.step);
104}
105
106pub fn addSystemIncludePath(translate_c: *TranslateC, lazy_path: LazyPath) void {
107 const graph = translate_c.step.owner.graph;
108 const arena = graph.arena;
109 translate_c.include_dirs.append(arena, .{ .path_system = lazy_path.dupe(graph) }) catch
110 @panic("OOM");
111 lazy_path.addStepDependencies(&translate_c.step);
112}
113
114pub fn addIncludePath(translate_c: *TranslateC, lazy_path: LazyPath) void {
115 const graph = translate_c.step.owner.graph;
116 const arena = graph.arena;
117 translate_c.include_dirs.append(arena, .{ .path = lazy_path.dupe(graph) }) catch
118 @panic("OOM");
119 lazy_path.addStepDependencies(&translate_c.step);
120}
121
122pub fn addConfigHeader(translate_c: *TranslateC, config_header: *Step.ConfigHeader) void {
123 const graph = translate_c.step.owner.graph;
124 const arena = graph.arena;
125 translate_c.include_dirs.append(arena, .{ .config_header_step = config_header }) catch
126 @panic("OOM");
127 translate_c.step.dependOn(&config_header.step);
128}
129
130pub fn addSystemFrameworkPath(translate_c: *TranslateC, directory_path: LazyPath) void {
131 const graph = translate_c.step.owner.graph;
132 const arena = graph.arena;
133 translate_c.include_dirs.append(arena, .{ .framework_path_system = directory_path.dupe(graph) }) catch
134 @panic("OOM");
135 directory_path.addStepDependencies(&translate_c.step);
136}
137
138pub fn addFrameworkPath(translate_c: *TranslateC, directory_path: LazyPath) void {
139 const graph = translate_c.step.owner.graph;
140 const arena = graph.arena;
141 translate_c.include_dirs.append(arena, .{ .framework_path = directory_path.dupe(graph) }) catch
142 @panic("OOM");
143 directory_path.addStepDependencies(&translate_c.step);
144}
145
146pub fn addCheckFile(translate_c: *TranslateC, expected_matches: []const []const u8) *Step.CheckFile {
147 return Step.CheckFile.create(
148 translate_c.step.owner,
149 translate_c.getOutput(),
150 .{ .expected_matches = expected_matches },
151 );
152}
153
154/// If the value is omitted, it is set to 1.
155/// `name` and `value` need not live longer than the function call.
156pub fn defineCMacro(translate_c: *TranslateC, name: []const u8, value: ?[]const u8) void {
157 const graph = translate_c.step.owner.graph;
158 const arena = graph.arena;
159 const wc = &graph.wip_configuration;
160 const macro = arena.print("{s}={s}", .{ name, value orelse "1" }) catch @panic("OOM");
161 const macro_string = wc.addString(macro) catch @panic("OOM");
162 translate_c.c_macros.append(arena, macro_string) catch @panic("OOM");
163}
164
165/// name_and_value looks like [name]=[value].
166pub fn defineCMacroRaw(translate_c: *TranslateC, name_and_value: []const u8) void {
167 const graph = translate_c.step.owner.graph;
168 const arena = graph.arena;
169 const wc = &graph.wip_configuration;
170 const macro_string = wc.addString(name_and_value) catch @panic("OOM");
171 translate_c.c_macros.append(arena, macro_string) catch @panic("OOM");
172}
173
174pub fn linkSystemLibrary(
175 translate_c: *TranslateC,
176 name: []const u8,
177 options: std.Build.Module.LinkSystemLibraryOptions,
178) void {
179 const graph = translate_c.step.owner.graph;
180 const arena = graph.arena;
181 translate_c.system_libs.append(arena, .{
182 .name = graph.dupeString(name),
183 .needed = options.needed,
184 .weak = options.weak,
185 .use_pkg_config = options.use_pkg_config,
186 .preferred_link_mode = options.preferred_link_mode,
187 .search_strategy = options.search_strategy,
188 }) catch @panic("OOM");
189}