-
Notifications
You must be signed in to change notification settings - Fork 59
Honor SDKROOT for local Apple deps #18
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
727197d
ed09153
745a70a
ff171a1
bb5b570
9e17756
093c9d7
8a693da
0b64ad0
504b27a
5eb5833
88b9fc6
bc0ee31
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -41,14 +41,28 @@ pub fn addPaths( | |
| }); | ||
|
|
||
| if (!gop.found_existing) { | ||
| const sdkroot_override = std.process.getEnvVarOwned(b.allocator, "SDKROOT") catch |err| switch (err) { | ||
| error.EnvironmentVariableNotFound => null, | ||
| else => return err, | ||
| }; | ||
| defer if (sdkroot_override) |sdkroot| b.allocator.free(sdkroot); | ||
|
|
||
| // Detect our SDK using the "findNative" Zig stdlib function. | ||
| // This is really important because it forces using `xcrun` to | ||
| // find the SDK path. | ||
| const libc = try std.zig.LibCInstallation.findNative(.{ | ||
| .allocator = b.allocator, | ||
| .target = &step.rootModuleTarget(), | ||
| .verbose = false, | ||
| }); | ||
| const libc = libc: { | ||
| if (sdkroot_override) |sdkroot| { | ||
| var libc: std.zig.LibCInstallation = .{}; | ||
| libc.include_dir = try std.fs.path.join(b.allocator, &.{ sdkroot, "usr", "include" }); | ||
| libc.sys_include_dir = try std.fs.path.join(b.allocator, &.{ sdkroot, "usr", "include" }); | ||
| break :libc libc; | ||
|
Comment on lines
+54
to
+58
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
When In practice this is fine for ARM macOS targets (no CRT/libc stubs needed), but it's worth documenting the assumption with a comment, e.g.: // SDKROOT override: only header paths are required for Apple Silicon targets.
// CRT/lib/gcc dirs are not needed and left null.If this ever needs to work on other Apple targets (iOS device, tvOS, etc.) the missing Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!
Comment on lines
+54
to
+58
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Using Useful? React with 👍 / 👎. |
||
| } | ||
| break :libc try std.zig.LibCInstallation.findNative(.{ | ||
| .allocator = b.allocator, | ||
| .target = &step.rootModuleTarget(), | ||
| .verbose = false, | ||
| }); | ||
| }; | ||
|
|
||
| // Render the file compatible with the `--libc` Zig flag. | ||
| var stream: std.io.Writer.Allocating = .init(b.allocator); | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,21 @@ | ||
| MIT License | ||
|
|
||
| Copyright (c) 2023 Mitchell Hashimoto | ||
|
|
||
| Permission is hereby granted, free of charge, to any person obtaining a copy | ||
| of this software and associated documentation files (the "Software"), to deal | ||
| in the Software without restriction, including without limitation the rights | ||
| to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
| copies of the Software, and to permit persons to whom the Software is | ||
| furnished to do so, subject to the following conditions: | ||
|
|
||
| The above copyright notice and this permission notice shall be included in all | ||
| copies or substantial portions of the Software. | ||
|
|
||
| THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
| IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
| FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
| AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
| LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
| OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||
| SOFTWARE. |
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -0,0 +1,101 @@ | ||||||
| # zig-objc - Objective-C Runtime Bindings for Zig | ||||||
|
|
||||||
| zig-objc allows Zig to call Objective-C using the macOS | ||||||
| [Objective-C runtime](https://developer.apple.com/documentation/objectivec/objective-c_runtime?language=objc). | ||||||
|
|
||||||
| **Project Status:** This library does not currently have 100% coverage over the Objective-C | ||||||
| runtime, but supports enough features to be useful. I use this library in | ||||||
| shipping code that I run every day. | ||||||
|
|
||||||
| ## Features | ||||||
|
|
||||||
| * Classes: | ||||||
| - Find classes | ||||||
| - Read property metadata | ||||||
| - Call methods | ||||||
| - Create subclasses | ||||||
| - Add methods | ||||||
| - Replace methods | ||||||
| - Add instance variables | ||||||
| * Objects: | ||||||
| - Class or class name for object | ||||||
| - Read and write properties | ||||||
| - Read and write instance variables | ||||||
| - Call methods | ||||||
| - Call superclass methods | ||||||
| * Protocols: | ||||||
| - Check conformance | ||||||
| - Read property metadata | ||||||
| * Blocks: | ||||||
| - Define and invoke blocks with captured values | ||||||
| - Pass blocks to C APIs which can then invoke your Zig code | ||||||
| * Autorelease pools | ||||||
|
|
||||||
| There is still a bunch of the runtime API that isn't supported. It wouldn't | ||||||
| be hard work to add it, I just haven't needed it. For example: object | ||||||
| instance variables, protocols, dynamically registering new classes, etc. | ||||||
|
|
||||||
| Feel free to open a pull request if you want additional features. | ||||||
| **Do not open issues to request features (only pull requests).** I'm | ||||||
| only going to add features I need, _unless_ you open a pull request to | ||||||
| add it yourself. | ||||||
|
|
||||||
| ## Example | ||||||
|
|
||||||
| Here is an example that uses `NSProcessInfo` to implement a function | ||||||
| `macosVersionAtLeast` that returns true if the running macOS versions | ||||||
| is at least the given arguments. | ||||||
|
Comment on lines
+46
to
+47
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fix grammar in the example description. Line 46-Line 47 should use singular phrasing: “running macOS version is…”. 🤖 Prompt for AI Agents |
||||||
|
|
||||||
| ```zig | ||||||
| const objc = @import("objc"); | ||||||
|
|
||||||
| pub fn macosVersionAtLeast(major: i64, minor: i64, patch: i64) bool { | ||||||
| /// Get the objc class from the runtime | ||||||
| const NSProcessInfo = objc.getClass("NSProcessInfo").?; | ||||||
|
|
||||||
| /// Call a class method with no arguments that returns another objc object. | ||||||
| const info = NSProcessInfo.msgSend(objc.Object, "processInfo", .{}); | ||||||
|
|
||||||
| /// Call an instance method that returns a boolean and takes a single | ||||||
| /// argument. | ||||||
| return info.msgSend(bool, "isOperatingSystemAtLeastVersion:", .{ | ||||||
| NSOperatingSystemVersion{ .major = major, .minor = minor, .patch = patch }, | ||||||
| }); | ||||||
| } | ||||||
|
|
||||||
| /// This extern struct matches the Cocoa headers for layout. | ||||||
| const NSOperatingSystemVersion = extern struct { | ||||||
| major: i64, | ||||||
| minor: i64, | ||||||
| patch: i64, | ||||||
| }; | ||||||
| ``` | ||||||
|
|
||||||
| ## Usage | ||||||
|
|
||||||
| Add this repository to your `build.zig.zon` file. Then: | ||||||
|
|
||||||
| ```zig | ||||||
| pub fn build(b: *std.build.Builder) !void { | ||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🏁 Script executed: #!/bin/bash
# Verify build API usage patterns in this repository.
rg -nP --type=zig 'pub fn build\s*\(\s*b:\s*\*std\.Build\s*\)'
rg -nP --type=zig 'std\.build\.Builder'Repository: manaflow-ai/ghostty Length of output: 2035 🏁 Script executed: cat -n pkg/zig-objc/README.md | sed -n '70,90p'Repository: manaflow-ai/ghostty Length of output: 659 Update the build function signature in the usage example to use the current Zig API. Line 79 documents Suggested fix-pub fn build(b: *std.build.Builder) !void {
+pub fn build(b: *std.Build) !void {📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||
| // ... other stuff | ||||||
|
|
||||||
| exe.root_module.addImport("objc", b.dependency("zig_objc", .{ | ||||||
| .target = target, | ||||||
| .optimize = optimize, | ||||||
| }).module("objc")); | ||||||
| } | ||||||
| ``` | ||||||
|
|
||||||
| Note that `zig-objc` will find and link to headers from the target SDK | ||||||
| (macOS, iOS, etc.) automatically by finding your Xcode installation. If | ||||||
| Xcode is not installed, you can add it manually but you must set the | ||||||
| `-Dadd-paths=false` flag. | ||||||
|
|
||||||
| **`zig-objc` only works with released versions of Zig.** We don't support | ||||||
| nightly versions because the Zig compiler is still changing too much. | ||||||
|
|
||||||
| ## Documentation | ||||||
|
|
||||||
| Read the source code, it is well commented. If something isn't clear, please | ||||||
| open an issue and I'll enhance the source code. Some familiarity with | ||||||
| Objective-C concepts is expected for understanding the doc comments. | ||||||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,99 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||
| const std = @import("std"); | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| pub fn build(b: *std.Build) !void { | ||||||||||||||||||||||||||||||||||||||||||||||||||
| const optimize = b.standardOptimizeOption(.{}); | ||||||||||||||||||||||||||||||||||||||||||||||||||
| const target = b.standardTargetOptions(.{}); | ||||||||||||||||||||||||||||||||||||||||||||||||||
| const add_paths = b.option( | ||||||||||||||||||||||||||||||||||||||||||||||||||
| bool, | ||||||||||||||||||||||||||||||||||||||||||||||||||
| "add-paths", | ||||||||||||||||||||||||||||||||||||||||||||||||||
| "add apple SDK paths from Xcode installation", | ||||||||||||||||||||||||||||||||||||||||||||||||||
| ) orelse true; | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| const objc = b.addModule("objc", .{ | ||||||||||||||||||||||||||||||||||||||||||||||||||
| .root_source_file = b.path("src/main.zig"), | ||||||||||||||||||||||||||||||||||||||||||||||||||
| .target = target, | ||||||||||||||||||||||||||||||||||||||||||||||||||
| .optimize = optimize, | ||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||
| if (add_paths) try addAppleSDK(b, objc); | ||||||||||||||||||||||||||||||||||||||||||||||||||
| objc.linkSystemLibrary("objc", .{}); | ||||||||||||||||||||||||||||||||||||||||||||||||||
| objc.linkFramework("Foundation", .{}); | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| const tests = b.addTest(.{ | ||||||||||||||||||||||||||||||||||||||||||||||||||
| .name = "objc-test", | ||||||||||||||||||||||||||||||||||||||||||||||||||
| .root_module = b.createModule(.{ | ||||||||||||||||||||||||||||||||||||||||||||||||||
| .root_source_file = b.path("src/main.zig"), | ||||||||||||||||||||||||||||||||||||||||||||||||||
| .target = target, | ||||||||||||||||||||||||||||||||||||||||||||||||||
| .optimize = optimize, | ||||||||||||||||||||||||||||||||||||||||||||||||||
| }), | ||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||
| tests.linkSystemLibrary("objc"); | ||||||||||||||||||||||||||||||||||||||||||||||||||
| tests.linkFramework("Foundation"); | ||||||||||||||||||||||||||||||||||||||||||||||||||
| tests.linkFramework("AppKit"); // Required by 'tagged pointer' test. | ||||||||||||||||||||||||||||||||||||||||||||||||||
| try addAppleSDK(b, tests.root_module); | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+21
to
+32
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Test module doesn't respect The 🔧 Suggested fix tests.linkSystemLibrary("objc");
tests.linkFramework("Foundation");
tests.linkFramework("AppKit"); // Required by 'tagged pointer' test.
- try addAppleSDK(b, tests.root_module);
+ if (add_paths) try addAppleSDK(b, tests.root_module);
b.installArtifact(tests);📝 Committable suggestion
Suggested change
🤖 Prompt for AI AgentsThere was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. P2: Honor the add-paths option for the tests module too; otherwise Prompt for AI agents |
||||||||||||||||||||||||||||||||||||||||||||||||||
| b.installArtifact(tests); | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| const test_step = b.step("test", "Run tests"); | ||||||||||||||||||||||||||||||||||||||||||||||||||
| const tests_run = b.addRunArtifact(tests); | ||||||||||||||||||||||||||||||||||||||||||||||||||
| test_step.dependOn(&tests_run.step); | ||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| /// Add the SDK framework, include, and library paths to the given module. | ||||||||||||||||||||||||||||||||||||||||||||||||||
| /// The module target is used to determine the SDK to use so it must have | ||||||||||||||||||||||||||||||||||||||||||||||||||
| /// a resolved target. | ||||||||||||||||||||||||||||||||||||||||||||||||||
| /// | ||||||||||||||||||||||||||||||||||||||||||||||||||
| /// The Apple SDK is determined based on the build target and found using | ||||||||||||||||||||||||||||||||||||||||||||||||||
| /// xcrun, so it requires a valid Xcode installation. | ||||||||||||||||||||||||||||||||||||||||||||||||||
| pub fn addAppleSDK(b: *std.Build, m: *std.Build.Module) !void { | ||||||||||||||||||||||||||||||||||||||||||||||||||
| // The cache. This always uses b.allocator and never frees memory | ||||||||||||||||||||||||||||||||||||||||||||||||||
| // (which is idiomatic for a Zig build exe). | ||||||||||||||||||||||||||||||||||||||||||||||||||
| const Cache = struct { | ||||||||||||||||||||||||||||||||||||||||||||||||||
| const Key = struct { | ||||||||||||||||||||||||||||||||||||||||||||||||||
| arch: std.Target.Cpu.Arch, | ||||||||||||||||||||||||||||||||||||||||||||||||||
| os: std.Target.Os.Tag, | ||||||||||||||||||||||||||||||||||||||||||||||||||
| abi: std.Target.Abi, | ||||||||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| var map: std.AutoHashMapUnmanaged(Key, ?[]const u8) = .{}; | ||||||||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| const target = m.resolved_target.?.result; | ||||||||||||||||||||||||||||||||||||||||||||||||||
| const gop = try Cache.map.getOrPut(b.allocator, .{ | ||||||||||||||||||||||||||||||||||||||||||||||||||
| .arch = target.cpu.arch, | ||||||||||||||||||||||||||||||||||||||||||||||||||
| .os = target.os.tag, | ||||||||||||||||||||||||||||||||||||||||||||||||||
| .abi = target.abi, | ||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| // This executes `xcrun` to get the SDK path. We don't want to execute | ||||||||||||||||||||||||||||||||||||||||||||||||||
| // this multiple times so we cache the value. | ||||||||||||||||||||||||||||||||||||||||||||||||||
| if (!gop.found_existing) { | ||||||||||||||||||||||||||||||||||||||||||||||||||
| const sdkroot_override = std.process.getEnvVarOwned(b.allocator, "SDKROOT") catch |err| switch (err) { | ||||||||||||||||||||||||||||||||||||||||||||||||||
| error.EnvironmentVariableNotFound => null, | ||||||||||||||||||||||||||||||||||||||||||||||||||
| else => return err, | ||||||||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||||||
| defer if (sdkroot_override) |sdkroot| b.allocator.free(sdkroot); | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| if (sdkroot_override) |sdkroot| { | ||||||||||||||||||||||||||||||||||||||||||||||||||
| gop.value_ptr.* = try b.allocator.dupe(u8, sdkroot); | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+75
to
+76
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
The SDK override is applied unconditionally for every target in this dependency, so a single Useful? React with 👍 / 👎. |
||||||||||||||||||||||||||||||||||||||||||||||||||
| } else { | ||||||||||||||||||||||||||||||||||||||||||||||||||
| gop.value_ptr.* = std.zig.system.darwin.getSdk( | ||||||||||||||||||||||||||||||||||||||||||||||||||
| b.allocator, | ||||||||||||||||||||||||||||||||||||||||||||||||||
| &m.resolved_target.?.result, | ||||||||||||||||||||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| // The active SDK we want to use | ||||||||||||||||||||||||||||||||||||||||||||||||||
| const path = gop.value_ptr.* orelse return switch (target.os.tag) { | ||||||||||||||||||||||||||||||||||||||||||||||||||
| // Return a more descriptive error. Before we just returned the | ||||||||||||||||||||||||||||||||||||||||||||||||||
| // generic error but this was confusing a lot of community members. | ||||||||||||||||||||||||||||||||||||||||||||||||||
| // It costs us nothing in the build script to return something better. | ||||||||||||||||||||||||||||||||||||||||||||||||||
| .macos => error.XcodeMacOSSDKNotFound, | ||||||||||||||||||||||||||||||||||||||||||||||||||
| .ios => error.XcodeiOSSDKNotFound, | ||||||||||||||||||||||||||||||||||||||||||||||||||
| .tvos => error.XcodeTVOSSDKNotFound, | ||||||||||||||||||||||||||||||||||||||||||||||||||
| .watchos => error.XcodeWatchOSSDKNotFound, | ||||||||||||||||||||||||||||||||||||||||||||||||||
| else => error.XcodeAppleSDKNotFound, | ||||||||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||||||
| m.addSystemFrameworkPath(.{ .cwd_relative = b.pathJoin(&.{ path, "System/Library/Frameworks" }) }); | ||||||||||||||||||||||||||||||||||||||||||||||||||
| m.addSystemIncludePath(.{ .cwd_relative = b.pathJoin(&.{ path, "usr/include" }) }); | ||||||||||||||||||||||||||||||||||||||||||||||||||
| m.addLibraryPath(.{ .cwd_relative = b.pathJoin(&.{ path, "usr/lib" }) }); | ||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,13 @@ | ||
| .{ | ||
| .name = .zig_objc, | ||
| .version = "0.0.0", | ||
| .fingerprint = 0x8a91772ba7d2bf22, | ||
| .paths = .{ | ||
| "src/", | ||
| "build.zig", | ||
| "build.zig.zon", | ||
| "README.md", | ||
| "LICENSE", | ||
| }, | ||
| .dependencies = .{}, | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,17 @@ | ||
| const std = @import("std"); | ||
|
|
||
| pub const AutoreleasePool = opaque { | ||
| /// Create a new autorelease pool. To clean it up, call deinit. | ||
| pub inline fn init() *AutoreleasePool { | ||
| return @ptrCast(objc_autoreleasePoolPush().?); | ||
| } | ||
|
|
||
| pub inline fn deinit(self: *AutoreleasePool) void { | ||
| objc_autoreleasePoolPop(self); | ||
| } | ||
| }; | ||
|
|
||
| // I'm not sure if these are internal or not... they aren't in any headers, | ||
| // but its how autorelease pools are implemented. | ||
| extern "c" fn objc_autoreleasePoolPush() ?*anyopaque; | ||
| extern "c" fn objc_autoreleasePoolPop(?*anyopaque) void; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Switching
sendTexttoghostty_surface_text_inputremoves paste encoding/protection for every caller of this method; unlikeghostty_surface_text, the new path skips bracketed-paste framing and control-byte filtering. In this codebase, drag-and-drop text is routed throughinsertTextintosendText(SurfaceView_AppKit.performDragOperation), so dropped content now reaches the terminal as raw typed input and can execute multiline/untrusted payloads immediately.Useful? React with 👍 / 👎.