build: build produces a broken object file for iOS

This gets `zig build -Dtarget=aarch64-ios` working. By "working" I mean
it produces an object file without compiler errors. However, the object
file certainly isn't useful since it uses a number of features that will
not work in the iOS sandbox.

This is just an experiment more than anything to see how hard it would be to
get libghostty working within iOS to render a terminal. Note iOS doesn't
support ptys so this wouldn't be a true on-device terminal. The
challenge right now is to just get a terminal rendering (not usable).
This commit is contained in:
Mitchell Hashimoto
2024-01-13 20:21:49 -08:00
parent 7a4c63522b
commit 3360a008cd
29 changed files with 228 additions and 46 deletions

101
build.zig
View File

@ -44,14 +44,31 @@ pub fn build(b: *std.Build) !void {
// to set since header files will use this to determine the availability
// of certain APIs and I believe it is also encoded in the Mach-O
// binaries.
if (result.result.os.tag == .macos and
result.query.os_version_min == null)
{
result.query.os_version_min = .{ .semver = .{
.major = 12,
.minor = 0,
.patch = 0,
} };
if (result.query.os_version_min == null) {
switch (result.result.os.tag) {
// The lowest supported version of macOS is 12.x because
// this is the first version to support Apple Silicon so it is
// the earliest version we can virtualize to test (I only have
// an Apple Silicon machine for macOS).
.macos => {
result.query.os_version_min = .{ .semver = .{
.major = 12,
.minor = 0,
.patch = 0,
} };
},
// iOS 17 picked arbitrarily
.ios => {
result.query.os_version_min = .{ .semver = .{
.major = 17,
.minor = 0,
.patch = 0,
} };
},
else => {},
}
}
break :target result;
@ -542,6 +559,65 @@ pub fn build(b: *std.Build) !void {
b.default_step.dependOn(xcframework.step);
}
// iOS
if (builtin.target.isDarwin() and target.result.os.tag == .ios) {
const ios_config: BuildConfig = config: {
var copy = config;
copy.static = true;
break :config copy;
};
const lib = b.addStaticLibrary(.{
.name = "ghostty",
.root_source_file = .{ .path = "src/main_c.zig" },
.target = b.resolveTargetQuery(.{
.cpu_arch = .aarch64,
.os_tag = .ios,
.os_version_min = target.query.os_version_min,
}),
.optimize = optimize,
});
lib.bundle_compiler_rt = true;
lib.linkLibC();
lib.root_module.addOptions("build_options", exe_options);
// Create a single static lib with all our dependencies merged
var lib_list = try addDeps(b, lib, ios_config);
try lib_list.append(lib.getEmittedBin());
const libtool = LibtoolStep.create(b, .{
.name = "ghostty",
.out_name = "libghostty-ios-fat.a",
.sources = lib_list.items,
});
libtool.step.dependOn(&lib.step);
b.default_step.dependOn(libtool.step);
// Add our library to zig-out
const lib_install = b.addInstallLibFile(
libtool.output,
"libghostty.a",
);
b.getInstallStep().dependOn(&lib_install.step);
// Copy our ghostty.h to include
const header_install = b.addInstallHeaderFile(
"include/ghostty.h",
"ghostty.h",
);
b.getInstallStep().dependOn(&header_install.step);
// // 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 = libtool.output,
// .headers = .{ .path = "include" },
// });
// xcframework.step.dependOn(libtool.step);
// b.default_step.dependOn(xcframework.step);
}
// wasm
{
// Build our Wasm target.
@ -830,7 +906,14 @@ fn addDeps(
// Mac Stuff
if (step.rootModuleTarget().isDarwin()) {
step.root_module.addImport("objc", objc_dep.module("objc"));
// This is a bit of a hack that should probably be fixed upstream
// in zig-objc, but we need to add the apple SDK paths to the
// zig-objc module so that it can find the objc runtime headers.
const module = objc_dep.module("objc");
module.resolved_target = step.root_module.resolved_target;
try @import("apple_sdk").addPaths(b, module);
step.root_module.addImport("objc", module);
step.root_module.addImport("macos", macos_dep.module("macos"));
step.linkLibrary(macos_dep.artifact("macos"));
try static_libs.append(macos_dep.artifact("macos").getEmittedBin());

View File

@ -5,8 +5,8 @@
.dependencies = .{
// Zig libs
.libxev = .{
.url = "https://github.com/mitchellh/libxev/archive/74bc7aea4a8f88210f0ad4215108613ab7e7af1a.tar.gz",
.hash = "122029743e5d96aa1b57a1b99ff58bf13ff9ed6d8f624ac3ae8074062feb91c5bd8d",
.url = "https://github.com/mitchellh/libxev/archive/4e6781895e4e6c477597c8c2713d36cd82b57d07.tar.gz",
.hash = "12203f87e00caa6c07c02a748f234a5c0ee2ca5c334ec464e88810d93e7b5495a56f",
},
.mach_glfw = .{
.url = "https://github.com/der-teufel-programming/mach-glfw/archive/a9aae000cdc104dabe75d829ff9dab6809e47604.tar.gz",

View File

@ -45,6 +45,7 @@ const SDK = struct {
pub fn fromTarget(target: std.Target) !SDK {
return switch (target.os.tag) {
.ios => .{ .platform = "iPhoneOS", .version = "" },
.macos => .{ .platform = "MacOSX", .version = "14" },
else => {
std.log.err("unsupported os={}", .{target.os.tag});

View File

@ -71,10 +71,12 @@ pub fn build(b: *std.Build) !void {
.file = imgui.path("backends/imgui_impl_metal.mm"),
.flags = flags.items,
});
lib.addCSourceFile(.{
.file = imgui.path("backends/imgui_impl_osx.mm"),
.flags = flags.items,
});
if (target.result.os.tag == .macos) {
lib.addCSourceFile(.{
.file = imgui.path("backends/imgui_impl_osx.mm"),
.flags = flags.items,
});
}
}
lib.installHeadersDirectoryOptions(.{

View File

@ -15,6 +15,11 @@ pub fn build(b: *std.Build) !void {
});
lib.linkLibC();
lib.addIncludePath(upstream.path("include"));
if (target.result.isDarwin()) {
const apple_sdk = @import("apple_sdk");
try apple_sdk.addPaths(b, &lib.root_module);
}
module.addIncludePath(upstream.path("include"));
module.addIncludePath(.{ .path = "" });
@ -86,7 +91,7 @@ pub fn build(b: *std.Build) !void {
b.installArtifact(lib);
{
if (target.query.isNative()) {
const test_exe = b.addTest(.{
.name = "test",
.root_source_file = .{ .path = "main.zig" },

View File

@ -1,12 +1,14 @@
.{
.name = "freetype",
.version = "2.13.2",
.paths = .{""},
.dependencies = .{
.freetype = .{
.url = "https://github.com/freetype/freetype/archive/refs/tags/VER-2-13-2.tar.gz",
.hash = "1220b81f6ecfb3fd222f76cf9106fecfa6554ab07ec7fdc4124b9bb063ae2adf969d",
},
.apple_sdk = .{ .path = "../apple-sdk" },
.libpng = .{ .path = "../libpng" },
.zlib = .{ .path = "../zlib" },
},

View File

@ -12,8 +12,15 @@ pub fn build(b: *std.Build) !void {
module.addIncludePath(upstream.path(""));
module.addIncludePath(.{ .path = "override" });
if (target.result.isDarwin()) {
// See pkg/harfbuzz/build.zig
module.resolved_target = target;
defer module.resolved_target = null;
const apple_sdk = @import("apple_sdk");
try apple_sdk.addPaths(b, module);
}
{
if (target.query.isNative()) {
const test_exe = b.addTest(.{
.name = "test",
.root_source_file = .{ .path = "main.zig" },
@ -45,6 +52,10 @@ fn buildGlslang(
lib.linkLibCpp();
lib.addIncludePath(upstream.path(""));
lib.addIncludePath(.{ .path = "override" });
if (target.result.isDarwin()) {
const apple_sdk = @import("apple_sdk");
try apple_sdk.addPaths(b, &lib.root_module);
}
var flags = std.ArrayList([]const u8).init(b.allocator);
defer flags.deinit();

View File

@ -7,5 +7,7 @@
.url = "https://github.com/KhronosGroup/glslang/archive/refs/tags/13.1.1.tar.gz",
.hash = "1220481fe19def1172cd0728743019c0f440181a6342b62d03e24d05c70141516799",
},
.apple_sdk = .{ .path = "../apple-sdk" },
},
}

View File

@ -34,6 +34,18 @@ pub fn build(b: *std.Build) !void {
lib.addIncludePath(upstream.path("src"));
module.addIncludePath(upstream.path("src"));
if (target.result.isDarwin()) {
// This is definitely super sketchy and not right but without this
// zig build test breaks on macOS. We have to look into what exactly
// is going on here but this getting comitted in the interest of
// unblocking zig build test.
module.resolved_target = target;
defer module.resolved_target = null;
try apple_sdk.addPaths(b, &lib.root_module);
try apple_sdk.addPaths(b, module);
}
const freetype_dep = b.dependency("freetype", .{ .target = target, .optimize = optimize });
lib.linkLibrary(freetype_dep.artifact("freetype"));
module.addIncludePath(freetype_dep.builder.dependency("freetype", .{}).path("include"));
@ -59,19 +71,10 @@ pub fn build(b: *std.Build) !void {
"-DHAVE_FT_DONE_MM_VAR=1",
"-DHAVE_FT_GET_TRANSFORM=1",
});
if (coretext_enabled and target.result.isDarwin()) {
// This is definitely super sketchy and not right but without this
// zig build test breaks on macOS. We have to look into what exactly
// is going on here but this getting comitted in the interest of
// unblocking zig build test.
module.resolved_target = target;
defer module.resolved_target = null;
if (coretext_enabled) {
try flags.appendSlice(&.{"-DHAVE_CORETEXT=1"});
try apple_sdk.addPaths(b, &lib.root_module);
try apple_sdk.addPaths(b, module);
lib.linkFramework("ApplicationServices");
module.linkFramework("ApplicationServices", .{});
lib.linkFramework("CoreText");
module.linkFramework("CoreText", .{});
}
lib.addCSourceFile(.{

View File

@ -1,6 +1,7 @@
.{
.name = "harfbuzz",
.version = "8.2.2",
.paths = .{""},
.dependencies = .{
.harfbuzz = .{
.url = "https://github.com/harfbuzz/harfbuzz/archive/refs/tags/8.2.2.tar.gz",

View File

@ -15,6 +15,10 @@ pub fn build(b: *std.Build) !void {
if (target.result.os.tag == .linux) {
lib.linkSystemLibrary("m");
}
if (target.result.isDarwin()) {
const apple_sdk = @import("apple_sdk");
try apple_sdk.addPaths(b, &lib.root_module);
}
const zlib_dep = b.dependency("zlib", .{ .target = target, .optimize = optimize });
lib.linkLibrary(zlib_dep.artifact("z"));

View File

@ -1,14 +1,14 @@
.{
.name = "libpng",
.version = "1.6.40",
.paths = .{""},
.dependencies = .{
.libpng = .{
.url = "https://github.com/glennrp/libpng/archive/refs/tags/v1.6.40.tar.gz",
.hash = "12203d2722e3af6f9556503b114c25fe3eead528a93f5f26eefcb187a460d1548e07",
},
.zlib = .{
.path = "../zlib",
},
.zlib = .{ .path = "../zlib" },
.apple_sdk = .{ .path = "../apple-sdk" },
},
}

View File

@ -28,14 +28,16 @@ pub fn build(b: *std.Build) !void {
.file = .{ .path = "text/ext.c" },
.flags = flags.items,
});
lib.linkFramework("Carbon");
lib.linkFramework("CoreFoundation");
lib.linkFramework("CoreGraphics");
lib.linkFramework("CoreText");
lib.linkFramework("CoreVideo");
if (target.result.os.tag == .macos) {
lib.linkFramework("Carbon");
module.linkFramework("Carbon", .{});
}
if (target.result.isDarwin()) {
module.linkFramework("Carbon", .{});
module.linkFramework("CoreFoundation", .{});
module.linkFramework("CoreGraphics", .{});
module.linkFramework("CoreText", .{});

View File

@ -12,7 +12,7 @@ pub fn build(b: *std.Build) !void {
module.addIncludePath(upstream.path("src"));
b.installArtifact(lib);
{
if (target.query.isNative()) {
const test_exe = b.addTest(.{
.name = "test",
.root_source_file = .{ .path = "main.zig" },
@ -44,6 +44,11 @@ fn buildOniguruma(
lib.linkLibC();
lib.addIncludePath(upstream.path("src"));
if (target.result.isDarwin()) {
const apple_sdk = @import("apple_sdk");
try apple_sdk.addPaths(b, &lib.root_module);
}
lib.addConfigHeader(b.addConfigHeader(.{
.style = .{ .cmake = upstream.path("src/config.h.cmake.in") },
}, .{

View File

@ -7,5 +7,7 @@
.url = "https://github.com/kkos/oniguruma/archive/refs/tags/v6.9.9.tar.gz",
.hash = "1220c15e72eadd0d9085a8af134904d9a0f5dfcbed5f606ad60edc60ebeccd9706bb",
},
.apple_sdk = .{ .path = "../apple-sdk" },
},
}

View File

@ -16,6 +16,10 @@ pub fn build(b: *std.Build) !void {
if (target.result.os.tag != .windows) {
lib.linkSystemLibrary("pthread");
}
if (target.result.isDarwin()) {
const apple_sdk = @import("apple_sdk");
try apple_sdk.addPaths(b, &lib.root_module);
}
lib.addIncludePath(upstream.path(""));
lib.addIncludePath(.{ .path = "" });
@ -68,7 +72,7 @@ pub fn build(b: *std.Build) !void {
b.installArtifact(lib);
{
if (target.query.isNative()) {
const test_exe = b.addTest(.{
.name = "test",
.root_source_file = .{ .path = "main.zig" },

View File

@ -1,10 +1,13 @@
.{
.name = "pixman",
.version = "0.42.2",
.paths = .{""},
.dependencies = .{
.pixman = .{
.url = "https://deps.files.ghostty.dev/pixman-pixman-0.42.2.tar.gz",
.hash = "12209b9206f9a5d31ccd9a2312cc72cb9dfc3e034aee1883c549dc1d753fae457230",
},
.apple_sdk = .{ .path = "../apple-sdk" },
},
}

View File

@ -12,7 +12,7 @@ pub fn build(b: *std.Build) !void {
const lib = try buildSpirvCross(b, upstream, target, optimize);
b.installArtifact(lib);
{
if (target.query.isNative()) {
const test_exe = b.addTest(.{
.name = "test",
.root_source_file = .{ .path = "main.zig" },
@ -42,8 +42,10 @@ fn buildSpirvCross(
});
lib.linkLibC();
lib.linkLibCpp();
//lib.addIncludePath(upstream.path(""));
//lib.addIncludePath(.{ .path = "override" });
if (target.result.isDarwin()) {
const apple_sdk = @import("apple_sdk");
try apple_sdk.addPaths(b, &lib.root_module);
}
var flags = std.ArrayList([]const u8).init(b.allocator);
defer flags.deinit();

View File

@ -7,5 +7,7 @@
.url = "https://github.com/KhronosGroup/SPIRV-Cross/archive/4818f7e7ef7b7078a3a7a5a52c4a338e0dda22f4.tar.gz",
.hash = "1220b2d8a6cff1926ef28a29e312a0a503b555ebc2f082230b882410f49e672ac9c6",
},
.apple_sdk = .{ .path = "../apple-sdk" },
},
}

View File

@ -13,6 +13,11 @@ pub fn build(b: *std.Build) !void {
});
lib.linkLibC();
lib.addIncludePath(upstream.path(""));
if (target.result.isDarwin()) {
const apple_sdk = @import("apple_sdk");
try apple_sdk.addPaths(b, &lib.root_module);
}
lib.installHeadersDirectoryOptions(.{
.source_dir = upstream.path(""),
.install_dir = .header,

View File

@ -1,10 +1,13 @@
.{
.name = "zlib",
.version = "1.3.0",
.paths = .{""},
.dependencies = .{
.zlib = .{
.url = "https://github.com/madler/zlib/archive/refs/tags/v1.3.tar.gz",
.hash = "12207d353609d95cee9da7891919e6d9582e97b7aa2831bd50f33bf523a582a08547",
},
.apple_sdk = .{ .path = "../apple-sdk" },
},
}

View File

@ -290,7 +290,7 @@ fn setupFd(src: File.Handle, target: i32) !void {
}
}
},
.macos => {
.ios, .macos => {
// Mac doesn't support dup3 so we use dup2. We purposely clear
// CLO_ON_EXEC for this fd.
const flags = try os.fcntl(src, os.F.GETFD, 0);

View File

@ -17,7 +17,7 @@ pub const SplitResizeDirection = Binding.Action.SplitResizeDirection;
// Keymap is only available on macOS right now. We could implement it
// in theory for XKB too on Linux but we don't need it right now.
pub const Keymap = switch (builtin.os.tag) {
.macos => @import("input/KeymapDarwin.zig"),
.ios, .macos => @import("input/KeymapDarwin.zig"),
else => struct {},
};

View File

@ -9,7 +9,7 @@ const Key = @import("key.zig").Key;
/// The full list of entries for the current platform.
pub const entries: []const Entry = entries: {
const native_idx = switch (builtin.os.tag) {
.macos => 4, // mac
.ios, .macos => 4, // mac
.windows => 3, // win
.linux => 2, // xkb
else => @compileError("unsupported platform"),

View File

@ -1,3 +1,4 @@
const std = @import("std");
pub const cursor = @import("cursor.zig");
pub const key = @import("key.zig");
pub const termio = @import("termio.zig");

View File

@ -52,6 +52,9 @@ pub fn launchedFromDesktop() bool {
// TODO: This should have some logic to detect this. Perhaps std.builtin.subsystem
.windows => false,
// iPhone/iPad is always launched from the "desktop"
.ios => true,
else => @compileError("unsupported platform"),
};
}

View File

@ -14,6 +14,10 @@ pub inline fn home(buf: []u8) !?[]u8 {
return switch (builtin.os.tag) {
inline .linux, .macos => try homeUnix(buf),
.windows => try homeWindows(buf),
// iOS doesn't have a user-writable home directory
.ios => null,
else => @compileError("unimplemented"),
};
}

View File

@ -8,6 +8,7 @@ pub fn open(alloc: Allocator, url: []const u8) !void {
.linux => &.{ "xdg-open", url },
.macos => &.{ "open", url },
.windows => &.{ "rundll32", "url.dll,FileProtocolHandler", url },
.ios => return error.Unimplemented,
else => @compileError("unsupported OS"),
};

View File

@ -14,10 +14,41 @@ pub const winsize = extern struct {
ws_ypixel: u16 = 600,
};
pub const Pty = if (builtin.os.tag == .windows)
WindowsPty
else
PosixPty;
pub const Pty = switch (builtin.os.tag) {
.windows => WindowsPty,
.ios => NullPty,
else => PosixPty,
};
// A pty implementation that does nothing.
//
// TODO: This should be removed. This is only temporary until we have
// a termio that doesn't use a pty. This isn't used in any user-facing
// artifacts, this is just a stopgap to get compilation to work on iOS.
const NullPty = struct {
pub const Fd = std.os.fd_t;
master: Fd,
slave: Fd,
pub fn open(size: winsize) !Pty {
_ = size;
return .{ .master = 0, .slave = 0 };
}
pub fn deinit(self: *Pty) void {
_ = self;
}
pub fn setSize(self: *Pty, size: winsize) !void {
_ = self;
_ = size;
}
pub fn childPreExec(self: Pty) !void {
_ = self;
}
};
/// Linux PTY creation and management. This is just a thin layer on top
/// of Linux syscalls. The caller is responsible for detail-oriented handling