build: make zig build run on macOS work with an empty zig-out

This commit is contained in:
Mitchell Hashimoto
2025-07-05 07:22:35 -07:00
parent 5fc0bbc58f
commit 8b44d0b3bb
5 changed files with 81 additions and 23 deletions

View File

@ -29,15 +29,7 @@ pub fn build(b: *std.Build) !void {
// If we aren't emitting docs we need to emit a placeholder so // If we aren't emitting docs we need to emit a placeholder so
// our macOS xcodeproject builds since it expects the `share/man` // our macOS xcodeproject builds since it expects the `share/man`
// directory to exist to copy into the app bundle. // directory to exist to copy into the app bundle.
var wf = b.addWriteFiles(); docs.installDummy(b.getInstallStep());
const path = "share/man/.placeholder";
b.getInstallStep().dependOn(&b.addInstallFile(
wf.add(
path,
"emit-docs not true so no man pages",
),
path,
).step);
} }
// Ghostty webdata // Ghostty webdata
@ -155,6 +147,12 @@ pub fn build(b: *std.Build) !void {
&xcframework_native, &xcframework_native,
); );
// The app depends on the resources and i18n to be in the
// install directory too.
resources.addStepDependencies(&macos_app_native_only.build.step);
i18n.addStepDependencies(&macos_app_native_only.build.step);
docs.installDummy(&macos_app_native_only.build.step);
const run_step = b.step("run", "Run the app"); const run_step = b.step("run", "Run the app");
run_step.dependOn(&macos_app_native_only.open.step); run_step.dependOn(&macos_app_native_only.open.step);
} }

View File

@ -93,5 +93,32 @@ pub fn init(
pub fn install(self: *const GhosttyDocs) void { pub fn install(self: *const GhosttyDocs) void {
const b = self.steps[0].owner; const b = self.steps[0].owner;
for (self.steps) |step| b.getInstallStep().dependOn(step); self.addStepDependencies(b.getInstallStep());
}
pub fn addStepDependencies(
self: *const GhosttyDocs,
other_step: *std.Build.Step,
) void {
for (self.steps) |step| other_step.dependOn(step);
}
/// Installs some dummy files to satisfy the folder structure of docs
/// without actually generating any documentation. This is useful
/// when the `emit-docs` option is not set to true, but we still
/// need the rough directory structure to exist, such as for the macOS
/// app.
pub fn installDummy(self: *const GhosttyDocs, step: *std.Build.Step) void {
_ = self;
const b = step.owner;
var wf = b.addWriteFiles();
const path = "share/man/.placeholder";
step.dependOn(&b.addInstallFile(
wf.add(
path,
"emit-docs not true so no man pages",
),
path,
).step);
} }

View File

@ -50,7 +50,14 @@ pub fn init(b: *std.Build, cfg: *const Config) !GhosttyI18n {
} }
pub fn install(self: *const GhosttyI18n) void { pub fn install(self: *const GhosttyI18n) void {
for (self.steps) |step| self.owner.getInstallStep().dependOn(step); self.addStepDependencies(self.owner.getInstallStep());
}
pub fn addStepDependencies(
self: *const GhosttyI18n,
other_step: *std.Build.Step,
) void {
for (self.steps) |step| other_step.dependOn(step);
} }
fn createUpdateStep(b: *std.Build) !*std.Build.Step { fn createUpdateStep(b: *std.Build) !*std.Build.Step {

View File

@ -397,5 +397,12 @@ fn addLinuxAppResources(
pub fn install(self: *const GhosttyResources) void { pub fn install(self: *const GhosttyResources) void {
const b = self.steps[0].owner; const b = self.steps[0].owner;
for (self.steps) |step| b.getInstallStep().dependOn(step); self.addStepDependencies(b.getInstallStep());
}
pub fn addStepDependencies(
self: *const GhosttyResources,
other_step: *std.Build.Step,
) void {
for (self.steps) |step| other_step.dependOn(step);
} }

View File

@ -6,8 +6,9 @@ const RunStep = std.Build.Step.Run;
const Config = @import("Config.zig"); const Config = @import("Config.zig");
const XCFramework = @import("GhosttyXCFramework.zig"); const XCFramework = @import("GhosttyXCFramework.zig");
xcodebuild: *std.Build.Step.Run, build: *std.Build.Step.Run,
open: *std.Build.Step.Run, open: *std.Build.Step.Run,
copy: *std.Build.Step.Run,
pub fn init( pub fn init(
b: *std.Build, b: *std.Build,
@ -22,6 +23,8 @@ pub fn init(
=> "Release", => "Release",
}; };
const app_path = b.fmt("macos/build/{s}/Ghostty.app", .{xc_config});
// Our step to build the Ghostty macOS app. // Our step to build the Ghostty macOS app.
const build = build: { const build = build: {
// External environment variables can mess up xcodebuild, so // External environment variables can mess up xcodebuild, so
@ -74,13 +77,11 @@ pub fn init(
const open = open: { const open = open: {
const open = RunStep.create(b, "run Ghostty app"); const open = RunStep.create(b, "run Ghostty app");
open.has_side_effects = true; open.has_side_effects = true;
open.cwd = b.path("macos"); open.cwd = b.path("");
open.addArgs(&.{ open.addArgs(&.{b.fmt(
b.fmt( "{s}/Contents/MacOS/ghostty",
"build/{s}/Ghostty.app/Contents/MacOS/ghostty", .{app_path},
.{xc_config}, )});
),
});
// Open depends on the app // Open depends on the app
open.step.dependOn(&build.step); open.step.dependOn(&build.step);
@ -105,13 +106,31 @@ pub fn init(
break :open open; break :open open;
}; };
// Our step to copy the app bundle to the install path.
// We have to use `cp -R` because there are symlinks in the
// bundle.
const copy = copy: {
const step = RunStep.create(b, "copy app bundle");
step.addArgs(&.{ "cp", "-R" });
step.addFileArg(b.path(app_path));
step.addArg(b.fmt("{s}", .{b.install_path}));
step.step.dependOn(&build.step);
break :copy step;
};
return .{ return .{
.xcodebuild = build, .build = build,
.open = open, .open = open,
.copy = copy,
}; };
} }
pub fn install(self: *const Ghostty) void { pub fn install(self: *const Ghostty) void {
const b = self.xcodebuild.step.owner; const b = self.copy.step.owner;
b.getInstallStep().dependOn(&self.xcodebuild.step); b.getInstallStep().dependOn(&self.copy.step);
}
pub fn installXcframework(self: *const Ghostty) void {
const b = self.build.step.owner;
b.getInstallStep().dependOn(&self.build.step);
} }