Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions .github/workflows/test-all.yml
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,8 @@ jobs:
if: github.repository == 'react/react-native'
outputs:
any_code_change: ${{ steps.filter_exclusions.outputs.any_code_change == 'true' || github.event_name != 'pull_request' }}
should_test_android: ${{ steps.filter_exclusions.outputs.should_test_android == 'true' || github.event_name != 'pull_request' }}
should_test_ios: ${{ steps.filter_exclusions.outputs.should_test_ios == 'true' || github.event_name != 'pull_request' }}
should_test_android: ${{ steps.filter_exclusions.outputs.should_test_android == 'true' || steps.filter_inclusions.outputs.react_shared == 'true' || github.event_name != 'pull_request' }}
should_test_ios: ${{ steps.filter_exclusions.outputs.should_test_ios == 'true' || steps.filter_inclusions.outputs.react_shared == 'true' || github.event_name != 'pull_request' }}
debugger_shell: ${{ steps.filter_inclusions.outputs.debugger_shell }}
steps:
- name: Checkout
Expand Down Expand Up @@ -85,6 +85,9 @@ jobs:
id: filter_inclusions
with:
filters: |
# Shared Kotlin and its build configuration affect both platforms.
react_shared:
- 'packages/react-native/ReactShared/**'
debugger_shell:
- 'packages/debugger-shell/**'
- 'scripts/debugger-shell/**'
Expand Down
283 changes: 282 additions & 1 deletion .github/workflows/test-kmp.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
name: Test Kotlin Multiplatform foundation
name: Test Kotlin Multiplatform pilot

on:
workflow_dispatch:
Expand All @@ -7,7 +7,37 @@ on:
- '.github/workflows/test-kmp.yml'
- '.github/actions/setup-gradle/**'
- '.github/actions/setup-xcode/**'
- '.github/actions/setup-node/**'
- '.github/actions/yarn-install/**'
- 'Gemfile'
- 'Gemfile.lock'
- 'build.gradle.kts'
- 'gradle.properties'
- 'gradle/wrapper/**'
- 'settings.gradle.kts'
- 'yarn.lock'
- 'packages/rn-tester/Podfile'
- 'packages/rn-tester/RCTTest/**'
- 'packages/rn-tester/RNTesterPods.xcodeproj/**'
- 'packages/rn-tester/RNTester/RNTester.xctestplan'
- 'packages/react-native/ReactShared/**'
- 'packages/react-native/ReactAndroid/build.gradle.kts'
- 'packages/react-native/ReactAndroid/gradle.properties'
- 'packages/react-native/gradle/libs.versions.toml'
- 'packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/style/ColorStop.kt'
- 'packages/react-native/ReactAndroid/src/test/java/com/facebook/react/uimanager/style/ColorStopTest.kt'
- 'packages/react-native/React/Fabric/Utils/RCTGradientUtils.*'
- 'packages/react-native/React/Fabric/RCTConversions.h'
- 'packages/react-native/Libraries/NativeAnimation/RCTAnimationUtils.*'
- 'packages/react-native/ReactCommon/react/renderer/graphics/**'
- 'packages/react-native/ReactCommon/react/utils/FloatComparison.h'
- 'packages/react-native/ReactCommon/react/utils/ManagedObjectWrapper.*'
- 'packages/react-native/React/React-RCTFabric.podspec'
- 'packages/react-native/scripts/react_native_pods.rb'
- 'packages/react-native/scripts/cocoapods/kmp.rb'
- 'packages/react-native/Package.swift'
- 'packages/react-native/package.json'
- 'packages/react-native/settings.gradle.kts'
push:
branches:
- main
Expand All @@ -16,7 +46,37 @@ on:
- '.github/workflows/test-kmp.yml'
- '.github/actions/setup-gradle/**'
- '.github/actions/setup-xcode/**'
- '.github/actions/setup-node/**'
- '.github/actions/yarn-install/**'
- 'Gemfile'
- 'Gemfile.lock'
- 'build.gradle.kts'
- 'gradle.properties'
- 'gradle/wrapper/**'
- 'settings.gradle.kts'
- 'yarn.lock'
- 'packages/rn-tester/Podfile'
- 'packages/rn-tester/RCTTest/**'
- 'packages/rn-tester/RNTesterPods.xcodeproj/**'
- 'packages/rn-tester/RNTester/RNTester.xctestplan'
- 'packages/react-native/ReactShared/**'
- 'packages/react-native/ReactAndroid/build.gradle.kts'
- 'packages/react-native/ReactAndroid/gradle.properties'
- 'packages/react-native/gradle/libs.versions.toml'
- 'packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/style/ColorStop.kt'
- 'packages/react-native/ReactAndroid/src/test/java/com/facebook/react/uimanager/style/ColorStopTest.kt'
- 'packages/react-native/React/Fabric/Utils/RCTGradientUtils.*'
- 'packages/react-native/React/Fabric/RCTConversions.h'
- 'packages/react-native/Libraries/NativeAnimation/RCTAnimationUtils.*'
- 'packages/react-native/ReactCommon/react/renderer/graphics/**'
- 'packages/react-native/ReactCommon/react/utils/FloatComparison.h'
- 'packages/react-native/ReactCommon/react/utils/ManagedObjectWrapper.*'
- 'packages/react-native/React/React-RCTFabric.podspec'
- 'packages/react-native/scripts/react_native_pods.rb'
- 'packages/react-native/scripts/cocoapods/kmp.rb'
- 'packages/react-native/Package.swift'
- 'packages/react-native/package.json'
- 'packages/react-native/settings.gradle.kts'

permissions:
contents: read
Expand All @@ -26,6 +86,227 @@ concurrency:
cancel-in-progress: true

jobs:
test-shared:
runs-on: macos-26
timeout-minutes: 45
steps:
- name: Checkout
uses: actions/checkout@v6
- name: Set up JDK 17
uses: actions/setup-java@v5
with:
java-version: '17'
distribution: 'zulu'
- name: Set up Xcode for Kotlin Native
uses: ./.github/actions/setup-xcode
with:
xcode-version: '26.4.1'
- name: Set up Gradle
uses: ./.github/actions/setup-gradle
- name: Set up Ruby and CocoaPods
uses: ruby/setup-ruby@v1
with:
ruby-version: '3.2.0'
bundler-cache: true
- name: Check CocoaPods linking configurations
run: bundle exec ruby packages/react-native/ReactShared/scripts/test-cocoapods-linking.rb
- name: Cache Kotlin Native toolchain
uses: actions/cache@v5
with:
path: ~/.konan
key: kmp-native-${{ runner.os }}-${{ runner.arch }}-${{ hashFiles('packages/react-native/ReactShared/*.gradle.kts', 'packages/react-native/ReactShared/gradle.properties', 'packages/react-native/ReactShared/gradle/wrapper/gradle-wrapper.properties') }}
- name: Test shared code on JVM and iOS
id: shared
working-directory: packages/react-native/ReactShared
run: >-
./gradlew
jvmTest iosSimulatorArm64Test
linkDebugFrameworkIosArm64 linkReleaseFrameworkIosArm64
linkDebugFrameworkIosSimulatorArm64 linkReleaseFrameworkIosSimulatorArm64
linkDebugFrameworkIosX64 linkReleaseFrameworkIosX64
--stacktrace --console=plain
- name: Compare the Apple adapter with native gradients
if: ${{ !cancelled() && steps.shared.outcome == 'success' }}
working-directory: packages/react-native/ReactShared
run: ./scripts/test-apple-gradient.sh
- name: Compare optimized Apple gradients
if: ${{ !cancelled() && steps.shared.outcome == 'success' }}
working-directory: packages/react-native/ReactShared
env:
RCT_KMP_BUILD_TYPE: Release
run: ./scripts/test-apple-gradient.sh
- name: Test packaged XCFramework consumption
if: ${{ !cancelled() && steps.shared.outcome == 'success' }}
working-directory: packages/react-native/ReactShared
run: ./scripts/test-apple-distribution.sh
- name: Test independent Kotlin runtime coexistence
if: ${{ !cancelled() && steps.shared.outcome == 'success' }}
working-directory: packages/react-native/ReactShared
run: python3 scripts/test-kotlin-coexistence.py
- name: Measure native gradient costs
if: ${{ !cancelled() && steps.shared.outcome == 'success' }}
working-directory: packages/react-native/ReactShared
run: python3 scripts/benchmark-apple-gradient.py
- name: Measure clean and incremental shared builds
if: ${{ !cancelled() && steps.shared.outcome == 'success' }}
working-directory: packages/react-native/ReactShared
run: python3 scripts/benchmark-kmp-build.py
- name: Upload test reports
if: always()
uses: actions/upload-artifact@v6
with:
name: kotlin-multiplatform-test-results
path: |
packages/react-native/ReactShared/build/reports/tests
packages/react-native/ReactShared/build/test-results
packages/react-native/ReactShared/build/apple-distribution/**/*.json
packages/react-native/ReactShared/build/apple-distribution/**/*.log
packages/react-native/ReactShared/build/apple-distribution/**/consumer.xcresult
packages/react-native/ReactShared/build/kotlin-coexistence/**/*.json
packages/react-native/ReactShared/build/kotlin-coexistence/**/*.log
packages/react-native/ReactShared/build/apple-gradient-benchmark/**/*.json
packages/react-native/ReactShared/build/apple-gradient-benchmark/**/*.log
packages/react-native/ReactShared/build/kmp-build-benchmark/**/*.json
packages/react-native/ReactShared/build/kmp-build-benchmark/**/*.log
if-no-files-found: warn

test-android-consumers:
name: Android Maven and npm source consumers
runs-on: 8-core-ubuntu
timeout-minutes: 90
steps:
- name: Checkout
uses: actions/checkout@v6
- name: Set up Node
uses: ./.github/actions/setup-node
- name: Install JavaScript dependencies
uses: ./.github/actions/yarn-install
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.12'
- name: Set up JDK 17
uses: actions/setup-java@v5
with:
java-version: '17'
distribution: 'zulu'
- name: Set up Gradle
uses: ./.github/actions/setup-gradle
- name: Install Android build SDK
id: android-sdk
run: |
python3 - <<'PY'
import os, pathlib, re, shutil, subprocess, tomllib
versions = tomllib.loads(pathlib.Path('packages/react-native/gradle/libs.versions.toml').read_text())['versions']
sdkmanager = shutil.which('sdkmanager') or str(pathlib.Path(os.environ['ANDROID_HOME']) / 'cmdline-tools/latest/bin/sdkmanager')
packages = subprocess.check_output([sdkmanager, '--list'], text=True)
base = 'platforms;android-' + versions['compileSdk']
# Recent SDKs use a minor version in their package ID (for example android-37.0).
platform = next((name for name in [base + '.0', base] if re.search(r'^\s*' + re.escape(name) + r'\s*\|', packages, re.M)), None)
if platform is None:
raise SystemExit('No Android SDK package matches compileSdk ' + versions['compileSdk'])
subprocess.run([sdkmanager, platform, 'build-tools;' + versions['buildTools']], check=True)
with open(os.environ['GITHUB_OUTPUT'], 'a') as output:
output.write('ndk=' + versions['ndkVersion'] + '\n')
PY
- name: Enable emulator hardware acceleration
run: |
echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' | sudo tee /etc/udev/rules.d/99-kvm4all.rules
sudo udevadm control --reload-rules
sudo udevadm trigger --name-match=kvm
- name: Build and run packaged Android consumers
uses: reactivecircus/android-emulator-runner@v2
with:
api-level: 35
target: google_apis
arch: x86_64
ndk: ${{ steps.android-sdk.outputs.ndk }}
cmake: 3.30.5
cores: 4
disable-animations: true
script: python3 packages/react-native/ReactShared/scripts/test-android-consumers.py --output-dir "$RUNNER_TEMP/kmp-android-consumers" --abi x86_64 --device emulator-5554
- name: Upload consumer reports
if: always()
uses: actions/upload-artifact@v6
with:
name: kmp-android-consumers
path: |
${{ runner.temp }}/kmp-android-consumers/*.log
${{ runner.temp }}/kmp-android-consumers/*.json
if-no-files-found: warn

test-apple-app:
name: RNTester KMP ${{ matrix.platform }} (${{ matrix.linkage }})
runs-on: macos-26
timeout-minutes: 90
strategy:
fail-fast: false
matrix:
include:
- platform: simulator
linkage: libraries
- platform: simulator
linkage: static
- platform: simulator
linkage: dynamic
- platform: device
linkage: libraries
- platform: catalyst
linkage: libraries
env:
RCT_KMP_APP_OUTPUT_DIR: ${{ github.workspace }}/packages/react-native/ReactShared/build/apple-app-results
steps:
- name: Checkout
uses: actions/checkout@v6
- name: Set up Node
uses: ./.github/actions/setup-node
- name: Install JavaScript dependencies
uses: ./.github/actions/yarn-install
- name: Select the Hermes compiler package
run: node scripts/releases/use-hermes-prebuilt.js
- name: Install the selected Hermes compiler
uses: ./.github/actions/yarn-install
- name: Set up JDK 17
uses: actions/setup-java@v5
with:
java-version: '17'
distribution: 'zulu'
- name: Set up Xcode for Kotlin Native
uses: ./.github/actions/setup-xcode
with:
xcode-version: '26.4.1'
- name: Set up Gradle
uses: ./.github/actions/setup-gradle
- name: Set up Ruby and CocoaPods
uses: ruby/setup-ruby@v1
with:
ruby-version: '3.2.0'
bundler-cache: true
- name: Cache Kotlin Native toolchain
uses: actions/cache@v5
with:
path: ~/.konan
key: kmp-native-${{ runner.os }}-${{ runner.arch }}-${{ hashFiles('packages/react-native/ReactShared/*.gradle.kts', 'packages/react-native/ReactShared/gradle.properties', 'packages/react-native/ReactShared/gradle/wrapper/gradle-wrapper.properties') }}
- name: Build and validate the KMP source application
run: |
if [[ '${{ matrix.linkage }}' != libraries ]]; then
export USE_FRAMEWORKS='${{ matrix.linkage }}'
fi
./packages/react-native/ReactShared/scripts/test-apple-app.sh '${{ matrix.platform }}'
- name: Upload application test reports
if: always()
uses: actions/upload-artifact@v6
with:
name: kmp-app-${{ matrix.platform }}-${{ matrix.linkage }}
path: |
${{ env.RCT_KMP_APP_OUTPUT_DIR }}/*.log
${{ env.RCT_KMP_APP_OUTPUT_DIR }}/*.json
${{ env.RCT_KMP_APP_OUTPUT_DIR }}/*.txt
${{ env.RCT_KMP_APP_OUTPUT_DIR }}/*.xcresult
${{ env.RCT_KMP_APP_OUTPUT_DIR }}/Podfile*
${{ env.RCT_KMP_APP_OUTPUT_DIR }}/project.pbxproj
if-no-files-found: warn

smoke:
runs-on: macos-26
timeout-minutes: 45
Expand Down
2 changes: 2 additions & 0 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ nexusPublishing {
tasks.register("clean", Delete::class.java) {
description = "Remove all the build files and intermediate build outputs"
dependsOn(gradle.includedBuild("gradle-plugin").task(":clean"))
dependsOn(gradle.includedBuild("react-native-shared").task(":clean"))
subprojects.forEach {
if (
it.project.plugins.hasPlugin("com.android.library") ||
Expand Down Expand Up @@ -103,6 +104,7 @@ tasks.register("clean", Delete::class.java) {
tasks.register("build") {
description = "Build and test all the React Native relevant projects."
dependsOn(gradle.includedBuild("gradle-plugin").task(":build"))
dependsOn(gradle.includedBuild("react-native-shared").task(":jvmTest"))
}

tasks.register("publishAllToMavenTempLocal") {
Expand Down
4 changes: 4 additions & 0 deletions packages/react-native/Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@
import Foundation
import PackageDescription

if ProcessInfo.processInfo.environment["RCT_USE_KMP"] == "1" {
fatalError("RCT_USE_KMP=1 currently supports CocoaPods source builds only. Run RCT_USE_KMP=1 pod install in the iOS app; the SwiftPM prebuild does not include ReactNativeShared.")
}

let BUILD_FROM_SOURCE = false

// Removing the legacy TurboModule and component interop layers is opt-in while those
Expand Down
Loading
Loading