Skip to content

Commit d729185

Browse files
authored
feat: add xcframework build script for local development (#654)
1 parent 8df011d commit d729185

File tree

5 files changed

+106
-43
lines changed

5 files changed

+106
-43
lines changed

.github/workflows/pr.yml

+9-1
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,7 @@ jobs:
129129
run: npm install
130130
working-directory: ./Apps/BRNPlayground
131131

132-
- name: Build Windows Bundle
132+
- name: Build iOS Bundle
133133
run: npm run build:ios
134134
working-directory: ./Apps/BRNPlayground
135135

@@ -140,6 +140,14 @@ jobs:
140140
run: npx gulp buildIOSRNTA
141141
working-directory: ./Package
142142

143+
- name: Cache XCFrameworks
144+
uses: actions/cache@v2
145+
with:
146+
path: Modules/@babylonjs/react-native-iosandroid/ios/libs
147+
key: ${{ runner.os }}-xcframeworks-${{ github.sha }}
148+
restore-keys: |
149+
${{ runner.os }}-xcframeworks
150+
143151
test-publish-android-ios:
144152
runs-on: macos-latest
145153
steps:

Apps/BRNPlayground/postinstall.js

+4
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,10 @@ function postInstall() {
3232
if (os.platform() === "darwin") {
3333
iosCMake();
3434

35+
exec("npm install && npx gulp buildIOSRNTA", {
36+
cwd: "../../Package",
37+
});
38+
3539
console.log(chalk.black.bgCyan("Installing iOS pods..."));
3640
exec("pod install", { cwd: "ios" });
3741
}

Modules/@babylonjs/react-native-iosandroid/.gitignore

+2-1
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ DerivedData
2727
*.ipa
2828
*.xcuserstate
2929
project.xcworkspace
30+
ios/libs
3031

3132
# Android/IntelliJ
3233
#
@@ -51,4 +52,4 @@ CMakeCache.txt
5152
cmake_install.cmake
5253
ReactNativeBabylon.xcodeproj
5354
*.tgz
54-
jsi
55+
jsi

Modules/@babylonjs/react-native-iosandroid/react-native-babylon.podspec

+26-39
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ require "json"
22

33
package = JSON.parse(File.read(File.join(__dir__, "package.json")))
44

5+
# This Podspec is used for local development
6+
57
Pod::Spec.new do |s|
68
s.name = "react-native-babylon"
79
s.version = package["version"]
@@ -17,47 +19,32 @@ Pod::Spec.new do |s|
1719
s.requires_arc = true
1820
s.xcconfig = { 'USER_HEADER_SEARCH_PATHS' => '$(inherited) ${PODS_TARGET_SRCROOT}/shared ${PODS_TARGET_SRCROOT}/../react-native/shared' }
1921

20-
s.libraries = 'astc-encoder',
21-
'etc1',
22-
'etc2',
23-
'nvtt',
24-
'squish',
25-
'pvrtc',
26-
'iqa',
27-
'edtaa3',
28-
'tinyexr',
29-
'BabylonNative',
30-
'bgfx',
31-
'bimg',
32-
'bx',
33-
'Canvas',
34-
'GenericCodeGen',
35-
'glslang',
36-
'glslang-default-resource-limits',
37-
'Graphics',
38-
'jsRuntime',
39-
'OGLCompiler',
40-
'OSDependent',
41-
'MachineIndependent',
42-
'napi',
43-
'NativeCamera',
44-
'NativeCapture',
45-
'NativeEngine',
46-
'NativeInput',
47-
'NativeOptimizations',
48-
'NativeTracing',
49-
'NativeXR',
50-
'SPIRV',
51-
'spirv-cross-core',
52-
'spirv-cross-msl',
53-
'tinyexr',
54-
'UrlLib',
55-
'Window',
56-
'XMLHttpRequest',
57-
'xr'
22+
s.vendored_frameworks = "ios/libs/*.xcframework"
5823

5924
s.frameworks = "MetalKit", "ARKit"
6025

61-
s.dependency "React"
26+
# install_modules_dependencies has been defined in RN 0.70
27+
# This check ensure that the library can work on older versions of RN
28+
if defined?(install_modules_dependencies)
29+
install_modules_dependencies(s)
30+
else
31+
s.dependency "React-Core"
32+
33+
# Don't install the dependencies when we run `pod install` in the old architecture.
34+
if new_arch_enabled then
35+
s.compiler_flags = folly_compiler_flags + " -DRCT_NEW_ARCH_ENABLED=1"
36+
s.pod_target_xcconfig = {
37+
"HEADER_SEARCH_PATHS" => "\"$(PODS_ROOT)/boost\"",
38+
"OTHER_CPLUSPLUSFLAGS" => "-DFOLLY_NO_CONFIG -DFOLLY_MOBILE=1 -DFOLLY_USE_LIBCPP=1",
39+
"CLANG_CXX_LANGUAGE_STANDARD" => "c++17"
40+
}
41+
s.dependency "React-Codegen"
42+
s.dependency "RCT-Folly"
43+
s.dependency "RCTRequired"
44+
s.dependency "RCTTypeSafety"
45+
s.dependency "ReactCommon/turbomodule/core"
46+
s.dependency "React-RCTFabric"
47+
end
48+
end
6249
end
6350

Package/gulpfile.js

+65-2
Original file line numberDiff line numberDiff line change
@@ -91,8 +91,6 @@ const buildIphoneSimulator = async () => {
9191
exec('xcodebuild -sdk iphonesimulator -arch x86_64 -configuration Release -project ReactNativeBabylon.xcodeproj -scheme BabylonNative build CODE_SIGNING_ALLOWED=NO', 'iOS/Build');
9292
};
9393

94-
const buildIOS = gulp.series(makeXCodeProj, buildIphoneOS, buildIphoneSimulator);
95-
const buildIOSRNTA = gulp.series(makeXCodeProjRNTA, buildIphoneOS, buildIphoneSimulator);
9694

9795
const buildAndroid = async () => {
9896
const basekitBuildProp = basekitBuild ? "-PBASEKIT_BUILD=1" : "";
@@ -235,6 +233,68 @@ const createIOSUniversalLibs = async () => {
235233
libs.map(lib => exec(`lipo -create iOS/Build/Release-iphoneos/${lib} iOS/Build/Release-iphonesimulator/${lib} -output ${assemblediOSAndroidDir}/ios/libs/${lib}`));
236234
};
237235

236+
const createXCFrameworks = async () => {
237+
if (fs.existsSync('../Modules/@babylonjs/react-native-iosandroid/ios/libs/')) {
238+
console.log('XCFrameworks already exist, skipping creation. If you want to recreate them, delete the ios/libs directory in the react-native-iosandroid module.');
239+
return;
240+
}
241+
242+
const PLATFORMS_MAP = {
243+
'iphoneos': ['arm64'],
244+
'iphonesimulator': ['x86_64', 'arm64'],
245+
};
246+
247+
// Build static libraries for each platform
248+
Object.keys(PLATFORMS_MAP).forEach(platform => {
249+
const archs = PLATFORMS_MAP[platform];
250+
archs.forEach(arch => {
251+
const outputDir = `iOS/Build/${platform}-${arch}`;
252+
shelljs.mkdir('-p', outputDir);
253+
const buildCommand = `xcodebuild -sdk ${platform} -arch ${arch} -configuration Release -project ReactNativeBabylon.xcodeproj -scheme BabylonNative build CODE_SIGNING_ALLOWED=NO BUILD_LIBRARY_FOR_DISTRIBUTION=YES`;
254+
exec(buildCommand, 'iOS/Build');
255+
exec(`mv iOS/Build/Release-${platform}/*.a ${outputDir}`);
256+
exec(`rm -rf iOS/Build/Release-${platform}`);
257+
});
258+
});
259+
260+
// Get the list of libraries
261+
const libs = await readdirAsync(`iOS/Build/iphoneos-arm64`);
262+
263+
// Merge multi arch libraries into single libraries for platform with multiple archs
264+
Object.keys(PLATFORMS_MAP).forEach(platform => {
265+
const archs = PLATFORMS_MAP[platform];
266+
if (archs.length === 1) {
267+
// Copy the single arch library to the output directory
268+
const outputDir = `${assemblediOSAndroidDir}/ios/libs/${platform}`;
269+
shelljs.mkdir('-p', outputDir);
270+
exec(`cp -r iOS/Build/${platform}-${archs[0]}/*.a ${outputDir}`);
271+
return
272+
}
273+
274+
const outputDir = `${assemblediOSAndroidDir}/ios/libs/${platform}`;
275+
shelljs.mkdir('-p', outputDir);
276+
libs.forEach(lib => {
277+
let params = ""
278+
archs.forEach(arch => {
279+
params += ` iOS/Build/${platform}-${arch}/${lib}`
280+
});
281+
exec(`lipo -create ${params} -output ${outputDir}/${lib}`);
282+
});
283+
});
284+
285+
// Create xcframework for each library
286+
libs.forEach(lib => {
287+
const params = Object.keys(PLATFORMS_MAP).map(platform => ` -library ${assemblediOSAndroidDir}/ios/libs/${platform}/${lib}`).join('');
288+
const outputDir = `${assemblediOSAndroidDir}/ios/libs/`;
289+
const libName = lib.split('.')[0];
290+
exec(`xcodebuild -create-xcframework ${params} -output ${outputDir}/${libName}.xcframework`);
291+
});
292+
293+
shelljs.mkdir('-p', '../Modules/@babylonjs/react-native-iosandroid/ios/libs');
294+
exec(`cp -r ${assemblediOSAndroidDir}/ios/libs/*.xcframework ../Modules/@babylonjs/react-native-iosandroid/ios/libs/`);
295+
exec(`rm -rf ${assemblediOSAndroidDir}/ios/libs`);
296+
};
297+
238298
const copyAndroidFiles = async () => {
239299
await new Promise(resolve => {
240300
gulp.src(`${basekitPackagePath}Android/build.gradle`)
@@ -621,6 +681,8 @@ const patchPackageVersion = async () => {
621681

622682
const copyFiles = gulp.parallel(copyIOSAndroidCommonFiles, copyIOSFiles, copyAndroidFiles);
623683

684+
const buildIOS = gulp.series(makeXCodeProj, buildIphoneOS, buildIphoneSimulator);
685+
const buildIOSRNTA = gulp.series(makeXCodeProjRNTA, createXCFrameworks);
624686
const buildTS = gulp.series(patchPackageVersion, copyCommonFiles, copySharedFiles, buildTypeScript, validateAssembled);
625687
const buildIOSAndroid = gulp.series(patchPackageVersion, buildIOS, buildAndroid, createIOSUniversalLibs, copyFiles, validateAssemblediOSAndroid);
626688
const build = gulp.series(buildIOSAndroid, switchToBaseKit, buildIOSAndroid);
@@ -635,6 +697,7 @@ exports.buildIOSRNTA = buildIOSRNTA;
635697
exports.buildAndroid = buildAndroid;
636698
exports.buildAndroidRNTA = buildAndroidRNTA;
637699
exports.createIOSUniversalLibs = createIOSUniversalLibs;
700+
exports.createXCFrameworks = createXCFrameworks;
638701
exports.copyFiles = copyFiles;
639702

640703
exports.clean = clean;

0 commit comments

Comments
 (0)