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