From b9c42eed1faa8f256ffac55f8da21a9e5f08dc3b Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Fri, 18 Sep 2026 18:06:26 +0800 Subject: [PATCH] feat(ios): add iOS simulator support and improve build system - Extended iOS platform support to handle simulator targets with arm64-apple-ios-simulator - Added new ios-simulator.yml configuration for building simulator-compatible executables - Created build-ios.sh script to handle both device and simulator builds with parameter selection - Implemented dynamic SDK directory selection based on target platform (iphoneos vs iphonesimulator) - Updated error messages to reflect generic iOS SDK terminology instead of iPhoneOS specific - Added package-ios-simulator-app.sh for packaging simulator applications without provisioning profiles - Updated README documentation with complete iOS simulator setup and build instructions - Modified codesign commands to include --generate-entitlement-der flag for proper entitlement handling - Adjusted gitignore to exclude new ios-simulator-build directory and simulator executable --- examples/apple-native/.gitignore | 2 + examples/apple-native/README.md | 56 +++++++++++++++++-- examples/apple-native/build-ios.sh | 36 ++++++++++++ examples/apple-native/ios-simulator.yml | 26 +++++++++ examples/apple-native/package-ios-app.sh | 1 + .../apple-native/package-ios-simulator-app.sh | 23 ++++++++ src/Build/NativeBuildConfigurationTrait.php | 13 +++-- src/Platform/Ios.php | 2 +- 8 files changed, 148 insertions(+), 11 deletions(-) create mode 100755 examples/apple-native/build-ios.sh create mode 100644 examples/apple-native/ios-simulator.yml create mode 100755 examples/apple-native/package-ios-simulator-app.sh diff --git a/examples/apple-native/.gitignore b/examples/apple-native/.gitignore index 014c319e..14537b90 100644 --- a/examples/apple-native/.gitignore +++ b/examples/apple-native/.gitignore @@ -1,7 +1,9 @@ /build/ /build-*/ /ios-build/ +/ios-simulator-build/ /dist/ /*.rsp /typephp_macos_hello /typephp_ios_hello +/typephp_ios_simulator_hello diff --git a/examples/apple-native/README.md b/examples/apple-native/README.md index aa58cda6..4fe8198f 100644 --- a/examples/apple-native/README.md +++ b/examples/apple-native/README.md @@ -89,10 +89,10 @@ SDK: ```sh export PHPX_HOME=/path/to/swoole-cli/thirdparty/phpx -php bin/tpc.php examples/objective-c-macos/ios.yml --no-progress +php bin/tpc.php examples/apple-native/ios.yml --no-progress ``` -The compiler produces `examples/objective-c-macos/typephp_ios_hello`. Package +The compiler produces `examples/apple-native/typephp_ios_hello`. Package and sign it with a development provisioning profile and identity installed in the login keychain: @@ -101,7 +101,7 @@ export TYPEPHP_IOS_PROVISIONING_PROFILE=/path/to/profile.mobileprovision export TYPEPHP_IOS_CODE_SIGN_IDENTITY='Apple Development: Your Name (TEAMID)' # Set this when the profile uses a different identifier than the example. export TYPEPHP_IOS_BUNDLE_IDENTIFIER='your.provisioned.bundle.identifier' -sh examples/objective-c-macos/package-ios-app.sh +sh examples/apple-native/package-ios-app.sh ``` The package includes iPhone icon sizes generated from the repository's @@ -109,7 +109,7 @@ The package includes iPhone icon sizes generated from the repository's and run: ```sh -sh examples/objective-c-macos/generate-ios-icons.sh +sh examples/apple-native/generate-ios-icons.sh ``` Find the connected iPhone and install the bundle: @@ -118,8 +118,54 @@ Find the connected iPhone and install the bundle: xcrun devicectl list devices xcrun devicectl device install app \ --device \ - 'examples/objective-c-macos/dist/TypePHP iOS Hello.app' + 'examples/apple-native/dist/TypePHP iOS Hello.app' ``` +## iOS Simulator (Apple silicon) + +The simulator needs archives compiled for `arm64-apple-ios-simulator`. +The physical iPhone SDK above cannot be reused. Build the separate runtime +and PHPX SDK on the Mac: + +```sh +cd /path/to/swoole-cli +php prepare.php @iphonesimulator-arm64 --with-parallel-jobs=8 +./make.sh all-library +./make.sh config +./make.sh libphp +bash sapi/scripts/package-php-runtime-layer.sh iphonesimulator-arm64 + +# Copy the packaged runtime's include/ and lib/ trees into this prefix. +mkdir -p /path/to/phpx/ios/iphonesimulator-arm64 +cp -R runtime-layer/php-runtime-layer_*_iphonesimulator-arm64/include \ + runtime-layer/php-runtime-layer_*_iphonesimulator-arm64/lib \ + /path/to/phpx/ios/iphonesimulator-arm64/ +cp runtime-layer/php-runtime-layer_*_iphonesimulator-arm64/.typephp-php-runtime-abi \ + /path/to/phpx/ios/iphonesimulator-arm64/ +cp var/iphonesimulator-arm64/deps/gmp/lib/libgmp.a \ + var/iphonesimulator-arm64/deps/gmp/lib/libgmpxx.a \ + var/iphonesimulator-arm64/deps/mpfr/lib/libmpfr.a \ + /path/to/phpx/ios/iphonesimulator-arm64/lib/ +cd /path/to/phpx +./ios/build.sh --platform simulator +``` + +Build and install the app with the simulator SDK: + +```sh +cd /path/to/typephp +export PHPX_HOME=/path/to/phpx +sh examples/apple-native/build-ios.sh simulator +xcrun simctl install booted 'examples/apple-native/dist/TypePHP iOS Simulator Hello.app' +xcrun simctl launch booted org.swoole.typephp.ios-simulator-hello +``` + +The simulator bundle uses ad hoc signing and does not need a provisioning +profile or a physical iPhone. +The same source files are used for both targets. Select `device` instead of +`simulator` in `build-ios.sh` to build the physical iPhone executable. Set +`TYPEPHP_IOS_PROVISIONING_PROFILE` and `TYPEPHP_IOS_CODE_SIGN_IDENTITY` to +package and sign the device app in the same command. + The `.mm` bridges are deliberately thin. AppKit/UIKit own native controls and deliver events, but the shared TypePHP class owns the application behavior. diff --git a/examples/apple-native/build-ios.sh b/examples/apple-native/build-ios.sh new file mode 100755 index 00000000..9048aa56 --- /dev/null +++ b/examples/apple-native/build-ios.sh @@ -0,0 +1,36 @@ +#!/bin/sh + +set -eu + +project_dir=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd) +repo_dir=$(CDPATH= cd -- "$project_dir/../.." && pwd) + +if [ -z "${PHPX_HOME:-}" ]; then + echo "Set PHPX_HOME to the PHPX checkout containing both iOS SDK prefixes." >&2 + exit 2 +fi + +case "${1:-}" in + device) + config=ios.yml + package_script=package-ios-app.sh + ;; + simulator) + config=ios-simulator.yml + package_script=package-ios-simulator-app.sh + ;; + *) + echo "Usage: $0 " >&2 + exit 2 + ;; +esac + +cd "$repo_dir" +php bin/tpc.php "examples/apple-native/$config" --no-progress + +if [ "$package_script" = package-ios-app.sh ] && + { [ -z "${TYPEPHP_IOS_PROVISIONING_PROFILE:-}" ] || [ -z "${TYPEPHP_IOS_CODE_SIGN_IDENTITY:-}" ]; }; then + echo "Device executable built. Set the provisioning profile and signing identity, then run $project_dir/$package_script to package it." +else + sh "$project_dir/$package_script" +fi diff --git a/examples/apple-native/ios-simulator.yml b/examples/apple-native/ios-simulator.yml new file mode 100644 index 00000000..5c87c693 --- /dev/null +++ b/examples/apple-native/ios-simulator.yml @@ -0,0 +1,26 @@ +name: typephp_ios_simulator_hello +version: 0.1.0 +mode: bin +output: typephp_ios_simulator_hello +php-version: "8.4" +cxx-std: c++17 +cpp-compiler: xcrun --sdk iphonesimulator clang++ +target-platform: arm64-apple-ios15.0-simulator +build-dir: ios-simulator-build + +sources: + - php-src/application.php + - php-src/ios-main.php + - ios-src + +cxx-flags: + - -fobjc-arc + - -mios-simulator-version-min=15.0 + +ld-flags: + - -mios-simulator-version-min=15.0 + - -Wl,-dead_strip + - -framework UIKit + - -framework Foundation + - -framework CoreGraphics + - -lresolv diff --git a/examples/apple-native/package-ios-app.sh b/examples/apple-native/package-ios-app.sh index a1be434b..5f03fcb6 100755 --- a/examples/apple-native/package-ios-app.sh +++ b/examples/apple-native/package-ios-app.sh @@ -46,6 +46,7 @@ codesign \ --sign "$identity" \ --entitlements "$entitlements" \ --timestamp=none \ + --generate-entitlement-der \ "$bundle" codesign --verify --deep --strict --verbose=2 "$bundle" diff --git a/examples/apple-native/package-ios-simulator-app.sh b/examples/apple-native/package-ios-simulator-app.sh new file mode 100755 index 00000000..4fef2509 --- /dev/null +++ b/examples/apple-native/package-ios-simulator-app.sh @@ -0,0 +1,23 @@ +#!/bin/sh + +set -eu + +project_dir=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd) +executable="$project_dir/typephp_ios_simulator_hello" +bundle="$project_dir/dist/TypePHP iOS Simulator Hello.app" + +if [ ! -x "$executable" ]; then + echo "Missing $executable; build ios-simulator.yml first." >&2 + exit 1 +fi + +mkdir -p "$bundle" +cp "$project_dir/Info-iOS.plist" "$bundle/Info.plist" +plutil -replace CFBundleExecutable -string typephp_ios_simulator_hello "$bundle/Info.plist" +plutil -replace CFBundleIdentifier -string org.swoole.typephp.ios-simulator-hello "$bundle/Info.plist" +cp "$executable" "$bundle/typephp_ios_simulator_hello" +cp "$project_dir"/ios-assets/AppIcon*.png "$bundle/" +codesign --force --sign - --timestamp=none "$bundle" +codesign --verify --deep --strict "$bundle" + +echo "Created $bundle" diff --git a/src/Build/NativeBuildConfigurationTrait.php b/src/Build/NativeBuildConfigurationTrait.php index 4a845319..9866a083 100644 --- a/src/Build/NativeBuildConfigurationTrait.php +++ b/src/Build/NativeBuildConfigurationTrait.php @@ -52,19 +52,22 @@ trait NativeBuildConfigurationTrait protected function getIosSdkDir(): string { - $sdkDir = $this->getPhpxDir() . '/ios/iphoneos-arm64'; + $target = str_ends_with(strtolower($this->targetPlatform), '-simulator') + ? 'iphonesimulator-arm64' + : 'iphoneos-arm64'; + $sdkDir = $this->getPhpxDir() . '/ios/' . $target; if (!is_dir($sdkDir)) { $this->error( - 'The iPhoneOS SDK was not found at: ' . $sdkDir . "\n" + 'The iOS SDK was not found at: ' . $sdkDir . "\n" . ' Build/install the matching SDK inside PHPX before compiling this target.' ); } $abiStamp = $sdkDir . '/.typephp-ios-sdk-abi'; if (!is_file($abiStamp) - || trim((string) file_get_contents($abiStamp)) !== 'typephp-iphoneos-arm64-sdk-abi-v1' + || trim((string) file_get_contents($abiStamp)) !== 'typephp-' . $target . '-sdk-abi-v1' ) { $this->error( - 'The iPhoneOS SDK is missing or ABI-incompatible: ' . $sdkDir . "\n" + 'The iOS SDK is missing or ABI-incompatible: ' . $sdkDir . "\n" . ' Rebuild it with PHPX ios/build.sh and the matching PHP SDK.' ); } @@ -365,7 +368,7 @@ trait NativeBuildConfigurationTrait $buildHint = 'Build PHPX first (for example, run `nmake phpx` in ' . $this->getPhpxDir() . '\\build)'; } elseif ($this->isIosTarget()) { $expected = $this->getPhpDir() . '/lib/libphpx.a'; - $buildHint = 'Build the integrated iPhoneOS SDK in PHPX_HOME/ios/iphoneos-arm64'; + $buildHint = 'Build the matching iOS SDK in PHPX_HOME/ios'; } elseif ($this->isAndroidTarget()) { $expected = $this->getAndroidSdkDir() . '/lib/libphpx.a'; $buildHint = 'Build the Android SDK with PHPX sdk/build-native.sh'; diff --git a/src/Platform/Ios.php b/src/Platform/Ios.php index e2ec7d83..09dfca72 100644 --- a/src/Platform/Ios.php +++ b/src/Platform/Ios.php @@ -12,7 +12,7 @@ class Ios extends UnixPlatform { public static function supportsTarget(string $target): bool { - return preg_match('/^(?:arm64|aarch64)-apple-ios(?:\d+(?:\.\d+)*)?$/i', $target) === 1; + return preg_match('/^(?:arm64|aarch64)-apple-ios(?:\d+(?:\.\d+)*)?(?:-simulator)?$/i', $target) === 1; } public function getName(): string