ziglang / zig

General-purpose programming language and toolchain for maintaining robust, optimal, and reusable software.
https://ziglang.org
MIT License
33.7k stars 2.47k forks source link

Cannot build for iOS on macOS Sonoma. #19217

Open jonahnm opened 6 months ago

jonahnm commented 6 months ago

Zig Version

0.12.0-dev.3154+0b744da84

Steps to Reproduce and Observed Behavior

Run zig build targeting iOS with iOS SDK in build file, and you get error: error: unable to find libSystem system library.

Expected Behavior

Successful compilation.

mikdusan commented 6 months ago

To override the SDK the zig libc mechanism is used. Basically:

  1. ask xcrun --show-sdk-path --sdk iphoneos for the path
  2. generate a zig libc file zig libc > libc.txt
  3. edit the first two entries with xcrun's output, appending /usr/include. Here's an example:
    include_dir=/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS17.4.sdk/usr/include
    sys_include_dir=/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS17.4.sdk/usr/include
    crt_dir=
    msvc_lib_dir=
    kernel32_lib_dir=
    gcc_dir=
  4. invoke zig build-exe --libc libc.txt ... and things should work:
    zig build-exe z1.zig --libc libc.txt --verbose-link -target aarch64-ios
    LLVM Emit Object... zig ld -dynamic -platform_version ios 12.0.0 17.4 -syslibroot /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS17.4.sdk -e _main z1.o /Users/mike/project/zig/work/main/zig-cache/o/172afb7523b9b2cec813e0de72bbdc07/libcompiler_rt.a -o z1 -lSystem

Using the zig build system is a little more complicated. We currently do not integrate this but here's a quick take on making it work. But a slight warning, I don't use zig build that much and I believe there was talk of removing zig custom steps, which is what I implemented the libc.txt generator with.

const std = @import("std");

pub fn build(b: *std.Build) void {
    const target = b.standardTargetOptions(.{});
    const optimize = b.standardOptimizeOption(.{});

    const exe = b.addExecutable(.{
        .name = "hw",
        .root_source_file = .{ .path = "src/main.zig" },
        .target = target,
        .optimize = optimize,
    });
    b.installArtifact(exe);

    // map Target.Os → xcrun SDK name
    const o_sdk: ?[]const u8 = switch (target.result.os.tag) {
        .ios => "iphoneos",
        .macos => "macosx",
        .tvos => "appletvos",
        .watchos => "watchos",
        else => null,
    };

    if (o_sdk) |sdk| {
        const xcrun = b.addSystemCommand(&.{ "xcrun", "--show-sdk-path", "--sdk", sdk });
        exe.step.dependOn(&xcrun.step);

        const g = GenLibCFile.create(b, xcrun.captureStdOut());
        g.step.dependOn(&xcrun.step);
        exe.step.dependOn(&g.step);
        exe.setLibCFile(g.output);
    }
}

const GenLibCFile = struct {
    step: std.Build.Step,
    sdkroot: std.Build.LazyPath,
    generated: std.Build.GeneratedFile,
    output: std.Build.LazyPath,

    fn create(owner: *std.Build, sdkroot: std.Build.LazyPath) *GenLibCFile {
        const self = owner.allocator.create(GenLibCFile) catch @panic("OOM");
        self.* = .{
            .step = std.Build.Step.init(.{
                .id = .custom,
                .name = "GenLibCFile",
                .owner = owner,
                .makeFn = make,
            }),
            .sdkroot = sdkroot,
            .generated = .{ .step = &self.step },
            .output = .{ .generated = &self.generated },
        };
        return self;
    }

    fn make(step: *std.Build.Step, prog_node: *std.Progress.Node) !void {
        const b = step.owner;
        const self = @fieldParentPtr(GenLibCFile, "step", step);
        prog_node.activate();
        defer prog_node.end();

        // read xcrun output
        var buf: [std.c.PATH_MAX]u8 = undefined;
        var path = try std.fs.cwd().readFile(self.sdkroot.getPath(b), &buf);
        // want first line
        for (path, 0..) |c, i| {
            if (c == '\n') {
                path.len = i;
                break;
            }
        }
        prog_node.completeOne();

        var man = b.graph.cache.obtain();
        defer man.deinit();

        man.hash.addBytes("GenLibCFile");
        man.hash.addBytes(&[_]u8{ 0x2e, 0x4c, 0x48, 0x3d, 0x66, 0x3c, 0xc2, 0x80 });
        man.hash.addBytes(path);

        if (try step.cacheHit(&man)) {
            const digest = man.final();
            self.generated.path = try b.cache_root.join(b.allocator, &.{ "o", &digest, "libc.txt" });
            return;
        }

        const digest = man.final();
        self.generated.path = try b.cache_root.join(b.allocator, &.{ "o", &digest, "libc.txt" });

        const cache_path = "o" ++ std.fs.path.sep_str ++ digest;
        var cache_dir = b.cache_root.handle.makeOpenPath(cache_path, .{}) catch |err| {
            return step.fail("unable to make path '{}{s}': {s}", .{
                b.cache_root, cache_path, @errorName(err),
            });
        };
        defer cache_dir.close();

        var f = try cache_dir.createFile("libc.txt", .{});
        defer f.close();

        try f.writeAll("include_dir=");
        try f.writeAll(path);
        try f.writeAll(std.fs.path.sep_str ++ "usr" ++ std.fs.path.sep_str ++ "include");
        try f.writeAll("\nsys_include_dir=");
        try f.writeAll(path);
        try f.writeAll(std.fs.path.sep_str ++ "usr" ++ std.fs.path.sep_str ++ "include");
        try f.writeAll("\ncrt_dir=");
        try f.writeAll("\nmsvc_lib_dir=");
        try f.writeAll("\nkernel32_lib_dir=");
        try f.writeAll("\ngcc_dir=");
        try f.writeAll("\n");

        try step.writeManifest(&man);
        prog_node.completeOne();
    }
};
mikdusan commented 6 months ago

or... there is an environment variable that seems to do the trick as well which activates a previously prepared libc.txt

ZIG_LIBC=libc.txt zig build -Dtarget=aarch64-ios --verbose
jonahnm commented 6 months ago

or... there is an environment variable that seems to do the trick as well which activates a previously prepared libc.txt

ZIG_LIBC=libc.txt zig build -Dtarget=aarch64-ios --verbose

This does allow it to compile indeed, but I believe a better solution should be incorporated.

julyfun commented 2 months ago

I met the same problem when I installed XCode 16 for Sonoma and rerun my zig file using zig 0.13.0. And I solved this by:

# ref: https://github.com/zed-industries/zed/blob/main/docs/src/development/macos.md
sudo xcode-select --switch /Applications/Xcode.app/Contents/Developer
# ref: brew said this when I tried to uinstall zig, this feels right
sudo xcodebuild -license accept
# and brew uninstall and reinstall zlg, & zig