Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Deferred compilation on Android #4027

Closed
wants to merge 3 commits into from

Conversation

kraenhansen
Copy link
Member

@kraenhansen kraenhansen commented Oct 14, 2021

What, How & Why?

The React Native project doesn't promise ABI / API stability of any shared object libraries shipped via the React Native package. In addition to this the end-user determines the NDK (hence the shared C++ STL) used when building the their app.

In principle this means that we can only publish a prebuilt shared object library file under the following assumptions:

  • Our prebuilt binary is installed alongside a specific subset of React Native versions (any version ABI compatible with the version we used when compiling our prebuilt binary).
  • The end-users app is compiled with the same NDK version that we used when building our prebuilt binary. This ensures ABI stability with the shared object C++ STL shipped with the app.

In practice however:

  • The breakage of the ABI might happen on the next release or ten releases from now, nobody really knows. We might be putting in a lot of work to build resilience towards a scenario that in practice rarely happens. Historically we've been "lucky" that we've mostly been integrating with ABIs of the Android platform and haven't had deep integrations with ABIs exposed by React Native. With the upcoming changes to support Hermes via JSI, we are not allowed that luxury.
  • We can always upgrade the React Native version, recompile and publish a new version of our library as a version of React Native ships with an ABI breaking change. If this happens a lot, it would be a bummer for our users (and our documentation and support crew) that the have to constantly know (possibly by NPM peerDepenency specifications or an NPM post-install script) what versions of Realm JS are compatible with which versions of React Native.

The goal of this PR is to increase resilience towards breaking ABI & API changes of the React Native JavaScript Interface (JSI) on Android. I'm putting this into draft for the time being, as it does complicated our build process and developer experience significantly and puts extra requirements on the end-users setup:

  • An expected version CMake via the SDK manager.
  • Building the app with an alpha release of the Android Gradle plugin. Although I still need to verify that this is the case when building Realm Core from prebuilt binaries. This doesn't seem to be an issue anymore (see "Bump Gradle version to 7.2, Bump Kotlin version to 1.5.31 (9ae3367431 by @svbutko)" in the React Native Changelog)

☑️ ToDos

  • 📝 Changelog entry
  • 📝 Compatibility label is updated or copied from previous entry
  • 🚦 Tests
  • 📝 Public documentation PR created or is not necessary
  • 💥 Breaking label has been applied or is not necessary

If this PR adds or changes public API's:

  • typescript definitions file is updated
  • jsdoc files updated
  • Chrome debug API is updated if API is available on React Native

@kraenhansen kraenhansen self-assigned this Oct 14, 2021
@cla-bot cla-bot bot added the cla: yes label Oct 14, 2021
Copy link
Member

@fealebenpae fealebenpae left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The motivation for defending against ABI breakage is sound. This was an amazingly annoying problem for Node.js pre-NAPI and here we don't even have the infrastructure like nan and node-pre-gyp to soften the blow.

However, one thing to note is that in order for us to be the most resilient against ABI breakage we can't rely on prebuilt Core binaries. There's simply no guarantee that a hypothetical future React Native release just won't pull in a different NDK version than the one Core used at the time of making a release. We might need to look into building everything on the user's machine, which might be just too annoying for the user. And even then we wouldn't have a hard guarantee that the user's environment is configured with the same NDK that Facebook used when making a specific React Native release. We might just need to hope that ABI changes in libc++ between NDK versions are more rare than in JSI.

I realize why deferring compilation is important, but I'm still saddened that we effectively have to introduce a regression in our build system to support it. We won't be able to utilize link-time optimization to make our .so smaller. And on iOS we'll need to again support Xcode and CocoaPods as build systems of the actual SDK.

@@ -39,6 +39,21 @@ set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>")

list(APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake")

set(PACKAGE_ROOT_DIR ${CMAKE_CURRENT_SOURCE_DIR})
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is just equivalent to CMAKE_SOURCE_DIR so we can just use that instead of PACKAGE_ROOT_DIR.


set(REALM_CORE_PLATFORM Android-${ANDROID_ABI})
# TODO: Revising the "devel" part of the following line
set(REALM_CORE_FILENAME realm-${CMAKE_BUILD_TYPE}-v${REALM_CORE_VERSION}-${REALM_CORE_PLATFORM}-devel)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we might want to always use Release here, instead of using CMAKE_BUILD_TYPE, because we might not want debug builds of user applications to bring in the debug build of Core.

else()
set(REALM_CORE_ARCHIVE_PATH ${CMAKE_BINARY_DIR}/${REALM_CORE_FILENAME}.tar.gz)
message(STATUS "Downloading Realm Core prebuilt binaries")
file(DOWNLOAD "https://static.realm.io/downloads/core/${REALM_CORE_FILENAME}.tar.gz" "${REALM_CORE_ARCHIVE_PATH}" STATUS DOWNLOAD_STATUS)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
file(DOWNLOAD "https://static.realm.io/downloads/core/${REALM_CORE_FILENAME}.tar.gz" "${REALM_CORE_ARCHIVE_PATH}" STATUS DOWNLOAD_STATUS)
file(DOWNLOAD "https://static.realm.io/downloads/core/v${REALM_CORE_VERSION}/android/${ANDROID_ABI}/Release/${REALM_CORE_FILENAME}.tar.gz" "${REALM_CORE_ARCHIVE_PATH}" STATUS DOWNLOAD_STATUS)

We currently publish to both locations now, but the previous one is effectively deprecated and at some point we'll just stop publishing there, because dumping a bunch of files every release in the same folder over years doesn't scale.

@@ -92,7 +92,6 @@ stage('build') {
parallelExecutors["Windows ia32 NAPI ${nodeTestVersion}"] = buildWindows(nodeTestVersion, 'ia32')
parallelExecutors["Windows x64 NAPI ${nodeTestVersion}"] = buildWindows(nodeTestVersion, 'x64')

parallelExecutors["Android RN"] = buildAndroid()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It might still be nice to have a CI task that builds the native libraries for all ABIs just to make sure they compile and link on every platform, not just the one we run tests on during CI. I would keep the Android build paths in Jenkins and Actions.

arguments '-DANDROID_STL=c++_shared', '-DREALM_JS_BUILD_CORE_FROM_SOURCE=off'
targets 'realm-js-android'
cppFlags ''
abiFilters 'x86', 'x86_64'
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe add a TODO so we don't forget about these?

@@ -1,3 +1,10 @@
cmake_minimum_required(VERSION 3.18.1)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This needs to be called in the top-level CMakeLists before the project() command, otherwise it means nothing.

@kraenhansen
Copy link
Member Author

Closing this as #6650 is realizing the same goal 🎉

@kraenhansen kraenhansen deleted the kh/deferred-android-compilation branch June 10, 2024 09:29
@github-actions github-actions bot locked as resolved and limited conversation to collaborators Jul 10, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants