diff --git a/native/android/app/CMakeLists.txt b/native/android/app/CMakeLists.txt index 97b68340c..156b3cc2e 100644 --- a/native/android/app/CMakeLists.txt +++ b/native/android/app/CMakeLists.txt @@ -1,246 +1,236 @@ # For more information about using CMake with Android Studio, read the # 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 # cmake-lint: disable=C0103 # Disable line length as some paths are hard to reduce without becoming cryptic # cmake-lint: disable=C0301 # Sets the minimum version of CMake required to build the native library. cmake_minimum_required(VERSION 3.18) # Creates and names a library, sets it as either STATIC # or SHARED, and provides the relative paths to its source code. # You can define multiple libraries, and CMake builds them for you. # Gradle automatically packages shared libraries with your APK. set(PACKAGE_NAME "comm_jni_module") find_package(fbjni REQUIRED CONFIG) set(BUILD_TESTING OFF) set(HAVE_SYMBOLIZE OFF) set(WITH_GTEST OFF CACHE BOOL "Use googletest" FORCE) set(WITH_GFLAGS OFF CACHE BOOL "Use gflags" FORCE) # General set(_third_party_dir ${CMAKE_CURRENT_SOURCE_DIR}/build/third-party-ndk) set(_android_build_dir build/${CMAKE_ANDROID_ARCH_ABI}) include(FetchContent) if(CMAKE_ANDROID_ARCH_ABI STREQUAL arm64-v8a) set(Rust_CARGO_TARGET aarch64-linux-android) elseif(CMAKE_ANDROID_ARCH_ABI STREQUAL x86) set(Rust_CARGO_TARGET i686-linux-android) elseif(CMAKE_ANDROID_ARCH_ABI STREQUAL x86_64) set(Rust_CARGO_TARGET x86_64-linux-android) elseif(CMAKE_ANDROID_ARCH_ABI STREQUAL armeabi-v7a) set(Rust_CARGO_TARGET arm-linux-androideabi) endif() string(TOLOWER ${CMAKE_HOST_SYSTEM_NAME} CMAKE_HOST_SYSTEM_NAME_LOWER) set(_toolchain_path "$ENV{ANDROID_HOME}/ndk/${NDK_VERSION}/toolchains/llvm/prebuilt/${CMAKE_HOST_SYSTEM_NAME_LOWER}-x86_64/bin" ) if(EXISTS "${_toolchain_path}/${Rust_CARGO_TARGET}-ar") set(AR "${_toolchain_path}/${Rust_CARGO_TARGET}-ar") else() set(AR "${_toolchain_path}/llvm-ar") endif() FetchContent_Declare( Corrosion GIT_REPOSITORY https://github.com/corrosion-rs/corrosion.git GIT_TAG v0.2.1 ) FetchContent_MakeAvailable(Corrosion) include(../../../shared/cmake/corrosion-cxx.cmake) add_library_rust(PATH ../../native_rust_library NAMESPACE comm) # We're updating parameters below for Cmake's find_OpenSSL() function set(OPENSSL_ROOT_DIR "${_third_party_dir}/openssl/openssl-${OPENSSL_VERSION}/${_android_build_dir}" ) list(APPEND CMAKE_FIND_ROOT_PATH "${OPENSSL_ROOT_DIR}") add_subdirectory(${_third_party_dir}/glog/glog-${GLOG_VERSION}/) add_subdirectory(../../node_modules/olm ./build) 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 ${_react_native_dir}/ReactCommon/jsi ${_react_native_dir}/ReactCommon/callinvoker ${_react_native_dir}/ReactAndroid/src/main/java/com/facebook/react/turbomodule/core/jni/ReactCommon # OpenSSL ${_third_party_dir}/openssl/openssl-${OPENSSL_VERSION}/build/${CMAKE_ANDROID_ARCH_ABI}/include # SQLCipher amalgamation ${_node_modules_dir}/@commapp/sqlcipher-amalgamation/src # SQLite ORM ../../cpp/third-party/sqlite_orm # symlinked React Native headers ../headers # external libs ${_third_party_dir}/folly - ${_third_party_dir}/boost/boost + ${_third_party_dir}/boost/boost_${BOOST_VERSION} ${_third_party_dir}/double-conversion # comm android specific code ./src/cpp # comm native mutual code ../../cpp/CommonCpp/ ../../cpp/CommonCpp/NativeModules ../../cpp/CommonCpp/NativeModules/InternalModules ../../cpp/CommonCpp/NativeModules/PersistentStorageUtilities ../../cpp/CommonCpp/NativeModules/PersistentStorageUtilities/ThreadOperationsUtilities ../../cpp/CommonCpp/NativeModules/PersistentStorageUtilities/MessageOperationsUtilities ../../cpp/CommonCpp/NativeModules/PersistentStorageUtilities/MessageOperationsUtilities/MessageSpecs ../../cpp/CommonCpp/DatabaseManagers ../../cpp/CommonCpp/Tools # native rust library ${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 -DSQLITE_TEMP_STORE=2 -DSQLCIPHER_CRYPTO_OPENSSL ) find_library(log-lib log) add_library( # OpenSSL Crypto lib openssl-crypto STATIC IMPORTED ) set_target_properties( # OpenSSL Crypto lib openssl-crypto PROPERTIES IMPORTED_LOCATION "${_third_party_dir}/openssl/openssl-${OPENSSL_VERSION}/build/${CMAKE_ANDROID_ARCH_ABI}/lib/libcrypto.a" ) add_library( # OpenSSL SSL lib openssl-ssl STATIC IMPORTED ) set_target_properties( # OpenSSL SSL lib openssl-ssl PROPERTIES IMPORTED_LOCATION "${_third_party_dir}/openssl/openssl-${OPENSSL_VERSION}/build/${CMAKE_ANDROID_ARCH_ABI}/lib/libssl.a" ) target_link_libraries( ${PACKAGE_NAME} fbjni::fbjni android ${log-lib} + ${folly-lib} glog::glog olm openssl-crypto openssl-ssl comm::native_rust_library ) # add a dummy library which is required by CallInvokerHolderImpl.java add_library( turbomodulejsijni # Sets the library as a shared library. SHARED # Provides a relative path to your source file(s). ./src/cpp/dummy.cpp ) diff --git a/native/android/app/build.gradle b/native/android/app/build.gradle index 03b1b12ef..445886570 100644 --- a/native/android/app/build.gradle +++ b/native/android/app/build.gradle @@ -1,669 +1,740 @@ apply plugin: "com.android.application" import com.android.build.OutputFile import de.undercouch.gradle.tasks.download.Download import app.comm.gradle.tasks.GitModules /** * The react.gradle file registers a task for each build variant (e.g. bundleDebugJsAndAssets * and bundleReleaseJsAndAssets). * These basically call `react-native bundle` with the correct arguments during the Android build * cycle. By default, bundleDebugJsAndAssets is skipped, as in debug/dev mode we prefer to load the * bundle directly from the development server. Below you can see all the possible configurations * and their defaults. If you decide to add a configuration block, make sure to add it before the * `apply from: "../../node_modules/react-native/react.gradle"` line. * * project.ext.react = [ * // the name of the generated asset file containing your JS bundle * bundleAssetName: "index.android.bundle", * * // the entry file for bundle generation. If none specified and * // "index.android.js" exists, it will be used. Otherwise "index.js" is * // default. Can be overridden with ENTRY_FILE environment variable. * entryFile: "index.android.js", * * // https://reactnative.dev/docs/performance#enable-the-ram-format * bundleCommand: "ram-bundle", * * // whether to bundle JS and assets in debug mode * bundleInDebug: false, * * // whether to bundle JS and assets in release mode * bundleInRelease: true, * * // whether to bundle JS and assets in another build variant (if configured). * // See http://tools.android.com/tech-docs/new-build-system/user-guide#TOC-Build-Variants * // The configuration property can be in the following formats * // 'bundleIn${productFlavor}${buildType}' * // 'bundleIn${buildType}' * // bundleInFreeDebug: true, * // bundleInPaidRelease: true, * // bundleInBeta: true, * * // whether to disable dev mode in custom build variants (by default only disabled in release) * // for example: to disable dev mode in the staging build type (if configured) * devDisabledInStaging: true, * // The configuration property can be in the following formats * // 'devDisabledIn${productFlavor}${buildType}' * // 'devDisabledIn${buildType}' * * // the root of your project, i.e. where "package.json" lives * root: "../../", * * // where to put the JS bundle asset in debug mode * jsBundleDirDebug: "$buildDir/intermediates/assets/debug", * * // where to put the JS bundle asset in release mode * jsBundleDirRelease: "$buildDir/intermediates/assets/release", * * // where to put drawable resources / React Native assets, e.g. the ones you use via * // require('./image.png')), in debug mode * resourcesDirDebug: "$buildDir/intermediates/res/merged/debug", * * // where to put drawable resources / React Native assets, e.g. the ones you use via * // require('./image.png')), in release mode * resourcesDirRelease: "$buildDir/intermediates/res/merged/release", * * // by default the gradle tasks are skipped if none of the JS files or assets change; this means * // that we don't look at files in android/ or ios/ to determine whether the tasks are up to * // date; if you have any other folders that you want to ignore for performance reasons (gradle * // indexes the entire tree), add them here. Alternatively, if you have JS files in android/ * // for example, you might want to remove it from here. * inputExcludes: ["android/**", "ios/**"], * * // override which node gets called and with what additional arguments * nodeExecutableAndArgs: ["node"], * * // supply additional arguments to the packager * extraPackagerArgs: [] * ] */ project.ext.react = [ enableHermes: true, // clean and rebuild if changing cliPath: ["node", "-e", "console.log(require('react-native/cli').bin);"].execute([], projectDir).text.trim(), ] apply from: new File(["node", "--print", "require.resolve('react-native/package.json')"].execute(null, rootDir).text.trim(), "../react.gradle") /** * Set this to true to create two separate APKs instead of one: * - An APK that only works on ARM devices * - An APK that only works on x86 devices * The advantage is the size of the APK is reduced by about 4MB. * Upload all the APKs to the Play Store and people will download * the correct one based on the CPU architecture of their device. */ def enableSeparateBuildPerCPUArchitecture = false /** * Run Proguard to shrink the Java bytecode in release builds. */ def enableProguardInReleaseBuilds = false /** * The preferred build flavor of JavaScriptCore. * * For example, to use the international variant, you can use: * `def jscFlavor = 'org.webkit:android-jsc-intl:+'` * * The international variant includes ICU i18n library and necessary data * allowing to use e.g. `Date.toLocaleString` and `String.localeCompare` that * give correct results when using with locales other than en-US. Note that * this variant is about 6MiB larger per architecture than default. */ def jscFlavor = 'org.webkit:android-jsc:+' /** * Whether to enable the Hermes VM. * * This should be set on project.ext.react and that value will be read here. If it is not set * on project.ext.react, JavaScript will not be compiled to Hermes Bytecode * and the benefits of using Hermes will therefore be sharply reduced. */ def enableHermes = project.ext.react.get("enableHermes", false) /** * Architectures to build native code for. */ def reactNativeArchitectures() { def value = project.getProperties().get("reactNativeArchitectures") return value ? value.split(",") : ["armeabi-v7a", "x86", "x86_64", "arm64-v8a"] } def customDownloadsDir = System.getenv("REACT_NATIVE_DOWNLOADS_DIR") def dependenciesPath = System.getenv("REACT_NATIVE_DEPENDENCIES") 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") onlyIfNewer(true) overwrite(false) 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") } // GLOG task downloadGlog(dependsOn: createNativeDepsDirectories, type: Download) { src("https://github.com/google/glog/archive/v${GLOG_VERSION}.tar.gz") onlyIfNewer(true) overwrite(false) dest(new File(downloadsDir, "glog-${GLOG_VERSION}.tar.gz")) } task prepareGlog(dependsOn: dependenciesPath ? [] : [downloadGlog], type: Copy) { from(dependenciesPath ?: tarTree(downloadGlog.dest)) include("glog-${GLOG_VERSION}/**/*") includeEmptyDirs = false into("$thirdPartyNdkDir/glog") } // 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}") } } // DOUBLE-CONVERSION task downloadDoubleConversion(dependsOn: createNativeDepsDirectories, type: Download) { src("https://github.com/google/double-conversion/archive/v${DOUBLE_CONVERSION_VERSION}.tar.gz") onlyIfNewer(true) overwrite(false) 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 into("$thirdPartyNdkDir/double-conversion") } // OPENSSL def hostSystem = System.getProperty('os.name').toLowerCase(Locale.ROOT) def hostTag = hostSystem.contains('mac') ? 'darwin-x86_64' : 'linux-x86_64' task downloadOpenSSL(dependsOn: createNativeDepsDirectories, type: Download) { src("https://www.openssl.org/source/openssl-${OPENSSL_VERSION}.tar.gz") onlyIfNewer(true) overwrite(false) dest(new File(downloadsDir, "openssl-${OPENSSL_VERSION}.tar.gz")) } task prepareOpenSSL( dependsOn: dependenciesPath ? [] : [downloadOpenSSL], type: Copy ) { from(dependenciesPath ?: tarTree(downloadOpenSSL.dest)) include("openssl-${OPENSSL_VERSION}/**/*") includeEmptyDirs = false into("${thirdPartyNdkDir}/openssl") } task prebuildOpenSSL(dependsOn: dependenciesPath ? [] : [prepareOpenSSL]) { inputs.properties([ 'openssl.version': OPENSSL_VERSION, 'openssl.abis': getBuildTypeABIs(reactNativeArchitectures()) ]) outputs.dir("${thirdPartyNdkDir}/openssl/openssl-${OPENSSL_VERSION}/build/") .withPropertyName('openssl.output') doFirst { getBuildTypeABIs(reactNativeArchitectures()).each { buildABI -> logger.info("Building OpenSSL library for the ${buildABI}") exec { commandLine './bash/build_openssl.sh', // OPENSSL_SUBMODULE_PATH "${thirdPartyNdkDir}/openssl/openssl-${OPENSSL_VERSION}/", // HOST_TAG hostTag, // ANDROID_ARCH_ABI "${buildABI}", // MIN_SDK_VERSION rootProject.ext.minSdkVersion, // ANDROID_NDK_HOME android.ndkDirectory.absolutePath, // PARALLEL_THREADS Runtime.getRuntime().availableProcessors() } } } } +// 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 dependsOn prepareGlog dependsOn prepareBoost dependsOn prepareDoubleConversion dependsOn prebuildOpenSSL } // Removes the '.cxx' directory to prevent running // ninja clean when the 'clean' command is executed task removeCXX(type: Exec) { commandLine 'rm', '-rf', '.cxx' } // 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 if (!isCleanRunning()) { afterEvaluate { preBuild.dependsOn(prepareExternalLibs, updateRustToolchain) } } // Run removing CXX task before the clean execution beforeEvaluate { clean.dependsOn(removeCXX) } // Detects are we running the 'clean' commands def isCleanRunning() { gradle.startParameter.taskRequests.any { !it.args.isEmpty() && it.args.first().startsWith('clean') } } // Release keystore via macOS Keychain Access def getPassword(String keyLabel) { if (System.getenv('ANDROID_SIGNING_PASSWORD')) { return System.getenv('ANDROID_SIGNING_PASSWORD') } def stdout = new ByteArrayOutputStream() exec { commandLine 'security', 'find-generic-password', '-wl', keyLabel, '-a', System.properties['user.name'] standardOutput = stdout ignoreExitValue true } return stdout.toString().strip() } // Returns all ABIs architectures for the 'bundleRelease' // or only from 'adb devices' if running debug release. def getBuildTypeABIs(nativeArchitectures) { if (System.getenv("BUILDKITE") == "true") { return ["arm64-v8a"] } def isBundleRelease = gradle.startParameter.taskRequests.any { !it.args.isEmpty() && it.args.first().contains("bundleRelease") } if (isBundleRelease) { // All of the supported ABIs // https://developer.android.com/ndk/guides/abis.html#sa final allAbis = ["armeabi-v7a", "x86", "arm64-v8a", "x86_64"] logger.info("Using all architectures to build: ${allAbis}") return allAbis } if (nativeArchitectures) { return nativeArchitectures } // Get current 'adb devices' architectures def commandOutput = new ByteArrayOutputStream() exec { commandLine "./bash/detect_abis.sh" standardOutput = commandOutput } final detectedAbis = commandOutput.toString("UTF-8").trim().tokenize() logger.info("Detected architectures to build: ${detectedAbis}") 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 compileSdkVersion rootProject.ext.compileSdkVersion defaultConfig { applicationId 'app.comm.android' minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion versionCode 166 versionName '1.0.166' buildConfigField "boolean", "IS_NEW_ARCHITECTURE_ENABLED", isNewArchitectureEnabled().toString() if (isNewArchitectureEnabled()) { // We configure the CMake build only if you decide to opt-in for the New Architecture. externalNativeBuild { cmake { arguments "-DPROJECT_BUILD_DIR=$buildDir", "-DREACT_ANDROID_DIR=$rootDir/../node_modules/react-native/ReactAndroid", "-DREACT_ANDROID_BUILD_DIR=$rootDir/../node_modules/react-native/ReactAndroid/build", "-DNODE_MODULES_DIR=$rootDir/../node_modules", "-DANDROID_STL=c++_shared" } } if (!enableSeparateBuildPerCPUArchitecture) { ndk { abiFilters (*reactNativeArchitectures()) } } } missingDimensionStrategy 'react-native-camera', 'general' multiDexEnabled true } if (isNewArchitectureEnabled()) { // We configure the NDK build only if you decide to opt-in for the New Architecture. externalNativeBuild { cmake { path "$projectDir/src/main/jni/CMakeLists.txt" } } def reactAndroidProjectDir = project(':ReactAndroid').projectDir def packageReactNdkDebugLibs = tasks.register("packageReactNdkDebugLibs", Copy) { dependsOn(":ReactAndroid:packageReactNdkDebugLibsForBuck") from("$reactAndroidProjectDir/src/main/jni/prebuilt/lib") into("$buildDir/react-ndk/exported") } def packageReactNdkReleaseLibs = tasks.register("packageReactNdkReleaseLibs", Copy) { dependsOn(":ReactAndroid:packageReactNdkReleaseLibsForBuck") from("$reactAndroidProjectDir/src/main/jni/prebuilt/lib") into("$buildDir/react-ndk/exported") } afterEvaluate { // If you wish to add a custom TurboModule or component locally, // you should uncomment this line. // preBuild.dependsOn("generateCodegenArtifactsFromSchema") preDebugBuild.dependsOn(packageReactNdkDebugLibs) preReleaseBuild.dependsOn(packageReactNdkReleaseLibs) // Due to a bug inside AGP, we have to explicitly set a dependency // between configureCMakeDebug* tasks and the preBuild tasks. // This can be removed once this is solved: https://issuetracker.google.com/issues/207403732 configureCMakeRelWithDebInfo.dependsOn(preReleaseBuild) configureCMakeDebug.dependsOn(preDebugBuild) reactNativeArchitectures().each { architecture -> tasks.findByName("configureCMakeDebug[${architecture}]")?.configure { dependsOn("preDebugBuild") } tasks.findByName("configureCMakeRelWithDebInfo[${architecture}]")?.configure { dependsOn("preReleaseBuild") } } } } splits { abi { reset() enable enableSeparateBuildPerCPUArchitecture universalApk false // If true, also generate a universal APK include (*reactNativeArchitectures()) } } signingConfigs { debug { storeFile file('debug.keystore') storePassword 'android' keyAlias 'androiddebugkey' keyPassword 'android' } release { if (project.hasProperty('COMM_UPLOAD_STORE_FILE')) { def password = getPassword('CommAndroidKeyPassword') storeFile file(COMM_UPLOAD_STORE_FILE) storePassword password keyAlias COMM_UPLOAD_KEY_ALIAS keyPassword password } } } buildTypes { final buildABIs = getBuildTypeABIs(reactNativeArchitectures()) release { if (project.hasProperty('COMM_UPLOAD_STORE_FILE')) { signingConfig signingConfigs.release } else { signingConfig signingConfigs.debug } minifyEnabled enableProguardInReleaseBuilds proguardFiles getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro" ndk { abiFilters = [] abiFilters.addAll(buildABIs) } } debug { signingConfig signingConfigs.debug ndk { abiFilters = [] abiFilters.addAll(buildABIs) } } } packagingOptions { pickFirst "**/libc++_shared.so" pickFirst "**/libfbjni.so" } defaultConfig { externalNativeBuild { cmake { 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" } } } externalNativeBuild { cmake { path "CMakeLists.txt" version "3.18.1" } } // applicationVariants are e.g. debug, release applicationVariants.all { variant -> variant.outputs.each { output -> // For each separate APK per architecture, set a unique version code as described here: // https://developer.android.com/studio/build/configure-apk-splits.html // Example: versionCode 1 will generate 1001 for armeabi-v7a, 1002 for x86, etc. def versionCodes = ["armeabi-v7a": 1, "x86": 2, "arm64-v8a": 3, "x86_64": 4] def abi = output.getFilter(OutputFile.ABI) if (abi != null) { // null for the universal-debug, universal-release variants output.versionCodeOverride = defaultConfig.versionCode * 1000 + versionCodes.get(abi) } } } + + afterEvaluate { + extractAARHeaders.dependsOn(prepareExternalLibs) + extractJNIFiles.dependsOn(prepareExternalLibs) + } } dependencies { implementation fileTree(dir: "libs", include: ["*.jar"]) implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.4.21" //noinspection GradleDynamicVersion implementation("com.facebook.react:react-native:+") def isGifEnabled = (findProperty('expo.gif.enabled') ?: "") == "true"; def isWebpEnabled = (findProperty('expo.webp.enabled') ?: "") == "true"; def isWebpAnimatedEnabled = (findProperty('expo.webp.animated') ?: "") == "true"; // If your app supports Android versions before Ice Cream Sandwich (API level 14) // All fresco packages should use the same version if (isGifEnabled || isWebpEnabled) { implementation 'com.facebook.fresco:fresco:2.0.0' implementation 'com.facebook.fresco:imagepipeline-okhttp3:2.0.0' } if (isGifEnabled) { // For animated gif support implementation 'com.facebook.fresco:animated-gif:2.0.0' } if (isWebpEnabled) { // For webp support implementation 'com.facebook.fresco:webpsupport:2.0.0' if (isWebpAnimatedEnabled) { // Animated webp support implementation 'com.facebook.fresco:animated-webp:2.0.0' } } implementation "androidx.swiperefreshlayout:swiperefreshlayout:1.0.0" debugImplementation("com.facebook.flipper:flipper:${FLIPPER_VERSION}") { exclude group:'com.facebook.fbjni' } debugImplementation("com.facebook.flipper:flipper-network-plugin:${FLIPPER_VERSION}") { exclude group:'com.facebook.flipper' } debugImplementation("com.facebook.flipper:flipper-fresco-plugin:${FLIPPER_VERSION}") { exclude group:'com.facebook.flipper' exclude group:'com.facebook.fresco', module:'stetho' } implementation "com.google.android.gms:play-services-base:16.1.0" implementation "com.google.firebase:firebase-core:16.0.9" implementation "com.google.firebase:firebase-messaging:18.0.0" implementation "me.leolin:ShortcutBadger:1.1.21@aar" implementation project(':reactnativekeyboardinput') implementation "androidx.multidex:multidex:2.0.1" implementation "androidx.lifecycle:lifecycle-runtime:2.2.0" implementation "androidx.lifecycle:lifecycle-process:2.2.0" implementation 'com.facebook.fresco:fresco:2.2.0' implementation 'com.facebook.fresco:animated-gif:2.2.0' implementation 'com.facebook.fresco:animated-webp:2.2.0' implementation 'com.facebook.fresco:webpsupport:2.2.0' implementation 'org.conscrypt:conscrypt-android:2.0.0' if (enableHermes) { //noinspection GradleDynamicVersion implementation("com.facebook.react:hermes-engine:+") { // From node_modules exclude group:'com.facebook.fbjni' } } else { implementation jscFlavor } } if (isNewArchitectureEnabled()) { // If new architecture is enabled, we let you build RN from source // Otherwise we fallback to a prebuilt .aar bundled in the NPM package. // This will be applied to all the imported transtitive dependency. configurations.all { resolutionStrategy.dependencySubstitution { substitute(module("com.facebook.react:react-native")) .using(project(":ReactAndroid")) .because("On New Architecture we're building React Native from source") substitute(module("com.facebook.react:hermes-engine")) .using(project(":ReactAndroid:hermes-engine")) .because("On New Architecture we're building Hermes from source") } } } // Run this once to be able to run the application with BUCK // puts all compile dependencies into folder libs for BUCK to use task copyDownloadableDepsToLibs(type: Copy) { from configurations.implementation into 'libs' } apply from: new File(["node", "--print", "require.resolve('@react-native-community/cli-platform-android/package.json')"].execute(null, rootDir).text.trim(), "../native_modules.gradle"); applyNativeModulesAppBuildGradle(project) def isNewArchitectureEnabled() { // To opt-in for the New Architecture, you can either: // - Set `newArchEnabled` to true inside the `gradle.properties` file // - Invoke gradle with `-newArchEnabled=true` // - Set an environment variable `ORG_GRADLE_PROJECT_newArchEnabled=true` return project.hasProperty("newArchEnabled") && project.newArchEnabled == "true" } apply plugin: 'com.google.gms.google-services' diff --git a/native/android/gradle.properties b/native/android/gradle.properties index 58822c94f..e2212913b 100644 --- a/native/android/gradle.properties +++ b/native/android/gradle.properties @@ -1,58 +1,55 @@ # Project-wide Gradle settings. # IDE (e.g. Android Studio) users: # Gradle settings configured through the IDE *will override* # any settings specified in this file. # For more details on how to configure your build environment visit # http://www.gradle.org/docs/current/userguide/build_environment.html # Specifies the JVM arguments used for the daemon process. # The setting is particularly useful for tweaking memory settings. # Default value: -Xmx512m -XX:MaxMetaspaceSize=256m org.gradle.jvmargs=-Xmx4096m -XX:MaxMetaspaceSize=1024m -XX:MaxPermSize=4096m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 # When configured, Gradle will run in incubating parallel mode. # This option should only be used with decoupled projects. More details, visit # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects org.gradle.parallel=true # AndroidX package structure to make it clearer which packages are bundled with the # Android operating system, and which are packaged with your app's APK # https://developer.android.com/topic/libraries/support-library/androidx-rn android.useAndroidX=true # Automatically convert third-party libraries to use AndroidX android.enableJetifier=true expo.jsEngine=hermes # Enable GIF support in React Native images (~200 B increase) expo.gif.enabled=true # Enable webp support in React Native images (~85 KB increase) expo.webp.enabled=true # Enable animated webp support (~3.4 MB increase) # Disabled by default because iOS doesn't support animated webp expo.webp.animated=false # Version of flipper SDK to use with React Native FLIPPER_VERSION=0.125.0 # Use this property to specify which architecture you want to build. # You can also override it from the CLI using # ./gradlew -PreactNativeArchitectures=x86_64 reactNativeArchitectures=armeabi-v7a,arm64-v8a,x86,x86_64 # Use this property to enable support to the new architecture. # This will allow you to use TurboModules and the Fabric render in # your application. You should enable this flag either if you want # to write custom TurboModules/Fabric components OR use libraries that # 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