From feea1c5c15535aa35a2b37ff10ac750b0de05ab5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Oskar=20Kwas=CC=81niewski?= Date: Thu, 19 Sep 2024 16:01:59 +0200 Subject: [PATCH] [wip] xcframework build script --- .github/workflows/pr.yml | 10 +- .../react-native-iosandroid/.gitignore | 3 +- .../react-native-babylon.podspec | 106 +++++++++++------- Package/gulpfile.js | 67 ++++++++++- 4 files changed, 143 insertions(+), 43 deletions(-) diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml index 3284112a0..756b946a3 100644 --- a/.github/workflows/pr.yml +++ b/.github/workflows/pr.yml @@ -129,7 +129,7 @@ jobs: run: npm install working-directory: ./Apps/BRNPlayground - - name: Build Windows Bundle + - name: Build iOS Bundle run: npm run build:ios working-directory: ./Apps/BRNPlayground @@ -140,6 +140,14 @@ jobs: run: npx gulp buildIOSRNTA working-directory: ./Package + - name: Cache XCFrameworks + uses: actions/cache@v2 + with: + path: Modules/@babylonjs/react-native-iosandroid/ios/libs + key: ${{ runner.os }}-xcframeworks-${{ github.sha }} + restore-keys: | + ${{ runner.os }}-xcframeworks + test-publish-android-ios: runs-on: macos-latest steps: diff --git a/Modules/@babylonjs/react-native-iosandroid/.gitignore b/Modules/@babylonjs/react-native-iosandroid/.gitignore index 60cd6473d..864834c5c 100644 --- a/Modules/@babylonjs/react-native-iosandroid/.gitignore +++ b/Modules/@babylonjs/react-native-iosandroid/.gitignore @@ -27,6 +27,7 @@ DerivedData *.ipa *.xcuserstate project.xcworkspace +ios/libs # Android/IntelliJ # @@ -51,4 +52,4 @@ CMakeCache.txt cmake_install.cmake ReactNativeBabylon.xcodeproj *.tgz -jsi \ No newline at end of file +jsi diff --git a/Modules/@babylonjs/react-native-iosandroid/react-native-babylon.podspec b/Modules/@babylonjs/react-native-iosandroid/react-native-babylon.podspec index 3149a07b1..db7ae2435 100644 --- a/Modules/@babylonjs/react-native-iosandroid/react-native-babylon.podspec +++ b/Modules/@babylonjs/react-native-iosandroid/react-native-babylon.podspec @@ -2,6 +2,8 @@ require "json" package = JSON.parse(File.read(File.join(__dir__, "package.json"))) +# This Podspec is used for local development + Pod::Spec.new do |s| s.name = "react-native-babylon" s.version = package["version"] @@ -17,47 +19,73 @@ Pod::Spec.new do |s| s.requires_arc = true s.xcconfig = { 'USER_HEADER_SEARCH_PATHS' => '$(inherited) ${PODS_TARGET_SRCROOT}/shared ${PODS_TARGET_SRCROOT}/../react-native/shared' } - s.libraries = 'astc-encoder', - 'etc1', - 'etc2', - 'nvtt', - 'squish', - 'pvrtc', - 'iqa', - 'edtaa3', - 'tinyexr', - 'BabylonNative', - 'bgfx', - 'bimg', - 'bx', - 'Canvas', - 'GenericCodeGen', - 'glslang', - 'glslang-default-resource-limits', - 'Graphics', - 'jsRuntime', - 'OGLCompiler', - 'OSDependent', - 'MachineIndependent', - 'napi', - 'NativeCamera', - 'NativeCapture', - 'NativeEngine', - 'NativeInput', - 'NativeOptimizations', - 'NativeTracing', - 'NativeXR', - 'SPIRV', - 'spirv-cross-core', - 'spirv-cross-msl', - 'tinyexr', - 'UrlLib', - 'Window', - 'XMLHttpRequest', - 'xr' + if ENV['BUILD_BABYLON_FROM_SOURCE'] == 'true' then + s.libraries = 'astc-encoder', + 'etc1', + 'etc2', + 'nvtt', + 'squish', + 'pvrtc', + 'iqa', + 'edtaa3', + 'tinyexr', + 'BabylonNative', + 'bgfx', + 'bimg', + 'bx', + 'Canvas', + 'GenericCodeGen', + 'glslang', + 'glslang-default-resource-limits', + 'Graphics', + 'jsRuntime', + 'OGLCompiler', + 'OSDependent', + 'MachineIndependent', + 'napi', + 'NativeCamera', + 'NativeCapture', + 'NativeEngine', + 'NativeInput', + 'NativeOptimizations', + 'NativeTracing', + 'NativeXR', + 'SPIRV', + 'spirv-cross-core', + 'spirv-cross-msl', + 'tinyexr', + 'UrlLib', + 'Window', + 'XMLHttpRequest', + 'xr' + else + s.vendored_frameworks = "ios/libs/*.xcframework" + end s.frameworks = "MetalKit", "ARKit" - s.dependency "React" + # install_modules_dependencies has been defined in RN 0.70 + # This check ensure that the library can work on older versions of RN + if defined?(install_modules_dependencies) + install_modules_dependencies(s) + else + s.dependency "React-Core" + + # Don't install the dependencies when we run `pod install` in the old architecture. + if new_arch_enabled then + s.compiler_flags = folly_compiler_flags + " -DRCT_NEW_ARCH_ENABLED=1" + s.pod_target_xcconfig = { + "HEADER_SEARCH_PATHS" => "\"$(PODS_ROOT)/boost\"", + "OTHER_CPLUSPLUSFLAGS" => "-DFOLLY_NO_CONFIG -DFOLLY_MOBILE=1 -DFOLLY_USE_LIBCPP=1", + "CLANG_CXX_LANGUAGE_STANDARD" => "c++17" + } + s.dependency "React-Codegen" + s.dependency "RCT-Folly" + s.dependency "RCTRequired" + s.dependency "RCTTypeSafety" + s.dependency "ReactCommon/turbomodule/core" + s.dependency "React-RCTFabric" + end + end end diff --git a/Package/gulpfile.js b/Package/gulpfile.js index 928a743f5..5397fbbfd 100644 --- a/Package/gulpfile.js +++ b/Package/gulpfile.js @@ -91,8 +91,6 @@ const buildIphoneSimulator = async () => { exec('xcodebuild -sdk iphonesimulator -arch x86_64 -configuration Release -project ReactNativeBabylon.xcodeproj -scheme BabylonNative build CODE_SIGNING_ALLOWED=NO', 'iOS/Build'); }; -const buildIOS = gulp.series(makeXCodeProj, buildIphoneOS, buildIphoneSimulator); -const buildIOSRNTA = gulp.series(makeXCodeProjRNTA, buildIphoneOS, buildIphoneSimulator); const buildAndroid = async () => { const basekitBuildProp = basekitBuild ? "-PBASEKIT_BUILD=1" : ""; @@ -235,6 +233,68 @@ const createIOSUniversalLibs = async () => { libs.map(lib => exec(`lipo -create iOS/Build/Release-iphoneos/${lib} iOS/Build/Release-iphonesimulator/${lib} -output ${assemblediOSAndroidDir}/ios/libs/${lib}`)); }; +const createXCFrameworks = async () => { + if (fs.existsSync('../Modules/@babylonjs/react-native-iosandroid/ios/libs/')) { + console.log('XCFrameworks already exist, skipping creation. If you want to recreate them, delete the ios/libs directory in the react-native-iosandroid module.'); + return; + } + + const PLATFORMS_MAP = { + 'iphoneos': ['arm64'], + 'iphonesimulator': ['x86_64', 'arm64'], + }; + + // Build static libraries for each platform + Object.keys(PLATFORMS_MAP).forEach(platform => { + const archs = PLATFORMS_MAP[platform]; + archs.forEach(arch => { + const outputDir = `iOS/Build/${platform}-${arch}`; + shelljs.mkdir('-p', outputDir); + const buildCommand = `xcodebuild -sdk ${platform} -arch ${arch} -configuration Release -project ReactNativeBabylon.xcodeproj -scheme BabylonNative build CODE_SIGNING_ALLOWED=NO BUILD_LIBRARY_FOR_DISTRIBUTION=YES`; + exec(buildCommand, 'iOS/Build'); + exec(`mv iOS/Build/Release-${platform}/*.a ${outputDir}`); + exec(`rm -rf iOS/Build/Release-${platform}`); + }); + }); + + // Get the list of libraries + const libs = await readdirAsync(`iOS/Build/iphoneos-arm64`); + + // Merge multi arch libraries into single libraries for platform with multiple archs + Object.keys(PLATFORMS_MAP).forEach(platform => { + const archs = PLATFORMS_MAP[platform]; + if (archs.length === 1) { + // Copy the single arch library to the output directory + const outputDir = `${assemblediOSAndroidDir}/ios/libs/${platform}`; + shelljs.mkdir('-p', outputDir); + exec(`cp -r iOS/Build/${platform}-${archs[0]}/*.a ${outputDir}`); + return + } + + const outputDir = `${assemblediOSAndroidDir}/ios/libs/${platform}`; + shelljs.mkdir('-p', outputDir); + libs.forEach(lib => { + let params = "" + archs.forEach(arch => { + params += ` iOS/Build/${platform}-${arch}/${lib}` + }); + exec(`lipo -create ${params} -output ${outputDir}/${lib}`); + }); + }); + + // Create xcframework for each library + libs.forEach(lib => { + const params = Object.keys(PLATFORMS_MAP).map(platform => ` -library ${assemblediOSAndroidDir}/ios/libs/${platform}/${lib}`).join(''); + const outputDir = `${assemblediOSAndroidDir}/ios/libs/`; + const libName = lib.split('.')[0]; + exec(`xcodebuild -create-xcframework ${params} -output ${outputDir}/${libName}.xcframework`); + }); + + shelljs.mkdir('-p', '../Modules/@babylonjs/react-native-iosandroid/ios/libs'); + exec(`cp -r ${assemblediOSAndroidDir}/ios/libs/*.xcframework ../Modules/@babylonjs/react-native-iosandroid/ios/libs/`); + exec(`rm -rf ${assemblediOSAndroidDir}/ios/libs`); +}; + const copyAndroidFiles = async () => { await new Promise(resolve => { gulp.src(`${basekitPackagePath}Android/build.gradle`) @@ -621,6 +681,8 @@ const patchPackageVersion = async () => { const copyFiles = gulp.parallel(copyIOSAndroidCommonFiles, copyIOSFiles, copyAndroidFiles); +const buildIOS = gulp.series(makeXCodeProj, buildIphoneOS, buildIphoneSimulator); +const buildIOSRNTA = gulp.series(makeXCodeProjRNTA, createXCFrameworks); const buildTS = gulp.series(patchPackageVersion, copyCommonFiles, copySharedFiles, buildTypeScript, validateAssembled); const buildIOSAndroid = gulp.series(patchPackageVersion, buildIOS, buildAndroid, createIOSUniversalLibs, copyFiles, validateAssemblediOSAndroid); const build = gulp.series(buildIOSAndroid, switchToBaseKit, buildIOSAndroid); @@ -635,6 +697,7 @@ exports.buildIOSRNTA = buildIOSRNTA; exports.buildAndroid = buildAndroid; exports.buildAndroidRNTA = buildAndroidRNTA; exports.createIOSUniversalLibs = createIOSUniversalLibs; +exports.createXCFrameworks = createXCFrameworks; exports.copyFiles = copyFiles; exports.clean = clean;