diff --git a/build.zig b/build.zig index 024e2db61..1c98b2fa5 100644 --- a/build.zig +++ b/build.zig @@ -8,7 +8,22 @@ comptime { } pub fn build(b: *std.Build) !void { + // This defines all the available build options (e.g. `-D`). const config = try buildpkg.Config.init(b); + const test_filter = b.option( + []const u8, + "test-filter", + "Filter for test. Only applies to Zig tests.", + ); + + // All our steps which we'll hook up later. The steps are shown + // up here just so that they are more self-documenting. + const run_step = b.step("run", "Run the app"); + const test_step = b.step("test", "Run all tests"); + const translations_step = b.step( + "update-translations", + "Update translation files", + ); // Ghostty resources like terminfo, shell integration, themes, etc. const resources = try buildpkg.GhosttyResources.init(b, &config); @@ -131,7 +146,6 @@ pub fn build(b: *std.Build) !void { b.getInstallPath(.prefix, "share/ghostty"), ); - const run_step = b.step("run", "Run the app"); run_step.dependOn(&run_cmd.step); break :run; } @@ -157,16 +171,18 @@ pub fn build(b: *std.Build) !void { }, ); - const run_step = b.step("run", "Run the app"); + // Run uses the native macOS app run_step.dependOn(&macos_app_native_only.open.step); + + // If we have no test filters, install the tests too + if (test_filter == null) { + macos_app_native_only.addTestStepDependencies(test_step); + } } } // Tests { - const test_step = b.step("test", "Run all tests"); - const test_filter = b.option([]const u8, "test-filter", "Filter for test"); - const test_exe = b.addTest(.{ .name = "ghostty-test", .filters = if (test_filter) |v| &.{v} else &.{}, @@ -180,18 +196,13 @@ pub fn build(b: *std.Build) !void { }), }); - { - if (config.emit_test_exe) b.installArtifact(test_exe); - _ = try deps.add(test_exe); - const test_run = b.addRunArtifact(test_exe); - test_step.dependOn(&test_run.step); - } + if (config.emit_test_exe) b.installArtifact(test_exe); + _ = try deps.add(test_exe); + const test_run = b.addRunArtifact(test_exe); + test_step.dependOn(&test_run.step); } // update-translations does what it sounds like and updates the "pot" // files. These should be committed to the repo. - { - const step = b.step("update-translations", "Update translation files"); - step.dependOn(i18n.update_step); - } + translations_step.dependOn(i18n.update_step); } diff --git a/macos/Ghostty.xcodeproj/project.pbxproj b/macos/Ghostty.xcodeproj/project.pbxproj index f7ae5f525..0c54ba693 100644 --- a/macos/Ghostty.xcodeproj/project.pbxproj +++ b/macos/Ghostty.xcodeproj/project.pbxproj @@ -303,7 +303,7 @@ /* End PBXFileReference section */ /* Begin PBXFileSystemSynchronizedRootGroup section */ - A54F45F42E1F047A0046BD5C /* GhosttyTests */ = {isa = PBXFileSystemSynchronizedRootGroup; explicitFileTypes = {}; explicitFolders = (); path = GhosttyTests; sourceTree = ""; }; + A54F45F42E1F047A0046BD5C /* Tests */ = {isa = PBXFileSystemSynchronizedRootGroup; explicitFileTypes = {}; explicitFolders = (); path = Tests; sourceTree = ""; }; /* End PBXFileSystemSynchronizedRootGroup section */ /* Begin PBXFrameworksBuildPhase section */ @@ -612,7 +612,7 @@ A51BFC282B30F26D00E92F16 /* GhosttyDebug.entitlements */, 3B39CAA42B33949B00DABEB8 /* GhosttyReleaseLocal.entitlements */, A54CD6ED299BEB14008C95BB /* Sources */, - A54F45F42E1F047A0046BD5C /* GhosttyTests */, + A54F45F42E1F047A0046BD5C /* Tests */, A5D495A3299BECBA00DD1313 /* Frameworks */, A5A1F8862A489D7400D1E8BC /* Resources */, A5B30532299BEAAA0047F10C /* Products */, @@ -712,7 +712,7 @@ A54F45F82E1F047A0046BD5C /* PBXTargetDependency */, ); fileSystemSynchronizedGroups = ( - A54F45F42E1F047A0046BD5C /* GhosttyTests */, + A54F45F42E1F047A0046BD5C /* Tests */, ); name = GhosttyTests; packageProductDependencies = ( diff --git a/macos/GhosttyTests/BenchmarkTests.swift b/macos/Tests/BenchmarkTests.swift similarity index 100% rename from macos/GhosttyTests/BenchmarkTests.swift rename to macos/Tests/BenchmarkTests.swift diff --git a/pkg/harfbuzz/build.zig b/pkg/harfbuzz/build.zig index 3bdc30a32..424f2afed 100644 --- a/pkg/harfbuzz/build.zig +++ b/pkg/harfbuzz/build.zig @@ -15,15 +15,23 @@ pub fn build(b: *std.Build) !void { }); const macos = b.dependency("macos", .{ .target = target, .optimize = optimize }); - const module = b.addModule("harfbuzz", .{ - .root_source_file = b.path("main.zig"), - .target = target, - .optimize = optimize, - .imports = &.{ - .{ .name = "freetype", .module = freetype.module("freetype") }, - .{ .name = "macos", .module = macos.module("macos") }, - }, - }); + const module = harfbuzz: { + const module = b.addModule("harfbuzz", .{ + .root_source_file = b.path("main.zig"), + .target = target, + .optimize = optimize, + .imports = &.{ + .{ .name = "freetype", .module = freetype.module("freetype") }, + .{ .name = "macos", .module = macos.module("macos") }, + }, + }); + + const options = b.addOptions(); + options.addOption(bool, "coretext", coretext_enabled); + options.addOption(bool, "freetype", freetype_enabled); + module.addOptions("build_options", options); + break :harfbuzz module; + }; // For dynamic linking, we prefer dynamic linking and to search by // mode first. Mode first will search all paths for a dynamic library diff --git a/pkg/harfbuzz/c.zig b/pkg/harfbuzz/c.zig index 51e477ebf..49e87dce7 100644 --- a/pkg/harfbuzz/c.zig +++ b/pkg/harfbuzz/c.zig @@ -1,7 +1,8 @@ const builtin = @import("builtin"); +const build_options = @import("build_options"); pub const c = @cImport({ @cInclude("hb.h"); - @cInclude("hb-ft.h"); - if (builtin.os.tag == .macos) @cInclude("hb-coretext.h"); + if (build_options.freetype) @cInclude("hb-ft.h"); + if (build_options.coretext) @cInclude("hb-coretext.h"); }); diff --git a/src/build/GhosttyXcodebuild.zig b/src/build/GhosttyXcodebuild.zig index 7fa2d2f95..d3bda032d 100644 --- a/src/build/GhosttyXcodebuild.zig +++ b/src/build/GhosttyXcodebuild.zig @@ -12,6 +12,7 @@ const XCFramework = @import("GhosttyXCFramework.zig"); build: *std.Build.Step.Run, open: *std.Build.Step.Run, copy: *std.Build.Step.Run, +xctest: *std.Build.Step.Run, pub const Deps = struct { xcframework: *const XCFramework, @@ -33,6 +34,21 @@ pub fn init( => "Release", }; + const xc_arch: ?[]const u8 = switch (deps.xcframework.target) { + // Universal is our default target, so we don't have to + // add anything. + .universal => null, + + // Native we need to override the architecture in the Xcode + // project with the -arch flag. + .native => switch (builtin.cpu.arch) { + .aarch64 => "arm64", + .x86_64 => "x86_64", + else => @panic("unsupported macOS arch"), + }, + }; + + const env = try std.process.getEnvMap(b.allocator); const app_path = b.fmt("macos/build/{s}/Ghostty.app", .{xc_config}); // Our step to build the Ghostty macOS app. @@ -41,12 +57,13 @@ pub fn init( // we create a new empty environment. const env_map = try b.allocator.create(std.process.EnvMap); env_map.* = .init(b.allocator); + if (env.get("PATH")) |v| try env_map.put("PATH", v); - const build = RunStep.create(b, "xcodebuild"); - build.has_side_effects = true; - build.cwd = b.path("macos"); - build.env_map = env_map; - build.addArgs(&.{ + const step = RunStep.create(b, "xcodebuild"); + step.has_side_effects = true; + step.cwd = b.path("macos"); + step.env_map = env_map; + step.addArgs(&.{ "xcodebuild", "-target", "Ghostty", @@ -54,36 +71,55 @@ pub fn init( xc_config, }); - switch (deps.xcframework.target) { - // Universal is our default target, so we don't have to - // add anything. - .universal => {}, - - // Native we need to override the architecture in the Xcode - // project with the -arch flag. - .native => build.addArgs(&.{ - "-arch", - switch (builtin.cpu.arch) { - .aarch64 => "arm64", - .x86_64 => "x86_64", - else => @panic("unsupported macOS arch"), - }, - }), - } + // If we have a specific architecture, we need to pass it + // to xcodebuild. + if (xc_arch) |arch| step.addArgs(&.{ "-arch", arch }); // We need the xcframework - deps.xcframework.addStepDependencies(&build.step); + deps.xcframework.addStepDependencies(&step.step); // We also need all these resources because the xcode project // references them via symlinks. - deps.resources.addStepDependencies(&build.step); - deps.i18n.addStepDependencies(&build.step); - deps.docs.installDummy(&build.step); + deps.resources.addStepDependencies(&step.step); + deps.i18n.addStepDependencies(&step.step); + deps.docs.installDummy(&step.step); // Expect success - build.expectExitCode(0); + step.expectExitCode(0); - break :build build; + break :build step; + }; + + const xctest = xctest: { + const env_map = try b.allocator.create(std.process.EnvMap); + env_map.* = .init(b.allocator); + if (env.get("PATH")) |v| try env_map.put("PATH", v); + + const step = RunStep.create(b, "xcodebuild test"); + step.has_side_effects = true; + step.cwd = b.path("macos"); + step.env_map = env_map; + step.addArgs(&.{ + "xcodebuild", + "test", + "-scheme", + "Ghostty", + }); + if (xc_arch) |arch| step.addArgs(&.{ "-arch", arch }); + + // We need the xcframework + deps.xcframework.addStepDependencies(&step.step); + + // We also need all these resources because the xcode project + // references them via symlinks. + deps.resources.addStepDependencies(&step.step); + deps.i18n.addStepDependencies(&step.step); + deps.docs.installDummy(&step.step); + + // Expect success + step.expectExitCode(0); + + break :xctest step; }; // Our step to open the resulting Ghostty app. @@ -143,6 +179,7 @@ pub fn init( .build = build, .open = open, .copy = copy, + .xctest = xctest, }; } @@ -155,3 +192,10 @@ pub fn installXcframework(self: *const Ghostty) void { const b = self.build.step.owner; b.getInstallStep().dependOn(&self.build.step); } + +pub fn addTestStepDependencies( + self: *const Ghostty, + other_step: *std.Build.Step, +) void { + other_step.dependOn(&self.xctest.step); +} diff --git a/src/build/SharedDeps.zig b/src/build/SharedDeps.zig index b6e9900e2..ea7e696ef 100644 --- a/src/build/SharedDeps.zig +++ b/src/build/SharedDeps.zig @@ -139,7 +139,7 @@ pub fn add( if (b.lazyDependency("harfbuzz", .{ .target = target, .optimize = optimize, - .@"enable-freetype" = true, + .@"enable-freetype" = self.config.font_backend.hasFreetype(), .@"enable-coretext" = self.config.font_backend.hasCoretext(), })) |harfbuzz_dep| { step.root_module.addImport(