Skip to content

Commit

Permalink
feat: add xcframework build script for local development
Browse files Browse the repository at this point in the history
  • Loading branch information
okwasniewski committed Oct 2, 2024
1 parent 8df011d commit e44b245
Show file tree
Hide file tree
Showing 5 changed files with 106 additions and 43 deletions.
10 changes: 9 additions & 1 deletion .github/workflows/pr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -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:
Expand Down
4 changes: 4 additions & 0 deletions Apps/BRNPlayground/postinstall.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,10 @@ function postInstall() {
if (os.platform() === "darwin") {
iosCMake();

exec("npm install && npx gulp buildIOSRNTA", {
cwd: "../../Package",
});

console.log(chalk.black.bgCyan("Installing iOS pods..."));
exec("pod install", { cwd: "ios" });
}
Expand Down
3 changes: 2 additions & 1 deletion Modules/@babylonjs/react-native-iosandroid/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ DerivedData
*.ipa
*.xcuserstate
project.xcworkspace
ios/libs

# Android/IntelliJ
#
Expand All @@ -51,4 +52,4 @@ CMakeCache.txt
cmake_install.cmake
ReactNativeBabylon.xcodeproj
*.tgz
jsi
jsi
Original file line number Diff line number Diff line change
Expand Up @@ -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"]
Expand All @@ -17,47 +19,32 @@ 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'
s.vendored_frameworks = "ios/libs/*.xcframework"

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

67 changes: 65 additions & 2 deletions Package/gulpfile.js
Original file line number Diff line number Diff line change
Expand Up @@ -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" : "";
Expand Down Expand Up @@ -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`)
Expand Down Expand Up @@ -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);
Expand All @@ -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;
Expand Down

0 comments on commit e44b245

Please sign in to comment.