diff --git a/native/android/app/CMakeLists.txt b/native/android/app/CMakeLists.txt --- a/native/android/app/CMakeLists.txt +++ b/native/android/app/CMakeLists.txt @@ -2,7 +2,7 @@ # documentation: https://d.android.com/studio/projects/add-native-code.html project(comm CXX C) -set(CMAKE_CXX_STANDARD 14) +set(CMAKE_CXX_STANDARD 17) # C0103 is a naming convention, but the variable names which need to be set # are determined by the upstream project @@ -76,9 +76,40 @@ set(_node_modules_dir ${CMAKE_CURRENT_SOURCE_DIR}/../../node_modules) set(_react_native_dir ${_node_modules_dir}/react-native) -string(TOLOWER ${CMAKE_BUILD_TYPE} CMAKE_BUILD_TYPE_LOWER) +file(GLOB SQLCIPHER + "${_node_modules_dir}/@commapp/sqlcipher-amalgamation/src/*.c" +) +file(GLOB ANDROID_NATIVE_CODE "./src/cpp/*.cpp") +file(GLOB_RECURSE COMMON_NATIVE_CODE "../../cpp/CommonCpp/**/*.cpp") +add_library( + # Sets the name of the library + ${PACKAGE_NAME} -include_directories( + # Sets the library as a shared library + SHARED + + # React dependencies + ${_react_native_dir}/ReactCommon/jsi/jsi/jsi.cpp + ${_react_native_dir}/ReactAndroid/src/main/java/com/facebook/react/turbomodule/core/jni/ReactCommon/CallInvokerHolder.cpp + ${_react_native_dir}/ReactCommon/react/nativemodule/core/ReactCommon/TurboModule.cpp + ${_react_native_dir}/ReactCommon/react/bridging/LongLivedObject.cpp + ${_react_native_dir}/ReactCommon/react/nativemodule/core/ReactCommon/TurboModuleUtils.cpp + + # Third party dependencies + ${SQLCIPHER} + + # comm code + ${ANDROID_NATIVE_CODE} + ${COMMON_NATIVE_CODE} +) + +set(BUILD_DIR ${CMAKE_SOURCE_DIR}/build) + +target_include_directories( + ${PACKAGE_NAME} + PRIVATE + + # React Native ${_react_native_dir}/React ${_react_native_dir}/React/Base ${_react_native_dir}/ReactCommon @@ -99,9 +130,9 @@ ../headers # external libs - ${_third_party_dir}/folly - ${_third_party_dir}/boost/boost - ${_third_party_dir}/double-conversion + "${_third_party_dir}/folly" + "${_third_party_dir}/boost/boost_${BOOST_VERSION}" + "${_third_party_dir}/double-conversion" # comm android specific code ./src/cpp @@ -121,72 +152,30 @@ ${native_rust_library_include_dir} ) -# search for all cpp files in this directory -file(GLOB SQLCIPHER - "${_node_modules_dir}/@commapp/sqlcipher-amalgamation/src/*.c" -) -file(GLOB_RECURSE COMMON_NATIVE_CODE "../../cpp/CommonCpp/**/*.cpp") -file(GLOB ANDROID_NATIVE_CODE "./src/cpp/*.cpp") -file(GLOB DOUBLE_CONVERSION_SOURCES - "${_third_party_dir}/double-conversion/double-conversion/*.cc" +file(GLOB LIBRN_DIR "${REACT_NATIVE_SO_DIR}/${ANDROID_ABI}") +if (NOT LIBRN_DIR) + # If /${ANDROID_ABI} dir not found, then ${REACT_NATIVE_SO_DIR} is probably: + # ReactAndroid/build/react-ndk/exported + file(GLOB LIBRN_DIR "${REACT_NATIVE_SO_DIR}") +endif () + +find_library( + folly-lib + folly_runtime + PATHS ${LIBRN_DIR} + NO_CMAKE_FIND_ROOT_PATH ) -add_library( - # Sets the name of the library. +target_compile_options( ${PACKAGE_NAME} - - # Sets the library as a shared library. - SHARED - - # Provides a relative path to your source file(s). - ${_react_native_dir}/ReactCommon/jsi/jsi/jsi.cpp - ${_react_native_dir}/ReactAndroid/src/main/java/com/facebook/react/turbomodule/core/jni/ReactCommon/CallInvokerHolder.cpp - ${_react_native_dir}/ReactCommon/react/nativemodule/core/ReactCommon/TurboModule.cpp - ${_react_native_dir}/ReactCommon/react/bridging/LongLivedObject.cpp - ${_react_native_dir}/ReactCommon/react/nativemodule/core/ReactCommon/TurboModuleUtils.cpp - - # SQLCipher - ${SQLCIPHER} - - # folly - ${_third_party_dir}/folly/folly/detail/Futex.cpp - ${_third_party_dir}/folly/folly/synchronization/ParkingLot.cpp - ${_third_party_dir}/folly/folly/lang/SafeAssert.cpp - ${_third_party_dir}/folly/folly/FileUtil.cpp - ${_third_party_dir}/folly/folly/Subprocess.cpp - ${_third_party_dir}/folly/folly/File.cpp - ${_third_party_dir}/folly/folly/Format.cpp - ${_third_party_dir}/folly/folly/Conv.cpp - ${_third_party_dir}/folly/folly/io/IOBuf.cpp - ${_third_party_dir}/folly/folly/memory/detail/MallocImpl.cpp - ${_third_party_dir}/folly/folly/ScopeGuard.cpp - ${_third_party_dir}/folly/folly/hash/SpookyHashV2.cpp - ${_third_party_dir}/folly/folly/io/IOBufQueue.cpp - ${_third_party_dir}/folly/folly/lang/Assume.cpp - ${_third_party_dir}/folly/folly/String.cpp - ${_third_party_dir}/folly/folly/portability/SysUio.cpp - ${_third_party_dir}/folly/folly/net/NetOps.cpp - ${_third_party_dir}/folly/folly/dynamic.cpp - ${_third_party_dir}/folly/folly/json.cpp - ${_third_party_dir}/folly/folly/json_pointer.cpp - ${_third_party_dir}/folly/folly/Unicode.cpp - - # double-conversion - ${DOUBLE_CONVERSION_SOURCES} - - # comm code - ${ANDROID_NATIVE_CODE} - ${COMMON_NATIVE_CODE} -) - -add_definitions( - # Folly - -DFOLLY_NO_CONFIG=1 + PRIVATE -DFOLLY_NO_CONFIG=1 -DFOLLY_HAVE_CLOCK_GETTIME=1 -DFOLLY_HAVE_MEMRCHR=1 -DFOLLY_USE_LIBCPP=1 -DFOLLY_MOBILE=1 +) +add_definitions( # SQLCipher -DSQLITE_THREADSAFE=0 -DSQLITE_HAS_CODEC @@ -229,6 +218,7 @@ fbjni::fbjni android ${log-lib} + ${folly-lib} glog::glog olm openssl-crypto diff --git a/native/android/app/build.gradle b/native/android/app/build.gradle --- a/native/android/app/build.gradle +++ b/native/android/app/build.gradle @@ -136,16 +136,21 @@ def downloadsDir = customDownloadsDir ? new File(customDownloadsDir) : new File("$buildDir/downloads") def thirdPartyNdkDir = new File("$buildDir/third-party-ndk") -// The Boost library is a very large download (>100MB). -// If Boost is already present on your system, define the REACT_NATIVE_BOOST_PATH env variable -// and the build will use that. -def boostPath = dependenciesPath ?: System.getenv("REACT_NATIVE_BOOST_PATH") - task createNativeDepsDirectories { downloadsDir.mkdirs() thirdPartyNdkDir.mkdirs() } +def REACT_NATIVE_DIR = new File(["node", "--print", "require.resolve('react-native/package.json')"].execute(null, rootDir).text.trim()).parent +def reactNativeThirdParty = new File("$REACT_NATIVE_DIR/ReactAndroid/src/main/jni/third-party") + +def reactProperties = new Properties() +file("$REACT_NATIVE_DIR/ReactAndroid/gradle.properties").withInputStream { reactProperties.load(it) } + +def FOLLY_VERSION = reactProperties.getProperty("FOLLY_VERSION") +def BOOST_VERSION = reactProperties.getProperty("BOOST_VERSION") +def DOUBLE_CONVERSION_VERSION = reactProperties.getProperty("DOUBLE_CONVERSION_VERSION") + // FOLLY task downloadFolly(dependsOn: createNativeDepsDirectories, type: Download) { src("https://github.com/facebook/folly/archive/v${FOLLY_VERSION}.tar.gz") @@ -154,35 +159,11 @@ dest(new File(downloadsDir, "folly-${FOLLY_VERSION}.tar.gz")) } -def follyFindWrapNoInt = 'return int\\(wrapNoInt\\(open, name, flags, mode\\)\\);' -def follyReplaceWrapNoInt = ''' - ssize_t r; - do { - r = open(name, flags, mode); - } while (r == -1 && errno == EINTR); - return r; -''' -def follyFindTableSize = 'for \\(int fd = getdtablesize\\(\\) - 1' -def follyReplaceTableSize = 'for (int fd = sysconf(_SC_OPEN_MAX) - 1' - -task prepareFolly(dependsOn: dependenciesPath ? [] : [downloadFolly], type: Copy) { - inputs.properties([ - 'findWrapNoInt': follyFindWrapNoInt, - 'replaceWrapNoInt': follyReplaceWrapNoInt, - 'findTableSize': follyFindTableSize, - 'replaceTableSize': follyReplaceTableSize, - ]) - from(dependenciesPath ?: tarTree(downloadFolly.dest)) - include("folly-${FOLLY_VERSION}/folly/**/*") +task prepareFolly(dependsOn: [downloadFolly], type: Copy) { + from(tarTree(downloadFolly.dest)) + from("$reactNativeThirdParty/folly/Android.mk") + include("folly-${FOLLY_VERSION}/folly/**/*", "Android.mk") eachFile { fname -> fname.path = (fname.path - "folly-${FOLLY_VERSION}/") } - - // Fixes problem with Folly failing to build on certain systems. See - // https://github.com/facebook/react-native/issues/28298 - filter { line -> line.replaceAll(follyFindWrapNoInt, follyReplaceWrapNoInt) } - // fix undeclared identifier 'getdtablesize' in Subprocess.cpp - // https://stackoverflow.com/questions/8225186/portable-equivalent-of-open-max/8225235#8225235 - filter { line -> line.replaceAll(follyFindTableSize, follyReplaceTableSize) } - includeEmptyDirs = false into("$thirdPartyNdkDir/folly") } @@ -203,31 +184,27 @@ } // BOOST + +// The Boost library is a very large download (>100MB). +// If Boost is already present on your system, define the REACT_NATIVE_BOOST_PATH env variable +// and the build will use that. +def boostPath = dependenciesPath ?: System.getenv("REACT_NATIVE_BOOST_PATH") + task downloadBoost(dependsOn: createNativeDepsDirectories, type: Download) { - src("https://github.com/react-native-community/boost-for-react-native/releases/download/v${BOOST_VERSION.replace("_", ".")}-0/boost_${BOOST_VERSION}.tar.gz") + src("https://boostorg.jfrog.io/artifactory/main/release/${BOOST_VERSION.replace("_", ".")}/source/boost_${BOOST_VERSION}.tar.gz") onlyIfNewer(true) overwrite(false) dest(new File(downloadsDir, "boost_${BOOST_VERSION}.tar.gz")) } -task prepareBoost(dependsOn: boostPath ? [] : [downloadBoost]) { - inputs.properties([ - 'boost.version': BOOST_VERSION - ]) - outputs.dir("$thirdPartyNdkDir/boost/boost/") - .withPropertyName('boost.output') - doFirst { - copy { - from(boostPath ?: tarTree(resources.gzip(downloadBoost.dest))) - include("boost_${BOOST_VERSION}/boost/**/*.hpp", "boost/boost/**/*.hpp") - include("boost_${BOOST_VERSION}/boost/**/*.ipp", "boost/boost/**/*.ipp") - include("boost_${BOOST_VERSION}/boost/**/*.h", "boost/boost/**/*.h") - includeEmptyDirs = false - into("$thirdPartyNdkDir/boost") - } - } +task prepareBoost(dependsOn: [downloadBoost], type: Copy) { + from(tarTree(resources.gzip(downloadBoost.dest))) + from("$reactNativeThirdParty/boost/Android.mk") + include("Android.mk", "boost_${BOOST_VERSION}/boost/**/*.hpp", "boost/boost/**/*.hpp") + includeEmptyDirs = false + into("$thirdPartyNdkDir/boost") doLast { - file("$thirdPartyNdkDir/boost/boost_${BOOST_VERSION}").renameTo("$thirdPartyNdkDir/boost/boost") + file("$thirdPartyNdkDir/boost/boost").renameTo("$thirdPartyNdkDir/boost/boost_${BOOST_VERSION}") } } @@ -239,9 +216,9 @@ dest(new File(downloadsDir, "double-conversion-${DOUBLE_CONVERSION_VERSION}.tar.gz")) } -task prepareDoubleConversion(dependsOn: dependenciesPath ? [] : [downloadDoubleConversion], type: Copy) { - from(dependenciesPath ?: tarTree(downloadDoubleConversion.dest)) - from("src/main/jni/third-party/double-conversion/Android.mk") +task prepareDoubleConversion(dependsOn: [downloadDoubleConversion], type: Copy) { + from(tarTree(downloadDoubleConversion.dest)) + from("$reactNativeThirdParty/double-conversion/Android.mk") include("double-conversion-${DOUBLE_CONVERSION_VERSION}/src/**/*", "Android.mk") filesMatching("*/src/**/*", { fname -> fname.path = "double-conversion/${fname.name}" }) includeEmptyDirs = false @@ -298,6 +275,86 @@ } } +// JNI +def REACT_NATIVE_AAR = "${buildDir}/react-native.aar" + +def extractReactNativeAAR = { buildType -> + def suffix = buildType == 'Debug' ? '-debug' : '-release' + def rnAARs = fileTree(REACT_NATIVE_DIR).matching { include "**/react-native/**/*${suffix}.aar" } + if (rnAARs.isEmpty()) { + rnAARs = fileTree(REACT_NATIVE_DIR).matching { include "**/react-native/**/*.aar" } + } + if (rnAARs.any() && rnAARs.size() > 1) { + logger.error("More than one React Native AAR file has been found:") + rnAARs.each { println(it) } + throw new GradleException( + "Multiple React Native AARs found:\n${rnAARs.join("\n")}" + + "\nRemove the old ones and try again" + ) + } + def rnAAR = rnAARs.singleFile + def file = rnAAR.absoluteFile + def packageName = file.name.tokenize('-')[0] + copy { + from zipTree(file) + into REACT_NATIVE_AAR + include "jni/**/*" + } +} + +task extractReactNativeAARRelease { + doLast { + extractReactNativeAAR('Release') + } +} + +task extractReactNativeAARDebug { + doLast { + extractReactNativeAAR('Debug') + } +} + + +task extractAARHeaders { + doLast { + configurations.extractHeaders.files.each { + def file = it.absoluteFile + copy { + from zipTree(file) + into "$buildDir/$file.name" + include "**/*.h" + } + } + } +} + +task extractJNIFiles { + doLast { + configurations.extractJNI.files.each { + def file = it.absoluteFile + copy { + from zipTree(file) + into "$buildDir/$file.name" + include "jni/**/*" + } + } + } +} + +tasks.whenTaskAdded { task -> + if ( + !task.name.contains("Clean") && + (task.name.contains('externalNativeBuild') || + task.name.startsWith('configureCMake') || + task.name.startsWith('buildCMake')) + ) { + def buildType = task.name.endsWith('Debug') ? 'Debug' : 'Release' + task.dependsOn(extractAARHeaders) + task.dependsOn(extractJNIFiles) + task.dependsOn("extractReactNativeAAR${buildType}") + } +} + // EXTERNAL LIBS task prepareExternalLibs { dependsOn prepareFolly @@ -316,11 +373,11 @@ // Add cross-compilation targets to Rust toolchain task updateRustToolchain(type: Exec) { commandLine "rustup", "target", "add", - "aarch64-linux-android", - "armv7-linux-androideabi", - "i686-linux-android", - "arm-linux-androideabi", - "x86_64-linux-android" + "aarch64-linux-android", + "armv7-linux-androideabi", + "i686-linux-android", + "arm-linux-androideabi", + "x86_64-linux-android" } // Bind preBuild dependencies only if not 'clean' running @@ -389,15 +446,22 @@ return detectedAbis } +def REACT_NATIVE_SO_DIR = "${REACT_NATIVE_AAR}/jni" + android { buildFeatures { prefab true } configurations { all*.exclude module: 'fbjni-java-only' + extractHeaders + extractJNI } dependencies { - implementation 'com.facebook.fbjni:fbjni:0.1.0' + implementation 'com.facebook.fbjni:fbjni:0.2.2' + compileOnly 'com.facebook.fbjni:fbjni:0.2.2' + extractHeaders 'com.facebook.fbjni:fbjni:0.2.2:headers' + extractJNI 'com.facebook.fbjni:fbjni:0.2.2' } ndkVersion rootProject.ext.ndkVersion @@ -532,7 +596,9 @@ arguments "-DANDROID_STL=c++_shared", "-DGLOG_VERSION=" + GLOG_VERSION, "-DOPENSSL_VERSION=" + OPENSSL_VERSION, - "-DNDK_VERSION=" + rootProject.ext.ndkVersion + "-DNDK_VERSION=" + rootProject.ext.ndkVersion, + "-DREACT_NATIVE_SO_DIR=${REACT_NATIVE_SO_DIR}", + "-DBOOST_VERSION=${BOOST_VERSION}" targets "comm_jni_module", "turbomodulejsijni" } } @@ -559,6 +625,11 @@ } } } + + afterEvaluate { + extractAARHeaders.dependsOn(prepareExternalLibs) + extractJNIFiles.dependsOn(prepareExternalLibs) + } } dependencies { diff --git a/native/android/gradle.properties b/native/android/gradle.properties --- a/native/android/gradle.properties +++ b/native/android/gradle.properties @@ -49,10 +49,7 @@ # are providing them. newArchEnabled=false -FOLLY_VERSION=2020.01.13.00 GLOG_VERSION=0.4.0 -BOOST_VERSION=1_63_0 -DOUBLE_CONVERSION_VERSION=1.1.6 # Version of OpenSSL library to build and link OPENSSL_VERSION=1.1.1l