master
1const std = @import("std");
2const assert = std.debug.assert;
3const mem = std.mem;
4const process = std.process;
5const aro = @import("aro");
6const compiler_util = @import("../util.zig");
7const Translator = @import("Translator.zig");
8
9const fast_exit = @import("builtin").mode != .Debug;
10
11var general_purpose_allocator: std.heap.GeneralPurposeAllocator(.{}) = .init;
12
13pub fn main() u8 {
14 const gpa = general_purpose_allocator.allocator();
15 defer _ = general_purpose_allocator.deinit();
16
17 var arena_instance = std.heap.ArenaAllocator.init(gpa);
18 defer arena_instance.deinit();
19 const arena = arena_instance.allocator();
20
21 var threaded: std.Io.Threaded = .init(gpa);
22 defer threaded.deinit();
23 const io = threaded.io();
24
25 const args = process.argsAlloc(arena) catch {
26 std.debug.print("ran out of memory allocating arguments\n", .{});
27 if (fast_exit) process.exit(1);
28 return 1;
29 };
30
31 var zig_integration = false;
32 if (args.len > 1 and std.mem.eql(u8, args[1], "--zig-integration")) {
33 zig_integration = true;
34 }
35
36 var stderr_buf: [1024]u8 = undefined;
37 var stderr = std.fs.File.stderr().writer(&stderr_buf);
38 var diagnostics: aro.Diagnostics = switch (zig_integration) {
39 false => .{ .output = .{ .to_writer = .{
40 .color = .detect(stderr.file),
41 .writer = &stderr.interface,
42 } } },
43 true => .{ .output = .{ .to_list = .{
44 .arena = .init(gpa),
45 } } },
46 };
47 defer diagnostics.deinit();
48
49 var comp = aro.Compilation.initDefault(gpa, arena, io, &diagnostics, std.fs.cwd()) catch |err| switch (err) {
50 error.OutOfMemory => {
51 std.debug.print("ran out of memory initializing C compilation\n", .{});
52 if (fast_exit) process.exit(1);
53 return 1;
54 },
55 };
56 defer comp.deinit();
57
58 var driver: aro.Driver = .{ .comp = &comp, .diagnostics = &diagnostics, .aro_name = "aro" };
59 defer driver.deinit();
60
61 var toolchain: aro.Toolchain = .{ .driver = &driver };
62 defer toolchain.deinit();
63
64 translate(&driver, &toolchain, args, zig_integration) catch |err| switch (err) {
65 error.OutOfMemory => {
66 std.debug.print("ran out of memory translating\n", .{});
67 if (fast_exit) process.exit(1);
68 return 1;
69 },
70 error.FatalError => if (zig_integration) {
71 serveErrorBundle(arena, &diagnostics) catch |bundle_err| {
72 std.debug.print("unable to serve error bundle: {}\n", .{bundle_err});
73 if (fast_exit) process.exit(1);
74 return 1;
75 };
76
77 if (fast_exit) process.exit(0);
78 return 0;
79 } else {
80 if (fast_exit) process.exit(1);
81 return 1;
82 },
83 error.WriteFailed => {
84 std.debug.print("unable to write to stdout\n", .{});
85 if (fast_exit) process.exit(1);
86 return 1;
87 },
88 };
89
90 assert(comp.diagnostics.errors == 0 or !zig_integration);
91 if (fast_exit) process.exit(@intFromBool(comp.diagnostics.errors != 0));
92 return @intFromBool(comp.diagnostics.errors != 0);
93}
94
95fn serveErrorBundle(arena: std.mem.Allocator, diagnostics: *const aro.Diagnostics) !void {
96 const error_bundle = try compiler_util.aroDiagnosticsToErrorBundle(
97 diagnostics,
98 arena,
99 "translation failure",
100 );
101 var stdout_buffer: [1024]u8 = undefined;
102 var stdout_writer = std.fs.File.stdout().writer(&stdout_buffer);
103 var server: std.zig.Server = .{
104 .out = &stdout_writer.interface,
105 .in = undefined,
106 };
107 try server.serveErrorBundle(error_bundle);
108}
109
110pub const usage =
111 \\Usage {s}: [options] file [CC options]
112 \\
113 \\Options:
114 \\ --help Print this message
115 \\ --version Print translate-c version
116 \\ -fmodule-libs Import libraries as modules
117 \\ -fno-module-libs (default) Install libraries next to output file
118 \\
119 \\
120;
121
122fn translate(d: *aro.Driver, tc: *aro.Toolchain, args: [][:0]u8, zig_integration: bool) !void {
123 const gpa = d.comp.gpa;
124
125 const aro_args = args: {
126 var i: usize = 0;
127 for (args) |arg| {
128 args[i] = arg;
129 if (mem.eql(u8, arg, "--help")) {
130 var stdout_buf: [512]u8 = undefined;
131 var stdout = std.fs.File.stdout().writer(&stdout_buf);
132 try stdout.interface.print(usage, .{args[0]});
133 try stdout.interface.flush();
134 return;
135 } else if (mem.eql(u8, arg, "--version")) {
136 var stdout_buf: [512]u8 = undefined;
137 var stdout = std.fs.File.stdout().writer(&stdout_buf);
138 // TODO add version
139 try stdout.interface.writeAll("0.0.0-dev\n");
140 try stdout.interface.flush();
141 return;
142 } else if (mem.eql(u8, arg, "--zig-integration")) {
143 if (i != 1 or !zig_integration)
144 return d.fatal("--zig-integration must be the first argument", .{});
145 } else {
146 i += 1;
147 }
148 }
149 break :args args[0..i];
150 };
151 const user_macros = macros: {
152 var macro_buf: std.ArrayList(u8) = .empty;
153 defer macro_buf.deinit(gpa);
154
155 var discard_buf: [256]u8 = undefined;
156 var discarding: std.Io.Writer.Discarding = .init(&discard_buf);
157 assert(!try d.parseArgs(&discarding.writer, ¯o_buf, aro_args));
158 if (macro_buf.items.len > std.math.maxInt(u32)) {
159 return d.fatal("user provided macro source exceeded max size", .{});
160 }
161
162 const has_output_file = if (d.output_name) |path|
163 !std.mem.eql(u8, path, "-")
164 else
165 false;
166 if (zig_integration and !has_output_file) {
167 return d.fatal("--zig-integration requires specifying an output file", .{});
168 }
169
170 const content = try macro_buf.toOwnedSlice(gpa);
171 errdefer gpa.free(content);
172
173 break :macros try d.comp.addSourceFromOwnedBuffer("<command line>", content, .user);
174 };
175
176 if (d.inputs.items.len != 1) {
177 return d.fatal("expected exactly one input file", .{});
178 }
179 const source = d.inputs.items[0];
180
181 tc.discover() catch |er| switch (er) {
182 error.OutOfMemory => return error.OutOfMemory,
183 error.TooManyMultilibs => return d.fatal("found more than one multilib with the same priority", .{}),
184 };
185 try tc.defineSystemIncludes();
186 try d.comp.initSearchPath(d.includes.items, d.verbose_search_path);
187
188 const builtin_macros = d.comp.generateBuiltinMacros(d.system_defines) catch |err| switch (err) {
189 error.FileTooBig => return d.fatal("builtin macro source exceeded max size", .{}),
190 else => |e| return e,
191 };
192
193 var pp = try aro.Preprocessor.initDefault(d.comp);
194 defer pp.deinit();
195
196 var name_buf: [std.fs.max_name_bytes]u8 = undefined;
197 // Omit the source file from the dep file so that it can be tracked separately.
198 // In the Zig compiler we want to omit it from the cache hash since it will
199 // be written to a tmp file then renamed into place, meaning the path will be
200 // wrong as soon as the work is done.
201 var opt_dep_file = try d.initDepFile(source, &name_buf, true);
202 defer if (opt_dep_file) |*dep_file| dep_file.deinit(gpa);
203
204 if (opt_dep_file) |*dep_file| pp.dep_file = dep_file;
205
206 try pp.preprocessSources(.{
207 .main = source,
208 .builtin = builtin_macros,
209 .command_line = user_macros,
210 .imacros = d.imacros.items,
211 .implicit_includes = d.implicit_includes.items,
212 });
213
214 var c_tree = try pp.parse();
215 defer c_tree.deinit();
216
217 if (d.diagnostics.errors != 0) {
218 if (fast_exit and !zig_integration) process.exit(1);
219 return error.FatalError;
220 }
221
222 var out_buf: [4096]u8 = undefined;
223 if (opt_dep_file) |dep_file| {
224 const dep_file_name = try d.getDepFileName(source, out_buf[0..std.fs.max_name_bytes]);
225
226 const file = if (dep_file_name) |path|
227 d.comp.cwd.createFile(path, .{}) catch |er|
228 return d.fatal("unable to create dependency file '{s}': {s}", .{ path, aro.Driver.errorDescription(er) })
229 else
230 std.fs.File.stdout();
231 defer if (dep_file_name != null) file.close();
232
233 var file_writer = file.writer(&out_buf);
234 dep_file.write(&file_writer.interface) catch
235 return d.fatal("unable to write dependency file: {s}", .{aro.Driver.errorDescription(file_writer.err.?)});
236 }
237
238 const rendered_zig = try Translator.translate(.{
239 .gpa = gpa,
240 .comp = d.comp,
241 .pp = &pp,
242 .tree = &c_tree,
243 });
244 defer gpa.free(rendered_zig);
245
246 var close_out_file = false;
247 var out_file_path: []const u8 = "<stdout>";
248 var out_file: std.fs.File = .stdout();
249 defer if (close_out_file) out_file.close();
250
251 if (d.output_name) |path| blk: {
252 if (std.mem.eql(u8, path, "-")) break :blk;
253 if (std.fs.path.dirname(path)) |dirname| {
254 std.fs.cwd().makePath(dirname) catch |err|
255 return d.fatal("failed to create path to '{s}': {s}", .{ path, aro.Driver.errorDescription(err) });
256 }
257 out_file = std.fs.cwd().createFile(path, .{}) catch |err| {
258 return d.fatal("failed to create output file '{s}': {s}", .{ path, aro.Driver.errorDescription(err) });
259 };
260 close_out_file = true;
261 out_file_path = path;
262 }
263
264 var out_writer = out_file.writer(&out_buf);
265 out_writer.interface.writeAll(rendered_zig) catch {};
266 out_writer.interface.flush() catch {};
267 if (out_writer.err) |write_err|
268 return d.fatal("failed to write result to '{s}': {s}", .{ out_file_path, aro.Driver.errorDescription(write_err) });
269
270 if (fast_exit and !zig_integration) process.exit(0);
271}
272
273test {
274 _ = Translator;
275 _ = @import("helpers.zig");
276 _ = @import("PatternList.zig");
277}