From 8ef8150671b1cf3ece2662da1ce28cec464cf245 Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Wed, 6 Nov 2024 19:35:57 +0200 Subject: [PATCH 1/3] create base for a TranslateC build step --- build.zig | 2 + build/TranslateC.zig | 112 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 114 insertions(+) create mode 100644 build/TranslateC.zig diff --git a/build.zig b/build.zig index 6424c7e..ee1fc94 100644 --- a/build.zig +++ b/build.zig @@ -1,5 +1,7 @@ const std = @import("std"); +pub const TranslateC = @import("build/TranslateC.zig"); + pub fn build(b: *std.Build) void { const target = b.standardTargetOptions(.{}); const optimize = b.standardOptimizeOption(.{}); diff --git a/build/TranslateC.zig b/build/TranslateC.zig new file mode 100644 index 0000000..0266215 --- /dev/null +++ b/build/TranslateC.zig @@ -0,0 +1,112 @@ +const std = @import("std"); +const Step = std.Build.Step; +const LazyPath = std.Build.LazyPath; +const fs = std.fs; +const mem = std.mem; + +const TranslateC = @This(); + +pub const base_id: Step.Id = .custom; + +step: Step, +translate_c_exe: *Step.Compile, +source: std.Build.LazyPath, +include_dirs: std.ArrayList(std.Build.Module.IncludeDir), +target: std.Build.ResolvedTarget, +optimize: std.builtin.OptimizeMode, +output_file: std.Build.GeneratedFile, +link_libc: bool, + +pub const Options = struct { + root_source_file: std.Build.LazyPath, + target: std.Build.ResolvedTarget, + optimize: std.builtin.OptimizeMode, + link_libc: bool = true, + translate_c_dep_name: []const u8 = "translate-c", +}; + +pub fn create(owner: *std.Build, options: Options) *TranslateC { + const translate_c_exe = owner.dependency(options.translate_c_dep_name, .{ + .optimize = .ReleaseFast, + }).artifact("translate-c"); + + const translate_c = owner.allocator.create(TranslateC) catch @panic("OOM"); + const source = options.root_source_file.dupe(owner); + translate_c.* = .{ + .step = Step.init(.{ + .id = base_id, + .name = "translate-c", + .owner = owner, + .makeFn = make, + }), + .translate_c_exe = translate_c_exe, + .source = source, + .include_dirs = .init(owner.allocator), + .target = options.target, + .optimize = options.optimize, + .output_file = .{ .step = &translate_c.step }, + .link_libc = options.link_libc, + }; + source.addStepDependencies(&translate_c.step); + translate_c.step.dependOn(&translate_c_exe.step); + return translate_c; +} + +pub const AddExecutableOptions = struct { + name: ?[]const u8 = null, + version: ?std.SemanticVersion = null, + target: ?std.Build.ResolvedTarget = null, + optimize: ?std.builtin.OptimizeMode = null, + linkage: ?std.builtin.LinkMode = null, +}; + +pub fn getOutput(translate_c: *TranslateC) std.Build.LazyPath { + return .{ .generated = .{ .file = &translate_c.output_file } }; +} + +/// Creates a step to build an executable from the translated source. +pub fn addExecutable(translate_c: *TranslateC, options: AddExecutableOptions) *Step.Compile { + return translate_c.step.owner.addExecutable(.{ + .root_source_file = translate_c.getOutput(), + .name = options.name orelse "translated_c", + .version = options.version, + .target = options.target orelse translate_c.target, + .optimize = options.optimize orelse translate_c.optimize, + .linkage = options.linkage, + }); +} + +/// Creates a module from the translated source and adds it to the package's +/// module set making it available to other packages which depend on this one. +/// `createModule` can be used instead to create a private module. +pub fn addModule(translate_c: *TranslateC, name: []const u8) *std.Build.Module { + return translate_c.step.owner.addModule(name, .{ + .root_source_file = translate_c.getOutput(), + }); +} + +/// Creates a private module from the translated source to be used by the +/// current package, but not exposed to other packages depending on this one. +/// `addModule` can be used instead to create a public module. +pub fn createModule(translate_c: *TranslateC) *std.Build.Module { + return translate_c.step.owner.createModule(.{ + .root_source_file = translate_c.getOutput(), + .target = translate_c.target, + .optimize = translate_c.optimize, + .link_libc = translate_c.link_libc, + }); +} + +pub fn addCheckFile(translate_c: *TranslateC, expected_matches: []const []const u8) *Step.CheckFile { + return Step.CheckFile.create( + translate_c.step.owner, + translate_c.getOutput(), + .{ .expected_matches = expected_matches }, + ); +} + +fn make(step: *Step, options: Step.MakeOptions) !void { + _ = options; + const translate_c: *TranslateC = @fieldParentPtr("step", step); + _ = translate_c; +} From 8d0b51f4aba2391007651529d70c69cc9a9f9861 Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Wed, 6 Nov 2024 21:59:53 +0200 Subject: [PATCH 2/3] run translate command from build step --- build/TranslateC.zig | 79 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 78 insertions(+), 1 deletion(-) diff --git a/build/TranslateC.zig b/build/TranslateC.zig index 0266215..9e3e314 100644 --- a/build/TranslateC.zig +++ b/build/TranslateC.zig @@ -107,6 +107,83 @@ pub fn addCheckFile(translate_c: *TranslateC, expected_matches: []const []const fn make(step: *Step, options: Step.MakeOptions) !void { _ = options; + const b = step.owner; const translate_c: *TranslateC = @fieldParentPtr("step", step); - _ = translate_c; + + var argv_list = std.ArrayList([]const u8).init(b.allocator); + try argv_list.append(translate_c.translate_c_exe.getEmittedBin().getPath(b)); + + var man = b.graph.cache.obtain(); + defer man.deinit(); + + // Random bytes to make TranslateC unique. Refresh this with new + // random bytes when TranslateC implementation is modified in a + // non-backwards-compatible way. + man.hash.add(@as(u32, 0x2701BED2)); + + if (!translate_c.target.query.isNative()) { + const triple = try translate_c.target.query.zigTriple(b.allocator); + try argv_list.append(b.fmt("--target={s}", .{triple})); + man.hash.addBytes(triple); + } + + const c_source_path = translate_c.source.getPath3(b, step); + _ = try man.addFilePath(c_source_path, null); + const resolved_source_path = b.pathResolve(&.{ c_source_path.root_dir.path orelse ".", c_source_path.sub_path }); + try argv_list.append(resolved_source_path); + + const out_name = b.fmt("{s}.zig", .{std.fs.path.stem(c_source_path.sub_path)}); + if (try step.cacheHit(&man)) { + const digest = man.final(); + translate_c.output_file.path = try b.cache_root.join(b.allocator, &.{ + "o", &digest, out_name, + }); + return; + } + + const digest = man.final(); + + const sub_path = b.pathJoin(&.{ "o", &digest, out_name }); + const sub_path_dirname = std.fs.path.dirname(sub_path).?; + + b.cache_root.handle.makePath(sub_path_dirname) catch |err| { + return step.fail("unable to make path '{}{s}': {s}", .{ + b.cache_root, sub_path_dirname, @errorName(err), + }); + }; + try argv_list.append("-o"); + try argv_list.append(sub_path); + + var child = std.process.Child.init(argv_list.items, b.allocator); + child.cwd = b.build_root.path; + child.cwd_dir = b.build_root.handle; + child.env_map = &b.graph.env_map; + + child.stdin_behavior = .Ignore; + child.stdout_behavior = .Ignore; + child.stderr_behavior = .Pipe; + + try child.spawn(); + const stderr = try child.stderr.?.reader().readAllAlloc(b.allocator, 10 * 1024 * 1024); + const term = try child.wait(); + + switch (term) { + .Exited => |code| { + if (code != 0) { + return step.fail( + "failed to translate {s}:\n{s}", + .{ resolved_source_path, stderr }, + ); + } + }, + .Signal, .Stopped, .Unknown => { + return step.fail( + "command to translate {s} failed unexpectedly", + .{resolved_source_path}, + ); + }, + } + + translate_c.output_file.path = try b.cache_root.join(b.allocator, &.{sub_path}); + try man.writeManifest(); } From f69c763a2f1aad30f8929ed264bc5f0413fa47f3 Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Wed, 6 Nov 2024 22:26:48 +0200 Subject: [PATCH 3/3] move arg processing to main --- build/TranslateC.zig | 5 +++-- src/Translator.zig | 31 +------------------------- src/main.zig | 53 ++++++++++++++++++++++++++++++++++++-------- 3 files changed, 48 insertions(+), 41 deletions(-) diff --git a/build/TranslateC.zig b/build/TranslateC.zig index 9e3e314..7f052d1 100644 --- a/build/TranslateC.zig +++ b/build/TranslateC.zig @@ -145,6 +145,7 @@ fn make(step: *Step, options: Step.MakeOptions) !void { const sub_path = b.pathJoin(&.{ "o", &digest, out_name }); const sub_path_dirname = std.fs.path.dirname(sub_path).?; + const out_path = try b.cache_root.join(b.allocator, &.{sub_path}); b.cache_root.handle.makePath(sub_path_dirname) catch |err| { return step.fail("unable to make path '{}{s}': {s}", .{ @@ -152,7 +153,7 @@ fn make(step: *Step, options: Step.MakeOptions) !void { }); }; try argv_list.append("-o"); - try argv_list.append(sub_path); + try argv_list.append(out_path); var child = std.process.Child.init(argv_list.items, b.allocator); child.cwd = b.build_root.path; @@ -184,6 +185,6 @@ fn make(step: *Step, options: Step.MakeOptions) !void { }, } - translate_c.output_file.path = try b.cache_root.join(b.allocator, &.{sub_path}); + translate_c.output_file.path = out_path; try man.writeManifest(); } diff --git a/src/Translator.zig b/src/Translator.zig index 7627c18..90f14b3 100644 --- a/src/Translator.zig +++ b/src/Translator.zig @@ -437,37 +437,8 @@ fn warn(c: *Context, scope: *Scope, loc: TokenIndex, comptime format: []const u8 pub fn translate( gpa: mem.Allocator, comp: *aro.Compilation, - args: []const []const u8, + tree: aro.Tree, ) !std.zig.Ast { - try comp.addDefaultPragmaHandlers(); - comp.langopts.setEmulatedCompiler(aro.target_util.systemCompiler(comp.target)); - - var driver: aro.Driver = .{ .comp = comp }; - defer driver.deinit(); - - var macro_buf = std.ArrayList(u8).init(gpa); - defer macro_buf.deinit(); - - assert(!try driver.parseArgs(std.io.null_writer, macro_buf.writer(), args)); - assert(driver.inputs.items.len == 1); - const source = driver.inputs.items[0]; - - const builtin_macros = try comp.generateBuiltinMacros(.include_system_defines, null); - const user_macros = try comp.addSourceFromBuffer("", macro_buf.items); - - var pp = try aro.Preprocessor.initDefault(comp); - defer pp.deinit(); - - try pp.preprocessSources(&.{ source, builtin_macros, user_macros }); - - var tree = try pp.parse(); - defer tree.deinit(); - - // Workaround for https://github.com/Vexu/arocc/issues/603 - for (comp.diagnostics.list.items) |msg| { - if (msg.kind == .@"error" or msg.kind == .@"fatal error") return error.ParsingFailed; - } - const mapper = tree.comp.string_interner.getFastTypeMapper(tree.comp.gpa) catch tree.comp.string_interner.getSlowTypeMapper(); defer mapper.deinit(tree.comp.gpa); diff --git a/src/main.zig b/src/main.zig index 3899e3f..bcfdedd 100644 --- a/src/main.zig +++ b/src/main.zig @@ -1,7 +1,9 @@ const std = @import("std"); +const assert = std.debug.assert; const aro = @import("aro"); const Translator = @import("Translator.zig"); +// TODO cleanup and handle errors more gracefully pub fn main() !void { var arena_instance = std.heap.ArenaAllocator.init(std.heap.page_allocator); defer arena_instance.deinit(); @@ -12,17 +14,50 @@ pub fn main() !void { const args = try std.process.argsAlloc(arena); - var aro_comp = aro.Compilation.init(gpa, std.fs.cwd()); - defer aro_comp.deinit(); + var comp = aro.Compilation.init(gpa, std.fs.cwd()); + defer comp.deinit(); - var tree = Translator.translate(gpa, &aro_comp, args) catch |err| switch (err) { - error.ParsingFailed, error.FatalError => renderErrorsAndExit(&aro_comp), - error.OutOfMemory => return error.OutOfMemory, - error.StreamTooLong => std.zig.fatal("An input file was larger than 4GiB", .{}), - }; - defer tree.deinit(gpa); + try comp.addDefaultPragmaHandlers(); + comp.langopts.setEmulatedCompiler(aro.target_util.systemCompiler(comp.target)); - const formatted = try tree.render(arena); + var driver: aro.Driver = .{ .comp = &comp }; + defer driver.deinit(); + + var macro_buf = std.ArrayList(u8).init(gpa); + defer macro_buf.deinit(); + + assert(!try driver.parseArgs(std.io.null_writer, macro_buf.writer(), args)); + assert(driver.inputs.items.len == 1); + const source = driver.inputs.items[0]; + + const builtin_macros = try comp.generateBuiltinMacros(.include_system_defines, null); + const user_macros = try comp.addSourceFromBuffer("", macro_buf.items); + + var pp = try aro.Preprocessor.initDefault(&comp); + defer pp.deinit(); + + try pp.preprocessSources(&.{ source, builtin_macros, user_macros }); + + var c_tree = try pp.parse(); + defer c_tree.deinit(); + + // Workaround for https://github.com/Vexu/arocc/issues/603 + for (comp.diagnostics.list.items) |msg| { + if (msg.kind == .@"error" or msg.kind == .@"fatal error") return renderErrorsAndExit(&comp); + } + + var zig_tree = try Translator.translate(gpa, &comp, c_tree); + defer zig_tree.deinit(gpa); + + const formatted = try zig_tree.render(arena); + if (driver.output_name) |path| blk: { + if (std.mem.eql(u8, path, "-")) break :blk; + const out_file = try std.fs.cwd().createFile(path, .{}); + defer out_file.close(); + + try out_file.writeAll(formatted); + return std.process.cleanExit(); + } try std.io.getStdOut().writeAll(formatted); return std.process.cleanExit(); }