Skip to content

Cross-compiling for iOS on Linux: #10964

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

Open
acarlstein opened this issue May 25, 2025 · 6 comments
Open

Cross-compiling for iOS on Linux: #10964

acarlstein opened this issue May 25, 2025 · 6 comments
Labels
area:manual Issues and PRs related to the Manual/Tutorials section of the documentation bug platform:ios Issues or PRs related specifically to ios topic:buildsystem

Comments

@acarlstein
Copy link

acarlstein commented May 25, 2025

Your Godot version:
4.4.1

Issue description:

The documentation for Cross-compiling for iOS on Linux seems outdated.
Many of the components (pbzx, xar, etc.) are wrong but they can still be obtained.

Some steps like the one below doesn't work because something is missing.

cp -r xcode/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include/c++/* iPhoneSDK/iPhoneOS${IOS_SDK_VERSION}.sdk/usr/include/c++

Or, there is no a clear example of the path (in this case for the chain):

scons platform=ios arch=arm64 target=template_release IOS_SDK_PATH="$IOS_SDK_PATH" IOS_TOOLCHAIN_PATH="$IOS_TOOLCHAIN_PATH" ios_triple="arm-apple-darwin11-"

Where IOS_TOOLCHAIN_PATH could be, in my case:

  • This: $HOME/iostoolchain/usr/bin
  • or this: $HOME/iostoolchain/usr
  • or this: $HOME/iostoolchain

Finally, it takes days for whoever is trying this the first time.
I can't even make it work yet.

I think that creating an installation bash file is required for sanity sake.

URL to the documentation page (if already existing):

(Cross-compiling for iOS on Linux)[https://docs.godotengine.org/en/stable/contributing/development/compiling/cross-compiling_for_ios_on_linux.html#cross-compiling-for-ios-on-linux]

install_ios_cross_compile.sh

First draft of the script:

#!/usr/bin/env bash
# filepath: install_ios_cross_compile.sh

set -e

# Default values from your environment
XCODE_PATH="${XCODE_PATH:-$HOME/xcode}"
IOS_SDK_VERSION="${IOS_SDK_VERSION:-16.4}"
IOS_SDK_PATH="${IOS_SDK_PATH:-$HOME/iPhoneSDK/iPhoneOS$IOS_SDK_VERSION.sdk}"
IOS_TOOLCHAIN_PATH="${IOS_TOOLCHAIN_PATH:-$HOME/iostoolchain/usr/bin}"
NONINTERACTIVE=0

# Parse arguments
for arg in "$@"; do
    case $arg in
        -y|--yes)
        NONINTERACTIVE=1
        shift
        ;;
    esac
done

ask() {
    if [ "$NONINTERACTIVE" -eq 1 ]; then
        return 0
    fi
    read -p "$1 [Y/n] " yn
    case $yn in
        [Nn]*) return 1 ;;
        *) return 0 ;;
    esac
}

install_package() {
    PKG="$1"
    if ! dpkg -s "$PKG" >/dev/null 2>&1; then
        if ask "Install $PKG?"; then
            sudo apt-get update
            sudo apt-get install -y "$PKG"
        else
            echo "Please install $PKG manually and re-run the script."
            exit 1
        fi
    fi
}

echo "Checking required programs..."

install_package clang
install_package git
install_package automake
install_package autogen
install_package libtool
install_package xz-utils
install_package cpio
install_package fuse
install_package libfuse2

# xar
if ! command -v xar >/dev/null 2>&1; then
    echo "xar not found."
    echo "You can build it from https://mackyle.github.io/xar/ or use your package manager."
    if ask "Try to install xar from apt?"; then
        sudo apt-get install -y xar
    else
        echo "Please install xar manually and re-run the script."
        exit 1
    fi
fi

# pbzx
if ! command -v pbzx >/dev/null 2>&1; then
    echo "pbzx not found."
    echo "You can get pbzx from https://github.com/NiklasRosenstein/pbzx"
    if ask "Clone and install pbzx to ~/pbzx?"; then
        git clone https://github.com/NiklasRosenstein/pbzx.git ~/pbzx
        sudo install ~/pbzx/pbzx /usr/local/bin/
    else
        echo "Please install pbzx manually and re-run the script."
        exit 1
    fi
fi

# Check for Xcode .xip
if [ ! -d "$XCODE_PATH" ]; then
    echo "Xcode not found at $XCODE_PATH."
    echo "Please download the Xcode .xip from https://developer.apple.com/download/all/?q=Xcode"
    echo "and extract it to $XCODE_PATH."
    exit 1
fi

# Prepare SDK
if [ -d "$IOS_SDK_PATH" ]; then
    echo "iOS SDK already exists at $IOS_SDK_PATH."
    if ask "Skip SDK extraction?"; then
        :
    else
        rm -rf "$IOS_SDK_PATH"
    fi
fi

if [ ! -d "$IOS_SDK_PATH" ]; then
    if ask "Extract iOS SDK version $IOS_SDK_VERSION from Xcode?"; then
        mkdir -p "$IOS_SDK_PATH"
        cp -r "$XCODE_PATH/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk/"* "$IOS_SDK_PATH/"
        cp -r "$XCODE_PATH/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include/c++/"* "$IOS_SDK_PATH/usr/include/c++/"
    fi
fi

# Pack SDK
SDK_ARCHIVE="$HOME/iPhoneSDK/iPhoneOS${IOS_SDK_VERSION}.sdk.tar.xz"
if [ -f "$SDK_ARCHIVE" ]; then
    echo "SDK archive $SDK_ARCHIVE already exists."
    if ask "Skip SDK archiving?"; then
        :
    else
        rm -f "$SDK_ARCHIVE"
    fi
fi

if [ ! -f "$SDK_ARCHIVE" ]; then
    if ask "Pack the SDK into $SDK_ARCHIVE?"; then
        cd "$(dirname "$IOS_SDK_PATH")"
        tar -cf - "$(basename "$IOS_SDK_PATH")" | xz -9 -c - > "$SDK_ARCHIVE"
    fi
fi

# Build cctools
if [ -d "$HOME/cctools-port" ]; then
    echo "cctools-port already cloned."
    if ask "Skip cctools-port clone?"; then
        :
    else
        rm -rf "$HOME/cctools-port"
    fi
fi

if [ ! -d "$HOME/cctools-port" ]; then
    git clone https://github.com/tpoechtrager/cctools-port.git "$HOME/cctools-port"
fi

if [ -d "$HOME/cctools-port/usage_examples/ios_toolchain/target/bin" ]; then
    echo "cctools already built."
    if ask "Skip cctools build?"; then
        :
    else
        rm -rf "$HOME/cctools-port/usage_examples/ios_toolchain/target"
    fi
fi

if [ ! -d "$HOME/cctools-port/usage_examples/ios_toolchain/target/bin" ]; then
    cd "$HOME/cctools-port/usage_examples/ios_toolchain"
    ./build.sh "$SDK_ARCHIVE" arm64
fi

# Copy toolchain
if [ -d "$IOS_TOOLCHAIN_PATH" ]; then
    echo "Toolchain already exists at $IOS_TOOLCHAIN_PATH."
    if ask "Skip copying toolchain?"; then
        :
    else
        rm -rf "$IOS_TOOLCHAIN_PATH"
    fi
fi

if [ ! -d "$IOS_TOOLCHAIN_PATH" ]; then
    mkdir -p "$(dirname "$IOS_TOOLCHAIN_PATH")"
    cp -r "$HOME/cctools-port/usage_examples/ios_toolchain/target/bin" "$(dirname "$IOS_TOOLCHAIN_PATH")/"
fi

echo "Setup complete!"
echo "You can now build Godot for iOS using:"
echo "  scons platform=ios arch=arm64 target=template_release IOS_SDK_PATH=\"$IOS_SDK_PATH\" IOS_TOOLCHAIN_PATH=\"$IOS_TOOLCHAIN_PATH\" ios_triple=\"arm-apple-darwin11-\""

# End of script
@acarlstein
Copy link
Author

acarlstein commented May 25, 2025

I'm going to split this file in different one for sanity sake:

Lets start with a script that will detect all the components required:

check_ios_cross_compile_requirements.sh

#!/usr/bin/env bash
# filepath: check_ios_cross_compile_requirements.sh

set -e

REQUIRED_CMDS=(
    clang
    git
    xar
    pbzx
    automake
    autogen
    libtool
    xz
    cpio
    fusermount
)

MISSING=0

check_required_cmds() {
    echo "Checking required commands and tools for iOS cross-compiling..."
    for cmd in "${REQUIRED_CMDS[@]}"; do
        if ! command -v "$cmd" >/dev/null 2>&1; then
            echo "❌ $cmd: NOT FOUND"
            MISSING=1
        else
            echo "✅ $cmd: found at $(command -v $cmd)"
        fi
    done
}

check_xcode_sdk_dir() {
    if [ -z "$XCODE_PATH" ]; then
        XCODE_PATH="$HOME/xcode"
    fi
    if [ ! -d "$XCODE_PATH" ]; then
        echo "❌ Xcode SDK directory not found at $XCODE_PATH"
        MISSING=1
    else
        echo "✅ Xcode SDK directory found at $XCODE_PATH"
    fi
}

check_ios_sdk_version() {
    if [ -z "$IOS_SDK_VERSION" ]; then
        echo "❌ IOS_SDK_VERSION environment variable is not set"
        MISSING=1
    else
        echo "✅ IOS_SDK_VERSION is set to $IOS_SDK_VERSION"
    fi
}

check_pbzx() {
    if ! command -v pbzx >/dev/null 2>&1; then
        if [ -f "$HOME/pbzx/pbzx" ]; then
            echo "✅ pbzx found at $HOME/pbzx/pbzx (not in PATH)"
        else
            echo "❌ pbzx: NOT FOUND"
            MISSING=1
        fi
    fi
}

# Run all checks
check_required_cmds
check_xcode_sdk_dir
check_ios_sdk_version
check_pbzx

if [ "$MISSING" -eq 1 ]; then
    echo ""
    echo "Some required components are missing. Please install them before proceeding."
    exit 1
else
    echo ""
    echo "All required components are installed and accessible."
fi

Output Example

$ ./check_ios_cross_compile_requirements.sh 
Checking required commands and tools for iOS cross-compiling...
✅ clang: found at /usr/bin/clang
✅ git: found at /usr/bin/git
✅ xar: found at /usr/local/bin/xar
✅ pbzx: found at /home/myhome/pbzx/pbzx
✅ automake: found at /usr/bin/automake
❌ autogen: NOT FOUND
❌ libtool: NOT FOUND
✅ xz: found at /usr/bin/xz
✅ cpio: found at /usr/bin/cpio
✅ fusermount: found at /usr/bin/fusermount
✅ Xcode SDK directory found at /home/myhome/xcode
✅ IOS_SDK_VERSION is set to 16.4

Some required components are missing. Please install them before proceeding.

@acarlstein
Copy link
Author

Updated the code to try to install the components:

#!/usr/bin/env bash
# filepath: check_ios_cross_compile_requirements.sh

set -e

REQUIRED_CMDS=(
    clang
    git
    xar
    pbzx
    automake
    autogen
    libtool
    xz
    cpio
    fusermount
)

MISSING=0
MISSING_CMDS=()

check_required_cmds() {
    echo "Checking required commands and tools for iOS cross-compiling..."
    for cmd in "${REQUIRED_CMDS[@]}"; do
        if ! command -v "$cmd" >/dev/null 2>&1; then
            echo "$cmd: NOT FOUND"
            MISSING=1
            MISSING_CMDS+=("$cmd")
        else
            echo "$cmd: found at $(command -v $cmd)"
        fi
    done
}

check_xcode_sdk_dir() {
    if [ -z "$XCODE_PATH" ]; then
        XCODE_PATH="$HOME/xcode"
    fi
    if [ ! -d "$XCODE_PATH" ]; then
        echo "❌ Xcode SDK directory not found at $XCODE_PATH"
        MISSING=1
    else
        echo "✅ Xcode SDK directory found at $XCODE_PATH"
    fi
}

check_ios_sdk_version() {
    if [ -z "$IOS_SDK_VERSION" ]; then
        echo "❌ IOS_SDK_VERSION environment variable is not set"
        MISSING=1
    else
        echo "✅ IOS_SDK_VERSION is set to $IOS_SDK_VERSION"
    fi
}

check_pbzx() {
    if ! command -v pbzx >/dev/null 2>&1; then
        if [ -f "$HOME/pbzx/pbzx" ]; then
            echo "✅ pbzx found at $HOME/pbzx/pbzx (not in PATH)"
        else
            echo "❌ pbzx: NOT FOUND"
            MISSING=1
            if [[ ! " ${MISSING_CMDS[@]} " =~ " pbzx " ]]; then
                MISSING_CMDS+=("pbzx")
            fi
        fi
    fi
}

install_xar_from_source() {
    echo "Installing xar from source..."
    wget https://storage.googleapis.com/google-code-archive-downloads/v2/code.google.com/xar/xar-1.5.2.tar.gz
    tar -zxvf xar-1.5.2.tar.gz
    cd xar-1.5.2
    sudo apt install -y gcc
    sudo apt-get install -y libxml2-dev zlib1g-dev autoconf libbz2-dev
    ./autogen.sh --noconfigure
    ./configure --with-bzip2
    make
    sudo make install
    cd ..
    echo "xar installation from source complete."
}

install_pbzx_from_source() {
    echo "Installing pbzx from source..."
    git clone https://github.com/NiklasRosenstein/pbzx.git
    cd pbzx
    sudo apt-get install -y clang liblzma-dev
    clang -llzma -lxar -I /usr/local/include pbzx.c -o pbzx
    echo "pbzx installation from source complete. The binary is at $(pwd)/pbzx"
    cd ..
}

handle_missing_components() {
    echo ""
    echo "Some required components are missing:"
    for cmd in "${MISSING_CMDS[@]}"; do
        echo "  - $cmd"
    done
    echo ""
    read -p "Do you want to attempt to install the missing components now? [y/N]: " yn
    case "$yn" in
        [Yy]* )
            echo "Attempting to install missing components..."
            sudo apt-get update
            PKGS=()
            for cmd in "${MISSING_CMDS[@]}"; do
                case "$cmd" in
                    clang) PKGS+=("clang") ;;
                    git) PKGS+=("git") ;;
                    xar) install_xar_from_source ;;
                    pbzx) install_pbzx_from_source ;;
                    automake) PKGS+=("automake") ;;
                    autogen) PKGS+=("autogen") ;;
                    libtool) PKGS+=("libtool") ;;
                    xz) PKGS+=("xz-utils") ;;
                    cpio) PKGS+=("cpio") ;;
                    fusermount) PKGS+=("fuse") ;;
                    *) PKGS+=("$cmd") ;;
                esac
            done
            if [ "${#PKGS[@]}" -gt 0 ]; then
                sudo apt-get install -y "${PKGS[@]}"
            fi
            ;;
        * )
            echo "Exiting without installing missing components."
            exit 1
            ;;
    esac
    exit 1
}

# Run all checks
check_required_cmds
check_xcode_sdk_dir
check_ios_sdk_version
check_pbzx

if [ "$MISSING" -eq 1 ]; then
    handle_missing_components
else
    echo ""
    echo "All required components are installed and accessible."
fi

@acarlstein
Copy link
Author

Updated the code to try to install the components.
Made it to be the stable version 1.0.0.
Let me know if I'm missing something:

#!/usr/bin/env bash
# filepath: check_ios_cross_compile_requirements.sh
# version: 1.0.0
# description: This script checks for the required tools and environment variables for iOS cross-compiling.

set -e

REQUIRED_CMDS=(
    clang
    git
    xar
    pbzx
    automake
    autogen
    libtool
    xz
    cpio
    fusermount
)

MISSING=0
MISSING_CMDS=()

check_required_cmds() {
    echo "Checking required commands and tools for iOS cross-compiling..."
    for cmd in "${REQUIRED_CMDS[@]}"; do
        if ! command -v "$cmd" >/dev/null 2>&1; then
            echo "$cmd: NOT FOUND"
            MISSING=1
            MISSING_CMDS+=("$cmd")
        else
            echo "$cmd: found at $(command -v $cmd)"
        fi
    done
}

check_xcode_sdk_dir() {
    if [ -z "$XCODE_PATH" ]; then
        XCODE_PATH="$HOME/xcode"
    fi
    if [ ! -d "$XCODE_PATH" ]; then
        echo "❌ Xcode SDK directory not found at $XCODE_PATH"
        MISSING=1
    else
        echo "✅ Xcode SDK directory found at $XCODE_PATH"
    fi
}

check_ios_sdk_version() {
    if [ -z "$IOS_SDK_VERSION" ]; then
        echo "❌ IOS_SDK_VERSION environment variable is not set"
        MISSING=1
    else
        echo "✅ IOS_SDK_VERSION is set to $IOS_SDK_VERSION"
    fi
}

check_pbzx() {
    if ! command -v pbzx >/dev/null 2>&1; then
        if [ -f "$HOME/pbzx/pbzx" ]; then
            echo "✅ pbzx found at $HOME/pbzx/pbzx (not in PATH)"
        else
            echo "❌ pbzx: NOT FOUND"
            MISSING=1
            if [[ ! " ${MISSING_CMDS[@]} " =~ " pbzx " ]]; then
                MISSING_CMDS+=("pbzx")
            fi
        fi
    fi
}

install_xar_from_source() {
    echo "Installing xar from source..."
    wget https://storage.googleapis.com/google-code-archive-downloads/v2/code.google.com/xar/xar-1.5.2.tar.gz
    tar -zxvf xar-1.5.2.tar.gz
    cd xar-1.5.2
    sudo apt install -y gcc
    sudo apt-get install -y libxml2-dev zlib1g-dev autoconf libbz2-dev
    ./autogen.sh --noconfigure
    ./configure --with-bzip2
    make
    sudo make install
    cd ..
    echo "xar installation from source complete."
}

install_pbzx_from_source() {
    echo "Installing pbzx from source..."
    git clone https://github.com/NiklasRosenstein/pbzx.git
    cd pbzx
    sudo apt-get install -y clang liblzma-dev
    clang -llzma -lxar -I /usr/local/include pbzx.c -o pbzx
    echo "pbzx installation from source complete. The binary is at $(pwd)/pbzx"
    cd ..
}

handle_missing_components() {
    echo ""
    echo "Some required components are missing:"
    for cmd in "${MISSING_CMDS[@]}"; do
        echo "  - $cmd"
    done
    echo ""
    read -p "Do you want to attempt to install the missing components now? [y/N]: " yn
    case "$yn" in
        [Yy]* )
            echo "Attempting to install missing components..."
            sudo apt-get update
            PKGS=()
            for cmd in "${MISSING_CMDS[@]}"; do
                case "$cmd" in
                    clang) PKGS+=("clang") ;;
                    git) PKGS+=("git") ;;
                    xar) install_xar_from_source ;;
                    pbzx) install_pbzx_from_source ;;
                    automake) PKGS+=("automake") ;;
                    autogen) PKGS+=("autogen") ;;
                    libtool) PKGS+=("libtool-bin") ;;  # Use libtool-bin for the actual binary
                    xz) PKGS+=("xz-utils") ;;
                    cpio) PKGS+=("cpio") ;;
                    fusermount) PKGS+=("fuse") ;;
                    *) PKGS+=("$cmd") ;;
                esac
            done
            if [ "${#PKGS[@]}" -gt 0 ]; then
                sudo apt-get install -y "${PKGS[@]}"
            fi
            ;;
        * )
            echo "Exiting without installing missing components."
            exit 1
            ;;
    esac
    exit 1
}

# Run all checks
check_required_cmds
check_xcode_sdk_dir
check_ios_sdk_version
check_pbzx

if [ "$MISSING" -eq 1 ]; then
    handle_missing_components
else
    echo ""
    echo "All required components are installed and accessible."
fi

Here is the output on my machine:

$ ./check_ios_cross_compile_requirements.sh 
Checking required commands and tools for iOS cross-compiling...
✅ clang: found at /usr/bin/clang
✅ git: found at /usr/bin/git
✅ xar: found at /usr/local/bin/xar
✅ pbzx: found at /home/myhome/pbzx/pbzx
✅ automake: found at /usr/bin/automake
✅ autogen: found at /usr/bin/autogen
✅ libtool: found at /usr/bin/libtool
✅ xz: found at /usr/bin/xz
✅ cpio: found at /usr/bin/cpio
✅ fusermount: found at /usr/bin/fusermount
✅ Xcode SDK directory found at /home/myhome/xcode
✅ IOS_SDK_VERSION is set to 16.4

All required components are installed and accessible.

@acarlstein
Copy link
Author

Next, the configuration file that will try to do the steps of the documentation

configuration.sh

#!/usr/bin/env bash
# version: 0.0.1
# description: Automate SDK extraction, toolchain build, and environment setup for Godot iOS cross-compiling.

set -e

AUTO_INSTALL=0

usage() {
    echo "Usage: $0 [--auto]"
    echo "  --auto   Automatically run all steps without prompting."
    exit 1
}

if [[ "$1" == "--auto" ]]; then
    AUTO_INSTALL=1
elif [[ -n "$1" ]]; then
    usage
fi

prompt_step() {
    local msg="$1"
    if [[ "$AUTO_INSTALL" -eq 1 ]]; then
        return 0
    fi
    read -p "$msg [y/N]: " yn
    case "$yn" in
        [Yy]*) return 0 ;;
        *) return 1 ;;
    esac
}

extract_xcode_xip() {
    if prompt_step "Extract Xcode .xip and unpack Xcode.app?"; then
        # Search for .xip files in current dir and $HOME
        mapfile -t XIP_FILES < <(find . "$HOME" -maxdepth 2 -type f -name "*.xip" 2>/dev/null)
        if [[ ${#XIP_FILES[@]} -eq 0 ]]; then
            echo "No .xip files found. Please provide the path manually."
            read -e -p "Enter path to Xcode .xip file: " XCODE_XIP
        else
            echo "Found the following .xip files:"
            select XIP_CHOICE in "${XIP_FILES[@]}" "Enter path manually"; do
                if [[ "$REPLY" -gt 0 && "$REPLY" -le "${#XIP_FILES[@]}" ]]; then
                    XCODE_XIP="${XIP_FILES[$((REPLY-1))]}"
                    break
                elif [[ "$REPLY" -eq $((${#XIP_FILES[@]}+1)) ]]; then
                    read -e -p "Enter path to Xcode .xip file: " XCODE_XIP
                    break
                else
                    echo "Invalid selection."
                fi
            done
        fi
        mkdir -p xcode
        xar -xf "$XCODE_XIP" -C xcode
        cd xcode
        pbzx -n Content | cpio -i
        cd ..
        echo "Xcode .xip extraction complete."
    fi
}

extract_ios_sdk() {
    if prompt_step "Extract and prepare iOS SDK from Xcode.app?"; then
        read -e -p "Enter iOS SDK version (e.g., 17.0): " IOS_SDK_VERSION
        export IOS_SDK_VERSION
        mkdir -p iPhoneSDK/iPhoneOS${IOS_SDK_VERSION}.sdk
        cp -r xcode/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk/* iPhoneSDK/iPhoneOS${IOS_SDK_VERSION}.sdk
        cp -r xcode/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include/c++/* iPhoneSDK/iPhoneOS${IOS_SDK_VERSION}.sdk/usr/include/c++
        fusermount -u xcode || true
        echo "iOS SDK extracted to iPhoneSDK/iPhoneOS${IOS_SDK_VERSION}.sdk"
    fi
}

pack_ios_sdk() {
    if prompt_step "Pack the iOS SDK for cctools-port?"; then
        cd iPhoneSDK
        tar -cf - * | xz -9 -c - > iPhoneOS${IOS_SDK_VERSION}.sdk.tar.xz
        cd ..
        echo "Packed SDK: iPhoneSDK/iPhoneOS${IOS_SDK_VERSION}.sdk.tar.xz"
    fi
}

build_cctools_port() {
    if prompt_step "Build cctools-port toolchain?"; then
        git clone https://github.com/tpoechtrager/cctools-port.git || true
        cd cctools-port/usage_examples/ios_toolchain
        sudo apt-get install -y automake autogen libtool-bin
        ./build.sh ../../../../iPhoneSDK/iPhoneOS${IOS_SDK_VERSION}.sdk.tar.xz arm64
        cd ../../..
        echo "cctools-port toolchain build complete."
    fi
}

copy_toolchain_binaries() {
    if prompt_step "Copy toolchain binaries to \$HOME/iostoolchain/usr/bin?"; then
        mkdir -p "$HOME/iostoolchain/usr"
        cp -r cctools-port/usage_examples/ios_toolchain/target/bin "$HOME/iostoolchain/usr/"
        echo "Toolchain binaries copied to $HOME/iostoolchain/usr/bin"
    fi
}

set_env_and_show_scons() {
    if prompt_step "Show environment setup and SCons build command?"; then
        echo "You must keep the built toolchain and iPhoneOS SDK directory."
        echo "Set the following environment variable before building:"
        echo
        echo '    export OSXCROSS_IOS="anything"'
        echo
        echo "Example SCons build command:"
        echo
        echo "    scons platform=ios arch=arm64 target=template_release \\"
        echo "          IOS_SDK_PATH=\"$(pwd)/iPhoneSDK\" \\"
        echo "          IOS_TOOLCHAIN_PATH=\"\$HOME/iostoolchain\" \\"
        echo "          ios_triple=\"arm-apple-darwin11-\""
        echo
    fi
}

main() {
    extract_xcode_xip
    extract_ios_sdk
    pack_ios_sdk
    build_cctools_port
    copy_toolchain_binaries
    set_env_and_show_scons
    echo "iOS cross-compiling environment setup steps complete."
}

main

@acarlstein
Copy link
Author

Updated the configure.sh:

#!/usr/bin/env bash
# version: 0.0.5
# description: Automate SDK extraction, toolchain build, and environment setup for Godot iOS cross-compiling.

set -e

AUTO_INSTALL=0

usage() {
    echo "Usage: $0 [--auto]"
    echo "  --auto   Automatically run all steps without prompting."
    exit 1
}

if [[ "$1" == "--auto" ]]; then
    AUTO_INSTALL=1
elif [[ -n "$1" ]]; then
    usage
fi

prompt_step() {
    local msg="$1"
    if [[ "$AUTO_INSTALL" -eq 1 ]]; then
        return 0
    fi
    read -p "$msg [y/N]: " yn
    case "$yn" in
        [Yy]*) return 0 ;;
        *) return 1 ;;
    esac
}

extract_xcode_xip() {
    if prompt_step "Extract Xcode .xip and unpack Xcode.app into \$HOME/xcode?"; then
        # Search for .xip files in current dir and $HOME
        mapfile -t XIP_FILES < <(find . "$HOME" -maxdepth 2 -type f -name "*.xip" 2>/dev/null)
        if [[ ${#XIP_FILES[@]} -eq 0 ]]; then
            echo "No .xip files found. Please provide the path manually."
            read -e -p "Enter path to Xcode .xip file: " XCODE_XIP
        else
            echo "Found the following .xip files:"
            select XIP_CHOICE in "${XIP_FILES[@]}" "Enter path manually"; do
                if [[ "$REPLY" -gt 0 && "$REPLY" -le "${#XIP_FILES[@]}" ]]; then
                    XCODE_XIP="${XIP_FILES[$((REPLY-1))]}"
                    break
                elif [[ "$REPLY" -eq $((${#XIP_FILES[@]}+1)) ]]; then
                    read -e -p "Enter path to Xcode .xip file: " XCODE_XIP
                    break
                else
                    echo "Invalid selection."
                fi
            done
        fi

        XCODE_DIR="$HOME/xcode"
        if [[ -d "$XCODE_DIR" ]]; then
            if prompt_step "Directory $XCODE_DIR already exists. Overwrite?"; then
                rm -rf "$XCODE_DIR"
            else
                echo "Skipping extraction."
                return
            fi
        fi

        mkdir -p "$XCODE_DIR"
        pushd "$XCODE_DIR"
        echo "Extracting Xcode .xip archive (this may take several minutes)..."
        xar -xf "$XCODE_XIP"
        echo "Unpacking pbzx stream (this may take several minutes)..."
        pbzx -n Content | cpio -i
        popd
        echo "Xcode .xip extraction complete in $XCODE_DIR."
    fi
}

extract_ios_sdk() {
    if prompt_step "Extract and prepare iOS SDK from Xcode.app?"; then
        # Try to find iOS SDK directories inside $HOME/xcode/Xcode.app
        mapfile -t SDK_DIRS < <(find "$HOME/xcode/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/" -maxdepth 1 -type d -name "iPhoneOS*.sdk" 2>/dev/null)
        if [[ ${#SDK_DIRS[@]} -eq 0 ]]; then
            echo "No iOS SDK directories found. Please provide the path manually."
            read -e -p "Enter path to iPhoneOS.sdk directory: " SDK_PATH
        else
            echo "Found the following iOS SDK directories:"
            select SDK_CHOICE in "${SDK_DIRS[@]}" "Enter path manually"; do
                if [[ "$REPLY" -gt 0 && "$REPLY" -le "${#SDK_DIRS[@]}" ]]; then
                    SDK_PATH="${SDK_DIRS[$((REPLY-1))]}"
                    break
                elif [[ "$REPLY" -eq $((${#SDK_DIRS[@]}+1)) ]]; then
                    read -e -p "Enter path to iPhoneOS.sdk directory: " SDK_PATH
                    break
                else
                    echo "Invalid selection."
                fi
            done
        fi
        read -e -p "Enter iOS SDK version (e.g., 17.0): " IOS_SDK_VERSION
        export IOS_SDK_VERSION
        mkdir -p iPhoneSDK/iPhoneOS${IOS_SDK_VERSION}.sdk
        cp -r "$SDK_PATH"/* iPhoneSDK/iPhoneOS${IOS_SDK_VERSION}.sdk
        cp -r "$HOME/xcode/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include/c++/"* iPhoneSDK/iPhoneOS${IOS_SDK_VERSION}.sdk/usr/include/c++
        fusermount -u "$HOME/xcode" || true
        echo "iOS SDK extracted to iPhoneSDK/iPhoneOS${IOS_SDK_VERSION}.sdk"
    fi
}

pack_ios_sdk() {
    if prompt_step "Pack the iOS SDK for cctools-port?"; then
        cd iPhoneSDK
        echo "Packing iOS SDK (this may take a while)..."
        tar -cf - * | xz -9 -c - > iPhoneOS${IOS_SDK_VERSION}.sdk.tar.xz
        cd ..
        echo "Packed SDK: iPhoneSDK/iPhoneOS${IOS_SDK_VERSION}.sdk.tar.xz"
    fi
}

build_cctools_port() {
    if prompt_step "Build cctools-port toolchain?"; then
        git clone https://github.com/tpoechtrager/cctools-port.git || true
        cd cctools-port/usage_examples/ios_toolchain
        sudo apt-get install -y automake autogen libtool-bin
        ./build.sh ../../../../iPhoneSDK/iPhoneOS${IOS_SDK_VERSION}.sdk.tar.xz arm64
        cd ../../..
        echo "cctools-port toolchain build complete."
    fi
}

copy_toolchain_binaries() {
    if prompt_step "Copy toolchain binaries to \$HOME/iostoolchain/usr/bin?"; then
        mkdir -p "$HOME/iostoolchain/usr"
        cp -r cctools-port/usage_examples/ios_toolchain/target/bin "$HOME/iostoolchain/usr/"
        echo "Toolchain binaries copied to $HOME/iostoolchain/usr/bin"
    fi
}

set_env_and_show_scons() {
    if prompt_step "Show environment setup and SCons build command?"; then
        echo "You must keep the built toolchain and iPhoneOS SDK directory."
        echo "Set the following environment variable before building:"
        echo
        echo '    export OSXCROSS_IOS="anything"'
        echo
        echo "Example SCons build command:"
        echo
        echo "    scons platform=ios arch=arm64 target=template_release \\"
        echo "          IOS_SDK_PATH=\"$(pwd)/iPhoneSDK\" \\"
        echo "          IOS_TOOLCHAIN_PATH=\"\$HOME/iostoolchain\" \\"
        echo "          ios_triple=\"arm-apple-darwin11-\""
        echo
    fi
}

main() {
    extract_xcode_xip
    extract_ios_sdk
    pack_ios_sdk
    build_cctools_port
    copy_toolchain_binaries
    set_env_and_show_scons
    echo "iOS cross-compiling environment setup steps complete."
}

main

I'm not sure it works yet. I have to create some project in godot and see if I can make it work.
The question I have is how do I deploy to my iphone. Does the command scons take care of that?

@acarlstein
Copy link
Author

Updated configure.sh to create the file build_ios_godot.sh, so the user can do one step less:

#!/usr/bin/env bash
# version: 0.0.6
# description: Automate SDK extraction, toolchain build, and environment setup for Godot iOS cross-compiling.

set -e

AUTO_INSTALL=0

usage() {
    echo "Usage: $0 [--auto]"
    echo "  --auto   Automatically run all steps without prompting."
    exit 1
}

if [[ "$1" == "--auto" ]]; then
    AUTO_INSTALL=1
elif [[ -n "$1" ]]; then
    usage
fi

prompt_step() {
    local msg="$1"
    if [[ "$AUTO_INSTALL" -eq 1 ]]; then
        return 0
    fi
    read -p "$msg [y/N]: " yn
    case "$yn" in
        [Yy]*) return 0 ;;
        *) return 1 ;;
    esac
}

extract_xcode_xip() {
    if prompt_step "Extract Xcode .xip and unpack Xcode.app into \$HOME/xcode?"; then
        # Search for .xip files in current dir and $HOME
        mapfile -t XIP_FILES < <(find . "$HOME" -maxdepth 2 -type f -name "*.xip" 2>/dev/null)
        if [[ ${#XIP_FILES[@]} -eq 0 ]]; then
            echo "No .xip files found. Please provide the path manually."
            read -e -p "Enter path to Xcode .xip file: " XCODE_XIP
        else
            echo "Found the following .xip files:"
            select XIP_CHOICE in "${XIP_FILES[@]}" "Enter path manually"; do
                if [[ "$REPLY" -gt 0 && "$REPLY" -le "${#XIP_FILES[@]}" ]]; then
                    XCODE_XIP="${XIP_FILES[$((REPLY-1))]}"
                    break
                elif [[ "$REPLY" -eq $((${#XIP_FILES[@]}+1)) ]]; then
                    read -e -p "Enter path to Xcode .xip file: " XCODE_XIP
                    break
                else
                    echo "Invalid selection."
                fi
            done
        fi

        XCODE_DIR="$HOME/xcode"
        if [[ -d "$XCODE_DIR" ]]; then
            if prompt_step "Directory $XCODE_DIR already exists. Overwrite?"; then
                rm -rf "$XCODE_DIR"
            else
                echo "Skipping extraction."
                return
            fi
        fi

        mkdir -p "$XCODE_DIR"
        pushd "$XCODE_DIR"
        echo "Extracting Xcode .xip archive (this may take several minutes)..."
        xar -xf "$XCODE_XIP"
        echo "Unpacking pbzx stream (this may take several minutes)..."
        pbzx -n Content | cpio -i
        popd
        echo "Xcode .xip extraction complete in $XCODE_DIR."
    fi
}

extract_ios_sdk() {
    if prompt_step "Extract and prepare iOS SDK from Xcode.app?"; then
        # Try to find iOS SDK directories inside $HOME/xcode/Xcode.app
        mapfile -t SDK_DIRS < <(find "$HOME/xcode/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/" -maxdepth 1 -type d -name "iPhoneOS*.sdk" 2>/dev/null)
        if [[ ${#SDK_DIRS[@]} -eq 0 ]]; then
            echo "No iOS SDK directories found. Please provide the path manually."
            read -e -p "Enter path to iPhoneOS.sdk directory: " SDK_PATH
        else
            echo "Found the following iOS SDK directories:"
            select SDK_CHOICE in "${SDK_DIRS[@]}" "Enter path manually"; do
                if [[ "$REPLY" -gt 0 && "$REPLY" -le "${#SDK_DIRS[@]}" ]]; then
                    SDK_PATH="${SDK_DIRS[$((REPLY-1))]}"
                    break
                elif [[ "$REPLY" -eq $((${#SDK_DIRS[@]}+1)) ]]; then
                    read -e -p "Enter path to iPhoneOS.sdk directory: " SDK_PATH
                    break
                else
                    echo "Invalid selection."
                fi
            done
        fi
        read -e -p "Enter iOS SDK version (e.g., 17.0): " IOS_SDK_VERSION
        export IOS_SDK_VERSION
        mkdir -p iPhoneSDK/iPhoneOS${IOS_SDK_VERSION}.sdk
        cp -r "$SDK_PATH"/* iPhoneSDK/iPhoneOS${IOS_SDK_VERSION}.sdk
        cp -r "$HOME/xcode/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include/c++/"* iPhoneSDK/iPhoneOS${IOS_SDK_VERSION}.sdk/usr/include/c++
        fusermount -u "$HOME/xcode" || true
        echo "iOS SDK extracted to iPhoneSDK/iPhoneOS${IOS_SDK_VERSION}.sdk"
    fi
}

pack_ios_sdk() {
    if prompt_step "Pack the iOS SDK for cctools-port?"; then
        cd iPhoneSDK
        echo "Packing iOS SDK (this may take a while)..."
        tar -cf - * | xz -9 -c - > iPhoneOS${IOS_SDK_VERSION}.sdk.tar.xz
        cd ..
        echo "Packed SDK: iPhoneSDK/iPhoneOS${IOS_SDK_VERSION}.sdk.tar.xz"
    fi
}

build_cctools_port() {
    if prompt_step "Build cctools-port toolchain?"; then
        git clone https://github.com/tpoechtrager/cctools-port.git || true
        cd cctools-port/usage_examples/ios_toolchain
        sudo apt-get install -y automake autogen libtool-bin
        ./build.sh ../../../../iPhoneSDK/iPhoneOS${IOS_SDK_VERSION}.sdk.tar.xz arm64
        cd ../../..
        echo "cctools-port toolchain build complete."
    fi
}

copy_toolchain_binaries() {
    if prompt_step "Copy toolchain binaries to \$HOME/iostoolchain/usr/bin?"; then
        mkdir -p "$HOME/iostoolchain/usr"
        cp -r cctools-port/usage_examples/ios_toolchain/target/bin "$HOME/iostoolchain/usr/"
        echo "Toolchain binaries copied to $HOME/iostoolchain/usr/bin"
    fi
}

set_env_and_show_scons() {
    if prompt_step "Show environment setup and SCons build command?"; then
        echo "You must keep the built toolchain and iPhoneOS SDK directory."
        echo "Set the following environment variable before building:"
        echo
        echo '    export OSXCROSS_IOS="anything"'
        echo
        echo "Example SCons build command:"
        echo
        echo "    scons platform=ios arch=arm64 target=template_release \\"
        echo "          IOS_SDK_PATH=\"$(pwd)/iPhoneSDK\" \\"
        echo "          IOS_TOOLCHAIN_PATH=\"\$HOME/iostoolchain\" \\"
        echo "          ios_triple=\"arm-apple-darwin11-\""
        echo

        if prompt_step "Would you like to generate an executable bash script to run these commands?"; then
            SCRIPT_PATH="./build_ios_godot.sh"
            cat > "$SCRIPT_PATH" <<EOF
#!/usr/bin/env bash
# Auto-generated script to build Godot for iOS

export OSXCROSS_IOS="anything"

scons platform=ios arch=arm64 target=template_release \\
    IOS_SDK_PATH="$(pwd)/iPhoneSDK" \\
    IOS_TOOLCHAIN_PATH="\$HOME/iostoolchain" \\
    ios_triple="arm-apple-darwin11-"
EOF
            chmod +x "$SCRIPT_PATH"
            echo "Executable script created at: $SCRIPT_PATH"
        fi
    fi
}

main() {
    extract_xcode_xip
    extract_ios_sdk
    pack_ios_sdk
    build_cctools_port
    copy_toolchain_binaries
    set_env_and_show_scons
    echo "iOS cross-compiling environment setup steps complete."
}

main

@Calinou Calinou added bug area:manual Issues and PRs related to the Manual/Tutorials section of the documentation topic:buildsystem platform:ios Issues or PRs related specifically to ios labels May 28, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area:manual Issues and PRs related to the Manual/Tutorials section of the documentation bug platform:ios Issues or PRs related specifically to ios topic:buildsystem
Projects
None yet
Development

No branches or pull requests

2 participants