build: ghostty lib, framework, build into app

This commit is contained in:
Mitchell Hashimoto
2023-02-13 22:20:33 -08:00
parent 81299fde9c
commit 8035865596
8 changed files with 153 additions and 6 deletions

View File

@ -22,6 +22,7 @@ const tracylib = @import("pkg/tracy/build.zig");
const system_sdk = @import("vendor/mach/libs/glfw/system_sdk.zig");
const WasmTarget = @import("src/os/wasm/target.zig").Target;
const SwiftBuildStep = @import("src/build/SwiftBuildStep.zig");
const XCFrameworkStep = @import("src/build/XCFrameworkStep.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,
@ -132,17 +133,41 @@ pub fn build(b: *std.build.Builder) !void {
b.installFile("dist/macos/Ghostty.icns", "Ghostty.app/Contents/Resources/Ghostty.icns");
}
// Mac App based on Swift
{
// c lib
const static_lib = lib: {
const static_lib = b.addStaticLibrary("ghostty", "src/main_c.zig");
static_lib.setBuildMode(mode);
static_lib.setTarget(target);
static_lib.install();
static_lib.linkLibC();
b.default_step.dependOn(&static_lib.step);
break :lib static_lib;
};
// On Mac we can build the app.
const macapp = b.step("macapp", "Build macOS app");
if (builtin.target.isDarwin()) {
// The xcframework wraps our ghostty library so that we can link
// it to the final app built with Swift.
const xcframework = XCFrameworkStep.create(b, .{
.name = "GhosttyKit",
.out_path = "macos/GhosttyKit.xcframework",
.library = .{ .generated = &static_lib.output_lib_path_source },
.headers = .{ .path = "include" },
});
xcframework.step.dependOn(&static_lib.step);
macapp.dependOn(&xcframework.step);
// Build our swift app
const swift_build = SwiftBuildStep.create(b, .{
.product = "Ghostty",
.target = target,
.optimize = mode,
.cwd = .{ .path = "macos" },
});
const macapp = b.step("macapp", "Build macOS app");
macapp.dependOn(&swift_build.step);
// Build our app bundle
macapp.dependOn(&b.addInstallFileWithDir(
.{ .generated = &swift_build.bin_path },
.prefix,

16
include/ghostty.h Normal file
View File

@ -0,0 +1,16 @@
#ifndef GHOSTTY_H
#define GHOSTTY_H
#ifdef __cplusplus
extern "C" {
#endif
#include <stdint.h>
uint64_t ghostty_hello(void);
#ifdef __cplusplus
}
#endif
#endif /* GHOSTTY_H */

4
include/module.modulemap Normal file
View File

@ -0,0 +1,4 @@
module GhosttyKit {
umbrella header "ghostty.h"
export *
}

1
macos/.gitignore vendored
View File

@ -2,6 +2,7 @@
/.build
/Packages
/*.xcodeproj
/*.xcframework
xcuserdata/
DerivedData/
.swiftpm/config/registries.json

View File

@ -10,10 +10,18 @@ let package = Package(
// SwiftUI
.macOS(.v11),
],
products: [
.executable(
name: "Ghostty",
targets: ["Ghostty"]),
],
dependencies: [],
targets: [
.executableTarget(
name: "Ghostty",
dependencies: []),
dependencies: ["GhosttyKit"]),
.binaryTarget(
name: "GhosttyKit",
path: "GhosttyKit.xcframework"),
]
)

View File

@ -1,10 +1,14 @@
import SwiftUI
import GhosttyKit
@main
struct MyApp: App {
@State private var num = ghostty_hello()
var body: some Scene {
WindowGroup {
ContentView()
Text(String(num))
.font(.largeTitle)
}
}
}

View File

@ -0,0 +1,78 @@
//! A zig builder step that runs "swift build" in the context of
//! a Swift project managed with SwiftPM. This is primarily meant to build
//! executables currently since that is what we build.
const XCFrameworkStep = @This();
const std = @import("std");
const Step = std.build.Step;
const GeneratedFile = std.build.GeneratedFile;
pub const Options = struct {
/// The name of the xcframework to create.
name: []const u8,
/// The path to write the framework
out_path: []const u8,
/// Library file (dylib, a) to package.
library: std.build.FileSource,
/// Path to a directory with the headers.
headers: std.build.FileSource,
};
step: Step,
builder: *std.build.Builder,
/// See Options
name: []const u8,
out_path: []const u8,
library: std.build.FileSource,
headers: std.build.FileSource,
pub fn create(builder: *std.build.Builder, opts: Options) *XCFrameworkStep {
const self = builder.allocator.create(XCFrameworkStep) catch @panic("OOM");
self.* = .{
.step = Step.init(.custom, builder.fmt(
"xcframework {s}",
.{opts.name},
), builder.allocator, make),
.builder = builder,
.name = opts.name,
.out_path = opts.out_path,
.library = opts.library,
.headers = opts.headers,
};
return self;
}
fn make(step: *Step) !void {
const self = @fieldParentPtr(XCFrameworkStep, "step", step);
// TODO: use the zig cache system when it is in the stdlib
// https://github.com/ziglang/zig/pull/14571
const output_path = self.out_path;
// We use a RunStep here to ease our configuration.
{
const run = std.build.RunStep.create(self.builder, self.builder.fmt(
"xcframework delete {s}",
.{self.name},
));
run.addArgs(&.{ "rm", "-rf", output_path });
try run.step.make();
}
{
const run = std.build.RunStep.create(self.builder, self.builder.fmt(
"xcframework {s}",
.{self.name},
));
run.addArgs(&.{
"xcodebuild", "-create-xcframework",
"-library", self.library.getPath(self.builder),
"-headers", self.headers.getPath(self.builder),
"-output", output_path,
});
try run.step.make();
}
}

11
src/main_c.zig Normal file
View File

@ -0,0 +1,11 @@
// This is the main file for the C API. The C API is used to embed Ghostty
// within other applications. Depending on the build settings some APIs
// may not be available (i.e. embedding into macOS exposes various Metal
// support).
const std = @import("std");
const builtin = @import("builtin");
// We're just testing right now.
export fn ghostty_hello() u64 {
return 42;
}