be5ec69b02
* Fix building iOS without Reanimated * Conditionally compile Frame Processors (gradle) * Conditionally use externalNativeBuild * Remove Reanimated import * fix: Conditionally load REA/VisionCamera libraries * fix: Add disable FP to docs * fix: Fix dummy placeholder for Scheduler.mm * fix: Fix dummy `Scheduler` declaration * fix: Only init `CameraView` C++ side if frame processors are enabled * fix: Install JSI Bindings on Frame Processor Manager ctor * fix: Wrong conditional * whoops
501 lines
17 KiB
Groovy
501 lines
17 KiB
Groovy
import groovy.json.JsonSlurper
|
|
import org.apache.tools.ant.filters.ReplaceTokens
|
|
import java.nio.file.Paths
|
|
|
|
def reactNative = new File("$projectDir/../node_modules/react-native")
|
|
|
|
def reactProperties = new Properties()
|
|
file("$projectDir/../node_modules/react-native/ReactAndroid/gradle.properties").withInputStream { reactProperties.load(it) }
|
|
def REACT_NATIVE_VERSION = reactProperties.getProperty("VERSION_NAME").split("\\.")[1].toInteger()
|
|
|
|
def FOR_HERMES = System.getenv("FOR_HERMES") == "True"
|
|
rootProject.getSubprojects().forEach({project ->
|
|
if (project.plugins.hasPlugin("com.android.application")) {
|
|
FOR_HERMES = project.ext.react.enableHermes
|
|
}
|
|
})
|
|
|
|
/**
|
|
* Finds the path of the installed npm package with the given name using Node's
|
|
* module resolution algorithm, which searches "node_modules" directories up to
|
|
* the file system root. This handles various cases, including:
|
|
*
|
|
* - Working in the open-source RN repo:
|
|
* Gradle: /path/to/react-native/ReactAndroid
|
|
* Node module: /path/to/react-native/node_modules/[package]
|
|
*
|
|
* - Installing RN as a dependency of an app and searching for hoisted
|
|
* dependencies:
|
|
* Gradle: /path/to/app/node_modules/react-native/ReactAndroid
|
|
* Node module: /path/to/app/node_modules/[package]
|
|
*
|
|
* - Working in a larger repo (e.g., Facebook) that contains RN:
|
|
* Gradle: /path/to/repo/path/to/react-native/ReactAndroid
|
|
* Node module: /path/to/repo/node_modules/[package]
|
|
*
|
|
* The search begins at the given base directory (a File object). The returned
|
|
* path is a string.
|
|
*/
|
|
static def findNodeModulePath(baseDir, packageName) {
|
|
def basePath = baseDir.toPath().normalize()
|
|
// Node's module resolution algorithm searches up to the root directory,
|
|
// after which the base path will be null
|
|
while (basePath) {
|
|
def candidatePath = Paths.get(basePath.toString(), "node_modules", packageName)
|
|
if (candidatePath.toFile().exists()) {
|
|
return candidatePath.toString()
|
|
}
|
|
basePath = basePath.getParent()
|
|
}
|
|
return null
|
|
}
|
|
|
|
buildscript {
|
|
// Buildscript is evaluated before everything else so we can't use getExtOrDefault
|
|
def kotlin_version = rootProject.ext.has('kotlinVersion') ? rootProject.ext.get('kotlinVersion') : project.properties['VisionCamera_kotlinVersion']
|
|
|
|
repositories {
|
|
google()
|
|
mavenCentral()
|
|
maven {
|
|
url "https://plugins.gradle.org/m2/"
|
|
}
|
|
}
|
|
|
|
dependencies {
|
|
classpath 'com.android.tools.build:gradle:4.2.2'
|
|
classpath 'de.undercouch:gradle-download-task:4.1.2'
|
|
// noinspection DifferentKotlinGradleVersion
|
|
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
|
classpath "org.jetbrains.kotlin:kotlin-android-extensions:$kotlin_version"
|
|
}
|
|
}
|
|
|
|
apply plugin: 'com.android.library'
|
|
apply plugin: 'de.undercouch.download'
|
|
apply plugin: 'kotlin-android'
|
|
apply plugin: 'kotlin-android-extensions'
|
|
|
|
def getExtOrDefault(name) {
|
|
return rootProject.ext.has(name) ? rootProject.ext.get(name) : project.properties['VisionCamera_' + name]
|
|
}
|
|
|
|
def getExtOrIntegerDefault(name) {
|
|
return rootProject.ext.has(name) ? rootProject.ext.get(name) : (project.properties['VisionCamera_' + name]).toInteger()
|
|
}
|
|
|
|
def reanimated = rootProject.subprojects.find { it.name == 'react-native-reanimated' }
|
|
def ENABLE_FRAME_PROCESSORS = !getExtOrDefault("disableFrameProcessors") && reanimated != null
|
|
|
|
android {
|
|
compileSdkVersion getExtOrIntegerDefault('compileSdkVersion')
|
|
buildToolsVersion getExtOrDefault('buildToolsVersion')
|
|
ndkVersion getExtOrDefault('ndkVersion')
|
|
|
|
defaultConfig {
|
|
minSdkVersion 21
|
|
targetSdkVersion getExtOrIntegerDefault('targetSdkVersion')
|
|
|
|
if (ENABLE_FRAME_PROCESSORS) {
|
|
externalNativeBuild {
|
|
cmake {
|
|
cppFlags "-fexceptions", "-frtti", "-std=c++1y", "-DONANDROID"
|
|
abiFilters 'x86', 'x86_64', 'armeabi-v7a', 'arm64-v8a'
|
|
arguments '-DANDROID_STL=c++_shared',
|
|
"-DREACT_NATIVE_VERSION=${REACT_NATIVE_VERSION}",
|
|
"-DNODE_MODULES_DIR=${rootDir}/../node_modules",
|
|
"-DFOR_HERMES=${FOR_HERMES}"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
dexOptions {
|
|
javaMaxHeapSize "4g"
|
|
}
|
|
|
|
if (ENABLE_FRAME_PROCESSORS) {
|
|
externalNativeBuild {
|
|
cmake {
|
|
path "CMakeLists.txt"
|
|
}
|
|
}
|
|
}
|
|
|
|
packagingOptions {
|
|
// Exclude all Libraries that are already present in the user's app (through React Native or by him installing REA)
|
|
excludes = ["**/libc++_shared.so", "**/libfbjni.so", "**/libjsi.so", "**/libreactnativejni.so", "**/libfolly_json.so", "**/libreanimated.so", "**/libjscexecutor.so"]
|
|
// META-INF is duplicate by CameraX.
|
|
exclude "META-INF/**"
|
|
}
|
|
|
|
buildTypes {
|
|
release {
|
|
minifyEnabled false
|
|
}
|
|
}
|
|
|
|
lintOptions {
|
|
disable 'GradleCompatible'
|
|
}
|
|
compileOptions {
|
|
sourceCompatibility JavaVersion.VERSION_1_8
|
|
targetCompatibility JavaVersion.VERSION_1_8
|
|
}
|
|
|
|
configurations {
|
|
extractHeaders
|
|
extractJNI
|
|
}
|
|
}
|
|
|
|
repositories {
|
|
mavenCentral()
|
|
google()
|
|
|
|
def found = false
|
|
def defaultDir = null
|
|
def androidSourcesName = 'React Native sources'
|
|
|
|
if (rootProject.ext.has('reactNativeAndroidRoot')) {
|
|
defaultDir = rootProject.ext.get('reactNativeAndroidRoot')
|
|
} else {
|
|
defaultDir = new File(
|
|
projectDir,
|
|
'/../../../node_modules/react-native/android'
|
|
)
|
|
}
|
|
|
|
if (defaultDir.exists()) {
|
|
maven {
|
|
url defaultDir.toString()
|
|
name androidSourcesName
|
|
}
|
|
|
|
logger.info(":${project.name}:reactNativeAndroidRoot ${defaultDir.canonicalPath}")
|
|
found = true
|
|
} else {
|
|
def parentDir = rootProject.projectDir
|
|
|
|
1.upto(5, {
|
|
if (found) return true
|
|
parentDir = parentDir.parentFile
|
|
|
|
def androidSourcesDir = new File(
|
|
parentDir,
|
|
'node_modules/react-native'
|
|
)
|
|
|
|
def androidPrebuiltBinaryDir = new File(
|
|
parentDir,
|
|
'node_modules/react-native/android'
|
|
)
|
|
|
|
if (androidPrebuiltBinaryDir.exists()) {
|
|
maven {
|
|
url androidPrebuiltBinaryDir.toString()
|
|
name androidSourcesName
|
|
}
|
|
|
|
logger.info(":${project.name}:reactNativeAndroidRoot ${androidPrebuiltBinaryDir.canonicalPath}")
|
|
found = true
|
|
} else if (androidSourcesDir.exists()) {
|
|
maven {
|
|
url androidSourcesDir.toString()
|
|
name androidSourcesName
|
|
}
|
|
|
|
logger.info(":${project.name}:reactNativeAndroidRoot ${androidSourcesDir.canonicalPath}")
|
|
found = true
|
|
}
|
|
})
|
|
}
|
|
|
|
if (!found) {
|
|
throw new GradleException(
|
|
"${project.name}: unable to locate React Native android sources. " +
|
|
"Ensure you have you installed React Native as a dependency in your project and try again."
|
|
)
|
|
}
|
|
}
|
|
|
|
def kotlin_version = getExtOrDefault('kotlinVersion')
|
|
|
|
dependencies {
|
|
// noinspection GradleDynamicVersion
|
|
implementation 'com.facebook.react:react-native:+'
|
|
|
|
if (ENABLE_FRAME_PROCESSORS) {
|
|
implementation project(':react-native-reanimated')
|
|
|
|
//noinspection GradleDynamicVersion
|
|
extractHeaders("com.facebook.fbjni:fbjni:+:headers")
|
|
//noinspection GradleDynamicVersion
|
|
extractJNI("com.facebook.fbjni:fbjni:+")
|
|
|
|
def rnAAR = fileTree("${rootDir}/../node_modules/react-native/android").matching({ it.include "**/**/*.aar" }).singleFile
|
|
def jscAAR = fileTree("${rootDir}/../node_modules/jsc-android/dist/org/webkit/android-jsc").matching({ it.include "**/**/*.aar" }).singleFile
|
|
|
|
def inputFile = new File(rootDir, '../node_modules/react-native/package.json')
|
|
def json = new JsonSlurper().parseText(inputFile.text)
|
|
def reactNativeVersion = json.version as String
|
|
def (major, minor, patch) = reactNativeVersion.tokenize('.')
|
|
|
|
def jsEngine = FOR_HERMES ? "hermes" : "jsc"
|
|
def reaAAR = "${rootDir}/../node_modules/react-native-reanimated/android/react-native-reanimated-${minor}-${jsEngine}.aar"
|
|
|
|
extractJNI(files(rnAAR, jscAAR, reaAAR))
|
|
}
|
|
|
|
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
|
|
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-guava:1.5.2"
|
|
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.5.2"
|
|
|
|
implementation "androidx.camera:camera-core:1.1.0-alpha12"
|
|
implementation "androidx.camera:camera-camera2:1.1.0-alpha12"
|
|
implementation "androidx.camera:camera-lifecycle:1.1.0-alpha12"
|
|
implementation "androidx.camera:camera-video:1.1.0-alpha12"
|
|
|
|
implementation "androidx.camera:camera-view:1.0.0-alpha32"
|
|
implementation "androidx.camera:camera-extensions:1.0.0-alpha32"
|
|
|
|
implementation "androidx.exifinterface:exifinterface:1.3.3"
|
|
}
|
|
|
|
|
|
if (ENABLE_FRAME_PROCESSORS) {
|
|
// third-party-ndk deps headers
|
|
// mostly a copy of https://github.com/software-mansion/react-native-reanimated/blob/master/android/build.gradle#L115
|
|
|
|
def downloadsDir = new File("$buildDir/downloads")
|
|
def thirdPartyNdkDir = new File("$buildDir/third-party-ndk")
|
|
def thirdPartyVersionsFile = new File("${rootDir}/../node_modules/react-native/ReactAndroid/gradle.properties")
|
|
def thirdPartyVersions = new Properties()
|
|
thirdPartyVersions.load(new FileInputStream(thirdPartyVersionsFile))
|
|
|
|
def BOOST_VERSION = thirdPartyVersions["BOOST_VERSION"]
|
|
def boost_file = new File(downloadsDir, "boost_${BOOST_VERSION}.tar.gz")
|
|
def DOUBLE_CONVERSION_VERSION = thirdPartyVersions["DOUBLE_CONVERSION_VERSION"]
|
|
def double_conversion_file = new File(downloadsDir, "double-conversion-${DOUBLE_CONVERSION_VERSION}.tar.gz")
|
|
def FOLLY_VERSION = thirdPartyVersions["FOLLY_VERSION"]
|
|
def folly_file = new File(downloadsDir, "folly-${FOLLY_VERSION}.tar.gz")
|
|
def GLOG_VERSION = thirdPartyVersions["GLOG_VERSION"]
|
|
def glog_file = new File(downloadsDir, "glog-${GLOG_VERSION}.tar.gz")
|
|
|
|
task createNativeDepsDirectories {
|
|
doLast {
|
|
downloadsDir.mkdirs()
|
|
thirdPartyNdkDir.mkdirs()
|
|
}
|
|
}
|
|
|
|
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")
|
|
onlyIfNewer(true)
|
|
overwrite(false)
|
|
dest(boost_file)
|
|
}
|
|
|
|
task prepareBoost(dependsOn: downloadBoost, type: Copy) {
|
|
from(tarTree(resources.gzip(downloadBoost.dest)))
|
|
from("src/main/jni/third-party/boost/Android.mk")
|
|
include("Android.mk", "boost_${BOOST_VERSION}/boost/**/*.hpp", "boost/boost/**/*.hpp")
|
|
includeEmptyDirs = false
|
|
into("$thirdPartyNdkDir") // /boost_X_XX_X
|
|
doLast {
|
|
file("$thirdPartyNdkDir/boost_${BOOST_VERSION}").renameTo("$thirdPartyNdkDir/boost")
|
|
}
|
|
}
|
|
|
|
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(double_conversion_file)
|
|
}
|
|
|
|
task prepareDoubleConversion(dependsOn: downloadDoubleConversion, type: Copy) {
|
|
from(tarTree(downloadDoubleConversion.dest))
|
|
from("src/main/jni/third-party/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")
|
|
}
|
|
|
|
task downloadFolly(dependsOn: createNativeDepsDirectories, type: Download) {
|
|
src("https://github.com/facebook/folly/archive/v${FOLLY_VERSION}.tar.gz")
|
|
onlyIfNewer(true)
|
|
overwrite(false)
|
|
dest(folly_file)
|
|
}
|
|
|
|
task prepareFolly(dependsOn: downloadFolly, type: Copy) {
|
|
from(tarTree(downloadFolly.dest))
|
|
from("src/main/jni/third-party/folly/Android.mk")
|
|
include("folly-${FOLLY_VERSION}/folly/**/*", "Android.mk")
|
|
eachFile { fname -> fname.path = (fname.path - "folly-${FOLLY_VERSION}/") }
|
|
includeEmptyDirs = false
|
|
into("$thirdPartyNdkDir/folly")
|
|
}
|
|
|
|
task downloadGlog(dependsOn: createNativeDepsDirectories, type: Download) {
|
|
src("https://github.com/google/glog/archive/v${GLOG_VERSION}.tar.gz")
|
|
onlyIfNewer(true)
|
|
overwrite(false)
|
|
dest(glog_file)
|
|
}
|
|
|
|
task prepareGlog(dependsOn: downloadGlog, type: Copy) {
|
|
from(tarTree(downloadGlog.dest))
|
|
from("src/main/jni/third-party/glog/")
|
|
include("glog-${GLOG_VERSION}/src/**/*", "Android.mk", "config.h")
|
|
includeEmptyDirs = false
|
|
filesMatching("**/*.h.in") {
|
|
filter(ReplaceTokens, tokens: [
|
|
ac_cv_have_unistd_h : "1",
|
|
ac_cv_have_stdint_h : "1",
|
|
ac_cv_have_systypes_h : "1",
|
|
ac_cv_have_inttypes_h : "1",
|
|
ac_cv_have_libgflags : "0",
|
|
ac_google_start_namespace : "namespace google {",
|
|
ac_cv_have_uint16_t : "1",
|
|
ac_cv_have_u_int16_t : "1",
|
|
ac_cv_have___uint16 : "0",
|
|
ac_google_end_namespace : "}",
|
|
ac_cv_have___builtin_expect : "1",
|
|
ac_google_namespace : "google",
|
|
ac_cv___attribute___noinline : "__attribute__ ((noinline))",
|
|
ac_cv___attribute___noreturn : "__attribute__ ((noreturn))",
|
|
ac_cv___attribute___printf_4_5: "__attribute__((__format__ (__printf__, 4, 5)))"
|
|
])
|
|
it.path = (it.name - ".in")
|
|
}
|
|
into("$thirdPartyNdkDir/glog")
|
|
|
|
doLast {
|
|
copy {
|
|
from(fileTree(dir: "$thirdPartyNdkDir/glog", includes: ["stl_logging.h", "logging.h", "raw_logging.h", "vlog_is_on.h", "**/src/glog/log_severity.h"]).files)
|
|
includeEmptyDirs = false
|
|
into("$thirdPartyNdkDir/glog/exported/glog")
|
|
}
|
|
}
|
|
}
|
|
|
|
task prepareThirdPartyNdkHeaders {
|
|
if (!boost_file.exists()) {
|
|
dependsOn(prepareBoost)
|
|
}
|
|
if (!double_conversion_file.exists()) {
|
|
dependsOn(prepareDoubleConversion)
|
|
}
|
|
if (!folly_file.exists()) {
|
|
dependsOn(prepareFolly)
|
|
}
|
|
if (!glog_file.exists()) {
|
|
dependsOn(prepareGlog)
|
|
}
|
|
}
|
|
|
|
prepareThirdPartyNdkHeaders.mustRunAfter createNativeDepsDirectories
|
|
|
|
task prepareHermes() {
|
|
doLast {
|
|
def hermesPackagePath = findNodeModulePath(projectDir, "hermes-engine")
|
|
if (!hermesPackagePath) {
|
|
throw new GradleScriptException("Could not find the hermes-engine npm package", null)
|
|
}
|
|
|
|
def hermesAAR = file("$hermesPackagePath/android/hermes-debug.aar")
|
|
if (!hermesAAR.exists()) {
|
|
throw new GradleScriptException("The hermes-engine npm package is missing \"android/hermes-debug.aar\"", null)
|
|
}
|
|
|
|
def soFiles = zipTree(hermesAAR).matching({ it.include "**/*.so" })
|
|
|
|
copy {
|
|
from soFiles
|
|
from "$reactNative/ReactAndroid/src/main/jni/first-party/hermes/Android.mk"
|
|
into "$thirdPartyNdkDir/hermes"
|
|
}
|
|
}
|
|
}
|
|
|
|
prepareHermes.mustRunAfter prepareThirdPartyNdkHeaders
|
|
|
|
task prepareJSC {
|
|
doLast {
|
|
def jscPackagePath = findNodeModulePath(projectDir, "jsc-android")
|
|
if (!jscPackagePath) {
|
|
throw new GradleScriptException("Could not find the jsc-android npm package", null)
|
|
}
|
|
|
|
def jscDist = file("$jscPackagePath/dist")
|
|
if (!jscDist.exists()) {
|
|
throw new GradleScriptException("The jsc-android npm package is missing its \"dist\" directory", null)
|
|
}
|
|
|
|
def jscAAR = fileTree(jscDist).matching({ it.include "**/android-jsc/**/*.aar" }).singleFile
|
|
def soFiles = zipTree(jscAAR).matching({ it.include "**/*.so" })
|
|
|
|
def headerFiles = fileTree(jscDist).matching({ it.include "**/include/*.h" })
|
|
|
|
copy {
|
|
from(soFiles)
|
|
from(headerFiles)
|
|
from("$reactNative/ReactAndroid/src/main/jni/third-party/jsc/Android.mk")
|
|
|
|
filesMatching("**/*.h", { it.path = "JavaScriptCore/${it.name}" })
|
|
|
|
includeEmptyDirs(false)
|
|
into("$thirdPartyNdkDir/jsc")
|
|
}
|
|
}
|
|
}
|
|
|
|
prepareJSC.mustRunAfter prepareHermes
|
|
|
|
task extractAARHeaders {
|
|
doLast {
|
|
configurations.extractHeaders.files.each {
|
|
def file = it.absoluteFile
|
|
copy {
|
|
from zipTree(file)
|
|
into "$buildDir/$file.name"
|
|
include "**/*.h"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
extractAARHeaders.mustRunAfter prepareJSC
|
|
|
|
task extractJNIFiles {
|
|
doLast {
|
|
configurations.extractJNI.files.each {
|
|
def file = it.absoluteFile
|
|
|
|
copy {
|
|
from zipTree(file)
|
|
into "$buildDir/$file.name"
|
|
include "jni/**/*"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
extractJNIFiles.mustRunAfter extractAARHeaders
|
|
|
|
// pre-native build pipeline
|
|
|
|
tasks.whenTaskAdded { task ->
|
|
if (!task.name.contains('Clean') && (task.name.contains('externalNative') || task.name.contains('CMake'))) {
|
|
task.dependsOn(extractAARHeaders)
|
|
task.dependsOn(extractJNIFiles)
|
|
task.dependsOn(prepareJSC)
|
|
task.dependsOn(prepareHermes)
|
|
task.dependsOn(prepareThirdPartyNdkHeaders)
|
|
}
|
|
}
|
|
}
|