1class KotlinExpoModulesCorePlugin implements Plugin<Project> {
2  void apply(Project project) {
3    project.buildscript {
4      project.ext.kotlinVersion = {
5        project.rootProject.ext.has("kotlinVersion")
6            ? project.rootProject.ext.get("kotlinVersion")
7            : "1.6.10"
8      }
9    }
10  }
11}
12
13ext.applyKotlinExpoModulesCorePlugin = {
14  apply plugin: KotlinExpoModulesCorePlugin
15}
16
17// [BEGIN] Remove when we drop SDK 47
18abstract class ExtractReactNativeAARTask extends DefaultTask {
19  @Input
20  abstract Property<String> getBuildType()
21
22  @Input
23  abstract Property<String> getReactNativeDir()
24
25  @TaskAction
26  def taskAction() {
27    def suffix = buildType.get() == 'Debug' ? '-debug' : '-release'
28    def rnAARs = project.fileTree("${reactNativeDir.get()}/android").matching { include "**/react-native/**/*${suffix}.aar" }
29    if (rnAARs.isEmpty()) {
30      rnAARs = project.fileTree("${reactNativeDir.get()}/android").matching { include "**/react-native/**/*.aar" }
31    }
32    if (rnAARs.any()) {
33      // node_modules/react-native has a .aar, extract headers
34      if (rnAARs.size() > 1) {
35        logger.error("More than one React Native AAR file has been found:")
36        rnAARs.each {println(it) }
37        throw new GradleException("Multiple React Native AARs found:\n${rnAARs.join("\n")}" +
38            "\nRemove the old ones and try again")
39      }
40    }
41    def rnAAR = rnAARs.singleFile
42    def file = rnAAR.absoluteFile
43    def packageName = file.name.tokenize('-')[0]
44    project.copy {
45      from project.zipTree(file)
46      into "${project.buildDir}/${packageName}"
47      include "jni/**/*"
48    }
49  }
50}
51
52class LegacyReactNativeLibsExtractionPlugin implements Plugin<Project> {
53  void nativeBuildDependsOn(project, dependsOnTask, buildTypesIncludes) {
54    def buildTasks = project.tasks.findAll { task ->
55      def taskName = task.name
56      if (taskName.contains("Clean")) { return false }
57      if (taskName.contains("externalNative") || taskName.contains("CMake") || taskName.contains("generateJsonModel")) {
58        if (buildTypesIncludes == null) { return true }
59        for (buildType in buildTypesIncludes) {
60          if (taskName.contains(buildType)) { return true }
61        }
62      }
63      return false
64    }
65    buildTasks.forEach { task -> task.dependsOn(dependsOnTask) }
66  }
67
68  void apply(Project project) {
69    def REACT_NATIVE_BUILD_FROM_SOURCE = project.findProject(":ReactAndroid") != null
70    def REACT_NATIVE_DIR = REACT_NATIVE_BUILD_FROM_SOURCE
71      ? project.findProject(":ReactAndroid").getProjectDir().parent
72      : new File(["node", "--print", "require.resolve('react-native/package.json')"].execute(null, project.rootDir).text.trim()).parent
73
74    def reactProperties = new Properties()
75    new File("$REACT_NATIVE_DIR/ReactAndroid/gradle.properties").withInputStream { reactProperties.load(it) }
76    def FOLLY_VERSION = reactProperties.getProperty("FOLLY_VERSION")
77    def DOUBLE_CONVERSION_VERSION = reactProperties.getProperty("DOUBLE_CONVERSION_VERSION")
78    def REACT_NATIVE_VERSION = System.getenv("REACT_NATIVE_OVERRIDE_VERSION") ?: reactProperties.getProperty("VERSION_NAME")
79    def REACT_NATIVE_TARGET_VERSION = REACT_NATIVE_VERSION.split("\\.")[1].toInteger()
80
81    def isNewArchitectureEnabled = project.findProperty("newArchEnabled") == "true"
82    def customDownloadsDir = System.getenv("REACT_NATIVE_DOWNLOADS_DIR")
83    def downloadsDir = customDownloadsDir ? new File(customDownloadsDir) : new File("${project.buildDir}/downloads")
84    def thirdPartyNdkDir = new File("${project.buildDir}/third-party-ndk")
85    def reactNativeThirdParty = new File("$REACT_NATIVE_DIR/ReactAndroid/src/main/jni/third-party")
86
87    def createNativeDepsDirectories = project.tasks.findByName('createNativeDepsDirectories') ?: project.tasks.register('createNativeDepsDirectories') {
88      downloadsDir.mkdirs()
89      thirdPartyNdkDir.mkdirs()
90    }
91
92    def extractReactNativeAARRelease = project.tasks.register('extractReactNativeAARRelease', ExtractReactNativeAARTask) {
93      reactNativeDir = REACT_NATIVE_DIR
94      buildType = 'Release'
95    }
96    def extractReactNativeAARDebug = project.tasks.register('extractReactNativeAARDebug', ExtractReactNativeAARTask) {
97      reactNativeDir = REACT_NATIVE_DIR
98      buildType = 'Debug'
99    }
100
101    def packageReactNdkDebugLibs = project.tasks.register("packageReactNdkDebugLibs", Copy) {
102      dependsOn(":ReactAndroid:packageReactNdkDebugLibsForBuck")
103      from("$REACT_NATIVE_DIR/ReactAndroid/src/main/jni/prebuilt/lib")
104      into("${project.buildDir}/react-ndk/exported")
105    }
106    def packageReactNdkReleaseLibs = project.tasks.register("packageReactNdkReleaseLibs", Copy) {
107      dependsOn(":ReactAndroid:packageReactNdkReleaseLibsForBuck")
108      from("$REACT_NATIVE_DIR/ReactAndroid/src/main/jni/prebuilt/lib")
109      into("${project.buildDir}/react-ndk/exported")
110    }
111
112    // [BEGIN] Extra libs
113   def downloadDoubleConversion = project.tasks.create('downloadDoubleConversion', project.Download) {
114      dependsOn(createNativeDepsDirectories)
115      src("https://github.com/google/double-conversion/archive/v${DOUBLE_CONVERSION_VERSION}.tar.gz")
116      onlyIfNewer(true)
117      overwrite(false)
118      dest(new File(downloadsDir, "double-conversion-${DOUBLE_CONVERSION_VERSION}.tar.gz"))
119    }
120
121    def prepareDoubleConversion = project.tasks.register('prepareDoubleConversion', Copy) {
122      dependsOn(downloadDoubleConversion)
123      from(project.tarTree(downloadDoubleConversion.dest))
124      from("$reactNativeThirdParty/double-conversion/Android.mk")
125      include("double-conversion-${DOUBLE_CONVERSION_VERSION}/src/**/*", "Android.mk")
126      filesMatching("*/src/**/*", { fname -> fname.path = "double-conversion/${fname.name}" })
127      includeEmptyDirs = false
128      into("$thirdPartyNdkDir/double-conversion")
129    }
130
131    def downloadFolly = project.tasks.create('downloadFolly', project.Download) {
132      dependsOn(createNativeDepsDirectories)
133      src("https://github.com/facebook/folly/archive/v${FOLLY_VERSION}.tar.gz")
134      onlyIfNewer(true)
135      overwrite(false)
136      dest(new File(downloadsDir, "folly-${FOLLY_VERSION}.tar.gz"))
137    }
138
139    def prepareFolly = project.tasks.register('prepareFolly', Copy) {
140      dependsOn(downloadFolly)
141      from(project.tarTree(downloadFolly.dest))
142      from("$reactNativeThirdParty/folly/Android.mk")
143      include("folly-${FOLLY_VERSION}/folly/**/*", "Android.mk")
144      eachFile { fname -> fname.path = (fname.path - "folly-${FOLLY_VERSION}/") }
145      // Fixes problem with Folly failing to build on certain systems. See
146      // https://github.com/software-mansion/react-native-reanimated/issues/1024
147      def follyReplaceContent = '''
148      ssize_t r;
149      do {
150        r = open(name, flags, mode);
151      } while (r == -1 && errno == EINTR);
152        return r;
153      '''
154      filter { line -> line.replaceAll("return int\\(wrapNoInt\\(open, name, flags, mode\\)\\);", follyReplaceContent) }
155      includeEmptyDirs = false
156      into("$thirdPartyNdkDir/folly")
157    }
158    // [END] Extra libs
159
160    project.afterEvaluate {
161      if (REACT_NATIVE_BUILD_FROM_SOURCE) {
162        nativeBuildDependsOn(project, ":ReactAndroid:copyReleaseJniLibsProjectOnly", ["Release", "RelWithDebInfo"])
163        nativeBuildDependsOn(project, ":ReactAndroid:copyDebugJniLibsProjectOnly", ["Debug"])
164      } else {
165        nativeBuildDependsOn(project, extractReactNativeAARRelease, ["Release", "RelWithDebInfo"])
166        nativeBuildDependsOn(project, extractReactNativeAARDebug, ["Debug"])
167      }
168
169      def extraLibs = project.extensions.extraProperties.has('extraLegacyReactNativeLibs')
170        ? project.extensions.extraProperties.get('extraLegacyReactNativeLibs')
171        : []
172      extraLibs.each {
173        nativeBuildDependsOn(project, project.tasks.named(it), null)
174      }
175
176      if (isNewArchitectureEnabled) {
177        def preDebugBuild = project.tasks.named('preDebugBuild')
178        def preReleaseBuild = project.tasks.named('preReleaseBuild')
179        preDebugBuild.configure {
180          dependsOn(packageReactNdkDebugLibs)
181        }
182        preReleaseBuild.configure {
183          dependsOn(packageReactNdkReleaseLibs)
184        }
185
186        // Due to a bug inside AGP, we have to explicitly set a dependency
187        // between configureCMake* tasks and the preBuild tasks.
188        // This can be removed once this is solved: https://issuetracker.google.com/issues/207403732
189        project.tasks.named('configureCMakeDebug').configure {
190          dependsOn(preDebugBuild)
191        }
192        project.tasks.named('configureCMakeRelWithDebInfo').configure {
193          dependsOn(preReleaseBuild)
194        }
195        def reactNativeArchitectures = project.getProperties().get("reactNativeArchitectures")?.split(",") ?: ["armeabi-v7a", "x86", "x86_64", "arm64-v8a"]
196
197        reactNativeArchitectures.each { architecture ->
198          project.tasks.named("configureCMakeDebug[${architecture}]")?.configure {
199            dependsOn("preDebugBuild")
200          }
201          project.tasks.named("configureCMakeRelWithDebInfo[${architecture}]")?.configure {
202            dependsOn("preReleaseBuild")
203          }
204        }
205      }
206    }
207  }
208}
209
210ext.applyLegacyReactNativeLibsExtractionPlugin = {
211  apply plugin: LegacyReactNativeLibsExtractionPlugin
212}
213
214// [END] Remove when we drop SDK 47
215