#!/usr/bin/env bash # Build the Go static library for iOS using go build -buildmode=c-archive. # # This is the production-grade approach (same as Tailscale's own iOS app). # No gomobile dependency — just the standard Go compiler + Xcode SDK. # # Prerequisites: # - Go 1.23+ # - Xcode with iOS SDK # # Usage: # cd /path/to/tailscale_kit # ./build_go.sh # # Output: ios/TailscaleKit.xcframework (static library xcframework) set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" GO_DIR="$SCRIPT_DIR/ios/Go" BUILD_DIR="$SCRIPT_DIR/ios/build" OUTPUT="$SCRIPT_DIR/ios/TailscaleKit.xcframework" MIN_IOS="14.0" # Omit Tailscale features we don't need — we only use tsnet.Dial() for proxying. OMIT_TAGS="ts_omit_ssh,ts_omit_drive,ts_omit_taildrop,ts_omit_serve" OMIT_TAGS="$OMIT_TAGS,ts_omit_webclient,ts_omit_capture,ts_omit_appconnectors" OMIT_TAGS="$OMIT_TAGS,ts_omit_debug,ts_omit_doctor,ts_omit_portlist,ts_omit_posture" OMIT_TAGS="$OMIT_TAGS,ts_omit_cli,ts_omit_kube,ts_omit_aws,ts_omit_bird" OMIT_TAGS="$OMIT_TAGS,ts_omit_synology,ts_omit_tpm,ts_omit_tap,ts_omit_conn25" OMIT_TAGS="$OMIT_TAGS,ts_omit_qrcodes,ts_omit_relayserver,ts_omit_systray" OMIT_TAGS="$OMIT_TAGS,ts_omit_webbrowser,ts_omit_completion,ts_omit_completion_scripts" OMIT_TAGS="$OMIT_TAGS,ts_omit_colorable,ts_omit_clientupdate,ts_omit_wakeonlan" OMIT_TAGS="$OMIT_TAGS,ts_omit_usermetrics,ts_omit_desktop_sessions,ts_omit_ace" OMIT_TAGS="$OMIT_TAGS,ts_omit_acme,ts_omit_hujsonconf,ts_omit_tailnetlock" OMIT_TAGS="$OMIT_TAGS,ts_omit_netlog,ts_omit_syspolicy,ts_omit_cachenetmap" rm -rf "$BUILD_DIR" "$OUTPUT" mkdir -p "$BUILD_DIR/ios-arm64/Headers" "$BUILD_DIR/ios-arm64-simulator/Headers" cd "$GO_DIR" echo "==> Tidying Go modules..." go mod tidy # --- Build for iOS device (arm64) --- echo "==> Building for iOS device (arm64)..." IPHONEOS_SDK=$(xcrun --sdk iphoneos --show-sdk-path) IPHONEOS_CC=$(xcrun --sdk iphoneos --find clang) CGO_ENABLED=1 \ GOOS=ios \ GOARCH=arm64 \ CC="$IPHONEOS_CC" \ CGO_CFLAGS="-isysroot $IPHONEOS_SDK -arch arm64 -miphoneos-version-min=$MIN_IOS" \ CGO_LDFLAGS="-isysroot $IPHONEOS_SDK -arch arm64 -miphoneos-version-min=$MIN_IOS" \ go build \ -buildmode=c-archive \ -tags "$OMIT_TAGS" \ -ldflags="-s -w" \ -o "$BUILD_DIR/ios-arm64/libtailscale.a" \ . # Helper: create a framework bundle from a static library create_framework() { local lib_path="$1" local header_path="$2" local framework_dir="$3" mkdir -p "$framework_dir/Headers" "$framework_dir/Modules" # Copy the static library as the framework binary cp "$lib_path" "$framework_dir/TailscaleKit" # Copy and clean the header (remove Go-internal types) cp "$header_path" "$framework_dir/Headers/tailscale.h" # Module map cat > "$framework_dir/Modules/module.modulemap" <<'MODMAP' framework module TailscaleKit { header "tailscale.h" export * } MODMAP # Info.plist cat > "$framework_dir/Info.plist" < CFBundleIdentifier com.constellation-heating.TailscaleKit CFBundleName TailscaleKit CFBundleVersion 1 CFBundlePackageType FMWK PLIST } create_framework \ "$BUILD_DIR/ios-arm64/libtailscale.a" \ "$BUILD_DIR/ios-arm64/libtailscale.h" \ "$BUILD_DIR/ios-arm64/TailscaleKit.framework" # --- Build for iOS simulator (arm64) --- echo "==> Building for iOS simulator (arm64)..." SIMULATOR_SDK=$(xcrun --sdk iphonesimulator --show-sdk-path) SIMULATOR_CC=$(xcrun --sdk iphonesimulator --find clang) CGO_ENABLED=1 \ GOOS=ios \ GOARCH=arm64 \ CC="$SIMULATOR_CC" \ CGO_CFLAGS="-isysroot $SIMULATOR_SDK -arch arm64 -miphoneos-version-min=$MIN_IOS -target arm64-apple-ios${MIN_IOS}-simulator" \ CGO_LDFLAGS="-isysroot $SIMULATOR_SDK -arch arm64 -miphoneos-version-min=$MIN_IOS -target arm64-apple-ios${MIN_IOS}-simulator" \ go build \ -buildmode=c-archive \ -tags "$OMIT_TAGS" \ -ldflags="-s -w" \ -o "$BUILD_DIR/ios-arm64-simulator/libtailscale.a" \ . create_framework \ "$BUILD_DIR/ios-arm64-simulator/libtailscale.a" \ "$BUILD_DIR/ios-arm64-simulator/libtailscale.h" \ "$BUILD_DIR/ios-arm64-simulator/TailscaleKit.framework" # --- Create xcframework from framework bundles --- echo "==> Creating xcframework..." xcodebuild -create-xcframework \ -framework "$BUILD_DIR/ios-arm64/TailscaleKit.framework" \ -framework "$BUILD_DIR/ios-arm64-simulator/TailscaleKit.framework" \ -output "$OUTPUT" # Clean up build artifacts rm -rf "$BUILD_DIR" # Also copy the header to Classes/ for CocoaPods umbrella header cp "$OUTPUT/ios-arm64/TailscaleKit.framework/Headers/tailscale.h" "$SCRIPT_DIR/ios/Classes/tailscale.h" echo "==> Built: $OUTPUT" du -sh "$OUTPUT" echo "==> Device framework:" ls -lh "$OUTPUT/ios-arm64/TailscaleKit.framework/TailscaleKit"