Skip to content

Commit

Permalink
build: multiversion build v2
Browse files Browse the repository at this point in the history
This moves multiversion from release.zig to build.zig, so that you can
just build a multiversion binary normally, eg:

```
$ ./zig/zig build -Dmultiversion=0.15.6 -Dconfig-release=0.15.7 -Dconfig-release-client-min=0.15.6 -Dtarget=x86_64-linux
$ ./tigerbeetle multiversion ./tigerbeetle
multiversioning.exe_path=./tigerbeetle
multiversioning.absolute_exe_path=/home/matklad/p/tb/work/tigerbeetle
multiversioning.releases_bundled={ 0.15.3, 0.15.4, 0.15.5, 0.15.6, 0.15.7 }
...
```

You can pass `-Dmultiversion=latest` to fetch the latest release from
GitHub.

Note that `-Dtarget=aarch64-macos` will build a _universal_ macos image
(this _is_ confusing flag name, but I don't see a  simple non-ambigious
solution here).

As a bonus, multiversion build now should work on any host, and not on
x86_64 linux only.

Implementation wise, the bulk of work happens in
`build_multiversion.zig` program, which is invoked from build.zig as a
custom Run step. Why not encode all that logic direcctly into build.zig?

The main reason is adrewrk, the most famous Zig troll. His favorite way
of trolling Zig developers is by regularly breaking the API of build.zig:

ziglang/zig#14498

The next planned trolling is to separate "configure" and "make" phases
of `build.zig` into physically separate processes, and remove support
for custom steps:

ziglang/zig#20981

In this PR, we are going to troll adrewrk _back_ by fixing our code even
before it gets broken, and by making our custom step a Run step instead.

That being said, it I have split `gh release download` into a separate
step, so that is properly cached.
  • Loading branch information
matklad committed Aug 23, 2024
1 parent c017811 commit 2db71da
Show file tree
Hide file tree
Showing 4 changed files with 1,015 additions and 794 deletions.
174 changes: 156 additions & 18 deletions build.zig
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,11 @@ pub fn build(b: *std.Build) !void {
// Build options passed with `-D` flags.
const build_options = .{
.target = b.option([]const u8, "target", "The CPU architecture and OS to build for"),
.multiversion = b.option(
[]const u8,
"multiversion",
"Past version to include for upgrades",
),
.config = b.option(config.ConfigBase, "config", "Base configuration.") orelse .default,
.config_aof_record = b.option(
bool,
Expand Down Expand Up @@ -174,10 +179,13 @@ pub fn build(b: *std.Build) !void {
}, .{
.vsr_module = vsr_module,
.vsr_options = vsr_options,
.target_requested = build_options.target orelse
@tagName(builtin.target.cpu.arch) ++ "-" ++ @tagName(builtin.target.os.tag),
.target = target,
.mode = mode,
.emit_llvm_ir = build_options.emit_llvm_ir,
.tracer_backend = build_options.tracer_backend,
.emit_llvm_ir = build_options.emit_llvm_ir,
.multiversion = build_options.multiversion,
});

// zig build aof
Expand Down Expand Up @@ -362,12 +370,110 @@ fn build_tigerbeetle(
options: struct {
vsr_module: *std.Build.Module,
vsr_options: *std.Build.Step.Options,
target_requested: []const u8,
target: std.Build.ResolvedTarget,
mode: std.builtin.OptimizeMode,
tracer_backend: config.TracerBackend,
multiversion: ?[]const u8,
emit_llvm_ir: bool,
},
) void {
const out_filename = if (options.target.result.os.tag == .windows)
"tigerbeetle.exe"
else
"tigerbeetle";
const tigerbeetle_bin = if (options.multiversion) |version| bin: {
assert(!options.emit_llvm_ir);
const build_mutliversion_exe = b.addExecutable(.{
.name = "build_mutliversion",
.root_source_file = b.path("src/build_multiversion.zig"),
.target = resolve_target(b, null) catch @panic("unsupported host"),
});
// Ideally, we should pass `vsr_options` here at runtime. Making them comptime
// parameters is inelegant, but practical!
build_mutliversion_exe.root_module.addOptions("vsr_options", options.vsr_options);

const build_mutliversion = b.addRunArtifact(build_mutliversion_exe);
if (options.target.result.os.tag == .macos) {
build_mutliversion.addArg("--target=macos");
inline for (.{ "x86_64", "aarch64" }, .{ "x86-64", "aarch64" }) |arch, flag| {
build_mutliversion.addPrefixedFileArg(
"--tigerbeetle-current-" ++ flag ++ "=",
build_tigerbeetle_executable(b, .{
.vsr_module = options.vsr_module,
.vsr_options = options.vsr_options,
.target = resolve_target(b, arch ++ "-macos") catch unreachable,
.mode = options.mode,
.tracer_backend = options.tracer_backend,
}).getEmittedBin(),
);
}
} else {
build_mutliversion.addArg(b.fmt(
"--target={s}",
.{options.target_requested},
));
build_mutliversion.addPrefixedFileArg(
"--tigerbeetle-current=",
build_tigerbeetle_executable(b, .{
.vsr_module = options.vsr_module,
.vsr_options = options.vsr_options,
.target = options.target,
.mode = options.mode,
.tracer_backend = options.tracer_backend,
}).getEmittedBin(),
);
}

if (options.mode == .Debug) {
build_mutliversion.addArg("--debug");
}

build_mutliversion.addPrefixedFileArg(
"--tigerbeetle-past=",
download_release(b, version, options.target, options.mode),
);
build_mutliversion.addArg(b.fmt(
"--tmp={s}",
.{b.cache_root.join(b.allocator, &.{"tmp"}) catch @panic("OOM")},
));
break :bin build_mutliversion.addPrefixedOutputFileArg("--output=", out_filename);
} else bin: {
const tigerbeetle_exe = build_tigerbeetle_executable(b, .{
.vsr_module = options.vsr_module,
.vsr_options = options.vsr_options,
.target = options.target,
.mode = options.mode,
.tracer_backend = options.tracer_backend,
});
if (options.emit_llvm_ir) {
steps.install.dependOn(&b.addInstallBinFile(
tigerbeetle_exe.getEmittedLlvmIr(),
"tigerbeetle.ll",
).step);
}
break :bin tigerbeetle_exe.getEmittedBin();
};

// "zig build install" moves the server executable to the root folder:
steps.install.dependOn(&b.addInstallFile(
tigerbeetle_bin,
b.pathJoin(&.{ "../", out_filename }),
).step);

const run_cmd = std.Build.Step.Run.create(b, b.fmt("run tigerbeetle", .{}));
run_cmd.addFileArg(tigerbeetle_bin);
if (b.args) |args| run_cmd.addArgs(args);
steps.run.dependOn(&run_cmd.step);
}

fn build_tigerbeetle_executable(b: *std.Build, options: struct {
vsr_module: *std.Build.Module,
vsr_options: *std.Build.Step.Options,
target: std.Build.ResolvedTarget,
mode: std.builtin.OptimizeMode,
tracer_backend: config.TracerBackend,
}) *std.Build.Step.Compile {
const tigerbeetle = b.addExecutable(.{
.name = "tigerbeetle",
.root_source_file = b.path("src/tigerbeetle/main.zig"),
Expand All @@ -381,23 +487,7 @@ fn build_tigerbeetle(
}
// Ensure that we get stack traces even in release builds.
tigerbeetle.root_module.omit_frame_pointer = false;
if (options.emit_llvm_ir) {
steps.install.dependOn(&b.addInstallBinFile(
tigerbeetle.getEmittedLlvmIr(),
"tigerbeetle.ll",
).step);
}

// "zig build install" moves the server executable to the root folder:
steps.install.dependOn(&b.addInstallArtifact(tigerbeetle, .{}).step);
steps.install.dependOn(&b.addInstallFile(
tigerbeetle.getEmittedBin(),
b.pathJoin(&.{ "../", tigerbeetle.out_filename }),
).step);

const run_cmd = b.addRunArtifact(tigerbeetle);
if (b.args) |args| run_cmd.addArgs(args);
steps.run.dependOn(&run_cmd.step);
return tigerbeetle;
}

fn build_aof(
Expand Down Expand Up @@ -1323,3 +1413,51 @@ const Generated = struct {
return result;
}
};

fn download_release(
b: *std.Build,
version_or_latest: []const u8,
target: std.Build.ResolvedTarget,
mode: std.builtin.OptimizeMode,
) std.Build.LazyPath {
const os = switch (target.result.os.tag) {
.windows => "windows",
.linux => "linux",
.macos => "macos",
else => @panic("unsupported OS"),
};
const arch = if (target.result.os.tag == .macos)
"universal"
else switch (target.result.cpu.arch) {
.x86_64 => "x86_64",
.aarch64 => "aarch64",
else => @panic("unsupported CPU"),
};
const debug = switch (mode) {
.ReleaseSafe => "",
.Debug => "-debug",
else => @panic("unsupported mode"),
};

const version = if (std.mem.eql(u8, version_or_latest, "latest"))
std.mem.trimRight(
u8,
b.run(&.{ "gh", "release", "view", "--json", "tagName", "--jq", ".tagName" }),
"\n",
)
else
version_or_latest;

const release_archive = b.addSystemCommand(&.{
"gh", "release",
"download", version,
"--pattern", b.fmt("tigerbeetle-{s}-{s}{s}.zip", .{ arch, os, debug }),
"--output", "-",
});
release_archive.max_stdio_size = 512 * 1024 * 1024;

const unzip = b.addSystemCommand(&.{ "unzip", "-p" });
unzip.addFileArg(release_archive.captureStdOut());
unzip.max_stdio_size = 512 * 1024 * 1024;
return unzip.captureStdOut();
}
Loading

0 comments on commit 2db71da

Please sign in to comment.