iosxcodegoclanggo-build

Building a static library with go build for Iphone simulator


I an building a c archive in my iOS project using following:

GOOS=ios GOARCH=arm64 CGO_ENABLED=1 SDK=iphonesimulator CGO_CFLAGS="-fembed-bitcode" CC=pwd/clangwrap.sh go build -buildmode=c-archive -o libuplink.a

Clangwrap.sh looks like this

#!/bin/sh

# go/clangwrap.sh

SDK_PATH=`xcrun --sdk $SDK --show-sdk-path`
CLANG=`xcrun --sdk $SDK --find clang`

if [ "$GOARCH" == "amd64" ]; then
    CARCH="x86_64"
elif [ "$GOARCH" == "arm64" ]; then
    CARCH="arm64"
fi

exec $CLANG -arch $CARCH -isysroot $SDK_PATH -mios-version-min=10.0 "$@"

When I link it up in XCode and attempt to run with simulator however, I can only run it on the device itself:

building for iOS Simulator, but linking in object file built for iOS ... for architecture arm64

How do I target the simulator for a go build for a static library that's used in Swift project?


Solution

  • Requirements

    TL;DR

    You could do something similar to Xcode if you choose a simulator as run destination.

    So basically use something like -target arm64-apple-ios16.2-simulator instead of -arch arm64. Also omit -mios-version-min=10.0, since the actual minimal version is encoded in the -target (e.g. 16.2), which takes precedence (the correct option for the simulator would be -miphonesimulator-version-min anyway).

    Then as CGO_LDFLAGS also specify the -target option plus -syslibroot with the path to the SDK.

    Your build scripts tweaked a bit, it might look something like this:

    This specifies the simulator as the target and the minimum version is 15.

    build.sh

    #!/bin/sh
    export GOOS=ios
    export GOARCH=arm64
    export CGO_ENABLED=1
    export SDK=iphonesimulator
    export CGO_CFLAGS="-fembed-bitcode"
    export MIN_VERSION=15
    
    . ./target.sh
    
    export CGO_LDFLAGS="-target ${TARGET} -syslibroot \"${SDK_PATH}\""
    CC="$(pwd)/clangwrap.sh"
    export CC
    
    go build -buildmode=c-archive -o libuplink.a
    

    target.sh

    #!/bin/sh
    
    SDK_PATH=$(xcrun --sdk "$SDK" --show-sdk-path)
    export SDK_PATH
    
    if [ "$GOARCH" = "amd64" ]; then
        CARCH="x86_64"
    elif [ "$GOARCH" = "arm64" ]; then
        CARCH="arm64"
    fi
    
    if [ "$SDK" = "iphoneos" ]; then
      export TARGET="$CARCH-apple-ios$MIN_VERSION"
    elif [ "$SDK" = "iphonesimulator" ]; then
      export TARGET="$CARCH-apple-ios$MIN_VERSION-simulator"
    fi
    

    clangwrap.sh

    The clangwrap.sh then simplifies to:

    #!/bin/zsh
    
    CLANG=$(xcrun --sdk "$SDK" --find clang)
    
    exec "$CLANG" -target "$TARGET" -isysroot "$SDK_PATH" "$@"
    

    Details


    Different SDKs

    Different SDKs must be specified for an iOS device and the iPhone simulator. You can find them next to the other platforms supported by Xcode under /Applications/Xcode.app/Contents/Developer/Platforms. For example, in Xcode 14.2, among others, there is an iPhoneOS platform with an iPhoneOS16.2.sdk and an iPhoneSimulator platform with iPhoneSimulator16.2.sdk.

    There is this interesting post from an Apple employee in the Apple developer forum: https://developer.apple.com/forums/thread/673387#662260022

    To check a generated static library to display the load commands, you can call:

    otool -l libuplink.a
    

    A static library generated for use with an Apple Silicon simulator should display something like the following:

    ...
    Load command 1
          cmd LC_BUILD_VERSION
      cmdsize 24
     platform 7
        minos 15.0
          sdk 16.2
    ...    
    
    

    Note: platform 7 denotes the simulator, minos the minimum deployment target, and sdk the actual SDK version used.

    See the section in the include file loader.h that reads:

    /* Known values for the above platform field. */
    #define PLATFORM_UNKNOWN 0
    #define PLATFORM_ANY 0xFFFFFF
    #define PLATFORM_MACOS 1
    #define PLATFORM_IOS 2
    #define PLATFORM_TVOS 3
    #define PLATFORM_WATCHOS 4
    #define PLATFORM_BRIDGEOS 5
    #define PLATFORM_MACCATALYST 6
    #define PLATFORM_IOSSIMULATOR 7
    #define PLATFORM_TVOSSIMULATOR 8
    #define PLATFORM_WATCHOSSIMULATOR 9
    #define PLATFORM_DRIVERKIT 10
    

    You can view them yourself on your system as follows:

    cat `xcrun --sdk iphonesimulator --show-sdk-path`/usr/include/mach-o/loader.h
    

    Build for iPhone device

    To build a static library for the iPhone SDK, you would change this:

    export SDK=iphoneos
    

    in the build.sh script above.

    If you tried to use this library in the simulator, it would fail with the message building for iOS Simulator, but linking in object file built for iOS, file 'libuplink.a' for architecture arm64.

    The output of otool -l would read:

    ...
    Load command 1
          cmd LC_BUILD_VERSION
      cmdsize 24
     platform 2
        minos 15.0
          sdk 16.2
       ntools 0
    ...
    

    Note: Platform 2 stands for PLATFORM_IOS and not for the simulator.

    This will of course run perfectly on the device.