build.gradle 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470
  1. /*
  2. * Copyright (c) Facebook, Inc. and its affiliates.
  3. *
  4. * This source code is licensed under the MIT license found in the
  5. * LICENSE file in the root directory of this source tree.
  6. */
  7. plugins {
  8. id("com.android.library")
  9. id("maven")
  10. id("de.undercouch.download")
  11. }
  12. import java.nio.file.Paths
  13. import de.undercouch.gradle.tasks.download.Download
  14. import org.apache.tools.ant.taskdefs.condition.Os
  15. import org.apache.tools.ant.filters.ReplaceTokens
  16. // We download various C++ open-source dependencies into downloads.
  17. // We then copy both the downloaded code and our custom makefiles and headers into third-party-ndk.
  18. // After that we build native code from src/main/jni with module path pointing at third-party-ndk.
  19. def customDownloadsDir = System.getenv("REACT_NATIVE_DOWNLOADS_DIR")
  20. def downloadsDir = customDownloadsDir ? new File(customDownloadsDir) : new File("$buildDir/downloads")
  21. def thirdPartyNdkDir = new File("$buildDir/third-party-ndk")
  22. // You need to have following folders in this directory:
  23. // - boost_1_63_0
  24. // - double-conversion-1.1.6
  25. // - folly-deprecate-dynamic-initializer
  26. // - glog-0.3.5
  27. def dependenciesPath = System.getenv("REACT_NATIVE_DEPENDENCIES")
  28. // The Boost library is a very large download (>100MB).
  29. // If Boost is already present on your system, define the REACT_NATIVE_BOOST_PATH env variable
  30. // and the build will use that.
  31. def boostPath = dependenciesPath ?: System.getenv("REACT_NATIVE_BOOST_PATH")
  32. // Setup build type for NDK, supported values: {debug, release}
  33. def nativeBuildType = System.getenv("NATIVE_BUILD_TYPE") ?: "release"
  34. task createNativeDepsDirectories {
  35. downloadsDir.mkdirs()
  36. thirdPartyNdkDir.mkdirs()
  37. }
  38. task downloadBoost(dependsOn: createNativeDepsDirectories, type: Download) {
  39. src("https://github.com/react-native-community/boost-for-react-native/releases/download/v${BOOST_VERSION.replace("_", ".")}-0/boost_${BOOST_VERSION}.tar.gz")
  40. onlyIfNewer(true)
  41. overwrite(false)
  42. dest(new File(downloadsDir, "boost_${BOOST_VERSION}.tar.gz"))
  43. }
  44. task prepareBoost(dependsOn: boostPath ? [] : [downloadBoost], type: Copy) {
  45. from(boostPath ?: tarTree(resources.gzip(downloadBoost.dest)))
  46. from("src/main/jni/third-party/boost/Android.mk")
  47. include("Android.mk", "boost_${BOOST_VERSION}/boost/**/*.hpp", "boost/boost/**/*.hpp")
  48. includeEmptyDirs = false
  49. into("$thirdPartyNdkDir/boost")
  50. doLast {
  51. file("$thirdPartyNdkDir/boost/boost").renameTo("$thirdPartyNdkDir/boost/boost_${BOOST_VERSION}")
  52. }
  53. }
  54. task downloadDoubleConversion(dependsOn: createNativeDepsDirectories, type: Download) {
  55. src("https://github.com/google/double-conversion/archive/v${DOUBLE_CONVERSION_VERSION}.tar.gz")
  56. onlyIfNewer(true)
  57. overwrite(false)
  58. dest(new File(downloadsDir, "double-conversion-${DOUBLE_CONVERSION_VERSION}.tar.gz"))
  59. }
  60. task prepareDoubleConversion(dependsOn: dependenciesPath ? [] : [downloadDoubleConversion], type: Copy) {
  61. from(dependenciesPath ?: tarTree(downloadDoubleConversion.dest))
  62. from("src/main/jni/third-party/double-conversion/Android.mk")
  63. include("double-conversion-${DOUBLE_CONVERSION_VERSION}/src/**/*", "Android.mk")
  64. filesMatching("*/src/**/*", { fname -> fname.path = "double-conversion/${fname.name}" })
  65. includeEmptyDirs = false
  66. into("$thirdPartyNdkDir/double-conversion")
  67. }
  68. task downloadFolly(dependsOn: createNativeDepsDirectories, type: Download) {
  69. src("https://github.com/facebook/folly/archive/v${FOLLY_VERSION}.tar.gz")
  70. onlyIfNewer(true)
  71. overwrite(false)
  72. dest(new File(downloadsDir, "folly-${FOLLY_VERSION}.tar.gz"))
  73. }
  74. task prepareFolly(dependsOn: dependenciesPath ? [] : [downloadFolly], type: Copy) {
  75. from(dependenciesPath ?: tarTree(downloadFolly.dest))
  76. from("src/main/jni/third-party/folly/Android.mk")
  77. include("folly-${FOLLY_VERSION}/folly/**/*", "Android.mk")
  78. eachFile { fname -> fname.path = (fname.path - "folly-${FOLLY_VERSION}/") }
  79. includeEmptyDirs = false
  80. into("$thirdPartyNdkDir/folly")
  81. }
  82. task prepareHermes() {
  83. def hermesPackagePath = findNodeModulePath(projectDir, "hermes-engine")
  84. if (!hermesPackagePath) {
  85. throw new GradleScriptException("Could not find the hermes-engine npm package")
  86. }
  87. def hermesAAR = file("$hermesPackagePath/android/hermes-debug.aar")
  88. if (!hermesAAR.exists()) {
  89. throw new GradleScriptException("The hermes-engine npm package is missing \"android/hermes-debug.aar\"")
  90. }
  91. def soFiles = zipTree(hermesAAR).matching({ it.include "**/*.so" })
  92. copy {
  93. from soFiles
  94. from "src/main/jni/first-party/hermes/Android.mk"
  95. into "$thirdPartyNdkDir/hermes"
  96. }
  97. }
  98. task downloadGlog(dependsOn: createNativeDepsDirectories, type: Download) {
  99. src("https://github.com/google/glog/archive/v${GLOG_VERSION}.tar.gz")
  100. onlyIfNewer(true)
  101. overwrite(false)
  102. dest(new File(downloadsDir, "glog-${GLOG_VERSION}.tar.gz"))
  103. }
  104. // Prepare glog sources to be compiled, this task will perform steps that normally should've been
  105. // executed by automake. This way we can avoid dependencies on make/automake
  106. task prepareGlog(dependsOn: dependenciesPath ? [] : [downloadGlog], type: Copy) {
  107. from(dependenciesPath ?: tarTree(downloadGlog.dest))
  108. from("src/main/jni/third-party/glog/")
  109. include("glog-${GLOG_VERSION}/src/**/*", "Android.mk", "config.h")
  110. includeEmptyDirs = false
  111. filesMatching("**/*.h.in") {
  112. filter(ReplaceTokens, tokens: [
  113. ac_cv_have_unistd_h : "1",
  114. ac_cv_have_stdint_h : "1",
  115. ac_cv_have_systypes_h : "1",
  116. ac_cv_have_inttypes_h : "1",
  117. ac_cv_have_libgflags : "0",
  118. ac_google_start_namespace : "namespace google {",
  119. ac_cv_have_uint16_t : "1",
  120. ac_cv_have_u_int16_t : "1",
  121. ac_cv_have___uint16 : "0",
  122. ac_google_end_namespace : "}",
  123. ac_cv_have___builtin_expect : "1",
  124. ac_google_namespace : "google",
  125. ac_cv___attribute___noinline : "__attribute__ ((noinline))",
  126. ac_cv___attribute___noreturn : "__attribute__ ((noreturn))",
  127. ac_cv___attribute___printf_4_5: "__attribute__((__format__ (__printf__, 4, 5)))"
  128. ])
  129. it.path = (it.name - ".in")
  130. }
  131. into("$thirdPartyNdkDir/glog")
  132. doLast {
  133. copy {
  134. from(fileTree(dir: "$thirdPartyNdkDir/glog", includes: ["stl_logging.h", "logging.h", "raw_logging.h", "vlog_is_on.h", "**/src/glog/log_severity.h"]).files)
  135. includeEmptyDirs = false
  136. into("$thirdPartyNdkDir/glog/exported/glog")
  137. }
  138. }
  139. }
  140. // Create Android.mk library module based on jsc from npm
  141. task prepareJSC {
  142. doLast {
  143. def jscPackagePath = findNodeModulePath(projectDir, "jsc-android")
  144. if (!jscPackagePath) {
  145. throw new GradleScriptException("Could not find the jsc-android npm package")
  146. }
  147. def jscDist = file("$jscPackagePath/dist")
  148. if (!jscDist.exists()) {
  149. throw new GradleScriptException("The jsc-android npm package is missing its \"dist\" directory")
  150. }
  151. def jscAAR = fileTree(jscDist).matching({ it.include "**/android-jsc/**/*.aar" }).singleFile
  152. def soFiles = zipTree(jscAAR).matching({ it.include "**/*.so" })
  153. def headerFiles = fileTree(jscDist).matching({ it.include "**/include/*.h" })
  154. copy {
  155. from(soFiles)
  156. from(headerFiles)
  157. from("src/main/jni/third-party/jsc/Android.mk")
  158. filesMatching("**/*.h", { it.path = "JavaScriptCore/${it.name}" })
  159. includeEmptyDirs(false)
  160. into("$thirdPartyNdkDir/jsc")
  161. }
  162. }
  163. }
  164. task downloadNdkBuildDependencies {
  165. if (!boostPath) {
  166. dependsOn(downloadBoost)
  167. }
  168. dependsOn(downloadDoubleConversion)
  169. dependsOn(downloadFolly)
  170. dependsOn(downloadGlog)
  171. }
  172. /**
  173. * Finds the path of the installed npm package with the given name using Node's
  174. * module resolution algorithm, which searches "node_modules" directories up to
  175. * the file system root. This handles various cases, including:
  176. *
  177. * - Working in the open-source RN repo:
  178. * Gradle: /path/to/react-native/ReactAndroid
  179. * Node module: /path/to/react-native/node_modules/[package]
  180. *
  181. * - Installing RN as a dependency of an app and searching for hoisted
  182. * dependencies:
  183. * Gradle: /path/to/app/node_modules/react-native/ReactAndroid
  184. * Node module: /path/to/app/node_modules/[package]
  185. *
  186. * - Working in a larger repo (e.g., Facebook) that contains RN:
  187. * Gradle: /path/to/repo/path/to/react-native/ReactAndroid
  188. * Node module: /path/to/repo/node_modules/[package]
  189. *
  190. * The search begins at the given base directory (a File object). The returned
  191. * path is a string.
  192. */
  193. def findNodeModulePath(baseDir, packageName) {
  194. def basePath = baseDir.toPath().normalize()
  195. // Node's module resolution algorithm searches up to the root directory,
  196. // after which the base path will be null
  197. while (basePath) {
  198. def candidatePath = Paths.get(basePath.toString(), "node_modules", packageName)
  199. if (candidatePath.toFile().exists()) {
  200. return candidatePath.toString()
  201. }
  202. basePath = basePath.getParent()
  203. }
  204. return null
  205. }
  206. def getNdkBuildName() {
  207. if (Os.isFamily(Os.FAMILY_WINDOWS)) {
  208. return "ndk-build.cmd"
  209. } else {
  210. return "ndk-build"
  211. }
  212. }
  213. def findNdkBuildFullPath() {
  214. // we allow to provide full path to ndk-build tool
  215. if (hasProperty("ndk.command")) {
  216. return property("ndk.command")
  217. }
  218. // or just a path to the containing directory
  219. if (hasProperty("ndk.path")) {
  220. def ndkDir = property("ndk.path")
  221. return new File(ndkDir, getNdkBuildName()).getAbsolutePath()
  222. }
  223. if (System.getenv("ANDROID_NDK") != null) {
  224. def ndkDir = System.getenv("ANDROID_NDK")
  225. return new File(ndkDir, getNdkBuildName()).getAbsolutePath()
  226. }
  227. def ndkDir = android.ndkDirectory ? android.ndkDirectory.absolutePath : null
  228. if (ndkDir) {
  229. return new File(ndkDir, getNdkBuildName()).getAbsolutePath()
  230. }
  231. return null
  232. }
  233. def reactNativeDevServerPort() {
  234. def value = project.getProperties().get("reactNativeDevServerPort")
  235. return value != null ? value : "8081"
  236. }
  237. def reactNativeInspectorProxyPort() {
  238. def value = project.getProperties().get("reactNativeInspectorProxyPort")
  239. return value != null ? value : reactNativeDevServerPort()
  240. }
  241. def getNdkBuildFullPath() {
  242. def ndkBuildFullPath = findNdkBuildFullPath()
  243. if (ndkBuildFullPath == null) {
  244. throw new GradleScriptException(
  245. "ndk-build binary cannot be found, check if you've set " +
  246. "\$ANDROID_NDK environment variable correctly or if ndk.dir is " +
  247. "setup in local.properties",
  248. null)
  249. }
  250. if (!new File(ndkBuildFullPath).canExecute()) {
  251. throw new GradleScriptException(
  252. "ndk-build binary " + ndkBuildFullPath + " doesn't exist or isn't executable.\n" +
  253. "Check that the \$ANDROID_NDK environment variable, or ndk.dir in local.properties, is set correctly.\n" +
  254. "(On Windows, make sure you escape backslashes in local.properties or use forward slashes, e.g. C:\\\\ndk or C:/ndk rather than C:\\ndk)",
  255. null)
  256. }
  257. return ndkBuildFullPath
  258. }
  259. def buildReactNdkLib = tasks.register("buildReactNdkLib", Exec) {
  260. dependsOn(prepareJSC, prepareHermes, prepareBoost, prepareDoubleConversion, prepareFolly, prepareGlog, extractAARHeaders, extractJNIFiles)
  261. inputs.dir("$projectDir/../ReactCommon")
  262. inputs.dir("src/main/jni")
  263. inputs.dir("src/main/java/com/facebook/react/modules/blob")
  264. outputs.dir("$buildDir/react-ndk/all")
  265. commandLine(getNdkBuildFullPath(),
  266. "NDK_DEBUG=" + (nativeBuildType.equalsIgnoreCase("debug") ? "1" : "0"),
  267. "NDK_PROJECT_PATH=null",
  268. "NDK_APPLICATION_MK=$projectDir/src/main/jni/Application.mk",
  269. "NDK_OUT=" + temporaryDir,
  270. "NDK_LIBS_OUT=$buildDir/react-ndk/all",
  271. "THIRD_PARTY_NDK_DIR=$buildDir/third-party-ndk",
  272. "REACT_COMMON_DIR=$projectDir/../ReactCommon",
  273. "REACT_SRC_DIR=$projectDir/src/main/java/com/facebook/react",
  274. "-C", file("src/main/jni/react/jni").absolutePath,
  275. "--jobs", project.findProperty("jobs") ?: Runtime.runtime.availableProcessors()
  276. )
  277. }
  278. def cleanReactNdkLib = tasks.register("cleanReactNdkLib", Exec) {
  279. ignoreExitValue(true)
  280. errorOutput(new ByteArrayOutputStream())
  281. commandLine(getNdkBuildFullPath(),
  282. "NDK_APPLICATION_MK=$projectDir/src/main/jni/Application.mk",
  283. "THIRD_PARTY_NDK_DIR=$buildDir/third-party-ndk",
  284. "REACT_COMMON_DIR=$projectDir/../ReactCommon",
  285. "-C", file("src/main/jni/react/jni").absolutePath,
  286. "clean")
  287. doLast {
  288. file(AAR_OUTPUT_URL).delete()
  289. println("Deleted aar output dir at ${file(AAR_OUTPUT_URL)}")
  290. }
  291. }
  292. def packageReactNdkLibs = tasks.register("packageReactNdkLibs", Copy) {
  293. dependsOn(buildReactNdkLib)
  294. from("$buildDir/react-ndk/all")
  295. into("$buildDir/react-ndk/exported")
  296. exclude("**/libjsc.so")
  297. exclude("**/libhermes.so")
  298. }
  299. def packageReactNdkLibsForBuck = tasks.register("packageReactNdkLibsForBuck", Copy) {
  300. dependsOn(packageReactNdkLibs)
  301. from("$buildDir/react-ndk/exported")
  302. into("src/main/jni/prebuilt/lib")
  303. }
  304. task extractAARHeaders {
  305. doLast {
  306. configurations.extractHeaders.files.each {
  307. def file = it.absoluteFile
  308. def packageName = file.name.tokenize('-')[0]
  309. copy {
  310. from zipTree(file)
  311. into "$projectDir/src/main/jni/first-party/$packageName/headers"
  312. include "**/*.h"
  313. }
  314. }
  315. }
  316. }
  317. task extractJNIFiles {
  318. doLast {
  319. configurations.extractJNI.files.each {
  320. def file = it.absoluteFile
  321. def packageName = file.name.tokenize('-')[0]
  322. copy {
  323. from zipTree(file)
  324. into "$projectDir/src/main/jni/first-party/$packageName/"
  325. include "jni/**/*"
  326. }
  327. }
  328. }
  329. }
  330. android {
  331. compileSdkVersion 29
  332. compileOptions {
  333. sourceCompatibility(JavaVersion.VERSION_1_8)
  334. targetCompatibility(JavaVersion.VERSION_1_8)
  335. }
  336. defaultConfig {
  337. minSdkVersion(16)
  338. targetSdkVersion(28)
  339. versionCode(1)
  340. versionName("1.0")
  341. consumerProguardFiles("proguard-rules.pro")
  342. ndk {
  343. moduleName("reactnativejni")
  344. }
  345. buildConfigField("boolean", "IS_INTERNAL_BUILD", "false")
  346. buildConfigField("int", "EXOPACKAGE_FLAGS", "0")
  347. resValue "integer", "react_native_dev_server_port", reactNativeDevServerPort()
  348. resValue "integer", "react_native_inspector_proxy_port", reactNativeInspectorProxyPort()
  349. testApplicationId("com.facebook.react.tests.gradle")
  350. testInstrumentationRunner("androidx.test.runner.AndroidJUnitRunner")
  351. }
  352. sourceSets.main {
  353. jni.srcDirs = []
  354. jniLibs.srcDir("$buildDir/react-ndk/exported")
  355. res.srcDirs = ["src/main/res/devsupport", "src/main/res/shell", "src/main/res/views/modal", "src/main/res/views/uimanager"]
  356. java {
  357. srcDirs = ["src/main/java", "src/main/libraries/soloader/java", "src/main/jni/first-party/fb/jni/java"]
  358. exclude("com/facebook/react/processing")
  359. exclude("com/facebook/react/module/processing")
  360. }
  361. }
  362. tasks.withType(JavaCompile) {
  363. compileTask ->
  364. compileTask.dependsOn(packageReactNdkLibs)
  365. }
  366. clean.dependsOn(cleanReactNdkLib)
  367. lintOptions {
  368. abortOnError(false)
  369. }
  370. packagingOptions {
  371. exclude("META-INF/NOTICE")
  372. exclude("META-INF/LICENSE")
  373. }
  374. configurations {
  375. extractHeaders
  376. extractJNI
  377. }
  378. }
  379. dependencies {
  380. api("com.facebook.infer.annotation:infer-annotation:0.11.2")
  381. api("com.facebook.yoga:proguard-annotations:1.14.1")
  382. api("javax.inject:javax.inject:1")
  383. api("androidx.appcompat:appcompat:1.0.2")
  384. api("androidx.swiperefreshlayout:swiperefreshlayout:1.0.0")
  385. api("com.facebook.fresco:fresco:${FRESCO_VERSION}")
  386. api("com.facebook.fresco:imagepipeline-okhttp3:${FRESCO_VERSION}")
  387. api("com.facebook.soloader:soloader:${SO_LOADER_VERSION}")
  388. api("com.google.code.findbugs:jsr305:3.0.2")
  389. api("com.squareup.okhttp3:okhttp:${OKHTTP_VERSION}")
  390. api("com.squareup.okhttp3:okhttp-urlconnection:${OKHTTP_VERSION}")
  391. api("com.squareup.okio:okio:1.15.0")
  392. api("com.facebook.fbjni:fbjni-java-only:0.0.3")
  393. extractHeaders("com.facebook.fbjni:fbjni:0.0.2:headers")
  394. extractJNI("com.facebook.fbjni:fbjni:0.0.2")
  395. testImplementation("junit:junit:${JUNIT_VERSION}")
  396. testImplementation("org.powermock:powermock-api-mockito:${POWERMOCK_VERSION}")
  397. testImplementation("org.powermock:powermock-module-junit4-rule:${POWERMOCK_VERSION}")
  398. testImplementation("org.powermock:powermock-classloading-xstream:${POWERMOCK_VERSION}")
  399. testImplementation("org.mockito:mockito-core:${MOCKITO_CORE_VERSION}")
  400. testImplementation("org.easytesting:fest-assert-core:${FEST_ASSERT_CORE_VERSION}")
  401. testImplementation("org.robolectric:robolectric:${ROBOLECTRIC_VERSION}")
  402. androidTestImplementation(fileTree(dir: "src/main/third-party/java/buck-android-support/", include: ["*.jar"]))
  403. androidTestImplementation("androidx.test:runner:${ANDROIDX_TEST_VERSION}")
  404. androidTestImplementation("androidx.test:rules:${ANDROIDX_TEST_VERSION}")
  405. androidTestImplementation("org.mockito:mockito-core:${MOCKITO_CORE_VERSION}")
  406. }
  407. apply(from: "release.gradle")