From 0907da4ebab8ef9dfa386e88bca8130869934b29 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Sat, 4 Mar 2023 20:34:15 -0800 Subject: [PATCH] build: generate a version number, show in log on startup --- build.zig | 30 +++++++++++++++++++++++- src/build/Version.zig | 53 +++++++++++++++++++++++++++++++++++++++++++ src/build_config.zig | 4 ++++ src/main.zig | 1 + 4 files changed, 87 insertions(+), 1 deletion(-) create mode 100644 src/build/Version.zig diff --git a/build.zig b/build.zig index 72d60c2d1..4698281f0 100644 --- a/build.zig +++ b/build.zig @@ -26,6 +26,7 @@ const WasmTarget = @import("src/os/wasm/target.zig").Target; const LibtoolStep = @import("src/build/LibtoolStep.zig"); const LipoStep = @import("src/build/LipoStep.zig"); const XCFrameworkStep = @import("src/build/XCFrameworkStep.zig"); +const Version = @import("src/build/Version.zig"); // Do a comptime Zig version requirement. The required Zig version is // somewhat arbitrary: it is meant to be a version that we feel works well, @@ -43,7 +44,10 @@ comptime { } } -// Build options, see the build options help for more info. +/// The version of the next release. +const app_version = std.builtin.Version{ .major = 0, .minor = 1, .patch = 0 }; + +/// Build options, see the build options help for more info. var tracy: bool = false; var flatpak: bool = false; var app_runtime: apprt.Runtime = .none; @@ -112,6 +116,28 @@ pub fn build(b: *std.build.Builder) !void { "Build and install the benchmark executables.", ) orelse false; + var version_string = b.option( + []const u8, + "version-string", + "A specific version string to use for the build. " ++ + "If not specified, git will be used. This must be a semantic version.", + ); + + var version: std.SemanticVersion = if (version_string) |v| + try std.SemanticVersion.parse(v) + else version: { + const vsn = try Version.detect(b); + if (vsn.tag != null) @panic("tagged releases are not yet supported"); + + break :version .{ + .major = app_version.major, + .minor = app_version.minor, + .patch = app_version.patch, + .pre = vsn.branch, + .build = vsn.short_hash, + }; + }; + // We can use wasmtime to test wasm b.enable_wasmtime = true; @@ -125,6 +151,8 @@ pub fn build(b: *std.build.Builder) !void { .optimize = optimize, }); const exe_options = b.addOptions(); + exe_options.addOption(std.SemanticVersion, "app_version", version); + exe_options.addOption([]const u8, "app_version_string", b.fmt("{}", .{version})); exe_options.addOption(bool, "tracy_enabled", tracy); exe_options.addOption(bool, "flatpak", flatpak); exe_options.addOption(apprt.Runtime, "app_runtime", app_runtime); diff --git a/src/build/Version.zig b/src/build/Version.zig new file mode 100644 index 000000000..6663c3f73 --- /dev/null +++ b/src/build/Version.zig @@ -0,0 +1,53 @@ +const Version = @This(); + +const std = @import("std"); + +/// The short hash (7 characters) of the latest commit. +short_hash: []const u8, + +/// True if there was a diff at build time. +changes: bool, + +/// The tag -- if any -- that this commit is a part of. +tag: ?[]const u8, + +/// The branch that was checked out at the time of the build. +branch: []const u8, + +/// Initialize the version and detect it from the Git environment. This +/// allocates using the build allocator and doesn't free. +pub fn detect(b: *std.Build) !Version { + // Execute a bunch of git commands to determine the automatic version. + var code: u8 = 0; + const branch = try b.execAllowFail(&[_][]const u8{ "git", "-C", b.build_root.path orelse ".", "rev-parse", "--abbrev-ref", "HEAD" }, &code, .Ignore); + + const short_hash = short_hash: { + const output = try b.execAllowFail(&[_][]const u8{ "git", "-C", b.build_root.path orelse ".", "log", "--pretty=format:%h", "-n", "1" }, &code, .Ignore); + break :short_hash std.mem.trimRight(u8, output, "\r\n "); + }; + + const tag = b.execAllowFail(&[_][]const u8{ "git", "-C", b.build_root.path orelse ".", "describe", "--exact-match", "--tags" }, &code, .Ignore) catch |err| switch (err) { + error.ExitCodeFailure => "", // expected + else => return err, + }; + + _ = b.execAllowFail(&[_][]const u8{ + "git", + "-C", + b.build_root.path orelse ".", + "diff", + "--quiet", + "--exit-code", + }, &code, .Ignore) catch |err| switch (err) { + error.ExitCodeFailure => {}, // expected + else => return err, + }; + const changes = code != 0; + + return .{ + .short_hash = short_hash, + .changes = changes, + .tag = if (tag.len > 0) std.mem.trimRight(u8, tag, "\r\n ") else null, + .branch = std.mem.trimRight(u8, branch, "\r\n "), + }; +} diff --git a/src/build_config.zig b/src/build_config.zig index b3f072eab..abe3daa8a 100644 --- a/src/build_config.zig +++ b/src/build_config.zig @@ -9,6 +9,10 @@ const assert = std.debug.assert; const apprt = @import("apprt.zig"); const font = @import("font/main.zig"); +/// The semantic version of this build. +pub const version = options.app_version; +pub const version_string = options.app_version_string; + /// The artifact we're producing. This can be used to determine if we're /// building a standalone exe, an embedded lib, etc. pub const artifact = Artifact.detect(); diff --git a/src/main.zig b/src/main.zig index 5242e0339..abd9dfdef 100644 --- a/src/main.zig +++ b/src/main.zig @@ -128,6 +128,7 @@ pub const GlobalState = struct { pub fn init(self: *GlobalState) void { // Output some debug information right away + std.log.info("ghostty version={s}", .{build_config.version_string}); std.log.info("runtime={}", .{build_config.app_runtime}); std.log.info("font_backend={}", .{build_config.font_backend}); std.log.info("dependency harfbuzz={s}", .{harfbuzz.versionString()});