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 BOOST_VERSION = reactProperties.getProperty("BOOST_VERSION")
78    def DOUBLE_CONVERSION_VERSION = reactProperties.getProperty("DOUBLE_CONVERSION_VERSION")
79    def REACT_NATIVE_VERSION = System.getenv("REACT_NATIVE_OVERRIDE_VERSION") ?: reactProperties.getProperty("VERSION_NAME")
80    def REACT_NATIVE_TARGET_VERSION = REACT_NATIVE_VERSION.split("\\.")[1].toInteger()
81
82    def isNewArchitectureEnabled = project.findProperty("newArchEnabled") == "true"
83    def customDownloadsDir = System.getenv("REACT_NATIVE_DOWNLOADS_DIR")
84    def downloadsDir = customDownloadsDir ? new File(customDownloadsDir) : new File("${project.buildDir}/downloads")
85    def thirdPartyNdkDir = new File("${project.buildDir}/third-party-ndk")
86    def reactNativeThirdParty = new File("$REACT_NATIVE_DIR/ReactAndroid/src/main/jni/third-party")
87
88    def createNativeDepsDirectories = project.tasks.findByName('createNativeDepsDirectories') ?: project.tasks.register('createNativeDepsDirectories') {
89      downloadsDir.mkdirs()
90      thirdPartyNdkDir.mkdirs()
91    }
92
93    def extractReactNativeAARRelease = project.tasks.register('extractReactNativeAARRelease', ExtractReactNativeAARTask) {
94      reactNativeDir = REACT_NATIVE_DIR
95      buildType = 'Release'
96    }
97    def extractReactNativeAARDebug = project.tasks.register('extractReactNativeAARDebug', ExtractReactNativeAARTask) {
98      reactNativeDir = REACT_NATIVE_DIR
99      buildType = 'Debug'
100    }
101
102    def packageReactNdkDebugLibs = project.tasks.register("packageReactNdkDebugLibs", Copy) {
103      dependsOn(":ReactAndroid:packageReactNdkDebugLibsForBuck")
104      from("$REACT_NATIVE_DIR/ReactAndroid/src/main/jni/prebuilt/lib")
105      into("${project.buildDir}/react-ndk/exported")
106    }
107    def packageReactNdkReleaseLibs = project.tasks.register("packageReactNdkReleaseLibs", Copy) {
108      dependsOn(":ReactAndroid:packageReactNdkReleaseLibsForBuck")
109      from("$REACT_NATIVE_DIR/ReactAndroid/src/main/jni/prebuilt/lib")
110      into("${project.buildDir}/react-ndk/exported")
111    }
112
113    // [BEGIN] Extra libs
114   def downloadDoubleConversion = project.tasks.create('downloadDoubleConversion', project.Download) {
115      dependsOn(createNativeDepsDirectories)
116      src("https://github.com/google/double-conversion/archive/v${DOUBLE_CONVERSION_VERSION}.tar.gz")
117      onlyIfNewer(true)
118      overwrite(false)
119      dest(new File(downloadsDir, "double-conversion-${DOUBLE_CONVERSION_VERSION}.tar.gz"))
120    }
121
122    def prepareDoubleConversion = project.tasks.register('prepareDoubleConversion', Copy) {
123      dependsOn(downloadDoubleConversion)
124      from(project.tarTree(downloadDoubleConversion.dest))
125      from("$reactNativeThirdParty/double-conversion/Android.mk")
126      include("double-conversion-${DOUBLE_CONVERSION_VERSION}/src/**/*", "Android.mk")
127      filesMatching("*/src/**/*", { fname -> fname.path = "double-conversion/${fname.name}" })
128      includeEmptyDirs = false
129      into("$thirdPartyNdkDir/double-conversion")
130    }
131
132    def downloadFolly = project.tasks.create('downloadFolly', project.Download) {
133      dependsOn(createNativeDepsDirectories)
134      src("https://github.com/facebook/folly/archive/v${FOLLY_VERSION}.tar.gz")
135      onlyIfNewer(true)
136      overwrite(false)
137      dest(new File(downloadsDir, "folly-${FOLLY_VERSION}.tar.gz"))
138    }
139
140    def prepareFolly = project.tasks.register('prepareFolly', Copy) {
141      dependsOn(downloadFolly)
142      from(project.tarTree(downloadFolly.dest))
143      from("$reactNativeThirdParty/folly/Android.mk")
144      include("folly-${FOLLY_VERSION}/folly/**/*", "Android.mk")
145      eachFile { fname -> fname.path = (fname.path - "folly-${FOLLY_VERSION}/") }
146      // Fixes problem with Folly failing to build on certain systems. See
147      // https://github.com/software-mansion/react-native-reanimated/issues/1024
148      def follyReplaceContent = '''
149      ssize_t r;
150      do {
151        r = open(name, flags, mode);
152      } while (r == -1 && errno == EINTR);
153        return r;
154      '''
155      filter { line -> line.replaceAll("return int\\(wrapNoInt\\(open, name, flags, mode\\)\\);", follyReplaceContent) }
156      includeEmptyDirs = false
157      into("$thirdPartyNdkDir/folly")
158    }
159    // [END] Extra libs
160
161    project.afterEvaluate {
162      if (REACT_NATIVE_BUILD_FROM_SOURCE) {
163        nativeBuildDependsOn(project, ":ReactAndroid:copyReleaseJniLibsProjectOnly", ["Release", "RelWithDebInfo"])
164        nativeBuildDependsOn(project, ":ReactAndroid:copyDebugJniLibsProjectOnly", ["Debug"])
165      } else {
166        nativeBuildDependsOn(project, extractReactNativeAARRelease, ["Release", "RelWithDebInfo"])
167        nativeBuildDependsOn(project, extractReactNativeAARDebug, ["Debug"])
168      }
169
170      def extraLibs = project.extensions.extraProperties.has('extraLegacyReactNativeLibs')
171        ? project.extensions.extraProperties.get('extraLegacyReactNativeLibs')
172        : []
173      extraLibs.each {
174        nativeBuildDependsOn(project, project.tasks.named(it), null)
175      }
176
177      if (isNewArchitectureEnabled) {
178        def preDebugBuild = project.tasks.named('preDebugBuild')
179        def preReleaseBuild = project.tasks.named('preReleaseBuild')
180        preDebugBuild.configure {
181          dependsOn(packageReactNdkDebugLibs)
182        }
183        preReleaseBuild.configure {
184          dependsOn(packageReactNdkReleaseLibs)
185        }
186
187        // Due to a bug inside AGP, we have to explicitly set a dependency
188        // between configureCMake* tasks and the preBuild tasks.
189        // This can be removed once this is solved: https://issuetracker.google.com/issues/207403732
190        project.tasks.named('configureCMakeDebug').configure {
191          dependsOn(preDebugBuild)
192        }
193        project.tasks.named('configureCMakeRelWithDebInfo').configure {
194          dependsOn(preReleaseBuild)
195        }
196        def reactNativeArchitectures = project.getProperties().get("reactNativeArchitectures")?.split(",") ?: ["armeabi-v7a", "x86", "x86_64", "arm64-v8a"]
197
198        reactNativeArchitectures.each { architecture ->
199          project.tasks.named("configureCMakeDebug[${architecture}]")?.configure {
200            dependsOn("preDebugBuild")
201          }
202          project.tasks.named("configureCMakeRelWithDebInfo[${architecture}]")?.configure {
203            dependsOn("preReleaseBuild")
204          }
205        }
206      }
207    }
208  }
209}
210
211ext.applyLegacyReactNativeLibsExtractionPlugin = {
212  apply plugin: LegacyReactNativeLibsExtractionPlugin
213}
214
215// [END] Remove when we drop SDK 47
216