Skip to content

Commit 50fb8b7

Browse files
committed
Merge branch 'build-step'
2 parents c3dbba1 + f69c763 commit 50fb8b7

File tree

4 files changed

+237
-39
lines changed

4 files changed

+237
-39
lines changed

build.zig

+2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
const std = @import("std");
22

3+
pub const TranslateC = @import("build/TranslateC.zig");
4+
35
pub fn build(b: *std.Build) void {
46
const target = b.standardTargetOptions(.{});
57
const optimize = b.standardOptimizeOption(.{});

build/TranslateC.zig

+190
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,190 @@
1+
const std = @import("std");
2+
const Step = std.Build.Step;
3+
const LazyPath = std.Build.LazyPath;
4+
const fs = std.fs;
5+
const mem = std.mem;
6+
7+
const TranslateC = @This();
8+
9+
pub const base_id: Step.Id = .custom;
10+
11+
step: Step,
12+
translate_c_exe: *Step.Compile,
13+
source: std.Build.LazyPath,
14+
include_dirs: std.ArrayList(std.Build.Module.IncludeDir),
15+
target: std.Build.ResolvedTarget,
16+
optimize: std.builtin.OptimizeMode,
17+
output_file: std.Build.GeneratedFile,
18+
link_libc: bool,
19+
20+
pub const Options = struct {
21+
root_source_file: std.Build.LazyPath,
22+
target: std.Build.ResolvedTarget,
23+
optimize: std.builtin.OptimizeMode,
24+
link_libc: bool = true,
25+
translate_c_dep_name: []const u8 = "translate-c",
26+
};
27+
28+
pub fn create(owner: *std.Build, options: Options) *TranslateC {
29+
const translate_c_exe = owner.dependency(options.translate_c_dep_name, .{
30+
.optimize = .ReleaseFast,
31+
}).artifact("translate-c");
32+
33+
const translate_c = owner.allocator.create(TranslateC) catch @panic("OOM");
34+
const source = options.root_source_file.dupe(owner);
35+
translate_c.* = .{
36+
.step = Step.init(.{
37+
.id = base_id,
38+
.name = "translate-c",
39+
.owner = owner,
40+
.makeFn = make,
41+
}),
42+
.translate_c_exe = translate_c_exe,
43+
.source = source,
44+
.include_dirs = .init(owner.allocator),
45+
.target = options.target,
46+
.optimize = options.optimize,
47+
.output_file = .{ .step = &translate_c.step },
48+
.link_libc = options.link_libc,
49+
};
50+
source.addStepDependencies(&translate_c.step);
51+
translate_c.step.dependOn(&translate_c_exe.step);
52+
return translate_c;
53+
}
54+
55+
pub const AddExecutableOptions = struct {
56+
name: ?[]const u8 = null,
57+
version: ?std.SemanticVersion = null,
58+
target: ?std.Build.ResolvedTarget = null,
59+
optimize: ?std.builtin.OptimizeMode = null,
60+
linkage: ?std.builtin.LinkMode = null,
61+
};
62+
63+
pub fn getOutput(translate_c: *TranslateC) std.Build.LazyPath {
64+
return .{ .generated = .{ .file = &translate_c.output_file } };
65+
}
66+
67+
/// Creates a step to build an executable from the translated source.
68+
pub fn addExecutable(translate_c: *TranslateC, options: AddExecutableOptions) *Step.Compile {
69+
return translate_c.step.owner.addExecutable(.{
70+
.root_source_file = translate_c.getOutput(),
71+
.name = options.name orelse "translated_c",
72+
.version = options.version,
73+
.target = options.target orelse translate_c.target,
74+
.optimize = options.optimize orelse translate_c.optimize,
75+
.linkage = options.linkage,
76+
});
77+
}
78+
79+
/// Creates a module from the translated source and adds it to the package's
80+
/// module set making it available to other packages which depend on this one.
81+
/// `createModule` can be used instead to create a private module.
82+
pub fn addModule(translate_c: *TranslateC, name: []const u8) *std.Build.Module {
83+
return translate_c.step.owner.addModule(name, .{
84+
.root_source_file = translate_c.getOutput(),
85+
});
86+
}
87+
88+
/// Creates a private module from the translated source to be used by the
89+
/// current package, but not exposed to other packages depending on this one.
90+
/// `addModule` can be used instead to create a public module.
91+
pub fn createModule(translate_c: *TranslateC) *std.Build.Module {
92+
return translate_c.step.owner.createModule(.{
93+
.root_source_file = translate_c.getOutput(),
94+
.target = translate_c.target,
95+
.optimize = translate_c.optimize,
96+
.link_libc = translate_c.link_libc,
97+
});
98+
}
99+
100+
pub fn addCheckFile(translate_c: *TranslateC, expected_matches: []const []const u8) *Step.CheckFile {
101+
return Step.CheckFile.create(
102+
translate_c.step.owner,
103+
translate_c.getOutput(),
104+
.{ .expected_matches = expected_matches },
105+
);
106+
}
107+
108+
fn make(step: *Step, options: Step.MakeOptions) !void {
109+
_ = options;
110+
const b = step.owner;
111+
const translate_c: *TranslateC = @fieldParentPtr("step", step);
112+
113+
var argv_list = std.ArrayList([]const u8).init(b.allocator);
114+
try argv_list.append(translate_c.translate_c_exe.getEmittedBin().getPath(b));
115+
116+
var man = b.graph.cache.obtain();
117+
defer man.deinit();
118+
119+
// Random bytes to make TranslateC unique. Refresh this with new
120+
// random bytes when TranslateC implementation is modified in a
121+
// non-backwards-compatible way.
122+
man.hash.add(@as(u32, 0x2701BED2));
123+
124+
if (!translate_c.target.query.isNative()) {
125+
const triple = try translate_c.target.query.zigTriple(b.allocator);
126+
try argv_list.append(b.fmt("--target={s}", .{triple}));
127+
man.hash.addBytes(triple);
128+
}
129+
130+
const c_source_path = translate_c.source.getPath3(b, step);
131+
_ = try man.addFilePath(c_source_path, null);
132+
const resolved_source_path = b.pathResolve(&.{ c_source_path.root_dir.path orelse ".", c_source_path.sub_path });
133+
try argv_list.append(resolved_source_path);
134+
135+
const out_name = b.fmt("{s}.zig", .{std.fs.path.stem(c_source_path.sub_path)});
136+
if (try step.cacheHit(&man)) {
137+
const digest = man.final();
138+
translate_c.output_file.path = try b.cache_root.join(b.allocator, &.{
139+
"o", &digest, out_name,
140+
});
141+
return;
142+
}
143+
144+
const digest = man.final();
145+
146+
const sub_path = b.pathJoin(&.{ "o", &digest, out_name });
147+
const sub_path_dirname = std.fs.path.dirname(sub_path).?;
148+
const out_path = try b.cache_root.join(b.allocator, &.{sub_path});
149+
150+
b.cache_root.handle.makePath(sub_path_dirname) catch |err| {
151+
return step.fail("unable to make path '{}{s}': {s}", .{
152+
b.cache_root, sub_path_dirname, @errorName(err),
153+
});
154+
};
155+
try argv_list.append("-o");
156+
try argv_list.append(out_path);
157+
158+
var child = std.process.Child.init(argv_list.items, b.allocator);
159+
child.cwd = b.build_root.path;
160+
child.cwd_dir = b.build_root.handle;
161+
child.env_map = &b.graph.env_map;
162+
163+
child.stdin_behavior = .Ignore;
164+
child.stdout_behavior = .Ignore;
165+
child.stderr_behavior = .Pipe;
166+
167+
try child.spawn();
168+
const stderr = try child.stderr.?.reader().readAllAlloc(b.allocator, 10 * 1024 * 1024);
169+
const term = try child.wait();
170+
171+
switch (term) {
172+
.Exited => |code| {
173+
if (code != 0) {
174+
return step.fail(
175+
"failed to translate {s}:\n{s}",
176+
.{ resolved_source_path, stderr },
177+
);
178+
}
179+
},
180+
.Signal, .Stopped, .Unknown => {
181+
return step.fail(
182+
"command to translate {s} failed unexpectedly",
183+
.{resolved_source_path},
184+
);
185+
},
186+
}
187+
188+
translate_c.output_file.path = out_path;
189+
try man.writeManifest();
190+
}

src/Translator.zig

+1-30
Original file line numberDiff line numberDiff line change
@@ -445,37 +445,8 @@ fn tokenIndex(c: *Context, node: NodeIndex) ?TokenIndex {
445445
pub fn translate(
446446
gpa: mem.Allocator,
447447
comp: *aro.Compilation,
448-
args: []const []const u8,
448+
tree: aro.Tree,
449449
) !std.zig.Ast {
450-
try comp.addDefaultPragmaHandlers();
451-
comp.langopts.setEmulatedCompiler(aro.target_util.systemCompiler(comp.target));
452-
453-
var driver: aro.Driver = .{ .comp = comp };
454-
defer driver.deinit();
455-
456-
var macro_buf = std.ArrayList(u8).init(gpa);
457-
defer macro_buf.deinit();
458-
459-
assert(!try driver.parseArgs(std.io.null_writer, macro_buf.writer(), args));
460-
assert(driver.inputs.items.len == 1);
461-
const source = driver.inputs.items[0];
462-
463-
const builtin_macros = try comp.generateBuiltinMacros(.include_system_defines, null);
464-
const user_macros = try comp.addSourceFromBuffer("<command line>", macro_buf.items);
465-
466-
var pp = try aro.Preprocessor.initDefault(comp);
467-
defer pp.deinit();
468-
469-
try pp.preprocessSources(&.{ source, builtin_macros, user_macros });
470-
471-
var tree = try pp.parse();
472-
defer tree.deinit();
473-
474-
// Workaround for https://github.com/Vexu/arocc/issues/603
475-
for (comp.diagnostics.list.items) |msg| {
476-
if (msg.kind == .@"error" or msg.kind == .@"fatal error") return error.ParsingFailed;
477-
}
478-
479450
const mapper = tree.comp.string_interner.getFastTypeMapper(tree.comp.gpa) catch tree.comp.string_interner.getSlowTypeMapper();
480451
defer mapper.deinit(tree.comp.gpa);
481452

src/main.zig

+44-9
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
const std = @import("std");
2+
const assert = std.debug.assert;
23
const aro = @import("aro");
34
const Translator = @import("Translator.zig");
45

6+
// TODO cleanup and handle errors more gracefully
57
pub fn main() !void {
68
var arena_instance = std.heap.ArenaAllocator.init(std.heap.page_allocator);
79
defer arena_instance.deinit();
@@ -12,17 +14,50 @@ pub fn main() !void {
1214

1315
const args = try std.process.argsAlloc(arena);
1416

15-
var aro_comp = aro.Compilation.init(gpa, std.fs.cwd());
16-
defer aro_comp.deinit();
17+
var comp = aro.Compilation.init(gpa, std.fs.cwd());
18+
defer comp.deinit();
1719

18-
var tree = Translator.translate(gpa, &aro_comp, args) catch |err| switch (err) {
19-
error.ParsingFailed, error.FatalError => renderErrorsAndExit(&aro_comp),
20-
error.OutOfMemory => return error.OutOfMemory,
21-
error.StreamTooLong => std.zig.fatal("An input file was larger than 4GiB", .{}),
22-
};
23-
defer tree.deinit(gpa);
20+
try comp.addDefaultPragmaHandlers();
21+
comp.langopts.setEmulatedCompiler(aro.target_util.systemCompiler(comp.target));
2422

25-
const formatted = try tree.render(arena);
23+
var driver: aro.Driver = .{ .comp = &comp };
24+
defer driver.deinit();
25+
26+
var macro_buf = std.ArrayList(u8).init(gpa);
27+
defer macro_buf.deinit();
28+
29+
assert(!try driver.parseArgs(std.io.null_writer, macro_buf.writer(), args));
30+
assert(driver.inputs.items.len == 1);
31+
const source = driver.inputs.items[0];
32+
33+
const builtin_macros = try comp.generateBuiltinMacros(.include_system_defines, null);
34+
const user_macros = try comp.addSourceFromBuffer("<command line>", macro_buf.items);
35+
36+
var pp = try aro.Preprocessor.initDefault(&comp);
37+
defer pp.deinit();
38+
39+
try pp.preprocessSources(&.{ source, builtin_macros, user_macros });
40+
41+
var c_tree = try pp.parse();
42+
defer c_tree.deinit();
43+
44+
// Workaround for https://github.com/Vexu/arocc/issues/603
45+
for (comp.diagnostics.list.items) |msg| {
46+
if (msg.kind == .@"error" or msg.kind == .@"fatal error") return renderErrorsAndExit(&comp);
47+
}
48+
49+
var zig_tree = try Translator.translate(gpa, &comp, c_tree);
50+
defer zig_tree.deinit(gpa);
51+
52+
const formatted = try zig_tree.render(arena);
53+
if (driver.output_name) |path| blk: {
54+
if (std.mem.eql(u8, path, "-")) break :blk;
55+
const out_file = try std.fs.cwd().createFile(path, .{});
56+
defer out_file.close();
57+
58+
try out_file.writeAll(formatted);
59+
return std.process.cleanExit();
60+
}
2661
try std.io.getStdOut().writeAll(formatted);
2762
return std.process.cleanExit();
2863
}

0 commit comments

Comments
 (0)