-
-
Save novusnota/7dfe509ba52f45d0a328d9a8dc855389 to your computer and use it in GitHub Desktop.
Building with Zig and system LLVM
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const std = @import("std"); | |
const Build = std.Build; | |
pub fn build(b: *Build) void { | |
const target = b.standardTargetOptions(.{}); | |
const optimize = b.standardOptimizeOption(.{}); | |
// Instead of using the classic b.addExecutable, we use a custom rule | |
// to compile with Zig, then optimize with LLVM. | |
const exe = addLLvmExecutable(b, .{ | |
.name = "my_project", | |
.root_source_file = .{ .path = "src/main.zig" }, | |
.target = target, | |
.optimize = optimize, | |
}); | |
b.installArtifact(exe); | |
const run_cmd = b.addRunArtifact(exe); | |
run_cmd.step.dependOn(b.getInstallStep()); | |
if (b.args) |args| { | |
run_cmd.addArgs(args); | |
} | |
const run_step = b.step("run", "Run the app"); | |
run_step.dependOn(&run_cmd.step); | |
const unit_tests = b.addTest(.{ | |
.root_source_file = .{ .path = "src/main.zig" }, | |
.target = target, | |
.optimize = optimize, | |
}); | |
const run_unit_tests = b.addRunArtifact(unit_tests); | |
const test_step = b.step("test", "Run unit tests"); | |
test_step.dependOn(&run_unit_tests.step); | |
} | |
pub fn addLLvmExecutable(b: *Build, options: Build.ExecutableOptions) *Build.Step.Compile { | |
var obj_options = .{ | |
.name = options.name, | |
.root_source_file = options.root_source_file, | |
.target = options.target, | |
.optimize = options.optimize, | |
.max_rss = options.max_rss, | |
.link_libc = options.link_libc, | |
.single_threaded = options.single_threaded, | |
.use_llvm = options.use_llvm, | |
.use_lld = options.use_lld, | |
.zig_lib_dir = options.zig_lib_dir, | |
.main_pkg_path = options.main_pkg_path, | |
}; | |
const obj = b.addObject(obj_options); | |
// Use LLVM to optimize our object | |
var opt_obj = addLlvmOptArtifactStep(b, obj); | |
// Call Zig again to link the optimized object. | |
var exe_options= options; | |
exe_options.root_source_file = null; | |
var compile = b.addExecutable(exe_options); | |
compile.addObjectFile(opt_obj.obj); | |
compile.step.dependOn(&opt_obj.cmd.step); | |
return compile; | |
} | |
// TODO: this pattern seems convenient, why isn't the run step keeping a pointer to the outfile ? | |
const Cmd = struct { | |
cmd: *Build.Step.Run, | |
obj: Build.LazyPath, | |
}; | |
pub fn addLlvmOptArtifactStep(b: *Build, obj: *Build.Step.Compile) Cmd { | |
// Even though we don't directly use the unoptimized object file, | |
// we need to mark this dependency otherwise the obj step isn't run at all. | |
// We then have to remove the object. | |
// TODO: allow Zig to only generate the LLVM Bitcode without the object. | |
var rm = b.addSystemCommand(&[_][]const u8{"rm", "-f", "--preserve-root"}); | |
rm.addFileArg(obj.getEmittedBin()); | |
rm.step.dependOn(&obj.step); | |
var llc = b.addSystemCommand( | |
&[_][]const u8{"llc", "-O2", "-filetype=obj"} | |
); | |
llc.addFileArg(obj.getEmittedLlvmBc()); | |
llc.addArg("-o"); | |
llc.step.dependOn(&rm.step); | |
const obj_name = std.mem.concat(b.allocator, u8, &[_][]const u8{ obj.name, ".opt.o" }) catch @panic("OOM"); | |
const obj_path = llc.addOutputFileArg(obj_name); | |
return .{.cmd = llc, .obj = obj_path}; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment